Merge branch 'jn/block-sha1'

The code to load a word one-byte-at-a-time was optimized into a
word-wide load instruction even when the pointer was not aligned,
which caused issues on architectures that do not like unaligned
access.

* jn/block-sha1:
  Makefile: BLK_SHA1 does not require fast htonl() and unaligned loads
  block-sha1: put expanded macro parameters in parentheses
  block-sha1: avoid pointer conversion that violates alignment constraints
diff --git a/.gitattributes b/.gitattributes
index 0636dee..5e98806 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,2 +1,3 @@
 * whitespace=!indent,trail,space
 *.[ch] whitespace=indent,trail,space
+*.sh whitespace=indent,trail,space
diff --git a/.gitignore b/.gitignore
index 41c0b20..bb5c91e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,179 +1,235 @@
-GIT-BUILD-OPTIONS
-GIT-CFLAGS
-GIT-GUI-VARS
-GIT-VERSION-FILE
-git
-git-add
-git-add--interactive
-git-am
-git-annotate
-git-apply
-git-archimport
-git-archive
-git-bisect
-git-bisect--helper
-git-blame
-git-branch
-git-bundle
-git-cat-file
-git-check-attr
-git-check-ref-format
-git-checkout
-git-checkout-index
-git-cherry
-git-cherry-pick
-git-clean
-git-clone
-git-commit
-git-commit-tree
-git-config
-git-count-objects
-git-cvsexportcommit
-git-cvsimport
-git-cvsserver
-git-daemon
-git-diff
-git-diff-files
-git-diff-index
-git-diff-tree
-git-difftool
-git-difftool--helper
-git-describe
-git-fast-export
-git-fast-import
-git-fetch
-git-fetch--tool
-git-fetch-pack
-git-filter-branch
-git-fmt-merge-msg
-git-for-each-ref
-git-format-patch
-git-fsck
-git-fsck-objects
-git-gc
-git-get-tar-commit-id
-git-grep
-git-hash-object
-git-help
-git-http-fetch
-git-http-push
-git-imap-send
-git-index-pack
-git-init
-git-init-db
-git-instaweb
-git-log
-git-lost-found
-git-ls-files
-git-ls-remote
-git-ls-tree
-git-mailinfo
-git-mailsplit
-git-merge
-git-merge-base
-git-merge-index
-git-merge-file
-git-merge-tree
-git-merge-octopus
-git-merge-one-file
-git-merge-ours
-git-merge-recursive
-git-merge-resolve
-git-merge-subtree
-git-mergetool
-git-mergetool--lib
-git-mktag
-git-mktree
-git-name-rev
-git-mv
-git-pack-redundant
-git-pack-objects
-git-pack-refs
-git-parse-remote
-git-patch-id
-git-peek-remote
-git-prune
-git-prune-packed
-git-pull
-git-push
-git-quiltimport
-git-read-tree
-git-rebase
-git-rebase--interactive
-git-receive-pack
-git-reflog
-git-relink
-git-remote
-git-repack
-git-repo-config
-git-request-pull
-git-rerere
-git-reset
-git-rev-list
-git-rev-parse
-git-revert
-git-rm
-git-send-email
-git-send-pack
-git-sh-setup
-git-shell
-git-shortlog
-git-show
-git-show-branch
-git-show-index
-git-show-ref
-git-stage
-git-stash
-git-status
-git-stripspace
-git-submodule
-git-svn
-git-symbolic-ref
-git-tag
-git-tar-tree
-git-unpack-file
-git-unpack-objects
-git-update-index
-git-update-ref
-git-update-server-info
-git-upload-archive
-git-upload-pack
-git-var
-git-verify-pack
-git-verify-tag
-git-web--browse
-git-whatchanged
-git-write-tree
-git-core-*/?*
-gitk-wish
-gitweb/gitweb.cgi
-test-chmtime
-test-ctype
-test-date
-test-delta
-test-dump-cache-tree
-test-genrandom
-test-match-trees
-test-parse-options
-test-path-utils
-test-sha1
-test-sigchain
-common-cmds.h
+/GIT-BUILD-OPTIONS
+/GIT-CFLAGS
+/GIT-LDFLAGS
+/GIT-GUI-VARS
+/GIT-PREFIX
+/GIT-SCRIPT-DEFINES
+/GIT-USER-AGENT
+/GIT-VERSION-FILE
+/bin-wrappers/
+/git
+/git-add
+/git-add--interactive
+/git-am
+/git-annotate
+/git-apply
+/git-archimport
+/git-archive
+/git-bisect
+/git-bisect--helper
+/git-blame
+/git-branch
+/git-bundle
+/git-cat-file
+/git-check-attr
+/git-check-ref-format
+/git-checkout
+/git-checkout-index
+/git-cherry
+/git-cherry-pick
+/git-clean
+/git-clone
+/git-column
+/git-commit
+/git-commit-tree
+/git-config
+/git-count-objects
+/git-credential
+/git-credential-cache
+/git-credential-cache--daemon
+/git-credential-store
+/git-cvsexportcommit
+/git-cvsimport
+/git-cvsserver
+/git-daemon
+/git-diff
+/git-diff-files
+/git-diff-index
+/git-diff-tree
+/git-difftool
+/git-difftool--helper
+/git-describe
+/git-fast-export
+/git-fast-import
+/git-fetch
+/git-fetch-pack
+/git-filter-branch
+/git-fmt-merge-msg
+/git-for-each-ref
+/git-format-patch
+/git-fsck
+/git-fsck-objects
+/git-gc
+/git-get-tar-commit-id
+/git-grep
+/git-hash-object
+/git-help
+/git-http-backend
+/git-http-fetch
+/git-http-push
+/git-imap-send
+/git-index-pack
+/git-init
+/git-init-db
+/git-instaweb
+/git-log
+/git-lost-found
+/git-ls-files
+/git-ls-remote
+/git-ls-tree
+/git-mailinfo
+/git-mailsplit
+/git-merge
+/git-merge-base
+/git-merge-index
+/git-merge-file
+/git-merge-tree
+/git-merge-octopus
+/git-merge-one-file
+/git-merge-ours
+/git-merge-recursive
+/git-merge-resolve
+/git-merge-subtree
+/git-mergetool
+/git-mergetool--lib
+/git-mktag
+/git-mktree
+/git-name-rev
+/git-mv
+/git-notes
+/git-p4
+/git-pack-redundant
+/git-pack-objects
+/git-pack-refs
+/git-parse-remote
+/git-patch-id
+/git-peek-remote
+/git-prune
+/git-prune-packed
+/git-pull
+/git-push
+/git-quiltimport
+/git-read-tree
+/git-rebase
+/git-rebase--am
+/git-rebase--interactive
+/git-rebase--merge
+/git-receive-pack
+/git-reflog
+/git-relink
+/git-remote
+/git-remote-http
+/git-remote-https
+/git-remote-ftp
+/git-remote-ftps
+/git-remote-fd
+/git-remote-ext
+/git-remote-testgit
+/git-repack
+/git-replace
+/git-repo-config
+/git-request-pull
+/git-rerere
+/git-reset
+/git-rev-list
+/git-rev-parse
+/git-revert
+/git-rm
+/git-send-email
+/git-send-pack
+/git-sh-i18n
+/git-sh-i18n--envsubst
+/git-sh-setup
+/git-sh-i18n
+/git-shell
+/git-shortlog
+/git-show
+/git-show-branch
+/git-show-index
+/git-show-ref
+/git-stage
+/git-stash
+/git-status
+/git-stripspace
+/git-submodule
+/git-svn
+/git-symbolic-ref
+/git-tag
+/git-tar-tree
+/git-unpack-file
+/git-unpack-objects
+/git-update-index
+/git-update-ref
+/git-update-server-info
+/git-upload-archive
+/git-upload-pack
+/git-var
+/git-verify-pack
+/git-verify-tag
+/git-web--browse
+/git-whatchanged
+/git-write-tree
+/git-core-*/?*
+/gitk-git/gitk-wish
+/gitweb/GITWEB-BUILD-OPTIONS
+/gitweb/gitweb.cgi
+/gitweb/static/gitweb.js
+/gitweb/static/gitweb.min.*
+/test-chmtime
+/test-ctype
+/test-date
+/test-delta
+/test-dump-cache-tree
+/test-scrap-cache-tree
+/test-genrandom
+/test-index-version
+/test-line-buffer
+/test-match-trees
+/test-mergesort
+/test-mktemp
+/test-parse-options
+/test-path-utils
+/test-revision-walking
+/test-run-command
+/test-sha1
+/test-sigchain
+/test-subprocess
+/test-svn-fe
+/common-cmds.h
 *.tar.gz
 *.dsc
 *.deb
-git.spec
+/git.spec
 *.exe
 *.[aos]
 *.py[co]
-config.mak
-autom4te.cache
-config.cache
-config.log
-config.status
-config.mak.autogen
-config.mak.append
-configure
-tags
-TAGS
-cscope*
+.depend/
+*.gcda
+*.gcno
+*.gcov
+/coverage-untested-functions
+/cover_db/
+/cover_db_html/
+*+
+/config.mak
+/autom4te.cache
+/config.cache
+/config.log
+/config.status
+/config.mak.autogen
+/config.mak.append
+/configure
+/tags
+/TAGS
+/cscope*
+*.obj
+*.lib
+*.sln
+*.suo
+*.ncb
+*.vcproj
+*.user
+*.idb
+*.pdb
+/Debug/
+/Release/
diff --git a/.mailmap b/.mailmap
index 373476b..6303782 100644
--- a/.mailmap
+++ b/.mailmap
@@ -5,6 +5,7 @@
 # same person appearing not to be so.
 #
 
+Alex Bennée <kernel-hacker@bennee.com>
 Alexander Gavrilov <angavrilov@gmail.com>
 Aneesh Kumar K.V <aneesh.kumar@gmail.com>
 Brian M. Carlson <sandals@crustytoothpaste.ath.cx>
@@ -15,6 +16,7 @@
 David D. Kilzer <ddkilzer@kilzer.net>
 David Kågedal <davidk@lysator.liu.se>
 David S. Miller <davem@davemloft.net>
+Deskin Miller <deskinm@umich.edu>
 Dirk Süsserott <newsletter@dirk.my1.cc>
 Fredrik Kuivinen <freku045@student.liu.se>
 H. Peter Anvin <hpa@bonde.sc.orionmulti.com>
@@ -27,20 +29,28 @@
 Jon Loeliger <jdl@freescale.com>
 Jon Seymour <jon@blackcubes.dyndns.org>
 Jonathan Nieder <jrnieder@uchicago.edu>
-Junio C Hamano <junio@twinsun.com>
+Junio C Hamano <gitster@pobox.com> <gitster@pobox.com>
+Junio C Hamano <gitster@pobox.com> <junio@pobox.com>
+Junio C Hamano <gitster@pobox.com> <junio@twinsun.com>
+Junio C Hamano <gitster@pobox.com> <junkio@twinsun.com>
+Junio C Hamano <gitster@pobox.com> <junio@hera.kernel.org>
+Junio C Hamano <gitster@pobox.com> <junio@kernel.org>
+Junio C Hamano <gitster@pobox.com> <junkio@cox.net>
 Karl Hasselström <kha@treskal.com>
 Kent Engstrom <kent@lysator.liu.se>
 Lars Doelle <lars.doelle@on-line ! de>
 Lars Doelle <lars.doelle@on-line.de>
 Li Hong <leehong@pku.edu.cn>
 Lukas Sandström <lukass@etek.chalmers.se>
-Martin Langhoff <martin@catalyst.net.nz>
+Martin Langhoff <martin@laptop.org>
 Michael Coleman <tutufan@gmail.com>
+Michael J Gruber <git@drmicha.warpmail.net> <michaeljgruber+gmane@fastmail.fm>
 Michael W. Olson <mwolson@gnu.org>
 Michele Ballabio <barra_cuda@katamail.com>
 Nanako Shiraishi <nanako3@bluebottle.com>
 Nanako Shiraishi <nanako3@lavabit.com>
 Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
+<nico@fluxnic.net> <nico@cam.org>
 Philippe Bruhat <book@cpan.org>
 Ramsay Allan Jones <ramsay@ramsay1.demon.co.uk>
 René Scharfe <rene.scharfe@lsrfire.ath.cx>
@@ -58,6 +68,7 @@
 Uwe Kleine-König <uzeisberger@io.fsforth.de>
 Uwe Kleine-König <zeisberg@informatik.uni-freiburg.de>
 Ville Skyttä <scop@xemacs.org>
+Vitaly "_Vi" Shukela <public_vi@tut.by>
 William Pursell <bill.pursell@gmail.com>
 YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
 anonymous <linux@horizon.com>
diff --git a/COPYING b/COPYING
index 6ff87c4..536e555 100644
--- a/COPYING
+++ b/COPYING
@@ -22,8 +22,8 @@
 		    GNU GENERAL PUBLIC LICENSE
 		       Version 2, June 1991
 
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
-                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  Everyone is permitted to copy and distribute verbatim copies
  of this license document, but changing it is not allowed.
 
@@ -36,7 +36,7 @@
 General Public License applies to most of the Free Software
 Foundation's software and to any other program whose authors commit to
 using it.  (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.)  You can apply it to
+the GNU Lesser General Public License instead.)  You can apply it to
 your programs, too.
 
   When we speak of free software, we are referring to freedom, not
@@ -76,7 +76,7 @@
 
   The precise terms and conditions for copying, distribution and
 modification follow.
-
+
 		    GNU GENERAL PUBLIC LICENSE
    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
 
@@ -131,7 +131,7 @@
     License.  (Exception: if the Program itself is interactive but
     does not normally print such an announcement, your work based on
     the Program is not required to print an announcement.)
-
+
 These requirements apply to the modified work as a whole.  If
 identifiable sections of that work are not derived from the Program,
 and can be reasonably considered independent and separate works in
@@ -189,7 +189,7 @@
 access to copy the source code from the same place counts as
 distribution of the source code, even though third parties are not
 compelled to copy the source along with the object code.
-
+
   4. You may not copy, modify, sublicense, or distribute the Program
 except as expressly provided under this License.  Any attempt
 otherwise to copy, modify, sublicense or distribute the Program is
@@ -246,7 +246,7 @@
 
 This section is intended to make thoroughly clear what is believed to
 be a consequence of the rest of this License.
-
+
   8. If the distribution and/or use of the Program is restricted in
 certain countries either by patents or by copyrighted interfaces, the
 original copyright holder who places the Program under this License
@@ -299,7 +299,7 @@
 POSSIBILITY OF SUCH DAMAGES.
 
 		     END OF TERMS AND CONDITIONS
-
+
 	    How to Apply These Terms to Your New Programs
 
   If you develop a new program, and you want it to be of the greatest
@@ -324,10 +324,9 @@
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.
 
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
 Also add information on how to contact you by electronic and paper mail.
 
@@ -357,5 +356,5 @@
 This General Public License does not permit incorporating your program into
 proprietary programs.  If your program is a subroutine library, you may
 consider it more useful to permit linking proprietary applications with the
-library.  If this is what you want to do, use the GNU Library General
+library.  If this is what you want to do, use the GNU Lesser General
 Public License instead of this License.
diff --git a/Documentation/.gitignore b/Documentation/.gitignore
index d8edd90..d62aebd 100644
--- a/Documentation/.gitignore
+++ b/Documentation/.gitignore
@@ -3,8 +3,10 @@
 *.[1-8]
 *.made
 *.texi
+*.pdf
 git.info
 gitman.info
 howto-index.txt
 doc.dep
 cmds-*.txt
+manpage-base-url.xsl
diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines
index b8bf618..4557711 100644
--- a/Documentation/CodingGuidelines
+++ b/Documentation/CodingGuidelines
@@ -31,25 +31,48 @@
 
 For shell scripts specifically (not exhaustive):
 
+ - We use tabs for indentation.
+
+ - Case arms are indented at the same depth as case and esac lines.
+
+ - Redirection operators should be written with space before, but no
+   space after them.  In other words, write 'echo test >"$file"'
+   instead of 'echo test> $file' or 'echo test > $file'.  Note that
+   even though it is not required by POSIX to double-quote the
+   redirection target in a variable (as shown above), our code does so
+   because some versions of bash issue a warning without the quotes.
+
  - We prefer $( ... ) for command substitution; unlike ``, it
    properly nests.  It should have been the way Bourne spelled
    it from day one, but unfortunately isn't.
 
- - We use ${parameter-word} and its [-=?+] siblings, and their
-   colon'ed "unset or null" form.
+ - If you want to find out if a command is available on the user's
+   $PATH, you should use 'type <command>', instead of 'which <command>'.
+   The output of 'which' is not machine parseable and its exit code
+   is not reliable across platforms.
 
- - We use ${parameter#word} and its [#%] siblings, and their
-   doubled "longest matching" form.
+ - We use POSIX compliant parameter substitutions and avoid bashisms;
+   namely:
+
+   - We use ${parameter-word} and its [-=?+] siblings, and their
+     colon'ed "unset or null" form.
+
+   - We use ${parameter#word} and its [#%] siblings, and their
+     doubled "longest matching" form.
+
+   - No "Substring Expansion" ${parameter:offset:length}.
+
+   - No shell arrays.
+
+   - No strlen ${#parameter}.
+
+   - No pattern replacement ${parameter/pattern/string}.
 
  - We use Arithmetic Expansion $(( ... )).
 
- - No "Substring Expansion" ${parameter:offset:length}.
-
- - No shell arrays.
-
- - No strlen ${#parameter}.
-
- - No regexp ${parameter/pattern/string}.
+ - Inside Arithmetic Expansion, spell shell variables with $ in front
+   of them, as some shells do not grok $((x)) while accepting $(($x))
+   just fine (e.g. dash older than 0.5.4).
 
  - We do not use Process Substitution <(list) or >(list).
 
@@ -70,6 +93,10 @@
      are ERE elements not BRE (note that \? and \+ are not even part
      of BRE -- making them accessible from BRE is a GNU extension).
 
+ - Use Git's gettext wrappers in git-sh-i18n to make the user
+   interface translatable. See "Marking strings for translation" in
+   po/README.
+
 For C programs:
 
  - We use tabs to indent, and interpret tabs as taking up to
@@ -132,3 +159,58 @@
 
  - When we pass <string, length> pair to functions, we should try to
    pass them in that order.
+
+ - Use Git's gettext wrappers to make the user interface
+   translatable. See "Marking strings for translation" in po/README.
+
+Writing Documentation:
+
+ Every user-visible change should be reflected in the documentation.
+ The same general rule as for code applies -- imitate the existing
+ conventions.  A few commented examples follow to provide reference
+ when writing or modifying command usage strings and synopsis sections
+ in the manual pages:
+
+ Placeholders are spelled in lowercase and enclosed in angle brackets:
+   <file>
+   --sort=<key>
+   --abbrev[=<n>]
+
+ Possibility of multiple occurrences is indicated by three dots:
+   <file>...
+   (One or more of <file>.)
+
+ Optional parts are enclosed in square brackets:
+   [<extra>]
+   (Zero or one <extra>.)
+
+   --exec-path[=<path>]
+   (Option with an optional argument.  Note that the "=" is inside the
+   brackets.)
+
+   [<patch>...]
+   (Zero or more of <patch>.  Note that the dots are inside, not
+   outside the brackets.)
+
+ Multiple alternatives are indicated with vertical bar:
+   [-q | --quiet]
+   [--utf8 | --no-utf8]
+
+ Parentheses are used for grouping:
+   [(<rev>|<range>)...]
+   (Any number of either <rev> or <range>.  Parens are needed to make
+   it clear that "..." pertains to both <rev> and <range>.)
+
+   [(-p <parent>)...]
+   (Any number of option -p, each with one <parent> argument.)
+
+   git remote set-head <name> (-a | -d | <branch>)
+   (One and only one of "-a", "-d" or "<branch>" _must_ (no square
+   brackets) be provided.)
+
+ And a somewhat more contrived example:
+   --diff-filter=[(A|C|D|M|R|T|U|X|B)...[*]]
+   Here "=" is outside the brackets, because "--diff-filter=" is a
+   valid usage.  "*" has its own pair of brackets, because it can
+   (optionally) be specified only when one or more of the letters is
+   also provided.
diff --git a/Documentation/Makefile b/Documentation/Makefile
index 06b0c57..063fa69 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -1,12 +1,13 @@
 MAN1_TXT= \
 	$(filter-out $(addsuffix .txt, $(ARTICLES) $(SP_ARTICLES)), \
 		$(wildcard git-*.txt)) \
-	gitk.txt git.txt
+	gitk.txt gitweb.txt git.txt
 MAN5_TXT=gitattributes.txt gitignore.txt gitmodules.txt githooks.txt \
-	gitrepository-layout.txt
+	gitrepository-layout.txt gitweb.conf.txt
 MAN7_TXT=gitcli.txt gittutorial.txt gittutorial-2.txt \
 	gitcvs-migration.txt gitcore-tutorial.txt gitglossary.txt \
-	gitdiffcore.txt gitworkflows.txt
+	gitdiffcore.txt gitnamespaces.txt gitrevisions.txt gitworkflows.txt
+MAN7_TXT += gitcredentials.txt
 
 MAN_TXT = $(MAN1_TXT) $(MAN5_TXT) $(MAN7_TXT)
 MAN_XML=$(patsubst %.txt,%.xml,$(MAN_TXT))
@@ -17,8 +18,12 @@
 ARTICLES = howto-index
 ARTICLES += everyday
 ARTICLES += git-tools
+ARTICLES += git-bisect-lk2009
 # with their own formatting rules.
-SP_ARTICLES = howto/revert-branch-rebase howto/using-merge-subtree user-manual
+SP_ARTICLES = user-manual
+SP_ARTICLES += howto/revert-branch-rebase
+SP_ARTICLES += howto/using-merge-subtree
+SP_ARTICLES += howto/using-signed-tag-in-pull-request
 API_DOCS = $(patsubst %.txt,%,$(filter-out technical/api-index-skel.txt technical/api-index.txt, $(wildcard technical/api-*.txt)))
 SP_ARTICLES += $(API_DOCS)
 SP_ARTICLES += technical/api-index
@@ -45,8 +50,8 @@
 XMLTO_EXTRA =
 INSTALL?=install
 RM ?= rm -f
-DOC_REF = origin/man
-HTML_REF = origin/html
+MAN_REPO = ../../git-manpages
+HTML_REPO = ../../git-htmldocs
 
 infodir?=$(prefix)/share/info
 MAKEINFO=makeinfo
@@ -61,36 +66,20 @@
 -include ../config.mak
 
 #
-# For asciidoc ...
-#	-7.1.2,	no extra settings are needed.
-#	8.0-,	set ASCIIDOC8.
-#
-
-#
 # For docbook-xsl ...
-#	-1.68.1,	set ASCIIDOC_NO_ROFF? (based on changelog from 1.73.0)
-#	1.69.0,		no extra settings are needed?
+#	-1.68.1,	no extra settings are needed?
+#	1.69.0,		set ASCIIDOC_ROFF?
 #	1.69.1-1.71.0,	set DOCBOOK_SUPPRESS_SP?
-#	1.71.1,		no extra settings are needed?
+#	1.71.1,		set ASCIIDOC_ROFF?
 #	1.72.0,		set DOCBOOK_XSL_172.
-#	1.73.0-,	set ASCIIDOC_NO_ROFF
+#	1.73.0-,	no extra settings are needed
 #
 
-#
-# If you had been using DOCBOOK_XSL_172 in an attempt to get rid
-# of 'the ".ft C" problem' in your generated manpages, and you
-# instead ended up with weird characters around callouts, try
-# using ASCIIDOC_NO_ROFF instead (it works fine with ASCIIDOC8).
-#
-
-ifdef ASCIIDOC8
-ASCIIDOC_EXTRA += -a asciidoc7compatible -a no-inline-literal
-endif
 ifdef DOCBOOK_XSL_172
 ASCIIDOC_EXTRA += -a git-asciidoc-no-roff
 MANPAGE_XSL = manpage-1.72.xsl
 else
-	ifdef ASCIIDOC_NO_ROFF
+	ifndef ASCIIDOC_ROFF
 	# docbook-xsl after 1.72 needs the regular XSL, but will not
 	# pass-thru raw roff codes from asciidoc.conf, so turn them off.
 	ASCIIDOC_EXTRA += -a git-asciidoc-no-roff
@@ -103,18 +92,38 @@
 XMLTO_EXTRA += -m manpage-suppress-sp.xsl
 endif
 
+# Newer DocBook stylesheet emits warning cruft in the output when
+# this is not set, and if set it shows an absolute link.  Older
+# stylesheets simply ignore this parameter.
+#
+# Distros may want to use MAN_BASE_URL=file:///path/to/git/docs/
+# or similar.
+ifndef MAN_BASE_URL
+MAN_BASE_URL = file://$(htmldir)/
+endif
+XMLTO_EXTRA += -m manpage-base-url.xsl
+
+# If your target system uses GNU groff, it may try to render
+# apostrophes as a "pretty" apostrophe using unicode.  This breaks
+# cut&paste, so you should set GNU_ROFF to force them to be ASCII
+# apostrophes.  Unfortunately does not work with non-GNU roff.
+ifdef GNU_ROFF
+XMLTO_EXTRA += -m manpage-quote-apos.xsl
+endif
+
 SHELL_PATH ?= $(SHELL)
 # Shell quote;
 SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
 
-#
-# Please note that there is a minor bug in asciidoc.
-# The version after 6.0.3 _will_ include the patch found here:
-#   http://marc.theaimsgroup.com/?l=git&m=111558757202243&w=2
-#
-# Until that version is released you may have to apply the patch
-# yourself - yes, all 6 characters of it!
-#
+ifdef DEFAULT_PAGER
+DEFAULT_PAGER_SQ = $(subst ','\'',$(DEFAULT_PAGER))
+ASCIIDOC_EXTRA += -a 'git-default-pager=$(DEFAULT_PAGER_SQ)'
+endif
+
+ifdef DEFAULT_EDITOR
+DEFAULT_EDITOR_SQ = $(subst ','\'',$(DEFAULT_EDITOR))
+ASCIIDOC_EXTRA += -a 'git-default-editor=$(DEFAULT_EDITOR_SQ)'
+endif
 
 QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir
 QUIET_SUBDIR1  =
@@ -184,7 +193,7 @@
 install-html: html
 	'$(SHELL_PATH_SQ)' ./install-webdoc.sh $(DESTDIR)$(htmldir)
 
-../GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
+../GIT-VERSION-FILE: FORCE
 	$(QUIET_SUBDIR0)../ $(QUIET_SUBDIR1) GIT-VERSION-FILE
 
 -include ../GIT-VERSION-FILE
@@ -219,9 +228,11 @@
 clean:
 	$(RM) *.xml *.xml+ *.html *.html+ *.1 *.5 *.7
 	$(RM) *.texi *.texi+ *.texi++ git.info gitman.info
+	$(RM) *.pdf
 	$(RM) howto-index.txt howto/*.html doc.dep
 	$(RM) technical/api-*.html technical/api-index.txt
 	$(RM) $(cmds_txt) *.made
+	$(RM) manpage-base-url.xsl
 
 $(MAN_HTML): %.html : %.txt
 	$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
@@ -229,7 +240,10 @@
 		$(ASCIIDOC_EXTRA) -agit_version=$(GIT_VERSION) -o $@+ $< && \
 	mv $@+ $@
 
-%.1 %.5 %.7 : %.xml
+manpage-base-url.xsl: manpage-base-url.xsl.in
+	sed "s|@@MAN_BASE_URL@@|$(MAN_BASE_URL)|" $< > $@
+
+%.1 %.5 %.7 : %.xml manpage-base-url.xsl
 	$(QUIET_XMLTO)$(RM) $@ && \
 	xmlto -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $<
 
@@ -240,12 +254,15 @@
 	mv $@+ $@
 
 user-manual.xml: user-manual.txt user-manual.conf
-	$(QUIET_ASCIIDOC)$(ASCIIDOC) $(ASCIIDOC_EXTRA) -b docbook -d book $<
+	$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
+	$(ASCIIDOC) $(ASCIIDOC_EXTRA) -b docbook -d book -o $@+ $< && \
+	mv $@+ $@
 
 technical/api-index.txt: technical/api-index-skel.txt \
 	technical/api-index.sh $(patsubst %,%.txt,$(API_DOCS))
 	$(QUIET_GEN)cd technical && '$(SHELL_PATH_SQ)' ./api-index.sh
 
+technical/%.html: ASCIIDOC_EXTRA += -a git-relative-html-prefix=../
 $(patsubst %,%.html,$(API_DOCS) technical/api-index): %.html : %.txt
 	$(QUIET_ASCIIDOC)$(ASCIIDOC) -b xhtml11 -f asciidoc.conf \
 		$(ASCIIDOC_EXTRA) -agit_version=$(GIT_VERSION) $*.txt
@@ -253,8 +270,10 @@
 XSLT = docbook.xsl
 XSLTOPTS = --xinclude --stringparam html.stylesheet docbook-xsl.css
 
-user-manual.html: user-manual.xml
-	$(QUIET_XSLTPROC)xsltproc $(XSLTOPTS) -o $@ $(XSLT) $<
+user-manual.html: user-manual.xml $(XSLT)
+	$(QUIET_XSLTPROC)$(RM) $@+ $@ && \
+	xsltproc $(XSLTOPTS) -o $@+ $(XSLT) $< && \
+	mv $@+ $@
 
 git.info: user-manual.texi
 	$(QUIET_MAKEINFO)$(MAKEINFO) --no-split -o $@ user-manual.texi
@@ -297,6 +316,7 @@
 
 WEBDOC_DEST = /pub/software/scm/git/docs
 
+howto/%.html: ASCIIDOC_EXTRA += -a git-relative-html-prefix=../
 $(patsubst %.txt,%.html,$(wildcard howto/*.txt)): %.html : %.txt
 	$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
 	sed -e '1,/^$$/d' $< | $(ASCIIDOC) $(ASCIIDOC_EXTRA) -b xhtml11 - >$@+ && \
@@ -305,12 +325,23 @@
 install-webdoc : html
 	'$(SHELL_PATH_SQ)' ./install-webdoc.sh $(WEBDOC_DEST)
 
+# You must have a clone of git-htmldocs and git-manpages repositories
+# next to the git repository itself for the following to work.
+
 quick-install: quick-install-man
 
-quick-install-man:
-	'$(SHELL_PATH_SQ)' ./install-doc-quick.sh $(DOC_REF) $(DESTDIR)$(mandir)
+require-manrepo::
+	@if test ! -d $(MAN_REPO); \
+	then echo "git-manpages repository must exist at $(MAN_REPO)"; exit 1; fi
 
-quick-install-html:
-	'$(SHELL_PATH_SQ)' ./install-doc-quick.sh $(HTML_REF) $(DESTDIR)$(htmldir)
+quick-install-man: require-manrepo
+	'$(SHELL_PATH_SQ)' ./install-doc-quick.sh $(MAN_REPO) $(DESTDIR)$(mandir)
 
-.PHONY: .FORCE-GIT-VERSION-FILE
+require-htmlrepo::
+	@if test ! -d $(HTML_REPO); \
+	then echo "git-htmldocs repository must exist at $(HTML_REPO)"; exit 1; fi
+
+quick-install-html: require-htmlrepo
+	'$(SHELL_PATH_SQ)' ./install-doc-quick.sh $(HTML_REPO) $(DESTDIR)$(htmldir)
+
+.PHONY: FORCE
diff --git a/Documentation/RelNotes-1.5.6.3.txt b/Documentation/RelNotes-1.5.6.3.txt
deleted file mode 100644
index 9426112..0000000
--- a/Documentation/RelNotes-1.5.6.3.txt
+++ /dev/null
@@ -1,52 +0,0 @@
-GIT v1.5.6.3 Release Notes
-==========================
-
-Fixes since v1.5.6.2
---------------------
-
-* Setting core.sharerepository to traditional "true" value was supposed to make
-  the repository group writable but should not affect permission for others.
-  However, since 1.5.6, it was broken to drop permission for others when umask is
-  022, making the repository unreadable by others.
-
-* Setting GIT_TRACE will report spawning of external process via run_command().
-
-* Using an object with very deep delta chain pinned memory needed for extracting
-  intermediate base objects unnecessarily long, leading to excess memory usage.
-
-* Bash completion script did not notice '--' marker on the command
-  line and tried the relatively slow "ref completion" even when
-  completing arguments after one.
-
-* Registering a non-empty blob racily and then truncating the working
-  tree file for it confused "racy-git avoidance" logic into thinking
-  that the path is now unchanged.
-
-* The section that describes attributes related to git-archive were placed
-  in a wrong place in the gitattributes(5) manual page.
-
-* "git am" was not helpful to the users when it detected that the committer
-  information is not set up properly yet.
-
-* "git clone" had a leftover debugging fprintf().
-
-* "git clone -q" was not quiet enough as it used to and gave object count
-  and progress reports.
-
-* "git clone" marked downloaded packfile with .keep; this could be a
-  good thing if the remote side is well packed but otherwise not,
-  especially for a project that is not really big.
-
-* "git daemon" used to call syslog() from a signal handler, which
-  could raise signals of its own but generally is not reentrant.  This
-  was fixed by restructuring the code to report syslog() after the handler
-  returns.
-
-* When "git push" tries to remove a remote ref, and corresponding
-  tracking ref is missing, we used to report error (i.e. failure to
-  remove something that does not exist).
-
-* "git mailinfo" (hence "git am") did not handle commit log messages in a
-  MIME multipart mail correctly.
-
-Contains other various documentation fixes.
diff --git a/Documentation/RelNotes-1.6.0.2.txt b/Documentation/RelNotes-1.6.0.2.txt
deleted file mode 100644
index 51b32f5..0000000
--- a/Documentation/RelNotes-1.6.0.2.txt
+++ /dev/null
@@ -1,87 +0,0 @@
-GIT v1.6.0.2 Release Notes
-==========================
-
-Fixes since v1.6.0.1
---------------------
-
-* Installation on platforms that needs .exe suffix to git-* programs were
-  broken in 1.6.0.1.
-
-* Installation on filesystems without symbolic links support did not
-  work well.
-
-* In-tree documentations and test scripts now use "git foo" form to set a
-  better example, instead of the "git-foo" form (which is an acceptable
-  form if you have "PATH=$(git --exec-path):$PATH" in your script)
-
-* Many commands did not use the correct working tree location when used
-  with GIT_WORK_TREE environment settings.
-
-* Some systems needs to use compatibility fnmach and regex libraries
-  independent from each other; the compat/ area has been reorganized to
-  allow this.
-
-
-* "git apply --unidiff-zero" incorrectly applied a -U0 patch that inserts
-  a new line before the second line.
-
-* "git blame -c" did not exactly work like "git annotate" when range
-  boundaries are involved.
-
-* "git checkout file" when file is still unmerged checked out contents from
-  a random high order stage, which was confusing.
-
-* "git clone $there $here/" with extra trailing slashes after explicit
-  local directory name $here did not work as expected.
-
-* "git diff" on tracked contents with CRLF line endings did not drive "less"
-  intelligently when showing added or removed lines.
-
-* "git diff --dirstat -M" did not add changes in subdirectories up
-  correctly for renamed paths.
-
-* "git diff --cumulative" did not imply "--dirstat".
-
-* "git for-each-ref refs/heads/" did not work as expected.
-
-* "git gui" allowed users to feed patch without any context to be applied.
-
-* "git gui" botched parsing "diff" output when a line that begins with two
-  dashes and a space gets removed or a line that begins with two pluses
-  and a space gets added.
-
-* "git gui" translation updates and i18n fixes.
-
-* "git index-pack" is more careful against disk corruption while completing
-  a thin pack.
-
-* "git log -i --grep=pattern" did not ignore case; neither "git log -E
-  --grep=pattern" triggered extended regexp.
-
-* "git log --pretty="%ad" --date=short" did not use short format when
-  showing the timestamp.
-
-* "git log --author=author" match incorrectly matched with the
-  timestamp part of "author " line in commit objects.
-
-* "git log -F --author=author" did not work at all.
-
-* Build procedure for "git shell" that used stub versions of some
-  functions and globals was not understood by linkers on some platforms.
-
-* "git stash" was fooled by a stat-dirty but otherwise unmodified paths
-  and refused to work until the user refreshed the index.
-
-* "git svn" was broken on Perl before 5.8 with recent fixes to reduce
-  use of temporary files.
-
-* "git verify-pack -v" did not work correctly when given more than one
-  packfile.
-
-Also contains many documentation updates.
-
---
-exec >/var/tmp/1
-O=v1.6.0.1-78-g3632cfc
-echo O=$(git describe maint)
-git shortlog --no-merges $O..maint
diff --git a/Documentation/RelNotes-1.6.5.txt b/Documentation/RelNotes-1.6.5.txt
deleted file mode 100644
index 856047d..0000000
--- a/Documentation/RelNotes-1.6.5.txt
+++ /dev/null
@@ -1,51 +0,0 @@
-GIT v1.6.5 Release Notes
-========================
-
-In git 1.7.0, which is planned to be the release after 1.6.5, "git push"
-into a branch that is currently checked out will be refused by default.
-
-You can choose what should happen upon such a push by setting the
-configuration variable receive.denyCurrentBranch in the receiving
-repository.
-
-Also, "git push $there :$killed" to delete the branch $killed in a remote
-repository $there, when $killed branch is the current branch pointed at by
-its HEAD, will be refused by default.
-
-You can choose what should happen upon such a push by setting the
-configuration variable receive.denyDeleteCurrent in the receiving
-repository.
-
-To ease the transition plan, the receiving repository of such a
-push running this release will issue a big warning when the
-configuration variable is missing.  Please refer to:
-
-  http://git.or.cz/gitwiki/GitFaq#non-bare
-  http://thread.gmane.org/gmane.comp.version-control.git/107758/focus=108007
-
-for more details on the reason why this change is needed and the
-transition plan.
-
-Updates since v1.6.4
---------------------
-
-(subsystems)
-
-(portability)
-
-(performance)
-
-(usability, bells and whistles)
-
-(developers)
-
-Fixes since v1.6.4
-------------------
-
-# All of the fixes in v1.6.4.X maintenance series are included in this
-# release, unless otherwise noted.
-
-# Here are fixes that this release has, but have not been backported to
-# v1.6.4.X series.
-
-
diff --git a/Documentation/RelNotes-1.5.0.1.txt b/Documentation/RelNotes/1.5.0.1.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.0.1.txt
rename to Documentation/RelNotes/1.5.0.1.txt
diff --git a/Documentation/RelNotes-1.5.0.2.txt b/Documentation/RelNotes/1.5.0.2.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.0.2.txt
rename to Documentation/RelNotes/1.5.0.2.txt
diff --git a/Documentation/RelNotes-1.5.0.3.txt b/Documentation/RelNotes/1.5.0.3.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.0.3.txt
rename to Documentation/RelNotes/1.5.0.3.txt
diff --git a/Documentation/RelNotes-1.5.0.4.txt b/Documentation/RelNotes/1.5.0.4.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.0.4.txt
rename to Documentation/RelNotes/1.5.0.4.txt
diff --git a/Documentation/RelNotes-1.5.0.5.txt b/Documentation/RelNotes/1.5.0.5.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.0.5.txt
rename to Documentation/RelNotes/1.5.0.5.txt
diff --git a/Documentation/RelNotes-1.5.0.6.txt b/Documentation/RelNotes/1.5.0.6.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.0.6.txt
rename to Documentation/RelNotes/1.5.0.6.txt
diff --git a/Documentation/RelNotes-1.5.0.7.txt b/Documentation/RelNotes/1.5.0.7.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.0.7.txt
rename to Documentation/RelNotes/1.5.0.7.txt
diff --git a/Documentation/RelNotes-1.5.0.txt b/Documentation/RelNotes/1.5.0.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.0.txt
rename to Documentation/RelNotes/1.5.0.txt
diff --git a/Documentation/RelNotes-1.5.1.1.txt b/Documentation/RelNotes/1.5.1.1.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.1.1.txt
rename to Documentation/RelNotes/1.5.1.1.txt
diff --git a/Documentation/RelNotes-1.5.1.2.txt b/Documentation/RelNotes/1.5.1.2.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.1.2.txt
rename to Documentation/RelNotes/1.5.1.2.txt
diff --git a/Documentation/RelNotes-1.5.1.3.txt b/Documentation/RelNotes/1.5.1.3.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.1.3.txt
rename to Documentation/RelNotes/1.5.1.3.txt
diff --git a/Documentation/RelNotes-1.5.1.4.txt b/Documentation/RelNotes/1.5.1.4.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.1.4.txt
rename to Documentation/RelNotes/1.5.1.4.txt
diff --git a/Documentation/RelNotes-1.5.1.5.txt b/Documentation/RelNotes/1.5.1.5.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.1.5.txt
rename to Documentation/RelNotes/1.5.1.5.txt
diff --git a/Documentation/RelNotes-1.5.1.6.txt b/Documentation/RelNotes/1.5.1.6.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.1.6.txt
rename to Documentation/RelNotes/1.5.1.6.txt
diff --git a/Documentation/RelNotes-1.5.1.txt b/Documentation/RelNotes/1.5.1.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.1.txt
rename to Documentation/RelNotes/1.5.1.txt
diff --git a/Documentation/RelNotes-1.5.2.1.txt b/Documentation/RelNotes/1.5.2.1.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.2.1.txt
rename to Documentation/RelNotes/1.5.2.1.txt
diff --git a/Documentation/RelNotes-1.5.2.2.txt b/Documentation/RelNotes/1.5.2.2.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.2.2.txt
rename to Documentation/RelNotes/1.5.2.2.txt
diff --git a/Documentation/RelNotes-1.5.2.3.txt b/Documentation/RelNotes/1.5.2.3.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.2.3.txt
rename to Documentation/RelNotes/1.5.2.3.txt
diff --git a/Documentation/RelNotes-1.5.2.4.txt b/Documentation/RelNotes/1.5.2.4.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.2.4.txt
rename to Documentation/RelNotes/1.5.2.4.txt
diff --git a/Documentation/RelNotes-1.5.2.5.txt b/Documentation/RelNotes/1.5.2.5.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.2.5.txt
rename to Documentation/RelNotes/1.5.2.5.txt
diff --git a/Documentation/RelNotes-1.5.2.txt b/Documentation/RelNotes/1.5.2.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.2.txt
rename to Documentation/RelNotes/1.5.2.txt
diff --git a/Documentation/RelNotes-1.5.3.1.txt b/Documentation/RelNotes/1.5.3.1.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.3.1.txt
rename to Documentation/RelNotes/1.5.3.1.txt
diff --git a/Documentation/RelNotes-1.5.3.2.txt b/Documentation/RelNotes/1.5.3.2.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.3.2.txt
rename to Documentation/RelNotes/1.5.3.2.txt
diff --git a/Documentation/RelNotes-1.5.3.3.txt b/Documentation/RelNotes/1.5.3.3.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.3.3.txt
rename to Documentation/RelNotes/1.5.3.3.txt
diff --git a/Documentation/RelNotes-1.5.3.4.txt b/Documentation/RelNotes/1.5.3.4.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.3.4.txt
rename to Documentation/RelNotes/1.5.3.4.txt
diff --git a/Documentation/RelNotes-1.5.3.5.txt b/Documentation/RelNotes/1.5.3.5.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.3.5.txt
rename to Documentation/RelNotes/1.5.3.5.txt
diff --git a/Documentation/RelNotes-1.5.3.6.txt b/Documentation/RelNotes/1.5.3.6.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.3.6.txt
rename to Documentation/RelNotes/1.5.3.6.txt
diff --git a/Documentation/RelNotes-1.5.3.7.txt b/Documentation/RelNotes/1.5.3.7.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.3.7.txt
rename to Documentation/RelNotes/1.5.3.7.txt
diff --git a/Documentation/RelNotes-1.5.3.8.txt b/Documentation/RelNotes/1.5.3.8.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.3.8.txt
rename to Documentation/RelNotes/1.5.3.8.txt
diff --git a/Documentation/RelNotes-1.5.3.txt b/Documentation/RelNotes/1.5.3.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.3.txt
rename to Documentation/RelNotes/1.5.3.txt
diff --git a/Documentation/RelNotes-1.5.4.1.txt b/Documentation/RelNotes/1.5.4.1.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.4.1.txt
rename to Documentation/RelNotes/1.5.4.1.txt
diff --git a/Documentation/RelNotes-1.5.4.2.txt b/Documentation/RelNotes/1.5.4.2.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.4.2.txt
rename to Documentation/RelNotes/1.5.4.2.txt
diff --git a/Documentation/RelNotes-1.5.4.3.txt b/Documentation/RelNotes/1.5.4.3.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.4.3.txt
rename to Documentation/RelNotes/1.5.4.3.txt
diff --git a/Documentation/RelNotes-1.5.4.4.txt b/Documentation/RelNotes/1.5.4.4.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.4.4.txt
rename to Documentation/RelNotes/1.5.4.4.txt
diff --git a/Documentation/RelNotes-1.5.4.5.txt b/Documentation/RelNotes/1.5.4.5.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.4.5.txt
rename to Documentation/RelNotes/1.5.4.5.txt
diff --git a/Documentation/RelNotes-1.5.4.6.txt b/Documentation/RelNotes/1.5.4.6.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.4.6.txt
rename to Documentation/RelNotes/1.5.4.6.txt
diff --git a/Documentation/RelNotes-1.5.4.7.txt b/Documentation/RelNotes/1.5.4.7.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.4.7.txt
rename to Documentation/RelNotes/1.5.4.7.txt
diff --git a/Documentation/RelNotes-1.5.4.txt b/Documentation/RelNotes/1.5.4.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.4.txt
rename to Documentation/RelNotes/1.5.4.txt
diff --git a/Documentation/RelNotes-1.5.5.1.txt b/Documentation/RelNotes/1.5.5.1.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.5.1.txt
rename to Documentation/RelNotes/1.5.5.1.txt
diff --git a/Documentation/RelNotes-1.5.5.2.txt b/Documentation/RelNotes/1.5.5.2.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.5.2.txt
rename to Documentation/RelNotes/1.5.5.2.txt
diff --git a/Documentation/RelNotes-1.5.5.3.txt b/Documentation/RelNotes/1.5.5.3.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.5.3.txt
rename to Documentation/RelNotes/1.5.5.3.txt
diff --git a/Documentation/RelNotes-1.5.5.4.txt b/Documentation/RelNotes/1.5.5.4.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.5.4.txt
rename to Documentation/RelNotes/1.5.5.4.txt
diff --git a/Documentation/RelNotes-1.5.5.5.txt b/Documentation/RelNotes/1.5.5.5.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.5.5.txt
rename to Documentation/RelNotes/1.5.5.5.txt
diff --git a/Documentation/RelNotes-1.5.5.6.txt b/Documentation/RelNotes/1.5.5.6.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.5.6.txt
rename to Documentation/RelNotes/1.5.5.6.txt
diff --git a/Documentation/RelNotes-1.5.5.txt b/Documentation/RelNotes/1.5.5.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.5.txt
rename to Documentation/RelNotes/1.5.5.txt
diff --git a/Documentation/RelNotes-1.5.6.1.txt b/Documentation/RelNotes/1.5.6.1.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.6.1.txt
rename to Documentation/RelNotes/1.5.6.1.txt
diff --git a/Documentation/RelNotes-1.5.6.2.txt b/Documentation/RelNotes/1.5.6.2.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.6.2.txt
rename to Documentation/RelNotes/1.5.6.2.txt
diff --git a/Documentation/RelNotes/1.5.6.3.txt b/Documentation/RelNotes/1.5.6.3.txt
new file mode 100644
index 0000000..f61dd35
--- /dev/null
+++ b/Documentation/RelNotes/1.5.6.3.txt
@@ -0,0 +1,52 @@
+GIT v1.5.6.3 Release Notes
+==========================
+
+Fixes since v1.5.6.2
+--------------------
+
+* Setting core.sharedrepository to traditional "true" value was supposed to make
+  the repository group writable but should not affect permission for others.
+  However, since 1.5.6, it was broken to drop permission for others when umask is
+  022, making the repository unreadable by others.
+
+* Setting GIT_TRACE will report spawning of external process via run_command().
+
+* Using an object with very deep delta chain pinned memory needed for extracting
+  intermediate base objects unnecessarily long, leading to excess memory usage.
+
+* Bash completion script did not notice '--' marker on the command
+  line and tried the relatively slow "ref completion" even when
+  completing arguments after one.
+
+* Registering a non-empty blob racily and then truncating the working
+  tree file for it confused "racy-git avoidance" logic into thinking
+  that the path is now unchanged.
+
+* The section that describes attributes related to git-archive were placed
+  in a wrong place in the gitattributes(5) manual page.
+
+* "git am" was not helpful to the users when it detected that the committer
+  information is not set up properly yet.
+
+* "git clone" had a leftover debugging fprintf().
+
+* "git clone -q" was not quiet enough as it used to and gave object count
+  and progress reports.
+
+* "git clone" marked downloaded packfile with .keep; this could be a
+  good thing if the remote side is well packed but otherwise not,
+  especially for a project that is not really big.
+
+* "git daemon" used to call syslog() from a signal handler, which
+  could raise signals of its own but generally is not reentrant.  This
+  was fixed by restructuring the code to report syslog() after the handler
+  returns.
+
+* When "git push" tries to remove a remote ref, and corresponding
+  tracking ref is missing, we used to report error (i.e. failure to
+  remove something that does not exist).
+
+* "git mailinfo" (hence "git am") did not handle commit log messages in a
+  MIME multipart mail correctly.
+
+Contains other various documentation fixes.
diff --git a/Documentation/RelNotes-1.5.6.4.txt b/Documentation/RelNotes/1.5.6.4.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.6.4.txt
rename to Documentation/RelNotes/1.5.6.4.txt
diff --git a/Documentation/RelNotes-1.5.6.5.txt b/Documentation/RelNotes/1.5.6.5.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.6.5.txt
rename to Documentation/RelNotes/1.5.6.5.txt
diff --git a/Documentation/RelNotes-1.5.6.6.txt b/Documentation/RelNotes/1.5.6.6.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.6.6.txt
rename to Documentation/RelNotes/1.5.6.6.txt
diff --git a/Documentation/RelNotes-1.5.6.txt b/Documentation/RelNotes/1.5.6.txt
similarity index 100%
rename from Documentation/RelNotes-1.5.6.txt
rename to Documentation/RelNotes/1.5.6.txt
diff --git a/Documentation/RelNotes-1.6.0.1.txt b/Documentation/RelNotes/1.6.0.1.txt
similarity index 100%
rename from Documentation/RelNotes-1.6.0.1.txt
rename to Documentation/RelNotes/1.6.0.1.txt
diff --git a/Documentation/RelNotes/1.6.0.2.txt b/Documentation/RelNotes/1.6.0.2.txt
new file mode 100644
index 0000000..e1e24b3
--- /dev/null
+++ b/Documentation/RelNotes/1.6.0.2.txt
@@ -0,0 +1,87 @@
+GIT v1.6.0.2 Release Notes
+==========================
+
+Fixes since v1.6.0.1
+--------------------
+
+* Installation on platforms that needs .exe suffix to git-* programs were
+  broken in 1.6.0.1.
+
+* Installation on filesystems without symbolic links support did not
+  work well.
+
+* In-tree documentations and test scripts now use "git foo" form to set a
+  better example, instead of the "git-foo" form (which is an acceptable
+  form if you have "PATH=$(git --exec-path):$PATH" in your script)
+
+* Many commands did not use the correct working tree location when used
+  with GIT_WORK_TREE environment settings.
+
+* Some systems need to use compatibility fnmatch and regex libraries
+  independent from each other; the compat/ area has been reorganized to
+  allow this.
+
+
+* "git apply --unidiff-zero" incorrectly applied a -U0 patch that inserts
+  a new line before the second line.
+
+* "git blame -c" did not exactly work like "git annotate" when range
+  boundaries are involved.
+
+* "git checkout file" when file is still unmerged checked out contents from
+  a random high order stage, which was confusing.
+
+* "git clone $there $here/" with extra trailing slashes after explicit
+  local directory name $here did not work as expected.
+
+* "git diff" on tracked contents with CRLF line endings did not drive "less"
+  intelligently when showing added or removed lines.
+
+* "git diff --dirstat -M" did not add changes in subdirectories up
+  correctly for renamed paths.
+
+* "git diff --cumulative" did not imply "--dirstat".
+
+* "git for-each-ref refs/heads/" did not work as expected.
+
+* "git gui" allowed users to feed patch without any context to be applied.
+
+* "git gui" botched parsing "diff" output when a line that begins with two
+  dashes and a space gets removed or a line that begins with two pluses
+  and a space gets added.
+
+* "git gui" translation updates and i18n fixes.
+
+* "git index-pack" is more careful against disk corruption while completing
+  a thin pack.
+
+* "git log -i --grep=pattern" did not ignore case; neither "git log -E
+  --grep=pattern" triggered extended regexp.
+
+* "git log --pretty="%ad" --date=short" did not use short format when
+  showing the timestamp.
+
+* "git log --author=author" match incorrectly matched with the
+  timestamp part of "author " line in commit objects.
+
+* "git log -F --author=author" did not work at all.
+
+* Build procedure for "git shell" that used stub versions of some
+  functions and globals was not understood by linkers on some platforms.
+
+* "git stash" was fooled by a stat-dirty but otherwise unmodified paths
+  and refused to work until the user refreshed the index.
+
+* "git svn" was broken on Perl before 5.8 with recent fixes to reduce
+  use of temporary files.
+
+* "git verify-pack -v" did not work correctly when given more than one
+  packfile.
+
+Also contains many documentation updates.
+
+--
+exec >/var/tmp/1
+O=v1.6.0.1-78-g3632cfc
+echo O=$(git describe maint)
+git shortlog --no-merges $O..maint
diff --git a/Documentation/RelNotes-1.6.0.3.txt b/Documentation/RelNotes/1.6.0.3.txt
similarity index 100%
rename from Documentation/RelNotes-1.6.0.3.txt
rename to Documentation/RelNotes/1.6.0.3.txt
diff --git a/Documentation/RelNotes-1.6.0.4.txt b/Documentation/RelNotes/1.6.0.4.txt
similarity index 100%
rename from Documentation/RelNotes-1.6.0.4.txt
rename to Documentation/RelNotes/1.6.0.4.txt
diff --git a/Documentation/RelNotes-1.6.0.5.txt b/Documentation/RelNotes/1.6.0.5.txt
similarity index 100%
rename from Documentation/RelNotes-1.6.0.5.txt
rename to Documentation/RelNotes/1.6.0.5.txt
diff --git a/Documentation/RelNotes-1.6.0.6.txt b/Documentation/RelNotes/1.6.0.6.txt
similarity index 100%
rename from Documentation/RelNotes-1.6.0.6.txt
rename to Documentation/RelNotes/1.6.0.6.txt
diff --git a/Documentation/RelNotes-1.6.0.txt b/Documentation/RelNotes/1.6.0.txt
similarity index 100%
rename from Documentation/RelNotes-1.6.0.txt
rename to Documentation/RelNotes/1.6.0.txt
diff --git a/Documentation/RelNotes-1.6.1.1.txt b/Documentation/RelNotes/1.6.1.1.txt
similarity index 100%
rename from Documentation/RelNotes-1.6.1.1.txt
rename to Documentation/RelNotes/1.6.1.1.txt
diff --git a/Documentation/RelNotes-1.6.1.2.txt b/Documentation/RelNotes/1.6.1.2.txt
similarity index 100%
rename from Documentation/RelNotes-1.6.1.2.txt
rename to Documentation/RelNotes/1.6.1.2.txt
diff --git a/Documentation/RelNotes-1.6.1.3.txt b/Documentation/RelNotes/1.6.1.3.txt
similarity index 100%
rename from Documentation/RelNotes-1.6.1.3.txt
rename to Documentation/RelNotes/1.6.1.3.txt
diff --git a/Documentation/RelNotes-1.6.1.4.txt b/Documentation/RelNotes/1.6.1.4.txt
similarity index 100%
rename from Documentation/RelNotes-1.6.1.4.txt
rename to Documentation/RelNotes/1.6.1.4.txt
diff --git a/Documentation/RelNotes-1.6.1.txt b/Documentation/RelNotes/1.6.1.txt
similarity index 100%
rename from Documentation/RelNotes-1.6.1.txt
rename to Documentation/RelNotes/1.6.1.txt
diff --git a/Documentation/RelNotes-1.6.2.1.txt b/Documentation/RelNotes/1.6.2.1.txt
similarity index 100%
rename from Documentation/RelNotes-1.6.2.1.txt
rename to Documentation/RelNotes/1.6.2.1.txt
diff --git a/Documentation/RelNotes-1.6.2.2.txt b/Documentation/RelNotes/1.6.2.2.txt
similarity index 100%
rename from Documentation/RelNotes-1.6.2.2.txt
rename to Documentation/RelNotes/1.6.2.2.txt
diff --git a/Documentation/RelNotes-1.6.2.3.txt b/Documentation/RelNotes/1.6.2.3.txt
similarity index 100%
rename from Documentation/RelNotes-1.6.2.3.txt
rename to Documentation/RelNotes/1.6.2.3.txt
diff --git a/Documentation/RelNotes-1.6.2.4.txt b/Documentation/RelNotes/1.6.2.4.txt
similarity index 100%
rename from Documentation/RelNotes-1.6.2.4.txt
rename to Documentation/RelNotes/1.6.2.4.txt
diff --git a/Documentation/RelNotes-1.6.2.5.txt b/Documentation/RelNotes/1.6.2.5.txt
similarity index 100%
rename from Documentation/RelNotes-1.6.2.5.txt
rename to Documentation/RelNotes/1.6.2.5.txt
diff --git a/Documentation/RelNotes-1.6.2.txt b/Documentation/RelNotes/1.6.2.txt
similarity index 100%
rename from Documentation/RelNotes-1.6.2.txt
rename to Documentation/RelNotes/1.6.2.txt
diff --git a/Documentation/RelNotes-1.6.3.1.txt b/Documentation/RelNotes/1.6.3.1.txt
similarity index 100%
rename from Documentation/RelNotes-1.6.3.1.txt
rename to Documentation/RelNotes/1.6.3.1.txt
diff --git a/Documentation/RelNotes-1.6.3.2.txt b/Documentation/RelNotes/1.6.3.2.txt
similarity index 100%
rename from Documentation/RelNotes-1.6.3.2.txt
rename to Documentation/RelNotes/1.6.3.2.txt
diff --git a/Documentation/RelNotes-1.6.3.3.txt b/Documentation/RelNotes/1.6.3.3.txt
similarity index 100%
rename from Documentation/RelNotes-1.6.3.3.txt
rename to Documentation/RelNotes/1.6.3.3.txt
diff --git a/Documentation/RelNotes-1.6.3.4.txt b/Documentation/RelNotes/1.6.3.4.txt
similarity index 100%
rename from Documentation/RelNotes-1.6.3.4.txt
rename to Documentation/RelNotes/1.6.3.4.txt
diff --git a/Documentation/RelNotes-1.6.3.txt b/Documentation/RelNotes/1.6.3.txt
similarity index 100%
rename from Documentation/RelNotes-1.6.3.txt
rename to Documentation/RelNotes/1.6.3.txt
diff --git a/Documentation/RelNotes/1.6.4.1.txt b/Documentation/RelNotes/1.6.4.1.txt
new file mode 100644
index 0000000..e439e45
--- /dev/null
+++ b/Documentation/RelNotes/1.6.4.1.txt
@@ -0,0 +1,46 @@
+GIT v1.6.4.1 Release Notes
+==========================
+
+Fixes since v1.6.4
+------------------
+
+ * An unquoted value in the configuration file, when it contains more than
+   one whitespaces in a row, got them replaced with a single space.
+
+ * "git am" used to accept a single piece of e-mail per file (not a mbox)
+   as its input, but multiple input format support in v1.6.4 broke it.
+   Apparently many people have been depending on this feature.
+
+ * The short help text for "git filter-branch" command was a single long
+   line, wrapped by terminals, and was hard to read.
+
+ * The "recursive" strategy of "git merge" segfaulted when a merge has
+   more than one merge-bases, and merging of these merge-bases involves
+   a rename/rename or a rename/add conflict.
+
+ * "git pull --rebase" did not use the right fork point when the
+   repository has already fetched from the upstream that rewinds the
+   branch it is based on in an earlier fetch.
+
+ * Explain the concept of fast-forward more fully in "git push"
+   documentation, and hint to refer to it from an error message when the
+   command refuses an update to protect the user.
+
+ * The default value for pack.deltacachesize, used by "git repack", is now
+   256M, instead of unbounded.  Otherwise a repack of a moderately sized
+   repository would needlessly eat into swap.
+
+ * Document how "git repack" (hence "git gc") interacts with a repository
+   that borrows its objects from other repositories (e.g. ones created by
+   "git clone -s").
+
+ * "git show" on an annotated tag lacked a delimiting blank line between
+   the tag itself and the contents of the object it tags.
+
+ * "git verify-pack -v" erroneously reported number of objects with too
+   deep delta depths as "chain length 0" objects.
+
+ * Long names of authors and committers outside US-ASCII were sometimes
+   incorrectly shown in "gitweb".
+
+Other minor documentation updates are included.
diff --git a/Documentation/RelNotes/1.6.4.2.txt b/Documentation/RelNotes/1.6.4.2.txt
new file mode 100644
index 0000000..c11ec01
--- /dev/null
+++ b/Documentation/RelNotes/1.6.4.2.txt
@@ -0,0 +1,32 @@
+GIT v1.6.4.2 Release Notes
+==========================
+
+Fixes since v1.6.4.1
+--------------------
+
+* --date=relative output between 1 and 5 years ago rounded the number of
+    years when saying X years Y months ago, instead of rounding it down.
+
+* "git add -p" did not handle changes in executable bits correctly
+  (a regression around 1.6.3).
+
+* "git apply" did not honor GNU diff's convention to mark the creation/deletion
+  event with UNIX epoch timestamp on missing side.
+
+* "git checkout" incorrectly removed files in a directory pointed by a
+  symbolic link during a branch switch that replaces a directory with
+  a symbolic link.
+
+* "git clean -d -f" happily descended into a subdirectory that is managed by a
+  separate git repository.  It now requires two -f options for safety.
+
+* "git fetch/push" over http transports had two rather grave bugs.
+
+* "git format-patch --cover-letter" did not prepare the cover letter file
+  for use with non-ASCII strings when there are the series contributors with
+  non-ASCII names.
+
+* "git pull origin branch" and "git fetch origin && git merge origin/branch"
+  left different merge messages in the resulting commit.
+
+Other minor documentation updates are included.
diff --git a/Documentation/RelNotes/1.6.4.3.txt b/Documentation/RelNotes/1.6.4.3.txt
new file mode 100644
index 0000000..5643e65
--- /dev/null
+++ b/Documentation/RelNotes/1.6.4.3.txt
@@ -0,0 +1,29 @@
+GIT v1.6.4.3 Release Notes
+==========================
+
+Fixes since v1.6.4.2
+--------------------
+
+* "git clone" from an empty repository gave unnecessary error message,
+  even though it did everything else correctly.
+
+* "git cvsserver" invoked git commands via "git-foo" style, which has long
+  been deprecated.
+
+* "git fetch" and "git clone" had an extra sanity check to verify the
+  presence of the corresponding *.pack file before downloading *.idx
+  file by issuing a HEAD request.  Github server however sometimes
+  gave 500 (Internal server error) response to HEAD even if a GET
+  request for *.pack file to the same URL would have succeeded, and broke
+  clone over HTTP from some of their repositories.  As a workaround, this
+  verification has been removed (as it is not absolutely necessary).
+
+* "git grep" did not like relative pathname to refer outside the current
+  directory when run from a subdirectory.
+
+* an error message from "git push" was formatted in a very ugly way.
+
+* "git svn" did not quote the subversion user name correctly when
+  running its author-prog helper program.
+
+Other minor documentation updates are included.
diff --git a/Documentation/RelNotes/1.6.4.4.txt b/Documentation/RelNotes/1.6.4.4.txt
new file mode 100644
index 0000000..0ead45f
--- /dev/null
+++ b/Documentation/RelNotes/1.6.4.4.txt
@@ -0,0 +1,26 @@
+GIT v1.6.4.4 Release Notes
+==========================
+
+Fixes since v1.6.4.4
+--------------------
+
+* The workaround for Github server that sometimes gave 500 (Internal server
+  error) response to HEAD requests in 1.6.4.3 introduced a regression that
+  caused re-fetching projects over http to segfault in certain cases due
+  to uninitialized pointer being freed.
+
+* "git pull" on an unborn branch used to consider anything in the work
+  tree and the index discardable.
+
+* "git diff -b/w" did not work well on the incomplete line at the end of
+  the file, due to an incorrect hashing of lines in the low-level xdiff
+  routines.
+
+* "git checkout-index --prefix=$somewhere" used to work when $somewhere is
+  a symbolic link to a directory elsewhere, but v1.6.4.2 broke it.
+
+* "git unpack-objects --strict", invoked when receive.fsckobjects
+  configuration is set in the receiving repository of "git push", did not
+  properly check the objects, especially the submodule links, it received.
+
+Other minor documentation updates are included.
diff --git a/Documentation/RelNotes/1.6.4.5.txt b/Documentation/RelNotes/1.6.4.5.txt
new file mode 100644
index 0000000..eb6307d
--- /dev/null
+++ b/Documentation/RelNotes/1.6.4.5.txt
@@ -0,0 +1,20 @@
+Git v1.6.4.5 Release Notes
+==========================
+
+Fixes since v1.6.4.4
+--------------------
+
+ * Simplified base85 implementation.
+
+ * An overlong line after ".gitdir: " in a git file caused out of bounds
+   access to an array on the stack.
+
+ * "git count-objects" did not handle packs larger than 4G.
+
+ * "git rev-parse --parseopt --stop-at-non-option" did not stop at non option
+   when --keep-dashdash was in effect.
+
+ * "gitweb" can sometimes be tricked into parrotting a filename argument
+   given in a request without properly quoting.
+
+Other minor fixes and documentation updates are included.
diff --git a/Documentation/RelNotes-1.6.4.txt b/Documentation/RelNotes/1.6.4.txt
similarity index 100%
rename from Documentation/RelNotes-1.6.4.txt
rename to Documentation/RelNotes/1.6.4.txt
diff --git a/Documentation/RelNotes/1.6.5.1.txt b/Documentation/RelNotes/1.6.5.1.txt
new file mode 100644
index 0000000..309ba18
--- /dev/null
+++ b/Documentation/RelNotes/1.6.5.1.txt
@@ -0,0 +1,20 @@
+GIT v1.6.5.1 Release Notes
+==========================
+
+Fixes since v1.6.5
+------------------
+
+ * An corrupt pack could make codepath to read objects into an
+   infinite loop.
+
+ * Download throughput display was always shown in KiB/s but on fast links
+   it is more appropriate to show it in MiB/s.
+
+ * "git grep -f filename" used uninitialized variable and segfaulted.
+
+ * "git clone -b branch" gave a wrong commit object name to post-checkout
+   hook.
+
+ * "git pull" over http did not work on msys.
+
+Other minor documentation updates are included.
diff --git a/Documentation/RelNotes/1.6.5.2.txt b/Documentation/RelNotes/1.6.5.2.txt
new file mode 100644
index 0000000..aa7ccce
--- /dev/null
+++ b/Documentation/RelNotes/1.6.5.2.txt
@@ -0,0 +1,19 @@
+GIT v1.6.5.2 Release Notes
+==========================
+
+Fixes since v1.6.5.1
+--------------------
+
+ * Installation of templates triggered a bug in busybox when using tar
+   implementation from it.
+
+ * "git add -i" incorrectly ignored paths that are already in the index
+   if they matched .gitignore patterns.
+
+ * "git describe --always" should have produced some output even there
+   were no tags in the repository, but it didn't.
+
+ * "git ls-files" when showing tracked files incorrectly paid attention
+   to the exclude patterns.
+
+Other minor documentation updates are included.
diff --git a/Documentation/RelNotes/1.6.5.3.txt b/Documentation/RelNotes/1.6.5.3.txt
new file mode 100644
index 0000000..b2fad1b
--- /dev/null
+++ b/Documentation/RelNotes/1.6.5.3.txt
@@ -0,0 +1,63 @@
+Git v1.6.5.3 Release Notes
+==========================
+
+Fixes since v1.6.5.2
+--------------------
+
+ * info/grafts file didn't ignore trailing CR at the end of lines.
+
+ * Packages generated on newer FC were unreadable by older versions of
+   RPM as the new default is to use stronger hash.
+
+ * output from "git blame" was unreadable when the file ended in an
+   incomplete line.
+
+ * "git add -i/-p" didn't handle deletion of empty files correctly.
+
+ * "git clone" takes up to two parameters, but did not complain when
+   given more arguments than necessary and silently ignored them.
+
+ * "git cvsimport" did not read files given as command line arguments
+   correctly when it is run from a subdirectory.
+
+ * "git diff --color-words -U0" didn't work correctly.
+
+ * The handling of blank lines at the end of file by "git diff/apply
+   --whitespace" was inconsistent with the other kinds of errors.
+   They are now colored, warned against, and fixed the same way as others.
+
+ * There was no way to allow blank lines at the end of file without
+   allowing extra blanks at the end of lines.  You can use blank-at-eof
+   and blank-at-eol whitespace error class to specify them separately.
+   The old trailing-space error class is now a short-hand to set both.
+
+ * "-p" option to "git format-patch" was supposed to suppress diffstat
+   generation, but it was broken since 1.6.1.
+
+ * "git imap-send" did not compile cleanly with newer OpenSSL.
+
+ * "git help -a" outside of a git repository was broken.
+
+ * "git ls-files -i" was supposed to be inverse of "git ls-files" without -i
+   with respect to exclude patterns, but it was broken since 1.6.5.2.
+
+ * "git ls-remote" outside of a git repository over http was broken.
+
+ * "git rebase -i" gave bogus error message when the command word was
+   misspelled.
+
+ * "git receive-pack" that is run in response to "git push" did not run
+   garbage collection nor update-server-info, but in larger hosting sites,
+   these almost always need to be run.  To help site administrators, the
+   command now runs "gc --auto" and "u-s-i" by setting receive.autogc
+   and receive.updateserverinfo configuration variables, respectively.
+
+ * Release notes spelled the package name with incorrect capitalization.
+
+ * "gitweb" did not escape non-ascii characters correctly in the URL.
+
+ * "gitweb" showed "patch" link even for merge commits.
+
+ * "gitweb" showed incorrect links for blob line numbers in pathinfo mode.
+
+Other minor documentation updates are included.
diff --git a/Documentation/RelNotes/1.6.5.4.txt b/Documentation/RelNotes/1.6.5.4.txt
new file mode 100644
index 0000000..d3a2a3e
--- /dev/null
+++ b/Documentation/RelNotes/1.6.5.4.txt
@@ -0,0 +1,32 @@
+Git v1.6.5.4 Release Notes
+==========================
+
+Fixes since v1.6.5.3
+--------------------
+
+ * "git help" (without argument) used to check if you are in a directory
+   under git control. There was no breakage in behaviour per-se, but this
+   was unnecessary.
+
+ * "git prune-packed" gave progress output even when its standard error is
+   not connected to a terminal; this caused cron jobs that run it to
+   produce crufts.
+
+ * "git pack-objects --all-progress" is an option to ask progress output
+   from write-object phase _if_ progress output were to be produced, and
+   shouldn't have forced the progress output.
+
+ * "git apply -p<n> --directory=<elsewhere>" did not work well for a
+   non-default value of n.
+
+ * "git merge foo HEAD" was misparsed as an old-style invocation of the
+   command and produced a confusing error message.  As it does not specify
+   any other branch to merge, it shouldn't be mistaken as such.  We will
+   remove the old style "git merge <message> HEAD <commit>..."  syntax in
+   future versions, but not in this release,
+
+ * "git merge -m <message> <branch>..." added the standard merge message
+   on its own after user-supplied message, which should have overridden the
+   standard one.
+
+Other minor documentation updates are included.
diff --git a/Documentation/RelNotes/1.6.5.5.txt b/Documentation/RelNotes/1.6.5.5.txt
new file mode 100644
index 0000000..ecfc57d
--- /dev/null
+++ b/Documentation/RelNotes/1.6.5.5.txt
@@ -0,0 +1,49 @@
+Git v1.6.5.5 Release Notes
+==========================
+
+Fixes since v1.6.5.4
+--------------------
+
+ * Manual pages can be formatted with older xmlto again.
+
+ * GREP_OPTIONS exported from user's environment could have broken
+   our scripted commands.
+
+ * In configuration files, a few variables that name paths can begin with
+   ~/ and ~username/ and they are expanded as expected.  This is not a
+   bugfix but 1.6.6 will have this and without backporting users cannot
+   easily use the same ~/.gitconfig across versions.
+
+ * "git diff -B -M" did the same computation to hash lines of contents
+   twice, and held onto memory after it has used the data in it
+   unnecessarily before it freed.
+
+ * "git diff -B" and "git diff --dirstat" was not counting newly added
+   contents correctly.
+
+ * "git format-patch revisions... -- path" issued an incorrect error
+   message that suggested to use "--" on the command line when path
+   does not exist in the current work tree (it is a separate matter if
+   it makes sense to limit format-patch with pathspecs like that
+   without using the --full-diff option).
+
+ * "git grep -F -i StRiNg" did not work as expected.
+
+ * Enumeration of available merge strategies iterated over the list of
+   commands in a wrong way, sometimes producing an incorrect result.
+
+ * "git shortlog" did not honor the "encoding" header embedded in the
+   commit object like "git log" did.
+
+ * Reading progress messages that come from the remote side while running
+   "git pull" is given precedence over reading the actual pack data to
+   prevent garbled progress message on the user's terminal.
+
+ * "git rebase" got confused when the log message began with certain
+   strings that looked like Subject:, Date: or From: header.
+
+ * "git reset" accidentally run in .git/ directory checked out the
+   work tree contents in there.
+
+
+Other minor documentation updates are included.
diff --git a/Documentation/RelNotes/1.6.5.6.txt b/Documentation/RelNotes/1.6.5.6.txt
new file mode 100644
index 0000000..a9eaf76
--- /dev/null
+++ b/Documentation/RelNotes/1.6.5.6.txt
@@ -0,0 +1,23 @@
+Git v1.6.5.6 Release Notes
+==========================
+
+Fixes since v1.6.5.5
+--------------------
+
+ * "git add -p" had a regression since v1.6.5.3 that broke deletion of
+   non-empty files.
+
+ * "git archive -o o.zip -- Makefile" produced an archive in o.zip
+   but in POSIX tar format.
+
+ * Error message given to "git pull --rebase" when the user didn't give
+   enough clue as to what branch to integrate with still talked about
+   "merging with" the branch.
+
+ * Error messages given by "git merge" when the merge resulted in a
+   fast-forward still were in plumbing lingo, even though in v1.6.5
+   we reworded messages in other cases.
+
+ * The post-upload-hook run by upload-pack in response to "git fetch" has
+   been removed, due to security concerns (the hook first appeared in
+   1.6.5).
diff --git a/Documentation/RelNotes/1.6.5.7.txt b/Documentation/RelNotes/1.6.5.7.txt
new file mode 100644
index 0000000..dc5302c
--- /dev/null
+++ b/Documentation/RelNotes/1.6.5.7.txt
@@ -0,0 +1,19 @@
+Git v1.6.5.7 Release Notes
+==========================
+
+Fixes since v1.6.5.6
+--------------------
+
+* If a user specifies a color for a <slot> (i.e. a class of things to show
+  in a particular color) that is known only by newer versions of git
+  (e.g. "color.diff.func" was recently added for upcoming 1.6.6 release),
+  an older version of git should just ignore them.  Instead we diagnosed
+  it as an error.
+
+* With help.autocorrect set to non-zero value, the logic to guess typos
+  in the subcommand name misfired and ran a random nonsense command.
+
+* If a command is run with an absolute path as a pathspec inside a bare
+  repository, e.g. "rev-list HEAD -- /home", the code tried to run
+  strlen() on NULL, which is the result of get_git_work_tree(), and
+  segfaulted.
diff --git a/Documentation/RelNotes/1.6.5.8.txt b/Documentation/RelNotes/1.6.5.8.txt
new file mode 100644
index 0000000..8b24beb
--- /dev/null
+++ b/Documentation/RelNotes/1.6.5.8.txt
@@ -0,0 +1,28 @@
+Git v1.6.5.8 Release Notes
+==========================
+
+Fixes since v1.6.5.7
+--------------------
+
+* "git count-objects" did not handle packfiles that are bigger than 4G on
+  platforms with 32-bit off_t.
+
+* "git rebase -i" did not abort cleanly if it failed to launch the editor.
+
+* "git blame" did not work well when commit lacked the author name.
+
+* "git fast-import" choked when handling a tag that points at an object
+  that is not a commit.
+
+* "git reset --hard" did not work correctly when GIT_WORK_TREE environment
+  variable is used to point at the root of the true work tree.
+
+* "git grep" fed a buffer that is not NUL-terminated to underlying
+  regexec().
+
+* "git checkout -m other" while on a branch that does not have any commit
+  segfaulted, instead of failing.
+
+* "git branch -a other" should have diagnosed the command as an error.
+
+Other minor documentation updates are also included.
diff --git a/Documentation/RelNotes/1.6.5.9.txt b/Documentation/RelNotes/1.6.5.9.txt
new file mode 100644
index 0000000..bb469dd
--- /dev/null
+++ b/Documentation/RelNotes/1.6.5.9.txt
@@ -0,0 +1,18 @@
+Git v1.6.5.9 Release Notes
+==========================
+
+Fixes since v1.6.5.8
+--------------------
+
+ * An overlong line after ".gitdir: " in a git file caused out of bounds
+   access to an array on the stack.
+
+ * "git blame -L $start,$end" segfaulted when too large $start was given.
+
+ * "git rev-parse --parseopt --stop-at-non-option" did not stop at non option
+   when --keep-dashdash was in effect.
+
+ * "gitweb" can sometimes be tricked into parrotting a filename argument
+   given in a request without properly quoting.
+
+Other minor fixes and documentation updates are included.
diff --git a/Documentation/RelNotes/1.6.5.txt b/Documentation/RelNotes/1.6.5.txt
new file mode 100644
index 0000000..ee141c1
--- /dev/null
+++ b/Documentation/RelNotes/1.6.5.txt
@@ -0,0 +1,169 @@
+GIT v1.6.5 Release Notes
+========================
+
+In git 1.7.0, which was planned to be the release after 1.6.5, "git
+push" into a branch that is currently checked out will be refused by
+default.
+
+You can choose what should happen upon such a push by setting the
+configuration variable receive.denyCurrentBranch in the receiving
+repository.
+
+Also, "git push $there :$killed" to delete the branch $killed in a remote
+repository $there, when $killed branch is the current branch pointed at by
+its HEAD, will be refused by default.
+
+You can choose what should happen upon such a push by setting the
+configuration variable receive.denyDeleteCurrent in the receiving
+repository.
+
+To ease the transition plan, the receiving repository of such a
+push running this release will issue a big warning when the
+configuration variable is missing.  Please refer to:
+
+  http://git.or.cz/gitwiki/GitFaq#non-bare
+  http://thread.gmane.org/gmane.comp.version-control.git/107758/focus=108007
+
+for more details on the reason why this change is needed and the
+transition plan.
+
+Updates since v1.6.4
+--------------------
+
+(subsystems)
+
+ * various updates to gitk, git-svn and gitweb.
+
+(portability)
+
+ * more improvements on mingw port.
+
+ * mingw will also give FRSX as the default value for the LESS
+   environment variable when the user does not have one.
+
+ * initial support to compile git on Windows with MSVC.
+
+(performance)
+
+ * On major platforms, the system can be compiled to use with Linus's
+   block-sha1 implementation of the SHA-1 hash algorithm, which
+   outperforms the default fallback implementation we borrowed from
+   Mozilla.
+
+ * Unnecessary inefficiency in deepening of a shallow repository has
+   been removed.
+
+ * "git clone" does not grab objects that it does not need (i.e.
+   referenced only from refs outside refs/heads and refs/tags
+   hierarchy) anymore.
+
+ * The "git" main binary used to link with libcurl, which then dragged
+   in a large number of external libraries.  When using basic plumbing
+   commands in scripts, this unnecessarily slowed things down.  We now
+   implement http/https/ftp transfer as a separate executable as we
+   used to.
+
+ * "git clone" run locally hardlinks or copies the files in .git/ to
+   newly created repository.  It used to give new mtime to copied files,
+   but this delayed garbage collection to trigger unnecessarily in the
+   cloned repository.  We now preserve mtime for these files to avoid
+   this issue.
+
+(usability, bells and whistles)
+
+ * Human writable date format to various options, e.g. --since=yesterday,
+   master@{2000.09.17}, are taught to infer some omitted input properly.
+
+ * A few programs gave verbose "advice" messages to help uninitiated
+   people when issuing error messages.  An infrastructure to allow
+   users to squelch them has been introduced, and a few such messages
+   can be silenced now.
+
+ * refs/replace/ hierarchy is designed to be usable as a replacement
+   of the "grafts" mechanism, with the added advantage that it can be
+   transferred across repositories.
+
+ * "git am" learned to optionally ignore whitespace differences.
+
+ * "git am" handles input e-mail files that has CRLF line endings sensibly.
+
+ * "git am" learned "--scissors" option to allow you to discard early part
+   of an incoming e-mail.
+
+ * "git archive -o output.zip" works without being told what format to
+   use with an explicit "--format=zip".option.
+
+ * "git checkout", "git reset" and "git stash" learned to pick and
+   choose to use selected changes you made, similar to "git add -p".
+
+ * "git clone" learned a "-b" option to pick a HEAD to check out
+   different from the remote's default branch.
+
+ * "git clone" learned --recursive option.
+
+ * "git clone" from a local repository on a different filesystem used to
+   copy individual object files without preserving the old timestamp, giving
+   them extra lifetime in the new repository until they gc'ed.
+
+ * "git commit --dry-run $args" is a new recommended way to ask "what would
+   happen if I try to commit with these arguments."
+
+ * "git commit --dry-run" and "git status" shows conflicted paths in a
+   separate section to make them easier to spot during a merge.
+
+ * "git cvsimport" now supports password-protected pserver access even
+   when the password is not taken from ~/.cvspass file.
+
+ * "git fast-export" learned --no-data option that can be useful when
+   reordering commits and trees without touching the contents of
+   blobs.
+
+ * "git fast-import" has a pair of new front-end in contrib/ area.
+
+ * "git init" learned to mkdir/chdir into a directory when given an
+   extra argument (i.e. "git init this").
+
+ * "git instaweb" optionally can use mongoose as the web server.
+
+ * "git log --decorate" can optionally be told with --decorate=full to
+   give the reference name in full.
+
+ * "git merge" issued an unnecessarily scary message when it detected
+   that the merge may have to touch the path that the user has local
+   uncommitted changes to. The message has been reworded to make it
+   clear that the command aborted, without doing any harm.
+
+ * "git push" can be told to be --quiet.
+
+ * "git push" pays attention to url.$base.pushInsteadOf and uses a URL
+   that is derived from the URL used for fetching.
+
+ * informational output from "git reset" that lists the locally modified
+   paths is made consistent with that of "git checkout $another_branch".
+
+ * "git submodule" learned to give submodule name to scripts run with
+   "foreach" subcommand.
+
+ * various subcommands to "git submodule" learned --recursive option.
+
+ * "git submodule summary" learned --files option to compare the work
+   tree vs the commit bound at submodule path, instead of comparing
+   the index.
+
+ * "git upload-pack", which is the server side support for "git clone" and
+   "git fetch", can call a new post-upload-pack hook for statistics purposes.
+
+(developers)
+
+ * With GIT_TEST_OPTS="--root=/p/a/t/h", tests can be run outside the
+   source directory; using tmpfs may give faster turnaround.
+
+ * With NO_PERL_MAKEMAKER set, DESTDIR= is now honoured, so you can
+   build for one location, and install into another location to tar it
+   up.
+
+Fixes since v1.6.4
+------------------
+
+All of the fixes in v1.6.4.X maintenance series are included in this
+release, unless otherwise noted.
diff --git a/Documentation/RelNotes/1.6.6.1.txt b/Documentation/RelNotes/1.6.6.1.txt
new file mode 100644
index 0000000..f1d0a4a
--- /dev/null
+++ b/Documentation/RelNotes/1.6.6.1.txt
@@ -0,0 +1,37 @@
+Git v1.6.6.1 Release Notes
+==========================
+
+Fixes since v1.6.6
+------------------
+
+ * "git blame" did not work well when commit lacked the author name.
+
+ * "git branch -a name" wasn't diagnosed as an error.
+
+ * "git count-objects" did not handle packfiles that are bigger than 4G on
+   platforms with 32-bit off_t.
+
+ * "git checkout -m other" while on a branch that does not have any commit
+   segfaulted, instead of failing.
+
+ * "git fast-import" choked when fed a tag that do not point at a
+   commit.
+
+ * "git grep" finding from work tree files could have fed garbage to
+   the underlying regexec(3).
+
+ * "git grep -L" didn't show empty files (they should never match, and
+   they should always appear in -L output as unmatching).
+
+ * "git rebase -i" did not abort cleanly if it failed to launch the editor.
+
+ * "git reset --hard" did not work correctly when GIT_WORK_TREE environment
+   variable is used to point at the root of the true work tree.
+
+ * http-backend was not listed in the command list in the documentation.
+
+ * Building on FreeBSD (both 7 and 8) needs OLD_ICONV set in the Makefile
+
+ * "git checkout -m some-branch" while on an unborn branch crashed.
+
+Other minor documentation updates are included.
diff --git a/Documentation/RelNotes/1.6.6.2.txt b/Documentation/RelNotes/1.6.6.2.txt
new file mode 100644
index 0000000..4eaddc0
--- /dev/null
+++ b/Documentation/RelNotes/1.6.6.2.txt
@@ -0,0 +1,46 @@
+Git v1.6.6.2 Release Notes
+==========================
+
+Fixes since v1.6.6.1
+--------------------
+
+ * recursive merge didn't correctly diagnose its own programming errors,
+   and instead caused the caller to segfault.
+
+ * The new "smart http" aware clients probed the web servers to see if
+   they support smart http, but did not fall back to dumb http transport
+   correctly with some servers.
+
+ * Time based reflog syntax e.g. "@{yesterday}" didn't diagnose a misspelled
+   time specification and instead assumed "@{now}".
+
+ * "git archive HEAD -- no-such-directory" produced an empty archive
+   without complaining.
+
+ * "git blame -L start,end -- file" misbehaved when given a start that is
+   larger than the number of lines in the file.
+
+ * "git checkout -m" didn't correctly call custom merge backend supplied
+   by the end user.
+
+ * "git config -f <file>" misbehaved when run from a subdirectory.
+
+ * "git cvsserver" didn't like having regex metacharacters (e.g. '+') in
+   CVSROOT environment.
+
+ * "git fast-import" did not correctly handle large blobs that may
+   bust the pack size limit.
+
+ * "git gui" is supposed to work even when launched from inside a .git
+   directory.
+
+ * "git gui" misbehaved when applying a hunk that ends with deletion.
+
+ * "git imap-send" did not honor imap.preformattedHTML as documented.
+
+ * "git log" family incorrectly showed the commit notes unconditionally by
+   mistake, which was especially irritating when running "git log --oneline".
+
+ * "git status" shouldn't require an write access to the repository.
+
+Other minor documentation updates are included.
diff --git a/Documentation/RelNotes/1.6.6.3.txt b/Documentation/RelNotes/1.6.6.3.txt
new file mode 100644
index 0000000..11483ac
--- /dev/null
+++ b/Documentation/RelNotes/1.6.6.3.txt
@@ -0,0 +1,23 @@
+Git v1.6.6.3 Release Notes
+==========================
+
+Fixes since v1.6.6.2
+--------------------
+
+ * An overlong line after ".gitdir: " in a git file caused out of bounds
+   access to an array on the stack.
+
+ * "git bisect $path" did not correctly diagnose an error when given a
+   non-existent path.
+
+ * "git blame -L $start,$end" segfaulted when too large $start was given.
+
+ * "git imap-send" did not write draft box with CRLF line endings per RFC.
+
+ * "git rev-parse --parseopt --stop-at-non-option" did not stop at non option
+   when --keep-dashdash was in effect.
+
+ * "gitweb" can sometimes be tricked into parrotting a filename argument
+   given in a request without properly quoting.
+
+Other minor fixes and documentation updates are included.
diff --git a/Documentation/RelNotes/1.6.6.txt b/Documentation/RelNotes/1.6.6.txt
new file mode 100644
index 0000000..c50b59c
--- /dev/null
+++ b/Documentation/RelNotes/1.6.6.txt
@@ -0,0 +1,224 @@
+Git v1.6.6 Release Notes
+========================
+
+Notes on behaviour change
+-------------------------
+
+ * In this release, "git fsck" defaults to "git fsck --full" and
+   checks packfiles, and because of this it will take much longer to
+   complete than before.  If you prefer a quicker check only on loose
+   objects (the old default), you can say "git fsck --no-full".  This
+   has been supported by 1.5.4 and newer versions of git, so it is
+   safe to write it in your script even if you use slightly older git
+   on some of your machines.
+
+Preparing yourselves for compatibility issues in 1.7.0
+------------------------------------------------------
+
+In git 1.7.0, which is planned to be the release after 1.6.6, there will
+be a handful of behaviour changes that will break backward compatibility.
+
+These changes were discussed long time ago and existing behaviours have
+been identified as more problematic to the userbase than keeping them for
+the sake of backward compatibility.
+
+When necessary, a transition strategy for existing users has been designed
+not to force them running around setting configuration variables and
+updating their scripts in order to either keep the traditional behaviour
+or adjust to the new behaviour, on the day their sysadmin decides to install
+the new version of git.  When we switched from "git-foo" to "git foo" in
+1.6.0, even though the change had been advertised and the transition
+guide had been provided for a very long time, the users procrastinated
+during the entire transition period, and ended up panicking on the day
+their sysadmins updated their git installation.  We are trying to avoid
+repeating that unpleasantness in the 1.7.0 release.
+
+For changes decided to be in 1.7.0, commands that will be affected
+have been much louder to strongly discourage such procrastination, and
+they continue to be in this release.  If you have been using recent
+versions of git, you would have seen warnings issued when you used
+features whose behaviour will change, with a clear instruction on how
+to keep the existing behaviour if you want to.  You hopefully are
+already well prepared.
+
+Of course, we have also been giving "this and that will change in
+1.7.0; prepare yourselves" warnings in the release notes and
+announcement messages for the past few releases.  Let's see how well
+users will fare this time.
+
+ * "git push" into a branch that is currently checked out (i.e. pointed by
+   HEAD in a repository that is not bare) will be refused by default.
+
+   Similarly, "git push $there :$killed" to delete the branch $killed
+   in a remote repository $there, when $killed branch is the current
+   branch pointed at by its HEAD, will be refused by default.
+
+   Setting the configuration variables receive.denyCurrentBranch and
+   receive.denyDeleteCurrent to 'ignore' in the receiving repository
+   can be used to override these safety features.  Versions of git
+   since 1.6.2 have issued a loud warning when you tried to do these
+   operations without setting the configuration, so repositories of
+   people who still need to be able to perform such a push should
+   already have been future proofed.
+
+   Please refer to:
+
+   http://git.or.cz/gitwiki/GitFaq#non-bare
+   http://thread.gmane.org/gmane.comp.version-control.git/107758/focus=108007
+
+   for more details on the reason why this change is needed and the
+   transition process that already took place so far.
+
+ * "git send-email" will not make deep threads by default when sending a
+   patch series with more than two messages.  All messages will be sent
+   as a reply to the first message, i.e. cover letter.  Git 1.6.6 (this
+   release) will issue a warning about the upcoming default change, when
+   it uses the traditional "deep threading" behaviour as the built-in
+   default.  To squelch the warning but still use the "deep threading"
+   behaviour, give --chain-reply-to option or set sendemail.chainreplyto
+   to true.
+
+   It has been possible to configure send-email to send "shallow thread"
+   by setting sendemail.chainreplyto configuration variable to false.
+   The only thing 1.7.0 release will do is to change the default when
+   you haven't configured that variable.
+
+ * "git status" will not be "git commit --dry-run".  This change does not
+   affect you if you run the command without pathspec.
+
+   Nobody sane found the current behaviour of "git status Makefile" useful
+   nor meaningful, and it confused users.  "git commit --dry-run" has been
+   provided as a way to get the current behaviour of this command since
+   1.6.5.
+
+ * "git diff" traditionally treated various "ignore whitespace" options
+   only as a way to filter the patch output.  "git diff --exit-code -b"
+   exited with non-zero status even if all changes were about changing the
+   amount of whitespace and nothing else.  and "git diff -b" showed the
+   "diff --git" header line for such a change without patch text.
+
+   In 1.7.0, the "ignore whitespaces" will affect the semantics of the
+   diff operation itself.  A change that does not affect anything but
+   whitespaces will be reported with zero exit status when run with
+   --exit-code, and there will not be "diff --git" header for such a
+   change.
+
+
+Updates since v1.6.5
+--------------------
+
+(subsystems)
+
+ * various gitk updates including use of themed widgets under Tk 8.5,
+   Japanese translation, a fix to a bug when running "gui blame" from
+   a subdirectory, etc.
+
+ * various git-gui updates including new translations, wm states fixes,
+   Tk bug workaround after quitting, improved heuristics to trigger gc,
+   etc.
+
+ * various git-svn updates.
+
+ * "git fetch" over http learned a new mode that is different from the
+   traditional "dumb commit walker".
+
+(portability)
+
+ * imap-send can be built on mingw port.
+
+(performance)
+
+ * "git diff -B" has smaller memory footprint.
+
+(usability, bells and whistles)
+
+ * The object replace mechanism can be bypassed with --no-replace-objects
+   global option given to the "git" program.
+
+ * In configuration files, a few variables that name paths can begin with ~/
+   and ~username/ and they are expanded as expected.
+
+ * "git subcmd -h" now shows short usage help for many more subcommands.
+
+ * "git bisect reset" can reset to an arbitrary commit.
+
+ * "git checkout frotz" when there is no local branch "frotz" but there
+   is only one remote tracking branch "frotz" is taken as a request to
+   start the named branch at the corresponding remote tracking branch.
+
+ * "git commit -c/-C/--amend" can be told with a new "--reset-author" option
+   to ignore authorship information in the commit it is taking the message
+   from.
+
+ * "git describe" can be told to add "-dirty" suffix with "--dirty" option.
+
+ * "git diff" learned --submodule option to show a list of one-line logs
+   instead of differences between the commit object names.
+
+ * "git diff" learned to honor diff.color.func configuration to paint
+   function name hint printed on the hunk header "@@ -j,k +l,m @@" line
+   in the specified color.
+
+ * "git fetch" learned --all and --multiple options, to run fetch from
+   many repositories, and --prune option to remove remote tracking
+   branches that went stale.  These make "git remote update" and "git
+   remote prune" less necessary (there is no plan to remove "remote
+   update" nor "remote prune", though).
+
+ * "git fsck" by default checks the packfiles (i.e. "--full" is the
+   default); you can turn it off with "git fsck --no-full".
+
+ * "git grep" can use -F (fixed strings) and -i (ignore case) together.
+
+ * import-tars contributed fast-import frontend learned more types of
+   compressed tarballs.
+
+ * "git instaweb" knows how to talk with mod_cgid to apache2.
+
+ * "git log --decorate" shows the location of HEAD as well.
+
+ * "git log" and "git rev-list" learned to take revs and pathspecs from
+   the standard input with the new "--stdin" option.
+
+ * "--pretty=format" option to "log" family of commands learned:
+
+   . to wrap text with the "%w()" specifier.
+   . to show reflog information with "%g[sdD]" specifier.
+
+ * "git notes" command to annotate existing commits.
+
+ * "git merge" (and "git pull") learned --ff-only option to make it fail
+   if the merge does not result in a fast-forward.
+
+ * "git mergetool" learned to use p4merge.
+
+ * "git rebase -i" learned "reword" that acts like "edit" but immediately
+   starts an editor to tweak the log message without returning control to
+   the shell, which is done by "edit" to give an opportunity to tweak the
+   contents.
+
+ * "git send-email" can be told with "--envelope-sender=auto" to use the
+   same address as "From:" address as the envelope sender address.
+
+ * "git send-email" will issue a warning when it defaults to the
+   --chain-reply-to behaviour without being told by the user and
+   instructs to prepare for the change of the default in 1.7.0 release.
+
+ * In "git submodule add <repository> <path>", <path> is now optional and
+   inferred from <repository> the same way "git clone <repository>" does.
+
+ * "git svn" learned to read SVN 1.5+ and SVK merge tickets.
+
+ * "git svn" learned to recreate empty directories tracked only by SVN.
+
+ * "gitweb" can optionally render its "blame" output incrementally (this
+   requires JavaScript on the client side).
+
+ * Author names shown in gitweb output are links to search commits by the
+   author.
+
+Fixes since v1.6.5
+------------------
+
+All of the fixes in v1.6.5.X maintenance series are included in this
+release, unless otherwise noted.
diff --git a/Documentation/RelNotes/1.7.0.1.txt b/Documentation/RelNotes/1.7.0.1.txt
new file mode 100644
index 0000000..8ff5bca
--- /dev/null
+++ b/Documentation/RelNotes/1.7.0.1.txt
@@ -0,0 +1,35 @@
+Git v1.7.0.1 Release Notes
+==========================
+
+Fixes since v1.7.0
+------------------
+
+ * In a freshly created repository "rev-parse HEAD^0" complained that
+   it is dangling symref, even though "rev-parse HEAD" didn't.
+
+ * "git show :no-such-name" tried to access the index without bounds
+   check, leading to a potential segfault.
+
+ * Message from "git cherry-pick" was harder to read and use than necessary
+   when it stopped due to conflicting changes.
+
+ * We referred to ".git/refs/" throughout the documentation when we
+   meant to talk about abstract notion of "ref namespace".  Because
+   people's repositories often have packed refs these days, this was
+   confusing.
+
+ * "git diff --output=/path/that/cannot/be/written" did not correctly
+   error out.
+
+ * "git grep -e -pattern-that-begin-with-dash paths..." could not be
+   spelled as "git grep -- -pattern-that-begin-with-dash paths..." which
+   would be a GNU way to use "--" as "end of options".
+
+ * "git grep" compiled with threading support tried to access an
+   uninitialized mutex on boxes with a single CPU.
+
+ * "git stash pop -q --index" failed because the unnecessary --index
+   option was propagated to "git stash drop" that is internally run at the
+   end.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.0.2.txt b/Documentation/RelNotes/1.7.0.2.txt
new file mode 100644
index 0000000..fcb46ca
--- /dev/null
+++ b/Documentation/RelNotes/1.7.0.2.txt
@@ -0,0 +1,40 @@
+Git v1.7.0.2 Release Notes
+==========================
+
+Fixes since v1.7.0.1
+--------------------
+
+ * GIT_PAGER was not honored consistently by some scripted Porcelains, most
+   notably "git am".
+
+ * updating working tree files after telling git to add them to the
+   index and while it is still working created garbage object files in
+   the repository without diagnosing it as an error.
+
+ * "git bisect -- pathspec..." did not diagnose an error condition properly when
+   the simplification with given pathspec made the history empty.
+
+ * "git rev-list --cherry-pick A...B" now has an obvious optimization when the
+   histories haven't diverged (i.e. when one end is an ancestor of the other).
+
+ * "git diff --quiet -w" did not work as expected.
+
+ * "git fast-import" didn't work with a large input, as it lacked support
+   for producing the pack index in v2 format.
+
+ * "git imap-send" didn't use CRLF line endings over the imap protocol
+   when storing its payload to the draft box, violating RFC 3501.
+
+ * "git log --format='%w(x,y,z)%b'" and friends that rewrap message
+   has been optimized for utf-8 payload.
+
+ * Error messages generated on the receiving end did not come back to "git
+   push".
+
+ * "git status" in 1.7.0 lacked the optimization we used to have in 1.6.X series
+   to speed up scanning of large working tree.
+
+ * "gitweb" did not diagnose parsing errors properly while reading tis configuration
+   file.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.0.3.txt b/Documentation/RelNotes/1.7.0.3.txt
new file mode 100644
index 0000000..3b35573
--- /dev/null
+++ b/Documentation/RelNotes/1.7.0.3.txt
@@ -0,0 +1,34 @@
+Git v1.7.0.3 Release Notes
+==========================
+
+Fixes since v1.7.0.2
+--------------------
+
+ * Object files are created in a more ACL friendly way in repositories
+   where group permission is ACL controlled.
+
+ * "git add -i" didn't handle a deleted path very well.
+
+ * "git blame" padded line numbers with one extra SP when the total number
+   of lines was one less than multiple of ten due to an off-by-one error.
+
+ * "git fetch --all/--multi" used to discard information for remotes that
+   are fetched earlier.
+
+ * "git log --author=me --grep=it" tried to find commits that have "it"
+   or are written by "me", instead of the ones that have "it" _and_ are
+   written by "me".
+
+ * "git log -g branch" misbehaved when there was no entries in the reflog
+   for the named branch.
+
+ * "git mailinfo" (hence "git am") incorrectly removed initial indent from
+   paragraphs.
+
+ * "git prune" and "git reflog" (hence "git gc" as well) didn't honor
+   an instruction never to expire by setting gc.reflogexpire to never.
+
+ * "git push" misbehaved when branch.<name>.merge was configured without
+   matching branch.<name>.remote.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.0.4.txt b/Documentation/RelNotes/1.7.0.4.txt
new file mode 100644
index 0000000..cf7f60e
--- /dev/null
+++ b/Documentation/RelNotes/1.7.0.4.txt
@@ -0,0 +1,27 @@
+Git v1.7.0.4 Release Notes
+==========================
+
+Fixes since v1.7.0.3
+--------------------
+
+ * Optimized ntohl/htonl on big-endian machines were broken.
+
+ * Color values given to "color.<cmd>.<slot>" configuration can now have
+   more than one attributes (e.g. "bold ul").
+
+ * "git add -u nonexistent-path" did not complain.
+
+ * "git apply --whitespace=fix" didn't work well when an early patch in
+   a patch series adds trailing blank lines and a later one depended on
+   such a block of blank lines at the end.
+
+ * "git fast-export" didn't check error status and stop when marks file
+   cannot be opened.
+
+ * "git format-patch --ignore-if-in-upstream" gave unwarranted errors
+   when the range was empty, instead of silently finishing.
+
+ * "git remote prune" did not detect remote tracking refs that became
+   dangling correctly.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.0.5.txt b/Documentation/RelNotes/1.7.0.5.txt
new file mode 100644
index 0000000..3149c91
--- /dev/null
+++ b/Documentation/RelNotes/1.7.0.5.txt
@@ -0,0 +1,26 @@
+Git v1.7.0.5 Release Notes
+==========================
+
+Fixes since v1.7.0.4
+--------------------
+
+ * "git daemon" failed to compile on platforms without sockaddr_storage type.
+
+ * Output from "git rev-list --pretty=oneline" was unparsable when a
+   commit did not have any message, which is abnormal but possible in a
+   repository converted from foreign scm.
+
+ * "git stash show <commit-that-is-not-a-stash>" gave an error message
+   that was not so useful.  Reworded the message to "<it> is not a
+   stash".
+
+ * Python scripts in contrib/ area now start with "#!/usr/bin/env python"
+   to honor user's PATH.
+
+ * "git imap-send" used to mistake any line that begins with "From " as a
+   message separator in format-patch output.
+
+ * Smart http server backend failed to report an internal server error and
+   infinitely looped instead after output pipe was closed.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.0.6.txt b/Documentation/RelNotes/1.7.0.6.txt
new file mode 100644
index 0000000..b2852b6
--- /dev/null
+++ b/Documentation/RelNotes/1.7.0.6.txt
@@ -0,0 +1,13 @@
+Git v1.7.0.6 Release Notes
+==========================
+
+Fixes since v1.7.0.5
+--------------------
+
+ * "git diff --stat" used "int" to count the size of differences,
+   which could result in overflowing.
+
+ * "git rev-list --abbrev-commit" defaulted to 40-byte abbreviations, unlike
+   newer tools in the git toolset.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.0.7.txt b/Documentation/RelNotes/1.7.0.7.txt
new file mode 100644
index 0000000..d0cb7ca
--- /dev/null
+++ b/Documentation/RelNotes/1.7.0.7.txt
@@ -0,0 +1,16 @@
+Git v1.7.0.7 Release Notes
+==========================
+
+Fixes since v1.7.0.6
+--------------------
+
+ * "make NO_CURL=NoThanks install" was broken.
+
+ * An overlong line after ".gitdir: " in a git file caused out of bounds
+   access to an array on the stack.
+
+ * "git config --path conf.var" to attempt to expand a variable conf.var
+   that uses "~/" short-hand segfaulted when $HOME environment variable
+   was not set.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.0.8.txt b/Documentation/RelNotes/1.7.0.8.txt
new file mode 100644
index 0000000..7f05b48
--- /dev/null
+++ b/Documentation/RelNotes/1.7.0.8.txt
@@ -0,0 +1,10 @@
+Git v1.7.0.8 Release Notes
+==========================
+
+This is primarily to backport support for the new "add.ignoreErrors"
+name given to the existing "add.ignore-errors" configuration variable.
+
+The next version, Git 1.7.4, and future versions, will support both
+old and incorrect name and the new corrected name, but without this
+backport, users who want to use the new name "add.ignoreErrors" in
+their repositories cannot use older versions of Git.
diff --git a/Documentation/RelNotes/1.7.0.9.txt b/Documentation/RelNotes/1.7.0.9.txt
new file mode 100644
index 0000000..bfb3166
--- /dev/null
+++ b/Documentation/RelNotes/1.7.0.9.txt
@@ -0,0 +1,8 @@
+Git v1.7.0.9 Release Notes
+==========================
+
+Fixes since v1.7.0.8
+--------------------
+
+ * "gitweb" can sometimes be tricked into parrotting a filename argument
+   given in a request without properly quoting.
diff --git a/Documentation/RelNotes/1.7.0.txt b/Documentation/RelNotes/1.7.0.txt
new file mode 100644
index 0000000..0bb8c0b
--- /dev/null
+++ b/Documentation/RelNotes/1.7.0.txt
@@ -0,0 +1,214 @@
+Git v1.7.0 Release Notes
+========================
+
+Notes on behaviour change
+-------------------------
+
+ * "git push" into a branch that is currently checked out (i.e. pointed at by
+   HEAD in a repository that is not bare) is refused by default.
+
+   Similarly, "git push $there :$killed" to delete the branch $killed
+   in a remote repository $there, when $killed branch is the current
+   branch pointed at by its HEAD, will be refused by default.
+
+   Setting the configuration variables receive.denyCurrentBranch and
+   receive.denyDeleteCurrent to 'ignore' in the receiving repository
+   can be used to override these safety features.
+
+ * "git send-email" does not make deep threads by default when sending a
+   patch series with more than two messages.  All messages will be sent
+   as a reply to the first message, i.e. cover letter.
+
+   It has been possible already to configure send-email to send "shallow thread"
+   by setting sendemail.chainreplyto configuration variable to false.  The
+   only thing this release does is to change the default when you haven't
+   configured that variable.
+
+ * "git status" is not "git commit --dry-run" anymore.  This change does
+   not affect you if you run the command without argument.
+
+ * "git diff" traditionally treated various "ignore whitespace" options
+   only as a way to filter the patch output.  "git diff --exit-code -b"
+   exited with non-zero status even if all changes were about changing the
+   amount of whitespace and nothing else;  and "git diff -b" showed the
+   "diff --git" header line for such a change without patch text.
+
+   In this release, the "ignore whitespaces" options affect the semantics
+   of the diff operation.  A change that does not affect anything but
+   whitespaces is reported with zero exit status when run with
+   --exit-code, and there is no "diff --git" header for such a change.
+
+ * External diff and textconv helpers are now executed using the shell.
+   This makes them consistent with other programs executed by git, and
+   allows you to pass command-line parameters to the helpers. Any helper
+   paths containing spaces or other metacharacters now need to be
+   shell-quoted.  The affected helpers are GIT_EXTERNAL_DIFF in the
+   environment, and diff.*.command and diff.*.textconv in the config
+   file.
+
+ * The --max-pack-size argument to 'git repack', 'git pack-objects', and
+   'git fast-import' was assuming the provided size to be expressed in MiB,
+   unlike the corresponding config variable and other similar options accepting
+   a size value.  It is now expecting a size expressed in bytes, with a possible
+   unit suffix of 'k', 'm', or 'g'.
+
+Updates since v1.6.6
+--------------------
+
+(subsystems)
+
+ * "git fast-import" updates; adds "option" and "feature" to detect the
+   mismatch between fast-import and the frontends that produce the input
+   stream.
+
+ * "git svn" support of subversion "merge tickets" and miscellaneous fixes.
+
+ * "gitk" and "git gui" translation updates.
+
+ * "gitweb" updates (code clean-up, load checking etc.)
+
+(portability)
+
+ * Some more MSVC portability patches for msysgit port.
+
+ * Minimum Pthreads emulation for msysgit port.
+
+(performance)
+
+ * More performance improvement patches for msysgit port.
+
+(usability, bells and whistles)
+
+ * More commands learned "--quiet" and "--[no-]progress" options.
+
+ * Various commands given by the end user (e.g. diff.type.textconv,
+   and GIT_EDITOR) can be specified with command line arguments.  E.g. it
+   is now possible to say "[diff "utf8doc"] textconv = nkf -w".
+
+ * "sparse checkout" feature allows only part of the work tree to be
+   checked out.
+
+ * HTTP transfer can use authentication scheme other than basic
+   (i.e./e.g. digest).
+
+ * Switching from a version of superproject that used to have a submodule
+   to another version of superproject that no longer has it did not remove
+   the submodule directory when it should (namely, when you are not
+   interested in the submodule at all and didn't clone/checkout).
+
+ * A new attribute conflict-marker-size can be used to change the size of
+   the conflict markers from the default 7; this is useful when tracked
+   contents (e.g. git-merge documentation) have strings that resemble the
+   conflict markers.
+
+ * A new syntax "<branch>@{upstream}" can be used on the command line to
+   substitute the name of the "upstream" of the branch.  Missing branch
+   defaults to the current branch, so "git fetch && git merge @{upstream}"
+   will be equivalent to "git pull".
+
+ * "git am --resolved" has a synonym "git am --continue".
+
+ * "git branch --set-upstream" can be used to update the (surprise!) upstream,
+   i.e. where the branch is supposed to pull and merge from (or rebase onto).
+
+ * "git checkout A...B" is a way to detach HEAD at the merge base between
+   A and B.
+
+ * "git checkout -m path" to reset the work tree file back into the
+   conflicted state works even when you already ran "git add path" and
+   resolved the conflicts.
+
+ * "git commit --date='<date>'" can be used to override the author date
+   just like "git commit --author='<name> <email>'" can be used to
+   override the author identity.
+
+ * "git commit --no-status" can be used to omit the listing of the index
+   and the work tree status in the editor used to prepare the log message.
+
+ * "git commit" warns a bit more aggressively until you configure user.email,
+   whose default value almost always is not (and fundamentally cannot be)
+   what you want.
+
+ * "git difftool" has been extended to make it easier to integrate it
+   with gitk.
+
+ * "git fetch --all" can now be used in place of "git remote update".
+
+ * "git grep" does not rely on external grep anymore.  It can use more than
+   one thread to accelerate the operation.
+
+ * "git grep" learned "--quiet" option.
+
+ * "git log" and friends learned "--glob=heads/*" syntax that is a more
+   flexible way to complement "--branches/--tags/--remotes".
+
+ * "git merge" learned to pass options specific to strategy-backends.  E.g.
+
+    - "git merge -Xsubtree=path/to/directory" can be used to tell the subtree
+      strategy how much to shift the trees explicitly.
+
+    - "git merge -Xtheirs" can be used to auto-merge as much as possible,
+      while discarding your own changes and taking merged version in
+      conflicted regions.
+
+ * "git push" learned "git push origin --delete branch", a syntactic sugar
+   for "git push origin :branch".
+
+ * "git push" learned "git push --set-upstream origin forker:forkee" that
+   lets you configure your "forker" branch to later pull from "forkee"
+   branch at "origin".
+
+ * "git rebase --onto A...B" means the history is replayed on top of the
+   merge base between A and B.
+
+ * "git rebase -i" learned new action "fixup" that squashes the change
+   but does not affect existing log message.
+
+ * "git rebase -i" also learned --autosquash option that is useful
+   together with the new "fixup" action.
+
+ * "git remote" learned set-url subcommand that updates (surprise!) url
+   for an existing remote nickname.
+
+ * "git rerere" learned "forget path" subcommand.  Together with "git
+   checkout -m path" it will be useful when you recorded a wrong
+   resolution.
+
+ * Use of "git reset --merge" has become easier when resetting away a
+   conflicted mess left in the work tree.
+
+ * "git rerere" had rerere.autoupdate configuration but there was no way
+   to countermand it from the command line; --no-rerere-autoupdate option
+   given to "merge", "revert", etc. fixes this.
+
+ * "git status" learned "-s(hort)" output format.
+
+(developers)
+
+ * The infrastructure to build foreign SCM interface has been updated.
+
+ * Many more commands are now built-in.
+
+ * THREADED_DELTA_SEARCH is no more.  If you build with threads, delta
+   compression will always take advantage of it.
+
+Fixes since v1.6.6
+------------------
+
+All of the fixes in v1.6.6.X maintenance series are included in this
+release, unless otherwise noted.
+
+ * "git branch -d branch" used to refuse deleting the branch even when
+   the branch is fully merged to its upstream branch if it is not merged
+   to the current branch.  It now deletes it in such a case.
+
+ * "filter-branch" command incorrectly said --prune-empty and --filter-commit
+   were incompatible; the latter should be read as --commit-filter.
+
+ * When using "git status" or asking "git diff" to compare the work tree
+   with something, they used to consider that a checked-out submodule with
+   uncommitted changes is not modified; this could cause people to forget
+   committing these changes in the submodule before committing in the
+   superproject. They now consider such a change as a modification and
+   "git diff" will append a "-dirty" to the work tree side when generating
+   patch output or when used with the --submodule option.
diff --git a/Documentation/RelNotes/1.7.1.1.txt b/Documentation/RelNotes/1.7.1.1.txt
new file mode 100644
index 0000000..3f6b314
--- /dev/null
+++ b/Documentation/RelNotes/1.7.1.1.txt
@@ -0,0 +1,96 @@
+Git v1.7.1.1 Release Notes
+==========================
+
+Fixes since v1.7.1
+------------------
+
+ * Authentication over http transport can now be made lazily, in that the
+   request can first go to a URL without username, get a 401 response and
+   then the client will ask for the username to use.
+
+ * We used to mistakenly think "../work" is a subdirectory of the current
+   directory when we are in "../work-xyz".
+
+ * The attribute mechanism now allows an entry that uses an attribute
+   macro that set/unset one attribute, immediately followed by an
+   overriding setting; this makes attribute macros much easier to use.
+
+ * We didn't recognize timezone "Z" as a synonym for "UTC" (75b37e70).
+
+ * In 1.7.0, read-tree and user commands that use the mechanism such as
+   checkout and merge were fixed to handle switching between branches one
+   of which has a file while the other has a directory at the same path
+   correctly even when there are some "confusing" pathnames in them.  But
+   the algorithm used for this fix was suboptimal and had a terrible
+   performance degradation especially in larger trees.
+
+ * "git am -3" did not show diagnosis when the patch in the message was corrupt.
+
+ * After "git apply --whitespace=fix" removed trailing blank lines in an
+   patch in a patch series, it failed to apply later patches that depend
+   on the presence of such blank lines.
+
+ * "git bundle --stdin" segfaulted.
+
+ * "git checkout" and "git rebase" overwrote paths that are marked "assume
+   unchanged".
+
+ * "git commit --amend" on a commit with an invalid author-name line that
+   lacks the display name didn't work.
+
+ * "git describe" did not tie-break tags that point at the same commit
+   correctly; newer ones are preferred by paying attention to the
+   tagger date now.
+
+ * "git diff" used to tell underlying xdiff machinery to work very hard to
+   minimize the output, but this often was spending too many extra cycles
+   for very little gain.
+
+ * "git diff --color" did not paint extended diff headers per line
+   (i.e. the coloring escape sequence didn't end at the end of line),
+   which confused "less -R".
+
+ * "git fetch" over HTTP verifies the downloaded packfiles more robustly.
+
+ * The memory usage by "git index-pack" (run during "git fetch" and "git
+   push") got leaner.
+
+ * "GIT_DIR=foo.git git init --bare bar.git" created foo.git instead of bar.git.
+
+ * "git log --abbrev=$num --format='%h' ignored --abbrev=$num.
+
+ * "git ls-files ../out/side/cwd" refused to work.
+
+ * "git merge --log" used to replace the custom message given by "-m" with
+   the shortlog, instead of appending to it.
+
+ * "git notes copy" without any other argument segfaulted.
+
+ * "git pull" accepted "--dry-run", gave it to underlying "git fetch" but
+   ignored the option itself, resulting in a bogus attempt to merge
+   unrelated commit.
+
+ * "git rebase" did not faithfully reproduce a malformed author ident, that
+   is often seen in a repository converted from foreign SCMs.
+
+ * "git reset --hard" started from a wrong directory and a working tree in
+   a nonstandard location is in use got confused.
+
+ * "git send-email" lacked a way to specify the domainname used in the
+   EHLO/HELO exchange, causing rejected connection from picky servers.
+   It learned --smtp-domain option to solve this issue.
+
+ * "git send-email" did not declare a content-transfer-encoding and
+   content-type even when its payload needs to be sent in 8-bit.
+
+ * "git show -C -C" and other corner cases lost diff metainfo output
+   in 1.7.0.
+
+ * "git stash" incorrectly lost paths in the working tree that were
+   previously removed from the index.
+
+ * "git status" stopped refreshing the index by mistake in 1.7.1.
+
+ * "git status" showed excess "hints" even when advice.statusHints is set to false.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.1.2.txt b/Documentation/RelNotes/1.7.1.2.txt
new file mode 100644
index 0000000..61ba14e
--- /dev/null
+++ b/Documentation/RelNotes/1.7.1.2.txt
@@ -0,0 +1,28 @@
+Git v1.7.1.2 Release Notes
+==========================
+
+Fixes since v1.7.1.1
+--------------------
+
+ * "git commit" did not honor GIT_REFLOG_ACTION environment variable, resulting
+   reflog messages for cherry-pick and revert actions to be recorded as "commit".
+
+ * "git clone/fetch/pull" issued an incorrect error message when a ref and
+   a symref that points to the ref were updated at the same time.  This
+   obviously would update them to the same value, and should not result in
+   an error condition.
+
+ * "git diff" inside a tree with many pathnames that have certain
+   characters has become very slow in 1.7.0 by mistake.
+
+ * "git rev-parse --parseopt --stop-at-non-option" did not stop at non option
+   when --keep-dashdash was in effect.
+
+ * An overlong line after ".gitdir: " in a git file caused out of bounds
+   access to an array on the stack.
+
+ * "git config --path conf.var" to attempt to expand a variable conf.var
+   that uses "~/" short-hand segfaulted when $HOME environment variable
+   was not set.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.1.3.txt b/Documentation/RelNotes/1.7.1.3.txt
new file mode 100644
index 0000000..5b18518
--- /dev/null
+++ b/Documentation/RelNotes/1.7.1.3.txt
@@ -0,0 +1,10 @@
+Git v1.7.1.3 Release Notes
+==========================
+
+This is primarily to backport support for the new "add.ignoreErrors"
+name given to the existing "add.ignore-errors" configuration variable.
+
+The next version, Git 1.7.4, and future versions, will support both
+old and incorrect name and the new corrected name, but without this
+backport, users who want to use the new name "add.ignoreErrors" in
+their repositories cannot use older versions of Git.
diff --git a/Documentation/RelNotes/1.7.1.4.txt b/Documentation/RelNotes/1.7.1.4.txt
new file mode 100644
index 0000000..7c734b4
--- /dev/null
+++ b/Documentation/RelNotes/1.7.1.4.txt
@@ -0,0 +1,8 @@
+Git v1.7.1.4 Release Notes
+==========================
+
+Fixes since v1.7.1.3
+--------------------
+
+ * "gitweb" can sometimes be tricked into parrotting a filename argument
+   given in a request without properly quoting.
diff --git a/Documentation/RelNotes/1.7.1.txt b/Documentation/RelNotes/1.7.1.txt
new file mode 100644
index 0000000..9d89fed
--- /dev/null
+++ b/Documentation/RelNotes/1.7.1.txt
@@ -0,0 +1,89 @@
+Git v1.7.1 Release Notes
+========================
+
+Updates since v1.7.0
+--------------------
+
+ * Eric Raymond is the maintainer of updated CIAbot scripts, in contrib/.
+
+ * gitk updates.
+
+ * Some commands (e.g. svn and http interfaces) that interactively ask
+   for a password can be told to use an external program given via
+   GIT_ASKPASS.
+
+ * Conflict markers that lead the common ancestor in diff3-style output
+   now have a label, which hopefully would help third-party tools that
+   expect one.
+
+ * Comes with an updated bash-completion script.
+
+ * "git am" learned "--keep-cr" option to handle inputs that are
+   a mixture of changes to files with and without CRLF line endings.
+
+ * "git cvsimport" learned -R option to leave revision mapping between
+   CVS revisions and resulting git commits.
+
+ * "git diff --submodule" notices and describes dirty submodules.
+
+ * "git for-each-ref" learned %(symref), %(symref:short) and %(flag)
+   tokens.
+
+ * "git hash-object --stdin-paths" can take "--no-filters" option now.
+
+ * "git init" can be told to look at init.templatedir configuration
+   variable (obviously that has to come from either /etc/gitconfig or
+   $HOME/.gitconfig).
+
+ * "git grep" learned "--no-index" option, to search inside contents that
+   are not managed by git.
+
+ * "git grep" learned --color=auto/always/never.
+
+ * "git grep" learned to paint filename and line-number in colors.
+
+ * "git log -p --first-parent -m" shows one-parent diff for merge
+   commits, instead of showing combined diff.
+
+ * "git merge-file" learned to use custom conflict marker size and also
+   to use the "union merge" behaviour.
+
+ * "git notes" command has been rewritten in C and learned many commands
+   and features to help you carry notes forward across rebases and amends.
+
+ * "git request-pull" identifies the commit the request is relative to in
+   a more readable way.
+
+ * "git reset" learned "--keep" option that lets you discard commits
+   near the tip while preserving your local changes in a way similar
+   to how "git checkout branch" does.
+
+ * "git status" notices and describes dirty submodules.
+
+ * "git svn" should work better when interacting with repositories
+   with CRLF line endings.
+
+ * "git imap-send" learned to support CRAM-MD5 authentication.
+
+ * "gitweb" installation procedure can use "minified" js/css files
+   better.
+
+ * Various documentation updates.
+
+Fixes since v1.7.0
+------------------
+
+All of the fixes in v1.7.0.X maintenance series are included in this
+release, unless otherwise noted.
+
+ * "git add frotz/nitfol" did not complain when the entire frotz/ directory
+   was ignored.
+
+ * "git diff --stat" used "int" to count the size of differences,
+   which could result in overflowing.
+
+ * "git rev-list --pretty=oneline" didn't terminate a record with LF for
+   commits without any message.
+
+ * "git rev-list --abbrev-commit" defaulted to 40-byte abbreviations, unlike
+   newer tools in the git toolset.
diff --git a/Documentation/RelNotes/1.7.10.1.txt b/Documentation/RelNotes/1.7.10.1.txt
new file mode 100644
index 0000000..806a965
--- /dev/null
+++ b/Documentation/RelNotes/1.7.10.1.txt
@@ -0,0 +1,78 @@
+Git v1.7.10.1 Release Notes
+===========================
+
+Additions since v1.7.10
+-----------------------
+
+Localization message files for Danish and German have been added.
+
+
+Fixes since v1.7.10
+-------------------
+
+ * "git add -p" is not designed to deal with unmerged paths but did
+   not exclude them and tried to apply funny patches only to fail.
+
+ * "git blame" started missing quite a few changes from the origin
+   since we stopped using the diff minimalization by default in v1.7.2
+   era.
+
+ * When PATH contains an unreadable directory, alias expansion code
+   did not kick in, and failed with an error that said "git-subcmd"
+   was not found.
+
+ * "git clean -d -f" (not "-d -f -f") is supposed to protect nested
+   working trees of independent git repositories that exist in the
+   current project working tree from getting removed, but the
+   protection applied only to such working trees that are at the
+   top-level of the current project by mistake.
+
+ * "git commit --author=$name" did not tell the name that was being
+   recorded in the resulting commit to hooks, even though it does do
+   so when the end user overrode the authorship via the
+   "GIT_AUTHOR_NAME" environment variable.
+
+ * When "git commit --template F" errors out because the user did not
+   touch the message, it claimed that it aborts due to "empty
+   message", which was utterly wrong.
+
+ * The regexp configured with diff.wordregex was incorrectly reused
+   across files.
+
+ * An age-old corner case bug in combine diff (only triggered with -U0
+   and the hunk at the beginning of the file needs to be shown) has
+   been fixed.
+
+ * Rename detection logic used to match two empty files as renames
+   during merge-recursive, leading to unnatural mismerges.
+
+ * The parser in "fast-import" did not diagnose ":9" style references
+   that is not followed by required SP/LF as an error.
+
+ * When "git fetch" encounters repositories with too many references,
+   the command line of "fetch-pack" that is run by a helper
+   e.g. remote-curl, may fail to hold all of them. Now such an
+   internal invocation can feed the references through the standard
+   input of "fetch-pack".
+
+ * "git fetch" that recurses into submodules on demand did not check
+   if it needs to go into submodules when non branches (most notably,
+   tags) are fetched.
+
+ * "log -p --graph" used with "--stat" had a few formatting error.
+
+ * Running "notes merge --commit" failed to perform correctly when run
+   from any directory inside $GIT_DIR/.  When "notes merge" stops with
+   conflicts, $GIT_DIR/NOTES_MERGE_WORKTREE is the place a user edits
+   to resolve it.
+
+ * The 'push to upstream' implementation was broken in some corner
+   cases. "git push $there" without refspec, when the current branch
+   is set to push to a remote different from $there, used to push to
+   $there using the upstream information to a remote unreleated to
+   $there.
+
+ * Giving "--continue" to a conflicted "rebase -i" session skipped a
+   commit that only results in changes to submodules.
+
+Also contains minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.10.2.txt b/Documentation/RelNotes/1.7.10.2.txt
new file mode 100644
index 0000000..7a7e9d6
--- /dev/null
+++ b/Documentation/RelNotes/1.7.10.2.txt
@@ -0,0 +1,85 @@
+Git v1.7.10.2 Release Notes
+===========================
+
+Fixes since v1.7.10.1
+---------------------
+
+ * The test scaffolding for git-daemon was flaky.
+
+ * The test scaffolding for fast-import was flaky.
+
+ * The filesystem boundary was not correctly reported when .git directory
+   discovery stopped at a mount point.
+
+ * HTTP transport that requires authentication did not work correctly when
+   multiple connections are used simultaneously.
+
+ * Minor memory leak during unpack_trees (hence "merge" and "checkout"
+   to check out another branch) has been plugged.
+
+ * In the older days, the header "Conflicts:" in "cherry-pick" and "merge"
+   was separated by a blank line from the list of paths that follow for
+   readability, but when "merge" was rewritten in C, we lost it by
+   mistake. Remove the newline from "cherry-pick" to make them match
+   again.
+
+ * The command line parser choked "git cherry-pick $name" when $name can
+   be both revision name and a pathname, even though $name can never be a
+   path in the context of the command.
+
+ * The "include.path" facility in the configuration mechanism added in
+   1.7.10 forgot to interpret "~/path" and "~user/path" as it should.
+
+ * "git config --rename-section" to rename an existing section into a
+   bogus one did not check the new name.
+
+ * The "diff --no-index" codepath used limited-length buffers, risking
+   pathnames getting truncated.  Update it to use the strbuf API.
+
+ * The report from "git fetch" said "new branch" even for a non branch
+   ref.
+
+ * The http-backend (the server side of the smart http transfer) used
+   to overwrite GIT_COMMITTER_NAME and GIT_COMMITTER_EMAIL with the
+   value obtained from REMOTE_USER unconditionally, making it
+   impossible for the server side site-specific customization to use
+   different identity sources to affect the names logged. It now uses
+   REMOTE_USER only as a fallback value.
+
+ * "log --graph" was not very friendly with "--stat" option and its
+   output had line breaks at wrong places.
+
+ * Octopus merge strategy did not reduce heads that are recorded in the
+   final commit correctly.
+
+ * "git push" over smart-http lost progress output a few releases ago;
+   this release resurrects it.
+
+ * The error and advice messages given by "git push" when it fails due
+   to non-ff were not very helpful to new users; it has been broken
+   into three cases, and each is given a separate advice message.
+
+ * The insn sheet given by "rebase -i" did not make it clear that the
+   insn lines can be re-ordered to affect the order of the commits in
+   the resulting history.
+
+ * "git repack" used to write out unreachable objects as loose objects
+   when repacking, even if such loose objects will immediately pruned
+   due to its age.
+
+ * A contrib script "rerere-train" did not work out of the box unless
+   user futzed with her $PATH.
+
+ * "git rev-parse --show-prefix" used to emit nothing when run at the
+   top-level of the working tree, but now it gives a blank line.
+
+ * The i18n of error message "git stash save" was not properly done.
+
+ * "git submodule" used a sed script that some platforms mishandled.
+
+ * When using a Perl script on a system where "perl" found on user's
+   $PATH could be ancient or otherwise broken, we allow builders to
+   specify the path to a good copy of Perl with $PERL_PATH.  The
+   gitweb test forgot to use that Perl when running its test.
+
+Also contains minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.10.3.txt b/Documentation/RelNotes/1.7.10.3.txt
new file mode 100644
index 0000000..703fbf1
--- /dev/null
+++ b/Documentation/RelNotes/1.7.10.3.txt
@@ -0,0 +1,43 @@
+Git v1.7.10.3 Release Notes
+===========================
+
+Fixes since v1.7.10.2
+---------------------
+
+ * The message file for German translation has been updated a bit.
+
+ * Running "git checkout" on an unborn branch used to corrupt HEAD.
+
+ * When checking out another commit from an already detached state, we
+   used to report all commits that are not reachable from any of the
+   refs as lossage, but some of them might be reachable from the new
+   HEAD, and there is no need to warn about them.
+
+ * Some time ago, "git clone" lost the progress output for its
+   "checkout" phase; when run without any "--quiet" option, it should
+   give progress to the lengthy operation.
+
+ * The directory path used in "git diff --no-index", when it recurses
+   down, was broken with a recent update after v1.7.10.1 release.
+
+ * "log -z --pretty=tformat:..." did not terminate each record with
+   NUL.  The fix is not entirely correct when the output also asks for
+   --patch and/or --stat, though.
+
+ * The DWIM behaviour for "log --pretty=format:%gd -g" was somewhat
+   broken and gave undue precedence to configured log.date, causing
+   "git stash list" to show "stash@{time stamp string}".
+
+ * "git status --porcelain" ignored "--branch" option by mistake.  The
+   output for "git status --branch -z" was also incorrect and did not
+   terminate the record for the current branch name with NUL as asked.
+
+ * When a submodule repository uses alternate object store mechanism,
+   some commands that were started from the superproject did not
+   notice it and failed with "No such object" errors.  The subcommands
+   of "git submodule" command that recursed into the submodule in a
+   separate process were OK; only the ones that cheated and peeked
+   directly into the submodule's repository from the primary process
+   were affected.
+
+Also contains minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.10.4.txt b/Documentation/RelNotes/1.7.10.4.txt
new file mode 100644
index 0000000..326670d
--- /dev/null
+++ b/Documentation/RelNotes/1.7.10.4.txt
@@ -0,0 +1,29 @@
+Git v1.7.10.4 Release Notes
+===========================
+
+Fixes since v1.7.10.3
+---------------------
+
+ * The message file for Swedish translation has been updated a bit.
+
+ * A name taken from mailmap was copied into an internal buffer
+   incorrectly and could overun the buffer if it is too long.
+
+ * A malformed commit object that has a header line chomped in the
+   middle could kill git with a NULL pointer dereference.
+
+ * An author/committer name that is a single character was mishandled
+   as an invalid name by mistake.
+
+ * The progress indicator for a large "git checkout" was sent to
+   stderr even if it is not a terminal.
+
+ * "git grep -e '$pattern'", unlike the case where the patterns are
+   read from a file, did not treat individual lines in the given
+   pattern argument as separate regular expressions as it should.
+
+ * When "git rebase" was given a bad commit to replay the history on,
+   its error message did not correctly give the command line argument
+   it had trouble parsing.
+
+Also contains minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.10.5.txt b/Documentation/RelNotes/1.7.10.5.txt
new file mode 100644
index 0000000..4db1770
--- /dev/null
+++ b/Documentation/RelNotes/1.7.10.5.txt
@@ -0,0 +1,12 @@
+Git v1.7.10.5 Release Notes
+===========================
+
+Fixes since v1.7.10.4
+---------------------
+
+ * "git fast-export" did not give a readable error message when the
+   same mark erroneously appeared twice in the --import-marks input.
+
+ * "git rebase -p" used to pay attention to rebase.autosquash which
+    was wrong.  "git rebase -p -i" should, but "git rebase -p" by
+    itself should not.
diff --git a/Documentation/RelNotes/1.7.10.txt b/Documentation/RelNotes/1.7.10.txt
new file mode 100644
index 0000000..58100bf
--- /dev/null
+++ b/Documentation/RelNotes/1.7.10.txt
@@ -0,0 +1,219 @@
+Git v1.7.10 Release Notes
+=========================
+
+Compatibility Notes
+-------------------
+
+ * From this release on, the "git merge" command in an interactive
+   session will start an editor when it automatically resolves the
+   merge for the user to explain the resulting commit, just like the
+   "git commit" command does when it wasn't given a commit message.
+
+   If you have a script that runs "git merge" and keeps its standard
+   input and output attached to the user's terminal, and if you do not
+   want the user to explain the resulting merge commits, you can
+   export GIT_MERGE_AUTOEDIT environment variable set to "no", like
+   this:
+
+	#!/bin/sh
+	GIT_MERGE_AUTOEDIT=no
+	export GIT_MERGE_AUTOEDIT
+
+   to disable this behavior (if you want your users to explain their
+   merge commits, you do not have to do anything).  Alternatively, you
+   can give the "--no-edit" option to individual invocations of the
+   "git merge" command if you know everybody who uses your script has
+   Git v1.7.8 or newer.
+
+ * The "--binary/-b" options to "git am" have been a no-op for quite a
+   while and were deprecated in mid 2008 (v1.6.0).  When you give these
+   options to "git am", it will now warn and ask you not to use them.
+
+ * When you do not tell which branches and tags to push to the "git
+   push" command in any way, the command used "matching refs" rule to
+   update remote branches and tags with branches and tags with the
+   same name you locally have.  In future versions of Git, this will
+   change to push out only your current branch according to either the
+   "upstream" or the "current" rule.  Although "upstream" may be more
+   powerful once the user understands Git better, the semantics
+   "current" gives is simpler and easier to understand for beginners
+   and may be a safer and better default option.  We haven't decided
+   yet which one to switch to.
+
+
+Updates since v1.7.9
+--------------------
+
+UI, Workflows & Features
+
+ * various "gitk" updates.
+   - show the path to the top level directory in the window title
+   - update preference edit dialog
+   - display file list correctly when directories are given on command line
+   - make "git-describe" output in the log message into a clickable link
+   - avoid matching the UNIX timestamp part when searching all fields
+   - give preference to symbolic font names like sans & monospace
+   - allow comparing two commits using a mark
+   - "gitk" honors log.showroot configuration.
+
+ * Teams for localizing the messages from the Porcelain layer of
+   commands are starting to form, thanks to Jiang Xin who volunteered
+   to be the localization coordinator.  Translated messages for
+   simplified Chinese, Swedish and Portuguese are available.
+
+ * The configuration mechanism learned an "include" facility; an
+   assignment to the include.path pseudo-variable causes the named
+   file to be included in-place when Git looks up configuration
+   variables.
+
+ * A content filter (clean/smudge) used to be just a way to make the
+   recorded contents "more useful", and allowed to fail; a filter can
+   now optionally be marked as "required".
+
+ * Options whose names begin with "--no-" (e.g. the "--no-verify"
+   option of the "git commit" command) can be negated by omitting
+   "no-" from its name, e.g. "git commit --verify".
+
+ * "git am" learned to pass "-b" option to underlying "git mailinfo", so
+   that a bracketed string other than "PATCH" at the beginning can be kept.
+
+ * "git clone" learned "--single-branch" option to limit cloning to a
+   single branch (surprise!); tags that do not point into the history
+   of the branch are not fetched.
+
+ * "git clone" learned to detach the HEAD in the resulting repository
+   when the user specifies a tag with "--branch" (e.g., "--branch=v1.0").
+   Clone also learned to print the usual "detached HEAD" advice in such
+   a case, similar to "git checkout v1.0".
+
+ * When showing a patch while ignoring whitespace changes, the context
+   lines are taken from the postimage, in order to make it easier to
+   view the output.
+
+ * "git diff --stat" learned to adjust the width of the output on
+   wider terminals, and give more columns to pathnames as needed.
+
+ * "diff-highlight" filter (in contrib/) was updated to produce more
+   aesthetically pleasing output.
+
+ * "fsck" learned "--no-dangling" option to omit dangling object
+   information.
+
+ * "git log -G" and "git log -S" learned to pay attention to the "-i"
+   option.  With "-i", "log -G" ignores the case when finding patch
+   hunks that introduce or remove a string that matches the given
+   pattern.  Similarly with "-i", "log -S" ignores the case when
+   finding the commit the given block of text appears or disappears
+   from the file.
+
+ * "git merge" in an interactive session learned to spawn the editor
+   by default to let the user edit the auto-generated merge message,
+   to encourage people to explain their merges better. Legacy scripts
+   can export GIT_MERGE_AUTOEDIT=no to retain the historical behavior.
+   Both "git merge" and "git pull" can be given --no-edit from the
+   command line to accept the auto-generated merge message.
+
+ * The advice message given when the user didn't give enough clue on
+   what to merge to "git pull" and "git merge" has been updated to
+   be more concise and easier to understand.
+
+ * "git push" learned the "--prune" option, similar to "git fetch".
+
+ * The whole directory that houses a top-level superproject managed by
+   "git submodule" can be moved to another place.
+
+ * "git symbolic-ref" learned the "--short" option to abbreviate the
+   refname it shows unambiguously.
+
+ * "git tag --list" can be given "--points-at <object>" to limit its
+   output to those that point at the given object.
+
+ * "gitweb" allows intermediate entries in the directory hierarchy
+   that leads to a project to be clicked, which in turn shows the
+   list of projects inside that directory.
+
+ * "gitweb" learned to read various pieces of information for the
+   repositories lazily, instead of reading everything that could be
+   needed (including the ones that are not necessary for a specific
+   task).
+
+ * Project search in "gitweb" shows the substring that matched in the
+   project name and description highlighted.
+
+ * HTTP transport learned to authenticate with a proxy if needed.
+
+ * A new script "diffall" is added to contrib/; it drives an
+   external tool to perform a directory diff of two Git revisions
+   in one go, unlike "difftool" that compares one file at a time.
+
+Foreign Interface
+
+ * Improved handling of views, labels and branches in "git-p4" (in contrib).
+
+ * "git-p4" (in contrib) suffered from unnecessary merge conflicts when
+   p4 expanded the embedded $RCS$-like keywords; it can be now told to
+   unexpand them.
+
+ * Some "git-svn" updates.
+
+ * "vcs-svn"/"svn-fe" learned to read dumps with svn-deltas and
+   support incremental imports.
+
+ * "git difftool/mergetool" learned to drive DeltaWalker.
+
+Performance
+
+ * Unnecessary calls to parse_object() "git upload-pack" makes in
+   response to "git fetch", have been eliminated, to help performance
+   in repositories with excessive number of refs.
+
+Internal Implementation (please report possible regressions)
+
+ * Recursive call chains in "git index-pack" to deal with long delta
+   chains have been flattened, to reduce the stack footprint.
+
+ * Use of add_extra_ref() API is now gone, to make it possible to
+   cleanly restructure the overall refs API.
+
+ * The command line parser of "git pack-objects" now uses parse-options
+   API.
+
+ * The test suite supports the new "test_pause" helper function.
+
+ * Parallel to the test suite, there is a beginning of performance
+   benchmarking framework.
+
+ * t/Makefile is adjusted to prevent newer versions of GNU make from
+   running tests in seemingly random order.
+
+ * The code to check if a path points at a file beyond a symbolic link
+   has been restructured to be thread-safe.
+
+ * When pruning directories that has become empty during "git prune"
+   and "git prune-packed", call closedir() that iterates over a
+   directory before rmdir() it.
+
+Also contains minor documentation updates and code clean-ups.
+
+
+Fixes since v1.7.9
+------------------
+
+Unless otherwise noted, all the fixes since v1.7.9 in the maintenance
+releases are contained in this release (see release notes to them for
+details).
+
+ * Build with NO_PERL_MAKEMAKER was broken and Git::I18N did not work
+   with versions of Perl older than 5.8.3.
+   (merge 5eb660e ab/perl-i18n later to maint).
+
+ * "git tag -s" honored "gpg.program" configuration variable since
+   1.7.9, but "git tag -v" and "git verify-tag" didn't.
+   (merge a2c2506 az/verify-tag-use-gpg-config later to maint).
+
+ * "configure" script learned to take "--with-sane-tool-path" from the
+   command line to record SANE_TOOL_PATH (used to avoid broken platform
+   tools in /usr/bin) in config.mak.autogen.  This may be useful for
+   people on Solaris who have saner tools outside /usr/xpg[46]/bin.
+
+ * zsh port of bash completion script needed another workaround.
diff --git a/Documentation/RelNotes/1.7.11.1.txt b/Documentation/RelNotes/1.7.11.1.txt
new file mode 100644
index 0000000..577ecca
--- /dev/null
+++ b/Documentation/RelNotes/1.7.11.1.txt
@@ -0,0 +1,9 @@
+Git v1.7.11.1 Release Notes
+===========================
+
+Fixes since v1.7.11
+-------------------
+
+ * The cross links in the HTML version of manual pages were broken.
+
+Also contains minor typofixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.11.2.txt b/Documentation/RelNotes/1.7.11.2.txt
new file mode 100644
index 0000000..a0d24d1
--- /dev/null
+++ b/Documentation/RelNotes/1.7.11.2.txt
@@ -0,0 +1,53 @@
+Git v1.7.11.2 Release Notes
+===========================
+
+Fixes since v1.7.11.1
+---------------------
+
+ * On Cygwin, the platform pread(2) is not thread safe, just like our
+   own compat/ emulation, and cannot be used in the index-pack
+   program.  Makefile variable NO_THREAD_SAFE_PREAD can be defined to
+   avoid use of this function in a threaded program.
+
+ * "git add" allows adding a regular file to the path where a
+   submodule used to exist, but "git update-index" does not allow an
+   equivalent operation to Porcelain writers.
+
+ * "git archive" incorrectly computed the header checksum; the symptom
+   was observed only when using pathnames with hi-bit set.
+
+ * "git blame" did not try to make sure that the abbreviated commit
+   object names in its output are unique.
+
+ * Running "git bundle verify" on a bundle that records a complete
+   history said "it requires these 0 commits".
+
+ * "git clone --single-branch" to clone a single branch did not limit
+   the cloning to the specified branch.
+
+ * "git diff --no-index" did not correctly handle relative paths and
+   did not correctly give exit codes when run under "--quiet" option.
+
+ * "git diff --no-index" did not work with pagers correctly.
+
+ * "git diff COPYING HEAD:COPYING" gave a nonsense error message that
+   claimed that the treeish HEAD did not have COPYING in it.
+
+ * When "git log" gets "--simplify-merges/by-decoration" together with
+   "--first-parent", the combination of these options makes the
+   simplification logic to use in-core commit objects that haven't
+   been examined for relevance, either producing incorrect result or
+   taking too long to produce any output.  Teach the simplification
+   logic to ignore commits that the first-parent traversal logic
+   ignored when both are in effect to work around the issue.
+
+ * "git ls-files --exclude=t -i" did not consider anything under t/ as
+   excluded, as it did not pay attention to exclusion of leading paths
+   while walking the index.  Other two users of excluded() are also
+   updated.
+
+ * "git request-pull $url dev" when the tip of "dev" branch was tagged
+   with "ext4-for-linus" used the contents from the tag in the output
+   but still asked the "dev" branch to be pulled, not the tag.
+
+Also contains minor typofixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.11.3.txt b/Documentation/RelNotes/1.7.11.3.txt
new file mode 100644
index 0000000..64494f8
--- /dev/null
+++ b/Documentation/RelNotes/1.7.11.3.txt
@@ -0,0 +1,53 @@
+Git v1.7.11.3 Release Notes
+===========================
+
+Fixes since v1.7.11.3
+---------------------
+
+ * The error message from "git push $there :bogo" (and its equivalent
+   "git push $there --delete bogo") mentioned that we tried and failed
+   to guess what ref is being deleted based on the LHS of the refspec,
+   which we don't.
+
+ * A handful of files and directories we create had tighter than
+   necessary permission bits when the user wanted to have group
+   writability (e.g. by setting "umask 002").
+
+ * "commit --amend" used to refuse amending a commit with an empty log
+   message, with or without "--allow-empty-message".
+
+ * "git commit --amend --only --" was meant to allow "Clever" people to
+   rewrite the commit message without making any change even when they
+   have already changes for the next commit added to their index, but
+   it never worked as advertised since it was introduced in 1.3.0 era.
+
+ * Even though the index can record pathnames longer than 1<<12 bytes,
+   in some places we were not comparing them in full, potentially
+   replacing index entries instead of adding.
+
+ * "git show"'s auto-walking behaviour was an unreliable and
+   unpredictable hack; it now behaves just like "git log" does when it
+   walks.
+
+ * "git diff", "git status" and anything that internally uses the
+   comparison machinery was utterly broken when the difference
+   involved a file with "-" as its name.  This was due to the way "git
+   diff --no-index" was incorrectly bolted on to the system, making
+   any comparison that involves a file "-" at the root level
+   incorrectly read from the standard input.
+
+ * We did not have test to make sure "git rebase" without extra options
+   filters out an empty commit in the original history.
+
+ * "git fast-export" produced an input stream for fast-import without
+   properly quoting pathnames when they contain SPs in them.
+
+ * "git checkout --detach", when you are still on an unborn branch,
+   should be forbidden, but it wasn't.
+
+ * Some implementations of Perl terminates "lines" with CRLF even when
+   the script is operating on just a sequence of bytes.  Make sure to
+   use "$PERL_PATH", the version of Perl the user told Git to use, in
+   our tests to avoid unnecessary breakages in tests.
+
+Also contains minor typofixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.11.txt b/Documentation/RelNotes/1.7.11.txt
new file mode 100644
index 0000000..15b954c
--- /dev/null
+++ b/Documentation/RelNotes/1.7.11.txt
@@ -0,0 +1,139 @@
+Git v1.7.11 Release Notes
+=========================
+
+Updates since v1.7.10
+---------------------
+
+UI, Workflows & Features
+
+ * A new mode for push, "simple", which is a cross between "current"
+   and "upstream", has been introduced. "git push" without any refspec
+   will push the current branch out to the same name at the remote
+   repository only when it is set to track the branch with the same
+   name over there.  The plan is to make this mode the new default
+   value when push.default is not configured.
+
+ * A couple of commands learned the "--column" option to produce
+   columnar output.
+
+ * A third-party tool "git subtree" is distributed in contrib/
+
+ * A remote helper that acts as a proxy and caches ssl session for the
+   https:// transport is added to the contrib/ area.
+
+ * Error messages given when @{u} is used for a branch without its
+   upstream configured have been clarified.
+
+ * Even with the "-q"uiet option, "checkout" used to report setting up
+   tracking.  Also "branch" learned the "-q"uiet option to squelch
+   informational message.
+
+ * Your build platform may support hardlinks but you may prefer not to
+   use them, e.g. when installing to DESTDIR to make a tarball and
+   untarring on a filesystem that has poor support for hardlinks.
+   There is a Makefile option NO_INSTALL_HARDLINKS for you.
+
+ * The smart-http backend used to always override GIT_COMMITTER_*
+   variables with REMOTE_USER and REMOTE_ADDR, but these variables are
+   now preserved when set.
+
+ * "git am" learned the "--include" option, which is an opposite of
+   existing the "--exclude" option.
+
+ * When "git am -3" needs to fall back to an application of the patch
+   to a synthesized preimage followed by a 3-way merge, the paths that
+   needed such treatment are now reported to the end user, so that the
+   result in them can be eyeballed with extra care.
+
+ * The output from "diff/log --stat" used to always allocate 4 columns
+   to show the number of modified lines, but not anymore.
+
+ * "git difftool" learned the "--dir-diff" option to spawn external
+   diff tools that can compare two directory hierarchies at a time
+   after populating two temporary directories, instead of running an
+   instance of the external tool once per a file pair.
+
+ * The "fmt-merge-msg" command learned to list the primary contributors
+   involved in the side topic you are merging in a comment in the merge
+   commit template.
+
+ * "git rebase" learned to optionally keep commits that do not
+   introduce any change in the original history.
+
+ * "git push --recurse-submodules" learned to optionally look into the
+   histories of submodules bound to the superproject and push them
+   out.
+
+ * A 'snapshot' request to "gitweb" honors If-Modified-Since: header,
+   based on the commit date.
+
+ * "gitweb" learned to highlight the patch it outputs even more.
+
+Foreign Interface
+
+ * "git svn" used to die with unwanted SIGPIPE when talking with an HTTP
+   server that uses keep-alive.
+
+ * "git svn" learned to use platform specific authentication
+   providers, e.g. gnome-keyring, kwallet, etc.
+
+ * "git p4" has been moved out of the contrib/ area and has seen more
+   work on importing labels as tags from (and exporting tags as labels
+   to) p4.
+
+Performance and Internal Implementation (please report possible regressions)
+
+ * Bash completion script (in contrib/) have been cleaned up to make
+   future work on it simpler.
+
+ * An experimental "version 4" format of the index file has been
+   introduced to reduce on-disk footprint and I/O overhead.
+
+ * "git archive" learned to produce its output without reading the
+   blob object it writes out in memory in its entirety.
+
+ * "git index-pack" that runs when fetching or pushing objects to
+   complete the packfile on the receiving end learned to use multiple
+   threads to do its job when available.
+
+ * The code to compute hash values for lines used by the internal diff
+   engine was optimized on little-endian machines, using the same
+   trick the kernel folks came up with.
+
+ * "git apply" had some memory leaks plugged.
+
+ * Setting up a revision traversal with many starting points was
+   inefficient as these were placed in a date-order priority queue
+   one-by-one.  Now they are collected in the queue unordered first,
+   and sorted immediately before getting used.
+
+ * More lower-level commands learned to use the streaming API to read
+   from the object store without keeping everything in core.
+
+ * The weighting parameters to suggestion command name typo have been
+   tweaked, so that "git tags" will suggest "tag?" and not "stage?".
+
+ * Because "sh" on the user's PATH may be utterly broken on some
+   systems, run-command API now uses SHELL_PATH, not /bin/sh, when
+   spawning an external command (not applicable to Windows port).
+
+ * The API to iterate over the refs/ hierarchy has been tweaked to
+   allow walking only a subset of it more efficiently.
+
+Also contains minor documentation updates and code clean-ups.
+
+
+Fixes since v1.7.10
+-------------------
+
+Unless otherwise noted, all the fixes since v1.7.10 in the maintenance
+releases are contained in this release (see release notes to them for
+details).
+
+ * "git submodule init" used to report "registered for path ..."
+   even for submodules that were registered earlier.
+   (cherry-pick c1c259e jl/submodule-report-new-path-once later to maint).
+
+ * "git diff --stat" used to fully count a binary file with modified
+   execution bits whose contents is unmodified, which was not quite
+   right.
diff --git a/Documentation/RelNotes/1.7.12.txt b/Documentation/RelNotes/1.7.12.txt
new file mode 100644
index 0000000..eddef23
--- /dev/null
+++ b/Documentation/RelNotes/1.7.12.txt
@@ -0,0 +1,140 @@
+Git v1.7.12 Release Notes
+=========================
+
+Updates since v1.7.11
+---------------------
+
+UI, Workflows & Features
+
+ * Git can be told to normalize pathnames it read from readdir(3) and
+   all arguments it got from the command line into precomposed UTF-8
+   (assuming that they come as decomposed UTF-8), in order to work
+   around issues on Mac OS.
+
+   I think there still are other places that need conversion
+   (e.g. paths that are read from stdin for some commands), but this
+   should be a good first step in the right direction.
+
+ * Per-user $HOME/.gitconfig file can optionally be stored in
+   $HOME/.config/git/config instead, which is in line with XDG.
+
+ * The value of core.attributesfile and core.excludesfile default to
+   $HOME/.config/attributes and $HOME/.config/ignore respectively when
+   these files exist.
+
+ * Logic to disambiguate abbreviated object names have been taught to
+   take advantage of object types that are expected in the context,
+   e.g. XXXXXX in the "git describe" output v1.2.3-gXXXXXX must be a
+   commit object, not a blob nor a tree.  This will help us prolong
+   the lifetime of abbreviated object names.
+
+ * "git apply" learned to wiggle the base version and perform three-way
+   merge when a patch does not exactly apply to the version you have.
+
+ * Scripted Porcelain writers now have access to the credential API via
+   the "git credential" plumbing command.
+
+ * "git help" used to always default to "man" format even on platforms
+   where "man" viewer is not widely available.
+
+ * "git clone --local $path" started its life as an experiment to
+   optionally use link/copy when cloning a repository on the disk, but
+   we didn't deprecate it after we made the option a no-op to always
+   use the optimization.  The command learned "--no-local" option to
+   turn this off, as a more explicit alternative over use of file://
+   URL.
+
+ * "git fetch" and friends used to say "remote side hung up
+   unexpectedly" when they failed to get response they expect from the
+   other side, but one common reason why they don't get expected
+   response is that the remote repository does not exist or cannot be
+   read. The error message in this case was updated to give better
+   hints to the user.
+
+ * git native protocol agents learned to show software version over
+   the wire, so that the server log can be examined to see the vintage
+   distribution of clients.
+
+ * "git help -w $cmd" can show HTML version of documentation for
+   "git-$cmd" by setting help.htmlpath to somewhere other than the
+   default location where the build procedure installs them locally;
+   the variable can even point at a http:// URL.
+
+ * "git rebase [-i] --root $tip" can now be used to rewrite all the
+   history leading to "$tip" down to the root commit.
+
+ * "git rebase -i" learned "-x <cmd>" to insert "exec <cmd>" after
+   each commit in the resulting history.
+
+ * "git status" gives finer classification to various states of paths
+   in conflicted state and offer advice messages in its output.
+
+ * "git submodule" learned to deal with nested submodule structure
+   where a module is contained within a module whose origin is
+   specified as a relative URL to its superproject's origin.
+
+ * A rather heavy-ish "git completion" script has been split to create
+   a separate "git prompting" script, to help lazy-autoloading of the
+   completion part while making prompting part always available.
+
+
+Foreign Interface
+
+ * "mediawiki" remote helper (in contrib/) learned to handle file
+   attachments.
+
+ * "git p4" now uses "Jobs:" and "p4 move" when appropriate.
+
+ * vcs-svn has been updated to clean-up compilation, lift 32-bit
+   limitations, etc.
+
+
+Performance, Internal Implementation, etc. (please report possible regressions)
+
+ * Some tests showed false failures caused by a bug in ecryptofs.
+
+ * We no longer use AsciiDoc7 syntax in our documentation and favor a
+   more modern style.
+
+ * "git am --rebasing" codepath was taught to grab authorship, log
+   message and the patch text directly out of existing commits.  This
+   will help rebasing commits that have confusing "diff" output in
+   their log messages.
+
+ * "git index-pack" and "git pack-objects" use streaming API to read
+   from the object store to avoid having to hold a large blob object
+   in-core while they are doing their thing.
+
+ * Code to match paths with exclude patterns learned to avoid calling
+   fnmatch() by comparing fixed leading substring literally when
+   possible.
+
+ * "git log -n 1 -- rarely-touched-path" was spending unnecessary
+   cycles after showing the first change to find the next one, only to
+   discard it.
+
+
+Also contains minor documentation updates and code clean-ups.
+
+
+Fixes since v1.7.11
+-------------------
+
+Unless otherwise noted, all the fixes since v1.7.11 in the maintenance
+releases are contained in this release (see release notes to them for
+details).
+
+ * When "git am" failed, old timers knew to check .git/rebase-apply/patch
+   to see what went wrong, but we never told the users about it.
+   (merge 14bf2d5 pg/maint-1.7.9-am-where-is-patch later to maint).
+
+ * When "git submodule add" clones a submodule repository, it can get
+   confused where to store the resulting submodule repository in the
+   superproject's .git/ directory when there is a symbolic link in the
+   path to the current directory.
+   (merge 6eafa6d jl/maint-1.7.10-recurse-submodules-with-symlink later to maint).
+
+ * In 1.7.9 era, we taught "git rebase" about the raw timestamp format
+   but we did not teach the same trick to "filter-branch", which rolled
+   a similar logic on its own.
+   (merge 44b85e89 jc/maint-filter-branch-epoch-date later to maint).
diff --git a/Documentation/RelNotes/1.7.2.1.txt b/Documentation/RelNotes/1.7.2.1.txt
new file mode 100644
index 0000000..1103c47
--- /dev/null
+++ b/Documentation/RelNotes/1.7.2.1.txt
@@ -0,0 +1,25 @@
+Git v1.7.2.1 Release Notes
+==========================
+
+Fixes since v1.7.2
+------------------
+
+ * "git instaweb" wasn't useful when your Apache was installed under a
+   name other than apache2 (e.g. "httpd").
+
+ * Similarly, "git web--browse" (invoked by "git help -w") learned that
+   chrome browser is sometimes called google-chrome.
+
+ * An overlong line after ".gitdir: " in a git file caused out of bounds
+   access to an array on the stack.
+
+ * "git config --path conf.var" to attempt to expand a variable conf.var
+   that uses "~/" short-hand segfaulted when $HOME environment variable
+   was not set.
+
+ * Documentation on Cygwin failed to build.
+
+ * The error message from "git pull blarg" when 'blarg' is an unknown
+   remote name has been improved.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.2.2.txt b/Documentation/RelNotes/1.7.2.2.txt
new file mode 100644
index 0000000..71eb6a8
--- /dev/null
+++ b/Documentation/RelNotes/1.7.2.2.txt
@@ -0,0 +1,22 @@
+Git v1.7.2.2 Release Notes
+==========================
+
+Fixes since v1.7.2.1
+--------------------
+
+ * Object transfer over smart http transport deadlocked the client when
+   the remote HTTP server returned a failure, instead of erroring it out.
+
+ * git-gui honors custom textconv filters when showing diff and blame;
+
+ * git diff --relative=subdir (without the necessary trailing /) did not
+   work well;
+
+ * "git diff-files -p --submodule" was recently broken;
+
+ * "git checkout -b n ':/token'" did not work;
+
+ * "git index-pack" (hence "git fetch/clone/pull/push") enabled the object
+   replacement machinery by mistake (it never should have);
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.2.3.txt b/Documentation/RelNotes/1.7.2.3.txt
new file mode 100644
index 0000000..610960c
--- /dev/null
+++ b/Documentation/RelNotes/1.7.2.3.txt
@@ -0,0 +1,39 @@
+Git v1.7.2.3 Release Notes
+==========================
+
+Fixes since v1.7.2.2
+--------------------
+
+ * When people try insane things such as delta-compressing 4GiB files, we
+   threw an assertion failure.
+
+ * "git archive" gave the full commit ID for "$Format:%h$".
+
+ * "git fetch --tags" did not fetch tags when remote.<nick>.tagopt was set
+   to --no-tags.  The command line option now overrides the configuration
+   setting.
+
+ * "git for-each-ref --format='%(objectname:short)'" has been completely
+   broken for a long time.
+
+ * "git gc" incorrectly pruned a rerere record that was created long
+   time ago but still is actively and repeatedly used.
+
+ * "git log --follow -M -p" was seriously broken in 1.7.2, reporting
+   assertion failure.
+
+ * Running "git log" with an incorrect option started pager nevertheless,
+   forcing the user to dismiss it.
+
+ * "git rebase" did not work well when the user has diff.renames
+   configuration variable set.
+
+ * An earlier (and rather old) fix to "git rebase" against a rebased
+   upstream broke a more normal, non rebased upstream case rather badly,
+   attempting to re-apply patches that are already accepted upstream.
+
+ * "git submodule sync" forgot to update the superproject's config file
+   when submodule URL changed.
+
+ * "git pack-refs --all --prune" did not remove a directory that has
+   become empty.
diff --git a/Documentation/RelNotes/1.7.2.4.txt b/Documentation/RelNotes/1.7.2.4.txt
new file mode 100644
index 0000000..f7950a4
--- /dev/null
+++ b/Documentation/RelNotes/1.7.2.4.txt
@@ -0,0 +1,10 @@
+Git v1.7.2.4 Release Notes
+==========================
+
+This is primarily to backport support for the new "add.ignoreErrors"
+name given to the existing "add.ignore-errors" configuration variable.
+
+The next version, Git 1.7.4, and future versions, will support both
+old and incorrect name and the new corrected name, but without this
+backport, users who want to use the new name "add.ignoreErrors" in
+their repositories cannot use older versions of Git.
diff --git a/Documentation/RelNotes/1.7.2.5.txt b/Documentation/RelNotes/1.7.2.5.txt
new file mode 100644
index 0000000..bf976c4
--- /dev/null
+++ b/Documentation/RelNotes/1.7.2.5.txt
@@ -0,0 +1,8 @@
+Git v1.7.2.5 Release Notes
+==========================
+
+Fixes since v1.7.2.4
+--------------------
+
+ * "gitweb" can sometimes be tricked into parrotting a filename argument
+   given in a request without properly quoting.
diff --git a/Documentation/RelNotes/1.7.2.txt b/Documentation/RelNotes/1.7.2.txt
new file mode 100644
index 0000000..15cf011
--- /dev/null
+++ b/Documentation/RelNotes/1.7.2.txt
@@ -0,0 +1,151 @@
+Git v1.7.2 Release Notes
+========================
+
+Updates since v1.7.1
+--------------------
+
+ * core.eol configuration and text/eol attributes are the new way to control
+   the end of line conventions for files in the working tree.
+
+ * core.autocrlf has been made safer - it will now only handle line
+   endings for new files and files that are LF-only in the
+   repository. To normalize content that has been checked in with
+   CRLF, use the new eol/text attributes.
+
+ * The whitespace rules used in "git apply --whitespace" and "git diff"
+   gained a new member in the family (tab-in-indent) to help projects with
+   policy to indent only with spaces.
+
+ * When working from a subdirectory, by default, git does not look for its
+   metadirectory ".git" across filesystems, primarily to help people who
+   have invocations of git in their custom PS1 prompts, as being outside
+   of a git repository would look for ".git" all the way up to the root
+   directory, and NFS mounts are often slow.  DISCOVERY_ACROSS_FILESYSTEM
+   environment variable can be used to tell git not to stop at a
+   filesystem boundary.
+
+ * Usage help messages generated by parse-options library (i.e. most
+   of the Porcelain commands) are sent to the standard output now.
+
+ * ':/<string>' notation to look for a commit now takes regular expression
+   and it is not anchored at the beginning of the commit log message
+   anymore (this is a backward incompatible change).
+
+ * "git" wrapper learned "-c name=value" option to override configuration
+   variable from the command line.
+
+ * Improved portability for various platforms including older SunOS,
+   HP-UX 10/11, AIX, Tru64, etc. and platforms with Python 2.4.
+
+ * The message from "git am -3" has been improved when conflict
+   resolution ended up making the patch a no-op.
+
+ * "git blame" applies the textconv filter to the contents it works
+   on, when available.
+
+ * "git checkout --orphan newbranch" is similar to "-b newbranch" but
+   prepares to create a root commit that is not connected to any existing
+   commit.
+
+ * "git cherry-pick" learned to pick a range of commits
+   (e.g. "cherry-pick A..B" and "cherry-pick --stdin"), so did "git
+   revert"; these do not support the nicer sequencing control "rebase
+   [-i]" has, though.
+
+ * "git cherry-pick" and "git revert" learned --strategy option to specify
+   the merge strategy to be used when performing three-way merges.
+
+ * "git cvsserver" can be told to use pserver; its password file can be
+   stored outside the repository.
+
+ * The output from the textconv filter used by "git diff" can be cached to
+   speed up their reuse.
+
+ * "git diff --word-diff=<mode>" extends the existing "--color-words"
+   option, making it more useful in color-challenged environments.
+
+ * The regexp to detect function headers used by "git diff" for PHP has
+   been enhanced for visibility modifiers (public, protected, etc.) to
+   better support PHP5.
+
+ * "diff.noprefix" configuration variable can be used to implicitly
+   ask for "diff --no-prefix" behaviour.
+
+ * "git for-each-ref" learned "%(objectname:short)" that gives the object
+   name abbreviated.
+
+ * "git format-patch" learned --signature option and format.signature
+   configuration variable to customize the e-mail signature used in the
+   output.
+
+ * Various options to "git grep" (e.g. --count, --name-only) work better
+   with binary files.
+
+ * "git grep" learned "-Ovi" to open the files with hits in your editor.
+
+ * "git help -w" learned "chrome" and "chromium" browsers.
+
+ * "git log --decorate" shows commit decorations in various colours.
+
+ * "git log --follow <path>" follows across copies (it used to only follow
+   renames).  This may make the processing more expensive.
+
+ * "git log --pretty=format:<template>" specifier learned "% <something>"
+   magic that inserts a space only when %<something> expands to a
+   non-empty string; this is similar to "%+<something>" magic, but is
+   useful in a context to generate a single line output.
+
+ * "git notes prune" learned "-n" (dry-run) and "-v" options, similar to
+   what "git prune" has.
+
+ * "git patch-id" can be fed a mbox without getting confused by the
+   signature line in the format-patch output.
+
+ * "git remote" learned "set-branches" subcommand.
+
+ * "git rev-list A..B" learned --ancestry-path option to further limit
+   the result to the commits that are on the ancestry chain between A and
+   B (i.e. commits that are not descendants of A are excluded).
+
+ * "git show -5" is equivalent to "git show --do-walk 5"; this is similar
+   to the update to make "git show master..next" walk the history,
+   introduced in 1.6.4.
+
+ * "git status [-s] --ignored" can be used to list ignored paths.
+
+ * "git status -s -b" shows the current branch in the output.
+
+ * "git status" learned "--ignore-submodules" option.
+
+ * Various "gitweb" enhancements and clean-ups, including syntax
+   highlighting, "plackup" support for instaweb, .fcgi suffix to run
+   it as FastCGI script, etc.
+
+ * The test harness has been updated to produce TAP-friendly output.
+
+ * Many documentation improvement patches are also included.
+
+
+Fixes since v1.7.1
+------------------
+
+All of the fixes in v1.7.1.X maintenance series are included in this
+release, unless otherwise noted.
+
+ * We didn't URL decode "file:///path/to/repo" correctly when path/to/repo
+   had percent-encoded characters (638794c, 9d2e942, ce83eda, 3c73a1d).
+
+ * "git clone" did not configure remote.origin.url correctly for bare
+   clones (df61c889).
+
+ * "git diff --graph" works better with "--color-words" and other options
+   (81fa024..4297c0a).
+
+ * "git diff" could show ambiguous abbreviation of blob object names on
+   its "index" line (3e5a188).
+
+ * "git reset --hard" started from a wrong directory and a working tree in
+   a nonstandard location is in use got confused (560fb6a1).
+
+ * "git read-tree -m A B" used to switch to branch B while retaining
+   local changes added an incorrect cache-tree information (b1f47514).
diff --git a/Documentation/RelNotes/1.7.3.1.txt b/Documentation/RelNotes/1.7.3.1.txt
new file mode 100644
index 0000000..002c93b
--- /dev/null
+++ b/Documentation/RelNotes/1.7.3.1.txt
@@ -0,0 +1,14 @@
+Git v1.7.3.1 Release Notes
+==========================
+
+Fixes since v1.7.3
+------------------
+
+ * "git stash show stash@{$n}" was accidentally broken in 1.7.3 ("git
+   stash show" without any argument still worked, though).
+
+ * "git stash branch $branch stash@{$n}" was accidentally broken in
+   1.7.3 and started dropping the named stash even when branch creation
+   failed.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.3.2.txt b/Documentation/RelNotes/1.7.3.2.txt
new file mode 100644
index 0000000..5c93b85
--- /dev/null
+++ b/Documentation/RelNotes/1.7.3.2.txt
@@ -0,0 +1,5 @@
+Git v1.7.3.2 Release Notes
+==========================
+
+This is primarily to push out many documentation fixes accumulated since
+the 1.7.3.1 release.
diff --git a/Documentation/RelNotes/1.7.3.3.txt b/Documentation/RelNotes/1.7.3.3.txt
new file mode 100644
index 0000000..9b2b244
--- /dev/null
+++ b/Documentation/RelNotes/1.7.3.3.txt
@@ -0,0 +1,54 @@
+Git v1.7.3.3 Release Notes
+==========================
+
+In addition to the usual fixes, this release also includes support for
+the new "add.ignoreErrors" name given to the existing "add.ignore-errors"
+configuration variable.
+
+The next version, Git 1.7.4, and future versions, will support both
+old and incorrect name and the new corrected name, but without this
+backport, users who want to use the new name "add.ignoreErrors" in
+their repositories cannot use older versions of Git.
+
+Fixes since v1.7.3.2
+--------------------
+
+ * "git apply" segfaulted when a bogus input is fed to it.
+
+ * Running "git cherry-pick --ff" on a root commit segfaulted.
+
+ * "diff", "blame" and friends incorrectly applied textconv filters to
+   symlinks.
+
+ * Highlighting of whitespace breakage in "diff" output was showing
+   incorrect amount of whitespaces when blank-at-eol is set and the line
+   consisted only of whitespaces and a TAB.
+
+ * "diff" was overly inefficient when trying to find the line to use for
+   the function header (i.e. equivalent to --show-c-function of GNU diff).
+
+ * "git imap-send" depends on libcrypto but our build rule relied on the
+   linker to implicitly link it via libssl, which was wrong.
+
+ * "git merge-file" can be called from within a subdirectory now.
+
+ * "git repack -f" expanded and recompressed non-delta objects in the
+   existing pack, which was wasteful.  Use new "-F" option if you really
+   want to (e.g. when changing the pack.compression level).
+
+ * "git rev-list --format="...%x00..." incorrectly chopped its output
+   at NUL.
+
+ * "git send-email" did not correctly remove duplicate mail addresses from
+   the Cc: header that appear on the To: header.
+
+ * The completion script (in contrib/completion) ignored lightweight tags
+   in __git_ps1().
+
+ * "git-blame" mode (in contrib/emacs) didn't say (require 'format-spec)
+   even though it depends on it; it didn't work with Emacs 22 or older
+   unless Gnus is used.
+
+ * "git-p4" (in contrib/) did not correctly handle deleted files.
+
+Other minor fixes and documentation updates are also included.
diff --git a/Documentation/RelNotes/1.7.3.4.txt b/Documentation/RelNotes/1.7.3.4.txt
new file mode 100644
index 0000000..e57f7c1
--- /dev/null
+++ b/Documentation/RelNotes/1.7.3.4.txt
@@ -0,0 +1,45 @@
+Git v1.7.3.4 Release Notes
+==========================
+
+Fixes since v1.7.3.3
+--------------------
+
+ * Smart HTTP transport used to incorrectly retry redirected POST
+   request with GET request.
+
+ * "git apply" did not correctly handle patches that only change modes
+   if told to apply while stripping leading paths with -p option.
+
+ * "git apply" can deal with patches with timezone formatted with a
+   colon between the hours and minutes part (e.g. "-08:00" instead of
+   "-0800").
+
+ * "git checkout" removed an untracked file "foo" from the working
+   tree when switching to a branch that contains a tracked path
+   "foo/bar".  Prevent this, just like the case where the conflicting
+   path were "foo" (c752e7f..7980872d).
+
+ * "git cherry-pick" or "git revert" refused to work when a path that
+   would be modified by the operation was stat-dirty without a real
+   difference in the contents of the file.
+
+ * "git diff --check" reported an incorrect line number for added
+   blank lines at the end of file.
+
+ * "git imap-send" failed to build under NO_OPENSSL.
+
+ * Setting log.decorate configuration variable to "0" or "1" to mean
+   "false" or "true" did not work.
+
+ * "git push" over dumb HTTP protocol did not work against WebDAV
+   servers that did not terminate a collection name with a slash.
+
+ * "git tag -v" did not work with GPG signatures in rfc1991 mode.
+
+ * The post-receive-email sample hook was accidentally broken in 1.7.3.3
+   update.
+
+ * "gitweb" can sometimes be tricked into parrotting a filename argument
+   given in a request without properly quoting.
+
+Other minor fixes and documentation updates are also included.
diff --git a/Documentation/RelNotes/1.7.3.5.txt b/Documentation/RelNotes/1.7.3.5.txt
new file mode 100644
index 0000000..40f3ba5
--- /dev/null
+++ b/Documentation/RelNotes/1.7.3.5.txt
@@ -0,0 +1,34 @@
+Git 1.7.3.5 Release Notes
+=========================
+
+ * The xfuncname pattern used by "git diff" and "git grep" to show the
+   last notable line in context were broken for python and ruby for a long
+   time.
+
+ * "git merge" into an unborn branch removed an untracked file "foo" from
+   the working tree when merged branch had "foo" (this fix was already in
+   1.7.3.3 but was omitted from the release notes by mistake).
+
+ * "git status -s" did not quote unprintable characters in paths as
+   documented.
+
+ * "git am --abort" used to always reset to the commit at the beginning of
+   the last "am" invocation that has stopped, losing any unrelated commits
+   that may have been made since then.  Now it refrains from doing so and
+   instead issues a warning.
+
+ * "git blame" incorrectly reused bogusly cached result of textconv
+   filter for files from the working tree.
+
+ * "git commit" used to abort after the user edited the log message
+   when the committer information was not correctly set up.  It now
+   aborts before starting the editor.
+
+ * "git commit --date=invalid" used to silently ignore the incorrectly
+   specified date; it is now diagnosed as an error.
+
+ * "git rebase --skip" to skip the last commit in a series used to fail
+   to run post-rewrite hook and to copy notes from old commits that have
+   successfully been rebased so far.  Now it do (backmerge ef88ad2).
+
+ * "gitweb" tried to show a wrong feed logo when none was specified.
diff --git a/Documentation/RelNotes/1.7.3.txt b/Documentation/RelNotes/1.7.3.txt
new file mode 100644
index 0000000..309c331
--- /dev/null
+++ b/Documentation/RelNotes/1.7.3.txt
@@ -0,0 +1,76 @@
+Git v1.7.3 Release Notes
+========================
+
+Updates since v1.7.2
+--------------------
+
+ * git-gui, now at version 0.13.0, got various updates and a new
+   maintainer, Pat Thoyts.
+
+ * Gitweb allows its configuration to change per each request; it used to
+   read the configuration once upon startup.
+
+ * When git finds a corrupt object, it now reports the file that contains
+   it.
+
+ * "git checkout -B <it>" is a shorter way to say "git branch -f <it>"
+   followed by "git checkout <it>".
+
+ * When "git checkout" or "git merge" refuse to proceed in order to
+   protect local modification to your working tree, they used to stop
+   after showing just one path that might be lost.  They now show all,
+   in a format that is easier to read.
+
+ * "git clean" learned "-e" ("--exclude") option.
+
+ * Hunk headers produced for C# files by "git diff" and friends show more
+   relevant context than before.
+
+ * diff.ignoresubmodules configuration variable can be used to squelch the
+   differences in submodules reported when running commands (e.g. "diff",
+   "status", etc.) at the superproject level.
+
+ * http.useragent configuration can be used to lie who you are to your
+   restrictive firewall.
+
+ * "git rebase --strategy <s>" learned "-X" option to pass extra options
+   that are understood by the chosen merge strategy.
+
+ * "git rebase -i" learned "exec" that you can insert into the insn sheet
+   to run a command between its steps.
+
+ * "git rebase" between branches that have many binary changes that do
+   not conflict should be faster.
+
+ * "git rebase -i" peeks into rebase.autosquash configuration and acts as
+   if you gave --autosquash from the command line.
+
+
+Also contains various documentation updates.
+
+
+Fixes since v1.7.2
+------------------
+
+All of the fixes in v1.7.2.X maintenance series are included in this
+release, unless otherwise noted.
+
+ * "git merge -s recursive" (which is the default) did not handle cases
+   where a directory becomes a file (or vice versa) very well.
+
+ * "git fetch" and friends were accidentally broken for url with "+" in
+   its path, e.g. "git://git.gnome.org/gtk+".
+
+ * "git fetch $url" (i.e. without refspecs) was broken for quite some
+   time, if the current branch happen to be tracking some remote.
+
+ * "git ls-tree dir dirgarbage", when "dir" was a directory,
+   incorrectly recursed into "dir".
+
+ * "git note remove" created unnecessary extra commit when named object
+   did not have any note to begin with.
+
+ * "git rebase" did not work well if you had diff.noprefix configured.
+
+ * "git -c foo=bar subcmd" did not work well for subcmd that is not
+   implemented as a built-in command.
diff --git a/Documentation/RelNotes/1.7.4.1.txt b/Documentation/RelNotes/1.7.4.1.txt
new file mode 100644
index 0000000..79923a6
--- /dev/null
+++ b/Documentation/RelNotes/1.7.4.1.txt
@@ -0,0 +1,27 @@
+Git v1.7.4.1 Release Notes
+==========================
+
+Fixes since v1.7.4
+------------------
+
+ * On Windows platform, the codepath to spawn a new child process forgot
+   to first flush the output buffer.
+
+ * "git bundle" did not use OFS_DELTA encoding, making its output a few
+   per-cent larger than necessarily.
+
+ * The option to tell "git clone" to recurse into the submodules was
+   misspelled with an underscore "--recurse_submodules".
+
+ * "git diff --cached HEAD" before the first commit does what an end user
+   would expect (namely, show what would be committed without further "git
+   add").
+
+ * "git fast-import" didn't accept the command to ask for "notes" feature
+   to be present in its input stream, even though it was capable of the
+   feature.
+
+ * "git fsck" gave up scanning loose object files in directories with
+   garbage files.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.4.2.txt b/Documentation/RelNotes/1.7.4.2.txt
new file mode 100644
index 0000000..ef4ce1f
--- /dev/null
+++ b/Documentation/RelNotes/1.7.4.2.txt
@@ -0,0 +1,58 @@
+Git v1.7.4.2 Release Notes
+==========================
+
+Fixes since v1.7.4.1
+--------------------
+
+ * Many documentation updates to match "git cmd -h" output and the
+   git-cmd manual page.
+
+ * We used to keep one file descriptor open for each and every packfile
+   that we have a mmap window on it (read: "in use"), even when for very
+   tiny packfiles.  We now close the file descriptor early when the entire
+   packfile fits inside one mmap window.
+
+ * "git bisect visualize" tried to run "gitk" in windowing
+   environments even when "gitk" is not installed, resulting in a
+   strange error message.
+
+ * "git clone /no/such/path" did not fail correctly.
+
+ * "git commit" did not correctly error out when the user asked to use a
+   non existent file as the commit message template.
+
+ * "git diff --stat -B" ran on binary files counted the changes in lines,
+   which was nonsensical.
+
+ * "git diff -M" opportunistically detected copies, which was not
+   necessarily a good thing, especially when it is internally run by
+   recursive merge.
+
+ * "git difftool" didn't tell (g)vimdiff that the files it is reading are
+   to be opened read-only.
+
+ * "git merge" didn't pay attention to prepare-commit-msg hook, even
+   though if a merge is conflicted and manually resolved, the subsequent
+   "git commit" would have triggered the hook, which was inconsistent.
+
+ * "git patch-id" (and commands like "format-patch --ignore-in-upstream"
+   that use it as their internal logic) handled changes to files that end
+   with incomplete lines incorrectly.
+
+ * The official value to tell "git push" to push the current branch back
+   to update the upstream branch it forked from is now called "upstream".
+   The old name "tracking" is and will be supported.
+
+ * "git submodule update" used to honor the --merge/--rebase option (or
+   corresponding configuration variables) even for a newly cloned
+   subproject, which made no sense (so/submodule-no-update-first-time).
+
+ * gitweb's "highlight" interface mishandled tabs.
+
+ * gitweb didn't understand timezones with GMT offset that is not
+   multiple of a whole hour.
+
+ * gitweb had a few forward-incompatible syntactic constructs and
+   also used incorrect variable when showing the file mode in a diff.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.4.3.txt b/Documentation/RelNotes/1.7.4.3.txt
new file mode 100644
index 0000000..02a3d5b
--- /dev/null
+++ b/Documentation/RelNotes/1.7.4.3.txt
@@ -0,0 +1,32 @@
+Git v1.7.4.3 Release Notes
+==========================
+
+Fixes since v1.7.4.2
+--------------------
+
+ * "git apply" used to confuse lines updated by previous hunks as lines
+   that existed before when applying a hunk, contributing misapplication
+   of patches with offsets.
+
+ * "git branch --track" (and "git checkout --track --branch") used to
+   allow setting up a random non-branch that does not make sense to follow
+   as the "upstream".  The command correctly diagnoses it as an error.
+
+ * "git checkout $other_branch" silently removed untracked symbolic links
+   in the working tree that are in the way in order to check out paths
+   under it from the named branch.
+
+ * "git cvsimport" did not bail out immediately when the cvs server cannot
+   be reached, spewing unnecessary error messages that complain about the
+   server response that it never got.
+
+ * "git diff --quiet" did not work very well with the "--diff-filter"
+   option.
+
+ * "git grep -n" lacked a long-hand synonym --line-number.
+
+ * "git stash apply" reported the result of its operation by running
+   "git status" from the top-level of the working tree; it should (and
+   now does) run it from the user's working directory.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.4.4.txt b/Documentation/RelNotes/1.7.4.4.txt
new file mode 100644
index 0000000..ff06e04
--- /dev/null
+++ b/Documentation/RelNotes/1.7.4.4.txt
@@ -0,0 +1,35 @@
+Git v1.7.4.4 Release Notes
+==========================
+
+Fixes since v1.7.4.3
+--------------------
+
+ * Compilation of sha1_file.c on BSD platforms were broken due to our
+   recent use of getrlimit() without including <sys/resource.h>.
+
+ * "git config" did not diagnose incorrect configuration variable names.
+
+ * "git format-patch" did not wrap a long subject line that resulted from
+   rfc2047 encoding.
+
+ * "git instaweb" should work better again with plackup.
+
+ * "git log --max-count=4 -Sfoobar" now shows 4 commits that changes the
+   number of occurrences of string "foobar"; it used to scan only for 4
+   commits and then emitted only matching ones.
+
+ * "git log --first-parent --boundary $c^..$c" segfaulted on a merge.
+
+ * "git pull" into an empty branch should have behaved as if
+   fast-forwarding from emptiness to the version being pulled, with
+   the usual protection against overwriting untracked files.
+
+ * "git submodule" that is run while a merge in the superproject is in
+   conflicted state tried to process each conflicted submodule up to
+   three times.
+
+ * "git status" spent all the effort to notice racily-clean index entries
+   but didn't update the index file to help later operations go faster in
+   some cases.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.4.5.txt b/Documentation/RelNotes/1.7.4.5.txt
new file mode 100644
index 0000000..b7a0eeb
--- /dev/null
+++ b/Documentation/RelNotes/1.7.4.5.txt
@@ -0,0 +1,4 @@
+Git v1.7.4.5 Release Notes
+==========================
+
+This contains only minor documentation fixes accumulated since 1.7.4.4.
diff --git a/Documentation/RelNotes/1.7.4.txt b/Documentation/RelNotes/1.7.4.txt
new file mode 100644
index 0000000..d5bca73
--- /dev/null
+++ b/Documentation/RelNotes/1.7.4.txt
@@ -0,0 +1,156 @@
+Git v1.7.4 Release Notes
+========================
+
+Updates since v1.7.3
+--------------------
+
+ * The documentation Makefile now assumes by default asciidoc 8 and
+   docbook-xsl >= 1.73. If you have older versions, you can set
+   ASCIIDOC7 and ASCIIDOC_ROFF, respectively.
+
+ * The option parsers of various commands that create new branches (or
+   rename existing ones to a new name) were too loose and users were
+   allowed to give a branch a name that begins with a dash by creative
+   abuse of their command line options, which only led to burning
+   themselves.  The name of a branch cannot begin with a dash now.
+
+ * System-wide fallback default attributes can be stored in
+   /etc/gitattributes; the core.attributesfile configuration variable can
+   be used to customize the path to this file.
+
+ * The thread structure generated by "git send-email" has changed
+   slightly.  Setting the cover letter of the latest series as a reply
+   to the cover letter of the previous series with --in-reply-to used
+   to make the new cover letter and all the patches replies to the
+   cover letter of the previous series; this has been changed to make
+   the patches in the new series replies to the new cover letter.
+
+ * The Bash completion script in contrib/ has been adjusted to be usable with
+   Bash 4 (options with '=value' didn't complete).  It has been also made
+   usable with zsh.
+
+ * Different pagers can be chosen depending on which subcommand is
+   being run under the pager, using the "pager.<subcommand>" variable.
+
+ * The hardcoded tab-width of 8 that is used in whitespace breakage checks is now
+   configurable via the attributes mechanism.
+
+ * Support of case insensitive filesystems (i.e. "core.ignorecase") has
+   been improved.  For example, the gitignore mechanism didn't pay attention
+   to case insensitivity.
+
+ * The <tree>:<path> syntax for naming a blob in a tree, and the :<path>
+   syntax for naming a blob in the index (e.g. "master:Makefile",
+   ":hello.c") have been extended.  You can start <path> with "./" to
+   implicitly have the (sub)directory you are in prefixed to the
+   lookup.  Similarly, ":../Makefile" from a subdirectory would mean
+   "the Makefile of the parent directory in the index".
+
+ * "git blame" learned the --show-email option to display the e-mail
+   addresses instead of the names of authors.
+
+ * "git commit" learned the --fixup and --squash options to help later invocation
+   of interactive rebase.
+
+ * Command line options to "git cvsimport" whose names are in capital
+   letters (-A, -M, -R and -S) can now be specified as the default in
+   the .git/config file by their longer names (cvsimport.authorsFile,
+   cvsimport.mergeRegex, cvsimport.trackRevisions, cvsimport.ignorePaths).
+
+ * "git daemon" can be built in the MinGW environment.
+
+ * "git daemon" can take more than one --listen option to listen to
+   multiple addresses.
+
+ * "git describe --exact-match" was optimized not to read commit
+   objects unnecessarily.
+
+ * "git diff" and "git grep" learned what functions and subroutines
+   in Fortran, Pascal and Perl look like.
+
+ * "git fetch" learned the "--recurse-submodules" option.
+
+ * "git mergetool" tells vim/gvim to show a three-way diff by default
+   (use vimdiff2/gvimdiff2 as the tool name for old behavior).
+
+ * "git log -G<pattern>" limits the output to commits whose change has
+   added or deleted lines that match the given pattern.
+
+ * "git read-tree" with no argument as a way to empty the index is
+   deprecated; we might want to remove it in the future.  Users can
+   use the new --empty option to be more explicit instead.
+
+ * "git repack -f" does not spend cycles to recompress objects in the
+   non-delta representation anymore (use -F if you really mean it
+   e.g. after you changed the core.compression variable setting).
+
+ * "git merge --log" used to limit the resulting merge log to 20
+   entries; this is now customizable by giving e.g. "--log=47".
+
+ * "git merge" may work better when all files were moved out of a
+   directory in one branch while a new file is created in place of that
+   directory in the other branch.
+
+ * "git merge" learned the "--abort" option, synonymous to
+   "git reset --merge" when a merge is in progress.
+
+ * "git notes" learned the "merge" subcommand to merge notes refs.
+   In addition to the default manual conflict resolution, there are
+   also several notes merge strategies for automatically resolving
+   notes merge conflicts.
+
+ * "git rebase --autosquash" can use SHA-1 object names to name the
+   commit which is to be fixed up (e.g. "fixup! e83c5163").
+
+ * The default "recursive" merge strategy learned the --rename-threshold
+   option to influence the rename detection, similar to the -M option
+   of "git diff".  From the "git merge" frontend, the "-X<strategy option>"
+   interface, e.g. "git merge -Xrename-threshold=50% ...", can be used
+   to trigger this.
+
+ * The "recursive" strategy also learned to ignore various whitespace
+   changes; the most notable is -Xignore-space-at-eol.
+
+ * "git send-email" learned "--to-cmd", similar to "--cc-cmd", to read
+   the recipient list from a command output.
+
+ * "git send-email" learned to read and use "To:" from its input files.
+
+ * you can extend "git shell", which is often used on boxes that allow
+   git-only login over ssh as login shell, with a custom set of
+   commands.
+
+ * The current branch name in "git status" output can be colored differently
+   from the generic header color by setting the "color.status.branch" variable.
+
+ * "git submodule sync" updates metainformation for all submodules,
+   not just the ones that have been checked out.
+
+ * gitweb can use a custom 'highlight' command with its configuration file.
+
+ * other gitweb updates.
+
+
+Also contains various documentation updates.
+
+
+Fixes since v1.7.3
+------------------
+
+All of the fixes in the v1.7.3.X maintenance series are included in this
+release, unless otherwise noted.
+
+ * "git log --author=me --author=her" did not find commits written by
+   me or by her; instead it looked for commits written by me and by
+   her, which is impossible.
+
+ * "git push --progress" shows progress indicators now.
+
+ * "git rebase -i" showed a confusing error message when given a
+   branch name that does not exist.
+
+ * "git repack" places its temporary packs under $GIT_OBJECT_DIRECTORY/pack
+   instead of $GIT_OBJECT_DIRECTORY/ to avoid cross directory renames.
+
+ * "git submodule update --recursive --other-flags" passes flags down
+   to its subinvocations.
diff --git a/Documentation/RelNotes/1.7.5.1.txt b/Documentation/RelNotes/1.7.5.1.txt
new file mode 100644
index 0000000..c6ebd76
--- /dev/null
+++ b/Documentation/RelNotes/1.7.5.1.txt
@@ -0,0 +1,47 @@
+Git v1.7.5.1 Release Notes
+==========================
+
+Fixes since v1.7.5
+------------------
+
+ * When an object "$tree:$path" does not exist, if $path does exist in the
+   subtree of $tree that corresponds to the subdirectory the user is in,
+   git now suggests using "$tree:./$path" in addition to the advice to use
+   the full path from the root of the working tree.
+
+ * The "--date=relative" output format used to say "X years, 12 months"
+   when it should have said "X+1 years".
+
+ * The smart-HTTP transfer was broken in 1.7.5 when the client needs
+   to issue a small POST (which uses content-length) and then a large
+   POST (which uses chunked) back to back.
+
+ * "git clean" used to fail on an empty directory that is not readable,
+   even though rmdir(2) could remove such a directory.  Now we attempt it
+   as the last resort.
+
+ * The "--dirstat" option of "diff" family of commands used to totally
+   ignore a change that only rearranged lines within a file.  Such a
+   change now counts as at least a minimum but non zero change.
+
+ * The "--dirstat" option of "diff" family of commands used to use the
+   pathname in the original, instead of the pathname in the result,
+   when renames are involved.
+
+ * "git pack-object" did not take core.bigfilethreashold into account
+   (unlike fast-import); now it does.
+
+ * "git reflog" ignored options like "--format=.." on the command line.
+
+ * "git stash apply" used to refuse to work if there was any change in
+   the working tree, even when the change did not overlap with the change
+   the stash recorded.
+
+ * "git stash apply @{99999}" was not diagnosed as an error, even when you
+   did not have that many stash entries.
+
+ * An error message from "git send-email" to diagnose a broken SMTP
+   connection configuration lacked a space between "hello=<smtp-domain>"
+   and "port=<smtp-server-port>".
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.5.2.txt b/Documentation/RelNotes/1.7.5.2.txt
new file mode 100644
index 0000000..951eb7c
--- /dev/null
+++ b/Documentation/RelNotes/1.7.5.2.txt
@@ -0,0 +1,57 @@
+Git v1.7.5.2 Release Notes
+==========================
+
+The release notes to 1.7.5.1 forgot to mention:
+
+ * "git stash -p --no-keep-index" and "git stash --no-keep-index -p" now
+   mean the same thing.
+
+ * "git upload-pack" (hence "git push" over git native protocol) had a
+   subtle race condition that could lead to a deadlock.
+
+Fixes since v1.7.5.1
+--------------------
+
+ * "git add -p" did not work correctly when a hunk is split and then
+   one of them was given to the editor.
+
+ * "git add -u" did not resolve a conflict where our history deleted and
+   their history modified the same file, and the working tree resolved to
+   keep a file.
+
+ * "git cvsimport" did not know that CVSNT stores its password file in a
+   location different from the traditional CVS.
+
+ * "git diff-files" did not show the mode information from the working
+   tree side of an unmerged path correctly.
+
+ * "git diff -M --cached" used to use unmerged path as a possible rename
+   source candidate, which made no sense.
+
+ * The option name parser in "git fast-import" used prefix matches for
+   some options where it shouldn't, and accepted non-existent options,
+   e.g. "--relative-marksmith" or "--forceps".
+
+ * "git format-patch" did not quote RFC822 special characters in the
+   email address (e.g From: Junio C. Hamano <jch@example.com>, not
+   From: "Junio C. Hamano" <jch@example.com>).
+
+ * "git format-patch" when run with "--quiet" option used to produce a
+   nonsense result that consists of alternating empty output.
+
+ * In "git merge", per-branch branch.<name>.mergeoptions configuration
+   variables did not override the fallback default merge.<option>
+   configuration variables such as merge.ff, merge.log, etc.
+
+ * "git merge-one-file" did not honor GIT_WORK_TREE settings when
+   handling a "both sides added, differently" conflict.
+
+ * "git mergetool" did not handle conflicted submoudules gracefully.
+
+ * "git-p4" (in contrib) used a wrong base image while merge a file that
+   was added on both branches differently.
+
+ * "git rebase -i -p" failed to preserve the history when there is a
+   redundant merge created with the --no-ff option.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.5.3.txt b/Documentation/RelNotes/1.7.5.3.txt
new file mode 100644
index 0000000..9c03353
--- /dev/null
+++ b/Documentation/RelNotes/1.7.5.3.txt
@@ -0,0 +1,32 @@
+Git v1.7.5.3 Release Notes
+==========================
+
+Fixes since v1.7.5.2
+--------------------
+
+ * The bash completion scripts should correctly work using zsh's bash
+   completion emulation layer now.
+
+ * Setting $(prefix) in config.mak did not affect where etc/gitconfig
+   file is read from, even though passing it from the command line of
+   $(MAKE) did.
+
+ * The logic to handle "&" (expand to UNIX username) in GECOS field
+   miscounted the length of the name it formatted.
+
+ * "git cherry-pick -s resolve" failed to cherry-pick a root commit.
+
+ * "git diff --word-diff" misbehaved when diff.suppress-blank-empty was
+   in effect.
+
+ * "git log --stdin path" with an input that has additional pathspec
+   used to corrupt memory.
+
+ * "git send-pack" (hence "git push") over smalt-HTTP protocol could
+   deadlock when the client side pack-object died early.
+
+ * Compressed tarball gitweb generates used to be made with the timestamp
+   of the tarball generation; this was bad because snapshot from the same
+   tree should result in a same tarball.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.5.4.txt b/Documentation/RelNotes/1.7.5.4.txt
new file mode 100644
index 0000000..cf3f455
--- /dev/null
+++ b/Documentation/RelNotes/1.7.5.4.txt
@@ -0,0 +1,21 @@
+Git v1.7.5.4 Release Notes
+==========================
+
+Fixes since v1.7.5.3
+--------------------
+
+ * The single-key mode of "git add -p" was easily fooled into thinking
+   that it was told to add everthing ('a') when up-arrow was pressed by
+   mistake.
+
+ * Setting a git command that uses custom configuration via "-c var=val"
+   as an alias caused a crash due to a realloc(3) failure.
+
+ * "git diff -C -C" used to disable the rename detection entirely when
+   there are too many copy candidate paths in the tree; now it falls
+   back to "-C" when doing so would keep the copy candidate paths
+   under the rename detection limit.
+
+ * "git rerere" did not diagnose a corrupt MERGE_RR file in some cases.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.5.txt b/Documentation/RelNotes/1.7.5.txt
new file mode 100644
index 0000000..987919c
--- /dev/null
+++ b/Documentation/RelNotes/1.7.5.txt
@@ -0,0 +1,132 @@
+Git v1.7.5 Release Notes
+========================
+
+Updates since v1.7.4
+--------------------
+
+ * Various MinGW portability fixes.
+
+ * Various git-p4 enhancements (in contrib).
+
+ * Various vcs-svn, git-svn and gitk enhancements and fixes.
+
+ * Various git-gui updates (0.14.0).
+
+ * Update to more modern HP-UX port.
+
+ * The codebase is getting prepared for i18n/l10n; no translated
+   strings nor translation mechanism in the code yet, but the strings
+   are being marked for l10n.
+
+ * The bash completion script can now complete symmetric difference
+   for "git diff" command, e.g. "git diff ...bra<TAB>".
+
+ * The default minimum length of abbreviated and unique object names
+   can now be configured by setting the core.abbrev configuration
+   variable.
+
+ * "git apply -v" reports offset lines when the patch does not apply at
+   the exact location recorded in the diff output.
+
+ * "git config" used to be also known as "git repo-config", but the old
+   name is now officially deprecated.
+
+ * "git checkout --detach <commit>" is a more user friendly synonym for
+   "git checkout <commit>^0".
+
+ * "git checkout" performed on detached HEAD gives a warning and
+   advice when the commit being left behind will become unreachable from
+   any branch or tag.
+
+ * "git cherry-pick" and "git revert" can be told to use a custom merge
+   strategy, similar to "git rebase".
+
+ * "git cherry-pick" remembers which commit failed to apply when it is
+   stopped by conflicts, making it unnecessary to use "commit -c $commit"
+   to conclude it.
+
+ * "git cvsimport" bails out immediately when the cvs server cannot be
+   reached, without spewing unnecessary error messages that complain about
+   the server response it never got.
+
+ * "git fetch" vs "git upload-pack" transfer learned 'no-done'
+   protocol extension to save one round-trip after the content
+   negotiation is done. This saves one HTTP RPC, reducing the overall
+   latency for a trivial fetch.
+
+ * "git fetch" can be told to recursively fetch submodules on-demand.
+
+ * "git grep -f <filename>" learned to treat "-" as "read from the
+   standard input stream".
+
+ * "git grep --no-index" did not honor pathspecs correctly, returning
+   paths outside the specified area.
+
+ * "git init" learned the --separate-git-dir option to allow the git
+   directory for a new repository created elsewhere and linked via the
+   gitdir mechanism. This is primarily to help submodule support later
+   to switch between a branch of superproject that has the submodule
+   and another that does not.
+
+ * "git log" type commands now understand globbing pathspecs.  You
+   can say "git log -- '*.txt'" for example.
+
+ * "git log" family of commands learned --cherry and --cherry-mark
+   options that can be used to view two diverged branches while omitting
+   or highlighting equivalent changes that appear on both sides of a
+   symmetric difference (e.g. "log --cherry A...B").
+
+ * A lazy "git merge" that didn't say what to merge used to be an error.
+   When run on a branch that has an upstream defined, however, the command
+   now merges from the configured upstream.
+
+ * "git mergetool" learned how to drive "beyond compare 3" as well.
+
+ * "git rerere forget" without pathspec used to forget all the saved
+   conflicts that relate to the current merge; it now requires you to
+   give it pathspecs.
+
+ * "git rev-list --objects $revs -- $pathspec" now limits the objects listed
+   in its output properly with the pathspec, in preparation for narrow
+   clones.
+
+ * "git push" with no parameters gives better advice messages when
+   "tracking" is used as the push.default semantics or there is no remote
+   configured yet.
+
+ * A possible value to the "push.default" configuration variable,
+   'tracking', gained a synonym that more naturally describes what it
+   does, 'upstream'.
+
+ * "git rerere" learned a new subcommand "remaining" that is similar to
+   "status" and lists the paths that had conflicts which are known to
+   rerere, but excludes the paths that have already been marked as
+   resolved in the index from its output.  "git mergetool" has been
+   updated to use this facility.
+
+Also contains various documentation updates.
+
+
+Fixes since v1.7.4
+------------------
+
+All of the fixes in the v1.7.4.X maintenance series are included in this
+release, unless otherwise noted.
+
+ * "git fetch" from a client that is mostly following the remote
+   needlessly told all of its refs to the server for both sides to
+   compute the set of objects that need to be transferred efficiently,
+   instead of stopping when the server heard enough. In a project with
+   many tags, this turns out to be extremely wasteful, especially over
+   the smart HTTP transport (sp/maint-{upload,fetch}-pack-stop-early~1).
+
+ * "git fetch" run from a repository that uses the same repository as
+   its alternate object store as the repository it is fetching from
+   did not tell the server that it already has access to objects
+   reachable from the refs in their common alternate object store,
+   causing it to fetch unnecessary objects (jc/maint-fetch-alt).
+
+ * "git remote add --mirror" created a configuration that is suitable for
+   doing both a mirror fetch and a mirror push at the same time, which
+   made little sense.  We now warn and require the command line to specify
+   either --mirror=fetch or --mirror=push.
diff --git a/Documentation/RelNotes/1.7.6.1.txt b/Documentation/RelNotes/1.7.6.1.txt
new file mode 100644
index 0000000..42e46ab
--- /dev/null
+++ b/Documentation/RelNotes/1.7.6.1.txt
@@ -0,0 +1,63 @@
+Git v1.7.6.1 Release Notes
+==========================
+
+Fixes since v1.7.6
+------------------
+
+ * Various codepaths that invoked zlib deflate/inflate assumed that these
+   functions can compress or uncompress more than 4GB data in one call on
+   platforms with 64-bit long, which has been corrected.
+
+ * "git unexecutable" reported that "unexecutable" was not found, even
+   though the actual error was that "unexecutable" was found but did
+   not have a proper she-bang line to be executed.
+
+ * Error exits from $PAGER were silently ignored.
+
+ * "git checkout -b <branch>" was confused when attempting to create a
+   branch whose name ends with "-g" followed by hexadecimal digits,
+   and refused to work.
+
+ * "git checkout -b <branch>" sometimes wrote a bogus reflog entry,
+   causing later "git checkout -" to fail.
+
+ * "git diff --cc" learned to correctly ignore binary files.
+
+ * "git diff -c/--cc" mishandled a deletion that resolves a conflict, and
+   looked in the working tree instead.
+
+ * "git fast-export" forgot to quote pathnames with unsafe characters
+   in its output.
+
+ * "git fetch" over smart-http transport used to abort when the
+   repository was updated between the initial connection and the
+   subsequent object transfer.
+
+ * "git fetch" did not recurse into submodules in subdirectories.
+
+ * "git ls-tree" did not error out when asked to show a corrupt tree.
+
+ * "git pull" without any argument left an extra whitespace after the
+   command name in its reflog.
+
+ * "git push --quiet" was not really quiet.
+
+ * "git rebase -i -p" incorrectly dropped commits from side branches.
+
+ * "git reset [<commit>] paths..." did not reset the index entry correctly
+   for unmerged paths.
+
+ * "git submodule add" did not allow a relative repository path when
+   the superproject did not have any default remote url.
+
+ * "git submodule foreach" failed to correctly give the standard input to
+   the user-supplied command it invoked.
+
+ * submodules that the user has never showed interest in by running
+   "git submodule init" was incorrectly marked as interesting by "git
+   submodule sync".
+
+ * "git submodule update --quiet" was not really quiet.
+
+  * "git tag -l <glob>..." did not take multiple glob patterns from the
+   command line.
diff --git a/Documentation/RelNotes/1.7.6.2.txt b/Documentation/RelNotes/1.7.6.2.txt
new file mode 100644
index 0000000..67ae414
--- /dev/null
+++ b/Documentation/RelNotes/1.7.6.2.txt
@@ -0,0 +1,8 @@
+Git v1.7.6.2 Release Notes
+==========================
+
+Fixes since v1.7.6.1
+--------------------
+
+ * v1.7.6.1 broke "git push --quiet"; it used to be a no-op against an old
+   version of Git running on the other end, but v1.7.6.1 made it abort.
diff --git a/Documentation/RelNotes/1.7.6.3.txt b/Documentation/RelNotes/1.7.6.3.txt
new file mode 100644
index 0000000..9597183
--- /dev/null
+++ b/Documentation/RelNotes/1.7.6.3.txt
@@ -0,0 +1,24 @@
+Git v1.7.6.3 Release Notes
+==========================
+
+Fixes since v1.7.6.2
+--------------------
+
+ * "git -c var=value subcmd" misparsed the custom configuration when
+   value contained an equal sign.
+
+ * "git fetch" had a major performance regression, wasting many
+   needless cycles in a repository where there is no submodules
+   present. This was especially bad, when there were many refs.
+
+ * "git reflog $refname" did not default to the "show" subcommand as
+   the documentation advertised the command to do.
+
+ * "git reset" did not leave meaningful log message in the reflog.
+
+ * "git status --ignored" did not show ignored items when there is no
+   untracked items.
+
+ * "git tag --contains $commit" was unnecessarily inefficient.
+
+Also contains minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.6.4.txt b/Documentation/RelNotes/1.7.6.4.txt
new file mode 100644
index 0000000..e19acac
--- /dev/null
+++ b/Documentation/RelNotes/1.7.6.4.txt
@@ -0,0 +1,32 @@
+Git v1.7.6.4 Release Notes
+==========================
+
+Fixes since v1.7.6.3
+--------------------
+
+ * The error reporting logic of "git am" when the command is fed a file
+   whose mail-storage format is unknown was fixed.
+
+ * "git branch --set-upstream @{-1} foo" did not expand @{-1} correctly.
+
+ * "git check-ref-format --print" used to parrot a candidate string that
+   began with a slash (e.g. /refs/heads/master) without stripping it, to make
+   the result a suitably normalized string the caller can append to "$GIT_DIR/".
+
+ * "git clone" failed to clone locally from a ".git" file that itself
+   is not a directory but is a pointer to one.
+
+ * "git clone" from a local repository that borrows from another
+   object store using a relative path in its objects/info/alternates
+   file did not adjust the alternates in the resulting repository.
+
+ * "git describe --dirty" did not refresh the index before checking the
+   state of the working tree files.
+
+ * "git ls-files ../$path" that is run from a subdirectory reported errors
+   incorrectly when there is no such path that matches the given pathspec.
+
+ * "git mergetool" could loop forever prompting when nothing can be read
+   from the standard input.
+
+Also contains minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.6.5.txt b/Documentation/RelNotes/1.7.6.5.txt
new file mode 100644
index 0000000..6713132
--- /dev/null
+++ b/Documentation/RelNotes/1.7.6.5.txt
@@ -0,0 +1,26 @@
+Git v1.7.6.5 Release Notes
+==========================
+
+Fixes since v1.7.6.4
+--------------------
+
+ * The date parser did not accept timezone designators that lack minutes
+   part and also has a colon between "hh:mm".
+
+ * After fetching from a remote that has very long refname, the reporting
+   output could have corrupted by overrunning a static buffer.
+
+ * "git mergetool" did not use its arguments as pathspec, but as a path to
+   the file that may not even have any conflict.
+
+ * "git name-rev --all" tried to name all _objects_, naturally failing to
+   describe many blobs and trees, instead of showing only commits as
+   advertised in its documentation.
+
+ * "git remote rename $a $b" were not careful to match the remote name
+   against $a (i.e. source side of the remote nickname).
+
+ * "gitweb" used to produce a non-working link while showing the contents
+   of a blob, when JavaScript actions are enabled.
+
+Also contains minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.6.6.txt b/Documentation/RelNotes/1.7.6.6.txt
new file mode 100644
index 0000000..5343e00
--- /dev/null
+++ b/Documentation/RelNotes/1.7.6.6.txt
@@ -0,0 +1,16 @@
+Git v1.7.6.6 Release Notes
+==========================
+
+Fixes since v1.7.6.5
+--------------------
+
+ * The code to look up attributes for paths reused entries from a wrong
+   directory when two paths in question are in adjacent directories and
+   the name of the one directory is a prefix of the other.
+
+ * When producing a "thin pack" (primarily used in bundles and smart
+   HTTP transfers) out of a fully packed repository, we unnecessarily
+   avoided sending recent objects as a delta against objects we know
+   the other side has.
+
+Also contains minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.6.txt b/Documentation/RelNotes/1.7.6.txt
new file mode 100644
index 0000000..9ec498e
--- /dev/null
+++ b/Documentation/RelNotes/1.7.6.txt
@@ -0,0 +1,136 @@
+Git v1.7.6 Release Notes
+========================
+
+Updates since v1.7.5
+--------------------
+
+ * Various git-svn updates.
+
+ * Updates the way content tags are handled in gitweb.  Also adds
+   a UI to choose common timezone for displaying the dates.
+
+ * Similar to branch names, tagnames that begin with "-" are now
+   disallowed.
+
+ * Clean-up of the C part of i18n (but not l10n---please wait)
+   continues.
+
+ * The scripting part of the codebase is getting prepared for i18n/l10n.
+
+ * Pushing and pulling from a repository with large number of refs that
+   point to identical commits are optimized by not listing the same commit
+   during the common ancestor negotiation exchange with the other side.
+
+ * Adding a file larger than core.bigfilethreshold (defaults to 1/2 Gig)
+   using "git add" will send the contents straight to a packfile without
+   having to hold it and its compressed representation both at the same
+   time in memory.
+
+ * Processes spawned by "[alias] <name> = !process" in the configuration
+   can inspect GIT_PREFIX environment variable to learn where in the
+   working tree the original command was invoked.
+
+ * A magic pathspec ":/" tells a command that limits its operation to
+   the current directory when ran from a subdirectory to work on the
+   entire working tree. In general, ":/path/to/file" would be relative
+   to the root of the working tree hierarchy.
+
+   After "git reset --hard; edit Makefile; cd t/", "git add -u" would
+   be a no-op, but "git add -u :/" would add the updated contents of
+   the Makefile at the top level. If you want to name a path in the
+   current subdirectory whose unusual name begins with ":/", you can
+   name it by "./:/that/path" or by "\:/that/path".
+
+ * "git blame" learned "--abbrev[=<n>]" option to control the minimum
+   number of hexdigits shown for commit object names.
+
+ * "git blame" learned "--line-porcelain" that is less efficient but is
+   easier to parse.
+
+ * Aborting "git commit --interactive" discards updates to the index
+   made during the interactive session.
+
+ * "git commit" learned a "--patch" option to directly jump to the
+   per-hunk selection UI of the interactive mode.
+
+ * "git diff" and its family of commands learned --dirstat=0 to show
+   directories that contribute less than 0.1% of changes.
+
+ * "git diff" and its family of commands learned --dirstat=lines mode to
+   assess damage to the directory based on number of lines in the patch
+   output, not based on the similarity numbers.
+
+ * "git format-patch" learned "--quiet" option to suppress the output of
+   the names of generated files.
+
+ * "git format-patch" quotes people's names when it has RFC822 special
+   characters in it, e.g. "Junio C. Hamano" <jch@example.com>.  Earlier
+   it was up to the user to do this when using its output.
+
+ * "git format-patch" can take an empty --subject-prefix now.
+
+ * "git grep" learned the "-P" option to take pcre regular expressions.
+
+ * "git log" and friends learned a new "--notes" option to replace the
+   "--show-notes" option.  Unlike "--show-notes", "--notes=<ref>" does
+   not imply showing the default notes.
+
+ * They also learned a log.abbrevCommit configuration variable to augment
+   the --abbrev-commit command line option.
+
+ * "git ls-remote" learned "--exit-code" option to consider it a
+   different kind of error when no remote ref to be shown.
+
+ * "git merge" learned "-" as a short-hand for "the previous branch", just
+   like the way "git checkout -" works.
+
+ * "git merge" uses "merge.ff" configuration variable to decide to always
+   create a merge commit (i.e. --no-ff, aka merge.ff=no), refuse to create
+   a merge commit (i.e. --ff-only, aka merge.ff=only). Setting merge.ff=yes
+   (or not setting it at all) restores the default behaviour of allowing
+   fast-forward to happen when possible.
+
+ * p4-import (from contrib) learned a new option --preserve-user.
+
+ * "git read-tree -m" learned "--dry-run" option that reports if a merge
+   would fail without touching the index nor the working tree.
+
+ * "git rebase" that does not specify on top of which branch to rebase
+   the current branch now uses @{upstream} of the current branch.
+
+ * "git rebase" finished either normally or with --abort did not
+   update the reflog for HEAD to record the event to come back to
+   where it started from.
+
+ * "git remote add -t only-this-branch --mirror=fetch" is now allowed. Earlier
+   a fetch-mode mirror meant mirror everything, but now it only means refs are
+   not renamed.
+
+ * "git rev-list --count" used with "--cherry-mark" counts the cherry-picked
+   commits separately, producing more a useful output.
+
+ * "git submodule update" learned "--force" option to get rid of local
+   changes in submodules and replace them with the up-to-date version.
+
+ * "git status" and friends ignore .gitmodules file while the file is
+   still in a conflicted state during a merge, to avoid using information
+   that is not final and possibly corrupt with conflict markers.
+
+Also contains various documentation updates and minor miscellaneous
+changes.
+
+
+Fixes since v1.7.5
+------------------
+
+Unless otherwise noted, all the fixes in 1.7.5.X maintenance track are
+included in this release.
+
+ * "git config" used to choke with an insanely long line.
+   (merge ef/maint-strbuf-init later)
+
+ * "git diff --quiet" did not work well with --diff-filter.
+   (merge jk/diff-not-so-quick later)
+
+ * "git status -z" did not default to --porcelain output format.
+   (merge bc/maint-status-z-to-use-porcelain later)
diff --git a/Documentation/RelNotes/1.7.7.1.txt b/Documentation/RelNotes/1.7.7.1.txt
new file mode 100644
index 0000000..ac9b838
--- /dev/null
+++ b/Documentation/RelNotes/1.7.7.1.txt
@@ -0,0 +1,60 @@
+Git v1.7.7.1 Release Notes
+==========================
+
+Fixes since v1.7.7
+------------------
+
+ * On some BSD systems, adding +s bit on directories is detrimental
+   (it is not necessary on BSD to begin with). "git init --shared"
+   has been updated to take this into account without extra makefile
+   settings on platforms the Makefile knows about.
+
+ * After incorrectly written third-party tools store a tag object in
+   HEAD, git diagnosed it as a repository corruption and refused to
+   proceed in order to avoid spreading the damage. We now gracefully
+   recover from such a situation by pretending as if the commit that
+   is pointed at by the tag were in HEAD.
+
+ * "git apply --whitespace=error" did not bother to report the exact
+   line number in the patch that introduced new blank lines at the end
+   of the file.
+
+ * "git apply --index" did not check corrupted patch.
+
+ * "git checkout $tree $directory/" resurrected paths locally removed or
+   modified only in the working tree in $directory/ that did not appear
+   in $directory of the given $tree. They should have been kept intact.
+
+ * "git diff $tree $path" used to apply the pathspec at the output stage,
+   reading the whole tree, wasting resources.
+
+ * The code to check for updated submodules during a "git fetch" of the
+   superproject had an unnecessary quadratic loop.
+
+ * "git fetch" from a large bundle did not enable the progress output.
+
+ * When "git fsck --lost-and-found" found that an empty blob object in the
+   object store is unreachable, it incorrectly reported an error after
+   writing the lost blob out successfully.
+
+ * "git filter-branch" did not refresh the index before checking that the
+   working tree was clean.
+
+ * "git grep $tree" when run with multiple threads had an unsafe access to
+   the object database that should have been protected with mutex.
+
+ * The "--ancestry-path" option to "git log" and friends misbehaved in a
+   history with complex criss-cross merges and showed an uninteresting
+   side history as well.
+
+ * Test t1304 assumed LOGNAME is always set, which may not be true on
+   some systems.
+
+ * Tests with --valgrind failed to find "mergetool" scriptlets.
+
+ * "git patch-id" miscomputed the patch-id in a patch that has a line longer
+   than 1kB.
+
+ * When an "exec" insn failed after modifying the index and/or the working
+   tree during "rebase -i", we now check and warn that the changes need to
+   be cleaned up.
diff --git a/Documentation/RelNotes/1.7.7.2.txt b/Documentation/RelNotes/1.7.7.2.txt
new file mode 100644
index 0000000..e6bbef2
--- /dev/null
+++ b/Documentation/RelNotes/1.7.7.2.txt
@@ -0,0 +1,44 @@
+Git v1.7.7.2 Release Notes
+==========================
+
+Fixes since v1.7.7.1
+--------------------
+
+ * We used to drop error messages from libcurl on certain kinds of
+   errors.
+
+ * Error report from smart HTTP transport, when the connection was
+   broken in the middle of a transfer, showed a useless message on
+   a corrupt packet.
+
+ * "git fetch --prune" was unsafe when used with refspecs from the
+   command line.
+
+ * The attribute mechanism did not use case insensitive match when
+   core.ignorecase was set.
+
+ * "git bisect" did not notice when it failed to update the working tree
+   to the next commit to be tested.
+
+ * "git config --bool --get-regexp" failed to separate the variable name
+   and its value "true" when the variable is defined without "= true".
+
+ * "git remote rename $a $b" were not careful to match the remote name
+   against $a (i.e. source side of the remote nickname).
+
+ * "git mergetool" did not use its arguments as pathspec, but as a path to
+   the file that may not even have any conflict.
+
+ * "git diff --[num]stat" used to use the number of lines of context
+   different from the default, potentially giving different results from
+   "git diff | diffstat" and confusing the users.
+
+ * "git pull" and "git rebase" did not work well even when GIT_WORK_TREE is
+   set correctly with GIT_DIR if the current directory is outside the working
+   tree.
+
+ * "git send-email" did not honor the configured hostname when restarting
+   the HELO/EHLO exchange after switching TLS on.
+
+ * "gitweb" used to produce a non-working link while showing the contents
+   of a blob, when JavaScript actions are enabled.
diff --git a/Documentation/RelNotes/1.7.7.3.txt b/Documentation/RelNotes/1.7.7.3.txt
new file mode 100644
index 0000000..09301f0
--- /dev/null
+++ b/Documentation/RelNotes/1.7.7.3.txt
@@ -0,0 +1,19 @@
+Git v1.7.7.3 Release Notes
+==========================
+
+Fixes since v1.7.7.2
+--------------------
+
+ * Adjust the "quick-install-doc" procedures as preformatted
+   html/manpage are no longer in the source repository.
+
+ * The logic to optimize the locality of the data in a pack introduced in
+   1.7.7 was grossly inefficient.
+
+ * The logic to filter out forked projects in the project list in
+   "gitweb" was broken for some time.
+
+ * "git branch -m/-M" advertised to update RENAME_REF ref in the
+   commit log message that introduced the feature but not anywhere in
+   the documentation, and never did update such a ref anyway. This
+   undocumented misfeature that did not exist has been excised.
diff --git a/Documentation/RelNotes/1.7.7.4.txt b/Documentation/RelNotes/1.7.7.4.txt
new file mode 100644
index 0000000..e523448
--- /dev/null
+++ b/Documentation/RelNotes/1.7.7.4.txt
@@ -0,0 +1,14 @@
+Git v1.7.7.4 Release Notes
+==========================
+
+Fixes since v1.7.7.3
+--------------------
+
+ * A few header dependencies were missing from the Makefile.
+
+ * Some newer parts of the code used C99 __VA_ARGS__ while we still
+   try to cater to older compilers.
+
+ * "git name-rev --all" tried to name all _objects_, naturally failing to
+   describe many blobs and trees, instead of showing only commits as
+   advertised in its documentation.
diff --git a/Documentation/RelNotes/1.7.7.5.txt b/Documentation/RelNotes/1.7.7.5.txt
new file mode 100644
index 0000000..7b09319
--- /dev/null
+++ b/Documentation/RelNotes/1.7.7.5.txt
@@ -0,0 +1,14 @@
+Git v1.7.7.5 Release Notes
+==========================
+
+Fixes since v1.7.7.4
+--------------------
+
+ * After fetching from a remote that has very long refname, the reporting
+   output could have corrupted by overrunning a static buffer.
+
+ * "git checkout" and "git merge" treated in-tree .gitignore and exclude
+   file in $GIT_DIR/info/ directory inconsistently when deciding which
+   untracked files are ignored and expendable.
+
+Also contains minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.7.6.txt b/Documentation/RelNotes/1.7.7.6.txt
new file mode 100644
index 0000000..8df606d
--- /dev/null
+++ b/Documentation/RelNotes/1.7.7.6.txt
@@ -0,0 +1,20 @@
+Git v1.7.7.6 Release Notes
+==========================
+
+Fixes since v1.7.7.5
+--------------------
+
+ * The code to look up attributes for paths reused entries from a wrong
+   directory when two paths in question are in adjacent directories and
+   the name of the one directory is a prefix of the other.
+
+ * A wildcard that matches deeper hierarchy given to the "diff-index" command,
+   e.g. "git diff-index HEAD -- '*.txt'", incorrectly reported additions of
+   matching files even when there is no change.
+
+ * When producing a "thin pack" (primarily used in bundles and smart
+   HTTP transfers) out of a fully packed repository, we unnecessarily
+   avoided sending recent objects as a delta against objects we know
+   the other side has.
+
+Also contains minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.7.7.txt b/Documentation/RelNotes/1.7.7.7.txt
new file mode 100644
index 0000000..e79118d
--- /dev/null
+++ b/Documentation/RelNotes/1.7.7.7.txt
@@ -0,0 +1,13 @@
+Git v1.7.7.7 Release Notes
+==========================
+
+Fixes since v1.7.7.6
+--------------------
+
+ * An error message from 'git bundle' had an unmatched single quote pair in it.
+
+ * 'git diff --histogram' option was not described.
+
+ * 'git imap-send' carried an unused dead code.
+
+Also contains minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.7.txt b/Documentation/RelNotes/1.7.7.txt
new file mode 100644
index 0000000..7655ccc
--- /dev/null
+++ b/Documentation/RelNotes/1.7.7.txt
@@ -0,0 +1,134 @@
+Git v1.7.7 Release Notes
+========================
+
+Updates since v1.7.6
+--------------------
+
+ * The scripting part of the codebase is getting prepared for i18n/l10n.
+
+ * Interix, Cygwin and Minix ports got updated.
+
+ * Various updates to git-p4 (in contrib/), fast-import, and git-svn.
+
+ * Gitweb learned to read from /etc/gitweb-common.conf when it exists,
+   before reading from gitweb_config.perl or from /etc/gitweb.conf
+   (this last one is read only when per-repository gitweb_config.perl
+   does not exist).
+
+ * Various codepaths that invoked zlib deflate/inflate assumed that these
+   functions can compress or uncompress more than 4GB data in one call on
+   platforms with 64-bit long, which has been corrected.
+
+ * Git now recognizes loose objects written by other implementations that
+   use a non-standard window size for zlib deflation (e.g. Agit running on
+   Android with 4kb window). We used to reject anything that was not
+   deflated with 32kb window.
+
+ * Interaction between the use of pager and coloring of the output has
+   been improved, especially when a command that is not built-in was
+   involved.
+
+ * "git am" learned to pass the "--exclude=<path>" option through to underlying
+   "git apply".
+
+ * You can now feed many empty lines before feeding an mbox file to
+   "git am".
+
+ * "git archive" can be told to pass the output to gzip compression and
+   produce "archive.tar.gz".
+
+ * "git bisect" can be used in a bare repository (provided that the test
+   you perform per each iteration does not need a working tree, of
+   course).
+
+ * The length of abbreviated object names in "git branch -v" output
+   now honors the core.abbrev configuration variable.
+
+ * "git check-attr" can take relative paths from the command line.
+
+ * "git check-attr" learned an "--all" option to list the attributes for a
+   given path.
+
+ * "git checkout" (both the code to update the files upon checking out a
+   different branch and the code to checkout a specific set of files) learned
+   to stream the data from object store when possible, without having to
+   read the entire contents of a file into memory first. An earlier round
+   of this code that is not in any released version had a large leak but
+   now it has been plugged.
+
+ * "git clone" can now take a "--config key=value" option to set the
+   repository configuration options that affect the initial checkout.
+
+ * "git commit <paths>..." now lets you feed relative pathspecs that
+   refer to outside your current subdirectory.
+
+ * "git diff --stat" learned a --stat-count option to limit the output of
+   a diffstat report.
+
+ * "git diff" learned a "--histogram" option to use a different diff
+   generation machinery stolen from jgit, which might give better
+   performance.
+
+ * "git diff" had a weird worst case behaviour that can be triggered
+   when comparing files with potentially many places that could match.
+
+ * "git fetch", "git push" and friends no longer show connection
+   errors for addresses that couldn't be connected to when at least one
+   address succeeds (this is arguably a regression but a deliberate
+   one).
+
+ * "git grep" learned "--break" and "--heading" options, to let users mimic
+   the output format of "ack".
+
+ * "git grep" learned a "-W" option that shows wider context using the same
+   logic used by "git diff" to determine the hunk header.
+
+ * Invoking the low-level "git http-fetch" without "-a" option (which
+   git itself never did---normal users should not have to worry about
+   this) is now deprecated.
+
+ * The "--decorate" option to "git log" and its family learned to
+   highlight grafted and replaced commits.
+
+ * "git rebase master topci" no longer spews usage hints after giving
+   the "fatal: no such branch: topci" error message.
+
+ * The recursive merge strategy implementation got a fairly large
+   fix for many corner cases that may rarely happen in real world
+   projects (it has been verified that none of the 16000+ merges in
+   the Linux kernel history back to v2.6.12 is affected with the
+   corner case bugs this update fixes).
+
+ * "git stash" learned an "--include-untracked option".
+
+ * "git submodule update" used to stop at the first error updating a
+   submodule; it now goes on to update other submodules that can be
+   updated, and reports the ones with errors at the end.
+
+ * "git push" can be told with the "--recurse-submodules=check" option to
+   refuse pushing of the supermodule, if any of its submodules'
+   commits hasn't been pushed out to their remotes.
+
+ * "git upload-pack" and "git receive-pack" learned to pretend that only a
+   subset of the refs exist in a repository. This may help a site to
+   put many tiny repositories into one repository (this would not be
+   useful for larger repositories as repacking would be problematic).
+
+ * "git verify-pack" has been rewritten to use the "index-pack" machinery
+   that is more efficient in reading objects in packfiles.
+
+ * test scripts for gitweb tried to run even when CGI-related perl modules
+   are not installed; they now exit early when the latter are unavailable.
+
+Also contains various documentation updates and minor miscellaneous
+changes.
+
+
+Fixes since v1.7.6
+------------------
+
+Unless otherwise noted, all fixes in the 1.7.6.X maintenance track are
+included in this release.
+
+ * "git branch -m" and "git checkout -b" incorrectly allowed the tip
+   of the branch that is currently checked out updated.
diff --git a/Documentation/RelNotes/1.7.8.1.txt b/Documentation/RelNotes/1.7.8.1.txt
new file mode 100644
index 0000000..33dc948
--- /dev/null
+++ b/Documentation/RelNotes/1.7.8.1.txt
@@ -0,0 +1,38 @@
+Git v1.7.8.1 Release Notes
+==========================
+
+Fixes since v1.7.8
+------------------
+
+ * In some codepaths (notably, checkout and merge), the ignore patterns
+   recorded in $GIT_DIR/info/exclude were not honored. They now are.
+
+ * "git apply --check" did not error out when given an empty input
+   without any patch.
+
+ * "git archive" mistakenly allowed remote clients to ask for commits
+   that are not at the tip of any ref.
+
+ * "git checkout" and "git merge" treated in-tree .gitignore and exclude
+   file in $GIT_DIR/info/ directory inconsistently when deciding which
+   untracked files are ignored and expendable.
+
+ * LF-to-CRLF streaming filter used when checking out a large-ish blob
+   fell into an infinite loop with a rare input.
+
+ * The function header pattern for files with "diff=cpp" attribute did
+   not consider "type *funcname(type param1,..." as the beginning of a
+   function.
+
+ * The error message from "git diff" and "git status" when they fail
+   to inspect changes in submodules did not report which submodule they
+   had trouble with.
+
+ * After fetching from a remote that has very long refname, the reporting
+   output could have corrupted by overrunning a static buffer.
+
+ * "git pack-objects" avoids creating cyclic dependencies among deltas
+   when seeing a broken packfile that records the same object in both
+   the deflated form and as a delta.
+
+Also contains minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.8.2.txt b/Documentation/RelNotes/1.7.8.2.txt
new file mode 100644
index 0000000..e74f4ef
--- /dev/null
+++ b/Documentation/RelNotes/1.7.8.2.txt
@@ -0,0 +1,71 @@
+Git v1.7.8.2 Release Notes
+==========================
+
+Fixes since v1.7.8.1
+--------------------
+
+ * Porcelain commands like "git reset" did not distinguish deletions
+   and type-changes from ordinary modification, and reported them with
+   the same 'M' moniker. They now use 'D' (for deletion) and 'T' (for
+   type-change) to match "git status -s" and "git diff --name-status".
+
+ * The configuration file parser used for sizes (e.g. bigFileThreshold)
+   did not correctly interpret 'g' suffix.
+
+ * The replacement implemention for snprintf used on platforms with
+   native snprintf that is broken did not use va_copy correctly.
+
+ * LF-to-CRLF streaming filter replaced all LF with CRLF, which might
+   be techinically correct but not friendly to people who are trying
+   to recover from earlier mistakes of using CRLF in the repository
+   data in the first place. It now refrains from doing so for LF that
+   follows a CR.
+
+ * git native connection going over TCP (not over SSH) did not set
+   SO_KEEPALIVE option which failed to receive link layer errors.
+
+ * "git branch -m <current branch> HEAD" is an obvious no-op but was not
+   allowed.
+
+ * "git checkout -m" did not recreate the conflicted state in a "both
+   sides added, without any common ancestor version" conflict
+   situation.
+
+ * "git cherry-pick $commit" (not a range) created an unnecessary
+   sequencer state and interfered with valid workflow to use the
+   command during a session to cherry-pick multiple commits.
+
+ * You could make "git commit" segfault by giving the "--no-message"
+   option.
+
+ * "fast-import" did not correctly update an existing notes tree,
+   possibly corrupting the fan-out.
+
+ * "git fetch-pack" accepted unqualified refs that do not begin with
+   refs/ by mistake and compensated it by matching the refspec with
+   tail-match, which was doubly wrong. This broke fetching from a
+   repository with a funny named ref "refs/foo/refs/heads/master" and a
+   'master' branch with "git fetch-pack refs/heads/master", as the
+   command incorrectly considered the former a "match".
+
+ * "git log --follow" did not honor the rename threshold score given
+   with the -M option (e.g. "-M50%").
+
+ * "git mv" gave suboptimal error/warning messages when it overwrites
+   target files. It also did not pay attention to "-v" option.
+
+ * Authenticated "git push" over dumb HTTP were broken with a recent
+   change and failed without asking for password when username is
+   given.
+
+ * "git push" to an empty repository over HTTP were broken with a
+   recent change to the ref handling.
+
+ * "git push -v" forgot how to be verbose by mistake. It now properly
+   becomes verbose when asked to.
+
+ * When a "reword" action in "git rebase -i" failed to run "commit --amend",
+   we did not give the control back to the user to resolve the situation, and
+   instead kept the original commit log message.
+
+Also contains minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.8.3.txt b/Documentation/RelNotes/1.7.8.3.txt
new file mode 100644
index 0000000..a92714c
--- /dev/null
+++ b/Documentation/RelNotes/1.7.8.3.txt
@@ -0,0 +1,16 @@
+Git v1.7.8.3 Release Notes
+==========================
+
+Fixes since v1.7.8.2
+--------------------
+
+ * Attempt to fetch from an empty file pretending it to be a bundle did
+   not error out correctly.
+
+ * gitweb did not correctly fall back to configured $fallback_encoding
+   that is not 'latin1'.
+
+ * "git clone --depth $n" did not catch a non-number given as $n as an
+   error.
+
+Also contains minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.8.4.txt b/Documentation/RelNotes/1.7.8.4.txt
new file mode 100644
index 0000000..9bebdbf
--- /dev/null
+++ b/Documentation/RelNotes/1.7.8.4.txt
@@ -0,0 +1,23 @@
+Git v1.7.8.4 Release Notes
+==========================
+
+Fixes since v1.7.8.3
+--------------------
+
+ * The code to look up attributes for paths reused entries from a wrong
+   directory when two paths in question are in adjacent directories and
+   the name of the one directory is a prefix of the other.
+
+ * A wildcard that matches deeper hierarchy given to the "diff-index" command,
+   e.g. "git diff-index HEAD -- '*.txt'", incorrectly reported additions of
+   matching files even when there is no change.
+
+ * When producing a "thin pack" (primarily used in bundles and smart
+   HTTP transfers) out of a fully packed repository, we unnecessarily
+   avoided sending recent objects as a delta against objects we know
+   the other side has.
+
+ * "git send-email" did not properly treat sendemail.multiedit as a
+   boolean (e.g. setting it to "false" did not turn it off).
+
+Also contains minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.8.5.txt b/Documentation/RelNotes/1.7.8.5.txt
new file mode 100644
index 0000000..011fd2a
--- /dev/null
+++ b/Documentation/RelNotes/1.7.8.5.txt
@@ -0,0 +1,19 @@
+Git v1.7.8.5 Release Notes
+==========================
+
+Fixes since v1.7.8.4
+--------------------
+
+ * Dependency on our thread-utils.h header file was missing for
+   objects that depend on it in the Makefile.
+
+ * "git am" when fed an empty file did not correctly finish reading it
+   when it attempts to guess the input format.
+
+ * "git grep -P" (when PCRE is enabled in the build) did not match the
+   beginning and the end of the line correctly with ^ and $.
+
+ * "git rebase -m" tried to run "git notes copy" needlessly when
+   nothing was rewritten.
+
+Also contains minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.8.6.txt b/Documentation/RelNotes/1.7.8.6.txt
new file mode 100644
index 0000000..d9bf2b7
--- /dev/null
+++ b/Documentation/RelNotes/1.7.8.6.txt
@@ -0,0 +1,22 @@
+Git v1.7.8.6 Release Notes
+==========================
+
+Fixes since v1.7.8.5
+--------------------
+
+ * An error message from 'git bundle' had an unmatched single quote pair in it.
+
+ * 'git diff --histogram' option was not described.
+
+ * Documentation for 'git rev-list' had minor formatting errors.
+
+ * 'git imap-send' carried an unused dead code.
+
+ * The way 'git fetch' implemented its connectivity check over
+   received objects was overly pessimistic, and wasted a lot of
+   cycles.
+
+ * Various minor backports of fixes from the 'master' and the 'maint'
+   branch.
+
+Also contains minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.8.txt b/Documentation/RelNotes/1.7.8.txt
new file mode 100644
index 0000000..b4d90bb
--- /dev/null
+++ b/Documentation/RelNotes/1.7.8.txt
@@ -0,0 +1,161 @@
+Git v1.7.8 Release Notes
+========================
+
+Updates since v1.7.7
+--------------------
+
+ * Some git-svn, git-gui, git-p4 (in contrib) and msysgit updates.
+
+ * Updates to bash completion scripts.
+
+ * The build procedure has been taught to take advantage of computed
+   dependency automatically when the complier supports it.
+
+ * The date parser now accepts timezone designators that lack minutes
+   part and also has a colon between "hh:mm".
+
+ * The contents of the /etc/mailname file, if exists, is used as the
+   default value of the hostname part of the committer/author e-mail.
+
+ * "git am" learned how to read from patches generated by Hg.
+
+ * "git archive" talking with a remote repository can report errors
+   from the remote side in a more informative way.
+
+ * "git branch" learned an explicit --list option to ask for branches
+   listed, optionally with a glob matching pattern to limit its output.
+
+ * "git check-attr" learned "--cached" option to look at .gitattributes
+   files from the index, not from the working tree.
+
+ * Variants of "git cherry-pick" and "git revert" that take multiple
+   commits learned to "--continue" and "--abort".
+
+ * "git daemon" gives more human readble error messages to clients
+   using ERR packets when appropriate.
+
+ * Errors at the network layer is logged by "git daemon".
+
+ * "git diff" learned "--minimal" option to spend extra cycles to come
+   up with a minimal patch output.
+
+ * "git diff" learned "--function-context" option to show the whole
+   function as context that was affected by a change.
+
+ * "git difftool" can be told to skip launching the tool for a path by
+   answering 'n' to its prompt.
+
+ * "git fetch" learned to honor transfer.fsckobjects configuration to
+   validate the objects that were received from the other end, just like
+   "git receive-pack" (the receiving end of "git push") does.
+
+ * "git fetch" makes sure that the set of objects it received from the
+   other end actually completes the history before updating the refs.
+   "git receive-pack" (the receiving end of "git push") learned to do the
+   same.
+
+ * "git fetch" learned that fetching/cloning from a regular file on the
+   filesystem is not necessarily a request to unpack a bundle file; the
+   file could be ".git" with "gitdir: <path>" in it.
+
+ * "git for-each-ref" learned "%(contents:subject)", "%(contents:body)"
+   and "%(contents:signature)". The last one is useful for signed tags.
+
+ * "git grep" used to incorrectly pay attention to .gitignore files
+   scattered in the directory it was working in even when "--no-index"
+   option was used. It no longer does this. The "--exclude-standard"
+   option needs to be given to explicitly activate the ignore
+   mechanism.
+
+ * "git grep" learned "--untracked" option, where given patterns are
+    searched in untracked (but not ignored) files as well as tracked
+    files in the working tree, so that matches in new but not yet
+    added files do not get missed.
+
+ * The recursive merge backend no longer looks for meaningless
+   existing merges in submodules unless in the outermost merge.
+
+ * "git log" and friends learned "--children" option.
+
+ * "git ls-remote" learned to respond to "-h"(elp) requests.
+
+ * "mediawiki" remote helper can interact with (surprise!) MediaWiki
+   with "git fetch" & "git push".
+
+ * "git merge" learned the "--edit" option to allow users to edit the
+   merge commit log message.
+
+ * "git rebase -i" can be told to use special purpose editor suitable
+   only for its insn sheet via sequence.editor configuration variable.
+
+ * "git send-email" learned to respond to "-h"(elp) requests.
+
+ * "git send-email" allows the value given to sendemail.aliasfile to begin
+   with "~/" to refer to the $HOME directory.
+
+ * "git send-email" forces use of Authen::SASL::Perl to work around
+   issues between Authen::SASL::Cyrus and AUTH PLAIN/LOGIN.
+
+ * "git stash" learned "--include-untracked" option to stash away
+   untracked/ignored cruft from the working tree.
+
+ * "git submodule clone" does not leak an error message to the UI
+   level unnecessarily anymore.
+
+ * "git submodule update" learned to honor "none" as the value for
+   submodule.<name>.update to specify that the named submodule should
+   not be checked out by default.
+
+ * When populating a new submodule directory with "git submodule init",
+   the $GIT_DIR metainformation directory for submodules is created inside
+   $GIT_DIR/modules/<name>/ directory of the superproject and referenced
+   via the gitfile mechanism. This is to make it possible to switch
+   between commits in the superproject that has and does not have the
+   submodule in the tree without re-cloning.
+
+ * "gitweb" leaked unescaped control characters from syntax hiliter
+   outputs.
+
+ * "gitweb" can be told to give custom string at the end of the HTML
+   HEAD element.
+
+ * "gitweb" now has its own manual pages.
+
+
+Also contains other documentation updates and minor code cleanups.
+
+
+Fixes since v1.7.7
+------------------
+
+Unless otherwise noted, all fixes in the 1.7.7.X maintenance track are
+included in this release.
+
+ * HTTP transport did not use pushurl correctly, and also did not tell
+   what host it is trying to authenticate with when asking for
+   credentials.
+   (merge deba493 jk/http-auth later to maint).
+
+ * "git blame" was aborted if started from an uncommitted content and
+   the path had the textconv filter in effect.
+   (merge 8518088 ss/blame-textconv-fake-working-tree later to maint).
+
+ * Adding many refs to the local repository in one go (e.g. "git fetch"
+   that fetches many tags) and looking up a ref by name in a repository
+   with too many refs were unnecessarily slow.
+   (merge 17d68a54d jp/get-ref-dir-unsorted later to maint).
+
+ * Report from "git commit" on untracked files was confused under
+   core.ignorecase option.
+   (merge 395c7356 jk/name-hash-dirent later to maint).
+
+ * "git merge" did not understand ":/<pattern>" as a way to name a commit.
+
+ " "git push" on the receiving end used to call post-receive and post-update
+   hooks for attempted removal of non-existing refs.
+   (merge 160b81ed ph/push-to-delete-nothing later to maint).
+
+ * Help text for "git remote set-url" and "git remote set-branches"
+   were misspelled.
+   (merge c49904e fc/remote-seturl-usage-fix later to maint).
+   (merge 656cdf0 jc/remote-setbranches-usage-fix later to maint).
diff --git a/Documentation/RelNotes/1.7.9.1.txt b/Documentation/RelNotes/1.7.9.1.txt
new file mode 100644
index 0000000..6957183
--- /dev/null
+++ b/Documentation/RelNotes/1.7.9.1.txt
@@ -0,0 +1,63 @@
+Git v1.7.9.1 Release Notes
+==========================
+
+Fixes since v1.7.9
+------------------
+
+ * The makefile allowed environment variable X seep into it result in
+   command names suffixed with unnecessary strings.
+
+ * The set of included header files in compat/inet-{ntop,pton}
+   wrappers was updated for Windows some time ago, but in a way that
+   broke Solaris build.
+
+ * rpmbuild noticed an unpackaged but installed *.mo file and failed.
+
+ * Subprocesses spawned from various git programs were often left running
+   to completion even when the top-level process was killed.
+
+ * "git add -e" learned not to show a diff for an otherwise unmodified
+   submodule that only has uncommitted local changes in the patch
+   prepared by for the user to edit.
+
+ * Typo in "git branch --edit-description my-tpoic" was not diagnosed.
+
+ * Using "git grep -l/-L" together with options -W or --break may not
+   make much sense as the output is to only count the number of hits
+   and there is no place for file breaks, but the latter options made
+   "-l/-L" to miscount the hits.
+
+ * "git log --first-parent $pathspec" did not stay on the first parent
+   chain and veered into side branch from which the whole change to the
+   specified paths came.
+
+ * "git merge --no-edit $tag" failed to honor the --no-edit option.
+
+ * "git merge --ff-only $tag" failed because it cannot record the
+   required mergetag without creating a merge, but this is so common
+   operation for branch that is used _only_ to follow the upstream, so
+   it was changed to allow fast-forwarding without recording the mergetag.
+
+ * "git mergetool" now gives an empty file as the common base version
+   to the backend when dealing with the "both sides added, differently"
+   case.
+
+ * "git push -q" was not sufficiently quiet.
+
+ * When "git push" fails to update any refs, the client side did not
+   report an error correctly to the end user.
+
+ * "rebase" and "commit --amend" failed to work on commits with ancient
+   timestamps near year 1970.
+
+ * When asking for a tag to be pulled, "request-pull" did not show the
+   name of the tag prefixed with "tags/", which would have helped older
+   clients.
+
+ * "git submodule add $path" forgot to recompute the name to be stored
+   in .gitmodules when the submodule at $path was once added to the
+   superproject and already initialized.
+
+ * Many small corner case bugs on "git tag -n" was corrected.
+
+Also contains minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.9.2.txt b/Documentation/RelNotes/1.7.9.2.txt
new file mode 100644
index 0000000..e500da7
--- /dev/null
+++ b/Documentation/RelNotes/1.7.9.2.txt
@@ -0,0 +1,69 @@
+Git v1.7.9.2 Release Notes
+==========================
+
+Fixes since v1.7.9.1
+--------------------
+
+ * Bash completion script (in contrib/) did not like a pattern that
+   begins with a dash to be passed to __git_ps1 helper function.
+
+ * Adaptation of the bash completion script (in contrib/) for zsh
+   incorrectly listed all subcommands when "git <TAB><TAB>" was given
+   to ask for list of porcelain subcommands.
+
+ * The build procedure for profile-directed optimized binary was not
+   working very well.
+
+ * Some systems need to explicitly link -lcharset to get locale_charset().
+
+ * t5541 ignored user-supplied port number used for HTTP server testing.
+
+ * The error message emitted when we see an empty loose object was
+   not phrased correctly.
+
+ * The code to ask for password did not fall back to the terminal
+   input when GIT_ASKPASS is set but does not work (e.g. lack of X
+   with GUI askpass helper).
+
+ * We failed to give the true terminal width to any subcommand when
+   they are invoked with the pager, i.e. "git -p cmd".
+
+ * map_user() was not rewriting its output correctly, which resulted
+   in the user visible symptom that "git blame -e" sometimes showed
+   excess '>' at the end of email addresses.
+
+ * "git checkout -b" did not allow switching out of an unborn branch.
+
+ * When you have both .../foo and .../foo.git, "git clone .../foo" did not
+   favor the former but the latter.
+
+ * "git commit" refused to create a commit when entries added with
+   "add -N" remained in the index, without telling Git what their content
+   in the next commit should be. We should have created the commit without
+   these paths.
+
+ * "git diff --stat" said "files", "insertions", and "deletions" even
+   when it is showing one "file", one "insertion" or one "deletion".
+
+ * The output from "git diff --stat" for two paths that have the same
+   amount of changes showed graph bars of different length due to the
+   way we handled rounding errors.
+
+ * "git grep" did not pay attention to -diff (hence -binary) attribute.
+
+ * The transport programs (fetch, push, clone)ignored --no-progress
+   and showed progress when sending their output to a terminal.
+
+ * Sometimes error status detected by a check in an earlier phase of
+   "git receive-pack" (the other end of "git push") was lost by later
+   checks, resulting in false indication of success.
+
+ * "git rev-list --verify" sometimes skipped verification depending on
+   the phase of the moon, which dates back to 1.7.8.x series.
+
+ * Search box in "gitweb" did not accept non-ASCII characters correctly.
+
+ * Search interface of "gitweb" did not show multiple matches in the same file
+   correctly.
+
+Also contains minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.9.3.txt b/Documentation/RelNotes/1.7.9.3.txt
new file mode 100644
index 0000000..91c6501
--- /dev/null
+++ b/Documentation/RelNotes/1.7.9.3.txt
@@ -0,0 +1,51 @@
+Git v1.7.9.3 Release Notes
+==========================
+
+Fixes since v1.7.9.2
+--------------------
+
+ * "git p4" (in contrib/) submit the changes to a wrong place when the
+   "--use-client-spec" option is set.
+
+ * The config.mak.autogen generated by optional autoconf support tried
+   to link the binary with -lintl even when libintl.h is missing from
+   the system.
+
+ * When the filter driver exits before reading the content before the
+   main git process writes the contents to be filtered to the pipe to
+   it, the latter could be killed with SIGPIPE instead of ignoring
+   such an event as an error.
+
+ * "git add --refresh <pathspec>" used to warn about unmerged paths
+   outside the given pathspec.
+
+ * The bulk check-in codepath in "git add" streamed contents that
+   needs smudge/clean filters without running them, instead of punting
+   and delegating to the codepath to run filters after slurping
+   everything to core.
+
+ * "git branch --with $that" assumed incorrectly that the user will never
+   ask the question with nonsense value in $that.
+
+ * "git bundle create" produced a corrupt bundle file upon seeing
+   commits with excessively long subject line.
+
+ * When a remote helper exits before reading the blank line from the
+   main git process to signal the end of commands, the latter could be
+   killed with SIGPIPE. Instead we should ignore such event as a
+   non-error.
+
+ * The commit log template given with "git merge --edit" did not have
+   a short instructive text like what "git commit" gives.
+
+ * "git rev-list --verify-objects -q" omitted the extra verification
+   it needs to do over "git rev-list --objects -q" by mistake.
+
+ * "gitweb" used to drop warnings in the log file when "heads" view is
+   accessed in a repository whose HEAD does not point at a valid
+   branch.
+
+ * An invalid regular expression pattern given by an end user made
+   "gitweb" to return garbled response.
+
+Also contains minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.9.4.txt b/Documentation/RelNotes/1.7.9.4.txt
new file mode 100644
index 0000000..e5217a1
--- /dev/null
+++ b/Documentation/RelNotes/1.7.9.4.txt
@@ -0,0 +1,24 @@
+Git v1.7.9.4 Release Notes
+==========================
+
+Fixes since v1.7.9.3
+--------------------
+
+ * The code to synthesize the fake ancestor tree used by 3-way merge
+   fallback in "git am" was not prepared to read a patch created with
+   a non-standard -p<num> value.
+
+ * "git bundle" did not record boundary commits correctly when there
+   are many of them.
+
+ * "git diff-index" and its friends at the plumbing level showed the
+   "diff --git" header and nothing else for a path whose cached stat
+   info is dirty without actual difference when asked to produce a
+   patch. This was a longstanding bug that we could have fixed long
+   time ago.
+
+ * "gitweb" did use quotemeta() to prepare search string when asked to
+   do a fixed-string project search, but did not use it by mistake and
+   used the user-supplied string instead.
+
+Also contains minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.9.5.txt b/Documentation/RelNotes/1.7.9.5.txt
new file mode 100644
index 0000000..95cc2bb
--- /dev/null
+++ b/Documentation/RelNotes/1.7.9.5.txt
@@ -0,0 +1,23 @@
+Git v1.7.9.5 Release Notes
+==========================
+
+Fixes since v1.7.9.4
+--------------------
+
+ * When "git config" diagnoses an error in a configuration file and
+   shows the line number for the offending line, it miscounted if the
+   error was at the end of line.
+
+ * "git fast-import" accepted "ls" command with an empty path by
+   mistake.
+
+ * Various new-ish output decoration modes of "git grep" were not
+   documented in the manual's synopsis section.
+
+ * The "remaining" subcommand to "git rerere" was not documented.
+
+ * "gitweb" used to drop warnings in the log file when "heads" view is
+   accessed in a repository whose HEAD does not point at a valid
+   branch.
+
+Also contains minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.9.6.txt b/Documentation/RelNotes/1.7.9.6.txt
new file mode 100644
index 0000000..74bf882
--- /dev/null
+++ b/Documentation/RelNotes/1.7.9.6.txt
@@ -0,0 +1,12 @@
+Git v1.7.9.6 Release Notes
+==========================
+
+Fixes since v1.7.9.5
+--------------------
+
+ * "git merge $tag" to merge an annotated tag always opens the editor
+   during an interactive edit session. v1.7.10 series introduced an
+   environment variable GIT_MERGE_AUTOEDIT to help older scripts decline
+   this behaviour, but the maintenance track should also support it.
+
+Also contains minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.9.7.txt b/Documentation/RelNotes/1.7.9.7.txt
new file mode 100644
index 0000000..59667d0
--- /dev/null
+++ b/Documentation/RelNotes/1.7.9.7.txt
@@ -0,0 +1,13 @@
+Git v1.7.9.7 Release Notes
+==========================
+
+Fixes since v1.7.9.6
+--------------------
+
+ * An error message from 'git bundle' had an unmatched single quote pair in it.
+
+ * The way 'git fetch' implemented its connectivity check over
+   received objects was overly pessimistic, and wasted a lot of
+   cycles.
+
+Also contains minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.9.txt b/Documentation/RelNotes/1.7.9.txt
new file mode 100644
index 0000000..95320aa
--- /dev/null
+++ b/Documentation/RelNotes/1.7.9.txt
@@ -0,0 +1,112 @@
+Git v1.7.9 Release Notes
+========================
+
+Updates since v1.7.8
+--------------------
+
+ * gitk updates accumulated since early 2011.
+
+ * git-gui updated to 0.16.0.
+
+ * git-p4 (in contrib/) updates.
+
+ * Git uses gettext to translate its most common interface messages
+   into the user's language if translations are available and the
+   locale is appropriately set. Distributors can drop new PO files
+   in po/ to add new translations.
+
+ * The code to handle username/password for HTTP transactions used in
+   "git push" & "git fetch" learned to talk "credential API" to
+   external programs to cache or store them, to allow integration with
+   platform native keychain mechanisms.
+
+ * The input prompts in the terminal use our own getpass() replacement
+   when possible. HTTP transactions used to ask for the username without
+   echoing back what was typed, but with this change you will see it as
+   you type.
+
+ * The internals of "revert/cherry-pick" have been tweaked to prepare
+   building more generic "sequencer" on top of the implementation that
+   drives them.
+
+ * "git rev-parse FETCH_HEAD" after "git fetch" without specifying
+   what to fetch from the command line will now show the commit that
+   would be merged if the command were "git pull".
+
+ * "git add" learned to stream large files directly into a packfile
+   instead of writing them into individual loose object files.
+
+ * "git checkout -B <current branch> <elsewhere>" is a more intuitive
+   way to spell "git reset --keep <elsewhere>".
+
+ * "git checkout" and "git merge" learned "--no-overwrite-ignore" option
+   to tell Git that untracked and ignored files are not expendable.
+
+ * "git commit --amend" learned "--no-edit" option to say that the
+   user is amending the tree being recorded, without updating the
+   commit log message.
+
+ * "git commit" and "git reset" re-learned the optimization to prime
+   the cache-tree information in the index, which makes it faster to
+   write a tree object out after the index entries are updated.
+
+ * "git commit" detects and rejects an attempt to stuff NUL byte in
+   the commit log message.
+
+ * "git commit" learned "-S" to GPG-sign the commit; this can be shown
+   with the "--show-signature" option to "git log".
+
+ * fsck and prune are relatively lengthy operations that still go
+   silent while making the end-user wait. They learned to give progress
+   output like other slow operations.
+
+ * The set of built-in function-header patterns for various languages
+   knows MATLAB.
+
+ * "git log --format='<format>'" learned new %g[nNeE] specifiers to
+   show information from the reflog entries when walking the reflog
+   (i.e. with "-g").
+
+ * "git pull" can be used to fetch and merge an annotated/signed tag,
+   instead of the tip of a topic branch. The GPG signature from the
+   signed tag is recorded in the resulting merge commit for later
+   auditing.
+
+ * "git log" learned "--show-signature" option to show the signed tag
+   that was merged that is embedded in the merge commit. It also can
+   show the signature made on the commit with "git commit -S".
+
+ * "git branch --edit-description" can be used to add descriptive text
+   to explain what a topic branch is about.
+
+ * "git fmt-merge-msg" learned to take the branch description into
+   account when preparing a merge summary that "git merge" records
+   when merging a local branch.
+
+ * "git request-pull" has been updated to convey more information
+   useful for integrators to decide if a topic is worth merging and
+   what is pulled is indeed what the requestor asked to pull,
+   including:
+
+   - the tip of the branch being requested to be merged;
+   - the branch description describing what the topic is about;
+   - the contents of the annotated tag, when requesting to pull a tag.
+
+ * "git pull" learned to notice 'pull.rebase' configuration variable,
+   which serves as a global fallback for setting 'branch.<name>.rebase'
+   configuration variable per branch.
+
+ * "git tag" learned "--cleanup" option to control how the whitespaces
+   and empty lines in tag message are cleaned up.
+
+ * "gitweb" learned to show side-by-side diff.
+
+Also contains minor documentation updates and code clean-ups.
+
+
+Fixes since v1.7.8
+------------------
+
+Unless otherwise noted, all the fixes since v1.7.8 in the maintenance
+releases are contained in this release (see release notes to them for
+details).
diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches
index 76fc84d..0dbf2c9 100644
--- a/Documentation/SubmittingPatches
+++ b/Documentation/SubmittingPatches
@@ -7,17 +7,24 @@
 	  before committing
 	- do not check in commented out code or unneeded files
 	- the first line of the commit message should be a short
-	  description and should skip the full stop
+	  description (50 characters is the soft limit, see DISCUSSION
+	  in git-commit(1)), and should skip the full stop
 	- the body should provide a meaningful commit message, which:
-		- uses the imperative, present tense: "change",
-		  not "changed" or "changes".
-		- includes motivation for the change, and contrasts
-		  its implementation with previous behaviour
-	- if you want your work included in git.git, add a
-	  "Signed-off-by: Your Name <you@example.com>" line to the
-	  commit message (or just use the option "-s" when
-	  committing) to confirm that you agree to the Developer's
-	  Certificate of Origin
+	  . explains the problem the change tries to solve, iow, what
+	    is wrong with the current code without the change.
+	  . justifies the way the change solves the problem, iow, why
+	    the result with the change is better.
+	  . alternate solutions considered but discarded, if any.
+	- describe changes in imperative mood, e.g. "make xyzzy do frotz"
+	  instead of "[This patch] makes xyzzy do frotz" or "[I] changed
+	  xyzzy to do frotz", as if you are giving orders to the codebase
+	  to change its behaviour.
+	- try to make sure your explanation can be understood without
+	  external resources. Instead of giving a URL to a mailing list
+	  archive, summarize the relevant points of the discussion.
+	- add a "Signed-off-by: Your Name <you@example.com>" line to the
+	  commit message (or just use the option "-s" when committing)
+	  to confirm that you agree to the Developer's Certificate of Origin
 	- make sure that you have tests for the bug you are fixing
 	- make sure that the test suite passes after your commit
 
@@ -41,6 +48,7 @@
 	  maintainer (gitster@pobox.com) if (and only if) the patch
 	  is ready for inclusion. If you use git-send-email(1),
 	  please test it first by sending email to yourself.
+	- see below for instructions specific to your mailer
 
 Long version:
 
@@ -53,6 +61,34 @@
 here on the technical/contents front, because the core GIT is
 thousand times smaller ;-).  So here is only the relevant bits.
 
+(0) Decide what to base your work on.
+
+In general, always base your work on the oldest branch that your
+change is relevant to.
+
+ - A bugfix should be based on 'maint' in general. If the bug is not
+   present in 'maint', base it on 'master'. For a bug that's not yet
+   in 'master', find the topic that introduces the regression, and
+   base your work on the tip of the topic.
+
+ - A new feature should be based on 'master' in general. If the new
+   feature depends on a topic that is in 'pu', but not in 'master',
+   base your work on the tip of that topic.
+
+ - Corrections and enhancements to a topic not yet in 'master' should
+   be based on the tip of that topic. If the topic has not been merged
+   to 'next', it's alright to add a note to squash minor corrections
+   into the series.
+
+ - In the exceptional case that a new feature depends on several topics
+   not in 'master', start working on 'next' or 'pu' privately and send
+   out patches for discussion. Before the final merge, you may have to
+   wait until some of the dependent topics graduate to 'master', and
+   rebase your work.
+
+To find the tip of a topic branch, run "git log --first-parent
+master..pu" and look for the merge commit. The second parent of this
+commit is the tip of the topic branch.
 
 (1) Make separate commits for logically separate changes.
 
@@ -62,7 +98,10 @@
 commit message and generate a series of patches from your
 repository.  It is a good discipline.
 
-Describe the technical detail of the change(s).
+Give an explanation for the change(s) that is detailed enough so
+that people can judge if it is good thing to do, without reading
+the actual patch text to determine how well the code does what
+the explanation promises to do.
 
 If your description starts to get too long, that's a sign that you
 probably need to split up your commit to finer grained pieces.
@@ -71,9 +110,8 @@
 the code, are the most beautiful patches.  Descriptions that summarise
 the point in the subject well, and describe the motivation for the
 change, the approach taken by the change, and if relevant how this
-differs substantially from the prior version, can be found on Usenet
-archives back into the late 80's.  Consider it like good Netiquette,
-but for code.
+differs substantially from the prior version, are all good things
+to have.
 
 Oh, another thing.  I am picky about whitespaces.  Make sure your
 changes do not trigger errors with the sample pre-commit hook shipped
@@ -96,8 +134,7 @@
 
 (2) Generate your patch using git tools out of your commits.
 
-git based diff tools (git, Cogito, and StGIT included) generate
-unidiff which is the preferred format.
+git based diff tools generate unidiff which is the preferred format.
 
 You do not have to be afraid to use -M option to "git diff" or
 "git format-patch", if your patch involves file renames.  The
@@ -170,17 +207,16 @@
 that starts with '-----BEGIN PGP SIGNED MESSAGE-----'.  That is
 not a text/plain, it's something else.
 
-Note that your maintainer does not necessarily read everything
-on the git mailing list.  If your patch is for discussion first,
-send it "To:" the mailing list, and optionally "cc:" him.  If it
-is trivially correct or after the list reached a consensus, send
-it "To:" the maintainer and optionally "cc:" the list for
-inclusion.
-
-Also note that your maintainer does not actively involve himself in
-maintaining what are in contrib/ hierarchy.  When you send fixes and
-enhancements to them, do not forget to "cc: " the person who primarily
-worked on that hierarchy in contrib/.
+Unless your patch is a very trivial and an obviously correct one,
+first send it with "To:" set to the mailing list, with "cc:" listing
+people who are involved in the area you are touching (the output from
+"git blame $path" and "git shortlog --no-merges $path" would help to
+identify them), to solicit comments and reviews.  After the list
+reached a consensus that it is a good idea to apply the patch, re-send
+it with "To:" set to the maintainer and optionally "cc:" the list for
+inclusion.  Do not forget to add trailers such as "Acked-by:",
+"Reviewed-by:" and "Tested-by:" after your "Signed-off-by:" line as
+necessary.
 
 
 (4) Sign your work
@@ -237,12 +273,21 @@
 Also notice that a real name is used in the Signed-off-by: line. Please
 don't hide your real name.
 
-Some people also put extra tags at the end.
+If you like, you can put extra tags at the end:
 
-"Acked-by:" says that the patch was reviewed by the person who
-is more familiar with the issues and the area the patch attempts
-to modify.  "Tested-by:" says the patch was tested by the person
-and found to have the desired effect.
+1. "Reported-by:" is used to credit someone who found the bug that
+   the patch attempts to fix.
+2. "Acked-by:" says that the person who is more familiar with the area
+   the patch attempts to modify liked the patch.
+3. "Reviewed-by:", unlike the other tags, can only be offered by the
+   reviewer and means that she is completely satisfied that the patch
+   is ready for application.  It is usually offered only after a
+   detailed review.
+4. "Tested-by:" is used to indicate that the person applied the patch
+   and found it to have the desired effect.
+
+You can also create your own tag or use one that's in common usage
+such as "Thanks-to:", "Based-on-patch-by:", or "Mentored-by:".
 
 ------------------------------------------------
 An ideal patch flow
@@ -280,54 +325,38 @@
 their trees themselves.
 
 ------------------------------------------------
+Know the status of your patch after submission
+
+* You can use Git itself to find out when your patch is merged in
+  master. 'git pull --rebase' will automatically skip already-applied
+  patches, and will let you know. This works only if you rebase on top
+  of the branch in which your patch has been merged (i.e. it will not
+  tell you if your patch is merged in pu if you rebase on top of
+  master).
+
+* Read the git mailing list, the maintainer regularly posts messages
+  entitled "What's cooking in git.git" and "What's in git.git" giving
+  the status of various proposed changes.
+
+------------------------------------------------
 MUA specific hints
 
 Some of patches I receive or pick up from the list share common
 patterns of breakage.  Please make sure your MUA is set up
-properly not to corrupt whitespaces.  Here are two common ones
-I have seen:
+properly not to corrupt whitespaces.
 
-* Empty context lines that do not have _any_ whitespace.
+See the DISCUSSION section of git-format-patch(1) for hints on
+checking your patch by mailing it to yourself and applying with
+git-am(1).
 
-* Non empty context lines that have one extra whitespace at the
-  beginning.
-
-One test you could do yourself if your MUA is set up correctly is:
-
-* Send the patch to yourself, exactly the way you would, except
-  To: and Cc: lines, which would not contain the list and
-  maintainer address.
-
-* Save that patch to a file in UNIX mailbox format.  Call it say
-  a.patch.
-
-* Try to apply to the tip of the "master" branch from the
-  git.git public repository:
-
-    $ git fetch http://kernel.org/pub/scm/git/git.git master:test-apply
-    $ git checkout test-apply
-    $ git reset --hard
-    $ git am a.patch
-
-If it does not apply correctly, there can be various reasons.
-
-* Your patch itself does not apply cleanly.  That is _bad_ but
-  does not have much to do with your MUA.  Please rebase the
-  patch appropriately.
-
-* Your MUA corrupted your patch; "am" would complain that
-  the patch does not apply.  Look at .git/rebase-apply/ subdirectory and
-  see what 'patch' file contains and check for the common
-  corruption patterns mentioned above.
-
-* While you are at it, check what are in 'info' and
-  'final-commit' files as well.  If what is in 'final-commit' is
-  not exactly what you would want to see in the commit log
-  message, it is very likely that your maintainer would end up
-  hand editing the log message when he applies your patch.
-  Things like "Hi, this is my first patch.\n", if you really
-  want to put in the patch e-mail, should come after the
-  three-dash line that signals the end of the commit message.
+While you are at it, check the resulting commit log message from
+a trial run of applying the patch.  If what is in the resulting
+commit is not exactly what you would want to see, it is very
+likely that your maintainer would end up hand editing the log
+message when he applies your patch.  Things like "Hi, this is my
+first patch.\n", if you really want to put in the patch e-mail,
+should come after the three-dash line that signals the end of the
+commit message.
 
 
 Pine
@@ -383,89 +412,10 @@
 it.
 
 
-Thunderbird
------------
+Thunderbird, KMail, GMail
+-------------------------
 
-(A Large Angry SCM)
-
-By default, Thunderbird will both wrap emails as well as flag them as
-being 'format=flowed', both of which will make the resulting email unusable
-by git.
-
-Here are some hints on how to successfully submit patches inline using
-Thunderbird.
-
-There are two different approaches.  One approach is to configure
-Thunderbird to not mangle patches.  The second approach is to use
-an external editor to keep Thunderbird from mangling the patches.
-
-Approach #1 (configuration):
-
-This recipe is current as of Thunderbird 2.0.0.19.  Three steps:
-  1.  Configure your mail server composition as plain text
-      Edit...Account Settings...Composition & Addressing,
-        uncheck 'Compose Messages in HTML'.
-  2.  Configure your general composition window to not wrap
-      Edit..Preferences..Composition, wrap plain text messages at 0
-  3.  Disable the use of format=flowed
-      Edit..Preferences..Advanced..Config Editor.  Search for:
-        mailnews.send_plaintext_flowed
-      toggle it to make sure it is set to 'false'.
-
-After that is done, you should be able to compose email as you
-otherwise would (cut + paste, git-format-patch | git-imap-send, etc),
-and the patches should not be mangled.
-
-Approach #2 (external editor):
-
-This recipe appears to work with the current [*1*] Thunderbird from Suse.
-
-The following Thunderbird extensions are needed:
-	AboutConfig 0.5
-		http://aboutconfig.mozdev.org/
-	External Editor 0.7.2
-		http://globs.org/articles.php?lng=en&pg=8
-
-1) Prepare the patch as a text file using your method of choice.
-
-2) Before opening a compose window, use Edit->Account Settings to
-uncheck the "Compose messages in HTML format" setting in the
-"Composition & Addressing" panel of the account to be used to send the
-patch. [*2*]
-
-3) In the main Thunderbird window, _before_ you open the compose window
-for the patch, use Tools->about:config to set the following to the
-indicated values:
-	mailnews.send_plaintext_flowed	=> false
-	mailnews.wraplength		=> 0
-
-4) Open a compose window and click the external editor icon.
-
-5) In the external editor window, read in the patch file and exit the
-editor normally.
-
-6) Back in the compose window: Add whatever other text you wish to the
-message, complete the addressing and subject fields, and press send.
-
-7) Optionally, undo the about:config/account settings changes made in
-steps 2 & 3.
-
-
-[Footnotes]
-*1* Version 1.0 (20041207) from the MozillaThunderbird-1.0-5 rpm of Suse
-9.3 professional updates.
-
-*2* It may be possible to do this with about:config and the following
-settings but I haven't tried, yet.
-	mail.html_compose			=> false
-	mail.identity.default.compose_html	=> false
-	mail.identity.id?.compose_html		=> false
-
-(Lukas Sandström)
-
-There is a script in contrib/thunderbird-patch-inline which can help
-you include patches with Thunderbird in an easy way. To use it, do the
-steps above and then use the script as the external editor.
+See the MUA-SPECIFIC HINTS section of git-format-patch(1).
 
 Gnus
 ----
@@ -480,58 +430,3 @@
 whitespaces (fatal in patches).  Running 'C-u g' to display the
 message in raw form before using '|' to run the pipe can work
 this problem around.
-
-
-KMail
------
-
-This should help you to submit patches inline using KMail.
-
-1) Prepare the patch as a text file.
-
-2) Click on New Mail.
-
-3) Go under "Options" in the Composer window and be sure that
-"Word wrap" is not set.
-
-4) Use Message -> Insert file... and insert the patch.
-
-5) Back in the compose window: add whatever other text you wish to the
-message, complete the addressing and subject fields, and press send.
-
-
-Gmail
------
-
-GMail does not appear to have any way to turn off line wrapping in the web
-interface, so this will mangle any emails that you send.  You can however
-use any IMAP email client to connect to the google imap server, and forward
-the emails through that.  Just make sure to disable line wrapping in that
-email client.  Alternatively, use "git send-email" instead.
-
-Submitting properly formatted patches via Gmail is simple now that
-IMAP support is available. First, edit your ~/.gitconfig to specify your
-account settings:
-
-[imap]
-	folder = "[Gmail]/Drafts"
-	host = imaps://imap.gmail.com
-	user = user@gmail.com
-	pass = p4ssw0rd
-	port = 993
-	sslverify = false
-
-You might need to instead use: folder = "[Google Mail]/Drafts" if you get an error
-that the "Folder doesn't exist".
-
-Next, ensure that your Gmail settings are correct. In "Settings" the
-"Use Unicode (UTF-8) encoding for outgoing messages" should be checked.
-
-Once your commits are ready to send to the mailing list, run the following
-command to send the patch emails to your Gmail Drafts folder.
-
-	$ git format-patch -M --stdout origin/master | git imap-send
-
-Go to your Gmail account, open the Drafts folder, find the patch email, fill
-in the To: and CC: fields and send away!
-
diff --git a/Documentation/asciidoc.conf b/Documentation/asciidoc.conf
index 87a90f2..a26d245 100644
--- a/Documentation/asciidoc.conf
+++ b/Documentation/asciidoc.conf
@@ -16,8 +16,11 @@
 caret=&#94;
 startsb=&#91;
 endsb=&#93;
+backslash=&#92;
 tilde=&#126;
+apostrophe=&#39;
 backtick=&#96;
+litdd=&#45;&#45;
 
 ifdef::backend-docbook[]
 [linkgit-inlinemacro]
@@ -87,6 +90,8 @@
 endif::doctype-manpage[]
 
 ifdef::backend-xhtml11[]
+[attributes]
+git-relative-html-prefix=
 [linkgit-inlinemacro]
-<a href="{target}.html">{target}{0?({0})}</a>
+<a href="{git-relative-html-prefix}{target}.html">{target}{0?({0})}</a>
 endif::backend-xhtml11[]
diff --git a/Documentation/blame-options.txt b/Documentation/blame-options.txt
index 1625ffc..d4a51da 100644
--- a/Documentation/blame-options.txt
+++ b/Documentation/blame-options.txt
@@ -52,6 +52,11 @@
 --porcelain::
 	Show in a format designed for machine consumption.
 
+--line-porcelain::
+	Show the porcelain format, but output commit information for
+	each line, not just the first time a commit is referenced.
+	Implies --porcelain.
+
 --incremental::
 	Show the result incrementally in a format designed for
 	machine consumption.
@@ -79,33 +84,37 @@
 	of the --date option at linkgit:git-log[1].
 
 -M|<num>|::
-	Detect moving lines in the file as well.  When a commit
-	moves a block of lines in a file (e.g. the original file
-	has A and then B, and the commit changes it to B and
-	then A), the traditional 'blame' algorithm typically blames
-	the lines that were moved up (i.e. B) to the parent and
-	assigns blame to the lines that were moved down (i.e. A)
-	to the child commit.  With this option, both groups of lines
-	are blamed on the parent.
+	Detect moved or copied lines within a file. When a commit
+	moves or copies a block of lines (e.g. the original file
+	has A and then B, and the commit changes it to B and then
+	A), the traditional 'blame' algorithm notices only half of
+	the movement and typically blames the lines that were moved
+	up (i.e. B) to the parent and assigns blame to the lines that
+	were moved down (i.e. A) to the child commit.  With this
+	option, both groups of lines are blamed on the parent by
+	running extra passes of inspection.
 +
 <num> is optional but it is the lower bound on the number of
-alphanumeric characters that git must detect as moving
+alphanumeric characters that git must detect as moving/copying
 within a file for it to associate those lines with the parent
-commit.
+commit. The default value is 20.
 
 -C|<num>|::
-	In addition to `-M`, detect lines copied from other
+	In addition to `-M`, detect lines moved or copied from other
 	files that were modified in the same commit.  This is
 	useful when you reorganize your program and move code
 	around across files.  When this option is given twice,
-	the command additionally looks for copies from all other
-	files in the parent for the commit that creates the file.
+	the command additionally looks for copies from other
+	files in the commit that creates the file. When this
+	option is given three times, the command additionally
+	looks for copies from other files in any commit.
 +
 <num> is optional but it is the lower bound on the number of
-alphanumeric characters that git must detect as moving
+alphanumeric characters that git must detect as moving/copying
 between files for it to associate those lines with the parent
-commit.
+commit. And the default value is 40. If there are more than one
+`-C` options given, the <num> argument of the last `-C` will
+take effect.
 
 -h::
---help::
 	Show help message.
diff --git a/Documentation/config.txt b/Documentation/config.txt
index c6f09f8..a95e5a4 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -12,8 +12,9 @@
 and the porcelains. The variables are divided into sections, wherein
 the fully qualified variable name of the variable itself is the last
 dot-separated segment and the section name is everything before the last
-dot. The variable names are case-insensitive and only alphanumeric
-characters are allowed. Some variables may appear multiple times.
+dot. The variable names are case-insensitive, allow only alphanumeric
+characters and `-`, and must start with an alphabetic character.  Some
+variables may appear multiple times.
 
 Syntax
 ~~~~~~
@@ -45,26 +46,28 @@
 You can have `[section]` if you have `[section "subsection"]`, but you
 don't need to.
 
-There is also a case insensitive alternative `[section.subsection]` syntax.
-In this syntax, subsection names follow the same restrictions as for section
-names.
+There is also a deprecated `[section.subsection]` syntax. With this
+syntax, the subsection name is converted to lower-case and is also
+compared case sensitively. These subsection names follow the same
+restrictions as section names.
 
 All the other lines (and the remainder of the line after the section
 header) are recognized as setting variables, in the form
 'name = value'.  If there is no equal sign on the line, the entire line
 is taken as 'name' and the variable is recognized as boolean "true".
-The variable names are case-insensitive and only alphanumeric
-characters and `-` are allowed.  There can be more than one value
-for a given variable; we say then that variable is multivalued.
+The variable names are case-insensitive, allow only alphanumeric characters
+and `-`, and must start with an alphabetic character.  There can be more
+than one value for a given variable; we say then that the variable is
+multivalued.
 
 Leading and trailing whitespace in a variable value is discarded.
 Internal whitespace within a variable value is retained verbatim.
 
 The values following the equals sign in variable assign are all either
 a string, an integer, or a boolean.  Boolean values may be given as yes/no,
-0/1, true/false or on/off.  Case is not significant in boolean values, when
+1/0, true/false or on/off.  Case is not significant in boolean values, when
 converting value to the canonical form using '--bool' type specifier;
-'git-config' will ensure that the output is "true" or "false".
+'git config' will ensure that the output is "true" or "false".
 
 String values may be entirely or partially enclosed in double quotes.
 You need to enclose variable values in double quotes if you want to
@@ -83,6 +86,19 @@
 
 Some variables may require a special value format.
 
+Includes
+~~~~~~~~
+
+You can include one config file from another by setting the special
+`include.path` variable to the name of the file to be included. The
+included file is expanded immediately, as if its contents had been
+found at the location of the include directive. If the value of the
+`include.path` variable is a relative path, the path is considered to be
+relative to the configuration file in which the include directive was
+found. The value of `include.path` is subject to tilde expansion: `~/`
+is expanded to the value of `$HOME`, and `~user/` to the specified
+user's home directory. See below for examples.
+
 Example
 ~~~~~~~
 
@@ -105,6 +121,11 @@
 		gitProxy="ssh" for "kernel.org"
 		gitProxy=default-proxy ; for the rest
 
+	[include]
+		path = /path/to/foo.inc ; include by absolute path
+		path = foo ; expand "foo" relative to the current file
+		path = ~/foo ; expand "foo" in your $HOME directory
+
 Variables
 ~~~~~~~~~
 
@@ -113,10 +134,62 @@
 in the appropriate manual page. You will find a description of non-core
 porcelain configuration variables in the respective porcelain documentation.
 
+advice.*::
+	These variables control various optional help messages designed to
+	aid new users. All 'advice.*' variables default to 'true', and you
+	can tell Git that you do not need help by setting these to 'false':
++
+--
+	pushNonFastForward::
+		Set this variable to 'false' if you want to disable
+		'pushNonFFCurrent', 'pushNonFFDefault', and
+		'pushNonFFMatching' simultaneously.
+	pushNonFFCurrent::
+		Advice shown when linkgit:git-push[1] fails due to a
+		non-fast-forward update to the current branch.
+	pushNonFFDefault::
+		Advice to set 'push.default' to 'upstream' or 'current'
+		when you ran linkgit:git-push[1] and pushed 'matching
+		refs' by default (i.e. you did not provide an explicit
+		refspec, and no 'push.default' configuration was set)
+		and it resulted in a non-fast-forward error.
+	pushNonFFMatching::
+		Advice shown when you ran linkgit:git-push[1] and pushed
+		'matching refs' explicitly (i.e. you used ':', or
+		specified a refspec that isn't your current branch) and
+		it resulted in a non-fast-forward error.
+	statusHints::
+		Show directions on how to proceed from the current
+		state in the output of linkgit:git-status[1] and in
+		the template shown when writing commit messages in
+		linkgit:git-commit[1].
+	commitBeforeMerge::
+		Advice shown when linkgit:git-merge[1] refuses to
+		merge to avoid overwriting local changes.
+	resolveConflict::
+		Advices shown by various commands when conflicts
+		prevent the operation from being performed.
+	implicitIdentity::
+		Advice on how to set your identity configuration when
+		your information is guessed from the system username and
+		domain name.
+	detachedHead::
+		Advice shown when you used linkgit:git-checkout[1] to
+		move to the detach HEAD state, to instruct how to create
+		a local branch after the fact.
+	amWorkDir::
+		Advice that shows the location of the patch file when
+		linkgit:git-am[1] fails to apply it.
+--
+
 core.fileMode::
 	If false, the executable bit differences between the index and
-	the working copy are ignored; useful on broken filesystems like FAT.
-	See linkgit:git-update-index[1]. True by default.
+	the working tree are ignored; useful on broken filesystems like FAT.
+	See linkgit:git-update-index[1].
++
+The default is true, except linkgit:git-clone[1] or linkgit:git-init[1]
+will probe and set core.fileMode false if appropriate when the
+repository is created.
 
 core.ignoreCygwinFSTricks::
 	This option is only used by Cygwin implementation of Git. If false,
@@ -129,9 +202,30 @@
 	is true, in which case ignoreCygwinFSTricks is ignored as Cygwin's
 	POSIX emulation is required to support core.filemode.
 
+core.ignorecase::
+	If true, this option enables various workarounds to enable
+	git to work better on filesystems that are not case sensitive,
+	like FAT. For example, if a directory listing finds
+	"makefile" when git expects "Makefile", git will assume
+	it is really the same file, and continue to remember it as
+	"Makefile".
++
+The default is false, except linkgit:git-clone[1] or linkgit:git-init[1]
+will probe and set core.ignorecase true if appropriate when the repository
+is created.
+
+core.precomposeunicode::
+	This option is only used by Mac OS implementation of git.
+	When core.precomposeunicode=true, git reverts the unicode decomposition
+	of filenames done by Mac OS. This is useful when sharing a repository
+	between Mac OS and Linux or Windows.
+	(Git for Windows 1.7.10 or higher is needed, or git under cygwin 1.7).
+	When false, file names are handled fully transparent by git,
+	which is backward compatible with older versions of git.
+
 core.trustctime::
 	If false, the ctime differences between the index and the
-	working copy are ignored; useful when the inode change time
+	working tree are ignored; useful when the inode change time
 	is regularly modified by something outside Git (file system
 	crawlers and some backup systems).
 	See linkgit:git-update-index[1]. True by default.
@@ -148,19 +242,17 @@
 	quoted without `-z` regardless of the setting of this
 	variable.
 
-core.autocrlf::
-	If true, makes git convert `CRLF` at the end of lines in text files to
-	`LF` when reading from the filesystem, and convert in reverse when
-	writing to the filesystem.  The variable can be set to
-	'input', in which case the conversion happens only while
-	reading from the filesystem but files are written out with
-	`LF` at the end of lines.  Currently, which paths to consider
-	"text" (i.e. be subjected to the autocrlf mechanism) is
-	decided purely based on the contents.
+core.eol::
+	Sets the line ending type to use in the working directory for
+	files that have the `text` property set.  Alternatives are
+	'lf', 'crlf' and 'native', which uses the platform's native
+	line ending.  The default value is `native`.  See
+	linkgit:gitattributes[5] for more information on end-of-line
+	conversion.
 
 core.safecrlf::
-	If true, makes git check if converting `CRLF` as controlled by
-	`core.autocrlf` is reversible.  Git will verify if a command
+	If true, makes git check if converting `CRLF` is reversible when
+	end-of-line conversion is active.  Git will verify if a command
 	modifies a file in the work tree either directly or indirectly.
 	For example, committing a file followed by checking out the
 	same file should yield the original file in the work tree.  If
@@ -170,7 +262,7 @@
 	irreversible conversion but continue the operation.
 +
 CRLF conversion bears a slight chance of corrupting data.
-autocrlf=true will convert CRLF to LF during commit and LF to
+When it is enabled, git will convert CRLF to LF during commit and LF to
 CRLF during checkout.  A file that contains a mixture of LF and
 CRLF before the commit cannot be recreated by git.  For text
 files this is the right thing to do: it corrects line endings
@@ -194,21 +286,35 @@
 +
 Note, this safety check does not mean that a checkout will generate a
 file identical to the original file for a different setting of
-`core.autocrlf`, but only for the current one.  For example, a text
-file with `LF` would be accepted with `core.autocrlf=input` and could
-later be checked out with `core.autocrlf=true`, in which case the
+`core.eol` and `core.autocrlf`, but only for the current one.  For
+example, a text file with `LF` would be accepted with `core.eol=lf`
+and could later be checked out with `core.eol=crlf`, in which case the
 resulting file would contain `CRLF`, although the original file
 contained `LF`.  However, in both work trees the line endings would be
 consistent, that is either all `LF` or all `CRLF`, but never mixed.  A
 file with mixed line endings would be reported by the `core.safecrlf`
 mechanism.
 
+core.autocrlf::
+	Setting this variable to "true" is almost the same as setting
+	the `text` attribute to "auto" on all files except that text
+	files are not guaranteed to be normalized: files that contain
+	`CRLF` in the repository will not be touched.  Use this
+	setting if you want to have `CRLF` line endings in your
+	working directory even though the repository does not have
+	normalized line endings.  This variable can be set to 'input',
+	in which case no output conversion is performed.
+
 core.symlinks::
 	If false, symbolic links are checked out as small plain files that
 	contain the link text. linkgit:git-update-index[1] and
 	linkgit:git-add[1] will not change the recorded type to regular
 	file. Useful on filesystems like FAT that do not support
-	symbolic links. True by default.
+	symbolic links.
++
+The default is true, except linkgit:git-clone[1] or linkgit:git-init[1]
+will probe and set core.symlinks false if appropriate when the repository
+is created.
 
 core.gitProxy::
 	A "proxy command" to execute (as 'command host port') instead
@@ -232,7 +338,7 @@
 	If true, commands which modify both the working tree and the index
 	will mark the updated paths with the "assume unchanged" bit in the
 	index. These marked files are then assumed to stay unchanged in the
-	working copy, until you	mark them otherwise manually - Git will not
+	working tree, until you mark them otherwise manually - Git will not
 	detect the file changes	by lstat() calls. This is useful on systems
 	where those are very slow, such as Microsoft Windows.
 	See linkgit:git-update-index[1].
@@ -257,17 +363,26 @@
 = true).
 
 core.worktree::
-	Set the path to the working tree.  The value will not be
-	used in combination with repositories found automatically in
-	a .git directory (i.e. $GIT_DIR is not set).
+	Set the path to the root of the working tree.
 	This can be overridden by the GIT_WORK_TREE environment
-	variable and the '--work-tree' command line option. It can be
-	a absolute path or relative path to the directory specified by
-	--git-dir or GIT_DIR.
-	Note: If --git-dir or GIT_DIR are specified but none of
+	variable and the '--work-tree' command line option.
+	The value can be an absolute path or relative to the path to
+	the .git directory, which is either specified by --git-dir
+	or GIT_DIR, or automatically discovered.
+	If --git-dir or GIT_DIR is specified but none of
 	--work-tree, GIT_WORK_TREE and core.worktree is specified,
-	the current working directory is regarded as the top directory
+	the current working directory is regarded as the top level
 	of your working tree.
++
+Note that this variable is honored even when set in a configuration
+file in a ".git" subdirectory of a directory and its value differs
+from the latter directory (e.g. "/path/to/.git/config" has
+core.worktree set to "/different/path"), which is most likely a
+misconfiguration.  Running git commands in the "/path/to" directory will
+still use "/different/path" as the root of the work tree and can cause
+confusion unless you know what you are doing (e.g. you are creating a
+read-only snapshot of the same index to a location different from the
+repository's usual working tree).
 
 core.logAllRefUpdates::
 	Enable the reflog. Updates to a ref <ref> is logged to the file
@@ -275,7 +390,9 @@
 	SHA1, the date/time and the reason of the update, but
 	only when the file exists.  If this configuration
 	variable is set to true, missing "$GIT_DIR/logs/<ref>"
-	file is automatically created for branch heads.
+	file is automatically created for branch heads (i.e. under
+	refs/heads/), remote refs (i.e. under refs/remotes/),
+	note refs (i.e. under refs/notes/), and the symbolic ref HEAD.
 +
 This information can be used to determine what commit
 was the tip of a branch "2 days ago".
@@ -351,7 +468,7 @@
 
 core.deltaBaseCacheLimit::
 	Maximum number of bytes to reserve for caching base objects
-	that multiple deltafied objects reference.  By storing the
+	that may be referenced by multiple deltified objects.  By storing the
 	entire decompressed base objects in a cache Git is able
 	to avoid unpacking and decompressing frequently used base
 	objects multiple times.
@@ -362,19 +479,55 @@
 +
 Common unit suffixes of 'k', 'm', or 'g' are supported.
 
+core.bigFileThreshold::
+	Files larger than this size are stored deflated, without
+	attempting delta compression.  Storing large files without
+	delta compression avoids excessive memory usage, at the
+	slight expense of increased disk usage.
++
+Default is 512 MiB on all platforms.  This should be reasonable
+for most projects as source code and other text files can still
+be delta compressed, but larger binary media files won't be.
++
+Common unit suffixes of 'k', 'm', or 'g' are supported.
+
 core.excludesfile::
 	In addition to '.gitignore' (per-directory) and
 	'.git/info/exclude', git looks into this file for patterns
-	of files which are not meant to be tracked.  See
-	linkgit:gitignore[5].
+	of files which are not meant to be tracked.  "`~/`" is expanded
+	to the value of `$HOME` and "`~user/`" to the specified user's
+	home directory. Its default value is $XDG_CONFIG_HOME/git/ignore.
+	If $XDG_CONFIG_HOME is either not set or empty, $HOME/.config/git/ignore
+	is used instead. See linkgit:gitignore[5].
+
+core.askpass::
+	Some commands (e.g. svn and http interfaces) that interactively
+	ask for a password can be told to use an external program given
+	via the value of this variable. Can be overridden by the 'GIT_ASKPASS'
+	environment variable. If not set, fall back to the value of the
+	'SSH_ASKPASS' environment variable or, failing that, a simple password
+	prompt. The external program shall be given a suitable prompt as
+	command line argument and write the password on its STDOUT.
+
+core.attributesfile::
+	In addition to '.gitattributes' (per-directory) and
+	'.git/info/attributes', git looks into this file for attributes
+	(see linkgit:gitattributes[5]). Path expansions are made the same
+	way as for `core.excludesfile`. Its default value is
+	$XDG_CONFIG_HOME/git/attributes. If $XDG_CONFIG_HOME is either not
+	set or empty, $HOME/.config/git/attributes is used instead.
 
 core.editor::
 	Commands such as `commit` and `tag` that lets you edit
 	messages by launching an editor uses the value of this
 	variable when it is set, and the environment variable
-	`GIT_EDITOR` is not set.  The order of preference is
-	`GIT_EDITOR` environment, `core.editor`, `VISUAL` and
-	`EDITOR` environment variables and then finally `vi`.
+	`GIT_EDITOR` is not set.  See linkgit:git-var[1].
+
+sequence.editor::
+	Text editor used by `git rebase -i` for editing the rebase insn file.
+	The value is meant to be interpreted by the shell when it is used.
+	It can be overridden by the `GIT_SEQUENCE_EDITOR` environment variable.
+	When not configured the default commit message editor is used instead.
 
 core.pager::
 	The command that git will use to paginate output.  Can
@@ -396,22 +549,31 @@
 
 core.whitespace::
 	A comma separated list of common whitespace problems to
-	notice.  'git-diff' will use `color.diff.whitespace` to
-	highlight them, and 'git-apply --whitespace=error' will
+	notice.  'git diff' will use `color.diff.whitespace` to
+	highlight them, and 'git apply --whitespace=error' will
 	consider them as errors.  You can prefix `-` to disable
 	any of them (e.g. `-trailing-space`):
 +
-* `trailing-space` treats trailing whitespaces at the end of the line
+* `blank-at-eol` treats trailing whitespaces at the end of the line
   as an error (enabled by default).
 * `space-before-tab` treats a space character that appears immediately
   before a tab character in the initial indent part of the line as an
   error (enabled by default).
 * `indent-with-non-tab` treats a line that is indented with 8 or more
   space characters as an error (not enabled by default).
+* `tab-in-indent` treats a tab character in the initial indent part of
+  the line as an error (not enabled by default).
+* `blank-at-eof` treats blank lines added at the end of file as an error
+  (enabled by default).
+* `trailing-space` is a short-hand to cover both `blank-at-eol` and
+  `blank-at-eof`.
 * `cr-at-eol` treats a carriage-return at the end of line as
   part of the line terminator, i.e. with it, `trailing-space`
   does not trigger if the character before such a carriage-return
   is not a whitespace (not enabled by default).
+* `tabwidth=<n>` tells how many character positions a tab occupies; this
+  is relevant for `indent-with-non-tab` and when git fixes `tab-in-indent`
+  errors. The default tab width is 8. Allowed values are 1 to 63.
 
 core.fsyncobjectfiles::
 	This boolean will enable 'fsync()' when writing object files.
@@ -439,10 +601,33 @@
 Set this config setting to 'rename' there; However, This will remove the
 check that makes sure that existing object files will not get overwritten.
 
+core.notesRef::
+	When showing commit messages, also show notes which are stored in
+	the given ref.  The ref must be fully qualified.  If the given
+	ref does not exist, it is not an error but means that no
+	notes should be printed.
++
+This setting defaults to "refs/notes/commits", and it can be overridden by
+the 'GIT_NOTES_REF' environment variable.  See linkgit:git-notes[1].
+
+core.sparseCheckout::
+	Enable "sparse checkout" feature. See section "Sparse checkout" in
+	linkgit:git-read-tree[1] for more information.
+
+core.abbrev::
+	Set the length object names are abbreviated to.  If unspecified,
+	many commands abbreviate to 7 hexdigits, which may not be enough
+	for abbreviated object names to stay unique for sufficiently long
+	time.
+
 add.ignore-errors::
-	Tells 'git-add' to continue adding files when some files cannot be
+add.ignoreErrors::
+	Tells 'git add' to continue adding files when some files cannot be
 	added due to indexing errors. Equivalent to the '--ignore-errors'
-	option of linkgit:git-add[1].
+	option of linkgit:git-add[1].  Older versions of git accept only
+	`add.ignore-errors`, which does not follow the usual naming
+	convention for configuration variables.  Newer versions of git
+	honor `add.ignoreErrors` as well.
 
 alias.*::
 	Command aliases for the linkgit:git[1] command wrapper - e.g.
@@ -460,31 +645,49 @@
 "gitk --all --not ORIG_HEAD".  Note that shell commands will be
 executed from the top-level directory of a repository, which may
 not necessarily be the current directory.
+'GIT_PREFIX' is set as returned by running 'git rev-parse --show-prefix'
+from the original current directory. See linkgit:git-rev-parse[1].
+
+am.keepcr::
+	If true, git-am will call git-mailsplit for patches in mbox format
+	with parameter '--keep-cr'. In this case git-mailsplit will
+	not remove `\r` from lines ending with `\r\n`. Can be overridden
+	by giving '--no-keep-cr' from the command line.
+	See linkgit:git-am[1], linkgit:git-mailsplit[1].
+
+apply.ignorewhitespace::
+	When set to 'change', tells 'git apply' to ignore changes in
+	whitespace, in the same way as the '--ignore-space-change'
+	option.
+	When set to one of: no, none, never, false tells 'git apply' to
+	respect all whitespace differences.
+	See linkgit:git-apply[1].
 
 apply.whitespace::
-	Tells 'git-apply' how to handle whitespaces, in the same way
+	Tells 'git apply' how to handle whitespaces, in the same way
 	as the '--whitespace' option. See linkgit:git-apply[1].
 
 branch.autosetupmerge::
-	Tells 'git-branch' and 'git-checkout' to setup new branches
+	Tells 'git branch' and 'git checkout' to set up new branches
 	so that linkgit:git-pull[1] will appropriately merge from the
 	starting point branch. Note that even if this option is not set,
 	this behavior can be chosen per-branch using the `--track`
 	and `--no-track` options. The valid settings are: `false` -- no
 	automatic setup is done; `true` -- automatic setup is done when the
-	starting point is a remote branch; `always` -- automatic setup is
-	done when the starting point is either a local branch or remote
+	starting point is a remote-tracking branch; `always` --
+	automatic setup is done when the starting point is either a
+	local branch or remote-tracking
 	branch. This option defaults to true.
 
 branch.autosetuprebase::
-	When a new branch is created with 'git-branch' or 'git-checkout'
+	When a new branch is created with 'git branch' or 'git checkout'
 	that tracks another branch, this variable tells git to set
 	up pull to rebase instead of merge (see "branch.<name>.rebase").
 	When `never`, rebase is never automatically set to true.
 	When `local`, rebase is set to true for tracked branches of
 	other local branches.
 	When `remote`, rebase is set to true for tracked branches of
-	remote branches.
+	remote-tracking branches.
 	When `always`, rebase will be set to true for all tracking
 	branches.
 	See "branch.autosetupmerge" for details on how to set up a
@@ -492,46 +695,48 @@
 	This option defaults to never.
 
 branch.<name>.remote::
-	When in branch <name>, it tells 'git-fetch' and 'git-push' which
+	When in branch <name>, it tells 'git fetch' and 'git push' which
 	remote to fetch from/push to.  It defaults to `origin` if no remote is
 	configured. `origin` is also used if you are not on any branch.
 
 branch.<name>.merge::
 	Defines, together with branch.<name>.remote, the upstream branch
-	for the given branch. It tells 'git-fetch'/'git-pull' which
-	branch to merge and can also affect 'git-push' (see push.default).
-	When in branch <name>, it tells 'git-fetch' the default
+	for the given branch. It tells 'git fetch'/'git pull'/'git rebase' which
+	branch to merge and can also affect 'git push' (see push.default).
+	When in branch <name>, it tells 'git fetch' the default
 	refspec to be marked for merging in FETCH_HEAD. The value is
 	handled like the remote part of a refspec, and must match a
 	ref which is fetched from the remote given by
 	"branch.<name>.remote".
-	The merge information is used by 'git-pull' (which at first calls
-	'git-fetch') to lookup the default branch for merging. Without
-	this option, 'git-pull' defaults to merge the first refspec fetched.
+	The merge information is used by 'git pull' (which at first calls
+	'git fetch') to lookup the default branch for merging. Without
+	this option, 'git pull' defaults to merge the first refspec fetched.
 	Specify multiple values to get an octopus merge.
-	If you wish to setup 'git-pull' so that it merges into <name> from
+	If you wish to setup 'git pull' so that it merges into <name> from
 	another branch in the local repository, you can point
 	branch.<name>.merge to the desired branch, and use the special setting
 	`.` (a period) for branch.<name>.remote.
 
 branch.<name>.mergeoptions::
 	Sets default options for merging into branch <name>. The syntax and
-	supported options are equal to that of linkgit:git-merge[1], but
+	supported options are the same as those of linkgit:git-merge[1], but
 	option values containing whitespace characters are currently not
 	supported.
 
 branch.<name>.rebase::
 	When true, rebase the branch <name> on top of the fetched branch,
 	instead of merging the default branch from the default remote when
-	"git pull" is run.
-	*NOTE*: this is a possibly dangerous operation; do *not* use
-	it unless you understand the implications (see linkgit:git-rebase[1]
-	for details).
+	"git pull" is run. See "pull.rebase" for doing this in a non
+	branch-specific manner.
++
+*NOTE*: this is a possibly dangerous operation; do *not* use
+it unless you understand the implications (see linkgit:git-rebase[1]
+for details).
 
 browser.<tool>.cmd::
 	Specify the command to invoke the specified browser. The
 	specified command is evaluated in shell with the URLs passed
-	as arguments. (See linkgit:git-web--browse[1].)
+	as arguments. (See linkgit:git-web{litdd}browse[1].)
 
 browser.<tool>.path::
 	Override the path for the given tool that may be used to
@@ -551,7 +756,7 @@
 color.branch.<slot>::
 	Use customized color for branch coloration. `<slot>` is one of
 	`current` (the current branch), `local` (a local branch),
-	`remote` (a tracking branch in refs/remotes/), `plain` (other
+	`remote` (a remote-tracking branch in refs/remotes/), `plain` (other
 	refs).
 +
 The value for these configuration variables is a list of colors (at most
@@ -563,37 +768,59 @@
 doesn't matter.
 
 color.diff::
-	When set to `always`, always use colors in patch.
-	When false (or `never`), never.  When set to `true` or `auto`, use
-	colors only when the output is to the terminal. Defaults to false.
+	Whether to use ANSI escape sequences to add color to patches.
+	If this is set to `always`, linkgit:git-diff[1],
+	linkgit:git-log[1], and linkgit:git-show[1] will use color
+	for all patches.  If it is set to `true` or `auto`, those
+	commands will only use color when output is to the terminal.
+	Defaults to false.
++
+This does not affect linkgit:git-format-patch[1] nor the
+'git-diff-{asterisk}' plumbing commands.  Can be overridden on the
+command line with the `--color[=<when>]` option.
 
 color.diff.<slot>::
 	Use customized color for diff colorization.  `<slot>` specifies
 	which part of the patch to use the specified color, and is one
 	of `plain` (context text), `meta` (metainformation), `frag`
-	(hunk header), `old` (removed lines), `new` (added lines),
-	`commit` (commit headers), or `whitespace` (highlighting
-	whitespace errors). The values of these variables may be specified as
-	in color.branch.<slot>.
+	(hunk header), 'func' (function in hunk header), `old` (removed lines),
+	`new` (added lines), `commit` (commit headers), or `whitespace`
+	(highlighting whitespace errors). The values of these variables may be
+	specified as in color.branch.<slot>.
+
+color.decorate.<slot>::
+	Use customized color for 'git log --decorate' output.  `<slot>` is one
+	of `branch`, `remoteBranch`, `tag`, `stash` or `HEAD` for local
+	branches, remote-tracking branches, tags, stash and HEAD, respectively.
 
 color.grep::
 	When set to `always`, always highlight matches.  When `false` (or
 	`never`), never.  When set to `true` or `auto`, use color only
 	when the output is written to the terminal.  Defaults to `false`.
 
-color.grep.external::
-	The string value of this variable is passed to an external 'grep'
-	command as a command line option if match highlighting is turned
-	on.  If set to an empty string, no option is passed at all,
-	turning off coloring for external 'grep' calls; this is the default.
-	For GNU grep, set it to `--color=always` to highlight matches even
-	when a pager is used.
-
-color.grep.match::
-	Use customized color for matches.  The value of this variable
-	may be specified as in color.branch.<slot>.  It is passed using
-	the environment variables 'GREP_COLOR' and 'GREP_COLORS' when
-	calling an external 'grep'.
+color.grep.<slot>::
+	Use customized color for grep colorization.  `<slot>` specifies which
+	part of the line to use the specified color, and is one of
++
+--
+`context`;;
+	non-matching text in context lines (when using `-A`, `-B`, or `-C`)
+`filename`;;
+	filename prefix (when not using `-h`)
+`function`;;
+	function name lines (when using `-p`)
+`linenumber`;;
+	line number prefix (when using `-n`)
+`match`;;
+	matching text
+`selected`;;
+	non-matching text in selected lines
+`separator`;;
+	separators between fields on a line (`:`, `-`, and `=`)
+	and between hunks (`--`)
+--
++
+The values of these variables may be specified as in color.branch.<slot>.
 
 color.interactive::
 	When set to `always`, always use colors for interactive prompts
@@ -602,10 +829,10 @@
 	colors only when the output is to the terminal. Defaults to false.
 
 color.interactive.<slot>::
-	Use customized color for 'git-add --interactive'
+	Use customized color for 'git add --interactive'
 	output. `<slot>` may be `prompt`, `header`, `help` or `error`, for
 	four distinct types of normal output from interactive
-	programs.  The values of these variables may be specified as
+	commands.  The values of these variables may be specified as
 	in color.branch.<slot>.
 
 color.pager::
@@ -629,74 +856,95 @@
 	one of `header` (the header text of the status message),
 	`added` or `updated` (files which are added but not committed),
 	`changed` (files which are changed but not added in the index),
-	`untracked` (files which are not tracked by git), or
+	`untracked` (files which are not tracked by git),
+	`branch` (the current branch), or
 	`nobranch` (the color the 'no branch' warning is shown in, defaulting
 	to red). The values of these variables may be specified as in
 	color.branch.<slot>.
 
 color.ui::
-	When set to `always`, always use colors in all git commands which
-	are capable of colored output. When false (or `never`), never. When
-	set to `true` or `auto`, use colors only when the output is to the
-	terminal. When more specific variables of color.* are set, they always
-	take precedence over this setting. Defaults to false.
+	This variable determines the default value for variables such
+	as `color.diff` and `color.grep` that control the use of color
+	per command family. Its scope will expand as more commands learn
+	configuration to set a default for the `--color` option.  Set it
+	to `always` if you want all output not intended for machine
+	consumption to use color, to `true` or `auto` if you want such
+	output to use color when written to the terminal, or to `false` or
+	`never` if you prefer git commands not to use color unless enabled
+	explicitly with some other configuration or the `--color` option.
+
+column.ui::
+	Specify whether supported commands should output in columns.
+	This variable consists of a list of tokens separated by spaces
+	or commas:
++
+--
+`always`;;
+	always show in columns
+`never`;;
+	never show in columns
+`auto`;;
+	show in columns if the output is to the terminal
+`column`;;
+	fill columns before rows (default)
+`row`;;
+	fill rows before columns
+`plain`;;
+	show in one column
+`dense`;;
+	make unequal size columns to utilize more space
+`nodense`;;
+	make equal size columns
+--
++
+This option defaults to 'never'.
+
+column.branch::
+	Specify whether to output branch listing in `git branch` in columns.
+	See `column.ui` for details.
+
+column.status::
+	Specify whether to output untracked files in `git status` in columns.
+	See `column.ui` for details.
+
+column.tag::
+	Specify whether to output tag listing in `git tag` in columns.
+	See `column.ui` for details.
+
+commit.status::
+	A boolean to enable/disable inclusion of status information in the
+	commit message template when using an editor to prepare the commit
+	message.  Defaults to true.
 
 commit.template::
 	Specify a file to use as the template for new commit messages.
+	"`~/`" is expanded to the value of `$HOME` and "`~user/`" to the
+	specified user's home directory.
 
-diff.autorefreshindex::
-	When using 'git-diff' to compare with work tree
-	files, do not consider stat-only change as changed.
-	Instead, silently run `git update-index --refresh` to
-	update the cached stat information for paths whose
-	contents in the work tree match the contents in the
-	index.  This option defaults to true.  Note that this
-	affects only 'git-diff' Porcelain, and not lower level
-	'diff' commands, such as 'git-diff-files'.
+credential.helper::
+	Specify an external helper to be called when a username or
+	password credential is needed; the helper may consult external
+	storage to avoid prompting the user for the credentials. See
+	linkgit:gitcredentials[7] for details.
 
-diff.external::
-	If this config variable is set, diff generation is not
-	performed using the internal diff machinery, but using the
-	given command.  Can be overridden with the `GIT_EXTERNAL_DIFF'
-	environment variable.  The command is called with parameters
-	as described under "git Diffs" in linkgit:git[1].  Note: if
-	you want to use an external diff program only on a subset of
-	your files, you	might want to use linkgit:gitattributes[5] instead.
+credential.useHttpPath::
+	When acquiring credentials, consider the "path" component of an http
+	or https URL to be important. Defaults to false. See
+	linkgit:gitcredentials[7] for more information.
 
-diff.mnemonicprefix::
-	If set, 'git-diff' uses a prefix pair that is different from the
-	standard "a/" and "b/" depending on what is being compared.  When
-	this configuration is in effect, reverse diff output also swaps
-	the order of the prefixes:
-'git-diff';;
-	compares the (i)ndex and the (w)ork tree;
-'git-diff HEAD';;
-	 compares a (c)ommit and the (w)ork tree;
-'git diff --cached';;
-	compares a (c)ommit and the (i)ndex;
-'git-diff HEAD:file1 file2';;
-	compares an (o)bject and a (w)ork tree entity;
-'git diff --no-index a b';;
-	compares two non-git things (1) and (2).
+credential.username::
+	If no username is set for a network authentication, use this username
+	by default. See credential.<context>.* below, and
+	linkgit:gitcredentials[7].
 
-diff.renameLimit::
-	The number of files to consider when performing the copy/rename
-	detection; equivalent to the 'git-diff' option '-l'.
+credential.<url>.*::
+	Any of the credential.* options above can be applied selectively to
+	some credentials. For example "credential.https://example.com.username"
+	would set the default username only for https connections to
+	example.com. See linkgit:gitcredentials[7] for details on how URLs are
+	matched.
 
-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.
-
-diff.suppressBlankEmpty::
-	A boolean to inhibit the standard behavior of printing a space
-	before each empty output line. Defaults to false.
-
-diff.tool::
-	Controls which diff tool is used.  `diff.tool` overrides
-	`merge.tool` when used by linkgit:git-difftool[1] and has
-	the same valid values as `merge.tool` minus "tortoisemerge"
-	and plus "kompare".
+include::diff-config.txt[]
 
 difftool.<tool>.path::
 	Override the path for the given tool.  This is useful in case
@@ -719,6 +967,22 @@
 	sequences that match the regular expression are "words", all other
 	characters are *ignorable* whitespace.
 
+fetch.recurseSubmodules::
+	This option can be either set to a boolean value or to 'on-demand'.
+	Setting it to a boolean changes the behavior of fetch and pull to
+	unconditionally recurse into submodules when set to true or to not
+	recurse at all when set to false. When set to 'on-demand' (the default
+	value), fetch and pull will only recurse into a populated submodule
+	when its superproject retrieves a commit that updates the submodule's
+	reference.
+
+fetch.fsckObjects::
+	If it is set to true, git-fetch-pack will check all fetched
+	objects. It will abort in the case of a malformed object or a
+	broken link. The result of an abort are only dangling objects.
+	Defaults to false. If not set, the value of `transfer.fsckObjects`
+	is used instead.
+
 fetch.unpackLimit::
 	If the number of objects fetched over the git native
 	transfer is below this
@@ -748,14 +1012,22 @@
 	Additional email headers to include in a patch to be submitted
 	by mail.  See linkgit:git-format-patch[1].
 
+format.to::
 format.cc::
-	Additional "Cc:" headers to include in a patch to be submitted
-	by mail.  See the --cc option in linkgit:git-format-patch[1].
+	Additional recipients to include in a patch to be submitted
+	by mail.  See the --to and --cc options in
+	linkgit:git-format-patch[1].
 
 format.subjectprefix::
 	The default for format-patch is to output files with the '[PATCH]'
 	subject prefix. Use this variable to change that prefix.
 
+format.signature::
+	The default for format-patch is to output a signature containing
+	the git version number. Use this variable to change that default.
+	Set this variable to the empty string ("") to suppress
+	signature generation.
+
 format.suffix::
 	The default for format-patch is to output files with the suffix
 	`.patch`. Use this variable to change that suffix (make sure to
@@ -767,11 +1039,11 @@
 	linkgit:git-whatchanged[1].
 
 format.thread::
-	The default threading style for 'git-format-patch'.  Can be
-	either a boolean value, `shallow` or `deep`.  `shallow`
-	threading makes every mail a reply to the head of the series,
+	The default threading style for 'git format-patch'.  Can be
+	a boolean value, or `shallow` or `deep`.  `shallow` threading
+	makes every mail a reply to the head of the series,
 	where the head is chosen from the cover letter, the
-	`\--in-reply-to`, and the first patch mail, in this order.
+	`--in-reply-to`, and the first patch mail, in this order.
 	`deep` threading makes every mail a reply to the previous one.
 	A true boolean value is the same as `shallow`, and a false
 	value disables threading.
@@ -783,10 +1055,20 @@
     the rights to submit this work under the same open source license.
     Please see the 'SubmittingPatches' document for further discussion.
 
+filter.<driver>.clean::
+	The command which is used to convert the content of a worktree
+	file to a blob upon checkin.  See linkgit:gitattributes[5] for
+	details.
+
+filter.<driver>.smudge::
+	The command which is used to convert the content of a blob
+	object to a worktree file upon checkout.  See
+	linkgit:gitattributes[5] for details.
+
 gc.aggressiveWindow::
 	The window size parameter used in the delta compression
-	algorithm used by 'git-gc --aggressive'.  This defaults
-	to 10.
+	algorithm used by 'git gc --aggressive'.  This defaults
+	to 250.
 
 gc.auto::
 	When there are approximately more than this many loose
@@ -802,39 +1084,42 @@
 	default	value is 50.  Setting this to 0 disables it.
 
 gc.packrefs::
-	'git-gc' does not run `git pack-refs` in a bare repository by
-	default so that older dumb-transport clients can still fetch
-	from the repository.  Setting this to `true` lets 'git-gc'
-	to run `git pack-refs`.  Setting this to `false` tells
-	'git-gc' never to run `git pack-refs`. The default setting is
-	`notbare`. Enable it only when you know you do not have to
-	support such clients.  The default setting will change to `true`
-	at some stage, and setting this to `false` will continue to
-	prevent `git pack-refs` from being run from 'git-gc'.
+	Running `git pack-refs` in a repository renders it
+	unclonable by Git versions prior to 1.5.1.2 over dumb
+	transports such as HTTP.  This variable determines whether
+	'git gc' runs `git pack-refs`. This can be set to `notbare`
+	to enable it within all non-bare repos or it can be set to a
+	boolean value.  The default is `true`.
 
 gc.pruneexpire::
-	When 'git-gc' is run, it will call 'prune --expire 2.weeks.ago'.
+	When 'git gc' is run, it will call 'prune --expire 2.weeks.ago'.
 	Override the grace period with this config variable.  The value
 	"now" may be used to disable this  grace period and always prune
 	unreachable objects immediately.
 
 gc.reflogexpire::
-	'git-reflog expire' removes reflog entries older than
-	this time; defaults to 90 days.
+gc.<pattern>.reflogexpire::
+	'git reflog expire' removes reflog entries older than
+	this time; defaults to 90 days.  With "<pattern>" (e.g.
+	"refs/stash") in the middle the setting applies only to
+	the refs that match the <pattern>.
 
 gc.reflogexpireunreachable::
-	'git-reflog expire' removes reflog entries older than
+gc.<ref>.reflogexpireunreachable::
+	'git reflog expire' removes reflog entries older than
 	this time and are not reachable from the current tip;
-	defaults to 30 days.
+	defaults to 30 days.  With "<pattern>" (e.g. "refs/stash")
+	in the middle, the setting applies only to the refs that
+	match the <pattern>.
 
 gc.rerereresolved::
 	Records of conflicted merge you resolved earlier are
-	kept for this many days when 'git-rerere gc' is run.
+	kept for this many days when 'git rerere gc' is run.
 	The default is 60 days.  See linkgit:git-rerere[1].
 
 gc.rerereunresolved::
 	Records of conflicted merge you have not resolved are
-	kept for this many days when 'git-rerere gc' is run.
+	kept for this many days when 'git rerere gc' is run.
 	The default is 15 days.  See linkgit:git-rerere[1].
 
 gitcvs.commitmsgannotation::
@@ -850,13 +1135,15 @@
 	various stuff. See linkgit:git-cvsserver[1].
 
 gitcvs.usecrlfattr::
-	If true, the server will look up the `crlf` attribute for
-	files to determine the '-k' modes to use. If `crlf` is set,
-	the '-k' mode will be left blank, so cvs clients will
-	treat it as text. If `crlf` is explicitly unset, the file
+	If true, the server will look up the end-of-line conversion
+	attributes for files to determine the '-k' modes to use. If
+	the attributes force git to treat a file as text,
+	the '-k' mode will be left blank so CVS clients will
+	treat it as text. If they suppress text conversion, the file
 	will be set with '-kb' mode, which suppresses any newline munging
-	the client might otherwise do. If `crlf` is not specified,
-	then 'gitcvs.allbinary' is used. See linkgit:gitattributes[5].
+	the client might otherwise do. If the attributes do not allow
+	the file type to be determined, then 'gitcvs.allbinary' is
+	used. See linkgit:gitattributes[5].
 
 gitcvs.allbinary::
 	This is used if 'gitcvs.usecrlfattr' does not resolve
@@ -903,6 +1190,40 @@
 is one of "ext" and "pserver") to make them apply only for the given
 access method.
 
+gitweb.category::
+gitweb.description::
+gitweb.owner::
+gitweb.url::
+	See linkgit:gitweb[1] for description.
+
+gitweb.avatar::
+gitweb.blame::
+gitweb.grep::
+gitweb.highlight::
+gitweb.patches::
+gitweb.pickaxe::
+gitweb.remote_heads::
+gitweb.showsizes::
+gitweb.snapshot::
+	See linkgit:gitweb.conf[5] for description.
+
+grep.lineNumber::
+	If set to true, enable '-n' option by default.
+
+grep.extendedRegexp::
+	If set to true, enable '--extended-regexp' option by default.
+
+gpg.program::
+	Use this custom program instead of "gpg" found on $PATH when
+	making or verifying a PGP signature. The program must support the
+	same command line interface as GPG, namely, to verify a detached
+	signature, "gpg --verify $file - <$signature" is run, and the
+	program is expected to signal a good signature by exiting with
+	code 0, and to generate an ascii-armored detached signature, the
+	standard input of "gpg -bsau $key" is fed with the contents to be
+	signed, and the program is expected to send the result to its
+	standard output.
+
 gui.commitmsgwidth::
 	Defines how wide the commit message window is in the
 	linkgit:git-gui[1]. "75" is the default.
@@ -929,7 +1250,7 @@
 	linkgit:git-gui[1].
 
 gui.pruneduringfetch::
-	"true" if linkgit:git-gui[1] should prune tracking branches when
+	"true" if linkgit:git-gui[1] should prune remote-tracking branches when
 	performing a fetch. The default value is "false".
 
 gui.trustmtime::
@@ -942,7 +1263,7 @@
 	off.
 
 gui.fastcopyblame::
-	If true, 'git gui blame' uses '-C' instead of '-C -C' for original
+	If true, 'git gui blame' uses `-C` instead of `-C -C` for original
 	location detection. It makes blame significantly faster on huge
 	repositories at the expense of less thorough copy detection.
 
@@ -1027,9 +1348,18 @@
 	This is the default.
 
 http.proxy::
-	Override the HTTP proxy, normally configured using the 'http_proxy'
-	environment variable (see linkgit:curl[1]).  This can be overridden
-	on a per-remote basis; see remote.<name>.proxy
+	Override the HTTP proxy, normally configured using the 'http_proxy',
+	'https_proxy', and 'all_proxy' environment variables (see
+	`curl(1)`).  This can be overridden on a per-remote basis; see
+	remote.<name>.proxy
+
+http.cookiefile::
+	File containing previously stored cookie lines which should be used
+	in the git http session, if they match the server. The file format
+	of the file to read cookies from should be plain HTTP headers or
+	the Netscape/Mozilla cookie file format (see linkgit:curl[1]).
+	NOTE that the file specified with http.cookiefile is only used as
+	input. No cookies will be stored in the file.
 
 http.sslVerify::
 	Whether to verify the SSL certificate when fetching or pushing
@@ -1066,6 +1396,20 @@
 	How many HTTP requests to launch in parallel. Can be overridden
 	by the 'GIT_HTTP_MAX_REQUESTS' environment variable. Default is 5.
 
+http.minSessions::
+	The number of curl sessions (counted across slots) to be kept across
+	requests. They will not be ended with curl_easy_cleanup() until
+	http_cleanup() is invoked. If USE_CURL_MULTI is not defined, this
+	value will be capped at 1. Defaults to 1.
+
+http.postBuffer::
+	Maximum size in bytes of the buffer used by smart HTTP
+	transports when POSTing data to the remote system.
+	For requests larger than this buffer size, HTTP/1.1 and
+	Transfer-Encoding: chunked is used to avoid creating a
+	massive pack file locally.  Default is 1 MiB, which is
+	sufficient for most requests.
+
 http.lowSpeedLimit, http.lowSpeedTime::
 	If the HTTP transfer speed is less than 'http.lowSpeedLimit'
 	for longer than 'http.lowSpeedTime' seconds, the transfer is aborted.
@@ -1078,6 +1422,15 @@
 	support EPSV mode. Can be overridden by the 'GIT_CURL_FTP_NO_EPSV'
 	environment variable. Default is false (curl will use EPSV).
 
+http.useragent::
+	The HTTP USER_AGENT string presented to an HTTP server.  The default
+	value represents the version of the client git such as git/1.7.1.
+	This option allows you to override this value to a more common value
+	such as Mozilla/4.0.  This may be necessary, for instance, if
+	connecting through a firewall that restricts HTTP connections to a set
+	of common USER_AGENT strings (but not including those like git/1.7.1).
+	Can be overridden by the 'GIT_HTTP_USER_AGENT' environment variable.
+
 i18n.commitEncoding::
 	Character encoding the commit messages are stored in; git itself
 	does not care per se, but this information is necessary e.g. when
@@ -1087,12 +1440,16 @@
 
 i18n.logOutputEncoding::
 	Character encoding the commit messages are converted to when
-	running 'git-log' and friends.
+	running 'git log' and friends.
 
 imap::
 	The configuration variables in the 'imap' section are described
 	in linkgit:git-imap-send[1].
 
+init.templatedir::
+	Specify the directory from which templates will be copied.
+	(See the "TEMPLATE DIRECTORY" section of linkgit:git-init[1].)
+
 instaweb.browser::
 	Specify the program that will be used to browse your working
 	repository in gitweb. See linkgit:git-instaweb[1].
@@ -1106,24 +1463,41 @@
 	be bound to the local IP (127.0.0.1).
 
 instaweb.modulepath::
-	The module path for an apache httpd used by linkgit:git-instaweb[1].
+	The default module path for linkgit:git-instaweb[1] to use
+	instead of /usr/lib/apache2/modules.  Only used if httpd
+	is Apache.
 
 instaweb.port::
 	The port number to bind the gitweb httpd to. See
 	linkgit:git-instaweb[1].
 
 interactive.singlekey::
-	In interactive programs, allow the user to provide one-letter
+	In interactive commands, allow the user to provide one-letter
 	input with a single key (i.e., without hitting enter).
-	Currently this is used only by the `\--patch` mode of
-	linkgit:git-add[1].  Note that this setting is silently
-	ignored if portable keystroke input is not available.
+	Currently this is used by the `--patch` mode of
+	linkgit:git-add[1], linkgit:git-checkout[1], linkgit:git-commit[1],
+	linkgit:git-reset[1], and linkgit:git-stash[1]. Note that this
+	setting is silently ignored if portable keystroke input
+	is not available.
+
+log.abbrevCommit::
+	If true, makes linkgit:git-log[1], linkgit:git-show[1], and
+	linkgit:git-whatchanged[1] assume `--abbrev-commit`. You may
+	override this option with `--no-abbrev-commit`.
 
 log.date::
-	Set default date-time mode for the log command. Setting log.date
-	value is similar to using 'git-log'\'s --date option. The value is one of the
-	following alternatives: {relative,local,default,iso,rfc,short}.
-	See linkgit:git-log[1].
+	Set the default date-time mode for the 'log' command.
+	Setting a value for log.date is similar to using 'git log''s
+	`--date` option.  Possible values are `relative`, `local`,
+	`default`, `iso`, `rfc`, and `short`; see linkgit:git-log[1]
+	for details.
+
+log.decorate::
+	Print out the ref names of any commits that are shown by the log
+	command. If 'short' is specified, the ref name prefixes 'refs/heads/',
+	'refs/tags/' and 'refs/remotes/' will not be printed. If 'full' is
+	specified, the full ref name (including prefix) will be printed.
+	This is the same as the log commands '--decorate' option.
 
 log.showroot::
 	If true, the initial commit will be shown as a big creation event.
@@ -1193,6 +1567,54 @@
 mergetool.prompt::
 	Prompt before each invocation of the merge resolution program.
 
+notes.displayRef::
+	The (fully qualified) refname from which to show notes when
+	showing commit messages.  The value of this variable can be set
+	to a glob, in which case notes from all matching refs will be
+	shown.  You may also specify this configuration variable
+	several times.  A warning will be issued for refs that do not
+	exist, but a glob that does not match any refs is silently
+	ignored.
++
+This setting can be overridden with the `GIT_NOTES_DISPLAY_REF`
+environment variable, which must be a colon separated list of refs or
+globs.
++
+The effective value of "core.notesRef" (possibly overridden by
+GIT_NOTES_REF) is also implicitly added to the list of refs to be
+displayed.
+
+notes.rewrite.<command>::
+	When rewriting commits with <command> (currently `amend` or
+	`rebase`) and this variable is set to `true`, git
+	automatically copies your notes from the original to the
+	rewritten commit.  Defaults to `true`, but see
+	"notes.rewriteRef" below.
+
+notes.rewriteMode::
+	When copying notes during a rewrite (see the
+	"notes.rewrite.<command>" option), determines what to do if
+	the target commit already has a note.  Must be one of
+	`overwrite`, `concatenate`, or `ignore`.  Defaults to
+	`concatenate`.
++
+This setting can be overridden with the `GIT_NOTES_REWRITE_MODE`
+environment variable.
+
+notes.rewriteRef::
+	When copying notes during a rewrite, specifies the (fully
+	qualified) ref whose notes should be copied.  The ref may be a
+	glob, in which case notes in all matching refs will be copied.
+	You may also specify this configuration several times.
++
+Does not have a default value; you must configure this variable to
+enable note rewriting.  Set it to `refs/notes/commits` to enable
+rewriting for the default commit notes.
++
+This setting can be overridden with the `GIT_NOTES_REWRITE_REF`
+environment variable, which must be a colon separated list of refs or
+globs.
+
 pack.window::
 	The size of the window used by linkgit:git-pack-objects[1] when no
 	window size is given on the command line. Defaults to 10.
@@ -1215,15 +1637,27 @@
 	not set,  defaults to -1, the zlib default, which is "a default
 	compromise between speed and compression (currently equivalent
 	to level 6)."
++
+Note that changing the compression level will not automatically recompress
+all existing objects. You can force recompression by passing the -F option
+to linkgit:git-repack[1].
 
 pack.deltaCacheSize::
 	The maximum memory in bytes used for caching deltas in
-	linkgit:git-pack-objects[1].
-	A value of 0 means no limit. Defaults to 0.
+	linkgit:git-pack-objects[1] before writing them out to a pack.
+	This cache is used to speed up the writing object phase by not
+	having to recompute the final delta result once the best match
+	for all objects is found.  Repacking large repositories on machines
+	which are tight with memory might be badly impacted by this though,
+	especially if this cache pushes the system into swapping.
+	A value of 0 means no limit. The smallest size of 1 byte may be
+	used to virtually disable this cache. Defaults to 256 MiB.
 
 pack.deltaCacheLimit::
 	The maximum size of a delta, that is cached in
-	linkgit:git-pack-objects[1]. Defaults to 1000.
+	linkgit:git-pack-objects[1]. This cache is used to speed up the
+	writing object phase by not having to recompute the final delta
+	result once the best match for all objects is found. Defaults to 1000.
 
 pack.threads::
 	Specifies the number of threads to spawn when searching for best
@@ -1244,26 +1678,51 @@
 	and this config option ignored whenever the corresponding pack is
 	larger than 2 GB.
 +
-If you have an old git that does not understand the version 2 `{asterisk}.idx` file,
+If you have an old git that does not understand the version 2 `*.idx` file,
 cloning or fetching over a non native protocol (e.g. "http" and "rsync")
-that will copy both `{asterisk}.pack` file and corresponding `{asterisk}.idx` file from the
+that will copy both `*.pack` file and corresponding `*.idx` file from the
 other side may give you a repository that cannot be accessed with your
-older version of git. If the `{asterisk}.pack` file is smaller than 2 GB, however,
+older version of git. If the `*.pack` file is smaller than 2 GB, however,
 you can use linkgit:git-index-pack[1] on the *.pack file to regenerate
-the `{asterisk}.idx` file.
+the `*.idx` file.
 
 pack.packSizeLimit::
-	The default maximum size of a pack.  This setting only affects
-	packing to a file, i.e. the git:// protocol is unaffected.  It
-	can be overridden by the `\--max-pack-size` option of
-	linkgit:git-repack[1].
+	The maximum size of a pack.  This setting only affects
+	packing to a file when repacking, i.e. the git:// protocol
+	is unaffected.  It can be overridden by the `--max-pack-size`
+	option of linkgit:git-repack[1]. The minimum size allowed is
+	limited to 1 MiB. The default is unlimited.
+	Common unit suffixes of 'k', 'm', or 'g' are
+	supported.
 
 pager.<cmd>::
-	Allows turning on or off pagination of the output of a
-	particular git subcommand when writing to a tty.  If
-	`\--paginate` or `\--no-pager` is specified on the command line,
-	it takes precedence over this option.  To disable pagination for
-	all commands, set `core.pager` or `GIT_PAGER` to `cat`.
+	If the value is boolean, turns on or off pagination of the
+	output of a particular git subcommand when writing to a tty.
+	Otherwise, turns on pagination for the subcommand using the
+	pager specified by the value of `pager.<cmd>`.  If `--paginate`
+	or `--no-pager` is specified on the command line, it takes
+	precedence over this option.  To disable pagination for all
+	commands, set `core.pager` or `GIT_PAGER` to `cat`.
+
+pretty.<name>::
+	Alias for a --pretty= format string, as specified in
+	linkgit:git-log[1]. Any aliases defined here can be used just
+	as the built-in pretty formats could. For example,
+	running `git config pretty.changelog "format:* %H %s"`
+	would cause the invocation `git log --pretty=changelog`
+	to be equivalent to running `git log "--pretty=format:* %H %s"`.
+	Note that an alias with the same name as a built-in format
+	will be silently ignored.
+
+pull.rebase::
+	When true, rebase branches on top of the fetched branch, instead
+	of merging the default branch from the default remote when "git
+	pull" is run. See "branch.<name>.rebase" for setting this on a
+	per-branch basis.
++
+*NOTE*: this is a possibly dangerous operation; do *not* use
+it unless you understand the implications (see linkgit:git-rebase[1]
+for details).
 
 pull.octopus::
 	The default merge strategy to use when pulling multiple branches
@@ -1278,22 +1737,52 @@
 	no refspec is implied by any of the options given on the command
 	line. Possible values are:
 +
-* `nothing` do not push anything.
-* `matching` push all matching branches.
-  All branches having the same name in both ends are considered to be
-  matching. This is the default.
-* `tracking` push the current branch to its upstream branch.
-* `current` push the current branch to a branch of the same name.
+--
+* `nothing` - do not push anything.
+* `matching` - push all branches having the same name in both ends.
+  This is for those who prepare all the branches into a publishable
+  shape and then push them out with a single command.  It is not
+  appropriate for pushing into a repository shared by multiple users,
+  since locally stalled branches will attempt a non-fast forward push
+  if other users updated the branch.
+  +
+  This is currently the default, but Git 2.0 will change the default
+  to `simple`.
+* `upstream` - push the current branch to its upstream branch.
+  With this, `git push` will update the same remote ref as the one which
+  is merged by `git pull`, making `push` and `pull` symmetrical.
+  See "branch.<name>.merge" for how to configure the upstream branch.
+* `simple` - like `upstream`, but refuses to push if the upstream
+  branch's name is different from the local one. This is the safest
+  option and is well-suited for beginners. It will become the default
+  in Git 2.0.
+* `current` - push the current branch to a branch of the same name.
+--
++
+The `simple`, `current` and `upstream` modes are for those who want to
+push out a single branch after finishing work, even when the other
+branches are not yet ready to be pushed out. If you are working with
+other people to push into the same shared repository, you would want
+to use one of these.
 
 rebase.stat::
 	Whether to show a diffstat of what changed upstream since the last
 	rebase. False by default.
 
+rebase.autosquash::
+	If set to true enable '--autosquash' option by default.
+
+receive.autogc::
+	By default, git-receive-pack will run "git-gc --auto" after
+	receiving data from git-push and updating refs.  You can stop
+	it by setting this variable to false.
+
 receive.fsckObjects::
 	If it is set to true, git-receive-pack will check all received
 	objects. It will abort in the case of a malformed object or a
 	broken link. The result of an abort are only dangling objects.
-	Defaults to false.
+	Defaults to false. If not set, the value of `transfer.fsckObjects`
+	is used instead.
 
 receive.unpackLimit::
 	If the number of objects received in a push is below this
@@ -1309,21 +1798,29 @@
 	If set to true, git-receive-pack will deny a ref update that deletes
 	the ref. Use this to prevent such a ref deletion via a push.
 
+receive.denyDeleteCurrent::
+	If set to true, git-receive-pack will deny a ref update that
+	deletes the currently checked out branch of a non-bare repository.
+
 receive.denyCurrentBranch::
-	If set to true or "refuse", receive-pack will deny a ref update
+	If set to true or "refuse", git-receive-pack will deny a ref update
 	to the currently checked out branch of a non-bare repository.
 	Such a push is potentially dangerous because it brings the HEAD
 	out of sync with the index and working tree. If set to "warn",
 	print a warning of such a push to stderr, but allow the push to
 	proceed. If set to false or "ignore", allow such pushes with no
-	message. Defaults to "warn".
+	message. Defaults to "refuse".
 
 receive.denyNonFastForwards::
 	If set to true, git-receive-pack will deny a ref update which is
-	not a fast forward. Use this to prevent such an update via a push,
+	not a fast-forward. Use this to prevent such an update via a push,
 	even if that push is forced. This configuration variable is
 	set when initializing a shared repository.
 
+receive.updateserverinfo::
+	If set to true, git-receive-pack will run git-update-server-info
+	after receiving data from git-push and updating refs.
+
 remote.<name>.url::
 	The URL of a remote repository.  See linkgit:git-fetch[1] or
 	linkgit:git-push[1].
@@ -1346,11 +1843,17 @@
 
 remote.<name>.mirror::
 	If true, pushing to this remote will automatically behave
-	as if the `\--mirror` option was given on the command line.
+	as if the `--mirror` option was given on the command line.
 
 remote.<name>.skipDefaultUpdate::
 	If true, this remote will be skipped by default when updating
-	using the update subcommand of linkgit:git-remote[1].
+	using linkgit:git-fetch[1] or the `update` subcommand of
+	linkgit:git-remote[1].
+
+remote.<name>.skipFetchAll::
+	If true, this remote will be skipped by default when updating
+	using linkgit:git-fetch[1] or the `update` subcommand of
+	linkgit:git-remote[1].
 
 remote.<name>.receivepack::
 	The default program to execute on the remote side when pushing.  See
@@ -1362,7 +1865,15 @@
 
 remote.<name>.tagopt::
 	Setting this value to \--no-tags disables automatic tag following when
-	fetching from remote <name>
+	fetching from remote <name>. Setting it to \--tags will fetch every
+	tag from remote <name>, even if they are not reachable from remote
+	branch heads. Passing these flags directly to linkgit:git-fetch[1] can
+	override this setting. See options \--tags and \--no-tags of
+	linkgit:git-fetch[1].
+
+remote.<name>.vcs::
+	Setting this to a value <vcs> will cause git to interact with
+	the remote with the git-remote-<vcs> helper.
 
 remotes.<group>::
 	The list of remotes which are fetched by "git remote update
@@ -1383,10 +1894,11 @@
 
 rerere.enabled::
 	Activate recording of resolved conflicts, so that identical
-	conflict hunks can be resolved automatically, should they
-	be encountered again.  linkgit:git-rerere[1] command is by
-	default enabled if you create `rr-cache` directory under
-	`$GIT_DIR`, but can be disabled by setting this option to false.
+	conflict hunks can be resolved automatically, should they be
+	encountered again.  By default, linkgit:git-rerere[1] is
+	enabled if there is an `rr-cache` directory under the
+	`$GIT_DIR`, e.g. if "rerere" was previously used in the
+	repository.
 
 sendemail.identity::
 	A configuration identity. When given, causes values in the
@@ -1422,8 +1934,10 @@
 sendemail.suppresscc::
 sendemail.suppressfrom::
 sendemail.to::
+sendemail.smtpdomain::
 sendemail.smtpserver::
 sendemail.smtpserverport::
+sendemail.smtpserveroption::
 sendemail.smtpuser::
 sendemail.thread::
 sendemail.validate::
@@ -1452,15 +1966,51 @@
 	the untracked files. Possible values are:
 +
 --
-	- 'no'     - Show no untracked files
-	- 'normal' - Shows untracked files and directories
-	- 'all'    - Shows also individual files in untracked directories.
+* `no` - Show no untracked files.
+* `normal` - Show untracked files and directories.
+* `all` - Show also individual files in untracked directories.
 --
 +
 If this variable is not specified, it defaults to 'normal'.
 This variable can be overridden with the -u|--untracked-files option
 of linkgit:git-status[1] and linkgit:git-commit[1].
 
+status.submodulesummary::
+	Defaults to false.
+	If this is set to a non zero number or true (identical to -1 or an
+	unlimited number), the submodule summary will be enabled and a
+	summary of commits for modified submodules will be shown (see
+	--summary-limit option of linkgit:git-submodule[1]).
+
+submodule.<name>.path::
+submodule.<name>.url::
+submodule.<name>.update::
+	The path within this project, URL, and the updating strategy
+	for a submodule.  These variables are initially populated
+	by 'git submodule init'; edit them to override the
+	URL and other values found in the `.gitmodules` file.  See
+	linkgit:git-submodule[1] and linkgit:gitmodules[5] for details.
+
+submodule.<name>.fetchRecurseSubmodules::
+	This option can be used to control recursive fetching of this
+	submodule. It can be overridden by using the --[no-]recurse-submodules
+	command line option to "git fetch" and "git pull".
+	This setting will override that from in the linkgit:gitmodules[5]
+	file.
+
+submodule.<name>.ignore::
+	Defines under what circumstances "git status" and the diff family show
+	a submodule as modified. When set to "all", it will never be considered
+	modified, "dirty" will ignore all changes to the submodules work tree and
+	takes only differences between the HEAD of the submodule and the commit
+	recorded in the superproject into account. "untracked" will additionally
+	let submodules with modified tracked files in their work tree show up.
+	Using "none" (the default when this option is not set) also shows
+	submodules that have untracked files in their work tree as changed.
+	This setting overrides any setting made in .gitmodules for this submodule,
+	both settings can be overridden on the command line by using the
+	"--ignore-submodules" option.
+
 tar.umask::
 	This variable can be used to restrict the permission bits of
 	tar archive entries.  The default is 0002, which turns off the
@@ -1468,6 +2018,11 @@
 	archiving user's umask will be used instead.  See umask(2) and
 	linkgit:git-archive[1].
 
+transfer.fsckObjects::
+	When `fetch.fsckObjects` or `receive.fsckObjects` are
+	not set, the value of this variable is used instead.
+	Defaults to false.
+
 transfer.unpackLimit::
 	When `fetch.unpackLimit` or `receive.unpackLimit` are
 	not set, the value of this variable is used instead.
@@ -1484,6 +2039,19 @@
 	never-before-seen repository on the site.  When more than one
 	insteadOf strings match a given URL, the longest match is used.
 
+url.<base>.pushInsteadOf::
+	Any URL that starts with this value will not be pushed to;
+	instead, it will be rewritten to start with <base>, and the
+	resulting URL will be pushed to. In cases where some site serves
+	a large number of repositories, and serves them with multiple
+	access methods, some of which do not allow push, this feature
+	allows people to specify a pull-only URL and have git
+	automatically use an appropriate URL to push, even for a
+	never-before-seen repository on the site.  When more than one
+	pushInsteadOf strings match a given URL, the longest match is
+	used.  If a remote has an explicit pushurl, git will ignore this
+	setting for that remote.
+
 user.email::
 	Your email address to be recorded in any newly created commits.
 	Can be overridden by the 'GIT_AUTHOR_EMAIL', 'GIT_COMMITTER_EMAIL', and
diff --git a/Documentation/date-formats.txt b/Documentation/date-formats.txt
new file mode 100644
index 0000000..c000f08
--- /dev/null
+++ b/Documentation/date-formats.txt
@@ -0,0 +1,26 @@
+DATE FORMATS
+------------
+
+The GIT_AUTHOR_DATE, GIT_COMMITTER_DATE environment variables
+ifdef::git-commit[]
+and the `--date` option
+endif::git-commit[]
+support the following date formats:
+
+Git internal format::
+	It is `<unix timestamp> <timezone offset>`, where `<unix
+	timestamp>` is the number of seconds since the UNIX epoch.
+	`<timezone offset>` is a positive or negative offset from UTC.
+	For example CET (which is 2 hours ahead UTC) is `+0200`.
+
+RFC 2822::
+	The standard email format as described by RFC 2822, for example
+	`Thu, 07 Apr 2005 22:13:13 +0200`.
+
+ISO 8601::
+	Time and date specified by the ISO 8601 standard, for example
+	`2005-04-07T22:13:13`. The parser accepts a space instead of the
+	`T` character as well.
++
+NOTE: In addition, the date part is accepted in the following formats:
+`YYYY.MM.DD`, `MM/DD/YYYY` and `DD.MM.YYYY`.
diff --git a/Documentation/diff-config.txt b/Documentation/diff-config.txt
new file mode 100644
index 0000000..67a90a8
--- /dev/null
+++ b/Documentation/diff-config.txt
@@ -0,0 +1,140 @@
+diff.autorefreshindex::
+	When using 'git diff' to compare with work tree
+	files, do not consider stat-only change as changed.
+	Instead, silently run `git update-index --refresh` to
+	update the cached stat information for paths whose
+	contents in the work tree match the contents in the
+	index.  This option defaults to true.  Note that this
+	affects only 'git diff' Porcelain, and not lower level
+	'diff' commands such as 'git diff-files'.
+
+diff.dirstat::
+	A comma separated list of `--dirstat` parameters specifying the
+	default behavior of the `--dirstat` option to linkgit:git-diff[1]`
+	and friends. The defaults can be overridden on the command line
+	(using `--dirstat=<param1,param2,...>`). The fallback defaults
+	(when not changed by `diff.dirstat`) are `changes,noncumulative,3`.
+	The following parameters are available:
++
+--
+`changes`;;
+	Compute the dirstat numbers by counting the lines that have been
+	removed from the source, or added to the destination. This ignores
+	the amount of pure code movements within a file.  In other words,
+	rearranging lines in a file is not counted as much as other changes.
+	This is the default behavior when no parameter is given.
+`lines`;;
+	Compute the dirstat numbers by doing the regular line-based diff
+	analysis, and summing the removed/added line counts. (For binary
+	files, count 64-byte chunks instead, since binary files have no
+	natural concept of lines). This is a more expensive `--dirstat`
+	behavior than the `changes` behavior, but it does count rearranged
+	lines within a file as much as other changes. The resulting output
+	is consistent with what you get from the other `--*stat` options.
+`files`;;
+	Compute the dirstat numbers by counting the number of files changed.
+	Each changed file counts equally in the dirstat analysis. This is
+	the computationally cheapest `--dirstat` behavior, since it does
+	not have to look at the file contents at all.
+`cumulative`;;
+	Count changes in a child directory for the parent directory as well.
+	Note that when using `cumulative`, the sum of the percentages
+	reported may exceed 100%. The default (non-cumulative) behavior can
+	be specified with the `noncumulative` parameter.
+<limit>;;
+	An integer parameter specifies a cut-off percent (3% by default).
+	Directories contributing less than this percentage of the changes
+	are not shown in the output.
+--
++
+Example: The following will count changed files, while ignoring
+directories with less than 10% of the total amount of changed files,
+and accumulating child directory counts in the parent directories:
+`files,10,cumulative`.
+
+diff.statGraphWidth::
+	Limit the width of the graph part in --stat output. If set, applies
+	to all commands generating --stat output except format-patch.
+
+diff.external::
+	If this config variable is set, diff generation is not
+	performed using the internal diff machinery, but using the
+	given command.  Can be overridden with the `GIT_EXTERNAL_DIFF'
+	environment variable.  The command is called with parameters
+	as described under "git Diffs" in linkgit:git[1].  Note: if
+	you want to use an external diff program only on a subset of
+	your files, you	might want to use linkgit:gitattributes[5] instead.
+
+diff.ignoreSubmodules::
+	Sets the default value of --ignore-submodules. Note that this
+	affects only 'git diff' Porcelain, and not lower level 'diff'
+	commands such as 'git diff-files'. 'git checkout' also honors
+	this setting when reporting uncommitted changes.
+
+diff.mnemonicprefix::
+	If set, 'git diff' uses a prefix pair that is different from the
+	standard "a/" and "b/" depending on what is being compared.  When
+	this configuration is in effect, reverse diff output also swaps
+	the order of the prefixes:
+`git diff`;;
+	compares the (i)ndex and the (w)ork tree;
+`git diff HEAD`;;
+	 compares a (c)ommit and the (w)ork tree;
+`git diff --cached`;;
+	compares a (c)ommit and the (i)ndex;
+`git diff HEAD:file1 file2`;;
+	compares an (o)bject and a (w)ork tree entity;
+`git diff --no-index a b`;;
+	compares two non-git things (1) and (2).
+
+diff.noprefix::
+	If set, 'git diff' does not show any source or destination prefix.
+
+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.
+
+diff.suppressBlankEmpty::
+	A boolean to inhibit the standard behavior of printing a space
+	before each empty output line. Defaults to false.
+
+diff.<driver>.command::
+	The custom diff driver command.  See linkgit:gitattributes[5]
+	for details.
+
+diff.<driver>.xfuncname::
+	The regular expression that the diff driver should use to
+	recognize the hunk header.  A built-in pattern may also be used.
+	See linkgit:gitattributes[5] for details.
+
+diff.<driver>.binary::
+	Set this option to true to make the diff driver treat files as
+	binary.  See linkgit:gitattributes[5] for details.
+
+diff.<driver>.textconv::
+	The command that the diff driver should call to generate the
+	text-converted version of a file.  The result of the
+	conversion is used to generate a human-readable diff.  See
+	linkgit:gitattributes[5] for details.
+
+diff.<driver>.wordregex::
+	The regular expression that the diff driver should use to
+	split words in a line.  See linkgit:gitattributes[5] for
+	details.
+
+diff.<driver>.cachetextconv::
+	Set this option to true to make the diff driver cache the text
+	conversion outputs.  See linkgit:gitattributes[5] for details.
+
+diff.tool::
+	The diff tool to be used by linkgit:git-difftool[1].  This
+	option overrides `merge.tool`, and has the same valid built-in
+	values as `merge.tool` minus "tortoisemerge" and plus
+	"kompare".  Any other value is treated as a custom diff tool,
+	and there must be a corresponding `difftool.<tool>.cmd`
+	option.
diff --git a/Documentation/diff-format.txt b/Documentation/diff-format.txt
index b717124..15c7e79 100644
--- a/Documentation/diff-format.txt
+++ b/Documentation/diff-format.txt
@@ -19,7 +19,7 @@
 git-diff-files [<pattern>...]::
         compares the index and the files on the filesystem.
 
-The "git-diff-tree" command begins its ouput by printing the hash of
+The "git-diff-tree" command begins its output by printing the hash of
 what is being compared. After that, all the commands print one output
 line per changed file.
 
diff --git a/Documentation/diff-generate-patch.txt b/Documentation/diff-generate-patch.txt
index 0f25ba7..55f499a 100644
--- a/Documentation/diff-generate-patch.txt
+++ b/Documentation/diff-generate-patch.txt
@@ -9,16 +9,15 @@
 GIT_EXTERNAL_DIFF and the GIT_DIFF_OPTS environment variables.
 
 What the -p option produces is slightly different from the traditional
-diff format.
+diff format:
 
-1.   It is preceded with a "git diff" header, that looks like
-     this:
+1.   It is preceded with a "git diff" header that looks like this:
 
        diff --git a/file1 b/file2
 +
 The `a/` and `b/` filenames are the same unless rename/copy is
 involved.  Especially, even for a creation or a deletion,
-`/dev/null` is _not_ used in place of `a/` or `b/` filenames.
+`/dev/null` is _not_ used in place of the `a/` or `b/` filenames.
 +
 When rename/copy is involved, `file1` and `file2` show the
 name of the source file of the rename/copy and the name of
@@ -37,26 +36,51 @@
        similarity index <number>
        dissimilarity index <number>
        index <hash>..<hash> <mode>
-
-3.  TAB, LF, double quote and backslash characters in pathnames
-    are represented as `\t`, `\n`, `\"` and `\\`, respectively.
-    If there is need for such substitution then the whole
-    pathname is put in double quotes.
-
++
+File modes are printed as 6-digit octal numbers including the file type
+and file permission bits.
++
+Path names in extended headers do not include the `a/` and `b/` prefixes.
++
 The similarity index is the percentage of unchanged lines, and
 the dissimilarity index is the percentage of changed lines.  It
 is a rounded down integer, followed by a percent sign.  The
 similarity index value of 100% is thus reserved for two equal
 files, while 100% dissimilarity means that no line from the old
 file made it into the new one.
++
+The index line includes the SHA-1 checksum before and after the change.
+The <mode> is included if the file mode does not change; otherwise,
+separate lines indicate the old and the new mode.
+
+3.  TAB, LF, double quote and backslash characters in pathnames
+    are represented as `\t`, `\n`, `\"` and `\\`, respectively.
+    If there is need for such substitution then the whole
+    pathname is put in double quotes.
+
+4.  All the `file1` files in the output refer to files before the
+    commit, and all the `file2` files refer to files after the commit.
+    It is incorrect to apply each change to each file sequentially.  For
+    example, this patch will swap a and b:
+
+      diff --git a/a b/b
+      rename from a
+      rename to b
+      diff --git a/b b/a
+      rename from b
+      rename to a
 
 
 combined diff format
 --------------------
 
-"git-diff-tree", "git-diff-files" and "git-diff" can take '-c' or
-'--cc' option to produce 'combined diff'.  For showing a merge commit
-with "git log -p", this is the default format.
+Any diff-generating command can take the `-c` or `--cc` option to
+produce a 'combined diff' when showing a merge. This is the default
+format when showing merges with linkgit:git-diff[1] or
+linkgit:git-show[1]. Note also that you can give the `-m' option to any
+of these commands to force generation of diffs with individual parents
+of a merge.
+
 A 'combined diff' format looks like this:
 
 ------------
@@ -151,7 +175,7 @@
 from both files (hence two `-` removals from both file1 and
 file2, plus `++` to mean one line that was added does not appear
 in either file1 nor file2).  Also eight other lines are the same
-from file1 but do not appear in file2 (hence prefixed with `{plus}`).
+from file1 but do not appear in file2 (hence prefixed with `+`).
 
 When shown by `git diff-tree -c`, it compares the parents of a
 merge commit with the merge result (i.e. file1..fileN are the
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 9276fae..cf4b216 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -14,12 +14,14 @@
 
 ifdef::git-format-patch[]
 -p::
-	Generate patches without diffstat.
+--no-stat::
+	Generate plain patches without any diffstats.
 endif::git-format-patch[]
 
 ifndef::git-format-patch[]
 -p::
 -u::
+--patch::
 	Generate patch (see section on generating patches).
 	{git-diff? This is the default.}
 endif::git-format-patch[]
@@ -27,58 +29,133 @@
 -U<n>::
 --unified=<n>::
 	Generate diffs with <n> lines of context instead of
-	the usual three. Implies "-p".
+	the usual three.
+ifndef::git-format-patch[]
+	Implies `-p`.
+endif::git-format-patch[]
 
+ifndef::git-format-patch[]
 --raw::
 	Generate the raw format.
 	{git-diff-core? This is the default.}
+endif::git-format-patch[]
 
+ifndef::git-format-patch[]
 --patch-with-raw::
-	Synonym for "-p --raw".
+	Synonym for `-p --raw`.
+endif::git-format-patch[]
+
+--minimal::
+	Spend extra time to make sure the smallest possible
+	diff is produced.
 
 --patience::
 	Generate a diff using the "patience diff" algorithm.
 
---stat[=width[,name-width]]::
-	Generate a diffstat.  You can override the default
-	output width for 80-column terminal by "--stat=width".
-	The width of the filename part can be controlled by
-	giving another width to it separated by a comma.
+--histogram::
+	Generate a diff using the "histogram diff" algorithm.
+
+--stat[=<width>[,<name-width>[,<count>]]]::
+	Generate a diffstat. By default, as much space as necessary
+	will be used for the filename part, and the rest for the graph
+	part. Maximum width defaults to terminal width, or 80 columns
+	if not connected to a terminal, and can be overridden by
+	`<width>`. The width of the filename part can be limited by
+	giving another width `<name-width>` after a comma. The width
+	of the graph part can be limited by using
+	`--stat-graph-width=<width>` (affects all commands generating
+	a stat graph) or by setting `diff.statGraphWidth=<width>`
+	(does not affect `git format-patch`).
+	By giving a third parameter `<count>`, you can limit the
+	output to the first `<count>` lines, followed by `...` if
+	there are more.
++
+These parameters can also be set individually with `--stat-width=<width>`,
+`--stat-name-width=<name-width>` and `--stat-count=<count>`.
 
 --numstat::
-	Similar to \--stat, but shows number of added and
+	Similar to `--stat`, but shows number of added and
 	deleted lines in decimal notation and pathname without
 	abbreviation, to make it more machine friendly.  For
 	binary files, outputs two `-` instead of saying
 	`0 0`.
 
 --shortstat::
-	Output only the last line of the --stat format containing total
+	Output only the last line of the `--stat` format containing total
 	number of modified files, as well as number of added and deleted
 	lines.
 
---dirstat[=limit]::
-	Output the distribution of relative amount of changes (number of lines added or
-	removed) for each sub-directory. Directories with changes below
-	a cut-off percent (3% by default) are not shown. The cut-off percent
-	can be set with "--dirstat=limit". Changes in a child directory is not
-	counted for the parent directory, unless "--cumulative" is used.
-
---dirstat-by-file[=limit]::
-	Same as --dirstat, but counts changed files instead of lines.
+--dirstat[=<param1,param2,...>]::
+	Output the distribution of relative amount of changes for each
+	sub-directory. The behavior of `--dirstat` can be customized by
+	passing it a comma separated list of parameters.
+	The defaults are controlled by the `diff.dirstat` configuration
+	variable (see linkgit:git-config[1]).
+	The following parameters are available:
++
+--
+`changes`;;
+	Compute the dirstat numbers by counting the lines that have been
+	removed from the source, or added to the destination. This ignores
+	the amount of pure code movements within a file.  In other words,
+	rearranging lines in a file is not counted as much as other changes.
+	This is the default behavior when no parameter is given.
+`lines`;;
+	Compute the dirstat numbers by doing the regular line-based diff
+	analysis, and summing the removed/added line counts. (For binary
+	files, count 64-byte chunks instead, since binary files have no
+	natural concept of lines). This is a more expensive `--dirstat`
+	behavior than the `changes` behavior, but it does count rearranged
+	lines within a file as much as other changes. The resulting output
+	is consistent with what you get from the other `--*stat` options.
+`files`;;
+	Compute the dirstat numbers by counting the number of files changed.
+	Each changed file counts equally in the dirstat analysis. This is
+	the computationally cheapest `--dirstat` behavior, since it does
+	not have to look at the file contents at all.
+`cumulative`;;
+	Count changes in a child directory for the parent directory as well.
+	Note that when using `cumulative`, the sum of the percentages
+	reported may exceed 100%. The default (non-cumulative) behavior can
+	be specified with the `noncumulative` parameter.
+<limit>;;
+	An integer parameter specifies a cut-off percent (3% by default).
+	Directories contributing less than this percentage of the changes
+	are not shown in the output.
+--
++
+Example: The following will count changed files, while ignoring
+directories with less than 10% of the total amount of changed files,
+and accumulating child directory counts in the parent directories:
+`--dirstat=files,10,cumulative`.
 
 --summary::
 	Output a condensed summary of extended header information
 	such as creations, renames and mode changes.
 
+ifndef::git-format-patch[]
 --patch-with-stat::
-	Synonym for "-p --stat".
-	{git-format-patch? This is the default.}
+	Synonym for `-p --stat`.
+endif::git-format-patch[]
+
+ifndef::git-format-patch[]
 
 -z::
-	NUL-line termination on output.  This affects the --raw
-	output field terminator.  Also output from commands such
-	as "git-log" will be delimited with NUL between commits.
+ifdef::git-log[]
+	Separate the commits with NULs instead of with new newlines.
++
+Also, when `--raw` or `--numstat` has been given, do not munge
+pathnames and use NULs as output field terminators.
+endif::git-log[]
+ifndef::git-log[]
+	When `--raw`, `--numstat`, `--name-only` or `--name-status` has been
+	given, do not munge pathnames and use NULs as output field terminators.
+endif::git-log[]
++
+Without this option, each pathname output will have TAB, LF, double quotes,
+and backslash characters replaced with `\t`, `\n`, `\"`, and `\\`,
+respectively, and the pathname will be enclosed in double quotes if
+any of those replacements occurred.
 
 --name-only::
 	Show only names of changed files.
@@ -87,18 +164,63 @@
 	Show only names and status of changed files. See the description
 	of the `--diff-filter` option on what the status letters mean.
 
---color::
+--submodule[=<format>]::
+	Specify how differences in submodules are shown.  When `--submodule`
+	or `--submodule=log` is given, the 'log' format is used.  This format lists
+	the commits in the range like linkgit:git-submodule[1] `summary` does.
+	Omitting the `--submodule` option or specifying `--submodule=short`,
+	uses the 'short' format. This format just shows the names of the commits
+	at the beginning and end of the range.
+
+--color[=<when>]::
 	Show colored diff.
+	The value must be `always` (the default for `<when>`), `never`, or `auto`.
+	The default value is `never`.
+ifdef::git-diff[]
+	It can be changed by the `color.ui` and `color.diff`
+	configuration settings.
+endif::git-diff[]
 
 --no-color::
-	Turn off colored diff, even when the configuration file
-	gives the default to color output.
+	Turn off colored diff.
+ifdef::git-diff[]
+	This can be used to override configuration settings.
+endif::git-diff[]
+	It is the same as `--color=never`.
 
---color-words[=<regex>]::
-	Show colored word diff, i.e., color words which have changed.
-	By default, words are separated by whitespace.
+--word-diff[=<mode>]::
+	Show a word diff, using the <mode> to delimit changed words.
+	By default, words are delimited by whitespace; see
+	`--word-diff-regex` below.  The <mode> defaults to 'plain', and
+	must be one of:
 +
-When a <regex> is specified, every non-overlapping match of the
+--
+color::
+	Highlight changed words using only colors.  Implies `--color`.
+plain::
+	Show words as `[-removed-]` and `{+added+}`.  Makes no
+	attempts to escape the delimiters if they appear in the input,
+	so the output may be ambiguous.
+porcelain::
+	Use a special line-based format intended for script
+	consumption.  Added/removed/unchanged runs are printed in the
+	usual unified diff format, starting with a `+`/`-`/` `
+	character at the beginning of the line and extending to the
+	end of the line.  Newlines in the input are represented by a
+	tilde `~` on a line of its own.
+none::
+	Disable word diff again.
+--
++
+Note that despite the name of the first mode, color is used to
+highlight the changed parts in all modes if enabled.
+
+--word-diff-regex=<regex>::
+	Use <regex> to decide what a word is, instead of considering
+	runs of non-whitespace to be a word.  Also implies
+	`--word-diff` unless it was already enabled.
++
+Every non-overlapping match of the
 <regex> is considered a word.  Anything between these matches is
 considered whitespace and ignored(!) for the purposes of finding
 differences.  You may want to append `|[^[:space:]]` to your regular
@@ -111,15 +233,26 @@
 overrides any diff driver or configuration setting.  Diff drivers
 override configuration settings.
 
+--color-words[=<regex>]::
+	Equivalent to `--word-diff=color` plus (if a regex was
+	specified) `--word-diff-regex=<regex>`.
+endif::git-format-patch[]
+
 --no-renames::
 	Turn off rename detection, even when the configuration
 	file gives the default to do so.
 
+ifndef::git-format-patch[]
 --check::
-	Warn if changes introduce trailing whitespace
-	or an indent that uses a space before a tab. Exits with
-	non-zero status if problems are found. Not compatible with
-	--exit-code.
+	Warn if changes introduce whitespace errors.  What are
+	considered whitespace errors is controlled by `core.whitespace`
+	configuration.  By default, trailing whitespaces (including
+	lines that solely consist of whitespaces) and a space character
+	that is immediately followed by a tab character inside the
+	initial indent of the line are considered whitespace errors.
+	Exits with non-zero status if problems are found. Not compatible
+	with --exit-code.
+endif::git-format-patch[]
 
 --full-index::
 	Instead of the first handful of characters, show the full
@@ -127,37 +260,60 @@
 	line when generating patch format output.
 
 --binary::
-	In addition to --full-index, output "binary diff" that
-	can be applied with "git apply".
+	In addition to `--full-index`, output a binary diff that
+	can be applied with `git-apply`.
 
 --abbrev[=<n>]::
 	Instead of showing the full 40-byte hexadecimal object
 	name in diff-raw format output and diff-tree header
 	lines, show only a partial prefix.  This is
-	independent of --full-index option above, which controls
+	independent of the `--full-index` option above, which controls
 	the diff-patch output format.  Non default number of
-	digits can be specified with --abbrev=<n>.
+	digits can be specified with `--abbrev=<n>`.
 
--B::
-	Break complete rewrite changes into pairs of delete and create.
+-B[<n>][/<m>]::
+--break-rewrites[=[<n>][/<m>]]::
+	Break complete rewrite changes into pairs of delete and
+	create. This serves two purposes:
++
+It affects the way a change that amounts to a total rewrite of a file
+not as a series of deletion and insertion mixed together with a very
+few lines that happen to match textually as the context, but as a
+single deletion of everything old followed by a single insertion of
+everything new, and the number `m` controls this aspect of the -B
+option (defaults to 60%). `-B/70%` specifies that less than 30% of the
+original should remain in the result for git to consider it a total
+rewrite (i.e. otherwise the resulting patch will be a series of
+deletion and insertion mixed together with context lines).
++
+When used with -M, a totally-rewritten file is also considered as the
+source of a rename (usually -M only considers a file that disappeared
+as the source of a rename), and the number `n` controls this aspect of
+the -B option (defaults to 50%). `-B20%` specifies that a change with
+addition and deletion compared to 20% or more of the file's size are
+eligible for being picked up as a possible source of a rename to
+another file.
 
--M::
+-M[<n>]::
+--find-renames[=<n>]::
+ifndef::git-log[]
 	Detect renames.
+endif::git-log[]
+ifdef::git-log[]
+	If generating diffs, detect and report renames for each commit.
+	For following files across renames while traversing history, see
+	`--follow`.
+endif::git-log[]
+	If `n` is specified, it is a threshold on the similarity
+	index (i.e. amount of addition/deletions compared to the
+	file's size). For example, `-M90%` means git should consider a
+	delete/add pair to be a rename if more than 90% of the file
+	hasn't changed.
 
--C::
+-C[<n>]::
+--find-copies[=<n>]::
 	Detect copies as well as renames.  See also `--find-copies-harder`.
-
---diff-filter=[ACDMRTUXB*]::
-	Select only files that are Added (`A`), Copied (`C`),
-	Deleted (`D`), Modified (`M`), Renamed (`R`), have their
-	type (i.e. regular file, symlink, submodule, ...) changed (`T`),
-	are Unmerged (`U`), are
-	Unknown (`X`), or have had their pairing Broken (`B`).
-	Any combination of the filter characters may be used.
-	When `*` (All-or-none) is added to the combination, all
-	paths are selected if there is any file that matches
-	other criteria in the comparison; if there is no file
-	that matches other criteria, nothing is selected.
+	If `n` is specified, it has the same meaning as for `-M<n>`.
 
 --find-copies-harder::
 	For performance reasons, by default, `-C` option finds copies only
@@ -168,32 +324,64 @@
 	projects, so use it with caution.  Giving more than one
 	`-C` option has the same effect.
 
+-D::
+--irreversible-delete::
+	Omit the preimage for deletes, i.e. print only the header but not
+	the diff between the preimage and `/dev/null`. The resulting patch
+	is not meant to be applied with `patch` nor `git apply`; this is
+	solely for people who want to just concentrate on reviewing the
+	text after the change. In addition, the output obviously lack
+	enough information to apply such a patch in reverse, even manually,
+	hence the name of the option.
++
+When used together with `-B`, omit also the preimage in the deletion part
+of a delete/create pair.
+
 -l<num>::
-	-M and -C options require O(n^2) processing time where n
+	The `-M` and `-C` options require O(n^2) processing time where n
 	is the number of potential rename/copy targets.  This
 	option prevents rename/copy detection from running if
 	the number of rename/copy targets exceeds the specified
 	number.
 
+ifndef::git-format-patch[]
+--diff-filter=[(A|C|D|M|R|T|U|X|B)...[*]]::
+	Select only files that are Added (`A`), Copied (`C`),
+	Deleted (`D`), Modified (`M`), Renamed (`R`), have their
+	type (i.e. regular file, symlink, submodule, ...) changed (`T`),
+	are Unmerged (`U`), are
+	Unknown (`X`), or have had their pairing Broken (`B`).
+	Any combination of the filter characters (including none) can be used.
+	When `*` (All-or-none) is added to the combination, all
+	paths are selected if there is any file that matches
+	other criteria in the comparison; if there is no file
+	that matches other criteria, nothing is selected.
+
 -S<string>::
 	Look for differences that introduce or remove an instance of
 	<string>. Note that this is different than the string simply
 	appearing in diff output; see the 'pickaxe' entry in
 	linkgit:gitdiffcore[7] for more details.
 
+-G<regex>::
+	Look for differences whose added or removed line matches
+	the given <regex>.
+
 --pickaxe-all::
-	When -S finds a change, show all the changes in that
+	When `-S` or `-G` finds a change, show all the changes in that
 	changeset, not just the files that contain the change
 	in <string>.
 
 --pickaxe-regex::
 	Make the <string> not a plain string but an extended POSIX
 	regex to match.
+endif::git-format-patch[]
 
 -O<orderfile>::
 	Output the patch in the order specified in the
 	<orderfile>, which has one shell glob pattern per line.
 
+ifndef::git-format-patch[]
 -R::
 	Swap two inputs; that is, show differences from index or
 	on-disk file to tree contents.
@@ -205,6 +393,7 @@
 	not in a subdirectory (e.g. in a bare repository), you
 	can name which subdirectory to make the output relative
 	to by giving a <path> as an argument.
+endif::git-format-patch[]
 
 -a::
 --text::
@@ -229,13 +418,21 @@
 	Show the context between diff hunks, up to the specified number
 	of lines, thereby fusing hunks that are close to each other.
 
+-W::
+--function-context::
+	Show whole surrounding functions of changes.
+
+ifndef::git-format-patch[]
+ifndef::git-log[]
 --exit-code::
 	Make the program exit with codes similar to diff(1).
 	That is, it exits with 1 if there were differences and
 	0 means no differences.
 
 --quiet::
-	Disable all output of the program. Implies --exit-code.
+	Disable all output of the program. Implies `--exit-code`.
+endif::git-log[]
+endif::git-format-patch[]
 
 --ext-diff::
 	Allow an external diff helper to be executed. If you set an
@@ -245,8 +442,29 @@
 --no-ext-diff::
 	Disallow external diff drivers.
 
---ignore-submodules::
-	Ignore changes to submodules in the diff generation.
+--textconv::
+--no-textconv::
+	Allow (or disallow) external text conversion filters to be run
+	when comparing binary files. See linkgit:gitattributes[5] for
+	details. Because textconv filters are typically a one-way
+	conversion, the resulting diff is suitable for human
+	consumption, but cannot be applied. For this reason, textconv
+	filters are enabled by default only for linkgit:git-diff[1] and
+	linkgit:git-log[1], but not for linkgit:git-format-patch[1] or
+	diff plumbing commands.
+
+--ignore-submodules[=<when>]::
+	Ignore changes to submodules in the diff generation. <when> can be
+	either "none", "untracked", "dirty" or "all", which is the default
+	Using "none" will consider the submodule modified when it either contains
+	untracked or modified files or its HEAD differs from the commit recorded
+	in the superproject and can be used to override any settings of the
+	'ignore' option in linkgit:git-config[1] or linkgit:gitmodules[5]. When
+	"untracked" is used submodules are not considered dirty when they only
+	contain untracked content (but they are still scanned for modified
+	content). Using "dirty" ignores all changes to the work tree of submodules,
+	only changes to the commits stored in the superproject are shown (this was
+	the behavior until 1.7.0). Using "all" hides all changes to submodules.
 
 --src-prefix=<prefix>::
 	Show the given source prefix instead of "a/".
diff --git a/Documentation/docbook.xsl b/Documentation/docbook.xsl
index 9a6912c..da8b05b 100644
--- a/Documentation/docbook.xsl
+++ b/Documentation/docbook.xsl
@@ -1,5 +1,8 @@
 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 		version='1.0'>
  <xsl:import href="http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl"/>
- <xsl:output method="html" encoding="UTF-8" indent="no" />
+ <xsl:output method="html"
+     encoding="UTF-8" indent="no"
+     doctype-public="-//W3C//DTD HTML 4.01//EN"
+     doctype-system="http://www.w3.org/TR/html4/strict.dtd" />
 </xsl:stylesheet>
diff --git a/Documentation/everyday.txt b/Documentation/everyday.txt
index 9310b65..048337b 100644
--- a/Documentation/everyday.txt
+++ b/Documentation/everyday.txt
@@ -1,13 +1,8 @@
 Everyday GIT With 20 Commands Or So
 ===================================
 
-<<Basic Repository>> commands are needed by people who have a
-repository --- that is everybody, because every working tree of
-git is a repository.
-
-In addition, <<Individual Developer (Standalone)>> commands are
-essential for anybody who makes a commit, even for somebody who
-works alone.
+<<Individual Developer (Standalone)>> commands are essential for
+anybody who makes a commit, even for somebody who works alone.
 
 If you work with other people, you will need commands listed in
 the <<Individual Developer (Participant)>> section as well.
@@ -20,46 +15,6 @@
 of git repositories.
 
 
-Basic Repository[[Basic Repository]]
-------------------------------------
-
-Everybody uses these commands to maintain git repositories.
-
-  * linkgit:git-init[1] or linkgit:git-clone[1] to create a
-    new repository.
-
-  * linkgit:git-fsck[1] to check the repository for errors.
-
-  * linkgit:git-gc[1] to do common housekeeping tasks such as
-    repack and prune.
-
-Examples
-~~~~~~~~
-
-Check health and remove cruft.::
-+
-------------
-$ git fsck <1>
-$ git count-objects <2>
-$ git gc <3>
-------------
-+
-<1> running without `\--full` is usually cheap and assures the
-repository health reasonably well.
-<2> check how many loose objects there are and how much
-disk space is wasted by not repacking.
-<3> repacks the local repository and performs other housekeeping tasks.
-
-Repack a small project into single pack.::
-+
-------------
-$ git gc <1>
-------------
-+
-<1> pack all the objects reachable from the refs into one pack,
-then remove the other packs.
-
-
 Individual Developer (Standalone)[[Individual Developer (Standalone)]]
 ----------------------------------------------------------------------
 
@@ -67,6 +22,8 @@
 other people, and works alone in a single repository, using the
 following commands.
 
+  * linkgit:git-init[1] to create a new repository.
+
   * linkgit:git-show-branch[1] to see where you are.
 
   * linkgit:git-log[1] to see what happened.
@@ -141,8 +98,8 @@
 <9> switch to the master branch.
 <10> merge a topic branch into your master branch.
 <11> review commit logs; other forms to limit output can be
-combined and include `\--max-count=10` (show 10 commits),
-`\--until=2005-12-10`, etc.
+combined and include `--max-count=10` (show 10 commits),
+`--until=2005-12-10`, etc.
 <12> view only the changes that touch what's in `curses/`
 directory, since `v2.43` tag.
 
@@ -223,12 +180,12 @@
 machine.
 <2> clone sets these configuration variables by default.
 It arranges `git pull` to fetch and store the branches of mothership
-machine to local `remotes/origin/*` tracking branches.
+machine to local `remotes/origin/*` remote-tracking branches.
 <3> arrange `git push` to push local `master` branch to
 `remotes/satellite/master` branch of the mothership machine.
 <4> push will stash our work away on `remotes/satellite/master`
-tracking branch on the mothership machine.  You could use this as
-a back-up method.
+remote-tracking branch on the mothership machine.  You could use this
+as a back-up method.
 <5> on mothership machine, merge the work done on the satellite
 machine into the master branch.
 
diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt
index d313795..39d326a 100644
--- a/Documentation/fetch-options.txt
+++ b/Documentation/fetch-options.txt
@@ -1,11 +1,5 @@
--q::
---quiet::
-	Pass --quiet to git-fetch-pack and silence any other internally
-	used programs.
-
--v::
---verbose::
-	Be verbose.
+--all::
+	Fetch all remotes.
 
 -a::
 --append::
@@ -13,20 +7,39 @@
 	existing contents of `.git/FETCH_HEAD`.  Without this
 	option old data in `.git/FETCH_HEAD` will be overwritten.
 
---upload-pack <upload-pack>::
-	When given, and the repository to fetch from is handled
-	by 'git-fetch-pack', '--exec=<upload-pack>' is passed to
-	the command to specify non-default path for the command
-	run on the other end.
+--depth=<depth>::
+	Deepen the history of a 'shallow' repository created by
+	`git clone` with `--depth=<depth>` option (see linkgit:git-clone[1])
+	by the specified number of commits.
+
+ifndef::git-pull[]
+--dry-run::
+	Show what would be done, without making any changes.
+endif::git-pull[]
 
 -f::
 --force::
-	When 'git-fetch' is used with `<rbranch>:<lbranch>`
+	When 'git fetch' is used with `<rbranch>:<lbranch>`
 	refspec, it refuses to update the local branch
 	`<lbranch>` unless the remote branch `<rbranch>` it
 	fetches is a descendant of `<lbranch>`.  This option
 	overrides that check.
 
+-k::
+--keep::
+	Keep downloaded pack.
+
+ifndef::git-pull[]
+--multiple::
+	Allow several <repository> and <group> arguments to be
+	specified. No <refspec>s may be specified.
+
+-p::
+--prune::
+	After fetching, remove any remote-tracking branches which
+	no longer exist	on the remote.
+endif::git-pull[]
+
 ifdef::git-pull[]
 --no-tags::
 endif::git-pull[]
@@ -36,8 +49,11 @@
 endif::git-pull[]
 	By default, tags that point at objects that are downloaded
 	from the remote repository are fetched and stored locally.
-	This option disables this automatic tag following.
+	This option disables this automatic tag following. The default
+	behavior for a remote may be specified with the remote.<name>.tagopt
+	setting. See linkgit:git-config[1].
 
+ifndef::git-pull[]
 -t::
 --tags::
 	Most of the tags are fetched automatically as branch
@@ -45,22 +61,68 @@
 	objects reachable from the branch heads that are being
 	tracked will not be fetched by this mechanism.  This
 	flag lets all tags and their associated objects be
-	downloaded.
+	downloaded. The default behavior for a remote may be
+	specified with the remote.<name>.tagopt setting. See
+	linkgit:git-config[1].
 
--k::
---keep::
-	Keep downloaded pack.
+--recurse-submodules[=yes|on-demand|no]::
+	This option controls if and under what conditions new commits of
+	populated submodules should be fetched too. It can be used as a
+	boolean option to completely disable recursion when set to 'no' or to
+	unconditionally recurse into all populated submodules when set to
+	'yes', which is the default when this option is used without any
+	value. Use 'on-demand' to only recurse into a populated submodule
+	when the superproject retrieves a commit that updates the submodule's
+	reference to a commit that isn't already in the local submodule
+	clone.
+
+--no-recurse-submodules::
+	Disable recursive fetching of submodules (this has the same effect as
+	using the '--recurse-submodules=no' option).
+
+--submodule-prefix=<path>::
+	Prepend <path> to paths printed in informative messages
+	such as "Fetching submodule foo".  This option is used
+	internally when recursing over submodules.
+
+--recurse-submodules-default=[yes|on-demand]::
+	This option is used internally to temporarily provide a
+	non-negative default value for the --recurse-submodules
+	option.  All other methods of configuring fetch's submodule
+	recursion (such as settings in linkgit:gitmodules[5] and
+	linkgit:git-config[1]) override this option, as does
+	specifying --[no-]recurse-submodules directly.
+endif::git-pull[]
 
 -u::
 --update-head-ok::
-	By default 'git-fetch' refuses to update the head which
+	By default 'git fetch' refuses to update the head which
 	corresponds to the current branch.  This flag disables the
-	check.  This is purely for the internal use for 'git-pull'
-	to communicate with 'git-fetch', and unless you are
+	check.  This is purely for the internal use for 'git pull'
+	to communicate with 'git fetch', and unless you are
 	implementing your own Porcelain you are not supposed to
 	use it.
 
---depth=<depth>::
-	Deepen the history of a 'shallow' repository created by
-	`git clone` with `--depth=<depth>` option (see linkgit:git-clone[1])
-	by the specified number of commits.
+--upload-pack <upload-pack>::
+	When given, and the repository to fetch from is handled
+	by 'git fetch-pack', '--exec=<upload-pack>' is passed to
+	the command to specify non-default path for the command
+	run on the other end.
+
+ifndef::git-pull[]
+-q::
+--quiet::
+	Pass --quiet to git-fetch-pack and silence any other internally
+	used git commands. Progress is not reported to the standard error
+	stream.
+
+-v::
+--verbose::
+	Be verbose.
+endif::git-pull[]
+
+--progress::
+	Progress status is reported on the standard error stream
+	by default when it is attached to a terminal, unless -q
+	is specified. This flag forces progress status even if the
+	standard error stream is not directed to a terminal.
diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt
index ab1943c..9c1d395 100644
--- a/Documentation/git-add.txt
+++ b/Documentation/git-add.txt
@@ -10,32 +10,37 @@
 [verse]
 'git add' [-n] [-v] [--force | -f] [--interactive | -i] [--patch | -p]
 	  [--edit | -e] [--all | [--update | -u]] [--intent-to-add | -N]
-	  [--refresh] [--ignore-errors] [--] <filepattern>...
+	  [--refresh] [--ignore-errors] [--ignore-missing] [--]
+	  [<filepattern>...]
 
 DESCRIPTION
 -----------
-This command adds the current content of new or modified files to the
-index, thus staging that content for inclusion in the next commit.
+This command updates the index using the current content found in
+the working tree, to prepare the content staged for the next commit.
+It typically adds the current content of existing paths as a whole,
+but with some options it can also be used to add content with
+only part of the changes made to the working tree files applied, or
+remove paths that do not exist in the working tree anymore.
 
 The "index" holds a snapshot of the content of the working tree, and it
 is this snapshot that is taken as the contents of the next commit.  Thus
 after making any changes to the working directory, and before running
-the commit command, you must use the 'add' command to add any new or
+the commit command, you must use the `add` command to add any new or
 modified files to the index.
 
 This command can be performed multiple times before a commit.  It only
 adds the content of the specified file(s) at the time the add command is
 run; if you want subsequent changes included in the next commit, then
-you must run 'git add' again to add the new content to the index.
+you must run `git add` again to add the new content to the index.
 
-The 'git status' command can be used to obtain a summary of which
+The `git status` command can be used to obtain a summary of which
 files have changes that are staged for the next commit.
 
-The 'git add' command will not add ignored files by default.  If any
-ignored files were explicitly specified on the command line, 'git add'
+The `git add` command will not add ignored files by default.  If any
+ignored files were explicitly specified on the command line, `git add`
 will fail with a list of ignored files.  Ignored files reached by
 directory recursion or filename globbing performed by Git (quote your
-globs before the shell) will be silently ignored.  The 'add' command can
+globs before the shell) will be silently ignored.  The 'git add' command can
 be used to add ignored files with the `-f` (force) option.
 
 Please see linkgit:git-commit[1] for alternative ways to add content to a
@@ -53,7 +58,8 @@
 
 -n::
 --dry-run::
-        Don't actually add the file(s), just show if they exist.
+	Don't actually add the file(s), just show if they exist and/or will
+	be ignored.
 
 -v::
 --verbose::
@@ -72,43 +78,53 @@
 
 -p::
 --patch::
-	Similar to Interactive mode but the initial command loop is
-	bypassed and the 'patch' subcommand is invoked using each of
-	the specified filepatterns before exiting.
+	Interactively choose hunks of patch between the index and the
+	work tree and add them to the index. This gives the user a chance
+	to review the difference before adding modified contents to the
+	index.
++
+This effectively runs `add --interactive`, but bypasses the
+initial command menu and directly jumps to the `patch` subcommand.
+See ``Interactive mode'' for details.
 
 -e, \--edit::
 	Open the diff vs. the index in an editor and let the user
 	edit it.  After the editor was closed, adjust the hunk headers
 	and apply the patch to the index.
 +
-*NOTE*: Obviously, if you change anything else than the first character
-on lines beginning with a space or a minus, the patch will no longer
-apply.
+The intent of this option is to pick and choose lines of the patch to
+apply, or even to modify the contents of lines to be staged. This can be
+quicker and more flexible than using the interactive hunk selector.
+However, it is easy to confuse oneself and create a patch that does not
+apply to the index. See EDITING PATCHES below.
 
 -u::
 --update::
-	Update only files that git already knows about, staging modified
-	content for commit and marking deleted files for removal. This
-	is similar
-	to what "git commit -a" does in preparation for making a commit,
-	except that the update is limited to paths specified on the
-	command line. If no paths are specified, all tracked files in the
-	current directory and its subdirectories are updated.
+	Only match <filepattern> against already tracked files in
+	the index rather than the working tree. That means that it
+	will never stage new files, but that it will stage modified
+	new contents of tracked files and that it will remove files
+	from the index if the corresponding files in the working tree
+	have been removed.
++
+If no <filepattern> is given, default to "."; in other words,
+update all tracked files in the current directory and its
+subdirectories.
 
 -A::
 --all::
-	Update files that git already knows about (same as '\--update')
-	and add all untracked files that are not ignored by '.gitignore'
-	mechanism.
-
+	Like `-u`, but match <filepattern> against files in the
+	working tree in addition to the index. That means that it
+	will find new files as well as staging modified content and
+	removing files that are no longer in the working tree.
 
 -N::
 --intent-to-add::
 	Record only the fact that the path will be added later. An entry
 	for the path is placed in the index with no content. This is
 	useful for, among other things, showing the unstaged content of
-	such files with 'git diff' and committing them with 'git commit
-	-a'.
+	such files with `git diff` and committing them with `git commit
+	-a`.
 
 --refresh::
 	Don't add the file(s), but only refresh their stat()
@@ -118,6 +134,14 @@
 	If some files could not be added because of errors indexing
 	them, do not abort the operation, but continue adding the
 	others. The command shall still exit with non-zero status.
+	The configuration variable `add.ignoreErrors` can be set to
+	true to make this the default behaviour.
+
+--ignore-missing::
+	This option can only be used together with --dry-run. By using
+	this option the user can check if any of the given files would
+	be ignored, no matter if they are already present in the work
+	tree or not.
 
 \--::
 	This option can be used to separate command-line options from
@@ -128,7 +152,7 @@
 Configuration
 -------------
 
-The optional configuration variable 'core.excludesfile' indicates a path to a
+The optional configuration variable `core.excludesfile` indicates a path to a
 file containing patterns of file names to exclude from git-add, similar to
 $GIT_DIR/info/exclude.  Patterns in the exclude file are used in addition to
 those in info/exclude.  See linkgit:gitrepository-layout[5].
@@ -137,14 +161,14 @@
 EXAMPLES
 --------
 
-* Adds content from all `\*.txt` files under `Documentation` directory
+* Adds content from all `*.txt` files under `Documentation` directory
 and its subdirectories:
 +
 ------------
-$ git add Documentation/\\*.txt
+$ git add Documentation/\*.txt
 ------------
 +
-Note that the asterisk `\*` is quoted from the shell in this
+Note that the asterisk `*` is quoted from the shell in this
 example; this lets the command include the files from
 subdirectories of `Documentation/` directory.
 
@@ -176,7 +200,7 @@
     What now> 1
 ------------
 
-You also could say "s" or "sta" or "status" above as long as the
+You also could say `s` or `sta` or `status` above as long as the
 choice is unique.
 
 The main command loop has 6 subcommands (plus help and quit).
@@ -184,9 +208,9 @@
 status::
 
    This shows the change between HEAD and index (i.e. what will be
-   committed if you say "git commit"), and between index and
+   committed if you say `git commit`), and between index and
    working tree files (i.e. what you could stage further before
-   "git commit" using "git-add") for each path.  A sample output
+   `git commit` using `git add`) for each path.  A sample output
    looks like this:
 +
 ------------
@@ -200,7 +224,7 @@
 difference between indexed copy and the working tree
 version (if the working tree version were also different,
 'binary' would have been shown in place of 'nothing').  The
-other file, git-add--interactive.perl, has 403 lines added
+other file, git-add{litdd}interactive.perl, has 403 lines added
 and 35 lines deleted if you commit what is in the index, but
 working tree file has further modifications (one addition and
 one deletion).
@@ -250,13 +274,14 @@
   This lets you choose one path out of a 'status' like selection.
   After choosing the path, it presents the diff between the index
   and the working tree file and asks you if you want to stage
-  the change of each hunk.  You can say:
+  the change of each hunk.  You can select one of the following
+  options and type return:
 
        y - stage this hunk
        n - do not stage this hunk
-       q - quit, do not stage this hunk nor any of the remaining ones
-       a - stage this and all the remaining hunks in the file
-       d - do not stage this hunk nor any of the remaining hunks in the file
+       q - quit; do not stage this hunk nor any of the remaining ones
+       a - stage this hunk and all later hunks in the file
+       d - do not stage this hunk nor any of the later hunks in the file
        g - select a hunk to go to
        / - search for a hunk matching the given regex
        j - leave this hunk undecided, see next undecided hunk
@@ -269,12 +294,87 @@
 +
 After deciding the fate for all hunks, if there is any hunk
 that was chosen, the index is updated with the selected hunks.
++
+You can omit having to type return here, by setting the configuration
+variable `interactive.singlekey` to `true`.
 
 diff::
 
   This lets you review what will be committed (i.e. between
   HEAD and index).
 
+
+EDITING PATCHES
+---------------
+
+Invoking `git add -e` or selecting `e` from the interactive hunk
+selector will open a patch in your editor; after the editor exits, the
+result is applied to the index. You are free to make arbitrary changes
+to the patch, but note that some changes may have confusing results, or
+even result in a patch that cannot be applied.  If you want to abort the
+operation entirely (i.e., stage nothing new in the index), simply delete
+all lines of the patch. The list below describes some common things you
+may see in a patch, and which editing operations make sense on them.
+
+--
+added content::
+
+Added content is represented by lines beginning with "{plus}". You can
+prevent staging any addition lines by deleting them.
+
+removed content::
+
+Removed content is represented by lines beginning with "-". You can
+prevent staging their removal by converting the "-" to a " " (space).
+
+modified content::
+
+Modified content is represented by "-" lines (removing the old content)
+followed by "{plus}" lines (adding the replacement content). You can
+prevent staging the modification by converting "-" lines to " ", and
+removing "{plus}" lines. Beware that modifying only half of the pair is
+likely to introduce confusing changes to the index.
+--
+
+There are also more complex operations that can be performed. But beware
+that because the patch is applied only to the index and not the working
+tree, the working tree will appear to "undo" the change in the index.
+For example, introducing a new line into the index that is in neither
+the HEAD nor the working tree will stage the new line for commit, but
+the line will appear to be reverted in the working tree.
+
+Avoid using these constructs, or do so with extreme caution.
+
+--
+removing untouched content::
+
+Content which does not differ between the index and working tree may be
+shown on context lines, beginning with a " " (space).  You can stage
+context lines for removal by converting the space to a "-". The
+resulting working tree file will appear to re-add the content.
+
+modifying existing content::
+
+One can also modify context lines by staging them for removal (by
+converting " " to "-") and adding a "{plus}" line with the new content.
+Similarly, one can modify "{plus}" lines for existing additions or
+modifications. In all cases, the new modification will appear reverted
+in the working tree.
+
+new content::
+
+You may also add new content that does not exist in the patch; simply
+add new lines, each starting with "{plus}". The addition will appear
+reverted in the working tree.
+--
+
+There are also several operations which should be avoided entirely, as
+they will make the patch impossible to apply:
+
+* adding context (" ") or removal ("-") lines
+* deleting context or removal lines
+* modifying the contents of context or removal lines
+
 SEE ALSO
 --------
 linkgit:git-status[1]
@@ -284,14 +384,6 @@
 linkgit:git-commit[1]
 linkgit:git-update-index[1]
 
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 32e689b..19d57a8 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -9,13 +9,14 @@
 SYNOPSIS
 --------
 [verse]
-'git am' [--signoff] [--keep] [--utf8 | --no-utf8]
+'git am' [--signoff] [--keep] [--keep-cr | --no-keep-cr] [--utf8 | --no-utf8]
 	 [--3way] [--interactive] [--committer-date-is-author-date]
-	 [--ignore-date]
+	 [--ignore-date] [--ignore-space-change | --ignore-whitespace]
 	 [--whitespace=<option>] [-C<n>] [-p<n>] [--directory=<dir>]
-	 [--reject] [-q | --quiet]
-	 [<mbox> | <Maildir>...]
-'git am' (--skip | --resolved | --abort)
+	 [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
+	 [--scissors | --no-scissors]
+	 [(<mbox> | <Maildir>)...]
+'git am' (--continue | --skip | --abort)
 
 DESCRIPTION
 -----------
@@ -25,7 +26,7 @@
 
 OPTIONS
 -------
-<mbox>|<Maildir>...::
+(<mbox>|<Maildir>)...::
 	The list of mailbox files to read patches from. If you do not
 	supply this argument, the command reads from the standard input.
 	If you supply directories, they will be treated as Maildirs.
@@ -37,7 +38,25 @@
 
 -k::
 --keep::
-	Pass `-k` flag to 'git-mailinfo' (see linkgit:git-mailinfo[1]).
+	Pass `-k` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]).
+
+--keep-non-patch::
+	Pass `-b` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]).
+
+--keep-cr::
+--no-keep-cr::
+	With `--keep-cr`, call 'git mailsplit' (see linkgit:git-mailsplit[1])
+	with the same option, to prevent it from stripping CR at the end of
+	lines. `am.keepcr` configuration variable can be used to specify the
+	default behaviour.  `--no-keep-cr` is useful to override `am.keepcr`.
+
+-c::
+--scissors::
+	Remove everything in body before a scissors line (see
+	linkgit:git-mailinfo[1]).
+
+--no-scissors::
+	Ignore scissors lines (see linkgit:git-mailinfo[1]).
 
 -q::
 --quiet::
@@ -45,7 +64,7 @@
 
 -u::
 --utf8::
-	Pass `-u` flag to 'git-mailinfo' (see linkgit:git-mailinfo[1]).
+	Pass `-u` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]).
 	The proposed commit log message taken from the e-mail
 	is re-coded into UTF-8 encoding (configuration variable
 	`i18n.commitencoding` can be used to specify project's
@@ -55,7 +74,7 @@
 default.   You can use `--no-utf8` to override this.
 
 --no-utf8::
-	Pass `-n` flag to 'git-mailinfo' (see
+	Pass `-n` flag to 'git mailinfo' (see
 	linkgit:git-mailinfo[1]).
 
 -3::
@@ -65,12 +84,17 @@
 	it is supposed to apply to and we have those blobs
 	available locally.
 
+--ignore-date::
+--ignore-space-change::
+--ignore-whitespace::
 --whitespace=<option>::
 -C<n>::
 -p<n>::
 --directory=<dir>::
+--exclude=<path>::
+--include=<path>::
 --reject::
-	These flags are passed to the 'git-apply' (see linkgit:git-apply[1])
+	These flags are passed to the 'git apply' (see linkgit:git-apply[1])
 	program that applies
 	the patch.
 
@@ -96,6 +120,7 @@
 	Skip the current patch.  This is only meaningful when
 	restarting an aborted patch.
 
+--continue::
 -r::
 --resolved::
 	After a patch failure (e.g. attempting to apply
@@ -110,7 +135,7 @@
 	to the screen before exiting.  This overrides the
 	standard message informing you to use `--resolved`
 	or `--skip` to handle the failure.  This is solely
-	for internal use between 'git-rebase' and 'git-am'.
+	for internal use between 'git rebase' and 'git am'.
 
 --abort::
 	Restore the original branch and abort the patching operation.
@@ -125,10 +150,8 @@
 The "Subject: " line is supposed to concisely describe what the
 commit is about in one line of text.
 
-"From: " and "Subject: " lines starting the body (the rest of the
-message after the blank line terminating the RFC2822 headers)
-override the respective commit author name and title values taken
-from the headers.
+"From: " and "Subject: " lines starting the body override the respective
+commit author name and title values taken from the headers.
 
 The commit message is formed by the title taken from the
 "Subject: ", a blank line and the body of the message up to
@@ -156,9 +179,9 @@
   the index file to bring it into a state that the patch should
   have produced.  Then run the command with the '--resolved' option.
 
-The command refuses to process new mailboxes while the `.git/rebase-apply`
-directory exists, so if you decide to start over from scratch,
-run `rm -f -r .git/rebase-apply` before running the command with mailbox
+The command refuses to process new mailboxes until the current
+operation is finished, so if you decide to start over from scratch,
+run `git am --abort` before running the command with mailbox
 names.
 
 Before any patches are applied, ORIG_HEAD is set to the tip of the
@@ -172,15 +195,6 @@
 --------
 linkgit:git-apply[1].
 
-
-Author
-------
-Written by Junio C Hamano <gitster@pobox.com>
-
-Documentation
---------------
-Documentation by Petr Baudis, Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-annotate.txt b/Documentation/git-annotate.txt
index 0590eec..05fd482 100644
--- a/Documentation/git-annotate.txt
+++ b/Documentation/git-annotate.txt
@@ -7,6 +7,7 @@
 
 SYNOPSIS
 --------
+[verse]
 'git annotate' [options] file [revision]
 
 DESCRIPTION
@@ -27,10 +28,6 @@
 --------
 linkgit:git-blame[1]
 
-AUTHOR
-------
-Written by Ryan Anderson <ryan@michonline.com>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt
index 735374d..634b84e 100644
--- a/Documentation/git-apply.txt
+++ b/Documentation/git-apply.txt
@@ -3,24 +3,32 @@
 
 NAME
 ----
-git-apply - Apply a patch on a git index file and/or a working tree
+git-apply - Apply a patch to files and/or to the index
 
 
 SYNOPSIS
 --------
 [verse]
-'git apply' [--stat] [--numstat] [--summary] [--check] [--index]
+'git apply' [--stat] [--numstat] [--summary] [--check] [--index] [--3way]
 	  [--apply] [--no-add] [--build-fake-ancestor=<file>] [-R | --reverse]
 	  [--allow-binary-replacement | --binary] [--reject] [-z]
-	  [-pNUM] [-CNUM] [--inaccurate-eof] [--recount] [--cached]
-	  [--whitespace=<nowarn|warn|fix|error|error-all>]
-	  [--exclude=PATH] [--include=PATH] [--directory=<root>]
+	  [-p<n>] [-C<n>] [--inaccurate-eof] [--recount] [--cached]
+	  [--ignore-space-change | --ignore-whitespace ]
+	  [--whitespace=(nowarn|warn|fix|error|error-all)]
+	  [--exclude=<path>] [--include=<path>] [--directory=<root>]
 	  [--verbose] [<patch>...]
 
 DESCRIPTION
 -----------
-Reads supplied 'diff' output and applies it on a git index file
-and a work tree.
+Reads the supplied diff output (i.e. "a patch") and applies it to files.
+With the `--index` option the patch is also applied to the index, and
+with the `--cached` option the patch is only applied to the index.
+Without these options, the command applies the patch only to files,
+and does not require them to be in a git repository.
+
+This command applies the patch but does not create a commit.  Use
+linkgit:git-am[1] to create commits from patches generated by
+linkgit:git-format-patch[1] and/or received by email.
 
 OPTIONS
 -------
@@ -33,7 +41,7 @@
 	input.  Turns off "apply".
 
 --numstat::
-	Similar to \--stat, but shows the number of added and
+	Similar to `--stat`, but shows the number of added and
 	deleted lines in decimal notation and the pathname without
 	abbreviation, to make it more machine friendly.  For
 	binary files, outputs two `-` instead of saying
@@ -47,25 +55,34 @@
 
 --check::
 	Instead of applying the patch, see if the patch is
-	applicable to the current work tree and/or the index
+	applicable to the current working tree and/or the index
 	file and detects errors.  Turns off "apply".
 
 --index::
-	When --check is in effect, or when applying the patch
+	When `--check` is in effect, or when applying the patch
 	(which is the default when none of the options that
 	disables it is in effect), make sure the patch is
 	applicable to what the current index file records.  If
-	the file to be patched in the work tree is not
+	the file to be patched in the working tree is not
 	up-to-date, it is flagged as an error.  This flag also
 	causes the index file to be updated.
 
 --cached::
 	Apply a patch without touching the working tree. Instead take the
 	cached data, apply the patch, and store the result in the index
-	without using the working tree. This implies '--index'.
+	without using the working tree. This implies `--index`.
+
+-3::
+--3way::
+	When the patch does not apply cleanly, fall back on 3-way merge if
+	the patch records the identity of blobs it is supposed to apply to,
+	and we have those blobs available locally, possibly leaving the
+	conflict markers in the files in the working tree for the user to
+	resolve.  This option implies the `--index` option, and is incompatible
+	with the `--reject` and the `--cached` options.
 
 --build-fake-ancestor=<file>::
-	Newer 'git-diff' output has embedded 'index information'
+	Newer 'git diff' output has embedded 'index information'
 	for each blob to help identify the original version that
 	the patch applies to.  When this flag is given, and if
 	the original versions of the blobs are available locally,
@@ -79,18 +96,20 @@
 	Apply the patch in reverse.
 
 --reject::
-	For atomicity, 'git-apply' by default fails the whole patch and
+	For atomicity, 'git apply' by default fails the whole patch and
 	does not touch the working tree when some of the hunks
 	do not apply.  This option makes it apply
 	the parts of the patch that are applicable, and leave the
 	rejected hunks in corresponding *.rej files.
 
 -z::
-	When showing the index information, do not munge paths,
-	but use NUL terminated machine readable format.  Without
-	this flag, the pathnames output will have TAB, LF, and
-	backslash characters replaced with `\t`, `\n`, and `\\`,
-	respectively.
+	When `--numstat` has been given, do not munge pathnames,
+	but use a NUL-terminated machine-readable format.
++
+Without this option, each pathname output will have TAB, LF, double quotes,
+and backslash characters replaced with `\t`, `\n`, `\"`, and `\\`,
+respectively, and the pathname will be enclosed in double quotes if
+any of those replacements occurred.
 
 -p<n>::
 	Remove <n> leading slashes from traditional diff paths. The
@@ -103,18 +122,18 @@
 	ever ignored.
 
 --unidiff-zero::
-	By default, 'git-apply' expects that the patch being
+	By default, 'git apply' expects that the patch being
 	applied is a unified diff with at least one line of context.
 	This provides good safety measures, but breaks down when
-	applying a diff generated with --unified=0. To bypass these
-	checks use '--unidiff-zero'.
+	applying a diff generated with `--unified=0`. To bypass these
+	checks use `--unidiff-zero`.
 +
 Note, for the reasons stated above usage of context-free patches is
 discouraged.
 
 --apply::
 	If you use any of the options marked "Turns off
-	'apply'" above, 'git-apply' reads and outputs the
+	'apply'" above, 'git apply' reads and outputs the
 	requested information without actually applying the
 	patch.  Give this flag after those flags to also apply
 	the patch.
@@ -143,12 +162,20 @@
 	be useful when importing patchsets, where you want to include certain
 	files or directories.
 +
-When --exclude and --include patterns are used, they are examined in the
+When `--exclude` and `--include` patterns are used, they are examined in the
 order they appear on the command line, and the first match determines if a
 patch to each path is used.  A patch to a path that does not match any
 include/exclude pattern is used by default if there is no include pattern
 on the command line, and ignored if there is any include pattern.
 
+--ignore-space-change::
+--ignore-whitespace::
+	When applying a patch, ignore changes in whitespace in context
+	lines if necessary.
+	Context lines will preserve their whitespace, and they will not
+	undergo whitespace fixing regardless of the value of the
+	`--whitespace` option. New lines will still be fixed, though.
+
 --whitespace=<action>::
 	When applying a patch, detect a new or modified line that has
 	whitespace errors.  What are considered whitespace errors is
@@ -205,32 +232,32 @@
 Configuration
 -------------
 
+apply.ignorewhitespace::
+	Set to 'change' if you want changes in whitespace to be ignored by default.
+	Set to one of: no, none, never, false if you want changes in
+	whitespace to be significant.
 apply.whitespace::
 	When no `--whitespace` flag is given from the command
 	line, this configuration item is used as the default.
 
 Submodules
 ----------
-If the patch contains any changes to submodules then 'git-apply'
+If the patch contains any changes to submodules then 'git apply'
 treats these changes as follows.
 
-If --index is specified (explicitly or implicitly), then the submodule
+If `--index` is specified (explicitly or implicitly), then the submodule
 commits must match the index exactly for the patch to apply.  If any
 of the submodules are checked-out, then these check-outs are completely
 ignored, i.e., they are not required to be up-to-date or clean and they
 are not updated.
 
-If --index is not specified, then the submodule commits in the patch
+If `--index` is not specified, then the submodule commits in the patch
 are ignored and only the absence or presence of the corresponding
 subdirectory is checked and (if possible) updated.
 
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by Junio C Hamano
+SEE ALSO
+--------
+linkgit:git-am[1].
 
 GIT
 ---
diff --git a/Documentation/git-archimport.txt b/Documentation/git-archimport.txt
index c7a6e3e..f4504ba 100644
--- a/Documentation/git-archimport.txt
+++ b/Documentation/git-archimport.txt
@@ -29,22 +29,22 @@
 edit your <archive/branch> parameters to define clearly the scope of the
 import.
 
-'git-archimport' uses `tla` extensively in the background to access the
+'git archimport' uses `tla` extensively in the background to access the
 Arch repository.
 Make sure you have a recent version of `tla` available in the path. `tla` must
-know about the repositories you pass to 'git-archimport'.
+know about the repositories you pass to 'git archimport'.
 
-For the initial import, 'git-archimport' expects to find itself in an empty
+For the initial import, 'git archimport' expects to find itself in an empty
 directory. To follow the development of a project that uses Arch, rerun
-'git-archimport' with the same parameters as the initial import to perform
+'git archimport' with the same parameters as the initial import to perform
 incremental imports.
 
-While 'git-archimport' will try to create sensible branch names for the
+While 'git archimport' will try to create sensible branch names for the
 archives that it imports, it is also possible to specify git branch names
 manually.  To do so, write a git branch name after each <archive/branch>
 parameter, separated by a colon.  This way, you can shorten the Arch
 branch names and convert Arch jargon to git jargon, for example mapping a
-"PROJECT--devo--VERSION" branch to "master".
+"PROJECT{litdd}devo{litdd}VERSION" branch to "master".
 
 Associating multiple Arch branches to one git branch is possible; the
 result will make the most sense only if no commits are made to the first
@@ -84,9 +84,9 @@
 
 -o::
 	Use this for compatibility with old-style branch names used by
-	earlier versions of 'git-archimport'.  Old-style branch names
-	were category--branch, whereas new-style branch names are
-	archive,category--branch--version.  In both cases, names given
+	earlier versions of 'git archimport'.  Old-style branch names
+	were category{litdd}branch, whereas new-style branch names are
+	archive,category{litdd}branch{litdd}version.  In both cases, names given
 	on the command-line will override the automatically-generated
 	ones.
 
@@ -107,14 +107,6 @@
 	Archive/branch identifier in a format that `tla log` understands.
 
 
-Author
-------
-Written by Martin Langhoff <martin@catalyst.net.nz>.
-
-Documentation
---------------
-Documentation by Junio C Hamano, Martin Langhoff and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-archive.txt b/Documentation/git-archive.txt
index bc132c8..59d73e5 100644
--- a/Documentation/git-archive.txt
+++ b/Documentation/git-archive.txt
@@ -9,10 +9,10 @@
 SYNOPSIS
 --------
 [verse]
-'git archive' --format=<fmt> [--list] [--prefix=<prefix>/] [<extra>]
-	      [--output=<file>] [--worktree-attributes]
+'git archive' [--format=<fmt>] [--list] [--prefix=<prefix>/] [<extra>]
+	      [-o | --output=<file>] [--worktree-attributes]
 	      [--remote=<repo> [--exec=<git-upload-archive>]] <tree-ish>
-	      [path...]
+	      [<path>...]
 
 DESCRIPTION
 -----------
@@ -21,21 +21,24 @@
 output.  If <prefix> is specified it is
 prepended to the filenames in the archive.
 
-'git-archive' behaves differently when given a tree ID versus when
+'git archive' behaves differently when given a tree ID versus when
 given a commit ID or tag ID.  In the first case the current time is
 used as the modification time of each file in the archive.  In the latter
 case the commit time as recorded in the referenced commit object is
 used instead.  Additionally the commit ID is stored in a global
 extended pax header if the tar format is used; it can be extracted
-using 'git-get-tar-commit-id'. In ZIP files it is stored as a file
+using 'git get-tar-commit-id'. In ZIP files it is stored as a file
 comment.
 
 OPTIONS
 -------
 
 --format=<fmt>::
-	Format of the resulting archive: 'tar' or 'zip'.  The default
-	is 'tar'.
+	Format of the resulting archive: 'tar' or 'zip'. If this option
+	is not given, and the output file is specified, the format is
+	inferred from the filename if possible (e.g. writing to "foo.zip"
+	makes the output to be in the zip format). Otherwise the output
+	format is `tar`.
 
 -l::
 --list::
@@ -48,6 +51,7 @@
 --prefix=<prefix>/::
 	Prepend <prefix>/ to each filename in the archive.
 
+-o <file>::
 --output=<file>::
 	Write the archive to <file> instead of stdout.
 
@@ -69,9 +73,10 @@
 <tree-ish>::
 	The tree or commit to produce an archive for.
 
-path::
-	If one or more paths are specified, include only these in the
-	archive, otherwise include all files and subdirectories.
+<path>::
+	Without an optional path parameter, all files and subdirectories
+	of the current working directory are included in the archive.
+	If one or more paths are specified, only these are included.
 
 BACKEND EXTRA OPTIONS
 ---------------------
@@ -93,7 +98,27 @@
 	tar archive entries.  The default is 0002, which turns off the
 	world write bit.  The special value "user" indicates that the
 	archiving user's umask will be used instead.  See umask(2) for
-	details.
+	details.  If `--remote` is used then only the configuration of
+	the remote repository takes effect.
+
+tar.<format>.command::
+	This variable specifies a shell command through which the tar
+	output generated by `git archive` should be piped. The command
+	is executed using the shell with the generated tar file on its
+	standard input, and should produce the final output on its
+	standard output. Any compression-level options will be passed
+	to the command (e.g., "-9"). An output file with the same
+	extension as `<format>` will be use this format if no other
+	format is given.
++
+The "tar.gz" and "tgz" formats are defined automatically and default to
+`gzip -cn`. You may override them with custom commands.
+
+tar.<format>.remote::
+	If true, enable `<format>` for use by remote clients via
+	linkgit:git-upload-archive[1]. Defaults to false for
+	user-defined formats, but true for the "tar.gz" and "tgz"
+	formats.
 
 ATTRIBUTES
 ----------
@@ -107,41 +132,61 @@
 	expand several placeholders when adding this file to an archive.
 	See linkgit:gitattributes[5] for details.
 
+Note that attributes are by default taken from the `.gitattributes` files
+in the tree that is being archived.  If you want to tweak the way the
+output is generated after the fact (e.g. you committed without adding an
+appropriate export-ignore in its `.gitattributes`), adjust the checked out
+`.gitattributes` file as necessary and use `--worktree-attributes`
+option.  Alternatively you can keep necessary attributes that should apply
+while archiving any tree in your `$GIT_DIR/info/attributes` file.
+
 EXAMPLES
 --------
-git archive --format=tar --prefix=junk/ HEAD | (cd /var/tmp/ && tar xf -)::
+`git archive --format=tar --prefix=junk/ HEAD | (cd /var/tmp/ && tar xf -)`::
 
 	Create a tar archive that contains the contents of the
 	latest commit on the current branch, and extract it in the
 	`/var/tmp/junk` directory.
 
-git archive --format=tar --prefix=git-1.4.0/ v1.4.0 | gzip >git-1.4.0.tar.gz::
+`git archive --format=tar --prefix=git-1.4.0/ v1.4.0 | gzip >git-1.4.0.tar.gz`::
 
 	Create a compressed tarball for v1.4.0 release.
 
-git archive --format=tar --prefix=git-1.4.0/ v1.4.0{caret}\{tree\} | gzip >git-1.4.0.tar.gz::
+`git archive --format=tar.gz --prefix=git-1.4.0/ v1.4.0 >git-1.4.0.tar.gz`::
+
+	Same as above, but using the builtin tar.gz handling.
+
+`git archive --prefix=git-1.4.0/ -o git-1.4.0.tar.gz v1.4.0`::
+
+	Same as above, but the format is inferred from the output file.
+
+`git archive --format=tar --prefix=git-1.4.0/ v1.4.0^{tree} | gzip >git-1.4.0.tar.gz`::
 
 	Create a compressed tarball for v1.4.0 release, but without a
 	global extended pax header.
 
-git archive --format=zip --prefix=git-docs/ HEAD:Documentation/ > git-1.4.0-docs.zip::
+`git archive --format=zip --prefix=git-docs/ HEAD:Documentation/ > git-1.4.0-docs.zip`::
 
 	Put everything in the current head's Documentation/ directory
 	into 'git-1.4.0-docs.zip', with the prefix 'git-docs/'.
 
+`git archive -o latest.zip HEAD`::
+
+	Create a Zip archive that contains the contents of the latest
+	commit on the current branch. Note that the output format is
+	inferred by the extension of the output file.
+
+`git config tar.tar.xz.command "xz -c"`::
+
+	Configure a "tar.xz" format for making LZMA-compressed tarfiles.
+	You can use it specifying `--format=tar.xz`, or by creating an
+	output file like `-o foo.tar.xz`.
+
 
 SEE ALSO
 --------
 linkgit:gitattributes[5]
 
-Author
-------
-Written by Franck Bui-Huu and Rene Scharfe.
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-bisect-lk2009.txt b/Documentation/git-bisect-lk2009.txt
new file mode 100644
index 0000000..8a2ba37
--- /dev/null
+++ b/Documentation/git-bisect-lk2009.txt
@@ -0,0 +1,1358 @@
+Fighting regressions with git bisect
+====================================
+:Author: Christian Couder
+:Email: chriscool@tuxfamily.org
+:Date: 2009/11/08
+
+Abstract
+--------
+
+"git bisect" enables software users and developers to easily find the
+commit that introduced a regression. We show why it is important to
+have good tools to fight regressions. We describe how "git bisect"
+works from the outside and the algorithms it uses inside. Then we
+explain how to take advantage of "git bisect" to improve current
+practices. And we discuss how "git bisect" could improve in the
+future.
+
+
+Introduction to "git bisect"
+----------------------------
+
+Git is a Distributed Version Control system (DVCS) created by Linus
+Torvalds and maintained by Junio Hamano.
+
+In Git like in many other Version Control Systems (VCS), the different
+states of the data that is managed by the system are called
+commits. And, as VCS are mostly used to manage software source code,
+sometimes "interesting" changes of behavior in the software are
+introduced in some commits.
+
+In fact people are specially interested in commits that introduce a
+"bad" behavior, called a bug or a regression. They are interested in
+these commits because a commit (hopefully) contains a very small set
+of source code changes. And it's much easier to understand and
+properly fix a problem when you only need to check a very small set of
+changes, than when you don't know where look in the first place.
+
+So to help people find commits that introduce a "bad" behavior, the
+"git bisect" set of commands was invented. And it follows of course
+that in "git bisect" parlance, commits where the "interesting
+behavior" is present are called "bad" commits, while other commits are
+called "good" commits. And a commit that introduce the behavior we are
+interested in is called a "first bad commit". Note that there could be
+more than one "first bad commit" in the commit space we are searching.
+
+So "git bisect" is designed to help find a "first bad commit". And to
+be as efficient as possible, it tries to perform a binary search.
+
+
+Fighting regressions overview
+-----------------------------
+
+Regressions: a big problem
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Regressions are a big problem in the software industry. But it's
+difficult to put some real numbers behind that claim.
+
+There are some numbers about bugs in general, like a NIST study in
+2002 <<1>> that said:
+
+_____________
+Software bugs, or errors, are so prevalent and so detrimental that
+they cost the U.S. economy an estimated $59.5 billion annually, or
+about 0.6 percent of the gross domestic product, according to a newly
+released study commissioned by the Department of Commerce's National
+Institute of Standards and Technology (NIST). At the national level,
+over half of the costs are borne by software users and the remainder
+by software developers/vendors.  The study also found that, although
+all errors cannot be removed, more than a third of these costs, or an
+estimated $22.2 billion, could be eliminated by an improved testing
+infrastructure that enables earlier and more effective identification
+and removal of software defects. These are the savings associated with
+finding an increased percentage (but not 100 percent) of errors closer
+to the development stages in which they are introduced. Currently,
+over half of all errors are not found until "downstream" in the
+development process or during post-sale software use.
+_____________
+
+And then:
+
+_____________
+Software developers already spend approximately 80 percent of
+development costs on identifying and correcting defects, and yet few
+products of any type other than software are shipped with such high
+levels of errors.
+_____________
+
+Eventually the conclusion started with:
+
+_____________
+The path to higher software quality is significantly improved software
+testing.
+_____________
+
+There are other estimates saying that 80% of the cost related to
+software is about maintenance <<2>>.
+
+Though, according to Wikipedia <<3>>:
+
+_____________
+A common perception of maintenance is that it is merely fixing
+bugs. However, studies and surveys over the years have indicated that
+the majority, over 80%, of the maintenance effort is used for
+non-corrective actions (Pigosky 1997). This perception is perpetuated
+by users submitting problem reports that in reality are functionality
+enhancements to the system.
+_____________
+
+But we can guess that improving on existing software is very costly
+because you have to watch out for regressions. At least this would
+make the above studies consistent among themselves.
+
+Of course some kind of software is developed, then used during some
+time without being improved on much, and then finally thrown away. In
+this case, of course, regressions may not be a big problem. But on the
+other hand, there is a lot of big software that is continually
+developed and maintained during years or even tens of years by a lot
+of people. And as there are often many people who depend (sometimes
+critically) on such software, regressions are a really big problem.
+
+One such software is the linux kernel. And if we look at the linux
+kernel, we can see that a lot of time and effort is spent to fight
+regressions. The release cycle start with a 2 weeks long merge
+window. Then the first release candidate (rc) version is tagged. And
+after that about 7 or 8 more rc versions will appear with around one
+week between each of them, before the final release.
+
+The time between the first rc release and the final release is
+supposed to be used to test rc versions and fight bugs and especially
+regressions. And this time is more than 80% of the release cycle
+time. But this is not the end of the fight yet, as of course it
+continues after the release.
+
+And then this is what Ingo Molnar (a well known linux kernel
+developer) says about his use of git bisect:
+
+_____________
+I most actively use it during the merge window (when a lot of trees
+get merged upstream and when the influx of bugs is the highest) - and
+yes, there have been cases that i used it multiple times a day. My
+average is roughly once a day.
+_____________
+
+So regressions are fought all the time by developers, and indeed it is
+well known that bugs should be fixed as soon as possible, so as soon
+as they are found. That's why it is interesting to have good tools for
+this purpose.
+
+Other tools to fight regressions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+So what are the tools used to fight regressions? They are nearly the
+same as those used to fight regular bugs. The only specific tools are
+test suites and tools similar as "git bisect".
+
+Test suites are very nice. But when they are used alone, they are
+supposed to be used so that all the tests are checked after each
+commit. This means that they are not very efficient, because many
+tests are run for no interesting result, and they suffer from
+combinational explosion.
+
+In fact the problem is that big software often has many different
+configuration options and that each test case should pass for each
+configuration after each commit. So if you have for each release: N
+configurations, M commits and T test cases, you should perform:
+
+-------------
+N * M * T tests
+-------------
+
+where N, M and T are all growing with the size your software.
+
+So very soon it will not be possible to completely test everything.
+
+And if some bugs slip through your test suite, then you can add a test
+to your test suite. But if you want to use your new improved test
+suite to find where the bug slipped in, then you will either have to
+emulate a bisection process or you will perhaps bluntly test each
+commit backward starting from the "bad" commit you have which may be
+very wasteful.
+
+"git bisect" overview
+---------------------
+
+Starting a bisection
+~~~~~~~~~~~~~~~~~~~~
+
+The first "git bisect" subcommand to use is "git bisect start" to
+start the search. Then bounds must be set to limit the commit
+space. This is done usually by giving one "bad" and at least one
+"good" commit. They can be passed in the initial call to "git bisect
+start" like this:
+
+-------------
+$ git bisect start [BAD [GOOD...]]
+-------------
+
+or they can be set using:
+
+-------------
+$ git bisect bad [COMMIT]
+-------------
+
+and:
+
+-------------
+$ git bisect good [COMMIT...]
+-------------
+
+where BAD, GOOD and COMMIT are all names that can be resolved to a
+commit.
+
+Then "git bisect" will checkout a commit of its choosing and ask the
+user to test it, like this:
+
+-------------
+$ git bisect start v2.6.27 v2.6.25
+Bisecting: 10928 revisions left to test after this (roughly 14 steps)
+[2ec65f8b89ea003c27ff7723525a2ee335a2b393] x86: clean up using max_low_pfn on 32-bit
+-------------
+
+Note that the example that we will use is really a toy example, we
+will be looking for the first commit that has a version like
+"2.6.26-something", that is the commit that has a "SUBLEVEL = 26" line
+in the top level Makefile. This is a toy example because there are
+better ways to find this commit with git than using "git bisect" (for
+example "git blame" or "git log -S<string>").
+
+Driving a bisection manually
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+At this point there are basically 2 ways to drive the search. It can
+be driven manually by the user or it can be driven automatically by a
+script or a command.
+
+If the user is driving it, then at each step of the search, the user
+will have to test the current commit and say if it is "good" or "bad"
+using the "git bisect good" or "git bisect bad" commands respectively
+that have been described above. For example:
+
+-------------
+$ git bisect bad
+Bisecting: 5480 revisions left to test after this (roughly 13 steps)
+[66c0b394f08fd89236515c1c84485ea712a157be] KVM: kill file->f_count abuse in kvm
+-------------
+
+And after a few more steps like that, "git bisect" will eventually
+find a first bad commit:
+
+-------------
+$ git bisect bad
+2ddcca36c8bcfa251724fe342c8327451988be0d is the first bad commit
+commit 2ddcca36c8bcfa251724fe342c8327451988be0d
+Author: Linus Torvalds <torvalds@linux-foundation.org>
+Date:   Sat May 3 11:59:44 2008 -0700
+
+    Linux 2.6.26-rc1
+
+:100644 100644 5cf8258195331a4dbdddff08b8d68642638eea57 4492984efc09ab72ff6219a7bc21fb6a957c4cd5 M      Makefile
+-------------
+
+At this point we can see what the commit does, check it out (if it's
+not already checked out) or tinker with it, for example:
+
+-------------
+$ git show HEAD
+commit 2ddcca36c8bcfa251724fe342c8327451988be0d
+Author: Linus Torvalds <torvalds@linux-foundation.org>
+Date:   Sat May 3 11:59:44 2008 -0700
+
+    Linux 2.6.26-rc1
+
+diff --git a/Makefile b/Makefile
+index 5cf8258..4492984 100644
+--- a/Makefile
++++ b/Makefile
+@@ -1,7 +1,7 @@
+ VERSION = 2
+ PATCHLEVEL = 6
+-SUBLEVEL = 25
+-EXTRAVERSION =
++SUBLEVEL = 26
++EXTRAVERSION = -rc1
+ NAME = Funky Weasel is Jiggy wit it
+
+ # *DOCUMENTATION*
+-------------
+
+And when we are finished we can use "git bisect reset" to go back to
+the branch we were in before we started bisecting:
+
+-------------
+$ git bisect reset
+Checking out files: 100% (21549/21549), done.
+Previous HEAD position was 2ddcca3... Linux 2.6.26-rc1
+Switched to branch 'master'
+-------------
+
+Driving a bisection automatically
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The other way to drive the bisection process is to tell "git bisect"
+to launch a script or command at each bisection step to know if the
+current commit is "good" or "bad". To do that, we use the "git bisect
+run" command. For example:
+
+-------------
+$ git bisect start v2.6.27 v2.6.25
+Bisecting: 10928 revisions left to test after this (roughly 14 steps)
+[2ec65f8b89ea003c27ff7723525a2ee335a2b393] x86: clean up using max_low_pfn on 32-bit
+$
+$ git bisect run grep '^SUBLEVEL = 25' Makefile
+running grep ^SUBLEVEL = 25 Makefile
+Bisecting: 5480 revisions left to test after this (roughly 13 steps)
+[66c0b394f08fd89236515c1c84485ea712a157be] KVM: kill file->f_count abuse in kvm
+running grep ^SUBLEVEL = 25 Makefile
+SUBLEVEL = 25
+Bisecting: 2740 revisions left to test after this (roughly 12 steps)
+[671294719628f1671faefd4882764886f8ad08cb] V4L/DVB(7879): Adding cx18 Support for mxl5005s
+...
+...
+running grep ^SUBLEVEL = 25 Makefile
+Bisecting: 0 revisions left to test after this (roughly 0 steps)
+[2ddcca36c8bcfa251724fe342c8327451988be0d] Linux 2.6.26-rc1
+running grep ^SUBLEVEL = 25 Makefile
+2ddcca36c8bcfa251724fe342c8327451988be0d is the first bad commit
+commit 2ddcca36c8bcfa251724fe342c8327451988be0d
+Author: Linus Torvalds <torvalds@linux-foundation.org>
+Date:   Sat May 3 11:59:44 2008 -0700
+
+    Linux 2.6.26-rc1
+
+:100644 100644 5cf8258195331a4dbdddff08b8d68642638eea57 4492984efc09ab72ff6219a7bc21fb6a957c4cd5 M      Makefile
+bisect run success
+-------------
+
+In this example, we passed "grep '^SUBLEVEL = 25' Makefile" as
+parameter to "git bisect run". This means that at each step, the grep
+command we passed will be launched. And if it exits with code 0 (that
+means success) then git bisect will mark the current state as
+"good". If it exits with code 1 (or any code between 1 and 127
+included, except the special code 125), then the current state will be
+marked as "bad".
+
+Exit code between 128 and 255 are special to "git bisect run". They
+make it stop immediately the bisection process. This is useful for
+example if the command passed takes too long to complete, because you
+can kill it with a signal and it will stop the bisection process.
+
+It can also be useful in scripts passed to "git bisect run" to "exit
+255" if some very abnormal situation is detected.
+
+Avoiding untestable commits
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Sometimes it happens that the current state cannot be tested, for
+example if it does not compile because there was a bug preventing it
+at that time. This is what the special exit code 125 is for. It tells
+"git bisect run" that the current commit should be marked as
+untestable and that another one should be chosen and checked out.
+
+If the bisection process is driven manually, you can use "git bisect
+skip" to do the same thing. (In fact the special exit code 125 makes
+"git bisect run" use "git bisect skip" in the background.)
+
+Or if you want more control, you can inspect the current state using
+for example "git bisect visualize". It will launch gitk (or "git log"
+if the DISPLAY environment variable is not set) to help you find a
+better bisection point.
+
+Either way, if you have a string of untestable commits, it might
+happen that the regression you are looking for has been introduced by
+one of these untestable commits. In this case it's not possible to
+tell for sure which commit introduced the regression.
+
+So if you used "git bisect skip" (or the run script exited with
+special code 125) you could get a result like this:
+
+-------------
+There are only 'skip'ped commits left to test.
+The first bad commit could be any of:
+15722f2fa328eaba97022898a305ffc8172db6b1
+78e86cf3e850bd755bb71831f42e200626fbd1e0
+e15b73ad3db9b48d7d1ade32f8cd23a751fe0ace
+070eab2303024706f2924822bfec8b9847e4ac1b
+We cannot bisect more!
+-------------
+
+Saving a log and replaying it
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you want to show other people your bisection process, you can get a
+log using for example:
+
+-------------
+$ git bisect log > bisect_log.txt
+-------------
+
+And it is possible to replay it using:
+
+-------------
+$ git bisect replay bisect_log.txt
+-------------
+
+
+"git bisect" details
+--------------------
+
+Bisection algorithm
+~~~~~~~~~~~~~~~~~~~
+
+As the Git commits form a directed acyclic graph (DAG), finding the
+best bisection commit to test at each step is not so simple. Anyway
+Linus found and implemented a "truly stupid" algorithm, later improved
+by Junio Hamano, that works quite well.
+
+So the algorithm used by "git bisect" to find the best bisection
+commit when there are no skipped commits is the following:
+
+1) keep only the commits that:
+
+a) are ancestor of the "bad" commit (including the "bad" commit itself),
+b) are not ancestor of a "good" commit (excluding the "good" commits).
+
+This means that we get rid of the uninteresting commits in the DAG.
+
+For example if we start with a graph like this:
+
+-------------
+G-Y-G-W-W-W-X-X-X-X
+	   \ /
+	    W-W-B
+	   /
+Y---G-W---W
+ \ /   \
+Y-Y     X-X-X-X
+
+-> time goes this way ->
+-------------
+
+where B is the "bad" commit, "G" are "good" commits and W, X, and Y
+are other commits, we will get the following graph after this first
+step:
+
+-------------
+W-W-W
+     \
+      W-W-B
+     /
+W---W
+-------------
+
+So only the W and B commits will be kept. Because commits X and Y will
+have been removed by rules a) and b) respectively, and because commits
+G are removed by rule b) too.
+
+Note for git users, that it is equivalent as keeping only the commit
+given by:
+
+-------------
+git rev-list BAD --not GOOD1 GOOD2...
+-------------
+
+Also note that we don't require the commits that are kept to be
+descendants of a "good" commit. So in the following example, commits W
+and Z will be kept:
+
+-------------
+G-W-W-W-B
+   /
+Z-Z
+-------------
+
+2) starting from the "good" ends of the graph, associate to each
+commit the number of ancestors it has plus one
+
+For example with the following graph where H is the "bad" commit and A
+and D are some parents of some "good" commits:
+
+-------------
+A-B-C
+     \
+      F-G-H
+     /
+D---E
+-------------
+
+this will give:
+
+-------------
+1 2 3
+A-B-C
+     \6 7 8
+      F-G-H
+1   2/
+D---E
+-------------
+
+3) associate to each commit: min(X, N - X)
+
+where X is the value associated to the commit in step 2) and N is the
+total number of commits in the graph.
+
+In the above example we have N = 8, so this will give:
+
+-------------
+1 2 3
+A-B-C
+     \2 1 0
+      F-G-H
+1   2/
+D---E
+-------------
+
+4) the best bisection point is the commit with the highest associated
+number
+
+So in the above example the best bisection point is commit C.
+
+5) note that some shortcuts are implemented to speed up the algorithm
+
+As we know N from the beginning, we know that min(X, N - X) can't be
+greater than N/2. So during steps 2) and 3), if we would associate N/2
+to a commit, then we know this is the best bisection point. So in this
+case we can just stop processing any other commit and return the
+current commit.
+
+Bisection algorithm debugging
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+For any commit graph, you can see the number associated with each
+commit using "git rev-list --bisect-all".
+
+For example, for the above graph, a command like:
+
+-------------
+$ git rev-list --bisect-all BAD --not GOOD1 GOOD2
+-------------
+
+would output something like:
+
+-------------
+e15b73ad3db9b48d7d1ade32f8cd23a751fe0ace (dist=3)
+15722f2fa328eaba97022898a305ffc8172db6b1 (dist=2)
+78e86cf3e850bd755bb71831f42e200626fbd1e0 (dist=2)
+a1939d9a142de972094af4dde9a544e577ddef0e (dist=2)
+070eab2303024706f2924822bfec8b9847e4ac1b (dist=1)
+a3864d4f32a3bf5ed177ddef598490a08760b70d (dist=1)
+a41baa717dd74f1180abf55e9341bc7a0bb9d556 (dist=1)
+9e622a6dad403b71c40979743bb9d5be17b16bd6 (dist=0)
+-------------
+
+Bisection algorithm discussed
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+First let's define "best bisection point". We will say that a commit X
+is a best bisection point or a best bisection commit if knowing its
+state ("good" or "bad") gives as much information as possible whether
+the state of the commit happens to be "good" or "bad".
+
+This means that the best bisection commits are the commits where the
+following function is maximum:
+
+-------------
+f(X) = min(information_if_good(X), information_if_bad(X))
+-------------
+
+where information_if_good(X) is the information we get if X is good
+and information_if_bad(X) is the information we get if X is bad.
+
+Now we will suppose that there is only one "first bad commit". This
+means that all its descendants are "bad" and all the other commits are
+"good". And we will suppose that all commits have an equal probability
+of being good or bad, or of being the first bad commit, so knowing the
+state of c commits gives always the same amount of information
+wherever these c commits are on the graph and whatever c is. (So we
+suppose that these commits being for example on a branch or near a
+good or a bad commit does not give more or less information).
+
+Let's also suppose that we have a cleaned up graph like one after step
+1) in the bisection algorithm above. This means that we can measure
+the information we get in terms of number of commit we can remove from
+the graph..
+
+And let's take a commit X in the graph.
+
+If X is found to be "good", then we know that its ancestors are all
+"good", so we want to say that:
+
+-------------
+information_if_good(X) = number_of_ancestors(X)  (TRUE)
+-------------
+
+And this is true because at step 1) b) we remove the ancestors of the
+"good" commits.
+
+If X is found to be "bad", then we know that its descendants are all
+"bad", so we want to say that:
+
+-------------
+information_if_bad(X) = number_of_descendants(X)  (WRONG)
+-------------
+
+But this is wrong because at step 1) a) we keep only the ancestors of
+the bad commit. So we get more information when a commit is marked as
+"bad", because we also know that the ancestors of the previous "bad"
+commit that are not ancestors of the new "bad" commit are not the
+first bad commit. We don't know if they are good or bad, but we know
+that they are not the first bad commit because they are not ancestor
+of the new "bad" commit.
+
+So when a commit is marked as "bad" we know we can remove all the
+commits in the graph except those that are ancestors of the new "bad"
+commit. This means that:
+
+-------------
+information_if_bad(X) = N - number_of_ancestors(X)  (TRUE)
+-------------
+
+where N is the number of commits in the (cleaned up) graph.
+
+So in the end this means that to find the best bisection commits we
+should maximize the function:
+
+-------------
+f(X) = min(number_of_ancestors(X), N - number_of_ancestors(X))
+-------------
+
+And this is nice because at step 2) we compute number_of_ancestors(X)
+and so at step 3) we compute f(X).
+
+Let's take the following graph as an example:
+
+-------------
+	    G-H-I-J
+	   /       \
+A-B-C-D-E-F         O
+	   \       /
+	    K-L-M-N
+-------------
+
+If we compute the following non optimal function on it:
+
+-------------
+g(X) = min(number_of_ancestors(X), number_of_descendants(X))
+-------------
+
+we get:
+
+-------------
+	    4 3 2 1
+	    G-H-I-J
+1 2 3 4 5 6/       \0
+A-B-C-D-E-F         O
+	   \       /
+	    K-L-M-N
+	    4 3 2 1
+-------------
+
+but with the algorithm used by git bisect we get:
+
+-------------
+	    7 7 6 5
+	    G-H-I-J
+1 2 3 4 5 6/       \0
+A-B-C-D-E-F         O
+	   \       /
+	    K-L-M-N
+	    7 7 6 5
+-------------
+
+So we chose G, H, K or L as the best bisection point, which is better
+than F. Because if for example L is bad, then we will know not only
+that L, M and N are bad but also that G, H, I and J are not the first
+bad commit (since we suppose that there is only one first bad commit
+and it must be an ancestor of L).
+
+So the current algorithm seems to be the best possible given what we
+initially supposed.
+
+Skip algorithm
+~~~~~~~~~~~~~~
+
+When some commits have been skipped (using "git bisect skip"), then
+the bisection algorithm is the same for step 1) to 3). But then we use
+roughly the following steps:
+
+6) sort the commit by decreasing associated value
+
+7) if the first commit has not been skipped, we can return it and stop
+here
+
+8) otherwise filter out all the skipped commits in the sorted list
+
+9) use a pseudo random number generator (PRNG) to generate a random
+number between 0 and 1
+
+10) multiply this random number with its square root to bias it toward
+0
+
+11) multiply the result by the number of commits in the filtered list
+to get an index into this list
+
+12) return the commit at the computed index
+
+Skip algorithm discussed
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+After step 7) (in the skip algorithm), we could check if the second
+commit has been skipped and return it if it is not the case. And in
+fact that was the algorithm we used from when "git bisect skip" was
+developed in git version 1.5.4 (released on February 1st 2008) until
+git version 1.6.4 (released July 29th 2009).
+
+But Ingo Molnar and H. Peter Anvin (another well known linux kernel
+developer) both complained that sometimes the best bisection points
+all happened to be in an area where all the commits are
+untestable. And in this case the user was asked to test many
+untestable commits, which could be very inefficient.
+
+Indeed untestable commits are often untestable because a breakage was
+introduced at one time, and that breakage was fixed only after many
+other commits were introduced.
+
+This breakage is of course most of the time unrelated to the breakage
+we are trying to locate in the commit graph. But it prevents us to
+know if the interesting "bad behavior" is present or not.
+
+So it is a fact that commits near an untestable commit have a high
+probability of being untestable themselves. And the best bisection
+commits are often found together too (due to the bisection algorithm).
+
+This is why it is a bad idea to just chose the next best unskipped
+bisection commit when the first one has been skipped.
+
+We found that most commits on the graph may give quite a lot of
+information when they are tested. And the commits that will not on
+average give a lot of information are the one near the good and bad
+commits.
+
+So using a PRNG with a bias to favor commits away from the good and
+bad commits looked like a good choice.
+
+One obvious improvement to this algorithm would be to look for a
+commit that has an associated value near the one of the best bisection
+commit, and that is on another branch, before using the PRNG. Because
+if such a commit exists, then it is not very likely to be untestable
+too, so it will probably give more information than a nearly randomly
+chosen one.
+
+Checking merge bases
+~~~~~~~~~~~~~~~~~~~~
+
+There is another tweak in the bisection algorithm that has not been
+described in the "bisection algorithm" above.
+
+We supposed in the previous examples that the "good" commits were
+ancestors of the "bad" commit. But this is not a requirement of "git
+bisect".
+
+Of course the "bad" commit cannot be an ancestor of a "good" commit,
+because the ancestors of the good commits are supposed to be
+"good". And all the "good" commits must be related to the bad commit.
+They cannot be on a branch that has no link with the branch of the
+"bad" commit. But it is possible for a good commit to be related to a
+bad commit and yet not be neither one of its ancestor nor one of its
+descendants.
+
+For example, there can be a "main" branch, and a "dev" branch that was
+forked of the main branch at a commit named "D" like this:
+
+-------------
+A-B-C-D-E-F-G  <--main
+       \
+	H-I-J  <--dev
+-------------
+
+The commit "D" is called a "merge base" for branch "main" and "dev"
+because it's the best common ancestor for these branches for a merge.
+
+Now let's suppose that commit J is bad and commit G is good and that
+we apply the bisection algorithm like it has been previously
+described.
+
+As described in step 1) b) of the bisection algorithm, we remove all
+the ancestors of the good commits because they are supposed to be good
+too.
+
+So we would be left with only:
+
+-------------
+H-I-J
+-------------
+
+But what happens if the first bad commit is "B" and if it has been
+fixed in the "main" branch by commit "F"?
+
+The result of such a bisection would be that we would find that H is
+the first bad commit, when in fact it's B. So that would be wrong!
+
+And yes it can happen in practice that people working on one branch
+are not aware that people working on another branch fixed a bug! It
+could also happen that F fixed more than one bug or that it is a
+revert of some big development effort that was not ready to be
+released.
+
+In fact development teams often maintain both a development branch and
+a maintenance branch, and it would be quite easy for them if "git
+bisect" just worked when they want to bisect a regression on the
+development branch that is not on the maintenance branch. They should
+be able to start bisecting using:
+
+-------------
+$ git bisect start dev main
+-------------
+
+To enable that additional nice feature, when a bisection is started
+and when some good commits are not ancestors of the bad commit, we
+first compute the merge bases between the bad and the good commits and
+we chose these merge bases as the first commits that will be checked
+out and tested.
+
+If it happens that one merge base is bad, then the bisection process
+is stopped with a message like:
+
+-------------
+The merge base BBBBBB is bad.
+This means the bug has been fixed between BBBBBB and [GGGGGG,...].
+-------------
+
+where BBBBBB is the sha1 hash of the bad merge base and [GGGGGG,...]
+is a comma separated list of the sha1 of the good commits.
+
+If some of the merge bases are skipped, then the bisection process
+continues, but the following message is printed for each skipped merge
+base:
+
+-------------
+Warning: the merge base between BBBBBB and [GGGGGG,...] must be skipped.
+So we cannot be sure the first bad commit is between MMMMMM and BBBBBB.
+We continue anyway.
+-------------
+
+where BBBBBB is the sha1 hash of the bad commit, MMMMMM is the sha1
+hash of the merge base that is skipped and [GGGGGG,...]  is a comma
+separated list of the sha1 of the good commits.
+
+So if there is no bad merge base, the bisection process continues as
+usual after this step.
+
+Best bisecting practices
+------------------------
+
+Using test suites and git bisect together
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you both have a test suite and use git bisect, then it becomes less
+important to check that all tests pass after each commit. Though of
+course it is probably a good idea to have some checks to avoid
+breaking too many things because it could make bisecting other bugs
+more difficult.
+
+You can focus your efforts to check at a few points (for example rc
+and beta releases) that all the T test cases pass for all the N
+configurations. And when some tests don't pass you can use "git
+bisect" (or better "git bisect run"). So you should perform roughly:
+
+-------------
+c * N * T + b * M * log2(M) tests
+-------------
+
+where c is the number of rounds of test (so a small constant) and b is
+the ratio of bug per commit (hopefully a small constant too).
+
+So of course it's much better as it's O(N * T) vs O(N * T * M) if
+you would test everything after each commit.
+
+This means that test suites are good to prevent some bugs from being
+committed and they are also quite good to tell you that you have some
+bugs. But they are not so good to tell you where some bugs have been
+introduced. To tell you that efficiently, git bisect is needed.
+
+The other nice thing with test suites, is that when you have one, you
+already know how to test for bad behavior. So you can use this
+knowledge to create a new test case for "git bisect" when it appears
+that there is a regression. So it will be easier to bisect the bug and
+fix it. And then you can add the test case you just created to your
+test suite.
+
+So if you know how to create test cases and how to bisect, you will be
+subject to a virtuous circle:
+
+more tests => easier to create tests => easier to bisect => more tests
+
+So test suites and "git bisect" are complementary tools that are very
+powerful and efficient when used together.
+
+Bisecting build failures
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+You can very easily automatically bisect broken builds using something
+like:
+
+-------------
+$ git bisect start BAD GOOD
+$ git bisect run make
+-------------
+
+Passing sh -c "some commands" to "git bisect run"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+For example:
+
+-------------
+$ git bisect run sh -c "make || exit 125; ./my_app | grep 'good output'"
+-------------
+
+On the other hand if you do this often, then it can be worth having
+scripts to avoid too much typing.
+
+Finding performance regressions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Here is an example script that comes slightly modified from a real
+world script used by Junio Hamano <<4>>.
+
+This script can be passed to "git bisect run" to find the commit that
+introduced a performance regression:
+
+-------------
+#!/bin/sh
+
+# Build errors are not what I am interested in.
+make my_app || exit 255
+
+# We are checking if it stops in a reasonable amount of time, so
+# let it run in the background...
+
+./my_app >log 2>&1 &
+
+# ... and grab its process ID.
+pid=$!
+
+# ... and then wait for sufficiently long.
+sleep $NORMAL_TIME
+
+# ... and then see if the process is still there.
+if kill -0 $pid
+then
+	# It is still running -- that is bad.
+	kill $pid; sleep 1; kill $pid;
+	exit 1
+else
+	# It has already finished (the $pid process was no more),
+	# and we are happy.
+	exit 0
+fi
+-------------
+
+Following general best practices
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+It is obviously a good idea not to have commits with changes that
+knowingly break things, even if some other commits later fix the
+breakage.
+
+It is also a good idea when using any VCS to have only one small
+logical change in each commit.
+
+The smaller the changes in your commit, the most effective "git
+bisect" will be. And you will probably need "git bisect" less in the
+first place, as small changes are easier to review even if they are
+only reviewed by the committer.
+
+Another good idea is to have good commit messages. They can be very
+helpful to understand why some changes were made.
+
+These general best practices are very helpful if you bisect often.
+
+Avoiding bug prone merges
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+First merges by themselves can introduce some regressions even when
+the merge needs no source code conflict resolution. This is because a
+semantic change can happen in one branch while the other branch is not
+aware of it.
+
+For example one branch can change the semantic of a function while the
+other branch add more calls to the same function.
+
+This is made much worse if many files have to be fixed to resolve
+conflicts. That's why such merges are called "evil merges". They can
+make regressions very difficult to track down. It can even be
+misleading to know the first bad commit if it happens to be such a
+merge, because people might think that the bug comes from bad conflict
+resolution when it comes from a semantic change in one branch.
+
+Anyway "git rebase" can be used to linearize history. This can be used
+either to avoid merging in the first place. Or it can be used to
+bisect on a linear history instead of the non linear one, as this
+should give more information in case of a semantic change in one
+branch.
+
+Merges can be also made simpler by using smaller branches or by using
+many topic branches instead of only long version related branches.
+
+And testing can be done more often in special integration branches
+like linux-next for the linux kernel.
+
+Adapting your work-flow
+~~~~~~~~~~~~~~~~~~~~~~~
+
+A special work-flow to process regressions can give great results.
+
+Here is an example of a work-flow used by Andreas Ericsson:
+
+* write, in the test suite, a test script that exposes the regression
+* use "git bisect run" to find the commit that introduced it
+* fix the bug that is often made obvious by the previous step
+* commit both the fix and the test script (and if needed more tests)
+
+And here is what Andreas said about this work-flow <<5>>:
+
+_____________
+To give some hard figures, we used to have an average report-to-fix
+cycle of 142.6 hours (according to our somewhat weird bug-tracker
+which just measures wall-clock time). Since we moved to git, we've
+lowered that to 16.2 hours. Primarily because we can stay on top of
+the bug fixing now, and because everyone's jockeying to get to fix
+bugs (we're quite proud of how lazy we are to let git find the bugs
+for us). Each new release results in ~40% fewer bugs (almost certainly
+due to how we now feel about writing tests).
+_____________
+
+Clearly this work-flow uses the virtuous circle between test suites
+and "git bisect". In fact it makes it the standard procedure to deal
+with regression.
+
+In other messages Andreas says that they also use the "best practices"
+described above: small logical commits, topic branches, no evil
+merge,... These practices all improve the bisectability of the commit
+graph, by making it easier and more useful to bisect.
+
+So a good work-flow should be designed around the above points. That
+is making bisecting easier, more useful and standard.
+
+Involving QA people and if possible end users
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+One nice about "git bisect" is that it is not only a developer
+tool. It can effectively be used by QA people or even end users (if
+they have access to the source code or if they can get access to all
+the builds).
+
+There was a discussion at one point on the linux kernel mailing list
+of whether it was ok to always ask end user to bisect, and very good
+points were made to support the point of view that it is ok.
+
+For example David Miller wrote <<6>>:
+
+_____________
+What people don't get is that this is a situation where the "end node
+principle" applies. When you have limited resources (here: developers)
+you don't push the bulk of the burden upon them. Instead you push
+things out to the resource you have a lot of, the end nodes (here:
+users), so that the situation actually scales.
+_____________
+
+This means that it is often "cheaper" if QA people or end users can do
+it.
+
+What is interesting too is that end users that are reporting bugs (or
+QA people that reproduced a bug) have access to the environment where
+the bug happens. So they can often more easily reproduce a
+regression. And if they can bisect, then more information will be
+extracted from the environment where the bug happens, which means that
+it will be easier to understand and then fix the bug.
+
+For open source projects it can be a good way to get more useful
+contributions from end users, and to introduce them to QA and
+development activities.
+
+Using complex scripts
+~~~~~~~~~~~~~~~~~~~~~
+
+In some cases like for kernel development it can be worth developing
+complex scripts to be able to fully automate bisecting.
+
+Here is what Ingo Molnar says about that <<7>>:
+
+_____________
+i have a fully automated bootup-hang bisection script. It is based on
+"git-bisect run". I run the script, it builds and boots kernels fully
+automatically, and when the bootup fails (the script notices that via
+the serial log, which it continuously watches - or via a timeout, if
+the system does not come up within 10 minutes it's a "bad" kernel),
+the script raises my attention via a beep and i power cycle the test
+box. (yeah, i should make use of a managed power outlet to 100%
+automate it)
+_____________
+
+Combining test suites, git bisect and other systems together
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+We have seen that test suites an git bisect are very powerful when
+used together. It can be even more powerful if you can combine them
+with other systems.
+
+For example some test suites could be run automatically at night with
+some unusual (or even random) configurations. And if a regression is
+found by a test suite, then "git bisect" can be automatically
+launched, and its result can be emailed to the author of the first bad
+commit found by "git bisect", and perhaps other people too. And a new
+entry in the bug tracking system could be automatically created too.
+
+
+The future of bisecting
+-----------------------
+
+"git replace"
+~~~~~~~~~~~~~
+
+We saw earlier that "git bisect skip" is now using a PRNG to try to
+avoid areas in the commit graph where commits are untestable. The
+problem is that sometimes the first bad commit will be in an
+untestable area.
+
+To simplify the discussion we will suppose that the untestable area is
+a simple string of commits and that it was created by a breakage
+introduced by one commit (let's call it BBC for bisect breaking
+commit) and later fixed by another one (let's call it BFC for bisect
+fixing commit).
+
+For example:
+
+-------------
+...-Y-BBC-X1-X2-X3-X4-X5-X6-BFC-Z-...
+-------------
+
+where we know that Y is good and BFC is bad, and where BBC and X1 to
+X6 are untestable.
+
+In this case if you are bisecting manually, what you can do is create
+a special branch that starts just before the BBC. The first commit in
+this branch should be the BBC with the BFC squashed into it. And the
+other commits in the branch should be the commits between BBC and BFC
+rebased on the first commit of the branch and then the commit after
+BFC also rebased on.
+
+For example:
+
+-------------
+      (BBC+BFC)-X1'-X2'-X3'-X4'-X5'-X6'-Z'
+     /
+...-Y-BBC-X1-X2-X3-X4-X5-X6-BFC-Z-...
+-------------
+
+where commits quoted with ' have been rebased.
+
+You can easily create such a branch with Git using interactive rebase.
+
+For example using:
+
+-------------
+$ git rebase -i Y Z
+-------------
+
+and then moving BFC after BBC and squashing it.
+
+After that you can start bisecting as usual in the new branch and you
+should eventually find the first bad commit.
+
+For example:
+
+-------------
+$ git bisect start Z' Y
+-------------
+
+If you are using "git bisect run", you can use the same manual fix up
+as above, and then start another "git bisect run" in the special
+branch. Or as the "git bisect" man page says, the script passed to
+"git bisect run" can apply a patch before it compiles and test the
+software <<8>>. The patch should turn a current untestable commits
+into a testable one. So the testing will result in "good" or "bad" and
+"git bisect" will be able to find the first bad commit. And the script
+should not forget to remove the patch once the testing is done before
+exiting from the script.
+
+(Note that instead of a patch you can use "git cherry-pick BFC" to
+apply the fix, and in this case you should use "git reset --hard
+HEAD^" to revert the cherry-pick after testing and before returning
+from the script.)
+
+But the above ways to work around untestable areas are a little bit
+clunky. Using special branches is nice because these branches can be
+shared by developers like usual branches, but the risk is that people
+will get many such branches. And it disrupts the normal "git bisect"
+work-flow. So, if you want to use "git bisect run" completely
+automatically, you have to add special code in your script to restart
+bisection in the special branches.
+
+Anyway one can notice in the above special branch example that the Z'
+and Z commits should point to the same source code state (the same
+"tree" in git parlance). That's because Z' result from applying the
+same changes as Z just in a slightly different order.
+
+So if we could just "replace" Z by Z' when we bisect, then we would
+not need to add anything to a script. It would just work for anyone in
+the project sharing the special branches and the replacements.
+
+With the example above that would give:
+
+-------------
+      (BBC+BFC)-X1'-X2'-X3'-X4'-X5'-X6'-Z'-...
+     /
+...-Y-BBC-X1-X2-X3-X4-X5-X6-BFC-Z
+-------------
+
+That's why the "git replace" command was created. Technically it
+stores replacements "refs" in the "refs/replace/" hierarchy. These
+"refs" are like branches (that are stored in "refs/heads/") or tags
+(that are stored in "refs/tags"), and that means that they can
+automatically be shared like branches or tags among developers.
+
+"git replace" is a very powerful mechanism. It can be used to fix
+commits in already released history, for example to change the commit
+message or the author. And it can also be used instead of git "grafts"
+to link a repository with another old repository.
+
+In fact it's this last feature that "sold" it to the git community, so
+it is now in the "master" branch of git's git repository and it should
+be released in git 1.6.5 in October or November 2009.
+
+One problem with "git replace" is that currently it stores all the
+replacements refs in "refs/replace/", but it would be perhaps better
+if the replacement refs that are useful only for bisecting would be in
+"refs/replace/bisect/". This way the replacement refs could be used
+only for bisecting, while other refs directly in "refs/replace/" would
+be used nearly all the time.
+
+Bisecting sporadic bugs
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Another possible improvement to "git bisect" would be to optionally
+add some redundancy to the tests performed so that it would be more
+reliable when tracking sporadic bugs.
+
+This has been requested by some kernel developers because some bugs
+called sporadic bugs do not appear in all the kernel builds because
+they are very dependent on the compiler output.
+
+The idea is that every 3 test for example, "git bisect" could ask the
+user to test a commit that has already been found to be "good" or
+"bad" (because one of its descendants or one of its ancestors has been
+found to be "good" or "bad" respectively). If it happens that a commit
+has been previously incorrectly classified then the bisection can be
+aborted early, hopefully before too many mistakes have been made. Then
+the user will have to look at what happened and then restart the
+bisection using a fixed bisect log.
+
+There is already a project called BBChop created by Ealdwulf Wuffinga
+on Github that does something like that using Bayesian Search Theory
+<<9>>:
+
+_____________
+BBChop is like 'git bisect' (or equivalent), but works when your bug
+is intermittent. That is, it works in the presence of false negatives
+(when a version happens to work this time even though it contains the
+bug). It assumes that there are no false positives (in principle, the
+same approach would work, but adding it may be non-trivial).
+_____________
+
+But BBChop is independent of any VCS and it would be easier for Git
+users to have something integrated in Git.
+
+Conclusion
+----------
+
+We have seen that regressions are an important problem, and that "git
+bisect" has nice features that complement very well practices and
+other tools, especially test suites, that are generally used to fight
+regressions. But it might be needed to change some work-flows and
+(bad) habits to get the most out of it.
+
+Some improvements to the algorithms inside "git bisect" are possible
+and some new features could help in some cases, but overall "git
+bisect" works already very well, is used a lot, and is already very
+useful. To back up that last claim, let's give the final word to Ingo
+Molnar when he was asked by the author how much time does he think
+"git bisect" saves him when he uses it:
+
+_____________
+a _lot_.
+
+About ten years ago did i do my first 'bisection' of a Linux patch
+queue. That was prior the Git (and even prior the BitKeeper) days. I
+literally days spent sorting out patches, creating what in essence
+were standalone commits that i guessed to be related to that bug.
+
+It was a tool of absolute last resort. I'd rather spend days looking
+at printk output than do a manual 'patch bisection'.
+
+With Git bisect it's a breeze: in the best case i can get a ~15 step
+kernel bisection done in 20-30 minutes, in an automated way. Even with
+manual help or when bisecting multiple, overlapping bugs, it's rarely
+more than an hour.
+
+In fact it's invaluable because there are bugs i would never even
+_try_ to debug if it wasn't for git bisect. In the past there were bug
+patterns that were immediately hopeless for me to debug - at best i
+could send the crash/bug signature to lkml and hope that someone else
+can think of something.
+
+And even if a bisection fails today it tells us something valuable
+about the bug: that it's non-deterministic - timing or kernel image
+layout dependent.
+
+So git bisect is unconditional goodness - and feel free to quote that
+;-)
+_____________
+
+Acknowledgements
+----------------
+
+Many thanks to Junio Hamano for his help in reviewing this paper, for
+reviewing the patches I sent to the git mailing list, for discussing
+some ideas and helping me improve them, for improving "git bisect" a
+lot and for his awesome work in maintaining and developing Git.
+
+Many thanks to Ingo Molnar for giving me very useful information that
+appears in this paper, for commenting on this paper, for his
+suggestions to improve "git bisect" and for evangelizing "git bisect"
+on the linux kernel mailing lists.
+
+Many thanks to Linus Torvalds for inventing, developing and
+evangelizing "git bisect", Git and Linux.
+
+Many thanks to the many other great people who helped one way or
+another when I worked on git, especially to Andreas Ericsson, Johannes
+Schindelin, H. Peter Anvin, Daniel Barkalow, Bill Lear, John Hawley,
+Shawn O. Pierce, Jeff King, Sam Vilain, Jon Seymour.
+
+Many thanks to the Linux-Kongress program committee for choosing the
+author to given a talk and for publishing this paper.
+
+References
+----------
+
+- [[[1]]] http://www.nist.gov/public_affairs/releases/n02-10.htm['Software Errors Cost U.S. Economy $59.5 Billion Annually'. Nist News Release.]
+- [[[2]]] http://java.sun.com/docs/codeconv/html/CodeConventions.doc.html#16712['Code Conventions for the Java Programming Language'. Sun Microsystems.]
+- [[[3]]] http://en.wikipedia.org/wiki/Software_maintenance['Software maintenance'. Wikipedia.]
+- [[[4]]] http://article.gmane.org/gmane.comp.version-control.git/45195/[Junio C Hamano. 'Automated bisect success story'. Gmane.]
+- [[[5]]] http://lwn.net/Articles/317154/[Christian Couder. 'Fully automated bisecting with "git bisect run"'. LWN.net.]
+- [[[6]]] http://lwn.net/Articles/277872/[Jonathan Corbet. 'Bisection divides users and developers'. LWN.net.]
+- [[[7]]] http://article.gmane.org/gmane.linux.scsi/36652/[Ingo Molnar. 'Re: BUG 2.6.23-rc3 can't see sd partitions on Alpha'. Gmane.]
+- [[[8]]] http://www.kernel.org/pub/software/scm/git/docs/git-bisect.html[Junio C Hamano and the git-list. 'git-bisect(1) Manual Page'. Linux Kernel Archives.]
+- [[[9]]] http://github.com/Ealdwulf/bbchop[Ealdwulf. 'bbchop'. GitHub.]
diff --git a/Documentation/git-bisect.txt b/Documentation/git-bisect.txt
index 63e7a42..e4f46bc 100644
--- a/Documentation/git-bisect.txt
+++ b/Documentation/git-bisect.txt
@@ -8,6 +8,7 @@
 
 SYNOPSIS
 --------
+[verse]
 'git bisect' <subcommand> <options>
 
 DESCRIPTION
@@ -16,11 +17,11 @@
 on the subcommand:
 
  git bisect help
- git bisect start [<bad> [<good>...]] [--] [<paths>...]
+ git bisect start [--no-checkout] [<bad> [<good>...]] [--] [<paths>...]
  git bisect bad [<rev>]
  git bisect good [<rev>...]
  git bisect skip [(<rev>|<range>)...]
- git bisect reset [<branch>]
+ git bisect reset [<commit>]
  git bisect visualize
  git bisect replay <logfile>
  git bisect log
@@ -81,16 +82,27 @@
 Bisect reset
 ~~~~~~~~~~~~
 
-To return to the original head after a bisect session, issue the
-following command:
+After a bisect session, to clean up the bisection state and return to
+the original HEAD, issue the following command:
 
 ------------------------------------------------
 $ git bisect reset
 ------------------------------------------------
 
-This resets the tree to the original branch instead of being on the
-bisection commit ("git bisect start" will also do that, as it resets
-the bisection state).
+By default, this will return your tree to the commit that was checked
+out before `git bisect start`.  (A new `git bisect start` will also do
+that, as it cleans up the old bisection state.)
+
+With an optional argument, you can return to a different commit
+instead:
+
+------------------------------------------------
+$ git bisect reset <commit>
+------------------------------------------------
+
+For example, `git bisect reset HEAD` will leave you on the current
+bisection commit and avoid switching commits at all, while `git bisect
+reset bisect/bad` will check out the first bad revision.
 
 Bisect visualize
 ~~~~~~~~~~~~~~~~
@@ -230,7 +242,12 @@
 
 The special exit code 125 should be used when the current source code
 cannot be tested. If the script exits with this code, the current
-revision will be skipped (see `git bisect skip` above).
+revision will be skipped (see `git bisect skip` above). 125 was chosen
+as the highest sensible value to use for this purpose, because 126 and 127
+are used by POSIX shells to signal specific error status (127 is for
+command not found, 126 is for command found but not executable---these
+details do not matter, as they are normal errors in the script, as far as
+"bisect run" is concerned).
 
 You may often find that during a bisect session you want to have
 temporary modifications (e.g. s/#define DEBUG 0/#define DEBUG 1/ in a
@@ -246,6 +263,19 @@
 with the status of the real test to let the "git bisect run" command loop
 determine the eventual outcome of the bisect session.
 
+OPTIONS
+-------
+--no-checkout::
++
+Do not checkout the new working tree at each iteration of the bisection
+process. Instead just update a special reference named 'BISECT_HEAD' to make
+it point to the commit that should be tested.
++
+This option may be useful when the test you would perform in each step
+does not require a checked out tree.
++
+If the repository is bare, `--no-checkout` is assumed.
+
 EXAMPLES
 --------
 
@@ -263,61 +293,92 @@
 $ git bisect run make test           # "make test" builds and tests
 ------------
 
-* Automatically bisect a broken test suite:
-+
-------------
-$ cat ~/test.sh
-#!/bin/sh
-make || exit 125                   # this skips broken builds
-make test                          # "make test" runs the test suite
-$ git bisect start v1.3 v1.1 --    # v1.3 is bad, v1.1 is good
-$ git bisect run ~/test.sh
-------------
-+
-Here we use a "test.sh" custom script. In this script, if "make"
-fails, we skip the current commit.
-+
-It is safer to use a custom script outside the repository to prevent
-interactions between the bisect, make and test processes and the
-script.
-+
-"make test" should "exit 0", if the test suite passes, and
-"exit 1" otherwise.
-
 * Automatically bisect a broken test case:
 +
 ------------
 $ cat ~/test.sh
 #!/bin/sh
 make || exit 125                     # this skips broken builds
-~/check_test_case.sh                 # does the test case passes ?
+~/check_test_case.sh                 # does the test case pass?
 $ git bisect start HEAD HEAD~10 --   # culprit is among the last 10
 $ git bisect run ~/test.sh
 ------------
 +
-Here "check_test_case.sh" should "exit 0" if the test case passes,
+Here we use a "test.sh" custom script. In this script, if "make"
+fails, we skip the current commit.
+"check_test_case.sh" should "exit 0" if the test case passes,
 and "exit 1" otherwise.
 +
-It is safer if both "test.sh" and "check_test_case.sh" scripts are
+It is safer if both "test.sh" and "check_test_case.sh" are
 outside the repository to prevent interactions between the bisect,
 make and test processes and the scripts.
 
-* Automatically bisect a broken test suite:
+* Automatically bisect with temporary modifications (hot-fix):
++
+------------
+$ cat ~/test.sh
+#!/bin/sh
+
+# tweak the working tree by merging the hot-fix branch
+# and then attempt a build
+if	git merge --no-commit hot-fix &&
+	make
+then
+	# run project specific test and report its status
+	~/check_test_case.sh
+	status=$?
+else
+	# tell the caller this is untestable
+	status=125
+fi
+
+# undo the tweak to allow clean flipping to the next commit
+git reset --hard
+
+# return control
+exit $status
+------------
++
+This applies modifications from a hot-fix branch before each test run,
+e.g. in case your build or test environment changed so that older
+revisions may need a fix which newer ones have already. (Make sure the
+hot-fix branch is based off a commit which is contained in all revisions
+which you are bisecting, so that the merge does not pull in too much, or
+use `git cherry-pick` instead of `git merge`.)
+
+* Automatically bisect a broken test case:
 +
 ------------
 $ git bisect start HEAD HEAD~10 --   # culprit is among the last 10
 $ git bisect run sh -c "make || exit 125; ~/check_test_case.sh"
 ------------
 +
-Does the same as the previous example, but on a single line.
+This shows that you can do without a run script if you write the test
+on a single line.
 
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
+* Locate a good region of the object graph in a damaged repository
++
+------------
+$ git bisect start HEAD <known-good-commit> [ <boundary-commit> ... ] --no-checkout
+$ git bisect run sh -c '
+	GOOD=$(git for-each-ref "--format=%(objectname)" refs/bisect/good-*) &&
+	git rev-list --objects BISECT_HEAD --not $GOOD >tmp.$$ &&
+	git pack-objects --stdout >/dev/null <tmp.$$
+	rc=$?
+	rm -f tmp.$$
+	test $rc = 0'
 
-Documentation
--------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
+------------
++
+In this case, when 'git bisect run' finishes, bisect/bad will refer to a commit that
+has at least one parent whose reachable graph is fully traversable in the sense
+required by 'git pack objects'.
+
+
+SEE ALSO
+--------
+link:git-bisect-lk2009.html[Fighting regressions with git bisect],
+linkgit:git-blame[1].
 
 GIT
 ---
diff --git a/Documentation/git-blame.txt b/Documentation/git-blame.txt
index 8c7b7b0..7ee9236 100644
--- a/Documentation/git-blame.txt
+++ b/Documentation/git-blame.txt
@@ -8,8 +8,8 @@
 SYNOPSIS
 --------
 [verse]
-'git blame' [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-p] [-w] [--incremental] [-L n,m]
-            [-S <revs-file>] [-M] [-C] [-C] [--since=<date>]
+'git blame' [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-e] [-p] [-w] [--incremental] [-L n,m]
+	    [-S <revs-file>] [-M] [-C] [-C] [-C] [--since=<date>] [--abbrev=<n>]
 	    [<rev> | --contents <file> | --reverse <rev>] [--] <file>
 
 DESCRIPTION
@@ -21,7 +21,7 @@
 The command can also limit the range of lines annotated.
 
 The report does not tell you anything about lines which have been deleted or
-replaced; you need to use a tool such as 'git-diff' or the "pickaxe"
+replaced; you need to use a tool such as 'git diff' or the "pickaxe"
 interface briefly mentioned in the following paragraph.
 
 Apart from supporting file annotation, git also supports searching the
@@ -49,7 +49,7 @@
 	file (see `-M`).  The first number listed is the score.
 	This is the number of alphanumeric characters detected
 	as having been moved between or within files.  This must be above
-	a certain threshold for 'git-blame' to consider those lines
+	a certain threshold for 'git blame' to consider those lines
 	of code to have been moved.
 
 -f::
@@ -65,10 +65,19 @@
 -s::
 	Suppress the author name and timestamp from the output.
 
+-e::
+--show-email::
+	Show the author email instead of author name (Default: off).
+
 -w::
 	Ignore whitespace when comparing the parent's version and
 	the child's to find where the lines came from.
 
+--abbrev=<n>::
+	Instead of using the default 7+1 hexadecimal digits as the
+	abbreviated object name, use <n>+1 digits. Note that 1 column
+	is used for a caret to mark the boundary commit.
+
 
 THE PORCELAIN FORMAT
 --------------------
@@ -96,11 +105,24 @@
 header, prefixed by a TAB. This is to allow adding more
 header elements later.
 
+The porcelain format generally suppresses commit information that has
+already been seen. For example, two lines that are blamed to the same
+commit will both be shown, but the details for that commit will be shown
+only once. This is more efficient, but may require more state be kept by
+the reader. The `--line-porcelain` option can be used to output full
+commit information for each line, allowing simpler (but less efficient)
+usage like:
+
+	# count the number of lines attributed to each author
+	git blame --line-porcelain file |
+	sed -n 's/^author //p' |
+	sort | uniq -c | sort -rn
+
 
 SPECIFYING RANGES
 -----------------
 
-Unlike 'git-blame' and 'git-annotate' in older versions of git, the extent
+Unlike 'git blame' and 'git annotate' in older versions of git, the extent
 of the annotation can be limited to both line ranges and revision
 ranges.  When you are interested in finding the origin for
 lines 40-60 for file `foo`, you can use the `-L` option like so
@@ -118,7 +140,7 @@
 
 When you are not interested in changes older than version
 v2.6.18, or changes older than 3 weeks, you can use revision
-range specifiers  similar to 'git-rev-list':
+range specifiers  similar to 'git rev-list':
 
 	git blame v2.6.18.. -- foo
 	git blame --since=3.weeks -- foo
@@ -138,7 +160,7 @@
 	git log --diff-filter=A --pretty=short -- foo
 
 and then annotate the change between the commit and its
-parents, using `commit{caret}!` notation:
+parents, using `commit^!` notation:
 
 	git blame -C -C -f $commit^! -- foo
 
@@ -194,10 +216,6 @@
 --------
 linkgit:git-annotate[1]
 
-AUTHOR
-------
-Written by Junio C Hamano <gitster@pobox.com>
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index ae201de..47235be 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -8,19 +8,25 @@
 SYNOPSIS
 --------
 [verse]
-'git branch' [--color | --no-color] [-r | -a]
-	[-v [--abbrev=<length> | --no-abbrev]]
-	[(--merged | --no-merged | --contains) [<commit>]]
-'git branch' [--track | --no-track] [-l] [-f] <branchname> [<start-point>]
+'git branch' [--color[=<when>] | --no-color] [-r | -a]
+	[--list] [-v [--abbrev=<length> | --no-abbrev]]
+	[--column[=<options>] | --no-column]
+	[(--merged | --no-merged | --contains) [<commit>]] [<pattern>...]
+'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]
 'git branch' (-m | -M) [<oldbranch>] <newbranch>
 'git branch' (-d | -D) [-r] <branchname>...
+'git branch' --edit-description [<branchname>]
 
 DESCRIPTION
 -----------
 
 With no arguments, existing branches are listed and the current branch will
 be highlighted with an asterisk.  Option `-r` causes the remote-tracking
-branches to be listed, and option `-a` shows both.
+branches to be listed, and option `-a` shows both. This list mode is also
+activated by the `--list` option (see below).
+<pattern> restricts the output to matching branches, the pattern is a shell
+wildcard (i.e., matched using fnmatch(3)).
+Multiple patterns may be given; if any of them matches, the branch is shown.
 
 With `--contains`, shows only the branches that contain the named commit
 (in other words, the branches whose tip commits are descendants of the
@@ -30,22 +36,21 @@
 the named commit will be listed.  If the <commit> argument is missing it
 defaults to 'HEAD' (i.e. the tip of the current branch).
 
-In the command's second form, a new branch named <branchname> will be created.
-It will start out with a head equal to the one given as <start-point>.
-If no <start-point> is given, the branch will be created with a head
-equal to that of the currently checked out branch.
+The command's second form creates a new branch head named <branchname>
+which points to the current 'HEAD', or <start-point> if given.
 
 Note that this will create the new branch, but it will not switch the
 working tree to it; use "git checkout <newbranch>" to switch to the
 new branch.
 
-When a local branch is started off a remote branch, git sets up the
-branch so that 'git-pull' will appropriately merge from
-the remote branch. This behavior may be changed via the global
+When a local branch is started off a remote-tracking branch, git sets up the
+branch so that 'git pull' will appropriately merge from
+the remote-tracking branch. This behavior may be changed via the global
 `branch.autosetupmerge` configuration flag. That setting can be
-overridden by using the `--track` and `--no-track` options.
+overridden by using the `--track` and `--no-track` options, and
+changed later using `git branch --set-upstream`.
 
-With a '-m' or '-M' option, <oldbranch> will be renamed to <newbranch>.
+With a `-m` or `-M` option, <oldbranch> will be renamed to <newbranch>.
 If <oldbranch> had a corresponding reflog, it is renamed to match
 <newbranch>, and a reflog entry is created to remember the branch
 renaming. If <newbranch> exists, -M must be used to force the rename
@@ -55,9 +60,9 @@
 specify more than one branch for deletion.  If the branch currently
 has a reflog then the reflog will also be deleted.
 
-Use -r together with -d to delete remote-tracking branches. Note, that it
+Use `-r` together with `-d` to delete remote-tracking branches. Note, that it
 only makes sense to delete remote-tracking branches if they no longer exist
-in the remote repository or if 'git-fetch' was configured not to fetch
+in the remote repository or if 'git fetch' was configured not to fetch
 them again. See also the 'prune' subcommand of linkgit:git-remote[1] for a
 way to clean up all obsolete remote-tracking branches.
 
@@ -65,48 +70,80 @@
 OPTIONS
 -------
 -d::
-	Delete a branch. The branch must be fully merged in HEAD.
+--delete::
+	Delete a branch. The branch must be fully merged in its
+	upstream branch, or in `HEAD` if no upstream was set with
+	`--track` or `--set-upstream`.
 
 -D::
 	Delete a branch irrespective of its merged status.
 
 -l::
+--create-reflog::
 	Create the branch's reflog.  This activates recording of
 	all changes made to the branch ref, enabling use of date
 	based sha1 expressions such as "<branchname>@\{yesterday}".
+	Note that in non-bare repositories, reflogs are usually
+	enabled by default by the `core.logallrefupdates` config option.
 
 -f::
+--force::
 	Reset <branchname> to <startpoint> if <branchname> exists
-	already. Without `-f` 'git-branch' refuses to change an existing branch.
+	already. Without `-f` 'git branch' refuses to change an existing branch.
 
 -m::
+--move::
 	Move/rename a branch and the corresponding reflog.
 
 -M::
 	Move/rename a branch even if the new branch name already exists.
 
---color::
-	Color branches to highlight current, local, and remote branches.
+--color[=<when>]::
+	Color branches to highlight current, local, and
+	remote-tracking branches.
+	The value must be always (the default), never, or auto.
 
 --no-color::
 	Turn off branch colors, even when the configuration file gives the
 	default to color output.
+	Same as `--color=never`.
+
+--column[=<options>]::
+--no-column::
+	Display branch listing in columns. See configuration variable
+	column.branch for option syntax.`--column` and `--no-column`
+	without options are equivalent to 'always' and 'never' respectively.
++
+This option is only applicable in non-verbose mode.
 
 -r::
+--remotes::
 	List or delete (if used with -d) the remote-tracking branches.
 
 -a::
+--all::
 	List both remote-tracking branches and local branches.
 
+--list::
+	Activate the list mode. `git branch <pattern>` would try to create a branch,
+	use `git branch --list <pattern>` to list matching branches.
+
 -v::
 --verbose::
-	Show sha1 and commit subject line for each head, along with
+	When in list mode,
+	show sha1 and commit subject line for each head, along with
 	relationship to upstream branch (if any). If given twice, print
 	the name of the upstream branch, as well.
 
+-q::
+--quiet::
+	Be more quiet when creating or deleting a branch, suppressing
+	non-error messages.
+
 --abbrev=<length>::
 	Alter the sha1's minimum display length in the output listing.
-	The default value is 7.
+	The default value is 7 and can be overridden by the `core.abbrev`
+	config option.
 
 --no-abbrev::
 	Display the full sha1s in the output listing rather than abbreviating them.
@@ -120,24 +157,37 @@
 	it directs `git pull` without arguments to pull from the
 	upstream when the new branch is checked out.
 +
-This behavior is the default when the start point is a remote branch.
+This behavior is the default when the start point is a remote-tracking branch.
 Set the branch.autosetupmerge configuration variable to `false` if you
 want `git checkout` and `git branch` to always behave as if '--no-track'
 were given. Set it to `always` if you want this behavior when the
-start-point is either a local or remote branch.
+start-point is either a local or remote-tracking branch.
 
 --no-track::
 	Do not set up "upstream" configuration, even if the
 	branch.autosetupmerge configuration variable is true.
 
---contains <commit>::
-	Only list branches which contain the specified commit.
+--set-upstream::
+	If specified branch does not exist yet or if `--force` has been
+	given, acts exactly like `--track`. Otherwise sets up configuration
+	like `--track` would when creating the branch, except that where
+	branch points to is not changed.
 
---merged::
-	Only list branches which are fully contained by HEAD.
+--edit-description::
+	Open an editor and edit the text to explain what the branch is
+	for, to be used by various other commands (e.g. `request-pull`).
 
---no-merged::
-	Do not list branches which are fully contained by HEAD.
+--contains [<commit>]::
+	Only list branches which contain the specified commit (HEAD
+	if not specified).
+
+--merged [<commit>]::
+	Only list branches whose tips are reachable from the
+	specified commit (HEAD if not specified).
+
+--no-merged [<commit>]::
+	Only list branches whose tips are not reachable from the
+	specified commit (HEAD if not specified).
 
 <branchname>::
 	The name of the branch to create or delete.
@@ -146,9 +196,9 @@
 	may restrict the characters allowed in a branch name.
 
 <start-point>::
-	The new branch will be created with a HEAD equal to this.  It may
-	be given as a branch name, a commit-id, or a tag.  If this option
-	is omitted, the current branch is assumed.
+	The new branch head will point to this commit.  It may be
+	given as a branch name, a commit-id, or a tag.  If this
+	option is omitted, the current HEAD will be used instead.
 
 <oldbranch>::
 	The name of an existing branch to rename.
@@ -209,13 +259,13 @@
 - `--no-merged` is used to find branches which are candidates for merging
   into HEAD, since those branches are not fully contained by HEAD.
 
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org> and Junio C Hamano <gitster@pobox.com>
-
-Documentation
---------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
+SEE ALSO
+--------
+linkgit:git-check-ref-format[1],
+linkgit:git-fetch[1],
+linkgit:git-remote[1],
+link:user-manual.html#what-is-a-branch[``Understanding history: What is
+a branch?''] in the Git User's Manual.
 
 GIT
 ---
diff --git a/Documentation/git-bundle.txt b/Documentation/git-bundle.txt
index aee7e4a..16a6b0a 100644
--- a/Documentation/git-bundle.txt
+++ b/Documentation/git-bundle.txt
@@ -9,10 +9,10 @@
 SYNOPSIS
 --------
 [verse]
-'git bundle' create <file> <git-rev-list args>
+'git bundle' create <file> <git-rev-list-args>
 'git bundle' verify <file>
-'git bundle' list-heads <file> [refname...]
-'git bundle' unbundle <file> [refname...]
+'git bundle' list-heads <file> [<refname>...]
+'git bundle' unbundle <file> [<refname>...]
 
 DESCRIPTION
 -----------
@@ -21,10 +21,10 @@
 machine be replicated on another machine, but the two machines cannot
 be directly connected, and therefore the interactive git protocols (git,
 ssh, rsync, http) cannot be used.  This command provides support for
-'git-fetch' and 'git-pull' to operate by packaging objects and references
+'git fetch' and 'git pull' to operate by packaging objects and references
 in an archive at the originating machine, then importing those into
-another repository using 'git-fetch' and 'git-pull'
-after moving the archive by some means (i.e., by sneakernet).  As no
+another repository using 'git fetch' and 'git pull'
+after moving the archive by some means (e.g., by sneakernet).  As no
 direct connection between the repositories exists, the user must specify a
 basis for the bundle that is held by the destination repository: the
 bundle assumes that all objects in the basis are already in the
@@ -34,57 +34,58 @@
 -------
 
 create <file>::
-       Used to create a bundle named 'file'.  This requires the
-       'git-rev-list' arguments to define the bundle contents.
+	Used to create a bundle named 'file'.  This requires the
+	'git-rev-list-args' arguments to define the bundle contents.
 
 verify <file>::
-       Used to check that a bundle file is valid and will apply
-       cleanly to the current repository.  This includes checks on the
-       bundle format itself as well as checking that the prerequisite
-       commits exist and are fully linked in the current repository.
-       'git-bundle' prints a list of missing commits, if any, and exits
-       with a non-zero status.
+	Used to check that a bundle file is valid and will apply
+	cleanly to the current repository.  This includes checks on the
+	bundle format itself as well as checking that the prerequisite
+	commits exist and are fully linked in the current repository.
+	'git bundle' prints a list of missing commits, if any, and exits
+	with a non-zero status.
 
 list-heads <file>::
-       Lists the references defined in the bundle.  If followed by a
-       list of references, only references matching those given are
-       printed out.
+	Lists the references defined in the bundle.  If followed by a
+	list of references, only references matching those given are
+	printed out.
 
 unbundle <file>::
-       Passes the objects in the bundle to 'git-index-pack'
-       for storage in the repository, then prints the names of all
-       defined references. If a list of references is given, only
-       references matching those in the list are printed. This command is
-       really plumbing, intended to be called only by 'git-fetch'.
+	Passes the objects in the bundle to 'git index-pack'
+	for storage in the repository, then prints the names of all
+	defined references. If a list of references is given, only
+	references matching those in the list are printed. This command is
+	really plumbing, intended to be called only by 'git fetch'.
 
-[git-rev-list-args...]::
-       A list of arguments, acceptable to 'git-rev-parse' and
-       'git-rev-list', that specifies the specific objects and references
-       to transport.  For example, `master\~10..master` causes the
-       current master reference to be packaged along with all objects
-       added since its 10th ancestor commit.  There is no explicit
-       limit to the number of references and objects that may be
-       packaged.
+<git-rev-list-args>::
+	A list of arguments, acceptable to 'git rev-parse' and
+	'git rev-list' (and containing a named ref, see SPECIFYING REFERENCES
+	below), that specifies the specific objects and references
+	to transport.  For example, `master~10..master` causes the
+	current master reference to be packaged along with all objects
+	added since its 10th ancestor commit.  There is no explicit
+	limit to the number of references and objects that may be
+	packaged.
 
 
-[refname...]::
-       A list of references used to limit the references reported as
-       available. This is principally of use to 'git-fetch', which
-       expects to receive only those references asked for and not
-       necessarily everything in the pack (in this case, 'git-bundle' acts
-       like 'git-fetch-pack').
+[<refname>...]::
+	A list of references used to limit the references reported as
+	available. This is principally of use to 'git fetch', which
+	expects to receive only those references asked for and not
+	necessarily everything in the pack (in this case, 'git bundle' acts
+	like 'git fetch-pack').
 
 SPECIFYING REFERENCES
 ---------------------
 
-'git-bundle' will only package references that are shown by
-'git-show-ref': this includes heads, tags, and remote heads.  References
-such as `master\~1` cannot be packaged, but are perfectly suitable for
+'git bundle' will only package references that are shown by
+'git show-ref': this includes heads, tags, and remote heads.  References
+such as `master~1` cannot be packaged, but are perfectly suitable for
 defining the basis.  More than one reference may be packaged, and more
 than one basis can be specified.  The objects packaged are those not
 contained in the union of the given bases.  Each basis can be
-specified explicitly (e.g. `^master\~10`), or implicitly (e.g.
-`master\~10..master`, `--since=10.days.ago master`).
+specified explicitly (e.g. `^master~10`), or implicitly (e.g.
+`master~10..master`, `--since=10.days.ago master`).
 
 It is very important that the basis used be held by the destination.
 It is okay to err on the side of caution, causing the bundle file
@@ -154,7 +155,7 @@
 If you know up to what commit the intended recipient repository should
 have the necessary objects, you can use that knowledge to specify the
 basis, giving a cut-off point to limit the revisions and objects that go
-in the resulting bundle. The previous example used lastR2bundle tag
+in the resulting bundle. The previous example used the lastR2bundle tag
 for this purpose, but you can use any other options that you would give to
 the linkgit:git-log[1] command. Here are more examples:
 
@@ -194,16 +195,12 @@
 $ git fetch mybundle master:localRef
 ----------------
 
-You can also see what references it offers.
+You can also see what references it offers:
 
 ----------------
 $ git ls-remote mybundle
 ----------------
 
-Author
-------
-Written by Mark Levedahl <mdl123@verizon.net>
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt
index 58c8d65..2fb95bb 100644
--- a/Documentation/git-cat-file.txt
+++ b/Documentation/git-cat-file.txt
@@ -9,14 +9,15 @@
 SYNOPSIS
 --------
 [verse]
-'git cat-file' (-t | -s | -e | -p | <type>) <object>
+'git cat-file' (-t | -s | -e | -p | <type> | --textconv ) <object>
 'git cat-file' (--batch | --batch-check) < <list-of-objects>
 
 DESCRIPTION
 -----------
 In its first form, the command provides the content or the type of an object in
 the repository. The type is required unless '-t' or '-p' is used to find the
-object type, or '-s' is used to find the object size.
+object type, or '-s' is used to find the object size, or '--textconv' is used
+(which implies type "blob").
 
 In the second form, a list of objects (separated by linefeeds) is provided on
 stdin, and the SHA1, type, and size of each object is printed on stdout.
@@ -26,7 +27,7 @@
 <object>::
 	The name of the object to show.
 	For a more complete list of ways to spell object names, see
-	the "SPECIFYING REVISIONS" section in linkgit:git-rev-parse[1].
+	the "SPECIFYING REVISIONS" section in linkgit:gitrevisions[7].
 
 -t::
 	Instead of the content, show the object type identified by
@@ -51,6 +52,11 @@
 	or to ask for a "blob" with <object> being a tag object that
 	points at it.
 
+--textconv::
+	Show the content as transformed by a textconv filter. In this case,
+	<object> has be of the form <treeish>:<path>, or :<path> in order
+	to apply the filter to the content recorded in the index at <path>.
+
 --batch::
 	Print the SHA1, type, size, and contents of each object provided on
 	stdin. May not be combined with any other options or arguments.
@@ -94,14 +100,6 @@
 <object> SP missing LF
 ------------
 
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-check-attr.txt b/Documentation/git-check-attr.txt
index 50824e3..5abdbaa 100644
--- a/Documentation/git-check-attr.txt
+++ b/Documentation/git-check-attr.txt
@@ -9,8 +9,8 @@
 SYNOPSIS
 --------
 [verse]
-'git check-attr' attr... [--] pathname...
-'git check-attr' --stdin [-z] attr... < <list-of-paths>
+'git check-attr' [-a | --all | attr...] [--] pathname...
+'git check-attr' --stdin [-z] [-a | --all | attr...] < <list-of-paths>
 
 DESCRIPTION
 -----------
@@ -19,6 +19,14 @@
 
 OPTIONS
 -------
+-a, --all::
+	List all attributes that are associated with the specified
+	paths.  If this option is used, then 'unspecified' attributes
+	will not be included in the output.
+
+--cached::
+	Consider `.gitattributes` in the index only, ignoring the working tree.
+
 --stdin::
 	Read file names from stdin instead of from the command-line.
 
@@ -28,8 +36,11 @@
 
 \--::
 	Interpret all preceding arguments as attributes and all following
-	arguments as path names. If not supplied, only the first argument will
-	be treated as an attribute.
+	arguments as path names.
+
+If none of `--stdin`, `--all`, or `--` is used, the first argument
+will be treated as an attribute and the rest of the arguments as
+pathnames.
 
 OUTPUT
 ------
@@ -69,6 +80,13 @@
 org/example/MyClass.java: myAttr: set
 ---------------
 
+* Listing all attributes for a file:
+---------------
+$ git check-attr --all -- org/example/MyClass.java
+org/example/MyClass.java: diff: java
+org/example/MyClass.java: myAttr: set
+---------------
+
 * Listing an attribute for multiple files:
 ---------------
 $ git check-attr myAttr -- org/example/MyClass.java org/example/NoMyAttr.java
@@ -86,15 +104,6 @@
 --------
 linkgit:gitattributes[5].
 
-
-Author
-------
-Written by Junio C Hamano <gitster@pobox.com>
-
-Documentation
---------------
-Documentation by James Bowes.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-check-ref-format.txt b/Documentation/git-check-ref-format.txt
index 0b7982e..98009d1 100644
--- a/Documentation/git-check-ref-format.txt
+++ b/Documentation/git-check-ref-format.txt
@@ -8,8 +8,10 @@
 SYNOPSIS
 --------
 [verse]
-'git check-ref-format' <refname>
-'git check-ref-format' [--branch] <branchname-shorthand>
+'git check-ref-format' [--normalize]
+       [--[no-]allow-onelevel] [--refspec-pattern]
+       <refname>
+'git check-ref-format' --branch <branchname-shorthand>
 
 DESCRIPTION
 -----------
@@ -17,63 +19,108 @@
 status if it is not.
 
 A reference is used in git to specify branches and tags.  A
-branch head is stored under the `$GIT_DIR/refs/heads` directory, and
-a tag is stored under the `$GIT_DIR/refs/tags` directory.  git
-imposes the following rules on how references are named:
+branch head is stored in the `refs/heads` hierarchy, while
+a tag is stored in the `refs/tags` hierarchy of the ref namespace
+(typically in `$GIT_DIR/refs/heads` and `$GIT_DIR/refs/tags`
+directories or, as entries in file `$GIT_DIR/packed-refs`
+if refs are packed by `git gc`).
+
+git imposes the following rules on how references are named:
 
 . They can include slash `/` for hierarchical (directory)
   grouping, but no slash-separated component can begin with a
-  dot `.`.
+  dot `.` or end with the sequence `.lock`.
 
 . They must contain at least one `/`. This enforces the presence of a
   category like `heads/`, `tags/` etc. but the actual names are not
-  restricted.
+  restricted.  If the `--allow-onelevel` option is used, this rule
+  is waived.
 
 . They cannot have two consecutive dots `..` anywhere.
 
 . They cannot have ASCII control characters (i.e. bytes whose
   values are lower than \040, or \177 `DEL`), space, tilde `~`,
-  caret `{caret}`, colon `:`, question-mark `?`, asterisk `*`,
-  or open bracket `[` anywhere.
+  caret `^`, or colon `:` anywhere.
 
-. They cannot end with a slash `/` nor a dot `.`.
+. They cannot have question-mark `?`, asterisk `*`, or open
+  bracket `[` anywhere.  See the `--refspec-pattern` option below for
+  an exception to this rule.
 
-. They cannot end with the sequence `.lock`.
+. They cannot begin or end with a slash `/` or contain multiple
+  consecutive slashes (see the `--normalize` option below for an
+  exception to this rule)
+
+. They cannot end with a dot `.`.
 
 . They cannot contain a sequence `@{`.
 
-- They cannot contain a `\\`.
+. They cannot contain a `\`.
 
 These rules make it easy for shell script based tools to parse
 reference names, pathname expansion by the shell when a reference name is used
 unquoted (by mistake), and also avoids ambiguities in certain
-reference name expressions (see linkgit:git-rev-parse[1]):
+reference name expressions (see linkgit:gitrevisions[7]):
 
 . A double-dot `..` is often used as in `ref1..ref2`, and in some
-  contexts this notation means `{caret}ref1 ref2` (i.e. not in
+  contexts this notation means `^ref1 ref2` (i.e. not in
   `ref1` and in `ref2`).
 
-. A tilde `~` and caret `{caret}` are used to introduce the postfix
+. A tilde `~` and caret `^` are used to introduce the postfix
   'nth parent' and 'peel onion' operation.
 
 . A colon `:` is used as in `srcref:dstref` to mean "use srcref\'s
   value and store it in dstref" in fetch and push operations.
   It may also be used to select a specific object such as with
-  'git-cat-file': "git cat-file blob v1.3.3:refs.c".
+  'git cat-file': "git cat-file blob v1.3.3:refs.c".
 
 . at-open-brace `@{` is used as a notation to access a reflog entry.
 
-With the `--branch` option, it expands a branch name shorthand and
-prints the name of the branch the shorthand refers to.
+With the `--branch` option, it expands the ``previous branch syntax''
+`@{-n}`.  For example, `@{-1}` is a way to refer the last branch you
+were on.  This option should be used by porcelains to accept this
+syntax anywhere a branch name is expected, so they can act as if you
+typed the branch name.
 
-EXAMPLE
+OPTIONS
 -------
+--allow-onelevel::
+--no-allow-onelevel::
+	Controls whether one-level refnames are accepted (i.e.,
+	refnames that do not contain multiple `/`-separated
+	components).  The default is `--no-allow-onelevel`.
 
-git check-ref-format --branch @{-1}::
+--refspec-pattern::
+	Interpret <refname> as a reference name pattern for a refspec
+	(as used with remote repositories).  If this option is
+	enabled, <refname> is allowed to contain a single `*`
+	in place of a one full pathname component (e.g.,
+	`foo/*/bar` but not `foo/bar*`).
 
-Print the name of the previous branch.
+--normalize::
+	Normalize 'refname' by removing any leading slash (`/`)
+	characters and collapsing runs of adjacent slashes between
+	name components into a single slash.  Iff the normalized
+	refname is valid then print it to standard output and exit
+	with a status of 0.  (`--print` is a deprecated way to spell
+	`--normalize`.)
 
 
+EXAMPLES
+--------
+
+* Print the name of the previous branch:
++
+------------
+$ git check-ref-format --branch @{-1}
+------------
+
+* Determine the reference name to use for a new branch:
++
+------------
+$ ref=$(git check-ref-format --normalize "refs/heads/$newbranch") ||
+die "we do not like '$newbranch' as a branch name."
+------------
+
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-checkout-index.txt b/Documentation/git-checkout-index.txt
index 62d8483..4d33e7b 100644
--- a/Documentation/git-checkout-index.txt
+++ b/Documentation/git-checkout-index.txt
@@ -13,7 +13,7 @@
 		   [--stage=<number>|all]
 		   [--temp]
 		   [-z] [--stdin]
-		   [--] [<file>]\*
+		   [--] [<file>...]
 
 DESCRIPTION
 -----------
@@ -88,7 +88,7 @@
 which will force all existing `*.h` files to be replaced with their
 cached copies. If an empty command line implied "all", then this would
 force-refresh everything in the index, which was not the point.  But
-since 'git-checkout-index' accepts --stdin it would be faster to use:
+since 'git checkout-index' accepts --stdin it would be faster to use:
 
 ----------------
 $ find . -name '*.h' -print0 | git checkout-index -f -z --stdin
@@ -102,7 +102,7 @@
 Using --temp or --stage=all
 ---------------------------
 When `--temp` is used (or implied by `--stage=all`)
-'git-checkout-index' will create a temporary file for each index
+'git checkout-index' will create a temporary file for each index
 entry being checked out.  The index will not be updated with stat
 information.  These options can be useful if the caller needs all
 stages of all unmerged entries so that the unmerged files can be
@@ -147,9 +147,9 @@
 $ git checkout-index -n -f -a && git update-index --ignore-missing --refresh
 ----------------
 
-Using 'git-checkout-index' to "export an entire tree"::
+Using 'git checkout-index' to "export an entire tree"::
 	The prefix ability basically makes it trivial to use
-	'git-checkout-index' as an "export as tree" function.
+	'git checkout-index' as an "export as tree" function.
 	Just read the desired tree into the index, and do:
 +
 ----------------
@@ -172,18 +172,6 @@
 This will check out the currently cached copy of `Makefile`
 into the file `.merged-Makefile`.
 
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-
-Documentation
---------------
-Documentation by David Greaves,
-Junio C Hamano and the git-list <git@vger.kernel.org>.
-
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index ad4b31e..63a2516 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -9,45 +9,69 @@
 --------
 [verse]
 'git checkout' [-q] [-f] [-m] [<branch>]
-'git checkout' [-q] [-f] [-m] [-b <new_branch>] [<start_point>]
+'git checkout' [-q] [-f] [-m] [--detach] [<commit>]
+'git checkout' [-q] [-f] [-m] [[-b|-B|--orphan] <new_branch>] [<start_point>]
 'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>...
+'git checkout' [-p|--patch] [<tree-ish>] [--] [<paths>...]
 
 DESCRIPTION
 -----------
-
-When <paths> are not given, this command switches branches by
-updating the index, working tree, and HEAD to reflect the specified
+Updates files in the working tree to match the version in the index
+or the specified tree.  If no paths are given, 'git checkout' will
+also update `HEAD` to set the specified branch as the current
 branch.
 
-If `-b` is given, a new branch is created and checked out, as if
-linkgit:git-branch[1] were called; in this case you can
-use the --track or --no-track options, which will be passed to `git
-branch`.  As a convenience, --track without `-b` implies branch
-creation; see the description of --track below.
+'git checkout' [<branch>]::
+'git checkout' -b|-B <new_branch> [<start point>]::
+'git checkout' [--detach] [<commit>]::
 
-When <paths> are given, this command does *not* switch
-branches.  It updates the named paths in the working tree from
-the index file, or from a named <tree-ish> (most often a commit).  In
-this case, the `-b` and `--track` options are meaningless and giving
-either of them results in an error. The <tree-ish> argument can be
-used to specify a specific tree-ish (i.e. commit, tag or tree)
-to update the index for the given paths before updating the
-working tree.
+	This form switches branches by updating the index, working
+	tree, and HEAD to reflect the specified branch or commit.
++
+If `-b` is given, a new branch is created as if linkgit:git-branch[1]
+were called and then checked out; in this case you can
+use the `--track` or `--no-track` options, which will be passed to
+'git branch'.  As a convenience, `--track` without `-b` implies branch
+creation; see the description of `--track` below.
++
+If `-B` is given, <new_branch> is created if it doesn't exist; otherwise, it
+is reset. This is the transactional equivalent of
++
+------------
+$ git branch -f <branch> [<start point>]
+$ git checkout <branch>
+------------
++
+that is to say, the branch is not reset/created unless "git checkout" is
+successful.
 
-The index may contain unmerged entries after a failed merge.  By
-default, if you try to check out such an entry from the index, the
+'git checkout' [-p|--patch] [<tree-ish>] [--] <pathspec>...::
+
+	When <paths> or `--patch` are given, 'git checkout' does *not*
+	switch branches.  It updates the named paths in the working tree
+	from the index file or from a named <tree-ish> (most often a
+	commit).  In this case, the `-b` and `--track` options are
+	meaningless and giving either of them results in an error.  The
+	<tree-ish> argument can be used to specify a specific tree-ish
+	(i.e.  commit, tag or tree) to update the index for the given
+	paths before updating the working tree.
++
+The index may contain unmerged entries because of a previous failed merge.
+By default, if you try to check out such an entry from the index, the
 checkout operation will fail and nothing will be checked out.
-Using -f will ignore these unmerged entries.  The contents from a
+Using `-f` will ignore these unmerged entries.  The contents from a
 specific side of the merge can be checked out of the index by
-using --ours or --theirs.  With -m, changes made to the working tree
-file can be discarded to recreate the original conflicted merge result.
+using `--ours` or `--theirs`.  With `-m`, changes made to the working tree
+file can be discarded to re-create the original conflicted merge result.
 
 OPTIONS
 -------
 -q::
+--quiet::
 	Quiet, suppress feedback messages.
 
 -f::
+--force::
 	When switching branches, proceed even if the index or the
 	working tree differs from HEAD.  This is used to throw away
 	local changes.
@@ -64,13 +88,19 @@
 	Create a new branch named <new_branch> and start it at
 	<start_point>; see linkgit:git-branch[1] for details.
 
+-B::
+	Creates the branch <new_branch> and start it at <start_point>;
+	if it already exists, then reset it to <start_point>. This is
+	equivalent to running "git branch" with "-f"; see
+	linkgit:git-branch[1] for details.
+
 -t::
 --track::
 	When creating a new branch, set up "upstream" configuration. See
 	"--track" in linkgit:git-branch[1] for details.
 +
 If no '-b' option is given, the name of the new branch will be
-derived from the remote branch.  If "remotes/" or "refs/remotes/"
+derived from the remote-tracking branch.  If "remotes/" or "refs/remotes/"
 is prefixed it is stripped away, and then the part up to the
 next slash (which would be the nickname of the remote) is removed.
 This would tell us to use "hack" as the local branch when branching
@@ -87,6 +117,38 @@
 	Create the new branch's reflog; see linkgit:git-branch[1] for
 	details.
 
+--detach::
+	Rather than checking out a branch to work on it, check out a
+	commit for inspection and discardable experiments.
+	This is the default behavior of "git checkout <commit>" when
+	<commit> is not a branch name.  See the "DETACHED HEAD" section
+	below for details.
+
+--orphan::
+	Create a new 'orphan' branch, named <new_branch>, started from
+	<start_point> and switch to it.  The first commit made on this
+	new branch will have no parents and it will be the root of a new
+	history totally disconnected from all the other branches and
+	commits.
++
+The index and the working tree are adjusted as if you had previously run
+"git checkout <start_point>".  This allows you to start a new history
+that records a set of paths similar to <start_point> by easily running
+"git commit -a" to make the root commit.
++
+This can be useful when you want to publish the tree from a commit
+without exposing its full history. You might want to do this to publish
+an open source branch of a project whose current tree is "clean", but
+whose full history contains proprietary or otherwise encumbered bits of
+code.
++
+If you want to start a disconnected history that records a set of paths
+that is totally different from the one of <start_point>, then you should
+clear the index and the working tree right after creating the orphan
+branch by running "git rm -rf ." from the top level of the working tree.
+Afterwards you will be ready to prepare your new files, repopulating the
+working tree, by copying them from elsewhere, extracting a tarball, etc.
+
 -m::
 --merge::
 	When switching branches,
@@ -113,6 +175,17 @@
 	"merge" (default) and "diff3" (in addition to what is shown by
 	"merge" style, shows the original contents).
 
+-p::
+--patch::
+	Interactively select hunks in the difference between the
+	<tree-ish> (or the index, if unspecified) and the working
+	tree.  The chosen hunks are then applied in reverse to the
+	working tree (and if a <tree-ish> was specified, the index).
++
+This means that you can use `git checkout -p` to selectively discard
+edits from your current working tree. See the ``Interactive Mode''
+section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
+
 <branch>::
 	Branch to checkout; if it refers to a branch (i.e., a name that,
 	when prepended with "refs/heads/", is a valid ref), then that
@@ -120,9 +193,13 @@
 	commit, your HEAD becomes "detached" and you are no longer on
 	any branch (see below for details).
 +
-As a special case, the `"@\{-N\}"` syntax for the N-th last branch
+As a special case, the `"@{-N}"` syntax for the N-th last branch
 checks out the branch (instead of detaching).  You may also specify
-`-` which is synonymous with `"@\{-1\}"`.
+`-` which is synonymous with `"@{-1}"`.
++
+As a further special case, you may use `"A...B"` as a shortcut for the
+merge base of `A` and `B` if there is exactly one merge base. You can
+leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.
 
 <new_branch>::
 	Name for the new branch.
@@ -137,43 +214,141 @@
 
 
 
-Detached HEAD
+DETACHED HEAD
 -------------
-
-It is sometimes useful to be able to 'checkout' a commit that is
-not at the tip of one of your branches.  The most obvious
-example is to check out the commit at a tagged official release
-point, like this:
+HEAD normally refers to a named branch (e.g. 'master'). Meanwhile, each
+branch refers to a specific commit. Let's look at a repo with three
+commits, one of them tagged, and with branch 'master' checked out:
 
 ------------
-$ git checkout v2.6.18
+	   HEAD (refers to branch 'master')
+	    |
+	    v
+a---b---c  branch 'master' (refers to commit 'c')
+    ^
+    |
+  tag 'v2.0' (refers to commit 'b')
 ------------
 
-Earlier versions of git did not allow this and asked you to
-create a temporary branch using the `-b` option, but starting from
-version 1.5.0, the above command 'detaches' your HEAD from the
-current branch and directly points at the commit named by the tag
-(`v2.6.18` in the example above).
-
-You can use all git commands while in this state.  You can use
-`git reset --hard $othercommit` to further move around, for
-example.  You can make changes and create a new commit on top of
-a detached HEAD.  You can even create a merge by using `git
-merge $othercommit`.
-
-The state you are in while your HEAD is detached is not recorded
-by any branch (which is natural --- you are not on any branch).
-What this means is that you can discard your temporary commits
-and merges by switching back to an existing branch (e.g. `git
-checkout master`), and a later `git prune` or `git gc` would
-garbage-collect them.  If you did this by mistake, you can ask
-the reflog for HEAD where you were, e.g.
+When a commit is created in this state, the branch is updated to refer to
+the new commit. Specifically, 'git commit' creates a new commit 'd', whose
+parent is commit 'c', and then updates branch 'master' to refer to new
+commit 'd'. HEAD still refers to branch 'master' and so indirectly now refers
+to commit 'd':
 
 ------------
+$ edit; git add; git commit
+
+	       HEAD (refers to branch 'master')
+		|
+		v
+a---b---c---d  branch 'master' (refers to commit 'd')
+    ^
+    |
+  tag 'v2.0' (refers to commit 'b')
+------------
+
+It is sometimes useful to be able to checkout a commit that is not at
+the tip of any named branch, or even to create a new commit that is not
+referenced by a named branch. Let's look at what happens when we
+checkout commit 'b' (here we show two ways this may be done):
+
+------------
+$ git checkout v2.0  # or
+$ git checkout master^^
+
+   HEAD (refers to commit 'b')
+    |
+    v
+a---b---c---d  branch 'master' (refers to commit 'd')
+    ^
+    |
+  tag 'v2.0' (refers to commit 'b')
+------------
+
+Notice that regardless of which checkout command we use, HEAD now refers
+directly to commit 'b'. This is known as being in detached HEAD state.
+It means simply that HEAD refers to a specific commit, as opposed to
+referring to a named branch. Let's see what happens when we create a commit:
+
+------------
+$ edit; git add; git commit
+
+     HEAD (refers to commit 'e')
+      |
+      v
+      e
+     /
+a---b---c---d  branch 'master' (refers to commit 'd')
+    ^
+    |
+  tag 'v2.0' (refers to commit 'b')
+------------
+
+There is now a new commit 'e', but it is referenced only by HEAD. We can
+of course add yet another commit in this state:
+
+------------
+$ edit; git add; git commit
+
+	 HEAD (refers to commit 'f')
+	  |
+	  v
+      e---f
+     /
+a---b---c---d  branch 'master' (refers to commit 'd')
+    ^
+    |
+  tag 'v2.0' (refers to commit 'b')
+------------
+
+In fact, we can perform all the normal git operations. But, let's look
+at what happens when we then checkout master:
+
+------------
+$ git checkout master
+
+	       HEAD (refers to branch 'master')
+      e---f     |
+     /          v
+a---b---c---d  branch 'master' (refers to commit 'd')
+    ^
+    |
+  tag 'v2.0' (refers to commit 'b')
+------------
+
+It is important to realize that at this point nothing refers to commit
+'f'. Eventually commit 'f' (and by extension commit 'e') will be deleted
+by the routine git garbage collection process, unless we create a reference
+before that happens. If we have not yet moved away from commit 'f',
+any of these will create a reference to it:
+
+------------
+$ git checkout -b foo   <1>
+$ git branch foo        <2>
+$ git tag foo           <3>
+------------
+
+<1> creates a new branch 'foo', which refers to commit 'f', and then
+updates HEAD to refer to branch 'foo'. In other words, we'll no longer
+be in detached HEAD state after this command.
+
+<2> similarly creates a new branch 'foo', which refers to commit 'f',
+but leaves HEAD detached.
+
+<3> creates a new tag 'foo', which refers to commit 'f',
+leaving HEAD detached.
+
+If we have moved away from commit 'f', then we must first recover its object
+name (typically by using git reflog), and then we can create a reference to
+it. For example, to see the last two commits to which HEAD referred, we
+can use either of these commands:
+
+------------
+$ git reflog -2 HEAD # or
 $ git log -g -2 HEAD
 ------------
 
-
 EXAMPLES
 --------
 
@@ -213,7 +388,7 @@
 +
 ------------
 $ git checkout mytopic
-fatal: Entry 'frotz' not uptodate. Cannot merge.
+error: You have local changes to 'frotz'; not switching branches.
 ------------
 +
 You can give the `-m` flag to the command, which would try a
@@ -248,15 +423,6 @@
 $ git add frotz
 ------------
 
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt
index b764130..0e170a5 100644
--- a/Documentation/git-cherry-pick.txt
+++ b/Documentation/git-cherry-pick.txt
@@ -3,34 +3,64 @@
 
 NAME
 ----
-git-cherry-pick - Apply the change introduced by an existing commit
+git-cherry-pick - Apply the changes introduced by some existing commits
 
 SYNOPSIS
 --------
-'git cherry-pick' [--edit] [-n] [-m parent-number] [-s] [-x] <commit>
+[verse]
+'git cherry-pick' [--edit] [-n] [-m parent-number] [-s] [-x] [--ff] <commit>...
+'git cherry-pick' --continue
+'git cherry-pick' --quit
+'git cherry-pick' --abort
 
 DESCRIPTION
 -----------
-Given one existing commit, apply the change the patch introduces, and record a
-new commit that records it.  This requires your working tree to be clean (no
-modifications from the HEAD commit).
+
+Given one or more existing commits, apply the change each one
+introduces, recording a new commit for each.  This requires your
+working tree to be clean (no modifications from the HEAD commit).
+
+When it is not obvious how to apply a change, the following
+happens:
+
+1. The current branch and `HEAD` pointer stay at the last commit
+   successfully made.
+2. The `CHERRY_PICK_HEAD` ref is set to point at the commit that
+   introduced the change that is difficult to apply.
+3. Paths in which the change applied cleanly are updated both
+   in the index file and in your working tree.
+4. For conflicting paths, the index file records up to three
+   versions, as described in the "TRUE MERGE" section of
+   linkgit:git-merge[1].  The working tree files will include
+   a description of the conflict bracketed by the usual
+   conflict markers `<<<<<<<` and `>>>>>>>`.
+5. No other modifications are made.
+
+See linkgit:git-merge[1] for some hints on resolving such
+conflicts.
 
 OPTIONS
 -------
-<commit>::
-	Commit to cherry-pick.
-	For a more complete list of ways to spell commits, see the
-	"SPECIFYING REVISIONS" section in linkgit:git-rev-parse[1].
+<commit>...::
+	Commits to cherry-pick.
+	For a more complete list of ways to spell commits, see
+	linkgit:gitrevisions[7].
+	Sets of commits can be passed but no traversal is done by
+	default, as if the '--no-walk' option was specified, see
+	linkgit:git-rev-list[1]. Note that specifying a range will
+	feed all <commit>... arguments to a single revision walk
+	(see a later example that uses 'maint master..next').
 
 -e::
 --edit::
-	With this option, 'git-cherry-pick' will let you edit the commit
+	With this option, 'git cherry-pick' will let you edit the commit
 	message prior to committing.
 
 -x::
-	When recording the commit, append to the original commit
-	message a note that indicates which commit this change
-	was cherry-picked from.  Append the note only for cherry
+	When recording the commit, append a line that says
+	"(cherry picked from commit ...)" to the original commit
+	message in order to indicate which commit this change was
+	cherry-picked from.  This is done only for cherry
 	picks without conflicts.  Do not use this option if
 	you are cherry-picking from your private branch because
 	the information is useless to the recipient.  If on the
@@ -55,10 +85,10 @@
 
 -n::
 --no-commit::
-	Usually the command automatically creates a commit.
-	This flag applies the change necessary to cherry-pick
-	the named commit to your working tree and the index,
-	but does not make the commit.  In addition, when this
+	Usually the command automatically creates a sequence of commits.
+	This flag applies the changes necessary to cherry-pick
+	each named commit to your working tree and the index,
+	without making any commit.  In addition, when this
 	option is used, your index does not have to match the
 	HEAD commit.  The cherry-pick is done against the
 	beginning state of your index.
@@ -70,14 +100,119 @@
 --signoff::
 	Add Signed-off-by line at the end of the commit message.
 
+--ff::
+	If the current HEAD is the same as the parent of the
+	cherry-pick'ed commit, then a fast forward to this commit will
+	be performed.
 
-Author
-------
-Written by Junio C Hamano <gitster@pobox.com>
+--allow-empty::
+	By default, cherry-picking an empty commit will fail,
+	indicating that an explicit invocation of `git commit
+	--allow-empty` is required. This option overrides that
+	behavior, allowing empty commits to be preserved automatically
+	in a cherry-pick. Note that when "--ff" is in effect, empty
+	commits that meet the "fast-forward" requirement will be kept
+	even without this option.  Note also, that use of this option only
+	keeps commits that were initially empty (i.e. the commit recorded the
+	same tree as its parent).  Commits which are made empty due to a
+	previous commit are dropped.  To force the inclusion of those commits
+	use `--keep-redundant-commits`.
 
-Documentation
---------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
+--keep-redundant-commits::
+	If a commit being cherry picked duplicates a commit already in the
+	current history, it will become empty.  By default these
+	redundant commits are ignored.  This option overrides that behavior and
+	creates an empty commit object.  Implies `--allow-empty`.
+
+--strategy=<strategy>::
+	Use the given merge strategy.  Should only be used once.
+	See the MERGE STRATEGIES section in linkgit:git-merge[1]
+	for details.
+
+-X<option>::
+--strategy-option=<option>::
+	Pass the merge strategy-specific option through to the
+	merge strategy.  See linkgit:git-merge[1] for details.
+
+SEQUENCER SUBCOMMANDS
+---------------------
+include::sequencer.txt[]
+
+EXAMPLES
+--------
+`git cherry-pick master`::
+
+	Apply the change introduced by the commit at the tip of the
+	master branch and create a new commit with this change.
+
+`git cherry-pick ..master`::
+`git cherry-pick ^HEAD master`::
+
+	Apply the changes introduced by all commits that are ancestors
+	of master but not of HEAD to produce new commits.
+
+`git cherry-pick maint next ^master`::
+`git cherry-pick maint master..next`::
+
+	Apply the changes introduced by all commits that are
+	ancestors of maint or next, but not master or any of its
+	ancestors.  Note that the latter does not mean `maint` and
+	everything between `master` and `next`; specifically,
+	`maint` will not be used if it is included in `master`.
+
+`git cherry-pick master~4 master~2`::
+
+	Apply the changes introduced by the fifth and third last
+	commits pointed to by master and create 2 new commits with
+	these changes.
+
+`git cherry-pick -n master~1 next`::
+
+	Apply to the working tree and the index the changes introduced
+	by the second last commit pointed to by master and by the last
+	commit pointed to by next, but do not create any commit with
+	these changes.
+
+`git cherry-pick --ff ..next`::
+
+	If history is linear and HEAD is an ancestor of next, update
+	the working tree and advance the HEAD pointer to match next.
+	Otherwise, apply the changes introduced by those commits that
+	are in next but not HEAD to the current branch, creating a new
+	commit for each new change.
+
+`git rev-list --reverse master -- README | git cherry-pick -n --stdin`::
+
+	Apply the changes introduced by all commits on the master
+	branch that touched README to the working tree and index,
+	so the result can be inspected and made into a single new
+	commit if suitable.
+
+The following sequence attempts to backport a patch, bails out because
+the code the patch applies to has changed too much, and then tries
+again, this time exercising more care about matching up context lines.
+
+------------
+$ git cherry-pick topic^             <1>
+$ git diff                           <2>
+$ git reset --merge ORIG_HEAD        <3>
+$ git cherry-pick -Xpatience topic^  <4>
+------------
+<1> apply the change that would be shown by `git show topic^`.
+In this example, the patch does not apply cleanly, so
+information about the conflict is written to the index and
+working tree and no new commit results.
+<2> summarize changes to be reconciled
+<3> cancel the cherry-pick.  In other words, return to the
+pre-cherry-pick state, preserving any local modifications you had in
+the working tree.
+<4> try to apply the change introduced by `topic^` again,
+spending extra time to avoid mistakes based on incorrectly matching
+context lines.
+
+SEE ALSO
+--------
+linkgit:git-revert[1]
 
 GIT
 ---
diff --git a/Documentation/git-cherry.txt b/Documentation/git-cherry.txt
index 7deefda..f6c19c7 100644
--- a/Documentation/git-cherry.txt
+++ b/Documentation/git-cherry.txt
@@ -7,6 +7,7 @@
 
 SYNOPSIS
 --------
+[verse]
 'git cherry' [-v] [<upstream> [<head> [<limit>]]]
 
 DESCRIPTION
@@ -14,7 +15,7 @@
 The changeset (or "diff") of each commit between the fork-point and <head>
 is compared against each commit between the fork-point and <upstream>.
 The commits are compared with their 'patch id', obtained from
-the 'git-patch-id' program.
+the 'git patch-id' program.
 
 Every commit that doesn't exist in the <upstream> branch
 has its id (sha1) reported, prefixed by a symbol.  The ones that have
@@ -37,8 +38,8 @@
               \__*__*__<limit>__-__+__> <head>
 
 
-Because 'git-cherry' compares the changeset rather than the commit id
-(sha1), you can use 'git-cherry' to find out if a commit you made locally
+Because 'git cherry' compares the changeset rather than the commit id
+(sha1), you can use 'git cherry' to find out if a commit you made locally
 has been applied <upstream> under a different commit id.  For example,
 this will happen if you're feeding patches <upstream> via email rather
 than pushing or pulling commits directly.
@@ -63,14 +64,6 @@
 --------
 linkgit:git-patch-id[1]
 
-Author
-------
-Written by Junio C Hamano <gitster@pobox.com>
-
-Documentation
---------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-citool.txt b/Documentation/git-citool.txt
index 670cb02..c7a11c3 100644
--- a/Documentation/git-citool.txt
+++ b/Documentation/git-citool.txt
@@ -7,6 +7,7 @@
 
 SYNOPSIS
 --------
+[verse]
 'git citool'
 
 DESCRIPTION
@@ -14,19 +15,11 @@
 A Tcl/Tk based graphical interface to review modified files, stage
 them into the index, enter a commit message and record the new
 commit onto the current branch.  This interface is an alternative
-to the less interactive 'git-commit' program.
+to the less interactive 'git commit' program.
 
-'git-citool' is actually a standard alias for `git gui citool`.
+'git citool' is actually a standard alias for `git gui citool`.
 See linkgit:git-gui[1] for more details.
 
-Author
-------
-Written by Shawn O. Pearce <spearce@spearce.org>.
-
-Documentation
---------------
-Documentation by Shawn O. Pearce <spearce@spearce.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-clean.txt b/Documentation/git-clean.txt
index be894af..79fb984 100644
--- a/Documentation/git-clean.txt
+++ b/Documentation/git-clean.txt
@@ -8,7 +8,7 @@
 SYNOPSIS
 --------
 [verse]
-'git clean' [-d] [-f] [-n] [-q] [-x | -X] [--] <path>...
+'git clean' [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <path>...
 
 DESCRIPTION
 -----------
@@ -27,10 +27,14 @@
 -------
 -d::
 	Remove untracked directories in addition to untracked files.
+	If an untracked directory is managed by a different git
+	repository, it is not removed by default.  Use -f option twice
+	if you really want to remove such a directory.
 
 -f::
-	If the git configuration specifies clean.requireForce as true,
-	'git-clean' will refuse to run unless given -f or -n.
+--force::
+	If the git configuration variable clean.requireForce is not set
+	to false, 'git clean' will refuse to run unless given -f or -n.
 
 -n::
 --dry-run::
@@ -41,22 +45,24 @@
 	Be quiet, only report errors, but not the files that are
 	successfully removed.
 
+-e <pattern>::
+--exclude=<pattern>::
+	In addition to those found in .gitignore (per directory) and
+	$GIT_DIR/info/exclude, also consider these patterns to be in the
+	set of the ignore rules in effect.
+
 -x::
-	Don't use the ignore rules.  This allows removing all untracked
+	Don't use the standard ignore rules read from .gitignore (per
+	directory) and $GIT_DIR/info/exclude, but do still use the ignore
+	rules given with `-e` options.  This allows removing all untracked
 	files, including build products.  This can be used (possibly in
-	conjunction with 'git-reset') to create a pristine
+	conjunction with 'git reset') to create a pristine
 	working directory to test a clean build.
 
 -X::
 	Remove only files ignored by git.  This may be useful to rebuild
 	everything from scratch, but keep manually created files.
 
-
-Author
-------
-Written by Pavel Roskin <proski@gnu.org>
-
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index b14de6c..c1ddd4c 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -11,16 +11,20 @@
 [verse]
 'git clone' [--template=<template_directory>]
 	  [-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [--mirror]
-	  [-o <name>] [-u <upload-pack>] [--reference <repository>]
-	  [--depth <depth>] [--] <repository> [<directory>]
+	  [-o <name>] [-b <name>] [-u <upload-pack>] [--reference <repository>]
+	  [--separate-git-dir <git dir>]
+	  [--depth <depth>] [--[no-]single-branch]
+	  [--recursive|--recurse-submodules] [--] <repository>
+	  [<directory>]
 
 DESCRIPTION
 -----------
 
 Clones a repository into a newly created directory, creates
 remote-tracking branches for each branch in the cloned repository
-(visible using `git branch -r`), and creates and checks out an initial
-branch equal to the cloned repository's currently active branch.
+(visible using `git branch -r`), and creates and checks out an
+initial branch that is forked from the cloned repository's
+currently active branch.
 
 After the clone, a plain `git fetch` without arguments will update
 all the remote-tracking branches, and a `git pull` without
@@ -28,7 +32,7 @@
 current master branch, if any.
 
 This default configuration is achieved by creating references to
-the remote branch heads under `$GIT_DIR/refs/remotes/origin` and
+the remote branch heads under `refs/remotes/origin` and
 by initializing `remote.origin.url` and `remote.origin.fetch`
 configuration variables.
 
@@ -38,17 +42,22 @@
 --local::
 -l::
 	When the repository to clone from is on a local machine,
-	this flag bypasses normal "git aware" transport
+	this flag bypasses the normal "git aware" transport
 	mechanism and clones the repository by making a copy of
 	HEAD and everything under objects and refs directories.
 	The files under `.git/objects/` directory are hardlinked
-	to save space when possible.  This is now the default when
-	the source repository is specified with `/path/to/repo`
-	syntax, so it essentially is a no-op option.  To force
-	copying instead of hardlinking (which may be desirable
-	if you are trying to make a back-up of your repository),
-	but still avoid the usual "git aware" transport
-	mechanism, `--no-hardlinks` can be used.
+	to save space when possible.
++
+If the repository is specified as a local path (e.g., `/path/to/repo`),
+this is the default, and --local is essentially a no-op.  If the
+repository is specified as a URL, then this flag is ignored (and we
+never use the local optimizations).  Specifying `--no-local` will
+override the default when `/path/to/repo` is given, using the regular
+git transport instead.
++
+To force copying instead of hardlinking (which may be desirable if you
+are trying to make a back-up of your repository), but still avoid the
+usual "git aware" transport mechanism, `--no-hardlinks` can be used.
 
 --no-hardlinks::
 	Optimize the cloning process from a repository on a
@@ -59,7 +68,7 @@
 -s::
 	When the repository to clone is on the local machine,
 	instead of using hard links, automatically setup
-	.git/objects/info/alternates to share the objects
+	`.git/objects/info/alternates` to share the objects
 	with the source repository.  The resulting repository
 	starts out without any object of its own.
 +
@@ -68,32 +77,47 @@
 repository using this option and then delete branches (or use any
 other git command that makes any existing commit unreferenced) in the
 source repository, some objects may become unreferenced (or dangling).
-These objects may be removed by normal git operations (such as 'git-commit')
+These objects may be removed by normal git operations (such as `git commit`)
 which automatically call `git gc --auto`. (See linkgit:git-gc[1].)
 If these objects are removed and were referenced by the cloned repository,
 then the cloned repository will become corrupt.
-
-
++
+Note that running `git repack` without the `-l` option in a repository
+cloned with `-s` will copy objects from the source repository into a pack
+in the cloned repository, removing the disk space savings of `clone -s`.
+It is safe, however, to run `git gc`, which uses the `-l` option by
+default.
++
+If you want to break the dependency of a repository cloned with `-s` on
+its source repository, you can simply run `git repack -a` to copy all
+objects from the source repository into a pack in the cloned repository.
 
 --reference <repository>::
-	If the reference repository is on the local machine
-	automatically setup .git/objects/info/alternates to
+	If the reference repository is on the local machine,
+	automatically setup `.git/objects/info/alternates` to
 	obtain objects from the reference repository.  Using
 	an already existing repository as an alternate will
 	require fewer objects to be copied from the repository
 	being cloned, reducing network and local storage costs.
 +
-*NOTE*: see NOTE to --shared option.
+*NOTE*: see the NOTE for the `--shared` option.
 
 --quiet::
 -q::
-	Operate quietly.  This flag is also passed to the `rsync'
+	Operate quietly.  Progress is not reported to the standard
+	error stream. This flag is also passed to the `rsync'
 	command when given.
 
 --verbose::
 -v::
-	Display the progressbar, even in case the standard output is not
-	a terminal.
+	Run verbosely. Does not affect the reporting of progress status
+	to the standard error stream.
+
+--progress::
+	Progress status is reported on the standard error stream
+	by default when it is attached to a terminal, unless -q
+	is specified. This flag forces progress status even if the
+	standard error stream is not directed to a terminal.
 
 --no-checkout::
 -n::
@@ -112,12 +136,25 @@
 	configuration variables are created.
 
 --mirror::
-	Set up a mirror of the remote repository.  This implies --bare.
+	Set up a mirror of the source repository.  This implies `--bare`.
+	Compared to `--bare`, `--mirror` not only maps local branches of the
+	source to local branches of the target, it maps all refs (including
+	remote-tracking branches, notes etc.) and sets up a refspec configuration such
+	that all these refs are overwritten by a `git remote update` in the
+	target repository.
 
 --origin <name>::
 -o <name>::
-	Instead of using the remote name 'origin' to keep track
-	of the upstream repository, use <name>.
+	Instead of using the remote name `origin` to keep track
+	of the upstream repository, use `<name>`.
+
+--branch <name>::
+-b <name>::
+	Instead of pointing the newly created HEAD to the branch pointed
+	to by the cloned repository's HEAD, point to `<name>` branch
+	instead. `--branch` can also take tags and treat them like
+	detached HEAD. In a non-bare repository, this is the branch
+	that will be checked out.
 
 --upload-pack <upload-pack>::
 -u <upload-pack>::
@@ -127,8 +164,18 @@
 
 --template=<template_directory>::
 	Specify the directory from which templates will be used;
-	if unset the templates are taken from the installation
-	defined default, typically `/usr/share/git-core/templates`.
+	(See the "TEMPLATE DIRECTORY" section of linkgit:git-init[1].)
+
+--config <key>=<value>::
+-c <key>=<value>::
+	Set a configuration variable in the newly-created repository;
+	this takes effect immediately after the repository is
+	initialized, but before the remote history is fetched or any
+	files checked out.  The key is in the same format as expected by
+	linkgit:git-config[1] (e.g., `core.eol=true`). If multiple
+	values are given for the same key, each value will be written to
+	the config file. This makes it safe, for example, to add
+	additional fetch refspecs to the origin remote.
 
 --depth <depth>::
 	Create a 'shallow' clone with a history truncated to the
@@ -139,6 +186,31 @@
 	with a long history, and would want to send in fixes
 	as patches.
 
+--single-branch::
+	Clone only the history leading to the tip of a single branch,
+	either specified by the `--branch` option or the primary
+	branch remote's `HEAD` points at. When creating a shallow
+	clone with the `--depth` option, this is the default, unless
+	`--no-single-branch` is given to fetch the histories near the
+	tips of all branches.
+
+--recursive::
+--recurse-submodules::
+	After the clone is created, initialize all submodules within,
+	using their default settings. This is equivalent to running
+	`git submodule update --init --recursive` immediately after
+	the clone is finished. This option is ignored if the cloned
+	repository does not have a worktree/checkout (i.e. if any of
+	`--no-checkout`/`-n`, `--bare`, or `--mirror` is given)
+
+--separate-git-dir=<git dir>::
+	Instead of placing the cloned repository where it is supposed
+	to be, place the cloned repository at the specified directory,
+	then make a filesytem-agnostic git symbolic link to there.
+	The result is git repository can be separated from working
+	tree.
+
+
 <repository>::
 	The (possibly remote) repository to clone from.  See the
 	<<URLS,URLS>> section below for more information on specifying
@@ -147,8 +219,8 @@
 <directory>::
 	The name of a new directory to clone into.  The "humanish"
 	part of the source repository is used if no directory is
-	explicitly given ("repo" for "/path/to/repo.git" and "foo"
-	for "host.xz:foo/.git").  Cloning into an existing directory
+	explicitly given (`repo` for `/path/to/repo.git` and `foo`
+	for `host.xz:foo/.git`).  Cloning into an existing directory
 	is only allowed if the directory is empty.
 
 :git-clone: 1
@@ -157,7 +229,7 @@
 Examples
 --------
 
-Clone from upstream::
+* Clone from upstream:
 +
 ------------
 $ git clone git://git.kernel.org/pub/scm/.../linux-2.6 my2.6
@@ -166,7 +238,7 @@
 ------------
 
 
-Make a local clone that borrows from the current directory, without checking things out::
+* Make a local clone that borrows from the current directory, without checking things out:
 +
 ------------
 $ git clone -l -s -n . ../copy
@@ -175,7 +247,7 @@
 ------------
 
 
-Clone from upstream while borrowing from an existing local directory::
+* Clone from upstream while borrowing from an existing local directory:
 +
 ------------
 $ git clone --reference my2.6 \
@@ -185,31 +257,20 @@
 ------------
 
 
-Create a bare repository to publish your changes to the public::
+* Create a bare repository to publish your changes to the public:
 +
 ------------
 $ git clone --bare -l /home/proj/.git /pub/scm/proj.git
 ------------
 
 
-Create a repository on the kernel.org machine that borrows from Linus::
+* Create a repository on the kernel.org machine that borrows from Linus:
 +
 ------------
 $ git clone --bare -l -s /pub/scm/.../torvalds/linux-2.6.git \
     /pub/scm/.../me/subsys-2.6.git
 ------------
 
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-
-Documentation
---------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
-
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-column.txt b/Documentation/git-column.txt
new file mode 100644
index 0000000..5d6f1cc
--- /dev/null
+++ b/Documentation/git-column.txt
@@ -0,0 +1,53 @@
+git-column(1)
+=============
+
+NAME
+----
+git-column - Display data in columns
+
+SYNOPSIS
+--------
+[verse]
+'git column' [--command=<name>] [--[raw-]mode=<mode>] [--width=<width>]
+	     [--indent=<string>] [--nl=<string>] [--padding=<n>]
+
+DESCRIPTION
+-----------
+This command formats its input into multiple columns.
+
+OPTIONS
+-------
+--command=<name>::
+	Look up layout mode using configuration variable column.<name> and
+	column.ui.
+
+--mode=<mode>::
+	Specify layout mode. See configuration variable column.ui for option
+	syntax.
+
+--raw-mode=<n>::
+	Same as --mode but take mode encoded as a number. This is mainly used
+	by other commands that have already parsed layout mode.
+
+--width=<width>::
+	Specify the terminal width. By default 'git column' will detect the
+	terminal width, or fall back to 80 if it is unable to do so.
+
+--indent=<string>::
+	String to be printed at the beginning of each line.
+
+--nl=<N>::
+	String to be printed at the end of each line,
+	including newline character.
+
+--padding=<N>::
+	The number of spaces between columns. One space by default.
+
+
+Author
+------
+Written by Nguyen Thai Ngoc Duy <pclouds@gmail.com>
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-commit-tree.txt b/Documentation/git-commit-tree.txt
index b8834ba..6d5a04c 100644
--- a/Documentation/git-commit-tree.txt
+++ b/Documentation/git-commit-tree.txt
@@ -8,7 +8,9 @@
 
 SYNOPSIS
 --------
-'git commit-tree' <tree> [-p <parent commit>]\* < changelog
+[verse]
+'git commit-tree' <tree> [(-p <parent>)...] < changelog
+'git commit-tree' [(-p <parent>)...] [(-m <message>)...] [(-F <file>)...] <tree>
 
 DESCRIPTION
 -----------
@@ -16,7 +18,8 @@
 linkgit:git-commit[1] instead.
 
 Creates a new commit object based on the provided tree object and
-emits the new commit object id on stdout.
+emits the new commit object id on stdout. The log message is read
+from the standard input, unless `-m` or `-F` options are given.
 
 A commit object may have any number of parents. With exactly one
 parent, it is an ordinary commit. Having more than one parent makes
@@ -38,9 +41,17 @@
 <tree>::
 	An existing tree object
 
--p <parent commit>::
+-p <parent>::
 	Each '-p' indicates the id of a parent commit object.
 
+-m <message>::
+	A paragraph in the commit log message. This can be given more than
+	once and each <message> becomes its own paragraph.
+
+-F <file>::
+	Read the commit log message from the given file. Use `-` to read
+	from the standard input.
+
 
 Commit Information
 ------------------
@@ -67,40 +78,29 @@
 
 In case (some of) these environment variables are not set, the information
 is taken from the configuration items user.name and user.email, or, if not
-present, system user name and fully qualified hostname.
+present, system user name and the hostname used for outgoing mail (taken
+from `/etc/mailname` and falling back to the fully qualified hostname when
+that file does not exist).
 
 A commit comment is read from stdin. If a changelog
-entry is not provided via "<" redirection, 'git-commit-tree' will just wait
+entry is not provided via "<" redirection, 'git commit-tree' will just wait
 for one to be entered and terminated with ^D.
 
-
-Diagnostics
------------
-You don't exist. Go away!::
-    The passwd(5) gecos field couldn't be read
-Your parents must have hated you!::
-    The passwd(5) gecos field is longer than a giant static buffer.
-Your sysadmin must hate you!::
-    The passwd(5) name field is longer than a giant static buffer.
+include::date-formats.txt[]
 
 Discussion
 ----------
 
 include::i18n.txt[]
 
+FILES
+-----
+/etc/mailname
+
 SEE ALSO
 --------
 linkgit:git-write-tree[1]
 
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index b5d81be..f400835 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -8,10 +8,12 @@
 SYNOPSIS
 --------
 [verse]
-'git commit' [-a | --interactive] [-s] [-v] [-u<mode>] [--amend]
-	   [(-c | -C) <commit>] [-F <file> | -m <msg>]
-	   [--allow-empty] [--no-verify] [-e] [--author=<author>]
-	   [--cleanup=<mode>] [--] [[-i | -o ]<file>...]
+'git commit' [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]
+	   [--dry-run] [(-c | -C | --fixup | --squash) <commit>]
+	   [-F <file> | -m <msg>] [--reset-author] [--allow-empty]
+	   [--allow-empty-message] [--no-verify] [-e] [--author=<author>]
+	   [--date=<date>] [--cleanup=<mode>] [--status | --no-status]
+	   [-i | -o] [--] [<file>...]
 
 DESCRIPTION
 -----------
@@ -20,11 +22,11 @@
 
 The content to be added can be specified in several ways:
 
-1. by using 'git-add' to incrementally "add" changes to the
+1. by using 'git add' to incrementally "add" changes to the
    index before using the 'commit' command (Note: even modified
    files must be "added");
 
-2. by using 'git-rm' to remove files from the working tree
+2. by using 'git rm' to remove files from the working tree
    and the index, again before using the 'commit' command;
 
 3. by listing files as arguments to the 'commit' command, in which
@@ -38,17 +40,17 @@
    that have been removed from the working tree, and then perform the
    actual commit;
 
-5. by using the --interactive switch with the 'commit' command to decide one
-   by one which files should be part of the commit, before finalizing the
-   operation.  Currently, this is done by invoking 'git-add --interactive'.
+5. by using the --interactive or --patch switches with the 'commit' command
+   to decide one by one which files or hunks should be part of the commit,
+   before finalizing the operation. See the ``Interactive Mode'' section of
+   linkgit:git-add[1] to learn how to operate these modes.
 
-The 'git-status' command can be used to obtain a
+The `--dry-run` option can be used to obtain a
 summary of what is included by any of the above for the next
-commit by giving the same set of parameters you would give to
-this command.
+commit by giving the same set of parameters (options and paths).
 
 If you make a commit and then find a mistake immediately after
-that, you can recover from it with 'git-reset'.
+that, you can recover from it with 'git reset'.
 
 
 OPTIONS
@@ -59,6 +61,12 @@
 	been modified and deleted, but new files you have not
 	told git about are not affected.
 
+-p::
+--patch::
+	Use the interactive patch selection interface to chose
+	which changes to commit. See linkgit:git-add[1] for
+	details.
+
 -C <commit>::
 --reuse-message=<commit>::
 	Take an existing commit object, and reuse the log message
@@ -70,16 +78,57 @@
 	Like '-C', but with '-c' the editor is invoked, so that
 	the user can further edit the commit message.
 
+--fixup=<commit>::
+	Construct a commit message for use with `rebase --autosquash`.
+	The commit message will be the subject line from the specified
+	commit with a prefix of "fixup! ".  See linkgit:git-rebase[1]
+	for details.
+
+--squash=<commit>::
+	Construct a commit message for use with `rebase --autosquash`.
+	The commit message subject line is taken from the specified
+	commit with a prefix of "squash! ".  Can be used with additional
+	commit message options (`-m`/`-c`/`-C`/`-F`). See
+	linkgit:git-rebase[1] for details.
+
+--reset-author::
+	When used with -C/-c/--amend options, or when committing after a
+	a conflicting cherry-pick, declare that the authorship of the
+	resulting commit now belongs of the committer. This also renews
+	the author timestamp.
+
+--short::
+	When doing a dry-run, give the output in the short-format. See
+	linkgit:git-status[1] for details. Implies `--dry-run`.
+
+--branch::
+	Show the branch and tracking info even in short-format.
+
+--porcelain::
+	When doing a dry-run, give the output in a porcelain-ready
+	format. See linkgit:git-status[1] for details. Implies
+	`--dry-run`.
+
+-z::
+--null::
+	When showing `short` or `porcelain` status output, terminate
+	entries in the status output with NUL, instead of LF. If no
+	format is given, implies the `--porcelain` output format.
+
 -F <file>::
 --file=<file>::
 	Take the commit message from the given file.  Use '-' to
 	read the message from the standard input.
 
 --author=<author>::
-	Override the author name used in the commit.  You can use the
-	standard `A U Thor <author@example.com>` format.  Otherwise,
-	an existing commit that matches the given string and its author
-	name is used.
+	Override the commit author. Specify an explicit author using the
+	standard `A U Thor <author@example.com>` format. Otherwise <author>
+	is assumed to be a pattern and is used to search for an existing
+	commit by that author (i.e. rev-list --all -i --author=<author>);
+	the commit author is then copied from the first such commit found.
+
+--date=<date>::
+	Override the author date used in the commit.
 
 -m <msg>::
 --message=<msg>::
@@ -87,11 +136,14 @@
 
 -t <file>::
 --template=<file>::
-	Use the contents of the given file as the initial version
-	of the commit message. The editor is invoked and you can
-	make subsequent changes. If a message is specified using
-	the `-m` or `-F` options, this option has no effect. This
-	overrides the `commit.template` configuration variable.
+	When editing the commit message, start the editor with the
+	contents in the given file.  The `commit.template` configuration
+	variable is often used to give this option implicitly to the
+	command.  This mechanism can be used by projects that want to
+	guide participants with some hints on what to write in the message
+	in what order.  If the user exits the editor without editing the
+	message, the commit is aborted.  This has no effect when a message
+	is given by other means, e.g. with the `-m` or `-F` options.
 
 -s::
 --signoff::
@@ -107,7 +159,13 @@
 	Usually recording a commit that has the exact same tree as its
 	sole parent commit is a mistake, and the command prevents you
 	from making such a commit.  This option bypasses the safety, and
-	is primarily for use by foreign scm interface scripts.
+	is primarily for use by foreign SCM interface scripts.
+
+--allow-empty-message::
+       Like --allow-empty this command is primarily for use by foreign
+       SCM interface scripts. It allows you to create a commit with an
+       empty commit message without using plumbing commands like
+       linkgit:git-commit-tree[1].
 
 --cleanup=<mode>::
 	This option sets how the commit message is cleaned up.
@@ -135,6 +193,10 @@
 	current tip -- if it was a merge, it will have the parents of
 	the current tip as parents -- so the current top commit is
 	discarded.
+
+--no-post-rewrite::
+	Bypass the post-rewrite hook.
+
 +
 --
 It is a rough equivalent for:
@@ -163,7 +225,7 @@
 	Make a commit only from the paths specified on the
 	command line, disregarding any contents that have been
 	staged so far. This is the default mode of operation of
-	'git-commit' if any paths are given on the command line,
+	'git commit' if any paths are given on the command line,
 	in which case this option can be omitted.
 	If this option is specified together with '--amend', then
 	no paths need to be specified, which can be used to amend
@@ -172,20 +234,20 @@
 
 -u[<mode>]::
 --untracked-files[=<mode>]::
-	Show untracked files (Default: 'all').
+	Show untracked files.
 +
-The mode parameter is optional, and is used to specify
-the handling of untracked files. The possible options are:
+The mode parameter is optional (defaults to 'all'), and is used to
+specify the handling of untracked files; when -u is not used, the
+default is 'normal', i.e. show untracked files and directories.
 +
---
+The possible options are:
++
 	- 'no'     - Show no untracked files
 	- 'normal' - Shows untracked files and directories
 	- 'all'    - Also shows individual files in untracked directories.
---
 +
-See linkgit:git-config[1] for configuration variable
-used to change the default for when the option is not
-specified.
+The default can be changed using the status.showUntrackedFiles
+configuration variable documented in linkgit:git-config[1].
 
 -v::
 --verbose::
@@ -198,6 +260,22 @@
 --quiet::
 	Suppress commit summary message.
 
+--dry-run::
+	Do not create a commit, but show a list of paths that are
+	to be committed, paths with local changes that will be left
+	uncommitted and paths that are untracked.
+
+--status::
+	Include the output of linkgit:git-status[1] in the commit
+	message template when using an editor to prepare the commit
+	message.  Defaults to on, but can be used to override
+	configuration variable commit.status.
+
+--no-status::
+	Do not include the output of linkgit:git-status[1] in the
+	commit message template when using an editor to prepare the
+	default commit message.
+
 \--::
 	Do not interpret any more arguments as options.
 
@@ -208,15 +286,17 @@
 	these files are also staged for the next commit on top
 	of what have been staged before.
 
+:git-commit: 1
+include::date-formats.txt[]
 
 EXAMPLES
 --------
 When recording your own work, the contents of modified files in
 your working tree are temporarily stored to a staging area
-called the "index" with 'git-add'.  A file can be
+called the "index" with 'git add'.  A file can be
 reverted back, only in the index but not in the working tree,
 to that of the last commit with `git reset HEAD -- <file>`,
-which effectively reverts 'git-add' and prevents the changes to
+which effectively reverts 'git add' and prevents the changes to
 this file from participating in the next commit.  After building
 the state to be committed incrementally with these commands,
 `git commit` (without any pathname parameter) is used to record what
@@ -272,13 +352,13 @@
 this second commit would record the changes to `hello.c` and
 `hello.h` as expected.
 
-After a merge (initiated by 'git-merge' or 'git-pull') stops
+After a merge (initiated by 'git merge' or 'git pull') stops
 because of conflicts, cleanly merged
 paths are already staged to be committed for you, and paths that
 conflicted are left in unmerged state.  You would have to first
-check which paths are conflicting with 'git-status'
+check which paths are conflicting with 'git status'
 and after fixing them manually in your working tree, you would
-stage the result as usual with 'git-add':
+stage the result as usual with 'git add':
 
 ------------
 $ git status | grep unmerged
@@ -319,7 +399,7 @@
 The editor used to edit the commit log message will be chosen from the
 GIT_EDITOR environment variable, the core.editor configuration variable, the
 VISUAL environment variable, or the EDITOR environment variable (in that
-order).
+order).  See linkgit:git-var[1] for details.
 
 HOOKS
 -----
@@ -336,12 +416,6 @@
 linkgit:git-merge[1],
 linkgit:git-commit-tree[1]
 
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org> and
-Junio C Hamano <gitster@pobox.com>
-
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt
index f68b198..2d6ef32 100644
--- a/Documentation/git-config.txt
+++ b/Documentation/git-config.txt
@@ -37,28 +37,35 @@
 you want to handle the lines that do *not* match the regex, just
 prepend a single exclamation mark in front (see also <<EXAMPLES>>).
 
-The type specifier can be either '--int' or '--bool', which will make
-'git-config' ensure that the variable(s) are of the given type and
+The type specifier can be either '--int' or '--bool', to make
+'git config' ensure that the variable(s) are of the given type and
 convert the value to the canonical form (simple decimal number for int,
-a "true" or "false" string for bool).  If no type specifier is passed,
-no checks or transformations are performed on the value.
+a "true" or "false" string for bool), or '--path', which does some
+path expansion (see '--path' below).  If no type specifier is passed, no
+checks or transformations are performed on the value.
 
-The file-option can be one of '--system', '--global' or '--file'
-which specify where the values will be read from or written to.
-The default is to assume the config file of the current repository,
-.git/config unless defined otherwise with GIT_DIR and GIT_CONFIG
-(see <<FILES>>).
+When reading, the values are read from the system, global and
+repository local configuration files by default, and options
+'--system', '--global', '--local' and '--file <filename>' can be
+used to tell the command to read from only that location (see <<FILES>>).
 
-This command will fail if:
+When writing, the new value is written to the repository local
+configuration file by default, and options '--system', '--global',
+'--file <filename>' can be used to tell the command to write to
+that location (you can say '--local' but that is the default).
 
-. The config file is invalid,
-. Can not write to the config file,
-. no section was provided,
-. the section or key is invalid,
-. you try to unset an option which does not exist,
-. you try to unset/set an option for which multiple lines match, or
-. you use '--global' option without $HOME being properly set.
+This command will fail (with exit code ret) if:
 
+. The config file is invalid (ret=3),
+. can not write to the config file (ret=4),
+. no section or name was provided (ret=2),
+. the section or key is invalid (ret=1),
+. you try to unset an option which does not exist (ret=5),
+. you try to unset/set an option for which multiple lines match (ret=5),
+. you try to use an invalid regexp (ret=6), or
+. you use '--global' option without $HOME being properly set (ret=128).
+
+On success, the command returns the exit code 0.
 
 OPTIONS
 -------
@@ -82,15 +89,19 @@
 	is not exactly one.
 
 --get-regexp::
-	Like --get-all, but interprets the name as a regular expression.
-	Also outputs the key names.
+	Like --get-all, but interprets the name as a regular expression and
+	writes out the key names.  Regular expression matching is currently
+	case-sensitive and done against a canonicalized version of the key
+	in which section and variable names are lowercased, but subsection
+	names are not.
 
 --global::
 	For writing options: write to global ~/.gitconfig file rather than
-	the repository .git/config.
+	the repository .git/config, write to $XDG_CONFIG_HOME/git/config file
+	if this file exists and the ~/.gitconfig file doesn't.
 +
-For reading options: read only from global ~/.gitconfig rather than
-from all available files.
+For reading options: read only from global ~/.gitconfig and from
+$XDG_CONFIG_HOME/git/config rather than from all available files.
 +
 See also <<FILES>>.
 
@@ -124,18 +135,25 @@
 	List all variables set in config file.
 
 --bool::
-	'git-config' will ensure that the output is "true" or "false"
+	'git config' will ensure that the output is "true" or "false"
 
 --int::
-	'git-config' will ensure that the output is a simple
+	'git config' will ensure that the output is a simple
 	decimal number.  An optional value suffix of 'k', 'm', or 'g'
 	in the config file will cause the value to be multiplied
 	by 1024, 1048576, or 1073741824 prior to output.
 
 --bool-or-int::
-	'git-config' will ensure that the output matches the format of
+	'git config' will ensure that the output matches the format of
 	either --bool or --int, as described above.
 
+--path::
+	'git-config' will expand leading '{tilde}' to the value of
+	'$HOME', and '{tilde}user' to the home directory for the
+	specified user.  This option has no effect when setting the
+	value (but you can use 'git config bla {tilde}/' from the
+	command line to let your shell do the expansion).
+
 -z::
 --null::
 	For all options that output values and/or keys, always
@@ -168,34 +186,45 @@
 	Opens an editor to modify the specified config file; either
 	'--system', '--global', or repository (default).
 
+--includes::
+--no-includes::
+	Respect `include.*` directives in config files when looking up
+	values. Defaults to on.
+
 [[FILES]]
 FILES
 -----
 
-If not set explicitly with '--file', there are three files where
-'git-config' will search for configuration options:
+If not set explicitly with '--file', there are four files where
+'git config' will search for configuration options:
 
 $GIT_DIR/config::
-	Repository specific configuration file. (The filename is
-	of course relative to the repository root, not the working
-	directory.)
+	Repository specific configuration file.
 
 ~/.gitconfig::
 	User-specific configuration file. Also called "global"
 	configuration file.
 
+$XDG_CONFIG_HOME/git/config::
+	Second user-specific configuration file. If $XDG_CONFIG_HOME is not set
+	or empty, $HOME/.config/git/config will be used. Any single-valued
+	variable set in this file will be overwritten by whatever is in
+	~/.gitconfig.  It is a good idea not to create this file if
+	you sometimes use older versions of Git, as support for this
+	file was added fairly recently.
+
 $(prefix)/etc/gitconfig::
 	System-wide configuration file.
 
 If no further options are given, all reading options will read all of these
 files that are available. If the global or the system-wide configuration
 file are not available they will be ignored. If the repository configuration
-file is not available or readable, 'git-config' will exit with a non-zero
+file is not available or readable, 'git config' will exit with a non-zero
 error code. However, in neither case will an error message be issued.
 
 All writing options will per default write to the repository specific
 configuration file. Note that this also affects options like '--replace-all'
-and '--unset'. *'git-config' will only ever change one file at a time*.
+and '--unset'. *'git config' will only ever change one file at a time*.
 
 You can override these rules either by command line options or by environment
 variables. The '--global' and the '--system' options will limit the file used
@@ -328,15 +357,6 @@
 
 include::config.txt[]
 
-
-Author
-------
-Written by Johannes Schindelin <Johannes.Schindelin@gmx.de>
-
-Documentation
---------------
-Documentation by Johannes Schindelin, Petr Baudis and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-count-objects.txt b/Documentation/git-count-objects.txt
index 6bc1c21..23c80ce 100644
--- a/Documentation/git-count-objects.txt
+++ b/Documentation/git-count-objects.txt
@@ -7,6 +7,7 @@
 
 SYNOPSIS
 --------
+[verse]
 'git count-objects' [-v]
 
 DESCRIPTION
@@ -25,15 +26,6 @@
 	and number of objects that can be removed by running
 	`git prune-packed`.
 
-
-Author
-------
-Written by Junio C Hamano <gitster@pobox.com>
-
-Documentation
---------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-credential-cache--daemon.txt b/Documentation/git-credential-cache--daemon.txt
new file mode 100644
index 0000000..11edc5a
--- /dev/null
+++ b/Documentation/git-credential-cache--daemon.txt
@@ -0,0 +1,26 @@
+git-credential-cache--daemon(1)
+===============================
+
+NAME
+----
+git-credential-cache--daemon - temporarily store user credentials in memory
+
+SYNOPSIS
+--------
+[verse]
+git credential-cache--daemon <socket>
+
+DESCRIPTION
+-----------
+
+NOTE: You probably don't want to invoke this command yourself; it is
+started automatically when you use linkgit:git-credential-cache[1].
+
+This command listens on the Unix domain socket specified by `<socket>`
+for `git-credential-cache` clients. Clients may store and retrieve
+credentials. Each credential is held for a timeout specified by the
+client; once no credentials are held, the daemon exits.
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-credential-cache.txt b/Documentation/git-credential-cache.txt
new file mode 100644
index 0000000..f3d09c5
--- /dev/null
+++ b/Documentation/git-credential-cache.txt
@@ -0,0 +1,77 @@
+git-credential-cache(1)
+=======================
+
+NAME
+----
+git-credential-cache - helper to temporarily store passwords in memory
+
+SYNOPSIS
+--------
+-----------------------------
+git config credential.helper 'cache [options]'
+-----------------------------
+
+DESCRIPTION
+-----------
+
+This command caches credentials in memory for use by future git
+programs. The stored credentials never touch the disk, and are forgotten
+after a configurable timeout.  The cache is accessible over a Unix
+domain socket, restricted to the current user by filesystem permissions.
+
+You probably don't want to invoke this command directly; it is meant to
+be used as a credential helper by other parts of git. See
+linkgit:gitcredentials[7] or `EXAMPLES` below.
+
+OPTIONS
+-------
+
+--timeout <seconds>::
+
+	Number of seconds to cache credentials (default: 900).
+
+--socket <path>::
+
+	Use `<path>` to contact a running cache daemon (or start a new
+	cache daemon if one is not started). Defaults to
+	`~/.git-credential-cache/socket`. If your home directory is on a
+	network-mounted filesystem, you may need to change this to a
+	local filesystem.
+
+CONTROLLING THE DAEMON
+----------------------
+
+If you would like the daemon to exit early, forgetting all cached
+credentials before their timeout, you can issue an `exit` action:
+
+--------------------------------------
+git credential-cache exit
+--------------------------------------
+
+EXAMPLES
+--------
+
+The point of this helper is to reduce the number of times you must type
+your username or password. For example:
+
+------------------------------------
+$ git config credential.helper cache
+$ git push http://example.com/repo.git
+Username: <type your username>
+Password: <type your password>
+
+[work for 5 more minutes]
+$ git push http://example.com/repo.git
+[your credentials are used automatically]
+------------------------------------
+
+You can provide options via the credential.helper configuration
+variable (this example drops the cache time to 5 minutes):
+
+-------------------------------------------------------
+$ git config credential.helper 'cache --timeout=300'
+-------------------------------------------------------
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-credential-store.txt b/Documentation/git-credential-store.txt
new file mode 100644
index 0000000..3109346
--- /dev/null
+++ b/Documentation/git-credential-store.txt
@@ -0,0 +1,75 @@
+git-credential-store(1)
+=======================
+
+NAME
+----
+git-credential-store - helper to store credentials on disk
+
+SYNOPSIS
+--------
+-------------------
+git config credential.helper 'store [options]'
+-------------------
+
+DESCRIPTION
+-----------
+
+NOTE: Using this helper will store your passwords unencrypted on disk,
+protected only by filesystem permissions. If this is not an acceptable
+security tradeoff, try linkgit:git-credential-cache[1], or find a helper
+that integrates with secure storage provided by your operating system.
+
+This command stores credentials indefinitely on disk for use by future
+git programs.
+
+You probably don't want to invoke this command directly; it is meant to
+be used as a credential helper by other parts of git. See
+linkgit:gitcredentials[7] or `EXAMPLES` below.
+
+OPTIONS
+-------
+
+--store=<path>::
+
+	Use `<path>` to store credentials. The file will have its
+	filesystem permissions set to prevent other users on the system
+	from reading it, but will not be encrypted or otherwise
+	protected. Defaults to `~/.git-credentials`.
+
+EXAMPLES
+--------
+
+The point of this helper is to reduce the number of times you must type
+your username or password. For example:
+
+------------------------------------------
+$ git config credential.helper store
+$ git push http://example.com/repo.git
+Username: <type your username>
+Password: <type your password>
+
+[several days later]
+$ git push http://example.com/repo.git
+[your credentials are used automatically]
+------------------------------------------
+
+STORAGE FORMAT
+--------------
+
+The `.git-credentials` file is stored in plaintext. Each credential is
+stored on its own line as a URL like:
+
+------------------------------
+https://user:pass@example.com
+------------------------------
+
+When git needs authentication for a particular URL context,
+credential-store will consider that context a pattern to match against
+each entry in the credentials file.  If the protocol, hostname, and
+username (if we already have one) match, then the password is returned
+to git. See the discussion of configuration in linkgit:gitcredentials[7]
+for more information.
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-credential.txt b/Documentation/git-credential.txt
new file mode 100644
index 0000000..53adee3
--- /dev/null
+++ b/Documentation/git-credential.txt
@@ -0,0 +1,154 @@
+git-credential(1)
+=================
+
+NAME
+----
+git-credential - retrieve and store user credentials
+
+SYNOPSIS
+--------
+------------------
+git credential <fill|approve|reject>
+------------------
+
+DESCRIPTION
+-----------
+
+Git has an internal interface for storing and retrieving credentials
+from system-specific helpers, as well as prompting the user for
+usernames and passwords. The git-credential command exposes this
+interface to scripts which may want to retrieve, store, or prompt for
+credentials in the same manner as git. The design of this scriptable
+interface models the internal C API; see
+link:technical/api-credentials.txt[the git credential API] for more
+background on the concepts.
+
+git-credential takes an "action" option on the command-line (one of
+`fill`, `approve`, or `reject`) and reads a credential description
+on stdin (see <<IOFMT,INPUT/OUTPUT FORMAT>>).
+
+If the action is `fill`, git-credential will attempt to add "username"
+and "password" attributes to the description by reading config files,
+by contacting any configured credential helpers, or by prompting the
+user. The username and password attributes of the credential
+description are then printed to stdout together with the attributes
+already provided.
+
+If the action is `approve`, git-credential will send the description
+to any configured credential helpers, which may store the credential
+for later use.
+
+If the action is `reject`, git-credential will send the description to
+any configured credential helpers, which may erase any stored
+credential matching the description.
+
+If the action is `approve` or `reject`, no output should be emitted.
+
+TYPICAL USE OF GIT CREDENTIAL
+-----------------------------
+
+An application using git-credential will typically use `git
+credential` following these steps:
+
+  1. Generate a credential description based on the context.
++
+For example, if we want a password for
+`https://example.com/foo.git`, we might generate the following
+credential description (don't forget the blank line at the end; it
+tells `git credential` that the application finished feeding all the
+infomation it has):
+
+	 protocol=https
+	 host=example.com
+	 path=foo.git
+
+  2. Ask git-credential to give us a username and password for this
+     description. This is done by running `git credential fill`,
+     feeding the description from step (1) to its standard input. The complete
+     credential description (including the credential per se, i.e. the
+     login and password) will be produced on standard output, like:
+
+	protocol=https
+	host=example.com
+	username=bob
+	password=secr3t
++
+In most cases, this means the attributes given in the input will be
+repeated in the output, but git may also modify the credential
+description, for example by removing the `path` attribute when the
+protocol is HTTP(s) and `credential.useHttpPath` is false.
++
+If the `git credential` knew about the password, this step may
+not have involved the user actually typing this password (the
+user may have typed a password to unlock the keychain instead,
+or no user interaction was done if the keychain was already
+unlocked) before it returned `password=secr3t`.
+
+  3. Use the credential (e.g., access the URL with the username and
+     password from step (2)), and see if it's accepted.
+
+  4. Report on the success or failure of the password. If the
+     credential allowed the operation to complete successfully, then
+     it can be marked with an "approve" action to tell `git
+     credential` to reuse it in its next invocation. If the credential
+     was rejected during the operation, use the "reject" action so
+     that `git credential` will ask for a new password in its next
+     invocation. In either case, `git credential` should be fed with
+     the credential description obtained from step (2) (which also
+     contain the ones provided in step (1)).
+
+[[IOFMT]]
+INPUT/OUTPUT FORMAT
+-------------------
+
+`git credential` reads and/or writes (depending on the action used)
+credential information in its standard input/output. This information
+can correspond either to keys for which `git credential` will obtain
+the login/password information (e.g. host, protocol, path), or to the
+actual credential data to be obtained (login/password).
+
+The credential is split into a set of named attributes, with one
+attribute per line. Each attribute is
+specified by a key-value pair, separated by an `=` (equals) sign,
+followed by a newline. The key may contain any bytes except `=`,
+newline, or NUL. The value may contain any bytes except newline or NUL.
+In both cases, all bytes are treated as-is (i.e., there is no quoting,
+and one cannot transmit a value with newline or NUL in it). The list of
+attributes is terminated by a blank line or end-of-file.
+Git understands the following attributes:
+
+`protocol`::
+
+	The protocol over which the credential will be used (e.g.,
+	`https`).
+
+`host`::
+
+	The remote hostname for a network credential.
+
+`path`::
+
+	The path with which the credential will be used. E.g., for
+	accessing a remote https repository, this will be the
+	repository's path on the server.
+
+`username`::
+
+	The credential's username, if we already have one (e.g., from a
+	URL, from the user, or from a previously run helper).
+
+`password`::
+
+	The credential's password, if we are asking it to be stored.
+
+`url`::
+
+	When this special attribute is read by `git credential`, the
+	value is parsed as a URL and treated as if its constituent parts
+	were read (e.g., `url=https://example.com` would behave as if
+	`protocol=https` and `host=example.com` had been provided). This
+	can help callers avoid parsing URLs themselves.  Note that any
+	components which are missing from the URL (e.g., there is no
+	username in the example above) will be set to empty; if you want
+	to provide a URL and override some attributes, provide the URL
+	attribute first, followed by any overrides.
diff --git a/Documentation/git-cvsexportcommit.txt b/Documentation/git-cvsexportcommit.txt
index abaaf27..7f79cec 100644
--- a/Documentation/git-cvsexportcommit.txt
+++ b/Documentation/git-cvsexportcommit.txt
@@ -8,6 +8,7 @@
 
 SYNOPSIS
 --------
+[verse]
 'git cvsexportcommit' [-h] [-u] [-v] [-c] [-P] [-p] [-a] [-d cvsroot]
 	[-w cvsworkdir] [-W] [-f] [-m msgprefix] [PARENTCOMMIT] COMMITID
 
@@ -27,7 +28,7 @@
 
 Supports file additions, removals, and commits that affect binary files.
 
-If the commit is a merge commit, you must tell 'git-cvsexportcommit' what
+If the commit is a merge commit, you must tell 'git cvsexportcommit' what
 parent the changeset should be done against.
 
 OPTIONS
@@ -112,14 +113,6 @@
 $ git cherry cvshead myhead | sed -n 's/^+ //p' | xargs -l1 git cvsexportcommit -c -p -v
 ------------
 
-Author
-------
-Written by Martin Langhoff <martin@catalyst.net.nz> and others.
-
-Documentation
---------------
-Documentation by Martin Langhoff <martin@catalyst.net.nz> and others.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-cvsimport.txt b/Documentation/git-cvsimport.txt
index 614e769..6695ab3 100644
--- a/Documentation/git-cvsimport.txt
+++ b/Documentation/git-cvsimport.txt
@@ -13,7 +13,7 @@
 	      [-A <author-conv-file>] [-p <options-for-cvsps>] [-P <file>]
 	      [-C <git_repository>] [-z <fuzz>] [-i] [-k] [-u] [-s <subst>]
 	      [-a] [-m] [-M <regex>] [-S <regex>] [-L <commitlimit>]
-	      [-r <remote>] [<CVS_module>]
+	      [-r <remote>] [-R] [<CVS_module>]
 
 
 DESCRIPTION
@@ -28,9 +28,9 @@
 Please see the section <<issues,ISSUES>> for further reference.
 
 You should *never* do any work of your own on the branches that are
-created by 'git-cvsimport'.  By default initial import will create and populate a
+created by 'git cvsimport'.  By default initial import will create and populate a
 "master" branch from the CVS repository's main branch which you're free
-to work with; after that, you need to 'git-merge' incremental imports, or
+to work with; after that, you need to 'git merge' incremental imports, or
 any CVS branches, yourself.  It is advisable to specify a named remote via
 -r to separate and protect the incoming branches.
 
@@ -49,13 +49,13 @@
 -d <CVSROOT>::
 	The root of the CVS archive. May be local (a simple path) or remote;
 	currently, only the :local:, :ext: and :pserver: access methods
-	are supported. If not given, 'git-cvsimport' will try to read it
+	are supported. If not given, 'git cvsimport' will try to read it
 	from `CVS/Root`. If no such file exists, it checks for the
 	`CVSROOT` environment variable.
 
 <CVS_module>::
 	The CVS module you want to import. Relative to <CVSROOT>.
-	If not given, 'git-cvsimport' tries to read it from
+	If not given, 'git cvsimport' tries to read it from
 	`CVS/Repository`.
 
 -C <target-dir>::
@@ -65,14 +65,14 @@
 -r <remote>::
 	The git remote to import this CVS repository into.
 	Moves all CVS branches into remotes/<remote>/<branch>
-	akin to the way 'git-clone' uses 'origin' by default.
+	akin to the way 'git clone' uses 'origin' by default.
 
 -o <branch-for-HEAD>::
 	When no remote is specified (via -r) the 'HEAD' branch
 	from CVS is imported to the 'origin' branch within the git
 	repository, as 'HEAD' already has a special meaning for git.
 	When a remote is specified the 'HEAD' branch is named
-	remotes/<remote>/master mirroring 'git-clone' behaviour.
+	remotes/<remote>/master mirroring 'git clone' behaviour.
 	Use this option if you want to import into a different
 	branch.
 +
@@ -145,17 +145,33 @@
 
 ---------
 +
-'git-cvsimport' will make it appear as those authors had
+'git cvsimport' will make it appear as those authors had
 their GIT_AUTHOR_NAME and GIT_AUTHOR_EMAIL set properly
 all along.
 +
 For convenience, this data is saved to `$GIT_DIR/cvs-authors`
 each time the '-A' option is provided and read from that same
-file each time 'git-cvsimport' is run.
+file each time 'git cvsimport' is run.
 +
 It is not recommended to use this feature if you intend to
 export changes back to CVS again later with
-'git-cvsexportcommit'.
+'git cvsexportcommit'.
+
+-R::
+	Generate a `$GIT_DIR/cvs-revisions` file containing a mapping from CVS
+	revision numbers to newly-created Git commit IDs.  The generated file
+	will contain one line for each (filename, revision) pair imported;
+	each line will look like
++
+---------
+src/widget.c 1.1 1d862f173cdc7325b6fa6d2ae1cfd61fd1b512b7
+---------
++
+The revision data is appended to the file if it already exists, for use when
+doing incremental imports.
++
+This option may be useful if you have CVS revision numbers stored in commit
+messages, bug-tracking systems, email archives, and the like.
 
 -h::
 	Print a short usage message and exit.
@@ -172,7 +188,7 @@
 ------
 Problems related to timestamps:
 
- * If timestamps of commits in the cvs repository are not stable enough
+ * If timestamps of commits in the CVS repository are not stable enough
    to be used for ordering commits changes may show up in the wrong
    order.
  * If any files were ever "cvs import"ed more than once (e.g., import of
@@ -185,7 +201,7 @@
 
  * Branches on which no commits have been made are not imported.
  * All files from the branching point are added to a branch even if
-   never added in cvs.
+   never added in CVS.
  * This applies to files added to the source branch *after* a daughter
    branch was created: if previously no commit was made on the daughter
    branch they will erroneously be added to the daughter branch in git.
@@ -201,15 +217,6 @@
 * cvs2git (part of cvs2svn), `http://cvs2svn.tigris.org`
 * parsecvs, `http://cgit.freedesktop.org/~keithp/parsecvs`
 
-Author
-------
-Written by Matthias Urlichs <smurf@smurf.noris.de>, with help from
-various participants of the git-list <git@vger.kernel.org>.
-
-Documentation
---------------
-Documentation by Matthias Urlichs <smurf@smurf.noris.de>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-cvsserver.txt b/Documentation/git-cvsserver.txt
index 785779e..88d814a 100644
--- a/Documentation/git-cvsserver.txt
+++ b/Documentation/git-cvsserver.txt
@@ -22,7 +22,7 @@
 Usage:
 
 [verse]
-'git cvsserver' [options] [pserver|server] [<directory> ...]
+'git-cvsserver' [options] [pserver|server] [<directory> ...]
 
 OPTIONS
 -------
@@ -72,9 +72,6 @@
 LIMITATIONS
 -----------
 
-Currently cvsserver works over SSH connections for read/write clients, and
-over pserver for anonymous CVS access.
-
 CVS clients cannot tag, branch or perform GIT merges.
 
 'git-cvsserver' maps GIT branches to CVS modules. This is very different
@@ -84,7 +81,7 @@
 INSTALLATION
 ------------
 
-1. If you are going to offer anonymous CVS access via pserver, add a line in
+1. If you are going to offer CVS access via pserver, add a line in
    /etc/inetd.conf like
 +
 --
@@ -101,6 +98,38 @@
    cvspserver stream tcp nowait nobody /usr/bin/git-cvsserver git-cvsserver pserver
 
 ------
+
+Only anonymous access is provided by pserve by default. To commit you
+will have to create pserver accounts, simply add a gitcvs.authdb
+setting in the config file of the repositories you want the cvsserver
+to allow writes to, for example:
+
+------
+
+   [gitcvs]
+	authdb = /etc/cvsserver/passwd
+
+------
+The format of these files is username followed by the crypted password,
+for example:
+
+------
+   myuser:$1Oyx5r9mdGZ2
+   myuser:$1$BA)@$vbnMJMDym7tA32AamXrm./
+------
+You can use the 'htpasswd' facility that comes with Apache to make these
+files, but Apache's MD5 crypt method differs from the one used by most C
+library's crypt() function, so don't use the -m option.
+
+Alternatively you can produce the password with perl's crypt() operator:
+-----
+   perl -e 'my ($user, $pass) = @ARGV; printf "%s:%s\n", $user, crypt($user, $pass)' $USER password
+-----
+
+Then provide your password via the pserver method, for example:
+------
+   cvs -d:pserver:someuser:somepassword <at> server/path/repo.git co <HEAD_name>
+------
 No special setup is needed for SSH access, other than having GIT tools
 in the PATH. If you have clients that do not accept the CVS_SERVER
 environment variable, you can rename 'git-cvsserver' to `cvs`.
@@ -182,10 +211,9 @@
 ----------------
 
 'git-cvsserver' uses one database per git head (i.e. CVS module) to
-store information about the repository for faster access. The
-database doesn't contain any persistent data and can be completely
-regenerated from the git repository at any time. The database
-needs to be updated (i.e. written to) after every commit.
+store information about the repository to maintain consistent
+CVS revision numbers. The database needs to be
+updated (i.e. written to) after every commit.
 
 If the commit is done directly by using `git` (as opposed to
 using 'git-cvsserver') the update will need to happen on the
@@ -204,6 +232,18 @@
 'git-cvsserver' write access to the database file without granting
 them write access to the directory, too.
 
+The database can not be reliably regenerated in a
+consistent form after the branch it is tracking has changed.
+Example: For merged branches, 'git-cvsserver' only tracks
+one branch of development, and after a 'git merge' an
+incrementally updated database may track a different branch
+than a database regenerated from scratch, causing inconsistent
+CVS revision numbers. `git-cvsserver` has no way of knowing which
+branch it would have picked if it had been run incrementally
+pre-merge. So if you have to fully or partially (from old
+backup) regenerate the database, you should be suspicious
+of pre-existing CVS sandboxes.
+
 You can configure the database backend with the following
 configuration variables:
 
@@ -266,6 +306,21 @@
 	If no name can be determined, the
 	numeric uid is used.
 
+ENVIRONMENT
+-----------
+
+These variables obviate the need for command-line options in some
+circumstances, allowing easier restricted usage through git-shell.
+
+GIT_CVSSERVER_BASE_PATH takes the place of the argument to --base-path.
+
+GIT_CVSSERVER_ROOT specifies a single-directory whitelist. The
+repository must still be configured to allow access through
+git-cvsserver, as described above.
+
+When these environment variables are set, the corresponding
+command-line arguments may not be used.
+
 Eclipse CVS Client Notes
 ------------------------
 
@@ -283,7 +338,7 @@
 Protocol notes: If you are using anonymous access via pserver, just select that.
 Those using SSH access should choose the 'ext' protocol, and configure 'ext'
 access on the Preferences->Team->CVS->ExtConnection pane. Set CVS_SERVER to
-"'git cvsserver'". Note that password support is not good when using 'ext',
+"`git cvsserver`". Note that password support is not good when using 'ext',
 you will definitely want to have SSH keys setup.
 
 Alternatively, you can just use the non-standard extssh protocol that Eclipse
@@ -311,19 +366,16 @@
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 By default the server leaves the '-k' mode blank for all files,
-which causes the cvs client to treat them as a text files, subject
-to crlf conversion on some platforms.
+which causes the CVS client to treat them as a text files, subject
+to end-of-line conversion on some platforms.
 
-You can make the server use `crlf` attributes to set the '-k' modes
-for files by setting the `gitcvs.usecrlfattr` config variable.
-In this case, if `crlf` is explicitly unset ('-crlf'), then the
-server will set '-kb' mode for binary files. If `crlf` is set,
-then the '-k' mode will explicitly be left blank.  See
-also linkgit:gitattributes[5] for more information about the `crlf`
-attribute.
+You can make the server use the end-of-line conversion attributes to
+set the '-k' modes for files by setting the `gitcvs.usecrlfattr`
+config variable.  See linkgit:gitattributes[5] for more information
+about end-of-line conversion.
 
 Alternatively, if `gitcvs.usecrlfattr` config is not enabled
-or if the `crlf` attribute is unspecified for a filename, then
+or the attributes do not allow automatic detection for a filename, then
 the server uses the `gitcvs.allbinary` config for the default setting.
 If `gitcvs.allbinary` is set, then file not otherwise
 specified will default to '-kb' mode. Otherwise the '-k' mode
@@ -339,22 +391,6 @@
 ------------
 'git-cvsserver' depends on DBD::SQLite.
 
-Copyright and Authors
----------------------
-
-This program is copyright The Open University UK - 2006.
-
-Authors:
-
-- Martyn Smith    <martyn@catalyst.net.nz>
-- Martin Langhoff <martin@catalyst.net.nz>
-
-with ideas and patches from participants of the git-list <git@vger.kernel.org>.
-
-Documentation
---------------
-Documentation by Martyn Smith <martyn@catalyst.net.nz>, Martin Langhoff <martin@catalyst.net.nz>, and Matthias Urlichs <smurf@smurf.noris.de>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt
index a85121c..e8f7577 100644
--- a/Documentation/git-daemon.txt
+++ b/Documentation/git-daemon.txt
@@ -9,15 +9,15 @@
 --------
 [verse]
 'git daemon' [--verbose] [--syslog] [--export-all]
-	     [--timeout=n] [--init-timeout=n] [--max-connections=n]
-	     [--strict-paths] [--base-path=path] [--base-path-relaxed]
-	     [--user-path | --user-path=path]
-	     [--interpolated-path=pathtemplate]
-	     [--reuseaddr] [--detach] [--pid-file=file]
-	     [--enable=service] [--disable=service]
-	     [--allow-override=service] [--forbid-override=service]
-	     [--inetd | [--listen=host_or_ipaddr] [--port=n] [--user=user [--group=group]]
-	     [directory...]
+	     [--timeout=<n>] [--init-timeout=<n>] [--max-connections=<n>]
+	     [--strict-paths] [--base-path=<path>] [--base-path-relaxed]
+	     [--user-path | --user-path=<path>]
+	     [--interpolated-path=<pathtemplate>]
+	     [--reuseaddr] [--detach] [--pid-file=<file>]
+	     [--enable=<service>] [--disable=<service>]
+	     [--allow-override=<service>] [--forbid-override=<service>]
+	     [--inetd | [--listen=<host_or_ipaddr>] [--port=<n>] [--user=<user> [--group=<group>]]
+	     [<directory>...]
 
 DESCRIPTION
 -----------
@@ -28,40 +28,40 @@
 It verifies that the directory has the magic file "git-daemon-export-ok", and
 it will refuse to export any git directory that hasn't explicitly been marked
 for export this way (unless the '--export-all' parameter is specified). If you
-pass some directory paths as 'git-daemon' arguments, you can further restrict
+pass some directory paths as 'git daemon' arguments, you can further restrict
 the offers to a whitelist comprising of those.
 
 By default, only `upload-pack` service is enabled, which serves
-'git-fetch-pack' and 'git-ls-remote' clients, which are invoked
-from 'git-fetch', 'git-pull', and 'git-clone'.
+'git fetch-pack' and 'git ls-remote' clients, which are invoked
+from 'git fetch', 'git pull', and 'git clone'.
 
 This is ideally suited for read-only updates, i.e., pulling from
 git repositories.
 
-An `upload-archive` also exists to serve 'git-archive'.
+An `upload-archive` also exists to serve 'git archive'.
 
 OPTIONS
 -------
 --strict-paths::
 	Match paths exactly (i.e. don't allow "/foo/repo" when the real path is
 	"/foo/repo.git" or "/foo/repo/.git") and don't do user-relative paths.
-	'git-daemon' will refuse to start when this option is enabled and no
+	'git daemon' will refuse to start when this option is enabled and no
 	whitelist is specified.
 
---base-path=path::
+--base-path=<path>::
 	Remap all the path requests as relative to the given path.
-	This is sort of "GIT root" - if you run 'git-daemon' with
+	This is sort of "GIT root" - if you run 'git daemon' with
 	'--base-path=/srv/git' on example.com, then if you later try to pull
-	'git://example.com/hello.git', 'git-daemon' will interpret the path
+	'git://example.com/hello.git', 'git daemon' will interpret the path
 	as '/srv/git/hello.git'.
 
 --base-path-relaxed::
 	If --base-path is enabled and repo lookup fails, with this option
-	'git-daemon' will attempt to lookup without prefixing the base path.
+	'git daemon' will attempt to lookup without prefixing the base path.
 	This is useful for switching to --base-path usage, while still
 	allowing the old paths.
 
---interpolated-path=pathtemplate::
+--interpolated-path=<pathtemplate>::
 	To support virtual hosting, an interpolated path template can be
 	used to dynamically construct alternate paths.  The template
 	supports %H for the target hostname as supplied by the client but
@@ -78,29 +78,31 @@
 
 --inetd::
 	Have the server run as an inetd service. Implies --syslog.
-	Incompatible with --port, --listen, --user and --group options.
+	Incompatible with --detach, --port, --listen, --user and --group
+	options.
 
---listen=host_or_ipaddr::
+--listen=<host_or_ipaddr>::
 	Listen on a specific IP address or hostname.  IP addresses can
 	be either an IPv4 address or an IPv6 address if supported.  If IPv6
 	is not supported, then --listen=hostname is also not supported and
 	--listen must be given an IPv4 address.
+	Can be given more than once.
 	Incompatible with '--inetd' option.
 
---port=n::
+--port=<n>::
 	Listen on an alternative port.  Incompatible with '--inetd' option.
 
---init-timeout=n::
-	Timeout between the moment the connection is established and the
-	client request is received (typically a rather low value, since
+--init-timeout=<n>::
+	Timeout (in seconds) between the moment the connection is established
+	and the client request is received (typically a rather low value, since
 	that should be basically immediate).
 
---timeout=n::
-	Timeout for specific client sub-requests. This includes the time
-	it takes for the server to process the sub-request and the time spent
-	waiting for the next client's request.
+--timeout=<n>::
+	Timeout (in seconds) for specific client sub-requests. This includes
+	the time it takes for the server to process the sub-request and the
+	time spent waiting for the next client's request.
 
---max-connections=n::
+--max-connections=<n>::
 	Maximum number of concurrent clients, defaults to 32.  Set it to
 	zero for no limit.
 
@@ -109,7 +111,7 @@
 	--verbose, thus by default only error conditions will be logged.
 
 --user-path::
---user-path=path::
+--user-path=<path>::
 	Allow {tilde}user notation to be used in requests.  When
 	specified with no parameter, requests to
 	git://host/{tilde}alice/foo is taken as a request to access
@@ -129,12 +131,12 @@
 --detach::
 	Detach from the shell. Implies --syslog.
 
---pid-file=file::
+--pid-file=<file>::
 	Save the process id in 'file'.  Ignored when the daemon
 	is run under `--inetd`.
 
---user=user::
---group=group::
+--user=<user>::
+--group=<group>::
 	Change daemon's uid and gid before entering the service loop.
 	When only `--user` is given without `--group`, the
 	primary group ID for the user is used.  The values of
@@ -143,22 +145,32 @@
 +
 Giving these options is an error when used with `--inetd`; use
 the facility of inet daemon to achieve the same before spawning
-'git-daemon' if needed.
+'git daemon' if needed.
 
---enable=service::
---disable=service::
+--enable=<service>::
+--disable=<service>::
 	Enable/disable the service site-wide per default.  Note
 	that a service disabled site-wide can still be enabled
 	per repository if it is marked overridable and the
 	repository enables the service with a configuration
 	item.
 
---allow-override=service::
---forbid-override=service::
+--allow-override=<service>::
+--forbid-override=<service>::
 	Allow/forbid overriding the site-wide default with per
 	repository configuration.  By default, all the services
 	are overridable.
 
+--informative-errors::
+--no-informative-errors::
+	When informative errors are turned on, git-daemon will report
+	more verbose errors to the client, differentiating conditions
+	like "no such repository" from "repository not exported". This
+	is more convenient for clients, but may leak information about
+	the existence of unexported repositories.  When informative
+	errors are not enabled, all errors report "access denied" to the
+	client. The default is --no-informative-errors.
+
 <directory>::
 	A directory to add to the whitelist of allowed directories. Unless
 	--strict-paths is specified this will also include subdirectories
@@ -169,30 +181,30 @@
 
 These services can be globally enabled/disabled using the
 command line options of this command.  If a finer-grained
-control is desired (e.g. to allow 'git-archive' to be run
+control is desired (e.g. to allow 'git archive' to be run
 against only in a few selected repositories the daemon serves),
 the per-repository configuration file can be used to enable or
 disable them.
 
 upload-pack::
-	This serves 'git-fetch-pack' and 'git-ls-remote'
+	This serves 'git fetch-pack' and 'git ls-remote'
 	clients.  It is enabled by default, but a repository can
 	disable it by setting `daemon.uploadpack` configuration
 	item to `false`.
 
 upload-archive::
-	This serves 'git-archive --remote'.  It is disabled by
+	This serves 'git archive --remote'.  It is disabled by
 	default, but a repository can enable it by setting
 	`daemon.uploadarch` configuration item to `true`.
 
 receive-pack::
-	This serves 'git-send-pack' clients, allowing anonymous
+	This serves 'git send-pack' clients, allowing anonymous
 	push.  It is disabled by default, as there is _no_
 	authentication in the protocol (in other words, anybody
 	can push anything into the repository, including removal
 	of refs).  This is solely meant for a closed LAN setting
 	where everybody is friendly.  This service can be
-	enabled by `daemon.receivepack` configuration item to
+	enabled by setting `daemon.receivepack` configuration item to
 	`true`.
 
 EXAMPLES
@@ -204,8 +216,8 @@
 git		9418/tcp		# Git Version Control System
 ------------
 
-'git-daemon' as inetd server::
-	To set up 'git-daemon' as an inetd service that handles any
+'git daemon' as inetd server::
+	To set up 'git daemon' as an inetd service that handles any
 	repository under the whitelisted set of directories, /pub/foo
 	and /pub/bar, place an entry like the following into
 	/etc/inetd all on one line:
@@ -217,8 +229,8 @@
 ------------------------------------------------
 
 
-'git-daemon' as inetd server for virtual hosts::
-	To set up 'git-daemon' as an inetd service that handles
+'git daemon' as inetd server for virtual hosts::
+	To set up 'git daemon' as an inetd service that handles
 	repositories for different virtual hosts, `www.example.com`
 	and `www.example.org`, place an entry like the following into
 	`/etc/inetd` all on one line:
@@ -240,8 +252,8 @@
 default repository could be made as well.
 
 
-'git-daemon' as regular daemon for virtual hosts::
-	To set up 'git-daemon' as a regular, non-inetd service that
+'git daemon' as regular daemon for virtual hosts::
+	To set up 'git daemon' as a regular, non-inetd service that
 	handles repositories for multiple virtual hosts based on
 	their IP addresses, start the daemon like this:
 +
@@ -258,7 +270,7 @@
 they correspond to these IP addresses.
 
 selectively enable/disable services per repository::
-	To enable 'git-archive --remote' and disable 'git-fetch' against
+	To enable 'git archive --remote' and disable 'git fetch' against
 	a repository, have the following in the configuration file in the
 	repository (that is the file 'config' next to 'HEAD', 'refs' and
 	'objects').
@@ -272,22 +284,11 @@
 
 ENVIRONMENT
 -----------
-'git-daemon' will set REMOTE_ADDR to the IP address of the client
+'git daemon' will set REMOTE_ADDR to the IP address of the client
 that connected to it, if the IP address is available. REMOTE_ADDR will
 be available in the environment of hooks called when
 services are performed.
 
-
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>, YOSHIFUJI Hideaki
-<yoshfuji@linux-ipv6.org> and the git-list <git@vger.kernel.org>
-
-Documentation
---------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-describe.txt b/Documentation/git-describe.txt
index b231dbb..039cce2 100644
--- a/Documentation/git-describe.txt
+++ b/Documentation/git-describe.txt
@@ -8,7 +8,9 @@
 
 SYNOPSIS
 --------
+[verse]
 'git describe' [--all] [--tags] [--contains] [--abbrev=<n>] <committish>...
+'git describe' [--all] [--tags] [--contains] [--abbrev=<n>] --dirty[=<mark>]
 
 DESCRIPTION
 -----------
@@ -27,10 +29,15 @@
 <committish>...::
 	Committish object names to describe.
 
+--dirty[=<mark>]::
+	Describe the working tree.
+	It means describe HEAD and appends <mark> (`-dirty` by
+	default) if the working tree is dirty.
+
 --all::
 	Instead of using only the annotated tags, use any ref
 	found in `.git/refs/`.  This option enables matching
-	any known branch, remote branch, or lightweight tag.
+	any known branch, remote-tracking branch, or lightweight tag.
 
 --tags::
 	Instead of using only the annotated tags, use any tag
@@ -44,7 +51,9 @@
 
 --abbrev=<n>::
 	Instead of using the default 7 hexadecimal digits as the
-	abbreviated object name, use <n> digits.
+	abbreviated object name, use <n> digits, or as many digits
+	as needed to form a unique object name.  An <n> of 0
+	will suppress long format, only showing the closest tag.
 
 --candidates=<n>::
 	Instead of considering only the 10 most recent tags as
@@ -68,8 +77,8 @@
 	This is useful when you want to see parts of the commit object name
 	in "describe" output, even when the commit in question happens to be
 	a tagged version.  Instead of just emitting the tag name, it will
-	describe such a commit as v1.2-0-deadbeef (0th commit since tag v1.2
-	that points at object deadbeef....).
+	describe such a commit as v1.2-0-gdeadbee (0th commit since tag v1.2
+	that points at object deadbee....).
 
 --match <pattern>::
 	Only consider tags matching the given pattern (can be used to avoid
@@ -96,8 +105,11 @@
 of commits which would be displayed by "git log v1.0.4..parent".
 The hash suffix is "-g" + 7-char abbreviation for the tip commit
 of parent (which was `2414721b194453f058079d897d13c4e377f92dc6`).
+The "g" prefix stands for "git" and is used to allow describing the version of
+a software depending on the SCM the software is managed with. This is useful
+in an environment where people may use different SCMs.
 
-Doing a 'git-describe' on a tag-name will just show the tag name:
+Doing a 'git describe' on a tag-name will just show the tag name:
 
 	[torvalds@g5 git]$ git describe v1.0.4
 	v1.0.4
@@ -108,7 +120,7 @@
 	[torvalds@g5 git]$ git describe --all --abbrev=4 v1.0.5^2
 	tags/v1.0.0-21-g975b
 
-	[torvalds@g5 git]$ git describe --all HEAD^
+	[torvalds@g5 git]$ git describe --all --abbrev=4 HEAD^
 	heads/lt/describe-7-g975b
 
 With --abbrev set to 0, the command can be used to find the
@@ -117,16 +129,23 @@
 	[torvalds@g5 git]$ git describe --abbrev=0 v1.0.5^2
 	tags/v1.0.0
 
+Note that the suffix you get if you type these commands today may be
+longer than what Linus saw above when he ran these commands, as your
+git repository may have new commits whose object names begin with
+975b that did not exist back then, and "-g975b" suffix alone may not
+be sufficient to disambiguate these commits.
+
+
 SEARCH STRATEGY
 ---------------
 
-For each committish supplied, 'git-describe' will first look for
+For each committish supplied, 'git describe' will first look for
 a tag which tags exactly that commit.  Annotated tags will always
 be preferred over lightweight tags, and tags with newer dates will
 always be preferred over tags with older dates.  If an exact match
 is found, its name will be output and searching will stop.
 
-If an exact match was not found, 'git-describe' will walk back
+If an exact match was not found, 'git describe' will walk back
 through the commit history to locate an ancestor commit which
 has been tagged.  The ancestor's tag will be output along with an
 abbreviation of the input committish's SHA1.
@@ -137,17 +156,6 @@
 the number of commits which would be shown by `git log tag..input`
 will be the smallest number of commits possible.
 
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>, but somewhat
-butchered by Junio C Hamano <gitster@pobox.com>.  Later significantly
-updated by Shawn Pearce <spearce@spearce.org>.
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-diff-files.txt b/Documentation/git-diff-files.txt
index 4ef0357..906774f 100644
--- a/Documentation/git-diff-files.txt
+++ b/Documentation/git-diff-files.txt
@@ -8,6 +8,7 @@
 
 SYNOPSIS
 --------
+[verse]
 'git diff-files' [-q] [-0|-1|-2|-3|-c|--cc] [<common diff options>] [<path>...]
 
 DESCRIPTION
@@ -15,7 +16,7 @@
 Compares the files in the working tree and the index.  When paths
 are specified, compares only those named paths.  Otherwise all
 entries in the index are compared.  The output format is the
-same as for 'git-diff-index' and 'git-diff-tree'.
+same as for 'git diff-index' and 'git diff-tree'.
 
 OPTIONS
 -------
@@ -46,15 +47,6 @@
 
 include::diff-format.txt[]
 
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-diff-index.txt b/Documentation/git-diff-index.txt
index 8b9ed29..c0b7c58 100644
--- a/Documentation/git-diff-index.txt
+++ b/Documentation/git-diff-index.txt
@@ -8,6 +8,7 @@
 
 SYNOPSIS
 --------
+[verse]
 'git diff-index' [-m] [--cached] [<common diff options>] <tree-ish> [<path>...]
 
 DESCRIPTION
@@ -31,7 +32,7 @@
 -m::
 	By default, files recorded in the index but not checked
 	out are reported as deleted.  This flag makes
-	'git-diff-index' say that all non-checked-out files are up
+	'git diff-index' say that all non-checked-out files are up
 	to date.
 
 include::diff-format.txt[]
@@ -48,7 +49,7 @@
 If '--cached' is specified, it allows you to ask:
 
 	show me the differences between HEAD and the current index
-	contents (the ones I'd write using 'git-write-tree')
+	contents (the ones I'd write using 'git write-tree')
 
 For example, let's say that you have worked on your working directory, updated
 some files in the index and are ready to commit. You want to see exactly
@@ -60,7 +61,7 @@
 Example: let's say I had renamed `commit.c` to `git-commit.c`, and I had
 done an `update-index` to make that effective in the index file.
 `git diff-files` wouldn't show anything at all, since the index file
-matches my working directory. But doing a 'git-diff-index' does:
+matches my working directory. But doing a 'git diff-index' does:
 
   torvalds@ppc970:~/git> git diff-index --cached HEAD
   -100644 blob    4161aecc6700a2eb579e842af0b7f22b98443f74        commit.c
@@ -69,10 +70,10 @@
 You can see easily that the above is a rename.
 
 In fact, `git diff-index --cached` *should* always be entirely equivalent to
-actually doing a 'git-write-tree' and comparing that. Except this one is much
+actually doing a 'git write-tree' and comparing that. Except this one is much
 nicer for the case where you just want to check where you are.
 
-So doing a 'git-diff-index --cached' is basically very useful when you are
+So doing a `git diff-index --cached` is basically very useful when you are
 asking yourself "what have I already marked for being committed, and
 what's the difference to a previous tree".
 
@@ -80,35 +81,35 @@
 ---------------
 The "non-cached" mode takes a different approach, and is potentially
 the more useful of the two in that what it does can't be emulated with
-a 'git-write-tree' + 'git-diff-tree'. Thus that's the default mode.
+a 'git write-tree' + 'git diff-tree'. Thus that's the default mode.
 The non-cached version asks the question:
 
   show me the differences between HEAD and the currently checked out
   tree - index contents _and_ files that aren't up-to-date
 
 which is obviously a very useful question too, since that tells you what
-you *could* commit. Again, the output matches the 'git-diff-tree -r'
+you *could* commit. Again, the output matches the 'git diff-tree -r'
 output to a tee, but with a twist.
 
 The twist is that if some file doesn't match the index, we don't have
 a backing store thing for it, and we use the magic "all-zero" sha1 to
 show that. So let's say that you have edited `kernel/sched.c`, but
-have not actually done a 'git-update-index' on it yet - there is no
+have not actually done a 'git update-index' on it yet - there is no
 "object" associated with the new state, and you get:
 
-  torvalds@ppc970:~/v2.6/linux> git diff-index HEAD
-  *100644->100664 blob    7476bb......->000000......      kernel/sched.c
+  torvalds@ppc970:~/v2.6/linux> git diff-index --abbrev HEAD
+  :100644 100664 7476bb... 000000...      kernel/sched.c
 
 i.e., it shows that the tree has changed, and that `kernel/sched.c` has is
 not up-to-date and may contain new stuff. The all-zero sha1 means that to
 get the real diff, you need to look at the object in the working directory
 directly rather than do an object-to-object diff.
 
-NOTE: As with other commands of this type, 'git-diff-index' does not
+NOTE: As with other commands of this type, 'git diff-index' does not
 actually look at the contents of the file at all. So maybe
 `kernel/sched.c` hasn't actually changed, and it's just that you
 touched it. In either case, it's a note that you need to
-'git-update-index' it to make the index be in sync.
+'git update-index' it to make the index be in sync.
 
 NOTE: You can have a mixture of files show up as "has been updated"
 and "is still dirty in the working directory" together. You can always
@@ -116,15 +117,6 @@
 show a valid sha1, and the "not in sync with the index" ones will
 always have the special all-zero sha1.
 
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-diff-tree.txt b/Documentation/git-diff-tree.txt
index f2cef12..1439486 100644
--- a/Documentation/git-diff-tree.txt
+++ b/Documentation/git-diff-tree.txt
@@ -20,7 +20,7 @@
 If there is only one <tree-ish> given, the commit is compared with its parents
 (see --stdin below).
 
-Note that 'git-diff-tree' can use the tree encapsulated in a commit object.
+Note that 'git diff-tree' can use the tree encapsulated in a commit object.
 
 OPTIONS
 -------
@@ -67,25 +67,25 @@
 commits (but not trees).
 
 -m::
-	By default, 'git-diff-tree --stdin' does not show
+	By default, 'git diff-tree --stdin' does not show
 	differences for merge commits.  With this flag, it shows
 	differences to that commit from all of its parents. See
 	also '-c'.
 
 -s::
-	By default, 'git-diff-tree --stdin' shows differences,
+	By default, 'git diff-tree --stdin' shows differences,
 	either in machine-readable form (without '-p') or in patch
 	form (with '-p').  This output can be suppressed.  It is
 	only useful with '-v' flag.
 
 -v::
-	This flag causes 'git-diff-tree --stdin' to also show
+	This flag causes 'git diff-tree --stdin' to also show
 	the commit message before the differences.
 
 include::pretty-options.txt[]
 
 --no-commit-id::
-	'git-diff-tree' outputs a line with the commit ID when
+	'git diff-tree' outputs a line with the commit ID when
 	applicable.  This flag suppressed the commit ID output.
 
 -c::
@@ -138,8 +138,8 @@
 
 An example of normal usage is:
 
-  torvalds@ppc970:~/git> git diff-tree 5319e4......
-  *100664->100664 blob    ac348b.......->a01513.......      git-fsck-objects.c
+  torvalds@ppc970:~/git> git diff-tree --abbrev 5319e4
+  :100664 100664 ac348b... a01513...	git-fsck-objects.c
 
 which tells you that the last commit changed just one file (it's from
 this one:
@@ -162,15 +162,6 @@
 
 include::diff-format.txt[]
 
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-diff.txt b/Documentation/git-diff.txt
index 0ac7112..f8d0819 100644
--- a/Documentation/git-diff.txt
+++ b/Documentation/git-diff.txt
@@ -8,12 +8,17 @@
 
 SYNOPSIS
 --------
-'git diff' [<common diff options>] <commit>{0,2} [--] [<path>...]
+[verse]
+'git diff' [options] [<commit>] [--] [<path>...]
+'git diff' [options] --cached [<commit>] [--] [<path>...]
+'git diff' [options] <commit> <commit> [--] [<path>...]
+'git diff' [options] [--no-index] [--] <path> <path>
 
 DESCRIPTION
 -----------
-Show changes between two trees, a tree and the working tree, a
-tree and the index file, or the index file and the working tree.
+Show changes between the working tree and the index or a tree, changes
+between the index and a tree, changes between two trees, or changes
+between two files on disk.
 
 'git diff' [--options] [--] [<path>...]::
 
@@ -23,9 +28,9 @@
 	further add to the index but you still haven't.  You can
 	stage these changes by using linkgit:git-add[1].
 +
-If exactly two paths are given, and at least one is untracked,
-compare the two files / directories. This behavior can be
-forced by --no-index.
+If exactly two paths are given and at least one points outside
+the current repository, 'git diff' will compare the two files /
+directories. This behavior can be forced by --no-index.
 
 'git diff' [--options] --cached [<commit>] [--] [<path>...]::
 
@@ -33,6 +38,8 @@
 	commit relative to the named <commit>.  Typically you
 	would want comparison with the latest commit, so if you
 	do not give <commit>, it defaults to HEAD.
+	If HEAD does not exist (e.g. unborned branches) and
+	<commit> is not given, it shows all staged changes.
 	--staged is a synonym of --cached.
 
 'git diff' [--options] <commit> [--] [<path>...]::
@@ -64,15 +71,16 @@
 
 Just in case if you are doing something exotic, it should be
 noted that all of the <commit> in the above description, except
-for the last two forms that use ".." notations, can be any
-<tree-ish>.
+in the last two forms that use ".." notations, can be any
+<tree>.  The third form ('git diff <commit> <commit>') can also
+be used to compare two <blob> objects.
 
 For a more complete list of ways to spell <commit>, see
-"SPECIFYING REVISIONS" section in linkgit:git-rev-parse[1].
+"SPECIFYING REVISIONS" section in linkgit:gitrevisions[7].
 However, "diff" is about comparing two _endpoints_, not ranges,
 and the range notations ("<commit>..<commit>" and
 "<commit>\...<commit>") do not mean a range as defined in the
-"SPECIFYING RANGES" section in linkgit:git-rev-parse[1].
+"SPECIFYING RANGES" section in linkgit:gitrevisions[7].
 
 OPTIONS
 -------
@@ -157,14 +165,14 @@
 rewrites (very expensive).
 <2> Output diff in reverse.
 
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
+SEE ALSO
+--------
+diff(1),
+linkgit:git-difftool[1],
+linkgit:git-log[1],
+linkgit:gitdiffcore[7],
+linkgit:git-format-patch[1],
+linkgit:git-apply[1]
 
 GIT
 ---
diff --git a/Documentation/git-difftool.txt b/Documentation/git-difftool.txt
index 96a6c51..31fc2e3 100644
--- a/Documentation/git-difftool.txt
+++ b/Documentation/git-difftool.txt
@@ -7,16 +7,24 @@
 
 SYNOPSIS
 --------
-'git difftool' [--tool=<tool>] [-y|--no-prompt|--prompt] [<'git diff' options>]
+[verse]
+'git difftool' [<options>] [<commit> [<commit>]] [--] [<path>...]
 
 DESCRIPTION
 -----------
-'git-difftool' is a git command that allows you to compare and edit files
+'git difftool' is a git command that allows you to compare and edit files
 between revisions using common diff tools.  'git difftool' is a frontend
-to 'git-diff' and accepts the same options and arguments.
+to 'git diff' and accepts the same options and arguments. See
+linkgit:git-diff[1].
 
 OPTIONS
 -------
+-d::
+--dir-diff::
+	Copy the modified files to a temporary location and perform
+	a directory diff on them. This mode never prompts before
+	launching the diff tool.
+
 -y::
 --no-prompt::
 	Do not prompt before launching a diff tool.
@@ -28,46 +36,65 @@
 
 -t <tool>::
 --tool=<tool>::
-	Use the diff tool specified by <tool>.
-	Valid merge tools are:
-	kdiff3, kompare, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff,
-	ecmerge, diffuse, opendiff and araxis.
+	Use the diff tool specified by <tool>.  Valid values include
+	emerge, kompare, meld, and vimdiff. Run `git difftool --tool-help`
+	for the list of valid <tool> settings.
 +
-If a diff tool is not specified, 'git-difftool'
+If a diff tool is not specified, 'git difftool'
 will use the configuration variable `diff.tool`.  If the
-configuration variable `diff.tool` is not set, 'git-difftool'
+configuration variable `diff.tool` is not set, 'git difftool'
 will pick a suitable default.
 +
 You can explicitly provide a full path to the tool by setting the
 configuration variable `difftool.<tool>.path`. For example, you
 can configure the absolute path to kdiff3 by setting
-`difftool.kdiff3.path`. Otherwise, 'git-difftool' assumes the
+`difftool.kdiff3.path`. Otherwise, 'git difftool' assumes the
 tool is available in PATH.
 +
 Instead of running one of the known diff tools,
-'git-difftool' can be customized to run an alternative program
+'git difftool' can be customized to run an alternative program
 by specifying the command line to invoke in a configuration
 variable `difftool.<tool>.cmd`.
 +
-When 'git-difftool' is invoked with this tool (either through the
+When 'git difftool' is invoked with this tool (either through the
 `-t` or `--tool` option or the `diff.tool` configuration variable)
 the configured command line will be invoked with the following
 variables available: `$LOCAL` is set to the name of the temporary
 file containing the contents of the diff pre-image and `$REMOTE`
 is set to the name of the temporary file containing the contents
-of the diff post-image.  `$BASE` is provided for compatibility
-with custom merge tool commands and has the same value as `$LOCAL`.
+of the diff post-image.  `$MERGED` is the name of the file which is
+being compared. `$BASE` is provided for compatibility
+with custom merge tool commands and has the same value as `$MERGED`.
+
+--tool-help::
+	Print a list of diff tools that may be used with `--tool`.
+
+-x <command>::
+--extcmd=<command>::
+	Specify a custom command for viewing diffs.
+	'git-difftool' ignores the configured defaults and runs
+	`$command $LOCAL $REMOTE` when this option is specified.
+	Additionally, `$BASE` is set in the environment.
+
+-g::
+--gui::
+	When 'git-difftool' is invoked with the `-g` or `--gui` option
+	the default diff tool will be read from the configured
+	`diff.guitool` variable instead of `diff.tool`.
 
 See linkgit:git-diff[1] for the full list of supported options.
 
 CONFIG VARIABLES
 ----------------
-'git-difftool' falls back to 'git-mergetool' config variables when the
+'git difftool' falls back to 'git mergetool' config variables when the
 difftool equivalents have not been defined.
 
 diff.tool::
 	The default diff tool to use.
 
+diff.guitool::
+	The default diff tool to use when `--gui` is specified.
+
 difftool.<tool>.path::
 	Override the path for the given tool.  This is useful in case
 	your tool is not in the PATH.
@@ -91,15 +118,6 @@
 linkgit:git-config[1]::
 	 Get and set repository or global options
 
-
-AUTHOR
-------
-Written by David Aguilar <davvid@gmail.com>.
-
-Documentation
---------------
-Documentation by David Aguilar and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-fast-export.txt b/Documentation/git-fast-export.txt
index 75b06f3..d6487e1 100644
--- a/Documentation/git-fast-export.txt
+++ b/Documentation/git-fast-export.txt
@@ -8,23 +8,24 @@
 
 SYNOPSIS
 --------
+[verse]
 'git fast-export [options]' | 'git fast-import'
 
 DESCRIPTION
 -----------
 This program dumps the given revisions in a form suitable to be piped
-into 'git-fast-import'.
+into 'git fast-import'.
 
 You can use it as a human-readable bundle replacement (see
 linkgit:git-bundle[1]), or as a kind of an interactive
-'git-filter-branch'.
+'git filter-branch'.
 
 
 OPTIONS
 -------
 --progress=<n>::
 	Insert 'progress' statements every <n> objects, to be shown by
-	'git-fast-import' during import.
+	'git fast-import' during import.
 
 --signed-tags=(verbatim|warn|strip|abort)::
 	Specify how to handle signed tags.  Since any transformation
@@ -37,7 +38,7 @@
 and with 'warn', they will be exported, but you will see a warning.
 
 --tag-of-filtered-object=(abort|drop|rewrite)::
-	Specify how to handle tags whose tagged objectis filtered out.
+	Specify how to handle tags whose tagged object is filtered out.
 	Since revisions and files to export can be limited by path,
 	tagged objects may be filtered completely.
 +
@@ -82,6 +83,10 @@
 	allow that.  So fake a tagger to be able to fast-import the
 	output.
 
+--use-done-feature::
+	Start the stream with a 'feature done' stanza, and terminate
+	it with a 'done' command.
+
 --no-data::
 	Skip output of blob objects and instead refer to blobs via
 	their original SHA-1 hash.  This is useful when rewriting the
@@ -90,10 +95,16 @@
 	resulting stream can only be used by a repository which
 	already contains the necessary objects.
 
-[git-rev-list-args...]::
-       A list of arguments, acceptable to 'git-rev-parse' and
-       'git-rev-list', that specifies the specific objects and references
-       to export.  For example, `master\~10..master` causes the
+--full-tree::
+	This option will cause fast-export to issue a "deleteall"
+	directive for each commit followed by a full list of all files
+	in the commit (as opposed to just listing the files which are
+	different from the commit's first parent).
+
+[<git-rev-list-args>...]::
+       A list of arguments, acceptable to 'git rev-parse' and
+       'git rev-list', that specifies the specific objects and references
+       to export.  For example, `master~10..master` causes the
        current master reference to be exported along with all objects
        added since its 10th ancestor commit.
 
@@ -125,19 +136,10 @@
 Limitations
 -----------
 
-Since 'git-fast-import' cannot tag trees, you will not be
+Since 'git fast-import' cannot tag trees, you will not be
 able to export the linux-2.6.git repository completely, as it contains
 a tag referencing a tree instead of a commit.
 
-
-Author
-------
-Written by Johannes E. Schindelin <johannes.schindelin@gmx.de>.
-
-Documentation
---------------
-Documentation by Johannes E. Schindelin <johannes.schindelin@gmx.de>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-fast-import.txt b/Documentation/git-fast-import.txt
index c2f483a..2620d28 100644
--- a/Documentation/git-fast-import.txt
+++ b/Documentation/git-fast-import.txt
@@ -8,6 +8,7 @@
 
 SYNOPSIS
 --------
+[verse]
 frontend | 'git fast-import' [options]
 
 DESCRIPTION
@@ -15,7 +16,7 @@
 This program is usually not what the end user wants to run directly.
 Most end users want to use one of the existing frontend programs,
 which parses a specific type of foreign source and feeds the contents
-stored there to 'git-fast-import'.
+stored there to 'git fast-import'.
 
 fast-import reads a mixed command/data stream from standard input and
 writes one or more packfiles directly into the current repository.
@@ -24,7 +25,7 @@
 with the newly imported data.
 
 The fast-import backend itself can import into an empty repository (one that
-has already been initialized by 'git-init') or incrementally
+has already been initialized by 'git init') or incrementally
 update an existing populated repository.  Whether or not incremental
 imports are supported from a particular foreign source depends on
 the frontend program in use.
@@ -44,11 +45,14 @@
 	not contain the old commit).
 
 --max-pack-size=<n>::
-	Maximum size of each output packfile, expressed in MiB.
-	The default is 4096 (4 GiB) as that is the maximum allowed
-	packfile size (due to file format limitations). Some
-	importers may wish to lower this, such as to ensure the
-	resulting packfiles fit on CDs.
+	Maximum size of each output packfile.
+	The default is unlimited.
+
+--big-file-threshold=<n>::
+	Maximum size of a blob that fast-import will attempt to
+	create a delta for, expressed in bytes.  The default is 512m
+	(512 MiB).  Some importers may wish to lower this on systems
+	with constrained memory.
 
 --depth=<n>::
 	Maximum delta depth, for blob and tree deltification.
@@ -75,6 +79,36 @@
 	set of marks.  If a mark is defined to different values,
 	the last file wins.
 
+--import-marks-if-exists=<file>::
+	Like --import-marks but instead of erroring out, silently
+	skips the file if it does not exist.
+
+--relative-marks::
+	After specifying --relative-marks the paths specified
+	with --import-marks= and --export-marks= are relative
+	to an internal directory in the current repository.
+	In git-fast-import this means that the paths are relative
+	to the .git/info/fast-import directory. However, other
+	importers may use a different location.
+
+--no-relative-marks::
+	Negates a previous --relative-marks. Allows for combining
+	relative and non-relative marks by interweaving
+	--(no-)-relative-marks with the --(import|export)-marks=
+	options.
+
+--cat-blob-fd=<fd>::
+	Write responses to `cat-blob` and `ls` queries to the
+	file descriptor <fd> instead of `stdout`.  Allows `progress`
+	output intended for the end-user to be separated from other
+	output.
+
+--done::
+	Require a `done` command at the end of the stream.
+	This option might be useful for detecting errors that
+	cause the frontend to terminate before it has started to
+	write a stream.
+
 --export-pack-edges=<file>::
 	After creating a packfile, print a line of data to
 	<file> listing the filename of the packfile and the last
@@ -82,7 +116,7 @@
 	This information may be useful after importing projects
 	whose total object set exceeds the 4 GiB packfile limit,
 	as these commits can be used as edge points during calls
-	to 'git-pack-objects'.
+	to 'git pack-objects'.
 
 --quiet::
 	Disable all non-fatal output, making fast-import silent when it
@@ -124,9 +158,9 @@
 
 Parallel Operation
 ------------------
-Like 'git-push' or 'git-fetch', imports handled by fast-import are safe to
+Like 'git push' or 'git fetch', imports handled by fast-import are safe to
 run alongside parallel `git repack -a -d` or `git gc` invocations,
-or any other Git operation (including 'git-prune', as loose objects
+or any other Git operation (including 'git prune', as loose objects
 are never used by fast-import).
 
 fast-import does not lock the branch or tag refs it is actively importing.
@@ -138,7 +172,7 @@
 prints a warning message.  fast-import will always attempt to update all
 branch refs, and does not stop on the first failure.
 
-Branch updates can be forced with \--force, but its recommended that
+Branch updates can be forced with \--force, but it's recommended that
 this only be used on an otherwise quiet repository.  Using \--force
 is not necessary for an initial import into an empty repository.
 
@@ -170,7 +204,8 @@
 Ruby is being used.
 
 fast-import is very strict about its input.  Where we say SP below we mean
-*exactly* one space.  Likewise LF means one (and only one) linefeed.
+*exactly* one space.  Likewise LF means one (and only one) linefeed
+and HT one (and only one) horizontal tab.
 Supplying additional whitespace characters will cause unexpected
 results, such as branch names or file names with leading or trailing
 spaces in their name, or early termination of fast-import when it encounters
@@ -220,7 +255,7 @@
 +
 An example value is ``Tue Feb 6 11:22:18 2007 -0500''.  The Git
 parser is accurate, but a little on the lenient side.  It is the
-same parser used by 'git-am' when applying patches
+same parser used by 'git am' when applying patches
 received from email.
 +
 Some malformed strings may be accepted as valid dates.  In some of
@@ -253,10 +288,10 @@
 created by fast-import.  There is no way to specify a different time or
 timezone.
 +
-This particular format is supplied as its short to implement and
+This particular format is supplied as it's short to implement and
 may be useful to a process that wants to create a new commit
 right now, without needing to use a working directory or
-'git-update-index'.
+'git update-index'.
 +
 If separate `author` and `committer` commands are used in a `commit`
 the timestamps may not match, as the system clock will be polled
@@ -303,6 +338,30 @@
 	standard output.  This command is optional and is not needed
 	to perform an import.
 
+`done`::
+	Marks the end of the stream. This command is optional
+	unless the `done` feature was requested using the
+	`--done` command line option or `feature done` command.
+
+`cat-blob`::
+	Causes fast-import to print a blob in 'cat-file --batch'
+	format to the file descriptor set with `--cat-blob-fd` or
+	`stdout` if unspecified.
+
+`ls`::
+	Causes fast-import to print a line describing a directory
+	entry in 'ls-tree' format to the file descriptor set with
+	`--cat-blob-fd` or `stdout` if unspecified.
+
+`feature`::
+	Require that fast-import supports the specified feature, or
+	abort if it does not.
+
+`option`::
+	Specify any of the options listed under OPTIONS that do not
+	change stream semantic to suit the frontend's needs. This
+	command is optional and is not needed to perform an import.
+
 `commit`
 ~~~~~~~~
 Create or update a branch with a new commit, recording one logical
@@ -311,12 +370,12 @@
 ....
 	'commit' SP <ref> LF
 	mark?
-	('author' SP <name> SP LT <email> GT SP <when> LF)?
-	'committer' SP <name> SP LT <email> GT SP <when> LF
+	('author' (SP <name>)? SP LT <email> GT SP <when> LF)?
+	'committer' (SP <name>)? SP LT <email> GT SP <when> LF
 	data
 	('from' SP <committish> LF)?
 	('merge' SP <committish> LF)?
-	(filemodify | filedelete | filecopy | filerename | filedeleteall)*
+	(filemodify | filedelete | filecopy | filerename | filedeleteall | notemodify)*
 	LF?
 ....
 
@@ -339,14 +398,13 @@
 and are not interpreted by Git.  Currently they must be encoded in
 UTF-8, as fast-import does not permit other encodings to be specified.
 
-Zero or more `filemodify`, `filedelete`, `filecopy`, `filerename`
-and `filedeleteall` commands
+Zero or more `filemodify`, `filedelete`, `filecopy`, `filerename`,
+`filedeleteall` and `notemodify` commands
 may be included to update the contents of the branch prior to
 creating the commit.  These commands may be supplied in any order.
 However it is recommended that a `filedeleteall` command precede
-all `filemodify`, `filecopy` and `filerename` commands in the same
-commit, as `filedeleteall`
-wipes the branch clean (see below).
+all `filemodify`, `filecopy`, `filerename` and `notemodify` commands in
+the same commit, as `filedeleteall` wipes the branch clean (see below).
 
 The `LF` after the command is optional (it used to be required).
 
@@ -368,8 +426,8 @@
 (``cm@example.com'').  `LT` and `GT` are the literal less-than (\x3c)
 and greater-than (\x3e) symbols.  These are required to delimit
 the email address from the other fields in the line.  Note that
-`<name>` is free-form and may contain any sequence of bytes, except
-`LT` and `LF`.  It is typically UTF-8 encoded.
+`<name>` and `<email>` are free-form and may contain any sequence
+of bytes, except `LT`, `GT` and `LF`.  `<name>` is typically UTF-8 encoded.
 
 The time of the change is specified by `<when>` using the date format
 that was selected by the \--date-format=<fmt> command line option.
@@ -398,7 +456,7 @@
 Here `<committish>` is any of the following:
 
 * The name of an existing branch already in fast-import's internal branch
-  table.  If fast-import doesn't know the name, its treated as a SHA-1
+  table.  If fast-import doesn't know the name, it's treated as a SHA-1
   expression.
 
 * A mark reference, `:<idnum>`, where `<idnum>` is the mark number.
@@ -414,16 +472,16 @@
 * A complete 40 byte or abbreviated commit SHA-1 in hex.
 
 * Any valid Git SHA-1 expression that resolves to a commit.  See
-  ``SPECIFYING REVISIONS'' in linkgit:git-rev-parse[1] for details.
+  ``SPECIFYING REVISIONS'' in linkgit:gitrevisions[7] for details.
 
 The special case of restarting an incremental import from the
 current branch value should be written as:
 ----
 	from refs/heads/branch^0
 ----
-The `{caret}0` suffix is necessary as fast-import does not permit a branch to
+The `^0` suffix is necessary as fast-import does not permit a branch to
 start from itself, and the branch is created in memory before the
-`from` command is even read from the input.  Adding `{caret}0` will force
+`from` command is even read from the input.  Adding `^0` will force
 fast-import to resolve the commit through Git's revision parsing library,
 rather than its internal branch table, thereby loading in the
 existing value of the branch.
@@ -457,9 +515,11 @@
 	'M' SP <mode> SP <dataref> SP <path> LF
 ....
 +
-Here `<dataref>` can be either a mark reference (`:<idnum>`)
+Here usually `<dataref>` must be either a mark reference (`:<idnum>`)
 set by a prior `blob` command, or a full 40-byte SHA-1 of an
-existing Git blob object.
+existing Git blob object.  If `<mode>` is `040000`` then
+`<dataref>` must be the full 40-byte SHA-1 of an existing
+Git tree object or a mark reference set with `--import-marks`.
 
 Inline data format::
 	The data content for the file has not been supplied yet.
@@ -484,6 +544,8 @@
 * `160000`: A gitlink, SHA-1 of the object refers to a commit in
   another repository. Git links can only be specified by SHA or through
   a commit mark. They are used to implement submodules.
+* `040000`: A subdirectory.  Subdirectories can only be specified by
+  SHA or through a tree mark set with `--import-marks`.
 
 In both formats `<path>` is the complete path of the file to be added
 (if not already existing) or modified (if already existing).
@@ -503,6 +565,8 @@
 * contain the special component `.` or `..` (e.g. `foo/./bar` and
   `foo/../bar` are invalid).
 
+The root of the tree can be represented by an empty string as `<path>`.
+
 It is recommended that `<path>` always be encoded using UTF-8.
 
 `filedelete`
@@ -595,6 +659,45 @@
 projects); so frontends that can easily obtain only the affected
 paths for a commit are encouraged to do so.
 
+`notemodify`
+^^^^^^^^^^^^
+Included in a `commit` `<notes_ref>` command to add a new note
+annotating a `<committish>` or change this annotation contents.
+Internally it is similar to filemodify 100644 on `<committish>`
+path (maybe split into subdirectories). It's not advised to
+use any other commands to write to the `<notes_ref>` tree except
+`filedeleteall` to delete all existing notes in this tree.
+This command has two different means of specifying the content
+of the note.
+
+External data format::
+	The data content for the note was already supplied by a prior
+	`blob` command.  The frontend just needs to connect it to the
+	commit that is to be annotated.
++
+....
+	'N' SP <dataref> SP <committish> LF
+....
++
+Here `<dataref>` can be either a mark reference (`:<idnum>`)
+set by a prior `blob` command, or a full 40-byte SHA-1 of an
+existing Git blob object.
+
+Inline data format::
+	The data content for the note has not been supplied yet.
+	The frontend wants to supply it as part of this modify
+	command.
++
+....
+	'N' SP 'inline' SP <committish> LF
+	data
+....
++
+See below for a detailed description of the `data` command.
+
+In both formats `<committish>` is any of the commit specification
+expressions also accepted by `from` (see above).
+
 `mark`
 ~~~~~~
 Arranges for fast-import to save a reference to the current object, allowing
@@ -624,7 +727,7 @@
 ....
 	'tag' SP <name> LF
 	'from' SP <committish> LF
-	'tagger' SP <name> SP LT <email> GT SP <when> LF
+	'tagger' (SP <name>)? SP LT <email> GT SP <when> LF
 	data
 ....
 
@@ -657,7 +760,7 @@
 complete set of bytes which normally goes into such a signature.
 If signing is required, create lightweight tags from within fast-import with
 `reset`, then create the annotated versions of those tags offline
-with the standard 'git-tag' process.
+with the standard 'git tag' process.
 
 `reset`
 ~~~~~~~
@@ -703,7 +806,7 @@
 
 The mark command is optional here as some frontends have chosen
 to generate the Git SHA-1 for the blob on their own, and feed that
-directly to `commit`.  This is typically more work than its worth
+directly to `commit`.  This is typically more work than it's worth
 however, as marks are inexpensive to store and easy to use.
 
 `data`
@@ -813,6 +916,205 @@
 inform the reader when the `checkpoint` has been completed and it
 can safely access the refs that fast-import updated.
 
+`cat-blob`
+~~~~~~~~~~
+Causes fast-import to print a blob to a file descriptor previously
+arranged with the `--cat-blob-fd` argument.  The command otherwise
+has no impact on the current import; its main purpose is to
+retrieve blobs that may be in fast-import's memory but not
+accessible from the target repository.
+
+....
+	'cat-blob' SP <dataref> LF
+....
+
+The `<dataref>` can be either a mark reference (`:<idnum>`)
+set previously or a full 40-byte SHA-1 of a Git blob, preexisting or
+ready to be written.
+
+Output uses the same format as `git cat-file --batch`:
+
+====
+	<sha1> SP 'blob' SP <size> LF
+	<contents> LF
+====
+
+This command can be used anywhere in the stream that comments are
+accepted.  In particular, the `cat-blob` command can be used in the
+middle of a commit but not in the middle of a `data` command.
+
+See ``Responses To Commands'' below for details about how to read
+this output safely.
+
+`ls`
+~~~~
+Prints information about the object at a path to a file descriptor
+previously arranged with the `--cat-blob-fd` argument.  This allows
+printing a blob from the active commit (with `cat-blob`) or copying a
+blob or tree from a previous commit for use in the current one (with
+`filemodify`).
+
+The `ls` command can be used anywhere in the stream that comments are
+accepted, including the middle of a commit.
+
+Reading from the active commit::
+	This form can only be used in the middle of a `commit`.
+	The path names a directory entry within fast-import's
+	active commit.  The path must be quoted in this case.
++
+....
+	'ls' SP <path> LF
+....
+
+Reading from a named tree::
+	The `<dataref>` can be a mark reference (`:<idnum>`) or the
+	full 40-byte SHA-1 of a Git tag, commit, or tree object,
+	preexisting or waiting to be written.
+	The path is relative to the top level of the tree
+	named by `<dataref>`.
++
+....
+	'ls' SP <dataref> SP <path> LF
+....
+
+See `filemodify` above for a detailed description of `<path>`.
+
+Output uses the same format as `git ls-tree <tree> -- <path>`:
+
+====
+	<mode> SP ('blob' | 'tree' | 'commit') SP <dataref> HT <path> LF
+====
+
+The <dataref> represents the blob, tree, or commit object at <path>
+and can be used in later 'cat-blob', 'filemodify', or 'ls' commands.
+
+If there is no file or subtree at that path, 'git fast-import' will
+instead report
+
+====
+	missing SP <path> LF
+====
+
+See ``Responses To Commands'' below for details about how to read
+this output safely.
+
+`feature`
+~~~~~~~~~
+Require that fast-import supports the specified feature, or abort if
+it does not.
+
+....
+	'feature' SP <feature> ('=' <argument>)? LF
+....
+
+The <feature> part of the command may be any one of the following:
+
+date-format::
+export-marks::
+relative-marks::
+no-relative-marks::
+force::
+	Act as though the corresponding command-line option with
+	a leading '--' was passed on the command line
+	(see OPTIONS, above).
+
+import-marks::
+import-marks-if-exists::
+	Like --import-marks except in two respects: first, only one
+	"feature import-marks" or "feature import-marks-if-exists"
+	command is allowed per stream; second, an --import-marks=
+	or --import-marks-if-exists command-line option overrides
+	any of these "feature" commands in the stream; third,
+	"feature import-marks-if-exists" like a corresponding
+	command-line option silently skips a nonexistent file.
+
+cat-blob::
+ls::
+	Require that the backend support the 'cat-blob' or 'ls' command.
+	Versions of fast-import not supporting the specified command
+	will exit with a message indicating so.
+	This lets the import error out early with a clear message,
+	rather than wasting time on the early part of an import
+	before the unsupported command is detected.
+
+notes::
+	Require that the backend support the 'notemodify' (N)
+	subcommand to the 'commit' command.
+	Versions of fast-import not supporting notes will exit
+	with a message indicating so.
+
+done::
+	Error out if the stream ends without a 'done' command.
+	Without this feature, errors causing the frontend to end
+	abruptly at a convenient point in the stream can go
+	undetected.
+
+`option`
+~~~~~~~~
+Processes the specified option so that git fast-import behaves in a
+way that suits the frontend's needs.
+Note that options specified by the frontend are overridden by any
+options the user may specify to git fast-import itself.
+
+....
+    'option' SP <option> LF
+....
+
+The `<option>` part of the command may contain any of the options
+listed in the OPTIONS section that do not change import semantics,
+without the leading '--' and is treated in the same way.
+
+Option commands must be the first commands on the input (not counting
+feature commands), to give an option command after any non-option
+command is an error.
+
+The following commandline options change import semantics and may therefore
+not be passed as option:
+
+* date-format
+* import-marks
+* export-marks
+* cat-blob-fd
+* force
+
+`done`
+~~~~~~
+If the `done` feature is not in use, treated as if EOF was read.
+This can be used to tell fast-import to finish early.
+
+If the `--done` command line option or `feature done` command is
+in use, the `done` command is mandatory and marks the end of the
+stream.
+
+Responses To Commands
+---------------------
+New objects written by fast-import are not available immediately.
+Most fast-import commands have no visible effect until the next
+checkpoint (or completion).  The frontend can send commands to
+fill fast-import's input pipe without worrying about how quickly
+they will take effect, which improves performance by simplifying
+scheduling.
+
+For some frontends, though, it is useful to be able to read back
+data from the current repository as it is being updated (for
+example when the source material describes objects in terms of
+patches to be applied to previously imported objects).  This can
+be accomplished by connecting the frontend and fast-import via
+bidirectional pipes:
+
+====
+	mkfifo fast-import-output
+	frontend <fast-import-output |
+	git fast-import >fast-import-output
+====
+
+A frontend set up this way can use `progress`, `ls`, and `cat-blob`
+commands to read information from the import in progress.
+
+To avoid deadlock, such frontends must completely consume any
+pending output from `progress`, `ls`, and `cat-blob` before
+performing writes to fast-import that might block.
+
 Crash Reports
 -------------
 If fast-import is supplied invalid input it will terminate with a
@@ -958,7 +1260,7 @@
 
 When committing fixups, consider using `merge` to connect the
 commit(s) which are supplying file revisions to the fixup branch.
-Doing so will allow tools such as 'git-blame' to track
+Doing so will allow tools such as 'git blame' to track
 through the real commit history and properly annotate the source
 files.
 
@@ -987,7 +1289,7 @@
 ~~~~~~~~~~~~~~~~~~~~~~~~~
 If you are repacking very old imported data (e.g. older than the
 last year), consider expending some extra CPU time and supplying
-\--window=50 (or higher) when you run 'git-repack'.
+\--window=50 (or higher) when you run 'git repack'.
 This will take longer, but will also produce a smaller packfile.
 You only need to expend the effort once, and everyone using your
 project will benefit from the smaller repository.
@@ -1111,14 +1413,13 @@
 projects with 2,000+ branches and 45,114+ files in a very limited
 memory footprint (less than 2.7 MiB per active branch).
 
-
-Author
-------
-Written by Shawn O. Pearce <spearce@spearce.org>.
-
-Documentation
---------------
-Documentation by Shawn O. Pearce <spearce@spearce.org>.
+Signals
+-------
+Sending *SIGUSR1* to the 'git fast-import' process ends the current
+packfile early, simulating a `checkpoint` command.  The impatient
+operator can use this facility to peek at the objects and refs from an
+import in progress, at the cost of some added running time and worse
+compression.
 
 GIT
 ---
diff --git a/Documentation/git-fetch-pack.txt b/Documentation/git-fetch-pack.txt
index 47448da..474fa30 100644
--- a/Documentation/git-fetch-pack.txt
+++ b/Documentation/git-fetch-pack.txt
@@ -8,17 +8,18 @@
 
 SYNOPSIS
 --------
+[verse]
 'git fetch-pack' [--all] [--quiet|-q] [--keep|-k] [--thin] [--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] [--no-progress] [-v] [<host>:]<directory> [<refs>...]
 
 DESCRIPTION
 -----------
-Usually you would want to use 'git-fetch', which is a
+Usually you would want to use 'git fetch', which is a
 higher level wrapper of this command, instead.
 
 Invokes 'git-upload-pack' on a possibly remote repository
 and asks it to send objects missing from this repository, to
 update the named heads.  The list of commits available locally
-is found out by scanning local $GIT_DIR/refs/ and sent to
+is found out by scanning the local refs/ hierarchy and sent to
 'git-upload-pack' running on the other end.
 
 This command degenerates to download everything to complete the
@@ -31,21 +32,31 @@
 --all::
 	Fetch all remote refs.
 
+--stdin::
+	Take the list of refs from stdin, one per line. If there
+	are refs specified on the command line in addition to this
+	option, then the refs from stdin are processed after those
+	on the command line.
++
+If '--stateless-rpc' is specified together with this option then
+the list of refs must be in packet format (pkt-line). Each ref must
+be in a separate packet, and the list must end with a flush packet.
+
 -q::
 --quiet::
-	Pass '-q' flag to 'git-unpack-objects'; this makes the
+	Pass '-q' flag to 'git unpack-objects'; this makes the
 	cloning process less verbose.
 
 -k::
 --keep::
-	Do not invoke 'git-unpack-objects' on received data, but
+	Do not invoke 'git unpack-objects' on received data, but
 	create a single packfile out of it instead, and store it
 	in the object database. If provided twice then the pack is
 	locked against repacking.
 
 --thin::
-	Spend extra cycles to minimize the number of objects to be sent.
-	Use it on slower connection.
+	Fetch a "thin" pack, which records objects in deltified form based
+	on objects not included in the pack to reduce network traffic.
 
 --include-tag::
 	If the remote side supports it, annotated tags objects will
@@ -90,15 +101,6 @@
 	$GIT_DIR (e.g. "HEAD", "refs/heads/master").  When
 	unspecified, update from all heads the remote side has.
 
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by Junio C Hamano.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt
index d3164c5..b41d7c1 100644
--- a/Documentation/git-fetch.txt
+++ b/Documentation/git-fetch.txt
@@ -8,19 +8,23 @@
 
 SYNOPSIS
 --------
-'git fetch' <options> <repository> <refspec>...
+[verse]
+'git fetch' [<options>] [<repository> [<refspec>...]]
+'git fetch' [<options>] <group>
+'git fetch' --multiple [<options>] [(<repository> | <group>)...]
+'git fetch' --all [<options>]
 
 
 DESCRIPTION
 -----------
-Fetches named heads or tags from another repository, along with
-the objects necessary to complete them.
+Fetches named heads or tags from one or more other repositories,
+along with the objects necessary to complete them.
 
 The ref names and their object names of fetched refs are stored
 in `.git/FETCH_HEAD`.  This information is left for a later merge
-operation done by 'git-merge'.
+operation done by 'git merge'.
 
-When <refspec> stores the fetched result in tracking branches,
+When <refspec> stores the fetched result in remote-tracking branches,
 the tags that point at these branches are automatically
 followed.  This is done by first fetching from the remote using
 the given <refspec>s, and if the repository has objects that are
@@ -28,6 +32,10 @@
 those missing tags.  If the other end has tags that point at
 branches you are not interested in, you will not get them.
 
+'git fetch' can fetch from either a single named repository,
+or from several repositories at once if <group> is given and
+there is a remotes.<group> entry in the configuration file.
+(See linkgit:git-config[1]).
 
 OPTIONS
 -------
@@ -37,20 +45,48 @@
 
 include::urls-remotes.txt[]
 
+
+EXAMPLES
+--------
+
+* Update the remote-tracking branches:
++
+------------------------------------------------
+$ git fetch origin
+------------------------------------------------
++
+The above command copies all branches from the remote refs/heads/
+namespace and stores them to the local refs/remotes/origin/ namespace,
+unless the branch.<name>.fetch option is used to specify a non-default
+refspec.
+
+* Using refspecs explicitly:
++
+------------------------------------------------
+$ git fetch origin +pu:pu maint:tmp
+------------------------------------------------
++
+This updates (or creates, as necessary) branches `pu` and `tmp` in
+the local repository by fetching from the branches (respectively)
+`pu` and `maint` from the remote repository.
++
+The `pu` branch will be updated even if it is does not fast-forward,
+because it is prefixed with a plus sign; `tmp` will not be.
+
+
+BUGS
+----
+Using --recurse-submodules can only fetch new commits in already checked
+out submodules right now. When e.g. upstream added a new submodule in the
+just fetched commits of the superproject the submodule itself can not be
+fetched, making it impossible to check out that submodule later without
+having to do a fetch again. This is expected to be fixed in a future git
+version.
+
 SEE ALSO
 --------
 linkgit:git-pull[1]
 
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org> and
-Junio C Hamano <gitster@pobox.com>
-
-Documentation
--------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt
index ab527b5..81f5823 100644
--- a/Documentation/git-filter-branch.txt
+++ b/Documentation/git-filter-branch.txt
@@ -12,6 +12,7 @@
 	[--index-filter <command>] [--parent-filter <command>]
 	[--msg-filter <command>] [--commit-filter <command>]
 	[--tag-name-filter <command>] [--subdirectory-filter <directory>]
+	[--prune-empty]
 	[--original <namespace>] [-d <directory>] [-f | --force]
 	[--] [<rev-list options>...]
 
@@ -31,8 +32,9 @@
 useful in the future for compensating for some git bugs or such,
 therefore such a usage is permitted.
 
-*NOTE*: This command honors `.git/info/grafts`. If you have any grafts
-defined, running this command will make them permanent.
+*NOTE*: This command honors `.git/info/grafts` and `.git/refs/replace/`.
+If you have any grafts or replacement refs defined, running this command
+will make them permanent.
 
 *WARNING*! The rewritten history will have different object names for all
 the objects and will not converge with the original branch.  You will not
@@ -80,7 +82,7 @@
 	This filter may be used if you only need to modify the environment
 	in which the commit will be performed.  Specifically, you might
 	want to rewrite the author/committer name/email/time environment
-	variables (see linkgit:git-commit[1] for details).  Do not forget
+	variables (see linkgit:git-commit-tree[1] for details).  Do not forget
 	to re-export the variables.
 
 --tree-filter <command>::
@@ -94,8 +96,8 @@
 --index-filter <command>::
 	This is the filter for rewriting the index.  It is similar to the
 	tree filter but does not check out the tree, which makes it much
-	faster.  Frequently used with `git rm \--cached
-	\--ignore-unmatch ...`, see EXAMPLES below.  For hairy
+	faster.  Frequently used with `git rm --cached
+	--ignore-unmatch ...`, see EXAMPLES below.  For hairy
 	cases, see linkgit:git-update-index[1].
 
 --parent-filter <command>::
@@ -115,8 +117,8 @@
 --commit-filter <command>::
 	This is the filter for performing the commit.
 	If this filter is specified, it will be called instead of the
-	'git-commit-tree' command, with arguments of the form
-	"<TREE_ID> [-p <PARENT_COMMIT_ID>]..." and the log message on
+	'git commit-tree' command, with arguments of the form
+	"<TREE_ID> [(-p <PARENT_COMMIT_ID>)...]" and the log message on
 	stdin.  The commit id is expected on stdout.
 +
 As a special extension, the commit filter may emit multiple
@@ -126,10 +128,10 @@
 You can use the 'map' convenience function in this filter, and other
 convenience functions, too.  For example, calling 'skip_commit "$@"'
 will leave out the current commit (but not its changes! If you want
-that, use 'git-rebase' instead).
+that, use 'git rebase' instead).
 +
-You can also use the 'git_commit_non_empty_tree "$@"' instead of
-'git commit-tree "$@"' if you don't wish to keep commits with a single parent
+You can also use the `git_commit_non_empty_tree "$@"` instead of
+`git commit-tree "$@"` if you don't wish to keep commits with a single parent
 and that makes no change to the tree.
 
 --tag-name-filter <command>::
@@ -158,7 +160,7 @@
 --subdirectory-filter <directory>::
 	Only look at the history which touches the given subdirectory.
 	The result will contain that directory (and only that) as its
-	project root.
+	project root. Implies <<Remap_to_ancestor>>.
 
 --prune-empty::
 	Some kind of filters will generate empty commits, that left the tree
@@ -167,7 +169,7 @@
 	and only one parent, it will hence keep merges points. Also, this
 	option is not compatible with the use of '--commit-filter'. Though you
 	just need to use the function 'git_commit_non_empty_tree "$@"' instead
-	of the 'git commit-tree "$@"' idiom in your commit filter to make that
+	of the `git commit-tree "$@"` idiom in your commit filter to make that
 	happen.
 
 --original <namespace>::
@@ -184,15 +186,26 @@
 
 -f::
 --force::
-	'git-filter-branch' refuses to start with an existing temporary
+	'git filter-branch' refuses to start with an existing temporary
 	directory or when there are already refs starting with
 	'refs/original/', unless forced.
 
 <rev-list options>...::
-	Arguments for 'git-rev-list'.  All positive refs included by
+	Arguments for 'git rev-list'.  All positive refs included by
 	these options are rewritten.  You may also specify options
 	such as '--all', but you must use '--' to separate them from
-	the 'git-filter-branch' options.
+	the 'git filter-branch' options. Implies <<Remap_to_ancestor>>.
+
+
+[[Remap_to_ancestor]]
+Remap to ancestor
+~~~~~~~~~~~~~~~~~
+
+By using linkgit:rev-list[1] arguments, e.g., path limiters, you can limit the
+set of revisions which get rewritten. However, positive refs on the command
+line are distinguished: we don't let them be excluded by such limiters. For
+this purpose, they are instead rewritten to point at the nearest ancestor that
+was not excluded.
 
 
 Examples
@@ -209,11 +222,11 @@
 a simple `rm filename` will fail for that tree and commit.
 Thus you may instead want to use `rm -f filename` as the script.
 
-Using `\--index-filter` with 'git-rm' yields a significantly faster
+Using `--index-filter` with 'git rm' yields a significantly faster
 version.  Like with using `rm filename`, `git rm --cached filename`
 will fail if the file is absent from the tree of a commit.  If you
 want to "completely forget" a file, it does not matter when it entered
-history, so we also add `\--ignore-unmatch`:
+history, so we also add `--ignore-unmatch`:
 
 --------------------------------------------------------------------------
 git filter-branch --index-filter 'git rm --cached --ignore-unmatch filename' HEAD
@@ -229,8 +242,8 @@
 -------------------------------------------------------
 
 Thus you can, e.g., turn a library subdirectory into a repository of
-its own.  Note the `\--` that separates 'filter-branch' options from
-revision options, and the `\--all` to rewrite all branches and tags.
+its own.  Note the `--` that separates 'filter-branch' options from
+revision options, and the `--all` to rewrite all branches and tags.
 
 To set a commit (which typically is at the tip of another
 history) to be the parent of the current initial commit, in
@@ -291,7 +304,7 @@
 as their parents instead of the merge commit.
 
 You can rewrite the commit log messages using `--msg-filter`.  For
-example, 'git-svn-id' strings in a repository created by 'git-svn' can
+example, 'git svn-id' strings in a repository created by 'git svn' can
 be removed this way:
 
 -------------------------------------------------------
@@ -302,13 +315,23 @@
 
 To restrict rewriting to only part of the history, specify a revision
 range in addition to the new branch name.  The new branch name will
-point to the top-most revision that a 'git-rev-list' of this range
+point to the top-most revision that a 'git rev-list' of this range
 will print.
 
+If you need to add 'Acked-by' lines to, say, the last 10 commits (none
+of which is a merge), use this command:
+
+--------------------------------------------------------
+git filter-branch --msg-filter '
+	cat &&
+	echo "Acked-by: Bugs Bunny <bunny@bugzilla.org>"
+' HEAD~10..HEAD
+--------------------------------------------------------
+
 *NOTE* the changes introduced by the commits, and which are not reverted
 by subsequent commits, will still be in the rewritten branch. If you want
 to throw out _changes_ together with the commits, you should use the
-interactive mode of 'git-rebase'.
+interactive mode of 'git rebase'.
 
 
 Consider this history:
@@ -336,10 +359,10 @@
 
 ---------------------------------------------------------------
 git filter-branch --index-filter \
-	'git ls-files -s | sed "s-\t-&newsubdir/-" |
+	'git ls-files -s | sed "s-\t\"*-&newsubdir/-" |
 		GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
 			git update-index --index-info &&
-	 mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE' HEAD
+	 mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' HEAD
 ---------------------------------------------------------------
 
 
@@ -348,23 +371,23 @@
 ------------------------------------
 
 git-filter-branch is often used to get rid of a subset of files,
-usually with some combination of `\--index-filter` and
-`\--subdirectory-filter`.  People expect the resulting repository to
+usually with some combination of `--index-filter` and
+`--subdirectory-filter`.  People expect the resulting repository to
 be smaller than the original, but you need a few more steps to
 actually make it smaller, because git tries hard not to lose your
 objects until you tell it to.  First make sure that:
 
 * You really removed all variants of a filename, if a blob was moved
-  over its lifetime.  `git log \--name-only \--follow \--all \--
-  filename` can help you find renames.
+  over its lifetime.  `git log --name-only --follow --all -- filename`
+  can help you find renames.
 
-* You really filtered all refs: use `\--tag-name-filter cat \--
-  \--all` when calling git-filter-branch.
+* You really filtered all refs: use `--tag-name-filter cat -- --all`
+  when calling git-filter-branch.
 
 Then there are two ways to get a smaller repository.  A safer way is
 to clone, that keeps your original intact.
 
-* Clone it with `git clone +++file:///path/to/repo+++`.  The clone
+* Clone it with `git clone file:///path/to/repo`.  The clone
   will not have the removed objects.  See linkgit:git-clone[1].  (Note
   that cloning with a plain path just hardlinks everything!)
 
@@ -374,24 +397,14 @@
 warned.
 
 * Remove the original refs backed up by git-filter-branch: say `git
-  for-each-ref \--format="%(refname)" refs/original/ | xargs -n 1 git
+  for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git
   update-ref -d`.
 
-* Expire all reflogs with `git reflog expire \--expire=now \--all`.
+* Expire all reflogs with `git reflog expire --expire=now --all`.
 
-* Garbage collect all unreferenced objects with `git gc \--prune=now`
+* Garbage collect all unreferenced objects with `git gc --prune=now`
   (or if your git-gc is not new enough to support arguments to
-  `\--prune`, use `git repack -ad; git prune` instead).
-
-
-Author
-------
-Written by Petr "Pasky" Baudis <pasky@suse.cz>,
-and the git list <git@vger.kernel.org>
-
-Documentation
---------------
-Documentation by Petr Baudis and the git list.
+  `--prune`, use `git repack -ad; git prune` instead).
 
 GIT
 ---
diff --git a/Documentation/git-fmt-merge-msg.txt b/Documentation/git-fmt-merge-msg.txt
index 1c24796..3a0f55e 100644
--- a/Documentation/git-fmt-merge-msg.txt
+++ b/Documentation/git-fmt-merge-msg.txt
@@ -9,25 +9,27 @@
 SYNOPSIS
 --------
 [verse]
-'git fmt-merge-msg' [--log | --no-log] <$GIT_DIR/FETCH_HEAD
-'git fmt-merge-msg' [--log | --no-log] -F <file>
+'git fmt-merge-msg' [-m <message>] [--log[=<n>] | --no-log] <$GIT_DIR/FETCH_HEAD
+'git fmt-merge-msg' [-m <message>] [--log[=<n>] | --no-log] -F <file>
 
 DESCRIPTION
 -----------
 Takes the list of merged objects on stdin and produces a suitable
 commit message to be used for the merge commit, usually to be
-passed as the '<merge-message>' argument of 'git-merge'.
+passed as the '<merge-message>' argument of 'git merge'.
 
-This script is intended mostly for internal use by scripts
-automatically invoking 'git-merge'.
+This command is intended mostly for internal use by scripts
+automatically invoking 'git merge'.
 
 OPTIONS
 -------
 
---log::
+--log[=<n>]::
 	In addition to branch names, populate the log message with
 	one-line descriptions from the actual commits that are being
-	merged.
+	merged.  At most <n> commits from each merge parent will be
+	used (20 if <n> is omitted).  This overrides the `merge.log`
+	configuration variable.
 
 --no-log::
 	Do not list one-line descriptions from the actual commits being
@@ -38,6 +40,11 @@
 	Synonyms to --log and --no-log; these are deprecated and will be
 	removed in the future.
 
+-m <message>::
+--message <message>::
+	Use <message> instead of the branch names for the first line
+	of the log message.  For use with `--log`.
+
 -F <file>::
 --file <file>::
 	Take the list of merged objects from <file> instead of
@@ -46,9 +53,16 @@
 CONFIGURATION
 -------------
 
+merge.branchdesc::
+	In addition to branch names, populate the log message with
+	the branch description text associated with them.  Defaults
+	to false.
+
 merge.log::
-	Whether to include summaries of merged commits in newly
-	merge commit messages. False by default.
+	In addition to branch names, populate the log message with at
+	most the specified number of one-line descriptions from the
+	actual commits that are being merged.  Defaults to false, and
+	true is a synonym for 20.
 
 merge.summary::
 	Synonym to `merge.log`; this is deprecated and will be removed in
@@ -58,15 +72,6 @@
 --------
 linkgit:git-merge[1]
 
-
-Author
-------
-Written by Junio C Hamano <gitster@pobox.com>
-
-Documentation
---------------
-Documentation by Petr Baudis, Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
index 8dc873f..c872b88 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -9,7 +9,7 @@
 --------
 [verse]
 'git for-each-ref' [--count=<count>] [--shell|--perl|--python|--tcl]
-		   [--sort=<key>]\* [--format=<format>] [<pattern>...]
+		   [(--sort=<key>)...] [--format=<format>] [<pattern>...]
 
 DESCRIPTION
 -----------
@@ -82,10 +82,11 @@
 	The type of the object (`blob`, `tree`, `commit`, `tag`).
 
 objectsize::
-	The size of the object (the same as 'git-cat-file -s' reports).
+	The size of the object (the same as 'git cat-file -s' reports).
 
 objectname::
 	The object name (aka SHA-1).
+	For a non-ambiguous abbreviation of the object name append `:short`.
 
 upstream::
 	The name of a local ref which can be considered ``upstream''
@@ -100,9 +101,10 @@
 `committer`, and `tagger`) can be suffixed with `name`, `email`,
 and `date` to extract the named component.
 
-The first line of the message in a commit and tag object is
-`subject`, the remaining lines are `body`.  The whole message
-is `contents`.
+The complete message in a commit and tag object is `contents`.
+Its first line is `contents:subject`, the remaining lines
+are `contents:body` and the optional GPG signature
+is `contents:signature`.
 
 For sorting purposes, fields with numeric values sort in numeric
 order (`objectsize`, `authordate`, `committerdate`, `taggerdate`).
@@ -122,7 +124,7 @@
 --------
 
 An example directly producing formatted text.  Show the most recent
-3 tagged commits::
+3 tagged commits:
 
 ------------
 #!/bin/sh
@@ -139,7 +141,7 @@
 
 
 A simple example showing the use of shell eval on the output,
-demonstrating the use of --shell.  List the prefixes of all heads::
+demonstrating the use of --shell.  List the prefixes of all heads:
 ------------
 #!/bin/sh
 
@@ -153,7 +155,7 @@
 
 
 A bit more elaborate report on tags, demonstrating that the format
-may be an entire script::
+may be an entire script:
 ------------
 #!/bin/sh
 
@@ -203,3 +205,15 @@
 	refs/tags`
 eval "$eval"
 ------------
+
+Author
+------
+Written by Junio C Hamano <gitster@pobox.com>.
+
+Documentation
+-------------
+Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 687e667..04c7346 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -13,13 +13,14 @@
 		   [--no-thread | --thread[=<style>]]
 		   [(--attach|--inline)[=<boundary>] | --no-attach]
 		   [-s | --signoff]
+		   [--signature=<signature> | --no-signature]
 		   [-n | --numbered | -N | --no-numbered]
 		   [--start-number <n>] [--numbered-files]
 		   [--in-reply-to=Message-Id] [--suffix=.<sfx>]
 		   [--ignore-if-in-upstream]
 		   [--subject-prefix=Subject-Prefix]
-		   [--cc=<email>]
-		   [--cover-letter]
+		   [--to=<email>] [--cc=<email>]
+		   [--cover-letter] [--quiet]
 		   [<common diff options>]
 		   [ <since> | <revision range> ]
 
@@ -29,7 +30,7 @@
 Prepare each commit with its patch in
 one file per commit, formatted to resemble UNIX mailbox format.
 The output of this command is convenient for e-mail submission or
-for use with 'git-am'.
+for use with 'git am'.
 
 There are two ways to specify which commits to operate on.
 
@@ -38,33 +39,33 @@
    that leads to the <since> to be output.
 
 2. Generic <revision range> expression (see "SPECIFYING
-   REVISIONS" section in linkgit:git-rev-parse[1]) means the
+   REVISIONS" section in linkgit:gitrevisions[7]) means the
    commits in the specified range.
 
 The first rule takes precedence in the case of a single <commit>.  To
 apply the second rule, i.e., format everything since the beginning of
-history up until <commit>, use the '\--root' option: "git format-patch
-\--root <commit>".  If you want to format only <commit> itself, you
-can do this with "git format-patch -1 <commit>".
+history up until <commit>, use the '\--root' option: `git format-patch
+--root <commit>`.  If you want to format only <commit> itself, you
+can do this with `git format-patch -1 <commit>`.
 
 By default, each output file is numbered sequentially from 1, and uses the
 first line of the commit message (massaged for pathname safety) as
-the filename. With the --numbered-files option, the output file names
+the filename. With the `--numbered-files` option, the output file names
 will only be numbers, without the first line of the commit appended.
 The names of the output files are printed to standard
-output, unless the --stdout option is specified.
+output, unless the `--stdout` option is specified.
 
-If -o is specified, output files are created in <dir>.  Otherwise
+If `-o` is specified, output files are created in <dir>.  Otherwise
 they are created in the current working directory.
 
 By default, the subject of a single patch is "[PATCH] First Line" and
 the subject when multiple patches are output is "[PATCH n/m] First
-Line". To force 1/1 to be added for a single patch, use -n.  To omit
-patch numbers from the subject, use -N
+Line". To force 1/1 to be added for a single patch, use `-n`.  To omit
+patch numbers from the subject, use `-N`.
 
-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
+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
@@ -73,7 +74,7 @@
 include::diff-options.txt[]
 
 -<n>::
-	Limits the number of patches to prepare.
+	Prepare patches from the topmost <n> commits.
 
 -o <dir>::
 --output-directory <dir>::
@@ -112,7 +113,7 @@
 --attach[=<boundary>]::
 	Create multipart/mixed attachment, the first part of
 	which is the commit message and the patch itself in the
-	second part, with "Content-Disposition: attachment".
+	second part, with `Content-Disposition: attachment`.
 
 --no-attach::
 	Disable the creation of an attachment, overriding the
@@ -121,31 +122,31 @@
 --inline[=<boundary>]::
 	Create multipart/mixed attachment, the first part of
 	which is the commit message and the patch itself in the
-	second part, with "Content-Disposition: inline".
+	second part, with `Content-Disposition: inline`.
 
 --thread[=<style>]::
 --no-thread::
-	Controls addition of In-Reply-To and References headers to
+	Controls addition of `In-Reply-To` and `References` headers to
 	make the second and subsequent mails appear as replies to the
-	first.  Also controls generation of the Message-Id header to
+	first.  Also controls generation of the `Message-Id` header to
 	reference.
 +
 The optional <style> argument can be either `shallow` or `deep`.
 'shallow' threading makes every mail a reply to the head of the
 series, where the head is chosen from the cover letter, the
-`\--in-reply-to`, and the first patch mail, in this order.  'deep'
+`--in-reply-to`, and the first patch mail, in this order.  'deep'
 threading makes every mail a reply to the previous one.
 +
-The default is --no-thread, unless the 'format.thread' configuration
-is set.  If --thread is specified without a style, it defaults to the
+The default is `--no-thread`, unless the 'format.thread' configuration
+is set.  If `--thread` is specified without a style, it defaults to the
 style specified by 'format.thread' if any, or else `shallow`.
 +
 Beware that the default for 'git send-email' is to thread emails
-itself.  If you want 'git format-patch' to take care of hreading, you
-will want to ensure that threading is disabled for 'git send-email'.
+itself.  If you want `git format-patch` to take care of threading, you
+will want to ensure that threading is disabled for `git send-email`.
 
 --in-reply-to=Message-Id::
-	Make the first mail (or all the mails with --no-thread) appear as a
+	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.
 
@@ -160,22 +161,39 @@
 	Instead of the standard '[PATCH]' prefix in the subject
 	line, instead use '[<Subject-Prefix>]'. This
 	allows for useful naming of a patch series, and can be
-	combined with the --numbered option.
+	combined with the `--numbered` option.
+
+--to=<email>::
+	Add a `To:` header to the email headers. This is in addition
+	to any configured headers, and may be used multiple times.
+	The negated form `--no-to` discards all `To:` headers added so
+	far (from config or command line).
 
 --cc=<email>::
-	Add a "Cc:" header to the email headers. This is in addition
+	Add a `Cc:` header to the email headers. This is in addition
 	to any configured headers, and may be used multiple times.
+	The negated form `--no-cc` discards all `Cc:` headers added so
+	far (from config or command line).
 
 --add-header=<header>::
 	Add an arbitrary header to the email headers.  This is in addition
 	to any configured headers, and may be used multiple times.
-	For example, --add-header="Organization: git-foo"
+	For example, `--add-header="Organization: git-foo"`.
+	The negated form `--no-add-header` discards *all* (`To:`,
+	`Cc:`, and custom) headers added so far from config or command
+	line.
 
 --cover-letter::
 	In addition to the patches, generate a cover letter file
 	containing the shortlog and the overall diffstat.  You can
 	fill in a description in the file before sending it out.
 
+--[no]-signature=<signature>::
+	Add a signature to each message produced. Per RFC 3676 the signature
+	is separated from the body by a line with '-- ' on it. If the
+	signature option is omitted the signature defaults to the git version
+	number.
+
 --suffix=.<sfx>::
 	Instead of using `.patch` as the suffix for generated
 	filenames, use specified suffix.  A common alternative is
@@ -185,6 +203,9 @@
 Note that the leading character does not have to be a dot; for example,
 you can use `--suffix=-patch` to get `0001-description-of-my-change-patch`.
 
+--quiet::
+	Do not print the names of the generated files to standard output.
+
 --no-binary::
 	Do not output contents of changes in binary files, instead
 	display a notice that those files changed.  Patches generated
@@ -202,8 +223,8 @@
 -------------
 You can specify extra mail header lines to be added to each message,
 defaults for the subject prefix and file suffix, number patches when
-outputting more than one patch, add "Cc:" headers, configure attachments,
-and sign off patches with configuration variables.
+outputting more than one patch, add "To" or "Cc:" headers, configure
+attachments, and sign off patches with configuration variables.
 
 ------------
 [format]
@@ -211,17 +232,245 @@
 	subjectprefix = CHANGE
 	suffix = .txt
 	numbered = auto
+	to = <email>
 	cc = <email>
 	attach [ = mime-boundary-string ]
 	signoff = true
 ------------
 
 
+DISCUSSION
+----------
+
+The patch produced by 'git format-patch' is in UNIX mailbox format,
+with a fixed "magic" time stamp to indicate that the file is output
+from format-patch rather than a real mailbox, like so:
+
+------------
+From 8f72bad1baf19a53459661343e21d6491c3908d3 Mon Sep 17 00:00:00 2001
+From: Tony Luck <tony.luck@intel.com>
+Date: Tue, 13 Jul 2010 11:42:54 -0700
+Subject: [PATCH] =?UTF-8?q?[IA64]=20Put=20ia64=20config=20files=20on=20the=20?=
+ =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20diet?=
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+arch/arm config files were slimmed down using a python script
+(See commit c2330e286f68f1c408b4aa6515ba49d57f05beae comment)
+
+Do the same for ia64 so we can have sleek & trim looking
+...
+------------
+
+Typically it will be placed in a MUA's drafts folder, edited to add
+timely commentary that should not go in the changelog after the three
+dashes, and then sent as a message whose body, in our example, starts
+with "arch/arm config files were...".  On the receiving end, readers
+can save interesting patches in a UNIX mailbox and apply them with
+linkgit:git-am[1].
+
+When a patch is part of an ongoing discussion, the patch generated by
+'git format-patch' can be tweaked to take advantage of the 'git am
+--scissors' feature.  After your response to the discussion comes a
+line that consists solely of "`-- >8 --`" (scissors and perforation),
+followed by the patch with unnecessary header fields removed:
+
+------------
+...
+> So we should do such-and-such.
+
+Makes sense to me.  How about this patch?
+
+-- >8 --
+Subject: [IA64] Put ia64 config files on the Uwe Kleine-König diet
+
+arch/arm config files were slimmed down using a python script
+...
+------------
+
+When sending a patch this way, most often you are sending your own
+patch, so in addition to the "`From $SHA1 $magic_timestamp`" marker you
+should omit `From:` and `Date:` lines from the patch file.  The patch
+title is likely to be different from the subject of the discussion the
+patch is in response to, so it is likely that you would want to keep
+the Subject: line, like the example above.
+
+Checking for patch corruption
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Many mailers if not set up properly will corrupt whitespace.  Here are
+two common types of corruption:
+
+* Empty context lines that do not have _any_ whitespace.
+
+* Non-empty context lines that have one extra whitespace at the
+  beginning.
+
+One way to test if your MUA is set up correctly is:
+
+* Send the patch to yourself, exactly the way you would, except
+  with To: and Cc: lines that do not contain the list and
+  maintainer address.
+
+* Save that patch to a file in UNIX mailbox format.  Call it a.patch,
+  say.
+
+* Apply it:
+
+    $ git fetch <project> master:test-apply
+    $ git checkout test-apply
+    $ git reset --hard
+    $ git am a.patch
+
+If it does not apply correctly, there can be various reasons.
+
+* The patch itself does not apply cleanly.  That is _bad_ but
+  does not have much to do with your MUA.  You might want to rebase
+  the patch with linkgit:git-rebase[1] before regenerating it in
+  this case.
+
+* The MUA corrupted your patch; "am" would complain that
+  the patch does not apply.  Look in the .git/rebase-apply/ subdirectory and
+  see what 'patch' file contains and check for the common
+  corruption patterns mentioned above.
+
+* While at it, check the 'info' and 'final-commit' files as well.
+  If what is in 'final-commit' is not exactly what you would want to
+  see in the commit log message, it is very likely that the
+  receiver would end up hand editing the log message when applying
+  your patch.  Things like "Hi, this is my first patch.\n" in the
+  patch e-mail should come after the three-dash line that signals
+  the end of the commit message.
+
+MUA-SPECIFIC HINTS
+------------------
+Here are some hints on how to successfully submit patches inline using
+various mailers.
+
+GMail
+~~~~~
+GMail does not have any way to turn off line wrapping in the web
+interface, so it will mangle any emails that you send.  You can however
+use "git send-email" and send your patches through the GMail SMTP server, or
+use any IMAP email client to connect to the google IMAP server and forward
+the emails through that.
+
+For hints on using 'git send-email' to send your patches through the
+GMail SMTP server, see the EXAMPLE section of linkgit:git-send-email[1].
+
+For hints on submission using the IMAP interface, see the EXAMPLE
+section of linkgit:git-imap-send[1].
+
+Thunderbird
+~~~~~~~~~~~
+By default, Thunderbird will both wrap emails as well as flag
+them as being 'format=flowed', both of which will make the
+resulting email unusable by git.
+
+There are three different approaches: use an add-on to turn off line wraps,
+configure Thunderbird to not mangle patches, or use
+an external editor to keep Thunderbird from mangling the patches.
+
+Approach #1 (add-on)
+^^^^^^^^^^^^^^^^^^^^
+
+Install the Toggle Word Wrap add-on that is available from
+https://addons.mozilla.org/thunderbird/addon/toggle-word-wrap/
+It adds a menu entry "Enable Word Wrap" in the composer's "Options" menu
+that you can tick off. Now you can compose the message as you otherwise do
+(cut + paste, 'git format-patch' | 'git imap-send', etc), but you have to
+insert line breaks manually in any text that you type.
+
+Approach #2 (configuration)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Three steps:
+
+1. Configure your mail server composition as plain text:
+   Edit...Account Settings...Composition & Addressing,
+   uncheck "Compose Messages in HTML".
+
+2. Configure your general composition window to not wrap.
++
+In Thunderbird 2:
+Edit..Preferences..Composition, wrap plain text messages at 0
++
+In Thunderbird 3:
+Edit..Preferences..Advanced..Config Editor.  Search for
+"mail.wrap_long_lines".
+Toggle it to make sure it is set to `false`.
+
+3. Disable the use of format=flowed:
+Edit..Preferences..Advanced..Config Editor.  Search for
+"mailnews.send_plaintext_flowed".
+Toggle it to make sure it is set to `false`.
+
+After that is done, you should be able to compose email as you
+otherwise would (cut + paste, 'git format-patch' | 'git imap-send', etc),
+and the patches will not be mangled.
+
+Approach #3 (external editor)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The following Thunderbird extensions are needed:
+AboutConfig from http://aboutconfig.mozdev.org/ and
+External Editor from http://globs.org/articles.php?lng=en&pg=8
+
+1. Prepare the patch as a text file using your method of choice.
+
+2. Before opening a compose window, use Edit->Account Settings to
+   uncheck the "Compose messages in HTML format" setting in the
+   "Composition & Addressing" panel of the account to be used to
+   send the patch.
+
+3. In the main Thunderbird window, 'before' you open the compose
+   window for the patch, use Tools->about:config to set the
+   following to the indicated values:
++
+----------
+	mailnews.send_plaintext_flowed  => false
+	mailnews.wraplength             => 0
+----------
+
+4. Open a compose window and click the external editor icon.
+
+5. In the external editor window, read in the patch file and exit
+   the editor normally.
+
+Side note: it may be possible to do step 2 with
+about:config and the following settings but no one's tried yet.
+
+----------
+	mail.html_compose                       => false
+	mail.identity.default.compose_html      => false
+	mail.identity.id?.compose_html          => false
+----------
+
+There is a script in contrib/thunderbird-patch-inline which can help
+you include patches with Thunderbird in an easy way. To use it, do the
+steps above and then use the script as the external editor.
+
+KMail
+~~~~~
+This should help you to submit patches inline using KMail.
+
+1. Prepare the patch as a text file.
+
+2. Click on New Mail.
+
+3. Go under "Options" in the Composer window and be sure that
+   "Word wrap" is not set.
+
+4. Use Message -> Insert file... and insert the patch.
+
+5. Back in the compose window: add whatever other text you wish to the
+   message, complete the addressing and subject fields, and press send.
+
+
 EXAMPLES
 --------
 
 * Extract commits between revisions R1 and R2, and apply them on top of
-the current branch using 'git-am' to cherry-pick them:
+the current branch using 'git am' to cherry-pick them:
 +
 ------------
 $ git format-patch -k --stdout R1..R2 | git am -3 -k
@@ -266,15 +515,6 @@
 --------
 linkgit:git-am[1], linkgit:git-send-email[1]
 
-
-Author
-------
-Written by Junio C Hamano <gitster@pobox.com>
-
-Documentation
---------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-fsck-objects.txt b/Documentation/git-fsck-objects.txt
index 965a827..eec4bdb 100644
--- a/Documentation/git-fsck-objects.txt
+++ b/Documentation/git-fsck-objects.txt
@@ -8,6 +8,7 @@
 
 SYNOPSIS
 --------
+[verse]
 'git fsck-objects' ...
 
 DESCRIPTION
@@ -15,3 +16,7 @@
 
 This is a synonym for linkgit:git-fsck[1].  Please refer to the
 documentation of that command.
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-fsck.txt b/Documentation/git-fsck.txt
index 287c4fc..bbb25da 100644
--- a/Documentation/git-fsck.txt
+++ b/Documentation/git-fsck.txt
@@ -10,7 +10,8 @@
 --------
 [verse]
 'git fsck' [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]
-	 [--full] [--strict] [--verbose] [--lost-found] [<object>*]
+	 [--[no-]full] [--strict] [--verbose] [--lost-found]
+	 [--[no-]dangling] [--[no-]progress] [<object>*]
 
 DESCRIPTION
 -----------
@@ -21,14 +22,19 @@
 <object>::
 	An object to treat as the head of an unreachability trace.
 +
-If no objects are given, 'git-fsck' defaults to using the
+If no objects are given, 'git fsck' defaults to using the
 index file, all SHA1 references in .git/refs/*, and all reflogs (unless
 --no-reflogs is given) as heads.
 
 --unreachable::
-	Print out objects that exist but that aren't readable from any
+	Print out objects that exist but that aren't reachable from any
 	of the reference nodes.
 
+--dangling::
+--no-dangling::
+	Print objects that exist but that are never 'directly' used (default).
+	`--no-dangling` can be used to omit this information from the output.
+
 --root::
 	Report root nodes.
 
@@ -52,7 +58,8 @@
 	or $GIT_DIR/objects/info/alternates,
 	and in packed git archives found in $GIT_DIR/objects/pack
 	and corresponding pack subdirectories in alternate
-	object pools.
+	object pools.  This is now default; you can turn it off
+	with --no-full.
 
 --strict::
 	Enable more strict checking, namely to catch a file mode
@@ -71,30 +78,28 @@
 	a blob, the contents are written into the file, rather than
 	its object name.
 
-It tests SHA1 and general object sanity, and it does full tracking of
-the resulting reachability and everything else. It prints out any
+--progress::
+--no-progress::
+	Progress status is reported on the standard error stream by
+	default when it is attached to a terminal, unless
+	--no-progress or --verbose is specified. --progress forces
+	progress status even if the standard error stream is not
+	directed to a terminal.
+
+DISCUSSION
+----------
+
+git-fsck tests SHA1 and general object sanity, and it does full tracking
+of the resulting reachability and everything else. It prints out any
 corruption it finds (missing or bad objects), and if you use the
-'--unreachable' flag it will also print out objects that exist but
-that aren't readable from any of the specified head nodes.
-
-So for example
-
-	git fsck --unreachable HEAD \
-		$(git for-each-ref --format="%(objectname)" refs/heads)
-
-will do quite a _lot_ of verification on the tree. There are a few
-extra validity tests to be added (make sure that tree objects are
-sorted properly etc), but on the whole if 'git-fsck' is happy, you
-do have a valid tree.
+'--unreachable' flag it will also print out objects that exist but that
+aren't reachable from any of the specified head nodes (or the default
+set, as mentioned above).
 
 Any corrupt objects you will have to find in backups or other archives
 (i.e., you can just remove them and do an 'rsync' with some other site in
 the hopes that somebody else has the object you have corrupted).
 
-Of course, "valid tree" doesn't mean that it wasn't generated by some
-evil person, and the end result might be crap. git is a revision
-tracking system, not a quality assurance system ;)
-
 Extracted Diagnostics
 ---------------------
 
@@ -122,9 +127,6 @@
 	The <type> object <object>, is present in the database but never
 	'directly' used. A dangling commit could be a root node.
 
-warning: git-fsck: tree <tree> has full pathnames in it::
-	And it shouldn't...
-
 sha1 mismatch <object>::
 	The database has an object who's sha1 doesn't match the
 	database value.
@@ -142,14 +144,6 @@
 GIT_ALTERNATE_OBJECT_DIRECTORIES::
 	used to specify additional object database roots (usually unset)
 
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-gc.txt b/Documentation/git-gc.txt
index b292e98..b370b02 100644
--- a/Documentation/git-gc.txt
+++ b/Documentation/git-gc.txt
@@ -8,6 +8,7 @@
 
 SYNOPSIS
 --------
+[verse]
 'git gc' [--aggressive] [--auto] [--quiet] [--prune=<date> | --no-prune]
 
 DESCRIPTION
@@ -15,13 +16,13 @@
 Runs a number of housekeeping tasks within the current repository,
 such as compressing file revisions (to reduce disk space and increase
 performance) and removing unreachable objects which may have been
-created from prior invocations of 'git-add'.
+created from prior invocations of 'git add'.
 
 Users are encouraged to run this task on a regular basis within
 each repository to maintain good disk space utilization and good
 operating performance.
 
-Some git commands may automatically run 'git-gc'; see the `--auto` flag
+Some git commands may automatically run 'git gc'; see the `--auto` flag
 below for details. If you know what you're doing and all you want is to
 disable this behavior permanently without further considerations, just do:
 
@@ -33,15 +34,15 @@
 -------
 
 --aggressive::
-	Usually 'git-gc' runs very quickly while providing good disk
+	Usually 'git gc' runs very quickly while providing good disk
 	space utilization and performance.  This option will cause
-	'git-gc' to more aggressively optimize the repository at the expense
+	'git gc' to more aggressively optimize the repository at the expense
 	of taking much more time.  The effects of this optimization are
 	persistent, so this option only needs to be used occasionally; every
 	few hundred changesets or so.
 
 --auto::
-	With this option, 'git-gc' checks whether any housekeeping is
+	With this option, 'git gc' checks whether any housekeeping is
 	required; if not, it exits without performing any work.
 	Some git commands run `git gc --auto` after performing
 	operations that could create many loose objects.
@@ -50,18 +51,18 @@
 too many packs in the repository. If the number of loose objects
 exceeds the value of the `gc.auto` configuration variable, then
 all loose objects are combined into a single pack using
-'git-repack -d -l'.  Setting the value of `gc.auto` to 0
+`git repack -d -l`.  Setting the value of `gc.auto` to 0
 disables automatic packing of loose objects.
 +
 If the number of packs exceeds the value of `gc.autopacklimit`,
 then existing packs (except those marked with a `.keep` file)
 are consolidated into a single pack by using the `-A` option of
-'git-repack'. Setting `gc.autopacklimit` to 0 disables
+'git repack'. Setting `gc.autopacklimit` to 0 disables
 automatic consolidation of packs.
 
 --prune=<date>::
 	Prune loose objects older than date (default is 2 weeks ago,
-	overrideable by the config variable `gc.pruneExpire`).  This
+	overridable by the config variable `gc.pruneExpire`).  This
 	option is on by default.
 
 --no-prune::
@@ -83,11 +84,21 @@
 can be set to indicate how long historical reflog entries which
 are not part of the current branch should remain available in
 this repository.  These types of entries are generally created as
-a result of using `git commit \--amend` or `git rebase` and are the
+a result of using `git commit --amend` or `git rebase` and are the
 commits prior to the amend or rebase occurring.  Since these changes
 are not part of the current project most users will want to expire
 them sooner.  This option defaults to '30 days'.
 
+The above two configuration variables can be given to a pattern.  For
+example, this sets non-default expiry values only to remote-tracking
+branches:
+
+------------
+[gc "refs/remotes/*"]
+	reflogExpire = never
+	reflogexpireUnreachable = 3 days
+------------
+
 The optional configuration variable 'gc.rerereresolved' indicates
 how long records of conflicted merge you resolved earlier are
 kept.  This defaults to 60 days.
@@ -97,7 +108,7 @@
 kept.  This defaults to 15 days.
 
 The optional configuration variable 'gc.packrefs' determines if
-'git-gc' runs 'git-pack-refs'. This can be set to "nobare" to enable
+'git gc' runs 'git pack-refs'. This can be set to "notbare" to enable
 it within all non-bare repos or it can be set to a boolean value.
 This defaults to true.
 
@@ -106,7 +117,7 @@
 the repository when the --aggressive option is specified.  The larger
 the value, the more time is spent optimizing the delta compression.  See
 the documentation for the --window' option in linkgit:git-repack[1] for
-more details.  This defaults to 10.
+more details.  This defaults to 250.
 
 The optional configuration variable 'gc.pruneExpire' controls how old
 the unreferenced loose objects have to be before they are pruned.  The
@@ -116,17 +127,24 @@
 Notes
 -----
 
-'git-gc' tries very hard to be safe about the garbage it collects. In
+'git gc' tries very hard to be safe about the garbage it collects. In
 particular, it will keep not only objects referenced by your current set
-of branches and tags, but also objects referenced by the index, remote
-tracking branches, refs saved by 'git-filter-branch' in
-refs/original/, or reflogs (which may references commits in branches
+of branches and tags, but also objects referenced by the index,
+remote-tracking branches, refs saved by 'git filter-branch' in
+refs/original/, or reflogs (which may reference commits in branches
 that were later amended or rewound).
 
 If you are expecting some objects to be collected and they aren't, check
 all of those locations and decide whether it makes sense in your case to
 remove those references.
 
+HOOKS
+-----
+
+The 'git gc --auto' command will run the 'pre-auto-gc' hook.  See
+linkgit:githooks[5] for more information.
+
+
 SEE ALSO
 --------
 linkgit:git-prune[1]
@@ -134,10 +152,6 @@
 linkgit:git-repack[1]
 linkgit:git-rerere[1]
 
-Author
-------
-Written by Shawn O. Pearce <spearce@spearce.org>
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-get-tar-commit-id.txt b/Documentation/git-get-tar-commit-id.txt
index 84f23ee..1e2a20d 100644
--- a/Documentation/git-get-tar-commit-id.txt
+++ b/Documentation/git-get-tar-commit-id.txt
@@ -8,29 +8,21 @@
 
 SYNOPSIS
 --------
+[verse]
 'git get-tar-commit-id' < <tarfile>
 
 
 DESCRIPTION
 -----------
 Acts as a filter, extracting the commit ID stored in archives created by
-'git-archive'.  It reads only the first 1024 bytes of input, thus its
+'git archive'.  It reads only the first 1024 bytes of input, thus its
 runtime is not influenced by the size of <tarfile> very much.
 
-If no commit ID is found, 'git-get-tar-commit-id' quietly exists with a
+If no commit ID is found, 'git get-tar-commit-id' quietly exists with a
 return code of 1.  This can happen if <tarfile> had not been created
-using 'git-archive' or if the first parameter of 'git-archive' had been
+using 'git archive' or if the first parameter of 'git archive' had been
 a tree ID instead of a commit ID or tag.
 
-
-Author
-------
-Written by Rene Scharfe <rene.scharfe@lsrfire.ath.cx>
-
-Documentation
---------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt
index b753c9d..3bec036 100644
--- a/Documentation/git-grep.txt
+++ b/Documentation/git-grep.txt
@@ -9,31 +9,64 @@
 SYNOPSIS
 --------
 [verse]
-'git grep' [--cached]
-	   [-a | --text] [-I] [-i | --ignore-case] [-w | --word-regexp]
+'git grep' [-a | --text] [-I] [-i | --ignore-case] [-w | --word-regexp]
 	   [-v | --invert-match] [-h|-H] [--full-name]
 	   [-E | --extended-regexp] [-G | --basic-regexp]
-	   [-F | --fixed-strings] [-n]
+	   [-P | --perl-regexp]
+	   [-F | --fixed-strings] [-n | --line-number]
 	   [-l | --files-with-matches] [-L | --files-without-match]
+	   [(-O | --open-files-in-pager) [<pager>]]
 	   [-z | --null]
-	   [-c | --count] [--all-match]
-	   [--color | --no-color]
+	   [-c | --count] [--all-match] [-q | --quiet]
+	   [--max-depth <depth>]
+	   [--color[=<when>] | --no-color]
+	   [--break] [--heading] [-p | --show-function]
 	   [-A <post-context>] [-B <pre-context>] [-C <context>]
+	   [-W | --function-context]
 	   [-f <file>] [-e] <pattern>
-	   [--and|--or|--not|(|)|-e <pattern>...] [<tree>...]
-	   [--] [<path>...]
+	   [--and|--or|--not|(|)|-e <pattern>...]
+	   [ [--exclude-standard] [--cached | --no-index | --untracked] | <tree>...]
+	   [--] [<pathspec>...]
 
 DESCRIPTION
 -----------
-Look for specified patterns in the working tree files, blobs
-registered in the index file, or given tree objects.
+Look for specified patterns in the tracked files in the work tree, blobs
+registered in the index file, or blobs in given tree objects.  Patterns
+are lists of one or more search expressions separated by newline
+characters.  An empty string as search expression matches all lines.
+
+
+CONFIGURATION
+-------------
+
+grep.lineNumber::
+	If set to true, enable '-n' option by default.
+
+grep.extendedRegexp::
+	If set to true, enable '--extended-regexp' option by default.
 
 
 OPTIONS
 -------
 --cached::
-	Instead of searching in the working tree files, check
-	the blobs registered in the index file.
+	Instead of searching tracked files in the working tree, search
+	blobs registered in the index file.
+
+--no-index::
+	Search files in the current directory that is not managed by git.
+
+--untracked::
+	In addition to searching in the tracked files in the working
+	tree, search also in untracked files.
+
+--no-exclude-standard::
+	Also search in ignored files by not honoring the `.gitignore`
+	mechanism. Only useful with `--untracked`.
+
+--exclude-standard::
+	Do not pay attention to ignored files specified via the	`.gitignore`
+	mechanism.  Only useful when searching files in the current directory
+	with `--no-index`.
 
 -a::
 --text::
@@ -47,6 +80,13 @@
 -I::
 	Don't match the pattern in binary files.
 
+--max-depth <depth>::
+	For each <pathspec> given on command line, descend at most <depth>
+	levels of directories. A negative value means no limit.
+	This option is ignored if <pathspec> contains active wildcards.
+	In other words if "a*" matches a directory named "a*",
+	"*" is matched literally so --max-depth is still effective.
+
 -w::
 --word-regexp::
 	Match the pattern only at word boundary (either begin at the
@@ -78,12 +118,18 @@
 	Use POSIX extended/basic regexp for patterns.  Default
 	is to use basic regexp.
 
+-P::
+--perl-regexp::
+	Use Perl-compatible regexp for patterns. Requires libpcre to be
+	compiled in.
+
 -F::
 --fixed-strings::
 	Use fixed strings for patterns (don't interpret pattern
 	as a regex).
 
 -n::
+--line-number::
 	Prefix the line number to matching lines.
 
 -l::
@@ -93,8 +139,15 @@
 --files-without-match::
 	Instead of showing every matched line, show only the
 	names of files that contain (or do not contain) matches.
-	For better compatibility with 'git-diff', --name-only is a
-	synonym for --files-with-matches.
+	For better compatibility with 'git diff', `--name-only` is a
+	synonym for `--files-with-matches`.
+
+-O [<pager>]::
+--open-files-in-pager [<pager>]::
+	Open the matching files in the pager (not the output of 'grep').
+	If the pager happens to be "less" or "vi", and the user
+	specified only one pattern, the first file is positioned at
+	the first match automatically.
 
 -z::
 --null::
@@ -106,21 +159,21 @@
 	Instead of showing every matched line, show the number of
 	lines that match.
 
---color::
+--color[=<when>]::
 	Show colored matches.
+	The value must be always (the default), never, or auto.
 
 --no-color::
 	Turn off match highlighting, even when the configuration file
 	gives the default to color output.
+	Same as `--color=never`.
 
--[ABC] <context>::
-	Show `context` trailing (`A` -- after), or leading (`B`
-	-- before), or both (`C` -- context) lines, and place a
-	line containing `--` between contiguous groups of
-	matches.
+--break::
+	Print an empty line between matches from different files.
 
--<num>::
-	A shortcut for specifying -C<num>.
+--heading::
+	Show the filename above the matches in that file instead of
+	at the start of each shown line.
 
 -p::
 --show-function::
@@ -130,12 +183,35 @@
 	patch hunk headers (see 'Defining a custom hunk-header' in
 	linkgit:gitattributes[5]).
 
+-<num>::
+-C <num>::
+--context <num>::
+	Show <num> leading and trailing lines, and place a line
+	containing `--` between contiguous groups of matches.
+
+-A <num>::
+--after-context <num>::
+	Show <num> trailing lines, and place a line containing
+	`--` between contiguous groups of matches.
+
+-B <num>::
+--before-context <num>::
+	Show <num> leading lines, and place a line containing
+	`--` between contiguous groups of matches.
+
+-W::
+--function-context::
+	Show the surrounding text from the previous line containing a
+	function name up to the one before the next function name,
+	effectively showing the whole function in which the match was
+	found.
+
 -f <file>::
 	Read patterns from <file>, one per line.
 
 -e::
 	The next parameter is the pattern. This option has to be
-	used for patterns starting with - and should be used in
+	used for patterns starting with `-` and should be used in
 	scripts passing user input to grep.  Multiple patterns are
 	combined by 'or'.
 
@@ -153,35 +229,38 @@
 	this flag is specified to limit the match to files that
 	have lines to match all of them.
 
-`<tree>...`::
-	Search blobs in the trees for specified patterns.
+-q::
+--quiet::
+	Do not output matched lines; instead, exit with status 0 when
+	there is a match and with non-zero status when there isn't.
+
+<tree>...::
+	Instead of searching tracked files in the working tree, search
+	blobs in the given trees.
 
 \--::
 	Signals the end of options; the rest of the parameters
-	are <path> limiters.
+	are <pathspec> limiters.
 
+<pathspec>...::
+	If given, limit the search to paths matching at least one pattern.
+	Both leading paths match and glob(7) patterns are supported.
 
-Example
--------
+Examples
+--------
 
-git grep -e \'#define\' --and \( -e MAX_PATH -e PATH_MAX \)::
+`git grep 'time_t' -- '*.[ch]'`::
+	Looks for `time_t` in all tracked .c and .h files in the working
+	directory and its subdirectories.
+
+`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`.
 
-git grep --all-match -e NODE -e Unexpected::
+`git grep --all-match -e NODE -e Unexpected`::
 	Looks for a line that has `NODE` or `Unexpected` in
 	files that have lines that match both.
 
-Author
-------
-Originally written by Linus Torvalds <torvalds@osdl.org>, later
-revamped by Junio C Hamano.
-
-
-Documentation
---------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-gui.txt b/Documentation/git-gui.txt
index d0bc98b..0041994 100644
--- a/Documentation/git-gui.txt
+++ b/Documentation/git-gui.txt
@@ -7,23 +7,24 @@
 
 SYNOPSIS
 --------
+[verse]
 'git gui' [<command>] [arguments]
 
 DESCRIPTION
 -----------
-A Tcl/Tk based graphical user interface to Git.  'git-gui' focuses
+A Tcl/Tk based graphical user interface to Git.  'git gui' focuses
 on allowing users to make changes to their repository by making
 new commits, amending existing ones, creating branches, performing
 local merges, and fetching/pushing to remote repositories.
 
-Unlike 'gitk', 'git-gui' focuses on commit generation
+Unlike 'gitk', 'git gui' focuses on commit generation
 and single file annotation and does not show project history.
 It does however supply menu actions to start a 'gitk' session from
-within 'git-gui'.
+within 'git gui'.
 
-'git-gui' is known to work on all popular UNIX systems, Mac OS X,
+'git gui' is known to work on all popular UNIX systems, Mac OS X,
 and Windows (under both Cygwin and MSYS).  To the extent possible
-OS specific user interface guidelines are followed, making 'git-gui'
+OS specific user interface guidelines are followed, making 'git gui'
 a fairly native interface for users.
 
 COMMANDS
@@ -38,18 +39,18 @@
 	browser are opened in the blame viewer.
 
 citool::
-	Start 'git-gui' and arrange to make exactly one commit before
+	Start 'git gui' and arrange to make exactly one commit before
 	exiting and returning to the shell.  The interface is limited
 	to only commit actions, slightly reducing the application's
 	startup time and simplifying the menubar.
 
 version::
-	Display the currently running version of 'git-gui'.
+	Display the currently running version of 'git gui'.
 
 
 Examples
 --------
-git gui blame Makefile::
+`git gui blame Makefile`::
 
 	Show the contents of the file 'Makefile' in the current
 	working directory, and provide annotations for both the
@@ -58,41 +59,41 @@
 	uncommitted changes (if any) are explicitly attributed to
 	'Not Yet Committed'.
 
-git gui blame v0.99.8 Makefile::
+`git gui blame v0.99.8 Makefile`::
 
 	Show the contents of 'Makefile' in revision 'v0.99.8'
 	and provide annotations for each line.  Unlike the above
 	example the file is read from the object database and not
 	the working directory.
 
-git gui blame --line=100 Makefile::
+`git gui blame --line=100 Makefile`::
 
 	Loads annotations as described above and automatically
 	scrolls the view to center on line '100'.
 
-git gui citool::
+`git gui citool`::
 
 	Make one commit and return to the shell when it is complete.
 	This command returns a non-zero exit code if the window was
 	closed in any way other than by making a commit.
 
-git gui citool --amend::
+`git gui citool --amend`::
 
 	Automatically enter the 'Amend Last Commit' mode of
 	the interface.
 
-git gui citool --nocommit::
+`git gui citool --nocommit`::
 
 	Behave as normal citool, but instead of making a commit
 	simply terminate with a zero exit code. It still checks
 	that the index does not contain any unmerged entries, so
 	you can use it as a GUI version of linkgit:git-mergetool[1]
 
-git citool::
+`git citool`::
 
 	Same as `git gui citool` (above).
 
-git gui browser maint::
+`git gui browser maint`::
 
 	Show a browser for the tree of the 'maint' branch.  Files
 	selected in the browser can be viewed with the internal
@@ -103,15 +104,15 @@
 linkgit:gitk[1]::
 	The git repository browser.  Shows branches, commit history
 	and file differences.  gitk is the utility started by
-	'git-gui''s Repository Visualize actions.
+	'git gui''s Repository Visualize actions.
 
 Other
 -----
-'git-gui' is actually maintained as an independent project, but stable
+'git gui' is actually maintained as an independent project, but stable
 versions are distributed as part of the Git suite for the convenience
 of end users.
 
-A 'git-gui' development repository can be obtained from:
+A 'git gui' development repository can be obtained from:
 
   git clone git://repo.or.cz/git-gui.git
 
@@ -121,14 +122,6 @@
 
 or browsed online at http://repo.or.cz/w/git-gui.git/[].
 
-Author
-------
-Written by Shawn O. Pearce <spearce@spearce.org>.
-
-Documentation
---------------
-Documentation by Shawn O. Pearce <spearce@spearce.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-hash-object.txt b/Documentation/git-hash-object.txt
index 0af40cf..4b0a502 100644
--- a/Documentation/git-hash-object.txt
+++ b/Documentation/git-hash-object.txt
@@ -10,7 +10,7 @@
 --------
 [verse]
 'git hash-object' [-t <type>] [-w] [--path=<file>|--no-filters] [--stdin] [--] <file>...
-'git hash-object' [-t <type>] [-w] --stdin-paths < <list-of-paths>
+'git hash-object' [-t <type>] [-w] --stdin-paths [--no-filters] < <list-of-paths>
 
 DESCRIPTION
 -----------
@@ -18,7 +18,7 @@
 with the contents of the named file (which can be outside of the
 work tree), and optionally writes the resulting object into the
 object database.  Reports its object ID to its standard output.
-This is used by 'git-cvsimport' to update the index
+This is used by 'git cvsimport' to update the index
 without modifying files in the work tree.  When <type> is not
 specified, it defaults to "blob".
 
@@ -49,18 +49,10 @@
 
 --no-filters::
 	Hash the contents as is, ignoring any input filter that would
-	have been chosen by the attributes mechanism, including crlf
+	have been chosen by the attributes mechanism, including the end-of-line
 	conversion. If the file is read from standard input then this
 	is always implied, unless the --path option is given.
 
-Author
-------
-Written by Junio C Hamano <gitster@pobox.com>
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-help.txt b/Documentation/git-help.txt
index d9b9c34..9e0b3f6 100644
--- a/Documentation/git-help.txt
+++ b/Documentation/git-help.txt
@@ -7,6 +7,7 @@
 
 SYNOPSIS
 --------
+[verse]
 'git help' [-a|--all|-i|--info|-m|--man|-w|--web] [COMMAND]
 
 DESCRIPTION
@@ -55,9 +56,9 @@
 +
 The web browser can be specified using the configuration variable
 'help.browser', or 'web.browser' if the former is not set. If none of
-these config variables is set, the 'git-web--browse' helper script
-(called by 'git-help') will pick a suitable default. See
-linkgit:git-web--browse[1] for more information about this.
+these config variables is set, the 'git web{litdd}browse' helper script
+(called by 'git help') will pick a suitable default. See
+linkgit:git-web{litdd}browse[1] for more information about this.
 
 CONFIGURATION VARIABLES
 -----------------------
@@ -67,7 +68,7 @@
 
 If no command line option is passed, the 'help.format' configuration
 variable will be checked. The following values are supported for this
-variable; they make 'git-help' behave as their corresponding command
+variable; they make 'git help' behave as their corresponding command
 line option:
 
 * "man" corresponds to '-m|--man',
@@ -80,7 +81,7 @@
 The 'help.browser', 'web.browser' and 'browser.<tool>.path' will also
 be checked if the 'web' format is chosen (either by command line
 option or configuration variable). See '-w|--web' in the OPTIONS
-section above and linkgit:git-web--browse[1].
+section above and linkgit:git-web{litdd}browse[1].
 
 man.viewer
 ~~~~~~~~~~
@@ -122,7 +123,7 @@
 You can explicitly provide a full path to your preferred man viewer by
 setting the configuration variable 'man.<tool>.path'. For example, you
 can configure the absolute path to konqueror by setting
-'man.konqueror.path'. Otherwise, 'git-help' assumes the tool is
+'man.konqueror.path'. Otherwise, 'git help' assumes the tool is
 available in PATH.
 
 man.<tool>.cmd
@@ -171,17 +172,6 @@
 as they are probably more user specific than repository specific.
 See linkgit:git-config[1] for more information about this.
 
-Author
-------
-Written by Junio C Hamano <gitster@pobox.com> and the git-list
-<git@vger.kernel.org>.
-
-Documentation
--------------
-Initial documentation was part of the linkgit:git[1] man page.
-Christian Couder <chriscool@tuxfamily.org> extracted and rewrote it a
-little. Maintenance is done by the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-http-backend.txt b/Documentation/git-http-backend.txt
new file mode 100644
index 0000000..f4e0741
--- /dev/null
+++ b/Documentation/git-http-backend.txt
@@ -0,0 +1,196 @@
+git-http-backend(1)
+===================
+
+NAME
+----
+git-http-backend - Server side implementation of Git over HTTP
+
+SYNOPSIS
+--------
+[verse]
+'git http-backend'
+
+DESCRIPTION
+-----------
+A simple CGI program to serve the contents of a Git repository to Git
+clients accessing the repository over http:// and https:// protocols.
+The program supports clients fetching using both the smart HTTP protocol
+and the backwards-compatible dumb HTTP protocol, as well as clients
+pushing using the smart HTTP protocol.
+
+It verifies that the directory has the magic file
+"git-daemon-export-ok", and it will refuse to export any git directory
+that hasn't explicitly been marked for export this way (unless the
+GIT_HTTP_EXPORT_ALL environmental variable is set).
+
+By default, only the `upload-pack` service is enabled, which serves
+'git fetch-pack' and 'git ls-remote' clients, which are invoked from
+'git fetch', 'git pull', and 'git clone'.  If the client is authenticated,
+the `receive-pack` service is enabled, which serves 'git send-pack'
+clients, which is invoked from 'git push'.
+
+SERVICES
+--------
+These services can be enabled/disabled using the per-repository
+configuration file:
+
+http.getanyfile::
+	This serves Git clients older than version 1.6.6 that are unable to use the
+	upload pack service.  When enabled, clients are able to read
+	any file within the repository, including objects that are
+	no longer reachable from a branch but are still present.
+	It is enabled by default, but a repository can disable it
+	by setting this configuration item to `false`.
+
+http.uploadpack::
+	This serves 'git fetch-pack' and 'git ls-remote' clients.
+	It is enabled by default, but a repository can disable it
+	by setting this configuration item to `false`.
+
+http.receivepack::
+	This serves 'git send-pack' clients, allowing push.  It is
+	disabled by default for anonymous users, and enabled by
+	default for users authenticated by the web server.  It can be
+	disabled by setting this item to `false`, or enabled for all
+	users, including anonymous users, by setting it to `true`.
+
+URL TRANSLATION
+---------------
+To determine the location of the repository on disk, 'git http-backend'
+concatenates the environment variables PATH_INFO, which is set
+automatically by the web server, and GIT_PROJECT_ROOT, which must be set
+manually in the web server configuration.  If GIT_PROJECT_ROOT is not
+set, 'git http-backend' reads PATH_TRANSLATED, which is also set
+automatically by the web server.
+
+EXAMPLES
+--------
+All of the following examples map 'http://$hostname/git/foo/bar.git'
+to '/var/www/git/foo/bar.git'.
+
+Apache 2.x::
+	Ensure mod_cgi, mod_alias, and mod_env are enabled, set
+	GIT_PROJECT_ROOT (or DocumentRoot) appropriately, and
+	create a ScriptAlias to the CGI:
++
+----------------------------------------------------------------
+SetEnv GIT_PROJECT_ROOT /var/www/git
+SetEnv GIT_HTTP_EXPORT_ALL
+ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/
+----------------------------------------------------------------
++
+To enable anonymous read access but authenticated write access,
+require authorization with a LocationMatch directive:
++
+----------------------------------------------------------------
+<LocationMatch "^/git/.*/git-receive-pack$">
+	AuthType Basic
+	AuthName "Git Access"
+	Require group committers
+	...
+</LocationMatch>
+----------------------------------------------------------------
++
+To require authentication for both reads and writes, use a Location
+directive around the repository, or one of its parent directories:
++
+----------------------------------------------------------------
+<Location /git/private>
+	AuthType Basic
+	AuthName "Private Git Access"
+	Require group committers
+	...
+</Location>
+----------------------------------------------------------------
++
+To serve gitweb at the same url, use a ScriptAliasMatch to only
+those URLs that 'git http-backend' can handle, and forward the
+rest to gitweb:
++
+----------------------------------------------------------------
+ScriptAliasMatch \
+	"(?x)^/git/(.*/(HEAD | \
+			info/refs | \
+			objects/(info/[^/]+ | \
+				 [0-9a-f]{2}/[0-9a-f]{38} | \
+				 pack/pack-[0-9a-f]{40}\.(pack|idx)) | \
+			git-(upload|receive)-pack))$" \
+	/usr/libexec/git-core/git-http-backend/$1
+
+ScriptAlias /git/ /var/www/cgi-bin/gitweb.cgi/
+----------------------------------------------------------------
++
+To serve multiple repositories from different linkgit:gitnamespaces[7] in a
+single repository:
++
+----------------------------------------------------------------
+SetEnvIf Request_URI "^/git/([^/]*)" GIT_NAMESPACE=$1
+ScriptAliasMatch ^/git/[^/]*(.*) /usr/libexec/git-core/git-http-backend/storage.git$1
+----------------------------------------------------------------
+
+Accelerated static Apache 2.x::
+	Similar to the above, but Apache can be used to return static
+	files that are stored on disk.	On many systems this may
+	be more efficient as Apache can ask the kernel to copy the
+	file contents from the file system directly to the network:
++
+----------------------------------------------------------------
+SetEnv GIT_PROJECT_ROOT /var/www/git
+
+AliasMatch ^/git/(.*/objects/[0-9a-f]{2}/[0-9a-f]{38})$          /var/www/git/$1
+AliasMatch ^/git/(.*/objects/pack/pack-[0-9a-f]{40}.(pack|idx))$ /var/www/git/$1
+ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/
+----------------------------------------------------------------
++
+This can be combined with the gitweb configuration:
++
+----------------------------------------------------------------
+SetEnv GIT_PROJECT_ROOT /var/www/git
+
+AliasMatch ^/git/(.*/objects/[0-9a-f]{2}/[0-9a-f]{38})$          /var/www/git/$1
+AliasMatch ^/git/(.*/objects/pack/pack-[0-9a-f]{40}.(pack|idx))$ /var/www/git/$1
+ScriptAliasMatch \
+	"(?x)^/git/(.*/(HEAD | \
+			info/refs | \
+			objects/info/[^/]+ | \
+			git-(upload|receive)-pack))$" \
+	/usr/libexec/git-core/git-http-backend/$1
+ScriptAlias /git/ /var/www/cgi-bin/gitweb.cgi/
+----------------------------------------------------------------
+
+
+ENVIRONMENT
+-----------
+'git http-backend' relies upon the CGI environment variables set
+by the invoking web server, including:
+
+* PATH_INFO (if GIT_PROJECT_ROOT is set, otherwise PATH_TRANSLATED)
+* REMOTE_USER
+* REMOTE_ADDR
+* CONTENT_TYPE
+* QUERY_STRING
+* REQUEST_METHOD
+
+The GIT_HTTP_EXPORT_ALL environmental variable may be passed to
+'git-http-backend' to bypass the check for the "git-daemon-export-ok"
+file in each repository before allowing export of that repository.
+
+The backend process sets GIT_COMMITTER_NAME to '$REMOTE_USER' and
+GIT_COMMITTER_EMAIL to '$\{REMOTE_USER}@http.$\{REMOTE_ADDR\}',
+ensuring that any reflogs created by 'git-receive-pack' contain some
+identifying information of the remote user who performed the push.
+
+All CGI environment variables are available to each of the hooks
+invoked by the 'git-receive-pack'.
+
+Author
+------
+Written by Shawn O. Pearce <spearce@spearce.org>.
+
+Documentation
+--------------
+Documentation by Shawn O. Pearce <spearce@spearce.org>.
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-http-fetch.txt b/Documentation/git-http-fetch.txt
index e7c7961..070cd1e 100644
--- a/Documentation/git-http-fetch.txt
+++ b/Documentation/git-http-fetch.txt
@@ -8,12 +8,16 @@
 
 SYNOPSIS
 --------
+[verse]
 'git http-fetch' [-c] [-t] [-a] [-d] [-v] [-w filename] [--recover] [--stdin] <commit> <url>
 
 DESCRIPTION
 -----------
 Downloads a remote git repository via HTTP.
 
+*NOTE*: use of this command without -a is deprecated.  The -a
+behaviour will become the default in a future release.
+
 OPTIONS
 -------
 commit-id::
@@ -35,7 +39,7 @@
 
 --stdin::
 	Instead of a commit id on the command line (which is not expected in this
-	case), 'git-http-fetch' expects lines on stdin in the format
+	case), 'git http-fetch' expects lines on stdin in the format
 
 		<commit-id>['\t'<filename-as-in--w>]
 
@@ -43,14 +47,6 @@
 	Verify that everything reachable from target is fetched.  Used after
 	an earlier fetch is interrupted.
 
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-http-push.txt b/Documentation/git-http-push.txt
index aef383e..2e67362 100644
--- a/Documentation/git-http-push.txt
+++ b/Documentation/git-http-push.txt
@@ -8,6 +8,7 @@
 
 SYNOPSIS
 --------
+[verse]
 'git http-push' [--all] [--dry-run] [--force] [--verbose] <url> <ref> [<ref>...]
 
 DESCRIPTION
@@ -82,24 +83,15 @@
 
 Without '--force', the <src> ref is stored at the remote only if
 <dst> does not exist, or <dst> is a proper subset (i.e. an
-ancestor) of <src>.  This check, known as "fast forward check",
+ancestor) of <src>.  This check, known as "fast-forward check",
 is performed in order to avoid accidentally overwriting the
 remote ref and lose other peoples' commits from there.
 
-With '--force', the fast forward check is disabled for all refs.
+With '--force', the fast-forward check is disabled for all refs.
 
 Optionally, a <ref> parameter can be prefixed with a plus '+' sign
 to disable the fast-forward check only on that ref.
 
-
-Author
-------
-Written by Nick Hengeveld <nickh@reactrix.com>
-
-Documentation
---------------
-Documentation by Nick Hengeveld
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-imap-send.txt b/Documentation/git-imap-send.txt
index d016daf..875d283 100644
--- a/Documentation/git-imap-send.txt
+++ b/Documentation/git-imap-send.txt
@@ -8,15 +8,18 @@
 
 SYNOPSIS
 --------
+[verse]
 'git imap-send'
 
 
 DESCRIPTION
 -----------
-This command uploads a mailbox generated with 'git-format-patch'
+This command uploads a mailbox generated with 'git format-patch'
 into an IMAP drafts folder.  This allows patches to be sent as
 other email is when using mail clients that cannot read mailbox
-files directly.
+files directly. The command also works with any general mailbox
+in which emails have the fields "From", "Date", and "Subject" in
+that order.
 
 Typical usage is something like:
 
@@ -71,6 +74,10 @@
 	option causes Thunderbird to send the patch as a plain/text,
 	format=fixed email.  Default is `false`.
 
+imap.authMethod::
+	Specify authenticate method for authentication with IMAP server.
+	Current supported method is 'CRAM-MD5' only.
+
 Examples
 ~~~~~~~~
 
@@ -105,6 +112,31 @@
 ..........................
 
 
+EXAMPLE
+-------
+To submit patches using GMail's IMAP interface, first, edit your ~/.gitconfig
+to specify your account settings:
+
+---------
+[imap]
+	folder = "[Gmail]/Drafts"
+	host = imaps://imap.gmail.com
+	user = user@gmail.com
+	port = 993
+	sslverify = false
+---------
+
+You might need to instead use: folder = "[Google Mail]/Drafts" if you get an error
+that the "Folder doesn't exist".
+
+Once the commits are ready to be sent, run the following command:
+
+  $ git format-patch --cover-letter -M --stdout origin/master | git imap-send
+
+Just make sure to disable line wrapping in the email client (GMail's web
+interface will wrap lines no matter what, so you need to use a real
+IMAP client).
+
 CAUTION
 -------
 It is still your responsibility to make sure that the email message
@@ -118,19 +150,9 @@
 users may wish to visit this web page for more information:
   http://kb.mozillazine.org/Plain_text_e-mail_-_Thunderbird#Completely_plain_email
 
-
-BUGS
-----
-Doesn't handle lines starting with "From " in the message body.
-
-
-Author
-------
-Derived from isync 1.0.1 by Mike McCormack.
-
-Documentation
---------------
-Documentation by Mike McCormack
+SEE ALSO
+--------
+linkgit:git-format-patch[1], linkgit:git-send-email[1], mbox(5)
 
 GIT
 ---
diff --git a/Documentation/git-index-pack.txt b/Documentation/git-index-pack.txt
index 4b5c743..39e6d0d 100644
--- a/Documentation/git-index-pack.txt
+++ b/Documentation/git-index-pack.txt
@@ -43,30 +43,26 @@
 	a default name determined from the pack content.  If
 	<pack-file> is not specified consider using --keep to
 	prevent a race condition between this process and
-	'git-repack'.
+	'git repack'.
 
 --fix-thin::
-	It is possible for 'git-pack-objects' to build
-	"thin" pack, which records objects in deltified form based on
-	objects not included in the pack to reduce network traffic.
-	Those objects are expected to be present on the receiving end
-	and they must be included in the pack for that pack to be self
-	contained and indexable. Without this option any attempt to
-	index a thin pack will fail. This option only makes sense in
-	conjunction with --stdin.
+	Fix a "thin" pack produced by `git pack-objects --thin` (see
+	linkgit:git-pack-objects[1] for details) by adding the
+	excluded objects the deltified objects are based on to the
+	pack. This option only makes sense in conjunction with --stdin.
 
 --keep::
 	Before moving the index into its final destination
 	create an empty .keep file for the associated pack file.
 	This option is usually necessary with --stdin to prevent a
-	simultaneous 'git-repack' process from deleting
+	simultaneous 'git repack' process from deleting
 	the newly constructed pack and index before refs can be
 	updated to use objects contained in the pack.
 
---keep='why'::
+--keep=<msg>::
 	Like --keep create a .keep file before moving the index into
 	its final destination, but rather than creating an empty file
-	place 'why' followed by an LF into the .keep file.  The 'why'
+	place '<msg>' followed by an LF into the .keep file.  The '<msg>'
 	message can later be searched for within all .keep files to
 	locate any which have outlived their usefulness.
 
@@ -78,6 +74,16 @@
 --strict::
 	Die, if the pack contains broken objects or links.
 
+--threads=<n>::
+	Specifies the number of threads to spawn when resolving
+	deltas. This requires that index-pack be compiled with
+	pthreads otherwise this option is ignored with a warning.
+	This is meant to reduce packing time on multiprocessor
+	machines. The required amount of memory for the delta search
+	window is however multiplied by the number of threads.
+	Specifying 0 will cause git to auto-detect the number of CPU's
+	and use maximum 3 threads.
+
 
 Note
 ----
@@ -86,18 +92,9 @@
 and the SHA1 hash of that list is printed to stdout. If --stdin was
 also used then this is prefixed by either "pack\t", or "keep\t" if a
 new .keep file was successfully created. This is useful to remove a
-.keep file used as a lock to prevent the race with 'git-repack'
+.keep file used as a lock to prevent the race with 'git repack'
 mentioned above.
 
-
-Author
-------
-Written by Sergey Vlasov <vsu@altlinux.ru>
-
-Documentation
--------------
-Documentation by Sergey Vlasov
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-init-db.txt b/Documentation/git-init-db.txt
index eba3cb4..a21e346 100644
--- a/Documentation/git-init-db.txt
+++ b/Documentation/git-init-db.txt
@@ -8,7 +8,8 @@
 
 SYNOPSIS
 --------
-'git init-db' [-q | --quiet] [--bare] [--template=<template_directory>] [--shared[=<permissions>]]
+[verse]
+'git init-db' [-q | --quiet] [--bare] [--template=<template_directory>] [--separate-git-dir <git dir>] [--shared[=<permissions>]]
 
 
 DESCRIPTION
@@ -16,3 +17,7 @@
 
 This is a synonym for linkgit:git-init[1].  Please refer to the
 documentation of that command.
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-init.txt b/Documentation/git-init.txt
index f081b24..9ac2bba 100644
--- a/Documentation/git-init.txt
+++ b/Documentation/git-init.txt
@@ -8,9 +8,33 @@
 
 SYNOPSIS
 --------
-'git init' [-q | --quiet] [--bare] [--template=<template_directory>] [--shared[=<permissions>]] [directory]
+[verse]
+'git init' [-q | --quiet] [--bare] [--template=<template_directory>]
+	  [--separate-git-dir <git dir>]
+	  [--shared[=<permissions>]] [directory]
 
 
+DESCRIPTION
+-----------
+
+This command creates an empty git repository - basically a `.git`
+directory with subdirectories for `objects`, `refs/heads`,
+`refs/tags`, and template files.  An initial `HEAD` file that
+references the HEAD of the master branch is also created.
+
+If the `$GIT_DIR` environment variable is set then it specifies a path
+to use instead of `./.git` for the base of the repository.
+
+If the object storage directory is specified via the
+`$GIT_OBJECT_DIRECTORY` environment variable then the sha1 directories
+are created underneath - otherwise the default `$GIT_DIR/objects`
+directory is used.
+
+Running 'git init' in an existing repository is safe. It will not
+overwrite things that are already there. The primary reason for
+rerunning 'git init' is to pick up newly added templates (or to move
+the repository to another place if --separate-git-dir is given).
+
 OPTIONS
 -------
 
@@ -28,16 +52,19 @@
 
 --template=<template_directory>::
 
-Provide the directory from which templates will be used.  The default template
-directory is `/usr/share/git-core/templates`.
+Specify the directory from which templates will be used.  (See the "TEMPLATE
+DIRECTORY" section below.)
 
-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.
+--separate-git-dir=<git dir>::
 
---shared[={false|true|umask|group|all|world|everybody|0xxx}]::
+Instead of initializing the repository where it is supposed to be,
+place a filesytem-agnostic git symbolic link there, pointing to the
+specified git path, and initialize a git repository at the path. The
+result is git repository can be separated from working tree. If this
+is reinitialization, the repository will be moved to the specified
+path.
+
+--shared[=(false|true|umask|group|all|world|everybody|0xxx)]::
 
 Specify that the git repository is to be shared amongst several users.  This
 allows users belonging to the same group to push into that
@@ -80,32 +107,25 @@
 --
 
 
-DESCRIPTION
------------
-This command creates an empty git repository - basically a `.git` directory
-with subdirectories for `objects`, `refs/heads`, `refs/tags`, and
-template files.
-An initial `HEAD` file that references the HEAD of the master branch
-is also created.
+TEMPLATE DIRECTORY
+------------------
 
-If the `$GIT_DIR` environment variable is set then it specifies a path
-to use instead of `./.git` for the base of the repository.
+The template directory contains files and directories that will be copied to
+the `$GIT_DIR` after it is created.
 
-If the object storage directory is specified via the `$GIT_OBJECT_DIRECTORY`
-environment variable then the sha1 directories are created underneath -
-otherwise the default `$GIT_DIR/objects` directory is used.
+The template directory used will (in order):
 
-Running 'git-init' in an existing repository is safe. It will not overwrite
-things that are already there. The primary reason for rerunning 'git-init'
-is to pick up newly added templates.
+ - The argument given with the `--template` option.
 
-Note that 'git-init' is the same as 'git-init-db'.  The command
-was primarily meant to initialize the object database, but over
-time it has become responsible for setting up the other aspects
-of the repository, such as installing the default hooks and
-setting the configuration variables.  The old name is retained
-for backward compatibility reasons.
+ - The contents of the `$GIT_TEMPLATE_DIR` environment variable.
 
+ - The `init.templatedir` configuration variable.
+
+ - The default template directory: `/usr/share/git-core/templates`.
+
+The default template directory includes some directory structure, some
+suggested "exclude patterns", and copies of sample "hook" files.
+The suggested patterns and hook files are all modifiable and extensible.
 
 EXAMPLES
 --------
@@ -121,15 +141,6 @@
 <1> prepare /path/to/my/codebase/.git directory
 <2> add all existing file to the index
 
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-instaweb.txt b/Documentation/git-instaweb.txt
index 22da21a..f3eef51 100644
--- a/Documentation/git-instaweb.txt
+++ b/Documentation/git-instaweb.txt
@@ -29,7 +29,7 @@
 	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, apache2 and webrick are supported.
+	Currently apache2, lighttpd, mongoose, plackup and webrick are supported.
 	(Default: lighttpd)
 
 -m::
@@ -44,23 +44,26 @@
 -b::
 --browser::
 	The web browser that should be used to view the gitweb
-	page. This will be passed to the 'git-web--browse' helper
+	page. This will be passed to the 'git web{litdd}browse' helper
 	script along with the URL of the gitweb instance. See
-	linkgit:git-web--browse[1] for more information about this. If
+	linkgit:git-web{litdd}browse[1] for more information about this. If
 	the script fails, the URL will be printed to stdout.
 
+start::
 --start::
-	Start the httpd instance and exit.  This does not generate
-	any of the configuration files for spawning a new instance.
+	Start the httpd instance and exit.  Regenerate configuration files
+	as necessary for spawning a new instance.
 
+stop::
 --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::
-	Restart the httpd instance and exit.  This does not generate
-	any of the configuration files for spawning a new instance.
+	Restart the httpd instance and exit.  Regenerate configuration files
+	as necessary for spawning a new instance.
 
 CONFIGURATION
 -------------
@@ -79,15 +82,11 @@
 
 If the configuration variable 'instaweb.browser' is not set,
 'web.browser' will be used instead if it is defined. See
-linkgit:git-web--browse[1] for more information about this.
+linkgit:git-web{litdd}browse[1] for more information about this.
 
-Author
-------
-Written by Eric Wong <normalperson@yhbt.net>
-
-Documentation
---------------
-Documentation by Eric Wong <normalperson@yhbt.net>.
+SEE ALSO
+--------
+linkgit:gitweb[1]
 
 GIT
 ---
diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt
index 34cf4e5..1f90620 100644
--- a/Documentation/git-log.txt
+++ b/Documentation/git-log.txt
@@ -8,37 +8,44 @@
 
 SYNOPSIS
 --------
+[verse]
 'git log' [<options>] [<since>..<until>] [[\--] <path>...]
 
 DESCRIPTION
 -----------
 Shows the commit logs.
 
-The command takes options applicable to the 'git-rev-list'
+The command takes options applicable to the 'git rev-list'
 command to control what is shown and how, and options applicable to
-the 'git-diff-*' commands to control how the changes
+the 'git diff-*' commands to control how the changes
 each commit introduces are shown.
 
 
 OPTIONS
 -------
 
-:git-log: 1
-include::diff-options.txt[]
-
 -<n>::
 	Limits the number of commits to show.
+	Note that this is a commit limiting option, see below.
 
 <since>..<until>::
 	Show only commits between the named two commits.  When
 	either <since> or <until> is omitted, it defaults to
 	`HEAD`, i.e. the tip of the current branch.
 	For a more complete list of ways to spell <since>
-	and <until>, see "SPECIFYING REVISIONS" section in
-	linkgit:git-rev-parse[1].
+	and <until>, see linkgit:gitrevisions[7].
 
---decorate::
-	Print out the ref names of any commits that are shown.
+--follow::
+	Continue listing the history of a file beyond renames
+	(works only for a single file).
+
+--no-decorate::
+--decorate[=short|full|no]::
+	Print out the ref names of any commits that are shown. If 'short' is
+	specified, the ref name prefixes 'refs/heads/', 'refs/tags/' and
+	'refs/remotes/' will not be printed. If 'full' is specified, the
+	full ref name (including prefix) will be printed. The default option
+	is 'short'.
 
 --source::
 	Print out the ref name given on the command line by which each
@@ -50,9 +57,9 @@
 	paths.  With this, the full diff is shown for commits that touch
 	the specified paths; this means that "<path>..." limits only
 	commits, and doesn't limit diff for those commits.
-
---follow::
-	Continue listing the history of a file beyond renames.
++
+Note that this affects all diff-based output types, e.g. those
+produced by --stat etc.
 
 --log-size::
 	Before the log message print out its size in bytes. Intended
@@ -62,60 +69,122 @@
 	its size is not included.
 
 [\--] <path>...::
-	Show only commits that affect any of the specified paths. To
-	prevent confusion with options and branch names, paths may need
-	to be prefixed with "\-- " to separate them from options or
-	refnames.
-
+	Show only commits that are enough to explain how the files
+	that match the specified paths came to be.  See "History
+	Simplification" below for details and other simplification
+	modes.
++
+To prevent confusion with options and branch names, paths may need to
+be prefixed with "\-- " to separate them from options or refnames.
 
 include::rev-list-options.txt[]
 
 include::pretty-formats.txt[]
 
+Common diff options
+-------------------
+
+:git-log: 1
+include::diff-options.txt[]
+
 include::diff-generate-patch.txt[]
 
 Examples
 --------
-git log --no-merges::
+`git log --no-merges`::
 
 	Show the whole commit history, but skip any merges
 
-git log v2.6.12.. include/scsi drivers/scsi::
+`git log v2.6.12.. include/scsi drivers/scsi`::
 
 	Show all commits since version 'v2.6.12' that changed any file
 	in the include/scsi or drivers/scsi subdirectories
 
-git log --since="2 weeks ago" \-- gitk::
+`git log --since="2 weeks ago" -- gitk`::
 
 	Show the changes during the last two weeks to the file 'gitk'.
 	The "--" is necessary to avoid confusion with the *branch* named
 	'gitk'
 
-git log --name-status release..test::
+`git log --name-status release..test`::
 
 	Show the commits that are in the "test" branch but not yet
 	in the "release" branch, along with the list of paths
 	each commit modifies.
 
-git log --follow builtin-rev-list.c::
+`git log --follow builtin-rev-list.c`::
 
 	Shows the commits that changed builtin-rev-list.c, including
 	those commits that occurred before the file was given its
 	present name.
 
+`git log --branches --not --remotes=origin`::
+
+	Shows all commits that are in any of local branches but not in
+	any of remote-tracking branches for 'origin' (what you have that
+	origin doesn't).
+
+`git log master --not --remotes=*/master`::
+
+	Shows all commits that are in local master but not in any remote
+	repository master branches.
+
+`git log -p -m --first-parent`::
+
+	Shows the history including change diffs, but only from the
+	"main branch" perspective, skipping commits that come from merged
+	branches, and showing full diffs of changes introduced by the merges.
+	This makes sense only when following a strict policy of merging all
+	topic branches when staying on a single integration branch.
+
+
 Discussion
 ----------
 
 include::i18n.txt[]
 
+Configuration
+-------------
 
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
+See linkgit:git-config[1] for core variables and linkgit:git-diff[1]
+for settings related to diff generation.
 
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+format.pretty::
+	Default for the `--format` option.  (See "PRETTY FORMATS" above.)
+	Defaults to "medium".
+
+i18n.logOutputEncoding::
+	Encoding to use when displaying logs.  (See "Discussion", above.)
+	Defaults to the value of `i18n.commitEncoding` if set, UTF-8
+	otherwise.
+
+log.date::
+	Default format for human-readable dates.  (Compare the
+	`--date` option.)  Defaults to "default", which means to write
+	dates like `Sat May 8 19:35:34 2010 -0500`.
+
+log.showroot::
+	If `false`, 'git log' and related commands will not treat the
+	initial commit as a big creation event.  Any root commits in
+	`git log -p` output would be shown without a diff attached.
+	The default is `true`.
+
+mailmap.file::
+	See linkgit:git-shortlog[1].
+
+notes.displayRef::
+	Which refs, in addition to the default set by `core.notesRef`
+	or 'GIT_NOTES_REF', to read notes from when showing commit
+	messages with the 'log' family of commands.  See
+	linkgit:git-notes[1].
++
+May be an unabbreviated ref name or a glob and may be specified
+multiple times.  A warning will be issued for refs that do not exist,
+but a glob that does not match any refs is silently ignored.
++
+This setting can be disabled by the `--no-notes` option,
+overridden by the 'GIT_NOTES_DISPLAY_REF' environment variable,
+and overridden by the `--notes=<ref>` option.
 
 GIT
 ---
diff --git a/Documentation/git-lost-found.txt b/Documentation/git-lost-found.txt
index 602b8d5..c406a11 100644
--- a/Documentation/git-lost-found.txt
+++ b/Documentation/git-lost-found.txt
@@ -7,6 +7,7 @@
 
 SYNOPSIS
 --------
+[verse]
 'git lost-found'
 
 DESCRIPTION
@@ -67,15 +68,6 @@
 1ef2b196d909eed523d4f3c9bf54b78cdd6843c6
 ------------
 
-Author
-------
-Written by Junio C Hamano <gitster@pobox.com>
-
-Documentation
---------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
-
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt
index 057a021..4b28292 100644
--- a/Documentation/git-ls-files.txt
+++ b/Documentation/git-ls-files.txt
@@ -10,14 +10,14 @@
 --------
 [verse]
 'git ls-files' [-z] [-t] [-v]
-		(--[cached|deleted|others|ignored|stage|unmerged|killed|modified])\*
-		(-[c|d|o|i|s|u|k|m])\*
+		(--[cached|deleted|others|ignored|stage|unmerged|killed|modified])*
+		(-[c|d|o|i|s|u|k|m])*
 		[-x <pattern>|--exclude=<pattern>]
 		[-X <file>|--exclude-from=<file>]
 		[--exclude-per-directory=<file>]
 		[--exclude-standard]
 		[--error-unmatch] [--with-tree=<tree-ish>]
-		[--full-name] [--abbrev] [--] [<file>]\*
+		[--full-name] [--abbrev] [--] [<file>...]
 
 DESCRIPTION
 -----------
@@ -44,12 +44,14 @@
 
 -o::
 --others::
-	Show other files in the output
+	Show other (i.e. untracked) files in the output
 
 -i::
 --ignored::
-	Show ignored files in the output.
-	Note that this also reverses any exclude list present.
+	Show only ignored files in the output. When showing files in the
+	index, print only those matched by an exclude pattern. When
+	showing "other" files, show only those matched by an exclude
+	pattern.
 
 -s::
 --stage::
@@ -77,15 +79,16 @@
 
 -x <pattern>::
 --exclude=<pattern>::
-	Skips files matching pattern.
-	Note that pattern is a shell wildcard pattern.
+	Skip untracked files matching pattern.
+	Note that pattern is a shell wildcard pattern. See EXCLUDE PATTERNS
+	below for more information.
 
 -X <file>::
 --exclude-from=<file>::
-	exclude patterns are read from <file>; 1 per line.
+	Read exclude patterns from <file>; 1 per line.
 
 --exclude-per-directory=<file>::
-	read additional exclude patterns that apply only to the
+	Read additional exclude patterns that apply only to the
 	directory and its subdirectories in <file>.
 
 --exclude-standard::
@@ -104,9 +107,18 @@
 	with `-s` or `-u` options does not make any sense.
 
 -t::
-	Identify the file status with the following tags (followed by
-	a space) at the start of each line:
+	This feature is semi-deprecated. For scripting purpose,
+	linkgit:git-status[1] `--porcelain` and
+	linkgit:git-diff-files[1] `--name-status` are almost always
+	superior alternatives, and users should look at
+	linkgit:git-status[1] `--short` or linkgit:git-diff[1]
+	`--name-status` for more user-friendly alternatives.
++
+This option identifies the file status with the following tags (followed by
+a space) at the start of each line:
+
 	H::	cached
+	S::	skip-worktree
 	M::	unmerged
 	R::	removed/deleted
 	C::	modified/changed
@@ -129,6 +141,12 @@
 	lines, show only a partial prefix.
 	Non default number of digits can be specified with --abbrev=<n>.
 
+--debug::
+	After each line that describes a file, add more data about its
+	cache entry.  This is intended to show as much information as
+	possible for manual inspection; the exact format may change at
+	any time.
+
 \--::
 	Do not interpret any more arguments as options.
 
@@ -138,12 +156,12 @@
 
 Output
 ------
-show files just outputs the filename unless '--stage' is specified in
+'git ls-files' just outputs the filenames unless '--stage' is specified in
 which case it outputs:
 
         [<tag> ]<mode> <object> <stage> <file>
 
-'git-ls-files --unmerged' and 'git-ls-files --stage' can be used to examine
+'git ls-files --unmerged' and 'git ls-files --stage' can be used to examine
 detailed information on unmerged paths.
 
 For an unmerged path, instead of recording a single mode/SHA1 pair,
@@ -160,7 +178,7 @@
 Exclude Patterns
 ----------------
 
-'git-ls-files' can use a list of "exclude patterns" when
+'git ls-files' can use a list of "exclude patterns" when
 traversing the directory tree and finding files to show when the
 flags --others or --ignored are specified.  linkgit:gitignore[5]
 specifies the format of exclude patterns.
@@ -175,8 +193,8 @@
      file containing a list of patterns.  Patterns are ordered
      in the same order they appear in the file.
 
-  3. command line flag --exclude-per-directory=<name> specifies
-     a name of the file in each directory 'git-ls-files'
+  3. The command line flag --exclude-per-directory=<name> specifies
+     a name of the file in each directory 'git ls-files'
      examines, normally `.gitignore`.  Files in deeper
      directories take precedence.  Patterns are ordered in the
      same order they appear in the files.
@@ -191,15 +209,6 @@
 --------
 linkgit:git-read-tree[1], linkgit:gitignore[5]
 
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano, Josh Triplett, and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-ls-remote.txt b/Documentation/git-ls-remote.txt
index abe7bf9..7a9b86a 100644
--- a/Documentation/git-ls-remote.txt
+++ b/Documentation/git-ls-remote.txt
@@ -10,7 +10,7 @@
 --------
 [verse]
 'git ls-remote' [--heads] [--tags]  [-u <exec> | --upload-pack <exec>]
-	      <repository> <refs>...
+	      [--exit-code] <repository> [<refs>...]
 
 DESCRIPTION
 -----------
@@ -36,6 +36,12 @@
 	SSH and where the SSH daemon does not use the PATH configured by the
 	user.
 
+--exit-code::
+	Exit with status "2" when no matching refs are found in the remote
+	repository. Usually the command exits with status "0" to indicate
+	it successfully talked with the remote repository, whether it
+	found any matching refs.
+
 <repository>::
 	Location of the repository.  The shorthand defined in
 	$GIT_DIR/branches/ can be used. Use "." (dot) to list references in
@@ -67,10 +73,6 @@
 	c5db5456ae3b0873fc659c19fafdde22313cc441	refs/tags/v0.99.2
 	7ceca275d047c90c0c7d5afb13ab97efdf51bd6e	refs/tags/v0.99.3
 
-Author
-------
-Written by Junio C Hamano <gitster@pobox.com>
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-ls-tree.txt b/Documentation/git-ls-tree.txt
index c3fdccb..16e87fd 100644
--- a/Documentation/git-ls-tree.txt
+++ b/Documentation/git-ls-tree.txt
@@ -10,8 +10,8 @@
 --------
 [verse]
 'git ls-tree' [-d] [-r] [-t] [-l] [-z]
-	    [--name-only] [--name-status] [--full-name] [--full-tree] [--abbrev=[<n>]]
-	    <tree-ish> [paths...]
+	    [--name-only] [--name-status] [--full-name] [--full-tree] [--abbrev[=<n>]]
+	    <tree-ish> [<path>...]
 
 DESCRIPTION
 -----------
@@ -19,16 +19,16 @@
 in the current working directory.  Note that:
 
  - the behaviour is slightly different from that of "/bin/ls" in that the
-   'paths' denote just a list of patterns to match, e.g. so specifying
+   '<path>' denotes just a list of patterns to match, e.g. so specifying
    directory name (without '-r') will behave differently, and order of the
    arguments does not matter.
 
- - the behaviour is similar to that of "/bin/ls" in that the 'paths' is
+ - the behaviour is similar to that of "/bin/ls" in that the '<path>' is
    taken as relative to the current working directory.  E.g. when you are
    in a directory 'sub' that has a directory 'dir', you can run 'git
    ls-tree -r HEAD dir' to list the contents of the tree (that is
    'sub/dir' in 'HEAD').  You don't want to give a tree that is not at the
-   root level (e.g. 'git ls-tree -r HEAD:sub dir') in this case, as that
+   root level (e.g. `git ls-tree -r HEAD:sub dir`) in this case, as that
    would result in asking for 'sub/sub/dir' in the 'HEAD' commit.
    However, the current working directory can be ignored by passing
    --full-tree option.
@@ -72,7 +72,7 @@
 	Do not limit the listing to the current working directory.
 	Implies --full-name.
 
-paths::
+[<path>...]::
 	When paths are given, show them (note that this isn't really raw
 	pathnames, but rather a list of patterns to match).  Otherwise
 	implicitly uses the root level of the tree as the sole path argument.
@@ -84,7 +84,7 @@
 
 Unless the `-z` option is used, TAB, LF, and backslash characters
 in pathnames are represented as `\t`, `\n`, and `\\`, respectively.
-This output format is compatible with what '--index-info --stdin' of
+This output format is compatible with what `--index-info --stdin` of
 'git update-index' expects.
 
 When the `-l` option is used, format changes to
@@ -95,18 +95,6 @@
 with minimum width of 7 characters.  Object size is given only for blobs
 (file) entries; for other entries `-` character is used in place of size.
 
-
-Author
-------
-Written by Petr Baudis <pasky@suse.cz>
-Completely rewritten from scratch by Junio C Hamano <gitster@pobox.com>,
-another major rewrite by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list
-<git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-mailinfo.txt b/Documentation/git-mailinfo.txt
index 8d95aaa..97e7a8e 100644
--- a/Documentation/git-mailinfo.txt
+++ b/Documentation/git-mailinfo.txt
@@ -8,7 +8,8 @@
 
 SYNOPSIS
 --------
-'git mailinfo' [-k] [-u | --encoding=<encoding> | -n] <msg> <patch>
+[verse]
+'git mailinfo' [-k|-b] [-u | --encoding=<encoding> | -n] [--scissors] <msg> <patch>
 
 
 DESCRIPTION
@@ -16,7 +17,7 @@
 Reads a single e-mail message from the standard input, and
 writes the commit log message in <msg> file, and the patches in
 <patch> file.  The author name, e-mail and e-mail subject are
-written out to the standard output to be used by 'git-am'
+written out to the standard output to be used by 'git am'
 to create a commit.  It is usually not necessary to use this
 command directly.  See linkgit:git-am[1] instead.
 
@@ -24,31 +25,66 @@
 OPTIONS
 -------
 -k::
-	Usually the program 'cleans up' the Subject: header line
-	to extract the title line for the commit log message,
-	among which (1) remove 'Re:' or 're:', (2) leading
-	whitespaces, (3) '[' up to ']', typically '[PATCH]', and
-	then prepends "[PATCH] ".  This flag forbids this
-	munging, and is most useful when used to read back
-	'git-format-patch -k' output.
+	Usually the program removes email cruft from the Subject:
+	header line to extract the title line for the commit log
+	message.  This option prevents this munging, and is most
+	useful when used to read back 'git format-patch -k' output.
++
+Specifically, the following are removed until none of them remain:
++
+--
+*	Leading and trailing whitespace.
+
+*	Leading `Re:`, `re:`, and `:`.
+
+*	Leading bracketed strings (between `[` and `]`, usually
+	`[PATCH]`).
+--
++
+Finally, runs of whitespace are normalized to a single ASCII space
+character.
+
+-b::
+	When -k is not in effect, all leading strings bracketed with '['
+	and ']' pairs are stripped.  This option limits the stripping to
+	only the pairs whose bracketed string contains the word "PATCH".
 
 -u::
 	The commit log message, author name and author email are
 	taken from the e-mail, and after minimally decoding MIME
-	transfer encoding, re-coded in UTF-8 by transliterating
+	transfer encoding, re-coded in the charset specified by
+	i18n.commitencoding (defaulting to UTF-8) by transliterating
 	them.  This used to be optional but now it is the default.
 +
 Note that the patch is always used as-is without charset
 conversion, even with this flag.
 
 --encoding=<encoding>::
-	Similar to -u but if the local convention is different
-	from what is specified by i18n.commitencoding, this flag
-	can be used to override it.
+	Similar to -u.  But when re-coding, the charset specified here is
+	used instead of the one specified by i18n.commitencoding or UTF-8.
 
 -n::
 	Disable all charset re-coding of the metadata.
 
+--scissors::
+	Remove everything in body before a scissors line.  A line that
+	mainly consists of scissors (either ">8" or "8<") and perforation
+	(dash "-") marks is called a scissors line, and is used to request
+	the reader to cut the message at that line.  If such a line
+	appears in the body of the message before the patch, everything
+	before it (including the scissors line itself) is ignored when
+	this option is used.
++
+This is useful if you want to begin your message in a discussion thread
+with comments and suggestions on the message you are responding to, and to
+conclude it with a patch submission, separating the discussion and the
+beginning of the proposed commit log message with a scissors line.
++
+This can enabled by default with the configuration option mailinfo.scissors.
+
+--no-scissors::
+	Ignore scissors lines. Useful for overriding mailinfo.scissors settings.
+
 <msg>::
 	The commit log message extracted from e-mail, usually
 	except the title line which comes from e-mail Subject.
@@ -56,17 +92,6 @@
 <patch>::
 	The patch extracted from e-mail.
 
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org> and
-Junio C Hamano <gitster@pobox.com>
-
-
-Documentation
---------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-mailsplit.txt b/Documentation/git-mailsplit.txt
index 5cc94ec..4d1b871 100644
--- a/Documentation/git-mailsplit.txt
+++ b/Documentation/git-mailsplit.txt
@@ -7,7 +7,8 @@
 
 SYNOPSIS
 --------
-'git mailsplit' [-b] [-f<nn>] [-d<prec>] -o<directory> [--] [<mbox>|<Maildir>...]
+[verse]
+'git mailsplit' [-b] [-f<nn>] [-d<prec>] [--keep-cr] -o<directory> [--] [(<mbox>|<Maildir>)...]
 
 DESCRIPTION
 -----------
@@ -43,15 +44,8 @@
 	Skip the first <nn> numbers, for example if -f3 is specified,
 	start the numbering with 0004.
 
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-and Junio C Hamano <gitster@pobox.com>
-
-
-Documentation
---------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
+--keep-cr::
+	Do not remove `\r` from lines ending with `\r\n`.
 
 GIT
 ---
diff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt
index ce5b369..b295bf8 100644
--- a/Documentation/git-merge-base.txt
+++ b/Documentation/git-merge-base.txt
@@ -8,7 +8,10 @@
 
 SYNOPSIS
 --------
+[verse]
 'git merge-base' [-a|--all] <commit> <commit>...
+'git merge-base' [-a|--all] --octopus <commit>...
+'git merge-base' --independent <commit>...
 
 DESCRIPTION
 -----------
@@ -20,17 +23,33 @@
 ancestor', i.e. a 'merge base'.  Note that there can be more than one
 merge base for a pair of commits.
 
-Among the two commits to compute the merge base from, one is specified by
-the first commit argument on the command line; the other commit is a
-(possibly hypothetical) commit that is a merge across all the remaining
-commits on the command line.  As the most common special case, specifying only
-two commits on the command line means computing the merge base between
-the given two commits.
+OPERATION MODE
+--------------
+
+As the most common special case, specifying only two commits on the
+command line means computing the merge base between the given two commits.
+
+More generally, among the two commits to compute the merge base from,
+one is specified by the first commit argument on the command line;
+the other commit is a (possibly hypothetical) commit that is a merge
+across all the remaining commits on the command line.
 
 As a consequence, the 'merge base' is not necessarily contained in each of the
 commit arguments if more than two commits are specified. This is different
 from linkgit:git-show-branch[1] when used with the `--merge-base` option.
 
+--octopus::
+	Compute the best common ancestors of all supplied commits,
+	in preparation for an n-way merge.  This mimics the behavior
+	of 'git show-branch --merge-base'.
+
+--independent::
+	Instead of printing merge bases, print a minimal subset of
+	the supplied commits with the same ancestors.  In other words,
+	among the commits given, list those which cannot be reached
+	from any other.  This mimics the behavior of 'git show-branch
+	--independent'.
+
 OPTIONS
 -------
 -a::
@@ -75,6 +94,9 @@
 common ancestor between 'A' and 'M', but '1' is a better common ancestor,
 because '2' is an ancestor of '1'.  Hence, '2' is not a merge base.
 
+The result of `git merge-base --octopus A B C` is '2', because '2' is
+the best common ancestor of all commits.
+
 When the history involves criss-cross merges, there can be more than one
 'best' common ancestor for two commits.  For example, with this topology:
 
@@ -88,13 +110,11 @@
 the other (both are 'best' merge bases).  When the `--all` option is not given,
 it is unspecified which best one is output.
 
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+See also
+--------
+linkgit:git-rev-list[1],
+linkgit:git-show-branch[1],
+linkgit:git-merge[1]
 
 GIT
 ---
diff --git a/Documentation/git-merge-file.txt b/Documentation/git-merge-file.txt
index 3035373..d7db2a3 100644
--- a/Documentation/git-merge-file.txt
+++ b/Documentation/git-merge-file.txt
@@ -10,20 +10,21 @@
 --------
 [verse]
 'git merge-file' [-L <current-name> [-L <base-name> [-L <other-name>]]]
-	[-p|--stdout] [-q|--quiet] <current-file> <base-file> <other-file>
+	[--ours|--theirs|--union] [-p|--stdout] [-q|--quiet] [--marker-size=<n>]
+	<current-file> <base-file> <other-file>
 
 
 DESCRIPTION
 -----------
-'git-merge-file' incorporates all changes that lead from the `<base-file>`
+'git merge-file' incorporates all changes that lead from the `<base-file>`
 to `<other-file>` into `<current-file>`. The result ordinarily goes into
-`<current-file>`. 'git-merge-file' is useful for combining separate changes
+`<current-file>`. 'git merge-file' is useful for combining separate changes
 to an original. Suppose `<base-file>` is the original, and both
 `<current-file>` and `<other-file>` are modifications of `<base-file>`,
-then 'git-merge-file' combines both changes.
+then 'git merge-file' combines both changes.
 
 A conflict occurs if both `<current-file>` and `<other-file>` have changes
-in a common segment of lines. If a conflict is found, 'git-merge-file'
+in a common segment of lines. If a conflict is found, 'git merge-file'
 normally outputs a warning and brackets the conflict with lines containing
 <<<<<<< and >>>>>>> markers. A typical conflict will look like this:
 
@@ -34,12 +35,15 @@
 	>>>>>>> B
 
 If there are conflicts, the user should edit the result and delete one of
-the alternatives.
+the alternatives.  When `--ours`, `--theirs`, or `--union` option is in effect,
+however, these conflicts are resolved favouring lines from `<current-file>`,
+lines from `<other-file>`, or lines from both respectively.  The length of the
+conflict markers can be given with the `--marker-size` option.
 
 The exit value of this program is negative on error, and the number of
 conflicts otherwise. If the merge was clean, the exit value is 0.
 
-'git-merge-file' is designed to be a minimal clone of RCS 'merge'; that is, it
+'git merge-file' is designed to be a minimal clone of RCS 'merge'; that is, it
 implements all of RCS 'merge''s functionality which is needed by
 linkgit:git[1].
 
@@ -62,31 +66,26 @@
 -q::
 	Quiet; do not warn about conflicts.
 
+--ours::
+--theirs::
+--union::
+	Instead of leaving conflicts in the file, resolve conflicts
+	favouring our (or their or both) side of the lines.
+
 
 EXAMPLES
 --------
 
-git merge-file README.my README README.upstream::
+`git merge-file README.my README README.upstream`::
 
 	combines the changes of README.my and README.upstream since README,
 	tries to merge them and writes the result into README.my.
 
-git merge-file -L a -L b -L c tmp/a123 tmp/b234 tmp/c345::
+`git merge-file -L a -L b -L c tmp/a123 tmp/b234 tmp/c345`::
 
 	merges tmp/a123 and tmp/c345 with the base tmp/b234, but uses labels
 	`a` and `c` instead of `tmp/a123` and `tmp/c345`.
 
-
-Author
-------
-Written by Johannes Schindelin <johannes.schindelin@gmx.de>
-
-
-Documentation
---------------
-Documentation by Johannes Schindelin and the git-list <git@vger.kernel.org>,
-with parts copied from the original documentation of RCS 'merge'.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-merge-index.txt b/Documentation/git-merge-index.txt
index 123e6d0..e0df1b3 100644
--- a/Documentation/git-merge-index.txt
+++ b/Documentation/git-merge-index.txt
@@ -8,7 +8,8 @@
 
 SYNOPSIS
 --------
-'git merge-index' [-o] [-q] <merge-program> (-a | [--] <file>\*)
+[verse]
+'git merge-index' [-o] [-q] <merge-program> (-a | [--] <file>*)
 
 DESCRIPTION
 -----------
@@ -36,14 +37,14 @@
 	failure usually indicates conflicts during the merge). This is for
 	porcelains which might want to emit custom messages.
 
-If 'git-merge-index' is called with multiple <file>s (or -a) then it
+If 'git merge-index' is called with multiple <file>s (or -a) then it
 processes them in turn only stopping if merge returns a non-zero exit
 code.
 
 Typically this is run with a script calling git's imitation of
 the 'merge' command from the RCS package.
 
-A sample script called 'git-merge-one-file' is included in the
+A sample script called 'git merge-one-file' is included in the
 distribution.
 
 ALERT ALERT ALERT! The git "merge object order" is different from the
@@ -68,19 +69,10 @@
   This is added AA in the branch B.
   fatal: merge program failed
 
-where the latter example shows how 'git-merge-index' will stop trying to
+where the latter example shows how 'git merge-index' will stop trying to
 merge once anything has returned an error (i.e., `cat` returned an error
 for the AA file, because it didn't exist in the original, and thus
-'git-merge-index' didn't even try to merge the MM thing).
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-One-shot merge by Petr Baudis <pasky@ucw.cz>
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+'git merge-index' didn't even try to merge the MM thing).
 
 GIT
 ---
diff --git a/Documentation/git-merge-one-file.txt b/Documentation/git-merge-one-file.txt
index dc8a96a..04e803d 100644
--- a/Documentation/git-merge-one-file.txt
+++ b/Documentation/git-merge-one-file.txt
@@ -8,21 +8,13 @@
 
 SYNOPSIS
 --------
-'git-merge-one-file'
+[verse]
+'git merge-one-file'
 
 DESCRIPTION
 -----------
-This is the standard helper program to use with 'git-merge-index'
-to resolve a merge after the trivial merge done with 'git-read-tree -m'.
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>,
-Junio C Hamano <gitster@pobox.com> and Petr Baudis <pasky@suse.cz>.
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+This is the standard helper program to use with 'git merge-index'
+to resolve a merge after the trivial merge done with 'git read-tree -m'.
 
 GIT
 ---
diff --git a/Documentation/git-merge-tree.txt b/Documentation/git-merge-tree.txt
index f869a7f..c5f84b6 100644
--- a/Documentation/git-merge-tree.txt
+++ b/Documentation/git-merge-tree.txt
@@ -8,6 +8,7 @@
 
 SYNOPSIS
 --------
+[verse]
 'git merge-tree' <base-tree> <branch1> <branch2>
 
 DESCRIPTION
@@ -23,14 +24,6 @@
 index.  For this reason, the output from the command omits
 entries that match the <branch1> tree.
 
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index c04ae73..20f9228 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -9,18 +9,56 @@
 SYNOPSIS
 --------
 [verse]
-'git merge' [-n] [--stat] [--no-commit] [--squash] [-s <strategy>]...
-	[-m <msg>] <remote> <remote>...
-'git merge' <msg> HEAD <remote>...
+'git merge' [-n] [--stat] [--no-commit] [--squash] [--[no-]edit]
+	[-s <strategy>] [-X <strategy-option>]
+	[--[no-]rerere-autoupdate] [-m <msg>] [<commit>...]
+'git merge' <msg> HEAD <commit>...
+'git merge' --abort
 
 DESCRIPTION
 -----------
-This is the top-level interface to the merge machinery
-which drives multiple merge strategy scripts.
+Incorporates changes from the named commits (since the time their
+histories diverged from the current branch) into the current
+branch.  This command is used by 'git pull' to incorporate changes
+from another repository and can be used by hand to merge changes
+from one branch into another.
 
-The second syntax (<msg> `HEAD` <remote>) is supported for
+Assume the following history exists and the current branch is
+"`master`":
+
+------------
+	  A---B---C topic
+	 /
+    D---E---F---G master
+------------
+
+Then "`git merge topic`" will replay the changes made on the
+`topic` branch since it diverged from `master` (i.e., `E`) until
+its current commit (`C`) on top of `master`, and record the result
+in a new commit along with the names of the two parent commits and
+a log message from the user describing the changes.
+
+------------
+	  A---B---C topic
+	 /         \
+    D---E---F---G---H master
+------------
+
+The second syntax (<msg> `HEAD` <commit>...) is supported for
 historical reasons.  Do not use it from the command line or in
-new scripts.  It is the same as `git merge -m <msg> <remote>`.
+new scripts.  It is the same as `git merge -m <msg> <commit>...`.
+
+The third syntax ("`git merge --abort`") can only be run after the
+merge has resulted in conflicts. 'git merge --abort' will abort the
+merge process and try to reconstruct the pre-merge state. However,
+if there were uncommitted changes when the merge started (and
+especially if those changes were further modified after the merge
+was started), 'git merge --abort' will in some cases be unable to
+reconstruct the original (pre-merge) changes. Therefore:
+
+*Warning*: Running 'git merge' with uncommitted changes is
+discouraged: while possible, it leaves you in a state that is hard to
+back out of in the case of a conflict.
 
 
 OPTIONS
@@ -28,97 +66,110 @@
 include::merge-options.txt[]
 
 -m <msg>::
-	The commit message to be used for the merge commit (in case
-	it is created). The 'git-fmt-merge-msg' script can be used
-	to give a good default for automated 'git-merge' invocations.
+	Set the commit message to be used for the merge commit (in
+	case one is created).
++
+If `--log` is specified, a shortlog of the commits being merged
+will be appended to the specified message.
++
+The 'git fmt-merge-msg' command can be
+used to give a good default for automated 'git merge'
+invocations.
 
-<remote>...::
-	Other branch heads to merge into our branch.  You need at
-	least one <remote>.  Specifying more than one <remote>
-	obviously means you are trying an Octopus.
+--rerere-autoupdate::
+--no-rerere-autoupdate::
+	Allow the rerere mechanism to update the index with the
+	result of auto-conflict resolution if possible.
 
-include::merge-strategies.txt[]
+--abort::
+	Abort the current conflict resolution process, and
+	try to reconstruct the pre-merge state.
++
+If there were uncommitted worktree changes present when the merge
+started, 'git merge --abort' will in some cases be unable to
+reconstruct these changes. It is therefore recommended to always
+commit or stash your changes before running 'git merge'.
++
+'git merge --abort' is equivalent to 'git reset --merge' when
+`MERGE_HEAD` is present.
+
+<commit>...::
+	Commits, usually other branch heads, to merge into our branch.
+	Specifying more than one commit will create a merge with
+	more than two parents (affectionately called an Octopus merge).
++
+If no commit is given from the command line, and if `merge.defaultToUpstream`
+configuration variable is set, merge the remote tracking branches
+that the current branch is configured to use as its upstream.
+See also the configuration section of this manual page.
 
 
-If you tried a merge which resulted in complex conflicts and
-want to start over, you can recover with 'git-reset'.
+PRE-MERGE CHECKS
+----------------
 
-CONFIGURATION
--------------
-include::merge-config.txt[]
+Before applying outside changes, you should get your own work in
+good shape and committed locally, so it will not be clobbered if
+there are conflicts.  See also linkgit:git-stash[1].
+'git pull' and 'git merge' will stop without doing anything when
+local uncommitted changes overlap with files that 'git pull'/'git
+merge' may need to update.
 
-branch.<name>.mergeoptions::
-	Sets default options for merging into branch <name>. The syntax and
-	supported options are equal to that of 'git-merge', but option values
-	containing whitespace characters are currently not supported.
+To avoid recording unrelated changes in the merge commit,
+'git pull' and 'git merge' will also abort if there are any changes
+registered in the index relative to the `HEAD` commit.  (One
+exception is when the changed index entries are in the state that
+would result from the merge already.)
 
-HOW MERGE WORKS
----------------
+If all named commits are already ancestors of `HEAD`, 'git merge'
+will exit early with the message "Already up-to-date."
 
-A merge is always between the current `HEAD` and one or more
-commits (usually, branch head or tag), and the index file must
-match the tree of `HEAD` commit (i.e. the contents of the last commit)
-when it starts out.  In other words, `git diff --cached HEAD` must
-report no changes.  (One exception is when the changed index
-entries are already in the same state that would result from
-the merge anyway.)
+FAST-FORWARD MERGE
+------------------
 
-Three kinds of merge can happen:
+Often the current branch head is an ancestor of the named commit.
+This is the most common case especially when invoked from 'git
+pull': you are tracking an upstream repository, you have committed
+no local changes, and now you want to update to a newer upstream
+revision.  In this case, a new commit is not needed to store the
+combined history; instead, the `HEAD` (along with the index) is
+updated to point at the named commit, without creating an extra
+merge commit.
 
-* The merged commit is already contained in `HEAD`. This is the
-  simplest case, called "Already up-to-date."
+This behavior can be suppressed with the `--no-ff` option.
 
-* `HEAD` is already contained in the merged commit. This is the
-  most common case especially when invoked from 'git pull':
-  you are tracking an upstream repository, have committed no local
-  changes and now you want to update to a newer upstream revision.
-  Your `HEAD` (and the index) is updated to point at the merged
-  commit, without creating an extra merge commit.  This is
-  called "Fast-forward".
+TRUE MERGE
+----------
 
-* Both the merged commit and `HEAD` are independent and must be
-  tied together by a merge commit that has both of them as its parents.
-  The rest of this section describes this "True merge" case.
+Except in a fast-forward merge (see above), the branches to be
+merged must be tied together by a merge commit that has both of them
+as its parents.
 
-The chosen merge strategy merges the two commits into a single
-new source tree.
-When things merge cleanly, this is what happens:
+A merged version reconciling the changes from all branches to be
+merged is committed, and your `HEAD`, index, and working tree are
+updated to it.  It is possible to have modifications in the working
+tree as long as they do not overlap; the update will preserve them.
 
-1. The results are updated both in the index file and in your
-   working tree;
-2. Index file is written out as a tree;
-3. The tree gets committed; and
-4. The `HEAD` pointer gets advanced.
+When it is not obvious how to reconcile the changes, the following
+happens:
 
-Because of 2., we require that the original state of the index
-file matches exactly the current `HEAD` commit; otherwise we
-will write out your local changes already registered in your
-index file along with the merge result, which is not good.
-Because 1. involves only those paths differing between your
-branch and the remote branch you are pulling from during the
-merge (which is typically a fraction of the whole tree), you can
-have local modifications in your working tree as long as they do
-not overlap with what the merge updates.
-
-When there are conflicts, the following happens:
-
-1. `HEAD` stays the same.
-
-2. Cleanly merged paths are updated both in the index file and
+1. The `HEAD` pointer stays the same.
+2. The `MERGE_HEAD` ref is set to point to the other branch head.
+3. Paths that merged cleanly are updated both in the index file and
    in your working tree.
-
-3. For conflicting paths, the index file records up to three
-   versions; stage1 stores the version from the common ancestor,
-   stage2 from `HEAD`, and stage3 from the remote branch (you
+4. For conflicting paths, the index file records up to three
+   versions: stage 1 stores the version from the common ancestor,
+   stage 2 from `HEAD`, and stage 3 from `MERGE_HEAD` (you
    can inspect the stages with `git ls-files -u`).  The working
    tree files contain the result of the "merge" program; i.e. 3-way
-   merge results with familiar conflict markers `<<< === >>>`.
-
-4. No other changes are done.  In particular, the local
+   merge results with familiar conflict markers `<<<` `===` `>>>`.
+5. No other changes are made.  In particular, the local
    modifications you had before you started merge will stay the
    same and the index entries for them stay as they were,
    i.e. matching `HEAD`.
 
+If you tried a merge which resulted in complex conflicts and
+want to start over, you can recover with `git merge --abort`.
+
 HOW CONFLICTS ARE PRESENTED
 ---------------------------
 
@@ -130,7 +181,7 @@
 however, git cannot randomly pick one side over the other, and asks you to
 resolve it by leaving what both sides did to that area.
 
-By default, git uses the same style as that is used by "merge" program
+By default, git uses the same style as the one used by the "merge" program
 from the RCS suite to present such a conflicted hunk, like this:
 
 ------------
@@ -188,28 +239,74 @@
 
  * Decide not to merge.  The only clean-ups you need are to reset
    the index file to the `HEAD` commit to reverse 2. and to clean
-   up working tree changes made by 2. and 3.; 'git-reset --hard' can
-   be used for this.
+   up working tree changes made by 2. and 3.; `git merge --abort`
+   can be used for this.
 
  * Resolve the conflicts.  Git will mark the conflicts in
    the working tree.  Edit the files into shape and
-   'git-add' them to the index.  Use 'git-commit' to seal the deal.
+   'git add' them to the index.  Use 'git commit' to seal the deal.
 
 You can work through the conflict with a number of tools:
 
- * Use a mergetool.  'git mergetool' to launch a graphical
+ * Use a mergetool.  `git mergetool` to launch a graphical
    mergetool which will work you through the merge.
 
- * Look at the diffs.  'git diff' will show a three-way diff,
-   highlighting changes from both the HEAD and remote versions.
+ * Look at the diffs.  `git diff` will show a three-way diff,
+   highlighting changes from both the `HEAD` and `MERGE_HEAD`
+   versions.
 
- * Look at the diffs on their own. 'git log --merge -p <path>'
-   will show diffs first for the HEAD version and then the
-   remote version.
+ * Look at the diffs from each branch. `git log --merge -p <path>`
+   will show diffs first for the `HEAD` version and then the
+   `MERGE_HEAD` version.
 
- * Look at the originals.  'git show :1:filename' shows the
-   common ancestor, 'git show :2:filename' shows the HEAD
-   version and 'git show :3:filename' shows the remote version.
+ * Look at the originals.  `git show :1:filename` shows the
+   common ancestor, `git show :2:filename` shows the `HEAD`
+   version, and `git show :3:filename` shows the `MERGE_HEAD`
+   version.
+
+
+EXAMPLES
+--------
+
+* Merge branches `fixes` and `enhancements` on top of
+  the current branch, making an octopus merge:
++
+------------------------------------------------
+$ git merge fixes enhancements
+------------------------------------------------
+
+* Merge branch `obsolete` into the current branch, using `ours`
+  merge strategy:
++
+------------------------------------------------
+$ git merge -s ours obsolete
+------------------------------------------------
+
+* Merge branch `maint` into the current branch, but do not make
+  a new commit automatically:
++
+------------------------------------------------
+$ git merge --no-commit maint
+------------------------------------------------
++
+This can be used when you want to include further changes to the
+merge, or want to write your own merge commit message.
++
+You should refrain from abusing this option to sneak substantial
+changes into a merge commit.  Small fixups like bumping
+release/version name would be acceptable.
+
+
+include::merge-strategies.txt[]
+
+CONFIGURATION
+-------------
+include::merge-config.txt[]
+
+branch.<name>.mergeoptions::
+	Sets default options for merging into branch <name>. The syntax and
+	supported options are the same as those of 'git merge', but option
+	values containing whitespace characters are currently not supported.
 
 SEE ALSO
 --------
@@ -220,15 +317,6 @@
 linkgit:git-add[1], linkgit:git-rm[1],
 linkgit:git-mergetool[1]
 
-Author
-------
-Written by Junio C Hamano <gitster@pobox.com>
-
-
-Documentation
---------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-mergetool--lib.txt b/Documentation/git-mergetool--lib.txt
index 78eb03f..f98a41b 100644
--- a/Documentation/git-mergetool--lib.txt
+++ b/Documentation/git-mergetool--lib.txt
@@ -1,5 +1,5 @@
-git-mergetool--lib(1)
-=====================
+git-mergetool{litdd}lib(1)
+==========================
 
 NAME
 ----
@@ -7,7 +7,8 @@
 
 SYNOPSIS
 --------
-'TOOL_MODE=(diff|merge) . "$(git --exec-path)/git-mergetool--lib"'
+[verse]
+'TOOL_MODE=(diff|merge) . "$(git --exec-path)/git-mergetool{litdd}lib"'
 
 DESCRIPTION
 -----------
@@ -16,11 +17,11 @@
 This documentation is meant for people who are studying the
 Porcelain-ish scripts and/or are writing new ones.
 
-The 'git-mergetool--lib' scriptlet is designed to be sourced (using
+The 'git-mergetool{litdd}lib' scriptlet is designed to be sourced (using
 `.`) by other shell scripts to set up functions for working
 with git merge tools.
 
-Before sourcing 'git-mergetool--lib', your script must set `TOOL_MODE`
+Before sourcing 'git-mergetool{litdd}lib', your script must set `TOOL_MODE`
 to define the operation mode for the functions listed below.
 'diff' and 'merge' are valid values.
 
@@ -41,14 +42,6 @@
 	'$MERGED', '$LOCAL', '$REMOTE', and '$BASE' must be defined
 	for use by the merge tool.
 
-Author
-------
-Written by David Aguilar <davvid@gmail.com>
-
-Documentation
---------------
-Documentation by David Aguilar and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-mergetool.txt b/Documentation/git-mergetool.txt
index 68ed6c0..2a49de7 100644
--- a/Documentation/git-mergetool.txt
+++ b/Documentation/git-mergetool.txt
@@ -7,18 +7,20 @@
 
 SYNOPSIS
 --------
-'git mergetool' [--tool=<tool>] [-y|--no-prompt|--prompt] [<file>]...
+[verse]
+'git mergetool' [--tool=<tool>] [-y|--no-prompt|--prompt] [<file>...]
 
 DESCRIPTION
 -----------
 
 Use `git mergetool` to run one of several merge utilities to resolve
-merge conflicts.  It is typically run after 'git-merge'.
+merge conflicts.  It is typically run after 'git merge'.
 
 If one or more <file> parameters are given, the merge tool program will
-be run to resolve differences on each file.  If no <file> names are
-specified, 'git-mergetool' will run the merge tool program on every file
-with merge conflicts.
+be run to resolve differences on each file (skipping those without
+conflicts).  Specifying a directory will include all unresolved files in
+that path.  If no <file> names are specified, 'git mergetool' will run
+the merge tool program on every file with merge conflicts.
 
 OPTIONS
 -------
@@ -26,26 +28,26 @@
 --tool=<tool>::
 	Use the merge resolution program specified by <tool>.
 	Valid merge tools are:
-	kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge,
-	diffuse, tortoisemerge, opendiff and araxis.
+	araxis, bc3, diffuse, ecmerge, emerge, gvimdiff, kdiff3,
+	meld, opendiff, p4merge, tkdiff, tortoisemerge, vimdiff and xxdiff.
 +
-If a merge resolution program is not specified, 'git-mergetool'
+If a merge resolution program is not specified, 'git mergetool'
 will use the configuration variable `merge.tool`.  If the
-configuration variable `merge.tool` is not set, 'git-mergetool'
+configuration variable `merge.tool` is not set, 'git mergetool'
 will pick a suitable default.
 +
 You can explicitly provide a full path to the tool by setting the
 configuration variable `mergetool.<tool>.path`. For example, you
 can configure the absolute path to kdiff3 by setting
-`mergetool.kdiff3.path`. Otherwise, 'git-mergetool' assumes the
+`mergetool.kdiff3.path`. Otherwise, 'git mergetool' assumes the
 tool is available in PATH.
 +
 Instead of running one of the known merge tool programs,
-'git-mergetool' can be customized to run an alternative program
+'git mergetool' can be customized to run an alternative program
 by specifying the command line to invoke in a configuration
 variable `mergetool.<tool>.cmd`.
 +
-When 'git-mergetool' is invoked with this tool (either through the
+When 'git mergetool' is invoked with this tool (either through the
 `-t` or `--tool` option or the `merge.tool` configuration
 variable) the configured command line will be invoked with `$BASE`
 set to the name of a temporary file containing the common base for
@@ -59,7 +61,7 @@
 If the custom merge tool correctly indicates the success of a
 merge resolution with its exit code, then the configuration
 variable `mergetool.<tool>.trustExitCode` can be set to `true`.
-Otherwise, 'git-mergetool' will prompt the user to indicate the
+Otherwise, 'git mergetool' will prompt the user to indicate the
 success of the resolution after the custom tool has exited.
 
 -y::
@@ -72,13 +74,15 @@
 	This is the default behaviour; the option is provided to
 	override any configuration settings.
 
-Author
-------
-Written by Theodore Y Ts'o <tytso@mit.edu>
+TEMPORARY FILES
+---------------
+`git mergetool` creates `*.orig` backup files while resolving merges.
+These are safe to remove once a file has been merged and its
+`git mergetool` session has completed.
 
-Documentation
---------------
-Documentation by Theodore Y Ts'o.
+Setting the `mergetool.keepBackup` configuration variable to `false`
+causes `git mergetool` to automatically remove the backup as files
+are successfully merged.
 
 GIT
 ---
diff --git a/Documentation/git-mktag.txt b/Documentation/git-mktag.txt
index 8bcc114..65e167a 100644
--- a/Documentation/git-mktag.txt
+++ b/Documentation/git-mktag.txt
@@ -8,6 +8,7 @@
 
 SYNOPSIS
 --------
+[verse]
 'git mktag' < signature_file
 
 DESCRIPTION
@@ -32,15 +33,6 @@
 message part may contain a signature that git itself doesn't
 care about, but that can be verified with gpg.
 
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-mktree.txt b/Documentation/git-mktree.txt
index 81e3326..5c6ebdf 100644
--- a/Documentation/git-mktree.txt
+++ b/Documentation/git-mktree.txt
@@ -8,6 +8,7 @@
 
 SYNOPSIS
 --------
+[verse]
 'git mktree' [-z] [--missing] [--batch]
 
 DESCRIPTION
@@ -34,14 +35,6 @@
 	optional.  Note - if the '-z' option is used, lines are terminated
 	with NUL.
 
-Author
-------
-Written by Junio C Hamano <gitster@pobox.com>
-
-Documentation
---------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-mv.txt b/Documentation/git-mv.txt
index 9c56602..e3c8448 100644
--- a/Documentation/git-mv.txt
+++ b/Documentation/git-mv.txt
@@ -8,14 +8,15 @@
 
 SYNOPSIS
 --------
+[verse]
 'git mv' <options>... <args>...
 
 DESCRIPTION
 -----------
 This script is used to move or rename a file, directory or symlink.
 
- git mv [-f] [-n] <source> <destination>
- git mv [-f] [-n] [-k] <source> ... <destination directory>
+ git mv [-v] [-f] [-n] [-k] <source> <destination>
+ git mv [-v] [-f] [-n] [-k] <source> ... <destination directory>
 
 In the first form, it renames <source>, which must exist and be either
 a file, symlink or directory, to <destination>.
@@ -28,6 +29,7 @@
 OPTIONS
 -------
 -f::
+--force::
 	Force renaming or moving of a file even if the target exists
 -k::
         Skip move or rename actions which would lead to an error
@@ -38,16 +40,9 @@
 --dry-run::
 	Do nothing; only show what would happen
 
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-Rewritten by Ryan Anderson <ryan@michonline.com>
-Move functionality added by Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+-v::
+--verbose::
+	Report the names of files as they are moved.
 
 GIT
 ---
diff --git a/Documentation/git-name-rev.txt b/Documentation/git-name-rev.txt
index 7ca8a7b..ad1d146 100644
--- a/Documentation/git-name-rev.txt
+++ b/Documentation/git-name-rev.txt
@@ -15,7 +15,7 @@
 DESCRIPTION
 -----------
 Finds symbolic names suitable for human digestion for revisions given in any
-format parsable by 'git-rev-parse'.
+format parsable by 'git rev-parse'.
 
 
 OPTIONS
@@ -55,7 +55,7 @@
 Of course, you look into the commit, but that only tells you what happened, but
 not the context.
 
-Enter 'git-name-rev':
+Enter 'git name-rev':
 
 ------------
 % git name-rev 33db5f4d9027a10e477ccf054b2c1ab94f74c85a
@@ -70,15 +70,6 @@
 % git log | git name-rev --stdin
 ------------
 
-
-Author
-------
-Written by Johannes Schindelin <Johannes.Schindelin@gmx.de>
-
-Documentation
---------------
-Documentation by Johannes Schindelin.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt
new file mode 100644
index 0000000..b95aafa
--- /dev/null
+++ b/Documentation/git-notes.txt
@@ -0,0 +1,386 @@
+git-notes(1)
+============
+
+NAME
+----
+git-notes - Add or inspect object notes
+
+SYNOPSIS
+--------
+[verse]
+'git notes' [list [<object>]]
+'git notes' add [-f] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
+'git notes' copy [-f] ( --stdin | <from-object> <to-object> )
+'git notes' append [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
+'git notes' edit [<object>]
+'git notes' show [<object>]
+'git notes' merge [-v | -q] [-s <strategy> ] <notes_ref>
+'git notes' merge --commit [-v | -q]
+'git notes' merge --abort [-v | -q]
+'git notes' remove [--ignore-missing] [--stdin] [<object>...]
+'git notes' prune [-n | -v]
+'git notes' get-ref
+
+
+DESCRIPTION
+-----------
+Adds, removes, or reads notes attached to objects, without touching
+the objects themselves.
+
+By default, notes are saved to and read from `refs/notes/commits`, but
+this default can be overridden.  See the OPTIONS, CONFIGURATION, and
+ENVIRONMENT sections below.  If this ref does not exist, it will be
+quietly created when it is first needed to store a note.
+
+A typical use of notes is to supplement a commit message without
+changing the commit itself. Notes can be shown by 'git log' along with
+the original commit message. To distinguish these notes from the
+message stored in the commit object, the notes are indented like the
+message, after an unindented line saying "Notes (<refname>):" (or
+"Notes:" for `refs/notes/commits`).
+
+To change which notes are shown by 'git log', see the
+"notes.displayRef" configuration in linkgit:git-log[1].
+
+See the "notes.rewrite.<command>" configuration for a way to carry
+notes across commands that rewrite commits.
+
+
+SUBCOMMANDS
+-----------
+
+list::
+	List the notes object for a given object. If no object is
+	given, show a list of all note objects and the objects they
+	annotate (in the format "<note object> <annotated object>").
+	This is the default subcommand if no subcommand is given.
+
+add::
+	Add notes for a given object (defaults to HEAD). Abort if the
+	object already has notes (use `-f` to overwrite existing notes).
+	However, if you're using `add` interactively (using an editor
+	to supply the notes contents), then - instead of aborting -
+	the existing notes will be opened in the editor (like the `edit`
+	subcommand).
+
+copy::
+	Copy the notes for the first object onto the second object.
+	Abort if the second object already has notes, or if the first
+	object has none (use -f to overwrite existing notes to the
+	second object). This subcommand is equivalent to:
+	`git notes add [-f] -C $(git notes list <from-object>) <to-object>`
++
+In `--stdin` mode, take lines in the format
++
+----------
+<from-object> SP <to-object> [ SP <rest> ] LF
+----------
++
+on standard input, and copy the notes from each <from-object> to its
+corresponding <to-object>.  (The optional `<rest>` is ignored so that
+the command can read the input given to the `post-rewrite` hook.)
+
+append::
+	Append to the notes of an existing object (defaults to HEAD).
+	Creates a new notes object if needed.
+
+edit::
+	Edit the notes for a given object (defaults to HEAD).
+
+show::
+	Show the notes for a given object (defaults to HEAD).
+
+merge::
+	Merge the given notes ref into the current notes ref.
+	This will try to merge the changes made by the given
+	notes ref (called "remote") since the merge-base (if
+	any) into the current notes ref (called "local").
++
+If conflicts arise and a strategy for automatically resolving
+conflicting notes (see the -s/--strategy option) is not given,
+the "manual" resolver is used. This resolver checks out the
+conflicting notes in a special worktree (`.git/NOTES_MERGE_WORKTREE`),
+and instructs the user to manually resolve the conflicts there.
+When done, the user can either finalize the merge with
+'git notes merge --commit', or abort the merge with
+'git notes merge --abort'.
+
+remove::
+	Remove the notes for given objects (defaults to HEAD). When
+	giving zero or one object from the command line, this is
+	equivalent to specifying an empty note message to
+	the `edit` subcommand.
+
+prune::
+	Remove all notes for non-existing/unreachable objects.
+
+get-ref::
+	Print the current notes ref. This provides an easy way to
+	retrieve the current notes ref (e.g. from scripts).
+
+OPTIONS
+-------
+-f::
+--force::
+	When adding notes to an object that already has notes,
+	overwrite the existing notes (instead of aborting).
+
+-m <msg>::
+--message=<msg>::
+	Use the given note message (instead of prompting).
+	If multiple `-m` options are given, their values
+	are concatenated as separate paragraphs.
+	Lines starting with `#` and empty lines other than a
+	single line between paragraphs will be stripped out.
+
+-F <file>::
+--file=<file>::
+	Take the note message from the given file.  Use '-' to
+	read the note message from the standard input.
+	Lines starting with `#` and empty lines other than a
+	single line between paragraphs will be stripped out.
+
+-C <object>::
+--reuse-message=<object>::
+	Take the given blob object (for	example, another note) as the
+	note message. (Use `git notes copy <object>` instead to
+	copy notes between objects.)
+
+-c <object>::
+--reedit-message=<object>::
+	Like '-C', but with '-c' the editor is invoked, so that
+	the user can further edit the note message.
+
+--ref <ref>::
+	Manipulate the notes tree in <ref>.  This overrides
+	'GIT_NOTES_REF' and the "core.notesRef" configuration.  The ref
+	is taken to be in `refs/notes/` if it is not qualified.
+
+--ignore-missing::
+	Do not consider it an error to request removing notes from an
+	object that does not have notes attached to it.
+
+--stdin::
+	Also read the object names to remove notes from from the standard
+	input (there is no reason you cannot combine this with object
+	names from the command line).
+
+-n::
+--dry-run::
+	Do not remove anything; just report the object names whose notes
+	would be removed.
+
+-s <strategy>::
+--strategy=<strategy>::
+	When merging notes, resolve notes conflicts using the given
+	strategy. The following strategies are recognized: "manual"
+	(default), "ours", "theirs", "union" and "cat_sort_uniq".
+	See the "NOTES MERGE STRATEGIES" section below for more
+	information on each notes merge strategy.
+
+--commit::
+	Finalize an in-progress 'git notes merge'. Use this option
+	when you have resolved the conflicts that 'git notes merge'
+	stored in .git/NOTES_MERGE_WORKTREE. This amends the partial
+	merge commit created by 'git notes merge' (stored in
+	.git/NOTES_MERGE_PARTIAL) by adding the notes in
+	.git/NOTES_MERGE_WORKTREE. The notes ref stored in the
+	.git/NOTES_MERGE_REF symref is updated to the resulting commit.
+
+--abort::
+	Abort/reset a in-progress 'git notes merge', i.e. a notes merge
+	with conflicts. This simply removes all files related to the
+	notes merge.
+
+-q::
+--quiet::
+	When merging notes, operate quietly.
+
+-v::
+--verbose::
+	When merging notes, be more verbose.
+	When pruning notes, report all object names whose notes are
+	removed.
+
+
+DISCUSSION
+----------
+
+Commit notes are blobs containing extra information about an object
+(usually information to supplement a commit's message).  These blobs
+are taken from notes refs.  A notes ref is usually a branch which
+contains "files" whose paths are the object names for the objects
+they describe, with some directory separators included for performance
+reasons footnote:[Permitted pathnames have the form
+'ab'`/`'cd'`/`'ef'`/`'...'`/`'abcdef...': a sequence of directory
+names of two hexadecimal digits each followed by a filename with the
+rest of the object ID.].
+
+Every notes change creates a new commit at the specified notes ref.
+You can therefore inspect the history of the notes by invoking, e.g.,
+`git log -p notes/commits`.  Currently the commit message only records
+which operation triggered the update, and the commit authorship is
+determined according to the usual rules (see linkgit:git-commit[1]).
+These details may change in the future.
+
+It is also permitted for a notes ref to point directly to a tree
+object, in which case the history of the notes can be read with
+`git log -p -g <refname>`.
+
+
+NOTES MERGE STRATEGIES
+----------------------
+
+The default notes merge strategy is "manual", which checks out
+conflicting notes in a special work tree for resolving notes conflicts
+(`.git/NOTES_MERGE_WORKTREE`), and instructs the user to resolve the
+conflicts in that work tree.
+When done, the user can either finalize the merge with
+'git notes merge --commit', or abort the merge with
+'git notes merge --abort'.
+
+"ours" automatically resolves conflicting notes in favor of the local
+version (i.e. the current notes ref).
+
+"theirs" automatically resolves notes conflicts in favor of the remote
+version (i.e. the given notes ref being merged into the current notes
+ref).
+
+"union" automatically resolves notes conflicts by concatenating the
+local and remote versions.
+
+"cat_sort_uniq" is similar to "union", but in addition to concatenating
+the local and remote versions, this strategy also sorts the resulting
+lines, and removes duplicate lines from the result. This is equivalent
+to applying the "cat | sort | uniq" shell pipeline to the local and
+remote versions. This strategy is useful if the notes follow a line-based
+format where one wants to avoid duplicated lines in the merge result.
+Note that if either the local or remote version contain duplicate lines
+prior to the merge, these will also be removed by this notes merge
+strategy.
+
+
+EXAMPLES
+--------
+
+You can use notes to add annotations with information that was not
+available at the time a commit was written.
+
+------------
+$ git notes add -m 'Tested-by: Johannes Sixt <j6t@kdbg.org>' 72a144e2
+$ git show -s 72a144e
+[...]
+    Signed-off-by: Junio C Hamano <gitster@pobox.com>
+
+Notes:
+    Tested-by: Johannes Sixt <j6t@kdbg.org>
+------------
+
+In principle, a note is a regular Git blob, and any kind of
+(non-)format is accepted.  You can binary-safely create notes from
+arbitrary files using 'git hash-object':
+
+------------
+$ cc *.c
+$ blob=$(git hash-object -w a.out)
+$ git notes --ref=built add -C "$blob" HEAD
+------------
+
+(You cannot simply use `git notes --ref=built add -F a.out HEAD`
+because that is not binary-safe.)
+Of course, it doesn't make much sense to display non-text-format notes
+with 'git log', so if you use such notes, you'll probably need to write
+some special-purpose tools to do something useful with them.
+
+
+CONFIGURATION
+-------------
+
+core.notesRef::
+	Notes ref to read and manipulate instead of
+	`refs/notes/commits`.  Must be an unabbreviated ref name.
+	This setting can be overridden through the environment and
+	command line.
+
+notes.displayRef::
+	Which ref (or refs, if a glob or specified more than once), in
+	addition to the default set by `core.notesRef` or
+	'GIT_NOTES_REF', to read notes from when showing commit
+	messages with the 'git log' family of commands.
+	This setting can be overridden on the command line or by the
+	'GIT_NOTES_DISPLAY_REF' environment variable.
+	See linkgit:git-log[1].
+
+notes.rewrite.<command>::
+	When rewriting commits with <command> (currently `amend` or
+	`rebase`), if this variable is `false`, git will not copy
+	notes from the original to the rewritten commit.  Defaults to
+	`true`.  See also "`notes.rewriteRef`" below.
++
+This setting can be overridden by the 'GIT_NOTES_REWRITE_REF'
+environment variable.
+
+notes.rewriteMode::
+	When copying notes during a rewrite, what to do if the target
+	commit already has a note.  Must be one of `overwrite`,
+	`concatenate`, and `ignore`.  Defaults to `concatenate`.
++
+This setting can be overridden with the `GIT_NOTES_REWRITE_MODE`
+environment variable.
+
+notes.rewriteRef::
+	When copying notes during a rewrite, specifies the (fully
+	qualified) ref whose notes should be copied.  May be a glob,
+	in which case notes in all matching refs will be copied.  You
+	may also specify this configuration several times.
++
+Does not have a default value; you must configure this variable to
+enable note rewriting.
++
+Can be overridden with the 'GIT_NOTES_REWRITE_REF' environment variable.
+
+
+ENVIRONMENT
+-----------
+
+'GIT_NOTES_REF'::
+	Which ref to manipulate notes from, instead of `refs/notes/commits`.
+	This overrides the `core.notesRef` setting.
+
+'GIT_NOTES_DISPLAY_REF'::
+	Colon-delimited list of refs or globs indicating which refs,
+	in addition to the default from `core.notesRef` or
+	'GIT_NOTES_REF', to read notes from when showing commit
+	messages.
+	This overrides the `notes.displayRef` setting.
++
+A warning will be issued for refs that do not exist, but a glob that
+does not match any refs is silently ignored.
+
+'GIT_NOTES_REWRITE_MODE'::
+	When copying notes during a rewrite, what to do if the target
+	commit already has a note.
+	Must be one of `overwrite`, `concatenate`, and `ignore`.
+	This overrides the `core.rewriteMode` setting.
+
+'GIT_NOTES_REWRITE_REF'::
+	When rewriting commits, which notes to copy from the original
+	to the rewritten commit.  Must be a colon-delimited list of
+	refs or globs.
++
+If not set in the environment, the list of notes to copy depends
+on the `notes.rewrite.<command>` and `notes.rewriteRef` settings.
+
+
+Author
+------
+Written by Johannes Schindelin <johannes.schindelin@gmx.de> and
+Johan Herland <johan@herland.net>
+
+Documentation
+-------------
+Documentation by Johannes Schindelin and Johan Herland
+
+GIT
+---
+Part of the linkgit:git[7] suite
diff --git a/Documentation/git-p4.txt b/Documentation/git-p4.txt
new file mode 100644
index 0000000..8228f33
--- /dev/null
+++ b/Documentation/git-p4.txt
@@ -0,0 +1,533 @@
+git-p4(1)
+=========
+
+NAME
+----
+git-p4 - Import from and submit to Perforce repositories
+
+
+SYNOPSIS
+--------
+[verse]
+'git p4 clone' [<sync options>] [<clone options>] <p4 depot path>...
+'git p4 sync' [<sync options>] [<p4 depot path>...]
+'git p4 rebase'
+'git p4 submit' [<submit options>] [<master branch name>]
+
+
+DESCRIPTION
+-----------
+This command provides a way to interact with p4 repositories
+using git.
+
+Create a new git repository from an existing p4 repository using
+'git p4 clone', giving it one or more p4 depot paths.  Incorporate
+new commits from p4 changes with 'git p4 sync'.  The 'sync' command
+is also used to include new branches from other p4 depot paths.
+Submit git changes back to p4 using 'git p4 submit'.  The command
+'git p4 rebase' does a sync plus rebases the current branch onto
+the updated p4 remote branch.
+
+
+EXAMPLE
+-------
+* Clone a repository:
++
+------------
+$ git p4 clone //depot/path/project
+------------
+
+* Do some work in the newly created git repository:
++
+------------
+$ cd project
+$ vi foo.h
+$ git commit -a -m "edited foo.h"
+------------
+
+* Update the git repository with recent changes from p4, rebasing your
+  work on top:
++
+------------
+$ git p4 rebase
+------------
+
+* Submit your commits back to p4:
++
+------------
+$ git p4 submit
+------------
+
+
+COMMANDS
+--------
+
+Clone
+~~~~~
+Generally, 'git p4 clone' is used to create a new git directory
+from an existing p4 repository:
+------------
+$ git p4 clone //depot/path/project
+------------
+This:
+
+1.   Creates an empty git repository in a subdirectory called 'project'.
++
+2.   Imports the full contents of the head revision from the given p4
+depot path into a single commit in the git branch 'refs/remotes/p4/master'.
++
+3.   Creates a local branch, 'master' from this remote and checks it out.
+
+To reproduce the entire p4 history in git, use the '@all' modifier on
+the depot path:
+------------
+$ git p4 clone //depot/path/project@all
+------------
+
+
+Sync
+~~~~
+As development continues in the p4 repository, those changes can
+be included in the git repository using:
+------------
+$ git p4 sync
+------------
+This command finds new changes in p4 and imports them as git commits.
+
+P4 repositories can be added to an existing git repository using
+'git p4 sync' too:
+------------
+$ mkdir repo-git
+$ cd repo-git
+$ git init
+$ git p4 sync //path/in/your/perforce/depot
+------------
+This imports the specified depot into
+'refs/remotes/p4/master' in an existing git repository.  The
+'--branch' option can be used to specify a different branch to
+be used for the p4 content.
+
+If a git repository includes branches 'refs/remotes/origin/p4', these
+will be fetched and consulted first during a 'git p4 sync'.  Since
+importing directly from p4 is considerably slower than pulling changes
+from a git remote, this can be useful in a multi-developer environment.
+
+
+Rebase
+~~~~~~
+A common working pattern is to fetch the latest changes from the p4 depot
+and merge them with local uncommitted changes.  Often, the p4 repository
+is the ultimate location for all code, thus a rebase workflow makes
+sense.  This command does 'git p4 sync' followed by 'git rebase' to move
+local commits on top of updated p4 changes.
+------------
+$ git p4 rebase
+------------
+
+
+Submit
+~~~~~~
+Submitting changes from a git repository back to the p4 repository
+requires a separate p4 client workspace.  This should be specified
+using the 'P4CLIENT' environment variable or the git configuration
+variable 'git-p4.client'.  The p4 client must exist, but the client root
+will be created and populated if it does not already exist.
+
+To submit all changes that are in the current git branch but not in
+the 'p4/master' branch, use:
+------------
+$ git p4 submit
+------------
+
+To specify a branch other than the current one, use:
+------------
+$ git p4 submit topicbranch
+------------
+
+The upstream reference is generally 'refs/remotes/p4/master', but can
+be overridden using the '--origin=' command-line option.
+
+The p4 changes will be created as the user invoking 'git p4 submit'. The
+'--preserve-user' option will cause ownership to be modified
+according to the author of the git commit.  This option requires admin
+privileges in p4, which can be granted using 'p4 protect'.
+
+
+OPTIONS
+-------
+
+General options
+~~~~~~~~~~~~~~~
+All commands except clone accept these options.
+
+--git-dir <dir>::
+	Set the 'GIT_DIR' environment variable.  See linkgit:git[1].
+
+--verbose::
+	Provide more progress information.
+
+Sync options
+~~~~~~~~~~~~
+These options can be used in the initial 'clone' as well as in
+subsequent 'sync' operations.
+
+--branch <branch>::
+	Import changes into given branch.  If the branch starts with
+	'refs/', it will be used as is, otherwise the path 'refs/heads/'
+	will be prepended.  The default branch is 'master'.  If used
+	with an initial clone, no HEAD will be checked out.
++
+This example imports a new remote "p4/proj2" into an existing
+git repository:
++
+----
+    $ git init
+    $ git p4 sync --branch=refs/remotes/p4/proj2 //depot/proj2
+----
+
+--detect-branches::
+	Use the branch detection algorithm to find new paths in p4.  It is
+	documented below in "BRANCH DETECTION".
+
+--changesfile <file>::
+	Import exactly the p4 change numbers listed in 'file', one per
+	line.  Normally, 'git p4' inspects the current p4 repository
+	state and detects the changes it should import.
+
+--silent::
+	Do not print any progress information.
+
+--detect-labels::
+	Query p4 for labels associated with the depot paths, and add
+	them as tags in git. Limited usefulness as only imports labels
+	associated with new changelists. Deprecated.
+
+--import-labels::
+	Import labels from p4 into git.
+
+--import-local::
+	By default, p4 branches are stored in 'refs/remotes/p4/',
+	where they will be treated as remote-tracking branches by
+	linkgit:git-branch[1] and other commands.  This option instead
+	puts p4 branches in 'refs/heads/p4/'.  Note that future
+	sync operations must specify '--import-local' as well so that
+	they can find the p4 branches in refs/heads.
+
+--max-changes <n>::
+	Limit the number of imported changes to 'n'.  Useful to
+	limit the amount of history when using the '@all' p4 revision
+	specifier.
+
+--keep-path::
+	The mapping of file names from the p4 depot path to git, by
+	default, involves removing the entire depot path.  With this
+	option, the full p4 depot path is retained in git.  For example,
+	path '//depot/main/foo/bar.c', when imported from
+	'//depot/main/', becomes 'foo/bar.c'.  With '--keep-path', the
+	git path is instead 'depot/main/foo/bar.c'.
+
+--use-client-spec::
+	Use a client spec to find the list of interesting files in p4.
+	See the "CLIENT SPEC" section below.
+
+Clone options
+~~~~~~~~~~~~~
+These options can be used in an initial 'clone', along with the 'sync'
+options described above.
+
+--destination <directory>::
+	Where to create the git repository.  If not provided, the last
+	component in the p4 depot path is used to create a new
+	directory.
+
+--bare::
+	Perform a bare clone.  See linkgit:git-clone[1].
+
+-/ <path>::
+	Exclude selected depot paths when cloning.
+
+Submit options
+~~~~~~~~~~~~~~
+These options can be used to modify 'git p4 submit' behavior.
+
+--origin <commit>::
+	Upstream location from which commits are identified to submit to
+	p4.  By default, this is the most recent p4 commit reachable
+	from 'HEAD'.
+
+-M::
+	Detect renames.  See linkgit:git-diff[1].  Renames will be
+	represented in p4 using explicit 'move' operations.  There
+	is no corresponding option to detect copies, but there are
+	variables for both moves and copies.
+
+--preserve-user::
+	Re-author p4 changes before submitting to p4.  This option
+	requires p4 admin privileges.
+
+--export-labels::
+	Export tags from git as p4 labels. Tags found in git are applied
+	to the perforce working directory.
+
+Rebase options
+~~~~~~~~~~~~~~
+These options can be used to modify 'git p4 rebase' behavior.
+
+--import-labels::
+	Import p4 labels.
+
+DEPOT PATH SYNTAX
+-----------------
+The p4 depot path argument to 'git p4 sync' and 'git p4 clone' can
+be one or more space-separated p4 depot paths, with an optional
+p4 revision specifier on the end:
+
+"//depot/my/project"::
+    Import one commit with all files in the '#head' change under that tree.
+
+"//depot/my/project@all"::
+    Import one commit for each change in the history of that depot path.
+
+"//depot/my/project@1,6"::
+    Import only changes 1 through 6.
+
+"//depot/proj1@all //depot/proj2@all"::
+    Import all changes from both named depot paths into a single
+    repository.  Only files below these directories are included.
+    There is not a subdirectory in git for each "proj1" and "proj2".
+    You must use the '--destination' option when specifying more
+    than one depot path.  The revision specifier must be specified
+    identically on each depot path.  If there are files in the
+    depot paths with the same name, the path with the most recently
+    updated version of the file is the one that appears in git.
+
+See 'p4 help revisions' for the full syntax of p4 revision specifiers.
+
+
+CLIENT SPEC
+-----------
+The p4 client specification is maintained with the 'p4 client' command
+and contains among other fields, a View that specifies how the depot
+is mapped into the client repository.  The 'clone' and 'sync' commands
+can consult the client spec when given the '--use-client-spec' option or
+when the useClientSpec variable is true.  After 'git p4 clone', the
+useClientSpec variable is automatically set in the repository
+configuration file.  This allows future 'git p4 submit' commands to
+work properly; the submit command looks only at the variable and does
+not have a command-line option.
+
+The full syntax for a p4 view is documented in 'p4 help views'.  'Git p4'
+knows only a subset of the view syntax.  It understands multi-line
+mappings, overlays with '+', exclusions with '-' and double-quotes
+around whitespace.  Of the possible wildcards, 'git p4' only handles
+'...', and only when it is at the end of the path.  'Git p4' will complain
+if it encounters an unhandled wildcard.
+
+Bugs in the implementation of overlap mappings exist.  If multiple depot
+paths map through overlays to the same location in the repository,
+'git p4' can choose the wrong one.  This is hard to solve without
+dedicating a client spec just for 'git p4'.
+
+The name of the client can be given to 'git p4' in multiple ways.  The
+variable 'git-p4.client' takes precedence if it exists.  Otherwise,
+normal p4 mechanisms of determining the client are used:  environment
+variable P4CLIENT, a file referenced by P4CONFIG, or the local host name.
+
+
+BRANCH DETECTION
+----------------
+P4 does not have the same concept of a branch as git.  Instead,
+p4 organizes its content as a directory tree, where by convention
+different logical branches are in different locations in the tree.
+The 'p4 branch' command is used to maintain mappings between
+different areas in the tree, and indicate related content.  'git p4'
+can use these mappings to determine branch relationships.
+
+If you have a repository where all the branches of interest exist as
+subdirectories of a single depot path, you can use '--detect-branches'
+when cloning or syncing to have 'git p4' automatically find
+subdirectories in p4, and to generate these as branches in git.
+
+For example, if the P4 repository structure is:
+----
+//depot/main/...
+//depot/branch1/...
+----
+
+And "p4 branch -o branch1" shows a View line that looks like:
+----
+//depot/main/... //depot/branch1/...
+----
+
+Then this 'git p4 clone' command:
+----
+git p4 clone --detect-branches //depot@all
+----
+produces a separate branch in 'refs/remotes/p4/' for //depot/main,
+called 'master', and one for //depot/branch1 called 'depot/branch1'.
+
+However, it is not necessary to create branches in p4 to be able to use
+them like branches.  Because it is difficult to infer branch
+relationships automatically, a git configuration setting
+'git-p4.branchList' can be used to explicitly identify branch
+relationships.  It is a list of "source:destination" pairs, like a
+simple p4 branch specification, where the "source" and "destination" are
+the path elements in the p4 repository.  The example above relied on the
+presence of the p4 branch.  Without p4 branches, the same result will
+occur with:
+----
+git config git-p4.branchList main:branch1
+git p4 clone --detect-branches //depot@all
+----
+
+
+PERFORMANCE
+-----------
+The fast-import mechanism used by 'git p4' creates one pack file for
+each invocation of 'git p4 sync'.  Normally, git garbage compression
+(linkgit:git-gc[1]) automatically compresses these to fewer pack files,
+but explicit invocation of 'git repack -adf' may improve performance.
+
+
+CONFIGURATION VARIABLES
+-----------------------
+The following config settings can be used to modify 'git p4' behavior.
+They all are in the 'git-p4' section.
+
+General variables
+~~~~~~~~~~~~~~~~~
+git-p4.user::
+	User specified as an option to all p4 commands, with '-u <user>'.
+	The environment variable 'P4USER' can be used instead.
+
+git-p4.password::
+	Password specified as an option to all p4 commands, with
+	'-P <password>'.
+	The environment variable 'P4PASS' can be used instead.
+
+git-p4.port::
+	Port specified as an option to all p4 commands, with
+	'-p <port>'.
+	The environment variable 'P4PORT' can be used instead.
+
+git-p4.host::
+	Host specified as an option to all p4 commands, with
+	'-h <host>'.
+	The environment variable 'P4HOST' can be used instead.
+
+git-p4.client::
+	Client specified as an option to all p4 commands, with
+	'-c <client>', including the client spec.
+
+Clone and sync variables
+~~~~~~~~~~~~~~~~~~~~~~~~
+git-p4.syncFromOrigin::
+	Because importing commits from other git repositories is much faster
+	than importing them from p4, a mechanism exists to find p4 changes
+	first in git remotes.  If branches exist under 'refs/remote/origin/p4',
+	those will be fetched and used when syncing from p4.  This
+	variable can be set to 'false' to disable this behavior.
+
+git-p4.branchUser::
+	One phase in branch detection involves looking at p4 branches
+	to find new ones to import.  By default, all branches are
+	inspected.  This option limits the search to just those owned
+	by the single user named in the variable.
+
+git-p4.branchList::
+	List of branches to be imported when branch detection is
+	enabled.  Each entry should be a pair of branch names separated
+	by a colon (:).  This example declares that both branchA and
+	branchB were created from main:
++
+-------------
+git config       git-p4.branchList main:branchA
+git config --add git-p4.branchList main:branchB
+-------------
+
+git-p4.ignoredP4Labels::
+	List of p4 labels to ignore. This is built automatically as
+	unimportable labels are discovered.
+
+git-p4.importLabels::
+	Import p4 labels into git, as per --import-labels.
+
+git-p4.labelImportRegexp::
+	Only p4 labels matching this regular expression will be imported. The
+	default value is '[a-zA-Z0-9_\-.]+$'.
+
+git-p4.useClientSpec::
+	Specify that the p4 client spec should be used to identify p4
+	depot paths of interest.  This is equivalent to specifying the
+	option '--use-client-spec'.  See the "CLIENT SPEC" section above.
+	This variable is a boolean, not the name of a p4 client.
+
+Submit variables
+~~~~~~~~~~~~~~~~
+git-p4.detectRenames::
+	Detect renames.  See linkgit:git-diff[1].  This can be true,
+	false, or a score as expected by 'git diff -M'.
+
+git-p4.detectCopies::
+	Detect copies.  See linkgit:git-diff[1].  This can be true,
+	false, or a score as expected by 'git diff -C'.
+
+git-p4.detectCopiesHarder::
+	Detect copies harder.  See linkgit:git-diff[1].  A boolean.
+
+git-p4.preserveUser::
+	On submit, re-author changes to reflect the git author,
+	regardless of who invokes 'git p4 submit'.
+
+git-p4.allowMissingP4Users::
+	When 'preserveUser' is true, 'git p4' normally dies if it
+	cannot find an author in the p4 user map.  This setting
+	submits the change regardless.
+
+git-p4.skipSubmitEdit::
+	The submit process invokes the editor before each p4 change
+	is submitted.  If this setting is true, though, the editing
+	step is skipped.
+
+git-p4.skipSubmitEditCheck::
+	After editing the p4 change message, 'git p4' makes sure that
+	the description really was changed by looking at the file
+	modification time.  This option disables that test.
+
+git-p4.allowSubmit::
+	By default, any branch can be used as the source for a 'git p4
+	submit' operation.  This configuration variable, if set, permits only
+	the named branches to be used as submit sources.  Branch names
+	must be the short names (no "refs/heads/"), and should be
+	separated by commas (","), with no spaces.
+
+git-p4.skipUserNameCheck::
+	If the user running 'git p4 submit' does not exist in the p4
+	user map, 'git p4' exits.  This option can be used to force
+	submission regardless.
+
+git-p4.attemptRCSCleanup::
+	If enabled, 'git p4 submit' will attempt to cleanup RCS keywords
+	($Header$, etc). These would otherwise cause merge conflicts and prevent
+	the submit going ahead. This option should be considered experimental at
+	present.
+
+git-p4.exportLabels::
+	Export git tags to p4 labels, as per --export-labels.
+
+git-p4.labelExportRegexp::
+	Only p4 labels matching this regular expression will be exported. The
+	default value is '[a-zA-Z0-9_\-.]+$'.
+
+IMPLEMENTATION DETAILS
+----------------------
+* Changesets from p4 are imported using git fast-import.
+* Cloning or syncing does not require a p4 client; file contents are
+  collected using 'p4 print'.
+* Submitting requires a p4 client, which is not in the same location
+  as the git repository.  Patches are applied, one at a time, to
+  this p4 client and submitted from there.
+* Each commit imported by 'git p4' has a line at the end of the log
+  message indicating the p4 depot location and change number.  This
+  line is used by later 'git p4 sync' operations to know which p4
+  changes are new.
diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt
index 2e49929..20c8551 100644
--- a/Documentation/git-pack-objects.txt
+++ b/Documentation/git-pack-objects.txt
@@ -9,9 +9,10 @@
 SYNOPSIS
 --------
 [verse]
-'git pack-objects' [-q] [--no-reuse-delta] [--delta-base-offset] [--non-empty]
-	[--local] [--incremental] [--window=N] [--depth=N] [--all-progress]
-	[--revs [--unpacked | --all]*] [--stdout | base-name]
+'git pack-objects' [-q | --progress | --all-progress] [--all-progress-implied]
+	[--no-reuse-delta] [--delta-base-offset] [--non-empty]
+	[--local] [--incremental] [--window=<n>] [--depth=<n>]
+	[--revs [--unpacked | --all]] [--stdout | base-name]
 	[--keep-true-parents] < object-list
 
 
@@ -20,27 +21,28 @@
 Reads list of objects from the standard input, and writes a packed
 archive with specified base-name, or to the standard output.
 
-A packed archive is an efficient way to transfer set of objects
-between two repositories, and also is an archival format which
-is efficient to access.  The packed archive format (.pack) is
-designed to be self contained so that it can be unpacked without
-any further information, but for fast, random access to the objects
-in the pack, a pack index file (.idx) will be generated.
+A packed archive is an efficient way to transfer a set of objects
+between two repositories as well as an access efficient archival
+format.  In a packed archive, an object is either stored as a
+compressed whole or as a difference from some other object.
+The latter is often called a delta.
 
-Placing both in the pack/ subdirectory of $GIT_OBJECT_DIRECTORY (or
+The packed archive format (.pack) is designed to be self-contained
+so that it can be unpacked without any further information. Therefore,
+each object that a delta depends upon must be present within the pack.
+
+A pack index file (.idx) is generated for fast, random access to the
+objects in the pack. Placing both the index file (.idx) and the packed
+archive (.pack) in the pack/ subdirectory of $GIT_OBJECT_DIRECTORY (or
 any of the directories on $GIT_ALTERNATE_OBJECT_DIRECTORIES)
-enables git to read from such an archive.
+enables git to read from the pack archive.
 
-The 'git-unpack-objects' command can read the packed archive and
+The 'git unpack-objects' command can read the packed archive and
 expand the objects contained in the pack into "one-file
 one-object" format; this is typically done by the smart-pull
 commands when a pack is created on-the-fly for efficient network
 transport by their peers.
 
-In a packed archive, an object is either stored as a compressed
-whole, or as a difference from some other object.  The latter is
-often called a delta.
-
 
 OPTIONS
 -------
@@ -60,7 +62,7 @@
 --revs::
 	Read the revision arguments from the standard input, instead of
 	individual object names.  The revision arguments are processed
-	the same way as 'git-rev-list' with the `--objects` flag
+	the same way as 'git rev-list' with the `--objects` flag
 	uses its `commit` arguments to build the list of objects it
 	outputs.  The objects on the resulting list are packed.
 
@@ -72,7 +74,7 @@
 --all::
 	This implies `--revs`.  In addition to the list of
 	revision arguments read from the standard input, pretend
-	as if all refs under `$GIT_DIR/refs` are specified to be
+	as if all refs under `refs/` are specified to be
 	included.
 
 --include-tag::
@@ -80,8 +82,8 @@
 	reference was included in the resulting packfile.  This
 	can be useful to send new tags to native git clients.
 
---window=[N]::
---depth=[N]::
+--window=<n>::
+--depth=<n>::
 	These two options affect how the objects contained in
 	the pack are stored using delta compression.  The
 	objects are first internally sorted by type, size and
@@ -93,10 +95,10 @@
 	times to get to the necessary object.
 	The default value for --window is 10 and --depth is 50.
 
---window-memory=[N]::
+--window-memory=<n>::
 	This option provides an additional limit on top of `--window`;
 	the window size will dynamically scale down so as to not take
-	up more than N bytes in memory.  This is useful in
+	up more than '<n>' bytes in memory.  This is useful in
 	repositories with a mix of large and small objects to not run
 	out of memory with a large window, but still be able to take
 	advantage of the large window for the smaller objects.  The
@@ -105,25 +107,25 @@
 	default.
 
 --max-pack-size=<n>::
-	Maximum size of each output packfile, expressed in MiB.
+	Maximum size of each output pack file. The size can be suffixed with
+	"k", "m", or "g". The minimum size allowed is limited to 1 MiB.
 	If specified,  multiple packfiles may be created.
 	The default is unlimited, unless the config variable
 	`pack.packSizeLimit` is set.
 
 --honor-pack-keep::
 	This flag causes an object already in a local pack that
-	has a .keep file to be ignored, even if it appears in the
-	standard input.
+	has a .keep file to be ignored, even if it would have
+	otherwise been packed.
 
 --incremental::
-	This flag causes an object already in a pack ignored
-	even if it appears in the standard input.
+	This flag causes an object already in a pack to be ignored
+	even if it would have otherwise been packed.
 
 --local::
-	This flag is similar to `--incremental`; instead of
-	ignoring all packed objects, it only ignores objects
-	that are packed and/or not in the local object store
-	(i.e. borrowed from an alternate).
+	This flag causes an object that is borrowed from an alternate
+	object store to be ignored even if it would have otherwise been
+	packed.
 
 --non-empty::
         Only create a packed archive if it would contain at
@@ -137,7 +139,7 @@
 
 --all-progress::
 	When --stdout is specified then progress report is
-	displayed during the object count and deltification phases
+	displayed during the object count and compression phases
 	but inhibited during the write-out phase. The reason is
 	that in some cases the output stream is directly linked
 	to another command which may wish to display progress
@@ -146,6 +148,11 @@
 	report for the write-out phase as well even if --stdout is
 	used.
 
+--all-progress-implied::
+	This is used to imply --all-progress whenever progress display
+	is activated.  Unlike --all-progress this flag doesn't actually
+	force any progress display by itself.
+
 -q::
 	This flag makes the command not to report its progress
 	on the standard error stream.
@@ -164,7 +171,7 @@
 	wholesale enforcement of a different compression level on the
 	packed data is desired.
 
---compression=[N]::
+--compression=<n>::
 	Specifies compression level for newly-compressed data in the
 	generated pack.  If not specified,  pack compression level is
 	determined first by pack.compression,  then by core.compression,
@@ -172,16 +179,31 @@
 	Add --no-reuse-object if you want to force a uniform compression
 	level on all data no matter the source.
 
+--thin::
+	Create a "thin" pack by omitting the common objects between a
+	sender and a receiver in order to reduce network transfer. This
+	option only makes sense in conjunction with --stdout.
++
+Note: A thin pack violates the packed archive format by omitting
+required objects and is thus unusable by git without making it
+self-contained. Use `git index-pack --fix-thin`
+(see linkgit:git-index-pack[1]) to restore the self-contained property.
+
 --delta-base-offset::
-	A packed archive can express base object of a delta as
-	either 20-byte object name or as an offset in the
-	stream, but older version of git does not understand the
-	latter.  By default, 'git-pack-objects' only uses the
+	A packed archive can express the base object of a delta as
+	either a 20-byte object name or as an offset in the
+	stream, but ancient versions of git don't understand the
+	latter.  By default, 'git pack-objects' only uses the
 	former format for better compatibility.  This option
 	allows the command to use the latter format for
 	compactness.  Depending on the average delta chain
 	length, this option typically shrinks the resulting
 	packfile by 3-5 per-cent.
++
+Note: Porcelain commands such as `git gc` (see linkgit:git-gc[1]),
+`git repack` (see linkgit:git-repack[1]) pass this option by default
+in modern git when they put objects in your repository into pack files.
+So does `git bundle` (see linkgit:git-bundle[1]) when it creates a bundle.
 
 --threads=<n>::
 	Specifies the number of threads to spawn when searching for best
@@ -202,15 +224,6 @@
 	With this option, parents that are hidden by grafts are packed
 	nevertheless.
 
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
--------------
-Documentation by Junio C Hamano
-
 SEE ALSO
 --------
 linkgit:git-rev-list[1]
diff --git a/Documentation/git-pack-redundant.txt b/Documentation/git-pack-redundant.txt
index 5f9435e..f2869da 100644
--- a/Documentation/git-pack-redundant.txt
+++ b/Documentation/git-pack-redundant.txt
@@ -8,6 +8,7 @@
 
 SYNOPSIS
 --------
+[verse]
 'git pack-redundant' [ --verbose ] [ --alt-odb ] < --all | .pack filename ... >
 
 DESCRIPTION
@@ -16,7 +17,7 @@
 are redundant. The output is suitable for piping to
 `xargs rm` if you are in the root of the repository.
 
-'git-pack-redundant' accepts a list of objects on standard input. Any objects
+'git pack-redundant' accepts a list of objects on standard input. Any objects
 given will be ignored when checking which packs are required. This makes the
 following command useful when wanting to remove packs which contain unreachable
 objects.
@@ -38,14 +39,6 @@
 --verbose::
 	Outputs some statistics to stderr. Has a small performance penalty.
 
-Author
-------
-Written by Lukas Sandström <lukass@etek.chalmers.se>
-
-Documentation
---------------
-Documentation by Lukas Sandström <lukass@etek.chalmers.se>
-
 SEE ALSO
 --------
 linkgit:git-pack-objects[1]
diff --git a/Documentation/git-pack-refs.txt b/Documentation/git-pack-refs.txt
index 1ee99c2..10afd4e 100644
--- a/Documentation/git-pack-refs.txt
+++ b/Documentation/git-pack-refs.txt
@@ -7,6 +7,7 @@
 
 SYNOPSIS
 --------
+[verse]
 'git pack-refs' [--all] [--no-prune]
 
 DESCRIPTION
@@ -31,7 +32,7 @@
 
 A recommended practice to deal with a repository with too many
 refs is to pack its refs with `--all --prune` once, and
-occasionally run `git pack-refs \--prune`.  Tags are by
+occasionally run `git pack-refs --prune`.  Tags are by
 definition stationary and are not expected to change.  Branch
 heads will be packed with the initial `pack-refs --all`, but
 only the currently active branch heads will become unpacked,
@@ -56,11 +57,6 @@
 The command usually removes loose refs under `$GIT_DIR/refs`
 hierarchy after packing them.  This option tells it not to.
 
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-parse-remote.txt b/Documentation/git-parse-remote.txt
index 39d9daa..a45ea1e 100644
--- a/Documentation/git-parse-remote.txt
+++ b/Documentation/git-parse-remote.txt
@@ -8,6 +8,7 @@
 
 SYNOPSIS
 --------
+[verse]
 '. "$(git --exec-path)/git-parse-remote"'
 
 DESCRIPTION
@@ -17,14 +18,6 @@
 $GIT_DIR/branches/ and configuration variables that are related
 to fetching, pulling and pushing.
 
-Author
-------
-Written by Junio C Hamano.
-
-Documentation
---------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-patch-id.txt b/Documentation/git-patch-id.txt
index 253fc0f..90268f0 100644
--- a/Documentation/git-patch-id.txt
+++ b/Documentation/git-patch-id.txt
@@ -7,6 +7,7 @@
 
 SYNOPSIS
 --------
+[verse]
 'git patch-id' < <patch>
 
 DESCRIPTION
@@ -18,7 +19,7 @@
 
 IOW, you can use this thing to look for likely duplicate commits.
 
-When dealing with 'git-diff-tree' output, it takes advantage of
+When dealing with 'git diff-tree' output, it takes advantage of
 the fact that the patch is prefixed with the object name of the
 commit, and outputs two 40-byte hexadecimal strings.  The first
 string is the patch ID, and the second string is the commit ID.
@@ -29,14 +30,6 @@
 <patch>::
 	The diff to create the ID of.
 
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-peek-remote.txt b/Documentation/git-peek-remote.txt
index 8282a5e..87ea3fb 100644
--- a/Documentation/git-peek-remote.txt
+++ b/Documentation/git-peek-remote.txt
@@ -8,11 +8,12 @@
 
 SYNOPSIS
 --------
+[verse]
 'git peek-remote' [--upload-pack=<git-upload-pack>] [<host>:]<directory>
 
 DESCRIPTION
 -----------
-This command is deprecated; use 'git-ls-remote' instead.
+This command is deprecated; use 'git ls-remote' instead.
 
 OPTIONS
 -------
@@ -37,14 +38,6 @@
 	The repository to sync from.
 
 
-Author
-------
-Written by Junio C Hamano <gitster@pobox.com>
-
-Documentation
---------------
-Documentation by Junio C Hamano.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-prune-packed.txt b/Documentation/git-prune-packed.txt
index abfc6b6..80dc022 100644
--- a/Documentation/git-prune-packed.txt
+++ b/Documentation/git-prune-packed.txt
@@ -8,6 +8,7 @@
 
 SYNOPSIS
 --------
+[verse]
 'git prune-packed' [-n|--dry-run] [-q|--quiet]
 
 
@@ -36,14 +37,6 @@
 --quiet::
 	Squelch the progress indicator.
 
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by Ryan Anderson <ryan@michonline.com>
-
 SEE ALSO
 --------
 linkgit:git-pack-objects[1]
diff --git a/Documentation/git-prune.txt b/Documentation/git-prune.txt
index da6055d..80d01b0 100644
--- a/Documentation/git-prune.txt
+++ b/Documentation/git-prune.txt
@@ -8,21 +8,22 @@
 
 SYNOPSIS
 --------
-'git-prune' [-n] [-v] [--expire <expire>] [--] [<head>...]
+[verse]
+'git prune' [-n] [-v] [--expire <expire>] [--] [<head>...]
 
 DESCRIPTION
 -----------
 
-NOTE: In most cases, users should run 'git-gc', which calls
-'git-prune'. See the section "NOTES", below.
+NOTE: In most cases, users should run 'git gc', which calls
+'git prune'. See the section "NOTES", below.
 
-This runs 'git-fsck --unreachable' using all the refs
-available in `$GIT_DIR/refs`, optionally with additional set of
+This runs 'git fsck --unreachable' using all the refs
+available in `refs/`, optionally with additional set of
 objects specified on the command line, and prunes all unpacked
 objects unreachable from any of these head objects from the object database.
 In addition, it
 prunes the unpacked objects that are also found in packs by
-running 'git-prune-packed'.
+running 'git prune-packed'.
 
 Note that unreachable, packed objects will remain.  If this is
 not desired, see linkgit:git-repack[1].
@@ -31,10 +32,12 @@
 -------
 
 -n::
+--dry-run::
 	Do not remove anything; just report what it would
 	remove.
 
 -v::
+--verbose::
 	Report all removed objects.
 
 \--::
@@ -62,12 +65,12 @@
 Notes
 -----
 
-In most cases, users will not need to call 'git-prune' directly, but
-should instead call 'git-gc', which handles pruning along with
+In most cases, users will not need to call 'git prune' directly, but
+should instead call 'git gc', which handles pruning along with
 many other housekeeping tasks.
 
 For a description of which objects are considered for pruning, see
-'git-fsck''s --unreachable option.
+'git fsck''s --unreachable option.
 
 SEE ALSO
 --------
@@ -76,14 +79,6 @@
 linkgit:git-gc[1],
 linkgit:git-reflog[1]
 
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt
index 7578623..defb544 100644
--- a/Documentation/git-pull.txt
+++ b/Documentation/git-pull.txt
@@ -8,35 +8,109 @@
 
 SYNOPSIS
 --------
-'git pull' <options> <repository> <refspec>...
+[verse]
+'git pull' [options] [<repository> [<refspec>...]]
 
 
 DESCRIPTION
 -----------
-Runs 'git-fetch' with the given parameters, and calls 'git-merge'
-to merge the retrieved head(s) into the current branch.
-With `--rebase`, calls 'git-rebase' instead of 'git-merge'.
 
-Note that you can use `.` (current directory) as the
-<repository> to pull from the local repository -- this is useful
-when merging local branches into the current branch.
+Incorporates changes from a remote repository into the current
+branch.  In its default mode, `git pull` is shorthand for
+`git fetch` followed by `git merge FETCH_HEAD`.
 
-Also note that options meant for 'git-pull' itself and underlying
-'git-merge' must be given before the options meant for 'git-fetch'.
+More precisely, 'git pull' runs 'git fetch' with the given
+parameters and calls 'git merge' to merge the retrieved branch
+heads into the current branch.
+With `--rebase`, it runs 'git rebase' instead of 'git merge'.
+
+<repository> should be the name of a remote repository as
+passed to linkgit:git-fetch[1].  <refspec> can name an
+arbitrary remote ref (for example, the name of a tag) or even
+a collection of refs with corresponding remote-tracking branches
+(e.g., refs/heads/{asterisk}:refs/remotes/origin/{asterisk}),
+but usually it is the name of a branch in the remote repository.
+
+Default values for <repository> and <branch> are read from the
+"remote" and "merge" configuration for the current branch
+as set by linkgit:git-branch[1] `--track`.
+
+Assume the following history exists and the current branch is
+"`master`":
+
+------------
+	  A---B---C master on origin
+	 /
+    D---E---F---G master
+------------
+
+Then "`git pull`" will fetch and replay the changes from the remote
+`master` branch since it diverged from the local `master` (i.e., `E`)
+until its current commit (`C`) on top of `master` and record the
+result in a new commit along with the names of the two parent commits
+and a log message from the user describing the changes.
+
+------------
+	  A---B---C remotes/origin/master
+	 /         \
+    D---E---F---G---H master
+------------
+
+See linkgit:git-merge[1] for details, including how conflicts
+are presented and handled.
+
+In git 1.7.0 or later, to cancel a conflicting merge, use
+`git reset --merge`.  *Warning*: In older versions of git, running 'git pull'
+with uncommitted changes is discouraged: while possible, it leaves you
+in a state that may be hard to back out of in the case of a conflict.
+
+If any of the remote changes overlap with local uncommitted changes,
+the merge will be automatically cancelled and the work tree untouched.
+It is generally best to get any local changes in working order before
+pulling or stash them away with linkgit:git-stash[1].
 
 OPTIONS
 -------
+
+Options meant for 'git pull' itself and the underlying 'git merge'
+must be given before the options meant for 'git fetch'.
+
+-q::
+--quiet::
+	This is passed to both underlying git-fetch to squelch reporting of
+	during transfer, and underlying git-merge to squelch output during
+	merging.
+
+-v::
+--verbose::
+	Pass --verbose to git-fetch and git-merge.
+
+--[no-]recurse-submodules[=yes|on-demand|no]::
+	This option controls if new commits of all populated submodules should
+	be fetched too (see linkgit:git-config[1] and linkgit:gitmodules[5]).
+	That might be necessary to get the data needed for merging submodule
+	commits, a feature git learned in 1.7.3. Notice that the result of a
+	merge will not be checked out in the submodule, "git submodule update"
+	has to be called afterwards to bring the work tree up to date with the
+	merge result.
+
+Options related to merging
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
 include::merge-options.txt[]
 
 :git-pull: 1
 
 --rebase::
-	Instead of a merge, perform a rebase after fetching.  If
-	there is a remote ref for the upstream branch, and this branch
-	was rebased since last fetched, the rebase uses that information
-	to avoid rebasing non-local changes. To make this the default
-	for branch `<name>`, set configuration `branch.<name>.rebase`
-	to `true`.
+	Rebase the current branch on top of the upstream branch after
+	fetching.  If there is a remote-tracking branch corresponding to
+	the upstream branch and the upstream branch was rebased since last
+	fetched, the rebase uses that information to avoid rebasing
+	non-local changes.
++
+See `pull.rebase`, `branch.<name>.rebase` and `branch.autosetuprebase` in
+linkgit:git-config[1] if you want to make `git pull` always use
+`--rebase` instead of merging.
 +
 [NOTE]
 This is a potentially _dangerous_ mode of operation.
@@ -47,6 +121,9 @@
 --no-rebase::
 	Override earlier --rebase.
 
+Options related to fetching
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
 include::fetch-options.txt[]
 
 include::pull-fetch-param.txt[]
@@ -70,7 +147,7 @@
 in `$GIT_DIR/remotes/<origin>` file is used.
 
 In order to determine what remote branches to fetch (and
-optionally store in the tracking branches) when the command is
+optionally store in the remote-tracking branches) when the command is
 run without any refspec parameters on the command line, values
 of the configuration variable `remote.<origin>.fetch` are
 consulted, and if there aren't any, `$GIT_DIR/remotes/<origin>`
@@ -83,9 +160,9 @@
 ------------
 
 A globbing refspec must have a non-empty RHS (i.e. must store
-what were fetched in tracking branches), and its LHS and RHS
+what were fetched in remote-tracking branches), and its LHS and RHS
 must end with `/*`.  The above specifies that all remote
-branches are tracked using tracking branches in
+branches are tracked using remote-tracking branches in
 `refs/remotes/origin/` hierarchy under the same name.
 
 The rule to determine which remote branch to merge after
@@ -131,76 +208,32 @@
 ------------------------------------------------
 +
 This leaves a copy of `next` temporarily in FETCH_HEAD, but
-does not update any remote-tracking branches.
-
-* Bundle local branch `fixes` and `enhancements` on top of
-  the current branch, making an Octopus merge:
+does not update any remote-tracking branches. Using remote-tracking
+branches, the same can be done by invoking fetch and merge:
 +
 ------------------------------------------------
-$ git pull . fixes enhancements
+$ git fetch origin
+$ git merge origin/next
 ------------------------------------------------
-+
-This `git pull .` syntax is equivalent to `git merge`.
-
-* Merge local branch `obsolete` into the current branch, using `ours`
-  merge strategy:
-+
-------------------------------------------------
-$ git pull -s ours . obsolete
-------------------------------------------------
-
-* Merge local branch `maint` into the current branch, but do not make
-  a commit automatically:
-+
-------------------------------------------------
-$ git pull --no-commit . maint
-------------------------------------------------
-+
-This can be used when you want to include further changes to the
-merge, or want to write your own merge commit message.
-+
-You should refrain from abusing this option to sneak substantial
-changes into a merge commit.  Small fixups like bumping
-release/version name would be acceptable.
-
-* Command line pull of multiple branches from one repository:
-+
-------------------------------------------------
-$ git checkout master
-$ git fetch origin +pu:pu maint:tmp
-$ git pull . tmp
-------------------------------------------------
-+
-This updates (or creates, as necessary) branches `pu` and `tmp` in
-the local repository by fetching from the branches (respectively)
-`pu` and `maint` from the remote repository.
-+
-The `pu` branch will be updated even if it is does not fast-forward;
-the others will not be.
-+
-The final command then merges the newly fetched `tmp` into master.
 
 
 If you tried a pull which resulted in a complex conflicts and
-would want to start over, you can recover with 'git-reset'.
+would want to start over, you can recover with 'git reset'.
 
 
+BUGS
+----
+Using --recurse-submodules can only fetch new commits in already checked
+out submodules right now. When e.g. upstream added a new submodule in the
+just fetched commits of the superproject the submodule itself can not be
+fetched, making it impossible to check out that submodule later without
+having to do a fetch again. This is expected to be fixed in a future git
+version.
+
 SEE ALSO
 --------
 linkgit:git-fetch[1], linkgit:git-merge[1], linkgit:git-config[1]
 
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-and Junio C Hamano <gitster@pobox.com>
-
-Documentation
---------------
-Documentation by Jon Loeliger,
-David Greaves,
-Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt
index 2653388..cb97cc1 100644
--- a/Documentation/git-push.txt
+++ b/Documentation/git-push.txt
@@ -9,9 +9,9 @@
 SYNOPSIS
 --------
 [verse]
-'git push' [--all | --mirror | --tags] [--dry-run] [--receive-pack=<git-receive-pack>]
-	   [--repo=<repository>] [-f | --force] [-v | --verbose]
-	   [<repository> <refspec>...]
+'git push' [--all | --mirror | --tags] [-n | --dry-run] [--receive-pack=<git-receive-pack>]
+	   [--repo=<repository>] [-f | --force] [--prune] [-v | --verbose] [-u | --set-upstream]
+	   [<repository> [<refspec>...]]
 
 DESCRIPTION
 -----------
@@ -34,14 +34,14 @@
 
 <refspec>...::
 	The format of a <refspec> parameter is an optional plus
-	`{plus}`, followed by the source ref <src>, followed
+	`+`, followed by the source ref <src>, followed
 	by a colon `:`, followed by the destination ref <dst>.
 	It is used to specify with what <src> object the <dst> ref
 	in the remote repository is to be updated.
 +
 The <src> is often the name of the branch you would want to push, but
 it can be any arbitrary "SHA-1 expression", such as `master~4` or
-`HEAD` (see linkgit:git-rev-parse[1]).
+`HEAD` (see linkgit:gitrevisions[7]).
 +
 The <dst> tells which ref on the remote side is updated with this
 push. Arbitrary expressions cannot be used here, an actual ref must
@@ -50,9 +50,9 @@
 +
 The object referenced by <src> is used to update the <dst> reference
 on the remote side, but by default this is only allowed if the
-update can fast forward <dst>.  By having the optional leading `{plus}`,
+update can fast-forward <dst>.  By having the optional leading `+`,
 you can tell git to update the <dst> ref even when the update is not a
-fast forward.  This does *not* attempt to merge <src> into <dst>.  See
+fast-forward.  This does *not* attempt to merge <src> into <dst>.  See
 EXAMPLES below for details.
 +
 `tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.
@@ -60,7 +60,7 @@
 Pushing an empty <src> allows you to delete the <dst> ref from
 the remote repository.
 +
-The special refspec `:` (or `{plus}:` to allow non-fast forward updates)
+The special refspec `:` (or `+:` to allow non-fast-forward updates)
 directs git to push "matching" branches: for every branch that exists on
 the local side, the remote side is updated if a branch of the same name
 already exists on the remote side.  This is the default operation mode
@@ -69,11 +69,19 @@
 
 --all::
 	Instead of naming each ref to push, specifies that all
-	refs under `$GIT_DIR/refs/heads/` be pushed.
+	refs under `refs/heads/` be pushed.
+
+--prune::
+	Remove remote branches that don't have a local counterpart. For example
+	a remote branch `tmp` will be removed if a local branch with the same
+	name doesn't exist any more. This also respects refspecs, e.g.
+	`git push --prune remote refs/heads/*:refs/tmp/*` would
+	make sure that remote `refs/tmp/foo` will be removed if `refs/heads/foo`
+	doesn't exist.
 
 --mirror::
 	Instead of naming each ref to push, specifies that all
-	refs under `$GIT_DIR/refs/` (which includes but is not
+	refs under `refs/` (which includes but is not
 	limited to `refs/heads/`, `refs/remotes/`, and `refs/tags/`)
 	be mirrored to the remote repository.  Newly created local
 	refs will be pushed to the remote end, locally updated refs
@@ -82,6 +90,7 @@
 	if the configuration option `remote.<remote>.mirror` is
 	set.
 
+-n::
 --dry-run::
 	Do everything except actually send the updates.
 
@@ -90,8 +99,12 @@
 	will be tab-separated and sent to stdout instead of stderr.  The full
 	symbolic names of the refs will be given.
 
+--delete::
+	All listed refs are deleted from the remote repository. This is
+	the same as prefixing all refs with a colon.
+
 --tags::
-	All refs under `$GIT_DIR/refs/tags` are pushed, in
+	All refs under `refs/tags` are pushed, in
 	addition to refspecs explicitly listed on the command
 	line.
 
@@ -111,7 +124,7 @@
 
 --repo=<repository>::
 	This option is only relevant if no <repository> argument is
-	passed in the invocation. In this case, 'git-push' derives the
+	passed in the invocation. In this case, 'git push' derives the
 	remote name from the current branch: If it tracks a remote
 	branch, then that remote repository is pushed to. Otherwise,
 	the name "origin" is used. For this latter case, this option
@@ -125,18 +138,50 @@
 +
 is that #1 always pushes to "public" whereas #2 pushes to "public"
 only if the current branch does not track a remote branch. This is
-useful if you write an alias or script around 'git-push'.
+useful if you write an alias or script around 'git push'.
+
+-u::
+--set-upstream::
+	For every branch that is up to date or successfully pushed, add
+	upstream (tracking) reference, used by argument-less
+	linkgit:git-pull[1] and other commands. For more information,
+	see 'branch.<name>.merge' in linkgit:git-config[1].
 
 --thin::
 --no-thin::
-	These options are passed to 'git-send-pack'.  Thin
-	transfer spends extra cycles to minimize the number of
-	objects to be sent and meant to be used on slower connection.
+	These options are passed to linkgit:git-send-pack[1]. A thin transfer
+	significantly reduces the amount of sent data when the sender and
+	receiver share many of the same objects in common. The default is
+	\--thin.
+
+-q::
+--quiet::
+	Suppress all output, including the listing of updated refs,
+	unless an error occurs. Progress is not reported to the standard
+	error stream.
 
 -v::
 --verbose::
 	Run verbosely.
 
+--progress::
+	Progress status is reported on the standard error stream
+	by default when it is attached to a terminal, unless -q
+	is specified. This flag forces progress status even if the
+	standard error stream is not directed to a terminal.
+
+--recurse-submodules=check|on-demand::
+	Make sure all submodule commits used by the revisions to be
+	pushed are available on a remote tracking branch. If 'check' is
+	used git will verify that all submodule commits that changed in
+	the revisions to be pushed are available on at least one remote
+	of the submodule. If any commits are missing the push will be
+	aborted and exit with non-zero status. If 'on-demand' is used
+	all submodules that changed in the revisions to be pushed will
+	be pushed. If on-demand was not able to push all necessary
+	revisions it will also be aborted and exit with non-zero status.
+
+
 include::urls-remotes.txt[]
 
 OUTPUT
@@ -159,27 +204,45 @@
  <flag> \t <from>:<to> \t <summary> (<reason>)
 -------------------------------
 
+The status of up-to-date refs is shown only if --porcelain or --verbose
+option is used.
+
 flag::
-	A single character indicating the status of the ref. This is
-	blank for a successfully pushed ref, `!` for a ref that was
-	rejected or failed to push, and '=' for a ref that was up to
-	date and did not need pushing (note that the status of up to
-	date refs is shown only when `git push` is running verbosely).
+	A single character indicating the status of the ref:
+(space);; for a successfully pushed fast-forward;
+`+`;; for a successful forced update;
+`-`;; for a successfully deleted ref;
+`*`;; for a successfully pushed new ref;
+`!`;; for a ref that was rejected or failed to push; and
+`=`;; for a ref that was up to date and did not need pushing.
 
 summary::
 	For a successfully pushed ref, the summary shows the old and new
 	values of the ref in a form suitable for using as an argument to
 	`git log` (this is `<old>..<new>` in most cases, and
-	`<old>...<new>` for forced non-fast forward updates). For a
-	failed update, more details are given for the failure.
-	The string `rejected` indicates that git did not try to send the
-	ref at all (typically because it is not a fast forward). The
-	string `remote rejected` indicates that the remote end refused
-	the update; this rejection is typically caused by a hook on the
-	remote side. The string `remote failure` indicates that the
-	remote end did not report the successful update of the ref
-	(perhaps because of a temporary error on the remote side, a
-	break in the network connection, or other transient error).
+	`<old>...<new>` for forced non-fast-forward updates).
++
+For a failed update, more details are given:
++
+--
+rejected::
+	Git did not try to send the ref at all, typically because it
+	is not a fast-forward and you did not force the update.
+
+remote rejected::
+	The remote end refused the update.  Usually caused by a hook
+	on the remote side, or because the remote repository has one
+	of the following safety options in effect:
+	`receive.denyCurrentBranch` (for pushes to the checked out
+	branch), `receive.denyNonFastForwards` (for forced
+	non-fast-forward updates), `receive.denyDeletes` or
+	`receive.denyDeleteCurrent`.  See linkgit:git-config[1].
+
+remote failure::
+	The remote end did not report the successful update of the ref,
+	perhaps because of a temporary error on the remote side, a
+	break in the network connection, or other transient error.
+--
 
 from::
 	The name of the local ref being pushed, minus its
@@ -195,15 +258,101 @@
 	refs, no explanation is needed. For a failed ref, the reason for
 	failure is described.
 
+Note about fast-forwards
+------------------------
+
+When an update changes a branch (or more in general, a ref) that used to
+point at commit A to point at another commit B, it is called a
+fast-forward update if and only if B is a descendant of A.
+
+In a fast-forward update from A to B, the set of commits that the original
+commit A built on top of is a subset of the commits the new commit B
+builds on top of.  Hence, it does not lose any history.
+
+In contrast, a non-fast-forward update will lose history.  For example,
+suppose you and somebody else started at the same commit X, and you built
+a history leading to commit B while the other person built a history
+leading to commit A.  The history looks like this:
+
+----------------
+
+      B
+     /
+ ---X---A
+
+----------------
+
+Further suppose that the other person already pushed changes leading to A
+back to the original repository you two obtained the original commit X.
+
+The push done by the other person updated the branch that used to point at
+commit X to point at commit A.  It is a fast-forward.
+
+But if you try to push, you will attempt to update the branch (that
+now points at A) with commit B.  This does _not_ fast-forward.  If you did
+so, the changes introduced by commit A will be lost, because everybody
+will now start building on top of B.
+
+The command by default does not allow an update that is not a fast-forward
+to prevent such loss of history.
+
+If you do not want to lose your work (history from X to B) nor the work by
+the other person (history from X to A), you would need to first fetch the
+history from the repository, create a history that contains changes done
+by both parties, and push the result back.
+
+You can perform "git pull", resolve potential conflicts, and "git push"
+the result.  A "git pull" will create a merge commit C between commits A
+and B.
+
+----------------
+
+      B---C
+     /   /
+ ---X---A
+
+----------------
+
+Updating A with the resulting merge commit will fast-forward and your
+push will be accepted.
+
+Alternatively, you can rebase your change between X and B on top of A,
+with "git pull --rebase", and push the result back.  The rebase will
+create a new commit D that builds the change between X and B on top of
+A.
+
+----------------
+
+      B   D
+     /   /
+ ---X---A
+
+----------------
+
+Again, updating A with this commit will fast-forward and your push will be
+accepted.
+
+There is another common situation where you may encounter non-fast-forward
+rejection when you try to push, and it is possible even when you are
+pushing into a repository nobody else pushes into. After you push commit
+A yourself (in the first picture in this section), replace it with "git
+commit --amend" to produce commit B, and you try to push it out, because
+forgot that you have pushed A out already. In such a case, and only if
+you are certain that nobody in the meantime fetched your earlier commit A
+(and started building on top of it), you can run "git push --force" to
+overwrite it. In other words, "git push --force" is a method reserved for
+a case where you do mean to lose history.
+
+
 Examples
 --------
 
-git push::
+`git push`::
 	Works like `git push <remote>`, where <remote> is the
 	current branch's remote (or `origin`, if no remote is
 	configured for the current branch).
 
-git push origin::
+`git push origin`::
 	Without additional configuration, works like
 	`git push origin :`.
 +
@@ -215,49 +364,49 @@
 the ones in the examples below) can be configured as the default for
 `git push origin`.
 
-git push origin :::
+`git push origin :`::
 	Push "matching" branches to `origin`. See
 	<refspec> in the <<OPTIONS,OPTIONS>> section above for a
 	description of "matching" branches.
 
-git push origin master::
+`git push origin master`::
 	Find a ref that matches `master` in the source repository
 	(most likely, it would find `refs/heads/master`), and update
 	the same ref (e.g. `refs/heads/master`) in `origin` repository
 	with it.  If `master` did not exist remotely, it would be
 	created.
 
-git push origin HEAD::
+`git push origin HEAD`::
 	A handy way to push the current branch to the same name on the
 	remote.
 
-git push origin master:satellite/master dev:satellite/dev::
+`git push origin master:satellite/master dev:satellite/dev`::
 	Use the source ref that matches `master` (e.g. `refs/heads/master`)
 	to update the ref that matches `satellite/master` (most probably
 	`refs/remotes/satellite/master`) in the `origin` repository, then
 	do the same for `dev` and `satellite/dev`.
 
-git push origin HEAD:master::
+`git push origin HEAD:master`::
 	Push the current branch to the remote ref matching `master` in the
 	`origin` repository. This form is convenient to push the current
 	branch without thinking about its local name.
 
-git push origin master:refs/heads/experimental::
+`git push origin master:refs/heads/experimental`::
 	Create the branch `experimental` in the `origin` repository
 	by copying the current `master` branch.  This form is only
 	needed to create a new branch or tag in the remote repository when
 	the local name and the remote name are different; otherwise,
 	the ref name on its own will work.
 
-git push origin :experimental::
+`git push origin :experimental`::
 	Find a ref that matches `experimental` in the `origin` repository
 	(e.g. `refs/heads/experimental`), and delete it.
 
-git push origin {plus}dev:master::
+`git push origin +dev:master`::
 	Update the origin repository's master branch with the dev branch,
-	allowing non-fast forward updates.  *This can leave unreferenced
+	allowing non-fast-forward updates.  *This can leave unreferenced
 	commits dangling in the origin repository.*  Consider the
-	following situation, where a fast forward is not possible:
+	following situation, where a fast-forward is not possible:
 +
 ----
 	    o---o---o---A---B  origin/master
@@ -277,16 +426,6 @@
 and so would be unreachable.  As such, these commits would be removed by
 a `git gc` command on the origin repository.
 
-
-Author
-------
-Written by Junio C Hamano <gitster@pobox.com>, later rewritten in C
-by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-quiltimport.txt b/Documentation/git-quiltimport.txt
index d4037de..7f112f3 100644
--- a/Documentation/git-quiltimport.txt
+++ b/Documentation/git-quiltimport.txt
@@ -9,7 +9,7 @@
 SYNOPSIS
 --------
 [verse]
-'git quiltimport' [--dry-run] [--author <author>] [--patches <dir>]
+'git quiltimport' [--dry-run | -n] [--author <author>] [--patches <dir>]
 
 
 DESCRIPTION
@@ -49,14 +49,6 @@
 or the value of the $QUILT_PATCHES environment
 variable.
 
-Author
-------
-Written by Eric Biederman <ebiederm@lnxi.com>
-
-Documentation
---------------
-Documentation by Eric Biederman <ebiederm@lnxi.com>
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-read-tree.txt b/Documentation/git-read-tree.txt
index 4a932b0..c4bde65 100644
--- a/Documentation/git-read-tree.txt
+++ b/Documentation/git-read-tree.txt
@@ -8,10 +8,11 @@
 
 SYNOPSIS
 --------
+[verse]
 'git read-tree' [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>]
 		[-u [--exclude-per-directory=<gitignore>] | -i]]
-		[--index-output=<file>]
-		<tree-ish1> [<tree-ish2> [<tree-ish3>]]
+		[--index-output=<file>] [--no-sparse-checkout]
+		(--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])
 
 
 DESCRIPTION
@@ -25,8 +26,8 @@
 flag.  When used with `-m`, the `-u` flag causes it to also update
 the files in the work tree with the result of the merge.
 
-Trivial merges are done by 'git-read-tree' itself.  Only conflicting paths
-will be in unmerged state when 'git-read-tree' returns.
+Trivial merges are done by 'git read-tree' itself.  Only conflicting paths
+will be in unmerged state when 'git read-tree' returns.
 
 OPTIONS
 -------
@@ -46,42 +47,46 @@
 
 -i::
 	Usually a merge requires the index file as well as the
-	files in the working tree are up to date with the
+	files in the working tree to be up to date with the
 	current head commit, in order not to lose local
 	changes.  This flag disables the check with the working
 	tree and is meant to be used when creating a merge of
 	trees that are not directly related to the current
 	working tree status into a temporary index file.
 
+-n::
+--dry-run::
+	Check if the command would error out, without updating the index
+	nor the files in the working tree for real.
+
 -v::
 	Show the progress of checking files out.
 
 --trivial::
-	Restrict three-way merge by 'git-read-tree' to happen
+	Restrict three-way merge by 'git read-tree' to happen
 	only if there is no file-level merging required, instead
 	of resolving merge for trivial cases and leaving
 	conflicting files unresolved in the index.
 
 --aggressive::
-	Usually a three-way merge by 'git-read-tree' resolves
+	Usually a three-way merge by 'git read-tree' resolves
 	the merge for really trivial cases and leaves other
-	cases unresolved in the index, so that Porcelains can
+	cases unresolved in the index, so that porcelains can
 	implement different merge policies.  This flag makes the
-	command to resolve a few more cases internally:
+	command resolve a few more cases internally:
 +
 * when one side removes a path and the other side leaves the path
   unmodified.  The resolution is to remove that path.
 * when both sides remove a path.  The resolution is to remove that path.
-* when both sides adds a path identically.  The resolution
+* when both sides add a path identically.  The resolution
   is to add that path.
 
 --prefix=<prefix>/::
 	Keep the current index contents, and read the contents
-	of named tree-ish under directory at `<prefix>`.  The
-	original index file cannot have anything at the path
-	`<prefix>` itself, and have nothing in `<prefix>/`
-	directory.  Note that the `<prefix>/` value must end
-	with a slash.
+	of the named tree-ish under the directory at `<prefix>`.
+	The command will refuse to overwrite entries that already
+	existed in the original index file. Note that the `<prefix>/`
+	value must end with a slash.
 
 --exclude-per-directory=<gitignore>::
 	When running the command with `-u` and `-m` options, the
@@ -110,13 +115,21 @@
 	directories the index file and index output file are
 	located in.
 
+--no-sparse-checkout::
+	Disable sparse checkout support even if `core.sparseCheckout`
+	is true.
+
+--empty::
+	Instead of reading tree object(s) into the index, just empty
+	it.
+
 <tree-ish#>::
 	The id of the tree object(s) to be read/merged.
 
 
 Merging
 -------
-If `-m` is specified, 'git-read-tree' can perform 3 kinds of
+If `-m` is specified, 'git read-tree' can perform 3 kinds of
 merge, a single tree merge if only 1 tree is given, a
 fast-forward merge with 2 trees, or a 3-way merge if 3 trees are
 provided.
@@ -124,18 +137,18 @@
 
 Single Tree Merge
 ~~~~~~~~~~~~~~~~~
-If only 1 tree is specified, 'git-read-tree' operates as if the user did not
+If only 1 tree is specified, 'git read-tree' operates as if the user did not
 specify `-m`, except that if the original index has an entry for a
-given pathname, and the contents of the path matches with the tree
+given pathname, and the contents of the path match with the tree
 being read, the stat info from the index is used. (In other words, the
 index's stat()s take precedence over the merged tree's).
 
 That means that if you do a `git read-tree -m <newtree>` followed by a
-`git checkout-index -f -u -a`, the 'git-checkout-index' only checks out
+`git checkout-index -f -u -a`, the 'git checkout-index' only checks out
 the stuff that really changed.
 
-This is used to avoid unnecessary false hits when 'git-diff-files' is
-run after 'git-read-tree'.
+This is used to avoid unnecessary false hits when 'git diff-files' is
+run after 'git read-tree'.
 
 
 Two Tree Merge
@@ -144,46 +157,48 @@
 Typically, this is invoked as `git read-tree -m $H $M`, where $H
 is the head commit of the current repository, and $M is the head
 of a foreign tree, which is simply ahead of $H (i.e. we are in a
-fast forward situation).
+fast-forward situation).
 
-When two trees are specified, the user is telling 'git-read-tree'
+When two trees are specified, the user is telling 'git read-tree'
 the following:
 
      1. The current index and work tree is derived from $H, but
-        the user may have local changes in them since $H;
+	the user may have local changes in them since $H.
 
      2. The user wants to fast-forward to $M.
 
 In this case, the `git read-tree -m $H $M` command makes sure
 that no local change is lost as the result of this "merge".
-Here are the "carry forward" rules:
+Here are the "carry forward" rules, where "I" denotes the index,
+"clean" means that index and work tree coincide, and "exists"/"nothing"
+refer to the presence of a path in the specified commit:
 
-        I (index)           H        M        Result
+	I                   H        M        Result
        -------------------------------------------------------
-      0 nothing             nothing  nothing  (does not happen)
-      1 nothing             nothing  exists   use M
-      2 nothing             exists   nothing  remove path from index
-      3 nothing             exists   exists,  use M if "initial checkout"
+     0  nothing             nothing  nothing  (does not happen)
+     1  nothing             nothing  exists   use M
+     2  nothing             exists   nothing  remove path from index
+     3  nothing             exists   exists,  use M if "initial checkout",
 				     H == M   keep index otherwise
-				     exists   fail
+				     exists,  fail
 				     H != M
 
         clean I==H  I==M
        ------------------
-      4 yes   N/A   N/A     nothing  nothing  keep index
-      5 no    N/A   N/A     nothing  nothing  keep index
+     4  yes   N/A   N/A     nothing  nothing  keep index
+     5  no    N/A   N/A     nothing  nothing  keep index
 
-      6 yes   N/A   yes     nothing  exists   keep index
-      7 no    N/A   yes     nothing  exists   keep index
-      8 yes   N/A   no      nothing  exists   fail
-      9 no    N/A   no      nothing  exists   fail
+     6  yes   N/A   yes     nothing  exists   keep index
+     7  no    N/A   yes     nothing  exists   keep index
+     8  yes   N/A   no      nothing  exists   fail
+     9  no    N/A   no      nothing  exists   fail
 
      10 yes   yes   N/A     exists   nothing  remove path from index
      11 no    yes   N/A     exists   nothing  fail
      12 yes   no    N/A     exists   nothing  fail
      13 no    no    N/A     exists   nothing  fail
 
-        clean (H=M)
+	clean (H==M)
        ------
      14 yes                 exists   exists   keep index
      15 no                  exists   exists   keep index
@@ -198,26 +213,26 @@
      21 no    yes   no      exists   exists   fail
 
 In all "keep index" cases, the index entry stays as in the
-original index file.  If the entry were not up to date,
-'git-read-tree' keeps the copy in the work tree intact when
+original index file.  If the entry is not up to date,
+'git read-tree' keeps the copy in the work tree intact when
 operating under the -u flag.
 
-When this form of 'git-read-tree' returns successfully, you can
-see what "local changes" you made are carried forward by running
+When this form of 'git read-tree' returns successfully, you can
+see which of the "local changes" that you made were carried forward by running
 `git diff-index --cached $M`.  Note that this does not
-necessarily match `git diff-index --cached $H` would have
+necessarily match what `git diff-index --cached $H` would have
 produced before such a two tree merge.  This is because of cases
 18 and 19 --- if you already had the changes in $M (e.g. maybe
 you picked it up via e-mail in a patch form), `git diff-index
 --cached $H` would have told you about the change before this
 merge, but it would not show in `git diff-index --cached $M`
-output after two-tree merge.
+output after the two-tree merge.
 
-Case #3 is slightly tricky and needs explanation.  The result from this
+Case 3 is slightly tricky and needs explanation.  The result from this
 rule logically should be to remove the path if the user staged the removal
 of the path and then switching to a new branch.  That however will prevent
 the initial checkout from happening, so the rule is modified to use M (new
-tree) only when the contents of the index is empty.  Otherwise the removal
+tree) only when the content of the index is empty.  Otherwise the removal
 of the path is kept as long as $H and $M are the same.
 
 3-Way Merge
@@ -225,7 +240,7 @@
 Each "index" entry has two bits worth of "stage" state. stage 0 is the
 normal one, and is the only one you'd see in any kind of normal use.
 
-However, when you do 'git-read-tree' with three trees, the "stage"
+However, when you do 'git read-tree' with three trees, the "stage"
 starts out at 1.
 
 This means that you can do
@@ -241,7 +256,7 @@
 as <tree1>, the current branch head as <tree2>, and the other
 branch head as <tree3>.
 
-Furthermore, 'git-read-tree' has special-case logic that says: if you see
+Furthermore, 'git read-tree' has special-case logic that says: if you see
 a file that matches in all respects in the following states, it
 "collapses" back to "stage0":
 
@@ -257,7 +272,7 @@
    - stage 1 and stage 3 are the same and stage 2 is different take
      stage 2 (we did something while they did nothing)
 
-The 'git-write-tree' command refuses to write a nonsensical tree, and it
+The 'git write-tree' command refuses to write a nonsensical tree, and it
 will complain about unmerged entries if it sees a single entry that is not
 stage 0.
 
@@ -273,7 +288,7 @@
 populated.  Here is an outline of how the algorithm works:
 
 - if a file exists in identical format in all three trees, it will
-  automatically collapse to "merged" state by 'git-read-tree'.
+  automatically collapse to "merged" state by 'git read-tree'.
 
 - a file that has _any_ difference what-so-ever in the three trees
   will stay as separate entries in the index. It's up to "porcelain
@@ -297,8 +312,8 @@
     matching "stage1" entry if it exists too.  .. all the normal
     trivial rules ..
 
-You would normally use 'git-merge-index' with supplied
-'git-merge-one-file' to do this last step.  The script updates
+You would normally use 'git merge-index' with supplied
+'git merge-one-file' to do this last step.  The script updates
 the files in the working tree as it merges each path and at the
 end of a successful merge.
 
@@ -320,13 +335,13 @@
 $ git checkout-index -f -u -a $JC
 ----------------
 
-You do random edits, without running 'git-update-index'.  And then
+You do random edits, without running 'git update-index'.  And then
 you notice that the tip of your "upstream" tree has advanced
 since you pulled from him:
 
 ----------------
 $ git fetch git://.... linus
-$ LT=`cat .git/FETCH_HEAD`
+$ LT=`git rev-parse FETCH_HEAD`
 ----------------
 
 Your work tree is still based on your HEAD ($JC), but you have
@@ -346,34 +361,71 @@
 updated to the result of the merge.
 
 However, if you have local changes in the working tree that
-would be overwritten by this merge, 'git-read-tree' will refuse
+would be overwritten by this merge, 'git read-tree' will refuse
 to run to prevent your changes from being lost.
 
 In other words, there is no need to worry about what exists only
 in the working tree.  When you have local changes in a part of
 the project that is not involved in the merge, your changes do
 not interfere with the merge, and are kept intact.  When they
-*do* interfere, the merge does not even start ('git-read-tree'
+*do* interfere, the merge does not even start ('git read-tree'
 complains loudly and fails without modifying anything).  In such
 a case, you can simply continue doing what you were in the
 middle of doing, and when your working tree is ready (i.e. you
 have finished your work-in-progress), attempt the merge again.
 
 
+Sparse checkout
+---------------
+
+"Sparse checkout" allows populating the working directory sparsely.
+It uses the skip-worktree bit (see linkgit:git-update-index[1]) to tell
+Git whether a file in the working directory is worth looking at.
+
+'git read-tree' and other merge-based commands ('git merge', 'git
+checkout'...) can help maintaining the skip-worktree bitmap and working
+directory update. `$GIT_DIR/info/sparse-checkout` is used to
+define the skip-worktree reference bitmap. When 'git read-tree' needs
+to update the working directory, it resets the skip-worktree bit in the index
+based on this file, which uses the same syntax as .gitignore files.
+If an entry matches a pattern in this file, skip-worktree will not be
+set on that entry. Otherwise, skip-worktree will be set.
+
+Then it compares the new skip-worktree value with the previous one. If
+skip-worktree turns from set to unset, it will add the corresponding
+file back. If it turns from unset to set, that file will be removed.
+
+While `$GIT_DIR/info/sparse-checkout` is usually used to specify what
+files are in, you can also specify what files are _not_ in, using
+negate patterns. For example, to remove the file `unwanted`:
+
+----------------
+/*
+!unwanted
+----------------
+
+Another tricky thing is fully repopulating the working directory when you
+no longer want sparse checkout. You cannot just disable "sparse
+checkout" because skip-worktree bits are still in the index and your working
+directory is still sparsely populated. You should re-populate the working
+directory with the `$GIT_DIR/info/sparse-checkout` file content as
+follows:
+
+----------------
+/*
+----------------
+
+Then you can disable sparse checkout. Sparse checkout support in 'git
+read-tree' and similar commands is disabled by default. You need to
+turn `core.sparseCheckout` on in order to have sparse checkout
+support.
+
+
 SEE ALSO
 --------
 linkgit:git-write-tree[1]; linkgit:git-ls-files[1];
 linkgit:gitignore[5]
 
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index db1b71d..fd535b0 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -8,19 +8,24 @@
 SYNOPSIS
 --------
 [verse]
-'git rebase' [-i | --interactive] [options] [--onto <newbase>]
-	<upstream> [<branch>]
-'git rebase' [-i | --interactive] [options] --onto <newbase>
+'git rebase' [-i | --interactive] [options] [--exec <cmd>] [--onto <newbase>]
+	[<upstream>] [<branch>]
+'git rebase' [-i | --interactive] [options] [--exec <cmd>] [--onto <newbase>]
 	--root [<branch>]
-
 'git rebase' --continue | --skip | --abort
 
 DESCRIPTION
 -----------
-If <branch> is specified, 'git-rebase' will perform an automatic
+If <branch> is specified, 'git rebase' will perform an automatic
 `git checkout <branch>` before doing anything else.  Otherwise
 it remains on the current branch.
 
+If <upstream> is not specified, the upstream configured in
+branch.<name>.remote and branch.<name>.merge options will be used; see
+linkgit:git-config[1] for details.  If you are currently not on any
+branch or if the current branch does not have a configured upstream,
+the rebase will abort.
+
 All changes made by commits in the current branch but that are not
 in <upstream> are saved to a temporary area.  This is the same set
 of commits that would be shown by `git log <upstream>..HEAD` (or
@@ -40,7 +45,7 @@
 It is possible that a merge failure will prevent this process from being
 completely automatic.  You will have to resolve any such merge failure
 and run `git rebase --continue`.  Another option is to bypass the commit
-that caused the merge failure with `git rebase --skip`.  To restore the
+that caused the merge failure with `git rebase --skip`.  To check out the
 original <branch> and remove the .git/rebase-apply working files, use the
 command `git rebase --abort` instead.
 
@@ -66,8 +71,9 @@
     D---E---F---G master
 ------------
 
-The latter form is just a short-hand of `git checkout topic`
-followed by `git rebase master`.
+*NOTE:* The latter form is just a short-hand of `git checkout topic`
+followed by `git rebase master`. When rebase exits `topic` will
+remain the checked-out branch.
 
 If the upstream branch already contains a change you have made (e.g.,
 because you mailed a patch which was applied upstream), then that commit
@@ -170,8 +176,8 @@
 part of topicA.  Note that the argument to --onto and the <upstream>
 parameter can be any valid commit-ish.
 
-In case of conflict, 'git-rebase' will stop at the first problematic commit
-and leave conflict markers in the tree.  You can use 'git-diff' to locate
+In case of conflict, 'git rebase' will stop at the first problematic commit
+and leave conflict markers in the tree.  You can use 'git diff' to locate
 the markers (<<<<<<) and make edits to resolve the conflict.  For each
 file you edit, you need to tell git that the conflict has been resolved,
 typically this would be done with
@@ -187,7 +193,7 @@
     git rebase --continue
 
 
-Alternatively, you can undo the 'git-rebase' with
+Alternatively, you can undo the 'git rebase' with
 
 
     git rebase --abort
@@ -199,17 +205,25 @@
 	Whether to show a diffstat of what changed upstream since the last
 	rebase. False by default.
 
+rebase.autosquash::
+	If set to true enable '--autosquash' option by default.
+
 OPTIONS
 -------
-<newbase>::
+--onto <newbase>::
 	Starting point at which to create the new commits. If the
 	--onto option is not specified, the starting point is
 	<upstream>.  May be any valid commit, and not just an
 	existing branch name.
++
+As a special case, you may use "A\...B" as a shortcut for the
+merge base of A and B if there is exactly one merge base. You can
+leave out at most one of A and B, in which case it defaults to HEAD.
 
 <upstream>::
 	Upstream branch to compare against.  May be any valid commit,
-	not just an existing branch name.
+	not just an existing branch name. Defaults to the configured
+	upstream for the current branch.
 
 <branch>::
 	Working branch; defaults to HEAD.
@@ -218,7 +232,15 @@
 	Restart the rebasing process after having resolved a merge conflict.
 
 --abort::
-	Restore the original branch and abort the rebase operation.
+	Abort the rebase operation and reset HEAD to the original
+	branch. If <branch> was provided when the rebase operation was
+	started, then HEAD will be reset to <branch>. Otherwise HEAD
+	will be reset to where it was when the rebase operation was
+	started.
+
+--keep-empty::
+	Keep the commits that do not change anything from its
+	parents in the result.
 
 --skip::
 	Restart the rebasing process by skipping the current patch.
@@ -228,13 +250,30 @@
 	Use merging strategies to rebase.  When the recursive (default) merge
 	strategy is used, this allows rebase to be aware of renames on the
 	upstream side.
++
+Note that a rebase merge works by replaying each commit from the working
+branch on top of the <upstream> branch.  Because of this, when a merge
+conflict happens, the side reported as 'ours' is the so-far rebased
+series, starting with <upstream>, and 'theirs' is the working branch.  In
+other words, the sides are swapped.
 
 -s <strategy>::
 --strategy=<strategy>::
 	Use the given merge strategy.
-	If there is no `-s` option, a built-in list of strategies
-	is used instead ('git-merge-recursive' when merging a single
-	head, 'git-merge-octopus' otherwise).  This implies --merge.
+	If there is no `-s` option 'git merge-recursive' is used
+	instead.  This implies --merge.
++
+Because 'git rebase' replays each commit from the working branch
+on top of the <upstream> branch using the given strategy, using
+the 'ours' strategy simply discards all patches from the <branch>,
+which makes little sense.
+
+-X <strategy-option>::
+--strategy-option=<strategy-option>::
+	Pass the <strategy-option> through to the merge strategy.
+	This implies `--merge` and, if no strategy has been
+	specified, `-s recursive`.  Note the reversal of 'ours' and
+	'theirs' as noted above for the `-m` option.
 
 -q::
 --quiet::
@@ -255,6 +294,10 @@
 --no-verify::
 	This option bypasses the pre-rebase hook.  See also linkgit:githooks[5].
 
+--verify::
+	Allows the pre-rebase hook to run, which is the default.  This option can
+	be used to override --no-verify.  See also linkgit:githooks[5].
+
 -C<n>::
 	Ensure at least <n> lines of surrounding context match before
 	and after each change.  When fewer lines of surrounding
@@ -264,19 +307,28 @@
 -f::
 --force-rebase::
 	Force the rebase even if the current branch is a descendant
-	of the commit you are rebasing onto.  Normally the command will
+	of the commit you are rebasing onto.  Normally non-interactive rebase will
 	exit with the message "Current branch is up to date" in such a
 	situation.
+	Incompatible with the --interactive option.
++
+You may find this (or --no-ff with an interactive rebase) helpful after
+reverting a topic branch merge, as this option recreates the topic branch with
+fresh commits so it can be remerged successfully without needing to "revert
+the reversion" (see the
+link:howto/revert-a-faulty-merge.txt[revert-a-faulty-merge How-To] for details).
 
+--ignore-whitespace::
 --whitespace=<option>::
-	This flag is passed to the 'git-apply' program
+	These flag are passed to the 'git apply' program
 	(see linkgit:git-apply[1]) that applies the patch.
 	Incompatible with the --interactive option.
 
 --committer-date-is-author-date::
 --ignore-date::
-	These flags are passed to 'git-am' to easily change the dates
+	These flags are passed to 'git am' to easily change the dates
 	of the rebased commits (see linkgit:git-am[1]).
+	Incompatible with the --interactive option.
 
 -i::
 --interactive::
@@ -287,22 +339,76 @@
 -p::
 --preserve-merges::
 	Instead of ignoring merges, try to recreate them.
++
+This uses the `--interactive` machinery internally, but combining it
+with the `--interactive` option explicitly is generally not a good
+idea unless you know what you are doing (see BUGS below).
+
+-x <cmd>::
+--exec <cmd>::
+	Append "exec <cmd>" after each line creating a commit in the
+	final history. <cmd> will be interpreted as one or more shell
+	commands.
++
+This option can only be used with the `--interactive` option
+(see INTERACTIVE MODE below).
++
+You may execute several commands by either using one instance of `--exec`
+with several commands:
++
+	git rebase -i --exec "cmd1 && cmd2 && ..."
++
+or by giving more than one `--exec`:
++
+	git rebase -i --exec "cmd1" --exec "cmd2" --exec ...
++
+If `--autosquash` is used, "exec" lines will not be appended for
+the intermediate commits, and will only appear at the end of each
+squash/fixup series.
 
 --root::
 	Rebase all commits reachable from <branch>, instead of
 	limiting them with an <upstream>.  This allows you to rebase
-	the root commit(s) on a branch.  Must be used with --onto, and
+	the root commit(s) on a branch.  When used with --onto, it
 	will skip changes already contained in <newbase> (instead of
-	<upstream>).  When used together with --preserve-merges, 'all'
-	root commits will be rewritten to have <newbase> as parent
+	<upstream>) whereas without --onto it will operate on every change.
+	When used together with both --onto and --preserve-merges,
+	'all' root commits will be rewritten to have <newbase> as parent
 	instead.
 
+--autosquash::
+--no-autosquash::
+	When the commit log message begins with "squash! ..." (or
+	"fixup! ..."), and there is a commit whose title begins with
+	the same ..., automatically modify the todo list of rebase -i
+	so that the commit marked for squashing comes right after the
+	commit to be modified, and change the action of the moved
+	commit from `pick` to `squash` (or `fixup`).
++
+This option is only valid when the '--interactive' option is used.
++
+If the '--autosquash' option is enabled by default using the
+configuration variable `rebase.autosquash`, this option can be
+used to override and disable this setting.
+
+--no-ff::
+	With --interactive, cherry-pick all rebased commits instead of
+	fast-forwarding over the unchanged ones.  This ensures that the
+	entire history of the rebased branch is composed of new commits.
++
+Without --interactive, this is a synonym for --force-rebase.
++
+You may find this helpful after reverting a topic branch merge, as this option
+recreates the topic branch with fresh commits so it can be remerged
+successfully without needing to "revert the reversion" (see the
+link:howto/revert-a-faulty-merge.txt[revert-a-faulty-merge How-To] for details).
+
 include::merge-strategies.txt[]
 
 NOTES
 -----
 
-You should understand the implications of using 'git-rebase' on a
+You should understand the implications of using 'git rebase' on a
 repository that you share.  See also RECOVERING FROM UPSTREAM REBASE
 below.
 
@@ -329,10 +435,13 @@
 
 where point 2. consists of several instances of
 
-a. regular use
+a) regular use
+
  1. finish something worthy of a commit
  2. commit
-b. independent fixup
+
+b) independent fixup
+
  1. realize that something does not work
  2. fix that
  3. commit it
@@ -358,27 +467,33 @@
 ...
 -------------------------------------------
 
-The oneline descriptions are purely for your pleasure; 'git-rebase' will
+The oneline descriptions are purely for your pleasure; 'git rebase' will
 not look at them but at the commit names ("deadbee" and "fa1afe1" in this
 example), so do not delete or edit the names.
 
 By replacing the command "pick" with the command "edit", you can tell
-'git-rebase' to stop after applying that commit, so that you can edit
+'git rebase' to stop after applying that commit, so that you can edit
 the files and/or the commit message, amend the commit, and continue
 rebasing.
 
-If you want to fold two or more commits into one, replace the command
-"pick" with "squash" for the second and subsequent commit.  If the
-commits had different authors, it will attribute the squashed commit to
-the author of the first commit.
+If you just want to edit the commit message for a commit, replace the
+command "pick" with the command "reword".
 
-In both cases, or when a "pick" does not succeed (because of merge
-errors), the loop will stop to let you fix things, and you can continue
-the loop with `git rebase --continue`.
+If you want to fold two or more commits into one, replace the command
+"pick" for the second and subsequent commits with "squash" or "fixup".
+If the commits had different authors, the folded commit will be
+attributed to the author of the first commit.  The suggested commit
+message for the folded commit is the concatenation of the commit
+messages of the first commit and of those with the "squash" command,
+but omits the commit messages of commits with the "fixup" command.
+
+'git rebase' will stop when "pick" has been replaced with "edit" or
+when a command fails due to merge errors. When you are done editing
+and/or resolving conflicts you can continue with `git rebase --continue`.
 
 For example, if you want to reorder the last 5 commits, such that what
 was HEAD~4 becomes the new HEAD. To achieve that, you would call
-'git-rebase' like this:
+'git rebase' like this:
 
 ----------------------
 $ git rebase -i HEAD~5
@@ -403,12 +518,54 @@
 $ git rebase -i -p --onto Q O
 -----------------------------
 
+Reordering and editing commits usually creates untested intermediate
+steps.  You may want to check that your history editing did not break
+anything by running a test, or at least recompiling at intermediate
+points in history by using the "exec" command (shortcut "x").  You may
+do so by creating a todo list like this one:
+
+-------------------------------------------
+pick deadbee Implement feature XXX
+fixup f1a5c00 Fix to feature XXX
+exec make
+pick c0ffeee The oneline of the next commit
+edit deadbab The oneline of the commit after
+exec cd subdir; make test
+...
+-------------------------------------------
+
+The interactive rebase will stop when a command fails (i.e. exits with
+non-0 status) to give you an opportunity to fix the problem. You can
+continue with `git rebase --continue`.
+
+The "exec" command launches the command in a shell (the one specified
+in `$SHELL`, or the default shell if `$SHELL` is not set), so you can
+use shell features (like "cd", ">", ";" ...). The command is run from
+the root of the working tree.
+
+----------------------------------
+$ git rebase -i --exec "make test"
+----------------------------------
+
+This command lets you check that intermediate commits are compilable.
+The todo list becomes like that:
+
+--------------------
+pick 5928aea one
+exec make test
+pick 04d0fda two
+exec make test
+pick ba46169 three
+exec make test
+pick f4593f9 four
+exec make test
+--------------------
 
 SPLITTING COMMITS
 -----------------
 
 In interactive mode, you can mark commits with the action "edit".  However,
-this does not necessarily mean that 'git-rebase' expects the result of this
+this does not necessarily mean that 'git rebase' expects the result of this
 edit to be exactly one commit.  Indeed, you can undo the commit, or you can
 add other commits.  This can be used to split a commit into two:
 
@@ -424,7 +581,7 @@
 
 - Now add the changes to the index that you want to have in the first
   commit.  You can use `git add` (possibly interactively) or
-  'git-gui' (or both) to do that.
+  'git gui' (or both) to do that.
 
 - Commit the now-current index with whatever commit message is appropriate
   now.
@@ -435,7 +592,7 @@
 
 If you are not absolutely sure that the intermediate revisions are
 consistent (they compile, pass the testsuite, etc.) you should use
-'git-stash' to stash away the not-yet-committed changes
+'git stash' to stash away the not-yet-committed changes
 after each commit, test, and amend the commit if fixes are necessary.
 
 
@@ -498,8 +655,8 @@
 Hard case: The changes are not the same.::
 
 	This happens if the 'subsystem' rebase had conflicts, or used
-	`\--interactive` to omit, edit, or squash commits; or if the
-	upstream used one of `commit \--amend`, `reset`, or
+	`--interactive` to omit, edit, squash, or fixup commits; or
+	if the upstream used one of `commit --amend`, `reset`, or
 	`filter-branch`.
 
 
@@ -510,7 +667,7 @@
 'subsystem' are literally the same before and after the rebase
 'subsystem' did.
 
-In that case, the fix is easy because 'git-rebase' knows to skip
+In that case, the fix is easy because 'git rebase' knows to skip
 changes that are already present in the new upstream.  So if you say
 (assuming you're on 'topic')
 ------------
@@ -535,15 +692,15 @@
 NOTE: While an "easy case recovery" sometimes appears to be successful
       even in the hard case, it may have unintended consequences.  For
       example, a commit that was removed via `git rebase
-      \--interactive` will be **resurrected**!
+      --interactive` will be **resurrected**!
 
-The idea is to manually tell 'git-rebase' "where the old 'subsystem'
+The idea is to manually tell 'git rebase' "where the old 'subsystem'
 ended and your 'topic' began", that is, what the old merge-base
 between them was.  You will have to find a way to name the last commit
 of the old 'subsystem', for example:
 
-* With the 'subsystem' reflog: after 'git-fetch', the old tip of
-  'subsystem' is at `subsystem@\{1}`.  Subsequent fetches will
+* With the 'subsystem' reflog: after 'git fetch', the old tip of
+  'subsystem' is at `subsystem@{1}`.  Subsequent fetches will
   increase the number.  (See linkgit:git-reflog[1].)
 
 * Relative to the tip of 'topic': knowing that your 'topic' has three
@@ -559,15 +716,27 @@
 'everyone' downstream from 'topic' will now have to perform a "hard
 case" recovery too!
 
+BUGS
+----
+The todo list presented by `--preserve-merges --interactive` does not
+represent the topology of the revision graph.  Editing commits and
+rewording their commit messages should work fine, but attempts to
+reorder commits tend to produce counterintuitive results.
 
-Authors
-------
-Written by Junio C Hamano <gitster@pobox.com> and
-Johannes E. Schindelin <johannes.schindelin@gmx.de>
-
-Documentation
---------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
+For example, an attempt to rearrange
+------------
+1 --- 2 --- 3 --- 4 --- 5
+------------
+to
+------------
+1 --- 2 --- 4 --- 3 --- 5
+------------
+by moving the "pick 4" line will result in the following history:
+------------
+	3
+       /
+1 --- 2 --- 4 --- 5
+------------
 
 GIT
 ---
diff --git a/Documentation/git-receive-pack.txt b/Documentation/git-receive-pack.txt
index 514f03c..b1f7dc6 100644
--- a/Documentation/git-receive-pack.txt
+++ b/Documentation/git-receive-pack.txt
@@ -8,19 +8,20 @@
 
 SYNOPSIS
 --------
-'git receive-pack' <directory>
+[verse]
+'git-receive-pack' <directory>
 
 DESCRIPTION
 -----------
-Invoked by 'git-send-pack' and updates the repository with the
+Invoked by 'git send-pack' and updates the repository with the
 information fed from the remote end.
 
 This command is usually not invoked directly by the end user.
-The UI for the protocol is on the 'git-send-pack' side, and the
+The UI for the protocol is on the 'git send-pack' side, and the
 program pair is meant to be used to push updates to remote
 repository.  For pull operations, see linkgit:git-fetch-pack[1].
 
-The command allows for creation and fast forwarding of sha1 refs
+The command allows for creation and fast-forwarding of sha1 refs
 (heads/tags) on the remote end (strictly speaking, it is the
 local end 'git-receive-pack' runs, but to the user who is sitting at
 the send-pack end, it is updating the remote.  Confused?)
@@ -149,16 +150,7 @@
 
 SEE ALSO
 --------
-linkgit:git-send-pack[1]
-
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by Junio C Hamano.
+linkgit:git-send-pack[1], linkgit:gitnamespaces[7]
 
 GIT
 ---
diff --git a/Documentation/git-reflog.txt b/Documentation/git-reflog.txt
index 7f7a544..7fe2d22 100644
--- a/Documentation/git-reflog.txt
+++ b/Documentation/git-reflog.txt
@@ -8,6 +8,7 @@
 
 SYNOPSIS
 --------
+[verse]
 'git reflog' <subcommand> <options>
 
 DESCRIPTION
@@ -18,9 +19,7 @@
 [verse]
 'git reflog expire' [--dry-run] [--stale-fix] [--verbose]
 	[--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>...
-+
 'git reflog delete' ref@\{specifier\}...
-+
 'git reflog' ['show'] [log-options] [<ref>]
 
 Reflog is a mechanism to record when the tip of branches are
@@ -40,13 +39,13 @@
 see linkgit:git-log[1].
 
 The reflog is useful in various git commands, to specify the old value
-of a reference. For example, `HEAD@\{2\}` means "where HEAD used to be
-two moves ago", `master@\{one.week.ago\}` means "where master used to
-point to one week ago", and so on. See linkgit:git-rev-parse[1] for
+of a reference. For example, `HEAD@{2}` means "where HEAD used to be
+two moves ago", `master@{one.week.ago}` means "where master used to
+point to one week ago", and so on. See linkgit:gitrevisions[7] for
 more details.
 
 To delete single entries from the reflog, use the subcommand "delete"
-and specify the _exact_ entry (e.g. "`git reflog delete master@\{2\}`").
+and specify the _exact_ entry (e.g. "`git reflog delete master@{2}`").
 
 
 OPTIONS
@@ -60,7 +59,7 @@
 	refs.
 +
 This computation involves traversing all the reachable objects, i.e. it
-has the same cost as 'git-prune'.  Fortunately, once this is run, we
+has the same cost as 'git prune'.  Fortunately, once this is run, we
 should not have to ever worry about missing objects, because the current
 prune and pack-objects know about reflogs and protect objects referred by
 them.
@@ -92,14 +91,6 @@
 --verbose::
 	Print extra information on screen.
 
-Author
-------
-Written by Junio C Hamano <gitster@pobox.com>
-
-Documentation
---------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-relink.txt b/Documentation/git-relink.txt
index 25ff8f9..3b33c99 100644
--- a/Documentation/git-relink.txt
+++ b/Documentation/git-relink.txt
@@ -7,7 +7,8 @@
 
 SYNOPSIS
 --------
-'git relink' [--safe] <dir> [<dir>]\* <master_dir>
+[verse]
+'git relink' [--safe] <dir>... <master_dir>
 
 DESCRIPTION
 -----------
@@ -24,14 +25,6 @@
 <dir>::
 	Directories containing a .git/objects/ subdirectory.
 
-Author
-------
-Written by Ryan Anderson <ryan@michonline.com>
-
-Documentation
---------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-remote-ext.txt b/Documentation/git-remote-ext.txt
new file mode 100644
index 0000000..8a8e1d7
--- /dev/null
+++ b/Documentation/git-remote-ext.txt
@@ -0,0 +1,126 @@
+git-remote-ext(1)
+=================
+
+NAME
+----
+git-remote-ext - Bridge smart transport to external command.
+
+SYNOPSIS
+--------
+[verse]
+git remote add <nick> "ext::<command>[ <arguments>...]"
+
+DESCRIPTION
+-----------
+This remote helper uses the specified '<command>' to connect
+to a remote git server.
+
+Data written to stdin of the specified '<command>' is assumed
+to be sent to a git:// server, git-upload-pack, git-receive-pack
+or git-upload-archive (depending on situation), and data read
+from stdout of <command> is assumed to be received from
+the same service.
+
+Command and arguments are separated by an unescaped space.
+
+The following sequences have a special meaning:
+
+'% '::
+	Literal space in command or argument.
+
+'%%'::
+	Literal percent sign.
+
+'%s'::
+	Replaced with name (receive-pack, upload-pack, or
+	upload-archive) of the service git wants to invoke.
+
+'%S'::
+	Replaced with long name (git-receive-pack,
+	git-upload-pack, or git-upload-archive) of the service
+	git wants to invoke.
+
+'%G' (must be the first characters in an argument)::
+	This argument will not be passed to '<command>'. Instead, it
+	will cause the helper to start by sending git:// service requests to
+	the remote side with the service field set to an appropriate value and
+	the repository field set to rest of the argument. Default is not to send
+	such a request.
++
+This is useful if remote side is git:// server accessed over
+some tunnel.
+
+'%V' (must be first characters in argument)::
+	This argument will not be passed to '<command>'. Instead it sets
+	the vhost field in the git:// service request (to rest of the argument).
+	Default is not to send vhost in such request (if sent).
+
+ENVIRONMENT VARIABLES:
+----------------------
+
+GIT_TRANSLOOP_DEBUG::
+	If set, prints debugging information about various reads/writes.
+
+ENVIRONMENT VARIABLES PASSED TO COMMAND:
+----------------------------------------
+
+GIT_EXT_SERVICE::
+	Set to long name (git-upload-pack, etc...) of service helper needs
+	to invoke.
+
+GIT_EXT_SERVICE_NOPREFIX::
+	Set to long name (upload-pack, etc...) of service helper needs
+	to invoke.
+
+
+EXAMPLES:
+---------
+This remote helper is transparently used by git when
+you use commands such as "git fetch <URL>", "git clone <URL>",
+, "git push <URL>" or "git remote add <nick> <URL>", where <URL>
+begins with `ext::`.  Examples:
+
+"ext::ssh -i /home/foo/.ssh/somekey user&#64;host.example %S 'foo/repo'"::
+	Like host.example:foo/repo, but use /home/foo/.ssh/somekey as
+	keypair and user as user on remote side. This avoids needing to
+	edit .ssh/config.
+
+"ext::socat -t3600 - ABSTRACT-CONNECT:/git-server %G/somerepo"::
+	Represents repository with path /somerepo accessable over
+	git protocol at abstract namespace address /git-server.
+
+"ext::git-server-alias foo %G/repo"::
+	Represents a repository with path /repo accessed using the
+	helper program "git-server-alias foo".  The path to the
+	repository and type of request are not passed on the command
+	line but as part of the protocol stream, as usual with git://
+	protocol.
+
+"ext::git-server-alias foo %G/repo %Vfoo"::
+	Represents a repository with path /repo accessed using the
+	helper program "git-server-alias foo".  The hostname for the
+	remote server passed in the protocol stream will be "foo"
+	(this allows multiple virtual git servers to share a
+	link-level address).
+
+"ext::git-server-alias foo %G/repo% with% spaces %Vfoo"::
+	Represents a repository with path '/repo with spaces' accessed
+	using the helper program "git-server-alias foo".  The hostname for
+	the remote server passed in the protocol stream will be "foo"
+	(this allows multiple virtual git servers to share a
+	link-level address).
+
+"ext::git-ssl foo.example /bar"::
+	Represents a repository accessed using the helper program
+	"git-ssl foo.example /bar".  The type of request can be
+	determined by the helper using environment variables (see
+	above).
+
+Documentation
+--------------
+Documentation by Ilari Liusvaara, Jonathan Nieder and the git list
+<git@vger.kernel.org>
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-remote-fd.txt b/Documentation/git-remote-fd.txt
new file mode 100644
index 0000000..f095d57
--- /dev/null
+++ b/Documentation/git-remote-fd.txt
@@ -0,0 +1,59 @@
+git-remote-fd(1)
+================
+
+NAME
+----
+git-remote-fd - Reflect smart transport stream back to caller
+
+SYNOPSIS
+--------
+"fd::<infd>[,<outfd>][/<anything>]" (as URL)
+
+DESCRIPTION
+-----------
+This helper uses specified file descriptors to connect to a remote git server.
+This is not meant for end users but for programs and scripts calling git
+fetch, push or archive.
+
+If only <infd> is given, it is assumed to be a bidirectional socket connected
+to remote git server (git-upload-pack, git-receive-pack or
+git-upload-achive). If both <infd> and <outfd> are given, they are assumed
+to be pipes connected to a remote git server (<infd> being the inbound pipe
+and <outfd> being the outbound pipe.
+
+It is assumed that any handshaking procedures have already been completed
+(such as sending service request for git://) before this helper is started.
+
+<anything> can be any string. It is ignored. It is meant for providing
+information to user in the URL in case that URL is displayed in some
+context.
+
+ENVIRONMENT VARIABLES
+---------------------
+GIT_TRANSLOOP_DEBUG::
+	If set, prints debugging information about various reads/writes.
+
+EXAMPLES
+--------
+`git fetch fd::17 master`::
+	Fetch master, using file descriptor #17 to communicate with
+	git-upload-pack.
+
+`git fetch fd::17/foo master`::
+	Same as above.
+
+`git push fd::7,8 master (as URL)`::
+	Push master, using file descriptor #7 to read data from
+	git-receive-pack and file descriptor #8 to write data to
+	same service.
+
+`git push fd::7,8/bar master`::
+	Same as above.
+
+Documentation
+--------------
+Documentation by Ilari Liusvaara and the git list <git@vger.kernel.org>
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-remote-helpers.txt b/Documentation/git-remote-helpers.txt
new file mode 100644
index 0000000..f5836e4
--- /dev/null
+++ b/Documentation/git-remote-helpers.txt
@@ -0,0 +1,369 @@
+git-remote-helpers(1)
+=====================
+
+NAME
+----
+git-remote-helpers - Helper programs to interact with remote repositories
+
+SYNOPSIS
+--------
+[verse]
+'git remote-<transport>' <repository> [<URL>]
+
+DESCRIPTION
+-----------
+
+Remote helper programs are normally not used directly by end users,
+but they are invoked by git when it needs to interact with remote
+repositories git does not support natively.  A given helper will
+implement a subset of the capabilities documented here. When git
+needs to interact with a repository using a remote helper, it spawns
+the helper as an independent process, sends commands to the helper's
+standard input, and expects results from the helper's standard
+output. Because a remote helper runs as an independent process from
+git, there is no need to re-link git to add a new helper, nor any
+need to link the helper with the implementation of git.
+
+Every helper must support the "capabilities" command, which git
+uses to determine what other commands the helper will accept.  Those
+other commands can be used to discover and update remote refs,
+transport objects between the object database and the remote repository,
+and update the local object store.
+
+Git comes with a "curl" family of remote helpers, that handle various
+transport protocols, such as 'git-remote-http', 'git-remote-https',
+'git-remote-ftp' and 'git-remote-ftps'. They implement the capabilities
+'fetch', 'option', and 'push'.
+
+INPUT FORMAT
+------------
+
+Git sends the remote helper a list of commands on standard input, one
+per line.  The first command is always the 'capabilities' command, in
+response to which the remote helper must print a list of the
+capabilities it supports (see below) followed by a blank line.  The
+response to the capabilities command determines what commands Git uses
+in the remainder of the command stream.
+
+The command stream is terminated by a blank line.  In some cases
+(indicated in the documentation of the relevant commands), this blank
+line is followed by a payload in some other protocol (e.g., the pack
+protocol), while in others it indicates the end of input.
+
+Capabilities
+~~~~~~~~~~~~
+
+Each remote helper is expected to support only a subset of commands.
+The operations a helper supports are declared to git in the response
+to the `capabilities` command (see COMMANDS, below).
+
+'option'::
+	For specifying settings like `verbosity` (how much output to
+	write to stderr) and `depth` (how much history is wanted in the
+	case of a shallow clone) that affect how other commands are
+	carried out.
+
+'connect'::
+	For fetching and pushing using git's native packfile protocol
+	that requires a bidirectional, full-duplex connection.
+
+'push'::
+	For listing remote refs and pushing specified objects from the
+	local object store to remote refs.
+
+'fetch'::
+	For listing remote refs and fetching the associated history to
+	the local object store.
+
+'import'::
+	For listing remote refs and fetching the associated history as
+	a fast-import stream.
+
+'refspec' <refspec>::
+	This modifies the 'import' capability, allowing the produced
+	fast-import stream to modify refs in a private namespace
+	instead of writing to refs/heads or refs/remotes directly.
+	It is recommended that all importers providing the 'import'
+	capability use this.
++
+A helper advertising the capability
+`refspec refs/heads/*:refs/svn/origin/branches/*`
+is saying that, when it is asked to `import refs/heads/topic`, the
+stream it outputs will update the `refs/svn/origin/branches/topic`
+ref.
++
+This capability can be advertised multiple times.  The first
+applicable refspec takes precedence.  The left-hand of refspecs
+advertised with this capability must cover all refs reported by
+the list command.  If no 'refspec' capability is advertised,
+there is an implied `refspec *:*`.
+
+Capabilities for Pushing
+~~~~~~~~~~~~~~~~~~~~~~~~
+'connect'::
+	Can attempt to connect to 'git receive-pack' (for pushing),
+	'git upload-pack', etc for communication using the
+	packfile protocol.
++
+Supported commands: 'connect'.
+
+'push'::
+	Can discover remote refs and push local commits and the
+	history leading up to them to new or existing remote refs.
++
+Supported commands: 'list for-push', 'push'.
+
+If a helper advertises both 'connect' and 'push', git will use
+'connect' if possible and fall back to 'push' if the helper requests
+so when connecting (see the 'connect' command under COMMANDS).
+
+Capabilities for Fetching
+~~~~~~~~~~~~~~~~~~~~~~~~~
+'connect'::
+	Can try to connect to 'git upload-pack' (for fetching),
+	'git receive-pack', etc for communication using the
+	packfile protocol.
++
+Supported commands: 'connect'.
+
+'fetch'::
+	Can discover remote refs and transfer objects reachable from
+	them to the local object store.
++
+Supported commands: 'list', 'fetch'.
+
+'import'::
+	Can discover remote refs and output objects reachable from
+	them as a stream in fast-import format.
++
+Supported commands: 'list', 'import'.
+
+If a helper advertises 'connect', git will use it if possible and
+fall back to another capability if the helper requests so when
+connecting (see the 'connect' command under COMMANDS).
+When choosing between 'fetch' and 'import', git prefers 'fetch'.
+Other frontends may have some other order of preference.
+
+'refspec' <refspec>::
+	This modifies the 'import' capability.
++
+A helper advertising
+`refspec refs/heads/*:refs/svn/origin/branches/*`
+in its capabilities is saying that, when it handles
+`import refs/heads/topic`, the stream it outputs will update the
+`refs/svn/origin/branches/topic` ref.
++
+This capability can be advertised multiple times.  The first
+applicable refspec takes precedence.  The left-hand of refspecs
+advertised with this capability must cover all refs reported by
+the list command.  If no 'refspec' capability is advertised,
+there is an implied `refspec *:*`.
+
+INVOCATION
+----------
+
+Remote helper programs are invoked with one or (optionally) two
+arguments. The first argument specifies a remote repository as in git;
+it is either the name of a configured remote or a URL. The second
+argument specifies a URL; it is usually of the form
+'<transport>://<address>', but any arbitrary string is possible.
+The 'GIT_DIR' environment variable is set up for the remote helper
+and can be used to determine where to store additional data or from
+which directory to invoke auxiliary git commands.
+
+When git encounters a URL of the form '<transport>://<address>', where
+'<transport>' is a protocol that it cannot handle natively, it
+automatically invokes 'git remote-<transport>' with the full URL as
+the second argument. If such a URL is encountered directly on the
+command line, the first argument is the same as the second, and if it
+is encountered in a configured remote, the first argument is the name
+of that remote.
+
+A URL of the form '<transport>::<address>' explicitly instructs git to
+invoke 'git remote-<transport>' with '<address>' as the second
+argument. If such a URL is encountered directly on the command line,
+the first argument is '<address>', and if it is encountered in a
+configured remote, the first argument is the name of that remote.
+
+Additionally, when a configured remote has 'remote.<name>.vcs' set to
+'<transport>', git explicitly invokes 'git remote-<transport>' with
+'<name>' as the first argument. If set, the second argument is
+'remote.<name>.url'; otherwise, the second argument is omitted.
+
+COMMANDS
+--------
+
+Commands are given by the caller on the helper's standard input, one per line.
+
+'capabilities'::
+	Lists the capabilities of the helper, one per line, ending
+	with a blank line. Each capability may be preceded with '*',
+	which marks them mandatory for git version using the remote
+	helper to understand (unknown mandatory capability is fatal
+	error).
+
+'list'::
+	Lists the refs, one per line, in the format "<value> <name>
+	[<attr> ...]". The value may be a hex sha1 hash, "@<dest>" for
+	a symref, or "?" to indicate that the helper could not get the
+	value of the ref. A space-separated list of attributes follows
+	the name; unrecognized attributes are ignored. The list ends
+	with a blank line.
++
+If 'push' is supported this may be called as 'list for-push'
+to obtain the current refs prior to sending one or more 'push'
+commands to the helper.
+
+'option' <name> <value>::
+	Sets the transport helper option <name> to <value>.  Outputs a
+	single line containing one of 'ok' (option successfully set),
+	'unsupported' (option not recognized) or 'error <msg>'
+	(option <name> is supported but <value> is not valid
+	for it).  Options should be set before other commands,
+	and may influence the behavior of those commands.
++
+Supported if the helper has the "option" capability.
+
+'fetch' <sha1> <name>::
+	Fetches the given object, writing the necessary objects
+	to the database.  Fetch commands are sent in a batch, one
+	per line, terminated with a blank line.
+	Outputs a single blank line when all fetch commands in the
+	same batch are complete. Only objects which were reported
+	in the ref list with a sha1 may be fetched this way.
++
+Optionally may output a 'lock <file>' line indicating a file under
+GIT_DIR/objects/pack which is keeping a pack until refs can be
+suitably updated.
++
+Supported if the helper has the "fetch" capability.
+
+'push' +<src>:<dst>::
+	Pushes the given local <src> commit or branch to the
+	remote branch described by <dst>.  A batch sequence of
+	one or more 'push' commands is terminated with a blank line
+	(if there is only one reference to push, a single 'push' command
+	is followed by a blank line). For example, the following would
+	be two batches of 'push', the first asking the remote-helper
+	to push the local ref 'master' to the remote ref 'master' and
+	the local 'HEAD' to the remote 'branch', and the second
+	asking to push ref 'foo' to ref 'bar' (forced update requested
+	by the '+').
++
+------------
+push refs/heads/master:refs/heads/master
+push HEAD:refs/heads/branch
+\n
+push +refs/heads/foo:refs/heads/bar
+\n
+------------
++
+Zero or more protocol options may be entered after the last 'push'
+command, before the batch's terminating blank line.
++
+When the push is complete, outputs one or more 'ok <dst>' or
+'error <dst> <why>?' lines to indicate success or failure of
+each pushed ref.  The status report output is terminated by
+a blank line.  The option field <why> may be quoted in a C
+style string if it contains an LF.
++
+Supported if the helper has the "push" capability.
+
+'import' <name>::
+	Produces a fast-import stream which imports the current value
+	of the named ref. It may additionally import other refs as
+	needed to construct the history efficiently. The script writes
+	to a helper-specific private namespace. The value of the named
+	ref should be written to a location in this namespace derived
+	by applying the refspecs from the "refspec" capability to the
+	name of the ref.
++
+Especially useful for interoperability with a foreign versioning
+system.
++
+Just like 'push', a batch sequence of one or more 'import' is
+terminated with a blank line. For each batch of 'import', the remote
+helper should produce a fast-import stream terminated by a 'done'
+command.
++
+Supported if the helper has the "import" capability.
+
+'connect' <service>::
+	Connects to given service. Standard input and standard output
+	of helper are connected to specified service (git prefix is
+	included in service name so e.g. fetching uses 'git-upload-pack'
+	as service) on remote side. Valid replies to this command are
+	empty line (connection established), 'fallback' (no smart
+	transport support, fall back to dumb transports) and just
+	exiting with error message printed (can't connect, don't
+	bother trying to fall back). After line feed terminating the
+	positive (empty) response, the output of service starts. After
+	the connection ends, the remote helper exits.
++
+Supported if the helper has the "connect" capability.
+
+If a fatal error occurs, the program writes the error message to
+stderr and exits. The caller should expect that a suitable error
+message has been printed if the child closes the connection without
+completing a valid response for the current command.
+
+Additional commands may be supported, as may be determined from
+capabilities reported by the helper.
+
+REF LIST ATTRIBUTES
+-------------------
+
+'for-push'::
+	The caller wants to use the ref list to prepare push
+	commands.  A helper might chose to acquire the ref list by
+	opening a different type of connection to the destination.
+
+'unchanged'::
+	This ref is unchanged since the last import or fetch, although
+	the helper cannot necessarily determine what value that produced.
+
+OPTIONS
+-------
+'option verbosity' <n>::
+	Changes the verbosity of messages displayed by the helper.
+	A value of 0 for <n> means that processes operate
+	quietly, and the helper produces only error output.
+	1 is the default level of verbosity, and higher values
+	of <n> correspond to the number of -v flags passed on the
+	command line.
+
+'option progress' \{'true'|'false'\}::
+	Enables (or disables) progress messages displayed by the
+	transport helper during a command.
+
+'option depth' <depth>::
+	Deepens the history of a shallow repository.
+
+'option followtags' \{'true'|'false'\}::
+	If enabled the helper should automatically fetch annotated
+	tag objects if the object the tag points at was transferred
+	during the fetch command.  If the tag is not fetched by
+	the helper a second fetch command will usually be sent to
+	ask for the tag specifically.  Some helpers may be able to
+	use this option to avoid a second network connection.
+
+'option dry-run' \{'true'|'false'\}:
+	If true, pretend the operation completed successfully,
+	but don't actually change any repository data.	For most
+	helpers this only applies to the 'push', if supported.
+
+'option servpath <c-style-quoted-path>'::
+	Sets service path (--upload-pack, --receive-pack etc.) for
+	next connect. Remote helper may support this option, but
+	must not rely on this option being set before
+	connect request occurs.
+
+SEE ALSO
+--------
+linkgit:git-remote[1]
+
+linkgit:git-remote-testgit[1]
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-remote-testgit.txt b/Documentation/git-remote-testgit.txt
new file mode 100644
index 0000000..2a67d45
--- /dev/null
+++ b/Documentation/git-remote-testgit.txt
@@ -0,0 +1,30 @@
+git-remote-testgit(1)
+=====================
+
+NAME
+----
+git-remote-testgit - Example remote-helper
+
+
+SYNOPSIS
+--------
+[verse]
+git clone testgit::<source-repo> [<destination>]
+
+DESCRIPTION
+-----------
+
+This command is a simple remote-helper, that is used both as a
+testcase for the remote-helper functionality, and as an example to
+show remote-helper authors one possible implementation.
+
+The best way to learn more is to read the comments and source code in
+'git-remote-testgit.py'.
+
+SEE ALSO
+--------
+linkgit:git-remote-helpers[1]
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt
index 9e2b4ea..a308f4c 100644
--- a/Documentation/git-remote.txt
+++ b/Documentation/git-remote.txt
@@ -10,13 +10,17 @@
 --------
 [verse]
 'git remote' [-v | --verbose]
-'git remote add' [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url>
+'git remote add' [-t <branch>] [-m <master>] [-f] [--tags|--no-tags] [--mirror=<fetch|push>] <name> <url>
 'git remote rename' <old> <new>
 'git remote rm' <name>
-'git remote set-head' <name> [-a | -d | <branch>]
-'git remote show' [-n] <name>
+'git remote set-head' <name> (-a | -d | <branch>)
+'git remote set-branches' [--add] <name> <branch>...
+'git remote set-url' [--push] <name> <newurl> [<oldurl>]
+'git remote set-url --add' [--push] <name> <newurl>
+'git remote set-url --delete' [--push] <name> <url>
+'git remote' [-v | --verbose] 'show' [-n] <name>
 'git remote prune' [-n | --dry-run] <name>
-'git remote update' [-p | --prune] [group | remote]...
+'git remote' [-v | --verbose] 'update' [-p | --prune] [(<group> | <remote>)...]
 
 DESCRIPTION
 -----------
@@ -30,6 +34,7 @@
 -v::
 --verbose::
 	Be a little more verbose and show remote url after name.
+	NOTE: This must be placed between `remote` and `subcommand`.
 
 
 COMMANDS
@@ -47,24 +52,33 @@
 With `-f` option, `git fetch <name>` is run immediately after
 the remote information is set up.
 +
+With `--tags` option, `git fetch <name>` imports every tag from the
+remote repository.
++
+With `--no-tags` option, `git fetch <name>` does not import tags from
+the remote repository.
++
 With `-t <branch>` option, instead of the default glob
 refspec for the remote to track all branches under
-`$GIT_DIR/remotes/<name>/`, a refspec to track only `<branch>`
+the `refs/remotes/<name>/` namespace, a refspec to track only `<branch>`
 is created.  You can give more than one `-t <branch>` to track
 multiple branches without grabbing all branches.
 +
-With `-m <master>` option, `$GIT_DIR/remotes/<name>/HEAD` is set
+With `-m <master>` option, a symbolic-ref `refs/remotes/<name>/HEAD` is set
 up to point at remote's `<master>` branch. See also the set-head command.
 +
-In mirror mode, enabled with `\--mirror`, the refs will not be stored
-in the 'refs/remotes/' namespace, but in 'refs/heads/'.  This option
-only makes sense in bare repositories.  If a remote uses mirror
-mode, furthermore, `git push` will always behave as if `\--mirror`
-was passed.
+When a fetch mirror is created with `--mirror=fetch`, the refs will not
+be stored in the 'refs/remotes/' namespace, but rather everything in
+'refs/' on the remote will be directly mirrored into 'refs/' in the
+local repository. This option only makes sense in bare repositories,
+because a fetch would overwrite any local commits.
++
+When a push mirror is created with `--mirror=push`, then `git push`
+will always behave as if `--mirror` was passed.
 
 'rename'::
 
-Rename the remote named <old> to <new>. All remote tracking branches and
+Rename the remote named <old> to <new>. All remote-tracking branches and
 configuration settings for the remote are updated.
 +
 In case <old> and <new> are the same, and <old> is a file under
@@ -73,33 +87,60 @@
 
 'rm'::
 
-Remove the remote named <name>. All remote tracking branches and
+Remove the remote named <name>. All remote-tracking branches and
 configuration settings for the remote are removed.
 
 'set-head'::
 
-Sets or deletes the default branch (`$GIT_DIR/remotes/<name>/HEAD`) for
+Sets or deletes the default branch (i.e. the target of the
+symbolic-ref `refs/remotes/<name>/HEAD`) for
 the named remote. Having a default branch for a remote is not required,
 but allows the name of the remote to be specified in lieu of a specific
 branch. For example, if the default branch for `origin` is set to
 `master`, then `origin` may be specified wherever you would normally
 specify `origin/master`.
 +
-With `-d`, `$GIT_DIR/remotes/<name>/HEAD` is deleted.
+With `-d`, the symbolic ref `refs/remotes/<name>/HEAD` is deleted.
 +
-With `-a`, the remote is queried to determine its `HEAD`, then
-`$GIT_DIR/remotes/<name>/HEAD` is set to the same branch. e.g., if the remote
+With `-a`, the remote is queried to determine its `HEAD`, then the
+symbolic-ref `refs/remotes/<name>/HEAD` is set to the same branch. e.g., if the remote
 `HEAD` is pointed at `next`, "`git remote set-head origin -a`" will set
-`$GIT_DIR/refs/remotes/origin/HEAD` to `refs/remotes/origin/next`. This will
+the symbolic-ref `refs/remotes/origin/HEAD` to `refs/remotes/origin/next`. This will
 only work if `refs/remotes/origin/next` already exists; if not it must be
 fetched first.
 +
-Use `<branch>` to set `$GIT_DIR/remotes/<name>/HEAD` explicitly. e.g., "git
-remote set-head origin master" will set `$GIT_DIR/refs/remotes/origin/HEAD` to
+Use `<branch>` to set the symbolic-ref `refs/remotes/<name>/HEAD` explicitly. e.g., "git
+remote set-head origin master" will set the symbolic-ref `refs/remotes/origin/HEAD` to
 `refs/remotes/origin/master`. This will only work if
 `refs/remotes/origin/master` already exists; if not it must be fetched first.
 +
 
+'set-branches'::
+
+Changes the list of branches tracked by the named remote.
+This can be used to track a subset of the available remote branches
+after the initial setup for a remote.
++
+The named branches will be interpreted as if specified with the
+`-t` option on the 'git remote add' command line.
++
+With `--add`, instead of replacing the list of currently tracked
+branches, adds to that list.
+
+'set-url'::
+
+Changes URL remote points to. Sets first URL remote points to matching
+regex <oldurl> (first URL if no <oldurl> is given) to <newurl>. If
+<oldurl> doesn't match any URL, error occurs and nothing is changed.
++
+With '--push', push URLs are manipulated instead of fetch URLs.
++
+With '--add', instead of changing some URL, new URL is added.
++
+With '--delete', instead of changing some URL, all URLs matching
+regex <url> are deleted. Trying to delete all non-push URLs is an
+error.
+
 'show'::
 
 Gives some information about the remote <name>.
@@ -109,19 +150,19 @@
 
 'prune'::
 
-Deletes all stale tracking branches under <name>.
+Deletes all stale remote-tracking branches under <name>.
 These stale branches have already been removed from the remote repository
 referenced by <name>, but are still locally available in
 "remotes/<name>".
 +
-With `--dry-run` option, report what branches will be pruned, but do no
+With `--dry-run` option, report what branches will be pruned, but do not
 actually prune them.
 
 'update'::
 
 Fetch updates for a named set of remotes in the repository as defined by
 remotes.<group>.  If a named group is not specified on the command line,
-the configuration parameter remotes.default will get used; if
+the configuration parameter remotes.default will be used; if
 remotes.default is not defined, all remotes which do not have the
 configuration parameter remote.<name>.skipDefaultUpdate set to true will
 be updated.  (See linkgit:git-config[1]).
@@ -160,7 +201,7 @@
 ...
 ------------
 
-* Imitate 'git-clone' but track only selected branches
+* Imitate 'git clone' but track only selected branches
 +
 ------------
 $ mkdir project.git
@@ -177,16 +218,6 @@
 linkgit:git-branch[1]
 linkgit:git-config[1]
 
-Author
-------
-Written by Junio Hamano
-
-
-Documentation
---------------
-Documentation by J. Bruce Fields and the git-list <git@vger.kernel.org>.
-
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt
index c9257a1..4c1aff6 100644
--- a/Documentation/git-repack.txt
+++ b/Documentation/git-repack.txt
@@ -8,7 +8,8 @@
 
 SYNOPSIS
 --------
-'git repack' [-a] [-A] [-d] [-f] [-l] [-n] [-q] [--window=N] [--depth=N]
+[verse]
+'git repack' [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [--window=<n>] [--depth=<n>]
 
 DESCRIPTION
 -----------
@@ -33,7 +34,7 @@
 	Especially useful when packing a repository that is used
 	for private development. Use
 	with '-d'.  This will clean up the objects that `git prune`
-	leaves behind, but `git fsck --full` shows as
+	leaves behind, but `git fsck --full --dangling` shows as
 	dangling.
 +
 Note that users fetching over dumb protocols will have to fetch the
@@ -49,35 +50,39 @@
 	deleted by way of being left in the old pack and then
 	removed.  Instead, the loose unreachable objects
 	will be pruned according to normal expiry rules
-	with the next 'git-gc' invocation. See linkgit:git-gc[1].
+	with the next 'git gc' invocation. See linkgit:git-gc[1].
 
 -d::
 	After packing, if the newly created packs make some
 	existing packs redundant, remove the redundant packs.
-	Also run  'git-prune-packed' to remove redundant
+	Also run  'git prune-packed' to remove redundant
 	loose object files.
 
 -l::
-	Pass the `--local` option to 'git-pack-objects'. See
+	Pass the `--local` option to 'git pack-objects'. See
 	linkgit:git-pack-objects[1].
 
 -f::
+	Pass the `--no-reuse-delta` option to `git-pack-objects`, see
+	linkgit:git-pack-objects[1].
+
+-F::
 	Pass the `--no-reuse-object` option to `git-pack-objects`, see
 	linkgit:git-pack-objects[1].
 
 -q::
-	Pass the `-q` option to 'git-pack-objects'. See
+	Pass the `-q` option to 'git pack-objects'. See
 	linkgit:git-pack-objects[1].
 
 -n::
 	Do not update the server information with
-	'git-update-server-info'.  This option skips
+	'git update-server-info'.  This option skips
 	updating local catalog files needed to publish
 	this repository (or a direct copy of it)
 	over HTTP or FTP.  See linkgit:git-update-server-info[1].
 
---window=[N]::
---depth=[N]::
+--window=<n>::
+--depth=<n>::
 	These two options affect how the objects contained in the pack are
 	stored using delta compression. The objects are first internally
 	sorted by type, size and optionally names and compared against the
@@ -87,10 +92,10 @@
 	to be applied that many times to get to the necessary object.
 	The default value for --window is 10 and --depth is 50.
 
---window-memory=[N]::
+--window-memory=<n>::
 	This option provides an additional limit on top of `--window`;
 	the window size will dynamically scale down so as to not take
-	up more than N bytes in memory.  This is useful in
+	up more than '<n>' bytes in memory.  This is useful in
 	repositories with a mix of large and small objects to not run
 	out of memory with a large window, but still be able to take
 	advantage of the large window for the smaller objects.  The
@@ -99,32 +104,25 @@
 	default.
 
 --max-pack-size=<n>::
-	Maximum size of each output packfile, expressed in MiB.
+	Maximum size of each output pack file. The size can be suffixed with
+	"k", "m", or "g". The minimum size allowed is limited to 1 MiB.
 	If specified,  multiple packfiles may be created.
-	The default is unlimited.
+	The default is unlimited, unless the config variable
+	`pack.packSizeLimit` is set.
 
 
 Configuration
 -------------
 
-When configuration variable `repack.UseDeltaBaseOffset` is set
-for the repository, the command passes `--delta-base-offset`
-option to 'git-pack-objects'; this typically results in slightly
-smaller packs, but the generated packs are incompatible with
-versions of git older than (and including) v1.4.3; do not set
-the variable in a repository that older version of git needs to
-be able to read (this includes repositories from which packs can
-be copied out over http or rsync, and people who obtained packs
-that way can try to use older git with it).
-
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by Ryan Anderson <ryan@michonline.com>
+By default, the command passes `--delta-base-offset` option to
+'git pack-objects'; this typically results in slightly smaller packs,
+but the generated packs are incompatible with versions of Git older than
+version 1.4.4. If you need to share your repository with such ancient Git
+versions, either directly or via the dumb http or rsync protocol, then you
+need to set the configuration variable `repack.UseDeltaBaseOffset` to
+"false" and repack. Access from old Git versions over the native protocol
+is unaffected by this option as the conversion is performed on the fly
+as needed in that case.
 
 SEE ALSO
 --------
diff --git a/Documentation/git-replace.txt b/Documentation/git-replace.txt
new file mode 100644
index 0000000..17df525
--- /dev/null
+++ b/Documentation/git-replace.txt
@@ -0,0 +1,85 @@
+git-replace(1)
+==============
+
+NAME
+----
+git-replace - Create, list, delete refs to replace objects
+
+SYNOPSIS
+--------
+[verse]
+'git replace' [-f] <object> <replacement>
+'git replace' -d <object>...
+'git replace' -l [<pattern>]
+
+DESCRIPTION
+-----------
+Adds a 'replace' reference in `.git/refs/replace/`
+
+The name of the 'replace' reference is the SHA1 of the object that is
+replaced. The content of the 'replace' reference is the SHA1 of the
+replacement object.
+
+Unless `-f` is given, the 'replace' reference must not yet exist in
+`.git/refs/replace/` directory.
+
+Replacement references will be used by default by all git commands
+except those doing reachability traversal (prune, pack transfer and
+fsck).
+
+It is possible to disable use of replacement references for any
+command using the `--no-replace-objects` option just after 'git'.
+
+For example if commit 'foo' has been replaced by commit 'bar':
+
+------------------------------------------------
+$ git --no-replace-objects cat-file commit foo
+------------------------------------------------
+
+shows information about commit 'foo', while:
+
+------------------------------------------------
+$ git cat-file commit foo
+------------------------------------------------
+
+shows information about commit 'bar'.
+
+The 'GIT_NO_REPLACE_OBJECTS' environment variable can be set to
+achieve the same effect as the `--no-replace-objects` option.
+
+OPTIONS
+-------
+-f::
+	If an existing replace ref for the same object exists, it will
+	be overwritten (instead of failing).
+
+-d::
+	Delete existing replace refs for the given objects.
+
+-l <pattern>::
+	List replace refs for objects that match the given pattern (or
+	all if no pattern is given).
+	Typing "git replace" without arguments, also lists all replace
+	refs.
+
+BUGS
+----
+Comparing blobs or trees that have been replaced with those that
+replace them will not work properly. And using `git reset --hard` to
+go back to a replaced commit will move the branch to the replacement
+commit instead of the replaced commit.
+
+There may be other problems when using 'git rev-list' related to
+pending objects. And of course things may break if an object of one
+type is replaced by an object of another type (for example a blob
+replaced by a commit).
+
+SEE ALSO
+--------
+linkgit:git-tag[1]
+linkgit:git-branch[1]
+linkgit:git[1]
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-repo-config.txt b/Documentation/git-repo-config.txt
index e5bdb55..9ec115b 100644
--- a/Documentation/git-repo-config.txt
+++ b/Documentation/git-repo-config.txt
@@ -8,6 +8,7 @@
 
 SYNOPSIS
 --------
+[verse]
 'git repo-config' ...
 
 
@@ -16,3 +17,7 @@
 
 This is a synonym for linkgit:git-config[1].  Please refer to the
 documentation of that command.
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-request-pull.txt b/Documentation/git-request-pull.txt
index 19335fd..b99681c 100644
--- a/Documentation/git-request-pull.txt
+++ b/Documentation/git-request-pull.txt
@@ -7,7 +7,8 @@
 
 SYNOPSIS
 --------
-'git request-pull' <start> <url> [<end>]
+[verse]
+'git request-pull' [-p] <start> <url> [<end>]
 
 DESCRIPTION
 -----------
@@ -17,6 +18,9 @@
 
 OPTIONS
 -------
+-p::
+	Show patch text
+
 <start>::
 	Commit to start at.
 
@@ -26,14 +30,6 @@
 <end>::
 	Commit to end at; defaults to HEAD.
 
-Author
-------
-Written by Ryan Anderson <ryan@michonline.com> and Junio C Hamano <gitster@pobox.com>
-
-Documentation
---------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-rerere.txt b/Documentation/git-rerere.txt
index 7dd515b..a62227f 100644
--- a/Documentation/git-rerere.txt
+++ b/Documentation/git-rerere.txt
@@ -7,7 +7,8 @@
 
 SYNOPSIS
 --------
-'git rerere' ['clear'|'diff'|'status'|'gc']
+[verse]
+'git rerere' ['clear'|'forget' <pathspec>|'diff'|'remaining'|'status'|'gc']
 
 DESCRIPTION
 -----------
@@ -30,31 +31,41 @@
 COMMANDS
 --------
 
-Normally, 'git-rerere' is run without arguments or user-intervention.
+Normally, 'git rerere' is run without arguments or user-intervention.
 However, it has several commands that allow it to interact with
 its working state.
 
 'clear'::
 
-This resets the metadata used by rerere if a merge resolution is to be
-aborted.  Calling 'git-am [--skip|--abort]' or 'git-rebase [--skip|--abort]'
+Reset the metadata used by rerere if a merge resolution is to be
+aborted.  Calling 'git am [--skip|--abort]' or 'git rebase [--skip|--abort]'
 will automatically invoke this command.
 
+'forget' <pathspec>::
+
+Reset the conflict resolutions which rerere has recorded for the current
+conflict in <pathspec>.
+
 'diff'::
 
-This displays diffs for the current state of the resolution.  It is
+Display diffs for the current state of the resolution.  It is
 useful for tracking what has changed while the user is resolving
 conflicts.  Additional arguments are passed directly to the system
 'diff' command installed in PATH.
 
 'status'::
 
-Like 'diff', but this only prints the filenames that will be tracked
-for resolutions.
+Print paths with conflicts whose merge resolution rerere will record.
+
+'remaining'::
+
+Print paths with conflicts that have not been autoresolved by rerere.
+This includes paths whose resolutions cannot be tracked by rerere,
+such as conflicting submodules.
 
 'gc'::
 
-This prunes records of conflicted merges that
+Prune records of conflicted merges that
 occurred a long time ago.  By default, unresolved conflicts older
 than 15 days and resolved conflicts older than 60
 days are pruned.  These defaults are controlled via the
@@ -90,15 +101,15 @@
 
 The commits marked with `*` touch the same area in the same
 file; you need to resolve the conflicts when creating the commit
-marked with `{plus}`.  Then you can test the result to make sure your
+marked with `+`.  Then you can test the result to make sure your
 work-in-progress still works with what is in the latest master.
 
 After this test merge, there are two ways to continue your work
 on the topic.  The easiest is to build on top of the test merge
-commit `{plus}`, and when your work in the topic branch is finally
+commit `+`, and when your work in the topic branch is finally
 ready, pull the topic branch into master, and/or ask the
 upstream to pull from you.  By that time, however, the master or
-the upstream might have been advanced since the test merge `{plus}`,
+the upstream might have been advanced since the test merge `+`,
 in which case the final commit graph would look like this:
 
 ------------
@@ -142,32 +153,32 @@
 would require you to resolve the conflict, introduced by the
 commits marked with `*`.  However, this conflict is often the
 same conflict you resolved when you created the test merge you
-blew away.  'git-rerere' helps you resolve this final
+blew away.  'git rerere' helps you resolve this final
 conflicted merge using the information from your earlier hand
 resolve.
 
-Running the 'git-rerere' command immediately after a conflicted
+Running the 'git rerere' command immediately after a conflicted
 automerge records the conflicted working tree files, with the
 usual conflict markers `<<<<<<<`, `=======`, and `>>>>>>>` in
 them.  Later, after you are done resolving the conflicts,
-running 'git-rerere' again will record the resolved state of these
+running 'git rerere' again will record the resolved state of these
 files.  Suppose you did this when you created the test merge of
 master into the topic branch.
 
 Next time, after seeing the same conflicted automerge,
-running 'git-rerere' will perform a three-way merge between the
+running 'git rerere' will perform a three-way merge between the
 earlier conflicted automerge, the earlier manual resolution, and
 the current conflicted automerge.
 If this three-way merge resolves cleanly, the result is written
 out to your working tree file, so you do not have to manually
-resolve it.  Note that 'git-rerere' leaves the index file alone,
+resolve it.  Note that 'git rerere' leaves the index file alone,
 so you still need to do the final sanity checks with `git diff`
-(or `git diff -c`) and 'git-add' when you are satisfied.
+(or `git diff -c`) and 'git add' when you are satisfied.
 
-As a convenience measure, 'git-merge' automatically invokes
-'git-rerere' upon exiting with a failed automerge and 'git-rerere'
+As a convenience measure, 'git merge' automatically invokes
+'git rerere' upon exiting with a failed automerge and 'git rerere'
 records the hand resolve when it is a new conflict, or reuses the earlier hand
-resolve when it is not.  'git-commit' also invokes 'git-rerere'
+resolve when it is not.  'git commit' also invokes 'git rerere'
 when committing a merge result.  What this means is that you do
 not have to do anything special yourself (besides enabling
 the rerere.enabled config variable).
@@ -177,8 +188,8 @@
 actual merge later with the updated master and topic branch, as long
 as the recorded resolution is still applicable.
 
-The information 'git-rerere' records is also used when running
-'git-rebase'.  After blowing away the test merge and continuing
+The information 'git rerere' records is also used when running
+'git rebase'.  After blowing away the test merge and continuing
 development on the topic branch:
 
 ------------
@@ -197,14 +208,9 @@
 up-to-date before your topic is ready to be sent upstream.
 This would result in falling back to a three-way merge, and it
 would conflict the same way as the test merge you resolved earlier.
-'git-rerere' will be run by 'git-rebase' to help you resolve this
+'git rerere' will be run by 'git rebase' to help you resolve this
 conflict.
 
-
-Author
-------
-Written by Junio C Hamano <gitster@pobox.com>
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index abb25d1..117e374 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -8,104 +8,96 @@
 SYNOPSIS
 --------
 [verse]
-'git reset' [--mixed | --soft | --hard | --merge] [-q] [<commit>]
 'git reset' [-q] [<commit>] [--] <paths>...
+'git reset' (--patch | -p) [<commit>] [--] [<paths>...]
+'git reset' (--soft | --mixed | --hard | --merge | --keep) [-q] [<commit>]
 
 DESCRIPTION
 -----------
-Sets the current head to the specified commit and optionally resets the
-index and working tree to match.
+In the first and second form, copy entries from <commit> to the index.
+In the third form, set the current branch head (HEAD) to <commit>, optionally
+modifying index and working tree to match.  The <commit> defaults to HEAD
+in all forms.
 
-This command is useful if you notice some small error in a recent
-commit (or set of commits) and want to redo that part without showing
-the undo in the history.
+'git reset' [-q] [<commit>] [--] <paths>...::
+	This form resets the index entries for all <paths> to their
+	state at <commit>.  (It does not affect the working tree, nor
+	the current branch.)
++
+This means that `git reset <paths>` is the opposite of `git add
+<paths>`.
++
+After running `git reset <paths>` to update the index entry, you can
+use linkgit:git-checkout[1] to check the contents out of the index to
+the working tree.
+Alternatively, using linkgit:git-checkout[1] and specifying a commit, you
+can copy the contents of a path out of a commit to the index and to the
+working tree in one go.
 
-If you want to undo a commit other than the latest on a branch,
-linkgit:git-revert[1] is your friend.
+'git reset' (--patch | -p) [<commit>] [--] [<paths>...]::
+	Interactively select hunks in the difference between the index
+	and <commit> (defaults to HEAD).  The chosen hunks are applied
+	in reverse to the index.
++
+This means that `git reset -p` is the opposite of `git add -p`, i.e.
+you can use it to selectively reset hunks. See the ``Interactive Mode''
+section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
 
-The second form with 'paths' is used to revert selected paths in
-the index from a given commit, without moving HEAD.
+'git reset' --<mode> [<commit>]::
+	This form resets the current branch head to <commit> and
+	possibly updates the index (resetting it to the tree of <commit>) and
+	the working tree depending on <mode>, which
+	must be one of the following:
++
+--
+--soft::
+	Does not touch the index file nor the working tree at all (but
+	resets the head to <commit>, just like all modes do). This leaves
+	all your changed files "Changes to be committed", as 'git status'
+	would put it.
 
-
-OPTIONS
--------
 --mixed::
 	Resets the index but not the working tree (i.e., the changed files
 	are preserved but not marked for commit) and reports what has not
 	been updated. This is the default action.
 
---soft::
-	Does not touch the index file nor the working tree at all, but
-	requires them to be in a good order. This leaves all your changed
-	files "Changes to be committed", as 'git-status' would
-	put it.
-
 --hard::
-	Matches the working tree and index to that of the tree being
-	switched to. Any changes to tracked files in the working tree
-	since <commit> are lost.
+	Resets the index and working tree. Any changes to tracked files in the
+	working tree since <commit> are discarded.
 
 --merge::
-	Resets the index to match the tree recorded by the named commit,
-	and updates the files that are different between the named commit
-	and the current commit in the working tree.
+	Resets the index and updates the files in the working tree that are
+	different between <commit> and HEAD, but keeps those which are
+	different between the index and working tree (i.e. which have changes
+	which have not been added).
+	If a file that is different between <commit> and the index has unstaged
+	changes, reset is aborted.
++
+In other words, --merge does something like a 'git read-tree -u -m <commit>',
+but carries forward unmerged index entries.
+
+--keep::
+	Resets index entries and updates files in the working tree that are
+	different between <commit> and HEAD.
+	If a file that is different between <commit> and HEAD has local changes,
+	reset is aborted.
+--
+
+If you want to undo a commit other than the latest on a branch,
+linkgit:git-revert[1] is your friend.
+
+
+OPTIONS
+-------
 
 -q::
+--quiet::
 	Be quiet, only report errors.
 
-<commit>::
-	Commit to make the current HEAD. If not given defaults to HEAD.
 
-Examples
+EXAMPLES
 --------
 
-Undo a commit and redo::
-+
-------------
-$ git commit ...
-$ git reset --soft HEAD^      <1>
-$ edit                        <2>
-$ git commit -a -c ORIG_HEAD  <3>
-------------
-+
-<1> This is most often done when you remembered what you
-just committed is incomplete, or you misspelled your commit
-message, or both.  Leaves working tree as it was before "reset".
-<2> Make corrections to working tree files.
-<3> "reset" copies the old head to .git/ORIG_HEAD; redo the
-commit by starting with its log message.  If you do not need to
-edit the message further, you can give -C option instead.
-+
-See also the --amend option to linkgit:git-commit[1].
-
-Undo commits permanently::
-+
-------------
-$ git commit ...
-$ git reset --hard HEAD~3   <1>
-------------
-+
-<1> The last three commits (HEAD, HEAD^, and HEAD~2) were bad
-and you do not want to ever see them again.  Do *not* do this if
-you have already given these commits to somebody else.  (See the
-"RECOVERING FROM UPSTREAM REBASE" section in linkgit:git-rebase[1] for
-the implications of doing so.)
-
-Undo a commit, making it a topic branch::
-+
-------------
-$ git branch topic/wip     <1>
-$ git reset --hard HEAD~3  <2>
-$ git checkout topic/wip   <3>
-------------
-+
-<1> You have made some commits, but realize they were premature
-to be in the "master" branch.  You want to continue polishing
-them in a topic branch, so create "topic/wip" branch off of the
-current HEAD.
-<2> Rewind the master branch to get rid of those three commits.
-<3> Switch to "topic/wip" branch and keep working.
-
 Undo add::
 +
 ------------
@@ -129,6 +121,53 @@
 <4> Then you can pull and merge, leaving frotz.c and filfre.c
 changes still in the working tree.
 
+Undo a commit and redo::
++
+------------
+$ git commit ...
+$ git reset --soft HEAD^      <1>
+$ edit                        <2>
+$ git commit -a -c ORIG_HEAD  <3>
+------------
++
+<1> This is most often done when you remembered what you
+just committed is incomplete, or you misspelled your commit
+message, or both.  Leaves working tree as it was before "reset".
+<2> Make corrections to working tree files.
+<3> "reset" copies the old head to .git/ORIG_HEAD; redo the
+commit by starting with its log message.  If you do not need to
+edit the message further, you can give -C option instead.
++
+See also the --amend option to linkgit:git-commit[1].
+
+Undo a commit, making it a topic branch::
++
+------------
+$ git branch topic/wip     <1>
+$ git reset --hard HEAD~3  <2>
+$ git checkout topic/wip   <3>
+------------
++
+<1> You have made some commits, but realize they were premature
+to be in the "master" branch.  You want to continue polishing
+them in a topic branch, so create "topic/wip" branch off of the
+current HEAD.
+<2> Rewind the master branch to get rid of those three commits.
+<3> Switch to "topic/wip" branch and keep working.
+
+Undo commits permanently::
++
+------------
+$ git commit ...
+$ git reset --hard HEAD~3   <1>
+------------
++
+<1> The last three commits (HEAD, HEAD^, and HEAD~2) were bad
+and you do not want to ever see them again.  Do *not* do this if
+you have already given these commits to somebody else.  (See the
+"RECOVERING FROM UPSTREAM REBASE" section in linkgit:git-rebase[1] for
+the implications of doing so.)
+
 Undo a merge or pull::
 +
 ------------
@@ -139,7 +178,7 @@
 $ git reset --hard                 <2>
 $ git pull . topic/branch          <3>
 Updating from 41223... to 13134...
-Fast forward
+Fast-forward
 $ git reset --hard ORIG_HEAD       <4>
 ------------
 +
@@ -150,14 +189,14 @@
 which is a synonym for "git reset --hard HEAD" clears the mess
 from the index file and the working tree.
 <3> Merge a topic branch into the current branch, which resulted
-in a fast forward.
+in a fast-forward.
 <4> But you decided that the topic branch is not ready for public
 consumption yet.  "pull" or "merge" always leaves the original
 tip of the current branch in ORIG_HEAD, so resetting hard to it
 brings your index file and the working tree back to that state,
 and resets the tip of the branch to that commit.
 
-Undo a merge or pull inside a dirty work tree::
+Undo a merge or pull inside a dirty working tree::
 +
 ------------
 $ git pull                         <1>
@@ -224,13 +263,140 @@
 <2> This commits all other changes in the index.
 <3> Adds the file to the index again.
 
-Author
-------
-Written by Junio C Hamano <gitster@pobox.com> and Linus Torvalds <torvalds@osdl.org>
+Keep changes in working tree while discarding some previous commits::
++
+Suppose you are working on something and you commit it, and then you
+continue working a bit more, but now you think that what you have in
+your working tree should be in another branch that has nothing to do
+with what you committed previously. You can start a new branch and
+reset it while keeping the changes in your working tree.
++
+------------
+$ git tag start
+$ git checkout -b branch1
+$ edit
+$ git commit ...                            <1>
+$ edit
+$ git checkout -b branch2                   <2>
+$ git reset --keep start                    <3>
+------------
++
+<1> This commits your first edits in branch1.
+<2> In the ideal world, you could have realized that the earlier
+    commit did not belong to the new topic when you created and switched
+    to branch2 (i.e. "git checkout -b branch2 start"), but nobody is
+    perfect.
+<3> But you can use "reset --keep" to remove the unwanted commit after
+    you switched to "branch2".
 
-Documentation
---------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+DISCUSSION
+----------
+
+The tables below show what happens when running:
+
+----------
+git reset --option target
+----------
+
+to reset the HEAD to another commit (`target`) with the different
+reset options depending on the state of the files.
+
+In these tables, A, B, C and D are some different states of a
+file. For example, the first line of the first table means that if a
+file is in state A in the working tree, in state B in the index, in
+state C in HEAD and in state D in the target, then "git reset --soft
+target" will leave the file in the working tree in state A and in the
+index in state B.  It resets (i.e. moves) the HEAD (i.e. the tip of
+the current branch, if you are on one) to "target" (which has the file
+in state D).
+
+      working index HEAD target         working index HEAD
+      ----------------------------------------------------
+       A       B     C    D     --soft   A       B     D
+				--mixed  A       D     D
+				--hard   D       D     D
+				--merge (disallowed)
+				--keep  (disallowed)
+
+      working index HEAD target         working index HEAD
+      ----------------------------------------------------
+       A       B     C    C     --soft   A       B     C
+				--mixed  A       C     C
+				--hard   C       C     C
+				--merge (disallowed)
+				--keep   A       C     C
+
+      working index HEAD target         working index HEAD
+      ----------------------------------------------------
+       B       B     C    D     --soft   B       B     D
+				--mixed  B       D     D
+				--hard   D       D     D
+				--merge  D       D     D
+				--keep  (disallowed)
+
+      working index HEAD target         working index HEAD
+      ----------------------------------------------------
+       B       B     C    C     --soft   B       B     C
+				--mixed  B       C     C
+				--hard   C       C     C
+				--merge  C       C     C
+				--keep   B       C     C
+
+      working index HEAD target         working index HEAD
+      ----------------------------------------------------
+       B       C     C    D     --soft   B       C     D
+				--mixed  B       D     D
+				--hard   D       D     D
+				--merge (disallowed)
+				--keep  (disallowed)
+
+      working index HEAD target         working index HEAD
+      ----------------------------------------------------
+       B       C     C    C     --soft   B       C     C
+				--mixed  B       C     C
+				--hard   C       C     C
+				--merge  B       C     C
+				--keep   B       C     C
+
+"reset --merge" is meant to be used when resetting out of a conflicted
+merge. Any mergy operation guarantees that the working tree file that is
+involved in the merge does not have local change wrt the index before
+it starts, and that it writes the result out to the working tree. So if
+we see some difference between the index and the target and also
+between the index and the working tree, then it means that we are not
+resetting out from a state that a mergy operation left after failing
+with a conflict. That is why we disallow --merge option in this case.
+
+"reset --keep" is meant to be used when removing some of the last
+commits in the current branch while keeping changes in the working
+tree. If there could be conflicts between the changes in the commit we
+want to remove and the changes in the working tree we want to keep,
+the reset is disallowed. That's why it is disallowed if there are both
+changes between the working tree and HEAD, and between HEAD and the
+target. To be safe, it is also disallowed when there are unmerged
+entries.
+
+The following tables show what happens when there are unmerged
+entries:
+
+      working index HEAD target         working index HEAD
+      ----------------------------------------------------
+       X       U     A    B     --soft  (disallowed)
+				--mixed  X       B     B
+				--hard   B       B     B
+				--merge  B       B     B
+				--keep  (disallowed)
+
+      working index HEAD target         working index HEAD
+      ----------------------------------------------------
+       X       U     A    A     --soft  (disallowed)
+				--mixed  X       A     A
+				--hard   A       A     A
+				--merge  A       A     A
+				--keep  (disallowed)
+
+X means any state and U means an unmerged index.
 
 GIT
 ---
diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt
index a765cfa..38fafca 100644
--- a/Documentation/git-rev-list.txt
+++ b/Documentation/git-rev-list.txt
@@ -9,34 +9,43 @@
 SYNOPSIS
 --------
 [verse]
-'git-rev-list' [ \--max-count=number ]
-	     [ \--skip=number ]
-	     [ \--max-age=timestamp ]
-	     [ \--min-age=timestamp ]
+'git rev-list' [ \--max-count=<number> ]
+	     [ \--skip=<number> ]
+	     [ \--max-age=<timestamp> ]
+	     [ \--min-age=<timestamp> ]
 	     [ \--sparse ]
 	     [ \--merges ]
 	     [ \--no-merges ]
+	     [ \--min-parents=<number> ]
+	     [ \--no-min-parents ]
+	     [ \--max-parents=<number> ]
+	     [ \--no-max-parents ]
 	     [ \--first-parent ]
 	     [ \--remove-empty ]
 	     [ \--full-history ]
 	     [ \--not ]
 	     [ \--all ]
-	     [ \--branches ]
-	     [ \--tags ]
-	     [ \--remotes ]
+	     [ \--branches[=<pattern>] ]
+	     [ \--tags[=<pattern>] ]
+	     [ \--remotes[=<pattern>] ]
+	     [ \--glob=<glob-pattern> ]
+	     [ \--ignore-missing ]
 	     [ \--stdin ]
 	     [ \--quiet ]
 	     [ \--topo-order ]
 	     [ \--parents ]
 	     [ \--timestamp ]
 	     [ \--left-right ]
+	     [ \--left-only ]
+	     [ \--right-only ]
+	     [ \--cherry-mark ]
 	     [ \--cherry-pick ]
 	     [ \--encoding[=<encoding>] ]
 	     [ \--(author|committer|grep)=<pattern> ]
 	     [ \--regexp-ignore-case | -i ]
 	     [ \--extended-regexp | -E ]
 	     [ \--fixed-strings | -F ]
-	     [ \--date={local|relative|default|iso|rfc|short} ]
+	     [ \--date=(local|relative|default|iso|rfc|short) ]
 	     [ [\--objects | \--objects-edge] [ \--unpacked ] ]
 	     [ \--pretty | \--header ]
 	     [ \--bisect ]
@@ -51,20 +60,26 @@
 DESCRIPTION
 -----------
 
-Lists commit objects in reverse chronological order starting at the
-given commit(s), taking ancestry relationship into account.  This is
-useful to produce human-readable log output.
+List commits that are reachable by following the `parent` links from the
+given commit(s), but exclude commits that are reachable from the one(s)
+given with a '{caret}' in front of them.  The output is given in reverse
+chronological order by default.
 
-Commits which are stated with a preceding '{caret}' cause listing to
-stop at that point. Their parents are implied. Thus the following
-command:
+You can think of this as a set operation.  Commits given on the command
+line form a set of commits that are reachable from any of them, and then
+commits reachable from any of the ones given with '{caret}' in front are
+subtracted from that set.  The remaining commits are what comes out in the
+command's output.  Various other options and paths parameters can be used
+to further limit the result.
+
+Thus, the following command:
 
 -----------------------------------------------------------------------
 	$ git rev-list foo bar ^baz
 -----------------------------------------------------------------------
 
-means "list all the commits which are included in 'foo' and 'bar', but
-not in 'baz'".
+means "list all the commits which are reachable from 'foo' or 'bar', but
+not from 'baz'".
 
 A special notation "'<commit1>'..'<commit2>'" can be used as a
 short-hand for "{caret}'<commit1>' '<commit2>'". For example, either of
@@ -84,11 +99,11 @@
 	$ git rev-list A...B
 -----------------------------------------------------------------------
 
-'git-rev-list' is a very essential git program, since it
+'rev-list' is a very essential git command, since it
 provides the ability to build and traverse commit ancestry graphs. For
 this reason, it has a lot of different options that enables it to be
-used by commands as different as 'git-bisect' and
-'git-repack'.
+used by commands as different as 'git bisect' and
+'git repack'.
 
 OPTIONS
 -------
@@ -98,16 +113,6 @@
 
 include::pretty-formats.txt[]
 
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano, Jonas Fonseca
-and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index 82045a2..3c63561 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -8,6 +8,7 @@
 
 SYNOPSIS
 --------
+[verse]
 'git rev-parse' [ --option ] <args>...
 
 DESCRIPTION
@@ -15,16 +16,16 @@
 
 Many git porcelainish commands take mixture of flags
 (i.e. parameters that begin with a dash '-') and parameters
-meant for the underlying 'git-rev-list' command they use internally
+meant for the underlying 'git rev-list' command they use internally
 and flags and parameters for the other commands they use
-downstream of 'git-rev-list'.  This command is used to
+downstream of 'git rev-list'.  This command is used to
 distinguish between them.
 
 
 OPTIONS
 -------
 --parseopt::
-	Use 'git-rev-parse' in option parsing mode (see PARSEOPT section below).
+	Use 'git rev-parse' in option parsing mode (see PARSEOPT section below).
 
 --keep-dashdash::
 	Only meaningful in `--parseopt` mode. Tells the option parser to echo
@@ -33,20 +34,20 @@
 --stop-at-non-option::
 	Only meaningful in `--parseopt` mode.  Lets the option parser stop at
 	the first non-option argument.  This can be used to parse sub-commands
-	that take options themself.
+	that take options themselves.
 
 --sq-quote::
-	Use 'git-rev-parse' in shell quoting mode (see SQ-QUOTE
+	Use 'git rev-parse' in shell quoting mode (see SQ-QUOTE
 	section below). In contrast to the `--sq` option below, this
 	mode does only quoting. Nothing else is done to command input.
 
 --revs-only::
 	Do not output flags and parameters not meant for
-	'git-rev-list' command.
+	'git rev-list' command.
 
 --no-revs::
 	Do not output flags and parameters meant for
-	'git-rev-list' command.
+	'git rev-list' command.
 
 --flags::
 	Do not output non-flag parameters.
@@ -74,7 +75,7 @@
 	properly quoted for consumption by shell.  Useful when
 	you expect your parameter to contain whitespaces and
 	newlines (e.g. when using pickaxe `-S` with
-	'git-diff-\*'). In contrast to the `--sq-quote` option,
+	'git diff-{asterisk}'). In contrast to the `--sq-quote` option,
 	the command input is still interpreted as usual.
 
 --not::
@@ -95,22 +96,40 @@
 	unfortunately named tag "master"), and show them as full
 	refnames (e.g. "refs/heads/master").
 
---abbrev-ref[={strict|loose}]::
+--abbrev-ref[=(strict|loose)]::
 	A non-ambiguous short name of the objects name.
 	The option core.warnAmbiguousRefs is used to select the strict
 	abbreviation mode.
 
+--disambiguate=<prefix>::
+	Show every object whose name begins with the given prefix.
+	The <prefix> must be at least 4 hexadecimal digits long to
+	avoid listing each and every object in the repository by
+	mistake.
+
 --all::
-	Show all refs found in `$GIT_DIR/refs`.
+	Show all refs found in `refs/`.
 
---branches::
-	Show branch refs found in `$GIT_DIR/refs/heads`.
+--branches[=pattern]::
+--tags[=pattern]::
+--remotes[=pattern]::
+	Show all branches, tags, or remote-tracking branches,
+	respectively (i.e., refs found in `refs/heads`,
+	`refs/tags`, or `refs/remotes`, respectively).
++
+If a `pattern` is given, only refs matching the given shell glob are
+shown.  If the pattern does not contain a globbing character (`?`,
+`*`, or `[`), it is turned into a prefix match by appending `/*`.
 
---tags::
-	Show tag refs found in `$GIT_DIR/refs/tags`.
+--glob=pattern::
+	Show all refs matching the shell glob pattern `pattern`. If
+	the pattern does not start with `refs/`, this is automatically
+	prepended.  If the pattern does not contain a globbing
+	character (`?`, `*`, or `[`), it is turned into a prefix
+	match by appending `/*`.
 
---remotes::
-	Show tag refs found in `$GIT_DIR/refs/remotes`.
+--show-toplevel::
+	Show the absolute path of the top-level directory.
 
 --show-prefix::
 	When the command is invoked from a subdirectory, show the
@@ -123,7 +142,13 @@
 	directory (typically a sequence of "../", or an empty string).
 
 --git-dir::
-	Show `$GIT_DIR` if defined else show the path to the .git directory.
+	Show `$GIT_DIR` if defined. Otherwise show the path to
+	the .git directory. The path shown, when relative, is
+	relative to the current working directory.
++
+If `$GIT_DIR` is not defined and the current directory
+is not detected to lie in a git repository or work tree
+print a message to stderr and exit with nonzero status.
 
 --is-inside-git-dir::
 	When the current working directory is below the repository
@@ -136,6 +161,12 @@
 --is-bare-repository::
 	When the repository is bare print "true", otherwise "false".
 
+--local-env-vars::
+	List the GIT_* environment variables that are local to the
+	repository (e.g. GIT_DIR or GIT_WORK_TREE, but not GIT_EDITOR).
+	Only the names of the variables are listed, not their value,
+	even if they are set.
+
 --short::
 --short=number::
 	Instead of outputting the full SHA1 values of object names try to
@@ -145,223 +176,42 @@
 --since=datestring::
 --after=datestring::
 	Parse the date string, and output the corresponding
-	--max-age= parameter for 'git-rev-list'.
+	--max-age= parameter for 'git rev-list'.
 
 --until=datestring::
 --before=datestring::
 	Parse the date string, and output the corresponding
-	--min-age= parameter for 'git-rev-list'.
+	--min-age= parameter for 'git rev-list'.
 
 <args>...::
 	Flags and parameters to be parsed.
 
+--resolve-git-dir <path>::
+	Check if <path> is a valid git-dir or a git-file pointing to a valid
+	git-dir. If <path> is a valid git-dir the resolved path to git-dir will
+	be printed.
 
-SPECIFYING REVISIONS
---------------------
-
-A revision parameter typically, but not necessarily, names a
-commit object.  They use what is called an 'extended SHA1'
-syntax.  Here are various ways to spell object names.  The
-ones listed near the end of this list are to name trees and
-blobs contained in a commit.
-
-* The full SHA1 object name (40-byte hexadecimal string), or
-  a substring of such that is unique within the repository.
-  E.g. dae86e1950b1277e545cee180551750029cfe735 and dae86e both
-  name the same commit object if there are no other object in
-  your repository whose object name starts with dae86e.
-
-* An output from 'git-describe'; i.e. a closest tag, optionally
-  followed by a dash and a number of commits, followed by a dash, a
-  `g`, and an abbreviated object name.
-
-* A symbolic ref name.  E.g. 'master' typically means the commit
-  object referenced by $GIT_DIR/refs/heads/master.  If you
-  happen to have both heads/master and tags/master, you can
-  explicitly say 'heads/master' to tell git which one you mean.
-  When ambiguous, a `<name>` is disambiguated by taking the
-  first match in the following rules:
-
-  . if `$GIT_DIR/<name>` exists, that is what you mean (this is usually
-    useful only for `HEAD`, `FETCH_HEAD`, `ORIG_HEAD` and `MERGE_HEAD`);
-
-  . otherwise, `$GIT_DIR/refs/<name>` if exists;
-
-  . otherwise, `$GIT_DIR/refs/tags/<name>` if exists;
-
-  . otherwise, `$GIT_DIR/refs/heads/<name>` if exists;
-
-  . otherwise, `$GIT_DIR/refs/remotes/<name>` if exists;
-
-  . otherwise, `$GIT_DIR/refs/remotes/<name>/HEAD` if exists.
-+
-HEAD names the commit your changes in the working tree is based on.
-FETCH_HEAD records the branch you fetched from a remote repository
-with your last 'git-fetch' invocation.
-ORIG_HEAD is created by commands that moves your HEAD in a drastic
-way, to record the position of the HEAD before their operation, so that
-you can change the tip of the branch back to the state before you ran
-them easily.
-MERGE_HEAD records the commit(s) you are merging into your branch
-when you run 'git-merge'.
-
-* A ref followed by the suffix '@' with a date specification
-  enclosed in a brace
-  pair (e.g. '\{yesterday\}', '\{1 month 2 weeks 3 days 1 hour 1
-  second ago\}' or '\{1979-02-26 18:30:00\}') to specify the value
-  of the ref at a prior point in time.  This suffix may only be
-  used immediately following a ref name and the ref must have an
-  existing log ($GIT_DIR/logs/<ref>). Note that this looks up the state
-  of your *local* ref at a given time; e.g., what was in your local
-  `master` branch last week. If you want to look at commits made during
-  certain times, see `--since` and `--until`.
-
-* A ref followed by the suffix '@' with an ordinal specification
-  enclosed in a brace pair (e.g. '\{1\}', '\{15\}') to specify
-  the n-th prior value of that ref.  For example 'master@\{1\}'
-  is the immediate prior value of 'master' while 'master@\{5\}'
-  is the 5th prior value of 'master'. This suffix may only be used
-  immediately following a ref name and the ref must have an existing
-  log ($GIT_DIR/logs/<ref>).
-
-* You can use the '@' construct with an empty ref part to get at a
-  reflog of the current branch. For example, if you are on the
-  branch 'blabla', then '@\{1\}' means the same as 'blabla@\{1\}'.
-
-* The special construct '@\{-<n>\}' means the <n>th branch checked out
-  before the current one.
-
-* A suffix '{caret}' to a revision parameter means the first parent of
-  that commit object.  '{caret}<n>' means the <n>th parent (i.e.
-  'rev{caret}'
-  is equivalent to 'rev{caret}1').  As a special rule,
-  'rev{caret}0' means the commit itself and is used when 'rev' is the
-  object name of a tag object that refers to a commit object.
-
-* A suffix '{tilde}<n>' to a revision parameter means the commit
-  object that is the <n>th generation grand-parent of the named
-  commit object, following only the first parent.  I.e. rev~3 is
-  equivalent to rev{caret}{caret}{caret} which is equivalent to
-  rev{caret}1{caret}1{caret}1.  See below for a illustration of
-  the usage of this form.
-
-* A suffix '{caret}' followed by an object type name enclosed in
-  brace pair (e.g. `v0.99.8{caret}\{commit\}`) means the object
-  could be a tag, and dereference the tag recursively until an
-  object of that type is found or the object cannot be
-  dereferenced anymore (in which case, barf).  `rev{caret}0`
-  introduced earlier is a short-hand for `rev{caret}\{commit\}`.
-
-* A suffix '{caret}' followed by an empty brace pair
-  (e.g. `v0.99.8{caret}\{\}`) means the object could be a tag,
-  and dereference the tag recursively until a non-tag object is
-  found.
-
-* A colon, followed by a slash, followed by a text: this names
-  a commit whose commit message starts with the specified text.
-  This name returns the youngest matching commit which is
-  reachable from any ref.  If the commit message starts with a
-  '!', you have to repeat that;  the special sequence ':/!',
-  followed by something else than '!' is reserved for now.
-
-* A suffix ':' followed by a path; this names the blob or tree
-  at the given path in the tree-ish object named by the part
-  before the colon.
-
-* A colon, optionally followed by a stage number (0 to 3) and a
-  colon, followed by a path; this names a blob object in the
-  index at the given path.  Missing stage number (and the colon
-  that follows it) names a stage 0 entry. During a merge, stage
-  1 is the common ancestor, stage 2 is the target branch's version
-  (typically the current branch), and stage 3 is the version from
-  the branch being merged.
-
-Here is an illustration, by Jon Loeliger.  Both commit nodes B
-and C are parents of commit node A.  Parent commits are ordered
-left-to-right.
-
-........................................
-G   H   I   J
- \ /     \ /
-  D   E   F
-   \  |  / \
-    \ | /   |
-     \|/    |
-      B     C
-       \   /
-        \ /
-         A
-........................................
-
-    A =      = A^0
-    B = A^   = A^1     = A~1
-    C = A^2  = A^2
-    D = A^^  = A^1^1   = A~2
-    E = B^2  = A^^2
-    F = B^3  = A^^3
-    G = A^^^ = A^1^1^1 = A~3
-    H = D^2  = B^^2    = A^^^2  = A~2^2
-    I = F^   = B^3^    = A^^3^
-    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.  When you have two commits `r1` and `r2` (named according
-to the syntax explained in SPECIFYING REVISIONS above), you can ask
-for commits that are reachable from r2 excluding those that are reachable
-from r1 by `{caret}r1 r2` and it can be written as `r1..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 is the set of commits that are reachable from either one of
-`r1` or `r2` but not from both.
-
-Two other shorthands for naming a set that is formed by a commit
-and its parent commits exist.  The `r1{caret}@` notation means all
-parents of `r1`.  `r1{caret}!` includes commit `r1` but excludes
-all of its parents.
-
-Here are a handful of examples:
-
-   D                G H D
-   D F              G H I J D F
-   ^G D             H D
-   ^D B             E I J F B
-   B...C            G H D E B C
-   ^D B C           E I J F B C
-   C^@              I J F
-   F^! D            G H D F
+include::revisions.txt[]
 
 PARSEOPT
 --------
 
-In `--parseopt` mode, 'git-rev-parse' helps massaging options to bring to shell
+In `--parseopt` mode, 'git rev-parse' helps massaging options to bring to shell
 scripts the same facilities C builtins have. It works as an option normalizer
 (e.g. splits single switches aggregate values), a bit like `getopt(1)` does.
 
 It takes on the standard input the specification of the options to parse and
-understand, and echoes on the standard output a line suitable for `sh(1)` `eval`
+understand, and echoes on the standard output a string suitable for `sh(1)` `eval`
 to replace the arguments with normalized ones.  In case of error, it outputs
 usage on the standard error stream, and exits with code 129.
 
+Note: Make sure you quote the result when passing it to `eval`.  See
+below for an example.
+
 Input Format
 ~~~~~~~~~~~~
 
-'git-rev-parse --parseopt' input format is fully text based. It has two parts,
+'git rev-parse --parseopt' input format is fully text based. It has two parts,
 separated by a line that contains only `--`. The lines before the separator
 (should be more than one) are used for the usage.
 The lines after the separator describe the options.
@@ -414,19 +264,19 @@
   An option group Header
 C?        option C with an optional argument"
 
-eval `echo "$OPTS_SPEC" | git rev-parse --parseopt -- "$@" || echo exit $?`
+eval "$(echo "$OPTS_SPEC" | git rev-parse --parseopt -- "$@" || echo exit $?)"
 ------------
 
 SQ-QUOTE
 --------
 
-In `--sq-quote` mode, 'git-rev-parse' echoes on the standard output a
+In `--sq-quote` mode, 'git rev-parse' echoes on the standard output a
 single line suitable for `sh(1)` `eval`. This line is made by
 normalizing the arguments following `--sq-quote`. Nothing other than
 quoting the arguments is done.
 
 If you want command input to still be interpreted as usual by
-'git-rev-parse' before the output is shell quoted, see the `--sq`
+'git rev-parse' before the output is shell quoted, see the `--sq`
 option.
 
 Example
@@ -469,16 +319,6 @@
 +
 but if $REV is empty, the commit object name from master will be printed.
 
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org> .
-Junio C Hamano <gitster@pobox.com> and Pierre Habouzit <madcoder@debian.org>
-
-Documentation
---------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt
index 5e11758..70152e8 100644
--- a/Documentation/git-revert.txt
+++ b/Documentation/git-revert.txt
@@ -3,37 +3,46 @@
 
 NAME
 ----
-git-revert - Revert an existing commit
+git-revert - Revert some existing commits
 
 SYNOPSIS
 --------
-'git revert' [--edit | --no-edit] [-n] [-m parent-number] [-s] <commit>
+[verse]
+'git revert' [--edit | --no-edit] [-n] [-m parent-number] [-s] <commit>...
+'git revert' --continue
+'git revert' --quit
+'git revert' --abort
 
 DESCRIPTION
 -----------
-Given one existing commit, revert the change the patch introduces, and record a
-new commit that records it.  This requires your working tree to be clean (no
-modifications from the HEAD commit).
 
-Note: 'git revert' is used to record a new commit to reverse the
-effect of an earlier commit (often a faulty one).  If you want to
+Given one or more existing commits, revert the changes that the
+related patches introduce, and record some new commits that record
+them.  This requires your working tree to be clean (no modifications
+from the HEAD commit).
+
+Note: 'git revert' is used to record some new commits to reverse the
+effect of some earlier commits (often only a faulty one).  If you want to
 throw away all uncommitted changes in your working directory, you
 should see linkgit:git-reset[1], particularly the '--hard' option.  If
 you want to extract specific files as they were in another commit, you
-should see linkgit:git-checkout[1], specifically the 'git checkout
-<commit> -- <filename>' syntax.  Take care with these alternatives as
+should see linkgit:git-checkout[1], specifically the `git checkout
+<commit> -- <filename>` syntax.  Take care with these alternatives as
 both will discard uncommitted changes in your working directory.
 
 OPTIONS
 -------
-<commit>::
-	Commit to revert.
+<commit>...::
+	Commits to revert.
 	For a more complete list of ways to spell commit names, see
-	"SPECIFYING REVISIONS" section in linkgit:git-rev-parse[1].
+	linkgit:gitrevisions[7].
+	Sets of commits can also be given but no traversal is done by
+	default, see linkgit:git-rev-list[1] and its '--no-walk'
+	option.
 
 -e::
 --edit::
-	With this option, 'git-revert' will let you edit the commit
+	With this option, 'git revert' will let you edit the commit
 	message prior to committing the revert. This is the default if
 	you run the command from a terminal.
 
@@ -54,16 +63,16 @@
 more details.
 
 --no-edit::
-	With this option, 'git-revert' will not start the commit
+	With this option, 'git revert' will not start the commit
 	message editor.
 
 -n::
 --no-commit::
-	Usually the command automatically creates a commit with
-	a commit log message stating which commit was
-	reverted.  This flag applies the change necessary
-	to revert the named commit to your working tree
-	and the index, but does not make the commit.  In addition,
+	Usually the command automatically creates some commits with
+	commit log messages stating which commits were
+	reverted.  This flag applies the changes necessary
+	to revert the named commits to your working tree
+	and the index, but does not make the commits.  In addition,
 	when this option is used, your index does not have to match
 	the HEAD commit.  The revert is done against the
 	beginning state of your index.
@@ -75,14 +84,38 @@
 --signoff::
 	Add Signed-off-by line at the end of the commit message.
 
+--strategy=<strategy>::
+	Use the given merge strategy.  Should only be used once.
+	See the MERGE STRATEGIES section in linkgit:git-merge[1]
+	for details.
 
-Author
-------
-Written by Junio C Hamano <gitster@pobox.com>
+-X<option>::
+--strategy-option=<option>::
+	Pass the merge strategy-specific option through to the
+	merge strategy.  See linkgit:git-merge[1] for details.
 
-Documentation
---------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
+SEQUENCER SUBCOMMANDS
+---------------------
+include::sequencer.txt[]
+
+EXAMPLES
+--------
+`git revert HEAD~3`::
+
+	Revert the changes specified by the fourth last commit in HEAD
+	and create a new commit with the reverted changes.
+
+`git revert -n master~5..master~2`::
+
+	Revert the changes done by commits from the fifth last commit
+	in master (included) to the third last commit in master
+	(included), but do not create any commit with the reverted
+	changes. The revert only modifies the working tree and the
+	index.
+
+SEE ALSO
+--------
+linkgit:git-cherry-pick[1]
 
 GIT
 ---
diff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt
index 5afb1e7..5d31860 100644
--- a/Documentation/git-rm.txt
+++ b/Documentation/git-rm.txt
@@ -7,18 +7,19 @@
 
 SYNOPSIS
 --------
+[verse]
 'git rm' [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <file>...
 
 DESCRIPTION
 -----------
 Remove files from the index, or from the working tree and the index.
-'git-rm' will not remove a file from just your working directory.
-(There is no option to remove a file only from the work tree
+`git rm` will not remove a file from just your working directory.
+(There is no option to remove a file only from the working tree
 and yet keep it in the index; use `/bin/rm` if you want to do that.)
 The files being removed have to be identical to the tip of the branch,
 and no updates to their contents can be staged in the index,
 though that default behavior can be overridden with the `-f` option.
-When '--cached' is given, the staged content has to
+When `--cached` is given, the staged content has to
 match either the tip of the branch or the file on disk,
 allowing the file to be removed from just the index.
 
@@ -64,7 +65,7 @@
 
 -q::
 --quiet::
-	'git-rm' normally outputs one line (in the form of an "rm" command)
+	`git rm` normally outputs one line (in the form of an `rm` command)
 	for each file removed. This option suppresses that output.
 
 
@@ -78,20 +79,72 @@
 
 File globbing matches across directory boundaries.  Thus, given
 two directories `d` and `d2`, there is a difference between
-using `git rm \'d\*\'` and `git rm \'d/\*\'`, as the former will
+using `git rm 'd*'` and `git rm 'd/*'`, as the former will
 also remove all of directory `d2`.
 
+REMOVING FILES THAT HAVE DISAPPEARED FROM THE FILESYSTEM
+--------------------------------------------------------
+There is no option for `git rm` to remove from the index only
+the paths that have disappeared from the filesystem. However,
+depending on the use case, there are several ways that can be
+done.
+
+Using ``git commit -a''
+~~~~~~~~~~~~~~~~~~~~~~~
+If you intend that your next commit should record all modifications
+of tracked files in the working tree and record all removals of
+files that have been removed from the working tree with `rm`
+(as opposed to `git rm`), use `git commit -a`, as it will
+automatically notice and record all removals.  You can also have a
+similar effect without committing by using `git add -u`.
+
+Using ``git add -A''
+~~~~~~~~~~~~~~~~~~~~
+When accepting a new code drop for a vendor branch, you probably
+want to record both the removal of paths and additions of new paths
+as well as modifications of existing paths.
+
+Typically you would first remove all tracked files from the working
+tree using this command:
+
+----------------
+git ls-files -z | xargs -0 rm -f
+----------------
+
+and then untar the new code in the working tree. Alternately
+you could 'rsync' the changes into the working tree.
+
+After that, the easiest way to record all removals, additions, and
+modifications in the working tree is:
+
+----------------
+git add -A
+----------------
+
+See linkgit:git-add[1].
+
+Other ways
+~~~~~~~~~~
+If all you really want to do is to remove from the index the files
+that are no longer present in the working tree (perhaps because
+your working tree is dirty so that you cannot use `git commit -a`),
+use the following command:
+
+----------------
+git diff --name-only --diff-filter=D -z | xargs -0 git rm --cached
+----------------
+
 EXAMPLES
 --------
-git rm Documentation/\\*.txt::
-	Removes all `\*.txt` files from the index that are under the
+`git rm Documentation/\*.txt`::
+	Removes all `*.txt` files from the index that are under the
 	`Documentation` directory and any of its subdirectories.
 +
-Note that the asterisk `\*` is quoted from the shell in this
+Note that the asterisk `*` is quoted from the shell in this
 example; this lets git, and not the shell, expand the pathnames
 of files and subdirectories under the `Documentation/` directory.
 
-git rm -f git-*.sh::
+`git rm -f git-*.sh`::
 	Because this example lets the shell expand the asterisk
 	(i.e. you are listing the files explicitly), it
 	does not remove `subdir/git-foo.sh`.
@@ -100,14 +153,6 @@
 --------
 linkgit:git-add[1]
 
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index 767cf4d..3241170 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -8,6 +8,7 @@
 
 SYNOPSIS
 --------
+[verse]
 'git send-email' [options] <file|directory|rev-list options>...
 
 
@@ -60,8 +61,8 @@
 The --cc option must be repeated for each user you want on the cc list.
 
 --compose::
-	Use $GIT_EDITOR, core.editor, $VISUAL, or $EDITOR to edit an
-	introductory message for the patch series.
+	Invoke a text editor (see GIT_EDITOR in linkgit:git-var[1])
+	to edit an introductory message for the patch series.
 +
 When '--compose' is used, git send-email will use the From, Subject, and
 In-Reply-To headers specified in the message. If the body of the message
@@ -82,11 +83,26 @@
 	set, as returned by "git var -l".
 
 --in-reply-to=<identifier>::
-	Specify the contents of the first In-Reply-To header.
-	Subsequent emails will refer to the previous email
-	instead of this if --chain-reply-to is set (the default)
-	Only necessary if --compose is also set.  If --compose
-	is not set, this will be prompted for.
+	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.
+	The second and subsequent emails will be sent as replies according to
+	the `--[no]-chain-reply-to` setting.
++
+So for example when `--thread` and `--no-chain-reply-to` are specified, the
+second and subsequent patches will be replies to the first one like in the
+illustration below where `[PATCH v2 0/3]` is in reply to `[PATCH 0/2]`:
++
+  [PATCH 0/2] Here is what I did...
+    [PATCH 1/2] Clean up and tests
+    [PATCH 2/2] Implementation
+    [PATCH v2 0/3] Here is a reroll
+      [PATCH v2 1/3] Clean up
+      [PATCH v2 2/3] New tests
+      [PATCH v2 3/3] Implementation
++
+Only necessary if --compose is also set.  If --compose
+is not set, this will be prompted for.
 
 --subject=<string>::
 	Specify the initial subject of the email thread.
@@ -97,10 +113,19 @@
 	Specify the primary recipient of the emails generated. Generally, this
 	will be the upstream maintainer of the project involved. Default is the
 	value of the 'sendemail.to' configuration value; if that is unspecified,
-	this will be prompted for.
+	and --to-cmd is not specified, this will be prompted for.
 +
 The --to option must be repeated for each user you want on the to list.
 
+--8bit-encoding=<encoding>::
+	When encountering a non-ASCII message or subject that does not
+	declare its encoding, add headers/quoting to indicate it is
+	encoded in <encoding>.  Default is the value of the
+	'sendemail.assume8bitEncoding'; if that is unspecified, this
+	will be prompted for if any non-ASCII files are encountered.
++
+Note that no attempts whatsoever are made to validate the encoding.
+
 
 Sending
 ~~~~~~~
@@ -108,9 +133,10 @@
 --envelope-sender=<address>::
 	Specify the envelope sender used to send the emails.
 	This is useful if your default address is not the address that is
-	subscribed to a list. If you use the sendmail binary, you must have
-	suitable privileges for the -f parameter. Default is the value of
-	the 'sendemail.envelopesender' configuration variable; if that is
+	subscribed to a list. In order to use the 'From' address, set the
+	value to "auto". If you use the sendmail binary, you must have
+	suitable privileges for the -f parameter.  Default is the value of the
+	'sendemail.envelopesender' configuration variable; if that is
 	unspecified, choosing the envelope sender is left to your MTA.
 
 --smtp-encryption=<encryption>::
@@ -118,6 +144,13 @@
 	value reverts to plain SMTP.  Default is the value of
 	'sendemail.smtpencryption'.
 
+--smtp-domain=<FQDN>::
+	Specifies the Fully Qualified Domain Name (FQDN) used in the
+	HELO/EHLO command to the SMTP server.  Some servers require the
+	FQDN to match your IP address.  If not set, git send-email attempts
+	to determine your FQDN automatically.  Default is the value of
+	'sendemail.smtpdomain'.
+
 --smtp-pass[=<password>]::
 	Password for SMTP-AUTH. The argument is optional: If no
 	argument is specified, then the empty string is used as
@@ -148,6 +181,15 @@
 	are also accepted. The port can also be set with the
 	'sendemail.smtpserverport' configuration variable.
 
+--smtp-server-option=<option>::
+	If set, specifies the outgoing SMTP server option to use.
+	Default value can be specified by the 'sendemail.smtpserveroption'
+	configuration option.
++
+The --smtp-server-option option must be repeated for each option you want
+to pass to the server. Likewise, different lines in the configuration files
+must be used for each option.
+
 --smtp-ssl::
 	Legacy alias for '--smtp-encryption ssl'.
 
@@ -156,10 +198,20 @@
 	if a username is not specified (with '--smtp-user' or 'sendemail.smtpuser'),
 	then authentication is not attempted.
 
+--smtp-debug=0|1::
+	Enable (1) or disable (0) debug output. If enabled, SMTP
+	commands and replies will be printed. Useful to debug TLS
+	connection and authentication problems.
 
 Automating
 ~~~~~~~~~~
 
+--to-cmd=<command>::
+	Specify a command to execute once per patch file which
+	should generate patch file specific "To:" entries.
+	Output of this command must be single email address per line.
+	Default is the value of 'sendemail.tocmd' configuration value.
+
 --cc-cmd=<command>::
 	Specify a command to execute once per patch file which
 	should generate patch file specific "Cc:" entries.
@@ -171,8 +223,8 @@
 	email sent.  If disabled with "--no-chain-reply-to", all emails after
 	the first will be sent as replies to the first email sent.  When using
 	this, it is recommended that the first file given be an overview of the
-	entire patch series. Default is the value of the 'sendemail.chainreplyto'
-	configuration value; if that is unspecified, default to --chain-reply-to.
+	entire patch series. Disabled by default, but the 'sendemail.chainreplyto'
+	configuration variable can be used to enable it.
 
 --identity=<identity>::
 	A configuration identity. When given, causes values in the
@@ -275,6 +327,9 @@
 Default is the value of 'sendemail.validate'; if this is not set,
 default to '--validate'.
 
+--force::
+	Send emails even if safety checks would prevent it.
+
 
 CONFIGURATION
 -------------
@@ -298,19 +353,32 @@
 	one of 'always', 'never', 'cc', 'compose', or 'auto'. See '--confirm'
 	in the previous section for the meaning of these values.
 
+EXAMPLE
+-------
+Use gmail as the smtp server
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+To use 'git send-email' to send your patches through the GMail SMTP server,
+edit ~/.gitconfig to specify your account settings:
 
-Author
-------
-Written by Ryan Anderson <ryan@michonline.com>
+	[sendemail]
+		smtpencryption = tls
+		smtpserver = smtp.gmail.com
+		smtpuser = yourname@gmail.com
+		smtpserverport = 587
 
-git-send-email is originally based upon
-send_lots_of_email.pl by Greg Kroah-Hartman.
+Once your commits are ready to be sent to the mailing list, run the
+following commands:
 
+	$ git format-patch --cover-letter -M origin/master -o outgoing/
+	$ edit outgoing/0000-*
+	$ git send-email outgoing/*
 
-Documentation
---------------
-Documentation by Ryan Anderson
+Note: the following perl modules are required
+      Net::SMTP::SSL, MIME::Base64 and Authen::SASL
 
+SEE ALSO
+--------
+linkgit:git-format-patch[1], linkgit:git-imap-send[1], mbox(5)
 
 GIT
 ---
diff --git a/Documentation/git-send-pack.txt b/Documentation/git-send-pack.txt
index 3998218..bd3eaa6 100644
--- a/Documentation/git-send-pack.txt
+++ b/Documentation/git-send-pack.txt
@@ -8,11 +8,12 @@
 
 SYNOPSIS
 --------
+[verse]
 'git send-pack' [--all] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]
 
 DESCRIPTION
 -----------
-Usually you would want to use 'git-push', which is a
+Usually you would want to use 'git push', which is a
 higher-level wrapper of this command, instead. See linkgit:git-push[1].
 
 Invokes 'git-receive-pack' on a possibly remote repository, and
@@ -48,8 +49,8 @@
 	Run verbosely.
 
 --thin::
-	Spend extra cycles to minimize the number of objects to be sent.
-	Use it on slower connection.
+	Send a "thin" pack, which records objects in deltified form based
+	on objects not included in the pack to reduce network traffic.
 
 <host>::
 	A remote host to house the repository.  When this
@@ -86,7 +87,7 @@
 pushed is determined by finding a match that matches the source
 side, and where it is pushed is determined by using the
 destination side. The rules used to match a ref are the same
-rules used by 'git-rev-parse' to resolve a symbolic ref
+rules used by 'git rev-parse' to resolve a symbolic ref
 name. See linkgit:git-rev-parse[1].
 
  - It is an error if <src> does not match exactly one of the
@@ -105,24 +106,15 @@
 
 Without '--force', the <src> ref is stored at the remote only if
 <dst> does not exist, or <dst> is a proper subset (i.e. an
-ancestor) of <src>.  This check, known as "fast forward check",
+ancestor) of <src>.  This check, known as "fast-forward check",
 is performed in order to avoid accidentally overwriting the
 remote ref and lose other peoples' commits from there.
 
-With '--force', the fast forward check is disabled for all refs.
+With '--force', the fast-forward check is disabled for all refs.
 
 Optionally, a <ref> parameter can be prefixed with a plus '+' sign
 to disable the fast-forward check only on that ref.
 
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by Junio C Hamano.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-sh-i18n--envsubst.txt b/Documentation/git-sh-i18n--envsubst.txt
new file mode 100644
index 0000000..2ffaf93
--- /dev/null
+++ b/Documentation/git-sh-i18n--envsubst.txt
@@ -0,0 +1,36 @@
+git-sh-i18n{litdd}envsubst(1)
+=============================
+
+NAME
+----
+git-sh-i18n--envsubst - Git's own envsubst(1) for i18n fallbacks
+
+SYNOPSIS
+--------
+[verse]
+eval_gettext () {
+	printf "%s" "$1" | (
+		export PATH $('git sh-i18n{litdd}envsubst' --variables "$1");
+		'git sh-i18n{litdd}envsubst' "$1"
+	)
+}
+
+DESCRIPTION
+-----------
+
+This is not a command the end user would want to run.  Ever.
+This documentation is meant for people who are studying the
+plumbing scripts and/or are writing new ones.
+
+'git sh-i18n{litdd}envsubst' is Git's stripped-down copy of the GNU
+`envsubst(1)` program that comes with the GNU gettext package. It's
+used internally by linkgit:git-sh-i18n[1] to interpolate the variables
+passed to the `eval_gettext` function.
+
+No promises are made about the interface, or that this
+program won't disappear without warning in the next version
+of Git. Don't use it.
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-sh-i18n.txt b/Documentation/git-sh-i18n.txt
new file mode 100644
index 0000000..60cf49c
--- /dev/null
+++ b/Documentation/git-sh-i18n.txt
@@ -0,0 +1,43 @@
+git-sh-i18n(1)
+==============
+
+NAME
+----
+git-sh-i18n - Git's i18n setup code for shell scripts
+
+SYNOPSIS
+--------
+[verse]
+'. "$(git --exec-path)/git-sh-i18n"'
+
+DESCRIPTION
+-----------
+
+This is not a command the end user would want to run.  Ever.
+This documentation is meant for people who are studying the
+Porcelain-ish scripts and/or are writing new ones.
+
+The 'git sh-i18n scriptlet is designed to be sourced (using
+`.`) by Git's porcelain programs implemented in shell
+script. It provides wrappers for the GNU `gettext` and
+`eval_gettext` functions accessible through the `gettext.sh`
+script, and provides pass-through fallbacks on systems
+without GNU gettext.
+
+FUNCTIONS
+---------
+
+gettext::
+	Currently a dummy fall-through function implemented as a wrapper
+	around `printf(1)`. Will be replaced by a real gettext
+	implementation in a later version.
+
+eval_gettext::
+	Currently a dummy fall-through function implemented as a wrapper
+	around `printf(1)` with variables expanded by the
+	linkgit:git-sh-i18n{litdd}envsubst[1] helper. Will be replaced by a
+	real gettext implementation in a later version.
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-sh-setup.txt b/Documentation/git-sh-setup.txt
index 18f14b5..5e5f1c8 100644
--- a/Documentation/git-sh-setup.txt
+++ b/Documentation/git-sh-setup.txt
@@ -7,6 +7,7 @@
 
 SYNOPSIS
 --------
+[verse]
 '. "$(git --exec-path)/git-sh-setup"'
 
 DESCRIPTION
@@ -16,7 +17,7 @@
 This documentation is meant for people who are studying the
 Porcelain-ish scripts and/or are writing new ones.
 
-The 'git-sh-setup' scriptlet is designed to be sourced (using
+The 'git sh-setup' scriptlet is designed to be sourced (using
 `.`) by other shell scripts to set up some variables pointing at
 the normal git directories and a few helper shell functions.
 
@@ -58,23 +59,29 @@
 	runs chdir to the toplevel of the working tree.
 
 require_work_tree::
-	checks if the repository is a bare repository, and dies
-	if so.  Used by scripts that require working tree
-	(e.g. `checkout`).
+	checks if the current directory is within the working tree
+	of the repository, and otherwise dies.
+
+require_work_tree_exists::
+	checks if the working tree associated with the repository
+	exists, and otherwise dies.  Often done before calling
+	cd_to_toplevel, which is impossible to do if there is no
+	working tree.
+
+require_clean_work_tree <action> [<hint>]::
+	checks that the working tree and index associated with the
+	repository have no uncommitted changes to tracked files.
+	Otherwise it emits an error message of the form `Cannot
+	<action>: <reason>. <hint>`, and dies.  Example:
++
+----------------
+require_clean_work_tree rebase "Please commit or stash them."
+----------------
 
 get_author_ident_from_commit::
 	outputs code for use with eval to set the GIT_AUTHOR_NAME,
 	GIT_AUTHOR_EMAIL and GIT_AUTHOR_DATE variables for a given commit.
 
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-shell.txt b/Documentation/git-shell.txt
index 0f3ad81..9b92506 100644
--- a/Documentation/git-shell.txt
+++ b/Documentation/git-shell.txt
@@ -3,32 +3,31 @@
 
 NAME
 ----
-git-shell - Restricted login shell for GIT-only SSH access
+git-shell - Restricted login shell for Git-only SSH access
 
 
 SYNOPSIS
 --------
-'$(git --exec-path)/git-shell' -c <command> <argument>
+[verse]
+'git shell' [-c <command> <argument>]
 
 DESCRIPTION
 -----------
-This is meant to be used as a login shell for SSH accounts you want
-to restrict to GIT pull/push access only. It permits execution only
-of server-side GIT commands implementing the pull/push functionality.
-The commands can be executed only by the '-c' option; the shell is not
-interactive.
 
-Currently, only four commands are permitted to be called, 'git-receive-pack'
-'git-upload-pack' and 'git-upload-archive' with a single required argument, or
-'cvs server' (to invoke 'git-cvsserver').
+A login shell for SSH accounts to provide restricted Git access. When
+'-c' is given, the program executes <command> non-interactively;
+<command> can be one of 'git receive-pack', 'git upload-pack', 'git
+upload-archive', 'cvs server', or a command in COMMAND_DIR. The shell
+is started in interactive mode when no arguments are given; in this
+case, COMMAND_DIR must exist, and any of the executables in it can be
+invoked.
 
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
+'cvs server' is a special command which executes git-cvsserver.
 
-Documentation
---------------
-Documentation by Petr Baudis and the git-list <git@vger.kernel.org>.
+COMMAND_DIR is the path "$HOME/git-shell-commands". The user must have
+read and execute permissions to the directory in order to execute the
+programs in it. The programs are executed with a cwd of $HOME, and
+<argument> is parsed as a command-line string.
 
 GIT
 ---
diff --git a/Documentation/git-shortlog.txt b/Documentation/git-shortlog.txt
index 42463a9..01d8417 100644
--- a/Documentation/git-shortlog.txt
+++ b/Documentation/git-shortlog.txt
@@ -3,22 +3,27 @@
 
 NAME
 ----
-git-shortlog - Summarize 'git-log' output
+git-shortlog - Summarize 'git log' output
 
 SYNOPSIS
 --------
 [verse]
 git log --pretty=short | 'git shortlog' [-h] [-n] [-s] [-e] [-w]
-git shortlog [-n|--numbered] [-s|--summary] [-e|--email] [-w[<width>[,<indent1>[,<indent2>]]]] [<committish>...]
+'git shortlog' [-n|--numbered] [-s|--summary] [-e|--email] [-w[<width>[,<indent1>[,<indent2>]]]] <commit>...
 
 DESCRIPTION
 -----------
-Summarizes 'git-log' output in a format suitable for inclusion
+Summarizes 'git log' output in a format suitable for inclusion
 in release announcements. Each commit will be grouped by author and
 the first line of the commit message will be shown.
 
 Additionally, "[PATCH]" will be stripped from the commit description.
 
+If no revisions are passed on the command line and either standard input
+is not a terminal or there is no current branch, 'git shortlog' will
+output a summary of the log read from standard input, without
+reference to the current repository.
+
 OPTIONS
 -------
 
@@ -39,6 +44,14 @@
 --email::
 	Show the email address of each author.
 
+--format[=<format>]::
+	Instead of the commit subject, use some other information to
+	describe each commit.  '<format>' can be any string accepted
+	by the `--format` option of 'git log', such as '* [%h] %s'.
+	(See the "PRETTY FORMATS" section of linkgit:git-log[1].)
+
+	Each pretty-printed commit will be rewrapped before it is shown.
+
 -w[<width>[,<indent1>[,<indent2>]]]::
 	Linewrap the output by wrapping each line at `width`.  The first
 	line of each entry is indented by `indent1` spaces, and the second
@@ -55,15 +68,6 @@
 
 include::mailmap.txt[]
 
-
-Author
-------
-Written by Jeff Garzik <jgarzik@pobox.com>
-
-Documentation
---------------
-Documentation by Junio C Hamano.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-show-branch.txt b/Documentation/git-show-branch.txt
index 7343361..a8e77b5 100644
--- a/Documentation/git-show-branch.txt
+++ b/Documentation/git-show-branch.txt
@@ -9,19 +9,18 @@
 --------
 [verse]
 'git show-branch' [-a|--all] [-r|--remotes] [--topo-order | --date-order]
-		[--current] [--color | --no-color] [--sparse]
+		[--current] [--color[=<when>] | --no-color] [--sparse]
 		[--more=<n> | --list | --independent | --merge-base]
 		[--no-name | --sha1-name] [--topics]
-		[<rev> | <glob>]...
-
+		[(<rev> | <glob>)...]
 'git show-branch' (-g|--reflog)[=<n>[,<base>]] [--list] [<ref>]
 
 DESCRIPTION
 -----------
 
 Shows the commit ancestry graph starting from the commits named
-with <rev>s or <globs>s (or all refs under $GIT_DIR/refs/heads
-and/or $GIT_DIR/refs/tags) semi-visually.
+with <rev>s or <globs>s (or all refs under refs/heads
+and/or refs/tags) semi-visually.
 
 It cannot show more than 29 branches and commits at a time.
 
@@ -32,13 +31,13 @@
 OPTIONS
 -------
 <rev>::
-	Arbitrary extended SHA1 expression (see linkgit:git-rev-parse[1])
+	Arbitrary extended SHA1 expression (see linkgit:gitrevisions[7])
 	that typically names a branch head or a tag.
 
 <glob>::
 	A glob pattern that matches branch or tag names under
-	$GIT_DIR/refs.  For example, if you have many topic
-	branches under $GIT_DIR/refs/heads/topic, giving
+	refs/.  For example, if you have many topic
+	branches under refs/heads/topic, giving
 	`topic/*` would show all of them.
 
 -r::
@@ -117,13 +116,15 @@
 	When no explicit <ref> parameter is given, it defaults to the
 	current branch (or `HEAD` if it is detached).
 
---color::
+--color[=<when>]::
 	Color the status sign (one of these: `*` `!` `+` `-`) of each commit
 	corresponding to the branch it's in.
+	The value must be always (the default), never, or auto.
 
 --no-color::
 	Turn off colored output, even when the configuration file gives the
 	default to color output.
+	Same as `--color=never`.
 
 Note that --more, --list, --independent and --merge-base options
 are mutually exclusive.
@@ -166,17 +167,17 @@
 ------------------------------------------------
 
 These three branches all forked from a common commit, [master],
-whose commit message is "Add \'git show-branch\'". The "fixes"
-branch adds one commit "Introduce "reset type" flag to "git reset"".
-The "mhf" branch adds many other commits. The current branch
-is "master".
+whose commit message is "Add {apostrophe}git show-branch{apostrophe}".
+The "fixes" branch adds one commit "Introduce "reset type" flag to
+"git reset"". The "mhf" branch adds many other commits.
+The current branch is "master".
 
 
 EXAMPLE
 -------
 
 If you keep your primary branches immediately under
-`$GIT_DIR/refs/heads`, and topic branches in subdirectories of
+`refs/heads`, and topic branches in subdirectories of
 it, having the following in the configuration file may help:
 
 ------------
@@ -198,17 +199,6 @@
 Without `--list`, the output also shows how these tips are
 topologically related with each other.
 
-
-Author
-------
-Written by Junio C Hamano <gitster@pobox.com>
-
-
-Documentation
---------------
-Documentation by Junio C Hamano.
-
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-show-index.txt b/Documentation/git-show-index.txt
index e3285aa..2dcbbb2 100644
--- a/Documentation/git-show-index.txt
+++ b/Documentation/git-show-index.txt
@@ -8,27 +8,19 @@
 
 SYNOPSIS
 --------
+[verse]
 'git show-index' < idx-file
 
 
 DESCRIPTION
 -----------
 Reads given idx file for packed git archive created with
-'git-pack-objects' command, and dumps its contents.
+'git pack-objects' command, and dumps its contents.
 
 The information it outputs is subset of what you can get from
-'git-verify-pack -v'; this command only shows the packfile
+'git verify-pack -v'; this command only shows the packfile
 offset and SHA1 of each object.
 
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by Junio C Hamano
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-show-ref.txt b/Documentation/git-show-ref.txt
index f4429bd..5dbcd47 100644
--- a/Documentation/git-show-ref.txt
+++ b/Documentation/git-show-ref.txt
@@ -8,9 +8,9 @@
 SYNOPSIS
 --------
 [verse]
-'git show-ref' [-q|--quiet] [--verify] [-h|--head] [-d|--dereference]
+'git show-ref' [-q|--quiet] [--verify] [--head] [-d|--dereference]
 	     [-s|--hash[=<n>]] [--abbrev[=<n>]] [--tags]
-	     [--heads] [--] <pattern>...
+	     [--heads] [--] [<pattern>...]
 'git show-ref' --exclude-existing[=<pattern>] < ref-list
 
 DESCRIPTION
@@ -30,7 +30,6 @@
 OPTIONS
 -------
 
--h::
 --head::
 
 	Show the HEAD reference.
@@ -45,7 +44,7 @@
 -d::
 --dereference::
 
-	Dereference tags into object IDs as well. They will be shown with "^{}"
+	Dereference tags into object IDs as well. They will be shown with "{caret}{}"
 	appended.
 
 -s::
@@ -73,10 +72,10 @@
 
 --exclude-existing[=<pattern>]::
 
-	Make 'git-show-ref' act as a filter that reads refs from stdin of the
-	form "^(?:<anything>\s)?<refname>(?:\^\{\})?$" and performs the
-	following actions on each:
-	(1) strip "^{}" at the end of line if any;
+	Make 'git show-ref' act as a filter that reads refs from stdin of the
+	form "`^(?:<anything>\s)?<refname>(?:\^{})?$`"
+	and performs the following actions on each:
+	(1) strip "{caret}{}" at the end of line if any;
 	(2) ignore if pattern is provided and does not head-match refname;
 	(3) warn if refname is not a well-formed refname and skip;
 	(4) ignore if refname is a ref that exists in the local repository;
@@ -85,7 +84,11 @@
 
 <pattern>...::
 
-	Show references matching one or more patterns.
+	Show references matching one or more patterns. Patterns are matched from
+	the end of the full name, and only complete parts are matched, e.g.
+	'master' matches 'refs/heads/master', 'refs/remotes/origin/master',
+	'refs/tags/jedi/master' but not 'refs/heads/mymaster' nor
+	'refs/remotes/master/jedi'.
 
 OUTPUT
 ------
@@ -136,7 +139,7 @@
 
 will only match the exact branch called "master".
 
-If nothing matches, 'git-show-ref' will return an error code of 1,
+If nothing matches, 'git show-ref' will return an error code of 1,
 and in the case of verification, it will show an error message.
 
 For scripting, you can ask it to be quiet with the "--quiet" flag, which
@@ -164,14 +167,15 @@
 
 to get a listing of all tags together with what they dereference.
 
+FILES
+-----
+`.git/refs/*`, `.git/packed-refs`
+
 SEE ALSO
 --------
-linkgit:git-ls-remote[1]
-
-AUTHORS
--------
-Written by Linus Torvalds <torvalds@osdl.org>.
-Man page by Jonas Fonseca <fonseca@diku.dk>.
+linkgit:git-ls-remote[1],
+linkgit:git-update-ref[1],
+linkgit:gitrepository-layout[5]
 
 GIT
 ---
diff --git a/Documentation/git-show.txt b/Documentation/git-show.txt
index 48b612e..ae4edcc 100644
--- a/Documentation/git-show.txt
+++ b/Documentation/git-show.txt
@@ -8,6 +8,7 @@
 
 SYNOPSIS
 --------
+[verse]
 'git show' [options] <object>...
 
 DESCRIPTION
@@ -16,16 +17,16 @@
 
 For commits it shows the log message and textual diff. It also
 presents the merge commit in a special format as produced by
-'git-diff-tree --cc'.
+'git diff-tree --cc'.
 
 For tags, it shows the tag message and the referenced objects.
 
-For trees, it shows the names (equivalent to 'git-ls-tree'
+For trees, it shows the names (equivalent to 'git ls-tree'
 with \--name-only).
 
 For plain blobs, it shows the plain contents.
 
-The command takes options applicable to the 'git-diff-tree' command to
+The command takes options applicable to the 'git diff-tree' command to
 control how the changes the commit introduces are shown.
 
 This manual page describes only the most frequently used options.
@@ -36,7 +37,7 @@
 <object>...::
 	The names of objects to show.
 	For a more complete list of ways to spell object names, see
-	"SPECIFYING REVISIONS" section in linkgit:git-rev-parse[1].
+	"SPECIFYING REVISIONS" section in linkgit:gitrevisions[7].
 
 include::pretty-options.txt[]
 
@@ -47,19 +48,23 @@
 EXAMPLES
 --------
 
-git show v1.0.0::
+`git show v1.0.0`::
 	Shows the tag `v1.0.0`, along with the object the tags
 	points at.
 
-git show v1.0.0^\{tree\}::
+`git show v1.0.0^{tree}`::
 	Shows the tree pointed to by the tag `v1.0.0`.
 
-git show next~10:Documentation/README::
+`git show -s --format=%s v1.0.0^{commit}`::
+	Shows the subject of the commit pointed to by the
+	tag `v1.0.0`.
+
+`git show next~10:Documentation/README`::
 	Shows the contents of the file `Documentation/README` as
 	they were current in the 10th last commit of the branch
 	`next`.
 
-git show master:Makefile master:t/Makefile::
+`git show master:Makefile master:t/Makefile`::
 	Concatenates the contents of said Makefiles in the head
 	of the branch `master`.
 
@@ -68,17 +73,6 @@
 
 include::i18n.txt[]
 
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org> and
-Junio C Hamano <gitster@pobox.com>.  Significantly enhanced by
-Johannes Schindelin <Johannes.Schindelin@gmx.de>.
-
-
-Documentation
--------------
-Documentation by David Greaves, Petr Baudis and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-stage.txt b/Documentation/git-stage.txt
index 7f251a5..ba3fe0d 100644
--- a/Documentation/git-stage.txt
+++ b/Documentation/git-stage.txt
@@ -17,3 +17,7 @@
 
 This is a synonym for linkgit:git-add[1].  Please refer to the
 documentation of that command.
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index 1c64a02..0aa4e20 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -13,14 +13,15 @@
 'git stash' drop [-q|--quiet] [<stash>]
 'git stash' ( pop | apply ) [--index] [-q|--quiet] [<stash>]
 'git stash' branch <branchname> [<stash>]
-'git stash' [save [--keep-index] [-q|--quiet] [<message>]]
+'git stash' [save [--patch] [-k|--[no-]keep-index] [-q|--quiet]
+	     [-u|--include-untracked] [-a|--all] [<message>]]
 'git stash' clear
 'git stash' create
 
 DESCRIPTION
 -----------
 
-Use 'git stash' when you want to record the current state of the
+Use `git stash` when you want to record the current state of the
 working directory and the index, but want to go back to a clean
 working directory.  The command saves your local modifications away
 and reverts the working directory to match the `HEAD` commit.
@@ -33,29 +34,47 @@
 you can give a more descriptive message on the command line when
 you create one.
 
-The latest stash you created is stored in `$GIT_DIR/refs/stash`; older
+The latest stash you created is stored in `refs/stash`; older
 stashes are found in the reflog of this reference and can be named using
-the usual reflog syntax (e.g. `stash@\{0}` is the most recently
-created stash, `stash@\{1}` is the one before it, `stash@\{2.hours.ago}`
+the usual reflog syntax (e.g. `stash@{0}` is the most recently
+created stash, `stash@{1}` is the one before it, `stash@{2.hours.ago}`
 is also possible).
 
 OPTIONS
 -------
 
-save [--keep-index] [-q|--quiet] [<message>]::
+save [-p|--patch] [--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [<message>]::
 
 	Save your local modifications to a new 'stash', and run `git reset
-	--hard` to revert them.  This is the default action when no
-	subcommand is given. The <message> part is optional and gives
-	the description along with the stashed state.
+	--hard` to revert them.  The <message> part is optional and gives
+	the description along with the stashed state.  For quickly making
+	a snapshot, you can omit _both_ "save" and <message>, but giving
+	only <message> does not trigger this action to prevent a misspelled
+	subcommand from making an unwanted stash.
 +
 If the `--keep-index` option is used, all changes already added to the
 index are left intact.
++
+If the `--include-untracked` option is used, all untracked files are also
+stashed and then cleaned up with `git clean`, leaving the working directory
+in a very clean state. If the `--all` option is used instead then the
+ignored files are stashed and cleaned in addition to the untracked files.
++
+With `--patch`, you can interactively select hunks from the diff
+between HEAD and the working tree to be stashed.  The stash entry is
+constructed such that its index state is the same as the index state
+of your repository, and its worktree contains only the changes you
+selected interactively.  The selected changes are then rolled back
+from your worktree. See the ``Interactive Mode'' section of
+linkgit:git-add[1] to learn how to operate the `--patch` mode.
++
+The `--patch` option implies `--keep-index`.  You can use
+`--no-keep-index` to override this.
 
 list [<options>]::
 
 	List the stashes that you currently have.  Each 'stash' is listed
-	with its name (e.g. `stash@\{0}` is the latest stash, `stash@\{1}` is
+	with its name (e.g. `stash@{0}` is the latest stash, `stash@{1}` is
 	the one before, etc.), the name of the branch that was current when the
 	stash was made, and a short description of the commit the stash was
 	based on.
@@ -65,7 +84,7 @@
 stash@{1}: On master: 9cc0589... Add git-stash
 ----------------------------------------------------------------
 +
-The command takes options applicable to the 'git-log'
+The command takes options applicable to the 'git log'
 command to control what is shown and how. See linkgit:git-log[1].
 
 show [<stash>]::
@@ -73,8 +92,8 @@
 	Show the changes recorded in the stash as a diff between the
 	stashed state and its original parent. When no `<stash>` is given,
 	shows the latest one. By default, the command shows the diffstat, but
-	it will accept any format known to 'git-diff' (e.g., `git stash show
-	-p stash@\{1}` to view the second most recent stash in patch form).
+	it will accept any format known to 'git diff' (e.g., `git stash show
+	-p stash@{1}` to view the second most recent stash in patch form).
 
 pop [--index] [-q|--quiet] [<stash>]::
 
@@ -92,18 +111,22 @@
 have conflicts (which are stored in the index, where you therefore can no
 longer apply the changes as they were originally).
 +
-When no `<stash>` is given, `stash@\{0}` is assumed.
+When no `<stash>` is given, `stash@{0}` is assumed, otherwise `<stash>` must
+be a reference of the form `stash@{<revision>}`.
 
 apply [--index] [-q|--quiet] [<stash>]::
 
-	Like `pop`, but do not remove the state from the stash list.
+	Like `pop`, but do not remove the state from the stash list. Unlike `pop`,
+	`<stash>` may be any commit that looks like a commit created by
+	`stash save` or `stash create`.
 
 branch <branchname> [<stash>]::
 
 	Creates and checks out a new branch named `<branchname>` starting from
 	the commit at which the `<stash>` was originally created, applies the
-	changes recorded in `<stash>` to the new working tree and index, then
-	drops the `<stash>` if that completes successfully. When no `<stash>`
+	changes recorded in `<stash>` to the new working tree and index.
+	If that succeeds, and `<stash>` is a reference of the form
+	`stash@{<revision>}`, it then drops the `<stash>`. When no `<stash>`
 	is given, applies the latest one.
 +
 This is useful if the branch on which you ran `git stash save` has
@@ -114,12 +137,15 @@
 
 clear::
 	Remove all the stashed states. Note that those states will then
-	be subject to pruning, and may be difficult or impossible to recover.
+	be subject to pruning, and may be impossible to recover (see
+	'Examples' below for a possible strategy).
 
 drop [-q|--quiet] [<stash>]::
 
 	Remove a single stashed state from the stash list. When no `<stash>`
-	is given, it removes the latest one. i.e. `stash@\{0}`
+	is given, it removes the latest one. i.e. `stash@{0}`, otherwise
+	`<stash>` must a valid stash log reference of the form
+	`stash@{<revision>}`.
 
 create::
 
@@ -188,7 +214,7 @@
 # ... continue hacking ...
 ----------------------------------------------------------------
 +
-You can use 'git-stash' to simplify the above, like this:
+You can use 'git stash' to simplify the above, like this:
 +
 ----------------------------------------------------------------
 # ... hack hack hack ...
@@ -217,6 +243,20 @@
 $ git commit foo -m 'Remaining parts'
 ----------------------------------------------------------------
 
+Recovering stashes that were cleared/dropped erroneously::
+
+If you mistakenly drop or clear stashes, they cannot be recovered
+through the normal safety mechanisms.  However, you can try the
+following incantation to get a list of stashes that are still in your
+repository, but not reachable any more:
++
+----------------------------------------------------------------
+git fsck --unreachable |
+grep commit | cut -d\  -f3 |
+xargs git log --merges --no-walk --grep=WIP
+----------------------------------------------------------------
+
+
 SEE ALSO
 --------
 linkgit:git-checkout[1],
@@ -224,10 +264,6 @@
 linkgit:git-reflog[1],
 linkgit:git-reset[1]
 
-AUTHOR
-------
-Written by Nanako Shiraishi <nanako3@bluebottle.com>
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt
index 84f60f3..67e5f53 100644
--- a/Documentation/git-status.txt
+++ b/Documentation/git-status.txt
@@ -8,7 +8,8 @@
 
 SYNOPSIS
 --------
-'git status' <options>...
+[verse]
+'git status' [<options>...] [--] [<pathspec>...]
 
 DESCRIPTION
 -----------
@@ -17,28 +18,173 @@
 tree and the index file, and paths in the working tree that are not
 tracked by git (and are not ignored by linkgit:gitignore[5]). The first
 are what you _would_ commit by running `git commit`; the second and
-third are what you _could_ commit by running 'git-add' before running
+third are what you _could_ commit by running 'git add' before running
 `git commit`.
 
-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'.
+OPTIONS
+-------
 
-If there is no path that is different between the index file and
-the current HEAD commit (i.e., there is nothing to commit by running
-`git commit`), the command exits with non-zero status.
+-s::
+--short::
+	Give the output in the short-format.
+
+-b::
+--branch::
+	Show the branch and tracking info even in short-format.
+
+--porcelain::
+	Give the output in an easy-to-parse format for scripts.
+	This is similar to the short output, but will remain stable
+	across git versions and regardless of user configuration. See
+	below for details.
+
+-u[<mode>]::
+--untracked-files[=<mode>]::
+	Show untracked files.
++
+The mode parameter is optional (defaults to 'all'), and is used to
+specify the handling of untracked files; when -u is not used, the
+default is 'normal', i.e. show untracked files and directories.
++
+The possible options are:
++
+	- 'no'     - Show no untracked files
+	- 'normal' - Shows untracked files and directories
+	- 'all'    - Also shows individual files in untracked directories.
++
+The default can be changed using the status.showUntrackedFiles
+configuration variable documented in linkgit:git-config[1].
+
+--ignore-submodules[=<when>]::
+	Ignore changes to submodules when looking for changes. <when> can be
+	either "none", "untracked", "dirty" or "all", which is the default.
+	Using "none" will consider the submodule modified when it either contains
+	untracked or modified files or its HEAD differs from the commit recorded
+	in the superproject and can be used to override any settings of the
+	'ignore' option in linkgit:git-config[1] or linkgit:gitmodules[5]. When
+	"untracked" is used submodules are not considered dirty when they only
+	contain untracked content (but they are still scanned for modified
+	content). Using "dirty" ignores all changes to the work tree of submodules,
+	only changes to the commits stored in the superproject are shown (this was
+	the behavior before 1.7.0). Using "all" hides all changes to submodules
+	(and suppresses the output of submodule summaries when the config option
+	`status.submodulesummary` is set).
+
+--ignored::
+	Show ignored files as well.
+
+-z::
+	Terminate entries with NUL, instead of LF.  This implies
+	the `--porcelain` output format if no other format is given.
+
+--column[=<options>]::
+--no-column::
+	Display untracked files in columns. See configuration variable
+	column.status for option syntax.`--column` and `--no-column`
+	without options are equivalent to 'always' and 'never'
+	respectively.
 
 
 OUTPUT
 ------
 The output from this command is designed to be used as a commit
 template comment, and all the output lines are prefixed with '#'.
+The default, long format, is designed to be human readable,
+verbose and descriptive.  Its contents and format are subject to change
+at any time.
 
 The paths mentioned in the output, unlike many other git commands, are
 made relative to the current directory if you are working in a
 subdirectory (this is on purpose, to help cutting and pasting). See
 the status.relativePaths config option below.
 
+Short Format
+~~~~~~~~~~~~
+
+In the short-format, the status of each path is shown as
+
+	XY PATH1 -> PATH2
+
+where `PATH1` is the path in the `HEAD`, and the " `-> PATH2`" part is
+shown only when `PATH1` corresponds to a different path in the
+index/worktree (i.e. the file is renamed). The 'XY' is a two-letter
+status code.
+
+The fields (including the `->`) are separated from each other by a
+single space. If a filename contains whitespace or other nonprintable
+characters, that field will be quoted in the manner of a C string
+literal: surrounded by ASCII double quote (34) characters, and with
+interior special characters backslash-escaped.
+
+For paths with merge conflicts, `X` and 'Y' show the modification
+states of each side of the merge. For paths that do not have merge
+conflicts, `X` shows the status of the index, and `Y` shows the status
+of the work tree.  For untracked paths, `XY` are `??`.  Other status
+codes can be interpreted as follows:
+
+* ' ' = unmodified
+* 'M' = modified
+* 'A' = added
+* 'D' = deleted
+* 'R' = renamed
+* 'C' = copied
+* 'U' = updated but unmerged
+
+Ignored files are not listed, unless `--ignored` option is in effect,
+in which case `XY` are `!!`.
+
+    X          Y     Meaning
+    -------------------------------------------------
+              [MD]   not updated
+    M        [ MD]   updated in index
+    A        [ MD]   added to index
+    D         [ M]   deleted from index
+    R        [ MD]   renamed in index
+    C        [ MD]   copied in index
+    [MARC]           index and work tree matches
+    [ MARC]     M    work tree changed since index
+    [ MARC]     D    deleted in work tree
+    -------------------------------------------------
+    D           D    unmerged, both deleted
+    A           U    unmerged, added by us
+    U           D    unmerged, deleted by them
+    U           A    unmerged, added by them
+    D           U    unmerged, deleted by us
+    A           A    unmerged, both added
+    U           U    unmerged, both modified
+    -------------------------------------------------
+    ?           ?    untracked
+    !           !    ignored
+    -------------------------------------------------
+
+If -b is used the short-format status is preceded by a line
+
+## branchname tracking info
+
+Porcelain Format
+~~~~~~~~~~~~~~~~
+
+The porcelain format is similar to the short format, but is guaranteed
+not to change in a backwards-incompatible way between git versions or
+based on user configuration. This makes it ideal for parsing by scripts.
+The description of the short format above also describes the porcelain
+format, with a few exceptions:
+
+1. The user's color.status configuration is not respected; color will
+   always be off.
+
+2. The user's status.relativePaths configuration is not respected; paths
+   shown will always be relative to the repository root.
+
+There is also an alternate -z format recommended for machine parsing. In
+that format, the status field is the same, but some other things
+change.  First, the '\->' is omitted from rename entries and the field
+order is reversed (e.g 'from \-> to' becomes 'to from'). Second, a NUL
+(ASCII 0) follows each filename, replacing space as a field separator
+and the terminating newline (but a space still separates the status
+field from the first filename).  Third, filenames containing special
+characters are not specially formatted; no quoting or
+backslash-escaping is performed.
 
 CONFIGURATION
 -------------
@@ -53,23 +199,14 @@
 directory.
 
 If `status.submodulesummary` is set to a non zero number or true (identical
-to -1 or an unlimited number), the submodule summary will be enabled and a
-summary of commits for modified submodules will be shown (see --summary-limit
-option of linkgit:git-submodule[1]).
+to -1 or an unlimited number), the submodule summary will be enabled for
+the long format and a summary of commits for modified submodules will be
+shown (see --summary-limit option of linkgit:git-submodule[1]).
 
 SEE ALSO
 --------
 linkgit:gitignore[5]
 
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org> and
-Junio C Hamano <gitster@pobox.com>.
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-stripspace.txt b/Documentation/git-stripspace.txt
index 7508c0e..a80d946 100644
--- a/Documentation/git-stripspace.txt
+++ b/Documentation/git-stripspace.txt
@@ -3,33 +3,83 @@
 
 NAME
 ----
-git-stripspace - Filter out empty lines
+git-stripspace - Remove unnecessary whitespace
 
 
 SYNOPSIS
 --------
-'git stripspace' [-s | --strip-comments] < <stream>
+[verse]
+'git stripspace' [-s | --strip-comments] < input
 
 DESCRIPTION
 -----------
-Remove multiple empty lines, and empty lines at beginning and end.
+
+Clean the input in the manner used by 'git' for text such as commit
+messages, notes, tags and branch descriptions.
+
+With no arguments, this will:
+
+- remove trailing whitespace from all lines
+- collapse multiple consecutive empty lines into one empty line
+- remove empty lines from the beginning and end of the input
+- add a missing '\n' to the last line if necessary.
+
+In the case where the input consists entirely of whitespace characters, no
+output will be produced.
+
+*NOTE*: This is intended for cleaning metadata, prefer the `--whitespace=fix`
+mode of linkgit:git-apply[1] for correcting whitespace of patches or files in
+the repository.
 
 OPTIONS
 -------
 -s::
 --strip-comments::
-	In addition to empty lines, also strip lines starting with '#'.
+	Skip and remove all lines starting with '#'.
 
-<stream>::
-	Byte stream to act on.
+EXAMPLES
+--------
 
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
+Given the following noisy input with '$' indicating the end of a line:
 
-Documentation
---------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
+--------
+|A brief introduction   $
+|   $
+|$
+|A new paragraph$
+|# with a commented-out line    $
+|explaining lots of stuff.$
+|$
+|# An old paragraph, also commented-out. $
+|      $
+|The end.$
+|  $
+---------
+
+Use 'git stripspace' with no arguments to obtain:
+
+--------
+|A brief introduction$
+|$
+|A new paragraph$
+|# with a commented-out line$
+|explaining lots of stuff.$
+|$
+|# An old paragraph, also commented-out.$
+|$
+|The end.$
+---------
+
+Use 'git stripspace --strip-comments' to obtain:
+
+--------
+|A brief introduction$
+|$
+|A new paragraph$
+|explaining lots of stuff.$
+|$
+|The end.$
+---------
 
 GIT
 ---
diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt
index 7dd73ae..fbbbcb2 100644
--- a/Documentation/git-submodule.txt
+++ b/Documentation/git-submodule.txt
@@ -9,14 +9,15 @@
 SYNOPSIS
 --------
 [verse]
-'git submodule' [--quiet] add [-b branch]
-	      [--reference <repository>] [--] <repository> <path>
-'git submodule' [--quiet] status [--cached] [--] [<path>...]
+'git submodule' [--quiet] add [-b branch] [-f|--force]
+	      [--reference <repository>] [--] <repository> [<path>]
+'git submodule' [--quiet] status [--cached] [--recursive] [--] [<path>...]
 'git submodule' [--quiet] init [--] [<path>...]
 'git submodule' [--quiet] update [--init] [-N|--no-fetch] [--rebase]
-	      [--reference <repository>] [--merge] [--] [<path>...]
-'git submodule' [--quiet] summary [--cached] [--summary-limit <n>] [commit] [--] [<path>...]
-'git submodule' [--quiet] foreach <command>
+	      [--reference <repository>] [--merge] [--recursive] [--] [<path>...]
+'git submodule' [--quiet] summary [--cached|--files] [(-n|--summary-limit) <n>]
+	      [commit] [--] [<path>...]
+'git submodule' [--quiet] foreach [--recursive] <command>
 'git submodule' [--quiet] sync [--] [<path>...]
 
 
@@ -42,9 +43,9 @@
 Submodules are composed from a so-called `gitlink` tree entry
 in the main repository that refers to a particular commit object
 within the inner repository that is completely separate.
-A record in the `.gitmodules` file at the root of the source
-tree assigns a logical name to the submodule and describes
-the default URL the submodule shall be cloned from.
+A record in the `.gitmodules` (see linkgit:gitmodules[5]) file at the
+root of the source tree assigns a logical name to the submodule and
+describes the default URL the submodule shall be cloned from.
 The logical name can be used for overriding this URL within your
 local repository configuration (see 'submodule init').
 
@@ -69,12 +70,23 @@
 	to the changeset to be committed next to the current
 	project: the current project is termed the "superproject".
 +
-This requires two arguments: <repository> and <path>.
+This requires at least one argument: <repository>. The optional
+argument <path> is the relative location for the cloned submodule
+to exist in the superproject. If <path> is not given, the
+"humanish" part of the source repository is used ("repo" for
+"/path/to/repo.git" and "foo" for "host.xz:foo/.git").
 +
 <repository> is the URL of the new submodule's origin repository.
 This may be either an absolute URL, or (if it begins with ./
 or ../), the location relative to the superproject's origin
-repository.
+repository (Please note that to specify a repository 'foo.git'
+which is located right next to a superproject 'bar.git', you'll
+have to use '../foo.git' instead of './foo.git' - as one might expect
+when following the rules for relative URLs - because the evaluation
+of relative URLs in Git is identical to that of relative directories).
+If the superproject doesn't have an origin configured
+the superproject is its own authoritative upstream and the current
+working directory is used instead.
 +
 <path> is the relative location for the cloned submodule to
 exist in the superproject. If <path> does not exist, then the
@@ -95,48 +107,75 @@
 status::
 	Show the status of the submodules. This will print the SHA-1 of the
 	currently checked out commit for each submodule, along with the
-	submodule path and the output of 'git-describe' for the
+	submodule path and the output of 'git describe' for the
 	SHA-1. Each SHA-1 will be prefixed with `-` if the submodule is not
-	initialized and `+` if the currently checked out submodule commit
+	initialized, `+` if the currently checked out submodule commit
 	does not match the SHA-1 found in the index of the containing
-	repository. This command is the default command for 'git-submodule'.
+	repository and `U` if the submodule has merge conflicts.
+	This command is the default command for 'git submodule'.
++
+If `--recursive` is specified, this command will recurse into nested
+submodules, and show their status as well.
++
+If you are only interested in changes of the currently initialized
+submodules with respect to the commit recorded in the index or the HEAD,
+linkgit:git-status[1] and linkgit:git-diff[1] will provide that information
+too (and can also report changes to a submodule's work tree).
 
 init::
 	Initialize the submodules, i.e. register each submodule name
 	and url found in .gitmodules into .git/config.
+	It will also copy the value of `submodule.$name.update` into
+	.git/config.
 	The key used in .git/config is `submodule.$name.url`.
 	This command does not alter existing information in .git/config.
 	You can then customize the submodule clone URLs in .git/config
-	for your local setup and proceed to 'git submodule update';
-	you can also just use 'git submodule update --init' without
+	for your local setup and proceed to `git submodule update`;
+	you can also just use `git submodule update --init` without
 	the explicit 'init' step if you do not intend to customize
 	any submodule locations.
 
 update::
 	Update the registered submodules, i.e. clone missing submodules and
 	checkout the commit specified in the index of the containing repository.
-	This will make the submodules HEAD be detached unless '--rebase' or
-	'--merge' is specified or the key `submodule.$name.update` is set to
-	`rebase` or `merge`.
+	This will make the submodules HEAD be detached unless `--rebase` or
+	`--merge` is specified or the key `submodule.$name.update` is set to
+	`rebase`, `merge` or `none`. `none` can be overridden by specifying
+	`--checkout`.
 +
 If the submodule is not yet initialized, and you just want to use the
 setting as stored in .gitmodules, you can automatically initialize the
-submodule with the --init option.
+submodule with the `--init` option.
++
+If `--recursive` is specified, this command will recurse into the
+registered submodules, and update any nested submodules within.
 
 summary::
 	Show commit summary between the given commit (defaults to HEAD) and
 	working tree/index. For a submodule in question, a series of commits
 	in the submodule between the given super project commit and the
-	index or working tree (switched by --cached) are shown.
+	index or working tree (switched by `--cached`) are shown. If the option
+	`--files` is given, show the series of commits in the submodule between
+	the index of the super project and the working tree of the submodule
+	(this option doesn't allow to use the `--cached` option or to provide an
+	explicit commit).
++
+Using the `--submodule=log` option with linkgit:git-diff[1] will provide that
+information too.
 
 foreach::
 	Evaluates an arbitrary shell command in each checked out submodule.
-	The command has access to the variables $path and $sha1:
+	The command has access to the variables $name, $path, $sha1 and
+	$toplevel:
+	$name is the name of the relevant submodule section in .gitmodules,
 	$path is the name of the submodule directory relative to the
-	superproject, and $sha1 is the commit as recorded in the superproject.
+	superproject, $sha1 is the commit as recorded in the superproject,
+	and $toplevel is the absolute path to the top-level of the superproject.
 	Any submodules defined in the superproject but not checked out are
-	ignored by this command. Unless given --quiet, foreach prints the name
+	ignored by this command. Unless given `--quiet`, foreach prints the name
 	of each submodule before evaluating the command.
+	If `--recursive` is given, submodules are traversed recursively (i.e.
+	the given shell command is evaluated in nested submodules as well).
 	A non-zero return from the command in any submodule causes
 	the processing to terminate. This can be overridden by adding '|| :'
 	to the end of the command.
@@ -147,12 +186,14 @@
 
 sync::
 	Synchronizes submodules' remote URL configuration setting
-	to the value specified in .gitmodules.  This is useful when
+	to the value specified in .gitmodules. It will only affect those
+	submodules which already have a URL entry in .git/config (that is the
+	case when they are initialized or freshly added). This is useful when
 	submodule URLs change upstream and you need to update your local
 	repositories accordingly.
 +
 "git submodule sync" synchronizes all submodules while
-"git submodule sync -- A" synchronizes submodule "A" only.
+"git submodule sync \-- A" synchronizes submodule "A" only.
 
 OPTIONS
 -------
@@ -164,11 +205,23 @@
 --branch::
 	Branch of repository to add as submodule.
 
+-f::
+--force::
+	This option is only valid for add and update commands.
+	When running add, allow adding an otherwise ignored submodule path.
+	When running update, throw away local changes in submodules when
+	switching to a different commit.
+
 --cached::
 	This option is only valid for status and summary commands.  These
 	commands typically use the commit found in the submodule HEAD, but
 	with this option, the commit stored in the index is used instead.
 
+--files::
+	This option is only valid for the summary command. This command
+	compares the commit in the index with that in the submodule HEAD
+	when this option is used.
+
 -n::
 --summary-limit::
 	This option is only valid for the summary command.
@@ -196,18 +249,29 @@
 	This option is only valid for the update command.
 	Rebase the current branch onto the commit recorded in the
 	superproject. If this option is given, the submodule's HEAD will not
-	be detached. If a a merge failure prevents this process, you will have
+	be detached. If a merge failure prevents this process, you will have
 	to resolve these failures with linkgit:git-rebase[1].
 	If the key `submodule.$name.update` is set to `rebase`, this option is
 	implicit.
 
+--init::
+	This option is only valid for the update command.
+	Initialize all submodules for which "git submodule init" has not been
+	called so far before updating.
+
 --reference <repository>::
 	This option is only valid for add and update commands.  These
 	commands sometimes need to clone a remote repository. In this case,
 	this option will be passed to the linkgit:git-clone[1] command.
 +
 *NOTE*: Do *not* use this option unless you have read the note
-for linkgit:git-clone[1]'s --reference and --shared options carefully.
+for linkgit:git-clone[1]'s `--reference` and `--shared` options carefully.
+
+--recursive::
+	This option is only valid for foreach, update and status commands.
+	Traverse submodules recursively. The operation is performed not
+	only in the submodules of the current repo, but also
+	in any nested submodules inside those submodules (and so on).
 
 <path>...::
 	Paths to submodule(s). When specified this will restrict the command
@@ -222,11 +286,6 @@
 to each submodule url is "submodule.$name.url".  See linkgit:gitmodules[5]
 for details.
 
-
-AUTHOR
-------
-Written by Lars Hjemli <hjemli@gmail.com>
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt
index 22a0389..cfe8d2b 100644
--- a/Documentation/git-svn.txt
+++ b/Documentation/git-svn.txt
@@ -7,6 +7,7 @@
 
 SYNOPSIS
 --------
+[verse]
 'git svn' <command> [options] [arguments]
 
 DESCRIPTION
@@ -56,13 +57,17 @@
 	as well, they take precedence.
 --no-metadata;;
 	Set the 'noMetadata' option in the [svn-remote] config.
+	This option is not recommended, please read the 'svn.noMetadata'
+	section of this manpage before using this option.
 --use-svm-props;;
 	Set the 'useSvmProps' option in the [svn-remote] config.
 --use-svnsync-props;;
 	Set the 'useSvnsyncProps' option in the [svn-remote] config.
 --rewrite-root=<URL>;;
 	Set the 'rewriteRoot' option in the [svn-remote] config.
---username=<USER>;;
+--rewrite-uuid=<UUID>;;
+	Set the 'rewriteUUID' option in the [svn-remote] config.
+--username=<user>;;
 	For transports that SVN handles authentication for (http,
 	https, and plain svn), specify the username.  For other
 	transports (eg svn+ssh://), you must include the username in
@@ -102,9 +107,6 @@
 	Store Git commit times in the local timezone instead of UTC.  This
 	makes 'git log' (even without --date=local) show the same times
 	that `svn log` would in the local timezone.
-
---parent;;
-	Fetch only from the SVN parent of the current HEAD.
 +
 This doesn't interfere with interoperating with the Subversion
 repository you cloned from, but if you wish for your local Git
@@ -112,6 +114,9 @@
 repository, either don't use this option or you should both use it in
 the same local timezone.
 
+--parent;;
+	Fetch only from the SVN parent of the current HEAD.
+
 --ignore-paths=<regex>;;
 	This allows one to specify a Perl regular expression that will
 	cause skipping of all matching paths from checkout from SVN.
@@ -141,17 +146,6 @@
 ------------------------------------------------------------------------
 --
 
---use-log-author;;
-	When retrieving svn commits into git (as part of fetch, rebase, or
-	dcommit operations), look for the first From: or Signed-off-by: line
-	in the log message and use that as the author string.
---add-author-from;;
-	When committing to svn from git (as part of commit or dcommit
-	operations), if the existing log message doesn't already have a
-	From: or Signed-off-by: line, append a From: line based on the
-	git commit's author string.  If you use this, then --use-log-author
-	will retrieve a valid author string for all commits.
-
 'clone'::
 	Runs 'init' and 'fetch'.  It will automatically create a
 	directory based on the basename of the URL passed to it;
@@ -163,6 +157,17 @@
 	affecting the working tree; and the 'rebase' command will be
 	able to update the working tree with the latest changes.
 
+--preserve-empty-dirs;;
+	Create a placeholder file in the local Git repository for each
+	empty directory fetched from Subversion.  This includes directories
+	that become empty by removing all entries in the Subversion
+	repository (but not the directory itself).  The placeholder files
+	are also tracked and removed when no longer necessary.
+
+--placeholder-filename=<filename>;;
+	Set the name of placeholder files created by --preserve-empty-dirs.
+	Default: ".gitignore"
+
 'rebase'::
 	This fetches revisions from the SVN parent of the current HEAD
 	and rebases the current (uncommitted to SVN) work against it.
@@ -184,18 +189,16 @@
 	last fetched commit from the upstream SVN.
 
 'dcommit'::
-	Commit each diff from a specified head directly to the SVN
+	Commit each diff from the current branch directly to the SVN
 	repository, and then rebase or reset (depending on whether or
 	not there is a diff between SVN and head).  This will create
 	a revision in SVN for each commit in git.
-	It is recommended that you run 'git svn' fetch and rebase (not
-	pull or merge) your commits against the latest changes in the
-	SVN repository.
-	An optional revision or branch argument may be specified, and
-	causes 'git svn' to do all work on that revision/branch
-	instead of HEAD.
-	This is advantageous over 'set-tree' (below) because it produces
-	cleaner, more linear history.
++
+When an optional git branch name (or a git commit object name)
+is specified as an argument, the subcommand works on the specified
+branch, not on the current branch.
++
+Use of 'dcommit' is preferred to 'set-tree' (below).
 +
 --no-rebase;;
 	After committing, do not rebase or reset.
@@ -213,6 +216,30 @@
 Using this option for any other purpose (don't ask) is very strongly
 discouraged.
 
+--mergeinfo=<mergeinfo>;;
+	Add the given merge information during the dcommit
+	(e.g. `--mergeinfo="/branches/foo:1-10"`). All svn server versions can
+	store this information (as a property), and svn clients starting from
+	version 1.5 can make use of it. To specify merge information from multiple
+	branches, use a single space character between the branches
+	(`--mergeinfo="/branches/foo:1-10 /branches/bar:3,5-6,8"`)
++
+[verse]
+config key: svn.pushmergeinfo
++
+This option will cause git-svn to attempt to automatically populate the
+svn:mergeinfo property in the SVN repository when possible. Currently, this can
+only be done when dcommitting non-fast-forward merges where all parents but the
+first have already been pushed into SVN.
+
+--interactive;;
+	Ask the user to confirm that a patch set should actually be sent to SVN.
+	For each patch, one may answer "yes" (accept this patch), "no" (discard this
+	patch), "all" (accept all patches), or "quit".
+	+
+	'git svn dcommit' returns immediately if answer if "no" or "quit", without
+	commiting anything to SVN.
+
 'branch'::
 	Create a branch in the SVN repository.
 
@@ -239,6 +266,19 @@
 where <name> is the name of the SVN repository as specified by the -R option to
 'init' (or "svn" by default).
 
+--username;;
+	Specify the SVN username to perform the commit as.  This option overrides
+	the 'username' configuration property.
+
+--commit-url;;
+	Use the specified URL to connect to the destination Subversion
+	repository.  This is useful in cases where the source SVN
+	repository is read-only.  This option overrides configuration
+	property 'commiturl'.
++
+	git config --get-all svn-remote.<name>.commiturl
++
+
 'tag'::
 	Create a tag in the SVN repository. This is a shorthand for
 	'branch -t'.
@@ -284,7 +324,7 @@
        Show what revision and author last modified each line of a file. The
        output of this mode is format-compatible with the output of
        `svn blame' by default. Like the SVN blame command,
-       local uncommitted changes in the working copy are ignored;
+       local uncommitted changes in the working tree are ignored;
        the version of the file in the HEAD revision is annotated. Unknown
        arguments are passed directly to 'git blame'.
 +
@@ -320,6 +360,15 @@
 	directories.  The output is suitable for appending to
 	the $GIT_DIR/info/exclude file.
 
+'mkdirs'::
+	Attempts to recreate empty directories that core git cannot track
+	based on information in $GIT_DIR/svn/<refname>/unhandled.log files.
+	Empty directories are automatically recreated when using
+	"git svn clone" and "git svn rebase", so "mkdirs" is intended
+	for use after commands like "git checkout" or "git reset".
+	(See the svn-remote.<name>.automkdirs config file option for
+	more information.)
+
 'commit-diff'::
 	Commits the diff of two tree-ish arguments from the
 	command-line.  This command does not rely on being inside an `git svn
@@ -414,13 +463,13 @@
 OPTIONS
 -------
 
---shared[={false|true|umask|group|all|world|everybody}]::
+--shared[=(false|true|umask|group|all|world|everybody)]::
 --template=<template_directory>::
 	Only used with the 'init' command.
 	These are passed directly to 'git init'.
 
--r <ARG>::
---revision <ARG>::
+-r <arg>::
+--revision <arg>::
 	   Used with the 'fetch' command.
 +
 This allows revision ranges for partial/cauterized history
@@ -521,6 +570,8 @@
 --merge::
 -s<strategy>::
 --strategy=<strategy>::
+-p::
+--preserve-merges::
 	These are only used with the 'dcommit' and 'rebase' commands.
 +
 Passed directly to 'git rebase' when using 'dcommit' if a
@@ -541,6 +592,17 @@
 For 'branch' and 'tag', display the urls that will be used for copying when
 creating the branch or tag.
 
+--use-log-author::
+	When retrieving svn commits into git (as part of 'fetch', 'rebase', or
+	'dcommit' operations), look for the first `From:` or `Signed-off-by:` line
+	in the log message and use that as the author string.
+--add-author-from::
+	When committing to svn from git (as part of 'commit-diff', 'set-tree' or 'dcommit'
+	operations), if the existing log message doesn't already have a
+	`From:` or `Signed-off-by:` line, append a `From:` line based on the
+	git commit's author string.  If you use this, then `--use-log-author`
+	will retrieve a valid author string for all commits.
+
 
 ADVANCED OPTIONS
 ----------------
@@ -575,13 +637,22 @@
 svn-remote.<name>.noMetadata::
 	This gets rid of the 'git-svn-id:' lines at the end of every commit.
 +
-If you lose your .git/svn/git-svn/.rev_db file, 'git svn' will not
-be able to rebuild it and you won't be able to fetch again,
-either.  This is fine for one-shot imports.
+This option can only be used for one-shot imports as 'git svn'
+will not be able to fetch again without metadata. Additionally,
+if you lose your .git/svn/**/.rev_map.* files, 'git svn' will not
+be able to rebuild them.
 +
 The 'git svn log' command will not work on repositories using
 this, either.  Using this conflicts with the 'useSvmProps'
 option for (hopefully) obvious reasons.
++
+This option is NOT recommended as it makes it difficult to track down
+old references to SVN revision numbers in existing documentation, bug
+reports and archives.  If you plan to eventually migrate from SVN to git
+and are certain about dropping SVN history, consider
+linkgit:git-filter-branch[1] instead.  filter-branch also allows
+reformatting of metadata for ease-of-reading and rewriting authorship
+info for non-"svn.authorsFile" users.
 
 svn.useSvmProps::
 svn-remote.<name>.useSvmProps::
@@ -609,6 +680,22 @@
 	the repository with a public http:// or svn:// URL in the
 	metadata so users of it will see the public URL.
 
+svn-remote.<name>.rewriteUUID::
+	Similar to the useSvmProps option; this is for users who need
+	to remap the UUID manually. This may be useful in situations
+	where the original UUID is not available via either useSvmProps
+	or useSvnsyncProps.
+
+svn-remote.<name>.pushurl::
+
+	Similar to git's 'remote.<name>.pushurl', this key is designed
+	to be used in cases where 'url' points to an SVN repository
+	via a read-only transport, to provide an alternate read/write
+	transport. It is assumed that both keys point to the same
+	repository. Unlike 'commiturl', 'pushurl' is a base path. If
+	either 'commiturl' or 'pushurl' could be used, 'commiturl'
+	takes precedence.
+
 svn.brokenSymlinkWorkaround::
 	This disables potentially expensive checks to workaround
 	broken symlinks checked into SVN by broken clients.  Set this
@@ -618,13 +705,28 @@
 	revision fetched.  If unset, 'git svn' assumes this option to
 	be "true".
 
-Since the noMetadata, rewriteRoot, useSvnsyncProps and useSvmProps
+svn.pathnameencoding::
+	This instructs git svn to recode pathnames to a given encoding.
+	It can be used by windows users and by those who work in non-utf8
+	locales to avoid corrupted file names with non-ASCII characters.
+	Valid encodings are the ones supported by Perl's Encode module.
+
+svn-remote.<name>.automkdirs::
+	Normally, the "git svn clone" and "git svn rebase" commands
+	attempt to recreate empty directories that are in the
+	Subversion repository.  If this option is set to "false", then
+	empty directories will only be created if the "git svn mkdirs"
+	command is run explicitly.  If unset, 'git svn' assumes this
+	option to be "true".
+
+Since the noMetadata, rewriteRoot, rewriteUUID, useSvnsyncProps and useSvmProps
 options all affect the metadata generated and used by 'git svn'; they
 *must* be set in the configuration file before any history is imported
 and these settings should never be changed once they are set.
 
-Additionally, only one of these four options can be used per-svn-remote
-section because they affect the 'git-svn-id:' metadata line.
+Additionally, only one of these options can be used per svn-remote
+section because they affect the 'git-svn-id:' metadata line, except
+for rewriteRoot and rewriteUUID which can be used together.
 
 
 BASIC EXAMPLES
@@ -683,8 +785,11 @@
 	cd project
 	git init
 	git remote add origin server:/pub/project
-	git config --add remote.origin.fetch '+refs/remotes/*:refs/remotes/*'
+	git config --replace-all remote.origin.fetch '+refs/remotes/*:refs/remotes/*'
 	git fetch
+# Prevent fetch/pull from remote git server in the future,
+# we only want to use git svn for future updates
+	git config --remove-section remote.origin
 # Create a local branch from one of the branches just fetched
 	git checkout -b master FETCH_HEAD
 # Initialize 'git svn' locally (be sure to use the same URL and -T/-b/-t options as were used on server)
@@ -695,23 +800,23 @@
 
 REBASE VS. PULL/MERGE
 ---------------------
+Prefer to use 'git svn rebase' or 'git rebase', rather than
+'git pull' or 'git merge' to synchronize unintegrated commits with a 'git svn'
+branch. Doing so will keep the history of unintegrated commits linear with
+respect to the upstream SVN repository and allow the use of the preferred
+'git svn dcommit' subcommand to push unintegrated commits back into SVN.
 
-Originally, 'git svn' recommended that the 'remotes/git-svn' branch be
-pulled or merged from.  This is because the author favored
+Originally, 'git svn' recommended that developers pulled or merged from
+the 'git svn' branch.  This was because the author favored
 `git svn set-tree B` to commit a single head rather than the
-`git svn set-tree A..B` notation to commit multiple commits.
+`git svn set-tree A..B` notation to commit multiple commits. Use of
+'git pull' or 'git merge' with `git svn set-tree A..B` will cause non-linear
+history to be flattened when committing into SVN and this can lead to merge
+commits unexpectedly reversing previous commits in SVN.
 
-If you use `git svn set-tree A..B` to commit several diffs and you do
-not have the latest remotes/git-svn merged into my-branch, you should
-use `git svn rebase` to update your work branch instead of `git pull` or
-`git merge`.  `pull`/`merge` can cause non-linear history to be flattened
-when committing into SVN, which can lead to merge commits reversing
-previous commits in SVN.
-
-DESIGN PHILOSOPHY
------------------
-Merge tracking in Subversion is lacking and doing branched development
-with Subversion can be cumbersome as a result.  While 'git svn' can track
+MERGE TRACKING
+--------------
+While 'git svn' can track
 copy history (including branches and tags) for repositories adopting a
 standard layout, it cannot yet represent merge history that happened
 inside git back upstream to SVN users.  Therefore it is advised that
@@ -721,20 +826,29 @@
 CAVEATS
 -------
 
-For the sake of simplicity and interoperating with a less-capable system
-(SVN), it is recommended that all 'git svn' users clone, fetch and dcommit
+For the sake of simplicity and interoperating with Subversion,
+it is recommended that all 'git svn' users clone, fetch and dcommit
 directly from the SVN server, and avoid all 'git clone'/'pull'/'merge'/'push'
 operations between git repositories and branches.  The recommended
 method of exchanging code between git branches and users is
 'git format-patch' and 'git am', or just 'dcommit'ing to the SVN repository.
 
 Running 'git merge' or 'git pull' is NOT recommended on a branch you
-plan to 'dcommit' from.  Subversion does not represent merges in any
-reasonable or useful fashion; so users using Subversion cannot see any
+plan to 'dcommit' from because Subversion users cannot see any
 merges you've made.  Furthermore, if you merge or pull from a git branch
 that is a mirror of an SVN branch, 'dcommit' may commit to the wrong
 branch.
 
+If you do merge, note the following rule: 'git svn dcommit' will
+attempt to commit on top of the SVN commit named in
+------------------------------------------------------------------------
+git log --grep=^git-svn-id: --first-parent -1
+------------------------------------------------------------------------
+You 'must' therefore ensure that the most recent commit of the branch
+you want to dcommit to is the 'first' parent of the merge.  Chaos will
+ensue otherwise, especially if the first parent is an older commit on
+the same SVN branch.
+
 'git clone' does not clone branches under the refs/remotes/ hierarchy or
 any 'git svn' metadata, or config.  So repositories created and managed with
 using 'git svn' should use 'rsync' for cloning, if cloning is to be done
@@ -770,7 +884,7 @@
 tracked when committing to SVN.  I do not plan on adding support for
 this as it's quite difficult and time-consuming to get working for all
 the possible corner cases (git doesn't do it, either).  Committing
-renamed and copied files are fully supported if they're similar enough
+renamed and copied files is fully supported if they're similar enough
 for git to detect them.
 
 CONFIGURATION
@@ -799,14 +913,26 @@
 type of configuration is not automatically created by 'init' and
 should be manually entered with a text-editor or using 'git config'.
 
+It is also possible to fetch a subset of branches or tags by using a
+comma-separated list of names within braces. For example:
+
+------------------------------------------------------------------------
+[svn-remote "huge-project"]
+	url = http://server.org/svn
+	fetch = trunk/src:refs/remotes/trunk
+	branches = branches/{red,green}/src:refs/remotes/branches/*
+	tags = tags/{1.0,2.0}/src:refs/remotes/tags/*
+------------------------------------------------------------------------
+
+Note that git-svn keeps track of the highest revision in which a branch
+or tag has appeared. If the subset of branches or tags is changed after
+fetching, then .git/svn/.metadata must be manually edited to remove (or
+reset) branches-maxRev and/or tags-maxRev as appropriate.
+
 SEE ALSO
 --------
 linkgit:git-rebase[1]
 
-Author
-------
-Written by Eric Wong <normalperson@yhbt.net>.
-
-Documentation
--------------
-Written by Eric Wong <normalperson@yhbt.net>.
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-symbolic-ref.txt b/Documentation/git-symbolic-ref.txt
index 210fde0..981d3a8 100644
--- a/Documentation/git-symbolic-ref.txt
+++ b/Documentation/git-symbolic-ref.txt
@@ -7,16 +7,18 @@
 
 SYNOPSIS
 --------
-'git symbolic-ref' [-q] [-m <reason>] <name> [<ref>]
+[verse]
+'git symbolic-ref' [-m <reason>] <name> <ref>
+'git symbolic-ref' [-q] [--short] <name>
 
 DESCRIPTION
 -----------
 Given one argument, reads which branch head the given symbolic
 ref refers to and outputs its path, relative to the `.git/`
 directory.  Typically you would give `HEAD` as the <name>
-argument to see on which branch your working tree is on.
+argument to see which branch your working tree is on.
 
-Give two arguments, create or update a symbolic ref <name> to
+Given two arguments, creates or updates a symbolic ref <name> to
 point at the given branch <ref>.
 
 A symbolic ref is a regular file that stores a string that
@@ -32,6 +34,10 @@
 	symbolic ref but a detached HEAD; instead exit with
 	non-zero status silently.
 
+--short::
+	When showing the value of <name> as a symbolic ref, try to shorten the
+	value, e.g. from `refs/heads/master` to `master`.
+
 -m::
 	Update the reflog for <name> with <reason>.  This is valid only
 	when creating or updating a symbolic ref.
@@ -42,21 +48,14 @@
 `refs/heads/master`.  When we wanted to switch to another branch,
 we did `ln -sf refs/heads/newbranch .git/HEAD`, and when we wanted
 to find out which branch we are on, we did `readlink .git/HEAD`.
-This was fine, and internally that is what still happens by
-default, but on platforms that do not have working symlinks,
-or that do not have the `readlink(1)` command, this was a bit
-cumbersome.  On some platforms, `ln -sf` does not even work as
-advertised (horrors).  Therefore symbolic links are now deprecated
-and symbolic refs are used by default.
+But symbolic links are not entirely portable, so they are now
+deprecated and symbolic refs (as described above) are used by
+default.
 
-'git-symbolic-ref' will exit with status 0 if the contents of the
+'git symbolic-ref' will exit with status 0 if the contents of the
 symbolic ref were printed correctly, with status 1 if the requested
 name is not a symbolic ref, or 128 if another error occurs.
 
-Author
-------
-Written by Junio C Hamano <gitster@pobox.com>
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index fa73321..e36a7c3 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -10,52 +10,64 @@
 --------
 [verse]
 'git tag' [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]
-	<name> [<commit> | <object>]
-'git tag' -d <name>...
-'git tag' [-n[<num>]] -l [--contains <commit>] [<pattern>]
-'git tag' -v <name>...
+	<tagname> [<commit> | <object>]
+'git tag' -d <tagname>...
+'git tag' [-n[<num>]] -l [--contains <commit>] [--points-at <object>]
+	[--column[=<options>] | --no-column] [<pattern>...]
+	[<pattern>...]
+'git tag' -v <tagname>...
 
 DESCRIPTION
 -----------
-Adds a 'tag' reference in `.git/refs/tags/`
 
-Unless `-f` is given, the tag must not yet exist in
+Add a tag reference in `.git/refs/tags/`, unless `-d/-l/-v` is given
+to delete, list or verify tags.
+
+Unless `-f` is given, the tag to be created must not yet exist in the
 `.git/refs/tags/` directory.
 
 If one of `-a`, `-s`, or `-u <key-id>` is passed, the command
-creates a 'tag' object, and requires the tag message.  Unless
+creates a 'tag' object, and requires a tag message.  Unless
 `-m <msg>` or `-F <file>` is given, an editor is started for the user to type
 in the tag message.
 
 If `-m <msg>` or `-F <file>` is given and `-a`, `-s`, and `-u <key-id>`
 are absent, `-a` is implied.
 
-Otherwise just the SHA1 object name of the commit object is
-written (i.e. a lightweight tag).
+Otherwise just a tag reference for the SHA1 object name of the commit object is
+created (i.e. a lightweight tag).
 
 A GnuPG signed tag object will be created when `-s` or `-u
 <key-id>` is used.  When `-u <key-id>` is not used, the
 committer identity for the current user is used to find the
-GnuPG key for signing.
+GnuPG key for signing. 	The configuration variable `gpg.program`
+is used to specify custom GnuPG binary.
+
 
 OPTIONS
 -------
 -a::
+--annotate::
 	Make an unsigned, annotated tag object
 
 -s::
-	Make a GPG-signed tag, using the default e-mail address's key
+--sign::
+	Make a GPG-signed tag, using the default e-mail address's key.
 
 -u <key-id>::
-	Make a GPG-signed tag, using the given key
+--local-user=<key-id>::
+	Make a GPG-signed tag, using the given key.
 
 -f::
+--force::
 	Replace an existing tag with the given name (instead of failing)
 
 -d::
+--delete::
 	Delete existing tags with the given names.
 
 -v::
+--verify::
 	Verify the gpg signature of the given tag names.
 
 -n<num>::
@@ -66,13 +78,29 @@
 	If the tag is not annotated, the commit message is displayed instead.
 
 -l <pattern>::
-	List tags with names that match the given pattern (or all if no pattern is given).
-	Typing "git tag" without arguments, also lists all tags.
+--list <pattern>::
+	List tags with names that match the given pattern (or all if no
+	pattern is given).  Running "git tag" without arguments also
+	lists all tags. The pattern is a shell wildcard (i.e., matched
+	using fnmatch(3)).  Multiple patterns may be given; if any of
+	them matches, the tag is shown.
+
+--column[=<options>]::
+--no-column::
+	Display tag listing in columns. See configuration variable
+	column.tag for option syntax.`--column` and `--no-column`
+	without options are equivalent to 'always' and 'never' respectively.
++
+This option is only applicable when listing tags without annotation lines.
 
 --contains <commit>::
 	Only list tags which contain the specified commit.
 
+--points-at <object>::
+	Only list tags of the given object.
+
 -m <msg>::
+--message=<msg>::
 	Use the given tag message (instead of prompting).
 	If multiple `-m` options are given, their values are
 	concatenated as separate paragraphs.
@@ -80,14 +108,28 @@
 	is given.
 
 -F <file>::
+--file=<file>::
 	Take the tag message from the given file.  Use '-' to
 	read the message from the standard input.
 	Implies `-a` if none of `-a`, `-s`, or `-u <key-id>`
 	is given.
 
+--cleanup=<mode>::
+	This option sets how the tag message is cleaned up.
+	The  '<mode>' can be one of 'verbatim', 'whitespace' and 'strip'.  The
+	'strip' mode is default. The 'verbatim' mode does not change message at
+	all, 'whitespace' removes just leading/trailing whitespace lines and
+	'strip' removes both whitespace and commentary.
+
+<tagname>::
+	The name of the tag to create, delete, or describe.
+	The new tag name must pass all checks defined by
+	linkgit:git-check-ref-format[1].  Some of these checks
+	may restrict the characters allowed in a tag name.
+
 CONFIGURATION
 -------------
-By default, 'git-tag' in sign-with-default mode (-s) will use your
+By default, 'git tag' in sign-with-default mode (-s) will use your
 committer identity (of the form "Your Name <your@email.address>") to
 find a key.  If you want to use a different default key, you can specify
 it in the repository configuration as follows:
@@ -123,12 +165,12 @@
 
 . The insane thing.
 You really want to call the new version "X" too, 'even though'
-others have already seen the old one. So just use 'git-tag -f'
+others have already seen the old one. So just use 'git tag -f'
 again, as if you hadn't already published the old one.
 
 However, Git does *not* (and it should not) change tags behind
 users back. So if somebody already got the old tag, doing a
-'git-pull' on your tree shouldn't just make them overwrite the old
+'git pull' on your tree shouldn't just make them overwrite the old
 one.
 
 If somebody got a release tag from you, you cannot just change
@@ -156,20 +198,19 @@
 
 which should return 0123456789abcdef.. if you have the new version.
 
-Sorry for inconvenience.
+Sorry for the inconvenience.
 ------------
 
 Does this seem a bit complicated?  It *should* be. There is no
-way that it would be correct to just "fix" it behind peoples
-backs. People need to know that their tags might have been
-changed.
+way that it would be correct to just "fix" it automatically.
+People need to know that their tags might have been changed.
 
 
 On Automatic following
 ~~~~~~~~~~~~~~~~~~~~~~
 
 If you are following somebody else's tree, you are most likely
-using tracking branches (`refs/heads/origin` in traditional
+using remote-tracking branches (`refs/heads/origin` in traditional
 layout, or `refs/remotes/origin/master` in the separate-remote
 layout).  You usually want the tags from the other end.
 
@@ -180,9 +221,10 @@
 from each other do not necessarily want to automatically get
 private anchor point tags from the other person.
 
-You would notice "please pull" messages on the mailing list says
-repo URL and branch name alone.  This is designed to be easily
-cut&pasted to a 'git-fetch' command line:
+Often, "please pull" messages on the mailing list just provide
+two pieces of information: a repo URL and a branch name; this
+is designed to be easily cut&pasted at the end of a 'git fetch'
+command line:
 
 ------------
 Linus, please pull from
@@ -198,14 +240,14 @@
 $ git pull git://git..../proj.git master
 ------------
 
-In such a case, you do not want to automatically follow other's
-tags.
+In such a case, you do not want to automatically follow the other
+person's tags.
 
-One important aspect of git is it is distributed, and being
-distributed largely means there is no inherent "upstream" or
+One important aspect of git is its distributed nature, which
+largely means there is no inherent "upstream" or
 "downstream" in the system.  On the face of it, the above
 example might seem to indicate that the tag namespace is owned
-by upper echelon of people and tags only flow downwards, but
+by the upper echelon of people and that tags only flow downwards, but
 that is not the case.  It only shows that the usage pattern
 determines who are interested in whose tags.
 
@@ -223,8 +265,8 @@
 
 It may well be that among networking people, they may want to
 exchange the tags internal to their group, but in that workflow
-they are most likely tracking with each other's progress by
-having tracking branches.  Again, the heuristic to automatically
+they are most likely tracking each other's progress by
+having remote-tracking branches.  Again, the heuristic to automatically
 follow such tags is a good thing.
 
 
@@ -233,30 +275,25 @@
 
 If you have imported some changes from another VCS and would like
 to add tags for major releases of your work, it is useful to be able
-to specify the date to embed inside of the tag object.  The data in
+to specify the date to embed inside of the tag object; such data in
 the tag object affects, for example, the ordering of tags in the
 gitweb interface.
 
 To set the date used in future tag objects, set the environment
-variable GIT_COMMITTER_DATE to one or more of the date and time.  The
-date and time can be specified in a number of ways; the most common
-is "YYYY-MM-DD HH:MM".
+variable GIT_COMMITTER_DATE (see the later discussion of possible
+values; the most common form is "YYYY-MM-DD HH:MM").
 
-An example follows.
+For example:
 
 ------------
 $ GIT_COMMITTER_DATE="2006-10-02 10:31" git tag -s v1.0.1
 ------------
 
+include::date-formats.txt[]
 
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>,
-Junio C Hamano <gitster@pobox.com> and Chris Wright <chrisw@osdl.org>.
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+SEE ALSO
+--------
+linkgit:git-check-ref-format[1].
 
 GIT
 ---
diff --git a/Documentation/git-tar-tree.txt b/Documentation/git-tar-tree.txt
index a5d9558..f7362dc 100644
--- a/Documentation/git-tar-tree.txt
+++ b/Documentation/git-tar-tree.txt
@@ -8,23 +8,24 @@
 
 SYNOPSIS
 --------
+[verse]
 'git tar-tree' [--remote=<repo>] <tree-ish> [ <base> ]
 
 DESCRIPTION
 -----------
-THIS COMMAND IS DEPRECATED.  Use 'git-archive' with `--format=tar`
+THIS COMMAND IS DEPRECATED.  Use 'git archive' with `--format=tar`
 option instead (and move the <base> argument to `--prefix=base/`).
 
 Creates a tar archive containing the tree structure for the named tree.
 When <base> is specified it is added as a leading path to the files in the
 generated tar archive.
 
-'git-tar-tree' behaves differently when given a tree ID versus when given
+'git tar-tree' behaves differently when given a tree ID versus when given
 a commit ID or tag ID.  In the first case the current time is used as
 modification time of each file in the archive.  In the latter case the
 commit time as recorded in the referenced commit object is used instead.
 Additionally the commit ID is stored in a global extended pax header.
-It can be extracted using 'git-get-tar-commit-id'.
+It can be extracted using 'git get-tar-commit-id'.
 
 OPTIONS
 -------
@@ -52,38 +53,30 @@
 
 EXAMPLES
 --------
-git tar-tree HEAD junk | (cd /var/tmp/ && tar xf -)::
+`git tar-tree HEAD junk | (cd /var/tmp/ && tar xf -)`::
 
 	Create a tar archive that contains the contents of the
 	latest commit on the current branch, and extracts it in
 	`/var/tmp/junk` directory.
 
-git tar-tree v1.4.0 git-1.4.0 | gzip >git-1.4.0.tar.gz::
+`git tar-tree v1.4.0 git-1.4.0 | gzip >git-1.4.0.tar.gz`::
 
 	Create a tarball for v1.4.0 release.
 
-git tar-tree v1.4.0{caret}\{tree\} git-1.4.0 | gzip >git-1.4.0.tar.gz::
+`git tar-tree v1.4.0^{tree} git-1.4.0 | gzip >git-1.4.0.tar.gz`::
 
 	Create a tarball for v1.4.0 release, but without a
 	global extended pax header.
 
-git tar-tree --remote=example.com:git.git v1.4.0 >git-1.4.0.tar::
+`git tar-tree --remote=example.com:git.git v1.4.0 >git-1.4.0.tar`::
 
 	Get a tarball v1.4.0 from example.com.
 
-git tar-tree HEAD:Documentation/ git-docs > git-1.4.0-docs.tar::
+`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.
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-unpack-file.txt b/Documentation/git-unpack-file.txt
index 995db9f..e9f148a 100644
--- a/Documentation/git-unpack-file.txt
+++ b/Documentation/git-unpack-file.txt
@@ -9,6 +9,7 @@
 
 SYNOPSIS
 --------
+[verse]
 'git unpack-file' <blob>
 
 DESCRIPTION
@@ -22,14 +23,6 @@
 <blob>::
 	Must be a blob id
 
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-unpack-objects.txt b/Documentation/git-unpack-objects.txt
index 36d1038..ff23494 100644
--- a/Documentation/git-unpack-objects.txt
+++ b/Documentation/git-unpack-objects.txt
@@ -8,6 +8,7 @@
 
 SYNOPSIS
 --------
+[verse]
 'git unpack-objects' [-n] [-q] [-r] [--strict] <pack-file
 
 
@@ -43,15 +44,6 @@
 --strict::
 	Don't write objects with broken content or links.
 
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
--------------
-Documentation by Junio C Hamano
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-update-index.txt b/Documentation/git-update-index.txt
index 25e0bbe..9d0b151 100644
--- a/Documentation/git-update-index.txt
+++ b/Documentation/git-update-index.txt
@@ -12,15 +12,16 @@
 'git update-index'
 	     [--add] [--remove | --force-remove] [--replace]
 	     [--refresh] [-q] [--unmerged] [--ignore-missing]
-	     [--cacheinfo <mode> <object> <file>]\*
+	     [(--cacheinfo <mode> <object> <file>)...]
 	     [--chmod=(+|-)x]
 	     [--assume-unchanged | --no-assume-unchanged]
+	     [--skip-worktree | --no-skip-worktree]
 	     [--ignore-submodules]
 	     [--really-refresh] [--unresolve] [--again | -g]
 	     [--info-only] [--index-info]
-	     [-z] [--stdin]
+	     [-z] [--stdin] [--index-version <n>]
 	     [--verbose]
-	     [--] [<file>]\*
+	     [--] [<file>...]
 
 DESCRIPTION
 -----------
@@ -31,7 +32,7 @@
 See also linkgit:git-add[1] for a more user-friendly way to do some of
 the most common operations on the index.
 
-The way 'git-update-index' handles files it is told about can be modified
+The way 'git update-index' handles files it is told about can be modified
 using the various options:
 
 OPTIONS
@@ -53,7 +54,7 @@
 -q::
         Quiet.  If --refresh finds that the index needs an update, the
         default behavior is to error out.  This option makes
-	'git-update-index' continue anyway.
+	'git update-index' continue anyway.
 
 --ignore-submodules::
 	Do not try to update submodules.  This option is only respected
@@ -61,7 +62,7 @@
 
 --unmerged::
         If --refresh finds unmerged changes in the index, the default
-	behavior is to error out.  This option makes 'git-update-index'
+	behavior is to error out.  This option makes 'git update-index'
         continue anyway.
 
 --ignore-missing::
@@ -92,16 +93,25 @@
 This option can be also used as a coarse file-level mechanism
 to ignore uncommitted changes in tracked files (akin to what
 `.gitignore` does for untracked files).
-You should remember that an explicit 'git add' operation will
-still cause the file to be refreshed from the working tree.
 Git will fail (gracefully) in case it needs to modify this file
 in the index e.g. when merging in a commit;
 thus, in case the assumed-untracked file is changed upstream,
 you will need to handle the situation manually.
 
+--really-refresh::
+	Like '--refresh', but checks stat information unconditionally,
+	without regard to the "assume unchanged" setting.
+
+--skip-worktree::
+--no-skip-worktree::
+	When one of these flags is specified, the object name recorded
+	for the paths are not updated. Instead, these options
+	set and unset the "skip-worktree" bit for the paths. See
+	section "Skip-worktree bit" below for more information.
+
 -g::
 --again::
-	Runs 'git-update-index' itself on the paths whose index
+	Runs 'git update-index' itself on the paths whose index
 	entries are different from those from the `HEAD` commit.
 
 --unresolve::
@@ -119,7 +129,7 @@
 
 --replace::
 	By default, when a file `path` exists in the index,
-	'git-update-index' refuses an attempt to add `path/file`.
+	'git update-index' refuses an attempt to add `path/file`.
 	Similarly if a file `path/file` exists, a file `path`
 	cannot be added.  With --replace flag, existing entries
 	that conflict with the entry being added are
@@ -133,9 +143,13 @@
 --verbose::
         Report what is being added and removed from index.
 
+--index-version <n>::
+	Write the resulting index out in the named on-disk format version.
+	The current default version is 2.
+
 -z::
-	Only meaningful with `--stdin`; paths are separated with
-	NUL character instead of LF.
+	Only meaningful with `--stdin` or `--index-info`; paths are
+	separated with NUL character instead of LF.
 
 \--::
 	Do not interpret any more arguments as options.
@@ -155,7 +169,7 @@
 can refresh the index for a file that hasn't been changed but where
 the stat entry is out of date.
 
-For example, you'd want to do this after doing a 'git-read-tree', to link
+For example, you'd want to do this after doing a 'git read-tree', to link
 up the stat index details with the proper files.
 
 Using --cacheinfo or --info-only
@@ -196,13 +210,13 @@
 
     . mode SP type SP sha1          TAB path
 +
-The second format is to stuff 'git-ls-tree' output
+The second format is to stuff 'git ls-tree' output
 into the index file.
 
     . mode         SP sha1 SP stage TAB path
 +
 This format is to put higher order stages into the
-index file and matches 'git-ls-files --stage' output.
+index file and matches 'git ls-files --stage' output.
 
 To place a higher stage entry to the index, the path should
 first be removed by feeding a mode=0 entry for the path, and
@@ -254,13 +268,15 @@
 "assume unchanged" bit, either before or after you modify them.
 
 In order to set "assume unchanged" bit, use `--assume-unchanged`
-option.  To unset, use `--no-assume-unchanged`.
+option.  To unset, use `--no-assume-unchanged`. To see which files
+have the "assume unchanged" bit set, use `git ls-files -v`
+(see linkgit:git-ls-files[1]).
 
 The command looks at `core.ignorestat` configuration variable.  When
 this is true, paths updated with `git update-index paths...` and
 paths updated with other git commands that update both index and
-working tree (e.g. 'git-apply --index', 'git-checkout-index -u',
-and 'git-read-tree -u') are automatically marked as "assume
+working tree (e.g. 'git apply --index', 'git checkout-index -u',
+and 'git read-tree -u') are automatically marked as "assume
 unchanged".  Note that "assume unchanged" bit is *not* set if
 `git update-index --refresh` finds the working tree file matches
 the index (use `git update-index --really-refresh` if you want
@@ -304,16 +320,37 @@
 <9> now it checks with lstat(2) and finds it has been changed.
 
 
+Skip-worktree bit
+-----------------
+
+Skip-worktree bit can be defined in one (long) sentence: When reading
+an entry, if it is marked as skip-worktree, then Git pretends its
+working directory version is up to date and read the index version
+instead.
+
+To elaborate, "reading" means checking for file existence, reading
+file attributes or file content. The working directory version may be
+present or absent. If present, its content may match against the index
+version or not. Writing is not affected by this bit, content safety
+is still first priority. Note that Git _can_ update working directory
+file, that is marked skip-worktree, if it is safe to do so (i.e.
+working directory version matches index version)
+
+Although this bit looks similar to assume-unchanged bit, its goal is
+different from assume-unchanged bit's. Skip-worktree also takes
+precedence over assume-unchanged bit when both are set.
+
+
 Configuration
 -------------
 
 The command honors `core.filemode` configuration variable.  If
-your repository is on an filesystem whose executable bits are
+your repository is on a filesystem whose executable bits are
 unreliable, this should be set to 'false' (see linkgit:git-config[1]).
 This causes the command to ignore differences in file modes recorded
 in the index and the file mode on the filesystem if they differ only on
 executable bit.   On such an unfortunate filesystem, you may
-need to use 'git-update-index --chmod='.
+need to use 'git update-index --chmod='.
 
 Quite similarly, if `core.symlinks` configuration variable is set
 to 'false' (see linkgit:git-config[1]), symbolic links are checked out
@@ -332,16 +369,8 @@
 SEE ALSO
 --------
 linkgit:git-config[1],
-linkgit:git-add[1]
-
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+linkgit:git-add[1],
+linkgit:git-ls-files[1]
 
 GIT
 ---
diff --git a/Documentation/git-update-ref.txt b/Documentation/git-update-ref.txt
index 9639f70..d377a35 100644
--- a/Documentation/git-update-ref.txt
+++ b/Documentation/git-update-ref.txt
@@ -7,6 +7,7 @@
 
 SYNOPSIS
 --------
+[verse]
 'git update-ref' [-m <reason>] (-d <ref> [<oldvalue>] | [--no-deref] <ref> <newvalue> [<oldvalue>])
 
 DESCRIPTION
@@ -60,8 +61,9 @@
 
 Logging Updates
 ---------------
-If config parameter "core.logAllRefUpdates" is true or the file
-"$GIT_DIR/logs/<ref>" exists then `git update-ref` will append
+If config parameter "core.logAllRefUpdates" is true and the ref is one under
+"refs/heads/", "refs/remotes/", "refs/notes/", or the symbolic ref HEAD; or
+the file "$GIT_DIR/logs/<ref>" exists then `git update-ref` will append
 a line to the log file "$GIT_DIR/logs/<ref>" (dereferencing all
 symbolic refs before creating the log name) describing the change
 in ref value.  Log lines are formatted as:
@@ -84,10 +86,6 @@
 unable to create a new log file, append to the existing log file
 or does not have committer information available.
 
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-update-server-info.txt b/Documentation/git-update-server-info.txt
index 035cc30..bd0e364 100644
--- a/Documentation/git-update-server-info.txt
+++ b/Documentation/git-update-server-info.txt
@@ -8,6 +8,7 @@
 
 SYNOPSIS
 --------
+[verse]
 'git update-server-info' [--force]
 
 DESCRIPTION
@@ -38,15 +39,6 @@
 
 * info/refs
 
-
-Author
-------
-Written by Junio C Hamano <gitster@pobox.com>
-
-Documentation
---------------
-Documentation by Junio C Hamano.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-upload-archive.txt b/Documentation/git-upload-archive.txt
index bbd7617..4d52d38 100644
--- a/Documentation/git-upload-archive.txt
+++ b/Documentation/git-upload-archive.txt
@@ -8,15 +8,16 @@
 
 SYNOPSIS
 --------
+[verse]
 'git upload-archive' <directory>
 
 DESCRIPTION
 -----------
-Invoked by 'git-archive --remote' and sends a generated archive to the
+Invoked by 'git archive --remote' and sends a generated archive to the
 other end over the git protocol.
 
 This command is usually not invoked directly by the end user.  The UI
-for the protocol is on the 'git-archive' side, and the program pair
+for the protocol is on the 'git archive' side, and the program pair
 is meant to be used to get an archive from a remote repository.
 
 OPTIONS
@@ -24,14 +25,6 @@
 <directory>::
 	The repository to get a tar archive from.
 
-Author
-------
-Written by Franck Bui-Huu.
-
-Documentation
---------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-upload-pack.txt b/Documentation/git-upload-pack.txt
index b8e49dc..71f1608 100644
--- a/Documentation/git-upload-pack.txt
+++ b/Documentation/git-upload-pack.txt
@@ -8,17 +8,18 @@
 
 SYNOPSIS
 --------
-'git upload-pack' [--strict] [--timeout=<n>] <directory>
+[verse]
+'git-upload-pack' [--strict] [--timeout=<n>] <directory>
 
 DESCRIPTION
 -----------
-Invoked by 'git-fetch-pack', learns what
+Invoked by 'git fetch-pack', learns what
 objects the other side is missing, and sends them after packing.
 
 This command is usually not invoked directly by the end user.
-The UI for the protocol is on the 'git-fetch-pack' side, and the
+The UI for the protocol is on the 'git fetch-pack' side, and the
 program pair is meant to be used to pull updates from a remote
-repository.  For push operations, see 'git-send-pack'.
+repository.  For push operations, see 'git send-pack'.
 
 
 OPTIONS
@@ -33,13 +34,9 @@
 <directory>::
 	The repository to sync from.
 
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by Junio C Hamano.
+SEE ALSO
+--------
+linkgit:gitnamespaces[7]
 
 GIT
 ---
diff --git a/Documentation/git-var.txt b/Documentation/git-var.txt
index e2f4c09..67edf58 100644
--- a/Documentation/git-var.txt
+++ b/Documentation/git-var.txt
@@ -8,7 +8,8 @@
 
 SYNOPSIS
 --------
-'git var' [ -l | <variable> ]
+[verse]
+'git var' ( -l | <variable> )
 
 DESCRIPTION
 -----------
@@ -20,7 +21,7 @@
 	Cause the logical variables to be listed. In addition, all the
 	variables of the git configuration file .git/config are listed
 	as well. (However, the configuration variables listing functionality
-	is deprecated in favor of 'git config -l'.)
+	is deprecated in favor of `git config -l`.)
 
 EXAMPLE
 --------
@@ -36,14 +37,27 @@
 GIT_COMMITTER_IDENT::
     The person who put a piece of code into git.
 
-Diagnostics
------------
-You don't exist. Go away!::
-    The passwd(5) gecos field couldn't be read
-Your parents must have hated you!::
-    The passwd(5) gecos field is longer than a giant static buffer.
-Your sysadmin must hate you!::
-    The passwd(5) name field is longer than a giant static buffer.
+GIT_EDITOR::
+    Text editor for use by git commands.  The value is meant to be
+    interpreted by the shell when it is used.  Examples: `~/bin/vi`,
+    `$SOME_ENVIRONMENT_VARIABLE`, `"C:\Program Files\Vim\gvim.exe"
+    --nofork`.  The order of preference is the `$GIT_EDITOR`
+    environment variable, then `core.editor` configuration, then
+    `$VISUAL`, then `$EDITOR`, and then the default chosen at compile
+    time, which is usually 'vi'.
+ifdef::git-default-editor[]
+    The build you are using chose '{git-default-editor}' as the default.
+endif::git-default-editor[]
+
+GIT_PAGER::
+    Text viewer for use by git commands (e.g., 'less').  The value
+    is meant to be interpreted by the shell.  The order of preference
+    is the `$GIT_PAGER` environment variable, then `core.pager`
+    configuration, then `$PAGER`, and then the default chosen at
+    compile time (usually 'less').
+ifdef::git-default-pager[]
+    The build you are using chose '{git-default-pager}' as the default.
+endif::git-default-pager[]
 
 SEE ALSO
 --------
@@ -51,14 +65,6 @@
 linkgit:git-tag[1]
 linkgit:git-config[1]
 
-Author
-------
-Written by Eric Biederman <ebiederm@xmission.com>
-
-Documentation
---------------
-Documentation by Eric Biederman and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-verify-pack.txt b/Documentation/git-verify-pack.txt
index d791a80..cd23076 100644
--- a/Documentation/git-verify-pack.txt
+++ b/Documentation/git-verify-pack.txt
@@ -8,13 +8,14 @@
 
 SYNOPSIS
 --------
-'git verify-pack' [-v|--verbose] [--] <pack>.idx ...
+[verse]
+'git verify-pack' [-v|--verbose] [-s|--stat-only] [--] <pack>.idx ...
 
 
 DESCRIPTION
 -----------
 Reads given idx file for packed git archive created with the
-'git-pack-objects' command and verifies idx file and the
+'git pack-objects' command and verifies idx file and the
 corresponding pack file.
 
 OPTIONS
@@ -25,7 +26,13 @@
 -v::
 --verbose::
 	After verifying the pack, show list of objects contained
-	in the pack.
+	in the pack and a histogram of delta chain length.
+
+-s::
+--stat-only::
+	Do not verify the pack contents; only show the histogram of delta
+	chain length.  With `--verbose`, list of objects is also shown.
+
 \--::
 	Do not interpret any more arguments as options.
 
@@ -41,14 +48,6 @@
 
 for objects that are deltified.
 
-Author
-------
-Written by Junio C Hamano <gitster@pobox.com>
-
-Documentation
---------------
-Documentation by Junio C Hamano
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-verify-tag.txt b/Documentation/git-verify-tag.txt
index 84e70a0..5ff76e8 100644
--- a/Documentation/git-verify-tag.txt
+++ b/Documentation/git-verify-tag.txt
@@ -7,25 +7,22 @@
 
 SYNOPSIS
 --------
+[verse]
 'git verify-tag' <tag>...
 
 DESCRIPTION
 -----------
-Validates the gpg signature created by 'git-tag'.
+Validates the gpg signature created by 'git tag'.
 
 OPTIONS
 -------
+-v::
+--verbose::
+	Print the contents of the tag object before validating it.
+
 <tag>...::
 	SHA1 identifiers of git tag objects.
 
-Author
-------
-Written by Jan Harkes <jaharkes@cs.cmu.edu> and Eric W. Biederman <ebiederm@xmission.com>
-
-Documentation
---------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-web--browse.txt b/Documentation/git-web--browse.txt
index 278cf73..c2bc87b 100644
--- a/Documentation/git-web--browse.txt
+++ b/Documentation/git-web--browse.txt
@@ -1,5 +1,5 @@
-git-web--browse(1)
-==================
+git-web{litdd}browse(1)
+=======================
 
 NAME
 ----
@@ -7,7 +7,8 @@
 
 SYNOPSIS
 --------
-'git web--browse' [OPTIONS] URL/FILE ...
+[verse]
+'git web{litdd}browse' [OPTIONS] URL/FILE ...
 
 DESCRIPTION
 -----------
@@ -20,8 +21,14 @@
 
 * firefox (this is the default under X Window when not using KDE)
 * iceweasel
+* seamonkey
+* iceape
+* chromium (also supported as chromium-browser)
+* google-chrome (also supported as chrome)
 * konqueror (this is the default under KDE, see 'Note about konqueror' below)
+* opera
 * w3m (this is the default outside graphical environments)
+* elinks
 * links
 * lynx
 * dillo
@@ -32,19 +39,19 @@
 
 OPTIONS
 -------
--b BROWSER::
---browser=BROWSER::
-	Use the specified BROWSER. It must be in the list of supported
+-b <browser>::
+--browser=<browser>::
+	Use the specified browser. It must be in the list of supported
 	browsers.
 
--t BROWSER::
---tool=BROWSER::
+-t <browser>::
+--tool=<browser>::
 	Same as above.
 
--c CONF.VAR::
---config=CONF.VAR::
+-c <conf.var>::
+--config=<conf.var>::
 	CONF.VAR is looked up in the git config files. If it's set,
-	then its value specify the browser that should be used.
+	then its value specifies the browser that should be used.
 
 CONFIGURATION VARIABLES
 -----------------------
@@ -62,7 +69,7 @@
 You can explicitly provide a full path to your preferred browser by
 setting the configuration variable 'browser.<tool>.path'. For example,
 you can configure the absolute path to firefox by setting
-'browser.firefox.path'. Otherwise, 'git-web--browse' assumes the tool
+'browser.firefox.path'. Otherwise, 'git web{litdd}browse' assumes the tool
 is available in PATH.
 
 browser.<tool>.cmd
@@ -71,7 +78,7 @@
 When the browser, specified by options or configuration variables, is
 not among the supported ones, then the corresponding
 'browser.<tool>.cmd' configuration variable will be looked up. If this
-variable exists then 'git-web--browse' will treat the specified tool
+variable exists then 'git web{litdd}browse' will treat the specified tool
 as a custom command and will use a shell eval to run the command with
 the URLs passed as arguments.
 
@@ -110,16 +117,6 @@
 as they are probably more user specific than repository specific.
 See linkgit:git-config[1] for more information about this.
 
-Author
-------
-Written by Christian Couder <chriscool@tuxfamily.org> and the git-list
-<git@vger.kernel.org>, based on 'git-mergetool' by Theodore Y. Ts'o.
-
-Documentation
--------------
-Documentation by Christian Couder <chriscool@tuxfamily.org> and the
-git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-whatchanged.txt b/Documentation/git-whatchanged.txt
index cadfbd9..6c8f510 100644
--- a/Documentation/git-whatchanged.txt
+++ b/Documentation/git-whatchanged.txt
@@ -8,13 +8,14 @@
 
 SYNOPSIS
 --------
+[verse]
 'git whatchanged' <option>...
 
 DESCRIPTION
 -----------
 Shows commit logs and diff output each commit introduces.  The
-command internally invokes 'git-rev-list' piped to
-'git-diff-tree', and takes command line options for both of
+command internally invokes 'git rev-list' piped to
+'git diff-tree', and takes command line options for both of
 these commands.
 
 This manual page describes only the most frequently used options.
@@ -52,28 +53,17 @@
 
 Examples
 --------
-git whatchanged -p v2.6.12.. include/scsi drivers/scsi::
+`git whatchanged -p v2.6.12.. include/scsi drivers/scsi`::
 
 	Show as patches the commits since version 'v2.6.12' that changed
 	any file in the include/scsi or drivers/scsi subdirectories
 
-git whatchanged --since="2 weeks ago" \-- gitk::
+`git whatchanged --since="2 weeks ago" -- gitk`::
 
 	Show the changes during the last two weeks to the file 'gitk'.
 	The "--" is necessary to avoid confusion with the *branch* named
 	'gitk'
 
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org> and
-Junio C Hamano <gitster@pobox.com>
-
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-write-tree.txt b/Documentation/git-write-tree.txt
index 26d3850..f22041a 100644
--- a/Documentation/git-write-tree.txt
+++ b/Documentation/git-write-tree.txt
@@ -8,25 +8,27 @@
 
 SYNOPSIS
 --------
+[verse]
 'git write-tree' [--missing-ok] [--prefix=<prefix>/]
 
 DESCRIPTION
 -----------
-Creates a tree object using the current index.
+Creates a tree object using the current index. The name of the new
+tree object is printed to standard output.
 
 The index must be in a fully merged state.
 
-Conceptually, 'git-write-tree' sync()s the current index contents
+Conceptually, 'git write-tree' sync()s the current index contents
 into a set of tree files.
 In order to have that match what is actually in your directory right
-now, you need to have done a 'git-update-index' phase before you did the
-'git-write-tree'.
+now, you need to have done a 'git update-index' phase before you did the
+'git write-tree'.
 
 
 OPTIONS
 -------
 --missing-ok::
-	Normally 'git-write-tree' ensures that the objects referenced by the
+	Normally 'git write-tree' ensures that the objects referenced by the
 	directory exist in the object database.  This option disables this
 	check.
 
@@ -35,15 +37,6 @@
 	`<prefix>`.  This can be used to write the tree object
 	for a subproject that is in the named subdirectory.
 
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 5fd5953..bf22ad5 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -9,10 +9,11 @@
 SYNOPSIS
 --------
 [verse]
-'git' [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]
-    [-p|--paginate|--no-pager]
-    [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE]
-    [--help] COMMAND [ARGS]
+'git' [--version] [--help] [-c <name>=<value>]
+    [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
+    [-p|--paginate|--no-pager] [--no-replace-objects] [--bare]
+    [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
+    <command> [<args>]
 
 DESCRIPTION
 -----------
@@ -27,7 +28,7 @@
 the link:user-manual.html[Git User's Manual] for a more in-depth
 introduction.
 
-The COMMAND is either a name of a Git command (see below) or an alias
+The '<command>' is either a name of a Git command (see below) or an alias
 as defined in the configuration file (see linkgit:git-config[1]).
 
 Formatted and hyperlinked version of the latest git
@@ -43,125 +44,278 @@
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v1.6.4/git.html[documentation for release 1.6.4]
+* link:v1.7.11.3/git.html[documentation for release 1.7.11.3]
 
 * release notes for
-  link:RelNotes-1.6.4.txt[1.6.4].
+  link:RelNotes/1.7.11.3.txt[1.7.11.3],
+  link:RelNotes/1.7.11.2.txt[1.7.11.2],
+  link:RelNotes/1.7.11.1.txt[1.7.11.1],
+  link:RelNotes/1.7.11.txt[1.7.11].
+
+* link:v1.7.10.5/git.html[documentation for release 1.7.10.5]
+
+* release notes for
+  link:RelNotes/1.7.10.5.txt[1.7.10.5],
+  link:RelNotes/1.7.10.4.txt[1.7.10.4],
+  link:RelNotes/1.7.10.3.txt[1.7.10.3],
+  link:RelNotes/1.7.10.2.txt[1.7.10.2],
+  link:RelNotes/1.7.10.1.txt[1.7.10.1],
+  link:RelNotes/1.7.10.txt[1.7.10].
+
+* link:v1.7.9.7/git.html[documentation for release 1.7.9.7]
+
+* release notes for
+  link:RelNotes/1.7.9.7.txt[1.7.9.7],
+  link:RelNotes/1.7.9.6.txt[1.7.9.6],
+  link:RelNotes/1.7.9.5.txt[1.7.9.5],
+  link:RelNotes/1.7.9.4.txt[1.7.9.4],
+  link:RelNotes/1.7.9.3.txt[1.7.9.3],
+  link:RelNotes/1.7.9.2.txt[1.7.9.2],
+  link:RelNotes/1.7.9.1.txt[1.7.9.1],
+  link:RelNotes/1.7.9.txt[1.7.9].
+
+* link:v1.7.8.6/git.html[documentation for release 1.7.8.6]
+
+* release notes for
+  link:RelNotes/1.7.8.6.txt[1.7.8.6],
+  link:RelNotes/1.7.8.5.txt[1.7.8.5],
+  link:RelNotes/1.7.8.4.txt[1.7.8.4],
+  link:RelNotes/1.7.8.3.txt[1.7.8.3],
+  link:RelNotes/1.7.8.2.txt[1.7.8.2],
+  link:RelNotes/1.7.8.1.txt[1.7.8.1],
+  link:RelNotes/1.7.8.txt[1.7.8].
+
+* link:v1.7.7.7/git.html[documentation for release 1.7.7.7]
+
+* release notes for
+  link:RelNotes/1.7.7.7.txt[1.7.7.7],
+  link:RelNotes/1.7.7.6.txt[1.7.7.6],
+  link:RelNotes/1.7.7.5.txt[1.7.7.5],
+  link:RelNotes/1.7.7.4.txt[1.7.7.4],
+  link:RelNotes/1.7.7.3.txt[1.7.7.3],
+  link:RelNotes/1.7.7.2.txt[1.7.7.2],
+  link:RelNotes/1.7.7.1.txt[1.7.7.1],
+  link:RelNotes/1.7.7.txt[1.7.7].
+
+* link:v1.7.6.6/git.html[documentation for release 1.7.6.6]
+
+* release notes for
+  link:RelNotes/1.7.6.6.txt[1.7.6.6],
+  link:RelNotes/1.7.6.5.txt[1.7.6.5],
+  link:RelNotes/1.7.6.4.txt[1.7.6.4],
+  link:RelNotes/1.7.6.3.txt[1.7.6.3],
+  link:RelNotes/1.7.6.2.txt[1.7.6.2],
+  link:RelNotes/1.7.6.1.txt[1.7.6.1],
+  link:RelNotes/1.7.6.txt[1.7.6].
+
+* link:v1.7.5.4/git.html[documentation for release 1.7.5.4]
+
+* release notes for
+  link:RelNotes/1.7.5.4.txt[1.7.5.4],
+  link:RelNotes/1.7.5.3.txt[1.7.5.3],
+  link:RelNotes/1.7.5.2.txt[1.7.5.2],
+  link:RelNotes/1.7.5.1.txt[1.7.5.1],
+  link:RelNotes/1.7.5.txt[1.7.5].
+
+* link:v1.7.4.5/git.html[documentation for release 1.7.4.5]
+
+* release notes for
+  link:RelNotes/1.7.4.5.txt[1.7.4.5],
+  link:RelNotes/1.7.4.4.txt[1.7.4.4],
+  link:RelNotes/1.7.4.3.txt[1.7.4.3],
+  link:RelNotes/1.7.4.2.txt[1.7.4.2],
+  link:RelNotes/1.7.4.1.txt[1.7.4.1],
+  link:RelNotes/1.7.4.txt[1.7.4].
+
+* link:v1.7.3.5/git.html[documentation for release 1.7.3.5]
+
+* release notes for
+  link:RelNotes/1.7.3.5.txt[1.7.3.5],
+  link:RelNotes/1.7.3.4.txt[1.7.3.4],
+  link:RelNotes/1.7.3.3.txt[1.7.3.3],
+  link:RelNotes/1.7.3.2.txt[1.7.3.2],
+  link:RelNotes/1.7.3.1.txt[1.7.3.1],
+  link:RelNotes/1.7.3.txt[1.7.3].
+
+* link:v1.7.2.5/git.html[documentation for release 1.7.2.5]
+
+* release notes for
+  link:RelNotes/1.7.2.5.txt[1.7.2.5],
+  link:RelNotes/1.7.2.4.txt[1.7.2.4],
+  link:RelNotes/1.7.2.3.txt[1.7.2.3],
+  link:RelNotes/1.7.2.2.txt[1.7.2.2],
+  link:RelNotes/1.7.2.1.txt[1.7.2.1],
+  link:RelNotes/1.7.2.txt[1.7.2].
+
+* link:v1.7.1.4/git.html[documentation for release 1.7.1.4]
+
+* release notes for
+  link:RelNotes/1.7.1.4.txt[1.7.1.4],
+  link:RelNotes/1.7.1.3.txt[1.7.1.3],
+  link:RelNotes/1.7.1.2.txt[1.7.1.2],
+  link:RelNotes/1.7.1.1.txt[1.7.1.1],
+  link:RelNotes/1.7.1.txt[1.7.1].
+
+* link:v1.7.0.9/git.html[documentation for release 1.7.0.9]
+
+* release notes for
+  link:RelNotes/1.7.0.9.txt[1.7.0.9],
+  link:RelNotes/1.7.0.8.txt[1.7.0.8],
+  link:RelNotes/1.7.0.7.txt[1.7.0.7],
+  link:RelNotes/1.7.0.6.txt[1.7.0.6],
+  link:RelNotes/1.7.0.5.txt[1.7.0.5],
+  link:RelNotes/1.7.0.4.txt[1.7.0.4],
+  link:RelNotes/1.7.0.3.txt[1.7.0.3],
+  link:RelNotes/1.7.0.2.txt[1.7.0.2],
+  link:RelNotes/1.7.0.1.txt[1.7.0.1],
+  link:RelNotes/1.7.0.txt[1.7.0].
+
+* link:v1.6.6.3/git.html[documentation for release 1.6.6.3]
+
+* release notes for
+  link:RelNotes/1.6.6.3.txt[1.6.6.3],
+  link:RelNotes/1.6.6.2.txt[1.6.6.2],
+  link:RelNotes/1.6.6.1.txt[1.6.6.1],
+  link:RelNotes/1.6.6.txt[1.6.6].
+
+* link:v1.6.5.9/git.html[documentation for release 1.6.5.9]
+
+* release notes for
+  link:RelNotes/1.6.5.9.txt[1.6.5.9],
+  link:RelNotes/1.6.5.8.txt[1.6.5.8],
+  link:RelNotes/1.6.5.7.txt[1.6.5.7],
+  link:RelNotes/1.6.5.6.txt[1.6.5.6],
+  link:RelNotes/1.6.5.5.txt[1.6.5.5],
+  link:RelNotes/1.6.5.4.txt[1.6.5.4],
+  link:RelNotes/1.6.5.3.txt[1.6.5.3],
+  link:RelNotes/1.6.5.2.txt[1.6.5.2],
+  link:RelNotes/1.6.5.1.txt[1.6.5.1],
+  link:RelNotes/1.6.5.txt[1.6.5].
+
+* link:v1.6.4.5/git.html[documentation for release 1.6.4.5]
+
+* release notes for
+  link:RelNotes/1.6.4.5.txt[1.6.4.5],
+  link:RelNotes/1.6.4.4.txt[1.6.4.4],
+  link:RelNotes/1.6.4.3.txt[1.6.4.3],
+  link:RelNotes/1.6.4.2.txt[1.6.4.2],
+  link:RelNotes/1.6.4.1.txt[1.6.4.1],
+  link:RelNotes/1.6.4.txt[1.6.4].
 
 * link:v1.6.3.4/git.html[documentation for release 1.6.3.4]
 
 * release notes for
-  link:RelNotes-1.6.3.4.txt[1.6.3.4],
-  link:RelNotes-1.6.3.3.txt[1.6.3.3],
-  link:RelNotes-1.6.3.2.txt[1.6.3.2],
-  link:RelNotes-1.6.3.1.txt[1.6.3.1],
-  link:RelNotes-1.6.3.txt[1.6.3].
+  link:RelNotes/1.6.3.4.txt[1.6.3.4],
+  link:RelNotes/1.6.3.3.txt[1.6.3.3],
+  link:RelNotes/1.6.3.2.txt[1.6.3.2],
+  link:RelNotes/1.6.3.1.txt[1.6.3.1],
+  link:RelNotes/1.6.3.txt[1.6.3].
 
 * release notes for
-  link:RelNotes-1.6.2.5.txt[1.6.2.5],
-  link:RelNotes-1.6.2.4.txt[1.6.2.4],
-  link:RelNotes-1.6.2.3.txt[1.6.2.3],
-  link:RelNotes-1.6.2.2.txt[1.6.2.2],
-  link:RelNotes-1.6.2.1.txt[1.6.2.1],
-  link:RelNotes-1.6.2.txt[1.6.2].
+  link:RelNotes/1.6.2.5.txt[1.6.2.5],
+  link:RelNotes/1.6.2.4.txt[1.6.2.4],
+  link:RelNotes/1.6.2.3.txt[1.6.2.3],
+  link:RelNotes/1.6.2.2.txt[1.6.2.2],
+  link:RelNotes/1.6.2.1.txt[1.6.2.1],
+  link:RelNotes/1.6.2.txt[1.6.2].
 
 * link:v1.6.1.3/git.html[documentation for release 1.6.1.3]
 
 * release notes for
-  link:RelNotes-1.6.1.3.txt[1.6.1.3],
-  link:RelNotes-1.6.1.2.txt[1.6.1.2],
-  link:RelNotes-1.6.1.1.txt[1.6.1.1],
-  link:RelNotes-1.6.1.txt[1.6.1].
+  link:RelNotes/1.6.1.3.txt[1.6.1.3],
+  link:RelNotes/1.6.1.2.txt[1.6.1.2],
+  link:RelNotes/1.6.1.1.txt[1.6.1.1],
+  link:RelNotes/1.6.1.txt[1.6.1].
 
 * link:v1.6.0.6/git.html[documentation for release 1.6.0.6]
 
 * release notes for
-  link:RelNotes-1.6.0.6.txt[1.6.0.6],
-  link:RelNotes-1.6.0.5.txt[1.6.0.5],
-  link:RelNotes-1.6.0.4.txt[1.6.0.4],
-  link:RelNotes-1.6.0.3.txt[1.6.0.3],
-  link:RelNotes-1.6.0.2.txt[1.6.0.2],
-  link:RelNotes-1.6.0.1.txt[1.6.0.1],
-  link:RelNotes-1.6.0.txt[1.6.0].
+  link:RelNotes/1.6.0.6.txt[1.6.0.6],
+  link:RelNotes/1.6.0.5.txt[1.6.0.5],
+  link:RelNotes/1.6.0.4.txt[1.6.0.4],
+  link:RelNotes/1.6.0.3.txt[1.6.0.3],
+  link:RelNotes/1.6.0.2.txt[1.6.0.2],
+  link:RelNotes/1.6.0.1.txt[1.6.0.1],
+  link:RelNotes/1.6.0.txt[1.6.0].
 
 * link:v1.5.6.6/git.html[documentation for release 1.5.6.6]
 
 * release notes for
-  link:RelNotes-1.5.6.6.txt[1.5.6.6],
-  link:RelNotes-1.5.6.5.txt[1.5.6.5],
-  link:RelNotes-1.5.6.4.txt[1.5.6.4],
-  link:RelNotes-1.5.6.3.txt[1.5.6.3],
-  link:RelNotes-1.5.6.2.txt[1.5.6.2],
-  link:RelNotes-1.5.6.1.txt[1.5.6.1],
-  link:RelNotes-1.5.6.txt[1.5.6].
+  link:RelNotes/1.5.6.6.txt[1.5.6.6],
+  link:RelNotes/1.5.6.5.txt[1.5.6.5],
+  link:RelNotes/1.5.6.4.txt[1.5.6.4],
+  link:RelNotes/1.5.6.3.txt[1.5.6.3],
+  link:RelNotes/1.5.6.2.txt[1.5.6.2],
+  link:RelNotes/1.5.6.1.txt[1.5.6.1],
+  link:RelNotes/1.5.6.txt[1.5.6].
 
 * link:v1.5.5.6/git.html[documentation for release 1.5.5.6]
 
 * release notes for
-  link:RelNotes-1.5.5.6.txt[1.5.5.6],
-  link:RelNotes-1.5.5.5.txt[1.5.5.5],
-  link:RelNotes-1.5.5.4.txt[1.5.5.4],
-  link:RelNotes-1.5.5.3.txt[1.5.5.3],
-  link:RelNotes-1.5.5.2.txt[1.5.5.2],
-  link:RelNotes-1.5.5.1.txt[1.5.5.1],
-  link:RelNotes-1.5.5.txt[1.5.5].
+  link:RelNotes/1.5.5.6.txt[1.5.5.6],
+  link:RelNotes/1.5.5.5.txt[1.5.5.5],
+  link:RelNotes/1.5.5.4.txt[1.5.5.4],
+  link:RelNotes/1.5.5.3.txt[1.5.5.3],
+  link:RelNotes/1.5.5.2.txt[1.5.5.2],
+  link:RelNotes/1.5.5.1.txt[1.5.5.1],
+  link:RelNotes/1.5.5.txt[1.5.5].
 
 * link:v1.5.4.7/git.html[documentation for release 1.5.4.7]
 
 * release notes for
-  link:RelNotes-1.5.4.7.txt[1.5.4.7],
-  link:RelNotes-1.5.4.6.txt[1.5.4.6],
-  link:RelNotes-1.5.4.5.txt[1.5.4.5],
-  link:RelNotes-1.5.4.4.txt[1.5.4.4],
-  link:RelNotes-1.5.4.3.txt[1.5.4.3],
-  link:RelNotes-1.5.4.2.txt[1.5.4.2],
-  link:RelNotes-1.5.4.1.txt[1.5.4.1],
-  link:RelNotes-1.5.4.txt[1.5.4].
+  link:RelNotes/1.5.4.7.txt[1.5.4.7],
+  link:RelNotes/1.5.4.6.txt[1.5.4.6],
+  link:RelNotes/1.5.4.5.txt[1.5.4.5],
+  link:RelNotes/1.5.4.4.txt[1.5.4.4],
+  link:RelNotes/1.5.4.3.txt[1.5.4.3],
+  link:RelNotes/1.5.4.2.txt[1.5.4.2],
+  link:RelNotes/1.5.4.1.txt[1.5.4.1],
+  link:RelNotes/1.5.4.txt[1.5.4].
 
 * link:v1.5.3.8/git.html[documentation for release 1.5.3.8]
 
 * release notes for
-  link:RelNotes-1.5.3.8.txt[1.5.3.8],
-  link:RelNotes-1.5.3.7.txt[1.5.3.7],
-  link:RelNotes-1.5.3.6.txt[1.5.3.6],
-  link:RelNotes-1.5.3.5.txt[1.5.3.5],
-  link:RelNotes-1.5.3.4.txt[1.5.3.4],
-  link:RelNotes-1.5.3.3.txt[1.5.3.3],
-  link:RelNotes-1.5.3.2.txt[1.5.3.2],
-  link:RelNotes-1.5.3.1.txt[1.5.3.1],
-  link:RelNotes-1.5.3.txt[1.5.3].
+  link:RelNotes/1.5.3.8.txt[1.5.3.8],
+  link:RelNotes/1.5.3.7.txt[1.5.3.7],
+  link:RelNotes/1.5.3.6.txt[1.5.3.6],
+  link:RelNotes/1.5.3.5.txt[1.5.3.5],
+  link:RelNotes/1.5.3.4.txt[1.5.3.4],
+  link:RelNotes/1.5.3.3.txt[1.5.3.3],
+  link:RelNotes/1.5.3.2.txt[1.5.3.2],
+  link:RelNotes/1.5.3.1.txt[1.5.3.1],
+  link:RelNotes/1.5.3.txt[1.5.3].
 
 * link:v1.5.2.5/git.html[documentation for release 1.5.2.5]
 
 * release notes for
-  link:RelNotes-1.5.2.5.txt[1.5.2.5],
-  link:RelNotes-1.5.2.4.txt[1.5.2.4],
-  link:RelNotes-1.5.2.3.txt[1.5.2.3],
-  link:RelNotes-1.5.2.2.txt[1.5.2.2],
-  link:RelNotes-1.5.2.1.txt[1.5.2.1],
-  link:RelNotes-1.5.2.txt[1.5.2].
+  link:RelNotes/1.5.2.5.txt[1.5.2.5],
+  link:RelNotes/1.5.2.4.txt[1.5.2.4],
+  link:RelNotes/1.5.2.3.txt[1.5.2.3],
+  link:RelNotes/1.5.2.2.txt[1.5.2.2],
+  link:RelNotes/1.5.2.1.txt[1.5.2.1],
+  link:RelNotes/1.5.2.txt[1.5.2].
 
 * link:v1.5.1.6/git.html[documentation for release 1.5.1.6]
 
 * release notes for
-  link:RelNotes-1.5.1.6.txt[1.5.1.6],
-  link:RelNotes-1.5.1.5.txt[1.5.1.5],
-  link:RelNotes-1.5.1.4.txt[1.5.1.4],
-  link:RelNotes-1.5.1.3.txt[1.5.1.3],
-  link:RelNotes-1.5.1.2.txt[1.5.1.2],
-  link:RelNotes-1.5.1.1.txt[1.5.1.1],
-  link:RelNotes-1.5.1.txt[1.5.1].
+  link:RelNotes/1.5.1.6.txt[1.5.1.6],
+  link:RelNotes/1.5.1.5.txt[1.5.1.5],
+  link:RelNotes/1.5.1.4.txt[1.5.1.4],
+  link:RelNotes/1.5.1.3.txt[1.5.1.3],
+  link:RelNotes/1.5.1.2.txt[1.5.1.2],
+  link:RelNotes/1.5.1.1.txt[1.5.1.1],
+  link:RelNotes/1.5.1.txt[1.5.1].
 
 * link:v1.5.0.7/git.html[documentation for release 1.5.0.7]
 
 * release notes for
-  link:RelNotes-1.5.0.7.txt[1.5.0.7],
-  link:RelNotes-1.5.0.6.txt[1.5.0.6],
-  link:RelNotes-1.5.0.5.txt[1.5.0.5],
-  link:RelNotes-1.5.0.3.txt[1.5.0.3],
-  link:RelNotes-1.5.0.2.txt[1.5.0.2],
-  link:RelNotes-1.5.0.1.txt[1.5.0.1],
-  link:RelNotes-1.5.0.txt[1.5.0].
+  link:RelNotes/1.5.0.7.txt[1.5.0.7],
+  link:RelNotes/1.5.0.6.txt[1.5.0.6],
+  link:RelNotes/1.5.0.5.txt[1.5.0.5],
+  link:RelNotes/1.5.0.3.txt[1.5.0.3],
+  link:RelNotes/1.5.0.2.txt[1.5.0.2],
+  link:RelNotes/1.5.0.1.txt[1.5.0.1],
+  link:RelNotes/1.5.0.txt[1.5.0].
 
 * documentation for release link:v1.4.4.4/git.html[1.4.4.4],
   link:v1.3.3/git.html[1.3.3],
@@ -188,19 +342,36 @@
 because `git --help ...` is converted internally into `git
 help ...`.
 
---exec-path::
+-c <name>=<value>::
+	Pass a configuration parameter to the command. The value
+	given will override values from configuration files.
+	The <name> is expected in the same format as listed by
+	'git config' (subkeys separated by dots).
+
+--exec-path[=<path>]::
 	Path to wherever your core git programs are installed.
 	This can also be controlled by setting the GIT_EXEC_PATH
 	environment variable. If no path is given, 'git' will print
 	the current setting and then exit.
 
 --html-path::
-	Print the path to wherever your git HTML documentation is installed
-	and exit.
+	Print the path, without trailing slash, where git's HTML
+	documentation is installed and exit.
+
+--man-path::
+	Print the manpath (see `man(1)`) for the man pages for
+	this version of git and exit.
+
+--info-path::
+	Print the path where the Info files documenting this
+	version of git are installed and exit.
 
 -p::
 --paginate::
-	Pipe all output into 'less' (or if set, $PAGER).
+	Pipe all output into 'less' (or if set, $PAGER) if standard
+	output is a terminal.  This overrides the `pager.<cmd>`
+	configuration options (see the "Configuration Mechanism" section
+	below).
 
 --no-pager::
 	Do not pipe git output into a pager.
@@ -211,23 +382,27 @@
 	path or relative path to current working directory.
 
 --work-tree=<path>::
-	Set the path to the working tree.  The value will not be
-	used in combination with repositories found automatically in
-	a .git directory (i.e. $GIT_DIR is not set).
+	Set the path to the working tree. It can be an absolute path
+	or a path relative to the current working directory.
 	This can also be controlled by setting the GIT_WORK_TREE
 	environment variable and the core.worktree configuration
-	variable. It can be an absolute path or relative path to
-	the directory specified by --git-dir or GIT_DIR.
-	Note: If --git-dir or GIT_DIR are specified but none of
-	--work-tree, GIT_WORK_TREE and core.worktree is specified,
-	the current working directory is regarded as the top directory
-	of your working tree.
+	variable (see core.worktree in linkgit:git-config[1] for a
+	more detailed discussion).
+
+--namespace=<path>::
+	Set the git namespace.  See linkgit:gitnamespaces[7] for more
+	details.  Equivalent to setting the `GIT_NAMESPACE` environment
+	variable.
 
 --bare::
 	Treat the repository as a bare repository.  If GIT_DIR
 	environment is not set, it is set to the current working
 	directory.
 
+--no-replace-objects::
+	Do not use replacement refs to replace git objects. See
+	linkgit:git-replace[1] for more information.
+
 
 FURTHER DOCUMENTATION
 ---------------------
@@ -327,7 +502,7 @@
 
 include::cmds-synchingrepositories.txt[]
 
-The following are helper programs used by the above; end users
+The following are helper commands used by the above; end users
 typically do not use them directly.
 
 include::cmds-synchelpers.txt[]
@@ -368,7 +543,8 @@
 ------------
 
 Various commands read from the configuration file and adjust
-their operation accordingly.
+their operation accordingly.  See linkgit:git-config[1] for a
+list.
 
 
 Identifier Terminology
@@ -411,19 +587,18 @@
 symbolic notation:
 
 HEAD::
-	indicates the head of the current branch (i.e. the
-	contents of `$GIT_DIR/HEAD`).
+	indicates the head of the current branch.
 
 <tag>::
 	a valid tag 'name'
-	(i.e. the contents of `$GIT_DIR/refs/tags/<tag>`).
+	(i.e. a `refs/tags/<tag>` reference).
 
 <head>::
 	a valid head 'name'
-	(i.e. the contents of `$GIT_DIR/refs/heads/<head>`).
+	(i.e. a `refs/heads/<head>` reference).
 
 For a more complete list of ways to spell object names, see
-"SPECIFYING REVISIONS" section in linkgit:git-rev-parse[1].
+"SPECIFYING REVISIONS" section in linkgit:gitrevisions[7].
 
 
 File/Directory Structure
@@ -482,6 +657,10 @@
 	This can also be controlled by the '--work-tree' command line
 	option and the core.worktree configuration variable.
 
+'GIT_NAMESPACE'::
+	Set the git namespace; see linkgit:gitnamespaces[7] for details.
+	The '--namespace' command-line option also sets this value.
+
 'GIT_CEILING_DIRECTORIES'::
 	This should be a colon-separated list of absolute paths.
 	If set, it is a list of directories that git should not chdir
@@ -490,6 +669,16 @@
 	a GIT_DIR set on the command line or in the environment.
 	(Useful for excluding slow-loading network directories.)
 
+'GIT_DISCOVERY_ACROSS_FILESYSTEM'::
+	When run in a directory that does not have ".git" repository
+	directory, git tries to find such a directory in the parent
+	directories to find the top of the working tree, but by default it
+	does not cross filesystem boundaries.  This environment variable
+	can be set to true to tell git not to stop at filesystem
+	boundaries.  Like 'GIT_CEILING_DIRECTORIES', this will not affect
+	an explicit repository directory set via 'GIT_DIR' or on the
+	command line.
+
 git Commits
 ~~~~~~~~~~~
 'GIT_AUTHOR_NAME'::
@@ -523,7 +712,6 @@
                          contents of <old|new>,
 	<old|new>-hex:: are the 40-hexdigit SHA1 hashes,
 	<old|new>-mode:: are the octal representation of the file modes.
-
 +
 The file parameters can point at the user's working file
 (e.g. `new-file` in "git-diff-files"), `/dev/null` (e.g. `old-file`
@@ -547,9 +735,15 @@
 	a pager.  See also the `core.pager` option in
 	linkgit:git-config[1].
 
+'GIT_EDITOR'::
+	This environment variable overrides `$EDITOR` and `$VISUAL`.
+	It is used by several git commands when, on interactive mode,
+	an editor is to be launched. See also linkgit:git-var[1]
+	and the `core.editor` option in linkgit:git-config[1].
+
 'GIT_SSH'::
-	If this environment variable is set then 'git-fetch'
-	and 'git-push' will use this command instead
+	If this environment variable is set then 'git fetch'
+	and 'git push' will use this command instead
 	of 'ssh' when they need to connect to a remote system.
 	The '$GIT_SSH' command will be given exactly two arguments:
 	the 'username@host' (or just 'host') from the URL and the
@@ -563,10 +757,17 @@
 personal `.ssh/config` file.  Please consult your ssh documentation
 for further details.
 
+'GIT_ASKPASS'::
+	If this environment variable is set, then git commands which need to
+	acquire passwords or passphrases (e.g. for HTTP or IMAP authentication)
+	will call this program with a suitable prompt as command line argument
+	and read the password from its STDOUT. See also the 'core.askpass'
+	option in linkgit:git-config[1].
+
 'GIT_FLUSH'::
 	If this environment variable is set to "1", then commands such
-	as 'git-blame' (in incremental mode), 'git-rev-list', 'git-log',
-	and 'git-whatchanged' will force a flush of the output stream
+	as 'git blame' (in incremental mode), 'git rev-list', 'git log',
+	and 'git whatchanged' will force a flush of the output stream
 	after each commit-oriented record have been flushed.   If this
 	variable is set to "0", the output of these commands will be done
 	using completely buffered I/O.   If this environment variable is
@@ -642,16 +843,19 @@
 
 Authors
 -------
-* git's founding father is Linus Torvalds <torvalds@osdl.org>.
-* The current git nurse is Junio C Hamano <gitster@pobox.com>.
-* The git potty was written by Andreas Ericsson <ae@op5.se>.
-* General upbringing is handled by the git-list <git@vger.kernel.org>.
+Git was started by Linus Torvalds, and is currently maintained by Junio
+C Hamano. Numerous contributions have come from the git mailing list
+<git@vger.kernel.org>. For a more complete list of contributors, see
+http://git-scm.com/about. If you have a clone of git.git itself, the
+output of linkgit:git-shortlog[1] and linkgit:git-blame[1] can show you
+the authors for specific parts of the project.
 
-Documentation
+Reporting Bugs
 --------------
-The documentation for git suite was started by David Greaves
-<david@dgreaves.com>, and later enhanced greatly by the
-contributors on the git-list <git@vger.kernel.org>.
+
+Report bugs to the Git mailing list <git@vger.kernel.org> where the
+development and maintenance is primarily done.  You do not have to be
+subscribed to the list to send a message there.
 
 SEE ALSO
 --------
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index aaa073e..e16f3e1 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -62,17 +62,26 @@
 precedence), `.gitattributes` file in the same directory as the
 path in question, and its parent directories up to the toplevel of the
 work tree (the further the directory that contains `.gitattributes`
-is from the path in question, the lower its precedence).
+is from the path in question, the lower its precedence). Finally
+global and system-wide files are considered (they have the lowest
+precedence).
 
 If you wish to affect only a single repository (i.e., to assign
-attributes to files that are particular to one user's workflow), then
+attributes to files that are particular to
+one user's workflow for that repository), then
 attributes should be placed in the `$GIT_DIR/info/attributes` file.
 Attributes which should be version-controlled and distributed to other
 repositories (i.e., attributes of interest to all users) should go into
-`.gitattributes` files.
+`.gitattributes` files. Attributes that should affect all repositories
+for a single user should be placed in a file specified by the
+`core.attributesfile` configuration option (see linkgit:git-config[1]).
+Its default value is $XDG_CONFIG_HOME/git/attributes. If $XDG_CONFIG_HOME
+is either not set or empty, $HOME/.config/git/attributes is used instead.
+Attributes for all users on a system should be placed in the
+`$(prefix)/etc/gitattributes` file.
 
 Sometimes you would need to override an setting of an attribute
-for a path to `unspecified` state.  This can be done by listing
+for a path to `Unspecified` state.  This can be done by listing
 the name of the attribute prefixed with an exclamation point `!`.
 
 
@@ -88,57 +97,158 @@
 
 These attributes affect how the contents stored in the
 repository are copied to the working tree files when commands
-such as 'git-checkout' and 'git-merge' run.  They also affect how
+such as 'git checkout' and 'git merge' run.  They also affect how
 git stores the contents you prepare in the working tree in the
-repository upon 'git-add' and 'git-commit'.
+repository upon 'git add' and 'git commit'.
 
-`crlf`
+`text`
 ^^^^^^
 
-This attribute controls the line-ending convention.
+This attribute enables and controls end-of-line normalization.  When a
+text file is normalized, its line endings are converted to LF in the
+repository.  To control what line ending style is used in the working
+directory, use the `eol` attribute for a single file and the
+`core.eol` configuration variable for all text files.
 
 Set::
 
-	Setting the `crlf` attribute on a path is meant to mark
-	the path as a "text" file.  'core.autocrlf' conversion
-	takes place without guessing the content type by
-	inspection.
+	Setting the `text` attribute on a path enables end-of-line
+	normalization and marks the path as a text file.  End-of-line
+	conversion takes place without guessing the content type.
 
 Unset::
 
-	Unsetting the `crlf` attribute on a path tells git not to
+	Unsetting the `text` attribute on a path tells git not to
 	attempt any end-of-line conversion upon checkin or checkout.
 
+Set to string value "auto"::
+
+	When `text` is set to "auto", the path is marked for automatic
+	end-of-line normalization.  If git decides that the content is
+	text, its line endings are normalized to LF on checkin.
+
 Unspecified::
 
-	Unspecified `crlf` attribute tells git to apply the
-	`core.autocrlf` conversion when the file content looks
-	like text.
+	If the `text` attribute is unspecified, git uses the
+	`core.autocrlf` configuration variable to determine if the
+	file should be converted.
 
-Set to string value "input"::
+Any other value causes git to act as if `text` has been left
+unspecified.
 
-	This is similar to setting the attribute to `true`, but
-	also forces git to act as if `core.autocrlf` is set to
-	`input` for the path.
+`eol`
+^^^^^
 
-Any other value set to `crlf` attribute is ignored and git acts
-as if the attribute is left unspecified.
+This attribute sets a specific line-ending style to be used in the
+working directory.  It enables end-of-line normalization without any
+content checks, effectively setting the `text` attribute.
 
+Set to string value "crlf"::
 
-The `core.autocrlf` conversion
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+	This setting forces git to normalize line endings for this
+	file on checkin and convert them to CRLF when the file is
+	checked out.
 
-If the configuration variable `core.autocrlf` is false, no
-conversion is done.
+Set to string value "lf"::
 
-When `core.autocrlf` is true, it means that the platform wants
-CRLF line endings for files in the working tree, and you want to
-convert them back to the normal LF line endings when checking
-in to the repository.
+	This setting forces git to normalize line endings to LF on
+	checkin and prevents conversion to CRLF when the file is
+	checked out.
 
-When `core.autocrlf` is set to "input", line endings are
-converted to LF upon checkin, but there is no conversion done
-upon checkout.
+Backwards compatibility with `crlf` attribute
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+For backwards compatibility, the `crlf` attribute is interpreted as
+follows:
+
+------------------------
+crlf		text
+-crlf		-text
+crlf=input	eol=lf
+------------------------
+
+End-of-line conversion
+^^^^^^^^^^^^^^^^^^^^^^
+
+While git normally leaves file contents alone, it can be configured to
+normalize line endings to LF in the repository and, optionally, to
+convert them to CRLF when files are checked out.
+
+Here is an example that will make git normalize .txt, .vcproj and .sh
+files, ensure that .vcproj files have CRLF and .sh files have LF in
+the working directory, and prevent .jpg files from being normalized
+regardless of their content.
+
+------------------------
+*.txt		text
+*.vcproj	eol=crlf
+*.sh		eol=lf
+*.jpg		-text
+------------------------
+
+Other source code management systems normalize all text files in their
+repositories, and there are two ways to enable similar automatic
+normalization in git.
+
+If you simply want to have CRLF line endings in your working directory
+regardless of the repository you are working with, you can set the
+config variable "core.autocrlf" without changing any attributes.
+
+------------------------
+[core]
+	autocrlf = true
+------------------------
+
+This does not force normalization of all text files, but does ensure
+that text files that you introduce to the repository have their line
+endings normalized to LF when they are added, and that files that are
+already normalized in the repository stay normalized.
+
+If you want to interoperate with a source code management system that
+enforces end-of-line normalization, or you simply want all text files
+in your repository to be normalized, you should instead set the `text`
+attribute to "auto" for _all_ files.
+
+------------------------
+*	text=auto
+------------------------
+
+This ensures that all files that git considers to be text will have
+normalized (LF) line endings in the repository.  The `core.eol`
+configuration variable controls which line endings git will use for
+normalized files in your working directory; the default is to use the
+native line ending for your platform, or CRLF if `core.autocrlf` is
+set.
+
+NOTE: When `text=auto` normalization is enabled in an existing
+repository, any text files containing CRLFs should be normalized.  If
+they are not they will be normalized the next time someone tries to
+change them, causing unfortunate misattribution.  From a clean working
+directory:
+
+-------------------------------------------------
+$ echo "* text=auto" >>.gitattributes
+$ rm .git/index     # Remove the index to force git to
+$ git reset         # re-scan the working directory
+$ git status        # Show files that will be normalized
+$ git add -u
+$ git add .gitattributes
+$ git commit -m "Introduce end-of-line normalization"
+-------------------------------------------------
+
+If any files that should not be normalized show up in 'git status',
+unset their `text` attribute before running 'git add -u'.
+
+------------------------
+manual.pdf	-text
+------------------------
+
+Conversely, text files that git does not detect can have normalization
+enabled manually.
+
+------------------------
+weirdchars.txt	text
+------------------------
 
 If `core.safecrlf` is set to "true" or "warn", git verifies if
 the conversion is reversible for the current setting of
@@ -148,16 +258,16 @@
 a conversion done to the files in the work tree, but there are a
 few exceptions.  Even though...
 
-- 'git-add' itself does not touch the files in the work tree, the
+- 'git add' itself does not touch the files in the work tree, the
   next checkout would, so the safety triggers;
 
-- 'git-apply' to update a text file with a patch does touch the files
+- 'git apply' to update a text file with a patch does touch the files
   in the work tree, but the operation is about text files and CRLF
   conversion is about fixing the line ending inconsistencies, so the
   safety does not trigger;
 
-- 'git-diff' itself does not touch the files in the work tree, it is
-  often run to inspect the changes you intend to next 'git-add'.  To
+- 'git diff' itself does not touch the files in the work tree, it is
+  often run to inspect the changes you intend to next 'git add'.  To
   catch potential problems early, safety triggers.
 
 
@@ -186,16 +296,77 @@
 `clean` command is used to convert the contents of worktree file
 upon checkin.
 
-A missing filter driver definition in the config is not an error
-but makes the filter a no-op passthru.
+One use of the content filtering is to massage the content into a shape
+that is more convenient for the platform, filesystem, and the user to use.
+For this mode of operation, the key phrase here is "more convenient" and
+not "turning something unusable into usable".  In other words, the intent
+is that if someone unsets the filter driver definition, or does not have
+the appropriate filter program, the project should still be usable.
 
-The content filtering is done to massage the content into a
-shape that is more convenient for the platform, filesystem, and
-the user to use.  The key phrase here is "more convenient" and not
-"turning something unusable into usable".  In other words, the
-intent is that if someone unsets the filter driver definition,
-or does not have the appropriate filter program, the project
-should still be usable.
+Another use of the content filtering is to store the content that cannot
+be directly used in the repository (e.g. a UUID that refers to the true
+content stored outside git, or an encrypted content) and turn it into a
+usable form upon checkout (e.g. download the external content, or decrypt
+the encrypted content).
+
+These two filters behave differently, and by default, a filter is taken as
+the former, massaging the contents into more convenient shape.  A missing
+filter driver definition in the config, or a filter driver that exits with
+a non-zero status, is not an error but makes the filter a no-op passthru.
+
+You can declare that a filter turns a content that by itself is unusable
+into a usable content by setting the filter.<driver>.required configuration
+variable to `true`.
+
+For example, in .gitattributes, you would assign the `filter`
+attribute for paths.
+
+------------------------
+*.c	filter=indent
+------------------------
+
+Then you would define a "filter.indent.clean" and "filter.indent.smudge"
+configuration in your .git/config to specify a pair of commands to
+modify the contents of C programs when the source files are checked
+in ("clean" is run) and checked out (no change is made because the
+command is "cat").
+
+------------------------
+[filter "indent"]
+	clean = indent
+	smudge = cat
+------------------------
+
+For best results, `clean` should not alter its output further if it is
+run twice ("clean->clean" should be equivalent to "clean"), and
+multiple `smudge` commands should not alter `clean`'s output
+("smudge->smudge->clean" should be equivalent to "clean").  See the
+section on merging below.
+
+The "indent" filter is well-behaved in this regard: it will not modify
+input that is already correctly indented.  In this case, the lack of a
+smudge filter means that the clean filter _must_ accept its own output
+without modifying it.
+
+If a filter _must_ succeed in order to make the stored contents usable,
+you can declare that the filter is `required`, in the configuration:
+
+------------------------
+[filter "crypt"]
+	clean = openssl enc ...
+	smudge = openssl enc -d ...
+	required
+------------------------
+
+Sequence "%f" on the filter command line is replaced with the name of
+the file the filter is working on.  A filter might use this in keyword
+substitution.  For example:
+
+------------------------
+[filter "p4"]
+	clean = git-p4-filter --clean %f
+	smudge = git-p4-filter --smudge %f
+------------------------
 
 
 Interaction between checkin/checkout attributes
@@ -204,11 +375,34 @@
 In the check-in codepath, the worktree file is first converted
 with `filter` driver (if specified and corresponding driver
 defined), then the result is processed with `ident` (if
-specified), and then finally with `crlf` (again, if specified
+specified), and then finally with `text` (again, if specified
 and applicable).
 
 In the check-out codepath, the blob content is first converted
-with `crlf`, and then `ident` and fed to `filter`.
+with `text`, and then `ident` and fed to `filter`.
+
+
+Merging branches with differing checkin/checkout attributes
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+If you have added attributes to a file that cause the canonical
+repository format for that file to change, such as adding a
+clean/smudge filter or text/eol/ident attributes, merging anything
+where the attribute is not in place would normally cause merge
+conflicts.
+
+To prevent these unnecessary merge conflicts, git can be told to run a
+virtual check-out and check-in of all three stages of a file when
+resolving a three-way merge by setting the `merge.renormalize`
+configuration variable.  This prevents changes caused by check-in
+conversion from causing spurious merge conflicts when a converted file
+is merged with an unconverted file.
+
+As long as a "smudge->clean" results in the same output as a "clean"
+even on files that are already smudged, this strategy will
+automatically resolve all filter-related conflicts.  Filters that do
+not act in this way may cause additional merge conflicts that must be
+resolved manually.
 
 
 Generating diff text
@@ -321,14 +515,22 @@
 
 - `cpp` suitable for source code in the C and C++ languages.
 
+- `csharp` suitable for source code in the C# language.
+
+- `fortran` suitable for source code in the Fortran language.
+
 - `html` suitable for HTML/XHTML documents.
 
 - `java` suitable for source code in the Java language.
 
+- `matlab` suitable for source code in the MATLAB language.
+
 - `objc` suitable for source code in the Objective-C language.
 
 - `pascal` suitable for source code in the Pascal/Delphi language.
 
+- `perl` suitable for source code in the Perl language.
+
 - `php` suitable for source code in the PHP language.
 
 - `python` suitable for source code in the Python language.
@@ -341,7 +543,7 @@
 Customizing word diff
 ^^^^^^^^^^^^^^^^^^^^^
 
-You can customize the rules that `git diff --color-words` uses to
+You can customize the rules that `git diff --word-diff` uses to
 split words in a line, by specifying an appropriate regular expression
 in the "diff.*.wordRegex" configuration variable.  For example, in TeX
 a backslash followed by a sequence of letters forms a command, but
@@ -395,6 +597,90 @@
 should generate it separately and send it as a comment _in
 addition to_ the usual binary diff that you might send.
 
+Because text conversion can be slow, especially when doing a
+large number of them with `git log -p`, git provides a mechanism
+to cache the output and use it in future diffs.  To enable
+caching, set the "cachetextconv" variable in your diff driver's
+config. For example:
+
+------------------------
+[diff "jpg"]
+	textconv = exif
+	cachetextconv = true
+------------------------
+
+This will cache the result of running "exif" on each blob
+indefinitely. If you change the textconv config variable for a
+diff driver, git will automatically invalidate the cache entries
+and re-run the textconv filter. If you want to invalidate the
+cache manually (e.g., because your version of "exif" was updated
+and now produces better output), you can remove the cache
+manually with `git update-ref -d refs/notes/textconv/jpg` (where
+"jpg" is the name of the diff driver, as in the example above).
+
+Choosing textconv versus external diff
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+If you want to show differences between binary or specially-formatted
+blobs in your repository, you can choose to use either an external diff
+command, or to use textconv to convert them to a diff-able text format.
+Which method you choose depends on your exact situation.
+
+The advantage of using an external diff command is flexibility. You are
+not bound to find line-oriented changes, nor is it necessary for the
+output to resemble unified diff. You are free to locate and report
+changes in the most appropriate way for your data format.
+
+A textconv, by comparison, is much more limiting. You provide a
+transformation of the data into a line-oriented text format, and git
+uses its regular diff tools to generate the output. There are several
+advantages to choosing this method:
+
+1. Ease of use. It is often much simpler to write a binary to text
+   transformation than it is to perform your own diff. In many cases,
+   existing programs can be used as textconv filters (e.g., exif,
+   odt2txt).
+
+2. Git diff features. By performing only the transformation step
+   yourself, you can still utilize many of git's diff features,
+   including colorization, word-diff, and combined diffs for merges.
+
+3. Caching. Textconv caching can speed up repeated diffs, such as those
+   you might trigger by running `git log -p`.
+
+
+Marking files as binary
+^^^^^^^^^^^^^^^^^^^^^^^
+
+Git usually guesses correctly whether a blob contains text or binary
+data by examining the beginning of the contents. However, sometimes you
+may want to override its decision, either because a blob contains binary
+data later in the file, or because the content, while technically
+composed of text characters, is opaque to a human reader. For example,
+many postscript files contain only ascii characters, but produce noisy
+and meaningless diffs.
+
+The simplest way to mark a file as binary is to unset the diff
+attribute in the `.gitattributes` file:
+
+------------------------
+*.ps -diff
+------------------------
+
+This will cause git to generate `Binary files differ` (or a binary
+patch, if binary patches are enabled) instead of a regular diff.
+
+However, one may also want to specify other diff driver attributes. For
+example, you might want to use `textconv` to convert postscript files to
+an ascii representation for human viewing, but otherwise treat them as
+binary files. You cannot specify both `-diff` and `diff=ps` attributes.
+The solution is to use the `diff.*.binary` config option:
+
+------------------------
+[diff "ps"]
+  textconv = ps2ascii
+  binary = true
+------------------------
 
 Performing a three-way merge
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -402,9 +688,9 @@
 `merge`
 ^^^^^^^
 
-The attribute `merge` affects how three versions of a file is
+The attribute `merge` affects how three versions of a file are
 merged when a file-level merge is necessary during `git merge`,
-and other programs such as `git revert` and `git cherry-pick`.
+and other commands such as `git revert` and `git cherry-pick`.
 
 Set::
 
@@ -416,15 +702,15 @@
 
 	Take the version from the current branch as the
 	tentative merge result, and declare that the merge has
-	conflicts.  This is suitable for binary files that does
+	conflicts.  This is suitable for binary files that do
 	not have a well-defined merge semantics.
 
 Unspecified::
 
 	By default, this uses the same built-in 3-way merge
-	driver as is the case the `merge` attribute is set.
-	However, `merge.default` configuration variable can name
-	different merge driver to be used for paths to which the
+	driver as is the case when the `merge` attribute is set.
+	However, the `merge.default` configuration variable can name
+	different merge driver to be used with paths for which the
 	`merge` attribute is unspecified.
 
 String::
@@ -492,7 +778,8 @@
 version (`%A`) and the other branches' version (`%B`).  These
 three tokens are replaced with the names of temporary files that
 hold the contents of these versions when the command line is
-built.
+built. Additionally, %L will be replaced with the conflict marker
+size (see below).
 
 The merge driver is expected to leave the result of the merge in
 the file named with `%A` by overwriting it, and exit with zero
@@ -506,6 +793,23 @@
 internal merge and the final merge.
 
 
+`conflict-marker-size`
+^^^^^^^^^^^^^^^^^^^^^^
+
+This attribute controls the length of conflict markers left in
+the work tree file during a conflicted merge.  Only setting to
+the value to a positive integer has any meaningful effect.
+
+For example, this line in `.gitattributes` can be used to tell the merge
+machinery to leave much longer (instead of the usual 7-character-long)
+conflict markers when merging the file `Documentation/git-merge.txt`
+results in a conflict.
+
+------------------------
+Documentation/git-merge.txt	conflict-marker-size=32
+------------------------
+
+
 Checking whitespace errors
 ~~~~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -520,6 +824,8 @@
 Set::
 
 	Notice all types of potential whitespace errors known to git.
+	The tab width is taken from the value of the `core.whitespace`
+	configuration variable.
 
 Unset::
 
@@ -527,13 +833,13 @@
 
 Unspecified::
 
-	Use the value of `core.whitespace` configuration variable to
+	Use the value of the `core.whitespace` configuration variable to
 	decide what to notice as error.
 
 String::
 
 	Specify a comma separate list of common whitespace problems to
-	notice in the same format as `core.whitespace` configuration
+	notice in the same format as the `core.whitespace` configuration
 	variable.
 
 
@@ -560,6 +866,16 @@
 commit hash.
 
 
+Packing objects
+~~~~~~~~~~~~~~~
+
+`delta`
+^^^^^^^
+
+Delta compression will not be attempted for blobs for paths with the
+attribute `delta` set to false.
+
+
 Viewing files in GUI tools
 ~~~~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -577,38 +893,41 @@
 (See linkgit:git-config[1]).
 
 
-USING ATTRIBUTE MACROS
+USING MACRO ATTRIBUTES
 ----------------------
 
 You do not want any end-of-line conversions applied to, nor textual diffs
 produced for, any binary file you track.  You would need to specify e.g.
 
 ------------
-*.jpg -crlf -diff
+*.jpg -text -diff
 ------------
 
 but that may become cumbersome, when you have many attributes.  Using
-attribute macros, you can specify groups of attributes set or unset at
-the same time.  The system knows a built-in attribute macro, `binary`:
+macro attributes, you can define an attribute that, when set, also
+sets or unsets a number of other attributes at the same time.  The
+system knows a built-in macro attribute, `binary`:
 
 ------------
 *.jpg binary
 ------------
 
-which is equivalent to the above.  Note that the attribute macros can only
-be "Set" (see the above example that sets "binary" macro as if it were an
-ordinary attribute --- setting it in turn unsets "crlf" and "diff").
+Setting the "binary" attribute also unsets the "text" and "diff"
+attributes as above.  Note that macro attributes can only be "Set",
+though setting one might have the effect of setting or unsetting other
+attributes or even returning other attributes to the "Unspecified"
+state.
 
 
-DEFINING ATTRIBUTE MACROS
+DEFINING MACRO ATTRIBUTES
 -------------------------
 
-Custom attribute macros can be defined only in the `.gitattributes` file
-at the toplevel (i.e. not in any subdirectory).  The built-in attribute
-macro "binary" is equivalent to:
+Custom macro attributes can be defined only in the `.gitattributes`
+file at the toplevel (i.e. not in any subdirectory).  The built-in
+macro attribute "binary" is equivalent to:
 
 ------------
-[attr]binary -diff -crlf
+[attr]binary -diff -text
 ------------
 
 
@@ -661,6 +980,9 @@
 ----------------------------------------------------------------
 
 
+SEE ALSO
+--------
+linkgit:git-check-attr[1].
 
 GIT
 ---
diff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt
index be39ed7..ea17f7a 100644
--- a/Documentation/gitcli.txt
+++ b/Documentation/gitcli.txt
@@ -25,22 +25,22 @@
    are paths.
 
  * When an argument can be misunderstood as either a revision or a path,
-   they can be disambiguated by placing `\--` between them.
-   E.g. `git diff \-- HEAD` is, "I have a file called HEAD in my work
+   they can be disambiguated by placing `--` between them.
+   E.g. `git diff -- HEAD` is, "I have a file called HEAD in my work
    tree.  Please show changes between the version I staged in the index
    and what I have in the work tree for that file". not "show difference
    between the HEAD commit and the work tree as a whole".  You can say
-   `git diff HEAD \--` to ask for the latter.
+   `git diff HEAD --` to ask for the latter.
 
- * Without disambiguating `\--`, git makes a reasonable guess, but errors
+ * Without disambiguating `--`, git makes a reasonable guess, but errors
    out and asking you to disambiguate when ambiguous.  E.g. if you have a
    file called HEAD in your work tree, `git diff HEAD` is ambiguous, and
-   you have to say either `git diff HEAD \--` or `git diff \-- HEAD` to
+   you have to say either `git diff HEAD --` or `git diff -- HEAD` to
    disambiguate.
 
 When writing a script that is expected to handle random user-input, it is
 a good practice to make it explicit which arguments are which by placing
-disambiguating `\--` at appropriate places.
+disambiguating `--` at appropriate places.
 
 Here are the rules regarding the "flags" that you should follow when you are
 scripting git:
@@ -81,7 +81,7 @@
 +
 ---------------------------------------------
 $ git describe -h
-usage: git-describe [options] <committish>*
+usage: git describe [options] <committish>*
 
     --contains            find the tag that comes after the commit
     --debug               debug search strategy on stderr
@@ -169,10 +169,6 @@
 http://marc.info/?l=git&m=119150393620273 for further
 information.
 
-Documentation
--------------
-Documentation by Pierre Habouzit and the git-list <git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/gitcore-tutorial.txt b/Documentation/gitcore-tutorial.txt
index 7ba5e58..9d89336 100644
--- a/Documentation/gitcore-tutorial.txt
+++ b/Documentation/gitcore-tutorial.txt
@@ -12,7 +12,7 @@
 DESCRIPTION
 -----------
 
-This tutorial explains how to use the "core" git programs to set up and
+This tutorial explains how to use the "core" git commands to set up and
 work with a git repository.
 
 If you just need to use git as a revision control system you may prefer
@@ -27,6 +27,14 @@
 plumbing directly very often, but it can be good to know what the
 plumbing does for when the porcelain isn't flushing.
 
+Back when this document was originally written, many porcelain
+commands were shell scripts. For simplicity, it still uses them as
+examples to illustrate how plumbing is fit together to form the
+porcelain commands. The source tree includes some of these scripts in
+contrib/examples/ for reference. Although these are not implemented as
+shell scripts anymore, the description of what the plumbing layer
+commands do is still valid.
+
 [NOTE]
 Deeper technical details are often marked as Notes, which you can
 skip on your first reading.
@@ -44,7 +52,7 @@
 For our first example, we're going to start a totally new repository from
 scratch, with no pre-existing files, and we'll call it 'git-tutorial'.
 To start up, create a subdirectory for it, change into that
-subdirectory, and initialize the git infrastructure with 'git-init':
+subdirectory, and initialize the git infrastructure with 'git init':
 
 ------------------------------------------------
 $ mkdir git-tutorial
@@ -102,7 +110,7 @@
 and a reference to an object is always the 40-byte hex
 representation of that SHA1 name. The files in the `refs`
 subdirectory are expected to contain these hex references
-(usually with a final `\'\n\'` at the end), and you should thus
+(usually with a final `\n` at the end), and you should thus
 expect to see a number of 41-byte files containing these
 references in these `refs` subdirectories when you actually start
 populating your tree.
@@ -139,12 +147,12 @@
  - commit that index file as an object.
 
 The first step is trivial: when you want to tell git about any changes
-to your working tree, you use the 'git-update-index' program. That
+to your working tree, you use the 'git update-index' program. That
 program normally just takes a list of filenames you want to update, but
 to avoid trivial mistakes, it refuses to add new entries to the index
 (or remove existing ones) unless you explicitly tell it that you're
-adding a new entry with the `\--add` flag (or removing an entry with the
-`\--remove`) flag.
+adding a new entry with the `--add` flag (or removing an entry with the
+`--remove`) flag.
 
 So to populate the index with the two files you just created, you can do
 
@@ -173,19 +181,19 @@
 which correspond with the objects with names of `557db...` and
 `f24c7...` respectively.
 
-If you want to, you can use 'git-cat-file' to look at those objects, but
+If you want to, you can use 'git cat-file' to look at those objects, but
 you'll have to use the object name, not the filename of the object:
 
 ----------------
 $ git cat-file -t 557db03de997c86a4a028e1ebd3a1ceb225be238
 ----------------
 
-where the `-t` tells 'git-cat-file' to tell you what the "type" of the
+where the `-t` tells 'git cat-file' to tell you what the "type" of the
 object is. git will tell you that you have a "blob" object (i.e., just a
 regular file), and you can see the contents with
 
 ----------------
-$ git cat-file "blob" 557db03
+$ git cat-file blob 557db03
 ----------------
 
 which will print out "Hello World". The object `557db03` is nothing
@@ -205,7 +213,7 @@
 Anyway, as we mentioned previously, you normally never actually take a
 look at the objects themselves, and typing long 40-character hex
 names is not something you'd normally want to do. The above digression
-was just to show that 'git-update-index' did something magical, and
+was just to show that 'git update-index' did something magical, and
 actually saved away the contents of your files into the git object
 database.
 
@@ -228,7 +236,7 @@
 
 and you can now, since you told git about the previous state of `hello`, ask
 git what has changed in the tree compared to your old index, using the
-'git-diff-files' command:
+'git diff-files' command:
 
 ------------
 $ git diff-files
@@ -239,7 +247,7 @@
 that it has noticed that "hello" has been modified, and that the old object
 contents it had have been replaced with something else.
 
-To make it readable, we can tell 'git-diff-files' to output the
+To make it readable, we can tell 'git diff-files' to output the
 differences as a patch, using the `-p` flag:
 
 ------------
@@ -255,7 +263,7 @@
 
 i.e. the diff of the change we caused by adding another line to `hello`.
 
-In other words, 'git-diff-files' always shows us the difference between
+In other words, 'git diff-files' always shows us the difference between
 what is recorded in the index, and what is currently in the working
 tree. That's very useful.
 
@@ -283,7 +291,7 @@
 object as a 'commit' object together with an explanation of what the
 tree was all about, along with information of how we came to that state.
 
-Creating a tree object is trivial, and is done with 'git-write-tree'.
+Creating a tree object is trivial, and is done with 'git write-tree'.
 There are no options or other input: `git write-tree` will take the
 current index state, and write an object that describes that whole
 index. In other words, we're now tying together all the different
@@ -302,28 +310,28 @@
 ----------------
 
 which is another incomprehensible object name. Again, if you want to,
-you can use `git cat-file -t 8988d\...` to see that this time the object
+you can use `git cat-file -t 8988d...` to see that this time the object
 is not a "blob" object, but a "tree" object (you can also use
 `git cat-file` to actually output the raw object contents, but you'll see
 mainly a binary mess, so that's less interesting).
 
-However -- normally you'd never use 'git-write-tree' on its own, because
+However -- normally you'd never use 'git write-tree' on its own, because
 normally you always commit a tree into a commit object using the
-'git-commit-tree' command. In fact, it's easier to not actually use
-'git-write-tree' on its own at all, but to just pass its result in as an
-argument to 'git-commit-tree'.
+'git commit-tree' command. In fact, it's easier to not actually use
+'git write-tree' on its own at all, but to just pass its result in as an
+argument to 'git commit-tree'.
 
-'git-commit-tree' normally takes several arguments -- it wants to know
+'git commit-tree' normally takes several arguments -- it wants to know
 what the 'parent' of a commit was, but since this is the first commit
 ever in this new repository, and it has no parents, we only need to pass in
-the object name of the tree. However, 'git-commit-tree' also wants to get a
+the object name of the tree. However, 'git commit-tree' also wants to get a
 commit message on its standard input, and it will write out the resulting
 object name for the commit to its standard output.
 
 And this is where we create the `.git/refs/heads/master` file
 which is pointed at by `HEAD`. This file is supposed to contain
 the reference to the top-of-tree of the master branch, and since
-that's exactly what 'git-commit-tree' spits out, we can do this
+that's exactly what 'git commit-tree' spits out, we can do this
 all with a sequence of simple shell commands:
 
 ------------------------------------------------
@@ -345,11 +353,11 @@
 Making a change
 ---------------
 
-Remember how we did the 'git-update-index' on file `hello` and then we
+Remember how we did the 'git update-index' on file `hello` and then we
 changed `hello` afterward, and could compare the new state of `hello` with the
 state we saved in the index file?
 
-Further, remember how I said that 'git-write-tree' writes the contents
+Further, remember how I said that 'git write-tree' writes the contents
 of the *index* file to the tree, and thus what we just committed was in
 fact the *original* contents of the file `hello`, not the new ones. We did
 that on purpose, to show the difference between the index state, and the
@@ -360,12 +368,12 @@
 we'll still see the same difference we saw last time: the index file
 hasn't changed by the act of committing anything. However, now that we
 have committed something, we can also learn to use a new command:
-'git-diff-index'.
+'git diff-index'.
 
-Unlike 'git-diff-files', which showed the difference between the index
-file and the working tree, 'git-diff-index' shows the differences
+Unlike 'git diff-files', which showed the difference between the index
+file and the working tree, 'git diff-index' shows the differences
 between a committed *tree* and either the index file or the working
-tree. In other words, 'git-diff-index' wants a tree to be diffed
+tree. In other words, 'git diff-index' wants a tree to be diffed
 against, and before we did the commit, we couldn't do that, because we
 didn't have anything to diff against.
 
@@ -375,7 +383,7 @@
 $ git diff-index -p HEAD
 ----------------
 
-(where `-p` has the same meaning as it did in 'git-diff-files'), and it
+(where `-p` has the same meaning as it did in 'git diff-files'), and it
 will show us the same difference, but for a totally different reason.
 Now we're comparing the working tree not against the index file,
 but against the tree we just wrote. It just so happens that those two
@@ -390,20 +398,20 @@
 
 which ends up doing the above for you.
 
-In other words, 'git-diff-index' normally compares a tree against the
-working tree, but when given the `\--cached` flag, it is told to
+In other words, 'git diff-index' normally compares a tree against the
+working tree, but when given the `--cached` flag, it is told to
 instead compare against just the index cache contents, and ignore the
 current working tree state entirely. Since we just wrote the index
-file to HEAD, doing `git diff-index \--cached -p HEAD` should thus return
+file to HEAD, doing `git diff-index --cached -p HEAD` should thus return
 an empty set of differences, and that's exactly what it does.
 
 [NOTE]
 ================
-'git-diff-index' really always uses the index for its
+'git diff-index' really always uses the index for its
 comparisons, and saying that it compares a tree against the working
 tree is thus not strictly accurate. In particular, the list of
 files to compare (the "meta-data") *always* comes from the index file,
-regardless of whether the `\--cached` flag is used or not. The `\--cached`
+regardless of whether the `--cached` flag is used or not. The `--cached`
 flag really only determines whether the file *contents* to be compared
 come from the working tree or not.
 
@@ -425,14 +433,14 @@
 $ git update-index hello
 ------------------------------------------------
 
-(note how we didn't need the `\--add` flag this time, since git knew
+(note how we didn't need the `--add` flag this time, since git knew
 about the file already).
 
-Note what happens to the different 'git-diff-\*' versions here. After
-we've updated `hello` in the index, `git diff-files -p` now shows no
+Note what happens to the different 'git diff-{asterisk}' versions here.
+After we've updated `hello` in the index, `git diff-files -p` now shows no
 differences, but `git diff-index -p HEAD` still *does* show that the
 current state is different from the state we committed. In fact, now
-'git-diff-index' shows the same difference whether we use the `--cached`
+'git diff-index' shows the same difference whether we use the `--cached`
 flag or not, since now the index is coherent with the working tree.
 
 Now, since we've updated `hello` in the index, we can commit the new
@@ -460,7 +468,7 @@
 looking at what `git commit` really does, feel free to investigate:
 it's a few very simple shell scripts to generate the helpful (?) commit
 message headers, and a few one-liners that actually do the
-commit itself ('git-commit').
+commit itself ('git commit').
 
 
 Inspecting Changes
@@ -468,9 +476,9 @@
 
 While creating changes is useful, it's even more useful if you can tell
 later what changed. The most useful command for this is another of the
-'diff' family, namely 'git-diff-tree'.
+'diff' family, namely 'git diff-tree'.
 
-'git-diff-tree' can be given two arbitrary trees, and it will tell you the
+'git diff-tree' can be given two arbitrary trees, and it will tell you the
 differences between them. Perhaps even more commonly, though, you can
 give it just a single commit object, and it will figure out the parent
 of that commit itself, and show the difference directly. Thus, to get
@@ -486,7 +494,7 @@
 [NOTE]
 ============
 Here is an ASCII art by Jon Loeliger that illustrates how
-various diff-\* commands compare things.
+various 'diff-{asterisk}' commands compare things.
 
                       diff-tree
                        +----+
@@ -518,15 +526,15 @@
                     +-----------+
 ============
 
-More interestingly, you can also give 'git-diff-tree' the `--pretty` flag,
+More interestingly, you can also give 'git diff-tree' the `--pretty` flag,
 which tells it to also show the commit message and author and date of the
 commit, and you can tell it to show a whole series of diffs.
 Alternatively, you can tell it to be "silent", and not show the diffs at
 all, but just show the actual commit message.
 
-In fact, together with the 'git-rev-list' program (which generates a
-list of revisions), 'git-diff-tree' ends up being a veritable fount of
-changes. A trivial (but very useful) script called 'git-whatchanged' is
+In fact, together with the 'git rev-list' program (which generates a
+list of revisions), 'git diff-tree' ends up being a veritable fount of
+changes. A trivial (but very useful) script called 'git whatchanged' is
 included with git which does exactly this, and shows a log of recent
 activities.
 
@@ -552,15 +560,15 @@
 When using the above two commands, the initial commit will be shown.
 If this is a problem because it is huge, you can hide it by setting
 the log.showroot configuration variable to false. Having this, you
-can still show it for each command just adding the `\--root` option,
-which is a flag for 'git-diff-tree' accepted by both commands.
+can still show it for each command just adding the `--root` option,
+which is a flag for 'git diff-tree' accepted by both commands.
 
 With that, you should now be having some inkling of what git does, and
 can explore on your own.
 
 [NOTE]
 Most likely, you are not directly using the core
-git Plumbing commands, but using Porcelain such as 'git-add', `git-rm'
+git Plumbing commands, but using Porcelain such as 'git add', `git-rm'
 and `git-commit'.
 
 
@@ -595,14 +603,14 @@
 message, along with optionally a PGP signature that says that yes,
 you really did
 that tag. You create these annotated tags with either the `-a` or
-`-s` flag to 'git-tag':
+`-s` flag to 'git tag':
 
 ----------------
 $ git tag -s <tagname>
 ----------------
 
 which will sign the current `HEAD` (but you can also give it another
-argument that specifies the thing to tag, i.e., you could have tagged the
+argument that specifies the thing to tag, e.g., you could have tagged the
 current `mybranch` point by using `git tag <tagname> mybranch`).
 
 You normally only do signed tags for major releases or things
@@ -642,7 +650,7 @@
 history outside the project you created.
 
  - if you want to move or duplicate a git repository, you can do so. There
-   is 'git-clone' command, but if all you want to do is just to
+   is 'git clone' command, but if all you want to do is just to
    create a copy of your repository (with all the full history that
    went along with it), you can do so with a regular
    `cp -a git-tutorial new-git-tutorial`.
@@ -666,7 +674,7 @@
 index cache when you do this, and especially with other peoples'
 repositories you often want to make sure that the index cache is in some
 known state (you don't know *what* they've done and not yet checked in),
-so usually you'll precede the 'git-update-index' with a
+so usually you'll precede the 'git update-index' with a
 
 ----------------
 $ git read-tree --reset HEAD
@@ -674,7 +682,7 @@
 ----------------
 
 which will force a total index re-build from the tree pointed to by `HEAD`.
-It resets the index contents to `HEAD`, and then the 'git-update-index'
+It resets the index contents to `HEAD`, and then the 'git update-index'
 makes sure to match up all index entries with the checked-out files.
 If the original repository had uncommitted changes in its
 working tree, `git update-index --refresh` notices them and
@@ -689,8 +697,8 @@
 and in fact a lot of the common git command combinations can be scripted
 with the `git xyz` interfaces.  You can learn things by just looking
 at what the various git scripts do.  For example, `git reset` used to be
-the above two lines implemented in 'git-reset', but some things like
-'git-status' and 'git-commit' are slightly more complex scripts around
+the above two lines implemented in 'git reset', but some things like
+'git status' and 'git commit' are slightly more complex scripts around
 the basic git commands.
 
 Many (most?) public remote repositories will not contain any of
@@ -729,7 +737,7 @@
 up-to-date (so that you don't have to refresh it afterward), and the
 `-a` flag means "check out all files" (if you have a stale copy or an
 older version of a checked out tree you may also need to add the `-f`
-flag first, to tell 'git-checkout-index' to *force* overwriting of any old
+flag first, to tell 'git checkout-index' to *force* overwriting of any old
 files).
 
 Again, this can all be simplified with
@@ -776,7 +784,7 @@
 ================================================
 If you make the decision to start your new branch at some
 other point in the history than the current `HEAD`, you can do so by
-just telling 'git-checkout' what the base of the checkout would be.
+just telling 'git checkout' what the base of the checkout would be.
 In other words, if you have an earlier tag or branch, you'd just do
 
 ------------
@@ -819,7 +827,7 @@
 
 which will simply _create_ the branch, but will not do anything further.
 You can then later -- once you decide that you want to actually develop
-on that branch -- switch to that branch with a regular 'git-checkout'
+on that branch -- switch to that branch with a regular 'git checkout'
 with the branchname as the argument.
 
 
@@ -873,7 +881,7 @@
 $ gitk --all
 ----------------
 
-will show you graphically both of your branches (that's what the `\--all`
+will show you graphically both of your branches (that's what the `--all`
 means: normally it will just show you your current `HEAD`) and their
 histories. You can also see exactly how they came to be from a common
 source.
@@ -881,7 +889,7 @@
 Anyway, let's exit 'gitk' (`^Q` or the File menu), and decide that we want
 to merge the work we did on the `mybranch` branch into the `master`
 branch (which is currently our `HEAD` too). To do that, there's a nice
-script called 'git-merge', which wants to know which branches you want
+script called 'git merge', which wants to know which branches you want
 to resolve and what the merge is all about:
 
 ------------
@@ -925,9 +933,9 @@
 
 which will very loudly warn you that you're now committing a merge
 (which is correct, so never mind), and you can write a small merge
-message about your adventures in 'git-merge'-land.
+message about your adventures in 'git merge'-land.
 
-After you're done, start up `gitk \--all` to see graphically what the
+After you're done, start up `gitk --all` to see graphically what the
 history looks like. Notice that `mybranch` still exists, and you can
 switch to it, and continue to work with it if you want to. The
 `mybranch` branch will not contain the merge, but next time you merge it
@@ -950,7 +958,7 @@
 The first two lines indicate that it is showing the two branches
 and the first line of the commit log message from their
 top-of-the-tree commits, you are currently on `master` branch
-(notice the asterisk `\*` character), and the first column for
+(notice the asterisk `*` character), and the first column for
 the later output lines is used to show commits contained in the
 `master` branch, and the second column for the `mybranch`
 branch. Three commits are shown along with their log messages.
@@ -963,25 +971,25 @@
 before the commit log message is a short name you can use to
 name the commit.  In the above example, 'master' and 'mybranch'
 are branch heads.  'master^' is the first parent of 'master'
-branch head.  Please see linkgit:git-rev-parse[1] if you want to
+branch head.  Please see linkgit:gitrevisions[7] if you want to
 see more complex cases.
 
 [NOTE]
-Without the '--more=1' option, 'git-show-branch' would not output the
+Without the '--more=1' option, 'git show-branch' would not output the
 '[master^]' commit, as '[mybranch]' commit is a common ancestor of
 both 'master' and 'mybranch' tips.  Please see linkgit:git-show-branch[1]
 for details.
 
 [NOTE]
 If there were more commits on the 'master' branch after the merge, the
-merge commit itself would not be shown by 'git-show-branch' by
+merge commit itself would not be shown by 'git show-branch' by
 default.  You would need to provide '--sparse' option to make the
 merge commit visible in this case.
 
 Now, let's pretend you are the one who did all the work in
 `mybranch`, and the fruit of your hard work has finally been merged
 to the `master` branch. Let's go back to `mybranch`, and run
-'git-merge' to get the "upstream changes" back to your branch.
+'git merge' to get the "upstream changes" back to your branch.
 
 ------------
 $ git checkout mybranch
@@ -993,19 +1001,19 @@
 
 ----------------
 Updating from ae3a2da... to a80b4aa....
-Fast forward (no commit created; -m option ignored)
- example |    1 +
- hello   |    1 +
- 2 files changed, 2 insertions(+), 0 deletions(-)
+Fast-forward (no commit created; -m option ignored)
+ example | 1 +
+ hello   | 1 +
+ 2 files changed, 2 insertions(+)
 ----------------
 
 Because your branch did not contain anything more than what had
 already been merged into the `master` branch, the merge operation did
 not actually do a merge. Instead, it just updated the top of
 the tree of your branch to that of the `master` branch. This is
-often called 'fast forward' merge.
+often called 'fast-forward' merge.
 
-You can run `gitk \--all` again to see how the commit ancestry
+You can run `gitk --all` again to see how the commit ancestry
 looks like, or run 'show-branch', which tells you this.
 
 ------------------------------------------------
@@ -1023,12 +1031,12 @@
 It's usually much more common that you merge with somebody else than
 merging with your own branches, so it's worth pointing out that git
 makes that very easy too, and in fact, it's not that different from
-doing a 'git-merge'. In fact, a remote merge ends up being nothing
+doing a 'git merge'. In fact, a remote merge ends up being nothing
 more than "fetch the work from a remote repository into a temporary tag"
-followed by a 'git-merge'.
+followed by a 'git merge'.
 
 Fetching from a remote repository is done by, unsurprisingly,
-'git-fetch':
+'git fetch':
 
 ----------------
 $ git fetch <remote-repository>
@@ -1084,7 +1092,7 @@
 first obtains the topmost commit object name from the remote site
 by looking at the specified refname under `repo.git/refs/` directory,
 and then tries to obtain the
-commit object by downloading from `repo.git/objects/xx/xxx\...`
+commit object by downloading from `repo.git/objects/xx/xxx...`
 using the object name of that commit object.  Then it reads the
 commit object to find out its parent commits and the associate
 tree object; it repeats this process until it gets all the
@@ -1095,7 +1103,7 @@
 transports', because they do not require any git aware smart
 server like git Native transport does.  Any stock HTTP server
 that does not even support directory index would suffice.  But
-you must prepare your repository with 'git-update-server-info'
+you must prepare your repository with 'git update-server-info'
 to help dumb transport downloaders.
 
 Once you fetch from the remote repository, you `merge` that
@@ -1115,7 +1123,7 @@
 [NOTE]
 You could do without using any branches at all, by
 keeping as many local repositories as you would like to have
-branches, and merging between them with 'git-pull', just like
+branches, and merging between them with 'git pull', just like
 you merge between branches. The advantage of this approach is
 that it lets you keep a set of files for each `branch` checked
 out and you may find it easier to switch back and forth if you
@@ -1132,7 +1140,7 @@
 $ git config remote.linus.url http://www.kernel.org/pub/scm/git/git.git/
 ------------------------------------------------
 
-and use the "linus" keyword with 'git-pull' instead of the full URL.
+and use the "linus" keyword with 'git pull' instead of the full URL.
 
 Examples.
 
@@ -1168,7 +1176,7 @@
 +* [master^] Some fun.
 ------------
 
-Remember, before running 'git-merge', our `master` head was at
+Remember, before running 'git merge', our `master` head was at
 "Some fun." commit, while our `mybranch` head was at "Some
 work." commit.
 
@@ -1186,16 +1194,16 @@
 * [master] Some fun.
  ! [mybranch] Some work.
 --
- + [mybranch] Some work.
 *  [master] Some fun.
-*+ [mybranch^] New day.
+ + [mybranch] Some work.
+*+ [master^] Initial commit
 ------------
 
 Now we are ready to experiment with the merge by hand.
 
 `git merge` command, when merging two branches, uses 3-way merge
 algorithm.  First, it finds the common ancestor between them.
-The command it uses is 'git-merge-base':
+The command it uses is 'git merge-base':
 
 ------------
 $ mb=$(git merge-base HEAD mybranch)
@@ -1204,11 +1212,11 @@
 The command writes the commit object name of the common ancestor
 to the standard output, so we captured its output to a variable,
 because we will be using it in the next step.  By the way, the common
-ancestor commit is the "New day." commit in this case.  You can
+ancestor commit is the "Initial commit" commit in this case.  You can
 tell it by:
 
 ------------
-$ git name-rev $mb
+$ git name-rev --name-only --tags $mb
 my-first-tag
 ------------
 
@@ -1219,7 +1227,7 @@
 $ git read-tree -m -u $mb HEAD mybranch
 ------------
 
-This is the same 'git-read-tree' command we have already seen,
+This is the same 'git read-tree' command we have already seen,
 but it takes three trees, unlike previous examples.  This reads
 the contents of each tree into different 'stage' in the index
 file (the first tree goes to stage 1, the second to stage 2,
@@ -1237,8 +1245,8 @@
 ------------
 $ git ls-files --stage
 100644 7f8b141b65fdcee47321e399a2598a235a032422 0	example
-100644 263414f423d0e4d70dae8fe53fa34614ff3e2860 1	hello
-100644 06fa6a24256dc7e560efa5687fa84b51f0263c3a 2	hello
+100644 557db03de997c86a4a028e1ebd3a1ceb225be238 1	hello
+100644 ba42a2a96e3027f3333e13ede4ccf4498c3ae942 2	hello
 100644 cc44c73eb783565da5831b4d820c962954019b69 3	hello
 ------------
 
@@ -1249,19 +1257,19 @@
 fairly quickly, leaving only a handful of real changes in non-zero
 stages.
 
-To look at only non-zero stages, use `\--unmerged` flag:
+To look at only non-zero stages, use `--unmerged` flag:
 
 ------------
 $ git ls-files --unmerged
-100644 263414f423d0e4d70dae8fe53fa34614ff3e2860 1	hello
-100644 06fa6a24256dc7e560efa5687fa84b51f0263c3a 2	hello
+100644 557db03de997c86a4a028e1ebd3a1ceb225be238 1	hello
+100644 ba42a2a96e3027f3333e13ede4ccf4498c3ae942 2	hello
 100644 cc44c73eb783565da5831b4d820c962954019b69 3	hello
 ------------
 
 The next step of merging is to merge these three versions of the
 file, using 3-way merge.  This is done by giving
-'git-merge-one-file' command as one of the arguments to
-'git-merge-index' command:
+'git merge-one-file' command as one of the arguments to
+'git merge-index' command:
 
 ------------
 $ git merge-index git-merge-one-file hello
@@ -1270,7 +1278,7 @@
 fatal: merge program failed
 ------------
 
-'git-merge-one-file' script is called with parameters to
+'git merge-one-file' script is called with parameters to
 describe those three versions, and is responsible to leave the
 merge results in the working tree.
 It is a fairly straightforward shell script, and
@@ -1283,15 +1291,15 @@
 ------------
 $ git ls-files --stage
 100644 7f8b141b65fdcee47321e399a2598a235a032422 0	example
-100644 263414f423d0e4d70dae8fe53fa34614ff3e2860 1	hello
-100644 06fa6a24256dc7e560efa5687fa84b51f0263c3a 2	hello
+100644 557db03de997c86a4a028e1ebd3a1ceb225be238 1	hello
+100644 ba42a2a96e3027f3333e13ede4ccf4498c3ae942 2	hello
 100644 cc44c73eb783565da5831b4d820c962954019b69 3	hello
 ------------
 
 This is the state of the index file and the working file after
-'git-merge' returns control back to you, leaving the conflicting
+'git merge' returns control back to you, leaving the conflicting
 merge for you to resolve.  Notice that the path `hello` is still
-unmerged, and what you see with 'git-diff' at this point is
+unmerged, and what you see with 'git diff' at this point is
 differences since stage 2 (i.e. your version).
 
 
@@ -1328,8 +1336,8 @@
 done only once.
 
 [NOTE]
-'git-push' uses a pair of programs,
-'git-send-pack' on your local machine, and 'git-receive-pack'
+'git push' uses a pair of commands,
+'git send-pack' on your local machine, and 'git-receive-pack'
 on the remote machine. The communication between the two over
 the network internally uses an SSH connection.
 
@@ -1344,7 +1352,7 @@
 ------------
 
 Then, make that directory into a git repository by running
-'git-init', but this time, since its name is not the usual
+'git init', but this time, since its name is not the usual
 `.git`, we do things slightly differently:
 
 ------------
@@ -1407,12 +1415,12 @@
 
 will do it for you. If you followed the tutorial examples, you
 would have accumulated about 17 objects in `.git/objects/??/`
-directories by now. 'git-repack' tells you how many objects it
+directories by now. 'git repack' tells you how many objects it
 packed, and stores the packed file in `.git/objects/pack`
 directory.
 
 [NOTE]
-You will see two files, `pack-\*.pack` and `pack-\*.idx`,
+You will see two files, `pack-*.pack` and `pack-*.idx`,
 in `.git/objects/pack` directory. They are closely related to
 each other, and if you ever copy them by hand to a different
 repository for whatever reason, you should make sure you copy
@@ -1420,7 +1428,7 @@
 in the pack, and the latter holds the index for random
 access.
 
-If you are paranoid, running 'git-verify-pack' command would
+If you are paranoid, running 'git verify-pack' command would
 detect if you have a corrupt pack, but do not worry too much.
 Our programs are always perfect ;-).
 
@@ -1487,17 +1495,17 @@
 transport protocols (HTTP), you need to keep this repository
 'dumb transport friendly'.  After `git init`,
 `$GIT_DIR/hooks/post-update.sample` copied from the standard templates
-would contain a call to 'git-update-server-info'
+would contain a call to 'git update-server-info'
 but you need to manually enable the hook with
 `mv post-update.sample post-update`.  This makes sure
-'git-update-server-info' keeps the necessary files up-to-date.
+'git update-server-info' keeps the necessary files up-to-date.
 
 3. Push into the public repository from your primary
    repository.
 
-4. 'git-repack' the public repository. This establishes a big
+4. 'git repack' the public repository. This establishes a big
    pack that contains the initial set of objects as the
-   baseline, and possibly 'git-prune' if the transport
+   baseline, and possibly 'git prune' if the transport
    used for pulling from your repository supports packed
    repositories.
 
@@ -1511,14 +1519,14 @@
 6. Push your changes to the public repository, and announce it
    to the public.
 
-7. Every once in a while, 'git-repack' the public repository.
+7. Every once in a while, 'git repack' the public repository.
    Go back to step 5. and continue working.
 
 
 A recommended work cycle for a "subsystem maintainer" who works
 on that project and has an own "public repository" goes like this:
 
-1. Prepare your work repository, by 'git-clone' the public
+1. Prepare your work repository, by 'git clone' the public
    repository of the "project lead". The URL used for the
    initial cloning is stored in the remote.origin.url
    configuration variable.
@@ -1533,7 +1541,7 @@
    point at the repository you are borrowing from.
 
 4. Push into the public repository from your primary
-   repository. Run 'git-repack', and possibly 'git-prune' if the
+   repository. Run 'git repack', and possibly 'git prune' if the
    transport used for pulling from your repository supports
    packed repositories.
 
@@ -1550,7 +1558,7 @@
    "project lead" and possibly your "sub-subsystem
    maintainers" to pull from it.
 
-7. Every once in a while, 'git-repack' the public repository.
+7. Every once in a while, 'git repack' the public repository.
    Go back to step 5. and continue working.
 
 
@@ -1558,7 +1566,7 @@
 not have a "public" repository is somewhat different. It goes
 like this:
 
-1. Prepare your work repository, by 'git-clone' the public
+1. Prepare your work repository, by 'git clone' the public
    repository of the "project lead" (or a "subsystem
    maintainer", if you work on a subsystem). The URL used for
    the initial cloning is stored in the remote.origin.url
@@ -1656,8 +1664,8 @@
 ------------
 
 You can make sure `git show-branch` matches the state before
-those two 'git-merge' you just did.  Then, instead of running
-two 'git-merge' commands in a row, you would merge these two
+those two 'git merge' you just did.  Then, instead of running
+two 'git merge' commands in a row, you would merge these two
 branch heads (this is known as 'making an Octopus'):
 
 ------------
diff --git a/Documentation/gitcredentials.txt b/Documentation/gitcredentials.txt
new file mode 100644
index 0000000..7dfffc0
--- /dev/null
+++ b/Documentation/gitcredentials.txt
@@ -0,0 +1,183 @@
+gitcredentials(7)
+=================
+
+NAME
+----
+gitcredentials - providing usernames and passwords to git
+
+SYNOPSIS
+--------
+------------------
+git config credential.https://example.com.username myusername
+git config credential.helper "$helper $options"
+------------------
+
+DESCRIPTION
+-----------
+
+Git will sometimes need credentials from the user in order to perform
+operations; for example, it may need to ask for a username and password
+in order to access a remote repository over HTTP. This manual describes
+the mechanisms git uses to request these credentials, as well as some
+features to avoid inputting these credentials repeatedly.
+
+REQUESTING CREDENTIALS
+----------------------
+
+Without any credential helpers defined, git will try the following
+strategies to ask the user for usernames and passwords:
+
+1. If the `GIT_ASKPASS` environment variable is set, the program
+   specified by the variable is invoked. A suitable prompt is provided
+   to the program on the command line, and the user's input is read
+   from its standard output.
+
+2. Otherwise, if the `core.askpass` configuration variable is set, its
+   value is used as above.
+
+3. Otherwise, if the `SSH_ASKPASS` environment variable is set, its
+   value is used as above.
+
+4. Otherwise, the user is prompted on the terminal.
+
+AVOIDING REPETITION
+-------------------
+
+It can be cumbersome to input the same credentials over and over.  Git
+provides two methods to reduce this annoyance:
+
+1. Static configuration of usernames for a given authentication context.
+
+2. Credential helpers to cache or store passwords, or to interact with
+   a system password wallet or keychain.
+
+The first is simple and appropriate if you do not have secure storage available
+for a password. It is generally configured by adding this to your config:
+
+---------------------------------------
+[credential "https://example.com"]
+	username = me
+---------------------------------------
+
+Credential helpers, on the other hand, are external programs from which git can
+request both usernames and passwords; they typically interface with secure
+storage provided by the OS or other programs.
+
+To use a helper, you must first select one to use. Git currently
+includes the following helpers:
+
+cache::
+
+	Cache credentials in memory for a short period of time. See
+	linkgit:git-credential-cache[1] for details.
+
+store::
+
+	Store credentials indefinitely on disk. See
+	linkgit:git-credential-store[1] for details.
+
+You may also have third-party helpers installed; search for
+`credential-*` in the output of `git help -a`, and consult the
+documentation of individual helpers.  Once you have selected a helper,
+you can tell git to use it by putting its name into the
+credential.helper variable.
+
+1. Find a helper.
++
+-------------------------------------------
+$ git help -a | grep credential-
+credential-foo
+-------------------------------------------
+
+2. Read its description.
++
+-------------------------------------------
+$ git help credential-foo
+-------------------------------------------
+
+3. Tell git to use it.
++
+-------------------------------------------
+$ git config --global credential.helper foo
+-------------------------------------------
+
+If there are multiple instances of the `credential.helper` configuration
+variable, each helper will be tried in turn, and may provide a username,
+password, or nothing. Once git has acquired both a username and a
+password, no more helpers will be tried.
+
+
+CREDENTIAL CONTEXTS
+-------------------
+
+Git considers each credential to have a context defined by a URL. This context
+is used to look up context-specific configuration, and is passed to any
+helpers, which may use it as an index into secure storage.
+
+For instance, imagine we are accessing `https://example.com/foo.git`. When git
+looks into a config file to see if a section matches this context, it will
+consider the two a match if the context is a more-specific subset of the
+pattern in the config file. For example, if you have this in your config file:
+
+--------------------------------------
+[credential "https://example.com"]
+	username = foo
+--------------------------------------
+
+then we will match: both protocols are the same, both hosts are the same, and
+the "pattern" URL does not care about the path component at all. However, this
+context would not match:
+
+--------------------------------------
+[credential "https://kernel.org"]
+	username = foo
+--------------------------------------
+
+because the hostnames differ. Nor would it match `foo.example.com`; git
+compares hostnames exactly, without considering whether two hosts are part of
+the same domain. Likewise, a config entry for `http://example.com` would not
+match: git compares the protocols exactly.
+
+
+CONFIGURATION OPTIONS
+---------------------
+
+Options for a credential context can be configured either in
+`credential.*` (which applies to all credentials), or
+`credential.<url>.*`, where <url> matches the context as described
+above.
+
+The following options are available in either location:
+
+helper::
+
+	The name of an external credential helper, and any associated options.
+	If the helper name is not an absolute path, then the string `git
+	credential-` is prepended. The resulting string is executed by the
+	shell (so, for example, setting this to `foo --option=bar` will execute
+	`git credential-foo --option=bar` via the shell. See the manual of
+	specific helpers for examples of their use.
+
+username::
+
+	A default username, if one is not provided in the URL.
+
+useHttpPath::
+
+	By default, git does not consider the "path" component of an http URL
+	to be worth matching via external helpers. This means that a credential
+	stored for `https://example.com/foo.git` will also be used for
+	`https://example.com/bar.git`. If you do want to distinguish these
+	cases, set this option to `true`.
+
+
+CUSTOM HELPERS
+--------------
+
+You can write your own custom helpers to interface with any system in
+which you keep credentials. See the documentation for git's
+link:technical/api-credentials.html[credentials API] for details.
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/gitcvs-migration.txt b/Documentation/gitcvs-migration.txt
index 0e49c1c..aeb0cdc 100644
--- a/Documentation/gitcvs-migration.txt
+++ b/Documentation/gitcvs-migration.txt
@@ -7,7 +7,8 @@
 
 SYNOPSIS
 --------
-git cvsimport *
+[verse]
+'git cvsimport' *
 
 DESCRIPTION
 -----------
@@ -47,25 +48,25 @@
 [NOTE]
 ================================
 The 'pull' command knows where to get updates from because of certain
-configuration variables that were set by the first 'git-clone'
+configuration variables that were set by the first 'git clone'
 command; see `git config -l` and the linkgit:git-config[1] man
 page for details.
 ================================
 
 You can update the shared repository with your changes by first committing
-your changes, and then using the 'git-push' command:
+your changes, and then using the 'git push' command:
 
 ------------------------------------------------
 $ git push origin master
 ------------------------------------------------
 
 to "push" those commits to the shared repository.  If someone else has
-updated the repository more recently, 'git-push', like 'cvs commit', will
+updated the repository more recently, 'git push', like 'cvs commit', will
 complain, in which case you must pull any changes before attempting the
 push again.
 
-In the 'git-push' command above we specify the name of the remote branch
-to update (`master`).  If we leave that out, 'git-push' tries to update
+In the 'git push' command above we specify the name of the remote branch
+to update (`master`).  If we leave that out, 'git push' tries to update
 any branches in the remote repository that have the same name as a branch
 in the local repository.  So the last 'push' can be done with either of:
 
diff --git a/Documentation/gitdiffcore.txt b/Documentation/gitdiffcore.txt
index e8041bc..daf1782 100644
--- a/Documentation/gitdiffcore.txt
+++ b/Documentation/gitdiffcore.txt
@@ -3,16 +3,17 @@
 
 NAME
 ----
-gitdiffcore - Tweaking diff output (June 2005)
+gitdiffcore - Tweaking diff output
 
 SYNOPSIS
 --------
+[verse]
 'git diff' *
 
 DESCRIPTION
 -----------
 
-The diff commands 'git-diff-index', 'git-diff-files', and 'git-diff-tree'
+The diff commands 'git diff-index', 'git diff-files', and 'git diff-tree'
 can be told to manipulate differences they find in
 unconventional ways before showing 'diff' output.  The manipulation
 is collectively called "diffcore transformation".  This short note
@@ -23,18 +24,18 @@
 The chain of operation
 ----------------------
 
-The 'git-diff-{asterisk}' family works by first comparing two sets of
+The 'git diff-{asterisk}' family works by first comparing two sets of
 files:
 
- - 'git-diff-index' compares contents of a "tree" object and the
+ - 'git diff-index' compares contents of a "tree" object and the
    working directory (when '\--cached' flag is not used) or a
    "tree" object and the index file (when '\--cached' flag is
    used);
 
- - 'git-diff-files' compares contents of the index file and the
+ - 'git diff-files' compares contents of the index file and the
    working directory;
 
- - 'git-diff-tree' compares contents of two "tree" objects;
+ - 'git diff-tree' compares contents of two "tree" objects;
 
 In all of these cases, the commands themselves first optionally limit
 the two sets of files by any pathspecs given on their command-lines,
@@ -74,12 +75,12 @@
 - diffcore-pickaxe
 - diffcore-order
 
-These are applied in sequence.  The set of filepairs 'git-diff-{asterisk}'
+These are applied in sequence.  The set of filepairs 'git diff-{asterisk}'
 commands find are used as the input to diffcore-break, and
 the output from diffcore-break is used as the input to the
 next transformation.  The final result is then passed to the
 output routine and generates either diff-raw format (see Output
-format sections of the manual for 'git-diff-{asterisk}' commands) or
+format sections of the manual for 'git diff-{asterisk}' commands) or
 diff-patch format.
 
 
@@ -87,7 +88,7 @@
 ----------------------------------------------------
 
 The second transformation in the chain is diffcore-break, and is
-controlled by the -B option to the 'git-diff-{asterisk}' commands.  This is
+controlled by the -B option to the 'git diff-{asterisk}' commands.  This is
 used to detect a filepair that represents "complete rewrite" and
 break such filepair into two filepairs that represent delete and
 create.  E.g.  If the input contained this filepair:
@@ -123,7 +124,7 @@
 
 This transformation is used to detect renames and copies, and is
 controlled by the -M option (to detect renames) and the -C option
-(to detect copies as well) to the 'git-diff-{asterisk}' commands.  If the
+(to detect copies as well) to the 'git diff-{asterisk}' commands.  If the
 input contained these filepairs:
 
 ------------------------------------------------
@@ -167,12 +168,12 @@
 number after the "-M" or "-C" option (e.g. "-M8" to tell it to use
 8/10 = 80%).
 
-Note.  When the "-C" option is used with `\--find-copies-harder`
-option, 'git-diff-{asterisk}' commands feed unmodified filepairs to
+Note.  When the "-C" option is used with `--find-copies-harder`
+option, 'git diff-{asterisk}' commands feed unmodified filepairs to
 diffcore mechanism as well as modified ones.  This lets the copy
 detector consider unmodified files as copy source candidates at
-the expense of making it slower.  Without `\--find-copies-harder`,
-'git-diff-{asterisk}' commands can detect copies only if the file that was
+the expense of making it slower.  Without `--find-copies-harder`,
+'git diff-{asterisk}' commands can detect copies only if the file that was
 copied happened to have been modified in the same changeset.
 
 
@@ -223,18 +224,18 @@
 
 This transformation is used to find filepairs that represent
 changes that touch a specified string, and is controlled by the
--S option and the `\--pickaxe-all` option to the 'git-diff-{asterisk}'
+-S option and the `--pickaxe-all` option to the 'git diff-*'
 commands.
 
 When diffcore-pickaxe is in use, it checks if there are
-filepairs whose "original" side has the specified string and
-whose "result" side does not.  Such a filepair represents "the
-string appeared in this changeset".  It also checks for the
+filepairs whose "result" side and whose "origin" side have
+different number of specified string.  Such a filepair represents
+"the string appeared in this changeset".  It also checks for the
 opposite case that loses the specified string.
 
-When `\--pickaxe-all` is not in effect, diffcore-pickaxe leaves
+When `--pickaxe-all` is not in effect, diffcore-pickaxe leaves
 only such filepairs that touch the specified string in its
-output.  When `\--pickaxe-all` is used, diffcore-pickaxe leaves all
+output.  When `--pickaxe-all` is used, diffcore-pickaxe leaves all
 filepairs intact if there is such a filepair, or makes the
 output empty otherwise.  The latter behaviour is designed to
 make reviewing of the changes in the context of the whole
@@ -246,7 +247,7 @@
 
 This is used to reorder the filepairs according to the user's
 (or project's) taste, and is controlled by the -O option to the
-'git-diff-{asterisk}' commands.
+'git diff-{asterisk}' commands.
 
 This takes a text file each of whose lines is a shell glob
 pattern.  Filepairs that match a glob pattern on an earlier line
diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
index 1c73673..b9003fe 100644
--- a/Documentation/githooks.txt
+++ b/Documentation/githooks.txt
@@ -15,7 +15,7 @@
 
 Hooks are little scripts you can place in `$GIT_DIR/hooks`
 directory to trigger action at certain points.  When
-'git-init' is run, a handful of example hooks are copied into the
+'git init' is run, a handful of example hooks are copied into the
 `hooks` directory of the new repository, but by default they are
 all disabled.  To enable a hook, rename it by removing its `.sample`
 suffix.
@@ -26,13 +26,16 @@
 
 This document describes the currently defined hooks.
 
-applypatch-msg
---------------
+HOOKS
+-----
 
-This hook is invoked by 'git-am' script.  It takes a single
+applypatch-msg
+~~~~~~~~~~~~~~
+
+This hook is invoked by 'git am' script.  It takes a single
 parameter, the name of the file that holds the proposed commit
 log message.  Exiting with non-zero status causes
-'git-am' to abort before applying the patch.
+'git am' to abort before applying the patch.
 
 The hook is allowed to edit the message file in place, and can
 be used to normalize the message into some project standard
@@ -43,9 +46,9 @@
 'commit-msg' hook, if the latter is enabled.
 
 pre-applypatch
---------------
+~~~~~~~~~~~~~~
 
-This hook is invoked by 'git-am'.  It takes no parameter, and is
+This hook is invoked by 'git am'.  It takes no parameter, and is
 invoked after the patch is applied, but before a commit is made.
 
 If it exits with non-zero status, then the working tree will not be
@@ -58,35 +61,35 @@
 'pre-commit' hook, if the latter is enabled.
 
 post-applypatch
----------------
+~~~~~~~~~~~~~~~
 
-This hook is invoked by 'git-am'.  It takes no parameter,
+This hook is invoked by 'git am'.  It takes no parameter,
 and is invoked after the patch is applied and a commit is made.
 
 This hook is meant primarily for notification, and cannot affect
-the outcome of 'git-am'.
+the outcome of 'git am'.
 
 pre-commit
-----------
+~~~~~~~~~~
 
-This hook is invoked by 'git-commit', and can be bypassed
-with `\--no-verify` option.  It takes no parameter, and is
+This hook is invoked by 'git commit', and can be bypassed
+with `--no-verify` option.  It takes no parameter, and is
 invoked before obtaining the proposed commit log message and
 making a commit.  Exiting with non-zero status from this script
-causes the 'git-commit' to abort.
+causes the 'git commit' to abort.
 
 The default 'pre-commit' hook, when enabled, catches introduction
 of lines with trailing whitespaces and aborts the commit when
 such a line is found.
 
-All the 'git-commit' hooks are invoked with the environment
+All the 'git commit' hooks are invoked with the environment
 variable `GIT_EDITOR=:` if the command will not bring up an editor
 to modify the commit message.
 
 prepare-commit-msg
-------------------
+~~~~~~~~~~~~~~~~~~
 
-This hook is invoked by 'git-commit' right after preparing the
+This hook is invoked by 'git commit' right after preparing the
 default log message, and before the editor is started.
 
 It takes one to three parameters.  The first is the name of the file
@@ -96,12 +99,12 @@
 configuration option `commit.template` is set); `merge` (if the
 commit is a merge or a `.git/MERGE_MSG` file exists); `squash`
 (if a `.git/SQUASH_MSG` file exists); or `commit`, followed by
-a commit SHA1 (if a `-c`, `-C` or `\--amend` option was given).
+a commit SHA1 (if a `-c`, `-C` or `--amend` option was given).
 
-If the exit status is non-zero, 'git-commit' will abort.
+If the exit status is non-zero, 'git commit' will abort.
 
 The purpose of the hook is to edit the message file in place, and
-it is not suppressed by the `\--no-verify` option.  A non-zero exit
+it is not suppressed by the `--no-verify` option.  A non-zero exit
 means a failure of the hook and aborts the commit.  It should not
 be used as replacement for pre-commit hook.
 
@@ -109,12 +112,12 @@
 out the `Conflicts:` part of a merge's commit message.
 
 commit-msg
-----------
+~~~~~~~~~~
 
-This hook is invoked by 'git-commit', and can be bypassed
-with `\--no-verify` option.  It takes a single parameter, the
+This hook is invoked by 'git commit', and can be bypassed
+with `--no-verify` option.  It takes a single parameter, the
 name of the file that holds the proposed commit log message.
-Exiting with non-zero status causes the 'git-commit' to
+Exiting with non-zero status causes the 'git commit' to
 abort.
 
 The hook is allowed to edit the message file in place, and can
@@ -126,32 +129,32 @@
 "Signed-off-by" lines, and aborts the commit if one is found.
 
 post-commit
------------
+~~~~~~~~~~~
 
-This hook is invoked by 'git-commit'.  It takes no
+This hook is invoked by 'git commit'.  It takes no
 parameter, and is invoked after a commit is made.
 
 This hook is meant primarily for notification, and cannot affect
-the outcome of 'git-commit'.
+the outcome of 'git commit'.
 
 pre-rebase
-----------
+~~~~~~~~~~
 
-This hook is called by 'git-rebase' and can be used to prevent a branch
+This hook is called by 'git rebase' and can be used to prevent a branch
 from getting rebased.
 
 
 post-checkout
------------
+~~~~~~~~~~~~~
 
-This hook is invoked when a 'git-checkout' is run after having updated the
+This hook is invoked when a 'git checkout' is run after having updated the
 worktree.  The hook is given three parameters: the ref of the previous HEAD,
 the ref of the new HEAD (which may or may not have changed), and a flag
 indicating whether the checkout was a branch checkout (changing branches,
 flag=1) or a file checkout (retrieving a file from the index, flag=0).
-This hook cannot affect the outcome of 'git-checkout'.
+This hook cannot affect the outcome of 'git checkout'.
 
-It is also run after 'git-clone', unless the --no-checkout (-n) option is
+It is also run after 'git clone', unless the --no-checkout (-n) option is
 used. The first parameter given to the hook is the null-ref, the second the
 ref of the new HEAD and the flag is always 1.
 
@@ -160,12 +163,12 @@
 properties.
 
 post-merge
------------
+~~~~~~~~~~
 
-This hook is invoked by 'git-merge', which happens when a 'git-pull'
+This hook is invoked by 'git merge', which happens when a 'git pull'
 is done on a local repository.  The hook takes a single parameter, a status
 flag specifying whether or not the merge being done was a squash merge.
-This hook cannot affect the outcome of 'git-merge' and is not executed,
+This hook cannot affect the outcome of 'git merge' and is not executed,
 if the merge failed due to conflicts.
 
 This hook can be used in conjunction with a corresponding pre-commit hook to
@@ -175,10 +178,10 @@
 
 [[pre-receive]]
 pre-receive
------------
+~~~~~~~~~~~
 
 This hook is invoked by 'git-receive-pack' on the remote repository,
-which happens when a 'git-push' is done on a local repository.
+which happens when a 'git push' is done on a local repository.
 Just before starting to update refs on the remote repository, the
 pre-receive hook is invoked.  Its exit status determines the success
 or failure of the update.
@@ -199,15 +202,15 @@
 still be prevented by the <<update,'update'>> hook.
 
 Both standard output and standard error output are forwarded to
-'git-send-pack' on the other end, so you can simply `echo` messages
+'git send-pack' on the other end, so you can simply `echo` messages
 for the user.
 
 [[update]]
 update
-------
+~~~~~~
 
 This hook is invoked by 'git-receive-pack' on the remote repository,
-which happens when a 'git-push' is done on a local repository.
+which happens when a 'git push' is done on a local repository.
 Just before updating the ref on the remote repository, the update hook
 is invoked.  Its exit status determines the success or failure of
 the ref update.
@@ -226,7 +229,7 @@
 This hook can be used to prevent 'forced' update on certain refs by
 making sure that the object name is a commit object that is a
 descendant of the commit object named by the old object name.
-That is, to enforce a "fast forward only" policy.
+That is, to enforce a "fast-forward only" policy.
 
 It could also be used to log the old..new status.  However, it
 does not know the entire set of branches, so it would end up
@@ -238,19 +241,19 @@
 based on filesystem group.
 
 Both standard output and standard error output are forwarded to
-'git-send-pack' on the other end, so you can simply `echo` messages
+'git send-pack' on the other end, so you can simply `echo` messages
 for the user.
 
 The default 'update' hook, when enabled--and with
-`hooks.allowunannotated` config option turned on--prevents
+`hooks.allowunannotated` config option unset or set to false--prevents
 unannotated tags to be pushed.
 
 [[post-receive]]
 post-receive
-------------
+~~~~~~~~~~~~
 
 This hook is invoked by 'git-receive-pack' on the remote repository,
-which happens when a 'git-push' is done on a local repository.
+which happens when a 'git push' is done on a local repository.
 It executes on the remote repository once after all the refs have
 been updated.
 
@@ -267,7 +270,7 @@
 names.
 
 Both standard output and standard error output are forwarded to
-'git-send-pack' on the other end, so you can simply `echo` messages
+'git send-pack' on the other end, so you can simply `echo` messages
 for the user.
 
 The default 'post-receive' hook is empty, but there is
@@ -277,10 +280,10 @@
 
 [[post-update]]
 post-update
------------
+~~~~~~~~~~~
 
 This hook is invoked by 'git-receive-pack' on the remote repository,
-which happens when a 'git-push' is done on a local repository.
+which happens when a 'git push' is done on a local repository.
 It executes on the remote repository once after all the refs have
 been updated.
 
@@ -298,22 +301,56 @@
 them.
 
 When enabled, the default 'post-update' hook runs
-'git-update-server-info' to keep the information used by dumb
+'git update-server-info' to keep the information used by dumb
 transports (e.g., HTTP) up-to-date.  If you are publishing
 a git repository that is accessible via HTTP, you should
 probably enable this hook.
 
 Both standard output and standard error output are forwarded to
-'git-send-pack' on the other end, so you can simply `echo` messages
+'git send-pack' on the other end, so you can simply `echo` messages
 for the user.
 
 pre-auto-gc
------------
+~~~~~~~~~~~
 
-This hook is invoked by 'git-gc --auto'. It takes no parameter, and
-exiting with non-zero status from this script causes the 'git-gc --auto'
+This hook is invoked by 'git gc --auto'. It takes no parameter, and
+exiting with non-zero status from this script causes the 'git gc --auto'
 to abort.
 
+post-rewrite
+~~~~~~~~~~~~
+
+This hook is invoked by commands that rewrite commits (`git commit
+--amend`, 'git-rebase'; currently 'git-filter-branch' does 'not' call
+it!).  Its first argument denotes the command it was invoked by:
+currently one of `amend` or `rebase`.  Further command-dependent
+arguments may be passed in the future.
+
+The hook receives a list of the rewritten commits on stdin, in the
+format
+
+  <old-sha1> SP <new-sha1> [ SP <extra-info> ] LF
+
+The 'extra-info' is again command-dependent.  If it is empty, the
+preceding SP is also omitted.  Currently, no commands pass any
+'extra-info'.
+
+The hook always runs after the automatic note copying (see
+"notes.rewrite.<command>" in linkgit:git-config.txt) has happened, and
+thus has access to these notes.
+
+The following command-specific comments apply:
+
+rebase::
+	For the 'squash' and 'fixup' operation, all commits that were
+	squashed are listed as being rewritten to the squashed commit.
+	This means that there will be several lines sharing the same
+	'new-sha1'.
++
+The commits are guaranteed to be listed in the order that they were
+processed by rebase.
+
+
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt
index 7df3cef..c1f692a 100644
--- a/Documentation/gitignore.txt
+++ b/Documentation/gitignore.txt
@@ -14,11 +14,8 @@
 
 A `gitignore` file specifies intentionally untracked files that
 git should ignore.
-Note that all the `gitignore` files really concern only files
-that are not already tracked by git;
-in order to ignore uncommitted changes in already tracked files,
-please refer to the 'git update-index --assume-unchanged'
-documentation.
+Files already tracked by git are not affected; see the NOTES
+below for details.
 
 Each line in a `gitignore` file specifies a pattern.
 When deciding whether to ignore a path, git normally checks
@@ -53,16 +50,19 @@
 the `$GIT_DIR/info/exclude` file.  Patterns which a user wants git to
 ignore in all situations (e.g., backup or temporary files generated by
 the user's editor of choice) generally go into a file specified by
-`core.excludesfile` in the user's `~/.gitconfig`.
+`core.excludesfile` in the user's `~/.gitconfig`. Its default value is
+$XDG_CONFIG_HOME/git/ignore. If $XDG_CONFIG_HOME is either not set or empty,
+$HOME/.config/git/ignore is used instead.
 
 The underlying git plumbing tools, such as
-'git-ls-files' and 'git-read-tree', read
+'git ls-files' and 'git read-tree', read
 `gitignore` patterns specified by command-line options, or from
 files specified by command-line options.  Higher-level git
-tools, such as 'git-status' and 'git-add',
+tools, such as 'git status' and 'git add',
 use patterns from the sources specified above.
 
-Patterns have the following format:
+PATTERN FORMAT
+--------------
 
  - A blank line matches no files, so it can serve as a separator
    for readability.
@@ -83,18 +83,35 @@
 
  - If the pattern does not contain a slash '/', git treats it as
    a shell glob pattern and checks for a match against the
-   pathname without leading directories.
+   pathname relative to the location of the `.gitignore` file
+   (relative to the toplevel of the work tree if not from a
+   `.gitignore` file).
 
  - Otherwise, git treats the pattern as a shell glob suitable
    for consumption by fnmatch(3) with the FNM_PATHNAME flag:
    wildcards in the pattern will not match a / in the pathname.
-   For example, "Documentation/\*.html" matches
-   "Documentation/git.html" but not
-   "Documentation/ppc/ppc.html".  A leading slash matches the
-   beginning of the pathname; for example, "/*.c" matches
-   "cat-file.c" but not "mozilla-sha1/sha1.c".
+   For example, "Documentation/{asterisk}.html" matches
+   "Documentation/git.html" but not "Documentation/ppc/ppc.html"
+   or "tools/perf/Documentation/perf.html".
 
-An example:
+ - A leading slash matches the beginning of the pathname.
+   For example, "/{asterisk}.c" matches "cat-file.c" but not
+   "mozilla-sha1/sha1.c".
+
+NOTES
+-----
+
+The purpose of gitignore files is to ensure that certain files
+not tracked by git remain untracked.
+
+To ignore uncommitted changes in a file that is already tracked,
+use 'git update-index {litdd}assume-unchanged'.
+
+To stop tracking a file that is currently tracked, use
+'git rm --cached'.
+
+EXAMPLES
+--------
 
 --------------------------------------------------------------
     $ git status
@@ -136,10 +153,10 @@
 The second .gitignore prevents git from ignoring
 `arch/foo/kernel/vmlinux.lds.S`.
 
-Documentation
--------------
-Documentation by David Greaves, Junio C Hamano, Josh Triplett,
-Frank Lichtenheld, and the git-list <git@vger.kernel.org>.
+SEE ALSO
+--------
+linkgit:git-rm[1], linkgit:git-update-index[1],
+linkgit:gitrepository-layout[5]
 
 GIT
 ---
diff --git a/Documentation/gitk.txt b/Documentation/gitk.txt
index cf465cb..a17a354 100644
--- a/Documentation/gitk.txt
+++ b/Documentation/gitk.txt
@@ -7,6 +7,7 @@
 
 SYNOPSIS
 --------
+[verse]
 'gitk' [<option>...] [<revs>] [--] [<path>...]
 
 DESCRIPTION
@@ -22,7 +23,7 @@
 OPTIONS
 -------
 To control which revisions to show, the command takes options applicable to
-the 'git-rev-list' command (see linkgit:git-rev-list[1]).
+the 'git rev-list' command (see linkgit:git-rev-list[1]).
 This manual page describes only the most
 frequently used options.
 
@@ -69,7 +70,7 @@
 	the form "'<from>'..'<to>'" to show all revisions between '<from>' and
 	back to '<to>'. Note, more advanced revision selection can be applied.
 	For a more complete list of ways to spell object names, see
-	"SPECIFYING REVISIONS" section in linkgit:git-rev-parse[1].
+	linkgit:gitrevisions[7].
 
 <path>...::
 
@@ -113,15 +114,6 @@
 	A minimal repository browser and git tool output highlighter written
 	in C using Ncurses.
 
-Author
-------
-Written by Paul Mackerras <paulus@samba.org>.
-
-Documentation
---------------
-Documentation by Junio C Hamano, Jonas Fonseca, and the git-list
-<git@vger.kernel.org>.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/gitmodules.txt b/Documentation/gitmodules.txt
index 5daf750..4effd78 100644
--- a/Documentation/gitmodules.txt
+++ b/Documentation/gitmodules.txt
@@ -28,7 +28,10 @@
 	be unique within the .gitmodules file.
 
 submodule.<name>.url::
-	Defines an url from where the submodule repository can be cloned.
+	Defines a URL from which the submodule repository can be cloned.
+	This may be either an absolute URL ready to be passed to
+	linkgit:git-clone[1] or (if it begins with ./ or ../) a location
+	relative to the superproject's origin repository.
 
 submodule.<name>.update::
 	Defines what to do when the submodule is updated by the superproject.
@@ -38,8 +41,34 @@
 	the commit specified in the superproject. If 'merge', the commit
 	specified in the superproject will be merged into the current branch
 	in the submodule.
+	If 'none', the submodule with name `$name` will not be updated
+	by default.
+
 	This config option is overridden if 'git submodule update' is given
-	the '--merge' or '--rebase' options.
+	the '--merge', '--rebase' or '--checkout' options.
+
+submodule.<name>.fetchRecurseSubmodules::
+	This option can be used to control recursive fetching of this
+	submodule. If this option is also present in the submodules entry in
+	.git/config of the superproject, the setting there will override the
+	one found in .gitmodules.
+	Both settings can be overridden on the command line by using the
+	"--[no-]recurse-submodules" option to "git fetch" and "git pull".
+
+submodule.<name>.ignore::
+	Defines under what circumstances "git status" and the diff family show
+	a submodule as modified. When set to "all", it will never be considered
+	modified, "dirty" will ignore all changes to the submodules work tree and
+	takes only differences between the HEAD of the submodule and the commit
+	recorded in the superproject into account. "untracked" will additionally
+	let submodules with modified tracked files in their work tree show up.
+	Using "none" (the default when this option is not set) also shows
+	submodules that have untracked files in their work tree as changed.
+	If this option is also present in the submodules entry in .git/config of
+	the superproject, the setting there will override the one found in
+	.gitmodules.
+	Both settings can be overridden on the command line by using the
+	"--ignore-submodule" option.
 
 
 EXAMPLES
@@ -58,16 +87,12 @@
 
 This defines two submodules, `libfoo` and `libbar`. These are expected to
 be checked out in the paths 'include/foo' and 'include/bar', and for both
-submodules an url is specified which can be used for cloning the submodules.
+submodules a URL is specified which can be used for cloning the submodules.
 
 SEE ALSO
 --------
 linkgit:git-submodule[1] linkgit:git-config[1]
 
-DOCUMENTATION
--------------
-Documentation by Lars Hjemli <hjemli@gmail.com>
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/gitnamespaces.txt b/Documentation/gitnamespaces.txt
new file mode 100644
index 0000000..c6713cf
--- /dev/null
+++ b/Documentation/gitnamespaces.txt
@@ -0,0 +1,82 @@
+gitnamespaces(7)
+================
+
+NAME
+----
+gitnamespaces - Git namespaces
+
+SYNOPSIS
+--------
+[verse]
+GIT_NAMESPACE=<namespace> 'git upload-pack'
+GIT_NAMESPACE=<namespace> 'git receive-pack'
+
+
+DESCRIPTION
+-----------
+
+Git supports dividing the refs of a single repository into multiple
+namespaces, each of which has its own branches, tags, and HEAD.  Git can
+expose each namespace as an independent repository to pull from and push
+to, while sharing the object store, and exposing all the refs to
+operations such as linkgit:git-gc[1].
+
+Storing multiple repositories as namespaces of a single repository
+avoids storing duplicate copies of the same objects, such as when
+storing multiple branches of the same source.  The alternates mechanism
+provides similar support for avoiding duplicates, but alternates do not
+prevent duplication between new objects added to the repositories
+without ongoing maintenance, while namespaces do.
+
+To specify a namespace, set the `GIT_NAMESPACE` environment variable to
+the namespace.  For each ref namespace, git stores the corresponding
+refs in a directory under `refs/namespaces/`.  For example,
+`GIT_NAMESPACE=foo` will store refs under `refs/namespaces/foo/`.  You
+can also specify namespaces via the `--namespace` option to
+linkgit:git[1].
+
+Note that namespaces which include a `/` will expand to a hierarchy of
+namespaces; for example, `GIT_NAMESPACE=foo/bar` will store refs under
+`refs/namespaces/foo/refs/namespaces/bar/`.  This makes paths in
+`GIT_NAMESPACE` behave hierarchically, so that cloning with
+`GIT_NAMESPACE=foo/bar` produces the same result as cloning with
+`GIT_NAMESPACE=foo` and cloning from that repo with `GIT_NAMESPACE=bar`.  It
+also avoids ambiguity with strange namespace paths such as `foo/refs/heads/`,
+which could otherwise generate directory/file conflicts within the `refs`
+directory.
+
+linkgit:git-upload-pack[1] and linkgit:git-receive-pack[1] rewrite the
+names of refs as specified by `GIT_NAMESPACE`.  git-upload-pack and
+git-receive-pack will ignore all references outside the specified
+namespace.
+
+The smart HTTP server, linkgit:git-http-backend[1], will pass
+GIT_NAMESPACE through to the backend programs; see
+linkgit:git-http-backend[1] for sample configuration to expose
+repository namespaces as repositories.
+
+For a simple local test, you can use linkgit:git-remote-ext[1]:
+
+----------
+git clone ext::'git --namespace=foo %s /tmp/prefixed.git'
+----------
+
+SECURITY
+--------
+
+Anyone with access to any namespace within a repository can potentially
+access objects from any other namespace stored in the same repository.
+You can't directly say "give me object ABCD" if you don't have a ref to
+it, but you can do some other sneaky things like:
+
+. Claiming to push ABCD, at which point the server will optimize out the
+  need for you to actually send it. Now you have a ref to ABCD and can
+  fetch it (claiming not to have it, of course).
+
+. Requesting other refs, claiming that you have ABCD, at which point the
+  server may generate deltas against ABCD.
+
+None of this causes a problem if you only host public repositories, or
+if everyone who may read one namespace may also read everything in every
+other namespace (for instance, if everyone in an organization has read
+permission to every repository).
diff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt
index 1befca9..5c891f1 100644
--- a/Documentation/gitrepository-layout.txt
+++ b/Documentation/gitrepository-layout.txt
@@ -16,39 +16,32 @@
 directory for a repository associated with your working tree, or
 `<project>.git` directory for a public 'bare' repository. It is
 also possible to have a working tree where `.git` is a plain
-ascii file containing `gitdir: <path>`, i.e. the path to the
+ASCII file containing `gitdir: <path>`, i.e. the path to the
 real git repository).
 
 objects::
 	Object store associated with this repository.  Usually
 	an object store is self sufficient (i.e. all the objects
 	that are referred to by an object found in it are also
-	found in it), but there are couple of ways to violate
-	it.
+	found in it), but there are a few ways to violate it.
 +
-. You could populate the repository by running a commit walker
-without `-a` option.  Depending on which options are given, you
-could have only commit objects without associated blobs and
-trees this way, for example.  A repository with this kind of
-incomplete object store is not suitable to be published to the
-outside world but sometimes useful for private repository.
-. You also could have an incomplete but locally usable repository
-by cloning shallowly.  See linkgit:git-clone[1].
-. You can be using `objects/info/alternates` mechanism, or
-`$GIT_ALTERNATE_OBJECT_DIRECTORIES` mechanism to 'borrow'
+. You could have an incomplete but locally usable repository
+by creating a shallow clone.  See linkgit:git-clone[1].
+. You could be using the `objects/info/alternates` or
+`$GIT_ALTERNATE_OBJECT_DIRECTORIES` mechanisms to 'borrow'
 objects from other object stores.  A repository with this kind
 of incomplete object store is not suitable to be published for
 use with dumb transports but otherwise is OK as long as
-`objects/info/alternates` points at the right object stores
-it borrows from.
+`objects/info/alternates` points at the object stores it
+borrows from.
 
 objects/[0-9a-f][0-9a-f]::
-	Traditionally, each object is stored in its own file.
-	They are split into 256 subdirectories using the first
-	two letters from its object name to keep the number of
-	directory entries `objects` directory itself needs to
-	hold.  Objects found here are often called 'unpacked'
-	(or 'loose') objects.
+	A newly created object is stored in its own file.
+	The objects are splayed over 256 subdirectories using
+	the first two characters of the sha1 object name to
+	keep the number of directory entries in `objects`
+	itself to a manageable number. Objects found
+	here are often called 'unpacked' (or 'loose') objects.
 
 objects/pack::
 	Packs (files that store many object in compressed form,
@@ -64,7 +57,7 @@
 	are available in this object store.  Whenever a pack is
 	added or removed, `git update-server-info` should be run
 	to keep this file up-to-date if the repository is
-	published for dumb transports.  'git-repack' does this
+	published for dumb transports.  'git repack' does this
 	by default.
 
 objects/info/alternates::
@@ -85,7 +78,7 @@
 
 refs::
 	References are stored in subdirectories of this
-	directory.  The 'git-prune' command knows to keep
+	directory.  The 'git prune' command knows to preserve
 	objects reachable from refs found in this directory and
 	its subdirectories.
 
@@ -119,21 +112,22 @@
 +
 HEAD can also record a specific commit directly, instead of
 being a symref to point at the current branch.  Such a state
-is often called 'detached HEAD', and almost all commands work
-identically as normal.  See linkgit:git-checkout[1] for
-details.
+is often called 'detached HEAD.'  See linkgit:git-checkout[1]
+for details.
 
 branches::
 	A slightly deprecated way to store shorthands to be used
-	to specify URL to 'git-fetch', 'git-pull' and 'git-push'
-	commands is to store a file in `branches/<name>` and
-	give 'name' to these commands in place of 'repository'
-	argument.
+	to specify a URL to 'git fetch', 'git pull' and 'git push'.
+	A file can be stored as `branches/<name>` and then
+	'name' can be given to these commands in place of
+	'repository' argument.  See the REMOTES section in
+	linkgit:git-fetch[1] for details.  This mechanism is legacy
+	and not likely to be found in modern repositories.
 
 hooks::
 	Hooks are customization scripts used by various git
 	commands.  A handful of sample hooks are installed when
-	'git-init' is run, but all of them are disabled by
+	'git init' is run, but all of them are disabled by
 	default.  To enable, the `.sample` suffix has to be
 	removed from the filename by renaming.
 	Read linkgit:githooks[5] for more details about
@@ -151,10 +145,10 @@
 	This file helps dumb transports discover what refs are
 	available in this repository.  If the repository is
 	published for dumb transports, this file should be
-	regenerated by 'git-update-server-info' every time a tag
+	regenerated by 'git update-server-info' every time a tag
 	or branch is created or modified.  This is normally done
 	from the `hooks/update` hook, which is run by the
-	'git-receive-pack' command when you 'git-push' into the
+	'git-receive-pack' command when you 'git push' into the
 	repository.
 
 info/grafts::
@@ -168,14 +162,16 @@
 info/exclude::
 	This file, by convention among Porcelains, stores the
 	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
+	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: linkgit:gitignore[5].
 
 remotes::
-	Stores shorthands to be used to give URL and default
-	refnames to interact with remote repository to
-	'git-fetch', 'git-pull' and 'git-push' commands.
+	Stores shorthands for URL and default refnames for use
+	when interacting with remote repositories via 'git fetch',
+	'git pull' and 'git push' commands.  See the REMOTES section
+	in linkgit:git-fetch[1] for details.  This mechanism is legacy
+	and not likely to be found in modern repositories.
 
 logs::
 	Records of changes made to refs are stored in this
diff --git a/Documentation/gitrevisions.txt b/Documentation/gitrevisions.txt
new file mode 100644
index 0000000..fc4789f
--- /dev/null
+++ b/Documentation/gitrevisions.txt
@@ -0,0 +1,35 @@
+gitrevisions(7)
+================
+
+NAME
+----
+gitrevisions - specifying revisions and ranges for git
+
+SYNOPSIS
+--------
+gitrevisions
+
+
+DESCRIPTION
+-----------
+
+Many Git commands take revision parameters as arguments. Depending on
+the command, they denote a specific commit or, for commands which
+walk the revision graph (such as linkgit:git-log[1]), all commits which can
+be reached from that commit. In the latter case one can also specify a
+range of revisions explicitly.
+
+In addition, some Git commands (such as linkgit:git-show[1]) also take
+revision parameters which denote other objects than commits, e.g. blobs
+("files") or trees ("directories of files").
+
+include::revisions.txt[]
+
+
+SEE ALSO
+--------
+linkgit:git-rev-parse[1]
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/gittutorial-2.txt b/Documentation/gittutorial-2.txt
index dc8fc3a..e00a4d2 100644
--- a/Documentation/gittutorial-2.txt
+++ b/Documentation/gittutorial-2.txt
@@ -7,6 +7,7 @@
 
 SYNOPSIS
 --------
+[verse]
 git *
 
 DESCRIPTION
@@ -33,12 +34,12 @@
 $ git add .
 $ git commit -a -m "initial commit"
 [master (root-commit) 54196cc] initial commit
- 1 files changed, 1 insertions(+), 0 deletions(-)
+ 1 file changed, 1 insertion(+)
  create mode 100644 file.txt
 $ echo 'hello world!' >file.txt
 $ git commit -a -m "add emphasis"
 [master c4d59f3] add emphasis
- 1 files changed, 1 insertions(+), 1 deletions(-)
+ 1 file changed, 1 insertion(+), 1 deletion(-)
 ------------------------------------------------
 
 What are the 7 digits of hex that git responded to the commit with?
@@ -260,7 +261,7 @@
 +hello world, again
 ------------------------------------------------
 
-So 'git-diff' is comparing against something other than the head.
+So 'git diff' is comparing against something other than the head.
 The thing that it's comparing against is actually the index file,
 which is stored in .git/index in a binary format, but whose contents
 we can examine with ls-files:
@@ -275,9 +276,9 @@
 hello world, again
 ------------------------------------------------
 
-So what our 'git-add' did was store a new blob and then put
+So what our 'git add' did was store a new blob and then put
 a reference to it in the index file.  If we modify the file again,
-we'll see that the new modifications are reflected in the 'git-diff'
+we'll see that the new modifications are reflected in the 'git diff'
 output:
 
 ------------------------------------------------
@@ -292,7 +293,7 @@
 +again?
 ------------------------------------------------
 
-With the right arguments, 'git-diff' can also show us the difference
+With the right arguments, 'git diff' can also show us the difference
 between the working directory and the last commit, or between the
 index and the last commit:
 
@@ -316,7 +317,7 @@
 +hello world, again
 ------------------------------------------------
 
-At any time, we can create a new commit using 'git-commit' (without
+At any time, we can create a new commit using 'git commit' (without
 the "-a" option), and verify that the state committed only includes the
 changes stored in the index file, not the additional change that is
 still only in our working tree:
@@ -334,11 +335,11 @@
 +again?
 ------------------------------------------------
 
-So by default 'git-commit' uses the index to create the commit, not
+So by default 'git commit' uses the index to create the commit, not
 the working tree; the "-a" option to commit tells it to first update
 the index with all changes in the working tree.
 
-Finally, it's worth looking at the effect of 'git-add' on the index
+Finally, it's worth looking at the effect of 'git add' on the index
 file:
 
 ------------------------------------------------
@@ -346,7 +347,7 @@
 $ git add closing.txt
 ------------------------------------------------
 
-The effect of the 'git-add' was to add one entry to the index file:
+The effect of the 'git add' was to add one entry to the index file:
 
 ------------------------------------------------
 $ git ls-files --stage
@@ -373,7 +374,7 @@
 #
 #       new file: closing.txt
 #
-# Changed but not updated:
+# Changes not staged for commit:
 #   (use "git add <file>..." to update what will be committed)
 #
 #       modified: file.txt
diff --git a/Documentation/gittutorial.txt b/Documentation/gittutorial.txt
index cf0689c..dee0505 100644
--- a/Documentation/gittutorial.txt
+++ b/Documentation/gittutorial.txt
@@ -7,6 +7,7 @@
 
 SYNOPSIS
 --------
+[verse]
 git *
 
 DESCRIPTION
@@ -67,7 +68,7 @@
 directory created, named ".git".
 
 Next, tell git to take a snapshot of the contents of all files under the
-current directory (note the '.'), with 'git-add':
+current directory (note the '.'), with 'git add':
 
 ------------------------------------------------
 $ git add .
@@ -75,7 +76,7 @@
 
 This snapshot is now stored in a temporary staging area which git calls
 the "index".  You can permanently store the contents of the index in the
-repository with 'git-commit':
+repository with 'git commit':
 
 ------------------------------------------------
 $ git commit
@@ -94,15 +95,15 @@
 ------------------------------------------------
 
 You are now ready to commit.  You can see what is about to be committed
-using 'git-diff' with the --cached option:
+using 'git diff' with the --cached option:
 
 ------------------------------------------------
 $ git diff --cached
 ------------------------------------------------
 
-(Without --cached, 'git-diff' will show you any changes that
+(Without --cached, 'git diff' will show you any changes that
 you've made but not yet added to the index.)  You can also get a brief
-summary of the situation with 'git-status':
+summary of the situation with 'git status':
 
 ------------------------------------------------
 $ git status
@@ -126,7 +127,7 @@
 This will again prompt you for a message describing the change, and then
 record a new version of the project.
 
-Alternatively, instead of running 'git-add' beforehand, you can use
+Alternatively, instead of running 'git add' beforehand, you can use
 
 ------------------------------------------------
 $ git commit -a
@@ -147,7 +148,7 @@
 
 Many revision control systems provide an `add` command that tells the
 system to start tracking changes to a new file.  Git's `add` command
-does something simpler and more powerful: 'git-add' is used both for new
+does something simpler and more powerful: 'git add' is used both for new
 and newly modified files, and in both cases it takes a snapshot of the
 given files and stages that content in the index, ready for inclusion in
 the next commit.
@@ -376,7 +377,7 @@
 ------------------------------------------------
 
 With this, Alice can perform the first part of the "pull" operation
-alone using the 'git-fetch' command without merging them with her own
+alone using the 'git fetch' command without merging them with her own
 branch, using:
 
 -------------------------------------
@@ -384,8 +385,8 @@
 -------------------------------------
 
 Unlike the longhand form, when Alice fetches from Bob using a
-remote repository shorthand set up with 'git-remote', what was
-fetched is stored in a remote tracking branch, in this case
+remote repository shorthand set up with 'git remote', what was
+fetched is stored in a remote-tracking branch, in this case
 `bob/master`.  So after this:
 
 -------------------------------------
@@ -402,8 +403,8 @@
 alice$ git merge bob/master
 -------------------------------------
 
-This `merge` can also be done by 'pulling from her own remote
-tracking branch', like this:
+This `merge` can also be done by 'pulling from her own remote-tracking
+branch', like this:
 
 -------------------------------------
 alice$ git pull . remotes/bob/master
@@ -428,7 +429,7 @@
 /home/alice/project
 -------------------------------------
 
-(The complete configuration created by 'git-clone' is visible using
+(The complete configuration created by 'git clone' is visible using
 `git config -l`, and the linkgit:git-config[1] man page
 explains the meaning of each option.)
 
@@ -458,7 +459,7 @@
 -----------------
 
 Git history is represented as a series of interrelated commits.  We
-have already seen that the 'git-log' command can list those commits.
+have already seen that the 'git log' command can list those commits.
 Note that first line of each git log entry also gives a name for the
 commit:
 
@@ -471,7 +472,7 @@
     merge-base: Clarify the comments on post processing.
 -------------------------------------
 
-We can give this name to 'git-show' to see the details about this
+We can give this name to 'git show' to see the details about this
 commit.
 
 -------------------------------------
@@ -529,13 +530,13 @@
 Be careful with that last command: in addition to losing any changes
 in the working directory, it will also remove all later commits from
 this branch.  If this branch is the only branch containing those
-commits, they will be lost.  Also, don't use 'git-reset' on a
+commits, they will be lost.  Also, don't use 'git reset' on a
 publicly-visible branch that other developers pull from, as it will
 force needless merges on other developers to clean up the history.
-If you need to undo changes that you have pushed, use 'git-revert'
+If you need to undo changes that you have pushed, use 'git revert'
 instead.
 
-The 'git-grep' command can search for strings in any version of your
+The 'git grep' command can search for strings in any version of your
 project, so
 
 -------------------------------------
@@ -544,7 +545,7 @@
 
 searches for all occurrences of "hello" in v2.5.
 
-If you leave out the commit name, 'git-grep' will search any of the
+If you leave out the commit name, 'git grep' will search any of the
 files it manages in your current directory.  So
 
 -------------------------------------
@@ -554,7 +555,7 @@
 is a quick way to search just the files that are tracked by git.
 
 Many git commands also take sets of commits, which can be specified
-in a number of ways.  Here are some examples with 'git-log':
+in a number of ways.  Here are some examples with 'git log':
 
 -------------------------------------
 $ git log v2.5..v2.6            # commits between v2.5 and v2.6
@@ -564,7 +565,7 @@
 				# Makefile
 -------------------------------------
 
-You can also give 'git-log' a "range" of commits where the first is not
+You can also give 'git log' a "range" of commits where the first is not
 necessarily an ancestor of the second; for example, if the tips of
 the branches "stable" and "master" diverged from a common
 commit some time ago, then
@@ -583,9 +584,9 @@
 will show the list of commits made on the stable branch but not
 the master branch.
 
-The 'git-log' command has a weakness: it must present commits in a
+The 'git log' command has a weakness: it must present commits in a
 list.  When the history has lines of development that diverged and
-then merged back together, the order in which 'git-log' presents
+then merged back together, the order in which 'git log' presents
 those commits is meaningless.
 
 Most projects with multiple contributors (such as the Linux kernel,
@@ -609,7 +610,7 @@
 $ git diff v2.5:Makefile HEAD:Makefile.in
 -------------------------------------
 
-You can also use 'git-show' to see any such file:
+You can also use 'git show' to see any such file:
 
 -------------------------------------
 $ git show v2.5:Makefile
diff --git a/Documentation/gitweb.conf.txt b/Documentation/gitweb.conf.txt
new file mode 100644
index 0000000..4947455
--- /dev/null
+++ b/Documentation/gitweb.conf.txt
@@ -0,0 +1,896 @@
+gitweb.conf(5)
+==============
+
+NAME
+----
+gitweb.conf - Gitweb (git web interface) configuration file
+
+SYNOPSIS
+--------
+/etc/gitweb.conf, /etc/gitweb-common.conf, $GITWEBDIR/gitweb_config.perl
+
+DESCRIPTION
+-----------
+
+The gitweb CGI script for viewing Git repositories over the web uses a
+perl script fragment as its configuration file.  You can set variables
+using "`our $variable = value`"; text from a "#" character until the
+end of a line is ignored.  See *perlsyn*(1) for details.
+
+An example:
+
+    # gitweb configuration file for http://git.example.org
+    #
+    our $projectroot = "/srv/git"; # FHS recommendation
+    our $site_name = 'Example.org >> Repos';
+
+
+The configuration file is used to override the default settings that
+were built into gitweb at the time the 'gitweb.cgi' script was generated.
+
+While one could just alter the configuration settings in the gitweb
+CGI itself, those changes would be lost upon upgrade.  Configuration
+settings might also be placed into a file in the same directory as the
+CGI script with the default name 'gitweb_config.perl' -- allowing
+one to have multiple gitweb instances with different configurations by
+the use of symlinks.
+
+Note that some configuration can be controlled on per-repository rather than
+gitweb-wide basis: see "Per-repository gitweb configuration" subsection on
+linkgit:gitweb[1] manpage.
+
+
+DISCUSSION
+----------
+Gitweb reads configuration data from the following sources in the
+following order:
+
+ * built-in values (some set during build stage),
+
+ * common system-wide configuration file (defaults to
+   '/etc/gitweb-common.conf'),
+
+ * either per-instance configuration file (defaults to 'gitweb_config.perl'
+   in the same directory as the installed gitweb), or if it does not exists
+   then fallback system-wide configuration file (defaults to '/etc/gitweb.conf').
+
+Values obtained in later configuration files override values obtained earlier
+in the above sequence.
+
+Locations of the common system-wide configuration file, the fallback
+system-wide configuration file and the per-instance configuration file
+are defined at compile time using build-time Makefile configuration
+variables, respectively `GITWEB_CONFIG_COMMON`, `GITWEB_CONFIG_SYSTEM`
+and `GITWEB_CONFIG`.
+
+You can also override locations of gitweb configuration files during
+runtime by setting the following environment variables:
+`GITWEB_CONFIG_COMMON`, `GITWEB_CONFIG_SYSTEM` and `GITWEB_CONFIG`
+to a non-empty value.
+
+
+The syntax of the configuration files is that of Perl, since these files are
+handled by sourcing them as fragments of Perl code (the language that
+gitweb itself is written in). Variables are typically set using the
+`our` qualifier (as in "`our $variable = <value>;`") to avoid syntax
+errors if a new version of gitweb no longer uses a variable and therefore
+stops declaring it.
+
+You can include other configuration file using read_config_file()
+subroutine.  For example, one might want to put gitweb configuration
+related to access control for viewing repositories via Gitolite (one
+of git repository management tools) in a separate file, e.g. in
+'/etc/gitweb-gitolite.conf'.  To include it, put
+
+--------------------------------------------------
+read_config_file("/etc/gitweb-gitolite.conf");
+--------------------------------------------------
+
+somewhere in gitweb configuration file used, e.g. in per-installation
+gitweb configuration file.  Note that read_config_file() checks itself
+that the file it reads exists, and does nothing if it is not found.
+It also handles errors in included file.
+
+
+The default configuration with no configuration file at all may work
+perfectly well for some installations.  Still, a configuration file is
+useful for customizing or tweaking the behavior of gitweb in many ways, and
+some optional features will not be present unless explicitly enabled using
+the configurable `%features` variable (see also "Configuring gitweb
+features" section below).
+
+
+CONFIGURATION VARIABLES
+-----------------------
+Some configuration variables have their default values (embedded in the CGI
+script) set during building gitweb -- if that is the case, this fact is put
+in their description.  See gitweb's 'INSTALL' file for instructions on building
+and installing gitweb.
+
+
+Location of repositories
+~~~~~~~~~~~~~~~~~~~~~~~~
+The configuration variables described below control how gitweb finds
+git repositories, and how repositories are displayed and accessed.
+
+See also "Repositories" and later subsections in linkgit:gitweb[1] manpage.
+
+$projectroot::
+	Absolute filesystem path which will be prepended to project path;
+	the path to repository is `$projectroot/$project`.  Set to
+	`$GITWEB_PROJECTROOT` during installation.  This variable has to be
+	set correctly for gitweb to find repositories.
++
+For example, if `$projectroot` is set to "/srv/git" by putting the following
+in gitweb config file:
++
+----------------------------------------------------------------------------
+our $projectroot = "/srv/git";
+----------------------------------------------------------------------------
++
+then
++
+------------------------------------------------
+http://git.example.com/gitweb.cgi?p=foo/bar.git
+------------------------------------------------
++
+and its path_info based equivalent
++
+------------------------------------------------
+http://git.example.com/gitweb.cgi/foo/bar.git
+------------------------------------------------
++
+will map to the path '/srv/git/foo/bar.git' on the filesystem.
+
+$projects_list::
+	Name of a plain text file listing projects, or a name of directory
+	to be scanned for projects.
++
+Project list files should list one project per line, with each line
+having the following format
++
+-----------------------------------------------------------------------------
+<URI-encoded filesystem path to repository> SP <URI-encoded repository owner>
+-----------------------------------------------------------------------------
++
+The default value of this variable is determined by the `GITWEB_LIST`
+makefile variable at installation time.  If this variable is empty, gitweb
+will fall back to scanning the `$projectroot` directory for repositories.
+
+$project_maxdepth::
+	If `$projects_list` variable is unset, gitweb will recursively
+	scan filesystem for git repositories.  The `$project_maxdepth`
+	is used to limit traversing depth, relative to `$projectroot`
+	(starting point); it means that directories which are further
+	from `$projectroot` than `$project_maxdepth` will be skipped.
++
+It is purely performance optimization, originally intended for MacOS X,
+where recursive directory traversal is slow.  Gitweb follows symbolic
+links, but it detects cycles, ignoring any duplicate files and directories.
++
+The default value of this variable is determined by the build-time
+configuration variable `GITWEB_PROJECT_MAXDEPTH`, which defaults to
+2007.
+
+$export_ok::
+	Show repository only if this file exists (in repository).  Only
+	effective if this variable evaluates to true.  Can be set when
+	building gitweb by setting `GITWEB_EXPORT_OK`.  This path is
+	relative to `GIT_DIR`.  git-daemon[1] uses 'git-daemon-export-ok',
+	unless started with `--export-all`.  By default this variable is
+	not set, which means that this feature is turned off.
+
+$export_auth_hook::
+	Function used to determine which repositories should be shown.
+	This subroutine should take one parameter, the full path to
+	a project, and if it returns true, that project will be included
+	in the projects list and can be accessed through gitweb as long
+	as it fulfills the other requirements described by $export_ok,
+	$projects_list, and $projects_maxdepth.  Example:
++
+----------------------------------------------------------------------------
+our $export_auth_hook = sub { return -e "$_[0]/git-daemon-export-ok"; };
+----------------------------------------------------------------------------
++
+though the above might be done by using `$export_ok` instead
++
+----------------------------------------------------------------------------
+our $export_ok = "git-daemon-export-ok";
+----------------------------------------------------------------------------
++
+If not set (default), it means that this feature is disabled.
++
+See also more involved example in "Controlling access to git repositories"
+subsection on linkgit:gitweb[1] manpage.
+
+$strict_export::
+	Only allow viewing of repositories also shown on the overview page.
+	This for example makes `$gitweb_export_ok` file decide if repository is
+	available and not only if it is shown.  If `$gitweb_list` points to
+	file with list of project, only those repositories listed would be
+	available for gitweb.  Can be set during building gitweb via
+	`GITWEB_STRICT_EXPORT`.  By default this variable is not set, which
+	means that you can directly access those repositories that are hidden
+	from projects list page (e.g. the are not listed in the $projects_list
+	file).
+
+
+Finding files
+~~~~~~~~~~~~~
+The following configuration variables tell gitweb where to find files.
+The values of these variables are paths on the filesystem.
+
+$GIT::
+	Core git executable to use.  By default set to `$GIT_BINDIR/git`, which
+	in turn is by default set to `$(bindir)/git`.  If you use git installed
+	from a binary package, you should usually set this to "/usr/bin/git".
+	This can just be "git" if your web server has a sensible PATH; from
+	security point of view it is better to use absolute path to git binary.
+	If you have multiple git versions installed it can be used to choose
+	which one to use.  Must be (correctly) set for gitweb to be able to
+	work.
+
+$mimetypes_file::
+	File to use for (filename extension based) guessing of MIME types before
+	trying '/etc/mime.types'.  *NOTE* that this path, if relative, is taken
+	as relative to the current git repository, not to CGI script.  If unset,
+	only '/etc/mime.types' is used (if present on filesystem).  If no mimetypes
+	file is found, mimetype guessing based on extension of file is disabled.
+	Unset by default.
+
+$highlight_bin::
+	Path to the highlight executable to use (it must be the one from
+	http://www.andre-simon.de[] due to assumptions about parameters and output).
+	By default set to 'highlight'; set it to full path to highlight
+	executable if it is not installed on your web server's PATH.
+	Note that 'highlight' feature must be set for gitweb to actually
+	use syntax highlighting.
++
+*NOTE*: if you want to add support for new file type (supported by
+"highlight" but not used by gitweb), you need to modify `%highlight_ext`
+or `%highlight_basename`, depending on whether you detect type of file
+based on extension (for example "sh") or on its basename (for example
+"Makefile").  The keys of these hashes are extension and basename,
+respectively, and value for given key is name of syntax to be passed via
+`--syntax <syntax>` to highlighter.
++
+For example if repositories you are hosting use "phtml" extension for
+PHP files, and you want to have correct syntax-highlighting for those
+files, you can add the following to gitweb configuration:
++
+---------------------------------------------------------
+our %highlight_ext;
+$highlight_ext{'phtml'} = 'php';
+---------------------------------------------------------
+
+
+Links and their targets
+~~~~~~~~~~~~~~~~~~~~~~~
+The configuration variables described below configure some of gitweb links:
+their target and their look (text or image), and where to find page
+prerequisites (stylesheet, favicon, images, scripts).  Usually they are left
+at their default values, with the possible exception of `@stylesheets`
+variable.
+
+@stylesheets::
+	List of URIs of stylesheets (relative to the base URI of a page). You
+	might specify more than one stylesheet, for example to use "gitweb.css"
+	as base with site specific modifications in a separate stylesheet
+	to make it easier to upgrade gitweb.  For example, you can add
+	a `site` stylesheet by putting
++
+----------------------------------------------------------------------------
+push @stylesheets, "gitweb-site.css";
+----------------------------------------------------------------------------
++
+in the gitweb config file.  Those values that are relative paths are
+relative to base URI of gitweb.
++
+This list should contain the URI of gitweb's standard stylesheet.  The default
+URI of gitweb stylesheet can be set at build time using the `GITWEB_CSS`
+makefile variable.  Its default value is 'static/gitweb.css'
+(or 'static/gitweb.min.css' if the `CSSMIN` variable is defined,
+i.e. if CSS minifier is used during build).
++
+*Note*: there is also a legacy `$stylesheet` configuration variable, which was
+used by older gitweb.  If `$stylesheet` variable is defined, only CSS stylesheet
+given by this variable is used by gitweb.
+
+$logo::
+	Points to the location where you put 'git-logo.png' on your web
+	server, or to be more the generic URI of logo, 72x27 size).  This image
+	is displayed in the top right corner of each gitweb page and used as
+	a logo for the Atom feed.  Relative to the base URI of gitweb (as a path).
+	Can be adjusted when building gitweb using `GITWEB_LOGO` variable
+	By default set to 'static/git-logo.png'.
+
+$favicon::
+	Points to the location where you put 'git-favicon.png' on your web
+	server, or to be more the generic URI of favicon, which will be served
+	as "image/png" type.  Web browsers that support favicons (website icons)
+	may display them in the browser's URL bar and next to the site name in
+	bookmarks.  Relative to the base URI of gitweb.  Can be adjusted at
+	build time using `GITWEB_FAVICON` variable.
+	By default set to 'static/git-favicon.png'.
+
+$javascript::
+	Points to the location where you put 'gitweb.js' on your web server,
+	or to be more generic the URI of JavaScript code used by gitweb.
+	Relative to the base URI of gitweb.  Can be set at build time using
+	the `GITWEB_JS` build-time configuration variable.
++
+The default value is either 'static/gitweb.js', or 'static/gitweb.min.js' if
+the `JSMIN` build variable was defined, i.e. if JavaScript minifier was used
+at build time.  *Note* that this single file is generated from multiple
+individual JavaScript "modules".
+
+$home_link::
+	Target of the home link on the top of all pages (the first part of view
+	"breadcrumbs").  By default it is set to the absolute URI of a current page
+	(to the value of `$my_uri` variable, or to "/" if `$my_uri` is undefined
+	or is an empty string).
+
+$home_link_str::
+	Label for the "home link" at the top of all pages, leading to `$home_link`
+	(usually the main gitweb page, which contains the projects list).  It is
+	used as the first component of gitweb's "breadcrumb trail":
+	`<home link> / <project> / <action>`.  Can be set at build time using
+	the `GITWEB_HOME_LINK_STR` variable.  By default it is set to "projects",
+	as this link leads to the list of projects.  Other popular choice it to
+	set it to the name of site.
+
+$logo_url::
+$logo_label::
+	URI and label (title) for the Git logo link (or your site logo,
+	if you chose to use different logo image). By default, these both
+	refer to git homepage, http://git-scm.com[]; in the past, they pointed
+	to git documentation at http://www.kernel.org[].
+
+
+Changing gitweb's look
+~~~~~~~~~~~~~~~~~~~~~~
+You can adjust how pages generated by gitweb look using the variables described
+below.  You can change the site name, add common headers and footers for all
+pages, and add a description of this gitweb installation on its main page
+(which is the projects list page), etc.
+
+$site_name::
+	Name of your site or organization, to appear in page titles.  Set it
+	to something descriptive for clearer bookmarks etc.  If this variable
+	is not set or is, then gitweb uses the value of the `SERVER_NAME`
+	CGI environment variable, setting site name to "$SERVER_NAME Git",
+	or "Untitled Git" if this variable is not set (e.g. if running gitweb
+	as standalone script).
++
+Can be set using the `GITWEB_SITENAME` at build time.  Unset by default.
+
+$site_html_head_string::
+	HTML snippet to be included in the <head> section of each page.
+	Can be set using `GITWEB_SITE_HTML_HEAD_STRING` at build time.
+	No default value.
+
+$site_header::
+	Name of a file with HTML to be included at the top of each page.
+	Relative to the directory containing the 'gitweb.cgi' script.
+	Can be set using `GITWEB_SITE_HEADER` at build time.  No default
+	value.
+
+$site_footer::
+	Name of a file with HTML to be included at the bottom of each page.
+	Relative to the directory containing the 'gitweb.cgi' script.
+	Can be set using `GITWEB_SITE_FOOTER` at build time.  No default
+	value.
+
+$home_text::
+	Name of a HTML file which, if it exists, is included on the
+	gitweb projects overview page ("projects_list" view).  Relative to
+	the directory containing the gitweb.cgi script.  Default value
+	can be adjusted during build time using `GITWEB_HOMETEXT` variable.
+	By default set to 'indextext.html'.
+
+$projects_list_description_width::
+	The width (in characters) of the "Description" column of the projects list.
+	Longer descriptions will be truncated (trying to cut at word boundary);
+	the full description is available in the 'title' attribute (usually shown on
+	mouseover).  The default is 25, which might be too small if you
+	use long project descriptions.
+
+$default_projects_order::
+	Default value of ordering of projects on projects list page, which
+	means the ordering used if you don't explicitly sort projects list
+	(if there is no "o" CGI query parameter in the URL).  Valid values
+	are "none" (unsorted), "project" (projects are by project name,
+	i.e. path to repository relative to `$projectroot`), "descr"
+	(project description), "owner", and "age" (by date of most current
+	commit).
++
+Default value is "project".  Unknown value means unsorted.
+
+
+Changing gitweb's behavior
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+These configuration variables control _internal_ gitweb behavior.
+
+$default_blob_plain_mimetype::
+	Default mimetype for the blob_plain (raw) view, if mimetype checking
+	doesn't result in some other type; by default "text/plain".
+	Gitweb guesses mimetype of a file to display based on extension
+	of its filename, using `$mimetypes_file` (if set and file exists)
+	and '/etc/mime.types' files (see *mime.types*(5) manpage; only
+	filename extension rules are supported by gitweb).
+
+$default_text_plain_charset::
+	Default charset for text files. If this is not set, the web server
+	configuration will be used.  Unset by default.
+
+$fallback_encoding::
+	Gitweb assumes this charset when a line contains non-UTF-8 characters.
+	The fallback decoding is used without error checking, so it can be even
+	"utf-8". The value must be a valid encoding; see the *Encoding::Supported*(3pm)
+	man page for a list. The default is "latin1", aka. "iso-8859-1".
+
+@diff_opts::
+	Rename detection options for git-diff and git-diff-tree. The default is
+	(\'-M'); set it to (\'-C') or (\'-C', \'-C') to also detect copies,
+	or set it to () i.e. empty list if you don't want to have renames
+	detection.
++
+*Note* that rename and especially copy detection can be quite
+CPU-intensive.  Note also that non git tools can have problems with
+patches generated with options mentioned above, especially when they
+involve file copies (\'-C') or criss-cross renames (\'-B').
+
+
+Some optional features and policies
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Most of features are configured via `%feature` hash; however some of extra
+gitweb features can be turned on and configured using variables described
+below.  This list beside configuration variables that control how gitweb
+looks does contain variables configuring administrative side of gitweb
+(e.g. cross-site scripting prevention; admittedly this as side effect
+affects how "summary" pages look like, or load limiting).
+
+@git_base_url_list::
+	List of git base URLs.  These URLs are used to generate URLs
+	describing from where to fetch a project, which are shown on
+	project summary page.  The full fetch URL is "`$git_base_url/$project`",
+	for each element of this list. You can set up multiple base URLs
+	(for example one for `git://` protocol, and one for `http://`
+	protocol).
++
+Note that per repository configuration can be set in '$GIT_DIR/cloneurl'
+file, or as values of multi-value `gitweb.url` configuration variable in
+project config.  Per-repository configuration takes precedence over value
+composed from `@git_base_url_list` elements and project name.
++
+You can setup one single value (single entry/item in this list) at build
+time by setting the `GITWEB_BASE_URL` built-time configuration variable.
+By default it is set to (), i.e. an empty list.  This means that gitweb
+would not try to create project URL (to fetch) from project name.
+
+$projects_list_group_categories::
+	Whether to enables the grouping of projects by category on the project
+	list page. The category of a project is determined by the
+	`$GIT_DIR/category` file or the `gitweb.category` variable in each
+	repository's configuration.  Disabled by default (set to 0).
+
+$project_list_default_category::
+	Default category for projects for which none is specified.  If this is
+	set to the empty string, such projects will remain uncategorized and
+	listed at the top, above categorized projects.  Used only if project
+	categories are enabled, which means if `$projects_list_group_categories`
+	is true.  By default set to "" (empty string).
+
+$prevent_xss::
+	If true, some gitweb features are disabled to prevent content in
+	repositories from launching cross-site scripting (XSS) attacks.  Set this
+	to true if you don't trust the content of your repositories.
+	False by default (set to 0).
+
+$maxload::
+	Used to set the maximum load that we will still respond to gitweb queries.
+	If the server load exceeds this value then gitweb will return
+	"503 Service Unavailable" error.  The server load is taken to be 0
+	if gitweb cannot determine its value.  Currently it works only on Linux,
+	where it uses '/proc/loadavg'; the load there is the number of active
+	tasks on the system -- processes that are actually running -- averaged
+	over the last minute.
++
+Set `$maxload` to undefined value (`undef`) to turn this feature off.
+The default value is 300.
+
+$omit_age_column::
+	If true, omit the column with date of the most current commit on the
+	projects list page. It can save a bit of I/O and a fork per repository.
+
+$omit_owner::
+	If true prevents displaying information about repository owner.
+
+$per_request_config::
+	If this is set to code reference, it will be run once for each request.
+	You can	set parts of configuration that change per session this way.
+	For example, one might use the following code in a gitweb configuration
+	file
++
+--------------------------------------------------------------------------------
+our $per_request_config = sub {
+	$ENV{GL_USER} = $cgi->remote_user || "gitweb";
+};
+--------------------------------------------------------------------------------
++
+If `$per_request_config` is not a code reference, it is interpreted as boolean
+value.  If it is true gitweb will process config files once per request,
+and if it is false gitweb will process config files only once, each time it
+is executed.  True by default (set to 1).
++
+*NOTE*: `$my_url`, `$my_uri`, and `$base_url` are overwritten with their default
+values before every request, so if you want to change them, be sure to set
+this variable to true or a code reference effecting the desired changes.
++
+This variable matters only when using persistent web environments that
+serve multiple requests using single gitweb instance, like mod_perl,
+FastCGI or Plackup.
+
+
+Other variables
+~~~~~~~~~~~~~~~
+Usually you should not need to change (adjust) any of configuration
+variables described below; they should be automatically set by gitweb to
+correct value.
+
+
+$version::
+	Gitweb version, set automatically when creating gitweb.cgi from
+	gitweb.perl. You might want to modify it if you are running modified
+	gitweb, for example
++
+---------------------------------------------------
+our $version .= " with caching";
+---------------------------------------------------
++
+if you run modified version of gitweb with caching support.  This variable
+is purely informational, used e.g. in the "generator" meta header in HTML
+header.
+
+$my_url::
+$my_uri::
+	Full URL and absolute URL of the gitweb script;
+	in earlier versions of gitweb you might have need to set those
+	variables, but now there should be no need to do it.  See
+	`$per_request_config` if you need to set them still.
+
+$base_url::
+	Base URL for relative URLs in pages generated by gitweb,
+	(e.g. `$logo`, `$favicon`, `@stylesheets` if they are relative URLs),
+	needed and used '<base href="$base_url">' only for URLs with nonempty
+	PATH_INFO.  Usually gitweb sets its value correctly,
+	and there is no need to set this variable, e.g. to $my_uri or "/".
+	See `$per_request_config` if you need to override it anyway.
+
+
+CONFIGURING GITWEB FEATURES
+---------------------------
+Many gitweb features can be enabled (or disabled) and configured using the
+`%feature` hash.  Names of gitweb features are keys of this hash.
+
+Each `%feature` hash element is a hash reference and has the following
+structure:
+----------------------------------------------------------------------
+"<feature_name>" => {
+	"sub" => <feature-sub (subroutine)>,
+	"override" => <allow-override (boolean)>,
+	"default" => [ <options>... ]
+},
+----------------------------------------------------------------------
+Some features cannot be overridden per project.  For those
+features the structure of appropriate `%feature` hash element has a simpler
+form:
+----------------------------------------------------------------------
+"<feature_name>" => {
+	"override" => 0,
+	"default" => [ <options>... ]
+},
+----------------------------------------------------------------------
+As one can see it lacks the \'sub' element.
+
+The meaning of each part of feature configuration is described
+below:
+
+default::
+	List (array reference) of feature parameters (if there are any),
+	used also to toggle (enable or disable) given feature.
++
+Note that it is currently *always* an array reference, even if
+feature doesn't accept any configuration parameters, and \'default'
+is used only to turn it on or off.  In such case you turn feature on
+by setting this element to `[1]`, and torn it off by setting it to
+`[0]`.  See also the passage about the "blame" feature in the "Examples"
+section.
++
+To disable features that accept parameters (are configurable), you
+need to set this element to empty list i.e. `[]`.
+
+override::
+	If this field has a true value then the given feature is
+	overriddable, which means that it can be configured
+	(or enabled/disabled) on a per-repository basis.
++
+Usually given "<feature>" is configurable via the `gitweb.<feature>`
+config variable in the per-repository git configuration file.
++
+*Note* that no feature is overriddable by default.
+
+sub::
+	Internal detail of implementation.  What is important is that
+	if this field is not present then per-repository override for
+	given feature is not supported.
++
+You wouldn't need to ever change it in gitweb config file.
+
+
+Features in `%feature`
+~~~~~~~~~~~~~~~~~~~~~~
+The gitweb features that are configurable via `%feature` hash are listed
+below.  This should be a complete list, but ultimately the authoritative
+and complete list is in gitweb.cgi source code, with features described
+in the comments.
+
+blame::
+	Enable the "blame" and "blame_incremental" blob views, showing for
+	each line the last commit that modified it; see linkgit:git-blame[1].
+	This can be very CPU-intensive and is therefore disabled by default.
++
+This feature can be configured on a per-repository basis via
+repository's `gitweb.blame` configuration variable (boolean).
+
+snapshot::
+	Enable and configure the "snapshot" action, which allows user to
+	download a compressed archive of any tree or commit, as produced
+	by linkgit:git-archive[1] and possibly additionally compressed.
+	This can potentially generate high traffic if you have large project.
++
+The value of \'default' is a list of names of snapshot formats,
+defined in `%known_snapshot_formats` hash, that you wish to offer.
+Supported formats include "tgz", "tbz2", "txz" (gzip/bzip2/xz
+compressed tar archive) and "zip"; please consult gitweb sources for
+a definitive list.  By default only "tgz" is offered.
++
+This feature can be configured on a per-repository basis via
+repository's `gitweb.blame` configuration variable, which contains
+a comma separated list of formats or "none" to disable snapshots.
+Unknown values are ignored.
+
+grep::
+	Enable grep search, which lists the files in currently selected
+	tree (directory) containing the given string; see linkgit:git-grep[1].
+	This can be potentially CPU-intensive, of course.  Enabled by default.
++
+This feature can be configured on a per-repository basis via
+repository's `gitweb.grep` configuration variable (boolean).
+
+pickaxe::
+	Enable the so called pickaxe search, which will list the commits
+	that introduced or removed a given string in a file.  This can be
+	practical and quite faster alternative to "blame" action, but it is
+	still potentially CPU-intensive.  Enabled by default.
++
+The pickaxe search is described in linkgit:git-log[1] (the
+description of `-S<string>` option, which refers to pickaxe entry in
+linkgit:gitdiffcore[7] for more details).
++
+This feature can be configured on a per-repository basis by setting
+repository's `gitweb.pickaxe` configuration variable (boolean).
+
+show-sizes::
+	Enable showing size of blobs (ordinary files) in a "tree" view, in a
+	separate column, similar to what `ls -l` does; see description of
+	`-l` option in linkgit:git-ls-tree[1] manpage.  This costs a bit of
+	I/O.  Enabled by default.
++
+This feature can be configured on a per-repository basis via
+repository's `gitweb.showsizes` configuration variable (boolean).
+
+patches::
+	Enable and configure "patches" view, which displays list of commits in email
+	(plain text) output format; see also linkgit:git-format-patch[1].
+	The value is the maximum number of patches in a patchset generated
+	in "patches" view.  Set the 'default' field to a list containing single
+	item of or to an empty list to disable patch view, or to a list
+	containing a single negative number to remove any limit.
+	Default value is 16.
++
+This feature can be configured on a per-repository basis via
+repository's `gitweb.patches` configuration variable (integer).
+
+avatar::
+	Avatar support.  When this feature is enabled, views such as
+	"shortlog" or "commit" will display an avatar associated with
+	the email of each committer and author.
++
+Currently available providers are *"gravatar"* and *"picon"*.
+Only one provider at a time can be selected ('default' is one element list).
+If an unknown provider is specified, the feature is disabled.
+*Note* that some providers might require extra Perl packages to be
+installed; see 'gitweb/INSTALL' for more details.
++
+This feature can be configured on a per-repository basis via
+repository's `gitweb.avatar` configuration variable.
++
+See also `%avatar_size` with pixel sizes for icons and avatars
+("default" is used for one-line like "log" and "shortlog", "double"
+is used for two-line like "commit", "commitdiff" or "tag").  If the
+default font sizes or lineheights are changed (e.g. via adding extra
+CSS stylesheet in `@stylesheets`), it may be appropriate to change
+these values.
+
+highlight::
+	Server-side syntax highlight support in "blob" view.  It requires
+	`$highlight_bin` program to be available (see the description of
+	this variable in the "Configuration variables" section above),
+	and therefore is disabled by default.
++
+This feature can be configured on a per-repository basis via
+repository's `gitweb.highlight` configuration variable (boolean).
+
+remote_heads::
+	Enable displaying remote heads (remote-tracking branches) in the "heads"
+	list.  In most cases the list of remote-tracking branches is an
+	unnecessary internal private detail, and this feature is therefore
+	disabled by default.  linkgit:git-instaweb[1], which is usually used
+	to browse local repositories, enables and uses this feature.
++
+This feature can be configured on a per-repository basis via
+repository's `gitweb.remote_heads` configuration variable (boolean).
+
+
+The remaining features cannot be overridden on a per project basis.
+
+search::
+	Enable text search, which will list the commits which match author,
+	committer or commit text to a given string; see the description of
+	`--author`, `--committer` and `--grep` options in linkgit:git-log[1]
+	manpage.  Enabled by default.
++
+Project specific override is not supported.
+
+forks::
+	If this feature is enabled, gitweb considers projects in
+	subdirectories of project root (basename) to be forks of existing
+	projects.  For each project +$projname.git+, projects in the
+	+$projname/+ directory and its subdirectories will not be
+	shown in the main projects list.  Instead, a \'\+' mark is shown
+	next to +$projname+, which links to a "forks" view that lists all
+	the forks (all projects in +$projname/+ subdirectory).  Additionally
+	a "forks" view for a project is linked from project summary page.
++
+If the project list is taken from a file (+$projects_list+ points to a
+file), forks are only recognized if they are listed after the main project
+in that file.
++
+Project specific override is not supported.
+
+actions::
+	Insert custom links to the action bar of all project pages.  This
+	allows you to link to third-party scripts integrating into gitweb.
++
+The "default" value consists of a list of triplets in the form
+`("<label>", "<link>", "<position>")` where "position" is the label
+after which to insert the link, "link" is a format string where `%n`
+expands to the project name, `%f` to the project path within the
+filesystem (i.e. "$projectroot/$project"), `%h` to the current hash
+(\'h' gitweb parameter) and `%b` to the current hash base
+(\'hb' gitweb parameter); `%%` expands to \'%'.
++
+For example, at the time this page was written, the http://repo.or.cz[]
+git hosting site set it to the following to enable graphical log
+(using the third party tool *git-browser*):
++
+----------------------------------------------------------------------
+$feature{'actions'}{'default'} =
+	[ ('graphiclog', '/git-browser/by-commit.html?r=%n', 'summary')];
+----------------------------------------------------------------------
++
+This adds a link titled "graphiclog" after the "summary" link, leading to
+`git-browser` script, passing `r=<project>` as a query parameter.
++
+Project specific override is not supported.
+
+timed::
+	Enable displaying how much time and how many git commands it took to
+	generate and display each page in the page footer (at the bottom of
+	page).  For example the footer might contain: "This page took 6.53325
+	seconds and 13 git commands to generate."  Disabled by default.
++
+Project specific override is not supported.
+
+javascript-timezone::
+	Enable and configure the ability to change a common timezone for dates
+	in gitweb output via JavaScript.  Dates in gitweb output include
+	authordate and committerdate in "commit", "commitdiff" and "log"
+	views, and taggerdate in "tag" view.  Enabled by default.
++
+The value is a list of three values: a default timezone (for if the client
+hasn't selected some other timezone and saved it in a cookie), a name of cookie
+where to store selected timezone, and a CSS class used to mark up
+dates for manipulation.  If you want to turn this feature off, set "default"
+to empty list: `[]`.
++
+Typical gitweb config files will only change starting (default) timezone,
+and leave other elements at their default values:
++
+---------------------------------------------------------------------------
+$feature{'javascript-timezone'}{'default'}[0] = "utc";
+---------------------------------------------------------------------------
++
+The example configuration presented here is guaranteed to be backwards
+and forward compatible.
++
+Timezone values can be "local" (for local timezone that browser uses), "utc"
+(what gitweb uses when JavaScript or this feature is disabled), or numerical
+timezones in the form of "+/-HHMM", such as "+0200".
++
+Project specific override is not supported.
+
+
+EXAMPLES
+--------
+
+To enable blame, pickaxe search, and snapshot support (allowing "tar.gz" and
+"zip" snapshots), while allowing individual projects to turn them off, put
+the following in your GITWEB_CONFIG file:
+
+	$feature{'blame'}{'default'} = [1];
+	$feature{'blame'}{'override'} = 1;
+
+	$feature{'pickaxe'}{'default'} = [1];
+	$feature{'pickaxe'}{'override'} = 1;
+
+	$feature{'snapshot'}{'default'} = ['zip', 'tgz'];
+	$feature{'snapshot'}{'override'} = 1;
+
+If you allow overriding for the snapshot feature, you can specify which
+snapshot formats are globally disabled. You can also add any command line
+options you want (such as setting the compression level). For instance, you
+can disable Zip compressed snapshots and set *gzip*(1) to run at level 6 by
+adding the following lines to your gitweb configuration file:
+
+	$known_snapshot_formats{'zip'}{'disabled'} = 1;
+	$known_snapshot_formats{'tgz'}{'compressor'} = ['gzip','-6'];
+
+ENVIRONMENT
+-----------
+The location of per-instance and system-wide configuration files can be
+overridden using the following environment variables:
+
+GITWEB_CONFIG::
+	Sets location of per-instance configuration file.
+GITWEB_CONFIG_SYSTEM::
+	Sets location of fallback system-wide configuration file.
+	This file is read only if per-instance one does not exist.
+GITWEB_CONFIG_COMMON::
+	Sets location of common system-wide configuration file.
+
+
+FILES
+-----
+gitweb_config.perl::
+	This is default name of per-instance configuration file.  The
+	format of this file is described above.
+/etc/gitweb.conf::
+	This is default name of fallback system-wide configuration
+	file.  This file is used only if per-instance configuration
+	variable is not found.
+/etc/gitweb-common.conf::
+	This is default name of common system-wide configuration
+	file.
+
+
+SEE ALSO
+--------
+linkgit:gitweb[1], linkgit:git-instaweb[1]
+
+'gitweb/README', 'gitweb/INSTALL'
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/gitweb.txt b/Documentation/gitweb.txt
new file mode 100644
index 0000000..168e8bf
--- /dev/null
+++ b/Documentation/gitweb.txt
@@ -0,0 +1,704 @@
+gitweb(1)
+=========
+
+NAME
+----
+gitweb - Git web interface (web frontend to Git repositories)
+
+SYNOPSIS
+--------
+To get started with gitweb, run linkgit:git-instaweb[1] from a git repository.
+This would configure and start your web server, and run web browser pointing to
+gitweb.
+
+
+DESCRIPTION
+-----------
+Gitweb provides a web interface to git repositories.  Its features include:
+
+* Viewing multiple Git repositories with common root.
+* Browsing every revision of the repository.
+* Viewing the contents of files in the repository at any revision.
+* Viewing the revision log of branches, history of files and directories,
+  see what was changed when, by who.
+* Viewing the blame/annotation details of any file (if enabled).
+* Generating RSS and Atom feeds of commits, for any branch.
+  The feeds are auto-discoverable in modern web browsers.
+* Viewing everything that was changed in a revision, and step through
+  revisions one at a time, viewing the history of the repository.
+* Finding commits which commit messages matches given search term.
+
+See http://git.kernel.org/?p=git/git.git;a=tree;f=gitweb[] or
+http://repo.or.cz/w/git.git/tree/HEAD:/gitweb/[] for gitweb source code,
+browsed using gitweb itself.
+
+
+CONFIGURATION
+-------------
+Various aspects of gitweb's behavior can be controlled through the configuration
+file 'gitweb_config.perl' or '/etc/gitweb.conf'.  See the linkgit:gitweb.conf[5]
+for details.
+
+Repositories
+~~~~~~~~~~~~
+Gitweb can show information from one or more Git repositories.  These
+repositories have to be all on local filesystem, and have to share common
+repository root, i.e. be all under a single parent repository (but see also
+"Advanced web server setup" section, "Webserver configuration with multiple
+projects' root" subsection).
+
+-----------------------------------------------------------------------
+our $projectroot = '/path/to/parent/directory';
+-----------------------------------------------------------------------
+
+The default value for `$projectroot` is '/pub/git'.  You can change it during
+building gitweb via `GITWEB_PROJECTROOT` build configuration variable.
+
+By default all git repositories under `$projectroot` are visible and available
+to gitweb.  The list of projects is generated by default by scanning the
+`$projectroot` directory for git repositories (for object databases to be
+more exact; gitweb is not interested in a working area, and is best suited
+to showing "bare" repositories).
+
+The name of the repository in gitweb is the path to its `$GIT_DIR` (its object
+database) relative to `$projectroot`.  Therefore the repository $repo can be
+found at "$projectroot/$repo".
+
+
+Projects list file format
+~~~~~~~~~~~~~~~~~~~~~~~~~
+Instead of having gitweb find repositories by scanning filesystem
+starting from $projectroot, you can provide a pre-generated list of
+visible projects by setting `$projects_list` to point to a plain text
+file with a list of projects (with some additional info).
+
+This file uses the following format:
+
+* One record (for project / repository) per line; does not support line
+continuation (newline escaping).
+
+* Leading and trailing whitespace are ignored.
+
+* Whitespace separated fields; any run of whitespace can be used as field
+separator (rules for Perl's "`split(" ", $line)`").
+
+* Fields use modified URI encoding, defined in RFC 3986, section 2.1
+(Percent-Encoding), or rather "Query string encoding" (see
+link:http://en.wikipedia.org/wiki/Query_string#URL_encoding[]), the difference
+being that SP (" ") can be encoded as "{plus}" (and therefore "{plus}" has to be
+also percent-encoded).
++
+Reserved characters are: "%" (used for encoding), "{plus}" (can be used to
+encode SPACE), all whitespace characters as defined in Perl, including SP,
+TAB and LF, (used to separate fields in a record).
+
+* Currently recognized fields are:
+<repository path>::
+	path to repository GIT_DIR, relative to `$projectroot`
+<repository owner>::
+	displayed as repository owner, preferably full name, or email,
+	or both
+
+You can generate the projects list index file using the project_index action
+(the 'TXT' link on projects list page) directly from gitweb; see also
+"Generating projects list using gitweb" section below.
+
+Example contents:
+-----------------------------------------------------------------------
+foo.git       Joe+R+Hacker+<joe@example.com>
+foo/bar.git   O+W+Ner+<owner@example.org>
+-----------------------------------------------------------------------
+
+
+By default this file controls only which projects are *visible* on projects
+list page (note that entries that do not point to correctly recognized git
+repositories won't be displayed by gitweb).  Even if a project is not
+visible on projects list page, you can view it nevertheless by hand-crafting
+a gitweb URL.  By setting `$strict_export` configuration variable (see
+linkgit:gitweb.conf[5]) to true value you can allow viewing only of
+repositories also shown on the overview page (i.e. only projects explicitly
+listed in projects list file will be accessible).
+
+
+Generating projects list using gitweb
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+We assume that GITWEB_CONFIG has its default Makefile value, namely
+'gitweb_config.perl'. Put the following in 'gitweb_make_index.perl' file:
+----------------------------------------------------------------------------
+read_config_file("gitweb_config.perl");
+$projects_list = $projectroot;
+----------------------------------------------------------------------------
+
+Then create the following script to get list of project in the format
+suitable for GITWEB_LIST build configuration variable (or
+`$projects_list` variable in gitweb config):
+
+----------------------------------------------------------------------------
+#!/bin/sh
+
+export GITWEB_CONFIG="gitweb_make_index.perl"
+export GATEWAY_INTERFACE="CGI/1.1"
+export HTTP_ACCEPT="*/*"
+export REQUEST_METHOD="GET"
+export QUERY_STRING="a=project_index"
+
+perl -- /var/www/cgi-bin/gitweb.cgi
+----------------------------------------------------------------------------
+
+Run this script and save its output to a file.  This file could then be used
+as projects list file, which means that you can set `$projects_list` to its
+filename.
+
+
+Controlling access to git repositories
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+By default all git repositories under `$projectroot` are visible and
+available to gitweb.  You can however configure how gitweb controls access
+to repositories.
+
+* As described in "Projects list file format" section, you can control which
+projects are *visible* by selectively including repositories in projects
+list file, and setting `$projects_list` gitweb configuration variable to
+point to it.  With `$strict_export` set, projects list file can be used to
+control which repositories are *available* as well.
+
+* You can configure gitweb to only list and allow viewing of the explicitly
+exported repositories, via `$export_ok` variable in gitweb config file; see
+linkgit:gitweb.conf[5] manpage.  If it evaluates to true, gitweb shows
+repositories only if this file named by `$export_ok` exists in its object
+database (if directory has the magic file named `$export_ok`).
++
+For example linkgit:git-daemon[1] by default (unless `--export-all` option
+is used) allows pulling only for those repositories that have
+'git-daemon-export-ok' file.  Adding
++
+--------------------------------------------------------------------------
+our $export_ok = "git-daemon-export-ok";
+--------------------------------------------------------------------------
++
+makes gitweb show and allow access only to those repositories that can be
+fetched from via `git://` protocol.
+
+* Finally, it is possible to specify an arbitrary perl subroutine that will
+be called for each repository to determine if it can be exported.  The
+subroutine receives an absolute path to the project (repository) as its only
+parameter (i.e. "$projectroot/$project").
++
+For example, if you use mod_perl to run the script, and have dumb
+HTTP protocol authentication configured for your repositories, you
+can use the following hook to allow access only if the user is
+authorized to read the files:
++
+--------------------------------------------------------------------------
+$export_auth_hook = sub {
+	use Apache2::SubRequest ();
+	use Apache2::Const -compile => qw(HTTP_OK);
+	my $path = "$_[0]/HEAD";
+	my $r    = Apache2::RequestUtil->request;
+	my $sub  = $r->lookup_file($path);
+	return $sub->filename eq $path
+	    && $sub->status == Apache2::Const::HTTP_OK;
+};
+--------------------------------------------------------------------------
+
+
+Per-repository gitweb configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+You can configure individual repositories shown in gitweb by creating file
+in the 'GIT_DIR' of git repository, or by setting some repo configuration
+variable (in 'GIT_DIR/config', see linkgit:git-config[1]).
+
+You can use the following files in repository:
+
+README.html::
+	A html file (HTML fragment) which is included on the gitweb project
+	"summary" page inside `<div>` block element. You can use it for longer
+	description of a project, to provide links (for example to project's
+	homepage), etc. This is recognized only if XSS prevention is off
+	(`$prevent_xss` is false, see linkgit:gitweb.conf[5]); a way to include
+	a README safely when XSS prevention is on may be worked out in the
+	future.
+
+description (or `gitweb.description`)::
+	Short (shortened to `$projects_list_description_width` in the projects
+	list page, which is 25 characters by default; see
+	linkgit:gitweb.conf[5]) single line description of a project (of a
+	repository).  Plain text file; HTML will be escaped.  By default set to
++
+-------------------------------------------------------------------------------
+Unnamed repository; edit this file to name it for gitweb.
+-------------------------------------------------------------------------------
++
+from the template during repository creation, usually installed in
+'/usr/share/git-core/templates/'.  You can use the `gitweb.description` repo
+configuration variable, but the file takes precedence.
+
+category (or `gitweb.category`)::
+	Singe line category of a project, used to group projects if
+	`$projects_list_group_categories` is enabled.  By default (file and
+	configuration variable absent), uncategorized projects are put in the
+	`$project_list_default_category` category.  You can use the
+	`gitweb.category` repo configuration variable, but the file takes
+	precedence.
++
+The configuration variables `$projects_list_group_categories` and
+`$project_list_default_category` are described in linkgit:gitweb.conf[5]
+
+cloneurl (or multiple-valued `gitweb.url`)::
+	File with repository URL (used for clone and fetch), one per line.
+	Displayed in the project summary page. You can use multiple-valued
+	`gitweb.url` repository configuration variable for that, but the file
+	takes precedence.
++
+This is per-repository enhancement / version of global prefix-based
+`@git_base_url_list` gitweb configuration variable (see
+linkgit:gitweb.conf[5]).
+
+gitweb.owner::
+	You can use the `gitweb.owner` repository configuration variable to set
+	repository's owner.  It is displayed in the project list and summary
+	page.
++
+If it's not set, filesystem directory's owner is used (via GECOS field,
+i.e. real name field from *getpwuid*(3)) if `$projects_list` is unset
+(gitweb scans `$projectroot` for repositories); if `$projects_list`
+points to file with list of repositories, then project owner defaults to
+value from this file for given repository.
+
+various `gitweb.*` config variables (in config)::
+	Read description of `%feature` hash for detailed list, and descriptions.
+	See also "Configuring gitweb features" section in linkgit:gitweb.conf[5]
+
+
+ACTIONS, AND URLS
+-----------------
+Gitweb can use path_info (component) based URLs, or it can pass all necessary
+information via query parameters.  The typical gitweb URLs are broken down in to
+five components:
+
+-----------------------------------------------------------------------
+.../gitweb.cgi/<repo>/<action>/<revision>:/<path>?<arguments>
+-----------------------------------------------------------------------
+
+repo::
+	The repository the action will be performed on.
++
+All actions except for those that list all available projects,
+in whatever form, require this parameter.
+
+action::
+	The action that will be run.  Defaults to 'projects_list' if repo
+	is not set, and to 'summary' otherwise.
+
+revision::
+	Revision shown.  Defaults to HEAD.
+
+path::
+	The path within the <repository> that the action is performed on,
+	for those actions that require it.
+
+arguments::
+	Any arguments that control the behaviour of the action.
+
+Some actions require or allow to specify two revisions, and sometimes even two
+pathnames.  In most general form such path_info (component) based gitweb URL
+looks like this:
+
+-----------------------------------------------------------------------
+.../gitweb.cgi/<repo>/<action>/<revision_from>:/<path_from>..<revision_to>:/<path_to>?<arguments>
+-----------------------------------------------------------------------
+
+
+Each action is implemented as a subroutine, and must be present in %actions
+hash.  Some actions are disabled by default, and must be turned on via feature
+mechanism.  For example to enable 'blame' view add the following to gitweb
+configuration file:
+
+-----------------------------------------------------------------------
+$feature{'blame'}{'default'} = [1];
+-----------------------------------------------------------------------
+
+
+Actions:
+~~~~~~~~
+The standard actions are:
+
+project_list::
+	Lists the available Git repositories.  This is the default command if no
+	repository is specified in the URL.
+
+summary::
+	Displays summary about given repository.  This is the default command if
+	no action is specified in URL, and only repository is specified.
+
+heads::
+remotes::
+	Lists all local or all remote-tracking branches in given repository.
++
+The latter is not available by default, unless configured.
+
+tags::
+	List all tags (lightweight and annotated) in given repository.
+
+blob::
+tree::
+	Shows the files and directories in a given repository path, at given
+	revision.  This is default command if no action is specified in the URL,
+	and path is given.
+
+blob_plain::
+	Returns the raw data for the file in given repository, at given path and
+	revision.  Links to this action are marked 'raw'.
+
+blobdiff::
+	Shows the difference between two revisions of the same file.
+
+blame::
+blame_incremental::
+	Shows the blame (also called annotation) information for a file. On a
+	per line basis it shows the revision in which that line was last changed
+	and the user that committed the change.  The incremental version (which
+	if configured is used automatically when JavaScript is enabled) uses
+	Ajax to incrementally add blame info to the contents of given file.
++
+This action is disabled by default for performance reasons.
+
+commit::
+commitdiff::
+	Shows information about a specific commit in a repository.  The 'commit'
+	view shows information about commit in more detail, the 'commitdiff'
+	action shows changeset for given commit.
+
+patch::
+	Returns the commit in plain text mail format, suitable for applying with
+	linkgit:git-am[1].
+
+tag::
+	Display specific annotated tag (tag object).
+
+log::
+shortlog::
+	Shows log information (commit message or just commit subject) for a
+	given branch (starting from given revision).
++
+The 'shortlog' view is more compact; it shows one commit per line.
+
+history::
+	Shows history of the file or directory in a given repository path,
+	starting from given revision (defaults to HEAD, i.e. default branch).
++
+This view is similar to 'shortlog' view.
+
+rss::
+atom::
+	Generates an RSS (or Atom) feed of changes to repository.
+
+
+WEBSERVER CONFIGURATION
+-----------------------
+This section explains how to configure some common webservers to run gitweb. In
+all cases, `/path/to/gitweb` in the examples is the directory you ran installed
+gitweb in, and contains `gitweb_config.perl`.
+
+If you've configured a web server that isn't listed here for gitweb, please send
+in the instructions so they can be included in a future release.
+
+Apache as CGI
+~~~~~~~~~~~~~
+Apache must be configured to support CGI scripts in the directory in
+which gitweb is installed.  Let's assume that it is '/var/www/cgi-bin'
+directory.
+
+-----------------------------------------------------------------------
+ScriptAlias /cgi-bin/ "/var/www/cgi-bin/"
+
+<Directory "/var/www/cgi-bin">
+    Options Indexes FollowSymlinks ExecCGI
+    AllowOverride None
+    Order allow,deny
+    Allow from all
+</Directory>
+-----------------------------------------------------------------------
+
+With that configuration the full path to browse repositories would be:
+
+  http://server/cgi-bin/gitweb.cgi
+
+Apache with mod_perl, via ModPerl::Registry
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+You can use mod_perl with gitweb.  You must install Apache::Registry
+(for mod_perl 1.x) or ModPerl::Registry (for mod_perl 2.x) to enable
+this support.
+
+Assuming that gitweb is installed to '/var/www/perl', the following
+Apache configuration (for mod_perl 2.x) is suitable.
+
+-----------------------------------------------------------------------
+Alias /perl "/var/www/perl"
+
+<Directory "/var/www/perl">
+    SetHandler perl-script
+    PerlResponseHandler ModPerl::Registry
+    PerlOptions +ParseHeaders
+    Options Indexes FollowSymlinks +ExecCGI
+    AllowOverride None
+    Order allow,deny
+    Allow from all
+</Directory>
+-----------------------------------------------------------------------
+
+With that configuration the full path to browse repositories would be:
+
+  http://server/perl/gitweb.cgi
+
+Apache with FastCGI
+~~~~~~~~~~~~~~~~~~~
+Gitweb works with Apache and FastCGI.  First you need to rename, copy
+or symlink gitweb.cgi to gitweb.fcgi.  Let's assume that gitweb is
+installed in '/usr/share/gitweb' directory.  The following Apache
+configuration is suitable (UNTESTED!)
+
+-----------------------------------------------------------------------
+FastCgiServer /usr/share/gitweb/gitweb.cgi
+ScriptAlias /gitweb /usr/share/gitweb/gitweb.cgi
+
+Alias /gitweb/static /usr/share/gitweb/static
+<Directory /usr/share/gitweb/static>
+    SetHandler default-handler
+</Directory>
+-----------------------------------------------------------------------
+
+With that configuration the full path to browse repositories would be:
+
+  http://server/gitweb
+
+
+ADVANCED WEB SERVER SETUP
+-------------------------
+All of those examples use request rewriting, and need `mod_rewrite`
+(or equivalent; examples below are written for Apache).
+
+Single URL for gitweb and for fetching
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+If you want to have one URL for both gitweb and your `http://`
+repositories, you can configure Apache like this:
+
+-----------------------------------------------------------------------
+<VirtualHost *:80>
+    ServerName    git.example.org
+    DocumentRoot  /pub/git
+    SetEnv        GITWEB_CONFIG   /etc/gitweb.conf
+
+    # turning on mod rewrite
+    RewriteEngine on
+
+    # make the front page an internal rewrite to the gitweb script
+    RewriteRule ^/$  /cgi-bin/gitweb.cgi
+
+    # make access for "dumb clients" work
+    RewriteRule ^/(.*\.git/(?!/?(HEAD|info|objects|refs)).*)?$ \
+		/cgi-bin/gitweb.cgi%{REQUEST_URI}  [L,PT]
+</VirtualHost>
+-----------------------------------------------------------------------
+
+The above configuration expects your public repositories to live under
+'/pub/git' and will serve them as `http://git.domain.org/dir-under-pub-git`,
+both as cloneable GIT URL and as browseable gitweb interface.  If you then
+start your linkgit:git-daemon[1] with `--base-path=/pub/git --export-all`
+then you can even use the `git://` URL with exactly the same path.
+
+Setting the environment variable `GITWEB_CONFIG` will tell gitweb to use the
+named file (i.e. in this example '/etc/gitweb.conf') as a configuration for
+gitweb.  You don't really need it in above example; it is required only if
+your configuration file is in different place than built-in (during
+compiling gitweb) 'gitweb_config.perl' or '/etc/gitweb.conf'.  See
+linkgit:gitweb.conf[5] for details, especially information about precedence
+rules.
+
+If you use the rewrite rules from the example you *might* also need
+something like the following in your gitweb configuration file
+('/etc/gitweb.conf' following example):
+----------------------------------------------------------------------------
+@stylesheets = ("/some/absolute/path/gitweb.css");
+$my_uri    = "/";
+$home_link = "/";
+$per_request_config = 1;
+----------------------------------------------------------------------------
+Nowadays though gitweb should create HTML base tag when needed (to set base
+URI for relative links), so it should work automatically.
+
+
+Webserver configuration with multiple projects' root
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+If you want to use gitweb with several project roots you can edit your
+Apache virtual host and gitweb configuration files in the following way.
+
+The virtual host configuration (in Apache configuration file) should look
+like this:
+--------------------------------------------------------------------------
+<VirtualHost *:80>
+    ServerName    git.example.org
+    DocumentRoot  /pub/git
+    SetEnv        GITWEB_CONFIG  /etc/gitweb.conf
+
+    # turning on mod rewrite
+    RewriteEngine on
+
+    # make the front page an internal rewrite to the gitweb script
+    RewriteRule ^/$  /cgi-bin/gitweb.cgi  [QSA,L,PT]
+
+    # look for a public_git folder in unix users' home
+    # http://git.example.org/~<user>/
+    RewriteRule ^/\~([^\/]+)(/|/gitweb.cgi)?$	/cgi-bin/gitweb.cgi \
+		[QSA,E=GITWEB_PROJECTROOT:/home/$1/public_git/,L,PT]
+
+    # http://git.example.org/+<user>/
+    #RewriteRule ^/\+([^\/]+)(/|/gitweb.cgi)?$	/cgi-bin/gitweb.cgi \
+		 [QSA,E=GITWEB_PROJECTROOT:/home/$1/public_git/,L,PT]
+
+    # http://git.example.org/user/<user>/
+    #RewriteRule ^/user/([^\/]+)/(gitweb.cgi)?$	/cgi-bin/gitweb.cgi \
+		 [QSA,E=GITWEB_PROJECTROOT:/home/$1/public_git/,L,PT]
+
+    # defined list of project roots
+    RewriteRule ^/scm(/|/gitweb.cgi)?$ /cgi-bin/gitweb.cgi \
+		[QSA,E=GITWEB_PROJECTROOT:/pub/scm/,L,PT]
+    RewriteRule ^/var(/|/gitweb.cgi)?$ /cgi-bin/gitweb.cgi \
+		[QSA,E=GITWEB_PROJECTROOT:/var/git/,L,PT]
+
+    # make access for "dumb clients" work
+    RewriteRule ^/(.*\.git/(?!/?(HEAD|info|objects|refs)).*)?$ \
+		/cgi-bin/gitweb.cgi%{REQUEST_URI}  [L,PT]
+</VirtualHost>
+--------------------------------------------------------------------------
+
+Here actual project root is passed to gitweb via `GITWEB_PROJECT_ROOT`
+environment variable from a web server, so you need to put the following
+line in gitweb configuration file ('/etc/gitweb.conf' in above example):
+--------------------------------------------------------------------------
+$projectroot = $ENV{'GITWEB_PROJECTROOT'} || "/pub/git";
+--------------------------------------------------------------------------
+*Note* that this requires to be set for each request, so either
+`$per_request_config` must be false, or the above must be put in code
+referenced by `$per_request_config`;
+
+These configurations enable two things. First, each unix user (`<user>`) of
+the server will be able to browse through gitweb git repositories found in
+'~/public_git/' with the following url:
+
+  http://git.example.org/~<user>/
+
+If you do not want this feature on your server just remove the second
+rewrite rule.
+
+If you already use `mod_userdir` in your virtual host or you don't want to
+use the \'~' as first character, just comment or remove the second rewrite
+rule, and uncomment one of the following according to what you want.
+
+Second, repositories found in '/pub/scm/' and '/var/git/' will be accessible
+through `http://git.example.org/scm/` and `http://git.example.org/var/`.
+You can add as many project roots as you want by adding rewrite rules like
+the third and the fourth.
+
+
+PATH_INFO usage
+~~~~~~~~~~~~~~~
+If you enable PATH_INFO usage in gitweb by putting
+----------------------------------------------------------------------------
+$feature{'pathinfo'}{'default'} = [1];
+----------------------------------------------------------------------------
+in your gitweb configuration file, it is possible to set up your server so
+that it consumes and produces URLs in the form
+
+  http://git.example.com/project.git/shortlog/sometag
+
+i.e. without 'gitweb.cgi' part, by using a configuration such as the
+following.  This configuration assumes that '/var/www/gitweb' is the
+DocumentRoot of your webserver, contains the gitweb.cgi script and
+complementary static files (stylesheet, favicon, JavaScript):
+
+----------------------------------------------------------------------------
+<VirtualHost *:80>
+	ServerAlias git.example.com
+
+	DocumentRoot /var/www/gitweb
+
+	<Directory /var/www/gitweb>
+		Options ExecCGI
+		AddHandler cgi-script cgi
+
+		DirectoryIndex gitweb.cgi
+
+		RewriteEngine On
+		RewriteCond %{REQUEST_FILENAME} !-f
+		RewriteCond %{REQUEST_FILENAME} !-d
+		RewriteRule ^.* /gitweb.cgi/$0 [L,PT]
+	</Directory>
+</VirtualHost>
+----------------------------------------------------------------------------
+The rewrite rule guarantees that existing static files will be properly
+served, whereas any other URL will be passed to gitweb as PATH_INFO
+parameter.
+
+*Notice* that in this case you don't need special settings for
+`@stylesheets`, `$my_uri` and `$home_link`, but you lose "dumb client"
+access to your project .git dirs (described in "Single URL for gitweb and
+for fetching" section).  A possible workaround for the latter is the
+following: in your project root dir (e.g. '/pub/git') have the projects
+named *without* a .git extension (e.g. '/pub/git/project' instead of
+'/pub/git/project.git') and configure Apache as follows:
+----------------------------------------------------------------------------
+<VirtualHost *:80>
+	ServerAlias git.example.com
+
+	DocumentRoot /var/www/gitweb
+
+	AliasMatch ^(/.*?)(\.git)(/.*)?$ /pub/git$1$3
+	<Directory /var/www/gitweb>
+		Options ExecCGI
+		AddHandler cgi-script cgi
+
+		DirectoryIndex gitweb.cgi
+
+		RewriteEngine On
+		RewriteCond %{REQUEST_FILENAME} !-f
+		RewriteCond %{REQUEST_FILENAME} !-d
+		RewriteRule ^.* /gitweb.cgi/$0 [L,PT]
+	</Directory>
+</VirtualHost>
+----------------------------------------------------------------------------
+
+The additional AliasMatch makes it so that
+
+  http://git.example.com/project.git
+
+will give raw access to the project's git dir (so that the project can be
+cloned), while
+
+  http://git.example.com/project
+
+will provide human-friendly gitweb access.
+
+This solution is not 100% bulletproof, in the sense that if some project has
+a named ref (branch, tag) starting with 'git/', then paths such as
+
+  http://git.example.com/project/command/abranch..git/abranch
+
+will fail with a 404 error.
+
+
+BUGS
+----
+Please report any bugs or feature requests to git@vger.kernel.org,
+putting "gitweb" in the subject of email.
+
+SEE ALSO
+--------
+linkgit:gitweb.conf[5], linkgit:git-instaweb[1]
+
+'gitweb/README', 'gitweb/INSTALL'
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/gitworkflows.txt b/Documentation/gitworkflows.txt
index 2b021e3..8b8c6ae 100644
--- a/Documentation/gitworkflows.txt
+++ b/Documentation/gitworkflows.txt
@@ -7,6 +7,7 @@
 
 SYNOPSIS
 --------
+[verse]
 git *
 
 
@@ -38,8 +39,8 @@
 beginning. It is always easier to squash a few commits together than
 to split one big commit into several.  Don't be afraid of making too
 small or imperfect steps along the way. You can always go back later
-and edit the commits with `git rebase \--interactive` before you
-publish them.  You can use `git stash save \--keep-index` to run the
+and edit the commits with `git rebase --interactive` before you
+publish them.  You can use `git stash save --keep-index` to run the
 test suite independent of other uncommitted changes; see the EXAMPLES
 section of linkgit:git-stash[1].
 
@@ -209,6 +210,121 @@
 has such an official throw-away integration branch called 'pu'.
 
 
+Branch management for a release
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Assuming you are using the merge approach discussed above, when you
+are releasing your project you will need to do some additional branch
+management work.
+
+A feature release is created from the 'master' branch, since 'master'
+tracks the commits that should go into the next feature release.
+
+The 'master' branch is supposed to be a superset of 'maint'. If this
+condition does not hold, then 'maint' contains some commits that
+are not included on 'master'. The fixes represented by those commits
+will therefore not be included in your feature release.
+
+To verify that 'master' is indeed a superset of 'maint', use git log:
+
+.Verify 'master' is a superset of 'maint'
+[caption="Recipe: "]
+=====================================
+`git log master..maint`
+=====================================
+
+This command should not list any commits.  Otherwise, check out
+'master' and merge 'maint' into it.
+
+Now you can proceed with the creation of the feature release. Apply a
+tag to the tip of 'master' indicating the release version:
+
+.Release tagging
+[caption="Recipe: "]
+=====================================
+`git tag -s -m "GIT X.Y.Z" vX.Y.Z master`
+=====================================
+
+You need to push the new tag to a public git server (see
+"DISTRIBUTED WORKFLOWS" below). This makes the tag available to
+others tracking your project. The push could also trigger a
+post-update hook to perform release-related items such as building
+release tarballs and preformatted documentation pages.
+
+Similarly, for a maintenance release, 'maint' is tracking the commits
+to be released. Therefore, in the steps above simply tag and push
+'maint' rather than 'master'.
+
+
+Maintenance branch management after a feature release
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+After a feature release, you need to manage your maintenance branches.
+
+First, if you wish to continue to release maintenance fixes for the
+feature release made before the recent one, then you must create
+another branch to track commits for that previous release.
+
+To do this, the current maintenance branch is copied to another branch
+named with the previous release version number (e.g. maint-X.Y.(Z-1)
+where X.Y.Z is the current release).
+
+.Copy maint
+[caption="Recipe: "]
+=====================================
+`git branch maint-X.Y.(Z-1) maint`
+=====================================
+
+The 'maint' branch should now be fast-forwarded to the newly released
+code so that maintenance fixes can be tracked for the current release:
+
+.Update maint to new release
+[caption="Recipe: "]
+=====================================
+* `git checkout maint`
+* `git merge --ff-only master`
+=====================================
+
+If the merge fails because it is not a fast-forward, then it is
+possible some fixes on 'maint' were missed in the feature release.
+This will not happen if the content of the branches was verified as
+described in the previous section.
+
+
+Branch management for next and pu after a feature release
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+After a feature release, the integration branch 'next' may optionally be
+rewound and rebuilt from the tip of 'master' using the surviving
+topics on 'next':
+
+.Rewind and rebuild next
+[caption="Recipe: "]
+=====================================
+* `git checkout next`
+* `git reset --hard master`
+* `git merge ai/topic_in_next1`
+* `git merge ai/topic_in_next2`
+* ...
+=====================================
+
+The advantage of doing this is that the history of 'next' will be
+clean. For example, some topics merged into 'next' may have initially
+looked promising, but were later found to be undesirable or premature.
+In such a case, the topic is reverted out of 'next' but the fact
+remains in the history that it was once merged and reverted. By
+recreating 'next', you give another incarnation of such topics a clean
+slate to retry, and a feature release is a good point in history to do
+so.
+
+If you do this, then you should make a public announcement indicating
+that 'next' was rewound and rebuilt.
+
+The same rewind and rebuild process may be followed for 'pu'. A public
+announcement is not necessary since 'pu' is a throw-away branch, as
+described above.
+
+
 DISTRIBUTED WORKFLOWS
 ---------------------
 
@@ -245,7 +361,7 @@
 
 * linkgit:git-pull[1] that does fetch and merge in one go.
 
-Note the last point.  Do 'not' use 'git-pull' unless you actually want
+Note the last point.  Do 'not' use 'git pull' unless you actually want
 to merge the remote branch.
 
 Getting changes out is easy:
@@ -282,7 +398,7 @@
     <url> <branch>
 -------------------------------------
 
-In that case, 'git-pull' can do the fetch and merge in one go, as
+In that case, 'git pull' can do the fetch and merge in one go, as
 follows.
 
 .Push/pull: Merging remote topics
@@ -334,7 +450,7 @@
 
 If you receive such a patch series (as maintainer, or perhaps as a
 reader of the mailing list it was sent to), save the mails to files,
-create a new topic branch and use 'git-am' to import the commits:
+create a new topic branch and use 'git am' to import the commits:
 
 .format-patch/am: Importing patches
 [caption="Recipe: "]
diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt
index 572374f..f928b57 100644
--- a/Documentation/glossary-content.txt
+++ b/Documentation/glossary-content.txt
@@ -117,21 +117,21 @@
 
 [[def_ent]]ent::
 	Favorite synonym to "<<def_tree-ish,tree-ish>>" by some total geeks. See
-	`http://en.wikipedia.org/wiki/Ent_(Middle-earth)` for an in-depth
+	http://en.wikipedia.org/wiki/Ent_(Middle-earth) for an in-depth
 	explanation. Avoid this term, not to confuse people.
 
 [[def_evil_merge]]evil merge::
 	An evil merge is a <<def_merge,merge>> that introduces changes that
 	do not appear in any <<def_parent,parent>>.
 
-[[def_fast_forward]]fast forward::
+[[def_fast_forward]]fast-forward::
 	A fast-forward is a special type of <<def_merge,merge>> where you have a
 	<<def_revision,revision>> and you are "merging" another
 	<<def_branch,branch>>'s changes that happen to be a descendant of what
 	you have. In such these cases, you do not make a new <<def_merge,merge>>
 	<<def_commit,commit>> but instead just update to his
 	revision. This will happen frequently on a
-	<<def_tracking_branch,tracking branch>> of a remote
+	<<def_remote_tracking_branch,remote-tracking branch>> of a remote
 	<<def_repository,repository>>.
 
 [[def_fetch]]fetch::
@@ -161,8 +161,8 @@
 
 [[def_head]]head::
 	A <<def_ref,named reference>> to the <<def_commit,commit>> at the tip of a
-	<<def_branch,branch>>.  Heads are stored in
-	`$GIT_DIR/refs/heads/`, except when using packed refs. (See
+	<<def_branch,branch>>.  Heads are stored in a file in
+	`$GIT_DIR/refs/heads/` directory, except when using packed refs. (See
 	linkgit:git-pack-refs[1].)
 
 [[def_HEAD]]HEAD::
@@ -170,8 +170,8 @@
 	working tree>> is normally derived from the state of the tree
 	referred to by HEAD.  HEAD is a reference to one of the
 	<<def_head,heads>> in your repository, except when using a
-	<<def_detached_HEAD,detached HEAD>>, in which case it may
-	reference an arbitrary commit.
+	<<def_detached_HEAD,detached HEAD>>, in which case it directly
+	references an arbitrary commit.
 
 [[def_head_ref]]head ref::
 	A synonym for <<def_head,head>>.
@@ -220,7 +220,7 @@
 	conflict, manual intervention may be required to complete the
 	merge.
 +
-As a noun: unless it is a <<def_fast_forward,fast forward>>, a
+As a noun: unless it is a <<def_fast_forward,fast-forward>>, a
 successful merge results in the creation of a new <<def_commit,commit>>
 representing the result of the merge, and having as
 <<def_parent,parents>> the tips of the merged <<def_branch,branches>>.
@@ -260,7 +260,7 @@
 	The default upstream <<def_repository,repository>>. Most projects have
 	at least one upstream project which they track. By default
 	'origin' is used for that purpose. New upstream updates
-	will be fetched into remote <<def_tracking_branch,tracking branches>> named
+	will be fetched into remote <<def_remote_tracking_branch,remote-tracking branches>> named
 	origin/name-of-upstream-branch, which you can see using
 	`git branch -r`.
 
@@ -273,6 +273,61 @@
 	<<def_pack,pack>>, to assist in efficiently accessing the contents of a
 	pack.
 
+[[def_pathspec]]pathspec::
+       Pattern used to specify paths.
++
+Pathspecs are used on the command line of "git ls-files", "git
+ls-tree", "git add", "git grep", "git diff", "git checkout",
+and many other commands to
+limit the scope of operations to some subset of the tree or
+worktree.  See the documentation of each command for whether
+paths are relative to the current directory or toplevel.  The
+pathspec syntax is as follows:
+
+* any path matches itself
+* the pathspec up to the last slash represents a
+  directory prefix.  The scope of that pathspec is
+  limited to that subtree.
+* the rest of the pathspec is a pattern for the remainder
+  of the pathname.  Paths relative to the directory
+  prefix will be matched against that pattern using fnmatch(3);
+  in particular, '*' and '?' _can_ match directory separators.
++
+For example, Documentation/*.jpg will match all .jpg files
+in the Documentation subtree,
+including Documentation/chapter_1/figure_1.jpg.
+
++
+A pathspec that begins with a colon `:` has special meaning.  In the
+short form, the leading colon `:` is followed by zero or more "magic
+signature" letters (which optionally is terminated by another colon `:`),
+and the remainder is the pattern to match against the path. The optional
+colon that terminates the "magic signature" can be omitted if the pattern
+begins with a character that cannot be a "magic signature" and is not a
+colon.
++
+In the long form, the leading colon `:` is followed by a open
+parenthesis `(`, a comma-separated list of zero or more "magic words",
+and a close parentheses `)`, and the remainder is the pattern to match
+against the path.
++
+The "magic signature" consists of an ASCII symbol that is not
+alphanumeric.
++
+--
+top `/`;;
+	The magic word `top` (mnemonic: `/`) makes the pattern match
+	from the root of the working tree, even when you are running
+	the command from inside a subdirectory.
+--
++
+Currently only the slash `/` is recognized as the "magic signature",
+but it is envisioned that we will support more types of magic in later
+versions of git.
++
+A pathspec with only a colon means "there is no pathspec". This form
+should not be combined with other pathspec.
+
 [[def_parent]]parent::
 	A <<def_commit_object,commit object>> contains a (possibly empty) list
 	of the logical predecessor(s) in the line of development, i.e. its
@@ -327,8 +382,9 @@
 
 [[def_ref]]ref::
 	A 40-byte hex representation of a <<def_SHA1,SHA1>> or a name that
-	denotes a particular <<def_object,object>>. These may be stored in
-	`$GIT_DIR/refs/`.
+	denotes a particular <<def_object,object>>. They may be stored in
+	a file under `$GIT_DIR/refs/` directory, or
+	in the `$GIT_DIR/packed-refs` file.
 
 [[def_reflog]]reflog::
 	A reflog shows the local "history" of a ref.  In other words,
@@ -349,6 +405,14 @@
 	master branch head as to-upstream branch at $URL". See also
 	linkgit:git-push[1].
 
+[[def_remote_tracking_branch]]remote-tracking branch::
+	A regular git <<def_branch,branch>> that is used to follow changes from
+	another <<def_repository,repository>>. A remote-tracking
+	branch should not contain direct modifications or have local commits
+	made to it. A remote-tracking branch can usually be
+	identified as the right-hand-side <<def_ref,ref>> in a Pull:
+	<<def_refspec,refspec>>.
+
 [[def_repository]]repository::
 	A collection of <<def_ref,refs>> together with an
 	<<def_object_database,object database>> containing all objects
@@ -396,14 +460,14 @@
 	command.
 
 [[def_tag]]tag::
-	A <<def_ref,ref>> pointing to a <<def_tag_object,tag>> or
-	<<def_commit_object,commit object>>. In contrast to a <<def_head,head>>,
-	a tag is not changed by a <<def_commit,commit>>. Tags (not
-	<<def_tag_object,tag objects>>) are stored in `$GIT_DIR/refs/tags/`. A
-	git tag has nothing to do with a Lisp tag (which would be
-	called an <<def_object_type,object type>> in git's context). A
-	tag is most typically used to mark a particular point in the
-	commit ancestry <<def_chain,chain>>.
+	A <<def_ref,ref>> under `refs/tags/` namespace that points to an
+	object of an arbitrary type (typically a tag points to either a
+	<<def_tag_object,tag>> or a <<def_commit_object,commit object>>).
+	In contrast to a <<def_head,head>>, a tag is not updated by
+	the `commit` command. A git tag has nothing to do with a Lisp
+	tag (which would be called an <<def_object_type,object type>>
+	in git's context). A tag is most typically used to mark a particular
+	point in the commit ancestry <<def_chain,chain>>.
 
 [[def_tag_object]]tag object::
 	An <<def_object,object>> containing a <<def_ref,ref>> pointing to
@@ -418,14 +482,6 @@
 	that each contain very well defined concepts or small incremental yet
 	related changes.
 
-[[def_tracking_branch]]tracking branch::
-	A regular git <<def_branch,branch>> that is used to follow changes from
-	another <<def_repository,repository>>. A tracking
-	branch should not contain direct modifications or have local commits
-	made to it. A tracking branch can usually be
-	identified as the right-hand-side <<def_ref,ref>> in a Pull:
-	<<def_refspec,refspec>>.
-
 [[def_tree]]tree::
 	Either a <<def_working_tree,working tree>>, or a <<def_tree_object,tree
 	object>> together with the dependent <<def_blob_object,blob>> and tree objects
@@ -456,6 +512,6 @@
 	of 'A' is 'origin/B' sometimes we say "'A' is tracking 'origin/B'".
 
 [[def_working_tree]]working tree::
-	The tree of actual checked out files.  The working tree is
-	normally equal to the <<def_HEAD,HEAD>> plus any local changes
-	that you have made but not yet committed.
+	The tree of actual checked out files.  The working tree normally
+	contains the contents of the <<def_HEAD,HEAD>> commit's tree,
+	plus any local changes that you have made but not yet committed.
diff --git a/Documentation/howto/maintain-git.txt b/Documentation/howto/maintain-git.txt
index 4357e26..8823a37 100644
--- a/Documentation/howto/maintain-git.txt
+++ b/Documentation/howto/maintain-git.txt
@@ -59,7 +59,7 @@
    not yet pass the criteria set for 'next'.
 
  - The tips of 'master', 'maint' and 'next' branches will always
-   fast forward, to allow people to build their own
+   fast-forward, to allow people to build their own
    customization on top of them.
 
  - Usually 'master' contains all of 'maint', 'next' contains all
@@ -176,7 +176,7 @@
  - Update "What's cooking" message to review the updates to
    existing topics, newly added topics and graduated topics.
 
-   This step is helped with Meta/UWC script (where Meta/ contains
+   This step is helped with Meta/cook script (where Meta/ contains
    a checkout of the 'todo' branch).
 
  - Merge topics to 'next'.  For each branch whose tip is not
@@ -197,10 +197,9 @@
 
    - Nothing is next-worthy; do not do anything.
 
- - Rebase topics that do not have any commit in next yet.  This
-   step is optional but sometimes is worth doing when an old
-   series that is not in next can take advantage of low-level
-   framework change that is merged to 'master' already.
+ - [** OBSOLETE **] Optionally rebase topics that do not have any commit
+   in next yet, when they can take advantage of low-level framework
+   change that is merged to 'master' already.
 
      $ git rebase master ai/topic
 
@@ -209,7 +208,7 @@
    pre-rebase hook to make sure that topics that are already in
    'next' are not rebased beyond the merged commit.
 
- - Rebuild "pu" to merge the tips of topics not in 'next'.
+ - [** OBSOLETE **] Rebuild "pu" to merge the tips of topics not in 'next'.
 
      $ git checkout pu
      $ git reset --hard next
@@ -241,7 +240,7 @@
 
  - Fetch html and man branches back from k.org, and push four
    integration branches and the two documentation branches to
-   repo.or.cz
+   repo.or.cz and other mirrors.
 
 
 Some observations to be made.
diff --git a/Documentation/howto/revert-a-faulty-merge.txt b/Documentation/howto/revert-a-faulty-merge.txt
index 3b4a390..6fd7119 100644
--- a/Documentation/howto/revert-a-faulty-merge.txt
+++ b/Documentation/howto/revert-a-faulty-merge.txt
@@ -142,6 +142,8 @@
    revert of a merge was rebuilt from scratch (i.e. rebasing and fixing,
    as you seem to have interpreted), then re-merging the result without
    doing anything else fancy would be the right thing to do.
+   (See the ADDENDUM below for how to rebuild a branch from scratch
+   without changing its original branching-off point.)
 
 However, there are things to keep in mind when reverting a merge (and
 reverting such a revert).
@@ -177,3 +179,91 @@
 ready yet, and I really need to undo _all_ of the merge"). So then you
 really should revert the merge, but when you want to re-do the merge, you
 now need to do it by reverting the revert.
+
+ADDENDUM
+
+Sometimes you have to rewrite one of a topic branch's commits *and* you can't
+change the topic's branching-off point.  Consider the following situation:
+
+ P---o---o---M---x---x---W---x
+  \         /
+   A---B---C
+
+where commit W reverted commit M because it turned out that commit B was wrong
+and needs to be rewritten, but you need the rewritten topic to still branch
+from commit P (perhaps P is a branching-off point for yet another branch, and
+you want be able to merge the topic into both branches).
+
+The natural thing to do in this case is to checkout the A-B-C branch and use
+"rebase -i P" to change commit B.  However this does not rewrite commit A,
+because "rebase -i" by default fast-forwards over any initial commits selected
+with the "pick" command.  So you end up with this:
+
+ P---o---o---M---x---x---W---x
+  \         /
+   A---B---C   <-- old branch
+    \
+     B'---C'   <-- naively rewritten branch
+
+To merge A-B'-C' into the mainline branch you would still have to first revert
+commit W in order to pick up the changes in A, but then it's likely that the
+changes in B' will conflict with the original B changes re-introduced by the
+reversion of W.
+
+However, you can avoid these problems if you recreate the entire branch,
+including commit A:
+
+   A'---B'---C'  <-- completely rewritten branch
+  /
+ P---o---o---M---x---x---W---x
+  \         /
+   A---B---C
+
+You can merge A'-B'-C' into the mainline branch without worrying about first
+reverting W.  Mainline's history would look like this:
+
+   A'---B'---C'------------------
+  /                              \
+ P---o---o---M---x---x---W---x---M2
+  \         /
+   A---B---C
+
+But if you don't actually need to change commit A, then you need some way to
+recreate it as a new commit with the same changes in it.  The rebase command's
+--no-ff option provides a way to do this:
+
+    $ git rebase [-i] --no-ff P
+
+The --no-ff option creates a new branch A'-B'-C' with all-new commits (all the
+SHA IDs will be different) even if in the interactive case you only actually
+modify commit B.  You can then merge this new branch directly into the mainline
+branch and be sure you'll get all of the branch's changes.
+
+You can also use --no-ff in cases where you just add extra commits to the topic
+to fix it up.  Let's revisit the situation discussed at the start of this howto:
+
+ P---o---o---M---x---x---W---x
+  \         /
+   A---B---C----------------D---E   <-- fixed-up topic branch
+
+At this point, you can use --no-ff to recreate the topic branch:
+
+    $ git checkout E
+    $ git rebase --no-ff P
+
+yielding
+
+   A'---B'---C'------------D'---E'  <-- recreated topic branch
+  /
+ P---o---o---M---x---x---W---x
+  \         /
+   A---B---C----------------D---E
+
+You can merge the recreated branch into the mainline without reverting commit W,
+and mainline's history will look like this:
+
+   A'---B'---C'------------D'---E'
+  /                              \
+ P---o---o---M---x---x---W---x---M2
+  \         /
+   A---B---C
diff --git a/Documentation/howto/revert-branch-rebase.txt b/Documentation/howto/revert-branch-rebase.txt
index e70d8a3..093c656 100644
--- a/Documentation/howto/revert-branch-rebase.txt
+++ b/Documentation/howto/revert-branch-rebase.txt
@@ -85,7 +85,7 @@
 
 ------------------------------------------------
 $ git checkout master
-$ git merge revert-c99 ;# this should be a fast forward
+$ git merge revert-c99 ;# this should be a fast-forward
 Updating from 10d781b9caa4f71495c7b34963bef137216f86a8 to e3a693c...
  cache.h        |    8 ++++----
  commit.c       |    2 +-
@@ -95,7 +95,7 @@
  5 files changed, 8 insertions(+), 8 deletions(-)
 ------------------------------------------------
 
-There is no need to redo the test at this point.  We fast forwarded
+There is no need to redo the test at this point.  We fast-forwarded
 and we know 'master' matches 'revert-c99' exactly.  In fact:
 
 ------------------------------------------------
@@ -112,25 +112,19 @@
 $ git rebase master
 * Applying: Redo "revert" using three-way merge machinery.
 First trying simple merge strategy to cherry-pick.
-Finished one cherry-pick.
 * Applying: Remove git-apply-patch-script.
 First trying simple merge strategy to cherry-pick.
 Simple cherry-pick fails; trying Automatic cherry-pick.
 Removing Documentation/git-apply-patch-script.txt
 Removing git-apply-patch-script
-Finished one cherry-pick.
 * Applying: Document "git cherry-pick" and "git revert"
 First trying simple merge strategy to cherry-pick.
-Finished one cherry-pick.
 * Applying: mailinfo and applymbox updates
 First trying simple merge strategy to cherry-pick.
-Finished one cherry-pick.
 * Applying: Show commits in topo order and name all commits.
 First trying simple merge strategy to cherry-pick.
-Finished one cherry-pick.
 * Applying: More documentation updates.
 First trying simple merge strategy to cherry-pick.
-Finished one cherry-pick.
 ------------------------------------------------
 
 The temporary tag 'pu-anchor' is me just being careful, in case 'git
diff --git a/Documentation/howto/update-hook-example.txt b/Documentation/howto/update-hook-example.txt
index 697d918..b7f8d41 100644
--- a/Documentation/howto/update-hook-example.txt
+++ b/Documentation/howto/update-hook-example.txt
@@ -76,7 +76,7 @@
     if expr "$2" : '0*$' >/dev/null; then
       info "The branch '$1' is new..."
     else
-      # updating -- make sure it is a fast forward
+      # updating -- make sure it is a fast-forward
       mb=$(git-merge-base "$2" "$3")
       case "$mb,$2" in
         "$2,$mb") info "Update is fast-forward" ;;
diff --git a/Documentation/howto/using-merge-subtree.txt b/Documentation/howto/using-merge-subtree.txt
index 0953a50..1ae8d12 100644
--- a/Documentation/howto/using-merge-subtree.txt
+++ b/Documentation/howto/using-merge-subtree.txt
@@ -25,7 +25,7 @@
 situation.
 
 In this example, let's say you have the repository at `/path/to/B` (but
-it can be an URL as well, if you want). You want to merge the 'master'
+it can be a URL as well, if you want). You want to merge the 'master'
 branch of that repository to the `dir-B` subdirectory in your current
 branch.
 
@@ -71,5 +71,5 @@
   relevant parts of your tree.
 
 - Please note that if the other project merges from you, then it will
-  connects its history to yours, which can be something they don't want
+  connect its history to yours, which can be something they don't want
   to.
diff --git a/Documentation/howto/using-signed-tag-in-pull-request.txt b/Documentation/howto/using-signed-tag-in-pull-request.txt
new file mode 100644
index 0000000..98c0033
--- /dev/null
+++ b/Documentation/howto/using-signed-tag-in-pull-request.txt
@@ -0,0 +1,217 @@
+From: Junio C Hamano <gitster@pobox.com>
+Date: Tue, 17 Jan 2011 13:00:00 -0800
+Subject: Using signed tag in pull requests
+Abstract: Beginning v1.7.9, a contributor can push a signed tag to her
+ publishing repository and ask her integrator to pull it. This assures the
+ integrator that the pulled history is authentic and allows others to
+ later validate it.
+Content-type: text/asciidoc
+
+Using signed tag in pull requests
+=================================
+
+A typical distributed workflow using Git is for a contributor to fork a
+project, build on it, publish the result to her public repository, and ask
+the "upstream" person (often the owner of the project where she forked
+from) to pull from her public repository. Requesting such a "pull" is made
+easy by the `git request-pull` command.
+
+Earlier, a typical pull request may have started like this:
+
+------------
+ The following changes since commit 406da78032179...:
+
+   Froboz 3.2 (2011-09-30 14:20:57 -0700)
+
+ are available in the git repository at:
+
+   example.com:/git/froboz.git for-xyzzy
+------------
+
+followed by a shortlog of the changes and a diffstat.
+
+The request was for a branch name (e.g. `for-xyzzy`) in the public
+repository of the contributor, and even though it stated where the
+contributor forked her work from, the message did not say anything about
+the commit to expect at the tip of the for-xyzzy branch. If the site that
+hosts the public repository of the contributor cannot be fully trusted, it
+was unnecessarily hard to make sure what was pulled by the integrator was
+genuinely what the contributor had produced for the project. Also there
+was no easy way for third-party auditors to later verify the resulting
+history.
+
+Starting from Git release v1.7.9, a contributor can add a signed tag to
+the commit at the tip of the history and ask the integrator to pull that
+signed tag. When the integrator runs `git pull`, the signed tag is
+automatically verified to assure that the history is not tampered with.
+In addition, the resulting merge commit records the content of the signed
+tag, so that other people can verify that the branch merged by the
+integrator was signed by the contributor, without fetching the signed tag
+used to validate the pull request separately and keeping it in the refs
+namespace.
+
+This document describes the workflow between the contributor and the
+integrator, using Git v1.7.9 or later.
+
+
+A contributor or a lieutenant
+-----------------------------
+
+After preparing her work to be pulled, the contributor uses `git tag -s`
+to create a signed tag:
+
+------------
+ $ git checkout work
+ $ ... "git pull" from sublieutenants, "git commit" your own work ...
+ $ git tag -s -m "Completed frotz feature" frotz-for-xyzzy work
+------------
+
+Note that this example uses the `-m` option to create a signed tag with
+just a one-liner message, but this is for illustration purposes only. It
+is advisable to compose a well-written explanation of what the topic does
+to justify why it is worthwhile for the integrator to pull it, as this
+message will eventually become part of the final history after the
+integrator responds to the pull request (as we will see later).
+
+Then she pushes the tag out to her public repository:
+
+------------
+ $ git push example.com:/git/froboz.git/ +frotz-for-xyzzy
+------------
+
+There is no need to push the `work` branch or anything else.
+
+Note that the above command line used a plus sign at the beginning of
+`+frotz-for-xyzzy` to allow forcing the update of a tag, as the same
+contributor may want to reuse a signed tag with the same name after the
+previous pull request has already been responded to.
+
+The contributor then prepares a message to request a "pull":
+
+------------
+ $ git request-pull v3.2 example.com:/git/froboz.git/ frotz-for-xyzzy >msg.txt
+------------
+
+The arguments are:
+
+. the version of the integrator's commit the contributor based her work on;
+. the URL of the repository, to which the contributor has pushed what she
+  wants to get pulled; and
+. the name of the tag the contributor wants to get pulled (earlier, she could
+  write only a branch name here).
+
+The resulting msg.txt file begins like so:
+
+------------
+ The following changes since commit 406da78032179...:
+
+   Froboz 3.2 (2011-09-30 14:20:57 -0700)
+
+ are available in the git repository at:
+
+   example.com:/git/froboz.git tags/frotz-for-xyzzy
+
+ for you to fetch changes up to 703f05ad5835c...:
+
+   Add tests and documentation for frotz (2011-12-02 10:02:52 -0800)
+
+ -----------------------------------------------
+ Completed frotz feature
+ -----------------------------------------------
+------------
+
+followed by a shortlog of the changes and a diffstat.  Comparing this with
+the earlier illustration of the output from the traditional `git request-pull`
+command, the reader should notice that:
+
+. The tip commit to expect is shown to the integrator; and
+. The signed tag message is shown prominently between the dashed lines
+  before the shortlog.
+
+The latter is why the contributor would want to justify why pulling her
+work is worthwhile when creating the signed tag.  The contributor then
+opens her favorite MUA, reads msg.txt, edits and sends it to her upstream
+integrator.
+
+
+Integrator
+----------
+
+After receiving such a pull request message, the integrator fetches and
+integrates the tag named in the request, with:
+
+------------
+ $ git pull example.com:/git/froboz.git/ tags/frotz-for-xyzzy
+------------
+
+This operation will always open an editor to allow the integrator to fine
+tune the commit log message when merging a signed tag.  Also, pulling a
+signed tag will always create a merge commit even when the integrator does
+not have any new commit since the contributor's work forked (i.e. 'fast
+forward'), so that the integrator can properly explain what the merge is
+about and why it was made.
+
+In the editor, the integrator will see something like this:
+
+------------
+ Merge tag 'frotz-for-xyzzy' of example.com:/git/froboz.git/
+
+ Completed frotz feature
+ # gpg: Signature made Fri 02 Dec 2011 10:03:01 AM PST using RSA key ID 96AFE6CB
+ # gpg: Good signature from "Con Tributor <nitfol@example.com>"
+------------
+
+Notice that the message recorded in the signed tag "Completed frotz
+feature" appears here, and again that is why it is important for the
+contributor to explain her work well when creating the signed tag.
+
+As usual, the lines commented with `#` are stripped out. The resulting
+commit records the signed tag used for this validation in a hidden field
+so that it can later be used by others to audit the history. There is no
+need for the integrator to keep a separate copy of the tag in his
+repository (i.e. `git tag -l` won't list the `frotz-for-xyzzy` tag in the
+above example), and there is no need to publish the tag to his public
+repository, either.
+
+After the integrator responds to the pull request and her work becomes
+part of the permanent history, the contributor can remove the tag from
+her public repository, if she chooses, in order to keep the tag namespace
+of her public repository clean, with:
+
+------------
+ $ git push example.com:/git/froboz.git :frotz-for-xyzzy
+------------
+
+
+Auditors
+--------
+
+The `--show-signature` option can be given to `git log` or `git show` and
+shows the verification status of the embedded signed tag in merge commits
+created when the integrator responded to a pull request of a signed tag.
+
+A typical output from `git show --show-signature` may look like this:
+
+------------
+ $ git show --show-signature
+ commit 02306ef6a3498a39118aef9df7975bdb50091585
+ merged tag 'frotz-for-xyzzy'
+ gpg: Signature made Fri 06 Jan 2012 12:41:49 PM PST using RSA key ID 96AFE6CB
+ gpg: Good signature from "Con Tributor <nitfol@example.com>"
+ Merge: 406da78 703f05a
+ Author: Inte Grator <xyzzy@example.com>
+ Date:   Tue Jan 17 13:49:41 2012 -0800
+
+     Merge tag 'frotz-for-xyzzy' of example.com:/git/froboz.git/
+
+     Completed frotz feature
+
+     * tag 'frotz-for-xyzzy' (100 commits)
+       Add tests and documentation for frotz
+       ...
+------------
+
+There is no need for the auditor to explicitly fetch the contributor's
+signature, or to even be aware of what tag(s) the contributor and integrator
+used to communicate the signature.  All the required information is recorded
+as part of the merge commit.
diff --git a/Documentation/i18n.txt b/Documentation/i18n.txt
index 708da6c..625d315 100644
--- a/Documentation/i18n.txt
+++ b/Documentation/i18n.txt
@@ -21,7 +21,7 @@
 does not forbid it.  However, there are a few things to keep in
 mind.
 
-. 'git-commit' and 'git-commit-tree' issues
+. 'git commit' and 'git commit-tree' issues
   a warning if the commit log message given to it does not look
   like a valid UTF-8 string, unless you explicitly say your
   project uses a legacy encoding.  The way to say this is to
@@ -37,7 +37,7 @@
 help other people who look at them later.  Lack of this header
 implies that the commit log message is encoded in UTF-8.
 
-. 'git-log', 'git-show', 'git-blame' and friends look at the
+. 'git log', 'git show', 'git blame' and friends look at the
   `encoding` header of a commit object, and try to re-code the
   log message into UTF-8 unless otherwise specified.  You can
   specify the desired output encoding with
diff --git a/Documentation/install-doc-quick.sh b/Documentation/install-doc-quick.sh
index 35f4408..327f69b 100755
--- a/Documentation/install-doc-quick.sh
+++ b/Documentation/install-doc-quick.sh
@@ -1,31 +1,39 @@
 #!/bin/sh
-# This requires a branch named in $head
-# (usually 'man' or 'html', provided by the git.git repository)
-set -e
-head="$1"
-mandir="$2"
-SUBDIRECTORY_OK=t
-USAGE='<refname> <target directory>'
-. "$(git --exec-path)"/git-sh-setup
-cd_to_toplevel
+# This requires git-manpages and/or git-htmldocs repositories
 
-test -z "$mandir" && usage
-if ! git rev-parse --verify "$head^0" >/dev/null; then
-	echo >&2 "head: $head does not exist in the current repository"
-	usage
+repository=${1?repository}
+destdir=${2?destination}
+
+head=master GIT_DIR=
+for d in "$repository/.git" "$repository"
+do
+	if GIT_DIR="$d" git rev-parse refs/heads/master >/dev/null 2>&1
+	then
+		GIT_DIR="$d"
+		export GIT_DIR
+		break
+	fi
+done
+
+if test -z "$GIT_DIR"
+then
+	echo >&2 "Neither $repository nor $repository/.git is a repository"
+	exit 1
 fi
 
-GIT_INDEX_FILE=`pwd`/.quick-doc.index
-export GIT_INDEX_FILE
+GIT_WORK_TREE=$(pwd)
+GIT_INDEX_FILE=$(pwd)/.quick-doc.$$
+export GIT_INDEX_FILE GIT_WORK_TREE
 rm -f "$GIT_INDEX_FILE"
 trap 'rm -f "$GIT_INDEX_FILE"' 0
 
 git read-tree $head
-git checkout-index -a -f --prefix="$mandir"/
+git checkout-index -a -f --prefix="$destdir"/
 
-if test -n "$GZ"; then
+if test -n "$GZ"
+then
 	git ls-tree -r --name-only $head |
-	xargs printf "$mandir/%s\n" |
+	xargs printf "$destdir/%s\n" |
 	xargs gzip -f
 fi
 rm -f "$GIT_INDEX_FILE"
diff --git a/Documentation/install-webdoc.sh b/Documentation/install-webdoc.sh
index 2135a8e..76d69a9 100755
--- a/Documentation/install-webdoc.sh
+++ b/Documentation/install-webdoc.sh
@@ -6,13 +6,13 @@
 	*.txt *.html \
 	howto/*.txt howto/*.html \
 	technical/*.txt technical/*.html \
-	RelNotes-*.txt *.css
+	RelNotes/*.txt *.css
 do
 	if test ! -f "$h"
 	then
 		: did not match
 	elif test -f "$T/$h" &&
-	   diff -u -I'Last updated [0-9][0-9]-[A-Z][a-z][a-z]-' "$T/$h" "$h"
+		$DIFF -u -I'^Last updated ' "$T/$h" "$h"
 	then
 		:; # up to date
 	else
@@ -30,7 +30,7 @@
 do
 	h=`expr "$th" : "$strip_leading"'\(.*\)'`
 	case "$h" in
-	index.html) continue ;;
+	RelNotes-*.txt | index.html) continue ;;
 	esac
 	test -f "$h" && continue
 	echo >&2 "# rm -f $th"
diff --git a/Documentation/manpage-base-url.xsl.in b/Documentation/manpage-base-url.xsl.in
new file mode 100644
index 0000000..e800904
--- /dev/null
+++ b/Documentation/manpage-base-url.xsl.in
@@ -0,0 +1,10 @@
+<!-- manpage-base-url.xsl:
+     special settings for manpages rendered from newer docbook -->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+		version="1.0">
+
+<!-- set a base URL for relative links -->
+<xsl:param name="man.base.url.for.relative.links"
+	>@@MAN_BASE_URL@@</xsl:param>
+
+</xsl:stylesheet>
diff --git a/Documentation/manpage-quote-apos.xsl b/Documentation/manpage-quote-apos.xsl
new file mode 100644
index 0000000..aeb8839
--- /dev/null
+++ b/Documentation/manpage-quote-apos.xsl
@@ -0,0 +1,16 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+		version="1.0">
+
+<!-- work around newer groff/man setups using a prettier apostrophe
+     that unfortunately does not quote anything when cut&pasting
+     examples to the shell -->
+<xsl:template name="escape.apostrophe">
+  <xsl:param name="content"/>
+  <xsl:call-template name="string.subst">
+    <xsl:with-param name="string" select="$content"/>
+    <xsl:with-param name="target">'</xsl:with-param>
+    <xsl:with-param name="replacement">\(aq</xsl:with-param>
+  </xsl:call-template>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/Documentation/merge-config.txt b/Documentation/merge-config.txt
index c0f96e7..861bd6f 100644
--- a/Documentation/merge-config.txt
+++ b/Documentation/merge-config.txt
@@ -6,25 +6,57 @@
 	a `>>>>>>>` marker.  An alternate style, "diff3", adds a `|||||||`
 	marker and the original text before the `=======` marker.
 
+merge.defaultToUpstream::
+	If merge is called without any commit argument, merge the upstream
+	branches configured for the current branch by using their last
+	observed values stored in their remote tracking branches.
+	The values of the `branch.<current branch>.merge` that name the
+	branches at the remote named by `branch.<current branch>.remote`
+	are consulted, and then they are mapped via `remote.<remote>.fetch`
+	to their corresponding remote tracking branches, and the tips of
+	these tracking branches are merged.
+
+merge.ff::
+	By default, git does not create an extra merge commit when merging
+	a commit that is a descendant of the current commit. Instead, the
+	tip of the current branch is fast-forwarded. When set to `false`,
+	this variable tells git to create an extra merge commit in such
+	a case (equivalent to giving the `--no-ff` option from the command
+	line). When set to `only`, only such fast-forward merges are
+	allowed (equivalent to giving the `--ff-only` option from the
+	command line).
+
 merge.log::
-	Whether to include summaries of merged commits in newly created
-	merge commit messages. False by default.
+	In addition to branch names, populate the log message with at
+	most the specified number of one-line descriptions from the
+	actual commits that are being merged.  Defaults to false, and
+	true is a synonym for 20.
 
 merge.renameLimit::
 	The number of files to consider when performing rename detection
 	during a merge; if not specified, defaults to the value of
 	diff.renameLimit.
 
+merge.renormalize::
+	Tell git that canonical representation of files in the
+	repository has changed over time (e.g. earlier commits record
+	text files with CRLF line endings, but recent ones use LF line
+	endings).  In such a repository, git can convert the data
+	recorded in commits to a canonical form before performing a
+	merge to reduce unnecessary conflicts.  For more information,
+	see section "Merging branches with differing checkin/checkout
+	attributes" in linkgit:gitattributes[5].
+
 merge.stat::
 	Whether to print the diffstat between ORIG_HEAD and the merge result
 	at the end of the merge.  True by default.
 
 merge.tool::
 	Controls which merge resolution program is used by
-	linkgit:git-mergetool[1].  Valid built-in values are: "kdiff3",
-	"tkdiff", "meld", "xxdiff", "emerge", "vimdiff", "gvimdiff",
-	"diffuse", "ecmerge", "tortoisemerge", "araxis", and
-	"opendiff".  Any other value is treated is custom merge tool
+	linkgit:git-mergetool[1].  Valid built-in values are: "araxis",
+	"bc3", "diffuse", "ecmerge", "emerge", "gvimdiff", "kdiff3", "meld",
+	"opendiff", "p4merge", "tkdiff", "tortoisemerge", "vimdiff"
+	and "xxdiff".  Any other value is treated is custom merge tool
 	and there must be a corresponding mergetool.<tool>.cmd option.
 
 merge.verbosity::
diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt
index adadf8e..0bcbe0a 100644
--- a/Documentation/merge-options.txt
+++ b/Documentation/merge-options.txt
@@ -1,43 +1,63 @@
--q::
---quiet::
-	Operate quietly.
-
--v::
---verbose::
-	Be verbose.
-
---stat::
-	Show a diffstat at the end of the merge. The diffstat is also
-	controlled by the configuration option merge.stat.
-
--n::
---no-stat::
-	Do not show a diffstat at the end of the merge.
-
---summary::
---no-summary::
-	Synonyms to --stat and --no-stat; these are deprecated and will be
-	removed in the future.
-
---log::
-	In addition to branch names, populate the log message with
-	one-line descriptions from the actual commits that are being
-	merged.
-
---no-log::
-	Do not list one-line descriptions from the actual commits being
-	merged.
-
---no-commit::
-	Perform the merge but pretend the merge failed and do
-	not autocommit, to give the user a chance to inspect and
-	further tweak the merge result before committing.
-
 --commit::
+--no-commit::
 	Perform the merge and commit the result. This option can
 	be used to override --no-commit.
++
+With --no-commit perform the merge but pretend the merge
+failed and do not autocommit, to give the user a chance to
+inspect and further tweak the merge result before committing.
+
+--edit::
+--no-edit::
+	Invoke an editor before committing successful mechanical merge to
+	further edit the auto-generated merge message, so that the user
+	can explain and justify the merge. The `--no-edit` option can be
+	used to accept the auto-generated message (this is generally
+	discouraged). The `--edit` option is still useful if you are
+	giving a draft message with the `-m` option from the command line
+	and want to edit it in the editor.
++
+Older scripts may depend on the historical behaviour of not allowing the
+user to edit the merge log message. They will see an editor opened when
+they run `git merge`. To make it easier to adjust such scripts to the
+updated behaviour, the environment variable `GIT_MERGE_AUTOEDIT` can be
+set to `no` at the beginning of them.
+
+--ff::
+	When the merge resolves as a fast-forward, only update the branch
+	pointer, without creating a merge commit.  This is the default
+	behavior.
+
+--no-ff::
+	Create a merge commit even when the merge resolves as a
+	fast-forward.
+
+--ff-only::
+	Refuse to merge and exit with a non-zero status unless the
+	current `HEAD` is already up-to-date or the merge can be
+	resolved as a fast-forward.
+
+--log[=<n>]::
+--no-log::
+	In addition to branch names, populate the log message with
+	one-line descriptions from at most <n> actual commits that are being
+	merged. See also linkgit:git-fmt-merge-msg[1].
++
+With --no-log do not list one-line descriptions from the
+actual commits being merged.
+
+
+--stat::
+-n::
+--no-stat::
+	Show a diffstat at the end of the merge. The diffstat is also
+	controlled by the configuration option merge.stat.
++
+With -n or --no-stat do not show a diffstat at the end of the
+merge.
 
 --squash::
+--no-squash::
 	Produce the working tree and index state as if a real
 	merge happened (except for the merge information),
 	but do not actually make a commit or
@@ -46,24 +66,42 @@
 	commit.  This allows you to create a single commit on
 	top of the current branch whose effect is the same as
 	merging another branch (or more in case of an octopus).
-
---no-squash::
-	Perform the merge and commit the result. This option can
-	be used to override --squash.
-
---no-ff::
-	Generate a merge commit even if the merge resolved as a
-	fast-forward.
-
---ff::
-	Do not generate a merge commit if the merge resolved as
-	a fast-forward, only update the branch pointer. This is
-	the default behavior of git-merge.
++
+With --no-squash perform the merge and commit the result. This
+option can be used to override --squash.
 
 -s <strategy>::
 --strategy=<strategy>::
 	Use the given merge strategy; can be supplied more than
 	once to specify them in the order they should be tried.
 	If there is no `-s` option, a built-in list of strategies
-	is used instead ('git-merge-recursive' when merging a single
-	head, 'git-merge-octopus' otherwise).
+	is used instead ('git merge-recursive' when merging a single
+	head, 'git merge-octopus' otherwise).
+
+-X <option>::
+--strategy-option=<option>::
+	Pass merge strategy specific option through to the merge
+	strategy.
+
+--summary::
+--no-summary::
+	Synonyms to --stat and --no-stat; these are deprecated and will be
+	removed in the future.
+
+ifndef::git-pull[]
+-q::
+--quiet::
+	Operate quietly. Implies --no-progress.
+
+-v::
+--verbose::
+	Be verbose.
+
+--progress::
+--no-progress::
+	Turn progress on/off explicitly. If neither is specified,
+	progress is shown if standard error is connected to a terminal.
+	Note that not all merge strategies may support progress
+	reporting.
+
+endif::git-pull[]
diff --git a/Documentation/merge-strategies.txt b/Documentation/merge-strategies.txt
index 4365b7e..595a3cf 100644
--- a/Documentation/merge-strategies.txt
+++ b/Documentation/merge-strategies.txt
@@ -1,6 +1,11 @@
 MERGE STRATEGIES
 ----------------
 
+The merge mechanism ('git-merge' and 'git-pull' commands) allows the
+backend 'merge strategies' to be chosen with `-s` option.  Some strategies
+can also take their own options, which can be passed by giving `-X<option>`
+arguments to 'git-merge' and/or 'git-pull'.
+
 resolve::
 	This can only resolve two heads (i.e. the current branch
 	and another branch you pulled from) using a 3-way merge
@@ -20,6 +25,65 @@
 	Additionally this can detect and handle merges involving
 	renames.  This is the default merge strategy when
 	pulling or merging one branch.
++
+The 'recursive' strategy can take the following options:
+
+ours;;
+	This option forces conflicting hunks to be auto-resolved cleanly by
+	favoring 'our' version.  Changes from the other tree that do not
+	conflict with our side are reflected to the merge result.
++
+This should not be confused with the 'ours' merge strategy, which does not
+even look at what the other tree contains at all.  It discards everything
+the other tree did, declaring 'our' history contains all that happened in it.
+
+theirs;;
+	This is opposite of 'ours'.
+
+patience;;
+	With this option, 'merge-recursive' spends a little extra time
+	to avoid mismerges that sometimes occur due to unimportant
+	matching lines (e.g., braces from distinct functions).  Use
+	this when the branches to be merged have diverged wildly.
+	See also linkgit:git-diff[1] `--patience`.
+
+ignore-space-change;;
+ignore-all-space;;
+ignore-space-at-eol;;
+	Treats lines with the indicated type of whitespace change as
+	unchanged for the sake of a three-way merge.  Whitespace
+	changes mixed with other changes to a line are not ignored.
+	See also linkgit:git-diff[1] `-b`, `-w`, and
+	`--ignore-space-at-eol`.
++
+* If 'their' version only introduces whitespace changes to a line,
+  'our' version is used;
+* If 'our' version introduces whitespace changes but 'their'
+  version includes a substantial change, 'their' version is used;
+* Otherwise, the merge proceeds in the usual way.
+
+renormalize;;
+	This runs a virtual check-out and check-in of all three stages
+	of a file when resolving a three-way merge.  This option is
+	meant to be used when merging branches with different clean
+	filters or end-of-line normalization rules.  See "Merging
+	branches with differing checkin/checkout attributes" in
+	linkgit:gitattributes[5] for details.
+
+no-renormalize;;
+	Disables the `renormalize` option.  This overrides the
+	`merge.renormalize` configuration variable.
+
+rename-threshold=<n>;;
+	Controls the similarity threshold used for rename detection.
+	See also linkgit:git-diff[1] `-M`.
+
+subtree[=<path>];;
+	This option is a more advanced form of 'subtree' strategy, where
+	the strategy makes a guess on how two trees must be shifted to
+	match with each other when merging.  Instead, the specified path
+	is prefixed (or stripped from the beginning) to make the shape of
+	two trees to match.
 
 octopus::
 	This resolves cases with more than two heads, but refuses to do
@@ -29,10 +93,12 @@
 	pulling or merging more than one branch.
 
 ours::
-	This resolves any number of heads, but the result of the
-	merge is always the current branch head.  It is meant to
+	This resolves any number of heads, but the resulting tree of the
+	merge is always that of the current branch head, effectively
+	ignoring all changes from all other branches.  It is meant to
 	be used to supersede old development history of side
-	branches.
+	branches.  Note that this is different from the -Xours option to
+	the 'recursive' merge strategy.
 
 subtree::
 	This is a modified recursive strategy. When merging trees A and
diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt
index 2a845b1..e3d8a83 100644
--- a/Documentation/pretty-formats.txt
+++ b/Documentation/pretty-formats.txt
@@ -11,7 +11,12 @@
 only interested in changes related to a certain directory or
 file.
 
-Here are some additional details for each format:
+There are several built-in formats, and you can define
+additional formats by setting a pretty.<name>
+config option to either another format name, or a
+'format:' string, as described below (see
+linkgit:git-config[1]). Here are the details of the
+built-in formats:
 
 * 'oneline'
 
@@ -76,9 +81,9 @@
 true parent commits, without taking grafts nor history
 simplification into account.
 
-* 'format:'
+* 'format:<string>'
 +
-The 'format:' format allows you to specify which information
+The 'format:<string>' format allows you to specify which information
 you want to show. It works a little bit like printf format,
 with the notable exception that you get a newline with '%n'
 instead of '\n'.
@@ -123,6 +128,15 @@
 - '%s': subject
 - '%f': sanitized subject line, suitable for a filename
 - '%b': body
+- '%B': raw body (unwrapped subject and body)
+- '%N': commit notes
+- '%gD': reflog selector, e.g., `refs/stash@{1}`
+- '%gd': shortened reflog selector, e.g., `stash@{1}`
+- '%gn': reflog identity name
+- '%gN': reflog identity name (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])
+- '%ge': reflog identity email
+- '%gE': reflog identity email (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])
+- '%gs': reflog subject
 - '%Cred': switch color to red
 - '%Cgreen': switch color to green
 - '%Cblue': switch color to blue
@@ -130,7 +144,28 @@
 - '%C(...)': color specification, as described in color.branch.* config option
 - '%m': left, right or boundary mark
 - '%n': newline
+- '%%': a raw '%'
 - '%x00': print a byte from a hex code
+- '%w([<w>[,<i1>[,<i2>]]])': switch line wrapping, like the -w option of
+  linkgit:git-shortlog[1].
+
+NOTE: Some placeholders may depend on other options given to the
+revision traversal engine. For example, the `%g*` reflog options will
+insert an empty string unless we are traversing reflog entries (e.g., by
+`git log -g`). The `%d` placeholder will use the "short" decoration
+format if `--decorate` was not already provided on the command line.
+
+If you add a `+` (plus sign) after '%' of a placeholder, a line-feed
+is inserted immediately before the expansion if and only if the
+placeholder expands to a non-empty string.
+
+If you add a `-` (minus sign) after '%' of a placeholder, line-feeds that
+immediately precede the expansion are deleted if and only if the
+placeholder expands to an empty string.
+
+If you add a ` ` (space) after '%' of a placeholder, a space
+is inserted immediately before the expansion if and only if the
+placeholder expands to a non-empty string.
 
 * 'tformat:'
 +
diff --git a/Documentation/pretty-options.txt b/Documentation/pretty-options.txt
index bff9499..2a3dc86 100644
--- a/Documentation/pretty-options.txt
+++ b/Documentation/pretty-options.txt
@@ -1,10 +1,11 @@
---pretty[='<format>']::
---format[='<format>']::
+--pretty[=<format>]::
+--format=<format>::
 
 	Pretty-print the contents of the commit logs in a given format,
 	where '<format>' can be one of 'oneline', 'short', 'medium',
-	'full', 'fuller', 'email', 'raw' and 'format:<string>'.
-	When omitted, the format defaults to 'medium'.
+	'full', 'fuller', 'email', 'raw' and 'format:<string>'.  See
+	the "PRETTY FORMATS" section for some additional details for each
+	format.  When omitted, the format defaults to 'medium'.
 +
 Note: you can specify the default pretty format in the repository
 configuration (see linkgit:git-config[1]).
@@ -18,6 +19,11 @@
 This should make "--pretty=oneline" a whole lot more readable for
 people using 80-column terminals.
 
+--no-abbrev-commit::
+	Show the full 40-byte hexadecimal commit object name. This negates
+	`--abbrev-commit` and those options which imply it such as
+	"--oneline". It also overrides the 'log.abbrevCommit' variable.
+
 --oneline::
 	This is a shorthand for "--pretty=oneline --abbrev-commit"
 	used together.
@@ -28,3 +34,35 @@
 	command to re-code the commit log message in the encoding
 	preferred by the user.  For non plumbing commands this
 	defaults to UTF-8.
+
+--notes[=<ref>]::
+	Show the notes (see linkgit:git-notes[1]) that annotate the
+	commit, when showing the commit log message.  This is the default
+	for `git log`, `git show` and `git whatchanged` commands when
+	there is no `--pretty`, `--format` nor `--oneline` option given
+	on the command line.
++
+By default, the notes shown are from the notes refs listed in the
+'core.notesRef' and 'notes.displayRef' variables (or corresponding
+environment overrides). See linkgit:git-config[1] for more details.
++
+With an optional '<ref>' argument, show this notes ref instead of the
+default notes ref(s). The ref is taken to be in `refs/notes/` if it
+is not qualified.
++
+Multiple --notes options can be combined to control which notes are
+being displayed. Examples: "--notes=foo" will show only notes from
+"refs/notes/foo"; "--notes=foo --notes" will show both notes from
+"refs/notes/foo" and from the default notes ref(s).
+
+--no-notes::
+	Do not show notes. This negates the above `--notes` option, by
+	resetting the list of notes refs from which notes are shown.
+	Options are parsed in the order given on the command line, so e.g.
+	"--notes --notes=foo --no-notes --notes=bar" will only show notes
+	from "refs/notes/bar".
+
+--show-notes[=<ref>]::
+--[no-]standard-notes::
+	These options are deprecated. Use the above --notes/--no-notes
+	options instead.
diff --git a/Documentation/pt_BR/gittutorial.txt b/Documentation/pt_BR/gittutorial.txt
index 81e7ad7..beba065 100644
--- a/Documentation/pt_BR/gittutorial.txt
+++ b/Documentation/pt_BR/gittutorial.txt
@@ -1,15 +1,15 @@
 gittutorial(7)
 ==============
 
-NAME
+NOME
 ----
 gittutorial - Um tutorial de introdução ao git (para versão 1.5.1 ou mais nova)
 
-SYNOPSIS
+SINOPSE
 --------
 git *
 
-DESCRIPTION
+DESCRIÇÃO
 -----------
 
 Este tutorial explica como importar um novo projeto para o git,
@@ -64,11 +64,11 @@
 Initialized empty Git repository in .git/
 ------------------------------------------------
 
-Você agora iniciou seu diretório de trabalho--você deve ter notado um
-novo diretório criado, com o nome de ".git".
+Agora que você iniciou seu diretório de trabalho, você deve ter notado que um
+novo diretório foi criado com o nome de ".git".
 
 A seguir, diga ao git para gravar um instantâneo do conteúdo de todos os
-arquivos sob o diretório corrente (note o '.'), com 'git-add':
+arquivos sob o diretório atual (note o '.'), com 'git-add':
 
 ------------------------------------------------
 $ git add .
@@ -126,8 +126,8 @@
 $ git commit
 ------------------------------------------------
 
-Isto irá novamente te pedir por uma mensagem descrevendo a mudança, e,
-então, gravar a nova versão do projeto.
+Ao executar esse comando, ele irá te pedir uma mensagem descrevendo a mudança,
+e, então, irá gravar a nova versão do projeto.
 
 Alternativamente, ao invés de executar 'git-add' antes, você pode usar
 
@@ -143,7 +143,7 @@
 caracteres) linha sumarizando a mudança, seguida de uma linha em branco
 e, então, uma descrição mais detalhada. Ferramentas que transformam
 commits em email, por exemplo, usam a primeira linha no campo de
-cabeçalho Subject: e o resto no corpo.
+cabeçalho "Subject:" e o resto no corpo.
 
 Git rastreia conteúdo, não arquivos
 ----------------------------
@@ -155,7 +155,7 @@
 em ambos os casos, ele tira o instantâneo dos arquivos dados e armazena
 o conteúdo no índice, pronto para inclusão do próximo commit.
 
-Visualizando história do projeto
+Visualizando a história do projeto
 -----------------------
 
 Em qualquer ponto você pode visualizar a história das suas mudanças
@@ -165,7 +165,7 @@
 $ git log
 ------------------------------------------------
 
-Se você também quer ver a diferença completa a cada passo, use
+Se você também quiser ver a diferença completa a cada passo, use
 
 ------------------------------------------------
 $ git log -p
diff --git a/Documentation/pull-fetch-param.txt b/Documentation/pull-fetch-param.txt
index f9811f2..94a9d32 100644
--- a/Documentation/pull-fetch-param.txt
+++ b/Documentation/pull-fetch-param.txt
@@ -4,16 +4,23 @@
 	(see the section <<URLS,GIT URLS>> below) or the name
 	of a remote (see the section <<REMOTES,REMOTES>> below).
 
+ifndef::git-pull[]
+<group>::
+	A name referring to a list of repositories as the value
+	of remotes.<group> in the configuration file.
+	(See linkgit:git-config[1]).
+endif::git-pull[]
+
 <refspec>::
 	The format of a <refspec> parameter is an optional plus
-	`{plus}`, followed by the source ref <src>, followed
+	`+`, followed by the source ref <src>, followed
 	by a colon `:`, followed by the destination ref <dst>.
 +
 The remote ref that matches <src>
 is fetched, and if <dst> is not empty string, the local
-ref that matches it is fast forwarded using <src>.
+ref that matches it is fast-forwarded using <src>.
 If the optional plus `+` is used, the local ref
-is updated even if it does not result in a fast forward
+is updated even if it does not result in a fast-forward
 update.
 +
 [NOTE]
@@ -31,7 +38,7 @@
 [NOTE]
 You never do your own development on branches that appear
 on the right hand side of a <refspec> colon on `Pull:` lines;
-they are to be updated by 'git-fetch'.  If you intend to do
+they are to be updated by 'git fetch'.  If you intend to do
 development derived from a remote branch `B`, have a `Pull:`
 line to track it (i.e. `Pull: B:remote-B`), and have a separate
 branch `my-B` to do your development on top of it.  The latter
@@ -43,13 +50,13 @@
 +
 [NOTE]
 There is a difference between listing multiple <refspec>
-directly on 'git-pull' command line and having multiple
+directly on 'git pull' command line and having multiple
 `Pull:` <refspec> lines for a <repository> and running
-'git-pull' command without any explicit <refspec> parameters.
+'git pull' command without any explicit <refspec> parameters.
 <refspec> listed explicitly on the command line are always
 merged into the current branch after fetching.  In other words,
 if you list more than one remote refs, you would be making
-an Octopus.  While 'git-pull' run without any explicit <refspec>
+an Octopus.  While 'git pull' run without any explicit <refspec>
 parameter takes default <refspec>s from `Pull:` lines, it
 merges only the first <refspec> found into the current branch,
 after fetching all the remote refs.  This is because making an
diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index bf66116..d9b2b5b 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -1,148 +1,17 @@
-Commit Formatting
-~~~~~~~~~~~~~~~~~
-
-ifdef::git-rev-list[]
-Using these options, linkgit:git-rev-list[1] will act similar to the
-more specialized family of commit log tools: linkgit:git-log[1],
-linkgit:git-show[1], and linkgit:git-whatchanged[1]
-endif::git-rev-list[]
-
-include::pretty-options.txt[]
-
---relative-date::
-
-	Synonym for `--date=relative`.
-
---date={relative,local,default,iso,rfc,short,raw}::
-
-	Only takes effect for dates shown in human-readable format, such
-	as when using "--pretty". `log.date` config variable sets a default
-	value for log command's --date option.
-+
-`--date=relative` shows dates relative to the current time,
-e.g. "2 hours ago".
-+
-`--date=local` shows timestamps in user's local timezone.
-+
-`--date=iso` (or `--date=iso8601`) shows timestamps in ISO 8601 format.
-+
-`--date=rfc` (or `--date=rfc2822`) shows timestamps in RFC 2822
-format, often found in E-mail messages.
-+
-`--date=short` shows only date but not time, in `YYYY-MM-DD` format.
-+
-`--date=raw` shows the date in the internal raw git format `%s %z` format.
-+
-`--date=default` shows timestamps in the original timezone
-(either committer's or author's).
-
-ifdef::git-rev-list[]
---header::
-
-	Print the contents of the commit in raw-format; each record is
-	separated with a NUL character.
-endif::git-rev-list[]
-
---parents::
-
-	Print the parents of the commit.  Also enables parent
-	rewriting, see 'History Simplification' below.
-
---children::
-
-	Print the children of the commit.  Also enables parent
-	rewriting, see 'History Simplification' below.
-
-ifdef::git-rev-list[]
---timestamp::
-	Print the raw commit timestamp.
-endif::git-rev-list[]
-
---left-right::
-
-	Mark which side of a symmetric diff a commit is reachable from.
-	Commits from the left side are prefixed with `<` and those from
-	the right with `>`.  If combined with `--boundary`, those
-	commits are prefixed with `-`.
-+
-For example, if you have this topology:
-+
------------------------------------------------------------------------
-             y---b---b  branch B
-            / \ /
-           /   .
-          /   / \
-         o---x---a---a  branch A
------------------------------------------------------------------------
-+
-you would get an output like this:
-+
------------------------------------------------------------------------
-	$ git rev-list --left-right --boundary --pretty=oneline A...B
-
-	>bbbbbbb... 3rd on b
-	>bbbbbbb... 2nd on b
-	<aaaaaaa... 3rd on a
-	<aaaaaaa... 2nd on a
-	-yyyyyyy... 1st on b
-	-xxxxxxx... 1st on a
------------------------------------------------------------------------
-
---graph::
-
-	Draw a text-based graphical representation of the commit history
-	on the left hand side of the output.  This may cause extra lines
-	to be printed in between commits, in order for the graph history
-	to be drawn properly.
-+
-This implies the '--topo-order' option by default, but the
-'--date-order' option may also be specified.
-
-ifndef::git-rev-list[]
-Diff Formatting
-~~~~~~~~~~~~~~~
-
-Below are listed options that control the formatting of diff output.
-Some of them are specific to linkgit:git-rev-list[1], however other diff
-options may be given. See linkgit:git-diff-files[1] for more options.
-
--c::
-
-	This flag changes the way a merge commit is displayed.  It shows
-	the differences from each of the parents to the merge result
-	simultaneously instead of showing pairwise diff between a parent
-	and the result one at a time. Furthermore, it lists only files
-	which were modified from all parents.
-
---cc::
-
-	This flag implies the '-c' options and further compresses the
-	patch output by omitting uninteresting hunks whose contents in
-	the parents have only two variants and the merge result picks
-	one of them without modification.
-
--r::
-
-	Show recursive diffs.
-
--t::
-
-	Show the tree objects in the diff output. This implies '-r'.
-endif::git-rev-list[]
-
 Commit Limiting
 ~~~~~~~~~~~~~~~
 
 Besides specifying a range of commits that should be listed using the
 special notations explained in the description, additional commit
-limiting may be applied.
+limiting may be applied. Note that they are applied before commit
+ordering and formatting options, such as '--reverse'.
 
 --
 
 -n 'number'::
 --max-count=<number>::
 
-	Limit the number of commits output.
+	Limit the number of commits to output.
 
 --skip=<number>::
 
@@ -203,11 +72,26 @@
 
 --merges::
 
-	Print only merge commits.
+	Print only merge commits. This is exactly the same as `--min-parents=2`.
 
 --no-merges::
 
-	Do not print commits with more than one parent.
+	Do not print commits with more than one parent. This is
+	exactly the same as `--max-parents=1`.
+
+--min-parents=<number>::
+--max-parents=<number>::
+--no-min-parents::
+--no-max-parents::
+
+	Show only commits which have at least (or at most) that many
+	commits. In particular, `--max-parents=1` is the same as `--no-merges`,
+	`--min-parents=2` is the same as `--merges`.  `--max-parents=0`
+	gives all root commits and `--min-parents=3` all octopus merges.
++
+`--no-min-parents` and `--no-max-parents` reset these limits (to no limit)
+again.  Equivalent forms are `--min-parents=0` (any commit has 0 or more
+parents) and `--max-parents=-1` (negative numbers denote no upper limit).
 
 --first-parent::
 	Follow only the first parent commit upon seeing a merge
@@ -225,30 +109,58 @@
 
 --all::
 
-	Pretend as if all the refs in `$GIT_DIR/refs/` are listed on the
+	Pretend as if all the refs in `refs/` are listed on the
 	command line as '<commit>'.
 
---branches::
+--branches[=<pattern>]::
 
-	Pretend as if all the refs in `$GIT_DIR/refs/heads` are listed
-	on the command line as '<commit>'.
+	Pretend as if all the refs in `refs/heads` are listed
+	on the command line as '<commit>'. If '<pattern>' is given, limit
+	branches to ones matching given shell glob. If pattern lacks '?',
+	'{asterisk}', or '[', '/{asterisk}' at the end is implied.
 
---tags::
+--tags[=<pattern>]::
 
-	Pretend as if all the refs in `$GIT_DIR/refs/tags` are listed
-	on the command line as '<commit>'.
+	Pretend as if all the refs in `refs/tags` are listed
+	on the command line as '<commit>'. If '<pattern>' is given, limit
+	tags to ones matching given shell glob. If pattern lacks '?', '{asterisk}',
+	or '[', '/{asterisk}' at the end is implied.
 
---remotes::
+--remotes[=<pattern>]::
 
-	Pretend as if all the refs in `$GIT_DIR/refs/remotes` are listed
-	on the command line as '<commit>'.
+	Pretend as if all the refs in `refs/remotes` are listed
+	on the command line as '<commit>'. If '<pattern>' is given, limit
+	remote-tracking branches to ones matching given shell glob.
+	If pattern lacks '?', '{asterisk}', or '[', '/{asterisk}' at the end is implied.
 
-ifdef::git-rev-list[]
+--glob=<glob-pattern>::
+	Pretend as if all the refs matching shell glob '<glob-pattern>'
+	are listed on the command line as '<commit>'. Leading 'refs/',
+	is automatically prepended if missing. If pattern lacks '?', '{asterisk}',
+	or '[', '/{asterisk}' at the end is implied.
+
+--ignore-missing::
+
+	Upon seeing an invalid object name in the input, pretend as if
+	the bad input was not given.
+
+ifndef::git-rev-list[]
+--bisect::
+
+	Pretend as if the bad bisection ref `refs/bisect/bad`
+	was listed and as if it was followed by `--not` and the good
+	bisection refs `refs/bisect/good-*` on the command
+	line.
+endif::git-rev-list[]
+
 --stdin::
 
 	In addition to the '<commit>' listed on the command
-	line, read them from the standard input.
+	line, read them from the standard input. If a '--' separator is
+	seen, stop reading commits and start reading paths to limit the
+	result.
 
+ifdef::git-rev-list[]
 --quiet::
 
 	Don't print anything to standard output.  This form
@@ -258,6 +170,11 @@
 	to /dev/null as the output does not have to be formatted.
 endif::git-rev-list[]
 
+--cherry-mark::
+
+	Like `--cherry-pick` (see below) but mark equivalent commits
+	with `=` rather than omitting them, and inequivalent ones with `+`.
+
 --cherry-pick::
 
 	Omit any commit that introduces the same change as
@@ -266,12 +183,33 @@
 +
 For example, if you have two branches, `A` and `B`, a usual way
 to list all commits on only one side of them is with
-`--left-right`, like the example above in the description of
-that option.  It however shows the commits that were cherry-picked
+`--left-right` (see the example below in the description of
+the `--left-right` option).  It however shows the commits that were cherry-picked
 from the other branch (for example, "3rd on b" may be cherry-picked
 from branch A).  With this option, such pairs of commits are
 excluded from the output.
 
+--left-only::
+--right-only::
+
+	List only commits on the respective side of a symmetric range,
+	i.e. only those which would be marked `<` resp. `>` by
+	`--left-right`.
++
+For example, `--cherry-pick --right-only A...B` omits those
+commits from `B` which are in `A` or are patch-equivalent to a commit in
+`A`. In other words, this lists the `+` commits from `git cherry A B`.
+More precisely, `--cherry-pick --right-only --no-merges` gives the exact
+list.
+
+--cherry::
+
+	A synonym for `--right-only --cherry-mark --no-merges`; useful to
+	limit the output to the commits on our side and mark those that
+	have been applied to the other side of a forked history with
+	`git log --cherry upstream...mybranch`, similar to
+	`git cherry upstream mybranch`.
+
 -g::
 --walk-reflogs::
 
@@ -279,7 +217,7 @@
 	reflog entries from the most recent one to older ones.
 	When this option is used you cannot specify commits to
 	exclude (that is, '{caret}commit', 'commit1..commit2',
-	nor 'commit1...commit2' notations cannot be used).
+	nor 'commit1\...commit2' notations cannot be used).
 +
 With '\--pretty' format other than oneline (for obvious reasons),
 this causes the output to have two extra lines of information
@@ -334,7 +272,7 @@
 
 --full-history::
 
-	As the default mode but does not prune some history.
+	Same as the default mode, but does not prune some history.
 
 --dense::
 
@@ -351,6 +289,14 @@
 	merges from the resulting history, as there are no selected
 	commits contributing to this merge.
 
+--ancestry-path::
+
+	When given a range of commits to display (e.g. 'commit1..commit2'
+	or 'commit2 {caret}commit1'), only display commits that exist
+	directly on the ancestry chain between the 'commit1' and
+	'commit2', i.e. commits that are both descendants of 'commit1',
+	and ancestors of 'commit2'.
+
 A more detailed explanation follows.
 
 Suppose you specified `foo` as the <paths>.  We shall call commits
@@ -367,7 +313,7 @@
 	 \   /   /   /   /
 	  `-------------'
 -----------------------------------------------------------------------
-The horizontal line of history A--P is taken to be the first parent of
+The horizontal line of history A---P is taken to be the first parent of
 each merge.  The commits are:
 
 * `I` is the initial commit, in which `foo` exists with contents
@@ -407,7 +353,7 @@
 +
 -----------------------------------------------------------------------
 	  .-A---N---O
-	 /         /
+	 /     /   /
 	I---------D
 -----------------------------------------------------------------------
 +
@@ -478,8 +424,6 @@
 one of the parents is TREESAME, we follow only that one, so the other
 sides of the merge are never walked.
 
-Finally, there is a fourth simplification mode available:
-
 --simplify-merges::
 
 	First, build a history graph in the same way that
@@ -511,7 +455,7 @@
 	  `---------'
 -----------------------------------------------------------------------
 +
-Note the major differences in `N` and `P` over '\--full-history':
+Note the major differences in `N` and `P` over '--full-history':
 +
 --
 * `N`'s parent list had `I` removed, because it is an ancestor of the
@@ -521,6 +465,46 @@
   removed completely, because it had one parent and is TREESAME.
 --
 
+Finally, there is a fifth simplification mode available:
+
+--ancestry-path::
+
+	Limit the displayed commits to those directly on the ancestry
+	chain between the "from" and "to" commits in the given commit
+	range. I.e. only display commits that are ancestor of the "to"
+	commit, and descendants of the "from" commit.
++
+As an example use case, consider the following commit history:
++
+-----------------------------------------------------------------------
+	    D---E-------F
+	   /     \       \
+	  B---C---G---H---I---J
+	 /                     \
+	A-------K---------------L--M
+-----------------------------------------------------------------------
++
+A regular 'D..M' computes the set of commits that are ancestors of `M`,
+but excludes the ones that are ancestors of `D`. This is useful to see
+what happened to the history leading to `M` since `D`, in the sense
+that "what does `M` have that did not exist in `D`". The result in this
+example would be all the commits, except `A` and `B` (and `D` itself,
+of course).
++
+When we want to find out what commits in `M` are contaminated with the
+bug introduced by `D` and need fixing, however, we might want to view
+only the subset of 'D..M' that are actually descendants of `D`, i.e.
+excluding `C` and `K`. This is exactly what the '--ancestry-path'
+option does. Applied to the 'D..M' range, it results in:
++
+-----------------------------------------------------------------------
+		E-------F
+		 \       \
+		  G---H---I---J
+			       \
+				L--M
+-----------------------------------------------------------------------
+
 The '\--simplify-by-decoration' option allows you to view only the
 big picture of the topology of the history, by omitting commits
 that are not referenced by tags.  Commits are marked as !TREESAME
@@ -536,7 +520,11 @@
 --bisect::
 
 Limit output to the one commit object which is roughly halfway between
-the included and excluded commits. Thus, if
+included and excluded commits. Note that the bad bisection ref
+`refs/bisect/bad` is added to the included commits (if it
+exists) and the good bisection refs `refs/bisect/good-*` are
+added to the excluded commits (if they exist). Thus, supposing there
+are no refs in `refs/bisect/`, if
 
 -----------------------------------------------------------------------
 	$ git rev-list --bisect foo ^bar ^baz
@@ -556,22 +544,24 @@
 
 --bisect-vars::
 
-This calculates the same as `--bisect`, but outputs text ready
-to be eval'ed by the shell. These lines will assign the name of
-the midpoint revision to the variable `bisect_rev`, and the
-expected number of commits to be tested after `bisect_rev` is
-tested to `bisect_nr`, the expected number of commits to be
-tested if `bisect_rev` turns out to be good to `bisect_good`,
-the expected number of commits to be tested if `bisect_rev`
-turns out to be bad to `bisect_bad`, and the number of commits
-we are bisecting right now to `bisect_all`.
+This calculates the same as `--bisect`, except that refs in
+`refs/bisect/` are not used, and except that this outputs
+text ready to be eval'ed by the shell. These lines will assign the
+name of the midpoint revision to the variable `bisect_rev`, and the
+expected number of commits to be tested after `bisect_rev` is tested
+to `bisect_nr`, the expected number of commits to be tested if
+`bisect_rev` turns out to be good to `bisect_good`, the expected
+number of commits to be tested if `bisect_rev` turns out to be bad to
+`bisect_bad`, and the number of commits we are bisecting right now to
+`bisect_all`.
 
 --bisect-all::
 
 This outputs all the commit objects between the included and excluded
 commits, ordered by their distance to the included and excluded
-commits. The farthest from them is displayed first. (This is the only
-one displayed by `--bisect`.)
+commits. Refs in `refs/bisect/` are not used. The farthest
+from them is displayed first. (This is the only one displayed by
+`--bisect`.)
 +
 This is useful because it makes it easy to choose a good commit to
 test when you want to avoid to test some of them for some reason (they
@@ -632,7 +622,166 @@
 --no-walk::
 
 	Only show the given revs, but do not traverse their ancestors.
+	This has no effect if a range is specified.
 
 --do-walk::
 
 	Overrides a previous --no-walk.
+
+Commit Formatting
+~~~~~~~~~~~~~~~~~
+
+ifdef::git-rev-list[]
+Using these options, linkgit:git-rev-list[1] will act similar to the
+more specialized family of commit log tools: linkgit:git-log[1],
+linkgit:git-show[1], and linkgit:git-whatchanged[1]
+endif::git-rev-list[]
+
+include::pretty-options.txt[]
+
+--relative-date::
+
+	Synonym for `--date=relative`.
+
+--date=(relative|local|default|iso|rfc|short|raw)::
+
+	Only takes effect for dates shown in human-readable format, such
+	as when using "--pretty". `log.date` config variable sets a default
+	value for log command's --date option.
++
+`--date=relative` shows dates relative to the current time,
+e.g. "2 hours ago".
++
+`--date=local` shows timestamps in user's local timezone.
++
+`--date=iso` (or `--date=iso8601`) shows timestamps in ISO 8601 format.
++
+`--date=rfc` (or `--date=rfc2822`) shows timestamps in RFC 2822
+format, often found in E-mail messages.
++
+`--date=short` shows only date but not time, in `YYYY-MM-DD` format.
++
+`--date=raw` shows the date in the internal raw git format `%s %z` format.
++
+`--date=default` shows timestamps in the original timezone
+(either committer's or author's).
+
+ifdef::git-rev-list[]
+--header::
+
+	Print the contents of the commit in raw-format; each record is
+	separated with a NUL character.
+endif::git-rev-list[]
+
+--parents::
+
+	Print also the parents of the commit (in the form "commit parent...").
+	Also enables parent rewriting, see 'History Simplification' below.
+
+--children::
+
+	Print also the children of the commit (in the form "commit child...").
+	Also enables parent rewriting, see 'History Simplification' below.
+
+ifdef::git-rev-list[]
+--timestamp::
+	Print the raw commit timestamp.
+endif::git-rev-list[]
+
+--left-right::
+
+	Mark which side of a symmetric diff a commit is reachable from.
+	Commits from the left side are prefixed with `<` and those from
+	the right with `>`.  If combined with `--boundary`, those
+	commits are prefixed with `-`.
++
+For example, if you have this topology:
++
+-----------------------------------------------------------------------
+	     y---b---b  branch B
+	    / \ /
+	   /   .
+	  /   / \
+	 o---x---a---a  branch A
+-----------------------------------------------------------------------
++
+you would get an output like this:
++
+-----------------------------------------------------------------------
+	$ git rev-list --left-right --boundary --pretty=oneline A...B
+
+	>bbbbbbb... 3rd on b
+	>bbbbbbb... 2nd on b
+	<aaaaaaa... 3rd on a
+	<aaaaaaa... 2nd on a
+	-yyyyyyy... 1st on b
+	-xxxxxxx... 1st on a
+-----------------------------------------------------------------------
+
+--graph::
+
+	Draw a text-based graphical representation of the commit history
+	on the left hand side of the output.  This may cause extra lines
+	to be printed in between commits, in order for the graph history
+	to be drawn properly.
++
+This enables parent rewriting, see 'History Simplification' below.
++
+This implies the '--topo-order' option by default, but the
+'--date-order' option may also be specified.
+
+ifdef::git-rev-list[]
+--count::
+	Print a number stating how many commits would have been
+	listed, and suppress all other output.  When used together
+	with '--left-right', instead print the counts for left and
+	right commits, separated by a tab. When used together with
+	'--cherry-mark', omit patch equivalent commits from these
+	counts and print the count for equivalent commits separated
+	by a tab.
+endif::git-rev-list[]
+
+
+ifndef::git-rev-list[]
+Diff Formatting
+~~~~~~~~~~~~~~~
+
+Below are listed options that control the formatting of diff output.
+Some of them are specific to linkgit:git-rev-list[1], however other diff
+options may be given. See linkgit:git-diff-files[1] for more options.
+
+-c::
+
+	With this option, diff output for a merge commit
+	shows the differences from each of the parents to the merge result
+	simultaneously instead of showing pairwise diff between a parent
+	and the result one at a time. Furthermore, it lists only files
+	which were modified from all parents.
+
+--cc::
+
+	This flag implies the '-c' option and further compresses the
+	patch output by omitting uninteresting hunks whose contents in
+	the parents have only two variants and the merge result picks
+	one of them without modification.
+
+-m::
+
+	This flag makes the merge commits show the full diff like
+	regular commits; for each merge parent, a separate log entry
+	and diff is generated. An exception is that only diff against
+	the first parent is shown when '--first-parent' option is given;
+	in that case, the output represents the changes the merge
+	brought _into_ the then-current branch.
+
+-r::
+
+	Show recursive diffs.
+
+-t::
+
+	Show the tree objects in the diff output. This implies '-r'.
+
+-s::
+	Suppress diff output.
+endif::git-rev-list[]
diff --git a/Documentation/revisions.txt b/Documentation/revisions.txt
new file mode 100644
index 0000000..1725661
--- /dev/null
+++ b/Documentation/revisions.txt
@@ -0,0 +1,230 @@
+SPECIFYING REVISIONS
+--------------------
+
+A revision parameter '<rev>' typically, but not necessarily, names a
+commit object.  It uses what is called an 'extended SHA1'
+syntax.  Here are various ways to spell object names.  The
+ones listed near the end of this list name trees and
+blobs contained in a commit.
+
+'<sha1>', e.g. 'dae86e1950b1277e545cee180551750029cfe735', 'dae86e'::
+  The full SHA1 object name (40-byte hexadecimal string), or
+  a leading substring that is unique within the repository.
+  E.g. dae86e1950b1277e545cee180551750029cfe735 and dae86e both
+  name the same commit object if there is no other object in
+  your repository whose object name starts with dae86e.
+
+'<describeOutput>', e.g. 'v1.7.4.2-679-g3bee7fb'::
+  Output from `git describe`; i.e. a closest tag, optionally
+  followed by a dash and a number of commits, followed by a dash, a
+  'g', and an abbreviated object name.
+
+'<refname>', e.g. 'master', 'heads/master', 'refs/heads/master'::
+  A symbolic ref name.  E.g. 'master' typically means the commit
+  object referenced by 'refs/heads/master'.  If you
+  happen to have both 'heads/master' and 'tags/master', you can
+  explicitly say 'heads/master' to tell git which one you mean.
+  When ambiguous, a '<name>' is disambiguated by taking the
+  first match in the following rules:
+
+  . If '$GIT_DIR/<name>' exists, that is what you mean (this is usually
+    useful only for 'HEAD', 'FETCH_HEAD', 'ORIG_HEAD', 'MERGE_HEAD'
+    and 'CHERRY_PICK_HEAD');
+
+  . otherwise, 'refs/<name>' if it exists;
+
+  . otherwise, 'refs/tags/<refname>' if it exists;
+
+  . otherwise, 'refs/heads/<name>' if it exists;
+
+  . otherwise, 'refs/remotes/<name>' if it exists;
+
+  . otherwise, 'refs/remotes/<name>/HEAD' if it exists.
++
+'HEAD' names the commit on which you based the changes in the working tree.
+'FETCH_HEAD' records the branch which you fetched from a remote repository
+with your last `git fetch` invocation.
+'ORIG_HEAD' is created by commands that move your 'HEAD' in a drastic
+way, to record the position of the 'HEAD' before their operation, so that
+you can easily change the tip of the branch back to the state before you ran
+them.
+'MERGE_HEAD' records the commit(s) which you are merging into your branch
+when you run `git merge`.
+'CHERRY_PICK_HEAD' records the commit which you are cherry-picking
+when you run `git cherry-pick`.
++
+Note that any of the 'refs/*' cases above may come either from
+the '$GIT_DIR/refs' directory or from the '$GIT_DIR/packed-refs' file.
+
+'<refname>@\{<date>\}', e.g. 'master@\{yesterday\}', 'HEAD@\{5 minutes ago\}'::
+  A ref followed by the suffix '@' with a date specification
+  enclosed in a brace
+  pair (e.g. '\{yesterday\}', '\{1 month 2 weeks 3 days 1 hour 1
+  second ago\}' or '\{1979-02-26 18:30:00\}') specifies the value
+  of the ref at a prior point in time.  This suffix may only be
+  used immediately following a ref name and the ref must have an
+  existing log ('$GIT_DIR/logs/<ref>'). Note that this looks up the state
+  of your *local* ref at a given time; e.g., what was in your local
+  'master' branch last week. If you want to look at commits made during
+  certain times, see '--since' and '--until'.
+
+'<refname>@\{<n>\}', e.g. 'master@\{1\}'::
+  A ref followed by the suffix '@' with an ordinal specification
+  enclosed in a brace pair (e.g. '\{1\}', '\{15\}') specifies
+  the n-th prior value of that ref.  For example 'master@\{1\}'
+  is the immediate prior value of 'master' while 'master@\{5\}'
+  is the 5th prior value of 'master'. This suffix may only be used
+  immediately following a ref name and the ref must have an existing
+  log ('$GIT_DIR/logs/<refname>').
+
+'@\{<n>\}', e.g. '@\{1\}'::
+  You can use the '@' construct with an empty ref part to get at a
+  reflog entry of the current branch. For example, if you are on
+  branch 'blabla' then '@\{1\}' means the same as 'blabla@\{1\}'.
+
+'@\{-<n>\}', e.g. '@\{-1\}'::
+  The construct '@\{-<n>\}' means the <n>th branch checked out
+  before the current one.
+
+'<refname>@\{upstream\}', e.g. 'master@\{upstream\}', '@\{u\}'::
+  The suffix '@\{upstream\}' to a ref (short form '<refname>@\{u\}') refers to
+  the branch the ref is set to build on top of.  A missing ref defaults
+  to the current branch.
+
+'<rev>{caret}', e.g. 'HEAD{caret}, v1.5.1{caret}0'::
+  A suffix '{caret}' to a revision parameter means the first parent of
+  that commit object.  '{caret}<n>' means the <n>th parent (i.e.
+  '<rev>{caret}'
+  is equivalent to '<rev>{caret}1').  As a special rule,
+  '<rev>{caret}0' means the commit itself and is used when '<rev>' is the
+  object name of a tag object that refers to a commit object.
+
+'<rev>{tilde}<n>', e.g. 'master{tilde}3'::
+  A suffix '{tilde}<n>' to a revision parameter means the commit
+  object that is the <n>th generation ancestor of the named
+  commit object, following only the first parents.  I.e. '<rev>{tilde}3' is
+  equivalent to '<rev>{caret}{caret}{caret}' which is equivalent to
+  '<rev>{caret}1{caret}1{caret}1'.  See below for an illustration of
+  the usage of this form.
+
+'<rev>{caret}\{<type>\}', e.g. 'v0.99.8{caret}\{commit\}'::
+  A suffix '{caret}' followed by an object type name enclosed in
+  brace pair means the object
+  could be a tag, and dereference the tag recursively until an
+  object of that type is found or the object cannot be
+  dereferenced anymore (in which case, barf).  '<rev>{caret}0'
+  is a short-hand for '<rev>{caret}\{commit\}'.
+
+'<rev>{caret}\{\}', e.g. 'v0.99.8{caret}\{\}'::
+  A suffix '{caret}' followed by an empty brace pair
+  means the object could be a tag,
+  and dereference the tag recursively until a non-tag object is
+  found.
+
+'<rev>{caret}\{/<text>\}', e.g. 'HEAD^{/fix nasty bug}'::
+  A suffix '{caret}' to a revision parameter, followed by a brace
+  pair that contains a text led by a slash,
+  is the same as the ':/fix nasty bug' syntax below except that
+  it returns the youngest matching commit which is reachable from
+  the '<rev>' before '{caret}'.
+
+':/<text>', e.g. ':/fix nasty bug'::
+  A colon, followed by a slash, followed by a text, names
+  a commit whose commit message matches the specified regular expression.
+  This name returns the youngest matching commit which is
+  reachable from any ref.  If the commit message starts with a
+  '!' you have to repeat that;  the special sequence ':/!',
+  followed by something else than '!', is reserved for now.
+  The regular expression can match any part of the commit message. To
+  match messages starting with a string, one can use e.g. ':/^foo'.
+
+'<rev>:<path>', e.g. 'HEAD:README', ':README', 'master:./README'::
+  A suffix ':' followed by a path names the blob or tree
+  at the given path in the tree-ish object named by the part
+  before the colon.
+  ':path' (with an empty part before the colon)
+  is a special case of the syntax described next: content
+  recorded in the index at the given path.
+  A path starting with './' or '../' is relative to the current working directory.
+  The given path will be converted to be relative to the working tree's root directory.
+  This is most useful to address a blob or tree from a commit or tree that has
+  the same tree structure as the working tree.
+
+':<n>:<path>', e.g. ':0:README', ':README'::
+  A colon, optionally followed by a stage number (0 to 3) and a
+  colon, followed by a path, names a blob object in the
+  index at the given path. A missing stage number (and the colon
+  that follows it) names a stage 0 entry. During a merge, stage
+  1 is the common ancestor, stage 2 is the target branch's version
+  (typically the current branch), and stage 3 is the version from
+  the branch which is being merged.
+
+Here is an illustration, by Jon Loeliger.  Both commit nodes B
+and C are parents of commit node A.  Parent commits are ordered
+left-to-right.
+
+........................................
+G   H   I   J
+ \ /     \ /
+  D   E   F
+   \  |  / \
+    \ | /   |
+     \|/    |
+      B     C
+       \   /
+        \ /
+         A
+........................................
+
+    A =      = A^0
+    B = A^   = A^1     = A~1
+    C = A^2  = A^2
+    D = A^^  = A^1^1   = A~2
+    E = B^2  = A^^2
+    F = B^3  = A^^3
+    G = A^^^ = A^1^1^1 = A~3
+    H = D^2  = B^^2    = A^^^2  = A~2^2
+    I = F^   = B^3^    = A^^3^
+    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.  When you have two commits 'r1' and 'r2' (named according
+to the syntax explained in SPECIFYING REVISIONS above), you can ask
+for commits that are reachable from r2 excluding those that are reachable
+from r1 by '{caret}r1 r2' and it can be written as 'r1..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 is the set of commits that are reachable from either one of
+'r1' or 'r2' but not from both.
+
+Two other shorthands for naming a set that is formed by a commit
+and its parent commits exist.  The 'r1{caret}@' notation means all
+parents of 'r1'.  'r1{caret}!' includes commit 'r1' but excludes
+all of its parents.
+
+Here are a handful of examples:
+
+   D                G H D
+   D F              G H I J D F
+   ^G D             H D
+   ^D B             E I J F B
+   B...C            G H D E B C
+   ^D B C           E I J F B C
+   C^@              I J F
+   F^! D            G H D F
diff --git a/Documentation/sequencer.txt b/Documentation/sequencer.txt
new file mode 100644
index 0000000..5747f44
--- /dev/null
+++ b/Documentation/sequencer.txt
@@ -0,0 +1,12 @@
+--continue::
+	Continue the operation in progress using the information in
+	'.git/sequencer'.  Can be used to continue after resolving
+	conflicts in a failed cherry-pick or revert.
+
+--quit::
+	Forget about the current operation in progress.  Can be used
+	to clear the sequencer state after a failed cherry-pick or
+	revert.
+
+--abort::
+	Cancel the operation and return to the pre-sequence state.
diff --git a/Documentation/technical/api-argv-array.txt b/Documentation/technical/api-argv-array.txt
new file mode 100644
index 0000000..1b7d8f1
--- /dev/null
+++ b/Documentation/technical/api-argv-array.txt
@@ -0,0 +1,51 @@
+argv-array API
+==============
+
+The argv-array API allows one to dynamically build and store
+NULL-terminated lists.  An argv-array maintains the invariant that the
+`argv` member always points to a non-NULL array, and that the array is
+always NULL-terminated at the element pointed to by `argv[argc]`. This
+makes the result suitable for passing to functions expecting to receive
+argv from main(), or the link:api-run-command.html[run-command API].
+
+The link:api-string-list.html[string-list API] is similar, but cannot be
+used for these purposes; instead of storing a straight string pointer,
+it contains an item structure with a `util` field that is not compatible
+with the traditional argv interface.
+
+Each `argv_array` manages its own memory. Any strings pushed into the
+array are duplicated, and all memory is freed by argv_array_clear().
+
+Data Structures
+---------------
+
+`struct argv_array`::
+
+	A single array. This should be initialized by assignment from
+	`ARGV_ARRAY_INIT`, or by calling `argv_array_init`. The `argv`
+	member contains the actual array; the `argc` member contains the
+	number of elements in the array, not including the terminating
+	NULL.
+
+Functions
+---------
+
+`argv_array_init`::
+	Initialize an array. This is no different than assigning from
+	`ARGV_ARRAY_INIT`.
+
+`argv_array_push`::
+	Push a copy of a string onto the end of the array.
+
+`argv_array_pushl`::
+	Push a list of strings onto the end of the array. The arguments
+	should be a list of `const char *` strings, terminated by a NULL
+	argument.
+
+`argv_array_pushf`::
+	Format a string and push it onto the end of the array. This is a
+	convenience wrapper combining `strbuf_addf` and `argv_array_push`.
+
+`argv_array_clear`::
+	Free all memory associated with the array and return it to the
+	initial, empty state.
diff --git a/Documentation/technical/api-builtin.txt b/Documentation/technical/api-builtin.txt
index 5cb2b05..b0cafe8 100644
--- a/Documentation/technical/api-builtin.txt
+++ b/Documentation/technical/api-builtin.txt
@@ -49,6 +49,8 @@
 
 . Add an entry for `git-foo` to `command-list.txt`.
 
+. Add an entry for `/git-foo` to `.gitignore`.
+
 
 How a built-in is called
 ------------------------
diff --git a/Documentation/technical/api-config.txt b/Documentation/technical/api-config.txt
new file mode 100644
index 0000000..edf8dfb
--- /dev/null
+++ b/Documentation/technical/api-config.txt
@@ -0,0 +1,140 @@
+config API
+==========
+
+The config API gives callers a way to access git configuration files
+(and files which have the same syntax). See linkgit:git-config[1] for a
+discussion of the config file syntax.
+
+General Usage
+-------------
+
+Config files are parsed linearly, and each variable found is passed to a
+caller-provided callback function. The callback function is responsible
+for any actions to be taken on the config option, and is free to ignore
+some options. It is not uncommon for the configuration to be parsed
+several times during the run of a git program, with different callbacks
+picking out different variables useful to themselves.
+
+A config callback function takes three parameters:
+
+- the name of the parsed variable. This is in canonical "flat" form: the
+  section, subsection, and variable segments will be separated by dots,
+  and the section and variable segments will be all lowercase. E.g.,
+  `core.ignorecase`, `diff.SomeType.textconv`.
+
+- the value of the found variable, as a string. If the variable had no
+  value specified, the value will be NULL (typically this means it
+  should be interpreted as boolean true).
+
+- a void pointer passed in by the caller of the config API; this can
+  contain callback-specific data
+
+A config callback should return 0 for success, or -1 if the variable
+could not be parsed properly.
+
+Basic Config Querying
+---------------------
+
+Most programs will simply want to look up variables in all config files
+that git knows about, using the normal precedence rules. To do this,
+call `git_config` with a callback function and void data pointer.
+
+`git_config` will read all config sources in order of increasing
+priority. Thus a callback should typically overwrite previously-seen
+entries with new ones (e.g., if both the user-wide `~/.gitconfig` and
+repo-specific `.git/config` contain `color.ui`, the config machinery
+will first feed the user-wide one to the callback, and then the
+repo-specific one; by overwriting, the higher-priority repo-specific
+value is left at the end).
+
+The `git_config_with_options` function lets the caller examine config
+while adjusting some of the default behavior of `git_config`. It should
+almost never be used by "regular" git code that is looking up
+configuration variables. It is intended for advanced callers like
+`git-config`, which are intentionally tweaking the normal config-lookup
+process. It takes two extra parameters:
+
+`filename`::
+If this parameter is non-NULL, it specifies the name of a file to
+parse for configuration, rather than looking in the usual files. Regular
+`git_config` defaults to `NULL`.
+
+`respect_includes`::
+Specify whether include directives should be followed in parsed files.
+Regular `git_config` defaults to `1`.
+
+There is a special version of `git_config` called `git_config_early`.
+This version takes an additional parameter to specify the repository
+config, instead of having it looked up via `git_path`. This is useful
+early in a git program before the repository has been found. Unless
+you're working with early setup code, you probably don't want to use
+this.
+
+Reading Specific Files
+----------------------
+
+To read a specific file in git-config format, use
+`git_config_from_file`. This takes the same callback and data parameters
+as `git_config`.
+
+Value Parsing Helpers
+---------------------
+
+To aid in parsing string values, the config API provides callbacks with
+a number of helper functions, including:
+
+`git_config_int`::
+Parse the string to an integer, including unit factors. Dies on error;
+otherwise, returns the parsed result.
+
+`git_config_ulong`::
+Identical to `git_config_int`, but for unsigned longs.
+
+`git_config_bool`::
+Parse a string into a boolean value, respecting keywords like "true" and
+"false". Integer values are converted into true/false values (when they
+are non-zero or zero, respectively). Other values cause a die(). If
+parsing is successful, the return value is the result.
+
+`git_config_bool_or_int`::
+Same as `git_config_bool`, except that integers are returned as-is, and
+an `is_bool` flag is unset.
+
+`git_config_maybe_bool`::
+Same as `git_config_bool`, except that it returns -1 on error rather
+than dying.
+
+`git_config_string`::
+Allocates and copies the value string into the `dest` parameter; if no
+string is given, prints an error message and returns -1.
+
+`git_config_pathname`::
+Similar to `git_config_string`, but expands `~` or `~user` into the
+user's home directory when found at the beginning of the path.
+
+Include Directives
+------------------
+
+By default, the config parser does not respect include directives.
+However, a caller can use the special `git_config_include` wrapper
+callback to support them. To do so, you simply wrap your "real" callback
+function and data pointer in a `struct config_include_data`, and pass
+the wrapper to the regular config-reading functions. For example:
+
+-------------------------------------------
+int read_file_with_include(const char *file, config_fn_t fn, void *data)
+{
+	struct config_include_data inc = CONFIG_INCLUDE_INIT;
+	inc.fn = fn;
+	inc.data = data;
+	return git_config_from_file(git_config_include, file, &inc);
+}
+-------------------------------------------
+
+`git_config` respects includes automatically. The lower-level
+`git_config_from_file` does not.
+
+Writing Config Files
+--------------------
+
+TODO
diff --git a/Documentation/technical/api-credentials.txt b/Documentation/technical/api-credentials.txt
new file mode 100644
index 0000000..5977b58
--- /dev/null
+++ b/Documentation/technical/api-credentials.txt
@@ -0,0 +1,268 @@
+credentials API
+===============
+
+The credentials API provides an abstracted way of gathering username and
+password credentials from the user (even though credentials in the wider
+world can take many forms, in this document the word "credential" always
+refers to a username and password pair).
+
+This document describes two interfaces: the C API that the credential
+subsystem provides to the rest of git, and the protocol that git uses to
+communicate with system-specific "credential helpers". If you are
+writing git code that wants to look up or prompt for credentials, see
+the section "C API" below. If you want to write your own helper, see
+the section on "Credential Helpers" below.
+
+Typical setup
+-------------
+
+------------
++-----------------------+
+| git code (C)          |--- to server requiring --->
+|                       |        authentication
+|.......................|
+| C credential API      |--- prompt ---> User
++-----------------------+
+	^      |
+	| pipe |
+	|      v
++-----------------------+
+| git credential helper |
++-----------------------+
+------------
+
+The git code (typically a remote-helper) will call the C API to obtain
+credential data like a login/password pair (credential_fill). The
+API will itself call a remote helper (e.g. "git credential-cache" or
+"git credential-store") that may retrieve credential data from a
+store. If the credential helper cannot find the information, the C API
+will prompt the user. Then, the caller of the API takes care of
+contacting the server, and does the actual authentication.
+
+C API
+-----
+
+The credential C API is meant to be called by git code which needs to
+acquire or store a credential. It is centered around an object
+representing a single credential and provides three basic operations:
+fill (acquire credentials by calling helpers and/or prompting the user),
+approve (mark a credential as successfully used so that it can be stored
+for later use), and reject (mark a credential as unsuccessful so that it
+can be erased from any persistent storage).
+
+Data Structures
+~~~~~~~~~~~~~~~
+
+`struct credential`::
+
+	This struct represents a single username/password combination
+	along with any associated context. All string fields should be
+	heap-allocated (or NULL if they are not known or not applicable).
+	The meaning of the individual context fields is the same as
+	their counterparts in the helper protocol; see the section below
+	for a description of each field.
++
+The `helpers` member of the struct is a `string_list` of helpers.  Each
+string specifies an external helper which will be run, in order, to
+either acquire or store credentials. See the section on credential
+helpers below. This list is filled-in by the API functions
+according to the corresponding configuration variables before
+consulting helpers, so there usually is no need for a caller to
+modify the helpers field at all.
++
+This struct should always be initialized with `CREDENTIAL_INIT` or
+`credential_init`.
+
+
+Functions
+~~~~~~~~~
+
+`credential_init`::
+
+	Initialize a credential structure, setting all fields to empty.
+
+`credential_clear`::
+
+	Free any resources associated with the credential structure,
+	returning it to a pristine initialized state.
+
+`credential_fill`::
+
+	Instruct the credential subsystem to fill the username and
+	password fields of the passed credential struct by first
+	consulting helpers, then asking the user. After this function
+	returns, the username and password fields of the credential are
+	guaranteed to be non-NULL. If an error occurs, the function will
+	die().
+
+`credential_reject`::
+
+	Inform the credential subsystem that the provided credentials
+	have been rejected. This will cause the credential subsystem to
+	notify any helpers of the rejection (which allows them, for
+	example, to purge the invalid credentials from storage).  It
+	will also free() the username and password fields of the
+	credential and set them to NULL (readying the credential for
+	another call to `credential_fill`). Any errors from helpers are
+	ignored.
+
+`credential_approve`::
+
+	Inform the credential subsystem that the provided credentials
+	were successfully used for authentication.  This will cause the
+	credential subsystem to notify any helpers of the approval, so
+	that they may store the result to be used again.  Any errors
+	from helpers are ignored.
+
+`credential_from_url`::
+
+	Parse a URL into broken-down credential fields.
+
+Example
+~~~~~~~
+
+The example below shows how the functions of the credential API could be
+used to login to a fictitious "foo" service on a remote host:
+
+-----------------------------------------------------------------------
+int foo_login(struct foo_connection *f)
+{
+	int status;
+	/*
+	 * Create a credential with some context; we don't yet know the
+	 * username or password.
+	 */
+
+	struct credential c = CREDENTIAL_INIT;
+	c.protocol = xstrdup("foo");
+	c.host = xstrdup(f->hostname);
+
+	/*
+	 * Fill in the username and password fields by contacting
+	 * helpers and/or asking the user. The function will die if it
+	 * fails.
+	 */
+	credential_fill(&c);
+
+	/*
+	 * Otherwise, we have a username and password. Try to use it.
+	 */
+	status = send_foo_login(f, c.username, c.password);
+	switch (status) {
+	case FOO_OK:
+		/* It worked. Store the credential for later use. */
+		credential_accept(&c);
+		break;
+	case FOO_BAD_LOGIN:
+		/* Erase the credential from storage so we don't try it
+		 * again. */
+		credential_reject(&c);
+		break;
+	default:
+		/*
+		 * Some other error occured. We don't know if the
+		 * credential is good or bad, so report nothing to the
+		 * credential subsystem.
+		 */
+	}
+
+	/* Free any associated resources. */
+	credential_clear(&c);
+
+	return status;
+}
+-----------------------------------------------------------------------
+
+
+Credential Helpers
+------------------
+
+Credential helpers are programs executed by git to fetch or save
+credentials from and to long-term storage (where "long-term" is simply
+longer than a single git process; e.g., credentials may be stored
+in-memory for a few minutes, or indefinitely on disk).
+
+Each helper is specified by a single string in the configuration
+variable `credential.helper` (and others, see linkgit:git-config[1]).
+The string is transformed by git into a command to be executed using
+these rules:
+
+  1. If the helper string begins with "!", it is considered a shell
+     snippet, and everything after the "!" becomes the command.
+
+  2. Otherwise, if the helper string begins with an absolute path, the
+     verbatim helper string becomes the command.
+
+  3. Otherwise, the string "git credential-" is prepended to the helper
+     string, and the result becomes the command.
+
+The resulting command then has an "operation" argument appended to it
+(see below for details), and the result is executed by the shell.
+
+Here are some example specifications:
+
+----------------------------------------------------
+# run "git credential-foo"
+foo
+
+# same as above, but pass an argument to the helper
+foo --bar=baz
+
+# the arguments are parsed by the shell, so use shell
+# quoting if necessary
+foo --bar="whitespace arg"
+
+# you can also use an absolute path, which will not use the git wrapper
+/path/to/my/helper --with-arguments
+
+# or you can specify your own shell snippet
+!f() { echo "password=`cat $HOME/.secret`"; }; f
+----------------------------------------------------
+
+Generally speaking, rule (3) above is the simplest for users to specify.
+Authors of credential helpers should make an effort to assist their
+users by naming their program "git-credential-$NAME", and putting it in
+the $PATH or $GIT_EXEC_PATH during installation, which will allow a user
+to enable it with `git config credential.helper $NAME`.
+
+When a helper is executed, it will have one "operation" argument
+appended to its command line, which is one of:
+
+`get`::
+
+	Return a matching credential, if any exists.
+
+`store`::
+
+	Store the credential, if applicable to the helper.
+
+`erase`::
+
+	Remove a matching credential, if any, from the helper's storage.
+
+The details of the credential will be provided on the helper's stdin
+stream. The exact format is the same as the input/output format of the
+`git credential` plumbing command (see the section `INPUT/OUTPUT
+FORMAT` in linkgit:git-credential[7] for a detailed specification).
+
+For a `get` operation, the helper should produce a list of attributes
+on stdout in the same format. A helper is free to produce a subset, or
+even no values at all if it has nothing useful to provide. Any provided
+attributes will overwrite those already known about by git.
+
+For a `store` or `erase` operation, the helper's output is ignored.
+If it fails to perform the requested operation, it may complain to
+stderr to inform the user. If it does not support the requested
+operation (e.g., a read-only store), it should silently ignore the
+request.
+
+If a helper receives any other operation, it should silently ignore the
+request. This leaves room for future operations to be added (older
+helpers will just ignore the new requests).
+
+See also
+--------
+
+linkgit:gitcredentials[7]
+
+linkgit:git-config[5] (See configuration variables `credential.*`)
diff --git a/Documentation/technical/api-diff.txt b/Documentation/technical/api-diff.txt
index 20b0241..2d2ebc0 100644
--- a/Documentation/technical/api-diff.txt
+++ b/Documentation/technical/api-diff.txt
@@ -32,7 +32,7 @@
 
 * As you find different pairs of files, call `diff_change()` to feed
   modified files, `diff_addremove()` to feed created or deleted files,
-  or `diff_unmerged()` to feed a file whose state is 'unmerged' to the
+  or `diff_unmerge()` to feed a file whose state is 'unmerged' to the
   API.  These are thin wrappers to a lower-level `diff_queue()` function
   that is flexible enough to record any of these kinds of changes.
 
@@ -50,7 +50,7 @@
 This is the internal representation for a single file (blob).  It
 records the blob object name (if known -- for a work tree file it
 typically is a NUL SHA-1), filemode and pathname.  This is what the
-`diff_addremove()`, `diff_change()` and `diff_unmerged()` synthesize and
+`diff_addremove()`, `diff_change()` and `diff_unmerge()` synthesize and
 feed `diff_queue()` function with.
 
 * `struct diff_filepair`
diff --git a/Documentation/technical/api-directory-listing.txt b/Documentation/technical/api-directory-listing.txt
index 5bbd18f..add6f43 100644
--- a/Documentation/technical/api-directory-listing.txt
+++ b/Documentation/technical/api-directory-listing.txt
@@ -58,6 +58,9 @@
 Calling sequence
 ----------------
 
+Note: index may be looked at for .gitignore files that are CE_SKIP_WORKTREE
+marked. If you to exclude files, make sure you have loaded index first.
+
 * Prepare `struct dir_struct dir` and clear it with `memset(&dir, 0,
   sizeof(dir))`.
 
diff --git a/Documentation/technical/api-gitattributes.txt b/Documentation/technical/api-gitattributes.txt
index 9d97eaa..ce363b6 100644
--- a/Documentation/technical/api-gitattributes.txt
+++ b/Documentation/technical/api-gitattributes.txt
@@ -11,27 +11,15 @@
 `struct git_attr`::
 
 	An attribute is an opaque object that is identified by its name.
-	Pass the name and its length to `git_attr()` function to obtain
-	the object of this type.  The internal representation of this
-	structure is of no interest to the calling programs.
+	Pass the name to `git_attr()` function to obtain the object of
+	this type.  The internal representation of this structure is
+	of no interest to the calling programs.  The name of the
+	attribute can be retrieved by calling `git_attr_name()`.
 
 `struct git_attr_check`::
 
 	This structure represents a set of attributes to check in a call
-	to `git_checkattr()` function, and receives the results.
-
-
-Calling Sequence
-----------------
-
-* Prepare an array of `struct git_attr_check` to define the list of
-  attributes you would want to check.  To populate this array, you would
-  need to define necessary attributes by calling `git_attr()` function.
-
-* Call git_checkattr() to check the attributes for the path.
-
-* Inspect `git_attr_check` structure to see how each of the attribute in
-  the array is defined for the path.
+	to `git_check_attr()` function, and receives the results.
 
 
 Attribute Values
@@ -57,6 +45,19 @@
 value of the attribute for the path.
 
 
+Querying Specific Attributes
+----------------------------
+
+* Prepare an array of `struct git_attr_check` to define the list of
+  attributes you would want to check.  To populate this array, you would
+  need to define necessary attributes by calling `git_attr()` function.
+
+* Call `git_check_attr()` to check the attributes for the path.
+
+* Inspect `git_attr_check` structure to see how each of the attribute in
+  the array is defined for the path.
+
+
 Example
 -------
 
@@ -72,18 +73,18 @@
 {
 	if (check[0].attr)
 		return; /* already done */
-	check[0].attr = git_attr("crlf", 4);
-	check[1].attr = git_attr("ident", 5);
+	check[0].attr = git_attr("crlf");
+	check[1].attr = git_attr("ident");
 }
 ------------
 
-. Call `git_checkattr()` with the prepared array of `struct git_attr_check`:
+. Call `git_check_attr()` with the prepared array of `struct git_attr_check`:
 
 ------------
 	const char *path;
 
 	setup_check();
-	git_checkattr(path, ARRAY_SIZE(check), check);
+	git_check_attr(path, ARRAY_SIZE(check), check);
 ------------
 
 . Act on `.value` member of the result, left in `check[]`:
@@ -108,4 +109,20 @@
 	}
 ------------
 
-(JC)
+
+Querying All Attributes
+-----------------------
+
+To get the values of all attributes associated with a file:
+
+* Call `git_all_attrs()`, which returns an array of `git_attr_check`
+  structures.
+
+* Iterate over the `git_attr_check` array to examine the attribute
+  names and values.  The name of the attribute described by a
+  `git_attr_check` object can be retrieved via
+  `git_attr_name(check[i].attr)`.  (Please note that no items will be
+  returned for unset attributes, so `ATTR_UNSET()` will return false
+  for all returned `git_array_check` objects.)
+
+* Free the `git_array_check` array.
diff --git a/Documentation/technical/api-hash.txt b/Documentation/technical/api-hash.txt
index c784d3e..e5061e0 100644
--- a/Documentation/technical/api-hash.txt
+++ b/Documentation/technical/api-hash.txt
@@ -1,6 +1,52 @@
 hash API
 ========
 
-Talk about <hash.h>
+The hash API is a collection of simple hash table functions. Users are expected
+to implement their own hashing.
 
-(Linus)
+Data Structures
+---------------
+
+`struct hash_table`::
+
+	The hash table structure. The `array` member points to the hash table
+	entries. The `size` member counts the total number of valid and invalid
+	entries in the table. The `nr` member keeps track of the number of
+	valid entries.
+
+`struct hash_table_entry`::
+
+	An opaque structure representing an entry in the hash table. The `hash`
+	member is the entry's hash key and the `ptr` member is the entry's
+	value.
+
+Functions
+---------
+
+`init_hash`::
+
+	Initialize the hash table.
+
+`free_hash`::
+
+	Release memory associated with the hash table.
+
+`insert_hash`::
+
+	Insert a pointer into the hash table. If an entry with that hash
+	already exists, a pointer to the existing entry's value is returned.
+	Otherwise NULL is returned.  This allows callers to implement
+	chaining, etc.
+
+`lookup_hash`::
+
+	Lookup an entry in the hash table. If an entry with that hash exists
+	the entry's value is returned. Otherwise NULL is returned.
+
+`for_each_hash`::
+
+	Call a function for each entry in the hash table. The function is
+	expected to take the entry's value as its only argument and return an
+	int. If the function returns a negative int the loop is aborted
+	immediately.  Otherwise, the return value is accumulated and the sum
+	returned upon completion of the loop.
diff --git a/Documentation/technical/api-history-graph.txt b/Documentation/technical/api-history-graph.txt
index d66e61b..d6fc90a 100644
--- a/Documentation/technical/api-history-graph.txt
+++ b/Documentation/technical/api-history-graph.txt
@@ -11,9 +11,6 @@
 
 * `graph_init()` creates a new `struct git_graph`
 
-* `graph_release()` destroys a `struct git_graph`, and frees the memory
-  associated with it.
-
 * `graph_update()` moves the graph to a new commit.
 
 * `graph_next_line()` outputs the next line of the graph into a strbuf.  It
@@ -134,8 +131,6 @@
 			putchar(opts->diffopt.line_termination);
 	}
 }
-
-graph_release(graph);
 ------------
 
 Sample output
diff --git a/Documentation/technical/api-merge.txt b/Documentation/technical/api-merge.txt
new file mode 100644
index 0000000..9dc1bed
--- /dev/null
+++ b/Documentation/technical/api-merge.txt
@@ -0,0 +1,104 @@
+merge API
+=========
+
+The merge API helps a program to reconcile two competing sets of
+improvements to some files (e.g., unregistered changes from the work
+tree versus changes involved in switching to a new branch), reporting
+conflicts if found.  The library called through this API is
+responsible for a few things.
+
+ * determining which trees to merge (recursive ancestor consolidation);
+
+ * lining up corresponding files in the trees to be merged (rename
+   detection, subtree shifting), reporting edge cases like add/add
+   and rename/rename conflicts to the user;
+
+ * performing a three-way merge of corresponding files, taking
+   path-specific merge drivers (specified in `.gitattributes`)
+   into account.
+
+Data structures
+---------------
+
+* `mmbuffer_t`, `mmfile_t`
+
+These store data usable for use by the xdiff backend, for writing and
+for reading, respectively.  See `xdiff/xdiff.h` for the definitions
+and `diff.c` for examples.
+
+* `struct ll_merge_options`
+
+This describes the set of options the calling program wants to affect
+the operation of a low-level (single file) merge.  Some options:
+
+`virtual_ancestor`::
+	Behave as though this were part of a merge between common
+	ancestors in a recursive merge.
+	If a helper program is specified by the
+	`[merge "<driver>"] recursive` configuration, it will
+	be used (see linkgit:gitattributes[5]).
+
+`variant`::
+	Resolve local conflicts automatically in favor
+	of one side or the other (as in 'git merge-file'
+	`--ours`/`--theirs`/`--union`).  Can be `0`,
+	`XDL_MERGE_FAVOR_OURS`, `XDL_MERGE_FAVOR_THEIRS`, or
+	`XDL_MERGE_FAVOR_UNION`.
+
+`renormalize`::
+	Resmudge and clean the "base", "theirs" and "ours" files
+	before merging.  Use this when the merge is likely to have
+	overlapped with a change in smudge/clean or end-of-line
+	normalization rules.
+
+Low-level (single file) merge
+-----------------------------
+
+`ll_merge`::
+
+	Perform a three-way single-file merge in core.  This is
+	a thin wrapper around `xdl_merge` that takes the path and
+	any merge backend specified in `.gitattributes` or
+	`.git/info/attributes` into account.  Returns 0 for a
+	clean merge.
+
+Calling sequence:
+
+* Prepare a `struct ll_merge_options` to record options.
+  If you have no special requests, skip this and pass `NULL`
+  as the `opts` parameter to use the default options.
+
+* Allocate an mmbuffer_t variable for the result.
+
+* Allocate and fill variables with the file's original content
+  and two modified versions (using `read_mmfile`, for example).
+
+* Call `ll_merge()`.
+
+* Read the merged content from `result_buf.ptr` and `result_buf.size`.
+
+* Release buffers when finished.  A simple
+  `free(ancestor.ptr); free(ours.ptr); free(theirs.ptr);
+  free(result_buf.ptr);` will do.
+
+If the modifications do not merge cleanly, `ll_merge` will return a
+nonzero value and `result_buf` will generally include a description of
+the conflict bracketed by markers such as the traditional `<<<<<<<`
+and `>>>>>>>`.
+
+The `ancestor_label`, `our_label`, and `their_label` parameters are
+used to label the different sides of a conflict if the merge driver
+supports this.
+
+Everything else
+---------------
+
+Talk about <merge-recursive.h> and merge_file():
+
+ - merge_trees() to merge with rename detection
+ - merge_recursive() for ancestor consolidation
+ - try_merge_command() for other strategies
+ - conflict format
+ - merge options
+
+(Daniel, Miklos, Stephan, JC)
diff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt
index 50f9e9a..3062389 100644
--- a/Documentation/technical/api-parse-options.txt
+++ b/Documentation/technical/api-parse-options.txt
@@ -21,7 +21,7 @@
 * There are basically two forms of options:
   'Short options' consist of one dash (`-`) and one alphanumeric
   character.
-  'Long options' begin with two dashes (`\--`) and some
+  'Long options' begin with two dashes (`--`) and some
   alphanumeric characters.
 
 * Options are case-sensitive.
@@ -31,7 +31,7 @@
 
 * 'sticked' and 'separate form' of options with arguments.
   `-oArg` is sticked, `-o Arg` is separate form.
-  `\--option=Arg` is sticked, `\--option Arg` is separate form.
+  `--option=Arg` is sticked, `--option Arg` is separate form.
 
 * Long options may be 'abbreviated', as long as the abbreviation
   is unambiguous.
@@ -39,11 +39,12 @@
 * Short options may be bundled, e.g. `-a -b` can be specified as `-ab`.
 
 * Boolean long options can be 'negated' (or 'unset') by prepending
-  `no-`, e.g. `\--no-abbrev` instead of `\--abbrev`.
+  `no-`, e.g. `--no-abbrev` instead of `--abbrev`. Conversely,
+  options that begin with `no-` can be 'negated' by removing it.
 
-* Options and non-option arguments can clearly be separated using the `\--`
-  option, e.g. `-a -b \--option \-- \--this-is-a-file` indicates that
-  `\--this-is-a-file` must not be processed as an option.
+* Options and non-option arguments can clearly be separated using the `--`
+  option, e.g. `-a -b --option -- --this-is-a-file` indicates that
+  `--this-is-a-file` must not be processed as an option.
 
 Steps to parse options
 ----------------------
@@ -75,7 +76,7 @@
 Flags are the bitwise-or of:
 
 `PARSE_OPT_KEEP_DASHDASH`::
-	Keep the `\--` that usually separates options from
+	Keep the `--` that usually separates options from
 	non-option arguments.
 
 `PARSE_OPT_STOP_AT_NON_OPTION`::
@@ -113,25 +114,36 @@
 There are some macros to easily define options:
 
 `OPT__ABBREV(&int_var)`::
-	Add `\--abbrev[=<n>]`.
+	Add `--abbrev[=<n>]`.
 
-`OPT__DRY_RUN(&int_var)`::
-	Add `-n, \--dry-run`.
+`OPT__COLOR(&int_var, description)`::
+	Add `--color[=<when>]` and `--no-color`.
 
-`OPT__QUIET(&int_var)`::
-	Add `-q, \--quiet`.
+`OPT__DRY_RUN(&int_var, description)`::
+	Add `-n, --dry-run`.
 
-`OPT__VERBOSE(&int_var)`::
-	Add `-v, \--verbose`.
+`OPT__FORCE(&int_var, description)`::
+	Add `-f, --force`.
+
+`OPT__QUIET(&int_var, description)`::
+	Add `-q, --quiet`.
+
+`OPT__VERBOSE(&int_var, description)`::
+	Add `-v, --verbose`.
 
 `OPT_GROUP(description)`::
 	Start an option group. `description` is a short string that
 	describes the group or an empty string.
 	Start the description with an upper-case letter.
 
-`OPT_BOOLEAN(short, long, &int_var, description)`::
-	Introduce a boolean option.
-	`int_var` is incremented on each use.
+`OPT_BOOL(short, long, &int_var, description)`::
+	Introduce a boolean option. `int_var` is set to one with
+	`--option` and set to zero with `--no-option`.
+
+`OPT_COUNTUP(short, long, &int_var, description)`::
+	Introduce a count-up option.
+	`int_var` is incremented on each use of `--option`, and
+	reset to zero with `--no-option`.
 
 `OPT_BIT(short, long, &int_var, description, mask)`::
 	Introduce a boolean option.
@@ -142,8 +154,9 @@
 	If used, `int_var` is bitwise-anded with the inverted `mask`.
 
 `OPT_SET_INT(short, long, &int_var, description, integer)`::
-	Introduce a boolean option.
-	If used, set `int_var` to `integer`.
+	Introduce an integer option.
+	`int_var` is set to `integer` with `--option`, and
+	reset to zero with `--no-option`.
 
 `OPT_SET_PTR(short, long, &ptr_var, description, ptr)`::
 	Introduce a boolean option.
@@ -183,16 +196,30 @@
 	arguments.  Short options that happen to be digits take
 	precedence over it.
 
+`OPT_COLOR_FLAG(short, long, &int_var, description)`::
+	Introduce an option that takes an optional argument that can
+	have one of three values: "always", "never", or "auto".  If the
+	argument is not given, it defaults to "always".  The `--no-` form
+	works like `--long=never`; it cannot take an argument.  If
+	"always", set `int_var` to 1; if "never", set `int_var` to 0; if
+	"auto", set `int_var` to 1 if stdout is a tty or a pager,
+	0 otherwise.
+
+`OPT_NOOP_NOARG(short, long)`::
+	Introduce an option that has no effect and takes no arguments.
+	Use it to hide deprecated options that are still to be recognized
+	and ignored silently.
+
 
 The last element of the array must be `OPT_END()`.
 
 If not stated otherwise, interpret the arguments as follows:
 
 * `short` is a character for the short option
-  (e.g. `\'e\'` for `-e`, use `0` to omit),
+  (e.g. `'e'` for `-e`, use `0` to omit),
 
 * `long` is a string for the long option
-  (e.g. `"example"` for `\--example`, use `NULL` to omit),
+  (e.g. `"example"` for `--example`, use `NULL` to omit),
 
 * `int_var` is an integer variable,
 
@@ -217,7 +244,7 @@
 
 * Inside `func`, the only interesting member of the structure
   given by `opt` is the void pointer `opt->value`.
-  `\*opt->value` will be the value that is saved into `var`, if you
+  `*opt->value` will be the value that is saved into `var`, if you
   use `OPT_CALLBACK()`.
   For example, do `*(unsigned long *)opt->value = 42;` to get 42
   into an `unsigned long` variable.
diff --git a/Documentation/technical/api-ref-iteration.txt b/Documentation/technical/api-ref-iteration.txt
new file mode 100644
index 0000000..dbbea95
--- /dev/null
+++ b/Documentation/technical/api-ref-iteration.txt
@@ -0,0 +1,81 @@
+ref iteration API
+=================
+
+
+Iteration of refs is done by using an iterate function which will call a
+callback function for every ref. The callback function has this
+signature:
+
+	int handle_one_ref(const char *refname, const unsigned char *sha1,
+			   int flags, void *cb_data);
+
+There are different kinds of iterate functions which all take a
+callback of this type. The callback is then called for each found ref
+until the callback returns nonzero. The returned value is then also
+returned by the iterate function.
+
+Iteration functions
+-------------------
+
+* `head_ref()` just iterates the head ref.
+
+* `for_each_ref()` iterates all refs.
+
+* `for_each_ref_in()` iterates all refs which have a defined prefix and
+  strips that prefix from the passed variable refname.
+
+* `for_each_tag_ref()`, `for_each_branch_ref()`, `for_each_remote_ref()`,
+  `for_each_replace_ref()` iterate refs from the respective area.
+
+* `for_each_glob_ref()` iterates all refs that match the specified glob
+  pattern.
+
+* `for_each_glob_ref_in()` the previous and `for_each_ref_in()` combined.
+
+* `head_ref_submodule()`, `for_each_ref_submodule()`,
+  `for_each_ref_in_submodule()`, `for_each_tag_ref_submodule()`,
+  `for_each_branch_ref_submodule()`, `for_each_remote_ref_submodule()`
+  do the same as the functions descibed above but for a specified
+  submodule.
+
+* `for_each_rawref()` can be used to learn about broken ref and symref.
+
+* `for_each_reflog()` iterates each reflog file.
+
+Submodules
+----------
+
+If you want to iterate the refs of a submodule you first need to add the
+submodules object database. You can do this by a code-snippet like
+this:
+
+	const char *path = "path/to/submodule"
+	if (!add_submodule_odb(path))
+		die("Error submodule '%s' not populated.", path);
+
+`add_submodule_odb()` will return an non-zero value on success. If you
+do not do this you will get an error for each ref that it does not point
+to a valid object.
+
+Note: As a side-effect of this you can not safely assume that all
+objects you lookup are available in superproject. All submodule objects
+will be available the same way as the superprojects objects.
+
+Example:
+--------
+
+----
+static int handle_remote_ref(const char *refname,
+		const unsigned char *sha1, int flags, void *cb_data)
+{
+	struct strbuf *output = cb_data;
+	strbuf_addf(output, "%s\n", refname);
+	return 0;
+}
+
+...
+
+	struct strbuf output = STRBUF_INIT;
+	for_each_remote_ref(handle_remote_ref, &output);
+	printf("%s", output.buf);
+----
diff --git a/Documentation/technical/api-revision-walking.txt b/Documentation/technical/api-revision-walking.txt
index 996da05..b7d0d9a 100644
--- a/Documentation/technical/api-revision-walking.txt
+++ b/Documentation/technical/api-revision-walking.txt
@@ -56,6 +56,11 @@
 	returning a `struct commit *` each time you call it. The end of the
 	revision list is indicated by returning a NULL pointer.
 
+`reset_revision_walk`::
+
+	Reset the flags used by the revision walking api. You can use
+	this to do multiple sequencial revision walks.
+
 Data structures
 ---------------
 
diff --git a/Documentation/technical/api-run-command.txt b/Documentation/technical/api-run-command.txt
index 2efe7a4..f18b4f4 100644
--- a/Documentation/technical/api-run-command.txt
+++ b/Documentation/technical/api-run-command.txt
@@ -35,17 +35,37 @@
 	Convenience functions that encapsulate a sequence of
 	start_command() followed by finish_command(). The argument argv
 	specifies the program and its arguments. The argument opt is zero
-	or more of the flags `RUN_COMMAND_NO_STDIN`, `RUN_GIT_CMD`, or
-	`RUN_COMMAND_STDOUT_TO_STDERR` that correspond to the members
-	.no_stdin, .git_cmd, .stdout_to_stderr of `struct child_process`.
+	or more of the flags `RUN_COMMAND_NO_STDIN`, `RUN_GIT_CMD`,
+	`RUN_COMMAND_STDOUT_TO_STDERR`, or `RUN_SILENT_EXEC_FAILURE`
+	that correspond to the members .no_stdin, .git_cmd,
+	.stdout_to_stderr, .silent_exec_failure of `struct child_process`.
 	The argument dir corresponds the member .dir. The argument env
 	corresponds to the member .env.
 
+The functions above do the following:
+
+. If a system call failed, errno is set and -1 is returned. A diagnostic
+  is printed.
+
+. If the program was not found, then -1 is returned and errno is set to
+  ENOENT; a diagnostic is printed only if .silent_exec_failure is 0.
+
+. Otherwise, the program is run. If it terminates regularly, its exit
+  code is returned. No diagnostic is printed, even if the exit code is
+  non-zero.
+
+. If the program terminated due to a signal, then the return value is the
+  signal number - 128, ie. it is negative and so indicates an unusual
+  condition; a diagnostic is printed. This return value can be passed to
+  exit(2), which will report the same code to the parent process that a
+  POSIX shell's $? would report for a program that died from the signal.
+
+
 `start_async`::
 
 	Run a function asynchronously. Takes a pointer to a `struct
-	async` that specifies the details and returns a pipe FD
-	from which the caller reads. See below for details.
+	async` that specifies the details and returns a set of pipe FDs
+	for communication with the function. See below for details.
 
 `finish_async`::
 
@@ -115,7 +135,7 @@
 
 	.in: The FD must be readable; it becomes child's stdin.
 	.out: The FD must be writable; it becomes child's stdout.
-	.err > 0 is not supported.
+	.err: The FD must be writable; it becomes child's stderr.
 
   The specified FD is closed by start_command(), even if it fails to
   run the sub-process!
@@ -143,6 +163,11 @@
 To specify a new initial working directory for the sub-process,
 specify it in the .dir member.
 
+If the program cannot be found, the functions return -1 and set
+errno to ENOENT. Normally, an error message is printed, but if
+.silent_exec_failure is set to 1, no message is printed for this
+special error condition.
+
 
 * `struct async`
 
@@ -155,17 +180,47 @@
    struct async variable;
 2. initializes .proc and .data;
 3. calls start_async();
-4. processes the data by reading from the fd in .out;
-5. closes .out;
+4. processes communicates with proc through .in and .out;
+5. closes .in and .out;
 6. calls finish_async().
 
+The members .in, .out are used to provide a set of fd's for
+communication between the caller and the callee as follows:
+
+. Specify 0 to have no file descriptor passed.  The callee will
+  receive -1 in the corresponding argument.
+
+. Specify < 0 to have a pipe allocated; start_async() replaces
+  with the pipe FD in the following way:
+
+	.in: Returns the writable pipe end into which the caller
+	writes; the readable end of the pipe becomes the function's
+	in argument.
+
+	.out: Returns the readable pipe end from which the caller
+	reads; the writable end of the pipe becomes the function's
+	out argument.
+
+  The caller of start_async() must close the returned FDs after it
+  has completed reading from/writing from them.
+
+. Specify a file descriptor > 0 to be used by the function:
+
+	.in: The FD must be readable; it becomes the function's in.
+	.out: The FD must be writable; it becomes the function's out.
+
+  The specified FD is closed by start_async(), even if it fails to
+  run the function.
+
 The function pointer in .proc has the following signature:
 
-	int proc(int fd, void *data);
+	int proc(int in, int out, void *data);
 
-. fd specifies a writable file descriptor to which the function must
-  write the data that it produces. The function *must* close this
-  descriptor before it returns.
+. in, out specifies a set of file descriptors to which the function
+  must read/write the data that it needs/produces.  The function
+  *must* close these descriptors before it returns.  A descriptor
+  may be -1 if the caller did not configure a descriptor for that
+  direction.
 
 . data is the value that the caller has specified in the .data member
   of struct async.
@@ -176,12 +231,13 @@
 
 
 There are serious restrictions on what the asynchronous function can do
-because this facility is implemented by a pipe to a forked process on
-UNIX, but by a thread in the same address space on Windows:
+because this facility is implemented by a thread in the same address
+space on most platforms (when pthreads is available), but by a pipe to
+a forked process otherwise:
 
 . It cannot change the program's state (global variables, environment,
-  etc.) in a way that the caller notices; in other words, .out is the
-  only communication channel to the caller.
+  etc.) in a way that the caller notices; in other words, .in and .out
+  are the only communication channels to the caller.
 
 . It must not change the program's state that the caller of the
   facility also uses.
diff --git a/Documentation/technical/api-sha1-array.txt b/Documentation/technical/api-sha1-array.txt
new file mode 100644
index 0000000..4a4bae8
--- /dev/null
+++ b/Documentation/technical/api-sha1-array.txt
@@ -0,0 +1,79 @@
+sha1-array API
+==============
+
+The sha1-array API provides storage and manipulation of sets of SHA1
+identifiers. The emphasis is on storage and processing efficiency,
+making them suitable for large lists. Note that the ordering of items is
+not preserved over some operations.
+
+Data Structures
+---------------
+
+`struct sha1_array`::
+
+	A single array of SHA1 hashes. This should be initialized by
+	assignment from `SHA1_ARRAY_INIT`.  The `sha1` member contains
+	the actual data. The `nr` member contains the number of items in
+	the set.  The `alloc` and `sorted` members are used internally,
+	and should not be needed by API callers.
+
+Functions
+---------
+
+`sha1_array_append`::
+	Add an item to the set. The sha1 will be placed at the end of
+	the array (but note that some operations below may lose this
+	ordering).
+
+`sha1_array_sort`::
+	Sort the elements in the array.
+
+`sha1_array_lookup`::
+	Perform a binary search of the array for a specific sha1.
+	If found, returns the offset (in number of elements) of the
+	sha1. If not found, returns a negative integer. If the array is
+	not sorted, this function has the side effect of sorting it.
+
+`sha1_array_clear`::
+	Free all memory associated with the array and return it to the
+	initial, empty state.
+
+`sha1_array_for_each_unique`::
+	Efficiently iterate over each unique element of the list,
+	executing the callback function for each one. If the array is
+	not sorted, this function has the side effect of sorting it.
+
+Examples
+--------
+
+-----------------------------------------
+void print_callback(const unsigned char sha1[20],
+		    void *data)
+{
+	printf("%s\n", sha1_to_hex(sha1));
+}
+
+void some_func(void)
+{
+	struct sha1_array hashes = SHA1_ARRAY_INIT;
+	unsigned char sha1[20];
+
+	/* Read objects into our set */
+	while (read_object_from_stdin(sha1))
+		sha1_array_append(&hashes, sha1);
+
+	/* Check if some objects are in our set */
+	while (read_object_from_stdin(sha1)) {
+		if (sha1_array_lookup(&hashes, sha1) >= 0)
+			printf("it's in there!\n");
+
+	/*
+	 * Print the unique set of objects. We could also have
+	 * avoided adding duplicate objects in the first place,
+	 * but we would end up re-sorting the array repeatedly.
+	 * Instead, this will sort once and then skip duplicates
+	 * in linear time.
+	 */
+	sha1_array_for_each_unique(&hashes, print_callback, NULL);
+}
+-----------------------------------------
diff --git a/Documentation/technical/api-sigchain.txt b/Documentation/technical/api-sigchain.txt
new file mode 100644
index 0000000..9e1189e
--- /dev/null
+++ b/Documentation/technical/api-sigchain.txt
@@ -0,0 +1,41 @@
+sigchain API
+============
+
+Code often wants to set a signal handler to clean up temporary files or
+other work-in-progress when we die unexpectedly. For multiple pieces of
+code to do this without conflicting, each piece of code must remember
+the old value of the handler and restore it either when:
+
+  1. The work-in-progress is finished, and the handler is no longer
+     necessary. The handler should revert to the original behavior
+     (either another handler, SIG_DFL, or SIG_IGN).
+
+  2. The signal is received. We should then do our cleanup, then chain
+     to the next handler (or die if it is SIG_DFL).
+
+Sigchain is a tiny library for keeping a stack of handlers. Your handler
+and installation code should look something like:
+
+------------------------------------------
+  void clean_foo_on_signal(int sig)
+  {
+	  clean_foo();
+	  sigchain_pop(sig);
+	  raise(sig);
+  }
+
+  void other_func()
+  {
+	  sigchain_push_common(clean_foo_on_signal);
+	  mess_up_foo();
+	  clean_foo();
+  }
+------------------------------------------
+
+Handlers are given the typedef of sigchain_fun. This is the same type
+that is given to signal() or sigaction(). It is perfectly reasonable to
+push SIG_DFL or SIG_IGN onto the stack.
+
+You can sigchain_push and sigchain_pop individual signals. For
+convenience, sigchain_push_common will push the handler onto the stack
+for many common signals.
diff --git a/Documentation/technical/api-strbuf.txt b/Documentation/technical/api-strbuf.txt
index 7438149..95a8bf3 100644
--- a/Documentation/technical/api-strbuf.txt
+++ b/Documentation/technical/api-strbuf.txt
@@ -12,7 +12,7 @@
 
 strbufs has some invariants that are very important to keep in mind:
 
-. The `buf` member is never NULL, so you it can be used in any usual C
+. The `buf` member is never NULL, so it can be used in any usual C
 string operations safely. strbuf's _have_ to be initialized either by
 `strbuf_init()` or by `= STRBUF_INIT` before the invariants, though.
 +
@@ -55,7 +55,7 @@
 
 * `struct strbuf`
 
-This is string buffer structure. The `len` member can be used to
+This is the string buffer structure. The `len` member can be used to
 determine the current length of the string, and `buf` member provides access to
 the string itself.
 
@@ -199,6 +199,10 @@
 the length of the placeholder recognized and `strbuf_expand()` skips
 over it.
 +
+The format `%%` is automatically expanded to a single `%` as a quoting
+mechanism; callers do not need to handle the `%` placeholder themselves,
+and the callback function will not be invoked for this placeholder.
++
 All other characters (non-percent and not skipped ones) are copied
 verbatim to the strbuf.  If the callback returned zero, meaning that the
 placeholder is unknown, then the percent sign is copied, too.
@@ -214,6 +218,13 @@
 	placeholder and replacement string.  The array needs to be
 	terminated by an entry with placeholder set to NULL.
 
+`strbuf_addbuf_percentquote`::
+
+	Append the contents of one strbuf to another, quoting any
+	percent signs ("%") into double-percents ("%%") in the
+	destination. This is useful for literal data to be fed to either
+	strbuf_expand or to the *printf family of functions.
+
 `strbuf_addf`::
 
 	Add a formatted string to the buffer.
@@ -244,8 +255,24 @@
 
 `strbuf_getline`::
 
-	Read a line from a FILE* pointer. The second argument specifies the line
+	Read a line from a FILE *, overwriting the existing contents
+	of the strbuf. The second argument specifies the line
 	terminator character, typically `'\n'`.
+	Reading stops after the terminator or at EOF.  The terminator
+	is removed from the buffer before returning.  Returns 0 unless
+	there was nothing left before EOF, in which case it returns `EOF`.
+
+`strbuf_getwholeline`::
+
+	Like `strbuf_getline`, but keeps the trailing terminator (if
+	any) in the buffer.
+
+`strbuf_getwholeline_fd`::
+
+	Like `strbuf_getwholeline`, but operates on a file descriptor.
+	It reads one character at a time, so it is very slow.  Do not
+	use it unless you need the correct position in the file
+	descriptor.
 
 `stripspace`::
 
@@ -253,3 +280,9 @@
 	comments are considered contents to be removed or not.
 
 `launch_editor`::
+
+	Launch the user preferred editor to edit a file and fill the buffer
+	with the file's contents upon the user completing their editing. The
+	third argument can be used to set the environment which the editor is
+	run in. If the buffer is NULL the editor is launched as usual but the
+	file's contents are not read into the buffer upon completion.
diff --git a/Documentation/technical/api-string-list.txt b/Documentation/technical/api-string-list.txt
index 293bb15..5a0c14f 100644
--- a/Documentation/technical/api-string-list.txt
+++ b/Documentation/technical/api-string-list.txt
@@ -29,6 +29,9 @@
 
 . Can sort an unsorted list using `sort_string_list`.
 
+. Can remove individual items of an unsorted list using
+  `unsorted_string_list_delete_item`.
+
 . Finally it should free the list using `string_list_clear`.
 
 Example:
@@ -38,8 +41,8 @@
 int i;
 
 memset(&list, 0, sizeof(struct string_list));
-string_list_append("foo", &list);
-string_list_append("bar", &list);
+string_list_append(&list, "foo");
+string_list_append(&list, "bar");
 for (i = 0; i < list.nr; i++)
 	printf("%s\n", list.items[i].string)
 ----
@@ -80,7 +83,9 @@
 
 	Insert a new element to the string_list. The returned pointer can be
 	handy if you want to write something to the `util` pointer of the
-	string_list_item containing the just added string.
+	string_list_item containing the just added string. If the given
+	string already exists the insertion will be skipped and the
+	pointer to the existing item returned.
 +
 Since this function uses xrealloc() (which die()s if it fails) if the
 list needs to grow, it is safe not to check the pointer. I.e. you may
@@ -104,10 +109,21 @@
 `unsorted_string_list_has_string`::
 
 	It's like `string_list_has_string()` but for unsorted lists.
+
+`unsorted_string_list_lookup`::
+
+	It's like `string_list_lookup()` but for unsorted lists.
 +
-This function needs to look through all items, as opposed to its
+The above two functions need to look through all items, as opposed to their
 counterpart for sorted lists, which performs a binary search.
 
+`unsorted_string_list_delete_item`::
+
+	Remove an item from a string_list. The `string` pointer of the items
+	will be freed in case the `strdup_strings` member of the string_list
+	is set. The third parameter controls if the `util` pointer of the
+	items should be freed or not.
+
 Data structures
 ---------------
 
diff --git a/Documentation/technical/api-tree-walking.txt b/Documentation/technical/api-tree-walking.txt
index 55b7286..14af37c 100644
--- a/Documentation/technical/api-tree-walking.txt
+++ b/Documentation/technical/api-tree-walking.txt
@@ -42,6 +42,8 @@
 
 * `data` can be anything the `fn` callback would want to use.
 
+* `show_all_errors` tells whether to stop at the first error or not.
+
 Initializing
 ------------
 
diff --git a/Documentation/technical/index-format.txt b/Documentation/technical/index-format.txt
new file mode 100644
index 0000000..9d25b30
--- /dev/null
+++ b/Documentation/technical/index-format.txt
@@ -0,0 +1,199 @@
+GIT index format
+================
+
+= The git index file has the following format
+
+  All binary numbers are in network byte order. Version 2 is described
+  here unless stated otherwise.
+
+   - A 12-byte header consisting of
+
+     4-byte signature:
+       The signature is { 'D', 'I', 'R', 'C' } (stands for "dircache")
+
+     4-byte version number:
+       The current supported versions are 2 and 3.
+
+     32-bit number of index entries.
+
+   - A number of sorted index entries (see below).
+
+   - Extensions
+
+     Extensions are identified by signature. Optional extensions can
+     be ignored if GIT does not understand them.
+
+     GIT currently supports cached tree and resolve undo extensions.
+
+     4-byte extension signature. If the first byte is 'A'..'Z' the
+     extension is optional and can be ignored.
+
+     32-bit size of the extension
+
+     Extension data
+
+   - 160-bit SHA-1 over the content of the index file before this
+     checksum.
+
+== Index entry
+
+  Index entries are sorted in ascending order on the name field,
+  interpreted as a string of unsigned bytes (i.e. memcmp() order, no
+  localization, no special casing of directory separator '/'). Entries
+  with the same name are sorted by their stage field.
+
+  32-bit ctime seconds, the last time a file's metadata changed
+    this is stat(2) data
+
+  32-bit ctime nanosecond fractions
+    this is stat(2) data
+
+  32-bit mtime seconds, the last time a file's data changed
+    this is stat(2) data
+
+  32-bit mtime nanosecond fractions
+    this is stat(2) data
+
+  32-bit dev
+    this is stat(2) data
+
+  32-bit ino
+    this is stat(2) data
+
+  32-bit mode, split into (high to low bits)
+
+    4-bit object type
+      valid values in binary are 1000 (regular file), 1010 (symbolic link)
+      and 1110 (gitlink)
+
+    3-bit unused
+
+    9-bit unix permission. Only 0755 and 0644 are valid for regular files.
+    Symbolic links and gitlinks have value 0 in this field.
+
+  32-bit uid
+    this is stat(2) data
+
+  32-bit gid
+    this is stat(2) data
+
+  32-bit file size
+    This is the on-disk size from stat(2), truncated to 32-bit.
+
+  160-bit SHA-1 for the represented object
+
+  A 16-bit 'flags' field split into (high to low bits)
+
+    1-bit assume-valid flag
+
+    1-bit extended flag (must be zero in version 2)
+
+    2-bit stage (during merge)
+
+    12-bit name length if the length is less than 0xFFF; otherwise 0xFFF
+    is stored in this field.
+
+  (Version 3) A 16-bit field, only applicable if the "extended flag"
+  above is 1, split into (high to low bits).
+
+    1-bit reserved for future
+
+    1-bit skip-worktree flag (used by sparse checkout)
+
+    1-bit intent-to-add flag (used by "git add -N")
+
+    13-bit unused, must be zero
+
+  Entry path name (variable length) relative to top level directory
+    (without leading slash). '/' is used as path separator. The special
+    path components ".", ".." and ".git" (without quotes) are disallowed.
+    Trailing slash is also disallowed.
+
+    The exact encoding is undefined, but the '.' and '/' characters
+    are encoded in 7-bit ASCII and the encoding cannot contain a NUL
+    byte (iow, this is a UNIX pathname).
+
+  (Version 4) In version 4, the entry path name is prefix-compressed
+    relative to the path name for the previous entry (the very first
+    entry is encoded as if the path name for the previous entry is an
+    empty string).  At the beginning of an entry, an integer N in the
+    variable width encoding (the same encoding as the offset is encoded
+    for OFS_DELTA pack entries; see pack-format.txt) is stored, followed
+    by a NUL-terminated string S.  Removing N bytes from the end of the
+    path name for the previous entry, and replacing it with the string S
+    yields the path name for this entry.
+
+  1-8 nul bytes as necessary to pad the entry to a multiple of eight bytes
+  while keeping the name NUL-terminated.
+
+  (Version 4) In version 4, the padding after the pathname does not
+  exist.
+
+== Extensions
+
+=== Cached tree
+
+  Cached tree extension contains pre-computed hashes for trees that can
+  be derived from the index. It helps speed up tree object generation
+  from index for a new commit.
+
+  When a path is updated in index, the path must be invalidated and
+  removed from tree cache.
+
+  The signature for this extension is { 'T', 'R', 'E', 'E' }.
+
+  A series of entries fill the entire extension; each of which
+  consists of:
+
+  - NUL-terminated path component (relative to its parent directory);
+
+  - ASCII decimal number of entries in the index that is covered by the
+    tree this entry represents (entry_count);
+
+  - A space (ASCII 32);
+
+  - ASCII decimal number that represents the number of subtrees this
+    tree has;
+
+  - A newline (ASCII 10); and
+
+  - 160-bit object name for the object that would result from writing
+    this span of index as a tree.
+
+  An entry can be in an invalidated state and is represented by having
+  -1 in the entry_count field. In this case, there is no object name
+  and the next entry starts immediately after the newline.
+
+  The entries are written out in the top-down, depth-first order.  The
+  first entry represents the root level of the repository, followed by the
+  first subtree---let's call this A---of the root level (with its name
+  relative to the root level), followed by the first subtree of A (with
+  its name relative to A), ...
+
+=== Resolve undo
+
+  A conflict is represented in the index as a set of higher stage entries.
+  When a conflict is resolved (e.g. with "git add path"), these higher
+  stage entries will be removed and a stage-0 entry with proper resoluton
+  is added.
+
+  When these higher stage entries are removed, they are saved in the
+  resolve undo extension, so that conflicts can be recreated (e.g. with
+  "git checkout -m"), in case users want to redo a conflict resolution
+  from scratch.
+
+  The signature for this extension is { 'R', 'E', 'U', 'C' }.
+
+  A series of entries fill the entire extension; each of which
+  consists of:
+
+  - NUL-terminated pathname the entry describes (relative to the root of
+    the repository, i.e. full pathname);
+
+  - Three NUL-terminated ASCII octal numbers, entry mode of entries in
+    stage 1 to 3 (a missing stage is represented by "0" in this field);
+    and
+
+  - At most three 160-bit object names of the entry in stages from 1 to 3
+    (nothing is written for a missing stage).
+
diff --git a/Documentation/technical/pack-protocol.txt b/Documentation/technical/pack-protocol.txt
index 9cd48b4..49cdc57 100644
--- a/Documentation/technical/pack-protocol.txt
+++ b/Documentation/technical/pack-protocol.txt
@@ -1,41 +1,546 @@
-Pack transfer protocols
-=======================
+Packfile transfer protocols
+===========================
 
-There are two Pack push-pull protocols.
+Git supports transferring data in packfiles over the ssh://, git:// and
+file:// transports.  There exist two sets of protocols, one for pushing
+data from a client to a server and another for fetching data from a
+server to a client.  All three transports (ssh, git, file) use the same
+protocol to transfer data.
 
-upload-pack (S) | fetch/clone-pack (C) protocol:
+The processes invoked in the canonical Git implementation are 'upload-pack'
+on the server side and 'fetch-pack' on the client side for fetching data;
+then 'receive-pack' on the server and 'send-pack' on the client for pushing
+data.  The protocol functions to have a server tell a client what is
+currently on the server, then for the two to negotiate the smallest amount
+of data to send in order to fully update one or the other.
 
-	# Tell the puller what commits we have and what their names are
-	S: SHA1 name
-	S: ...
-	S: SHA1 name
-	S: # flush -- it's your turn
-	# Tell the pusher what commits we want, and what we have
-	C: want name
-	C: ..
-	C: want name
-	C: have SHA1
-	C: have SHA1
-	C: ...
-	C: # flush -- occasionally ask "had enough?"
-	S: NAK
-	C: have SHA1
-	C: ...
-	C: have SHA1
-	S: ACK
-	C: done
-	S: XXXXXXX -- packfile contents.
+Transports
+----------
+There are three transports over which the packfile protocol is
+initiated.  The Git transport is a simple, unauthenticated server that
+takes the command (almost always 'upload-pack', though Git
+servers can be configured to be globally writable, in which 'receive-
+pack' initiation is also allowed) with which the client wishes to
+communicate and executes it and connects it to the requesting
+process.
 
-send-pack | receive-pack protocol.
+In the SSH transport, the client just runs the 'upload-pack'
+or 'receive-pack' process on the server over the SSH protocol and then
+communicates with that invoked process over the SSH connection.
 
-	# Tell the pusher what commits we have and what their names are
-	C: SHA1 name
-	C: ...
-	C: SHA1 name
-	C: # flush -- it's your turn
-	# Tell the puller what the pusher has
-	S: old-SHA1 new-SHA1 name
-	S: old-SHA1 new-SHA1 name
-	S: ...
-	S: # flush -- done with the list
-	S: XXXXXXX --- packfile contents.
+The file:// transport runs the 'upload-pack' or 'receive-pack'
+process locally and communicates with it over a pipe.
+
+Git Transport
+-------------
+
+The Git transport starts off by sending the command and repository
+on the wire using the pkt-line format, followed by a NUL byte and a
+hostname parameter, terminated by a NUL byte.
+
+   0032git-upload-pack /project.git\0host=myserver.com\0
+
+--
+   git-proto-request = request-command SP pathname NUL [ host-parameter NUL ]
+   request-command   = "git-upload-pack" / "git-receive-pack" /
+		       "git-upload-archive"   ; case sensitive
+   pathname          = *( %x01-ff ) ; exclude NUL
+   host-parameter    = "host=" hostname [ ":" port ]
+--
+
+Only host-parameter is allowed in the git-proto-request. Clients
+MUST NOT attempt to send additional parameters. It is used for the
+git-daemon name based virtual hosting.  See --interpolated-path
+option to git daemon, with the %H/%CH format characters.
+
+Basically what the Git client is doing to connect to an 'upload-pack'
+process on the server side over the Git protocol is this:
+
+   $ echo -e -n \
+     "0039git-upload-pack /schacon/gitbook.git\0host=example.com\0" |
+     nc -v example.com 9418
+
+If the server refuses the request for some reasons, it could abort
+gracefully with an error message.
+
+----
+  error-line     =  PKT-LINE("ERR" SP explanation-text)
+----
+
+
+SSH Transport
+-------------
+
+Initiating the upload-pack or receive-pack processes over SSH is
+executing the binary on the server via SSH remote execution.
+It is basically equivalent to running this:
+
+   $ ssh git.example.com "git-upload-pack '/project.git'"
+
+For a server to support Git pushing and pulling for a given user over
+SSH, that user needs to be able to execute one or both of those
+commands via the SSH shell that they are provided on login.  On some
+systems, that shell access is limited to only being able to run those
+two commands, or even just one of them.
+
+In an ssh:// format URI, it's absolute in the URI, so the '/' after
+the host name (or port number) is sent as an argument, which is then
+read by the remote git-upload-pack exactly as is, so it's effectively
+an absolute path in the remote filesystem.
+
+       git clone ssh://user@example.com/project.git
+		    |
+		    v
+    ssh user@example.com "git-upload-pack '/project.git'"
+
+In a "user@host:path" format URI, its relative to the user's home
+directory, because the Git client will run:
+
+     git clone user@example.com:project.git
+		    |
+		    v
+  ssh user@example.com "git-upload-pack 'project.git'"
+
+The exception is if a '~' is used, in which case
+we execute it without the leading '/'.
+
+      ssh://user@example.com/~alice/project.git,
+		     |
+		     v
+   ssh user@example.com "git-upload-pack '~alice/project.git'"
+
+A few things to remember here:
+
+- The "command name" is spelled with dash (e.g. git-upload-pack), but
+  this can be overridden by the client;
+
+- The repository path is always quoted with single quotes.
+
+Fetching Data From a Server
+===========================
+
+When one Git repository wants to get data that a second repository
+has, the first can 'fetch' from the second.  This operation determines
+what data the server has that the client does not then streams that
+data down to the client in packfile format.
+
+
+Reference Discovery
+-------------------
+
+When the client initially connects the server will immediately respond
+with a listing of each reference it has (all branches and tags) along
+with the object name that each reference currently points to.
+
+   $ echo -e -n "0039git-upload-pack /schacon/gitbook.git\0host=example.com\0" |
+      nc -v example.com 9418
+   00887217a7c7e582c46cec22a130adf4b9d7d950fba0 HEAD\0multi_ack thin-pack side-band side-band-64k ofs-delta shallow no-progress include-tag
+   00441d3fcd5ced445d1abc402225c0b8a1299641f497 refs/heads/integration
+   003f7217a7c7e582c46cec22a130adf4b9d7d950fba0 refs/heads/master
+   003cb88d2441cac0977faf98efc80305012112238d9d refs/tags/v0.9
+   003c525128480b96c89e6418b1e40909bf6c5b2d580f refs/tags/v1.0
+   003fe92df48743b7bc7d26bcaabfddde0a1e20cae47c refs/tags/v1.0^{}
+   0000
+
+Server SHOULD terminate each non-flush line using LF ("\n") terminator;
+client MUST NOT complain if there is no terminator.
+
+The returned response is a pkt-line stream describing each ref and
+its current value.  The stream MUST be sorted by name according to
+the C locale ordering.
+
+If HEAD is a valid ref, HEAD MUST appear as the first advertised
+ref.  If HEAD is not a valid ref, HEAD MUST NOT appear in the
+advertisement list at all, but other refs may still appear.
+
+The stream MUST include capability declarations behind a NUL on the
+first ref. The peeled value of a ref (that is "ref^{}") MUST be
+immediately after the ref itself, if presented. A conforming server
+MUST peel the ref if it's an annotated tag.
+
+----
+  advertised-refs  =  (no-refs / list-of-refs)
+		      flush-pkt
+
+  no-refs          =  PKT-LINE(zero-id SP "capabilities^{}"
+		      NUL capability-list LF)
+
+  list-of-refs     =  first-ref *other-ref
+  first-ref        =  PKT-LINE(obj-id SP refname
+		      NUL capability-list LF)
+
+  other-ref        =  PKT-LINE(other-tip / other-peeled)
+  other-tip        =  obj-id SP refname LF
+  other-peeled     =  obj-id SP refname "^{}" LF
+
+  capability-list  =  capability *(SP capability)
+  capability       =  1*(LC_ALPHA / DIGIT / "-" / "_")
+  LC_ALPHA         =  %x61-7A
+----
+
+Server and client MUST use lowercase for obj-id, both MUST treat obj-id
+as case-insensitive.
+
+See protocol-capabilities.txt for a list of allowed server capabilities
+and descriptions.
+
+Packfile Negotiation
+--------------------
+After reference and capabilities discovery, the client can decide to
+terminate the connection by sending a flush-pkt, telling the server it can
+now gracefully terminate, and disconnect, when it does not need any pack
+data. This can happen with the ls-remote command, and also can happen when
+the client already is up-to-date.
+
+Otherwise, it enters the negotiation phase, where the client and
+server determine what the minimal packfile necessary for transport is,
+by telling the server what objects it wants, its shallow objects
+(if any), and the maximum commit depth it wants (if any).  The client
+will also send a list of the capabilities it wants to be in effect,
+out of what the server said it could do with the first 'want' line.
+
+----
+  upload-request    =  want-list
+		       *shallow-line
+		       *1depth-request
+		       flush-pkt
+
+  want-list         =  first-want
+		       *additional-want
+
+  shallow-line      =  PKT_LINE("shallow" SP obj-id)
+
+  depth-request     =  PKT_LINE("deepen" SP depth)
+
+  first-want        =  PKT-LINE("want" SP obj-id SP capability-list LF)
+  additional-want   =  PKT-LINE("want" SP obj-id LF)
+
+  depth             =  1*DIGIT
+----
+
+Clients MUST send all the obj-ids it wants from the reference
+discovery phase as 'want' lines. Clients MUST send at least one
+'want' command in the request body. Clients MUST NOT mention an
+obj-id in a 'want' command which did not appear in the response
+obtained through ref discovery.
+
+The client MUST write all obj-ids which it only has shallow copies
+of (meaning that it does not have the parents of a commit) as
+'shallow' lines so that the server is aware of the limitations of
+the client's history. Clients MUST NOT mention an obj-id which
+it does not know exists on the server.
+
+The client now sends the maximum commit history depth it wants for
+this transaction, which is the number of commits it wants from the
+tip of the history, if any, as a 'deepen' line.  A depth of 0 is the
+same as not making a depth request. The client does not want to receive
+any commits beyond this depth, nor objects needed only to complete
+those commits. Commits whose parents are not received as a result are
+defined as shallow and marked as such in the server. This information
+is sent back to the client in the next step.
+
+Once all the 'want's and 'shallow's (and optional 'deepen') are
+transferred, clients MUST send a flush-pkt, to tell the server side
+that it is done sending the list.
+
+Otherwise, if the client sent a positive depth request, the server
+will determine which commits will and will not be shallow and
+send this information to the client. If the client did not request
+a positive depth, this step is skipped.
+
+----
+  shallow-update   =  *shallow-line
+		      *unshallow-line
+		      flush-pkt
+
+  shallow-line     =  PKT-LINE("shallow" SP obj-id)
+
+  unshallow-line   =  PKT-LINE("unshallow" SP obj-id)
+----
+
+If the client has requested a positive depth, the server will compute
+the set of commits which are no deeper than the desired depth, starting
+at the client's wants. The server writes 'shallow' lines for each
+commit whose parents will not be sent as a result. The server writes
+an 'unshallow' line for each commit which the client has indicated is
+shallow, but is no longer shallow at the currently requested depth
+(that is, its parents will now be sent). The server MUST NOT mark
+as unshallow anything which the client has not indicated was shallow.
+
+Now the client will send a list of the obj-ids it has using 'have'
+lines, so the server can make a packfile that only contains the objects
+that the client needs. In multi_ack mode, the canonical implementation
+will send up to 32 of these at a time, then will send a flush-pkt. The
+canonical implementation will skip ahead and send the next 32 immediately,
+so that there is always a block of 32 "in-flight on the wire" at a time.
+
+----
+  upload-haves      =  have-list
+		       compute-end
+
+  have-list         =  *have-line
+  have-line         =  PKT-LINE("have" SP obj-id LF)
+  compute-end       =  flush-pkt / PKT-LINE("done")
+----
+
+If the server reads 'have' lines, it then will respond by ACKing any
+of the obj-ids the client said it had that the server also has. The
+server will ACK obj-ids differently depending on which ack mode is
+chosen by the client.
+
+In multi_ack mode:
+
+  * the server will respond with 'ACK obj-id continue' for any common
+    commits.
+
+  * once the server has found an acceptable common base commit and is
+    ready to make a packfile, it will blindly ACK all 'have' obj-ids
+    back to the client.
+
+  * the server will then send a 'NACK' and then wait for another response
+    from the client - either a 'done' or another list of 'have' lines.
+
+In multi_ack_detailed mode:
+
+  * the server will differentiate the ACKs where it is signaling
+    that it is ready to send data with 'ACK obj-id ready' lines, and
+    signals the identified common commits with 'ACK obj-id common' lines.
+
+Without either multi_ack or multi_ack_detailed:
+
+ * upload-pack sends "ACK obj-id" on the first common object it finds.
+   After that it says nothing until the client gives it a "done".
+
+ * upload-pack sends "NAK" on a flush-pkt if no common object
+   has been found yet.  If one has been found, and thus an ACK
+   was already sent, it's silent on the flush-pkt.
+
+After the client has gotten enough ACK responses that it can determine
+that the server has enough information to send an efficient packfile
+(in the canonical implementation, this is determined when it has received
+enough ACKs that it can color everything left in the --date-order queue
+as common with the server, or the --date-order queue is empty), or the
+client determines that it wants to give up (in the canonical implementation,
+this is determined when the client sends 256 'have' lines without getting
+any of them ACKed by the server - meaning there is nothing in common and
+the server should just send all of its objects), then the client will send
+a 'done' command.  The 'done' command signals to the server that the client
+is ready to receive its packfile data.
+
+However, the 256 limit *only* turns on in the canonical client
+implementation if we have received at least one "ACK %s continue"
+during a prior round.  This helps to ensure that at least one common
+ancestor is found before we give up entirely.
+
+Once the 'done' line is read from the client, the server will either
+send a final 'ACK obj-id' or it will send a 'NAK'. The server only sends
+ACK after 'done' if there is at least one common base and multi_ack or
+multi_ack_detailed is enabled. The server always sends NAK after 'done'
+if there is no common base found.
+
+Then the server will start sending its packfile data.
+
+----
+  server-response = *ack_multi ack / nak
+  ack_multi       = PKT-LINE("ACK" SP obj-id ack_status LF)
+  ack_status      = "continue" / "common" / "ready"
+  ack             = PKT-LINE("ACK SP obj-id LF)
+  nak             = PKT-LINE("NAK" LF)
+----
+
+A simple clone may look like this (with no 'have' lines):
+
+----
+   C: 0054want 74730d410fcb6603ace96f1dc55ea6196122532d multi_ack \
+     side-band-64k ofs-delta\n
+   C: 0032want 7d1665144a3a975c05f1f43902ddaf084e784dbe\n
+   C: 0032want 5a3f6be755bbb7deae50065988cbfa1ffa9ab68a\n
+   C: 0032want 7e47fe2bd8d01d481f44d7af0531bd93d3b21c01\n
+   C: 0032want 74730d410fcb6603ace96f1dc55ea6196122532d\n
+   C: 0000
+   C: 0009done\n
+
+   S: 0008NAK\n
+   S: [PACKFILE]
+----
+
+An incremental update (fetch) response might look like this:
+
+----
+   C: 0054want 74730d410fcb6603ace96f1dc55ea6196122532d multi_ack \
+     side-band-64k ofs-delta\n
+   C: 0032want 7d1665144a3a975c05f1f43902ddaf084e784dbe\n
+   C: 0032want 5a3f6be755bbb7deae50065988cbfa1ffa9ab68a\n
+   C: 0000
+   C: 0032have 7e47fe2bd8d01d481f44d7af0531bd93d3b21c01\n
+   C: [30 more have lines]
+   C: 0032have 74730d410fcb6603ace96f1dc55ea6196122532d\n
+   C: 0000
+
+   S: 003aACK 7e47fe2bd8d01d481f44d7af0531bd93d3b21c01 continue\n
+   S: 003aACK 74730d410fcb6603ace96f1dc55ea6196122532d continue\n
+   S: 0008NAK\n
+
+   C: 0009done\n
+
+   S: 0031ACK 74730d410fcb6603ace96f1dc55ea6196122532d\n
+   S: [PACKFILE]
+----
+
+
+Packfile Data
+-------------
+
+Now that the client and server have finished negotiation about what
+the minimal amount of data that needs to be sent to the client is, the server
+will construct and send the required data in packfile format.
+
+See pack-format.txt for what the packfile itself actually looks like.
+
+If 'side-band' or 'side-band-64k' capabilities have been specified by
+the client, the server will send the packfile data multiplexed.
+
+Each packet starting with the packet-line length of the amount of data
+that follows, followed by a single byte specifying the sideband the
+following data is coming in on.
+
+In 'side-band' mode, it will send up to 999 data bytes plus 1 control
+code, for a total of up to 1000 bytes in a pkt-line.  In 'side-band-64k'
+mode it will send up to 65519 data bytes plus 1 control code, for a
+total of up to 65520 bytes in a pkt-line.
+
+The sideband byte will be a '1', '2' or a '3'. Sideband '1' will contain
+packfile data, sideband '2' will be used for progress information that the
+client will generally print to stderr and sideband '3' is used for error
+information.
+
+If no 'side-band' capability was specified, the server will stream the
+entire packfile without multiplexing.
+
+
+Pushing Data To a Server
+========================
+
+Pushing data to a server will invoke the 'receive-pack' process on the
+server, which will allow the client to tell it which references it should
+update and then send all the data the server will need for those new
+references to be complete.  Once all the data is received and validated,
+the server will then update its references to what the client specified.
+
+Authentication
+--------------
+
+The protocol itself contains no authentication mechanisms.  That is to be
+handled by the transport, such as SSH, before the 'receive-pack' process is
+invoked.  If 'receive-pack' is configured over the Git transport, those
+repositories will be writable by anyone who can access that port (9418) as
+that transport is unauthenticated.
+
+Reference Discovery
+-------------------
+
+The reference discovery phase is done nearly the same way as it is in the
+fetching protocol. Each reference obj-id and name on the server is sent
+in packet-line format to the client, followed by a flush-pkt.  The only
+real difference is that the capability listing is different - the only
+possible values are 'report-status', 'delete-refs' and 'ofs-delta'.
+
+Reference Update Request and Packfile Transfer
+----------------------------------------------
+
+Once the client knows what references the server is at, it can send a
+list of reference update requests.  For each reference on the server
+that it wants to update, it sends a line listing the obj-id currently on
+the server, the obj-id the client would like to update it to and the name
+of the reference.
+
+This list is followed by a flush-pkt and then the packfile that should
+contain all the objects that the server will need to complete the new
+references.
+
+----
+  update-request    =  command-list [pack-file]
+
+  command-list      =  PKT-LINE(command NUL capability-list LF)
+		       *PKT-LINE(command LF)
+		       flush-pkt
+
+  command           =  create / delete / update
+  create            =  zero-id SP new-id  SP name
+  delete            =  old-id  SP zero-id SP name
+  update            =  old-id  SP new-id  SP name
+
+  old-id            =  obj-id
+  new-id            =  obj-id
+
+  pack-file         = "PACK" 28*(OCTET)
+----
+
+If the receiving end does not support delete-refs, the sending end MUST
+NOT ask for delete command.
+
+The pack-file MUST NOT be sent if the only command used is 'delete'.
+
+A pack-file MUST be sent if either create or update command is used,
+even if the server already has all the necessary objects.  In this
+case the client MUST send an empty pack-file.   The only time this
+is likely to happen is if the client is creating
+a new branch or a tag that points to an existing obj-id.
+
+The server will receive the packfile, unpack it, then validate each
+reference that is being updated that it hasn't changed while the request
+was being processed (the obj-id is still the same as the old-id), and
+it will run any update hooks to make sure that the update is acceptable.
+If all of that is fine, the server will then update the references.
+
+Report Status
+-------------
+
+After receiving the pack data from the sender, the receiver sends a
+report if 'report-status' capability is in effect.
+It is a short listing of what happened in that update.  It will first
+list the status of the packfile unpacking as either 'unpack ok' or
+'unpack [error]'.  Then it will list the status for each of the references
+that it tried to update.  Each line is either 'ok [refname]' if the
+update was successful, or 'ng [refname] [error]' if the update was not.
+
+----
+  report-status     = unpack-status
+		      1*(command-status)
+		      flush-pkt
+
+  unpack-status     = PKT-LINE("unpack" SP unpack-result LF)
+  unpack-result     = "ok" / error-msg
+
+  command-status    = command-ok / command-fail
+  command-ok        = PKT-LINE("ok" SP refname LF)
+  command-fail      = PKT-LINE("ng" SP refname SP error-msg LF)
+
+  error-msg         = 1*(OCTECT) ; where not "ok"
+----
+
+Updates can be unsuccessful for a number of reasons.  The reference can have
+changed since the reference discovery phase was originally sent, meaning
+someone pushed in the meantime.  The reference being pushed could be a
+non-fast-forward reference and the update hooks or configuration could be
+set to not allow that, etc.  Also, some references can be updated while others
+can be rejected.
+
+An example client/server communication might look like this:
+
+----
+   S: 007c74730d410fcb6603ace96f1dc55ea6196122532d refs/heads/local\0report-status delete-refs ofs-delta\n
+   S: 003e7d1665144a3a975c05f1f43902ddaf084e784dbe refs/heads/debug\n
+   S: 003f74730d410fcb6603ace96f1dc55ea6196122532d refs/heads/master\n
+   S: 003f74730d410fcb6603ace96f1dc55ea6196122532d refs/heads/team\n
+   S: 0000
+
+   C: 003e7d1665144a3a975c05f1f43902ddaf084e784dbe 74730d410fcb6603ace96f1dc55ea6196122532d refs/heads/debug\n
+   C: 003e74730d410fcb6603ace96f1dc55ea6196122532d 5a3f6be755bbb7deae50065988cbfa1ffa9ab68a refs/heads/master\n
+   C: 0000
+   C: [PACKDATA]
+
+   S: 000eunpack ok\n
+   S: 0018ok refs/heads/debug\n
+   S: 002ang refs/heads/master non-fast-forward\n
+----
diff --git a/Documentation/technical/protocol-capabilities.txt b/Documentation/technical/protocol-capabilities.txt
new file mode 100644
index 0000000..b15517f
--- /dev/null
+++ b/Documentation/technical/protocol-capabilities.txt
@@ -0,0 +1,187 @@
+Git Protocol Capabilities
+=========================
+
+Servers SHOULD support all capabilities defined in this document.
+
+On the very first line of the initial server response of either
+receive-pack and upload-pack the first reference is followed by
+a NUL byte and then a list of space delimited server capabilities.
+These allow the server to declare what it can and cannot support
+to the client.
+
+Client will then send a space separated list of capabilities it wants
+to be in effect. The client MUST NOT ask for capabilities the server
+did not say it supports.
+
+Server MUST diagnose and abort if capabilities it does not understand
+was sent.  Server MUST NOT ignore capabilities that client requested
+and server advertised.  As a consequence of these rules, server MUST
+NOT advertise capabilities it does not understand.
+
+The 'report-status' and 'delete-refs' capabilities are sent and
+recognized by the receive-pack (push to server) process.
+
+The 'ofs-delta' capability is sent and recognized by both upload-pack
+and receive-pack protocols.
+
+All other capabilities are only recognized by the upload-pack (fetch
+from server) process.
+
+multi_ack
+---------
+
+The 'multi_ack' capability allows the server to return "ACK obj-id
+continue" as soon as it finds a commit that it can use as a common
+base, between the client's wants and the client's have set.
+
+By sending this early, the server can potentially head off the client
+from walking any further down that particular branch of the client's
+repository history.  The client may still need to walk down other
+branches, sending have lines for those, until the server has a
+complete cut across the DAG, or the client has said "done".
+
+Without multi_ack, a client sends have lines in --date-order until
+the server has found a common base.  That means the client will send
+have lines that are already known by the server to be common, because
+they overlap in time with another branch that the server hasn't found
+a common base on yet.
+
+For example suppose the client has commits in caps that the server
+doesn't and the server has commits in lower case that the client
+doesn't, as in the following diagram:
+
+       +---- u ---------------------- x
+      /              +----- y
+     /              /
+    a -- b -- c -- d -- E -- F
+       \
+	+--- Q -- R -- S
+
+If the client wants x,y and starts out by saying have F,S, the server
+doesn't know what F,S is.  Eventually the client says "have d" and
+the server sends "ACK d continue" to let the client know to stop
+walking down that line (so don't send c-b-a), but it's not done yet,
+it needs a base for x. The client keeps going with S-R-Q, until a
+gets reached, at which point the server has a clear base and it all
+ends.
+
+Without multi_ack the client would have sent that c-b-a chain anyway,
+interleaved with S-R-Q.
+
+thin-pack
+---------
+
+This capability means that the server can send a 'thin' pack, a pack
+which does not contain base objects; if those base objects are available
+on client side. Client requests 'thin-pack' capability when it
+understands how to "thicken" it by adding required delta bases making
+it self-contained.
+
+Client MUST NOT request 'thin-pack' capability if it cannot turn a thin
+pack into a self-contained pack.
+
+
+side-band, side-band-64k
+------------------------
+
+This capability means that server can send, and client understand multiplexed
+progress reports and error info interleaved with the packfile itself.
+
+These two options are mutually exclusive. A modern client always
+favors 'side-band-64k'.
+
+Either mode indicates that the packfile data will be streamed broken
+up into packets of up to either 1000 bytes in the case of 'side_band',
+or 65520 bytes in the case of 'side_band_64k'. Each packet is made up
+of a leading 4-byte pkt-line length of how much data is in the packet,
+followed by a 1-byte stream code, followed by the actual data.
+
+The stream code can be one of:
+
+ 1 - pack data
+ 2 - progress messages
+ 3 - fatal error message just before stream aborts
+
+The "side-band-64k" capability came about as a way for newer clients
+that can handle much larger packets to request packets that are
+actually crammed nearly full, while maintaining backward compatibility
+for the older clients.
+
+Further, with side-band and its up to 1000-byte messages, it's actually
+999 bytes of payload and 1 byte for the stream code. With side-band-64k,
+same deal, you have up to 65519 bytes of data and 1 byte for the stream
+code.
+
+The client MUST send only maximum of one of "side-band" and "side-
+band-64k".  Server MUST diagnose it as an error if client requests
+both.
+
+ofs-delta
+---------
+
+Server can send, and client understand PACKv2 with delta referring to
+its base by position in pack rather than by an obj-id.  That is, they can
+send/read OBJ_OFS_DELTA (aka type 6) in a packfile.
+
+shallow
+-------
+
+This capability adds "deepen", "shallow" and "unshallow" commands to
+the  fetch-pack/upload-pack protocol so clients can request shallow
+clones.
+
+no-progress
+-----------
+
+The client was started with "git clone -q" or something, and doesn't
+want that side band 2.  Basically the client just says "I do not
+wish to receive stream 2 on sideband, so do not send it to me, and if
+you did, I will drop it on the floor anyway".  However, the sideband
+channel 3 is still used for error responses.
+
+include-tag
+-----------
+
+The 'include-tag' capability is about sending annotated tags if we are
+sending objects they point to.  If we pack an object to the client, and
+a tag object points exactly at that object, we pack the tag object too.
+In general this allows a client to get all new annotated tags when it
+fetches a branch, in a single network connection.
+
+Clients MAY always send include-tag, hardcoding it into a request when
+the server advertises this capability. The decision for a client to
+request include-tag only has to do with the client's desires for tag
+data, whether or not a server had advertised objects in the
+refs/tags/* namespace.
+
+Servers MUST pack the tags if their referrant is packed and the client
+has requested include-tags.
+
+Clients MUST be prepared for the case where a server has ignored
+include-tag and has not actually sent tags in the pack.  In such
+cases the client SHOULD issue a subsequent fetch to acquire the tags
+that include-tag would have otherwise given the client.
+
+The server SHOULD send include-tag, if it supports it, regardless
+of whether or not there are tags available.
+
+report-status
+-------------
+
+The upload-pack process can receive a 'report-status' capability,
+which tells it that the client wants a report of what happened after
+a packfile upload and reference update.  If the pushing client requests
+this capability, after unpacking and updating references the server
+will respond with whether the packfile unpacked successfully and if
+each reference was updated successfully.  If any of those were not
+successful, it will send back an error message.  See pack-protocol.txt
+for example messages.
+
+delete-refs
+-----------
+
+If the server sends back the 'delete-refs' capability, it means that
+it is capable of accepting a zero-id value as the target
+value of a reference update.  It is not sent back by the client, it
+simply informs the client that it can be sent zero-id values
+to delete references.
diff --git a/Documentation/technical/protocol-common.txt b/Documentation/technical/protocol-common.txt
new file mode 100644
index 0000000..fb7ff08
--- /dev/null
+++ b/Documentation/technical/protocol-common.txt
@@ -0,0 +1,96 @@
+Documentation Common to Pack and Http Protocols
+===============================================
+
+ABNF Notation
+-------------
+
+ABNF notation as described by RFC 5234 is used within the protocol documents,
+except the following replacement core rules are used:
+----
+  HEXDIG    =  DIGIT / "a" / "b" / "c" / "d" / "e" / "f"
+----
+
+We also define the following common rules:
+----
+  NUL       =  %x00
+  zero-id   =  40*"0"
+  obj-id    =  40*(HEXDIGIT)
+
+  refname  =  "HEAD"
+  refname /=  "refs/" <see discussion below>
+----
+
+A refname is a hierarchical octet string beginning with "refs/" and
+not violating the 'git-check-ref-format' command's validation rules.
+More specifically, they:
+
+. They can include slash `/` for hierarchical (directory)
+  grouping, but no slash-separated component can begin with a
+  dot `.`.
+
+. They must contain at least one `/`. This enforces the presence of a
+  category like `heads/`, `tags/` etc. but the actual names are not
+  restricted.
+
+. They cannot have two consecutive dots `..` anywhere.
+
+. They cannot have ASCII control characters (i.e. bytes whose
+  values are lower than \040, or \177 `DEL`), space, tilde `~`,
+  caret `^`, colon `:`, question-mark `?`, asterisk `*`,
+  or open bracket `[` anywhere.
+
+. They cannot end with a slash `/` nor a dot `.`.
+
+. They cannot end with the sequence `.lock`.
+
+. They cannot contain a sequence `@{`.
+
+. They cannot contain a `\\`.
+
+
+pkt-line Format
+---------------
+
+Much (but not all) of the payload is described around pkt-lines.
+
+A pkt-line is a variable length binary string.  The first four bytes
+of the line, the pkt-len, indicates the total length of the line,
+in hexadecimal.  The pkt-len includes the 4 bytes used to contain
+the length's hexadecimal representation.
+
+A pkt-line MAY contain binary data, so implementors MUST ensure
+pkt-line parsing/formatting routines are 8-bit clean.
+
+A non-binary line SHOULD BE terminated by an LF, which if present
+MUST be included in the total length.
+
+The maximum length of a pkt-line's data component is 65520 bytes.
+Implementations MUST NOT send pkt-line whose length exceeds 65524
+(65520 bytes of payload + 4 bytes of length data).
+
+Implementations SHOULD NOT send an empty pkt-line ("0004").
+
+A pkt-line with a length field of 0 ("0000"), called a flush-pkt,
+is a special case and MUST be handled differently than an empty
+pkt-line ("0004").
+
+----
+  pkt-line     =  data-pkt / flush-pkt
+
+  data-pkt     =  pkt-len pkt-payload
+  pkt-len      =  4*(HEXDIG)
+  pkt-payload  =  (pkt-len - 4)*(OCTET)
+
+  flush-pkt    = "0000"
+----
+
+Examples (as C-style strings):
+
+----
+  pkt-line          actual value
+  ---------------------------------
+  "0006a\n"         "a\n"
+  "0005a"           "a"
+  "000bfoobar\n"    "foobar\n"
+  "0004"            ""
+----
diff --git a/Documentation/technical/racy-git.txt b/Documentation/technical/racy-git.txt
index 48bb97f..53aa0c8 100644
--- a/Documentation/technical/racy-git.txt
+++ b/Documentation/technical/racy-git.txt
@@ -42,10 +42,12 @@
 is not stable on network filesystems.  With `USE_NSEC`
 compile-time option, `st_mtim.tv_nsec` and `st_ctim.tv_nsec`
 members are also compared, but this is not enabled by default
-because the value of this member becomes meaningless once the
-inode is evicted from the inode cache on filesystems that do not
-store it on disk.
-
+because in-core timestamps can have finer granularity than
+on-disk timestamps, resulting in meaningless changes when an
+inode is evicted from the inode cache.  See commit 8ce13b0
+of git://git.kernel.org/pub/scm/linux/kernel/git/tglx/history.git
+([PATCH] Sync in core time granuality with filesystems,
+2005-01-04).
 
 Racy git
 --------
diff --git a/Documentation/urls-remotes.txt b/Documentation/urls-remotes.txt
index 2a0e7b8..00f7e79 100644
--- a/Documentation/urls-remotes.txt
+++ b/Documentation/urls-remotes.txt
@@ -52,8 +52,8 @@
 
 ------------
 
-`Push:` lines are used by 'git-push' and
-`Pull:` lines are used by 'git-pull' and 'git-fetch'.
+`Push:` lines are used by 'git push' and
+`Pull:` lines are used by 'git pull' and 'git fetch'.
 Multiple `Push:` and `Pull:` lines may
 be specified for additional branch mappings.
 
diff --git a/Documentation/urls.txt b/Documentation/urls.txt
index 5355ebc..2890194 100644
--- a/Documentation/urls.txt
+++ b/Documentation/urls.txt
@@ -1,50 +1,57 @@
 GIT URLS[[URLS]]
 ----------------
 
-One of the following notations can be used
-to name the remote repository:
+In general, URLs contain information about the transport protocol, the
+address of the remote server, and the path to the repository.
+Depending on the transport protocol, some of this information may be
+absent.
 
-===============================================================
-- rsync://host.xz/path/to/repo.git/
-- http://host.xz{startsb}:port{endsb}/path/to/repo.git/
-- https://host.xz{startsb}:port{endsb}/path/to/repo.git/
-- git://host.xz{startsb}:port{endsb}/path/to/repo.git/
-- git://host.xz{startsb}:port{endsb}/~user/path/to/repo.git/
+Git natively supports ssh, git, http, https, ftp, ftps, and rsync
+protocols. The following syntaxes may be used with them:
+
 - ssh://{startsb}user@{endsb}host.xz{startsb}:port{endsb}/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
-===============================================================
+- git://host.xz{startsb}:port{endsb}/path/to/repo.git/
+- http{startsb}s{endsb}://host.xz{startsb}:port{endsb}/path/to/repo.git/
+- ftp{startsb}s{endsb}://host.xz{startsb}:port{endsb}/path/to/repo.git/
+- rsync://host.xz/path/to/repo.git/
 
-SSH is the default transport protocol over the network.  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, but
-only the former supports port specification. The following
-three are identical to the last three above, respectively:
+An alternative scp-like syntax may also be used with the ssh protocol:
 
-===============================================================
-- {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
-===============================================================
+- {startsb}user@{endsb}host.xz:path/to/repo.git/
 
-To sync with a local directory, you can use:
+The ssh and git protocols additionally support ~username expansion:
 
-===============================================================
+- ssh://{startsb}user@{endsb}host.xz{startsb}:port{endsb}/~{startsb}user{endsb}/path/to/repo.git/
+- git://host.xz{startsb}:port{endsb}/~{startsb}user{endsb}/path/to/repo.git/
+- {startsb}user@{endsb}host.xz:/~{startsb}user{endsb}/path/to/repo.git/
+
+For local repositories, also supported by git natively, the following
+syntaxes may be used:
+
 - /path/to/repo.git/
 - file:///path/to/repo.git/
-===============================================================
 
 ifndef::git-clone[]
-They are mostly equivalent, except when cloning.  See
-linkgit:git-clone[1] for details.
+These two syntaxes are mostly equivalent, except when cloning, when
+the former implies --local option. See linkgit:git-clone[1] for
+details.
 endif::git-clone[]
 
 ifdef::git-clone[]
-They are equivalent, except the former implies --local option.
+These two syntaxes are mostly equivalent, except the former implies
+--local option.
 endif::git-clone[]
 
+When git doesn't know how to handle a certain transport protocol, it
+attempts to use the 'remote-<transport>' remote helper, if one
+exists. To explicitly request a remote helper, the following syntax
+may be used:
+
+- <transport>::<address>
+
+where <address> may be a path, a server and path, or an arbitrary
+URL-like string recognized by the specific remote helper being
+invoked. See linkgit:git-remote-helpers[1] for details.
 
 If there are a large number of similarly-named remote repositories and
 you want to use a different format for them (such that the URLs you
@@ -67,3 +74,21 @@
 a URL like "work:repo.git" or like "host.xz:/path/to/repo.git" will be
 rewritten in any context that takes a URL to be "git://git.host.xz/repo.git".
 
+If you want to rewrite URLs for push only, you can create a
+configuration section of the form:
+
+------------
+	[url "<actual url base>"]
+		pushInsteadOf = <other url base>
+------------
+
+For example, with this:
+
+------------
+	[url "ssh://example.org/"]
+		pushInsteadOf = git://example.org/
+------------
+
+a URL like "git://example.org/path/to/repo.git" will be rewritten to
+"ssh://example.org/path/to/repo.git" for pushes, but pulls will still
+use the original URL.
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index 0b88a51..03d95dc 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -344,7 +344,8 @@
 The "master" branch that was created at the time you cloned is a copy
 of the HEAD in the repository that you cloned from.  That repository
 may also have had other branches, though, and your local repository
-keeps branches which track each of those remote branches, which you
+keeps branches which track each of those remote branches, called
+remote-tracking branches, which you
 can view using the "-r" option to linkgit:git-branch[1]:
 
 ------------------------------------------------
@@ -359,13 +360,23 @@
   origin/todo
 ------------------------------------------------
 
-You cannot check out these remote-tracking branches, but you can
-examine them on a branch of your own, just as you would a tag:
+In this example, "origin" is called a remote repository, or "remote"
+for short. The branches of this repository are called "remote
+branches" from our point of view. The remote-tracking branches listed
+above were created based on the remote branches at clone time and will
+be updated by "git fetch" (hence "git pull") and "git push". See
+<<Updating-a-repository-With-git-fetch>> for details.
+
+You might want to build on one of these remote-tracking branches
+on a branch of your own, just as you would for a tag:
 
 ------------------------------------------------
 $ git checkout -b my-todo-copy origin/todo
 ------------------------------------------------
 
+You can also check out "origin/todo" directly to examine it or
+write a one-off patch.  See <<detached-head,detached head>>.
+
 Note that the name "origin" is just the name that git uses by default
 to refer to the repository that you cloned from.
 
@@ -397,7 +408,7 @@
 For the complete list of paths which git checks for references, and
 the order it uses to decide which to choose when there are multiple
 references with the same shorthand name, see the "SPECIFYING
-REVISIONS" section of linkgit:git-rev-parse[1].
+REVISIONS" section of linkgit:gitrevisions[7].
 
 [[Updating-a-repository-With-git-fetch]]
 Updating a repository with git fetch
@@ -435,7 +446,7 @@
 origin/master
 -------------------------------------------------
 
-If you run "git fetch <remote>" later, the tracking branches for the
+If you run "git fetch <remote>" later, the remote-tracking branches for the
 named <remote> will be updated.
 
 If you examine the file .git/config, you will see that git has added
@@ -568,7 +579,7 @@
 	- HEAD: refers to the head of the current branch
 
 There are many more; see the "SPECIFYING REVISIONS" section of the
-linkgit:git-rev-parse[1] man page for the complete list of ways to
+linkgit:gitrevisions[7] man page for the complete list of ways to
 name revisions.  Some examples:
 
 -------------------------------------------------
@@ -909,7 +920,7 @@
 $ gitk $( git show-ref --heads ) --not  $( git show-ref --tags )
 -------------------------------------------------
 
-(See linkgit:git-rev-parse[1] for explanations of commit-selecting
+(See linkgit:gitrevisions[7] for explanations of commit-selecting
 syntax such as `--not`.)
 
 [[making-a-release]]
@@ -955,7 +966,7 @@
 and then he just cut-and-pastes the output commands after verifying that
 they look OK.
 
-[[Finding-comments-With-given-Content]]
+[[Finding-commits-With-given-Content]]
 Finding commits referencing a file with given content
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -1183,7 +1194,23 @@
 -------------------------------------------------
 
 merges the development in the branch "branchname" into the current
-branch.  If there are conflicts--for example, if the same file is
+branch.
+
+A merge is made by combining the changes made in "branchname" and the
+changes made up to the latest commit in your current branch since
+their histories forked. The work tree is overwritten by the result of
+the merge when this combining is done cleanly, or overwritten by a
+half-merged results when this combining results in conflicts.
+Therefore, if you have uncommitted changes touching the same files as
+the ones impacted by the merge, Git will refuse to proceed. Most of
+the time, you will want to commit your changes before you can merge,
+and if you don't, then linkgit:git-stash[1] can take these changes
+away while you're doing the merge, and reapply them afterwards.
+
+If the changes are independent enough, Git will automatically complete
+the merge and commit the result (or reuse an existing commit in case
+of <<fast-forwards,fast-forward>>, see below). On the other hand,
+if there are conflicts--for example, if the same file is
 modified in two different ways in the remote branch and the local
 branch--then you are warned; the output may look something like this:
 
@@ -1384,7 +1411,7 @@
 
 However, if the current branch is a descendant of the other--so every
 commit present in the one is already contained in the other--then git
-just performs a "fast forward"; the head of the current branch is moved
+just performs a "fast-forward"; the head of the current branch is moved
 forward to point at the head of the merged-in branch, without any new
 commits being created.
 
@@ -1555,7 +1582,7 @@
 
 The linkgit:git-fsck[1] command runs a number of self-consistency checks
 on the repository, and reports on any problems.  This may take some
-time.  The most common warning by far is about "dangling" objects:
+time.
 
 -------------------------------------------------
 $ git fsck
@@ -1570,9 +1597,11 @@
 ...
 -------------------------------------------------
 
-Dangling objects are not a problem.  At worst they may take up a little
-extra disk space.  They can sometimes provide a last-resort method for
-recovering lost work--see <<dangling-objects>> for details.
+You will see informational messages on dangling objects. They are objects
+that still exist in the repository but are no longer referenced by any of
+your branches, and can (and will) be removed after a while with "gc".
+You can run `git fsck --no-dangling` to suppress these messages, and still
+view real errors.
 
 [[recovering-lost-changes]]
 Recovering lost changes
@@ -1582,7 +1611,7 @@
 Reflogs
 ^^^^^^^
 
-Say you modify a branch with `linkgit:git-reset[1] --hard`, and then
+Say you modify a branch with +linkgit:git-reset[1] \--hard+, and then
 realize that the branch was the only reference you had to that point in
 history.
 
@@ -1619,7 +1648,7 @@
 The reflogs are kept by default for 30 days, after which they may be
 pruned.  See linkgit:git-reflog[1] and linkgit:git-gc[1] to learn
 how to control this pruning, and see the "SPECIFYING REVISIONS"
-section of linkgit:git-rev-parse[1] for details.
+section of linkgit:gitrevisions[7] for details.
 
 Note that the reflog history is very different from normal git history.
 While normal history is shared by every repository that works on the
@@ -1679,12 +1708,12 @@
 Getting updates with git pull
 -----------------------------
 
-After you clone a repository and make a few changes of your own, you
+After you clone a repository and commit a few changes of your own, you
 may wish to check the original repository for updates and merge them
 into your own work.
 
 We have already seen <<Updating-a-repository-With-git-fetch,how to
-keep remote tracking branches up to date>> with linkgit:git-fetch[1],
+keep remote-tracking branches up to date>> with linkgit:git-fetch[1],
 and how to merge two branches.  So you can merge in changes from the
 original repository's master branch with:
 
@@ -1700,15 +1729,21 @@
 $ git pull origin master
 -------------------------------------------------
 
-In fact, if you have "master" checked out, then by default "git pull"
-merges from the HEAD branch of the origin repository.  So often you can
+In fact, if you have "master" checked out, then this branch has been
+configured by "git clone" to get changes from the HEAD branch of the
+origin repository.  So often you can
 accomplish the above with just a simple
 
 -------------------------------------------------
 $ git pull
 -------------------------------------------------
 
-More generally, a branch that is created from a remote branch will pull
+This command will fetch changes from the remote branches to your
+remote-tracking branches `origin/*`, and merge the default branch into
+the current branch.
+
+More generally, a branch that is created from a remote-tracking branch
+will pull
 by default from that branch.  See the descriptions of the
 branch.<name>.remote and branch.<name>.merge options in
 linkgit:git-config[1], and the discussion of the `--track` option in
@@ -1719,7 +1754,7 @@
 repository that you pulled from.
 
 (But note that no such commit will be created in the case of a
-<<fast-forwards,fast forward>>; instead, your branch will just be
+<<fast-forwards,fast-forward>>; instead, your branch will just be
 updated to point to the latest commit from the upstream branch.)
 
 The `git pull` command can also be given "." as the "remote" repository,
@@ -1943,7 +1978,7 @@
 -------------------------------------------------
 
 As with `git fetch`, `git push` will complain if this does not result in a
-<<fast-forwards,fast forward>>; see the following section for details on
+<<fast-forwards,fast-forward>>; see the following section for details on
 handling this case.
 
 Note that the target of a "push" is normally a
@@ -1976,7 +2011,7 @@
 What to do when a push fails
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-If a push would not result in a <<fast-forwards,fast forward>> of the
+If a push would not result in a <<fast-forwards,fast-forward>> of the
 remote branch, then it will fail with an error like:
 
 -------------------------------------------------
@@ -2090,7 +2125,7 @@
 $ cd work
 -------------------------------------------------
 
-Linus's tree will be stored in the remote branch named origin/master,
+Linus's tree will be stored in the remote-tracking branch named origin/master,
 and can be updated using linkgit:git-fetch[1]; you can track other
 public trees using linkgit:git-remote[1] to set up a "remote" and
 linkgit:git-fetch[1] to keep them up-to-date; see
@@ -2115,7 +2150,7 @@
 
 Important note!  If you have any local changes in these branches, then
 this merge will create a commit object in the history (with no local
-changes git will simply do a "Fast forward" merge).  Many people dislike
+changes git will simply do a "fast-forward" merge).  Many people dislike
 the "noise" that this creates in the Linux history, so you should avoid
 doing this capriciously in the "release" branch, as these noisy commits
 will become part of the permanent history when you ask Linus to pull
@@ -2155,11 +2190,14 @@
 
 Now to apply some patches from the community.  Think of a short
 snappy name for a branch to hold this patch (or related group of
-patches), and create a new branch from the current tip of Linus's
-branch:
+patches), and create a new branch from a recent stable tag of
+Linus's branch. Picking a stable base for your branch will:
+1) help you: by avoiding inclusion of unrelated and perhaps lightly
+tested changes
+2) help future bug hunters that use "git bisect" to find problems
 
 -------------------------------------------------
-$ git checkout -b speed-up-spinlocks origin
+$ git checkout -b speed-up-spinlocks v2.6.35
 -------------------------------------------------
 
 Now you apply the patch(es), run some tests, and commit the change(s).  If
@@ -2423,9 +2461,9 @@
 sequence of patches on top of "origin":
 
 ................................................
- o--o--o <-- origin
+ o--o--O <-- origin
         \
-         o--o--o <-- mywork
+	 a--b--c <-- mywork
 ................................................
 
 Some more interesting work has been done in the upstream project, and
@@ -2569,7 +2607,7 @@
 Other tools
 -----------
 
-There are numerous other tools, such as StGIT, which exist for the
+There are numerous other tools, such as StGit, which exist for the
 purpose of maintaining a patch series.  These are outside of the scope of
 this manual.
 
@@ -2729,9 +2767,9 @@
 checks to make sure that the most recent commit on the remote
 branch is a descendant of the most recent commit on your copy of the
 branch before updating your copy of the branch to point at the new
-commit.  Git calls this process a <<fast-forwards,fast forward>>.
+commit.  Git calls this process a <<fast-forwards,fast-forward>>.
 
-A fast forward looks something like this:
+A fast-forward looks something like this:
 
 ................................................
  o--o--o--o <-- old head of the branch
@@ -2781,8 +2819,8 @@
 may be lost, as we saw in the previous section.
 
 [[remote-branch-configuration]]
-Configuring remote branches
----------------------------
+Configuring remote-tracking branches
+------------------------------------
 
 We saw above that "origin" is just a shortcut to refer to the
 repository that you originally cloned from.  This information is
@@ -2832,7 +2870,7 @@
 You can also add a "+" to force the update each time:
 
 -------------------------------------------------
-$ git config remote.example.fetch +master:ref/remotes/example/master
+$ git config remote.example.fetch +master:refs/remotes/example/master
 -------------------------------------------------
 
 Don't do this unless you're sure you won't mind "git fetch" possibly
@@ -2928,7 +2966,7 @@
 
 - a tree: The SHA-1 name of a tree object (as defined below), representing
   the contents of a directory at a certain point in time.
-- parent(s): The SHA-1 name of some number of commits which represent the
+- parent(s): The SHA-1 name(s) of some number of commits which represent the
   immediately previous step(s) in the history of the project.  The
   example above has one parent; merge commits may have more than
   one.  A commit with no parents is called a "root" commit, and
@@ -3259,15 +3297,12 @@
 Assume the output looks like this:
 
 ------------------------------------------------
-$ git fsck --full
+$ git fsck --full --no-dangling
 broken link from    tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8
               to    blob 4b9458b3786228369c63936db65827de3cc06200
 missing blob 4b9458b3786228369c63936db65827de3cc06200
 ------------------------------------------------
 
-(Typically there will be some "dangling object" messages too, but they
-aren't interesting.)
-
 Now you know that blob 4b9458b3 is missing, and that the tree 2d9263c6
 points to it.  If you could find just one copy of that missing blob
 object, possibly in some other repository, you could move it into
@@ -3328,8 +3363,8 @@
 :100644 100644 oldsha... 4b9458b... M somedirectory/myfile
 ------------------------------------------------
 
-This tells you that the immediately preceding version of the file was
-"newsha", and that the immediately following version was "oldsha".
+This tells you that the immediately following version of the file was
+"newsha", and that the immediately preceding version was "oldsha".
 You also know the commit messages that went with the change from oldsha
 to 4b9458b and with the change from 4b9458b to newsha.
 
@@ -3624,6 +3659,26 @@
 Unable to checkout '261dfac35cb99d380eb966e102c1197139f7fa24' in submodule path 'a'
 -------------------------------------------------
 
+In older git versions it could be easily forgotten to commit new or modified
+files in a submodule, which silently leads to similar problems as not pushing
+the submodule changes. Starting with git 1.7.0 both "git status" and "git diff"
+in the superproject show submodules as modified when they contain new or
+modified files to protect against accidentally committing such a state. "git
+diff" will also add a "-dirty" to the work tree side when generating patch
+output or used with the --submodule option:
+
+-------------------------------------------------
+$ git diff
+diff --git a/sub b/sub
+--- a/sub
++++ b/sub
+@@ -1 +1 @@
+-Subproject commit 3f356705649b5d566d97ff843cf193359229a453
++Subproject commit 3f356705649b5d566d97ff843cf193359229a453-dirty
+$ git diff --submodule
+Submodule sub 3f35670..3f35670-dirty:
+-------------------------------------------------
+
 You also should not rewind branches in a submodule beyond commits that were
 ever recorded in any superproject.
 
@@ -3814,7 +3869,7 @@
 state at the time of the commit, and a list of parents:
 
 -------------------------------------------------
-$ git commit-tree <tree> -p <parent> [-p <parent2> ..]
+$ git commit-tree <tree> -p <parent> [(-p <parent2>)...]
 -------------------------------------------------
 
 and then giving the reason for the commit on stdin (either through
@@ -3980,8 +4035,8 @@
 Each line of the `git ls-files --unmerged` output begins with
 the blob mode bits, blob SHA-1, 'stage number', and the
 filename.  The 'stage number' is git's way to say which tree it
-came from: stage 1 corresponds to `$orig` tree, stage 2 `HEAD`
-tree, and stage3 `$target` tree.
+came from: stage 1 corresponds to the `$orig` tree, stage 2 to
+the `HEAD` tree, and stage 3 to the `$target` tree.
 
 Earlier we said that trivial merges are done inside
 `git read-tree -m`.  For example, if the file did not change
@@ -4131,7 +4186,7 @@
 
 `git rev-list` is the original version of the revision walker, which
 _always_ printed a list of revisions to stdout.  It is still functional,
-and needs to, since most new Git programs start out as scripts using
+and needs to, since most new Git commands start out as scripts using
 `git rev-list`.
 
 `git rev-parse` is not as important any more; it was only used to filter out
@@ -4152,7 +4207,7 @@
 
 If you are interested in more details of the revision walking process,
 just have a look at the first implementation of `cmd_log()`; call
-`git show v1.3.0{tilde}155^2{tilde}4` and scroll down to that function (note that you
+`git show v1.3.0~155^2~4` and scroll down to that function (note that you
 no longer need to call `setup_pager()` directly).
 
 Nowadays, `git log` is a builtin, which means that it is _contained_ in the
@@ -4215,9 +4270,9 @@
   negative numbers in case of different errors--and 0 on success.
 
 - the variable `sha1` in the function signature of `get_sha1()` is `unsigned
-  char \*`, but is actually expected to be a pointer to `unsigned
+  char *`, but is actually expected to be a pointer to `unsigned
   char[20]`.  This variable will contain the 160-bit SHA-1 of the given
-  commit.  Note that whenever a SHA-1 is passed as `unsigned char \*`, it
+  commit.  Note that whenever a SHA-1 is passed as `unsigned char *`, it
   is the binary representation, as opposed to the ASCII representation in
   hex characters, which is passed as `char *`.
 
@@ -4275,7 +4330,7 @@
 itself!
 
 [[glossary]]
-GIT Glossary
+Git Glossary
 ============
 
 include::glossary-content.txt[]
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index d7d9a9a..fde74a6 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.6.4.GIT
+DEF_VER=v1.7.11.GIT
 
 LF='
 '
@@ -12,7 +12,7 @@
 then
 	VN=$(cat version) || VN="$DEF_VER"
 elif test -d .git -o -f .git &&
-	VN=$(git describe --abbrev=4 HEAD 2>/dev/null) &&
+	VN=$(git describe --match "v[0-9]*" --abbrev=7 HEAD 2>/dev/null) &&
 	case "$VN" in
 	*$LF*) (exit 1) ;;
 	v[0-9]*)
diff --git a/INSTALL b/INSTALL
index ae7f750..28f34bd 100644
--- a/INSTALL
+++ b/INSTALL
@@ -13,6 +13,10 @@
 which are derived from $prefix, so "make all; make prefix=/usr
 install" would not work.
 
+The beginning of the Makefile documents many variables that affect the way
+git is built.  You can override them either from the command line, or in a
+config.mak file.
+
 Alternatively you can use autoconf generated ./configure script to
 set up install paths (via config.mak.autogen), so you can write instead
 
@@ -21,6 +25,28 @@
 	$ make all doc ;# as yourself
 	# make install install-doc install-html;# as root
 
+If you're willing to trade off (much) longer build time for a later
+faster git you can also do a profile feedback build with
+
+	$ make prefix=/usr PROFILE=BUILD all
+	# make prefix=/usr PROFILE=BUILD install
+
+This will run the complete test suite as training workload and then
+rebuild git with the generated profile feedback. This results in a git
+which is a few percent faster on CPU intensive workloads.  This
+may be a good tradeoff for distribution packagers.
+
+Or if you just want to install a profile-optimized version of git into
+your home directory, you could run:
+
+	$ make PROFILE=BUILD install
+
+As a caveat: a profile-optimized build takes a *lot* longer since the
+git tree must be built twice, and in order for the profiling
+measurements to work properly, ccache must be disabled and the test
+suite has to be run using only a single CPU.  In addition, the profile
+feedback build stage currently generates a lot of additional compiler
+warnings.
 
 Issues of note:
 
@@ -34,13 +60,17 @@
    Interactive Tools package still can install "git", but you can build it
    with --disable-transition option to avoid this.
 
- - You can use git after building but without installing if you
-   wanted to.  Various git commands need to find other git
-   commands and scripts to do their work, so you would need to
-   arrange a few environment variables to tell them that their
-   friends will be found in your built source area instead of at
-   their standard installation area.  Something like this works
-   for me:
+ - You can use git after building but without installing if you want
+   to test drive it.  Simply run git found in bin-wrappers directory
+   in the build directory, or prepend that directory to your $PATH.
+   This however is less efficient than running an installed git, as
+   you always need an extra fork+exec to run any git subcommand.
+
+   It is still possible to use git without installing by setting a few
+   environment variables, which was the way this was done
+   traditionally.  But using git found in bin-wrappers directory in
+   the build directory is far simpler.  As a historical reference, the
+   old way went like this:
 
 	GIT_EXEC_PATH=`pwd`
 	PATH=`pwd`:$PATH
@@ -48,32 +78,61 @@
 	export GIT_EXEC_PATH PATH GITPERLLIB
 
  - Git is reasonably self-sufficient, but does depend on a few external
-   programs and libraries:
+   programs and libraries.  Git can be used without most of them by adding
+   the approriate "NO_<LIBRARY>=YesPlease" to the make command line or
+   config.mak file.
 
 	- "zlib", the compression library. Git won't build without it.
 
-	- "openssl".  Unless you specify otherwise, you'll get the SHA1
-	  library from here.
+	- "ssh" is used to push and pull over the net.
 
-	  If you don't have openssl, you can use one of the SHA1 libraries
-	  that come with git (git includes the one from Mozilla, and has
-	  its own PowerPC and ARM optimized ones too - see the Makefile).
+	- A POSIX-compliant shell is required to run many scripts needed
+	  for everyday use (e.g. "bisect", "pull").
 
-	- libcurl library; git-http-fetch and git-fetch use them.  You
+	- "Perl" version 5.8 or later is needed to use some of the
+	  features (e.g. preparing a partial commit using "git add -i/-p",
+	  interacting with svn repositories with "git svn").  If you can
+	  live without these, use NO_PERL.  Note that recent releases of
+	  Redhat/Fedora are reported to ship Perl binary package with some
+	  core modules stripped away (see http://lwn.net/Articles/477234/),
+	  so you might need to install additional packages other than Perl
+	  itself, e.g. Time::HiRes.
+
+	- "openssl" library is used by git-imap-send to use IMAP over SSL.
+	  If you don't need it, use NO_OPENSSL.
+
+	  By default, git uses OpenSSL for SHA1 but it will use it's own
+	  library (inspired by Mozilla's) with either NO_OPENSSL or
+	  BLK_SHA1.  Also included is a version optimized for PowerPC
+	  (PPC_SHA1).
+
+	- "libcurl" library is used by git-http-fetch and git-fetch.  You
 	  might also want the "curl" executable for debugging purposes.
-	  If you do not use http transfer, you are probably OK if you
-	  do not have them.
+	  If you do not use http:// or https:// repositories, you do not
+	  have to have them (use NO_CURL).
 
-	- expat library; git-http-push uses it for remote lock
-	  management over DAV.  Similar to "curl" above, this is optional.
+	- "expat" library; git-http-push uses it for remote lock
+	  management over DAV.  Similar to "curl" above, this is optional
+	  (with NO_EXPAT).
 
-        - "wish", the Tcl/Tk windowing shell is used in gitk to show the
-          history graphically, and in git-gui.
+	- "wish", the Tcl/Tk windowing shell is used in gitk to show the
+	  history graphically, and in git-gui.  If you don't want gitk or
+	  git-gui, you can use NO_TCLTK.
 
-	- "ssh" is used to push and pull over the net
+	- A gettext library is used by default for localizing Git. The
+	  primary target is GNU libintl, but the Solaris gettext
+	  implementation also works.
 
-	- "perl" and POSIX-compliant shells are needed to use most of
-	  the bare-bones Porcelainish scripts.
+	  We need a gettext.h on the system for C code, gettext.sh (or
+	  Solaris gettext(1)) for shell scripts, and libintl-perl for Perl
+	  programs.
+
+	  Set NO_GETTEXT to disable localization support and make Git only
+	  use English. Under autoconf the configure script will do this
+	  automatically if it can't find libintl on the system.
+
+	- Python version 2.6 or later is needed to use the git-p4
+	  interface to Perforce.
 
  - Some platform specific issues are dealt with Makefile rules,
    but depending on your specific installation, you may not
@@ -102,40 +161,49 @@
    makeinfo and docbook2X.  Version 0.8.3 is known to work.
 
    Building and installing the pdf file additionally requires
-   dblatex.  Version 0.2.7 with asciidoc >= 8.2.7 is known to work.
+   dblatex.  Version >= 0.2.7 is known to work.
 
-   The documentation is written for AsciiDoc 7, but "make
-   ASCIIDOC8=YesPlease doc" will let you format with AsciiDoc 8.
-
-   Alternatively, pre-formatted documentation is available in
-   "html" and "man" branches of the git repository itself.  For
-   example, you could:
-
-	$ mkdir manual && cd manual
-	$ git init
-	$ git fetch-pack git://git.kernel.org/pub/scm/git/git.git man html |
-	  while read a b
-	  do
-	    echo $a >.git/$b
-	  done
-	$ cp .git/refs/heads/man .git/refs/heads/master
-	$ git checkout
-
-   to checkout the pre-built man pages.  Also in this repository:
-
-	$ git checkout html
-
-   would instead give you a copy of what you see at:
-
-	http://www.kernel.org/pub/software/scm/git/docs/
+   All formats require at least asciidoc 8.4.1.
 
    There are also "make quick-install-doc", "make quick-install-man"
    and "make quick-install-html" which install preformatted man pages
-   and html documentation.
-   This does not require asciidoc/xmlto, but it only works from within
-   a cloned checkout of git.git with these two extra branches, and will
-   not work for the maintainer for obvious chicken-and-egg reasons.
+   and html documentation. To use these build targets, you need to
+   clone two separate git-htmldocs and git-manpages repositories next
+   to the clone of git itself.
 
    It has been reported that docbook-xsl version 1.72 and 1.73 are
    buggy; 1.72 misformats manual pages for callouts, and 1.73 needs
    the patch in contrib/patches/docbook-xsl-manpages-charmap.patch
+
+   Users attempting to build the documentation on Cygwin may need to ensure
+   that the /etc/xml/catalog file looks something like this:
+
+   <?xml version="1.0"?>
+   <!DOCTYPE catalog PUBLIC
+      "-//OASIS//DTD Entity Resolution XML Catalog V1.0//EN"
+      "http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd"
+   >
+   <catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
+     <rewriteURI
+       uriStartString = "http://docbook.sourceforge.net/release/xsl/current"
+       rewritePrefix = "/usr/share/sgml/docbook/xsl-stylesheets"
+     />
+     <rewriteURI
+       uriStartString="http://www.oasis-open.org/docbook/xml/4.5"
+       rewritePrefix="/usr/share/sgml/docbook/xml-dtd-4.5"
+     />
+  </catalog>
+
+  This can be achieved with the following two xmlcatalog commands:
+
+  xmlcatalog --noout \
+     --add rewriteURI \
+        http://docbook.sourceforge.net/release/xsl/current \
+        /usr/share/sgml/docbook/xsl-stylesheets \
+     /etc/xml/catalog
+
+  xmlcatalog --noout \
+     --add rewriteURI \
+         http://www.oasis-open.org/docbook/xml/4.5/xsl/current \
+         /usr/share/sgml/docbook/xml-dtd-4.5 \
+     /etc/xml/catalog
diff --git a/LGPL-2.1 b/LGPL-2.1
new file mode 100644
index 0000000..d38b1b9
--- /dev/null
+++ b/LGPL-2.1
@@ -0,0 +1,511 @@
+
+ While most of this project is under the GPL (see COPYING), the xdiff/
+ library and some libc code from compat/ are licensed under the
+ GNU LGPL, version 2.1 or (at your option) any later version and some
+ other files are under other licenses.  Check the individual files to
+ be sure.
+
+----------------------------------------
+
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+                  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                            NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/Makefile b/Makefile
index eadcc70..b0b3493 100644
--- a/Makefile
+++ b/Makefile
@@ -8,6 +8,12 @@
 # Define SANE_TOOL_PATH to a colon-separated list of paths to prepend
 # to PATH if your tools in /usr/bin are broken.
 #
+# Define SOCKLEN_T to a suitable type (such as 'size_t') if your
+# system headers do not define a socklen_t type.
+#
+# Define INLINE to a suitable substitute (such as '__inline' or '') if git
+# fails to compile with errors about undefined inline functions or similar.
+#
 # Define SNPRINTF_RETURNS_BOGUS if your are on a system which snprintf()
 # or vsnprintf() return -1 instead of number of characters which would
 # have been written to the final string if enough space had been available.
@@ -18,28 +24,55 @@
 # Define NO_OPENSSL environment variable if you do not have OpenSSL.
 # This also implies BLK_SHA1.
 #
-# Define NO_CURL if you do not have libcurl installed.  git-http-pull and
+# Define USE_LIBPCRE if you have and want to use libpcre. git-grep will be
+# able to use Perl-compatible regular expressions.
+#
+# Define LIBPCREDIR=/foo/bar if your libpcre header and library files are in
+# /foo/bar/include and /foo/bar/lib directories.
+#
+# Define NO_CURL if you do not have libcurl installed.  git-http-fetch and
 # git-http-push are not built, and you cannot use http:// and https://
-# transports.
+# transports (neither smart nor dumb).
 #
 # 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.
+# not built, and you cannot push using http:// and https:// transports (dumb).
 #
 # Define EXPATDIR=/foo/bar if your expat header and library files are in
 # /foo/bar/include and /foo/bar/lib directories.
 #
+# Define NO_GETTEXT if you don't want Git output to be translated.
+# A translated Git requires GNU libintl or another gettext implementation,
+# plus libintl-perl at runtime.
+#
+# Define USE_GETTEXT_SCHEME and set it to 'fallthrough', if you don't trust
+# the installed gettext translation of the shell scripts output.
+#
+# Define HAVE_LIBCHARSET_H if you haven't set NO_GETTEXT and you can't
+# trust the langinfo.h's nl_langinfo(CODESET) function to return the
+# current character set. GNU and Solaris have a nl_langinfo(CODESET),
+# FreeBSD can use either, but MinGW and some others need to use
+# libcharset.h's locale_charset() instead.
+#
+# Define CHARSET_LIB to you need to link with library other than -liconv to
+# use locale_charset() function.  On some platforms this needs to set to
+# -lcharset
+#
+# Define LIBC_CONTAINS_LIBINTL if your gettext implementation doesn't
+# need -lintl when linking.
+#
+# Define NO_MSGFMT_EXTENDED_OPTIONS if your implementation of msgfmt
+# doesn't support GNU extensions like --check and --statistics
+#
+# Define HAVE_PATHS_H if you have paths.h and want to use the default PATH
+# it specifies.
+#
 # Define NO_D_INO_IN_DIRENT if you don't have d_ino in your struct dirent.
 #
 # 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.
+# d_type in struct dirent (Cygwin 1.5, fixed in Cygwin 1.7).
 #
 # Define NO_STRCASESTR if you don't have strcasestr.
 #
@@ -47,8 +80,8 @@
 #
 # Define NO_STRLCPY if you don't have strlcpy.
 #
-# Define NO_STRTOUMAX if you don't have strtoumax in the C library.
-# If your compiler also does not support long long or does not have
+# Define NO_STRTOUMAX if you don't have both strtoimax and strtoumax in the
+# C library. If your compiler also does not support long long or does not have
 # strtoull, define NO_STRTOULL.
 #
 # Define NO_SETENV if you don't have setenv in the C library.
@@ -59,6 +92,16 @@
 #
 # Define NO_MKSTEMPS if you don't have mkstemps in the C library.
 #
+# Define NO_STRTOK_R if you don't have strtok_r in the C library.
+#
+# Define NO_FNMATCH if you don't have fnmatch in the C library.
+#
+# Define NO_FNMATCH_CASEFOLD if your fnmatch function doesn't have the
+# FNM_CASEFOLD GNU extension.
+#
+# Define NO_GECOS_IN_PWENT if you don't have pw_gecos in struct passwd
+# in the C library.
+#
 # Define NO_LIBGEN_H if you don't have libgen.h.
 #
 # Define NEEDS_LIBGEN if your libgen needs -lgen when linking
@@ -90,7 +133,13 @@
 # Define PPC_SHA1 environment variable when running make to make use of
 # a bundled SHA1 routine optimized for PowerPC.
 #
-# Define NEEDS_SSL_WITH_CRYPTO if you need -lcrypto with -lssl (Darwin).
+# Define NEEDS_CRYPTO_WITH_SSL if you need -lcrypto when using -lssl (Darwin).
+#
+# Define NEEDS_SSL_WITH_CRYPTO if you need -lssl when using -lcrypto (Darwin).
+#
+# Define NEEDS_SSL_WITH_CURL if you need -lssl with -lcurl (Minix).
+#
+# Define NEEDS_IDN_WITH_CURL if you need -lidn when using -lcurl (Minix).
 #
 # Define NEEDS_LIBICONV if linking with libc is not enough (Darwin).
 #
@@ -106,7 +155,10 @@
 # Define NO_PTHREADS if you do not have or do not want to use Pthreads.
 #
 # Define NO_PREAD if you have a problem with pread() system call (e.g.
-# cygwin.dll before v1.5.22).
+# cygwin1.dll before v1.5.22).
+#
+# Define NO_THREAD_SAFE_PREAD if your pread() implementation is not
+# thread-safe. (e.g. compat/pread.c or cygwin)
 #
 # Define NO_FAST_WORKING_DIRECTORY if accessing objects in pack files is
 # generally faster on your platform than accessing the working directory.
@@ -116,6 +168,8 @@
 #
 # Define NO_IPV6 if you lack IPv6 support and getaddrinfo().
 #
+# Define NO_UNIX_SOCKETS if your system does not offer unix sockets.
+#
 # Define NO_SOCKADDR_STORAGE if your platform does not have struct
 # sockaddr_storage.
 #
@@ -130,6 +184,9 @@
 # that tells runtime paths to dynamic libraries;
 # "-Wl,-rpath=/path/lib" is used instead.
 #
+# Define NO_NORETURN if using buggy versions of gcc 4.6+ and profile feedback,
+# as the compiler can crash (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=49299)
+#
 # 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
@@ -148,15 +205,23 @@
 # Define NO_ST_BLOCKS_IN_STRUCT_STAT if your platform does not have st_blocks
 # field that counts the on-disk footprint in 512-byte blocks.
 #
-# Define ASCIIDOC8 if you want to format documentation with AsciiDoc 8
+# Define DOCBOOK_XSL_172 if you want to format man pages with DocBook XSL v1.72
+# (not v1.73 or v1.71).
 #
-# Define DOCBOOK_XSL_172 if you want to format man pages with DocBook XSL v1.72.
+# Define ASCIIDOC_ROFF if your DocBook XSL does not escape raw roff directives
+# (versions 1.68.1 through v1.72).
+#
+# Define GNU_ROFF if your target system uses GNU groff.  This forces
+# apostrophes to be ASCII so that cut&pasting examples to the shell
+# will work.
 #
 # Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's
 # MakeMaker (e.g. using ActiveState under Cygwin).
 #
 # Define NO_PERL if you do not want Perl scripts or libraries at all.
 #
+# Define NO_PYTHON if you do not want Python scripts or libraries at all.
+#
 # Define NO_TCLTK if you do not want Tcl/Tk GUI.
 #
 # The TCL_PATH variable governs the location of the Tcl interpreter
@@ -167,17 +232,10 @@
 # If not set it defaults to the bare 'wish'. If it is set to the empty
 # string then NO_TCLTK will be forced (this is used by configure script).
 #
-# Define THREADED_DELTA_SEARCH if you have pthreads and wish to exploit
-# parallel delta searching when packing objects.
-#
 # Define INTERNAL_QSORT to use Git's implementation of qsort(), which
 # is a simplified version of the merge sort used in glibc. This is
 # recommended if Git triggers O(n^2) behavior in your platform's qsort().
 #
-# Define NO_EXTERNAL_GREP if you don't want "git grep" to ever call
-# your external grep (e.g., if your system lacks grep, if its grep is
-# broken, or spawning external process is slower than built-in grep git has).
-#
 # Define UNRELIABLE_FSTAT if your system's fstat does not return the same
 # information on a not yet closed file that lstat would return for the same
 # file after it was closed.
@@ -189,12 +247,64 @@
 # Define NO_CROSS_DIRECTORY_HARDLINKS if you plan to distribute the installed
 # programs as a tar, where bin/ and libexec/ might be on different file systems.
 #
+# Define NO_INSTALL_HARDLINKS if you prefer to use either symbolic links or
+# copies to install built-in git commands e.g. git-cat-file.
+#
 # Define USE_NED_ALLOCATOR if you want to replace the platforms default
 # memory allocators with the nedmalloc allocator written by Niall Douglas.
 #
 # Define NO_REGEX if you have no or inferior regex support in your C library.
+#
+# Define HAVE_DEV_TTY if your system can open /dev/tty to interact with the
+# user.
+#
+# Define GETTEXT_POISON if you are debugging the choice of strings marked
+# for translation.  In a GETTEXT_POISON build, you can turn all strings marked
+# for translation into gibberish by setting the GIT_GETTEXT_POISON variable
+# (to any value) in your environment.
+#
+# Define JSMIN to point to JavaScript minifier that functions as
+# a filter to have gitweb.js minified.
+#
+# Define CSSMIN to point to a CSS minifier in order to generate a minified
+# version of gitweb.css
+#
+# Define DEFAULT_PAGER to a sensible pager command (defaults to "less") if
+# you want to use something different.  The value will be interpreted by the
+# shell at runtime when it is used.
+#
+# Define DEFAULT_EDITOR to a sensible editor command (defaults to "vi") if you
+# want to use something different.  The value will be interpreted by the shell
+# if necessary when it is used.  Examples:
+#
+#   DEFAULT_EDITOR='~/bin/vi',
+#   DEFAULT_EDITOR='$GIT_FALLBACK_EDITOR',
+#   DEFAULT_EDITOR='"C:\Program Files\Vim\gvim.exe" --nofork'
+#
+# Define COMPUTE_HEADER_DEPENDENCIES to "yes" if you want dependencies on
+# header files to be automatically computed, to avoid rebuilding objects when
+# an unrelated header file changes.  Define it to "no" to use the hard-coded
+# dependency rules.  The default is "auto", which means to use computed header
+# dependencies if your compiler is detected to support it.
+#
+# Define CHECK_HEADER_DEPENDENCIES to check for problems in the hard-coded
+# dependency rules.
+#
+# Define NATIVE_CRLF if your platform uses CRLF for line endings.
+#
+# Define XDL_FAST_HASH to use an alternative line-hashing method in
+# the diff algorithm.  It gives a nice speedup if your processor has
+# fast unaligned word loads.  Does NOT work on big-endian systems!
+# Enabled by default on x86_64.
+#
+# Define GIT_USER_AGENT if you want to change how git identifies itself during
+# network interactions.  The default is "git/$(GIT_VERSION)".
+#
+# Define DEFAULT_HELP_FORMAT to "man", "info" or "html"
+# (defaults to "man") if you want to have a different default when
+# "git help" is called without a parameter specifying the format.
 
-GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
+GIT-VERSION-FILE: FORCE
 	@$(SHELL_PATH) ./GIT-VERSION-GEN
 -include GIT-VERSION-FILE
 
@@ -205,11 +315,17 @@
 uname_P := $(shell sh -c 'uname -p 2>/dev/null || echo not')
 uname_V := $(shell sh -c 'uname -v 2>/dev/null || echo not')
 
+ifdef MSVC
+	# avoid the MingW and Cygwin configuration sections
+	uname_S := Windows
+	uname_O := Windows
+endif
+
 # CFLAGS and LDFLAGS are for the users to override from the command line.
 
 CFLAGS = -g -O2 -Wall
 LDFLAGS =
-ALL_CFLAGS = $(CFLAGS)
+ALL_CFLAGS = $(CPPFLAGS) $(CFLAGS)
 ALL_LDFLAGS = $(LDFLAGS)
 STRIP ?= strip
 
@@ -219,7 +335,7 @@
 #   mandir
 #   infodir
 #   htmldir
-#   ETC_GITCONFIG (but not sysconfdir)
+#   sysconfdir
 # can be specified as a relative path some/where/else;
 # this is interpreted as relative to $(prefix) and "git" at
 # runtime figures out where they are based on the path to the executable.
@@ -231,56 +347,39 @@
 mandir = share/man
 infodir = share/info
 gitexecdir = libexec/git-core
+mergetoolsdir = $(gitexecdir)/mergetools
 sharedir = $(prefix)/share
+gitwebdir = $(sharedir)/gitweb
+localedir = $(sharedir)/locale
 template_dir = share/git-core/templates
 htmldir = share/doc/git-doc
-ifeq ($(prefix),/usr)
-sysconfdir = /etc
 ETC_GITCONFIG = $(sysconfdir)/gitconfig
-else
-sysconfdir = $(prefix)/etc
-ETC_GITCONFIG = etc/gitconfig
-endif
+ETC_GITATTRIBUTES = $(sysconfdir)/gitattributes
 lib = lib
 # DESTDIR=
 pathsep = :
 
-# default configuration for gitweb
-GITWEB_CONFIG = gitweb_config.perl
-GITWEB_CONFIG_SYSTEM = /etc/gitweb.conf
-GITWEB_HOME_LINK_STR = projects
-GITWEB_SITENAME =
-GITWEB_PROJECTROOT = /pub/git
-GITWEB_PROJECT_MAXDEPTH = 2007
-GITWEB_EXPORT_OK =
-GITWEB_STRICT_EXPORT =
-GITWEB_BASE_URL =
-GITWEB_LIST =
-GITWEB_HOMETEXT = indextext.html
-GITWEB_CSS = gitweb.css
-GITWEB_LOGO = git-logo.png
-GITWEB_FAVICON = git-favicon.png
-GITWEB_SITE_HEADER =
-GITWEB_SITE_FOOTER =
+export prefix bindir sharedir sysconfdir gitwebdir localedir
 
-export prefix bindir sharedir sysconfdir
-
-CC = gcc
+CC = cc
 AR = ar
 RM = rm -f
+DIFF = diff
 TAR = tar
 FIND = find
 INSTALL = install
 RPMBUILD = rpmbuild
 TCL_PATH = tclsh
 TCLTK_PATH = wish
+XGETTEXT = xgettext
+MSGFMT = msgfmt
 PTHREAD_LIBS = -lpthread
+PTHREAD_CFLAGS =
+GCOV = gcov
 
 export TCL_PATH TCLTK_PATH
 
-# sparse is architecture-neutral, which means that we need to tell it
-# explicitly what architecture to check for. Fix this up for yours..
-SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__
+SPARSE_FLAGS =
 
 
 
@@ -289,7 +388,7 @@
 # Those must not be GNU-specific; they are shared with perl/ which may
 # be built by a different compiler. (Note that this is an artifact now
 # but it still might be nice to keep that distinction.)
-BASIC_CFLAGS =
+BASIC_CFLAGS = -I.
 BASIC_LDFLAGS =
 
 # Guard against environment variables
@@ -297,12 +396,25 @@
 BUILT_INS =
 COMPAT_CFLAGS =
 COMPAT_OBJS =
+XDIFF_OBJS =
+VCSSVN_OBJS =
+GENERATED_H =
+EXTRA_CPPFLAGS =
 LIB_H =
 LIB_OBJS =
+PROGRAM_OBJS =
 PROGRAMS =
 SCRIPT_PERL =
+SCRIPT_PYTHON =
 SCRIPT_SH =
-TEST_PROGRAMS =
+SCRIPT_LIB =
+TEST_PROGRAMS_NEED_X =
+
+# Having this variable in your environment would break pipelines because
+# you cause "cd" to echo its destination to stdout.  It can also take
+# scripts to unexpected places.  If you like CDPATH, define it for your
+# interactive shell sessions without exporting it.
+unexport CDPATH
 
 SCRIPT_SH += git-am.sh
 SCRIPT_SH += git-bisect.sh
@@ -313,19 +425,23 @@
 SCRIPT_SH += git-merge-one-file.sh
 SCRIPT_SH += git-merge-resolve.sh
 SCRIPT_SH += git-mergetool.sh
-SCRIPT_SH += git-mergetool--lib.sh
-SCRIPT_SH += git-parse-remote.sh
 SCRIPT_SH += git-pull.sh
 SCRIPT_SH += git-quiltimport.sh
-SCRIPT_SH += git-rebase--interactive.sh
 SCRIPT_SH += git-rebase.sh
 SCRIPT_SH += git-repack.sh
 SCRIPT_SH += git-request-pull.sh
-SCRIPT_SH += git-sh-setup.sh
 SCRIPT_SH += git-stash.sh
 SCRIPT_SH += git-submodule.sh
 SCRIPT_SH += git-web--browse.sh
 
+SCRIPT_LIB += git-mergetool--lib
+SCRIPT_LIB += git-parse-remote
+SCRIPT_LIB += git-rebase--am
+SCRIPT_LIB += git-rebase--interactive
+SCRIPT_LIB += git-rebase--merge
+SCRIPT_LIB += git-sh-setup
+SCRIPT_LIB += git-sh-i18n
+
 SCRIPT_PERL += git-add--interactive.perl
 SCRIPT_PERL += git-difftool.perl
 SCRIPT_PERL += git-archimport.perl
@@ -336,33 +452,63 @@
 SCRIPT_PERL += git-send-email.perl
 SCRIPT_PERL += git-svn.perl
 
+SCRIPT_PYTHON += git-remote-testgit.py
+SCRIPT_PYTHON += git-p4.py
+
 SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
 	  $(patsubst %.perl,%,$(SCRIPT_PERL)) \
+	  $(patsubst %.py,%,$(SCRIPT_PYTHON)) \
 	  git-instaweb
 
+ETAGS_TARGET = TAGS
+
 # Empty...
 EXTRA_PROGRAMS =
 
 # ... and all the rest that could be moved out of bindir to gitexecdir
 PROGRAMS += $(EXTRA_PROGRAMS)
-PROGRAMS += git-fast-import$X
-PROGRAMS += git-hash-object$X
-PROGRAMS += git-index-pack$X
-PROGRAMS += git-merge-index$X
-PROGRAMS += git-merge-tree$X
-PROGRAMS += git-mktag$X
-PROGRAMS += git-pack-redundant$X
-PROGRAMS += git-patch-id$X
-PROGRAMS += git-shell$X
-PROGRAMS += git-show-index$X
-PROGRAMS += git-unpack-file$X
-PROGRAMS += git-update-server-info$X
-PROGRAMS += git-upload-pack$X
-PROGRAMS += git-var$X
+
+PROGRAM_OBJS += credential-store.o
+PROGRAM_OBJS += daemon.o
+PROGRAM_OBJS += fast-import.o
+PROGRAM_OBJS += http-backend.o
+PROGRAM_OBJS += imap-send.o
+PROGRAM_OBJS += sh-i18n--envsubst.o
+PROGRAM_OBJS += shell.o
+PROGRAM_OBJS += show-index.o
+PROGRAM_OBJS += upload-pack.o
+
+# Binary suffix, set to .exe for Windows builds
+X =
+
+PROGRAMS += $(patsubst %.o,git-%$X,$(PROGRAM_OBJS))
+
+TEST_PROGRAMS_NEED_X += test-chmtime
+TEST_PROGRAMS_NEED_X += test-ctype
+TEST_PROGRAMS_NEED_X += test-date
+TEST_PROGRAMS_NEED_X += test-delta
+TEST_PROGRAMS_NEED_X += test-dump-cache-tree
+TEST_PROGRAMS_NEED_X += test-genrandom
+TEST_PROGRAMS_NEED_X += test-index-version
+TEST_PROGRAMS_NEED_X += test-line-buffer
+TEST_PROGRAMS_NEED_X += test-match-trees
+TEST_PROGRAMS_NEED_X += test-mergesort
+TEST_PROGRAMS_NEED_X += test-mktemp
+TEST_PROGRAMS_NEED_X += test-parse-options
+TEST_PROGRAMS_NEED_X += test-path-utils
+TEST_PROGRAMS_NEED_X += test-revision-walking
+TEST_PROGRAMS_NEED_X += test-run-command
+TEST_PROGRAMS_NEED_X += test-scrap-cache-tree
+TEST_PROGRAMS_NEED_X += test-sha1
+TEST_PROGRAMS_NEED_X += test-sigchain
+TEST_PROGRAMS_NEED_X += test-subprocess
+TEST_PROGRAMS_NEED_X += test-svn-fe
+
+TEST_PROGRAMS = $(patsubst %,%$X,$(TEST_PROGRAMS_NEED_X))
 
 # List built-in command $C whose implementation cmd_$C() is not in
-# builtin-$C.o but is linked in as part of some other command.
-BUILT_INS += $(patsubst builtin-%.o,git-%$X,$(BUILTIN_OBJS))
+# builtin/$C.o but is linked in as part of some other command.
+BUILT_INS += $(patsubst builtin/%.o,git-%$X,$(BUILTIN_OBJS))
 
 BUILT_INS += git-cherry$X
 BUILT_INS += git-cherry-pick$X
@@ -378,12 +524,22 @@
 BUILT_INS += git-status$X
 BUILT_INS += git-whatchanged$X
 
-# what 'all' will build and 'install' will install, in gitexecdir
+# what 'all' will build and 'install' will install in gitexecdir,
+# excluding programs for built-in commands
 ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
 
 # what 'all' will build but not install in gitexecdir
 OTHER_PROGRAMS = git$X
 
+# what test wrappers are needed and 'install' will install, in bindir
+BINDIR_PROGRAMS_NEED_X += git
+BINDIR_PROGRAMS_NEED_X += git-upload-pack
+BINDIR_PROGRAMS_NEED_X += git-receive-pack
+BINDIR_PROGRAMS_NEED_X += git-upload-archive
+BINDIR_PROGRAMS_NEED_X += git-shell
+
+BINDIR_PROGRAMS_NO_X += git-cvsserver
+
 # Set paths to tools early so that they can be used for version tests.
 ifndef SHELL_PATH
 	SHELL_PATH = /bin/sh
@@ -391,88 +547,166 @@
 ifndef PERL_PATH
 	PERL_PATH = /usr/bin/perl
 endif
+ifndef PYTHON_PATH
+	PYTHON_PATH = /usr/bin/python
+endif
 
 export PERL_PATH
+export PYTHON_PATH
 
 LIB_FILE=libgit.a
 XDIFF_LIB=xdiff/lib.a
+VCSSVN_LIB=vcs-svn/lib.a
 
+LIB_H += xdiff/xinclude.h
+LIB_H += xdiff/xmacros.h
+LIB_H += xdiff/xdiff.h
+LIB_H += xdiff/xtypes.h
+LIB_H += xdiff/xutils.h
+LIB_H += xdiff/xprepare.h
+LIB_H += xdiff/xdiffi.h
+LIB_H += xdiff/xemit.h
+
+LIB_H += vcs-svn/line_buffer.h
+LIB_H += vcs-svn/sliding_window.h
+LIB_H += vcs-svn/repo_tree.h
+LIB_H += vcs-svn/fast_export.h
+LIB_H += vcs-svn/svndiff.h
+LIB_H += vcs-svn/svndump.h
+
+GENERATED_H += common-cmds.h
+
+LIB_H += advice.h
 LIB_H += archive.h
+LIB_H += argv-array.h
 LIB_H += attr.h
+LIB_H += bisect.h
 LIB_H += blob.h
+LIB_H += branch.h
 LIB_H += builtin.h
-LIB_H += cache.h
+LIB_H += bulk-checkin.h
+LIB_H += bundle.h
 LIB_H += cache-tree.h
+LIB_H += cache.h
+LIB_H += color.h
+LIB_H += column.h
 LIB_H += commit.h
+LIB_H += compat/bswap.h
 LIB_H += compat/cygwin.h
 LIB_H += compat/mingw.h
+LIB_H += compat/obstack.h
+LIB_H += compat/precompose_utf8.h
+LIB_H += compat/terminal.h
+LIB_H += compat/win32/dirent.h
+LIB_H += compat/win32/poll.h
+LIB_H += compat/win32/pthread.h
+LIB_H += compat/win32/syslog.h
+LIB_H += connected.h
+LIB_H += convert.h
+LIB_H += credential.h
 LIB_H += csum-file.h
 LIB_H += decorate.h
 LIB_H += delta.h
-LIB_H += diffcore.h
 LIB_H += diff.h
+LIB_H += diffcore.h
 LIB_H += dir.h
+LIB_H += exec_cmd.h
+LIB_H += fetch-pack.h
+LIB_H += fmt-merge-msg.h
 LIB_H += fsck.h
+LIB_H += gettext.h
 LIB_H += git-compat-util.h
+LIB_H += gpg-interface.h
 LIB_H += graph.h
 LIB_H += grep.h
 LIB_H += hash.h
 LIB_H += help.h
+LIB_H += http.h
+LIB_H += kwset.h
 LIB_H += levenshtein.h
 LIB_H += list-objects.h
 LIB_H += ll-merge.h
 LIB_H += log-tree.h
 LIB_H += mailmap.h
+LIB_H += merge-file.h
 LIB_H += merge-recursive.h
+LIB_H += mergesort.h
+LIB_H += notes-cache.h
+LIB_H += notes-merge.h
+LIB_H += notes.h
 LIB_H += object.h
-LIB_H += pack.h
 LIB_H += pack-refs.h
 LIB_H += pack-revindex.h
+LIB_H += pack.h
 LIB_H += parse-options.h
 LIB_H += patch-ids.h
 LIB_H += pkt-line.h
 LIB_H += progress.h
+LIB_H += prompt.h
 LIB_H += quote.h
+LIB_H += reachable.h
 LIB_H += reflog-walk.h
 LIB_H += refs.h
 LIB_H += remote.h
 LIB_H += rerere.h
+LIB_H += resolve-undo.h
 LIB_H += revision.h
 LIB_H += run-command.h
+LIB_H += send-pack.h
+LIB_H += sequencer.h
+LIB_H += sha1-array.h
 LIB_H += sha1-lookup.h
+LIB_H += shortlog.h
 LIB_H += sideband.h
 LIB_H += sigchain.h
 LIB_H += strbuf.h
+LIB_H += streaming.h
 LIB_H += string-list.h
+LIB_H += submodule.h
 LIB_H += tag.h
+LIB_H += tar.h
+LIB_H += thread-utils.h
 LIB_H += transport.h
-LIB_H += tree.h
 LIB_H += tree-walk.h
+LIB_H += tree.h
 LIB_H += unpack-trees.h
+LIB_H += url.h
 LIB_H += userdiff.h
 LIB_H += utf8.h
+LIB_H += varint.h
+LIB_H += walker.h
 LIB_H += wt-status.h
+LIB_H += xdiff-interface.h
+LIB_H += xdiff/xdiff.h
 
 LIB_OBJS += abspath.o
+LIB_OBJS += advice.o
 LIB_OBJS += alias.o
 LIB_OBJS += alloc.o
 LIB_OBJS += archive.o
 LIB_OBJS += archive-tar.o
 LIB_OBJS += archive-zip.o
+LIB_OBJS += argv-array.o
 LIB_OBJS += attr.o
 LIB_OBJS += base85.o
 LIB_OBJS += bisect.o
 LIB_OBJS += blob.o
 LIB_OBJS += branch.o
+LIB_OBJS += bulk-checkin.o
 LIB_OBJS += bundle.o
 LIB_OBJS += cache-tree.o
 LIB_OBJS += color.o
+LIB_OBJS += column.o
 LIB_OBJS += combine-diff.o
 LIB_OBJS += commit.o
+LIB_OBJS += compat/obstack.o
+LIB_OBJS += compat/terminal.o
 LIB_OBJS += config.o
 LIB_OBJS += connect.o
+LIB_OBJS += connected.o
 LIB_OBJS += convert.o
 LIB_OBJS += copy.o
+LIB_OBJS += credential.o
 LIB_OBJS += csum-file.o
 LIB_OBJS += ctype.o
 LIB_OBJS += date.o
@@ -492,11 +726,15 @@
 LIB_OBJS += environment.o
 LIB_OBJS += exec_cmd.o
 LIB_OBJS += fsck.o
+LIB_OBJS += gettext.o
+LIB_OBJS += gpg-interface.o
 LIB_OBJS += graph.o
 LIB_OBJS += grep.o
 LIB_OBJS += hash.o
 LIB_OBJS += help.o
+LIB_OBJS += hex.o
 LIB_OBJS += ident.o
+LIB_OBJS += kwset.o
 LIB_OBJS += levenshtein.o
 LIB_OBJS += list-objects.o
 LIB_OBJS += ll-merge.o
@@ -506,7 +744,11 @@
 LIB_OBJS += match-trees.o
 LIB_OBJS += merge-file.o
 LIB_OBJS += merge-recursive.o
+LIB_OBJS += mergesort.o
 LIB_OBJS += name-hash.o
+LIB_OBJS += notes.o
+LIB_OBJS += notes-cache.o
+LIB_OBJS += notes-merge.o
 LIB_OBJS += object.o
 LIB_OBJS += pack-check.o
 LIB_OBJS += pack-refs.o
@@ -514,6 +756,7 @@
 LIB_OBJS += pack-write.o
 LIB_OBJS += pager.o
 LIB_OBJS += parse-options.o
+LIB_OBJS += parse-options-cb.o
 LIB_OBJS += patch-delta.o
 LIB_OBJS += patch-ids.o
 LIB_OBJS += path.o
@@ -521,17 +764,22 @@
 LIB_OBJS += preload-index.o
 LIB_OBJS += pretty.o
 LIB_OBJS += progress.o
+LIB_OBJS += prompt.o
 LIB_OBJS += quote.o
 LIB_OBJS += reachable.o
 LIB_OBJS += read-cache.o
 LIB_OBJS += reflog-walk.o
 LIB_OBJS += refs.o
 LIB_OBJS += remote.o
+LIB_OBJS += replace_object.o
 LIB_OBJS += rerere.o
+LIB_OBJS += resolve-undo.o
 LIB_OBJS += revision.o
 LIB_OBJS += run-command.o
+LIB_OBJS += sequencer.o
 LIB_OBJS += server-info.o
 LIB_OBJS += setup.o
+LIB_OBJS += sha1-array.o
 LIB_OBJS += sha1-lookup.o
 LIB_OBJS += sha1_file.o
 LIB_OBJS += sha1_name.o
@@ -539,108 +787,132 @@
 LIB_OBJS += sideband.o
 LIB_OBJS += sigchain.o
 LIB_OBJS += strbuf.o
+LIB_OBJS += streaming.o
 LIB_OBJS += string-list.o
+LIB_OBJS += submodule.o
 LIB_OBJS += symlinks.o
 LIB_OBJS += tag.o
 LIB_OBJS += trace.o
 LIB_OBJS += transport.o
+LIB_OBJS += transport-helper.o
 LIB_OBJS += tree-diff.o
 LIB_OBJS += tree.o
 LIB_OBJS += tree-walk.o
 LIB_OBJS += unpack-trees.o
+LIB_OBJS += url.o
 LIB_OBJS += usage.o
 LIB_OBJS += userdiff.o
 LIB_OBJS += utf8.o
+LIB_OBJS += varint.o
+LIB_OBJS += version.o
 LIB_OBJS += walker.o
 LIB_OBJS += wrapper.o
 LIB_OBJS += write_or_die.o
 LIB_OBJS += ws.o
 LIB_OBJS += wt-status.o
 LIB_OBJS += xdiff-interface.o
+LIB_OBJS += zlib.o
 
-BUILTIN_OBJS += builtin-add.o
-BUILTIN_OBJS += builtin-annotate.o
-BUILTIN_OBJS += builtin-apply.o
-BUILTIN_OBJS += builtin-archive.o
-BUILTIN_OBJS += builtin-bisect--helper.o
-BUILTIN_OBJS += builtin-blame.o
-BUILTIN_OBJS += builtin-branch.o
-BUILTIN_OBJS += builtin-bundle.o
-BUILTIN_OBJS += builtin-cat-file.o
-BUILTIN_OBJS += builtin-check-attr.o
-BUILTIN_OBJS += builtin-check-ref-format.o
-BUILTIN_OBJS += builtin-checkout-index.o
-BUILTIN_OBJS += builtin-checkout.o
-BUILTIN_OBJS += builtin-clean.o
-BUILTIN_OBJS += builtin-clone.o
-BUILTIN_OBJS += builtin-commit-tree.o
-BUILTIN_OBJS += builtin-commit.o
-BUILTIN_OBJS += builtin-config.o
-BUILTIN_OBJS += builtin-count-objects.o
-BUILTIN_OBJS += builtin-describe.o
-BUILTIN_OBJS += builtin-diff-files.o
-BUILTIN_OBJS += builtin-diff-index.o
-BUILTIN_OBJS += builtin-diff-tree.o
-BUILTIN_OBJS += builtin-diff.o
-BUILTIN_OBJS += builtin-fast-export.o
-BUILTIN_OBJS += builtin-fetch--tool.o
-BUILTIN_OBJS += builtin-fetch-pack.o
-BUILTIN_OBJS += builtin-fetch.o
-BUILTIN_OBJS += builtin-fmt-merge-msg.o
-BUILTIN_OBJS += builtin-for-each-ref.o
-BUILTIN_OBJS += builtin-fsck.o
-BUILTIN_OBJS += builtin-gc.o
-BUILTIN_OBJS += builtin-grep.o
-BUILTIN_OBJS += builtin-help.o
-BUILTIN_OBJS += builtin-init-db.o
-BUILTIN_OBJS += builtin-log.o
-BUILTIN_OBJS += builtin-ls-files.o
-BUILTIN_OBJS += builtin-ls-remote.o
-BUILTIN_OBJS += builtin-ls-tree.o
-BUILTIN_OBJS += builtin-mailinfo.o
-BUILTIN_OBJS += builtin-mailsplit.o
-BUILTIN_OBJS += builtin-merge.o
-BUILTIN_OBJS += builtin-merge-base.o
-BUILTIN_OBJS += builtin-merge-file.o
-BUILTIN_OBJS += builtin-merge-ours.o
-BUILTIN_OBJS += builtin-merge-recursive.o
-BUILTIN_OBJS += builtin-mktree.o
-BUILTIN_OBJS += builtin-mv.o
-BUILTIN_OBJS += builtin-name-rev.o
-BUILTIN_OBJS += builtin-pack-objects.o
-BUILTIN_OBJS += builtin-pack-refs.o
-BUILTIN_OBJS += builtin-prune-packed.o
-BUILTIN_OBJS += builtin-prune.o
-BUILTIN_OBJS += builtin-push.o
-BUILTIN_OBJS += builtin-read-tree.o
-BUILTIN_OBJS += builtin-receive-pack.o
-BUILTIN_OBJS += builtin-reflog.o
-BUILTIN_OBJS += builtin-remote.o
-BUILTIN_OBJS += builtin-rerere.o
-BUILTIN_OBJS += builtin-reset.o
-BUILTIN_OBJS += builtin-rev-list.o
-BUILTIN_OBJS += builtin-rev-parse.o
-BUILTIN_OBJS += builtin-revert.o
-BUILTIN_OBJS += builtin-rm.o
-BUILTIN_OBJS += builtin-send-pack.o
-BUILTIN_OBJS += builtin-shortlog.o
-BUILTIN_OBJS += builtin-show-branch.o
-BUILTIN_OBJS += builtin-show-ref.o
-BUILTIN_OBJS += builtin-stripspace.o
-BUILTIN_OBJS += builtin-symbolic-ref.o
-BUILTIN_OBJS += builtin-tag.o
-BUILTIN_OBJS += builtin-tar-tree.o
-BUILTIN_OBJS += builtin-unpack-objects.o
-BUILTIN_OBJS += builtin-update-index.o
-BUILTIN_OBJS += builtin-update-ref.o
-BUILTIN_OBJS += builtin-upload-archive.o
-BUILTIN_OBJS += builtin-verify-pack.o
-BUILTIN_OBJS += builtin-verify-tag.o
-BUILTIN_OBJS += builtin-write-tree.o
+BUILTIN_OBJS += builtin/add.o
+BUILTIN_OBJS += builtin/annotate.o
+BUILTIN_OBJS += builtin/apply.o
+BUILTIN_OBJS += builtin/archive.o
+BUILTIN_OBJS += builtin/bisect--helper.o
+BUILTIN_OBJS += builtin/blame.o
+BUILTIN_OBJS += builtin/branch.o
+BUILTIN_OBJS += builtin/bundle.o
+BUILTIN_OBJS += builtin/cat-file.o
+BUILTIN_OBJS += builtin/check-attr.o
+BUILTIN_OBJS += builtin/check-ref-format.o
+BUILTIN_OBJS += builtin/checkout-index.o
+BUILTIN_OBJS += builtin/checkout.o
+BUILTIN_OBJS += builtin/clean.o
+BUILTIN_OBJS += builtin/clone.o
+BUILTIN_OBJS += builtin/column.o
+BUILTIN_OBJS += builtin/commit-tree.o
+BUILTIN_OBJS += builtin/commit.o
+BUILTIN_OBJS += builtin/config.o
+BUILTIN_OBJS += builtin/count-objects.o
+BUILTIN_OBJS += builtin/credential.o
+BUILTIN_OBJS += builtin/describe.o
+BUILTIN_OBJS += builtin/diff-files.o
+BUILTIN_OBJS += builtin/diff-index.o
+BUILTIN_OBJS += builtin/diff-tree.o
+BUILTIN_OBJS += builtin/diff.o
+BUILTIN_OBJS += builtin/fast-export.o
+BUILTIN_OBJS += builtin/fetch-pack.o
+BUILTIN_OBJS += builtin/fetch.o
+BUILTIN_OBJS += builtin/fmt-merge-msg.o
+BUILTIN_OBJS += builtin/for-each-ref.o
+BUILTIN_OBJS += builtin/fsck.o
+BUILTIN_OBJS += builtin/gc.o
+BUILTIN_OBJS += builtin/grep.o
+BUILTIN_OBJS += builtin/hash-object.o
+BUILTIN_OBJS += builtin/help.o
+BUILTIN_OBJS += builtin/index-pack.o
+BUILTIN_OBJS += builtin/init-db.o
+BUILTIN_OBJS += builtin/log.o
+BUILTIN_OBJS += builtin/ls-files.o
+BUILTIN_OBJS += builtin/ls-remote.o
+BUILTIN_OBJS += builtin/ls-tree.o
+BUILTIN_OBJS += builtin/mailinfo.o
+BUILTIN_OBJS += builtin/mailsplit.o
+BUILTIN_OBJS += builtin/merge.o
+BUILTIN_OBJS += builtin/merge-base.o
+BUILTIN_OBJS += builtin/merge-file.o
+BUILTIN_OBJS += builtin/merge-index.o
+BUILTIN_OBJS += builtin/merge-ours.o
+BUILTIN_OBJS += builtin/merge-recursive.o
+BUILTIN_OBJS += builtin/merge-tree.o
+BUILTIN_OBJS += builtin/mktag.o
+BUILTIN_OBJS += builtin/mktree.o
+BUILTIN_OBJS += builtin/mv.o
+BUILTIN_OBJS += builtin/name-rev.o
+BUILTIN_OBJS += builtin/notes.o
+BUILTIN_OBJS += builtin/pack-objects.o
+BUILTIN_OBJS += builtin/pack-redundant.o
+BUILTIN_OBJS += builtin/pack-refs.o
+BUILTIN_OBJS += builtin/patch-id.o
+BUILTIN_OBJS += builtin/prune-packed.o
+BUILTIN_OBJS += builtin/prune.o
+BUILTIN_OBJS += builtin/push.o
+BUILTIN_OBJS += builtin/read-tree.o
+BUILTIN_OBJS += builtin/receive-pack.o
+BUILTIN_OBJS += builtin/reflog.o
+BUILTIN_OBJS += builtin/remote.o
+BUILTIN_OBJS += builtin/remote-ext.o
+BUILTIN_OBJS += builtin/remote-fd.o
+BUILTIN_OBJS += builtin/replace.o
+BUILTIN_OBJS += builtin/rerere.o
+BUILTIN_OBJS += builtin/reset.o
+BUILTIN_OBJS += builtin/rev-list.o
+BUILTIN_OBJS += builtin/rev-parse.o
+BUILTIN_OBJS += builtin/revert.o
+BUILTIN_OBJS += builtin/rm.o
+BUILTIN_OBJS += builtin/send-pack.o
+BUILTIN_OBJS += builtin/shortlog.o
+BUILTIN_OBJS += builtin/show-branch.o
+BUILTIN_OBJS += builtin/show-ref.o
+BUILTIN_OBJS += builtin/stripspace.o
+BUILTIN_OBJS += builtin/symbolic-ref.o
+BUILTIN_OBJS += builtin/tag.o
+BUILTIN_OBJS += builtin/tar-tree.o
+BUILTIN_OBJS += builtin/unpack-file.o
+BUILTIN_OBJS += builtin/unpack-objects.o
+BUILTIN_OBJS += builtin/update-index.o
+BUILTIN_OBJS += builtin/update-ref.o
+BUILTIN_OBJS += builtin/update-server-info.o
+BUILTIN_OBJS += builtin/upload-archive.o
+BUILTIN_OBJS += builtin/var.o
+BUILTIN_OBJS += builtin/verify-pack.o
+BUILTIN_OBJS += builtin/verify-tag.o
+BUILTIN_OBJS += builtin/write-tree.o
 
 GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
 EXTLIBS =
 
+GIT_USER_AGENT = git/$(GIT_VERSION)
+
 #
 # Platform specific tweaks
 #
@@ -649,15 +921,29 @@
 # because maintaining the nesting to match is a pain.  If
 # we had "elif" things would have been much nicer...
 
+ifeq ($(uname_M),x86_64)
+	XDL_FAST_HASH = YesPlease
+endif
+ifeq ($(uname_S),OSF1)
+	# Need this for u_short definitions et al
+	BASIC_CFLAGS += -D_OSF_SOURCE
+	SOCKLEN_T = int
+	NO_STRTOULL = YesPlease
+	NO_NSEC = YesPlease
+endif
 ifeq ($(uname_S),Linux)
 	NO_STRLCPY = YesPlease
 	NO_MKSTEMPS = YesPlease
-	THREADED_DELTA_SEARCH = YesPlease
+	HAVE_PATHS_H = YesPlease
+	LIBC_CONTAINS_LIBINTL = YesPlease
+	HAVE_DEV_TTY = YesPlease
 endif
 ifeq ($(uname_S),GNU/kFreeBSD)
 	NO_STRLCPY = YesPlease
 	NO_MKSTEMPS = YesPlease
-	THREADED_DELTA_SEARCH = YesPlease
+	HAVE_PATHS_H = YesPlease
+	DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease
+	LIBC_CONTAINS_LIBINTL = YesPlease
 endif
 ifeq ($(uname_S),UnixWare)
 	CC = cc
@@ -701,6 +987,7 @@
 	TAR = gtar
 endif
 ifeq ($(uname_S),Darwin)
+	NEEDS_CRYPTO_WITH_SSL = YesPlease
 	NEEDS_SSL_WITH_CRYPTO = YesPlease
 	NEEDS_LIBICONV = YesPlease
 	ifeq ($(shell expr "$(uname_R)" : '[15678]\.'),2)
@@ -710,8 +997,10 @@
 		NO_STRLCPY = YesPlease
 	endif
 	NO_MEMMEM = YesPlease
-	THREADED_DELTA_SEARCH = YesPlease
 	USE_ST_TIMESPEC = YesPlease
+	HAVE_DEV_TTY = YesPlease
+	COMPAT_OBJS += compat/precompose_utf8.o
+	BASIC_CFLAGS += -DPRECOMPOSE_UNICODE
 endif
 ifeq ($(uname_S),SunOS)
 	NEEDS_SOCKET = YesPlease
@@ -723,7 +1012,19 @@
 	NO_MKDTEMP = YesPlease
 	NO_MKSTEMPS = YesPlease
 	NO_REGEX = YesPlease
-	NO_EXTERNAL_GREP = YesPlease
+	NO_FNMATCH_CASEFOLD = YesPlease
+	NO_MSGFMT_EXTENDED_OPTIONS = YesPlease
+	ifeq ($(uname_R),5.6)
+		SOCKLEN_T = int
+		NO_HSTRERROR = YesPlease
+		NO_IPV6 = YesPlease
+		NO_SOCKADDR_STORAGE = YesPlease
+		NO_UNSETENV = YesPlease
+		NO_SETENV = YesPlease
+		NO_STRLCPY = YesPlease
+		NO_STRTOUMAX = YesPlease
+		GIT_TEST_CMP = cmp
+	endif
 	ifeq ($(uname_R),5.7)
 		NEEDS_RESOLV = YesPlease
 		NO_IPV6 = YesPlease
@@ -731,59 +1032,65 @@
 		NO_UNSETENV = YesPlease
 		NO_SETENV = YesPlease
 		NO_STRLCPY = YesPlease
-		NO_C99_FORMAT = YesPlease
 		NO_STRTOUMAX = YesPlease
+		GIT_TEST_CMP = cmp
 	endif
 	ifeq ($(uname_R),5.8)
 		NO_UNSETENV = YesPlease
 		NO_SETENV = YesPlease
-		NO_C99_FORMAT = YesPlease
 		NO_STRTOUMAX = YesPlease
+		GIT_TEST_CMP = cmp
 	endif
 	ifeq ($(uname_R),5.9)
 		NO_UNSETENV = YesPlease
 		NO_SETENV = YesPlease
-		NO_C99_FORMAT = YesPlease
 		NO_STRTOUMAX = YesPlease
-	endif
-	ifdef NO_IPV6
-		NEEDS_RESOLV = YesPlease
+		GIT_TEST_CMP = cmp
 	endif
 	INSTALL = /usr/ucb/install
 	TAR = gtar
 	BASIC_CFLAGS += -D__EXTENSIONS__ -D__sun__ -DHAVE_ALLOCA_H
 endif
 ifeq ($(uname_O),Cygwin)
-	NO_D_TYPE_IN_DIRENT = YesPlease
-	NO_D_INO_IN_DIRENT = YesPlease
-	NO_STRCASESTR = YesPlease
-	NO_MEMMEM = YesPlease
-	NO_MKSTEMPS = YesPlease
-	NO_SYMLINK_HEAD = YesPlease
+	ifeq ($(shell expr "$(uname_R)" : '1\.[1-6]\.'),4)
+		NO_D_TYPE_IN_DIRENT = YesPlease
+		NO_D_INO_IN_DIRENT = YesPlease
+		NO_STRCASESTR = YesPlease
+		NO_MEMMEM = YesPlease
+		NO_MKSTEMPS = YesPlease
+		NO_SYMLINK_HEAD = YesPlease
+		NO_IPV6 = YesPlease
+		OLD_ICONV = UnfortunatelyYes
+	endif
+	NO_THREAD_SAFE_PREAD = YesPlease
 	NEEDS_LIBICONV = YesPlease
 	NO_FAST_WORKING_DIRECTORY = UnfortunatelyYes
 	NO_TRUSTABLE_FILEMODE = UnfortunatelyYes
-	OLD_ICONV = UnfortunatelyYes
+	NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
 	# There are conflicting reports about this.
 	# On some boxes NO_MMAP is needed, and not so elsewhere.
 	# Try commenting this out if you suspect MMAP is more efficient
 	NO_MMAP = YesPlease
-	NO_IPV6 = YesPlease
 	X = .exe
+	COMPAT_OBJS += compat/cygwin.o
+	UNRELIABLE_FSTAT = UnfortunatelyYes
+	SPARSE_FLAGS = -isystem /usr/include/w32api -Wno-one-bit-signed-bitfield
 endif
 ifeq ($(uname_S),FreeBSD)
 	NEEDS_LIBICONV = YesPlease
+	OLD_ICONV = YesPlease
 	NO_MEMMEM = YesPlease
 	BASIC_CFLAGS += -I/usr/local/include
 	BASIC_LDFLAGS += -L/usr/local/lib
 	DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease
 	USE_ST_TIMESPEC = YesPlease
-	THREADED_DELTA_SEARCH = YesPlease
 	ifeq ($(shell expr "$(uname_R)" : '4\.'),2)
 		PTHREAD_LIBS = -pthread
 		NO_UINTMAX_T = YesPlease
 		NO_STRTOUMAX = YesPlease
 	endif
+	PYTHON_PATH = /usr/local/bin/python
+	HAVE_PATHS_H = YesPlease
 endif
 ifeq ($(uname_S),OpenBSD)
 	NO_STRCASESTR = YesPlease
@@ -792,7 +1099,7 @@
 	NEEDS_LIBICONV = YesPlease
 	BASIC_CFLAGS += -I/usr/local/include
 	BASIC_LDFLAGS += -L/usr/local/lib
-	THREADED_DELTA_SEARCH = YesPlease
+	HAVE_PATHS_H = YesPlease
 endif
 ifeq ($(uname_S),NetBSD)
 	ifeq ($(shell expr "$(uname_R)" : '[01]\.'),2)
@@ -800,11 +1107,12 @@
 	endif
 	BASIC_CFLAGS += -I/usr/pkg/include
 	BASIC_LDFLAGS += -L/usr/pkg/lib $(CC_LD_DYNPATH)/usr/pkg/lib
-	THREADED_DELTA_SEARCH = YesPlease
 	USE_ST_TIMESPEC = YesPlease
 	NO_MKSTEMPS = YesPlease
+	HAVE_PATHS_H = YesPlease
 endif
 ifeq ($(uname_S),AIX)
+	DEFAULT_PAGER = more
 	NO_STRCASESTR=YesPlease
 	NO_MEMMEM = YesPlease
 	NO_MKDTEMP = YesPlease
@@ -815,16 +1123,22 @@
 	INTERNAL_QSORT = UnfortunatelyYes
 	NEEDS_LIBICONV=YesPlease
 	BASIC_CFLAGS += -D_LARGE_FILES
-	ifneq ($(shell expr "$(uname_V)" : '[1234]'),1)
-		THREADED_DELTA_SEARCH = YesPlease
-	else
+	ifeq ($(shell expr "$(uname_V)" : '[1234]'),1)
 		NO_PTHREADS = YesPlease
+	else
+		PTHREAD_LIBS = -lpthread
 	endif
+	ifeq ($(shell expr "$(uname_V).$(uname_R)" : '5\.1'),3)
+		INLINE=''
+	endif
+	GIT_TEST_CMP = cmp
 endif
 ifeq ($(uname_S),GNU)
 	# GNU/Hurd
 	NO_STRLCPY=YesPlease
 	NO_MKSTEMPS = YesPlease
+	HAVE_PATHS_H = YesPlease
+	LIBC_CONTAINS_LIBINTL = YesPlease
 endif
 ifeq ($(uname_S),IRIX)
 	NO_SETENV = YesPlease
@@ -833,8 +1147,15 @@
 	NO_MEMMEM = YesPlease
 	NO_MKSTEMPS = YesPlease
 	NO_MKDTEMP = YesPlease
+	# When compiled with the MIPSpro 7.4.4m compiler, and without pthreads
+	# (i.e. NO_PTHREADS is set), and _with_ MMAP (i.e. NO_MMAP is not set),
+	# git dies with a segmentation fault when trying to access the first
+	# entry of a reflog.  The conservative choice is made to always set
+	# NO_MMAP.  If you suspect that your compiler is not affected by this
+	# issue, comment out the NO_MMAP statement.
 	NO_MMAP = YesPlease
-	NO_EXTERNAL_GREP = UnfortunatelyYes
+	NO_REGEX = YesPlease
+	NO_FNMATCH_CASEFOLD = YesPlease
 	SNPRINTF_RETURNS_BOGUS = YesPlease
 	SHELL_PATH = /usr/gnu/bin/bash
 	NEEDS_LIBGEN = YesPlease
@@ -846,13 +1167,21 @@
 	NO_MEMMEM = YesPlease
 	NO_MKSTEMPS = YesPlease
 	NO_MKDTEMP = YesPlease
+	# When compiled with the MIPSpro 7.4.4m compiler, and without pthreads
+	# (i.e. NO_PTHREADS is set), and _with_ MMAP (i.e. NO_MMAP is not set),
+	# git dies with a segmentation fault when trying to access the first
+	# entry of a reflog.  The conservative choice is made to always set
+	# NO_MMAP.  If you suspect that your compiler is not affected by this
+	# issue, comment out the NO_MMAP statement.
 	NO_MMAP = YesPlease
-	NO_EXTERNAL_GREP = UnfortunatelyYes
+	NO_REGEX = YesPlease
+	NO_FNMATCH_CASEFOLD = YesPlease
 	SNPRINTF_RETURNS_BOGUS = YesPlease
 	SHELL_PATH=/usr/gnu/bin/bash
 	NEEDS_LIBGEN = YesPlease
 endif
 ifeq ($(uname_S),HP-UX)
+	INLINE = __inline
 	NO_IPV6=YesPlease
 	NO_SETENV=YesPlease
 	NO_STRCASESTR=YesPlease
@@ -863,35 +1192,143 @@
 	NO_UNSETENV = YesPlease
 	NO_HSTRERROR = YesPlease
 	NO_SYS_SELECT_H = YesPlease
+	NO_FNMATCH_CASEFOLD = YesPlease
 	SNPRINTF_RETURNS_BOGUS = YesPlease
+	NO_NSEC = YesPlease
+	ifeq ($(uname_R),B.11.00)
+		NO_INET_NTOP = YesPlease
+		NO_INET_PTON = YesPlease
+	endif
+	ifeq ($(uname_R),B.10.20)
+		# Override HP-UX 11.x setting:
+		INLINE =
+		SOCKLEN_T = size_t
+		NO_PREAD = YesPlease
+		NO_INET_NTOP = YesPlease
+		NO_INET_PTON = YesPlease
+	endif
+	GIT_TEST_CMP = cmp
 endif
-ifneq (,$(findstring CYGWIN,$(uname_S)))
-	COMPAT_OBJS += compat/cygwin.o
-	UNRELIABLE_FSTAT = UnfortunatelyYes
-endif
-ifneq (,$(findstring MINGW,$(uname_S)))
+ifeq ($(uname_S),Windows)
+	GIT_VERSION := $(GIT_VERSION).MSVC
 	pathsep = ;
 	NO_PREAD = YesPlease
-	NO_OPENSSL = YesPlease
+	NEEDS_CRYPTO_WITH_SSL = YesPlease
 	NO_LIBGEN_H = YesPlease
+	NO_SYS_POLL_H = YesPlease
 	NO_SYMLINK_HEAD = YesPlease
 	NO_IPV6 = YesPlease
+	NO_UNIX_SOCKETS = YesPlease
 	NO_SETENV = YesPlease
 	NO_UNSETENV = YesPlease
 	NO_STRCASESTR = YesPlease
 	NO_STRLCPY = YesPlease
+	NO_STRTOK_R = YesPlease
+	NO_FNMATCH = YesPlease
 	NO_MEMMEM = YesPlease
-	NEEDS_LIBICONV = YesPlease
-	OLD_ICONV = YesPlease
-	NO_C99_FORMAT = YesPlease
+	# NEEDS_LIBICONV = YesPlease
+	NO_ICONV = YesPlease
 	NO_STRTOUMAX = YesPlease
+	NO_STRTOULL = YesPlease
 	NO_MKDTEMP = YesPlease
 	NO_MKSTEMPS = YesPlease
 	SNPRINTF_RETURNS_BOGUS = YesPlease
 	NO_SVN_TESTS = YesPlease
 	NO_PERL_MAKEMAKER = YesPlease
 	RUNTIME_PREFIX = YesPlease
-	NO_POSIX_ONLY_PROGRAMS = YesPlease
+	NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
+	NO_NSEC = YesPlease
+	USE_WIN32_MMAP = YesPlease
+	# USE_NED_ALLOCATOR = YesPlease
+	UNRELIABLE_FSTAT = UnfortunatelyYes
+	OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo
+	NO_REGEX = YesPlease
+	NO_CURL = YesPlease
+	NO_PYTHON = YesPlease
+	BLK_SHA1 = YesPlease
+	NO_POSIX_GOODIES = UnfortunatelyYes
+	NATIVE_CRLF = YesPlease
+	DEFAULT_HELP_FORMAT = html
+
+	CC = compat/vcbuild/scripts/clink.pl
+	AR = compat/vcbuild/scripts/lib.pl
+	CFLAGS =
+	BASIC_CFLAGS = -nologo -I. -I../zlib -Icompat/vcbuild -Icompat/vcbuild/include -DWIN32 -D_CONSOLE -DHAVE_STRING_H -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE
+	COMPAT_OBJS = compat/msvc.o compat/winansi.o \
+		compat/win32/pthread.o compat/win32/syslog.o \
+		compat/win32/poll.o compat/win32/dirent.o
+	COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -DHAVE_ALLOCA_H -Icompat -Icompat/regex -Icompat/win32 -DSTRIP_EXTENSION=\".exe\"
+	BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -SUBSYSTEM:CONSOLE -NODEFAULTLIB:MSVCRT.lib
+	EXTLIBS = user32.lib advapi32.lib shell32.lib wininet.lib ws2_32.lib
+	PTHREAD_LIBS =
+	lib =
+ifndef DEBUG
+	BASIC_CFLAGS += -GL -Os -MT
+	BASIC_LDFLAGS += -LTCG
+	AR += -LTCG
+else
+	BASIC_CFLAGS += -Zi -MTd
+endif
+	X = .exe
+endif
+ifeq ($(uname_S),Interix)
+	NO_INITGROUPS = YesPlease
+	NO_IPV6 = YesPlease
+	NO_MEMMEM = YesPlease
+	NO_MKDTEMP = YesPlease
+	NO_STRTOUMAX = YesPlease
+	NO_NSEC = YesPlease
+	NO_MKSTEMPS = YesPlease
+	ifeq ($(uname_R),3.5)
+		NO_INET_NTOP = YesPlease
+		NO_INET_PTON = YesPlease
+		NO_SOCKADDR_STORAGE = YesPlease
+		NO_FNMATCH_CASEFOLD = YesPlease
+	endif
+	ifeq ($(uname_R),5.2)
+		NO_INET_NTOP = YesPlease
+		NO_INET_PTON = YesPlease
+		NO_SOCKADDR_STORAGE = YesPlease
+		NO_FNMATCH_CASEFOLD = YesPlease
+	endif
+endif
+ifeq ($(uname_S),Minix)
+	NO_IPV6 = YesPlease
+	NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
+	NO_NSEC = YesPlease
+	NEEDS_LIBGEN =
+	NEEDS_CRYPTO_WITH_SSL = YesPlease
+	NEEDS_IDN_WITH_CURL = YesPlease
+	NEEDS_SSL_WITH_CURL = YesPlease
+	NEEDS_RESOLV =
+	NO_HSTRERROR = YesPlease
+	NO_MMAP = YesPlease
+	NO_CURL =
+	NO_EXPAT =
+endif
+ifneq (,$(findstring MINGW,$(uname_S)))
+	pathsep = ;
+	NO_PREAD = YesPlease
+	NEEDS_CRYPTO_WITH_SSL = YesPlease
+	NO_LIBGEN_H = YesPlease
+	NO_SYS_POLL_H = YesPlease
+	NO_SYMLINK_HEAD = YesPlease
+	NO_UNIX_SOCKETS = YesPlease
+	NO_SETENV = YesPlease
+	NO_UNSETENV = YesPlease
+	NO_STRCASESTR = YesPlease
+	NO_STRLCPY = YesPlease
+	NO_STRTOK_R = YesPlease
+	NO_FNMATCH = YesPlease
+	NO_MEMMEM = YesPlease
+	NEEDS_LIBICONV = YesPlease
+	OLD_ICONV = YesPlease
+	NO_STRTOUMAX = YesPlease
+	NO_MKDTEMP = YesPlease
+	NO_MKSTEMPS = YesPlease
+	NO_SVN_TESTS = YesPlease
+	NO_PERL_MAKEMAKER = YesPlease
+	RUNTIME_PREFIX = YesPlease
 	NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
 	NO_NSEC = YesPlease
 	USE_WIN32_MMAP = YesPlease
@@ -899,11 +1336,21 @@
 	UNRELIABLE_FSTAT = UnfortunatelyYes
 	OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo
 	NO_REGEX = YesPlease
-	COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/fnmatch
+	NO_PYTHON = YesPlease
+	BLK_SHA1 = YesPlease
+	ETAGS_TARGET = ETAGS
+	NO_INET_PTON = YesPlease
+	NO_INET_NTOP = YesPlease
+	NO_POSIX_GOODIES = UnfortunatelyYes
+	COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/win32
 	COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
-	COMPAT_OBJS += compat/mingw.o compat/fnmatch/fnmatch.o compat/winansi.o
+	COMPAT_OBJS += compat/mingw.o compat/winansi.o \
+		compat/win32/pthread.o compat/win32/syslog.o \
+		compat/win32/poll.o compat/win32/dirent.o
 	EXTLIBS += -lws2_32
+	PTHREAD_LIBS =
 	X = .exe
+	SPARSE_FLAGS = -Wno-one-bit-signed-bitfield
 ifneq (,$(wildcard ../THIS_IS_MSYSGIT))
 	htmldir=doc/git/html/
 	prefix =
@@ -911,16 +1358,52 @@
 	EXTLIBS += /mingw/lib/libz.a
 	NO_R_TO_GCC_LINKER = YesPlease
 	INTERNAL_QSORT = YesPlease
-	THREADED_DELTA_SEARCH = YesPlease
+	HAVE_LIBCHARSET_H = YesPlease
 else
 	NO_CURL = YesPlease
-	NO_PTHREADS = YesPlease
 endif
 endif
 
 -include config.mak.autogen
 -include config.mak
 
+ifndef sysconfdir
+ifeq ($(prefix),/usr)
+sysconfdir = /etc
+else
+sysconfdir = etc
+endif
+endif
+
+ifdef CHECK_HEADER_DEPENDENCIES
+COMPUTE_HEADER_DEPENDENCIES = no
+USE_COMPUTED_HEADER_DEPENDENCIES =
+endif
+
+ifndef COMPUTE_HEADER_DEPENDENCIES
+COMPUTE_HEADER_DEPENDENCIES = auto
+endif
+
+ifeq ($(COMPUTE_HEADER_DEPENDENCIES),auto)
+dep_check = $(shell $(CC) $(ALL_CFLAGS) \
+	-c -MF /dev/null -MMD -MP -x c /dev/null -o /dev/null 2>&1; \
+	echo $$?)
+ifeq ($(dep_check),0)
+override COMPUTE_HEADER_DEPENDENCIES = yes
+else
+override COMPUTE_HEADER_DEPENDENCIES = no
+endif
+endif
+
+ifeq ($(COMPUTE_HEADER_DEPENDENCIES),yes)
+USE_COMPUTED_HEADER_DEPENDENCIES = YesPlease
+else
+ifneq ($(COMPUTE_HEADER_DEPENDENCIES),no)
+$(error please set COMPUTE_HEADER_DEPENDENCIES to yes, no, or auto \
+(not "$(COMPUTE_HEADER_DEPENDENCIES)"))
+endif
+endif
+
 ifdef SANE_TOOL_PATH
 SANE_TOOL_PATH_SQ = $(subst ','\'',$(SANE_TOOL_PATH))
 BROKEN_PATH_FIX = 's|^\# @@BROKEN_PATH_FIX@@$$|git_broken_path_fix $(SANE_TOOL_PATH_SQ)|'
@@ -929,6 +1412,14 @@
 BROKEN_PATH_FIX = '/^\# @@BROKEN_PATH_FIX@@$$/d'
 endif
 
+ifneq (,$(INLINE))
+	BASIC_CFLAGS += -Dinline=$(INLINE)
+endif
+
+ifneq (,$(SOCKLEN_T))
+	BASIC_CFLAGS += -Dsocklen_t=$(SOCKLEN_T)
+endif
+
 ifeq ($(uname_S),Darwin)
 	ifndef NO_FINK
 		ifeq ($(shell test -d /sw/lib && echo y),y)
@@ -960,8 +1451,20 @@
 	COMPAT_OBJS += compat/basename.o
 endif
 
+ifdef USE_LIBPCRE
+	BASIC_CFLAGS += -DUSE_LIBPCRE
+	ifdef LIBPCREDIR
+		BASIC_CFLAGS += -I$(LIBPCREDIR)/include
+		EXTLIBS += -L$(LIBPCREDIR)/$(lib) $(CC_LD_DYNPATH)$(LIBPCREDIR)/$(lib)
+	endif
+	EXTLIBS += -lpcre
+endif
+
 ifdef NO_CURL
 	BASIC_CFLAGS += -DNO_CURL
+	REMOTE_CURL_PRIMARY =
+	REMOTE_CURL_ALIASES =
+	REMOTE_CURL_NAMES =
 else
 	ifdef CURLDIR
 		# Try "-Wl,-rpath=$(CURLDIR)/$(lib)" in such a case.
@@ -970,13 +1473,25 @@
 	else
 		CURL_LIBCURL = -lcurl
 	endif
-	BUILTIN_OBJS += builtin-http-fetch.o
-	EXTLIBS += $(CURL_LIBCURL)
-	LIB_OBJS += http.o http-walker.o
+	ifdef NEEDS_SSL_WITH_CURL
+		CURL_LIBCURL +=	-lssl
+		ifdef NEEDS_CRYPTO_WITH_SSL
+			CURL_LIBCURL += -lcrypto
+		endif
+	endif
+	ifdef NEEDS_IDN_WITH_CURL
+		CURL_LIBCURL += -lidn
+	endif
+
+	REMOTE_CURL_PRIMARY = git-remote-http$X
+	REMOTE_CURL_ALIASES = git-remote-https$X git-remote-ftp$X git-remote-ftps$X
+	REMOTE_CURL_NAMES = $(REMOTE_CURL_PRIMARY) $(REMOTE_CURL_ALIASES)
+	PROGRAM_OBJS += http-fetch.o
+	PROGRAMS += $(REMOTE_CURL_NAMES)
 	curl_check := $(shell (echo 070908; curl-config --vernum) | sort -r | sed -ne 2p)
 	ifeq "$(curl_check)" "070908"
 		ifndef NO_EXPAT
-			PROGRAMS += git-http-push$X
+			PROGRAM_OBJS += http-push.o
 		endif
 	endif
 	ifndef NO_EXPAT
@@ -995,10 +1510,6 @@
 endif
 EXTLIBS += -lz
 
-ifndef NO_POSIX_ONLY_PROGRAMS
-	PROGRAMS += git-daemon$X
-	PROGRAMS += git-imap-send$X
-endif
 ifndef NO_OPENSSL
 	OPENSSL_LIBSSL = -lssl
 	ifdef OPENSSLDIR
@@ -1007,16 +1518,23 @@
 	else
 		OPENSSL_LINK =
 	endif
+	ifdef NEEDS_CRYPTO_WITH_SSL
+		OPENSSL_LIBSSL += -lcrypto
+	endif
 else
 	BASIC_CFLAGS += -DNO_OPENSSL
 	BLK_SHA1 = 1
 	OPENSSL_LIBSSL =
 endif
+ifdef NO_OPENSSL
+	LIB_4_CRYPTO =
+else
 ifdef NEEDS_SSL_WITH_CRYPTO
 	LIB_4_CRYPTO = $(OPENSSL_LINK) -lcrypto -lssl
 else
 	LIB_4_CRYPTO = $(OPENSSL_LINK) -lcrypto
 endif
+endif
 ifdef NEEDS_LIBICONV
 	ifdef ICONVDIR
 		BASIC_CFLAGS += -I$(ICONVDIR)/include
@@ -1029,6 +1547,11 @@
 ifdef NEEDS_LIBGEN
 	EXTLIBS += -lgen
 endif
+ifndef NO_GETTEXT
+ifndef LIBC_CONTAINS_LIBINTL
+	EXTLIBS += -lintl
+endif
+endif
 ifdef NEEDS_SOCKET
 	EXTLIBS += -lsocket
 endif
@@ -1053,12 +1576,12 @@
 ifdef USE_ST_TIMESPEC
 	BASIC_CFLAGS += -DUSE_ST_TIMESPEC
 endif
+ifdef NO_NORETURN
+	BASIC_CFLAGS += -DNO_NORETURN
+endif
 ifdef NO_NSEC
 	BASIC_CFLAGS += -DNO_NSEC
 endif
-ifdef NO_C99_FORMAT
-	BASIC_CFLAGS += -DNO_C99_FORMAT
-endif
 ifdef SNPRINTF_RETURNS_BOGUS
 	COMPAT_CFLAGS += -DSNPRINTF_RETURNS_BOGUS
 	COMPAT_OBJS += compat/snprintf.o
@@ -1070,6 +1593,13 @@
 ifdef NO_SYMLINK_HEAD
 	BASIC_CFLAGS += -DNO_SYMLINK_HEAD
 endif
+ifdef GETTEXT_POISON
+	BASIC_CFLAGS += -DGETTEXT_POISON
+endif
+ifdef NO_GETTEXT
+	BASIC_CFLAGS += -DNO_GETTEXT
+	USE_GETTEXT_SCHEME ?= fallthrough
+endif
 ifdef NO_STRCASESTR
 	COMPAT_CFLAGS += -DNO_STRCASESTR
 	COMPAT_OBJS += compat/strcasestr.o
@@ -1080,11 +1610,26 @@
 endif
 ifdef NO_STRTOUMAX
 	COMPAT_CFLAGS += -DNO_STRTOUMAX
-	COMPAT_OBJS += compat/strtoumax.o
+	COMPAT_OBJS += compat/strtoumax.o compat/strtoimax.o
 endif
 ifdef NO_STRTOULL
 	COMPAT_CFLAGS += -DNO_STRTOULL
 endif
+ifdef NO_STRTOK_R
+	COMPAT_CFLAGS += -DNO_STRTOK_R
+	COMPAT_OBJS += compat/strtok_r.o
+endif
+ifdef NO_FNMATCH
+	COMPAT_CFLAGS += -Icompat/fnmatch
+	COMPAT_CFLAGS += -DNO_FNMATCH
+	COMPAT_OBJS += compat/fnmatch/fnmatch.o
+else
+ifdef NO_FNMATCH_CASEFOLD
+	COMPAT_CFLAGS += -Icompat/fnmatch
+	COMPAT_CFLAGS += -DNO_FNMATCH_CASEFOLD
+	COMPAT_OBJS += compat/fnmatch/fnmatch.o
+endif
+endif
 ifdef NO_SETENV
 	COMPAT_CFLAGS += -DNO_SETENV
 	COMPAT_OBJS += compat/setenv.o
@@ -1095,7 +1640,6 @@
 endif
 ifdef NO_MKSTEMPS
 	COMPAT_CFLAGS += -DNO_MKSTEMPS
-	COMPAT_OBJS += compat/mkstemps.o
 endif
 ifdef NO_UNSETENV
 	COMPAT_CFLAGS += -DNO_UNSETENV
@@ -1104,6 +1648,15 @@
 ifdef NO_SYS_SELECT_H
 	BASIC_CFLAGS += -DNO_SYS_SELECT_H
 endif
+ifdef NO_SYS_POLL_H
+	BASIC_CFLAGS += -DNO_SYS_POLL_H
+endif
+ifdef NO_INTTYPES_H
+	BASIC_CFLAGS += -DNO_INTTYPES_H
+endif
+ifdef NO_INITGROUPS
+	BASIC_CFLAGS += -DNO_INITGROUPS
+endif
 ifdef NO_MMAP
 	COMPAT_CFLAGS += -DNO_MMAP
 	COMPAT_OBJS += compat/mmap.o
@@ -1119,6 +1672,10 @@
 ifdef NO_PREAD
 	COMPAT_CFLAGS += -DNO_PREAD
 	COMPAT_OBJS += compat/pread.o
+	NO_THREAD_SAFE_PREAD = YesPlease
+endif
+ifdef NO_THREAD_SAFE_PREAD
+	BASIC_CFLAGS += -DNO_THREAD_SAFE_PREAD
 endif
 ifdef NO_FAST_WORKING_DIRECTORY
 	BASIC_CFLAGS += -DNO_FAST_WORKING_DIRECTORY
@@ -1141,9 +1698,17 @@
 endif
 ifdef NO_INET_NTOP
 	LIB_OBJS += compat/inet_ntop.o
+	BASIC_CFLAGS += -DNO_INET_NTOP
 endif
 ifdef NO_INET_PTON
 	LIB_OBJS += compat/inet_pton.o
+	BASIC_CFLAGS += -DNO_INET_PTON
+endif
+ifndef NO_UNIX_SOCKETS
+	LIB_OBJS += unix-socket.o
+	LIB_H += unix-socket.h
+	PROGRAM_OBJS += credential-cache.o
+	PROGRAM_OBJS += credential-cache--daemon.o
 endif
 
 ifdef NO_ICONV
@@ -1158,13 +1723,19 @@
 	BASIC_CFLAGS += -DNO_DEFLATE_BOUND
 endif
 
+ifdef NO_POSIX_GOODIES
+	BASIC_CFLAGS += -DNO_POSIX_GOODIES
+endif
+
 ifdef BLK_SHA1
 	SHA1_HEADER = "block-sha1/sha1.h"
 	LIB_OBJS += block-sha1/sha1.o
+	LIB_H += block-sha1/sha1.h
 else
 ifdef PPC_SHA1
 	SHA1_HEADER = "ppc/sha1.h"
 	LIB_OBJS += ppc/sha1.o ppc/sha1ppc.o
+	LIB_H += ppc/sha1.h
 else
 	SHA1_HEADER = <openssl/sha.h>
 	EXTLIBS += $(LIB_4_CRYPTO)
@@ -1190,22 +1761,29 @@
 endif
 
 ifdef NO_PTHREADS
-	THREADED_DELTA_SEARCH =
 	BASIC_CFLAGS += -DNO_PTHREADS
 else
+	BASIC_CFLAGS += $(PTHREAD_CFLAGS)
 	EXTLIBS += $(PTHREAD_LIBS)
-endif
-
-ifdef THREADED_DELTA_SEARCH
-	BASIC_CFLAGS += -DTHREADED_DELTA_SEARCH
 	LIB_OBJS += thread-utils.o
 endif
+
+ifdef HAVE_PATHS_H
+	BASIC_CFLAGS += -DHAVE_PATHS_H
+endif
+
+ifdef HAVE_LIBCHARSET_H
+	BASIC_CFLAGS += -DHAVE_LIBCHARSET_H
+	EXTLIBS += $(CHARSET_LIB)
+endif
+
+ifdef HAVE_DEV_TTY
+	BASIC_CFLAGS += -DHAVE_DEV_TTY
+endif
+
 ifdef DIR_HAS_BSD_GROUP_SEMANTICS
 	COMPAT_CFLAGS += -DDIR_HAS_BSD_GROUP_SEMANTICS
 endif
-ifdef NO_EXTERNAL_GREP
-	BASIC_CFLAGS += -DNO_EXTERNAL_GREP
-endif
 ifdef UNRELIABLE_FSTAT
 	BASIC_CFLAGS += -DUNRELIABLE_FSTAT
 endif
@@ -1215,10 +1793,22 @@
 endif
 
 ifdef USE_NED_ALLOCATOR
-       COMPAT_CFLAGS += -DUSE_NED_ALLOCATOR -DOVERRIDE_STRDUP -DNDEBUG -DREPLACE_SYSTEM_ALLOCATOR -Icompat/nedmalloc
+       COMPAT_CFLAGS += -Icompat/nedmalloc
        COMPAT_OBJS += compat/nedmalloc/nedmalloc.o
 endif
 
+ifdef GIT_TEST_CMP_USE_COPIED_CONTEXT
+	export GIT_TEST_CMP_USE_COPIED_CONTEXT
+endif
+
+ifndef NO_MSGFMT_EXTENDED_OPTIONS
+	MSGFMT += --check --statistics
+endif
+
+ifneq (,$(XDL_FAST_HASH))
+	BASIC_CFLAGS += -DXDL_FAST_HASH
+endif
+
 ifeq ($(TCLTK_PATH),)
 NO_TCLTK=NoThanks
 endif
@@ -1227,6 +1817,10 @@
 NO_PERL=NoThanks
 endif
 
+ifeq ($(PYTHON_PATH),)
+NO_PYTHON=NoThanks
+endif
+
 QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir
 QUIET_SUBDIR1  =
 
@@ -1243,6 +1837,11 @@
 	QUIET_LINK     = @echo '   ' LINK $@;
 	QUIET_BUILT_IN = @echo '   ' BUILTIN $@;
 	QUIET_GEN      = @echo '   ' GEN $@;
+	QUIET_LNCP     = @echo '   ' LN/CP $@;
+	QUIET_XGETTEXT = @echo '   ' XGETTEXT $@;
+	QUIET_MSGFMT   = @echo '   ' MSGFMT $@;
+	QUIET_GCOV     = @echo '   ' GCOV $@;
+	QUIET_SP       = @echo '   ' SP $<;
 	QUIET_SUBDIR0  = +@subdir=
 	QUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \
 			 $(MAKE) $(PRINT_DIR) -C $$subdir
@@ -1252,28 +1851,53 @@
 endif
 endif
 
-ifdef ASCIIDOC8
-	export ASCIIDOC8
+ifdef NO_INSTALL_HARDLINKS
+	export NO_INSTALL_HARDLINKS
+endif
+
+### profile feedback build
+#
+
+# Can adjust this to be a global directory if you want to do extended
+# data gathering
+PROFILE_DIR := $(CURDIR)
+
+ifeq ("$(PROFILE)","GEN")
+	CFLAGS += -fprofile-generate=$(PROFILE_DIR) -DNO_NORETURN=1
+	EXTLIBS += -lgcov
+	export CCACHE_DISABLE=t
+	V=1
+else
+ifneq ("$(PROFILE)","")
+	CFLAGS += -fprofile-use=$(PROFILE_DIR) -fprofile-correction -DNO_NORETURN=1
+	export CCACHE_DISABLE=t
+	V=1
+endif
 endif
 
 # Shell quote (do not use $(call) to accommodate ancient setups);
 
 SHA1_HEADER_SQ = $(subst ','\'',$(SHA1_HEADER))
 ETC_GITCONFIG_SQ = $(subst ','\'',$(ETC_GITCONFIG))
+ETC_GITATTRIBUTES_SQ = $(subst ','\'',$(ETC_GITATTRIBUTES))
 
 DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
 bindir_SQ = $(subst ','\'',$(bindir))
 bindir_relative_SQ = $(subst ','\'',$(bindir_relative))
 mandir_SQ = $(subst ','\'',$(mandir))
 infodir_SQ = $(subst ','\'',$(infodir))
+localedir_SQ = $(subst ','\'',$(localedir))
 gitexecdir_SQ = $(subst ','\'',$(gitexecdir))
 template_dir_SQ = $(subst ','\'',$(template_dir))
 htmldir_SQ = $(subst ','\'',$(htmldir))
 prefix_SQ = $(subst ','\'',$(prefix))
+gitwebdir_SQ = $(subst ','\'',$(gitwebdir))
 
 SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
 PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
+PYTHON_PATH_SQ = $(subst ','\'',$(PYTHON_PATH))
 TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_PATH))
+DIFF_SQ = $(subst ','\'',$(DIFF))
 
 LIBS = $(GITLIBS) $(EXTLIBS)
 
@@ -1281,19 +1905,64 @@
 	$(COMPAT_CFLAGS)
 LIB_OBJS += $(COMPAT_OBJS)
 
+# Quote for C
+
+ifdef DEFAULT_EDITOR
+DEFAULT_EDITOR_CQ = "$(subst ",\",$(subst \,\\,$(DEFAULT_EDITOR)))"
+DEFAULT_EDITOR_CQ_SQ = $(subst ','\'',$(DEFAULT_EDITOR_CQ))
+
+BASIC_CFLAGS += -DDEFAULT_EDITOR='$(DEFAULT_EDITOR_CQ_SQ)'
+endif
+
+ifdef DEFAULT_PAGER
+DEFAULT_PAGER_CQ = "$(subst ",\",$(subst \,\\,$(DEFAULT_PAGER)))"
+DEFAULT_PAGER_CQ_SQ = $(subst ','\'',$(DEFAULT_PAGER_CQ))
+
+BASIC_CFLAGS += -DDEFAULT_PAGER='$(DEFAULT_PAGER_CQ_SQ)'
+endif
+
+ifdef SHELL_PATH
+SHELL_PATH_CQ = "$(subst ",\",$(subst \,\\,$(SHELL_PATH)))"
+SHELL_PATH_CQ_SQ = $(subst ','\'',$(SHELL_PATH_CQ))
+
+BASIC_CFLAGS += -DSHELL_PATH='$(SHELL_PATH_CQ_SQ)'
+endif
+
+GIT_USER_AGENT_SQ = $(subst ','\'',$(GIT_USER_AGENT))
+GIT_USER_AGENT_CQ = "$(subst ",\",$(subst \,\\,$(GIT_USER_AGENT)))"
+GIT_USER_AGENT_CQ_SQ = $(subst ','\'',$(GIT_USER_AGENT_CQ))
+GIT-USER-AGENT: FORCE
+	@if test x'$(GIT_USER_AGENT_SQ)' != x"`cat GIT-USER-AGENT 2>/dev/null`"; then \
+		echo '$(GIT_USER_AGENT_SQ)' >GIT-USER-AGENT; \
+	fi
+
+ifdef DEFAULT_HELP_FORMAT
+BASIC_CFLAGS += -DDEFAULT_HELP_FORMAT='"$(DEFAULT_HELP_FORMAT)"'
+endif
+
 ALL_CFLAGS += $(BASIC_CFLAGS)
 ALL_LDFLAGS += $(BASIC_LDFLAGS)
 
-export TAR INSTALL DESTDIR SHELL_PATH
+export DIFF TAR INSTALL DESTDIR SHELL_PATH
 
 
 ### Build rules
 
 SHELL = $(SHELL_PATH)
 
-all:: shell_compatibility_test $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS) GIT-BUILD-OPTIONS
+all:: shell_compatibility_test
+
+ifeq "$(PROFILE)" "BUILD"
+ifeq ($(filter all,$(MAKECMDGOALS)),all)
+all:: profile-clean
+	$(MAKE) PROFILE=GEN all
+	$(MAKE) PROFILE=GEN -j1 test
+endif
+endif
+
+all:: $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) $(OTHER_PROGRAMS) GIT-BUILD-OPTIONS
 ifneq (,$X)
-	$(QUIET_BUILT_IN)$(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), test '$p' -ef '$p$X' || $(RM) '$p';)
+	$(QUIET_BUILT_IN)$(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), test -d '$p' -o '$p' -ef '$p$X' || $(RM) '$p';)
 endif
 
 all::
@@ -1302,9 +1971,12 @@
 	$(QUIET_SUBDIR0)gitk-git $(QUIET_SUBDIR1) all
 endif
 ifndef NO_PERL
-	$(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' all
+	$(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' localedir='$(localedir_SQ)' all
 endif
-	$(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1)
+ifndef NO_PYTHON
+	$(QUIET_SUBDIR0)git_remote_helpers $(QUIET_SUBDIR1) PYTHON_PATH='$(PYTHON_PATH_SQ)' prefix='$(prefix_SQ)' all
+endif
+	$(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1) SHELL_PATH='$(SHELL_PATH_SQ)' PERL_PATH='$(PERL_PATH_SQ)'
 
 please_set_SHELL_PATH_to_a_more_modern_shell:
 	@$$(:)
@@ -1314,20 +1986,61 @@
 strip: $(PROGRAMS) git$X
 	$(STRIP) $(STRIP_OPTS) $(PROGRAMS) git$X
 
-git.o: git.c common-cmds.h GIT-CFLAGS
-	$(QUIET_CC)$(CC) -DGIT_VERSION='"$(GIT_VERSION)"' \
-		'-DGIT_HTML_PATH="$(htmldir_SQ)"' \
-		$(ALL_CFLAGS) -c $(filter %.c,$^)
+### Target-specific flags and dependencies
 
-git$X: git.o $(BUILTIN_OBJS) $(GITLIBS)
+# The generic compilation pattern rule and automatically
+# computed header dependencies (falling back to a dependency on
+# LIB_H) are enough to describe how most targets should be built,
+# but some targets are special enough to need something a little
+# different.
+#
+# - When a source file "foo.c" #includes a generated header file,
+#   we need to list that dependency for the "foo.o" target.
+#
+#   We also list it from other targets that are built from foo.c
+#   like "foo.sp" and "foo.s", even though that is easy to forget
+#   to do because the generated header is already present around
+#   after a regular build attempt.
+#
+# - Some code depends on configuration kept in makefile
+#   variables. The target-specific variable EXTRA_CPPFLAGS can
+#   be used to convey that information to the C preprocessor
+#   using -D options.
+#
+#   The "foo.o" target should have a corresponding dependency on
+#   a file that changes when the value of the makefile variable
+#   changes.  For example, targets making use of the
+#   $(GIT_VERSION) variable depend on GIT-VERSION-FILE.
+#
+#   Technically the ".sp" and ".s" targets do not need this
+#   dependency because they are force-built, but they get the
+#   same dependency for consistency. This way, you do not have to
+#   know how each target is implemented. And it means the
+#   dependencies here will not need to change if the force-build
+#   details change some day.
+
+git.sp git.s git.o: GIT-PREFIX
+git.sp git.s git.o: EXTRA_CPPFLAGS = \
+	'-DGIT_HTML_PATH="$(htmldir_SQ)"' \
+	'-DGIT_MAN_PATH="$(mandir_SQ)"' \
+	'-DGIT_INFO_PATH="$(infodir_SQ)"'
+
+git$X: git.o GIT-LDFLAGS $(BUILTIN_OBJS) $(GITLIBS)
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ git.o \
 		$(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS)
 
-builtin-help.o: builtin-help.c common-cmds.h GIT-CFLAGS
-	$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \
-		'-DGIT_HTML_PATH="$(htmldir_SQ)"' \
-		'-DGIT_MAN_PATH="$(mandir_SQ)"' \
-		'-DGIT_INFO_PATH="$(infodir_SQ)"' $<
+help.sp help.s help.o: common-cmds.h
+
+builtin/help.sp builtin/help.s builtin/help.o: common-cmds.h GIT-PREFIX
+builtin/help.sp builtin/help.s builtin/help.o: EXTRA_CPPFLAGS = \
+	'-DGIT_HTML_PATH="$(htmldir_SQ)"' \
+	'-DGIT_MAN_PATH="$(mandir_SQ)"' \
+	'-DGIT_INFO_PATH="$(infodir_SQ)"'
+
+version.sp version.s version.o: GIT-VERSION-FILE GIT-USER-AGENT
+version.sp version.s version.o: EXTRA_CPPFLAGS = \
+	'-DGIT_VERSION="$(GIT_VERSION)"' \
+	'-DGIT_USER_AGENT=$(GIT_USER_AGENT_CQ_SQ)'
 
 $(BUILT_INS): git$X
 	$(QUIET_BUILT_IN)$(RM) $@ && \
@@ -1340,76 +2053,68 @@
 common-cmds.h: $(wildcard Documentation/git-*.txt)
 	$(QUIET_GEN)./generate-cmdlist.sh > $@+ && mv $@+ $@
 
-$(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
-	$(QUIET_GEN)$(RM) $@ $@+ && \
-	sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
-	    -e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \
-	    -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
-	    -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
-	    -e $(BROKEN_PATH_FIX) \
-	    $@.sh >$@+ && \
+SCRIPT_DEFINES = $(SHELL_PATH_SQ):$(DIFF_SQ):$(GIT_VERSION):\
+	$(localedir_SQ):$(NO_CURL):$(USE_GETTEXT_SCHEME):$(SANE_TOOL_PATH_SQ):\
+	$(gitwebdir_SQ):$(PERL_PATH_SQ)
+define cmd_munge_script
+$(RM) $@ $@+ && \
+sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
+    -e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \
+    -e 's|@@DIFF@@|$(DIFF_SQ)|' \
+    -e 's|@@LOCALEDIR@@|$(localedir_SQ)|g' \
+    -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
+    -e 's/@@USE_GETTEXT_SCHEME@@/$(USE_GETTEXT_SCHEME)/g' \
+    -e $(BROKEN_PATH_FIX) \
+    -e 's|@@GITWEBDIR@@|$(gitwebdir_SQ)|g' \
+    -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
+    $@.sh >$@+
+endef
+
+GIT-SCRIPT-DEFINES: FORCE
+	@FLAGS='$(SCRIPT_DEFINES)'; \
+	    if test x"$$FLAGS" != x"`cat $@ 2>/dev/null`" ; then \
+		echo 1>&2 "    * new script parameters"; \
+		echo "$$FLAGS" >$@; \
+            fi
+
+
+$(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh GIT-SCRIPT-DEFINES
+	$(QUIET_GEN)$(cmd_munge_script) && \
 	chmod +x $@+ && \
 	mv $@+ $@
 
+$(SCRIPT_LIB) : % : %.sh GIT-SCRIPT-DEFINES
+	$(QUIET_GEN)$(cmd_munge_script) && \
+	mv $@+ $@
+
 ifndef NO_PERL
 $(patsubst %.perl,%,$(SCRIPT_PERL)): perl/perl.mak
 
-perl/perl.mak: GIT-CFLAGS perl/Makefile perl/Makefile.PL
+perl/perl.mak: GIT-CFLAGS GIT-PREFIX perl/Makefile perl/Makefile.PL
 	$(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' $(@F)
 
-$(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl
+$(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl GIT-VERSION-FILE
 	$(QUIET_GEN)$(RM) $@ $@+ && \
 	INSTLIBDIR=`MAKEFLAGS= $(MAKE) -C perl -s --no-print-directory instlibdir` && \
 	sed -e '1{' \
 	    -e '	s|#!.*perl|#!$(PERL_PATH_SQ)|' \
 	    -e '	h' \
-	    -e '	s=.*=use lib (split(/$(pathsep)/, $$ENV{GITPERLLIB} || "@@INSTLIBDIR@@"));=' \
+	    -e '	s=.*=use lib (split(/$(pathsep)/, $$ENV{GITPERLLIB} || "'"$$INSTLIBDIR"'"));=' \
 	    -e '	H' \
 	    -e '	x' \
 	    -e '}' \
-	    -e 's|@@INSTLIBDIR@@|'"$$INSTLIBDIR"'|g' \
 	    -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
 	    $@.perl >$@+ && \
 	chmod +x $@+ && \
 	mv $@+ $@
 
-OTHER_PROGRAMS += gitweb/gitweb.cgi
-gitweb/gitweb.cgi: gitweb/gitweb.perl
-	$(QUIET_GEN)$(RM) $@ $@+ && \
-	sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \
-	    -e 's|++GIT_VERSION++|$(GIT_VERSION)|g' \
-	    -e 's|++GIT_BINDIR++|$(bindir)|g' \
-	    -e 's|++GITWEB_CONFIG++|$(GITWEB_CONFIG)|g' \
-	    -e 's|++GITWEB_CONFIG_SYSTEM++|$(GITWEB_CONFIG_SYSTEM)|g' \
-	    -e 's|++GITWEB_HOME_LINK_STR++|$(GITWEB_HOME_LINK_STR)|g' \
-	    -e 's|++GITWEB_SITENAME++|$(GITWEB_SITENAME)|g' \
-	    -e 's|++GITWEB_PROJECTROOT++|$(GITWEB_PROJECTROOT)|g' \
-	    -e 's|"++GITWEB_PROJECT_MAXDEPTH++"|$(GITWEB_PROJECT_MAXDEPTH)|g' \
-	    -e 's|++GITWEB_EXPORT_OK++|$(GITWEB_EXPORT_OK)|g' \
-	    -e 's|++GITWEB_STRICT_EXPORT++|$(GITWEB_STRICT_EXPORT)|g' \
-	    -e 's|++GITWEB_BASE_URL++|$(GITWEB_BASE_URL)|g' \
-	    -e 's|++GITWEB_LIST++|$(GITWEB_LIST)|g' \
-	    -e 's|++GITWEB_HOMETEXT++|$(GITWEB_HOMETEXT)|g' \
-	    -e 's|++GITWEB_CSS++|$(GITWEB_CSS)|g' \
-	    -e 's|++GITWEB_LOGO++|$(GITWEB_LOGO)|g' \
-	    -e 's|++GITWEB_FAVICON++|$(GITWEB_FAVICON)|g' \
-	    -e 's|++GITWEB_SITE_HEADER++|$(GITWEB_SITE_HEADER)|g' \
-	    -e 's|++GITWEB_SITE_FOOTER++|$(GITWEB_SITE_FOOTER)|g' \
-	    $< >$@+ && \
-	chmod +x $@+ && \
-	mv $@+ $@
 
-git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css
-	$(QUIET_GEN)$(RM) $@ $@+ && \
-	sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
-	    -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
-	    -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
-	    -e '/@@GITWEB_CGI@@/r gitweb/gitweb.cgi' \
-	    -e '/@@GITWEB_CGI@@/d' \
-	    -e '/@@GITWEB_CSS@@/r gitweb/gitweb.css' \
-	    -e '/@@GITWEB_CSS@@/d' \
-	    -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
-	    $@.sh > $@+ && \
+.PHONY: gitweb
+gitweb:
+	$(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) all
+
+git-instaweb: git-instaweb.sh gitweb GIT-SCRIPT-DEFINES
+	$(QUIET_GEN)$(cmd_munge_script) && \
 	chmod +x $@+ && \
 	mv $@+ $@
 else # NO_PERL
@@ -1422,75 +2127,243 @@
 	mv $@+ $@
 endif # NO_PERL
 
-configure: configure.ac
+ifndef NO_PYTHON
+$(patsubst %.py,%,$(SCRIPT_PYTHON)): GIT-CFLAGS GIT-PREFIX
+$(patsubst %.py,%,$(SCRIPT_PYTHON)): % : %.py
+	$(QUIET_GEN)$(RM) $@ $@+ && \
+	INSTLIBDIR=`MAKEFLAGS= $(MAKE) -C git_remote_helpers -s \
+		--no-print-directory prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' \
+		instlibdir` && \
+	sed -e '1s|#!.*python|#!$(PYTHON_PATH_SQ)|' \
+	    -e 's|\(os\.getenv("GITPYTHONLIB"\)[^)]*)|\1,"@@INSTLIBDIR@@")|' \
+	    -e 's|@@INSTLIBDIR@@|'"$$INSTLIBDIR"'|g' \
+	    $@.py >$@+ && \
+	chmod +x $@+ && \
+	mv $@+ $@
+else # NO_PYTHON
+$(patsubst %.py,%,$(SCRIPT_PYTHON)): % : unimplemented.sh
+	$(QUIET_GEN)$(RM) $@ $@+ && \
+	sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
+	    -e 's|@@REASON@@|NO_PYTHON=$(NO_PYTHON)|g' \
+	    unimplemented.sh >$@+ && \
+	chmod +x $@+ && \
+	mv $@+ $@
+endif # NO_PYTHON
+
+configure: configure.ac GIT-VERSION-FILE
 	$(QUIET_GEN)$(RM) $@ $<+ && \
 	sed -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
 	    $< > $<+ && \
 	autoconf -o $@ $<+ && \
 	$(RM) $<+
 
-# These can record GIT_VERSION
-git.o git.spec \
-	$(patsubst %.sh,%,$(SCRIPT_SH)) \
-	$(patsubst %.perl,%,$(SCRIPT_PERL)) \
-	: GIT-VERSION-FILE
-
-%.o: %.c GIT-CFLAGS
-	$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) $<
-%.s: %.c GIT-CFLAGS
-	$(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $<
-%.o: %.S
-	$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) $<
-
-exec_cmd.o: exec_cmd.c GIT-CFLAGS
-	$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \
-		'-DGIT_EXEC_PATH="$(gitexecdir_SQ)"' \
-		'-DBINDIR="$(bindir_relative_SQ)"' \
-		'-DPREFIX="$(prefix_SQ)"' \
-		$<
-
-builtin-init-db.o: builtin-init-db.c GIT-CFLAGS
-	$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"' $<
-
-config.o: config.c GIT-CFLAGS
-	$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DETC_GITCONFIG='"$(ETC_GITCONFIG_SQ)"' $<
-
-http.o: http.c GIT-CFLAGS
-	$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DGIT_USER_AGENT='"git/$(GIT_VERSION)"' $<
-
-ifdef NO_EXPAT
-http-walker.o: http-walker.c http.h GIT-CFLAGS
-	$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DNO_EXPAT $<
+ifdef AUTOCONFIGURED
+config.status: configure
+	$(QUIET_GEN)if test -f config.status; then \
+	  ./config.status --recheck; \
+	else \
+	  ./configure; \
+	fi
+reconfigure config.mak.autogen: config.status
+	$(QUIET_GEN)./config.status
+.PHONY: reconfigure # This is a convenience target.
 endif
 
-git-%$X: %.o $(GITLIBS)
+XDIFF_OBJS += xdiff/xdiffi.o
+XDIFF_OBJS += xdiff/xprepare.o
+XDIFF_OBJS += xdiff/xutils.o
+XDIFF_OBJS += xdiff/xemit.o
+XDIFF_OBJS += xdiff/xmerge.o
+XDIFF_OBJS += xdiff/xpatience.o
+XDIFF_OBJS += xdiff/xhistogram.o
+
+VCSSVN_OBJS += vcs-svn/line_buffer.o
+VCSSVN_OBJS += vcs-svn/sliding_window.o
+VCSSVN_OBJS += vcs-svn/repo_tree.o
+VCSSVN_OBJS += vcs-svn/fast_export.o
+VCSSVN_OBJS += vcs-svn/svndiff.o
+VCSSVN_OBJS += vcs-svn/svndump.o
+
+TEST_OBJS := $(patsubst test-%$X,test-%.o,$(TEST_PROGRAMS))
+OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \
+	$(XDIFF_OBJS) \
+	$(VCSSVN_OBJS) \
+	git.o
+ifndef NO_CURL
+	OBJECTS += http.o http-walker.o remote-curl.o
+endif
+
+dep_files := $(foreach f,$(OBJECTS),$(dir $f).depend/$(notdir $f).d)
+dep_dirs := $(addsuffix .depend,$(sort $(dir $(OBJECTS))))
+
+ifeq ($(COMPUTE_HEADER_DEPENDENCIES),yes)
+$(dep_dirs):
+	@mkdir -p $@
+
+missing_dep_dirs := $(filter-out $(wildcard $(dep_dirs)),$(dep_dirs))
+dep_file = $(dir $@).depend/$(notdir $@).d
+dep_args = -MF $(dep_file) -MMD -MP
+ifdef CHECK_HEADER_DEPENDENCIES
+$(error cannot compute header dependencies outside a normal build. \
+Please unset CHECK_HEADER_DEPENDENCIES and try again)
+endif
+endif
+
+ifneq ($(COMPUTE_HEADER_DEPENDENCIES),yes)
+ifndef CHECK_HEADER_DEPENDENCIES
+dep_dirs =
+missing_dep_dirs =
+dep_args =
+endif
+endif
+
+ifdef CHECK_HEADER_DEPENDENCIES
+ifndef PRINT_HEADER_DEPENDENCIES
+missing_deps = $(filter-out $(notdir $^), \
+	$(notdir $(shell $(MAKE) -s $@ \
+		CHECK_HEADER_DEPENDENCIES=YesPlease \
+		USE_COMPUTED_HEADER_DEPENDENCIES=YesPlease \
+		PRINT_HEADER_DEPENDENCIES=YesPlease)))
+endif
+endif
+
+ASM_SRC := $(wildcard $(OBJECTS:o=S))
+ASM_OBJ := $(ASM_SRC:S=o)
+C_OBJ := $(filter-out $(ASM_OBJ),$(OBJECTS))
+
+.SUFFIXES:
+
+ifdef PRINT_HEADER_DEPENDENCIES
+$(C_OBJ): %.o: %.c FORCE
+	echo $^
+$(ASM_OBJ): %.o: %.S FORCE
+	echo $^
+
+ifndef CHECK_HEADER_DEPENDENCIES
+$(error cannot print header dependencies during a normal build. \
+Please set CHECK_HEADER_DEPENDENCIES and try again)
+endif
+endif
+
+ifndef PRINT_HEADER_DEPENDENCIES
+ifdef CHECK_HEADER_DEPENDENCIES
+$(C_OBJ): %.o: %.c $(dep_files) FORCE
+	@set -e; echo CHECK $@; \
+	missing_deps="$(missing_deps)"; \
+	if test "$$missing_deps"; \
+	then \
+		echo missing dependencies: $$missing_deps; \
+		false; \
+	fi
+$(ASM_OBJ): %.o: %.S $(dep_files) FORCE
+	@set -e; echo CHECK $@; \
+	missing_deps="$(missing_deps)"; \
+	if test "$$missing_deps"; \
+	then \
+		echo missing dependencies: $$missing_deps; \
+		false; \
+	fi
+endif
+endif
+
+ifndef CHECK_HEADER_DEPENDENCIES
+$(C_OBJ): %.o: %.c GIT-CFLAGS $(missing_dep_dirs)
+	$(QUIET_CC)$(CC) -o $*.o -c $(dep_args) $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $<
+$(ASM_OBJ): %.o: %.S GIT-CFLAGS $(missing_dep_dirs)
+	$(QUIET_CC)$(CC) -o $*.o -c $(dep_args) $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $<
+endif
+
+%.s: %.c GIT-CFLAGS FORCE
+	$(QUIET_CC)$(CC) -o $@ -S $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $<
+
+ifdef USE_COMPUTED_HEADER_DEPENDENCIES
+# Take advantage of gcc's on-the-fly dependency generation
+# See <http://gcc.gnu.org/gcc-3.0/features.html>.
+dep_files_present := $(wildcard $(dep_files))
+ifneq ($(dep_files_present),)
+include $(dep_files_present)
+endif
+else
+# Dependencies on header files, for platforms that do not support
+# the gcc -MMD option.
+#
+# Dependencies on automatically generated headers such as common-cmds.h
+# should _not_ be included here, since they are necessary even when
+# building an object for the first time.
+
+$(OBJECTS): $(LIB_H)
+endif
+
+exec_cmd.sp exec_cmd.s exec_cmd.o: GIT-PREFIX
+exec_cmd.sp exec_cmd.s exec_cmd.o: EXTRA_CPPFLAGS = \
+	'-DGIT_EXEC_PATH="$(gitexecdir_SQ)"' \
+	'-DBINDIR="$(bindir_relative_SQ)"' \
+	'-DPREFIX="$(prefix_SQ)"'
+
+builtin/init-db.sp builtin/init-db.s builtin/init-db.o: GIT-PREFIX
+builtin/init-db.sp builtin/init-db.s builtin/init-db.o: EXTRA_CPPFLAGS = \
+	-DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"'
+
+config.sp config.s config.o: GIT-PREFIX
+config.sp config.s config.o: EXTRA_CPPFLAGS = \
+	-DETC_GITCONFIG='"$(ETC_GITCONFIG_SQ)"'
+
+attr.sp attr.s attr.o: GIT-PREFIX
+attr.sp attr.s attr.o: EXTRA_CPPFLAGS = \
+	-DETC_GITATTRIBUTES='"$(ETC_GITATTRIBUTES_SQ)"'
+
+gettext.sp gettext.s gettext.o: GIT-PREFIX
+gettext.sp gettext.s gettext.o: EXTRA_CPPFLAGS = \
+	-DGIT_LOCALE_PATH='"$(localedir_SQ)"'
+
+ifdef NO_EXPAT
+http-walker.sp http-walker.s http-walker.o: EXTRA_CPPFLAGS = -DNO_EXPAT
+endif
+
+ifdef NO_REGEX
+compat/regex/regex.sp compat/regex/regex.o: EXTRA_CPPFLAGS = \
+	-DGAWK -DNO_MBSUPPORT
+endif
+
+ifdef USE_NED_ALLOCATOR
+compat/nedmalloc/nedmalloc.sp compat/nedmalloc/nedmalloc.o: EXTRA_CPPFLAGS = \
+	-DNDEBUG -DOVERRIDE_STRDUP -DREPLACE_SYSTEM_ALLOCATOR
+endif
+
+git-%$X: %.o GIT-LDFLAGS $(GITLIBS)
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
 
-git-imap-send$X: imap-send.o $(GITLIBS)
+git-imap-send$X: imap-send.o GIT-LDFLAGS $(GITLIBS)
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
-		$(LIBS) $(OPENSSL_LINK) $(OPENSSL_LIBSSL)
+		$(LIBS) $(OPENSSL_LINK) $(OPENSSL_LIBSSL) $(LIB_4_CRYPTO)
 
-http.o http-walker.o http-push.o transport.o: http.h
-
-git-http-push$X: revision.o http.o http-push.o $(GITLIBS)
+git-http-fetch$X: revision.o http.o http-walker.o http-fetch.o GIT-LDFLAGS $(GITLIBS)
+	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
+		$(LIBS) $(CURL_LIBCURL)
+git-http-push$X: revision.o http.o http-push.o GIT-LDFLAGS $(GITLIBS)
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
 		$(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
 
-$(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)
-$(patsubst git-%$X,%.o,$(PROGRAMS)) git.o: $(LIB_H) $(wildcard */*.h)
-builtin-revert.o wt-status.o: wt-status.h
+$(REMOTE_CURL_ALIASES): $(REMOTE_CURL_PRIMARY)
+	$(QUIET_LNCP)$(RM) $@ && \
+	ln $< $@ 2>/dev/null || \
+	ln -s $< $@ 2>/dev/null || \
+	cp $< $@
+
+$(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o GIT-LDFLAGS $(GITLIBS)
+	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
+		$(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
 
 $(LIB_FILE): $(LIB_OBJS)
 	$(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS)
 
-XDIFF_OBJS=xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o \
-	xdiff/xmerge.o xdiff/xpatience.o
-$(XDIFF_OBJS): xdiff/xinclude.h xdiff/xmacros.h xdiff/xdiff.h xdiff/xtypes.h \
-	xdiff/xutils.h xdiff/xprepare.h xdiff/xdiffi.h xdiff/xemit.h
-
 $(XDIFF_LIB): $(XDIFF_OBJS)
 	$(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(XDIFF_OBJS)
 
+$(VCSSVN_LIB): $(VCSSVN_OBJS)
+	$(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(VCSSVN_OBJS)
+
+export DEFAULT_EDITOR DEFAULT_PAGER
 
 doc:
 	$(MAKE) -C Documentation all
@@ -1507,67 +2380,150 @@
 pdf:
 	$(MAKE) -C Documentation pdf
 
-TAGS:
-	$(RM) TAGS
-	$(FIND) . -name '*.[hcS]' -print | xargs etags -a
+XGETTEXT_FLAGS = \
+	--force-po \
+	--add-comments \
+	--msgid-bugs-address="Git Mailing List <git@vger.kernel.org>" \
+	--from-code=UTF-8
+XGETTEXT_FLAGS_C = $(XGETTEXT_FLAGS) --language=C \
+	--keyword=_ --keyword=N_ --keyword="Q_:1,2"
+XGETTEXT_FLAGS_SH = $(XGETTEXT_FLAGS) --language=Shell
+XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS) --keyword=__ --language=Perl
+LOCALIZED_C := $(C_OBJ:o=c) $(LIB_H) $(GENERATED_H)
+LOCALIZED_SH := $(SCRIPT_SH)
+LOCALIZED_PERL := $(SCRIPT_PERL)
 
-tags:
+ifdef XGETTEXT_INCLUDE_TESTS
+LOCALIZED_C += t/t0200/test.c
+LOCALIZED_SH += t/t0200/test.sh
+LOCALIZED_PERL += t/t0200/test.perl
+endif
+
+po/git.pot: $(LOCALIZED_C)
+	$(QUIET_XGETTEXT)$(XGETTEXT) -o$@+ $(XGETTEXT_FLAGS_C) $(LOCALIZED_C)
+	$(QUIET_XGETTEXT)$(XGETTEXT) -o$@+ --join-existing $(XGETTEXT_FLAGS_SH) \
+		$(LOCALIZED_SH)
+	$(QUIET_XGETTEXT)$(XGETTEXT) -o$@+ --join-existing $(XGETTEXT_FLAGS_PERL) \
+		$(LOCALIZED_PERL)
+	mv $@+ $@
+
+pot: po/git.pot
+
+POFILES := $(wildcard po/*.po)
+MOFILES := $(patsubst po/%.po,po/build/locale/%/LC_MESSAGES/git.mo,$(POFILES))
+
+ifndef NO_GETTEXT
+all:: $(MOFILES)
+endif
+
+po/build/locale/%/LC_MESSAGES/git.mo: po/%.po
+	$(QUIET_MSGFMT)mkdir -p $(dir $@) && $(MSGFMT) -o $@ $<
+
+FIND_SOURCE_FILES = ( git ls-files '*.[hcS]' 2>/dev/null || \
+			$(FIND) . \( -name .git -type d -prune \) \
+				-o \( -name '*.[hcS]' -type f -print \) )
+
+$(ETAGS_TARGET): FORCE
+	$(RM) $(ETAGS_TARGET)
+	$(FIND_SOURCE_FILES) | xargs etags -a -o $(ETAGS_TARGET)
+
+tags: FORCE
 	$(RM) tags
-	$(FIND) . -name '*.[hcS]' -print | xargs ctags -a
+	$(FIND_SOURCE_FILES) | xargs ctags -a
 
 cscope:
 	$(RM) cscope*
-	$(FIND) . -name '*.[hcS]' -print | xargs cscope -b
+	$(FIND_SOURCE_FILES) | xargs cscope -b
 
 ### Detect prefix changes
-TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\
-             $(bindir_SQ):$(gitexecdir_SQ):$(template_dir_SQ):$(prefix_SQ)
+TRACK_PREFIX = $(bindir_SQ):$(gitexecdir_SQ):$(template_dir_SQ):$(prefix_SQ):\
+		$(localedir_SQ)
 
-GIT-CFLAGS: .FORCE-GIT-CFLAGS
+GIT-PREFIX: FORCE
+	@FLAGS='$(TRACK_PREFIX)'; \
+	if test x"$$FLAGS" != x"`cat GIT-PREFIX 2>/dev/null`" ; then \
+		echo 1>&2 "    * new prefix flags"; \
+		echo "$$FLAGS" >GIT-PREFIX; \
+	fi
+
+TRACK_CFLAGS = $(CC):$(subst ','\'',$(ALL_CFLAGS)):$(USE_GETTEXT_SCHEME)
+
+GIT-CFLAGS: FORCE
 	@FLAGS='$(TRACK_CFLAGS)'; \
 	    if test x"$$FLAGS" != x"`cat GIT-CFLAGS 2>/dev/null`" ; then \
-		echo 1>&2 "    * new build flags or prefix"; \
+		echo 1>&2 "    * new build flags"; \
 		echo "$$FLAGS" >GIT-CFLAGS; \
             fi
 
+TRACK_LDFLAGS = $(subst ','\'',$(ALL_LDFLAGS))
+
+GIT-LDFLAGS: FORCE
+	@FLAGS='$(TRACK_LDFLAGS)'; \
+	    if test x"$$FLAGS" != x"`cat GIT-LDFLAGS 2>/dev/null`" ; then \
+		echo 1>&2 "    * new link flags"; \
+		echo "$$FLAGS" >GIT-LDFLAGS; \
+            fi
+
 # We need to apply sq twice, once to protect from the shell
 # that runs GIT-BUILD-OPTIONS, and then again to protect it
 # and the first level quoting from the shell that runs "echo".
-GIT-BUILD-OPTIONS: .FORCE-GIT-BUILD-OPTIONS
+GIT-BUILD-OPTIONS: FORCE
 	@echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@
+	@echo PERL_PATH=\''$(subst ','\'',$(PERL_PATH_SQ))'\' >>$@
+	@echo DIFF=\''$(subst ','\'',$(subst ','\'',$(DIFF)))'\' >>$@
+	@echo PYTHON_PATH=\''$(subst ','\'',$(PYTHON_PATH_SQ))'\' >>$@
 	@echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@
 	@echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@
+	@echo USE_LIBPCRE=\''$(subst ','\'',$(subst ','\'',$(USE_LIBPCRE)))'\' >>$@
 	@echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@
+	@echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@
+	@echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@
+ifdef GIT_TEST_OPTS
+	@echo GIT_TEST_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_OPTS)))'\' >>$@
+endif
+ifdef GIT_TEST_CMP
+	@echo GIT_TEST_CMP=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_CMP)))'\' >>$@
+endif
+ifdef GIT_TEST_CMP_USE_COPIED_CONTEXT
+	@echo GIT_TEST_CMP_USE_COPIED_CONTEXT=YesPlease >>$@
+endif
+	@echo NO_GETTEXT=\''$(subst ','\'',$(subst ','\'',$(NO_GETTEXT)))'\' >>$@
+	@echo GETTEXT_POISON=\''$(subst ','\'',$(subst ','\'',$(GETTEXT_POISON)))'\' >>$@
+ifdef GIT_PERF_REPEAT_COUNT
+	@echo GIT_PERF_REPEAT_COUNT=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_REPEAT_COUNT)))'\' >>$@
+endif
+ifdef GIT_PERF_REPO
+	@echo GIT_PERF_REPO=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_REPO)))'\' >>$@
+endif
+ifdef GIT_PERF_LARGE_REPO
+	@echo GIT_PERF_LARGE_REPO=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_LARGE_REPO)))'\' >>$@
+endif
+ifdef GIT_PERF_MAKE_OPTS
+	@echo GIT_PERF_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_MAKE_OPTS)))'\' >>$@
+endif
 
 ### Detect Tck/Tk interpreter path changes
 ifndef NO_TCLTK
 TRACK_VARS = $(subst ','\'',-DTCLTK_PATH='$(TCLTK_PATH_SQ)')
 
-GIT-GUI-VARS: .FORCE-GIT-GUI-VARS
+GIT-GUI-VARS: FORCE
 	@VARS='$(TRACK_VARS)'; \
 	    if test x"$$VARS" != x"`cat $@ 2>/dev/null`" ; then \
 		echo 1>&2 "    * new Tcl/Tk interpreter location"; \
 		echo "$$VARS" >$@; \
             fi
-
-.PHONY: .FORCE-GIT-GUI-VARS
 endif
 
-### Testing rules
+test_bindir_programs := $(patsubst %,bin-wrappers/%,$(BINDIR_PROGRAMS_NEED_X) $(BINDIR_PROGRAMS_NO_X) $(TEST_PROGRAMS_NEED_X))
 
-TEST_PROGRAMS += test-chmtime$X
-TEST_PROGRAMS += test-ctype$X
-TEST_PROGRAMS += test-date$X
-TEST_PROGRAMS += test-delta$X
-TEST_PROGRAMS += test-dump-cache-tree$X
-TEST_PROGRAMS += test-genrandom$X
-TEST_PROGRAMS += test-match-trees$X
-TEST_PROGRAMS += test-parse-options$X
-TEST_PROGRAMS += test-path-utils$X
-TEST_PROGRAMS += test-sha1$X
-TEST_PROGRAMS += test-sigchain$X
+all:: $(TEST_PROGRAMS) $(test_bindir_programs)
 
-all:: $(TEST_PROGRAMS)
+bin-wrappers/%: wrap-for-bin.sh
+	@mkdir -p bin-wrappers
+	$(QUIET_GEN)sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
+	     -e 's|@@BUILD_DIR@@|$(shell pwd)|' \
+	     -e 's|@@PROG@@|$(@F)|' < $< > $@ && \
+	chmod +x $@
 
 # GNU make supports exporting all variables by "export" without parameters.
 # However, the environment gets quite big, and some programs have problems
@@ -1575,34 +2531,50 @@
 
 export NO_SVN_TESTS
 
+### Testing rules
+
 test: all
 	$(MAKE) -C t/ all
 
+perf: all
+	$(MAKE) -C t/perf/ all
+
+.PHONY: test perf
+
 test-ctype$X: ctype.o
 
 test-date$X: date.o ctype.o
 
 test-delta$X: diff-delta.o patch-delta.o
 
-test-parse-options$X: parse-options.o
+test-line-buffer$X: vcs-svn/lib.a
 
-test-parse-options.o: parse-options.h
+test-parse-options$X: parse-options.o parse-options-cb.o
 
-.PRECIOUS: $(patsubst test-%$X,test-%.o,$(TEST_PROGRAMS))
+test-svn-fe$X: vcs-svn/lib.a
 
-test-%$X: test-%.o $(GITLIBS)
-	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
+.PRECIOUS: $(TEST_OBJS)
+
+test-%$X: test-%.o GIT-LDFLAGS $(GITLIBS)
+	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS)
 
 check-sha1:: test-sha1$X
 	./test-sha1.sh
 
+SP_OBJ = $(patsubst %.o,%.sp,$(C_OBJ))
+
+$(SP_OBJ): %.sp: %.c GIT-CFLAGS FORCE
+	$(QUIET_SP)cgcc -no-compile $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) \
+		$(SPARSE_FLAGS) $<
+
+.PHONY: sparse $(SP_OBJ)
+sparse: $(SP_OBJ)
+
 check: common-cmds.h
-	if sparse; \
+	@if sparse; \
 	then \
-		for i in *.c; \
-		do \
-			sparse $(ALL_CFLAGS) $(SPARSE_FLAGS) $$i || exit; \
-		done; \
+		echo 2>&1 "Use 'make sparse' instead"; \
+		$(MAKE) --no-print-directory sparse; \
 	else \
 		echo 2>&1 "Did you mean 'make test'?"; \
 		exit 1; \
@@ -1628,14 +2600,35 @@
 gitexec_instdir_SQ = $(subst ','\'',$(gitexec_instdir))
 export gitexec_instdir
 
+ifneq ($(filter /%,$(firstword $(mergetoolsdir))),)
+mergetools_instdir = $(mergetoolsdir)
+else
+mergetools_instdir = $(prefix)/$(mergetoolsdir)
+endif
+mergetools_instdir_SQ = $(subst ','\'',$(mergetools_instdir))
+
+install_bindir_programs := $(patsubst %,%$X,$(BINDIR_PROGRAMS_NEED_X)) $(BINDIR_PROGRAMS_NO_X)
+
 install: all
 	$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'
 	$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 	$(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
-	$(INSTALL) git$X git-upload-pack$X git-receive-pack$X git-upload-archive$X git-shell$X git-cvsserver '$(DESTDIR_SQ)$(bindir_SQ)'
+	$(INSTALL) -m 644 $(SCRIPT_LIB) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
+	$(INSTALL) $(install_bindir_programs) '$(DESTDIR_SQ)$(bindir_SQ)'
 	$(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
+	$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(mergetools_instdir_SQ)'
+	$(INSTALL) -m 644 mergetools/* '$(DESTDIR_SQ)$(mergetools_instdir_SQ)'
+ifndef NO_GETTEXT
+	$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(localedir_SQ)'
+	(cd po/build/locale && $(TAR) cf - .) | \
+	(cd '$(DESTDIR_SQ)$(localedir_SQ)' && umask 022 && $(TAR) xof -)
+endif
 ifndef NO_PERL
 	$(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
+	$(MAKE) -C gitweb install
+endif
+ifndef NO_PYTHON
+	$(MAKE) -C git_remote_helpers prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
 endif
 ifndef NO_TCLTK
 	$(MAKE) -C gitk-git install
@@ -1644,21 +2637,44 @@
 ifneq (,$X)
 	$(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), test '$(DESTDIR_SQ)$(gitexec_instdir_SQ)/$p' -ef '$(DESTDIR_SQ)$(gitexec_instdir_SQ)/$p$X' || $(RM) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)/$p';)
 endif
+
 	bindir=$$(cd '$(DESTDIR_SQ)$(bindir_SQ)' && pwd) && \
 	execdir=$$(cd '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' && pwd) && \
 	{ test "$$bindir/" = "$$execdir/" || \
-		{ $(RM) "$$execdir/git$X" && \
-		test -z "$(NO_CROSS_DIRECTORY_HARDLINKS)" && \
-		ln "$$bindir/git$X" "$$execdir/git$X" 2>/dev/null || \
-		cp "$$bindir/git$X" "$$execdir/git$X"; } ; } && \
-	{ for p in $(BUILT_INS); do \
+	  for p in git$X $(filter $(install_bindir_programs),$(ALL_PROGRAMS)); do \
 		$(RM) "$$execdir/$$p" && \
+		test -z "$(NO_INSTALL_HARDLINKS)$(NO_CROSS_DIRECTORY_HARDLINKS)" && \
+		ln "$$bindir/$$p" "$$execdir/$$p" 2>/dev/null || \
+		cp "$$bindir/$$p" "$$execdir/$$p" || exit; \
+	  done; \
+	} && \
+	for p in $(filter $(install_bindir_programs),$(BUILT_INS)); do \
+		$(RM) "$$bindir/$$p" && \
+		test -z "$(NO_INSTALL_HARDLINKS)" && \
+		ln "$$bindir/git$X" "$$bindir/$$p" 2>/dev/null || \
+		ln -s "git$X" "$$bindir/$$p" 2>/dev/null || \
+		cp "$$bindir/git$X" "$$bindir/$$p" || exit; \
+	done && \
+	for p in $(BUILT_INS); do \
+		$(RM) "$$execdir/$$p" && \
+		test -z "$(NO_INSTALL_HARDLINKS)" && \
 		ln "$$execdir/git$X" "$$execdir/$$p" 2>/dev/null || \
 		ln -s "git$X" "$$execdir/$$p" 2>/dev/null || \
 		cp "$$execdir/git$X" "$$execdir/$$p" || exit; \
-	  done; } && \
+	done && \
+	remote_curl_aliases="$(REMOTE_CURL_ALIASES)" && \
+	for p in $$remote_curl_aliases; do \
+		$(RM) "$$execdir/$$p" && \
+		test -z "$(NO_INSTALL_HARDLINKS)" && \
+		ln "$$execdir/git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \
+		ln -s "git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \
+		cp "$$execdir/git-remote-http$X" "$$execdir/$$p" || exit; \
+	done && \
 	./check_bindir "z$$bindir" "z$$execdir" "$$bindir/git-add$X"
 
+install-gitweb:
+	$(MAKE) -C gitweb install
+
 install-doc:
 	$(MAKE) -C Documentation install
 
@@ -1687,7 +2703,7 @@
 
 ### Maintainer's dist rules
 
-git.spec: git.spec.in
+git.spec: git.spec.in GIT-VERSION-FILE
 	sed -e 's/@@VERSION@@/$(GIT_VERSION)/g' < $< > $@+
 	mv $@+ $@
 
@@ -1708,7 +2724,10 @@
 	gzip -f -9 $(GIT_TARNAME).tar
 
 rpm: dist
-	$(RPMBUILD) -ta $(GIT_TARNAME).tar.gz
+	$(RPMBUILD) \
+		--define "_source_filedigest_algorithm md5" \
+		--define "_binary_filedigest_algorithm md5" \
+		-ta $(GIT_TARNAME).tar.gz
 
 htmldocs = git-htmldocs-$(GIT_VERSION)
 manpages = git-manpages-$(GIT_VERSION)
@@ -1734,45 +2753,57 @@
 
 distclean: clean
 	$(RM) configure
-
-clean:
-	$(RM) *.o mozilla-sha1/*.o arm/*.o ppc/*.o compat/*.o compat/*/*.o xdiff/*.o \
-		$(LIB_FILE) $(XDIFF_LIB)
-	$(RM) $(ALL_PROGRAMS) $(BUILT_INS) git$X
-	$(RM) $(TEST_PROGRAMS)
-	$(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags cscope*
+	$(RM) config.log config.status config.cache
+	$(RM) config.mak.autogen config.mak.append
 	$(RM) -r autom4te.cache
-	$(RM) config.log config.mak.autogen config.mak.append config.status config.cache
+
+profile-clean:
+	$(RM) $(addsuffix *.gcda,$(addprefix $(PROFILE_DIR)/, $(object_dirs)))
+	$(RM) $(addsuffix *.gcno,$(addprefix $(PROFILE_DIR)/, $(object_dirs)))
+
+clean: profile-clean
+	$(RM) *.o block-sha1/*.o ppc/*.o compat/*.o compat/*/*.o xdiff/*.o vcs-svn/*.o \
+		builtin/*.o $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB)
+	$(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X
+	$(RM) $(TEST_PROGRAMS)
+	$(RM) -r bin-wrappers
+	$(RM) -r $(dep_dirs)
+	$(RM) -r po/build/
+	$(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h $(ETAGS_TARGET) tags cscope*
 	$(RM) -r $(GIT_TARNAME) .doc-tmp-dir
 	$(RM) $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
 	$(RM) $(htmldocs).tar.gz $(manpages).tar.gz
 	$(MAKE) -C Documentation/ clean
 ifndef NO_PERL
-	$(RM) gitweb/gitweb.cgi
+	$(MAKE) -C gitweb clean
 	$(MAKE) -C perl clean
 endif
+ifndef NO_PYTHON
+	$(MAKE) -C git_remote_helpers clean
+endif
 	$(MAKE) -C templates/ clean
 	$(MAKE) -C t/ clean
 ifndef NO_TCLTK
 	$(MAKE) -C gitk-git clean
 	$(MAKE) -C git-gui clean
 endif
-	$(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-GUI-VARS GIT-BUILD-OPTIONS
+	$(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-LDFLAGS GIT-GUI-VARS GIT-BUILD-OPTIONS
+	$(RM) GIT-USER-AGENT GIT-PREFIX GIT-SCRIPT-DEFINES
 
-.PHONY: all install clean strip
+.PHONY: all install profile-clean clean strip
 .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell
-.PHONY: .FORCE-GIT-VERSION-FILE TAGS tags cscope .FORCE-GIT-CFLAGS
-.PHONY: .FORCE-GIT-BUILD-OPTIONS
+.PHONY: FORCE cscope
 
 ### Check documentation
 #
 check-docs::
-	@(for v in $(ALL_PROGRAMS) $(BUILT_INS) git gitk; \
+	@(for v in $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git gitk; \
 	do \
 		case "$$v" in \
 		git-merge-octopus | git-merge-ours | git-merge-recursive | \
 		git-merge-resolve | git-merge-subtree | \
 		git-fsck-objects | git-init-db | \
+		git-remote-* | git-stage | \
 		git-?*--?* ) continue ;; \
 		esac ; \
 		test -f "Documentation/$$v.txt" || \
@@ -1808,11 +2839,15 @@
 		documented,gitglossary | \
 		documented,githooks | \
 		documented,gitrepository-layout | \
+		documented,gitrevisions | \
 		documented,gittutorial | \
 		documented,gittutorial-2 | \
+		documented,git-bisect-lk2009 | \
+		documented,git-remote-helpers | \
+		documented,gitworkflows | \
 		sentinel,not,matching,is,ok ) continue ;; \
 		esac; \
-		case " $(ALL_PROGRAMS) $(BUILT_INS) git gitk " in \
+		case " $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git gitk " in \
 		*" $$cmd "*)	;; \
 		*) echo "removed but $$how: $$cmd" ;; \
 		esac; \
@@ -1831,11 +2866,18 @@
 	$(MAKE) coverage-build
 	$(MAKE) coverage-report
 
+object_dirs := $(sort $(dir $(OBJECTS)))
 coverage-clean:
-	rm -f *.gcda *.gcno
+	$(RM) $(addsuffix *.gcov,$(object_dirs))
+	$(RM) $(addsuffix *.gcda,$(object_dirs))
+	$(RM) $(addsuffix *.gcno,$(object_dirs))
+	$(RM) coverage-untested-functions
+	$(RM) -r cover_db/
+	$(RM) -r cover_db_html/
 
 COVERAGE_CFLAGS = $(CFLAGS) -O0 -ftest-coverage -fprofile-arcs
 COVERAGE_LDFLAGS = $(CFLAGS)  -O0 -lgcov
+GCOVFLAGS = --preserve-paths --branch-probabilities --all-blocks
 
 coverage-build: coverage-clean
 	$(MAKE) CFLAGS="$(COVERAGE_CFLAGS)" LDFLAGS="$(COVERAGE_LDFLAGS)" all
@@ -1843,7 +2885,18 @@
 		-j1 test
 
 coverage-report:
-	gcov -b *.c
+	$(QUIET_GCOV)for dir in $(object_dirs); do \
+		$(GCOV) $(GCOVFLAGS) --object-directory=$$dir $$dir*.c || exit; \
+	done
+
+coverage-untested-functions: coverage-report
 	grep '^function.*called 0 ' *.c.gcov \
 		| sed -e 's/\([^:]*\)\.gcov: *function \([^ ]*\) called.*/\1: \2/' \
-		| tee coverage-untested-functions
+		> coverage-untested-functions
+
+cover_db: coverage-report
+	gcov2perl -db cover_db *.gcov
+
+cover_db_html: cover_db
+	cover -report html -outputdir cover_db_html cover_db
+
diff --git a/README b/README
index c932ab3..d2690ec 100644
--- a/README
+++ b/README
@@ -37,15 +37,17 @@
 ("man gitcvs-migration" or "git help cvs-migration" if git is
 installed).
 
-Many Git online resources are accessible from http://git.or.cz/
+Many Git online resources are accessible from http://git-scm.com/
 including full documentation and Git related tools.
 
 The user discussion and development of Git take place on the Git
 mailing list -- everyone is welcome to post bug reports, feature
-requests, comments and patches to git@vger.kernel.org. To subscribe
-to the list, send an email with just "subscribe git" in the body to
-majordomo@vger.kernel.org. The mailing list archives are available at
-http://marc.theaimsgroup.com/?l=git and other archival sites.
+requests, comments and patches to git@vger.kernel.org (read
+Documentation/SubmittingPatches for instructions on patch submission).
+To subscribe to the list, send an email with just "subscribe git" in
+the body to majordomo@vger.kernel.org. The mailing list archives are
+available at http://marc.theaimsgroup.com/?l=git and other archival
+sites.
 
 The messages titled "A note from the maintainer", "What's in
 git.git (stable)" and "What's cooking in git.git (topics)" and
diff --git a/RelNotes b/RelNotes
index b62449d..19bb2eb 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes-1.6.5.txt
\ No newline at end of file
+Documentation/RelNotes/1.7.12.txt
\ No newline at end of file
diff --git a/abspath.c b/abspath.c
index 4bee0ba..f04ac18 100644
--- a/abspath.c
+++ b/abspath.c
@@ -14,22 +14,33 @@
 /* We allow "recursive" symbolic links. Only within reason, though. */
 #define MAXDEPTH 5
 
-const char *make_absolute_path(const char *path)
+/*
+ * Use this to get the real path, i.e. resolve links. If you want an
+ * absolute path but don't mind links, use absolute_path.
+ *
+ * If path is our buffer, then return path, as it's already what the
+ * user wants.
+ */
+const char *real_path(const char *path)
 {
 	static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1];
 	char cwd[1024] = "";
-	int buf_index = 1, len;
+	int buf_index = 1;
 
 	int depth = MAXDEPTH;
 	char *last_elem = NULL;
 	struct stat st;
 
+	/* We've already done it */
+	if (path == buf || path == next_buf)
+		return path;
+
 	if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
 		die ("Too long path: %.*s", 60, path);
 
 	while (depth--) {
 		if (!is_directory(buf)) {
-			char *last_slash = strrchr(buf, '/');
+			char *last_slash = find_last_dir_sep(buf);
 			if (last_slash) {
 				*last_slash = '\0';
 				last_elem = xstrdup(last_slash + 1);
@@ -50,18 +61,19 @@
 			die_errno ("Could not get current working directory");
 
 		if (last_elem) {
-			int len = strlen(buf);
+			size_t len = strlen(buf);
 			if (len + strlen(last_elem) + 2 > PATH_MAX)
 				die ("Too long path name: '%s/%s'",
 						buf, last_elem);
-			buf[len] = '/';
-			strcpy(buf + len + 1, last_elem);
+			if (len && !is_dir_sep(buf[len-1]))
+				buf[len++] = '/';
+			strcpy(buf + len, last_elem);
 			free(last_elem);
 			last_elem = NULL;
 		}
 
 		if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) {
-			len = readlink(buf, next_buf, PATH_MAX);
+			ssize_t len = readlink(buf, next_buf, PATH_MAX);
 			if (len < 0)
 				die_errno ("Invalid symlink '%s'", buf);
 			if (PATH_MAX <= len)
@@ -90,7 +102,8 @@
 	pwd = getenv("PWD");
 	if (pwd && strcmp(pwd, cwd)) {
 		stat(cwd, &cwd_stat);
-		if (!stat(pwd, &pwd_stat) &&
+		if ((cwd_stat.st_dev || cwd_stat.st_ino) &&
+		    !stat(pwd, &pwd_stat) &&
 		    pwd_stat.st_dev == cwd_stat.st_dev &&
 		    pwd_stat.st_ino == cwd_stat.st_ino) {
 			strlcpy(cwd, pwd, PATH_MAX);
@@ -99,7 +112,14 @@
 	return cwd;
 }
 
-const char *make_nonrelative_path(const char *path)
+/*
+ * Use this to get an absolute path from a relative one. If you want
+ * to resolve links, you should use real_path.
+ *
+ * If the path is already absolute, then return path. As the user is
+ * never meant to free the return value, we're safe.
+ */
+const char *absolute_path(const char *path)
 {
 	static char buf[PATH_MAX + 1];
 
@@ -107,11 +127,43 @@
 		if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
 			die("Too long path: %.*s", 60, path);
 	} else {
+		size_t len;
+		const char *fmt;
 		const char *cwd = get_pwd_cwd();
 		if (!cwd)
 			die_errno("Cannot determine the current working directory");
-		if (snprintf(buf, PATH_MAX, "%s/%s", cwd, path) >= PATH_MAX)
+		len = strlen(cwd);
+		fmt = (len > 0 && is_dir_sep(cwd[len-1])) ? "%s%s" : "%s/%s";
+		if (snprintf(buf, PATH_MAX, fmt, cwd, path) >= PATH_MAX)
 			die("Too long path: %.*s", 60, path);
 	}
 	return buf;
 }
+
+/*
+ * Unlike prefix_path, this should be used if the named file does
+ * not have to interact with index entry; i.e. name of a random file
+ * on the filesystem.
+ */
+const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
+{
+	static char path[PATH_MAX];
+#ifndef WIN32
+	if (!pfx_len || is_absolute_path(arg))
+		return arg;
+	memcpy(path, pfx, pfx_len);
+	strcpy(path + pfx_len, arg);
+#else
+	char *p;
+	/* don't add prefix to absolute paths, but still replace '\' by '/' */
+	if (is_absolute_path(arg))
+		pfx_len = 0;
+	else if (pfx_len)
+		memcpy(path, pfx, pfx_len);
+	strcpy(path + pfx_len, arg);
+	for (p = path + pfx_len; *p; p++)
+		if (*p == '\\')
+			*p = '/';
+#endif
+	return path;
+}
diff --git a/aclocal.m4 b/aclocal.m4
new file mode 100644
index 0000000..f11bc7e
--- /dev/null
+++ b/aclocal.m4
@@ -0,0 +1,40 @@
+dnl Check for socklen_t: historically on BSD it is an int, and in
+dnl POSIX 1g it is a type of its own, but some platforms use different
+dnl types for the argument to getsockopt, getpeername, etc.  So we
+dnl have to test to find something that will work.
+AC_DEFUN([TYPE_SOCKLEN_T],
+[
+   AC_CHECK_TYPE([socklen_t], ,[
+      AC_MSG_CHECKING([for socklen_t equivalent])
+      AC_CACHE_VAL([git_cv_socklen_t_equiv],
+      [
+         # Systems have either "struct sockaddr *" or
+         # "void *" as the second argument to getpeername
+         git_cv_socklen_t_equiv=
+         for arg2 in "struct sockaddr" void; do
+            for t in int size_t unsigned long "unsigned long"; do
+               AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
+                  #include <sys/types.h>
+                  #include <sys/socket.h>
+
+                  int getpeername (int, $arg2 *, $t *);
+               ],[
+                  $t len;
+                  getpeername(0,0,&len);
+               ])],[
+                  git_cv_socklen_t_equiv="$t"
+                  break 2
+               ])
+            done
+         done
+
+         if test "x$git_cv_socklen_t_equiv" = x; then
+            AC_MSG_ERROR([Cannot find a type to use in place of socklen_t])
+         fi
+      ])
+      AC_MSG_RESULT($git_cv_socklen_t_equiv)
+      AC_DEFINE_UNQUOTED(socklen_t, $git_cv_socklen_t_equiv,
+			[type to use in place of socklen_t if not defined])],
+      [#include <sys/types.h>
+#include <sys/socket.h>])
+])
diff --git a/advice.c b/advice.c
new file mode 100644
index 0000000..a492eea
--- /dev/null
+++ b/advice.c
@@ -0,0 +1,95 @@
+#include "cache.h"
+
+int advice_push_nonfastforward = 1;
+int advice_push_non_ff_current = 1;
+int advice_push_non_ff_default = 1;
+int advice_push_non_ff_matching = 1;
+int advice_status_hints = 1;
+int advice_commit_before_merge = 1;
+int advice_resolve_conflict = 1;
+int advice_implicit_identity = 1;
+int advice_detached_head = 1;
+
+static struct {
+	const char *name;
+	int *preference;
+} advice_config[] = {
+	{ "pushnonfastforward", &advice_push_nonfastforward },
+	{ "pushnonffcurrent", &advice_push_non_ff_current },
+	{ "pushnonffdefault", &advice_push_non_ff_default },
+	{ "pushnonffmatching", &advice_push_non_ff_matching },
+	{ "statushints", &advice_status_hints },
+	{ "commitbeforemerge", &advice_commit_before_merge },
+	{ "resolveconflict", &advice_resolve_conflict },
+	{ "implicitidentity", &advice_implicit_identity },
+	{ "detachedhead", &advice_detached_head },
+};
+
+void advise(const char *advice, ...)
+{
+	struct strbuf buf = STRBUF_INIT;
+	va_list params;
+	const char *cp, *np;
+
+	va_start(params, advice);
+	strbuf_addf(&buf, advice, params);
+	va_end(params);
+
+	for (cp = buf.buf; *cp; cp = np) {
+		np = strchrnul(cp, '\n');
+		fprintf(stderr,	_("hint: %.*s\n"), (int)(np - cp), cp);
+		if (*np)
+			np++;
+	}
+	strbuf_release(&buf);
+}
+
+int git_default_advice_config(const char *var, const char *value)
+{
+	const char *k = skip_prefix(var, "advice.");
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(advice_config); i++) {
+		if (strcmp(k, advice_config[i].name))
+			continue;
+		*advice_config[i].preference = git_config_bool(var, value);
+		return 0;
+	}
+
+	return 0;
+}
+
+int error_resolve_conflict(const char *me)
+{
+	error("'%s' is not possible because you have unmerged files.", me);
+	if (advice_resolve_conflict)
+		/*
+		 * Message used both when 'git commit' fails and when
+		 * other commands doing a merge do.
+		 */
+		advise(_("Fix them up in the work tree,\n"
+			 "and then use 'git add/rm <file>' as\n"
+			 "appropriate to mark resolution and make a commit,\n"
+			 "or use 'git commit -a'."));
+	return -1;
+}
+
+void NORETURN die_resolve_conflict(const char *me)
+{
+	error_resolve_conflict(me);
+	die("Exiting because of an unresolved conflict.");
+}
+
+void detach_advice(const char *new_name)
+{
+	const char fmt[] =
+	"Note: checking out '%s'.\n\n"
+	"You are in 'detached HEAD' state. You can look around, make experimental\n"
+	"changes and commit them, and you can discard any commits you make in this\n"
+	"state without impacting any branches by performing another checkout.\n\n"
+	"If you want to create a new branch to retain commits you create, you may\n"
+	"do so (now or later) by using -b with the checkout command again. Example:\n\n"
+	"  git checkout -b new_branch_name\n\n";
+
+	fprintf(stderr, fmt, new_name);
+}
diff --git a/advice.h b/advice.h
new file mode 100644
index 0000000..f3cdbbf
--- /dev/null
+++ b/advice.h
@@ -0,0 +1,22 @@
+#ifndef ADVICE_H
+#define ADVICE_H
+
+#include "git-compat-util.h"
+
+extern int advice_push_nonfastforward;
+extern int advice_push_non_ff_current;
+extern int advice_push_non_ff_default;
+extern int advice_push_non_ff_matching;
+extern int advice_status_hints;
+extern int advice_commit_before_merge;
+extern int advice_resolve_conflict;
+extern int advice_implicit_identity;
+extern int advice_detached_head;
+
+int git_default_advice_config(const char *var, const char *value);
+void advise(const char *advice, ...);
+int error_resolve_conflict(const char *me);
+extern void NORETURN die_resolve_conflict(const char *me);
+void detach_advice(const char *new_name);
+
+#endif /* ADVICE_H */
diff --git a/alias.c b/alias.c
index 372b7d8..eb9f08b 100644
--- a/alias.c
+++ b/alias.c
@@ -22,6 +22,13 @@
 	return alias_val;
 }
 
+#define SPLIT_CMDLINE_BAD_ENDING 1
+#define SPLIT_CMDLINE_UNCLOSED_QUOTE 2
+static const char *split_cmdline_errors[] = {
+	"cmdline ends with \\",
+	"unclosed quote"
+};
+
 int split_cmdline(char *cmdline, const char ***argv)
 {
 	int src, dst, count = 0, size = 16;
@@ -53,7 +60,7 @@
 				if (!c) {
 					free(*argv);
 					*argv = NULL;
-					return error("cmdline ends with \\");
+					return -SPLIT_CMDLINE_BAD_ENDING;
 				}
 			}
 			cmdline[dst++] = c;
@@ -66,7 +73,7 @@
 	if (quoted) {
 		free(*argv);
 		*argv = NULL;
-		return error("unclosed quote");
+		return -SPLIT_CMDLINE_UNCLOSED_QUOTE;
 	}
 
 	ALLOC_GROW(*argv, count+1, size);
@@ -75,3 +82,6 @@
 	return count;
 }
 
+const char *split_cmdline_strerror(int split_cmdline_errno) {
+	return split_cmdline_errors[-split_cmdline_errno-1];
+}
diff --git a/alloc.c b/alloc.c
index 6ef6753..aeae55c 100644
--- a/alloc.c
+++ b/alloc.c
@@ -51,19 +51,12 @@
 DEFINE_ALLOCATOR(tag, struct tag)
 DEFINE_ALLOCATOR(object, union any_object)
 
-#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);
+	fprintf(stderr, "%10s: %8u (%"PRIuMAX" kB)\n",
+			name, count, (uintmax_t) size);
 }
 
-#undef SZ_FMT
-
 #define REPORT(name)	\
     report(#name, name##_allocs, name##_allocs*sizeof(struct name) >> 10)
 
diff --git a/archive-tar.c b/archive-tar.c
index cee06ce..0ba3f25 100644
--- a/archive-tar.c
+++ b/archive-tar.c
@@ -4,6 +4,8 @@
 #include "cache.h"
 #include "tar.h"
 #include "archive.h"
+#include "streaming.h"
+#include "run-command.h"
 
 #define RECORDSIZE	(512)
 #define BLOCKSIZE	(RECORDSIZE * 20)
@@ -13,6 +15,9 @@
 
 static int tar_umask = 002;
 
+static int write_tar_filter_archive(const struct archiver *ar,
+				    struct archiver_args *args);
+
 /* writes out the whole block, but only if it is full */
 static void write_if_needed(void)
 {
@@ -26,10 +31,9 @@
  * queues up writes, so that all our write(2) calls write exactly one
  * full block; pads writes to RECORDSIZE
  */
-static void write_blocked(const void *data, unsigned long size)
+static void do_write_blocked(const void *data, unsigned long size)
 {
 	const char *buf = data;
-	unsigned long tail;
 
 	if (offset) {
 		unsigned long chunk = BLOCKSIZE - offset;
@@ -50,6 +54,11 @@
 		memcpy(block + offset, buf, size);
 		offset += size;
 	}
+}
+
+static void finish_record(void)
+{
+	unsigned long tail;
 	tail = offset % RECORDSIZE;
 	if (tail)  {
 		memset(block + offset, 0, RECORDSIZE - tail);
@@ -58,6 +67,12 @@
 	write_if_needed();
 }
 
+static void write_blocked(const void *data, unsigned long size)
+{
+	do_write_blocked(data, size);
+	finish_record();
+}
+
 /*
  * The end of tar archives is marked by 2*512 nul bytes and after that
  * follows the rest of the block (if any).
@@ -74,6 +89,33 @@
 }
 
 /*
+ * queues up writes, so that all our write(2) calls write exactly one
+ * full block; pads writes to RECORDSIZE
+ */
+static int stream_blocked(const unsigned char *sha1)
+{
+	struct git_istream *st;
+	enum object_type type;
+	unsigned long sz;
+	char buf[BLOCKSIZE];
+	ssize_t readlen;
+
+	st = open_istream(sha1, &type, &sz, NULL);
+	if (!st)
+		return error("cannot stream blob %s", sha1_to_hex(sha1));
+	for (;;) {
+		readlen = read_istream(st, buf, sizeof(buf));
+		if (readlen <= 0)
+			break;
+		do_write_blocked(buf, readlen);
+	}
+	close_istream(st);
+	if (!readlen)
+		finish_record();
+	return readlen;
+}
+
+/*
  * pax extended header records have the format "%u %s=%s\n".  %u contains
  * the size of the whole string (including the %u), the first %s is the
  * keyword, the second one is the value.  This function constructs such a
@@ -97,13 +139,13 @@
 
 static unsigned int ustar_header_chksum(const struct ustar_header *header)
 {
-	char *p = (char *)header;
+	const unsigned char *p = (const unsigned char *)header;
 	unsigned int chksum = 0;
-	while (p < header->chksum)
+	while (p < (const unsigned char *)header->chksum)
 		chksum += *p++;
 	chksum += sizeof(header->chksum) * ' ';
 	p += sizeof(header->chksum);
-	while (p < (char *)header + sizeof(struct ustar_header))
+	while (p < (const unsigned char *)header + sizeof(struct ustar_header))
 		chksum += *p++;
 	return chksum;
 }
@@ -119,56 +161,101 @@
 	return i;
 }
 
+static void prepare_header(struct archiver_args *args,
+			   struct ustar_header *header,
+			   unsigned int mode, unsigned long size)
+{
+	sprintf(header->mode, "%07o", mode & 07777);
+	sprintf(header->size, "%011lo", S_ISREG(mode) ? size : 0);
+	sprintf(header->mtime, "%011lo", (unsigned long) args->time);
+
+	sprintf(header->uid, "%07o", 0);
+	sprintf(header->gid, "%07o", 0);
+	strlcpy(header->uname, "root", sizeof(header->uname));
+	strlcpy(header->gname, "root", sizeof(header->gname));
+	sprintf(header->devmajor, "%07o", 0);
+	sprintf(header->devminor, "%07o", 0);
+
+	memcpy(header->magic, "ustar", 6);
+	memcpy(header->version, "00", 2);
+
+	sprintf(header->chksum, "%07o", ustar_header_chksum(header));
+}
+
+static int write_extended_header(struct archiver_args *args,
+				 const unsigned char *sha1,
+				 const void *buffer, unsigned long size)
+{
+	struct ustar_header header;
+	unsigned int mode;
+	memset(&header, 0, sizeof(header));
+	*header.typeflag = TYPEFLAG_EXT_HEADER;
+	mode = 0100666;
+	sprintf(header.name, "%s.paxheader", sha1_to_hex(sha1));
+	prepare_header(args, &header, mode, size);
+	write_blocked(&header, sizeof(header));
+	write_blocked(buffer, size);
+	return 0;
+}
+
 static int write_tar_entry(struct archiver_args *args,
-		const unsigned char *sha1, const char *path, size_t pathlen,
-		unsigned int mode, void *buffer, unsigned long size)
+			   const unsigned char *sha1,
+			   const char *path, size_t pathlen,
+			   unsigned int mode)
 {
 	struct ustar_header header;
 	struct strbuf ext_header = STRBUF_INIT;
+	unsigned int old_mode = mode;
+	unsigned long size;
+	void *buffer;
 	int err = 0;
 
 	memset(&header, 0, sizeof(header));
 
-	if (!sha1) {
-		*header.typeflag = TYPEFLAG_GLOBAL_HEADER;
-		mode = 0100666;
-		strcpy(header.name, "pax_global_header");
-	} else if (!path) {
-		*header.typeflag = TYPEFLAG_EXT_HEADER;
-		mode = 0100666;
-		sprintf(header.name, "%s.paxheader", sha1_to_hex(sha1));
+	if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
+		*header.typeflag = TYPEFLAG_DIR;
+		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 | ((mode & 0100) ? 0777 : 0666)) & ~tar_umask;
 	} else {
-		if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
-			*header.typeflag = TYPEFLAG_DIR;
-			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 | ((mode & 0100) ? 0777 : 0666)) & ~tar_umask;
-		} else {
-			return error("unsupported file mode: 0%o (SHA1: %s)",
-					mode, sha1_to_hex(sha1));
-		}
-		if (pathlen > sizeof(header.name)) {
-			size_t plen = get_path_prefix(path, pathlen,
-					sizeof(header.prefix));
-			size_t rest = pathlen - plen - 1;
-			if (plen > 0 && rest <= sizeof(header.name)) {
-				memcpy(header.prefix, path, plen);
+		return error("unsupported file mode: 0%o (SHA1: %s)",
+			     mode, sha1_to_hex(sha1));
+	}
+	if (pathlen > sizeof(header.name)) {
+		size_t plen = get_path_prefix(path, pathlen,
+					      sizeof(header.prefix));
+		size_t rest = pathlen - plen - 1;
+		if (plen > 0 && rest <= sizeof(header.name)) {
+			memcpy(header.prefix, path, plen);
 				memcpy(header.name, path + plen + 1, rest);
-			} else {
-				sprintf(header.name, "%s.data",
-				        sha1_to_hex(sha1));
-				strbuf_append_ext_header(&ext_header, "path",
-						path, pathlen);
-			}
-		} else
-			memcpy(header.name, path, pathlen);
+		} else {
+			sprintf(header.name, "%s.data",
+				sha1_to_hex(sha1));
+			strbuf_append_ext_header(&ext_header, "path",
+						 path, pathlen);
+		}
+	} else
+		memcpy(header.name, path, pathlen);
+
+	if (S_ISREG(mode) && !args->convert &&
+	    sha1_object_info(sha1, &size) == OBJ_BLOB &&
+	    size > big_file_threshold)
+		buffer = NULL;
+	else if (S_ISLNK(mode) || S_ISREG(mode)) {
+		enum object_type type;
+		buffer = sha1_file_to_archive(args, path, sha1, old_mode, &type, &size);
+		if (!buffer)
+			return error("cannot read %s", sha1_to_hex(sha1));
+	} else {
+		buffer = NULL;
+		size = 0;
 	}
 
-	if (S_ISLNK(mode) && buffer) {
+	if (S_ISLNK(mode)) {
 		if (size > sizeof(header.linkname)) {
 			sprintf(header.linkname, "see %s.paxheader",
 			        sha1_to_hex(sha1));
@@ -178,32 +265,25 @@
 			memcpy(header.linkname, buffer, size);
 	}
 
-	sprintf(header.mode, "%07o", mode & 07777);
-	sprintf(header.size, "%011lo", S_ISREG(mode) ? size : 0);
-	sprintf(header.mtime, "%011lo", (unsigned long) args->time);
-
-	sprintf(header.uid, "%07o", 0);
-	sprintf(header.gid, "%07o", 0);
-	strlcpy(header.uname, "root", sizeof(header.uname));
-	strlcpy(header.gname, "root", sizeof(header.gname));
-	sprintf(header.devmajor, "%07o", 0);
-	sprintf(header.devminor, "%07o", 0);
-
-	memcpy(header.magic, "ustar", 6);
-	memcpy(header.version, "00", 2);
-
-	sprintf(header.chksum, "%07o", ustar_header_chksum(&header));
+	prepare_header(args, &header, mode, size);
 
 	if (ext_header.len > 0) {
-		err = write_tar_entry(args, sha1, NULL, 0, 0, ext_header.buf,
-				ext_header.len);
-		if (err)
+		err = write_extended_header(args, sha1, ext_header.buf,
+					    ext_header.len);
+		if (err) {
+			free(buffer);
 			return err;
+		}
 	}
 	strbuf_release(&ext_header);
 	write_blocked(&header, sizeof(header));
-	if (S_ISREG(mode) && buffer && size > 0)
-		write_blocked(buffer, size);
+	if (S_ISREG(mode) && size > 0) {
+		if (buffer)
+			write_blocked(buffer, size);
+		else
+			err = stream_blocked(sha1);
+	}
+	free(buffer);
 	return err;
 }
 
@@ -211,15 +291,83 @@
 {
 	const unsigned char *sha1 = args->commit_sha1;
 	struct strbuf ext_header = STRBUF_INIT;
-	int err;
+	struct ustar_header header;
+	unsigned int mode;
+	int err = 0;
 
 	strbuf_append_ext_header(&ext_header, "comment", sha1_to_hex(sha1), 40);
-	err = write_tar_entry(args, NULL, NULL, 0, 0, ext_header.buf,
-			ext_header.len);
+	memset(&header, 0, sizeof(header));
+	*header.typeflag = TYPEFLAG_GLOBAL_HEADER;
+	mode = 0100666;
+	strcpy(header.name, "pax_global_header");
+	prepare_header(args, &header, mode, ext_header.len);
+	write_blocked(&header, sizeof(header));
+	write_blocked(ext_header.buf, ext_header.len);
 	strbuf_release(&ext_header);
 	return err;
 }
 
+static struct archiver **tar_filters;
+static int nr_tar_filters;
+static int alloc_tar_filters;
+
+static struct archiver *find_tar_filter(const char *name, int len)
+{
+	int i;
+	for (i = 0; i < nr_tar_filters; i++) {
+		struct archiver *ar = tar_filters[i];
+		if (!strncmp(ar->name, name, len) && !ar->name[len])
+			return ar;
+	}
+	return NULL;
+}
+
+static int tar_filter_config(const char *var, const char *value, void *data)
+{
+	struct archiver *ar;
+	const char *dot;
+	const char *name;
+	const char *type;
+	int namelen;
+
+	if (prefixcmp(var, "tar."))
+		return 0;
+	dot = strrchr(var, '.');
+	if (dot == var + 9)
+		return 0;
+
+	name = var + 4;
+	namelen = dot - name;
+	type = dot + 1;
+
+	ar = find_tar_filter(name, namelen);
+	if (!ar) {
+		ar = xcalloc(1, sizeof(*ar));
+		ar->name = xmemdupz(name, namelen);
+		ar->write_archive = write_tar_filter_archive;
+		ar->flags = ARCHIVER_WANT_COMPRESSION_LEVELS;
+		ALLOC_GROW(tar_filters, nr_tar_filters + 1, alloc_tar_filters);
+		tar_filters[nr_tar_filters++] = ar;
+	}
+
+	if (!strcmp(type, "command")) {
+		if (!value)
+			return config_error_nonbool(var);
+		free(ar->data);
+		ar->data = xstrdup(value);
+		return 0;
+	}
+	if (!strcmp(type, "remote")) {
+		if (git_config_bool(var, value))
+			ar->flags |= ARCHIVER_REMOTE;
+		else
+			ar->flags &= ~ARCHIVER_REMOTE;
+		return 0;
+	}
+
+	return 0;
+}
+
 static int git_tar_config(const char *var, const char *value, void *cb)
 {
 	if (!strcmp(var, "tar.umask")) {
@@ -231,15 +379,15 @@
 		}
 		return 0;
 	}
-	return git_default_config(var, value, cb);
+
+	return tar_filter_config(var, value, cb);
 }
 
-int write_tar_archive(struct archiver_args *args)
+static int write_tar_archive(const struct archiver *ar,
+			     struct archiver_args *args)
 {
 	int err = 0;
 
-	git_config(git_tar_config, NULL);
-
 	if (args->commit_sha1)
 		err = write_global_extended_header(args);
 	if (!err)
@@ -248,3 +396,65 @@
 		write_trailer();
 	return err;
 }
+
+static int write_tar_filter_archive(const struct archiver *ar,
+				    struct archiver_args *args)
+{
+	struct strbuf cmd = STRBUF_INIT;
+	struct child_process filter;
+	const char *argv[2];
+	int r;
+
+	if (!ar->data)
+		die("BUG: tar-filter archiver called with no filter defined");
+
+	strbuf_addstr(&cmd, ar->data);
+	if (args->compression_level >= 0)
+		strbuf_addf(&cmd, " -%d", args->compression_level);
+
+	memset(&filter, 0, sizeof(filter));
+	argv[0] = cmd.buf;
+	argv[1] = NULL;
+	filter.argv = argv;
+	filter.use_shell = 1;
+	filter.in = -1;
+
+	if (start_command(&filter) < 0)
+		die_errno("unable to start '%s' filter", argv[0]);
+	close(1);
+	if (dup2(filter.in, 1) < 0)
+		die_errno("unable to redirect descriptor");
+	close(filter.in);
+
+	r = write_tar_archive(ar, args);
+
+	close(1);
+	if (finish_command(&filter) != 0)
+		die("'%s' filter reported error", argv[0]);
+
+	strbuf_release(&cmd);
+	return r;
+}
+
+static struct archiver tar_archiver = {
+	"tar",
+	write_tar_archive,
+	ARCHIVER_REMOTE
+};
+
+void init_tar_archiver(void)
+{
+	int i;
+	register_archiver(&tar_archiver);
+
+	tar_filter_config("tar.tgz.command", "gzip -cn", NULL);
+	tar_filter_config("tar.tgz.remote", "true", NULL);
+	tar_filter_config("tar.tar.gz.command", "gzip -cn", NULL);
+	tar_filter_config("tar.tar.gz.remote", "true", NULL);
+	git_config(git_tar_config, NULL);
+	for (i = 0; i < nr_tar_filters; i++) {
+		/* omit any filters that never had a command configured */
+		if (tar_filters[i]->data)
+			register_archiver(tar_filters[i]);
+	}
+}
diff --git a/archive-zip.c b/archive-zip.c
index cf28504..f5af81f 100644
--- a/archive-zip.c
+++ b/archive-zip.c
@@ -3,6 +3,7 @@
  */
 #include "cache.h"
 #include "archive.h"
+#include "streaming.h"
 
 static int zip_date;
 static int zip_time;
@@ -15,6 +16,7 @@
 static unsigned int zip_dir_entries;
 
 #define ZIP_DIRECTORY_MIN_SIZE	(1024 * 1024)
+#define ZIP_STREAM (8)
 
 struct zip_local_header {
 	unsigned char magic[4];
@@ -31,6 +33,14 @@
 	unsigned char _end[1];
 };
 
+struct zip_data_desc {
+	unsigned char magic[4];
+	unsigned char crc32[4];
+	unsigned char compressed_size[4];
+	unsigned char size[4];
+	unsigned char _end[1];
+};
+
 struct zip_dir_header {
 	unsigned char magic[4];
 	unsigned char creator_version[2];
@@ -70,6 +80,7 @@
  * we're interested in.
  */
 #define ZIP_LOCAL_HEADER_SIZE	offsetof(struct zip_local_header, _end)
+#define ZIP_DATA_DESC_SIZE	offsetof(struct zip_data_desc, _end)
 #define ZIP_DIR_HEADER_SIZE	offsetof(struct zip_dir_header, _end)
 #define ZIP_DIR_TRAILER_SIZE	offsetof(struct zip_dir_trailer, _end)
 
@@ -90,14 +101,14 @@
 static void *zlib_deflate(void *data, unsigned long size,
 		int compression_level, unsigned long *compressed_size)
 {
-	z_stream stream;
+	git_zstream stream;
 	unsigned long maxsize;
 	void *buffer;
 	int result;
 
 	memset(&stream, 0, sizeof(stream));
-	deflateInit(&stream, compression_level);
-	maxsize = deflateBound(&stream, size);
+	git_deflate_init(&stream, compression_level);
+	maxsize = git_deflate_bound(&stream, size);
 	buffer = xmalloc(maxsize);
 
 	stream.next_in = data;
@@ -106,7 +117,7 @@
 	stream.avail_out = maxsize;
 
 	do {
-		result = deflate(&stream, Z_FINISH);
+		result = git_deflate(&stream, Z_FINISH);
 	} while (result == Z_OK);
 
 	if (result != Z_STREAM_END) {
@@ -114,26 +125,65 @@
 		return NULL;
 	}
 
-	deflateEnd(&stream);
+	git_deflate_end(&stream);
 	*compressed_size = stream.total_out;
 
 	return buffer;
 }
 
+static void write_zip_data_desc(unsigned long size,
+				unsigned long compressed_size,
+				unsigned long crc)
+{
+	struct zip_data_desc trailer;
+
+	copy_le32(trailer.magic, 0x08074b50);
+	copy_le32(trailer.crc32, crc);
+	copy_le32(trailer.compressed_size, compressed_size);
+	copy_le32(trailer.size, size);
+	write_or_die(1, &trailer, ZIP_DATA_DESC_SIZE);
+}
+
+static void set_zip_dir_data_desc(struct zip_dir_header *header,
+				  unsigned long size,
+				  unsigned long compressed_size,
+				  unsigned long crc)
+{
+	copy_le32(header->crc32, crc);
+	copy_le32(header->compressed_size, compressed_size);
+	copy_le32(header->size, size);
+}
+
+static void set_zip_header_data_desc(struct zip_local_header *header,
+				     unsigned long size,
+				     unsigned long compressed_size,
+				     unsigned long crc)
+{
+	copy_le32(header->crc32, crc);
+	copy_le32(header->compressed_size, compressed_size);
+	copy_le32(header->size, size);
+}
+
+#define STREAM_BUFFER_SIZE (1024 * 16)
+
 static int write_zip_entry(struct archiver_args *args,
-		const unsigned char *sha1, const char *path, size_t pathlen,
-		unsigned int mode, void *buffer, unsigned long size)
+			   const unsigned char *sha1,
+			   const char *path, size_t pathlen,
+			   unsigned int mode)
 {
 	struct zip_local_header header;
 	struct zip_dir_header dirent;
 	unsigned long attr2;
 	unsigned long compressed_size;
-	unsigned long uncompressed_size;
 	unsigned long crc;
 	unsigned long direntsize;
 	int method;
 	unsigned char *out;
 	void *deflated = NULL;
+	void *buffer;
+	struct git_istream *stream = NULL;
+	unsigned long flags = 0;
+	unsigned long size;
 
 	crc = crc32(0, NULL, 0);
 
@@ -146,24 +196,43 @@
 		method = 0;
 		attr2 = 16;
 		out = NULL;
-		uncompressed_size = 0;
+		size = 0;
 		compressed_size = 0;
+		buffer = NULL;
+		size = 0;
 	} else if (S_ISREG(mode) || S_ISLNK(mode)) {
+		enum object_type type = sha1_object_info(sha1, &size);
+
 		method = 0;
 		attr2 = S_ISLNK(mode) ? ((mode | 0777) << 16) :
 			(mode & 0111) ? ((mode) << 16) : 0;
-		if (S_ISREG(mode) && args->compression_level != 0)
+		if (S_ISREG(mode) && args->compression_level != 0 && size > 0)
 			method = 8;
-		crc = crc32(crc, buffer, size);
-		out = buffer;
-		uncompressed_size = size;
 		compressed_size = size;
+
+		if (S_ISREG(mode) && type == OBJ_BLOB && !args->convert &&
+		    size > big_file_threshold) {
+			stream = open_istream(sha1, &type, &size, NULL);
+			if (!stream)
+				return error("cannot stream blob %s",
+					     sha1_to_hex(sha1));
+			flags |= ZIP_STREAM;
+			out = buffer = NULL;
+		} else {
+			buffer = sha1_file_to_archive(args, path, sha1, mode,
+						      &type, &size);
+			if (!buffer)
+				return error("cannot read %s",
+					     sha1_to_hex(sha1));
+			crc = crc32(crc, buffer, size);
+			out = buffer;
+		}
 	} else {
 		return error("unsupported file mode: 0%o (SHA1: %s)", mode,
 				sha1_to_hex(sha1));
 	}
 
-	if (method == 8) {
+	if (buffer && method == 8) {
 		deflated = zlib_deflate(buffer, size, args->compression_level,
 				&compressed_size);
 		if (deflated && compressed_size - 6 < size) {
@@ -188,13 +257,11 @@
 	copy_le16(dirent.creator_version,
 		S_ISLNK(mode) || (S_ISREG(mode) && (mode & 0111)) ? 0x0317 : 0);
 	copy_le16(dirent.version, 10);
-	copy_le16(dirent.flags, 0);
+	copy_le16(dirent.flags, flags);
 	copy_le16(dirent.compression_method, method);
 	copy_le16(dirent.mtime, zip_time);
 	copy_le16(dirent.mdate, zip_date);
-	copy_le32(dirent.crc32, crc);
-	copy_le32(dirent.compressed_size, compressed_size);
-	copy_le32(dirent.size, uncompressed_size);
+	set_zip_dir_data_desc(&dirent, size, compressed_size, crc);
 	copy_le16(dirent.filename_length, pathlen);
 	copy_le16(dirent.extra_length, 0);
 	copy_le16(dirent.comment_length, 0);
@@ -202,33 +269,120 @@
 	copy_le16(dirent.attr1, 0);
 	copy_le32(dirent.attr2, attr2);
 	copy_le32(dirent.offset, zip_offset);
-	memcpy(zip_dir + zip_dir_offset, &dirent, ZIP_DIR_HEADER_SIZE);
-	zip_dir_offset += ZIP_DIR_HEADER_SIZE;
-	memcpy(zip_dir + zip_dir_offset, path, pathlen);
-	zip_dir_offset += pathlen;
-	zip_dir_entries++;
 
 	copy_le32(header.magic, 0x04034b50);
 	copy_le16(header.version, 10);
-	copy_le16(header.flags, 0);
+	copy_le16(header.flags, flags);
 	copy_le16(header.compression_method, method);
 	copy_le16(header.mtime, zip_time);
 	copy_le16(header.mdate, zip_date);
-	copy_le32(header.crc32, crc);
-	copy_le32(header.compressed_size, compressed_size);
-	copy_le32(header.size, uncompressed_size);
+	if (flags & ZIP_STREAM)
+		set_zip_header_data_desc(&header, 0, 0, 0);
+	else
+		set_zip_header_data_desc(&header, size, compressed_size, crc);
 	copy_le16(header.filename_length, pathlen);
 	copy_le16(header.extra_length, 0);
 	write_or_die(1, &header, ZIP_LOCAL_HEADER_SIZE);
 	zip_offset += ZIP_LOCAL_HEADER_SIZE;
 	write_or_die(1, path, pathlen);
 	zip_offset += pathlen;
-	if (compressed_size > 0) {
+	if (stream && method == 0) {
+		unsigned char buf[STREAM_BUFFER_SIZE];
+		ssize_t readlen;
+
+		for (;;) {
+			readlen = read_istream(stream, buf, sizeof(buf));
+			if (readlen <= 0)
+				break;
+			crc = crc32(crc, buf, readlen);
+			write_or_die(1, buf, readlen);
+		}
+		close_istream(stream);
+		if (readlen)
+			return readlen;
+
+		compressed_size = size;
+		zip_offset += compressed_size;
+
+		write_zip_data_desc(size, compressed_size, crc);
+		zip_offset += ZIP_DATA_DESC_SIZE;
+
+		set_zip_dir_data_desc(&dirent, size, compressed_size, crc);
+	} else if (stream && method == 8) {
+		unsigned char buf[STREAM_BUFFER_SIZE];
+		ssize_t readlen;
+		git_zstream zstream;
+		int result;
+		size_t out_len;
+		unsigned char compressed[STREAM_BUFFER_SIZE * 2];
+
+		memset(&zstream, 0, sizeof(zstream));
+		git_deflate_init(&zstream, args->compression_level);
+
+		compressed_size = 0;
+		zstream.next_out = compressed;
+		zstream.avail_out = sizeof(compressed);
+
+		for (;;) {
+			readlen = read_istream(stream, buf, sizeof(buf));
+			if (readlen <= 0)
+				break;
+			crc = crc32(crc, buf, readlen);
+
+			zstream.next_in = buf;
+			zstream.avail_in = readlen;
+			result = git_deflate(&zstream, 0);
+			if (result != Z_OK)
+				die("deflate error (%d)", result);
+			out = compressed;
+			if (!compressed_size)
+				out += 2;
+			out_len = zstream.next_out - out;
+
+			if (out_len > 0) {
+				write_or_die(1, out, out_len);
+				compressed_size += out_len;
+				zstream.next_out = compressed;
+				zstream.avail_out = sizeof(compressed);
+			}
+
+		}
+		close_istream(stream);
+		if (readlen)
+			return readlen;
+
+		zstream.next_in = buf;
+		zstream.avail_in = 0;
+		result = git_deflate(&zstream, Z_FINISH);
+		if (result != Z_STREAM_END)
+			die("deflate error (%d)", result);
+
+		git_deflate_end(&zstream);
+		out = compressed;
+		if (!compressed_size)
+			out += 2;
+		out_len = zstream.next_out - out - 4;
+		write_or_die(1, out, out_len);
+		compressed_size += out_len;
+		zip_offset += compressed_size;
+
+		write_zip_data_desc(size, compressed_size, crc);
+		zip_offset += ZIP_DATA_DESC_SIZE;
+
+		set_zip_dir_data_desc(&dirent, size, compressed_size, crc);
+	} else if (compressed_size > 0) {
 		write_or_die(1, out, compressed_size);
 		zip_offset += compressed_size;
 	}
 
 	free(deflated);
+	free(buffer);
+
+	memcpy(zip_dir + zip_dir_offset, &dirent, ZIP_DIR_HEADER_SIZE);
+	zip_dir_offset += ZIP_DIR_HEADER_SIZE;
+	memcpy(zip_dir + zip_dir_offset, path, pathlen);
+	zip_dir_offset += pathlen;
+	zip_dir_entries++;
 
 	return 0;
 }
@@ -261,7 +415,8 @@
 	*dos_time = t->tm_sec / 2 + t->tm_min * 32 + t->tm_hour * 2048;
 }
 
-int write_zip_archive(struct archiver_args *args)
+static int write_zip_archive(const struct archiver *ar,
+			     struct archiver_args *args)
 {
 	int err;
 
@@ -278,3 +433,14 @@
 
 	return err;
 }
+
+static struct archiver zip_archiver = {
+	"zip",
+	write_zip_archive,
+	ARCHIVER_WANT_COMPRESSION_LEVELS|ARCHIVER_REMOTE
+};
+
+void init_zip_archiver(void)
+{
+	register_archiver(&zip_archiver);
+}
diff --git a/archive.c b/archive.c
index 0bca9ca..a484433 100644
--- a/archive.c
+++ b/archive.c
@@ -7,23 +7,22 @@
 #include "unpack-trees.h"
 
 static char const * const archive_usage[] = {
-	"git archive [options] <tree-ish> [path...]",
+	"git archive [options] <tree-ish> [<path>...]",
 	"git archive --list",
-	"git archive --remote <repo> [--exec <cmd>] [options] <tree-ish> [path...]",
+	"git archive --remote <repo> [--exec <cmd>] [options] <tree-ish> [<path>...]",
 	"git archive --remote <repo> [--exec <cmd>] --list",
 	NULL
 };
 
-#define USES_ZLIB_COMPRESSION 1
+static const struct archiver **archivers;
+static int nr_archivers;
+static int alloc_archivers;
 
-static const struct archiver {
-	const char *name;
-	write_archive_fn_t write_archive;
-	unsigned int flags;
-} archivers[] = {
-	{ "tar", write_tar_archive },
-	{ "zip", write_zip_archive, USES_ZLIB_COMPRESSION },
-};
+void register_archiver(struct archiver *ar)
+{
+	ALLOC_GROW(archivers, nr_archivers + 1, alloc_archivers);
+	archivers[nr_archivers++] = ar;
+}
 
 static void format_subst(const struct commit *commit,
                          const char *src, size_t len,
@@ -31,6 +30,9 @@
 {
 	char *to_free = NULL;
 	struct strbuf fmt = STRBUF_INIT;
+	struct pretty_print_context ctx = {0};
+	ctx.date_mode = DATE_NORMAL;
+	ctx.abbrev = DEFAULT_ABBREV;
 
 	if (src == buf->buf)
 		to_free = strbuf_detach(buf, NULL);
@@ -48,7 +50,7 @@
 		strbuf_add(&fmt, b + 8, c - b - 8);
 
 		strbuf_add(buf, src, b - src);
-		format_commit_message(commit, fmt.buf, buf, DATE_NORMAL);
+		format_commit_message(commit, fmt.buf, buf, &ctx);
 		len -= c + 1 - src;
 		src  = c + 1;
 	}
@@ -57,12 +59,15 @@
 	free(to_free);
 }
 
-static void *sha1_file_to_archive(const char *path, const unsigned char *sha1,
-		unsigned int mode, enum object_type *type,
-		unsigned long *sizep, const struct commit *commit)
+void *sha1_file_to_archive(const struct archiver_args *args,
+			   const char *path, const unsigned char *sha1,
+			   unsigned int mode, enum object_type *type,
+			   unsigned long *sizep)
 {
 	void *buffer;
+	const struct commit *commit = args->convert ? args->commit : NULL;
 
+	path += args->baselen;
 	buffer = read_sha1_file(sha1, type, sizep);
 	if (buffer && S_ISREG(mode)) {
 		struct strbuf buf = STRBUF_INIT;
@@ -85,8 +90,8 @@
 	static struct git_attr *attr_export_subst;
 
 	if (!attr_export_ignore) {
-		attr_export_ignore = git_attr("export-ignore", 13);
-		attr_export_subst = git_attr("export-subst", 12);
+		attr_export_ignore = git_attr("export-ignore");
+		attr_export_subst = git_attr("export-subst");
 	}
 	check[0].attr = attr_export_ignore;
 	check[1].attr = attr_export_subst;
@@ -107,44 +112,36 @@
 	write_archive_entry_fn_t write_entry = c->write_entry;
 	struct git_attr_check check[2];
 	const char *path_without_prefix;
-	int convert = 0;
 	int err;
-	enum object_type type;
-	unsigned long size;
-	void *buffer;
 
+	args->convert = 0;
 	strbuf_reset(&path);
 	strbuf_grow(&path, PATH_MAX);
+	strbuf_add(&path, args->base, args->baselen);
 	strbuf_add(&path, base, baselen);
 	strbuf_addstr(&path, filename);
 	path_without_prefix = path.buf + args->baselen;
 
 	setup_archive_check(check);
-	if (!git_checkattr(path_without_prefix, ARRAY_SIZE(check), check)) {
+	if (!git_check_attr(path_without_prefix, ARRAY_SIZE(check), check)) {
 		if (ATTR_TRUE(check[0].value))
 			return 0;
-		convert = ATTR_TRUE(check[1].value);
+		args->convert = ATTR_TRUE(check[1].value);
 	}
 
 	if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
 		strbuf_addch(&path, '/');
 		if (args->verbose)
 			fprintf(stderr, "%.*s\n", (int)path.len, path.buf);
-		err = write_entry(args, sha1, path.buf, path.len, mode, NULL, 0);
+		err = write_entry(args, sha1, path.buf, path.len, mode);
 		if (err)
 			return err;
 		return (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0);
 	}
 
-	buffer = sha1_file_to_archive(path_without_prefix, sha1, mode,
-			&type, &size, convert ? args->commit : NULL);
-	if (!buffer)
-		return error("cannot read %s", sha1_to_hex(sha1));
 	if (args->verbose)
 		fprintf(stderr, "%.*s\n", (int)path.len, path.buf);
-	err = write_entry(args, sha1, path.buf, path.len, mode, buffer, size);
-	free(buffer);
-	return err;
+	return write_entry(args, sha1, path.buf, path.len, mode);
 }
 
 int write_archive_entries(struct archiver_args *args,
@@ -153,6 +150,7 @@
 	struct archiver_context context;
 	struct unpack_trees_options opts;
 	struct tree_desc t;
+	struct pathspec pathspec;
 	int err;
 
 	if (args->baselen > 0 && args->base[args->baselen - 1] == '/') {
@@ -163,7 +161,7 @@
 		if (args->verbose)
 			fprintf(stderr, "%.*s\n", (int)len, args->base);
 		err = write_entry(args, args->tree->object.sha1, args->base,
-				len, 040777, NULL, 0);
+				  len, 040777);
 		if (err)
 			return err;
 	}
@@ -187,8 +185,10 @@
 		git_attr_set_direction(GIT_ATTR_INDEX, &the_index);
 	}
 
-	err =  read_tree_recursive(args->tree, args->base, args->baselen, 0,
-			args->pathspec, write_archive_entry, &context);
+	init_pathspec(&pathspec, args->pathspec);
+	err = read_tree_recursive(args->tree, "", 0, 0, &pathspec,
+				  write_archive_entry, &context);
+	free_pathspec(&pathspec);
 	if (err == READ_TREE_RECURSIVE)
 		err = 0;
 	return err;
@@ -201,21 +201,48 @@
 	if (!name)
 		return NULL;
 
-	for (i = 0; i < ARRAY_SIZE(archivers); i++) {
-		if (!strcmp(name, archivers[i].name))
-			return &archivers[i];
+	for (i = 0; i < nr_archivers; i++) {
+		if (!strcmp(name, archivers[i]->name))
+			return archivers[i];
 	}
 	return NULL;
 }
 
+static int reject_entry(const unsigned char *sha1, const char *base,
+			int baselen, const char *filename, unsigned mode,
+			int stage, void *context)
+{
+	return -1;
+}
+
+static int path_exists(struct tree *tree, const char *path)
+{
+	const char *paths[] = { path, NULL };
+	struct pathspec pathspec;
+	int ret;
+
+	init_pathspec(&pathspec, paths);
+	ret = read_tree_recursive(tree, "", 0, 0, &pathspec, reject_entry, NULL);
+	free_pathspec(&pathspec);
+	return ret != 0;
+}
+
 static void parse_pathspec_arg(const char **pathspec,
 		struct archiver_args *ar_args)
 {
-	ar_args->pathspec = get_pathspec(ar_args->base, pathspec);
+	ar_args->pathspec = pathspec = get_pathspec("", pathspec);
+	if (pathspec) {
+		while (*pathspec) {
+			if (!path_exists(ar_args->tree, *pathspec))
+				die("path not found: %s", *pathspec);
+			pathspec++;
+		}
+	}
 }
 
 static void parse_treeish_arg(const char **argv,
-		struct archiver_args *ar_args, const char *prefix)
+		struct archiver_args *ar_args, const char *prefix,
+		int remote)
 {
 	const char *name = argv[0];
 	const unsigned char *commit_sha1;
@@ -224,6 +251,17 @@
 	const struct commit *commit;
 	unsigned char sha1[20];
 
+	/* Remotes are only allowed to fetch actual refs */
+	if (remote) {
+		char *ref = NULL;
+		const char *colon = strchr(name, ':');
+		int refnamelen = colon ? colon - name : strlen(name);
+
+		if (!dwim_ref(name, refnamelen, sha1, &ref))
+			die("no such ref: %.*s", refnamelen, name);
+		free(ref);
+	}
+
 	if (get_sha1(name, sha1))
 		die("Not a valid object name");
 
@@ -266,9 +304,10 @@
 	  PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_HIDDEN, NULL, (p) }
 
 static int parse_archive_args(int argc, const char **argv,
-		const struct archiver **ar, struct archiver_args *args)
+		const struct archiver **ar, struct archiver_args *args,
+		const char *name_hint, int is_remote)
 {
-	const char *format = "tar";
+	const char *format = NULL;
 	const char *base = NULL;
 	const char *remote = NULL;
 	const char *exec = NULL;
@@ -283,11 +322,11 @@
 		OPT_STRING(0, "format", &format, "fmt", "archive format"),
 		OPT_STRING(0, "prefix", &base, "prefix",
 			"prepend prefix to each pathname in the archive"),
-		OPT_STRING(0, "output", &output, "file",
+		OPT_STRING('o', "output", &output, "file",
 			"write the archive to this file"),
-		OPT_BOOLEAN(0, "worktree-attributes", &worktree_attributes,
+		OPT_BOOL(0, "worktree-attributes", &worktree_attributes,
 			"read .gitattributes in working directory"),
-		OPT__VERBOSE(&verbose),
+		OPT__VERBOSE(&verbose, "report archived files on stderr"),
 		OPT__COMPR('0', &compression_level, "store only", 0),
 		OPT__COMPR('1', &compression_level, "compress faster", 1),
 		OPT__COMPR_HIDDEN('2', &compression_level, 2),
@@ -299,7 +338,7 @@
 		OPT__COMPR_HIDDEN('8', &compression_level, 8),
 		OPT__COMPR('9', &compression_level, "compress better", 9),
 		OPT_GROUP(""),
-		OPT_BOOLEAN('l', "list", &list,
+		OPT_BOOL('l', "list", &list,
 			"list supported archive formats"),
 		OPT_GROUP(""),
 		OPT_STRING(0, "remote", &remote, "repo",
@@ -322,21 +361,27 @@
 		base = "";
 
 	if (list) {
-		for (i = 0; i < ARRAY_SIZE(archivers); i++)
-			printf("%s\n", archivers[i].name);
+		for (i = 0; i < nr_archivers; i++)
+			if (!is_remote || archivers[i]->flags & ARCHIVER_REMOTE)
+				printf("%s\n", archivers[i]->name);
 		exit(0);
 	}
 
+	if (!format && name_hint)
+		format = archive_format_from_filename(name_hint);
+	if (!format)
+		format = "tar";
+
 	/* We need at least one parameter -- tree-ish */
 	if (argc < 1)
 		usage_with_options(archive_usage, opts);
 	*ar = lookup_archiver(format);
-	if (!*ar)
+	if (!*ar || (is_remote && !((*ar)->flags & ARCHIVER_REMOTE)))
 		die("Unknown archive format '%s'", format);
 
 	args->compression_level = Z_DEFAULT_COMPRESSION;
 	if (compression_level != -1) {
-		if ((*ar)->flags & USES_ZLIB_COMPRESSION)
+		if ((*ar)->flags & ARCHIVER_WANT_COMPRESSION_LEVELS)
 			args->compression_level = compression_level;
 		else {
 			die("Argument not supported for format '%s': -%d",
@@ -352,19 +397,55 @@
 }
 
 int write_archive(int argc, const char **argv, const char *prefix,
-		int setup_prefix)
+		  int setup_prefix, const char *name_hint, int remote)
 {
+	int nongit = 0;
 	const struct archiver *ar = NULL;
 	struct archiver_args args;
 
-	argc = parse_archive_args(argc, argv, &ar, &args);
 	if (setup_prefix && prefix == NULL)
-		prefix = setup_git_directory();
-
-	parse_treeish_arg(argv, &args, prefix);
-	parse_pathspec_arg(argv + 1, &args);
+		prefix = setup_git_directory_gently(&nongit);
 
 	git_config(git_default_config, NULL);
+	init_tar_archiver();
+	init_zip_archiver();
 
-	return ar->write_archive(&args);
+	argc = parse_archive_args(argc, argv, &ar, &args, name_hint, remote);
+	if (nongit) {
+		/*
+		 * We know this will die() with an error, so we could just
+		 * die ourselves; but its error message will be more specific
+		 * than what we could write here.
+		 */
+		setup_git_directory();
+	}
+
+	parse_treeish_arg(argv, &args, prefix, remote);
+	parse_pathspec_arg(argv + 1, &args);
+
+	return ar->write_archive(ar, &args);
+}
+
+static int match_extension(const char *filename, const char *ext)
+{
+	int prefixlen = strlen(filename) - strlen(ext);
+
+	/*
+	 * We need 1 character for the '.', and 1 character to ensure that the
+	 * prefix is non-empty (k.e., we don't match .tar.gz with no actual
+	 * filename).
+	 */
+	if (prefixlen < 2 || filename[prefixlen-1] != '.')
+		return 0;
+	return !strcmp(filename + prefixlen, ext);
+}
+
+const char *archive_format_from_filename(const char *filename)
+{
+	int i;
+
+	for (i = 0; i < nr_archivers; i++)
+		if (match_extension(filename, archivers[i]->name))
+			return archivers[i]->name;
+	return NULL;
 }
diff --git a/archive.h b/archive.h
index 038ac35..895afcd 100644
--- a/archive.h
+++ b/archive.h
@@ -11,20 +11,35 @@
 	const char **pathspec;
 	unsigned int verbose : 1;
 	unsigned int worktree_attributes : 1;
+	unsigned int convert : 1;
 	int compression_level;
 };
 
-typedef int (*write_archive_fn_t)(struct archiver_args *);
+#define ARCHIVER_WANT_COMPRESSION_LEVELS 1
+#define ARCHIVER_REMOTE 2
+struct archiver {
+	const char *name;
+	int (*write_archive)(const struct archiver *, struct archiver_args *);
+	unsigned flags;
+	void *data;
+};
+extern void register_archiver(struct archiver *);
 
-typedef int (*write_archive_entry_fn_t)(struct archiver_args *args, const unsigned char *sha1, const char *path, size_t pathlen, unsigned int mode, void *buffer, unsigned long size);
+extern void init_tar_archiver(void);
+extern void init_zip_archiver(void);
 
-/*
- * Archive-format specific backends.
- */
-extern int write_tar_archive(struct archiver_args *);
-extern int write_zip_archive(struct archiver_args *);
+typedef int (*write_archive_entry_fn_t)(struct archiver_args *args,
+					const unsigned char *sha1,
+					const char *path, size_t pathlen,
+					unsigned int mode);
 
 extern int write_archive_entries(struct archiver_args *args, write_archive_entry_fn_t write_entry);
-extern int write_archive(int argc, const char **argv, const char *prefix, int setup_prefix);
+extern int write_archive(int argc, const char **argv, const char *prefix, int setup_prefix, const char *name_hint, int remote);
+
+const char *archive_format_from_filename(const char *filename);
+extern void *sha1_file_to_archive(const struct archiver_args *args,
+				  const char *path, const unsigned char *sha1,
+				  unsigned int mode, enum object_type *type,
+				  unsigned long *sizep);
 
 #endif	/* ARCHIVE_H */
diff --git a/argv-array.c b/argv-array.c
new file mode 100644
index 0000000..0b5f889
--- /dev/null
+++ b/argv-array.c
@@ -0,0 +1,61 @@
+#include "cache.h"
+#include "argv-array.h"
+#include "strbuf.h"
+
+const char *empty_argv[] = { NULL };
+
+void argv_array_init(struct argv_array *array)
+{
+	array->argv = empty_argv;
+	array->argc = 0;
+	array->alloc = 0;
+}
+
+static void argv_array_push_nodup(struct argv_array *array, const char *value)
+{
+	if (array->argv == empty_argv)
+		array->argv = NULL;
+
+	ALLOC_GROW(array->argv, array->argc + 2, array->alloc);
+	array->argv[array->argc++] = value;
+	array->argv[array->argc] = NULL;
+}
+
+void argv_array_push(struct argv_array *array, const char *value)
+{
+	argv_array_push_nodup(array, xstrdup(value));
+}
+
+void argv_array_pushf(struct argv_array *array, const char *fmt, ...)
+{
+	va_list ap;
+	struct strbuf v = STRBUF_INIT;
+
+	va_start(ap, fmt);
+	strbuf_vaddf(&v, fmt, ap);
+	va_end(ap);
+
+	argv_array_push_nodup(array, strbuf_detach(&v, NULL));
+}
+
+void argv_array_pushl(struct argv_array *array, ...)
+{
+	va_list ap;
+	const char *arg;
+
+	va_start(ap, array);
+	while((arg = va_arg(ap, const char *)))
+		argv_array_push(array, arg);
+	va_end(ap);
+}
+
+void argv_array_clear(struct argv_array *array)
+{
+	if (array->argv != empty_argv) {
+		int i;
+		for (i = 0; i < array->argc; i++)
+			free((char **)array->argv[i]);
+		free(array->argv);
+	}
+	argv_array_init(array);
+}
diff --git a/argv-array.h b/argv-array.h
new file mode 100644
index 0000000..b93a69c
--- /dev/null
+++ b/argv-array.h
@@ -0,0 +1,21 @@
+#ifndef ARGV_ARRAY_H
+#define ARGV_ARRAY_H
+
+extern const char *empty_argv[];
+
+struct argv_array {
+	const char **argv;
+	int argc;
+	int alloc;
+};
+
+#define ARGV_ARRAY_INIT { empty_argv, 0, 0 }
+
+void argv_array_init(struct argv_array *);
+void argv_array_push(struct argv_array *, const char *);
+__attribute__((format (printf,2,3)))
+void argv_array_pushf(struct argv_array *, const char *fmt, ...);
+void argv_array_pushl(struct argv_array *, ...);
+void argv_array_clear(struct argv_array *);
+
+#endif /* ARGV_ARRAY_H */
diff --git a/attr.c b/attr.c
index 55bdb7c..aef93d8 100644
--- a/attr.c
+++ b/attr.c
@@ -1,6 +1,17 @@
+/*
+ * Handle git attributes.  See gitattributes(5) for a description of
+ * the file syntax, and Documentation/technical/api-gitattributes.txt
+ * for a description of the API.
+ *
+ * One basic design decision here is that we are not going to support
+ * an insanely large number of attributes.
+ */
+
 #define NO_THE_INDEX_COMPATIBILITY_MACROS
 #include "cache.h"
+#include "exec_cmd.h"
 #include "attr.h"
+#include "dir.h"
 
 const char git_attr__true[] = "(builtin)true";
 const char git_attr__false[] = "\0(builtin)false";
@@ -10,12 +21,7 @@
 #define ATTR__UNSET NULL
 #define ATTR__UNKNOWN git_attr__unknown
 
-/*
- * The basic design decision here is that we are not going to have
- * insanely large number of attributes.
- *
- * This is a randomly chosen prime.
- */
+/* This is a randomly chosen prime. */
 #define HASHSIZE 257
 
 #ifndef DEBUG_ATTR
@@ -33,6 +39,11 @@
 static struct git_attr_check *check_all_attr;
 static struct git_attr *(git_attr_hash[HASHSIZE]);
 
+char *git_attr_name(struct git_attr *attr)
+{
+	return attr->name;
+}
+
 static unsigned hash_name(const char *name, int namelen)
 {
 	unsigned val = 0, c;
@@ -47,12 +58,10 @@
 static int invalid_attr_name(const char *name, int namelen)
 {
 	/*
-	 * Attribute name cannot begin with '-' and from
-	 * [-A-Za-z0-9_.].  We'd specifically exclude '=' for now,
-	 * as we might later want to allow non-binary value for
-	 * attributes, e.g. "*.svg	merge=special-merge-program-for-svg"
+	 * Attribute name cannot begin with '-' and must consist of
+	 * characters from [-A-Za-z0-9_.].
 	 */
-	if (*name == '-')
+	if (namelen <= 0 || *name == '-')
 		return -1;
 	while (namelen--) {
 		char ch = *name++;
@@ -65,7 +74,7 @@
 	return 0;
 }
 
-struct git_attr *git_attr(const char *name, int len)
+static struct git_attr *git_attr_internal(const char *name, int len)
 {
 	unsigned hval = hash_name(name, len);
 	unsigned pos = hval % HASHSIZE;
@@ -95,15 +104,10 @@
 	return a;
 }
 
-/*
- * .gitattributes file is one line per record, each of which is
- *
- * (1) glob pattern.
- * (2) whitespace
- * (3) whitespace separated list of attribute names, each of which
- *     could be prefixed with '-' to mean "set to false", '!' to mean
- *     "unset".
- */
+struct git_attr *git_attr(const char *name)
+{
+	return git_attr_internal(name, strlen(name));
+}
 
 /* What does a matched pattern decide? */
 struct attr_state {
@@ -111,6 +115,20 @@
 	const char *setto;
 };
 
+/*
+ * One rule, as from a .gitattributes file.
+ *
+ * If is_macro is true, then u.attr is a pointer to the git_attr being
+ * defined.
+ *
+ * If is_macro is false, then u.pattern points at the filename pattern
+ * to which the rule applies.  (The memory pointed to is part of the
+ * memory block allocated for the match_attr instance.)
+ *
+ * In either case, num_attr is the number of attributes affected by
+ * this rule, and state is an array listing them.  The attributes are
+ * listed as they appear in the file (macros unexpanded).
+ */
 struct match_attr {
 	union {
 		char *pattern;
@@ -123,8 +141,15 @@
 
 static const char blank[] = " \t\r\n";
 
+/*
+ * Parse a whitespace-delimited attribute state (i.e., "attr",
+ * "-attr", "!attr", or "attr=value") from the string starting at src.
+ * If e is not NULL, write the results to *e.  Return a pointer to the
+ * remainder of the string (with leading whitespace removed), or NULL
+ * if there was an error.
+ */
 static const char *parse_attr(const char *src, int lineno, const char *cp,
-			      int *num_attr, struct match_attr *res)
+			      struct attr_state *e)
 {
 	const char *ep, *equals;
 	int len;
@@ -137,7 +162,7 @@
 		len = equals - cp;
 	else
 		len = ep - cp;
-	if (!res) {
+	if (!e) {
 		if (*cp == '-' || *cp == '!') {
 			cp++;
 			len--;
@@ -149,9 +174,6 @@
 			return NULL;
 		}
 	} else {
-		struct attr_state *e;
-
-		e = &(res->state[*num_attr]);
 		if (*cp == '-' || *cp == '!') {
 			e->setto = (*cp == '-') ? ATTR__FALSE : ATTR__UNSET;
 			cp++;
@@ -162,9 +184,8 @@
 		else {
 			e->setto = xmemdupz(equals + 1, ep - equals - 1);
 		}
-		e->attr = git_attr(cp, len);
+		e->attr = git_attr_internal(cp, len);
 	}
-	(*num_attr)++;
 	return ep + strspn(ep, blank);
 }
 
@@ -172,10 +193,9 @@
 					  int lineno, int macro_ok)
 {
 	int namelen;
-	int num_attr;
-	const char *cp, *name;
+	int num_attr, i;
+	const char *cp, *name, *states;
 	struct match_attr *res = NULL;
-	int pass;
 	int is_macro;
 
 	cp = line + strspn(line, blank);
@@ -204,32 +224,35 @@
 	else
 		is_macro = 0;
 
-	for (pass = 0; pass < 2; pass++) {
-		/* pass 0 counts and allocates, pass 1 fills */
-		num_attr = 0;
-		cp = name + namelen;
-		cp = cp + strspn(cp, blank);
-		while (*cp) {
-			cp = parse_attr(src, lineno, cp, &num_attr, res);
-			if (!cp)
-				return NULL;
-		}
-		if (pass)
-			break;
-		res = xcalloc(1,
-			      sizeof(*res) +
-			      sizeof(struct attr_state) * num_attr +
-			      (is_macro ? 0 : namelen + 1));
-		if (is_macro)
-			res->u.attr = git_attr(name, namelen);
-		else {
-			res->u.pattern = (char *)&(res->state[num_attr]);
-			memcpy(res->u.pattern, name, namelen);
-			res->u.pattern[namelen] = 0;
-		}
-		res->is_macro = is_macro;
-		res->num_attr = num_attr;
+	states = name + namelen;
+	states += strspn(states, blank);
+
+	/* First pass to count the attr_states */
+	for (cp = states, num_attr = 0; *cp; num_attr++) {
+		cp = parse_attr(src, lineno, cp, NULL);
+		if (!cp)
+			return NULL;
 	}
+
+	res = xcalloc(1,
+		      sizeof(*res) +
+		      sizeof(struct attr_state) * num_attr +
+		      (is_macro ? 0 : namelen + 1));
+	if (is_macro)
+		res->u.attr = git_attr_internal(name, namelen);
+	else {
+		res->u.pattern = (char *)&(res->state[num_attr]);
+		memcpy(res->u.pattern, name, namelen);
+		res->u.pattern[namelen] = 0;
+	}
+	res->is_macro = is_macro;
+	res->num_attr = num_attr;
+
+	/* Second pass to fill the attr_states */
+	for (cp = states, i = 0; *cp; i++) {
+		cp = parse_attr(src, lineno, cp, &(res->state[i]));
+	}
+
 	return res;
 }
 
@@ -278,11 +301,12 @@
 		}
 		free(a);
 	}
+	free(e->attrs);
 	free(e);
 }
 
 static const char *builtin_attr[] = {
-	"[attr]binary -diff -crlf",
+	"[attr]binary -diff -text",
 	NULL,
 };
 
@@ -457,44 +481,85 @@
 	}
 }
 
+static const char *git_etc_gitattributes(void)
+{
+	static const char *system_wide;
+	if (!system_wide)
+		system_wide = system_path(ETC_GITATTRIBUTES);
+	return system_wide;
+}
+
+static int git_attr_system(void)
+{
+	return !git_env_bool("GIT_ATTR_NOSYSTEM", 0);
+}
+
 static void bootstrap_attr_stack(void)
 {
-	if (!attr_stack) {
-		struct attr_stack *elem;
+	struct attr_stack *elem;
+	char *xdg_attributes_file;
 
-		elem = read_attr_from_array(builtin_attr);
-		elem->origin = NULL;
-		elem->prev = attr_stack;
-		attr_stack = elem;
+	if (attr_stack)
+		return;
 
-		if (!is_bare_repository() || direction == GIT_ATTR_INDEX) {
-			elem = read_attr(GITATTRIBUTES_FILE, 1);
-			elem->origin = strdup("");
+	elem = read_attr_from_array(builtin_attr);
+	elem->origin = NULL;
+	elem->prev = attr_stack;
+	attr_stack = elem;
+
+	if (git_attr_system()) {
+		elem = read_attr_from_file(git_etc_gitattributes(), 1);
+		if (elem) {
+			elem->origin = NULL;
 			elem->prev = attr_stack;
 			attr_stack = elem;
-			debug_push(elem);
 		}
+	}
 
-		elem = read_attr_from_file(git_path(INFOATTRIBUTES_FILE), 1);
-		if (!elem)
-			elem = xcalloc(1, sizeof(*elem));
+	if (!git_attributes_file) {
+		home_config_paths(NULL, &xdg_attributes_file, "attributes");
+		git_attributes_file = xdg_attributes_file;
+	}
+	elem = read_attr_from_file(git_attributes_file, 1);
+	if (elem) {
 		elem->origin = NULL;
 		elem->prev = attr_stack;
 		attr_stack = elem;
 	}
+
+	if (!is_bare_repository() || direction == GIT_ATTR_INDEX) {
+		elem = read_attr(GITATTRIBUTES_FILE, 1);
+		elem->origin = xstrdup("");
+		elem->prev = attr_stack;
+		attr_stack = elem;
+		debug_push(elem);
+	}
+
+	elem = read_attr_from_file(git_path(INFOATTRIBUTES_FILE), 1);
+	if (!elem)
+		elem = xcalloc(1, sizeof(*elem));
+	elem->origin = NULL;
+	elem->prev = attr_stack;
+	attr_stack = elem;
 }
 
-static void prepare_attr_stack(const char *path, int dirlen)
+static void prepare_attr_stack(const char *path)
 {
 	struct attr_stack *elem, *info;
-	int len;
-	struct strbuf pathbuf;
+	int dirlen, len;
+	const char *cp;
 
-	strbuf_init(&pathbuf, dirlen+2+strlen(GITATTRIBUTES_FILE));
+	cp = strrchr(path, '/');
+	if (!cp)
+		dirlen = 0;
+	else
+		dirlen = cp - path;
 
 	/*
 	 * At the bottom of the attribute stack is the built-in
-	 * set of attribute definitions.  Then, contents from
+	 * set of attribute definitions, followed by the contents
+	 * of $(prefix)/etc/gitattributes and a file specified by
+	 * core.attributesfile.  Then, contents from
 	 * .gitattribute files from directories closer to the
 	 * root to the ones in deeper directories are pushed
 	 * to the stack.  Finally, at the very top of the stack
@@ -505,8 +570,7 @@
 	 * .gitattributes in deeper directories to shallower ones,
 	 * and finally use the built-in set as the default.
 	 */
-	if (!attr_stack)
-		bootstrap_attr_stack();
+	bootstrap_attr_stack();
 
 	/*
 	 * Pop the "info" one that is always at the top of the stack.
@@ -516,14 +580,17 @@
 
 	/*
 	 * Pop the ones from directories that are not the prefix of
-	 * the path we are checking.
+	 * the path we are checking. Break out of the loop when we see
+	 * the root one (whose origin is an empty string "") or the builtin
+	 * one (whose origin is NULL) without popping it.
 	 */
-	while (attr_stack && attr_stack->origin) {
+	while (attr_stack->origin) {
 		int namelen = strlen(attr_stack->origin);
 
 		elem = attr_stack;
 		if (namelen <= dirlen &&
-		    !strncmp(elem->origin, path, namelen))
+		    !strncmp(elem->origin, path, namelen) &&
+		    (!namelen || path[namelen] == '/'))
 			break;
 
 		debug_pop(elem);
@@ -535,27 +602,35 @@
 	 * Read from parent directories and push them down
 	 */
 	if (!is_bare_repository() || direction == GIT_ATTR_INDEX) {
-		while (1) {
-			char *cp;
+		/*
+		 * bootstrap_attr_stack() should have added, and the
+		 * above loop should have stopped before popping, the
+		 * root element whose attr_stack->origin is set to an
+		 * empty string.
+		 */
+		struct strbuf pathbuf = STRBUF_INIT;
 
+		assert(attr_stack->origin);
+		while (1) {
 			len = strlen(attr_stack->origin);
 			if (dirlen <= len)
 				break;
-			strbuf_reset(&pathbuf);
-			strbuf_add(&pathbuf, path, dirlen);
+			cp = memchr(path + len + 1, '/', dirlen - len - 1);
+			if (!cp)
+				cp = path + dirlen;
+			strbuf_add(&pathbuf, path, cp - path);
 			strbuf_addch(&pathbuf, '/');
-			cp = strchr(pathbuf.buf + len + 1, '/');
-			strcpy(cp + 1, GITATTRIBUTES_FILE);
+			strbuf_addstr(&pathbuf, GITATTRIBUTES_FILE);
 			elem = read_attr(pathbuf.buf, 0);
-			*cp = '\0';
-			elem->origin = strdup(pathbuf.buf);
+			strbuf_setlen(&pathbuf, cp - path);
+			elem->origin = strbuf_detach(&pathbuf, NULL);
 			elem->prev = attr_stack;
 			attr_stack = elem;
 			debug_push(elem);
 		}
-	}
 
-	strbuf_release(&pathbuf);
+		strbuf_release(&pathbuf);
+	}
 
 	/*
 	 * Finally push the "info" one at the top of the stack.
@@ -572,7 +647,7 @@
 		/* match basename */
 		const char *basename = strrchr(pathname, '/');
 		basename = basename ? basename + 1 : pathname;
-		return (fnmatch(pattern, basename, 0) == 0);
+		return (fnmatch_icase(pattern, basename, 0) == 0);
 	}
 	/*
 	 * match with FNM_PATHNAME; the pattern has base implicitly
@@ -586,23 +661,28 @@
 		return 0;
 	if (baselen != 0)
 		baselen++;
-	return fnmatch(pattern, pathname + baselen, FNM_PATHNAME) == 0;
+	return fnmatch_icase(pattern, pathname + baselen, FNM_PATHNAME) == 0;
 }
 
+static int macroexpand_one(int attr_nr, int rem);
+
 static int fill_one(const char *what, struct match_attr *a, int rem)
 {
 	struct git_attr_check *check = check_all_attr;
 	int i;
 
-	for (i = 0; 0 < rem && i < a->num_attr; i++) {
+	for (i = a->num_attr - 1; 0 < rem && 0 <= i; i--) {
 		struct git_attr *attr = a->state[i].attr;
 		const char **n = &(check[attr->attr_nr].value);
 		const char *v = a->state[i].setto;
 
 		if (*n == ATTR__UNKNOWN) {
-			debug_set(what, a->u.pattern, attr, v);
+			debug_set(what,
+				  a->is_macro ? a->u.attr->name : a->u.pattern,
+				  attr, v);
 			*n = v;
 			rem--;
+			rem = macroexpand_one(attr->attr_nr, rem);
 		}
 	}
 	return rem;
@@ -624,45 +704,54 @@
 	return rem;
 }
 
-static int macroexpand(struct attr_stack *stk, int rem)
+static int macroexpand_one(int attr_nr, int rem)
 {
+	struct attr_stack *stk;
+	struct match_attr *a = NULL;
 	int i;
-	struct git_attr_check *check = check_all_attr;
 
-	for (i = stk->num_matches - 1; 0 < rem && 0 <= i; i--) {
-		struct match_attr *a = stk->attrs[i];
-		if (!a->is_macro)
-			continue;
-		if (check[a->u.attr->attr_nr].value != ATTR__TRUE)
-			continue;
+	if (check_all_attr[attr_nr].value != ATTR__TRUE)
+		return rem;
+
+	for (stk = attr_stack; !a && stk; stk = stk->prev)
+		for (i = stk->num_matches - 1; !a && 0 <= i; i--) {
+			struct match_attr *ma = stk->attrs[i];
+			if (!ma->is_macro)
+				continue;
+			if (ma->u.attr->attr_nr == attr_nr)
+				a = ma;
+		}
+
+	if (a)
 		rem = fill_one("expand", a, rem);
-	}
+
 	return rem;
 }
 
-int git_checkattr(const char *path, int num, struct git_attr_check *check)
+/*
+ * Collect all attributes for path into the array pointed to by
+ * check_all_attr.
+ */
+static void collect_all_attrs(const char *path)
 {
 	struct attr_stack *stk;
-	const char *cp;
-	int dirlen, pathlen, i, rem;
+	int i, pathlen, rem;
 
-	bootstrap_attr_stack();
+	prepare_attr_stack(path);
 	for (i = 0; i < attr_nr; i++)
 		check_all_attr[i].value = ATTR__UNKNOWN;
 
 	pathlen = strlen(path);
-	cp = strrchr(path, '/');
-	if (!cp)
-		dirlen = 0;
-	else
-		dirlen = cp - path;
-	prepare_attr_stack(path, dirlen);
 	rem = attr_nr;
 	for (stk = attr_stack; 0 < rem && stk; stk = stk->prev)
 		rem = fill(path, pathlen, stk, rem);
+}
 
-	for (stk = attr_stack; 0 < rem && stk; stk = stk->prev)
-		rem = macroexpand(stk, rem);
+int git_check_attr(const char *path, int num, struct git_attr_check *check)
+{
+	int i;
+
+	collect_all_attrs(path);
 
 	for (i = 0; i < num; i++) {
 		const char *value = check_all_attr[check[i].attr->attr_nr].value;
@@ -674,6 +763,34 @@
 	return 0;
 }
 
+int git_all_attrs(const char *path, int *num, struct git_attr_check **check)
+{
+	int i, count, j;
+
+	collect_all_attrs(path);
+
+	/* Count the number of attributes that are set. */
+	count = 0;
+	for (i = 0; i < attr_nr; i++) {
+		const char *value = check_all_attr[i].value;
+		if (value != ATTR__UNSET && value != ATTR__UNKNOWN)
+			++count;
+	}
+	*num = count;
+	*check = xmalloc(sizeof(**check) * count);
+	j = 0;
+	for (i = 0; i < attr_nr; i++) {
+		const char *value = check_all_attr[i].value;
+		if (value != ATTR__UNSET && value != ATTR__UNKNOWN) {
+			(*check)[j].attr = check_all_attr[i].attr;
+			(*check)[j].value = value;
+			++j;
+		}
+	}
+
+	return 0;
+}
+
 void git_attr_set_direction(enum git_attr_direction new, struct index_state *istate)
 {
 	enum git_attr_direction old = direction;
diff --git a/attr.h b/attr.h
index 69b5767..8b08d33 100644
--- a/attr.h
+++ b/attr.h
@@ -8,7 +8,7 @@
  * Given a string, return the gitattribute object that
  * corresponds to it.
  */
-struct git_attr *git_attr(const char *, int);
+struct git_attr *git_attr(const char *);
 
 /* Internal use */
 extern const char git_attr__true[];
@@ -20,7 +20,7 @@
 #define ATTR_UNSET(v) ((v) == NULL)
 
 /*
- * Send one or more git_attr_check to git_checkattr(), and
+ * Send one or more git_attr_check to git_check_attr(), and
  * each 'value' member tells what its value is.
  * Unset one is returned as NULL.
  */
@@ -29,12 +29,28 @@
 	const char *value;
 };
 
-int git_checkattr(const char *path, int, struct git_attr_check *);
+/*
+ * Return the name of the attribute represented by the argument.  The
+ * return value is a pointer to a null-delimited string that is part
+ * of the internal data structure; it should not be modified or freed.
+ */
+char *git_attr_name(struct git_attr *);
+
+int git_check_attr(const char *path, int, struct git_attr_check *);
+
+/*
+ * Retrieve all attributes that apply to the specified path.  *num
+ * will be set to the number of attributes on the path; **check will
+ * be set to point at a newly-allocated array of git_attr_check
+ * objects describing the attributes and their values.  *check must be
+ * free()ed by the caller.
+ */
+int git_all_attrs(const char *path, int *num, struct git_attr_check **check);
 
 enum git_attr_direction {
 	GIT_ATTR_CHECKIN,
 	GIT_ATTR_CHECKOUT,
-	GIT_ATTR_INDEX,
+	GIT_ATTR_INDEX
 };
 void git_attr_set_direction(enum git_attr_direction, struct index_state *);
 
diff --git a/base85.c b/base85.c
index b417a15..781b575 100644
--- a/base85.c
+++ b/base85.c
@@ -7,9 +7,9 @@
 #define say1(a,b) fprintf(stderr, a, b)
 #define say2(a,b,c) fprintf(stderr, a, b, c)
 #else
-#define say(a) do {} while(0)
-#define say1(a,b) do {} while(0)
-#define say2(a,b,c) do {} while(0)
+#define say(a) do { /* nothing */ } while (0)
+#define say1(a,b) do { /* nothing */ } while (0)
+#define say2(a,b,c) do { /* nothing */ } while (0)
 #endif
 
 static const char en85[] = {
@@ -57,14 +57,8 @@
 		de = de85[ch];
 		if (--de < 0)
 			return error("invalid base85 alphabet %c", ch);
-		/*
-		 * Detect overflow.  The largest
-		 * 5-letter possible is "|NsC0" to
-		 * encode 0xffffffff, and "|NsC" gives
-		 * 0x03030303 at this point (i.e.
-		 * 0xffffffff = 0x03030303 * 85).
-		 */
-		if (0x03030303 < acc ||
+		/* Detect overflow. */
+		if (0xffffffff / 85 < acc ||
 		    0xffffffff - de < (acc *= 85))
 			return error("invalid base85 sequence %.5s", buffer-5);
 		acc += de;
@@ -84,8 +78,6 @@
 
 void encode_85(char *buf, const unsigned char *data, int bytes)
 {
-	prep_base85();
-
 	say("encode 85");
 	while (bytes) {
 		unsigned acc = 0;
@@ -118,7 +110,7 @@
 		int len = strlen(av[2]);
 		encode_85(buf, av[2], len);
 		if (len <= 26) len = len + 'A' - 1;
-		else len = len + 'a' - 26 + 1;
+		else len = len + 'a' - 26 - 1;
 		printf("encoded: %c%s\n", len, buf);
 		return 0;
 	}
diff --git a/bisect.c b/bisect.c
index 7f20acb..48acf73 100644
--- a/bisect.c
+++ b/bisect.c
@@ -9,27 +9,17 @@
 #include "run-command.h"
 #include "log-tree.h"
 #include "bisect.h"
-
-struct sha1_array {
-	unsigned char (*sha1)[20];
-	int sha1_nr;
-	int sha1_alloc;
-	int sorted;
-};
+#include "sha1-array.h"
+#include "argv-array.h"
 
 static struct sha1_array good_revs;
 static struct sha1_array skipped_revs;
 
 static const unsigned char *current_bad_sha1;
 
-struct argv_array {
-	const char **argv;
-	int argv_nr;
-	int argv_alloc;
-};
-
 static const char *argv_checkout[] = {"checkout", "-q", NULL, "--", NULL};
 static const char *argv_show_branch[] = {"show-branch", NULL, NULL};
+static const char *argv_update_ref[] = {"update-ref", "--no-deref", "BISECT_HEAD", NULL, NULL};
 
 /* bits #0-15 in revision.h */
 
@@ -141,7 +131,8 @@
 		enum object_type type;
 		unsigned long size;
 		char *buf = read_sha1_file(commit->object.sha1, &type, &size);
-		char *ep, *sp;
+		const char *subject_start;
+		int subject_len;
 
 		fprintf(stderr, "%c%c%c ",
 			(flags & TREESAME) ? ' ' : 'T',
@@ -156,13 +147,9 @@
 			fprintf(stderr, " %.*s", 8,
 				sha1_to_hex(pp->item->object.sha1));
 
-		sp = strstr(buf, "\n\n");
-		if (sp) {
-			sp += 2;
-			for (ep = sp; *ep && *ep != '\n'; ep++)
-				;
-			fprintf(stderr, " %.*s", (int)(ep - sp), sp);
-		}
+		subject_len = find_commit_subject(buf, &subject_start);
+		if (subject_len)
+			fprintf(stderr, " %.*s", subject_len, subject_start);
 		fprintf(stderr, "\n");
 	}
 }
@@ -413,37 +400,15 @@
 	return best;
 }
 
-static void argv_array_push(struct argv_array *array, const char *string)
-{
-	ALLOC_GROW(array->argv, array->argv_nr + 1, array->argv_alloc);
-	array->argv[array->argv_nr++] = string;
-}
-
-static void argv_array_push_sha1(struct argv_array *array,
-				 const unsigned char *sha1,
-				 const char *format)
-{
-	struct strbuf buf = STRBUF_INIT;
-	strbuf_addf(&buf, format, sha1_to_hex(sha1));
-	argv_array_push(array, strbuf_detach(&buf, NULL));
-}
-
-static void sha1_array_push(struct sha1_array *array,
-			    const unsigned char *sha1)
-{
-	ALLOC_GROW(array->sha1, array->sha1_nr + 1, array->sha1_alloc);
-	hashcpy(array->sha1[array->sha1_nr++], sha1);
-}
-
 static int register_ref(const char *refname, const unsigned char *sha1,
 			int flags, void *cb_data)
 {
 	if (!strcmp(refname, "bad")) {
 		current_bad_sha1 = sha1;
 	} else if (!prefixcmp(refname, "good-")) {
-		sha1_array_push(&good_revs, sha1);
+		sha1_array_append(&good_revs, sha1);
 	} else if (!prefixcmp(refname, "skip-")) {
-		sha1_array_push(&skipped_revs, sha1);
+		sha1_array_append(&skipped_revs, sha1);
 	}
 
 	return 0;
@@ -464,57 +429,24 @@
 		die_errno("Could not open file '%s'", filename);
 
 	while (strbuf_getline(&str, fp, '\n') != EOF) {
-		char *quoted;
-		int res;
-
 		strbuf_trim(&str);
-		quoted = strbuf_detach(&str, NULL);
-		res = sq_dequote_to_argv(quoted, &array->argv,
-					 &array->argv_nr, &array->argv_alloc);
-		if (res)
+		if (sq_dequote_to_argv_array(str.buf, array))
 			die("Badly quoted content in file '%s': %s",
-			    filename, quoted);
+			    filename, str.buf);
 	}
 
 	strbuf_release(&str);
 	fclose(fp);
 }
 
-static int array_cmp(const void *a, const void *b)
-{
-	return hashcmp(a, b);
-}
-
-static void sort_sha1_array(struct sha1_array *array)
-{
-	qsort(array->sha1, array->sha1_nr, sizeof(*array->sha1), array_cmp);
-
-	array->sorted = 1;
-}
-
-static const unsigned char *sha1_access(size_t index, void *table)
-{
-	unsigned char (*array)[20] = table;
-	return array[index];
-}
-
-static int lookup_sha1_array(struct sha1_array *array,
-			     const unsigned char *sha1)
-{
-	if (!array->sorted)
-		sort_sha1_array(array);
-
-	return sha1_pos(sha1, array->sha1, array->sha1_nr, sha1_access);
-}
-
 static char *join_sha1_array_hex(struct sha1_array *array, char delim)
 {
 	struct strbuf joined_hexs = STRBUF_INIT;
 	int i;
 
-	for (i = 0; i < array->sha1_nr; i++) {
+	for (i = 0; i < array->nr; i++) {
 		strbuf_addstr(&joined_hexs, sha1_to_hex(array->sha1[i]));
-		if (i + 1 < array->sha1_nr)
+		if (i + 1 < array->nr)
 			strbuf_addch(&joined_hexs, delim);
 	}
 
@@ -549,13 +481,13 @@
 	if (count)
 		*count = 0;
 
-	if (!skipped_revs.sha1_nr)
+	if (!skipped_revs.nr)
 		return list;
 
 	while (list) {
 		struct commit_list *next = list->next;
 		list->next = NULL;
-		if (0 <= lookup_sha1_array(&skipped_revs,
+		if (0 <= sha1_array_lookup(&skipped_revs,
 					   list->item->object.sha1)) {
 			if (skipped_first && !*skipped_first)
 				*skipped_first = 1;
@@ -593,7 +525,7 @@
  * is increased by one between each call, but that should not matter
  * for this application.
  */
-int get_prn(int count) {
+static int get_prn(int count) {
 	count = count * 1103515245 + 12345;
 	return ((unsigned)(count/65536) % PRN_MODULO);
 }
@@ -650,7 +582,7 @@
 
 	*tried = NULL;
 
-	if (!skipped_revs.sha1_nr)
+	if (!skipped_revs.nr)
 		return list;
 
 	list = filter_skipped(list, tried, 0, &count, &skipped_first);
@@ -665,7 +597,7 @@
 			     const char *bad_format, const char *good_format,
 			     int read_paths)
 {
-	struct argv_array rev_argv = { NULL, 0, 0 };
+	struct argv_array rev_argv = ARGV_ARRAY_INIT;
 	int i;
 
 	init_revisions(revs, prefix);
@@ -673,17 +605,17 @@
 	revs->commit_format = CMIT_FMT_UNSPECIFIED;
 
 	/* rev_argv.argv[0] will be ignored by setup_revisions */
-	argv_array_push(&rev_argv, xstrdup("bisect_rev_setup"));
-	argv_array_push_sha1(&rev_argv, current_bad_sha1, bad_format);
-	for (i = 0; i < good_revs.sha1_nr; i++)
-		argv_array_push_sha1(&rev_argv, good_revs.sha1[i],
-				     good_format);
-	argv_array_push(&rev_argv, xstrdup("--"));
+	argv_array_push(&rev_argv, "bisect_rev_setup");
+	argv_array_pushf(&rev_argv, bad_format, sha1_to_hex(current_bad_sha1));
+	for (i = 0; i < good_revs.nr; i++)
+		argv_array_pushf(&rev_argv, good_format,
+				 sha1_to_hex(good_revs.sha1[i]));
+	argv_array_push(&rev_argv, "--");
 	if (read_paths)
 		read_bisect_paths(&rev_argv);
-	argv_array_push(&rev_argv, NULL);
 
-	setup_revisions(rev_argv.argv_nr, rev_argv.argv, revs, NULL);
+	setup_revisions(rev_argv.argc, rev_argv.argv, revs, NULL);
+	/* XXX leak rev_argv, as "revs" may still be pointing to it */
 }
 
 static void bisect_common(struct rev_info *revs)
@@ -750,16 +682,23 @@
 		die("closing file %s: %s", filename, strerror(errno));
 }
 
-static int bisect_checkout(char *bisect_rev_hex)
+static int bisect_checkout(char *bisect_rev_hex, int no_checkout)
 {
 	int res;
 
 	mark_expected_rev(bisect_rev_hex);
 
 	argv_checkout[2] = bisect_rev_hex;
-	res = run_command_v_opt(argv_checkout, RUN_GIT_CMD);
-	if (res)
-		exit(res);
+	if (no_checkout) {
+		argv_update_ref[3] = bisect_rev_hex;
+		if (run_command_v_opt(argv_update_ref, RUN_GIT_CMD))
+			die("update-ref --no-deref HEAD failed on %s",
+			    bisect_rev_hex);
+	} else {
+		res = run_command_v_opt(argv_checkout, RUN_GIT_CMD);
+		if (res)
+			exit(res);
+	}
 
 	argv_show_branch[1] = bisect_rev_hex;
 	return run_command_v_opt(argv_show_branch, RUN_GIT_CMD);
@@ -775,12 +714,12 @@
 
 static struct commit **get_bad_and_good_commits(int *rev_nr)
 {
-	int len = 1 + good_revs.sha1_nr;
+	int len = 1 + good_revs.nr;
 	struct commit **rev = xmalloc(len * sizeof(*rev));
 	int i, n = 0;
 
 	rev[n++] = get_commit_reference(current_bad_sha1);
-	for (i = 0; i < good_revs.sha1_nr; i++)
+	for (i = 0; i < good_revs.nr; i++)
 		rev[n++] = get_commit_reference(good_revs.sha1[i]);
 	*rev_nr = n;
 
@@ -813,11 +752,11 @@
 	char *bad_hex = sha1_to_hex(current_bad_sha1);
 	char *good_hex = join_sha1_array_hex(&good_revs, ' ');
 
-	fprintf(stderr, "Warning: the merge base between %s and [%s] "
+	warning("the merge base between %s and [%s] "
 		"must be skipped.\n"
 		"So we cannot be sure the first bad commit is "
 		"between %s and %s.\n"
-		"We continue anyway.\n",
+		"We continue anyway.",
 		bad_hex, good_hex, mb_hex, bad_hex);
 	free(good_hex);
 }
@@ -831,7 +770,7 @@
  * - If one is "skipped", we can't know but we should warn.
  * - If we don't know, we should check it out and ask the user to test.
  */
-static void check_merge_bases(void)
+static void check_merge_bases(int no_checkout)
 {
 	struct commit_list *result;
 	int rev_nr;
@@ -843,13 +782,13 @@
 		const unsigned char *mb = result->item->object.sha1;
 		if (!hashcmp(mb, current_bad_sha1)) {
 			handle_bad_merge_base();
-		} else if (0 <= lookup_sha1_array(&good_revs, mb)) {
+		} else if (0 <= sha1_array_lookup(&good_revs, mb)) {
 			continue;
-		} else if (0 <= lookup_sha1_array(&skipped_revs, mb)) {
+		} else if (0 <= sha1_array_lookup(&skipped_revs, mb)) {
 			handle_skipped_merge_base(mb);
 		} else {
 			printf("Bisecting: a merge base must be tested\n");
-			exit(bisect_checkout(sha1_to_hex(mb)));
+			exit(bisect_checkout(sha1_to_hex(mb), no_checkout));
 		}
 	}
 
@@ -861,25 +800,25 @@
 {
 	struct rev_info revs;
 	struct object_array pending_copy;
-	int i, res;
+	int res;
 
 	bisect_rev_setup(&revs, prefix, "^%s", "%s", 0);
 
 	/* Save pending objects, so they can be cleaned up later. */
-	memset(&pending_copy, 0, sizeof(pending_copy));
-	for (i = 0; i < revs.pending.nr; i++)
-		add_object_array(revs.pending.objects[i].item,
-				 revs.pending.objects[i].name,
-				 &pending_copy);
+	pending_copy = revs.pending;
+	revs.leak_pending = 1;
 
+	/*
+	 * bisect_common calls prepare_revision_walk right away, which
+	 * (together with .leak_pending = 1) makes us the sole owner of
+	 * the list of pending objects.
+	 */
 	bisect_common(&revs);
 	res = (revs.commits != NULL);
 
 	/* Clean up objects used, as they will be reused. */
-	for (i = 0; i < pending_copy.nr; i++) {
-		struct object *o = pending_copy.objects[i].item;
-		clear_commit_marks((struct commit *)o, ALL_REV_FLAGS);
-	}
+	clear_commit_marks_for_object_array(&pending_copy, ALL_REV_FLAGS);
+	free(pending_copy.objects);
 
 	return res;
 }
@@ -892,9 +831,9 @@
  * If a merge base must be tested by the user, its source code will be
  * checked out to be tested by the user and we will exit.
  */
-static void check_good_are_ancestors_of_bad(const char *prefix)
+static void check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
 {
-	const char *filename = git_path("BISECT_ANCESTORS_OK");
+	char *filename = xstrdup(git_path("BISECT_ANCESTORS_OK"));
 	struct stat st;
 	int fd;
 
@@ -903,15 +842,15 @@
 
 	/* Check if file BISECT_ANCESTORS_OK exists. */
 	if (!stat(filename, &st) && S_ISREG(st.st_mode))
-		return;
+		goto done;
 
 	/* Bisecting with no good rev is ok. */
-	if (good_revs.sha1_nr == 0)
-		return;
+	if (good_revs.nr == 0)
+		goto done;
 
 	/* Check if all good revs are ancestor of the bad rev. */
 	if (check_ancestors(prefix))
-		check_merge_bases();
+		check_merge_bases(no_checkout);
 
 	/* Create file BISECT_ANCESTORS_OK. */
 	fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);
@@ -920,6 +859,8 @@
 			filename, strerror(errno));
 	else
 		close(fd);
+ done:
+	free(filename);
 }
 
 /*
@@ -951,19 +892,22 @@
  * We use the convention that exiting with an exit code 10 means that
  * the bisection process finished successfully.
  * In this case the calling shell script should exit 0.
+ *
+ * If no_checkout is non-zero, the bisection process does not
+ * checkout the trial commit but instead simply updates BISECT_HEAD.
  */
-int bisect_next_all(const char *prefix)
+int bisect_next_all(const char *prefix, int no_checkout)
 {
 	struct rev_info revs;
 	struct commit_list *tried;
-	int reaches = 0, all = 0, nr;
+	int reaches = 0, all = 0, nr, steps;
 	const unsigned char *bisect_rev;
 	char bisect_rev_hex[41];
 
 	if (read_bisect_refs())
 		die("reading bisect refs failed");
 
-	check_good_are_ancestors_of_bad(prefix);
+	check_good_are_ancestors_of_bad(prefix, no_checkout);
 
 	bisect_rev_setup(&revs, prefix, "%s", "^%s", 1);
 	revs.limited = 1;
@@ -971,7 +915,7 @@
 	bisect_common(&revs);
 
 	revs.commits = find_bisection(revs.commits, &reaches, &all,
-				       !!skipped_revs.sha1_nr);
+				       !!skipped_revs.nr);
 	revs.commits = managed_skipped(revs.commits, &tried);
 
 	if (!revs.commits) {
@@ -986,21 +930,29 @@
 		exit(1);
 	}
 
+	if (!all) {
+		fprintf(stderr, "No testable commit found.\n"
+			"Maybe you started with bad path parameters?\n");
+		exit(4);
+	}
+
 	bisect_rev = revs.commits->item->object.sha1;
 	memcpy(bisect_rev_hex, sha1_to_hex(bisect_rev), 41);
 
 	if (!hashcmp(bisect_rev, current_bad_sha1)) {
 		exit_if_skipped_commits(tried, current_bad_sha1);
-		printf("%s is first bad commit\n", bisect_rev_hex);
+		printf("%s is the first bad commit\n", bisect_rev_hex);
 		show_diff_tree(prefix, revs.commits->item);
 		/* This means the bisection process succeeded. */
 		exit(10);
 	}
 
 	nr = all - reaches - 1;
-	printf("Bisecting: %d revisions left to test after this "
-	       "(roughly %d steps)\n", nr, estimate_bisect_steps(all));
+	steps = estimate_bisect_steps(all);
+	printf("Bisecting: %d revision%s left to test after this "
+	       "(roughly %d step%s)\n", nr, (nr == 1 ? "" : "s"),
+	       steps, (steps == 1 ? "" : "s"));
 
-	return bisect_checkout(bisect_rev_hex);
+	return bisect_checkout(bisect_rev_hex, no_checkout);
 }
 
diff --git a/bisect.h b/bisect.h
index 82f8fc1..ec3c3ff 100644
--- a/bisect.h
+++ b/bisect.h
@@ -15,21 +15,18 @@
 			      const char *format_cur,
 			      const char *format_last);
 
-/* bisect_show_flags flags in struct rev_list_info */
 #define BISECT_SHOW_ALL		(1<<0)
-#define BISECT_SHOW_TRIED	(1<<1)
+#define REV_LIST_QUIET		(1<<1)
 
 struct rev_list_info {
 	struct rev_info *revs;
-	int bisect_show_flags;
+	int flags;
 	int show_timestamp;
 	int hdr_termination;
 	const char *header_prefix;
 };
 
-extern int show_bisect_vars(struct rev_list_info *info, int reaches, int all);
-
-extern int bisect_next_all(const char *prefix);
+extern int bisect_next_all(const char *prefix, int no_checkout);
 
 extern int estimate_bisect_steps(int all);
 
diff --git a/blob.c b/blob.c
index bd7d078..ae320bd 100644
--- a/blob.c
+++ b/blob.c
@@ -23,24 +23,3 @@
 	item->object.parsed = 1;
 	return 0;
 }
-
-int parse_blob(struct blob *item)
-{
-        enum object_type type;
-        void *buffer;
-        unsigned long size;
-	int ret;
-
-        if (item->object.parsed)
-                return 0;
-        buffer = read_sha1_file(item->object.sha1, &type, &size);
-        if (!buffer)
-                return error("Could not read %s",
-                             sha1_to_hex(item->object.sha1));
-        if (type != OBJ_BLOB)
-                return error("Object %s not a blob",
-                             sha1_to_hex(item->object.sha1));
-	ret = parse_blob_buffer(item, buffer, size);
-	free(buffer);
-	return ret;
-}
diff --git a/blob.h b/blob.h
index ea5d9e9..59b394e 100644
--- a/blob.h
+++ b/blob.h
@@ -13,6 +13,13 @@
 
 int parse_blob_buffer(struct blob *item, void *buffer, unsigned long size);
 
-int parse_blob(struct blob *item);
+/**
+ * Blobs do not contain references to other objects and do not have
+ * structured data that needs parsing. However, code may use the
+ * "parsed" bit in the struct object for a blob to determine whether
+ * its content has been found to actually be available, so
+ * parse_blob_buffer() is used (by object.c) to flag that the object
+ * has been read successfully from the database.
+ **/
 
 #endif /* BLOB_H */
diff --git a/block-sha1/sha1.c b/block-sha1/sha1.c
index 6f885c4..a8d4bf9 100644
--- a/block-sha1/sha1.c
+++ b/block-sha1/sha1.c
@@ -70,6 +70,7 @@
  */
 
 #if defined(__i386__) || defined(__x86_64__) || \
+    defined(_M_IX86) || defined(_M_X64) || \
     defined(__ppc__) || defined(__ppc64__) || \
     defined(__powerpc__) || defined(__powerpc64__) || \
     defined(__s390__) || defined(__s390x__)
@@ -236,13 +237,13 @@
 
 void blk_SHA1_Update(blk_SHA_CTX *ctx, const void *data, unsigned long len)
 {
-	int lenW = ctx->size & 63;
+	unsigned int lenW = ctx->size & 63;
 
 	ctx->size += len;
 
 	/* Read the data into W and process blocks as they get full */
 	if (lenW) {
-		int left = 64 - lenW;
+		unsigned int left = 64 - lenW;
 		if (len < left)
 			left = len;
 		memcpy(lenW + (char *)ctx->W, data, left);
@@ -269,8 +270,8 @@
 	int i;
 
 	/* Pad with a binary 1 (ie 0x80), then zeroes, then length */
-	padlen[0] = htonl(ctx->size >> 29);
-	padlen[1] = htonl(ctx->size << 3);
+	padlen[0] = htonl((uint32_t)(ctx->size >> 29));
+	padlen[1] = htonl((uint32_t)(ctx->size << 3));
 
 	i = ctx->size & 63;
 	blk_SHA1_Update(ctx, pad, 1+ (63 & (55 - i)));
diff --git a/branch.c b/branch.c
index 05ef3f5..2bef1e7 100644
--- a/branch.c
+++ b/branch.c
@@ -49,9 +49,19 @@
 
 void install_branch_config(int flag, const char *local, const char *origin, const char *remote)
 {
+	const char *shortname = remote + 11;
+	int remote_is_branch = !prefixcmp(remote, "refs/heads/");
 	struct strbuf key = STRBUF_INIT;
 	int rebasing = should_setup_rebase(origin);
 
+	if (remote_is_branch
+	    && !strcmp(local, shortname)
+	    && !origin) {
+		warning("Not setting branch %s as its own upstream.",
+			local);
+		return;
+	}
+
 	strbuf_addf(&key, "branch.%s.remote", local);
 	git_config_set(key.buf, origin ? origin : ".");
 
@@ -64,25 +74,33 @@
 		strbuf_addf(&key, "branch.%s.rebase", local);
 		git_config_set(key.buf, "true");
 	}
+	strbuf_release(&key);
 
 	if (flag & BRANCH_CONFIG_VERBOSE) {
-		strbuf_reset(&key);
-
-		strbuf_addstr(&key, origin ? "remote" : "local");
-
-		/* Are we tracking a proper "branch"? */
-		if (!prefixcmp(remote, "refs/heads/")) {
-			strbuf_addf(&key, " branch %s", remote + 11);
-			if (origin)
-				strbuf_addf(&key, " from %s", origin);
-		}
+		if (remote_is_branch && origin)
+			printf(rebasing ?
+			       "Branch %s set up to track remote branch %s from %s by rebasing.\n" :
+			       "Branch %s set up to track remote branch %s from %s.\n",
+			       local, shortname, origin);
+		else if (remote_is_branch && !origin)
+			printf(rebasing ?
+			       "Branch %s set up to track local branch %s by rebasing.\n" :
+			       "Branch %s set up to track local branch %s.\n",
+			       local, shortname);
+		else if (!remote_is_branch && origin)
+			printf(rebasing ?
+			       "Branch %s set up to track remote ref %s by rebasing.\n" :
+			       "Branch %s set up to track remote ref %s.\n",
+			       local, remote);
+		else if (!remote_is_branch && !origin)
+			printf(rebasing ?
+			       "Branch %s set up to track local ref %s by rebasing.\n" :
+			       "Branch %s set up to track local ref %s.\n",
+			       local, remote);
 		else
-			strbuf_addf(&key, " ref %s", remote);
-		printf("Branch %s set up to track %s%s.\n",
-		       local, key.buf,
-		       rebasing ? " by rebasing" : "");
+			die("BUG: impossible combination of %d and %p",
+			    remote_is_branch, origin);
 	}
-	strbuf_release(&key);
 }
 
 /*
@@ -91,9 +109,10 @@
  * config.
  */
 static int setup_tracking(const char *new_ref, const char *orig_ref,
-                          enum branch_track track)
+			  enum branch_track track, int quiet)
 {
 	struct tracking tracking;
+	int config_flags = quiet ? 0 : BRANCH_CONFIG_VERBOSE;
 
 	if (strlen(new_ref) > 1024 - 7 - 7 - 1)
 		return error("Tracking not set up: name too long: %s",
@@ -108,6 +127,7 @@
 		switch (track) {
 		case BRANCH_TRACK_ALWAYS:
 		case BRANCH_TRACK_EXPLICIT:
+		case BRANCH_TRACK_OVERRIDE:
 			break;
 		default:
 			return 1;
@@ -117,33 +137,90 @@
 		return error("Not tracking: ambiguous information for ref %s",
 				orig_ref);
 
-	install_branch_config(BRANCH_CONFIG_VERBOSE, new_ref, tracking.remote,
+	install_branch_config(config_flags, new_ref, tracking.remote,
 			      tracking.src ? tracking.src : orig_ref);
 
 	free(tracking.src);
 	return 0;
 }
 
+struct branch_desc_cb {
+	const char *config_name;
+	const char *value;
+};
+
+static int read_branch_desc_cb(const char *var, const char *value, void *cb)
+{
+	struct branch_desc_cb *desc = cb;
+	if (strcmp(desc->config_name, var))
+		return 0;
+	free((char *)desc->value);
+	return git_config_string(&desc->value, var, value);
+}
+
+int read_branch_desc(struct strbuf *buf, const char *branch_name)
+{
+	struct branch_desc_cb cb;
+	struct strbuf name = STRBUF_INIT;
+	strbuf_addf(&name, "branch.%s.description", branch_name);
+	cb.config_name = name.buf;
+	cb.value = NULL;
+	if (git_config(read_branch_desc_cb, &cb) < 0) {
+		strbuf_release(&name);
+		return -1;
+	}
+	if (cb.value)
+		strbuf_addstr(buf, cb.value);
+	strbuf_release(&name);
+	return 0;
+}
+
+int validate_new_branchname(const char *name, struct strbuf *ref,
+			    int force, int attr_only)
+{
+	if (strbuf_check_branch_ref(ref, name))
+		die("'%s' is not a valid branch name.", name);
+
+	if (!ref_exists(ref->buf))
+		return 0;
+	else if (!force && !attr_only)
+		die("A branch named '%s' already exists.", ref->buf + strlen("refs/heads/"));
+
+	if (!attr_only) {
+		const char *head;
+		unsigned char sha1[20];
+
+		head = resolve_ref_unsafe("HEAD", sha1, 0, NULL);
+		if (!is_bare_repository() && head && !strcmp(head, ref->buf))
+			die("Cannot force update the current branch.");
+	}
+	return 1;
+}
+
 void create_branch(const char *head,
 		   const char *name, const char *start_name,
-		   int force, int reflog, enum branch_track track)
+		   int force, int reflog, int clobber_head,
+		   int quiet, enum branch_track track)
 {
-	struct ref_lock *lock;
+	struct ref_lock *lock = NULL;
 	struct commit *commit;
 	unsigned char sha1[20];
 	char *real_ref, msg[PATH_MAX + 20];
 	struct strbuf ref = STRBUF_INIT;
 	int forcing = 0;
+	int dont_change_ref = 0;
+	int explicit_tracking = 0;
 
-	if (strbuf_check_branch_ref(&ref, name))
-		die("'%s' is not a valid branch name.", name);
+	if (track == BRANCH_TRACK_EXPLICIT || track == BRANCH_TRACK_OVERRIDE)
+		explicit_tracking = 1;
 
-	if (resolve_ref(ref.buf, sha1, 1, NULL)) {
+	if (validate_new_branchname(name, &ref, force,
+				    track == BRANCH_TRACK_OVERRIDE ||
+				    clobber_head)) {
 		if (!force)
-			die("A branch named '%s' already exists.", name);
-		else if (!is_bare_repository() && !strcmp(head, name))
-			die("Cannot force update the current branch.");
-		forcing = 1;
+			dont_change_ref = 1;
+		else
+			forcing = 1;
 	}
 
 	real_ref = NULL;
@@ -153,13 +230,18 @@
 	switch (dwim_ref(start_name, strlen(start_name), sha1, &real_ref)) {
 	case 0:
 		/* Not branching from any existing branch */
-		if (track == BRANCH_TRACK_EXPLICIT)
+		if (explicit_tracking)
 			die("Cannot setup tracking information; starting point is not a branch.");
 		break;
 	case 1:
-		/* Unique completion -- good, only if it is a real ref */
-		if (track == BRANCH_TRACK_EXPLICIT && !strcmp(real_ref, "HEAD"))
-			die("Cannot setup tracking information; starting point is not a branch.");
+		/* Unique completion -- good, only if it is a real branch */
+		if (prefixcmp(real_ref, "refs/heads/") &&
+		    prefixcmp(real_ref, "refs/remotes/")) {
+			if (explicit_tracking)
+				die("Cannot setup tracking information; starting point is not a branch.");
+			else
+				real_ref = NULL;
+		}
 		break;
 	default:
 		die("Ambiguous object name: '%s'.", start_name);
@@ -170,25 +252,28 @@
 		die("Not a valid branch point: '%s'.", start_name);
 	hashcpy(sha1, commit->object.sha1);
 
-	lock = lock_any_ref_for_update(ref.buf, NULL, 0);
-	if (!lock)
-		die_errno("Failed to lock ref for update");
+	if (!dont_change_ref) {
+		lock = lock_any_ref_for_update(ref.buf, NULL, 0);
+		if (!lock)
+			die_errno("Failed to lock ref for update");
+	}
 
 	if (reflog)
 		log_all_ref_updates = 1;
 
 	if (forcing)
-		snprintf(msg, sizeof msg, "branch: Reset from %s",
+		snprintf(msg, sizeof msg, "branch: Reset to %s",
 			 start_name);
-	else
+	else if (!dont_change_ref)
 		snprintf(msg, sizeof msg, "branch: Created from %s",
 			 start_name);
 
 	if (real_ref && track)
-		setup_tracking(name, real_ref, track);
+		setup_tracking(ref.buf+11, real_ref, track, quiet);
 
-	if (write_ref_sha1(lock, sha1, msg) < 0)
-		die_errno("Failed to write ref");
+	if (!dont_change_ref)
+		if (write_ref_sha1(lock, sha1, msg) < 0)
+			die_errno("Failed to write ref");
 
 	strbuf_release(&ref);
 	free(real_ref);
@@ -196,6 +281,8 @@
 
 void remove_branch_state(void)
 {
+	unlink(git_path("CHERRY_PICK_HEAD"));
+	unlink(git_path("REVERT_HEAD"));
 	unlink(git_path("MERGE_HEAD"));
 	unlink(git_path("MERGE_RR"));
 	unlink(git_path("MERGE_MSG"));
diff --git a/branch.h b/branch.h
index eed817a..64173ab 100644
--- a/branch.h
+++ b/branch.h
@@ -13,7 +13,26 @@
  * branch for (if any).
  */
 void create_branch(const char *head, const char *name, const char *start_name,
-		   int force, int reflog, enum branch_track track);
+		   int force, int reflog,
+		   int clobber_head, int quiet, enum branch_track track);
+
+/*
+ * Validates that the requested branch may be created, returning the
+ * interpreted ref in ref, force indicates whether (non-head) branches
+ * may be overwritten. A non-zero return value indicates that the force
+ * parameter was non-zero and the branch already exists.
+ *
+ * Contrary to all of the above, when attr_only is 1, the caller is
+ * not interested in verifying if it is Ok to update the named
+ * branch to point at a potentially different commit. It is merely
+ * asking if it is OK to change some attribute for the named branch
+ * (e.g. tracking upstream).
+ *
+ * NEEDSWORK: This needs to be split into two separate functions in the
+ * longer run for sanity.
+ *
+ */
+int validate_new_branchname(const char *name, struct strbuf *ref, int force, int attr_only);
 
 /*
  * Remove information about the state of working on the current
@@ -22,10 +41,15 @@
 void remove_branch_state(void);
 
 /*
- * Configure local branch "local" to merge remote branch "remote"
- * taken from origin "origin".
+ * Configure local branch "local" as downstream to branch "remote"
+ * from remote "origin".  Used by git branch --set-upstream.
  */
 #define BRANCH_CONFIG_VERBOSE 01
 extern void install_branch_config(int flag, const char *local, const char *origin, const char *remote);
 
+/*
+ * Read branch description
+ */
+extern int read_branch_desc(struct strbuf *, const char *branch_name);
+
 #endif
diff --git a/builtin-add.c b/builtin-add.c
deleted file mode 100644
index 581a2a1..0000000
--- a/builtin-add.c
+++ /dev/null
@@ -1,351 +0,0 @@
-/*
- * "git add" builtin command
- *
- * Copyright (C) 2006 Linus Torvalds
- */
-#include "cache.h"
-#include "builtin.h"
-#include "dir.h"
-#include "exec_cmd.h"
-#include "cache-tree.h"
-#include "run-command.h"
-#include "parse-options.h"
-#include "diff.h"
-#include "revision.h"
-
-static const char * const builtin_add_usage[] = {
-	"git add [options] [--] <filepattern>...",
-	NULL
-};
-static int patch_interactive, add_interactive, edit_interactive;
-static int take_worktree_changes;
-
-static void fill_pathspec_matches(const char **pathspec, char *seen, int specs)
-{
-	int num_unmatched = 0, i;
-
-	/*
-	 * Since we are walking the index as if we were walking the directory,
-	 * we have to mark the matched pathspec as seen; otherwise we will
-	 * mistakenly think that the user gave a pathspec that did not match
-	 * anything.
-	 */
-	for (i = 0; i < specs; i++)
-		if (!seen[i])
-			num_unmatched++;
-	if (!num_unmatched)
-		return;
-	for (i = 0; i < active_nr; i++) {
-		struct cache_entry *ce = active_cache[i];
-		match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen);
-	}
-}
-
-static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)
-{
-	char *seen;
-	int i, specs;
-	struct dir_entry **src, **dst;
-
-	for (specs = 0; pathspec[specs];  specs++)
-		/* nothing */;
-	seen = xcalloc(specs, 1);
-
-	src = dst = dir->entries;
-	i = dir->nr;
-	while (--i >= 0) {
-		struct dir_entry *entry = *src++;
-		if (match_pathspec(pathspec, entry->name, entry->len,
-				   prefix, seen))
-			*dst++ = entry;
-	}
-	dir->nr = dst - dir->entries;
-	fill_pathspec_matches(pathspec, seen, specs);
-
-	for (i = 0; i < specs; i++) {
-		if (!seen[i] && pathspec[i][0] && !file_exists(pathspec[i]))
-			die("pathspec '%s' did not match any files",
-					pathspec[i]);
-	}
-        free(seen);
-}
-
-static void treat_gitlinks(const char **pathspec)
-{
-	int i;
-
-	if (!pathspec || !*pathspec)
-		return;
-
-	for (i = 0; i < active_nr; i++) {
-		struct cache_entry *ce = active_cache[i];
-		if (S_ISGITLINK(ce->ce_mode)) {
-			int len = ce_namelen(ce), j;
-			for (j = 0; pathspec[j]; j++) {
-				int len2 = strlen(pathspec[j]);
-				if (len2 <= len || pathspec[j][len] != '/' ||
-				    memcmp(ce->name, pathspec[j], len))
-					continue;
-				if (len2 == len + 1)
-					/* strip trailing slash */
-					pathspec[j] = xstrndup(ce->name, len);
-				else
-					die ("Path '%s' is in submodule '%.*s'",
-						pathspec[j], len, ce->name);
-			}
-		}
-	}
-}
-
-static void refresh(int verbose, const char **pathspec)
-{
-	char *seen;
-	int i, specs;
-
-	for (specs = 0; pathspec[specs];  specs++)
-		/* nothing */;
-	seen = xcalloc(specs, 1);
-	refresh_index(&the_index, verbose ? REFRESH_SAY_CHANGED : REFRESH_QUIET,
-		      pathspec, seen);
-	for (i = 0; i < specs; i++) {
-		if (!seen[i])
-			die("pathspec '%s' did not match any files", pathspec[i]);
-	}
-        free(seen);
-}
-
-static const char **validate_pathspec(int argc, const char **argv, const char *prefix)
-{
-	const char **pathspec = get_pathspec(prefix, argv);
-
-	if (pathspec) {
-		const char **p;
-		for (p = pathspec; *p; p++) {
-			if (has_symlink_leading_path(*p, strlen(*p))) {
-				int len = prefix ? strlen(prefix) : 0;
-				die("'%s' is beyond a symbolic link", *p + len);
-			}
-		}
-	}
-
-	return pathspec;
-}
-
-int interactive_add(int argc, const char **argv, const char *prefix)
-{
-	int status, ac;
-	const char **args;
-	const char **pathspec = NULL;
-
-	if (argc) {
-		pathspec = validate_pathspec(argc, argv, prefix);
-		if (!pathspec)
-			return -1;
-	}
-
-	args = xcalloc(sizeof(const char *), (argc + 4));
-	ac = 0;
-	args[ac++] = "add--interactive";
-	if (patch_interactive)
-		args[ac++] = "--patch";
-	args[ac++] = "--";
-	if (argc) {
-		memcpy(&(args[ac]), pathspec, sizeof(const char *) * argc);
-		ac += argc;
-	}
-	args[ac] = NULL;
-
-	status = run_command_v_opt(args, RUN_GIT_CMD);
-	free(args);
-	return status;
-}
-
-static int edit_patch(int argc, const char **argv, const char *prefix)
-{
-	char *file = xstrdup(git_path("ADD_EDIT.patch"));
-	const char *apply_argv[] = { "apply", "--recount", "--cached",
-		file, NULL };
-	struct child_process child;
-	struct rev_info rev;
-	int out;
-	struct stat st;
-
-	git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
-
-	if (read_cache() < 0)
-		die ("Could not read the index");
-
-	init_revisions(&rev, prefix);
-	rev.diffopt.context = 7;
-
-	argc = setup_revisions(argc, argv, &rev, NULL);
-	rev.diffopt.output_format = DIFF_FORMAT_PATCH;
-	out = open(file, O_CREAT | O_WRONLY, 0644);
-	if (out < 0)
-		die ("Could not open '%s' for writing.", file);
-	rev.diffopt.file = fdopen(out, "w");
-	rev.diffopt.close_file = 1;
-	if (run_diff_files(&rev, 0))
-		die ("Could not write patch");
-
-	launch_editor(file, NULL, NULL);
-
-	if (stat(file, &st))
-		die_errno("Could not stat '%s'", file);
-	if (!st.st_size)
-		die("Empty patch. Aborted.");
-
-	memset(&child, 0, sizeof(child));
-	child.git_cmd = 1;
-	child.argv = apply_argv;
-	if (run_command(&child))
-		die ("Could not apply '%s'", file);
-
-	unlink(file);
-	return 0;
-}
-
-static struct lock_file lock_file;
-
-static const char ignore_error[] =
-"The following paths are ignored by one of your .gitignore files:\n";
-
-static int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0;
-static int ignore_add_errors, addremove, intent_to_add;
-
-static struct option builtin_add_options[] = {
-	OPT__DRY_RUN(&show_only),
-	OPT__VERBOSE(&verbose),
-	OPT_GROUP(""),
-	OPT_BOOLEAN('i', "interactive", &add_interactive, "interactive picking"),
-	OPT_BOOLEAN('p', "patch", &patch_interactive, "interactive patching"),
-	OPT_BOOLEAN('e', "edit", &edit_interactive, "edit current diff and apply"),
-	OPT_BOOLEAN('f', "force", &ignored_too, "allow adding otherwise ignored files"),
-	OPT_BOOLEAN('u', "update", &take_worktree_changes, "update tracked files"),
-	OPT_BOOLEAN('N', "intent-to-add", &intent_to_add, "record only the fact that the path will be added later"),
-	OPT_BOOLEAN('A', "all", &addremove, "add all, noticing removal of tracked files"),
-	OPT_BOOLEAN( 0 , "refresh", &refresh_only, "don't add, only refresh the index"),
-	OPT_BOOLEAN( 0 , "ignore-errors", &ignore_add_errors, "just skip files which cannot be added because of errors"),
-	OPT_END(),
-};
-
-static int add_config(const char *var, const char *value, void *cb)
-{
-	if (!strcasecmp(var, "add.ignore-errors")) {
-		ignore_add_errors = git_config_bool(var, value);
-		return 0;
-	}
-	return git_default_config(var, value, cb);
-}
-
-static int add_files(struct dir_struct *dir, int flags)
-{
-	int i, exit_status = 0;
-
-	if (dir->ignored_nr) {
-		fprintf(stderr, ignore_error);
-		for (i = 0; i < dir->ignored_nr; i++)
-			fprintf(stderr, "%s\n", dir->ignored[i]->name);
-		fprintf(stderr, "Use -f if you really want to add them.\n");
-		die("no files added");
-	}
-
-	for (i = 0; i < dir->nr; i++)
-		if (add_file_to_cache(dir->entries[i]->name, flags)) {
-			if (!ignore_add_errors)
-				die("adding files failed");
-			exit_status = 1;
-		}
-	return exit_status;
-}
-
-int cmd_add(int argc, const char **argv, const char *prefix)
-{
-	int exit_status = 0;
-	int newfd;
-	const char **pathspec;
-	struct dir_struct dir;
-	int flags;
-	int add_new_files;
-	int require_pathspec;
-
-	git_config(add_config, NULL);
-
-	argc = parse_options(argc, argv, prefix, builtin_add_options,
-			  builtin_add_usage, PARSE_OPT_KEEP_ARGV0);
-	if (patch_interactive)
-		add_interactive = 1;
-	if (add_interactive)
-		exit(interactive_add(argc - 1, argv + 1, prefix));
-
-	if (edit_interactive)
-		return(edit_patch(argc, argv, prefix));
-	argc--;
-	argv++;
-
-	if (addremove && take_worktree_changes)
-		die("-A and -u are mutually incompatible");
-	if ((addremove || take_worktree_changes) && !argc) {
-		static const char *here[2] = { ".", NULL };
-		argc = 1;
-		argv = here;
-	}
-
-	add_new_files = !take_worktree_changes && !refresh_only;
-	require_pathspec = !take_worktree_changes;
-
-	newfd = hold_locked_index(&lock_file, 1);
-
-	flags = ((verbose ? ADD_CACHE_VERBOSE : 0) |
-		 (show_only ? ADD_CACHE_PRETEND : 0) |
-		 (intent_to_add ? ADD_CACHE_INTENT : 0) |
-		 (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) |
-		 (!(addremove || take_worktree_changes)
-		  ? ADD_CACHE_IGNORE_REMOVAL : 0));
-
-	if (require_pathspec && argc == 0) {
-		fprintf(stderr, "Nothing specified, nothing added.\n");
-		fprintf(stderr, "Maybe you wanted to say 'git add .'?\n");
-		return 0;
-	}
-	pathspec = validate_pathspec(argc, argv, prefix);
-
-	if (read_cache() < 0)
-		die("index file corrupt");
-	treat_gitlinks(pathspec);
-
-	if (add_new_files) {
-		int baselen;
-
-		/* Set up the default git porcelain excludes */
-		memset(&dir, 0, sizeof(dir));
-		if (!ignored_too) {
-			dir.flags |= DIR_COLLECT_IGNORED;
-			setup_standard_excludes(&dir);
-		}
-
-		/* This picks up the paths that are not tracked */
-		baselen = fill_directory(&dir, pathspec);
-		if (pathspec)
-			prune_directory(&dir, pathspec, baselen);
-	}
-
-	if (refresh_only) {
-		refresh(verbose, pathspec);
-		goto finish;
-	}
-
-	exit_status |= add_files_to_cache(prefix, pathspec, flags);
-
-	if (add_new_files)
-		exit_status |= add_files(&dir, flags);
-
- finish:
-	if (active_cache_changed) {
-		if (write_cache(newfd, active_cache, active_nr) ||
-		    commit_locked_index(&lock_file))
-			die("Unable to write new index file");
-	}
-
-	return exit_status;
-}
diff --git a/builtin-apply.c b/builtin-apply.c
deleted file mode 100644
index 39dc96a..0000000
--- a/builtin-apply.c
+++ /dev/null
@@ -1,3482 +0,0 @@
-/*
- * apply.c
- *
- * Copyright (C) Linus Torvalds, 2005
- *
- * This applies patches on top of some (arbitrary) version of the SCM.
- *
- */
-#include "cache.h"
-#include "cache-tree.h"
-#include "quote.h"
-#include "blob.h"
-#include "delta.h"
-#include "builtin.h"
-#include "string-list.h"
-#include "dir.h"
-#include "parse-options.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.
- */
-static const char *prefix;
-static int prefix_length = -1;
-static int newfd = -1;
-
-static int unidiff_zero;
-static int p_value = 1;
-static int p_value_known;
-static int check_index;
-static int update_index;
-static int cached;
-static int diffstat;
-static int numstat;
-static int summary;
-static int check;
-static int apply = 1;
-static int apply_in_reverse;
-static int apply_with_reject;
-static int apply_verbosely;
-static int no_add;
-static const char *fake_ancestor;
-static int line_termination = '\n';
-static unsigned int p_context = UINT_MAX;
-static const char * const apply_usage[] = {
-	"git apply [options] [<patch>...]",
-	NULL
-};
-
-static enum ws_error_action {
-	nowarn_ws_error,
-	warn_on_ws_error,
-	die_on_ws_error,
-	correct_ws_error,
-} ws_error_action = warn_on_ws_error;
-static int whitespace_error;
-static int squelch_whitespace_errors = 5;
-static int applied_after_fixing_ws;
-static const char *patch_input_file;
-static const char *root;
-static int root_len;
-static int read_stdin = 1;
-static int options;
-
-static void parse_whitespace_option(const char *option)
-{
-	if (!option) {
-		ws_error_action = warn_on_ws_error;
-		return;
-	}
-	if (!strcmp(option, "warn")) {
-		ws_error_action = warn_on_ws_error;
-		return;
-	}
-	if (!strcmp(option, "nowarn")) {
-		ws_error_action = nowarn_ws_error;
-		return;
-	}
-	if (!strcmp(option, "error")) {
-		ws_error_action = die_on_ws_error;
-		return;
-	}
-	if (!strcmp(option, "error-all")) {
-		ws_error_action = die_on_ws_error;
-		squelch_whitespace_errors = 0;
-		return;
-	}
-	if (!strcmp(option, "strip") || !strcmp(option, "fix")) {
-		ws_error_action = correct_ws_error;
-		return;
-	}
-	die("unrecognized whitespace option '%s'", option);
-}
-
-static void set_default_whitespace_mode(const char *whitespace_option)
-{
-	if (!whitespace_option && !apply_default_whitespace)
-		ws_error_action = (apply ? warn_on_ws_error : nowarn_ws_error);
-}
-
-/*
- * For "diff-stat" like behaviour, we keep track of the biggest change
- * we've seen, and the longest filename. That allows us to do simple
- * scaling.
- */
-static int max_change, max_len;
-
-/*
- * Various "current state", notably line numbers and what
- * file (and how) we're patching right now.. The "is_xxxx"
- * things are flags, where -1 means "don't know yet".
- */
-static int linenr = 1;
-
-/*
- * This represents one "hunk" from a patch, starting with
- * "@@ -oldpos,oldlines +newpos,newlines @@" marker.  The
- * patch text is pointed at by patch, and its byte length
- * is stored in size.  leading and trailing are the number
- * of context lines.
- */
-struct fragment {
-	unsigned long leading, trailing;
-	unsigned long oldpos, oldlines;
-	unsigned long newpos, newlines;
-	const char *patch;
-	int size;
-	int rejected;
-	struct fragment *next;
-};
-
-/*
- * When dealing with a binary patch, we reuse "leading" field
- * to store the type of the binary hunk, either deflated "delta"
- * or deflated "literal".
- */
-#define binary_patch_method leading
-#define BINARY_DELTA_DEFLATED	1
-#define BINARY_LITERAL_DEFLATED 2
-
-/*
- * This represents a "patch" to a file, both metainfo changes
- * such as creation/deletion, filemode and content changes represented
- * as a series of fragments.
- */
-struct patch {
-	char *new_name, *old_name, *def_name;
-	unsigned int old_mode, new_mode;
-	int is_new, is_delete;	/* -1 = unknown, 0 = false, 1 = true */
-	int rejected;
-	unsigned ws_rule;
-	unsigned long deflate_origlen;
-	int lines_added, lines_deleted;
-	int score;
-	unsigned int is_toplevel_relative:1;
-	unsigned int inaccurate_eof:1;
-	unsigned int is_binary:1;
-	unsigned int is_copy:1;
-	unsigned int is_rename:1;
-	unsigned int recount:1;
-	struct fragment *fragments;
-	char *result;
-	size_t resultsize;
-	char old_sha1_prefix[41];
-	char new_sha1_prefix[41];
-	struct patch *next;
-};
-
-/*
- * A line in a file, len-bytes long (includes the terminating LF,
- * except for an incomplete line at the end if the file ends with
- * one), and its contents hashes to 'hash'.
- */
-struct line {
-	size_t len;
-	unsigned hash : 24;
-	unsigned flag : 8;
-#define LINE_COMMON     1
-};
-
-/*
- * This represents a "file", which is an array of "lines".
- */
-struct image {
-	char *buf;
-	size_t len;
-	size_t nr;
-	size_t alloc;
-	struct line *line_allocated;
-	struct line *line;
-};
-
-/*
- * Records filenames that have been touched, in order to handle
- * the case where more than one patches touch the same file.
- */
-
-static struct string_list fn_table;
-
-static uint32_t hash_line(const char *cp, size_t len)
-{
-	size_t i;
-	uint32_t h;
-	for (i = 0, h = 0; i < len; i++) {
-		if (!isspace(cp[i])) {
-			h = h * 3 + (cp[i] & 0xff);
-		}
-	}
-	return h;
-}
-
-static void add_line_info(struct image *img, const char *bol, size_t len, unsigned flag)
-{
-	ALLOC_GROW(img->line_allocated, img->nr + 1, img->alloc);
-	img->line_allocated[img->nr].len = len;
-	img->line_allocated[img->nr].hash = hash_line(bol, len);
-	img->line_allocated[img->nr].flag = flag;
-	img->nr++;
-}
-
-static void prepare_image(struct image *image, char *buf, size_t len,
-			  int prepare_linetable)
-{
-	const char *cp, *ep;
-
-	memset(image, 0, sizeof(*image));
-	image->buf = buf;
-	image->len = len;
-
-	if (!prepare_linetable)
-		return;
-
-	ep = image->buf + image->len;
-	cp = image->buf;
-	while (cp < ep) {
-		const char *next;
-		for (next = cp; next < ep && *next != '\n'; next++)
-			;
-		if (next < ep)
-			next++;
-		add_line_info(image, cp, next - cp, 0);
-		cp = next;
-	}
-	image->line = image->line_allocated;
-}
-
-static void clear_image(struct image *image)
-{
-	free(image->buf);
-	image->buf = NULL;
-	image->len = 0;
-}
-
-static void say_patch_name(FILE *output, const char *pre,
-			   struct patch *patch, const char *post)
-{
-	fputs(pre, output);
-	if (patch->old_name && patch->new_name &&
-	    strcmp(patch->old_name, patch->new_name)) {
-		quote_c_style(patch->old_name, NULL, output, 0);
-		fputs(" => ", output);
-		quote_c_style(patch->new_name, NULL, output, 0);
-	} else {
-		const char *n = patch->new_name;
-		if (!n)
-			n = patch->old_name;
-		quote_c_style(n, NULL, output, 0);
-	}
-	fputs(post, output);
-}
-
-#define CHUNKSIZE (8192)
-#define SLOP (16)
-
-static void read_patch_file(struct strbuf *sb, int fd)
-{
-	if (strbuf_read(sb, fd, 0) < 0)
-		die_errno("git apply: failed to read");
-
-	/*
-	 * Make sure that we have some slop in the buffer
-	 * so that we can do speculative "memcmp" etc, and
-	 * see to it that it is NUL-filled.
-	 */
-	strbuf_grow(sb, SLOP);
-	memset(sb->buf + sb->len, 0, SLOP);
-}
-
-static unsigned long linelen(const char *buffer, unsigned long size)
-{
-	unsigned long len = 0;
-	while (size--) {
-		len++;
-		if (*buffer++ == '\n')
-			break;
-	}
-	return len;
-}
-
-static int is_dev_null(const char *str)
-{
-	return !memcmp("/dev/null", str, 9) && isspace(str[9]);
-}
-
-#define TERM_SPACE	1
-#define TERM_TAB	2
-
-static int name_terminate(const char *name, int namelen, int c, int terminate)
-{
-	if (c == ' ' && !(terminate & TERM_SPACE))
-		return 0;
-	if (c == '\t' && !(terminate & TERM_TAB))
-		return 0;
-
-	return 1;
-}
-
-/* remove double slashes to make --index work with such filenames */
-static char *squash_slash(char *name)
-{
-	int i = 0, j = 0;
-
-	while (name[i]) {
-		if ((name[j++] = name[i++]) == '/')
-			while (name[i] == '/')
-				i++;
-	}
-	name[j] = '\0';
-	return name;
-}
-
-static char *find_name(const char *line, char *def, int p_value, int terminate)
-{
-	int len;
-	const char *start = line;
-
-	if (*line == '"') {
-		struct strbuf name = STRBUF_INIT;
-
-		/*
-		 * Proposed "new-style" GNU patch/diff format; see
-		 * http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2
-		 */
-		if (!unquote_c_style(&name, line, NULL)) {
-			char *cp;
-
-			for (cp = name.buf; p_value; p_value--) {
-				cp = strchr(cp, '/');
-				if (!cp)
-					break;
-				cp++;
-			}
-			if (cp) {
-				/* name can later be freed, so we need
-				 * to memmove, not just return cp
-				 */
-				strbuf_remove(&name, 0, cp - name.buf);
-				free(def);
-				if (root)
-					strbuf_insert(&name, 0, root, root_len);
-				return squash_slash(strbuf_detach(&name, NULL));
-			}
-		}
-		strbuf_release(&name);
-	}
-
-	for (;;) {
-		char c = *line;
-
-		if (isspace(c)) {
-			if (c == '\n')
-				break;
-			if (name_terminate(start, line-start, c, terminate))
-				break;
-		}
-		line++;
-		if (c == '/' && !--p_value)
-			start = line;
-	}
-	if (!start)
-		return squash_slash(def);
-	len = line - start;
-	if (!len)
-		return squash_slash(def);
-
-	/*
-	 * Generally we prefer the shorter name, especially
-	 * if the other one is just a variation of that with
-	 * something else tacked on to the end (ie "file.orig"
-	 * or "file~").
-	 */
-	if (def) {
-		int deflen = strlen(def);
-		if (deflen < len && !strncmp(start, def, deflen))
-			return squash_slash(def);
-		free(def);
-	}
-
-	if (root) {
-		char *ret = xmalloc(root_len + len + 1);
-		strcpy(ret, root);
-		memcpy(ret + root_len, start, len);
-		ret[root_len + len] = '\0';
-		return squash_slash(ret);
-	}
-
-	return squash_slash(xmemdupz(start, len));
-}
-
-static int count_slashes(const char *cp)
-{
-	int cnt = 0;
-	char ch;
-
-	while ((ch = *cp++))
-		if (ch == '/')
-			cnt++;
-	return cnt;
-}
-
-/*
- * Given the string after "--- " or "+++ ", guess the appropriate
- * p_value for the given patch.
- */
-static int guess_p_value(const char *nameline)
-{
-	char *name, *cp;
-	int val = -1;
-
-	if (is_dev_null(nameline))
-		return -1;
-	name = find_name(nameline, NULL, 0, TERM_SPACE | TERM_TAB);
-	if (!name)
-		return -1;
-	cp = strchr(name, '/');
-	if (!cp)
-		val = 0;
-	else if (prefix) {
-		/*
-		 * Does it begin with "a/$our-prefix" and such?  Then this is
-		 * very likely to apply to our directory.
-		 */
-		if (!strncmp(name, prefix, prefix_length))
-			val = count_slashes(prefix);
-		else {
-			cp++;
-			if (!strncmp(cp, prefix, prefix_length))
-				val = count_slashes(prefix) + 1;
-		}
-	}
-	free(name);
-	return val;
-}
-
-/*
- * Does the ---/+++ line has the POSIX timestamp after the last HT?
- * GNU diff puts epoch there to signal a creation/deletion event.  Is
- * this such a timestamp?
- */
-static int has_epoch_timestamp(const char *nameline)
-{
-	/*
-	 * We are only interested in epoch timestamp; any non-zero
-	 * fraction cannot be one, hence "(\.0+)?" in the regexp below.
-	 * For the same reason, the date must be either 1969-12-31 or
-	 * 1970-01-01, and the seconds part must be "00".
-	 */
-	const char stamp_regexp[] =
-		"^(1969-12-31|1970-01-01)"
-		" "
-		"[0-2][0-9]:[0-5][0-9]:00(\\.0+)?"
-		" "
-		"([-+][0-2][0-9][0-5][0-9])\n";
-	const char *timestamp = NULL, *cp;
-	static regex_t *stamp;
-	regmatch_t m[10];
-	int zoneoffset;
-	int hourminute;
-	int status;
-
-	for (cp = nameline; *cp != '\n'; cp++) {
-		if (*cp == '\t')
-			timestamp = cp + 1;
-	}
-	if (!timestamp)
-		return 0;
-	if (!stamp) {
-		stamp = xmalloc(sizeof(*stamp));
-		if (regcomp(stamp, stamp_regexp, REG_EXTENDED)) {
-			warning("Cannot prepare timestamp regexp %s",
-				stamp_regexp);
-			return 0;
-		}
-	}
-
-	status = regexec(stamp, timestamp, ARRAY_SIZE(m), m, 0);
-	if (status) {
-		if (status != REG_NOMATCH)
-			warning("regexec returned %d for input: %s",
-				status, timestamp);
-		return 0;
-	}
-
-	zoneoffset = strtol(timestamp + m[3].rm_so + 1, NULL, 10);
-	zoneoffset = (zoneoffset / 100) * 60 + (zoneoffset % 100);
-	if (timestamp[m[3].rm_so] == '-')
-		zoneoffset = -zoneoffset;
-
-	/*
-	 * YYYY-MM-DD hh:mm:ss must be from either 1969-12-31
-	 * (west of GMT) or 1970-01-01 (east of GMT)
-	 */
-	if ((zoneoffset < 0 && memcmp(timestamp, "1969-12-31", 10)) ||
-	    (0 <= zoneoffset && memcmp(timestamp, "1970-01-01", 10)))
-		return 0;
-
-	hourminute = (strtol(timestamp + 11, NULL, 10) * 60 +
-		      strtol(timestamp + 14, NULL, 10) -
-		      zoneoffset);
-
-	return ((zoneoffset < 0 && hourminute == 1440) ||
-		(0 <= zoneoffset && !hourminute));
-}
-
-/*
- * Get the name etc info from the ---/+++ lines of a traditional patch header
- *
- * FIXME! The end-of-filename heuristics are kind of screwy. For existing
- * files, we can happily check the index for a match, but for creating a
- * new file we should try to match whatever "patch" does. I have no idea.
- */
-static void parse_traditional_patch(const char *first, const char *second, struct patch *patch)
-{
-	char *name;
-
-	first += 4;	/* skip "--- " */
-	second += 4;	/* skip "+++ " */
-	if (!p_value_known) {
-		int p, q;
-		p = guess_p_value(first);
-		q = guess_p_value(second);
-		if (p < 0) p = q;
-		if (0 <= p && p == q) {
-			p_value = p;
-			p_value_known = 1;
-		}
-	}
-	if (is_dev_null(first)) {
-		patch->is_new = 1;
-		patch->is_delete = 0;
-		name = find_name(second, NULL, p_value, TERM_SPACE | TERM_TAB);
-		patch->new_name = name;
-	} else if (is_dev_null(second)) {
-		patch->is_new = 0;
-		patch->is_delete = 1;
-		name = find_name(first, NULL, p_value, TERM_SPACE | TERM_TAB);
-		patch->old_name = name;
-	} else {
-		name = find_name(first, NULL, p_value, TERM_SPACE | TERM_TAB);
-		name = find_name(second, name, p_value, TERM_SPACE | TERM_TAB);
-		if (has_epoch_timestamp(first)) {
-			patch->is_new = 1;
-			patch->is_delete = 0;
-			patch->new_name = name;
-		} else if (has_epoch_timestamp(second)) {
-			patch->is_new = 0;
-			patch->is_delete = 1;
-			patch->old_name = name;
-		} else {
-			patch->old_name = patch->new_name = name;
-		}
-	}
-	if (!name)
-		die("unable to find filename in patch at line %d", linenr);
-}
-
-static int gitdiff_hdrend(const char *line, struct patch *patch)
-{
-	return -1;
-}
-
-/*
- * We're anal about diff header consistency, to make
- * sure that we don't end up having strange ambiguous
- * patches floating around.
- *
- * As a result, gitdiff_{old|new}name() will check
- * their names against any previous information, just
- * to make sure..
- */
-static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, const char *oldnew)
-{
-	if (!orig_name && !isnull)
-		return find_name(line, NULL, p_value, TERM_TAB);
-
-	if (orig_name) {
-		int len;
-		const char *name;
-		char *another;
-		name = orig_name;
-		len = strlen(name);
-		if (isnull)
-			die("git apply: bad git-diff - expected /dev/null, got %s on line %d", name, linenr);
-		another = find_name(line, NULL, p_value, TERM_TAB);
-		if (!another || memcmp(another, name, len))
-			die("git apply: bad git-diff - inconsistent %s filename on line %d", oldnew, linenr);
-		free(another);
-		return orig_name;
-	}
-	else {
-		/* expect "/dev/null" */
-		if (memcmp("/dev/null", line, 9) || line[9] != '\n')
-			die("git apply: bad git-diff - expected /dev/null on line %d", linenr);
-		return NULL;
-	}
-}
-
-static int gitdiff_oldname(const char *line, struct patch *patch)
-{
-	patch->old_name = gitdiff_verify_name(line, patch->is_new, patch->old_name, "old");
-	return 0;
-}
-
-static int gitdiff_newname(const char *line, struct patch *patch)
-{
-	patch->new_name = gitdiff_verify_name(line, patch->is_delete, patch->new_name, "new");
-	return 0;
-}
-
-static int gitdiff_oldmode(const char *line, struct patch *patch)
-{
-	patch->old_mode = strtoul(line, NULL, 8);
-	return 0;
-}
-
-static int gitdiff_newmode(const char *line, struct patch *patch)
-{
-	patch->new_mode = strtoul(line, NULL, 8);
-	return 0;
-}
-
-static int gitdiff_delete(const char *line, struct patch *patch)
-{
-	patch->is_delete = 1;
-	patch->old_name = patch->def_name;
-	return gitdiff_oldmode(line, patch);
-}
-
-static int gitdiff_newfile(const char *line, struct patch *patch)
-{
-	patch->is_new = 1;
-	patch->new_name = patch->def_name;
-	return gitdiff_newmode(line, patch);
-}
-
-static int gitdiff_copysrc(const char *line, struct patch *patch)
-{
-	patch->is_copy = 1;
-	patch->old_name = find_name(line, NULL, 0, 0);
-	return 0;
-}
-
-static int gitdiff_copydst(const char *line, struct patch *patch)
-{
-	patch->is_copy = 1;
-	patch->new_name = find_name(line, NULL, 0, 0);
-	return 0;
-}
-
-static int gitdiff_renamesrc(const char *line, struct patch *patch)
-{
-	patch->is_rename = 1;
-	patch->old_name = find_name(line, NULL, 0, 0);
-	return 0;
-}
-
-static int gitdiff_renamedst(const char *line, struct patch *patch)
-{
-	patch->is_rename = 1;
-	patch->new_name = find_name(line, NULL, 0, 0);
-	return 0;
-}
-
-static int gitdiff_similarity(const char *line, struct patch *patch)
-{
-	if ((patch->score = strtoul(line, NULL, 10)) == ULONG_MAX)
-		patch->score = 0;
-	return 0;
-}
-
-static int gitdiff_dissimilarity(const char *line, struct patch *patch)
-{
-	if ((patch->score = strtoul(line, NULL, 10)) == ULONG_MAX)
-		patch->score = 0;
-	return 0;
-}
-
-static int gitdiff_index(const char *line, struct patch *patch)
-{
-	/*
-	 * index line is N hexadecimal, "..", N hexadecimal,
-	 * and optional space with octal mode.
-	 */
-	const char *ptr, *eol;
-	int len;
-
-	ptr = strchr(line, '.');
-	if (!ptr || ptr[1] != '.' || 40 < ptr - line)
-		return 0;
-	len = ptr - line;
-	memcpy(patch->old_sha1_prefix, line, len);
-	patch->old_sha1_prefix[len] = 0;
-
-	line = ptr + 2;
-	ptr = strchr(line, ' ');
-	eol = strchr(line, '\n');
-
-	if (!ptr || eol < ptr)
-		ptr = eol;
-	len = ptr - line;
-
-	if (40 < len)
-		return 0;
-	memcpy(patch->new_sha1_prefix, line, len);
-	patch->new_sha1_prefix[len] = 0;
-	if (*ptr == ' ')
-		patch->old_mode = strtoul(ptr+1, NULL, 8);
-	return 0;
-}
-
-/*
- * This is normal for a diff that doesn't change anything: we'll fall through
- * into the next diff. Tell the parser to break out.
- */
-static int gitdiff_unrecognized(const char *line, struct patch *patch)
-{
-	return -1;
-}
-
-static const char *stop_at_slash(const char *line, int llen)
-{
-	int i;
-
-	for (i = 0; i < llen; i++) {
-		int ch = line[i];
-		if (ch == '/')
-			return line + i;
-	}
-	return NULL;
-}
-
-/*
- * This is to extract the same name that appears on "diff --git"
- * line.  We do not find and return anything if it is a rename
- * patch, and it is OK because we will find the name elsewhere.
- * We need to reliably find name only when it is mode-change only,
- * creation or deletion of an empty file.  In any of these cases,
- * both sides are the same name under a/ and b/ respectively.
- */
-static char *git_header_name(char *line, int llen)
-{
-	const char *name;
-	const char *second = NULL;
-	size_t len;
-
-	line += strlen("diff --git ");
-	llen -= strlen("diff --git ");
-
-	if (*line == '"') {
-		const char *cp;
-		struct strbuf first = STRBUF_INIT;
-		struct strbuf sp = STRBUF_INIT;
-
-		if (unquote_c_style(&first, line, &second))
-			goto free_and_fail1;
-
-		/* advance to the first slash */
-		cp = stop_at_slash(first.buf, first.len);
-		/* we do not accept absolute paths */
-		if (!cp || cp == first.buf)
-			goto free_and_fail1;
-		strbuf_remove(&first, 0, cp + 1 - first.buf);
-
-		/*
-		 * second points at one past closing dq of name.
-		 * find the second name.
-		 */
-		while ((second < line + llen) && isspace(*second))
-			second++;
-
-		if (line + llen <= second)
-			goto free_and_fail1;
-		if (*second == '"') {
-			if (unquote_c_style(&sp, second, NULL))
-				goto free_and_fail1;
-			cp = stop_at_slash(sp.buf, sp.len);
-			if (!cp || cp == sp.buf)
-				goto free_and_fail1;
-			/* They must match, otherwise ignore */
-			if (strcmp(cp + 1, first.buf))
-				goto free_and_fail1;
-			strbuf_release(&sp);
-			return strbuf_detach(&first, NULL);
-		}
-
-		/* unquoted second */
-		cp = stop_at_slash(second, line + llen - second);
-		if (!cp || cp == second)
-			goto free_and_fail1;
-		cp++;
-		if (line + llen - cp != first.len + 1 ||
-		    memcmp(first.buf, cp, first.len))
-			goto free_and_fail1;
-		return strbuf_detach(&first, NULL);
-
-	free_and_fail1:
-		strbuf_release(&first);
-		strbuf_release(&sp);
-		return NULL;
-	}
-
-	/* unquoted first name */
-	name = stop_at_slash(line, llen);
-	if (!name || name == line)
-		return NULL;
-	name++;
-
-	/*
-	 * since the first name is unquoted, a dq if exists must be
-	 * the beginning of the second name.
-	 */
-	for (second = name; second < line + llen; second++) {
-		if (*second == '"') {
-			struct strbuf sp = STRBUF_INIT;
-			const char *np;
-
-			if (unquote_c_style(&sp, second, NULL))
-				goto free_and_fail2;
-
-			np = stop_at_slash(sp.buf, sp.len);
-			if (!np || np == sp.buf)
-				goto free_and_fail2;
-			np++;
-
-			len = sp.buf + sp.len - np;
-			if (len < second - name &&
-			    !strncmp(np, name, len) &&
-			    isspace(name[len])) {
-				/* Good */
-				strbuf_remove(&sp, 0, np - sp.buf);
-				return strbuf_detach(&sp, NULL);
-			}
-
-		free_and_fail2:
-			strbuf_release(&sp);
-			return NULL;
-		}
-	}
-
-	/*
-	 * Accept a name only if it shows up twice, exactly the same
-	 * form.
-	 */
-	for (len = 0 ; ; len++) {
-		switch (name[len]) {
-		default:
-			continue;
-		case '\n':
-			return NULL;
-		case '\t': case ' ':
-			second = name+len;
-			for (;;) {
-				char c = *second++;
-				if (c == '\n')
-					return NULL;
-				if (c == '/')
-					break;
-			}
-			if (second[len] == '\n' && !memcmp(name, second, len)) {
-				return xmemdupz(name, len);
-			}
-		}
-	}
-}
-
-/* Verify that we recognize the lines following a git header */
-static int parse_git_header(char *line, int len, unsigned int size, struct patch *patch)
-{
-	unsigned long offset;
-
-	/* A git diff has explicit new/delete information, so we don't guess */
-	patch->is_new = 0;
-	patch->is_delete = 0;
-
-	/*
-	 * Some things may not have the old name in the
-	 * rest of the headers anywhere (pure mode changes,
-	 * or removing or adding empty files), so we get
-	 * the default name from the header.
-	 */
-	patch->def_name = git_header_name(line, len);
-	if (patch->def_name && root) {
-		char *s = xmalloc(root_len + strlen(patch->def_name) + 1);
-		strcpy(s, root);
-		strcpy(s + root_len, patch->def_name);
-		free(patch->def_name);
-		patch->def_name = s;
-	}
-
-	line += len;
-	size -= len;
-	linenr++;
-	for (offset = len ; size > 0 ; offset += len, size -= len, line += len, linenr++) {
-		static const struct opentry {
-			const char *str;
-			int (*fn)(const char *, struct patch *);
-		} optable[] = {
-			{ "@@ -", gitdiff_hdrend },
-			{ "--- ", gitdiff_oldname },
-			{ "+++ ", gitdiff_newname },
-			{ "old mode ", gitdiff_oldmode },
-			{ "new mode ", gitdiff_newmode },
-			{ "deleted file mode ", gitdiff_delete },
-			{ "new file mode ", gitdiff_newfile },
-			{ "copy from ", gitdiff_copysrc },
-			{ "copy to ", gitdiff_copydst },
-			{ "rename old ", gitdiff_renamesrc },
-			{ "rename new ", gitdiff_renamedst },
-			{ "rename from ", gitdiff_renamesrc },
-			{ "rename to ", gitdiff_renamedst },
-			{ "similarity index ", gitdiff_similarity },
-			{ "dissimilarity index ", gitdiff_dissimilarity },
-			{ "index ", gitdiff_index },
-			{ "", gitdiff_unrecognized },
-		};
-		int i;
-
-		len = linelen(line, size);
-		if (!len || line[len-1] != '\n')
-			break;
-		for (i = 0; i < ARRAY_SIZE(optable); i++) {
-			const struct opentry *p = optable + i;
-			int oplen = strlen(p->str);
-			if (len < oplen || memcmp(p->str, line, oplen))
-				continue;
-			if (p->fn(line + oplen, patch) < 0)
-				return offset;
-			break;
-		}
-	}
-
-	return offset;
-}
-
-static int parse_num(const char *line, unsigned long *p)
-{
-	char *ptr;
-
-	if (!isdigit(*line))
-		return 0;
-	*p = strtoul(line, &ptr, 10);
-	return ptr - line;
-}
-
-static int parse_range(const char *line, int len, int offset, const char *expect,
-		       unsigned long *p1, unsigned long *p2)
-{
-	int digits, ex;
-
-	if (offset < 0 || offset >= len)
-		return -1;
-	line += offset;
-	len -= offset;
-
-	digits = parse_num(line, p1);
-	if (!digits)
-		return -1;
-
-	offset += digits;
-	line += digits;
-	len -= digits;
-
-	*p2 = 1;
-	if (*line == ',') {
-		digits = parse_num(line+1, p2);
-		if (!digits)
-			return -1;
-
-		offset += digits+1;
-		line += digits+1;
-		len -= digits+1;
-	}
-
-	ex = strlen(expect);
-	if (ex > len)
-		return -1;
-	if (memcmp(line, expect, ex))
-		return -1;
-
-	return offset + ex;
-}
-
-static void recount_diff(char *line, int size, struct fragment *fragment)
-{
-	int oldlines = 0, newlines = 0, ret = 0;
-
-	if (size < 1) {
-		warning("recount: ignore empty hunk");
-		return;
-	}
-
-	for (;;) {
-		int len = linelen(line, size);
-		size -= len;
-		line += len;
-
-		if (size < 1)
-			break;
-
-		switch (*line) {
-		case ' ': case '\n':
-			newlines++;
-			/* fall through */
-		case '-':
-			oldlines++;
-			continue;
-		case '+':
-			newlines++;
-			continue;
-		case '\\':
-			continue;
-		case '@':
-			ret = size < 3 || prefixcmp(line, "@@ ");
-			break;
-		case 'd':
-			ret = size < 5 || prefixcmp(line, "diff ");
-			break;
-		default:
-			ret = -1;
-			break;
-		}
-		if (ret) {
-			warning("recount: unexpected line: %.*s",
-				(int)linelen(line, size), line);
-			return;
-		}
-		break;
-	}
-	fragment->oldlines = oldlines;
-	fragment->newlines = newlines;
-}
-
-/*
- * Parse a unified diff fragment header of the
- * form "@@ -a,b +c,d @@"
- */
-static int parse_fragment_header(char *line, int len, struct fragment *fragment)
-{
-	int offset;
-
-	if (!len || line[len-1] != '\n')
-		return -1;
-
-	/* Figure out the number of lines in a fragment */
-	offset = parse_range(line, len, 4, " +", &fragment->oldpos, &fragment->oldlines);
-	offset = parse_range(line, len, offset, " @@", &fragment->newpos, &fragment->newlines);
-
-	return offset;
-}
-
-static int find_header(char *line, unsigned long size, int *hdrsize, struct patch *patch)
-{
-	unsigned long offset, len;
-
-	patch->is_toplevel_relative = 0;
-	patch->is_rename = patch->is_copy = 0;
-	patch->is_new = patch->is_delete = -1;
-	patch->old_mode = patch->new_mode = 0;
-	patch->old_name = patch->new_name = NULL;
-	for (offset = 0; size > 0; offset += len, size -= len, line += len, linenr++) {
-		unsigned long nextlen;
-
-		len = linelen(line, size);
-		if (!len)
-			break;
-
-		/* Testing this early allows us to take a few shortcuts.. */
-		if (len < 6)
-			continue;
-
-		/*
-		 * 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.
-		 */
-		if (!memcmp("@@ -", line, 4)) {
-			struct fragment dummy;
-			if (parse_fragment_header(line, len, &dummy) < 0)
-				continue;
-			die("patch fragment without header at line %d: %.*s",
-			    linenr, (int)len-1, line);
-		}
-
-		if (size < len + 6)
-			break;
-
-		/*
-		 * Git patch? It might not have a real patch, just a rename
-		 * or mode change, so we handle that specially
-		 */
-		if (!memcmp("diff --git ", line, 11)) {
-			int git_hdr_len = parse_git_header(line, len, size, patch);
-			if (git_hdr_len <= len)
-				continue;
-			if (!patch->old_name && !patch->new_name) {
-				if (!patch->def_name)
-					die("git diff header lacks filename information (line %d)", linenr);
-				patch->old_name = patch->new_name = patch->def_name;
-			}
-			patch->is_toplevel_relative = 1;
-			*hdrsize = git_hdr_len;
-			return offset;
-		}
-
-		/* --- followed by +++ ? */
-		if (memcmp("--- ", line,  4) || memcmp("+++ ", line + len, 4))
-			continue;
-
-		/*
-		 * We only accept unified patches, so we want it to
-		 * at least have "@@ -a,b +c,d @@\n", which is 14 chars
-		 * minimum ("@@ -0,0 +1 @@\n" is the shortest).
-		 */
-		nextlen = linelen(line + len, size - len);
-		if (size < nextlen + 14 || memcmp("@@ -", line + len + nextlen, 4))
-			continue;
-
-		/* Ok, we'll consider it a patch */
-		parse_traditional_patch(line, line+len, patch);
-		*hdrsize = len + nextlen;
-		linenr += 2;
-		return offset;
-	}
-	return -1;
-}
-
-static void check_whitespace(const char *line, int len, unsigned ws_rule)
-{
-	char *err;
-	unsigned result = ws_check(line + 1, len - 1, ws_rule);
-	if (!result)
-		return;
-
-	whitespace_error++;
-	if (squelch_whitespace_errors &&
-	    squelch_whitespace_errors < whitespace_error)
-		;
-	else {
-		err = whitespace_error_string(result);
-		fprintf(stderr, "%s:%d: %s.\n%.*s\n",
-			patch_input_file, linenr, err, len - 2, line + 1);
-		free(err);
-	}
-}
-
-/*
- * Parse a unified diff. Note that this really needs to parse each
- * fragment separately, since the only way to know the difference
- * between a "---" that is part of a patch, and a "---" that starts
- * the next patch is to look at the line counts..
- */
-static int parse_fragment(char *line, unsigned long size,
-			  struct patch *patch, struct fragment *fragment)
-{
-	int added, deleted;
-	int len = linelen(line, size), offset;
-	unsigned long oldlines, newlines;
-	unsigned long leading, trailing;
-
-	offset = parse_fragment_header(line, len, fragment);
-	if (offset < 0)
-		return -1;
-	if (offset > 0 && patch->recount)
-		recount_diff(line + offset, size - offset, fragment);
-	oldlines = fragment->oldlines;
-	newlines = fragment->newlines;
-	leading = 0;
-	trailing = 0;
-
-	/* Parse the thing.. */
-	line += len;
-	size -= len;
-	linenr++;
-	added = deleted = 0;
-	for (offset = len;
-	     0 < size;
-	     offset += len, size -= len, line += len, linenr++) {
-		if (!oldlines && !newlines)
-			break;
-		len = linelen(line, size);
-		if (!len || line[len-1] != '\n')
-			return -1;
-		switch (*line) {
-		default:
-			return -1;
-		case '\n': /* newer GNU diff, an empty context line */
-		case ' ':
-			oldlines--;
-			newlines--;
-			if (!deleted && !added)
-				leading++;
-			trailing++;
-			break;
-		case '-':
-			if (apply_in_reverse &&
-			    ws_error_action != nowarn_ws_error)
-				check_whitespace(line, len, patch->ws_rule);
-			deleted++;
-			oldlines--;
-			trailing = 0;
-			break;
-		case '+':
-			if (!apply_in_reverse &&
-			    ws_error_action != nowarn_ws_error)
-				check_whitespace(line, len, patch->ws_rule);
-			added++;
-			newlines--;
-			trailing = 0;
-			break;
-
-		/*
-		 * We allow "\ No newline at end of file". Depending
-                 * on locale settings when the patch was produced we
-                 * don't know what this line looks like. The only
-                 * thing we do know is that it begins with "\ ".
-		 * Checking for 12 is just for sanity check -- any
-		 * l10n of "\ No newline..." is at least that long.
-		 */
-		case '\\':
-			if (len < 12 || memcmp(line, "\\ ", 2))
-				return -1;
-			break;
-		}
-	}
-	if (oldlines || newlines)
-		return -1;
-	fragment->leading = leading;
-	fragment->trailing = trailing;
-
-	/*
-	 * If a fragment ends with an incomplete line, we failed to include
-	 * it in the above loop because we hit oldlines == newlines == 0
-	 * before seeing it.
-	 */
-	if (12 < size && !memcmp(line, "\\ ", 2))
-		offset += linelen(line, size);
-
-	patch->lines_added += added;
-	patch->lines_deleted += deleted;
-
-	if (0 < patch->is_new && oldlines)
-		return error("new file depends on old contents");
-	if (0 < patch->is_delete && newlines)
-		return error("deleted file still has contents");
-	return offset;
-}
-
-static int parse_single_patch(char *line, unsigned long size, struct patch *patch)
-{
-	unsigned long offset = 0;
-	unsigned long oldlines = 0, newlines = 0, context = 0;
-	struct fragment **fragp = &patch->fragments;
-
-	while (size > 4 && !memcmp(line, "@@ -", 4)) {
-		struct fragment *fragment;
-		int len;
-
-		fragment = xcalloc(1, sizeof(*fragment));
-		len = parse_fragment(line, size, patch, fragment);
-		if (len <= 0)
-			die("corrupt patch at line %d", linenr);
-		fragment->patch = line;
-		fragment->size = len;
-		oldlines += fragment->oldlines;
-		newlines += fragment->newlines;
-		context += fragment->leading + fragment->trailing;
-
-		*fragp = fragment;
-		fragp = &fragment->next;
-
-		offset += len;
-		line += len;
-		size -= len;
-	}
-
-	/*
-	 * If something was removed (i.e. we have old-lines) it cannot
-	 * be creation, and if something was added it cannot be
-	 * deletion.  However, the reverse is not true; --unified=0
-	 * patches that only add are not necessarily creation even
-	 * though they do not have any old lines, and ones that only
-	 * delete are not necessarily deletion.
-	 *
-	 * Unfortunately, a real creation/deletion patch do _not_ have
-	 * any context line by definition, so we cannot safely tell it
-	 * apart with --unified=0 insanity.  At least if the patch has
-	 * more than one hunk it is not creation or deletion.
-	 */
-	if (patch->is_new < 0 &&
-	    (oldlines || (patch->fragments && patch->fragments->next)))
-		patch->is_new = 0;
-	if (patch->is_delete < 0 &&
-	    (newlines || (patch->fragments && patch->fragments->next)))
-		patch->is_delete = 0;
-
-	if (0 < patch->is_new && oldlines)
-		die("new file %s depends on old contents", patch->new_name);
-	if (0 < patch->is_delete && newlines)
-		die("deleted file %s still has contents", patch->old_name);
-	if (!patch->is_delete && !newlines && context)
-		fprintf(stderr, "** warning: file %s becomes empty but "
-			"is not deleted\n", patch->new_name);
-
-	return offset;
-}
-
-static inline int metadata_changes(struct patch *patch)
-{
-	return	patch->is_rename > 0 ||
-		patch->is_copy > 0 ||
-		patch->is_new > 0 ||
-		patch->is_delete ||
-		(patch->old_mode && patch->new_mode &&
-		 patch->old_mode != patch->new_mode);
-}
-
-static char *inflate_it(const void *data, unsigned long size,
-			unsigned long inflated_size)
-{
-	z_stream stream;
-	void *out;
-	int st;
-
-	memset(&stream, 0, sizeof(stream));
-
-	stream.next_in = (unsigned char *)data;
-	stream.avail_in = size;
-	stream.next_out = out = xmalloc(inflated_size);
-	stream.avail_out = inflated_size;
-	git_inflate_init(&stream);
-	st = git_inflate(&stream, Z_FINISH);
-	git_inflate_end(&stream);
-	if ((st != Z_STREAM_END) || stream.total_out != inflated_size) {
-		free(out);
-		return NULL;
-	}
-	return out;
-}
-
-static struct fragment *parse_binary_hunk(char **buf_p,
-					  unsigned long *sz_p,
-					  int *status_p,
-					  int *used_p)
-{
-	/*
-	 * Expect a line that begins with binary patch method ("literal"
-	 * or "delta"), followed by the length of data before deflating.
-	 * a sequence of 'length-byte' followed by base-85 encoded data
-	 * should follow, terminated by a newline.
-	 *
-	 * Each 5-byte sequence of base-85 encodes up to 4 bytes,
-	 * and we would limit the patch line to 66 characters,
-	 * 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.
-	 */
-	int llen, used;
-	unsigned long size = *sz_p;
-	char *buffer = *buf_p;
-	int patch_method;
-	unsigned long origlen;
-	char *data = NULL;
-	int hunk_size = 0;
-	struct fragment *frag;
-
-	llen = linelen(buffer, size);
-	used = llen;
-
-	*status_p = 0;
-
-	if (!prefixcmp(buffer, "delta ")) {
-		patch_method = BINARY_DELTA_DEFLATED;
-		origlen = strtoul(buffer + 6, NULL, 10);
-	}
-	else if (!prefixcmp(buffer, "literal ")) {
-		patch_method = BINARY_LITERAL_DEFLATED;
-		origlen = strtoul(buffer + 8, NULL, 10);
-	}
-	else
-		return NULL;
-
-	linenr++;
-	buffer += llen;
-	while (1) {
-		int byte_length, max_byte_length, newsize;
-		llen = linelen(buffer, size);
-		used += llen;
-		linenr++;
-		if (llen == 1) {
-			/* consume the blank line */
-			buffer++;
-			size--;
-			break;
-		}
-		/*
-		 * Minimum line is "A00000\n" which is 7-byte long,
-		 * and the line length must be multiple of 5 plus 2.
-		 */
-		if ((llen < 7) || (llen-2) % 5)
-			goto corrupt;
-		max_byte_length = (llen - 2) / 5 * 4;
-		byte_length = *buffer;
-		if ('A' <= byte_length && byte_length <= 'Z')
-			byte_length = byte_length - 'A' + 1;
-		else if ('a' <= byte_length && byte_length <= 'z')
-			byte_length = byte_length - 'a' + 27;
-		else
-			goto corrupt;
-		/* if the input length was not multiple of 4, we would
-		 * have filler at the end but the filler should never
-		 * exceed 3 bytes
-		 */
-		if (max_byte_length < byte_length ||
-		    byte_length <= max_byte_length - 4)
-			goto corrupt;
-		newsize = hunk_size + byte_length;
-		data = xrealloc(data, newsize);
-		if (decode_85(data + hunk_size, buffer + 1, byte_length))
-			goto corrupt;
-		hunk_size = newsize;
-		buffer += llen;
-		size -= llen;
-	}
-
-	frag = xcalloc(1, sizeof(*frag));
-	frag->patch = inflate_it(data, hunk_size, origlen);
-	if (!frag->patch)
-		goto corrupt;
-	free(data);
-	frag->size = origlen;
-	*buf_p = buffer;
-	*sz_p = size;
-	*used_p = used;
-	frag->binary_patch_method = patch_method;
-	return frag;
-
- corrupt:
-	free(data);
-	*status_p = -1;
-	error("corrupt binary patch at line %d: %.*s",
-	      linenr-1, llen-1, buffer);
-	return NULL;
-}
-
-static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
-{
-	/*
-	 * We have read "GIT binary patch\n"; what follows is a line
-	 * that says the patch method (currently, either "literal" or
-	 * "delta") and the length of data before deflating; a
-	 * sequence of 'length-byte' followed by base-85 encoded data
-	 * follows.
-	 *
-	 * When a binary patch is reversible, there is another binary
-	 * hunk in the same format, starting with patch method (either
-	 * "literal" or "delta") with the length of data, and a sequence
-	 * of length-byte + base-85 encoded data, terminated with another
-	 * empty line.  This data, when applied to the postimage, produces
-	 * the preimage.
-	 */
-	struct fragment *forward;
-	struct fragment *reverse;
-	int status;
-	int used, used_1;
-
-	forward = parse_binary_hunk(&buffer, &size, &status, &used);
-	if (!forward && !status)
-		/* there has to be one hunk (forward hunk) */
-		return error("unrecognized binary patch at line %d", linenr-1);
-	if (status)
-		/* otherwise we already gave an error message */
-		return status;
-
-	reverse = parse_binary_hunk(&buffer, &size, &status, &used_1);
-	if (reverse)
-		used += used_1;
-	else if (status) {
-		/*
-		 * Not having reverse hunk is not an error, but having
-		 * a corrupt reverse hunk is.
-		 */
-		free((void*) forward->patch);
-		free(forward);
-		return status;
-	}
-	forward->next = reverse;
-	patch->fragments = forward;
-	patch->is_binary = 1;
-	return used;
-}
-
-static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
-{
-	int hdrsize, patchsize;
-	int offset = find_header(buffer, size, &hdrsize, patch);
-
-	if (offset < 0)
-		return offset;
-
-	patch->ws_rule = whitespace_rule(patch->new_name
-					 ? patch->new_name
-					 : patch->old_name);
-
-	patchsize = parse_single_patch(buffer + offset + hdrsize,
-				       size - offset - hdrsize, patch);
-
-	if (!patchsize) {
-		static const char *binhdr[] = {
-			"Binary files ",
-			"Files ",
-			NULL,
-		};
-		static const char git_binary[] = "GIT binary patch\n";
-		int i;
-		int hd = hdrsize + offset;
-		unsigned long llen = linelen(buffer + hd, size - hd);
-
-		if (llen == sizeof(git_binary) - 1 &&
-		    !memcmp(git_binary, buffer + hd, llen)) {
-			int used;
-			linenr++;
-			used = parse_binary(buffer + hd + llen,
-					    size - hd - llen, patch);
-			if (used)
-				patchsize = used + llen;
-			else
-				patchsize = 0;
-		}
-		else if (!memcmp(" differ\n", buffer + hd + llen - 8, 8)) {
-			for (i = 0; binhdr[i]; i++) {
-				int len = strlen(binhdr[i]);
-				if (len < size - hd &&
-				    !memcmp(binhdr[i], buffer + hd, len)) {
-					linenr++;
-					patch->is_binary = 1;
-					patchsize = llen;
-					break;
-				}
-			}
-		}
-
-		/* Empty patch cannot be applied if it is a text patch
-		 * without metadata change.  A binary patch appears
-		 * empty to us here.
-		 */
-		if ((apply || check) &&
-		    (!patch->is_binary && !metadata_changes(patch)))
-			die("patch with only garbage at line %d", linenr);
-	}
-
-	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);
-		}
-	}
-}
-
-static const char pluses[] =
-"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";
-static const char minuses[]=
-"----------------------------------------------------------------------";
-
-static void show_stats(struct patch *patch)
-{
-	struct strbuf qname = STRBUF_INIT;
-	char *cp = patch->new_name ? patch->new_name : patch->old_name;
-	int max, add, del;
-
-	quote_c_style(cp, &qname, NULL, 0);
-
-	/*
-	 * "scale" the filename
-	 */
-	max = max_len;
-	if (max > 50)
-		max = 50;
-
-	if (qname.len > max) {
-		cp = strchr(qname.buf + qname.len + 3 - max, '/');
-		if (!cp)
-			cp = qname.buf + qname.len + 3 - max;
-		strbuf_splice(&qname, 0, cp - qname.buf, "...", 3);
-	}
-
-	if (patch->is_binary) {
-		printf(" %-*s |  Bin\n", max, qname.buf);
-		strbuf_release(&qname);
-		return;
-	}
-
-	printf(" %-*s |", max, qname.buf);
-	strbuf_release(&qname);
-
-	/*
-	 * scale the add/delete
-	 */
-	max = max + max_change > 70 ? 70 - max : max_change;
-	add = patch->lines_added;
-	del = patch->lines_deleted;
-
-	if (max_change > 0) {
-		int total = ((add + del) * max + max_change / 2) / max_change;
-		add = (add * max + max_change / 2) / max_change;
-		del = total - add;
-	}
-	printf("%5d %.*s%.*s\n", patch->lines_added + patch->lines_deleted,
-		add, pluses, del, minuses);
-}
-
-static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)
-{
-	switch (st->st_mode & S_IFMT) {
-	case S_IFLNK:
-		if (strbuf_readlink(buf, path, st->st_size) < 0)
-			return error("unable to read symlink %s", path);
-		return 0;
-	case S_IFREG:
-		if (strbuf_read_file(buf, path, st->st_size) != st->st_size)
-			return error("unable to open or read %s", path);
-		convert_to_git(path, buf->buf, buf->len, buf, 0);
-		return 0;
-	default:
-		return -1;
-	}
-}
-
-static void update_pre_post_images(struct image *preimage,
-				   struct image *postimage,
-				   char *buf,
-				   size_t len)
-{
-	int i, ctx;
-	char *new, *old, *fixed;
-	struct image fixed_preimage;
-
-	/*
-	 * Update the preimage with whitespace fixes.  Note that we
-	 * are not losing preimage->buf -- apply_one_fragment() will
-	 * free "oldlines".
-	 */
-	prepare_image(&fixed_preimage, buf, len, 1);
-	assert(fixed_preimage.nr == preimage->nr);
-	for (i = 0; i < preimage->nr; i++)
-		fixed_preimage.line[i].flag = preimage->line[i].flag;
-	free(preimage->line_allocated);
-	*preimage = fixed_preimage;
-
-	/*
-	 * Adjust the common context lines in postimage, in place.
-	 * This is possible because whitespace fixing does not make
-	 * the string grow.
-	 */
-	new = old = postimage->buf;
-	fixed = preimage->buf;
-	for (i = ctx = 0; i < postimage->nr; i++) {
-		size_t len = postimage->line[i].len;
-		if (!(postimage->line[i].flag & LINE_COMMON)) {
-			/* an added line -- no counterparts in preimage */
-			memmove(new, old, len);
-			old += len;
-			new += len;
-			continue;
-		}
-
-		/* a common context -- skip it in the original postimage */
-		old += len;
-
-		/* and find the corresponding one in the fixed preimage */
-		while (ctx < preimage->nr &&
-		       !(preimage->line[ctx].flag & LINE_COMMON)) {
-			fixed += preimage->line[ctx].len;
-			ctx++;
-		}
-		if (preimage->nr <= ctx)
-			die("oops");
-
-		/* and copy it in, while fixing the line length */
-		len = preimage->line[ctx].len;
-		memcpy(new, fixed, len);
-		new += len;
-		fixed += len;
-		postimage->line[i].len = len;
-		ctx++;
-	}
-
-	/* Fix the length of the whole thing */
-	postimage->len = new - postimage->buf;
-}
-
-static int match_fragment(struct image *img,
-			  struct image *preimage,
-			  struct image *postimage,
-			  unsigned long try,
-			  int try_lno,
-			  unsigned ws_rule,
-			  int match_beginning, int match_end)
-{
-	int i;
-	char *fixed_buf, *buf, *orig, *target;
-
-	if (preimage->nr + try_lno > img->nr)
-		return 0;
-
-	if (match_beginning && try_lno)
-		return 0;
-
-	if (match_end && preimage->nr + try_lno != img->nr)
-		return 0;
-
-	/* Quick hash check */
-	for (i = 0; i < preimage->nr; i++)
-		if (preimage->line[i].hash != img->line[try_lno + i].hash)
-			return 0;
-
-	/*
-	 * Do we have an exact match?  If we were told to match
-	 * at the end, size must be exactly at try+fragsize,
-	 * otherwise try+fragsize must be still within the preimage,
-	 * and either case, the old piece should match the preimage
-	 * exactly.
-	 */
-	if ((match_end
-	     ? (try + preimage->len == img->len)
-	     : (try + preimage->len <= img->len)) &&
-	    !memcmp(img->buf + try, preimage->buf, preimage->len))
-		return 1;
-
-	if (ws_error_action != correct_ws_error)
-		return 0;
-
-	/*
-	 * The hunk does not apply byte-by-byte, but the hash says
-	 * it might with whitespace fuzz.
-	 */
-	fixed_buf = xmalloc(preimage->len + 1);
-	buf = fixed_buf;
-	orig = preimage->buf;
-	target = img->buf + try;
-	for (i = 0; i < preimage->nr; i++) {
-		size_t fixlen; /* length after fixing the preimage */
-		size_t oldlen = preimage->line[i].len;
-		size_t tgtlen = img->line[try_lno + i].len;
-		size_t tgtfixlen; /* length after fixing the target line */
-		char tgtfixbuf[1024], *tgtfix;
-		int match;
-
-		/* Try fixing the line in the preimage */
-		fixlen = ws_fix_copy(buf, orig, oldlen, ws_rule, NULL);
-
-		/* Try fixing the line in the target */
-		if (sizeof(tgtfixbuf) > tgtlen)
-			tgtfix = tgtfixbuf;
-		else
-			tgtfix = xmalloc(tgtlen);
-		tgtfixlen = ws_fix_copy(tgtfix, target, tgtlen, ws_rule, NULL);
-
-		/*
-		 * If they match, either the preimage was based on
-		 * a version before our tree fixed whitespace breakage,
-		 * or we are lacking a whitespace-fix patch the tree
-		 * the preimage was based on already had (i.e. target
-		 * has whitespace breakage, the preimage doesn't).
-		 * In either case, we are fixing the whitespace breakages
-		 * so we might as well take the fix together with their
-		 * real change.
-		 */
-		match = (tgtfixlen == fixlen && !memcmp(tgtfix, buf, fixlen));
-
-		if (tgtfix != tgtfixbuf)
-			free(tgtfix);
-		if (!match)
-			goto unmatch_exit;
-
-		orig += oldlen;
-		buf += fixlen;
-		target += tgtlen;
-	}
-
-	/*
-	 * Yes, the preimage is based on an older version that still
-	 * has whitespace breakages unfixed, and fixing them makes the
-	 * hunk match.  Update the context lines in the postimage.
-	 */
-	update_pre_post_images(preimage, postimage,
-			       fixed_buf, buf - fixed_buf);
-	return 1;
-
- unmatch_exit:
-	free(fixed_buf);
-	return 0;
-}
-
-static int find_pos(struct image *img,
-		    struct image *preimage,
-		    struct image *postimage,
-		    int line,
-		    unsigned ws_rule,
-		    int match_beginning, int match_end)
-{
-	int i;
-	unsigned long backwards, forwards, try;
-	int backwards_lno, forwards_lno, try_lno;
-
-	if (preimage->nr > img->nr)
-		return -1;
-
-	/*
-	 * If match_begining or match_end is specified, there is no
-	 * point starting from a wrong line that will never match and
-	 * wander around and wait for a match at the specified end.
-	 */
-	if (match_beginning)
-		line = 0;
-	else if (match_end)
-		line = img->nr - preimage->nr;
-
-	if (line > img->nr)
-		line = img->nr;
-
-	try = 0;
-	for (i = 0; i < line; i++)
-		try += img->line[i].len;
-
-	/*
-	 * There's probably some smart way to do this, but I'll leave
-	 * that to the smart and beautiful people. I'm simple and stupid.
-	 */
-	backwards = try;
-	backwards_lno = line;
-	forwards = try;
-	forwards_lno = line;
-	try_lno = line;
-
-	for (i = 0; ; i++) {
-		if (match_fragment(img, preimage, postimage,
-				   try, try_lno, ws_rule,
-				   match_beginning, match_end))
-			return try_lno;
-
-	again:
-		if (backwards_lno == 0 && forwards_lno == img->nr)
-			break;
-
-		if (i & 1) {
-			if (backwards_lno == 0) {
-				i++;
-				goto again;
-			}
-			backwards_lno--;
-			backwards -= img->line[backwards_lno].len;
-			try = backwards;
-			try_lno = backwards_lno;
-		} else {
-			if (forwards_lno == img->nr) {
-				i++;
-				goto again;
-			}
-			forwards += img->line[forwards_lno].len;
-			forwards_lno++;
-			try = forwards;
-			try_lno = forwards_lno;
-		}
-
-	}
-	return -1;
-}
-
-static void remove_first_line(struct image *img)
-{
-	img->buf += img->line[0].len;
-	img->len -= img->line[0].len;
-	img->line++;
-	img->nr--;
-}
-
-static void remove_last_line(struct image *img)
-{
-	img->len -= img->line[--img->nr].len;
-}
-
-static void update_image(struct image *img,
-			 int applied_pos,
-			 struct image *preimage,
-			 struct image *postimage)
-{
-	/*
-	 * remove the copy of preimage at offset in img
-	 * and replace it with postimage
-	 */
-	int i, nr;
-	size_t remove_count, insert_count, applied_at = 0;
-	char *result;
-
-	for (i = 0; i < applied_pos; i++)
-		applied_at += img->line[i].len;
-
-	remove_count = 0;
-	for (i = 0; i < preimage->nr; i++)
-		remove_count += img->line[applied_pos + i].len;
-	insert_count = postimage->len;
-
-	/* Adjust the contents */
-	result = xmalloc(img->len + insert_count - remove_count + 1);
-	memcpy(result, img->buf, applied_at);
-	memcpy(result + applied_at, postimage->buf, postimage->len);
-	memcpy(result + applied_at + postimage->len,
-	       img->buf + (applied_at + remove_count),
-	       img->len - (applied_at + remove_count));
-	free(img->buf);
-	img->buf = result;
-	img->len += insert_count - remove_count;
-	result[img->len] = '\0';
-
-	/* Adjust the line table */
-	nr = img->nr + postimage->nr - preimage->nr;
-	if (preimage->nr < postimage->nr) {
-		/*
-		 * NOTE: this knows that we never call remove_first_line()
-		 * on anything other than pre/post image.
-		 */
-		img->line = xrealloc(img->line, nr * sizeof(*img->line));
-		img->line_allocated = img->line;
-	}
-	if (preimage->nr != postimage->nr)
-		memmove(img->line + applied_pos + postimage->nr,
-			img->line + applied_pos + preimage->nr,
-			(img->nr - (applied_pos + preimage->nr)) *
-			sizeof(*img->line));
-	memcpy(img->line + applied_pos,
-	       postimage->line,
-	       postimage->nr * sizeof(*img->line));
-	img->nr = nr;
-}
-
-static int apply_one_fragment(struct image *img, struct fragment *frag,
-			      int inaccurate_eof, unsigned ws_rule)
-{
-	int match_beginning, match_end;
-	const char *patch = frag->patch;
-	int size = frag->size;
-	char *old, *new, *oldlines, *newlines;
-	int new_blank_lines_at_end = 0;
-	unsigned long leading, trailing;
-	int pos, applied_pos;
-	struct image preimage;
-	struct image postimage;
-
-	memset(&preimage, 0, sizeof(preimage));
-	memset(&postimage, 0, sizeof(postimage));
-	oldlines = xmalloc(size);
-	newlines = xmalloc(size);
-
-	old = oldlines;
-	new = newlines;
-	while (size > 0) {
-		char first;
-		int len = linelen(patch, size);
-		int plen, added;
-		int added_blank_line = 0;
-
-		if (!len)
-			break;
-
-		/*
-		 * "plen" is how much of the line we should use for
-		 * the actual patch data. Normally we just remove the
-		 * first character on the line, but if the line is
-		 * followed by "\ No newline", then we also remove the
-		 * last one (which is the newline, of course).
-		 */
-		plen = len - 1;
-		if (len < size && patch[len] == '\\')
-			plen--;
-		first = *patch;
-		if (apply_in_reverse) {
-			if (first == '-')
-				first = '+';
-			else if (first == '+')
-				first = '-';
-		}
-
-		switch (first) {
-		case '\n':
-			/* Newer GNU diff, empty context line */
-			if (plen < 0)
-				/* ... followed by '\No newline'; nothing */
-				break;
-			*old++ = '\n';
-			*new++ = '\n';
-			add_line_info(&preimage, "\n", 1, LINE_COMMON);
-			add_line_info(&postimage, "\n", 1, LINE_COMMON);
-			break;
-		case ' ':
-		case '-':
-			memcpy(old, patch + 1, plen);
-			add_line_info(&preimage, old, plen,
-				      (first == ' ' ? LINE_COMMON : 0));
-			old += plen;
-			if (first == '-')
-				break;
-		/* Fall-through for ' ' */
-		case '+':
-			/* --no-add does not add new lines */
-			if (first == '+' && no_add)
-				break;
-
-			if (first != '+' ||
-			    !whitespace_error ||
-			    ws_error_action != correct_ws_error) {
-				memcpy(new, patch + 1, plen);
-				added = plen;
-			}
-			else {
-				added = ws_fix_copy(new, patch + 1, plen, ws_rule, &applied_after_fixing_ws);
-			}
-			add_line_info(&postimage, new, added,
-				      (first == '+' ? 0 : LINE_COMMON));
-			new += added;
-			if (first == '+' &&
-			    added == 1 && new[-1] == '\n')
-				added_blank_line = 1;
-			break;
-		case '@': case '\\':
-			/* Ignore it, we already handled it */
-			break;
-		default:
-			if (apply_verbosely)
-				error("invalid start of line: '%c'", first);
-			return -1;
-		}
-		if (added_blank_line)
-			new_blank_lines_at_end++;
-		else
-			new_blank_lines_at_end = 0;
-		patch += len;
-		size -= len;
-	}
-	if (inaccurate_eof &&
-	    old > oldlines && old[-1] == '\n' &&
-	    new > newlines && new[-1] == '\n') {
-		old--;
-		new--;
-	}
-
-	leading = frag->leading;
-	trailing = frag->trailing;
-
-	/*
-	 * A hunk to change lines at the beginning would begin with
-	 * @@ -1,L +N,M @@
-	 * but we need to be careful.  -U0 that inserts before the second
-	 * line also has this pattern.
-	 *
-	 * And a hunk to add to an empty file would begin with
-	 * @@ -0,0 +N,M @@
-	 *
-	 * In other words, a hunk that is (frag->oldpos <= 1) with or
-	 * without leading context must match at the beginning.
-	 */
-	match_beginning = (!frag->oldpos ||
-			   (frag->oldpos == 1 && !unidiff_zero));
-
-	/*
-	 * A hunk without trailing lines must match at the end.
-	 * However, we simply cannot tell if a hunk must match end
-	 * from the lack of trailing lines if the patch was generated
-	 * with unidiff without any context.
-	 */
-	match_end = !unidiff_zero && !trailing;
-
-	pos = frag->newpos ? (frag->newpos - 1) : 0;
-	preimage.buf = oldlines;
-	preimage.len = old - oldlines;
-	postimage.buf = newlines;
-	postimage.len = new - newlines;
-	preimage.line = preimage.line_allocated;
-	postimage.line = postimage.line_allocated;
-
-	for (;;) {
-
-		applied_pos = find_pos(img, &preimage, &postimage, pos,
-				       ws_rule, match_beginning, match_end);
-
-		if (applied_pos >= 0)
-			break;
-
-		/* Am I at my context limits? */
-		if ((leading <= p_context) && (trailing <= p_context))
-			break;
-		if (match_beginning || match_end) {
-			match_beginning = match_end = 0;
-			continue;
-		}
-
-		/*
-		 * Reduce the number of context lines; reduce both
-		 * leading and trailing if they are equal otherwise
-		 * just reduce the larger context.
-		 */
-		if (leading >= trailing) {
-			remove_first_line(&preimage);
-			remove_first_line(&postimage);
-			pos--;
-			leading--;
-		}
-		if (trailing > leading) {
-			remove_last_line(&preimage);
-			remove_last_line(&postimage);
-			trailing--;
-		}
-	}
-
-	if (applied_pos >= 0) {
-		if (ws_error_action == correct_ws_error &&
-		    new_blank_lines_at_end &&
-		    postimage.nr + applied_pos == img->nr) {
-			/*
-			 * If the patch application adds blank lines
-			 * at the end, and if the patch applies at the
-			 * end of the image, remove those added blank
-			 * lines.
-			 */
-			while (new_blank_lines_at_end--)
-				remove_last_line(&postimage);
-		}
-
-		/*
-		 * Warn if it was necessary to reduce the number
-		 * of context lines.
-		 */
-		if ((leading != frag->leading) ||
-		    (trailing != frag->trailing))
-			fprintf(stderr, "Context reduced to (%ld/%ld)"
-				" to apply fragment at %d\n",
-				leading, trailing, applied_pos+1);
-		update_image(img, applied_pos, &preimage, &postimage);
-	} else {
-		if (apply_verbosely)
-			error("while searching for:\n%.*s",
-			      (int)(old - oldlines), oldlines);
-	}
-
-	free(oldlines);
-	free(newlines);
-	free(preimage.line_allocated);
-	free(postimage.line_allocated);
-
-	return (applied_pos < 0);
-}
-
-static int apply_binary_fragment(struct image *img, struct patch *patch)
-{
-	struct fragment *fragment = patch->fragments;
-	unsigned long len;
-	void *dst;
-
-	/* Binary patch is irreversible without the optional second hunk */
-	if (apply_in_reverse) {
-		if (!fragment->next)
-			return error("cannot reverse-apply a binary patch "
-				     "without the reverse hunk to '%s'",
-				     patch->new_name
-				     ? patch->new_name : patch->old_name);
-		fragment = fragment->next;
-	}
-	switch (fragment->binary_patch_method) {
-	case BINARY_DELTA_DEFLATED:
-		dst = patch_delta(img->buf, img->len, fragment->patch,
-				  fragment->size, &len);
-		if (!dst)
-			return -1;
-		clear_image(img);
-		img->buf = dst;
-		img->len = len;
-		return 0;
-	case BINARY_LITERAL_DEFLATED:
-		clear_image(img);
-		img->len = fragment->size;
-		img->buf = xmalloc(img->len+1);
-		memcpy(img->buf, fragment->patch, img->len);
-		img->buf[img->len] = '\0';
-		return 0;
-	}
-	return -1;
-}
-
-static int apply_binary(struct image *img, struct patch *patch)
-{
-	const char *name = patch->old_name ? patch->old_name : patch->new_name;
-	unsigned char sha1[20];
-
-	/*
-	 * For safety, we require patch index line to contain
-	 * full 40-byte textual SHA1 for old and new, at least for now.
-	 */
-	if (strlen(patch->old_sha1_prefix) != 40 ||
-	    strlen(patch->new_sha1_prefix) != 40 ||
-	    get_sha1_hex(patch->old_sha1_prefix, sha1) ||
-	    get_sha1_hex(patch->new_sha1_prefix, sha1))
-		return error("cannot apply binary patch to '%s' "
-			     "without full index line", name);
-
-	if (patch->old_name) {
-		/*
-		 * See if the old one matches what the patch
-		 * applies to.
-		 */
-		hash_sha1_file(img->buf, img->len, blob_type, sha1);
-		if (strcmp(sha1_to_hex(sha1), patch->old_sha1_prefix))
-			return error("the patch applies to '%s' (%s), "
-				     "which does not match the "
-				     "current contents.",
-				     name, sha1_to_hex(sha1));
-	}
-	else {
-		/* Otherwise, the old one must be empty. */
-		if (img->len)
-			return error("the patch applies to an empty "
-				     "'%s' but it is not empty", name);
-	}
-
-	get_sha1_hex(patch->new_sha1_prefix, sha1);
-	if (is_null_sha1(sha1)) {
-		clear_image(img);
-		return 0; /* deletion patch */
-	}
-
-	if (has_sha1_file(sha1)) {
-		/* We already have the postimage */
-		enum object_type type;
-		unsigned long size;
-		char *result;
-
-		result = read_sha1_file(sha1, &type, &size);
-		if (!result)
-			return error("the necessary postimage %s for "
-				     "'%s' cannot be read",
-				     patch->new_sha1_prefix, name);
-		clear_image(img);
-		img->buf = result;
-		img->len = size;
-	} else {
-		/*
-		 * We have verified buf matches the preimage;
-		 * apply the patch data to it, which is stored
-		 * in the patch->fragments->{patch,size}.
-		 */
-		if (apply_binary_fragment(img, patch))
-			return error("binary patch does not apply to '%s'",
-				     name);
-
-		/* verify that the result matches */
-		hash_sha1_file(img->buf, img->len, blob_type, sha1);
-		if (strcmp(sha1_to_hex(sha1), patch->new_sha1_prefix))
-			return error("binary patch to '%s' creates incorrect result (expecting %s, got %s)",
-				name, patch->new_sha1_prefix, sha1_to_hex(sha1));
-	}
-
-	return 0;
-}
-
-static int apply_fragments(struct image *img, struct patch *patch)
-{
-	struct fragment *frag = patch->fragments;
-	const char *name = patch->old_name ? patch->old_name : patch->new_name;
-	unsigned ws_rule = patch->ws_rule;
-	unsigned inaccurate_eof = patch->inaccurate_eof;
-
-	if (patch->is_binary)
-		return apply_binary(img, patch);
-
-	while (frag) {
-		if (apply_one_fragment(img, frag, inaccurate_eof, ws_rule)) {
-			error("patch failed: %s:%ld", name, frag->oldpos);
-			if (!apply_with_reject)
-				return -1;
-			frag->rejected = 1;
-		}
-		frag = frag->next;
-	}
-	return 0;
-}
-
-static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf)
-{
-	if (!ce)
-		return 0;
-
-	if (S_ISGITLINK(ce->ce_mode)) {
-		strbuf_grow(buf, 100);
-		strbuf_addf(buf, "Subproject commit %s\n", sha1_to_hex(ce->sha1));
-	} else {
-		enum object_type type;
-		unsigned long sz;
-		char *result;
-
-		result = read_sha1_file(ce->sha1, &type, &sz);
-		if (!result)
-			return -1;
-		/* XXX read_sha1_file NUL-terminates */
-		strbuf_attach(buf, result, sz, sz + 1);
-	}
-	return 0;
-}
-
-static struct patch *in_fn_table(const char *name)
-{
-	struct string_list_item *item;
-
-	if (name == NULL)
-		return NULL;
-
-	item = string_list_lookup(name, &fn_table);
-	if (item != NULL)
-		return (struct patch *)item->util;
-
-	return NULL;
-}
-
-/*
- * item->util in the filename table records the status of the path.
- * Usually it points at a patch (whose result records the contents
- * of it after applying it), but it could be PATH_WAS_DELETED for a
- * path that a previously applied patch has already removed.
- */
- #define PATH_TO_BE_DELETED ((struct patch *) -2)
-#define PATH_WAS_DELETED ((struct patch *) -1)
-
-static int to_be_deleted(struct patch *patch)
-{
-	return patch == PATH_TO_BE_DELETED;
-}
-
-static int was_deleted(struct patch *patch)
-{
-	return patch == PATH_WAS_DELETED;
-}
-
-static void add_to_fn_table(struct patch *patch)
-{
-	struct string_list_item *item;
-
-	/*
-	 * Always add new_name unless patch is a deletion
-	 * This should cover the cases for normal diffs,
-	 * file creations and copies
-	 */
-	if (patch->new_name != NULL) {
-		item = string_list_insert(patch->new_name, &fn_table);
-		item->util = patch;
-	}
-
-	/*
-	 * store a failure on rename/deletion cases because
-	 * later chunks shouldn't patch old names
-	 */
-	if ((patch->new_name == NULL) || (patch->is_rename)) {
-		item = string_list_insert(patch->old_name, &fn_table);
-		item->util = PATH_WAS_DELETED;
-	}
-}
-
-static void prepare_fn_table(struct patch *patch)
-{
-	/*
-	 * store information about incoming file deletion
-	 */
-	while (patch) {
-		if ((patch->new_name == NULL) || (patch->is_rename)) {
-			struct string_list_item *item;
-			item = string_list_insert(patch->old_name, &fn_table);
-			item->util = PATH_TO_BE_DELETED;
-		}
-		patch = patch->next;
-	}
-}
-
-static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)
-{
-	struct strbuf buf = STRBUF_INIT;
-	struct image image;
-	size_t len;
-	char *img;
-	struct patch *tpatch;
-
-	if (!(patch->is_copy || patch->is_rename) &&
-	    (tpatch = in_fn_table(patch->old_name)) != NULL && !to_be_deleted(tpatch)) {
-		if (was_deleted(tpatch)) {
-			return error("patch %s has been renamed/deleted",
-				patch->old_name);
-		}
-		/* We have a patched copy in memory use that */
-		strbuf_add(&buf, tpatch->result, tpatch->resultsize);
-	} else if (cached) {
-		if (read_file_or_gitlink(ce, &buf))
-			return error("read of %s failed", patch->old_name);
-	} else if (patch->old_name) {
-		if (S_ISGITLINK(patch->old_mode)) {
-			if (ce) {
-				read_file_or_gitlink(ce, &buf);
-			} else {
-				/*
-				 * There is no way to apply subproject
-				 * patch without looking at the index.
-				 */
-				patch->fragments = NULL;
-			}
-		} else {
-			if (read_old_data(st, patch->old_name, &buf))
-				return error("read of %s failed", patch->old_name);
-		}
-	}
-
-	img = strbuf_detach(&buf, &len);
-	prepare_image(&image, img, len, !patch->is_binary);
-
-	if (apply_fragments(&image, patch) < 0)
-		return -1; /* note with --reject this succeeds. */
-	patch->result = image.buf;
-	patch->resultsize = image.len;
-	add_to_fn_table(patch);
-	free(image.line_allocated);
-
-	if (0 < patch->is_delete && patch->resultsize)
-		return error("removal patch leaves file contents");
-
-	return 0;
-}
-
-static int check_to_create_blob(const char *new_name, int ok_if_exists)
-{
-	struct stat nst;
-	if (!lstat(new_name, &nst)) {
-		if (S_ISDIR(nst.st_mode) || ok_if_exists)
-			return 0;
-		/*
-		 * A leading component of new_name might be a symlink
-		 * that is going to be removed with this patch, but
-		 * still pointing at somewhere that has the path.
-		 * In such a case, path "new_name" does not exist as
-		 * far as git is concerned.
-		 */
-		if (has_symlink_leading_path(new_name, strlen(new_name)))
-			return 0;
-
-		return error("%s: already exists in working directory", new_name);
-	}
-	else if ((errno != ENOENT) && (errno != ENOTDIR))
-		return error("%s: %s", new_name, strerror(errno));
-	return 0;
-}
-
-static int verify_index_match(struct cache_entry *ce, struct stat *st)
-{
-	if (S_ISGITLINK(ce->ce_mode)) {
-		if (!S_ISDIR(st->st_mode))
-			return -1;
-		return 0;
-	}
-	return ce_match_stat(ce, st, CE_MATCH_IGNORE_VALID);
-}
-
-static int check_preimage(struct patch *patch, struct cache_entry **ce, struct stat *st)
-{
-	const char *old_name = patch->old_name;
-	struct patch *tpatch = NULL;
-	int stat_ret = 0;
-	unsigned st_mode = 0;
-
-	/*
-	 * Make sure that we do not have local modifications from the
-	 * index when we are looking at the index.  Also make sure
-	 * we have the preimage file to be patched in the work tree,
-	 * unless --cached, which tells git to apply only in the index.
-	 */
-	if (!old_name)
-		return 0;
-
-	assert(patch->is_new <= 0);
-
-	if (!(patch->is_copy || patch->is_rename) &&
-	    (tpatch = in_fn_table(old_name)) != NULL && !to_be_deleted(tpatch)) {
-		if (was_deleted(tpatch))
-			return error("%s: has been deleted/renamed", old_name);
-		st_mode = tpatch->new_mode;
-	} else if (!cached) {
-		stat_ret = lstat(old_name, st);
-		if (stat_ret && errno != ENOENT)
-			return error("%s: %s", old_name, strerror(errno));
-	}
-
-	if (to_be_deleted(tpatch))
-		tpatch = NULL;
-
-	if (check_index && !tpatch) {
-		int pos = cache_name_pos(old_name, strlen(old_name));
-		if (pos < 0) {
-			if (patch->is_new < 0)
-				goto is_new;
-			return error("%s: does not exist in index", old_name);
-		}
-		*ce = active_cache[pos];
-		if (stat_ret < 0) {
-			struct checkout costate;
-			/* checkout */
-			costate.base_dir = "";
-			costate.base_dir_len = 0;
-			costate.force = 0;
-			costate.quiet = 0;
-			costate.not_new = 0;
-			costate.refresh_cache = 1;
-			if (checkout_entry(*ce, &costate, NULL) ||
-			    lstat(old_name, st))
-				return -1;
-		}
-		if (!cached && verify_index_match(*ce, st))
-			return error("%s: does not match index", old_name);
-		if (cached)
-			st_mode = (*ce)->ce_mode;
-	} else if (stat_ret < 0) {
-		if (patch->is_new < 0)
-			goto is_new;
-		return error("%s: %s", old_name, strerror(errno));
-	}
-
-	if (!cached && !tpatch)
-		st_mode = ce_mode_from_stat(*ce, st->st_mode);
-
-	if (patch->is_new < 0)
-		patch->is_new = 0;
-	if (!patch->old_mode)
-		patch->old_mode = st_mode;
-	if ((st_mode ^ patch->old_mode) & S_IFMT)
-		return error("%s: wrong type", old_name);
-	if (st_mode != patch->old_mode)
-		warning("%s has type %o, expected %o",
-			old_name, st_mode, patch->old_mode);
-	if (!patch->new_mode && !patch->is_delete)
-		patch->new_mode = st_mode;
-	return 0;
-
- is_new:
-	patch->is_new = 1;
-	patch->is_delete = 0;
-	patch->old_name = NULL;
-	return 0;
-}
-
-static int check_patch(struct patch *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;
-	struct patch *tpatch;
-	int ok_if_exists;
-	int status;
-
-	patch->rejected = 1; /* we will drop this after we succeed */
-
-	status = check_preimage(patch, &ce, &st);
-	if (status)
-		return status;
-	old_name = patch->old_name;
-
-	if ((tpatch = in_fn_table(new_name)) &&
-			(was_deleted(tpatch) || to_be_deleted(tpatch)))
-		/*
-		 * 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 &&
-	    ((0 < patch->is_new) | (0 < patch->is_rename) | patch->is_copy)) {
-		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) {
-			int err = check_to_create_blob(new_name, ok_if_exists);
-			if (err)
-				return err;
-		}
-		if (!patch->new_mode) {
-			if (0 < patch->is_new)
-				patch->new_mode = S_IFREG | 0644;
-			else
-				patch->new_mode = patch->old_mode;
-		}
-	}
-
-	if (new_name && old_name) {
-		int same = !strcmp(old_name, new_name);
-		if (!patch->new_mode)
-			patch->new_mode = patch->old_mode;
-		if ((patch->old_mode ^ patch->new_mode) & S_IFMT)
-			return error("new mode (%o) of %s does not match old mode (%o)%s%s",
-				patch->new_mode, new_name, patch->old_mode,
-				same ? "" : " of ", same ? "" : old_name);
-	}
-
-	if (apply_data(patch, &st, ce) < 0)
-		return error("%s: patch does not apply", name);
-	patch->rejected = 0;
-	return 0;
-}
-
-static int check_patch_list(struct patch *patch)
-{
-	int err = 0;
-
-	prepare_fn_table(patch);
-	while (patch) {
-		if (apply_verbosely)
-			say_patch_name(stderr,
-				       "Checking patch ", patch, "...\n");
-		err |= check_patch(patch);
-		patch = patch->next;
-	}
-	return err;
-}
-
-/* This function tries to read the sha1 from the current index */
-static int get_current_sha1(const char *path, unsigned char *sha1)
-{
-	int pos;
-
-	if (read_cache() < 0)
-		return -1;
-	pos = cache_name_pos(path, strlen(path));
-	if (pos < 0)
-		return -1;
-	hashcpy(sha1, active_cache[pos]->sha1);
-	return 0;
-}
-
-/* Build an index that contains the just the files needed for a 3way merge */
-static void build_fake_ancestor(struct patch *list, const char *filename)
-{
-	struct patch *patch;
-	struct index_state result = { NULL };
-	int fd;
-
-	/* Once we start supporting the reverse patch, it may be
-	 * worth showing the new sha1 prefix, but until then...
-	 */
-	for (patch = list; patch; patch = patch->next) {
-		const unsigned char *sha1_ptr;
-		unsigned char sha1[20];
-		struct cache_entry *ce;
-		const char *name;
-
-		name = patch->old_name ? patch->old_name : patch->new_name;
-		if (0 < patch->is_new)
-			continue;
-		else if (get_sha1(patch->old_sha1_prefix, sha1))
-			/* git diff has no index line for mode/type changes */
-			if (!patch->lines_added && !patch->lines_deleted) {
-				if (get_current_sha1(patch->new_name, sha1) ||
-				    get_current_sha1(patch->old_name, sha1))
-					die("mode change for %s, which is not "
-						"in current HEAD", name);
-				sha1_ptr = sha1;
-			} else
-				die("sha1 information is lacking or useless "
-					"(%s).", name);
-		else
-			sha1_ptr = sha1;
-
-		ce = make_cache_entry(patch->old_mode, sha1_ptr, name, 0, 0);
-		if (!ce)
-			die("make_cache_entry failed for path '%s'", name);
-		if (add_index_entry(&result, ce, ADD_CACHE_OK_TO_ADD))
-			die ("Could not add %s to temporary index", name);
-	}
-
-	fd = open(filename, O_WRONLY | O_CREAT, 0666);
-	if (fd < 0 || write_index(&result, fd) || close(fd))
-		die ("Could not write temporary index to %s", filename);
-
-	discard_index(&result);
-}
-
-static void stat_patch_list(struct patch *patch)
-{
-	int files, adds, dels;
-
-	for (files = adds = dels = 0 ; patch ; patch = patch->next) {
-		files++;
-		adds += patch->lines_added;
-		dels += patch->lines_deleted;
-		show_stats(patch);
-	}
-
-	printf(" %d files changed, %d insertions(+), %d deletions(-)\n", files, adds, dels);
-}
-
-static void numstat_patch_list(struct patch *patch)
-{
-	for ( ; patch; patch = patch->next) {
-		const char *name;
-		name = patch->new_name ? patch->new_name : patch->old_name;
-		if (patch->is_binary)
-			printf("-\t-\t");
-		else
-			printf("%d\t%d\t", patch->lines_added, patch->lines_deleted);
-		write_name_quoted(name, stdout, line_termination);
-	}
-}
-
-static void show_file_mode_name(const char *newdelete, unsigned int mode, const char *name)
-{
-	if (mode)
-		printf(" %s mode %06o %s\n", newdelete, mode, name);
-	else
-		printf(" %s %s\n", newdelete, name);
-}
-
-static void show_mode_change(struct patch *p, int show_name)
-{
-	if (p->old_mode && p->new_mode && p->old_mode != p->new_mode) {
-		if (show_name)
-			printf(" mode change %06o => %06o %s\n",
-			       p->old_mode, p->new_mode, p->new_name);
-		else
-			printf(" mode change %06o => %06o\n",
-			       p->old_mode, p->new_mode);
-	}
-}
-
-static void show_rename_copy(struct patch *p)
-{
-	const char *renamecopy = p->is_rename ? "rename" : "copy";
-	const char *old, *new;
-
-	/* Find common prefix */
-	old = p->old_name;
-	new = p->new_name;
-	while (1) {
-		const char *slash_old, *slash_new;
-		slash_old = strchr(old, '/');
-		slash_new = strchr(new, '/');
-		if (!slash_old ||
-		    !slash_new ||
-		    slash_old - old != slash_new - new ||
-		    memcmp(old, new, slash_new - new))
-			break;
-		old = slash_old + 1;
-		new = slash_new + 1;
-	}
-	/* p->old_name thru old is the common prefix, and old and new
-	 * through the end of names are renames
-	 */
-	if (old != p->old_name)
-		printf(" %s %.*s{%s => %s} (%d%%)\n", renamecopy,
-		       (int)(old - p->old_name), p->old_name,
-		       old, new, p->score);
-	else
-		printf(" %s %s => %s (%d%%)\n", renamecopy,
-		       p->old_name, p->new_name, p->score);
-	show_mode_change(p, 0);
-}
-
-static void summary_patch_list(struct patch *patch)
-{
-	struct patch *p;
-
-	for (p = patch; p; p = p->next) {
-		if (p->is_new)
-			show_file_mode_name("create", p->new_mode, p->new_name);
-		else if (p->is_delete)
-			show_file_mode_name("delete", p->old_mode, p->old_name);
-		else {
-			if (p->is_rename || p->is_copy)
-				show_rename_copy(p);
-			else {
-				if (p->score) {
-					printf(" rewrite %s (%d%%)\n",
-					       p->new_name, p->score);
-					show_mode_change(p, 0);
-				}
-				else
-					show_mode_change(p, 1);
-			}
-		}
-	}
-}
-
-static void patch_stats(struct patch *patch)
-{
-	int lines = patch->lines_added + patch->lines_deleted;
-
-	if (lines > max_change)
-		max_change = lines;
-	if (patch->old_name) {
-		int len = quote_c_style(patch->old_name, NULL, NULL, 0);
-		if (!len)
-			len = strlen(patch->old_name);
-		if (len > max_len)
-			max_len = len;
-	}
-	if (patch->new_name) {
-		int len = quote_c_style(patch->new_name, NULL, NULL, 0);
-		if (!len)
-			len = strlen(patch->new_name);
-		if (len > max_len)
-			max_len = len;
-	}
-}
-
-static void remove_file(struct patch *patch, int rmdir_empty)
-{
-	if (update_index) {
-		if (remove_file_from_cache(patch->old_name) < 0)
-			die("unable to remove %s from index", patch->old_name);
-	}
-	if (!cached) {
-		if (S_ISGITLINK(patch->old_mode)) {
-			if (rmdir(patch->old_name))
-				warning("unable to remove submodule %s",
-					patch->old_name);
-		} else if (!unlink_or_warn(patch->old_name) && rmdir_empty) {
-			remove_path(patch->old_name);
-		}
-	}
-}
-
-static void add_index_file(const char *path, unsigned mode, void *buf, unsigned long size)
-{
-	struct stat st;
-	struct cache_entry *ce;
-	int namelen = strlen(path);
-	unsigned ce_size = cache_entry_size(namelen);
-
-	if (!update_index)
-		return;
-
-	ce = xcalloc(1, ce_size);
-	memcpy(ce->name, path, namelen);
-	ce->ce_mode = create_ce_mode(mode);
-	ce->ce_flags = namelen;
-	if (S_ISGITLINK(mode)) {
-		const char *s = buf;
-
-		if (get_sha1_hex(s + strlen("Subproject commit "), ce->sha1))
-			die("corrupt patch for subproject %s", path);
-	} else {
-		if (!cached) {
-			if (lstat(path, &st) < 0)
-				die_errno("unable to stat newly created file '%s'",
-					  path);
-			fill_stat_cache_info(ce, &st);
-		}
-		if (write_sha1_file(buf, size, blob_type, ce->sha1) < 0)
-			die("unable to create backing store for newly created file %s", path);
-	}
-	if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0)
-		die("unable to add cache entry for %s", path);
-}
-
-static int try_create_file(const char *path, unsigned int mode, const char *buf, unsigned long size)
-{
-	int fd;
-	struct strbuf nbuf = STRBUF_INIT;
-
-	if (S_ISGITLINK(mode)) {
-		struct stat st;
-		if (!lstat(path, &st) && S_ISDIR(st.st_mode))
-			return 0;
-		return mkdir(path, 0777);
-	}
-
-	if (has_symlinks && 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)
-		return -1;
-
-	if (convert_to_working_tree(path, buf, size, &nbuf)) {
-		size = nbuf.len;
-		buf  = nbuf.buf;
-	}
-	write_or_die(fd, buf, size);
-	strbuf_release(&nbuf);
-
-	if (close(fd) < 0)
-		die_errno("closing file '%s'", path);
-	return 0;
-}
-
-/*
- * We optimistically assume that the directories exist,
- * which is true 99% of the time anyway. If they don't,
- * we create them and try again.
- */
-static void create_one_file(char *path, unsigned mode, const char *buf, unsigned long size)
-{
-	if (cached)
-		return;
-	if (!try_create_file(path, mode, buf, size))
-		return;
-
-	if (errno == ENOENT) {
-		if (safe_create_leading_directories(path))
-			return;
-		if (!try_create_file(path, mode, buf, size))
-			return;
-	}
-
-	if (errno == EEXIST || errno == EACCES) {
-		/* We may be trying to create a file where a directory
-		 * used to be.
-		 */
-		struct stat st;
-		if (!lstat(path, &st) && (!S_ISDIR(st.st_mode) || !rmdir(path)))
-			errno = EEXIST;
-	}
-
-	if (errno == EEXIST) {
-		unsigned int nr = getpid();
-
-		for (;;) {
-			char newpath[PATH_MAX];
-			mksnpath(newpath, sizeof(newpath), "%s~%u", path, nr);
-			if (!try_create_file(newpath, mode, buf, size)) {
-				if (!rename(newpath, path))
-					return;
-				unlink_or_warn(newpath);
-				break;
-			}
-			if (errno != EEXIST)
-				break;
-			++nr;
-		}
-	}
-	die_errno("unable to write file '%s' mode %o", path, mode);
-}
-
-static void create_file(struct patch *patch)
-{
-	char *path = patch->new_name;
-	unsigned mode = patch->new_mode;
-	unsigned long size = patch->resultsize;
-	char *buf = patch->result;
-
-	if (!mode)
-		mode = S_IFREG | 0644;
-	create_one_file(path, mode, buf, size);
-	add_index_file(path, mode, buf, size);
-}
-
-/* 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) {
-		if (phase == 0)
-			remove_file(patch, 1);
-		return;
-	}
-	if (patch->is_new > 0 || patch->is_copy) {
-		if (phase == 1)
-			create_file(patch);
-		return;
-	}
-	/*
-	 * Rename or modification boils down to the same
-	 * thing: remove the old, write the new
-	 */
-	if (phase == 0)
-		remove_file(patch, patch->is_rename);
-	if (phase == 1)
-		create_file(patch);
-}
-
-static int write_out_one_reject(struct patch *patch)
-{
-	FILE *rej;
-	char namebuf[PATH_MAX];
-	struct fragment *frag;
-	int cnt = 0;
-
-	for (cnt = 0, frag = patch->fragments; frag; frag = frag->next) {
-		if (!frag->rejected)
-			continue;
-		cnt++;
-	}
-
-	if (!cnt) {
-		if (apply_verbosely)
-			say_patch_name(stderr,
-				       "Applied patch ", patch, " cleanly.\n");
-		return 0;
-	}
-
-	/* This should not happen, because a removal patch that leaves
-	 * contents are marked "rejected" at the patch level.
-	 */
-	if (!patch->new_name)
-		die("internal error");
-
-	/* Say this even without --verbose */
-	say_patch_name(stderr, "Applying patch ", patch, " with");
-	fprintf(stderr, " %d rejects...\n", cnt);
-
-	cnt = strlen(patch->new_name);
-	if (ARRAY_SIZE(namebuf) <= cnt + 5) {
-		cnt = ARRAY_SIZE(namebuf) - 5;
-		warning("truncating .rej filename to %.*s.rej",
-			cnt - 1, patch->new_name);
-	}
-	memcpy(namebuf, patch->new_name, cnt);
-	memcpy(namebuf + cnt, ".rej", 5);
-
-	rej = fopen(namebuf, "w");
-	if (!rej)
-		return error("cannot open %s: %s", namebuf, strerror(errno));
-
-	/* Normal git tools never deal with .rej, so do not pretend
-	 * this is a git patch by saying --git nor give extended
-	 * headers.  While at it, maybe please "kompare" that wants
-	 * the trailing TAB and some garbage at the end of line ;-).
-	 */
-	fprintf(rej, "diff a/%s b/%s\t(rejected hunks)\n",
-		patch->new_name, patch->new_name);
-	for (cnt = 1, frag = patch->fragments;
-	     frag;
-	     cnt++, frag = frag->next) {
-		if (!frag->rejected) {
-			fprintf(stderr, "Hunk #%d applied cleanly.\n", cnt);
-			continue;
-		}
-		fprintf(stderr, "Rejected hunk #%d.\n", cnt);
-		fprintf(rej, "%.*s", frag->size, frag->patch);
-		if (frag->patch[frag->size-1] != '\n')
-			fputc('\n', rej);
-	}
-	fclose(rej);
-	return -1;
-}
-
-static int write_out_results(struct patch *list, int skipped_patch)
-{
-	int phase;
-	int errs = 0;
-	struct patch *l;
-
-	if (!list && !skipped_patch)
-		return error("No changes");
-
-	for (phase = 0; phase < 2; phase++) {
-		l = list;
-		while (l) {
-			if (l->rejected)
-				errs = 1;
-			else {
-				write_out_one_result(l, phase);
-				if (phase == 1 && write_out_one_reject(l))
-					errs = 1;
-			}
-			l = l->next;
-		}
-	}
-	return errs;
-}
-
-static struct lock_file lock_file;
-
-static struct string_list limit_by_name;
-static int has_include;
-static void add_name_limit(const char *name, int exclude)
-{
-	struct string_list_item *it;
-
-	it = string_list_append(name, &limit_by_name);
-	it->util = exclude ? NULL : (void *) 1;
-}
-
-static int use_patch(struct patch *p)
-{
-	const char *pathname = p->new_name ? p->new_name : p->old_name;
-	int i;
-
-	/* Paths outside are not touched regardless of "--include" */
-	if (0 < prefix_length) {
-		int pathlen = strlen(pathname);
-		if (pathlen <= prefix_length ||
-		    memcmp(prefix, pathname, prefix_length))
-			return 0;
-	}
-
-	/* See if it matches any of exclude/include rule */
-	for (i = 0; i < limit_by_name.nr; i++) {
-		struct string_list_item *it = &limit_by_name.items[i];
-		if (!fnmatch(it->string, pathname, 0))
-			return (it->util != NULL);
-	}
-
-	/*
-	 * If we had any include, a path that does not match any rule is
-	 * not used.  Otherwise, we saw bunch of exclude rules (or none)
-	 * and such a path is used.
-	 */
-	return !has_include;
-}
-
-
-static void prefix_one(char **name)
-{
-	char *old_name = *name;
-	if (!old_name)
-		return;
-	*name = xstrdup(prefix_filename(prefix, prefix_length, *name));
-	free(old_name);
-}
-
-static void prefix_patches(struct patch *p)
-{
-	if (!prefix || p->is_toplevel_relative)
-		return;
-	for ( ; p; p = p->next) {
-		if (p->new_name == p->old_name) {
-			char *prefixed = p->new_name;
-			prefix_one(&prefixed);
-			p->new_name = p->old_name = prefixed;
-		}
-		else {
-			prefix_one(&p->new_name);
-			prefix_one(&p->old_name);
-		}
-	}
-}
-
-#define INACCURATE_EOF	(1<<0)
-#define RECOUNT		(1<<1)
-
-static int apply_patch(int fd, const char *filename, int options)
-{
-	size_t offset;
-	struct strbuf buf = STRBUF_INIT;
-	struct patch *list = NULL, **listp = &list;
-	int skipped_patch = 0;
-
-	/* FIXME - memory leak when using multiple patch files as inputs */
-	memset(&fn_table, 0, sizeof(struct string_list));
-	patch_input_file = filename;
-	read_patch_file(&buf, fd);
-	offset = 0;
-	while (offset < buf.len) {
-		struct patch *patch;
-		int nr;
-
-		patch = xcalloc(1, sizeof(*patch));
-		patch->inaccurate_eof = !!(options & INACCURATE_EOF);
-		patch->recount =  !!(options & RECOUNT);
-		nr = parse_chunk(buf.buf + offset, buf.len - offset, patch);
-		if (nr < 0)
-			break;
-		if (apply_in_reverse)
-			reverse_patches(patch);
-		if (prefix)
-			prefix_patches(patch);
-		if (use_patch(patch)) {
-			patch_stats(patch);
-			*listp = patch;
-			listp = &patch->next;
-		}
-		else {
-			/* perhaps free it a bit better? */
-			free(patch);
-			skipped_patch++;
-		}
-		offset += nr;
-	}
-
-	if (whitespace_error && (ws_error_action == die_on_ws_error))
-		apply = 0;
-
-	update_index = check_index && apply;
-	if (update_index && newfd < 0)
-		newfd = hold_locked_index(&lock_file, 1);
-
-	if (check_index) {
-		if (read_cache() < 0)
-			die("unable to read index file");
-	}
-
-	if ((check || apply) &&
-	    check_patch_list(list) < 0 &&
-	    !apply_with_reject)
-		exit(1);
-
-	if (apply && write_out_results(list, skipped_patch))
-		exit(1);
-
-	if (fake_ancestor)
-		build_fake_ancestor(list, fake_ancestor);
-
-	if (diffstat)
-		stat_patch_list(list);
-
-	if (numstat)
-		numstat_patch_list(list);
-
-	if (summary)
-		summary_patch_list(list);
-
-	strbuf_release(&buf);
-	return 0;
-}
-
-static int git_apply_config(const char *var, const char *value, void *cb)
-{
-	if (!strcmp(var, "apply.whitespace"))
-		return git_config_string(&apply_default_whitespace, var, value);
-	return git_default_config(var, value, cb);
-}
-
-static int option_parse_exclude(const struct option *opt,
-				const char *arg, int unset)
-{
-	add_name_limit(arg, 1);
-	return 0;
-}
-
-static int option_parse_include(const struct option *opt,
-				const char *arg, int unset)
-{
-	add_name_limit(arg, 0);
-	has_include = 1;
-	return 0;
-}
-
-static int option_parse_p(const struct option *opt,
-			  const char *arg, int unset)
-{
-	p_value = atoi(arg);
-	p_value_known = 1;
-	return 0;
-}
-
-static int option_parse_z(const struct option *opt,
-			  const char *arg, int unset)
-{
-	if (unset)
-		line_termination = '\n';
-	else
-		line_termination = 0;
-	return 0;
-}
-
-static int option_parse_whitespace(const struct option *opt,
-				   const char *arg, int unset)
-{
-	const char **whitespace_option = opt->value;
-
-	*whitespace_option = arg;
-	parse_whitespace_option(arg);
-	return 0;
-}
-
-static int option_parse_directory(const struct option *opt,
-				  const char *arg, int unset)
-{
-	root_len = strlen(arg);
-	if (root_len && arg[root_len - 1] != '/') {
-		char *new_root;
-		root = new_root = xmalloc(root_len + 2);
-		strcpy(new_root, arg);
-		strcpy(new_root + root_len++, "/");
-	} else
-		root = arg;
-	return 0;
-}
-
-int cmd_apply(int argc, const char **argv, const char *unused_prefix)
-{
-	int i;
-	int errs = 0;
-	int is_not_gitdir;
-	int binary;
-	int force_apply = 0;
-
-	const char *whitespace_option = NULL;
-
-	struct option builtin_apply_options[] = {
-		{ OPTION_CALLBACK, 0, "exclude", NULL, "path",
-			"don't apply changes matching the given path",
-			0, option_parse_exclude },
-		{ OPTION_CALLBACK, 0, "include", NULL, "path",
-			"apply changes matching the given path",
-			0, option_parse_include },
-		{ OPTION_CALLBACK, 'p', NULL, NULL, "num",
-			"remove <num> leading slashes from traditional diff paths",
-			0, option_parse_p },
-		OPT_BOOLEAN(0, "no-add", &no_add,
-			"ignore additions made by the patch"),
-		OPT_BOOLEAN(0, "stat", &diffstat,
-			"instead of applying the patch, output diffstat for the input"),
-		{ OPTION_BOOLEAN, 0, "allow-binary-replacement", &binary,
-		  NULL, "old option, now no-op",
-		  PARSE_OPT_HIDDEN | PARSE_OPT_NOARG },
-		{ OPTION_BOOLEAN, 0, "binary", &binary,
-		  NULL, "old option, now no-op",
-		  PARSE_OPT_HIDDEN | PARSE_OPT_NOARG },
-		OPT_BOOLEAN(0, "numstat", &numstat,
-			"shows number of added and deleted lines in decimal notation"),
-		OPT_BOOLEAN(0, "summary", &summary,
-			"instead of applying the patch, output a summary for the input"),
-		OPT_BOOLEAN(0, "check", &check,
-			"instead of applying the patch, see if the patch is applicable"),
-		OPT_BOOLEAN(0, "index", &check_index,
-			"make sure the patch is applicable to the current index"),
-		OPT_BOOLEAN(0, "cached", &cached,
-			"apply a patch without touching the working tree"),
-		OPT_BOOLEAN(0, "apply", &force_apply,
-			"also apply the patch (use with --stat/--summary/--check)"),
-		OPT_FILENAME(0, "build-fake-ancestor", &fake_ancestor,
-			"build a temporary index based on embedded index information"),
-		{ OPTION_CALLBACK, 'z', NULL, NULL, NULL,
-			"paths are separated with NUL character",
-			PARSE_OPT_NOARG, option_parse_z },
-		OPT_INTEGER('C', NULL, &p_context,
-				"ensure at least <n> lines of context match"),
-		{ OPTION_CALLBACK, 0, "whitespace", &whitespace_option, "action",
-			"detect new or modified lines that have whitespace errors",
-			0, option_parse_whitespace },
-		OPT_BOOLEAN('R', "reverse", &apply_in_reverse,
-			"apply the patch in reverse"),
-		OPT_BOOLEAN(0, "unidiff-zero", &unidiff_zero,
-			"don't expect at least one line of context"),
-		OPT_BOOLEAN(0, "reject", &apply_with_reject,
-			"leave the rejected hunks in corresponding *.rej files"),
-		OPT__VERBOSE(&apply_verbosely),
-		OPT_BIT(0, "inaccurate-eof", &options,
-			"tolerate incorrectly detected missing new-line at the end of file",
-			INACCURATE_EOF),
-		OPT_BIT(0, "recount", &options,
-			"do not trust the line counts in the hunk headers",
-			RECOUNT),
-		{ OPTION_CALLBACK, 0, "directory", NULL, "root",
-			"prepend <root> to all filenames",
-			0, option_parse_directory },
-		OPT_END()
-	};
-
-	prefix = setup_git_directory_gently(&is_not_gitdir);
-	prefix_length = prefix ? strlen(prefix) : 0;
-	git_config(git_apply_config, NULL);
-	if (apply_default_whitespace)
-		parse_whitespace_option(apply_default_whitespace);
-
-	argc = parse_options(argc, argv, prefix, builtin_apply_options,
-			apply_usage, 0);
-
-	if (apply_with_reject)
-		apply = apply_verbosely = 1;
-	if (!force_apply && (diffstat || numstat || summary || check || fake_ancestor))
-		apply = 0;
-	if (check_index && is_not_gitdir)
-		die("--index outside a repository");
-	if (cached) {
-		if (is_not_gitdir)
-			die("--cached outside a repository");
-		check_index = 1;
-	}
-	for (i = 0; i < argc; i++) {
-		const char *arg = argv[i];
-		int fd;
-
-		if (!strcmp(arg, "-")) {
-			errs |= apply_patch(0, "<stdin>", options);
-			read_stdin = 0;
-			continue;
-		} else if (0 < prefix_length)
-			arg = prefix_filename(prefix, prefix_length, arg);
-
-		fd = open(arg, O_RDONLY);
-		if (fd < 0)
-			die_errno("can't open patch '%s'", arg);
-		read_stdin = 0;
-		set_default_whitespace_mode(whitespace_option);
-		errs |= apply_patch(fd, arg, options);
-		close(fd);
-	}
-	set_default_whitespace_mode(whitespace_option);
-	if (read_stdin)
-		errs |= apply_patch(0, "<stdin>", options);
-	if (whitespace_error) {
-		if (squelch_whitespace_errors &&
-		    squelch_whitespace_errors < whitespace_error) {
-			int squelched =
-				whitespace_error - squelch_whitespace_errors;
-			warning("squelched %d "
-				"whitespace error%s",
-				squelched,
-				squelched == 1 ? "" : "s");
-		}
-		if (ws_error_action == die_on_ws_error)
-			die("%d line%s add%s whitespace errors.",
-			    whitespace_error,
-			    whitespace_error == 1 ? "" : "s",
-			    whitespace_error == 1 ? "s" : "");
-		if (applied_after_fixing_ws && apply)
-			warning("%d line%s applied after"
-				" fixing whitespace errors.",
-				applied_after_fixing_ws,
-				applied_after_fixing_ws == 1 ? "" : "s");
-		else if (whitespace_error)
-			warning("%d line%s add%s whitespace errors.",
-				whitespace_error,
-				whitespace_error == 1 ? "" : "s",
-				whitespace_error == 1 ? "s" : "");
-	}
-
-	if (update_index) {
-		if (write_cache(newfd, active_cache, active_nr) ||
-		    commit_locked_index(&lock_file))
-			die("Unable to write new index file");
-	}
-
-	return !!errs;
-}
diff --git a/builtin-archive.c b/builtin-archive.c
deleted file mode 100644
index f9a4bea..0000000
--- a/builtin-archive.c
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (c) 2006 Franck Bui-Huu
- * Copyright (c) 2006 Rene Scharfe
- */
-#include "cache.h"
-#include "builtin.h"
-#include "archive.h"
-#include "parse-options.h"
-#include "pkt-line.h"
-#include "sideband.h"
-
-static void create_output_file(const char *output_file)
-{
-	int output_fd = open(output_file, O_CREAT | O_WRONLY | O_TRUNC, 0666);
-	if (output_fd < 0)
-		die_errno("could not create archive file '%s'", output_file);
-	if (output_fd != 1) {
-		if (dup2(output_fd, 1) < 0)
-			die_errno("could not redirect output");
-		else
-			close(output_fd);
-	}
-}
-
-static int run_remote_archiver(int argc, const char **argv,
-			       const char *remote, const char *exec)
-{
-	char *url, buf[LARGE_PACKET_MAX];
-	int fd[2], i, len, rv;
-	struct child_process *conn;
-
-	url = xstrdup(remote);
-	conn = git_connect(fd, url, exec, 0);
-
-	for (i = 1; i < argc; i++)
-		packet_write(fd[1], "argument %s\n", argv[i]);
-	packet_flush(fd[1]);
-
-	len = packet_read_line(fd[0], buf, sizeof(buf));
-	if (!len)
-		die("git archive: expected ACK/NAK, got EOF");
-	if (buf[len-1] == '\n')
-		buf[--len] = 0;
-	if (strcmp(buf, "ACK")) {
-		if (len > 5 && !prefixcmp(buf, "NACK "))
-			die("git archive: NACK %s", buf + 5);
-		die("git archive: protocol error");
-	}
-
-	len = packet_read_line(fd[0], buf, sizeof(buf));
-	if (len)
-		die("git archive: expected a flush");
-
-	/* Now, start reading from fd[0] and spit it out to stdout */
-	rv = recv_sideband("archive", fd[0], 1);
-	close(fd[0]);
-	close(fd[1]);
-	rv |= finish_connect(conn);
-
-	return !!rv;
-}
-
-#define PARSE_OPT_KEEP_ALL ( PARSE_OPT_KEEP_DASHDASH | 	\
-			     PARSE_OPT_KEEP_ARGV0 | 	\
-			     PARSE_OPT_KEEP_UNKNOWN |	\
-			     PARSE_OPT_NO_INTERNAL_HELP	)
-
-int cmd_archive(int argc, const char **argv, const char *prefix)
-{
-	const char *exec = "git-upload-archive";
-	const char *output = NULL;
-	const char *remote = NULL;
-	struct option local_opts[] = {
-		OPT_STRING(0, "output", &output, "file",
-			"write the archive to this file"),
-		OPT_STRING(0, "remote", &remote, "repo",
-			"retrieve the archive from remote repository <repo>"),
-		OPT_STRING(0, "exec", &exec, "cmd",
-			"path to the remote git-upload-archive command"),
-		OPT_END()
-	};
-
-	argc = parse_options(argc, argv, prefix, local_opts, NULL,
-			     PARSE_OPT_KEEP_ALL);
-
-	if (output)
-		create_output_file(output);
-
-	if (remote)
-		return run_remote_archiver(argc, argv, remote, exec);
-
-	setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
-
-	return write_archive(argc, argv, prefix, 1);
-}
diff --git a/builtin-bisect--helper.c b/builtin-bisect--helper.c
deleted file mode 100644
index 5b22639..0000000
--- a/builtin-bisect--helper.c
+++ /dev/null
@@ -1,28 +0,0 @@
-#include "builtin.h"
-#include "cache.h"
-#include "parse-options.h"
-#include "bisect.h"
-
-static const char * const git_bisect_helper_usage[] = {
-	"git bisect--helper --next-all",
-	NULL
-};
-
-int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
-{
-	int next_all = 0;
-	struct option options[] = {
-		OPT_BOOLEAN(0, "next-all", &next_all,
-			    "perform 'git bisect next'"),
-		OPT_END()
-	};
-
-	argc = parse_options(argc, argv, prefix, options,
-			     git_bisect_helper_usage, 0);
-
-	if (!next_all)
-		usage_with_options(git_bisect_helper_usage, options);
-
-	/* next-all */
-	return bisect_next_all(prefix);
-}
diff --git a/builtin-blame.c b/builtin-blame.c
deleted file mode 100644
index fd6ca51..0000000
--- a/builtin-blame.c
+++ /dev/null
@@ -1,2463 +0,0 @@
-/*
- * Blame
- *
- * Copyright (c) 2006, Junio C Hamano
- */
-
-#include "cache.h"
-#include "builtin.h"
-#include "blob.h"
-#include "commit.h"
-#include "tag.h"
-#include "tree-walk.h"
-#include "diff.h"
-#include "diffcore.h"
-#include "revision.h"
-#include "quote.h"
-#include "xdiff-interface.h"
-#include "cache-tree.h"
-#include "string-list.h"
-#include "mailmap.h"
-#include "parse-options.h"
-#include "utf8.h"
-
-static char blame_usage[] = "git blame [options] [rev-opts] [rev] [--] file";
-
-static const char *blame_opt_usage[] = {
-	blame_usage,
-	"",
-	"[rev-opts] are documented in git-rev-list(1)",
-	NULL
-};
-
-static int longest_file;
-static int longest_author;
-static int max_orig_digits;
-static int max_digits;
-static int max_score_digits;
-static int show_root;
-static int reverse;
-static int blank_boundary;
-static int incremental;
-static int xdl_opts = XDF_NEED_MINIMAL;
-
-static enum date_mode blame_date_mode = DATE_ISO8601;
-static size_t blame_date_width;
-
-static struct string_list mailmap;
-
-#ifndef DEBUG
-#define DEBUG 0
-#endif
-
-/* stats */
-static int num_read_blob;
-static int num_get_patch;
-static int num_commits;
-
-#define PICKAXE_BLAME_MOVE		01
-#define PICKAXE_BLAME_COPY		02
-#define PICKAXE_BLAME_COPY_HARDER	04
-#define PICKAXE_BLAME_COPY_HARDEST	010
-
-/*
- * blame for a blame_entry with score lower than these thresholds
- * is not passed to the parent using move/copy logic.
- */
-static unsigned blame_move_score;
-static unsigned blame_copy_score;
-#define BLAME_DEFAULT_MOVE_SCORE	20
-#define BLAME_DEFAULT_COPY_SCORE	40
-
-/* bits #0..7 in revision.h, #8..11 used for merge_bases() in commit.c */
-#define METAINFO_SHOWN		(1u<<12)
-#define MORE_THAN_ONE_PATH	(1u<<13)
-
-/*
- * One blob in a commit that is being suspected
- */
-struct origin {
-	int refcnt;
-	struct origin *previous;
-	struct commit *commit;
-	mmfile_t file;
-	unsigned char blob_sha1[20];
-	char path[FLEX_ARRAY];
-};
-
-/*
- * Given an origin, prepare mmfile_t structure to be used by the
- * diff machinery
- */
-static void fill_origin_blob(struct origin *o, mmfile_t *file)
-{
-	if (!o->file.ptr) {
-		enum object_type type;
-		num_read_blob++;
-		file->ptr = read_sha1_file(o->blob_sha1, &type,
-					   (unsigned long *)(&(file->size)));
-		if (!file->ptr)
-			die("Cannot read blob %s for path %s",
-			    sha1_to_hex(o->blob_sha1),
-			    o->path);
-		o->file = *file;
-	}
-	else
-		*file = o->file;
-}
-
-/*
- * Origin is refcounted and usually we keep the blob contents to be
- * reused.
- */
-static inline struct origin *origin_incref(struct origin *o)
-{
-	if (o)
-		o->refcnt++;
-	return o;
-}
-
-static void origin_decref(struct origin *o)
-{
-	if (o && --o->refcnt <= 0) {
-		if (o->previous)
-			origin_decref(o->previous);
-		free(o->file.ptr);
-		free(o);
-	}
-}
-
-static void drop_origin_blob(struct origin *o)
-{
-	if (o->file.ptr) {
-		free(o->file.ptr);
-		o->file.ptr = NULL;
-	}
-}
-
-/*
- * Each group of lines is described by a blame_entry; it can be split
- * as we pass blame to the parents.  They form a linked list in the
- * scoreboard structure, sorted by the target line number.
- */
-struct blame_entry {
-	struct blame_entry *prev;
-	struct blame_entry *next;
-
-	/* the first line of this group in the final image;
-	 * internally all line numbers are 0 based.
-	 */
-	int lno;
-
-	/* how many lines this group has */
-	int num_lines;
-
-	/* the commit that introduced this group into the final image */
-	struct origin *suspect;
-
-	/* true if the suspect is truly guilty; false while we have not
-	 * checked if the group came from one of its parents.
-	 */
-	char guilty;
-
-	/* true if the entry has been scanned for copies in the current parent
-	 */
-	char scanned;
-
-	/* the line number of the first line of this group in the
-	 * suspect's file; internally all line numbers are 0 based.
-	 */
-	int s_lno;
-
-	/* how significant this entry is -- cached to avoid
-	 * scanning the lines over and over.
-	 */
-	unsigned score;
-};
-
-/*
- * The current state of the blame assignment.
- */
-struct scoreboard {
-	/* the final commit (i.e. where we started digging from) */
-	struct commit *final;
-	struct rev_info *revs;
-	const char *path;
-
-	/*
-	 * The contents in the final image.
-	 * Used by many functions to obtain contents of the nth line,
-	 * indexed with scoreboard.lineno[blame_entry.lno].
-	 */
-	const char *final_buf;
-	unsigned long final_buf_size;
-
-	/* linked list of blames */
-	struct blame_entry *ent;
-
-	/* look-up a line in the final buffer */
-	int num_lines;
-	int *lineno;
-};
-
-static inline int same_suspect(struct origin *a, struct origin *b)
-{
-	if (a == b)
-		return 1;
-	if (a->commit != b->commit)
-		return 0;
-	return !strcmp(a->path, b->path);
-}
-
-static void sanity_check_refcnt(struct scoreboard *);
-
-/*
- * If two blame entries that are next to each other came from
- * contiguous lines in the same origin (i.e. <commit, path> pair),
- * merge them together.
- */
-static void coalesce(struct scoreboard *sb)
-{
-	struct blame_entry *ent, *next;
-
-	for (ent = sb->ent; ent && (next = ent->next); ent = next) {
-		if (same_suspect(ent->suspect, next->suspect) &&
-		    ent->guilty == next->guilty &&
-		    ent->s_lno + ent->num_lines == next->s_lno) {
-			ent->num_lines += next->num_lines;
-			ent->next = next->next;
-			if (ent->next)
-				ent->next->prev = ent;
-			origin_decref(next->suspect);
-			free(next);
-			ent->score = 0;
-			next = ent; /* again */
-		}
-	}
-
-	if (DEBUG) /* sanity */
-		sanity_check_refcnt(sb);
-}
-
-/*
- * Given a commit and a path in it, create a new origin structure.
- * The callers that add blame to the scoreboard should use
- * get_origin() to obtain shared, refcounted copy instead of calling
- * this function directly.
- */
-static struct origin *make_origin(struct commit *commit, const char *path)
-{
-	struct origin *o;
-	o = xcalloc(1, sizeof(*o) + strlen(path) + 1);
-	o->commit = commit;
-	o->refcnt = 1;
-	strcpy(o->path, path);
-	return o;
-}
-
-/*
- * Locate an existing origin or create a new one.
- */
-static struct origin *get_origin(struct scoreboard *sb,
-				 struct commit *commit,
-				 const char *path)
-{
-	struct blame_entry *e;
-
-	for (e = sb->ent; e; e = e->next) {
-		if (e->suspect->commit == commit &&
-		    !strcmp(e->suspect->path, path))
-			return origin_incref(e->suspect);
-	}
-	return make_origin(commit, path);
-}
-
-/*
- * Fill the blob_sha1 field of an origin if it hasn't, so that later
- * call to fill_origin_blob() can use it to locate the data.  blob_sha1
- * for an origin is also used to pass the blame for the entire file to
- * the parent to detect the case where a child's blob is identical to
- * that of its parent's.
- */
-static int fill_blob_sha1(struct origin *origin)
-{
-	unsigned mode;
-
-	if (!is_null_sha1(origin->blob_sha1))
-		return 0;
-	if (get_tree_entry(origin->commit->object.sha1,
-			   origin->path,
-			   origin->blob_sha1, &mode))
-		goto error_out;
-	if (sha1_object_info(origin->blob_sha1, NULL) != OBJ_BLOB)
-		goto error_out;
-	return 0;
- error_out:
-	hashclr(origin->blob_sha1);
-	return -1;
-}
-
-/*
- * We have an origin -- check if the same path exists in the
- * parent and return an origin structure to represent it.
- */
-static struct origin *find_origin(struct scoreboard *sb,
-				  struct commit *parent,
-				  struct origin *origin)
-{
-	struct origin *porigin = NULL;
-	struct diff_options diff_opts;
-	const char *paths[2];
-
-	if (parent->util) {
-		/*
-		 * Each commit object can cache one origin in that
-		 * commit.  This is a freestanding copy of origin and
-		 * not refcounted.
-		 */
-		struct origin *cached = parent->util;
-		if (!strcmp(cached->path, origin->path)) {
-			/*
-			 * The same path between origin and its parent
-			 * without renaming -- the most common case.
-			 */
-			porigin = get_origin(sb, parent, cached->path);
-
-			/*
-			 * If the origin was newly created (i.e. get_origin
-			 * would call make_origin if none is found in the
-			 * scoreboard), it does not know the blob_sha1,
-			 * so copy it.  Otherwise porigin was in the
-			 * scoreboard and already knows blob_sha1.
-			 */
-			if (porigin->refcnt == 1)
-				hashcpy(porigin->blob_sha1, cached->blob_sha1);
-			return porigin;
-		}
-		/* otherwise it was not very useful; free it */
-		free(parent->util);
-		parent->util = NULL;
-	}
-
-	/* See if the origin->path is different between parent
-	 * and origin first.  Most of the time they are the
-	 * same and diff-tree is fairly efficient about this.
-	 */
-	diff_setup(&diff_opts);
-	DIFF_OPT_SET(&diff_opts, RECURSIVE);
-	diff_opts.detect_rename = 0;
-	diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
-	paths[0] = origin->path;
-	paths[1] = NULL;
-
-	diff_tree_setup_paths(paths, &diff_opts);
-	if (diff_setup_done(&diff_opts) < 0)
-		die("diff-setup");
-
-	if (is_null_sha1(origin->commit->object.sha1))
-		do_diff_cache(parent->tree->object.sha1, &diff_opts);
-	else
-		diff_tree_sha1(parent->tree->object.sha1,
-			       origin->commit->tree->object.sha1,
-			       "", &diff_opts);
-	diffcore_std(&diff_opts);
-
-	if (!diff_queued_diff.nr) {
-		/* The path is the same as parent */
-		porigin = get_origin(sb, parent, origin->path);
-		hashcpy(porigin->blob_sha1, origin->blob_sha1);
-	} else {
-		/*
-		 * Since origin->path is a pathspec, if the parent
-		 * commit had it as a directory, we will see a whole
-		 * bunch of deletion of files in the directory that we
-		 * do not care about.
-		 */
-		int i;
-		struct diff_filepair *p = NULL;
-		for (i = 0; i < diff_queued_diff.nr; i++) {
-			const char *name;
-			p = diff_queued_diff.queue[i];
-			name = p->one->path ? p->one->path : p->two->path;
-			if (!strcmp(name, origin->path))
-				break;
-		}
-		if (!p)
-			die("internal error in blame::find_origin");
-		switch (p->status) {
-		default:
-			die("internal error in blame::find_origin (%c)",
-			    p->status);
-		case 'M':
-			porigin = get_origin(sb, parent, origin->path);
-			hashcpy(porigin->blob_sha1, p->one->sha1);
-			break;
-		case 'A':
-		case 'T':
-			/* Did not exist in parent, or type changed */
-			break;
-		}
-	}
-	diff_flush(&diff_opts);
-	diff_tree_release_paths(&diff_opts);
-	if (porigin) {
-		/*
-		 * Create a freestanding copy that is not part of
-		 * the refcounted origin found in the scoreboard, and
-		 * cache it in the commit.
-		 */
-		struct origin *cached;
-
-		cached = make_origin(porigin->commit, porigin->path);
-		hashcpy(cached->blob_sha1, porigin->blob_sha1);
-		parent->util = cached;
-	}
-	return porigin;
-}
-
-/*
- * We have an origin -- find the path that corresponds to it in its
- * parent and return an origin structure to represent it.
- */
-static struct origin *find_rename(struct scoreboard *sb,
-				  struct commit *parent,
-				  struct origin *origin)
-{
-	struct origin *porigin = NULL;
-	struct diff_options diff_opts;
-	int i;
-	const char *paths[2];
-
-	diff_setup(&diff_opts);
-	DIFF_OPT_SET(&diff_opts, RECURSIVE);
-	diff_opts.detect_rename = DIFF_DETECT_RENAME;
-	diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
-	diff_opts.single_follow = origin->path;
-	paths[0] = NULL;
-	diff_tree_setup_paths(paths, &diff_opts);
-	if (diff_setup_done(&diff_opts) < 0)
-		die("diff-setup");
-
-	if (is_null_sha1(origin->commit->object.sha1))
-		do_diff_cache(parent->tree->object.sha1, &diff_opts);
-	else
-		diff_tree_sha1(parent->tree->object.sha1,
-			       origin->commit->tree->object.sha1,
-			       "", &diff_opts);
-	diffcore_std(&diff_opts);
-
-	for (i = 0; i < diff_queued_diff.nr; i++) {
-		struct diff_filepair *p = diff_queued_diff.queue[i];
-		if ((p->status == 'R' || p->status == 'C') &&
-		    !strcmp(p->two->path, origin->path)) {
-			porigin = get_origin(sb, parent, p->one->path);
-			hashcpy(porigin->blob_sha1, p->one->sha1);
-			break;
-		}
-	}
-	diff_flush(&diff_opts);
-	diff_tree_release_paths(&diff_opts);
-	return porigin;
-}
-
-/*
- * Link in a new blame entry to the scoreboard.  Entries that cover the
- * same line range have been removed from the scoreboard previously.
- */
-static void add_blame_entry(struct scoreboard *sb, struct blame_entry *e)
-{
-	struct blame_entry *ent, *prev = NULL;
-
-	origin_incref(e->suspect);
-
-	for (ent = sb->ent; ent && ent->lno < e->lno; ent = ent->next)
-		prev = ent;
-
-	/* prev, if not NULL, is the last one that is below e */
-	e->prev = prev;
-	if (prev) {
-		e->next = prev->next;
-		prev->next = e;
-	}
-	else {
-		e->next = sb->ent;
-		sb->ent = e;
-	}
-	if (e->next)
-		e->next->prev = e;
-}
-
-/*
- * src typically is on-stack; we want to copy the information in it to
- * a malloced blame_entry that is already on the linked list of the
- * scoreboard.  The origin of dst loses a refcnt while the origin of src
- * gains one.
- */
-static void dup_entry(struct blame_entry *dst, struct blame_entry *src)
-{
-	struct blame_entry *p, *n;
-
-	p = dst->prev;
-	n = dst->next;
-	origin_incref(src->suspect);
-	origin_decref(dst->suspect);
-	memcpy(dst, src, sizeof(*src));
-	dst->prev = p;
-	dst->next = n;
-	dst->score = 0;
-}
-
-static const char *nth_line(struct scoreboard *sb, int lno)
-{
-	return sb->final_buf + sb->lineno[lno];
-}
-
-/*
- * It is known that lines between tlno to same came from parent, and e
- * has an overlap with that range.  it also is known that parent's
- * line plno corresponds to e's line tlno.
- *
- *                <---- e ----->
- *                   <------>
- *                   <------------>
- *             <------------>
- *             <------------------>
- *
- * Split e into potentially three parts; before this chunk, the chunk
- * to be blamed for the parent, and after that portion.
- */
-static void split_overlap(struct blame_entry *split,
-			  struct blame_entry *e,
-			  int tlno, int plno, int same,
-			  struct origin *parent)
-{
-	int chunk_end_lno;
-	memset(split, 0, sizeof(struct blame_entry [3]));
-
-	if (e->s_lno < tlno) {
-		/* there is a pre-chunk part not blamed on parent */
-		split[0].suspect = origin_incref(e->suspect);
-		split[0].lno = e->lno;
-		split[0].s_lno = e->s_lno;
-		split[0].num_lines = tlno - e->s_lno;
-		split[1].lno = e->lno + tlno - e->s_lno;
-		split[1].s_lno = plno;
-	}
-	else {
-		split[1].lno = e->lno;
-		split[1].s_lno = plno + (e->s_lno - tlno);
-	}
-
-	if (same < e->s_lno + e->num_lines) {
-		/* there is a post-chunk part not blamed on parent */
-		split[2].suspect = origin_incref(e->suspect);
-		split[2].lno = e->lno + (same - e->s_lno);
-		split[2].s_lno = e->s_lno + (same - e->s_lno);
-		split[2].num_lines = e->s_lno + e->num_lines - same;
-		chunk_end_lno = split[2].lno;
-	}
-	else
-		chunk_end_lno = e->lno + e->num_lines;
-	split[1].num_lines = chunk_end_lno - split[1].lno;
-
-	/*
-	 * if it turns out there is nothing to blame the parent for,
-	 * forget about the splitting.  !split[1].suspect signals this.
-	 */
-	if (split[1].num_lines < 1)
-		return;
-	split[1].suspect = origin_incref(parent);
-}
-
-/*
- * split_overlap() divided an existing blame e into up to three parts
- * in split.  Adjust the linked list of blames in the scoreboard to
- * reflect the split.
- */
-static void split_blame(struct scoreboard *sb,
-			struct blame_entry *split,
-			struct blame_entry *e)
-{
-	struct blame_entry *new_entry;
-
-	if (split[0].suspect && split[2].suspect) {
-		/* The first part (reuse storage for the existing entry e) */
-		dup_entry(e, &split[0]);
-
-		/* The last part -- me */
-		new_entry = xmalloc(sizeof(*new_entry));
-		memcpy(new_entry, &(split[2]), sizeof(struct blame_entry));
-		add_blame_entry(sb, new_entry);
-
-		/* ... and the middle part -- parent */
-		new_entry = xmalloc(sizeof(*new_entry));
-		memcpy(new_entry, &(split[1]), sizeof(struct blame_entry));
-		add_blame_entry(sb, new_entry);
-	}
-	else if (!split[0].suspect && !split[2].suspect)
-		/*
-		 * The parent covers the entire area; reuse storage for
-		 * e and replace it with the parent.
-		 */
-		dup_entry(e, &split[1]);
-	else if (split[0].suspect) {
-		/* me and then parent */
-		dup_entry(e, &split[0]);
-
-		new_entry = xmalloc(sizeof(*new_entry));
-		memcpy(new_entry, &(split[1]), sizeof(struct blame_entry));
-		add_blame_entry(sb, new_entry);
-	}
-	else {
-		/* parent and then me */
-		dup_entry(e, &split[1]);
-
-		new_entry = xmalloc(sizeof(*new_entry));
-		memcpy(new_entry, &(split[2]), sizeof(struct blame_entry));
-		add_blame_entry(sb, new_entry);
-	}
-
-	if (DEBUG) { /* sanity */
-		struct blame_entry *ent;
-		int lno = sb->ent->lno, corrupt = 0;
-
-		for (ent = sb->ent; ent; ent = ent->next) {
-			if (lno != ent->lno)
-				corrupt = 1;
-			if (ent->s_lno < 0)
-				corrupt = 1;
-			lno += ent->num_lines;
-		}
-		if (corrupt) {
-			lno = sb->ent->lno;
-			for (ent = sb->ent; ent; ent = ent->next) {
-				printf("L %8d l %8d n %8d\n",
-				       lno, ent->lno, ent->num_lines);
-				lno = ent->lno + ent->num_lines;
-			}
-			die("oops");
-		}
-	}
-}
-
-/*
- * After splitting the blame, the origins used by the
- * on-stack blame_entry should lose one refcnt each.
- */
-static void decref_split(struct blame_entry *split)
-{
-	int i;
-
-	for (i = 0; i < 3; i++)
-		origin_decref(split[i].suspect);
-}
-
-/*
- * Helper for blame_chunk().  blame_entry e is known to overlap with
- * the patch hunk; split it and pass blame to the parent.
- */
-static void blame_overlap(struct scoreboard *sb, struct blame_entry *e,
-			  int tlno, int plno, int same,
-			  struct origin *parent)
-{
-	struct blame_entry split[3];
-
-	split_overlap(split, e, tlno, plno, same, parent);
-	if (split[1].suspect)
-		split_blame(sb, split, e);
-	decref_split(split);
-}
-
-/*
- * Find the line number of the last line the target is suspected for.
- */
-static int find_last_in_target(struct scoreboard *sb, struct origin *target)
-{
-	struct blame_entry *e;
-	int last_in_target = -1;
-
-	for (e = sb->ent; e; e = e->next) {
-		if (e->guilty || !same_suspect(e->suspect, target))
-			continue;
-		if (last_in_target < e->s_lno + e->num_lines)
-			last_in_target = e->s_lno + e->num_lines;
-	}
-	return last_in_target;
-}
-
-/*
- * Process one hunk from the patch between the current suspect for
- * blame_entry e and its parent.  Find and split the overlap, and
- * pass blame to the overlapping part to the parent.
- */
-static void blame_chunk(struct scoreboard *sb,
-			int tlno, int plno, int same,
-			struct origin *target, struct origin *parent)
-{
-	struct blame_entry *e;
-
-	for (e = sb->ent; e; e = e->next) {
-		if (e->guilty || !same_suspect(e->suspect, target))
-			continue;
-		if (same <= e->s_lno)
-			continue;
-		if (tlno < e->s_lno + e->num_lines)
-			blame_overlap(sb, e, tlno, plno, same, parent);
-	}
-}
-
-struct blame_chunk_cb_data {
-	struct scoreboard *sb;
-	struct origin *target;
-	struct origin *parent;
-	long plno;
-	long tlno;
-};
-
-static void blame_chunk_cb(void *data, long same, long p_next, long t_next)
-{
-	struct blame_chunk_cb_data *d = data;
-	blame_chunk(d->sb, d->tlno, d->plno, same, d->target, d->parent);
-	d->plno = p_next;
-	d->tlno = t_next;
-}
-
-/*
- * We are looking at the origin 'target' and aiming to pass blame
- * for the lines it is suspected to its parent.  Run diff to find
- * which lines came from parent and pass blame for them.
- */
-static int pass_blame_to_parent(struct scoreboard *sb,
-				struct origin *target,
-				struct origin *parent)
-{
-	int last_in_target;
-	mmfile_t file_p, file_o;
-	struct blame_chunk_cb_data d = { sb, target, parent, 0, 0 };
-	xpparam_t xpp;
-	xdemitconf_t xecfg;
-
-	last_in_target = find_last_in_target(sb, target);
-	if (last_in_target < 0)
-		return 1; /* nothing remains for this target */
-
-	fill_origin_blob(parent, &file_p);
-	fill_origin_blob(target, &file_o);
-	num_get_patch++;
-
-	memset(&xpp, 0, sizeof(xpp));
-	xpp.flags = xdl_opts;
-	memset(&xecfg, 0, sizeof(xecfg));
-	xecfg.ctxlen = 0;
-	xdi_diff_hunks(&file_p, &file_o, blame_chunk_cb, &d, &xpp, &xecfg);
-	/* The rest (i.e. anything after tlno) are the same as the parent */
-	blame_chunk(sb, d.tlno, d.plno, last_in_target, target, parent);
-
-	return 0;
-}
-
-/*
- * The lines in blame_entry after splitting blames many times can become
- * very small and trivial, and at some point it becomes pointless to
- * blame the parents.  E.g. "\t\t}\n\t}\n\n" appears everywhere in any
- * ordinary C program, and it is not worth to say it was copied from
- * totally unrelated file in the parent.
- *
- * Compute how trivial the lines in the blame_entry are.
- */
-static unsigned ent_score(struct scoreboard *sb, struct blame_entry *e)
-{
-	unsigned score;
-	const char *cp, *ep;
-
-	if (e->score)
-		return e->score;
-
-	score = 1;
-	cp = nth_line(sb, e->lno);
-	ep = nth_line(sb, e->lno + e->num_lines);
-	while (cp < ep) {
-		unsigned ch = *((unsigned char *)cp);
-		if (isalnum(ch))
-			score++;
-		cp++;
-	}
-	e->score = score;
-	return score;
-}
-
-/*
- * best_so_far[] and this[] are both a split of an existing blame_entry
- * that passes blame to the parent.  Maintain best_so_far the best split
- * so far, by comparing this and best_so_far and copying this into
- * bst_so_far as needed.
- */
-static void copy_split_if_better(struct scoreboard *sb,
-				 struct blame_entry *best_so_far,
-				 struct blame_entry *this)
-{
-	int i;
-
-	if (!this[1].suspect)
-		return;
-	if (best_so_far[1].suspect) {
-		if (ent_score(sb, &this[1]) < ent_score(sb, &best_so_far[1]))
-			return;
-	}
-
-	for (i = 0; i < 3; i++)
-		origin_incref(this[i].suspect);
-	decref_split(best_so_far);
-	memcpy(best_so_far, this, sizeof(struct blame_entry [3]));
-}
-
-/*
- * We are looking at a part of the final image represented by
- * ent (tlno and same are offset by ent->s_lno).
- * tlno is where we are looking at in the final image.
- * up to (but not including) same match preimage.
- * plno is where we are looking at in the preimage.
- *
- * <-------------- final image ---------------------->
- *       <------ent------>
- *         ^tlno ^same
- *    <---------preimage----->
- *         ^plno
- *
- * All line numbers are 0-based.
- */
-static void handle_split(struct scoreboard *sb,
-			 struct blame_entry *ent,
-			 int tlno, int plno, int same,
-			 struct origin *parent,
-			 struct blame_entry *split)
-{
-	if (ent->num_lines <= tlno)
-		return;
-	if (tlno < same) {
-		struct blame_entry this[3];
-		tlno += ent->s_lno;
-		same += ent->s_lno;
-		split_overlap(this, ent, tlno, plno, same, parent);
-		copy_split_if_better(sb, split, this);
-		decref_split(this);
-	}
-}
-
-struct handle_split_cb_data {
-	struct scoreboard *sb;
-	struct blame_entry *ent;
-	struct origin *parent;
-	struct blame_entry *split;
-	long plno;
-	long tlno;
-};
-
-static void handle_split_cb(void *data, long same, long p_next, long t_next)
-{
-	struct handle_split_cb_data *d = data;
-	handle_split(d->sb, d->ent, d->tlno, d->plno, same, d->parent, d->split);
-	d->plno = p_next;
-	d->tlno = t_next;
-}
-
-/*
- * Find the lines from parent that are the same as ent so that
- * we can pass blames to it.  file_p has the blob contents for
- * the parent.
- */
-static void find_copy_in_blob(struct scoreboard *sb,
-			      struct blame_entry *ent,
-			      struct origin *parent,
-			      struct blame_entry *split,
-			      mmfile_t *file_p)
-{
-	const char *cp;
-	int cnt;
-	mmfile_t file_o;
-	struct handle_split_cb_data d = { sb, ent, parent, split, 0, 0 };
-	xpparam_t xpp;
-	xdemitconf_t xecfg;
-
-	/*
-	 * Prepare mmfile that contains only the lines in ent.
-	 */
-	cp = nth_line(sb, ent->lno);
-	file_o.ptr = (char *) cp;
-	cnt = ent->num_lines;
-
-	while (cnt && cp < sb->final_buf + sb->final_buf_size) {
-		if (*cp++ == '\n')
-			cnt--;
-	}
-	file_o.size = cp - file_o.ptr;
-
-	/*
-	 * file_o is a part of final image we are annotating.
-	 * file_p partially may match that image.
-	 */
-	memset(&xpp, 0, sizeof(xpp));
-	xpp.flags = xdl_opts;
-	memset(&xecfg, 0, sizeof(xecfg));
-	xecfg.ctxlen = 1;
-	memset(split, 0, sizeof(struct blame_entry [3]));
-	xdi_diff_hunks(file_p, &file_o, handle_split_cb, &d, &xpp, &xecfg);
-	/* remainder, if any, all match the preimage */
-	handle_split(sb, ent, d.tlno, d.plno, ent->num_lines, parent, split);
-}
-
-/*
- * See if lines currently target is suspected for can be attributed to
- * parent.
- */
-static int find_move_in_parent(struct scoreboard *sb,
-			       struct origin *target,
-			       struct origin *parent)
-{
-	int last_in_target, made_progress;
-	struct blame_entry *e, split[3];
-	mmfile_t file_p;
-
-	last_in_target = find_last_in_target(sb, target);
-	if (last_in_target < 0)
-		return 1; /* nothing remains for this target */
-
-	fill_origin_blob(parent, &file_p);
-	if (!file_p.ptr)
-		return 0;
-
-	made_progress = 1;
-	while (made_progress) {
-		made_progress = 0;
-		for (e = sb->ent; e; e = e->next) {
-			if (e->guilty || !same_suspect(e->suspect, target) ||
-			    ent_score(sb, e) < blame_move_score)
-				continue;
-			find_copy_in_blob(sb, e, parent, split, &file_p);
-			if (split[1].suspect &&
-			    blame_move_score < ent_score(sb, &split[1])) {
-				split_blame(sb, split, e);
-				made_progress = 1;
-			}
-			decref_split(split);
-		}
-	}
-	return 0;
-}
-
-struct blame_list {
-	struct blame_entry *ent;
-	struct blame_entry split[3];
-};
-
-/*
- * Count the number of entries the target is suspected for,
- * and prepare a list of entry and the best split.
- */
-static struct blame_list *setup_blame_list(struct scoreboard *sb,
-					   struct origin *target,
-					   int min_score,
-					   int *num_ents_p)
-{
-	struct blame_entry *e;
-	int num_ents, i;
-	struct blame_list *blame_list = NULL;
-
-	for (e = sb->ent, num_ents = 0; e; e = e->next)
-		if (!e->scanned && !e->guilty &&
-		    same_suspect(e->suspect, target) &&
-		    min_score < ent_score(sb, e))
-			num_ents++;
-	if (num_ents) {
-		blame_list = xcalloc(num_ents, sizeof(struct blame_list));
-		for (e = sb->ent, i = 0; e; e = e->next)
-			if (!e->scanned && !e->guilty &&
-			    same_suspect(e->suspect, target) &&
-			    min_score < ent_score(sb, e))
-				blame_list[i++].ent = e;
-	}
-	*num_ents_p = num_ents;
-	return blame_list;
-}
-
-/*
- * Reset the scanned status on all entries.
- */
-static void reset_scanned_flag(struct scoreboard *sb)
-{
-	struct blame_entry *e;
-	for (e = sb->ent; e; e = e->next)
-		e->scanned = 0;
-}
-
-/*
- * For lines target is suspected for, see if we can find code movement
- * across file boundary from the parent commit.  porigin is the path
- * in the parent we already tried.
- */
-static int find_copy_in_parent(struct scoreboard *sb,
-			       struct origin *target,
-			       struct commit *parent,
-			       struct origin *porigin,
-			       int opt)
-{
-	struct diff_options diff_opts;
-	const char *paths[1];
-	int i, j;
-	int retval;
-	struct blame_list *blame_list;
-	int num_ents;
-
-	blame_list = setup_blame_list(sb, target, blame_copy_score, &num_ents);
-	if (!blame_list)
-		return 1; /* nothing remains for this target */
-
-	diff_setup(&diff_opts);
-	DIFF_OPT_SET(&diff_opts, RECURSIVE);
-	diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
-
-	paths[0] = NULL;
-	diff_tree_setup_paths(paths, &diff_opts);
-	if (diff_setup_done(&diff_opts) < 0)
-		die("diff-setup");
-
-	/* Try "find copies harder" on new path if requested;
-	 * we do not want to use diffcore_rename() actually to
-	 * match things up; find_copies_harder is set only to
-	 * force diff_tree_sha1() to feed all filepairs to diff_queue,
-	 * and this code needs to be after diff_setup_done(), which
-	 * usually makes find-copies-harder imply copy detection.
-	 */
-	if ((opt & PICKAXE_BLAME_COPY_HARDEST)
-	    || ((opt & PICKAXE_BLAME_COPY_HARDER)
-		&& (!porigin || strcmp(target->path, porigin->path))))
-		DIFF_OPT_SET(&diff_opts, FIND_COPIES_HARDER);
-
-	if (is_null_sha1(target->commit->object.sha1))
-		do_diff_cache(parent->tree->object.sha1, &diff_opts);
-	else
-		diff_tree_sha1(parent->tree->object.sha1,
-			       target->commit->tree->object.sha1,
-			       "", &diff_opts);
-
-	if (!DIFF_OPT_TST(&diff_opts, FIND_COPIES_HARDER))
-		diffcore_std(&diff_opts);
-
-	retval = 0;
-	while (1) {
-		int made_progress = 0;
-
-		for (i = 0; i < diff_queued_diff.nr; i++) {
-			struct diff_filepair *p = diff_queued_diff.queue[i];
-			struct origin *norigin;
-			mmfile_t file_p;
-			struct blame_entry this[3];
-
-			if (!DIFF_FILE_VALID(p->one))
-				continue; /* does not exist in parent */
-			if (S_ISGITLINK(p->one->mode))
-				continue; /* ignore git links */
-			if (porigin && !strcmp(p->one->path, porigin->path))
-				/* find_move already dealt with this path */
-				continue;
-
-			norigin = get_origin(sb, parent, p->one->path);
-			hashcpy(norigin->blob_sha1, p->one->sha1);
-			fill_origin_blob(norigin, &file_p);
-			if (!file_p.ptr)
-				continue;
-
-			for (j = 0; j < num_ents; j++) {
-				find_copy_in_blob(sb, blame_list[j].ent,
-						  norigin, this, &file_p);
-				copy_split_if_better(sb, blame_list[j].split,
-						     this);
-				decref_split(this);
-			}
-			origin_decref(norigin);
-		}
-
-		for (j = 0; j < num_ents; j++) {
-			struct blame_entry *split = blame_list[j].split;
-			if (split[1].suspect &&
-			    blame_copy_score < ent_score(sb, &split[1])) {
-				split_blame(sb, split, blame_list[j].ent);
-				made_progress = 1;
-			}
-			else
-				blame_list[j].ent->scanned = 1;
-			decref_split(split);
-		}
-		free(blame_list);
-
-		if (!made_progress)
-			break;
-		blame_list = setup_blame_list(sb, target, blame_copy_score, &num_ents);
-		if (!blame_list) {
-			retval = 1;
-			break;
-		}
-	}
-	reset_scanned_flag(sb);
-	diff_flush(&diff_opts);
-	diff_tree_release_paths(&diff_opts);
-	return retval;
-}
-
-/*
- * The blobs of origin and porigin exactly match, so everything
- * origin is suspected for can be blamed on the parent.
- */
-static void pass_whole_blame(struct scoreboard *sb,
-			     struct origin *origin, struct origin *porigin)
-{
-	struct blame_entry *e;
-
-	if (!porigin->file.ptr && origin->file.ptr) {
-		/* Steal its file */
-		porigin->file = origin->file;
-		origin->file.ptr = NULL;
-	}
-	for (e = sb->ent; e; e = e->next) {
-		if (!same_suspect(e->suspect, origin))
-			continue;
-		origin_incref(porigin);
-		origin_decref(e->suspect);
-		e->suspect = porigin;
-	}
-}
-
-/*
- * We pass blame from the current commit to its parents.  We keep saying
- * "parent" (and "porigin"), but what we mean is to find scapegoat to
- * exonerate ourselves.
- */
-static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit *commit)
-{
-	if (!reverse)
-		return commit->parents;
-	return lookup_decoration(&revs->children, &commit->object);
-}
-
-static int num_scapegoats(struct rev_info *revs, struct commit *commit)
-{
-	int cnt;
-	struct commit_list *l = first_scapegoat(revs, commit);
-	for (cnt = 0; l; l = l->next)
-		cnt++;
-	return cnt;
-}
-
-#define MAXSG 16
-
-static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
-{
-	struct rev_info *revs = sb->revs;
-	int i, pass, num_sg;
-	struct commit *commit = origin->commit;
-	struct commit_list *sg;
-	struct origin *sg_buf[MAXSG];
-	struct origin *porigin, **sg_origin = sg_buf;
-
-	num_sg = num_scapegoats(revs, commit);
-	if (!num_sg)
-		goto finish;
-	else if (num_sg < ARRAY_SIZE(sg_buf))
-		memset(sg_buf, 0, sizeof(sg_buf));
-	else
-		sg_origin = xcalloc(num_sg, sizeof(*sg_origin));
-
-	/*
-	 * The first pass looks for unrenamed path to optimize for
-	 * common cases, then we look for renames in the second pass.
-	 */
-	for (pass = 0; pass < 2; pass++) {
-		struct origin *(*find)(struct scoreboard *,
-				       struct commit *, struct origin *);
-		find = pass ? find_rename : find_origin;
-
-		for (i = 0, sg = first_scapegoat(revs, commit);
-		     i < num_sg && sg;
-		     sg = sg->next, i++) {
-			struct commit *p = sg->item;
-			int j, same;
-
-			if (sg_origin[i])
-				continue;
-			if (parse_commit(p))
-				continue;
-			porigin = find(sb, p, origin);
-			if (!porigin)
-				continue;
-			if (!hashcmp(porigin->blob_sha1, origin->blob_sha1)) {
-				pass_whole_blame(sb, origin, porigin);
-				origin_decref(porigin);
-				goto finish;
-			}
-			for (j = same = 0; j < i; j++)
-				if (sg_origin[j] &&
-				    !hashcmp(sg_origin[j]->blob_sha1,
-					     porigin->blob_sha1)) {
-					same = 1;
-					break;
-				}
-			if (!same)
-				sg_origin[i] = porigin;
-			else
-				origin_decref(porigin);
-		}
-	}
-
-	num_commits++;
-	for (i = 0, sg = first_scapegoat(revs, commit);
-	     i < num_sg && sg;
-	     sg = sg->next, i++) {
-		struct origin *porigin = sg_origin[i];
-		if (!porigin)
-			continue;
-		if (!origin->previous) {
-			origin_incref(porigin);
-			origin->previous = porigin;
-		}
-		if (pass_blame_to_parent(sb, origin, porigin))
-			goto finish;
-	}
-
-	/*
-	 * Optionally find moves in parents' files.
-	 */
-	if (opt & PICKAXE_BLAME_MOVE)
-		for (i = 0, sg = first_scapegoat(revs, commit);
-		     i < num_sg && sg;
-		     sg = sg->next, i++) {
-			struct origin *porigin = sg_origin[i];
-			if (!porigin)
-				continue;
-			if (find_move_in_parent(sb, origin, porigin))
-				goto finish;
-		}
-
-	/*
-	 * Optionally find copies from parents' files.
-	 */
-	if (opt & PICKAXE_BLAME_COPY)
-		for (i = 0, sg = first_scapegoat(revs, commit);
-		     i < num_sg && sg;
-		     sg = sg->next, i++) {
-			struct origin *porigin = sg_origin[i];
-			if (find_copy_in_parent(sb, origin, sg->item,
-						porigin, opt))
-				goto finish;
-		}
-
- finish:
-	for (i = 0; i < num_sg; i++) {
-		if (sg_origin[i]) {
-			drop_origin_blob(sg_origin[i]);
-			origin_decref(sg_origin[i]);
-		}
-	}
-	drop_origin_blob(origin);
-	if (sg_buf != sg_origin)
-		free(sg_origin);
-}
-
-/*
- * Information on commits, used for output.
- */
-struct commit_info
-{
-	const char *author;
-	const char *author_mail;
-	unsigned long author_time;
-	const char *author_tz;
-
-	/* filled only when asked for details */
-	const char *committer;
-	const char *committer_mail;
-	unsigned long committer_time;
-	const char *committer_tz;
-
-	const char *summary;
-};
-
-/*
- * Parse author/committer line in the commit object buffer
- */
-static void get_ac_line(const char *inbuf, const char *what,
-			int person_len, char *person,
-			int mail_len, char *mail,
-			unsigned long *time, const char **tz)
-{
-	int len, tzlen, maillen;
-	char *tmp, *endp, *timepos, *mailpos;
-
-	tmp = strstr(inbuf, what);
-	if (!tmp)
-		goto error_out;
-	tmp += strlen(what);
-	endp = strchr(tmp, '\n');
-	if (!endp)
-		len = strlen(tmp);
-	else
-		len = endp - tmp;
-	if (person_len <= len) {
-	error_out:
-		/* Ugh */
-		*tz = "(unknown)";
-		strcpy(mail, *tz);
-		*time = 0;
-		return;
-	}
-	memcpy(person, tmp, len);
-
-	tmp = person;
-	tmp += len;
-	*tmp = 0;
-	while (*tmp != ' ')
-		tmp--;
-	*tz = tmp+1;
-	tzlen = (person+len)-(tmp+1);
-
-	*tmp = 0;
-	while (*tmp != ' ')
-		tmp--;
-	*time = strtoul(tmp, NULL, 10);
-	timepos = tmp;
-
-	*tmp = 0;
-	while (*tmp != ' ')
-		tmp--;
-	mailpos = tmp + 1;
-	*tmp = 0;
-	maillen = timepos - tmp;
-	memcpy(mail, mailpos, maillen);
-
-	if (!mailmap.nr)
-		return;
-
-	/*
-	 * mailmap expansion may make the name longer.
-	 * make room by pushing stuff down.
-	 */
-	tmp = person + person_len - (tzlen + 1);
-	memmove(tmp, *tz, tzlen);
-	tmp[tzlen] = 0;
-	*tz = tmp;
-
-	/*
-	 * Now, convert both name and e-mail using mailmap
-	 */
-	if(map_user(&mailmap, mail+1, mail_len-1, person, tmp-person-1)) {
-		/* Add a trailing '>' to email, since map_user returns plain emails
-		   Note: It already has '<', since we replace from mail+1 */
-		mailpos = memchr(mail, '\0', mail_len);
-		if (mailpos && mailpos-mail < mail_len - 1) {
-			*mailpos = '>';
-			*(mailpos+1) = '\0';
-		}
-	}
-}
-
-static void get_commit_info(struct commit *commit,
-			    struct commit_info *ret,
-			    int detailed)
-{
-	int len;
-	char *tmp, *endp, *reencoded, *message;
-	static char author_name[1024];
-	static char author_mail[1024];
-	static char committer_name[1024];
-	static char committer_mail[1024];
-	static char summary_buf[1024];
-
-	/*
-	 * We've operated without save_commit_buffer, so
-	 * we now need to populate them for output.
-	 */
-	if (!commit->buffer) {
-		enum object_type type;
-		unsigned long size;
-		commit->buffer =
-			read_sha1_file(commit->object.sha1, &type, &size);
-		if (!commit->buffer)
-			die("Cannot read commit %s",
-			    sha1_to_hex(commit->object.sha1));
-	}
-	reencoded = reencode_commit_message(commit, NULL);
-	message   = reencoded ? reencoded : commit->buffer;
-	ret->author = author_name;
-	ret->author_mail = author_mail;
-	get_ac_line(message, "\nauthor ",
-		    sizeof(author_name), author_name,
-		    sizeof(author_mail), author_mail,
-		    &ret->author_time, &ret->author_tz);
-
-	if (!detailed) {
-		free(reencoded);
-		return;
-	}
-
-	ret->committer = committer_name;
-	ret->committer_mail = committer_mail;
-	get_ac_line(message, "\ncommitter ",
-		    sizeof(committer_name), committer_name,
-		    sizeof(committer_mail), committer_mail,
-		    &ret->committer_time, &ret->committer_tz);
-
-	ret->summary = summary_buf;
-	tmp = strstr(message, "\n\n");
-	if (!tmp) {
-	error_out:
-		sprintf(summary_buf, "(%s)", sha1_to_hex(commit->object.sha1));
-		free(reencoded);
-		return;
-	}
-	tmp += 2;
-	endp = strchr(tmp, '\n');
-	if (!endp)
-		endp = tmp + strlen(tmp);
-	len = endp - tmp;
-	if (len >= sizeof(summary_buf) || len == 0)
-		goto error_out;
-	memcpy(summary_buf, tmp, len);
-	summary_buf[len] = 0;
-	free(reencoded);
-}
-
-/*
- * To allow LF and other nonportable characters in pathnames,
- * they are c-style quoted as needed.
- */
-static void write_filename_info(const char *path)
-{
-	printf("filename ");
-	write_name_quoted(path, stdout, '\n');
-}
-
-/*
- * Porcelain/Incremental format wants to show a lot of details per
- * commit.  Instead of repeating this every line, emit it only once,
- * the first time each commit appears in the output.
- */
-static int emit_one_suspect_detail(struct origin *suspect)
-{
-	struct commit_info ci;
-
-	if (suspect->commit->object.flags & METAINFO_SHOWN)
-		return 0;
-
-	suspect->commit->object.flags |= METAINFO_SHOWN;
-	get_commit_info(suspect->commit, &ci, 1);
-	printf("author %s\n", ci.author);
-	printf("author-mail %s\n", ci.author_mail);
-	printf("author-time %lu\n", ci.author_time);
-	printf("author-tz %s\n", ci.author_tz);
-	printf("committer %s\n", ci.committer);
-	printf("committer-mail %s\n", ci.committer_mail);
-	printf("committer-time %lu\n", ci.committer_time);
-	printf("committer-tz %s\n", ci.committer_tz);
-	printf("summary %s\n", ci.summary);
-	if (suspect->commit->object.flags & UNINTERESTING)
-		printf("boundary\n");
-	if (suspect->previous) {
-		struct origin *prev = suspect->previous;
-		printf("previous %s ", sha1_to_hex(prev->commit->object.sha1));
-		write_name_quoted(prev->path, stdout, '\n');
-	}
-	return 1;
-}
-
-/*
- * The blame_entry is found to be guilty for the range.  Mark it
- * as such, and show it in incremental output.
- */
-static void found_guilty_entry(struct blame_entry *ent)
-{
-	if (ent->guilty)
-		return;
-	ent->guilty = 1;
-	if (incremental) {
-		struct origin *suspect = ent->suspect;
-
-		printf("%s %d %d %d\n",
-		       sha1_to_hex(suspect->commit->object.sha1),
-		       ent->s_lno + 1, ent->lno + 1, ent->num_lines);
-		emit_one_suspect_detail(suspect);
-		write_filename_info(suspect->path);
-		maybe_flush_or_die(stdout, "stdout");
-	}
-}
-
-/*
- * The main loop -- while the scoreboard has lines whose true origin
- * is still unknown, pick one blame_entry, and allow its current
- * suspect to pass blames to its parents.
- */
-static void assign_blame(struct scoreboard *sb, int opt)
-{
-	struct rev_info *revs = sb->revs;
-
-	while (1) {
-		struct blame_entry *ent;
-		struct commit *commit;
-		struct origin *suspect = NULL;
-
-		/* find one suspect to break down */
-		for (ent = sb->ent; !suspect && ent; ent = ent->next)
-			if (!ent->guilty)
-				suspect = ent->suspect;
-		if (!suspect)
-			return; /* all done */
-
-		/*
-		 * We will use this suspect later in the loop,
-		 * so hold onto it in the meantime.
-		 */
-		origin_incref(suspect);
-		commit = suspect->commit;
-		if (!commit->object.parsed)
-			parse_commit(commit);
-		if (reverse ||
-		    (!(commit->object.flags & UNINTERESTING) &&
-		     !(revs->max_age != -1 && commit->date < revs->max_age)))
-			pass_blame(sb, suspect, opt);
-		else {
-			commit->object.flags |= UNINTERESTING;
-			if (commit->object.parsed)
-				mark_parents_uninteresting(commit);
-		}
-		/* treat root commit as boundary */
-		if (!commit->parents && !show_root)
-			commit->object.flags |= UNINTERESTING;
-
-		/* Take responsibility for the remaining entries */
-		for (ent = sb->ent; ent; ent = ent->next)
-			if (same_suspect(ent->suspect, suspect))
-				found_guilty_entry(ent);
-		origin_decref(suspect);
-
-		if (DEBUG) /* sanity */
-			sanity_check_refcnt(sb);
-	}
-}
-
-static const char *format_time(unsigned long time, const char *tz_str,
-			       int show_raw_time)
-{
-	static char time_buf[128];
-	const char *time_str;
-	int time_len;
-	int tz;
-
-	if (show_raw_time) {
-		sprintf(time_buf, "%lu %s", time, tz_str);
-	}
-	else {
-		tz = atoi(tz_str);
-		time_str = show_date(time, tz, blame_date_mode);
-		time_len = strlen(time_str);
-		memcpy(time_buf, time_str, time_len);
-		memset(time_buf + time_len, ' ', blame_date_width - time_len);
-	}
-	return time_buf;
-}
-
-#define OUTPUT_ANNOTATE_COMPAT	001
-#define OUTPUT_LONG_OBJECT_NAME	002
-#define OUTPUT_RAW_TIMESTAMP	004
-#define OUTPUT_PORCELAIN	010
-#define OUTPUT_SHOW_NAME	020
-#define OUTPUT_SHOW_NUMBER	040
-#define OUTPUT_SHOW_SCORE      0100
-#define OUTPUT_NO_AUTHOR       0200
-
-static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent)
-{
-	int cnt;
-	const char *cp;
-	struct origin *suspect = ent->suspect;
-	char hex[41];
-
-	strcpy(hex, sha1_to_hex(suspect->commit->object.sha1));
-	printf("%s%c%d %d %d\n",
-	       hex,
-	       ent->guilty ? ' ' : '*', // purely for debugging
-	       ent->s_lno + 1,
-	       ent->lno + 1,
-	       ent->num_lines);
-	if (emit_one_suspect_detail(suspect) ||
-	    (suspect->commit->object.flags & MORE_THAN_ONE_PATH))
-		write_filename_info(suspect->path);
-
-	cp = nth_line(sb, ent->lno);
-	for (cnt = 0; cnt < ent->num_lines; cnt++) {
-		char ch;
-		if (cnt)
-			printf("%s %d %d\n", hex,
-			       ent->s_lno + 1 + cnt,
-			       ent->lno + 1 + cnt);
-		putchar('\t');
-		do {
-			ch = *cp++;
-			putchar(ch);
-		} while (ch != '\n' &&
-			 cp < sb->final_buf + sb->final_buf_size);
-	}
-}
-
-static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
-{
-	int cnt;
-	const char *cp;
-	struct origin *suspect = ent->suspect;
-	struct commit_info ci;
-	char hex[41];
-	int show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP);
-
-	get_commit_info(suspect->commit, &ci, 1);
-	strcpy(hex, sha1_to_hex(suspect->commit->object.sha1));
-
-	cp = nth_line(sb, ent->lno);
-	for (cnt = 0; cnt < ent->num_lines; cnt++) {
-		char ch;
-		int length = (opt & OUTPUT_LONG_OBJECT_NAME) ? 40 : 8;
-
-		if (suspect->commit->object.flags & UNINTERESTING) {
-			if (blank_boundary)
-				memset(hex, ' ', length);
-			else if (!(opt & OUTPUT_ANNOTATE_COMPAT)) {
-				length--;
-				putchar('^');
-			}
-		}
-
-		printf("%.*s", length, hex);
-		if (opt & OUTPUT_ANNOTATE_COMPAT)
-			printf("\t(%10s\t%10s\t%d)", ci.author,
-			       format_time(ci.author_time, ci.author_tz,
-					   show_raw_time),
-			       ent->lno + 1 + cnt);
-		else {
-			if (opt & OUTPUT_SHOW_SCORE)
-				printf(" %*d %02d",
-				       max_score_digits, ent->score,
-				       ent->suspect->refcnt);
-			if (opt & OUTPUT_SHOW_NAME)
-				printf(" %-*.*s", longest_file, longest_file,
-				       suspect->path);
-			if (opt & OUTPUT_SHOW_NUMBER)
-				printf(" %*d", max_orig_digits,
-				       ent->s_lno + 1 + cnt);
-
-			if (!(opt & OUTPUT_NO_AUTHOR)) {
-				int pad = longest_author - utf8_strwidth(ci.author);
-				printf(" (%s%*s %10s",
-				       ci.author, pad, "",
-				       format_time(ci.author_time,
-						   ci.author_tz,
-						   show_raw_time));
-			}
-			printf(" %*d) ",
-			       max_digits, ent->lno + 1 + cnt);
-		}
-		do {
-			ch = *cp++;
-			putchar(ch);
-		} while (ch != '\n' &&
-			 cp < sb->final_buf + sb->final_buf_size);
-	}
-}
-
-static void output(struct scoreboard *sb, int option)
-{
-	struct blame_entry *ent;
-
-	if (option & OUTPUT_PORCELAIN) {
-		for (ent = sb->ent; ent; ent = ent->next) {
-			struct blame_entry *oth;
-			struct origin *suspect = ent->suspect;
-			struct commit *commit = suspect->commit;
-			if (commit->object.flags & MORE_THAN_ONE_PATH)
-				continue;
-			for (oth = ent->next; oth; oth = oth->next) {
-				if ((oth->suspect->commit != commit) ||
-				    !strcmp(oth->suspect->path, suspect->path))
-					continue;
-				commit->object.flags |= MORE_THAN_ONE_PATH;
-				break;
-			}
-		}
-	}
-
-	for (ent = sb->ent; ent; ent = ent->next) {
-		if (option & OUTPUT_PORCELAIN)
-			emit_porcelain(sb, ent);
-		else {
-			emit_other(sb, ent, option);
-		}
-	}
-}
-
-/*
- * To allow quick access to the contents of nth line in the
- * final image, prepare an index in the scoreboard.
- */
-static int prepare_lines(struct scoreboard *sb)
-{
-	const char *buf = sb->final_buf;
-	unsigned long len = sb->final_buf_size;
-	int num = 0, incomplete = 0, bol = 1;
-
-	if (len && buf[len-1] != '\n')
-		incomplete++; /* incomplete line at the end */
-	while (len--) {
-		if (bol) {
-			sb->lineno = xrealloc(sb->lineno,
-					      sizeof(int *) * (num + 1));
-			sb->lineno[num] = buf - sb->final_buf;
-			bol = 0;
-		}
-		if (*buf++ == '\n') {
-			num++;
-			bol = 1;
-		}
-	}
-	sb->lineno = xrealloc(sb->lineno,
-			      sizeof(int *) * (num + incomplete + 1));
-	sb->lineno[num + incomplete] = buf - sb->final_buf;
-	sb->num_lines = num + incomplete;
-	return sb->num_lines;
-}
-
-/*
- * Add phony grafts for use with -S; this is primarily to
- * support git's cvsserver that wants to give a linear history
- * to its clients.
- */
-static int read_ancestry(const char *graft_file)
-{
-	FILE *fp = fopen(graft_file, "r");
-	char buf[1024];
-	if (!fp)
-		return -1;
-	while (fgets(buf, sizeof(buf), fp)) {
-		/* The format is just "Commit Parent1 Parent2 ...\n" */
-		int len = strlen(buf);
-		struct commit_graft *graft = read_graft_line(buf, len);
-		if (graft)
-			register_commit_graft(graft, 0);
-	}
-	fclose(fp);
-	return 0;
-}
-
-/*
- * How many columns do we need to show line numbers in decimal?
- */
-static int lineno_width(int lines)
-{
-	int i, width;
-
-	for (width = 1, i = 10; i <= lines + 1; width++)
-		i *= 10;
-	return width;
-}
-
-/*
- * How many columns do we need to show line numbers, authors,
- * and filenames?
- */
-static void find_alignment(struct scoreboard *sb, int *option)
-{
-	int longest_src_lines = 0;
-	int longest_dst_lines = 0;
-	unsigned largest_score = 0;
-	struct blame_entry *e;
-
-	for (e = sb->ent; e; e = e->next) {
-		struct origin *suspect = e->suspect;
-		struct commit_info ci;
-		int num;
-
-		if (strcmp(suspect->path, sb->path))
-			*option |= OUTPUT_SHOW_NAME;
-		num = strlen(suspect->path);
-		if (longest_file < num)
-			longest_file = num;
-		if (!(suspect->commit->object.flags & METAINFO_SHOWN)) {
-			suspect->commit->object.flags |= METAINFO_SHOWN;
-			get_commit_info(suspect->commit, &ci, 1);
-			num = utf8_strwidth(ci.author);
-			if (longest_author < num)
-				longest_author = num;
-		}
-		num = e->s_lno + e->num_lines;
-		if (longest_src_lines < num)
-			longest_src_lines = num;
-		num = e->lno + e->num_lines;
-		if (longest_dst_lines < num)
-			longest_dst_lines = num;
-		if (largest_score < ent_score(sb, e))
-			largest_score = ent_score(sb, e);
-	}
-	max_orig_digits = lineno_width(longest_src_lines);
-	max_digits = lineno_width(longest_dst_lines);
-	max_score_digits = lineno_width(largest_score);
-}
-
-/*
- * For debugging -- origin is refcounted, and this asserts that
- * we do not underflow.
- */
-static void sanity_check_refcnt(struct scoreboard *sb)
-{
-	int baa = 0;
-	struct blame_entry *ent;
-
-	for (ent = sb->ent; ent; ent = ent->next) {
-		/* Nobody should have zero or negative refcnt */
-		if (ent->suspect->refcnt <= 0) {
-			fprintf(stderr, "%s in %s has negative refcnt %d\n",
-				ent->suspect->path,
-				sha1_to_hex(ent->suspect->commit->object.sha1),
-				ent->suspect->refcnt);
-			baa = 1;
-		}
-	}
-	if (baa) {
-		int opt = 0160;
-		find_alignment(sb, &opt);
-		output(sb, opt);
-		die("Baa %d!", baa);
-	}
-}
-
-/*
- * Used for the command line parsing; check if the path exists
- * in the working tree.
- */
-static int has_string_in_work_tree(const char *path)
-{
-	struct stat st;
-	return !lstat(path, &st);
-}
-
-static unsigned parse_score(const char *arg)
-{
-	char *end;
-	unsigned long score = strtoul(arg, &end, 10);
-	if (*end)
-		return 0;
-	return score;
-}
-
-static const char *add_prefix(const char *prefix, const char *path)
-{
-	return prefix_path(prefix, prefix ? strlen(prefix) : 0, path);
-}
-
-/*
- * Parsing of (comma separated) one item in the -L option
- */
-static const char *parse_loc(const char *spec,
-			     struct scoreboard *sb, long lno,
-			     long begin, long *ret)
-{
-	char *term;
-	const char *line;
-	long num;
-	int reg_error;
-	regex_t regexp;
-	regmatch_t match[1];
-
-	/* Allow "-L <something>,+20" to mean starting at <something>
-	 * for 20 lines, or "-L <something>,-5" for 5 lines ending at
-	 * <something>.
-	 */
-	if (1 < begin && (spec[0] == '+' || spec[0] == '-')) {
-		num = strtol(spec + 1, &term, 10);
-		if (term != spec + 1) {
-			if (spec[0] == '-')
-				num = 0 - num;
-			if (0 < num)
-				*ret = begin + num - 2;
-			else if (!num)
-				*ret = begin;
-			else
-				*ret = begin + num;
-			return term;
-		}
-		return spec;
-	}
-	num = strtol(spec, &term, 10);
-	if (term != spec) {
-		*ret = num;
-		return term;
-	}
-	if (spec[0] != '/')
-		return spec;
-
-	/* it could be a regexp of form /.../ */
-	for (term = (char *) spec + 1; *term && *term != '/'; term++) {
-		if (*term == '\\')
-			term++;
-	}
-	if (*term != '/')
-		return spec;
-
-	/* try [spec+1 .. term-1] as regexp */
-	*term = 0;
-	begin--; /* input is in human terms */
-	line = nth_line(sb, begin);
-
-	if (!(reg_error = regcomp(&regexp, spec + 1, REG_NEWLINE)) &&
-	    !(reg_error = regexec(&regexp, line, 1, match, 0))) {
-		const char *cp = line + match[0].rm_so;
-		const char *nline;
-
-		while (begin++ < lno) {
-			nline = nth_line(sb, begin);
-			if (line <= cp && cp < nline)
-				break;
-			line = nline;
-		}
-		*ret = begin;
-		regfree(&regexp);
-		*term++ = '/';
-		return term;
-	}
-	else {
-		char errbuf[1024];
-		regerror(reg_error, &regexp, errbuf, 1024);
-		die("-L parameter '%s': %s", spec + 1, errbuf);
-	}
-}
-
-/*
- * Parsing of -L option
- */
-static void prepare_blame_range(struct scoreboard *sb,
-				const char *bottomtop,
-				long lno,
-				long *bottom, long *top)
-{
-	const char *term;
-
-	term = parse_loc(bottomtop, sb, lno, 1, bottom);
-	if (*term == ',') {
-		term = parse_loc(term + 1, sb, lno, *bottom + 1, top);
-		if (*term)
-			usage(blame_usage);
-	}
-	if (*term)
-		usage(blame_usage);
-}
-
-static int git_blame_config(const char *var, const char *value, void *cb)
-{
-	if (!strcmp(var, "blame.showroot")) {
-		show_root = git_config_bool(var, value);
-		return 0;
-	}
-	if (!strcmp(var, "blame.blankboundary")) {
-		blank_boundary = git_config_bool(var, value);
-		return 0;
-	}
-	if (!strcmp(var, "blame.date")) {
-		if (!value)
-			return config_error_nonbool(var);
-		blame_date_mode = parse_date_format(value);
-		return 0;
-	}
-	return git_default_config(var, value, cb);
-}
-
-/*
- * Prepare a dummy commit that represents the work tree (or staged) item.
- * Note that annotating work tree item never works in the reverse.
- */
-static struct commit *fake_working_tree_commit(const char *path, const char *contents_from)
-{
-	struct commit *commit;
-	struct origin *origin;
-	unsigned char head_sha1[20];
-	struct strbuf buf = STRBUF_INIT;
-	const char *ident;
-	time_t now;
-	int size, len;
-	struct cache_entry *ce;
-	unsigned mode;
-
-	if (get_sha1("HEAD", head_sha1))
-		die("No such ref: HEAD");
-
-	time(&now);
-	commit = xcalloc(1, sizeof(*commit));
-	commit->parents = xcalloc(1, sizeof(*commit->parents));
-	commit->parents->item = lookup_commit_reference(head_sha1);
-	commit->object.parsed = 1;
-	commit->date = now;
-	commit->object.type = OBJ_COMMIT;
-
-	origin = make_origin(commit, path);
-
-	if (!contents_from || strcmp("-", contents_from)) {
-		struct stat st;
-		const char *read_from;
-
-		if (contents_from) {
-			if (stat(contents_from, &st) < 0)
-				die_errno("Cannot stat '%s'", contents_from);
-			read_from = contents_from;
-		}
-		else {
-			if (lstat(path, &st) < 0)
-				die_errno("Cannot lstat '%s'", path);
-			read_from = path;
-		}
-		mode = canon_mode(st.st_mode);
-		switch (st.st_mode & S_IFMT) {
-		case S_IFREG:
-			if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
-				die_errno("cannot open or read '%s'", read_from);
-			break;
-		case S_IFLNK:
-			if (strbuf_readlink(&buf, read_from, st.st_size) < 0)
-				die_errno("cannot readlink '%s'", read_from);
-			break;
-		default:
-			die("unsupported file type %s", read_from);
-		}
-	}
-	else {
-		/* Reading from stdin */
-		contents_from = "standard input";
-		mode = 0;
-		if (strbuf_read(&buf, 0, 0) < 0)
-			die_errno("failed to read from stdin");
-	}
-	convert_to_git(path, buf.buf, buf.len, &buf, 0);
-	origin->file.ptr = buf.buf;
-	origin->file.size = buf.len;
-	pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_sha1);
-	commit->util = origin;
-
-	/*
-	 * Read the current index, replace the path entry with
-	 * origin->blob_sha1 without mucking with its mode or type
-	 * bits; we are not going to write this index out -- we just
-	 * want to run "diff-index --cached".
-	 */
-	discard_cache();
-	read_cache();
-
-	len = strlen(path);
-	if (!mode) {
-		int pos = cache_name_pos(path, len);
-		if (0 <= pos)
-			mode = active_cache[pos]->ce_mode;
-		else
-			/* Let's not bother reading from HEAD tree */
-			mode = S_IFREG | 0644;
-	}
-	size = cache_entry_size(len);
-	ce = xcalloc(1, size);
-	hashcpy(ce->sha1, origin->blob_sha1);
-	memcpy(ce->name, path, len);
-	ce->ce_flags = create_ce_flags(len, 0);
-	ce->ce_mode = create_ce_mode(mode);
-	add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
-
-	/*
-	 * We are not going to write this out, so this does not matter
-	 * right now, but someday we might optimize diff-index --cached
-	 * with cache-tree information.
-	 */
-	cache_tree_invalidate_path(active_cache_tree, path);
-
-	commit->buffer = xmalloc(400);
-	ident = fmt_ident("Not Committed Yet", "not.committed.yet", NULL, 0);
-	snprintf(commit->buffer, 400,
-		"tree 0000000000000000000000000000000000000000\n"
-		"parent %s\n"
-		"author %s\n"
-		"committer %s\n\n"
-		"Version of %s from %s\n",
-		sha1_to_hex(head_sha1),
-		ident, ident, path, contents_from ? contents_from : path);
-	return commit;
-}
-
-static const char *prepare_final(struct scoreboard *sb)
-{
-	int i;
-	const char *final_commit_name = NULL;
-	struct rev_info *revs = sb->revs;
-
-	/*
-	 * There must be one and only one positive commit in the
-	 * revs->pending array.
-	 */
-	for (i = 0; i < revs->pending.nr; i++) {
-		struct object *obj = revs->pending.objects[i].item;
-		if (obj->flags & UNINTERESTING)
-			continue;
-		while (obj->type == OBJ_TAG)
-			obj = deref_tag(obj, NULL, 0);
-		if (obj->type != OBJ_COMMIT)
-			die("Non commit %s?", revs->pending.objects[i].name);
-		if (sb->final)
-			die("More than one commit to dig from %s and %s?",
-			    revs->pending.objects[i].name,
-			    final_commit_name);
-		sb->final = (struct commit *) obj;
-		final_commit_name = revs->pending.objects[i].name;
-	}
-	return final_commit_name;
-}
-
-static const char *prepare_initial(struct scoreboard *sb)
-{
-	int i;
-	const char *final_commit_name = NULL;
-	struct rev_info *revs = sb->revs;
-
-	/*
-	 * There must be one and only one negative commit, and it must be
-	 * the boundary.
-	 */
-	for (i = 0; i < revs->pending.nr; i++) {
-		struct object *obj = revs->pending.objects[i].item;
-		if (!(obj->flags & UNINTERESTING))
-			continue;
-		while (obj->type == OBJ_TAG)
-			obj = deref_tag(obj, NULL, 0);
-		if (obj->type != OBJ_COMMIT)
-			die("Non commit %s?", revs->pending.objects[i].name);
-		if (sb->final)
-			die("More than one commit to dig down to %s and %s?",
-			    revs->pending.objects[i].name,
-			    final_commit_name);
-		sb->final = (struct commit *) obj;
-		final_commit_name = revs->pending.objects[i].name;
-	}
-	if (!final_commit_name)
-		die("No commit to dig down to?");
-	return final_commit_name;
-}
-
-static int blame_copy_callback(const struct option *option, const char *arg, int unset)
-{
-	int *opt = option->value;
-
-	/*
-	 * -C enables copy from removed files;
-	 * -C -C enables copy from existing files, but only
-	 *       when blaming a new file;
-	 * -C -C -C enables copy from existing files for
-	 *          everybody
-	 */
-	if (*opt & PICKAXE_BLAME_COPY_HARDER)
-		*opt |= PICKAXE_BLAME_COPY_HARDEST;
-	if (*opt & PICKAXE_BLAME_COPY)
-		*opt |= PICKAXE_BLAME_COPY_HARDER;
-	*opt |= PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE;
-
-	if (arg)
-		blame_copy_score = parse_score(arg);
-	return 0;
-}
-
-static int blame_move_callback(const struct option *option, const char *arg, int unset)
-{
-	int *opt = option->value;
-
-	*opt |= PICKAXE_BLAME_MOVE;
-
-	if (arg)
-		blame_move_score = parse_score(arg);
-	return 0;
-}
-
-static int blame_bottomtop_callback(const struct option *option, const char *arg, int unset)
-{
-	const char **bottomtop = option->value;
-	if (!arg)
-		return -1;
-	if (*bottomtop)
-		die("More than one '-L n,m' option given");
-	*bottomtop = arg;
-	return 0;
-}
-
-int cmd_blame(int argc, const char **argv, const char *prefix)
-{
-	struct rev_info revs;
-	const char *path;
-	struct scoreboard sb;
-	struct origin *o;
-	struct blame_entry *ent;
-	long dashdash_pos, bottom, top, lno;
-	const char *final_commit_name = NULL;
-	enum object_type type;
-
-	static const char *bottomtop = NULL;
-	static int output_option = 0, opt = 0;
-	static int show_stats = 0;
-	static const char *revs_file = NULL;
-	static const char *contents_from = NULL;
-	static const struct option options[] = {
-		OPT_BOOLEAN(0, "incremental", &incremental, "Show blame entries as we find them, incrementally"),
-		OPT_BOOLEAN('b', NULL, &blank_boundary, "Show blank SHA-1 for boundary commits (Default: off)"),
-		OPT_BOOLEAN(0, "root", &show_root, "Do not treat root commits as boundaries (Default: off)"),
-		OPT_BOOLEAN(0, "show-stats", &show_stats, "Show work cost statistics"),
-		OPT_BIT(0, "score-debug", &output_option, "Show output score for blame entries", OUTPUT_SHOW_SCORE),
-		OPT_BIT('f', "show-name", &output_option, "Show original filename (Default: auto)", OUTPUT_SHOW_NAME),
-		OPT_BIT('n', "show-number", &output_option, "Show original linenumber (Default: off)", OUTPUT_SHOW_NUMBER),
-		OPT_BIT('p', "porcelain", &output_option, "Show in a format designed for machine consumption", OUTPUT_PORCELAIN),
-		OPT_BIT('c', NULL, &output_option, "Use the same output mode as git-annotate (Default: off)", OUTPUT_ANNOTATE_COMPAT),
-		OPT_BIT('t', NULL, &output_option, "Show raw timestamp (Default: off)", OUTPUT_RAW_TIMESTAMP),
-		OPT_BIT('l', NULL, &output_option, "Show long commit SHA1 (Default: off)", OUTPUT_LONG_OBJECT_NAME),
-		OPT_BIT('s', NULL, &output_option, "Suppress author name and timestamp (Default: off)", OUTPUT_NO_AUTHOR),
-		OPT_BIT('w', NULL, &xdl_opts, "Ignore whitespace differences", XDF_IGNORE_WHITESPACE),
-		OPT_STRING('S', NULL, &revs_file, "file", "Use revisions from <file> instead of calling git-rev-list"),
-		OPT_STRING(0, "contents", &contents_from, "file", "Use <file>'s contents as the final image"),
-		{ OPTION_CALLBACK, 'C', NULL, &opt, "score", "Find line copies within and across files", PARSE_OPT_OPTARG, blame_copy_callback },
-		{ OPTION_CALLBACK, 'M', NULL, &opt, "score", "Find line movements within and across files", PARSE_OPT_OPTARG, blame_move_callback },
-		OPT_CALLBACK('L', NULL, &bottomtop, "n,m", "Process only line range n,m, counting from 1", blame_bottomtop_callback),
-		OPT_END()
-	};
-
-	struct parse_opt_ctx_t ctx;
-	int cmd_is_annotate = !strcmp(argv[0], "annotate");
-
-	git_config(git_blame_config, NULL);
-	init_revisions(&revs, NULL);
-	revs.date_mode = blame_date_mode;
-
-	save_commit_buffer = 0;
-	dashdash_pos = 0;
-
-	parse_options_start(&ctx, argc, argv, prefix, PARSE_OPT_KEEP_DASHDASH |
-			    PARSE_OPT_KEEP_ARGV0);
-	for (;;) {
-		switch (parse_options_step(&ctx, options, blame_opt_usage)) {
-		case PARSE_OPT_HELP:
-			exit(129);
-		case PARSE_OPT_DONE:
-			if (ctx.argv[0])
-				dashdash_pos = ctx.cpidx;
-			goto parse_done;
-		}
-
-		if (!strcmp(ctx.argv[0], "--reverse")) {
-			ctx.argv[0] = "--children";
-			reverse = 1;
-		}
-		parse_revision_opt(&revs, &ctx, options, blame_opt_usage);
-	}
-parse_done:
-	argc = parse_options_end(&ctx);
-
-	if (revs_file && read_ancestry(revs_file))
-		die_errno("reading graft file '%s' failed", revs_file);
-
-	if (cmd_is_annotate) {
-		output_option |= OUTPUT_ANNOTATE_COMPAT;
-		blame_date_mode = DATE_ISO8601;
-	} else {
-		blame_date_mode = revs.date_mode;
-	}
-
-	/* The maximum width used to show the dates */
-	switch (blame_date_mode) {
-	case DATE_RFC2822:
-		blame_date_width = sizeof("Thu, 19 Oct 2006 16:00:04 -0700");
-		break;
-	case DATE_ISO8601:
-		blame_date_width = sizeof("2006-10-19 16:00:04 -0700");
-		break;
-	case DATE_RAW:
-		blame_date_width = sizeof("1161298804 -0700");
-		break;
-	case DATE_SHORT:
-		blame_date_width = sizeof("2006-10-19");
-		break;
-	case DATE_RELATIVE:
-		/* "normal" is used as the fallback for "relative" */
-	case DATE_LOCAL:
-	case DATE_NORMAL:
-		blame_date_width = sizeof("Thu Oct 19 16:00:04 2006 -0700");
-		break;
-	}
-	blame_date_width -= 1; /* strip the null */
-
-	if (DIFF_OPT_TST(&revs.diffopt, FIND_COPIES_HARDER))
-		opt |= (PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE |
-			PICKAXE_BLAME_COPY_HARDER);
-
-	if (!blame_move_score)
-		blame_move_score = BLAME_DEFAULT_MOVE_SCORE;
-	if (!blame_copy_score)
-		blame_copy_score = BLAME_DEFAULT_COPY_SCORE;
-
-	/*
-	 * We have collected options unknown to us in argv[1..unk]
-	 * which are to be passed to revision machinery if we are
-	 * going to do the "bottom" processing.
-	 *
-	 * The remaining are:
-	 *
-	 * (1) if dashdash_pos != 0, its either
-	 *     "blame [revisions] -- <path>" or
-	 *     "blame -- <path> <rev>"
-	 *
-	 * (2) otherwise, its one of the two:
-	 *     "blame [revisions] <path>"
-	 *     "blame <path> <rev>"
-	 *
-	 * Note that we must strip out <path> from the arguments: we do not
-	 * want the path pruning but we may want "bottom" processing.
-	 */
-	if (dashdash_pos) {
-		switch (argc - dashdash_pos - 1) {
-		case 2: /* (1b) */
-			if (argc != 4)
-				usage_with_options(blame_opt_usage, options);
-			/* reorder for the new way: <rev> -- <path> */
-			argv[1] = argv[3];
-			argv[3] = argv[2];
-			argv[2] = "--";
-			/* FALLTHROUGH */
-		case 1: /* (1a) */
-			path = add_prefix(prefix, argv[--argc]);
-			argv[argc] = NULL;
-			break;
-		default:
-			usage_with_options(blame_opt_usage, options);
-		}
-	} else {
-		if (argc < 2)
-			usage_with_options(blame_opt_usage, options);
-		path = add_prefix(prefix, argv[argc - 1]);
-		if (argc == 3 && !has_string_in_work_tree(path)) { /* (2b) */
-			path = add_prefix(prefix, argv[1]);
-			argv[1] = argv[2];
-		}
-		argv[argc - 1] = "--";
-
-		setup_work_tree();
-		if (!has_string_in_work_tree(path))
-			die_errno("cannot stat path '%s'", path);
-	}
-
-	setup_revisions(argc, argv, &revs, NULL);
-	memset(&sb, 0, sizeof(sb));
-
-	sb.revs = &revs;
-	if (!reverse)
-		final_commit_name = prepare_final(&sb);
-	else if (contents_from)
-		die("--contents and --children do not blend well.");
-	else
-		final_commit_name = prepare_initial(&sb);
-
-	if (!sb.final) {
-		/*
-		 * "--not A B -- path" without anything positive;
-		 * do not default to HEAD, but use the working tree
-		 * or "--contents".
-		 */
-		setup_work_tree();
-		sb.final = fake_working_tree_commit(path, contents_from);
-		add_pending_object(&revs, &(sb.final->object), ":");
-	}
-	else if (contents_from)
-		die("Cannot use --contents with final commit object name");
-
-	/*
-	 * If we have bottom, this will mark the ancestors of the
-	 * bottom commits we would reach while traversing as
-	 * uninteresting.
-	 */
-	if (prepare_revision_walk(&revs))
-		die("revision walk setup failed");
-
-	if (is_null_sha1(sb.final->object.sha1)) {
-		char *buf;
-		o = sb.final->util;
-		buf = xmalloc(o->file.size + 1);
-		memcpy(buf, o->file.ptr, o->file.size + 1);
-		sb.final_buf = buf;
-		sb.final_buf_size = o->file.size;
-	}
-	else {
-		o = get_origin(&sb, sb.final, path);
-		if (fill_blob_sha1(o))
-			die("no such path %s in %s", path, final_commit_name);
-
-		sb.final_buf = read_sha1_file(o->blob_sha1, &type,
-					      &sb.final_buf_size);
-		if (!sb.final_buf)
-			die("Cannot read blob %s for path %s",
-			    sha1_to_hex(o->blob_sha1),
-			    path);
-	}
-	num_read_blob++;
-	lno = prepare_lines(&sb);
-
-	bottom = top = 0;
-	if (bottomtop)
-		prepare_blame_range(&sb, bottomtop, lno, &bottom, &top);
-	if (bottom && top && top < bottom) {
-		long tmp;
-		tmp = top; top = bottom; bottom = tmp;
-	}
-	if (bottom < 1)
-		bottom = 1;
-	if (top < 1)
-		top = lno;
-	bottom--;
-	if (lno < top)
-		die("file %s has only %lu lines", path, lno);
-
-	ent = xcalloc(1, sizeof(*ent));
-	ent->lno = bottom;
-	ent->num_lines = top - bottom;
-	ent->suspect = o;
-	ent->s_lno = bottom;
-
-	sb.ent = ent;
-	sb.path = path;
-
-	read_mailmap(&mailmap, NULL);
-
-	if (!incremental)
-		setup_pager();
-
-	assign_blame(&sb, opt);
-
-	if (incremental)
-		return 0;
-
-	coalesce(&sb);
-
-	if (!(output_option & OUTPUT_PORCELAIN))
-		find_alignment(&sb, &output_option);
-
-	output(&sb, output_option);
-	free((void *)sb.final_buf);
-	for (ent = sb.ent; ent; ) {
-		struct blame_entry *e = ent->next;
-		free(ent);
-		ent = e;
-	}
-
-	if (show_stats) {
-		printf("num read blob: %d\n", num_read_blob);
-		printf("num get patch: %d\n", num_get_patch);
-		printf("num commits: %d\n", num_commits);
-	}
-	return 0;
-}
diff --git a/builtin-branch.c b/builtin-branch.c
deleted file mode 100644
index 1a03d5f..0000000
--- a/builtin-branch.c
+++ /dev/null
@@ -1,645 +0,0 @@
-/*
- * Builtin "git branch"
- *
- * Copyright (c) 2006 Kristian Høgsberg <krh@redhat.com>
- * Based on git-branch.sh by Junio C Hamano.
- */
-
-#include "cache.h"
-#include "color.h"
-#include "refs.h"
-#include "commit.h"
-#include "builtin.h"
-#include "remote.h"
-#include "parse-options.h"
-#include "branch.h"
-#include "diff.h"
-#include "revision.h"
-
-static const char * const builtin_branch_usage[] = {
-	"git branch [options] [-r | -a] [--merged | --no-merged]",
-	"git branch [options] [-l] [-f] <branchname> [<start-point>]",
-	"git branch [options] [-r] (-d | -D) <branchname>",
-	"git branch [options] (-m | -M) [<oldbranch>] <newbranch>",
-	NULL
-};
-
-#define REF_LOCAL_BRANCH    0x01
-#define REF_REMOTE_BRANCH   0x02
-
-static const char *head;
-static unsigned char head_sha1[20];
-
-static int branch_use_color = -1;
-static char branch_colors[][COLOR_MAXLEN] = {
-	GIT_COLOR_RESET,
-	GIT_COLOR_NORMAL,	/* PLAIN */
-	GIT_COLOR_RED,		/* REMOTE */
-	GIT_COLOR_NORMAL,	/* LOCAL */
-	GIT_COLOR_GREEN,	/* CURRENT */
-};
-enum color_branch {
-	BRANCH_COLOR_RESET = 0,
-	BRANCH_COLOR_PLAIN = 1,
-	BRANCH_COLOR_REMOTE = 2,
-	BRANCH_COLOR_LOCAL = 3,
-	BRANCH_COLOR_CURRENT = 4,
-};
-
-static enum merge_filter {
-	NO_FILTER = 0,
-	SHOW_NOT_MERGED,
-	SHOW_MERGED,
-} merge_filter;
-static unsigned char merge_filter_ref[20];
-
-static int parse_branch_color_slot(const char *var, int ofs)
-{
-	if (!strcasecmp(var+ofs, "plain"))
-		return BRANCH_COLOR_PLAIN;
-	if (!strcasecmp(var+ofs, "reset"))
-		return BRANCH_COLOR_RESET;
-	if (!strcasecmp(var+ofs, "remote"))
-		return BRANCH_COLOR_REMOTE;
-	if (!strcasecmp(var+ofs, "local"))
-		return BRANCH_COLOR_LOCAL;
-	if (!strcasecmp(var+ofs, "current"))
-		return BRANCH_COLOR_CURRENT;
-	die("bad config variable '%s'", var);
-}
-
-static int git_branch_config(const char *var, const char *value, void *cb)
-{
-	if (!strcmp(var, "color.branch")) {
-		branch_use_color = git_config_colorbool(var, value, -1);
-		return 0;
-	}
-	if (!prefixcmp(var, "color.branch.")) {
-		int slot = parse_branch_color_slot(var, 13);
-		if (!value)
-			return config_error_nonbool(var);
-		color_parse(value, var, branch_colors[slot]);
-		return 0;
-	}
-	return git_color_default_config(var, value, cb);
-}
-
-static const char *branch_get_color(enum color_branch ix)
-{
-	if (branch_use_color > 0)
-		return branch_colors[ix];
-	return "";
-}
-
-static int delete_branches(int argc, const char **argv, int force, int kinds)
-{
-	struct commit *rev, *head_rev = head_rev;
-	unsigned char sha1[20];
-	char *name = NULL;
-	const char *fmt, *remote;
-	int i;
-	int ret = 0;
-	struct strbuf bname = STRBUF_INIT;
-
-	switch (kinds) {
-	case REF_REMOTE_BRANCH:
-		fmt = "refs/remotes/%s";
-		remote = "remote ";
-		force = 1;
-		break;
-	case REF_LOCAL_BRANCH:
-		fmt = "refs/heads/%s";
-		remote = "";
-		break;
-	default:
-		die("cannot use -a with -d");
-	}
-
-	if (!force) {
-		head_rev = lookup_commit_reference(head_sha1);
-		if (!head_rev)
-			die("Couldn't look up commit object for HEAD");
-	}
-	for (i = 0; i < argc; i++, strbuf_release(&bname)) {
-		strbuf_branchname(&bname, argv[i]);
-		if (kinds == REF_LOCAL_BRANCH && !strcmp(head, bname.buf)) {
-			error("Cannot delete the branch '%s' "
-			      "which you are currently on.", bname.buf);
-			ret = 1;
-			continue;
-		}
-
-		free(name);
-
-		name = xstrdup(mkpath(fmt, bname.buf));
-		if (!resolve_ref(name, sha1, 1, NULL)) {
-			error("%sbranch '%s' not found.",
-					remote, bname.buf);
-			ret = 1;
-			continue;
-		}
-
-		rev = lookup_commit_reference(sha1);
-		if (!rev) {
-			error("Couldn't look up commit object for '%s'", name);
-			ret = 1;
-			continue;
-		}
-
-		/* This checks whether the merge bases of branch and
-		 * HEAD contains branch -- which means that the HEAD
-		 * contains everything in both.
-		 */
-
-		if (!force &&
-		    !in_merge_bases(rev, &head_rev, 1)) {
-			error("The branch '%s' is not an ancestor of "
-			      "your current HEAD.\n"
-			      "If you are sure you want to delete it, "
-			      "run 'git branch -D %s'.", bname.buf, bname.buf);
-			ret = 1;
-			continue;
-		}
-
-		if (delete_ref(name, sha1, 0)) {
-			error("Error deleting %sbranch '%s'", remote,
-			      bname.buf);
-			ret = 1;
-		} else {
-			struct strbuf buf = STRBUF_INIT;
-			printf("Deleted %sbranch %s (was %s).\n", remote,
-			       bname.buf,
-			       find_unique_abbrev(sha1, DEFAULT_ABBREV));
-			strbuf_addf(&buf, "branch.%s", bname.buf);
-			if (git_config_rename_section(buf.buf, NULL) < 0)
-				warning("Update of config-file failed");
-			strbuf_release(&buf);
-		}
-	}
-
-	free(name);
-
-	return(ret);
-}
-
-struct ref_item {
-	char *name;
-	char *dest;
-	unsigned int kind, len;
-	struct commit *commit;
-};
-
-struct ref_list {
-	struct rev_info revs;
-	int index, alloc, maxwidth, verbose, abbrev;
-	struct ref_item *list;
-	struct commit_list *with_commit;
-	int kinds;
-};
-
-static char *resolve_symref(const char *src, const char *prefix)
-{
-	unsigned char sha1[20];
-	int flag;
-	const char *dst, *cp;
-
-	dst = resolve_ref(src, sha1, 0, &flag);
-	if (!(dst && (flag & REF_ISSYMREF)))
-		return NULL;
-	if (prefix && (cp = skip_prefix(dst, prefix)))
-		dst = cp;
-	return xstrdup(dst);
-}
-
-static int append_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
-{
-	struct ref_list *ref_list = (struct ref_list*)(cb_data);
-	struct ref_item *newitem;
-	struct commit *commit;
-	int kind, i;
-	const char *prefix, *orig_refname = refname;
-
-	static struct {
-		int kind;
-		const char *prefix;
-		int pfxlen;
-	} ref_kind[] = {
-		{ REF_LOCAL_BRANCH, "refs/heads/", 11 },
-		{ REF_REMOTE_BRANCH, "refs/remotes/", 13 },
-	};
-
-	/* Detect kind */
-	for (i = 0; i < ARRAY_SIZE(ref_kind); i++) {
-		prefix = ref_kind[i].prefix;
-		if (strncmp(refname, prefix, ref_kind[i].pfxlen))
-			continue;
-		kind = ref_kind[i].kind;
-		refname += ref_kind[i].pfxlen;
-		break;
-	}
-	if (ARRAY_SIZE(ref_kind) <= i)
-		return 0;
-
-	/* Don't add types the caller doesn't want */
-	if ((kind & ref_list->kinds) == 0)
-		return 0;
-
-	commit = NULL;
-	if (ref_list->verbose || ref_list->with_commit || merge_filter != NO_FILTER) {
-		commit = lookup_commit_reference_gently(sha1, 1);
-		if (!commit)
-			return error("branch '%s' does not point at a commit", refname);
-
-		/* Filter with with_commit if specified */
-		if (!is_descendant_of(commit, ref_list->with_commit))
-			return 0;
-
-		if (merge_filter != NO_FILTER)
-			add_pending_object(&ref_list->revs,
-					   (struct object *)commit, refname);
-	}
-
-	/* Resize buffer */
-	if (ref_list->index >= ref_list->alloc) {
-		ref_list->alloc = alloc_nr(ref_list->alloc);
-		ref_list->list = xrealloc(ref_list->list,
-				ref_list->alloc * sizeof(struct ref_item));
-	}
-
-	/* Record the new item */
-	newitem = &(ref_list->list[ref_list->index++]);
-	newitem->name = xstrdup(refname);
-	newitem->kind = kind;
-	newitem->commit = commit;
-	newitem->len = strlen(refname);
-	newitem->dest = resolve_symref(orig_refname, prefix);
-	/* adjust for "remotes/" */
-	if (newitem->kind == REF_REMOTE_BRANCH &&
-	    ref_list->kinds != REF_REMOTE_BRANCH)
-		newitem->len += 8;
-	if (newitem->len > ref_list->maxwidth)
-		ref_list->maxwidth = newitem->len;
-
-	return 0;
-}
-
-static void free_ref_list(struct ref_list *ref_list)
-{
-	int i;
-
-	for (i = 0; i < ref_list->index; i++) {
-		free(ref_list->list[i].name);
-		free(ref_list->list[i].dest);
-	}
-	free(ref_list->list);
-}
-
-static int ref_cmp(const void *r1, const void *r2)
-{
-	struct ref_item *c1 = (struct ref_item *)(r1);
-	struct ref_item *c2 = (struct ref_item *)(r2);
-
-	if (c1->kind != c2->kind)
-		return c1->kind - c2->kind;
-	return strcmp(c1->name, c2->name);
-}
-
-static void fill_tracking_info(struct strbuf *stat, const char *branch_name,
-		int show_upstream_ref)
-{
-	int ours, theirs;
-	struct branch *branch = branch_get(branch_name);
-
-	if (!stat_tracking_info(branch, &ours, &theirs)) {
-		if (branch && branch->merge && branch->merge[0]->dst &&
-		    show_upstream_ref)
-			strbuf_addf(stat, "[%s] ",
-			    shorten_unambiguous_ref(branch->merge[0]->dst, 0));
-		return;
-	}
-
-	strbuf_addch(stat, '[');
-	if (show_upstream_ref)
-		strbuf_addf(stat, "%s: ",
-			shorten_unambiguous_ref(branch->merge[0]->dst, 0));
-	if (!ours)
-		strbuf_addf(stat, "behind %d] ", theirs);
-	else if (!theirs)
-		strbuf_addf(stat, "ahead %d] ", ours);
-	else
-		strbuf_addf(stat, "ahead %d, behind %d] ", ours, theirs);
-}
-
-static int matches_merge_filter(struct commit *commit)
-{
-	int is_merged;
-
-	if (merge_filter == NO_FILTER)
-		return 1;
-
-	is_merged = !!(commit->object.flags & UNINTERESTING);
-	return (is_merged == (merge_filter == SHOW_MERGED));
-}
-
-static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
-			   int abbrev, int current, char *prefix)
-{
-	char c;
-	int color;
-	struct commit *commit = item->commit;
-	struct strbuf out = STRBUF_INIT, name = STRBUF_INIT;
-
-	if (!matches_merge_filter(commit))
-		return;
-
-	switch (item->kind) {
-	case REF_LOCAL_BRANCH:
-		color = BRANCH_COLOR_LOCAL;
-		break;
-	case REF_REMOTE_BRANCH:
-		color = BRANCH_COLOR_REMOTE;
-		break;
-	default:
-		color = BRANCH_COLOR_PLAIN;
-		break;
-	}
-
-	c = ' ';
-	if (current) {
-		c = '*';
-		color = BRANCH_COLOR_CURRENT;
-	}
-
-	strbuf_addf(&name, "%s%s", prefix, item->name);
-	if (verbose)
-		strbuf_addf(&out, "%c %s%-*s%s", c, branch_get_color(color),
-			    maxwidth, name.buf,
-			    branch_get_color(BRANCH_COLOR_RESET));
-	else
-		strbuf_addf(&out, "%c %s%s%s", c, branch_get_color(color),
-			    name.buf, branch_get_color(BRANCH_COLOR_RESET));
-
-	if (item->dest)
-		strbuf_addf(&out, " -> %s", item->dest);
-	else if (verbose) {
-		struct strbuf subject = STRBUF_INIT, stat = STRBUF_INIT;
-		const char *sub = " **** invalid ref ****";
-
-		commit = item->commit;
-		if (commit && !parse_commit(commit)) {
-			pretty_print_commit(CMIT_FMT_ONELINE, commit,
-					    &subject, 0, NULL, NULL, 0, 0);
-			sub = subject.buf;
-		}
-
-		if (item->kind == REF_LOCAL_BRANCH)
-			fill_tracking_info(&stat, item->name, verbose > 1);
-
-		strbuf_addf(&out, " %s %s%s",
-			find_unique_abbrev(item->commit->object.sha1, abbrev),
-			stat.buf, sub);
-		strbuf_release(&stat);
-		strbuf_release(&subject);
-	}
-	printf("%s\n", out.buf);
-	strbuf_release(&name);
-	strbuf_release(&out);
-}
-
-static int calc_maxwidth(struct ref_list *refs)
-{
-	int i, w = 0;
-	for (i = 0; i < refs->index; i++) {
-		if (!matches_merge_filter(refs->list[i].commit))
-			continue;
-		if (refs->list[i].len > w)
-			w = refs->list[i].len;
-	}
-	return w;
-}
-
-
-static void show_detached(struct ref_list *ref_list)
-{
-	struct commit *head_commit = lookup_commit_reference_gently(head_sha1, 1);
-
-	if (head_commit && is_descendant_of(head_commit, ref_list->with_commit)) {
-		struct ref_item item;
-		item.name = xstrdup("(no branch)");
-		item.len = strlen(item.name);
-		item.kind = REF_LOCAL_BRANCH;
-		item.dest = NULL;
-		item.commit = head_commit;
-		if (item.len > ref_list->maxwidth)
-			ref_list->maxwidth = item.len;
-		print_ref_item(&item, ref_list->maxwidth, ref_list->verbose, ref_list->abbrev, 1, "");
-		free(item.name);
-	}
-}
-
-static void print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit)
-{
-	int i;
-	struct ref_list ref_list;
-
-	memset(&ref_list, 0, sizeof(ref_list));
-	ref_list.kinds = kinds;
-	ref_list.verbose = verbose;
-	ref_list.abbrev = abbrev;
-	ref_list.with_commit = with_commit;
-	if (merge_filter != NO_FILTER)
-		init_revisions(&ref_list.revs, NULL);
-	for_each_rawref(append_ref, &ref_list);
-	if (merge_filter != NO_FILTER) {
-		struct commit *filter;
-		filter = lookup_commit_reference_gently(merge_filter_ref, 0);
-		filter->object.flags |= UNINTERESTING;
-		add_pending_object(&ref_list.revs,
-				   (struct object *) filter, "");
-		ref_list.revs.limited = 1;
-		prepare_revision_walk(&ref_list.revs);
-		if (verbose)
-			ref_list.maxwidth = calc_maxwidth(&ref_list);
-	}
-
-	qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp);
-
-	detached = (detached && (kinds & REF_LOCAL_BRANCH));
-	if (detached)
-		show_detached(&ref_list);
-
-	for (i = 0; i < ref_list.index; i++) {
-		int current = !detached &&
-			(ref_list.list[i].kind == REF_LOCAL_BRANCH) &&
-			!strcmp(ref_list.list[i].name, head);
-		char *prefix = (kinds != REF_REMOTE_BRANCH &&
-				ref_list.list[i].kind == REF_REMOTE_BRANCH)
-				? "remotes/" : "";
-		print_ref_item(&ref_list.list[i], ref_list.maxwidth, verbose,
-			       abbrev, current, prefix);
-	}
-
-	free_ref_list(&ref_list);
-}
-
-static void rename_branch(const char *oldname, const char *newname, int force)
-{
-	struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT;
-	unsigned char sha1[20];
-	struct strbuf oldsection = STRBUF_INIT, newsection = STRBUF_INIT;
-	int recovery = 0;
-
-	if (!oldname)
-		die("cannot rename the current branch while not on any.");
-
-	if (strbuf_check_branch_ref(&oldref, oldname)) {
-		/*
-		 * Bad name --- this could be an attempt to rename a
-		 * ref that we used to allow to be created by accident.
-		 */
-		if (resolve_ref(oldref.buf, sha1, 1, NULL))
-			recovery = 1;
-		else
-			die("Invalid branch name: '%s'", oldname);
-	}
-
-	if (strbuf_check_branch_ref(&newref, newname))
-		die("Invalid branch name: '%s'", newname);
-
-	if (resolve_ref(newref.buf, sha1, 1, NULL) && !force)
-		die("A branch named '%s' already exists.", newref.buf + 11);
-
-	strbuf_addf(&logmsg, "Branch: renamed %s to %s",
-		 oldref.buf, newref.buf);
-
-	if (rename_ref(oldref.buf, newref.buf, logmsg.buf))
-		die("Branch rename failed");
-	strbuf_release(&logmsg);
-
-	if (recovery)
-		warning("Renamed a misnamed branch '%s' away", oldref.buf + 11);
-
-	/* no need to pass logmsg here as HEAD didn't really move */
-	if (!strcmp(oldname, head) && create_symref("HEAD", newref.buf, NULL))
-		die("Branch renamed to %s, but HEAD is not updated!", newname);
-
-	strbuf_addf(&oldsection, "branch.%s", oldref.buf + 11);
-	strbuf_release(&oldref);
-	strbuf_addf(&newsection, "branch.%s", newref.buf + 11);
-	strbuf_release(&newref);
-	if (git_config_rename_section(oldsection.buf, newsection.buf) < 0)
-		die("Branch is renamed, but update of config-file failed");
-	strbuf_release(&oldsection);
-	strbuf_release(&newsection);
-}
-
-static int opt_parse_merge_filter(const struct option *opt, const char *arg, int unset)
-{
-	merge_filter = ((opt->long_name[0] == 'n')
-			? SHOW_NOT_MERGED
-			: SHOW_MERGED);
-	if (unset)
-		merge_filter = SHOW_NOT_MERGED; /* b/c for --no-merged */
-	if (!arg)
-		arg = "HEAD";
-	if (get_sha1(arg, merge_filter_ref))
-		die("malformed object name %s", arg);
-	return 0;
-}
-
-int cmd_branch(int argc, const char **argv, const char *prefix)
-{
-	int delete = 0, rename = 0, force_create = 0;
-	int verbose = 0, abbrev = DEFAULT_ABBREV, detached = 0;
-	int reflog = 0;
-	enum branch_track track;
-	int kinds = REF_LOCAL_BRANCH;
-	struct commit_list *with_commit = NULL;
-
-	struct option options[] = {
-		OPT_GROUP("Generic options"),
-		OPT__VERBOSE(&verbose),
-		OPT_SET_INT('t', "track",  &track, "set up tracking mode (see git-pull(1))",
-			BRANCH_TRACK_EXPLICIT),
-		OPT_BOOLEAN( 0 , "color",  &branch_use_color, "use colored output"),
-		OPT_SET_INT('r', NULL,     &kinds, "act on remote-tracking branches",
-			REF_REMOTE_BRANCH),
-		{
-			OPTION_CALLBACK, 0, "contains", &with_commit, "commit",
-			"print only branches that contain the commit",
-			PARSE_OPT_LASTARG_DEFAULT,
-			parse_opt_with_commit, (intptr_t)"HEAD",
-		},
-		{
-			OPTION_CALLBACK, 0, "with", &with_commit, "commit",
-			"print only branches that contain the commit",
-			PARSE_OPT_HIDDEN | PARSE_OPT_LASTARG_DEFAULT,
-			parse_opt_with_commit, (intptr_t) "HEAD",
-		},
-		OPT__ABBREV(&abbrev),
-
-		OPT_GROUP("Specific git-branch actions:"),
-		OPT_SET_INT('a', NULL, &kinds, "list both remote-tracking and local branches",
-			REF_REMOTE_BRANCH | REF_LOCAL_BRANCH),
-		OPT_BIT('d', NULL, &delete, "delete fully merged branch", 1),
-		OPT_BIT('D', NULL, &delete, "delete branch (even if not merged)", 2),
-		OPT_BIT('m', NULL, &rename, "move/rename a branch and its reflog", 1),
-		OPT_BIT('M', NULL, &rename, "move/rename a branch, even if target exists", 2),
-		OPT_BOOLEAN('l', NULL, &reflog, "create the branch's reflog"),
-		OPT_BOOLEAN('f', NULL, &force_create, "force creation (when already exists)"),
-		{
-			OPTION_CALLBACK, 0, "no-merged", &merge_filter_ref,
-			"commit", "print only not merged branches",
-			PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG,
-			opt_parse_merge_filter, (intptr_t) "HEAD",
-		},
-		{
-			OPTION_CALLBACK, 0, "merged", &merge_filter_ref,
-			"commit", "print only merged branches",
-			PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG,
-			opt_parse_merge_filter, (intptr_t) "HEAD",
-		},
-		OPT_END(),
-	};
-
-	git_config(git_branch_config, NULL);
-
-	if (branch_use_color == -1)
-		branch_use_color = git_use_color_default;
-
-	track = git_branch_track;
-
-	head = resolve_ref("HEAD", head_sha1, 0, NULL);
-	if (!head)
-		die("Failed to resolve HEAD as a valid ref.");
-	head = xstrdup(head);
-	if (!strcmp(head, "HEAD")) {
-		detached = 1;
-	} else {
-		if (prefixcmp(head, "refs/heads/"))
-			die("HEAD not found below refs/heads!");
-		head += 11;
-	}
-	hashcpy(merge_filter_ref, head_sha1);
-
-	argc = parse_options(argc, argv, prefix, options, builtin_branch_usage,
-			     0);
-	if (!!delete + !!rename + !!force_create > 1)
-		usage_with_options(builtin_branch_usage, options);
-
-	if (delete)
-		return delete_branches(argc, argv, delete > 1, kinds);
-	else if (argc == 0)
-		print_ref_list(kinds, detached, verbose, abbrev, with_commit);
-	else if (rename && (argc == 1))
-		rename_branch(head, argv[0], rename > 1);
-	else if (rename && (argc == 2))
-		rename_branch(argv[0], argv[1], rename > 1);
-	else if (argc <= 2)
-		create_branch(head, argv[0], (argc == 2) ? argv[1] : head,
-			      force_create, reflog, track);
-	else
-		usage_with_options(builtin_branch_usage, options);
-
-	return 0;
-}
diff --git a/builtin-bundle.c b/builtin-bundle.c
deleted file mode 100644
index 9b58152..0000000
--- a/builtin-bundle.c
+++ /dev/null
@@ -1,63 +0,0 @@
-#include "builtin.h"
-#include "cache.h"
-#include "bundle.h"
-
-/*
- * Basic handler for bundle files to connect repositories via sneakernet.
- * Invocation must include action.
- * This function can create a bundle or provide information on an existing
- * bundle supporting "fetch", "pull", and "ls-remote".
- */
-
-static const char *bundle_usage="git bundle (create <bundle> <git rev-list args> | verify <bundle> | list-heads <bundle> [refname]... | unbundle <bundle> [refname]... )";
-
-int cmd_bundle(int argc, const char **argv, const char *prefix)
-{
-	struct bundle_header header;
-	int nongit;
-	const char *cmd, *bundle_file;
-	int bundle_fd = -1;
-	char buffer[PATH_MAX];
-
-	if (argc < 3)
-		usage(bundle_usage);
-
-	cmd = argv[1];
-	bundle_file = argv[2];
-	argc -= 2;
-	argv += 2;
-
-	prefix = setup_git_directory_gently(&nongit);
-	if (prefix && bundle_file[0] != '/') {
-		snprintf(buffer, sizeof(buffer), "%s/%s", prefix, bundle_file);
-		bundle_file = buffer;
-	}
-
-	memset(&header, 0, sizeof(header));
-	if (strcmp(cmd, "create") && (bundle_fd =
-				read_bundle_header(bundle_file, &header)) < 0)
-		return 1;
-
-	if (!strcmp(cmd, "verify")) {
-		close(bundle_fd);
-		if (verify_bundle(&header, 1))
-			return 1;
-		fprintf(stderr, "%s is okay\n", bundle_file);
-		return 0;
-	}
-	if (!strcmp(cmd, "list-heads")) {
-		close(bundle_fd);
-		return !!list_bundle_refs(&header, argc, argv);
-	}
-	if (!strcmp(cmd, "create")) {
-		if (nongit)
-			die("Need a repository to create a bundle.");
-		return !!create_bundle(&header, bundle_file, argc, argv);
-	} else if (!strcmp(cmd, "unbundle")) {
-		if (nongit)
-			die("Need a repository to unbundle.");
-		return !!unbundle(&header, bundle_fd) ||
-			list_bundle_refs(&header, argc, argv);
-	} else
-		usage(bundle_usage);
-}
diff --git a/builtin-cat-file.c b/builtin-cat-file.c
deleted file mode 100644
index 5906842..0000000
--- a/builtin-cat-file.c
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * GIT - The information manager from hell
- *
- * Copyright (C) Linus Torvalds, 2005
- */
-#include "cache.h"
-#include "exec_cmd.h"
-#include "tag.h"
-#include "tree.h"
-#include "builtin.h"
-#include "parse-options.h"
-
-#define BATCH 1
-#define BATCH_CHECK 2
-
-static void pprint_tag(const unsigned char *sha1, const char *buf, unsigned long size)
-{
-	/* the parser in tag.c is useless here. */
-	const char *endp = buf + size;
-	const char *cp = buf;
-
-	while (cp < endp) {
-		char c = *cp++;
-		if (c != '\n')
-			continue;
-		if (7 <= endp - cp && !memcmp("tagger ", cp, 7)) {
-			const char *tagger = cp;
-
-			/* Found the tagger line.  Copy out the contents
-			 * of the buffer so far.
-			 */
-			write_or_die(1, buf, cp - buf);
-
-			/*
-			 * Do something intelligent, like pretty-printing
-			 * the date.
-			 */
-			while (cp < endp) {
-				if (*cp++ == '\n') {
-					/* tagger to cp is a line
-					 * that has ident and time.
-					 */
-					const char *sp = tagger;
-					char *ep;
-					unsigned long date;
-					long tz;
-					while (sp < cp && *sp != '>')
-						sp++;
-					if (sp == cp) {
-						/* give up */
-						write_or_die(1, tagger,
-							     cp - tagger);
-						break;
-					}
-					while (sp < cp &&
-					       !('0' <= *sp && *sp <= '9'))
-						sp++;
-					write_or_die(1, tagger, sp - tagger);
-					date = strtoul(sp, &ep, 10);
-					tz = strtol(ep, NULL, 10);
-					sp = show_date(date, tz, 0);
-					write_or_die(1, sp, strlen(sp));
-					xwrite(1, "\n", 1);
-					break;
-				}
-			}
-			break;
-		}
-		if (cp < endp && *cp == '\n')
-			/* end of header */
-			break;
-	}
-	/* At this point, we have copied out the header up to the end of
-	 * the tagger line and cp points at one past \n.  It could be the
-	 * next header line after the tagger line, or it could be another
-	 * \n that marks the end of the headers.  We need to copy out the
-	 * remainder as is.
-	 */
-	if (cp < endp)
-		write_or_die(1, cp, endp - cp);
-}
-
-static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
-{
-	unsigned char sha1[20];
-	enum object_type type;
-	void *buf;
-	unsigned long size;
-
-	if (get_sha1(obj_name, sha1))
-		die("Not a valid object name %s", obj_name);
-
-	buf = NULL;
-	switch (opt) {
-	case 't':
-		type = sha1_object_info(sha1, NULL);
-		if (type > 0) {
-			printf("%s\n", typename(type));
-			return 0;
-		}
-		break;
-
-	case 's':
-		type = sha1_object_info(sha1, &size);
-		if (type > 0) {
-			printf("%lu\n", size);
-			return 0;
-		}
-		break;
-
-	case 'e':
-		return !has_sha1_file(sha1);
-
-	case 'p':
-		type = sha1_object_info(sha1, NULL);
-		if (type < 0)
-			die("Not a valid object name %s", obj_name);
-
-		/* custom pretty-print here */
-		if (type == OBJ_TREE) {
-			const char *ls_args[3] = {"ls-tree", obj_name, NULL};
-			return cmd_ls_tree(2, ls_args, NULL);
-		}
-
-		buf = read_sha1_file(sha1, &type, &size);
-		if (!buf)
-			die("Cannot read object %s", obj_name);
-		if (type == OBJ_TAG) {
-			pprint_tag(sha1, buf, size);
-			return 0;
-		}
-
-		/* otherwise just spit out the data */
-		break;
-	case 0:
-		buf = read_object_with_reference(sha1, exp_type, &size, NULL);
-		break;
-
-	default:
-		die("git cat-file: unknown option: %s", exp_type);
-	}
-
-	if (!buf)
-		die("git cat-file %s: bad file", obj_name);
-
-	write_or_die(1, buf, size);
-	return 0;
-}
-
-static int batch_one_object(const char *obj_name, int print_contents)
-{
-	unsigned char sha1[20];
-	enum object_type type = 0;
-	unsigned long size;
-	void *contents = contents;
-
-	if (!obj_name)
-	   return 1;
-
-	if (get_sha1(obj_name, sha1)) {
-		printf("%s missing\n", obj_name);
-		fflush(stdout);
-		return 0;
-	}
-
-	if (print_contents == BATCH)
-		contents = read_sha1_file(sha1, &type, &size);
-	else
-		type = sha1_object_info(sha1, &size);
-
-	if (type <= 0) {
-		printf("%s missing\n", obj_name);
-		fflush(stdout);
-		return 0;
-	}
-
-	printf("%s %s %lu\n", sha1_to_hex(sha1), typename(type), size);
-	fflush(stdout);
-
-	if (print_contents == BATCH) {
-		write_or_die(1, contents, size);
-		printf("\n");
-		fflush(stdout);
-		free(contents);
-	}
-
-	return 0;
-}
-
-static int batch_objects(int print_contents)
-{
-	struct strbuf buf = STRBUF_INIT;
-
-	while (strbuf_getline(&buf, stdin, '\n') != EOF) {
-		int error = batch_one_object(buf.buf, print_contents);
-		if (error)
-			return error;
-	}
-
-	return 0;
-}
-
-static const char * const cat_file_usage[] = {
-	"git cat-file (-t|-s|-e|-p|<type>) <object>",
-	"git cat-file (--batch|--batch-check) < <list_of_objects>",
-	NULL
-};
-
-int cmd_cat_file(int argc, const char **argv, const char *prefix)
-{
-	int opt = 0, batch = 0;
-	const char *exp_type = NULL, *obj_name = NULL;
-
-	const struct option options[] = {
-		OPT_GROUP("<type> can be one of: blob, tree, commit, tag"),
-		OPT_SET_INT('t', NULL, &opt, "show object type", 't'),
-		OPT_SET_INT('s', NULL, &opt, "show object size", 's'),
-		OPT_SET_INT('e', NULL, &opt,
-			    "exit with zero when there's no error", 'e'),
-		OPT_SET_INT('p', NULL, &opt, "pretty-print object's content", 'p'),
-		OPT_SET_INT(0, "batch", &batch,
-			    "show info and content of objects feeded on stdin", BATCH),
-		OPT_SET_INT(0, "batch-check", &batch,
-			    "show info about objects feeded on stdin",
-			    BATCH_CHECK),
-		OPT_END()
-	};
-
-	git_config(git_default_config, NULL);
-
-	if (argc != 3 && argc != 2)
-		usage_with_options(cat_file_usage, options);
-
-	argc = parse_options(argc, argv, prefix, options, cat_file_usage, 0);
-
-	if (opt) {
-		if (argc == 1)
-			obj_name = argv[0];
-		else
-			usage_with_options(cat_file_usage, options);
-	}
-	if (!opt && !batch) {
-		if (argc == 2) {
-			exp_type = argv[0];
-			obj_name = argv[1];
-		} else
-			usage_with_options(cat_file_usage, options);
-	}
-	if (batch && (opt || argc)) {
-		usage_with_options(cat_file_usage, options);
-	}
-
-	if (batch)
-		return batch_objects(batch);
-
-	return cat_one_file(opt, exp_type, obj_name);
-}
diff --git a/builtin-check-attr.c b/builtin-check-attr.c
deleted file mode 100644
index 8bd0430..0000000
--- a/builtin-check-attr.c
+++ /dev/null
@@ -1,123 +0,0 @@
-#include "builtin.h"
-#include "cache.h"
-#include "attr.h"
-#include "quote.h"
-#include "parse-options.h"
-
-static int stdin_paths;
-static const char * const check_attr_usage[] = {
-"git check-attr attr... [--] pathname...",
-"git check-attr --stdin attr... < <list-of-paths>",
-NULL
-};
-
-static int null_term_line;
-
-static const struct option check_attr_options[] = {
-	OPT_BOOLEAN(0 , "stdin", &stdin_paths, "read file names from stdin"),
-	OPT_BOOLEAN('z', NULL, &null_term_line,
-		"input paths are terminated by a null character"),
-	OPT_END()
-};
-
-static void check_attr(int cnt, struct git_attr_check *check,
-	const char** name, const char *file)
-{
-	int j;
-	if (git_checkattr(file, cnt, check))
-		die("git_checkattr died");
-	for (j = 0; j < cnt; j++) {
-		const char *value = check[j].value;
-
-		if (ATTR_TRUE(value))
-			value = "set";
-		else if (ATTR_FALSE(value))
-			value = "unset";
-		else if (ATTR_UNSET(value))
-			value = "unspecified";
-
-		quote_c_style(file, NULL, stdout, 0);
-		printf(": %s: %s\n", name[j], value);
-	}
-}
-
-static void check_attr_stdin_paths(int cnt, struct git_attr_check *check,
-	const char** name)
-{
-	struct strbuf buf, nbuf;
-	int line_termination = null_term_line ? 0 : '\n';
-
-	strbuf_init(&buf, 0);
-	strbuf_init(&nbuf, 0);
-	while (strbuf_getline(&buf, stdin, line_termination) != EOF) {
-		if (line_termination && buf.buf[0] == '"') {
-			strbuf_reset(&nbuf);
-			if (unquote_c_style(&nbuf, buf.buf, NULL))
-				die("line is badly quoted");
-			strbuf_swap(&buf, &nbuf);
-		}
-		check_attr(cnt, check, name, buf.buf);
-		maybe_flush_or_die(stdout, "attribute to stdout");
-	}
-	strbuf_release(&buf);
-	strbuf_release(&nbuf);
-}
-
-int cmd_check_attr(int argc, const char **argv, const char *prefix)
-{
-	struct git_attr_check *check;
-	int cnt, i, doubledash;
-	const char *errstr = NULL;
-
-	argc = parse_options(argc, argv, prefix, check_attr_options,
-			     check_attr_usage, PARSE_OPT_KEEP_DASHDASH);
-	if (!argc)
-		usage_with_options(check_attr_usage, check_attr_options);
-
-	if (read_cache() < 0) {
-		die("invalid cache");
-	}
-
-	doubledash = -1;
-	for (i = 0; doubledash < 0 && i < argc; i++) {
-		if (!strcmp(argv[i], "--"))
-			doubledash = i;
-	}
-
-	/* If there is no double dash, we handle only one attribute */
-	if (doubledash < 0) {
-		cnt = 1;
-		doubledash = 0;
-	} else
-		cnt = doubledash;
-	doubledash++;
-
-	if (cnt <= 0)
-		errstr = "No attribute specified";
-	else if (stdin_paths && doubledash < argc)
-		errstr = "Can't specify files with --stdin";
-	if (errstr) {
-		error("%s", errstr);
-		usage_with_options(check_attr_usage, check_attr_options);
-	}
-
-	check = xcalloc(cnt, sizeof(*check));
-	for (i = 0; i < cnt; i++) {
-		const char *name;
-		struct git_attr *a;
-		name = argv[i];
-		a = git_attr(name, strlen(name));
-		if (!a)
-			return error("%s: not a valid attribute name", name);
-		check[i].attr = a;
-	}
-
-	if (stdin_paths)
-		check_attr_stdin_paths(cnt, check, argv);
-	else {
-		for (i = doubledash; i < argc; i++)
-			check_attr(cnt, check, argv, argv[i]);
-		maybe_flush_or_die(stdout, "attribute to stdout");
-	}
-	return 0;
-}
diff --git a/builtin-check-ref-format.c b/builtin-check-ref-format.c
deleted file mode 100644
index f9381e0..0000000
--- a/builtin-check-ref-format.c
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * GIT - The information manager from hell
- */
-
-#include "cache.h"
-#include "refs.h"
-#include "builtin.h"
-#include "strbuf.h"
-
-int cmd_check_ref_format(int argc, const char **argv, const char *prefix)
-{
-	if (argc == 3 && !strcmp(argv[1], "--branch")) {
-		struct strbuf sb = STRBUF_INIT;
-
-		if (strbuf_check_branch_ref(&sb, argv[2]))
-			die("'%s' is not a valid branch name", argv[2]);
-		printf("%s\n", sb.buf + 11);
-		exit(0);
-	}
-	if (argc != 2)
-		usage("git check-ref-format refname");
-	return !!check_ref_format(argv[1]);
-}
diff --git a/builtin-checkout-index.c b/builtin-checkout-index.c
deleted file mode 100644
index a7a5ee1..0000000
--- a/builtin-checkout-index.c
+++ /dev/null
@@ -1,315 +0,0 @@
-/*
- * Check-out files from the "current cache directory"
- *
- * Copyright (C) 2005 Linus Torvalds
- *
- * Careful: order of argument flags does matter. For example,
- *
- *	git checkout-index -a -f file.c
- *
- * Will first check out all files listed in the cache (but not
- * overwrite any old ones), and then force-checkout "file.c" a
- * second time (ie that one _will_ overwrite any old contents
- * with the same filename).
- *
- * Also, just doing "git checkout-index" does nothing. You probably
- * meant "git checkout-index -a". And if you want to force it, you
- * want "git checkout-index -f -a".
- *
- * Intuitiveness is not the goal here. Repeatability is. The
- * reason for the "no arguments means no work" thing is that
- * from scripts you are supposed to be able to do things like
- *
- *	find . -name '*.h' -print0 | xargs -0 git checkout-index -f --
- *
- * or:
- *
- *	find . -name '*.h' -print0 | git checkout-index -f -z --stdin
- *
- * which will force all existing *.h files to be replaced with
- * their cached copies. If an empty command line implied "all",
- * then this would force-refresh everything in the cache, which
- * was not the point.
- *
- * Oh, and the "--" is just a good idea when you know the rest
- * will be filenames. Just so that you wouldn't have a filename
- * of "-a" causing problems (not possible in the above example,
- * but get used to it in scripting!).
- */
-#include "builtin.h"
-#include "cache.h"
-#include "quote.h"
-#include "cache-tree.h"
-#include "parse-options.h"
-
-#define CHECKOUT_ALL 4
-static int line_termination = '\n';
-static int checkout_stage; /* default to checkout stage0 */
-static int to_tempfile;
-static char topath[4][PATH_MAX + 1];
-
-static struct checkout state;
-
-static void write_tempfile_record(const char *name, int prefix_length)
-{
-	int i;
-
-	if (CHECKOUT_ALL == checkout_stage) {
-		for (i = 1; i < 4; i++) {
-			if (i > 1)
-				putchar(' ');
-			if (topath[i][0])
-				fputs(topath[i], stdout);
-			else
-				putchar('.');
-		}
-	} else
-		fputs(topath[checkout_stage], stdout);
-
-	putchar('\t');
-	write_name_quoted(name + prefix_length, stdout, line_termination);
-
-	for (i = 0; i < 4; i++) {
-		topath[i][0] = 0;
-	}
-}
-
-static int checkout_file(const char *name, int prefix_length)
-{
-	int namelen = strlen(name);
-	int pos = cache_name_pos(name, namelen);
-	int has_same_name = 0;
-	int did_checkout = 0;
-	int errs = 0;
-
-	if (pos < 0)
-		pos = -pos - 1;
-
-	while (pos < active_nr) {
-		struct cache_entry *ce = active_cache[pos];
-		if (ce_namelen(ce) != namelen ||
-		    memcmp(ce->name, name, namelen))
-			break;
-		has_same_name = 1;
-		pos++;
-		if (ce_stage(ce) != checkout_stage
-		    && (CHECKOUT_ALL != checkout_stage || !ce_stage(ce)))
-			continue;
-		did_checkout = 1;
-		if (checkout_entry(ce, &state,
-		    to_tempfile ? topath[ce_stage(ce)] : NULL) < 0)
-			errs++;
-	}
-
-	if (did_checkout) {
-		if (to_tempfile)
-			write_tempfile_record(name, prefix_length);
-		return errs > 0 ? -1 : 0;
-	}
-
-	if (!state.quiet) {
-		fprintf(stderr, "git checkout-index: %s ", name);
-		if (!has_same_name)
-			fprintf(stderr, "is not in the cache");
-		else if (checkout_stage)
-			fprintf(stderr, "does not exist at stage %d",
-				checkout_stage);
-		else
-			fprintf(stderr, "is unmerged");
-		fputc('\n', stderr);
-	}
-	return -1;
-}
-
-static void checkout_all(const char *prefix, int prefix_length)
-{
-	int i, errs = 0;
-	struct cache_entry *last_ce = NULL;
-
-	for (i = 0; i < active_nr ; i++) {
-		struct cache_entry *ce = active_cache[i];
-		if (ce_stage(ce) != checkout_stage
-		    && (CHECKOUT_ALL != checkout_stage || !ce_stage(ce)))
-			continue;
-		if (prefix && *prefix &&
-		    (ce_namelen(ce) <= prefix_length ||
-		     memcmp(prefix, ce->name, prefix_length)))
-			continue;
-		if (last_ce && to_tempfile) {
-			if (ce_namelen(last_ce) != ce_namelen(ce)
-			    || memcmp(last_ce->name, ce->name, ce_namelen(ce)))
-				write_tempfile_record(last_ce->name, prefix_length);
-		}
-		if (checkout_entry(ce, &state,
-		    to_tempfile ? topath[ce_stage(ce)] : NULL) < 0)
-			errs++;
-		last_ce = ce;
-	}
-	if (last_ce && to_tempfile)
-		write_tempfile_record(last_ce->name, prefix_length);
-	if (errs)
-		/* we have already done our error reporting.
-		 * exit with the same code as die().
-		 */
-		exit(128);
-}
-
-static const char * const builtin_checkout_index_usage[] = {
-	"git checkout-index [options] [--] <file>...",
-	NULL
-};
-
-static struct lock_file lock_file;
-
-static int option_parse_u(const struct option *opt,
-			      const char *arg, int unset)
-{
-	int *newfd = opt->value;
-
-	state.refresh_cache = 1;
-	if (*newfd < 0)
-		*newfd = hold_locked_index(&lock_file, 1);
-	return 0;
-}
-
-static int option_parse_z(const struct option *opt,
-			  const char *arg, int unset)
-{
-	if (unset)
-		line_termination = '\n';
-	else
-		line_termination = 0;
-	return 0;
-}
-
-static int option_parse_prefix(const struct option *opt,
-			       const char *arg, int unset)
-{
-	state.base_dir = arg;
-	state.base_dir_len = strlen(arg);
-	return 0;
-}
-
-static int option_parse_stage(const struct option *opt,
-			      const char *arg, int unset)
-{
-	if (!strcmp(arg, "all")) {
-		to_tempfile = 1;
-		checkout_stage = CHECKOUT_ALL;
-	} else {
-		int ch = arg[0];
-		if ('1' <= ch && ch <= '3')
-			checkout_stage = arg[0] - '0';
-		else
-			die("stage should be between 1 and 3 or all");
-	}
-	return 0;
-}
-
-int cmd_checkout_index(int argc, const char **argv, const char *prefix)
-{
-	int i;
-	int newfd = -1;
-	int all = 0;
-	int read_from_stdin = 0;
-	int prefix_length;
-	int force = 0, quiet = 0, not_new = 0;
-	struct option builtin_checkout_index_options[] = {
-		OPT_BOOLEAN('a', "all", &all,
-			"checks out all files in the index"),
-		OPT_BOOLEAN('f', "force", &force,
-			"forces overwrite of existing files"),
-		OPT__QUIET(&quiet),
-		OPT_BOOLEAN('n', "no-create", &not_new,
-			"don't checkout new files"),
-		{ OPTION_CALLBACK, 'u', "index", &newfd, NULL,
-			"update stat information in the index file",
-			PARSE_OPT_NOARG, option_parse_u },
-		{ OPTION_CALLBACK, 'z', NULL, NULL, NULL,
-			"paths are separated with NUL character",
-			PARSE_OPT_NOARG, option_parse_z },
-		OPT_BOOLEAN(0, "stdin", &read_from_stdin,
-			"read list of paths from the standard input"),
-		OPT_BOOLEAN(0, "temp", &to_tempfile,
-			"write the content to temporary files"),
-		OPT_CALLBACK(0, "prefix", NULL, "string",
-			"when creating files, prepend <string>",
-			option_parse_prefix),
-		OPT_CALLBACK(0, "stage", NULL, NULL,
-			"copy out the files from named stage",
-			option_parse_stage),
-		OPT_END()
-	};
-
-	git_config(git_default_config, NULL);
-	state.base_dir = "";
-	prefix_length = prefix ? strlen(prefix) : 0;
-
-	if (read_cache() < 0) {
-		die("invalid cache");
-	}
-
-	argc = parse_options(argc, argv, prefix, builtin_checkout_index_options,
-			builtin_checkout_index_usage, 0);
-	state.force = force;
-	state.quiet = quiet;
-	state.not_new = not_new;
-
-	if (state.base_dir_len || to_tempfile) {
-		/* when --prefix is specified we do not
-		 * want to update cache.
-		 */
-		if (state.refresh_cache) {
-			rollback_lock_file(&lock_file);
-			newfd = -1;
-		}
-		state.refresh_cache = 0;
-	}
-
-	/* Check out named files first */
-	for (i = 0; i < argc; i++) {
-		const char *arg = argv[i];
-		const char *p;
-
-		if (all)
-			die("git checkout-index: don't mix '--all' and explicit filenames");
-		if (read_from_stdin)
-			die("git checkout-index: don't mix '--stdin' and explicit filenames");
-		p = prefix_path(prefix, prefix_length, arg);
-		checkout_file(p, prefix_length);
-		if (p < arg || p > arg + strlen(arg))
-			free((char *)p);
-	}
-
-	if (read_from_stdin) {
-		struct strbuf buf = STRBUF_INIT, nbuf = STRBUF_INIT;
-
-		if (all)
-			die("git checkout-index: don't mix '--all' and '--stdin'");
-
-		while (strbuf_getline(&buf, stdin, line_termination) != EOF) {
-			const char *p;
-			if (line_termination && buf.buf[0] == '"') {
-				strbuf_reset(&nbuf);
-				if (unquote_c_style(&nbuf, buf.buf, NULL))
-					die("line is badly quoted");
-				strbuf_swap(&buf, &nbuf);
-			}
-			p = prefix_path(prefix, prefix_length, buf.buf);
-			checkout_file(p, prefix_length);
-			if (p < buf.buf || p > buf.buf + buf.len)
-				free((char *)p);
-		}
-		strbuf_release(&nbuf);
-		strbuf_release(&buf);
-	}
-
-	if (all)
-		checkout_all(prefix, prefix_length);
-
-	if (0 <= newfd &&
-	    (write_cache(newfd, active_cache, active_nr) ||
-	     commit_locked_index(&lock_file)))
-		die("Unable to write new index file");
-	return 0;
-}
diff --git a/builtin-checkout.c b/builtin-checkout.c
deleted file mode 100644
index 8a9a474..0000000
--- a/builtin-checkout.c
+++ /dev/null
@@ -1,749 +0,0 @@
-#include "cache.h"
-#include "builtin.h"
-#include "parse-options.h"
-#include "refs.h"
-#include "commit.h"
-#include "tree.h"
-#include "tree-walk.h"
-#include "cache-tree.h"
-#include "unpack-trees.h"
-#include "dir.h"
-#include "run-command.h"
-#include "merge-recursive.h"
-#include "branch.h"
-#include "diff.h"
-#include "revision.h"
-#include "remote.h"
-#include "blob.h"
-#include "xdiff-interface.h"
-#include "ll-merge.h"
-
-static const char * const checkout_usage[] = {
-	"git checkout [options] <branch>",
-	"git checkout [options] [<branch>] -- <file>...",
-	NULL,
-};
-
-struct checkout_opts {
-	int quiet;
-	int merge;
-	int force;
-	int writeout_stage;
-	int writeout_error;
-
-	const char *new_branch;
-	int new_branch_log;
-	enum branch_track track;
-};
-
-static int post_checkout_hook(struct commit *old, struct commit *new,
-			      int changed)
-{
-	return run_hook(NULL, "post-checkout",
-			sha1_to_hex(old ? old->object.sha1 : null_sha1),
-			sha1_to_hex(new ? new->object.sha1 : null_sha1),
-			changed ? "1" : "0", NULL);
-	/* "new" can be NULL when checking out from the index before
-	   a commit exists. */
-
-}
-
-static int update_some(const unsigned char *sha1, const char *base, int baselen,
-		const char *pathname, unsigned mode, int stage, void *context)
-{
-	int len;
-	struct cache_entry *ce;
-
-	if (S_ISDIR(mode))
-		return READ_TREE_RECURSIVE;
-
-	len = baselen + strlen(pathname);
-	ce = xcalloc(1, cache_entry_size(len));
-	hashcpy(ce->sha1, sha1);
-	memcpy(ce->name, base, baselen);
-	memcpy(ce->name + baselen, pathname, len - baselen);
-	ce->ce_flags = create_ce_flags(len, 0);
-	ce->ce_mode = create_ce_mode(mode);
-	add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
-	return 0;
-}
-
-static int read_tree_some(struct tree *tree, const char **pathspec)
-{
-	read_tree_recursive(tree, "", 0, 0, pathspec, update_some, NULL);
-
-	/* update the index with the given tree's info
-	 * for all args, expanding wildcards, and exit
-	 * with any non-zero return code.
-	 */
-	return 0;
-}
-
-static int skip_same_name(struct cache_entry *ce, int pos)
-{
-	while (++pos < active_nr &&
-	       !strcmp(active_cache[pos]->name, ce->name))
-		; /* skip */
-	return pos;
-}
-
-static int check_stage(int stage, struct cache_entry *ce, int pos)
-{
-	while (pos < active_nr &&
-	       !strcmp(active_cache[pos]->name, ce->name)) {
-		if (ce_stage(active_cache[pos]) == stage)
-			return 0;
-		pos++;
-	}
-	return error("path '%s' does not have %s version",
-		     ce->name,
-		     (stage == 2) ? "our" : "their");
-}
-
-static int check_all_stages(struct cache_entry *ce, int pos)
-{
-	if (ce_stage(ce) != 1 ||
-	    active_nr <= pos + 2 ||
-	    strcmp(active_cache[pos+1]->name, ce->name) ||
-	    ce_stage(active_cache[pos+1]) != 2 ||
-	    strcmp(active_cache[pos+2]->name, ce->name) ||
-	    ce_stage(active_cache[pos+2]) != 3)
-		return error("path '%s' does not have all three versions",
-			     ce->name);
-	return 0;
-}
-
-static int checkout_stage(int stage, struct cache_entry *ce, int pos,
-			  struct checkout *state)
-{
-	while (pos < active_nr &&
-	       !strcmp(active_cache[pos]->name, ce->name)) {
-		if (ce_stage(active_cache[pos]) == stage)
-			return checkout_entry(active_cache[pos], state, NULL);
-		pos++;
-	}
-	return error("path '%s' does not have %s version",
-		     ce->name,
-		     (stage == 2) ? "our" : "their");
-}
-
-/* NEEDSWORK: share with merge-recursive */
-static void fill_mm(const unsigned char *sha1, mmfile_t *mm)
-{
-	unsigned long size;
-	enum object_type type;
-
-	if (!hashcmp(sha1, null_sha1)) {
-		mm->ptr = xstrdup("");
-		mm->size = 0;
-		return;
-	}
-
-	mm->ptr = read_sha1_file(sha1, &type, &size);
-	if (!mm->ptr || type != OBJ_BLOB)
-		die("unable to read blob object %s", sha1_to_hex(sha1));
-	mm->size = size;
-}
-
-static int checkout_merged(int pos, struct checkout *state)
-{
-	struct cache_entry *ce = active_cache[pos];
-	const char *path = ce->name;
-	mmfile_t ancestor, ours, theirs;
-	int status;
-	unsigned char sha1[20];
-	mmbuffer_t result_buf;
-
-	if (ce_stage(ce) != 1 ||
-	    active_nr <= pos + 2 ||
-	    strcmp(active_cache[pos+1]->name, path) ||
-	    ce_stage(active_cache[pos+1]) != 2 ||
-	    strcmp(active_cache[pos+2]->name, path) ||
-	    ce_stage(active_cache[pos+2]) != 3)
-		return error("path '%s' does not have all 3 versions", path);
-
-	fill_mm(active_cache[pos]->sha1, &ancestor);
-	fill_mm(active_cache[pos+1]->sha1, &ours);
-	fill_mm(active_cache[pos+2]->sha1, &theirs);
-
-	status = ll_merge(&result_buf, path, &ancestor,
-			  &ours, "ours", &theirs, "theirs", 1);
-	free(ancestor.ptr);
-	free(ours.ptr);
-	free(theirs.ptr);
-	if (status < 0 || !result_buf.ptr) {
-		free(result_buf.ptr);
-		return error("path '%s': cannot merge", path);
-	}
-
-	/*
-	 * NEEDSWORK:
-	 * There is absolutely no reason to write this as a blob object
-	 * and create a phony cache entry just to leak.  This hack is
-	 * primarily to get to the write_entry() machinery that massages
-	 * the contents to work-tree format and writes out which only
-	 * allows it for a cache entry.  The code in write_entry() needs
-	 * to be refactored to allow us to feed a <buffer, size, mode>
-	 * instead of a cache entry.  Such a refactoring would help
-	 * merge_recursive as well (it also writes the merge result to the
-	 * object database even when it may contain conflicts).
-	 */
-	if (write_sha1_file(result_buf.ptr, result_buf.size,
-			    blob_type, sha1))
-		die("Unable to add merge result for '%s'", path);
-	ce = make_cache_entry(create_ce_mode(active_cache[pos+1]->ce_mode),
-			      sha1,
-			      path, 2, 0);
-	if (!ce)
-		die("make_cache_entry failed for path '%s'", path);
-	status = checkout_entry(ce, state, NULL);
-	return status;
-}
-
-static int checkout_paths(struct tree *source_tree, const char **pathspec,
-			  struct checkout_opts *opts)
-{
-	int pos;
-	struct checkout state;
-	static char *ps_matched;
-	unsigned char rev[20];
-	int flag;
-	struct commit *head;
-	int errs = 0;
-	int stage = opts->writeout_stage;
-	int merge = opts->merge;
-	int newfd;
-	struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
-
-	newfd = hold_locked_index(lock_file, 1);
-	if (read_cache_preload(pathspec) < 0)
-		return error("corrupt index file");
-
-	if (source_tree)
-		read_tree_some(source_tree, pathspec);
-
-	for (pos = 0; pathspec[pos]; pos++)
-		;
-	ps_matched = xcalloc(1, pos);
-
-	for (pos = 0; pos < active_nr; pos++) {
-		struct cache_entry *ce = active_cache[pos];
-		match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, ps_matched);
-	}
-
-	if (report_path_error(ps_matched, pathspec, 0))
-		return 1;
-
-	/* Any unmerged paths? */
-	for (pos = 0; pos < active_nr; pos++) {
-		struct cache_entry *ce = active_cache[pos];
-		if (match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL)) {
-			if (!ce_stage(ce))
-				continue;
-			if (opts->force) {
-				warning("path '%s' is unmerged", ce->name);
-			} else if (stage) {
-				errs |= check_stage(stage, ce, pos);
-			} else if (opts->merge) {
-				errs |= check_all_stages(ce, pos);
-			} else {
-				errs = 1;
-				error("path '%s' is unmerged", ce->name);
-			}
-			pos = skip_same_name(ce, pos) - 1;
-		}
-	}
-	if (errs)
-		return 1;
-
-	/* Now we are committed to check them out */
-	memset(&state, 0, sizeof(state));
-	state.force = 1;
-	state.refresh_cache = 1;
-	for (pos = 0; pos < active_nr; pos++) {
-		struct cache_entry *ce = active_cache[pos];
-		if (match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL)) {
-			if (!ce_stage(ce)) {
-				errs |= checkout_entry(ce, &state, NULL);
-				continue;
-			}
-			if (stage)
-				errs |= checkout_stage(stage, ce, pos, &state);
-			else if (merge)
-				errs |= checkout_merged(pos, &state);
-			pos = skip_same_name(ce, pos) - 1;
-		}
-	}
-
-	if (write_cache(newfd, active_cache, active_nr) ||
-	    commit_locked_index(lock_file))
-		die("unable to write new index file");
-
-	resolve_ref("HEAD", rev, 0, &flag);
-	head = lookup_commit_reference_gently(rev, 1);
-
-	errs |= post_checkout_hook(head, head, 0);
-	return errs;
-}
-
-static void show_local_changes(struct object *head)
-{
-	struct rev_info rev;
-	/* I think we want full paths, even if we're in a subdirectory. */
-	init_revisions(&rev, NULL);
-	rev.abbrev = 0;
-	rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS;
-	if (diff_setup_done(&rev.diffopt) < 0)
-		die("diff_setup_done failed");
-	add_pending_object(&rev, head, NULL);
-	run_diff_index(&rev, 0);
-}
-
-static void describe_detached_head(char *msg, struct commit *commit)
-{
-	struct strbuf sb = STRBUF_INIT;
-	parse_commit(commit);
-	pretty_print_commit(CMIT_FMT_ONELINE, commit, &sb, 0, NULL, NULL, 0, 0);
-	fprintf(stderr, "%s %s... %s\n", msg,
-		find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV), sb.buf);
-	strbuf_release(&sb);
-}
-
-static int reset_tree(struct tree *tree, struct checkout_opts *o, int worktree)
-{
-	struct unpack_trees_options opts;
-	struct tree_desc tree_desc;
-
-	memset(&opts, 0, sizeof(opts));
-	opts.head_idx = -1;
-	opts.update = worktree;
-	opts.skip_unmerged = !worktree;
-	opts.reset = 1;
-	opts.merge = 1;
-	opts.fn = oneway_merge;
-	opts.verbose_update = !o->quiet;
-	opts.src_index = &the_index;
-	opts.dst_index = &the_index;
-	parse_tree(tree);
-	init_tree_desc(&tree_desc, tree->buffer, tree->size);
-	switch (unpack_trees(1, &tree_desc, &opts)) {
-	case -2:
-		o->writeout_error = 1;
-		/*
-		 * We return 0 nevertheless, as the index is all right
-		 * and more importantly we have made best efforts to
-		 * update paths in the work tree, and we cannot revert
-		 * them.
-		 */
-	case 0:
-		return 0;
-	default:
-		return 128;
-	}
-}
-
-struct branch_info {
-	const char *name; /* The short name used */
-	const char *path; /* The full name of a real branch */
-	struct commit *commit; /* The named commit */
-};
-
-static void setup_branch_path(struct branch_info *branch)
-{
-	struct strbuf buf = STRBUF_INIT;
-
-	strbuf_branchname(&buf, branch->name);
-	if (strcmp(buf.buf, branch->name))
-		branch->name = xstrdup(buf.buf);
-	strbuf_splice(&buf, 0, 0, "refs/heads/", 11);
-	branch->path = strbuf_detach(&buf, NULL);
-}
-
-static int merge_working_tree(struct checkout_opts *opts,
-			      struct branch_info *old, struct branch_info *new)
-{
-	int ret;
-	struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
-	int newfd = hold_locked_index(lock_file, 1);
-
-	if (read_cache_preload(NULL) < 0)
-		return error("corrupt index file");
-
-	if (opts->force) {
-		ret = reset_tree(new->commit->tree, opts, 1);
-		if (ret)
-			return ret;
-	} else {
-		struct tree_desc trees[2];
-		struct tree *tree;
-		struct unpack_trees_options topts;
-
-		memset(&topts, 0, sizeof(topts));
-		topts.head_idx = -1;
-		topts.src_index = &the_index;
-		topts.dst_index = &the_index;
-
-		topts.msgs.not_uptodate_file = "You have local changes to '%s'; cannot switch branches.";
-
-		refresh_cache(REFRESH_QUIET);
-
-		if (unmerged_cache()) {
-			error("you need to resolve your current index first");
-			return 1;
-		}
-
-		/* 2-way merge to the new branch */
-		topts.initial_checkout = is_cache_unborn();
-		topts.update = 1;
-		topts.merge = 1;
-		topts.gently = opts->merge;
-		topts.verbose_update = !opts->quiet;
-		topts.fn = twoway_merge;
-		topts.dir = xcalloc(1, sizeof(*topts.dir));
-		topts.dir->flags |= DIR_SHOW_IGNORED;
-		topts.dir->exclude_per_dir = ".gitignore";
-		tree = parse_tree_indirect(old->commit->object.sha1);
-		init_tree_desc(&trees[0], tree->buffer, tree->size);
-		tree = parse_tree_indirect(new->commit->object.sha1);
-		init_tree_desc(&trees[1], tree->buffer, tree->size);
-
-		ret = unpack_trees(2, trees, &topts);
-		if (ret == -1) {
-			/*
-			 * Unpack couldn't do a trivial merge; either
-			 * give up or do a real merge, depending on
-			 * whether the merge flag was used.
-			 */
-			struct tree *result;
-			struct tree *work;
-			struct merge_options o;
-			if (!opts->merge)
-				return 1;
-			parse_commit(old->commit);
-
-			/* Do more real merge */
-
-			/*
-			 * We update the index fully, then write the
-			 * tree from the index, then merge the new
-			 * branch with the current tree, with the old
-			 * branch as the base. Then we reset the index
-			 * (but not the working tree) to the new
-			 * branch, leaving the working tree as the
-			 * merged version, but skipping unmerged
-			 * entries in the index.
-			 */
-
-			add_files_to_cache(NULL, NULL, 0);
-			init_merge_options(&o);
-			o.verbosity = 0;
-			work = write_tree_from_memory(&o);
-
-			ret = reset_tree(new->commit->tree, opts, 1);
-			if (ret)
-				return ret;
-			o.branch1 = new->name;
-			o.branch2 = "local";
-			merge_trees(&o, new->commit->tree, work,
-				old->commit->tree, &result);
-			ret = reset_tree(new->commit->tree, opts, 0);
-			if (ret)
-				return ret;
-		}
-	}
-
-	if (write_cache(newfd, active_cache, active_nr) ||
-	    commit_locked_index(lock_file))
-		die("unable to write new index file");
-
-	if (!opts->force && !opts->quiet)
-		show_local_changes(&new->commit->object);
-
-	return 0;
-}
-
-static void report_tracking(struct branch_info *new)
-{
-	struct strbuf sb = STRBUF_INIT;
-	struct branch *branch = branch_get(new->name);
-
-	if (!format_tracking_info(branch, &sb))
-		return;
-	fputs(sb.buf, stdout);
-	strbuf_release(&sb);
-}
-
-static void update_refs_for_switch(struct checkout_opts *opts,
-				   struct branch_info *old,
-				   struct branch_info *new)
-{
-	struct strbuf msg = STRBUF_INIT;
-	const char *old_desc;
-	if (opts->new_branch) {
-		create_branch(old->name, opts->new_branch, new->name, 0,
-			      opts->new_branch_log, opts->track);
-		new->name = opts->new_branch;
-		setup_branch_path(new);
-	}
-
-	old_desc = old->name;
-	if (!old_desc && old->commit)
-		old_desc = sha1_to_hex(old->commit->object.sha1);
-	strbuf_addf(&msg, "checkout: moving from %s to %s",
-		    old_desc ? old_desc : "(invalid)", new->name);
-
-	if (new->path) {
-		create_symref("HEAD", new->path, msg.buf);
-		if (!opts->quiet) {
-			if (old->path && !strcmp(new->path, old->path))
-				fprintf(stderr, "Already on '%s'\n",
-					new->name);
-			else
-				fprintf(stderr, "Switched to%s branch '%s'\n",
-					opts->new_branch ? " a new" : "",
-					new->name);
-		}
-	} else if (strcmp(new->name, "HEAD")) {
-		update_ref(msg.buf, "HEAD", new->commit->object.sha1, NULL,
-			   REF_NODEREF, DIE_ON_ERR);
-		if (!opts->quiet) {
-			if (old->path)
-				fprintf(stderr, "Note: moving to '%s' which isn't a local branch\nIf you want to create a new branch from this checkout, you may do so\n(now or later) by using -b with the checkout command again. Example:\n  git checkout -b <new_branch_name>\n", new->name);
-			describe_detached_head("HEAD is now at", new->commit);
-		}
-	}
-	remove_branch_state();
-	strbuf_release(&msg);
-	if (!opts->quiet && (new->path || !strcmp(new->name, "HEAD")))
-		report_tracking(new);
-}
-
-static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
-{
-	int ret = 0;
-	struct branch_info old;
-	unsigned char rev[20];
-	int flag;
-	memset(&old, 0, sizeof(old));
-	old.path = resolve_ref("HEAD", rev, 0, &flag);
-	old.commit = lookup_commit_reference_gently(rev, 1);
-	if (!(flag & REF_ISSYMREF))
-		old.path = NULL;
-
-	if (old.path && !prefixcmp(old.path, "refs/heads/"))
-		old.name = old.path + strlen("refs/heads/");
-
-	if (!new->name) {
-		new->name = "HEAD";
-		new->commit = old.commit;
-		if (!new->commit)
-			die("You are on a branch yet to be born");
-		parse_commit(new->commit);
-	}
-
-	if (!old.commit && !opts->force) {
-		if (!opts->quiet) {
-			warning("You appear to be on a branch yet to be born.");
-			warning("Forcing checkout of %s.", new->name);
-		}
-		opts->force = 1;
-	}
-
-	ret = merge_working_tree(opts, &old, new);
-	if (ret)
-		return ret;
-
-	/*
-	 * If we were on a detached HEAD, but have now moved to
-	 * a new commit, we want to mention the old commit once more
-	 * to remind the user that it might be lost.
-	 */
-	if (!opts->quiet && !old.path && old.commit && new->commit != old.commit)
-		describe_detached_head("Previous HEAD position was", old.commit);
-
-	update_refs_for_switch(opts, &old, new);
-
-	ret = post_checkout_hook(old.commit, new->commit, 1);
-	return ret || opts->writeout_error;
-}
-
-static int git_checkout_config(const char *var, const char *value, void *cb)
-{
-	return git_xmerge_config(var, value, cb);
-}
-
-int cmd_checkout(int argc, const char **argv, const char *prefix)
-{
-	struct checkout_opts opts;
-	unsigned char rev[20];
-	const char *arg;
-	struct branch_info new;
-	struct tree *source_tree = NULL;
-	char *conflict_style = NULL;
-	struct option options[] = {
-		OPT__QUIET(&opts.quiet),
-		OPT_STRING('b', NULL, &opts.new_branch, "new branch", "branch"),
-		OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "log for new branch"),
-		OPT_SET_INT('t', "track",  &opts.track, "track",
-			BRANCH_TRACK_EXPLICIT),
-		OPT_SET_INT('2', "ours", &opts.writeout_stage, "stage",
-			    2),
-		OPT_SET_INT('3', "theirs", &opts.writeout_stage, "stage",
-			    3),
-		OPT_BOOLEAN('f', NULL, &opts.force, "force"),
-		OPT_BOOLEAN('m', "merge", &opts.merge, "merge"),
-		OPT_STRING(0, "conflict", &conflict_style, "style",
-			   "conflict style (merge or diff3)"),
-		OPT_END(),
-	};
-	int has_dash_dash;
-
-	memset(&opts, 0, sizeof(opts));
-	memset(&new, 0, sizeof(new));
-
-	git_config(git_checkout_config, NULL);
-
-	opts.track = BRANCH_TRACK_UNSPECIFIED;
-
-	argc = parse_options(argc, argv, prefix, options, checkout_usage,
-			     PARSE_OPT_KEEP_DASHDASH);
-
-	/* --track without -b should DWIM */
-	if (0 < opts.track && !opts.new_branch) {
-		const char *argv0 = argv[0];
-		if (!argc || !strcmp(argv0, "--"))
-			die ("--track needs a branch name");
-		if (!prefixcmp(argv0, "refs/"))
-			argv0 += 5;
-		if (!prefixcmp(argv0, "remotes/"))
-			argv0 += 8;
-		argv0 = strchr(argv0, '/');
-		if (!argv0 || !argv0[1])
-			die ("Missing branch name; try -b");
-		opts.new_branch = argv0 + 1;
-	}
-
-	if (opts.track == BRANCH_TRACK_UNSPECIFIED)
-		opts.track = git_branch_track;
-	if (conflict_style) {
-		opts.merge = 1; /* implied */
-		git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
-	}
-
-	if (opts.force && opts.merge)
-		die("git checkout: -f and -m are incompatible");
-
-	/*
-	 * case 1: git checkout <ref> -- [<paths>]
-	 *
-	 *   <ref> must be a valid tree, everything after the '--' must be
-	 *   a path.
-	 *
-	 * case 2: git checkout -- [<paths>]
-	 *
-	 *   everything after the '--' must be paths.
-	 *
-	 * case 3: git checkout <something> [<paths>]
-	 *
-	 *   With no paths, if <something> is a commit, that is to
-	 *   switch to the branch or detach HEAD at it.
-	 *
-	 *   Otherwise <something> shall not be ambiguous.
-	 *   - If it's *only* a reference, treat it like case (1).
-	 *   - If it's only a path, treat it like case (2).
-	 *   - else: fail.
-	 *
-	 */
-	if (argc) {
-		if (!strcmp(argv[0], "--")) {       /* case (2) */
-			argv++;
-			argc--;
-			goto no_reference;
-		}
-
-		arg = argv[0];
-		has_dash_dash = (argc > 1) && !strcmp(argv[1], "--");
-
-		if (!strcmp(arg, "-"))
-			arg = "@{-1}";
-
-		if (get_sha1(arg, rev)) {
-			if (has_dash_dash)          /* case (1) */
-				die("invalid reference: %s", arg);
-			goto no_reference;          /* case (3 -> 2) */
-		}
-
-		/* we can't end up being in (2) anymore, eat the argument */
-		argv++;
-		argc--;
-
-		new.name = arg;
-		if ((new.commit = lookup_commit_reference_gently(rev, 1))) {
-			setup_branch_path(&new);
-			if (resolve_ref(new.path, rev, 1, NULL))
-				new.commit = lookup_commit_reference(rev);
-			else
-				new.path = NULL;
-			parse_commit(new.commit);
-			source_tree = new.commit->tree;
-		} else
-			source_tree = parse_tree_indirect(rev);
-
-		if (!source_tree)                   /* case (1): want a tree */
-			die("reference is not a tree: %s", arg);
-		if (!has_dash_dash) {/* case (3 -> 1) */
-			/*
-			 * Do not complain the most common case
-			 *	git checkout branch
-			 * even if there happen to be a file called 'branch';
-			 * it would be extremely annoying.
-			 */
-			if (argc)
-				verify_non_filename(NULL, arg);
-		}
-		else {
-			argv++;
-			argc--;
-		}
-	}
-
-no_reference:
-	if (argc) {
-		const char **pathspec = get_pathspec(prefix, argv);
-
-		if (!pathspec)
-			die("invalid path specification");
-
-		/* Checkout paths */
-		if (opts.new_branch) {
-			if (argc == 1) {
-				die("git checkout: updating paths is incompatible with switching branches.\nDid you intend to checkout '%s' which can not be resolved as commit?", argv[0]);
-			} else {
-				die("git checkout: updating paths is incompatible with switching branches.");
-			}
-		}
-
-		if (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)
-			die("git checkout: --ours/--theirs, --force and --merge are incompatible when\nchecking out of the index.");
-
-		return checkout_paths(source_tree, pathspec, &opts);
-	}
-
-	if (opts.new_branch) {
-		struct strbuf buf = STRBUF_INIT;
-		if (strbuf_check_branch_ref(&buf, opts.new_branch))
-			die("git checkout: we do not like '%s' as a branch name.",
-			    opts.new_branch);
-		if (!get_sha1(buf.buf, rev))
-			die("git checkout: branch %s already exists", opts.new_branch);
-		strbuf_release(&buf);
-	}
-
-	if (new.name && !new.commit) {
-		die("Cannot switch branch to a non-commit.");
-	}
-	if (opts.writeout_stage)
-		die("--ours/--theirs is incompatible with switching branches.");
-
-	return switch_branches(&opts, &new);
-}
diff --git a/builtin-clean.c b/builtin-clean.c
deleted file mode 100644
index 2d8c735..0000000
--- a/builtin-clean.c
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * "git clean" builtin command
- *
- * Copyright (C) 2007 Shawn Bohrer
- *
- * Based on git-clean.sh by Pavel Roskin
- */
-
-#include "builtin.h"
-#include "cache.h"
-#include "dir.h"
-#include "parse-options.h"
-#include "quote.h"
-
-static int force = -1; /* unset */
-
-static const char *const builtin_clean_usage[] = {
-	"git clean [-d] [-f] [-n] [-q] [-x | -X] [--] <paths>...",
-	NULL
-};
-
-static int git_clean_config(const char *var, const char *value, void *cb)
-{
-	if (!strcmp(var, "clean.requireforce"))
-		force = !git_config_bool(var, value);
-	return git_default_config(var, value, cb);
-}
-
-int cmd_clean(int argc, const char **argv, const char *prefix)
-{
-	int i;
-	int show_only = 0, remove_directories = 0, quiet = 0, ignored = 0;
-	int ignored_only = 0, baselen = 0, config_set = 0, errors = 0;
-	struct strbuf directory = STRBUF_INIT;
-	struct dir_struct dir;
-	static const char **pathspec;
-	struct strbuf buf = STRBUF_INIT;
-	const char *qname;
-	char *seen = NULL;
-	struct option options[] = {
-		OPT__QUIET(&quiet),
-		OPT__DRY_RUN(&show_only),
-		OPT_BOOLEAN('f', NULL, &force, "force"),
-		OPT_BOOLEAN('d', NULL, &remove_directories,
-				"remove whole directories"),
-		OPT_BOOLEAN('x', NULL, &ignored, "remove ignored files, too"),
-		OPT_BOOLEAN('X', NULL, &ignored_only,
-				"remove only ignored files"),
-		OPT_END()
-	};
-
-	git_config(git_clean_config, NULL);
-	if (force < 0)
-		force = 0;
-	else
-		config_set = 1;
-
-	argc = parse_options(argc, argv, prefix, options, builtin_clean_usage,
-			     0);
-
-	memset(&dir, 0, sizeof(dir));
-	if (ignored_only)
-		dir.flags |= DIR_SHOW_IGNORED;
-
-	if (ignored && ignored_only)
-		die("-x and -X cannot be used together");
-
-	if (!show_only && !force)
-		die("clean.requireForce%s set and -n or -f not given; "
-		    "refusing to clean", config_set ? "" : " not");
-
-	dir.flags |= DIR_SHOW_OTHER_DIRECTORIES;
-
-	if (!ignored)
-		setup_standard_excludes(&dir);
-
-	pathspec = get_pathspec(prefix, argv);
-	read_cache();
-
-	fill_directory(&dir, pathspec);
-
-	if (pathspec)
-		seen = xmalloc(argc > 0 ? argc : 1);
-
-	for (i = 0; i < dir.nr; i++) {
-		struct dir_entry *ent = dir.entries[i];
-		int len, pos;
-		int matches = 0;
-		struct cache_entry *ce;
-		struct stat st;
-
-		/*
-		 * Remove the '/' at the end that directory
-		 * walking adds for directory entries.
-		 */
-		len = ent->len;
-		if (len && ent->name[len-1] == '/')
-			len--;
-		pos = cache_name_pos(ent->name, len);
-		if (0 <= pos)
-			continue;	/* exact match */
-		pos = -pos - 1;
-		if (pos < active_nr) {
-			ce = active_cache[pos];
-			if (ce_namelen(ce) == len &&
-			    !memcmp(ce->name, ent->name, len))
-				continue; /* Yup, this one exists unmerged */
-		}
-
-		/*
-		 * we might have removed this as part of earlier
-		 * recursive directory removal, so lstat() here could
-		 * fail with ENOENT.
-		 */
-		if (lstat(ent->name, &st))
-			continue;
-
-		if (pathspec) {
-			memset(seen, 0, argc > 0 ? argc : 1);
-			matches = match_pathspec(pathspec, ent->name, len,
-						 baselen, seen);
-		}
-
-		if (S_ISDIR(st.st_mode)) {
-			strbuf_addstr(&directory, ent->name);
-			qname = quote_path_relative(directory.buf, directory.len, &buf, prefix);
-			if (show_only && (remove_directories ||
-			    (matches == MATCHED_EXACTLY))) {
-				printf("Would remove %s\n", qname);
-			} else if (remove_directories ||
-				   (matches == MATCHED_EXACTLY)) {
-				if (!quiet)
-					printf("Removing %s\n", qname);
-				if (remove_dir_recursively(&directory, 0) != 0) {
-					warning("failed to remove '%s'", qname);
-					errors++;
-				}
-			} else if (show_only) {
-				printf("Would not remove %s\n", qname);
-			} else {
-				printf("Not removing %s\n", qname);
-			}
-			strbuf_reset(&directory);
-		} else {
-			if (pathspec && !matches)
-				continue;
-			qname = quote_path_relative(ent->name, -1, &buf, prefix);
-			if (show_only) {
-				printf("Would remove %s\n", qname);
-				continue;
-			} else if (!quiet) {
-				printf("Removing %s\n", qname);
-			}
-			if (unlink(ent->name) != 0) {
-				warning("failed to remove '%s'", qname);
-				errors++;
-			}
-		}
-	}
-	free(seen);
-
-	strbuf_release(&directory);
-	return (errors != 0);
-}
diff --git a/builtin-clone.c b/builtin-clone.c
deleted file mode 100644
index 32dea74..0000000
--- a/builtin-clone.c
+++ /dev/null
@@ -1,619 +0,0 @@
-/*
- * Builtin "git clone"
- *
- * Copyright (c) 2007 Kristian Høgsberg <krh@redhat.com>,
- *		 2008 Daniel Barkalow <barkalow@iabervon.org>
- * Based on git-commit.sh by Junio C Hamano and Linus Torvalds
- *
- * Clone a repository into a different directory that does not yet exist.
- */
-
-#include "cache.h"
-#include "parse-options.h"
-#include "fetch-pack.h"
-#include "refs.h"
-#include "tree.h"
-#include "tree-walk.h"
-#include "unpack-trees.h"
-#include "transport.h"
-#include "strbuf.h"
-#include "dir.h"
-#include "pack-refs.h"
-#include "sigchain.h"
-#include "branch.h"
-#include "remote.h"
-#include "run-command.h"
-
-/*
- * Overall FIXMEs:
- *  - respect DB_ENVIRONMENT for .git/objects.
- *
- * Implementation notes:
- *  - dropping use-separate-remote and no-separate-remote compatibility
- *
- */
-static const char * const builtin_clone_usage[] = {
-	"git clone [options] [--] <repo> [<dir>]",
-	NULL
-};
-
-static int option_quiet, option_no_checkout, option_bare, option_mirror;
-static int option_local, option_no_hardlinks, option_shared;
-static char *option_template, *option_reference, *option_depth;
-static char *option_origin = NULL;
-static char *option_upload_pack = "git-upload-pack";
-static int option_verbose;
-
-static struct option builtin_clone_options[] = {
-	OPT__QUIET(&option_quiet),
-	OPT__VERBOSE(&option_verbose),
-	OPT_BOOLEAN('n', "no-checkout", &option_no_checkout,
-		    "don't create a checkout"),
-	OPT_BOOLEAN(0, "bare", &option_bare, "create a bare repository"),
-	OPT_BOOLEAN(0, "naked", &option_bare, "create a bare repository"),
-	OPT_BOOLEAN(0, "mirror", &option_mirror,
-		    "create a mirror repository (implies bare)"),
-	OPT_BOOLEAN('l', "local", &option_local,
-		    "to clone from a local repository"),
-	OPT_BOOLEAN(0, "no-hardlinks", &option_no_hardlinks,
-		    "don't use local hardlinks, always copy"),
-	OPT_BOOLEAN('s', "shared", &option_shared,
-		    "setup as shared repository"),
-	OPT_STRING(0, "template", &option_template, "path",
-		   "path the template repository"),
-	OPT_STRING(0, "reference", &option_reference, "repo",
-		   "reference repository"),
-	OPT_STRING('o', "origin", &option_origin, "branch",
-		   "use <branch> instead of 'origin' to track upstream"),
-	OPT_STRING('u', "upload-pack", &option_upload_pack, "path",
-		   "path to git-upload-pack on the remote"),
-	OPT_STRING(0, "depth", &option_depth, "depth",
-		    "create a shallow clone of that depth"),
-
-	OPT_END()
-};
-
-static char *get_repo_path(const char *repo, int *is_bundle)
-{
-	static char *suffix[] = { "/.git", ".git", "" };
-	static char *bundle_suffix[] = { ".bundle", "" };
-	struct stat st;
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(suffix); i++) {
-		const char *path;
-		path = mkpath("%s%s", repo, suffix[i]);
-		if (is_directory(path)) {
-			*is_bundle = 0;
-			return xstrdup(make_nonrelative_path(path));
-		}
-	}
-
-	for (i = 0; i < ARRAY_SIZE(bundle_suffix); i++) {
-		const char *path;
-		path = mkpath("%s%s", repo, bundle_suffix[i]);
-		if (!stat(path, &st) && S_ISREG(st.st_mode)) {
-			*is_bundle = 1;
-			return xstrdup(make_nonrelative_path(path));
-		}
-	}
-
-	return NULL;
-}
-
-static char *guess_dir_name(const char *repo, int is_bundle, int is_bare)
-{
-	const char *end = repo + strlen(repo), *start;
-	char *dir;
-
-	/*
-	 * Strip trailing spaces, slashes and /.git
-	 */
-	while (repo < end && (is_dir_sep(end[-1]) || isspace(end[-1])))
-		end--;
-	if (end - repo > 5 && is_dir_sep(end[-5]) &&
-	    !strncmp(end - 4, ".git", 4)) {
-		end -= 5;
-		while (repo < end && is_dir_sep(end[-1]))
-			end--;
-	}
-
-	/*
-	 * Find last component, but be prepared that repo could have
-	 * the form  "remote.example.com:foo.git", i.e. no slash
-	 * in the directory part.
-	 */
-	start = end;
-	while (repo < start && !is_dir_sep(start[-1]) && start[-1] != ':')
-		start--;
-
-	/*
-	 * Strip .{bundle,git}.
-	 */
-	if (is_bundle) {
-		if (end - start > 7 && !strncmp(end - 7, ".bundle", 7))
-			end -= 7;
-	} else {
-		if (end - start > 4 && !strncmp(end - 4, ".git", 4))
-			end -= 4;
-	}
-
-	if (is_bare) {
-		struct strbuf result = STRBUF_INIT;
-		strbuf_addf(&result, "%.*s.git", (int)(end - start), start);
-		dir = strbuf_detach(&result, NULL);
-	} else
-		dir = xstrndup(start, end - start);
-	/*
-	 * Replace sequences of 'control' characters and whitespace
-	 * with one ascii space, remove leading and trailing spaces.
-	 */
-	if (*dir) {
-		char *out = dir;
-		int prev_space = 1 /* strip leading whitespace */;
-		for (end = dir; *end; ++end) {
-			char ch = *end;
-			if ((unsigned char)ch < '\x20')
-				ch = '\x20';
-			if (isspace(ch)) {
-				if (prev_space)
-					continue;
-				prev_space = 1;
-			} else
-				prev_space = 0;
-			*out++ = ch;
-		}
-		*out = '\0';
-		if (out > dir && prev_space)
-			out[-1] = '\0';
-	}
-	return dir;
-}
-
-static void strip_trailing_slashes(char *dir)
-{
-	char *end = dir + strlen(dir);
-
-	while (dir < end - 1 && is_dir_sep(end[-1]))
-		end--;
-	*end = '\0';
-}
-
-static void setup_reference(const char *repo)
-{
-	const char *ref_git;
-	char *ref_git_copy;
-
-	struct remote *remote;
-	struct transport *transport;
-	const struct ref *extra;
-
-	ref_git = make_absolute_path(option_reference);
-
-	if (is_directory(mkpath("%s/.git/objects", ref_git)))
-		ref_git = mkpath("%s/.git", ref_git);
-	else if (!is_directory(mkpath("%s/objects", ref_git)))
-		die("reference repository '%s' is not a local directory.",
-		    option_reference);
-
-	ref_git_copy = xstrdup(ref_git);
-
-	add_to_alternates_file(ref_git_copy);
-
-	remote = remote_get(ref_git_copy);
-	transport = transport_get(remote, ref_git_copy);
-	for (extra = transport_get_remote_refs(transport); extra;
-	     extra = extra->next)
-		add_extra_ref(extra->name, extra->old_sha1, 0);
-
-	transport_disconnect(transport);
-
-	free(ref_git_copy);
-}
-
-static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest)
-{
-	struct dirent *de;
-	struct stat buf;
-	int src_len, dest_len;
-	DIR *dir;
-
-	dir = opendir(src->buf);
-	if (!dir)
-		die_errno("failed to open '%s'", src->buf);
-
-	if (mkdir(dest->buf, 0777)) {
-		if (errno != EEXIST)
-			die_errno("failed to create directory '%s'", dest->buf);
-		else if (stat(dest->buf, &buf))
-			die_errno("failed to stat '%s'", dest->buf);
-		else if (!S_ISDIR(buf.st_mode))
-			die("%s exists and is not a directory", dest->buf);
-	}
-
-	strbuf_addch(src, '/');
-	src_len = src->len;
-	strbuf_addch(dest, '/');
-	dest_len = dest->len;
-
-	while ((de = readdir(dir)) != NULL) {
-		strbuf_setlen(src, src_len);
-		strbuf_addstr(src, de->d_name);
-		strbuf_setlen(dest, dest_len);
-		strbuf_addstr(dest, de->d_name);
-		if (stat(src->buf, &buf)) {
-			warning ("failed to stat %s\n", src->buf);
-			continue;
-		}
-		if (S_ISDIR(buf.st_mode)) {
-			if (de->d_name[0] != '.')
-				copy_or_link_directory(src, dest);
-			continue;
-		}
-
-		if (unlink(dest->buf) && errno != ENOENT)
-			die_errno("failed to unlink '%s'", dest->buf);
-		if (!option_no_hardlinks) {
-			if (!link(src->buf, dest->buf))
-				continue;
-			if (option_local)
-				die_errno("failed to create link '%s'", dest->buf);
-			option_no_hardlinks = 1;
-		}
-		if (copy_file(dest->buf, src->buf, 0666))
-			die_errno("failed to copy file to '%s'", dest->buf);
-	}
-	closedir(dir);
-}
-
-static const struct ref *clone_local(const char *src_repo,
-				     const char *dest_repo)
-{
-	const struct ref *ret;
-	struct strbuf src = STRBUF_INIT;
-	struct strbuf dest = STRBUF_INIT;
-	struct remote *remote;
-	struct transport *transport;
-
-	if (option_shared)
-		add_to_alternates_file(src_repo);
-	else {
-		strbuf_addf(&src, "%s/objects", src_repo);
-		strbuf_addf(&dest, "%s/objects", dest_repo);
-		copy_or_link_directory(&src, &dest);
-		strbuf_release(&src);
-		strbuf_release(&dest);
-	}
-
-	remote = remote_get(src_repo);
-	transport = transport_get(remote, src_repo);
-	ret = transport_get_remote_refs(transport);
-	transport_disconnect(transport);
-	return ret;
-}
-
-static const char *junk_work_tree;
-static const char *junk_git_dir;
-static pid_t junk_pid;
-
-static void remove_junk(void)
-{
-	struct strbuf sb = STRBUF_INIT;
-	if (getpid() != junk_pid)
-		return;
-	if (junk_git_dir) {
-		strbuf_addstr(&sb, junk_git_dir);
-		remove_dir_recursively(&sb, 0);
-		strbuf_reset(&sb);
-	}
-	if (junk_work_tree) {
-		strbuf_addstr(&sb, junk_work_tree);
-		remove_dir_recursively(&sb, 0);
-		strbuf_reset(&sb);
-	}
-}
-
-static void remove_junk_on_signal(int signo)
-{
-	remove_junk();
-	sigchain_pop(signo);
-	raise(signo);
-}
-
-static struct ref *write_remote_refs(const struct ref *refs,
-		struct refspec *refspec, const char *reflog)
-{
-	struct ref *local_refs = NULL;
-	struct ref **tail = &local_refs;
-	struct ref *r;
-
-	get_fetch_map(refs, refspec, &tail, 0);
-	if (!option_mirror)
-		get_fetch_map(refs, tag_refspec, &tail, 0);
-
-	for (r = local_refs; r; r = r->next)
-		add_extra_ref(r->peer_ref->name, r->old_sha1, 0);
-
-	pack_refs(PACK_REFS_ALL);
-	clear_extra_refs();
-
-	return local_refs;
-}
-
-int cmd_clone(int argc, const char **argv, const char *prefix)
-{
-	int is_bundle = 0;
-	struct stat buf;
-	const char *repo_name, *repo, *work_tree, *git_dir;
-	char *path, *dir;
-	int dest_exists;
-	const struct ref *refs, *head_points_at, *remote_head, *mapped_refs;
-	struct strbuf key = STRBUF_INIT, value = STRBUF_INIT;
-	struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;
-	struct transport *transport = NULL;
-	char *src_ref_prefix = "refs/heads/";
-	int err = 0;
-
-	struct refspec *refspec;
-	const char *fetch_pattern;
-
-	junk_pid = getpid();
-
-	argc = parse_options(argc, argv, prefix, builtin_clone_options,
-			     builtin_clone_usage, 0);
-
-	if (argc == 0)
-		die("You must specify a repository to clone.");
-
-	if (option_mirror)
-		option_bare = 1;
-
-	if (option_bare) {
-		if (option_origin)
-			die("--bare and --origin %s options are incompatible.",
-			    option_origin);
-		option_no_checkout = 1;
-	}
-
-	if (!option_origin)
-		option_origin = "origin";
-
-	repo_name = argv[0];
-
-	path = get_repo_path(repo_name, &is_bundle);
-	if (path)
-		repo = xstrdup(make_nonrelative_path(repo_name));
-	else if (!strchr(repo_name, ':'))
-		repo = xstrdup(make_absolute_path(repo_name));
-	else
-		repo = repo_name;
-
-	if (argc == 2)
-		dir = xstrdup(argv[1]);
-	else
-		dir = guess_dir_name(repo_name, is_bundle, option_bare);
-	strip_trailing_slashes(dir);
-
-	dest_exists = !stat(dir, &buf);
-	if (dest_exists && !is_empty_dir(dir))
-		die("destination path '%s' already exists and is not "
-			"an empty directory.", dir);
-
-	strbuf_addf(&reflog_msg, "clone: from %s", repo);
-
-	if (option_bare)
-		work_tree = NULL;
-	else {
-		work_tree = getenv("GIT_WORK_TREE");
-		if (work_tree && !stat(work_tree, &buf))
-			die("working tree '%s' already exists.", work_tree);
-	}
-
-	if (option_bare || work_tree)
-		git_dir = xstrdup(dir);
-	else {
-		work_tree = dir;
-		git_dir = xstrdup(mkpath("%s/.git", dir));
-	}
-
-	if (!option_bare) {
-		junk_work_tree = work_tree;
-		if (safe_create_leading_directories_const(work_tree) < 0)
-			die_errno("could not create leading directories of '%s'",
-				  work_tree);
-		if (!dest_exists && mkdir(work_tree, 0755))
-			die_errno("could not create work tree dir '%s'.",
-				  work_tree);
-		set_git_work_tree(work_tree);
-	}
-	junk_git_dir = git_dir;
-	atexit(remove_junk);
-	sigchain_push_common(remove_junk_on_signal);
-
-	setenv(CONFIG_ENVIRONMENT, mkpath("%s/config", git_dir), 1);
-
-	if (safe_create_leading_directories_const(git_dir) < 0)
-		die("could not create leading directories of '%s'", git_dir);
-	set_git_dir(make_absolute_path(git_dir));
-
-	init_db(option_template, option_quiet ? INIT_DB_QUIET : 0);
-
-	/*
-	 * At this point, the config exists, so we do not need the
-	 * environment variable.  We actually need to unset it, too, to
-	 * re-enable parsing of the global configs.
-	 */
-	unsetenv(CONFIG_ENVIRONMENT);
-
-	if (option_reference)
-		setup_reference(git_dir);
-
-	git_config(git_default_config, NULL);
-
-	if (option_bare) {
-		if (option_mirror)
-			src_ref_prefix = "refs/";
-		strbuf_addstr(&branch_top, src_ref_prefix);
-
-		git_config_set("core.bare", "true");
-	} else {
-		strbuf_addf(&branch_top, "refs/remotes/%s/", option_origin);
-	}
-
-	strbuf_addf(&value, "+%s*:%s*", src_ref_prefix, branch_top.buf);
-
-	if (option_mirror || !option_bare) {
-		/* Configure the remote */
-		strbuf_addf(&key, "remote.%s.fetch", option_origin);
-		git_config_set_multivar(key.buf, value.buf, "^$", 0);
-		strbuf_reset(&key);
-
-		if (option_mirror) {
-			strbuf_addf(&key, "remote.%s.mirror", option_origin);
-			git_config_set(key.buf, "true");
-			strbuf_reset(&key);
-		}
-
-		strbuf_addf(&key, "remote.%s.url", option_origin);
-		git_config_set(key.buf, repo);
-		strbuf_reset(&key);
-	}
-
-	fetch_pattern = value.buf;
-	refspec = parse_fetch_refspec(1, &fetch_pattern);
-
-	strbuf_reset(&value);
-
-	if (path && !is_bundle)
-		refs = clone_local(path, git_dir);
-	else {
-		struct remote *remote = remote_get(argv[0]);
-		transport = transport_get(remote, remote->url[0]);
-
-		if (!transport->get_refs_list || !transport->fetch)
-			die("Don't know how to clone %s", transport->url);
-
-		transport_set_option(transport, TRANS_OPT_KEEP, "yes");
-
-		if (option_depth)
-			transport_set_option(transport, TRANS_OPT_DEPTH,
-					     option_depth);
-
-		if (option_quiet)
-			transport->verbose = -1;
-		else if (option_verbose)
-			transport->progress = 1;
-
-		if (option_upload_pack)
-			transport_set_option(transport, TRANS_OPT_UPLOADPACK,
-					     option_upload_pack);
-
-		refs = transport_get_remote_refs(transport);
-		if(refs)
-			transport_fetch_refs(transport, refs);
-	}
-
-	if (refs) {
-		clear_extra_refs();
-
-		mapped_refs = write_remote_refs(refs, refspec, reflog_msg.buf);
-
-		remote_head = find_ref_by_name(refs, "HEAD");
-		head_points_at = guess_remote_head(remote_head, mapped_refs, 0);
-	}
-	else {
-		warning("You appear to have cloned an empty repository.");
-		head_points_at = NULL;
-		remote_head = NULL;
-		option_no_checkout = 1;
-		if (!option_bare)
-			install_branch_config(0, "master", option_origin,
-					      "refs/heads/master");
-	}
-
-	if (head_points_at) {
-		/* Local default branch link */
-		create_symref("HEAD", head_points_at->name, NULL);
-
-		if (!option_bare) {
-			struct strbuf head_ref = STRBUF_INIT;
-			const char *head = head_points_at->name;
-
-			if (!prefixcmp(head, "refs/heads/"))
-				head += 11;
-
-			/* Set up the initial local branch */
-
-			/* Local branch initial value */
-			update_ref(reflog_msg.buf, "HEAD",
-				   head_points_at->old_sha1,
-				   NULL, 0, DIE_ON_ERR);
-
-			strbuf_addstr(&head_ref, branch_top.buf);
-			strbuf_addstr(&head_ref, "HEAD");
-
-			/* Remote branch link */
-			create_symref(head_ref.buf,
-				      head_points_at->peer_ref->name,
-				      reflog_msg.buf);
-
-			install_branch_config(0, head, option_origin,
-					      head_points_at->name);
-		}
-	} else if (remote_head) {
-		/* Source had detached HEAD pointing somewhere. */
-		if (!option_bare)
-			update_ref(reflog_msg.buf, "HEAD",
-				   remote_head->old_sha1,
-				   NULL, REF_NODEREF, DIE_ON_ERR);
-	} else {
-		/* Nothing to checkout out */
-		if (!option_no_checkout)
-			warning("remote HEAD refers to nonexistent ref, "
-				"unable to checkout.\n");
-		option_no_checkout = 1;
-	}
-
-	if (transport)
-		transport_unlock_pack(transport);
-
-	if (!option_no_checkout) {
-		struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
-		struct unpack_trees_options opts;
-		struct tree *tree;
-		struct tree_desc t;
-		int fd;
-
-		/* We need to be in the new work tree for the checkout */
-		setup_work_tree();
-
-		fd = hold_locked_index(lock_file, 1);
-
-		memset(&opts, 0, sizeof opts);
-		opts.update = 1;
-		opts.merge = 1;
-		opts.fn = oneway_merge;
-		opts.verbose_update = !option_quiet;
-		opts.src_index = &the_index;
-		opts.dst_index = &the_index;
-
-		tree = parse_tree_indirect(remote_head->old_sha1);
-		parse_tree(tree);
-		init_tree_desc(&t, tree->buffer, tree->size);
-		unpack_trees(1, &t, &opts);
-
-		if (write_cache(fd, active_cache, active_nr) ||
-		    commit_locked_index(lock_file))
-			die("unable to write new index file");
-
-		err |= run_hook(NULL, "post-checkout", sha1_to_hex(null_sha1),
-				sha1_to_hex(remote_head->old_sha1), "1", NULL);
-	}
-
-	strbuf_release(&reflog_msg);
-	strbuf_release(&branch_top);
-	strbuf_release(&key);
-	strbuf_release(&value);
-	junk_pid = 0;
-	return err;
-}
diff --git a/builtin-commit-tree.c b/builtin-commit-tree.c
deleted file mode 100644
index 6467077..0000000
--- a/builtin-commit-tree.c
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * GIT - The information manager from hell
- *
- * Copyright (C) Linus Torvalds, 2005
- */
-#include "cache.h"
-#include "commit.h"
-#include "tree.h"
-#include "builtin.h"
-#include "utf8.h"
-
-#define BLOCKING (1ul << 14)
-
-/*
- * FIXME! Share the code with "write-tree.c"
- */
-static void check_valid(unsigned char *sha1, enum object_type expect)
-{
-	enum object_type type = sha1_object_info(sha1, NULL);
-	if (type < 0)
-		die("%s is not a valid object", sha1_to_hex(sha1));
-	if (type != expect)
-		die("%s is not a valid '%s' object", sha1_to_hex(sha1),
-		    typename(expect));
-}
-
-static const char commit_tree_usage[] = "git commit-tree <sha1> [-p <sha1>]* < changelog";
-
-static void new_parent(struct commit *parent, struct commit_list **parents_p)
-{
-	unsigned char *sha1 = parent->object.sha1;
-	struct commit_list *parents;
-	for (parents = *parents_p; parents; parents = parents->next) {
-		if (parents->item == parent) {
-			error("duplicate parent %s ignored", sha1_to_hex(sha1));
-			return;
-		}
-		parents_p = &parents->next;
-	}
-	commit_list_insert(parent, parents_p);
-}
-
-static const char commit_utf8_warn[] =
-"Warning: commit message does not conform to UTF-8.\n"
-"You may want to amend it after fixing the message, or set the config\n"
-"variable i18n.commitencoding to the encoding your project uses.\n";
-
-int commit_tree(const char *msg, unsigned char *tree,
-		struct commit_list *parents, unsigned char *ret,
-		const char *author)
-{
-	int result;
-	int encoding_is_utf8;
-	struct strbuf buffer;
-
-	check_valid(tree, OBJ_TREE);
-
-	/* Not having i18n.commitencoding is the same as having utf-8 */
-	encoding_is_utf8 = is_encoding_utf8(git_commit_encoding);
-
-	strbuf_init(&buffer, 8192); /* should avoid reallocs for the headers */
-	strbuf_addf(&buffer, "tree %s\n", sha1_to_hex(tree));
-
-	/*
-	 * NOTE! This ordering means that the same exact tree merged with a
-	 * different order of parents will be a _different_ changeset even
-	 * if everything else stays the same.
-	 */
-	while (parents) {
-		struct commit_list *next = parents->next;
-		strbuf_addf(&buffer, "parent %s\n",
-			sha1_to_hex(parents->item->object.sha1));
-		free(parents);
-		parents = next;
-	}
-
-	/* Person/date information */
-	if (!author)
-		author = git_author_info(IDENT_ERROR_ON_NO_NAME);
-	strbuf_addf(&buffer, "author %s\n", author);
-	strbuf_addf(&buffer, "committer %s\n", git_committer_info(IDENT_ERROR_ON_NO_NAME));
-	if (!encoding_is_utf8)
-		strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding);
-	strbuf_addch(&buffer, '\n');
-
-	/* And add the comment */
-	strbuf_addstr(&buffer, msg);
-
-	/* And check the encoding */
-	if (encoding_is_utf8 && !is_utf8(buffer.buf))
-		fprintf(stderr, commit_utf8_warn);
-
-	result = write_sha1_file(buffer.buf, buffer.len, commit_type, ret);
-	strbuf_release(&buffer);
-	return result;
-}
-
-int cmd_commit_tree(int argc, const char **argv, const char *prefix)
-{
-	int i;
-	struct commit_list *parents = NULL;
-	unsigned char tree_sha1[20];
-	unsigned char commit_sha1[20];
-	struct strbuf buffer = STRBUF_INIT;
-
-	git_config(git_default_config, NULL);
-
-	if (argc < 2)
-		usage(commit_tree_usage);
-	if (get_sha1(argv[1], tree_sha1))
-		die("Not a valid object name %s", argv[1]);
-
-	for (i = 2; i < argc; i += 2) {
-		unsigned char sha1[20];
-		const char *a, *b;
-		a = argv[i]; b = argv[i+1];
-		if (!b || strcmp(a, "-p"))
-			usage(commit_tree_usage);
-
-		if (get_sha1(b, sha1))
-			die("Not a valid object name %s", b);
-		check_valid(sha1, OBJ_COMMIT);
-		new_parent(lookup_commit(sha1), &parents);
-	}
-
-	if (strbuf_read(&buffer, 0, 0) < 0)
-		die_errno("git commit-tree: failed to read");
-
-	if (!commit_tree(buffer.buf, tree_sha1, parents, commit_sha1, NULL)) {
-		printf("%s\n", sha1_to_hex(commit_sha1));
-		return 0;
-	}
-	else
-		return 1;
-}
diff --git a/builtin-commit.c b/builtin-commit.c
deleted file mode 100644
index 4bcce06..0000000
--- a/builtin-commit.c
+++ /dev/null
@@ -1,1031 +0,0 @@
-/*
- * Builtin "git commit"
- *
- * Copyright (c) 2007 Kristian Høgsberg <krh@redhat.com>
- * Based on git-commit.sh by Junio C Hamano and Linus Torvalds
- */
-
-#include "cache.h"
-#include "cache-tree.h"
-#include "color.h"
-#include "dir.h"
-#include "builtin.h"
-#include "diff.h"
-#include "diffcore.h"
-#include "commit.h"
-#include "revision.h"
-#include "wt-status.h"
-#include "run-command.h"
-#include "refs.h"
-#include "log-tree.h"
-#include "strbuf.h"
-#include "utf8.h"
-#include "parse-options.h"
-#include "string-list.h"
-#include "rerere.h"
-#include "unpack-trees.h"
-
-static const char * const builtin_commit_usage[] = {
-	"git commit [options] [--] <filepattern>...",
-	NULL
-};
-
-static const char * const builtin_status_usage[] = {
-	"git status [options] [--] <filepattern>...",
-	NULL
-};
-
-static unsigned char head_sha1[20], merge_head_sha1[20];
-static char *use_message_buffer;
-static const char commit_editmsg[] = "COMMIT_EDITMSG";
-static struct lock_file index_lock; /* real index */
-static struct lock_file false_lock; /* used only for partial commits */
-static enum {
-	COMMIT_AS_IS = 1,
-	COMMIT_NORMAL,
-	COMMIT_PARTIAL,
-} commit_style;
-
-static const char *logfile, *force_author;
-static const char *template_file;
-static char *edit_message, *use_message;
-static char *author_name, *author_email, *author_date;
-static int all, edit_flag, also, interactive, only, amend, signoff;
-static int quiet, verbose, no_verify, allow_empty;
-static char *untracked_files_arg;
-/*
- * The default commit message cleanup mode will remove the lines
- * beginning with # (shell comments) and leading and trailing
- * whitespaces (empty lines or containing only whitespaces)
- * if editor is used, and only the whitespaces if the message
- * is specified explicitly.
- */
-static enum {
-	CLEANUP_SPACE,
-	CLEANUP_NONE,
-	CLEANUP_ALL,
-} cleanup_mode;
-static char *cleanup_arg;
-
-static int use_editor = 1, initial_commit, in_merge;
-static const char *only_include_assumed;
-static struct strbuf message;
-
-static int opt_parse_m(const struct option *opt, const char *arg, int unset)
-{
-	struct strbuf *buf = opt->value;
-	if (unset)
-		strbuf_setlen(buf, 0);
-	else {
-		strbuf_addstr(buf, arg);
-		strbuf_addstr(buf, "\n\n");
-	}
-	return 0;
-}
-
-static struct option builtin_commit_options[] = {
-	OPT__QUIET(&quiet),
-	OPT__VERBOSE(&verbose),
-	OPT_GROUP("Commit message options"),
-
-	OPT_FILENAME('F', "file", &logfile, "read log from file"),
-	OPT_STRING(0, "author", &force_author, "AUTHOR", "override author for commit"),
-	OPT_CALLBACK('m', "message", &message, "MESSAGE", "specify commit message", opt_parse_m),
-	OPT_STRING('c', "reedit-message", &edit_message, "COMMIT", "reuse and edit message from specified commit "),
-	OPT_STRING('C', "reuse-message", &use_message, "COMMIT", "reuse message from specified commit"),
-	OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
-	OPT_FILENAME('t', "template", &template_file, "use specified template file"),
-	OPT_BOOLEAN('e', "edit", &edit_flag, "force edit of commit"),
-
-	OPT_GROUP("Commit contents options"),
-	OPT_BOOLEAN('a', "all", &all, "commit all changed files"),
-	OPT_BOOLEAN('i', "include", &also, "add specified files to index for commit"),
-	OPT_BOOLEAN(0, "interactive", &interactive, "interactively add files"),
-	OPT_BOOLEAN('o', "only", &only, "commit only specified files"),
-	OPT_BOOLEAN('n', "no-verify", &no_verify, "bypass pre-commit hook"),
-	OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
-	{ OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
-	OPT_BOOLEAN(0, "allow-empty", &allow_empty, "ok to record an empty change"),
-	OPT_STRING(0, "cleanup", &cleanup_arg, "default", "how to strip spaces and #comments from message"),
-
-	OPT_END()
-};
-
-static void rollback_index_files(void)
-{
-	switch (commit_style) {
-	case COMMIT_AS_IS:
-		break; /* nothing to do */
-	case COMMIT_NORMAL:
-		rollback_lock_file(&index_lock);
-		break;
-	case COMMIT_PARTIAL:
-		rollback_lock_file(&index_lock);
-		rollback_lock_file(&false_lock);
-		break;
-	}
-}
-
-static int commit_index_files(void)
-{
-	int err = 0;
-
-	switch (commit_style) {
-	case COMMIT_AS_IS:
-		break; /* nothing to do */
-	case COMMIT_NORMAL:
-		err = commit_lock_file(&index_lock);
-		break;
-	case COMMIT_PARTIAL:
-		err = commit_lock_file(&index_lock);
-		rollback_lock_file(&false_lock);
-		break;
-	}
-
-	return err;
-}
-
-/*
- * Take a union of paths in the index and the named tree (typically, "HEAD"),
- * and return the paths that match the given pattern in list.
- */
-static int list_paths(struct string_list *list, const char *with_tree,
-		      const char *prefix, const char **pattern)
-{
-	int i;
-	char *m;
-
-	for (i = 0; pattern[i]; i++)
-		;
-	m = xcalloc(1, i);
-
-	if (with_tree)
-		overlay_tree_on_cache(with_tree, prefix);
-
-	for (i = 0; i < active_nr; i++) {
-		struct cache_entry *ce = active_cache[i];
-		if (ce->ce_flags & CE_UPDATE)
-			continue;
-		if (!match_pathspec(pattern, ce->name, ce_namelen(ce), 0, m))
-			continue;
-		string_list_insert(ce->name, list);
-	}
-
-	return report_path_error(m, pattern, prefix ? strlen(prefix) : 0);
-}
-
-static void add_remove_files(struct string_list *list)
-{
-	int i;
-	for (i = 0; i < list->nr; i++) {
-		struct stat st;
-		struct string_list_item *p = &(list->items[i]);
-
-		if (!lstat(p->string, &st)) {
-			if (add_to_cache(p->string, &st, 0))
-				die("updating files failed");
-		} else
-			remove_file_from_cache(p->string);
-	}
-}
-
-static void create_base_index(void)
-{
-	struct tree *tree;
-	struct unpack_trees_options opts;
-	struct tree_desc t;
-
-	if (initial_commit) {
-		discard_cache();
-		return;
-	}
-
-	memset(&opts, 0, sizeof(opts));
-	opts.head_idx = 1;
-	opts.index_only = 1;
-	opts.merge = 1;
-	opts.src_index = &the_index;
-	opts.dst_index = &the_index;
-
-	opts.fn = oneway_merge;
-	tree = parse_tree_indirect(head_sha1);
-	if (!tree)
-		die("failed to unpack HEAD tree object");
-	parse_tree(tree);
-	init_tree_desc(&t, tree->buffer, tree->size);
-	if (unpack_trees(1, &t, &opts))
-		exit(128); /* We've already reported the error, finish dying */
-}
-
-static char *prepare_index(int argc, const char **argv, const char *prefix)
-{
-	int fd;
-	struct string_list partial;
-	const char **pathspec = NULL;
-
-	if (interactive) {
-		if (interactive_add(argc, argv, prefix) != 0)
-			die("interactive add failed");
-		if (read_cache_preload(NULL) < 0)
-			die("index file corrupt");
-		commit_style = COMMIT_AS_IS;
-		return get_index_file();
-	}
-
-	if (*argv)
-		pathspec = get_pathspec(prefix, argv);
-
-	if (read_cache_preload(pathspec) < 0)
-		die("index file corrupt");
-
-	/*
-	 * Non partial, non as-is commit.
-	 *
-	 * (1) get the real index;
-	 * (2) update the_index as necessary;
-	 * (3) write the_index out to the real index (still locked);
-	 * (4) return the name of the locked index file.
-	 *
-	 * The caller should run hooks on the locked real index, and
-	 * (A) if all goes well, commit the real index;
-	 * (B) on failure, rollback the real index.
-	 */
-	if (all || (also && pathspec && *pathspec)) {
-		int fd = hold_locked_index(&index_lock, 1);
-		add_files_to_cache(also ? prefix : NULL, pathspec, 0);
-		refresh_cache(REFRESH_QUIET);
-		if (write_cache(fd, active_cache, active_nr) ||
-		    close_lock_file(&index_lock))
-			die("unable to write new_index file");
-		commit_style = COMMIT_NORMAL;
-		return index_lock.filename;
-	}
-
-	/*
-	 * As-is commit.
-	 *
-	 * (1) return the name of the real index file.
-	 *
-	 * The caller should run hooks on the real index, and run
-	 * hooks on the real index, and create commit from the_index.
-	 * We still need to refresh the index here.
-	 */
-	if (!pathspec || !*pathspec) {
-		fd = hold_locked_index(&index_lock, 1);
-		refresh_cache(REFRESH_QUIET);
-		if (write_cache(fd, active_cache, active_nr) ||
-		    commit_locked_index(&index_lock))
-			die("unable to write new_index file");
-		commit_style = COMMIT_AS_IS;
-		return get_index_file();
-	}
-
-	/*
-	 * A partial commit.
-	 *
-	 * (0) find the set of affected paths;
-	 * (1) get lock on the real index file;
-	 * (2) update the_index with the given paths;
-	 * (3) write the_index out to the real index (still locked);
-	 * (4) get lock on the false index file;
-	 * (5) reset the_index from HEAD;
-	 * (6) update the_index the same way as (2);
-	 * (7) write the_index out to the false index file;
-	 * (8) return the name of the false index file (still locked);
-	 *
-	 * The caller should run hooks on the locked false index, and
-	 * create commit from it.  Then
-	 * (A) if all goes well, commit the real index;
-	 * (B) on failure, rollback the real index;
-	 * In either case, rollback the false index.
-	 */
-	commit_style = COMMIT_PARTIAL;
-
-	if (file_exists(git_path("MERGE_HEAD")))
-		die("cannot do a partial commit during a merge.");
-
-	memset(&partial, 0, sizeof(partial));
-	partial.strdup_strings = 1;
-	if (list_paths(&partial, initial_commit ? NULL : "HEAD", prefix, pathspec))
-		exit(1);
-
-	discard_cache();
-	if (read_cache() < 0)
-		die("cannot read the index");
-
-	fd = hold_locked_index(&index_lock, 1);
-	add_remove_files(&partial);
-	refresh_cache(REFRESH_QUIET);
-	if (write_cache(fd, active_cache, active_nr) ||
-	    close_lock_file(&index_lock))
-		die("unable to write new_index file");
-
-	fd = hold_lock_file_for_update(&false_lock,
-				       git_path("next-index-%"PRIuMAX,
-						(uintmax_t) getpid()),
-				       LOCK_DIE_ON_ERROR);
-
-	create_base_index();
-	add_remove_files(&partial);
-	refresh_cache(REFRESH_QUIET);
-
-	if (write_cache(fd, active_cache, active_nr) ||
-	    close_lock_file(&false_lock))
-		die("unable to write temporary index file");
-
-	discard_cache();
-	read_cache_from(false_lock.filename);
-
-	return false_lock.filename;
-}
-
-static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn)
-{
-	struct wt_status s;
-
-	wt_status_prepare(&s);
-	if (wt_status_relative_paths)
-		s.prefix = prefix;
-
-	if (amend) {
-		s.amend = 1;
-		s.reference = "HEAD^1";
-	}
-	s.verbose = verbose;
-	s.untracked = (show_untracked_files == SHOW_ALL_UNTRACKED_FILES);
-	s.index_file = index_file;
-	s.fp = fp;
-	s.nowarn = nowarn;
-
-	wt_status_print(&s);
-
-	return s.commitable;
-}
-
-static int is_a_merge(const unsigned char *sha1)
-{
-	struct commit *commit = lookup_commit(sha1);
-	if (!commit || parse_commit(commit))
-		die("could not parse HEAD commit");
-	return !!(commit->parents && commit->parents->next);
-}
-
-static const char sign_off_header[] = "Signed-off-by: ";
-
-static void determine_author_info(void)
-{
-	char *name, *email, *date;
-
-	name = getenv("GIT_AUTHOR_NAME");
-	email = getenv("GIT_AUTHOR_EMAIL");
-	date = getenv("GIT_AUTHOR_DATE");
-
-	if (use_message) {
-		const char *a, *lb, *rb, *eol;
-
-		a = strstr(use_message_buffer, "\nauthor ");
-		if (!a)
-			die("invalid commit: %s", use_message);
-
-		lb = strstr(a + 8, " <");
-		rb = strstr(a + 8, "> ");
-		eol = strchr(a + 8, '\n');
-		if (!lb || !rb || !eol)
-			die("invalid commit: %s", use_message);
-
-		name = xstrndup(a + 8, lb - (a + 8));
-		email = xstrndup(lb + 2, rb - (lb + 2));
-		date = xstrndup(rb + 2, eol - (rb + 2));
-	}
-
-	if (force_author) {
-		const char *lb = strstr(force_author, " <");
-		const char *rb = strchr(force_author, '>');
-
-		if (!lb || !rb)
-			die("malformed --author parameter");
-		name = xstrndup(force_author, lb - force_author);
-		email = xstrndup(lb + 2, rb - (lb + 2));
-	}
-
-	author_name = name;
-	author_email = email;
-	author_date = date;
-}
-
-static int prepare_to_commit(const char *index_file, const char *prefix)
-{
-	struct stat statbuf;
-	int commitable, saved_color_setting;
-	struct strbuf sb = STRBUF_INIT;
-	char *buffer;
-	FILE *fp;
-	const char *hook_arg1 = NULL;
-	const char *hook_arg2 = NULL;
-	int ident_shown = 0;
-
-	if (!no_verify && run_hook(index_file, "pre-commit", NULL))
-		return 0;
-
-	if (message.len) {
-		strbuf_addbuf(&sb, &message);
-		hook_arg1 = "message";
-	} else if (logfile && !strcmp(logfile, "-")) {
-		if (isatty(0))
-			fprintf(stderr, "(reading log message from standard input)\n");
-		if (strbuf_read(&sb, 0, 0) < 0)
-			die_errno("could not read log from standard input");
-		hook_arg1 = "message";
-	} else if (logfile) {
-		if (strbuf_read_file(&sb, logfile, 0) < 0)
-			die_errno("could not read log file '%s'",
-				  logfile);
-		hook_arg1 = "message";
-	} else if (use_message) {
-		buffer = strstr(use_message_buffer, "\n\n");
-		if (!buffer || buffer[2] == '\0')
-			die("commit has empty message");
-		strbuf_add(&sb, buffer + 2, strlen(buffer + 2));
-		hook_arg1 = "commit";
-		hook_arg2 = use_message;
-	} else if (!stat(git_path("MERGE_MSG"), &statbuf)) {
-		if (strbuf_read_file(&sb, git_path("MERGE_MSG"), 0) < 0)
-			die_errno("could not read MERGE_MSG");
-		hook_arg1 = "merge";
-	} else if (!stat(git_path("SQUASH_MSG"), &statbuf)) {
-		if (strbuf_read_file(&sb, git_path("SQUASH_MSG"), 0) < 0)
-			die_errno("could not read SQUASH_MSG");
-		hook_arg1 = "squash";
-	} else if (template_file && !stat(template_file, &statbuf)) {
-		if (strbuf_read_file(&sb, template_file, 0) < 0)
-			die_errno("could not read '%s'", template_file);
-		hook_arg1 = "template";
-	}
-
-	/*
-	 * This final case does not modify the template message,
-	 * it just sets the argument to the prepare-commit-msg hook.
-	 */
-	else if (in_merge)
-		hook_arg1 = "merge";
-
-	fp = fopen(git_path(commit_editmsg), "w");
-	if (fp == NULL)
-		die_errno("could not open '%s'", git_path(commit_editmsg));
-
-	if (cleanup_mode != CLEANUP_NONE)
-		stripspace(&sb, 0);
-
-	if (signoff) {
-		struct strbuf sob = STRBUF_INIT;
-		int i;
-
-		strbuf_addstr(&sob, sign_off_header);
-		strbuf_addstr(&sob, fmt_name(getenv("GIT_COMMITTER_NAME"),
-					     getenv("GIT_COMMITTER_EMAIL")));
-		strbuf_addch(&sob, '\n');
-		for (i = sb.len - 1; i > 0 && sb.buf[i - 1] != '\n'; i--)
-			; /* do nothing */
-		if (prefixcmp(sb.buf + i, sob.buf)) {
-			if (prefixcmp(sb.buf + i, sign_off_header))
-				strbuf_addch(&sb, '\n');
-			strbuf_addbuf(&sb, &sob);
-		}
-		strbuf_release(&sob);
-	}
-
-	if (fwrite(sb.buf, 1, sb.len, fp) < sb.len)
-		die_errno("could not write commit template");
-
-	strbuf_release(&sb);
-
-	determine_author_info();
-
-	/* This checks if committer ident is explicitly given */
-	git_committer_info(0);
-	if (use_editor) {
-		char *author_ident;
-		const char *committer_ident;
-
-		if (in_merge)
-			fprintf(fp,
-				"#\n"
-				"# It looks like you may be committing a MERGE.\n"
-				"# If this is not correct, please remove the file\n"
-				"#	%s\n"
-				"# and try again.\n"
-				"#\n",
-				git_path("MERGE_HEAD"));
-
-		fprintf(fp,
-			"\n"
-			"# Please enter the commit message for your changes.");
-		if (cleanup_mode == CLEANUP_ALL)
-			fprintf(fp,
-				" Lines starting\n"
-				"# with '#' will be ignored, and an empty"
-				" message aborts the commit.\n");
-		else /* CLEANUP_SPACE, that is. */
-			fprintf(fp,
-				" Lines starting\n"
-				"# with '#' will be kept; you may remove them"
-				" yourself if you want to.\n"
-				"# An empty message aborts the commit.\n");
-		if (only_include_assumed)
-			fprintf(fp, "# %s\n", only_include_assumed);
-
-		author_ident = xstrdup(fmt_name(author_name, author_email));
-		committer_ident = fmt_name(getenv("GIT_COMMITTER_NAME"),
-					   getenv("GIT_COMMITTER_EMAIL"));
-		if (strcmp(author_ident, committer_ident))
-			fprintf(fp,
-				"%s"
-				"# Author:    %s\n",
-				ident_shown++ ? "" : "#\n",
-				author_ident);
-		free(author_ident);
-
-		if (!user_ident_explicitly_given)
-			fprintf(fp,
-				"%s"
-				"# Committer: %s\n",
-				ident_shown++ ? "" : "#\n",
-				committer_ident);
-
-		if (ident_shown)
-			fprintf(fp, "#\n");
-
-		saved_color_setting = wt_status_use_color;
-		wt_status_use_color = 0;
-		commitable = run_status(fp, index_file, prefix, 1);
-		wt_status_use_color = saved_color_setting;
-	} else {
-		unsigned char sha1[20];
-		const char *parent = "HEAD";
-
-		if (!active_nr && read_cache() < 0)
-			die("Cannot read index");
-
-		if (amend)
-			parent = "HEAD^1";
-
-		if (get_sha1(parent, sha1))
-			commitable = !!active_nr;
-		else
-			commitable = index_differs_from(parent, 0);
-	}
-
-	fclose(fp);
-
-	if (!commitable && !in_merge && !allow_empty &&
-	    !(amend && is_a_merge(head_sha1))) {
-		run_status(stdout, index_file, prefix, 0);
-		return 0;
-	}
-
-	/*
-	 * Re-read the index as pre-commit hook could have updated it,
-	 * and write it out as a tree.  We must do this before we invoke
-	 * the editor and after we invoke run_status above.
-	 */
-	discard_cache();
-	read_cache_from(index_file);
-	if (!active_cache_tree)
-		active_cache_tree = cache_tree();
-	if (cache_tree_update(active_cache_tree,
-			      active_cache, active_nr, 0, 0) < 0) {
-		error("Error building trees");
-		return 0;
-	}
-
-	if (run_hook(index_file, "prepare-commit-msg",
-		     git_path(commit_editmsg), hook_arg1, hook_arg2, NULL))
-		return 0;
-
-	if (use_editor) {
-		char index[PATH_MAX];
-		const char *env[2] = { index, NULL };
-		snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
-		if (launch_editor(git_path(commit_editmsg), NULL, env)) {
-			fprintf(stderr,
-			"Please supply the message using either -m or -F option.\n");
-			exit(1);
-		}
-	}
-
-	if (!no_verify &&
-	    run_hook(index_file, "commit-msg", git_path(commit_editmsg), NULL)) {
-		return 0;
-	}
-
-	return 1;
-}
-
-/*
- * Find out if the message in the strbuf contains only whitespace and
- * Signed-off-by lines.
- */
-static int message_is_empty(struct strbuf *sb)
-{
-	struct strbuf tmpl = STRBUF_INIT;
-	const char *nl;
-	int eol, i, start = 0;
-
-	if (cleanup_mode == CLEANUP_NONE && sb->len)
-		return 0;
-
-	/* See if the template is just a prefix of the message. */
-	if (template_file && strbuf_read_file(&tmpl, template_file, 0) > 0) {
-		stripspace(&tmpl, cleanup_mode == CLEANUP_ALL);
-		if (start + tmpl.len <= sb->len &&
-		    memcmp(tmpl.buf, sb->buf + start, tmpl.len) == 0)
-			start += tmpl.len;
-	}
-	strbuf_release(&tmpl);
-
-	/* Check if the rest is just whitespace and Signed-of-by's. */
-	for (i = start; i < sb->len; i++) {
-		nl = memchr(sb->buf + i, '\n', sb->len - i);
-		if (nl)
-			eol = nl - sb->buf;
-		else
-			eol = sb->len;
-
-		if (strlen(sign_off_header) <= eol - i &&
-		    !prefixcmp(sb->buf + i, sign_off_header)) {
-			i = eol;
-			continue;
-		}
-		while (i < eol)
-			if (!isspace(sb->buf[i++]))
-				return 0;
-	}
-
-	return 1;
-}
-
-static const char *find_author_by_nickname(const char *name)
-{
-	struct rev_info revs;
-	struct commit *commit;
-	struct strbuf buf = STRBUF_INIT;
-	const char *av[20];
-	int ac = 0;
-
-	init_revisions(&revs, NULL);
-	strbuf_addf(&buf, "--author=%s", name);
-	av[++ac] = "--all";
-	av[++ac] = "-i";
-	av[++ac] = buf.buf;
-	av[++ac] = NULL;
-	setup_revisions(ac, av, &revs, NULL);
-	prepare_revision_walk(&revs);
-	commit = get_revision(&revs);
-	if (commit) {
-		strbuf_release(&buf);
-		format_commit_message(commit, "%an <%ae>", &buf, DATE_NORMAL);
-		return strbuf_detach(&buf, NULL);
-	}
-	die("No existing author found with '%s'", name);
-}
-
-static int parse_and_validate_options(int argc, const char *argv[],
-				      const char * const usage[],
-				      const char *prefix)
-{
-	int f = 0;
-
-	argc = parse_options(argc, argv, prefix, builtin_commit_options, usage,
-			     0);
-
-	if (force_author && !strchr(force_author, '>'))
-		force_author = find_author_by_nickname(force_author);
-
-	if (logfile || message.len || use_message)
-		use_editor = 0;
-	if (edit_flag)
-		use_editor = 1;
-	if (!use_editor)
-		setenv("GIT_EDITOR", ":", 1);
-
-	if (get_sha1("HEAD", head_sha1))
-		initial_commit = 1;
-
-	if (!get_sha1("MERGE_HEAD", merge_head_sha1))
-		in_merge = 1;
-
-	/* Sanity check options */
-	if (amend && initial_commit)
-		die("You have nothing to amend.");
-	if (amend && in_merge)
-		die("You are in the middle of a merge -- cannot amend.");
-
-	if (use_message)
-		f++;
-	if (edit_message)
-		f++;
-	if (logfile)
-		f++;
-	if (f > 1)
-		die("Only one of -c/-C/-F can be used.");
-	if (message.len && f > 0)
-		die("Option -m cannot be combined with -c/-C/-F.");
-	if (edit_message)
-		use_message = edit_message;
-	if (amend && !use_message)
-		use_message = "HEAD";
-	if (use_message) {
-		unsigned char sha1[20];
-		static char utf8[] = "UTF-8";
-		const char *out_enc;
-		char *enc, *end;
-		struct commit *commit;
-
-		if (get_sha1(use_message, sha1))
-			die("could not lookup commit %s", use_message);
-		commit = lookup_commit_reference(sha1);
-		if (!commit || parse_commit(commit))
-			die("could not parse commit %s", use_message);
-
-		enc = strstr(commit->buffer, "\nencoding");
-		if (enc) {
-			end = strchr(enc + 10, '\n');
-			enc = xstrndup(enc + 10, end - (enc + 10));
-		} else {
-			enc = utf8;
-		}
-		out_enc = git_commit_encoding ? git_commit_encoding : utf8;
-
-		if (strcmp(out_enc, enc))
-			use_message_buffer =
-				reencode_string(commit->buffer, out_enc, enc);
-
-		/*
-		 * If we failed to reencode the buffer, just copy it
-		 * byte for byte so the user can try to fix it up.
-		 * This also handles the case where input and output
-		 * encodings are identical.
-		 */
-		if (use_message_buffer == NULL)
-			use_message_buffer = xstrdup(commit->buffer);
-		if (enc != utf8)
-			free(enc);
-	}
-
-	if (!!also + !!only + !!all + !!interactive > 1)
-		die("Only one of --include/--only/--all/--interactive can be used.");
-	if (argc == 0 && (also || (only && !amend)))
-		die("No paths with --include/--only does not make sense.");
-	if (argc == 0 && only && amend)
-		only_include_assumed = "Clever... amending the last one with dirty index.";
-	if (argc > 0 && !also && !only)
-		only_include_assumed = "Explicit paths specified without -i nor -o; assuming --only paths...";
-	if (!cleanup_arg || !strcmp(cleanup_arg, "default"))
-		cleanup_mode = use_editor ? CLEANUP_ALL : CLEANUP_SPACE;
-	else if (!strcmp(cleanup_arg, "verbatim"))
-		cleanup_mode = CLEANUP_NONE;
-	else if (!strcmp(cleanup_arg, "whitespace"))
-		cleanup_mode = CLEANUP_SPACE;
-	else if (!strcmp(cleanup_arg, "strip"))
-		cleanup_mode = CLEANUP_ALL;
-	else
-		die("Invalid cleanup mode %s", cleanup_arg);
-
-	if (!untracked_files_arg)
-		; /* default already initialized */
-	else if (!strcmp(untracked_files_arg, "no"))
-		show_untracked_files = SHOW_NO_UNTRACKED_FILES;
-	else if (!strcmp(untracked_files_arg, "normal"))
-		show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
-	else if (!strcmp(untracked_files_arg, "all"))
-		show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
-	else
-		die("Invalid untracked files mode '%s'", untracked_files_arg);
-
-	if (all && argc > 0)
-		die("Paths with -a does not make sense.");
-	else if (interactive && argc > 0)
-		die("Paths with --interactive does not make sense.");
-
-	return argc;
-}
-
-int cmd_status(int argc, const char **argv, const char *prefix)
-{
-	const char *index_file;
-	int commitable;
-
-	git_config(git_status_config, NULL);
-
-	if (wt_status_use_color == -1)
-		wt_status_use_color = git_use_color_default;
-
-	if (diff_use_color_default == -1)
-		diff_use_color_default = git_use_color_default;
-
-	argc = parse_and_validate_options(argc, argv, builtin_status_usage, prefix);
-
-	index_file = prepare_index(argc, argv, prefix);
-
-	commitable = run_status(stdout, index_file, prefix, 0);
-
-	rollback_index_files();
-
-	return commitable ? 0 : 1;
-}
-
-static void print_summary(const char *prefix, const unsigned char *sha1)
-{
-	struct rev_info rev;
-	struct commit *commit;
-	static const char *format = "format:%h] %s";
-	unsigned char junk_sha1[20];
-	const char *head = resolve_ref("HEAD", junk_sha1, 0, NULL);
-
-	commit = lookup_commit(sha1);
-	if (!commit)
-		die("couldn't look up newly created commit");
-	if (!commit || parse_commit(commit))
-		die("could not parse newly created commit");
-
-	init_revisions(&rev, prefix);
-	setup_revisions(0, NULL, &rev, NULL);
-
-	rev.abbrev = 0;
-	rev.diff = 1;
-	rev.diffopt.output_format =
-		DIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;
-
-	rev.verbose_header = 1;
-	rev.show_root_diff = 1;
-	get_commit_format(format, &rev);
-	rev.always_show_header = 0;
-	rev.diffopt.detect_rename = 1;
-	rev.diffopt.rename_limit = 100;
-	rev.diffopt.break_opt = 0;
-	diff_setup_done(&rev.diffopt);
-
-	printf("[%s%s ",
-		!prefixcmp(head, "refs/heads/") ?
-			head + 11 :
-			!strcmp(head, "HEAD") ?
-				"detached HEAD" :
-				head,
-		initial_commit ? " (root-commit)" : "");
-
-	if (!log_tree_commit(&rev, commit)) {
-		struct strbuf buf = STRBUF_INIT;
-		format_commit_message(commit, format + 7, &buf, DATE_NORMAL);
-		printf("%s\n", buf.buf);
-		strbuf_release(&buf);
-	}
-}
-
-static int git_commit_config(const char *k, const char *v, void *cb)
-{
-	if (!strcmp(k, "commit.template"))
-		return git_config_string(&template_file, k, v);
-
-	return git_status_config(k, v, cb);
-}
-
-int cmd_commit(int argc, const char **argv, const char *prefix)
-{
-	struct strbuf sb = STRBUF_INIT;
-	const char *index_file, *reflog_msg;
-	char *nl, *p;
-	unsigned char commit_sha1[20];
-	struct ref_lock *ref_lock;
-	struct commit_list *parents = NULL, **pptr = &parents;
-	struct stat statbuf;
-	int allow_fast_forward = 1;
-
-	git_config(git_commit_config, NULL);
-
-	if (wt_status_use_color == -1)
-		wt_status_use_color = git_use_color_default;
-
-	argc = parse_and_validate_options(argc, argv, builtin_commit_usage, prefix);
-
-	index_file = prepare_index(argc, argv, prefix);
-
-	/* Set up everything for writing the commit object.  This includes
-	   running hooks, writing the trees, and interacting with the user.  */
-	if (!prepare_to_commit(index_file, prefix)) {
-		rollback_index_files();
-		return 1;
-	}
-
-	/* Determine parents */
-	if (initial_commit) {
-		reflog_msg = "commit (initial)";
-	} else if (amend) {
-		struct commit_list *c;
-		struct commit *commit;
-
-		reflog_msg = "commit (amend)";
-		commit = lookup_commit(head_sha1);
-		if (!commit || parse_commit(commit))
-			die("could not parse HEAD commit");
-
-		for (c = commit->parents; c; c = c->next)
-			pptr = &commit_list_insert(c->item, pptr)->next;
-	} else if (in_merge) {
-		struct strbuf m = STRBUF_INIT;
-		FILE *fp;
-
-		reflog_msg = "commit (merge)";
-		pptr = &commit_list_insert(lookup_commit(head_sha1), pptr)->next;
-		fp = fopen(git_path("MERGE_HEAD"), "r");
-		if (fp == NULL)
-			die_errno("could not open '%s' for reading",
-				  git_path("MERGE_HEAD"));
-		while (strbuf_getline(&m, fp, '\n') != EOF) {
-			unsigned char sha1[20];
-			if (get_sha1_hex(m.buf, sha1) < 0)
-				die("Corrupt MERGE_HEAD file (%s)", m.buf);
-			pptr = &commit_list_insert(lookup_commit(sha1), pptr)->next;
-		}
-		fclose(fp);
-		strbuf_release(&m);
-		if (!stat(git_path("MERGE_MODE"), &statbuf)) {
-			if (strbuf_read_file(&sb, git_path("MERGE_MODE"), 0) < 0)
-				die_errno("could not read MERGE_MODE");
-			if (!strcmp(sb.buf, "no-ff"))
-				allow_fast_forward = 0;
-		}
-		if (allow_fast_forward)
-			parents = reduce_heads(parents);
-	} else {
-		reflog_msg = "commit";
-		pptr = &commit_list_insert(lookup_commit(head_sha1), pptr)->next;
-	}
-
-	/* Finally, get the commit message */
-	strbuf_reset(&sb);
-	if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) {
-		int saved_errno = errno;
-		rollback_index_files();
-		die("could not read commit message: %s", strerror(saved_errno));
-	}
-
-	/* Truncate the message just before the diff, if any. */
-	if (verbose) {
-		p = strstr(sb.buf, "\ndiff --git ");
-		if (p != NULL)
-			strbuf_setlen(&sb, p - sb.buf + 1);
-	}
-
-	if (cleanup_mode != CLEANUP_NONE)
-		stripspace(&sb, cleanup_mode == CLEANUP_ALL);
-	if (message_is_empty(&sb)) {
-		rollback_index_files();
-		fprintf(stderr, "Aborting commit due to empty commit message.\n");
-		exit(1);
-	}
-
-	if (commit_tree(sb.buf, active_cache_tree->sha1, parents, commit_sha1,
-			fmt_ident(author_name, author_email, author_date,
-				IDENT_ERROR_ON_NO_NAME))) {
-		rollback_index_files();
-		die("failed to write commit object");
-	}
-
-	ref_lock = lock_any_ref_for_update("HEAD",
-					   initial_commit ? NULL : head_sha1,
-					   0);
-
-	nl = strchr(sb.buf, '\n');
-	if (nl)
-		strbuf_setlen(&sb, nl + 1 - sb.buf);
-	else
-		strbuf_addch(&sb, '\n');
-	strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));
-	strbuf_insert(&sb, strlen(reflog_msg), ": ", 2);
-
-	if (!ref_lock) {
-		rollback_index_files();
-		die("cannot lock HEAD ref");
-	}
-	if (write_ref_sha1(ref_lock, commit_sha1, sb.buf) < 0) {
-		rollback_index_files();
-		die("cannot update HEAD ref");
-	}
-
-	unlink(git_path("MERGE_HEAD"));
-	unlink(git_path("MERGE_MSG"));
-	unlink(git_path("MERGE_MODE"));
-	unlink(git_path("SQUASH_MSG"));
-
-	if (commit_index_files())
-		die ("Repository has been updated, but unable to write\n"
-		     "new_index file. Check that disk is not full or quota is\n"
-		     "not exceeded, and then \"git reset HEAD\" to recover.");
-
-	rerere();
-	run_hook(get_index_file(), "post-commit", NULL);
-	if (!quiet)
-		print_summary(prefix, commit_sha1);
-
-	return 0;
-}
diff --git a/builtin-config.c b/builtin-config.c
deleted file mode 100644
index a2d656e..0000000
--- a/builtin-config.c
+++ /dev/null
@@ -1,478 +0,0 @@
-#include "builtin.h"
-#include "cache.h"
-#include "color.h"
-#include "parse-options.h"
-
-static const char *const builtin_config_usage[] = {
-	"git config [options]",
-	NULL
-};
-
-static char *key;
-static regex_t *key_regexp;
-static regex_t *regexp;
-static int show_keys;
-static int use_key_regexp;
-static int do_all;
-static int do_not_match;
-static int seen;
-static char delim = '=';
-static char key_delim = ' ';
-static char term = '\n';
-
-static int use_global_config, use_system_config;
-static const char *given_config_file;
-static int actions, types;
-static const char *get_color_slot, *get_colorbool_slot;
-static int end_null;
-
-#define ACTION_GET (1<<0)
-#define ACTION_GET_ALL (1<<1)
-#define ACTION_GET_REGEXP (1<<2)
-#define ACTION_REPLACE_ALL (1<<3)
-#define ACTION_ADD (1<<4)
-#define ACTION_UNSET (1<<5)
-#define ACTION_UNSET_ALL (1<<6)
-#define ACTION_RENAME_SECTION (1<<7)
-#define ACTION_REMOVE_SECTION (1<<8)
-#define ACTION_LIST (1<<9)
-#define ACTION_EDIT (1<<10)
-#define ACTION_SET (1<<11)
-#define ACTION_SET_ALL (1<<12)
-#define ACTION_GET_COLOR (1<<13)
-#define ACTION_GET_COLORBOOL (1<<14)
-
-#define TYPE_BOOL (1<<0)
-#define TYPE_INT (1<<1)
-#define TYPE_BOOL_OR_INT (1<<2)
-
-static struct option builtin_config_options[] = {
-	OPT_GROUP("Config file location"),
-	OPT_BOOLEAN(0, "global", &use_global_config, "use global config file"),
-	OPT_BOOLEAN(0, "system", &use_system_config, "use system config file"),
-	OPT_STRING('f', "file", &given_config_file, "FILE", "use given config file"),
-	OPT_GROUP("Action"),
-	OPT_BIT(0, "get", &actions, "get value: name [value-regex]", ACTION_GET),
-	OPT_BIT(0, "get-all", &actions, "get all values: key [value-regex]", ACTION_GET_ALL),
-	OPT_BIT(0, "get-regexp", &actions, "get values for regexp: name-regex [value-regex]", ACTION_GET_REGEXP),
-	OPT_BIT(0, "replace-all", &actions, "replace all matching variables: name value [value_regex]", ACTION_REPLACE_ALL),
-	OPT_BIT(0, "add", &actions, "adds a new variable: name value", ACTION_ADD),
-	OPT_BIT(0, "unset", &actions, "removes a variable: name [value-regex]", ACTION_UNSET),
-	OPT_BIT(0, "unset-all", &actions, "removes all matches: name [value-regex]", ACTION_UNSET_ALL),
-	OPT_BIT(0, "rename-section", &actions, "rename section: old-name new-name", ACTION_RENAME_SECTION),
-	OPT_BIT(0, "remove-section", &actions, "remove a section: name", ACTION_REMOVE_SECTION),
-	OPT_BIT('l', "list", &actions, "list all", ACTION_LIST),
-	OPT_BIT('e', "edit", &actions, "opens an editor", ACTION_EDIT),
-	OPT_STRING(0, "get-color", &get_color_slot, "slot", "find the color configured: [default]"),
-	OPT_STRING(0, "get-colorbool", &get_colorbool_slot, "slot", "find the color setting: [stdout-is-tty]"),
-	OPT_GROUP("Type"),
-	OPT_BIT(0, "bool", &types, "value is \"true\" or \"false\"", TYPE_BOOL),
-	OPT_BIT(0, "int", &types, "value is decimal number", TYPE_INT),
-	OPT_BIT(0, "bool-or-int", &types, "value is --bool or --int", TYPE_BOOL_OR_INT),
-	OPT_GROUP("Other"),
-	OPT_BOOLEAN('z', "null", &end_null, "terminate values with NUL byte"),
-	OPT_END(),
-};
-
-static void check_argc(int argc, int min, int max) {
-	if (argc >= min && argc <= max)
-		return;
-	error("wrong number of arguments");
-	usage_with_options(builtin_config_usage, builtin_config_options);
-}
-
-static int show_all_config(const char *key_, const char *value_, void *cb)
-{
-	if (value_)
-		printf("%s%c%s%c", key_, delim, value_, term);
-	else
-		printf("%s%c", key_, term);
-	return 0;
-}
-
-static int show_config(const char *key_, const char *value_, void *cb)
-{
-	char value[256];
-	const char *vptr = value;
-	int dup_error = 0;
-
-	if (!use_key_regexp && strcmp(key_, key))
-		return 0;
-	if (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0))
-		return 0;
-	if (regexp != NULL &&
-	    (do_not_match ^ !!regexec(regexp, (value_?value_:""), 0, NULL, 0)))
-		return 0;
-
-	if (show_keys) {
-		if (value_)
-			printf("%s%c", key_, key_delim);
-		else
-			printf("%s", key_);
-	}
-	if (seen && !do_all)
-		dup_error = 1;
-	if (types == TYPE_INT)
-		sprintf(value, "%d", git_config_int(key_, value_?value_:""));
-	else if (types == TYPE_BOOL)
-		vptr = git_config_bool(key_, value_) ? "true" : "false";
-	else if (types == TYPE_BOOL_OR_INT) {
-		int is_bool, v;
-		v = git_config_bool_or_int(key_, value_, &is_bool);
-		if (is_bool)
-			vptr = v ? "true" : "false";
-		else
-			sprintf(value, "%d", v);
-	}
-	else
-		vptr = value_?value_:"";
-	seen++;
-	if (dup_error) {
-		error("More than one value for the key %s: %s",
-				key_, vptr);
-	}
-	else
-		printf("%s%c", vptr, term);
-
-	return 0;
-}
-
-static int get_value(const char *key_, const char *regex_)
-{
-	int ret = -1;
-	char *tl;
-	char *global = NULL, *repo_config = NULL;
-	const char *system_wide = NULL, *local;
-
-	local = config_exclusive_filename;
-	if (!local) {
-		const char *home = getenv("HOME");
-		local = repo_config = git_pathdup("config");
-		if (git_config_global() && home)
-			global = xstrdup(mkpath("%s/.gitconfig", home));
-		if (git_config_system())
-			system_wide = git_etc_gitconfig();
-	}
-
-	key = xstrdup(key_);
-	for (tl=key+strlen(key)-1; tl >= key && *tl != '.'; --tl)
-		*tl = tolower(*tl);
-	for (tl=key; *tl && *tl != '.'; ++tl)
-		*tl = tolower(*tl);
-
-	if (use_key_regexp) {
-		key_regexp = (regex_t*)xmalloc(sizeof(regex_t));
-		if (regcomp(key_regexp, key, REG_EXTENDED)) {
-			fprintf(stderr, "Invalid key pattern: %s\n", key_);
-			goto free_strings;
-		}
-	}
-
-	if (regex_) {
-		if (regex_[0] == '!') {
-			do_not_match = 1;
-			regex_++;
-		}
-
-		regexp = (regex_t*)xmalloc(sizeof(regex_t));
-		if (regcomp(regexp, regex_, REG_EXTENDED)) {
-			fprintf(stderr, "Invalid pattern: %s\n", regex_);
-			goto free_strings;
-		}
-	}
-
-	if (do_all && system_wide)
-		git_config_from_file(show_config, system_wide, NULL);
-	if (do_all && global)
-		git_config_from_file(show_config, global, NULL);
-	git_config_from_file(show_config, local, NULL);
-	if (!do_all && !seen && global)
-		git_config_from_file(show_config, global, NULL);
-	if (!do_all && !seen && system_wide)
-		git_config_from_file(show_config, system_wide, NULL);
-
-	free(key);
-	if (regexp) {
-		regfree(regexp);
-		free(regexp);
-	}
-
-	if (do_all)
-		ret = !seen;
-	else
-		ret = (seen == 1) ? 0 : seen > 1 ? 2 : 1;
-
-free_strings:
-	free(repo_config);
-	free(global);
-	return ret;
-}
-
-static char *normalize_value(const char *key, const char *value)
-{
-	char *normalized;
-
-	if (!value)
-		return NULL;
-
-	if (types == 0)
-		normalized = xstrdup(value);
-	else {
-		normalized = xmalloc(64);
-		if (types == TYPE_INT) {
-			int v = git_config_int(key, value);
-			sprintf(normalized, "%d", v);
-		}
-		else if (types == TYPE_BOOL)
-			sprintf(normalized, "%s",
-				git_config_bool(key, value) ? "true" : "false");
-		else if (types == TYPE_BOOL_OR_INT) {
-			int is_bool, v;
-			v = git_config_bool_or_int(key, value, &is_bool);
-			if (!is_bool)
-				sprintf(normalized, "%d", v);
-			else
-				sprintf(normalized, "%s", v ? "true" : "false");
-		}
-	}
-
-	return normalized;
-}
-
-static int get_color_found;
-static const char *get_color_slot;
-static const char *get_colorbool_slot;
-static char parsed_color[COLOR_MAXLEN];
-
-static int git_get_color_config(const char *var, const char *value, void *cb)
-{
-	if (!strcmp(var, get_color_slot)) {
-		if (!value)
-			config_error_nonbool(var);
-		color_parse(value, var, parsed_color);
-		get_color_found = 1;
-	}
-	return 0;
-}
-
-static void get_color(const char *def_color)
-{
-	get_color_found = 0;
-	parsed_color[0] = '\0';
-	git_config(git_get_color_config, NULL);
-
-	if (!get_color_found && def_color)
-		color_parse(def_color, "command line", parsed_color);
-
-	fputs(parsed_color, stdout);
-}
-
-static int stdout_is_tty;
-static int get_colorbool_found;
-static int get_diff_color_found;
-static int git_get_colorbool_config(const char *var, const char *value,
-		void *cb)
-{
-	if (!strcmp(var, get_colorbool_slot)) {
-		get_colorbool_found =
-			git_config_colorbool(var, value, stdout_is_tty);
-	}
-	if (!strcmp(var, "diff.color")) {
-		get_diff_color_found =
-			git_config_colorbool(var, value, stdout_is_tty);
-	}
-	if (!strcmp(var, "color.ui")) {
-		git_use_color_default = git_config_colorbool(var, value, stdout_is_tty);
-		return 0;
-	}
-	return 0;
-}
-
-static int get_colorbool(int print)
-{
-	get_colorbool_found = -1;
-	get_diff_color_found = -1;
-	git_config(git_get_colorbool_config, NULL);
-
-	if (get_colorbool_found < 0) {
-		if (!strcmp(get_colorbool_slot, "color.diff"))
-			get_colorbool_found = get_diff_color_found;
-		if (get_colorbool_found < 0)
-			get_colorbool_found = git_use_color_default;
-	}
-
-	if (print) {
-		printf("%s\n", get_colorbool_found ? "true" : "false");
-		return 0;
-	} else
-		return get_colorbool_found ? 0 : 1;
-}
-
-int cmd_config(int argc, const char **argv, const char *unused_prefix)
-{
-	int nongit;
-	char *value;
-	const char *prefix = setup_git_directory_gently(&nongit);
-
-	config_exclusive_filename = getenv(CONFIG_ENVIRONMENT);
-
-	argc = parse_options(argc, argv, prefix, builtin_config_options,
-			     builtin_config_usage,
-			     PARSE_OPT_STOP_AT_NON_OPTION);
-
-	if (use_global_config + use_system_config + !!given_config_file > 1) {
-		error("only one config file at a time.");
-		usage_with_options(builtin_config_usage, builtin_config_options);
-	}
-
-	if (use_global_config) {
-		char *home = getenv("HOME");
-		if (home) {
-			char *user_config = xstrdup(mkpath("%s/.gitconfig", home));
-			config_exclusive_filename = user_config;
-		} else {
-			die("$HOME not set");
-		}
-	}
-	else if (use_system_config)
-		config_exclusive_filename = git_etc_gitconfig();
-	else if (given_config_file) {
-		if (!is_absolute_path(given_config_file) && prefix)
-			config_exclusive_filename = prefix_filename(prefix,
-								    strlen(prefix),
-								    argv[2]);
-		else
-			config_exclusive_filename = given_config_file;
-	}
-
-	if (end_null) {
-		term = '\0';
-		delim = '\n';
-		key_delim = '\n';
-	}
-
-	if (HAS_MULTI_BITS(types)) {
-		error("only one type at a time.");
-		usage_with_options(builtin_config_usage, builtin_config_options);
-	}
-
-	if (get_color_slot)
-	    actions |= ACTION_GET_COLOR;
-	if (get_colorbool_slot)
-	    actions |= ACTION_GET_COLORBOOL;
-
-	if ((get_color_slot || get_colorbool_slot) && types) {
-		error("--get-color and variable type are incoherent");
-		usage_with_options(builtin_config_usage, builtin_config_options);
-	}
-
-	if (HAS_MULTI_BITS(actions)) {
-		error("only one action at a time.");
-		usage_with_options(builtin_config_usage, builtin_config_options);
-	}
-	if (actions == 0)
-		switch (argc) {
-		case 1: actions = ACTION_GET; break;
-		case 2: actions = ACTION_SET; break;
-		case 3: actions = ACTION_SET_ALL; break;
-		default:
-			usage_with_options(builtin_config_usage, builtin_config_options);
-		}
-
-	if (actions == ACTION_LIST) {
-		check_argc(argc, 0, 0);
-		if (git_config(show_all_config, NULL) < 0) {
-			if (config_exclusive_filename)
-				die_errno("unable to read config file '%s'",
-					  config_exclusive_filename);
-			else
-				die("error processing config file(s)");
-		}
-	}
-	else if (actions == ACTION_EDIT) {
-		check_argc(argc, 0, 0);
-		if (!config_exclusive_filename && nongit)
-			die("not in a git directory");
-		git_config(git_default_config, NULL);
-		launch_editor(config_exclusive_filename ?
-			      config_exclusive_filename : git_path("config"),
-			      NULL, NULL);
-	}
-	else if (actions == ACTION_SET) {
-		check_argc(argc, 2, 2);
-		value = normalize_value(argv[0], argv[1]);
-		return git_config_set(argv[0], value);
-	}
-	else if (actions == ACTION_SET_ALL) {
-		check_argc(argc, 2, 3);
-		value = normalize_value(argv[0], argv[1]);
-		return git_config_set_multivar(argv[0], value, argv[2], 0);
-	}
-	else if (actions == ACTION_ADD) {
-		check_argc(argc, 2, 2);
-		value = normalize_value(argv[0], argv[1]);
-		return git_config_set_multivar(argv[0], value, "^$", 0);
-	}
-	else if (actions == ACTION_REPLACE_ALL) {
-		check_argc(argc, 2, 3);
-		value = normalize_value(argv[0], argv[1]);
-		return git_config_set_multivar(argv[0], value, argv[2], 1);
-	}
-	else if (actions == ACTION_GET) {
-		check_argc(argc, 1, 2);
-		return get_value(argv[0], argv[1]);
-	}
-	else if (actions == ACTION_GET_ALL) {
-		do_all = 1;
-		check_argc(argc, 1, 2);
-		return get_value(argv[0], argv[1]);
-	}
-	else if (actions == ACTION_GET_REGEXP) {
-		show_keys = 1;
-		use_key_regexp = 1;
-		do_all = 1;
-		check_argc(argc, 1, 2);
-		return get_value(argv[0], argv[1]);
-	}
-	else if (actions == ACTION_UNSET) {
-		check_argc(argc, 1, 2);
-		if (argc == 2)
-			return git_config_set_multivar(argv[0], NULL, argv[1], 0);
-		else
-			return git_config_set(argv[0], NULL);
-	}
-	else if (actions == ACTION_UNSET_ALL) {
-		check_argc(argc, 1, 2);
-		return git_config_set_multivar(argv[0], NULL, argv[1], 1);
-	}
-	else if (actions == ACTION_RENAME_SECTION) {
-		int ret;
-		check_argc(argc, 2, 2);
-		ret = git_config_rename_section(argv[0], argv[1]);
-		if (ret < 0)
-			return ret;
-		if (ret == 0)
-			die("No such section!");
-	}
-	else if (actions == ACTION_REMOVE_SECTION) {
-		int ret;
-		check_argc(argc, 1, 1);
-		ret = git_config_rename_section(argv[0], NULL);
-		if (ret < 0)
-			return ret;
-		if (ret == 0)
-			die("No such section!");
-	}
-	else if (actions == ACTION_GET_COLOR) {
-		get_color(argv[0]);
-	}
-	else if (actions == ACTION_GET_COLORBOOL) {
-		if (argc == 1)
-			stdout_is_tty = git_config_bool("command line", argv[0]);
-		else if (argc == 0)
-			stdout_is_tty = isatty(1);
-		return get_colorbool(argc != 0);
-	}
-
-	return 0;
-}
diff --git a/builtin-count-objects.c b/builtin-count-objects.c
deleted file mode 100644
index 1b0b6c8..0000000
--- a/builtin-count-objects.c
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Builtin "git count-objects".
- *
- * Copyright (c) 2006 Junio C Hamano
- */
-
-#include "cache.h"
-#include "dir.h"
-#include "builtin.h"
-#include "parse-options.h"
-
-static void count_objects(DIR *d, char *path, int len, int verbose,
-			  unsigned long *loose,
-			  unsigned long *loose_size,
-			  unsigned long *packed_loose,
-			  unsigned long *garbage)
-{
-	struct dirent *ent;
-	while ((ent = readdir(d)) != NULL) {
-		char hex[41];
-		unsigned char sha1[20];
-		const char *cp;
-		int bad = 0;
-
-		if (is_dot_or_dotdot(ent->d_name))
-			continue;
-		for (cp = ent->d_name; *cp; cp++) {
-			int ch = *cp;
-			if (('0' <= ch && ch <= '9') ||
-			    ('a' <= ch && ch <= 'f'))
-				continue;
-			bad = 1;
-			break;
-		}
-		if (cp - ent->d_name != 38)
-			bad = 1;
-		else {
-			struct stat st;
-			memcpy(path + len + 3, ent->d_name, 38);
-			path[len + 2] = '/';
-			path[len + 41] = 0;
-			if (lstat(path, &st) || !S_ISREG(st.st_mode))
-				bad = 1;
-			else
-				(*loose_size) += xsize_t(on_disk_bytes(st));
-		}
-		if (bad) {
-			if (verbose) {
-				error("garbage found: %.*s/%s",
-				      len + 2, path, ent->d_name);
-				(*garbage)++;
-			}
-			continue;
-		}
-		(*loose)++;
-		if (!verbose)
-			continue;
-		memcpy(hex, path+len, 2);
-		memcpy(hex+2, ent->d_name, 38);
-		hex[40] = 0;
-		if (get_sha1_hex(hex, sha1))
-			die("internal error");
-		if (has_sha1_pack(sha1))
-			(*packed_loose)++;
-	}
-}
-
-static char const * const count_objects_usage[] = {
-	"git count-objects [-v]",
-	NULL
-};
-
-int cmd_count_objects(int argc, const char **argv, const char *prefix)
-{
-	int i, verbose = 0;
-	const char *objdir = get_object_directory();
-	int len = strlen(objdir);
-	char *path = xmalloc(len + 50);
-	unsigned long loose = 0, packed = 0, packed_loose = 0, garbage = 0;
-	unsigned long loose_size = 0;
-	struct option opts[] = {
-		OPT__VERBOSE(&verbose),
-		OPT_END(),
-	};
-
-	argc = parse_options(argc, argv, prefix, opts, count_objects_usage, 0);
-	/* we do not take arguments other than flags for now */
-	if (argc)
-		usage_with_options(count_objects_usage, opts);
-	memcpy(path, objdir, len);
-	if (len && objdir[len-1] != '/')
-		path[len++] = '/';
-	for (i = 0; i < 256; i++) {
-		DIR *d;
-		sprintf(path + len, "%02x", i);
-		d = opendir(path);
-		if (!d)
-			continue;
-		count_objects(d, path, len, verbose,
-			      &loose, &loose_size, &packed_loose, &garbage);
-		closedir(d);
-	}
-	if (verbose) {
-		struct packed_git *p;
-		unsigned long num_pack = 0;
-		unsigned long size_pack = 0;
-		if (!packed_git)
-			prepare_packed_git();
-		for (p = packed_git; p; p = p->next) {
-			if (!p->pack_local)
-				continue;
-			if (open_pack_index(p))
-				continue;
-			packed += p->num_objects;
-			size_pack += p->pack_size + p->index_size;
-			num_pack++;
-		}
-		printf("count: %lu\n", loose);
-		printf("size: %lu\n", loose_size / 1024);
-		printf("in-pack: %lu\n", packed);
-		printf("packs: %lu\n", num_pack);
-		printf("size-pack: %lu\n", size_pack / 1024);
-		printf("prune-packable: %lu\n", packed_loose);
-		printf("garbage: %lu\n", garbage);
-	}
-	else
-		printf("%lu objects, %lu kilobytes\n",
-		       loose, loose_size / 1024);
-	return 0;
-}
diff --git a/builtin-describe.c b/builtin-describe.c
deleted file mode 100644
index 7a66298..0000000
--- a/builtin-describe.c
+++ /dev/null
@@ -1,365 +0,0 @@
-#include "cache.h"
-#include "commit.h"
-#include "tag.h"
-#include "refs.h"
-#include "builtin.h"
-#include "exec_cmd.h"
-#include "parse-options.h"
-
-#define SEEN		(1u<<0)
-#define MAX_TAGS	(FLAG_BITS - 1)
-
-static const char * const describe_usage[] = {
-	"git describe [options] <committish>*",
-	NULL
-};
-
-static int debug;	/* Display lots of verbose info */
-static int all;	/* Any valid ref can be used */
-static int tags;	/* Allow lightweight tags */
-static int longformat;
-static int abbrev = DEFAULT_ABBREV;
-static int max_candidates = 10;
-static const char *pattern;
-static int always;
-
-struct commit_name {
-	struct tag *tag;
-	int prio; /* annotated tag = 2, tag = 1, head = 0 */
-	unsigned char sha1[20];
-	char path[FLEX_ARRAY]; /* more */
-};
-static const char *prio_names[] = {
-	"head", "lightweight", "annotated",
-};
-
-static void add_to_known_names(const char *path,
-			       struct commit *commit,
-			       int prio,
-			       const unsigned char *sha1)
-{
-	struct commit_name *e = commit->util;
-	if (!e || e->prio < prio) {
-		size_t len = strlen(path)+1;
-		free(e);
-		e = xmalloc(sizeof(struct commit_name) + len);
-		e->tag = NULL;
-		e->prio = prio;
-		hashcpy(e->sha1, sha1);
-		memcpy(e->path, path, len);
-		commit->util = e;
-	}
-}
-
-static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data)
-{
-	int might_be_tag = !prefixcmp(path, "refs/tags/");
-	struct commit *commit;
-	struct object *object;
-	unsigned char peeled[20];
-	int is_tag, prio;
-
-	if (!all && !might_be_tag)
-		return 0;
-
-	if (!peel_ref(path, peeled) && !is_null_sha1(peeled)) {
-		commit = lookup_commit_reference_gently(peeled, 1);
-		if (!commit)
-			return 0;
-		is_tag = !!hashcmp(sha1, commit->object.sha1);
-	} else {
-		commit = lookup_commit_reference_gently(sha1, 1);
-		object = parse_object(sha1);
-		if (!commit || !object)
-			return 0;
-		is_tag = object->type == OBJ_TAG;
-	}
-
-	/* If --all, then any refs are used.
-	 * If --tags, then any tags are used.
-	 * Otherwise only annotated tags are used.
-	 */
-	if (might_be_tag) {
-		if (is_tag)
-			prio = 2;
-		else
-			prio = 1;
-
-		if (pattern && fnmatch(pattern, path + 10, 0))
-			prio = 0;
-	}
-	else
-		prio = 0;
-
-	if (!all) {
-		if (!prio)
-			return 0;
-		if (!tags && prio < 2)
-			return 0;
-	}
-	add_to_known_names(all ? path + 5 : path + 10, commit, prio, sha1);
-	return 0;
-}
-
-struct possible_tag {
-	struct commit_name *name;
-	int depth;
-	int found_order;
-	unsigned flag_within;
-};
-
-static int compare_pt(const void *a_, const void *b_)
-{
-	struct possible_tag *a = (struct possible_tag *)a_;
-	struct possible_tag *b = (struct possible_tag *)b_;
-	if (a->depth != b->depth)
-		return a->depth - b->depth;
-	if (a->found_order != b->found_order)
-		return a->found_order - b->found_order;
-	return 0;
-}
-
-static unsigned long finish_depth_computation(
-	struct commit_list **list,
-	struct possible_tag *best)
-{
-	unsigned long seen_commits = 0;
-	while (*list) {
-		struct commit *c = pop_commit(list);
-		struct commit_list *parents = c->parents;
-		seen_commits++;
-		if (c->object.flags & best->flag_within) {
-			struct commit_list *a = *list;
-			while (a) {
-				struct commit *i = a->item;
-				if (!(i->object.flags & best->flag_within))
-					break;
-				a = a->next;
-			}
-			if (!a)
-				break;
-		} else
-			best->depth++;
-		while (parents) {
-			struct commit *p = parents->item;
-			parse_commit(p);
-			if (!(p->object.flags & SEEN))
-				insert_by_date(p, list);
-			p->object.flags |= c->object.flags;
-			parents = parents->next;
-		}
-	}
-	return seen_commits;
-}
-
-static void display_name(struct commit_name *n)
-{
-	if (n->prio == 2 && !n->tag) {
-		n->tag = lookup_tag(n->sha1);
-		if (!n->tag || parse_tag(n->tag) || !n->tag->tag)
-			die("annotated tag %s not available", n->path);
-		if (strcmp(n->tag->tag, all ? n->path + 5 : n->path))
-			warning("tag '%s' is really '%s' here", n->tag->tag, n->path);
-	}
-
-	if (n->tag)
-		printf("%s", n->tag->tag);
-	else
-		printf("%s", n->path);
-}
-
-static void show_suffix(int depth, const unsigned char *sha1)
-{
-	printf("-%d-g%s", depth, find_unique_abbrev(sha1, abbrev));
-}
-
-static void describe(const char *arg, int last_one)
-{
-	unsigned char sha1[20];
-	struct commit *cmit, *gave_up_on = NULL;
-	struct commit_list *list;
-	static int initialized = 0;
-	struct commit_name *n;
-	struct possible_tag all_matches[MAX_TAGS];
-	unsigned int match_cnt = 0, annotated_cnt = 0, cur_match;
-	unsigned long seen_commits = 0;
-
-	if (get_sha1(arg, sha1))
-		die("Not a valid object name %s", arg);
-	cmit = lookup_commit_reference(sha1);
-	if (!cmit)
-		die("%s is not a valid '%s' object", arg, commit_type);
-
-	if (!initialized) {
-		initialized = 1;
-		for_each_ref(get_name, NULL);
-	}
-
-	n = cmit->util;
-	if (n) {
-		/*
-		 * Exact match to an existing ref.
-		 */
-		display_name(n);
-		if (longformat)
-			show_suffix(0, n->tag ? n->tag->tagged->sha1 : sha1);
-		printf("\n");
-		return;
-	}
-
-	if (!max_candidates)
-		die("no tag exactly matches '%s'", sha1_to_hex(cmit->object.sha1));
-	if (debug)
-		fprintf(stderr, "searching to describe %s\n", arg);
-
-	list = NULL;
-	cmit->object.flags = SEEN;
-	commit_list_insert(cmit, &list);
-	while (list) {
-		struct commit *c = pop_commit(&list);
-		struct commit_list *parents = c->parents;
-		seen_commits++;
-		n = c->util;
-		if (n) {
-			if (match_cnt < max_candidates) {
-				struct possible_tag *t = &all_matches[match_cnt++];
-				t->name = n;
-				t->depth = seen_commits - 1;
-				t->flag_within = 1u << match_cnt;
-				t->found_order = match_cnt;
-				c->object.flags |= t->flag_within;
-				if (n->prio == 2)
-					annotated_cnt++;
-			}
-			else {
-				gave_up_on = c;
-				break;
-			}
-		}
-		for (cur_match = 0; cur_match < match_cnt; cur_match++) {
-			struct possible_tag *t = &all_matches[cur_match];
-			if (!(c->object.flags & t->flag_within))
-				t->depth++;
-		}
-		if (annotated_cnt && !list) {
-			if (debug)
-				fprintf(stderr, "finished search at %s\n",
-					sha1_to_hex(c->object.sha1));
-			break;
-		}
-		while (parents) {
-			struct commit *p = parents->item;
-			parse_commit(p);
-			if (!(p->object.flags & SEEN))
-				insert_by_date(p, &list);
-			p->object.flags |= c->object.flags;
-			parents = parents->next;
-		}
-	}
-
-	if (!match_cnt) {
-		const unsigned char *sha1 = cmit->object.sha1;
-		if (always) {
-			printf("%s\n", find_unique_abbrev(sha1, abbrev));
-			return;
-		}
-		die("cannot describe '%s'", sha1_to_hex(sha1));
-	}
-
-	qsort(all_matches, match_cnt, sizeof(all_matches[0]), compare_pt);
-
-	if (gave_up_on) {
-		insert_by_date(gave_up_on, &list);
-		seen_commits--;
-	}
-	seen_commits += finish_depth_computation(&list, &all_matches[0]);
-	free_commit_list(list);
-
-	if (debug) {
-		for (cur_match = 0; cur_match < match_cnt; cur_match++) {
-			struct possible_tag *t = &all_matches[cur_match];
-			fprintf(stderr, " %-11s %8d %s\n",
-				prio_names[t->name->prio],
-				t->depth, t->name->path);
-		}
-		fprintf(stderr, "traversed %lu commits\n", seen_commits);
-		if (gave_up_on) {
-			fprintf(stderr,
-				"more than %i tags found; listed %i most recent\n"
-				"gave up search at %s\n",
-				max_candidates, max_candidates,
-				sha1_to_hex(gave_up_on->object.sha1));
-		}
-	}
-
-	display_name(all_matches[0].name);
-	if (abbrev)
-		show_suffix(all_matches[0].depth, cmit->object.sha1);
-	printf("\n");
-
-	if (!last_one)
-		clear_commit_marks(cmit, -1);
-}
-
-int cmd_describe(int argc, const char **argv, const char *prefix)
-{
-	int contains = 0;
-	struct option options[] = {
-		OPT_BOOLEAN(0, "contains",   &contains, "find the tag that comes after the commit"),
-		OPT_BOOLEAN(0, "debug",      &debug, "debug search strategy on stderr"),
-		OPT_BOOLEAN(0, "all",        &all, "use any ref in .git/refs"),
-		OPT_BOOLEAN(0, "tags",       &tags, "use any tag in .git/refs/tags"),
-		OPT_BOOLEAN(0, "long",       &longformat, "always use long format"),
-		OPT__ABBREV(&abbrev),
-		OPT_SET_INT(0, "exact-match", &max_candidates,
-			    "only output exact matches", 0),
-		OPT_INTEGER(0, "candidates", &max_candidates,
-			    "consider <n> most recent tags (default: 10)"),
-		OPT_STRING(0, "match",       &pattern, "pattern",
-			   "only consider tags matching <pattern>"),
-		OPT_BOOLEAN(0, "always",     &always,
-			   "show abbreviated commit object as fallback"),
-		OPT_END(),
-	};
-
-	argc = parse_options(argc, argv, prefix, options, describe_usage, 0);
-	if (max_candidates < 0)
-		max_candidates = 0;
-	else if (max_candidates > MAX_TAGS)
-		max_candidates = MAX_TAGS;
-
-	save_commit_buffer = 0;
-
-	if (longformat && abbrev == 0)
-		die("--long is incompatible with --abbrev=0");
-
-	if (contains) {
-		const char **args = xmalloc((7 + argc) * sizeof(char *));
-		int i = 0;
-		args[i++] = "name-rev";
-		args[i++] = "--name-only";
-		args[i++] = "--no-undefined";
-		if (always)
-			args[i++] = "--always";
-		if (!all) {
-			args[i++] = "--tags";
-			if (pattern) {
-				char *s = xmalloc(strlen("--refs=refs/tags/") + strlen(pattern) + 1);
-				sprintf(s, "--refs=refs/tags/%s", pattern);
-				args[i++] = s;
-			}
-		}
-		memcpy(args + i, argv, argc * sizeof(char *));
-		args[i + argc] = NULL;
-		return cmd_name_rev(i + argc, args, prefix);
-	}
-
-	if (argc == 0) {
-		describe("HEAD", 1);
-	} else {
-		while (argc-- > 0) {
-			describe(*argv++, argc == 0);
-		}
-	}
-	return 0;
-}
diff --git a/builtin-diff-files.c b/builtin-diff-files.c
deleted file mode 100644
index 5b64011..0000000
--- a/builtin-diff-files.c
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * GIT - The information manager from hell
- *
- * Copyright (C) Linus Torvalds, 2005
- */
-#include "cache.h"
-#include "diff.h"
-#include "commit.h"
-#include "revision.h"
-#include "builtin.h"
-
-static const char diff_files_usage[] =
-"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, const char *prefix)
-{
-	struct rev_info rev;
-	int result;
-	unsigned options = 0;
-
-	init_revisions(&rev, prefix);
-	git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
-	rev.abbrev = 0;
-
-	argc = setup_revisions(argc, argv, &rev, NULL);
-	while (1 < argc && argv[1][0] == '-') {
-		if (!strcmp(argv[1], "--base"))
-			rev.max_count = 1;
-		else if (!strcmp(argv[1], "--ours"))
-			rev.max_count = 2;
-		else if (!strcmp(argv[1], "--theirs"))
-			rev.max_count = 3;
-		else if (!strcmp(argv[1], "-q"))
-			options |= DIFF_SILENT_ON_REMOVED;
-		else
-			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), and
-	 * there is no other revision filtering parameters.
-	 */
-	if (rev.pending.nr ||
-	    rev.min_age != -1 || rev.max_age != -1 ||
-	    3 < rev.max_count)
-		usage(diff_files_usage);
-
-	/*
-	 * "diff-files --base -p" should not combine merges because it
-	 * was not asked to.  "diff-files -c -p" should not densify
-	 * (the user should ask with "diff-files --cc" explicitly).
-	 */
-	if (rev.max_count == -1 && !rev.combine_merges &&
-	    (rev.diffopt.output_format & DIFF_FORMAT_PATCH))
-		rev.combine_merges = rev.dense_combined_merges = 1;
-
-	if (read_cache_preload(rev.diffopt.paths) < 0) {
-		perror("read_cache_preload");
-		return -1;
-	}
-	result = run_diff_files(&rev, options);
-	return diff_result_code(&rev.diffopt, result);
-}
diff --git a/builtin-diff-index.c b/builtin-diff-index.c
deleted file mode 100644
index 0483749..0000000
--- a/builtin-diff-index.c
+++ /dev/null
@@ -1,50 +0,0 @@
-#include "cache.h"
-#include "diff.h"
-#include "commit.h"
-#include "revision.h"
-#include "builtin.h"
-
-static const char diff_cache_usage[] =
-"git diff-index [-m] [--cached] "
-"[<common diff options>] <tree-ish> [<path>...]"
-COMMON_DIFF_OPTIONS_HELP;
-
-int cmd_diff_index(int argc, const char **argv, const char *prefix)
-{
-	struct rev_info rev;
-	int cached = 0;
-	int i;
-	int result;
-
-	init_revisions(&rev, prefix);
-	git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
-	rev.abbrev = 0;
-
-	argc = setup_revisions(argc, argv, &rev, NULL);
-	for (i = 1; i < argc; i++) {
-		const char *arg = argv[i];
-
-		if (!strcmp(arg, "--cached"))
-			cached = 1;
-		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.
-	 */
-	if (rev.pending.nr != 1 ||
-	    rev.max_count != -1 || rev.min_age != -1 || rev.max_age != -1)
-		usage(diff_cache_usage);
-	if (!cached)
-		setup_work_tree();
-	if (read_cache() < 0) {
-		perror("read_cache");
-		return -1;
-	}
-	result = run_diff_index(&rev, cached);
-	return diff_result_code(&rev.diffopt, result);
-}
diff --git a/builtin-diff-tree.c b/builtin-diff-tree.c
deleted file mode 100644
index 79cedb7..0000000
--- a/builtin-diff-tree.c
+++ /dev/null
@@ -1,169 +0,0 @@
-#include "cache.h"
-#include "diff.h"
-#include "commit.h"
-#include "log-tree.h"
-#include "builtin.h"
-
-static struct rev_info log_tree_opt;
-
-static int diff_tree_commit_sha1(const unsigned char *sha1)
-{
-	struct commit *commit = lookup_commit_reference(sha1);
-	if (!commit)
-		return -1;
-	return log_tree_commit(&log_tree_opt, commit);
-}
-
-/* Diff one or more commits. */
-static int stdin_diff_commit(struct commit *commit, char *line, int len)
-{
-	unsigned char sha1[20];
-	if (isspace(line[40]) && !get_sha1_hex(line+41, sha1)) {
-		/* Graft the fake parents locally to the commit */
-		int pos = 41;
-		struct commit_list **pptr, *parents;
-
-		/* Free the real parent list */
-		for (parents = commit->parents; parents; ) {
-			struct commit_list *tmp = parents->next;
-			free(parents);
-			parents = tmp;
-		}
-		commit->parents = NULL;
-		pptr = &(commit->parents);
-		while (line[pos] && !get_sha1_hex(line + pos, sha1)) {
-			struct commit *parent = lookup_commit(sha1);
-			if (parent) {
-				pptr = &commit_list_insert(parent, pptr)->next;
-			}
-			pos += 41;
-		}
-	}
-	return log_tree_commit(&log_tree_opt, commit);
-}
-
-/* Diff two trees. */
-static int stdin_diff_trees(struct tree *tree1, char *line, int len)
-{
-	unsigned char sha1[20];
-	struct tree *tree2;
-	if (len != 82 || !isspace(line[40]) || get_sha1_hex(line + 41, sha1))
-		return error("Need exactly two trees, separated by a space");
-	tree2 = lookup_tree(sha1);
-	if (!tree2 || parse_tree(tree2))
-		return -1;
-	printf("%s %s\n", sha1_to_hex(tree1->object.sha1),
-			  sha1_to_hex(tree2->object.sha1));
-	diff_tree_sha1(tree1->object.sha1, tree2->object.sha1,
-		       "", &log_tree_opt.diffopt);
-	log_tree_diff_flush(&log_tree_opt);
-	return 0;
-}
-
-static int diff_tree_stdin(char *line)
-{
-	int len = strlen(line);
-	unsigned char sha1[20];
-	struct object *obj;
-
-	if (!len || line[len-1] != '\n')
-		return -1;
-	line[len-1] = 0;
-	if (get_sha1_hex(line, sha1))
-		return -1;
-	obj = lookup_unknown_object(sha1);
-	if (!obj || !obj->parsed)
-		obj = parse_object(sha1);
-	if (!obj)
-		return -1;
-	if (obj->type == OBJ_COMMIT)
-		return stdin_diff_commit((struct commit *)obj, line, len);
-	if (obj->type == OBJ_TREE)
-		return stdin_diff_trees((struct tree *)obj, line, len);
-	error("Object %s is a %s, not a commit or tree",
-	      sha1_to_hex(sha1), typename(obj->type));
-	return -1;
-}
-
-static const char diff_tree_usage[] =
-"git diff-tree [--stdin] [-m] [-c] [--cc] [-s] [-v] [--pretty] [-t] [-r] [--root] "
-"[<common diff options>] <tree-ish> [<tree-ish>] [<path>...]\n"
-"  -r            diff recursively\n"
-"  --root        include the initial commit as diff against /dev/null\n"
-COMMON_DIFF_OPTIONS_HELP;
-
-int cmd_diff_tree(int argc, const char **argv, const char *prefix)
-{
-	int nr_sha1;
-	char line[1000];
-	struct object *tree1, *tree2;
-	static struct rev_info *opt = &log_tree_opt;
-	int read_stdin = 0;
-
-	init_revisions(opt, prefix);
-	git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
-	opt->abbrev = 0;
-	opt->diff = 1;
-	argc = setup_revisions(argc, argv, opt, NULL);
-
-	while (--argc > 0) {
-		const char *arg = *++argv;
-
-		if (!strcmp(arg, "--stdin")) {
-			read_stdin = 1;
-			continue;
-		}
-		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
-	 * is marked UNINTERESTING.
-	 */
-	nr_sha1 = opt->pending.nr;
-	switch (nr_sha1) {
-	case 0:
-		if (!read_stdin)
-			usage(diff_tree_usage);
-		break;
-	case 1:
-		tree1 = opt->pending.objects[0].item;
-		diff_tree_commit_sha1(tree1->sha1);
-		break;
-	case 2:
-		tree1 = opt->pending.objects[0].item;
-		tree2 = opt->pending.objects[1].item;
-		if (tree2->flags & UNINTERESTING) {
-			struct object *tmp = tree2;
-			tree2 = tree1;
-			tree1 = tmp;
-		}
-		diff_tree_sha1(tree1->sha1,
-			       tree2->sha1,
-			       "", &opt->diffopt);
-		log_tree_diff_flush(opt);
-		break;
-	}
-
-	if (read_stdin) {
-		if (opt->diffopt.detect_rename)
-			opt->diffopt.setup |= (DIFF_SETUP_USE_SIZE_CACHE |
-					       DIFF_SETUP_USE_CACHE);
-		while (fgets(line, sizeof(line), stdin)) {
-			unsigned char sha1[20];
-
-			if (get_sha1_hex(line, sha1)) {
-				fputs(line, stdout);
-				fflush(stdout);
-			}
-			else
-				diff_tree_stdin(line);
-		}
-	}
-
-	return diff_result_code(&opt->diffopt, 0);
-}
diff --git a/builtin-diff.c b/builtin-diff.c
deleted file mode 100644
index 2e51f40..0000000
--- a/builtin-diff.c
+++ /dev/null
@@ -1,423 +0,0 @@
-/*
- * Builtin "git diff"
- *
- * Copyright (c) 2006 Junio C Hamano
- */
-#include "cache.h"
-#include "color.h"
-#include "commit.h"
-#include "blob.h"
-#include "tag.h"
-#include "diff.h"
-#include "diffcore.h"
-#include "revision.h"
-#include "log-tree.h"
-#include "builtin.h"
-
-struct blobinfo {
-	unsigned char sha1[20];
-	const char *name;
-	unsigned mode;
-};
-
-static const char builtin_diff_usage[] =
-"git diff <options> <rev>{0,2} -- <path>*";
-
-static void stuff_change(struct diff_options *opt,
-			 unsigned old_mode, unsigned new_mode,
-			 const unsigned char *old_sha1,
-			 const unsigned char *new_sha1,
-			 const char *old_name,
-			 const char *new_name)
-{
-	struct diff_filespec *one, *two;
-
-	if (!is_null_sha1(old_sha1) && !is_null_sha1(new_sha1) &&
-	    !hashcmp(old_sha1, new_sha1) && (old_mode == new_mode))
-		return;
-
-	if (DIFF_OPT_TST(opt, REVERSE_DIFF)) {
-		unsigned tmp;
-		const unsigned char *tmp_u;
-		const char *tmp_c;
-		tmp = old_mode; old_mode = new_mode; new_mode = tmp;
-		tmp_u = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_u;
-		tmp_c = old_name; old_name = new_name; new_name = tmp_c;
-	}
-
-	if (opt->prefix &&
-	    (strncmp(old_name, opt->prefix, opt->prefix_length) ||
-	     strncmp(new_name, opt->prefix, opt->prefix_length)))
-		return;
-
-	one = alloc_filespec(old_name);
-	two = alloc_filespec(new_name);
-	fill_filespec(one, old_sha1, old_mode);
-	fill_filespec(two, new_sha1, new_mode);
-
-	diff_queue(&diff_queued_diff, one, two);
-}
-
-static int builtin_diff_b_f(struct rev_info *revs,
-			    int argc, const char **argv,
-			    struct blobinfo *blob,
-			    const char *path)
-{
-	/* Blob vs file in the working tree*/
-	struct stat st;
-
-	if (argc > 1)
-		usage(builtin_diff_usage);
-
-	if (lstat(path, &st))
-		die_errno("failed to stat '%s'", path);
-	if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)))
-		die("'%s': not a regular file or symlink", path);
-
-	diff_set_mnemonic_prefix(&revs->diffopt, "o/", "w/");
-
-	if (blob[0].mode == S_IFINVALID)
-		blob[0].mode = canon_mode(st.st_mode);
-
-	stuff_change(&revs->diffopt,
-		     blob[0].mode, canon_mode(st.st_mode),
-		     blob[0].sha1, null_sha1,
-		     path, path);
-	diffcore_std(&revs->diffopt);
-	diff_flush(&revs->diffopt);
-	return 0;
-}
-
-static int builtin_diff_blobs(struct rev_info *revs,
-			      int argc, const char **argv,
-			      struct blobinfo *blob)
-{
-	unsigned mode = canon_mode(S_IFREG | 0644);
-
-	if (argc > 1)
-		usage(builtin_diff_usage);
-
-	if (blob[0].mode == S_IFINVALID)
-		blob[0].mode = mode;
-
-	if (blob[1].mode == S_IFINVALID)
-		blob[1].mode = mode;
-
-	stuff_change(&revs->diffopt,
-		     blob[0].mode, blob[1].mode,
-		     blob[0].sha1, blob[1].sha1,
-		     blob[0].name, blob[1].name);
-	diffcore_std(&revs->diffopt);
-	diff_flush(&revs->diffopt);
-	return 0;
-}
-
-static int builtin_diff_index(struct rev_info *revs,
-			      int argc, const char **argv)
-{
-	int cached = 0;
-	while (1 < argc) {
-		const char *arg = argv[1];
-		if (!strcmp(arg, "--cached") || !strcmp(arg, "--staged"))
-			cached = 1;
-		else
-			usage(builtin_diff_usage);
-		argv++; argc--;
-	}
-	if (!cached)
-		setup_work_tree();
-	/*
-	 * Make sure there is one revision (i.e. pending object),
-	 * and there is no revision filtering parameters.
-	 */
-	if (revs->pending.nr != 1 ||
-	    revs->max_count != -1 || revs->min_age != -1 ||
-	    revs->max_age != -1)
-		usage(builtin_diff_usage);
-	if (read_cache_preload(revs->diffopt.paths) < 0) {
-		perror("read_cache_preload");
-		return -1;
-	}
-	return run_diff_index(revs, cached);
-}
-
-static int builtin_diff_tree(struct rev_info *revs,
-			     int argc, const char **argv,
-			     struct object_array_entry *ent)
-{
-	const unsigned char *(sha1[2]);
-	int swap = 0;
-
-	if (argc > 1)
-		usage(builtin_diff_usage);
-
-	/* We saw two trees, ent[0] and ent[1].
-	 * if ent[1] is uninteresting, they are swapped
-	 */
-	if (ent[1].item->flags & UNINTERESTING)
-		swap = 1;
-	sha1[swap] = ent[0].item->sha1;
-	sha1[1-swap] = ent[1].item->sha1;
-	diff_tree_sha1(sha1[0], sha1[1], "", &revs->diffopt);
-	log_tree_diff_flush(revs);
-	return 0;
-}
-
-static int builtin_diff_combined(struct rev_info *revs,
-				 int argc, const char **argv,
-				 struct object_array_entry *ent,
-				 int ents)
-{
-	const unsigned char (*parent)[20];
-	int i;
-
-	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));
-	for (i = 0; i < ents; i++)
-		hashcpy((unsigned char *)(parent + i), ent[i].item->sha1);
-	diff_tree_combined(parent[0], parent + 1, ents - 1,
-			   revs->dense_combined_merges, revs);
-	return 0;
-}
-
-static void refresh_index_quietly(void)
-{
-	struct lock_file *lock_file;
-	int fd;
-
-	lock_file = xcalloc(1, sizeof(struct lock_file));
-	fd = hold_locked_index(lock_file, 0);
-	if (fd < 0)
-		return;
-	discard_cache();
-	read_cache();
-	refresh_cache(REFRESH_QUIET|REFRESH_UNMERGED);
-
-	if (active_cache_changed &&
-	    !write_cache(fd, active_cache, active_nr))
-		commit_locked_index(lock_file);
-
-	rollback_lock_file(lock_file);
-}
-
-static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv)
-{
-	int result;
-	unsigned int options = 0;
-
-	while (1 < argc && argv[1][0] == '-') {
-		if (!strcmp(argv[1], "--base"))
-			revs->max_count = 1;
-		else if (!strcmp(argv[1], "--ours"))
-			revs->max_count = 2;
-		else if (!strcmp(argv[1], "--theirs"))
-			revs->max_count = 3;
-		else if (!strcmp(argv[1], "-q"))
-			options |= DIFF_SILENT_ON_REMOVED;
-		else
-			return error("invalid option: %s", argv[1]);
-		argv++; argc--;
-	}
-
-	/*
-	 * "diff --base" should not combine merges because it was not
-	 * asked to.  "diff -c" should not densify (if the user wants
-	 * dense one, --cc can be explicitly asked for, or just rely
-	 * on the default).
-	 */
-	if (revs->max_count == -1 && !revs->combine_merges &&
-	    (revs->diffopt.output_format & DIFF_FORMAT_PATCH))
-		revs->combine_merges = revs->dense_combined_merges = 1;
-
-	setup_work_tree();
-	if (read_cache_preload(revs->diffopt.paths) < 0) {
-		perror("read_cache_preload");
-		return -1;
-	}
-	result = run_diff_files(revs, options);
-	return diff_result_code(&revs->diffopt, result);
-}
-
-int cmd_diff(int argc, const char **argv, const char *prefix)
-{
-	int i;
-	struct rev_info rev;
-	struct object_array_entry ent[100];
-	int ents = 0, blobs = 0, paths = 0;
-	const char *path = NULL;
-	struct blobinfo blob[2];
-	int nongit;
-	int result = 0;
-
-	/*
-	 * We could get N tree-ish in the rev.pending_objects list.
-	 * Also there could be M blobs there, and P pathspecs.
-	 *
-	 * N=0, M=0:
-	 *	cache vs files (diff-files)
-	 * N=0, M=2:
-	 *      compare two random blobs.  P must be zero.
-	 * N=0, M=1, P=1:
-	 *	compare a blob with a working tree file.
-	 *
-	 * N=1, M=0:
-	 *      tree vs cache (diff-index --cached)
-	 *
-	 * N=2, M=0:
-	 *      tree vs tree (diff-tree)
-	 *
-	 * N=0, M=0, P=2:
-	 *      compare two filesystem entities (aka --no-index).
-	 *
-	 * Other cases are errors.
-	 */
-
-	prefix = setup_git_directory_gently(&nongit);
-	git_config(git_diff_ui_config, NULL);
-
-	if (diff_use_color_default == -1)
-		diff_use_color_default = git_use_color_default;
-
-	init_revisions(&rev, prefix);
-
-	/* If this is a no-index diff, just run it and exit there. */
-	diff_no_index(&rev, argc, argv, nongit, prefix);
-
-	/* Otherwise, we are doing the usual "git" diff */
-	rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index;
-
-	/* Default to let external and textconv be used */
-	DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL);
-	DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV);
-
-	if (nongit)
-		die("Not a git repository");
-	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");
-	}
-
-	DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
-
-	/*
-	 * If the user asked for our exit code then don't start a
-	 * pager or we would end up reporting its exit code instead.
-	 */
-	if (!DIFF_OPT_TST(&rev.diffopt, EXIT_WITH_STATUS) &&
-	    check_pager_config("diff") != 0)
-		setup_pager();
-
-	/*
-	 * Do we have --cached and not have a pending object, then
-	 * default to HEAD by hand.  Eek.
-	 */
-	if (!rev.pending.nr) {
-		int i;
-		for (i = 1; i < argc; i++) {
-			const char *arg = argv[i];
-			if (!strcmp(arg, "--"))
-				break;
-			else if (!strcmp(arg, "--cached") ||
-				 !strcmp(arg, "--staged")) {
-				add_head_to_pending(&rev);
-				if (!rev.pending.nr)
-					die("No HEAD commit to compare with (yet)");
-				break;
-			}
-		}
-	}
-
-	for (i = 0; i < rev.pending.nr; i++) {
-		struct object_array_entry *list = rev.pending.objects+i;
-		struct object *obj = list->item;
-		const char *name = list->name;
-		int flags = (obj->flags & UNINTERESTING);
-		if (!obj->parsed)
-			obj = parse_object(obj->sha1);
-		obj = deref_tag(obj, NULL, 0);
-		if (!obj)
-			die("invalid object '%s' given.", name);
-		if (obj->type == OBJ_COMMIT)
-			obj = &((struct commit *)obj)->tree->object;
-		if (obj->type == OBJ_TREE) {
-			if (ARRAY_SIZE(ent) <= ents)
-				die("more than %d trees given: '%s'",
-				    (int) ARRAY_SIZE(ent), name);
-			obj->flags |= flags;
-			ent[ents].item = obj;
-			ent[ents].name = name;
-			ents++;
-			continue;
-		}
-		if (obj->type == OBJ_BLOB) {
-			if (2 <= blobs)
-				die("more than two blobs given: '%s'", name);
-			hashcpy(blob[blobs].sha1, obj->sha1);
-			blob[blobs].name = name;
-			blob[blobs].mode = list->mode;
-			blobs++;
-			continue;
-
-		}
-		die("unhandled object '%s' given.", name);
-	}
-	if (rev.prune_data) {
-		const char **pathspec = rev.prune_data;
-		while (*pathspec) {
-			if (!path)
-				path = *pathspec;
-			paths++;
-			pathspec++;
-		}
-	}
-
-	/*
-	 * Now, do the arguments look reasonable?
-	 */
-	if (!ents) {
-		switch (blobs) {
-		case 0:
-			result = builtin_diff_files(&rev, argc, argv);
-			break;
-		case 1:
-			if (paths != 1)
-				usage(builtin_diff_usage);
-			result = builtin_diff_b_f(&rev, argc, argv, blob, path);
-			break;
-		case 2:
-			if (paths)
-				usage(builtin_diff_usage);
-			result = builtin_diff_blobs(&rev, argc, argv, blob);
-			break;
-		default:
-			usage(builtin_diff_usage);
-		}
-	}
-	else if (blobs)
-		usage(builtin_diff_usage);
-	else if (ents == 1)
-		result = builtin_diff_index(&rev, argc, argv);
-	else if (ents == 2)
-		result = 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];
-		result = builtin_diff_tree(&rev, argc, argv, ent);
-	}
-	else
-		result = builtin_diff_combined(&rev, argc, argv,
-					     ent, ents);
-	result = diff_result_code(&rev.diffopt, result);
-	if (1 < rev.diffopt.skip_stat_unmatch)
-		refresh_index_quietly();
-	return result;
-}
diff --git a/builtin-fast-export.c b/builtin-fast-export.c
deleted file mode 100644
index b0a4029..0000000
--- a/builtin-fast-export.c
+++ /dev/null
@@ -1,633 +0,0 @@
-/*
- * "git fast-export" builtin command
- *
- * Copyright (C) 2007 Johannes E. Schindelin
- */
-#include "builtin.h"
-#include "cache.h"
-#include "commit.h"
-#include "object.h"
-#include "tag.h"
-#include "diff.h"
-#include "diffcore.h"
-#include "log-tree.h"
-#include "revision.h"
-#include "decorate.h"
-#include "string-list.h"
-#include "utf8.h"
-#include "parse-options.h"
-
-static const char *fast_export_usage[] = {
-	"git fast-export [rev-list-opts]",
-	NULL
-};
-
-static int progress;
-static enum { ABORT, VERBATIM, WARN, STRIP } signed_tag_mode = ABORT;
-static enum { ERROR, DROP, REWRITE } tag_of_filtered_mode = ABORT;
-static int fake_missing_tagger;
-static int no_data;
-
-static int parse_opt_signed_tag_mode(const struct option *opt,
-				     const char *arg, int unset)
-{
-	if (unset || !strcmp(arg, "abort"))
-		signed_tag_mode = ABORT;
-	else if (!strcmp(arg, "verbatim") || !strcmp(arg, "ignore"))
-		signed_tag_mode = VERBATIM;
-	else if (!strcmp(arg, "warn"))
-		signed_tag_mode = WARN;
-	else if (!strcmp(arg, "strip"))
-		signed_tag_mode = STRIP;
-	else
-		return error("Unknown signed-tag mode: %s", arg);
-	return 0;
-}
-
-static int parse_opt_tag_of_filtered_mode(const struct option *opt,
-					  const char *arg, int unset)
-{
-	if (unset || !strcmp(arg, "abort"))
-		tag_of_filtered_mode = ABORT;
-	else if (!strcmp(arg, "drop"))
-		tag_of_filtered_mode = DROP;
-	else if (!strcmp(arg, "rewrite"))
-		tag_of_filtered_mode = REWRITE;
-	else
-		return error("Unknown tag-of-filtered mode: %s", arg);
-	return 0;
-}
-
-static struct decoration idnums;
-static uint32_t last_idnum;
-
-static int has_unshown_parent(struct commit *commit)
-{
-	struct commit_list *parent;
-
-	for (parent = commit->parents; parent; parent = parent->next)
-		if (!(parent->item->object.flags & SHOWN) &&
-		    !(parent->item->object.flags & UNINTERESTING))
-			return 1;
-	return 0;
-}
-
-/* Since intptr_t is C99, we do not use it here */
-static inline uint32_t *mark_to_ptr(uint32_t mark)
-{
-	return ((uint32_t *)NULL) + mark;
-}
-
-static inline uint32_t ptr_to_mark(void * mark)
-{
-	return (uint32_t *)mark - (uint32_t *)NULL;
-}
-
-static inline void mark_object(struct object *object, uint32_t mark)
-{
-	add_decoration(&idnums, object, mark_to_ptr(mark));
-}
-
-static inline void mark_next_object(struct object *object)
-{
-	mark_object(object, ++last_idnum);
-}
-
-static int get_object_mark(struct object *object)
-{
-	void *decoration = lookup_decoration(&idnums, object);
-	if (!decoration)
-		return 0;
-	return ptr_to_mark(decoration);
-}
-
-static void show_progress(void)
-{
-	static int counter = 0;
-	if (!progress)
-		return;
-	if ((++counter % progress) == 0)
-		printf("progress %d objects\n", counter);
-}
-
-static void handle_object(const unsigned char *sha1)
-{
-	unsigned long size;
-	enum object_type type;
-	char *buf;
-	struct object *object;
-
-	if (no_data)
-		return;
-
-	if (is_null_sha1(sha1))
-		return;
-
-	object = parse_object(sha1);
-	if (!object)
-		die ("Could not read blob %s", sha1_to_hex(sha1));
-
-	if (object->flags & SHOWN)
-		return;
-
-	buf = read_sha1_file(sha1, &type, &size);
-	if (!buf)
-		die ("Could not read blob %s", sha1_to_hex(sha1));
-
-	mark_next_object(object);
-
-	printf("blob\nmark :%"PRIu32"\ndata %lu\n", last_idnum, size);
-	if (size && fwrite(buf, size, 1, stdout) != 1)
-		die_errno ("Could not write blob '%s'", sha1_to_hex(sha1));
-	printf("\n");
-
-	show_progress();
-
-	object->flags |= SHOWN;
-	free(buf);
-}
-
-static void show_filemodify(struct diff_queue_struct *q,
-			    struct diff_options *options, void *data)
-{
-	int i;
-	for (i = 0; i < q->nr; i++) {
-		struct diff_filespec *ospec = q->queue[i]->one;
-		struct diff_filespec *spec = q->queue[i]->two;
-
-		switch (q->queue[i]->status) {
-		case DIFF_STATUS_DELETED:
-			printf("D %s\n", spec->path);
-			break;
-
-		case DIFF_STATUS_COPIED:
-		case DIFF_STATUS_RENAMED:
-			printf("%c \"%s\" \"%s\"\n", q->queue[i]->status,
-			       ospec->path, spec->path);
-
-			if (!hashcmp(ospec->sha1, spec->sha1) &&
-			    ospec->mode == spec->mode)
-				break;
-			/* fallthrough */
-
-		case DIFF_STATUS_TYPE_CHANGED:
-		case DIFF_STATUS_MODIFIED:
-		case DIFF_STATUS_ADDED:
-			/*
-			 * Links refer to objects in another repositories;
-			 * output the SHA-1 verbatim.
-			 */
-			if (no_data || S_ISGITLINK(spec->mode))
-				printf("M %06o %s %s\n", spec->mode,
-				       sha1_to_hex(spec->sha1), spec->path);
-			else {
-				struct object *object = lookup_object(spec->sha1);
-				printf("M %06o :%d %s\n", spec->mode,
-				       get_object_mark(object), spec->path);
-			}
-			break;
-
-		default:
-			die("Unexpected comparison status '%c' for %s, %s",
-				q->queue[i]->status,
-				ospec->path ? ospec->path : "none",
-				spec->path ? spec->path : "none");
-		}
-	}
-}
-
-static const char *find_encoding(const char *begin, const char *end)
-{
-	const char *needle = "\nencoding ";
-	char *bol, *eol;
-
-	bol = memmem(begin, end ? end - begin : strlen(begin),
-		     needle, strlen(needle));
-	if (!bol)
-		return git_commit_encoding;
-	bol += strlen(needle);
-	eol = strchrnul(bol, '\n');
-	*eol = '\0';
-	return bol;
-}
-
-static void handle_commit(struct commit *commit, struct rev_info *rev)
-{
-	int saved_output_format = rev->diffopt.output_format;
-	const char *author, *author_end, *committer, *committer_end;
-	const char *encoding, *message;
-	char *reencoded = NULL;
-	struct commit_list *p;
-	int i;
-
-	rev->diffopt.output_format = DIFF_FORMAT_CALLBACK;
-
-	parse_commit(commit);
-	author = strstr(commit->buffer, "\nauthor ");
-	if (!author)
-		die ("Could not find author in commit %s",
-		     sha1_to_hex(commit->object.sha1));
-	author++;
-	author_end = strchrnul(author, '\n');
-	committer = strstr(author_end, "\ncommitter ");
-	if (!committer)
-		die ("Could not find committer in commit %s",
-		     sha1_to_hex(commit->object.sha1));
-	committer++;
-	committer_end = strchrnul(committer, '\n');
-	message = strstr(committer_end, "\n\n");
-	encoding = find_encoding(committer_end, message);
-	if (message)
-		message += 2;
-
-	if (commit->parents &&
-	    get_object_mark(&commit->parents->item->object) != 0) {
-		parse_commit(commit->parents->item);
-		diff_tree_sha1(commit->parents->item->tree->object.sha1,
-			       commit->tree->object.sha1, "", &rev->diffopt);
-	}
-	else
-		diff_root_tree_sha1(commit->tree->object.sha1,
-				    "", &rev->diffopt);
-
-	/* Export the referenced blobs, and remember the marks. */
-	for (i = 0; i < diff_queued_diff.nr; i++)
-		if (!S_ISGITLINK(diff_queued_diff.queue[i]->two->mode))
-			handle_object(diff_queued_diff.queue[i]->two->sha1);
-
-	mark_next_object(&commit->object);
-	if (!is_encoding_utf8(encoding))
-		reencoded = reencode_string(message, "UTF-8", encoding);
-	if (!commit->parents)
-		printf("reset %s\n", (const char*)commit->util);
-	printf("commit %s\nmark :%"PRIu32"\n%.*s\n%.*s\ndata %u\n%s",
-	       (const char *)commit->util, last_idnum,
-	       (int)(author_end - author), author,
-	       (int)(committer_end - committer), committer,
-	       (unsigned)(reencoded
-			  ? strlen(reencoded) : message
-			  ? strlen(message) : 0),
-	       reencoded ? reencoded : message ? message : "");
-	free(reencoded);
-
-	for (i = 0, p = commit->parents; p; p = p->next) {
-		int mark = get_object_mark(&p->item->object);
-		if (!mark)
-			continue;
-		if (i == 0)
-			printf("from :%d\n", mark);
-		else
-			printf("merge :%d\n", mark);
-		i++;
-	}
-
-	log_tree_diff_flush(rev);
-	rev->diffopt.output_format = saved_output_format;
-
-	printf("\n");
-
-	show_progress();
-}
-
-static void handle_tail(struct object_array *commits, struct rev_info *revs)
-{
-	struct commit *commit;
-	while (commits->nr) {
-		commit = (struct commit *)commits->objects[commits->nr - 1].item;
-		if (has_unshown_parent(commit))
-			return;
-		handle_commit(commit, revs);
-		commits->nr--;
-	}
-}
-
-static void handle_tag(const char *name, struct tag *tag)
-{
-	unsigned long size;
-	enum object_type type;
-	char *buf;
-	const char *tagger, *tagger_end, *message;
-	size_t message_size = 0;
-	struct object *tagged;
-	int tagged_mark;
-	struct commit *p;
-
-	/* Trees have no identifer in fast-export output, thus we have no way
-	 * to output tags of trees, tags of tags of trees, etc.  Simply omit
-	 * such tags.
-	 */
-	tagged = tag->tagged;
-	while (tagged->type == OBJ_TAG) {
-		tagged = ((struct tag *)tagged)->tagged;
-	}
-	if (tagged->type == OBJ_TREE) {
-		warning("Omitting tag %s,\nsince tags of trees (or tags of tags of trees, etc.) are not supported.",
-			sha1_to_hex(tag->object.sha1));
-		return;
-	}
-
-	buf = read_sha1_file(tag->object.sha1, &type, &size);
-	if (!buf)
-		die ("Could not read tag %s", sha1_to_hex(tag->object.sha1));
-	message = memmem(buf, size, "\n\n", 2);
-	if (message) {
-		message += 2;
-		message_size = strlen(message);
-	}
-	tagger = memmem(buf, message ? message - buf : size, "\ntagger ", 8);
-	if (!tagger) {
-		if (fake_missing_tagger)
-			tagger = "tagger Unspecified Tagger "
-				"<unspecified-tagger> 0 +0000";
-		else
-			tagger = "";
-		tagger_end = tagger + strlen(tagger);
-	} else {
-		tagger++;
-		tagger_end = strchrnul(tagger, '\n');
-	}
-
-	/* handle signed tags */
-	if (message) {
-		const char *signature = strstr(message,
-					       "\n-----BEGIN PGP SIGNATURE-----\n");
-		if (signature)
-			switch(signed_tag_mode) {
-			case ABORT:
-				die ("Encountered signed tag %s; use "
-				     "--signed-tag=<mode> to handle it.",
-				     sha1_to_hex(tag->object.sha1));
-			case WARN:
-				warning ("Exporting signed tag %s",
-					 sha1_to_hex(tag->object.sha1));
-				/* fallthru */
-			case VERBATIM:
-				break;
-			case STRIP:
-				message_size = signature + 1 - message;
-				break;
-			}
-	}
-
-	/* handle tag->tagged having been filtered out due to paths specified */
-	tagged = tag->tagged;
-	tagged_mark = get_object_mark(tagged);
-	if (!tagged_mark) {
-		switch(tag_of_filtered_mode) {
-		case ABORT:
-			die ("Tag %s tags unexported object; use "
-			     "--tag-of-filtered-object=<mode> to handle it.",
-			     sha1_to_hex(tag->object.sha1));
-		case DROP:
-			/* Ignore this tag altogether */
-			return;
-		case REWRITE:
-			if (tagged->type != OBJ_COMMIT) {
-				die ("Tag %s tags unexported %s!",
-				     sha1_to_hex(tag->object.sha1),
-				     typename(tagged->type));
-			}
-			p = (struct commit *)tagged;
-			for (;;) {
-				if (p->parents && p->parents->next)
-					break;
-				if (p->object.flags & UNINTERESTING)
-					break;
-				if (!(p->object.flags & TREESAME))
-					break;
-				if (!p->parents)
-					die ("Can't find replacement commit for tag %s\n",
-					     sha1_to_hex(tag->object.sha1));
-				p = p->parents->item;
-			}
-			tagged_mark = get_object_mark(&p->object);
-		}
-	}
-
-	if (!prefixcmp(name, "refs/tags/"))
-		name += 10;
-	printf("tag %s\nfrom :%d\n%.*s%sdata %d\n%.*s\n",
-	       name, tagged_mark,
-	       (int)(tagger_end - tagger), tagger,
-	       tagger == tagger_end ? "" : "\n",
-	       (int)message_size, (int)message_size, message ? message : "");
-}
-
-static void get_tags_and_duplicates(struct object_array *pending,
-				    struct string_list *extra_refs)
-{
-	struct tag *tag;
-	int i;
-
-	for (i = 0; i < pending->nr; i++) {
-		struct object_array_entry *e = pending->objects + i;
-		unsigned char sha1[20];
-		struct commit *commit = commit;
-		char *full_name;
-
-		if (dwim_ref(e->name, strlen(e->name), sha1, &full_name) != 1)
-			continue;
-
-		switch (e->item->type) {
-		case OBJ_COMMIT:
-			commit = (struct commit *)e->item;
-			break;
-		case OBJ_TAG:
-			tag = (struct tag *)e->item;
-
-			/* handle nested tags */
-			while (tag && tag->object.type == OBJ_TAG) {
-				parse_object(tag->object.sha1);
-				string_list_append(full_name, extra_refs)->util = tag;
-				tag = (struct tag *)tag->tagged;
-			}
-			if (!tag)
-				die ("Tag %s points nowhere?", e->name);
-			switch(tag->object.type) {
-			case OBJ_COMMIT:
-				commit = (struct commit *)tag;
-				break;
-			case OBJ_BLOB:
-				handle_object(tag->object.sha1);
-				continue;
-			default: /* OBJ_TAG (nested tags) is already handled */
-				warning("Tag points to object of unexpected type %s, skipping.",
-					typename(tag->object.type));
-				continue;
-			}
-			break;
-		default:
-			warning("%s: Unexpected object of type %s, skipping.",
-				e->name,
-				typename(e->item->type));
-			continue;
-		}
-		if (commit->util)
-			/* more than one name for the same object */
-			string_list_append(full_name, extra_refs)->util = commit;
-		else
-			commit->util = full_name;
-	}
-}
-
-static void handle_tags_and_duplicates(struct string_list *extra_refs)
-{
-	struct commit *commit;
-	int i;
-
-	for (i = extra_refs->nr - 1; i >= 0; i--) {
-		const char *name = extra_refs->items[i].string;
-		struct object *object = extra_refs->items[i].util;
-		switch (object->type) {
-		case OBJ_TAG:
-			handle_tag(name, (struct tag *)object);
-			break;
-		case OBJ_COMMIT:
-			/* create refs pointing to already seen commits */
-			commit = (struct commit *)object;
-			printf("reset %s\nfrom :%d\n\n", name,
-			       get_object_mark(&commit->object));
-			show_progress();
-			break;
-		}
-	}
-}
-
-static void export_marks(char *file)
-{
-	unsigned int i;
-	uint32_t mark;
-	struct object_decoration *deco = idnums.hash;
-	FILE *f;
-	int e = 0;
-
-	f = fopen(file, "w");
-	if (!f)
-		error("Unable to open marks file %s for writing.", file);
-
-	for (i = 0; i < idnums.size; i++) {
-		if (deco->base && deco->base->type == 1) {
-			mark = ptr_to_mark(deco->decoration);
-			if (fprintf(f, ":%"PRIu32" %s\n", mark,
-				sha1_to_hex(deco->base->sha1)) < 0) {
-			    e = 1;
-			    break;
-			}
-		}
-		deco++;
-	}
-
-	e |= ferror(f);
-	e |= fclose(f);
-	if (e)
-		error("Unable to write marks file %s.", file);
-}
-
-static void import_marks(char *input_file)
-{
-	char line[512];
-	FILE *f = fopen(input_file, "r");
-	if (!f)
-		die_errno("cannot read '%s'", input_file);
-
-	while (fgets(line, sizeof(line), f)) {
-		uint32_t mark;
-		char *line_end, *mark_end;
-		unsigned char sha1[20];
-		struct object *object;
-
-		line_end = strchr(line, '\n');
-		if (line[0] != ':' || !line_end)
-			die("corrupt mark line: %s", line);
-		*line_end = '\0';
-
-		mark = strtoumax(line + 1, &mark_end, 10);
-		if (!mark || mark_end == line + 1
-			|| *mark_end != ' ' || get_sha1(mark_end + 1, sha1))
-			die("corrupt mark line: %s", line);
-
-		object = parse_object(sha1);
-		if (!object)
-			die ("Could not read blob %s", sha1_to_hex(sha1));
-
-		if (object->flags & SHOWN)
-			error("Object %s already has a mark", sha1);
-
-		mark_object(object, mark);
-		if (last_idnum < mark)
-			last_idnum = mark;
-
-		object->flags |= SHOWN;
-	}
-	fclose(f);
-}
-
-int cmd_fast_export(int argc, const char **argv, const char *prefix)
-{
-	struct rev_info revs;
-	struct object_array commits = { 0, 0, NULL };
-	struct string_list extra_refs = { NULL, 0, 0, 0 };
-	struct commit *commit;
-	char *export_filename = NULL, *import_filename = NULL;
-	struct option options[] = {
-		OPT_INTEGER(0, "progress", &progress,
-			    "show progress after <n> objects"),
-		OPT_CALLBACK(0, "signed-tags", &signed_tag_mode, "mode",
-			     "select handling of signed tags",
-			     parse_opt_signed_tag_mode),
-		OPT_CALLBACK(0, "tag-of-filtered-object", &tag_of_filtered_mode, "mode",
-			     "select handling of tags that tag filtered objects",
-			     parse_opt_tag_of_filtered_mode),
-		OPT_STRING(0, "export-marks", &export_filename, "FILE",
-			     "Dump marks to this file"),
-		OPT_STRING(0, "import-marks", &import_filename, "FILE",
-			     "Import marks from this file"),
-		OPT_BOOLEAN(0, "fake-missing-tagger", &fake_missing_tagger,
-			     "Fake a tagger when tags lack one"),
-		{ OPTION_NEGBIT, 0, "data", &no_data, NULL,
-			"Skip output of blob data",
-			PARSE_OPT_NOARG | PARSE_OPT_NEGHELP, NULL, 1 },
-		OPT_END()
-	};
-
-	if (argc == 1)
-		usage_with_options (fast_export_usage, options);
-
-	/* we handle encodings */
-	git_config(git_default_config, NULL);
-
-	init_revisions(&revs, prefix);
-	revs.topo_order = 1;
-	revs.show_source = 1;
-	revs.rewrite_parents = 1;
-	argc = setup_revisions(argc, argv, &revs, NULL);
-	argc = parse_options(argc, argv, prefix, options, fast_export_usage, 0);
-	if (argc > 1)
-		usage_with_options (fast_export_usage, options);
-
-	if (import_filename)
-		import_marks(import_filename);
-
-	get_tags_and_duplicates(&revs.pending, &extra_refs);
-
-	if (prepare_revision_walk(&revs))
-		die("revision walk setup failed");
-	revs.diffopt.format_callback = show_filemodify;
-	DIFF_OPT_SET(&revs.diffopt, RECURSIVE);
-	while ((commit = get_revision(&revs))) {
-		if (has_unshown_parent(commit)) {
-			add_object_array(&commit->object, NULL, &commits);
-		}
-		else {
-			handle_commit(commit, &revs);
-			handle_tail(&commits, &revs);
-		}
-	}
-
-	handle_tags_and_duplicates(&extra_refs);
-
-	if (export_filename)
-		export_marks(export_filename);
-
-	return 0;
-}
diff --git a/builtin-fetch--tool.c b/builtin-fetch--tool.c
deleted file mode 100644
index 3dbdf7a..0000000
--- a/builtin-fetch--tool.c
+++ /dev/null
@@ -1,574 +0,0 @@
-#include "builtin.h"
-#include "cache.h"
-#include "refs.h"
-#include "commit.h"
-#include "sigchain.h"
-
-static char *get_stdin(void)
-{
-	struct strbuf buf = STRBUF_INIT;
-	if (strbuf_read(&buf, 0, 1024) < 0) {
-		die_errno("error reading standard input");
-	}
-	return strbuf_detach(&buf, NULL);
-}
-
-static void show_new(enum object_type type, unsigned char *sha1_new)
-{
-	fprintf(stderr, "  %s: %s\n", typename(type),
-		find_unique_abbrev(sha1_new, DEFAULT_ABBREV));
-}
-
-static int update_ref_env(const char *action,
-		      const char *refname,
-		      unsigned char *sha1,
-		      unsigned char *oldval)
-{
-	char msg[1024];
-	const char *rla = getenv("GIT_REFLOG_ACTION");
-
-	if (!rla)
-		rla = "(reflog update)";
-	if (snprintf(msg, sizeof(msg), "%s: %s", rla, action) >= sizeof(msg))
-		warning("reflog message too long: %.*s...", 50, msg);
-	return update_ref(msg, refname, sha1, oldval, 0, QUIET_ON_ERR);
-}
-
-static int update_local_ref(const char *name,
-			    const char *new_head,
-			    const char *note,
-			    int verbose, int force)
-{
-	unsigned char sha1_old[20], sha1_new[20];
-	char oldh[41], newh[41];
-	struct commit *current, *updated;
-	enum object_type type;
-
-	if (get_sha1_hex(new_head, sha1_new))
-		die("malformed object name %s", new_head);
-
-	type = sha1_object_info(sha1_new, NULL);
-	if (type < 0)
-		die("object %s not found", new_head);
-
-	if (!*name) {
-		/* Not storing */
-		if (verbose) {
-			fprintf(stderr, "* fetched %s\n", note);
-			show_new(type, sha1_new);
-		}
-		return 0;
-	}
-
-	if (get_sha1(name, sha1_old)) {
-		const char *msg;
-	just_store:
-		/* new ref */
-		if (!strncmp(name, "refs/tags/", 10))
-			msg = "storing tag";
-		else
-			msg = "storing head";
-		fprintf(stderr, "* %s: storing %s\n",
-			name, note);
-		show_new(type, sha1_new);
-		return update_ref_env(msg, name, sha1_new, NULL);
-	}
-
-	if (!hashcmp(sha1_old, sha1_new)) {
-		if (verbose) {
-			fprintf(stderr, "* %s: same as %s\n", name, note);
-			show_new(type, sha1_new);
-		}
-		return 0;
-	}
-
-	if (!strncmp(name, "refs/tags/", 10)) {
-		fprintf(stderr, "* %s: updating with %s\n", name, note);
-		show_new(type, sha1_new);
-		return update_ref_env("updating tag", name, sha1_new, NULL);
-	}
-
-	current = lookup_commit_reference(sha1_old);
-	updated = lookup_commit_reference(sha1_new);
-	if (!current || !updated)
-		goto just_store;
-
-	strcpy(oldh, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));
-	strcpy(newh, find_unique_abbrev(sha1_new, DEFAULT_ABBREV));
-
-	if (in_merge_bases(current, &updated, 1)) {
-		fprintf(stderr, "* %s: fast forward to %s\n",
-			name, note);
-		fprintf(stderr, "  old..new: %s..%s\n", oldh, newh);
-		return update_ref_env("fast forward", name, sha1_new, sha1_old);
-	}
-	if (!force) {
-		fprintf(stderr,
-			"* %s: not updating to non-fast forward %s\n",
-			name, note);
-		fprintf(stderr,
-			"  old...new: %s...%s\n", oldh, newh);
-		return 1;
-	}
-	fprintf(stderr,
-		"* %s: forcing update to non-fast forward %s\n",
-		name, note);
-	fprintf(stderr, "  old...new: %s...%s\n", oldh, newh);
-	return update_ref_env("forced-update", name, sha1_new, sha1_old);
-}
-
-static int append_fetch_head(FILE *fp,
-			     const char *head, const char *remote,
-			     const char *remote_name, const char *remote_nick,
-			     const char *local_name, int not_for_merge,
-			     int verbose, int force)
-{
-	struct commit *commit;
-	int remote_len, i, note_len;
-	unsigned char sha1[20];
-	char note[1024];
-	const char *what, *kind;
-
-	if (get_sha1(head, sha1))
-		return error("Not a valid object name: %s", head);
-	commit = lookup_commit_reference_gently(sha1, 1);
-	if (!commit)
-		not_for_merge = 1;
-
-	if (!strcmp(remote_name, "HEAD")) {
-		kind = "";
-		what = "";
-	}
-	else if (!strncmp(remote_name, "refs/heads/", 11)) {
-		kind = "branch";
-		what = remote_name + 11;
-	}
-	else if (!strncmp(remote_name, "refs/tags/", 10)) {
-		kind = "tag";
-		what = remote_name + 10;
-	}
-	else if (!strncmp(remote_name, "refs/remotes/", 13)) {
-		kind = "remote branch";
-		what = remote_name + 13;
-	}
-	else {
-		kind = "";
-		what = remote_name;
-	}
-
-	remote_len = strlen(remote);
-	for (i = remote_len - 1; remote[i] == '/' && 0 <= i; i--)
-		;
-	remote_len = i + 1;
-	if (4 < i && !strncmp(".git", remote + i - 3, 4))
-		remote_len = i - 3;
-
-	note_len = 0;
-	if (*what) {
-		if (*kind)
-			note_len += sprintf(note + note_len, "%s ", kind);
-		note_len += sprintf(note + note_len, "'%s' of ", what);
-	}
-	note_len += sprintf(note + note_len, "%.*s", remote_len, remote);
-	fprintf(fp, "%s\t%s\t%s\n",
-		sha1_to_hex(commit ? commit->object.sha1 : sha1),
-		not_for_merge ? "not-for-merge" : "",
-		note);
-	return update_local_ref(local_name, head, note, verbose, force);
-}
-
-static char *keep;
-static void remove_keep(void)
-{
-	if (keep && *keep)
-		unlink(keep);
-}
-
-static void remove_keep_on_signal(int signo)
-{
-	remove_keep();
-	sigchain_pop(signo);
-	raise(signo);
-}
-
-static char *find_local_name(const char *remote_name, const char *refs,
-			     int *force_p, int *not_for_merge_p)
-{
-	const char *ref = refs;
-	int len = strlen(remote_name);
-
-	while (ref) {
-		const char *next;
-		int single_force, not_for_merge;
-
-		while (*ref == '\n')
-			ref++;
-		if (!*ref)
-			break;
-		next = strchr(ref, '\n');
-
-		single_force = not_for_merge = 0;
-		if (*ref == '+') {
-			single_force = 1;
-			ref++;
-		}
-		if (*ref == '.') {
-			not_for_merge = 1;
-			ref++;
-			if (*ref == '+') {
-				single_force = 1;
-				ref++;
-			}
-		}
-		if (!strncmp(remote_name, ref, len) && ref[len] == ':') {
-			const char *local_part = ref + len + 1;
-			int retlen;
-
-			if (!next)
-				retlen = strlen(local_part);
-			else
-				retlen = next - local_part;
-			*force_p = single_force;
-			*not_for_merge_p = not_for_merge;
-			return xmemdupz(local_part, retlen);
-		}
-		ref = next;
-	}
-	return NULL;
-}
-
-static int fetch_native_store(FILE *fp,
-			      const char *remote,
-			      const char *remote_nick,
-			      const char *refs,
-			      int verbose, int force)
-{
-	char buffer[1024];
-	int err = 0;
-
-	sigchain_push_common(remove_keep_on_signal);
-	atexit(remove_keep);
-
-	while (fgets(buffer, sizeof(buffer), stdin)) {
-		int len;
-		char *cp;
-		char *local_name;
-		int single_force, not_for_merge;
-
-		for (cp = buffer; *cp && !isspace(*cp); cp++)
-			;
-		if (*cp)
-			*cp++ = 0;
-		len = strlen(cp);
-		if (len && cp[len-1] == '\n')
-			cp[--len] = 0;
-		if (!strcmp(buffer, "failed"))
-			die("Fetch failure: %s", remote);
-		if (!strcmp(buffer, "pack"))
-			continue;
-		if (!strcmp(buffer, "keep")) {
-			char *od = get_object_directory();
-			int len = strlen(od) + strlen(cp) + 50;
-			keep = xmalloc(len);
-			sprintf(keep, "%s/pack/pack-%s.keep", od, cp);
-			continue;
-		}
-
-		local_name = find_local_name(cp, refs,
-					     &single_force, &not_for_merge);
-		if (!local_name)
-			continue;
-		err |= append_fetch_head(fp,
-					 buffer, remote, cp, remote_nick,
-					 local_name, not_for_merge,
-					 verbose, force || single_force);
-	}
-	return err;
-}
-
-static int parse_reflist(const char *reflist)
-{
-	const char *ref;
-
-	printf("refs='");
-	for (ref = reflist; ref; ) {
-		const char *next;
-		while (*ref && isspace(*ref))
-			ref++;
-		if (!*ref)
-			break;
-		for (next = ref; *next && !isspace(*next); next++)
-			;
-		printf("\n%.*s", (int)(next - ref), ref);
-		ref = next;
-	}
-	printf("'\n");
-
-	printf("rref='");
-	for (ref = reflist; ref; ) {
-		const char *next, *colon;
-		while (*ref && isspace(*ref))
-			ref++;
-		if (!*ref)
-			break;
-		for (next = ref; *next && !isspace(*next); next++)
-			;
-		if (*ref == '.')
-			ref++;
-		if (*ref == '+')
-			ref++;
-		colon = strchr(ref, ':');
-		putchar('\n');
-		printf("%.*s", (int)((colon ? colon : next) - ref), ref);
-		ref = next;
-	}
-	printf("'\n");
-	return 0;
-}
-
-static int expand_refs_wildcard(const char *ls_remote_result, int numrefs,
-				const char **refs)
-{
-	int i, matchlen, replacelen;
-	int found_one = 0;
-	const char *remote = *refs++;
-	numrefs--;
-
-	if (numrefs == 0) {
-		fprintf(stderr, "Nothing specified for fetching with remote.%s.fetch\n",
-			remote);
-		printf("empty\n");
-	}
-
-	for (i = 0; i < numrefs; i++) {
-		const char *ref = refs[i];
-		const char *lref = ref;
-		const char *colon;
-		const char *tail;
-		const char *ls;
-		const char *next;
-
-		if (*lref == '+')
-			lref++;
-		colon = strchr(lref, ':');
-		tail = lref + strlen(lref);
-		if (!(colon &&
-		      2 < colon - lref &&
-		      colon[-1] == '*' &&
-		      colon[-2] == '/' &&
-		      2 < tail - (colon + 1) &&
-		      tail[-1] == '*' &&
-		      tail[-2] == '/')) {
-			/* not a glob */
-			if (!found_one++)
-				printf("explicit\n");
-			printf("%s\n", ref);
-			continue;
-		}
-
-		/* glob */
-		if (!found_one++)
-			printf("glob\n");
-
-		/* lref to colon-2 is remote hierarchy name;
-		 * colon+1 to tail-2 is local.
-		 */
-		matchlen = (colon-1) - lref;
-		replacelen = (tail-1) - (colon+1);
-		for (ls = ls_remote_result; ls; ls = next) {
-			const char *eol;
-			unsigned char sha1[20];
-			int namelen;
-
-			while (*ls && isspace(*ls))
-				ls++;
-			next = strchr(ls, '\n');
-			eol = !next ? (ls + strlen(ls)) : next;
-			if (!memcmp("^{}", eol-3, 3))
-				continue;
-			if (eol - ls < 40)
-				continue;
-			if (get_sha1_hex(ls, sha1))
-				continue;
-			ls += 40;
-			while (ls < eol && isspace(*ls))
-				ls++;
-			/* ls to next (or eol) is the name.
-			 * is it identical to lref to colon-2?
-			 */
-			if ((eol - ls) <= matchlen ||
-			    strncmp(ls, lref, matchlen))
-				continue;
-
-			/* Yes, it is a match */
-			namelen = eol - ls;
-			if (lref != ref)
-				putchar('+');
-			printf("%.*s:%.*s%.*s\n",
-			       namelen, ls,
-			       replacelen, colon + 1,
-			       namelen - matchlen, ls + matchlen);
-		}
-	}
-	return 0;
-}
-
-static int pick_rref(int sha1_only, const char *rref, const char *ls_remote_result)
-{
-	int err = 0;
-	int lrr_count = lrr_count, i, pass;
-	const char *cp;
-	struct lrr {
-		const char *line;
-		const char *name;
-		int namelen;
-		int shown;
-	} *lrr_list = lrr_list;
-
-	for (pass = 0; pass < 2; pass++) {
-		/* pass 0 counts and allocates, pass 1 fills... */
-		cp = ls_remote_result;
-		i = 0;
-		while (1) {
-			const char *np;
-			while (*cp && isspace(*cp))
-				cp++;
-			if (!*cp)
-				break;
-			np = strchrnul(cp, '\n');
-			if (pass) {
-				lrr_list[i].line = cp;
-				lrr_list[i].name = cp + 41;
-				lrr_list[i].namelen = np - (cp + 41);
-			}
-			i++;
-			cp = np;
-		}
-		if (!pass) {
-			lrr_count = i;
-			lrr_list = xcalloc(lrr_count, sizeof(*lrr_list));
-		}
-	}
-
-	while (1) {
-		const char *next;
-		int rreflen;
-		int i;
-
-		while (*rref && isspace(*rref))
-			rref++;
-		if (!*rref)
-			break;
-		next = strchrnul(rref, '\n');
-		rreflen = next - rref;
-
-		for (i = 0; i < lrr_count; i++) {
-			struct lrr *lrr = &(lrr_list[i]);
-
-			if (rreflen == lrr->namelen &&
-			    !memcmp(lrr->name, rref, rreflen)) {
-				if (!lrr->shown)
-					printf("%.*s\n",
-					       sha1_only ? 40 : lrr->namelen + 41,
-					       lrr->line);
-				lrr->shown = 1;
-				break;
-			}
-		}
-		if (lrr_count <= i) {
-			error("pick-rref: %.*s not found", rreflen, rref);
-			err = 1;
-		}
-		rref = next;
-	}
-	free(lrr_list);
-	return err;
-}
-
-int cmd_fetch__tool(int argc, const char **argv, const char *prefix)
-{
-	int verbose = 0;
-	int force = 0;
-	int sopt = 0;
-
-	while (1 < argc) {
-		const char *arg = argv[1];
-		if (!strcmp("-v", arg))
-			verbose = 1;
-		else if (!strcmp("-f", arg))
-			force = 1;
-		else if (!strcmp("-s", arg))
-			sopt = 1;
-		else
-			break;
-		argc--;
-		argv++;
-	}
-
-	if (argc <= 1)
-		return error("Missing subcommand");
-
-	if (!strcmp("append-fetch-head", argv[1])) {
-		int result;
-		FILE *fp;
-		char *filename;
-
-		if (argc != 8)
-			return error("append-fetch-head takes 6 args");
-		filename = git_path("FETCH_HEAD");
-		fp = fopen(filename, "a");
-		if (!fp)
-			return error("cannot open %s: %s\n", filename, strerror(errno));
-		result = append_fetch_head(fp, argv[2], argv[3],
-					   argv[4], argv[5],
-					   argv[6], !!argv[7][0],
-					   verbose, force);
-		fclose(fp);
-		return result;
-	}
-	if (!strcmp("native-store", argv[1])) {
-		int result;
-		FILE *fp;
-		char *filename;
-
-		if (argc != 5)
-			return error("fetch-native-store takes 3 args");
-		filename = git_path("FETCH_HEAD");
-		fp = fopen(filename, "a");
-		if (!fp)
-			return error("cannot open %s: %s\n", filename, strerror(errno));
-		result = fetch_native_store(fp, argv[2], argv[3], argv[4],
-					    verbose, force);
-		fclose(fp);
-		return result;
-	}
-	if (!strcmp("parse-reflist", argv[1])) {
-		const char *reflist;
-		if (argc != 3)
-			return error("parse-reflist takes 1 arg");
-		reflist = argv[2];
-		if (!strcmp(reflist, "-"))
-			reflist = get_stdin();
-		return parse_reflist(reflist);
-	}
-	if (!strcmp("pick-rref", argv[1])) {
-		const char *ls_remote_result;
-		if (argc != 4)
-			return error("pick-rref takes 2 args");
-		ls_remote_result = argv[3];
-		if (!strcmp(ls_remote_result, "-"))
-			ls_remote_result = get_stdin();
-		return pick_rref(sopt, argv[2], ls_remote_result);
-	}
-	if (!strcmp("expand-refs-wildcard", argv[1])) {
-		const char *reflist;
-		if (argc < 4)
-			return error("expand-refs-wildcard takes at least 2 args");
-		reflist = argv[2];
-		if (!strcmp(reflist, "-"))
-			reflist = get_stdin();
-		return expand_refs_wildcard(reflist, argc - 3, argv + 3);
-	}
-
-	return error("Unknown subcommand: %s", argv[1]);
-}
diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c
deleted file mode 100644
index 629735f..0000000
--- a/builtin-fetch-pack.c
+++ /dev/null
@@ -1,839 +0,0 @@
-#include "cache.h"
-#include "refs.h"
-#include "pkt-line.h"
-#include "commit.h"
-#include "tag.h"
-#include "exec_cmd.h"
-#include "pack.h"
-#include "sideband.h"
-#include "fetch-pack.h"
-#include "remote.h"
-#include "run-command.h"
-
-static int transfer_unpack_limit = -1;
-static int fetch_unpack_limit = -1;
-static int unpack_limit = 100;
-static int prefer_ofs_delta = 1;
-static struct fetch_pack_args args = {
-	/* .uploadpack = */ "git-upload-pack",
-};
-
-static const char fetch_pack_usage[] =
-"git fetch-pack [--all] [--quiet|-q] [--keep|-k] [--thin] [--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] [--no-progress] [-v] [<host>:]<directory> [<refs>...]";
-
-#define COMPLETE	(1U << 0)
-#define COMMON		(1U << 1)
-#define COMMON_REF	(1U << 2)
-#define SEEN		(1U << 3)
-#define POPPED		(1U << 4)
-
-static int marked;
-
-/*
- * After sending this many "have"s if we do not get any new ACK , we
- * give up traversing our history.
- */
-#define MAX_IN_VAIN 256
-
-static struct commit_list *rev_list;
-static int non_common_revs, multi_ack, use_sideband;
-
-static void rev_list_push(struct commit *commit, int mark)
-{
-	if (!(commit->object.flags & mark)) {
-		commit->object.flags |= mark;
-
-		if (!(commit->object.parsed))
-			if (parse_commit(commit))
-				return;
-
-		insert_by_date(commit, &rev_list);
-
-		if (!(commit->object.flags & COMMON))
-			non_common_revs++;
-	}
-}
-
-static int rev_list_insert_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
-{
-	struct object *o = deref_tag(parse_object(sha1), path, 0);
-
-	if (o && o->type == OBJ_COMMIT)
-		rev_list_push((struct commit *)o, SEEN);
-
-	return 0;
-}
-
-static int clear_marks(const char *path, const unsigned char *sha1, int flag, void *cb_data)
-{
-	struct object *o = deref_tag(parse_object(sha1), path, 0);
-
-	if (o && o->type == OBJ_COMMIT)
-		clear_commit_marks((struct commit *)o,
-				   COMMON | COMMON_REF | SEEN | POPPED);
-	return 0;
-}
-
-/*
-   This function marks a rev and its ancestors as common.
-   In some cases, it is desirable to mark only the ancestors (for example
-   when only the server does not yet know that they are common).
-*/
-
-static void mark_common(struct commit *commit,
-		int ancestors_only, int dont_parse)
-{
-	if (commit != NULL && !(commit->object.flags & COMMON)) {
-		struct object *o = (struct object *)commit;
-
-		if (!ancestors_only)
-			o->flags |= COMMON;
-
-		if (!(o->flags & SEEN))
-			rev_list_push(commit, SEEN);
-		else {
-			struct commit_list *parents;
-
-			if (!ancestors_only && !(o->flags & POPPED))
-				non_common_revs--;
-			if (!o->parsed && !dont_parse)
-				if (parse_commit(commit))
-					return;
-
-			for (parents = commit->parents;
-					parents;
-					parents = parents->next)
-				mark_common(parents->item, 0, dont_parse);
-		}
-	}
-}
-
-/*
-  Get the next rev to send, ignoring the common.
-*/
-
-static const unsigned char *get_rev(void)
-{
-	struct commit *commit = NULL;
-
-	while (commit == NULL) {
-		unsigned int mark;
-		struct commit_list *parents;
-
-		if (rev_list == NULL || non_common_revs == 0)
-			return NULL;
-
-		commit = rev_list->item;
-		if (!commit->object.parsed)
-			parse_commit(commit);
-		parents = commit->parents;
-
-		commit->object.flags |= POPPED;
-		if (!(commit->object.flags & COMMON))
-			non_common_revs--;
-
-		if (commit->object.flags & COMMON) {
-			/* do not send "have", and ignore ancestors */
-			commit = NULL;
-			mark = COMMON | SEEN;
-		} else if (commit->object.flags & COMMON_REF)
-			/* send "have", and ignore ancestors */
-			mark = COMMON | SEEN;
-		else
-			/* send "have", also for its ancestors */
-			mark = SEEN;
-
-		while (parents) {
-			if (!(parents->item->object.flags & SEEN))
-				rev_list_push(parents->item, mark);
-			if (mark & COMMON)
-				mark_common(parents->item, 1, 0);
-			parents = parents->next;
-		}
-
-		rev_list = rev_list->next;
-	}
-
-	return commit->object.sha1;
-}
-
-static int find_common(int fd[2], unsigned char *result_sha1,
-		       struct ref *refs)
-{
-	int fetching;
-	int count = 0, flushes = 0, retval;
-	const unsigned char *sha1;
-	unsigned in_vain = 0;
-	int got_continue = 0;
-
-	if (marked)
-		for_each_ref(clear_marks, NULL);
-	marked = 1;
-
-	for_each_ref(rev_list_insert_ref, NULL);
-
-	fetching = 0;
-	for ( ; refs ; refs = refs->next) {
-		unsigned char *remote = refs->old_sha1;
-		struct object *o;
-
-		/*
-		 * If that object is complete (i.e. it is an ancestor of a
-		 * local ref), we tell them we have it but do not have to
-		 * tell them about its ancestors, which they already know
-		 * about.
-		 *
-		 * We use lookup_object here because we are only
-		 * interested in the case we *know* the object is
-		 * reachable and we have already scanned it.
-		 */
-		if (((o = lookup_object(remote)) != NULL) &&
-				(o->flags & COMPLETE)) {
-			continue;
-		}
-
-		if (!fetching)
-			packet_write(fd[1], "want %s%s%s%s%s%s%s%s\n",
-				     sha1_to_hex(remote),
-				     (multi_ack ? " multi_ack" : ""),
-				     (use_sideband == 2 ? " side-band-64k" : ""),
-				     (use_sideband == 1 ? " side-band" : ""),
-				     (args.use_thin_pack ? " thin-pack" : ""),
-				     (args.no_progress ? " no-progress" : ""),
-				     (args.include_tag ? " include-tag" : ""),
-				     (prefer_ofs_delta ? " ofs-delta" : ""));
-		else
-			packet_write(fd[1], "want %s\n", sha1_to_hex(remote));
-		fetching++;
-	}
-	if (is_repository_shallow())
-		write_shallow_commits(fd[1], 1);
-	if (args.depth > 0)
-		packet_write(fd[1], "deepen %d", args.depth);
-	packet_flush(fd[1]);
-	if (!fetching)
-		return 1;
-
-	if (args.depth > 0) {
-		char line[1024];
-		unsigned char sha1[20];
-
-		while (packet_read_line(fd[0], line, sizeof(line))) {
-			if (!prefixcmp(line, "shallow ")) {
-				if (get_sha1_hex(line + 8, sha1))
-					die("invalid shallow line: %s", line);
-				register_shallow(sha1);
-				continue;
-			}
-			if (!prefixcmp(line, "unshallow ")) {
-				if (get_sha1_hex(line + 10, sha1))
-					die("invalid unshallow line: %s", line);
-				if (!lookup_object(sha1))
-					die("object not found: %s", line);
-				/* make sure that it is parsed as shallow */
-				if (!parse_object(sha1))
-					die("error in object: %s", line);
-				if (unregister_shallow(sha1))
-					die("no shallow found: %s", line);
-				continue;
-			}
-			die("expected shallow/unshallow, got %s", line);
-		}
-	}
-
-	flushes = 0;
-	retval = -1;
-	while ((sha1 = get_rev())) {
-		packet_write(fd[1], "have %s\n", sha1_to_hex(sha1));
-		if (args.verbose)
-			fprintf(stderr, "have %s\n", sha1_to_hex(sha1));
-		in_vain++;
-		if (!(31 & ++count)) {
-			int ack;
-
-			packet_flush(fd[1]);
-			flushes++;
-
-			/*
-			 * We keep one window "ahead" of the other side, and
-			 * will wait for an ACK only on the next one
-			 */
-			if (count == 32)
-				continue;
-
-			do {
-				ack = get_ack(fd[0], result_sha1);
-				if (args.verbose && ack)
-					fprintf(stderr, "got ack %d %s\n", ack,
-							sha1_to_hex(result_sha1));
-				if (ack == 1) {
-					flushes = 0;
-					multi_ack = 0;
-					retval = 0;
-					goto done;
-				} else if (ack == 2) {
-					struct commit *commit =
-						lookup_commit(result_sha1);
-					mark_common(commit, 0, 1);
-					retval = 0;
-					in_vain = 0;
-					got_continue = 1;
-				}
-			} while (ack);
-			flushes--;
-			if (got_continue && MAX_IN_VAIN < in_vain) {
-				if (args.verbose)
-					fprintf(stderr, "giving up\n");
-				break; /* give up */
-			}
-		}
-	}
-done:
-	packet_write(fd[1], "done\n");
-	if (args.verbose)
-		fprintf(stderr, "done\n");
-	if (retval != 0) {
-		multi_ack = 0;
-		flushes++;
-	}
-	while (flushes || multi_ack) {
-		int ack = get_ack(fd[0], result_sha1);
-		if (ack) {
-			if (args.verbose)
-				fprintf(stderr, "got ack (%d) %s\n", ack,
-					sha1_to_hex(result_sha1));
-			if (ack == 1)
-				return 0;
-			multi_ack = 1;
-			continue;
-		}
-		flushes--;
-	}
-	/* it is no error to fetch into a completely empty repo */
-	return count ? retval : 0;
-}
-
-static struct commit_list *complete;
-
-static int mark_complete(const char *path, const unsigned char *sha1, int flag, void *cb_data)
-{
-	struct object *o = parse_object(sha1);
-
-	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 == OBJ_COMMIT) {
-		struct commit *commit = (struct commit *)o;
-		commit->object.flags |= COMPLETE;
-		insert_by_date(commit, &complete);
-	}
-	return 0;
-}
-
-static void mark_recent_complete_commits(unsigned long cutoff)
-{
-	while (complete && cutoff <= complete->item->date) {
-		if (args.verbose)
-			fprintf(stderr, "Marking %s as complete\n",
-				sha1_to_hex(complete->item->object.sha1));
-		pop_most_recent_commit(&complete, COMPLETE);
-	}
-}
-
-static void filter_refs(struct ref **refs, int nr_match, char **match)
-{
-	struct ref **return_refs;
-	struct ref *newlist = NULL;
-	struct ref **newtail = &newlist;
-	struct ref *ref, *next;
-	struct ref *fastarray[32];
-
-	if (nr_match && !args.fetch_all) {
-		if (ARRAY_SIZE(fastarray) < nr_match)
-			return_refs = xcalloc(nr_match, sizeof(struct ref *));
-		else {
-			return_refs = fastarray;
-			memset(return_refs, 0, sizeof(struct ref *) * nr_match);
-		}
-	}
-	else
-		return_refs = NULL;
-
-	for (ref = *refs; ref; ref = next) {
-		next = ref->next;
-		if (!memcmp(ref->name, "refs/", 5) &&
-		    check_ref_format(ref->name + 5))
-			; /* trash */
-		else if (args.fetch_all &&
-			 (!args.depth || prefixcmp(ref->name, "refs/tags/") )) {
-			*newtail = ref;
-			ref->next = NULL;
-			newtail = &ref->next;
-			continue;
-		}
-		else {
-			int order = path_match(ref->name, nr_match, match);
-			if (order) {
-				return_refs[order-1] = ref;
-				continue; /* we will link it later */
-			}
-		}
-		free(ref);
-	}
-
-	if (!args.fetch_all) {
-		int i;
-		for (i = 0; i < nr_match; i++) {
-			ref = return_refs[i];
-			if (ref) {
-				*newtail = ref;
-				ref->next = NULL;
-				newtail = &ref->next;
-			}
-		}
-		if (return_refs != fastarray)
-			free(return_refs);
-	}
-	*refs = newlist;
-}
-
-static int everything_local(struct ref **refs, int nr_match, char **match)
-{
-	struct ref *ref;
-	int retval;
-	unsigned long cutoff = 0;
-
-	save_commit_buffer = 0;
-
-	for (ref = *refs; ref; ref = ref->next) {
-		struct object *o;
-
-		o = parse_object(ref->old_sha1);
-		if (!o)
-			continue;
-
-		/* We already have it -- which may mean that we were
-		 * in sync with the other side at some time after
-		 * that (it is OK if we guess wrong here).
-		 */
-		if (o->type == OBJ_COMMIT) {
-			struct commit *commit = (struct commit *)o;
-			if (!cutoff || cutoff < commit->date)
-				cutoff = commit->date;
-		}
-	}
-
-	if (!args.depth) {
-		for_each_ref(mark_complete, NULL);
-		if (cutoff)
-			mark_recent_complete_commits(cutoff);
-	}
-
-	/*
-	 * Mark all complete remote refs as common refs.
-	 * Don't mark them common yet; the server has to be told so first.
-	 */
-	for (ref = *refs; ref; ref = ref->next) {
-		struct object *o = deref_tag(lookup_object(ref->old_sha1),
-					     NULL, 0);
-
-		if (!o || o->type != OBJ_COMMIT || !(o->flags & COMPLETE))
-			continue;
-
-		if (!(o->flags & SEEN)) {
-			rev_list_push((struct commit *)o, COMMON_REF | SEEN);
-
-			mark_common((struct commit *)o, 1, 1);
-		}
-	}
-
-	filter_refs(refs, nr_match, match);
-
-	for (retval = 1, ref = *refs; ref ; ref = ref->next) {
-		const unsigned char *remote = ref->old_sha1;
-		unsigned char local[20];
-		struct object *o;
-
-		o = lookup_object(remote);
-		if (!o || !(o->flags & COMPLETE)) {
-			retval = 0;
-			if (!args.verbose)
-				continue;
-			fprintf(stderr,
-				"want %s (%s)\n", sha1_to_hex(remote),
-				ref->name);
-			continue;
-		}
-
-		hashcpy(ref->new_sha1, local);
-		if (!args.verbose)
-			continue;
-		fprintf(stderr,
-			"already have %s (%s)\n", sha1_to_hex(remote),
-			ref->name);
-	}
-	return retval;
-}
-
-static int sideband_demux(int fd, void *data)
-{
-	int *xd = data;
-
-	int ret = recv_sideband("fetch-pack", xd[0], fd);
-	close(fd);
-	return ret;
-}
-
-static int get_pack(int xd[2], char **pack_lockfile)
-{
-	struct async demux;
-	const char *argv[20];
-	char keep_arg[256];
-	char hdr_arg[256];
-	const char **av;
-	int do_keep = args.keep_pack;
-	struct child_process cmd;
-
-	memset(&demux, 0, sizeof(demux));
-	if (use_sideband) {
-		/* xd[] is talking with upload-pack; subprocess reads from
-		 * xd[0], spits out band#2 to stderr, and feeds us band#1
-		 * through demux->out.
-		 */
-		demux.proc = sideband_demux;
-		demux.data = xd;
-		if (start_async(&demux))
-			die("fetch-pack: unable to fork off sideband"
-			    " demultiplexer");
-	}
-	else
-		demux.out = xd[0];
-
-	memset(&cmd, 0, sizeof(cmd));
-	cmd.argv = argv;
-	av = argv;
-	*hdr_arg = 0;
-	if (!args.keep_pack && unpack_limit) {
-		struct pack_header header;
-
-		if (read_pack_header(demux.out, &header))
-			die("protocol error: bad pack header");
-		snprintf(hdr_arg, sizeof(hdr_arg),
-			 "--pack_header=%"PRIu32",%"PRIu32,
-			 ntohl(header.hdr_version), ntohl(header.hdr_entries));
-		if (ntohl(header.hdr_entries) < unpack_limit)
-			do_keep = 0;
-		else
-			do_keep = 1;
-	}
-
-	if (do_keep) {
-		if (pack_lockfile)
-			cmd.out = -1;
-		*av++ = "index-pack";
-		*av++ = "--stdin";
-		if (!args.quiet && !args.no_progress)
-			*av++ = "-v";
-		if (args.use_thin_pack)
-			*av++ = "--fix-thin";
-		if (args.lock_pack || unpack_limit) {
-			int s = sprintf(keep_arg,
-					"--keep=fetch-pack %"PRIuMAX " on ", (uintmax_t) getpid());
-			if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
-				strcpy(keep_arg + s, "localhost");
-			*av++ = keep_arg;
-		}
-	}
-	else {
-		*av++ = "unpack-objects";
-		if (args.quiet)
-			*av++ = "-q";
-	}
-	if (*hdr_arg)
-		*av++ = hdr_arg;
-	*av++ = NULL;
-
-	cmd.in = demux.out;
-	cmd.git_cmd = 1;
-	if (start_command(&cmd))
-		die("fetch-pack: unable to fork off %s", argv[0]);
-	if (do_keep && pack_lockfile) {
-		*pack_lockfile = index_pack_lockfile(cmd.out);
-		close(cmd.out);
-	}
-
-	if (finish_command(&cmd))
-		die("%s failed", argv[0]);
-	if (use_sideband && finish_async(&demux))
-		die("error in sideband demultiplexer");
-	return 0;
-}
-
-static struct ref *do_fetch_pack(int fd[2],
-		const struct ref *orig_ref,
-		int nr_match,
-		char **match,
-		char **pack_lockfile)
-{
-	struct ref *ref = copy_ref_list(orig_ref);
-	unsigned char sha1[20];
-
-	if (is_repository_shallow() && !server_supports("shallow"))
-		die("Server does not support shallow clients");
-	if (server_supports("multi_ack")) {
-		if (args.verbose)
-			fprintf(stderr, "Server supports multi_ack\n");
-		multi_ack = 1;
-	}
-	if (server_supports("side-band-64k")) {
-		if (args.verbose)
-			fprintf(stderr, "Server supports side-band-64k\n");
-		use_sideband = 2;
-	}
-	else if (server_supports("side-band")) {
-		if (args.verbose)
-			fprintf(stderr, "Server supports side-band\n");
-		use_sideband = 1;
-	}
-	if (server_supports("ofs-delta")) {
-		if (args.verbose)
-			fprintf(stderr, "Server supports ofs-delta\n");
-	} else
-		prefer_ofs_delta = 0;
-	if (everything_local(&ref, nr_match, match)) {
-		packet_flush(fd[1]);
-		goto all_done;
-	}
-	if (find_common(fd, sha1, ref) < 0)
-		if (!args.keep_pack)
-			/* When cloning, it is not unusual to have
-			 * no common commit.
-			 */
-			warning("no common commits");
-
-	if (get_pack(fd, pack_lockfile))
-		die("git fetch-pack: fetch failed.");
-
- all_done:
-	return ref;
-}
-
-static int remove_duplicates(int nr_heads, char **heads)
-{
-	int src, dst;
-
-	for (src = dst = 0; src < nr_heads; src++) {
-		/* If heads[src] is different from any of
-		 * heads[0..dst], push it in.
-		 */
-		int i;
-		for (i = 0; i < dst; i++) {
-			if (!strcmp(heads[i], heads[src]))
-				break;
-		}
-		if (i < dst)
-			continue;
-		if (src != dst)
-			heads[dst] = heads[src];
-		dst++;
-	}
-	return dst;
-}
-
-static int fetch_pack_config(const char *var, const char *value, void *cb)
-{
-	if (strcmp(var, "fetch.unpacklimit") == 0) {
-		fetch_unpack_limit = git_config_int(var, value);
-		return 0;
-	}
-
-	if (strcmp(var, "transfer.unpacklimit") == 0) {
-		transfer_unpack_limit = git_config_int(var, value);
-		return 0;
-	}
-
-	if (strcmp(var, "repack.usedeltabaseoffset") == 0) {
-		prefer_ofs_delta = git_config_bool(var, value);
-		return 0;
-	}
-
-	return git_default_config(var, value, cb);
-}
-
-static struct lock_file lock;
-
-static void fetch_pack_setup(void)
-{
-	static int did_setup;
-	if (did_setup)
-		return;
-	git_config(fetch_pack_config, NULL);
-	if (0 <= transfer_unpack_limit)
-		unpack_limit = transfer_unpack_limit;
-	else if (0 <= fetch_unpack_limit)
-		unpack_limit = fetch_unpack_limit;
-	did_setup = 1;
-}
-
-int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
-{
-	int i, ret, nr_heads;
-	struct ref *ref = NULL;
-	char *dest = NULL, **heads;
-	int fd[2];
-	struct child_process *conn;
-
-	nr_heads = 0;
-	heads = NULL;
-	for (i = 1; i < argc; i++) {
-		const char *arg = argv[i];
-
-		if (*arg == '-') {
-			if (!prefixcmp(arg, "--upload-pack=")) {
-				args.uploadpack = arg + 14;
-				continue;
-			}
-			if (!prefixcmp(arg, "--exec=")) {
-				args.uploadpack = arg + 7;
-				continue;
-			}
-			if (!strcmp("--quiet", arg) || !strcmp("-q", arg)) {
-				args.quiet = 1;
-				continue;
-			}
-			if (!strcmp("--keep", arg) || !strcmp("-k", arg)) {
-				args.lock_pack = args.keep_pack;
-				args.keep_pack = 1;
-				continue;
-			}
-			if (!strcmp("--thin", arg)) {
-				args.use_thin_pack = 1;
-				continue;
-			}
-			if (!strcmp("--include-tag", arg)) {
-				args.include_tag = 1;
-				continue;
-			}
-			if (!strcmp("--all", arg)) {
-				args.fetch_all = 1;
-				continue;
-			}
-			if (!strcmp("-v", arg)) {
-				args.verbose = 1;
-				continue;
-			}
-			if (!prefixcmp(arg, "--depth=")) {
-				args.depth = strtol(arg + 8, NULL, 0);
-				continue;
-			}
-			if (!strcmp("--no-progress", arg)) {
-				args.no_progress = 1;
-				continue;
-			}
-			usage(fetch_pack_usage);
-		}
-		dest = (char *)arg;
-		heads = (char **)(argv + i + 1);
-		nr_heads = argc - i - 1;
-		break;
-	}
-	if (!dest)
-		usage(fetch_pack_usage);
-
-	conn = git_connect(fd, (char *)dest, args.uploadpack,
-			   args.verbose ? CONNECT_VERBOSE : 0);
-	if (conn) {
-		get_remote_heads(fd[0], &ref, 0, NULL, 0, NULL);
-
-		ref = fetch_pack(&args, fd, conn, ref, dest, nr_heads, heads, NULL);
-		close(fd[0]);
-		close(fd[1]);
-		if (finish_connect(conn))
-			ref = NULL;
-	} else {
-		ref = NULL;
-	}
-	ret = !ref;
-
-	if (!ret && nr_heads) {
-		/* If the heads to pull were given, we should have
-		 * consumed all of them by matching the remote.
-		 * Otherwise, 'git fetch remote no-such-ref' would
-		 * silently succeed without issuing an error.
-		 */
-		for (i = 0; i < nr_heads; i++)
-			if (heads[i] && heads[i][0]) {
-				error("no such remote ref %s", heads[i]);
-				ret = 1;
-			}
-	}
-	while (ref) {
-		printf("%s %s\n",
-		       sha1_to_hex(ref->old_sha1), ref->name);
-		ref = ref->next;
-	}
-
-	return ret;
-}
-
-struct ref *fetch_pack(struct fetch_pack_args *my_args,
-		       int fd[], struct child_process *conn,
-		       const struct ref *ref,
-		const char *dest,
-		int nr_heads,
-		char **heads,
-		char **pack_lockfile)
-{
-	struct stat st;
-	struct ref *ref_cpy;
-
-	fetch_pack_setup();
-	if (&args != my_args)
-		memcpy(&args, my_args, sizeof(args));
-	if (args.depth > 0) {
-		if (stat(git_path("shallow"), &st))
-			st.st_mtime = 0;
-	}
-
-	if (heads && nr_heads)
-		nr_heads = remove_duplicates(nr_heads, heads);
-	if (!ref) {
-		packet_flush(fd[1]);
-		die("no matching remote head");
-	}
-	ref_cpy = do_fetch_pack(fd, ref, nr_heads, heads, pack_lockfile);
-
-	if (args.depth > 0) {
-		struct cache_time mtime;
-		char *shallow = git_path("shallow");
-		int fd;
-
-		mtime.sec = st.st_mtime;
-		mtime.nsec = ST_MTIME_NSEC(st);
-		if (stat(shallow, &st)) {
-			if (mtime.sec)
-				die("shallow file was removed during fetch");
-		} else if (st.st_mtime != mtime.sec
-#ifdef USE_NSEC
-				|| ST_MTIME_NSEC(st) != mtime.nsec
-#endif
-			  )
-			die("shallow file was changed during fetch");
-
-		fd = hold_lock_file_for_update(&lock, shallow,
-					       LOCK_DIE_ON_ERROR);
-		if (!write_shallow_commits(fd, 0)) {
-			unlink_or_warn(shallow);
-			rollback_lock_file(&lock);
-		} else {
-			commit_lock_file(&lock);
-		}
-	}
-
-	reprepare_packed_git();
-	return ref_cpy;
-}
diff --git a/builtin-fetch.c b/builtin-fetch.c
deleted file mode 100644
index 817dd6b..0000000
--- a/builtin-fetch.c
+++ /dev/null
@@ -1,707 +0,0 @@
-/*
- * "git fetch"
- */
-#include "cache.h"
-#include "refs.h"
-#include "commit.h"
-#include "builtin.h"
-#include "string-list.h"
-#include "remote.h"
-#include "transport.h"
-#include "run-command.h"
-#include "parse-options.h"
-#include "sigchain.h"
-
-static const char * const builtin_fetch_usage[] = {
-	"git fetch [options] [<repository> <refspec>...]",
-	NULL
-};
-
-enum {
-	TAGS_UNSET = 0,
-	TAGS_DEFAULT = 1,
-	TAGS_SET = 2
-};
-
-static int append, force, keep, update_head_ok, verbosity;
-static int tags = TAGS_DEFAULT;
-static const char *depth;
-static const char *upload_pack;
-static struct strbuf default_rla = STRBUF_INIT;
-static struct transport *transport;
-
-static struct option builtin_fetch_options[] = {
-	OPT__VERBOSITY(&verbosity),
-	OPT_BOOLEAN('a', "append", &append,
-		    "append to .git/FETCH_HEAD instead of overwriting"),
-	OPT_STRING(0, "upload-pack", &upload_pack, "PATH",
-		   "path to upload pack on remote end"),
-	OPT_BOOLEAN('f', "force", &force,
-		    "force overwrite of local branch"),
-	OPT_SET_INT('t', "tags", &tags,
-		    "fetch all tags and associated objects", TAGS_SET),
-	OPT_SET_INT('n', NULL, &tags,
-		    "do not fetch all tags (--no-tags)", TAGS_UNSET),
-	OPT_BOOLEAN('k', "keep", &keep, "keep downloaded pack"),
-	OPT_BOOLEAN('u', "update-head-ok", &update_head_ok,
-		    "allow updating of HEAD ref"),
-	OPT_STRING(0, "depth", &depth, "DEPTH",
-		   "deepen history of shallow clone"),
-	OPT_END()
-};
-
-static void unlock_pack(void)
-{
-	if (transport)
-		transport_unlock_pack(transport);
-}
-
-static void unlock_pack_on_signal(int signo)
-{
-	unlock_pack();
-	sigchain_pop(signo);
-	raise(signo);
-}
-
-static void add_merge_config(struct ref **head,
-			   const struct ref *remote_refs,
-		           struct branch *branch,
-		           struct ref ***tail)
-{
-	int i;
-
-	for (i = 0; i < branch->merge_nr; i++) {
-		struct ref *rm, **old_tail = *tail;
-		struct refspec refspec;
-
-		for (rm = *head; rm; rm = rm->next) {
-			if (branch_merge_matches(branch, i, rm->name)) {
-				rm->merge = 1;
-				break;
-			}
-		}
-		if (rm)
-			continue;
-
-		/*
-		 * Not fetched to a tracking branch?  We need to fetch
-		 * it anyway to allow this branch's "branch.$name.merge"
-		 * to be honored by 'git pull', but we do not have to
-		 * fail if branch.$name.merge is misconfigured to point
-		 * at a nonexisting branch.  If we were indeed called by
-		 * 'git pull', it will notice the misconfiguration because
-		 * there is no entry in the resulting FETCH_HEAD marked
-		 * for merging.
-		 */
-		refspec.src = branch->merge[i]->src;
-		refspec.dst = NULL;
-		refspec.pattern = 0;
-		refspec.force = 0;
-		get_fetch_map(remote_refs, &refspec, tail, 1);
-		for (rm = *old_tail; rm; rm = rm->next)
-			rm->merge = 1;
-	}
-}
-
-static void find_non_local_tags(struct transport *transport,
-			struct ref **head,
-			struct ref ***tail);
-
-static struct ref *get_ref_map(struct transport *transport,
-			       struct refspec *refs, int ref_count, int tags,
-			       int *autotags)
-{
-	int i;
-	struct ref *rm;
-	struct ref *ref_map = NULL;
-	struct ref **tail = &ref_map;
-
-	const struct ref *remote_refs = transport_get_remote_refs(transport);
-
-	if (ref_count || tags == TAGS_SET) {
-		for (i = 0; i < ref_count; i++) {
-			get_fetch_map(remote_refs, &refs[i], &tail, 0);
-			if (refs[i].dst && refs[i].dst[0])
-				*autotags = 1;
-		}
-		/* Merge everything on the command line, but not --tags */
-		for (rm = ref_map; rm; rm = rm->next)
-			rm->merge = 1;
-		if (tags == TAGS_SET)
-			get_fetch_map(remote_refs, tag_refspec, &tail, 0);
-	} else {
-		/* Use the defaults */
-		struct remote *remote = transport->remote;
-		struct branch *branch = branch_get(NULL);
-		int has_merge = branch_has_merge_config(branch);
-		if (remote && (remote->fetch_refspec_nr || has_merge)) {
-			for (i = 0; i < remote->fetch_refspec_nr; i++) {
-				get_fetch_map(remote_refs, &remote->fetch[i], &tail, 0);
-				if (remote->fetch[i].dst &&
-				    remote->fetch[i].dst[0])
-					*autotags = 1;
-				if (!i && !has_merge && ref_map &&
-				    !remote->fetch[0].pattern)
-					ref_map->merge = 1;
-			}
-			/*
-			 * if the remote we're fetching from is the same
-			 * as given in branch.<name>.remote, we add the
-			 * ref given in branch.<name>.merge, too.
-			 */
-			if (has_merge &&
-			    !strcmp(branch->remote_name, remote->name))
-				add_merge_config(&ref_map, remote_refs, branch, &tail);
-		} else {
-			ref_map = get_remote_ref(remote_refs, "HEAD");
-			if (!ref_map)
-				die("Couldn't find remote ref HEAD");
-			ref_map->merge = 1;
-			tail = &ref_map->next;
-		}
-	}
-	if (tags == TAGS_DEFAULT && *autotags)
-		find_non_local_tags(transport, &ref_map, &tail);
-	ref_remove_duplicates(ref_map);
-
-	return ref_map;
-}
-
-#define STORE_REF_ERROR_OTHER 1
-#define STORE_REF_ERROR_DF_CONFLICT 2
-
-static int s_update_ref(const char *action,
-			struct ref *ref,
-			int check_old)
-{
-	char msg[1024];
-	char *rla = getenv("GIT_REFLOG_ACTION");
-	static struct ref_lock *lock;
-
-	if (!rla)
-		rla = default_rla.buf;
-	snprintf(msg, sizeof(msg), "%s: %s", rla, action);
-	lock = lock_any_ref_for_update(ref->name,
-				       check_old ? ref->old_sha1 : NULL, 0);
-	if (!lock)
-		return errno == ENOTDIR ? STORE_REF_ERROR_DF_CONFLICT :
-					  STORE_REF_ERROR_OTHER;
-	if (write_ref_sha1(lock, ref->new_sha1, msg) < 0)
-		return errno == ENOTDIR ? STORE_REF_ERROR_DF_CONFLICT :
-					  STORE_REF_ERROR_OTHER;
-	return 0;
-}
-
-#define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
-#define REFCOL_WIDTH  10
-
-static int update_local_ref(struct ref *ref,
-			    const char *remote,
-			    char *display)
-{
-	struct commit *current = NULL, *updated;
-	enum object_type type;
-	struct branch *current_branch = branch_get(NULL);
-	const char *pretty_ref = prettify_refname(ref->name);
-
-	*display = 0;
-	type = sha1_object_info(ref->new_sha1, NULL);
-	if (type < 0)
-		die("object %s not found", sha1_to_hex(ref->new_sha1));
-
-	if (!hashcmp(ref->old_sha1, ref->new_sha1)) {
-		if (verbosity > 0)
-			sprintf(display, "= %-*s %-*s -> %s", SUMMARY_WIDTH,
-				"[up to date]", REFCOL_WIDTH, remote,
-				pretty_ref);
-		return 0;
-	}
-
-	if (current_branch &&
-	    !strcmp(ref->name, current_branch->name) &&
-	    !(update_head_ok || is_bare_repository()) &&
-	    !is_null_sha1(ref->old_sha1)) {
-		/*
-		 * If this is the head, and it's not okay to update
-		 * the head, and the old value of the head isn't empty...
-		 */
-		sprintf(display, "! %-*s %-*s -> %s  (can't fetch in current branch)",
-			SUMMARY_WIDTH, "[rejected]", REFCOL_WIDTH, remote,
-			pretty_ref);
-		return 1;
-	}
-
-	if (!is_null_sha1(ref->old_sha1) &&
-	    !prefixcmp(ref->name, "refs/tags/")) {
-		int r;
-		r = s_update_ref("updating tag", ref, 0);
-		sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : '-',
-			SUMMARY_WIDTH, "[tag update]", REFCOL_WIDTH, remote,
-			pretty_ref, r ? "  (unable to update local ref)" : "");
-		return r;
-	}
-
-	current = lookup_commit_reference_gently(ref->old_sha1, 1);
-	updated = lookup_commit_reference_gently(ref->new_sha1, 1);
-	if (!current || !updated) {
-		const char *msg;
-		const char *what;
-		int r;
-		if (!strncmp(ref->name, "refs/tags/", 10)) {
-			msg = "storing tag";
-			what = "[new tag]";
-		}
-		else {
-			msg = "storing head";
-			what = "[new branch]";
-		}
-
-		r = s_update_ref(msg, ref, 0);
-		sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : '*',
-			SUMMARY_WIDTH, what, REFCOL_WIDTH, remote, pretty_ref,
-			r ? "  (unable to update local ref)" : "");
-		return r;
-	}
-
-	if (in_merge_bases(current, &updated, 1)) {
-		char quickref[83];
-		int r;
-		strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));
-		strcat(quickref, "..");
-		strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));
-		r = s_update_ref("fast forward", ref, 1);
-		sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : ' ',
-			SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote,
-			pretty_ref, r ? "  (unable to update local ref)" : "");
-		return r;
-	} else if (force || ref->force) {
-		char quickref[84];
-		int r;
-		strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));
-		strcat(quickref, "...");
-		strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));
-		r = s_update_ref("forced-update", ref, 1);
-		sprintf(display, "%c %-*s %-*s -> %s  (%s)", r ? '!' : '+',
-			SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote,
-			pretty_ref,
-			r ? "unable to update local ref" : "forced update");
-		return r;
-	} else {
-		sprintf(display, "! %-*s %-*s -> %s  (non fast forward)",
-			SUMMARY_WIDTH, "[rejected]", REFCOL_WIDTH, remote,
-			pretty_ref);
-		return 1;
-	}
-}
-
-static int store_updated_refs(const char *raw_url, const char *remote_name,
-		struct ref *ref_map)
-{
-	FILE *fp;
-	struct commit *commit;
-	int url_len, i, note_len, shown_url = 0, rc = 0;
-	char note[1024];
-	const char *what, *kind;
-	struct ref *rm;
-	char *url, *filename = git_path("FETCH_HEAD");
-
-	fp = fopen(filename, "a");
-	if (!fp)
-		return error("cannot open %s: %s\n", filename, strerror(errno));
-
-	url = transport_anonymize_url(raw_url);
-	for (rm = ref_map; rm; rm = rm->next) {
-		struct ref *ref = NULL;
-
-		if (rm->peer_ref) {
-			ref = xcalloc(1, sizeof(*ref) + strlen(rm->peer_ref->name) + 1);
-			strcpy(ref->name, rm->peer_ref->name);
-			hashcpy(ref->old_sha1, rm->peer_ref->old_sha1);
-			hashcpy(ref->new_sha1, rm->old_sha1);
-			ref->force = rm->peer_ref->force;
-		}
-
-		commit = lookup_commit_reference_gently(rm->old_sha1, 1);
-		if (!commit)
-			rm->merge = 0;
-
-		if (!strcmp(rm->name, "HEAD")) {
-			kind = "";
-			what = "";
-		}
-		else if (!prefixcmp(rm->name, "refs/heads/")) {
-			kind = "branch";
-			what = rm->name + 11;
-		}
-		else if (!prefixcmp(rm->name, "refs/tags/")) {
-			kind = "tag";
-			what = rm->name + 10;
-		}
-		else if (!prefixcmp(rm->name, "refs/remotes/")) {
-			kind = "remote branch";
-			what = rm->name + 13;
-		}
-		else {
-			kind = "";
-			what = rm->name;
-		}
-
-		url_len = strlen(url);
-		for (i = url_len - 1; url[i] == '/' && 0 <= i; i--)
-			;
-		url_len = i + 1;
-		if (4 < i && !strncmp(".git", url + i - 3, 4))
-			url_len = i - 3;
-
-		note_len = 0;
-		if (*what) {
-			if (*kind)
-				note_len += sprintf(note + note_len, "%s ",
-						    kind);
-			note_len += sprintf(note + note_len, "'%s' of ", what);
-		}
-		note[note_len] = '\0';
-		fprintf(fp, "%s\t%s\t%s",
-			sha1_to_hex(commit ? commit->object.sha1 :
-				    rm->old_sha1),
-			rm->merge ? "" : "not-for-merge",
-			note);
-		for (i = 0; i < url_len; ++i)
-			if ('\n' == url[i])
-				fputs("\\n", fp);
-			else
-				fputc(url[i], fp);
-		fputc('\n', fp);
-
-		if (ref)
-			rc |= update_local_ref(ref, what, note);
-		else
-			sprintf(note, "* %-*s %-*s -> FETCH_HEAD",
-				SUMMARY_WIDTH, *kind ? kind : "branch",
-				 REFCOL_WIDTH, *what ? what : "HEAD");
-		if (*note) {
-			if (verbosity >= 0 && !shown_url) {
-				fprintf(stderr, "From %.*s\n",
-						url_len, url);
-				shown_url = 1;
-			}
-			if (verbosity >= 0)
-				fprintf(stderr, " %s\n", note);
-		}
-	}
-	free(url);
-	fclose(fp);
-	if (rc & STORE_REF_ERROR_DF_CONFLICT)
-		error("some local refs could not be updated; try running\n"
-		      " 'git remote prune %s' to remove any old, conflicting "
-		      "branches", remote_name);
-	return rc;
-}
-
-/*
- * We would want to bypass the object transfer altogether if
- * everything we are going to fetch already exists and is connected
- * locally.
- *
- * The refs we are going to fetch are in ref_map.  If running
- *
- *  $ git rev-list --objects --stdin --not --all
- *
- * (feeding all the refs in ref_map on its standard input)
- * does not error out, that means everything reachable from the
- * refs we are going to fetch exists and is connected to some of
- * our existing refs.
- */
-static int quickfetch(struct ref *ref_map)
-{
-	struct child_process revlist;
-	struct ref *ref;
-	int err;
-	const char *argv[] = {"rev-list",
-		"--quiet", "--objects", "--stdin", "--not", "--all", NULL};
-
-	/*
-	 * If we are deepening a shallow clone we already have these
-	 * objects reachable.  Running rev-list here will return with
-	 * a good (0) exit status and we'll bypass the fetch that we
-	 * really need to perform.  Claiming failure now will ensure
-	 * we perform the network exchange to deepen our history.
-	 */
-	if (depth)
-		return -1;
-
-	if (!ref_map)
-		return 0;
-
-	memset(&revlist, 0, sizeof(revlist));
-	revlist.argv = argv;
-	revlist.git_cmd = 1;
-	revlist.no_stdout = 1;
-	revlist.no_stderr = 1;
-	revlist.in = -1;
-
-	err = start_command(&revlist);
-	if (err) {
-		error("could not run rev-list");
-		return err;
-	}
-
-	/*
-	 * If rev-list --stdin encounters an unknown commit, it terminates,
-	 * which will cause SIGPIPE in the write loop below.
-	 */
-	sigchain_push(SIGPIPE, SIG_IGN);
-
-	for (ref = ref_map; ref; ref = ref->next) {
-		if (write_in_full(revlist.in, sha1_to_hex(ref->old_sha1), 40) < 0 ||
-		    write_in_full(revlist.in, "\n", 1) < 0) {
-			if (errno != EPIPE && errno != EINVAL)
-				error("failed write to rev-list: %s", strerror(errno));
-			err = -1;
-			break;
-		}
-	}
-
-	if (close(revlist.in)) {
-		error("failed to close rev-list's stdin: %s", strerror(errno));
-		err = -1;
-	}
-
-	sigchain_pop(SIGPIPE);
-
-	return finish_command(&revlist) || err;
-}
-
-static int fetch_refs(struct transport *transport, struct ref *ref_map)
-{
-	int ret = quickfetch(ref_map);
-	if (ret)
-		ret = transport_fetch_refs(transport, ref_map);
-	if (!ret)
-		ret |= store_updated_refs(transport->url,
-				transport->remote->name,
-				ref_map);
-	transport_unlock_pack(transport);
-	return ret;
-}
-
-static int add_existing(const char *refname, const unsigned char *sha1,
-			int flag, void *cbdata)
-{
-	struct string_list *list = (struct string_list *)cbdata;
-	string_list_insert(refname, list);
-	return 0;
-}
-
-static int will_fetch(struct ref **head, const unsigned char *sha1)
-{
-	struct ref *rm = *head;
-	while (rm) {
-		if (!hashcmp(rm->old_sha1, sha1))
-			return 1;
-		rm = rm->next;
-	}
-	return 0;
-}
-
-static void find_non_local_tags(struct transport *transport,
-			struct ref **head,
-			struct ref ***tail)
-{
-	struct string_list existing_refs = { NULL, 0, 0, 0 };
-	struct string_list new_refs = { NULL, 0, 0, 1 };
-	char *ref_name;
-	int ref_name_len;
-	const unsigned char *ref_sha1;
-	const struct ref *tag_ref;
-	struct ref *rm = NULL;
-	const struct ref *ref;
-
-	for_each_ref(add_existing, &existing_refs);
-	for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) {
-		if (prefixcmp(ref->name, "refs/tags"))
-			continue;
-
-		ref_name = xstrdup(ref->name);
-		ref_name_len = strlen(ref_name);
-		ref_sha1 = ref->old_sha1;
-
-		if (!strcmp(ref_name + ref_name_len - 3, "^{}")) {
-			ref_name[ref_name_len - 3] = 0;
-			tag_ref = transport_get_remote_refs(transport);
-			while (tag_ref) {
-				if (!strcmp(tag_ref->name, ref_name)) {
-					ref_sha1 = tag_ref->old_sha1;
-					break;
-				}
-				tag_ref = tag_ref->next;
-			}
-		}
-
-		if (!string_list_has_string(&existing_refs, ref_name) &&
-		    !string_list_has_string(&new_refs, ref_name) &&
-		    (has_sha1_file(ref->old_sha1) ||
-		     will_fetch(head, ref->old_sha1))) {
-			string_list_insert(ref_name, &new_refs);
-
-			rm = alloc_ref(ref_name);
-			rm->peer_ref = alloc_ref(ref_name);
-			hashcpy(rm->old_sha1, ref_sha1);
-
-			**tail = rm;
-			*tail = &rm->next;
-		}
-		free(ref_name);
-	}
-	string_list_clear(&existing_refs, 0);
-	string_list_clear(&new_refs, 0);
-}
-
-static void check_not_current_branch(struct ref *ref_map)
-{
-	struct branch *current_branch = branch_get(NULL);
-
-	if (is_bare_repository() || !current_branch)
-		return;
-
-	for (; ref_map; ref_map = ref_map->next)
-		if (ref_map->peer_ref && !strcmp(current_branch->refname,
-					ref_map->peer_ref->name))
-			die("Refusing to fetch into current branch %s "
-			    "of non-bare repository", current_branch->refname);
-}
-
-static int do_fetch(struct transport *transport,
-		    struct refspec *refs, int ref_count)
-{
-	struct ref *ref_map;
-	struct ref *rm;
-	int autotags = (transport->remote->fetch_tags == 1);
-	if (transport->remote->fetch_tags == 2 && tags != TAGS_UNSET)
-		tags = TAGS_SET;
-	if (transport->remote->fetch_tags == -1)
-		tags = TAGS_UNSET;
-
-	if (!transport->get_refs_list || !transport->fetch)
-		die("Don't know how to fetch from %s", transport->url);
-
-	/* if not appending, truncate FETCH_HEAD */
-	if (!append) {
-		char *filename = git_path("FETCH_HEAD");
-		FILE *fp = fopen(filename, "w");
-		if (!fp)
-			return error("cannot open %s: %s\n", filename, strerror(errno));
-		fclose(fp);
-	}
-
-	ref_map = get_ref_map(transport, refs, ref_count, tags, &autotags);
-	if (!update_head_ok)
-		check_not_current_branch(ref_map);
-
-	for (rm = ref_map; rm; rm = rm->next) {
-		if (rm->peer_ref)
-			read_ref(rm->peer_ref->name, rm->peer_ref->old_sha1);
-	}
-
-	if (tags == TAGS_DEFAULT && autotags)
-		transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1");
-	if (fetch_refs(transport, ref_map)) {
-		free_refs(ref_map);
-		return 1;
-	}
-	free_refs(ref_map);
-
-	/* if neither --no-tags nor --tags was specified, do automated tag
-	 * following ... */
-	if (tags == TAGS_DEFAULT && autotags) {
-		struct ref **tail = &ref_map;
-		ref_map = NULL;
-		find_non_local_tags(transport, &ref_map, &tail);
-		if (ref_map) {
-			transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
-			transport_set_option(transport, TRANS_OPT_DEPTH, "0");
-			fetch_refs(transport, ref_map);
-		}
-		free_refs(ref_map);
-	}
-
-	return 0;
-}
-
-static void set_option(const char *name, const char *value)
-{
-	int r = transport_set_option(transport, name, value);
-	if (r < 0)
-		die("Option \"%s\" value \"%s\" is not valid for %s",
-			name, value, transport->url);
-	if (r > 0)
-		warning("Option \"%s\" is ignored for %s\n",
-			name, transport->url);
-}
-
-int cmd_fetch(int argc, const char **argv, const char *prefix)
-{
-	struct remote *remote;
-	int i;
-	static const char **refs = NULL;
-	int ref_nr = 0;
-	int exit_code;
-
-	/* Record the command line for the reflog */
-	strbuf_addstr(&default_rla, "fetch");
-	for (i = 1; i < argc; i++)
-		strbuf_addf(&default_rla, " %s", argv[i]);
-
-	argc = parse_options(argc, argv, prefix,
-			     builtin_fetch_options, builtin_fetch_usage, 0);
-
-	if (argc == 0)
-		remote = remote_get(NULL);
-	else
-		remote = remote_get(argv[0]);
-
-	if (!remote)
-		die("Where do you want to fetch from today?");
-
-	transport = transport_get(remote, remote->url[0]);
-	if (verbosity >= 2)
-		transport->verbose = 1;
-	if (verbosity < 0)
-		transport->verbose = -1;
-	if (upload_pack)
-		set_option(TRANS_OPT_UPLOADPACK, upload_pack);
-	if (keep)
-		set_option(TRANS_OPT_KEEP, "yes");
-	if (depth)
-		set_option(TRANS_OPT_DEPTH, depth);
-
-	if (argc > 1) {
-		int j = 0;
-		refs = xcalloc(argc + 1, sizeof(const char *));
-		for (i = 1; i < argc; i++) {
-			if (!strcmp(argv[i], "tag")) {
-				char *ref;
-				i++;
-				if (i >= argc)
-					die("You need to specify a tag name.");
-				ref = xmalloc(strlen(argv[i]) * 2 + 22);
-				strcpy(ref, "refs/tags/");
-				strcat(ref, argv[i]);
-				strcat(ref, ":refs/tags/");
-				strcat(ref, argv[i]);
-				refs[j++] = ref;
-			} else
-				refs[j++] = argv[i];
-		}
-		refs[j] = NULL;
-		ref_nr = j;
-	}
-
-	sigchain_push_common(unlock_pack_on_signal);
-	atexit(unlock_pack);
-	exit_code = do_fetch(transport,
-			parse_fetch_refspec(ref_nr, refs), ref_nr);
-	transport_disconnect(transport);
-	transport = NULL;
-	return exit_code;
-}
diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c
deleted file mode 100644
index 9d52400..0000000
--- a/builtin-fmt-merge-msg.c
+++ /dev/null
@@ -1,381 +0,0 @@
-#include "builtin.h"
-#include "cache.h"
-#include "commit.h"
-#include "diff.h"
-#include "revision.h"
-#include "tag.h"
-
-static const char * const fmt_merge_msg_usage[] = {
-	"git fmt-merge-msg [--log|--no-log] [--file <file>]",
-	NULL
-};
-
-static int merge_summary;
-
-static int fmt_merge_msg_config(const char *key, const char *value, void *cb)
-{
-	static int found_merge_log = 0;
-	if (!strcmp("merge.log", key)) {
-		found_merge_log = 1;
-		merge_summary = git_config_bool(key, value);
-	}
-	if (!found_merge_log && !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 = xrealloc(list->list, sizeof(char *) * list->alloc);
-		list->payload = xrealloc(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]);
-		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 (!prefixcmp(line + 41, "not-for-merge"))
-		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, xstrdup(src),
-				xcalloc(1, sizeof(struct src_data)));
-	}
-	src_data = srcs.payload[i];
-
-	if (pulling_head) {
-		origin = xstrdup(src);
-		src_data->head_status |= 1;
-	} else if (!prefixcmp(line, "branch ")) {
-		origin = xstrdup(line + 7);
-		append_to_list(&src_data->branch, origin, NULL);
-		src_data->head_status |= 2;
-	} else if (!prefixcmp(line, "tag ")) {
-		origin = line;
-		append_to_list(&src_data->tag, xstrdup(origin + 4), NULL);
-		src_data->head_status |= 2;
-	} else if (!prefixcmp(line, "remote branch ")) {
-		origin = xstrdup(line + 14);
-		append_to_list(&src_data->r_branch, origin, NULL);
-		src_data->head_status |= 2;
-	} else {
-		origin = xstrdup(src);
-		append_to_list(&src_data->generic, xstrdup(line), NULL);
-		src_data->head_status |= 2;
-	}
-
-	if (!strcmp(".", src) || !strcmp(src, origin)) {
-		int len = strlen(origin);
-		if (origin[0] == '\'' && origin[len - 1] == '\'') {
-			origin = xmemdupz(origin + 1, len - 2);
-		} else {
-			origin = xstrdup(origin);
-		}
-	} else {
-		char *new_origin = xmalloc(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, struct strbuf *out)
-{
-	if (list->nr == 0)
-		return;
-	if (list->nr == 1) {
-		strbuf_addf(out, "%s%s", singular, list->list[0]);
-	} else {
-		int i;
-		strbuf_addstr(out, plural);
-		for (i = 0; i < list->nr - 1; i++)
-			strbuf_addf(out, "%s%s", i > 0 ? ", " : "", list->list[i]);
-		strbuf_addf(out, " 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,
-		struct strbuf *out)
-{
-	int i, count = 0;
-	struct commit *commit;
-	struct object *branch;
-	struct list subjects = { NULL, NULL, 0, 0 };
-	int flags = UNINTERESTING | TREESAME | 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;
-	if (prepare_revision_walk(rev))
-		die("revision walk setup failed");
-	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) {
-			unsigned char c;
-			do {
-				c = *++bol;
-			} while (isspace(c));
-			if (!c)
-				bol = NULL;
-		}
-
-		if (!bol) {
-			append_to_list(&subjects, xstrdup(sha1_to_hex(
-							commit->object.sha1)),
-					NULL);
-			continue;
-		}
-
-		eol = strchr(bol, '\n');
-		if (eol) {
-			oneline = xmemdupz(bol, eol - bol);
-		} else {
-			oneline = xstrdup(bol);
-		}
-		append_to_list(&subjects, oneline, NULL);
-	}
-
-	if (count > limit)
-		strbuf_addf(out, "\n* %s: (%d commits)\n", name, count);
-	else
-		strbuf_addf(out, "\n* %s:\n", name);
-
-	for (i = 0; i < subjects.nr; i++)
-		if (i >= limit)
-			strbuf_addf(out, "  ...\n");
-		else
-			strbuf_addf(out, "  %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 fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
-	int limit = 20, i = 0, pos = 0;
-	char *sep = "";
-	unsigned char head_sha1[20];
-	const char *current_branch;
-
-	/* get current branch */
-	current_branch = resolve_ref("HEAD", head_sha1, 1, NULL);
-	if (!current_branch)
-		die("No current branch");
-	if (!prefixcmp(current_branch, "refs/heads/"))
-		current_branch += 11;
-
-	/* get a line */
-	while (pos < in->len) {
-		int len;
-		char *newline, *p = in->buf + pos;
-
-		newline = strchr(p, '\n');
-		len = newline ? newline - p : strlen(p);
-		pos += len + !!newline;
-		i++;
-		p[len] = 0;
-		if (handle_line(p))
-			die ("Error in line %d: %.*s", i, len, p);
-	}
-
-	strbuf_addstr(out, "Merge ");
-	for (i = 0; i < srcs.nr; i++) {
-		struct src_data *src_data = srcs.payload[i];
-		const char *subsep = "";
-
-		strbuf_addstr(out, sep);
-		sep = "; ";
-
-		if (src_data->head_status == 1) {
-			strbuf_addstr(out, srcs.list[i]);
-			continue;
-		}
-		if (src_data->head_status == 3) {
-			subsep = ", ";
-			strbuf_addstr(out, "HEAD");
-		}
-		if (src_data->branch.nr) {
-			strbuf_addstr(out, subsep);
-			subsep = ", ";
-			print_joined("branch ", "branches ", &src_data->branch,
-					out);
-		}
-		if (src_data->r_branch.nr) {
-			strbuf_addstr(out, subsep);
-			subsep = ", ";
-			print_joined("remote branch ", "remote branches ",
-					&src_data->r_branch, out);
-		}
-		if (src_data->tag.nr) {
-			strbuf_addstr(out, subsep);
-			subsep = ", ";
-			print_joined("tag ", "tags ", &src_data->tag, out);
-		}
-		if (src_data->generic.nr) {
-			strbuf_addstr(out, subsep);
-			print_joined("commit ", "commits ", &src_data->generic,
-					out);
-		}
-		if (strcmp(".", srcs.list[i]))
-			strbuf_addf(out, " of %s", srcs.list[i]);
-	}
-
-	if (!strcmp("master", current_branch))
-		strbuf_addch(out, '\n');
-	else
-		strbuf_addf(out, " into %s\n", current_branch);
-
-	if (merge_summary) {
-		struct commit *head;
-		struct rev_info rev;
-
-		head = lookup_commit(head_sha1);
-		init_revisions(&rev, NULL);
-		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, out);
-	}
-	return 0;
-}
-
-int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
-{
-	const char *inpath = NULL;
-	struct option options[] = {
-		OPT_BOOLEAN(0, "log",     &merge_summary, "populate log with the shortlog"),
-		OPT_BOOLEAN(0, "summary", &merge_summary, "alias for --log"),
-		OPT_FILENAME('F', "file", &inpath, "file to read from"),
-		OPT_END()
-	};
-
-	FILE *in = stdin;
-	struct strbuf input = STRBUF_INIT, output = STRBUF_INIT;
-	int ret;
-
-	git_config(fmt_merge_msg_config, NULL);
-	argc = parse_options(argc, argv, prefix, options, fmt_merge_msg_usage,
-			     0);
-	if (argc > 0)
-		usage_with_options(fmt_merge_msg_usage, options);
-
-	if (inpath && strcmp(inpath, "-")) {
-		in = fopen(inpath, "r");
-		if (!in)
-			die_errno("cannot open '%s'", inpath);
-	}
-
-	if (strbuf_read(&input, fileno(in), 0) < 0)
-		die_errno("could not read input file");
-	ret = fmt_merge_msg(merge_summary, &input, &output);
-	if (ret)
-		return ret;
-	write_in_full(STDOUT_FILENO, output.buf, output.len);
-	return 0;
-}
diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c
deleted file mode 100644
index d7cc8ca..0000000
--- a/builtin-for-each-ref.c
+++ /dev/null
@@ -1,955 +0,0 @@
-#include "builtin.h"
-#include "cache.h"
-#include "refs.h"
-#include "object.h"
-#include "tag.h"
-#include "commit.h"
-#include "tree.h"
-#include "blob.h"
-#include "quote.h"
-#include "parse-options.h"
-#include "remote.h"
-
-/* Quoting styles */
-#define QUOTE_NONE 0
-#define QUOTE_SHELL 1
-#define QUOTE_PERL 2
-#define QUOTE_PYTHON 4
-#define QUOTE_TCL 8
-
-typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;
-
-struct atom_value {
-	const char *s;
-	unsigned long ul; /* used for sorting when not FIELD_STR */
-};
-
-struct ref_sort {
-	struct ref_sort *next;
-	int atom; /* index into used_atom array */
-	unsigned reverse : 1;
-};
-
-struct refinfo {
-	char *refname;
-	unsigned char objectname[20];
-	struct atom_value *value;
-};
-
-static struct {
-	const char *name;
-	cmp_type cmp_type;
-} valid_atom[] = {
-	{ "refname" },
-	{ "objecttype" },
-	{ "objectsize", FIELD_ULONG },
-	{ "objectname" },
-	{ "tree" },
-	{ "parent" },
-	{ "numparent", FIELD_ULONG },
-	{ "object" },
-	{ "type" },
-	{ "tag" },
-	{ "author" },
-	{ "authorname" },
-	{ "authoremail" },
-	{ "authordate", FIELD_TIME },
-	{ "committer" },
-	{ "committername" },
-	{ "committeremail" },
-	{ "committerdate", FIELD_TIME },
-	{ "tagger" },
-	{ "taggername" },
-	{ "taggeremail" },
-	{ "taggerdate", FIELD_TIME },
-	{ "creator" },
-	{ "creatordate", FIELD_TIME },
-	{ "subject" },
-	{ "body" },
-	{ "contents" },
-	{ "upstream" },
-};
-
-/*
- * An atom is a valid field atom listed above, possibly prefixed with
- * a "*" to denote deref_tag().
- *
- * We parse given format string and sort specifiers, and make a list
- * of properties that we need to extract out of objects.  refinfo
- * structure will hold an array of values extracted that can be
- * indexed with the "atom number", which is an index into this
- * array.
- */
-static const char **used_atom;
-static cmp_type *used_atom_type;
-static int used_atom_cnt, sort_atom_limit, need_tagged;
-
-/*
- * Used to parse format string and sort specifiers
- */
-static int parse_atom(const char *atom, const char *ep)
-{
-	const char *sp;
-	int i, at;
-
-	sp = atom;
-	if (*sp == '*' && sp < ep)
-		sp++; /* deref */
-	if (ep <= sp)
-		die("malformed field name: %.*s", (int)(ep-atom), atom);
-
-	/* Do we have the atom already used elsewhere? */
-	for (i = 0; i < used_atom_cnt; i++) {
-		int len = strlen(used_atom[i]);
-		if (len == ep - atom && !memcmp(used_atom[i], atom, len))
-			return i;
-	}
-
-	/* Is the atom a valid one? */
-	for (i = 0; i < ARRAY_SIZE(valid_atom); i++) {
-		int len = strlen(valid_atom[i].name);
-		/*
-		 * If the atom name has a colon, strip it and everything after
-		 * it off - it specifies the format for this entry, and
-		 * shouldn't be used for checking against the valid_atom
-		 * table.
-		 */
-		const char *formatp = strchr(sp, ':');
-		if (!formatp || ep < formatp)
-			formatp = ep;
-		if (len == formatp - sp && !memcmp(valid_atom[i].name, sp, len))
-			break;
-	}
-
-	if (ARRAY_SIZE(valid_atom) <= i)
-		die("unknown field name: %.*s", (int)(ep-atom), atom);
-
-	/* Add it in, including the deref prefix */
-	at = used_atom_cnt;
-	used_atom_cnt++;
-	used_atom = xrealloc(used_atom,
-			     (sizeof *used_atom) * used_atom_cnt);
-	used_atom_type = xrealloc(used_atom_type,
-				  (sizeof(*used_atom_type) * used_atom_cnt));
-	used_atom[at] = xmemdupz(atom, ep - atom);
-	used_atom_type[at] = valid_atom[i].cmp_type;
-	return at;
-}
-
-/*
- * In a format string, find the next occurrence of %(atom).
- */
-static const char *find_next(const char *cp)
-{
-	while (*cp) {
-		if (*cp == '%') {
-			/* %( is the start of an atom;
-			 * %% is a quoted per-cent.
-			 */
-			if (cp[1] == '(')
-				return cp;
-			else if (cp[1] == '%')
-				cp++; /* skip over two % */
-			/* otherwise this is a singleton, literal % */
-		}
-		cp++;
-	}
-	return NULL;
-}
-
-/*
- * Make sure the format string is well formed, and parse out
- * the used atoms.
- */
-static int verify_format(const char *format)
-{
-	const char *cp, *sp;
-	for (cp = format; *cp && (sp = find_next(cp)); ) {
-		const char *ep = strchr(sp, ')');
-		if (!ep)
-			return error("malformed format string %s", sp);
-		/* sp points at "%(" and ep points at the closing ")" */
-		parse_atom(sp + 2, ep);
-		cp = ep + 1;
-	}
-	return 0;
-}
-
-/*
- * Given an object name, read the object data and size, and return a
- * "struct object".  If the object data we are returning is also borrowed
- * by the "struct object" representation, set *eaten as well---it is a
- * signal from parse_object_buffer to us not to free the buffer.
- */
-static void *get_obj(const unsigned char *sha1, struct object **obj, unsigned long *sz, int *eaten)
-{
-	enum object_type type;
-	void *buf = read_sha1_file(sha1, &type, sz);
-
-	if (buf)
-		*obj = parse_object_buffer(sha1, type, *sz, buf, eaten);
-	else
-		*obj = NULL;
-	return buf;
-}
-
-/* See grab_values */
-static void grab_common_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
-{
-	int i;
-
-	for (i = 0; i < used_atom_cnt; i++) {
-		const char *name = used_atom[i];
-		struct atom_value *v = &val[i];
-		if (!!deref != (*name == '*'))
-			continue;
-		if (deref)
-			name++;
-		if (!strcmp(name, "objecttype"))
-			v->s = typename(obj->type);
-		else if (!strcmp(name, "objectsize")) {
-			char *s = xmalloc(40);
-			sprintf(s, "%lu", sz);
-			v->ul = sz;
-			v->s = s;
-		}
-		else if (!strcmp(name, "objectname")) {
-			char *s = xmalloc(41);
-			strcpy(s, sha1_to_hex(obj->sha1));
-			v->s = s;
-		}
-	}
-}
-
-/* See grab_values */
-static void grab_tag_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
-{
-	int i;
-	struct tag *tag = (struct tag *) obj;
-
-	for (i = 0; i < used_atom_cnt; i++) {
-		const char *name = used_atom[i];
-		struct atom_value *v = &val[i];
-		if (!!deref != (*name == '*'))
-			continue;
-		if (deref)
-			name++;
-		if (!strcmp(name, "tag"))
-			v->s = tag->tag;
-		else if (!strcmp(name, "type") && tag->tagged)
-			v->s = typename(tag->tagged->type);
-		else if (!strcmp(name, "object") && tag->tagged) {
-			char *s = xmalloc(41);
-			strcpy(s, sha1_to_hex(tag->tagged->sha1));
-			v->s = s;
-		}
-	}
-}
-
-static int num_parents(struct commit *commit)
-{
-	struct commit_list *parents;
-	int i;
-
-	for (i = 0, parents = commit->parents;
-	     parents;
-	     parents = parents->next)
-		i++;
-	return i;
-}
-
-/* See grab_values */
-static void grab_commit_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
-{
-	int i;
-	struct commit *commit = (struct commit *) obj;
-
-	for (i = 0; i < used_atom_cnt; i++) {
-		const char *name = used_atom[i];
-		struct atom_value *v = &val[i];
-		if (!!deref != (*name == '*'))
-			continue;
-		if (deref)
-			name++;
-		if (!strcmp(name, "tree")) {
-			char *s = xmalloc(41);
-			strcpy(s, sha1_to_hex(commit->tree->object.sha1));
-			v->s = s;
-		}
-		if (!strcmp(name, "numparent")) {
-			char *s = xmalloc(40);
-			v->ul = num_parents(commit);
-			sprintf(s, "%lu", v->ul);
-			v->s = s;
-		}
-		else if (!strcmp(name, "parent")) {
-			int num = num_parents(commit);
-			int i;
-			struct commit_list *parents;
-			char *s = xmalloc(41 * num + 1);
-			v->s = s;
-			for (i = 0, parents = commit->parents;
-			     parents;
-			     parents = parents->next, i = i + 41) {
-				struct commit *parent = parents->item;
-				strcpy(s+i, sha1_to_hex(parent->object.sha1));
-				if (parents->next)
-					s[i+40] = ' ';
-			}
-			if (!i)
-				*s = '\0';
-		}
-	}
-}
-
-static const char *find_wholine(const char *who, int wholen, const char *buf, unsigned long sz)
-{
-	const char *eol;
-	while (*buf) {
-		if (!strncmp(buf, who, wholen) &&
-		    buf[wholen] == ' ')
-			return buf + wholen + 1;
-		eol = strchr(buf, '\n');
-		if (!eol)
-			return "";
-		eol++;
-		if (*eol == '\n')
-			return ""; /* end of header */
-		buf = eol;
-	}
-	return "";
-}
-
-static const char *copy_line(const char *buf)
-{
-	const char *eol = strchrnul(buf, '\n');
-	return xmemdupz(buf, eol - buf);
-}
-
-static const char *copy_name(const char *buf)
-{
-	const char *cp;
-	for (cp = buf; *cp && *cp != '\n'; cp++) {
-		if (!strncmp(cp, " <", 2))
-			return xmemdupz(buf, cp - buf);
-	}
-	return "";
-}
-
-static const char *copy_email(const char *buf)
-{
-	const char *email = strchr(buf, '<');
-	const char *eoemail;
-	if (!email)
-		return "";
-	eoemail = strchr(email, '>');
-	if (!eoemail)
-		return "";
-	return xmemdupz(email, eoemail + 1 - email);
-}
-
-static void grab_date(const char *buf, struct atom_value *v, const char *atomname)
-{
-	const char *eoemail = strstr(buf, "> ");
-	char *zone;
-	unsigned long timestamp;
-	long tz;
-	enum date_mode date_mode = DATE_NORMAL;
-	const char *formatp;
-
-	/*
-	 * We got here because atomname ends in "date" or "date<something>";
-	 * it's not possible that <something> is not ":<format>" because
-	 * parse_atom() wouldn't have allowed it, so we can assume that no
-	 * ":" means no format is specified, and use the default.
-	 */
-	formatp = strchr(atomname, ':');
-	if (formatp != NULL) {
-		formatp++;
-		date_mode = parse_date_format(formatp);
-	}
-
-	if (!eoemail)
-		goto bad;
-	timestamp = strtoul(eoemail + 2, &zone, 10);
-	if (timestamp == ULONG_MAX)
-		goto bad;
-	tz = strtol(zone, NULL, 10);
-	if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)
-		goto bad;
-	v->s = xstrdup(show_date(timestamp, tz, date_mode));
-	v->ul = timestamp;
-	return;
- bad:
-	v->s = "";
-	v->ul = 0;
-}
-
-/* See grab_values */
-static void grab_person(const char *who, struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
-{
-	int i;
-	int wholen = strlen(who);
-	const char *wholine = NULL;
-
-	for (i = 0; i < used_atom_cnt; i++) {
-		const char *name = used_atom[i];
-		struct atom_value *v = &val[i];
-		if (!!deref != (*name == '*'))
-			continue;
-		if (deref)
-			name++;
-		if (strncmp(who, name, wholen))
-			continue;
-		if (name[wholen] != 0 &&
-		    strcmp(name + wholen, "name") &&
-		    strcmp(name + wholen, "email") &&
-		    prefixcmp(name + wholen, "date"))
-			continue;
-		if (!wholine)
-			wholine = find_wholine(who, wholen, buf, sz);
-		if (!wholine)
-			return; /* no point looking for it */
-		if (name[wholen] == 0)
-			v->s = copy_line(wholine);
-		else if (!strcmp(name + wholen, "name"))
-			v->s = copy_name(wholine);
-		else if (!strcmp(name + wholen, "email"))
-			v->s = copy_email(wholine);
-		else if (!prefixcmp(name + wholen, "date"))
-			grab_date(wholine, v, name);
-	}
-
-	/* For a tag or a commit object, if "creator" or "creatordate" is
-	 * requested, do something special.
-	 */
-	if (strcmp(who, "tagger") && strcmp(who, "committer"))
-		return; /* "author" for commit object is not wanted */
-	if (!wholine)
-		wholine = find_wholine(who, wholen, buf, sz);
-	if (!wholine)
-		return;
-	for (i = 0; i < used_atom_cnt; i++) {
-		const char *name = used_atom[i];
-		struct atom_value *v = &val[i];
-		if (!!deref != (*name == '*'))
-			continue;
-		if (deref)
-			name++;
-
-		if (!prefixcmp(name, "creatordate"))
-			grab_date(wholine, v, name);
-		else if (!strcmp(name, "creator"))
-			v->s = copy_line(wholine);
-	}
-}
-
-static void find_subpos(const char *buf, unsigned long sz, const char **sub, const char **body)
-{
-	while (*buf) {
-		const char *eol = strchr(buf, '\n');
-		if (!eol)
-			return;
-		if (eol[1] == '\n') {
-			buf = eol + 1;
-			break; /* found end of header */
-		}
-		buf = eol + 1;
-	}
-	while (*buf == '\n')
-		buf++;
-	if (!*buf)
-		return;
-	*sub = buf; /* first non-empty line */
-	buf = strchr(buf, '\n');
-	if (!buf) {
-		*body = "";
-		return; /* no body */
-	}
-	while (*buf == '\n')
-		buf++; /* skip blank between subject and body */
-	*body = buf;
-}
-
-/* See grab_values */
-static void grab_sub_body_contents(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
-{
-	int i;
-	const char *subpos = NULL, *bodypos = NULL;
-
-	for (i = 0; i < used_atom_cnt; i++) {
-		const char *name = used_atom[i];
-		struct atom_value *v = &val[i];
-		if (!!deref != (*name == '*'))
-			continue;
-		if (deref)
-			name++;
-		if (strcmp(name, "subject") &&
-		    strcmp(name, "body") &&
-		    strcmp(name, "contents"))
-			continue;
-		if (!subpos)
-			find_subpos(buf, sz, &subpos, &bodypos);
-		if (!subpos)
-			return;
-
-		if (!strcmp(name, "subject"))
-			v->s = copy_line(subpos);
-		else if (!strcmp(name, "body"))
-			v->s = xstrdup(bodypos);
-		else if (!strcmp(name, "contents"))
-			v->s = xstrdup(subpos);
-	}
-}
-
-/* We want to have empty print-string for field requests
- * that do not apply (e.g. "authordate" for a tag object)
- */
-static void fill_missing_values(struct atom_value *val)
-{
-	int i;
-	for (i = 0; i < used_atom_cnt; i++) {
-		struct atom_value *v = &val[i];
-		if (v->s == NULL)
-			v->s = "";
-	}
-}
-
-/*
- * val is a list of atom_value to hold returned values.  Extract
- * the values for atoms in used_atom array out of (obj, buf, sz).
- * when deref is false, (obj, buf, sz) is the object that is
- * pointed at by the ref itself; otherwise it is the object the
- * ref (which is a tag) refers to.
- */
-static void grab_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
-{
-	grab_common_values(val, deref, obj, buf, sz);
-	switch (obj->type) {
-	case OBJ_TAG:
-		grab_tag_values(val, deref, obj, buf, sz);
-		grab_sub_body_contents(val, deref, obj, buf, sz);
-		grab_person("tagger", val, deref, obj, buf, sz);
-		break;
-	case OBJ_COMMIT:
-		grab_commit_values(val, deref, obj, buf, sz);
-		grab_sub_body_contents(val, deref, obj, buf, sz);
-		grab_person("author", val, deref, obj, buf, sz);
-		grab_person("committer", val, deref, obj, buf, sz);
-		break;
-	case OBJ_TREE:
-		// grab_tree_values(val, deref, obj, buf, sz);
-		break;
-	case OBJ_BLOB:
-		// grab_blob_values(val, deref, obj, buf, sz);
-		break;
-	default:
-		die("Eh?  Object of type %d?", obj->type);
-	}
-}
-
-/*
- * Parse the object referred by ref, and grab needed value.
- */
-static void populate_value(struct refinfo *ref)
-{
-	void *buf;
-	struct object *obj;
-	int eaten, i;
-	unsigned long size;
-	const unsigned char *tagged;
-
-	ref->value = xcalloc(sizeof(struct atom_value), used_atom_cnt);
-
-	/* Fill in specials first */
-	for (i = 0; i < used_atom_cnt; i++) {
-		const char *name = used_atom[i];
-		struct atom_value *v = &ref->value[i];
-		int deref = 0;
-		const char *refname;
-		const char *formatp;
-
-		if (*name == '*') {
-			deref = 1;
-			name++;
-		}
-
-		if (!prefixcmp(name, "refname"))
-			refname = ref->refname;
-		else if(!prefixcmp(name, "upstream")) {
-			struct branch *branch;
-			/* only local branches may have an upstream */
-			if (prefixcmp(ref->refname, "refs/heads/"))
-				continue;
-			branch = branch_get(ref->refname + 11);
-
-			if (!branch || !branch->merge || !branch->merge[0] ||
-			    !branch->merge[0]->dst)
-				continue;
-			refname = branch->merge[0]->dst;
-		}
-		else
-			continue;
-
-		formatp = strchr(name, ':');
-		/* look for "short" refname format */
-		if (formatp) {
-			formatp++;
-			if (!strcmp(formatp, "short"))
-				refname = shorten_unambiguous_ref(refname,
-						      warn_ambiguous_refs);
-			else
-				die("unknown %.*s format %s",
-				    (int)(formatp - name), name, formatp);
-		}
-
-		if (!deref)
-			v->s = refname;
-		else {
-			int len = strlen(refname);
-			char *s = xmalloc(len + 4);
-			sprintf(s, "%s^{}", refname);
-			v->s = s;
-		}
-	}
-
-	for (i = 0; i < used_atom_cnt; i++) {
-		struct atom_value *v = &ref->value[i];
-		if (v->s == NULL)
-			goto need_obj;
-	}
-	return;
-
- need_obj:
-	buf = get_obj(ref->objectname, &obj, &size, &eaten);
-	if (!buf)
-		die("missing object %s for %s",
-		    sha1_to_hex(ref->objectname), ref->refname);
-	if (!obj)
-		die("parse_object_buffer failed on %s for %s",
-		    sha1_to_hex(ref->objectname), ref->refname);
-
-	grab_values(ref->value, 0, obj, buf, size);
-	if (!eaten)
-		free(buf);
-
-	/* If there is no atom that wants to know about tagged
-	 * object, we are done.
-	 */
-	if (!need_tagged || (obj->type != OBJ_TAG))
-		return;
-
-	/* If it is a tag object, see if we use a value that derefs
-	 * the object, and if we do grab the object it refers to.
-	 */
-	tagged = ((struct tag *)obj)->tagged->sha1;
-
-	/* NEEDSWORK: This derefs tag only once, which
-	 * is good to deal with chains of trust, but
-	 * is not consistent with what deref_tag() does
-	 * which peels the onion to the core.
-	 */
-	buf = get_obj(tagged, &obj, &size, &eaten);
-	if (!buf)
-		die("missing object %s for %s",
-		    sha1_to_hex(tagged), ref->refname);
-	if (!obj)
-		die("parse_object_buffer failed on %s for %s",
-		    sha1_to_hex(tagged), ref->refname);
-	grab_values(ref->value, 1, obj, buf, size);
-	if (!eaten)
-		free(buf);
-}
-
-/*
- * Given a ref, return the value for the atom.  This lazily gets value
- * out of the object by calling populate value.
- */
-static void get_value(struct refinfo *ref, int atom, struct atom_value **v)
-{
-	if (!ref->value) {
-		populate_value(ref);
-		fill_missing_values(ref->value);
-	}
-	*v = &ref->value[atom];
-}
-
-struct grab_ref_cbdata {
-	struct refinfo **grab_array;
-	const char **grab_pattern;
-	int grab_cnt;
-};
-
-/*
- * A call-back given to for_each_ref().  It is unfortunate that we
- * need to use global variables to pass extra information to this
- * function.
- */
-static int grab_single_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
-{
-	struct grab_ref_cbdata *cb = cb_data;
-	struct refinfo *ref;
-	int cnt;
-
-	if (*cb->grab_pattern) {
-		const char **pattern;
-		int namelen = strlen(refname);
-		for (pattern = cb->grab_pattern; *pattern; pattern++) {
-			const char *p = *pattern;
-			int plen = strlen(p);
-
-			if ((plen <= namelen) &&
-			    !strncmp(refname, p, plen) &&
-			    (refname[plen] == '\0' ||
-			     refname[plen] == '/' ||
-			     p[plen-1] == '/'))
-				break;
-			if (!fnmatch(p, refname, FNM_PATHNAME))
-				break;
-		}
-		if (!*pattern)
-			return 0;
-	}
-
-	/* We do not open the object yet; sort may only need refname
-	 * to do its job and the resulting list may yet to be pruned
-	 * by maxcount logic.
-	 */
-	ref = xcalloc(1, sizeof(*ref));
-	ref->refname = xstrdup(refname);
-	hashcpy(ref->objectname, sha1);
-
-	cnt = cb->grab_cnt;
-	cb->grab_array = xrealloc(cb->grab_array,
-				  sizeof(*cb->grab_array) * (cnt + 1));
-	cb->grab_array[cnt++] = ref;
-	cb->grab_cnt = cnt;
-	return 0;
-}
-
-static int cmp_ref_sort(struct ref_sort *s, struct refinfo *a, struct refinfo *b)
-{
-	struct atom_value *va, *vb;
-	int cmp;
-	cmp_type cmp_type = used_atom_type[s->atom];
-
-	get_value(a, s->atom, &va);
-	get_value(b, s->atom, &vb);
-	switch (cmp_type) {
-	case FIELD_STR:
-		cmp = strcmp(va->s, vb->s);
-		break;
-	default:
-		if (va->ul < vb->ul)
-			cmp = -1;
-		else if (va->ul == vb->ul)
-			cmp = 0;
-		else
-			cmp = 1;
-		break;
-	}
-	return (s->reverse) ? -cmp : cmp;
-}
-
-static struct ref_sort *ref_sort;
-static int compare_refs(const void *a_, const void *b_)
-{
-	struct refinfo *a = *((struct refinfo **)a_);
-	struct refinfo *b = *((struct refinfo **)b_);
-	struct ref_sort *s;
-
-	for (s = ref_sort; s; s = s->next) {
-		int cmp = cmp_ref_sort(s, a, b);
-		if (cmp)
-			return cmp;
-	}
-	return 0;
-}
-
-static void sort_refs(struct ref_sort *sort, struct refinfo **refs, int num_refs)
-{
-	ref_sort = sort;
-	qsort(refs, num_refs, sizeof(struct refinfo *), compare_refs);
-}
-
-static void print_value(struct refinfo *ref, int atom, int quote_style)
-{
-	struct atom_value *v;
-	get_value(ref, atom, &v);
-	switch (quote_style) {
-	case QUOTE_NONE:
-		fputs(v->s, stdout);
-		break;
-	case QUOTE_SHELL:
-		sq_quote_print(stdout, v->s);
-		break;
-	case QUOTE_PERL:
-		perl_quote_print(stdout, v->s);
-		break;
-	case QUOTE_PYTHON:
-		python_quote_print(stdout, v->s);
-		break;
-	case QUOTE_TCL:
-		tcl_quote_print(stdout, v->s);
-		break;
-	}
-}
-
-static int hex1(char ch)
-{
-	if ('0' <= ch && ch <= '9')
-		return ch - '0';
-	else if ('a' <= ch && ch <= 'f')
-		return ch - 'a' + 10;
-	else if ('A' <= ch && ch <= 'F')
-		return ch - 'A' + 10;
-	return -1;
-}
-static int hex2(const char *cp)
-{
-	if (cp[0] && cp[1])
-		return (hex1(cp[0]) << 4) | hex1(cp[1]);
-	else
-		return -1;
-}
-
-static void emit(const char *cp, const char *ep)
-{
-	while (*cp && (!ep || cp < ep)) {
-		if (*cp == '%') {
-			if (cp[1] == '%')
-				cp++;
-			else {
-				int ch = hex2(cp + 1);
-				if (0 <= ch) {
-					putchar(ch);
-					cp += 3;
-					continue;
-				}
-			}
-		}
-		putchar(*cp);
-		cp++;
-	}
-}
-
-static void show_ref(struct refinfo *info, const char *format, int quote_style)
-{
-	const char *cp, *sp, *ep;
-
-	for (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) {
-		ep = strchr(sp, ')');
-		if (cp < sp)
-			emit(cp, sp);
-		print_value(info, parse_atom(sp + 2, ep), quote_style);
-	}
-	if (*cp) {
-		sp = cp + strlen(cp);
-		emit(cp, sp);
-	}
-	putchar('\n');
-}
-
-static struct ref_sort *default_sort(void)
-{
-	static const char cstr_name[] = "refname";
-
-	struct ref_sort *sort = xcalloc(1, sizeof(*sort));
-
-	sort->next = NULL;
-	sort->atom = parse_atom(cstr_name, cstr_name + strlen(cstr_name));
-	return sort;
-}
-
-static int opt_parse_sort(const struct option *opt, const char *arg, int unset)
-{
-	struct ref_sort **sort_tail = opt->value;
-	struct ref_sort *s;
-	int len;
-
-	if (!arg) /* should --no-sort void the list ? */
-		return -1;
-
-	*sort_tail = s = xcalloc(1, sizeof(*s));
-
-	if (*arg == '-') {
-		s->reverse = 1;
-		arg++;
-	}
-	len = strlen(arg);
-	s->atom = parse_atom(arg, arg+len);
-	return 0;
-}
-
-static char const * const for_each_ref_usage[] = {
-	"git for-each-ref [options] [<pattern>]",
-	NULL
-};
-
-int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
-{
-	int i, num_refs;
-	const char *format = "%(objectname) %(objecttype)\t%(refname)";
-	struct ref_sort *sort = NULL, **sort_tail = &sort;
-	int maxcount = 0, quote_style = 0;
-	struct refinfo **refs;
-	struct grab_ref_cbdata cbdata;
-
-	struct option opts[] = {
-		OPT_BIT('s', "shell", &quote_style,
-		        "quote placeholders suitably for shells", QUOTE_SHELL),
-		OPT_BIT('p', "perl",  &quote_style,
-		        "quote placeholders suitably for perl", QUOTE_PERL),
-		OPT_BIT(0 , "python", &quote_style,
-		        "quote placeholders suitably for python", QUOTE_PYTHON),
-		OPT_BIT(0 , "tcl",  &quote_style,
-		        "quote placeholders suitably for tcl", QUOTE_TCL),
-
-		OPT_GROUP(""),
-		OPT_INTEGER( 0 , "count", &maxcount, "show only <n> matched refs"),
-		OPT_STRING(  0 , "format", &format, "format", "format to use for the output"),
-		OPT_CALLBACK(0 , "sort", sort_tail, "key",
-		            "field name to sort on", &opt_parse_sort),
-		OPT_END(),
-	};
-
-	parse_options(argc, argv, prefix, opts, for_each_ref_usage, 0);
-	if (maxcount < 0) {
-		error("invalid --count argument: `%d'", maxcount);
-		usage_with_options(for_each_ref_usage, opts);
-	}
-	if (HAS_MULTI_BITS(quote_style)) {
-		error("more than one quoting style?");
-		usage_with_options(for_each_ref_usage, opts);
-	}
-	if (verify_format(format))
-		usage_with_options(for_each_ref_usage, opts);
-
-	if (!sort)
-		sort = default_sort();
-	sort_atom_limit = used_atom_cnt;
-
-	/* for warn_ambiguous_refs */
-	git_config(git_default_config, NULL);
-
-	memset(&cbdata, 0, sizeof(cbdata));
-	cbdata.grab_pattern = argv;
-	for_each_rawref(grab_single_ref, &cbdata);
-	refs = cbdata.grab_array;
-	num_refs = cbdata.grab_cnt;
-
-	for (i = 0; i < used_atom_cnt; i++) {
-		if (used_atom[i][0] == '*') {
-			need_tagged = 1;
-			break;
-		}
-	}
-
-	sort_refs(sort, refs, num_refs);
-
-	if (!maxcount || num_refs < maxcount)
-		maxcount = num_refs;
-	for (i = 0; i < maxcount; i++)
-		show_ref(refs[i], format, quote_style);
-	return 0;
-}
diff --git a/builtin-fsck.c b/builtin-fsck.c
deleted file mode 100644
index b3d38fa..0000000
--- a/builtin-fsck.c
+++ /dev/null
@@ -1,681 +0,0 @@
-#include "builtin.h"
-#include "cache.h"
-#include "commit.h"
-#include "tree.h"
-#include "blob.h"
-#include "tag.h"
-#include "refs.h"
-#include "pack.h"
-#include "cache-tree.h"
-#include "tree-walk.h"
-#include "fsck.h"
-#include "parse-options.h"
-#include "dir.h"
-
-#define REACHABLE 0x0001
-#define SEEN      0x0002
-
-static int show_root;
-static int show_tags;
-static int show_unreachable;
-static int include_reflogs = 1;
-static int check_full;
-static int check_strict;
-static int keep_cache_objects;
-static unsigned char head_sha1[20];
-static const char *head_points_at;
-static int errors_found;
-static int write_lost_and_found;
-static int verbose;
-#define ERROR_OBJECT 01
-#define ERROR_REACHABLE 02
-
-#ifdef NO_D_INO_IN_DIRENT
-#define SORT_DIRENT 0
-#define DIRENT_SORT_HINT(de) 0
-#else
-#define SORT_DIRENT 1
-#define DIRENT_SORT_HINT(de) ((de)->d_ino)
-#endif
-
-static void objreport(struct object *obj, const char *severity,
-                      const char *err, va_list params)
-{
-	fprintf(stderr, "%s in %s %s: ",
-	        severity, typename(obj->type), sha1_to_hex(obj->sha1));
-	vfprintf(stderr, err, params);
-	fputs("\n", stderr);
-}
-
-static int objerror(struct object *obj, const char *err, ...)
-{
-	va_list params;
-	va_start(params, err);
-	errors_found |= ERROR_OBJECT;
-	objreport(obj, "error", err, params);
-	va_end(params);
-	return -1;
-}
-
-static int fsck_error_func(struct object *obj, int type, const char *err, ...)
-{
-	va_list params;
-	va_start(params, err);
-	objreport(obj, (type == FSCK_WARN) ? "warning" : "error", err, params);
-	va_end(params);
-	return (type == FSCK_WARN) ? 0 : 1;
-}
-
-static struct object_array pending;
-
-static int mark_object(struct object *obj, int type, void *data)
-{
-	struct object *parent = data;
-
-	if (!obj) {
-		printf("broken link from %7s %s\n",
-			   typename(parent->type), sha1_to_hex(parent->sha1));
-		printf("broken link from %7s %s\n",
-			   (type == OBJ_ANY ? "unknown" : typename(type)), "unknown");
-		errors_found |= ERROR_REACHABLE;
-		return 1;
-	}
-
-	if (type != OBJ_ANY && obj->type != type)
-		objerror(parent, "wrong object type in link");
-
-	if (obj->flags & REACHABLE)
-		return 0;
-	obj->flags |= REACHABLE;
-	if (!obj->parsed) {
-		if (parent && !has_sha1_file(obj->sha1)) {
-			printf("broken link from %7s %s\n",
-				 typename(parent->type), sha1_to_hex(parent->sha1));
-			printf("              to %7s %s\n",
-				 typename(obj->type), sha1_to_hex(obj->sha1));
-			errors_found |= ERROR_REACHABLE;
-		}
-		return 1;
-	}
-
-	add_object_array(obj, (void *) parent, &pending);
-	return 0;
-}
-
-static void mark_object_reachable(struct object *obj)
-{
-	mark_object(obj, OBJ_ANY, NULL);
-}
-
-static int traverse_one_object(struct object *obj, struct object *parent)
-{
-	int result;
-	struct tree *tree = NULL;
-
-	if (obj->type == OBJ_TREE) {
-		obj->parsed = 0;
-		tree = (struct tree *)obj;
-		if (parse_tree(tree) < 0)
-			return 1; /* error already displayed */
-	}
-	result = fsck_walk(obj, mark_object, obj);
-	if (tree) {
-		free(tree->buffer);
-		tree->buffer = NULL;
-	}
-	return result;
-}
-
-static int traverse_reachable(void)
-{
-	int result = 0;
-	while (pending.nr) {
-		struct object_array_entry *entry;
-		struct object *obj, *parent;
-
-		entry = pending.objects + --pending.nr;
-		obj = entry->item;
-		parent = (struct object *) entry->name;
-		result |= traverse_one_object(obj, parent);
-	}
-	return !!result;
-}
-
-static int mark_used(struct object *obj, int type, void *data)
-{
-	if (!obj)
-		return 1;
-	obj->used = 1;
-	return 0;
-}
-
-/*
- * Check a single reachable object
- */
-static void check_reachable_object(struct object *obj)
-{
-	/*
-	 * We obviously want the object to be parsed,
-	 * except if it was in a pack-file and we didn't
-	 * do a full fsck
-	 */
-	if (!obj->parsed) {
-		if (has_sha1_pack(obj->sha1))
-			return; /* it is in pack - forget about it */
-		printf("missing %s %s\n", typename(obj->type), sha1_to_hex(obj->sha1));
-		errors_found |= ERROR_REACHABLE;
-		return;
-	}
-}
-
-/*
- * Check a single unreachable object
- */
-static void check_unreachable_object(struct object *obj)
-{
-	/*
-	 * Missing unreachable object? Ignore it. It's not like
-	 * we miss it (since it can't be reached), nor do we want
-	 * to complain about it being unreachable (since it does
-	 * not exist).
-	 */
-	if (!obj->parsed)
-		return;
-
-	/*
-	 * Unreachable object that exists? Show it if asked to,
-	 * since this is something that is prunable.
-	 */
-	if (show_unreachable) {
-		printf("unreachable %s %s\n", typename(obj->type), sha1_to_hex(obj->sha1));
-		return;
-	}
-
-	/*
-	 * "!used" means that nothing at all points to it, including
-	 * other unreachable objects. In other words, it's the "tip"
-	 * of some set of unreachable objects, usually a commit that
-	 * got dropped.
-	 *
-	 * Such starting points are more interesting than some random
-	 * set of unreachable objects, so we show them even if the user
-	 * hasn't asked for _all_ unreachable objects. If you have
-	 * deleted a branch by mistake, this is a prime candidate to
-	 * start looking at, for example.
-	 */
-	if (!obj->used) {
-		printf("dangling %s %s\n", typename(obj->type),
-		       sha1_to_hex(obj->sha1));
-		if (write_lost_and_found) {
-			char *filename = git_path("lost-found/%s/%s",
-				obj->type == OBJ_COMMIT ? "commit" : "other",
-				sha1_to_hex(obj->sha1));
-			FILE *f;
-
-			if (safe_create_leading_directories(filename)) {
-				error("Could not create lost-found");
-				return;
-			}
-			if (!(f = fopen(filename, "w")))
-				die_errno("Could not open '%s'", filename);
-			if (obj->type == OBJ_BLOB) {
-				enum object_type type;
-				unsigned long size;
-				char *buf = read_sha1_file(obj->sha1,
-						&type, &size);
-				if (buf) {
-					if (fwrite(buf, size, 1, f) != 1)
-						die_errno("Could not write '%s'",
-							  filename);
-					free(buf);
-				}
-			} else
-				fprintf(f, "%s\n", sha1_to_hex(obj->sha1));
-			if (fclose(f))
-				die_errno("Could not finish '%s'",
-					  filename);
-		}
-		return;
-	}
-
-	/*
-	 * Otherwise? It's there, it's unreachable, and some other unreachable
-	 * object points to it. Ignore it - it's not interesting, and we showed
-	 * all the interesting cases above.
-	 */
-}
-
-static void check_object(struct object *obj)
-{
-	if (verbose)
-		fprintf(stderr, "Checking %s\n", sha1_to_hex(obj->sha1));
-
-	if (obj->flags & REACHABLE)
-		check_reachable_object(obj);
-	else
-		check_unreachable_object(obj);
-}
-
-static void check_connectivity(void)
-{
-	int i, max;
-
-	/* Traverse the pending reachable objects */
-	traverse_reachable();
-
-	/* Look up all the requirements, warn about missing objects.. */
-	max = get_max_object_index();
-	if (verbose)
-		fprintf(stderr, "Checking connectivity (%d objects)\n", max);
-
-	for (i = 0; i < max; i++) {
-		struct object *obj = get_indexed_object(i);
-
-		if (obj)
-			check_object(obj);
-	}
-}
-
-static int fsck_sha1(const unsigned char *sha1)
-{
-	struct object *obj = parse_object(sha1);
-	if (!obj) {
-		errors_found |= ERROR_OBJECT;
-		return error("%s: object corrupt or missing",
-			     sha1_to_hex(sha1));
-	}
-	if (obj->flags & SEEN)
-		return 0;
-	obj->flags |= SEEN;
-
-	if (verbose)
-		fprintf(stderr, "Checking %s %s\n",
-			typename(obj->type), sha1_to_hex(obj->sha1));
-
-	if (fsck_walk(obj, mark_used, NULL))
-		objerror(obj, "broken links");
-	if (fsck_object(obj, check_strict, fsck_error_func))
-		return -1;
-
-	if (obj->type == OBJ_TREE) {
-		struct tree *item = (struct tree *) obj;
-
-		free(item->buffer);
-		item->buffer = NULL;
-	}
-
-	if (obj->type == OBJ_COMMIT) {
-		struct commit *commit = (struct commit *) obj;
-
-		free(commit->buffer);
-		commit->buffer = NULL;
-
-		if (!commit->parents && show_root)
-			printf("root %s\n", sha1_to_hex(commit->object.sha1));
-	}
-
-	if (obj->type == OBJ_TAG) {
-		struct tag *tag = (struct tag *) obj;
-
-		if (show_tags && tag->tagged) {
-			printf("tagged %s %s", typename(tag->tagged->type), sha1_to_hex(tag->tagged->sha1));
-			printf(" (%s) in %s\n", tag->tag, sha1_to_hex(tag->object.sha1));
-		}
-	}
-
-	return 0;
-}
-
-/*
- * This is the sorting chunk size: make it reasonably
- * big so that we can sort well..
- */
-#define MAX_SHA1_ENTRIES (1024)
-
-struct sha1_entry {
-	unsigned long ino;
-	unsigned char sha1[20];
-};
-
-static struct {
-	unsigned long nr;
-	struct sha1_entry *entry[MAX_SHA1_ENTRIES];
-} sha1_list;
-
-static int ino_compare(const void *_a, const void *_b)
-{
-	const struct sha1_entry *a = _a, *b = _b;
-	unsigned long ino1 = a->ino, ino2 = b->ino;
-	return ino1 < ino2 ? -1 : ino1 > ino2 ? 1 : 0;
-}
-
-static void fsck_sha1_list(void)
-{
-	int i, nr = sha1_list.nr;
-
-	if (SORT_DIRENT)
-		qsort(sha1_list.entry, nr,
-		      sizeof(struct sha1_entry *), ino_compare);
-	for (i = 0; i < nr; i++) {
-		struct sha1_entry *entry = sha1_list.entry[i];
-		unsigned char *sha1 = entry->sha1;
-
-		sha1_list.entry[i] = NULL;
-		fsck_sha1(sha1);
-		free(entry);
-	}
-	sha1_list.nr = 0;
-}
-
-static void add_sha1_list(unsigned char *sha1, unsigned long ino)
-{
-	struct sha1_entry *entry = xmalloc(sizeof(*entry));
-	int nr;
-
-	entry->ino = ino;
-	hashcpy(entry->sha1, sha1);
-	nr = sha1_list.nr;
-	if (nr == MAX_SHA1_ENTRIES) {
-		fsck_sha1_list();
-		nr = 0;
-	}
-	sha1_list.entry[nr] = entry;
-	sha1_list.nr = ++nr;
-}
-
-static void fsck_dir(int i, char *path)
-{
-	DIR *dir = opendir(path);
-	struct dirent *de;
-
-	if (!dir)
-		return;
-
-	if (verbose)
-		fprintf(stderr, "Checking directory %s\n", path);
-
-	while ((de = readdir(dir)) != NULL) {
-		char name[100];
-		unsigned char sha1[20];
-
-		if (is_dot_or_dotdot(de->d_name))
-			continue;
-		if (strlen(de->d_name) == 38) {
-			sprintf(name, "%02x", i);
-			memcpy(name+2, de->d_name, 39);
-			if (get_sha1_hex(name, sha1) < 0)
-				break;
-			add_sha1_list(sha1, DIRENT_SORT_HINT(de));
-			continue;
-		}
-		if (!prefixcmp(de->d_name, "tmp_obj_"))
-			continue;
-		fprintf(stderr, "bad sha1 file: %s/%s\n", path, de->d_name);
-	}
-	closedir(dir);
-}
-
-static int default_refs;
-
-static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
-		const char *email, unsigned long timestamp, int tz,
-		const char *message, void *cb_data)
-{
-	struct object *obj;
-
-	if (verbose)
-		fprintf(stderr, "Checking reflog %s->%s\n",
-			sha1_to_hex(osha1), sha1_to_hex(nsha1));
-
-	if (!is_null_sha1(osha1)) {
-		obj = lookup_object(osha1);
-		if (obj) {
-			obj->used = 1;
-			mark_object_reachable(obj);
-		}
-	}
-	obj = lookup_object(nsha1);
-	if (obj) {
-		obj->used = 1;
-		mark_object_reachable(obj);
-	}
-	return 0;
-}
-
-static int fsck_handle_reflog(const char *logname, const unsigned char *sha1, int flag, void *cb_data)
-{
-	for_each_reflog_ent(logname, fsck_handle_reflog_ent, NULL);
-	return 0;
-}
-
-static int is_branch(const char *refname)
-{
-	return !strcmp(refname, "HEAD") || !prefixcmp(refname, "refs/heads/");
-}
-
-static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
-{
-	struct object *obj;
-
-	obj = parse_object(sha1);
-	if (!obj) {
-		error("%s: invalid sha1 pointer %s", refname, sha1_to_hex(sha1));
-		/* We'll continue with the rest despite the error.. */
-		return 0;
-	}
-	if (obj->type != OBJ_COMMIT && is_branch(refname))
-		error("%s: not a commit", refname);
-	default_refs++;
-	obj->used = 1;
-	mark_object_reachable(obj);
-
-	return 0;
-}
-
-static void get_default_heads(void)
-{
-	if (head_points_at && !is_null_sha1(head_sha1))
-		fsck_handle_ref("HEAD", head_sha1, 0, NULL);
-	for_each_ref(fsck_handle_ref, NULL);
-	if (include_reflogs)
-		for_each_reflog(fsck_handle_reflog, NULL);
-
-	/*
-	 * Not having any default heads isn't really fatal, but
-	 * it does mean that "--unreachable" no longer makes any
-	 * sense (since in this case everything will obviously
-	 * be unreachable by definition.
-	 *
-	 * Showing dangling objects is valid, though (as those
-	 * dangling objects are likely lost heads).
-	 *
-	 * So we just print a warning about it, and clear the
-	 * "show_unreachable" flag.
-	 */
-	if (!default_refs) {
-		fprintf(stderr, "notice: No default references\n");
-		show_unreachable = 0;
-	}
-}
-
-static void fsck_object_dir(const char *path)
-{
-	int i;
-
-	if (verbose)
-		fprintf(stderr, "Checking object directory\n");
-
-	for (i = 0; i < 256; i++) {
-		static char dir[4096];
-		sprintf(dir, "%s/%02x", path, i);
-		fsck_dir(i, dir);
-	}
-	fsck_sha1_list();
-}
-
-static int fsck_head_link(void)
-{
-	int flag;
-	int null_is_error = 0;
-
-	if (verbose)
-		fprintf(stderr, "Checking HEAD link\n");
-
-	head_points_at = resolve_ref("HEAD", head_sha1, 0, &flag);
-	if (!head_points_at)
-		return error("Invalid HEAD");
-	if (!strcmp(head_points_at, "HEAD"))
-		/* detached HEAD */
-		null_is_error = 1;
-	else if (prefixcmp(head_points_at, "refs/heads/"))
-		return error("HEAD points to something strange (%s)",
-			     head_points_at);
-	if (is_null_sha1(head_sha1)) {
-		if (null_is_error)
-			return error("HEAD: detached HEAD points at nothing");
-		fprintf(stderr, "notice: HEAD points to an unborn branch (%s)\n",
-			head_points_at + 11);
-	}
-	return 0;
-}
-
-static int fsck_cache_tree(struct cache_tree *it)
-{
-	int i;
-	int err = 0;
-
-	if (verbose)
-		fprintf(stderr, "Checking cache tree\n");
-
-	if (0 <= it->entry_count) {
-		struct object *obj = parse_object(it->sha1);
-		if (!obj) {
-			error("%s: invalid sha1 pointer in cache-tree",
-			      sha1_to_hex(it->sha1));
-			return 1;
-		}
-		mark_object_reachable(obj);
-		obj->used = 1;
-		if (obj->type != OBJ_TREE)
-			err |= objerror(obj, "non-tree in cache-tree");
-	}
-	for (i = 0; i < it->subtree_nr; i++)
-		err |= fsck_cache_tree(it->down[i]->cache_tree);
-	return err;
-}
-
-static char const * const fsck_usage[] = {
-	"git fsck [options] [<object>...]",
-	NULL
-};
-
-static struct option fsck_opts[] = {
-	OPT__VERBOSE(&verbose),
-	OPT_BOOLEAN(0, "unreachable", &show_unreachable, "show unreachable objects"),
-	OPT_BOOLEAN(0, "tags", &show_tags, "report tags"),
-	OPT_BOOLEAN(0, "root", &show_root, "report root nodes"),
-	OPT_BOOLEAN(0, "cache", &keep_cache_objects, "make index objects head nodes"),
-	OPT_BOOLEAN(0, "reflogs", &include_reflogs, "make reflogs head nodes (default)"),
-	OPT_BOOLEAN(0, "full", &check_full, "also consider alternate objects"),
-	OPT_BOOLEAN(0, "strict", &check_strict, "enable more strict checking"),
-	OPT_BOOLEAN(0, "lost-found", &write_lost_and_found,
-				"write dangling objects in .git/lost-found"),
-	OPT_END(),
-};
-
-int cmd_fsck(int argc, const char **argv, const char *prefix)
-{
-	int i, heads;
-	struct alternate_object_database *alt;
-
-	errors_found = 0;
-
-	argc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0);
-	if (write_lost_and_found) {
-		check_full = 1;
-		include_reflogs = 0;
-	}
-
-	fsck_head_link();
-	fsck_object_dir(get_object_directory());
-
-	prepare_alt_odb();
-	for (alt = alt_odb_list; alt; alt = alt->next) {
-		char namebuf[PATH_MAX];
-		int namelen = alt->name - alt->base;
-		memcpy(namebuf, alt->base, namelen);
-		namebuf[namelen - 1] = 0;
-		fsck_object_dir(namebuf);
-	}
-
-	if (check_full) {
-		struct packed_git *p;
-
-		prepare_packed_git();
-		for (p = packed_git; p; p = p->next)
-			/* verify gives error messages itself */
-			verify_pack(p);
-
-		for (p = packed_git; p; p = p->next) {
-			uint32_t j, num;
-			if (open_pack_index(p))
-				continue;
-			num = p->num_objects;
-			for (j = 0; j < num; j++)
-				fsck_sha1(nth_packed_object_sha1(p, j));
-		}
-	}
-
-	heads = 0;
-	for (i = 0; i < argc; i++) {
-		const char *arg = argv[i];
-		unsigned char sha1[20];
-		if (!get_sha1(arg, sha1)) {
-			struct object *obj = lookup_object(sha1);
-
-			/* Error is printed by lookup_object(). */
-			if (!obj)
-				continue;
-
-			obj->used = 1;
-			mark_object_reachable(obj);
-			heads++;
-			continue;
-		}
-		error("invalid parameter: expected sha1, got '%s'", arg);
-	}
-
-	/*
-	 * If we've not been given any explicit head information, do the
-	 * default ones from .git/refs. We also consider the index file
-	 * in this case (ie this implies --cache).
-	 */
-	if (!heads) {
-		get_default_heads();
-		keep_cache_objects = 1;
-	}
-
-	if (keep_cache_objects) {
-		read_cache();
-		for (i = 0; i < active_nr; i++) {
-			unsigned int mode;
-			struct blob *blob;
-			struct object *obj;
-
-			mode = active_cache[i]->ce_mode;
-			if (S_ISGITLINK(mode))
-				continue;
-			blob = lookup_blob(active_cache[i]->sha1);
-			if (!blob)
-				continue;
-			obj = &blob->object;
-			obj->used = 1;
-			mark_object_reachable(obj);
-		}
-		if (active_cache_tree)
-			fsck_cache_tree(active_cache_tree);
-	}
-
-	check_connectivity();
-	return errors_found;
-}
diff --git a/builtin-gc.c b/builtin-gc.c
deleted file mode 100644
index 7d3e9cc..0000000
--- a/builtin-gc.c
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- * git gc builtin command
- *
- * Cleanup unreachable files and optimize the repository.
- *
- * Copyright (c) 2007 James Bowes
- *
- * Based on git-gc.sh, which is
- *
- * Copyright (c) 2006 Shawn O. Pearce
- */
-
-#include "builtin.h"
-#include "cache.h"
-#include "parse-options.h"
-#include "run-command.h"
-
-#define FAILED_RUN "failed to run %s"
-
-static const char * const builtin_gc_usage[] = {
-	"git gc [options]",
-	NULL
-};
-
-static int pack_refs = 1;
-static int aggressive_window = 250;
-static int gc_auto_threshold = 6700;
-static int gc_auto_pack_limit = 50;
-static const char *prune_expire = "2.weeks.ago";
-
-#define MAX_ADD 10
-static const char *argv_pack_refs[] = {"pack-refs", "--all", "--prune", NULL};
-static const char *argv_reflog[] = {"reflog", "expire", "--all", NULL};
-static const char *argv_repack[MAX_ADD] = {"repack", "-d", "-l", NULL};
-static const char *argv_prune[] = {"prune", "--expire", NULL, NULL};
-static const char *argv_rerere[] = {"rerere", "gc", NULL};
-
-static int gc_config(const char *var, const char *value, void *cb)
-{
-	if (!strcmp(var, "gc.packrefs")) {
-		if (value && !strcmp(value, "notbare"))
-			pack_refs = -1;
-		else
-			pack_refs = git_config_bool(var, value);
-		return 0;
-	}
-	if (!strcmp(var, "gc.aggressivewindow")) {
-		aggressive_window = git_config_int(var, value);
-		return 0;
-	}
-	if (!strcmp(var, "gc.auto")) {
-		gc_auto_threshold = git_config_int(var, value);
-		return 0;
-	}
-	if (!strcmp(var, "gc.autopacklimit")) {
-		gc_auto_pack_limit = git_config_int(var, value);
-		return 0;
-	}
-	if (!strcmp(var, "gc.pruneexpire")) {
-		if (value && strcmp(value, "now")) {
-			unsigned long now = approxidate("now");
-			if (approxidate(value) >= now)
-				return error("Invalid %s: '%s'", var, value);
-		}
-		return git_config_string(&prune_expire, var, value);
-	}
-	return git_default_config(var, value, cb);
-}
-
-static void append_option(const char **cmd, const char *opt, int max_length)
-{
-	int i;
-
-	for (i = 0; cmd[i]; i++)
-		;
-
-	if (i + 2 >= max_length)
-		die("Too many options specified");
-	cmd[i++] = opt;
-	cmd[i] = NULL;
-}
-
-static int too_many_loose_objects(void)
-{
-	/*
-	 * Quickly check if a "gc" is needed, by estimating how
-	 * many loose objects there are.  Because SHA-1 is evenly
-	 * distributed, we can check only one and get a reasonable
-	 * estimate.
-	 */
-	char path[PATH_MAX];
-	const char *objdir = get_object_directory();
-	DIR *dir;
-	struct dirent *ent;
-	int auto_threshold;
-	int num_loose = 0;
-	int needed = 0;
-
-	if (gc_auto_threshold <= 0)
-		return 0;
-
-	if (sizeof(path) <= snprintf(path, sizeof(path), "%s/17", objdir)) {
-		warning("insanely long object directory %.*s", 50, objdir);
-		return 0;
-	}
-	dir = opendir(path);
-	if (!dir)
-		return 0;
-
-	auto_threshold = (gc_auto_threshold + 255) / 256;
-	while ((ent = readdir(dir)) != NULL) {
-		if (strspn(ent->d_name, "0123456789abcdef") != 38 ||
-		    ent->d_name[38] != '\0')
-			continue;
-		if (++num_loose > auto_threshold) {
-			needed = 1;
-			break;
-		}
-	}
-	closedir(dir);
-	return needed;
-}
-
-static int too_many_packs(void)
-{
-	struct packed_git *p;
-	int cnt;
-
-	if (gc_auto_pack_limit <= 0)
-		return 0;
-
-	prepare_packed_git();
-	for (cnt = 0, p = packed_git; p; p = p->next) {
-		if (!p->pack_local)
-			continue;
-		if (p->pack_keep)
-			continue;
-		/*
-		 * Perhaps check the size of the pack and count only
-		 * very small ones here?
-		 */
-		cnt++;
-	}
-	return gc_auto_pack_limit <= cnt;
-}
-
-static int need_to_gc(void)
-{
-	/*
-	 * Setting gc.auto to 0 or negative can disable the
-	 * automatic gc.
-	 */
-	if (gc_auto_threshold <= 0)
-		return 0;
-
-	/*
-	 * If there are too many loose objects, but not too many
-	 * packs, we run "repack -d -l".  If there are too many packs,
-	 * we run "repack -A -d -l".  Otherwise we tell the caller
-	 * there is no need.
-	 */
-	if (too_many_packs())
-		append_option(argv_repack,
-			      prune_expire && !strcmp(prune_expire, "now") ?
-			      "-a" : "-A",
-			      MAX_ADD);
-	else if (!too_many_loose_objects())
-		return 0;
-
-	if (run_hook(NULL, "pre-auto-gc", NULL))
-		return 0;
-	return 1;
-}
-
-int cmd_gc(int argc, const char **argv, const char *prefix)
-{
-	int aggressive = 0;
-	int auto_gc = 0;
-	int quiet = 0;
-	char buf[80];
-
-	struct option builtin_gc_options[] = {
-		{ OPTION_STRING, 0, "prune", &prune_expire, "date",
-			"prune unreferenced objects",
-			PARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire },
-		OPT_BOOLEAN(0, "aggressive", &aggressive, "be more thorough (increased runtime)"),
-		OPT_BOOLEAN(0, "auto", &auto_gc, "enable auto-gc mode"),
-		OPT_BOOLEAN('q', "quiet", &quiet, "suppress progress reports"),
-		OPT_END()
-	};
-
-	git_config(gc_config, NULL);
-
-	if (pack_refs < 0)
-		pack_refs = !is_bare_repository();
-
-	argc = parse_options(argc, argv, prefix, builtin_gc_options,
-			     builtin_gc_usage, 0);
-	if (argc > 0)
-		usage_with_options(builtin_gc_usage, builtin_gc_options);
-
-	if (aggressive) {
-		append_option(argv_repack, "-f", MAX_ADD);
-		append_option(argv_repack, "--depth=250", MAX_ADD);
-		if (aggressive_window > 0) {
-			sprintf(buf, "--window=%d", aggressive_window);
-			append_option(argv_repack, buf, MAX_ADD);
-		}
-	}
-	if (quiet)
-		append_option(argv_repack, "-q", MAX_ADD);
-
-	if (auto_gc) {
-		/*
-		 * Auto-gc should be least intrusive as possible.
-		 */
-		if (!need_to_gc())
-			return 0;
-		fprintf(stderr, "Auto packing your repository for optimum "
-			"performance. You may also\n"
-			"run \"git gc\" manually. See "
-			"\"git help gc\" for more information.\n");
-	} else
-		append_option(argv_repack,
-			      prune_expire && !strcmp(prune_expire, "now")
-			      ? "-a" : "-A",
-			      MAX_ADD);
-
-	if (pack_refs && run_command_v_opt(argv_pack_refs, RUN_GIT_CMD))
-		return error(FAILED_RUN, argv_pack_refs[0]);
-
-	if (run_command_v_opt(argv_reflog, RUN_GIT_CMD))
-		return error(FAILED_RUN, argv_reflog[0]);
-
-	if (run_command_v_opt(argv_repack, RUN_GIT_CMD))
-		return error(FAILED_RUN, argv_repack[0]);
-
-	if (prune_expire) {
-		argv_prune[2] = prune_expire;
-		if (run_command_v_opt(argv_prune, RUN_GIT_CMD))
-			return error(FAILED_RUN, argv_prune[0]);
-	}
-
-	if (run_command_v_opt(argv_rerere, RUN_GIT_CMD))
-		return error(FAILED_RUN, argv_rerere[0]);
-
-	if (auto_gc && too_many_loose_objects())
-		warning("There are too many unreachable loose objects; "
-			"run 'git prune' to remove them.");
-
-	return 0;
-}
diff --git a/builtin-grep.c b/builtin-grep.c
deleted file mode 100644
index f477659..0000000
--- a/builtin-grep.c
+++ /dev/null
@@ -1,867 +0,0 @@
-/*
- * Builtin "git grep"
- *
- * Copyright (c) 2006 Junio C Hamano
- */
-#include "cache.h"
-#include "blob.h"
-#include "tree.h"
-#include "commit.h"
-#include "tag.h"
-#include "tree-walk.h"
-#include "builtin.h"
-#include "parse-options.h"
-#include "userdiff.h"
-#include "grep.h"
-
-#ifndef NO_EXTERNAL_GREP
-#ifdef __unix__
-#define NO_EXTERNAL_GREP 0
-#else
-#define NO_EXTERNAL_GREP 1
-#endif
-#endif
-
-static char const * const grep_usage[] = {
-	"git grep [options] [-e] <pattern> [<rev>...] [[--] path...]",
-	NULL
-};
-
-static int grep_config(const char *var, const char *value, void *cb)
-{
-	struct grep_opt *opt = cb;
-
-	switch (userdiff_config(var, value)) {
-	case 0: break;
-	case -1: return -1;
-	default: return 0;
-	}
-
-	if (!strcmp(var, "color.grep")) {
-		opt->color = git_config_colorbool(var, value, -1);
-		return 0;
-	}
-	if (!strcmp(var, "color.grep.external"))
-		return git_config_string(&(opt->color_external), var, value);
-	if (!strcmp(var, "color.grep.match")) {
-		if (!value)
-			return config_error_nonbool(var);
-		color_parse(value, var, opt->color_match);
-		return 0;
-	}
-	return git_color_default_config(var, value, cb);
-}
-
-/*
- * git grep pathspecs are somewhat different from diff-tree pathspecs;
- * pathname wildcards are allowed.
- */
-static int pathspec_matches(const char **paths, const char *name)
-{
-	int namelen, i;
-	if (!paths || !*paths)
-		return 1;
-	namelen = strlen(name);
-	for (i = 0; paths[i]; i++) {
-		const char *match = paths[i];
-		int matchlen = strlen(match);
-		const char *cp, *meta;
-
-		if (!matchlen ||
-		    ((matchlen <= namelen) &&
-		     !strncmp(name, match, matchlen) &&
-		     (match[matchlen-1] == '/' ||
-		      name[matchlen] == '\0' || name[matchlen] == '/')))
-			return 1;
-		if (!fnmatch(match, name, 0))
-			return 1;
-		if (name[namelen-1] != '/')
-			continue;
-
-		/* We are being asked if the directory ("name") is worth
-		 * descending into.
-		 *
-		 * Find the longest leading directory name that does
-		 * not have metacharacter in the pathspec; the name
-		 * we are looking at must overlap with that directory.
-		 */
-		for (cp = match, meta = NULL; cp - match < matchlen; cp++) {
-			char ch = *cp;
-			if (ch == '*' || ch == '[' || ch == '?') {
-				meta = cp;
-				break;
-			}
-		}
-		if (!meta)
-			meta = cp; /* fully literal */
-
-		if (namelen <= meta - match) {
-			/* Looking at "Documentation/" and
-			 * the pattern says "Documentation/howto/", or
-			 * "Documentation/diff*.txt".  The name we
-			 * have should match prefix.
-			 */
-			if (!memcmp(match, name, namelen))
-				return 1;
-			continue;
-		}
-
-		if (meta - match < namelen) {
-			/* Looking at "Documentation/howto/" and
-			 * the pattern says "Documentation/h*";
-			 * match up to "Do.../h"; this avoids descending
-			 * into "Documentation/technical/".
-			 */
-			if (!memcmp(match, name, meta - match))
-				return 1;
-			continue;
-		}
-	}
-	return 0;
-}
-
-static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1, const char *name, int tree_name_len)
-{
-	unsigned long size;
-	char *data;
-	enum object_type type;
-	char *to_free = NULL;
-	int hit;
-
-	data = read_sha1_file(sha1, &type, &size);
-	if (!data) {
-		error("'%s': unable to read %s", name, sha1_to_hex(sha1));
-		return 0;
-	}
-	if (opt->relative && opt->prefix_length) {
-		static char name_buf[PATH_MAX];
-		char *cp;
-		int name_len = strlen(name) - opt->prefix_length + 1;
-
-		if (!tree_name_len)
-			name += opt->prefix_length;
-		else {
-			if (ARRAY_SIZE(name_buf) <= name_len)
-				cp = to_free = xmalloc(name_len);
-			else
-				cp = name_buf;
-			memcpy(cp, name, tree_name_len);
-			strcpy(cp + tree_name_len,
-			       name + tree_name_len + opt->prefix_length);
-			name = cp;
-		}
-	}
-	hit = grep_buffer(opt, name, data, size);
-	free(data);
-	free(to_free);
-	return hit;
-}
-
-static int grep_file(struct grep_opt *opt, const char *filename)
-{
-	struct stat st;
-	int i;
-	char *data;
-	size_t sz;
-
-	if (lstat(filename, &st) < 0) {
-	err_ret:
-		if (errno != ENOENT)
-			error("'%s': %s", filename, strerror(errno));
-		return 0;
-	}
-	if (!st.st_size)
-		return 0; /* empty file -- no grep hit */
-	if (!S_ISREG(st.st_mode))
-		return 0;
-	sz = xsize_t(st.st_size);
-	i = open(filename, O_RDONLY);
-	if (i < 0)
-		goto err_ret;
-	data = xmalloc(sz + 1);
-	if (st.st_size != read_in_full(i, data, sz)) {
-		error("'%s': short read %s", filename, strerror(errno));
-		close(i);
-		free(data);
-		return 0;
-	}
-	close(i);
-	if (opt->relative && opt->prefix_length)
-		filename += opt->prefix_length;
-	i = grep_buffer(opt, filename, data, sz);
-	free(data);
-	return i;
-}
-
-#if !NO_EXTERNAL_GREP
-static int exec_grep(int argc, const char **argv)
-{
-	pid_t pid;
-	int status;
-
-	argv[argc] = NULL;
-	pid = fork();
-	if (pid < 0)
-		return pid;
-	if (!pid) {
-		execvp("grep", (char **) argv);
-		exit(255);
-	}
-	while (waitpid(pid, &status, 0) < 0) {
-		if (errno == EINTR)
-			continue;
-		return -1;
-	}
-	if (WIFEXITED(status)) {
-		if (!WEXITSTATUS(status))
-			return 1;
-		return 0;
-	}
-	return -1;
-}
-
-#define MAXARGS 1000
-#define ARGBUF 4096
-#define push_arg(a) do { \
-	if (nr < MAXARGS) argv[nr++] = (a); \
-	else die("maximum number of args exceeded"); \
-	} while (0)
-
-/*
- * If you send a singleton filename to grep, it does not give
- * the name of the file.  GNU grep has "-H" but we would want
- * that behaviour in a portable way.
- *
- * So we keep two pathnames in argv buffer unsent to grep in
- * the main loop if we need to do more than one grep.
- */
-static int flush_grep(struct grep_opt *opt,
-		      int argc, int arg0, const char **argv, int *kept)
-{
-	int status;
-	int count = argc - arg0;
-	const char *kept_0 = NULL;
-
-	if (count <= 2) {
-		/*
-		 * Because we keep at least 2 paths in the call from
-		 * the main loop (i.e. kept != NULL), and MAXARGS is
-		 * far greater than 2, this usually is a call to
-		 * conclude the grep.  However, the user could attempt
-		 * to overflow the argv buffer by giving too many
-		 * options to leave very small number of real
-		 * arguments even for the call in the main loop.
-		 */
-		if (kept)
-			die("insanely many options to grep");
-
-		/*
-		 * If we have two or more paths, we do not have to do
-		 * anything special, but we need to push /dev/null to
-		 * get "-H" behaviour of GNU grep portably but when we
-		 * are not doing "-l" nor "-L" nor "-c".
-		 */
-		if (count == 1 &&
-		    !opt->name_only &&
-		    !opt->unmatch_name_only &&
-		    !opt->count) {
-			argv[argc++] = "/dev/null";
-			argv[argc] = NULL;
-		}
-	}
-
-	else if (kept) {
-		/*
-		 * Called because we found many paths and haven't finished
-		 * iterating over the cache yet.  We keep two paths
-		 * for the concluding call.  argv[argc-2] and argv[argc-1]
-		 * has the last two paths, so save the first one away,
-		 * replace it with NULL while sending the list to grep,
-		 * and recover them after we are done.
-		 */
-		*kept = 2;
-		kept_0 = argv[argc-2];
-		argv[argc-2] = NULL;
-		argc -= 2;
-	}
-
-	if (opt->pre_context || opt->post_context) {
-		/*
-		 * grep handles hunk marks between files, but we need to
-		 * do that ourselves between multiple calls.
-		 */
-		if (opt->show_hunk_mark)
-			write_or_die(1, "--\n", 3);
-		else
-			opt->show_hunk_mark = 1;
-	}
-
-	status = exec_grep(argc, argv);
-
-	if (kept_0) {
-		/*
-		 * Then recover them.  Now the last arg is beyond the
-		 * terminating NULL which is at argc, and the second
-		 * from the last is what we saved away in kept_0
-		 */
-		argv[arg0++] = kept_0;
-		argv[arg0] = argv[argc+1];
-	}
-	return status;
-}
-
-static void grep_add_color(struct strbuf *sb, const char *escape_seq)
-{
-	size_t orig_len = sb->len;
-
-	while (*escape_seq) {
-		if (*escape_seq == 'm')
-			strbuf_addch(sb, ';');
-		else if (*escape_seq != '\033' && *escape_seq  != '[')
-			strbuf_addch(sb, *escape_seq);
-		escape_seq++;
-	}
-	if (sb->len > orig_len && sb->buf[sb->len - 1] == ';')
-		strbuf_setlen(sb, sb->len - 1);
-}
-
-static int external_grep(struct grep_opt *opt, const char **paths, int cached)
-{
-	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 || (opt->relative && opt->prefix_length))
-		return -1;
-	len = nr = 0;
-	push_arg("grep");
-	if (opt->fixed)
-		push_arg("-F");
-	if (opt->linenum)
-		push_arg("-n");
-	if (!opt->pathname)
-		push_arg("-h");
-	if (opt->regflags & REG_EXTENDED)
-		push_arg("-E");
-	if (opt->regflags & REG_ICASE)
-		push_arg("-i");
-	if (opt->binary == GREP_BINARY_NOMATCH)
-		push_arg("-I");
-	if (opt->word_regexp)
-		push_arg("-w");
-	if (opt->name_only)
-		push_arg("-l");
-	if (opt->unmatch_name_only)
-		push_arg("-L");
-	if (opt->null_following_name)
-		/* in GNU grep git's "-z" translates to "-Z" */
-		push_arg("-Z");
-	if (opt->count)
-		push_arg("-c");
-	if (opt->post_context || opt->pre_context) {
-		if (opt->post_context != opt->pre_context) {
-			if (opt->pre_context) {
-				push_arg("-B");
-				len += snprintf(argptr, sizeof(randarg)-len,
-						"%u", opt->pre_context) + 1;
-				if (sizeof(randarg) <= len)
-					die("maximum length of args exceeded");
-				push_arg(argptr);
-				argptr += len;
-			}
-			if (opt->post_context) {
-				push_arg("-A");
-				len += snprintf(argptr, sizeof(randarg)-len,
-						"%u", opt->post_context) + 1;
-				if (sizeof(randarg) <= len)
-					die("maximum length of args exceeded");
-				push_arg(argptr);
-				argptr += len;
-			}
-		}
-		else {
-			push_arg("-C");
-			len += snprintf(argptr, sizeof(randarg)-len,
-					"%u", opt->post_context) + 1;
-			if (sizeof(randarg) <= len)
-				die("maximum length of args exceeded");
-			push_arg(argptr);
-			argptr += len;
-		}
-	}
-	for (p = opt->pattern_list; p; p = p->next) {
-		push_arg("-e");
-		push_arg(p->pattern);
-	}
-	if (opt->color) {
-		struct strbuf sb = STRBUF_INIT;
-
-		grep_add_color(&sb, opt->color_match);
-		setenv("GREP_COLOR", sb.buf, 1);
-
-		strbuf_reset(&sb);
-		strbuf_addstr(&sb, "mt=");
-		grep_add_color(&sb, opt->color_match);
-		strbuf_addstr(&sb, ":sl=:cx=:fn=:ln=:bn=:se=");
-		setenv("GREP_COLORS", sb.buf, 1);
-
-		strbuf_release(&sb);
-
-		if (opt->color_external && strlen(opt->color_external) > 0)
-			push_arg(opt->color_external);
-	}
-
-	hit = 0;
-	argc = nr;
-	for (i = 0; i < active_nr; i++) {
-		struct cache_entry *ce = active_cache[i];
-		char *name;
-		int kept;
-		if (!S_ISREG(ce->ce_mode))
-			continue;
-		if (!pathspec_matches(paths, ce->name))
-			continue;
-		name = ce->name;
-		if (name[0] == '-') {
-			int len = ce_namelen(ce);
-			name = xmalloc(len + 3);
-			memcpy(name, "./", 2);
-			memcpy(name + 2, ce->name, len + 1);
-		}
-		argv[argc++] = name;
-		if (MAXARGS <= argc) {
-			status = flush_grep(opt, argc, nr, argv, &kept);
-			if (0 < status)
-				hit = 1;
-			argc = nr + kept;
-		}
-		if (ce_stage(ce)) {
-			do {
-				i++;
-			} while (i < active_nr &&
-				 !strcmp(ce->name, active_cache[i]->name));
-			i--; /* compensate for loop control */
-		}
-	}
-	if (argc > nr) {
-		status = flush_grep(opt, argc, nr, argv, NULL);
-		if (0 < status)
-			hit = 1;
-	}
-	return hit;
-}
-#endif
-
-static int grep_cache(struct grep_opt *opt, const char **paths, int cached,
-		      int external_grep_allowed)
-{
-	int hit = 0;
-	int nr;
-	read_cache();
-
-#if !NO_EXTERNAL_GREP
-	/*
-	 * Use the external "grep" command for the case where
-	 * we grep through the checked-out files. It tends to
-	 * be a lot more optimized
-	 */
-	if (!cached && external_grep_allowed) {
-		hit = external_grep(opt, paths, cached);
-		if (hit >= 0)
-			return hit;
-	}
-#endif
-
-	for (nr = 0; nr < active_nr; nr++) {
-		struct cache_entry *ce = active_cache[nr];
-		if (!S_ISREG(ce->ce_mode))
-			continue;
-		if (!pathspec_matches(paths, ce->name))
-			continue;
-		/*
-		 * If CE_VALID is on, we assume worktree file and its cache entry
-		 * are identical, even if worktree file has been modified, so use
-		 * cache version instead
-		 */
-		if (cached || (ce->ce_flags & CE_VALID)) {
-			if (ce_stage(ce))
-				continue;
-			hit |= grep_sha1(opt, ce->sha1, ce->name, 0);
-		}
-		else
-			hit |= grep_file(opt, ce->name);
-		if (ce_stage(ce)) {
-			do {
-				nr++;
-			} while (nr < active_nr &&
-				 !strcmp(ce->name, active_cache[nr]->name));
-			nr--; /* compensate for loop control */
-		}
-	}
-	free_grep_patterns(opt);
-	return hit;
-}
-
-static int grep_tree(struct grep_opt *opt, const char **paths,
-		     struct tree_desc *tree,
-		     const char *tree_name, const char *base)
-{
-	int len;
-	int hit = 0;
-	struct name_entry entry;
-	char *down;
-	int tn_len = strlen(tree_name);
-	struct strbuf pathbuf;
-
-	strbuf_init(&pathbuf, PATH_MAX + tn_len);
-
-	if (tn_len) {
-		strbuf_add(&pathbuf, tree_name, tn_len);
-		strbuf_addch(&pathbuf, ':');
-		tn_len = pathbuf.len;
-	}
-	strbuf_addstr(&pathbuf, base);
-	len = pathbuf.len;
-
-	while (tree_entry(tree, &entry)) {
-		int te_len = tree_entry_len(entry.path, entry.sha1);
-		pathbuf.len = len;
-		strbuf_add(&pathbuf, entry.path, te_len);
-
-		if (S_ISDIR(entry.mode))
-			/* Match "abc/" against pathspec to
-			 * decide if we want to descend into "abc"
-			 * directory.
-			 */
-			strbuf_addch(&pathbuf, '/');
-
-		down = pathbuf.buf + tn_len;
-		if (!pathspec_matches(paths, down))
-			;
-		else if (S_ISREG(entry.mode))
-			hit |= grep_sha1(opt, entry.sha1, pathbuf.buf, tn_len);
-		else if (S_ISDIR(entry.mode)) {
-			enum object_type type;
-			struct tree_desc sub;
-			void *data;
-			unsigned long size;
-
-			data = read_sha1_file(entry.sha1, &type, &size);
-			if (!data)
-				die("unable to read tree (%s)",
-				    sha1_to_hex(entry.sha1));
-			init_tree_desc(&sub, data, size);
-			hit |= grep_tree(opt, paths, &sub, tree_name, down);
-			free(data);
-		}
-	}
-	strbuf_release(&pathbuf);
-	return hit;
-}
-
-static int grep_object(struct grep_opt *opt, const char **paths,
-		       struct object *obj, const char *name)
-{
-	if (obj->type == OBJ_BLOB)
-		return grep_sha1(opt, obj->sha1, name, 0);
-	if (obj->type == OBJ_COMMIT || obj->type == OBJ_TREE) {
-		struct tree_desc tree;
-		void *data;
-		unsigned long size;
-		int hit;
-		data = read_object_with_reference(obj->sha1, tree_type,
-						  &size, NULL);
-		if (!data)
-			die("unable to read tree (%s)", sha1_to_hex(obj->sha1));
-		init_tree_desc(&tree, data, size);
-		hit = grep_tree(opt, paths, &tree, name, "");
-		free(data);
-		return hit;
-	}
-	die("unable to grep from object of type %s", typename(obj->type));
-}
-
-static int context_callback(const struct option *opt, const char *arg,
-			    int unset)
-{
-	struct grep_opt *grep_opt = opt->value;
-	int value;
-	const char *endp;
-
-	if (unset) {
-		grep_opt->pre_context = grep_opt->post_context = 0;
-		return 0;
-	}
-	value = strtol(arg, (char **)&endp, 10);
-	if (*endp) {
-		return error("switch `%c' expects a numerical value",
-			     opt->short_name);
-	}
-	grep_opt->pre_context = grep_opt->post_context = value;
-	return 0;
-}
-
-static int file_callback(const struct option *opt, const char *arg, int unset)
-{
-	struct grep_opt *grep_opt = opt->value;
-	FILE *patterns;
-	int lno = 0;
-	struct strbuf sb;
-
-	patterns = fopen(arg, "r");
-	if (!patterns)
-		die_errno("cannot open '%s'", arg);
-	while (strbuf_getline(&sb, patterns, '\n') == 0) {
-		/* ignore empty line like grep does */
-		if (sb.len == 0)
-			continue;
-		append_grep_pattern(grep_opt, strbuf_detach(&sb, NULL), arg,
-				    ++lno, GREP_PATTERN);
-	}
-	fclose(patterns);
-	strbuf_release(&sb);
-	return 0;
-}
-
-static int not_callback(const struct option *opt, const char *arg, int unset)
-{
-	struct grep_opt *grep_opt = opt->value;
-	append_grep_pattern(grep_opt, "--not", "command line", 0, GREP_NOT);
-	return 0;
-}
-
-static int and_callback(const struct option *opt, const char *arg, int unset)
-{
-	struct grep_opt *grep_opt = opt->value;
-	append_grep_pattern(grep_opt, "--and", "command line", 0, GREP_AND);
-	return 0;
-}
-
-static int open_callback(const struct option *opt, const char *arg, int unset)
-{
-	struct grep_opt *grep_opt = opt->value;
-	append_grep_pattern(grep_opt, "(", "command line", 0, GREP_OPEN_PAREN);
-	return 0;
-}
-
-static int close_callback(const struct option *opt, const char *arg, int unset)
-{
-	struct grep_opt *grep_opt = opt->value;
-	append_grep_pattern(grep_opt, ")", "command line", 0, GREP_CLOSE_PAREN);
-	return 0;
-}
-
-static int pattern_callback(const struct option *opt, const char *arg,
-			    int unset)
-{
-	struct grep_opt *grep_opt = opt->value;
-	append_grep_pattern(grep_opt, arg, "-e option", 0, GREP_PATTERN);
-	return 0;
-}
-
-static int help_callback(const struct option *opt, const char *arg, int unset)
-{
-	return -1;
-}
-
-int cmd_grep(int argc, const char **argv, const char *prefix)
-{
-	int hit = 0;
-	int cached = 0;
-	int external_grep_allowed = 1;
-	int seen_dashdash = 0;
-	struct grep_opt opt;
-	struct object_array list = { 0, 0, NULL };
-	const char **paths = NULL;
-	int i;
-	int dummy;
-	struct option options[] = {
-		OPT_BOOLEAN(0, "cached", &cached,
-			"search in index instead of in the work tree"),
-		OPT_GROUP(""),
-		OPT_BOOLEAN('v', "invert-match", &opt.invert,
-			"show non-matching lines"),
-		OPT_BIT('i', "ignore-case", &opt.regflags,
-			"case insensitive matching", REG_ICASE),
-		OPT_BOOLEAN('w', "word-regexp", &opt.word_regexp,
-			"match patterns only at word boundaries"),
-		OPT_SET_INT('a', "text", &opt.binary,
-			"process binary files as text", GREP_BINARY_TEXT),
-		OPT_SET_INT('I', NULL, &opt.binary,
-			"don't match patterns in binary files",
-			GREP_BINARY_NOMATCH),
-		OPT_GROUP(""),
-		OPT_BIT('E', "extended-regexp", &opt.regflags,
-			"use extended POSIX regular expressions", REG_EXTENDED),
-		OPT_NEGBIT('G', "basic-regexp", &opt.regflags,
-			"use basic POSIX regular expressions (default)",
-			REG_EXTENDED),
-		OPT_BOOLEAN('F', "fixed-strings", &opt.fixed,
-			"interpret patterns as fixed strings"),
-		OPT_GROUP(""),
-		OPT_BOOLEAN('n', NULL, &opt.linenum, "show line numbers"),
-		OPT_NEGBIT('h', NULL, &opt.pathname, "don't show filenames", 1),
-		OPT_BIT('H', NULL, &opt.pathname, "show filenames", 1),
-		OPT_NEGBIT(0, "full-name", &opt.relative,
-			"show filenames relative to top directory", 1),
-		OPT_BOOLEAN('l', "files-with-matches", &opt.name_only,
-			"show only filenames instead of matching lines"),
-		OPT_BOOLEAN(0, "name-only", &opt.name_only,
-			"synonym for --files-with-matches"),
-		OPT_BOOLEAN('L', "files-without-match",
-			&opt.unmatch_name_only,
-			"show only the names of files without match"),
-		OPT_BOOLEAN('z', "null", &opt.null_following_name,
-			"print NUL after filenames"),
-		OPT_BOOLEAN('c', "count", &opt.count,
-			"show the number of matches instead of matching lines"),
-		OPT_SET_INT(0, "color", &opt.color, "highlight matches", 1),
-		OPT_GROUP(""),
-		OPT_CALLBACK('C', NULL, &opt, "n",
-			"show <n> context lines before and after matches",
-			context_callback),
-		OPT_INTEGER('B', NULL, &opt.pre_context,
-			"show <n> context lines before matches"),
-		OPT_INTEGER('A', NULL, &opt.post_context,
-			"show <n> context lines after matches"),
-		OPT_NUMBER_CALLBACK(&opt, "shortcut for -C NUM",
-			context_callback),
-		OPT_BOOLEAN('p', "show-function", &opt.funcname,
-			"show a line with the function name before matches"),
-		OPT_GROUP(""),
-		OPT_CALLBACK('f', NULL, &opt, "file",
-			"read patterns from file", file_callback),
-		{ OPTION_CALLBACK, 'e', NULL, &opt, "pattern",
-			"match <pattern>", PARSE_OPT_NONEG, pattern_callback },
-		{ OPTION_CALLBACK, 0, "and", &opt, NULL,
-		  "combine patterns specified with -e",
-		  PARSE_OPT_NOARG | PARSE_OPT_NONEG, and_callback },
-		OPT_BOOLEAN(0, "or", &dummy, ""),
-		{ OPTION_CALLBACK, 0, "not", &opt, NULL, "",
-		  PARSE_OPT_NOARG | PARSE_OPT_NONEG, not_callback },
-		{ OPTION_CALLBACK, '(', NULL, &opt, NULL, "",
-		  PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH,
-		  open_callback },
-		{ OPTION_CALLBACK, ')', NULL, &opt, NULL, "",
-		  PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH,
-		  close_callback },
-		OPT_BOOLEAN(0, "all-match", &opt.all_match,
-			"show only matches from files that match all patterns"),
-		OPT_GROUP(""),
-#if NO_EXTERNAL_GREP
-		OPT_BOOLEAN(0, "ext-grep", &external_grep_allowed,
-			"allow calling of grep(1) (ignored by this build)"),
-#else
-		OPT_BOOLEAN(0, "ext-grep", &external_grep_allowed,
-			"allow calling of grep(1) (default)"),
-#endif
-		{ OPTION_CALLBACK, 0, "help-all", &options, NULL, "show usage",
-		  PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, help_callback },
-		OPT_END()
-	};
-
-	memset(&opt, 0, sizeof(opt));
-	opt.prefix_length = (prefix && *prefix) ? strlen(prefix) : 0;
-	opt.relative = 1;
-	opt.pathname = 1;
-	opt.pattern_tail = &opt.pattern_list;
-	opt.regflags = REG_NEWLINE;
-
-	strcpy(opt.color_match, GIT_COLOR_RED GIT_COLOR_BOLD);
-	opt.color = -1;
-	git_config(grep_config, &opt);
-	if (opt.color == -1)
-		opt.color = git_use_color_default;
-
-	/*
-	 * If there is no -- then the paths must exist in the working
-	 * tree.  If there is no explicit pattern specified with -e or
-	 * -f, we take the first unrecognized non option to be the
-	 * 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
-	 * unrecognized non option is the beginning of the refs list
-	 * that continues up to the -- (if exists), and then paths.
-	 */
-	argc = parse_options(argc, argv, prefix, options, grep_usage,
-			     PARSE_OPT_KEEP_DASHDASH |
-			     PARSE_OPT_STOP_AT_NON_OPTION |
-			     PARSE_OPT_NO_INTERNAL_HELP);
-
-	/* First unrecognized non-option token */
-	if (argc > 0 && !opt.pattern_list) {
-		append_grep_pattern(&opt, argv[0], "command line", 0,
-				    GREP_PATTERN);
-		argv++;
-		argc--;
-	}
-
-	if ((opt.color && !opt.color_external) || opt.funcname)
-		external_grep_allowed = 0;
-	if (!opt.pattern_list)
-		die("no pattern given.");
-	if ((opt.regflags != REG_NEWLINE) && opt.fixed)
-		die("cannot mix --fixed-strings and regexp");
-	compile_grep_patterns(&opt);
-
-	/* Check revs and then paths */
-	for (i = 0; i < argc; i++) {
-		const char *arg = argv[i];
-		unsigned char sha1[20];
-		/* Is it a rev? */
-		if (!get_sha1(arg, sha1)) {
-			struct object *object = parse_object(sha1);
-			if (!object)
-				die("bad object %s", arg);
-			add_object_array(object, arg, &list);
-			continue;
-		}
-		if (!strcmp(arg, "--")) {
-			i++;
-			seen_dashdash = 1;
-		}
-		break;
-	}
-
-	/* The rest are paths */
-	if (!seen_dashdash) {
-		int j;
-		for (j = i; j < argc; j++)
-			verify_filename(prefix, argv[j]);
-	}
-
-	if (i < argc) {
-		paths = get_pathspec(prefix, argv + i);
-		if (opt.prefix_length && opt.relative) {
-			/* Make sure we do not get outside of paths */
-			for (i = 0; paths[i]; i++)
-				if (strncmp(prefix, paths[i], opt.prefix_length))
-					die("git grep: cannot generate relative filenames containing '..'");
-		}
-	}
-	else if (prefix) {
-		paths = xcalloc(2, sizeof(const char *));
-		paths[0] = prefix;
-		paths[1] = NULL;
-	}
-
-	if (!list.nr) {
-		if (!cached)
-			setup_work_tree();
-		return !grep_cache(&opt, paths, cached, external_grep_allowed);
-	}
-
-	if (cached)
-		die("both --cached and trees are given.");
-
-	for (i = 0; i < list.nr; i++) {
-		struct object *real_obj;
-		real_obj = deref_tag(list.objects[i].item, NULL, 0);
-		if (grep_object(&opt, paths, real_obj, list.objects[i].name))
-			hit = 1;
-	}
-	free_grep_patterns(&opt);
-	return !hit;
-}
diff --git a/builtin-help.c b/builtin-help.c
deleted file mode 100644
index e1eba77..0000000
--- a/builtin-help.c
+++ /dev/null
@@ -1,458 +0,0 @@
-/*
- * builtin-help.c
- *
- * Builtin help command
- */
-#include "cache.h"
-#include "builtin.h"
-#include "exec_cmd.h"
-#include "common-cmds.h"
-#include "parse-options.h"
-#include "run-command.h"
-#include "help.h"
-
-static struct man_viewer_list {
-	struct man_viewer_list *next;
-	char name[FLEX_ARRAY];
-} *man_viewer_list;
-
-static struct man_viewer_info_list {
-	struct man_viewer_info_list *next;
-	const char *info;
-	char name[FLEX_ARRAY];
-} *man_viewer_info_list;
-
-enum help_format {
-	HELP_FORMAT_MAN,
-	HELP_FORMAT_INFO,
-	HELP_FORMAT_WEB,
-};
-
-static int show_all = 0;
-static enum help_format help_format = HELP_FORMAT_MAN;
-static struct option builtin_help_options[] = {
-	OPT_BOOLEAN('a', "all", &show_all, "print all available commands"),
-	OPT_SET_INT('m', "man", &help_format, "show man page", HELP_FORMAT_MAN),
-	OPT_SET_INT('w', "web", &help_format, "show manual in web browser",
-			HELP_FORMAT_WEB),
-	OPT_SET_INT('i', "info", &help_format, "show info page",
-			HELP_FORMAT_INFO),
-	OPT_END(),
-};
-
-static const char * const builtin_help_usage[] = {
-	"git help [--all] [--man|--web|--info] [command]",
-	NULL
-};
-
-static enum help_format parse_help_format(const char *format)
-{
-	if (!strcmp(format, "man"))
-		return HELP_FORMAT_MAN;
-	if (!strcmp(format, "info"))
-		return HELP_FORMAT_INFO;
-	if (!strcmp(format, "web") || !strcmp(format, "html"))
-		return HELP_FORMAT_WEB;
-	die("unrecognized help format '%s'", format);
-}
-
-static const char *get_man_viewer_info(const char *name)
-{
-	struct man_viewer_info_list *viewer;
-
-	for (viewer = man_viewer_info_list; viewer; viewer = viewer->next)
-	{
-		if (!strcasecmp(name, viewer->name))
-			return viewer->info;
-	}
-	return NULL;
-}
-
-static int check_emacsclient_version(void)
-{
-	struct strbuf buffer = STRBUF_INIT;
-	struct child_process ec_process;
-	const char *argv_ec[] = { "emacsclient", "--version", NULL };
-	int version;
-
-	/* emacsclient prints its version number on stderr */
-	memset(&ec_process, 0, sizeof(ec_process));
-	ec_process.argv = argv_ec;
-	ec_process.err = -1;
-	ec_process.stdout_to_stderr = 1;
-	if (start_command(&ec_process))
-		return error("Failed to start emacsclient.");
-
-	strbuf_read(&buffer, ec_process.err, 20);
-	close(ec_process.err);
-
-	/*
-	 * Don't bother checking return value, because "emacsclient --version"
-	 * seems to always exits with code 1.
-	 */
-	finish_command(&ec_process);
-
-	if (prefixcmp(buffer.buf, "emacsclient")) {
-		strbuf_release(&buffer);
-		return error("Failed to parse emacsclient version.");
-	}
-
-	strbuf_remove(&buffer, 0, strlen("emacsclient"));
-	version = atoi(buffer.buf);
-
-	if (version < 22) {
-		strbuf_release(&buffer);
-		return error("emacsclient version '%d' too old (< 22).",
-			version);
-	}
-
-	strbuf_release(&buffer);
-	return 0;
-}
-
-static void exec_woman_emacs(const char *path, const char *page)
-{
-	if (!check_emacsclient_version()) {
-		/* This works only with emacsclient version >= 22. */
-		struct strbuf man_page = STRBUF_INIT;
-
-		if (!path)
-			path = "emacsclient";
-		strbuf_addf(&man_page, "(woman \"%s\")", page);
-		execlp(path, "emacsclient", "-e", man_page.buf, NULL);
-		warning("failed to exec '%s': %s", path, strerror(errno));
-	}
-}
-
-static void exec_man_konqueror(const char *path, const char *page)
-{
-	const char *display = getenv("DISPLAY");
-	if (display && *display) {
-		struct strbuf man_page = STRBUF_INIT;
-		const char *filename = "kfmclient";
-
-		/* It's simpler to launch konqueror using kfmclient. */
-		if (path) {
-			const char *file = strrchr(path, '/');
-			if (file && !strcmp(file + 1, "konqueror")) {
-				char *new = xstrdup(path);
-				char *dest = strrchr(new, '/');
-
-				/* strlen("konqueror") == strlen("kfmclient") */
-				strcpy(dest + 1, "kfmclient");
-				path = new;
-			}
-			if (file)
-				filename = file;
-		} else
-			path = "kfmclient";
-		strbuf_addf(&man_page, "man:%s(1)", page);
-		execlp(path, filename, "newTab", man_page.buf, NULL);
-		warning("failed to exec '%s': %s", path, strerror(errno));
-	}
-}
-
-static void exec_man_man(const char *path, const char *page)
-{
-	if (!path)
-		path = "man";
-	execlp(path, "man", page, NULL);
-	warning("failed to exec '%s': %s", path, strerror(errno));
-}
-
-static void exec_man_cmd(const char *cmd, const char *page)
-{
-	struct strbuf shell_cmd = STRBUF_INIT;
-	strbuf_addf(&shell_cmd, "%s %s", cmd, page);
-	execl("/bin/sh", "sh", "-c", shell_cmd.buf, NULL);
-	warning("failed to exec '%s': %s", cmd, strerror(errno));
-}
-
-static void add_man_viewer(const char *name)
-{
-	struct man_viewer_list **p = &man_viewer_list;
-	size_t len = strlen(name);
-
-	while (*p)
-		p = &((*p)->next);
-	*p = xcalloc(1, (sizeof(**p) + len + 1));
-	strncpy((*p)->name, name, len);
-}
-
-static int supported_man_viewer(const char *name, size_t len)
-{
-	return (!strncasecmp("man", name, len) ||
-		!strncasecmp("woman", name, len) ||
-		!strncasecmp("konqueror", name, len));
-}
-
-static void do_add_man_viewer_info(const char *name,
-				   size_t len,
-				   const char *value)
-{
-	struct man_viewer_info_list *new = xcalloc(1, sizeof(*new) + len + 1);
-
-	strncpy(new->name, name, len);
-	new->info = xstrdup(value);
-	new->next = man_viewer_info_list;
-	man_viewer_info_list = new;
-}
-
-static int add_man_viewer_path(const char *name,
-			       size_t len,
-			       const char *value)
-{
-	if (supported_man_viewer(name, len))
-		do_add_man_viewer_info(name, len, value);
-	else
-		warning("'%s': path for unsupported man viewer.\n"
-			"Please consider using 'man.<tool>.cmd' instead.",
-			name);
-
-	return 0;
-}
-
-static int add_man_viewer_cmd(const char *name,
-			      size_t len,
-			      const char *value)
-{
-	if (supported_man_viewer(name, len))
-		warning("'%s': cmd for supported man viewer.\n"
-			"Please consider using 'man.<tool>.path' instead.",
-			name);
-	else
-		do_add_man_viewer_info(name, len, value);
-
-	return 0;
-}
-
-static int add_man_viewer_info(const char *var, const char *value)
-{
-	const char *name = var + 4;
-	const char *subkey = strrchr(name, '.');
-
-	if (!subkey)
-		return 0;
-
-	if (!strcmp(subkey, ".path")) {
-		if (!value)
-			return config_error_nonbool(var);
-		return add_man_viewer_path(name, subkey - name, value);
-	}
-	if (!strcmp(subkey, ".cmd")) {
-		if (!value)
-			return config_error_nonbool(var);
-		return add_man_viewer_cmd(name, subkey - name, value);
-	}
-
-	return 0;
-}
-
-static int git_help_config(const char *var, const char *value, void *cb)
-{
-	if (!strcmp(var, "help.format")) {
-		if (!value)
-			return config_error_nonbool(var);
-		help_format = parse_help_format(value);
-		return 0;
-	}
-	if (!strcmp(var, "man.viewer")) {
-		if (!value)
-			return config_error_nonbool(var);
-		add_man_viewer(value);
-		return 0;
-	}
-	if (!prefixcmp(var, "man."))
-		return add_man_viewer_info(var, value);
-
-	return git_default_config(var, value, cb);
-}
-
-static struct cmdnames main_cmds, other_cmds;
-
-void list_common_cmds_help(void)
-{
-	int i, longest = 0;
-
-	for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
-		if (longest < strlen(common_cmds[i].name))
-			longest = strlen(common_cmds[i].name);
-	}
-
-	puts("The most commonly used git commands are:");
-	for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
-		printf("   %s   ", common_cmds[i].name);
-		mput_char(' ', longest - strlen(common_cmds[i].name));
-		puts(common_cmds[i].help);
-	}
-}
-
-static int is_git_command(const char *s)
-{
-	return is_in_cmdlist(&main_cmds, s) ||
-		is_in_cmdlist(&other_cmds, s);
-}
-
-static const char *prepend(const char *prefix, const char *cmd)
-{
-	size_t pre_len = strlen(prefix);
-	size_t cmd_len = strlen(cmd);
-	char *p = xmalloc(pre_len + cmd_len + 1);
-	memcpy(p, prefix, pre_len);
-	strcpy(p + pre_len, cmd);
-	return p;
-}
-
-static const char *cmd_to_page(const char *git_cmd)
-{
-	if (!git_cmd)
-		return "git";
-	else if (!prefixcmp(git_cmd, "git"))
-		return git_cmd;
-	else if (is_git_command(git_cmd))
-		return prepend("git-", git_cmd);
-	else
-		return prepend("git", git_cmd);
-}
-
-static void setup_man_path(void)
-{
-	struct strbuf new_path = STRBUF_INIT;
-	const char *old_path = getenv("MANPATH");
-
-	/* We should always put ':' after our path. If there is no
-	 * old_path, the ':' at the end will let 'man' to try
-	 * system-wide paths after ours to find the manual page. If
-	 * there is old_path, we need ':' as delimiter. */
-	strbuf_addstr(&new_path, system_path(GIT_MAN_PATH));
-	strbuf_addch(&new_path, ':');
-	if (old_path)
-		strbuf_addstr(&new_path, old_path);
-
-	setenv("MANPATH", new_path.buf, 1);
-
-	strbuf_release(&new_path);
-}
-
-static void exec_viewer(const char *name, const char *page)
-{
-	const char *info = get_man_viewer_info(name);
-
-	if (!strcasecmp(name, "man"))
-		exec_man_man(info, page);
-	else if (!strcasecmp(name, "woman"))
-		exec_woman_emacs(info, page);
-	else if (!strcasecmp(name, "konqueror"))
-		exec_man_konqueror(info, page);
-	else if (info)
-		exec_man_cmd(info, page);
-	else
-		warning("'%s': unknown man viewer.", name);
-}
-
-static void show_man_page(const char *git_cmd)
-{
-	struct man_viewer_list *viewer;
-	const char *page = cmd_to_page(git_cmd);
-	const char *fallback = getenv("GIT_MAN_VIEWER");
-
-	setup_man_path();
-	for (viewer = man_viewer_list; viewer; viewer = viewer->next)
-	{
-		exec_viewer(viewer->name, page); /* will return when unable */
-	}
-	if (fallback)
-		exec_viewer(fallback, page);
-	exec_viewer("man", page);
-	die("no man viewer handled the request");
-}
-
-static void show_info_page(const char *git_cmd)
-{
-	const char *page = cmd_to_page(git_cmd);
-	setenv("INFOPATH", system_path(GIT_INFO_PATH), 1);
-	execlp("info", "info", "gitman", page, NULL);
-}
-
-static void get_html_page_path(struct strbuf *page_path, const char *page)
-{
-	struct stat st;
-	const char *html_path = system_path(GIT_HTML_PATH);
-
-	/* Check that we have a git documentation directory. */
-	if (stat(mkpath("%s/git.html", html_path), &st)
-	    || !S_ISREG(st.st_mode))
-		die("'%s': not a documentation directory.", html_path);
-
-	strbuf_init(page_path, 0);
-	strbuf_addf(page_path, "%s/%s.html", html_path, page);
-}
-
-/*
- * If open_html is not defined in a platform-specific way (see for
- * example compat/mingw.h), we use the script web--browse to display
- * HTML.
- */
-#ifndef open_html
-static void open_html(const char *path)
-{
-	execl_git_cmd("web--browse", "-c", "help.browser", path, NULL);
-}
-#endif
-
-static void show_html_page(const char *git_cmd)
-{
-	const char *page = cmd_to_page(git_cmd);
-	struct strbuf page_path; /* it leaks but we exec bellow */
-
-	get_html_page_path(&page_path, page);
-
-	open_html(page_path.buf);
-}
-
-int cmd_help(int argc, const char **argv, const char *prefix)
-{
-	int nongit;
-	const char *alias;
-	load_command_list("git-", &main_cmds, &other_cmds);
-
-	setup_git_directory_gently(&nongit);
-	git_config(git_help_config, NULL);
-
-	argc = parse_options(argc, argv, prefix, builtin_help_options,
-			builtin_help_usage, 0);
-
-	if (show_all) {
-		printf("usage: %s\n\n", git_usage_string);
-		list_commands("git commands", &main_cmds, &other_cmds);
-		printf("%s\n", git_more_info_string);
-		return 0;
-	}
-
-	if (!argv[0]) {
-		printf("usage: %s\n\n", git_usage_string);
-		list_common_cmds_help();
-		printf("\n%s\n", git_more_info_string);
-		return 0;
-	}
-
-	alias = alias_lookup(argv[0]);
-	if (alias && !is_git_command(argv[0])) {
-		printf("`git %s' is aliased to `%s'\n", argv[0], alias);
-		return 0;
-	}
-
-	switch (help_format) {
-	case HELP_FORMAT_MAN:
-		show_man_page(argv[0]);
-		break;
-	case HELP_FORMAT_INFO:
-		show_info_page(argv[0]);
-		break;
-	case HELP_FORMAT_WEB:
-		show_html_page(argv[0]);
-		break;
-	}
-
-	return 0;
-}
diff --git a/builtin-http-fetch.c b/builtin-http-fetch.c
deleted file mode 100644
index f3e63d7..0000000
--- a/builtin-http-fetch.c
+++ /dev/null
@@ -1,86 +0,0 @@
-#include "cache.h"
-#include "walker.h"
-
-int cmd_http_fetch(int argc, const char **argv, const char *prefix)
-{
-	struct walker *walker;
-	int commits_on_stdin = 0;
-	int commits;
-	const char **write_ref = NULL;
-	char **commit_id;
-	const char *url;
-	char *rewritten_url = NULL;
-	int arg = 1;
-	int rc = 0;
-	int get_tree = 0;
-	int get_history = 0;
-	int get_all = 0;
-	int get_verbosely = 0;
-	int get_recover = 0;
-
-	git_config(git_default_config, NULL);
-
-	while (arg < argc && argv[arg][0] == '-') {
-		if (argv[arg][1] == 't') {
-			get_tree = 1;
-		} else if (argv[arg][1] == 'c') {
-			get_history = 1;
-		} else if (argv[arg][1] == 'a') {
-			get_all = 1;
-			get_tree = 1;
-			get_history = 1;
-		} else if (argv[arg][1] == 'v') {
-			get_verbosely = 1;
-		} else if (argv[arg][1] == 'w') {
-			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 - commits_on_stdin) {
-		usage("git http-fetch [-c] [-t] [-a] [-v] [--recover] [-w ref] [--stdin] commit-id url");
-		return 1;
-	}
-	if (commits_on_stdin) {
-		commits = walker_targets_stdin(&commit_id, &write_ref);
-	} else {
-		commit_id = (char **) &argv[arg++];
-		commits = 1;
-	}
-	url = argv[arg];
-	if (url && url[strlen(url)-1] != '/') {
-		rewritten_url = xmalloc(strlen(url)+2);
-		strcpy(rewritten_url, url);
-		strcat(rewritten_url, "/");
-		url = rewritten_url;
-	}
-
-	walker = get_http_walker(url, NULL);
-	walker->get_tree = get_tree;
-	walker->get_history = get_history;
-	walker->get_all = get_all;
-	walker->get_verbosely = get_verbosely;
-	walker->get_recover = get_recover;
-
-	rc = walker_fetch(walker, commits, commit_id, write_ref, url);
-
-	if (commits_on_stdin)
-		walker_targets_free(commits, commit_id, write_ref);
-
-	if (walker->corrupt_object_found) {
-		fprintf(stderr,
-"Some loose object were found to be corrupt, but they might be just\n"
-"a false '404 Not Found' error message sent with incorrect HTTP\n"
-"status code.  Suggest running 'git fsck'.\n");
-	}
-
-	walker_free(walker);
-
-	free(rewritten_url);
-
-	return rc;
-}
diff --git a/builtin-init-db.c b/builtin-init-db.c
deleted file mode 100644
index dd84cae..0000000
--- a/builtin-init-db.c
+++ /dev/null
@@ -1,498 +0,0 @@
-/*
- * GIT - The information manager from hell
- *
- * Copyright (C) Linus Torvalds, 2005
- */
-#include "cache.h"
-#include "builtin.h"
-#include "exec_cmd.h"
-#include "parse-options.h"
-
-#ifndef DEFAULT_GIT_TEMPLATE_DIR
-#define DEFAULT_GIT_TEMPLATE_DIR "/usr/share/git-core/templates"
-#endif
-
-#ifdef NO_TRUSTABLE_FILEMODE
-#define TEST_FILEMODE 0
-#else
-#define TEST_FILEMODE 1
-#endif
-
-static int init_is_bare_repository = 0;
-static int init_shared_repository = -1;
-
-static void safe_create_dir(const char *dir, int share)
-{
-	if (mkdir(dir, 0777) < 0) {
-		if (errno != EEXIST) {
-			perror(dir);
-			exit(1);
-		}
-	}
-	else if (share && adjust_shared_perm(dir))
-		die("Could not make %s writable by group", dir);
-}
-
-static void copy_templates_1(char *path, int baselen,
-			     char *template, int template_baselen,
-			     DIR *dir)
-{
-	struct dirent *de;
-
-	/* Note: if ".git/hooks" file exists in the repository being
-	 * re-initialized, /etc/core-git/templates/hooks/update would
-	 * cause "git init" to fail here.  I think this is sane but
-	 * it means that the set of templates we ship by default, along
-	 * with the way the namespace under .git/ is organized, should
-	 * be really carefully chosen.
-	 */
-	safe_create_dir(path, 1);
-	while ((de = readdir(dir)) != NULL) {
-		struct stat st_git, st_template;
-		int namelen;
-		int exists = 0;
-
-		if (de->d_name[0] == '.')
-			continue;
-		namelen = strlen(de->d_name);
-		if ((PATH_MAX <= baselen + namelen) ||
-		    (PATH_MAX <= template_baselen + namelen))
-			die("insanely long template name %s", de->d_name);
-		memcpy(path + baselen, de->d_name, namelen+1);
-		memcpy(template + template_baselen, de->d_name, namelen+1);
-		if (lstat(path, &st_git)) {
-			if (errno != ENOENT)
-				die_errno("cannot stat '%s'", path);
-		}
-		else
-			exists = 1;
-
-		if (lstat(template, &st_template))
-			die_errno("cannot stat template '%s'", template);
-
-		if (S_ISDIR(st_template.st_mode)) {
-			DIR *subdir = opendir(template);
-			int baselen_sub = baselen + namelen;
-			int template_baselen_sub = template_baselen + namelen;
-			if (!subdir)
-				die_errno("cannot opendir '%s'", template);
-			path[baselen_sub++] =
-				template[template_baselen_sub++] = '/';
-			path[baselen_sub] =
-				template[template_baselen_sub] = 0;
-			copy_templates_1(path, baselen_sub,
-					 template, template_baselen_sub,
-					 subdir);
-			closedir(subdir);
-		}
-		else if (exists)
-			continue;
-		else if (S_ISLNK(st_template.st_mode)) {
-			char lnk[256];
-			int len;
-			len = readlink(template, lnk, sizeof(lnk));
-			if (len < 0)
-				die_errno("cannot readlink '%s'", template);
-			if (sizeof(lnk) <= len)
-				die("insanely long symlink %s", template);
-			lnk[len] = 0;
-			if (symlink(lnk, path))
-				die_errno("cannot symlink '%s' '%s'", lnk, path);
-		}
-		else if (S_ISREG(st_template.st_mode)) {
-			if (copy_file(path, template, st_template.st_mode))
-				die_errno("cannot copy '%s' to '%s'", template,
-					  path);
-		}
-		else
-			error("ignoring template %s", template);
-	}
-}
-
-static void copy_templates(const char *template_dir)
-{
-	char path[PATH_MAX];
-	char template_path[PATH_MAX];
-	int template_len;
-	DIR *dir;
-	const char *git_dir = get_git_dir();
-	int len = strlen(git_dir);
-
-	if (!template_dir)
-		template_dir = getenv(TEMPLATE_DIR_ENVIRONMENT);
-	if (!template_dir)
-		template_dir = system_path(DEFAULT_GIT_TEMPLATE_DIR);
-	if (!template_dir[0])
-		return;
-	template_len = strlen(template_dir);
-	if (PATH_MAX <= (template_len+strlen("/config")))
-		die("insanely long template path %s", template_dir);
-	strcpy(template_path, template_dir);
-	if (template_path[template_len-1] != '/') {
-		template_path[template_len++] = '/';
-		template_path[template_len] = 0;
-	}
-	dir = opendir(template_path);
-	if (!dir) {
-		warning("templates not found %s", template_dir);
-		return;
-	}
-
-	/* Make sure that template is from the correct vintage */
-	strcpy(template_path + template_len, "config");
-	repository_format_version = 0;
-	git_config_from_file(check_repository_format_version,
-			     template_path, NULL);
-	template_path[template_len] = 0;
-
-	if (repository_format_version &&
-	    repository_format_version != GIT_REPO_VERSION) {
-		warning("not copying templates of "
-			"a wrong format version %d from '%s'",
-			repository_format_version,
-			template_dir);
-		closedir(dir);
-		return;
-	}
-
-	memcpy(path, git_dir, len);
-	if (len && path[len - 1] != '/')
-		path[len++] = '/';
-	path[len] = 0;
-	copy_templates_1(path, len,
-			 template_path, template_len,
-			 dir);
-	closedir(dir);
-}
-
-static int create_default_files(const char *template_path)
-{
-	const char *git_dir = get_git_dir();
-	unsigned len = strlen(git_dir);
-	static char path[PATH_MAX];
-	struct stat st1;
-	char repo_version_string[10];
-	char junk[2];
-	int reinit;
-	int filemode;
-
-	if (len > sizeof(path)-50)
-		die("insane git directory %s", git_dir);
-	memcpy(path, git_dir, len);
-
-	if (len && path[len-1] != '/')
-		path[len++] = '/';
-
-	/*
-	 * Create .git/refs/{heads,tags}
-	 */
-	safe_create_dir(git_path("refs"), 1);
-	safe_create_dir(git_path("refs/heads"), 1);
-	safe_create_dir(git_path("refs/tags"), 1);
-
-	/* First copy the templates -- we might have the default
-	 * config file there, in which case we would want to read
-	 * from it after installing.
-	 */
-	copy_templates(template_path);
-
-	git_config(git_default_config, NULL);
-	is_bare_repository_cfg = init_is_bare_repository;
-
-	/* reading existing config may have overwrote it */
-	if (init_shared_repository != -1)
-		shared_repository = init_shared_repository;
-
-	/*
-	 * We would have created the above under user's umask -- under
-	 * shared-repository settings, we would need to fix them up.
-	 */
-	if (shared_repository) {
-		adjust_shared_perm(get_git_dir());
-		adjust_shared_perm(git_path("refs"));
-		adjust_shared_perm(git_path("refs/heads"));
-		adjust_shared_perm(git_path("refs/tags"));
-	}
-
-	/*
-	 * Create the default symlink from ".git/HEAD" to the "master"
-	 * branch, if it does not exist yet.
-	 */
-	strcpy(path + len, "HEAD");
-	reinit = (!access(path, R_OK)
-		  || readlink(path, junk, sizeof(junk)-1) != -1);
-	if (!reinit) {
-		if (create_symref("HEAD", "refs/heads/master", NULL) < 0)
-			exit(1);
-	}
-
-	/* This forces creation of new config file */
-	sprintf(repo_version_string, "%d", GIT_REPO_VERSION);
-	git_config_set("core.repositoryformatversion", repo_version_string);
-
-	path[len] = 0;
-	strcpy(path + len, "config");
-
-	/* Check filemode trustability */
-	filemode = TEST_FILEMODE;
-	if (TEST_FILEMODE && !lstat(path, &st1)) {
-		struct stat st2;
-		filemode = (!chmod(path, st1.st_mode ^ S_IXUSR) &&
-				!lstat(path, &st2) &&
-				st1.st_mode != st2.st_mode);
-	}
-	git_config_set("core.filemode", filemode ? "true" : "false");
-
-	if (is_bare_repository())
-		git_config_set("core.bare", "true");
-	else {
-		const char *work_tree = get_git_work_tree();
-		git_config_set("core.bare", "false");
-		/* allow template config file to override the default */
-		if (log_all_ref_updates == -1)
-		    git_config_set("core.logallrefupdates", "true");
-		if (prefixcmp(git_dir, work_tree) ||
-		    strcmp(git_dir + strlen(work_tree), "/.git")) {
-			git_config_set("core.worktree", work_tree);
-		}
-	}
-
-	if (!reinit) {
-		/* Check if symlink is supported in the work tree */
-		path[len] = 0;
-		strcpy(path + len, "tXXXXXX");
-		if (!close(xmkstemp(path)) &&
-		    !unlink(path) &&
-		    !symlink("testing", path) &&
-		    !lstat(path, &st1) &&
-		    S_ISLNK(st1.st_mode))
-			unlink(path); /* good */
-		else
-			git_config_set("core.symlinks", "false");
-
-		/* Check if the filesystem is case-insensitive */
-		path[len] = 0;
-		strcpy(path + len, "CoNfIg");
-		if (!access(path, F_OK))
-			git_config_set("core.ignorecase", "true");
-	}
-
-	return reinit;
-}
-
-int init_db(const char *template_dir, unsigned int flags)
-{
-	const char *sha1_dir;
-	char *path;
-	int len, reinit;
-
-	safe_create_dir(get_git_dir(), 0);
-
-	init_is_bare_repository = is_bare_repository();
-
-	/* Check to see if the repository version is right.
-	 * Note that a newly created repository does not have
-	 * config file, so this will not fail.  What we are catching
-	 * is an attempt to reinitialize new repository with an old tool.
-	 */
-	check_repository_format();
-
-	reinit = create_default_files(template_dir);
-
-	sha1_dir = get_object_directory();
-	len = strlen(sha1_dir);
-	path = xmalloc(len + 40);
-	memcpy(path, sha1_dir, len);
-
-	safe_create_dir(sha1_dir, 1);
-	strcpy(path+len, "/pack");
-	safe_create_dir(path, 1);
-	strcpy(path+len, "/info");
-	safe_create_dir(path, 1);
-
-	if (shared_repository) {
-		char buf[10];
-		/* We do not spell "group" and such, so that
-		 * the configuration can be read by older version
-		 * of git. Note, we use octal numbers for new share modes,
-		 * and compatibility values for PERM_GROUP and
-		 * PERM_EVERYBODY.
-		 */
-		if (shared_repository < 0)
-			/* force to the mode value */
-			sprintf(buf, "0%o", -shared_repository);
-		else if (shared_repository == PERM_GROUP)
-			sprintf(buf, "%d", OLD_PERM_GROUP);
-		else if (shared_repository == PERM_EVERYBODY)
-			sprintf(buf, "%d", OLD_PERM_EVERYBODY);
-		else
-			die("oops");
-		git_config_set("core.sharedrepository", buf);
-		git_config_set("receive.denyNonFastforwards", "true");
-	}
-
-	if (!(flags & INIT_DB_QUIET))
-		printf("%s%s Git repository in %s/\n",
-		       reinit ? "Reinitialized existing" : "Initialized empty",
-		       shared_repository ? " shared" : "",
-		       get_git_dir());
-
-	return 0;
-}
-
-static int guess_repository_type(const char *git_dir)
-{
-	char cwd[PATH_MAX];
-	const char *slash;
-
-	/*
-	 * "GIT_DIR=. git init" is always bare.
-	 * "GIT_DIR=`pwd` git init" too.
-	 */
-	if (!strcmp(".", git_dir))
-		return 1;
-	if (!getcwd(cwd, sizeof(cwd)))
-		die_errno("cannot tell cwd");
-	if (!strcmp(git_dir, cwd))
-		return 1;
-	/*
-	 * "GIT_DIR=.git or GIT_DIR=something/.git is usually not.
-	 */
-	if (!strcmp(git_dir, ".git"))
-		return 0;
-	slash = strrchr(git_dir, '/');
-	if (slash && !strcmp(slash, "/.git"))
-		return 0;
-
-	/*
-	 * Otherwise it is often bare.  At this point
-	 * we are just guessing.
-	 */
-	return 1;
-}
-
-static int shared_callback(const struct option *opt, const char *arg, int unset)
-{
-	*((int *) opt->value) = (arg) ? git_config_perm("arg", arg) : PERM_GROUP;
-	return 0;
-}
-
-static const char *const init_db_usage[] = {
-	"git init [-q | --quiet] [--bare] [--template=<template-directory>] [--shared[=<permissions>]] [directory]",
-	NULL
-};
-
-/*
- * If you want to, you can share the DB area with any number of branches.
- * That has advantages: you can save space by sharing all the SHA1 objects.
- * 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, const char *prefix)
-{
-	const char *git_dir;
-	const char *template_dir = NULL;
-	unsigned int flags = 0;
-	const struct option init_db_options[] = {
-		OPT_STRING(0, "template", &template_dir, "template-directory",
-				"provide the directory from which templates will be used"),
-		OPT_SET_INT(0, "bare", &is_bare_repository_cfg,
-				"create a bare repository", 1),
-		{ OPTION_CALLBACK, 0, "shared", &init_shared_repository,
-			"permissions",
-			"specify that the git repository is to be shared amongst several users",
-			PARSE_OPT_OPTARG | PARSE_OPT_NONEG, shared_callback, 0},
-		OPT_BIT('q', "quiet", &flags, "be quiet", INIT_DB_QUIET),
-		OPT_END()
-	};
-
-	argc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0);
-
-	if (argc == 1) {
-		int mkdir_tried = 0;
-	retry:
-		if (chdir(argv[0]) < 0) {
-			if (!mkdir_tried) {
-				int saved;
-				/*
-				 * At this point we haven't read any configuration,
-				 * and we know shared_repository should always be 0;
-				 * but just in case we play safe.
-				 */
-				saved = shared_repository;
-				shared_repository = 0;
-				switch (safe_create_leading_directories_const(argv[0])) {
-				case -3:
-					errno = EEXIST;
-					/* fallthru */
-				case -1:
-					die_errno("cannot mkdir %s", argv[0]);
-					break;
-				default:
-					break;
-				}
-				shared_repository = saved;
-				if (mkdir(argv[0], 0777) < 0)
-					die_errno("cannot mkdir %s", argv[0]);
-				mkdir_tried = 1;
-				goto retry;
-			}
-			die_errno("cannot chdir to %s", argv[0]);
-		}
-	} else if (0 < argc) {
-		usage(init_db_usage[0]);
-	}
-	if (is_bare_repository_cfg == 1) {
-		static char git_dir[PATH_MAX+1];
-
-		setenv(GIT_DIR_ENVIRONMENT,
-			getcwd(git_dir, sizeof(git_dir)), 0);
-	}
-
-	if (init_shared_repository != -1)
-		shared_repository = init_shared_repository;
-
-	/*
-	 * GIT_WORK_TREE makes sense only in conjunction with GIT_DIR
-	 * without --bare.  Catch the error early.
-	 */
-	git_dir = getenv(GIT_DIR_ENVIRONMENT);
-	if ((!git_dir || is_bare_repository_cfg == 1)
-	    && getenv(GIT_WORK_TREE_ENVIRONMENT))
-		die("%s (or --work-tree=<directory>) not allowed without "
-		    "specifying %s (or --git-dir=<directory>)",
-		    GIT_WORK_TREE_ENVIRONMENT,
-		    GIT_DIR_ENVIRONMENT);
-
-	/*
-	 * Set up the default .git directory contents
-	 */
-	if (!git_dir)
-		git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
-
-	if (is_bare_repository_cfg < 0)
-		is_bare_repository_cfg = guess_repository_type(git_dir);
-
-	if (!is_bare_repository_cfg) {
-		if (git_dir) {
-			const char *git_dir_parent = strrchr(git_dir, '/');
-			if (git_dir_parent) {
-				char *rel = xstrndup(git_dir, git_dir_parent - git_dir);
-				git_work_tree_cfg = xstrdup(make_absolute_path(rel));
-				free(rel);
-			}
-		}
-		if (!git_work_tree_cfg) {
-			git_work_tree_cfg = xcalloc(PATH_MAX, 1);
-			if (!getcwd(git_work_tree_cfg, PATH_MAX))
-				die_errno ("Cannot access current working directory");
-		}
-		if (access(get_git_work_tree(), X_OK))
-			die_errno ("Cannot access work tree '%s'",
-				   get_git_work_tree());
-	}
-
-	set_git_dir(make_absolute_path(git_dir));
-
-	return init_db(template_dir, flags);
-}
diff --git a/builtin-log.c b/builtin-log.c
deleted file mode 100644
index 3035816..0000000
--- a/builtin-log.c
+++ /dev/null
@@ -1,1301 +0,0 @@
-/*
- * Builtin "git log" and related commands (show, whatchanged)
- *
- * (C) Copyright 2006 Linus Torvalds
- *		 2006 Junio Hamano
- */
-#include "cache.h"
-#include "color.h"
-#include "commit.h"
-#include "diff.h"
-#include "revision.h"
-#include "log-tree.h"
-#include "builtin.h"
-#include "tag.h"
-#include "reflog-walk.h"
-#include "patch-ids.h"
-#include "run-command.h"
-#include "shortlog.h"
-#include "remote.h"
-#include "string-list.h"
-#include "parse-options.h"
-
-/* Set a default date-time format for git log ("log.date" config variable) */
-static const char *default_date_mode = NULL;
-
-static int default_show_root = 1;
-static const char *fmt_patch_subject_prefix = "PATCH";
-static const char *fmt_pretty;
-
-static void cmd_log_init(int argc, const char **argv, const char *prefix,
-		      struct rev_info *rev)
-{
-	int i;
-
-	rev->abbrev = DEFAULT_ABBREV;
-	rev->commit_format = CMIT_FMT_DEFAULT;
-	if (fmt_pretty)
-		get_commit_format(fmt_pretty, rev);
-	rev->verbose_header = 1;
-	DIFF_OPT_SET(&rev->diffopt, RECURSIVE);
-	rev->show_root_diff = default_show_root;
-	rev->subject_prefix = fmt_patch_subject_prefix;
-	DIFF_OPT_SET(&rev->diffopt, ALLOW_TEXTCONV);
-
-	if (default_date_mode)
-		rev->date_mode = parse_date_format(default_date_mode);
-
-	argc = setup_revisions(argc, argv, rev, "HEAD");
-
-	if (rev->diffopt.pickaxe || rev->diffopt.filter)
-		rev->always_show_header = 0;
-	if (DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES)) {
-		rev->always_show_header = 0;
-		if (rev->diffopt.nr_paths != 1)
-			usage("git logs can only follow renames on one pathname at a time");
-	}
-	for (i = 1; i < argc; i++) {
-		const char *arg = argv[i];
-		if (!strcmp(arg, "--decorate")) {
-			load_ref_decorations();
-			rev->show_decorations = 1;
-		} else if (!strcmp(arg, "--source")) {
-			rev->show_source = 1;
-		} else
-			die("unrecognized argument: %s", arg);
-	}
-}
-
-/*
- * This gives a rough estimate for how many commits we
- * will print out in the list.
- */
-static int estimate_commit_count(struct rev_info *rev, struct commit_list *list)
-{
-	int n = 0;
-
-	while (list) {
-		struct commit *commit = list->item;
-		unsigned int flags = commit->object.flags;
-		list = list->next;
-		if (!(flags & (TREESAME | UNINTERESTING)))
-			n++;
-	}
-	return n;
-}
-
-static void show_early_header(struct rev_info *rev, const char *stage, int nr)
-{
-	if (rev->shown_one) {
-		rev->shown_one = 0;
-		if (rev->commit_format != CMIT_FMT_ONELINE)
-			putchar(rev->diffopt.line_termination);
-	}
-	printf("Final output: %d %s\n", nr, stage);
-}
-
-static struct itimerval early_output_timer;
-
-static void log_show_early(struct rev_info *revs, struct commit_list *list)
-{
-	int i = revs->early_output;
-	int show_header = 1;
-
-	sort_in_topological_order(&list, revs->lifo);
-	while (list && i) {
-		struct commit *commit = list->item;
-		switch (simplify_commit(revs, commit)) {
-		case commit_show:
-			if (show_header) {
-				int n = estimate_commit_count(revs, list);
-				show_early_header(revs, "incomplete", n);
-				show_header = 0;
-			}
-			log_tree_commit(revs, commit);
-			i--;
-			break;
-		case commit_ignore:
-			break;
-		case commit_error:
-			return;
-		}
-		list = list->next;
-	}
-
-	/* Did we already get enough commits for the early output? */
-	if (!i)
-		return;
-
-	/*
-	 * ..if no, then repeat it twice a second until we
-	 * do.
-	 *
-	 * NOTE! We don't use "it_interval", because if the
-	 * reader isn't listening, we want our output to be
-	 * throttled by the writing, and not have the timer
-	 * trigger every second even if we're blocked on a
-	 * reader!
-	 */
-	early_output_timer.it_value.tv_sec = 0;
-	early_output_timer.it_value.tv_usec = 500000;
-	setitimer(ITIMER_REAL, &early_output_timer, NULL);
-}
-
-static void early_output(int signal)
-{
-	show_early_output = log_show_early;
-}
-
-static void setup_early_output(struct rev_info *rev)
-{
-	struct sigaction sa;
-
-	/*
-	 * Set up the signal handler, minimally intrusively:
-	 * we only set a single volatile integer word (not
-	 * using sigatomic_t - trying to avoid unnecessary
-	 * system dependencies and headers), and using
-	 * SA_RESTART.
-	 */
-	memset(&sa, 0, sizeof(sa));
-	sa.sa_handler = early_output;
-	sigemptyset(&sa.sa_mask);
-	sa.sa_flags = SA_RESTART;
-	sigaction(SIGALRM, &sa, NULL);
-
-	/*
-	 * If we can get the whole output in less than a
-	 * tenth of a second, don't even bother doing the
-	 * early-output thing..
-	 *
-	 * This is a one-time-only trigger.
-	 */
-	early_output_timer.it_value.tv_sec = 0;
-	early_output_timer.it_value.tv_usec = 100000;
-	setitimer(ITIMER_REAL, &early_output_timer, NULL);
-}
-
-static void finish_early_output(struct rev_info *rev)
-{
-	int n = estimate_commit_count(rev, rev->commits);
-	signal(SIGALRM, SIG_IGN);
-	show_early_header(rev, "done", n);
-}
-
-static int cmd_log_walk(struct rev_info *rev)
-{
-	struct commit *commit;
-
-	if (rev->early_output)
-		setup_early_output(rev);
-
-	if (prepare_revision_walk(rev))
-		die("revision walk setup failed");
-
-	if (rev->early_output)
-		finish_early_output(rev);
-
-	/*
-	 * For --check and --exit-code, the exit code is based on CHECK_FAILED
-	 * and HAS_CHANGES being accumulated in rev->diffopt, so be careful to
-	 * retain that state information if replacing rev->diffopt in this loop
-	 */
-	while ((commit = get_revision(rev)) != NULL) {
-		log_tree_commit(rev, commit);
-		if (!rev->reflog_info) {
-			/* we allow cycles in reflog ancestry */
-			free(commit->buffer);
-			commit->buffer = NULL;
-		}
-		free_commit_list(commit->parents);
-		commit->parents = NULL;
-	}
-	if (rev->diffopt.output_format & DIFF_FORMAT_CHECKDIFF &&
-	    DIFF_OPT_TST(&rev->diffopt, CHECK_FAILED)) {
-		return 02;
-	}
-	return diff_result_code(&rev->diffopt, 0);
-}
-
-static int git_log_config(const char *var, const char *value, void *cb)
-{
-	if (!strcmp(var, "format.pretty"))
-		return git_config_string(&fmt_pretty, var, value);
-	if (!strcmp(var, "format.subjectprefix"))
-		return git_config_string(&fmt_patch_subject_prefix, var, value);
-	if (!strcmp(var, "log.date"))
-		return git_config_string(&default_date_mode, var, value);
-	if (!strcmp(var, "log.showroot")) {
-		default_show_root = git_config_bool(var, value);
-		return 0;
-	}
-	return git_diff_ui_config(var, value, cb);
-}
-
-int cmd_whatchanged(int argc, const char **argv, const char *prefix)
-{
-	struct rev_info rev;
-
-	git_config(git_log_config, NULL);
-
-	if (diff_use_color_default == -1)
-		diff_use_color_default = git_use_color_default;
-
-	init_revisions(&rev, prefix);
-	rev.diff = 1;
-	rev.simplify_history = 0;
-	cmd_log_init(argc, argv, prefix, &rev);
-	if (!rev.diffopt.output_format)
-		rev.diffopt.output_format = DIFF_FORMAT_RAW;
-	return cmd_log_walk(&rev);
-}
-
-static void show_tagger(char *buf, int len, struct rev_info *rev)
-{
-	struct strbuf out = STRBUF_INIT;
-
-	pp_user_info("Tagger", rev->commit_format, &out, buf, rev->date_mode,
-		git_log_output_encoding ?
-		git_log_output_encoding: git_commit_encoding);
-	printf("%s", out.buf);
-	strbuf_release(&out);
-}
-
-static int show_object(const unsigned char *sha1, int show_tag_object,
-	struct rev_info *rev)
-{
-	unsigned long size;
-	enum object_type type;
-	char *buf = read_sha1_file(sha1, &type, &size);
-	int offset = 0;
-
-	if (!buf)
-		return error("Could not read object %s", sha1_to_hex(sha1));
-
-	if (show_tag_object)
-		while (offset < size && buf[offset] != '\n') {
-			int new_offset = offset + 1;
-			while (new_offset < size && buf[new_offset++] != '\n')
-				; /* do nothing */
-			if (!prefixcmp(buf + offset, "tagger "))
-				show_tagger(buf + offset + 7,
-					    new_offset - offset - 7, rev);
-			offset = new_offset;
-		}
-
-	if (offset < size)
-		fwrite(buf + offset, size - offset, 1, stdout);
-	free(buf);
-	return 0;
-}
-
-static int show_tree_object(const unsigned char *sha1,
-		const char *base, int baselen,
-		const char *pathname, unsigned mode, int stage, void *context)
-{
-	printf("%s%s\n", pathname, S_ISDIR(mode) ? "/" : "");
-	return 0;
-}
-
-int cmd_show(int argc, const char **argv, const char *prefix)
-{
-	struct rev_info rev;
-	struct object_array_entry *objects;
-	int i, count, ret = 0;
-
-	git_config(git_log_config, NULL);
-
-	if (diff_use_color_default == -1)
-		diff_use_color_default = git_use_color_default;
-
-	init_revisions(&rev, prefix);
-	rev.diff = 1;
-	rev.combine_merges = 1;
-	rev.dense_combined_merges = 1;
-	rev.always_show_header = 1;
-	rev.ignore_merges = 0;
-	rev.no_walk = 1;
-	cmd_log_init(argc, argv, prefix, &rev);
-
-	count = rev.pending.nr;
-	objects = rev.pending.objects;
-	for (i = 0; i < count && !ret; i++) {
-		struct object *o = objects[i].item;
-		const char *name = objects[i].name;
-		switch (o->type) {
-		case OBJ_BLOB:
-			ret = show_object(o->sha1, 0, NULL);
-			break;
-		case OBJ_TAG: {
-			struct tag *t = (struct tag *)o;
-
-			if (rev.shown_one)
-				putchar('\n');
-			printf("%stag %s%s\n",
-					diff_get_color_opt(&rev.diffopt, DIFF_COMMIT),
-					t->tag,
-					diff_get_color_opt(&rev.diffopt, DIFF_RESET));
-			ret = show_object(o->sha1, 1, &rev);
-			rev.shown_one = 1;
-			if (ret)
-				break;
-			o = parse_object(t->tagged->sha1);
-			if (!o)
-				ret = error("Could not read object %s",
-					    sha1_to_hex(t->tagged->sha1));
-			objects[i].item = o;
-			i--;
-			break;
-		}
-		case OBJ_TREE:
-			if (rev.shown_one)
-				putchar('\n');
-			printf("%stree %s%s\n\n",
-					diff_get_color_opt(&rev.diffopt, DIFF_COMMIT),
-					name,
-					diff_get_color_opt(&rev.diffopt, DIFF_RESET));
-			read_tree_recursive((struct tree *)o, "", 0, 0, NULL,
-					show_tree_object, NULL);
-			rev.shown_one = 1;
-			break;
-		case OBJ_COMMIT:
-			rev.pending.nr = rev.pending.alloc = 0;
-			rev.pending.objects = NULL;
-			add_object_array(o, name, &rev.pending);
-			ret = cmd_log_walk(&rev);
-			break;
-		default:
-			ret = error("Unknown type: %d", o->type);
-		}
-	}
-	free(objects);
-	return ret;
-}
-
-/*
- * This is equivalent to "git log -g --abbrev-commit --pretty=oneline"
- */
-int cmd_log_reflog(int argc, const char **argv, const char *prefix)
-{
-	struct rev_info rev;
-
-	git_config(git_log_config, NULL);
-
-	if (diff_use_color_default == -1)
-		diff_use_color_default = git_use_color_default;
-
-	init_revisions(&rev, prefix);
-	init_reflog_walk(&rev.reflog_info);
-	rev.abbrev_commit = 1;
-	rev.verbose_header = 1;
-	cmd_log_init(argc, argv, prefix, &rev);
-
-	/*
-	 * This means that we override whatever commit format the user gave
-	 * on the cmd line.  Sad, but cmd_log_init() currently doesn't
-	 * allow us to set a different default.
-	 */
-	rev.commit_format = CMIT_FMT_ONELINE;
-	rev.use_terminator = 1;
-	rev.always_show_header = 1;
-
-	/*
-	 * We get called through "git reflog", so unlike the other log
-	 * routines, we need to set up our pager manually..
-	 */
-	setup_pager();
-
-	return cmd_log_walk(&rev);
-}
-
-int cmd_log(int argc, const char **argv, const char *prefix)
-{
-	struct rev_info rev;
-
-	git_config(git_log_config, NULL);
-
-	if (diff_use_color_default == -1)
-		diff_use_color_default = git_use_color_default;
-
-	init_revisions(&rev, prefix);
-	rev.always_show_header = 1;
-	cmd_log_init(argc, argv, prefix, &rev);
-	return cmd_log_walk(&rev);
-}
-
-/* format-patch */
-
-static const char *fmt_patch_suffix = ".patch";
-static int numbered = 0;
-static int auto_number = 1;
-
-static char *default_attach = NULL;
-
-static char **extra_hdr;
-static int extra_hdr_nr;
-static int extra_hdr_alloc;
-
-static char **extra_to;
-static int extra_to_nr;
-static int extra_to_alloc;
-
-static char **extra_cc;
-static int extra_cc_nr;
-static int extra_cc_alloc;
-
-static void add_header(const char *value)
-{
-	int len = strlen(value);
-	while (len && value[len - 1] == '\n')
-		len--;
-	if (!strncasecmp(value, "to: ", 4)) {
-		ALLOC_GROW(extra_to, extra_to_nr + 1, extra_to_alloc);
-		extra_to[extra_to_nr++] = xstrndup(value + 4, len - 4);
-		return;
-	}
-	if (!strncasecmp(value, "cc: ", 4)) {
-		ALLOC_GROW(extra_cc, extra_cc_nr + 1, extra_cc_alloc);
-		extra_cc[extra_cc_nr++] = xstrndup(value + 4, len - 4);
-		return;
-	}
-	ALLOC_GROW(extra_hdr, extra_hdr_nr + 1, extra_hdr_alloc);
-	extra_hdr[extra_hdr_nr++] = xstrndup(value, len);
-}
-
-#define THREAD_SHALLOW 1
-#define THREAD_DEEP 2
-static int thread = 0;
-static int do_signoff = 0;
-
-static int git_format_config(const char *var, const char *value, void *cb)
-{
-	if (!strcmp(var, "format.headers")) {
-		if (!value)
-			die("format.headers without value");
-		add_header(value);
-		return 0;
-	}
-	if (!strcmp(var, "format.suffix"))
-		return git_config_string(&fmt_patch_suffix, var, value);
-	if (!strcmp(var, "format.cc")) {
-		if (!value)
-			return config_error_nonbool(var);
-		ALLOC_GROW(extra_cc, extra_cc_nr + 1, extra_cc_alloc);
-		extra_cc[extra_cc_nr++] = xstrdup(value);
-		return 0;
-	}
-	if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
-		return 0;
-	}
-	if (!strcmp(var, "format.numbered")) {
-		if (value && !strcasecmp(value, "auto")) {
-			auto_number = 1;
-			return 0;
-		}
-		numbered = git_config_bool(var, value);
-		auto_number = auto_number && numbered;
-		return 0;
-	}
-	if (!strcmp(var, "format.attach")) {
-		if (value && *value)
-			default_attach = xstrdup(value);
-		else
-			default_attach = xstrdup(git_version_string);
-		return 0;
-	}
-	if (!strcmp(var, "format.thread")) {
-		if (value && !strcasecmp(value, "deep")) {
-			thread = THREAD_DEEP;
-			return 0;
-		}
-		if (value && !strcasecmp(value, "shallow")) {
-			thread = THREAD_SHALLOW;
-			return 0;
-		}
-		thread = git_config_bool(var, value) && THREAD_SHALLOW;
-		return 0;
-	}
-	if (!strcmp(var, "format.signoff")) {
-		do_signoff = git_config_bool(var, value);
-		return 0;
-	}
-
-	return git_log_config(var, value, cb);
-}
-
-static FILE *realstdout = NULL;
-static const char *output_directory = NULL;
-static int outdir_offset;
-
-static int reopen_stdout(struct commit *commit, struct rev_info *rev)
-{
-	struct strbuf filename = STRBUF_INIT;
-	int suffix_len = strlen(fmt_patch_suffix) + 1;
-
-	if (output_directory) {
-		strbuf_addstr(&filename, output_directory);
-		if (filename.len >=
-		    PATH_MAX - FORMAT_PATCH_NAME_MAX - suffix_len)
-			return error("name of output directory is too long");
-		if (filename.buf[filename.len - 1] != '/')
-			strbuf_addch(&filename, '/');
-	}
-
-	get_patch_filename(commit, rev->nr, fmt_patch_suffix, &filename);
-
-	if (!DIFF_OPT_TST(&rev->diffopt, QUIET))
-		fprintf(realstdout, "%s\n", filename.buf + outdir_offset);
-
-	if (freopen(filename.buf, "w", stdout) == NULL)
-		return error("Cannot open patch file %s", filename.buf);
-
-	strbuf_release(&filename);
-	return 0;
-}
-
-static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const char *prefix)
-{
-	struct rev_info check_rev;
-	struct commit *commit;
-	struct object *o1, *o2;
-	unsigned flags1, flags2;
-
-	if (rev->pending.nr != 2)
-		die("Need exactly one range.");
-
-	o1 = rev->pending.objects[0].item;
-	flags1 = o1->flags;
-	o2 = rev->pending.objects[1].item;
-	flags2 = o2->flags;
-
-	if ((flags1 & UNINTERESTING) == (flags2 & UNINTERESTING))
-		die("Not a range.");
-
-	init_patch_ids(ids);
-
-	/* given a range a..b get all patch ids for b..a */
-	init_revisions(&check_rev, prefix);
-	o1->flags ^= UNINTERESTING;
-	o2->flags ^= UNINTERESTING;
-	add_pending_object(&check_rev, o1, "o1");
-	add_pending_object(&check_rev, o2, "o2");
-	if (prepare_revision_walk(&check_rev))
-		die("revision walk setup failed");
-
-	while ((commit = get_revision(&check_rev)) != NULL) {
-		/* ignore merges */
-		if (commit->parents && commit->parents->next)
-			continue;
-
-		add_commit_patch_id(commit, ids);
-	}
-
-	/* reset for next revision walk */
-	clear_commit_marks((struct commit *)o1,
-			SEEN | UNINTERESTING | SHOWN | ADDED);
-	clear_commit_marks((struct commit *)o2,
-			SEEN | UNINTERESTING | SHOWN | ADDED);
-	o1->flags = flags1;
-	o2->flags = flags2;
-}
-
-static void gen_message_id(struct rev_info *info, char *base)
-{
-	const char *committer = git_committer_info(IDENT_WARN_ON_NO_NAME);
-	const char *email_start = strrchr(committer, '<');
-	const char *email_end = strrchr(committer, '>');
-	struct strbuf buf = STRBUF_INIT;
-	if (!email_start || !email_end || email_start > email_end - 1)
-		die("Could not extract email from committer identity.");
-	strbuf_addf(&buf, "%s.%lu.git.%.*s", base,
-		    (unsigned long) time(NULL),
-		    (int)(email_end - email_start - 1), email_start + 1);
-	info->message_id = strbuf_detach(&buf, NULL);
-}
-
-static void make_cover_letter(struct rev_info *rev, int use_stdout,
-			      int numbered, int numbered_files,
-			      struct commit *origin,
-			      int nr, struct commit **list, struct commit *head)
-{
-	const char *committer;
-	const char *subject_start = NULL;
-	const char *body = "*** SUBJECT HERE ***\n\n*** BLURB HERE ***\n";
-	const char *msg;
-	const char *extra_headers = rev->extra_headers;
-	struct shortlog log;
-	struct strbuf sb = STRBUF_INIT;
-	int i;
-	const char *encoding = "UTF-8";
-	struct diff_options opts;
-	int need_8bit_cte = 0;
-	struct commit *commit = NULL;
-
-	if (rev->commit_format != CMIT_FMT_EMAIL)
-		die("Cover letter needs email format");
-
-	committer = git_committer_info(0);
-
-	if (!numbered_files) {
-		/*
-		 * We fake a commit for the cover letter so we get the filename
-		 * desired.
-		 */
-		commit = xcalloc(1, sizeof(*commit));
-		commit->buffer = xmalloc(400);
-		snprintf(commit->buffer, 400,
-			"tree 0000000000000000000000000000000000000000\n"
-			"parent %s\n"
-			"author %s\n"
-			"committer %s\n\n"
-			"cover letter\n",
-			sha1_to_hex(head->object.sha1), committer, committer);
-	}
-
-	if (!use_stdout && reopen_stdout(commit, rev))
-		return;
-
-	if (commit) {
-
-		free(commit->buffer);
-		free(commit);
-	}
-
-	log_write_email_headers(rev, head, &subject_start, &extra_headers,
-				&need_8bit_cte);
-
-	msg = body;
-	pp_user_info(NULL, CMIT_FMT_EMAIL, &sb, committer, DATE_RFC2822,
-		     encoding);
-	pp_title_line(CMIT_FMT_EMAIL, &msg, &sb, subject_start, extra_headers,
-		      encoding, need_8bit_cte);
-	pp_remainder(CMIT_FMT_EMAIL, &msg, &sb, 0);
-	printf("%s\n", sb.buf);
-
-	strbuf_release(&sb);
-
-	shortlog_init(&log);
-	log.wrap_lines = 1;
-	log.wrap = 72;
-	log.in1 = 2;
-	log.in2 = 4;
-	for (i = 0; i < nr; i++)
-		shortlog_add_commit(&log, list[i]);
-
-	shortlog_output(&log);
-
-	/*
-	 * We can only do diffstat with a unique reference point
-	 */
-	if (!origin)
-		return;
-
-	memcpy(&opts, &rev->diffopt, sizeof(opts));
-	opts.output_format = DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
-
-	diff_setup_done(&opts);
-
-	diff_tree_sha1(origin->tree->object.sha1,
-		       head->tree->object.sha1,
-		       "", &opts);
-	diffcore_std(&opts);
-	diff_flush(&opts);
-
-	printf("\n");
-}
-
-static const char *clean_message_id(const char *msg_id)
-{
-	char ch;
-	const char *a, *z, *m;
-
-	m = msg_id;
-	while ((ch = *m) && (isspace(ch) || (ch == '<')))
-		m++;
-	a = m;
-	z = NULL;
-	while ((ch = *m)) {
-		if (!isspace(ch) && (ch != '>'))
-			z = m;
-		m++;
-	}
-	if (!z)
-		die("insane in-reply-to: %s", msg_id);
-	if (++z == m)
-		return a;
-	return xmemdupz(a, z - a);
-}
-
-static const char *set_outdir(const char *prefix, const char *output_directory)
-{
-	if (output_directory && is_absolute_path(output_directory))
-		return output_directory;
-
-	if (!prefix || !*prefix) {
-		if (output_directory)
-			return output_directory;
-		/* The user did not explicitly ask for "./" */
-		outdir_offset = 2;
-		return "./";
-	}
-
-	outdir_offset = strlen(prefix);
-	if (!output_directory)
-		return prefix;
-
-	return xstrdup(prefix_filename(prefix, outdir_offset,
-				       output_directory));
-}
-
-static const char * const builtin_format_patch_usage[] = {
-	"git format-patch [options] [<since> | <revision range>]",
-	NULL
-};
-
-static int keep_subject = 0;
-
-static int keep_callback(const struct option *opt, const char *arg, int unset)
-{
-	((struct rev_info *)opt->value)->total = -1;
-	keep_subject = 1;
-	return 0;
-}
-
-static int subject_prefix = 0;
-
-static int subject_prefix_callback(const struct option *opt, const char *arg,
-			    int unset)
-{
-	subject_prefix = 1;
-	((struct rev_info *)opt->value)->subject_prefix = arg;
-	return 0;
-}
-
-static int numbered_cmdline_opt = 0;
-
-static int numbered_callback(const struct option *opt, const char *arg,
-			     int unset)
-{
-	*(int *)opt->value = numbered_cmdline_opt = unset ? 0 : 1;
-	if (unset)
-		auto_number =  0;
-	return 0;
-}
-
-static int no_numbered_callback(const struct option *opt, const char *arg,
-				int unset)
-{
-	return numbered_callback(opt, arg, 1);
-}
-
-static int output_directory_callback(const struct option *opt, const char *arg,
-			      int unset)
-{
-	const char **dir = (const char **)opt->value;
-	if (*dir)
-		die("Two output directories?");
-	*dir = arg;
-	return 0;
-}
-
-static int thread_callback(const struct option *opt, const char *arg, int unset)
-{
-	int *thread = (int *)opt->value;
-	if (unset)
-		*thread = 0;
-	else if (!arg || !strcmp(arg, "shallow"))
-		*thread = THREAD_SHALLOW;
-	else if (!strcmp(arg, "deep"))
-		*thread = THREAD_DEEP;
-	else
-		return 1;
-	return 0;
-}
-
-static int attach_callback(const struct option *opt, const char *arg, int unset)
-{
-	struct rev_info *rev = (struct rev_info *)opt->value;
-	if (unset)
-		rev->mime_boundary = NULL;
-	else if (arg)
-		rev->mime_boundary = arg;
-	else
-		rev->mime_boundary = git_version_string;
-	rev->no_inline = unset ? 0 : 1;
-	return 0;
-}
-
-static int inline_callback(const struct option *opt, const char *arg, int unset)
-{
-	struct rev_info *rev = (struct rev_info *)opt->value;
-	if (unset)
-		rev->mime_boundary = NULL;
-	else if (arg)
-		rev->mime_boundary = arg;
-	else
-		rev->mime_boundary = git_version_string;
-	rev->no_inline = 0;
-	return 0;
-}
-
-static int header_callback(const struct option *opt, const char *arg, int unset)
-{
-	add_header(arg);
-	return 0;
-}
-
-static int cc_callback(const struct option *opt, const char *arg, int unset)
-{
-	ALLOC_GROW(extra_cc, extra_cc_nr + 1, extra_cc_alloc);
-	extra_cc[extra_cc_nr++] = xstrdup(arg);
-	return 0;
-}
-
-int cmd_format_patch(int argc, const char **argv, const char *prefix)
-{
-	struct commit *commit;
-	struct commit **list = NULL;
-	struct rev_info rev;
-	int nr = 0, total, i;
-	int use_stdout = 0;
-	int start_number = -1;
-	int numbered_files = 0;		/* _just_ numbers */
-	int ignore_if_in_upstream = 0;
-	int cover_letter = 0;
-	int boundary_count = 0;
-	int no_binary_diff = 0;
-	struct commit *origin = NULL, *head = NULL;
-	const char *in_reply_to = NULL;
-	struct patch_ids ids;
-	char *add_signoff = NULL;
-	struct strbuf buf = STRBUF_INIT;
-	const struct option builtin_format_patch_options[] = {
-		{ OPTION_CALLBACK, 'n', "numbered", &numbered, NULL,
-			    "use [PATCH n/m] even with a single patch",
-			    PARSE_OPT_NOARG, numbered_callback },
-		{ OPTION_CALLBACK, 'N', "no-numbered", &numbered, NULL,
-			    "use [PATCH] even with multiple patches",
-			    PARSE_OPT_NOARG, no_numbered_callback },
-		OPT_BOOLEAN('s', "signoff", &do_signoff, "add Signed-off-by:"),
-		OPT_BOOLEAN(0, "stdout", &use_stdout,
-			    "print patches to standard out"),
-		OPT_BOOLEAN(0, "cover-letter", &cover_letter,
-			    "generate a cover letter"),
-		OPT_BOOLEAN(0, "numbered-files", &numbered_files,
-			    "use simple number sequence for output file names"),
-		OPT_STRING(0, "suffix", &fmt_patch_suffix, "sfx",
-			    "use <sfx> instead of '.patch'"),
-		OPT_INTEGER(0, "start-number", &start_number,
-			    "start numbering patches at <n> instead of 1"),
-		{ OPTION_CALLBACK, 0, "subject-prefix", &rev, "prefix",
-			    "Use [<prefix>] instead of [PATCH]",
-			    PARSE_OPT_NONEG, subject_prefix_callback },
-		{ OPTION_CALLBACK, 'o', "output-directory", &output_directory,
-			    "dir", "store resulting files in <dir>",
-			    PARSE_OPT_NONEG, output_directory_callback },
-		{ OPTION_CALLBACK, 'k', "keep-subject", &rev, NULL,
-			    "don't strip/add [PATCH]",
-			    PARSE_OPT_NOARG | PARSE_OPT_NONEG, keep_callback },
-		OPT_BOOLEAN(0, "no-binary", &no_binary_diff,
-			    "don't output binary diffs"),
-		OPT_BOOLEAN(0, "ignore-if-in-upstream", &ignore_if_in_upstream,
-			    "don't include a patch matching a commit upstream"),
-		OPT_GROUP("Messaging"),
-		{ OPTION_CALLBACK, 0, "add-header", NULL, "header",
-			    "add email header", PARSE_OPT_NONEG,
-			    header_callback },
-		{ OPTION_CALLBACK, 0, "cc", NULL, "email", "add Cc: header",
-			    PARSE_OPT_NONEG, cc_callback },
-		OPT_STRING(0, "in-reply-to", &in_reply_to, "message-id",
-			    "make first mail a reply to <message-id>"),
-		{ OPTION_CALLBACK, 0, "attach", &rev, "boundary",
-			    "attach the patch", PARSE_OPT_OPTARG,
-			    attach_callback },
-		{ OPTION_CALLBACK, 0, "inline", &rev, "boundary",
-			    "inline the patch",
-			    PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
-			    inline_callback },
-		{ OPTION_CALLBACK, 0, "thread", &thread, "style",
-			    "enable message threading, styles: shallow, deep",
-			    PARSE_OPT_OPTARG, thread_callback },
-		OPT_END()
-	};
-
-	git_config(git_format_config, NULL);
-	init_revisions(&rev, prefix);
-	rev.commit_format = CMIT_FMT_EMAIL;
-	rev.verbose_header = 1;
-	rev.diff = 1;
-	rev.combine_merges = 0;
-	rev.ignore_merges = 1;
-	DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
-
-	rev.subject_prefix = fmt_patch_subject_prefix;
-
-	if (default_attach) {
-		rev.mime_boundary = default_attach;
-		rev.no_inline = 1;
-	}
-
-	/*
-	 * Parse the arguments before setup_revisions(), or something
-	 * like "git format-patch -o a123 HEAD^.." may fail; a123 is
-	 * possibly a valid SHA1.
-	 */
-	argc = parse_options(argc, argv, prefix, builtin_format_patch_options,
-			     builtin_format_patch_usage,
-			     PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN);
-
-	if (do_signoff) {
-		const char *committer;
-		const char *endpos;
-		committer = git_committer_info(IDENT_ERROR_ON_NO_NAME);
-		endpos = strchr(committer, '>');
-		if (!endpos)
-			die("bogus committer info %s", committer);
-		add_signoff = xmemdupz(committer, endpos - committer + 1);
-	}
-
-	for (i = 0; i < extra_hdr_nr; i++) {
-		strbuf_addstr(&buf, extra_hdr[i]);
-		strbuf_addch(&buf, '\n');
-	}
-
-	if (extra_to_nr)
-		strbuf_addstr(&buf, "To: ");
-	for (i = 0; i < extra_to_nr; i++) {
-		if (i)
-			strbuf_addstr(&buf, "    ");
-		strbuf_addstr(&buf, extra_to[i]);
-		if (i + 1 < extra_to_nr)
-			strbuf_addch(&buf, ',');
-		strbuf_addch(&buf, '\n');
-	}
-
-	if (extra_cc_nr)
-		strbuf_addstr(&buf, "Cc: ");
-	for (i = 0; i < extra_cc_nr; i++) {
-		if (i)
-			strbuf_addstr(&buf, "    ");
-		strbuf_addstr(&buf, extra_cc[i]);
-		if (i + 1 < extra_cc_nr)
-			strbuf_addch(&buf, ',');
-		strbuf_addch(&buf, '\n');
-	}
-
-	rev.extra_headers = strbuf_detach(&buf, NULL);
-
-	if (start_number < 0)
-		start_number = 1;
-
-	/*
-	 * If numbered is set solely due to format.numbered in config,
-	 * and it would conflict with --keep-subject (-k) from the
-	 * command line, reset "numbered".
-	 */
-	if (numbered && keep_subject && !numbered_cmdline_opt)
-		numbered = 0;
-
-	if (numbered && keep_subject)
-		die ("-n and -k are mutually exclusive.");
-	if (keep_subject && subject_prefix)
-		die ("--subject-prefix and -k are mutually exclusive.");
-
-	argc = setup_revisions(argc, argv, &rev, "HEAD");
-	if (argc > 1)
-		die ("unrecognized argument: %s", argv[1]);
-
-	if (!rev.diffopt.output_format
-		|| rev.diffopt.output_format == DIFF_FORMAT_PATCH)
-		rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_SUMMARY | DIFF_FORMAT_PATCH;
-
-	if (!DIFF_OPT_TST(&rev.diffopt, TEXT) && !no_binary_diff)
-		DIFF_OPT_SET(&rev.diffopt, BINARY);
-
-	if (!use_stdout)
-		output_directory = set_outdir(prefix, output_directory);
-
-	if (output_directory) {
-		if (use_stdout)
-			die("standard output, or directory, which one?");
-		if (mkdir(output_directory, 0777) < 0 && errno != EEXIST)
-			die_errno("Could not create directory '%s'",
-				  output_directory);
-	}
-
-	if (rev.pending.nr == 1) {
-		if (rev.max_count < 0 && !rev.show_root_diff) {
-			/*
-			 * This is traditional behaviour of "git format-patch
-			 * origin" that prepares what the origin side still
-			 * does not have.
-			 */
-			rev.pending.objects[0].item->flags |= UNINTERESTING;
-			add_head_to_pending(&rev);
-		}
-		/*
-		 * Otherwise, it is "format-patch -22 HEAD", and/or
-		 * "format-patch --root HEAD".  The user wants
-		 * get_revision() to do the usual traversal.
-		 */
-	}
-
-	/*
-	 * We cannot move this anywhere earlier because we do want to
-	 * know if --root was given explicitly from the comand line.
-	 */
-	rev.show_root_diff = 1;
-
-	if (cover_letter) {
-		/* remember the range */
-		int i;
-		for (i = 0; i < rev.pending.nr; i++) {
-			struct object *o = rev.pending.objects[i].item;
-			if (!(o->flags & UNINTERESTING))
-				head = (struct commit *)o;
-		}
-		/* We can't generate a cover letter without any patches */
-		if (!head)
-			return 0;
-	}
-
-	if (ignore_if_in_upstream)
-		get_patch_ids(&rev, &ids, prefix);
-
-	if (!use_stdout)
-		realstdout = xfdopen(xdup(1), "w");
-
-	if (prepare_revision_walk(&rev))
-		die("revision walk setup failed");
-	rev.boundary = 1;
-	while ((commit = get_revision(&rev)) != NULL) {
-		if (commit->object.flags & BOUNDARY) {
-			boundary_count++;
-			origin = (boundary_count == 1) ? commit : NULL;
-			continue;
-		}
-
-		/* ignore merges */
-		if (commit->parents && commit->parents->next)
-			continue;
-
-		if (ignore_if_in_upstream &&
-				has_commit_patch_id(commit, &ids))
-			continue;
-
-		nr++;
-		list = xrealloc(list, nr * sizeof(list[0]));
-		list[nr - 1] = commit;
-	}
-	total = nr;
-	if (!keep_subject && auto_number && total > 1)
-		numbered = 1;
-	if (numbered)
-		rev.total = total + start_number - 1;
-	if (in_reply_to || thread || cover_letter)
-		rev.ref_message_ids = xcalloc(1, sizeof(struct string_list));
-	if (in_reply_to) {
-		const char *msgid = clean_message_id(in_reply_to);
-		string_list_append(msgid, rev.ref_message_ids);
-	}
-	rev.numbered_files = numbered_files;
-	rev.patch_suffix = fmt_patch_suffix;
-	if (cover_letter) {
-		if (thread)
-			gen_message_id(&rev, "cover");
-		make_cover_letter(&rev, use_stdout, numbered, numbered_files,
-				  origin, nr, list, head);
-		total++;
-		start_number--;
-	}
-	rev.add_signoff = add_signoff;
-	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) {
-			/* Have we already had a message ID? */
-			if (rev.message_id) {
-				/*
-				 * For deep threading: make every mail
-				 * a reply to the previous one, no
-				 * matter what other options are set.
-				 *
-				 * For shallow threading:
-				 *
-				 * Without --cover-letter and
-				 * --in-reply-to, make every mail a
-				 * reply to the one before.
-				 *
-				 * With --in-reply-to but no
-				 * --cover-letter, make every mail a
-				 * reply to the <reply-to>.
-				 *
-				 * With --cover-letter, make every
-				 * mail but the cover letter a reply
-				 * to the cover letter.  The cover
-				 * letter is a reply to the
-				 * --in-reply-to, if specified.
-				 */
-				if (thread == THREAD_SHALLOW
-				    && rev.ref_message_ids->nr > 0
-				    && (!cover_letter || rev.nr > 1))
-					free(rev.message_id);
-				else
-					string_list_append(rev.message_id,
-							   rev.ref_message_ids);
-			}
-			gen_message_id(&rev, sha1_to_hex(commit->object.sha1));
-		}
-
-		if (!use_stdout && reopen_stdout(numbered_files ? NULL : commit,
-						 &rev))
-			die("Failed to create output files");
-		shown = log_tree_commit(&rev, commit);
-		free(commit->buffer);
-		commit->buffer = NULL;
-
-		/* We put one extra blank line between formatted
-		 * patches and this flag is used by log-tree code
-		 * to see if it needs to emit a LF before showing
-		 * the log; when using one file per patch, we do
-		 * not want the extra blank line.
-		 */
-		if (!use_stdout)
-			rev.shown_one = 0;
-		if (shown) {
-			if (rev.mime_boundary)
-				printf("\n--%s%s--\n\n\n",
-				       mime_boundary_leader,
-				       rev.mime_boundary);
-			else
-				printf("-- \n%s\n\n", git_version_string);
-		}
-		if (!use_stdout)
-			fclose(stdout);
-	}
-	free(list);
-	if (ignore_if_in_upstream)
-		free_patch_ids(&ids);
-	return 0;
-}
-
-static int add_pending_commit(const char *arg, struct rev_info *revs, int flags)
-{
-	unsigned char sha1[20];
-	if (get_sha1(arg, sha1) == 0) {
-		struct commit *commit = lookup_commit_reference(sha1);
-		if (commit) {
-			commit->object.flags |= flags;
-			add_pending_object(revs, &commit->object, arg);
-			return 0;
-		}
-	}
-	return -1;
-}
-
-static const char cherry_usage[] =
-"git cherry [-v] [<upstream> [<head> [<limit>]]]";
-int cmd_cherry(int argc, const char **argv, const char *prefix)
-{
-	struct rev_info revs;
-	struct patch_ids ids;
-	struct commit *commit;
-	struct commit_list *list = NULL;
-	struct branch *current_branch;
-	const char *upstream;
-	const char *head = "HEAD";
-	const char *limit = NULL;
-	int verbose = 0;
-
-	if (argc > 1 && !strcmp(argv[1], "-v")) {
-		verbose = 1;
-		argc--;
-		argv++;
-	}
-
-	switch (argc) {
-	case 4:
-		limit = argv[3];
-		/* FALLTHROUGH */
-	case 3:
-		head = argv[2];
-		/* FALLTHROUGH */
-	case 2:
-		upstream = argv[1];
-		break;
-	default:
-		current_branch = branch_get(NULL);
-		if (!current_branch || !current_branch->merge
-					|| !current_branch->merge[0]
-					|| !current_branch->merge[0]->dst) {
-			fprintf(stderr, "Could not find a tracked"
-					" remote branch, please"
-					" specify <upstream> manually.\n");
-			usage(cherry_usage);
-		}
-
-		upstream = current_branch->merge[0]->dst;
-	}
-
-	init_revisions(&revs, prefix);
-	revs.diff = 1;
-	revs.combine_merges = 0;
-	revs.ignore_merges = 1;
-	DIFF_OPT_SET(&revs.diffopt, RECURSIVE);
-
-	if (add_pending_commit(head, &revs, 0))
-		die("Unknown commit %s", head);
-	if (add_pending_commit(upstream, &revs, UNINTERESTING))
-		die("Unknown commit %s", upstream);
-
-	/* Don't say anything if head and upstream are the same. */
-	if (revs.pending.nr == 2) {
-		struct object_array_entry *o = revs.pending.objects;
-		if (hashcmp(o[0].item->sha1, o[1].item->sha1) == 0)
-			return 0;
-	}
-
-	get_patch_ids(&revs, &ids, prefix);
-
-	if (limit && add_pending_commit(limit, &revs, UNINTERESTING))
-		die("Unknown commit %s", limit);
-
-	/* reverse the list of commits */
-	if (prepare_revision_walk(&revs))
-		die("revision walk setup failed");
-	while ((commit = get_revision(&revs)) != NULL) {
-		/* ignore merges */
-		if (commit->parents && commit->parents->next)
-			continue;
-
-		commit_list_insert(commit, &list);
-	}
-
-	while (list) {
-		char sign = '+';
-
-		commit = list->item;
-		if (has_commit_patch_id(commit, &ids))
-			sign = '-';
-
-		if (verbose) {
-			struct strbuf buf = STRBUF_INIT;
-			pretty_print_commit(CMIT_FMT_ONELINE, commit,
-			                    &buf, 0, NULL, NULL, 0, 0);
-			printf("%c %s %s\n", sign,
-			       sha1_to_hex(commit->object.sha1), buf.buf);
-			strbuf_release(&buf);
-		}
-		else {
-			printf("%c %s\n", sign,
-			       sha1_to_hex(commit->object.sha1));
-		}
-
-		list = list->next;
-	}
-
-	free_patch_ids(&ids);
-	return 0;
-}
diff --git a/builtin-ls-files.c b/builtin-ls-files.c
deleted file mode 100644
index f473220..0000000
--- a/builtin-ls-files.c
+++ /dev/null
@@ -1,561 +0,0 @@
-/*
- * This merges the file listing in the directory cache index
- * with the actual working directory list, and shows different
- * combinations of the two.
- *
- * Copyright (C) Linus Torvalds, 2005
- */
-#include "cache.h"
-#include "quote.h"
-#include "dir.h"
-#include "builtin.h"
-#include "tree.h"
-#include "parse-options.h"
-
-static int abbrev;
-static int show_deleted;
-static int show_cached;
-static int show_others;
-static int show_stage;
-static int show_unmerged;
-static int show_modified;
-static int show_killed;
-static int show_valid_bit;
-static int line_terminator = '\n';
-
-static int prefix_len;
-static int prefix_offset;
-static const char **pathspec;
-static int error_unmatch;
-static char *ps_matched;
-static const char *with_tree;
-static int exc_given;
-
-static const char *tag_cached = "";
-static const char *tag_unmerged = "";
-static const char *tag_removed = "";
-static const char *tag_other = "";
-static const char *tag_killed = "";
-static const char *tag_modified = "";
-
-static void show_dir_entry(const char *tag, struct dir_entry *ent)
-{
-	int len = prefix_len;
-	int offset = prefix_offset;
-
-	if (len >= ent->len)
-		die("git ls-files: internal error - directory entry not superset of prefix");
-
-	if (!match_pathspec(pathspec, ent->name, ent->len, len, ps_matched))
-		return;
-
-	fputs(tag, stdout);
-	write_name_quoted(ent->name + offset, stdout, line_terminator);
-}
-
-static void show_other_files(struct dir_struct *dir)
-{
-	int i;
-
-	for (i = 0; i < dir->nr; i++) {
-		struct dir_entry *ent = dir->entries[i];
-		if (!cache_name_is_other(ent->name, ent->len))
-			continue;
-		show_dir_entry(tag_other, ent);
-	}
-}
-
-static void show_killed_files(struct dir_struct *dir)
-{
-	int i;
-	for (i = 0; i < dir->nr; i++) {
-		struct dir_entry *ent = dir->entries[i];
-		char *cp, *sp;
-		int pos, len, killed = 0;
-
-		for (cp = ent->name; cp - ent->name < ent->len; cp = sp + 1) {
-			sp = strchr(cp, '/');
-			if (!sp) {
-				/* If ent->name is prefix of an entry in the
-				 * cache, it will be killed.
-				 */
-				pos = cache_name_pos(ent->name, ent->len);
-				if (0 <= pos)
-					die("bug in show-killed-files");
-				pos = -pos - 1;
-				while (pos < active_nr &&
-				       ce_stage(active_cache[pos]))
-					pos++; /* skip unmerged */
-				if (active_nr <= pos)
-					break;
-				/* pos points at a name immediately after
-				 * ent->name in the cache.  Does it expect
-				 * ent->name to be a directory?
-				 */
-				len = ce_namelen(active_cache[pos]);
-				if ((ent->len < len) &&
-				    !strncmp(active_cache[pos]->name,
-					     ent->name, ent->len) &&
-				    active_cache[pos]->name[ent->len] == '/')
-					killed = 1;
-				break;
-			}
-			if (0 <= cache_name_pos(ent->name, sp - ent->name)) {
-				/* If any of the leading directories in
-				 * ent->name is registered in the cache,
-				 * ent->name will be killed.
-				 */
-				killed = 1;
-				break;
-			}
-		}
-		if (killed)
-			show_dir_entry(tag_killed, dir->entries[i]);
-	}
-}
-
-static void show_ce_entry(const char *tag, struct cache_entry *ce)
-{
-	int len = prefix_len;
-	int offset = prefix_offset;
-
-	if (len >= ce_namelen(ce))
-		die("git ls-files: internal error - cache entry not superset of prefix");
-
-	if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), len, ps_matched))
-		return;
-
-	if (tag && *tag && show_valid_bit &&
-	    (ce->ce_flags & CE_VALID)) {
-		static char alttag[4];
-		memcpy(alttag, tag, 3);
-		if (isalpha(tag[0]))
-			alttag[0] = tolower(tag[0]);
-		else if (tag[0] == '?')
-			alttag[0] = '!';
-		else {
-			alttag[0] = 'v';
-			alttag[1] = tag[0];
-			alttag[2] = ' ';
-			alttag[3] = 0;
-		}
-		tag = alttag;
-	}
-
-	if (!show_stage) {
-		fputs(tag, stdout);
-	} else {
-		printf("%s%06o %s %d\t",
-		       tag,
-		       ce->ce_mode,
-		       abbrev ? find_unique_abbrev(ce->sha1,abbrev)
-				: sha1_to_hex(ce->sha1),
-		       ce_stage(ce));
-	}
-	write_name_quoted(ce->name + offset, stdout, line_terminator);
-}
-
-static void show_files(struct dir_struct *dir, const char *prefix)
-{
-	int i;
-
-	/* For cached/deleted files we don't need to even do the readdir */
-	if (show_others || show_killed) {
-		fill_directory(dir, pathspec);
-		if (show_others)
-			show_other_files(dir);
-		if (show_killed)
-			show_killed_files(dir);
-	}
-	if (show_cached | show_stage) {
-		for (i = 0; i < active_nr; i++) {
-			struct cache_entry *ce = active_cache[i];
-			int dtype = ce_to_dtype(ce);
-			if (excluded(dir, ce->name, &dtype) !=
-					!!(dir->flags & DIR_SHOW_IGNORED))
-				continue;
-			if (show_unmerged && !ce_stage(ce))
-				continue;
-			if (ce->ce_flags & CE_UPDATE)
-				continue;
-			show_ce_entry(ce_stage(ce) ? tag_unmerged : tag_cached, ce);
-		}
-	}
-	if (show_deleted | show_modified) {
-		for (i = 0; i < active_nr; i++) {
-			struct cache_entry *ce = active_cache[i];
-			struct stat st;
-			int err;
-			int dtype = ce_to_dtype(ce);
-			if (excluded(dir, ce->name, &dtype) !=
-					!!(dir->flags & DIR_SHOW_IGNORED))
-				continue;
-			if (ce->ce_flags & CE_UPDATE)
-				continue;
-			err = lstat(ce->name, &st);
-			if (show_deleted && err)
-				show_ce_entry(tag_removed, ce);
-			if (show_modified && ce_modified(ce, &st, 0))
-				show_ce_entry(tag_modified, ce);
-		}
-	}
-}
-
-/*
- * Prune the index to only contain stuff starting with "prefix"
- */
-static void prune_cache(const char *prefix)
-{
-	int pos = cache_name_pos(prefix, prefix_len);
-	unsigned int first, last;
-
-	if (pos < 0)
-		pos = -pos-1;
-	memmove(active_cache, active_cache + pos,
-		(active_nr - pos) * sizeof(struct cache_entry *));
-	active_nr -= pos;
-	first = 0;
-	last = active_nr;
-	while (last > first) {
-		int next = (last + first) >> 1;
-		struct cache_entry *ce = active_cache[next];
-		if (!strncmp(ce->name, prefix, prefix_len)) {
-			first = next+1;
-			continue;
-		}
-		last = next;
-	}
-	active_nr = last;
-}
-
-static const char *verify_pathspec(const char *prefix)
-{
-	const char **p, *n, *prev;
-	unsigned long max;
-
-	prev = NULL;
-	max = PATH_MAX;
-	for (p = pathspec; (n = *p) != NULL; p++) {
-		int i, len = 0;
-		for (i = 0; i < max; i++) {
-			char c = n[i];
-			if (prev && prev[i] != c)
-				break;
-			if (!c || c == '*' || c == '?')
-				break;
-			if (c == '/')
-				len = i+1;
-		}
-		prev = n;
-		if (len < max) {
-			max = len;
-			if (!max)
-				break;
-		}
-	}
-
-	if (prefix_offset > max || memcmp(prev, prefix, prefix_offset))
-		die("git ls-files: cannot generate relative filenames containing '..'");
-
-	prefix_len = max;
-	return max ? xmemdupz(prev, max) : NULL;
-}
-
-static void strip_trailing_slash_from_submodules(void)
-{
-	const char **p;
-
-	for (p = pathspec; *p != NULL; p++) {
-		int len = strlen(*p), pos;
-
-		if (len < 1 || (*p)[len - 1] != '/')
-			continue;
-		pos = cache_name_pos(*p, len - 1);
-		if (pos >= 0 && S_ISGITLINK(active_cache[pos]->ce_mode))
-			*p = xstrndup(*p, len - 1);
-	}
-}
-
-/*
- * Read the tree specified with --with-tree option
- * (typically, HEAD) into stage #1 and then
- * squash them down to stage #0.  This is used for
- * --error-unmatch to list and check the path patterns
- * that were given from the command line.  We are not
- * going to write this index out.
- */
-void overlay_tree_on_cache(const char *tree_name, const char *prefix)
-{
-	struct tree *tree;
-	unsigned char sha1[20];
-	const char **match;
-	struct cache_entry *last_stage0 = NULL;
-	int i;
-
-	if (get_sha1(tree_name, sha1))
-		die("tree-ish %s not found.", tree_name);
-	tree = parse_tree_indirect(sha1);
-	if (!tree)
-		die("bad tree-ish %s", tree_name);
-
-	/* Hoist the unmerged entries up to stage #3 to make room */
-	for (i = 0; i < active_nr; i++) {
-		struct cache_entry *ce = active_cache[i];
-		if (!ce_stage(ce))
-			continue;
-		ce->ce_flags |= CE_STAGEMASK;
-	}
-
-	if (prefix) {
-		static const char *(matchbuf[2]);
-		matchbuf[0] = prefix;
-		matchbuf[1] = NULL;
-		match = matchbuf;
-	} else
-		match = NULL;
-	if (read_tree(tree, 1, match))
-		die("unable to read tree entries %s", tree_name);
-
-	for (i = 0; i < active_nr; i++) {
-		struct cache_entry *ce = active_cache[i];
-		switch (ce_stage(ce)) {
-		case 0:
-			last_stage0 = ce;
-			/* fallthru */
-		default:
-			continue;
-		case 1:
-			/*
-			 * If there is stage #0 entry for this, we do not
-			 * need to show it.  We use CE_UPDATE bit to mark
-			 * such an entry.
-			 */
-			if (last_stage0 &&
-			    !strcmp(last_stage0->name, ce->name))
-				ce->ce_flags |= CE_UPDATE;
-		}
-	}
-}
-
-int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset)
-{
-	/*
-	 * Make sure all pathspec matched; otherwise it is an error.
-	 */
-	int num, errors = 0;
-	for (num = 0; pathspec[num]; num++) {
-		int other, found_dup;
-
-		if (ps_matched[num])
-			continue;
-		/*
-		 * The caller might have fed identical pathspec
-		 * twice.  Do not barf on such a mistake.
-		 */
-		for (found_dup = other = 0;
-		     !found_dup && pathspec[other];
-		     other++) {
-			if (other == num || !ps_matched[other])
-				continue;
-			if (!strcmp(pathspec[other], pathspec[num]))
-				/*
-				 * Ok, we have a match already.
-				 */
-				found_dup = 1;
-		}
-		if (found_dup)
-			continue;
-
-		error("pathspec '%s' did not match any file(s) known to git.",
-		      pathspec[num] + prefix_offset);
-		errors++;
-	}
-	return errors;
-}
-
-static const char * const ls_files_usage[] = {
-	"git ls-files [options] [<file>]*",
-	NULL
-};
-
-static int option_parse_z(const struct option *opt,
-			  const char *arg, int unset)
-{
-	line_terminator = unset ? '\n' : '\0';
-
-	return 0;
-}
-
-static int option_parse_exclude(const struct option *opt,
-				const char *arg, int unset)
-{
-	struct exclude_list *list = opt->value;
-
-	exc_given = 1;
-	add_exclude(arg, "", 0, list);
-
-	return 0;
-}
-
-static int option_parse_exclude_from(const struct option *opt,
-				     const char *arg, int unset)
-{
-	struct dir_struct *dir = opt->value;
-
-	exc_given = 1;
-	add_excludes_from_file(dir, arg);
-
-	return 0;
-}
-
-static int option_parse_exclude_standard(const struct option *opt,
-					 const char *arg, int unset)
-{
-	struct dir_struct *dir = opt->value;
-
-	exc_given = 1;
-	setup_standard_excludes(dir);
-
-	return 0;
-}
-
-int cmd_ls_files(int argc, const char **argv, const char *prefix)
-{
-	int require_work_tree = 0, show_tag = 0;
-	struct dir_struct dir;
-	struct option builtin_ls_files_options[] = {
-		{ OPTION_CALLBACK, 'z', NULL, NULL, NULL,
-			"paths are separated with NUL character",
-			PARSE_OPT_NOARG, option_parse_z },
-		OPT_BOOLEAN('t', NULL, &show_tag,
-			"identify the file status with tags"),
-		OPT_BOOLEAN('v', NULL, &show_valid_bit,
-			"use lowercase letters for 'assume unchanged' files"),
-		OPT_BOOLEAN('c', "cached", &show_cached,
-			"show cached files in the output (default)"),
-		OPT_BOOLEAN('d', "deleted", &show_deleted,
-			"show deleted files in the output"),
-		OPT_BOOLEAN('m', "modified", &show_modified,
-			"show modified files in the output"),
-		OPT_BOOLEAN('o', "others", &show_others,
-			"show other files in the output"),
-		OPT_BIT('i', "ignored", &dir.flags,
-			"show ignored files in the output",
-			DIR_SHOW_IGNORED),
-		OPT_BOOLEAN('s', "stage", &show_stage,
-			"show staged contents' object name in the output"),
-		OPT_BOOLEAN('k', "killed", &show_killed,
-			"show files on the filesystem that need to be removed"),
-		OPT_BIT(0, "directory", &dir.flags,
-			"show 'other' directories' name only",
-			DIR_SHOW_OTHER_DIRECTORIES),
-		OPT_NEGBIT(0, "empty-directory", &dir.flags,
-			"don't show empty directories",
-			DIR_HIDE_EMPTY_DIRECTORIES),
-		OPT_BOOLEAN('u', "unmerged", &show_unmerged,
-			"show unmerged files in the output"),
-		{ OPTION_CALLBACK, 'x', "exclude", &dir.exclude_list[EXC_CMDL], "pattern",
-			"skip files matching pattern",
-			0, option_parse_exclude },
-		{ OPTION_CALLBACK, 'X', "exclude-from", &dir, "file",
-			"exclude patterns are read from <file>",
-			0, option_parse_exclude_from },
-		OPT_STRING(0, "exclude-per-directory", &dir.exclude_per_dir, "file",
-			"read additional per-directory exclude patterns in <file>"),
-		{ OPTION_CALLBACK, 0, "exclude-standard", &dir, NULL,
-			"add the standard git exclusions",
-			PARSE_OPT_NOARG, option_parse_exclude_standard },
-		{ OPTION_SET_INT, 0, "full-name", &prefix_offset, NULL,
-			"make the output relative to the project top directory",
-			PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL },
-		OPT_BOOLEAN(0, "error-unmatch", &error_unmatch,
-			"if any <file> is not in the index, treat this as an error"),
-		OPT_STRING(0, "with-tree", &with_tree, "tree-ish",
-			"pretend that paths removed since <tree-ish> are still present"),
-		OPT__ABBREV(&abbrev),
-		OPT_END()
-	};
-
-	memset(&dir, 0, sizeof(dir));
-	if (prefix)
-		prefix_offset = strlen(prefix);
-	git_config(git_default_config, NULL);
-
-	argc = parse_options(argc, argv, prefix, builtin_ls_files_options,
-			ls_files_usage, 0);
-	if (show_tag || show_valid_bit) {
-		tag_cached = "H ";
-		tag_unmerged = "M ";
-		tag_removed = "R ";
-		tag_modified = "C ";
-		tag_other = "? ";
-		tag_killed = "K ";
-	}
-	if (show_modified || show_others || show_deleted || (dir.flags & DIR_SHOW_IGNORED) || show_killed)
-		require_work_tree = 1;
-	if (show_unmerged)
-		/*
-		 * There's no point in showing unmerged unless
-		 * you also show the stage information.
-		 */
-		show_stage = 1;
-	if (dir.exclude_per_dir)
-		exc_given = 1;
-
-	if (require_work_tree && !is_inside_work_tree())
-		setup_work_tree();
-
-	pathspec = get_pathspec(prefix, argv);
-
-	/* be nice with submodule paths ending in a slash */
-	read_cache();
-	if (pathspec)
-		strip_trailing_slash_from_submodules();
-
-	/* Verify that the pathspec matches the prefix */
-	if (pathspec)
-		prefix = verify_pathspec(prefix);
-
-	/* Treat unmatching pathspec elements as errors */
-	if (pathspec && error_unmatch) {
-		int num;
-		for (num = 0; pathspec[num]; num++)
-			;
-		ps_matched = xcalloc(1, num);
-	}
-
-	if ((dir.flags & DIR_SHOW_IGNORED) && !exc_given) {
-		fprintf(stderr, "%s: --ignored needs some exclude pattern\n",
-			argv[0]);
-		exit(1);
-	}
-
-	/* With no flags, we default to showing the cached files */
-	if (!(show_stage | show_deleted | show_others | show_unmerged |
-	      show_killed | show_modified))
-		show_cached = 1;
-
-	if (prefix)
-		prune_cache(prefix);
-	if (with_tree) {
-		/*
-		 * Basic sanity check; show-stages and show-unmerged
-		 * would not make any sense with this option.
-		 */
-		if (show_stage || show_unmerged)
-			die("ls-files --with-tree is incompatible with -s or -u");
-		overlay_tree_on_cache(with_tree, prefix);
-	}
-	show_files(&dir, prefix);
-
-	if (ps_matched) {
-		int bad;
-		bad = report_path_error(ps_matched, pathspec, prefix_offset);
-		if (bad)
-			fprintf(stderr, "Did you forget to 'git add'?\n");
-
-		return bad ? 1 : 0;
-	}
-
-	return 0;
-}
diff --git a/builtin-ls-remote.c b/builtin-ls-remote.c
deleted file mode 100644
index 78a88f7..0000000
--- a/builtin-ls-remote.c
+++ /dev/null
@@ -1,107 +0,0 @@
-#include "builtin.h"
-#include "cache.h"
-#include "transport.h"
-#include "remote.h"
-
-static const char ls_remote_usage[] =
-"git ls-remote [--heads] [--tags]  [-u <exec> | --upload-pack <exec>] <repository> <refs>...";
-
-/*
- * Is there one among the list of patterns that match the tail part
- * of the path?
- */
-static int tail_match(const char **pattern, const char *path)
-{
-	const char *p;
-	char pathbuf[PATH_MAX];
-
-	if (!pattern)
-		return 1; /* no restriction */
-
-	if (snprintf(pathbuf, sizeof(pathbuf), "/%s", path) > sizeof(pathbuf))
-		return error("insanely long ref %.*s...", 20, path);
-	while ((p = *(pattern++)) != NULL) {
-		if (!fnmatch(p, pathbuf, 0))
-			return 1;
-	}
-	return 0;
-}
-
-int cmd_ls_remote(int argc, const char **argv, const char *prefix)
-{
-	int i;
-	const char *dest = NULL;
-	int nongit;
-	unsigned flags = 0;
-	const char *uploadpack = NULL;
-	const char **pattern = NULL;
-
-	struct remote *remote;
-	struct transport *transport;
-	const struct ref *ref;
-
-	setup_git_directory_gently(&nongit);
-
-	for (i = 1; i < argc; i++) {
-		const char *arg = argv[i];
-
-		if (*arg == '-') {
-			if (!prefixcmp(arg, "--upload-pack=")) {
-				uploadpack = arg + 14;
-				continue;
-			}
-			if (!prefixcmp(arg, "--exec=")) {
-				uploadpack = arg + 7;
-				continue;
-			}
-			if (!strcmp("--tags", arg) || !strcmp("-t", arg)) {
-				flags |= REF_TAGS;
-				continue;
-			}
-			if (!strcmp("--heads", arg) || !strcmp("-h", arg)) {
-				flags |= REF_HEADS;
-				continue;
-			}
-			if (!strcmp("--refs", arg)) {
-				flags |= REF_NORMAL;
-				continue;
-			}
-			usage(ls_remote_usage);
-		}
-		dest = arg;
-		i++;
-		break;
-	}
-
-	if (!dest)
-		usage(ls_remote_usage);
-
-	if (argv[i]) {
-		int j;
-		pattern = xcalloc(sizeof(const char *), argc - i + 1);
-		for (j = i; j < argc; j++) {
-			int len = strlen(argv[j]);
-			char *p = xmalloc(len + 3);
-			sprintf(p, "*/%s", argv[j]);
-			pattern[j - i] = p;
-		}
-	}
-	remote = nongit ? NULL : remote_get(dest);
-	if (remote && !remote->url_nr)
-		die("remote %s has no configured URL", dest);
-	transport = transport_get(remote, remote ? remote->url[0] : dest);
-	if (uploadpack != NULL)
-		transport_set_option(transport, TRANS_OPT_UPLOADPACK, uploadpack);
-
-	ref = transport_get_remote_refs(transport);
-	if (transport_disconnect(transport))
-		return 1;
-	for ( ; ref; ref = ref->next) {
-		if (!check_ref_type(ref, flags))
-			continue;
-		if (!tail_match(pattern, ref->name))
-			continue;
-		printf("%s	%s\n", sha1_to_hex(ref->old_sha1), ref->name);
-	}
-	return 0;
-}
diff --git a/builtin-ls-tree.c b/builtin-ls-tree.c
deleted file mode 100644
index 22008df..0000000
--- a/builtin-ls-tree.c
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * GIT - The information manager from hell
- *
- * Copyright (C) Linus Torvalds, 2005
- */
-#include "cache.h"
-#include "blob.h"
-#include "tree.h"
-#include "commit.h"
-#include "quote.h"
-#include "builtin.h"
-
-static int line_termination = '\n';
-#define LS_RECURSIVE 1
-#define LS_TREE_ONLY 2
-#define LS_SHOW_TREES 4
-#define LS_NAME_ONLY 8
-#define LS_SHOW_SIZE 16
-static int abbrev;
-static int ls_options;
-static const char **pathspec;
-static int chomp_prefix;
-static const char *ls_tree_prefix;
-
-static const char ls_tree_usage[] =
-	"git ls-tree [-d] [-r] [-t] [-l] [-z] [--name-only] [--name-status] [--full-name] [--full-tree] [--abbrev[=<n>]] <tree-ish> [path...]";
-
-static int show_recursive(const char *base, int baselen, const char *pathname)
-{
-	const char **s;
-
-	if (ls_options & LS_RECURSIVE)
-		return 1;
-
-	s = pathspec;
-	if (!s)
-		return 0;
-
-	for (;;) {
-		const char *spec = *s++;
-		int len, speclen;
-
-		if (!spec)
-			return 0;
-		if (strncmp(base, spec, baselen))
-			continue;
-		len = strlen(pathname);
-		spec += baselen;
-		speclen = strlen(spec);
-		if (speclen <= len)
-			continue;
-		if (memcmp(pathname, spec, len))
-			continue;
-		return 1;
-	}
-}
-
-static int show_tree(const unsigned char *sha1, const char *base, int baselen,
-		const char *pathname, unsigned mode, int stage, void *context)
-{
-	int retval = 0;
-	const char *type = blob_type;
-
-	if (S_ISGITLINK(mode)) {
-		/*
-		 * Maybe we want to have some recursive version here?
-		 *
-		 * Something similar to this incomplete example:
-		 *
-		if (show_subprojects(base, baselen, pathname))
-			retval = READ_TREE_RECURSIVE;
-		 *
-		 */
-		type = commit_type;
-	} else if (S_ISDIR(mode)) {
-		if (show_recursive(base, baselen, pathname)) {
-			retval = READ_TREE_RECURSIVE;
-			if (!(ls_options & LS_SHOW_TREES))
-				return retval;
-		}
-		type = tree_type;
-	}
-	else if (ls_options & LS_TREE_ONLY)
-		return 0;
-
-	if (chomp_prefix &&
-	    (baselen < chomp_prefix || memcmp(ls_tree_prefix, base, chomp_prefix)))
-		return 0;
-
-	if (!(ls_options & LS_NAME_ONLY)) {
-		if (ls_options & LS_SHOW_SIZE) {
-			char size_text[24];
-			if (!strcmp(type, blob_type)) {
-				unsigned long size;
-				if (sha1_object_info(sha1, &size) == OBJ_BAD)
-					strcpy(size_text, "BAD");
-				else
-					snprintf(size_text, sizeof(size_text),
-						 "%lu", size);
-			} else
-				strcpy(size_text, "-");
-			printf("%06o %s %s %7s\t", mode, type,
-			       abbrev ? find_unique_abbrev(sha1, abbrev)
-				      : sha1_to_hex(sha1),
-			       size_text);
-		} else
-			printf("%06o %s %s\t", mode, type,
-			       abbrev ? find_unique_abbrev(sha1, abbrev)
-			              : sha1_to_hex(sha1));
-	}
-	write_name_quotedpfx(base + chomp_prefix, baselen - chomp_prefix,
-			  pathname, stdout, line_termination);
-	return retval;
-}
-
-int cmd_ls_tree(int argc, const char **argv, const char *prefix)
-{
-	unsigned char sha1[20];
-	struct tree *tree;
-
-	git_config(git_default_config, NULL);
-	ls_tree_prefix = prefix;
-	if (prefix && *prefix)
-		chomp_prefix = strlen(prefix);
-	while (1 < argc && argv[1][0] == '-') {
-		switch (argv[1][1]) {
-		case 'z':
-			line_termination = 0;
-			break;
-		case 'r':
-			ls_options |= LS_RECURSIVE;
-			break;
-		case 'd':
-			ls_options |= LS_TREE_ONLY;
-			break;
-		case 't':
-			ls_options |= LS_SHOW_TREES;
-			break;
-		case 'l':
-			ls_options |= LS_SHOW_SIZE;
-			break;
-		case '-':
-			if (!strcmp(argv[1]+2, "name-only") ||
-			    !strcmp(argv[1]+2, "name-status")) {
-				ls_options |= LS_NAME_ONLY;
-				break;
-			}
-			if (!strcmp(argv[1]+2, "long")) {
-				ls_options |= LS_SHOW_SIZE;
-				break;
-			}
-			if (!strcmp(argv[1]+2, "full-name")) {
-				chomp_prefix = 0;
-				break;
-			}
-			if (!strcmp(argv[1]+2, "full-tree")) {
-				ls_tree_prefix = prefix = NULL;
-				chomp_prefix = 0;
-				break;
-			}
-			if (!prefixcmp(argv[1]+2, "abbrev=")) {
-				abbrev = strtoul(argv[1]+9, NULL, 10);
-				if (abbrev && abbrev < MINIMUM_ABBREV)
-					abbrev = MINIMUM_ABBREV;
-				else if (abbrev > 40)
-					abbrev = 40;
-				break;
-			}
-			if (!strcmp(argv[1]+2, "abbrev")) {
-				abbrev = DEFAULT_ABBREV;
-				break;
-			}
-			/* otherwise fallthru */
-		default:
-			usage(ls_tree_usage);
-		}
-		argc--; argv++;
-	}
-	/* -d -r should imply -t, but -d by itself should not have to. */
-	if ( (LS_TREE_ONLY|LS_RECURSIVE) ==
-	    ((LS_TREE_ONLY|LS_RECURSIVE) & ls_options))
-		ls_options |= LS_SHOW_TREES;
-
-	if (argc < 2)
-		usage(ls_tree_usage);
-	if (get_sha1(argv[1], sha1))
-		die("Not a valid object name %s", argv[1]);
-
-	pathspec = get_pathspec(prefix, argv + 2);
-	tree = parse_tree_indirect(sha1);
-	if (!tree)
-		die("not a tree object");
-	read_tree_recursive(tree, "", 0, 0, pathspec, show_tree, NULL);
-
-	return 0;
-}
diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
deleted file mode 100644
index 92637ac..0000000
--- a/builtin-mailinfo.c
+++ /dev/null
@@ -1,966 +0,0 @@
-/*
- * Another stupid program, this one parsing the headers of an
- * email to figure out authorship and subject
- */
-#include "cache.h"
-#include "builtin.h"
-#include "utf8.h"
-#include "strbuf.h"
-
-static FILE *cmitmsg, *patchfile, *fin, *fout;
-
-static int keep_subject;
-static const char *metainfo_charset;
-static struct strbuf line = STRBUF_INIT;
-static struct strbuf name = STRBUF_INIT;
-static struct strbuf email = STRBUF_INIT;
-
-static enum  {
-	TE_DONTCARE, TE_QP, TE_BASE64,
-} transfer_encoding;
-static enum  {
-	TYPE_TEXT, TYPE_OTHER,
-} message_type;
-
-static struct strbuf charset = STRBUF_INIT;
-static int patch_lines;
-static struct strbuf **p_hdr_data, **s_hdr_data;
-
-#define MAX_HDR_PARSED 10
-#define MAX_BOUNDARIES 5
-
-static void cleanup_space(struct strbuf *sb);
-
-
-static void get_sane_name(struct strbuf *out, struct strbuf *name, struct strbuf *email)
-{
-	struct strbuf *src = name;
-	if (name->len < 3 || 60 < name->len || strchr(name->buf, '@') ||
-		strchr(name->buf, '<') || strchr(name->buf, '>'))
-		src = email;
-	else if (name == out)
-		return;
-	strbuf_reset(out);
-	strbuf_addbuf(out, src);
-}
-
-static void parse_bogus_from(const struct strbuf *line)
-{
-	/* John Doe <johndoe> */
-
-	char *bra, *ket;
-	/* This is fallback, so do not bother if we already have an
-	 * e-mail address.
-	 */
-	if (email.len)
-		return;
-
-	bra = strchr(line->buf, '<');
-	if (!bra)
-		return;
-	ket = strchr(bra, '>');
-	if (!ket)
-		return;
-
-	strbuf_reset(&email);
-	strbuf_add(&email, bra + 1, ket - bra - 1);
-
-	strbuf_reset(&name);
-	strbuf_add(&name, line->buf, bra - line->buf);
-	strbuf_trim(&name);
-	get_sane_name(&name, &name, &email);
-}
-
-static void handle_from(const struct strbuf *from)
-{
-	char *at;
-	size_t el;
-	struct strbuf f;
-
-	strbuf_init(&f, from->len);
-	strbuf_addbuf(&f, from);
-
-	at = strchr(f.buf, '@');
-	if (!at) {
-		parse_bogus_from(from);
-		return;
-	}
-
-	/*
-	 * If we already have one email, don't take any confusing lines
-	 */
-	if (email.len && strchr(at + 1, '@')) {
-		strbuf_release(&f);
-		return;
-	}
-
-	/* Pick up the string around '@', possibly delimited with <>
-	 * pair; that is the email part.
-	 */
-	while (at > f.buf) {
-		char c = at[-1];
-		if (isspace(c))
-			break;
-		if (c == '<') {
-			at[-1] = ' ';
-			break;
-		}
-		at--;
-	}
-	el = strcspn(at, " \n\t\r\v\f>");
-	strbuf_reset(&email);
-	strbuf_add(&email, at, el);
-	strbuf_remove(&f, at - f.buf, el + (at[el] ? 1 : 0));
-
-	/* The remainder is name.  It could be
-	 *
-	 * - "John Doe <john.doe@xz>"			(a), or
-	 * - "john.doe@xz (John Doe)"			(b), or
-	 * - "John (zzz) Doe <john.doe@xz> (Comment)"	(c)
-	 *
-	 * but we have removed the email part, so
-	 *
-	 * - remove extra spaces which could stay after email (case 'c'), and
-	 * - trim from both ends, possibly removing the () pair at the end
-	 *   (cases 'a' and 'b').
-	 */
-	cleanup_space(&f);
-	strbuf_trim(&f);
-	if (f.buf[0] == '(' && f.len && f.buf[f.len - 1] == ')') {
-		strbuf_remove(&f, 0, 1);
-		strbuf_setlen(&f, f.len - 1);
-	}
-
-	get_sane_name(&name, &f, &email);
-	strbuf_release(&f);
-}
-
-static void handle_header(struct strbuf **out, const struct strbuf *line)
-{
-	if (!*out) {
-		*out = xmalloc(sizeof(struct strbuf));
-		strbuf_init(*out, line->len);
-	} else
-		strbuf_reset(*out);
-
-	strbuf_addbuf(*out, line);
-}
-
-/* NOTE NOTE NOTE.  We do not claim we do full MIME.  We just attempt
- * to have enough heuristics to grok MIME encoded patches often found
- * on our mailing lists.  For example, we do not even treat header lines
- * case insensitively.
- */
-
-static int slurp_attr(const char *line, const char *name, struct strbuf *attr)
-{
-	const char *ends, *ap = strcasestr(line, name);
-	size_t sz;
-
-	if (!ap) {
-		strbuf_setlen(attr, 0);
-		return 0;
-	}
-	ap += strlen(name);
-	if (*ap == '"') {
-		ap++;
-		ends = "\"";
-	}
-	else
-		ends = "; \t";
-	sz = strcspn(ap, ends);
-	strbuf_add(attr, ap, sz);
-	return 1;
-}
-
-static struct strbuf *content[MAX_BOUNDARIES];
-
-static struct strbuf **content_top = content;
-
-static void handle_content_type(struct strbuf *line)
-{
-	struct strbuf *boundary = xmalloc(sizeof(struct strbuf));
-	strbuf_init(boundary, line->len);
-
-	if (!strcasestr(line->buf, "text/"))
-		 message_type = TYPE_OTHER;
-	if (slurp_attr(line->buf, "boundary=", boundary)) {
-		strbuf_insert(boundary, 0, "--", 2);
-		if (++content_top > &content[MAX_BOUNDARIES]) {
-			fprintf(stderr, "Too many boundaries to handle\n");
-			exit(1);
-		}
-		*content_top = boundary;
-		boundary = NULL;
-	}
-	slurp_attr(line->buf, "charset=", &charset);
-
-	if (boundary) {
-		strbuf_release(boundary);
-		free(boundary);
-	}
-}
-
-static void handle_content_transfer_encoding(const struct strbuf *line)
-{
-	if (strcasestr(line->buf, "base64"))
-		transfer_encoding = TE_BASE64;
-	else if (strcasestr(line->buf, "quoted-printable"))
-		transfer_encoding = TE_QP;
-	else
-		transfer_encoding = TE_DONTCARE;
-}
-
-static int is_multipart_boundary(const struct strbuf *line)
-{
-	return (((*content_top)->len <= line->len) &&
-		!memcmp(line->buf, (*content_top)->buf, (*content_top)->len));
-}
-
-static void cleanup_subject(struct strbuf *subject)
-{
-	char *pos;
-	size_t remove;
-	while (subject->len) {
-		switch (*subject->buf) {
-		case 'r': case 'R':
-			if (subject->len <= 3)
-				break;
-			if (!memcmp(subject->buf + 1, "e:", 2)) {
-				strbuf_remove(subject, 0, 3);
-				continue;
-			}
-			break;
-		case ' ': case '\t': case ':':
-			strbuf_remove(subject, 0, 1);
-			continue;
-		case '[':
-			if ((pos = strchr(subject->buf, ']'))) {
-				remove = pos - subject->buf;
-				if (remove <= (subject->len - remove) * 2) {
-					strbuf_remove(subject, 0, remove + 1);
-					continue;
-				}
-			} else
-				strbuf_remove(subject, 0, 1);
-			break;
-		}
-		strbuf_trim(subject);
-		return;
-	}
-}
-
-static void cleanup_space(struct strbuf *sb)
-{
-	size_t pos, cnt;
-	for (pos = 0; pos < sb->len; pos++) {
-		if (isspace(sb->buf[pos])) {
-			sb->buf[pos] = ' ';
-			for (cnt = 0; isspace(sb->buf[pos + cnt + 1]); cnt++);
-			strbuf_remove(sb, pos + 1, cnt);
-		}
-	}
-}
-
-static void decode_header(struct strbuf *line);
-static const char *header[MAX_HDR_PARSED] = {
-	"From","Subject","Date",
-};
-
-static inline int cmp_header(const struct strbuf *line, const char *hdr)
-{
-	int len = strlen(hdr);
-	return !strncasecmp(line->buf, hdr, len) && line->len > len &&
-			line->buf[len] == ':' && isspace(line->buf[len + 1]);
-}
-
-static int check_header(const struct strbuf *line,
-				struct strbuf *hdr_data[], int overwrite)
-{
-	int i, ret = 0, len;
-	struct strbuf sb = STRBUF_INIT;
-	/* search for the interesting parts */
-	for (i = 0; header[i]; i++) {
-		int len = strlen(header[i]);
-		if ((!hdr_data[i] || overwrite) && cmp_header(line, header[i])) {
-			/* Unwrap inline B and Q encoding, and optionally
-			 * normalize the meta information to utf8.
-			 */
-			strbuf_add(&sb, line->buf + len + 2, line->len - len - 2);
-			decode_header(&sb);
-			handle_header(&hdr_data[i], &sb);
-			ret = 1;
-			goto check_header_out;
-		}
-	}
-
-	/* Content stuff */
-	if (cmp_header(line, "Content-Type")) {
-		len = strlen("Content-Type: ");
-		strbuf_add(&sb, line->buf + len, line->len - len);
-		decode_header(&sb);
-		strbuf_insert(&sb, 0, "Content-Type: ", len);
-		handle_content_type(&sb);
-		ret = 1;
-		goto check_header_out;
-	}
-	if (cmp_header(line, "Content-Transfer-Encoding")) {
-		len = strlen("Content-Transfer-Encoding: ");
-		strbuf_add(&sb, line->buf + len, line->len - len);
-		decode_header(&sb);
-		handle_content_transfer_encoding(&sb);
-		ret = 1;
-		goto check_header_out;
-	}
-
-	/* for inbody stuff */
-	if (!prefixcmp(line->buf, ">From") && isspace(line->buf[5])) {
-		ret = 1; /* Should this return 0? */
-		goto check_header_out;
-	}
-	if (!prefixcmp(line->buf, "[PATCH]") && isspace(line->buf[7])) {
-		for (i = 0; header[i]; i++) {
-			if (!memcmp("Subject", header[i], 7)) {
-				handle_header(&hdr_data[i], line);
-				ret = 1;
-				goto check_header_out;
-			}
-		}
-	}
-
-check_header_out:
-	strbuf_release(&sb);
-	return ret;
-}
-
-static int is_rfc2822_header(const struct strbuf *line)
-{
-	/*
-	 * The section that defines the loosest possible
-	 * field name is "3.6.8 Optional fields".
-	 *
-	 * optional-field = field-name ":" unstructured CRLF
-	 * field-name = 1*ftext
-	 * ftext = %d33-57 / %59-126
-	 */
-	int ch;
-	char *cp = line->buf;
-
-	/* Count mbox From headers as headers */
-	if (!prefixcmp(cp, "From ") || !prefixcmp(cp, ">From "))
-		return 1;
-
-	while ((ch = *cp++)) {
-		if (ch == ':')
-			return 1;
-		if ((33 <= ch && ch <= 57) ||
-		    (59 <= ch && ch <= 126))
-			continue;
-		break;
-	}
-	return 0;
-}
-
-static int read_one_header_line(struct strbuf *line, FILE *in)
-{
-	/* Get the first part of the line. */
-	if (strbuf_getline(line, in, '\n'))
-		return 0;
-
-	/*
-	 * Is it an empty line or not a valid rfc2822 header?
-	 * If so, stop here, and return false ("not a header")
-	 */
-	strbuf_rtrim(line);
-	if (!line->len || !is_rfc2822_header(line)) {
-		/* Re-add the newline */
-		strbuf_addch(line, '\n');
-		return 0;
-	}
-
-	/*
-	 * Now we need to eat all the continuation lines..
-	 * Yuck, 2822 header "folding"
-	 */
-	for (;;) {
-		int peek;
-		struct strbuf continuation = STRBUF_INIT;
-
-		peek = fgetc(in); ungetc(peek, in);
-		if (peek != ' ' && peek != '\t')
-			break;
-		if (strbuf_getline(&continuation, in, '\n'))
-			break;
-		continuation.buf[0] = '\n';
-		strbuf_rtrim(&continuation);
-		strbuf_addbuf(line, &continuation);
-	}
-
-	return 1;
-}
-
-static struct strbuf *decode_q_segment(const struct strbuf *q_seg, int rfc2047)
-{
-	const char *in = q_seg->buf;
-	int c;
-	struct strbuf *out = xmalloc(sizeof(struct strbuf));
-	strbuf_init(out, q_seg->len);
-
-	while ((c = *in++) != 0) {
-		if (c == '=') {
-			int d = *in++;
-			if (d == '\n' || !d)
-				break; /* drop trailing newline */
-			strbuf_addch(out, (hexval(d) << 4) | hexval(*in++));
-			continue;
-		}
-		if (rfc2047 && c == '_') /* rfc2047 4.2 (2) */
-			c = 0x20;
-		strbuf_addch(out, c);
-	}
-	return out;
-}
-
-static struct strbuf *decode_b_segment(const struct strbuf *b_seg)
-{
-	/* Decode in..ep, possibly in-place to ot */
-	int c, pos = 0, acc = 0;
-	const char *in = b_seg->buf;
-	struct strbuf *out = xmalloc(sizeof(struct strbuf));
-	strbuf_init(out, b_seg->len);
-
-	while ((c = *in++) != 0) {
-		if (c == '+')
-			c = 62;
-		else if (c == '/')
-			c = 63;
-		else if ('A' <= c && c <= 'Z')
-			c -= 'A';
-		else if ('a' <= c && c <= 'z')
-			c -= 'a' - 26;
-		else if ('0' <= c && c <= '9')
-			c -= '0' - 52;
-		else
-			continue; /* garbage */
-		switch (pos++) {
-		case 0:
-			acc = (c << 2);
-			break;
-		case 1:
-			strbuf_addch(out, (acc | (c >> 4)));
-			acc = (c & 15) << 4;
-			break;
-		case 2:
-			strbuf_addch(out, (acc | (c >> 2)));
-			acc = (c & 3) << 6;
-			break;
-		case 3:
-			strbuf_addch(out, (acc | c));
-			acc = pos = 0;
-			break;
-		}
-	}
-	return out;
-}
-
-/*
- * When there is no known charset, guess.
- *
- * Right now we assume that if the target is UTF-8 (the default),
- * and it already looks like UTF-8 (which includes US-ASCII as its
- * subset, of course) then that is what it is and there is nothing
- * to do.
- *
- * Otherwise, we default to assuming it is Latin1 for historical
- * reasons.
- */
-static const char *guess_charset(const struct strbuf *line, const char *target_charset)
-{
-	if (is_encoding_utf8(target_charset)) {
-		if (is_utf8(line->buf))
-			return NULL;
-	}
-	return "ISO8859-1";
-}
-
-static void convert_to_utf8(struct strbuf *line, const char *charset)
-{
-	char *out;
-
-	if (!charset || !*charset) {
-		charset = guess_charset(line, metainfo_charset);
-		if (!charset)
-			return;
-	}
-
-	if (!strcasecmp(metainfo_charset, charset))
-		return;
-	out = reencode_string(line->buf, metainfo_charset, charset);
-	if (!out)
-		die("cannot convert from %s to %s",
-		    charset, metainfo_charset);
-	strbuf_attach(line, out, strlen(out), strlen(out));
-}
-
-static int decode_header_bq(struct strbuf *it)
-{
-	char *in, *ep, *cp;
-	struct strbuf outbuf = STRBUF_INIT, *dec;
-	struct strbuf charset_q = STRBUF_INIT, piecebuf = STRBUF_INIT;
-	int rfc2047 = 0;
-
-	in = it->buf;
-	while (in - it->buf <= it->len && (ep = strstr(in, "=?")) != NULL) {
-		int encoding;
-		strbuf_reset(&charset_q);
-		strbuf_reset(&piecebuf);
-		rfc2047 = 1;
-
-		if (in != ep) {
-			/*
-			 * We are about to process an encoded-word
-			 * that begins at ep, but there is something
-			 * before the encoded word.
-			 */
-			char *scan;
-			for (scan = in; scan < ep; scan++)
-				if (!isspace(*scan))
-					break;
-
-			if (scan != ep || in == it->buf) {
-				/*
-				 * We should not lose that "something",
-				 * unless we have just processed an
-				 * encoded-word, and there is only LWS
-				 * before the one we are about to process.
-				 */
-				strbuf_add(&outbuf, in, ep - in);
-			}
-		}
-		/* E.g.
-		 * ep : "=?iso-2022-jp?B?GyR...?= foo"
-		 * ep : "=?ISO-8859-1?Q?Foo=FCbar?= baz"
-		 */
-		ep += 2;
-
-		if (ep - it->buf >= it->len || !(cp = strchr(ep, '?')))
-			goto decode_header_bq_out;
-
-		if (cp + 3 - it->buf > it->len)
-			goto decode_header_bq_out;
-		strbuf_add(&charset_q, ep, cp - ep);
-
-		encoding = cp[1];
-		if (!encoding || cp[2] != '?')
-			goto decode_header_bq_out;
-		ep = strstr(cp + 3, "?=");
-		if (!ep)
-			goto decode_header_bq_out;
-		strbuf_add(&piecebuf, cp + 3, ep - cp - 3);
-		switch (tolower(encoding)) {
-		default:
-			goto decode_header_bq_out;
-		case 'b':
-			dec = decode_b_segment(&piecebuf);
-			break;
-		case 'q':
-			dec = decode_q_segment(&piecebuf, 1);
-			break;
-		}
-		if (metainfo_charset)
-			convert_to_utf8(dec, charset_q.buf);
-
-		strbuf_addbuf(&outbuf, dec);
-		strbuf_release(dec);
-		free(dec);
-		in = ep + 2;
-	}
-	strbuf_addstr(&outbuf, in);
-	strbuf_reset(it);
-	strbuf_addbuf(it, &outbuf);
-decode_header_bq_out:
-	strbuf_release(&outbuf);
-	strbuf_release(&charset_q);
-	strbuf_release(&piecebuf);
-	return rfc2047;
-}
-
-static void decode_header(struct strbuf *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(struct strbuf *line)
-{
-	struct strbuf *ret;
-
-	switch (transfer_encoding) {
-	case TE_QP:
-		ret = decode_q_segment(line, 0);
-		break;
-	case TE_BASE64:
-		ret = decode_b_segment(line);
-		break;
-	case TE_DONTCARE:
-	default:
-		return;
-	}
-	strbuf_reset(line);
-	strbuf_addbuf(line, ret);
-	strbuf_release(ret);
-	free(ret);
-}
-
-static void handle_filter(struct strbuf *line);
-
-static int find_boundary(void)
-{
-	while (!strbuf_getline(&line, fin, '\n')) {
-		if (*content_top && is_multipart_boundary(&line))
-			return 1;
-	}
-	return 0;
-}
-
-static int handle_boundary(void)
-{
-	struct strbuf newline = STRBUF_INIT;
-
-	strbuf_addch(&newline, '\n');
-again:
-	if (line.len >= (*content_top)->len + 2 &&
-	    !memcmp(line.buf + (*content_top)->len, "--", 2)) {
-		/* we hit an end boundary */
-		/* pop the current boundary off the stack */
-		strbuf_release(*content_top);
-		free(*content_top);
-		*content_top = NULL;
-
-		/* technically won't happen as is_multipart_boundary()
-		   will fail first.  But just in case..
-		 */
-		if (--content_top < content) {
-			fprintf(stderr, "Detected mismatched boundaries, "
-					"can't recover\n");
-			exit(1);
-		}
-		handle_filter(&newline);
-		strbuf_release(&newline);
-
-		/* skip to the next boundary */
-		if (!find_boundary())
-			return 0;
-		goto again;
-	}
-
-	/* set some defaults */
-	transfer_encoding = TE_DONTCARE;
-	strbuf_reset(&charset);
-	message_type = TYPE_TEXT;
-
-	/* slurp in this section's info */
-	while (read_one_header_line(&line, fin))
-		check_header(&line, p_hdr_data, 0);
-
-	strbuf_release(&newline);
-	/* replenish line */
-	if (strbuf_getline(&line, fin, '\n'))
-		return 0;
-	strbuf_addch(&line, '\n');
-	return 1;
-}
-
-static inline int patchbreak(const struct strbuf *line)
-{
-	size_t i;
-
-	/* Beginning of a "diff -" header? */
-	if (!prefixcmp(line->buf, "diff -"))
-		return 1;
-
-	/* CVS "Index: " line? */
-	if (!prefixcmp(line->buf, "Index: "))
-		return 1;
-
-	/*
-	 * "--- <filename>" starts patches without headers
-	 * "---<sp>*" is a manual separator
-	 */
-	if (line->len < 4)
-		return 0;
-
-	if (!prefixcmp(line->buf, "---")) {
-		/* space followed by a filename? */
-		if (line->buf[3] == ' ' && !isspace(line->buf[4]))
-			return 1;
-		/* Just whitespace? */
-		for (i = 3; i < line->len; i++) {
-			unsigned char c = line->buf[i];
-			if (c == '\n')
-				return 1;
-			if (!isspace(c))
-				break;
-		}
-		return 0;
-	}
-	return 0;
-}
-
-static int handle_commit_msg(struct strbuf *line)
-{
-	static int still_looking = 1;
-
-	if (!cmitmsg)
-		return 0;
-
-	if (still_looking) {
-		strbuf_ltrim(line);
-		if (!line->len)
-			return 0;
-		if ((still_looking = check_header(line, s_hdr_data, 0)) != 0)
-			return 0;
-	}
-
-	/* normalize the log message to UTF-8. */
-	if (metainfo_charset)
-		convert_to_utf8(line, charset.buf);
-
-	if (patchbreak(line)) {
-		fclose(cmitmsg);
-		cmitmsg = NULL;
-		return 1;
-	}
-
-	fputs(line->buf, cmitmsg);
-	return 0;
-}
-
-static void handle_patch(const struct strbuf *line)
-{
-	fwrite(line->buf, 1, line->len, patchfile);
-	patch_lines++;
-}
-
-static void handle_filter(struct strbuf *line)
-{
-	static int filter = 0;
-
-	/* filter tells us which part we left off on */
-	switch (filter) {
-	case 0:
-		if (!handle_commit_msg(line))
-			break;
-		filter++;
-	case 1:
-		handle_patch(line);
-		break;
-	}
-}
-
-static void handle_body(void)
-{
-	int len = 0;
-	struct strbuf prev = STRBUF_INIT;
-
-	/* Skip up to the first boundary */
-	if (*content_top) {
-		if (!find_boundary())
-			goto handle_body_out;
-	}
-
-	do {
-		strbuf_setlen(&line, line.len + len);
-
-		/* process any boundary lines */
-		if (*content_top && is_multipart_boundary(&line)) {
-			/* flush any leftover */
-			if (prev.len) {
-				handle_filter(&prev);
-				strbuf_reset(&prev);
-			}
-			if (!handle_boundary())
-				goto handle_body_out;
-		}
-
-		/* Unwrap transfer encoding */
-		decode_transfer_encoding(&line);
-
-		switch (transfer_encoding) {
-		case TE_BASE64:
-		case TE_QP:
-		{
-			struct strbuf **lines, **it, *sb;
-
-			/* Prepend any previous partial lines */
-			strbuf_insert(&line, 0, prev.buf, prev.len);
-			strbuf_reset(&prev);
-
-			/* binary data most likely doesn't have newlines */
-			if (message_type != TYPE_TEXT) {
-				handle_filter(&line);
-				break;
-			}
-			/*
-			 * This is a decoded line that may contain
-			 * multiple new lines.  Pass only one chunk
-			 * at a time to handle_filter()
-			 */
-			lines = strbuf_split(&line, '\n');
-			for (it = lines; (sb = *it); it++) {
-				if (*(it + 1) == NULL) /* The last line */
-					if (sb->buf[sb->len - 1] != '\n') {
-						/* Partial line, save it for later. */
-						strbuf_addbuf(&prev, sb);
-						break;
-					}
-				handle_filter(sb);
-			}
-			/*
-			 * The partial chunk is saved in "prev" and will be
-			 * appended by the next iteration of read_line_with_nul().
-			 */
-			strbuf_list_free(lines);
-			break;
-		}
-		default:
-			handle_filter(&line);
-		}
-
-		strbuf_reset(&line);
-		if (strbuf_avail(&line) < 100)
-			strbuf_grow(&line, 100);
-	} while ((len = read_line_with_nul(line.buf, strbuf_avail(&line), fin)));
-
-handle_body_out:
-	strbuf_release(&prev);
-}
-
-static void output_header_lines(FILE *fout, const char *hdr, const struct strbuf *data)
-{
-	const char *sp = data->buf;
-	while (1) {
-		char *ep = strchr(sp, '\n');
-		int len;
-		if (!ep)
-			len = strlen(sp);
-		else
-			len = ep - sp;
-		fprintf(fout, "%s: %.*s\n", hdr, len, sp);
-		if (!ep)
-			break;
-		sp = ep + 1;
-	}
-}
-
-static void handle_info(void)
-{
-	struct strbuf *hdr;
-	int i;
-
-	for (i = 0; header[i]; i++) {
-		/* only print inbody headers if we output a patch file */
-		if (patch_lines && s_hdr_data[i])
-			hdr = s_hdr_data[i];
-		else if (p_hdr_data[i])
-			hdr = p_hdr_data[i];
-		else
-			continue;
-
-		if (!memcmp(header[i], "Subject", 7)) {
-			if (!keep_subject) {
-				cleanup_subject(hdr);
-				cleanup_space(hdr);
-			}
-			output_header_lines(fout, "Subject", hdr);
-		} else if (!memcmp(header[i], "From", 4)) {
-			cleanup_space(hdr);
-			handle_from(hdr);
-			fprintf(fout, "Author: %s\n", name.buf);
-			fprintf(fout, "Email: %s\n", email.buf);
-		} else {
-			cleanup_space(hdr);
-			fprintf(fout, "%s: %s\n", header[i], hdr->buf);
-		}
-	}
-	fprintf(fout, "\n");
-}
-
-static int mailinfo(FILE *in, FILE *out, int ks, const char *encoding,
-		    const char *msg, const char *patch)
-{
-	int peek;
-	keep_subject = ks;
-	metainfo_charset = encoding;
-	fin = in;
-	fout = out;
-
-	cmitmsg = fopen(msg, "w");
-	if (!cmitmsg) {
-		perror(msg);
-		return -1;
-	}
-	patchfile = fopen(patch, "w");
-	if (!patchfile) {
-		perror(patch);
-		fclose(cmitmsg);
-		return -1;
-	}
-
-	p_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*p_hdr_data));
-	s_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*s_hdr_data));
-
-	do {
-		peek = fgetc(in);
-	} while (isspace(peek));
-	ungetc(peek, in);
-
-	/* process the email header */
-	while (read_one_header_line(&line, fin))
-		check_header(&line, p_hdr_data, 1);
-
-	handle_body();
-	handle_info();
-
-	return 0;
-}
-
-static const char mailinfo_usage[] =
-	"git mailinfo [-k] [-u | --encoding=<encoding> | -n] msg patch <mail >info";
-
-int cmd_mailinfo(int argc, const char **argv, const char *prefix)
-{
-	const char *def_charset;
-
-	/* NEEDSWORK: might want to do the optional .git/ directory
-	 * discovery
-	 */
-	git_config(git_default_config, NULL);
-
-	def_charset = (git_commit_encoding ? git_commit_encoding : "UTF-8");
-	metainfo_charset = def_charset;
-
-	while (1 < argc && argv[1][0] == '-') {
-		if (!strcmp(argv[1], "-k"))
-			keep_subject = 1;
-		else if (!strcmp(argv[1], "-u"))
-			metainfo_charset = def_charset;
-		else if (!strcmp(argv[1], "-n"))
-			metainfo_charset = NULL;
-		else if (!prefixcmp(argv[1], "--encoding="))
-			metainfo_charset = argv[1] + 11;
-		else
-			usage(mailinfo_usage);
-		argc--; argv++;
-	}
-
-	if (argc != 3)
-		usage(mailinfo_usage);
-
-	return !!mailinfo(stdin, stdout, keep_subject, metainfo_charset, argv[1], argv[2]);
-}
diff --git a/builtin-mailsplit.c b/builtin-mailsplit.c
deleted file mode 100644
index ad5f6b5..0000000
--- a/builtin-mailsplit.c
+++ /dev/null
@@ -1,320 +0,0 @@
-/*
- * Totally braindamaged mbox splitter program.
- *
- * It just splits a mbox into a list of files: "0001" "0002" ..
- * so you can process them further from there.
- */
-#include "cache.h"
-#include "builtin.h"
-#include "string-list.h"
-
-static const char git_mailsplit_usage[] =
-"git mailsplit [-d<prec>] [-f<n>] [-b] -o<directory> [<mbox>|<Maildir>...]";
-
-static int is_from_line(const char *line, int len)
-{
-	const char *colon;
-
-	if (len < 20 || memcmp("From ", line, 5))
-		return 0;
-
-	colon = line + len - 2;
-	line += 5;
-	for (;;) {
-		if (colon < line)
-			return 0;
-		if (*--colon == ':')
-			break;
-	}
-
-	if (!isdigit(colon[-4]) ||
-	    !isdigit(colon[-2]) ||
-	    !isdigit(colon[-1]) ||
-	    !isdigit(colon[ 1]) ||
-	    !isdigit(colon[ 2]))
-		return 0;
-
-	/* year */
-	if (strtol(colon+3, NULL, 10) <= 90)
-		return 0;
-
-	/* Ok, close enough */
-	return 1;
-}
-
-/* Could be as small as 64, enough to hold a Unix "From " line. */
-static char buf[4096];
-
-/* We cannot use fgets() because our lines can contain NULs */
-int read_line_with_nul(char *buf, int size, FILE *in)
-{
-	int len = 0, c;
-
-	for (;;) {
-		c = getc(in);
-		if (c == EOF)
-			break;
-		buf[len++] = c;
-		if (c == '\n' || len + 1 >= size)
-			break;
-	}
-	buf[len] = '\0';
-
-	return len;
-}
-
-/* Called with the first line (potentially partial)
- * already in buf[] -- normally that should begin with
- * the Unix "From " line.  Write it into the specified
- * file.
- */
-static int split_one(FILE *mbox, const char *name, int allow_bare)
-{
-	FILE *output = NULL;
-	int len = strlen(buf);
-	int fd;
-	int status = 0;
-	int is_bare = !is_from_line(buf, len);
-
-	if (is_bare && !allow_bare)
-		goto corrupt;
-
-	fd = open(name, O_WRONLY | O_CREAT | O_EXCL, 0666);
-	if (fd < 0)
-		die_errno("cannot open output file '%s'", name);
-	output = fdopen(fd, "w");
-
-	/* Copy it out, while searching for a line that begins with
-	 * "From " and having something that looks like a date format.
-	 */
-	for (;;) {
-		int is_partial = len && buf[len-1] != '\n';
-
-		if (fwrite(buf, 1, len, output) != len)
-			die_errno("cannot write output");
-
-		len = read_line_with_nul(buf, sizeof(buf), mbox);
-		if (len == 0) {
-			if (feof(mbox)) {
-				status = 1;
-				break;
-			}
-			die_errno("cannot read mbox");
-		}
-		if (!is_partial && !is_bare && is_from_line(buf, len))
-			break; /* done with one message */
-	}
-	fclose(output);
-	return status;
-
- corrupt:
-	if (output)
-		fclose(output);
-	unlink(name);
-	fprintf(stderr, "corrupt mailbox\n");
-	exit(1);
-}
-
-static int populate_maildir_list(struct string_list *list, const char *path)
-{
-	DIR *dir;
-	struct dirent *dent;
-	char name[PATH_MAX];
-	char *subs[] = { "cur", "new", NULL };
-	char **sub;
-
-	for (sub = subs; *sub; ++sub) {
-		snprintf(name, sizeof(name), "%s/%s", path, *sub);
-		if ((dir = opendir(name)) == NULL) {
-			if (errno == ENOENT)
-				continue;
-			error("cannot opendir %s (%s)", name, strerror(errno));
-			return -1;
-		}
-
-		while ((dent = readdir(dir)) != NULL) {
-			if (dent->d_name[0] == '.')
-				continue;
-			snprintf(name, sizeof(name), "%s/%s", *sub, dent->d_name);
-			string_list_insert(name, list);
-		}
-
-		closedir(dir);
-	}
-
-	return 0;
-}
-
-static int split_maildir(const char *maildir, const char *dir,
-	int nr_prec, int skip)
-{
-	char file[PATH_MAX];
-	char name[PATH_MAX];
-	int ret = -1;
-	int i;
-	struct string_list list = {NULL, 0, 0, 1};
-
-	if (populate_maildir_list(&list, maildir) < 0)
-		goto out;
-
-	for (i = 0; i < list.nr; i++) {
-		FILE *f;
-		snprintf(file, sizeof(file), "%s/%s", maildir, list.items[i].string);
-		f = fopen(file, "r");
-		if (!f) {
-			error("cannot open mail %s (%s)", file, strerror(errno));
-			goto out;
-		}
-
-		if (fgets(buf, sizeof(buf), f) == NULL) {
-			error("cannot read mail %s (%s)", file, strerror(errno));
-			goto out;
-		}
-
-		sprintf(name, "%s/%0*d", dir, nr_prec, ++skip);
-		split_one(f, name, 1);
-
-		fclose(f);
-	}
-
-	ret = skip;
-out:
-	string_list_clear(&list, 1);
-	return ret;
-}
-
-static int split_mbox(const char *file, const char *dir, int allow_bare,
-		      int nr_prec, int skip)
-{
-	char name[PATH_MAX];
-	int ret = -1;
-	int peek;
-
-	FILE *f = !strcmp(file, "-") ? stdin : fopen(file, "r");
-	int file_done = 0;
-
-	if (!f) {
-		error("cannot open mbox %s", file);
-		goto out;
-	}
-
-	do {
-		peek = fgetc(f);
-	} while (isspace(peek));
-	ungetc(peek, f);
-
-	if (fgets(buf, sizeof(buf), f) == NULL) {
-		/* empty stdin is OK */
-		if (f != stdin) {
-			error("cannot read mbox %s", file);
-			goto out;
-		}
-		file_done = 1;
-	}
-
-	while (!file_done) {
-		sprintf(name, "%s/%0*d", dir, nr_prec, ++skip);
-		file_done = split_one(f, name, allow_bare);
-	}
-
-	if (f != stdin)
-		fclose(f);
-
-	ret = skip;
-out:
-	return ret;
-}
-
-int cmd_mailsplit(int argc, const char **argv, const char *prefix)
-{
-	int nr = 0, nr_prec = 4, num = 0;
-	int allow_bare = 0;
-	const char *dir = NULL;
-	const char **argp;
-	static const char *stdin_only[] = { "-", NULL };
-
-	for (argp = argv+1; *argp; argp++) {
-		const char *arg = *argp;
-
-		if (arg[0] != '-')
-			break;
-		/* do flags here */
-		if ( arg[1] == 'd' ) {
-			nr_prec = strtol(arg+2, NULL, 10);
-			if (nr_prec < 3 || 10 <= nr_prec)
-				usage(git_mailsplit_usage);
-			continue;
-		} else if ( arg[1] == 'f' ) {
-			nr = strtol(arg+2, NULL, 10);
-		} else if ( arg[1] == 'b' && !arg[2] ) {
-			allow_bare = 1;
-		} else if ( arg[1] == 'o' && arg[2] ) {
-			dir = arg+2;
-		} else if ( arg[1] == '-' && !arg[2] ) {
-			argp++;	/* -- marks end of options */
-			break;
-		} else {
-			die("unknown option: %s", arg);
-		}
-	}
-
-	if ( !dir ) {
-		/* Backwards compatibility: if no -o specified, accept
-		   <mbox> <dir> or just <dir> */
-		switch (argc - (argp-argv)) {
-		case 1:
-			dir = argp[0];
-			argp = stdin_only;
-			break;
-		case 2:
-			stdin_only[0] = argp[0];
-			dir = argp[1];
-			argp = stdin_only;
-			break;
-		default:
-			usage(git_mailsplit_usage);
-		}
-	} else {
-		/* New usage: if no more argument, parse stdin */
-		if ( !*argp )
-			argp = stdin_only;
-	}
-
-	while (*argp) {
-		const char *arg = *argp++;
-		struct stat argstat;
-		int ret = 0;
-
-		if (arg[0] == '-' && arg[1] == 0) {
-			ret = split_mbox(arg, dir, allow_bare, nr_prec, nr);
-			if (ret < 0) {
-				error("cannot split patches from stdin");
-				return 1;
-			}
-			num += (ret - nr);
-			nr = ret;
-			continue;
-		}
-
-		if (stat(arg, &argstat) == -1) {
-			error("cannot stat %s (%s)", arg, strerror(errno));
-			return 1;
-		}
-
-		if (S_ISDIR(argstat.st_mode))
-			ret = split_maildir(arg, dir, nr_prec, nr);
-		else
-			ret = split_mbox(arg, dir, allow_bare, nr_prec, nr);
-
-		if (ret < 0) {
-			error("cannot split patches from %s", arg);
-			return 1;
-		}
-		num += (ret - nr);
-		nr = ret;
-	}
-
-	printf("%d\n", num);
-
-	return 0;
-}
diff --git a/builtin-merge-base.c b/builtin-merge-base.c
deleted file mode 100644
index 54e7ec2..0000000
--- a/builtin-merge-base.c
+++ /dev/null
@@ -1,63 +0,0 @@
-#include "builtin.h"
-#include "cache.h"
-#include "commit.h"
-#include "parse-options.h"
-
-static int show_merge_base(struct commit **rev, int rev_nr, int show_all)
-{
-	struct commit_list *result;
-
-	result = get_merge_bases_many(rev[0], rev_nr - 1, rev + 1, 0);
-
-	if (!result)
-		return 1;
-
-	while (result) {
-		printf("%s\n", sha1_to_hex(result->item->object.sha1));
-		if (!show_all)
-			return 0;
-		result = result->next;
-	}
-
-	return 0;
-}
-
-static const char * const merge_base_usage[] = {
-	"git merge-base [-a|--all] <commit> <commit>...",
-	NULL
-};
-
-static struct commit *get_commit_reference(const char *arg)
-{
-	unsigned char revkey[20];
-	struct commit *r;
-
-	if (get_sha1(arg, revkey))
-		die("Not a valid object name %s", arg);
-	r = lookup_commit_reference(revkey);
-	if (!r)
-		die("Not a valid commit name %s", arg);
-
-	return r;
-}
-
-int cmd_merge_base(int argc, const char **argv, const char *prefix)
-{
-	struct commit **rev;
-	int rev_nr = 0;
-	int show_all = 0;
-
-	struct option options[] = {
-		OPT_BOOLEAN('a', "all", &show_all, "outputs all common ancestors"),
-		OPT_END()
-	};
-
-	git_config(git_default_config, NULL);
-	argc = parse_options(argc, argv, prefix, options, merge_base_usage, 0);
-	if (argc < 2)
-		usage_with_options(merge_base_usage, options);
-	rev = xmalloc(argc * sizeof(*rev));
-	while (argc-- > 0)
-		rev[rev_nr++] = get_commit_reference(*argv++);
-	return show_merge_base(rev, rev_nr, show_all);
-}
diff --git a/builtin-merge-file.c b/builtin-merge-file.c
deleted file mode 100644
index afd2ea7..0000000
--- a/builtin-merge-file.c
+++ /dev/null
@@ -1,91 +0,0 @@
-#include "builtin.h"
-#include "cache.h"
-#include "xdiff/xdiff.h"
-#include "xdiff-interface.h"
-#include "parse-options.h"
-
-static const char *const merge_file_usage[] = {
-	"git merge-file [options] [-L name1 [-L orig [-L name2]]] file1 orig_file file2",
-	NULL
-};
-
-static int label_cb(const struct option *opt, const char *arg, int unset)
-{
-	static int label_count = 0;
-	const char **names = (const char **)opt->value;
-
-	if (label_count >= 3)
-		return error("too many labels on the command line");
-	names[label_count++] = arg;
-	return 0;
-}
-
-int cmd_merge_file(int argc, const char **argv, const char *prefix)
-{
-	const char *names[3] = { NULL, NULL, NULL };
-	mmfile_t mmfs[3];
-	mmbuffer_t result = {NULL, 0};
-	xpparam_t xpp = {XDF_NEED_MINIMAL};
-	int ret = 0, i = 0, to_stdout = 0;
-	int merge_level = XDL_MERGE_ZEALOUS_ALNUM;
-	int merge_style = 0, quiet = 0;
-	int nongit;
-
-	struct option options[] = {
-		OPT_BOOLEAN('p', "stdout", &to_stdout, "send results to standard output"),
-		OPT_SET_INT(0, "diff3", &merge_style, "use a diff3 based merge", XDL_MERGE_DIFF3),
-		OPT__QUIET(&quiet),
-		OPT_CALLBACK('L', NULL, names, "name",
-			     "set labels for file1/orig_file/file2", &label_cb),
-		OPT_END(),
-	};
-
-	prefix = setup_git_directory_gently(&nongit);
-	if (!nongit) {
-		/* Read the configuration file */
-		git_config(git_xmerge_config, NULL);
-		if (0 <= git_xmerge_style)
-			merge_style = git_xmerge_style;
-	}
-
-	argc = parse_options(argc, argv, prefix, options, merge_file_usage, 0);
-	if (argc != 3)
-		usage_with_options(merge_file_usage, options);
-	if (quiet) {
-		if (!freopen("/dev/null", "w", stderr))
-			return error("failed to redirect stderr to /dev/null: "
-				     "%s\n", strerror(errno));
-	}
-
-	for (i = 0; i < 3; i++) {
-		if (!names[i])
-			names[i] = argv[i];
-		if (read_mmfile(mmfs + i, argv[i]))
-			return -1;
-		if (buffer_is_binary(mmfs[i].ptr, mmfs[i].size))
-			return error("Cannot merge binary files: %s\n",
-					argv[i]);
-	}
-
-	ret = xdl_merge(mmfs + 1, mmfs + 0, names[0], mmfs + 2, names[2],
-			&xpp, merge_level | merge_style, &result);
-
-	for (i = 0; i < 3; i++)
-		free(mmfs[i].ptr);
-
-	if (ret >= 0) {
-		const char *filename = argv[0];
-		FILE *f = to_stdout ? stdout : fopen(filename, "wb");
-
-		if (!f)
-			ret = error("Could not open %s for writing", filename);
-		else if (result.size &&
-			 fwrite(result.ptr, result.size, 1, f) != 1)
-			ret = error("Could not write to %s", filename);
-		else if (fclose(f))
-			ret = error("Could not close %s", filename);
-		free(result.ptr);
-	}
-
-	return ret;
-}
diff --git a/builtin-merge-ours.c b/builtin-merge-ours.c
deleted file mode 100644
index 8f5bbaf..0000000
--- a/builtin-merge-ours.c
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Implementation of git-merge-ours.sh as builtin
- *
- * Copyright (c) 2007 Thomas Harning Jr
- * Original:
- * Original Copyright (c) 2005 Junio C Hamano
- *
- * Pretend we resolved the heads, but declare our tree trumps everybody else.
- */
-#include "git-compat-util.h"
-#include "builtin.h"
-
-static const char *diff_index_args[] = {
-	"diff-index", "--quiet", "--cached", "HEAD", "--", NULL
-};
-#define NARGS (ARRAY_SIZE(diff_index_args) - 1)
-
-int cmd_merge_ours(int argc, const char **argv, const char *prefix)
-{
-	/*
-	 * We need to exit with 2 if the index does not match our HEAD tree,
-	 * because the current index is what we will be committing as the
-	 * merge result.
-	 */
-	if (cmd_diff_index(NARGS, diff_index_args, prefix))
-		exit(2);
-	exit(0);
-}
diff --git a/builtin-merge-recursive.c b/builtin-merge-recursive.c
deleted file mode 100644
index d26a96e..0000000
--- a/builtin-merge-recursive.c
+++ /dev/null
@@ -1,73 +0,0 @@
-#include "cache.h"
-#include "commit.h"
-#include "tag.h"
-#include "merge-recursive.h"
-
-static const char *better_branch_name(const char *branch)
-{
-	static char githead_env[8 + 40 + 1];
-	char *name;
-
-	if (strlen(branch) != 40)
-		return branch;
-	sprintf(githead_env, "GITHEAD_%s", branch);
-	name = getenv(githead_env);
-	return name ? name : branch;
-}
-
-int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
-{
-	const unsigned char *bases[21];
-	unsigned bases_count = 0;
-	int i, failed;
-	unsigned char h1[20], h2[20];
-	struct merge_options o;
-	struct commit *result;
-
-	init_merge_options(&o);
-	if (argv[0]) {
-		int namelen = strlen(argv[0]);
-		if (8 < namelen &&
-		    !strcmp(argv[0] + namelen - 8, "-subtree"))
-			o.subtree_merge = 1;
-	}
-
-	if (argc < 4)
-		die("Usage: %s <base>... -- <head> <remote> ...", argv[0]);
-
-	for (i = 1; i < argc; ++i) {
-		if (!strcmp(argv[i], "--"))
-			break;
-		if (bases_count < ARRAY_SIZE(bases)-1) {
-			unsigned char *sha = xmalloc(20);
-			if (get_sha1(argv[i], sha))
-				die("Could not parse object '%s'", argv[i]);
-			bases[bases_count++] = sha;
-		}
-		else
-			warning("Cannot handle more than %d bases. "
-				"Ignoring %s.",
-				(int)ARRAY_SIZE(bases)-1, argv[i]);
-	}
-	if (argc - i != 3) /* "--" "<head>" "<remote>" */
-		die("Not handling anything other than two heads merge.");
-
-	o.branch1 = argv[++i];
-	o.branch2 = argv[++i];
-
-	if (get_sha1(o.branch1, h1))
-		die("Could not resolve ref '%s'", o.branch1);
-	if (get_sha1(o.branch2, h2))
-		die("Could not resolve ref '%s'", o.branch2);
-
-	o.branch1 = better_branch_name(o.branch1);
-	o.branch2 = better_branch_name(o.branch2);
-
-	if (o.verbosity >= 3)
-		printf("Merging %s with %s\n", o.branch1, o.branch2);
-
-	failed = merge_recursive_generic(&o, h1, h2, bases_count, bases, &result);
-	if (failed < 0)
-		return 128; /* die() error code */
-	return failed;
-}
diff --git a/builtin-merge.c b/builtin-merge.c
deleted file mode 100644
index 82b5466..0000000
--- a/builtin-merge.c
+++ /dev/null
@@ -1,1225 +0,0 @@
-/*
- * Builtin "git merge"
- *
- * Copyright (c) 2008 Miklos Vajna <vmiklos@frugalware.org>
- *
- * Based on git-merge.sh by Junio C Hamano.
- */
-
-#include "cache.h"
-#include "parse-options.h"
-#include "builtin.h"
-#include "run-command.h"
-#include "diff.h"
-#include "refs.h"
-#include "commit.h"
-#include "diffcore.h"
-#include "revision.h"
-#include "unpack-trees.h"
-#include "cache-tree.h"
-#include "dir.h"
-#include "utf8.h"
-#include "log-tree.h"
-#include "color.h"
-#include "rerere.h"
-#include "help.h"
-#include "merge-recursive.h"
-
-#define DEFAULT_TWOHEAD (1<<0)
-#define DEFAULT_OCTOPUS (1<<1)
-#define NO_FAST_FORWARD (1<<2)
-#define NO_TRIVIAL      (1<<3)
-
-struct strategy {
-	const char *name;
-	unsigned attr;
-};
-
-static const char * const builtin_merge_usage[] = {
-	"git merge [options] <remote>...",
-	"git merge [options] <msg> HEAD <remote>",
-	NULL
-};
-
-static int show_diffstat = 1, option_log, squash;
-static int option_commit = 1, allow_fast_forward = 1;
-static int allow_trivial = 1, have_message;
-static struct strbuf merge_msg;
-static struct commit_list *remoteheads;
-static unsigned char head[20], stash[20];
-static struct strategy **use_strategies;
-static size_t use_strategies_nr, use_strategies_alloc;
-static const char *branch;
-static int verbosity;
-
-static struct strategy all_strategy[] = {
-	{ "recursive",  DEFAULT_TWOHEAD | NO_TRIVIAL },
-	{ "octopus",    DEFAULT_OCTOPUS },
-	{ "resolve",    0 },
-	{ "ours",       NO_FAST_FORWARD | NO_TRIVIAL },
-	{ "subtree",    NO_FAST_FORWARD | NO_TRIVIAL },
-};
-
-static const char *pull_twohead, *pull_octopus;
-
-static int option_parse_message(const struct option *opt,
-				const char *arg, int unset)
-{
-	struct strbuf *buf = opt->value;
-
-	if (unset)
-		strbuf_setlen(buf, 0);
-	else if (arg) {
-		strbuf_addf(buf, "%s\n\n", arg);
-		have_message = 1;
-	} else
-		return error("switch `m' requires a value");
-	return 0;
-}
-
-static struct strategy *get_strategy(const char *name)
-{
-	int i;
-	struct strategy *ret;
-	static struct cmdnames main_cmds, other_cmds;
-	static int loaded;
-
-	if (!name)
-		return NULL;
-
-	for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
-		if (!strcmp(name, all_strategy[i].name))
-			return &all_strategy[i];
-
-	if (!loaded) {
-		struct cmdnames not_strategies;
-		loaded = 1;
-
-		memset(&not_strategies, 0, sizeof(struct cmdnames));
-		load_command_list("git-merge-", &main_cmds, &other_cmds);
-		for (i = 0; i < main_cmds.cnt; i++) {
-			int j, found = 0;
-			struct cmdname *ent = main_cmds.names[i];
-			for (j = 0; j < ARRAY_SIZE(all_strategy); j++)
-				if (!strncmp(ent->name, all_strategy[j].name, ent->len)
-						&& !all_strategy[j].name[ent->len])
-					found = 1;
-			if (!found)
-				add_cmdname(&not_strategies, ent->name, ent->len);
-			exclude_cmds(&main_cmds, &not_strategies);
-		}
-	}
-	if (!is_in_cmdlist(&main_cmds, name) && !is_in_cmdlist(&other_cmds, name)) {
-		fprintf(stderr, "Could not find merge strategy '%s'.\n", name);
-		fprintf(stderr, "Available strategies are:");
-		for (i = 0; i < main_cmds.cnt; i++)
-			fprintf(stderr, " %s", main_cmds.names[i]->name);
-		fprintf(stderr, ".\n");
-		if (other_cmds.cnt) {
-			fprintf(stderr, "Available custom strategies are:");
-			for (i = 0; i < other_cmds.cnt; i++)
-				fprintf(stderr, " %s", other_cmds.names[i]->name);
-			fprintf(stderr, ".\n");
-		}
-		exit(1);
-	}
-
-	ret = xcalloc(1, sizeof(struct strategy));
-	ret->name = xstrdup(name);
-	return ret;
-}
-
-static void append_strategy(struct strategy *s)
-{
-	ALLOC_GROW(use_strategies, use_strategies_nr + 1, use_strategies_alloc);
-	use_strategies[use_strategies_nr++] = s;
-}
-
-static int option_parse_strategy(const struct option *opt,
-				 const char *name, int unset)
-{
-	if (unset)
-		return 0;
-
-	append_strategy(get_strategy(name));
-	return 0;
-}
-
-static int option_parse_n(const struct option *opt,
-			  const char *arg, int unset)
-{
-	show_diffstat = unset;
-	return 0;
-}
-
-static struct option builtin_merge_options[] = {
-	{ OPTION_CALLBACK, 'n', NULL, NULL, NULL,
-		"do not show a diffstat at the end of the merge",
-		PARSE_OPT_NOARG, option_parse_n },
-	OPT_BOOLEAN(0, "stat", &show_diffstat,
-		"show a diffstat at the end of the merge"),
-	OPT_BOOLEAN(0, "summary", &show_diffstat, "(synonym to --stat)"),
-	OPT_BOOLEAN(0, "log", &option_log,
-		"add list of one-line log to merge commit message"),
-	OPT_BOOLEAN(0, "squash", &squash,
-		"create a single commit instead of doing a merge"),
-	OPT_BOOLEAN(0, "commit", &option_commit,
-		"perform a commit if the merge succeeds (default)"),
-	OPT_BOOLEAN(0, "ff", &allow_fast_forward,
-		"allow fast forward (default)"),
-	OPT_CALLBACK('s', "strategy", &use_strategies, "strategy",
-		"merge strategy to use", option_parse_strategy),
-	OPT_CALLBACK('m', "message", &merge_msg, "message",
-		"message to be used for the merge commit (if any)",
-		option_parse_message),
-	OPT__VERBOSITY(&verbosity),
-	OPT_END()
-};
-
-/* Cleans up metadata that is uninteresting after a succeeded merge. */
-static void drop_save(void)
-{
-	unlink(git_path("MERGE_HEAD"));
-	unlink(git_path("MERGE_MSG"));
-	unlink(git_path("MERGE_MODE"));
-}
-
-static void save_state(void)
-{
-	int len;
-	struct child_process cp;
-	struct strbuf buffer = STRBUF_INIT;
-	const char *argv[] = {"stash", "create", NULL};
-
-	memset(&cp, 0, sizeof(cp));
-	cp.argv = argv;
-	cp.out = -1;
-	cp.git_cmd = 1;
-
-	if (start_command(&cp))
-		die("could not run stash.");
-	len = strbuf_read(&buffer, cp.out, 1024);
-	close(cp.out);
-
-	if (finish_command(&cp) || len < 0)
-		die("stash failed");
-	else if (!len)
-		return;
-	strbuf_setlen(&buffer, buffer.len-1);
-	if (get_sha1(buffer.buf, stash))
-		die("not a valid object: %s", buffer.buf);
-}
-
-static void reset_hard(unsigned const char *sha1, int verbose)
-{
-	int i = 0;
-	const char *args[6];
-
-	args[i++] = "read-tree";
-	if (verbose)
-		args[i++] = "-v";
-	args[i++] = "--reset";
-	args[i++] = "-u";
-	args[i++] = sha1_to_hex(sha1);
-	args[i] = NULL;
-
-	if (run_command_v_opt(args, RUN_GIT_CMD))
-		die("read-tree failed");
-}
-
-static void restore_state(void)
-{
-	struct strbuf sb = STRBUF_INIT;
-	const char *args[] = { "stash", "apply", NULL, NULL };
-
-	if (is_null_sha1(stash))
-		return;
-
-	reset_hard(head, 1);
-
-	args[2] = sha1_to_hex(stash);
-
-	/*
-	 * It is OK to ignore error here, for example when there was
-	 * nothing to restore.
-	 */
-	run_command_v_opt(args, RUN_GIT_CMD);
-
-	strbuf_release(&sb);
-	refresh_cache(REFRESH_QUIET);
-}
-
-/* This is called when no merge was necessary. */
-static void finish_up_to_date(const char *msg)
-{
-	if (verbosity >= 0)
-		printf("%s%s\n", squash ? " (nothing to squash)" : "", msg);
-	drop_save();
-}
-
-static void squash_message(void)
-{
-	struct rev_info rev;
-	struct commit *commit;
-	struct strbuf out = STRBUF_INIT;
-	struct commit_list *j;
-	int fd;
-
-	printf("Squash commit -- not updating HEAD\n");
-	fd = open(git_path("SQUASH_MSG"), O_WRONLY | O_CREAT, 0666);
-	if (fd < 0)
-		die_errno("Could not write to '%s'", git_path("SQUASH_MSG"));
-
-	init_revisions(&rev, NULL);
-	rev.ignore_merges = 1;
-	rev.commit_format = CMIT_FMT_MEDIUM;
-
-	commit = lookup_commit(head);
-	commit->object.flags |= UNINTERESTING;
-	add_pending_object(&rev, &commit->object, NULL);
-
-	for (j = remoteheads; j; j = j->next)
-		add_pending_object(&rev, &j->item->object, NULL);
-
-	setup_revisions(0, NULL, &rev, NULL);
-	if (prepare_revision_walk(&rev))
-		die("revision walk setup failed");
-
-	strbuf_addstr(&out, "Squashed commit of the following:\n");
-	while ((commit = get_revision(&rev)) != NULL) {
-		strbuf_addch(&out, '\n');
-		strbuf_addf(&out, "commit %s\n",
-			sha1_to_hex(commit->object.sha1));
-		pretty_print_commit(rev.commit_format, commit, &out, rev.abbrev,
-			NULL, NULL, rev.date_mode, 0);
-	}
-	if (write(fd, out.buf, out.len) < 0)
-		die_errno("Writing SQUASH_MSG");
-	if (close(fd))
-		die_errno("Finishing SQUASH_MSG");
-	strbuf_release(&out);
-}
-
-static void finish(const unsigned char *new_head, const char *msg)
-{
-	struct strbuf reflog_message = STRBUF_INIT;
-
-	if (!msg)
-		strbuf_addstr(&reflog_message, getenv("GIT_REFLOG_ACTION"));
-	else {
-		if (verbosity >= 0)
-			printf("%s\n", msg);
-		strbuf_addf(&reflog_message, "%s: %s",
-			getenv("GIT_REFLOG_ACTION"), msg);
-	}
-	if (squash) {
-		squash_message();
-	} else {
-		if (verbosity >= 0 && !merge_msg.len)
-			printf("No merge message -- not updating HEAD\n");
-		else {
-			const char *argv_gc_auto[] = { "gc", "--auto", NULL };
-			update_ref(reflog_message.buf, "HEAD",
-				new_head, head, 0,
-				DIE_ON_ERR);
-			/*
-			 * We ignore errors in 'gc --auto', since the
-			 * user should see them.
-			 */
-			run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
-		}
-	}
-	if (new_head && show_diffstat) {
-		struct diff_options opts;
-		diff_setup(&opts);
-		opts.output_format |=
-			DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
-		opts.detect_rename = DIFF_DETECT_RENAME;
-		if (diff_use_color_default > 0)
-			DIFF_OPT_SET(&opts, COLOR_DIFF);
-		if (diff_setup_done(&opts) < 0)
-			die("diff_setup_done failed");
-		diff_tree_sha1(head, new_head, "", &opts);
-		diffcore_std(&opts);
-		diff_flush(&opts);
-	}
-
-	/* Run a post-merge hook */
-	run_hook(NULL, "post-merge", squash ? "1" : "0", NULL);
-
-	strbuf_release(&reflog_message);
-}
-
-/* Get the name for the merge commit's message. */
-static void merge_name(const char *remote, struct strbuf *msg)
-{
-	struct object *remote_head;
-	unsigned char branch_head[20], buf_sha[20];
-	struct strbuf buf = STRBUF_INIT;
-	struct strbuf bname = STRBUF_INIT;
-	const char *ptr;
-	int len, early;
-
-	strbuf_branchname(&bname, remote);
-	remote = bname.buf;
-
-	memset(branch_head, 0, sizeof(branch_head));
-	remote_head = peel_to_type(remote, 0, NULL, OBJ_COMMIT);
-	if (!remote_head)
-		die("'%s' does not point to a commit", remote);
-
-	strbuf_addstr(&buf, "refs/heads/");
-	strbuf_addstr(&buf, remote);
-	resolve_ref(buf.buf, branch_head, 0, NULL);
-
-	if (!hashcmp(remote_head->sha1, branch_head)) {
-		strbuf_addf(msg, "%s\t\tbranch '%s' of .\n",
-			sha1_to_hex(branch_head), remote);
-		goto cleanup;
-	}
-
-	/* See if remote matches <name>^^^.. or <name>~<number> */
-	for (len = 0, ptr = remote + strlen(remote);
-	     remote < ptr && ptr[-1] == '^';
-	     ptr--)
-		len++;
-	if (len)
-		early = 1;
-	else {
-		early = 0;
-		ptr = strrchr(remote, '~');
-		if (ptr) {
-			int seen_nonzero = 0;
-
-			len++; /* count ~ */
-			while (*++ptr && isdigit(*ptr)) {
-				seen_nonzero |= (*ptr != '0');
-				len++;
-			}
-			if (*ptr)
-				len = 0; /* not ...~<number> */
-			else if (seen_nonzero)
-				early = 1;
-			else if (len == 1)
-				early = 1; /* "name~" is "name~1"! */
-		}
-	}
-	if (len) {
-		struct strbuf truname = STRBUF_INIT;
-		strbuf_addstr(&truname, "refs/heads/");
-		strbuf_addstr(&truname, remote);
-		strbuf_setlen(&truname, truname.len - len);
-		if (resolve_ref(truname.buf, buf_sha, 0, NULL)) {
-			strbuf_addf(msg,
-				    "%s\t\tbranch '%s'%s of .\n",
-				    sha1_to_hex(remote_head->sha1),
-				    truname.buf + 11,
-				    (early ? " (early part)" : ""));
-			strbuf_release(&truname);
-			goto cleanup;
-		}
-	}
-
-	if (!strcmp(remote, "FETCH_HEAD") &&
-			!access(git_path("FETCH_HEAD"), R_OK)) {
-		FILE *fp;
-		struct strbuf line = STRBUF_INIT;
-		char *ptr;
-
-		fp = fopen(git_path("FETCH_HEAD"), "r");
-		if (!fp)
-			die_errno("could not open '%s' for reading",
-				  git_path("FETCH_HEAD"));
-		strbuf_getline(&line, fp, '\n');
-		fclose(fp);
-		ptr = strstr(line.buf, "\tnot-for-merge\t");
-		if (ptr)
-			strbuf_remove(&line, ptr-line.buf+1, 13);
-		strbuf_addbuf(msg, &line);
-		strbuf_release(&line);
-		goto cleanup;
-	}
-	strbuf_addf(msg, "%s\t\tcommit '%s'\n",
-		sha1_to_hex(remote_head->sha1), remote);
-cleanup:
-	strbuf_release(&buf);
-	strbuf_release(&bname);
-}
-
-static int git_merge_config(const char *k, const char *v, void *cb)
-{
-	if (branch && !prefixcmp(k, "branch.") &&
-		!prefixcmp(k + 7, branch) &&
-		!strcmp(k + 7 + strlen(branch), ".mergeoptions")) {
-		const char **argv;
-		int argc;
-		char *buf;
-
-		buf = xstrdup(v);
-		argc = split_cmdline(buf, &argv);
-		if (argc < 0)
-			die("Bad branch.%s.mergeoptions string", branch);
-		argv = xrealloc(argv, sizeof(*argv) * (argc + 2));
-		memmove(argv + 1, argv, sizeof(*argv) * (argc + 1));
-		argc++;
-		parse_options(argc, argv, NULL, builtin_merge_options,
-			      builtin_merge_usage, 0);
-		free(buf);
-	}
-
-	if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat"))
-		show_diffstat = git_config_bool(k, v);
-	else if (!strcmp(k, "pull.twohead"))
-		return git_config_string(&pull_twohead, k, v);
-	else if (!strcmp(k, "pull.octopus"))
-		return git_config_string(&pull_octopus, k, v);
-	else if (!strcmp(k, "merge.log") || !strcmp(k, "merge.summary"))
-		option_log = git_config_bool(k, v);
-	return git_diff_ui_config(k, v, cb);
-}
-
-static int read_tree_trivial(unsigned char *common, unsigned char *head,
-			     unsigned char *one)
-{
-	int i, nr_trees = 0;
-	struct tree *trees[MAX_UNPACK_TREES];
-	struct tree_desc t[MAX_UNPACK_TREES];
-	struct unpack_trees_options opts;
-
-	memset(&opts, 0, sizeof(opts));
-	opts.head_idx = 2;
-	opts.src_index = &the_index;
-	opts.dst_index = &the_index;
-	opts.update = 1;
-	opts.verbose_update = 1;
-	opts.trivial_merges_only = 1;
-	opts.merge = 1;
-	trees[nr_trees] = parse_tree_indirect(common);
-	if (!trees[nr_trees++])
-		return -1;
-	trees[nr_trees] = parse_tree_indirect(head);
-	if (!trees[nr_trees++])
-		return -1;
-	trees[nr_trees] = parse_tree_indirect(one);
-	if (!trees[nr_trees++])
-		return -1;
-	opts.fn = threeway_merge;
-	cache_tree_free(&active_cache_tree);
-	for (i = 0; i < nr_trees; i++) {
-		parse_tree(trees[i]);
-		init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
-	}
-	if (unpack_trees(nr_trees, t, &opts))
-		return -1;
-	return 0;
-}
-
-static void write_tree_trivial(unsigned char *sha1)
-{
-	if (write_cache_as_tree(sha1, 0, NULL))
-		die("git write-tree failed to write a tree");
-}
-
-static int try_merge_strategy(const char *strategy, struct commit_list *common,
-			      const char *head_arg)
-{
-	const char **args;
-	int i = 0, ret;
-	struct commit_list *j;
-	struct strbuf buf = STRBUF_INIT;
-	int index_fd;
-	struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
-
-	index_fd = hold_locked_index(lock, 1);
-	refresh_cache(REFRESH_QUIET);
-	if (active_cache_changed &&
-			(write_cache(index_fd, active_cache, active_nr) ||
-			 commit_locked_index(lock)))
-		return error("Unable to write index.");
-	rollback_lock_file(lock);
-
-	if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree")) {
-		int clean;
-		struct commit *result;
-		struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
-		int index_fd;
-		struct commit_list *reversed = NULL;
-		struct merge_options o;
-
-		if (remoteheads->next) {
-			error("Not handling anything other than two heads merge.");
-			return 2;
-		}
-
-		init_merge_options(&o);
-		if (!strcmp(strategy, "subtree"))
-			o.subtree_merge = 1;
-
-		o.branch1 = head_arg;
-		o.branch2 = remoteheads->item->util;
-
-		for (j = common; j; j = j->next)
-			commit_list_insert(j->item, &reversed);
-
-		index_fd = hold_locked_index(lock, 1);
-		clean = merge_recursive(&o, lookup_commit(head),
-				remoteheads->item, reversed, &result);
-		if (active_cache_changed &&
-				(write_cache(index_fd, active_cache, active_nr) ||
-				 commit_locked_index(lock)))
-			die ("unable to write %s", get_index_file());
-		rollback_lock_file(lock);
-		return clean ? 0 : 1;
-	} else {
-		args = xmalloc((4 + commit_list_count(common) +
-					commit_list_count(remoteheads)) * sizeof(char *));
-		strbuf_addf(&buf, "merge-%s", strategy);
-		args[i++] = buf.buf;
-		for (j = common; j; j = j->next)
-			args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
-		args[i++] = "--";
-		args[i++] = head_arg;
-		for (j = remoteheads; j; j = j->next)
-			args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
-		args[i] = NULL;
-		ret = run_command_v_opt(args, RUN_GIT_CMD);
-		strbuf_release(&buf);
-		i = 1;
-		for (j = common; j; j = j->next)
-			free((void *)args[i++]);
-		i += 2;
-		for (j = remoteheads; j; j = j->next)
-			free((void *)args[i++]);
-		free(args);
-		discard_cache();
-		if (read_cache() < 0)
-			die("failed to read the cache");
-		return -ret;
-	}
-}
-
-static void count_diff_files(struct diff_queue_struct *q,
-			     struct diff_options *opt, void *data)
-{
-	int *count = data;
-
-	(*count) += q->nr;
-}
-
-static int count_unmerged_entries(void)
-{
-	const struct index_state *state = &the_index;
-	int i, ret = 0;
-
-	for (i = 0; i < state->cache_nr; i++)
-		if (ce_stage(state->cache[i]))
-			ret++;
-
-	return ret;
-}
-
-static int checkout_fast_forward(unsigned char *head, unsigned char *remote)
-{
-	struct tree *trees[MAX_UNPACK_TREES];
-	struct unpack_trees_options opts;
-	struct tree_desc t[MAX_UNPACK_TREES];
-	int i, fd, nr_trees = 0;
-	struct dir_struct dir;
-	struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
-
-	refresh_cache(REFRESH_QUIET);
-
-	fd = hold_locked_index(lock_file, 1);
-
-	memset(&trees, 0, sizeof(trees));
-	memset(&opts, 0, sizeof(opts));
-	memset(&t, 0, sizeof(t));
-	memset(&dir, 0, sizeof(dir));
-	dir.flags |= DIR_SHOW_IGNORED;
-	dir.exclude_per_dir = ".gitignore";
-	opts.dir = &dir;
-
-	opts.head_idx = 1;
-	opts.src_index = &the_index;
-	opts.dst_index = &the_index;
-	opts.update = 1;
-	opts.verbose_update = 1;
-	opts.merge = 1;
-	opts.fn = twoway_merge;
-
-	trees[nr_trees] = parse_tree_indirect(head);
-	if (!trees[nr_trees++])
-		return -1;
-	trees[nr_trees] = parse_tree_indirect(remote);
-	if (!trees[nr_trees++])
-		return -1;
-	for (i = 0; i < nr_trees; i++) {
-		parse_tree(trees[i]);
-		init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
-	}
-	if (unpack_trees(nr_trees, t, &opts))
-		return -1;
-	if (write_cache(fd, active_cache, active_nr) ||
-		commit_locked_index(lock_file))
-		die("unable to write new index file");
-	return 0;
-}
-
-static void split_merge_strategies(const char *string, struct strategy **list,
-				   int *nr, int *alloc)
-{
-	char *p, *q, *buf;
-
-	if (!string)
-		return;
-
-	buf = xstrdup(string);
-	q = buf;
-	for (;;) {
-		p = strchr(q, ' ');
-		if (!p) {
-			ALLOC_GROW(*list, *nr + 1, *alloc);
-			(*list)[(*nr)++].name = xstrdup(q);
-			free(buf);
-			return;
-		} else {
-			*p = '\0';
-			ALLOC_GROW(*list, *nr + 1, *alloc);
-			(*list)[(*nr)++].name = xstrdup(q);
-			q = ++p;
-		}
-	}
-}
-
-static void add_strategies(const char *string, unsigned attr)
-{
-	struct strategy *list = NULL;
-	int list_alloc = 0, list_nr = 0, i;
-
-	memset(&list, 0, sizeof(list));
-	split_merge_strategies(string, &list, &list_nr, &list_alloc);
-	if (list) {
-		for (i = 0; i < list_nr; i++)
-			append_strategy(get_strategy(list[i].name));
-		return;
-	}
-	for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
-		if (all_strategy[i].attr & attr)
-			append_strategy(&all_strategy[i]);
-
-}
-
-static int merge_trivial(void)
-{
-	unsigned char result_tree[20], result_commit[20];
-	struct commit_list *parent = xmalloc(sizeof(*parent));
-
-	write_tree_trivial(result_tree);
-	printf("Wonderful.\n");
-	parent->item = lookup_commit(head);
-	parent->next = xmalloc(sizeof(*parent->next));
-	parent->next->item = remoteheads->item;
-	parent->next->next = NULL;
-	commit_tree(merge_msg.buf, result_tree, parent, result_commit, NULL);
-	finish(result_commit, "In-index merge");
-	drop_save();
-	return 0;
-}
-
-static int finish_automerge(struct commit_list *common,
-			    unsigned char *result_tree,
-			    const char *wt_strategy)
-{
-	struct commit_list *parents = NULL, *j;
-	struct strbuf buf = STRBUF_INIT;
-	unsigned char result_commit[20];
-
-	free_commit_list(common);
-	if (allow_fast_forward) {
-		parents = remoteheads;
-		commit_list_insert(lookup_commit(head), &parents);
-		parents = reduce_heads(parents);
-	} else {
-		struct commit_list **pptr = &parents;
-
-		pptr = &commit_list_insert(lookup_commit(head),
-				pptr)->next;
-		for (j = remoteheads; j; j = j->next)
-			pptr = &commit_list_insert(j->item, pptr)->next;
-	}
-	free_commit_list(remoteheads);
-	strbuf_addch(&merge_msg, '\n');
-	commit_tree(merge_msg.buf, result_tree, parents, result_commit, NULL);
-	strbuf_addf(&buf, "Merge made by %s.", wt_strategy);
-	finish(result_commit, buf.buf);
-	strbuf_release(&buf);
-	drop_save();
-	return 0;
-}
-
-static int suggest_conflicts(void)
-{
-	FILE *fp;
-	int pos;
-
-	fp = fopen(git_path("MERGE_MSG"), "a");
-	if (!fp)
-		die_errno("Could not open '%s' for writing",
-			  git_path("MERGE_MSG"));
-	fprintf(fp, "\nConflicts:\n");
-	for (pos = 0; pos < active_nr; pos++) {
-		struct cache_entry *ce = active_cache[pos];
-
-		if (ce_stage(ce)) {
-			fprintf(fp, "\t%s\n", ce->name);
-			while (pos + 1 < active_nr &&
-					!strcmp(ce->name,
-						active_cache[pos + 1]->name))
-				pos++;
-		}
-	}
-	fclose(fp);
-	rerere();
-	printf("Automatic merge failed; "
-			"fix conflicts and then commit the result.\n");
-	return 1;
-}
-
-static struct commit *is_old_style_invocation(int argc, const char **argv)
-{
-	struct commit *second_token = NULL;
-	if (argc > 1) {
-		unsigned char second_sha1[20];
-
-		if (get_sha1(argv[1], second_sha1))
-			return NULL;
-		second_token = lookup_commit_reference_gently(second_sha1, 0);
-		if (!second_token)
-			die("'%s' is not a commit", argv[1]);
-		if (hashcmp(second_token->object.sha1, head))
-			return NULL;
-	}
-	return second_token;
-}
-
-static int evaluate_result(void)
-{
-	int cnt = 0;
-	struct rev_info rev;
-
-	/* Check how many files differ. */
-	init_revisions(&rev, "");
-	setup_revisions(0, NULL, &rev, NULL);
-	rev.diffopt.output_format |=
-		DIFF_FORMAT_CALLBACK;
-	rev.diffopt.format_callback = count_diff_files;
-	rev.diffopt.format_callback_data = &cnt;
-	run_diff_files(&rev, 0);
-
-	/*
-	 * Check how many unmerged entries are
-	 * there.
-	 */
-	cnt += count_unmerged_entries();
-
-	return cnt;
-}
-
-int cmd_merge(int argc, const char **argv, const char *prefix)
-{
-	unsigned char result_tree[20];
-	struct strbuf buf = STRBUF_INIT;
-	const char *head_arg;
-	int flag, head_invalid = 0, i;
-	int best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0;
-	struct commit_list *common = NULL;
-	const char *best_strategy = NULL, *wt_strategy = NULL;
-	struct commit_list **remotes = &remoteheads;
-
-	setup_work_tree();
-	if (file_exists(git_path("MERGE_HEAD")))
-		die("You have not concluded your merge. (MERGE_HEAD exists)");
-	if (read_cache_unmerged())
-		die("You are in the middle of a conflicted merge."
-				" (index unmerged)");
-
-	/*
-	 * Check if we are _not_ on a detached HEAD, i.e. if there is a
-	 * current branch.
-	 */
-	branch = resolve_ref("HEAD", head, 0, &flag);
-	if (branch && !prefixcmp(branch, "refs/heads/"))
-		branch += 11;
-	if (is_null_sha1(head))
-		head_invalid = 1;
-
-	git_config(git_merge_config, NULL);
-
-	/* for color.ui */
-	if (diff_use_color_default == -1)
-		diff_use_color_default = git_use_color_default;
-
-	argc = parse_options(argc, argv, prefix, builtin_merge_options,
-			builtin_merge_usage, 0);
-	if (verbosity < 0)
-		show_diffstat = 0;
-
-	if (squash) {
-		if (!allow_fast_forward)
-			die("You cannot combine --squash with --no-ff.");
-		option_commit = 0;
-	}
-
-	if (!argc)
-		usage_with_options(builtin_merge_usage,
-			builtin_merge_options);
-
-	/*
-	 * This could be traditional "merge <msg> HEAD <commit>..."  and
-	 * the way we can tell it is to see if the second token is HEAD,
-	 * but some people might have misused the interface and used a
-	 * committish that is the same as HEAD there instead.
-	 * Traditional format never would have "-m" so it is an
-	 * additional safety measure to check for it.
-	 */
-
-	if (!have_message && is_old_style_invocation(argc, argv)) {
-		strbuf_addstr(&merge_msg, argv[0]);
-		head_arg = argv[1];
-		argv += 2;
-		argc -= 2;
-	} else if (head_invalid) {
-		struct object *remote_head;
-		/*
-		 * If the merged head is a valid one there is no reason
-		 * to forbid "git merge" into a branch yet to be born.
-		 * We do the same for "git pull".
-		 */
-		if (argc != 1)
-			die("Can merge only exactly one commit into "
-				"empty head");
-		if (squash)
-			die("Squash commit into empty head not supported yet");
-		if (!allow_fast_forward)
-			die("Non-fast-forward commit does not make sense into "
-			    "an empty head");
-		remote_head = peel_to_type(argv[0], 0, NULL, OBJ_COMMIT);
-		if (!remote_head)
-			die("%s - not something we can merge", argv[0]);
-		update_ref("initial pull", "HEAD", remote_head->sha1, NULL, 0,
-				DIE_ON_ERR);
-		reset_hard(remote_head->sha1, 0);
-		return 0;
-	} else {
-		struct strbuf msg = STRBUF_INIT;
-
-		/* We are invoked directly as the first-class UI. */
-		head_arg = "HEAD";
-
-		/*
-		 * All the rest are the commits being merged;
-		 * prepare the standard merge summary message to
-		 * be appended to the given message.  If remote
-		 * is invalid we will die later in the common
-		 * codepath so we discard the error in this
-		 * loop.
-		 */
-		for (i = 0; i < argc; i++)
-			merge_name(argv[i], &msg);
-		fmt_merge_msg(option_log, &msg, &merge_msg);
-		if (merge_msg.len)
-			strbuf_setlen(&merge_msg, merge_msg.len-1);
-	}
-
-	if (head_invalid || !argc)
-		usage_with_options(builtin_merge_usage,
-			builtin_merge_options);
-
-	strbuf_addstr(&buf, "merge");
-	for (i = 0; i < argc; i++)
-		strbuf_addf(&buf, " %s", argv[i]);
-	setenv("GIT_REFLOG_ACTION", buf.buf, 0);
-	strbuf_reset(&buf);
-
-	for (i = 0; i < argc; i++) {
-		struct object *o;
-		struct commit *commit;
-
-		o = peel_to_type(argv[i], 0, NULL, OBJ_COMMIT);
-		if (!o)
-			die("%s - not something we can merge", argv[i]);
-		commit = lookup_commit(o->sha1);
-		commit->util = (void *)argv[i];
-		remotes = &commit_list_insert(commit, remotes)->next;
-
-		strbuf_addf(&buf, "GITHEAD_%s", sha1_to_hex(o->sha1));
-		setenv(buf.buf, argv[i], 1);
-		strbuf_reset(&buf);
-	}
-
-	if (!use_strategies) {
-		if (!remoteheads->next)
-			add_strategies(pull_twohead, DEFAULT_TWOHEAD);
-		else
-			add_strategies(pull_octopus, DEFAULT_OCTOPUS);
-	}
-
-	for (i = 0; i < use_strategies_nr; i++) {
-		if (use_strategies[i]->attr & NO_FAST_FORWARD)
-			allow_fast_forward = 0;
-		if (use_strategies[i]->attr & NO_TRIVIAL)
-			allow_trivial = 0;
-	}
-
-	if (!remoteheads->next)
-		common = get_merge_bases(lookup_commit(head),
-				remoteheads->item, 1);
-	else {
-		struct commit_list *list = remoteheads;
-		commit_list_insert(lookup_commit(head), &list);
-		common = get_octopus_merge_bases(list);
-		free(list);
-	}
-
-	update_ref("updating ORIG_HEAD", "ORIG_HEAD", head, NULL, 0,
-		DIE_ON_ERR);
-
-	if (!common)
-		; /* No common ancestors found. We need a real merge. */
-	else if (!remoteheads->next && !common->next &&
-			common->item == remoteheads->item) {
-		/*
-		 * If head can reach all the merge then we are up to date.
-		 * but first the most common case of merging one remote.
-		 */
-		finish_up_to_date("Already up-to-date.");
-		return 0;
-	} else if (allow_fast_forward && !remoteheads->next &&
-			!common->next &&
-			!hashcmp(common->item->object.sha1, head)) {
-		/* Again the most common case of merging one remote. */
-		struct strbuf msg = STRBUF_INIT;
-		struct object *o;
-		char hex[41];
-
-		strcpy(hex, find_unique_abbrev(head, DEFAULT_ABBREV));
-
-		if (verbosity >= 0)
-			printf("Updating %s..%s\n",
-				hex,
-				find_unique_abbrev(remoteheads->item->object.sha1,
-				DEFAULT_ABBREV));
-		strbuf_addstr(&msg, "Fast forward");
-		if (have_message)
-			strbuf_addstr(&msg,
-				" (no commit created; -m option ignored)");
-		o = peel_to_type(sha1_to_hex(remoteheads->item->object.sha1),
-			0, NULL, OBJ_COMMIT);
-		if (!o)
-			return 1;
-
-		if (checkout_fast_forward(head, remoteheads->item->object.sha1))
-			return 1;
-
-		finish(o->sha1, msg.buf);
-		drop_save();
-		return 0;
-	} else if (!remoteheads->next && common->next)
-		;
-		/*
-		 * We are not doing octopus and not fast forward.  Need
-		 * a real merge.
-		 */
-	else if (!remoteheads->next && !common->next && option_commit) {
-		/*
-		 * We are not doing octopus, not fast forward, and have
-		 * only one common.
-		 */
-		refresh_cache(REFRESH_QUIET);
-		if (allow_trivial) {
-			/* See if it is really trivial. */
-			git_committer_info(IDENT_ERROR_ON_NO_NAME);
-			printf("Trying really trivial in-index merge...\n");
-			if (!read_tree_trivial(common->item->object.sha1,
-					head, remoteheads->item->object.sha1))
-				return merge_trivial();
-			printf("Nope.\n");
-		}
-	} else {
-		/*
-		 * An octopus.  If we can reach all the remote we are up
-		 * to date.
-		 */
-		int up_to_date = 1;
-		struct commit_list *j;
-
-		for (j = remoteheads; j; j = j->next) {
-			struct commit_list *common_one;
-
-			/*
-			 * Here we *have* to calculate the individual
-			 * merge_bases again, otherwise "git merge HEAD^
-			 * HEAD^^" would be missed.
-			 */
-			common_one = get_merge_bases(lookup_commit(head),
-				j->item, 1);
-			if (hashcmp(common_one->item->object.sha1,
-				j->item->object.sha1)) {
-				up_to_date = 0;
-				break;
-			}
-		}
-		if (up_to_date) {
-			finish_up_to_date("Already up-to-date. Yeeah!");
-			return 0;
-		}
-	}
-
-	/* We are going to make a new commit. */
-	git_committer_info(IDENT_ERROR_ON_NO_NAME);
-
-	/*
-	 * At this point, we need a real merge.  No matter what strategy
-	 * we use, it would operate on the index, possibly affecting the
-	 * working tree, and when resolved cleanly, have the desired
-	 * tree in the index -- this means that the index must be in
-	 * sync with the head commit.  The strategies are responsible
-	 * to ensure this.
-	 */
-	if (use_strategies_nr != 1) {
-		/*
-		 * Stash away the local changes so that we can try more
-		 * than one.
-		 */
-		save_state();
-	} else {
-		memcpy(stash, null_sha1, 20);
-	}
-
-	for (i = 0; i < use_strategies_nr; i++) {
-		int ret;
-		if (i) {
-			printf("Rewinding the tree to pristine...\n");
-			restore_state();
-		}
-		if (use_strategies_nr != 1)
-			printf("Trying merge strategy %s...\n",
-				use_strategies[i]->name);
-		/*
-		 * Remember which strategy left the state in the working
-		 * tree.
-		 */
-		wt_strategy = use_strategies[i]->name;
-
-		ret = try_merge_strategy(use_strategies[i]->name,
-			common, head_arg);
-		if (!option_commit && !ret) {
-			merge_was_ok = 1;
-			/*
-			 * This is necessary here just to avoid writing
-			 * the tree, but later we will *not* exit with
-			 * status code 1 because merge_was_ok is set.
-			 */
-			ret = 1;
-		}
-
-		if (ret) {
-			/*
-			 * The backend exits with 1 when conflicts are
-			 * left to be resolved, with 2 when it does not
-			 * handle the given merge at all.
-			 */
-			if (ret == 1) {
-				int cnt = evaluate_result();
-
-				if (best_cnt <= 0 || cnt <= best_cnt) {
-					best_strategy = use_strategies[i]->name;
-					best_cnt = cnt;
-				}
-			}
-			if (merge_was_ok)
-				break;
-			else
-				continue;
-		}
-
-		/* Automerge succeeded. */
-		write_tree_trivial(result_tree);
-		automerge_was_ok = 1;
-		break;
-	}
-
-	/*
-	 * If we have a resulting tree, that means the strategy module
-	 * auto resolved the merge cleanly.
-	 */
-	if (automerge_was_ok)
-		return finish_automerge(common, result_tree, wt_strategy);
-
-	/*
-	 * Pick the result from the best strategy and have the user fix
-	 * it up.
-	 */
-	if (!best_strategy) {
-		restore_state();
-		if (use_strategies_nr > 1)
-			fprintf(stderr,
-				"No merge strategy handled the merge.\n");
-		else
-			fprintf(stderr, "Merge with strategy %s failed.\n",
-				use_strategies[0]->name);
-		return 2;
-	} else if (best_strategy == wt_strategy)
-		; /* We already have its result in the working tree. */
-	else {
-		printf("Rewinding the tree to pristine...\n");
-		restore_state();
-		printf("Using the %s to prepare resolving by hand.\n",
-			best_strategy);
-		try_merge_strategy(best_strategy, common, head_arg);
-	}
-
-	if (squash)
-		finish(NULL, NULL);
-	else {
-		int fd;
-		struct commit_list *j;
-
-		for (j = remoteheads; j; j = j->next)
-			strbuf_addf(&buf, "%s\n",
-				sha1_to_hex(j->item->object.sha1));
-		fd = open(git_path("MERGE_HEAD"), O_WRONLY | O_CREAT, 0666);
-		if (fd < 0)
-			die_errno("Could not open '%s' for writing",
-				  git_path("MERGE_HEAD"));
-		if (write_in_full(fd, buf.buf, buf.len) != buf.len)
-			die_errno("Could not write to '%s'", git_path("MERGE_HEAD"));
-		close(fd);
-		strbuf_addch(&merge_msg, '\n');
-		fd = open(git_path("MERGE_MSG"), O_WRONLY | O_CREAT, 0666);
-		if (fd < 0)
-			die_errno("Could not open '%s' for writing",
-				  git_path("MERGE_MSG"));
-		if (write_in_full(fd, merge_msg.buf, merge_msg.len) !=
-			merge_msg.len)
-			die_errno("Could not write to '%s'", git_path("MERGE_MSG"));
-		close(fd);
-		fd = open(git_path("MERGE_MODE"), O_WRONLY | O_CREAT | O_TRUNC, 0666);
-		if (fd < 0)
-			die_errno("Could not open '%s' for writing",
-				  git_path("MERGE_MODE"));
-		strbuf_reset(&buf);
-		if (!allow_fast_forward)
-			strbuf_addf(&buf, "no-ff");
-		if (write_in_full(fd, buf.buf, buf.len) != buf.len)
-			die_errno("Could not write to '%s'", git_path("MERGE_MODE"));
-		close(fd);
-	}
-
-	if (merge_was_ok) {
-		fprintf(stderr, "Automatic merge went well; "
-			"stopped before committing as requested\n");
-		return 0;
-	} else
-		return suggest_conflicts();
-}
diff --git a/builtin-mktree.c b/builtin-mktree.c
deleted file mode 100644
index 098395f..0000000
--- a/builtin-mktree.c
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * GIT - the stupid content tracker
- *
- * Copyright (c) Junio C Hamano, 2006, 2009
- */
-#include "builtin.h"
-#include "quote.h"
-#include "tree.h"
-#include "parse-options.h"
-
-static struct treeent {
-	unsigned mode;
-	unsigned char sha1[20];
-	int len;
-	char name[FLEX_ARRAY];
-} **entries;
-static int alloc, used;
-
-static void append_to_tree(unsigned mode, unsigned char *sha1, char *path)
-{
-	struct treeent *ent;
-	int len = strlen(path);
-	if (strchr(path, '/'))
-		die("path %s contains slash", path);
-
-	if (alloc <= used) {
-		alloc = alloc_nr(used);
-		entries = xrealloc(entries, sizeof(*entries) * alloc);
-	}
-	ent = entries[used++] = xmalloc(sizeof(**entries) + len + 1);
-	ent->mode = mode;
-	ent->len = len;
-	hashcpy(ent->sha1, sha1);
-	memcpy(ent->name, path, len+1);
-}
-
-static int ent_compare(const void *a_, const void *b_)
-{
-	struct treeent *a = *(struct treeent **)a_;
-	struct treeent *b = *(struct treeent **)b_;
-	return base_name_compare(a->name, a->len, a->mode,
-				 b->name, b->len, b->mode);
-}
-
-static void write_tree(unsigned char *sha1)
-{
-	struct strbuf buf;
-	size_t size;
-	int i;
-
-	qsort(entries, used, sizeof(*entries), ent_compare);
-	for (size = i = 0; i < used; i++)
-		size += 32 + entries[i]->len;
-
-	strbuf_init(&buf, size);
-	for (i = 0; i < used; i++) {
-		struct treeent *ent = entries[i];
-		strbuf_addf(&buf, "%o %s%c", ent->mode, ent->name, '\0');
-		strbuf_add(&buf, ent->sha1, 20);
-	}
-
-	write_sha1_file(buf.buf, buf.len, tree_type, sha1);
-}
-
-static const char *mktree_usage[] = {
-	"git mktree [-z] [--missing] [--batch]",
-	NULL
-};
-
-static void mktree_line(char *buf, size_t len, int line_termination, int allow_missing)
-{
-	char *ptr, *ntr;
-	unsigned mode;
-	enum object_type mode_type; /* object type derived from mode */
-	enum object_type obj_type; /* object type derived from sha */
-	char *path;
-	unsigned char sha1[20];
-
-	ptr = buf;
-	/*
-	 * Read non-recursive ls-tree output format:
-	 *     mode SP type SP sha1 TAB name
-	 */
-	mode = strtoul(ptr, &ntr, 8);
-	if (ptr == ntr || !ntr || *ntr != ' ')
-		die("input format error: %s", buf);
-	ptr = ntr + 1; /* type */
-	ntr = strchr(ptr, ' ');
-	if (!ntr || buf + len <= ntr + 40 ||
-	    ntr[41] != '\t' ||
-	    get_sha1_hex(ntr + 1, sha1))
-		die("input format error: %s", buf);
-
-	/* It is perfectly normal if we do not have a commit from a submodule */
-	if (S_ISGITLINK(mode))
-		allow_missing = 1;
-
-
-	*ntr++ = 0; /* now at the beginning of SHA1 */
-
-	path = ntr + 41;  /* at the beginning of name */
-	if (line_termination && path[0] == '"') {
-		struct strbuf p_uq = STRBUF_INIT;
-		if (unquote_c_style(&p_uq, path, NULL))
-			die("invalid quoting");
-		path = strbuf_detach(&p_uq, NULL);
-	}
-
-	/*
-	 * Object type is redundantly derivable three ways.
-	 * These should all agree.
-	 */
-	mode_type = object_type(mode);
-	if (mode_type != type_from_string(ptr)) {
-		die("entry '%s' object type (%s) doesn't match mode type (%s)",
-			path, ptr, typename(mode_type));
-	}
-
-	/* Check the type of object identified by sha1 */
-	obj_type = sha1_object_info(sha1, NULL);
-	if (obj_type < 0) {
-		if (allow_missing) {
-			; /* no problem - missing objects are presumed to be of the right type */
-		} else {
-			die("entry '%s' object %s is unavailable", path, sha1_to_hex(sha1));
-		}
-	} else {
-		if (obj_type != mode_type) {
-			/*
-			 * The object exists but is of the wrong type.
-			 * This is a problem regardless of allow_missing
-			 * because the new tree entry will never be correct.
-			 */
-			die("entry '%s' object %s is a %s but specified type was (%s)",
-				path, sha1_to_hex(sha1), typename(obj_type), typename(mode_type));
-		}
-	}
-
-	append_to_tree(mode, sha1, path);
-}
-
-int cmd_mktree(int ac, const char **av, const char *prefix)
-{
-	struct strbuf sb = STRBUF_INIT;
-	unsigned char sha1[20];
-	int line_termination = '\n';
-	int allow_missing = 0;
-	int is_batch_mode = 0;
-	int got_eof = 0;
-
-	const struct option option[] = {
-		OPT_SET_INT('z', NULL, &line_termination, "input is NUL terminated", '\0'),
-		OPT_SET_INT( 0 , "missing", &allow_missing, "allow missing objects", 1),
-		OPT_SET_INT( 0 , "batch", &is_batch_mode, "allow creation of more than one tree", 1),
-		OPT_END()
-	};
-
-	ac = parse_options(ac, av, prefix, option, mktree_usage, 0);
-
-	while (!got_eof) {
-		while (1) {
-			if (strbuf_getline(&sb, stdin, line_termination) == EOF) {
-				got_eof = 1;
-				break;
-			}
-			if (sb.buf[0] == '\0') {
-				/* empty lines denote tree boundaries in batch mode */
-				if (is_batch_mode)
-					break;
-				die("input format error: (blank line only valid in batch mode)");
-			}
-			mktree_line(sb.buf, sb.len, line_termination, allow_missing);
-		}
-		if (is_batch_mode && got_eof && used < 1) {
-			/*
-			 * Execution gets here if the last tree entry is terminated with a
-			 * new-line.  The final new-line has been made optional to be
-			 * consistent with the original non-batch behaviour of mktree.
-			 */
-			; /* skip creating an empty tree */
-		} else {
-			write_tree(sha1);
-			puts(sha1_to_hex(sha1));
-			fflush(stdout);
-		}
-		used=0; /* reset tree entry buffer for re-use in batch mode */
-	}
-	strbuf_release(&sb);
-	exit(0);
-}
diff --git a/builtin-mv.c b/builtin-mv.c
deleted file mode 100644
index b592c36..0000000
--- a/builtin-mv.c
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * "git mv" builtin command
- *
- * Copyright (C) 2006 Johannes Schindelin
- */
-#include "cache.h"
-#include "builtin.h"
-#include "dir.h"
-#include "cache-tree.h"
-#include "string-list.h"
-#include "parse-options.h"
-
-static const char * const builtin_mv_usage[] = {
-	"git mv [options] <source>... <destination>",
-	NULL
-};
-
-static const char **copy_pathspec(const char *prefix, const char **pathspec,
-				  int count, int base_name)
-{
-	int i;
-	const char **result = xmalloc((count + 1) * sizeof(const char *));
-	memcpy(result, pathspec, count * sizeof(const char *));
-	result[count] = NULL;
-	for (i = 0; i < count; i++) {
-		int length = strlen(result[i]);
-		if (length > 0 && is_dir_sep(result[i][length - 1]))
-			result[i] = xmemdupz(result[i], length - 1);
-		if (base_name)
-			result[i] = basename((char *)result[i]);
-	}
-	return get_pathspec(prefix, result);
-}
-
-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;
-	int verbose = 0, show_only = 0, force = 0, ignore_errors = 0;
-	struct option builtin_mv_options[] = {
-		OPT__DRY_RUN(&show_only),
-		OPT_BOOLEAN('f', NULL, &force, "force move/rename even if target exists"),
-		OPT_BOOLEAN('k', NULL, &ignore_errors, "skip move/rename errors"),
-		OPT_END(),
-	};
-	const char **source, **destination, **dest_path;
-	enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes;
-	struct stat st;
-	struct string_list src_for_dst = {NULL, 0, 0, 0};
-
-	git_config(git_default_config, NULL);
-
-	newfd = hold_locked_index(&lock_file, 1);
-	if (read_cache() < 0)
-		die("index file corrupt");
-
-	argc = parse_options(argc, argv, prefix, builtin_mv_options,
-			     builtin_mv_usage, 0);
-	if (--argc < 1)
-		usage_with_options(builtin_mv_usage, builtin_mv_options);
-
-	source = copy_pathspec(prefix, argv, argc, 0);
-	modes = xcalloc(argc, sizeof(enum update_mode));
-	dest_path = copy_pathspec(prefix, argv + argc, 1, 0);
-
-	if (dest_path[0][0] == '\0')
-		/* special case: "." was normalized to "" */
-		destination = copy_pathspec(dest_path[0], argv, argc, 1);
-	else 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, argc, 1);
-	} else {
-		if (argc != 1)
-			usage_with_options(builtin_mv_usage, builtin_mv_options);
-		destination = dest_path;
-	}
-
-	/* Checking */
-	for (i = 0; i < argc; i++) {
-		const char *src = source[i], *dst = destination[i];
-		int length, src_is_dir;
-		const char *bad = NULL;
-
-		if (show_only)
-			printf("Checking rename of '%s' to '%s'\n", src, dst);
-
-		length = strlen(src);
-		if (lstat(src, &st) < 0)
-			bad = "bad source";
-		else if (!strncmp(src, dst, length) &&
-				(dst[length] == 0 || dst[length] == '/')) {
-			bad = "can not move directory into itself";
-		} else if ((src_is_dir = S_ISDIR(st.st_mode))
-				&& lstat(dst, &st) == 0)
-			bad = "cannot move directory over file";
-		else if (src_is_dir) {
-			const char *src_w_slash = add_slash(src);
-			int len_w_slash = length + 1;
-			int first, last;
-
-			modes[i] = WORKING_DIRECTORY;
-
-			first = cache_name_pos(src_w_slash, len_w_slash);
-			if (first >= 0)
-				die ("Huh? %.*s is in index?",
-						len_w_slash, src_w_slash);
-
-			first = -1 - first;
-			for (last = first; last < active_nr; last++) {
-				const char *path = active_cache[last]->name;
-				if (strncmp(path, src_w_slash, len_w_slash))
-					break;
-			}
-			free((char *)src_w_slash);
-
-			if (last - first < 1)
-				bad = "source directory is empty";
-			else {
-				int j, dst_len;
-
-				if (last - first > 0) {
-					source = xrealloc(source,
-							(argc + last - first)
-							* sizeof(char *));
-					destination = xrealloc(destination,
-							(argc + last - first)
-							* sizeof(char *));
-					modes = xrealloc(modes,
-							(argc + last - first)
-							* sizeof(enum update_mode));
-				}
-
-				dst = add_slash(dst);
-				dst_len = strlen(dst);
-
-				for (j = 0; j < last - first; j++) {
-					const char *path =
-						active_cache[first + j]->name;
-					source[argc + j] = path;
-					destination[argc + j] =
-						prefix_path(dst, dst_len,
-							path + length + 1);
-					modes[argc + j] = INDEX;
-				}
-				argc += last - first;
-			}
-		} else if (cache_name_pos(src, length) < 0)
-			bad = "not under version control";
-		else if (lstat(dst, &st) == 0) {
-			bad = "destination exists";
-			if (force) {
-				/*
-				 * only files can overwrite each other:
-				 * check both source and destination
-				 */
-				if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
-					fprintf(stderr, "Warning: %s;"
-							" will overwrite!\n",
-							bad);
-					bad = NULL;
-				} else
-					bad = "Cannot overwrite";
-			}
-		} else if (string_list_has_string(&src_for_dst, dst))
-			bad = "multiple sources for the same target";
-		else
-			string_list_insert(dst, &src_for_dst);
-
-		if (bad) {
-			if (ignore_errors) {
-				if (--argc > 0) {
-					memmove(source + i, source + i + 1,
-						(argc - i) * sizeof(char *));
-					memmove(destination + i,
-						destination + i + 1,
-						(argc - i) * sizeof(char *));
-					i--;
-				}
-			} else
-				die ("%s, source=%s, destination=%s",
-				     bad, src, dst);
-		}
-	}
-
-	for (i = 0; i < argc; i++) {
-		const char *src = source[i], *dst = destination[i];
-		enum update_mode mode = modes[i];
-		int pos;
-		if (show_only || verbose)
-			printf("Renaming %s to %s\n", src, dst);
-		if (!show_only && mode != INDEX &&
-				rename(src, dst) < 0 && !ignore_errors)
-			die_errno ("renaming '%s' failed", src);
-
-		if (mode == WORKING_DIRECTORY)
-			continue;
-
-		pos = cache_name_pos(src, strlen(src));
-		assert(pos >= 0);
-		if (!show_only)
-			rename_cache_entry_at(pos, dst);
-	}
-
-	if (active_cache_changed) {
-		if (write_cache(newfd, active_cache, active_nr) ||
-		    commit_locked_index(&lock_file))
-			die("Unable to write new index file");
-	}
-
-	return 0;
-}
diff --git a/builtin-name-rev.c b/builtin-name-rev.c
deleted file mode 100644
index 06a38ac..0000000
--- a/builtin-name-rev.c
+++ /dev/null
@@ -1,305 +0,0 @@
-#include "builtin.h"
-#include "cache.h"
-#include "commit.h"
-#include "tag.h"
-#include "refs.h"
-#include "parse-options.h"
-
-#define CUTOFF_DATE_SLOP 86400 /* one day */
-
-typedef struct rev_name {
-	const char *tip_name;
-	int generation;
-	int distance;
-} rev_name;
-
-static long cutoff = LONG_MAX;
-
-/* How many generations are maximally preferred over _one_ merge traversal? */
-#define MERGE_TRAVERSAL_WEIGHT 65535
-
-static void name_rev(struct commit *commit,
-		const char *tip_name, int generation, int distance,
-		int deref)
-{
-	struct rev_name *name = (struct rev_name *)commit->util;
-	struct commit_list *parents;
-	int parent_number = 1;
-
-	if (!commit->object.parsed)
-		parse_commit(commit);
-
-	if (commit->date < cutoff)
-		return;
-
-	if (deref) {
-		char *new_name = xmalloc(strlen(tip_name)+3);
-		strcpy(new_name, tip_name);
-		strcat(new_name, "^0");
-		tip_name = new_name;
-
-		if (generation)
-			die("generation: %d, but deref?", generation);
-	}
-
-	if (name == NULL) {
-		name = xmalloc(sizeof(rev_name));
-		commit->util = name;
-		goto copy_data;
-	} else if (name->distance > distance) {
-copy_data:
-		name->tip_name = tip_name;
-		name->generation = generation;
-		name->distance = distance;
-	} else
-		return;
-
-	for (parents = commit->parents;
-			parents;
-			parents = parents->next, parent_number++) {
-		if (parent_number > 1) {
-			int len = strlen(tip_name);
-			char *new_name = xmalloc(len +
-				1 + decimal_length(generation) +  /* ~<n> */
-				1 + 2 +				  /* ^NN */
-				1);
-
-			if (len > 2 && !strcmp(tip_name + len - 2, "^0"))
-				len -= 2;
-			if (generation > 0)
-				sprintf(new_name, "%.*s~%d^%d", len, tip_name,
-						generation, parent_number);
-			else
-				sprintf(new_name, "%.*s^%d", len, tip_name,
-						parent_number);
-
-			name_rev(parents->item, new_name, 0,
-				distance + MERGE_TRAVERSAL_WEIGHT, 0);
-		} else {
-			name_rev(parents->item, tip_name, generation + 1,
-				distance + 1, 0);
-		}
-	}
-}
-
-struct name_ref_data {
-	int tags_only;
-	int name_only;
-	const char *ref_filter;
-};
-
-static int name_ref(const char *path, const unsigned char *sha1, int flags, void *cb_data)
-{
-	struct object *o = parse_object(sha1);
-	struct name_ref_data *data = cb_data;
-	int deref = 0;
-
-	if (data->tags_only && prefixcmp(path, "refs/tags/"))
-		return 0;
-
-	if (data->ref_filter && fnmatch(data->ref_filter, path, 0))
-		return 0;
-
-	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 == OBJ_COMMIT) {
-		struct commit *commit = (struct commit *)o;
-
-		if (!prefixcmp(path, "refs/heads/"))
-			path = path + 11;
-		else if (data->tags_only
-		    && data->name_only
-		    && !prefixcmp(path, "refs/tags/"))
-			path = path + 10;
-		else if (!prefixcmp(path, "refs/"))
-			path = path + 5;
-
-		name_rev(commit, xstrdup(path), 0, 0, deref);
-	}
-	return 0;
-}
-
-/* returns a static buffer */
-static const char *get_rev_name(const struct object *o)
-{
-	static char buffer[1024];
-	struct rev_name *n;
-	struct commit *c;
-
-	if (o->type != OBJ_COMMIT)
-		return NULL;
-	c = (struct commit *) o;
-	n = c->util;
-	if (!n)
-		return NULL;
-
-	if (!n->generation)
-		return n->tip_name;
-	else {
-		int len = strlen(n->tip_name);
-		if (len > 2 && !strcmp(n->tip_name + len - 2, "^0"))
-			len -= 2;
-		snprintf(buffer, sizeof(buffer), "%.*s~%d", len, n->tip_name,
-				n->generation);
-
-		return buffer;
-	}
-}
-
-static void show_name(const struct object *obj,
-		      const char *caller_name,
-		      int always, int allow_undefined, int name_only)
-{
-	const char *name;
-	const unsigned char *sha1 = obj->sha1;
-
-	if (!name_only)
-		printf("%s ", caller_name ? caller_name : sha1_to_hex(sha1));
-	name = get_rev_name(obj);
-	if (name)
-		printf("%s\n", name);
-	else if (allow_undefined)
-		printf("undefined\n");
-	else if (always)
-		printf("%s\n", find_unique_abbrev(sha1, DEFAULT_ABBREV));
-	else
-		die("cannot describe '%s'", sha1_to_hex(sha1));
-}
-
-static char const * const name_rev_usage[] = {
-	"git name-rev [options] ( --all | --stdin | <commit>... )",
-	NULL
-};
-
-static void name_rev_line(char *p, struct name_ref_data *data)
-{
-	int forty = 0;
-	char *p_start;
-	for (p_start = p; *p; p++) {
-#define ishex(x) (isdigit((x)) || ((x) >= 'a' && (x) <= 'f'))
-		if (!ishex(*p))
-			forty = 0;
-		else if (++forty == 40 &&
-			 !ishex(*(p+1))) {
-			unsigned char sha1[40];
-			const char *name = NULL;
-			char c = *(p+1);
-			int p_len = p - p_start + 1;
-
-			forty = 0;
-
-			*(p+1) = 0;
-			if (!get_sha1(p - 39, sha1)) {
-				struct object *o =
-					lookup_object(sha1);
-				if (o)
-					name = get_rev_name(o);
-			}
-			*(p+1) = c;
-
-			if (!name)
-				continue;
-
-			if (data->name_only)
-				printf("%.*s%s", p_len - 40, p_start, name);
-			else
-				printf("%.*s (%s)", p_len, p_start, name);
-			p_start = p + 1;
-		}
-	}
-
-	/* flush */
-	if (p_start != p)
-		fwrite(p_start, p - p_start, 1, stdout);
-}
-
-int cmd_name_rev(int argc, const char **argv, const char *prefix)
-{
-	struct object_array revs = { 0, 0, NULL };
-	int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0;
-	struct name_ref_data data = { 0, 0, NULL };
-	struct option opts[] = {
-		OPT_BOOLEAN(0, "name-only", &data.name_only, "print only names (no SHA-1)"),
-		OPT_BOOLEAN(0, "tags", &data.tags_only, "only use tags to name the commits"),
-		OPT_STRING(0, "refs", &data.ref_filter, "pattern",
-				   "only use refs matching <pattern>"),
-		OPT_GROUP(""),
-		OPT_BOOLEAN(0, "all", &all, "list all commits reachable from all refs"),
-		OPT_BOOLEAN(0, "stdin", &transform_stdin, "read from stdin"),
-		OPT_BOOLEAN(0, "undefined", &allow_undefined, "allow to print `undefined` names"),
-		OPT_BOOLEAN(0, "always",     &always,
-			   "show abbreviated commit object as fallback"),
-		OPT_END(),
-	};
-
-	git_config(git_default_config, NULL);
-	argc = parse_options(argc, argv, prefix, opts, name_rev_usage, 0);
-	if (!!all + !!transform_stdin + !!argc > 1) {
-		error("Specify either a list, or --all, not both!");
-		usage_with_options(name_rev_usage, opts);
-	}
-	if (all || transform_stdin)
-		cutoff = 0;
-
-	for (; argc; argc--, argv++) {
-		unsigned char sha1[20];
-		struct object *o;
-		struct commit *commit;
-
-		if (get_sha1(*argv, sha1)) {
-			fprintf(stderr, "Could not get sha1 for %s. Skipping.\n",
-					*argv);
-			continue;
-		}
-
-		o = deref_tag(parse_object(sha1), *argv, 0);
-		if (!o || o->type != OBJ_COMMIT) {
-			fprintf(stderr, "Could not get commit for %s. Skipping.\n",
-					*argv);
-			continue;
-		}
-
-		commit = (struct commit *)o;
-		if (cutoff > commit->date)
-			cutoff = commit->date;
-		add_object_array((struct object *)commit, *argv, &revs);
-	}
-
-	if (cutoff)
-		cutoff = cutoff - CUTOFF_DATE_SLOP;
-	for_each_ref(name_ref, &data);
-
-	if (transform_stdin) {
-		char buffer[2048];
-
-		while (!feof(stdin)) {
-			char *p = fgets(buffer, sizeof(buffer), stdin);
-			if (!p)
-				break;
-			name_rev_line(p, &data);
-		}
-	} else if (all) {
-		int i, max;
-
-		max = get_max_object_index();
-		for (i = 0; i < max; i++) {
-			struct object *obj = get_indexed_object(i);
-			if (!obj)
-				continue;
-			show_name(obj, NULL,
-				  always, allow_undefined, data.name_only);
-		}
-	} else {
-		int i;
-		for (i = 0; i < revs.nr; i++)
-			show_name(revs.objects[i].item, revs.objects[i].name,
-				  always, allow_undefined, data.name_only);
-	}
-
-	return 0;
-}
diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c
deleted file mode 100644
index ef4bf6b..0000000
--- a/builtin-pack-objects.c
+++ /dev/null
@@ -1,2325 +0,0 @@
-#include "builtin.h"
-#include "cache.h"
-#include "attr.h"
-#include "object.h"
-#include "blob.h"
-#include "commit.h"
-#include "tag.h"
-#include "tree.h"
-#include "delta.h"
-#include "pack.h"
-#include "pack-revindex.h"
-#include "csum-file.h"
-#include "tree-walk.h"
-#include "diff.h"
-#include "revision.h"
-#include "list-objects.h"
-#include "progress.h"
-#include "refs.h"
-
-#ifdef THREADED_DELTA_SEARCH
-#include "thread-utils.h"
-#include <pthread.h>
-#endif
-
-static const char pack_usage[] = "\
-git pack-objects [{ -q | --progress | --all-progress }] \n\
-	[--max-pack-size=N] [--local] [--incremental] \n\
-	[--window=N] [--window-memory=N] [--depth=N] \n\
-	[--no-reuse-delta] [--no-reuse-object] [--delta-base-offset] \n\
-	[--threads=N] [--non-empty] [--revs [--unpacked | --all]*] [--reflog] \n\
-	[--stdout | base-name] [--include-tag] \n\
-	[--keep-unreachable | --unpack-unreachable] \n\
-	[<ref-list | <object-list]";
-
-struct object_entry {
-	struct pack_idx_entry idx;
-	unsigned long size;	/* uncompressed size */
-	struct packed_git *in_pack; 	/* already in pack */
-	off_t in_pack_offset;
-	struct object_entry *delta;	/* delta base object */
-	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
-					     */
-	void *delta_data;	/* cached delta (uncompressed) */
-	unsigned long delta_size;	/* delta data size (uncompressed) */
-	unsigned long z_delta_size;	/* delta data size (compressed) */
-	unsigned int hash;	/* name hint hash */
-	enum object_type type;
-	enum object_type in_pack_type;	/* could be delta */
-	unsigned char in_pack_header_size;
-	unsigned char preferred_base; /* we do not pack this, but is available
-				       * to be used as the base object to delta
-				       * objects against.
-				       */
-	unsigned char no_try_delta;
-};
-
-/*
- * 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.
- */
-static struct object_entry *objects;
-static struct pack_idx_entry **written_list;
-static uint32_t nr_objects, nr_alloc, nr_result, nr_written;
-
-static int non_empty;
-static int reuse_delta = 1, reuse_object = 1;
-static int keep_unreachable, unpack_unreachable, include_tag;
-static int local;
-static int incremental;
-static int ignore_packed_keep;
-static int allow_ofs_delta;
-static const char *base_name;
-static int progress = 1;
-static int window = 10;
-static uint32_t pack_size_limit, pack_size_limit_cfg;
-static int depth = 50;
-static int delta_search_threads;
-static int pack_to_stdout;
-static int num_preferred_base;
-static struct progress *progress_state;
-static int pack_compression_level = Z_DEFAULT_COMPRESSION;
-static int pack_compression_seen;
-
-static unsigned long delta_cache_size = 0;
-static unsigned long max_delta_cache_size = 0;
-static unsigned long cache_max_small_delta_size = 1000;
-
-static unsigned long window_memory_limit = 0;
-
-/*
- * The object names in objects array are hashed with this hashtable,
- * to help looking up the entry by object name.
- * This hashtable is built after all the objects are seen.
- */
-static int *object_ix;
-static int object_ix_hashsz;
-
-/*
- * stats
- */
-static uint32_t written, written_delta;
-static uint32_t reused, reused_delta;
-
-
-static void *get_delta(struct object_entry *entry)
-{
-	unsigned long size, base_size, delta_size;
-	void *buf, *base_buf, *delta_buf;
-	enum object_type type;
-
-	buf = read_sha1_file(entry->idx.sha1, &type, &size);
-	if (!buf)
-		die("unable to read %s", sha1_to_hex(entry->idx.sha1));
-	base_buf = read_sha1_file(entry->delta->idx.sha1, &type, &base_size);
-	if (!base_buf)
-		die("unable to read %s", sha1_to_hex(entry->delta->idx.sha1));
-	delta_buf = diff_delta(base_buf, base_size,
-			       buf, size, &delta_size, 0);
-	if (!delta_buf || delta_size != entry->delta_size)
-		die("delta size changed");
-	free(buf);
-	free(base_buf);
-	return delta_buf;
-}
-
-static unsigned long do_compress(void **pptr, unsigned long size)
-{
-	z_stream stream;
-	void *in, *out;
-	unsigned long maxsize;
-
-	memset(&stream, 0, sizeof(stream));
-	deflateInit(&stream, pack_compression_level);
-	maxsize = deflateBound(&stream, size);
-
-	in = *pptr;
-	out = xmalloc(maxsize);
-	*pptr = out;
-
-	stream.next_in = in;
-	stream.avail_in = size;
-	stream.next_out = out;
-	stream.avail_out = maxsize;
-	while (deflate(&stream, Z_FINISH) == Z_OK)
-		; /* nothing */
-	deflateEnd(&stream);
-
-	free(in);
-	return stream.total_out;
-}
-
-/*
- * The per-object header is a pretty dense thing, which is
- *  - first byte: low four bits are "size", then three bits of "type",
- *    and the high bit is "size continues".
- *  - each byte afterwards: low seven bits are size continuation,
- *    with the high bit being "size continues"
- */
-static int encode_header(enum object_type type, unsigned long size, unsigned char *hdr)
-{
-	int n = 1;
-	unsigned char c;
-
-	if (type < OBJ_COMMIT || type > OBJ_REF_DELTA)
-		die("bad type %d", type);
-
-	c = (type << 4) | (size & 15);
-	size >>= 4;
-	while (size) {
-		*hdr++ = c | 0x80;
-		c = size & 0x7f;
-		size >>= 7;
-		n++;
-	}
-	*hdr = c;
-	return n;
-}
-
-/*
- * we are going to reuse the existing object data as is.  make
- * sure it is not corrupt.
- */
-static int check_pack_inflate(struct packed_git *p,
-		struct pack_window **w_curs,
-		off_t offset,
-		off_t len,
-		unsigned long expect)
-{
-	z_stream stream;
-	unsigned char fakebuf[4096], *in;
-	int st;
-
-	memset(&stream, 0, sizeof(stream));
-	git_inflate_init(&stream);
-	do {
-		in = use_pack(p, w_curs, offset, &stream.avail_in);
-		stream.next_in = in;
-		stream.next_out = fakebuf;
-		stream.avail_out = sizeof(fakebuf);
-		st = git_inflate(&stream, Z_FINISH);
-		offset += stream.next_in - in;
-	} while (st == Z_OK || st == Z_BUF_ERROR);
-	git_inflate_end(&stream);
-	return (st == Z_STREAM_END &&
-		stream.total_out == expect &&
-		stream.total_in == len) ? 0 : -1;
-}
-
-static void copy_pack_data(struct sha1file *f,
-		struct packed_git *p,
-		struct pack_window **w_curs,
-		off_t offset,
-		off_t len)
-{
-	unsigned char *in;
-	unsigned int avail;
-
-	while (len) {
-		in = use_pack(p, w_curs, offset, &avail);
-		if (avail > len)
-			avail = (unsigned int)len;
-		sha1write(f, in, avail);
-		offset += avail;
-		len -= avail;
-	}
-}
-
-static unsigned long write_object(struct sha1file *f,
-				  struct object_entry *entry,
-				  off_t write_offset)
-{
-	unsigned long size, limit, datalen;
-	void *buf;
-	unsigned char header[10], dheader[10];
-	unsigned hdrlen;
-	enum object_type type;
-	int usable_delta, to_reuse;
-
-	if (!pack_to_stdout)
-		crc32_begin(f);
-
-	type = entry->type;
-
-	/* write limit if limited packsize and not first object */
-	if (!pack_size_limit || !nr_written)
-		limit = 0;
-	else if (pack_size_limit <= write_offset)
-		/*
-		 * the earlier object did not fit the limit; avoid
-		 * mistaking this with unlimited (i.e. limit = 0).
-		 */
-		limit = 1;
-	else
-		limit = pack_size_limit - write_offset;
-
-	if (!entry->delta)
-		usable_delta = 0;	/* no delta */
-	else if (!pack_size_limit)
-	       usable_delta = 1;	/* unlimited packfile */
-	else if (entry->delta->idx.offset == (off_t)-1)
-		usable_delta = 0;	/* base was written to another pack */
-	else if (entry->delta->idx.offset)
-		usable_delta = 1;	/* base already exists in this pack */
-	else
-		usable_delta = 0;	/* base could end up in another pack */
-
-	if (!reuse_object)
-		to_reuse = 0;	/* explicit */
-	else if (!entry->in_pack)
-		to_reuse = 0;	/* can't reuse what we don't have */
-	else if (type == OBJ_REF_DELTA || type == OBJ_OFS_DELTA)
-				/* check_object() decided it for us ... */
-		to_reuse = usable_delta;
-				/* ... but pack split may override that */
-	else if (type != entry->in_pack_type)
-		to_reuse = 0;	/* pack has delta which is unusable */
-	else if (entry->delta)
-		to_reuse = 0;	/* we want to pack afresh */
-	else
-		to_reuse = 1;	/* we have it in-pack undeltified,
-				 * and we do not need to deltify it.
-				 */
-
-	if (!to_reuse) {
-		no_reuse:
-		if (!usable_delta) {
-			buf = read_sha1_file(entry->idx.sha1, &type, &size);
-			if (!buf)
-				die("unable to read %s", sha1_to_hex(entry->idx.sha1));
-			/*
-			 * make sure no cached delta data remains from a
-			 * previous attempt before a pack split occurred.
-			 */
-			free(entry->delta_data);
-			entry->delta_data = NULL;
-			entry->z_delta_size = 0;
-		} else if (entry->delta_data) {
-			size = entry->delta_size;
-			buf = entry->delta_data;
-			entry->delta_data = NULL;
-			type = (allow_ofs_delta && entry->delta->idx.offset) ?
-				OBJ_OFS_DELTA : OBJ_REF_DELTA;
-		} else {
-			buf = get_delta(entry);
-			size = entry->delta_size;
-			type = (allow_ofs_delta && entry->delta->idx.offset) ?
-				OBJ_OFS_DELTA : OBJ_REF_DELTA;
-		}
-
-		if (entry->z_delta_size)
-			datalen = entry->z_delta_size;
-		else
-			datalen = do_compress(&buf, size);
-
-		/*
-		 * The object header is a byte of 'type' followed by zero or
-		 * more bytes of length.
-		 */
-		hdrlen = encode_header(type, size, header);
-
-		if (type == OBJ_OFS_DELTA) {
-			/*
-			 * Deltas with relative base contain an additional
-			 * encoding of the relative offset for the delta
-			 * base from this object's position in the pack.
-			 */
-			off_t ofs = entry->idx.offset - entry->delta->idx.offset;
-			unsigned pos = sizeof(dheader) - 1;
-			dheader[pos] = ofs & 127;
-			while (ofs >>= 7)
-				dheader[--pos] = 128 | (--ofs & 127);
-			if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit) {
-				free(buf);
-				return 0;
-			}
-			sha1write(f, header, hdrlen);
-			sha1write(f, dheader + pos, sizeof(dheader) - pos);
-			hdrlen += sizeof(dheader) - pos;
-		} else if (type == OBJ_REF_DELTA) {
-			/*
-			 * Deltas with a base reference contain
-			 * an additional 20 bytes for the base sha1.
-			 */
-			if (limit && hdrlen + 20 + datalen + 20 >= limit) {
-				free(buf);
-				return 0;
-			}
-			sha1write(f, header, hdrlen);
-			sha1write(f, entry->delta->idx.sha1, 20);
-			hdrlen += 20;
-		} else {
-			if (limit && hdrlen + datalen + 20 >= limit) {
-				free(buf);
-				return 0;
-			}
-			sha1write(f, header, hdrlen);
-		}
-		sha1write(f, buf, datalen);
-		free(buf);
-	}
-	else {
-		struct packed_git *p = entry->in_pack;
-		struct pack_window *w_curs = NULL;
-		struct revindex_entry *revidx;
-		off_t offset;
-
-		if (entry->delta)
-			type = (allow_ofs_delta && entry->delta->idx.offset) ?
-				OBJ_OFS_DELTA : OBJ_REF_DELTA;
-		hdrlen = encode_header(type, entry->size, header);
-
-		offset = entry->in_pack_offset;
-		revidx = find_pack_revindex(p, offset);
-		datalen = revidx[1].offset - offset;
-		if (!pack_to_stdout && p->index_version > 1 &&
-		    check_pack_crc(p, &w_curs, offset, datalen, revidx->nr)) {
-			error("bad packed object CRC for %s", sha1_to_hex(entry->idx.sha1));
-			unuse_pack(&w_curs);
-			goto no_reuse;
-		}
-
-		offset += entry->in_pack_header_size;
-		datalen -= entry->in_pack_header_size;
-		if (!pack_to_stdout && p->index_version == 1 &&
-		    check_pack_inflate(p, &w_curs, offset, datalen, entry->size)) {
-			error("corrupt packed object for %s", sha1_to_hex(entry->idx.sha1));
-			unuse_pack(&w_curs);
-			goto no_reuse;
-		}
-
-		if (type == OBJ_OFS_DELTA) {
-			off_t ofs = entry->idx.offset - entry->delta->idx.offset;
-			unsigned pos = sizeof(dheader) - 1;
-			dheader[pos] = ofs & 127;
-			while (ofs >>= 7)
-				dheader[--pos] = 128 | (--ofs & 127);
-			if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit) {
-				unuse_pack(&w_curs);
-				return 0;
-			}
-			sha1write(f, header, hdrlen);
-			sha1write(f, dheader + pos, sizeof(dheader) - pos);
-			hdrlen += sizeof(dheader) - pos;
-			reused_delta++;
-		} else if (type == OBJ_REF_DELTA) {
-			if (limit && hdrlen + 20 + datalen + 20 >= limit) {
-				unuse_pack(&w_curs);
-				return 0;
-			}
-			sha1write(f, header, hdrlen);
-			sha1write(f, entry->delta->idx.sha1, 20);
-			hdrlen += 20;
-			reused_delta++;
-		} else {
-			if (limit && hdrlen + datalen + 20 >= limit) {
-				unuse_pack(&w_curs);
-				return 0;
-			}
-			sha1write(f, header, hdrlen);
-		}
-		copy_pack_data(f, p, &w_curs, offset, datalen);
-		unuse_pack(&w_curs);
-		reused++;
-	}
-	if (usable_delta)
-		written_delta++;
-	written++;
-	if (!pack_to_stdout)
-		entry->idx.crc32 = crc32_end(f);
-	return hdrlen + datalen;
-}
-
-static int write_one(struct sha1file *f,
-			       struct object_entry *e,
-			       off_t *offset)
-{
-	unsigned long size;
-
-	/* offset is non zero if object is written already. */
-	if (e->idx.offset || e->preferred_base)
-		return 1;
-
-	/* if we are deltified, write out base object first. */
-	if (e->delta && !write_one(f, e->delta, offset))
-		return 0;
-
-	e->idx.offset = *offset;
-	size = write_object(f, e, *offset);
-	if (!size) {
-		e->idx.offset = 0;
-		return 0;
-	}
-	written_list[nr_written++] = &e->idx;
-
-	/* make sure off_t is sufficiently large not to wrap */
-	if (*offset > *offset + size)
-		die("pack too large for current definition of off_t");
-	*offset += size;
-	return 1;
-}
-
-/* forward declaration for write_pack_file */
-static int adjust_perm(const char *path, mode_t mode);
-
-static void write_pack_file(void)
-{
-	uint32_t i = 0, j;
-	struct sha1file *f;
-	off_t offset;
-	struct pack_header hdr;
-	uint32_t nr_remaining = nr_result;
-	time_t last_mtime = 0;
-
-	if (progress > pack_to_stdout)
-		progress_state = start_progress("Writing objects", nr_result);
-	written_list = xmalloc(nr_objects * sizeof(*written_list));
-
-	do {
-		unsigned char sha1[20];
-		char *pack_tmp_name = NULL;
-
-		if (pack_to_stdout) {
-			f = sha1fd_throughput(1, "<stdout>", progress_state);
-		} else {
-			char tmpname[PATH_MAX];
-			int fd;
-			fd = odb_mkstemp(tmpname, sizeof(tmpname),
-					 "pack/tmp_pack_XXXXXX");
-			pack_tmp_name = xstrdup(tmpname);
-			f = sha1fd(fd, pack_tmp_name);
-		}
-
-		hdr.hdr_signature = htonl(PACK_SIGNATURE);
-		hdr.hdr_version = htonl(PACK_VERSION);
-		hdr.hdr_entries = htonl(nr_remaining);
-		sha1write(f, &hdr, sizeof(hdr));
-		offset = sizeof(hdr);
-		nr_written = 0;
-		for (; i < nr_objects; i++) {
-			if (!write_one(f, objects + i, &offset))
-				break;
-			display_progress(progress_state, written);
-		}
-
-		/*
-		 * Did we write the wrong # entries in the header?
-		 * If so, rewrite it like in fast-import
-		 */
-		if (pack_to_stdout) {
-			sha1close(f, sha1, CSUM_CLOSE);
-		} else if (nr_written == nr_remaining) {
-			sha1close(f, sha1, CSUM_FSYNC);
-		} else {
-			int fd = sha1close(f, sha1, 0);
-			fixup_pack_header_footer(fd, sha1, pack_tmp_name,
-						 nr_written, sha1, offset);
-			close(fd);
-		}
-
-		if (!pack_to_stdout) {
-			mode_t mode = umask(0);
-			struct stat st;
-			char *idx_tmp_name, tmpname[PATH_MAX];
-
-			umask(mode);
-			mode = 0444 & ~mode;
-
-			idx_tmp_name = write_idx_file(NULL, written_list,
-						      nr_written, sha1);
-
-			snprintf(tmpname, sizeof(tmpname), "%s-%s.pack",
-				 base_name, sha1_to_hex(sha1));
-			free_pack_by_name(tmpname);
-			if (adjust_perm(pack_tmp_name, mode))
-				die_errno("unable to make temporary pack file readable");
-			if (rename(pack_tmp_name, tmpname))
-				die_errno("unable to rename temporary pack file");
-
-			/*
-			 * Packs are runtime accessed in their mtime
-			 * order since newer packs are more likely to contain
-			 * younger objects.  So if we are creating multiple
-			 * packs then we should modify the mtime of later ones
-			 * to preserve this property.
-			 */
-			if (stat(tmpname, &st) < 0) {
-				warning("failed to stat %s: %s",
-					tmpname, strerror(errno));
-			} else if (!last_mtime) {
-				last_mtime = st.st_mtime;
-			} else {
-				struct utimbuf utb;
-				utb.actime = st.st_atime;
-				utb.modtime = --last_mtime;
-				if (utime(tmpname, &utb) < 0)
-					warning("failed utime() on %s: %s",
-						tmpname, strerror(errno));
-			}
-
-			snprintf(tmpname, sizeof(tmpname), "%s-%s.idx",
-				 base_name, sha1_to_hex(sha1));
-			if (adjust_perm(idx_tmp_name, mode))
-				die_errno("unable to make temporary index file readable");
-			if (rename(idx_tmp_name, tmpname))
-				die_errno("unable to rename temporary index file");
-
-			free(idx_tmp_name);
-			free(pack_tmp_name);
-			puts(sha1_to_hex(sha1));
-		}
-
-		/* mark written objects as written to previous pack */
-		for (j = 0; j < nr_written; j++) {
-			written_list[j]->offset = (off_t)-1;
-		}
-		nr_remaining -= nr_written;
-	} while (nr_remaining && i < nr_objects);
-
-	free(written_list);
-	stop_progress(&progress_state);
-	if (written != nr_result)
-		die("wrote %"PRIu32" objects while expecting %"PRIu32,
-			written, nr_result);
-	/*
-	 * We have scanned through [0 ... i).  Since we have written
-	 * the correct number of objects,  the remaining [i ... nr_objects)
-	 * items must be either already written (due to out-of-order delta base)
-	 * or a preferred base.  Count those which are neither and complain if any.
-	 */
-	for (j = 0; i < nr_objects; i++) {
-		struct object_entry *e = objects + i;
-		j += !e->idx.offset && !e->preferred_base;
-	}
-	if (j)
-		die("wrote %"PRIu32" objects as expected but %"PRIu32
-			" unwritten", written, j);
-}
-
-static int locate_object_entry_hash(const unsigned char *sha1)
-{
-	int i;
-	unsigned int ui;
-	memcpy(&ui, sha1, sizeof(unsigned int));
-	i = ui % object_ix_hashsz;
-	while (0 < object_ix[i]) {
-		if (!hashcmp(sha1, objects[object_ix[i] - 1].idx.sha1))
-			return i;
-		if (++i == object_ix_hashsz)
-			i = 0;
-	}
-	return -1 - i;
-}
-
-static struct object_entry *locate_object_entry(const unsigned char *sha1)
-{
-	int i;
-
-	if (!object_ix_hashsz)
-		return NULL;
-
-	i = locate_object_entry_hash(sha1);
-	if (0 <= i)
-		return &objects[object_ix[i]-1];
-	return NULL;
-}
-
-static void rehash_objects(void)
-{
-	uint32_t i;
-	struct object_entry *oe;
-
-	object_ix_hashsz = nr_objects * 3;
-	if (object_ix_hashsz < 1024)
-		object_ix_hashsz = 1024;
-	object_ix = xrealloc(object_ix, sizeof(int) * object_ix_hashsz);
-	memset(object_ix, 0, sizeof(int) * object_ix_hashsz);
-	for (i = 0, oe = objects; i < nr_objects; i++, oe++) {
-		int ix = locate_object_entry_hash(oe->idx.sha1);
-		if (0 <= ix)
-			continue;
-		ix = -1 - ix;
-		object_ix[ix] = i + 1;
-	}
-}
-
-static unsigned name_hash(const char *name)
-{
-	unsigned c, hash = 0;
-
-	if (!name)
-		return 0;
-
-	/*
-	 * This effectively just creates a sortable number from the
-	 * last sixteen non-whitespace characters. Last characters
-	 * count "most", so things that end in ".c" sort together.
-	 */
-	while ((c = *name++) != 0) {
-		if (isspace(c))
-			continue;
-		hash = (hash >> 2) + (c << 24);
-	}
-	return hash;
-}
-
-static void setup_delta_attr_check(struct git_attr_check *check)
-{
-	static struct git_attr *attr_delta;
-
-	if (!attr_delta)
-		attr_delta = git_attr("delta", 5);
-
-	check[0].attr = attr_delta;
-}
-
-static int no_try_delta(const char *path)
-{
-	struct git_attr_check check[1];
-
-	setup_delta_attr_check(check);
-	if (git_checkattr(path, ARRAY_SIZE(check), check))
-		return 0;
-	if (ATTR_FALSE(check->value))
-		return 1;
-	return 0;
-}
-
-static int add_object_entry(const unsigned char *sha1, enum object_type type,
-			    const char *name, int exclude)
-{
-	struct object_entry *entry;
-	struct packed_git *p, *found_pack = NULL;
-	off_t found_offset = 0;
-	int ix;
-	unsigned hash = name_hash(name);
-
-	ix = nr_objects ? locate_object_entry_hash(sha1) : -1;
-	if (ix >= 0) {
-		if (exclude) {
-			entry = objects + object_ix[ix] - 1;
-			if (!entry->preferred_base)
-				nr_result--;
-			entry->preferred_base = 1;
-		}
-		return 0;
-	}
-
-	if (!exclude && local && has_loose_object_nonlocal(sha1))
-		return 0;
-
-	for (p = packed_git; p; p = p->next) {
-		off_t offset = find_pack_entry_one(sha1, p);
-		if (offset) {
-			if (!found_pack) {
-				found_offset = offset;
-				found_pack = p;
-			}
-			if (exclude)
-				break;
-			if (incremental)
-				return 0;
-			if (local && !p->pack_local)
-				return 0;
-			if (ignore_packed_keep && p->pack_local && p->pack_keep)
-				return 0;
-		}
-	}
-
-	if (nr_objects >= nr_alloc) {
-		nr_alloc = (nr_alloc  + 1024) * 3 / 2;
-		objects = xrealloc(objects, nr_alloc * sizeof(*entry));
-	}
-
-	entry = objects + nr_objects++;
-	memset(entry, 0, sizeof(*entry));
-	hashcpy(entry->idx.sha1, sha1);
-	entry->hash = hash;
-	if (type)
-		entry->type = type;
-	if (exclude)
-		entry->preferred_base = 1;
-	else
-		nr_result++;
-	if (found_pack) {
-		entry->in_pack = found_pack;
-		entry->in_pack_offset = found_offset;
-	}
-
-	if (object_ix_hashsz * 3 <= nr_objects * 4)
-		rehash_objects();
-	else
-		object_ix[-1 - ix] = nr_objects;
-
-	display_progress(progress_state, nr_objects);
-
-	if (name && no_try_delta(name))
-		entry->no_try_delta = 1;
-
-	return 1;
-}
-
-struct pbase_tree_cache {
-	unsigned char sha1[20];
-	int ref;
-	int temporary;
-	void *tree_data;
-	unsigned long tree_size;
-};
-
-static struct pbase_tree_cache *(pbase_tree_cache[256]);
-static int pbase_tree_cache_ix(const unsigned char *sha1)
-{
-	return sha1[0] % ARRAY_SIZE(pbase_tree_cache);
-}
-static int pbase_tree_cache_ix_incr(int ix)
-{
-	return (ix+1) % ARRAY_SIZE(pbase_tree_cache);
-}
-
-static struct pbase_tree {
-	struct pbase_tree *next;
-	/* This is a phony "cache" entry; we are not
-	 * going to evict it nor find it through _get()
-	 * mechanism -- this is for the toplevel node that
-	 * would almost always change with any commit.
-	 */
-	struct pbase_tree_cache pcache;
-} *pbase_tree;
-
-static struct pbase_tree_cache *pbase_tree_get(const unsigned char *sha1)
-{
-	struct pbase_tree_cache *ent, *nent;
-	void *data;
-	unsigned long size;
-	enum object_type type;
-	int neigh;
-	int my_ix = pbase_tree_cache_ix(sha1);
-	int available_ix = -1;
-
-	/* pbase-tree-cache acts as a limited hashtable.
-	 * your object will be found at your index or within a few
-	 * slots after that slot if it is cached.
-	 */
-	for (neigh = 0; neigh < 8; neigh++) {
-		ent = pbase_tree_cache[my_ix];
-		if (ent && !hashcmp(ent->sha1, sha1)) {
-			ent->ref++;
-			return ent;
-		}
-		else if (((available_ix < 0) && (!ent || !ent->ref)) ||
-			 ((0 <= available_ix) &&
-			  (!ent && pbase_tree_cache[available_ix])))
-			available_ix = my_ix;
-		if (!ent)
-			break;
-		my_ix = pbase_tree_cache_ix_incr(my_ix);
-	}
-
-	/* Did not find one.  Either we got a bogus request or
-	 * we need to read and perhaps cache.
-	 */
-	data = read_sha1_file(sha1, &type, &size);
-	if (!data)
-		return NULL;
-	if (type != OBJ_TREE) {
-		free(data);
-		return NULL;
-	}
-
-	/* We need to either cache or return a throwaway copy */
-
-	if (available_ix < 0)
-		ent = NULL;
-	else {
-		ent = pbase_tree_cache[available_ix];
-		my_ix = available_ix;
-	}
-
-	if (!ent) {
-		nent = xmalloc(sizeof(*nent));
-		nent->temporary = (available_ix < 0);
-	}
-	else {
-		/* evict and reuse */
-		free(ent->tree_data);
-		nent = ent;
-	}
-	hashcpy(nent->sha1, sha1);
-	nent->tree_data = data;
-	nent->tree_size = size;
-	nent->ref = 1;
-	if (!nent->temporary)
-		pbase_tree_cache[my_ix] = nent;
-	return nent;
-}
-
-static void pbase_tree_put(struct pbase_tree_cache *cache)
-{
-	if (!cache->temporary) {
-		cache->ref--;
-		return;
-	}
-	free(cache->tree_data);
-	free(cache);
-}
-
-static int name_cmp_len(const char *name)
-{
-	int i;
-	for (i = 0; name[i] && name[i] != '\n' && name[i] != '/'; i++)
-		;
-	return i;
-}
-
-static void add_pbase_object(struct tree_desc *tree,
-			     const char *name,
-			     int cmplen,
-			     const char *fullname)
-{
-	struct name_entry entry;
-	int cmp;
-
-	while (tree_entry(tree,&entry)) {
-		if (S_ISGITLINK(entry.mode))
-			continue;
-		cmp = tree_entry_len(entry.path, entry.sha1) != cmplen ? 1 :
-		      memcmp(name, entry.path, cmplen);
-		if (cmp > 0)
-			continue;
-		if (cmp < 0)
-			return;
-		if (name[cmplen] != '/') {
-			add_object_entry(entry.sha1,
-					 object_type(entry.mode),
-					 fullname, 1);
-			return;
-		}
-		if (S_ISDIR(entry.mode)) {
-			struct tree_desc sub;
-			struct pbase_tree_cache *tree;
-			const char *down = name+cmplen+1;
-			int downlen = name_cmp_len(down);
-
-			tree = pbase_tree_get(entry.sha1);
-			if (!tree)
-				return;
-			init_tree_desc(&sub, tree->tree_data, tree->tree_size);
-
-			add_pbase_object(&sub, down, downlen, fullname);
-			pbase_tree_put(tree);
-		}
-	}
-}
-
-static unsigned *done_pbase_paths;
-static int done_pbase_paths_num;
-static int done_pbase_paths_alloc;
-static int done_pbase_path_pos(unsigned hash)
-{
-	int lo = 0;
-	int hi = done_pbase_paths_num;
-	while (lo < hi) {
-		int mi = (hi + lo) / 2;
-		if (done_pbase_paths[mi] == hash)
-			return mi;
-		if (done_pbase_paths[mi] < hash)
-			hi = mi;
-		else
-			lo = mi + 1;
-	}
-	return -lo-1;
-}
-
-static int check_pbase_path(unsigned hash)
-{
-	int pos = (!done_pbase_paths) ? -1 : done_pbase_path_pos(hash);
-	if (0 <= pos)
-		return 1;
-	pos = -pos - 1;
-	if (done_pbase_paths_alloc <= done_pbase_paths_num) {
-		done_pbase_paths_alloc = alloc_nr(done_pbase_paths_alloc);
-		done_pbase_paths = xrealloc(done_pbase_paths,
-					    done_pbase_paths_alloc *
-					    sizeof(unsigned));
-	}
-	done_pbase_paths_num++;
-	if (pos < done_pbase_paths_num)
-		memmove(done_pbase_paths + pos + 1,
-			done_pbase_paths + pos,
-			(done_pbase_paths_num - pos - 1) * sizeof(unsigned));
-	done_pbase_paths[pos] = hash;
-	return 0;
-}
-
-static void add_preferred_base_object(const char *name)
-{
-	struct pbase_tree *it;
-	int cmplen;
-	unsigned hash = name_hash(name);
-
-	if (!num_preferred_base || check_pbase_path(hash))
-		return;
-
-	cmplen = name_cmp_len(name);
-	for (it = pbase_tree; it; it = it->next) {
-		if (cmplen == 0) {
-			add_object_entry(it->pcache.sha1, OBJ_TREE, NULL, 1);
-		}
-		else {
-			struct tree_desc tree;
-			init_tree_desc(&tree, it->pcache.tree_data, it->pcache.tree_size);
-			add_pbase_object(&tree, name, cmplen, name);
-		}
-	}
-}
-
-static void add_preferred_base(unsigned char *sha1)
-{
-	struct pbase_tree *it;
-	void *data;
-	unsigned long size;
-	unsigned char tree_sha1[20];
-
-	if (window <= num_preferred_base++)
-		return;
-
-	data = read_object_with_reference(sha1, tree_type, &size, tree_sha1);
-	if (!data)
-		return;
-
-	for (it = pbase_tree; it; it = it->next) {
-		if (!hashcmp(it->pcache.sha1, tree_sha1)) {
-			free(data);
-			return;
-		}
-	}
-
-	it = xcalloc(1, sizeof(*it));
-	it->next = pbase_tree;
-	pbase_tree = it;
-
-	hashcpy(it->pcache.sha1, tree_sha1);
-	it->pcache.tree_data = data;
-	it->pcache.tree_size = size;
-}
-
-static void check_object(struct object_entry *entry)
-{
-	if (entry->in_pack) {
-		struct packed_git *p = entry->in_pack;
-		struct pack_window *w_curs = NULL;
-		const unsigned char *base_ref = NULL;
-		struct object_entry *base_entry;
-		unsigned long used, used_0;
-		unsigned int avail;
-		off_t ofs;
-		unsigned char *buf, c;
-
-		buf = use_pack(p, &w_curs, entry->in_pack_offset, &avail);
-
-		/*
-		 * We want in_pack_type even if we do not reuse delta
-		 * since non-delta representations could still be reused.
-		 */
-		used = unpack_object_header_buffer(buf, avail,
-						   &entry->in_pack_type,
-						   &entry->size);
-		if (used == 0)
-			goto give_up;
-
-		/*
-		 * Determine if this is a delta and if so whether we can
-		 * reuse it or not.  Otherwise let's find out as cheaply as
-		 * possible what the actual type and size for this object is.
-		 */
-		switch (entry->in_pack_type) {
-		default:
-			/* Not a delta hence we've already got all we need. */
-			entry->type = entry->in_pack_type;
-			entry->in_pack_header_size = used;
-			if (entry->type < OBJ_COMMIT || entry->type > OBJ_BLOB)
-				goto give_up;
-			unuse_pack(&w_curs);
-			return;
-		case OBJ_REF_DELTA:
-			if (reuse_delta && !entry->preferred_base)
-				base_ref = use_pack(p, &w_curs,
-						entry->in_pack_offset + used, NULL);
-			entry->in_pack_header_size = used + 20;
-			break;
-		case OBJ_OFS_DELTA:
-			buf = use_pack(p, &w_curs,
-				       entry->in_pack_offset + used, NULL);
-			used_0 = 0;
-			c = buf[used_0++];
-			ofs = c & 127;
-			while (c & 128) {
-				ofs += 1;
-				if (!ofs || MSB(ofs, 7)) {
-					error("delta base offset overflow in pack for %s",
-					      sha1_to_hex(entry->idx.sha1));
-					goto give_up;
-				}
-				c = buf[used_0++];
-				ofs = (ofs << 7) + (c & 127);
-			}
-			ofs = entry->in_pack_offset - ofs;
-			if (ofs <= 0 || ofs >= entry->in_pack_offset) {
-				error("delta base offset out of bound for %s",
-				      sha1_to_hex(entry->idx.sha1));
-				goto give_up;
-			}
-			if (reuse_delta && !entry->preferred_base) {
-				struct revindex_entry *revidx;
-				revidx = find_pack_revindex(p, ofs);
-				if (!revidx)
-					goto give_up;
-				base_ref = nth_packed_object_sha1(p, revidx->nr);
-			}
-			entry->in_pack_header_size = used + used_0;
-			break;
-		}
-
-		if (base_ref && (base_entry = locate_object_entry(base_ref))) {
-			/*
-			 * If base_ref was set above that means we wish to
-			 * reuse delta data, and we even found that base
-			 * in the list of objects we want to pack. Goodie!
-			 *
-			 * Depth value does not matter - find_deltas() will
-			 * never consider reused delta as the base object to
-			 * deltify other objects against, in order to avoid
-			 * circular deltas.
-			 */
-			entry->type = entry->in_pack_type;
-			entry->delta = base_entry;
-			entry->delta_size = entry->size;
-			entry->delta_sibling = base_entry->delta_child;
-			base_entry->delta_child = entry;
-			unuse_pack(&w_curs);
-			return;
-		}
-
-		if (entry->type) {
-			/*
-			 * This must be a delta and we already know what the
-			 * final object type is.  Let's extract the actual
-			 * object size from the delta header.
-			 */
-			entry->size = get_size_from_delta(p, &w_curs,
-					entry->in_pack_offset + entry->in_pack_header_size);
-			if (entry->size == 0)
-				goto give_up;
-			unuse_pack(&w_curs);
-			return;
-		}
-
-		/*
-		 * No choice but to fall back to the recursive delta walk
-		 * with sha1_object_info() to find about the object type
-		 * at this point...
-		 */
-		give_up:
-		unuse_pack(&w_curs);
-	}
-
-	entry->type = sha1_object_info(entry->idx.sha1, &entry->size);
-	/*
-	 * The error condition is checked in prepare_pack().  This is
-	 * to permit a missing preferred base object to be ignored
-	 * as a preferred base.  Doing so can result in a larger
-	 * pack file, but the transfer will still take place.
-	 */
-}
-
-static int pack_offset_sort(const void *_a, const void *_b)
-{
-	const struct object_entry *a = *(struct object_entry **)_a;
-	const struct object_entry *b = *(struct object_entry **)_b;
-
-	/* avoid filesystem trashing with loose objects */
-	if (!a->in_pack && !b->in_pack)
-		return hashcmp(a->idx.sha1, b->idx.sha1);
-
-	if (a->in_pack < b->in_pack)
-		return -1;
-	if (a->in_pack > b->in_pack)
-		return 1;
-	return a->in_pack_offset < b->in_pack_offset ? -1 :
-			(a->in_pack_offset > b->in_pack_offset);
-}
-
-static void get_object_details(void)
-{
-	uint32_t i;
-	struct object_entry **sorted_by_offset;
-
-	sorted_by_offset = xcalloc(nr_objects, sizeof(struct object_entry *));
-	for (i = 0; i < nr_objects; i++)
-		sorted_by_offset[i] = objects + i;
-	qsort(sorted_by_offset, nr_objects, sizeof(*sorted_by_offset), pack_offset_sort);
-
-	for (i = 0; i < nr_objects; i++)
-		check_object(sorted_by_offset[i]);
-
-	free(sorted_by_offset);
-}
-
-/*
- * We search for deltas in a list sorted by type, by filename hash, and then
- * by size, so that we see progressively smaller and smaller files.
- * That's because we prefer deltas to be from the bigger file
- * to the smaller -- deletes are potentially cheaper, but perhaps
- * more importantly, the bigger file is likely the more recent
- * one.  The deepest deltas are therefore the oldest objects which are
- * less susceptible to be accessed often.
- */
-static int type_size_sort(const void *_a, const void *_b)
-{
-	const struct object_entry *a = *(struct object_entry **)_a;
-	const struct object_entry *b = *(struct object_entry **)_b;
-
-	if (a->type > b->type)
-		return -1;
-	if (a->type < b->type)
-		return 1;
-	if (a->hash > b->hash)
-		return -1;
-	if (a->hash < b->hash)
-		return 1;
-	if (a->preferred_base > b->preferred_base)
-		return -1;
-	if (a->preferred_base < b->preferred_base)
-		return 1;
-	if (a->size > b->size)
-		return -1;
-	if (a->size < b->size)
-		return 1;
-	return a < b ? -1 : (a > b);  /* newest first */
-}
-
-struct unpacked {
-	struct object_entry *entry;
-	void *data;
-	struct delta_index *index;
-	unsigned depth;
-};
-
-static int delta_cacheable(unsigned long src_size, unsigned long trg_size,
-			   unsigned long delta_size)
-{
-	if (max_delta_cache_size && delta_cache_size + delta_size > max_delta_cache_size)
-		return 0;
-
-	if (delta_size < cache_max_small_delta_size)
-		return 1;
-
-	/* cache delta, if objects are large enough compared to delta size */
-	if ((src_size >> 20) + (trg_size >> 21) > (delta_size >> 10))
-		return 1;
-
-	return 0;
-}
-
-#ifdef THREADED_DELTA_SEARCH
-
-static pthread_mutex_t read_mutex = PTHREAD_MUTEX_INITIALIZER;
-#define read_lock()		pthread_mutex_lock(&read_mutex)
-#define read_unlock()		pthread_mutex_unlock(&read_mutex)
-
-static pthread_mutex_t cache_mutex = PTHREAD_MUTEX_INITIALIZER;
-#define cache_lock()		pthread_mutex_lock(&cache_mutex)
-#define cache_unlock()		pthread_mutex_unlock(&cache_mutex)
-
-static pthread_mutex_t progress_mutex = PTHREAD_MUTEX_INITIALIZER;
-#define progress_lock()		pthread_mutex_lock(&progress_mutex)
-#define progress_unlock()	pthread_mutex_unlock(&progress_mutex)
-
-#else
-
-#define read_lock()		(void)0
-#define read_unlock()		(void)0
-#define cache_lock()		(void)0
-#define cache_unlock()		(void)0
-#define progress_lock()		(void)0
-#define progress_unlock()	(void)0
-
-#endif
-
-static int try_delta(struct unpacked *trg, struct unpacked *src,
-		     unsigned max_depth, unsigned long *mem_usage)
-{
-	struct object_entry *trg_entry = trg->entry;
-	struct object_entry *src_entry = src->entry;
-	unsigned long trg_size, src_size, delta_size, sizediff, max_size, sz;
-	unsigned ref_depth;
-	enum object_type type;
-	void *delta_buf;
-
-	/* Don't bother doing diffs between different types */
-	if (trg_entry->type != src_entry->type)
-		return -1;
-
-	/*
-	 * We do not bother to try a delta that we discarded
-	 * on an earlier try, but only when reusing delta data.
-	 */
-	if (reuse_delta && trg_entry->in_pack &&
-	    trg_entry->in_pack == src_entry->in_pack &&
-	    trg_entry->in_pack_type != OBJ_REF_DELTA &&
-	    trg_entry->in_pack_type != OBJ_OFS_DELTA)
-		return 0;
-
-	/* Let's not bust the allowed depth. */
-	if (src->depth >= max_depth)
-		return 0;
-
-	/* Now some size filtering heuristics. */
-	trg_size = trg_entry->size;
-	if (!trg_entry->delta) {
-		max_size = trg_size/2 - 20;
-		ref_depth = 1;
-	} else {
-		max_size = trg_entry->delta_size;
-		ref_depth = trg->depth;
-	}
-	max_size = (uint64_t)max_size * (max_depth - src->depth) /
-						(max_depth - ref_depth + 1);
-	if (max_size == 0)
-		return 0;
-	src_size = src_entry->size;
-	sizediff = src_size < trg_size ? trg_size - src_size : 0;
-	if (sizediff >= max_size)
-		return 0;
-	if (trg_size < src_size / 32)
-		return 0;
-
-	/* Load data if not already done */
-	if (!trg->data) {
-		read_lock();
-		trg->data = read_sha1_file(trg_entry->idx.sha1, &type, &sz);
-		read_unlock();
-		if (!trg->data)
-			die("object %s cannot be read",
-			    sha1_to_hex(trg_entry->idx.sha1));
-		if (sz != trg_size)
-			die("object %s inconsistent object length (%lu vs %lu)",
-			    sha1_to_hex(trg_entry->idx.sha1), sz, trg_size);
-		*mem_usage += sz;
-	}
-	if (!src->data) {
-		read_lock();
-		src->data = read_sha1_file(src_entry->idx.sha1, &type, &sz);
-		read_unlock();
-		if (!src->data)
-			die("object %s cannot be read",
-			    sha1_to_hex(src_entry->idx.sha1));
-		if (sz != src_size)
-			die("object %s inconsistent object length (%lu vs %lu)",
-			    sha1_to_hex(src_entry->idx.sha1), sz, src_size);
-		*mem_usage += sz;
-	}
-	if (!src->index) {
-		src->index = create_delta_index(src->data, src_size);
-		if (!src->index) {
-			static int warned = 0;
-			if (!warned++)
-				warning("suboptimal pack - out of memory");
-			return 0;
-		}
-		*mem_usage += sizeof_delta_index(src->index);
-	}
-
-	delta_buf = create_delta(src->index, trg->data, trg_size, &delta_size, max_size);
-	if (!delta_buf)
-		return 0;
-
-	if (trg_entry->delta) {
-		/* Prefer only shallower same-sized deltas. */
-		if (delta_size == trg_entry->delta_size &&
-		    src->depth + 1 >= trg->depth) {
-			free(delta_buf);
-			return 0;
-		}
-	}
-
-	/*
-	 * Handle memory allocation outside of the cache
-	 * accounting lock.  Compiler will optimize the strangeness
-	 * away when THREADED_DELTA_SEARCH is not defined.
-	 */
-	free(trg_entry->delta_data);
-	cache_lock();
-	if (trg_entry->delta_data) {
-		delta_cache_size -= trg_entry->delta_size;
-		trg_entry->delta_data = NULL;
-	}
-	if (delta_cacheable(src_size, trg_size, delta_size)) {
-		delta_cache_size += delta_size;
-		cache_unlock();
-		trg_entry->delta_data = xrealloc(delta_buf, delta_size);
-	} else {
-		cache_unlock();
-		free(delta_buf);
-	}
-
-	trg_entry->delta = src_entry;
-	trg_entry->delta_size = delta_size;
-	trg->depth = src->depth + 1;
-
-	return 1;
-}
-
-static unsigned int check_delta_limit(struct object_entry *me, unsigned int n)
-{
-	struct object_entry *child = me->delta_child;
-	unsigned int m = n;
-	while (child) {
-		unsigned int c = check_delta_limit(child, n + 1);
-		if (m < c)
-			m = c;
-		child = child->delta_sibling;
-	}
-	return m;
-}
-
-static unsigned long free_unpacked(struct unpacked *n)
-{
-	unsigned long freed_mem = sizeof_delta_index(n->index);
-	free_delta_index(n->index);
-	n->index = NULL;
-	if (n->data) {
-		freed_mem += n->entry->size;
-		free(n->data);
-		n->data = NULL;
-	}
-	n->entry = NULL;
-	n->depth = 0;
-	return freed_mem;
-}
-
-static void find_deltas(struct object_entry **list, unsigned *list_size,
-			int window, int depth, unsigned *processed)
-{
-	uint32_t i, idx = 0, count = 0;
-	struct unpacked *array;
-	unsigned long mem_usage = 0;
-
-	array = xcalloc(window, sizeof(struct unpacked));
-
-	for (;;) {
-		struct object_entry *entry;
-		struct unpacked *n = array + idx;
-		int j, max_depth, best_base = -1;
-
-		progress_lock();
-		if (!*list_size) {
-			progress_unlock();
-			break;
-		}
-		entry = *list++;
-		(*list_size)--;
-		if (!entry->preferred_base) {
-			(*processed)++;
-			display_progress(progress_state, *processed);
-		}
-		progress_unlock();
-
-		mem_usage -= free_unpacked(n);
-		n->entry = entry;
-
-		while (window_memory_limit &&
-		       mem_usage > window_memory_limit &&
-		       count > 1) {
-			uint32_t tail = (idx + window - count) % window;
-			mem_usage -= free_unpacked(array + tail);
-			count--;
-		}
-
-		/* We do not compute delta to *create* objects we are not
-		 * going to pack.
-		 */
-		if (entry->preferred_base)
-			goto next;
-
-		/*
-		 * If the current object is at pack edge, take the depth the
-		 * objects that depend on the current object into account
-		 * otherwise they would become too deep.
-		 */
-		max_depth = depth;
-		if (entry->delta_child) {
-			max_depth -= check_delta_limit(entry, 0);
-			if (max_depth <= 0)
-				goto next;
-		}
-
-		j = window;
-		while (--j > 0) {
-			int ret;
-			uint32_t other_idx = idx + j;
-			struct unpacked *m;
-			if (other_idx >= window)
-				other_idx -= window;
-			m = array + other_idx;
-			if (!m->entry)
-				break;
-			ret = try_delta(n, m, max_depth, &mem_usage);
-			if (ret < 0)
-				break;
-			else if (ret > 0)
-				best_base = other_idx;
-		}
-
-		/*
-		 * If we decided to cache the delta data, then it is best
-		 * to compress it right away.  First because we have to do
-		 * it anyway, and doing it here while we're threaded will
-		 * save a lot of time in the non threaded write phase,
-		 * as well as allow for caching more deltas within
-		 * the same cache size limit.
-		 * ...
-		 * But only if not writing to stdout, since in that case
-		 * the network is most likely throttling writes anyway,
-		 * and therefore it is best to go to the write phase ASAP
-		 * instead, as we can afford spending more time compressing
-		 * between writes at that moment.
-		 */
-		if (entry->delta_data && !pack_to_stdout) {
-			entry->z_delta_size = do_compress(&entry->delta_data,
-							  entry->delta_size);
-			cache_lock();
-			delta_cache_size -= entry->delta_size;
-			delta_cache_size += entry->z_delta_size;
-			cache_unlock();
-		}
-
-		/* if we made n a delta, and if n is already at max
-		 * depth, leaving it in the window is pointless.  we
-		 * should evict it first.
-		 */
-		if (entry->delta && max_depth <= n->depth)
-			continue;
-
-		/*
-		 * Move the best delta base up in the window, after the
-		 * currently deltified object, to keep it longer.  It will
-		 * be the first base object to be attempted next.
-		 */
-		if (entry->delta) {
-			struct unpacked swap = array[best_base];
-			int dist = (window + idx - best_base) % window;
-			int dst = best_base;
-			while (dist--) {
-				int src = (dst + 1) % window;
-				array[dst] = array[src];
-				dst = src;
-			}
-			array[dst] = swap;
-		}
-
-		next:
-		idx++;
-		if (count + 1 < window)
-			count++;
-		if (idx >= window)
-			idx = 0;
-	}
-
-	for (i = 0; i < window; ++i) {
-		free_delta_index(array[i].index);
-		free(array[i].data);
-	}
-	free(array);
-}
-
-#ifdef THREADED_DELTA_SEARCH
-
-/*
- * The main thread waits on the condition that (at least) one of the workers
- * has stopped working (which is indicated in the .working member of
- * struct thread_params).
- * When a work thread has completed its work, it sets .working to 0 and
- * signals the main thread and waits on the condition that .data_ready
- * becomes 1.
- */
-
-struct thread_params {
-	pthread_t thread;
-	struct object_entry **list;
-	unsigned list_size;
-	unsigned remaining;
-	int window;
-	int depth;
-	int working;
-	int data_ready;
-	pthread_mutex_t mutex;
-	pthread_cond_t cond;
-	unsigned *processed;
-};
-
-static pthread_cond_t progress_cond = PTHREAD_COND_INITIALIZER;
-
-static void *threaded_find_deltas(void *arg)
-{
-	struct thread_params *me = arg;
-
-	while (me->remaining) {
-		find_deltas(me->list, &me->remaining,
-			    me->window, me->depth, me->processed);
-
-		progress_lock();
-		me->working = 0;
-		pthread_cond_signal(&progress_cond);
-		progress_unlock();
-
-		/*
-		 * We must not set ->data_ready before we wait on the
-		 * condition because the main thread may have set it to 1
-		 * before we get here. In order to be sure that new
-		 * work is available if we see 1 in ->data_ready, it
-		 * was initialized to 0 before this thread was spawned
-		 * and we reset it to 0 right away.
-		 */
-		pthread_mutex_lock(&me->mutex);
-		while (!me->data_ready)
-			pthread_cond_wait(&me->cond, &me->mutex);
-		me->data_ready = 0;
-		pthread_mutex_unlock(&me->mutex);
-	}
-	/* leave ->working 1 so that this doesn't get more work assigned */
-	return NULL;
-}
-
-static void ll_find_deltas(struct object_entry **list, unsigned list_size,
-			   int window, int depth, unsigned *processed)
-{
-	struct thread_params p[delta_search_threads];
-	int i, ret, active_threads = 0;
-
-	if (delta_search_threads <= 1) {
-		find_deltas(list, &list_size, window, depth, processed);
-		return;
-	}
-	if (progress > pack_to_stdout)
-		fprintf(stderr, "Delta compression using up to %d threads.\n",
-				delta_search_threads);
-
-	/* Partition the work amongst work threads. */
-	for (i = 0; i < delta_search_threads; i++) {
-		unsigned sub_size = list_size / (delta_search_threads - i);
-
-		/* don't use too small segments or no deltas will be found */
-		if (sub_size < 2*window && i+1 < delta_search_threads)
-			sub_size = 0;
-
-		p[i].window = window;
-		p[i].depth = depth;
-		p[i].processed = processed;
-		p[i].working = 1;
-		p[i].data_ready = 0;
-
-		/* try to split chunks on "path" boundaries */
-		while (sub_size && sub_size < list_size &&
-		       list[sub_size]->hash &&
-		       list[sub_size]->hash == list[sub_size-1]->hash)
-			sub_size++;
-
-		p[i].list = list;
-		p[i].list_size = sub_size;
-		p[i].remaining = sub_size;
-
-		list += sub_size;
-		list_size -= sub_size;
-	}
-
-	/* Start work threads. */
-	for (i = 0; i < delta_search_threads; i++) {
-		if (!p[i].list_size)
-			continue;
-		pthread_mutex_init(&p[i].mutex, NULL);
-		pthread_cond_init(&p[i].cond, NULL);
-		ret = pthread_create(&p[i].thread, NULL,
-				     threaded_find_deltas, &p[i]);
-		if (ret)
-			die("unable to create thread: %s", strerror(ret));
-		active_threads++;
-	}
-
-	/*
-	 * Now let's wait for work completion.  Each time a thread is done
-	 * with its work, we steal half of the remaining work from the
-	 * thread with the largest number of unprocessed objects and give
-	 * it to that newly idle thread.  This ensure good load balancing
-	 * until the remaining object list segments are simply too short
-	 * to be worth splitting anymore.
-	 */
-	while (active_threads) {
-		struct thread_params *target = NULL;
-		struct thread_params *victim = NULL;
-		unsigned sub_size = 0;
-
-		progress_lock();
-		for (;;) {
-			for (i = 0; !target && i < delta_search_threads; i++)
-				if (!p[i].working)
-					target = &p[i];
-			if (target)
-				break;
-			pthread_cond_wait(&progress_cond, &progress_mutex);
-		}
-
-		for (i = 0; i < delta_search_threads; i++)
-			if (p[i].remaining > 2*window &&
-			    (!victim || victim->remaining < p[i].remaining))
-				victim = &p[i];
-		if (victim) {
-			sub_size = victim->remaining / 2;
-			list = victim->list + victim->list_size - sub_size;
-			while (sub_size && list[0]->hash &&
-			       list[0]->hash == list[-1]->hash) {
-				list++;
-				sub_size--;
-			}
-			if (!sub_size) {
-				/*
-				 * It is possible for some "paths" to have
-				 * so many objects that no hash boundary
-				 * might be found.  Let's just steal the
-				 * exact half in that case.
-				 */
-				sub_size = victim->remaining / 2;
-				list -= sub_size;
-			}
-			target->list = list;
-			victim->list_size -= sub_size;
-			victim->remaining -= sub_size;
-		}
-		target->list_size = sub_size;
-		target->remaining = sub_size;
-		target->working = 1;
-		progress_unlock();
-
-		pthread_mutex_lock(&target->mutex);
-		target->data_ready = 1;
-		pthread_cond_signal(&target->cond);
-		pthread_mutex_unlock(&target->mutex);
-
-		if (!sub_size) {
-			pthread_join(target->thread, NULL);
-			pthread_cond_destroy(&target->cond);
-			pthread_mutex_destroy(&target->mutex);
-			active_threads--;
-		}
-	}
-}
-
-#else
-#define ll_find_deltas(l, s, w, d, p)	find_deltas(l, &s, w, d, p)
-#endif
-
-static int add_ref_tag(const char *path, const unsigned char *sha1, int flag, void *cb_data)
-{
-	unsigned char peeled[20];
-
-	if (!prefixcmp(path, "refs/tags/") && /* is a tag? */
-	    !peel_ref(path, peeled)        && /* peelable? */
-	    !is_null_sha1(peeled)          && /* annotated tag? */
-	    locate_object_entry(peeled))      /* object packed? */
-		add_object_entry(sha1, OBJ_TAG, NULL, 0);
-	return 0;
-}
-
-static void prepare_pack(int window, int depth)
-{
-	struct object_entry **delta_list;
-	uint32_t i, nr_deltas;
-	unsigned n;
-
-	get_object_details();
-
-	/*
-	 * If we're locally repacking then we need to be doubly careful
-	 * from now on in order to make sure no stealth corruption gets
-	 * propagated to the new pack.  Clients receiving streamed packs
-	 * should validate everything they get anyway so no need to incur
-	 * the additional cost here in that case.
-	 */
-	if (!pack_to_stdout)
-		do_check_packed_object_crc = 1;
-
-	if (!nr_objects || !window || !depth)
-		return;
-
-	delta_list = xmalloc(nr_objects * sizeof(*delta_list));
-	nr_deltas = n = 0;
-
-	for (i = 0; i < nr_objects; i++) {
-		struct object_entry *entry = objects + i;
-
-		if (entry->delta)
-			/* This happens if we decided to reuse existing
-			 * delta from a pack.  "reuse_delta &&" is implied.
-			 */
-			continue;
-
-		if (entry->size < 50)
-			continue;
-
-		if (entry->no_try_delta)
-			continue;
-
-		if (!entry->preferred_base) {
-			nr_deltas++;
-			if (entry->type < 0)
-				die("unable to get type of object %s",
-				    sha1_to_hex(entry->idx.sha1));
-		} else {
-			if (entry->type < 0) {
-				/*
-				 * This object is not found, but we
-				 * don't have to include it anyway.
-				 */
-				continue;
-			}
-		}
-
-		delta_list[n++] = entry;
-	}
-
-	if (nr_deltas && n > 1) {
-		unsigned nr_done = 0;
-		if (progress)
-			progress_state = start_progress("Compressing objects",
-							nr_deltas);
-		qsort(delta_list, n, sizeof(*delta_list), type_size_sort);
-		ll_find_deltas(delta_list, n, window+1, depth, &nr_done);
-		stop_progress(&progress_state);
-		if (nr_done != nr_deltas)
-			die("inconsistency with delta count");
-	}
-	free(delta_list);
-}
-
-static int git_pack_config(const char *k, const char *v, void *cb)
-{
-	if(!strcmp(k, "pack.window")) {
-		window = git_config_int(k, v);
-		return 0;
-	}
-	if (!strcmp(k, "pack.windowmemory")) {
-		window_memory_limit = git_config_ulong(k, v);
-		return 0;
-	}
-	if (!strcmp(k, "pack.depth")) {
-		depth = git_config_int(k, v);
-		return 0;
-	}
-	if (!strcmp(k, "pack.compression")) {
-		int level = git_config_int(k, v);
-		if (level == -1)
-			level = Z_DEFAULT_COMPRESSION;
-		else if (level < 0 || level > Z_BEST_COMPRESSION)
-			die("bad pack compression level %d", level);
-		pack_compression_level = level;
-		pack_compression_seen = 1;
-		return 0;
-	}
-	if (!strcmp(k, "pack.deltacachesize")) {
-		max_delta_cache_size = git_config_int(k, v);
-		return 0;
-	}
-	if (!strcmp(k, "pack.deltacachelimit")) {
-		cache_max_small_delta_size = git_config_int(k, v);
-		return 0;
-	}
-	if (!strcmp(k, "pack.threads")) {
-		delta_search_threads = git_config_int(k, v);
-		if (delta_search_threads < 0)
-			die("invalid number of threads specified (%d)",
-			    delta_search_threads);
-#ifndef THREADED_DELTA_SEARCH
-		if (delta_search_threads != 1)
-			warning("no threads support, ignoring %s", k);
-#endif
-		return 0;
-	}
-	if (!strcmp(k, "pack.indexversion")) {
-		pack_idx_default_version = git_config_int(k, v);
-		if (pack_idx_default_version > 2)
-			die("bad pack.indexversion=%"PRIu32,
-				pack_idx_default_version);
-		return 0;
-	}
-	if (!strcmp(k, "pack.packsizelimit")) {
-		pack_size_limit_cfg = git_config_ulong(k, v);
-		return 0;
-	}
-	return git_default_config(k, v, cb);
-}
-
-static void read_object_list_from_stdin(void)
-{
-	char line[40 + 1 + PATH_MAX + 2];
-	unsigned char sha1[20];
-
-	for (;;) {
-		if (!fgets(line, sizeof(line), stdin)) {
-			if (feof(stdin))
-				break;
-			if (!ferror(stdin))
-				die("fgets returned NULL, not EOF, not error!");
-			if (errno != EINTR)
-				die_errno("fgets");
-			clearerr(stdin);
-			continue;
-		}
-		if (line[0] == '-') {
-			if (get_sha1_hex(line+1, sha1))
-				die("expected edge sha1, got garbage:\n %s",
-				    line);
-			add_preferred_base(sha1);
-			continue;
-		}
-		if (get_sha1_hex(line, sha1))
-			die("expected sha1, got garbage:\n %s", line);
-
-		add_preferred_base_object(line+41);
-		add_object_entry(sha1, 0, line+41, 0);
-	}
-}
-
-#define OBJECT_ADDED (1u<<20)
-
-static void show_commit(struct commit *commit, void *data)
-{
-	add_object_entry(commit->object.sha1, OBJ_COMMIT, NULL, 0);
-	commit->object.flags |= OBJECT_ADDED;
-}
-
-static void show_object(struct object *obj, const struct name_path *path, const char *last)
-{
-	char *name = path_name(path, last);
-
-	add_preferred_base_object(name);
-	add_object_entry(obj->sha1, obj->type, name, 0);
-	obj->flags |= OBJECT_ADDED;
-
-	/*
-	 * We will have generated the hash from the name,
-	 * but not saved a pointer to it - we can free it
-	 */
-	free((char *)name);
-}
-
-static void show_edge(struct commit *commit)
-{
-	add_preferred_base(commit->object.sha1);
-}
-
-struct in_pack_object {
-	off_t offset;
-	struct object *object;
-};
-
-struct in_pack {
-	int alloc;
-	int nr;
-	struct in_pack_object *array;
-};
-
-static void mark_in_pack_object(struct object *object, struct packed_git *p, struct in_pack *in_pack)
-{
-	in_pack->array[in_pack->nr].offset = find_pack_entry_one(object->sha1, p);
-	in_pack->array[in_pack->nr].object = object;
-	in_pack->nr++;
-}
-
-/*
- * Compare the objects in the offset order, in order to emulate the
- * "git rev-list --objects" output that produced the pack originally.
- */
-static int ofscmp(const void *a_, const void *b_)
-{
-	struct in_pack_object *a = (struct in_pack_object *)a_;
-	struct in_pack_object *b = (struct in_pack_object *)b_;
-
-	if (a->offset < b->offset)
-		return -1;
-	else if (a->offset > b->offset)
-		return 1;
-	else
-		return hashcmp(a->object->sha1, b->object->sha1);
-}
-
-static void add_objects_in_unpacked_packs(struct rev_info *revs)
-{
-	struct packed_git *p;
-	struct in_pack in_pack;
-	uint32_t i;
-
-	memset(&in_pack, 0, sizeof(in_pack));
-
-	for (p = packed_git; p; p = p->next) {
-		const unsigned char *sha1;
-		struct object *o;
-
-		if (!p->pack_local || p->pack_keep)
-			continue;
-		if (open_pack_index(p))
-			die("cannot open pack index");
-
-		ALLOC_GROW(in_pack.array,
-			   in_pack.nr + p->num_objects,
-			   in_pack.alloc);
-
-		for (i = 0; i < p->num_objects; i++) {
-			sha1 = nth_packed_object_sha1(p, i);
-			o = lookup_unknown_object(sha1);
-			if (!(o->flags & OBJECT_ADDED))
-				mark_in_pack_object(o, p, &in_pack);
-			o->flags |= OBJECT_ADDED;
-		}
-	}
-
-	if (in_pack.nr) {
-		qsort(in_pack.array, in_pack.nr, sizeof(in_pack.array[0]),
-		      ofscmp);
-		for (i = 0; i < in_pack.nr; i++) {
-			struct object *o = in_pack.array[i].object;
-			add_object_entry(o->sha1, o->type, "", 0);
-		}
-	}
-	free(in_pack.array);
-}
-
-static int has_sha1_pack_kept_or_nonlocal(const unsigned char *sha1)
-{
-	static struct packed_git *last_found = (void *)1;
-	struct packed_git *p;
-
-	p = (last_found != (void *)1) ? last_found : packed_git;
-
-	while (p) {
-		if ((!p->pack_local || p->pack_keep) &&
-			find_pack_entry_one(sha1, p)) {
-			last_found = p;
-			return 1;
-		}
-		if (p == last_found)
-			p = packed_git;
-		else
-			p = p->next;
-		if (p == last_found)
-			p = p->next;
-	}
-	return 0;
-}
-
-static void loosen_unused_packed_objects(struct rev_info *revs)
-{
-	struct packed_git *p;
-	uint32_t i;
-	const unsigned char *sha1;
-
-	for (p = packed_git; p; p = p->next) {
-		if (!p->pack_local || p->pack_keep)
-			continue;
-
-		if (open_pack_index(p))
-			die("cannot open pack index");
-
-		for (i = 0; i < p->num_objects; i++) {
-			sha1 = nth_packed_object_sha1(p, i);
-			if (!locate_object_entry(sha1) &&
-				!has_sha1_pack_kept_or_nonlocal(sha1))
-				if (force_object_loose(sha1, p->mtime))
-					die("unable to force loose object");
-		}
-	}
-}
-
-static void get_object_list(int ac, const char **av)
-{
-	struct rev_info revs;
-	char line[1000];
-	int flags = 0;
-
-	init_revisions(&revs, NULL);
-	save_commit_buffer = 0;
-	setup_revisions(ac, av, &revs, NULL);
-
-	while (fgets(line, sizeof(line), stdin) != NULL) {
-		int len = strlen(line);
-		if (len && line[len - 1] == '\n')
-			line[--len] = 0;
-		if (!len)
-			break;
-		if (*line == '-') {
-			if (!strcmp(line, "--not")) {
-				flags ^= UNINTERESTING;
-				continue;
-			}
-			die("not a rev '%s'", line);
-		}
-		if (handle_revision_arg(line, &revs, flags, 1))
-			die("bad revision '%s'", line);
-	}
-
-	if (prepare_revision_walk(&revs))
-		die("revision walk setup failed");
-	mark_edges_uninteresting(revs.commits, &revs, show_edge);
-	traverse_commit_list(&revs, show_commit, show_object, NULL);
-
-	if (keep_unreachable)
-		add_objects_in_unpacked_packs(&revs);
-	if (unpack_unreachable)
-		loosen_unused_packed_objects(&revs);
-}
-
-static int adjust_perm(const char *path, mode_t mode)
-{
-	if (chmod(path, mode))
-		return -1;
-	return adjust_shared_perm(path);
-}
-
-int cmd_pack_objects(int argc, const char **argv, const char *prefix)
-{
-	int use_internal_rev_list = 0;
-	int thin = 0;
-	uint32_t i;
-	const char **rp_av;
-	int rp_ac_alloc = 64;
-	int rp_ac;
-
-	rp_av = xcalloc(rp_ac_alloc, sizeof(*rp_av));
-
-	rp_av[0] = "pack-objects";
-	rp_av[1] = "--objects"; /* --thin will make it --objects-edge */
-	rp_ac = 2;
-
-	git_config(git_pack_config, NULL);
-	if (!pack_compression_seen && core_compression_seen)
-		pack_compression_level = core_compression_level;
-
-	progress = isatty(2);
-	for (i = 1; i < argc; i++) {
-		const char *arg = argv[i];
-
-		if (*arg != '-')
-			break;
-
-		if (!strcmp("--non-empty", arg)) {
-			non_empty = 1;
-			continue;
-		}
-		if (!strcmp("--local", arg)) {
-			local = 1;
-			continue;
-		}
-		if (!strcmp("--incremental", arg)) {
-			incremental = 1;
-			continue;
-		}
-		if (!strcmp("--honor-pack-keep", arg)) {
-			ignore_packed_keep = 1;
-			continue;
-		}
-		if (!prefixcmp(arg, "--compression=")) {
-			char *end;
-			int level = strtoul(arg+14, &end, 0);
-			if (!arg[14] || *end)
-				usage(pack_usage);
-			if (level == -1)
-				level = Z_DEFAULT_COMPRESSION;
-			else if (level < 0 || level > Z_BEST_COMPRESSION)
-				die("bad pack compression level %d", level);
-			pack_compression_level = level;
-			continue;
-		}
-		if (!prefixcmp(arg, "--max-pack-size=")) {
-			char *end;
-			pack_size_limit_cfg = 0;
-			pack_size_limit = strtoul(arg+16, &end, 0) * 1024 * 1024;
-			if (!arg[16] || *end)
-				usage(pack_usage);
-			continue;
-		}
-		if (!prefixcmp(arg, "--window=")) {
-			char *end;
-			window = strtoul(arg+9, &end, 0);
-			if (!arg[9] || *end)
-				usage(pack_usage);
-			continue;
-		}
-		if (!prefixcmp(arg, "--window-memory=")) {
-			if (!git_parse_ulong(arg+16, &window_memory_limit))
-				usage(pack_usage);
-			continue;
-		}
-		if (!prefixcmp(arg, "--threads=")) {
-			char *end;
-			delta_search_threads = strtoul(arg+10, &end, 0);
-			if (!arg[10] || *end || delta_search_threads < 0)
-				usage(pack_usage);
-#ifndef THREADED_DELTA_SEARCH
-			if (delta_search_threads != 1)
-				warning("no threads support, "
-					"ignoring %s", arg);
-#endif
-			continue;
-		}
-		if (!prefixcmp(arg, "--depth=")) {
-			char *end;
-			depth = strtoul(arg+8, &end, 0);
-			if (!arg[8] || *end)
-				usage(pack_usage);
-			continue;
-		}
-		if (!strcmp("--progress", arg)) {
-			progress = 1;
-			continue;
-		}
-		if (!strcmp("--all-progress", arg)) {
-			progress = 2;
-			continue;
-		}
-		if (!strcmp("-q", arg)) {
-			progress = 0;
-			continue;
-		}
-		if (!strcmp("--no-reuse-delta", arg)) {
-			reuse_delta = 0;
-			continue;
-		}
-		if (!strcmp("--no-reuse-object", arg)) {
-			reuse_object = reuse_delta = 0;
-			continue;
-		}
-		if (!strcmp("--delta-base-offset", arg)) {
-			allow_ofs_delta = 1;
-			continue;
-		}
-		if (!strcmp("--stdout", arg)) {
-			pack_to_stdout = 1;
-			continue;
-		}
-		if (!strcmp("--revs", arg)) {
-			use_internal_rev_list = 1;
-			continue;
-		}
-		if (!strcmp("--keep-unreachable", arg)) {
-			keep_unreachable = 1;
-			continue;
-		}
-		if (!strcmp("--unpack-unreachable", arg)) {
-			unpack_unreachable = 1;
-			continue;
-		}
-		if (!strcmp("--include-tag", arg)) {
-			include_tag = 1;
-			continue;
-		}
-		if (!strcmp("--unpacked", arg) ||
-		    !strcmp("--reflog", arg) ||
-		    !strcmp("--all", arg)) {
-			use_internal_rev_list = 1;
-			if (rp_ac >= rp_ac_alloc - 1) {
-				rp_ac_alloc = alloc_nr(rp_ac_alloc);
-				rp_av = xrealloc(rp_av,
-						 rp_ac_alloc * sizeof(*rp_av));
-			}
-			rp_av[rp_ac++] = arg;
-			continue;
-		}
-		if (!strcmp("--thin", arg)) {
-			use_internal_rev_list = 1;
-			thin = 1;
-			rp_av[1] = "--objects-edge";
-			continue;
-		}
-		if (!prefixcmp(arg, "--index-version=")) {
-			char *c;
-			pack_idx_default_version = strtoul(arg + 16, &c, 10);
-			if (pack_idx_default_version > 2)
-				die("bad %s", arg);
-			if (*c == ',')
-				pack_idx_off32_limit = strtoul(c+1, &c, 0);
-			if (*c || pack_idx_off32_limit & 0x80000000)
-				die("bad %s", arg);
-			continue;
-		}
-		if (!strcmp(arg, "--keep-true-parents")) {
-			grafts_replace_parents = 0;
-			continue;
-		}
-		usage(pack_usage);
-	}
-
-	/* Traditionally "pack-objects [options] base extra" failed;
-	 * we would however want to take refs parameter that would
-	 * have been given to upstream rev-list ourselves, which means
-	 * we somehow want to say what the base name is.  So the
-	 * syntax would be:
-	 *
-	 * pack-objects [options] base <refs...>
-	 *
-	 * in other words, we would treat the first non-option as the
-	 * base_name and send everything else to the internal revision
-	 * walker.
-	 */
-
-	if (!pack_to_stdout)
-		base_name = argv[i++];
-
-	if (pack_to_stdout != !base_name)
-		usage(pack_usage);
-
-	if (!pack_to_stdout && !pack_size_limit)
-		pack_size_limit = pack_size_limit_cfg;
-
-	if (pack_to_stdout && pack_size_limit)
-		die("--max-pack-size cannot be used to build a pack for transfer.");
-
-	if (!pack_to_stdout && thin)
-		die("--thin cannot be used to build an indexable pack.");
-
-	if (keep_unreachable && unpack_unreachable)
-		die("--keep-unreachable and --unpack-unreachable are incompatible.");
-
-#ifdef THREADED_DELTA_SEARCH
-	if (!delta_search_threads)	/* --threads=0 means autodetect */
-		delta_search_threads = online_cpus();
-#endif
-
-	prepare_packed_git();
-
-	if (progress)
-		progress_state = start_progress("Counting objects", 0);
-	if (!use_internal_rev_list)
-		read_object_list_from_stdin();
-	else {
-		rp_av[rp_ac] = NULL;
-		get_object_list(rp_ac, rp_av);
-	}
-	if (include_tag && nr_result)
-		for_each_ref(add_ref_tag, NULL);
-	stop_progress(&progress_state);
-
-	if (non_empty && !nr_result)
-		return 0;
-	if (nr_result)
-		prepare_pack(window, depth);
-	write_pack_file();
-	if (progress)
-		fprintf(stderr, "Total %"PRIu32" (delta %"PRIu32"),"
-			" reused %"PRIu32" (delta %"PRIu32")\n",
-			written, written_delta, reused, reused_delta);
-	return 0;
-}
diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c
deleted file mode 100644
index 091860b..0000000
--- a/builtin-pack-refs.c
+++ /dev/null
@@ -1,21 +0,0 @@
-#include "cache.h"
-#include "parse-options.h"
-#include "pack-refs.h"
-
-static char const * const pack_refs_usage[] = {
-	"git pack-refs [options]",
-	NULL
-};
-
-int cmd_pack_refs(int argc, const char **argv, const char *prefix)
-{
-	unsigned int flags = PACK_REFS_PRUNE;
-	struct option opts[] = {
-		OPT_BIT(0, "all",   &flags, "pack everything", PACK_REFS_ALL),
-		OPT_BIT(0, "prune", &flags, "prune loose refs (default)", PACK_REFS_PRUNE),
-		OPT_END(),
-	};
-	if (parse_options(argc, argv, prefix, opts, pack_refs_usage, 0))
-		usage_with_options(pack_refs_usage, opts);
-	return pack_refs(flags);
-}
diff --git a/builtin-prune-packed.c b/builtin-prune-packed.c
deleted file mode 100644
index be99eb0..0000000
--- a/builtin-prune-packed.c
+++ /dev/null
@@ -1,86 +0,0 @@
-#include "builtin.h"
-#include "cache.h"
-#include "progress.h"
-#include "parse-options.h"
-
-static const char * const prune_packed_usage[] = {
-	"git prune-packed [-n|--dry-run] [-q|--quiet]",
-	NULL
-};
-
-#define DRY_RUN 01
-#define VERBOSE 02
-
-static struct progress *progress;
-
-static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts)
-{
-	struct dirent *de;
-	char hex[40];
-
-	sprintf(hex, "%02x", i);
-	while ((de = readdir(dir)) != NULL) {
-		unsigned char sha1[20];
-		if (strlen(de->d_name) != 38)
-			continue;
-		memcpy(hex+2, de->d_name, 38);
-		if (get_sha1_hex(hex, sha1))
-			continue;
-		if (!has_sha1_pack(sha1))
-			continue;
-		memcpy(pathname + len, de->d_name, 38);
-		if (opts & DRY_RUN)
-			printf("rm -f %s\n", pathname);
-		else
-			unlink_or_warn(pathname);
-		display_progress(progress, i + 1);
-	}
-	pathname[len] = 0;
-	rmdir(pathname);
-}
-
-void prune_packed_objects(int opts)
-{
-	int i;
-	static char pathname[PATH_MAX];
-	const char *dir = get_object_directory();
-	int len = strlen(dir);
-
-	if (opts == VERBOSE)
-		progress = start_progress_delay("Removing duplicate objects",
-			256, 95, 2);
-
-	if (len > PATH_MAX - 42)
-		die("impossible object directory");
-	memcpy(pathname, dir, len);
-	if (len && pathname[len-1] != '/')
-		pathname[len++] = '/';
-	for (i = 0; i < 256; i++) {
-		DIR *d;
-
-		display_progress(progress, i + 1);
-		sprintf(pathname + len, "%02x/", i);
-		d = opendir(pathname);
-		if (!d)
-			continue;
-		prune_dir(i, d, pathname, len + 3, opts);
-		closedir(d);
-	}
-	stop_progress(&progress);
-}
-
-int cmd_prune_packed(int argc, const char **argv, const char *prefix)
-{
-	int opts = VERBOSE;
-	const struct option prune_packed_options[] = {
-		OPT_BIT('n', "dry-run", &opts, "dry run", DRY_RUN),
-		OPT_NEGBIT('q', "quiet", &opts, "be quiet", VERBOSE),
-		OPT_END()
-	};
-
-	argc = parse_options(argc, argv, prefix, prune_packed_options,
-			     prune_packed_usage, 0);
-
-	prune_packed_objects(opts);
-	return 0;
-}
diff --git a/builtin-prune.c b/builtin-prune.c
deleted file mode 100644
index 0ed9cce..0000000
--- a/builtin-prune.c
+++ /dev/null
@@ -1,168 +0,0 @@
-#include "cache.h"
-#include "commit.h"
-#include "diff.h"
-#include "revision.h"
-#include "builtin.h"
-#include "reachable.h"
-#include "parse-options.h"
-#include "dir.h"
-
-static const char * const prune_usage[] = {
-	"git prune [-n] [-v] [--expire <time>] [--] [<head>...]",
-	NULL
-};
-static int show_only;
-static int verbose;
-static unsigned long expire;
-
-static int prune_tmp_object(const char *path, const char *filename)
-{
-	const char *fullpath = mkpath("%s/%s", path, filename);
-	if (expire) {
-		struct stat st;
-		if (lstat(fullpath, &st))
-			return error("Could not stat '%s'", fullpath);
-		if (st.st_mtime > expire)
-			return 0;
-	}
-	printf("Removing stale temporary file %s\n", fullpath);
-	if (!show_only)
-		unlink_or_warn(fullpath);
-	return 0;
-}
-
-static int prune_object(char *path, const char *filename, const unsigned char *sha1)
-{
-	const char *fullpath = mkpath("%s/%s", path, filename);
-	if (expire) {
-		struct stat st;
-		if (lstat(fullpath, &st))
-			return error("Could not stat '%s'", fullpath);
-		if (st.st_mtime > expire)
-			return 0;
-	}
-	if (show_only || verbose) {
-		enum object_type type = sha1_object_info(sha1, NULL);
-		printf("%s %s\n", sha1_to_hex(sha1),
-		       (type > 0) ? typename(type) : "unknown");
-	}
-	if (!show_only)
-		unlink_or_warn(fullpath);
-	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];
-
-		if (is_dot_or_dotdot(de->d_name))
-			continue;
-		if (strlen(de->d_name) == 38) {
-			sprintf(name, "%02x", i);
-			memcpy(name+2, de->d_name, 39);
-			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;
-		}
-		if (!prefixcmp(de->d_name, "tmp_obj_")) {
-			prune_tmp_object(path, de->d_name);
-			continue;
-		}
-		fprintf(stderr, "bad sha1 file: %s/%s\n", path, de->d_name);
-	}
-	if (!show_only)
-		rmdir(path);
-	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);
-	}
-}
-
-/*
- * Write errors (particularly out of space) can result in
- * failed temporary packs (and more rarely indexes and other
- * files begining with "tmp_") accumulating in the object
- * and the pack directories.
- */
-static void remove_temporary_files(const char *path)
-{
-	DIR *dir;
-	struct dirent *de;
-
-	dir = opendir(path);
-	if (!dir) {
-		fprintf(stderr, "Unable to open directory %s\n", path);
-		return;
-	}
-	while ((de = readdir(dir)) != NULL)
-		if (!prefixcmp(de->d_name, "tmp_"))
-			prune_tmp_object(path, de->d_name);
-	closedir(dir);
-}
-
-int cmd_prune(int argc, const char **argv, const char *prefix)
-{
-	struct rev_info revs;
-	const struct option options[] = {
-		OPT_BOOLEAN('n', NULL, &show_only,
-			    "do not remove, show only"),
-		OPT_BOOLEAN('v', NULL, &verbose,
-			"report pruned objects"),
-		OPT_DATE(0, "expire", &expire,
-			 "expire objects older than <time>"),
-		OPT_END()
-	};
-	char *s;
-
-	save_commit_buffer = 0;
-	init_revisions(&revs, prefix);
-
-	argc = parse_options(argc, argv, prefix, options, prune_usage, 0);
-	while (argc--) {
-		unsigned char sha1[20];
-		const char *name = *argv++;
-
-		if (!get_sha1(name, sha1)) {
-			struct object *object = parse_object(sha1);
-			if (!object)
-				die("bad object: %s", name);
-			add_pending_object(&revs, object, "");
-		}
-		else
-			die("unrecognized argument: %s", name);
-	}
-	mark_reachable_objects(&revs, 1);
-	prune_object_dir(get_object_directory());
-
-	prune_packed_objects(show_only);
-	remove_temporary_files(get_object_directory());
-	s = xstrdup(mkpath("%s/pack", get_object_directory()));
-	remove_temporary_files(s);
-	free(s);
-	return 0;
-}
diff --git a/builtin-push.c b/builtin-push.c
deleted file mode 100644
index 1d92e22..0000000
--- a/builtin-push.c
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * "git push"
- */
-#include "cache.h"
-#include "refs.h"
-#include "run-command.h"
-#include "builtin.h"
-#include "remote.h"
-#include "transport.h"
-#include "parse-options.h"
-
-static const char * const push_usage[] = {
-	"git push [--all | --mirror] [--dry-run] [--porcelain] [--tags] [--receive-pack=<git-receive-pack>] [--repo=<repository>] [-f | --force] [-v] [<repository> <refspec>...]",
-	NULL,
-};
-
-static int thin;
-static const char *receivepack;
-
-static const char **refspec;
-static int refspec_nr;
-
-static void add_refspec(const char *ref)
-{
-	int nr = refspec_nr + 1;
-	refspec = xrealloc(refspec, nr * sizeof(char *));
-	refspec[nr-1] = ref;
-	refspec_nr = nr;
-}
-
-static void set_refspecs(const char **refs, int nr)
-{
-	int i;
-	for (i = 0; i < nr; i++) {
-		const char *ref = refs[i];
-		if (!strcmp("tag", ref)) {
-			char *tag;
-			int len;
-			if (nr <= ++i)
-				die("tag shorthand without <tag>");
-			len = strlen(refs[i]) + 11;
-			tag = xmalloc(len);
-			strcpy(tag, "refs/tags/");
-			strcat(tag, refs[i]);
-			ref = tag;
-		}
-		add_refspec(ref);
-	}
-}
-
-static void setup_push_tracking(void)
-{
-	struct strbuf refspec = STRBUF_INIT;
-	struct branch *branch = branch_get(NULL);
-	if (!branch)
-		die("You are not currently on a branch.");
-	if (!branch->merge_nr)
-		die("The current branch %s is not tracking anything.",
-		    branch->name);
-	if (branch->merge_nr != 1)
-		die("The current branch %s is tracking multiple branches, "
-		    "refusing to push.", branch->name);
-	strbuf_addf(&refspec, "%s:%s", branch->name, branch->merge[0]->src);
-	add_refspec(refspec.buf);
-}
-
-static void setup_default_push_refspecs(void)
-{
-	git_config(git_default_config, NULL);
-	switch (push_default) {
-	default:
-	case PUSH_DEFAULT_MATCHING:
-		add_refspec(":");
-		break;
-
-	case PUSH_DEFAULT_TRACKING:
-		setup_push_tracking();
-		break;
-
-	case PUSH_DEFAULT_CURRENT:
-		add_refspec("HEAD");
-		break;
-
-	case PUSH_DEFAULT_NOTHING:
-		die("You didn't specify any refspecs to push, and "
-		    "push.default is \"nothing\".");
-		break;
-	}
-}
-
-static int do_push(const char *repo, int flags)
-{
-	int i, errs;
-	struct remote *remote = remote_get(repo);
-	const char **url;
-	int url_nr;
-
-	if (!remote) {
-		if (repo)
-			die("bad repository '%s'", repo);
-		die("No destination configured to push to.");
-	}
-
-	if (remote->mirror)
-		flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE);
-
-	if ((flags & TRANSPORT_PUSH_ALL) && refspec) {
-		if (!strcmp(*refspec, "refs/tags/*"))
-			return error("--all and --tags are incompatible");
-		return error("--all can't be combined with refspecs");
-	}
-
-	if ((flags & TRANSPORT_PUSH_MIRROR) && refspec) {
-		if (!strcmp(*refspec, "refs/tags/*"))
-			return error("--mirror and --tags are incompatible");
-		return error("--mirror can't be combined with refspecs");
-	}
-
-	if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) ==
-				(TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) {
-		return error("--all and --mirror are incompatible");
-	}
-
-	if (!refspec && !(flags & TRANSPORT_PUSH_ALL)) {
-		if (remote->push_refspec_nr) {
-			refspec = remote->push_refspec;
-			refspec_nr = remote->push_refspec_nr;
-		} else if (!(flags & TRANSPORT_PUSH_MIRROR))
-			setup_default_push_refspecs();
-	}
-	errs = 0;
-	if (remote->pushurl_nr) {
-		url = remote->pushurl;
-		url_nr = remote->pushurl_nr;
-	} else {
-		url = remote->url;
-		url_nr = remote->url_nr;
-	}
-	for (i = 0; i < url_nr; i++) {
-		struct transport *transport =
-			transport_get(remote, url[i]);
-		int err;
-		if (receivepack)
-			transport_set_option(transport,
-					     TRANS_OPT_RECEIVEPACK, receivepack);
-		if (thin)
-			transport_set_option(transport, TRANS_OPT_THIN, "yes");
-
-		if (flags & TRANSPORT_PUSH_VERBOSE)
-			fprintf(stderr, "Pushing to %s\n", url[i]);
-		err = transport_push(transport, refspec_nr, refspec, flags);
-		err |= transport_disconnect(transport);
-
-		if (!err)
-			continue;
-
-		error("failed to push some refs to '%s'", url[i]);
-		errs++;
-	}
-	return !!errs;
-}
-
-int cmd_push(int argc, const char **argv, const char *prefix)
-{
-	int flags = 0;
-	int tags = 0;
-	int rc;
-	const char *repo = NULL;	/* default repository */
-
-	struct option options[] = {
-		OPT_BIT('v', "verbose", &flags, "be verbose", TRANSPORT_PUSH_VERBOSE),
-		OPT_STRING( 0 , "repo", &repo, "repository", "repository"),
-		OPT_BIT( 0 , "all", &flags, "push all refs", TRANSPORT_PUSH_ALL),
-		OPT_BIT( 0 , "mirror", &flags, "mirror all refs",
-			    (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE)),
-		OPT_BOOLEAN( 0 , "tags", &tags, "push tags"),
-		OPT_BIT( 0 , "dry-run", &flags, "dry run", TRANSPORT_PUSH_DRY_RUN),
-		OPT_BIT( 0,  "porcelain", &flags, "machine-readable output", TRANSPORT_PUSH_PORCELAIN),
-		OPT_BIT('f', "force", &flags, "force updates", TRANSPORT_PUSH_FORCE),
-		OPT_BOOLEAN( 0 , "thin", &thin, "use thin pack"),
-		OPT_STRING( 0 , "receive-pack", &receivepack, "receive-pack", "receive pack program"),
-		OPT_STRING( 0 , "exec", &receivepack, "receive-pack", "receive pack program"),
-		OPT_END()
-	};
-
-	argc = parse_options(argc, argv, prefix, options, push_usage, 0);
-
-	if (tags)
-		add_refspec("refs/tags/*");
-
-	if (argc > 0) {
-		repo = argv[0];
-		set_refspecs(argv + 1, argc - 1);
-	}
-
-	rc = do_push(repo, flags);
-	if (rc == -1)
-		usage_with_options(push_usage, options);
-	else
-		return rc;
-}
diff --git a/builtin-read-tree.c b/builtin-read-tree.c
deleted file mode 100644
index 9c2d634..0000000
--- a/builtin-read-tree.c
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * GIT - The information manager from hell
- *
- * Copyright (C) Linus Torvalds, 2005
- */
-
-#include "cache.h"
-#include "object.h"
-#include "tree.h"
-#include "tree-walk.h"
-#include "cache-tree.h"
-#include "unpack-trees.h"
-#include "dir.h"
-#include "builtin.h"
-#include "parse-options.h"
-
-static int nr_trees;
-static struct tree *trees[MAX_UNPACK_TREES];
-
-static int list_tree(unsigned char *sha1)
-{
-	struct tree *tree;
-
-	if (nr_trees >= MAX_UNPACK_TREES)
-		die("I cannot read more than %d trees", MAX_UNPACK_TREES);
-	tree = parse_tree_indirect(sha1);
-	if (!tree)
-		return -1;
-	trees[nr_trees++] = tree;
-	return 0;
-}
-
-static const char * const read_tree_usage[] = {
-	"git read-tree [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>] [-u [--exclude-per-directory=<gitignore>] | -i]]  [--index-output=<file>] <tree-ish1> [<tree-ish2> [<tree-ish3>]]",
-	NULL
-};
-
-static int index_output_cb(const struct option *opt, const char *arg,
-				 int unset)
-{
-	set_alternate_index_output(arg);
-	return 0;
-}
-
-static int exclude_per_directory_cb(const struct option *opt, const char *arg,
-				    int unset)
-{
-	struct dir_struct *dir;
-	struct unpack_trees_options *opts;
-
-	opts = (struct unpack_trees_options *)opt->value;
-
-	if (opts->dir)
-		die("more than one --exclude-per-directory given.");
-
-	dir = xcalloc(1, sizeof(*opts->dir));
-	dir->flags |= DIR_SHOW_IGNORED;
-	dir->exclude_per_dir = arg;
-	opts->dir = dir;
-	/* We do not need to nor want to do read-directory
-	 * here; we are merely interested in reusing the
-	 * per directory ignore stack mechanism.
-	 */
-	return 0;
-}
-
-static struct lock_file lock_file;
-
-int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
-{
-	int i, newfd, stage = 0;
-	unsigned char sha1[20];
-	struct tree_desc t[MAX_UNPACK_TREES];
-	struct unpack_trees_options opts;
-	int prefix_set = 0;
-	const struct option read_tree_options[] = {
-		{ OPTION_CALLBACK, 0, "index-output", NULL, "FILE",
-		  "write resulting index to <FILE>",
-		  PARSE_OPT_NONEG, index_output_cb },
-		OPT__VERBOSE(&opts.verbose_update),
-		OPT_GROUP("Merging"),
-		OPT_SET_INT('m', NULL, &opts.merge,
-			    "perform a merge in addition to a read", 1),
-		OPT_SET_INT(0, "trivial", &opts.trivial_merges_only,
-			    "3-way merge if no file level merging required", 1),
-		OPT_SET_INT(0, "aggressive", &opts.aggressive,
-			    "3-way merge in presence of adds and removes", 1),
-		OPT_SET_INT(0, "reset", &opts.reset,
-			    "same as -m, but discard unmerged entries", 1),
-		{ OPTION_STRING, 0, "prefix", &opts.prefix, "<subdirectory>/",
-		  "read the tree into the index under <subdirectory>/",
-		  PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP },
-		OPT_SET_INT('u', NULL, &opts.update,
-			    "update working tree with merge result", 1),
-		{ OPTION_CALLBACK, 0, "exclude-per-directory", &opts,
-		  "gitignore",
-		  "allow explicitly ignored files to be overwritten",
-		  PARSE_OPT_NONEG, exclude_per_directory_cb },
-		OPT_SET_INT('i', NULL, &opts.index_only,
-			    "don't check the working tree after merging", 1),
-		OPT_END()
-	};
-
-	memset(&opts, 0, sizeof(opts));
-	opts.head_idx = -1;
-	opts.src_index = &the_index;
-	opts.dst_index = &the_index;
-
-	git_config(git_default_config, NULL);
-
-	newfd = hold_locked_index(&lock_file, 1);
-
-	argc = parse_options(argc, argv, unused_prefix, read_tree_options,
-			     read_tree_usage, 0);
-
-	if (read_cache_unmerged() && (opts.prefix || opts.merge))
-		die("You need to resolve your current index first");
-
-	prefix_set = opts.prefix ? 1 : 0;
-	if (1 < opts.merge + opts.reset + prefix_set)
-		die("Which one? -m, --reset, or --prefix?");
-	stage = opts.merge = (opts.reset || opts.merge || prefix_set);
-
-	for (i = 0; i < argc; i++) {
-		const char *arg = argv[i];
-
-		if (get_sha1(arg, sha1))
-			die("Not a valid object name %s", arg);
-		if (list_tree(sha1) < 0)
-			die("failed to unpack tree object %s", arg);
-		stage++;
-	}
-	if (1 < opts.index_only + opts.update)
-		die("-u and -i at the same time makes no sense");
-	if ((opts.update||opts.index_only) && !opts.merge)
-		die("%s is meaningless without -m, --reset, or --prefix",
-		    opts.update ? "-u" : "-i");
-	if ((opts.dir && !opts.update))
-		die("--exclude-per-directory is meaningless unless -u");
-	if (opts.merge && !opts.index_only)
-		setup_work_tree();
-
-	if (opts.merge) {
-		if (stage < 2)
-			die("just how do you expect me to merge %d trees?", stage-1);
-		switch (stage - 1) {
-		case 1:
-			opts.fn = opts.prefix ? bind_merge : oneway_merge;
-			break;
-		case 2:
-			opts.fn = twoway_merge;
-			opts.initial_checkout = is_cache_unborn();
-			break;
-		case 3:
-		default:
-			opts.fn = threeway_merge;
-			break;
-		}
-
-		if (stage - 1 >= 3)
-			opts.head_idx = stage - 2;
-		else
-			opts.head_idx = 1;
-	}
-
-	cache_tree_free(&active_cache_tree);
-	for (i = 0; i < nr_trees; i++) {
-		struct tree *tree = trees[i];
-		parse_tree(tree);
-		init_tree_desc(t+i, tree->buffer, tree->size);
-	}
-	if (unpack_trees(nr_trees, t, &opts))
-		return 128;
-
-	/*
-	 * When reading only one tree (either the most basic form,
-	 * "-m ent" or "--reset ent" form), we can obtain a fully
-	 * valid cache-tree because the index must match exactly
-	 * what came from the tree.
-	 *
-	 * The same holds true if we are switching between two trees
-	 * using read-tree -m A B.  The index must match B after that.
-	 */
-	if (nr_trees == 1 && !opts.prefix)
-		prime_cache_tree(&active_cache_tree, trees[0]);
-	else if (nr_trees == 2 && opts.merge)
-		prime_cache_tree(&active_cache_tree, trees[1]);
-
-	if (write_cache(newfd, active_cache, active_nr) ||
-	    commit_locked_index(&lock_file))
-		die("unable to write new index file");
-	return 0;
-}
diff --git a/builtin-receive-pack.c b/builtin-receive-pack.c
deleted file mode 100644
index 6ec1d05..0000000
--- a/builtin-receive-pack.c
+++ /dev/null
@@ -1,707 +0,0 @@
-#include "cache.h"
-#include "pack.h"
-#include "refs.h"
-#include "pkt-line.h"
-#include "run-command.h"
-#include "exec_cmd.h"
-#include "commit.h"
-#include "object.h"
-#include "remote.h"
-#include "transport.h"
-
-static const char receive_pack_usage[] = "git receive-pack <git-dir>";
-
-enum deny_action {
-	DENY_UNCONFIGURED,
-	DENY_IGNORE,
-	DENY_WARN,
-	DENY_REFUSE,
-};
-
-static int deny_deletes;
-static int deny_non_fast_forwards;
-static enum deny_action deny_current_branch = DENY_UNCONFIGURED;
-static enum deny_action deny_delete_current = DENY_UNCONFIGURED;
-static int receive_fsck_objects;
-static int receive_unpack_limit = -1;
-static int transfer_unpack_limit = -1;
-static int unpack_limit = 100;
-static int report_status;
-static int prefer_ofs_delta = 1;
-static const char *head_name;
-static char *capabilities_to_send;
-
-static enum deny_action parse_deny_action(const char *var, const char *value)
-{
-	if (value) {
-		if (!strcasecmp(value, "ignore"))
-			return DENY_IGNORE;
-		if (!strcasecmp(value, "warn"))
-			return DENY_WARN;
-		if (!strcasecmp(value, "refuse"))
-			return DENY_REFUSE;
-	}
-	if (git_config_bool(var, value))
-		return DENY_REFUSE;
-	return DENY_IGNORE;
-}
-
-static int receive_pack_config(const char *var, const char *value, void *cb)
-{
-	if (strcmp(var, "receive.denydeletes") == 0) {
-		deny_deletes = git_config_bool(var, value);
-		return 0;
-	}
-
-	if (strcmp(var, "receive.denynonfastforwards") == 0) {
-		deny_non_fast_forwards = git_config_bool(var, value);
-		return 0;
-	}
-
-	if (strcmp(var, "receive.unpacklimit") == 0) {
-		receive_unpack_limit = git_config_int(var, value);
-		return 0;
-	}
-
-	if (strcmp(var, "transfer.unpacklimit") == 0) {
-		transfer_unpack_limit = git_config_int(var, value);
-		return 0;
-	}
-
-	if (strcmp(var, "receive.fsckobjects") == 0) {
-		receive_fsck_objects = git_config_bool(var, value);
-		return 0;
-	}
-
-	if (!strcmp(var, "receive.denycurrentbranch")) {
-		deny_current_branch = parse_deny_action(var, value);
-		return 0;
-	}
-
-	if (strcmp(var, "receive.denydeletecurrent") == 0) {
-		deny_delete_current = parse_deny_action(var, value);
-		return 0;
-	}
-
-	if (strcmp(var, "repack.usedeltabaseoffset") == 0) {
-		prefer_ofs_delta = git_config_bool(var, value);
-		return 0;
-	}
-
-	return git_default_config(var, value, cb);
-}
-
-static int show_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
-{
-	if (!capabilities_to_send)
-		packet_write(1, "%s %s\n", sha1_to_hex(sha1), path);
-	else
-		packet_write(1, "%s %s%c%s\n",
-			     sha1_to_hex(sha1), path, 0, capabilities_to_send);
-	capabilities_to_send = NULL;
-	return 0;
-}
-
-static void write_head_info(void)
-{
-	for_each_ref(show_ref, NULL);
-	if (capabilities_to_send)
-		show_ref("capabilities^{}", null_sha1, 0, NULL);
-
-}
-
-struct command {
-	struct command *next;
-	const char *error_string;
-	unsigned char old_sha1[20];
-	unsigned char new_sha1[20];
-	char ref_name[FLEX_ARRAY]; /* more */
-};
-
-static struct command *commands;
-
-static const char pre_receive_hook[] = "hooks/pre-receive";
-static const char post_receive_hook[] = "hooks/post-receive";
-
-static int run_status(int code, const char *cmd_name)
-{
-	switch (code) {
-	case 0:
-		return 0;
-	case -ERR_RUN_COMMAND_FORK:
-		return error("fork of %s failed", cmd_name);
-	case -ERR_RUN_COMMAND_EXEC:
-		return error("execute of %s failed", cmd_name);
-	case -ERR_RUN_COMMAND_PIPE:
-		return error("pipe failed");
-	case -ERR_RUN_COMMAND_WAITPID:
-		return error("waitpid failed");
-	case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
-		return error("waitpid is confused");
-	case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
-		return error("%s died of signal", cmd_name);
-	case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
-		return error("%s died strangely", cmd_name);
-	default:
-		error("%s exited with error code %d", cmd_name, -code);
-		return -code;
-	}
-}
-
-static int run_receive_hook(const char *hook_name)
-{
-	static char buf[sizeof(commands->old_sha1) * 2 + PATH_MAX + 4];
-	struct command *cmd;
-	struct child_process proc;
-	const char *argv[2];
-	int have_input = 0, code;
-
-	for (cmd = commands; !have_input && cmd; cmd = cmd->next) {
-		if (!cmd->error_string)
-			have_input = 1;
-	}
-
-	if (!have_input || access(hook_name, X_OK) < 0)
-		return 0;
-
-	argv[0] = hook_name;
-	argv[1] = NULL;
-
-	memset(&proc, 0, sizeof(proc));
-	proc.argv = argv;
-	proc.in = -1;
-	proc.stdout_to_stderr = 1;
-
-	code = start_command(&proc);
-	if (code)
-		return run_status(code, hook_name);
-	for (cmd = commands; cmd; cmd = cmd->next) {
-		if (!cmd->error_string) {
-			size_t n = snprintf(buf, sizeof(buf), "%s %s %s\n",
-				sha1_to_hex(cmd->old_sha1),
-				sha1_to_hex(cmd->new_sha1),
-				cmd->ref_name);
-			if (write_in_full(proc.in, buf, n) != n)
-				break;
-		}
-	}
-	close(proc.in);
-	return run_status(finish_command(&proc), hook_name);
-}
-
-static int run_update_hook(struct command *cmd)
-{
-	static const char update_hook[] = "hooks/update";
-	const char *argv[5];
-
-	if (access(update_hook, X_OK) < 0)
-		return 0;
-
-	argv[0] = update_hook;
-	argv[1] = cmd->ref_name;
-	argv[2] = sha1_to_hex(cmd->old_sha1);
-	argv[3] = sha1_to_hex(cmd->new_sha1);
-	argv[4] = NULL;
-
-	return run_status(run_command_v_opt(argv, RUN_COMMAND_NO_STDIN |
-					RUN_COMMAND_STDOUT_TO_STDERR),
-			update_hook);
-}
-
-static int is_ref_checked_out(const char *ref)
-{
-	if (is_bare_repository())
-		return 0;
-
-	if (!head_name)
-		return 0;
-	return !strcmp(head_name, ref);
-}
-
-static char *warn_unconfigured_deny_msg[] = {
-	"Updating the currently checked out branch may cause confusion,",
-	"as the index and work tree do not reflect changes that are in HEAD.",
-	"As a result, you may see the changes you just pushed into it",
-	"reverted when you run 'git diff' over there, and you may want",
-	"to run 'git reset --hard' before starting to work to recover.",
-	"",
-	"You can set 'receive.denyCurrentBranch' configuration variable to",
-	"'refuse' in the remote repository to forbid pushing into its",
-	"current branch."
-	"",
-	"To allow pushing into the current branch, you can set it to 'ignore';",
-	"but this is not recommended unless you arranged to update its work",
-	"tree to match what you pushed in some other way.",
-	"",
-	"To squelch this message, you can set it to 'warn'.",
-	"",
-	"Note that the default will change in a future version of git",
-	"to refuse updating the current branch unless you have the",
-	"configuration variable set to either 'ignore' or 'warn'."
-};
-
-static void warn_unconfigured_deny(void)
-{
-	int i;
-	for (i = 0; i < ARRAY_SIZE(warn_unconfigured_deny_msg); i++)
-		warning("%s", warn_unconfigured_deny_msg[i]);
-}
-
-static char *warn_unconfigured_deny_delete_current_msg[] = {
-	"Deleting the current branch can cause confusion by making the next",
-	"'git clone' not check out any file.",
-	"",
-	"You can set 'receive.denyDeleteCurrent' configuration variable to",
-	"'refuse' in the remote repository to disallow deleting the current",
-	"branch.",
-	"",
-	"You can set it to 'ignore' to allow such a delete without a warning.",
-	"",
-	"To make this warning message less loud, you can set it to 'warn'.",
-	"",
-	"Note that the default will change in a future version of git",
-	"to refuse deleting the current branch unless you have the",
-	"configuration variable set to either 'ignore' or 'warn'."
-};
-
-static void warn_unconfigured_deny_delete_current(void)
-{
-	int i;
-	for (i = 0;
-	     i < ARRAY_SIZE(warn_unconfigured_deny_delete_current_msg);
-	     i++)
-		warning("%s", warn_unconfigured_deny_delete_current_msg[i]);
-}
-
-static const char *update(struct command *cmd)
-{
-	const char *name = cmd->ref_name;
-	unsigned char *old_sha1 = cmd->old_sha1;
-	unsigned char *new_sha1 = cmd->new_sha1;
-	struct ref_lock *lock;
-
-	/* only refs/... are allowed */
-	if (prefixcmp(name, "refs/") || check_ref_format(name + 5)) {
-		error("refusing to create funny ref '%s' remotely", name);
-		return "funny refname";
-	}
-
-	if (is_ref_checked_out(name)) {
-		switch (deny_current_branch) {
-		case DENY_IGNORE:
-			break;
-		case DENY_UNCONFIGURED:
-		case DENY_WARN:
-			warning("updating the current branch");
-			if (deny_current_branch == DENY_UNCONFIGURED)
-				warn_unconfigured_deny();
-			break;
-		case DENY_REFUSE:
-			error("refusing to update checked out branch: %s", name);
-			return "branch is currently checked out";
-		}
-	}
-
-	if (!is_null_sha1(new_sha1) && !has_sha1_file(new_sha1)) {
-		error("unpack should have generated %s, "
-		      "but I can't find it!", sha1_to_hex(new_sha1));
-		return "bad pack";
-	}
-
-	if (!is_null_sha1(old_sha1) && is_null_sha1(new_sha1)) {
-		if (deny_deletes && !prefixcmp(name, "refs/heads/")) {
-			error("denying ref deletion for %s", name);
-			return "deletion prohibited";
-		}
-
-		if (!strcmp(name, head_name)) {
-			switch (deny_delete_current) {
-			case DENY_IGNORE:
-				break;
-			case DENY_WARN:
-			case DENY_UNCONFIGURED:
-				if (deny_delete_current == DENY_UNCONFIGURED)
-					warn_unconfigured_deny_delete_current();
-				warning("deleting the current branch");
-				break;
-			case DENY_REFUSE:
-				error("refusing to delete the current branch: %s", name);
-				return "deletion of the current branch prohibited";
-			}
-		}
-	}
-
-	if (deny_non_fast_forwards && !is_null_sha1(new_sha1) &&
-	    !is_null_sha1(old_sha1) &&
-	    !prefixcmp(name, "refs/heads/")) {
-		struct object *old_object, *new_object;
-		struct commit *old_commit, *new_commit;
-		struct commit_list *bases, *ent;
-
-		old_object = parse_object(old_sha1);
-		new_object = parse_object(new_sha1);
-
-		if (!old_object || !new_object ||
-		    old_object->type != OBJ_COMMIT ||
-		    new_object->type != OBJ_COMMIT) {
-			error("bad sha1 objects for %s", name);
-			return "bad ref";
-		}
-		old_commit = (struct commit *)old_object;
-		new_commit = (struct commit *)new_object;
-		bases = get_merge_bases(old_commit, new_commit, 1);
-		for (ent = bases; ent; ent = ent->next)
-			if (!hashcmp(old_sha1, ent->item->object.sha1))
-				break;
-		free_commit_list(bases);
-		if (!ent) {
-			error("denying non-fast forward %s"
-			      " (you should pull first)", name);
-			return "non-fast forward";
-		}
-	}
-	if (run_update_hook(cmd)) {
-		error("hook declined to update %s", name);
-		return "hook declined";
-	}
-
-	if (is_null_sha1(new_sha1)) {
-		if (!parse_object(old_sha1)) {
-			warning ("Allowing deletion of corrupt ref.");
-			old_sha1 = NULL;
-		}
-		if (delete_ref(name, old_sha1, 0)) {
-			error("failed to delete %s", name);
-			return "failed to delete";
-		}
-		return NULL; /* good */
-	}
-	else {
-		lock = lock_any_ref_for_update(name, old_sha1, 0);
-		if (!lock) {
-			error("failed to lock %s", name);
-			return "failed to lock";
-		}
-		if (write_ref_sha1(lock, new_sha1, "push")) {
-			return "failed to write"; /* error() already called */
-		}
-		return NULL; /* good */
-	}
-}
-
-static char update_post_hook[] = "hooks/post-update";
-
-static void run_update_post_hook(struct command *cmd)
-{
-	struct command *cmd_p;
-	int argc, status;
-	const char **argv;
-
-	for (argc = 0, cmd_p = cmd; cmd_p; cmd_p = cmd_p->next) {
-		if (cmd_p->error_string)
-			continue;
-		argc++;
-	}
-	if (!argc || access(update_post_hook, X_OK) < 0)
-		return;
-	argv = xmalloc(sizeof(*argv) * (2 + argc));
-	argv[0] = update_post_hook;
-
-	for (argc = 1, cmd_p = cmd; cmd_p; cmd_p = cmd_p->next) {
-		char *p;
-		if (cmd_p->error_string)
-			continue;
-		p = xmalloc(strlen(cmd_p->ref_name) + 1);
-		strcpy(p, cmd_p->ref_name);
-		argv[argc] = p;
-		argc++;
-	}
-	argv[argc] = NULL;
-	status = run_command_v_opt(argv, RUN_COMMAND_NO_STDIN
-			| RUN_COMMAND_STDOUT_TO_STDERR);
-	run_status(status, update_post_hook);
-}
-
-static void execute_commands(const char *unpacker_error)
-{
-	struct command *cmd = commands;
-	unsigned char sha1[20];
-
-	if (unpacker_error) {
-		while (cmd) {
-			cmd->error_string = "n/a (unpacker error)";
-			cmd = cmd->next;
-		}
-		return;
-	}
-
-	if (run_receive_hook(pre_receive_hook)) {
-		while (cmd) {
-			cmd->error_string = "pre-receive hook declined";
-			cmd = cmd->next;
-		}
-		return;
-	}
-
-	head_name = resolve_ref("HEAD", sha1, 0, NULL);
-
-	while (cmd) {
-		cmd->error_string = update(cmd);
-		cmd = cmd->next;
-	}
-}
-
-static void read_head_info(void)
-{
-	struct command **p = &commands;
-	for (;;) {
-		static char line[1000];
-		unsigned char old_sha1[20], new_sha1[20];
-		struct command *cmd;
-		char *refname;
-		int len, reflen;
-
-		len = packet_read_line(0, line, sizeof(line));
-		if (!len)
-			break;
-		if (line[len-1] == '\n')
-			line[--len] = 0;
-		if (len < 83 ||
-		    line[40] != ' ' ||
-		    line[81] != ' ' ||
-		    get_sha1_hex(line, old_sha1) ||
-		    get_sha1_hex(line + 41, new_sha1))
-			die("protocol error: expected old/new/ref, got '%s'",
-			    line);
-
-		refname = line + 82;
-		reflen = strlen(refname);
-		if (reflen + 82 < len) {
-			if (strstr(refname + reflen + 1, "report-status"))
-				report_status = 1;
-		}
-		cmd = xmalloc(sizeof(struct command) + len - 80);
-		hashcpy(cmd->old_sha1, old_sha1);
-		hashcpy(cmd->new_sha1, new_sha1);
-		memcpy(cmd->ref_name, line + 82, len - 81);
-		cmd->error_string = NULL;
-		cmd->next = NULL;
-		*p = cmd;
-		p = &cmd->next;
-	}
-}
-
-static const char *parse_pack_header(struct pack_header *hdr)
-{
-	switch (read_pack_header(0, hdr)) {
-	case PH_ERROR_EOF:
-		return "eof before pack header was fully read";
-
-	case PH_ERROR_PACK_SIGNATURE:
-		return "protocol error (pack signature mismatch detected)";
-
-	case PH_ERROR_PROTOCOL:
-		return "protocol error (pack version unsupported)";
-
-	default:
-		return "unknown error in parse_pack_header";
-
-	case 0:
-		return NULL;
-	}
-}
-
-static const char *pack_lockfile;
-
-static const char *unpack(void)
-{
-	struct pack_header hdr;
-	const char *hdr_err;
-	char hdr_arg[38];
-
-	hdr_err = parse_pack_header(&hdr);
-	if (hdr_err)
-		return hdr_err;
-	snprintf(hdr_arg, sizeof(hdr_arg),
-			"--pack_header=%"PRIu32",%"PRIu32,
-			ntohl(hdr.hdr_version), ntohl(hdr.hdr_entries));
-
-	if (ntohl(hdr.hdr_entries) < unpack_limit) {
-		int code, i = 0;
-		const char *unpacker[4];
-		unpacker[i++] = "unpack-objects";
-		if (receive_fsck_objects)
-			unpacker[i++] = "--strict";
-		unpacker[i++] = hdr_arg;
-		unpacker[i++] = NULL;
-		code = run_command_v_opt(unpacker, RUN_GIT_CMD);
-		if (!code)
-			return NULL;
-		run_status(code, unpacker[0]);
-		return "unpack-objects abnormal exit";
-	} else {
-		const char *keeper[7];
-		int s, status, i = 0;
-		char keep_arg[256];
-		struct child_process ip;
-
-		s = sprintf(keep_arg, "--keep=receive-pack %"PRIuMAX" on ", (uintmax_t) getpid());
-		if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
-			strcpy(keep_arg + s, "localhost");
-
-		keeper[i++] = "index-pack";
-		keeper[i++] = "--stdin";
-		if (receive_fsck_objects)
-			keeper[i++] = "--strict";
-		keeper[i++] = "--fix-thin";
-		keeper[i++] = hdr_arg;
-		keeper[i++] = keep_arg;
-		keeper[i++] = NULL;
-		memset(&ip, 0, sizeof(ip));
-		ip.argv = keeper;
-		ip.out = -1;
-		ip.git_cmd = 1;
-		status = start_command(&ip);
-		if (status) {
-			run_status(status, keeper[0]);
-			return "index-pack fork failed";
-		}
-		pack_lockfile = index_pack_lockfile(ip.out);
-		close(ip.out);
-		status = finish_command(&ip);
-		if (!status) {
-			reprepare_packed_git();
-			return NULL;
-		}
-		run_status(status, keeper[0]);
-		return "index-pack abnormal exit";
-	}
-}
-
-static void report(const char *unpack_status)
-{
-	struct command *cmd;
-	packet_write(1, "unpack %s\n",
-		     unpack_status ? unpack_status : "ok");
-	for (cmd = commands; cmd; cmd = cmd->next) {
-		if (!cmd->error_string)
-			packet_write(1, "ok %s\n",
-				     cmd->ref_name);
-		else
-			packet_write(1, "ng %s %s\n",
-				     cmd->ref_name, cmd->error_string);
-	}
-	packet_flush(1);
-}
-
-static int delete_only(struct command *cmd)
-{
-	while (cmd) {
-		if (!is_null_sha1(cmd->new_sha1))
-			return 0;
-		cmd = cmd->next;
-	}
-	return 1;
-}
-
-static int add_refs_from_alternate(struct alternate_object_database *e, void *unused)
-{
-	char *other;
-	size_t len;
-	struct remote *remote;
-	struct transport *transport;
-	const struct ref *extra;
-
-	e->name[-1] = '\0';
-	other = xstrdup(make_absolute_path(e->base));
-	e->name[-1] = '/';
-	len = strlen(other);
-
-	while (other[len-1] == '/')
-		other[--len] = '\0';
-	if (len < 8 || memcmp(other + len - 8, "/objects", 8))
-		return 0;
-	/* Is this a git repository with refs? */
-	memcpy(other + len - 8, "/refs", 6);
-	if (!is_directory(other))
-		return 0;
-	other[len - 8] = '\0';
-	remote = remote_get(other);
-	transport = transport_get(remote, other);
-	for (extra = transport_get_remote_refs(transport);
-	     extra;
-	     extra = extra->next) {
-		add_extra_ref(".have", extra->old_sha1, 0);
-	}
-	transport_disconnect(transport);
-	free(other);
-	return 0;
-}
-
-static void add_alternate_refs(void)
-{
-	foreach_alt_odb(add_refs_from_alternate, NULL);
-}
-
-int cmd_receive_pack(int argc, const char **argv, const char *prefix)
-{
-	int i;
-	char *dir = NULL;
-
-	argv++;
-	for (i = 1; i < argc; i++) {
-		const char *arg = *argv++;
-
-		if (*arg == '-') {
-			/* Do flag handling here */
-			usage(receive_pack_usage);
-		}
-		if (dir)
-			usage(receive_pack_usage);
-		dir = xstrdup(arg);
-	}
-	if (!dir)
-		usage(receive_pack_usage);
-
-	setup_path();
-
-	if (!enter_repo(dir, 0))
-		die("'%s' does not appear to be a git repository", dir);
-
-	if (is_repository_shallow())
-		die("attempt to push into a shallow repository");
-
-	git_config(receive_pack_config, NULL);
-
-	if (0 <= transfer_unpack_limit)
-		unpack_limit = transfer_unpack_limit;
-	else if (0 <= receive_unpack_limit)
-		unpack_limit = receive_unpack_limit;
-
-	capabilities_to_send = (prefer_ofs_delta) ?
-		" report-status delete-refs ofs-delta " :
-		" report-status delete-refs ";
-
-	add_alternate_refs();
-	write_head_info();
-	clear_extra_refs();
-
-	/* EOF */
-	packet_flush(1);
-
-	read_head_info();
-	if (commands) {
-		const char *unpack_status = NULL;
-
-		if (!delete_only(commands))
-			unpack_status = unpack();
-		execute_commands(unpack_status);
-		if (pack_lockfile)
-			unlink_or_warn(pack_lockfile);
-		if (report_status)
-			report(unpack_status);
-		run_receive_hook(post_receive_hook);
-		run_update_post_hook(commands);
-	}
-	return 0;
-}
diff --git a/builtin-reflog.c b/builtin-reflog.c
deleted file mode 100644
index 95198c5..0000000
--- a/builtin-reflog.c
+++ /dev/null
@@ -1,716 +0,0 @@
-#include "cache.h"
-#include "builtin.h"
-#include "commit.h"
-#include "refs.h"
-#include "dir.h"
-#include "tree-walk.h"
-#include "diff.h"
-#include "revision.h"
-#include "reachable.h"
-
-/*
- * reflog expire
- */
-
-static const char reflog_expire_usage[] =
-"git reflog (show|expire) [--verbose] [--dry-run] [--stale-fix] [--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>...";
-static const char reflog_delete_usage[] =
-"git reflog delete [--verbose] [--dry-run] [--rewrite] [--updateref] <refs>...";
-
-static unsigned long default_reflog_expire;
-static unsigned long default_reflog_expire_unreachable;
-
-struct cmd_reflog_expire_cb {
-	struct rev_info revs;
-	int dry_run;
-	int stalefix;
-	int rewrite;
-	int updateref;
-	int verbose;
-	unsigned long expire_total;
-	unsigned long expire_unreachable;
-	int recno;
-};
-
-struct expire_reflog_cb {
-	FILE *newlog;
-	const char *ref;
-	struct commit *ref_commit;
-	struct cmd_reflog_expire_cb *cmd;
-	unsigned char last_kept_sha1[20];
-};
-
-struct collected_reflog {
-	unsigned char sha1[20];
-	char reflog[FLEX_ARRAY];
-};
-struct collect_reflog_cb {
-	struct collected_reflog **e;
-	int alloc;
-	int nr;
-};
-
-#define INCOMPLETE	(1u<<10)
-#define STUDYING	(1u<<11)
-#define REACHABLE	(1u<<12)
-
-static int tree_is_complete(const unsigned char *sha1)
-{
-	struct tree_desc desc;
-	struct name_entry entry;
-	int complete;
-	struct tree *tree;
-
-	tree = lookup_tree(sha1);
-	if (!tree)
-		return 0;
-	if (tree->object.flags & SEEN)
-		return 1;
-	if (tree->object.flags & INCOMPLETE)
-		return 0;
-
-	if (!tree->buffer) {
-		enum object_type type;
-		unsigned long size;
-		void *data = read_sha1_file(sha1, &type, &size);
-		if (!data) {
-			tree->object.flags |= INCOMPLETE;
-			return 0;
-		}
-		tree->buffer = data;
-		tree->size = size;
-	}
-	init_tree_desc(&desc, tree->buffer, tree->size);
-	complete = 1;
-	while (tree_entry(&desc, &entry)) {
-		if (!has_sha1_file(entry.sha1) ||
-		    (S_ISDIR(entry.mode) && !tree_is_complete(entry.sha1))) {
-			tree->object.flags |= INCOMPLETE;
-			complete = 0;
-		}
-	}
-	free(tree->buffer);
-	tree->buffer = NULL;
-
-	if (complete)
-		tree->object.flags |= SEEN;
-	return complete;
-}
-
-static int commit_is_complete(struct commit *commit)
-{
-	struct object_array study;
-	struct object_array found;
-	int is_incomplete = 0;
-	int i;
-
-	/* early return */
-	if (commit->object.flags & SEEN)
-		return 1;
-	if (commit->object.flags & INCOMPLETE)
-		return 0;
-	/*
-	 * Find all commits that are reachable and are not marked as
-	 * SEEN.  Then make sure the trees and blobs contained are
-	 * complete.  After that, mark these commits also as SEEN.
-	 * If some of the objects that are needed to complete this
-	 * commit are missing, mark this commit as INCOMPLETE.
-	 */
-	memset(&study, 0, sizeof(study));
-	memset(&found, 0, sizeof(found));
-	add_object_array(&commit->object, NULL, &study);
-	add_object_array(&commit->object, NULL, &found);
-	commit->object.flags |= STUDYING;
-	while (study.nr) {
-		struct commit *c;
-		struct commit_list *parent;
-
-		c = (struct commit *)study.objects[--study.nr].item;
-		if (!c->object.parsed && !parse_object(c->object.sha1))
-			c->object.flags |= INCOMPLETE;
-
-		if (c->object.flags & INCOMPLETE) {
-			is_incomplete = 1;
-			break;
-		}
-		else if (c->object.flags & SEEN)
-			continue;
-		for (parent = c->parents; parent; parent = parent->next) {
-			struct commit *p = parent->item;
-			if (p->object.flags & STUDYING)
-				continue;
-			p->object.flags |= STUDYING;
-			add_object_array(&p->object, NULL, &study);
-			add_object_array(&p->object, NULL, &found);
-		}
-	}
-	if (!is_incomplete) {
-		/*
-		 * make sure all commits in "found" array have all the
-		 * necessary objects.
-		 */
-		for (i = 0; i < found.nr; i++) {
-			struct commit *c =
-				(struct commit *)found.objects[i].item;
-			if (!tree_is_complete(c->tree->object.sha1)) {
-				is_incomplete = 1;
-				c->object.flags |= INCOMPLETE;
-			}
-		}
-		if (!is_incomplete) {
-			/* mark all found commits as complete, iow SEEN */
-			for (i = 0; i < found.nr; i++)
-				found.objects[i].item->flags |= SEEN;
-		}
-	}
-	/* clear flags from the objects we traversed */
-	for (i = 0; i < found.nr; i++)
-		found.objects[i].item->flags &= ~STUDYING;
-	if (is_incomplete)
-		commit->object.flags |= INCOMPLETE;
-	else {
-		/*
-		 * If we come here, we have (1) traversed the ancestry chain
-		 * from the "commit" until we reach SEEN commits (which are
-		 * known to be complete), and (2) made sure that the commits
-		 * encountered during the above traversal refer to trees that
-		 * are complete.  Which means that we know *all* the commits
-		 * we have seen during this process are complete.
-		 */
-		for (i = 0; i < found.nr; i++)
-			found.objects[i].item->flags |= SEEN;
-	}
-	/* free object arrays */
-	free(study.objects);
-	free(found.objects);
-	return !is_incomplete;
-}
-
-static int keep_entry(struct commit **it, unsigned char *sha1)
-{
-	struct commit *commit;
-
-	if (is_null_sha1(sha1))
-		return 1;
-	commit = lookup_commit_reference_gently(sha1, 1);
-	if (!commit)
-		return 0;
-
-	/*
-	 * Make sure everything in this commit exists.
-	 *
-	 * We have walked all the objects reachable from the refs
-	 * and cache earlier.  The commits reachable by this commit
-	 * must meet SEEN commits -- and then we should mark them as
-	 * SEEN as well.
-	 */
-	if (!commit_is_complete(commit))
-		return 0;
-	*it = commit;
-	return 1;
-}
-
-static int unreachable(struct expire_reflog_cb *cb, struct commit *commit, unsigned char *sha1)
-{
-	/*
-	 * We may or may not have the commit yet - if not, look it
-	 * up using the supplied sha1.
-	 */
-	if (!commit) {
-		if (is_null_sha1(sha1))
-			return 0;
-
-		commit = lookup_commit_reference_gently(sha1, 1);
-
-		/* Not a commit -- keep it */
-		if (!commit)
-			return 0;
-	}
-
-	/* Reachable from the current ref?  Don't prune. */
-	if (commit->object.flags & REACHABLE)
-		return 0;
-	if (in_merge_bases(commit, &cb->ref_commit, 1))
-		return 0;
-
-	/* We can't reach it - prune it. */
-	return 1;
-}
-
-static void mark_reachable(struct commit *commit, unsigned long expire_limit)
-{
-	/*
-	 * We need to compute whether the commit on either side of a reflog
-	 * entry is reachable from the tip of the ref for all entries.
-	 * Mark commits that are reachable from the tip down to the
-	 * time threshold first; we know a commit marked thusly is
-	 * reachable from the tip without running in_merge_bases()
-	 * at all.
-	 */
-	struct commit_list *pending = NULL;
-
-	commit_list_insert(commit, &pending);
-	while (pending) {
-		struct commit_list *entry = pending;
-		struct commit_list *parent;
-		pending = entry->next;
-		commit = entry->item;
-		free(entry);
-		if (commit->object.flags & REACHABLE)
-			continue;
-		if (parse_commit(commit))
-			continue;
-		commit->object.flags |= REACHABLE;
-		if (commit->date < expire_limit)
-			continue;
-		parent = commit->parents;
-		while (parent) {
-			commit = parent->item;
-			parent = parent->next;
-			if (commit->object.flags & REACHABLE)
-				continue;
-			commit_list_insert(commit, &pending);
-		}
-	}
-}
-
-static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
-		const char *email, unsigned long timestamp, int tz,
-		const char *message, void *cb_data)
-{
-	struct expire_reflog_cb *cb = cb_data;
-	struct commit *old, *new;
-
-	if (timestamp < cb->cmd->expire_total)
-		goto prune;
-
-	if (cb->cmd->rewrite)
-		osha1 = cb->last_kept_sha1;
-
-	old = new = NULL;
-	if (cb->cmd->stalefix &&
-	    (!keep_entry(&old, osha1) || !keep_entry(&new, nsha1)))
-		goto prune;
-
-	if (timestamp < cb->cmd->expire_unreachable) {
-		if (!cb->ref_commit)
-			goto prune;
-		if (unreachable(cb, old, osha1) || unreachable(cb, new, nsha1))
-			goto prune;
-	}
-
-	if (cb->cmd->recno && --(cb->cmd->recno) == 0)
-		goto prune;
-
-	if (cb->newlog) {
-		char sign = (tz < 0) ? '-' : '+';
-		int zone = (tz < 0) ? (-tz) : tz;
-		fprintf(cb->newlog, "%s %s %s %lu %c%04d\t%s",
-			sha1_to_hex(osha1), sha1_to_hex(nsha1),
-			email, timestamp, sign, zone,
-			message);
-		hashcpy(cb->last_kept_sha1, nsha1);
-	}
-	if (cb->cmd->verbose)
-		printf("keep %s", message);
-	return 0;
- prune:
-	if (!cb->newlog || cb->cmd->verbose)
-		printf("%sprune %s", cb->newlog ? "" : "would ", message);
-	return 0;
-}
-
-static int expire_reflog(const char *ref, const unsigned char *sha1, int unused, void *cb_data)
-{
-	struct cmd_reflog_expire_cb *cmd = cb_data;
-	struct expire_reflog_cb cb;
-	struct ref_lock *lock;
-	char *log_file, *newlog_path = NULL;
-	int status = 0;
-
-	memset(&cb, 0, sizeof(cb));
-
-	/*
-	 * we take the lock for the ref itself to prevent it from
-	 * getting updated.
-	 */
-	lock = lock_any_ref_for_update(ref, sha1, 0);
-	if (!lock)
-		return error("cannot lock ref '%s'", ref);
-	log_file = git_pathdup("logs/%s", ref);
-	if (!file_exists(log_file))
-		goto finish;
-	if (!cmd->dry_run) {
-		newlog_path = git_pathdup("logs/%s.lock", ref);
-		cb.newlog = fopen(newlog_path, "w");
-	}
-
-	cb.ref_commit = lookup_commit_reference_gently(sha1, 1);
-	cb.ref = ref;
-	cb.cmd = cmd;
-	if (cb.ref_commit)
-		mark_reachable(cb.ref_commit, cmd->expire_total);
-	for_each_reflog_ent(ref, expire_reflog_ent, &cb);
-	if (cb.ref_commit)
-		clear_commit_marks(cb.ref_commit, REACHABLE);
- finish:
-	if (cb.newlog) {
-		if (fclose(cb.newlog)) {
-			status |= error("%s: %s", strerror(errno),
-					newlog_path);
-			unlink(newlog_path);
-		} else if (cmd->updateref &&
-			(write_in_full(lock->lock_fd,
-				sha1_to_hex(cb.last_kept_sha1), 40) != 40 ||
-			 write_in_full(lock->lock_fd, "\n", 1) != 1 ||
-			 close_ref(lock) < 0)) {
-			status |= error("Couldn't write %s",
-				lock->lk->filename);
-			unlink(newlog_path);
-		} else if (rename(newlog_path, log_file)) {
-			status |= error("cannot rename %s to %s",
-					newlog_path, log_file);
-			unlink(newlog_path);
-		} else if (cmd->updateref && commit_ref(lock)) {
-			status |= error("Couldn't set %s", lock->ref_name);
-		} else {
-			adjust_shared_perm(log_file);
-		}
-	}
-	free(newlog_path);
-	free(log_file);
-	unlock_ref(lock);
-	return status;
-}
-
-static int collect_reflog(const char *ref, const unsigned char *sha1, int unused, void *cb_data)
-{
-	struct collected_reflog *e;
-	struct collect_reflog_cb *cb = cb_data;
-	size_t namelen = strlen(ref);
-
-	e = xmalloc(sizeof(*e) + namelen + 1);
-	hashcpy(e->sha1, sha1);
-	memcpy(e->reflog, ref, namelen + 1);
-	ALLOC_GROW(cb->e, cb->nr + 1, cb->alloc);
-	cb->e[cb->nr++] = e;
-	return 0;
-}
-
-static struct reflog_expire_cfg {
-	struct reflog_expire_cfg *next;
-	unsigned long expire_total;
-	unsigned long expire_unreachable;
-	size_t len;
-	char pattern[FLEX_ARRAY];
-} *reflog_expire_cfg, **reflog_expire_cfg_tail;
-
-static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len)
-{
-	struct reflog_expire_cfg *ent;
-
-	if (!reflog_expire_cfg_tail)
-		reflog_expire_cfg_tail = &reflog_expire_cfg;
-
-	for (ent = reflog_expire_cfg; ent; ent = ent->next)
-		if (ent->len == len &&
-		    !memcmp(ent->pattern, pattern, len))
-			return ent;
-
-	ent = xcalloc(1, (sizeof(*ent) + len));
-	memcpy(ent->pattern, pattern, len);
-	ent->len = len;
-	*reflog_expire_cfg_tail = ent;
-	reflog_expire_cfg_tail = &(ent->next);
-	return ent;
-}
-
-static int parse_expire_cfg_value(const char *var, const char *value, unsigned long *expire)
-{
-	if (!value)
-		return config_error_nonbool(var);
-	if (!strcmp(value, "never") || !strcmp(value, "false")) {
-		*expire = 0;
-		return 0;
-	}
-	*expire = approxidate(value);
-	return 0;
-}
-
-/* expiry timer slot */
-#define EXPIRE_TOTAL   01
-#define EXPIRE_UNREACH 02
-
-static int reflog_expire_config(const char *var, const char *value, void *cb)
-{
-	const char *lastdot = strrchr(var, '.');
-	unsigned long expire;
-	int slot;
-	struct reflog_expire_cfg *ent;
-
-	if (!lastdot || prefixcmp(var, "gc."))
-		return git_default_config(var, value, cb);
-
-	if (!strcmp(lastdot, ".reflogexpire")) {
-		slot = EXPIRE_TOTAL;
-		if (parse_expire_cfg_value(var, value, &expire))
-			return -1;
-	} else if (!strcmp(lastdot, ".reflogexpireunreachable")) {
-		slot = EXPIRE_UNREACH;
-		if (parse_expire_cfg_value(var, value, &expire))
-			return -1;
-	} else
-		return git_default_config(var, value, cb);
-
-	if (lastdot == var + 2) {
-		switch (slot) {
-		case EXPIRE_TOTAL:
-			default_reflog_expire = expire;
-			break;
-		case EXPIRE_UNREACH:
-			default_reflog_expire_unreachable = expire;
-			break;
-		}
-		return 0;
-	}
-
-	ent = find_cfg_ent(var + 3, lastdot - (var+3));
-	if (!ent)
-		return -1;
-	switch (slot) {
-	case EXPIRE_TOTAL:
-		ent->expire_total = expire;
-		break;
-	case EXPIRE_UNREACH:
-		ent->expire_unreachable = expire;
-		break;
-	}
-	return 0;
-}
-
-static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, int slot, const char *ref)
-{
-	struct reflog_expire_cfg *ent;
-
-	if (slot == (EXPIRE_TOTAL|EXPIRE_UNREACH))
-		return; /* both given explicitly -- nothing to tweak */
-
-	for (ent = reflog_expire_cfg; ent; ent = ent->next) {
-		if (!fnmatch(ent->pattern, ref, 0)) {
-			if (!(slot & EXPIRE_TOTAL))
-				cb->expire_total = ent->expire_total;
-			if (!(slot & EXPIRE_UNREACH))
-				cb->expire_unreachable = ent->expire_unreachable;
-			return;
-		}
-	}
-
-	/*
-	 * If unconfigured, make stash never expire
-	 */
-	if (!strcmp(ref, "refs/stash")) {
-		if (!(slot & EXPIRE_TOTAL))
-			cb->expire_total = 0;
-		if (!(slot & EXPIRE_UNREACH))
-			cb->expire_unreachable = 0;
-		return;
-	}
-
-	/* Nothing matched -- use the default value */
-	if (!(slot & EXPIRE_TOTAL))
-		cb->expire_total = default_reflog_expire;
-	if (!(slot & EXPIRE_UNREACH))
-		cb->expire_unreachable = default_reflog_expire_unreachable;
-}
-
-static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
-{
-	struct cmd_reflog_expire_cb cb;
-	unsigned long now = time(NULL);
-	int i, status, do_all;
-	int explicit_expiry = 0;
-
-	git_config(reflog_expire_config, NULL);
-
-	save_commit_buffer = 0;
-	do_all = status = 0;
-	memset(&cb, 0, sizeof(cb));
-
-	if (!default_reflog_expire_unreachable)
-		default_reflog_expire_unreachable = now - 30 * 24 * 3600;
-	if (!default_reflog_expire)
-		default_reflog_expire = now - 90 * 24 * 3600;
-	cb.expire_total = default_reflog_expire;
-	cb.expire_unreachable = default_reflog_expire_unreachable;
-
-	for (i = 1; i < argc; i++) {
-		const char *arg = argv[i];
-		if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
-			cb.dry_run = 1;
-		else if (!prefixcmp(arg, "--expire=")) {
-			cb.expire_total = approxidate(arg + 9);
-			explicit_expiry |= EXPIRE_TOTAL;
-		}
-		else if (!prefixcmp(arg, "--expire-unreachable=")) {
-			cb.expire_unreachable = approxidate(arg + 21);
-			explicit_expiry |= EXPIRE_UNREACH;
-		}
-		else if (!strcmp(arg, "--stale-fix"))
-			cb.stalefix = 1;
-		else if (!strcmp(arg, "--rewrite"))
-			cb.rewrite = 1;
-		else if (!strcmp(arg, "--updateref"))
-			cb.updateref = 1;
-		else if (!strcmp(arg, "--all"))
-			do_all = 1;
-		else if (!strcmp(arg, "--verbose"))
-			cb.verbose = 1;
-		else if (!strcmp(arg, "--")) {
-			i++;
-			break;
-		}
-		else if (arg[0] == '-')
-			usage(reflog_expire_usage);
-		else
-			break;
-	}
-
-	/*
-	 * We can trust the commits and objects reachable from refs
-	 * even in older repository.  We cannot trust what's reachable
-	 * from reflog if the repository was pruned with older git.
-	 */
-	if (cb.stalefix) {
-		init_revisions(&cb.revs, prefix);
-		if (cb.verbose)
-			printf("Marking reachable objects...");
-		mark_reachable_objects(&cb.revs, 0);
-		if (cb.verbose)
-			putchar('\n');
-	}
-
-	if (do_all) {
-		struct collect_reflog_cb collected;
-		int i;
-
-		memset(&collected, 0, sizeof(collected));
-		for_each_reflog(collect_reflog, &collected);
-		for (i = 0; i < collected.nr; i++) {
-			struct collected_reflog *e = collected.e[i];
-			set_reflog_expiry_param(&cb, explicit_expiry, e->reflog);
-			status |= expire_reflog(e->reflog, e->sha1, 0, &cb);
-			free(e);
-		}
-		free(collected.e);
-	}
-
-	for (; i < argc; i++) {
-		char *ref;
-		unsigned char sha1[20];
-		if (!dwim_log(argv[i], strlen(argv[i]), sha1, &ref)) {
-			status |= error("%s points nowhere!", argv[i]);
-			continue;
-		}
-		set_reflog_expiry_param(&cb, explicit_expiry, ref);
-		status |= expire_reflog(ref, sha1, 0, &cb);
-	}
-	return status;
-}
-
-static int count_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
-		const char *email, unsigned long timestamp, int tz,
-		const char *message, void *cb_data)
-{
-	struct cmd_reflog_expire_cb *cb = cb_data;
-	if (!cb->expire_total || timestamp < cb->expire_total)
-		cb->recno++;
-	return 0;
-}
-
-static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
-{
-	struct cmd_reflog_expire_cb cb;
-	int i, status = 0;
-
-	memset(&cb, 0, sizeof(cb));
-
-	for (i = 1; i < argc; i++) {
-		const char *arg = argv[i];
-		if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
-			cb.dry_run = 1;
-		else if (!strcmp(arg, "--rewrite"))
-			cb.rewrite = 1;
-		else if (!strcmp(arg, "--updateref"))
-			cb.updateref = 1;
-		else if (!strcmp(arg, "--verbose"))
-			cb.verbose = 1;
-		else if (!strcmp(arg, "--")) {
-			i++;
-			break;
-		}
-		else if (arg[0] == '-')
-			usage(reflog_delete_usage);
-		else
-			break;
-	}
-
-	if (argc - i < 1)
-		return error("Nothing to delete?");
-
-	for ( ; i < argc; i++) {
-		const char *spec = strstr(argv[i], "@{");
-		unsigned char sha1[20];
-		char *ep, *ref;
-		int recno;
-
-		if (!spec) {
-			status |= error("Not a reflog: %s", argv[i]);
-			continue;
-		}
-
-		if (!dwim_log(argv[i], spec - argv[i], sha1, &ref)) {
-			status |= error("no reflog for '%s'", argv[i]);
-			continue;
-		}
-
-		recno = strtoul(spec + 2, &ep, 10);
-		if (*ep == '}') {
-			cb.recno = -recno;
-			for_each_reflog_ent(ref, count_reflog_ent, &cb);
-		} else {
-			cb.expire_total = approxidate(spec + 2);
-			for_each_reflog_ent(ref, count_reflog_ent, &cb);
-			cb.expire_total = 0;
-		}
-
-		status |= expire_reflog(ref, sha1, 0, &cb);
-		free(ref);
-	}
-	return status;
-}
-
-/*
- * main "reflog"
- */
-
-static const char reflog_usage[] =
-"git reflog [ show | expire | delete ]";
-
-int cmd_reflog(int argc, const char **argv, const char *prefix)
-{
-	/* With no command, we default to showing it. */
-	if (argc < 2 || *argv[1] == '-')
-		return cmd_log_reflog(argc, argv, prefix);
-
-	if (!strcmp(argv[1], "show"))
-		return cmd_log_reflog(argc - 1, argv + 1, prefix);
-
-	if (!strcmp(argv[1], "expire"))
-		return cmd_reflog_expire(argc - 1, argv + 1, prefix);
-
-	if (!strcmp(argv[1], "delete"))
-		return cmd_reflog_delete(argc - 1, argv + 1, prefix);
-
-	/* Not a recognized reflog command..*/
-	usage(reflog_usage);
-}
diff --git a/builtin-remote.c b/builtin-remote.c
deleted file mode 100644
index 008abfe..0000000
--- a/builtin-remote.c
+++ /dev/null
@@ -1,1367 +0,0 @@
-#include "cache.h"
-#include "parse-options.h"
-#include "transport.h"
-#include "remote.h"
-#include "string-list.h"
-#include "strbuf.h"
-#include "run-command.h"
-#include "refs.h"
-
-static const char * const builtin_remote_usage[] = {
-	"git remote [-v | --verbose]",
-	"git remote add [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url>",
-	"git remote rename <old> <new>",
-	"git remote rm <name>",
-	"git remote set-head <name> [-a | -d | <branch>]",
-	"git remote show [-n] <name>",
-	"git remote prune [-n | --dry-run] <name>",
-	"git remote [-v | --verbose] update [-p | --prune] [group]",
-	NULL
-};
-
-#define GET_REF_STATES (1<<0)
-#define GET_HEAD_NAMES (1<<1)
-#define GET_PUSH_REF_STATES (1<<2)
-
-static int verbose;
-
-static int show_all(void);
-static int prune_remote(const char *remote, int dry_run);
-
-static inline int postfixcmp(const char *string, const char *postfix)
-{
-	int len1 = strlen(string), len2 = strlen(postfix);
-	if (len1 < len2)
-		return 1;
-	return strcmp(string + len1 - len2, postfix);
-}
-
-static int opt_parse_track(const struct option *opt, const char *arg, int not)
-{
-	struct string_list *list = opt->value;
-	if (not)
-		string_list_clear(list, 0);
-	else
-		string_list_append(arg, list);
-	return 0;
-}
-
-static int fetch_remote(const char *name)
-{
-	const char *argv[] = { "fetch", name, NULL, NULL };
-	if (verbose) {
-		argv[1] = "-v";
-		argv[2] = name;
-	}
-	printf("Updating %s\n", name);
-	if (run_command_v_opt(argv, RUN_GIT_CMD))
-		return error("Could not fetch %s", name);
-	return 0;
-}
-
-static int add(int argc, const char **argv)
-{
-	int fetch = 0, mirror = 0;
-	struct string_list track = { NULL, 0, 0 };
-	const char *master = NULL;
-	struct remote *remote;
-	struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT;
-	const char *name, *url;
-	int i;
-
-	struct option options[] = {
-		OPT_GROUP("add specific options"),
-		OPT_BOOLEAN('f', "fetch", &fetch, "fetch the remote branches"),
-		OPT_CALLBACK('t', "track", &track, "branch",
-			"branch(es) to track", opt_parse_track),
-		OPT_STRING('m', "master", &master, "branch", "master branch"),
-		OPT_BOOLEAN(0, "mirror", &mirror, "no separate remotes"),
-		OPT_END()
-	};
-
-	argc = parse_options(argc, argv, NULL, options, builtin_remote_usage,
-			     0);
-
-	if (argc < 2)
-		usage_with_options(builtin_remote_usage, options);
-
-	name = argv[0];
-	url = argv[1];
-
-	remote = remote_get(name);
-	if (remote && (remote->url_nr > 1 || strcmp(name, remote->url[0]) ||
-			remote->fetch_refspec_nr))
-		die("remote %s already exists.", name);
-
-	strbuf_addf(&buf2, "refs/heads/test:refs/remotes/%s/test", name);
-	if (!valid_fetch_refspec(buf2.buf))
-		die("'%s' is not a valid remote name", name);
-
-	strbuf_addf(&buf, "remote.%s.url", name);
-	if (git_config_set(buf.buf, url))
-		return 1;
-
-	strbuf_reset(&buf);
-	strbuf_addf(&buf, "remote.%s.fetch", name);
-
-	if (track.nr == 0)
-		string_list_append("*", &track);
-	for (i = 0; i < track.nr; i++) {
-		struct string_list_item *item = track.items + i;
-
-		strbuf_reset(&buf2);
-		strbuf_addch(&buf2, '+');
-		if (mirror)
-			strbuf_addf(&buf2, "refs/%s:refs/%s",
-					item->string, item->string);
-		else
-			strbuf_addf(&buf2, "refs/heads/%s:refs/remotes/%s/%s",
-					item->string, name, item->string);
-		if (git_config_set_multivar(buf.buf, buf2.buf, "^$", 0))
-			return 1;
-	}
-
-	if (mirror) {
-		strbuf_reset(&buf);
-		strbuf_addf(&buf, "remote.%s.mirror", name);
-		if (git_config_set(buf.buf, "true"))
-			return 1;
-	}
-
-	if (fetch && fetch_remote(name))
-		return 1;
-
-	if (master) {
-		strbuf_reset(&buf);
-		strbuf_addf(&buf, "refs/remotes/%s/HEAD", name);
-
-		strbuf_reset(&buf2);
-		strbuf_addf(&buf2, "refs/remotes/%s/%s", name, master);
-
-		if (create_symref(buf.buf, buf2.buf, "remote add"))
-			return error("Could not setup master '%s'", master);
-	}
-
-	strbuf_release(&buf);
-	strbuf_release(&buf2);
-	string_list_clear(&track, 0);
-
-	return 0;
-}
-
-struct branch_info {
-	char *remote_name;
-	struct string_list merge;
-	int rebase;
-};
-
-static struct string_list branch_list;
-
-static const char *abbrev_ref(const char *name, const char *prefix)
-{
-	const char *abbrev = skip_prefix(name, prefix);
-	if (abbrev)
-		return abbrev;
-	return name;
-}
-#define abbrev_branch(name) abbrev_ref((name), "refs/heads/")
-
-static int config_read_branches(const char *key, const char *value, void *cb)
-{
-	if (!prefixcmp(key, "branch.")) {
-		const char *orig_key = key;
-		char *name;
-		struct string_list_item *item;
-		struct branch_info *info;
-		enum { REMOTE, MERGE, REBASE } type;
-
-		key += 7;
-		if (!postfixcmp(key, ".remote")) {
-			name = xstrndup(key, strlen(key) - 7);
-			type = REMOTE;
-		} else if (!postfixcmp(key, ".merge")) {
-			name = xstrndup(key, strlen(key) - 6);
-			type = MERGE;
-		} else if (!postfixcmp(key, ".rebase")) {
-			name = xstrndup(key, strlen(key) - 7);
-			type = REBASE;
-		} else
-			return 0;
-
-		item = string_list_insert(name, &branch_list);
-
-		if (!item->util)
-			item->util = xcalloc(sizeof(struct branch_info), 1);
-		info = item->util;
-		if (type == REMOTE) {
-			if (info->remote_name)
-				warning("more than one %s", orig_key);
-			info->remote_name = xstrdup(value);
-		} else if (type == MERGE) {
-			char *space = strchr(value, ' ');
-			value = abbrev_branch(value);
-			while (space) {
-				char *merge;
-				merge = xstrndup(value, space - value);
-				string_list_append(merge, &info->merge);
-				value = abbrev_branch(space + 1);
-				space = strchr(value, ' ');
-			}
-			string_list_append(xstrdup(value), &info->merge);
-		} else
-			info->rebase = git_config_bool(orig_key, value);
-	}
-	return 0;
-}
-
-static void read_branches(void)
-{
-	if (branch_list.nr)
-		return;
-	git_config(config_read_branches, NULL);
-}
-
-struct ref_states {
-	struct remote *remote;
-	struct string_list new, stale, tracked, heads, push;
-	int queried;
-};
-
-static int handle_one_branch(const char *refname,
-	const unsigned char *sha1, int flags, void *cb_data)
-{
-	struct ref_states *states = cb_data;
-	struct refspec refspec;
-
-	memset(&refspec, 0, sizeof(refspec));
-	refspec.dst = (char *)refname;
-	if (!remote_find_tracking(states->remote, &refspec)) {
-		struct string_list_item *item;
-		const char *name = abbrev_branch(refspec.src);
-		/* symbolic refs pointing nowhere were handled already */
-		if ((flags & REF_ISSYMREF) ||
-		    string_list_has_string(&states->tracked, name) ||
-		    string_list_has_string(&states->new, name))
-			return 0;
-		item = string_list_append(name, &states->stale);
-		item->util = xstrdup(refname);
-	}
-	return 0;
-}
-
-static int get_ref_states(const struct ref *remote_refs, struct ref_states *states)
-{
-	struct ref *fetch_map = NULL, **tail = &fetch_map;
-	struct ref *ref;
-	int i;
-
-	for (i = 0; i < states->remote->fetch_refspec_nr; i++)
-		if (get_fetch_map(remote_refs, states->remote->fetch + i, &tail, 1))
-			die("Could not get fetch map for refspec %s",
-				states->remote->fetch_refspec[i]);
-
-	states->new.strdup_strings = states->tracked.strdup_strings = 1;
-	for (ref = fetch_map; ref; ref = ref->next) {
-		unsigned char sha1[20];
-		if (!ref->peer_ref || read_ref(ref->peer_ref->name, sha1))
-			string_list_append(abbrev_branch(ref->name), &states->new);
-		else
-			string_list_append(abbrev_branch(ref->name), &states->tracked);
-	}
-	free_refs(fetch_map);
-
-	sort_string_list(&states->new);
-	sort_string_list(&states->tracked);
-	for_each_ref(handle_one_branch, states);
-	sort_string_list(&states->stale);
-
-	return 0;
-}
-
-struct push_info {
-	char *dest;
-	int forced;
-	enum {
-		PUSH_STATUS_CREATE = 0,
-		PUSH_STATUS_DELETE,
-		PUSH_STATUS_UPTODATE,
-		PUSH_STATUS_FASTFORWARD,
-		PUSH_STATUS_OUTOFDATE,
-		PUSH_STATUS_NOTQUERIED,
-	} status;
-};
-
-static int get_push_ref_states(const struct ref *remote_refs,
-	struct ref_states *states)
-{
-	struct remote *remote = states->remote;
-	struct ref *ref, *local_refs, *push_map;
-	if (remote->mirror)
-		return 0;
-
-	local_refs = get_local_heads();
-	push_map = copy_ref_list(remote_refs);
-
-	match_refs(local_refs, &push_map, remote->push_refspec_nr,
-		   remote->push_refspec, MATCH_REFS_NONE);
-
-	states->push.strdup_strings = 1;
-	for (ref = push_map; ref; ref = ref->next) {
-		struct string_list_item *item;
-		struct push_info *info;
-
-		if (!ref->peer_ref)
-			continue;
-		hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
-
-		item = string_list_append(abbrev_branch(ref->peer_ref->name),
-					  &states->push);
-		item->util = xcalloc(sizeof(struct push_info), 1);
-		info = item->util;
-		info->forced = ref->force;
-		info->dest = xstrdup(abbrev_branch(ref->name));
-
-		if (is_null_sha1(ref->new_sha1)) {
-			info->status = PUSH_STATUS_DELETE;
-		} else if (!hashcmp(ref->old_sha1, ref->new_sha1))
-			info->status = PUSH_STATUS_UPTODATE;
-		else if (is_null_sha1(ref->old_sha1))
-			info->status = PUSH_STATUS_CREATE;
-		else if (has_sha1_file(ref->old_sha1) &&
-			 ref_newer(ref->new_sha1, ref->old_sha1))
-			info->status = PUSH_STATUS_FASTFORWARD;
-		else
-			info->status = PUSH_STATUS_OUTOFDATE;
-	}
-	free_refs(local_refs);
-	free_refs(push_map);
-	return 0;
-}
-
-static int get_push_ref_states_noquery(struct ref_states *states)
-{
-	int i;
-	struct remote *remote = states->remote;
-	struct string_list_item *item;
-	struct push_info *info;
-
-	if (remote->mirror)
-		return 0;
-
-	states->push.strdup_strings = 1;
-	if (!remote->push_refspec_nr) {
-		item = string_list_append("(matching)", &states->push);
-		info = item->util = xcalloc(sizeof(struct push_info), 1);
-		info->status = PUSH_STATUS_NOTQUERIED;
-		info->dest = xstrdup(item->string);
-	}
-	for (i = 0; i < remote->push_refspec_nr; i++) {
-		struct refspec *spec = remote->push + i;
-		if (spec->matching)
-			item = string_list_append("(matching)", &states->push);
-		else if (strlen(spec->src))
-			item = string_list_append(spec->src, &states->push);
-		else
-			item = string_list_append("(delete)", &states->push);
-
-		info = item->util = xcalloc(sizeof(struct push_info), 1);
-		info->forced = spec->force;
-		info->status = PUSH_STATUS_NOTQUERIED;
-		info->dest = xstrdup(spec->dst ? spec->dst : item->string);
-	}
-	return 0;
-}
-
-static int get_head_names(const struct ref *remote_refs, struct ref_states *states)
-{
-	struct ref *ref, *matches;
-	struct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map;
-	struct refspec refspec;
-
-	refspec.force = 0;
-	refspec.pattern = 1;
-	refspec.src = refspec.dst = "refs/heads/*";
-	states->heads.strdup_strings = 1;
-	get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0);
-	matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
-				    fetch_map, 1);
-	for(ref = matches; ref; ref = ref->next)
-		string_list_append(abbrev_branch(ref->name), &states->heads);
-
-	free_refs(fetch_map);
-	free_refs(matches);
-
-	return 0;
-}
-
-struct known_remote {
-	struct known_remote *next;
-	struct remote *remote;
-};
-
-struct known_remotes {
-	struct remote *to_delete;
-	struct known_remote *list;
-};
-
-static int add_known_remote(struct remote *remote, void *cb_data)
-{
-	struct known_remotes *all = cb_data;
-	struct known_remote *r;
-
-	if (!strcmp(all->to_delete->name, remote->name))
-		return 0;
-
-	r = xmalloc(sizeof(*r));
-	r->remote = remote;
-	r->next = all->list;
-	all->list = r;
-	return 0;
-}
-
-struct branches_for_remote {
-	struct remote *remote;
-	struct string_list *branches, *skipped;
-	struct known_remotes *keep;
-};
-
-static int add_branch_for_removal(const char *refname,
-	const unsigned char *sha1, int flags, void *cb_data)
-{
-	struct branches_for_remote *branches = cb_data;
-	struct refspec refspec;
-	struct string_list_item *item;
-	struct known_remote *kr;
-
-	memset(&refspec, 0, sizeof(refspec));
-	refspec.dst = (char *)refname;
-	if (remote_find_tracking(branches->remote, &refspec))
-		return 0;
-
-	/* don't delete a branch if another remote also uses it */
-	for (kr = branches->keep->list; kr; kr = kr->next) {
-		memset(&refspec, 0, sizeof(refspec));
-		refspec.dst = (char *)refname;
-		if (!remote_find_tracking(kr->remote, &refspec))
-			return 0;
-	}
-
-	/* don't delete non-remote refs */
-	if (prefixcmp(refname, "refs/remotes")) {
-		/* advise user how to delete local branches */
-		if (!prefixcmp(refname, "refs/heads/"))
-			string_list_append(abbrev_branch(refname),
-					   branches->skipped);
-		/* silently skip over other non-remote refs */
-		return 0;
-	}
-
-	/* make sure that symrefs are deleted */
-	if (flags & REF_ISSYMREF)
-		return unlink(git_path("%s", refname));
-
-	item = string_list_append(refname, branches->branches);
-	item->util = xmalloc(20);
-	hashcpy(item->util, sha1);
-
-	return 0;
-}
-
-struct rename_info {
-	const char *old;
-	const char *new;
-	struct string_list *remote_branches;
-};
-
-static int read_remote_branches(const char *refname,
-	const unsigned char *sha1, int flags, void *cb_data)
-{
-	struct rename_info *rename = cb_data;
-	struct strbuf buf = STRBUF_INIT;
-	struct string_list_item *item;
-	int flag;
-	unsigned char orig_sha1[20];
-	const char *symref;
-
-	strbuf_addf(&buf, "refs/remotes/%s", rename->old);
-	if(!prefixcmp(refname, buf.buf)) {
-		item = string_list_append(xstrdup(refname), rename->remote_branches);
-		symref = resolve_ref(refname, orig_sha1, 1, &flag);
-		if (flag & REF_ISSYMREF)
-			item->util = xstrdup(symref);
-		else
-			item->util = NULL;
-	}
-
-	return 0;
-}
-
-static int migrate_file(struct remote *remote)
-{
-	struct strbuf buf = STRBUF_INIT;
-	int i;
-	char *path = NULL;
-
-	strbuf_addf(&buf, "remote.%s.url", remote->name);
-	for (i = 0; i < remote->url_nr; i++)
-		if (git_config_set_multivar(buf.buf, remote->url[i], "^$", 0))
-			return error("Could not append '%s' to '%s'",
-					remote->url[i], buf.buf);
-	strbuf_reset(&buf);
-	strbuf_addf(&buf, "remote.%s.push", remote->name);
-	for (i = 0; i < remote->push_refspec_nr; i++)
-		if (git_config_set_multivar(buf.buf, remote->push_refspec[i], "^$", 0))
-			return error("Could not append '%s' to '%s'",
-					remote->push_refspec[i], buf.buf);
-	strbuf_reset(&buf);
-	strbuf_addf(&buf, "remote.%s.fetch", remote->name);
-	for (i = 0; i < remote->fetch_refspec_nr; i++)
-		if (git_config_set_multivar(buf.buf, remote->fetch_refspec[i], "^$", 0))
-			return error("Could not append '%s' to '%s'",
-					remote->fetch_refspec[i], buf.buf);
-	if (remote->origin == REMOTE_REMOTES)
-		path = git_path("remotes/%s", remote->name);
-	else if (remote->origin == REMOTE_BRANCHES)
-		path = git_path("branches/%s", remote->name);
-	if (path)
-		unlink_or_warn(path);
-	return 0;
-}
-
-static int mv(int argc, const char **argv)
-{
-	struct option options[] = {
-		OPT_END()
-	};
-	struct remote *oldremote, *newremote;
-	struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT, buf3 = STRBUF_INIT;
-	struct string_list remote_branches = { NULL, 0, 0, 0 };
-	struct rename_info rename;
-	int i;
-
-	if (argc != 3)
-		usage_with_options(builtin_remote_usage, options);
-
-	rename.old = argv[1];
-	rename.new = argv[2];
-	rename.remote_branches = &remote_branches;
-
-	oldremote = remote_get(rename.old);
-	if (!oldremote)
-		die("No such remote: %s", rename.old);
-
-	if (!strcmp(rename.old, rename.new) && oldremote->origin != REMOTE_CONFIG)
-		return migrate_file(oldremote);
-
-	newremote = remote_get(rename.new);
-	if (newremote && (newremote->url_nr > 1 || newremote->fetch_refspec_nr))
-		die("remote %s already exists.", rename.new);
-
-	strbuf_addf(&buf, "refs/heads/test:refs/remotes/%s/test", rename.new);
-	if (!valid_fetch_refspec(buf.buf))
-		die("'%s' is not a valid remote name", rename.new);
-
-	strbuf_reset(&buf);
-	strbuf_addf(&buf, "remote.%s", rename.old);
-	strbuf_addf(&buf2, "remote.%s", rename.new);
-	if (git_config_rename_section(buf.buf, buf2.buf) < 1)
-		return error("Could not rename config section '%s' to '%s'",
-				buf.buf, buf2.buf);
-
-	strbuf_reset(&buf);
-	strbuf_addf(&buf, "remote.%s.fetch", rename.new);
-	if (git_config_set_multivar(buf.buf, NULL, NULL, 1))
-		return error("Could not remove config section '%s'", buf.buf);
-	for (i = 0; i < oldremote->fetch_refspec_nr; i++) {
-		char *ptr;
-
-		strbuf_reset(&buf2);
-		strbuf_addstr(&buf2, oldremote->fetch_refspec[i]);
-		ptr = strstr(buf2.buf, rename.old);
-		if (ptr)
-			strbuf_splice(&buf2, ptr-buf2.buf, strlen(rename.old),
-					rename.new, strlen(rename.new));
-		if (git_config_set_multivar(buf.buf, buf2.buf, "^$", 0))
-			return error("Could not append '%s'", buf.buf);
-	}
-
-	read_branches();
-	for (i = 0; i < branch_list.nr; i++) {
-		struct string_list_item *item = branch_list.items + i;
-		struct branch_info *info = item->util;
-		if (info->remote_name && !strcmp(info->remote_name, rename.old)) {
-			strbuf_reset(&buf);
-			strbuf_addf(&buf, "branch.%s.remote", item->string);
-			if (git_config_set(buf.buf, rename.new)) {
-				return error("Could not set '%s'", buf.buf);
-			}
-		}
-	}
-
-	/*
-	 * First remove symrefs, then rename the rest, finally create
-	 * the new symrefs.
-	 */
-	for_each_ref(read_remote_branches, &rename);
-	for (i = 0; i < remote_branches.nr; i++) {
-		struct string_list_item *item = remote_branches.items + i;
-		int flag = 0;
-		unsigned char sha1[20];
-
-		resolve_ref(item->string, sha1, 1, &flag);
-		if (!(flag & REF_ISSYMREF))
-			continue;
-		if (delete_ref(item->string, NULL, REF_NODEREF))
-			die("deleting '%s' failed", item->string);
-	}
-	for (i = 0; i < remote_branches.nr; i++) {
-		struct string_list_item *item = remote_branches.items + i;
-
-		if (item->util)
-			continue;
-		strbuf_reset(&buf);
-		strbuf_addstr(&buf, item->string);
-		strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old),
-				rename.new, strlen(rename.new));
-		strbuf_reset(&buf2);
-		strbuf_addf(&buf2, "remote: renamed %s to %s",
-				item->string, buf.buf);
-		if (rename_ref(item->string, buf.buf, buf2.buf))
-			die("renaming '%s' failed", item->string);
-	}
-	for (i = 0; i < remote_branches.nr; i++) {
-		struct string_list_item *item = remote_branches.items + i;
-
-		if (!item->util)
-			continue;
-		strbuf_reset(&buf);
-		strbuf_addstr(&buf, item->string);
-		strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old),
-				rename.new, strlen(rename.new));
-		strbuf_reset(&buf2);
-		strbuf_addstr(&buf2, item->util);
-		strbuf_splice(&buf2, strlen("refs/remotes/"), strlen(rename.old),
-				rename.new, strlen(rename.new));
-		strbuf_reset(&buf3);
-		strbuf_addf(&buf3, "remote: renamed %s to %s",
-				item->string, buf.buf);
-		if (create_symref(buf.buf, buf2.buf, buf3.buf))
-			die("creating '%s' failed", buf.buf);
-	}
-	return 0;
-}
-
-static int remove_branches(struct string_list *branches)
-{
-	int i, result = 0;
-	for (i = 0; i < branches->nr; i++) {
-		struct string_list_item *item = branches->items + i;
-		const char *refname = item->string;
-		unsigned char *sha1 = item->util;
-
-		if (delete_ref(refname, sha1, 0))
-			result |= error("Could not remove branch %s", refname);
-	}
-	return result;
-}
-
-static int rm(int argc, const char **argv)
-{
-	struct option options[] = {
-		OPT_END()
-	};
-	struct remote *remote;
-	struct strbuf buf = STRBUF_INIT;
-	struct known_remotes known_remotes = { NULL, NULL };
-	struct string_list branches = { NULL, 0, 0, 1 };
-	struct string_list skipped = { NULL, 0, 0, 1 };
-	struct branches_for_remote cb_data = {
-		NULL, &branches, &skipped, &known_remotes
-	};
-	int i, result;
-
-	if (argc != 2)
-		usage_with_options(builtin_remote_usage, options);
-
-	remote = remote_get(argv[1]);
-	if (!remote)
-		die("No such remote: %s", argv[1]);
-
-	known_remotes.to_delete = remote;
-	for_each_remote(add_known_remote, &known_remotes);
-
-	strbuf_addf(&buf, "remote.%s", remote->name);
-	if (git_config_rename_section(buf.buf, NULL) < 1)
-		return error("Could not remove config section '%s'", buf.buf);
-
-	read_branches();
-	for (i = 0; i < branch_list.nr; i++) {
-		struct string_list_item *item = branch_list.items + i;
-		struct branch_info *info = item->util;
-		if (info->remote_name && !strcmp(info->remote_name, remote->name)) {
-			const char *keys[] = { "remote", "merge", NULL }, **k;
-			for (k = keys; *k; k++) {
-				strbuf_reset(&buf);
-				strbuf_addf(&buf, "branch.%s.%s",
-						item->string, *k);
-				if (git_config_set(buf.buf, NULL)) {
-					strbuf_release(&buf);
-					return -1;
-				}
-			}
-		}
-	}
-
-	/*
-	 * We cannot just pass a function to for_each_ref() which deletes
-	 * the branches one by one, since for_each_ref() relies on cached
-	 * refs, which are invalidated when deleting a branch.
-	 */
-	cb_data.remote = remote;
-	result = for_each_ref(add_branch_for_removal, &cb_data);
-	strbuf_release(&buf);
-
-	if (!result)
-		result = remove_branches(&branches);
-	string_list_clear(&branches, 1);
-
-	if (skipped.nr) {
-		fprintf(stderr, skipped.nr == 1 ?
-			"Note: A non-remote branch was not removed; "
-			"to delete it, use:\n" :
-			"Note: Non-remote branches were not removed; "
-			"to delete them, use:\n");
-		for (i = 0; i < skipped.nr; i++)
-			fprintf(stderr, "  git branch -d %s\n",
-				skipped.items[i].string);
-	}
-	string_list_clear(&skipped, 0);
-
-	return result;
-}
-
-static void clear_push_info(void *util, const char *string)
-{
-	struct push_info *info = util;
-	free(info->dest);
-	free(info);
-}
-
-static void free_remote_ref_states(struct ref_states *states)
-{
-	string_list_clear(&states->new, 0);
-	string_list_clear(&states->stale, 0);
-	string_list_clear(&states->tracked, 0);
-	string_list_clear(&states->heads, 0);
-	string_list_clear_func(&states->push, clear_push_info);
-}
-
-static int append_ref_to_tracked_list(const char *refname,
-	const unsigned char *sha1, int flags, void *cb_data)
-{
-	struct ref_states *states = cb_data;
-	struct refspec refspec;
-
-	if (flags & REF_ISSYMREF)
-		return 0;
-
-	memset(&refspec, 0, sizeof(refspec));
-	refspec.dst = (char *)refname;
-	if (!remote_find_tracking(states->remote, &refspec))
-		string_list_append(abbrev_branch(refspec.src), &states->tracked);
-
-	return 0;
-}
-
-static int get_remote_ref_states(const char *name,
-				 struct ref_states *states,
-				 int query)
-{
-	struct transport *transport;
-	const struct ref *remote_refs;
-
-	states->remote = remote_get(name);
-	if (!states->remote)
-		return error("No such remote: %s", name);
-
-	read_branches();
-
-	if (query) {
-		transport = transport_get(states->remote, states->remote->url_nr > 0 ?
-			states->remote->url[0] : NULL);
-		remote_refs = transport_get_remote_refs(transport);
-		transport_disconnect(transport);
-
-		states->queried = 1;
-		if (query & GET_REF_STATES)
-			get_ref_states(remote_refs, states);
-		if (query & GET_HEAD_NAMES)
-			get_head_names(remote_refs, states);
-		if (query & GET_PUSH_REF_STATES)
-			get_push_ref_states(remote_refs, states);
-	} else {
-		for_each_ref(append_ref_to_tracked_list, states);
-		sort_string_list(&states->tracked);
-		get_push_ref_states_noquery(states);
-	}
-
-	return 0;
-}
-
-struct show_info {
-	struct string_list *list;
-	struct ref_states *states;
-	int width, width2;
-	int any_rebase;
-};
-
-static int add_remote_to_show_info(struct string_list_item *item, void *cb_data)
-{
-	struct show_info *info = cb_data;
-	int n = strlen(item->string);
-	if (n > info->width)
-		info->width = n;
-	string_list_insert(item->string, info->list);
-	return 0;
-}
-
-static int show_remote_info_item(struct string_list_item *item, void *cb_data)
-{
-	struct show_info *info = cb_data;
-	struct ref_states *states = info->states;
-	const char *name = item->string;
-
-	if (states->queried) {
-		const char *fmt = "%s";
-		const char *arg = "";
-		if (string_list_has_string(&states->new, name)) {
-			fmt = " new (next fetch will store in remotes/%s)";
-			arg = states->remote->name;
-		} else if (string_list_has_string(&states->tracked, name))
-			arg = " tracked";
-		else if (string_list_has_string(&states->stale, name))
-			arg = " stale (use 'git remote prune' to remove)";
-		else
-			arg = " ???";
-		printf("    %-*s", info->width, name);
-		printf(fmt, arg);
-		printf("\n");
-	} else
-		printf("    %s\n", name);
-
-	return 0;
-}
-
-static int add_local_to_show_info(struct string_list_item *branch_item, void *cb_data)
-{
-	struct show_info *show_info = cb_data;
-	struct ref_states *states = show_info->states;
-	struct branch_info *branch_info = branch_item->util;
-	struct string_list_item *item;
-	int n;
-
-	if (!branch_info->merge.nr || !branch_info->remote_name ||
-	    strcmp(states->remote->name, branch_info->remote_name))
-		return 0;
-	if ((n = strlen(branch_item->string)) > show_info->width)
-		show_info->width = n;
-	if (branch_info->rebase)
-		show_info->any_rebase = 1;
-
-	item = string_list_insert(branch_item->string, show_info->list);
-	item->util = branch_info;
-
-	return 0;
-}
-
-static int show_local_info_item(struct string_list_item *item, void *cb_data)
-{
-	struct show_info *show_info = cb_data;
-	struct branch_info *branch_info = item->util;
-	struct string_list *merge = &branch_info->merge;
-	const char *also;
-	int i;
-
-	if (branch_info->rebase && branch_info->merge.nr > 1) {
-		error("invalid branch.%s.merge; cannot rebase onto > 1 branch",
-			item->string);
-		return 0;
-	}
-
-	printf("    %-*s ", show_info->width, item->string);
-	if (branch_info->rebase) {
-		printf("rebases onto remote %s\n", merge->items[0].string);
-		return 0;
-	} else if (show_info->any_rebase) {
-		printf(" merges with remote %s\n", merge->items[0].string);
-		also = "    and with remote";
-	} else {
-		printf("merges with remote %s\n", merge->items[0].string);
-		also = "   and with remote";
-	}
-	for (i = 1; i < merge->nr; i++)
-		printf("    %-*s %s %s\n", show_info->width, "", also,
-		       merge->items[i].string);
-
-	return 0;
-}
-
-static int add_push_to_show_info(struct string_list_item *push_item, void *cb_data)
-{
-	struct show_info *show_info = cb_data;
-	struct push_info *push_info = push_item->util;
-	struct string_list_item *item;
-	int n;
-	if ((n = strlen(push_item->string)) > show_info->width)
-		show_info->width = n;
-	if ((n = strlen(push_info->dest)) > show_info->width2)
-		show_info->width2 = n;
-	item = string_list_append(push_item->string, show_info->list);
-	item->util = push_item->util;
-	return 0;
-}
-
-/*
- * Sorting comparison for a string list that has push_info
- * structs in its util field
- */
-static int cmp_string_with_push(const void *va, const void *vb)
-{
-	const struct string_list_item *a = va;
-	const struct string_list_item *b = vb;
-	const struct push_info *a_push = a->util;
-	const struct push_info *b_push = b->util;
-	int cmp = strcmp(a->string, b->string);
-	return cmp ? cmp : strcmp(a_push->dest, b_push->dest);
-}
-
-static int show_push_info_item(struct string_list_item *item, void *cb_data)
-{
-	struct show_info *show_info = cb_data;
-	struct push_info *push_info = item->util;
-	char *src = item->string, *status = NULL;
-
-	switch (push_info->status) {
-	case PUSH_STATUS_CREATE:
-		status = "create";
-		break;
-	case PUSH_STATUS_DELETE:
-		status = "delete";
-		src = "(none)";
-		break;
-	case PUSH_STATUS_UPTODATE:
-		status = "up to date";
-		break;
-	case PUSH_STATUS_FASTFORWARD:
-		status = "fast forwardable";
-		break;
-	case PUSH_STATUS_OUTOFDATE:
-		status = "local out of date";
-		break;
-	case PUSH_STATUS_NOTQUERIED:
-		break;
-	}
-	if (status)
-		printf("    %-*s %s to %-*s (%s)\n", show_info->width, src,
-			push_info->forced ? "forces" : "pushes",
-			show_info->width2, push_info->dest, status);
-	else
-		printf("    %-*s %s to %s\n", show_info->width, src,
-			push_info->forced ? "forces" : "pushes",
-			push_info->dest);
-	return 0;
-}
-
-static int show(int argc, const char **argv)
-{
-	int no_query = 0, result = 0, query_flag = 0;
-	struct option options[] = {
-		OPT_GROUP("show specific options"),
-		OPT_BOOLEAN('n', NULL, &no_query, "do not query remotes"),
-		OPT_END()
-	};
-	struct ref_states states;
-	struct string_list info_list = { NULL, 0, 0, 0 };
-	struct show_info info;
-
-	argc = parse_options(argc, argv, NULL, options, builtin_remote_usage,
-			     0);
-
-	if (argc < 1)
-		return show_all();
-
-	if (!no_query)
-		query_flag = (GET_REF_STATES | GET_HEAD_NAMES | GET_PUSH_REF_STATES);
-
-	memset(&states, 0, sizeof(states));
-	memset(&info, 0, sizeof(info));
-	info.states = &states;
-	info.list = &info_list;
-	for (; argc; argc--, argv++) {
-		int i;
-		const char **url;
-		int url_nr;
-
-		get_remote_ref_states(*argv, &states, query_flag);
-
-		printf("* remote %s\n", *argv);
-		printf("  Fetch URL: %s\n", states.remote->url_nr > 0 ?
-			states.remote->url[0] : "(no URL)");
-		if (states.remote->pushurl_nr) {
-			url = states.remote->pushurl;
-			url_nr = states.remote->pushurl_nr;
-		} else {
-			url = states.remote->url;
-			url_nr = states.remote->url_nr;
-		}
-		for (i=0; i < url_nr; i++)
-			printf("  Push  URL: %s\n", url[i]);
-		if (!i)
-			printf("  Push  URL: %s\n", "(no URL)");
-		if (no_query)
-			printf("  HEAD branch: (not queried)\n");
-		else if (!states.heads.nr)
-			printf("  HEAD branch: (unknown)\n");
-		else if (states.heads.nr == 1)
-			printf("  HEAD branch: %s\n", states.heads.items[0].string);
-		else {
-			printf("  HEAD branch (remote HEAD is ambiguous,"
-			       " may be one of the following):\n");
-			for (i = 0; i < states.heads.nr; i++)
-				printf("    %s\n", states.heads.items[i].string);
-		}
-
-		/* remote branch info */
-		info.width = 0;
-		for_each_string_list(add_remote_to_show_info, &states.new, &info);
-		for_each_string_list(add_remote_to_show_info, &states.tracked, &info);
-		for_each_string_list(add_remote_to_show_info, &states.stale, &info);
-		if (info.list->nr)
-			printf("  Remote branch%s:%s\n",
-			       info.list->nr > 1 ? "es" : "",
-				no_query ? " (status not queried)" : "");
-		for_each_string_list(show_remote_info_item, info.list, &info);
-		string_list_clear(info.list, 0);
-
-		/* git pull info */
-		info.width = 0;
-		info.any_rebase = 0;
-		for_each_string_list(add_local_to_show_info, &branch_list, &info);
-		if (info.list->nr)
-			printf("  Local branch%s configured for 'git pull':\n",
-			       info.list->nr > 1 ? "es" : "");
-		for_each_string_list(show_local_info_item, info.list, &info);
-		string_list_clear(info.list, 0);
-
-		/* git push info */
-		if (states.remote->mirror)
-			printf("  Local refs will be mirrored by 'git push'\n");
-
-		info.width = info.width2 = 0;
-		for_each_string_list(add_push_to_show_info, &states.push, &info);
-		qsort(info.list->items, info.list->nr,
-			sizeof(*info.list->items), cmp_string_with_push);
-		if (info.list->nr)
-			printf("  Local ref%s configured for 'git push'%s:\n",
-				info.list->nr > 1 ? "s" : "",
-				no_query ? " (status not queried)" : "");
-		for_each_string_list(show_push_info_item, info.list, &info);
-		string_list_clear(info.list, 0);
-
-		free_remote_ref_states(&states);
-	}
-
-	return result;
-}
-
-static int set_head(int argc, const char **argv)
-{
-	int i, opt_a = 0, opt_d = 0, result = 0;
-	struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT;
-	char *head_name = NULL;
-
-	struct option options[] = {
-		OPT_GROUP("set-head specific options"),
-		OPT_BOOLEAN('a', "auto", &opt_a,
-			    "set refs/remotes/<name>/HEAD according to remote"),
-		OPT_BOOLEAN('d', "delete", &opt_d,
-			    "delete refs/remotes/<name>/HEAD"),
-		OPT_END()
-	};
-	argc = parse_options(argc, argv, NULL, options, builtin_remote_usage,
-			     0);
-	if (argc)
-		strbuf_addf(&buf, "refs/remotes/%s/HEAD", argv[0]);
-
-	if (!opt_a && !opt_d && argc == 2) {
-		head_name = xstrdup(argv[1]);
-	} else if (opt_a && !opt_d && argc == 1) {
-		struct ref_states states;
-		memset(&states, 0, sizeof(states));
-		get_remote_ref_states(argv[0], &states, GET_HEAD_NAMES);
-		if (!states.heads.nr)
-			result |= error("Cannot determine remote HEAD");
-		else if (states.heads.nr > 1) {
-			result |= error("Multiple remote HEAD branches. "
-					"Please choose one explicitly with:");
-			for (i = 0; i < states.heads.nr; i++)
-				fprintf(stderr, "  git remote set-head %s %s\n",
-					argv[0], states.heads.items[i].string);
-		} else
-			head_name = xstrdup(states.heads.items[0].string);
-		free_remote_ref_states(&states);
-	} else if (opt_d && !opt_a && argc == 1) {
-		if (delete_ref(buf.buf, NULL, REF_NODEREF))
-			result |= error("Could not delete %s", buf.buf);
-	} else
-		usage_with_options(builtin_remote_usage, options);
-
-	if (head_name) {
-		unsigned char sha1[20];
-		strbuf_addf(&buf2, "refs/remotes/%s/%s", argv[0], head_name);
-		/* make sure it's valid */
-		if (!resolve_ref(buf2.buf, sha1, 1, NULL))
-			result |= error("Not a valid ref: %s", buf2.buf);
-		else if (create_symref(buf.buf, buf2.buf, "remote set-head"))
-			result |= error("Could not setup %s", buf.buf);
-		if (opt_a)
-			printf("%s/HEAD set to %s\n", argv[0], head_name);
-		free(head_name);
-	}
-
-	strbuf_release(&buf);
-	strbuf_release(&buf2);
-	return result;
-}
-
-static int prune(int argc, const char **argv)
-{
-	int dry_run = 0, result = 0;
-	struct option options[] = {
-		OPT_GROUP("prune specific options"),
-		OPT__DRY_RUN(&dry_run),
-		OPT_END()
-	};
-
-	argc = parse_options(argc, argv, NULL, options, builtin_remote_usage,
-			     0);
-
-	if (argc < 1)
-		usage_with_options(builtin_remote_usage, options);
-
-	for (; argc; argc--, argv++)
-		result |= prune_remote(*argv, dry_run);
-
-	return result;
-}
-
-static int prune_remote(const char *remote, int dry_run)
-{
-	int result = 0, i;
-	struct ref_states states;
-	const char *dangling_msg = dry_run
-		? " %s will become dangling!\n"
-		: " %s has become dangling!\n";
-
-	memset(&states, 0, sizeof(states));
-	get_remote_ref_states(remote, &states, GET_REF_STATES);
-
-	if (states.stale.nr) {
-		printf("Pruning %s\n", remote);
-		printf("URL: %s\n",
-		       states.remote->url_nr
-		       ? states.remote->url[0]
-		       : "(no URL)");
-	}
-
-	for (i = 0; i < states.stale.nr; i++) {
-		const char *refname = states.stale.items[i].util;
-
-		if (!dry_run)
-			result |= delete_ref(refname, NULL, 0);
-
-		printf(" * [%s] %s\n", dry_run ? "would prune" : "pruned",
-		       abbrev_ref(refname, "refs/remotes/"));
-		warn_dangling_symref(dangling_msg, refname);
-	}
-
-	free_remote_ref_states(&states);
-	return result;
-}
-
-static int get_one_remote_for_update(struct remote *remote, void *priv)
-{
-	struct string_list *list = priv;
-	if (!remote->skip_default_update)
-		string_list_append(remote->name, list);
-	return 0;
-}
-
-static struct remote_group {
-	const char *name;
-	struct string_list *list;
-} remote_group;
-
-static int get_remote_group(const char *key, const char *value, void *num_hits)
-{
-	if (!prefixcmp(key, "remotes.") &&
-			!strcmp(key + 8, remote_group.name)) {
-		/* split list by white space */
-		int space = strcspn(value, " \t\n");
-		while (*value) {
-			if (space > 1) {
-				string_list_append(xstrndup(value, space),
-						remote_group.list);
-				++*((int *)num_hits);
-			}
-			value += space + (value[space] != '\0');
-			space = strcspn(value, " \t\n");
-		}
-	}
-
-	return 0;
-}
-
-static int update(int argc, const char **argv)
-{
-	int i, result = 0, prune = 0;
-	struct string_list list = { NULL, 0, 0, 0 };
-	static const char *default_argv[] = { NULL, "default", NULL };
-	struct option options[] = {
-		OPT_GROUP("update specific options"),
-		OPT_BOOLEAN('p', "prune", &prune,
-			    "prune remotes after fetching"),
-		OPT_END()
-	};
-
-	argc = parse_options(argc, argv, NULL, options, builtin_remote_usage,
-			     PARSE_OPT_KEEP_ARGV0);
-	if (argc < 2) {
-		argc = 2;
-		argv = default_argv;
-	}
-
-	remote_group.list = &list;
-	for (i = 1; i < argc; i++) {
-		int groups_found = 0;
-		remote_group.name = argv[i];
-		result = git_config(get_remote_group, &groups_found);
-		if (!groups_found && (i != 1 || strcmp(argv[1], "default"))) {
-			struct remote *remote;
-			if (!remote_is_configured(argv[i]))
-				die("No such remote or remote group: %s",
-				    argv[i]);
-			remote = remote_get(argv[i]);
-			string_list_append(remote->name, remote_group.list);
-		}
-	}
-
-	if (!result && !list.nr  && argc == 2 && !strcmp(argv[1], "default"))
-		result = for_each_remote(get_one_remote_for_update, &list);
-
-	for (i = 0; i < list.nr; i++) {
-		int err = fetch_remote(list.items[i].string);
-		result |= err;
-		if (!err && prune)
-			result |= prune_remote(list.items[i].string, 0);
-	}
-
-	/* all names were strdup()ed or strndup()ed */
-	list.strdup_strings = 1;
-	string_list_clear(&list, 0);
-
-	return result;
-}
-
-static int get_one_entry(struct remote *remote, void *priv)
-{
-	struct string_list *list = priv;
-	struct strbuf url_buf = STRBUF_INIT;
-	const char **url;
-	int i, url_nr;
-
-	if (remote->url_nr > 0) {
-		strbuf_addf(&url_buf, "%s (fetch)", remote->url[0]);
-		string_list_append(remote->name, list)->util =
-				strbuf_detach(&url_buf, NULL);
-	} else
-		string_list_append(remote->name, list)->util = NULL;
-	if (remote->pushurl_nr) {
-		url = remote->pushurl;
-		url_nr = remote->pushurl_nr;
-	} else {
-		url = remote->url;
-		url_nr = remote->url_nr;
-	}
-	for (i = 0; i < url_nr; i++)
-	{
-		strbuf_addf(&url_buf, "%s (push)", url[i]);
-		string_list_append(remote->name, list)->util =
-				strbuf_detach(&url_buf, NULL);
-	}
-
-	return 0;
-}
-
-static int show_all(void)
-{
-	struct string_list list = { NULL, 0, 0 };
-	int result;
-
-	list.strdup_strings = 1;
-	result = for_each_remote(get_one_entry, &list);
-
-	if (!result) {
-		int i;
-
-		sort_string_list(&list);
-		for (i = 0; i < list.nr; i++) {
-			struct string_list_item *item = list.items + i;
-			if (verbose)
-				printf("%s\t%s\n", item->string,
-					item->util ? (const char *)item->util : "");
-			else {
-				if (i && !strcmp((item - 1)->string, item->string))
-					continue;
-				printf("%s\n", item->string);
-			}
-		}
-	}
-	string_list_clear(&list, 1);
-	return result;
-}
-
-int cmd_remote(int argc, const char **argv, const char *prefix)
-{
-	struct option options[] = {
-		OPT__VERBOSE(&verbose),
-		OPT_END()
-	};
-	int result;
-
-	argc = parse_options(argc, argv, prefix, options, builtin_remote_usage,
-		PARSE_OPT_STOP_AT_NON_OPTION);
-
-	if (argc < 1)
-		result = show_all();
-	else if (!strcmp(argv[0], "add"))
-		result = add(argc, argv);
-	else if (!strcmp(argv[0], "rename"))
-		result = mv(argc, argv);
-	else if (!strcmp(argv[0], "rm"))
-		result = rm(argc, argv);
-	else if (!strcmp(argv[0], "set-head"))
-		result = set_head(argc, argv);
-	else if (!strcmp(argv[0], "show"))
-		result = show(argc, argv);
-	else if (!strcmp(argv[0], "prune"))
-		result = prune(argc, argv);
-	else if (!strcmp(argv[0], "update"))
-		result = update(argc, argv);
-	else {
-		error("Unknown subcommand: %s", argv[0]);
-		usage_with_options(builtin_remote_usage, options);
-	}
-
-	return result ? 1 : 0;
-}
diff --git a/builtin-rerere.c b/builtin-rerere.c
deleted file mode 100644
index adfb7b5..0000000
--- a/builtin-rerere.c
+++ /dev/null
@@ -1,136 +0,0 @@
-#include "builtin.h"
-#include "cache.h"
-#include "dir.h"
-#include "string-list.h"
-#include "rerere.h"
-#include "xdiff/xdiff.h"
-#include "xdiff-interface.h"
-
-static const char git_rerere_usage[] =
-"git rerere [clear | status | diff | gc]";
-
-/* these values are days */
-static int cutoff_noresolve = 15;
-static int cutoff_resolve = 60;
-
-static time_t rerere_created_at(const char *name)
-{
-	struct stat st;
-	return stat(rerere_path(name, "preimage"), &st) ? (time_t) 0 : st.st_mtime;
-}
-
-static void unlink_rr_item(const char *name)
-{
-	unlink(rerere_path(name, "thisimage"));
-	unlink(rerere_path(name, "preimage"));
-	unlink(rerere_path(name, "postimage"));
-	rmdir(git_path("rr-cache/%s", name));
-}
-
-static int git_rerere_gc_config(const char *var, const char *value, void *cb)
-{
-	if (!strcmp(var, "gc.rerereresolved"))
-		cutoff_resolve = git_config_int(var, value);
-	else if (!strcmp(var, "gc.rerereunresolved"))
-		cutoff_noresolve = git_config_int(var, value);
-	else
-		return git_default_config(var, value, cb);
-	return 0;
-}
-
-static void garbage_collect(struct string_list *rr)
-{
-	struct string_list to_remove = { NULL, 0, 0, 1 };
-	DIR *dir;
-	struct dirent *e;
-	int i, cutoff;
-	time_t now = time(NULL), then;
-
-	git_config(git_rerere_gc_config, NULL);
-	dir = opendir(git_path("rr-cache"));
-	while ((e = readdir(dir))) {
-		if (is_dot_or_dotdot(e->d_name))
-			continue;
-		then = rerere_created_at(e->d_name);
-		if (!then)
-			continue;
-		cutoff = (has_rerere_resolution(e->d_name)
-			  ? cutoff_resolve : cutoff_noresolve);
-		if (then < now - cutoff * 86400)
-			string_list_append(e->d_name, &to_remove);
-	}
-	for (i = 0; i < to_remove.nr; i++)
-		unlink_rr_item(to_remove.items[i].string);
-	string_list_clear(&to_remove, 0);
-}
-
-static int outf(void *dummy, mmbuffer_t *ptr, int nbuf)
-{
-	int i;
-	for (i = 0; i < nbuf; i++)
-		if (write_in_full(1, ptr[i].ptr, ptr[i].size) != ptr[i].size)
-			return -1;
-	return 0;
-}
-
-static int diff_two(const char *file1, const char *label1,
-		const char *file2, const char *label2)
-{
-	xpparam_t xpp;
-	xdemitconf_t xecfg;
-	xdemitcb_t ecb;
-	mmfile_t minus, plus;
-
-	if (read_mmfile(&minus, file1) || read_mmfile(&plus, file2))
-		return 1;
-
-	printf("--- a/%s\n+++ b/%s\n", label1, label2);
-	fflush(stdout);
-	memset(&xpp, 0, sizeof(xpp));
-	xpp.flags = XDF_NEED_MINIMAL;
-	memset(&xecfg, 0, sizeof(xecfg));
-	xecfg.ctxlen = 3;
-	ecb.outf = outf;
-	xdi_diff(&minus, &plus, &xpp, &xecfg, &ecb);
-
-	free(minus.ptr);
-	free(plus.ptr);
-	return 0;
-}
-
-int cmd_rerere(int argc, const char **argv, const char *prefix)
-{
-	struct string_list merge_rr = { NULL, 0, 0, 1 };
-	int i, fd;
-
-	if (argc < 2)
-		return rerere();
-
-	fd = setup_rerere(&merge_rr);
-	if (fd < 0)
-		return 0;
-
-	if (!strcmp(argv[1], "clear")) {
-		for (i = 0; i < merge_rr.nr; i++) {
-			const char *name = (const char *)merge_rr.items[i].util;
-			if (!has_rerere_resolution(name))
-				unlink_rr_item(name);
-		}
-		unlink_or_warn(git_path("rr-cache/MERGE_RR"));
-	} else if (!strcmp(argv[1], "gc"))
-		garbage_collect(&merge_rr);
-	else if (!strcmp(argv[1], "status"))
-		for (i = 0; i < merge_rr.nr; i++)
-			printf("%s\n", merge_rr.items[i].string);
-	else if (!strcmp(argv[1], "diff"))
-		for (i = 0; i < merge_rr.nr; i++) {
-			const char *path = merge_rr.items[i].string;
-			const char *name = (const char *)merge_rr.items[i].util;
-			diff_two(rerere_path(name, "preimage"), path, path, path);
-		}
-	else
-		usage(git_rerere_usage);
-
-	string_list_clear(&merge_rr, 1);
-	return 0;
-}
diff --git a/builtin-reset.c b/builtin-reset.c
deleted file mode 100644
index 5fa1789..0000000
--- a/builtin-reset.c
+++ /dev/null
@@ -1,314 +0,0 @@
-/*
- * "git reset" builtin command
- *
- * Copyright (c) 2007 Carlos Rica
- *
- * Based on git-reset.sh, which is
- *
- * Copyright (c) 2005, 2006 Linus Torvalds and Junio C Hamano
- */
-#include "cache.h"
-#include "tag.h"
-#include "object.h"
-#include "commit.h"
-#include "run-command.h"
-#include "refs.h"
-#include "diff.h"
-#include "diffcore.h"
-#include "tree.h"
-#include "branch.h"
-#include "parse-options.h"
-
-static const char * const git_reset_usage[] = {
-	"git reset [--mixed | --soft | --hard | --merge] [-q] [<commit>]",
-	"git reset [--mixed] <commit> [--] <paths>...",
-	NULL
-};
-
-enum reset_type { MIXED, SOFT, HARD, MERGE, NONE };
-static const char *reset_type_names[] = { "mixed", "soft", "hard", "merge", NULL };
-
-static char *args_to_str(const char **argv)
-{
-	char *buf = NULL;
-	unsigned long len, space = 0, nr = 0;
-
-	for (; *argv; argv++) {
-		len = strlen(*argv);
-		ALLOC_GROW(buf, nr + 1 + len, space);
-		if (nr)
-			buf[nr++] = ' ';
-		memcpy(buf + nr, *argv, len);
-		nr += len;
-	}
-	ALLOC_GROW(buf, nr + 1, space);
-	buf[nr] = '\0';
-
-	return buf;
-}
-
-static inline int is_merge(void)
-{
-	return !access(git_path("MERGE_HEAD"), F_OK);
-}
-
-static int reset_index_file(const unsigned char *sha1, int reset_type, int quiet)
-{
-	int i = 0;
-	const char *args[6];
-
-	args[i++] = "read-tree";
-	if (!quiet)
-		args[i++] = "-v";
-	switch (reset_type) {
-	case MERGE:
-		args[i++] = "-u";
-		args[i++] = "-m";
-		break;
-	case HARD:
-		args[i++] = "-u";
-		/* fallthrough */
-	default:
-		args[i++] = "--reset";
-	}
-	args[i++] = sha1_to_hex(sha1);
-	args[i] = NULL;
-
-	return run_command_v_opt(args, RUN_GIT_CMD);
-}
-
-static void print_new_head_line(struct commit *commit)
-{
-	const char *hex, *body;
-
-	hex = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
-	printf("HEAD is now at %s", hex);
-	body = strstr(commit->buffer, "\n\n");
-	if (body) {
-		const char *eol;
-		size_t len;
-		body += 2;
-		eol = strchr(body, '\n');
-		len = eol ? eol - body : strlen(body);
-		printf(" %.*s\n", (int) len, body);
-	}
-	else
-		printf("\n");
-}
-
-static int update_index_refresh(int fd, struct lock_file *index_lock, int flags)
-{
-	int result;
-
-	if (!index_lock) {
-		index_lock = xcalloc(1, sizeof(struct lock_file));
-		fd = hold_locked_index(index_lock, 1);
-	}
-
-	if (read_cache() < 0)
-		return error("Could not read index");
-
-	result = refresh_cache(flags) ? 1 : 0;
-	if (write_cache(fd, active_cache, active_nr) ||
-			commit_locked_index(index_lock))
-		return error ("Could not refresh index");
-	return result;
-}
-
-static void update_index_from_diff(struct diff_queue_struct *q,
-		struct diff_options *opt, void *data)
-{
-	int i;
-	int *discard_flag = data;
-
-	/* do_diff_cache() mangled the index */
-	discard_cache();
-	*discard_flag = 1;
-	read_cache();
-
-	for (i = 0; i < q->nr; i++) {
-		struct diff_filespec *one = q->queue[i]->one;
-		if (one->mode) {
-			struct cache_entry *ce;
-			ce = make_cache_entry(one->mode, one->sha1, one->path,
-				0, 0);
-			if (!ce)
-				die("make_cache_entry failed for path '%s'",
-				    one->path);
-			add_cache_entry(ce, ADD_CACHE_OK_TO_ADD |
-				ADD_CACHE_OK_TO_REPLACE);
-		} else
-			remove_file_from_cache(one->path);
-	}
-}
-
-static int read_from_tree(const char *prefix, const char **argv,
-		unsigned char *tree_sha1, int refresh_flags)
-{
-	struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
-	int index_fd, index_was_discarded = 0;
-	struct diff_options opt;
-
-	memset(&opt, 0, sizeof(opt));
-	diff_tree_setup_paths(get_pathspec(prefix, (const char **)argv), &opt);
-	opt.output_format = DIFF_FORMAT_CALLBACK;
-	opt.format_callback = update_index_from_diff;
-	opt.format_callback_data = &index_was_discarded;
-
-	index_fd = hold_locked_index(lock, 1);
-	index_was_discarded = 0;
-	read_cache();
-	if (do_diff_cache(tree_sha1, &opt))
-		return 1;
-	diffcore_std(&opt);
-	diff_flush(&opt);
-	diff_tree_release_paths(&opt);
-
-	if (!index_was_discarded)
-		/* The index is still clobbered from do_diff_cache() */
-		discard_cache();
-	return update_index_refresh(index_fd, lock, refresh_flags);
-}
-
-static void prepend_reflog_action(const char *action, char *buf, size_t size)
-{
-	const char *sep = ": ";
-	const char *rla = getenv("GIT_REFLOG_ACTION");
-	if (!rla)
-		rla = sep = "";
-	if (snprintf(buf, size, "%s%s%s", rla, sep, action) >= size)
-		warning("Reflog action message too long: %.*s...", 50, buf);
-}
-
-int cmd_reset(int argc, const char **argv, const char *prefix)
-{
-	int i = 0, reset_type = NONE, update_ref_status = 0, quiet = 0;
-	const char *rev = "HEAD";
-	unsigned char sha1[20], *orig = NULL, sha1_orig[20],
-				*old_orig = NULL, sha1_old_orig[20];
-	struct commit *commit;
-	char *reflog_action, msg[1024];
-	const struct option options[] = {
-		OPT_SET_INT(0, "mixed", &reset_type,
-						"reset HEAD and index", MIXED),
-		OPT_SET_INT(0, "soft", &reset_type, "reset only HEAD", SOFT),
-		OPT_SET_INT(0, "hard", &reset_type,
-				"reset HEAD, index and working tree", HARD),
-		OPT_SET_INT(0, "merge", &reset_type,
-				"reset HEAD, index and working tree", MERGE),
-		OPT_BOOLEAN('q', NULL, &quiet,
-				"disable showing new HEAD in hard reset and progress message"),
-		OPT_END()
-	};
-
-	git_config(git_default_config, NULL);
-
-	argc = parse_options(argc, argv, prefix, options, git_reset_usage,
-						PARSE_OPT_KEEP_DASHDASH);
-	reflog_action = args_to_str(argv);
-	setenv("GIT_REFLOG_ACTION", reflog_action, 0);
-
-	/*
-	 * Possible arguments are:
-	 *
-	 * git reset [-opts] <rev> <paths>...
-	 * git reset [-opts] <rev> -- <paths>...
-	 * git reset [-opts] -- <paths>...
-	 * git reset [-opts] <paths>...
-	 *
-	 * At this point, argv[i] points immediately after [-opts].
-	 */
-
-	if (i < argc) {
-		if (!strcmp(argv[i], "--")) {
-			i++; /* reset to HEAD, possibly with paths */
-		} else if (i + 1 < argc && !strcmp(argv[i+1], "--")) {
-			rev = argv[i];
-			i += 2;
-		}
-		/*
-		 * Otherwise, argv[i] could be either <rev> or <paths> and
-		 * has to be unambiguous.
-		 */
-		else if (!get_sha1(argv[i], sha1)) {
-			/*
-			 * Ok, argv[i] looks like a rev; it should not
-			 * be a filename.
-			 */
-			verify_non_filename(prefix, argv[i]);
-			rev = argv[i++];
-		} else {
-			/* Otherwise we treat this as a filename */
-			verify_filename(prefix, argv[i]);
-		}
-	}
-
-	if (get_sha1(rev, sha1))
-		die("Failed to resolve '%s' as a valid ref.", rev);
-
-	commit = lookup_commit_reference(sha1);
-	if (!commit)
-		die("Could not parse object '%s'.", rev);
-	hashcpy(sha1, commit->object.sha1);
-
-	/* git reset tree [--] paths... can be used to
-	 * load chosen paths from the tree into the index without
-	 * affecting the working tree nor HEAD. */
-	if (i < argc) {
-		if (reset_type == MIXED)
-			warning("--mixed option is deprecated with paths.");
-		else if (reset_type != NONE)
-			die("Cannot do %s reset with paths.",
-					reset_type_names[reset_type]);
-		return read_from_tree(prefix, argv + i, sha1,
-				quiet ? REFRESH_QUIET : REFRESH_SAY_CHANGED);
-	}
-	if (reset_type == NONE)
-		reset_type = MIXED; /* by default */
-
-	if (reset_type == HARD && is_bare_repository())
-		die("hard reset makes no sense in a bare repository");
-
-	/* Soft reset does not touch the index file nor the working tree
-	 * at all, but requires them in a good order.  Other resets reset
-	 * the index file to the tree object we are switching to. */
-	if (reset_type == SOFT) {
-		if (is_merge() || read_cache() < 0 || unmerged_cache())
-			die("Cannot do a soft reset in the middle of a merge.");
-	}
-	else if (reset_index_file(sha1, reset_type, quiet))
-		die("Could not reset index file to revision '%s'.", rev);
-
-	/* Any resets update HEAD to the head being switched to,
-	 * saving the previous head in ORIG_HEAD before. */
-	if (!get_sha1("ORIG_HEAD", sha1_old_orig))
-		old_orig = sha1_old_orig;
-	if (!get_sha1("HEAD", sha1_orig)) {
-		orig = sha1_orig;
-		prepend_reflog_action("updating ORIG_HEAD", msg, sizeof(msg));
-		update_ref(msg, "ORIG_HEAD", orig, old_orig, 0, MSG_ON_ERR);
-	}
-	else if (old_orig)
-		delete_ref("ORIG_HEAD", old_orig, 0);
-	prepend_reflog_action("updating HEAD", msg, sizeof(msg));
-	update_ref_status = update_ref(msg, "HEAD", sha1, orig, 0, MSG_ON_ERR);
-
-	switch (reset_type) {
-	case HARD:
-		if (!update_ref_status && !quiet)
-			print_new_head_line(commit);
-		break;
-	case SOFT: /* Nothing else to do. */
-		break;
-	case MIXED: /* Report what has not been updated. */
-		update_index_refresh(0, NULL,
-				quiet ? REFRESH_QUIET : REFRESH_SAY_CHANGED);
-		break;
-	}
-
-	remove_branch_state();
-
-	free(reflog_action);
-
-	return update_ref_status;
-}
diff --git a/builtin-rev-list.c b/builtin-rev-list.c
deleted file mode 100644
index 4ba1c12..0000000
--- a/builtin-rev-list.c
+++ /dev/null
@@ -1,404 +0,0 @@
-#include "cache.h"
-#include "commit.h"
-#include "diff.h"
-#include "revision.h"
-#include "list-objects.h"
-#include "builtin.h"
-#include "log-tree.h"
-#include "graph.h"
-#include "bisect.h"
-
-static const char rev_list_usage[] =
-"git rev-list [OPTION] <commit-id>... [ -- paths... ]\n"
-"  limiting output:\n"
-"    --max-count=nr\n"
-"    --max-age=epoch\n"
-"    --min-age=epoch\n"
-"    --sparse\n"
-"    --no-merges\n"
-"    --remove-empty\n"
-"    --all\n"
-"    --branches\n"
-"    --tags\n"
-"    --remotes\n"
-"    --stdin\n"
-"    --quiet\n"
-"  ordering output:\n"
-"    --topo-order\n"
-"    --date-order\n"
-"    --reverse\n"
-"  formatting output:\n"
-"    --parents\n"
-"    --children\n"
-"    --objects | --objects-edge\n"
-"    --unpacked\n"
-"    --header | --pretty\n"
-"    --abbrev=nr | --no-abbrev\n"
-"    --abbrev-commit\n"
-"    --left-right\n"
-"  special purpose:\n"
-"    --bisect\n"
-"    --bisect-vars\n"
-"    --bisect-all"
-;
-
-static void finish_commit(struct commit *commit, void *data);
-static void show_commit(struct commit *commit, void *data)
-{
-	struct rev_list_info *info = data;
-	struct rev_info *revs = info->revs;
-
-	graph_show_commit(revs->graph);
-
-	if (info->show_timestamp)
-		printf("%lu ", commit->date);
-	if (info->header_prefix)
-		fputs(info->header_prefix, stdout);
-
-	if (!revs->graph) {
-		if (commit->object.flags & BOUNDARY)
-			putchar('-');
-		else if (commit->object.flags & UNINTERESTING)
-			putchar('^');
-		else if (revs->left_right) {
-			if (commit->object.flags & SYMMETRIC_LEFT)
-				putchar('<');
-			else
-				putchar('>');
-		}
-	}
-	if (revs->abbrev_commit && revs->abbrev)
-		fputs(find_unique_abbrev(commit->object.sha1, revs->abbrev),
-		      stdout);
-	else
-		fputs(sha1_to_hex(commit->object.sha1), stdout);
-	if (revs->print_parents) {
-		struct commit_list *parents = commit->parents;
-		while (parents) {
-			printf(" %s", sha1_to_hex(parents->item->object.sha1));
-			parents = parents->next;
-		}
-	}
-	if (revs->children.name) {
-		struct commit_list *children;
-
-		children = lookup_decoration(&revs->children, &commit->object);
-		while (children) {
-			printf(" %s", sha1_to_hex(children->item->object.sha1));
-			children = children->next;
-		}
-	}
-	show_decorations(revs, commit);
-	if (revs->commit_format == CMIT_FMT_ONELINE)
-		putchar(' ');
-	else
-		putchar('\n');
-
-	if (revs->verbose_header && commit->buffer) {
-		struct strbuf buf = STRBUF_INIT;
-		pretty_print_commit(revs->commit_format, commit,
-				    &buf, revs->abbrev, NULL, NULL,
-				    revs->date_mode, 0);
-		if (revs->graph) {
-			if (buf.len) {
-				if (revs->commit_format != CMIT_FMT_ONELINE)
-					graph_show_oneline(revs->graph);
-
-				graph_show_commit_msg(revs->graph, &buf);
-
-				/*
-				 * Add a newline after the commit message.
-				 *
-				 * Usually, this newline produces a blank
-				 * padding line between entries, in which case
-				 * we need to add graph padding on this line.
-				 *
-				 * However, the commit message may not end in a
-				 * newline.  In this case the newline simply
-				 * ends the last line of the commit message,
-				 * and we don't need any graph output.  (This
-				 * always happens with CMIT_FMT_ONELINE, and it
-				 * happens with CMIT_FMT_USERFORMAT when the
-				 * format doesn't explicitly end in a newline.)
-				 */
-				if (buf.len && buf.buf[buf.len - 1] == '\n')
-					graph_show_padding(revs->graph);
-				putchar('\n');
-			} else {
-				/*
-				 * If the message buffer is empty, just show
-				 * the rest of the graph output for this
-				 * commit.
-				 */
-				if (graph_show_remainder(revs->graph))
-					putchar('\n');
-			}
-		} else {
-			if (buf.len)
-				printf("%s%c", buf.buf, info->hdr_termination);
-		}
-		strbuf_release(&buf);
-	} else {
-		if (graph_show_remainder(revs->graph))
-			putchar('\n');
-	}
-	maybe_flush_or_die(stdout, "stdout");
-	finish_commit(commit, data);
-}
-
-static void finish_commit(struct commit *commit, void *data)
-{
-	if (commit->parents) {
-		free_commit_list(commit->parents);
-		commit->parents = NULL;
-	}
-	free(commit->buffer);
-	commit->buffer = NULL;
-}
-
-static void finish_object(struct object *obj, const struct name_path *path, const char *name)
-{
-	if (obj->type == OBJ_BLOB && !has_sha1_file(obj->sha1))
-		die("missing blob object '%s'", sha1_to_hex(obj->sha1));
-}
-
-static void show_object(struct object *obj, const struct name_path *path, const char *component)
-{
-	char *name = path_name(path, component);
-	/* An object with name "foo\n0000000..." can be used to
-	 * confuse downstream "git pack-objects" very badly.
-	 */
-	const char *ep = strchr(name, '\n');
-
-	finish_object(obj, path, name);
-	if (ep) {
-		printf("%s %.*s\n", sha1_to_hex(obj->sha1),
-		       (int) (ep - name),
-		       name);
-	}
-	else
-		printf("%s %s\n", sha1_to_hex(obj->sha1), name);
-	free(name);
-}
-
-static void show_edge(struct commit *commit)
-{
-	printf("-%s\n", sha1_to_hex(commit->object.sha1));
-}
-
-static inline int log2i(int n)
-{
-	int log2 = 0;
-
-	for (; n > 1; n >>= 1)
-		log2++;
-
-	return log2;
-}
-
-static inline int exp2i(int n)
-{
-	return 1 << n;
-}
-
-/*
- * Estimate the number of bisect steps left (after the current step)
- *
- * For any x between 0 included and 2^n excluded, the probability for
- * n - 1 steps left looks like:
- *
- * P(2^n + x) == (2^n - x) / (2^n + x)
- *
- * and P(2^n + x) < 0.5 means 2^n < 3x
- */
-int estimate_bisect_steps(int all)
-{
-	int n, x, e;
-
-	if (all < 3)
-		return 0;
-
-	n = log2i(all);
-	e = exp2i(n);
-	x = all - e;
-
-	return (e < 3 * x) ? n : n - 1;
-}
-
-void print_commit_list(struct commit_list *list,
-		       const char *format_cur,
-		       const char *format_last)
-{
-	for ( ; list; list = list->next) {
-		const char *format = list->next ? format_cur : format_last;
-		printf(format, sha1_to_hex(list->item->object.sha1));
-	}
-}
-
-static void show_tried_revs(struct commit_list *tried)
-{
-	printf("bisect_tried='");
-	print_commit_list(tried, "%s|", "%s");
-	printf("'\n");
-}
-
-static void print_var_str(const char *var, const char *val)
-{
-	printf("%s='%s'\n", var, val);
-}
-
-static void print_var_int(const char *var, int val)
-{
-	printf("%s=%d\n", var, val);
-}
-
-int show_bisect_vars(struct rev_list_info *info, int reaches, int all)
-{
-	int cnt, flags = info->bisect_show_flags;
-	char hex[41] = "";
-	struct commit_list *tried;
-	struct rev_info *revs = info->revs;
-
-	if (!revs->commits && !(flags & BISECT_SHOW_TRIED))
-		return 1;
-
-	revs->commits = filter_skipped(revs->commits, &tried,
-				       flags & BISECT_SHOW_ALL,
-				       NULL, NULL);
-
-	/*
-	 * revs->commits can reach "reaches" commits among
-	 * "all" commits.  If it is good, then there are
-	 * (all-reaches) commits left to be bisected.
-	 * On the other hand, if it is bad, then the set
-	 * to bisect is "reaches".
-	 * A bisect set of size N has (N-1) commits further
-	 * to test, as we already know one bad one.
-	 */
-	cnt = all - reaches;
-	if (cnt < reaches)
-		cnt = reaches;
-
-	if (revs->commits)
-		strcpy(hex, sha1_to_hex(revs->commits->item->object.sha1));
-
-	if (flags & BISECT_SHOW_ALL) {
-		traverse_commit_list(revs, show_commit, show_object, info);
-		printf("------\n");
-	}
-
-	if (flags & BISECT_SHOW_TRIED)
-		show_tried_revs(tried);
-
-	print_var_str("bisect_rev", hex);
-	print_var_int("bisect_nr", cnt - 1);
-	print_var_int("bisect_good", all - reaches - 1);
-	print_var_int("bisect_bad", reaches - 1);
-	print_var_int("bisect_all", all);
-	print_var_int("bisect_steps", estimate_bisect_steps(all));
-
-	return 0;
-}
-
-int cmd_rev_list(int argc, const char **argv, const char *prefix)
-{
-	struct rev_info revs;
-	struct rev_list_info info;
-	int i;
-	int read_from_stdin = 0;
-	int bisect_list = 0;
-	int bisect_show_vars = 0;
-	int bisect_find_all = 0;
-	int quiet = 0;
-
-	git_config(git_default_config, NULL);
-	init_revisions(&revs, prefix);
-	revs.abbrev = 0;
-	revs.commit_format = CMIT_FMT_UNSPECIFIED;
-	argc = setup_revisions(argc, argv, &revs, NULL);
-
-	memset(&info, 0, sizeof(info));
-	info.revs = &revs;
-
-	quiet = DIFF_OPT_TST(&revs.diffopt, QUIET);
-	for (i = 1 ; i < argc; i++) {
-		const char *arg = argv[i];
-
-		if (!strcmp(arg, "--header")) {
-			revs.verbose_header = 1;
-			continue;
-		}
-		if (!strcmp(arg, "--timestamp")) {
-			info.show_timestamp = 1;
-			continue;
-		}
-		if (!strcmp(arg, "--bisect")) {
-			bisect_list = 1;
-			continue;
-		}
-		if (!strcmp(arg, "--bisect-all")) {
-			bisect_list = 1;
-			bisect_find_all = 1;
-			info.bisect_show_flags = BISECT_SHOW_ALL;
-			revs.show_decorations = 1;
-			continue;
-		}
-		if (!strcmp(arg, "--bisect-vars")) {
-			bisect_list = 1;
-			bisect_show_vars = 1;
-			continue;
-		}
-		if (!strcmp(arg, "--stdin")) {
-			if (read_from_stdin++)
-				die("--stdin given twice?");
-			read_revisions_from_stdin(&revs);
-			continue;
-		}
-		usage(rev_list_usage);
-
-	}
-	if (revs.commit_format != CMIT_FMT_UNSPECIFIED) {
-		/* The command line has a --pretty  */
-		info.hdr_termination = '\n';
-		if (revs.commit_format == CMIT_FMT_ONELINE)
-			info.header_prefix = "";
-		else
-			info.header_prefix = "commit ";
-	}
-	else if (revs.verbose_header)
-		/* Only --header was specified */
-		revs.commit_format = CMIT_FMT_RAW;
-
-	if ((!revs.commits &&
-	     (!(revs.tag_objects||revs.tree_objects||revs.blob_objects) &&
-	      !revs.pending.nr)) ||
-	    revs.diff)
-		usage(rev_list_usage);
-
-	save_commit_buffer = revs.verbose_header ||
-		revs.grep_filter.pattern_list;
-	if (bisect_list)
-		revs.limited = 1;
-
-	if (prepare_revision_walk(&revs))
-		die("revision walk setup failed");
-	if (revs.tree_objects)
-		mark_edges_uninteresting(revs.commits, &revs, show_edge);
-
-	if (bisect_list) {
-		int reaches = reaches, all = all;
-
-		revs.commits = find_bisection(revs.commits, &reaches, &all,
-					      bisect_find_all);
-
-		if (bisect_show_vars)
-			return show_bisect_vars(&info, reaches, all);
-	}
-
-	traverse_commit_list(&revs,
-			     quiet ? finish_commit : show_commit,
-			     quiet ? finish_object : show_object,
-			     &info);
-
-	return 0;
-}
diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c
deleted file mode 100644
index 45bead6..0000000
--- a/builtin-rev-parse.c
+++ /dev/null
@@ -1,674 +0,0 @@
-/*
- * rev-parse.c
- *
- * Copyright (C) Linus Torvalds, 2005
- */
-#include "cache.h"
-#include "commit.h"
-#include "refs.h"
-#include "quote.h"
-#include "builtin.h"
-#include "parse-options.h"
-
-#define DO_REVS		1
-#define DO_NOREV	2
-#define DO_FLAGS	4
-#define DO_NONFLAGS	8
-static int filter = ~0;
-
-static const char *def;
-
-#define NORMAL 0
-#define REVERSED 1
-static int show_type = NORMAL;
-
-#define SHOW_SYMBOLIC_ASIS 1
-#define SHOW_SYMBOLIC_FULL 2
-static int symbolic;
-static int abbrev;
-static int abbrev_ref;
-static int abbrev_ref_strict;
-static int output_sq;
-
-/*
- * Some arguments are relevant "revision" arguments,
- * others are about output format or other details.
- * This sorts it all out.
- */
-static int is_rev_argument(const char *arg)
-{
-	static const char *rev_args[] = {
-		"--all",
-		"--bisect",
-		"--dense",
-		"--branches",
-		"--header",
-		"--max-age=",
-		"--max-count=",
-		"--min-age=",
-		"--no-merges",
-		"--objects",
-		"--objects-edge",
-		"--parents",
-		"--pretty",
-		"--remotes",
-		"--sparse",
-		"--tags",
-		"--topo-order",
-		"--date-order",
-		"--unpacked",
-		NULL
-	};
-	const char **p = rev_args;
-
-	/* accept -<digit>, like traditional "head" */
-	if ((*arg == '-') && isdigit(arg[1]))
-		return 1;
-
-	for (;;) {
-		const char *str = *p++;
-		int len;
-		if (!str)
-			return 0;
-		len = strlen(str);
-		if (!strcmp(arg, str) ||
-		    (str[len-1] == '=' && !strncmp(arg, str, len)))
-			return 1;
-	}
-}
-
-/* Output argument as a string, either SQ or normal */
-static void show(const char *arg)
-{
-	if (output_sq) {
-		int sq = '\'', ch;
-
-		putchar(sq);
-		while ((ch = *arg++)) {
-			if (ch == sq)
-				fputs("'\\'", stdout);
-			putchar(ch);
-		}
-		putchar(sq);
-		putchar(' ');
-	}
-	else
-		puts(arg);
-}
-
-/* Like show(), but with a negation prefix according to type */
-static void show_with_type(int type, const char *arg)
-{
-	if (type != show_type)
-		putchar('^');
-	show(arg);
-}
-
-/* Output a revision, only if filter allows it */
-static void show_rev(int type, const unsigned char *sha1, const char *name)
-{
-	if (!(filter & DO_REVS))
-		return;
-	def = NULL;
-
-	if ((symbolic || abbrev_ref) && name) {
-		if (symbolic == SHOW_SYMBOLIC_FULL || abbrev_ref) {
-			unsigned char discard[20];
-			char *full;
-
-			switch (dwim_ref(name, strlen(name), discard, &full)) {
-			case 0:
-				/*
-				 * Not found -- not a ref.  We could
-				 * emit "name" here, but symbolic-full
-				 * users are interested in finding the
-				 * refs spelled in full, and they would
-				 * need to filter non-refs if we did so.
-				 */
-				break;
-			case 1: /* happy */
-				if (abbrev_ref)
-					full = shorten_unambiguous_ref(full,
-						abbrev_ref_strict);
-				show_with_type(type, full);
-				break;
-			default: /* ambiguous */
-				error("refname '%s' is ambiguous", name);
-				break;
-			}
-		} else {
-			show_with_type(type, name);
-		}
-	}
-	else if (abbrev)
-		show_with_type(type, find_unique_abbrev(sha1, abbrev));
-	else
-		show_with_type(type, sha1_to_hex(sha1));
-}
-
-/* Output a flag, only if filter allows it. */
-static int show_flag(const char *arg)
-{
-	if (!(filter & DO_FLAGS))
-		return 0;
-	if (filter & (is_rev_argument(arg) ? DO_REVS : DO_NOREV)) {
-		show(arg);
-		return 1;
-	}
-	return 0;
-}
-
-static int show_default(void)
-{
-	const char *s = def;
-
-	if (s) {
-		unsigned char sha1[20];
-
-		def = NULL;
-		if (!get_sha1(s, sha1)) {
-			show_rev(NORMAL, sha1, s);
-			return 1;
-		}
-	}
-	return 0;
-}
-
-static int show_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
-{
-	show_rev(NORMAL, sha1, refname);
-	return 0;
-}
-
-static void show_datestring(const char *flag, const char *datestr)
-{
-	static char buffer[100];
-
-	/* date handling requires both flags and revs */
-	if ((filter & (DO_FLAGS | DO_REVS)) != (DO_FLAGS | DO_REVS))
-		return;
-	snprintf(buffer, sizeof(buffer), "%s%lu", flag, approxidate(datestr));
-	show(buffer);
-}
-
-static int show_file(const char *arg)
-{
-	show_default();
-	if ((filter & (DO_NONFLAGS|DO_NOREV)) == (DO_NONFLAGS|DO_NOREV)) {
-		show(arg);
-		return 1;
-	}
-	return 0;
-}
-
-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;
-}
-
-static int try_parent_shorthands(const char *arg)
-{
-	char *dotdot;
-	unsigned char sha1[20];
-	struct commit *commit;
-	struct commit_list *parents;
-	int parents_only;
-
-	if ((dotdot = strstr(arg, "^!")))
-		parents_only = 0;
-	else if ((dotdot = strstr(arg, "^@")))
-		parents_only = 1;
-
-	if (!dotdot || dotdot[2])
-		return 0;
-
-	*dotdot = 0;
-	if (get_sha1(arg, sha1))
-		return 0;
-
-	if (!parents_only)
-		show_rev(NORMAL, sha1, arg);
-	commit = lookup_commit_reference(sha1);
-	for (parents = commit->parents; parents; parents = parents->next)
-		show_rev(parents_only ? NORMAL : REVERSED,
-				parents->item->object.sha1, arg);
-
-	return 1;
-}
-
-static int parseopt_dump(const struct option *o, const char *arg, int unset)
-{
-	struct strbuf *parsed = o->value;
-	if (unset)
-		strbuf_addf(parsed, " --no-%s", o->long_name);
-	else if (o->short_name)
-		strbuf_addf(parsed, " -%c", o->short_name);
-	else
-		strbuf_addf(parsed, " --%s", o->long_name);
-	if (arg) {
-		strbuf_addch(parsed, ' ');
-		sq_quote_buf(parsed, arg);
-	}
-	return 0;
-}
-
-static const char *skipspaces(const char *s)
-{
-	while (isspace(*s))
-		s++;
-	return s;
-}
-
-static int cmd_parseopt(int argc, const char **argv, const char *prefix)
-{
-	static int keep_dashdash = 0, stop_at_non_option = 0;
-	static char const * const parseopt_usage[] = {
-		"git rev-parse --parseopt [options] -- [<args>...]",
-		NULL
-	};
-	static struct option parseopt_opts[] = {
-		OPT_BOOLEAN(0, "keep-dashdash", &keep_dashdash,
-					"keep the `--` passed as an arg"),
-		OPT_BOOLEAN(0, "stop-at-non-option", &stop_at_non_option,
-					"stop parsing after the "
-					"first non-option argument"),
-		OPT_END(),
-	};
-
-	struct strbuf sb = STRBUF_INIT, parsed = STRBUF_INIT;
-	const char **usage = NULL;
-	struct option *opts = NULL;
-	int onb = 0, osz = 0, unb = 0, usz = 0;
-
-	strbuf_addstr(&parsed, "set --");
-	argc = parse_options(argc, argv, prefix, parseopt_opts, parseopt_usage,
-	                     PARSE_OPT_KEEP_DASHDASH);
-	if (argc < 1 || strcmp(argv[0], "--"))
-		usage_with_options(parseopt_usage, parseopt_opts);
-
-	/* get the usage up to the first line with a -- on it */
-	for (;;) {
-		if (strbuf_getline(&sb, stdin, '\n') == EOF)
-			die("premature end of input");
-		ALLOC_GROW(usage, unb + 1, usz);
-		if (!strcmp("--", sb.buf)) {
-			if (unb < 1)
-				die("no usage string given before the `--' separator");
-			usage[unb] = NULL;
-			break;
-		}
-		usage[unb++] = strbuf_detach(&sb, NULL);
-	}
-
-	/* parse: (<short>|<short>,<long>|<long>)[=?]? SP+ <help> */
-	while (strbuf_getline(&sb, stdin, '\n') != EOF) {
-		const char *s;
-		struct option *o;
-
-		if (!sb.len)
-			continue;
-
-		ALLOC_GROW(opts, onb + 1, osz);
-		memset(opts + onb, 0, sizeof(opts[onb]));
-
-		o = &opts[onb++];
-		s = strchr(sb.buf, ' ');
-		if (!s || *sb.buf == ' ') {
-			o->type = OPTION_GROUP;
-			o->help = xstrdup(skipspaces(sb.buf));
-			continue;
-		}
-
-		o->type = OPTION_CALLBACK;
-		o->help = xstrdup(skipspaces(s));
-		o->value = &parsed;
-		o->flags = PARSE_OPT_NOARG;
-		o->callback = &parseopt_dump;
-		while (s > sb.buf && strchr("*=?!", s[-1])) {
-			switch (*--s) {
-			case '=':
-				o->flags &= ~PARSE_OPT_NOARG;
-				break;
-			case '?':
-				o->flags &= ~PARSE_OPT_NOARG;
-				o->flags |= PARSE_OPT_OPTARG;
-				break;
-			case '!':
-				o->flags |= PARSE_OPT_NONEG;
-				break;
-			case '*':
-				o->flags |= PARSE_OPT_HIDDEN;
-				break;
-			}
-		}
-
-		if (s - sb.buf == 1) /* short option only */
-			o->short_name = *sb.buf;
-		else if (sb.buf[1] != ',') /* long option only */
-			o->long_name = xmemdupz(sb.buf, s - sb.buf);
-		else {
-			o->short_name = *sb.buf;
-			o->long_name = xmemdupz(sb.buf + 2, s - sb.buf - 2);
-		}
-	}
-	strbuf_release(&sb);
-
-	/* put an OPT_END() */
-	ALLOC_GROW(opts, onb + 1, osz);
-	memset(opts + onb, 0, sizeof(opts[onb]));
-	argc = parse_options(argc, argv, prefix, opts, usage,
-			keep_dashdash ? PARSE_OPT_KEEP_DASHDASH : 0 |
-			stop_at_non_option ? PARSE_OPT_STOP_AT_NON_OPTION : 0);
-
-	strbuf_addf(&parsed, " --");
-	sq_quote_argv(&parsed, argv, 0);
-	puts(parsed.buf);
-	return 0;
-}
-
-static int cmd_sq_quote(int argc, const char **argv)
-{
-	struct strbuf buf = STRBUF_INIT;
-
-	if (argc)
-		sq_quote_argv(&buf, argv, 0);
-	printf("%s\n", buf.buf);
-	strbuf_release(&buf);
-
-	return 0;
-}
-
-static void die_no_single_rev(int quiet)
-{
-	if (quiet)
-		exit(1);
-	else
-		die("Needed a single revision");
-}
-
-int cmd_rev_parse(int argc, const char **argv, const char *prefix)
-{
-	int i, as_is = 0, verify = 0, quiet = 0, revs_count = 0, type = 0;
-	unsigned char sha1[20];
-	const char *name = NULL;
-
-	if (argc > 1 && !strcmp("--parseopt", argv[1]))
-		return cmd_parseopt(argc - 1, argv + 1, prefix);
-
-	if (argc > 1 && !strcmp("--sq-quote", argv[1]))
-		return cmd_sq_quote(argc - 2, argv + 2);
-
-	prefix = setup_git_directory();
-	git_config(git_default_config, NULL);
-	for (i = 1; i < argc; i++) {
-		const char *arg = argv[i];
-
-		if (as_is) {
-			if (show_file(arg) && as_is < 2)
-				verify_filename(prefix, arg);
-			continue;
-		}
-		if (!strcmp(arg,"-n")) {
-			if (++i >= argc)
-				die("-n requires an argument");
-			if ((filter & DO_FLAGS) && (filter & DO_REVS)) {
-				show(arg);
-				show(argv[i]);
-			}
-			continue;
-		}
-		if (!prefixcmp(arg, "-n")) {
-			if ((filter & DO_FLAGS) && (filter & DO_REVS))
-				show(arg);
-			continue;
-		}
-
-		if (*arg == '-') {
-			if (!strcmp(arg, "--")) {
-				as_is = 2;
-				/* Pass on the "--" if we show anything but files.. */
-				if (filter & (DO_FLAGS | DO_REVS))
-					show_file(arg);
-				continue;
-			}
-			if (!strcmp(arg, "--default")) {
-				def = argv[i+1];
-				i++;
-				continue;
-			}
-			if (!strcmp(arg, "--revs-only")) {
-				filter &= ~DO_NOREV;
-				continue;
-			}
-			if (!strcmp(arg, "--no-revs")) {
-				filter &= ~DO_REVS;
-				continue;
-			}
-			if (!strcmp(arg, "--flags")) {
-				filter &= ~DO_NONFLAGS;
-				continue;
-			}
-			if (!strcmp(arg, "--no-flags")) {
-				filter &= ~DO_FLAGS;
-				continue;
-			}
-			if (!strcmp(arg, "--verify")) {
-				filter &= ~(DO_FLAGS|DO_NOREV);
-				verify = 1;
-				continue;
-			}
-			if (!strcmp(arg, "--quiet") || !strcmp(arg, "-q")) {
-				quiet = 1;
-				continue;
-			}
-			if (!strcmp(arg, "--short") ||
-			    !prefixcmp(arg, "--short=")) {
-				filter &= ~(DO_FLAGS|DO_NOREV);
-				verify = 1;
-				abbrev = DEFAULT_ABBREV;
-				if (arg[7] == '=')
-					abbrev = strtoul(arg + 8, NULL, 10);
-				if (abbrev < MINIMUM_ABBREV)
-					abbrev = MINIMUM_ABBREV;
-				else if (40 <= abbrev)
-					abbrev = 40;
-				continue;
-			}
-			if (!strcmp(arg, "--sq")) {
-				output_sq = 1;
-				continue;
-			}
-			if (!strcmp(arg, "--not")) {
-				show_type ^= REVERSED;
-				continue;
-			}
-			if (!strcmp(arg, "--symbolic")) {
-				symbolic = SHOW_SYMBOLIC_ASIS;
-				continue;
-			}
-			if (!strcmp(arg, "--symbolic-full-name")) {
-				symbolic = SHOW_SYMBOLIC_FULL;
-				continue;
-			}
-			if (!prefixcmp(arg, "--abbrev-ref") &&
-			    (!arg[12] || arg[12] == '=')) {
-				abbrev_ref = 1;
-				abbrev_ref_strict = warn_ambiguous_refs;
-				if (arg[12] == '=') {
-					if (!strcmp(arg + 13, "strict"))
-						abbrev_ref_strict = 1;
-					else if (!strcmp(arg + 13, "loose"))
-						abbrev_ref_strict = 0;
-					else
-						die("unknown mode for %s", arg);
-				}
-				continue;
-			}
-			if (!strcmp(arg, "--all")) {
-				for_each_ref(show_reference, NULL);
-				continue;
-			}
-			if (!strcmp(arg, "--branches")) {
-				for_each_branch_ref(show_reference, NULL);
-				continue;
-			}
-			if (!strcmp(arg, "--tags")) {
-				for_each_tag_ref(show_reference, NULL);
-				continue;
-			}
-			if (!strcmp(arg, "--remotes")) {
-				for_each_remote_ref(show_reference, NULL);
-				continue;
-			}
-			if (!strcmp(arg, "--show-prefix")) {
-				if (prefix)
-					puts(prefix);
-				continue;
-			}
-			if (!strcmp(arg, "--show-cdup")) {
-				const char *pfx = prefix;
-				if (!is_inside_work_tree()) {
-					const char *work_tree =
-						get_git_work_tree();
-					if (work_tree)
-						printf("%s\n", work_tree);
-					continue;
-				}
-				while (pfx) {
-					pfx = strchr(pfx, '/');
-					if (pfx) {
-						pfx++;
-						printf("../");
-					}
-				}
-				putchar('\n');
-				continue;
-			}
-			if (!strcmp(arg, "--git-dir")) {
-				const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
-				static char cwd[PATH_MAX];
-				if (gitdir) {
-					puts(gitdir);
-					continue;
-				}
-				if (!prefix) {
-					puts(".git");
-					continue;
-				}
-				if (!getcwd(cwd, PATH_MAX))
-					die_errno("unable to get current working directory");
-				printf("%s/.git\n", cwd);
-				continue;
-			}
-			if (!strcmp(arg, "--is-inside-git-dir")) {
-				printf("%s\n", is_inside_git_dir() ? "true"
-						: "false");
-				continue;
-			}
-			if (!strcmp(arg, "--is-inside-work-tree")) {
-				printf("%s\n", is_inside_work_tree() ? "true"
-						: "false");
-				continue;
-			}
-			if (!strcmp(arg, "--is-bare-repository")) {
-				printf("%s\n", is_bare_repository() ? "true"
-						: "false");
-				continue;
-			}
-			if (!prefixcmp(arg, "--since=")) {
-				show_datestring("--max-age=", arg+8);
-				continue;
-			}
-			if (!prefixcmp(arg, "--after=")) {
-				show_datestring("--max-age=", arg+8);
-				continue;
-			}
-			if (!prefixcmp(arg, "--before=")) {
-				show_datestring("--min-age=", arg+9);
-				continue;
-			}
-			if (!prefixcmp(arg, "--until=")) {
-				show_datestring("--min-age=", arg+8);
-				continue;
-			}
-			if (show_flag(arg) && verify)
-				die_no_single_rev(quiet);
-			continue;
-		}
-
-		/* Not a flag argument */
-		if (try_difference(arg))
-			continue;
-		if (try_parent_shorthands(arg))
-			continue;
-		name = arg;
-		type = NORMAL;
-		if (*arg == '^') {
-			name++;
-			type = REVERSED;
-		}
-		if (!get_sha1(name, sha1)) {
-			if (verify)
-				revs_count++;
-			else
-				show_rev(type, sha1, name);
-			continue;
-		}
-		if (verify)
-			die_no_single_rev(quiet);
-		as_is = 1;
-		if (!show_file(arg))
-			continue;
-		verify_filename(prefix, arg);
-	}
-	if (verify) {
-		if (revs_count == 1) {
-			show_rev(type, sha1, name);
-			return 0;
-		} else if (revs_count == 0 && show_default())
-			return 0;
-		die_no_single_rev(quiet);
-	} else
-		show_default();
-	return 0;
-}
diff --git a/builtin-revert.c b/builtin-revert.c
deleted file mode 100644
index 151aa6a..0000000
--- a/builtin-revert.c
+++ /dev/null
@@ -1,449 +0,0 @@
-#include "cache.h"
-#include "builtin.h"
-#include "object.h"
-#include "commit.h"
-#include "tag.h"
-#include "wt-status.h"
-#include "run-command.h"
-#include "exec_cmd.h"
-#include "utf8.h"
-#include "parse-options.h"
-#include "cache-tree.h"
-#include "diff.h"
-#include "revision.h"
-#include "rerere.h"
-#include "merge-recursive.h"
-
-/*
- * This implements the builtins revert and cherry-pick.
- *
- * Copyright (c) 2007 Johannes E. Schindelin
- *
- * Based on git-revert.sh, which is
- *
- * Copyright (c) 2005 Linus Torvalds
- * Copyright (c) 2005 Junio C Hamano
- */
-
-static const char * const revert_usage[] = {
-	"git revert [options] <commit-ish>",
-	NULL
-};
-
-static const char * const cherry_pick_usage[] = {
-	"git cherry-pick [options] <commit-ish>",
-	NULL
-};
-
-static int edit, no_replay, no_commit, mainline, signoff;
-static enum { REVERT, CHERRY_PICK } action;
-static struct commit *commit;
-
-static const char *me;
-
-#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
-
-static void parse_args(int argc, const char **argv)
-{
-	const char * const * usage_str =
-		action == REVERT ?  revert_usage : cherry_pick_usage;
-	unsigned char sha1[20];
-	const char *arg;
-	int noop;
-	struct option options[] = {
-		OPT_BOOLEAN('n', "no-commit", &no_commit, "don't automatically commit"),
-		OPT_BOOLEAN('e', "edit", &edit, "edit the commit message"),
-		OPT_BOOLEAN('x', NULL, &no_replay, "append commit name when cherry-picking"),
-		OPT_BOOLEAN('r', NULL, &noop, "no-op (backward compatibility)"),
-		OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
-		OPT_INTEGER('m', "mainline", &mainline, "parent number"),
-		OPT_END(),
-	};
-
-	if (parse_options(argc, argv, NULL, options, usage_str, 0) != 1)
-		usage_with_options(usage_str, options);
-	arg = argv[0];
-
-	if (get_sha1(arg, sha1))
-		die ("Cannot find '%s'", arg);
-	commit = (struct commit *)parse_object(sha1);
-	if (!commit)
-		die ("Could not find %s", sha1_to_hex(sha1));
-	if (commit->object.type == OBJ_TAG) {
-		commit = (struct commit *)
-			deref_tag((struct object *)commit, arg, strlen(arg));
-	}
-	if (commit->object.type != OBJ_COMMIT)
-		die ("'%s' does not point to a commit", arg);
-}
-
-static char *get_oneline(const char *message)
-{
-	char *result;
-	const char *p = message, *abbrev, *eol;
-	int abbrev_len, oneline_len;
-
-	if (!p)
-		die ("Could not read commit message of %s",
-				sha1_to_hex(commit->object.sha1));
-	while (*p && (*p != '\n' || p[1] != '\n'))
-		p++;
-
-	if (*p) {
-		p += 2;
-		for (eol = p + 1; *eol && *eol != '\n'; eol++)
-			; /* do nothing */
-	} else
-		eol = p;
-	abbrev = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
-	abbrev_len = strlen(abbrev);
-	oneline_len = eol - p;
-	result = xmalloc(abbrev_len + 5 + oneline_len);
-	memcpy(result, abbrev, abbrev_len);
-	memcpy(result + abbrev_len, "... ", 4);
-	memcpy(result + abbrev_len + 4, p, oneline_len);
-	result[abbrev_len + 4 + oneline_len] = '\0';
-	return result;
-}
-
-static char *get_encoding(const char *message)
-{
-	const char *p = message, *eol;
-
-	if (!p)
-		die ("Could not read commit message of %s",
-				sha1_to_hex(commit->object.sha1));
-	while (*p && *p != '\n') {
-		for (eol = p + 1; *eol && *eol != '\n'; eol++)
-			; /* do nothing */
-		if (!prefixcmp(p, "encoding ")) {
-			char *result = xmalloc(eol - 8 - p);
-			strlcpy(result, p + 9, eol - 8 - p);
-			return result;
-		}
-		p = eol;
-		if (*p == '\n')
-			p++;
-	}
-	return NULL;
-}
-
-static struct lock_file msg_file;
-static int msg_fd;
-
-static void add_to_msg(const char *string)
-{
-	int len = strlen(string);
-	if (write_in_full(msg_fd, string, len) < 0)
-		die_errno ("Could not write to MERGE_MSG");
-}
-
-static void add_message_to_msg(const char *message)
-{
-	const char *p = message;
-	while (*p && (*p != '\n' || p[1] != '\n'))
-		p++;
-
-	if (!*p)
-		add_to_msg(sha1_to_hex(commit->object.sha1));
-
-	p += 2;
-	add_to_msg(p);
-	return;
-}
-
-static void set_author_ident_env(const char *message)
-{
-	const char *p = message;
-	if (!p)
-		die ("Could not read commit message of %s",
-				sha1_to_hex(commit->object.sha1));
-	while (*p && *p != '\n') {
-		const char *eol;
-
-		for (eol = p; *eol && *eol != '\n'; eol++)
-			; /* do nothing */
-		if (!prefixcmp(p, "author ")) {
-			char *line, *pend, *email, *timestamp;
-
-			p += 7;
-			line = xmemdupz(p, eol - p);
-			email = strchr(line, '<');
-			if (!email)
-				die ("Could not extract author email from %s",
-					sha1_to_hex(commit->object.sha1));
-			if (email == line)
-				pend = line;
-			else
-				for (pend = email; pend != line + 1 &&
-						isspace(pend[-1]); pend--);
-					; /* do nothing */
-			*pend = '\0';
-			email++;
-			timestamp = strchr(email, '>');
-			if (!timestamp)
-				die ("Could not extract author time from %s",
-					sha1_to_hex(commit->object.sha1));
-			*timestamp = '\0';
-			for (timestamp++; *timestamp && isspace(*timestamp);
-					timestamp++)
-				; /* do nothing */
-			setenv("GIT_AUTHOR_NAME", line, 1);
-			setenv("GIT_AUTHOR_EMAIL", email, 1);
-			setenv("GIT_AUTHOR_DATE", timestamp, 1);
-			free(line);
-			return;
-		}
-		p = eol;
-		if (*p == '\n')
-			p++;
-	}
-	die ("No author information found in %s",
-			sha1_to_hex(commit->object.sha1));
-}
-
-static char *help_msg(const unsigned char *sha1)
-{
-	static char helpbuf[1024];
-	char *msg = getenv("GIT_CHERRY_PICK_HELP");
-
-	if (msg)
-		return msg;
-
-	strcpy(helpbuf, "  After resolving the conflicts,\n"
-	       "mark the corrected paths with 'git add <paths>' "
-	       "or 'git rm <paths>' and commit the result.");
-
-	if (action == CHERRY_PICK) {
-		sprintf(helpbuf + strlen(helpbuf),
-			"\nWhen commiting, use the option "
-			"'-c %s' to retain authorship and message.",
-			find_unique_abbrev(sha1, DEFAULT_ABBREV));
-	}
-	return helpbuf;
-}
-
-static struct tree *empty_tree(void)
-{
-	struct tree *tree = xcalloc(1, sizeof(struct tree));
-
-	tree->object.parsed = 1;
-	tree->object.type = OBJ_TREE;
-	pretend_sha1_file(NULL, 0, OBJ_TREE, tree->object.sha1);
-	return tree;
-}
-
-static int revert_or_cherry_pick(int argc, const char **argv)
-{
-	unsigned char head[20];
-	struct commit *base, *next, *parent;
-	int i, index_fd, clean;
-	char *oneline, *reencoded_message = NULL;
-	const char *message, *encoding;
-	char *defmsg = git_pathdup("MERGE_MSG");
-	struct merge_options o;
-	struct tree *result, *next_tree, *base_tree, *head_tree;
-	static struct lock_file index_lock;
-
-	git_config(git_default_config, NULL);
-	me = action == REVERT ? "revert" : "cherry-pick";
-	setenv(GIT_REFLOG_ACTION, me, 0);
-	parse_args(argc, argv);
-
-	/* this is copied from the shell script, but it's never triggered... */
-	if (action == REVERT && !no_replay)
-		die("revert is incompatible with replay");
-
-	if (read_cache() < 0)
-		die("git %s: failed to read the index", me);
-	if (no_commit) {
-		/*
-		 * We do not intend to commit immediately.  We just want to
-		 * merge the differences in, so let's compute the tree
-		 * that represents the "current" state for merge-recursive
-		 * to work on.
-		 */
-		if (write_cache_as_tree(head, 0, NULL))
-			die ("Your index file is unmerged.");
-	} else {
-		if (get_sha1("HEAD", head))
-			die ("You do not have a valid HEAD");
-		if (index_differs_from("HEAD", 0))
-			die ("Dirty index: cannot %s", me);
-	}
-	discard_cache();
-
-	index_fd = hold_locked_index(&index_lock, 1);
-
-	if (!commit->parents) {
-		if (action == REVERT)
-			die ("Cannot revert a root commit");
-		parent = NULL;
-	}
-	else if (commit->parents->next) {
-		/* Reverting or cherry-picking a merge commit */
-		int cnt;
-		struct commit_list *p;
-
-		if (!mainline)
-			die("Commit %s is a merge but no -m option was given.",
-			    sha1_to_hex(commit->object.sha1));
-
-		for (cnt = 1, p = commit->parents;
-		     cnt != mainline && p;
-		     cnt++)
-			p = p->next;
-		if (cnt != mainline || !p)
-			die("Commit %s does not have parent %d",
-			    sha1_to_hex(commit->object.sha1), mainline);
-		parent = p->item;
-	} else if (0 < mainline)
-		die("Mainline was specified but commit %s is not a merge.",
-		    sha1_to_hex(commit->object.sha1));
-	else
-		parent = commit->parents->item;
-
-	if (!(message = commit->buffer))
-		die ("Cannot get commit message for %s",
-				sha1_to_hex(commit->object.sha1));
-
-	if (parent && parse_commit(parent) < 0)
-		die("%s: cannot parse parent commit %s",
-		    me, sha1_to_hex(parent->object.sha1));
-
-	/*
-	 * "commit" is an existing commit.  We would want to apply
-	 * the difference it introduces since its first parent "prev"
-	 * on top of the current HEAD if we are cherry-pick.  Or the
-	 * reverse of it if we are revert.
-	 */
-
-	msg_fd = hold_lock_file_for_update(&msg_file, defmsg,
-					   LOCK_DIE_ON_ERROR);
-
-	encoding = get_encoding(message);
-	if (!encoding)
-		encoding = "UTF-8";
-	if (!git_commit_encoding)
-		git_commit_encoding = "UTF-8";
-	if ((reencoded_message = reencode_string(message,
-					git_commit_encoding, encoding)))
-		message = reencoded_message;
-
-	oneline = get_oneline(message);
-
-	if (action == REVERT) {
-		char *oneline_body = strchr(oneline, ' ');
-
-		base = commit;
-		next = parent;
-		add_to_msg("Revert \"");
-		add_to_msg(oneline_body + 1);
-		add_to_msg("\"\n\nThis reverts commit ");
-		add_to_msg(sha1_to_hex(commit->object.sha1));
-
-		if (commit->parents->next) {
-			add_to_msg(", reversing\nchanges made to ");
-			add_to_msg(sha1_to_hex(parent->object.sha1));
-		}
-		add_to_msg(".\n");
-	} else {
-		base = parent;
-		next = commit;
-		set_author_ident_env(message);
-		add_message_to_msg(message);
-		if (no_replay) {
-			add_to_msg("(cherry picked from commit ");
-			add_to_msg(sha1_to_hex(commit->object.sha1));
-			add_to_msg(")\n");
-		}
-	}
-
-	read_cache();
-	init_merge_options(&o);
-	o.branch1 = "HEAD";
-	o.branch2 = oneline;
-
-	head_tree = parse_tree_indirect(head);
-	next_tree = next ? next->tree : empty_tree();
-	base_tree = base ? base->tree : empty_tree();
-
-	clean = merge_trees(&o,
-			    head_tree,
-			    next_tree, base_tree, &result);
-
-	if (active_cache_changed &&
-	    (write_cache(index_fd, active_cache, active_nr) ||
-	     commit_locked_index(&index_lock)))
-		die("%s: Unable to write new index file", me);
-	rollback_lock_file(&index_lock);
-
-	if (!clean) {
-		add_to_msg("\nConflicts:\n\n");
-		for (i = 0; i < active_nr;) {
-			struct cache_entry *ce = active_cache[i++];
-			if (ce_stage(ce)) {
-				add_to_msg("\t");
-				add_to_msg(ce->name);
-				add_to_msg("\n");
-				while (i < active_nr && !strcmp(ce->name,
-						active_cache[i]->name))
-					i++;
-			}
-		}
-		if (commit_lock_file(&msg_file) < 0)
-			die ("Error wrapping up %s", defmsg);
-		fprintf(stderr, "Automatic %s failed.%s\n",
-			me, help_msg(commit->object.sha1));
-		rerere();
-		exit(1);
-	}
-	if (commit_lock_file(&msg_file) < 0)
-		die ("Error wrapping up %s", defmsg);
-	fprintf(stderr, "Finished one %s.\n", me);
-
-	/*
-	 *
-	 * If we are cherry-pick, and if the merge did not result in
-	 * hand-editing, we will hit this commit and inherit the original
-	 * author date and name.
-	 * If we are revert, or if our cherry-pick results in a hand merge,
-	 * we had better say that the current user is responsible for that.
-	 */
-
-	if (!no_commit) {
-		/* 6 is max possible length of our args array including NULL */
-		const char *args[6];
-		int i = 0;
-		args[i++] = "commit";
-		args[i++] = "-n";
-		if (signoff)
-			args[i++] = "-s";
-		if (!edit) {
-			args[i++] = "-F";
-			args[i++] = defmsg;
-		}
-		args[i] = NULL;
-		return execv_git_cmd(args);
-	}
-	free(reencoded_message);
-	free(defmsg);
-
-	return 0;
-}
-
-int cmd_revert(int argc, const char **argv, const char *prefix)
-{
-	if (isatty(0))
-		edit = 1;
-	no_replay = 1;
-	action = REVERT;
-	return revert_or_cherry_pick(argc, argv);
-}
-
-int cmd_cherry_pick(int argc, const char **argv, const char *prefix)
-{
-	no_replay = 0;
-	action = CHERRY_PICK;
-	return revert_or_cherry_pick(argc, argv);
-}
diff --git a/builtin-rm.c b/builtin-rm.c
deleted file mode 100644
index 57975db..0000000
--- a/builtin-rm.c
+++ /dev/null
@@ -1,271 +0,0 @@
-/*
- * "git rm" builtin command
- *
- * Copyright (C) Linus Torvalds 2006
- */
-#include "cache.h"
-#include "builtin.h"
-#include "dir.h"
-#include "cache-tree.h"
-#include "tree-walk.h"
-#include "parse-options.h"
-
-static const char * const builtin_rm_usage[] = {
-	"git rm [options] [--] <file>...",
-	NULL
-};
-
-static struct {
-	int nr, alloc;
-	const char **name;
-} list;
-
-static void add_list(const char *name)
-{
-	if (list.nr >= list.alloc) {
-		list.alloc = alloc_nr(list.alloc);
-		list.name = xrealloc(list.name, list.alloc * sizeof(const char *));
-	}
-	list.name[list.nr++] = name;
-}
-
-static int check_local_mod(unsigned char *head, int index_only)
-{
-	/*
-	 * Items in list are already sorted in the cache order,
-	 * so we could do this a lot more efficiently by using
-	 * tree_desc based traversal if we wanted to, but I am
-	 * lazy, and who cares if removal of files is a tad
-	 * slower than the theoretical maximum speed?
-	 */
-	int i, no_head;
-	int errs = 0;
-
-	no_head = is_null_sha1(head);
-	for (i = 0; i < list.nr; i++) {
-		struct stat st;
-		int pos;
-		struct cache_entry *ce;
-		const char *name = list.name[i];
-		unsigned char sha1[20];
-		unsigned mode;
-		int local_changes = 0;
-		int staged_changes = 0;
-
-		pos = cache_name_pos(name, strlen(name));
-		if (pos < 0)
-			continue; /* removing unmerged entry */
-		ce = active_cache[pos];
-
-		if (lstat(ce->name, &st) < 0) {
-			if (errno != ENOENT)
-				warning("'%s': %s", ce->name, strerror(errno));
-			/* It already vanished from the working tree */
-			continue;
-		}
-		else if (S_ISDIR(st.st_mode)) {
-			/* if a file was removed and it is now a
-			 * directory, that is the same as ENOENT as
-			 * far as git is concerned; we do not track
-			 * directories.
-			 */
-			continue;
-		}
-
-		/*
-		 * "rm" of a path that has changes need to be treated
-		 * carefully not to allow losing local changes
-		 * accidentally.  A local change could be (1) file in
-		 * work tree is different since the index; and/or (2)
-		 * the user staged a content that is different from
-		 * the current commit in the index.
-		 *
-		 * In such a case, you would need to --force the
-		 * removal.  However, "rm --cached" (remove only from
-		 * the index) is safe if the index matches the file in
-		 * the work tree or the HEAD commit, as it means that
-		 * the content being removed is available elsewhere.
-		 */
-
-		/*
-		 * Is the index different from the file in the work tree?
-		 */
-		if (ce_match_stat(ce, &st, 0))
-			local_changes = 1;
-
-		/*
-		 * Is the index different from the HEAD commit?  By
-		 * definition, before the very initial commit,
-		 * anything staged in the index is treated by the same
-		 * way as changed from the HEAD.
-		 */
-		if (no_head
-		     || get_tree_entry(head, name, sha1, &mode)
-		     || ce->ce_mode != create_ce_mode(mode)
-		     || hashcmp(ce->sha1, sha1))
-			staged_changes = 1;
-
-		/*
-		 * If the index does not match the file in the work
-		 * tree and if it does not match the HEAD commit
-		 * either, (1) "git rm" without --cached definitely
-		 * will lose information; (2) "git rm --cached" will
-		 * lose information unless it is about removing an
-		 * "intent to add" entry.
-		 */
-		if (local_changes && staged_changes) {
-			if (!index_only || !(ce->ce_flags & CE_INTENT_TO_ADD))
-				errs = error("'%s' has staged content different "
-					     "from both the file and the HEAD\n"
-					     "(use -f to force removal)", name);
-		}
-		else if (!index_only) {
-			if (staged_changes)
-				errs = error("'%s' has changes staged in the index\n"
-					     "(use --cached to keep the file, "
-					     "or -f to force removal)", name);
-			if (local_changes)
-				errs = error("'%s' has local modifications\n"
-					     "(use --cached to keep the file, "
-					     "or -f to force removal)", name);
-		}
-	}
-	return errs;
-}
-
-static struct lock_file lock_file;
-
-static int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0;
-static int ignore_unmatch = 0;
-
-static struct option builtin_rm_options[] = {
-	OPT__DRY_RUN(&show_only),
-	OPT__QUIET(&quiet),
-	OPT_BOOLEAN( 0 , "cached",         &index_only, "only remove from the index"),
-	OPT_BOOLEAN('f', "force",          &force,      "override the up-to-date check"),
-	OPT_BOOLEAN('r', NULL,             &recursive,  "allow recursive removal"),
-	OPT_BOOLEAN( 0 , "ignore-unmatch", &ignore_unmatch,
-				"exit with a zero status even if nothing matched"),
-	OPT_END(),
-};
-
-int cmd_rm(int argc, const char **argv, const char *prefix)
-{
-	int i, newfd;
-	const char **pathspec;
-	char *seen;
-
-	git_config(git_default_config, NULL);
-
-	argc = parse_options(argc, argv, prefix, builtin_rm_options,
-			     builtin_rm_usage, 0);
-	if (!argc)
-		usage_with_options(builtin_rm_usage, builtin_rm_options);
-
-	if (!index_only)
-		setup_work_tree();
-
-	newfd = hold_locked_index(&lock_file, 1);
-
-	if (read_cache() < 0)
-		die("index file corrupt");
-	refresh_cache(REFRESH_QUIET);
-
-	pathspec = get_pathspec(prefix, argv);
-	seen = NULL;
-	for (i = 0; pathspec[i] ; i++)
-		/* nothing */;
-	seen = xcalloc(i, 1);
-
-	for (i = 0; i < active_nr; i++) {
-		struct cache_entry *ce = active_cache[i];
-		if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen))
-			continue;
-		add_list(ce->name);
-	}
-
-	if (pathspec) {
-		const char *match;
-		int seen_any = 0;
-		for (i = 0; (match = pathspec[i]) != NULL ; i++) {
-			if (!seen[i]) {
-				if (!ignore_unmatch) {
-					die("pathspec '%s' did not match any files",
-					    match);
-				}
-			}
-			else {
-				seen_any = 1;
-			}
-			if (!recursive && seen[i] == MATCHED_RECURSIVELY)
-				die("not removing '%s' recursively without -r",
-				    *match ? match : ".");
-		}
-
-		if (! seen_any)
-			exit(0);
-	}
-
-	/*
-	 * If not forced, the file, the index and the HEAD (if exists)
-	 * must match; but the file can already been removed, since
-	 * this sequence is a natural "novice" way:
-	 *
-	 *	rm F; git rm F
-	 *
-	 * Further, if HEAD commit exists, "diff-index --cached" must
-	 * report no changes unless forced.
-	 */
-	if (!force) {
-		unsigned char sha1[20];
-		if (get_sha1("HEAD", sha1))
-			hashclr(sha1);
-		if (check_local_mod(sha1, index_only))
-			exit(1);
-	}
-
-	/*
-	 * First remove the names from the index: we won't commit
-	 * the index unless all of them succeed.
-	 */
-	for (i = 0; i < list.nr; i++) {
-		const char *path = list.name[i];
-		if (!quiet)
-			printf("rm '%s'\n", path);
-
-		if (remove_file_from_cache(path))
-			die("git rm: unable to remove %s", path);
-	}
-
-	if (show_only)
-		return 0;
-
-	/*
-	 * Then, unless we used "--cached", remove the filenames from
-	 * the workspace. If we fail to remove the first one, we
-	 * abort the "git rm" (but once we've successfully removed
-	 * any file at all, we'll go ahead and commit to it all:
-	 * by then we've already committed ourselves and can't fail
-	 * in the middle)
-	 */
-	if (!index_only) {
-		int removed = 0;
-		for (i = 0; i < list.nr; i++) {
-			const char *path = list.name[i];
-			if (!remove_path(path)) {
-				removed = 1;
-				continue;
-			}
-			if (!removed)
-				die_errno("git rm: '%s'", path);
-		}
-	}
-
-	if (active_cache_changed) {
-		if (write_cache(newfd, active_cache, active_nr) ||
-		    commit_locked_index(&lock_file))
-			die("Unable to write new index file");
-	}
-
-	return 0;
-}
diff --git a/builtin-send-pack.c b/builtin-send-pack.c
deleted file mode 100644
index 47fb9f7..0000000
--- a/builtin-send-pack.c
+++ /dev/null
@@ -1,592 +0,0 @@
-#include "cache.h"
-#include "commit.h"
-#include "refs.h"
-#include "pkt-line.h"
-#include "run-command.h"
-#include "remote.h"
-#include "send-pack.h"
-
-static const char send_pack_usage[] =
-"git send-pack [--all | --mirror] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n"
-"  --all and explicit <ref> specification are mutually exclusive.";
-
-static struct send_pack_args args;
-
-static int feed_object(const unsigned char *sha1, int fd, int negative)
-{
-	char buf[42];
-
-	if (negative && !has_sha1_file(sha1))
-		return 1;
-
-	memcpy(buf + negative, sha1_to_hex(sha1), 40);
-	if (negative)
-		buf[0] = '^';
-	buf[40 + negative] = '\n';
-	return write_or_whine(fd, buf, 41 + negative, "send-pack: send refs");
-}
-
-/*
- * Make a pack stream and spit it out into file descriptor fd
- */
-static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *extra, struct send_pack_args *args)
-{
-	/*
-	 * The child becomes pack-objects --revs; we feed
-	 * the revision parameters to it via its stdin and
-	 * let its stdout go back to the other end.
-	 */
-	const char *argv[] = {
-		"pack-objects",
-		"--all-progress",
-		"--revs",
-		"--stdout",
-		NULL,
-		NULL,
-		NULL,
-	};
-	struct child_process po;
-	int i;
-
-	i = 4;
-	if (args->use_thin_pack)
-		argv[i++] = "--thin";
-	if (args->use_ofs_delta)
-		argv[i++] = "--delta-base-offset";
-	memset(&po, 0, sizeof(po));
-	po.argv = argv;
-	po.in = -1;
-	po.out = fd;
-	po.git_cmd = 1;
-	if (start_command(&po))
-		die_errno("git pack-objects failed");
-
-	/*
-	 * We feed the pack-objects we just spawned with revision
-	 * parameters by writing to the pipe.
-	 */
-	for (i = 0; i < extra->nr; i++)
-		if (!feed_object(extra->array[i], po.in, 1))
-			break;
-
-	while (refs) {
-		if (!is_null_sha1(refs->old_sha1) &&
-		    !feed_object(refs->old_sha1, po.in, 1))
-			break;
-		if (!is_null_sha1(refs->new_sha1) &&
-		    !feed_object(refs->new_sha1, po.in, 0))
-			break;
-		refs = refs->next;
-	}
-
-	close(po.in);
-	if (finish_command(&po))
-		return error("pack-objects died with strange error");
-	return 0;
-}
-
-static int receive_status(int in, struct ref *refs)
-{
-	struct ref *hint;
-	char line[1000];
-	int ret = 0;
-	int len = packet_read_line(in, line, sizeof(line));
-	if (len < 10 || memcmp(line, "unpack ", 7))
-		return error("did not receive remote status");
-	if (memcmp(line, "unpack ok\n", 10)) {
-		char *p = line + strlen(line) - 1;
-		if (*p == '\n')
-			*p = '\0';
-		error("unpack failed: %s", line + 7);
-		ret = -1;
-	}
-	hint = NULL;
-	while (1) {
-		char *refname;
-		char *msg;
-		len = packet_read_line(in, line, sizeof(line));
-		if (!len)
-			break;
-		if (len < 3 ||
-		    (memcmp(line, "ok ", 3) && memcmp(line, "ng ", 3))) {
-			fprintf(stderr, "protocol error: %s\n", line);
-			ret = -1;
-			break;
-		}
-
-		line[strlen(line)-1] = '\0';
-		refname = line + 3;
-		msg = strchr(refname, ' ');
-		if (msg)
-			*msg++ = '\0';
-
-		/* first try searching at our hint, falling back to all refs */
-		if (hint)
-			hint = find_ref_by_name(hint, refname);
-		if (!hint)
-			hint = find_ref_by_name(refs, refname);
-		if (!hint) {
-			warning("remote reported status on unknown ref: %s",
-					refname);
-			continue;
-		}
-		if (hint->status != REF_STATUS_EXPECTING_REPORT) {
-			warning("remote reported status on unexpected ref: %s",
-					refname);
-			continue;
-		}
-
-		if (line[0] == 'o' && line[1] == 'k')
-			hint->status = REF_STATUS_OK;
-		else {
-			hint->status = REF_STATUS_REMOTE_REJECT;
-			ret = -1;
-		}
-		if (msg)
-			hint->remote_status = xstrdup(msg);
-		/* start our next search from the next ref */
-		hint = hint->next;
-	}
-	return ret;
-}
-
-static void update_tracking_ref(struct remote *remote, struct ref *ref)
-{
-	struct refspec rs;
-
-	if (ref->status != REF_STATUS_OK && ref->status != REF_STATUS_UPTODATE)
-		return;
-
-	rs.src = ref->name;
-	rs.dst = NULL;
-
-	if (!remote_find_tracking(remote, &rs)) {
-		if (args.verbose)
-			fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst);
-		if (ref->deletion) {
-			delete_ref(rs.dst, NULL, 0);
-		} else
-			update_ref("update by push", rs.dst,
-					ref->new_sha1, NULL, 0, 0);
-		free(rs.dst);
-	}
-}
-
-#define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
-
-static void print_ref_status(char flag, const char *summary, struct ref *to, struct ref *from, const char *msg)
-{
-	fprintf(stderr, " %c %-*s ", flag, SUMMARY_WIDTH, summary);
-	if (from)
-		fprintf(stderr, "%s -> %s", prettify_refname(from->name), prettify_refname(to->name));
-	else
-		fputs(prettify_refname(to->name), stderr);
-	if (msg) {
-		fputs(" (", stderr);
-		fputs(msg, stderr);
-		fputc(')', stderr);
-	}
-	fputc('\n', stderr);
-}
-
-static const char *status_abbrev(unsigned char sha1[20])
-{
-	return find_unique_abbrev(sha1, DEFAULT_ABBREV);
-}
-
-static void print_ok_ref_status(struct ref *ref)
-{
-	if (ref->deletion)
-		print_ref_status('-', "[deleted]", ref, NULL, NULL);
-	else if (is_null_sha1(ref->old_sha1))
-		print_ref_status('*',
-			(!prefixcmp(ref->name, "refs/tags/") ? "[new tag]" :
-			  "[new branch]"),
-			ref, ref->peer_ref, NULL);
-	else {
-		char quickref[84];
-		char type;
-		const char *msg;
-
-		strcpy(quickref, status_abbrev(ref->old_sha1));
-		if (ref->nonfastforward) {
-			strcat(quickref, "...");
-			type = '+';
-			msg = "forced update";
-		} else {
-			strcat(quickref, "..");
-			type = ' ';
-			msg = NULL;
-		}
-		strcat(quickref, status_abbrev(ref->new_sha1));
-
-		print_ref_status(type, quickref, ref, ref->peer_ref, msg);
-	}
-}
-
-static int print_one_push_status(struct ref *ref, const char *dest, int count)
-{
-	if (!count)
-		fprintf(stderr, "To %s\n", dest);
-
-	switch(ref->status) {
-	case REF_STATUS_NONE:
-		print_ref_status('X', "[no match]", ref, NULL, NULL);
-		break;
-	case REF_STATUS_REJECT_NODELETE:
-		print_ref_status('!', "[rejected]", ref, NULL,
-				"remote does not support deleting refs");
-		break;
-	case REF_STATUS_UPTODATE:
-		print_ref_status('=', "[up to date]", ref,
-				ref->peer_ref, NULL);
-		break;
-	case REF_STATUS_REJECT_NONFASTFORWARD:
-		print_ref_status('!', "[rejected]", ref, ref->peer_ref,
-				"non-fast forward");
-		break;
-	case REF_STATUS_REMOTE_REJECT:
-		print_ref_status('!', "[remote rejected]", ref,
-				ref->deletion ? NULL : ref->peer_ref,
-				ref->remote_status);
-		break;
-	case REF_STATUS_EXPECTING_REPORT:
-		print_ref_status('!', "[remote failure]", ref,
-				ref->deletion ? NULL : ref->peer_ref,
-				"remote failed to report status");
-		break;
-	case REF_STATUS_OK:
-		print_ok_ref_status(ref);
-		break;
-	}
-
-	return 1;
-}
-
-static void print_push_status(const char *dest, struct ref *refs)
-{
-	struct ref *ref;
-	int n = 0;
-
-	if (args.verbose) {
-		for (ref = refs; ref; ref = ref->next)
-			if (ref->status == REF_STATUS_UPTODATE)
-				n += print_one_push_status(ref, dest, n);
-	}
-
-	for (ref = refs; ref; ref = ref->next)
-		if (ref->status == REF_STATUS_OK)
-			n += print_one_push_status(ref, dest, n);
-
-	for (ref = refs; ref; ref = ref->next) {
-		if (ref->status != REF_STATUS_NONE &&
-		    ref->status != REF_STATUS_UPTODATE &&
-		    ref->status != REF_STATUS_OK)
-			n += print_one_push_status(ref, dest, n);
-	}
-}
-
-static int refs_pushed(struct ref *ref)
-{
-	for (; ref; ref = ref->next) {
-		switch(ref->status) {
-		case REF_STATUS_NONE:
-		case REF_STATUS_UPTODATE:
-			break;
-		default:
-			return 1;
-		}
-	}
-	return 0;
-}
-
-int send_pack(struct send_pack_args *args,
-	      int fd[], struct child_process *conn,
-	      struct ref *remote_refs,
-	      struct extra_have_objects *extra_have)
-{
-	int in = fd[0];
-	int out = fd[1];
-	struct ref *ref;
-	int new_refs;
-	int ask_for_status_report = 0;
-	int allow_deleting_refs = 0;
-	int expect_status_report = 0;
-	int ret;
-
-	/* Does the other end support the reporting? */
-	if (server_supports("report-status"))
-		ask_for_status_report = 1;
-	if (server_supports("delete-refs"))
-		allow_deleting_refs = 1;
-	if (server_supports("ofs-delta"))
-		args->use_ofs_delta = 1;
-
-	if (!remote_refs) {
-		fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
-			"Perhaps you should specify a branch such as 'master'.\n");
-		return 0;
-	}
-
-	/*
-	 * Finally, tell the other end!
-	 */
-	new_refs = 0;
-	for (ref = remote_refs; ref; ref = ref->next) {
-
-		if (ref->peer_ref)
-			hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
-		else if (!args->send_mirror)
-			continue;
-
-		ref->deletion = is_null_sha1(ref->new_sha1);
-		if (ref->deletion && !allow_deleting_refs) {
-			ref->status = REF_STATUS_REJECT_NODELETE;
-			continue;
-		}
-		if (!ref->deletion &&
-		    !hashcmp(ref->old_sha1, ref->new_sha1)) {
-			ref->status = REF_STATUS_UPTODATE;
-			continue;
-		}
-
-		/* This part determines what can overwrite what.
-		 * The rules are:
-		 *
-		 * (0) you can always use --force or +A:B notation to
-		 *     selectively force individual ref pairs.
-		 *
-		 * (1) if the old thing does not exist, it is OK.
-		 *
-		 * (2) if you do not have the old thing, you are not allowed
-		 *     to overwrite it; you would not know what you are losing
-		 *     otherwise.
-		 *
-		 * (3) if both new and old are commit-ish, and new is a
-		 *     descendant of old, it is OK.
-		 *
-		 * (4) regardless of all of the above, removing :B is
-		 *     always allowed.
-		 */
-
-		ref->nonfastforward =
-		    !ref->deletion &&
-		    !is_null_sha1(ref->old_sha1) &&
-		    (!has_sha1_file(ref->old_sha1)
-		      || !ref_newer(ref->new_sha1, ref->old_sha1));
-
-		if (ref->nonfastforward && !ref->force && !args->force_update) {
-			ref->status = REF_STATUS_REJECT_NONFASTFORWARD;
-			continue;
-		}
-
-		if (!ref->deletion)
-			new_refs++;
-
-		if (!args->dry_run) {
-			char *old_hex = sha1_to_hex(ref->old_sha1);
-			char *new_hex = sha1_to_hex(ref->new_sha1);
-
-			if (ask_for_status_report) {
-				packet_write(out, "%s %s %s%c%s",
-					old_hex, new_hex, ref->name, 0,
-					"report-status");
-				ask_for_status_report = 0;
-				expect_status_report = 1;
-			}
-			else
-				packet_write(out, "%s %s %s",
-					old_hex, new_hex, ref->name);
-		}
-		ref->status = expect_status_report ?
-			REF_STATUS_EXPECTING_REPORT :
-			REF_STATUS_OK;
-	}
-
-	packet_flush(out);
-	if (new_refs && !args->dry_run) {
-		if (pack_objects(out, remote_refs, extra_have, args) < 0) {
-			for (ref = remote_refs; ref; ref = ref->next)
-				ref->status = REF_STATUS_NONE;
-			return -1;
-		}
-	}
-
-	if (expect_status_report)
-		ret = receive_status(in, remote_refs);
-	else
-		ret = 0;
-
-	if (ret < 0)
-		return ret;
-	for (ref = remote_refs; ref; ref = ref->next) {
-		switch (ref->status) {
-		case REF_STATUS_NONE:
-		case REF_STATUS_UPTODATE:
-		case REF_STATUS_OK:
-			break;
-		default:
-			return -1;
-		}
-	}
-	return 0;
-}
-
-static void verify_remote_names(int nr_heads, const char **heads)
-{
-	int i;
-
-	for (i = 0; i < nr_heads; i++) {
-		const char *local = heads[i];
-		const char *remote = strrchr(heads[i], ':');
-
-		if (*local == '+')
-			local++;
-
-		/* A matching refspec is okay.  */
-		if (remote == local && remote[1] == '\0')
-			continue;
-
-		remote = remote ? (remote + 1) : local;
-		switch (check_ref_format(remote)) {
-		case 0: /* ok */
-		case CHECK_REF_FORMAT_ONELEVEL:
-			/* ok but a single level -- that is fine for
-			 * a match pattern.
-			 */
-		case CHECK_REF_FORMAT_WILDCARD:
-			/* ok but ends with a pattern-match character */
-			continue;
-		}
-		die("remote part of refspec is not a valid name in %s",
-		    heads[i]);
-	}
-}
-
-int cmd_send_pack(int argc, const char **argv, const char *prefix)
-{
-	int i, nr_refspecs = 0;
-	const char **refspecs = NULL;
-	const char *remote_name = NULL;
-	struct remote *remote = NULL;
-	const char *dest = NULL;
-	int fd[2];
-	struct child_process *conn;
-	struct extra_have_objects extra_have;
-	struct ref *remote_refs, *local_refs;
-	int ret;
-	int send_all = 0;
-	const char *receivepack = "git-receive-pack";
-	int flags;
-
-	argv++;
-	for (i = 1; i < argc; i++, argv++) {
-		const char *arg = *argv;
-
-		if (*arg == '-') {
-			if (!prefixcmp(arg, "--receive-pack=")) {
-				receivepack = arg + 15;
-				continue;
-			}
-			if (!prefixcmp(arg, "--exec=")) {
-				receivepack = arg + 7;
-				continue;
-			}
-			if (!prefixcmp(arg, "--remote=")) {
-				remote_name = arg + 9;
-				continue;
-			}
-			if (!strcmp(arg, "--all")) {
-				send_all = 1;
-				continue;
-			}
-			if (!strcmp(arg, "--dry-run")) {
-				args.dry_run = 1;
-				continue;
-			}
-			if (!strcmp(arg, "--mirror")) {
-				args.send_mirror = 1;
-				continue;
-			}
-			if (!strcmp(arg, "--force")) {
-				args.force_update = 1;
-				continue;
-			}
-			if (!strcmp(arg, "--verbose")) {
-				args.verbose = 1;
-				continue;
-			}
-			if (!strcmp(arg, "--thin")) {
-				args.use_thin_pack = 1;
-				continue;
-			}
-			usage(send_pack_usage);
-		}
-		if (!dest) {
-			dest = arg;
-			continue;
-		}
-		refspecs = (const char **) argv;
-		nr_refspecs = argc - i;
-		break;
-	}
-	if (!dest)
-		usage(send_pack_usage);
-	/*
-	 * --all and --mirror are incompatible; neither makes sense
-	 * with any refspecs.
-	 */
-	if ((refspecs && (send_all || args.send_mirror)) ||
-	    (send_all && args.send_mirror))
-		usage(send_pack_usage);
-
-	if (remote_name) {
-		remote = remote_get(remote_name);
-		if (!remote_has_url(remote, dest)) {
-			die("Destination %s is not a uri for %s",
-			    dest, remote_name);
-		}
-	}
-
-	conn = git_connect(fd, dest, receivepack, args.verbose ? CONNECT_VERBOSE : 0);
-
-	memset(&extra_have, 0, sizeof(extra_have));
-
-	get_remote_heads(fd[0], &remote_refs, 0, NULL, REF_NORMAL,
-			 &extra_have);
-
-	verify_remote_names(nr_refspecs, refspecs);
-
-	local_refs = get_local_heads();
-
-	flags = MATCH_REFS_NONE;
-
-	if (send_all)
-		flags |= MATCH_REFS_ALL;
-	if (args.send_mirror)
-		flags |= MATCH_REFS_MIRROR;
-
-	/* match them up */
-	if (match_refs(local_refs, &remote_refs, nr_refspecs, refspecs, flags))
-		return -1;
-
-	ret = send_pack(&args, fd, conn, remote_refs, &extra_have);
-
-	close(fd[1]);
-	close(fd[0]);
-
-	ret |= finish_connect(conn);
-
-	print_push_status(dest, remote_refs);
-
-	if (!args.dry_run && remote) {
-		struct ref *ref;
-		for (ref = remote_refs; ref; ref = ref->next)
-			update_tracking_ref(remote, ref);
-	}
-
-	if (!ret && !refs_pushed(remote_refs))
-		fprintf(stderr, "Everything up-to-date\n");
-
-	return ret;
-}
diff --git a/builtin-shortlog.c b/builtin-shortlog.c
deleted file mode 100644
index 6a3812e..0000000
--- a/builtin-shortlog.c
+++ /dev/null
@@ -1,337 +0,0 @@
-#include "builtin.h"
-#include "cache.h"
-#include "commit.h"
-#include "diff.h"
-#include "string-list.h"
-#include "revision.h"
-#include "utf8.h"
-#include "mailmap.h"
-#include "shortlog.h"
-#include "parse-options.h"
-
-static char const * const shortlog_usage[] = {
-	"git shortlog [-n] [-s] [-e] [-w] [rev-opts] [--] [<commit-id>... ]",
-	"",
-	"[rev-opts] are documented in git-rev-list(1)",
-	NULL
-};
-
-static int compare_by_number(const void *a1, const void *a2)
-{
-	const struct string_list_item *i1 = a1, *i2 = a2;
-	const struct string_list *l1 = i1->util, *l2 = i2->util;
-
-	if (l1->nr < l2->nr)
-		return 1;
-	else if (l1->nr == l2->nr)
-		return 0;
-	else
-		return -1;
-}
-
-const char *format_subject(struct strbuf *sb, const char *msg,
-			   const char *line_separator);
-
-static void insert_one_record(struct shortlog *log,
-			      const char *author,
-			      const char *oneline)
-{
-	const char *dot3 = log->common_repo_prefix;
-	char *buffer, *p;
-	struct string_list_item *item;
-	char namebuf[1024];
-	char emailbuf[1024];
-	size_t len;
-	const char *eol;
-	const char *boemail, *eoemail;
-	struct strbuf subject = STRBUF_INIT;
-
-	boemail = strchr(author, '<');
-	if (!boemail)
-		return;
-	eoemail = strchr(boemail, '>');
-	if (!eoemail)
-		return;
-
-	/* copy author name to namebuf, to support matching on both name and email */
-	memcpy(namebuf, author, boemail - author);
-	len = boemail - author;
-	while(len > 0 && isspace(namebuf[len-1]))
-		len--;
-	namebuf[len] = 0;
-
-	/* copy email name to emailbuf, to allow email replacement as well */
-	memcpy(emailbuf, boemail+1, eoemail - boemail);
-	emailbuf[eoemail - boemail - 1] = 0;
-
-	if (!map_user(&log->mailmap, emailbuf, sizeof(emailbuf), namebuf, sizeof(namebuf))) {
-		while (author < boemail && isspace(*author))
-			author++;
-		for (len = 0;
-		     len < sizeof(namebuf) - 1 && author + len < boemail;
-		     len++)
-			namebuf[len] = author[len];
-		while (0 < len && isspace(namebuf[len-1]))
-			len--;
-		namebuf[len] = '\0';
-	}
-	else
-		len = strlen(namebuf);
-
-	if (log->email) {
-		size_t room = sizeof(namebuf) - len - 1;
-		int maillen = strlen(emailbuf);
-		snprintf(namebuf + len, room, " <%.*s>", maillen, emailbuf);
-	}
-
-	item = string_list_insert(namebuf, &log->list);
-	if (item->util == NULL)
-		item->util = xcalloc(1, sizeof(struct string_list));
-
-	/* Skip any leading whitespace, including any blank lines. */
-	while (*oneline && isspace(*oneline))
-		oneline++;
-	eol = strchr(oneline, '\n');
-	if (!eol)
-		eol = oneline + strlen(oneline);
-	if (!prefixcmp(oneline, "[PATCH")) {
-		char *eob = strchr(oneline, ']');
-		if (eob && (!eol || eob < eol))
-			oneline = eob + 1;
-	}
-	while (*oneline && isspace(*oneline) && *oneline != '\n')
-		oneline++;
-	format_subject(&subject, oneline, " ");
-	buffer = strbuf_detach(&subject, NULL);
-
-	if (dot3) {
-		int dot3len = strlen(dot3);
-		if (dot3len > 5) {
-			while ((p = strstr(buffer, dot3)) != NULL) {
-				int taillen = strlen(p) - dot3len;
-				memcpy(p, "/.../", 5);
-				memmove(p + 5, p + dot3len, taillen + 1);
-			}
-		}
-	}
-
-	string_list_append(buffer, item->util);
-}
-
-static void read_from_stdin(struct shortlog *log)
-{
-	char author[1024], oneline[1024];
-
-	while (fgets(author, sizeof(author), stdin) != NULL) {
-		if (!(author[0] == 'A' || author[0] == 'a') ||
-		    prefixcmp(author + 1, "uthor: "))
-			continue;
-		while (fgets(oneline, sizeof(oneline), stdin) &&
-		       oneline[0] != '\n')
-			; /* discard headers */
-		while (fgets(oneline, sizeof(oneline), stdin) &&
-		       oneline[0] == '\n')
-			; /* discard blanks */
-		insert_one_record(log, author + 8, oneline);
-	}
-}
-
-void shortlog_add_commit(struct shortlog *log, struct commit *commit)
-{
-	const char *author = NULL, *buffer;
-
-	buffer = commit->buffer;
-	while (*buffer && *buffer != '\n') {
-		const char *eol = strchr(buffer, '\n');
-
-		if (eol == NULL)
-			eol = buffer + strlen(buffer);
-		else
-			eol++;
-
-		if (!prefixcmp(buffer, "author "))
-			author = buffer + 7;
-		buffer = eol;
-	}
-	if (!author)
-		die("Missing author: %s",
-		    sha1_to_hex(commit->object.sha1));
-	if (log->user_format) {
-		struct strbuf buf = STRBUF_INIT;
-
-		pretty_print_commit(CMIT_FMT_USERFORMAT, commit, &buf,
-			DEFAULT_ABBREV, "", "", DATE_NORMAL, 0);
-		insert_one_record(log, author, buf.buf);
-		strbuf_release(&buf);
-		return;
-	}
-	if (*buffer)
-		buffer++;
-	insert_one_record(log, author, !*buffer ? "<none>" : buffer);
-}
-
-static void get_from_rev(struct rev_info *rev, struct shortlog *log)
-{
-	struct commit *commit;
-
-	if (prepare_revision_walk(rev))
-		die("revision walk setup failed");
-	while ((commit = get_revision(rev)) != NULL)
-		shortlog_add_commit(log, commit);
-}
-
-static int parse_uint(char const **arg, int comma, int defval)
-{
-	unsigned long ul;
-	int ret;
-	char *endp;
-
-	ul = strtoul(*arg, &endp, 10);
-	if (*endp && *endp != comma)
-		return -1;
-	if (ul > INT_MAX)
-		return -1;
-	ret = *arg == endp ? defval : (int)ul;
-	*arg = *endp ? endp + 1 : endp;
-	return ret;
-}
-
-static const char wrap_arg_usage[] = "-w[<width>[,<indent1>[,<indent2>]]]";
-#define DEFAULT_WRAPLEN 76
-#define DEFAULT_INDENT1 6
-#define DEFAULT_INDENT2 9
-
-static int parse_wrap_args(const struct option *opt, const char *arg, int unset)
-{
-	struct shortlog *log = opt->value;
-
-	log->wrap_lines = !unset;
-	if (unset)
-		return 0;
-	if (!arg) {
-		log->wrap = DEFAULT_WRAPLEN;
-		log->in1 = DEFAULT_INDENT1;
-		log->in2 = DEFAULT_INDENT2;
-		return 0;
-	}
-
-	log->wrap = parse_uint(&arg, ',', DEFAULT_WRAPLEN);
-	log->in1 = parse_uint(&arg, ',', DEFAULT_INDENT1);
-	log->in2 = parse_uint(&arg, '\0', DEFAULT_INDENT2);
-	if (log->wrap < 0 || log->in1 < 0 || log->in2 < 0)
-		return error(wrap_arg_usage);
-	if (log->wrap &&
-	    ((log->in1 && log->wrap <= log->in1) ||
-	     (log->in2 && log->wrap <= log->in2)))
-		return error(wrap_arg_usage);
-	return 0;
-}
-
-void shortlog_init(struct shortlog *log)
-{
-	memset(log, 0, sizeof(*log));
-
-	read_mailmap(&log->mailmap, &log->common_repo_prefix);
-
-	log->list.strdup_strings = 1;
-	log->wrap = DEFAULT_WRAPLEN;
-	log->in1 = DEFAULT_INDENT1;
-	log->in2 = DEFAULT_INDENT2;
-}
-
-int cmd_shortlog(int argc, const char **argv, const char *prefix)
-{
-	static struct shortlog log;
-	static struct rev_info rev;
-	int nongit;
-
-	static const struct option options[] = {
-		OPT_BOOLEAN('n', "numbered", &log.sort_by_number,
-			    "sort output according to the number of commits per author"),
-		OPT_BOOLEAN('s', "summary", &log.summary,
-			    "Suppress commit descriptions, only provides commit count"),
-		OPT_BOOLEAN('e', "email", &log.email,
-			    "Show the email address of each author"),
-		{ OPTION_CALLBACK, 'w', NULL, &log, "w[,i1[,i2]]",
-			"Linewrap output", PARSE_OPT_OPTARG, &parse_wrap_args },
-		OPT_END(),
-	};
-
-	struct parse_opt_ctx_t ctx;
-
-	prefix = setup_git_directory_gently(&nongit);
-	git_config(git_default_config, NULL);
-	shortlog_init(&log);
-	init_revisions(&rev, prefix);
-	parse_options_start(&ctx, argc, argv, prefix, PARSE_OPT_KEEP_DASHDASH |
-			    PARSE_OPT_KEEP_ARGV0);
-
-	for (;;) {
-		switch (parse_options_step(&ctx, options, shortlog_usage)) {
-		case PARSE_OPT_HELP:
-			exit(129);
-		case PARSE_OPT_DONE:
-			goto parse_done;
-		}
-		parse_revision_opt(&rev, &ctx, options, shortlog_usage);
-	}
-parse_done:
-	argc = parse_options_end(&ctx);
-
-	if (setup_revisions(argc, argv, &rev, NULL) != 1) {
-		error("unrecognized argument: %s", argv[1]);
-		usage_with_options(shortlog_usage, options);
-	}
-
-	log.user_format = rev.commit_format == CMIT_FMT_USERFORMAT;
-
-	/* assume HEAD if from a tty */
-	if (!nongit && !rev.pending.nr && isatty(0))
-		add_head_to_pending(&rev);
-	if (rev.pending.nr == 0) {
-		read_from_stdin(&log);
-	}
-	else
-		get_from_rev(&rev, &log);
-
-	shortlog_output(&log);
-	return 0;
-}
-
-void shortlog_output(struct shortlog *log)
-{
-	int i, j;
-	if (log->sort_by_number)
-		qsort(log->list.items, log->list.nr, sizeof(struct string_list_item),
-			compare_by_number);
-	for (i = 0; i < log->list.nr; i++) {
-		struct string_list *onelines = log->list.items[i].util;
-
-		if (log->summary) {
-			printf("%6d\t%s\n", onelines->nr, log->list.items[i].string);
-		} else {
-			printf("%s (%d):\n", log->list.items[i].string, onelines->nr);
-			for (j = onelines->nr - 1; j >= 0; j--) {
-				const char *msg = onelines->items[j].string;
-
-				if (log->wrap_lines) {
-					int col = print_wrapped_text(msg, log->in1, log->in2, log->wrap);
-					if (col != log->wrap)
-						putchar('\n');
-				}
-				else
-					printf("      %s\n", msg);
-			}
-			putchar('\n');
-		}
-
-		onelines->strdup_strings = 1;
-		string_list_clear(onelines, 0);
-		free(onelines);
-		log->list.items[i].util = NULL;
-	}
-
-	log->list.strdup_strings = 1;
-	string_list_clear(&log->list, 1);
-	clear_mailmap(&log->mailmap);
-}
diff --git a/builtin-show-branch.c b/builtin-show-branch.c
deleted file mode 100644
index 3510a86..0000000
--- a/builtin-show-branch.c
+++ /dev/null
@@ -1,959 +0,0 @@
-#include "cache.h"
-#include "commit.h"
-#include "refs.h"
-#include "builtin.h"
-#include "color.h"
-#include "parse-options.h"
-
-static const char* show_branch_usage[] = {
-    "git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order] [--current] [--color | --no-color] [--sparse] [--more=<n> | --list | --independent | --merge-base] [--no-name | --sha1-name] [--topics] [<rev> | <glob>]...",
-    "git show-branch (-g|--reflog)[=<n>[,<base>]] [--list] [<ref>]",
-    NULL
-};
-
-static int showbranch_use_color = -1;
-static char column_colors[][COLOR_MAXLEN] = {
-	GIT_COLOR_RED,
-	GIT_COLOR_GREEN,
-	GIT_COLOR_YELLOW,
-	GIT_COLOR_BLUE,
-	GIT_COLOR_MAGENTA,
-	GIT_COLOR_CYAN,
-};
-
-#define COLUMN_COLORS_MAX (ARRAY_SIZE(column_colors))
-
-static int default_num;
-static int default_alloc;
-static const char **default_arg;
-
-#define UNINTERESTING	01
-
-#define REV_SHIFT	 2
-#define MAX_REVS	(FLAG_BITS - REV_SHIFT) /* should not exceed bits_per_int - REV_SHIFT */
-
-#define DEFAULT_REFLOG	4
-
-static const char *get_color_code(int idx)
-{
-	if (showbranch_use_color)
-		return column_colors[idx];
-	return "";
-}
-
-static const char *get_color_reset_code(void)
-{
-	if (showbranch_use_color)
-		return GIT_COLOR_RESET;
-	return "";
-}
-
-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;
-}
-
-static struct commit *pop_one_commit(struct commit_list **list_p)
-{
-	struct commit *commit;
-	struct commit_list *list;
-	list = *list_p;
-	commit = list->item;
-	*list_p = list->next;
-	free(list);
-	return commit;
-}
-
-struct commit_name {
-	const char *head_name; /* which head's ancestor? */
-	int generation; /* how many parents away from head_name */
-};
-
-/* Name the commit as nth generation ancestor of head_name;
- * we count only the first-parent relationship for naming purposes.
- */
-static void name_commit(struct commit *commit, const char *head_name, int nth)
-{
-	struct commit_name *name;
-	if (!commit->util)
-		commit->util = xmalloc(sizeof(struct commit_name));
-	name = commit->util;
-	name->head_name = head_name;
-	name->generation = nth;
-}
-
-/* Parent is the first parent of the commit.  We may name it
- * as (n+1)th generation ancestor of the same head_name as
- * commit is nth generation ancestor of, if that generation
- * number is better than the name it already has.
- */
-static void name_parent(struct commit *commit, struct commit *parent)
-{
-	struct commit_name *commit_name = commit->util;
-	struct commit_name *parent_name = parent->util;
-	if (!commit_name)
-		return;
-	if (!parent_name ||
-	    commit_name->generation + 1 < parent_name->generation)
-		name_commit(parent, commit_name->head_name,
-			    commit_name->generation + 1);
-}
-
-static int name_first_parent_chain(struct commit *c)
-{
-	int i = 0;
-	while (c) {
-		struct commit *p;
-		if (!c->util)
-			break;
-		if (!c->parents)
-			break;
-		p = c->parents->item;
-		if (!p->util) {
-			name_parent(c, p);
-			i++;
-		}
-		else
-			break;
-		c = p;
-	}
-	return i;
-}
-
-static void name_commits(struct commit_list *list,
-			 struct commit **rev,
-			 char **ref_name,
-			 int num_rev)
-{
-	struct commit_list *cl;
-	struct commit *c;
-	int i;
-
-	/* First give names to the given heads */
-	for (cl = list; cl; cl = cl->next) {
-		c = cl->item;
-		if (c->util)
-			continue;
-		for (i = 0; i < num_rev; i++) {
-			if (rev[i] == c) {
-				name_commit(c, ref_name[i], 0);
-				break;
-			}
-		}
-	}
-
-	/* Then commits on the first parent ancestry chain */
-	do {
-		i = 0;
-		for (cl = list; cl; cl = cl->next) {
-			i += name_first_parent_chain(cl->item);
-		}
-	} while (i);
-
-	/* Finally, any unnamed commits */
-	do {
-		i = 0;
-		for (cl = list; cl; cl = cl->next) {
-			struct commit_list *parents;
-			struct commit_name *n;
-			int nth;
-			c = cl->item;
-			if (!c->util)
-				continue;
-			n = c->util;
-			parents = c->parents;
-			nth = 0;
-			while (parents) {
-				struct commit *p = parents->item;
-				char newname[1000], *en;
-				parents = parents->next;
-				nth++;
-				if (p->util)
-					continue;
-				en = newname;
-				switch (n->generation) {
-				case 0:
-					en += sprintf(en, "%s", n->head_name);
-					break;
-				case 1:
-					en += sprintf(en, "%s^", n->head_name);
-					break;
-				default:
-					en += sprintf(en, "%s~%d",
-						n->head_name, n->generation);
-					break;
-				}
-				if (nth == 1)
-					en += sprintf(en, "^");
-				else
-					en += sprintf(en, "^%d", nth);
-				name_commit(p, xstrdup(newname), 0);
-				i++;
-				name_first_parent_chain(p);
-			}
-		}
-	} while (i);
-}
-
-static int mark_seen(struct commit *commit, struct commit_list **seen_p)
-{
-	if (!commit->object.flags) {
-		commit_list_insert(commit, seen_p);
-		return 1;
-	}
-	return 0;
-}
-
-static void join_revs(struct commit_list **list_p,
-		      struct commit_list **seen_p,
-		      int num_rev, int extra)
-{
-	int all_mask = ((1u << (REV_SHIFT + num_rev)) - 1);
-	int all_revs = all_mask & ~((1u << REV_SHIFT) - 1);
-
-	while (*list_p) {
-		struct commit_list *parents;
-		int still_interesting = !!interesting(*list_p);
-		struct commit *commit = pop_one_commit(list_p);
-		int flags = commit->object.flags & all_mask;
-
-		if (!still_interesting && extra <= 0)
-			break;
-
-		mark_seen(commit, seen_p);
-		if ((flags & all_revs) == all_revs)
-			flags |= UNINTERESTING;
-		parents = commit->parents;
-
-		while (parents) {
-			struct commit *p = parents->item;
-			int this_flag = p->object.flags;
-			parents = parents->next;
-			if ((this_flag & flags) == flags)
-				continue;
-			if (!p->object.parsed)
-				parse_commit(p);
-			if (mark_seen(p, seen_p) && !still_interesting)
-				extra--;
-			p->object.flags |= flags;
-			insert_by_date(p, list_p);
-		}
-	}
-
-	/*
-	 * Postprocess to complete well-poisoning.
-	 *
-	 * At this point we have all the commits we have seen in
-	 * seen_p list.  Mark anything that can be reached from
-	 * uninteresting commits not interesting.
-	 */
-	for (;;) {
-		int changed = 0;
-		struct commit_list *s;
-		for (s = *seen_p; s; s = s->next) {
-			struct commit *c = s->item;
-			struct commit_list *parents;
-
-			if (((c->object.flags & all_revs) != all_revs) &&
-			    !(c->object.flags & UNINTERESTING))
-				continue;
-
-			/* The current commit is either a merge base or
-			 * already uninteresting one.  Mark its parents
-			 * as uninteresting commits _only_ if they are
-			 * already parsed.  No reason to find new ones
-			 * here.
-			 */
-			parents = c->parents;
-			while (parents) {
-				struct commit *p = parents->item;
-				parents = parents->next;
-				if (!(p->object.flags & UNINTERESTING)) {
-					p->object.flags |= UNINTERESTING;
-					changed = 1;
-				}
-			}
-		}
-		if (!changed)
-			break;
-	}
-}
-
-static void show_one_commit(struct commit *commit, int no_name)
-{
-	struct strbuf pretty = STRBUF_INIT;
-	const char *pretty_str = "(unavailable)";
-	struct commit_name *name = commit->util;
-
-	if (commit->object.parsed) {
-		pretty_print_commit(CMIT_FMT_ONELINE, commit,
-				    &pretty, 0, NULL, NULL, 0, 0);
-		pretty_str = pretty.buf;
-	}
-	if (!prefixcmp(pretty_str, "[PATCH] "))
-		pretty_str += 8;
-
-	if (!no_name) {
-		if (name && name->head_name) {
-			printf("[%s", name->head_name);
-			if (name->generation) {
-				if (name->generation == 1)
-					printf("^");
-				else
-					printf("~%d", name->generation);
-			}
-			printf("] ");
-		}
-		else
-			printf("[%s] ",
-			       find_unique_abbrev(commit->object.sha1, 7));
-	}
-	puts(pretty_str);
-	strbuf_release(&pretty);
-}
-
-static char *ref_name[MAX_REVS + 1];
-static int ref_name_cnt;
-
-static const char *find_digit_prefix(const char *s, int *v)
-{
-	const char *p;
-	int ver;
-	char ch;
-
-	for (p = s, ver = 0;
-	     '0' <= (ch = *p) && ch <= '9';
-	     p++)
-		ver = ver * 10 + ch - '0';
-	*v = ver;
-	return p;
-}
-
-
-static int version_cmp(const char *a, const char *b)
-{
-	while (1) {
-		int va, vb;
-
-		a = find_digit_prefix(a, &va);
-		b = find_digit_prefix(b, &vb);
-		if (va != vb)
-			return va - vb;
-
-		while (1) {
-			int ca = *a;
-			int cb = *b;
-			if ('0' <= ca && ca <= '9')
-				ca = 0;
-			if ('0' <= cb && cb <= '9')
-				cb = 0;
-			if (ca != cb)
-				return ca - cb;
-			if (!ca)
-				break;
-			a++;
-			b++;
-		}
-		if (!*a && !*b)
-			return 0;
-	}
-}
-
-static int compare_ref_name(const void *a_, const void *b_)
-{
-	const char * const*a = a_, * const*b = b_;
-	return version_cmp(*a, *b);
-}
-
-static void sort_ref_range(int bottom, int top)
-{
-	qsort(ref_name + bottom, top - bottom, sizeof(ref_name[0]),
-	      compare_ref_name);
-}
-
-static int append_ref(const char *refname, const unsigned char *sha1,
-		      int allow_dups)
-{
-	struct commit *commit = lookup_commit_reference_gently(sha1, 1);
-	int i;
-
-	if (!commit)
-		return 0;
-
-	if (!allow_dups) {
-		/* Avoid adding the same thing twice */
-		for (i = 0; i < ref_name_cnt; i++)
-			if (!strcmp(refname, ref_name[i]))
-				return 0;
-	}
-	if (MAX_REVS <= ref_name_cnt) {
-		warning("ignoring %s; cannot handle more than %d refs",
-			refname, MAX_REVS);
-		return 0;
-	}
-	ref_name[ref_name_cnt++] = xstrdup(refname);
-	ref_name[ref_name_cnt] = NULL;
-	return 0;
-}
-
-static int append_head_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
-{
-	unsigned char tmp[20];
-	int ofs = 11;
-	if (prefixcmp(refname, "refs/heads/"))
-		return 0;
-	/* If both heads/foo and tags/foo exists, get_sha1 would
-	 * get confused.
-	 */
-	if (get_sha1(refname + ofs, tmp) || hashcmp(tmp, sha1))
-		ofs = 5;
-	return append_ref(refname + ofs, sha1, 0);
-}
-
-static int append_remote_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
-{
-	unsigned char tmp[20];
-	int ofs = 13;
-	if (prefixcmp(refname, "refs/remotes/"))
-		return 0;
-	/* If both heads/foo and tags/foo exists, get_sha1 would
-	 * get confused.
-	 */
-	if (get_sha1(refname + ofs, tmp) || hashcmp(tmp, sha1))
-		ofs = 5;
-	return append_ref(refname + ofs, sha1, 0);
-}
-
-static int append_tag_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
-{
-	if (prefixcmp(refname, "refs/tags/"))
-		return 0;
-	return append_ref(refname + 5, sha1, 0);
-}
-
-static const char *match_ref_pattern = NULL;
-static int match_ref_slash = 0;
-static int count_slash(const char *s)
-{
-	int cnt = 0;
-	while (*s)
-		if (*s++ == '/')
-			cnt++;
-	return cnt;
-}
-
-static int append_matching_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
-{
-	/* we want to allow pattern hold/<asterisk> to show all
-	 * branches under refs/heads/hold/, and v0.99.9? to show
-	 * refs/tags/v0.99.9a and friends.
-	 */
-	const char *tail;
-	int slash = count_slash(refname);
-	for (tail = refname; *tail && match_ref_slash < slash; )
-		if (*tail++ == '/')
-			slash--;
-	if (!*tail)
-		return 0;
-	if (fnmatch(match_ref_pattern, tail, 0))
-		return 0;
-	if (!prefixcmp(refname, "refs/heads/"))
-		return append_head_ref(refname, sha1, flag, cb_data);
-	if (!prefixcmp(refname, "refs/tags/"))
-		return append_tag_ref(refname, sha1, flag, cb_data);
-	return append_ref(refname, sha1, 0);
-}
-
-static void snarf_refs(int head, int remotes)
-{
-	if (head) {
-		int orig_cnt = ref_name_cnt;
-		for_each_ref(append_head_ref, NULL);
-		sort_ref_range(orig_cnt, ref_name_cnt);
-	}
-	if (remotes) {
-		int orig_cnt = ref_name_cnt;
-		for_each_ref(append_remote_ref, NULL);
-		sort_ref_range(orig_cnt, ref_name_cnt);
-	}
-}
-
-static int rev_is_head(char *head, int headlen, char *name,
-		       unsigned char *head_sha1, unsigned char *sha1)
-{
-	if ((!head[0]) ||
-	    (head_sha1 && sha1 && hashcmp(head_sha1, sha1)))
-		return 0;
-	if (!prefixcmp(head, "refs/heads/"))
-		head += 11;
-	if (!prefixcmp(name, "refs/heads/"))
-		name += 11;
-	else if (!prefixcmp(name, "heads/"))
-		name += 6;
-	return !strcmp(head, name);
-}
-
-static int show_merge_base(struct commit_list *seen, int num_rev)
-{
-	int all_mask = ((1u << (REV_SHIFT + num_rev)) - 1);
-	int all_revs = all_mask & ~((1u << REV_SHIFT) - 1);
-	int exit_status = 1;
-
-	while (seen) {
-		struct commit *commit = pop_one_commit(&seen);
-		int flags = commit->object.flags & all_mask;
-		if (!(flags & UNINTERESTING) &&
-		    ((flags & all_revs) == all_revs)) {
-			puts(sha1_to_hex(commit->object.sha1));
-			exit_status = 0;
-			commit->object.flags |= UNINTERESTING;
-		}
-	}
-	return exit_status;
-}
-
-static int show_independent(struct commit **rev,
-			    int num_rev,
-			    char **ref_name,
-			    unsigned int *rev_mask)
-{
-	int i;
-
-	for (i = 0; i < num_rev; i++) {
-		struct commit *commit = rev[i];
-		unsigned int flag = rev_mask[i];
-
-		if (commit->object.flags == flag)
-			puts(sha1_to_hex(commit->object.sha1));
-		commit->object.flags |= UNINTERESTING;
-	}
-	return 0;
-}
-
-static void append_one_rev(const char *av)
-{
-	unsigned char revkey[20];
-	if (!get_sha1(av, revkey)) {
-		append_ref(av, revkey, 0);
-		return;
-	}
-	if (strchr(av, '*') || strchr(av, '?') || strchr(av, '[')) {
-		/* glob style match */
-		int saved_matches = ref_name_cnt;
-		match_ref_pattern = av;
-		match_ref_slash = count_slash(av);
-		for_each_ref(append_matching_ref, NULL);
-		if (saved_matches == ref_name_cnt &&
-		    ref_name_cnt < MAX_REVS)
-			error("no matching refs with %s", av);
-		if (saved_matches + 1 < ref_name_cnt)
-			sort_ref_range(saved_matches, ref_name_cnt);
-		return;
-	}
-	die("bad sha1 reference %s", av);
-}
-
-static int git_show_branch_config(const char *var, const char *value, void *cb)
-{
-	if (!strcmp(var, "showbranch.default")) {
-		if (!value)
-			return config_error_nonbool(var);
-		if (default_alloc <= default_num + 1) {
-			default_alloc = default_alloc * 3 / 2 + 20;
-			default_arg = xrealloc(default_arg, sizeof *default_arg * default_alloc);
-		}
-		default_arg[default_num++] = xstrdup(value);
-		default_arg[default_num] = NULL;
-		return 0;
-	}
-
-	if (!strcmp(var, "color.showbranch")) {
-		showbranch_use_color = git_config_colorbool(var, value, -1);
-		return 0;
-	}
-
-	return git_color_default_config(var, value, cb);
-}
-
-static int omit_in_dense(struct commit *commit, struct commit **rev, int n)
-{
-	/* If the commit is tip of the named branches, do not
-	 * omit it.
-	 * Otherwise, if it is a merge that is reachable from only one
-	 * tip, it is not that interesting.
-	 */
-	int i, flag, count;
-	for (i = 0; i < n; i++)
-		if (rev[i] == commit)
-			return 0;
-	flag = commit->object.flags;
-	for (i = count = 0; i < n; i++) {
-		if (flag & (1u << (i + REV_SHIFT)))
-			count++;
-	}
-	if (count == 1)
-		return 1;
-	return 0;
-}
-
-static int reflog = 0;
-
-static int parse_reflog_param(const struct option *opt, const char *arg,
-			      int unset)
-{
-	char *ep;
-	const char **base = (const char **)opt->value;
-	if (!arg)
-		arg = "";
-	reflog = strtoul(arg, &ep, 10);
-	if (*ep == ',')
-		*base = ep + 1;
-	else if (*ep)
-		return error("unrecognized reflog param '%s'", arg);
-	else
-		*base = NULL;
-	if (reflog <= 0)
-		reflog = DEFAULT_REFLOG;
-	return 0;
-}
-
-int cmd_show_branch(int ac, const char **av, const char *prefix)
-{
-	struct commit *rev[MAX_REVS], *commit;
-	char *reflog_msg[MAX_REVS];
-	struct commit_list *list = NULL, *seen = NULL;
-	unsigned int rev_mask[MAX_REVS];
-	int num_rev, i, extra = 0;
-	int all_heads = 0, all_remotes = 0;
-	int all_mask, all_revs;
-	int lifo = 1;
-	char head[128];
-	const char *head_p;
-	int head_len;
-	unsigned char head_sha1[20];
-	int merge_base = 0;
-	int independent = 0;
-	int no_name = 0;
-	int sha1_name = 0;
-	int shown_merge_point = 0;
-	int with_current_branch = 0;
-	int head_at = -1;
-	int topics = 0;
-	int dense = 1;
-	const char *reflog_base = NULL;
-	struct option builtin_show_branch_options[] = {
-		OPT_BOOLEAN('a', "all", &all_heads,
-			    "show remote-tracking and local branches"),
-		OPT_BOOLEAN('r', "remotes", &all_remotes,
-			    "show remote-tracking branches"),
-		OPT_BOOLEAN(0, "color", &showbranch_use_color,
-			    "color '*!+-' corresponding to the branch"),
-		{ OPTION_INTEGER, 0, "more", &extra, "n",
-			    "show <n> more commits after the common ancestor",
-			    PARSE_OPT_OPTARG, NULL, (intptr_t)1 },
-		OPT_SET_INT(0, "list", &extra, "synonym to more=-1", -1),
-		OPT_BOOLEAN(0, "no-name", &no_name, "suppress naming strings"),
-		OPT_BOOLEAN(0, "current", &with_current_branch,
-			    "include the current branch"),
-		OPT_BOOLEAN(0, "sha1-name", &sha1_name,
-			    "name commits with their object names"),
-		OPT_BOOLEAN(0, "merge-base", &merge_base,
-			    "show possible merge bases"),
-		OPT_BOOLEAN(0, "independent", &independent,
-			    "show refs unreachable from any other ref"),
-		OPT_BOOLEAN(0, "topo-order", &lifo,
-			    "show commits in topological order"),
-		OPT_BOOLEAN(0, "topics", &topics,
-			    "show only commits not on the first branch"),
-		OPT_SET_INT(0, "sparse", &dense,
-			    "show merges reachable from only one tip", 0),
-		OPT_SET_INT(0, "date-order", &lifo,
-			    "show commits where no parent comes before its "
-			    "children", 0),
-		{ OPTION_CALLBACK, 'g', "reflog", &reflog_base, "<n>[,<base>]",
-			    "show <n> most recent ref-log entries starting at "
-			    "base",
-			    PARSE_OPT_OPTARG | PARSE_OPT_LITERAL_ARGHELP,
-			    parse_reflog_param },
-		OPT_END()
-	};
-
-	git_config(git_show_branch_config, NULL);
-
-	if (showbranch_use_color == -1)
-		showbranch_use_color = git_use_color_default;
-
-	/* If nothing is specified, try the default first */
-	if (ac == 1 && default_num) {
-		ac = default_num + 1;
-		av = default_arg - 1; /* ick; we would not address av[0] */
-	}
-
-	ac = parse_options(ac, av, prefix, builtin_show_branch_options,
-			   show_branch_usage, PARSE_OPT_STOP_AT_NON_OPTION);
-	if (all_heads)
-		all_remotes = 1;
-
-	if (extra || reflog) {
-		/* "listing" mode is incompatible with
-		 * independent nor merge-base modes.
-		 */
-		if (independent || merge_base)
-			usage_with_options(show_branch_usage,
-					   builtin_show_branch_options);
-		if (reflog && ((0 < extra) || all_heads || all_remotes))
-			/*
-			 * Asking for --more in reflog mode does not
-			 * make sense.  --list is Ok.
-			 *
-			 * Also --all and --remotes do not make sense either.
-			 */
-			die("--reflog is incompatible with --all, --remotes, "
-			    "--independent or --merge-base");
-	}
-
-	/* If nothing is specified, show all branches by default */
-	if (ac + all_heads + all_remotes == 0)
-		all_heads = 1;
-
-	if (reflog) {
-		unsigned char sha1[20];
-		char nth_desc[256];
-		char *ref;
-		int base = 0;
-
-		if (ac == 0) {
-			static const char *fake_av[2];
-			const char *refname;
-
-			refname = resolve_ref("HEAD", sha1, 1, NULL);
-			fake_av[0] = xstrdup(refname);
-			fake_av[1] = NULL;
-			av = fake_av;
-			ac = 1;
-		}
-		if (ac != 1)
-			die("--reflog option needs one branch name");
-
-		if (MAX_REVS < reflog)
-			die("Only %d entries can be shown at one time.",
-			    MAX_REVS);
-		if (!dwim_ref(*av, strlen(*av), sha1, &ref))
-			die("No such ref %s", *av);
-
-		/* Has the base been specified? */
-		if (reflog_base) {
-			char *ep;
-			base = strtoul(reflog_base, &ep, 10);
-			if (*ep) {
-				/* Ah, that is a date spec... */
-				unsigned long at;
-				at = approxidate(reflog_base);
-				read_ref_at(ref, at, -1, sha1, NULL,
-					    NULL, NULL, &base);
-			}
-		}
-
-		for (i = 0; i < reflog; i++) {
-			char *logmsg, *m;
-			const char *msg;
-			unsigned long timestamp;
-			int tz;
-
-			if (read_ref_at(ref, 0, base+i, sha1, &logmsg,
-					&timestamp, &tz, NULL)) {
-				reflog = i;
-				break;
-			}
-			msg = strchr(logmsg, '\t');
-			if (!msg)
-				msg = "(none)";
-			else
-				msg++;
-			m = xmalloc(strlen(msg) + 200);
-			sprintf(m, "(%s) %s",
-				show_date(timestamp, tz, 1),
-				msg);
-			reflog_msg[i] = m;
-			free(logmsg);
-			sprintf(nth_desc, "%s@{%d}", *av, base+i);
-			append_ref(nth_desc, sha1, 1);
-		}
-	}
-	else if (all_heads + all_remotes)
-		snarf_refs(all_heads, all_remotes);
-	else {
-		while (0 < ac) {
-			append_one_rev(*av);
-			ac--; av++;
-		}
-	}
-
-	head_p = resolve_ref("HEAD", head_sha1, 1, NULL);
-	if (head_p) {
-		head_len = strlen(head_p);
-		memcpy(head, head_p, head_len + 1);
-	}
-	else {
-		head_len = 0;
-		head[0] = 0;
-	}
-
-	if (with_current_branch && head_p) {
-		int has_head = 0;
-		for (i = 0; !has_head && i < ref_name_cnt; i++) {
-			/* We are only interested in adding the branch
-			 * HEAD points at.
-			 */
-			if (rev_is_head(head,
-					head_len,
-					ref_name[i],
-					head_sha1, NULL))
-				has_head++;
-		}
-		if (!has_head) {
-			int offset = !prefixcmp(head, "refs/heads/") ? 11 : 0;
-			append_one_rev(head + offset);
-		}
-	}
-
-	if (!ref_name_cnt) {
-		fprintf(stderr, "No revs to be shown.\n");
-		exit(0);
-	}
-
-	for (num_rev = 0; ref_name[num_rev]; num_rev++) {
-		unsigned char revkey[20];
-		unsigned int flag = 1u << (num_rev + REV_SHIFT);
-
-		if (MAX_REVS <= num_rev)
-			die("cannot handle more than %d revs.", MAX_REVS);
-		if (get_sha1(ref_name[num_rev], revkey))
-			die("'%s' is not a valid ref.", ref_name[num_rev]);
-		commit = lookup_commit_reference(revkey);
-		if (!commit)
-			die("cannot find commit %s (%s)",
-			    ref_name[num_rev], revkey);
-		parse_commit(commit);
-		mark_seen(commit, &seen);
-
-		/* rev#0 uses bit REV_SHIFT, rev#1 uses bit REV_SHIFT+1,
-		 * and so on.  REV_SHIFT bits from bit 0 are used for
-		 * internal bookkeeping.
-		 */
-		commit->object.flags |= flag;
-		if (commit->object.flags == flag)
-			insert_by_date(commit, &list);
-		rev[num_rev] = commit;
-	}
-	for (i = 0; i < num_rev; i++)
-		rev_mask[i] = rev[i]->object.flags;
-
-	if (0 <= extra)
-		join_revs(&list, &seen, num_rev, extra);
-
-	sort_by_date(&seen);
-
-	if (merge_base)
-		return show_merge_base(seen, num_rev);
-
-	if (independent)
-		return show_independent(rev, num_rev, ref_name, rev_mask);
-
-	/* Show list; --more=-1 means list-only */
-	if (1 < num_rev || extra < 0) {
-		for (i = 0; i < num_rev; i++) {
-			int j;
-			int is_head = rev_is_head(head,
-						  head_len,
-						  ref_name[i],
-						  head_sha1,
-						  rev[i]->object.sha1);
-			if (extra < 0)
-				printf("%c [%s] ",
-				       is_head ? '*' : ' ', ref_name[i]);
-			else {
-				for (j = 0; j < i; j++)
-					putchar(' ');
-				printf("%s%c%s [%s] ",
-				       get_color_code(i % COLUMN_COLORS_MAX),
-				       is_head ? '*' : '!',
-				       get_color_reset_code(), ref_name[i]);
-			}
-
-			if (!reflog) {
-				/* header lines never need name */
-				show_one_commit(rev[i], 1);
-			}
-			else
-				puts(reflog_msg[i]);
-
-			if (is_head)
-				head_at = i;
-		}
-		if (0 <= extra) {
-			for (i = 0; i < num_rev; i++)
-				putchar('-');
-			putchar('\n');
-		}
-	}
-	if (extra < 0)
-		exit(0);
-
-	/* Sort topologically */
-	sort_in_topological_order(&seen, lifo);
-
-	/* Give names to commits */
-	if (!sha1_name && !no_name)
-		name_commits(seen, rev, ref_name, num_rev);
-
-	all_mask = ((1u << (REV_SHIFT + num_rev)) - 1);
-	all_revs = all_mask & ~((1u << REV_SHIFT) - 1);
-
-	while (seen) {
-		struct commit *commit = pop_one_commit(&seen);
-		int this_flag = commit->object.flags;
-		int is_merge_point = ((this_flag & all_revs) == all_revs);
-
-		shown_merge_point |= is_merge_point;
-
-		if (1 < num_rev) {
-			int is_merge = !!(commit->parents &&
-					  commit->parents->next);
-			if (topics &&
-			    !is_merge_point &&
-			    (this_flag & (1u << REV_SHIFT)))
-				continue;
-			if (dense && is_merge &&
-			    omit_in_dense(commit, rev, num_rev))
-				continue;
-			for (i = 0; i < num_rev; i++) {
-				int mark;
-				if (!(this_flag & (1u << (i + REV_SHIFT))))
-					mark = ' ';
-				else if (is_merge)
-					mark = '-';
-				else if (i == head_at)
-					mark = '*';
-				else
-					mark = '+';
-				printf("%s%c%s",
-				       get_color_code(i % COLUMN_COLORS_MAX),
-				       mark, get_color_reset_code());
-			}
-			putchar(' ');
-		}
-		show_one_commit(commit, no_name);
-
-		if (shown_merge_point && --extra < 0)
-			break;
-	}
-	return 0;
-}
diff --git a/builtin-show-ref.c b/builtin-show-ref.c
deleted file mode 100644
index c46550c..0000000
--- a/builtin-show-ref.c
+++ /dev/null
@@ -1,243 +0,0 @@
-#include "builtin.h"
-#include "cache.h"
-#include "refs.h"
-#include "object.h"
-#include "tag.h"
-#include "string-list.h"
-#include "parse-options.h"
-
-static const char * const show_ref_usage[] = {
-	"git show-ref [-q|--quiet] [--verify] [-h|--head] [-d|--dereference] [-s|--hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [pattern*] ",
-	"git show-ref --exclude-existing[=pattern] < ref-list",
-	NULL
-};
-
-static int deref_tags, show_head, tags_only, heads_only, found_match, verify,
-	   quiet, hash_only, abbrev, exclude_arg;
-static const char **pattern;
-static const char *exclude_existing_arg;
-
-static void show_one(const char *refname, const unsigned char *sha1)
-{
-	const char *hex = find_unique_abbrev(sha1, abbrev);
-	if (hash_only)
-		printf("%s\n", hex);
-	else
-		printf("%s %s\n", hex, refname);
-}
-
-static int show_ref(const char *refname, const unsigned char *sha1, int flag, void *cbdata)
-{
-	struct object *obj;
-	const char *hex;
-	unsigned char peeled[20];
-
-	if (tags_only || heads_only) {
-		int match;
-
-		match = heads_only && !prefixcmp(refname, "refs/heads/");
-		match |= tags_only && !prefixcmp(refname, "refs/tags/");
-		if (!match)
-			return 0;
-	}
-	if (pattern) {
-		int reflen = strlen(refname);
-		const char **p = pattern, *m;
-		while ((m = *p++) != NULL) {
-			int len = strlen(m);
-			if (len > reflen)
-				continue;
-			if (memcmp(m, refname + reflen - len, len))
-				continue;
-			if (len == reflen)
-				goto match;
-			/* "--verify" requires an exact match */
-			if (verify)
-				continue;
-			if (refname[reflen - len - 1] == '/')
-				goto match;
-		}
-		return 0;
-	}
-
-match:
-	found_match++;
-
-	/* This changes the semantics slightly that even under quiet we
-	 * detect and return error if the repository is corrupt and
-	 * ref points at a nonexistent object.
-	 */
-	if (!has_sha1_file(sha1))
-		die("git show-ref: bad ref %s (%s)", refname,
-		    sha1_to_hex(sha1));
-
-	if (quiet)
-		return 0;
-
-	show_one(refname, sha1);
-
-	if (!deref_tags)
-		return 0;
-
-	if ((flag & REF_ISPACKED) && !peel_ref(refname, peeled)) {
-		if (!is_null_sha1(peeled)) {
-			hex = find_unique_abbrev(peeled, abbrev);
-			printf("%s %s^{}\n", hex, refname);
-		}
-	}
-	else {
-		obj = parse_object(sha1);
-		if (!obj)
-			die("git show-ref: bad ref %s (%s)", refname,
-			    sha1_to_hex(sha1));
-		if (obj->type == OBJ_TAG) {
-			obj = deref_tag(obj, refname, 0);
-			if (!obj)
-				die("git show-ref: bad tag at ref %s (%s)", refname,
-				    sha1_to_hex(sha1));
-			hex = find_unique_abbrev(obj->sha1, abbrev);
-			printf("%s %s^{}\n", hex, refname);
-		}
-	}
-	return 0;
-}
-
-static int add_existing(const char *refname, const unsigned char *sha1, int flag, void *cbdata)
-{
-	struct string_list *list = (struct string_list *)cbdata;
-	string_list_insert(refname, list);
-	return 0;
-}
-
-/*
- * read "^(?:<anything>\s)?<refname>(?:\^\{\})?$" from the standard input,
- * and
- * (1) strip "^{}" at the end of line if any;
- * (2) ignore if match is provided and does not head-match refname;
- * (3) warn if refname is not a well-formed refname and skip;
- * (4) ignore if refname is a ref that exists in the local repository;
- * (5) otherwise output the line.
- */
-static int exclude_existing(const char *match)
-{
-	static struct string_list existing_refs = { NULL, 0, 0, 0 };
-	char buf[1024];
-	int matchlen = match ? strlen(match) : 0;
-
-	for_each_ref(add_existing, &existing_refs);
-	while (fgets(buf, sizeof(buf), stdin)) {
-		char *ref;
-		int len = strlen(buf);
-
-		if (len > 0 && buf[len - 1] == '\n')
-			buf[--len] = '\0';
-		if (3 <= len && !strcmp(buf + len - 3, "^{}")) {
-			len -= 3;
-			buf[len] = '\0';
-		}
-		for (ref = buf + len; buf < ref; ref--)
-			if (isspace(ref[-1]))
-				break;
-		if (match) {
-			int reflen = buf + len - ref;
-			if (reflen < matchlen)
-				continue;
-			if (strncmp(ref, match, matchlen))
-				continue;
-		}
-		if (check_ref_format(ref)) {
-			warning("ref '%s' ignored", ref);
-			continue;
-		}
-		if (!string_list_has_string(&existing_refs, ref)) {
-			printf("%s\n", buf);
-		}
-	}
-	return 0;
-}
-
-static int hash_callback(const struct option *opt, const char *arg, int unset)
-{
-	hash_only = 1;
-	/* Use full length SHA1 if no argument */
-	if (!arg)
-		return 0;
-	return parse_opt_abbrev_cb(opt, arg, unset);
-}
-
-static int exclude_existing_callback(const struct option *opt, const char *arg,
-				     int unset)
-{
-	exclude_arg = 1;
-	*(const char **)opt->value = arg;
-	return 0;
-}
-
-static int help_callback(const struct option *opt, const char *arg, int unset)
-{
-	return -1;
-}
-
-static const struct option show_ref_options[] = {
-	OPT_BOOLEAN(0, "tags", &tags_only, "only show tags (can be combined with heads)"),
-	OPT_BOOLEAN(0, "heads", &heads_only, "only show heads (can be combined with tags)"),
-	OPT_BOOLEAN(0, "verify", &verify, "stricter reference checking, "
-		    "requires exact ref path"),
-	OPT_BOOLEAN('h', "head", &show_head, "show the HEAD reference"),
-	OPT_BOOLEAN('d', "dereference", &deref_tags,
-		    "dereference tags into object IDs"),
-	{ OPTION_CALLBACK, 's', "hash", &abbrev, "n",
-	  "only show SHA1 hash using <n> digits",
-	  PARSE_OPT_OPTARG, &hash_callback },
-	OPT__ABBREV(&abbrev),
-	OPT__QUIET(&quiet),
-	{ OPTION_CALLBACK, 0, "exclude-existing", &exclude_existing_arg,
-	  "pattern", "show refs from stdin that aren't in local repository",
-	  PARSE_OPT_OPTARG | PARSE_OPT_NONEG, exclude_existing_callback },
-	{ OPTION_CALLBACK, 0, "help-all", NULL, NULL, "show usage",
-	  PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, help_callback },
-	OPT_END()
-};
-
-int cmd_show_ref(int argc, const char **argv, const char *prefix)
-{
-	argc = parse_options(argc, argv, prefix, show_ref_options,
-			     show_ref_usage, PARSE_OPT_NO_INTERNAL_HELP);
-
-	if (exclude_arg)
-		return exclude_existing(exclude_existing_arg);
-
-	pattern = argv;
-	if (!*pattern)
-		pattern = NULL;
-
-	if (verify) {
-		if (!pattern)
-			die("--verify requires a reference");
-		while (*pattern) {
-			unsigned char sha1[20];
-
-			if (!prefixcmp(*pattern, "refs/") &&
-			    resolve_ref(*pattern, sha1, 1, NULL)) {
-				if (!quiet)
-					show_one(*pattern, sha1);
-			}
-			else if (!quiet)
-				die("'%s' - not a valid ref", *pattern);
-			else
-				return 1;
-			pattern++;
-		}
-		return 0;
-	}
-
-	if (show_head)
-		head_ref(show_ref, NULL);
-	for_each_ref(show_ref, NULL);
-	if (!found_match) {
-		if (verify && !quiet)
-			die("No match");
-		return 1;
-	}
-	return 0;
-}
diff --git a/builtin-stripspace.c b/builtin-stripspace.c
deleted file mode 100644
index 1fd2205..0000000
--- a/builtin-stripspace.c
+++ /dev/null
@@ -1,88 +0,0 @@
-#include "builtin.h"
-#include "cache.h"
-
-/*
- * Returns the length of a line, without trailing spaces.
- *
- * If the line ends with newline, it will be removed too.
- */
-static size_t cleanup(char *line, size_t len)
-{
-	while (len) {
-		unsigned char c = line[len - 1];
-		if (!isspace(c))
-			break;
-		len--;
-	}
-
-	return len;
-}
-
-/*
- * Remove empty lines from the beginning and end
- * and also trailing spaces from every line.
- *
- * Note that the buffer will not be NUL-terminated.
- *
- * Turn multiple consecutive empty lines between paragraphs
- * into just one empty line.
- *
- * If the input has only empty lines and spaces,
- * no output will be produced.
- *
- * If last line does not have a newline at the end, one is added.
- *
- * Enable skip_comments to skip every line starting with "#".
- */
-void stripspace(struct strbuf *sb, int skip_comments)
-{
-	int empties = 0;
-	size_t i, j, len, newlen;
-	char *eol;
-
-	/* We may have to add a newline. */
-	strbuf_grow(sb, 1);
-
-	for (i = j = 0; i < sb->len; i += len, j += newlen) {
-		eol = memchr(sb->buf + i, '\n', sb->len - i);
-		len = eol ? eol - (sb->buf + i) + 1 : sb->len - i;
-
-		if (skip_comments && len && sb->buf[i] == '#') {
-			newlen = 0;
-			continue;
-		}
-		newlen = cleanup(sb->buf + i, len);
-
-		/* Not just an empty line? */
-		if (newlen) {
-			if (empties > 0 && j > 0)
-				sb->buf[j++] = '\n';
-			empties = 0;
-			memmove(sb->buf + j, sb->buf + i, newlen);
-			sb->buf[newlen + j++] = '\n';
-		} else {
-			empties++;
-		}
-	}
-
-	strbuf_setlen(sb, j);
-}
-
-int cmd_stripspace(int argc, const char **argv, const char *prefix)
-{
-	struct strbuf buf = STRBUF_INIT;
-	int strip_comments = 0;
-
-	if (argc > 1 && (!strcmp(argv[1], "-s") ||
-				!strcmp(argv[1], "--strip-comments")))
-		strip_comments = 1;
-
-	if (strbuf_read(&buf, 0, 1024) < 0)
-		die_errno("could not read the input");
-
-	stripspace(&buf, strip_comments);
-
-	write_or_die(1, buf.buf, buf.len);
-	strbuf_release(&buf);
-	return 0;
-}
diff --git a/builtin-symbolic-ref.c b/builtin-symbolic-ref.c
deleted file mode 100644
index ca855a5..0000000
--- a/builtin-symbolic-ref.c
+++ /dev/null
@@ -1,57 +0,0 @@
-#include "builtin.h"
-#include "cache.h"
-#include "refs.h"
-#include "parse-options.h"
-
-static const char * const git_symbolic_ref_usage[] = {
-	"git symbolic-ref [options] name [ref]",
-	NULL
-};
-
-static void check_symref(const char *HEAD, int quiet)
-{
-	unsigned char sha1[20];
-	int flag;
-	const char *refs_heads_master = resolve_ref(HEAD, sha1, 0, &flag);
-
-	if (!refs_heads_master)
-		die("No such ref: %s", HEAD);
-	else if (!(flag & REF_ISSYMREF)) {
-		if (!quiet)
-			die("ref %s is not a symbolic ref", HEAD);
-		else
-			exit(1);
-	}
-	puts(refs_heads_master);
-}
-
-int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
-{
-	int quiet = 0;
-	const char *msg = NULL;
-	struct option options[] = {
-		OPT__QUIET(&quiet),
-		OPT_STRING('m', NULL, &msg, "reason", "reason of the update"),
-		OPT_END(),
-	};
-
-	git_config(git_default_config, NULL);
-	argc = parse_options(argc, argv, prefix, options,
-			     git_symbolic_ref_usage, 0);
-	if (msg &&!*msg)
-		die("Refusing to perform update with empty message");
-	switch (argc) {
-	case 1:
-		check_symref(argv[0], quiet);
-		break;
-	case 2:
-		if (!strcmp(argv[0], "HEAD") &&
-		    prefixcmp(argv[1], "refs/"))
-			die("Refusing to point HEAD outside of refs/");
-		create_symref(argv[0], argv[1], msg);
-		break;
-	default:
-		usage_with_options(git_symbolic_ref_usage, options);
-	}
-	return 0;
-}
diff --git a/builtin-tag.c b/builtin-tag.c
deleted file mode 100644
index a51a6d1..0000000
--- a/builtin-tag.c
+++ /dev/null
@@ -1,485 +0,0 @@
-/*
- * Builtin "git tag"
- *
- * Copyright (c) 2007 Kristian Høgsberg <krh@redhat.com>,
- *                    Carlos Rica <jasampler@gmail.com>
- * Based on git-tag.sh and mktag.c by Linus Torvalds.
- */
-
-#include "cache.h"
-#include "builtin.h"
-#include "refs.h"
-#include "tag.h"
-#include "run-command.h"
-#include "parse-options.h"
-
-static const char * const git_tag_usage[] = {
-	"git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]",
-	"git tag -d <tagname>...",
-	"git tag -l [-n[<num>]] [<pattern>]",
-	"git tag -v <tagname>...",
-	NULL
-};
-
-static char signingkey[1000];
-
-struct tag_filter {
-	const char *pattern;
-	int lines;
-	struct commit_list *with_commit;
-};
-
-#define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----"
-
-static int show_reference(const char *refname, const unsigned char *sha1,
-			  int flag, void *cb_data)
-{
-	struct tag_filter *filter = cb_data;
-
-	if (!fnmatch(filter->pattern, refname, 0)) {
-		int i;
-		unsigned long size;
-		enum object_type type;
-		char *buf, *sp, *eol;
-		size_t len;
-
-		if (filter->with_commit) {
-			struct commit *commit;
-
-			commit = lookup_commit_reference_gently(sha1, 1);
-			if (!commit)
-				return 0;
-			if (!is_descendant_of(commit, filter->with_commit))
-				return 0;
-		}
-
-		if (!filter->lines) {
-			printf("%s\n", refname);
-			return 0;
-		}
-		printf("%-15s ", refname);
-
-		buf = read_sha1_file(sha1, &type, &size);
-		if (!buf || !size)
-			return 0;
-
-		/* skip header */
-		sp = strstr(buf, "\n\n");
-		if (!sp) {
-			free(buf);
-			return 0;
-		}
-		/* only take up to "lines" lines, and strip the signature */
-		for (i = 0, sp += 2;
-				i < filter->lines && sp < buf + size &&
-				prefixcmp(sp, PGP_SIGNATURE "\n");
-				i++) {
-			if (i)
-				printf("\n    ");
-			eol = memchr(sp, '\n', size - (sp - buf));
-			len = eol ? eol - sp : size - (sp - buf);
-			fwrite(sp, len, 1, stdout);
-			if (!eol)
-				break;
-			sp = eol + 1;
-		}
-		putchar('\n');
-		free(buf);
-	}
-
-	return 0;
-}
-
-static int list_tags(const char *pattern, int lines,
-			struct commit_list *with_commit)
-{
-	struct tag_filter filter;
-
-	if (pattern == NULL)
-		pattern = "*";
-
-	filter.pattern = pattern;
-	filter.lines = lines;
-	filter.with_commit = with_commit;
-
-	for_each_tag_ref(show_reference, (void *) &filter);
-
-	return 0;
-}
-
-typedef int (*each_tag_name_fn)(const char *name, const char *ref,
-				const unsigned char *sha1);
-
-static int for_each_tag_name(const char **argv, each_tag_name_fn fn)
-{
-	const char **p;
-	char ref[PATH_MAX];
-	int had_error = 0;
-	unsigned char sha1[20];
-
-	for (p = argv; *p; p++) {
-		if (snprintf(ref, sizeof(ref), "refs/tags/%s", *p)
-					>= sizeof(ref)) {
-			error("tag name too long: %.*s...", 50, *p);
-			had_error = 1;
-			continue;
-		}
-		if (!resolve_ref(ref, sha1, 1, NULL)) {
-			error("tag '%s' not found.", *p);
-			had_error = 1;
-			continue;
-		}
-		if (fn(*p, ref, sha1))
-			had_error = 1;
-	}
-	return had_error;
-}
-
-static int delete_tag(const char *name, const char *ref,
-				const unsigned char *sha1)
-{
-	if (delete_ref(ref, sha1, 0))
-		return 1;
-	printf("Deleted tag '%s'\n", name);
-	return 0;
-}
-
-static int verify_tag(const char *name, const char *ref,
-				const unsigned char *sha1)
-{
-	const char *argv_verify_tag[] = {"git-verify-tag",
-					"-v", "SHA1_HEX", NULL};
-	argv_verify_tag[2] = sha1_to_hex(sha1);
-
-	if (run_command_v_opt(argv_verify_tag, 0))
-		return error("could not verify the tag '%s'", name);
-	return 0;
-}
-
-static int do_sign(struct strbuf *buffer)
-{
-	struct child_process gpg;
-	const char *args[4];
-	char *bracket;
-	int len;
-	int i, j;
-
-	if (!*signingkey) {
-		if (strlcpy(signingkey, git_committer_info(IDENT_ERROR_ON_NO_NAME),
-				sizeof(signingkey)) > sizeof(signingkey) - 1)
-			return error("committer info too long.");
-		bracket = strchr(signingkey, '>');
-		if (bracket)
-			bracket[1] = '\0';
-	}
-
-	/* When the username signingkey is bad, program could be terminated
-	 * because gpg exits without reading and then write gets SIGPIPE. */
-	signal(SIGPIPE, SIG_IGN);
-
-	memset(&gpg, 0, sizeof(gpg));
-	gpg.argv = args;
-	gpg.in = -1;
-	gpg.out = -1;
-	args[0] = "gpg";
-	args[1] = "-bsau";
-	args[2] = signingkey;
-	args[3] = NULL;
-
-	if (start_command(&gpg))
-		return error("could not run gpg.");
-
-	if (write_in_full(gpg.in, buffer->buf, buffer->len) != buffer->len) {
-		close(gpg.in);
-		close(gpg.out);
-		finish_command(&gpg);
-		return error("gpg did not accept the tag data");
-	}
-	close(gpg.in);
-	len = strbuf_read(buffer, gpg.out, 1024);
-	close(gpg.out);
-
-	if (finish_command(&gpg) || !len || len < 0)
-		return error("gpg failed to sign the tag");
-
-	/* Strip CR from the line endings, in case we are on Windows. */
-	for (i = j = 0; i < buffer->len; i++)
-		if (buffer->buf[i] != '\r') {
-			if (i != j)
-				buffer->buf[j] = buffer->buf[i];
-			j++;
-		}
-	strbuf_setlen(buffer, j);
-
-	return 0;
-}
-
-static const char tag_template[] =
-	"\n"
-	"#\n"
-	"# Write a tag message\n"
-	"#\n";
-
-static void set_signingkey(const char *value)
-{
-	if (strlcpy(signingkey, value, sizeof(signingkey)) >= sizeof(signingkey))
-		die("signing key value too long (%.10s...)", value);
-}
-
-static int git_tag_config(const char *var, const char *value, void *cb)
-{
-	if (!strcmp(var, "user.signingkey")) {
-		if (!value)
-			return config_error_nonbool(var);
-		set_signingkey(value);
-		return 0;
-	}
-
-	return git_default_config(var, value, cb);
-}
-
-static void write_tag_body(int fd, const unsigned char *sha1)
-{
-	unsigned long size;
-	enum object_type type;
-	char *buf, *sp, *eob;
-	size_t len;
-
-	buf = read_sha1_file(sha1, &type, &size);
-	if (!buf)
-		return;
-	/* skip header */
-	sp = strstr(buf, "\n\n");
-
-	if (!sp || !size || type != OBJ_TAG) {
-		free(buf);
-		return;
-	}
-	sp += 2; /* skip the 2 LFs */
-	eob = strstr(sp, "\n" PGP_SIGNATURE "\n");
-	if (eob)
-		len = eob - sp;
-	else
-		len = buf + size - sp;
-	write_or_die(fd, sp, len);
-
-	free(buf);
-}
-
-static int build_tag_object(struct strbuf *buf, int sign, unsigned char *result)
-{
-	if (sign && do_sign(buf) < 0)
-		return error("unable to sign the tag");
-	if (write_sha1_file(buf->buf, buf->len, tag_type, result) < 0)
-		return error("unable to write tag file");
-	return 0;
-}
-
-static void create_tag(const unsigned char *object, const char *tag,
-		       struct strbuf *buf, int message, int sign,
-		       unsigned char *prev, unsigned char *result)
-{
-	enum object_type type;
-	char header_buf[1024];
-	int header_len;
-	char *path = NULL;
-
-	type = sha1_object_info(object, NULL);
-	if (type <= OBJ_NONE)
-	    die("bad object type.");
-
-	header_len = snprintf(header_buf, sizeof(header_buf),
-			  "object %s\n"
-			  "type %s\n"
-			  "tag %s\n"
-			  "tagger %s\n\n",
-			  sha1_to_hex(object),
-			  typename(type),
-			  tag,
-			  git_committer_info(IDENT_ERROR_ON_NO_NAME));
-
-	if (header_len > sizeof(header_buf) - 1)
-		die("tag header too big.");
-
-	if (!message) {
-		int fd;
-
-		/* write the template message before editing: */
-		path = git_pathdup("TAG_EDITMSG");
-		fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
-		if (fd < 0)
-			die_errno("could not create file '%s'", path);
-
-		if (!is_null_sha1(prev))
-			write_tag_body(fd, prev);
-		else
-			write_or_die(fd, tag_template, strlen(tag_template));
-		close(fd);
-
-		if (launch_editor(path, buf, NULL)) {
-			fprintf(stderr,
-			"Please supply the message using either -m or -F option.\n");
-			exit(1);
-		}
-	}
-
-	stripspace(buf, 1);
-
-	if (!message && !buf->len)
-		die("no tag message?");
-
-	strbuf_insert(buf, 0, header_buf, header_len);
-
-	if (build_tag_object(buf, sign, result) < 0) {
-		if (path)
-			fprintf(stderr, "The tag message has been left in %s\n",
-				path);
-		exit(128);
-	}
-	if (path) {
-		unlink_or_warn(path);
-		free(path);
-	}
-}
-
-struct msg_arg {
-	int given;
-	struct strbuf buf;
-};
-
-static int parse_msg_arg(const struct option *opt, const char *arg, int unset)
-{
-	struct msg_arg *msg = opt->value;
-
-	if (!arg)
-		return -1;
-	if (msg->buf.len)
-		strbuf_addstr(&(msg->buf), "\n\n");
-	strbuf_addstr(&(msg->buf), arg);
-	msg->given = 1;
-	return 0;
-}
-
-int cmd_tag(int argc, const char **argv, const char *prefix)
-{
-	struct strbuf buf = STRBUF_INIT;
-	unsigned char object[20], prev[20];
-	char ref[PATH_MAX];
-	const char *object_ref, *tag;
-	struct ref_lock *lock;
-
-	int annotate = 0, sign = 0, force = 0, lines = -1,
-		list = 0, delete = 0, verify = 0;
-	const char *msgfile = NULL, *keyid = NULL;
-	struct msg_arg msg = { 0, STRBUF_INIT };
-	struct commit_list *with_commit = NULL;
-	struct option options[] = {
-		OPT_BOOLEAN('l', NULL, &list, "list tag names"),
-		{ OPTION_INTEGER, 'n', NULL, &lines, "n",
-				"print <n> lines of each tag message",
-				PARSE_OPT_OPTARG, NULL, 1 },
-		OPT_BOOLEAN('d', NULL, &delete, "delete tags"),
-		OPT_BOOLEAN('v', NULL, &verify, "verify tags"),
-
-		OPT_GROUP("Tag creation options"),
-		OPT_BOOLEAN('a', NULL, &annotate,
-					"annotated tag, needs a message"),
-		OPT_CALLBACK('m', NULL, &msg, "msg",
-			     "message for the tag", parse_msg_arg),
-		OPT_FILENAME('F', NULL, &msgfile, "message in a file"),
-		OPT_BOOLEAN('s', NULL, &sign, "annotated and GPG-signed tag"),
-		OPT_STRING('u', NULL, &keyid, "key-id",
-					"use another key to sign the tag"),
-		OPT_BOOLEAN('f', NULL, &force, "replace the tag if exists"),
-
-		OPT_GROUP("Tag listing options"),
-		{
-			OPTION_CALLBACK, 0, "contains", &with_commit, "commit",
-			"print only tags that contain the commit",
-			PARSE_OPT_LASTARG_DEFAULT,
-			parse_opt_with_commit, (intptr_t)"HEAD",
-		},
-		OPT_END()
-	};
-
-	git_config(git_tag_config, NULL);
-
-	argc = parse_options(argc, argv, prefix, options, git_tag_usage, 0);
-
-	if (keyid) {
-		sign = 1;
-		set_signingkey(keyid);
-	}
-	if (sign)
-		annotate = 1;
-	if (argc == 0 && !(delete || verify))
-		list = 1;
-
-	if ((annotate || msg.given || msgfile || force) &&
-	    (list || delete || verify))
-		usage_with_options(git_tag_usage, options);
-
-	if (list + delete + verify > 1)
-		usage_with_options(git_tag_usage, options);
-	if (list)
-		return list_tags(argv[0], lines == -1 ? 0 : lines,
-				 with_commit);
-	if (lines != -1)
-		die("-n option is only allowed with -l.");
-	if (with_commit)
-		die("--contains option is only allowed with -l.");
-	if (delete)
-		return for_each_tag_name(argv, delete_tag);
-	if (verify)
-		return for_each_tag_name(argv, verify_tag);
-
-	if (msg.given || msgfile) {
-		if (msg.given && msgfile)
-			die("only one -F or -m option is allowed.");
-		annotate = 1;
-		if (msg.given)
-			strbuf_addbuf(&buf, &(msg.buf));
-		else {
-			if (!strcmp(msgfile, "-")) {
-				if (strbuf_read(&buf, 0, 1024) < 0)
-					die_errno("cannot read '%s'", msgfile);
-			} else {
-				if (strbuf_read_file(&buf, msgfile, 1024) < 0)
-					die_errno("could not open or read '%s'",
-						msgfile);
-			}
-		}
-	}
-
-	tag = argv[0];
-
-	object_ref = argc == 2 ? argv[1] : "HEAD";
-	if (argc > 2)
-		die("too many params");
-
-	if (get_sha1(object_ref, object))
-		die("Failed to resolve '%s' as a valid ref.", object_ref);
-
-	if (snprintf(ref, sizeof(ref), "refs/tags/%s", tag) > sizeof(ref) - 1)
-		die("tag name too long: %.*s...", 50, tag);
-	if (check_ref_format(ref))
-		die("'%s' is not a valid tag name.", tag);
-
-	if (!resolve_ref(ref, prev, 1, NULL))
-		hashclr(prev);
-	else if (!force)
-		die("tag '%s' already exists", tag);
-
-	if (annotate)
-		create_tag(object, tag, &buf, msg.given || msgfile,
-			   sign, prev, object);
-
-	lock = lock_any_ref_for_update(ref, prev, 0);
-	if (!lock)
-		die("%s: cannot lock the ref", ref);
-	if (write_ref_sha1(lock, object, NULL) < 0)
-		die("%s: cannot update the ref", ref);
-
-	strbuf_release(&buf);
-	return 0;
-}
diff --git a/builtin-tar-tree.c b/builtin-tar-tree.c
deleted file mode 100644
index 8b3a35e..0000000
--- a/builtin-tar-tree.c
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (c) 2005, 2006 Rene Scharfe
- */
-#include "cache.h"
-#include "commit.h"
-#include "tar.h"
-#include "builtin.h"
-#include "quote.h"
-
-static const char tar_tree_usage[] =
-"git tar-tree [--remote=<repo>] <tree-ish> [basedir]\n"
-"*** Note that this command is now deprecated; use \"git archive\" instead.";
-
-int cmd_tar_tree(int argc, const char **argv, const char *prefix)
-{
-	/*
-	 * "git tar-tree" is now a wrapper around "git archive --format=tar"
-	 *
-	 * $0 --remote=<repo> arg... ==>
-	 *	git archive --format=tar --remote=<repo> arg...
-	 * $0 tree-ish ==>
-	 *	git archive --format=tar tree-ish
-	 * $0 tree-ish basedir ==>
-	 * 	git archive --format-tar --prefix=basedir tree-ish
-	 */
-	int i;
-	const char **nargv = xcalloc(sizeof(*nargv), argc + 3);
-	char *basedir_arg;
-	int nargc = 0;
-
-	nargv[nargc++] = "archive";
-	nargv[nargc++] = "--format=tar";
-
-	if (2 <= argc && !prefixcmp(argv[1], "--remote=")) {
-		nargv[nargc++] = argv[1];
-		argv++;
-		argc--;
-	}
-
-	/*
-	 * Because it's just a compatibility wrapper, tar-tree supports only
-	 * the old behaviour of reading attributes from the work tree.
-	 */
-	nargv[nargc++] = "--worktree-attributes";
-
-	switch (argc) {
-	default:
-		usage(tar_tree_usage);
-		break;
-	case 3:
-		/* base-path */
-		basedir_arg = xmalloc(strlen(argv[2]) + 11);
-		sprintf(basedir_arg, "--prefix=%s/", argv[2]);
-		nargv[nargc++] = basedir_arg;
-		/* fallthru */
-	case 2:
-		/* tree-ish */
-		nargv[nargc++] = argv[1];
-	}
-	nargv[nargc] = NULL;
-
-	fprintf(stderr,
-		"*** \"git tar-tree\" is now deprecated.\n"
-		"*** Running \"git archive\" instead.\n***");
-	for (i = 0; i < nargc; i++) {
-		fputc(' ', stderr);
-		sq_quote_print(stderr, nargv[i]);
-	}
-	fputc('\n', stderr);
-	return cmd_archive(nargc, nargv, prefix);
-}
-
-/* ustar header + extended global header content */
-#define RECORDSIZE	(512)
-#define HEADERSIZE (2 * RECORDSIZE)
-
-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;
-	char *content = buffer + RECORDSIZE;
-	ssize_t n;
-
-	n = read_in_full(0, buffer, HEADERSIZE);
-	if (n < HEADERSIZE)
-		die("git get-tar-commit-id: read error");
-	if (header->typeflag[0] != 'g')
-		return 1;
-	if (memcmp(content, "52 comment=", 11))
-		return 1;
-
-	n = write_in_full(1, content + 11, 41);
-	if (n < 41)
-		die_errno("git get-tar-commit-id: write error");
-
-	return 0;
-}
diff --git a/builtin-unpack-objects.c b/builtin-unpack-objects.c
deleted file mode 100644
index 557148a..0000000
--- a/builtin-unpack-objects.c
+++ /dev/null
@@ -1,564 +0,0 @@
-#include "builtin.h"
-#include "cache.h"
-#include "object.h"
-#include "delta.h"
-#include "pack.h"
-#include "blob.h"
-#include "commit.h"
-#include "tag.h"
-#include "tree.h"
-#include "tree-walk.h"
-#include "progress.h"
-#include "decorate.h"
-#include "fsck.h"
-
-static int dry_run, quiet, recover, has_errors, strict;
-static const char unpack_usage[] = "git unpack-objects [-n] [-q] [-r] [--strict] < pack-file";
-
-/* We always read in 4kB chunks. */
-static unsigned char buffer[4096];
-static unsigned int offset, len;
-static off_t consumed_bytes;
-static git_SHA_CTX ctx;
-
-/*
- * When running under --strict mode, objects whose reachability are
- * suspect are kept in core without getting written in the object
- * store.
- */
-struct obj_buffer {
-	char *buffer;
-	unsigned long size;
-};
-
-static struct decoration obj_decorate;
-
-static struct obj_buffer *lookup_object_buffer(struct object *base)
-{
-	return lookup_decoration(&obj_decorate, base);
-}
-
-static void add_object_buffer(struct object *object, char *buffer, unsigned long size)
-{
-	struct obj_buffer *obj;
-	obj = xcalloc(1, sizeof(struct obj_buffer));
-	obj->buffer = buffer;
-	obj->size = size;
-	if (add_decoration(&obj_decorate, object, obj))
-		die("object %s tried to add buffer twice!", sha1_to_hex(object->sha1));
-}
-
-/*
- * Make sure at least "min" bytes are available in the buffer, and
- * return the pointer to the buffer.
- */
-static void *fill(int min)
-{
-	if (min <= len)
-		return buffer + offset;
-	if (min > sizeof(buffer))
-		die("cannot fill %d bytes", min);
-	if (offset) {
-		git_SHA1_Update(&ctx, buffer, offset);
-		memmove(buffer, buffer + offset, len);
-		offset = 0;
-	}
-	do {
-		ssize_t ret = xread(0, buffer + len, sizeof(buffer) - len);
-		if (ret <= 0) {
-			if (!ret)
-				die("early EOF");
-			die_errno("read error on input");
-		}
-		len += ret;
-	} while (len < min);
-	return buffer;
-}
-
-static void use(int bytes)
-{
-	if (bytes > len)
-		die("used more bytes than were available");
-	len -= bytes;
-	offset += bytes;
-
-	/* make sure off_t is sufficiently large not to wrap */
-	if (consumed_bytes > consumed_bytes + bytes)
-		die("pack too large for current definition of off_t");
-	consumed_bytes += bytes;
-}
-
-static void *get_data(unsigned long size)
-{
-	z_stream stream;
-	void *buf = xmalloc(size);
-
-	memset(&stream, 0, sizeof(stream));
-
-	stream.next_out = buf;
-	stream.avail_out = size;
-	stream.next_in = fill(1);
-	stream.avail_in = len;
-	git_inflate_init(&stream);
-
-	for (;;) {
-		int ret = git_inflate(&stream, 0);
-		use(len - stream.avail_in);
-		if (stream.total_out == size && ret == Z_STREAM_END)
-			break;
-		if (ret != Z_OK) {
-			error("inflate returned %d\n", ret);
-			free(buf);
-			buf = NULL;
-			if (!recover)
-				exit(1);
-			has_errors = 1;
-			break;
-		}
-		stream.next_in = fill(1);
-		stream.avail_in = len;
-	}
-	git_inflate_end(&stream);
-	return buf;
-}
-
-struct delta_info {
-	unsigned char base_sha1[20];
-	unsigned nr;
-	off_t base_offset;
-	unsigned long size;
-	void *delta;
-	struct delta_info *next;
-};
-
-static struct delta_info *delta_list;
-
-static void add_delta_to_list(unsigned nr, unsigned const char *base_sha1,
-			      off_t base_offset,
-			      void *delta, unsigned long size)
-{
-	struct delta_info *info = xmalloc(sizeof(*info));
-
-	hashcpy(info->base_sha1, base_sha1);
-	info->base_offset = base_offset;
-	info->size = size;
-	info->delta = delta;
-	info->nr = nr;
-	info->next = delta_list;
-	delta_list = info;
-}
-
-struct obj_info {
-	off_t offset;
-	unsigned char sha1[20];
-	struct object *obj;
-};
-
-#define FLAG_OPEN (1u<<20)
-#define FLAG_WRITTEN (1u<<21)
-
-static struct obj_info *obj_list;
-static unsigned nr_objects;
-
-/*
- * Called only from check_object() after it verified this object
- * is Ok.
- */
-static void write_cached_object(struct object *obj)
-{
-	unsigned char sha1[20];
-	struct obj_buffer *obj_buf = lookup_object_buffer(obj);
-	if (write_sha1_file(obj_buf->buffer, obj_buf->size, typename(obj->type), sha1) < 0)
-		die("failed to write object %s", sha1_to_hex(obj->sha1));
-	obj->flags |= FLAG_WRITTEN;
-}
-
-/*
- * At the very end of the processing, write_rest() scans the objects
- * that have reachability requirements and calls this function.
- * Verify its reachability and validity recursively and write it out.
- */
-static int check_object(struct object *obj, int type, void *data)
-{
-	if (!obj)
-		return 0;
-
-	if (obj->flags & FLAG_WRITTEN)
-		return 1;
-
-	if (type != OBJ_ANY && obj->type != type)
-		die("object type mismatch");
-
-	if (!(obj->flags & FLAG_OPEN)) {
-		unsigned long size;
-		int type = sha1_object_info(obj->sha1, &size);
-		if (type != obj->type || type <= 0)
-			die("object of unexpected type");
-		obj->flags |= FLAG_WRITTEN;
-		return 1;
-	}
-
-	if (fsck_object(obj, 1, fsck_error_function))
-		die("Error in object");
-	if (!fsck_walk(obj, check_object, NULL))
-		die("Error on reachable objects of %s", sha1_to_hex(obj->sha1));
-	write_cached_object(obj);
-	return 1;
-}
-
-static void write_rest(void)
-{
-	unsigned i;
-	for (i = 0; i < nr_objects; i++)
-		check_object(obj_list[i].obj, OBJ_ANY, NULL);
-}
-
-static void added_object(unsigned nr, enum object_type type,
-			 void *data, unsigned long size);
-
-/*
- * Write out nr-th object from the list, now we know the contents
- * of it.  Under --strict, this buffers structured objects in-core,
- * to be checked at the end.
- */
-static void write_object(unsigned nr, enum object_type type,
-			 void *buf, unsigned long size)
-{
-	if (!strict) {
-		if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0)
-			die("failed to write object");
-		added_object(nr, type, buf, size);
-		free(buf);
-		obj_list[nr].obj = NULL;
-	} else if (type == OBJ_BLOB) {
-		struct blob *blob;
-		if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0)
-			die("failed to write object");
-		added_object(nr, type, buf, size);
-		free(buf);
-
-		blob = lookup_blob(obj_list[nr].sha1);
-		if (blob)
-			blob->object.flags |= FLAG_WRITTEN;
-		else
-			die("invalid blob object");
-		obj_list[nr].obj = NULL;
-	} else {
-		struct object *obj;
-		int eaten;
-		hash_sha1_file(buf, size, typename(type), obj_list[nr].sha1);
-		added_object(nr, type, buf, size);
-		obj = parse_object_buffer(obj_list[nr].sha1, type, size, buf, &eaten);
-		if (!obj)
-			die("invalid %s", typename(type));
-		add_object_buffer(obj, buf, size);
-		obj->flags |= FLAG_OPEN;
-		obj_list[nr].obj = obj;
-	}
-}
-
-static void resolve_delta(unsigned nr, enum object_type type,
-			  void *base, unsigned long base_size,
-			  void *delta, unsigned long delta_size)
-{
-	void *result;
-	unsigned long result_size;
-
-	result = patch_delta(base, base_size,
-			     delta, delta_size,
-			     &result_size);
-	if (!result)
-		die("failed to apply delta");
-	free(delta);
-	write_object(nr, type, result, result_size);
-}
-
-/*
- * We now know the contents of an object (which is nr-th in the pack);
- * resolve all the deltified objects that are based on it.
- */
-static void added_object(unsigned nr, enum object_type type,
-			 void *data, unsigned long size)
-{
-	struct delta_info **p = &delta_list;
-	struct delta_info *info;
-
-	while ((info = *p) != NULL) {
-		if (!hashcmp(info->base_sha1, obj_list[nr].sha1) ||
-		    info->base_offset == obj_list[nr].offset) {
-			*p = info->next;
-			p = &delta_list;
-			resolve_delta(info->nr, type, data, size,
-				      info->delta, info->size);
-			free(info);
-			continue;
-		}
-		p = &info->next;
-	}
-}
-
-static void unpack_non_delta_entry(enum object_type type, unsigned long size,
-				   unsigned nr)
-{
-	void *buf = get_data(size);
-
-	if (!dry_run && buf)
-		write_object(nr, type, buf, size);
-	else
-		free(buf);
-}
-
-static int resolve_against_held(unsigned nr, const unsigned char *base,
-				void *delta_data, unsigned long delta_size)
-{
-	struct object *obj;
-	struct obj_buffer *obj_buffer;
-	obj = lookup_object(base);
-	if (!obj)
-		return 0;
-	obj_buffer = lookup_object_buffer(obj);
-	if (!obj_buffer)
-		return 0;
-	resolve_delta(nr, obj->type, obj_buffer->buffer,
-		      obj_buffer->size, delta_data, delta_size);
-	return 1;
-}
-
-static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
-			       unsigned nr)
-{
-	void *delta_data, *base;
-	unsigned long base_size;
-	unsigned char base_sha1[20];
-
-	if (type == OBJ_REF_DELTA) {
-		hashcpy(base_sha1, fill(20));
-		use(20);
-		delta_data = get_data(delta_size);
-		if (dry_run || !delta_data) {
-			free(delta_data);
-			return;
-		}
-		if (has_sha1_file(base_sha1))
-			; /* Ok we have this one */
-		else if (resolve_against_held(nr, base_sha1,
-					      delta_data, delta_size))
-			return; /* we are done */
-		else {
-			/* cannot resolve yet --- queue it */
-			hashcpy(obj_list[nr].sha1, null_sha1);
-			add_delta_to_list(nr, base_sha1, 0, delta_data, delta_size);
-			return;
-		}
-	} else {
-		unsigned base_found = 0;
-		unsigned char *pack, c;
-		off_t base_offset;
-		unsigned lo, mid, hi;
-
-		pack = fill(1);
-		c = *pack;
-		use(1);
-		base_offset = c & 127;
-		while (c & 128) {
-			base_offset += 1;
-			if (!base_offset || MSB(base_offset, 7))
-				die("offset value overflow for delta base object");
-			pack = fill(1);
-			c = *pack;
-			use(1);
-			base_offset = (base_offset << 7) + (c & 127);
-		}
-		base_offset = obj_list[nr].offset - base_offset;
-		if (base_offset <= 0 || base_offset >= obj_list[nr].offset)
-			die("offset value out of bound for delta base object");
-
-		delta_data = get_data(delta_size);
-		if (dry_run || !delta_data) {
-			free(delta_data);
-			return;
-		}
-		lo = 0;
-		hi = nr;
-		while (lo < hi) {
-			mid = (lo + hi)/2;
-			if (base_offset < obj_list[mid].offset) {
-				hi = mid;
-			} else if (base_offset > obj_list[mid].offset) {
-				lo = mid + 1;
-			} else {
-				hashcpy(base_sha1, obj_list[mid].sha1);
-				base_found = !is_null_sha1(base_sha1);
-				break;
-			}
-		}
-		if (!base_found) {
-			/*
-			 * The delta base object is itself a delta that
-			 * has not been resolved yet.
-			 */
-			hashcpy(obj_list[nr].sha1, null_sha1);
-			add_delta_to_list(nr, null_sha1, base_offset, delta_data, delta_size);
-			return;
-		}
-	}
-
-	if (resolve_against_held(nr, base_sha1, delta_data, delta_size))
-		return;
-
-	base = read_sha1_file(base_sha1, &type, &base_size);
-	if (!base) {
-		error("failed to read delta-pack base object %s",
-		      sha1_to_hex(base_sha1));
-		if (!recover)
-			exit(1);
-		has_errors = 1;
-		return;
-	}
-	resolve_delta(nr, type, base, base_size, delta_data, delta_size);
-	free(base);
-}
-
-static void unpack_one(unsigned nr)
-{
-	unsigned shift;
-	unsigned char *pack;
-	unsigned long size, c;
-	enum object_type type;
-
-	obj_list[nr].offset = consumed_bytes;
-
-	pack = fill(1);
-	c = *pack;
-	use(1);
-	type = (c >> 4) & 7;
-	size = (c & 15);
-	shift = 4;
-	while (c & 0x80) {
-		pack = fill(1);
-		c = *pack;
-		use(1);
-		size += (c & 0x7f) << shift;
-		shift += 7;
-	}
-
-	switch (type) {
-	case OBJ_COMMIT:
-	case OBJ_TREE:
-	case OBJ_BLOB:
-	case OBJ_TAG:
-		unpack_non_delta_entry(type, size, nr);
-		return;
-	case OBJ_REF_DELTA:
-	case OBJ_OFS_DELTA:
-		unpack_delta_entry(type, size, nr);
-		return;
-	default:
-		error("bad object type %d", type);
-		has_errors = 1;
-		if (recover)
-			return;
-		exit(1);
-	}
-}
-
-static void unpack_all(void)
-{
-	int i;
-	struct progress *progress = NULL;
-	struct pack_header *hdr = fill(sizeof(struct pack_header));
-
-	nr_objects = ntohl(hdr->hdr_entries);
-
-	if (ntohl(hdr->hdr_signature) != PACK_SIGNATURE)
-		die("bad pack file");
-	if (!pack_version_ok(hdr->hdr_version))
-		die("unknown pack file version %"PRIu32,
-			ntohl(hdr->hdr_version));
-	use(sizeof(struct pack_header));
-
-	if (!quiet)
-		progress = start_progress("Unpacking objects", nr_objects);
-	obj_list = xcalloc(nr_objects, sizeof(*obj_list));
-	for (i = 0; i < nr_objects; i++) {
-		unpack_one(i);
-		display_progress(progress, i + 1);
-	}
-	stop_progress(&progress);
-
-	if (delta_list)
-		die("unresolved deltas left after unpacking");
-}
-
-int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
-{
-	int i;
-	unsigned char sha1[20];
-
-	git_config(git_default_config, NULL);
-
-	quiet = !isatty(2);
-
-	for (i = 1 ; i < argc; i++) {
-		const char *arg = argv[i];
-
-		if (*arg == '-') {
-			if (!strcmp(arg, "-n")) {
-				dry_run = 1;
-				continue;
-			}
-			if (!strcmp(arg, "-q")) {
-				quiet = 1;
-				continue;
-			}
-			if (!strcmp(arg, "-r")) {
-				recover = 1;
-				continue;
-			}
-			if (!strcmp(arg, "--strict")) {
-				strict = 1;
-				continue;
-			}
-			if (!prefixcmp(arg, "--pack_header=")) {
-				struct pack_header *hdr;
-				char *c;
-
-				hdr = (struct pack_header *)buffer;
-				hdr->hdr_signature = htonl(PACK_SIGNATURE);
-				hdr->hdr_version = htonl(strtoul(arg + 14, &c, 10));
-				if (*c != ',')
-					die("bad %s", arg);
-				hdr->hdr_entries = htonl(strtoul(c + 1, &c, 10));
-				if (*c)
-					die("bad %s", arg);
-				len = sizeof(*hdr);
-				continue;
-			}
-			usage(unpack_usage);
-		}
-
-		/* We don't take any non-flag arguments now.. Maybe some day */
-		usage(unpack_usage);
-	}
-	git_SHA1_Init(&ctx);
-	unpack_all();
-	git_SHA1_Update(&ctx, buffer, offset);
-	git_SHA1_Final(sha1, &ctx);
-	if (strict)
-		write_rest();
-	if (hashcmp(fill(20), sha1))
-		die("final sha1 did not match");
-	use(20);
-
-	/* Write the last part of the buffer to stdout */
-	while (len) {
-		int ret = xwrite(1, buffer + offset, len);
-		if (ret <= 0)
-			break;
-		len -= ret;
-		offset += ret;
-	}
-
-	/* All done */
-	return has_errors;
-}
diff --git a/builtin-update-index.c b/builtin-update-index.c
deleted file mode 100644
index 92beaaf..0000000
--- a/builtin-update-index.c
+++ /dev/null
@@ -1,755 +0,0 @@
-/*
- * GIT - The information manager from hell
- *
- * Copyright (C) Linus Torvalds, 2005
- */
-#include "cache.h"
-#include "quote.h"
-#include "cache-tree.h"
-#include "tree-walk.h"
-#include "builtin.h"
-#include "refs.h"
-
-/*
- * Default to not allowing changes to the list of files. The
- * tool doesn't actually care, but this makes it harder to add
- * files to the revision control by mistake by doing something
- * like "git update-index *" and suddenly having all the object
- * files be revision controlled.
- */
-static int allow_add;
-static int allow_remove;
-static int allow_replace;
-static int info_only;
-static int force_remove;
-static int verbose;
-static int mark_valid_only;
-#define MARK_VALID 1
-#define UNMARK_VALID 2
-
-static void report(const char *fmt, ...)
-{
-	va_list vp;
-
-	if (!verbose)
-		return;
-
-	va_start(vp, fmt);
-	vprintf(fmt, vp);
-	putchar('\n');
-	va_end(vp);
-}
-
-static int mark_valid(const char *path)
-{
-	int namelen = strlen(path);
-	int pos = cache_name_pos(path, namelen);
-	if (0 <= pos) {
-		switch (mark_valid_only) {
-		case MARK_VALID:
-			active_cache[pos]->ce_flags |= CE_VALID;
-			break;
-		case UNMARK_VALID:
-			active_cache[pos]->ce_flags &= ~CE_VALID;
-			break;
-		}
-		cache_tree_invalidate_path(active_cache_tree, path);
-		active_cache_changed = 1;
-		return 0;
-	}
-	return -1;
-}
-
-static int remove_one_path(const char *path)
-{
-	if (!allow_remove)
-		return error("%s: does not exist and --remove not passed", path);
-	if (remove_file_from_cache(path))
-		return error("%s: cannot remove from the index", path);
-	return 0;
-}
-
-/*
- * Handle a path that couldn't be lstat'ed. It's either:
- *  - missing file (ENOENT or ENOTDIR). That's ok if we're
- *    supposed to be removing it and the removal actually
- *    succeeds.
- *  - permission error. That's never ok.
- */
-static int process_lstat_error(const char *path, int err)
-{
-	if (err == ENOENT || err == ENOTDIR)
-		return remove_one_path(path);
-	return error("lstat(\"%s\"): %s", path, strerror(errno));
-}
-
-static int add_one_path(struct cache_entry *old, const char *path, int len, struct stat *st)
-{
-	int option, size;
-	struct cache_entry *ce;
-
-	/* Was the old index entry already up-to-date? */
-	if (old && !ce_stage(old) && !ce_match_stat(old, st, 0))
-		return 0;
-
-	size = cache_entry_size(len);
-	ce = xcalloc(1, size);
-	memcpy(ce->name, path, len);
-	ce->ce_flags = len;
-	fill_stat_cache_info(ce, st);
-	ce->ce_mode = ce_mode_from_stat(old, st->st_mode);
-
-	if (index_path(ce->sha1, path, st, !info_only))
-		return -1;
-	option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
-	option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
-	if (add_cache_entry(ce, option))
-		return error("%s: cannot add to the index - missing --add option?", path);
-	return 0;
-}
-
-/*
- * Handle a path that was a directory. Four cases:
- *
- *  - it's already a gitlink in the index, and we keep it that
- *    way, and update it if we can (if we cannot find the HEAD,
- *    we're going to keep it unchanged in the index!)
- *
- *  - it's a *file* in the index, in which case it should be
- *    removed as a file if removal is allowed, since it doesn't
- *    exist as such any more. If removal isn't allowed, it's
- *    an error.
- *
- *    (NOTE! This is old and arguably fairly strange behaviour.
- *    We might want to make this an error unconditionally, and
- *    use "--force-remove" if you actually want to force removal).
- *
- *  - it used to exist as a subdirectory (ie multiple files with
- *    this particular prefix) in the index, in which case it's wrong
- *    to try to update it as a directory.
- *
- *  - it doesn't exist at all in the index, but it is a valid
- *    git directory, and it should be *added* as a gitlink.
- */
-static int process_directory(const char *path, int len, struct stat *st)
-{
-	unsigned char sha1[20];
-	int pos = cache_name_pos(path, len);
-
-	/* Exact match: file or existing gitlink */
-	if (pos >= 0) {
-		struct cache_entry *ce = active_cache[pos];
-		if (S_ISGITLINK(ce->ce_mode)) {
-
-			/* Do nothing to the index if there is no HEAD! */
-			if (resolve_gitlink_ref(path, "HEAD", sha1) < 0)
-				return 0;
-
-			return add_one_path(ce, path, len, st);
-		}
-		/* Should this be an unconditional error? */
-		return remove_one_path(path);
-	}
-
-	/* Inexact match: is there perhaps a subdirectory match? */
-	pos = -pos-1;
-	while (pos < active_nr) {
-		struct cache_entry *ce = active_cache[pos++];
-
-		if (strncmp(ce->name, path, len))
-			break;
-		if (ce->name[len] > '/')
-			break;
-		if (ce->name[len] < '/')
-			continue;
-
-		/* Subdirectory match - error out */
-		return error("%s: is a directory - add individual files instead", path);
-	}
-
-	/* No match - should we add it as a gitlink? */
-	if (!resolve_gitlink_ref(path, "HEAD", sha1))
-		return add_one_path(NULL, path, len, st);
-
-	/* Error out. */
-	return error("%s: is a directory - add files inside instead", path);
-}
-
-/*
- * Process a regular file
- */
-static int process_file(const char *path, int len, struct stat *st)
-{
-	int pos = cache_name_pos(path, len);
-	struct cache_entry *ce = pos < 0 ? NULL : active_cache[pos];
-
-	if (ce && S_ISGITLINK(ce->ce_mode))
-		return error("%s is already a gitlink, not replacing", path);
-
-	return add_one_path(ce, path, len, st);
-}
-
-static int process_path(const char *path)
-{
-	int len;
-	struct stat st;
-
-	len = strlen(path);
-	if (has_symlink_leading_path(path, len))
-		return error("'%s' is beyond a symbolic link", path);
-
-	/*
-	 * First things first: get the stat information, to decide
-	 * what to do about the pathname!
-	 */
-	if (lstat(path, &st) < 0)
-		return process_lstat_error(path, errno);
-
-	if (S_ISDIR(st.st_mode))
-		return process_directory(path, len, &st);
-
-	return process_file(path, len, &st);
-}
-
-static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
-			 const char *path, int stage)
-{
-	int size, len, option;
-	struct cache_entry *ce;
-
-	if (!verify_path(path))
-		return error("Invalid path '%s'", path);
-
-	len = strlen(path);
-	size = cache_entry_size(len);
-	ce = xcalloc(1, size);
-
-	hashcpy(ce->sha1, sha1);
-	memcpy(ce->name, path, len);
-	ce->ce_flags = create_ce_flags(len, stage);
-	ce->ce_mode = create_ce_mode(mode);
-	if (assume_unchanged)
-		ce->ce_flags |= CE_VALID;
-	option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
-	option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
-	if (add_cache_entry(ce, option))
-		return error("%s: cannot add to the index - missing --add option?",
-			     path);
-	report("add '%s'", path);
-	return 0;
-}
-
-static void chmod_path(int flip, const char *path)
-{
-	int pos;
-	struct cache_entry *ce;
-	unsigned int mode;
-
-	pos = cache_name_pos(path, strlen(path));
-	if (pos < 0)
-		goto fail;
-	ce = active_cache[pos];
-	mode = ce->ce_mode;
-	if (!S_ISREG(mode))
-		goto fail;
-	switch (flip) {
-	case '+':
-		ce->ce_mode |= 0111; break;
-	case '-':
-		ce->ce_mode &= ~0111; break;
-	default:
-		goto fail;
-	}
-	cache_tree_invalidate_path(active_cache_tree, path);
-	active_cache_changed = 1;
-	report("chmod %cx '%s'", flip, path);
-	return;
- fail:
-	die("git update-index: cannot chmod %cx '%s'", flip, path);
-}
-
-static void update_one(const char *path, const char *prefix, int prefix_length)
-{
-	const char *p = prefix_path(prefix, prefix_length, path);
-	if (!verify_path(p)) {
-		fprintf(stderr, "Ignoring path %s\n", path);
-		goto free_return;
-	}
-	if (mark_valid_only) {
-		if (mark_valid(p))
-			die("Unable to mark file %s", path);
-		goto free_return;
-	}
-
-	if (force_remove) {
-		if (remove_file_from_cache(p))
-			die("git update-index: unable to remove %s", path);
-		report("remove '%s'", path);
-		goto free_return;
-	}
-	if (process_path(p))
-		die("Unable to process path %s", path);
-	report("add '%s'", path);
- free_return:
-	if (p < path || p > path + strlen(path))
-		free((char *)p);
-}
-
-static void read_index_info(int line_termination)
-{
-	struct strbuf buf = STRBUF_INIT;
-	struct strbuf uq = STRBUF_INIT;
-
-	while (strbuf_getline(&buf, stdin, line_termination) != EOF) {
-		char *ptr, *tab;
-		char *path_name;
-		unsigned char sha1[20];
-		unsigned int mode;
-		unsigned long ul;
-		int stage;
-
-		/* This reads lines formatted in one of three formats:
-		 *
-		 * (1) mode         SP sha1          TAB path
-		 * The first format is what "git apply --index-info"
-		 * reports, and used to reconstruct a partial tree
-		 * that is used for phony merge base tree when falling
-		 * back on 3-way merge.
-		 *
-		 * (2) mode SP type SP sha1          TAB path
-		 * The second format is to stuff "git ls-tree" output
-		 * into the index file.
-		 *
-		 * (3) mode         SP sha1 SP stage TAB path
-		 * This format is to put higher order stages into the
-		 * index file and matches "git ls-files --stage" output.
-		 */
-		errno = 0;
-		ul = strtoul(buf.buf, &ptr, 8);
-		if (ptr == buf.buf || *ptr != ' '
-		    || errno || (unsigned int) ul != ul)
-			goto bad_line;
-		mode = ul;
-
-		tab = strchr(ptr, '\t');
-		if (!tab || tab - ptr < 41)
-			goto bad_line;
-
-		if (tab[-2] == ' ' && '0' <= tab[-1] && tab[-1] <= '3') {
-			stage = tab[-1] - '0';
-			ptr = tab + 1; /* point at the head of path */
-			tab = tab - 2; /* point at tail of sha1 */
-		}
-		else {
-			stage = 0;
-			ptr = tab + 1; /* point at the head of path */
-		}
-
-		if (get_sha1_hex(tab - 40, sha1) || tab[-41] != ' ')
-			goto bad_line;
-
-		path_name = ptr;
-		if (line_termination && path_name[0] == '"') {
-			strbuf_reset(&uq);
-			if (unquote_c_style(&uq, path_name, NULL)) {
-				die("git update-index: bad quoting of path name");
-			}
-			path_name = uq.buf;
-		}
-
-		if (!verify_path(path_name)) {
-			fprintf(stderr, "Ignoring path %s\n", path_name);
-			continue;
-		}
-
-		if (!mode) {
-			/* mode == 0 means there is no such path -- remove */
-			if (remove_file_from_cache(path_name))
-				die("git update-index: unable to remove %s",
-				    ptr);
-		}
-		else {
-			/* mode ' ' sha1 '\t' name
-			 * ptr[-1] points at tab,
-			 * ptr[-41] is at the beginning of sha1
-			 */
-			ptr[-42] = ptr[-1] = 0;
-			if (add_cacheinfo(mode, sha1, path_name, stage))
-				die("git update-index: unable to update %s",
-				    path_name);
-		}
-		continue;
-
-	bad_line:
-		die("malformed index info %s", buf.buf);
-	}
-	strbuf_release(&buf);
-	strbuf_release(&uq);
-}
-
-static const char update_index_usage[] =
-"git update-index [-q] [--add] [--replace] [--remove] [--unmerged] [--refresh] [--really-refresh] [--cacheinfo] [--chmod=(+|-)x] [--assume-unchanged] [--info-only] [--force-remove] [--stdin] [--index-info] [--unresolve] [--again | -g] [--ignore-missing] [-z] [--verbose] [--] <file>...";
-
-static unsigned char head_sha1[20];
-static unsigned char merge_head_sha1[20];
-
-static struct cache_entry *read_one_ent(const char *which,
-					unsigned char *ent, const char *path,
-					int namelen, int stage)
-{
-	unsigned mode;
-	unsigned char sha1[20];
-	int size;
-	struct cache_entry *ce;
-
-	if (get_tree_entry(ent, path, sha1, &mode)) {
-		if (which)
-			error("%s: not in %s branch.", path, which);
-		return NULL;
-	}
-	if (mode == S_IFDIR) {
-		if (which)
-			error("%s: not a blob in %s branch.", path, which);
-		return NULL;
-	}
-	size = cache_entry_size(namelen);
-	ce = xcalloc(1, size);
-
-	hashcpy(ce->sha1, sha1);
-	memcpy(ce->name, path, namelen);
-	ce->ce_flags = create_ce_flags(namelen, stage);
-	ce->ce_mode = create_ce_mode(mode);
-	return ce;
-}
-
-static int unresolve_one(const char *path)
-{
-	int namelen = strlen(path);
-	int pos;
-	int ret = 0;
-	struct cache_entry *ce_2 = NULL, *ce_3 = NULL;
-
-	/* See if there is such entry in the index. */
-	pos = cache_name_pos(path, namelen);
-	if (pos < 0) {
-		/* If there isn't, either it is unmerged, or
-		 * resolved as "removed" by mistake.  We do not
-		 * want to do anything in the former case.
-		 */
-		pos = -pos-1;
-		if (pos < active_nr) {
-			struct cache_entry *ce = active_cache[pos];
-			if (ce_namelen(ce) == namelen &&
-			    !memcmp(ce->name, path, namelen)) {
-				fprintf(stderr,
-					"%s: skipping still unmerged path.\n",
-					path);
-				goto free_return;
-			}
-		}
-	}
-
-	/* Grab blobs from given path from HEAD and MERGE_HEAD,
-	 * stuff HEAD version in stage #2,
-	 * stuff MERGE_HEAD version in stage #3.
-	 */
-	ce_2 = read_one_ent("our", head_sha1, path, namelen, 2);
-	ce_3 = read_one_ent("their", merge_head_sha1, path, namelen, 3);
-
-	if (!ce_2 || !ce_3) {
-		ret = -1;
-		goto free_return;
-	}
-	if (!hashcmp(ce_2->sha1, ce_3->sha1) &&
-	    ce_2->ce_mode == ce_3->ce_mode) {
-		fprintf(stderr, "%s: identical in both, skipping.\n",
-			path);
-		goto free_return;
-	}
-
-	remove_file_from_cache(path);
-	if (add_cache_entry(ce_2, ADD_CACHE_OK_TO_ADD)) {
-		error("%s: cannot add our version to the index.", path);
-		ret = -1;
-		goto free_return;
-	}
-	if (!add_cache_entry(ce_3, ADD_CACHE_OK_TO_ADD))
-		return 0;
-	error("%s: cannot add their version to the index.", path);
-	ret = -1;
- free_return:
-	free(ce_2);
-	free(ce_3);
-	return ret;
-}
-
-static void read_head_pointers(void)
-{
-	if (read_ref("HEAD", head_sha1))
-		die("No HEAD -- no initial commit yet?");
-	if (read_ref("MERGE_HEAD", merge_head_sha1)) {
-		fprintf(stderr, "Not in the middle of a merge.\n");
-		exit(0);
-	}
-}
-
-static int do_unresolve(int ac, const char **av,
-			const char *prefix, int prefix_length)
-{
-	int i;
-	int err = 0;
-
-	/* Read HEAD and MERGE_HEAD; if MERGE_HEAD does not exist, we
-	 * are not doing a merge, so exit with success status.
-	 */
-	read_head_pointers();
-
-	for (i = 1; i < ac; i++) {
-		const char *arg = av[i];
-		const char *p = prefix_path(prefix, prefix_length, arg);
-		err |= unresolve_one(p);
-		if (p < arg || p > arg + strlen(arg))
-			free((char *)p);
-	}
-	return err;
-}
-
-static int do_reupdate(int ac, const char **av,
-		       const char *prefix, int prefix_length)
-{
-	/* Read HEAD and run update-index on paths that are
-	 * merged and already different between index and HEAD.
-	 */
-	int pos;
-	int has_head = 1;
-	const char **pathspec = get_pathspec(prefix, av + 1);
-
-	if (read_ref("HEAD", head_sha1))
-		/* If there is no HEAD, that means it is an initial
-		 * commit.  Update everything in the index.
-		 */
-		has_head = 0;
- redo:
-	for (pos = 0; pos < active_nr; pos++) {
-		struct cache_entry *ce = active_cache[pos];
-		struct cache_entry *old = NULL;
-		int save_nr;
-
-		if (ce_stage(ce) || !ce_path_match(ce, pathspec))
-			continue;
-		if (has_head)
-			old = read_one_ent(NULL, head_sha1,
-					   ce->name, ce_namelen(ce), 0);
-		if (old && ce->ce_mode == old->ce_mode &&
-		    !hashcmp(ce->sha1, old->sha1)) {
-			free(old);
-			continue; /* unchanged */
-		}
-		/* Be careful.  The working tree may not have the
-		 * path anymore, in which case, under 'allow_remove',
-		 * or worse yet 'allow_replace', active_nr may decrease.
-		 */
-		save_nr = active_nr;
-		update_one(ce->name + prefix_length, prefix, prefix_length);
-		if (save_nr != active_nr)
-			goto redo;
-	}
-	return 0;
-}
-
-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;
-	int prefix_length = prefix ? strlen(prefix) : 0;
-	char set_executable_bit = 0;
-	unsigned int refresh_flags = 0;
-	int lock_error = 0;
-	struct lock_file *lock_file;
-
-	git_config(git_default_config, NULL);
-
-	/* 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_locked_index(lock_file, 0);
-	if (newfd < 0)
-		lock_error = errno;
-
-	entries = read_cache();
-	if (entries < 0)
-		die("cache corrupted");
-
-	for (i = 1 ; i < argc; i++) {
-		const char *path = argv[i];
-		const char *p;
-
-		if (allow_options && *path == '-') {
-			if (!strcmp(path, "--")) {
-				allow_options = 0;
-				continue;
-			}
-			if (!strcmp(path, "-q")) {
-				refresh_flags |= REFRESH_QUIET;
-				continue;
-			}
-			if (!strcmp(path, "--ignore-submodules")) {
-				refresh_flags |= REFRESH_IGNORE_SUBMODULES;
-				continue;
-			}
-			if (!strcmp(path, "--add")) {
-				allow_add = 1;
-				continue;
-			}
-			if (!strcmp(path, "--replace")) {
-				allow_replace = 1;
-				continue;
-			}
-			if (!strcmp(path, "--remove")) {
-				allow_remove = 1;
-				continue;
-			}
-			if (!strcmp(path, "--unmerged")) {
-				refresh_flags |= REFRESH_UNMERGED;
-				continue;
-			}
-			if (!strcmp(path, "--refresh")) {
-				setup_work_tree();
-				has_errors |= refresh_cache(refresh_flags);
-				continue;
-			}
-			if (!strcmp(path, "--really-refresh")) {
-				setup_work_tree();
-				has_errors |= refresh_cache(REFRESH_REALLY | refresh_flags);
-				continue;
-			}
-			if (!strcmp(path, "--cacheinfo")) {
-				unsigned char sha1[20];
-				unsigned int mode;
-
-				if (i+3 >= argc)
-					die("git update-index: --cacheinfo <mode> <sha1> <path>");
-
-				if (strtoul_ui(argv[i+1], 8, &mode) ||
-				    get_sha1_hex(argv[i+2], sha1) ||
-				    add_cacheinfo(mode, sha1, argv[i+3], 0))
-					die("git update-index: --cacheinfo"
-					    " cannot add %s", argv[i+3]);
-				i += 3;
-				continue;
-			}
-			if (!strcmp(path, "--chmod=-x") ||
-			    !strcmp(path, "--chmod=+x")) {
-				if (argc <= i+1)
-					die("git update-index: %s <path>", path);
-				set_executable_bit = path[8];
-				continue;
-			}
-			if (!strcmp(path, "--assume-unchanged")) {
-				mark_valid_only = MARK_VALID;
-				continue;
-			}
-			if (!strcmp(path, "--no-assume-unchanged")) {
-				mark_valid_only = UNMARK_VALID;
-				continue;
-			}
-			if (!strcmp(path, "--info-only")) {
-				info_only = 1;
-				continue;
-			}
-			if (!strcmp(path, "--force-remove")) {
-				force_remove = 1;
-				continue;
-			}
-			if (!strcmp(path, "-z")) {
-				line_termination = 0;
-				continue;
-			}
-			if (!strcmp(path, "--stdin")) {
-				if (i != argc - 1)
-					die("--stdin must be at the end");
-				read_from_stdin = 1;
-				break;
-			}
-			if (!strcmp(path, "--index-info")) {
-				if (i != argc - 1)
-					die("--index-info must be at the end");
-				allow_add = allow_replace = allow_remove = 1;
-				read_index_info(line_termination);
-				break;
-			}
-			if (!strcmp(path, "--unresolve")) {
-				has_errors = do_unresolve(argc - i, argv + i,
-							  prefix, prefix_length);
-				if (has_errors)
-					active_cache_changed = 0;
-				goto finish;
-			}
-			if (!strcmp(path, "--again") || !strcmp(path, "-g")) {
-				setup_work_tree();
-				has_errors = do_reupdate(argc - i, argv + i,
-							 prefix, prefix_length);
-				if (has_errors)
-					active_cache_changed = 0;
-				goto finish;
-			}
-			if (!strcmp(path, "--ignore-missing")) {
-				refresh_flags |= REFRESH_IGNORE_MISSING;
-				continue;
-			}
-			if (!strcmp(path, "--verbose")) {
-				verbose = 1;
-				continue;
-			}
-			if (!strcmp(path, "-h") || !strcmp(path, "--help"))
-				usage(update_index_usage);
-			die("unknown option %s", path);
-		}
-		setup_work_tree();
-		p = prefix_path(prefix, prefix_length, path);
-		update_one(p, NULL, 0);
-		if (set_executable_bit)
-			chmod_path(set_executable_bit, p);
-		if (p < path || p > path + strlen(path))
-			free((char *)p);
-	}
-	if (read_from_stdin) {
-		struct strbuf buf = STRBUF_INIT, nbuf = STRBUF_INIT;
-
-		setup_work_tree();
-		while (strbuf_getline(&buf, stdin, line_termination) != EOF) {
-			const char *p;
-			if (line_termination && buf.buf[0] == '"') {
-				strbuf_reset(&nbuf);
-				if (unquote_c_style(&nbuf, buf.buf, NULL))
-					die("line is badly quoted");
-				strbuf_swap(&buf, &nbuf);
-			}
-			p = prefix_path(prefix, prefix_length, buf.buf);
-			update_one(p, NULL, 0);
-			if (set_executable_bit)
-				chmod_path(set_executable_bit, p);
-			if (p < buf.buf || p > buf.buf + buf.len)
-				free((char *)p);
-		}
-		strbuf_release(&nbuf);
-		strbuf_release(&buf);
-	}
-
- finish:
-	if (active_cache_changed) {
-		if (newfd < 0) {
-			if (refresh_flags & REFRESH_QUIET)
-				exit(128);
-			unable_to_lock_index_die(get_index_file(), lock_error);
-		}
-		if (write_cache(newfd, active_cache, active_nr) ||
-		    commit_locked_index(lock_file))
-			die("Unable to write new index file");
-	}
-
-	rollback_lock_file(lock_file);
-
-	return has_errors ? 1 : 0;
-}
diff --git a/builtin-update-ref.c b/builtin-update-ref.c
deleted file mode 100644
index 76ba1d5..0000000
--- a/builtin-update-ref.c
+++ /dev/null
@@ -1,58 +0,0 @@
-#include "cache.h"
-#include "refs.h"
-#include "builtin.h"
-#include "parse-options.h"
-
-static const char * const git_update_ref_usage[] = {
-	"git update-ref [options] -d <refname> [<oldval>]",
-	"git update-ref [options]    <refname> <newval> [<oldval>]",
-	NULL
-};
-
-int cmd_update_ref(int argc, const char **argv, const char *prefix)
-{
-	const char *refname, *oldval, *msg=NULL;
-	unsigned char sha1[20], oldsha1[20];
-	int delete = 0, no_deref = 0, flags = 0;
-	struct option options[] = {
-		OPT_STRING( 'm', NULL, &msg, "reason", "reason of the update"),
-		OPT_BOOLEAN('d', NULL, &delete, "deletes the reference"),
-		OPT_BOOLEAN( 0 , "no-deref", &no_deref,
-					"update <refname> not the one it points to"),
-		OPT_END(),
-	};
-
-	git_config(git_default_config, NULL);
-	argc = parse_options(argc, argv, prefix, options, git_update_ref_usage,
-			     0);
-	if (msg && !*msg)
-		die("Refusing to perform update with empty message.");
-
-	if (delete) {
-		if (argc < 1 || argc > 2)
-			usage_with_options(git_update_ref_usage, options);
-		refname = argv[0];
-		oldval = argv[1];
-	} else {
-		const char *value;
-		if (argc < 2 || argc > 3)
-			usage_with_options(git_update_ref_usage, options);
-		refname = argv[0];
-		value = argv[1];
-		oldval = argv[2];
-		if (get_sha1(value, sha1))
-			die("%s: not a valid SHA1", value);
-	}
-
-	hashclr(oldsha1); /* all-zero hash in case oldval is the empty string */
-	if (oldval && *oldval && get_sha1(oldval, oldsha1))
-		die("%s: not a valid old SHA1", oldval);
-
-	if (no_deref)
-		flags = REF_NODEREF;
-	if (delete)
-		return delete_ref(refname, oldval ? oldsha1 : NULL, flags);
-	else
-		return update_ref(msg, refname, sha1, oldval ? oldsha1 : NULL,
-				  flags, DIE_ON_ERR);
-}
diff --git a/builtin-upload-archive.c b/builtin-upload-archive.c
deleted file mode 100644
index c4cd1e1..0000000
--- a/builtin-upload-archive.c
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright (c) 2006 Franck Bui-Huu
- */
-#include "cache.h"
-#include "builtin.h"
-#include "archive.h"
-#include "pkt-line.h"
-#include "sideband.h"
-
-static const char upload_archive_usage[] =
-	"git upload-archive <repo>";
-
-static const char deadchild[] =
-"git upload-archive: archiver died with error";
-
-static const char lostchild[] =
-"git upload-archive: archiver process was lost";
-
-#define MAX_ARGS (64)
-
-static int run_upload_archive(int argc, const char **argv, const char *prefix)
-{
-	const char *sent_argv[MAX_ARGS];
-	const char *arg_cmd = "argument ";
-	char *p, buf[4096];
-	int sent_argc;
-	int len;
-
-	if (argc != 2)
-		usage(upload_archive_usage);
-
-	if (strlen(argv[1]) + 1 > sizeof(buf))
-		die("insanely long repository name");
-
-	strcpy(buf, argv[1]); /* enter-repo smudges its argument */
-
-	if (!enter_repo(buf, 0))
-		die("'%s' does not appear to be a git repository", buf);
-
-	/* put received options in sent_argv[] */
-	sent_argc = 1;
-	sent_argv[0] = "git-upload-archive";
-	for (p = buf;;) {
-		/* This will die if not enough free space in buf */
-		len = packet_read_line(0, p, (buf + sizeof buf) - p);
-		if (len == 0)
-			break;	/* got a flush */
-		if (sent_argc > MAX_ARGS - 2)
-			die("Too many options (>%d)", MAX_ARGS - 2);
-
-		if (p[len-1] == '\n') {
-			p[--len] = 0;
-		}
-		if (len < strlen(arg_cmd) ||
-		    strncmp(arg_cmd, p, strlen(arg_cmd)))
-			die("'argument' token or flush expected");
-
-		len -= strlen(arg_cmd);
-		memmove(p, p + strlen(arg_cmd), len);
-		sent_argv[sent_argc++] = p;
-		p += len;
-		*p++ = 0;
-	}
-	sent_argv[sent_argc] = NULL;
-
-	/* parse all options sent by the client */
-	return write_archive(sent_argc, sent_argv, prefix, 0);
-}
-
-static void error_clnt(const char *fmt, ...)
-{
-	char buf[1024];
-	va_list params;
-	int len;
-
-	va_start(params, fmt);
-	len = vsprintf(buf, fmt, params);
-	va_end(params);
-	send_sideband(1, 3, buf, len, LARGE_PACKET_MAX);
-	die("sent error to the client: %s", buf);
-}
-
-static ssize_t process_input(int child_fd, int band)
-{
-	char buf[16384];
-	ssize_t sz = read(child_fd, buf, sizeof(buf));
-	if (sz < 0) {
-		if (errno != EAGAIN && errno != EINTR)
-			error_clnt("read error: %s\n", strerror(errno));
-		return sz;
-	}
-	send_sideband(1, band, buf, sz, LARGE_PACKET_MAX);
-	return sz;
-}
-
-int cmd_upload_archive(int argc, const char **argv, const char *prefix)
-{
-	pid_t writer;
-	int fd1[2], fd2[2];
-	/*
-	 * Set up sideband subprocess.
-	 *
-	 * We (parent) monitor and read from child, sending its fd#1 and fd#2
-	 * multiplexed out to our fd#1.  If the child dies, we tell the other
-	 * end over channel #3.
-	 */
-	if (pipe(fd1) < 0 || pipe(fd2) < 0) {
-		int err = errno;
-		packet_write(1, "NACK pipe failed on the remote side\n");
-		die("upload-archive: %s", strerror(err));
-	}
-	writer = fork();
-	if (writer < 0) {
-		int err = errno;
-		packet_write(1, "NACK fork failed on the remote side\n");
-		die("upload-archive: %s", strerror(err));
-	}
-	if (!writer) {
-		/* child - connect fd#1 and fd#2 to the pipe */
-		dup2(fd1[1], 1);
-		dup2(fd2[1], 2);
-		close(fd1[1]); close(fd2[1]);
-		close(fd1[0]); close(fd2[0]); /* we do not read from pipe */
-
-		exit(run_upload_archive(argc, argv, prefix));
-	}
-
-	/* parent - read from child, multiplex and send out to fd#1 */
-	close(fd1[1]); close(fd2[1]); /* we do not write to pipe */
-	packet_write(1, "ACK\n");
-	packet_flush(1);
-
-	while (1) {
-		struct pollfd pfd[2];
-		ssize_t processed[2] = { 0, 0 };
-		int status;
-
-		pfd[0].fd = fd1[0];
-		pfd[0].events = POLLIN;
-		pfd[1].fd = fd2[0];
-		pfd[1].events = POLLIN;
-		if (poll(pfd, 2, -1) < 0) {
-			if (errno != EINTR) {
-				error("poll failed resuming: %s",
-				      strerror(errno));
-				sleep(1);
-			}
-			continue;
-		}
-		if (pfd[0].revents & POLLIN)
-			/* Data stream ready */
-			processed[0] = process_input(pfd[0].fd, 1);
-		if (pfd[1].revents & POLLIN)
-			/* Status stream ready */
-			processed[1] = process_input(pfd[1].fd, 2);
-		/* Always finish to read data when available */
-		if (processed[0] || processed[1])
-			continue;
-
-		if (waitpid(writer, &status, 0) < 0)
-			error_clnt("%s", lostchild);
-		else if (!WIFEXITED(status) || WEXITSTATUS(status) > 0)
-			error_clnt("%s", deadchild);
-		packet_flush(1);
-		break;
-	}
-	return 0;
-}
diff --git a/builtin-verify-pack.c b/builtin-verify-pack.c
deleted file mode 100644
index ebd6dff..0000000
--- a/builtin-verify-pack.c
+++ /dev/null
@@ -1,138 +0,0 @@
-#include "builtin.h"
-#include "cache.h"
-#include "pack.h"
-#include "pack-revindex.h"
-#include "parse-options.h"
-
-#define MAX_CHAIN 50
-
-static void show_pack_info(struct packed_git *p)
-{
-	uint32_t nr_objects, i, chain_histogram[MAX_CHAIN+1];
-
-	nr_objects = p->num_objects;
-	memset(chain_histogram, 0, sizeof(chain_histogram));
-
-	for (i = 0; i < nr_objects; i++) {
-		const unsigned char *sha1;
-		unsigned char base_sha1[20];
-		const char *type;
-		unsigned long size;
-		unsigned long store_size;
-		off_t offset;
-		unsigned int delta_chain_length;
-
-		sha1 = nth_packed_object_sha1(p, i);
-		if (!sha1)
-			die("internal error pack-check nth-packed-object");
-		offset = nth_packed_object_offset(p, i);
-		type = packed_object_info_detail(p, offset, &size, &store_size,
-						 &delta_chain_length,
-						 base_sha1);
-		printf("%s ", sha1_to_hex(sha1));
-		if (!delta_chain_length)
-			printf("%-6s %lu %lu %"PRIuMAX"\n",
-			       type, size, store_size, (uintmax_t)offset);
-		else {
-			printf("%-6s %lu %lu %"PRIuMAX" %u %s\n",
-			       type, size, store_size, (uintmax_t)offset,
-			       delta_chain_length, sha1_to_hex(base_sha1));
-			if (delta_chain_length <= MAX_CHAIN)
-				chain_histogram[delta_chain_length]++;
-			else
-				chain_histogram[0]++;
-		}
-	}
-
-	for (i = 0; i <= MAX_CHAIN; i++) {
-		if (!chain_histogram[i])
-			continue;
-		printf("chain length = %"PRIu32": %"PRIu32" object%s\n", i,
-		       chain_histogram[i], chain_histogram[i] > 1 ? "s" : "");
-	}
-	if (chain_histogram[0])
-		printf("chain length > %d: %"PRIu32" object%s\n", MAX_CHAIN,
-		       chain_histogram[0], chain_histogram[0] > 1 ? "s" : "");
-}
-
-static int verify_one_pack(const char *path, int verbose)
-{
-	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;
-	}
-
-	/*
-	 * 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);
-
-	install_packed_git(pack);
-	err = verify_pack(pack);
-
-	if (verbose) {
-		if (err)
-			printf("%s: bad\n", pack->pack_name);
-		else {
-			show_pack_info(pack);
-			printf("%s: ok\n", pack->pack_name);
-		}
-	}
-
-	return err;
-}
-
-static const char * const verify_pack_usage[] = {
-	"git verify-pack [-v|--verbose] <pack>...",
-	NULL
-};
-
-int cmd_verify_pack(int argc, const char **argv, const char *prefix)
-{
-	int err = 0;
-	int verbose = 0;
-	int i;
-	const struct option verify_pack_options[] = {
-		OPT__VERBOSE(&verbose),
-		OPT_END()
-	};
-
-	git_config(git_default_config, NULL);
-	argc = parse_options(argc, argv, prefix, verify_pack_options,
-			     verify_pack_usage, 0);
-	if (argc < 1)
-		usage_with_options(verify_pack_usage, verify_pack_options);
-	for (i = 0; i < argc; i++) {
-		if (verify_one_pack(argv[i], verbose))
-			err = 1;
-		discard_revindex();
-	}
-
-	return err;
-}
diff --git a/builtin-verify-tag.c b/builtin-verify-tag.c
deleted file mode 100644
index 9f482c2..0000000
--- a/builtin-verify-tag.c
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Builtin "git verify-tag"
- *
- * Copyright (c) 2007 Carlos Rica <jasampler@gmail.com>
- *
- * Based on git-verify-tag.sh
- */
-#include "cache.h"
-#include "builtin.h"
-#include "tag.h"
-#include "run-command.h"
-#include <signal.h>
-#include "parse-options.h"
-
-static const char * const verify_tag_usage[] = {
-		"git verify-tag [-v|--verbose] <tag>...",
-		NULL
-};
-
-#define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----"
-
-static int run_gpg_verify(const char *buf, unsigned long size, int verbose)
-{
-	struct child_process gpg;
-	const char *args_gpg[] = {"gpg", "--verify", "FILE", "-", NULL};
-	char path[PATH_MAX], *eol;
-	size_t len;
-	int fd, ret;
-
-	fd = git_mkstemp(path, PATH_MAX, ".git_vtag_tmpXXXXXX");
-	if (fd < 0)
-		return error("could not create temporary file '%s': %s",
-						path, strerror(errno));
-	if (write_in_full(fd, buf, size) < 0)
-		return error("failed writing temporary file '%s': %s",
-						path, strerror(errno));
-	close(fd);
-
-	/* find the length without signature */
-	len = 0;
-	while (len < size && prefixcmp(buf + len, PGP_SIGNATURE)) {
-		eol = memchr(buf + len, '\n', size - len);
-		len += eol ? eol - (buf + len) + 1 : size - len;
-	}
-	if (verbose)
-		write_in_full(1, buf, len);
-
-	memset(&gpg, 0, sizeof(gpg));
-	gpg.argv = args_gpg;
-	gpg.in = -1;
-	args_gpg[2] = path;
-	if (start_command(&gpg)) {
-		unlink(path);
-		return error("could not run gpg.");
-	}
-
-	write_in_full(gpg.in, buf, len);
-	close(gpg.in);
-	ret = finish_command(&gpg);
-
-	unlink_or_warn(path);
-
-	return ret;
-}
-
-static int verify_tag(const char *name, int verbose)
-{
-	enum object_type type;
-	unsigned char sha1[20];
-	char *buf;
-	unsigned long size;
-	int ret;
-
-	if (get_sha1(name, sha1))
-		return error("tag '%s' not found.", name);
-
-	type = sha1_object_info(sha1, NULL);
-	if (type != OBJ_TAG)
-		return error("%s: cannot verify a non-tag object of type %s.",
-				name, typename(type));
-
-	buf = read_sha1_file(sha1, &type, &size);
-	if (!buf)
-		return error("%s: unable to read file.", name);
-
-	ret = run_gpg_verify(buf, size, verbose);
-
-	free(buf);
-	return ret;
-}
-
-int cmd_verify_tag(int argc, const char **argv, const char *prefix)
-{
-	int i = 1, verbose = 0, had_error = 0;
-	const struct option verify_tag_options[] = {
-		OPT__VERBOSE(&verbose),
-		OPT_END()
-	};
-
-	git_config(git_default_config, NULL);
-
-	argc = parse_options(argc, argv, prefix, verify_tag_options,
-			     verify_tag_usage, PARSE_OPT_KEEP_ARGV0);
-	if (argc <= i)
-		usage_with_options(verify_tag_usage, verify_tag_options);
-
-	/* sometimes the program was terminated because this signal
-	 * was received in the process of writing the gpg input: */
-	signal(SIGPIPE, SIG_IGN);
-	while (i < argc)
-		if (verify_tag(argv[i++], verbose))
-			had_error = 1;
-	return had_error;
-}
diff --git a/builtin.h b/builtin.h
index 20427d2..ba6626b 100644
--- a/builtin.h
+++ b/builtin.h
@@ -5,21 +5,45 @@
 #include "strbuf.h"
 #include "cache.h"
 #include "commit.h"
+#include "notes.h"
 
-extern const char git_version_string[];
+#define DEFAULT_MERGE_LOG_LEN 20
+
 extern const char git_usage_string[];
 extern const char git_more_info_string[];
 
-extern void list_common_cmds_help(void);
-extern const char *help_unknown_cmd(const char *cmd);
 extern void prune_packed_objects(int);
-extern int read_line_with_nul(char *buf, int size, FILE *file);
-extern int fmt_merge_msg(int merge_summary, struct strbuf *in,
-	struct strbuf *out);
-extern int commit_tree(const char *msg, unsigned char *tree,
-		struct commit_list *parents, unsigned char *ret,
-		const char *author);
+
+struct fmt_merge_msg_opts {
+	unsigned add_title:1;
+	int shortlog_len;
+};
+
+extern int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
+			 struct fmt_merge_msg_opts *);
+extern void commit_notes(struct notes_tree *t, const char *msg);
+
+struct notes_rewrite_cfg {
+	struct notes_tree **trees;
+	const char *cmd;
+	int enabled;
+	combine_notes_fn combine;
+	struct string_list *refs;
+	int refs_from_env;
+	int mode_from_env;
+};
+
+combine_notes_fn parse_combine_notes_fn(const char *v);
+struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd);
+int copy_note_for_rewrite(struct notes_rewrite_cfg *c,
+			  const unsigned char *from_obj, const unsigned char *to_obj);
+void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c);
+
 extern int check_pager_config(const char *cmd);
+struct diff_options;
+extern void setup_diff_pager(struct diff_options *);
+
+extern int textconv_object(const char *path, unsigned mode, const unsigned char *sha1, char **buf, unsigned long *buf_size);
 
 extern int cmd_add(int argc, const char **argv, const char *prefix);
 extern int cmd_annotate(int argc, const char **argv, const char *prefix);
@@ -38,9 +62,12 @@
 extern int cmd_cherry_pick(int argc, const char **argv, const char *prefix);
 extern int cmd_clone(int argc, const char **argv, const char *prefix);
 extern int cmd_clean(int argc, const char **argv, const char *prefix);
+extern int cmd_column(int argc, const char **argv, const char *prefix);
 extern int cmd_commit(int argc, const char **argv, const char *prefix);
 extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);
+extern int cmd_config(int argc, const char **argv, const char *prefix);
 extern int cmd_count_objects(int argc, const char **argv, const char *prefix);
+extern int cmd_credential(int argc, const char **argv, const char *prefix);
 extern int cmd_describe(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);
@@ -49,7 +76,6 @@
 extern int cmd_fast_export(int argc, const char **argv, const char *prefix);
 extern int cmd_fetch(int argc, const char **argv, const char *prefix);
 extern int cmd_fetch_pack(int argc, const char **argv, const char *prefix);
-extern int cmd_fetch__tool(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_for_each_ref(int argc, const char **argv, const char *prefix);
 extern int cmd_format_patch(int argc, const char **argv, const char *prefix);
@@ -57,8 +83,9 @@
 extern int cmd_gc(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_grep(int argc, const char **argv, const char *prefix);
+extern int cmd_hash_object(int argc, const char **argv, const char *prefix);
 extern int cmd_help(int argc, const char **argv, const char *prefix);
-extern int cmd_http_fetch(int argc, const char **argv, const char *prefix);
+extern int cmd_index_pack(int argc, const char **argv, const char *prefix);
 extern int cmd_init_db(int argc, const char **argv, const char *prefix);
 extern int cmd_log(int argc, const char **argv, const char *prefix);
 extern int cmd_log_reflog(int argc, const char **argv, const char *prefix);
@@ -69,14 +96,19 @@
 extern int cmd_mailsplit(int argc, const char **argv, const char *prefix);
 extern int cmd_merge(int argc, const char **argv, const char *prefix);
 extern int cmd_merge_base(int argc, const char **argv, const char *prefix);
+extern int cmd_merge_index(int argc, const char **argv, const char *prefix);
 extern int cmd_merge_ours(int argc, const char **argv, const char *prefix);
 extern int cmd_merge_file(int argc, const char **argv, const char *prefix);
 extern int cmd_merge_recursive(int argc, const char **argv, const char *prefix);
+extern int cmd_merge_tree(int argc, const char **argv, const char *prefix);
+extern int cmd_mktag(int argc, const char **argv, const char *prefix);
 extern int cmd_mktree(int argc, const char **argv, const char *prefix);
 extern int cmd_mv(int argc, const char **argv, const char *prefix);
 extern int cmd_name_rev(int argc, const char **argv, const char *prefix);
+extern int cmd_notes(int argc, const char **argv, const char *prefix);
 extern int cmd_pack_objects(int argc, const char **argv, const char *prefix);
-extern int cmd_pickaxe(int argc, const char **argv, const char *prefix);
+extern int cmd_pack_redundant(int argc, const char **argv, const char *prefix);
+extern int cmd_patch_id(int argc, const char **argv, const char *prefix);
 extern int cmd_prune(int argc, const char **argv, const char *prefix);
 extern int cmd_prune_packed(int argc, const char **argv, const char *prefix);
 extern int cmd_push(int argc, const char **argv, const char *prefix);
@@ -84,7 +116,9 @@
 extern int cmd_receive_pack(int argc, const char **argv, const char *prefix);
 extern int cmd_reflog(int argc, const char **argv, const char *prefix);
 extern int cmd_remote(int argc, const char **argv, const char *prefix);
-extern int cmd_config(int argc, const char **argv, const char *prefix);
+extern int cmd_remote_ext(int argc, const char **argv, const char *prefix);
+extern int cmd_remote_fd(int argc, const char **argv, const char *prefix);
+extern int cmd_repo_config(int argc, const char **argv, const char *prefix);
 extern int cmd_rerere(int argc, const char **argv, const char *prefix);
 extern int cmd_reset(int argc, const char **argv, const char *prefix);
 extern int cmd_rev_list(int argc, const char **argv, const char *prefix);
@@ -100,11 +134,14 @@
 extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);
 extern int cmd_tag(int argc, const char **argv, const char *prefix);
 extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);
+extern int cmd_unpack_file(int argc, const char **argv, const char *prefix);
 extern int cmd_unpack_objects(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_update_server_info(int argc, const char **argv, const char *prefix);
 extern int cmd_upload_archive(int argc, const char **argv, const char *prefix);
-extern int cmd_upload_tar(int argc, const char **argv, const char *prefix);
+extern int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix);
+extern int cmd_var(int argc, const char **argv, const char *prefix);
 extern int cmd_verify_tag(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, const char *prefix);
@@ -112,5 +149,6 @@
 extern int cmd_verify_pack(int argc, const char **argv, const char *prefix);
 extern int cmd_show_ref(int argc, const char **argv, const char *prefix);
 extern int cmd_pack_refs(int argc, const char **argv, const char *prefix);
+extern int cmd_replace(int argc, const char **argv, const char *prefix);
 
 #endif
diff --git a/builtin/add.c b/builtin/add.c
new file mode 100644
index 0000000..89dce56
--- /dev/null
+++ b/builtin/add.c
@@ -0,0 +1,484 @@
+/*
+ * "git add" builtin command
+ *
+ * Copyright (C) 2006 Linus Torvalds
+ */
+#include "cache.h"
+#include "builtin.h"
+#include "dir.h"
+#include "exec_cmd.h"
+#include "cache-tree.h"
+#include "run-command.h"
+#include "parse-options.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "revision.h"
+#include "bulk-checkin.h"
+
+static const char * const builtin_add_usage[] = {
+	"git add [options] [--] <filepattern>...",
+	NULL
+};
+static int patch_interactive, add_interactive, edit_interactive;
+static int take_worktree_changes;
+
+struct update_callback_data {
+	int flags;
+	int add_errors;
+};
+
+static int fix_unmerged_status(struct diff_filepair *p,
+			       struct update_callback_data *data)
+{
+	if (p->status != DIFF_STATUS_UNMERGED)
+		return p->status;
+	if (!(data->flags & ADD_CACHE_IGNORE_REMOVAL) && !p->two->mode)
+		/*
+		 * This is not an explicit add request, and the
+		 * path is missing from the working tree (deleted)
+		 */
+		return DIFF_STATUS_DELETED;
+	else
+		/*
+		 * Either an explicit add request, or path exists
+		 * in the working tree.  An attempt to explicitly
+		 * add a path that does not exist in the working tree
+		 * will be caught as an error by the caller immediately.
+		 */
+		return DIFF_STATUS_MODIFIED;
+}
+
+static void update_callback(struct diff_queue_struct *q,
+			    struct diff_options *opt, void *cbdata)
+{
+	int i;
+	struct update_callback_data *data = cbdata;
+
+	for (i = 0; i < q->nr; i++) {
+		struct diff_filepair *p = q->queue[i];
+		const char *path = p->one->path;
+		switch (fix_unmerged_status(p, data)) {
+		default:
+			die(_("unexpected diff status %c"), p->status);
+		case DIFF_STATUS_MODIFIED:
+		case DIFF_STATUS_TYPE_CHANGED:
+			if (add_file_to_index(&the_index, path, data->flags)) {
+				if (!(data->flags & ADD_CACHE_IGNORE_ERRORS))
+					die(_("updating files failed"));
+				data->add_errors++;
+			}
+			break;
+		case DIFF_STATUS_DELETED:
+			if (data->flags & ADD_CACHE_IGNORE_REMOVAL)
+				break;
+			if (!(data->flags & ADD_CACHE_PRETEND))
+				remove_file_from_index(&the_index, path);
+			if (data->flags & (ADD_CACHE_PRETEND|ADD_CACHE_VERBOSE))
+				printf(_("remove '%s'\n"), path);
+			break;
+		}
+	}
+}
+
+int add_files_to_cache(const char *prefix, const char **pathspec, int flags)
+{
+	struct update_callback_data data;
+	struct rev_info rev;
+	init_revisions(&rev, prefix);
+	setup_revisions(0, NULL, &rev, NULL);
+	init_pathspec(&rev.prune_data, pathspec);
+	rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
+	rev.diffopt.format_callback = update_callback;
+	data.flags = flags;
+	data.add_errors = 0;
+	rev.diffopt.format_callback_data = &data;
+	rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
+	run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
+	return !!data.add_errors;
+}
+
+static void fill_pathspec_matches(const char **pathspec, char *seen, int specs)
+{
+	int num_unmatched = 0, i;
+
+	/*
+	 * Since we are walking the index as if we were walking the directory,
+	 * we have to mark the matched pathspec as seen; otherwise we will
+	 * mistakenly think that the user gave a pathspec that did not match
+	 * anything.
+	 */
+	for (i = 0; i < specs; i++)
+		if (!seen[i])
+			num_unmatched++;
+	if (!num_unmatched)
+		return;
+	for (i = 0; i < active_nr; i++) {
+		struct cache_entry *ce = active_cache[i];
+		match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen);
+	}
+}
+
+static char *find_used_pathspec(const char **pathspec)
+{
+	char *seen;
+	int i;
+
+	for (i = 0; pathspec[i];  i++)
+		; /* just counting */
+	seen = xcalloc(i, 1);
+	fill_pathspec_matches(pathspec, seen, i);
+	return seen;
+}
+
+static char *prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)
+{
+	char *seen;
+	int i, specs;
+	struct dir_entry **src, **dst;
+
+	for (specs = 0; pathspec[specs];  specs++)
+		/* nothing */;
+	seen = xcalloc(specs, 1);
+
+	src = dst = dir->entries;
+	i = dir->nr;
+	while (--i >= 0) {
+		struct dir_entry *entry = *src++;
+		if (match_pathspec(pathspec, entry->name, entry->len,
+				   prefix, seen))
+			*dst++ = entry;
+	}
+	dir->nr = dst - dir->entries;
+	fill_pathspec_matches(pathspec, seen, specs);
+	return seen;
+}
+
+static void treat_gitlinks(const char **pathspec)
+{
+	int i;
+
+	if (!pathspec || !*pathspec)
+		return;
+
+	for (i = 0; i < active_nr; i++) {
+		struct cache_entry *ce = active_cache[i];
+		if (S_ISGITLINK(ce->ce_mode)) {
+			int len = ce_namelen(ce), j;
+			for (j = 0; pathspec[j]; j++) {
+				int len2 = strlen(pathspec[j]);
+				if (len2 <= len || pathspec[j][len] != '/' ||
+				    memcmp(ce->name, pathspec[j], len))
+					continue;
+				if (len2 == len + 1)
+					/* strip trailing slash */
+					pathspec[j] = xstrndup(ce->name, len);
+				else
+					die (_("Path '%s' is in submodule '%.*s'"),
+						pathspec[j], len, ce->name);
+			}
+		}
+	}
+}
+
+static void refresh(int verbose, const char **pathspec)
+{
+	char *seen;
+	int i, specs;
+
+	for (specs = 0; pathspec[specs];  specs++)
+		/* nothing */;
+	seen = xcalloc(specs, 1);
+	refresh_index(&the_index, verbose ? REFRESH_IN_PORCELAIN : REFRESH_QUIET,
+		      pathspec, seen, _("Unstaged changes after refreshing the index:"));
+	for (i = 0; i < specs; i++) {
+		if (!seen[i])
+			die(_("pathspec '%s' did not match any files"), pathspec[i]);
+	}
+        free(seen);
+}
+
+static const char **validate_pathspec(int argc, const char **argv, const char *prefix)
+{
+	const char **pathspec = get_pathspec(prefix, argv);
+
+	if (pathspec) {
+		const char **p;
+		for (p = pathspec; *p; p++) {
+			if (has_symlink_leading_path(*p, strlen(*p))) {
+				int len = prefix ? strlen(prefix) : 0;
+				die(_("'%s' is beyond a symbolic link"), *p + len);
+			}
+		}
+	}
+
+	return pathspec;
+}
+
+int run_add_interactive(const char *revision, const char *patch_mode,
+			const char **pathspec)
+{
+	int status, ac, pc = 0;
+	const char **args;
+
+	if (pathspec)
+		while (pathspec[pc])
+			pc++;
+
+	args = xcalloc(sizeof(const char *), (pc + 5));
+	ac = 0;
+	args[ac++] = "add--interactive";
+	if (patch_mode)
+		args[ac++] = patch_mode;
+	if (revision)
+		args[ac++] = revision;
+	args[ac++] = "--";
+	if (pc) {
+		memcpy(&(args[ac]), pathspec, sizeof(const char *) * pc);
+		ac += pc;
+	}
+	args[ac] = NULL;
+
+	status = run_command_v_opt(args, RUN_GIT_CMD);
+	free(args);
+	return status;
+}
+
+int interactive_add(int argc, const char **argv, const char *prefix, int patch)
+{
+	const char **pathspec = NULL;
+
+	if (argc) {
+		pathspec = validate_pathspec(argc, argv, prefix);
+		if (!pathspec)
+			return -1;
+	}
+
+	return run_add_interactive(NULL,
+				   patch ? "--patch" : NULL,
+				   pathspec);
+}
+
+static int edit_patch(int argc, const char **argv, const char *prefix)
+{
+	char *file = xstrdup(git_path("ADD_EDIT.patch"));
+	const char *apply_argv[] = { "apply", "--recount", "--cached",
+		NULL, NULL };
+	struct child_process child;
+	struct rev_info rev;
+	int out;
+	struct stat st;
+
+	apply_argv[3] = file;
+
+	git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
+
+	if (read_cache() < 0)
+		die (_("Could not read the index"));
+
+	init_revisions(&rev, prefix);
+	rev.diffopt.context = 7;
+
+	argc = setup_revisions(argc, argv, &rev, NULL);
+	rev.diffopt.output_format = DIFF_FORMAT_PATCH;
+	DIFF_OPT_SET(&rev.diffopt, IGNORE_DIRTY_SUBMODULES);
+	out = open(file, O_CREAT | O_WRONLY, 0666);
+	if (out < 0)
+		die (_("Could not open '%s' for writing."), file);
+	rev.diffopt.file = xfdopen(out, "w");
+	rev.diffopt.close_file = 1;
+	if (run_diff_files(&rev, 0))
+		die (_("Could not write patch"));
+
+	launch_editor(file, NULL, NULL);
+
+	if (stat(file, &st))
+		die_errno(_("Could not stat '%s'"), file);
+	if (!st.st_size)
+		die(_("Empty patch. Aborted."));
+
+	memset(&child, 0, sizeof(child));
+	child.git_cmd = 1;
+	child.argv = apply_argv;
+	if (run_command(&child))
+		die (_("Could not apply '%s'"), file);
+
+	unlink(file);
+	return 0;
+}
+
+static struct lock_file lock_file;
+
+static const char ignore_error[] =
+N_("The following paths are ignored by one of your .gitignore files:\n");
+
+static int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0;
+static int ignore_add_errors, addremove, intent_to_add, ignore_missing = 0;
+
+static struct option builtin_add_options[] = {
+	OPT__DRY_RUN(&show_only, "dry run"),
+	OPT__VERBOSE(&verbose, "be verbose"),
+	OPT_GROUP(""),
+	OPT_BOOLEAN('i', "interactive", &add_interactive, "interactive picking"),
+	OPT_BOOLEAN('p', "patch", &patch_interactive, "select hunks interactively"),
+	OPT_BOOLEAN('e', "edit", &edit_interactive, "edit current diff and apply"),
+	OPT__FORCE(&ignored_too, "allow adding otherwise ignored files"),
+	OPT_BOOLEAN('u', "update", &take_worktree_changes, "update tracked files"),
+	OPT_BOOLEAN('N', "intent-to-add", &intent_to_add, "record only the fact that the path will be added later"),
+	OPT_BOOLEAN('A', "all", &addremove, "add changes from all tracked and untracked files"),
+	OPT_BOOLEAN( 0 , "refresh", &refresh_only, "don't add, only refresh the index"),
+	OPT_BOOLEAN( 0 , "ignore-errors", &ignore_add_errors, "just skip files which cannot be added because of errors"),
+	OPT_BOOLEAN( 0 , "ignore-missing", &ignore_missing, "check if - even missing - files are ignored in dry run"),
+	OPT_END(),
+};
+
+static int add_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, "add.ignoreerrors") ||
+	    !strcmp(var, "add.ignore-errors")) {
+		ignore_add_errors = git_config_bool(var, value);
+		return 0;
+	}
+	return git_default_config(var, value, cb);
+}
+
+static int add_files(struct dir_struct *dir, int flags)
+{
+	int i, exit_status = 0;
+
+	if (dir->ignored_nr) {
+		fprintf(stderr, _(ignore_error));
+		for (i = 0; i < dir->ignored_nr; i++)
+			fprintf(stderr, "%s\n", dir->ignored[i]->name);
+		fprintf(stderr, _("Use -f if you really want to add them.\n"));
+		die(_("no files added"));
+	}
+
+	for (i = 0; i < dir->nr; i++)
+		if (add_file_to_cache(dir->entries[i]->name, flags)) {
+			if (!ignore_add_errors)
+				die(_("adding files failed"));
+			exit_status = 1;
+		}
+	return exit_status;
+}
+
+int cmd_add(int argc, const char **argv, const char *prefix)
+{
+	int exit_status = 0;
+	int newfd;
+	const char **pathspec;
+	struct dir_struct dir;
+	int flags;
+	int add_new_files;
+	int require_pathspec;
+	char *seen = NULL;
+
+	git_config(add_config, NULL);
+
+	argc = parse_options(argc, argv, prefix, builtin_add_options,
+			  builtin_add_usage, PARSE_OPT_KEEP_ARGV0);
+	if (patch_interactive)
+		add_interactive = 1;
+	if (add_interactive)
+		exit(interactive_add(argc - 1, argv + 1, prefix, patch_interactive));
+
+	if (edit_interactive)
+		return(edit_patch(argc, argv, prefix));
+	argc--;
+	argv++;
+
+	if (addremove && take_worktree_changes)
+		die(_("-A and -u are mutually incompatible"));
+	if (!show_only && ignore_missing)
+		die(_("Option --ignore-missing can only be used together with --dry-run"));
+	if ((addremove || take_worktree_changes) && !argc) {
+		static const char *here[2] = { ".", NULL };
+		argc = 1;
+		argv = here;
+	}
+
+	add_new_files = !take_worktree_changes && !refresh_only;
+	require_pathspec = !take_worktree_changes;
+
+	newfd = hold_locked_index(&lock_file, 1);
+
+	flags = ((verbose ? ADD_CACHE_VERBOSE : 0) |
+		 (show_only ? ADD_CACHE_PRETEND : 0) |
+		 (intent_to_add ? ADD_CACHE_INTENT : 0) |
+		 (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) |
+		 (!(addremove || take_worktree_changes)
+		  ? ADD_CACHE_IGNORE_REMOVAL : 0));
+
+	if (require_pathspec && argc == 0) {
+		fprintf(stderr, _("Nothing specified, nothing added.\n"));
+		fprintf(stderr, _("Maybe you wanted to say 'git add .'?\n"));
+		return 0;
+	}
+	pathspec = validate_pathspec(argc, argv, prefix);
+
+	if (read_cache() < 0)
+		die(_("index file corrupt"));
+	treat_gitlinks(pathspec);
+
+	if (add_new_files) {
+		int baselen;
+
+		/* Set up the default git porcelain excludes */
+		memset(&dir, 0, sizeof(dir));
+		if (!ignored_too) {
+			dir.flags |= DIR_COLLECT_IGNORED;
+			setup_standard_excludes(&dir);
+		}
+
+		/* This picks up the paths that are not tracked */
+		baselen = fill_directory(&dir, pathspec);
+		if (pathspec)
+			seen = prune_directory(&dir, pathspec, baselen);
+	}
+
+	if (refresh_only) {
+		refresh(verbose, pathspec);
+		goto finish;
+	}
+
+	if (pathspec) {
+		int i;
+		struct path_exclude_check check;
+
+		path_exclude_check_init(&check, &dir);
+		if (!seen)
+			seen = find_used_pathspec(pathspec);
+		for (i = 0; pathspec[i]; i++) {
+			if (!seen[i] && pathspec[i][0]
+			    && !file_exists(pathspec[i])) {
+				if (ignore_missing) {
+					int dtype = DT_UNKNOWN;
+					if (path_excluded(&check, pathspec[i], -1, &dtype))
+						dir_add_ignored(&dir, pathspec[i], strlen(pathspec[i]));
+				} else
+					die(_("pathspec '%s' did not match any files"),
+					    pathspec[i]);
+			}
+		}
+		free(seen);
+		path_exclude_check_clear(&check);
+	}
+
+	plug_bulk_checkin();
+
+	exit_status |= add_files_to_cache(prefix, pathspec, flags);
+
+	if (add_new_files)
+		exit_status |= add_files(&dir, flags);
+
+	unplug_bulk_checkin();
+
+ finish:
+	if (active_cache_changed) {
+		if (write_cache(newfd, active_cache, active_nr) ||
+		    commit_locked_index(&lock_file))
+			die(_("Unable to write new index file"));
+	}
+
+	return exit_status;
+}
diff --git a/builtin-annotate.c b/builtin/annotate.c
similarity index 100%
rename from builtin-annotate.c
rename to builtin/annotate.c
diff --git a/builtin/apply.c b/builtin/apply.c
new file mode 100644
index 0000000..d453c83
--- /dev/null
+++ b/builtin/apply.c
@@ -0,0 +1,4437 @@
+/*
+ * apply.c
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ *
+ * This applies patches on top of some (arbitrary) version of the SCM.
+ *
+ */
+#include "cache.h"
+#include "cache-tree.h"
+#include "quote.h"
+#include "blob.h"
+#include "delta.h"
+#include "builtin.h"
+#include "string-list.h"
+#include "dir.h"
+#include "diff.h"
+#include "parse-options.h"
+#include "xdiff-interface.h"
+#include "ll-merge.h"
+#include "rerere.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.
+ */
+static const char *prefix;
+static int prefix_length = -1;
+static int newfd = -1;
+
+static int unidiff_zero;
+static int p_value = 1;
+static int p_value_known;
+static int check_index;
+static int update_index;
+static int cached;
+static int diffstat;
+static int numstat;
+static int summary;
+static int check;
+static int apply = 1;
+static int apply_in_reverse;
+static int apply_with_reject;
+static int apply_verbosely;
+static int allow_overlap;
+static int no_add;
+static int threeway;
+static const char *fake_ancestor;
+static int line_termination = '\n';
+static unsigned int p_context = UINT_MAX;
+static const char * const apply_usage[] = {
+	N_("git apply [options] [<patch>...]"),
+	NULL
+};
+
+static enum ws_error_action {
+	nowarn_ws_error,
+	warn_on_ws_error,
+	die_on_ws_error,
+	correct_ws_error
+} ws_error_action = warn_on_ws_error;
+static int whitespace_error;
+static int squelch_whitespace_errors = 5;
+static int applied_after_fixing_ws;
+
+static enum ws_ignore {
+	ignore_ws_none,
+	ignore_ws_change
+} ws_ignore_action = ignore_ws_none;
+
+
+static const char *patch_input_file;
+static const char *root;
+static int root_len;
+static int read_stdin = 1;
+static int options;
+
+static void parse_whitespace_option(const char *option)
+{
+	if (!option) {
+		ws_error_action = warn_on_ws_error;
+		return;
+	}
+	if (!strcmp(option, "warn")) {
+		ws_error_action = warn_on_ws_error;
+		return;
+	}
+	if (!strcmp(option, "nowarn")) {
+		ws_error_action = nowarn_ws_error;
+		return;
+	}
+	if (!strcmp(option, "error")) {
+		ws_error_action = die_on_ws_error;
+		return;
+	}
+	if (!strcmp(option, "error-all")) {
+		ws_error_action = die_on_ws_error;
+		squelch_whitespace_errors = 0;
+		return;
+	}
+	if (!strcmp(option, "strip") || !strcmp(option, "fix")) {
+		ws_error_action = correct_ws_error;
+		return;
+	}
+	die(_("unrecognized whitespace option '%s'"), option);
+}
+
+static void parse_ignorewhitespace_option(const char *option)
+{
+	if (!option || !strcmp(option, "no") ||
+	    !strcmp(option, "false") || !strcmp(option, "never") ||
+	    !strcmp(option, "none")) {
+		ws_ignore_action = ignore_ws_none;
+		return;
+	}
+	if (!strcmp(option, "change")) {
+		ws_ignore_action = ignore_ws_change;
+		return;
+	}
+	die(_("unrecognized whitespace ignore option '%s'"), option);
+}
+
+static void set_default_whitespace_mode(const char *whitespace_option)
+{
+	if (!whitespace_option && !apply_default_whitespace)
+		ws_error_action = (apply ? warn_on_ws_error : nowarn_ws_error);
+}
+
+/*
+ * For "diff-stat" like behaviour, we keep track of the biggest change
+ * we've seen, and the longest filename. That allows us to do simple
+ * scaling.
+ */
+static int max_change, max_len;
+
+/*
+ * Various "current state", notably line numbers and what
+ * file (and how) we're patching right now.. The "is_xxxx"
+ * things are flags, where -1 means "don't know yet".
+ */
+static int linenr = 1;
+
+/*
+ * This represents one "hunk" from a patch, starting with
+ * "@@ -oldpos,oldlines +newpos,newlines @@" marker.  The
+ * patch text is pointed at by patch, and its byte length
+ * is stored in size.  leading and trailing are the number
+ * of context lines.
+ */
+struct fragment {
+	unsigned long leading, trailing;
+	unsigned long oldpos, oldlines;
+	unsigned long newpos, newlines;
+	/*
+	 * 'patch' is usually borrowed from buf in apply_patch(),
+	 * but some codepaths store an allocated buffer.
+	 */
+	const char *patch;
+	unsigned free_patch:1,
+		rejected:1;
+	int size;
+	int linenr;
+	struct fragment *next;
+};
+
+/*
+ * When dealing with a binary patch, we reuse "leading" field
+ * to store the type of the binary hunk, either deflated "delta"
+ * or deflated "literal".
+ */
+#define binary_patch_method leading
+#define BINARY_DELTA_DEFLATED	1
+#define BINARY_LITERAL_DEFLATED 2
+
+/*
+ * This represents a "patch" to a file, both metainfo changes
+ * such as creation/deletion, filemode and content changes represented
+ * as a series of fragments.
+ */
+struct patch {
+	char *new_name, *old_name, *def_name;
+	unsigned int old_mode, new_mode;
+	int is_new, is_delete;	/* -1 = unknown, 0 = false, 1 = true */
+	int rejected;
+	unsigned ws_rule;
+	unsigned long deflate_origlen;
+	int lines_added, lines_deleted;
+	int score;
+	unsigned int is_toplevel_relative:1;
+	unsigned int inaccurate_eof:1;
+	unsigned int is_binary:1;
+	unsigned int is_copy:1;
+	unsigned int is_rename:1;
+	unsigned int recount:1;
+	unsigned int conflicted_threeway:1;
+	unsigned int direct_to_threeway:1;
+	struct fragment *fragments;
+	char *result;
+	size_t resultsize;
+	char old_sha1_prefix[41];
+	char new_sha1_prefix[41];
+	struct patch *next;
+
+	/* three-way fallback result */
+	unsigned char threeway_stage[3][20];
+};
+
+static void free_fragment_list(struct fragment *list)
+{
+	while (list) {
+		struct fragment *next = list->next;
+		if (list->free_patch)
+			free((char *)list->patch);
+		free(list);
+		list = next;
+	}
+}
+
+static void free_patch(struct patch *patch)
+{
+	free_fragment_list(patch->fragments);
+	free(patch->def_name);
+	free(patch->old_name);
+	free(patch->new_name);
+	free(patch->result);
+	free(patch);
+}
+
+static void free_patch_list(struct patch *list)
+{
+	while (list) {
+		struct patch *next = list->next;
+		free_patch(list);
+		list = next;
+	}
+}
+
+/*
+ * A line in a file, len-bytes long (includes the terminating LF,
+ * except for an incomplete line at the end if the file ends with
+ * one), and its contents hashes to 'hash'.
+ */
+struct line {
+	size_t len;
+	unsigned hash : 24;
+	unsigned flag : 8;
+#define LINE_COMMON     1
+#define LINE_PATCHED	2
+};
+
+/*
+ * This represents a "file", which is an array of "lines".
+ */
+struct image {
+	char *buf;
+	size_t len;
+	size_t nr;
+	size_t alloc;
+	struct line *line_allocated;
+	struct line *line;
+};
+
+/*
+ * Records filenames that have been touched, in order to handle
+ * the case where more than one patches touch the same file.
+ */
+
+static struct string_list fn_table;
+
+static uint32_t hash_line(const char *cp, size_t len)
+{
+	size_t i;
+	uint32_t h;
+	for (i = 0, h = 0; i < len; i++) {
+		if (!isspace(cp[i])) {
+			h = h * 3 + (cp[i] & 0xff);
+		}
+	}
+	return h;
+}
+
+/*
+ * Compare lines s1 of length n1 and s2 of length n2, ignoring
+ * whitespace difference. Returns 1 if they match, 0 otherwise
+ */
+static int fuzzy_matchlines(const char *s1, size_t n1,
+			    const char *s2, size_t n2)
+{
+	const char *last1 = s1 + n1 - 1;
+	const char *last2 = s2 + n2 - 1;
+	int result = 0;
+
+	/* ignore line endings */
+	while ((*last1 == '\r') || (*last1 == '\n'))
+		last1--;
+	while ((*last2 == '\r') || (*last2 == '\n'))
+		last2--;
+
+	/* skip leading whitespace */
+	while (isspace(*s1) && (s1 <= last1))
+		s1++;
+	while (isspace(*s2) && (s2 <= last2))
+		s2++;
+	/* early return if both lines are empty */
+	if ((s1 > last1) && (s2 > last2))
+		return 1;
+	while (!result) {
+		result = *s1++ - *s2++;
+		/*
+		 * Skip whitespace inside. We check for whitespace on
+		 * both buffers because we don't want "a b" to match
+		 * "ab"
+		 */
+		if (isspace(*s1) && isspace(*s2)) {
+			while (isspace(*s1) && s1 <= last1)
+				s1++;
+			while (isspace(*s2) && s2 <= last2)
+				s2++;
+		}
+		/*
+		 * If we reached the end on one side only,
+		 * lines don't match
+		 */
+		if (
+		    ((s2 > last2) && (s1 <= last1)) ||
+		    ((s1 > last1) && (s2 <= last2)))
+			return 0;
+		if ((s1 > last1) && (s2 > last2))
+			break;
+	}
+
+	return !result;
+}
+
+static void add_line_info(struct image *img, const char *bol, size_t len, unsigned flag)
+{
+	ALLOC_GROW(img->line_allocated, img->nr + 1, img->alloc);
+	img->line_allocated[img->nr].len = len;
+	img->line_allocated[img->nr].hash = hash_line(bol, len);
+	img->line_allocated[img->nr].flag = flag;
+	img->nr++;
+}
+
+/*
+ * "buf" has the file contents to be patched (read from various sources).
+ * attach it to "image" and add line-based index to it.
+ * "image" now owns the "buf".
+ */
+static void prepare_image(struct image *image, char *buf, size_t len,
+			  int prepare_linetable)
+{
+	const char *cp, *ep;
+
+	memset(image, 0, sizeof(*image));
+	image->buf = buf;
+	image->len = len;
+
+	if (!prepare_linetable)
+		return;
+
+	ep = image->buf + image->len;
+	cp = image->buf;
+	while (cp < ep) {
+		const char *next;
+		for (next = cp; next < ep && *next != '\n'; next++)
+			;
+		if (next < ep)
+			next++;
+		add_line_info(image, cp, next - cp, 0);
+		cp = next;
+	}
+	image->line = image->line_allocated;
+}
+
+static void clear_image(struct image *image)
+{
+	free(image->buf);
+	free(image->line_allocated);
+	memset(image, 0, sizeof(*image));
+}
+
+/* fmt must contain _one_ %s and no other substitution */
+static void say_patch_name(FILE *output, const char *fmt, struct patch *patch)
+{
+	struct strbuf sb = STRBUF_INIT;
+
+	if (patch->old_name && patch->new_name &&
+	    strcmp(patch->old_name, patch->new_name)) {
+		quote_c_style(patch->old_name, &sb, NULL, 0);
+		strbuf_addstr(&sb, " => ");
+		quote_c_style(patch->new_name, &sb, NULL, 0);
+	} else {
+		const char *n = patch->new_name;
+		if (!n)
+			n = patch->old_name;
+		quote_c_style(n, &sb, NULL, 0);
+	}
+	fprintf(output, fmt, sb.buf);
+	fputc('\n', output);
+	strbuf_release(&sb);
+}
+
+#define SLOP (16)
+
+static void read_patch_file(struct strbuf *sb, int fd)
+{
+	if (strbuf_read(sb, fd, 0) < 0)
+		die_errno("git apply: failed to read");
+
+	/*
+	 * Make sure that we have some slop in the buffer
+	 * so that we can do speculative "memcmp" etc, and
+	 * see to it that it is NUL-filled.
+	 */
+	strbuf_grow(sb, SLOP);
+	memset(sb->buf + sb->len, 0, SLOP);
+}
+
+static unsigned long linelen(const char *buffer, unsigned long size)
+{
+	unsigned long len = 0;
+	while (size--) {
+		len++;
+		if (*buffer++ == '\n')
+			break;
+	}
+	return len;
+}
+
+static int is_dev_null(const char *str)
+{
+	return !memcmp("/dev/null", str, 9) && isspace(str[9]);
+}
+
+#define TERM_SPACE	1
+#define TERM_TAB	2
+
+static int name_terminate(const char *name, int namelen, int c, int terminate)
+{
+	if (c == ' ' && !(terminate & TERM_SPACE))
+		return 0;
+	if (c == '\t' && !(terminate & TERM_TAB))
+		return 0;
+
+	return 1;
+}
+
+/* remove double slashes to make --index work with such filenames */
+static char *squash_slash(char *name)
+{
+	int i = 0, j = 0;
+
+	if (!name)
+		return NULL;
+
+	while (name[i]) {
+		if ((name[j++] = name[i++]) == '/')
+			while (name[i] == '/')
+				i++;
+	}
+	name[j] = '\0';
+	return name;
+}
+
+static char *find_name_gnu(const char *line, const char *def, int p_value)
+{
+	struct strbuf name = STRBUF_INIT;
+	char *cp;
+
+	/*
+	 * Proposed "new-style" GNU patch/diff format; see
+	 * http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2
+	 */
+	if (unquote_c_style(&name, line, NULL)) {
+		strbuf_release(&name);
+		return NULL;
+	}
+
+	for (cp = name.buf; p_value; p_value--) {
+		cp = strchr(cp, '/');
+		if (!cp) {
+			strbuf_release(&name);
+			return NULL;
+		}
+		cp++;
+	}
+
+	strbuf_remove(&name, 0, cp - name.buf);
+	if (root)
+		strbuf_insert(&name, 0, root, root_len);
+	return squash_slash(strbuf_detach(&name, NULL));
+}
+
+static size_t sane_tz_len(const char *line, size_t len)
+{
+	const char *tz, *p;
+
+	if (len < strlen(" +0500") || line[len-strlen(" +0500")] != ' ')
+		return 0;
+	tz = line + len - strlen(" +0500");
+
+	if (tz[1] != '+' && tz[1] != '-')
+		return 0;
+
+	for (p = tz + 2; p != line + len; p++)
+		if (!isdigit(*p))
+			return 0;
+
+	return line + len - tz;
+}
+
+static size_t tz_with_colon_len(const char *line, size_t len)
+{
+	const char *tz, *p;
+
+	if (len < strlen(" +08:00") || line[len - strlen(":00")] != ':')
+		return 0;
+	tz = line + len - strlen(" +08:00");
+
+	if (tz[0] != ' ' || (tz[1] != '+' && tz[1] != '-'))
+		return 0;
+	p = tz + 2;
+	if (!isdigit(*p++) || !isdigit(*p++) || *p++ != ':' ||
+	    !isdigit(*p++) || !isdigit(*p++))
+		return 0;
+
+	return line + len - tz;
+}
+
+static size_t date_len(const char *line, size_t len)
+{
+	const char *date, *p;
+
+	if (len < strlen("72-02-05") || line[len-strlen("-05")] != '-')
+		return 0;
+	p = date = line + len - strlen("72-02-05");
+
+	if (!isdigit(*p++) || !isdigit(*p++) || *p++ != '-' ||
+	    !isdigit(*p++) || !isdigit(*p++) || *p++ != '-' ||
+	    !isdigit(*p++) || !isdigit(*p++))	/* Not a date. */
+		return 0;
+
+	if (date - line >= strlen("19") &&
+	    isdigit(date[-1]) && isdigit(date[-2]))	/* 4-digit year */
+		date -= strlen("19");
+
+	return line + len - date;
+}
+
+static size_t short_time_len(const char *line, size_t len)
+{
+	const char *time, *p;
+
+	if (len < strlen(" 07:01:32") || line[len-strlen(":32")] != ':')
+		return 0;
+	p = time = line + len - strlen(" 07:01:32");
+
+	/* Permit 1-digit hours? */
+	if (*p++ != ' ' ||
+	    !isdigit(*p++) || !isdigit(*p++) || *p++ != ':' ||
+	    !isdigit(*p++) || !isdigit(*p++) || *p++ != ':' ||
+	    !isdigit(*p++) || !isdigit(*p++))	/* Not a time. */
+		return 0;
+
+	return line + len - time;
+}
+
+static size_t fractional_time_len(const char *line, size_t len)
+{
+	const char *p;
+	size_t n;
+
+	/* Expected format: 19:41:17.620000023 */
+	if (!len || !isdigit(line[len - 1]))
+		return 0;
+	p = line + len - 1;
+
+	/* Fractional seconds. */
+	while (p > line && isdigit(*p))
+		p--;
+	if (*p != '.')
+		return 0;
+
+	/* Hours, minutes, and whole seconds. */
+	n = short_time_len(line, p - line);
+	if (!n)
+		return 0;
+
+	return line + len - p + n;
+}
+
+static size_t trailing_spaces_len(const char *line, size_t len)
+{
+	const char *p;
+
+	/* Expected format: ' ' x (1 or more)  */
+	if (!len || line[len - 1] != ' ')
+		return 0;
+
+	p = line + len;
+	while (p != line) {
+		p--;
+		if (*p != ' ')
+			return line + len - (p + 1);
+	}
+
+	/* All spaces! */
+	return len;
+}
+
+static size_t diff_timestamp_len(const char *line, size_t len)
+{
+	const char *end = line + len;
+	size_t n;
+
+	/*
+	 * Posix: 2010-07-05 19:41:17
+	 * GNU: 2010-07-05 19:41:17.620000023 -0500
+	 */
+
+	if (!isdigit(end[-1]))
+		return 0;
+
+	n = sane_tz_len(line, end - line);
+	if (!n)
+		n = tz_with_colon_len(line, end - line);
+	end -= n;
+
+	n = short_time_len(line, end - line);
+	if (!n)
+		n = fractional_time_len(line, end - line);
+	end -= n;
+
+	n = date_len(line, end - line);
+	if (!n)	/* No date.  Too bad. */
+		return 0;
+	end -= n;
+
+	if (end == line)	/* No space before date. */
+		return 0;
+	if (end[-1] == '\t') {	/* Success! */
+		end--;
+		return line + len - end;
+	}
+	if (end[-1] != ' ')	/* No space before date. */
+		return 0;
+
+	/* Whitespace damage. */
+	end -= trailing_spaces_len(line, end - line);
+	return line + len - end;
+}
+
+static char *null_strdup(const char *s)
+{
+	return s ? xstrdup(s) : NULL;
+}
+
+static char *find_name_common(const char *line, const char *def,
+			      int p_value, const char *end, int terminate)
+{
+	int len;
+	const char *start = NULL;
+
+	if (p_value == 0)
+		start = line;
+	while (line != end) {
+		char c = *line;
+
+		if (!end && isspace(c)) {
+			if (c == '\n')
+				break;
+			if (name_terminate(start, line-start, c, terminate))
+				break;
+		}
+		line++;
+		if (c == '/' && !--p_value)
+			start = line;
+	}
+	if (!start)
+		return squash_slash(null_strdup(def));
+	len = line - start;
+	if (!len)
+		return squash_slash(null_strdup(def));
+
+	/*
+	 * Generally we prefer the shorter name, especially
+	 * if the other one is just a variation of that with
+	 * something else tacked on to the end (ie "file.orig"
+	 * or "file~").
+	 */
+	if (def) {
+		int deflen = strlen(def);
+		if (deflen < len && !strncmp(start, def, deflen))
+			return squash_slash(xstrdup(def));
+	}
+
+	if (root) {
+		char *ret = xmalloc(root_len + len + 1);
+		strcpy(ret, root);
+		memcpy(ret + root_len, start, len);
+		ret[root_len + len] = '\0';
+		return squash_slash(ret);
+	}
+
+	return squash_slash(xmemdupz(start, len));
+}
+
+static char *find_name(const char *line, char *def, int p_value, int terminate)
+{
+	if (*line == '"') {
+		char *name = find_name_gnu(line, def, p_value);
+		if (name)
+			return name;
+	}
+
+	return find_name_common(line, def, p_value, NULL, terminate);
+}
+
+static char *find_name_traditional(const char *line, char *def, int p_value)
+{
+	size_t len = strlen(line);
+	size_t date_len;
+
+	if (*line == '"') {
+		char *name = find_name_gnu(line, def, p_value);
+		if (name)
+			return name;
+	}
+
+	len = strchrnul(line, '\n') - line;
+	date_len = diff_timestamp_len(line, len);
+	if (!date_len)
+		return find_name_common(line, def, p_value, NULL, TERM_TAB);
+	len -= date_len;
+
+	return find_name_common(line, def, p_value, line + len, 0);
+}
+
+static int count_slashes(const char *cp)
+{
+	int cnt = 0;
+	char ch;
+
+	while ((ch = *cp++))
+		if (ch == '/')
+			cnt++;
+	return cnt;
+}
+
+/*
+ * Given the string after "--- " or "+++ ", guess the appropriate
+ * p_value for the given patch.
+ */
+static int guess_p_value(const char *nameline)
+{
+	char *name, *cp;
+	int val = -1;
+
+	if (is_dev_null(nameline))
+		return -1;
+	name = find_name_traditional(nameline, NULL, 0);
+	if (!name)
+		return -1;
+	cp = strchr(name, '/');
+	if (!cp)
+		val = 0;
+	else if (prefix) {
+		/*
+		 * Does it begin with "a/$our-prefix" and such?  Then this is
+		 * very likely to apply to our directory.
+		 */
+		if (!strncmp(name, prefix, prefix_length))
+			val = count_slashes(prefix);
+		else {
+			cp++;
+			if (!strncmp(cp, prefix, prefix_length))
+				val = count_slashes(prefix) + 1;
+		}
+	}
+	free(name);
+	return val;
+}
+
+/*
+ * Does the ---/+++ line has the POSIX timestamp after the last HT?
+ * GNU diff puts epoch there to signal a creation/deletion event.  Is
+ * this such a timestamp?
+ */
+static int has_epoch_timestamp(const char *nameline)
+{
+	/*
+	 * We are only interested in epoch timestamp; any non-zero
+	 * fraction cannot be one, hence "(\.0+)?" in the regexp below.
+	 * For the same reason, the date must be either 1969-12-31 or
+	 * 1970-01-01, and the seconds part must be "00".
+	 */
+	const char stamp_regexp[] =
+		"^(1969-12-31|1970-01-01)"
+		" "
+		"[0-2][0-9]:[0-5][0-9]:00(\\.0+)?"
+		" "
+		"([-+][0-2][0-9]:?[0-5][0-9])\n";
+	const char *timestamp = NULL, *cp, *colon;
+	static regex_t *stamp;
+	regmatch_t m[10];
+	int zoneoffset;
+	int hourminute;
+	int status;
+
+	for (cp = nameline; *cp != '\n'; cp++) {
+		if (*cp == '\t')
+			timestamp = cp + 1;
+	}
+	if (!timestamp)
+		return 0;
+	if (!stamp) {
+		stamp = xmalloc(sizeof(*stamp));
+		if (regcomp(stamp, stamp_regexp, REG_EXTENDED)) {
+			warning(_("Cannot prepare timestamp regexp %s"),
+				stamp_regexp);
+			return 0;
+		}
+	}
+
+	status = regexec(stamp, timestamp, ARRAY_SIZE(m), m, 0);
+	if (status) {
+		if (status != REG_NOMATCH)
+			warning(_("regexec returned %d for input: %s"),
+				status, timestamp);
+		return 0;
+	}
+
+	zoneoffset = strtol(timestamp + m[3].rm_so + 1, (char **) &colon, 10);
+	if (*colon == ':')
+		zoneoffset = zoneoffset * 60 + strtol(colon + 1, NULL, 10);
+	else
+		zoneoffset = (zoneoffset / 100) * 60 + (zoneoffset % 100);
+	if (timestamp[m[3].rm_so] == '-')
+		zoneoffset = -zoneoffset;
+
+	/*
+	 * YYYY-MM-DD hh:mm:ss must be from either 1969-12-31
+	 * (west of GMT) or 1970-01-01 (east of GMT)
+	 */
+	if ((zoneoffset < 0 && memcmp(timestamp, "1969-12-31", 10)) ||
+	    (0 <= zoneoffset && memcmp(timestamp, "1970-01-01", 10)))
+		return 0;
+
+	hourminute = (strtol(timestamp + 11, NULL, 10) * 60 +
+		      strtol(timestamp + 14, NULL, 10) -
+		      zoneoffset);
+
+	return ((zoneoffset < 0 && hourminute == 1440) ||
+		(0 <= zoneoffset && !hourminute));
+}
+
+/*
+ * Get the name etc info from the ---/+++ lines of a traditional patch header
+ *
+ * FIXME! The end-of-filename heuristics are kind of screwy. For existing
+ * files, we can happily check the index for a match, but for creating a
+ * new file we should try to match whatever "patch" does. I have no idea.
+ */
+static void parse_traditional_patch(const char *first, const char *second, struct patch *patch)
+{
+	char *name;
+
+	first += 4;	/* skip "--- " */
+	second += 4;	/* skip "+++ " */
+	if (!p_value_known) {
+		int p, q;
+		p = guess_p_value(first);
+		q = guess_p_value(second);
+		if (p < 0) p = q;
+		if (0 <= p && p == q) {
+			p_value = p;
+			p_value_known = 1;
+		}
+	}
+	if (is_dev_null(first)) {
+		patch->is_new = 1;
+		patch->is_delete = 0;
+		name = find_name_traditional(second, NULL, p_value);
+		patch->new_name = name;
+	} else if (is_dev_null(second)) {
+		patch->is_new = 0;
+		patch->is_delete = 1;
+		name = find_name_traditional(first, NULL, p_value);
+		patch->old_name = name;
+	} else {
+		char *first_name;
+		first_name = find_name_traditional(first, NULL, p_value);
+		name = find_name_traditional(second, first_name, p_value);
+		free(first_name);
+		if (has_epoch_timestamp(first)) {
+			patch->is_new = 1;
+			patch->is_delete = 0;
+			patch->new_name = name;
+		} else if (has_epoch_timestamp(second)) {
+			patch->is_new = 0;
+			patch->is_delete = 1;
+			patch->old_name = name;
+		} else {
+			patch->old_name = name;
+			patch->new_name = xstrdup(name);
+		}
+	}
+	if (!name)
+		die(_("unable to find filename in patch at line %d"), linenr);
+}
+
+static int gitdiff_hdrend(const char *line, struct patch *patch)
+{
+	return -1;
+}
+
+/*
+ * We're anal about diff header consistency, to make
+ * sure that we don't end up having strange ambiguous
+ * patches floating around.
+ *
+ * As a result, gitdiff_{old|new}name() will check
+ * their names against any previous information, just
+ * to make sure..
+ */
+#define DIFF_OLD_NAME 0
+#define DIFF_NEW_NAME 1
+
+static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, int side)
+{
+	if (!orig_name && !isnull)
+		return find_name(line, NULL, p_value, TERM_TAB);
+
+	if (orig_name) {
+		int len;
+		const char *name;
+		char *another;
+		name = orig_name;
+		len = strlen(name);
+		if (isnull)
+			die(_("git apply: bad git-diff - expected /dev/null, got %s on line %d"), name, linenr);
+		another = find_name(line, NULL, p_value, TERM_TAB);
+		if (!another || memcmp(another, name, len + 1))
+			die((side == DIFF_NEW_NAME) ?
+			    _("git apply: bad git-diff - inconsistent new filename on line %d") :
+			    _("git apply: bad git-diff - inconsistent old filename on line %d"), linenr);
+		free(another);
+		return orig_name;
+	}
+	else {
+		/* expect "/dev/null" */
+		if (memcmp("/dev/null", line, 9) || line[9] != '\n')
+			die(_("git apply: bad git-diff - expected /dev/null on line %d"), linenr);
+		return NULL;
+	}
+}
+
+static int gitdiff_oldname(const char *line, struct patch *patch)
+{
+	char *orig = patch->old_name;
+	patch->old_name = gitdiff_verify_name(line, patch->is_new, patch->old_name,
+					      DIFF_OLD_NAME);
+	if (orig != patch->old_name)
+		free(orig);
+	return 0;
+}
+
+static int gitdiff_newname(const char *line, struct patch *patch)
+{
+	char *orig = patch->new_name;
+	patch->new_name = gitdiff_verify_name(line, patch->is_delete, patch->new_name,
+					      DIFF_NEW_NAME);
+	if (orig != patch->new_name)
+		free(orig);
+	return 0;
+}
+
+static int gitdiff_oldmode(const char *line, struct patch *patch)
+{
+	patch->old_mode = strtoul(line, NULL, 8);
+	return 0;
+}
+
+static int gitdiff_newmode(const char *line, struct patch *patch)
+{
+	patch->new_mode = strtoul(line, NULL, 8);
+	return 0;
+}
+
+static int gitdiff_delete(const char *line, struct patch *patch)
+{
+	patch->is_delete = 1;
+	free(patch->old_name);
+	patch->old_name = null_strdup(patch->def_name);
+	return gitdiff_oldmode(line, patch);
+}
+
+static int gitdiff_newfile(const char *line, struct patch *patch)
+{
+	patch->is_new = 1;
+	free(patch->new_name);
+	patch->new_name = null_strdup(patch->def_name);
+	return gitdiff_newmode(line, patch);
+}
+
+static int gitdiff_copysrc(const char *line, struct patch *patch)
+{
+	patch->is_copy = 1;
+	free(patch->old_name);
+	patch->old_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
+	return 0;
+}
+
+static int gitdiff_copydst(const char *line, struct patch *patch)
+{
+	patch->is_copy = 1;
+	free(patch->new_name);
+	patch->new_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
+	return 0;
+}
+
+static int gitdiff_renamesrc(const char *line, struct patch *patch)
+{
+	patch->is_rename = 1;
+	free(patch->old_name);
+	patch->old_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
+	return 0;
+}
+
+static int gitdiff_renamedst(const char *line, struct patch *patch)
+{
+	patch->is_rename = 1;
+	free(patch->new_name);
+	patch->new_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
+	return 0;
+}
+
+static int gitdiff_similarity(const char *line, struct patch *patch)
+{
+	if ((patch->score = strtoul(line, NULL, 10)) == ULONG_MAX)
+		patch->score = 0;
+	return 0;
+}
+
+static int gitdiff_dissimilarity(const char *line, struct patch *patch)
+{
+	if ((patch->score = strtoul(line, NULL, 10)) == ULONG_MAX)
+		patch->score = 0;
+	return 0;
+}
+
+static int gitdiff_index(const char *line, struct patch *patch)
+{
+	/*
+	 * index line is N hexadecimal, "..", N hexadecimal,
+	 * and optional space with octal mode.
+	 */
+	const char *ptr, *eol;
+	int len;
+
+	ptr = strchr(line, '.');
+	if (!ptr || ptr[1] != '.' || 40 < ptr - line)
+		return 0;
+	len = ptr - line;
+	memcpy(patch->old_sha1_prefix, line, len);
+	patch->old_sha1_prefix[len] = 0;
+
+	line = ptr + 2;
+	ptr = strchr(line, ' ');
+	eol = strchr(line, '\n');
+
+	if (!ptr || eol < ptr)
+		ptr = eol;
+	len = ptr - line;
+
+	if (40 < len)
+		return 0;
+	memcpy(patch->new_sha1_prefix, line, len);
+	patch->new_sha1_prefix[len] = 0;
+	if (*ptr == ' ')
+		patch->old_mode = strtoul(ptr+1, NULL, 8);
+	return 0;
+}
+
+/*
+ * This is normal for a diff that doesn't change anything: we'll fall through
+ * into the next diff. Tell the parser to break out.
+ */
+static int gitdiff_unrecognized(const char *line, struct patch *patch)
+{
+	return -1;
+}
+
+static const char *stop_at_slash(const char *line, int llen)
+{
+	int nslash = p_value;
+	int i;
+
+	for (i = 0; i < llen; i++) {
+		int ch = line[i];
+		if (ch == '/' && --nslash <= 0)
+			return &line[i];
+	}
+	return NULL;
+}
+
+/*
+ * This is to extract the same name that appears on "diff --git"
+ * line.  We do not find and return anything if it is a rename
+ * patch, and it is OK because we will find the name elsewhere.
+ * We need to reliably find name only when it is mode-change only,
+ * creation or deletion of an empty file.  In any of these cases,
+ * both sides are the same name under a/ and b/ respectively.
+ */
+static char *git_header_name(const char *line, int llen)
+{
+	const char *name;
+	const char *second = NULL;
+	size_t len, line_len;
+
+	line += strlen("diff --git ");
+	llen -= strlen("diff --git ");
+
+	if (*line == '"') {
+		const char *cp;
+		struct strbuf first = STRBUF_INIT;
+		struct strbuf sp = STRBUF_INIT;
+
+		if (unquote_c_style(&first, line, &second))
+			goto free_and_fail1;
+
+		/* advance to the first slash */
+		cp = stop_at_slash(first.buf, first.len);
+		/* we do not accept absolute paths */
+		if (!cp || cp == first.buf)
+			goto free_and_fail1;
+		strbuf_remove(&first, 0, cp + 1 - first.buf);
+
+		/*
+		 * second points at one past closing dq of name.
+		 * find the second name.
+		 */
+		while ((second < line + llen) && isspace(*second))
+			second++;
+
+		if (line + llen <= second)
+			goto free_and_fail1;
+		if (*second == '"') {
+			if (unquote_c_style(&sp, second, NULL))
+				goto free_and_fail1;
+			cp = stop_at_slash(sp.buf, sp.len);
+			if (!cp || cp == sp.buf)
+				goto free_and_fail1;
+			/* They must match, otherwise ignore */
+			if (strcmp(cp + 1, first.buf))
+				goto free_and_fail1;
+			strbuf_release(&sp);
+			return strbuf_detach(&first, NULL);
+		}
+
+		/* unquoted second */
+		cp = stop_at_slash(second, line + llen - second);
+		if (!cp || cp == second)
+			goto free_and_fail1;
+		cp++;
+		if (line + llen - cp != first.len + 1 ||
+		    memcmp(first.buf, cp, first.len))
+			goto free_and_fail1;
+		return strbuf_detach(&first, NULL);
+
+	free_and_fail1:
+		strbuf_release(&first);
+		strbuf_release(&sp);
+		return NULL;
+	}
+
+	/* unquoted first name */
+	name = stop_at_slash(line, llen);
+	if (!name || name == line)
+		return NULL;
+	name++;
+
+	/*
+	 * since the first name is unquoted, a dq if exists must be
+	 * the beginning of the second name.
+	 */
+	for (second = name; second < line + llen; second++) {
+		if (*second == '"') {
+			struct strbuf sp = STRBUF_INIT;
+			const char *np;
+
+			if (unquote_c_style(&sp, second, NULL))
+				goto free_and_fail2;
+
+			np = stop_at_slash(sp.buf, sp.len);
+			if (!np || np == sp.buf)
+				goto free_and_fail2;
+			np++;
+
+			len = sp.buf + sp.len - np;
+			if (len < second - name &&
+			    !strncmp(np, name, len) &&
+			    isspace(name[len])) {
+				/* Good */
+				strbuf_remove(&sp, 0, np - sp.buf);
+				return strbuf_detach(&sp, NULL);
+			}
+
+		free_and_fail2:
+			strbuf_release(&sp);
+			return NULL;
+		}
+	}
+
+	/*
+	 * Accept a name only if it shows up twice, exactly the same
+	 * form.
+	 */
+	second = strchr(name, '\n');
+	if (!second)
+		return NULL;
+	line_len = second - name;
+	for (len = 0 ; ; len++) {
+		switch (name[len]) {
+		default:
+			continue;
+		case '\n':
+			return NULL;
+		case '\t': case ' ':
+			second = stop_at_slash(name + len, line_len - len);
+			if (!second)
+				return NULL;
+			second++;
+			if (second[len] == '\n' && !strncmp(name, second, len)) {
+				return xmemdupz(name, len);
+			}
+		}
+	}
+}
+
+/* Verify that we recognize the lines following a git header */
+static int parse_git_header(const char *line, int len, unsigned int size, struct patch *patch)
+{
+	unsigned long offset;
+
+	/* A git diff has explicit new/delete information, so we don't guess */
+	patch->is_new = 0;
+	patch->is_delete = 0;
+
+	/*
+	 * Some things may not have the old name in the
+	 * rest of the headers anywhere (pure mode changes,
+	 * or removing or adding empty files), so we get
+	 * the default name from the header.
+	 */
+	patch->def_name = git_header_name(line, len);
+	if (patch->def_name && root) {
+		char *s = xmalloc(root_len + strlen(patch->def_name) + 1);
+		strcpy(s, root);
+		strcpy(s + root_len, patch->def_name);
+		free(patch->def_name);
+		patch->def_name = s;
+	}
+
+	line += len;
+	size -= len;
+	linenr++;
+	for (offset = len ; size > 0 ; offset += len, size -= len, line += len, linenr++) {
+		static const struct opentry {
+			const char *str;
+			int (*fn)(const char *, struct patch *);
+		} optable[] = {
+			{ "@@ -", gitdiff_hdrend },
+			{ "--- ", gitdiff_oldname },
+			{ "+++ ", gitdiff_newname },
+			{ "old mode ", gitdiff_oldmode },
+			{ "new mode ", gitdiff_newmode },
+			{ "deleted file mode ", gitdiff_delete },
+			{ "new file mode ", gitdiff_newfile },
+			{ "copy from ", gitdiff_copysrc },
+			{ "copy to ", gitdiff_copydst },
+			{ "rename old ", gitdiff_renamesrc },
+			{ "rename new ", gitdiff_renamedst },
+			{ "rename from ", gitdiff_renamesrc },
+			{ "rename to ", gitdiff_renamedst },
+			{ "similarity index ", gitdiff_similarity },
+			{ "dissimilarity index ", gitdiff_dissimilarity },
+			{ "index ", gitdiff_index },
+			{ "", gitdiff_unrecognized },
+		};
+		int i;
+
+		len = linelen(line, size);
+		if (!len || line[len-1] != '\n')
+			break;
+		for (i = 0; i < ARRAY_SIZE(optable); i++) {
+			const struct opentry *p = optable + i;
+			int oplen = strlen(p->str);
+			if (len < oplen || memcmp(p->str, line, oplen))
+				continue;
+			if (p->fn(line + oplen, patch) < 0)
+				return offset;
+			break;
+		}
+	}
+
+	return offset;
+}
+
+static int parse_num(const char *line, unsigned long *p)
+{
+	char *ptr;
+
+	if (!isdigit(*line))
+		return 0;
+	*p = strtoul(line, &ptr, 10);
+	return ptr - line;
+}
+
+static int parse_range(const char *line, int len, int offset, const char *expect,
+		       unsigned long *p1, unsigned long *p2)
+{
+	int digits, ex;
+
+	if (offset < 0 || offset >= len)
+		return -1;
+	line += offset;
+	len -= offset;
+
+	digits = parse_num(line, p1);
+	if (!digits)
+		return -1;
+
+	offset += digits;
+	line += digits;
+	len -= digits;
+
+	*p2 = 1;
+	if (*line == ',') {
+		digits = parse_num(line+1, p2);
+		if (!digits)
+			return -1;
+
+		offset += digits+1;
+		line += digits+1;
+		len -= digits+1;
+	}
+
+	ex = strlen(expect);
+	if (ex > len)
+		return -1;
+	if (memcmp(line, expect, ex))
+		return -1;
+
+	return offset + ex;
+}
+
+static void recount_diff(const char *line, int size, struct fragment *fragment)
+{
+	int oldlines = 0, newlines = 0, ret = 0;
+
+	if (size < 1) {
+		warning("recount: ignore empty hunk");
+		return;
+	}
+
+	for (;;) {
+		int len = linelen(line, size);
+		size -= len;
+		line += len;
+
+		if (size < 1)
+			break;
+
+		switch (*line) {
+		case ' ': case '\n':
+			newlines++;
+			/* fall through */
+		case '-':
+			oldlines++;
+			continue;
+		case '+':
+			newlines++;
+			continue;
+		case '\\':
+			continue;
+		case '@':
+			ret = size < 3 || prefixcmp(line, "@@ ");
+			break;
+		case 'd':
+			ret = size < 5 || prefixcmp(line, "diff ");
+			break;
+		default:
+			ret = -1;
+			break;
+		}
+		if (ret) {
+			warning(_("recount: unexpected line: %.*s"),
+				(int)linelen(line, size), line);
+			return;
+		}
+		break;
+	}
+	fragment->oldlines = oldlines;
+	fragment->newlines = newlines;
+}
+
+/*
+ * Parse a unified diff fragment header of the
+ * form "@@ -a,b +c,d @@"
+ */
+static int parse_fragment_header(const char *line, int len, struct fragment *fragment)
+{
+	int offset;
+
+	if (!len || line[len-1] != '\n')
+		return -1;
+
+	/* Figure out the number of lines in a fragment */
+	offset = parse_range(line, len, 4, " +", &fragment->oldpos, &fragment->oldlines);
+	offset = parse_range(line, len, offset, " @@", &fragment->newpos, &fragment->newlines);
+
+	return offset;
+}
+
+static int find_header(const char *line, unsigned long size, int *hdrsize, struct patch *patch)
+{
+	unsigned long offset, len;
+
+	patch->is_toplevel_relative = 0;
+	patch->is_rename = patch->is_copy = 0;
+	patch->is_new = patch->is_delete = -1;
+	patch->old_mode = patch->new_mode = 0;
+	patch->old_name = patch->new_name = NULL;
+	for (offset = 0; size > 0; offset += len, size -= len, line += len, linenr++) {
+		unsigned long nextlen;
+
+		len = linelen(line, size);
+		if (!len)
+			break;
+
+		/* Testing this early allows us to take a few shortcuts.. */
+		if (len < 6)
+			continue;
+
+		/*
+		 * 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.
+		 */
+		if (!memcmp("@@ -", line, 4)) {
+			struct fragment dummy;
+			if (parse_fragment_header(line, len, &dummy) < 0)
+				continue;
+			die(_("patch fragment without header at line %d: %.*s"),
+			    linenr, (int)len-1, line);
+		}
+
+		if (size < len + 6)
+			break;
+
+		/*
+		 * Git patch? It might not have a real patch, just a rename
+		 * or mode change, so we handle that specially
+		 */
+		if (!memcmp("diff --git ", line, 11)) {
+			int git_hdr_len = parse_git_header(line, len, size, patch);
+			if (git_hdr_len <= len)
+				continue;
+			if (!patch->old_name && !patch->new_name) {
+				if (!patch->def_name)
+					die(Q_("git diff header lacks filename information when removing "
+					       "%d leading pathname component (line %d)",
+					       "git diff header lacks filename information when removing "
+					       "%d leading pathname components (line %d)",
+					       p_value),
+					    p_value, linenr);
+				patch->old_name = xstrdup(patch->def_name);
+				patch->new_name = xstrdup(patch->def_name);
+			}
+			if (!patch->is_delete && !patch->new_name)
+				die("git diff header lacks filename information "
+				    "(line %d)", linenr);
+			patch->is_toplevel_relative = 1;
+			*hdrsize = git_hdr_len;
+			return offset;
+		}
+
+		/* --- followed by +++ ? */
+		if (memcmp("--- ", line,  4) || memcmp("+++ ", line + len, 4))
+			continue;
+
+		/*
+		 * We only accept unified patches, so we want it to
+		 * at least have "@@ -a,b +c,d @@\n", which is 14 chars
+		 * minimum ("@@ -0,0 +1 @@\n" is the shortest).
+		 */
+		nextlen = linelen(line + len, size - len);
+		if (size < nextlen + 14 || memcmp("@@ -", line + len + nextlen, 4))
+			continue;
+
+		/* Ok, we'll consider it a patch */
+		parse_traditional_patch(line, line+len, patch);
+		*hdrsize = len + nextlen;
+		linenr += 2;
+		return offset;
+	}
+	return -1;
+}
+
+static void record_ws_error(unsigned result, const char *line, int len, int linenr)
+{
+	char *err;
+
+	if (!result)
+		return;
+
+	whitespace_error++;
+	if (squelch_whitespace_errors &&
+	    squelch_whitespace_errors < whitespace_error)
+		return;
+
+	err = whitespace_error_string(result);
+	fprintf(stderr, "%s:%d: %s.\n%.*s\n",
+		patch_input_file, linenr, err, len, line);
+	free(err);
+}
+
+static void check_whitespace(const char *line, int len, unsigned ws_rule)
+{
+	unsigned result = ws_check(line + 1, len - 1, ws_rule);
+
+	record_ws_error(result, line + 1, len - 2, linenr);
+}
+
+/*
+ * Parse a unified diff. Note that this really needs to parse each
+ * fragment separately, since the only way to know the difference
+ * between a "---" that is part of a patch, and a "---" that starts
+ * the next patch is to look at the line counts..
+ */
+static int parse_fragment(const char *line, unsigned long size,
+			  struct patch *patch, struct fragment *fragment)
+{
+	int added, deleted;
+	int len = linelen(line, size), offset;
+	unsigned long oldlines, newlines;
+	unsigned long leading, trailing;
+
+	offset = parse_fragment_header(line, len, fragment);
+	if (offset < 0)
+		return -1;
+	if (offset > 0 && patch->recount)
+		recount_diff(line + offset, size - offset, fragment);
+	oldlines = fragment->oldlines;
+	newlines = fragment->newlines;
+	leading = 0;
+	trailing = 0;
+
+	/* Parse the thing.. */
+	line += len;
+	size -= len;
+	linenr++;
+	added = deleted = 0;
+	for (offset = len;
+	     0 < size;
+	     offset += len, size -= len, line += len, linenr++) {
+		if (!oldlines && !newlines)
+			break;
+		len = linelen(line, size);
+		if (!len || line[len-1] != '\n')
+			return -1;
+		switch (*line) {
+		default:
+			return -1;
+		case '\n': /* newer GNU diff, an empty context line */
+		case ' ':
+			oldlines--;
+			newlines--;
+			if (!deleted && !added)
+				leading++;
+			trailing++;
+			break;
+		case '-':
+			if (apply_in_reverse &&
+			    ws_error_action != nowarn_ws_error)
+				check_whitespace(line, len, patch->ws_rule);
+			deleted++;
+			oldlines--;
+			trailing = 0;
+			break;
+		case '+':
+			if (!apply_in_reverse &&
+			    ws_error_action != nowarn_ws_error)
+				check_whitespace(line, len, patch->ws_rule);
+			added++;
+			newlines--;
+			trailing = 0;
+			break;
+
+		/*
+		 * We allow "\ No newline at end of file". Depending
+                 * on locale settings when the patch was produced we
+                 * don't know what this line looks like. The only
+                 * thing we do know is that it begins with "\ ".
+		 * Checking for 12 is just for sanity check -- any
+		 * l10n of "\ No newline..." is at least that long.
+		 */
+		case '\\':
+			if (len < 12 || memcmp(line, "\\ ", 2))
+				return -1;
+			break;
+		}
+	}
+	if (oldlines || newlines)
+		return -1;
+	fragment->leading = leading;
+	fragment->trailing = trailing;
+
+	/*
+	 * If a fragment ends with an incomplete line, we failed to include
+	 * it in the above loop because we hit oldlines == newlines == 0
+	 * before seeing it.
+	 */
+	if (12 < size && !memcmp(line, "\\ ", 2))
+		offset += linelen(line, size);
+
+	patch->lines_added += added;
+	patch->lines_deleted += deleted;
+
+	if (0 < patch->is_new && oldlines)
+		return error(_("new file depends on old contents"));
+	if (0 < patch->is_delete && newlines)
+		return error(_("deleted file still has contents"));
+	return offset;
+}
+
+/*
+ * We have seen "diff --git a/... b/..." header (or a traditional patch
+ * header).  Read hunks that belong to this patch into fragments and hang
+ * them to the given patch structure.
+ *
+ * The (fragment->patch, fragment->size) pair points into the memory given
+ * by the caller, not a copy, when we return.
+ */
+static int parse_single_patch(const char *line, unsigned long size, struct patch *patch)
+{
+	unsigned long offset = 0;
+	unsigned long oldlines = 0, newlines = 0, context = 0;
+	struct fragment **fragp = &patch->fragments;
+
+	while (size > 4 && !memcmp(line, "@@ -", 4)) {
+		struct fragment *fragment;
+		int len;
+
+		fragment = xcalloc(1, sizeof(*fragment));
+		fragment->linenr = linenr;
+		len = parse_fragment(line, size, patch, fragment);
+		if (len <= 0)
+			die(_("corrupt patch at line %d"), linenr);
+		fragment->patch = line;
+		fragment->size = len;
+		oldlines += fragment->oldlines;
+		newlines += fragment->newlines;
+		context += fragment->leading + fragment->trailing;
+
+		*fragp = fragment;
+		fragp = &fragment->next;
+
+		offset += len;
+		line += len;
+		size -= len;
+	}
+
+	/*
+	 * If something was removed (i.e. we have old-lines) it cannot
+	 * be creation, and if something was added it cannot be
+	 * deletion.  However, the reverse is not true; --unified=0
+	 * patches that only add are not necessarily creation even
+	 * though they do not have any old lines, and ones that only
+	 * delete are not necessarily deletion.
+	 *
+	 * Unfortunately, a real creation/deletion patch do _not_ have
+	 * any context line by definition, so we cannot safely tell it
+	 * apart with --unified=0 insanity.  At least if the patch has
+	 * more than one hunk it is not creation or deletion.
+	 */
+	if (patch->is_new < 0 &&
+	    (oldlines || (patch->fragments && patch->fragments->next)))
+		patch->is_new = 0;
+	if (patch->is_delete < 0 &&
+	    (newlines || (patch->fragments && patch->fragments->next)))
+		patch->is_delete = 0;
+
+	if (0 < patch->is_new && oldlines)
+		die(_("new file %s depends on old contents"), patch->new_name);
+	if (0 < patch->is_delete && newlines)
+		die(_("deleted file %s still has contents"), patch->old_name);
+	if (!patch->is_delete && !newlines && context)
+		fprintf_ln(stderr,
+			   _("** warning: "
+			     "file %s becomes empty but is not deleted"),
+			   patch->new_name);
+
+	return offset;
+}
+
+static inline int metadata_changes(struct patch *patch)
+{
+	return	patch->is_rename > 0 ||
+		patch->is_copy > 0 ||
+		patch->is_new > 0 ||
+		patch->is_delete ||
+		(patch->old_mode && patch->new_mode &&
+		 patch->old_mode != patch->new_mode);
+}
+
+static char *inflate_it(const void *data, unsigned long size,
+			unsigned long inflated_size)
+{
+	git_zstream stream;
+	void *out;
+	int st;
+
+	memset(&stream, 0, sizeof(stream));
+
+	stream.next_in = (unsigned char *)data;
+	stream.avail_in = size;
+	stream.next_out = out = xmalloc(inflated_size);
+	stream.avail_out = inflated_size;
+	git_inflate_init(&stream);
+	st = git_inflate(&stream, Z_FINISH);
+	git_inflate_end(&stream);
+	if ((st != Z_STREAM_END) || stream.total_out != inflated_size) {
+		free(out);
+		return NULL;
+	}
+	return out;
+}
+
+/*
+ * Read a binary hunk and return a new fragment; fragment->patch
+ * points at an allocated memory that the caller must free, so
+ * it is marked as "->free_patch = 1".
+ */
+static struct fragment *parse_binary_hunk(char **buf_p,
+					  unsigned long *sz_p,
+					  int *status_p,
+					  int *used_p)
+{
+	/*
+	 * Expect a line that begins with binary patch method ("literal"
+	 * or "delta"), followed by the length of data before deflating.
+	 * a sequence of 'length-byte' followed by base-85 encoded data
+	 * should follow, terminated by a newline.
+	 *
+	 * Each 5-byte sequence of base-85 encodes up to 4 bytes,
+	 * and we would limit the patch line to 66 characters,
+	 * 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.
+	 */
+	int llen, used;
+	unsigned long size = *sz_p;
+	char *buffer = *buf_p;
+	int patch_method;
+	unsigned long origlen;
+	char *data = NULL;
+	int hunk_size = 0;
+	struct fragment *frag;
+
+	llen = linelen(buffer, size);
+	used = llen;
+
+	*status_p = 0;
+
+	if (!prefixcmp(buffer, "delta ")) {
+		patch_method = BINARY_DELTA_DEFLATED;
+		origlen = strtoul(buffer + 6, NULL, 10);
+	}
+	else if (!prefixcmp(buffer, "literal ")) {
+		patch_method = BINARY_LITERAL_DEFLATED;
+		origlen = strtoul(buffer + 8, NULL, 10);
+	}
+	else
+		return NULL;
+
+	linenr++;
+	buffer += llen;
+	while (1) {
+		int byte_length, max_byte_length, newsize;
+		llen = linelen(buffer, size);
+		used += llen;
+		linenr++;
+		if (llen == 1) {
+			/* consume the blank line */
+			buffer++;
+			size--;
+			break;
+		}
+		/*
+		 * Minimum line is "A00000\n" which is 7-byte long,
+		 * and the line length must be multiple of 5 plus 2.
+		 */
+		if ((llen < 7) || (llen-2) % 5)
+			goto corrupt;
+		max_byte_length = (llen - 2) / 5 * 4;
+		byte_length = *buffer;
+		if ('A' <= byte_length && byte_length <= 'Z')
+			byte_length = byte_length - 'A' + 1;
+		else if ('a' <= byte_length && byte_length <= 'z')
+			byte_length = byte_length - 'a' + 27;
+		else
+			goto corrupt;
+		/* if the input length was not multiple of 4, we would
+		 * have filler at the end but the filler should never
+		 * exceed 3 bytes
+		 */
+		if (max_byte_length < byte_length ||
+		    byte_length <= max_byte_length - 4)
+			goto corrupt;
+		newsize = hunk_size + byte_length;
+		data = xrealloc(data, newsize);
+		if (decode_85(data + hunk_size, buffer + 1, byte_length))
+			goto corrupt;
+		hunk_size = newsize;
+		buffer += llen;
+		size -= llen;
+	}
+
+	frag = xcalloc(1, sizeof(*frag));
+	frag->patch = inflate_it(data, hunk_size, origlen);
+	frag->free_patch = 1;
+	if (!frag->patch)
+		goto corrupt;
+	free(data);
+	frag->size = origlen;
+	*buf_p = buffer;
+	*sz_p = size;
+	*used_p = used;
+	frag->binary_patch_method = patch_method;
+	return frag;
+
+ corrupt:
+	free(data);
+	*status_p = -1;
+	error(_("corrupt binary patch at line %d: %.*s"),
+	      linenr-1, llen-1, buffer);
+	return NULL;
+}
+
+static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
+{
+	/*
+	 * We have read "GIT binary patch\n"; what follows is a line
+	 * that says the patch method (currently, either "literal" or
+	 * "delta") and the length of data before deflating; a
+	 * sequence of 'length-byte' followed by base-85 encoded data
+	 * follows.
+	 *
+	 * When a binary patch is reversible, there is another binary
+	 * hunk in the same format, starting with patch method (either
+	 * "literal" or "delta") with the length of data, and a sequence
+	 * of length-byte + base-85 encoded data, terminated with another
+	 * empty line.  This data, when applied to the postimage, produces
+	 * the preimage.
+	 */
+	struct fragment *forward;
+	struct fragment *reverse;
+	int status;
+	int used, used_1;
+
+	forward = parse_binary_hunk(&buffer, &size, &status, &used);
+	if (!forward && !status)
+		/* there has to be one hunk (forward hunk) */
+		return error(_("unrecognized binary patch at line %d"), linenr-1);
+	if (status)
+		/* otherwise we already gave an error message */
+		return status;
+
+	reverse = parse_binary_hunk(&buffer, &size, &status, &used_1);
+	if (reverse)
+		used += used_1;
+	else if (status) {
+		/*
+		 * Not having reverse hunk is not an error, but having
+		 * a corrupt reverse hunk is.
+		 */
+		free((void*) forward->patch);
+		free(forward);
+		return status;
+	}
+	forward->next = reverse;
+	patch->fragments = forward;
+	patch->is_binary = 1;
+	return used;
+}
+
+/*
+ * Read the patch text in "buffer" taht extends for "size" bytes; stop
+ * reading after seeing a single patch (i.e. changes to a single file).
+ * Create fragments (i.e. patch hunks) and hang them to the given patch.
+ * Return the number of bytes consumed, so that the caller can call us
+ * again for the next patch.
+ */
+static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
+{
+	int hdrsize, patchsize;
+	int offset = find_header(buffer, size, &hdrsize, patch);
+
+	if (offset < 0)
+		return offset;
+
+	patch->ws_rule = whitespace_rule(patch->new_name
+					 ? patch->new_name
+					 : patch->old_name);
+
+	patchsize = parse_single_patch(buffer + offset + hdrsize,
+				       size - offset - hdrsize, patch);
+
+	if (!patchsize) {
+		static const char *binhdr[] = {
+			"Binary files ",
+			"Files ",
+			NULL,
+		};
+		static const char git_binary[] = "GIT binary patch\n";
+		int i;
+		int hd = hdrsize + offset;
+		unsigned long llen = linelen(buffer + hd, size - hd);
+
+		if (llen == sizeof(git_binary) - 1 &&
+		    !memcmp(git_binary, buffer + hd, llen)) {
+			int used;
+			linenr++;
+			used = parse_binary(buffer + hd + llen,
+					    size - hd - llen, patch);
+			if (used)
+				patchsize = used + llen;
+			else
+				patchsize = 0;
+		}
+		else if (!memcmp(" differ\n", buffer + hd + llen - 8, 8)) {
+			for (i = 0; binhdr[i]; i++) {
+				int len = strlen(binhdr[i]);
+				if (len < size - hd &&
+				    !memcmp(binhdr[i], buffer + hd, len)) {
+					linenr++;
+					patch->is_binary = 1;
+					patchsize = llen;
+					break;
+				}
+			}
+		}
+
+		/* Empty patch cannot be applied if it is a text patch
+		 * without metadata change.  A binary patch appears
+		 * empty to us here.
+		 */
+		if ((apply || check) &&
+		    (!patch->is_binary && !metadata_changes(patch)))
+			die(_("patch with only garbage at line %d"), linenr);
+	}
+
+	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);
+		}
+	}
+}
+
+static const char pluses[] =
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";
+static const char minuses[]=
+"----------------------------------------------------------------------";
+
+static void show_stats(struct patch *patch)
+{
+	struct strbuf qname = STRBUF_INIT;
+	char *cp = patch->new_name ? patch->new_name : patch->old_name;
+	int max, add, del;
+
+	quote_c_style(cp, &qname, NULL, 0);
+
+	/*
+	 * "scale" the filename
+	 */
+	max = max_len;
+	if (max > 50)
+		max = 50;
+
+	if (qname.len > max) {
+		cp = strchr(qname.buf + qname.len + 3 - max, '/');
+		if (!cp)
+			cp = qname.buf + qname.len + 3 - max;
+		strbuf_splice(&qname, 0, cp - qname.buf, "...", 3);
+	}
+
+	if (patch->is_binary) {
+		printf(" %-*s |  Bin\n", max, qname.buf);
+		strbuf_release(&qname);
+		return;
+	}
+
+	printf(" %-*s |", max, qname.buf);
+	strbuf_release(&qname);
+
+	/*
+	 * scale the add/delete
+	 */
+	max = max + max_change > 70 ? 70 - max : max_change;
+	add = patch->lines_added;
+	del = patch->lines_deleted;
+
+	if (max_change > 0) {
+		int total = ((add + del) * max + max_change / 2) / max_change;
+		add = (add * max + max_change / 2) / max_change;
+		del = total - add;
+	}
+	printf("%5d %.*s%.*s\n", patch->lines_added + patch->lines_deleted,
+		add, pluses, del, minuses);
+}
+
+static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)
+{
+	switch (st->st_mode & S_IFMT) {
+	case S_IFLNK:
+		if (strbuf_readlink(buf, path, st->st_size) < 0)
+			return error(_("unable to read symlink %s"), path);
+		return 0;
+	case S_IFREG:
+		if (strbuf_read_file(buf, path, st->st_size) != st->st_size)
+			return error(_("unable to open or read %s"), path);
+		convert_to_git(path, buf->buf, buf->len, buf, 0);
+		return 0;
+	default:
+		return -1;
+	}
+}
+
+/*
+ * Update the preimage, and the common lines in postimage,
+ * from buffer buf of length len. If postlen is 0 the postimage
+ * is updated in place, otherwise it's updated on a new buffer
+ * of length postlen
+ */
+
+static void update_pre_post_images(struct image *preimage,
+				   struct image *postimage,
+				   char *buf,
+				   size_t len, size_t postlen)
+{
+	int i, ctx;
+	char *new, *old, *fixed;
+	struct image fixed_preimage;
+
+	/*
+	 * Update the preimage with whitespace fixes.  Note that we
+	 * are not losing preimage->buf -- apply_one_fragment() will
+	 * free "oldlines".
+	 */
+	prepare_image(&fixed_preimage, buf, len, 1);
+	assert(fixed_preimage.nr == preimage->nr);
+	for (i = 0; i < preimage->nr; i++)
+		fixed_preimage.line[i].flag = preimage->line[i].flag;
+	free(preimage->line_allocated);
+	*preimage = fixed_preimage;
+
+	/*
+	 * Adjust the common context lines in postimage. This can be
+	 * done in-place when we are just doing whitespace fixing,
+	 * which does not make the string grow, but needs a new buffer
+	 * when ignoring whitespace causes the update, since in this case
+	 * we could have e.g. tabs converted to multiple spaces.
+	 * We trust the caller to tell us if the update can be done
+	 * in place (postlen==0) or not.
+	 */
+	old = postimage->buf;
+	if (postlen)
+		new = postimage->buf = xmalloc(postlen);
+	else
+		new = old;
+	fixed = preimage->buf;
+	for (i = ctx = 0; i < postimage->nr; i++) {
+		size_t len = postimage->line[i].len;
+		if (!(postimage->line[i].flag & LINE_COMMON)) {
+			/* an added line -- no counterparts in preimage */
+			memmove(new, old, len);
+			old += len;
+			new += len;
+			continue;
+		}
+
+		/* a common context -- skip it in the original postimage */
+		old += len;
+
+		/* and find the corresponding one in the fixed preimage */
+		while (ctx < preimage->nr &&
+		       !(preimage->line[ctx].flag & LINE_COMMON)) {
+			fixed += preimage->line[ctx].len;
+			ctx++;
+		}
+		if (preimage->nr <= ctx)
+			die(_("oops"));
+
+		/* and copy it in, while fixing the line length */
+		len = preimage->line[ctx].len;
+		memcpy(new, fixed, len);
+		new += len;
+		fixed += len;
+		postimage->line[i].len = len;
+		ctx++;
+	}
+
+	/* Fix the length of the whole thing */
+	postimage->len = new - postimage->buf;
+}
+
+static int match_fragment(struct image *img,
+			  struct image *preimage,
+			  struct image *postimage,
+			  unsigned long try,
+			  int try_lno,
+			  unsigned ws_rule,
+			  int match_beginning, int match_end)
+{
+	int i;
+	char *fixed_buf, *buf, *orig, *target;
+	struct strbuf fixed;
+	size_t fixed_len;
+	int preimage_limit;
+
+	if (preimage->nr + try_lno <= img->nr) {
+		/*
+		 * The hunk falls within the boundaries of img.
+		 */
+		preimage_limit = preimage->nr;
+		if (match_end && (preimage->nr + try_lno != img->nr))
+			return 0;
+	} else if (ws_error_action == correct_ws_error &&
+		   (ws_rule & WS_BLANK_AT_EOF)) {
+		/*
+		 * This hunk extends beyond the end of img, and we are
+		 * removing blank lines at the end of the file.  This
+		 * many lines from the beginning of the preimage must
+		 * match with img, and the remainder of the preimage
+		 * must be blank.
+		 */
+		preimage_limit = img->nr - try_lno;
+	} else {
+		/*
+		 * The hunk extends beyond the end of the img and
+		 * we are not removing blanks at the end, so we
+		 * should reject the hunk at this position.
+		 */
+		return 0;
+	}
+
+	if (match_beginning && try_lno)
+		return 0;
+
+	/* Quick hash check */
+	for (i = 0; i < preimage_limit; i++)
+		if ((img->line[try_lno + i].flag & LINE_PATCHED) ||
+		    (preimage->line[i].hash != img->line[try_lno + i].hash))
+			return 0;
+
+	if (preimage_limit == preimage->nr) {
+		/*
+		 * Do we have an exact match?  If we were told to match
+		 * at the end, size must be exactly at try+fragsize,
+		 * otherwise try+fragsize must be still within the preimage,
+		 * and either case, the old piece should match the preimage
+		 * exactly.
+		 */
+		if ((match_end
+		     ? (try + preimage->len == img->len)
+		     : (try + preimage->len <= img->len)) &&
+		    !memcmp(img->buf + try, preimage->buf, preimage->len))
+			return 1;
+	} else {
+		/*
+		 * The preimage extends beyond the end of img, so
+		 * there cannot be an exact match.
+		 *
+		 * There must be one non-blank context line that match
+		 * a line before the end of img.
+		 */
+		char *buf_end;
+
+		buf = preimage->buf;
+		buf_end = buf;
+		for (i = 0; i < preimage_limit; i++)
+			buf_end += preimage->line[i].len;
+
+		for ( ; buf < buf_end; buf++)
+			if (!isspace(*buf))
+				break;
+		if (buf == buf_end)
+			return 0;
+	}
+
+	/*
+	 * No exact match. If we are ignoring whitespace, run a line-by-line
+	 * fuzzy matching. We collect all the line length information because
+	 * we need it to adjust whitespace if we match.
+	 */
+	if (ws_ignore_action == ignore_ws_change) {
+		size_t imgoff = 0;
+		size_t preoff = 0;
+		size_t postlen = postimage->len;
+		size_t extra_chars;
+		char *preimage_eof;
+		char *preimage_end;
+		for (i = 0; i < preimage_limit; i++) {
+			size_t prelen = preimage->line[i].len;
+			size_t imglen = img->line[try_lno+i].len;
+
+			if (!fuzzy_matchlines(img->buf + try + imgoff, imglen,
+					      preimage->buf + preoff, prelen))
+				return 0;
+			if (preimage->line[i].flag & LINE_COMMON)
+				postlen += imglen - prelen;
+			imgoff += imglen;
+			preoff += prelen;
+		}
+
+		/*
+		 * Ok, the preimage matches with whitespace fuzz.
+		 *
+		 * imgoff now holds the true length of the target that
+		 * matches the preimage before the end of the file.
+		 *
+		 * Count the number of characters in the preimage that fall
+		 * beyond the end of the file and make sure that all of them
+		 * are whitespace characters. (This can only happen if
+		 * we are removing blank lines at the end of the file.)
+		 */
+		buf = preimage_eof = preimage->buf + preoff;
+		for ( ; i < preimage->nr; i++)
+			preoff += preimage->line[i].len;
+		preimage_end = preimage->buf + preoff;
+		for ( ; buf < preimage_end; buf++)
+			if (!isspace(*buf))
+				return 0;
+
+		/*
+		 * Update the preimage and the common postimage context
+		 * lines to use the same whitespace as the target.
+		 * If whitespace is missing in the target (i.e.
+		 * if the preimage extends beyond the end of the file),
+		 * use the whitespace from the preimage.
+		 */
+		extra_chars = preimage_end - preimage_eof;
+		strbuf_init(&fixed, imgoff + extra_chars);
+		strbuf_add(&fixed, img->buf + try, imgoff);
+		strbuf_add(&fixed, preimage_eof, extra_chars);
+		fixed_buf = strbuf_detach(&fixed, &fixed_len);
+		update_pre_post_images(preimage, postimage,
+				fixed_buf, fixed_len, postlen);
+		return 1;
+	}
+
+	if (ws_error_action != correct_ws_error)
+		return 0;
+
+	/*
+	 * The hunk does not apply byte-by-byte, but the hash says
+	 * it might with whitespace fuzz. We haven't been asked to
+	 * ignore whitespace, we were asked to correct whitespace
+	 * errors, so let's try matching after whitespace correction.
+	 *
+	 * The preimage may extend beyond the end of the file,
+	 * but in this loop we will only handle the part of the
+	 * preimage that falls within the file.
+	 */
+	strbuf_init(&fixed, preimage->len + 1);
+	orig = preimage->buf;
+	target = img->buf + try;
+	for (i = 0; i < preimage_limit; i++) {
+		size_t oldlen = preimage->line[i].len;
+		size_t tgtlen = img->line[try_lno + i].len;
+		size_t fixstart = fixed.len;
+		struct strbuf tgtfix;
+		int match;
+
+		/* Try fixing the line in the preimage */
+		ws_fix_copy(&fixed, orig, oldlen, ws_rule, NULL);
+
+		/* Try fixing the line in the target */
+		strbuf_init(&tgtfix, tgtlen);
+		ws_fix_copy(&tgtfix, target, tgtlen, ws_rule, NULL);
+
+		/*
+		 * If they match, either the preimage was based on
+		 * a version before our tree fixed whitespace breakage,
+		 * or we are lacking a whitespace-fix patch the tree
+		 * the preimage was based on already had (i.e. target
+		 * has whitespace breakage, the preimage doesn't).
+		 * In either case, we are fixing the whitespace breakages
+		 * so we might as well take the fix together with their
+		 * real change.
+		 */
+		match = (tgtfix.len == fixed.len - fixstart &&
+			 !memcmp(tgtfix.buf, fixed.buf + fixstart,
+					     fixed.len - fixstart));
+
+		strbuf_release(&tgtfix);
+		if (!match)
+			goto unmatch_exit;
+
+		orig += oldlen;
+		target += tgtlen;
+	}
+
+
+	/*
+	 * Now handle the lines in the preimage that falls beyond the
+	 * end of the file (if any). They will only match if they are
+	 * empty or only contain whitespace (if WS_BLANK_AT_EOL is
+	 * false).
+	 */
+	for ( ; i < preimage->nr; i++) {
+		size_t fixstart = fixed.len; /* start of the fixed preimage */
+		size_t oldlen = preimage->line[i].len;
+		int j;
+
+		/* Try fixing the line in the preimage */
+		ws_fix_copy(&fixed, orig, oldlen, ws_rule, NULL);
+
+		for (j = fixstart; j < fixed.len; j++)
+			if (!isspace(fixed.buf[j]))
+				goto unmatch_exit;
+
+		orig += oldlen;
+	}
+
+	/*
+	 * Yes, the preimage is based on an older version that still
+	 * has whitespace breakages unfixed, and fixing them makes the
+	 * hunk match.  Update the context lines in the postimage.
+	 */
+	fixed_buf = strbuf_detach(&fixed, &fixed_len);
+	update_pre_post_images(preimage, postimage,
+			       fixed_buf, fixed_len, 0);
+	return 1;
+
+ unmatch_exit:
+	strbuf_release(&fixed);
+	return 0;
+}
+
+static int find_pos(struct image *img,
+		    struct image *preimage,
+		    struct image *postimage,
+		    int line,
+		    unsigned ws_rule,
+		    int match_beginning, int match_end)
+{
+	int i;
+	unsigned long backwards, forwards, try;
+	int backwards_lno, forwards_lno, try_lno;
+
+	/*
+	 * If match_beginning or match_end is specified, there is no
+	 * point starting from a wrong line that will never match and
+	 * wander around and wait for a match at the specified end.
+	 */
+	if (match_beginning)
+		line = 0;
+	else if (match_end)
+		line = img->nr - preimage->nr;
+
+	/*
+	 * Because the comparison is unsigned, the following test
+	 * will also take care of a negative line number that can
+	 * result when match_end and preimage is larger than the target.
+	 */
+	if ((size_t) line > img->nr)
+		line = img->nr;
+
+	try = 0;
+	for (i = 0; i < line; i++)
+		try += img->line[i].len;
+
+	/*
+	 * There's probably some smart way to do this, but I'll leave
+	 * that to the smart and beautiful people. I'm simple and stupid.
+	 */
+	backwards = try;
+	backwards_lno = line;
+	forwards = try;
+	forwards_lno = line;
+	try_lno = line;
+
+	for (i = 0; ; i++) {
+		if (match_fragment(img, preimage, postimage,
+				   try, try_lno, ws_rule,
+				   match_beginning, match_end))
+			return try_lno;
+
+	again:
+		if (backwards_lno == 0 && forwards_lno == img->nr)
+			break;
+
+		if (i & 1) {
+			if (backwards_lno == 0) {
+				i++;
+				goto again;
+			}
+			backwards_lno--;
+			backwards -= img->line[backwards_lno].len;
+			try = backwards;
+			try_lno = backwards_lno;
+		} else {
+			if (forwards_lno == img->nr) {
+				i++;
+				goto again;
+			}
+			forwards += img->line[forwards_lno].len;
+			forwards_lno++;
+			try = forwards;
+			try_lno = forwards_lno;
+		}
+
+	}
+	return -1;
+}
+
+static void remove_first_line(struct image *img)
+{
+	img->buf += img->line[0].len;
+	img->len -= img->line[0].len;
+	img->line++;
+	img->nr--;
+}
+
+static void remove_last_line(struct image *img)
+{
+	img->len -= img->line[--img->nr].len;
+}
+
+/*
+ * The change from "preimage" and "postimage" has been found to
+ * apply at applied_pos (counts in line numbers) in "img".
+ * Update "img" to remove "preimage" and replace it with "postimage".
+ */
+static void update_image(struct image *img,
+			 int applied_pos,
+			 struct image *preimage,
+			 struct image *postimage)
+{
+	/*
+	 * remove the copy of preimage at offset in img
+	 * and replace it with postimage
+	 */
+	int i, nr;
+	size_t remove_count, insert_count, applied_at = 0;
+	char *result;
+	int preimage_limit;
+
+	/*
+	 * If we are removing blank lines at the end of img,
+	 * the preimage may extend beyond the end.
+	 * If that is the case, we must be careful only to
+	 * remove the part of the preimage that falls within
+	 * the boundaries of img. Initialize preimage_limit
+	 * to the number of lines in the preimage that falls
+	 * within the boundaries.
+	 */
+	preimage_limit = preimage->nr;
+	if (preimage_limit > img->nr - applied_pos)
+		preimage_limit = img->nr - applied_pos;
+
+	for (i = 0; i < applied_pos; i++)
+		applied_at += img->line[i].len;
+
+	remove_count = 0;
+	for (i = 0; i < preimage_limit; i++)
+		remove_count += img->line[applied_pos + i].len;
+	insert_count = postimage->len;
+
+	/* Adjust the contents */
+	result = xmalloc(img->len + insert_count - remove_count + 1);
+	memcpy(result, img->buf, applied_at);
+	memcpy(result + applied_at, postimage->buf, postimage->len);
+	memcpy(result + applied_at + postimage->len,
+	       img->buf + (applied_at + remove_count),
+	       img->len - (applied_at + remove_count));
+	free(img->buf);
+	img->buf = result;
+	img->len += insert_count - remove_count;
+	result[img->len] = '\0';
+
+	/* Adjust the line table */
+	nr = img->nr + postimage->nr - preimage_limit;
+	if (preimage_limit < postimage->nr) {
+		/*
+		 * NOTE: this knows that we never call remove_first_line()
+		 * on anything other than pre/post image.
+		 */
+		img->line = xrealloc(img->line, nr * sizeof(*img->line));
+		img->line_allocated = img->line;
+	}
+	if (preimage_limit != postimage->nr)
+		memmove(img->line + applied_pos + postimage->nr,
+			img->line + applied_pos + preimage_limit,
+			(img->nr - (applied_pos + preimage_limit)) *
+			sizeof(*img->line));
+	memcpy(img->line + applied_pos,
+	       postimage->line,
+	       postimage->nr * sizeof(*img->line));
+	if (!allow_overlap)
+		for (i = 0; i < postimage->nr; i++)
+			img->line[applied_pos + i].flag |= LINE_PATCHED;
+	img->nr = nr;
+}
+
+/*
+ * Use the patch-hunk text in "frag" to prepare two images (preimage and
+ * postimage) for the hunk.  Find lines that match "preimage" in "img" and
+ * replace the part of "img" with "postimage" text.
+ */
+static int apply_one_fragment(struct image *img, struct fragment *frag,
+			      int inaccurate_eof, unsigned ws_rule,
+			      int nth_fragment)
+{
+	int match_beginning, match_end;
+	const char *patch = frag->patch;
+	int size = frag->size;
+	char *old, *oldlines;
+	struct strbuf newlines;
+	int new_blank_lines_at_end = 0;
+	int found_new_blank_lines_at_end = 0;
+	int hunk_linenr = frag->linenr;
+	unsigned long leading, trailing;
+	int pos, applied_pos;
+	struct image preimage;
+	struct image postimage;
+
+	memset(&preimage, 0, sizeof(preimage));
+	memset(&postimage, 0, sizeof(postimage));
+	oldlines = xmalloc(size);
+	strbuf_init(&newlines, size);
+
+	old = oldlines;
+	while (size > 0) {
+		char first;
+		int len = linelen(patch, size);
+		int plen;
+		int added_blank_line = 0;
+		int is_blank_context = 0;
+		size_t start;
+
+		if (!len)
+			break;
+
+		/*
+		 * "plen" is how much of the line we should use for
+		 * the actual patch data. Normally we just remove the
+		 * first character on the line, but if the line is
+		 * followed by "\ No newline", then we also remove the
+		 * last one (which is the newline, of course).
+		 */
+		plen = len - 1;
+		if (len < size && patch[len] == '\\')
+			plen--;
+		first = *patch;
+		if (apply_in_reverse) {
+			if (first == '-')
+				first = '+';
+			else if (first == '+')
+				first = '-';
+		}
+
+		switch (first) {
+		case '\n':
+			/* Newer GNU diff, empty context line */
+			if (plen < 0)
+				/* ... followed by '\No newline'; nothing */
+				break;
+			*old++ = '\n';
+			strbuf_addch(&newlines, '\n');
+			add_line_info(&preimage, "\n", 1, LINE_COMMON);
+			add_line_info(&postimage, "\n", 1, LINE_COMMON);
+			is_blank_context = 1;
+			break;
+		case ' ':
+			if (plen && (ws_rule & WS_BLANK_AT_EOF) &&
+			    ws_blank_line(patch + 1, plen, ws_rule))
+				is_blank_context = 1;
+		case '-':
+			memcpy(old, patch + 1, plen);
+			add_line_info(&preimage, old, plen,
+				      (first == ' ' ? LINE_COMMON : 0));
+			old += plen;
+			if (first == '-')
+				break;
+		/* Fall-through for ' ' */
+		case '+':
+			/* --no-add does not add new lines */
+			if (first == '+' && no_add)
+				break;
+
+			start = newlines.len;
+			if (first != '+' ||
+			    !whitespace_error ||
+			    ws_error_action != correct_ws_error) {
+				strbuf_add(&newlines, patch + 1, plen);
+			}
+			else {
+				ws_fix_copy(&newlines, patch + 1, plen, ws_rule, &applied_after_fixing_ws);
+			}
+			add_line_info(&postimage, newlines.buf + start, newlines.len - start,
+				      (first == '+' ? 0 : LINE_COMMON));
+			if (first == '+' &&
+			    (ws_rule & WS_BLANK_AT_EOF) &&
+			    ws_blank_line(patch + 1, plen, ws_rule))
+				added_blank_line = 1;
+			break;
+		case '@': case '\\':
+			/* Ignore it, we already handled it */
+			break;
+		default:
+			if (apply_verbosely)
+				error(_("invalid start of line: '%c'"), first);
+			return -1;
+		}
+		if (added_blank_line) {
+			if (!new_blank_lines_at_end)
+				found_new_blank_lines_at_end = hunk_linenr;
+			new_blank_lines_at_end++;
+		}
+		else if (is_blank_context)
+			;
+		else
+			new_blank_lines_at_end = 0;
+		patch += len;
+		size -= len;
+		hunk_linenr++;
+	}
+	if (inaccurate_eof &&
+	    old > oldlines && old[-1] == '\n' &&
+	    newlines.len > 0 && newlines.buf[newlines.len - 1] == '\n') {
+		old--;
+		strbuf_setlen(&newlines, newlines.len - 1);
+	}
+
+	leading = frag->leading;
+	trailing = frag->trailing;
+
+	/*
+	 * A hunk to change lines at the beginning would begin with
+	 * @@ -1,L +N,M @@
+	 * but we need to be careful.  -U0 that inserts before the second
+	 * line also has this pattern.
+	 *
+	 * And a hunk to add to an empty file would begin with
+	 * @@ -0,0 +N,M @@
+	 *
+	 * In other words, a hunk that is (frag->oldpos <= 1) with or
+	 * without leading context must match at the beginning.
+	 */
+	match_beginning = (!frag->oldpos ||
+			   (frag->oldpos == 1 && !unidiff_zero));
+
+	/*
+	 * A hunk without trailing lines must match at the end.
+	 * However, we simply cannot tell if a hunk must match end
+	 * from the lack of trailing lines if the patch was generated
+	 * with unidiff without any context.
+	 */
+	match_end = !unidiff_zero && !trailing;
+
+	pos = frag->newpos ? (frag->newpos - 1) : 0;
+	preimage.buf = oldlines;
+	preimage.len = old - oldlines;
+	postimage.buf = newlines.buf;
+	postimage.len = newlines.len;
+	preimage.line = preimage.line_allocated;
+	postimage.line = postimage.line_allocated;
+
+	for (;;) {
+
+		applied_pos = find_pos(img, &preimage, &postimage, pos,
+				       ws_rule, match_beginning, match_end);
+
+		if (applied_pos >= 0)
+			break;
+
+		/* Am I at my context limits? */
+		if ((leading <= p_context) && (trailing <= p_context))
+			break;
+		if (match_beginning || match_end) {
+			match_beginning = match_end = 0;
+			continue;
+		}
+
+		/*
+		 * Reduce the number of context lines; reduce both
+		 * leading and trailing if they are equal otherwise
+		 * just reduce the larger context.
+		 */
+		if (leading >= trailing) {
+			remove_first_line(&preimage);
+			remove_first_line(&postimage);
+			pos--;
+			leading--;
+		}
+		if (trailing > leading) {
+			remove_last_line(&preimage);
+			remove_last_line(&postimage);
+			trailing--;
+		}
+	}
+
+	if (applied_pos >= 0) {
+		if (new_blank_lines_at_end &&
+		    preimage.nr + applied_pos >= img->nr &&
+		    (ws_rule & WS_BLANK_AT_EOF) &&
+		    ws_error_action != nowarn_ws_error) {
+			record_ws_error(WS_BLANK_AT_EOF, "+", 1,
+					found_new_blank_lines_at_end);
+			if (ws_error_action == correct_ws_error) {
+				while (new_blank_lines_at_end--)
+					remove_last_line(&postimage);
+			}
+			/*
+			 * We would want to prevent write_out_results()
+			 * from taking place in apply_patch() that follows
+			 * the callchain led us here, which is:
+			 * apply_patch->check_patch_list->check_patch->
+			 * apply_data->apply_fragments->apply_one_fragment
+			 */
+			if (ws_error_action == die_on_ws_error)
+				apply = 0;
+		}
+
+		if (apply_verbosely && applied_pos != pos) {
+			int offset = applied_pos - pos;
+			if (apply_in_reverse)
+				offset = 0 - offset;
+			fprintf_ln(stderr,
+				   Q_("Hunk #%d succeeded at %d (offset %d line).",
+				      "Hunk #%d succeeded at %d (offset %d lines).",
+				      offset),
+				   nth_fragment, applied_pos + 1, offset);
+		}
+
+		/*
+		 * Warn if it was necessary to reduce the number
+		 * of context lines.
+		 */
+		if ((leading != frag->leading) ||
+		    (trailing != frag->trailing))
+			fprintf_ln(stderr, _("Context reduced to (%ld/%ld)"
+					     " to apply fragment at %d"),
+				   leading, trailing, applied_pos+1);
+		update_image(img, applied_pos, &preimage, &postimage);
+	} else {
+		if (apply_verbosely)
+			error(_("while searching for:\n%.*s"),
+			      (int)(old - oldlines), oldlines);
+	}
+
+	free(oldlines);
+	strbuf_release(&newlines);
+	free(preimage.line_allocated);
+	free(postimage.line_allocated);
+
+	return (applied_pos < 0);
+}
+
+static int apply_binary_fragment(struct image *img, struct patch *patch)
+{
+	struct fragment *fragment = patch->fragments;
+	unsigned long len;
+	void *dst;
+
+	if (!fragment)
+		return error(_("missing binary patch data for '%s'"),
+			     patch->new_name ?
+			     patch->new_name :
+			     patch->old_name);
+
+	/* Binary patch is irreversible without the optional second hunk */
+	if (apply_in_reverse) {
+		if (!fragment->next)
+			return error("cannot reverse-apply a binary patch "
+				     "without the reverse hunk to '%s'",
+				     patch->new_name
+				     ? patch->new_name : patch->old_name);
+		fragment = fragment->next;
+	}
+	switch (fragment->binary_patch_method) {
+	case BINARY_DELTA_DEFLATED:
+		dst = patch_delta(img->buf, img->len, fragment->patch,
+				  fragment->size, &len);
+		if (!dst)
+			return -1;
+		clear_image(img);
+		img->buf = dst;
+		img->len = len;
+		return 0;
+	case BINARY_LITERAL_DEFLATED:
+		clear_image(img);
+		img->len = fragment->size;
+		img->buf = xmalloc(img->len+1);
+		memcpy(img->buf, fragment->patch, img->len);
+		img->buf[img->len] = '\0';
+		return 0;
+	}
+	return -1;
+}
+
+/*
+ * Replace "img" with the result of applying the binary patch.
+ * The binary patch data itself in patch->fragment is still kept
+ * but the preimage prepared by the caller in "img" is freed here
+ * or in the helper function apply_binary_fragment() this calls.
+ */
+static int apply_binary(struct image *img, struct patch *patch)
+{
+	const char *name = patch->old_name ? patch->old_name : patch->new_name;
+	unsigned char sha1[20];
+
+	/*
+	 * For safety, we require patch index line to contain
+	 * full 40-byte textual SHA1 for old and new, at least for now.
+	 */
+	if (strlen(patch->old_sha1_prefix) != 40 ||
+	    strlen(patch->new_sha1_prefix) != 40 ||
+	    get_sha1_hex(patch->old_sha1_prefix, sha1) ||
+	    get_sha1_hex(patch->new_sha1_prefix, sha1))
+		return error("cannot apply binary patch to '%s' "
+			     "without full index line", name);
+
+	if (patch->old_name) {
+		/*
+		 * See if the old one matches what the patch
+		 * applies to.
+		 */
+		hash_sha1_file(img->buf, img->len, blob_type, sha1);
+		if (strcmp(sha1_to_hex(sha1), patch->old_sha1_prefix))
+			return error("the patch applies to '%s' (%s), "
+				     "which does not match the "
+				     "current contents.",
+				     name, sha1_to_hex(sha1));
+	}
+	else {
+		/* Otherwise, the old one must be empty. */
+		if (img->len)
+			return error("the patch applies to an empty "
+				     "'%s' but it is not empty", name);
+	}
+
+	get_sha1_hex(patch->new_sha1_prefix, sha1);
+	if (is_null_sha1(sha1)) {
+		clear_image(img);
+		return 0; /* deletion patch */
+	}
+
+	if (has_sha1_file(sha1)) {
+		/* We already have the postimage */
+		enum object_type type;
+		unsigned long size;
+		char *result;
+
+		result = read_sha1_file(sha1, &type, &size);
+		if (!result)
+			return error("the necessary postimage %s for "
+				     "'%s' cannot be read",
+				     patch->new_sha1_prefix, name);
+		clear_image(img);
+		img->buf = result;
+		img->len = size;
+	} else {
+		/*
+		 * We have verified buf matches the preimage;
+		 * apply the patch data to it, which is stored
+		 * in the patch->fragments->{patch,size}.
+		 */
+		if (apply_binary_fragment(img, patch))
+			return error(_("binary patch does not apply to '%s'"),
+				     name);
+
+		/* verify that the result matches */
+		hash_sha1_file(img->buf, img->len, blob_type, sha1);
+		if (strcmp(sha1_to_hex(sha1), patch->new_sha1_prefix))
+			return error(_("binary patch to '%s' creates incorrect result (expecting %s, got %s)"),
+				name, patch->new_sha1_prefix, sha1_to_hex(sha1));
+	}
+
+	return 0;
+}
+
+static int apply_fragments(struct image *img, struct patch *patch)
+{
+	struct fragment *frag = patch->fragments;
+	const char *name = patch->old_name ? patch->old_name : patch->new_name;
+	unsigned ws_rule = patch->ws_rule;
+	unsigned inaccurate_eof = patch->inaccurate_eof;
+	int nth = 0;
+
+	if (patch->is_binary)
+		return apply_binary(img, patch);
+
+	while (frag) {
+		nth++;
+		if (apply_one_fragment(img, frag, inaccurate_eof, ws_rule, nth)) {
+			error(_("patch failed: %s:%ld"), name, frag->oldpos);
+			if (!apply_with_reject)
+				return -1;
+			frag->rejected = 1;
+		}
+		frag = frag->next;
+	}
+	return 0;
+}
+
+static int read_blob_object(struct strbuf *buf, const unsigned char *sha1, unsigned mode)
+{
+	if (S_ISGITLINK(mode)) {
+		strbuf_grow(buf, 100);
+		strbuf_addf(buf, "Subproject commit %s\n", sha1_to_hex(sha1));
+	} else {
+		enum object_type type;
+		unsigned long sz;
+		char *result;
+
+		result = read_sha1_file(sha1, &type, &sz);
+		if (!result)
+			return -1;
+		/* XXX read_sha1_file NUL-terminates */
+		strbuf_attach(buf, result, sz, sz + 1);
+	}
+	return 0;
+}
+
+static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf)
+{
+	if (!ce)
+		return 0;
+	return read_blob_object(buf, ce->sha1, ce->ce_mode);
+}
+
+static struct patch *in_fn_table(const char *name)
+{
+	struct string_list_item *item;
+
+	if (name == NULL)
+		return NULL;
+
+	item = string_list_lookup(&fn_table, name);
+	if (item != NULL)
+		return (struct patch *)item->util;
+
+	return NULL;
+}
+
+/*
+ * item->util in the filename table records the status of the path.
+ * Usually it points at a patch (whose result records the contents
+ * of it after applying it), but it could be PATH_WAS_DELETED for a
+ * path that a previously applied patch has already removed, or
+ * PATH_TO_BE_DELETED for a path that a later patch would remove.
+ *
+ * The latter is needed to deal with a case where two paths A and B
+ * are swapped by first renaming A to B and then renaming B to A;
+ * moving A to B should not be prevented due to presense of B as we
+ * will remove it in a later patch.
+ */
+#define PATH_TO_BE_DELETED ((struct patch *) -2)
+#define PATH_WAS_DELETED ((struct patch *) -1)
+
+static int to_be_deleted(struct patch *patch)
+{
+	return patch == PATH_TO_BE_DELETED;
+}
+
+static int was_deleted(struct patch *patch)
+{
+	return patch == PATH_WAS_DELETED;
+}
+
+static void add_to_fn_table(struct patch *patch)
+{
+	struct string_list_item *item;
+
+	/*
+	 * Always add new_name unless patch is a deletion
+	 * This should cover the cases for normal diffs,
+	 * file creations and copies
+	 */
+	if (patch->new_name != NULL) {
+		item = string_list_insert(&fn_table, patch->new_name);
+		item->util = patch;
+	}
+
+	/*
+	 * store a failure on rename/deletion cases because
+	 * later chunks shouldn't patch old names
+	 */
+	if ((patch->new_name == NULL) || (patch->is_rename)) {
+		item = string_list_insert(&fn_table, patch->old_name);
+		item->util = PATH_WAS_DELETED;
+	}
+}
+
+static void prepare_fn_table(struct patch *patch)
+{
+	/*
+	 * store information about incoming file deletion
+	 */
+	while (patch) {
+		if ((patch->new_name == NULL) || (patch->is_rename)) {
+			struct string_list_item *item;
+			item = string_list_insert(&fn_table, patch->old_name);
+			item->util = PATH_TO_BE_DELETED;
+		}
+		patch = patch->next;
+	}
+}
+
+static int checkout_target(struct cache_entry *ce, struct stat *st)
+{
+	struct checkout costate;
+
+	memset(&costate, 0, sizeof(costate));
+	costate.base_dir = "";
+	costate.refresh_cache = 1;
+	if (checkout_entry(ce, &costate, NULL) || lstat(ce->name, st))
+		return error(_("cannot checkout %s"), ce->name);
+	return 0;
+}
+
+static struct patch *previous_patch(struct patch *patch, int *gone)
+{
+	struct patch *previous;
+
+	*gone = 0;
+	if (patch->is_copy || patch->is_rename)
+		return NULL; /* "git" patches do not depend on the order */
+
+	previous = in_fn_table(patch->old_name);
+	if (!previous)
+		return NULL;
+
+	if (to_be_deleted(previous))
+		return NULL; /* the deletion hasn't happened yet */
+
+	if (was_deleted(previous))
+		*gone = 1;
+
+	return previous;
+}
+
+static int verify_index_match(struct cache_entry *ce, struct stat *st)
+{
+	if (S_ISGITLINK(ce->ce_mode)) {
+		if (!S_ISDIR(st->st_mode))
+			return -1;
+		return 0;
+	}
+	return ce_match_stat(ce, st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);
+}
+
+#define SUBMODULE_PATCH_WITHOUT_INDEX 1
+
+static int load_patch_target(struct strbuf *buf,
+			     struct cache_entry *ce,
+			     struct stat *st,
+			     const char *name,
+			     unsigned expected_mode)
+{
+	if (cached) {
+		if (read_file_or_gitlink(ce, buf))
+			return error(_("read of %s failed"), name);
+	} else if (name) {
+		if (S_ISGITLINK(expected_mode)) {
+			if (ce)
+				return read_file_or_gitlink(ce, buf);
+			else
+				return SUBMODULE_PATCH_WITHOUT_INDEX;
+		} else {
+			if (read_old_data(st, name, buf))
+				return error(_("read of %s failed"), name);
+		}
+	}
+	return 0;
+}
+
+/*
+ * We are about to apply "patch"; populate the "image" with the
+ * current version we have, from the working tree or from the index,
+ * depending on the situation e.g. --cached/--index.  If we are
+ * applying a non-git patch that incrementally updates the tree,
+ * we read from the result of a previous diff.
+ */
+static int load_preimage(struct image *image,
+			 struct patch *patch, struct stat *st, struct cache_entry *ce)
+{
+	struct strbuf buf = STRBUF_INIT;
+	size_t len;
+	char *img;
+	struct patch *previous;
+	int status;
+
+	previous = previous_patch(patch, &status);
+	if (status)
+		return error(_("path %s has been renamed/deleted"),
+			     patch->old_name);
+	if (previous) {
+		/* We have a patched copy in memory; use that. */
+		strbuf_add(&buf, previous->result, previous->resultsize);
+	} else {
+		status = load_patch_target(&buf, ce, st,
+					   patch->old_name, patch->old_mode);
+		if (status < 0)
+			return status;
+		else if (status == SUBMODULE_PATCH_WITHOUT_INDEX) {
+			/*
+			 * There is no way to apply subproject
+			 * patch without looking at the index.
+			 * NEEDSWORK: shouldn't this be flagged
+			 * as an error???
+			 */
+			free_fragment_list(patch->fragments);
+			patch->fragments = NULL;
+		} else if (status) {
+			return error(_("read of %s failed"), patch->old_name);
+		}
+	}
+
+	img = strbuf_detach(&buf, &len);
+	prepare_image(image, img, len, !patch->is_binary);
+	return 0;
+}
+
+static int three_way_merge(struct image *image,
+			   char *path,
+			   const unsigned char *base,
+			   const unsigned char *ours,
+			   const unsigned char *theirs)
+{
+	mmfile_t base_file, our_file, their_file;
+	mmbuffer_t result = { NULL };
+	int status;
+
+	read_mmblob(&base_file, base);
+	read_mmblob(&our_file, ours);
+	read_mmblob(&their_file, theirs);
+	status = ll_merge(&result, path,
+			  &base_file, "base",
+			  &our_file, "ours",
+			  &their_file, "theirs", NULL);
+	free(base_file.ptr);
+	free(our_file.ptr);
+	free(their_file.ptr);
+	if (status < 0 || !result.ptr) {
+		free(result.ptr);
+		return -1;
+	}
+	clear_image(image);
+	image->buf = result.ptr;
+	image->len = result.size;
+
+	return status;
+}
+
+/*
+ * When directly falling back to add/add three-way merge, we read from
+ * the current contents of the new_name.  In no cases other than that
+ * this function will be called.
+ */
+static int load_current(struct image *image, struct patch *patch)
+{
+	struct strbuf buf = STRBUF_INIT;
+	int status, pos;
+	size_t len;
+	char *img;
+	struct stat st;
+	struct cache_entry *ce;
+	char *name = patch->new_name;
+	unsigned mode = patch->new_mode;
+
+	if (!patch->is_new)
+		die("BUG: patch to %s is not a creation", patch->old_name);
+
+	pos = cache_name_pos(name, strlen(name));
+	if (pos < 0)
+		return error(_("%s: does not exist in index"), name);
+	ce = active_cache[pos];
+	if (lstat(name, &st)) {
+		if (errno != ENOENT)
+			return error(_("%s: %s"), name, strerror(errno));
+		if (checkout_target(ce, &st))
+			return -1;
+	}
+	if (verify_index_match(ce, &st))
+		return error(_("%s: does not match index"), name);
+
+	status = load_patch_target(&buf, ce, &st, name, mode);
+	if (status < 0)
+		return status;
+	else if (status)
+		return -1;
+	img = strbuf_detach(&buf, &len);
+	prepare_image(image, img, len, !patch->is_binary);
+	return 0;
+}
+
+static int try_threeway(struct image *image, struct patch *patch,
+			struct stat *st, struct cache_entry *ce)
+{
+	unsigned char pre_sha1[20], post_sha1[20], our_sha1[20];
+	struct strbuf buf = STRBUF_INIT;
+	size_t len;
+	int status;
+	char *img;
+	struct image tmp_image;
+
+	/* No point falling back to 3-way merge in these cases */
+	if (patch->is_delete ||
+	    S_ISGITLINK(patch->old_mode) || S_ISGITLINK(patch->new_mode))
+		return -1;
+
+	/* Preimage the patch was prepared for */
+	if (patch->is_new)
+		write_sha1_file("", 0, blob_type, pre_sha1);
+	else if (get_sha1(patch->old_sha1_prefix, pre_sha1) ||
+		 read_blob_object(&buf, pre_sha1, patch->old_mode))
+		return error("repository lacks the necessary blob to fall back on 3-way merge.");
+
+	fprintf(stderr, "Falling back to three-way merge...\n");
+
+	img = strbuf_detach(&buf, &len);
+	prepare_image(&tmp_image, img, len, 1);
+	/* Apply the patch to get the post image */
+	if (apply_fragments(&tmp_image, patch) < 0) {
+		clear_image(&tmp_image);
+		return -1;
+	}
+	/* post_sha1[] is theirs */
+	write_sha1_file(tmp_image.buf, tmp_image.len, blob_type, post_sha1);
+	clear_image(&tmp_image);
+
+	/* our_sha1[] is ours */
+	if (patch->is_new) {
+		if (load_current(&tmp_image, patch))
+			return error("cannot read the current contents of '%s'",
+				     patch->new_name);
+	} else {
+		if (load_preimage(&tmp_image, patch, st, ce))
+			return error("cannot read the current contents of '%s'",
+				     patch->old_name);
+	}
+	write_sha1_file(tmp_image.buf, tmp_image.len, blob_type, our_sha1);
+	clear_image(&tmp_image);
+
+	/* in-core three-way merge between post and our using pre as base */
+	status = three_way_merge(image, patch->new_name,
+				 pre_sha1, our_sha1, post_sha1);
+	if (status < 0) {
+		fprintf(stderr, "Failed to fall back on three-way merge...\n");
+		return status;
+	}
+
+	if (status) {
+		patch->conflicted_threeway = 1;
+		if (patch->is_new)
+			hashclr(patch->threeway_stage[0]);
+		else
+			hashcpy(patch->threeway_stage[0], pre_sha1);
+		hashcpy(patch->threeway_stage[1], our_sha1);
+		hashcpy(patch->threeway_stage[2], post_sha1);
+		fprintf(stderr, "Applied patch to '%s' with conflicts.\n", patch->new_name);
+	} else {
+		fprintf(stderr, "Applied patch to '%s' cleanly.\n", patch->new_name);
+	}
+	return 0;
+}
+
+static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)
+{
+	struct image image;
+
+	if (load_preimage(&image, patch, st, ce) < 0)
+		return -1;
+
+	if (patch->direct_to_threeway ||
+	    apply_fragments(&image, patch) < 0) {
+		/* Note: with --reject, apply_fragments() returns 0 */
+		if (!threeway || try_threeway(&image, patch, st, ce) < 0)
+			return -1;
+	}
+	patch->result = image.buf;
+	patch->resultsize = image.len;
+	add_to_fn_table(patch);
+	free(image.line_allocated);
+
+	if (0 < patch->is_delete && patch->resultsize)
+		return error(_("removal patch leaves file contents"));
+
+	return 0;
+}
+
+/*
+ * If "patch" that we are looking at modifies or deletes what we have,
+ * we would want it not to lose any local modification we have, either
+ * in the working tree or in the index.
+ *
+ * This also decides if a non-git patch is a creation patch or a
+ * modification to an existing empty file.  We do not check the state
+ * of the current tree for a creation patch in this function; the caller
+ * check_patch() separately makes sure (and errors out otherwise) that
+ * the path the patch creates does not exist in the current tree.
+ */
+static int check_preimage(struct patch *patch, struct cache_entry **ce, struct stat *st)
+{
+	const char *old_name = patch->old_name;
+	struct patch *previous = NULL;
+	int stat_ret = 0, status;
+	unsigned st_mode = 0;
+
+	if (!old_name)
+		return 0;
+
+	assert(patch->is_new <= 0);
+	previous = previous_patch(patch, &status);
+
+	if (status)
+		return error(_("path %s has been renamed/deleted"), old_name);
+	if (previous) {
+		st_mode = previous->new_mode;
+	} else if (!cached) {
+		stat_ret = lstat(old_name, st);
+		if (stat_ret && errno != ENOENT)
+			return error(_("%s: %s"), old_name, strerror(errno));
+	}
+
+	if (check_index && !previous) {
+		int pos = cache_name_pos(old_name, strlen(old_name));
+		if (pos < 0) {
+			if (patch->is_new < 0)
+				goto is_new;
+			return error(_("%s: does not exist in index"), old_name);
+		}
+		*ce = active_cache[pos];
+		if (stat_ret < 0) {
+			if (checkout_target(*ce, st))
+				return -1;
+		}
+		if (!cached && verify_index_match(*ce, st))
+			return error(_("%s: does not match index"), old_name);
+		if (cached)
+			st_mode = (*ce)->ce_mode;
+	} else if (stat_ret < 0) {
+		if (patch->is_new < 0)
+			goto is_new;
+		return error(_("%s: %s"), old_name, strerror(errno));
+	}
+
+	if (!cached && !previous)
+		st_mode = ce_mode_from_stat(*ce, st->st_mode);
+
+	if (patch->is_new < 0)
+		patch->is_new = 0;
+	if (!patch->old_mode)
+		patch->old_mode = st_mode;
+	if ((st_mode ^ patch->old_mode) & S_IFMT)
+		return error(_("%s: wrong type"), old_name);
+	if (st_mode != patch->old_mode)
+		warning(_("%s has type %o, expected %o"),
+			old_name, st_mode, patch->old_mode);
+	if (!patch->new_mode && !patch->is_delete)
+		patch->new_mode = st_mode;
+	return 0;
+
+ is_new:
+	patch->is_new = 1;
+	patch->is_delete = 0;
+	free(patch->old_name);
+	patch->old_name = NULL;
+	return 0;
+}
+
+
+#define EXISTS_IN_INDEX 1
+#define EXISTS_IN_WORKTREE 2
+
+static int check_to_create(const char *new_name, int ok_if_exists)
+{
+	struct stat nst;
+
+	if (check_index &&
+	    cache_name_pos(new_name, strlen(new_name)) >= 0 &&
+	    !ok_if_exists)
+		return EXISTS_IN_INDEX;
+	if (cached)
+		return 0;
+
+	if (!lstat(new_name, &nst)) {
+		if (S_ISDIR(nst.st_mode) || ok_if_exists)
+			return 0;
+		/*
+		 * A leading component of new_name might be a symlink
+		 * that is going to be removed with this patch, but
+		 * still pointing at somewhere that has the path.
+		 * In such a case, path "new_name" does not exist as
+		 * far as git is concerned.
+		 */
+		if (has_symlink_leading_path(new_name, strlen(new_name)))
+			return 0;
+
+		return EXISTS_IN_WORKTREE;
+	} else if ((errno != ENOENT) && (errno != ENOTDIR)) {
+		return error("%s: %s", new_name, strerror(errno));
+	}
+	return 0;
+}
+
+/*
+ * Check and apply the patch in-core; leave the result in patch->result
+ * for the caller to write it out to the final destination.
+ */
+static int check_patch(struct patch *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;
+	struct patch *tpatch;
+	int ok_if_exists;
+	int status;
+
+	patch->rejected = 1; /* we will drop this after we succeed */
+
+	status = check_preimage(patch, &ce, &st);
+	if (status)
+		return status;
+	old_name = patch->old_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.
+	 *
+	 * A patch to swap-rename between A and B would first rename A
+	 * to B and then rename B to A.  While applying the first one,
+	 * the presense of B should not stop A from getting renamed to
+	 * B; ask to_be_deleted() about the later rename.  Removal of
+	 * B and rename from A to B is handled the same way by asking
+	 * was_deleted().
+	 */
+	if ((tpatch = in_fn_table(new_name)) &&
+	    (was_deleted(tpatch) || to_be_deleted(tpatch)))
+		ok_if_exists = 1;
+	else
+		ok_if_exists = 0;
+
+	if (new_name &&
+	    ((0 < patch->is_new) | (0 < patch->is_rename) | patch->is_copy)) {
+		int err = check_to_create(new_name, ok_if_exists);
+
+		if (err && threeway) {
+			patch->direct_to_threeway = 1;
+		} else switch (err) {
+		case 0:
+			break; /* happy */
+		case EXISTS_IN_INDEX:
+			return error(_("%s: already exists in index"), new_name);
+			break;
+		case EXISTS_IN_WORKTREE:
+			return error(_("%s: already exists in working directory"),
+				     new_name);
+		default:
+			return err;
+		}
+
+		if (!patch->new_mode) {
+			if (0 < patch->is_new)
+				patch->new_mode = S_IFREG | 0644;
+			else
+				patch->new_mode = patch->old_mode;
+		}
+	}
+
+	if (new_name && old_name) {
+		int same = !strcmp(old_name, new_name);
+		if (!patch->new_mode)
+			patch->new_mode = patch->old_mode;
+		if ((patch->old_mode ^ patch->new_mode) & S_IFMT) {
+			if (same)
+				return error(_("new mode (%o) of %s does not "
+					       "match old mode (%o)"),
+					patch->new_mode, new_name,
+					patch->old_mode);
+			else
+				return error(_("new mode (%o) of %s does not "
+					       "match old mode (%o) of %s"),
+					patch->new_mode, new_name,
+					patch->old_mode, old_name);
+		}
+	}
+
+	if (apply_data(patch, &st, ce) < 0)
+		return error(_("%s: patch does not apply"), name);
+	patch->rejected = 0;
+	return 0;
+}
+
+static int check_patch_list(struct patch *patch)
+{
+	int err = 0;
+
+	prepare_fn_table(patch);
+	while (patch) {
+		if (apply_verbosely)
+			say_patch_name(stderr,
+				       _("Checking patch %s..."), patch);
+		err |= check_patch(patch);
+		patch = patch->next;
+	}
+	return err;
+}
+
+/* This function tries to read the sha1 from the current index */
+static int get_current_sha1(const char *path, unsigned char *sha1)
+{
+	int pos;
+
+	if (read_cache() < 0)
+		return -1;
+	pos = cache_name_pos(path, strlen(path));
+	if (pos < 0)
+		return -1;
+	hashcpy(sha1, active_cache[pos]->sha1);
+	return 0;
+}
+
+/* Build an index that contains the just the files needed for a 3way merge */
+static void build_fake_ancestor(struct patch *list, const char *filename)
+{
+	struct patch *patch;
+	struct index_state result = { NULL };
+	int fd;
+
+	/* Once we start supporting the reverse patch, it may be
+	 * worth showing the new sha1 prefix, but until then...
+	 */
+	for (patch = list; patch; patch = patch->next) {
+		const unsigned char *sha1_ptr;
+		unsigned char sha1[20];
+		struct cache_entry *ce;
+		const char *name;
+
+		name = patch->old_name ? patch->old_name : patch->new_name;
+		if (0 < patch->is_new)
+			continue;
+		else if (get_sha1_blob(patch->old_sha1_prefix, sha1))
+			/* git diff has no index line for mode/type changes */
+			if (!patch->lines_added && !patch->lines_deleted) {
+				if (get_current_sha1(patch->old_name, sha1))
+					die("mode change for %s, which is not "
+						"in current HEAD", name);
+				sha1_ptr = sha1;
+			} else
+				die("sha1 information is lacking or useless "
+					"(%s).", name);
+		else
+			sha1_ptr = sha1;
+
+		ce = make_cache_entry(patch->old_mode, sha1_ptr, name, 0, 0);
+		if (!ce)
+			die(_("make_cache_entry failed for path '%s'"), name);
+		if (add_index_entry(&result, ce, ADD_CACHE_OK_TO_ADD))
+			die ("Could not add %s to temporary index", name);
+	}
+
+	fd = open(filename, O_WRONLY | O_CREAT, 0666);
+	if (fd < 0 || write_index(&result, fd) || close(fd))
+		die ("Could not write temporary index to %s", filename);
+
+	discard_index(&result);
+}
+
+static void stat_patch_list(struct patch *patch)
+{
+	int files, adds, dels;
+
+	for (files = adds = dels = 0 ; patch ; patch = patch->next) {
+		files++;
+		adds += patch->lines_added;
+		dels += patch->lines_deleted;
+		show_stats(patch);
+	}
+
+	print_stat_summary(stdout, files, adds, dels);
+}
+
+static void numstat_patch_list(struct patch *patch)
+{
+	for ( ; patch; patch = patch->next) {
+		const char *name;
+		name = patch->new_name ? patch->new_name : patch->old_name;
+		if (patch->is_binary)
+			printf("-\t-\t");
+		else
+			printf("%d\t%d\t", patch->lines_added, patch->lines_deleted);
+		write_name_quoted(name, stdout, line_termination);
+	}
+}
+
+static void show_file_mode_name(const char *newdelete, unsigned int mode, const char *name)
+{
+	if (mode)
+		printf(" %s mode %06o %s\n", newdelete, mode, name);
+	else
+		printf(" %s %s\n", newdelete, name);
+}
+
+static void show_mode_change(struct patch *p, int show_name)
+{
+	if (p->old_mode && p->new_mode && p->old_mode != p->new_mode) {
+		if (show_name)
+			printf(" mode change %06o => %06o %s\n",
+			       p->old_mode, p->new_mode, p->new_name);
+		else
+			printf(" mode change %06o => %06o\n",
+			       p->old_mode, p->new_mode);
+	}
+}
+
+static void show_rename_copy(struct patch *p)
+{
+	const char *renamecopy = p->is_rename ? "rename" : "copy";
+	const char *old, *new;
+
+	/* Find common prefix */
+	old = p->old_name;
+	new = p->new_name;
+	while (1) {
+		const char *slash_old, *slash_new;
+		slash_old = strchr(old, '/');
+		slash_new = strchr(new, '/');
+		if (!slash_old ||
+		    !slash_new ||
+		    slash_old - old != slash_new - new ||
+		    memcmp(old, new, slash_new - new))
+			break;
+		old = slash_old + 1;
+		new = slash_new + 1;
+	}
+	/* p->old_name thru old is the common prefix, and old and new
+	 * through the end of names are renames
+	 */
+	if (old != p->old_name)
+		printf(" %s %.*s{%s => %s} (%d%%)\n", renamecopy,
+		       (int)(old - p->old_name), p->old_name,
+		       old, new, p->score);
+	else
+		printf(" %s %s => %s (%d%%)\n", renamecopy,
+		       p->old_name, p->new_name, p->score);
+	show_mode_change(p, 0);
+}
+
+static void summary_patch_list(struct patch *patch)
+{
+	struct patch *p;
+
+	for (p = patch; p; p = p->next) {
+		if (p->is_new)
+			show_file_mode_name("create", p->new_mode, p->new_name);
+		else if (p->is_delete)
+			show_file_mode_name("delete", p->old_mode, p->old_name);
+		else {
+			if (p->is_rename || p->is_copy)
+				show_rename_copy(p);
+			else {
+				if (p->score) {
+					printf(" rewrite %s (%d%%)\n",
+					       p->new_name, p->score);
+					show_mode_change(p, 0);
+				}
+				else
+					show_mode_change(p, 1);
+			}
+		}
+	}
+}
+
+static void patch_stats(struct patch *patch)
+{
+	int lines = patch->lines_added + patch->lines_deleted;
+
+	if (lines > max_change)
+		max_change = lines;
+	if (patch->old_name) {
+		int len = quote_c_style(patch->old_name, NULL, NULL, 0);
+		if (!len)
+			len = strlen(patch->old_name);
+		if (len > max_len)
+			max_len = len;
+	}
+	if (patch->new_name) {
+		int len = quote_c_style(patch->new_name, NULL, NULL, 0);
+		if (!len)
+			len = strlen(patch->new_name);
+		if (len > max_len)
+			max_len = len;
+	}
+}
+
+static void remove_file(struct patch *patch, int rmdir_empty)
+{
+	if (update_index) {
+		if (remove_file_from_cache(patch->old_name) < 0)
+			die(_("unable to remove %s from index"), patch->old_name);
+	}
+	if (!cached) {
+		if (!remove_or_warn(patch->old_mode, patch->old_name) && rmdir_empty) {
+			remove_path(patch->old_name);
+		}
+	}
+}
+
+static void add_index_file(const char *path, unsigned mode, void *buf, unsigned long size)
+{
+	struct stat st;
+	struct cache_entry *ce;
+	int namelen = strlen(path);
+	unsigned ce_size = cache_entry_size(namelen);
+
+	if (!update_index)
+		return;
+
+	ce = xcalloc(1, ce_size);
+	memcpy(ce->name, path, namelen);
+	ce->ce_mode = create_ce_mode(mode);
+	ce->ce_flags = create_ce_flags(0);
+	ce->ce_namelen = namelen;
+	if (S_ISGITLINK(mode)) {
+		const char *s = buf;
+
+		if (get_sha1_hex(s + strlen("Subproject commit "), ce->sha1))
+			die(_("corrupt patch for subproject %s"), path);
+	} else {
+		if (!cached) {
+			if (lstat(path, &st) < 0)
+				die_errno(_("unable to stat newly created file '%s'"),
+					  path);
+			fill_stat_cache_info(ce, &st);
+		}
+		if (write_sha1_file(buf, size, blob_type, ce->sha1) < 0)
+			die(_("unable to create backing store for newly created file %s"), path);
+	}
+	if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0)
+		die(_("unable to add cache entry for %s"), path);
+}
+
+static int try_create_file(const char *path, unsigned int mode, const char *buf, unsigned long size)
+{
+	int fd;
+	struct strbuf nbuf = STRBUF_INIT;
+
+	if (S_ISGITLINK(mode)) {
+		struct stat st;
+		if (!lstat(path, &st) && S_ISDIR(st.st_mode))
+			return 0;
+		return mkdir(path, 0777);
+	}
+
+	if (has_symlinks && 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)
+		return -1;
+
+	if (convert_to_working_tree(path, buf, size, &nbuf)) {
+		size = nbuf.len;
+		buf  = nbuf.buf;
+	}
+	write_or_die(fd, buf, size);
+	strbuf_release(&nbuf);
+
+	if (close(fd) < 0)
+		die_errno(_("closing file '%s'"), path);
+	return 0;
+}
+
+/*
+ * We optimistically assume that the directories exist,
+ * which is true 99% of the time anyway. If they don't,
+ * we create them and try again.
+ */
+static void create_one_file(char *path, unsigned mode, const char *buf, unsigned long size)
+{
+	if (cached)
+		return;
+	if (!try_create_file(path, mode, buf, size))
+		return;
+
+	if (errno == ENOENT) {
+		if (safe_create_leading_directories(path))
+			return;
+		if (!try_create_file(path, mode, buf, size))
+			return;
+	}
+
+	if (errno == EEXIST || errno == EACCES) {
+		/* We may be trying to create a file where a directory
+		 * used to be.
+		 */
+		struct stat st;
+		if (!lstat(path, &st) && (!S_ISDIR(st.st_mode) || !rmdir(path)))
+			errno = EEXIST;
+	}
+
+	if (errno == EEXIST) {
+		unsigned int nr = getpid();
+
+		for (;;) {
+			char newpath[PATH_MAX];
+			mksnpath(newpath, sizeof(newpath), "%s~%u", path, nr);
+			if (!try_create_file(newpath, mode, buf, size)) {
+				if (!rename(newpath, path))
+					return;
+				unlink_or_warn(newpath);
+				break;
+			}
+			if (errno != EEXIST)
+				break;
+			++nr;
+		}
+	}
+	die_errno(_("unable to write file '%s' mode %o"), path, mode);
+}
+
+static void add_conflicted_stages_file(struct patch *patch)
+{
+	int stage, namelen;
+	unsigned ce_size, mode;
+	struct cache_entry *ce;
+
+	if (!update_index)
+		return;
+	namelen = strlen(patch->new_name);
+	ce_size = cache_entry_size(namelen);
+	mode = patch->new_mode ? patch->new_mode : (S_IFREG | 0644);
+
+	remove_file_from_cache(patch->new_name);
+	for (stage = 1; stage < 4; stage++) {
+		if (is_null_sha1(patch->threeway_stage[stage - 1]))
+			continue;
+		ce = xcalloc(1, ce_size);
+		memcpy(ce->name, patch->new_name, namelen);
+		ce->ce_mode = create_ce_mode(mode);
+		ce->ce_flags = create_ce_flags(stage);
+		ce->ce_namelen = namelen;
+		hashcpy(ce->sha1, patch->threeway_stage[stage - 1]);
+		if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0)
+			die(_("unable to add cache entry for %s"), patch->new_name);
+	}
+}
+
+static void create_file(struct patch *patch)
+{
+	char *path = patch->new_name;
+	unsigned mode = patch->new_mode;
+	unsigned long size = patch->resultsize;
+	char *buf = patch->result;
+
+	if (!mode)
+		mode = S_IFREG | 0644;
+	create_one_file(path, mode, buf, size);
+
+	if (patch->conflicted_threeway)
+		add_conflicted_stages_file(patch);
+	else
+		add_index_file(path, mode, buf, size);
+}
+
+/* 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) {
+		if (phase == 0)
+			remove_file(patch, 1);
+		return;
+	}
+	if (patch->is_new > 0 || patch->is_copy) {
+		if (phase == 1)
+			create_file(patch);
+		return;
+	}
+	/*
+	 * Rename or modification boils down to the same
+	 * thing: remove the old, write the new
+	 */
+	if (phase == 0)
+		remove_file(patch, patch->is_rename);
+	if (phase == 1)
+		create_file(patch);
+}
+
+static int write_out_one_reject(struct patch *patch)
+{
+	FILE *rej;
+	char namebuf[PATH_MAX];
+	struct fragment *frag;
+	int cnt = 0;
+	struct strbuf sb = STRBUF_INIT;
+
+	for (cnt = 0, frag = patch->fragments; frag; frag = frag->next) {
+		if (!frag->rejected)
+			continue;
+		cnt++;
+	}
+
+	if (!cnt) {
+		if (apply_verbosely)
+			say_patch_name(stderr,
+				       _("Applied patch %s cleanly."), patch);
+		return 0;
+	}
+
+	/* This should not happen, because a removal patch that leaves
+	 * contents are marked "rejected" at the patch level.
+	 */
+	if (!patch->new_name)
+		die(_("internal error"));
+
+	/* Say this even without --verbose */
+	strbuf_addf(&sb, Q_("Applying patch %%s with %d reject...",
+			    "Applying patch %%s with %d rejects...",
+			    cnt),
+		    cnt);
+	say_patch_name(stderr, sb.buf, patch);
+	strbuf_release(&sb);
+
+	cnt = strlen(patch->new_name);
+	if (ARRAY_SIZE(namebuf) <= cnt + 5) {
+		cnt = ARRAY_SIZE(namebuf) - 5;
+		warning(_("truncating .rej filename to %.*s.rej"),
+			cnt - 1, patch->new_name);
+	}
+	memcpy(namebuf, patch->new_name, cnt);
+	memcpy(namebuf + cnt, ".rej", 5);
+
+	rej = fopen(namebuf, "w");
+	if (!rej)
+		return error(_("cannot open %s: %s"), namebuf, strerror(errno));
+
+	/* Normal git tools never deal with .rej, so do not pretend
+	 * this is a git patch by saying --git nor give extended
+	 * headers.  While at it, maybe please "kompare" that wants
+	 * the trailing TAB and some garbage at the end of line ;-).
+	 */
+	fprintf(rej, "diff a/%s b/%s\t(rejected hunks)\n",
+		patch->new_name, patch->new_name);
+	for (cnt = 1, frag = patch->fragments;
+	     frag;
+	     cnt++, frag = frag->next) {
+		if (!frag->rejected) {
+			fprintf_ln(stderr, _("Hunk #%d applied cleanly."), cnt);
+			continue;
+		}
+		fprintf_ln(stderr, _("Rejected hunk #%d."), cnt);
+		fprintf(rej, "%.*s", frag->size, frag->patch);
+		if (frag->patch[frag->size-1] != '\n')
+			fputc('\n', rej);
+	}
+	fclose(rej);
+	return -1;
+}
+
+static int write_out_results(struct patch *list)
+{
+	int phase;
+	int errs = 0;
+	struct patch *l;
+	struct string_list cpath = STRING_LIST_INIT_DUP;
+
+	for (phase = 0; phase < 2; phase++) {
+		l = list;
+		while (l) {
+			if (l->rejected)
+				errs = 1;
+			else {
+				write_out_one_result(l, phase);
+				if (phase == 1) {
+					if (write_out_one_reject(l))
+						errs = 1;
+					if (l->conflicted_threeway) {
+						string_list_append(&cpath, l->new_name);
+						errs = 1;
+					}
+				}
+			}
+			l = l->next;
+		}
+	}
+
+	if (cpath.nr) {
+		struct string_list_item *item;
+
+		sort_string_list(&cpath);
+		for_each_string_list_item(item, &cpath)
+			fprintf(stderr, "U %s\n", item->string);
+		string_list_clear(&cpath, 0);
+
+		rerere(0);
+	}
+
+	return errs;
+}
+
+static struct lock_file lock_file;
+
+static struct string_list limit_by_name;
+static int has_include;
+static void add_name_limit(const char *name, int exclude)
+{
+	struct string_list_item *it;
+
+	it = string_list_append(&limit_by_name, name);
+	it->util = exclude ? NULL : (void *) 1;
+}
+
+static int use_patch(struct patch *p)
+{
+	const char *pathname = p->new_name ? p->new_name : p->old_name;
+	int i;
+
+	/* Paths outside are not touched regardless of "--include" */
+	if (0 < prefix_length) {
+		int pathlen = strlen(pathname);
+		if (pathlen <= prefix_length ||
+		    memcmp(prefix, pathname, prefix_length))
+			return 0;
+	}
+
+	/* See if it matches any of exclude/include rule */
+	for (i = 0; i < limit_by_name.nr; i++) {
+		struct string_list_item *it = &limit_by_name.items[i];
+		if (!fnmatch(it->string, pathname, 0))
+			return (it->util != NULL);
+	}
+
+	/*
+	 * If we had any include, a path that does not match any rule is
+	 * not used.  Otherwise, we saw bunch of exclude rules (or none)
+	 * and such a path is used.
+	 */
+	return !has_include;
+}
+
+
+static void prefix_one(char **name)
+{
+	char *old_name = *name;
+	if (!old_name)
+		return;
+	*name = xstrdup(prefix_filename(prefix, prefix_length, *name));
+	free(old_name);
+}
+
+static void prefix_patches(struct patch *p)
+{
+	if (!prefix || p->is_toplevel_relative)
+		return;
+	for ( ; p; p = p->next) {
+		prefix_one(&p->new_name);
+		prefix_one(&p->old_name);
+	}
+}
+
+#define INACCURATE_EOF	(1<<0)
+#define RECOUNT		(1<<1)
+
+static int apply_patch(int fd, const char *filename, int options)
+{
+	size_t offset;
+	struct strbuf buf = STRBUF_INIT; /* owns the patch text */
+	struct patch *list = NULL, **listp = &list;
+	int skipped_patch = 0;
+
+	patch_input_file = filename;
+	read_patch_file(&buf, fd);
+	offset = 0;
+	while (offset < buf.len) {
+		struct patch *patch;
+		int nr;
+
+		patch = xcalloc(1, sizeof(*patch));
+		patch->inaccurate_eof = !!(options & INACCURATE_EOF);
+		patch->recount =  !!(options & RECOUNT);
+		nr = parse_chunk(buf.buf + offset, buf.len - offset, patch);
+		if (nr < 0)
+			break;
+		if (apply_in_reverse)
+			reverse_patches(patch);
+		if (prefix)
+			prefix_patches(patch);
+		if (use_patch(patch)) {
+			patch_stats(patch);
+			*listp = patch;
+			listp = &patch->next;
+		}
+		else {
+			free_patch(patch);
+			skipped_patch++;
+		}
+		offset += nr;
+	}
+
+	if (!list && !skipped_patch)
+		die(_("unrecognized input"));
+
+	if (whitespace_error && (ws_error_action == die_on_ws_error))
+		apply = 0;
+
+	update_index = check_index && apply;
+	if (update_index && newfd < 0)
+		newfd = hold_locked_index(&lock_file, 1);
+
+	if (check_index) {
+		if (read_cache() < 0)
+			die(_("unable to read index file"));
+	}
+
+	if ((check || apply) &&
+	    check_patch_list(list) < 0 &&
+	    !apply_with_reject)
+		exit(1);
+
+	if (apply && write_out_results(list)) {
+		if (apply_with_reject)
+			exit(1);
+		/* with --3way, we still need to write the index out */
+		return 1;
+	}
+
+	if (fake_ancestor)
+		build_fake_ancestor(list, fake_ancestor);
+
+	if (diffstat)
+		stat_patch_list(list);
+
+	if (numstat)
+		numstat_patch_list(list);
+
+	if (summary)
+		summary_patch_list(list);
+
+	free_patch_list(list);
+	strbuf_release(&buf);
+	string_list_clear(&fn_table, 0);
+	return 0;
+}
+
+static int git_apply_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, "apply.whitespace"))
+		return git_config_string(&apply_default_whitespace, var, value);
+	else if (!strcmp(var, "apply.ignorewhitespace"))
+		return git_config_string(&apply_default_ignorewhitespace, var, value);
+	return git_default_config(var, value, cb);
+}
+
+static int option_parse_exclude(const struct option *opt,
+				const char *arg, int unset)
+{
+	add_name_limit(arg, 1);
+	return 0;
+}
+
+static int option_parse_include(const struct option *opt,
+				const char *arg, int unset)
+{
+	add_name_limit(arg, 0);
+	has_include = 1;
+	return 0;
+}
+
+static int option_parse_p(const struct option *opt,
+			  const char *arg, int unset)
+{
+	p_value = atoi(arg);
+	p_value_known = 1;
+	return 0;
+}
+
+static int option_parse_z(const struct option *opt,
+			  const char *arg, int unset)
+{
+	if (unset)
+		line_termination = '\n';
+	else
+		line_termination = 0;
+	return 0;
+}
+
+static int option_parse_space_change(const struct option *opt,
+			  const char *arg, int unset)
+{
+	if (unset)
+		ws_ignore_action = ignore_ws_none;
+	else
+		ws_ignore_action = ignore_ws_change;
+	return 0;
+}
+
+static int option_parse_whitespace(const struct option *opt,
+				   const char *arg, int unset)
+{
+	const char **whitespace_option = opt->value;
+
+	*whitespace_option = arg;
+	parse_whitespace_option(arg);
+	return 0;
+}
+
+static int option_parse_directory(const struct option *opt,
+				  const char *arg, int unset)
+{
+	root_len = strlen(arg);
+	if (root_len && arg[root_len - 1] != '/') {
+		char *new_root;
+		root = new_root = xmalloc(root_len + 2);
+		strcpy(new_root, arg);
+		strcpy(new_root + root_len++, "/");
+	} else
+		root = arg;
+	return 0;
+}
+
+int cmd_apply(int argc, const char **argv, const char *prefix_)
+{
+	int i;
+	int errs = 0;
+	int is_not_gitdir = !startup_info->have_repository;
+	int force_apply = 0;
+
+	const char *whitespace_option = NULL;
+
+	struct option builtin_apply_options[] = {
+		{ OPTION_CALLBACK, 0, "exclude", NULL, N_("path"),
+			N_("don't apply changes matching the given path"),
+			0, option_parse_exclude },
+		{ OPTION_CALLBACK, 0, "include", NULL, N_("path"),
+			N_("apply changes matching the given path"),
+			0, option_parse_include },
+		{ OPTION_CALLBACK, 'p', NULL, NULL, N_("num"),
+			N_("remove <num> leading slashes from traditional diff paths"),
+			0, option_parse_p },
+		OPT_BOOLEAN(0, "no-add", &no_add,
+			N_("ignore additions made by the patch")),
+		OPT_BOOLEAN(0, "stat", &diffstat,
+			N_("instead of applying the patch, output diffstat for the input")),
+		OPT_NOOP_NOARG(0, "allow-binary-replacement"),
+		OPT_NOOP_NOARG(0, "binary"),
+		OPT_BOOLEAN(0, "numstat", &numstat,
+			N_("shows number of added and deleted lines in decimal notation")),
+		OPT_BOOLEAN(0, "summary", &summary,
+			N_("instead of applying the patch, output a summary for the input")),
+		OPT_BOOLEAN(0, "check", &check,
+			N_("instead of applying the patch, see if the patch is applicable")),
+		OPT_BOOLEAN(0, "index", &check_index,
+			N_("make sure the patch is applicable to the current index")),
+		OPT_BOOLEAN(0, "cached", &cached,
+			N_("apply a patch without touching the working tree")),
+		OPT_BOOLEAN(0, "apply", &force_apply,
+			N_("also apply the patch (use with --stat/--summary/--check)")),
+		OPT_BOOL('3', "3way", &threeway,
+			 N_( "attempt three-way merge if a patch does not apply")),
+		OPT_FILENAME(0, "build-fake-ancestor", &fake_ancestor,
+			N_("build a temporary index based on embedded index information")),
+		{ OPTION_CALLBACK, 'z', NULL, NULL, NULL,
+			N_("paths are separated with NUL character"),
+			PARSE_OPT_NOARG, option_parse_z },
+		OPT_INTEGER('C', NULL, &p_context,
+				N_("ensure at least <n> lines of context match")),
+		{ OPTION_CALLBACK, 0, "whitespace", &whitespace_option, N_("action"),
+			N_("detect new or modified lines that have whitespace errors"),
+			0, option_parse_whitespace },
+		{ OPTION_CALLBACK, 0, "ignore-space-change", NULL, NULL,
+			N_("ignore changes in whitespace when finding context"),
+			PARSE_OPT_NOARG, option_parse_space_change },
+		{ OPTION_CALLBACK, 0, "ignore-whitespace", NULL, NULL,
+			N_("ignore changes in whitespace when finding context"),
+			PARSE_OPT_NOARG, option_parse_space_change },
+		OPT_BOOLEAN('R', "reverse", &apply_in_reverse,
+			N_("apply the patch in reverse")),
+		OPT_BOOLEAN(0, "unidiff-zero", &unidiff_zero,
+			N_("don't expect at least one line of context")),
+		OPT_BOOLEAN(0, "reject", &apply_with_reject,
+			N_("leave the rejected hunks in corresponding *.rej files")),
+		OPT_BOOLEAN(0, "allow-overlap", &allow_overlap,
+			N_("allow overlapping hunks")),
+		OPT__VERBOSE(&apply_verbosely, N_("be verbose")),
+		OPT_BIT(0, "inaccurate-eof", &options,
+			N_("tolerate incorrectly detected missing new-line at the end of file"),
+			INACCURATE_EOF),
+		OPT_BIT(0, "recount", &options,
+			N_("do not trust the line counts in the hunk headers"),
+			RECOUNT),
+		{ OPTION_CALLBACK, 0, "directory", NULL, N_("root"),
+			N_("prepend <root> to all filenames"),
+			0, option_parse_directory },
+		OPT_END()
+	};
+
+	prefix = prefix_;
+	prefix_length = prefix ? strlen(prefix) : 0;
+	git_config(git_apply_config, NULL);
+	if (apply_default_whitespace)
+		parse_whitespace_option(apply_default_whitespace);
+	if (apply_default_ignorewhitespace)
+		parse_ignorewhitespace_option(apply_default_ignorewhitespace);
+
+	argc = parse_options(argc, argv, prefix, builtin_apply_options,
+			apply_usage, 0);
+
+	if (apply_with_reject && threeway)
+		die("--reject and --3way cannot be used together.");
+	if (cached && threeway)
+		die("--cached and --3way cannot be used together.");
+	if (threeway) {
+		if (is_not_gitdir)
+			die(_("--3way outside a repository"));
+		check_index = 1;
+	}
+	if (apply_with_reject)
+		apply = apply_verbosely = 1;
+	if (!force_apply && (diffstat || numstat || summary || check || fake_ancestor))
+		apply = 0;
+	if (check_index && is_not_gitdir)
+		die(_("--index outside a repository"));
+	if (cached) {
+		if (is_not_gitdir)
+			die(_("--cached outside a repository"));
+		check_index = 1;
+	}
+	for (i = 0; i < argc; i++) {
+		const char *arg = argv[i];
+		int fd;
+
+		if (!strcmp(arg, "-")) {
+			errs |= apply_patch(0, "<stdin>", options);
+			read_stdin = 0;
+			continue;
+		} else if (0 < prefix_length)
+			arg = prefix_filename(prefix, prefix_length, arg);
+
+		fd = open(arg, O_RDONLY);
+		if (fd < 0)
+			die_errno(_("can't open patch '%s'"), arg);
+		read_stdin = 0;
+		set_default_whitespace_mode(whitespace_option);
+		errs |= apply_patch(fd, arg, options);
+		close(fd);
+	}
+	set_default_whitespace_mode(whitespace_option);
+	if (read_stdin)
+		errs |= apply_patch(0, "<stdin>", options);
+	if (whitespace_error) {
+		if (squelch_whitespace_errors &&
+		    squelch_whitespace_errors < whitespace_error) {
+			int squelched =
+				whitespace_error - squelch_whitespace_errors;
+			warning(Q_("squelched %d whitespace error",
+				   "squelched %d whitespace errors",
+				   squelched),
+				squelched);
+		}
+		if (ws_error_action == die_on_ws_error)
+			die(Q_("%d line adds whitespace errors.",
+			       "%d lines add whitespace errors.",
+			       whitespace_error),
+			    whitespace_error);
+		if (applied_after_fixing_ws && apply)
+			warning("%d line%s applied after"
+				" fixing whitespace errors.",
+				applied_after_fixing_ws,
+				applied_after_fixing_ws == 1 ? "" : "s");
+		else if (whitespace_error)
+			warning(Q_("%d line adds whitespace errors.",
+				   "%d lines add whitespace errors.",
+				   whitespace_error),
+				whitespace_error);
+	}
+
+	if (update_index) {
+		if (write_cache(newfd, active_cache, active_nr) ||
+		    commit_locked_index(&lock_file))
+			die(_("Unable to write new index file"));
+	}
+
+	return !!errs;
+}
diff --git a/builtin/archive.c b/builtin/archive.c
new file mode 100644
index 0000000..931956d
--- /dev/null
+++ b/builtin/archive.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2006 Franck Bui-Huu
+ * Copyright (c) 2006 Rene Scharfe
+ */
+#include "cache.h"
+#include "builtin.h"
+#include "archive.h"
+#include "transport.h"
+#include "parse-options.h"
+#include "pkt-line.h"
+#include "sideband.h"
+
+static void create_output_file(const char *output_file)
+{
+	int output_fd = open(output_file, O_CREAT | O_WRONLY | O_TRUNC, 0666);
+	if (output_fd < 0)
+		die_errno(_("could not create archive file '%s'"), output_file);
+	if (output_fd != 1) {
+		if (dup2(output_fd, 1) < 0)
+			die_errno(_("could not redirect output"));
+		else
+			close(output_fd);
+	}
+}
+
+static int run_remote_archiver(int argc, const char **argv,
+			       const char *remote, const char *exec,
+			       const char *name_hint)
+{
+	char buf[LARGE_PACKET_MAX];
+	int fd[2], i, len, rv;
+	struct transport *transport;
+	struct remote *_remote;
+
+	_remote = remote_get(remote);
+	if (!_remote->url[0])
+		die(_("git archive: Remote with no URL"));
+	transport = transport_get(_remote, _remote->url[0]);
+	transport_connect(transport, "git-upload-archive", exec, fd);
+
+	/*
+	 * Inject a fake --format field at the beginning of the
+	 * arguments, with the format inferred from our output
+	 * filename. This way explicit --format options can override
+	 * it.
+	 */
+	if (name_hint) {
+		const char *format = archive_format_from_filename(name_hint);
+		if (format)
+			packet_write(fd[1], "argument --format=%s\n", format);
+	}
+	for (i = 1; i < argc; i++)
+		packet_write(fd[1], "argument %s\n", argv[i]);
+	packet_flush(fd[1]);
+
+	len = packet_read_line(fd[0], buf, sizeof(buf));
+	if (!len)
+		die(_("git archive: expected ACK/NAK, got EOF"));
+	if (buf[len-1] == '\n')
+		buf[--len] = 0;
+	if (strcmp(buf, "ACK")) {
+		if (len > 5 && !prefixcmp(buf, "NACK "))
+			die(_("git archive: NACK %s"), buf + 5);
+		if (len > 4 && !prefixcmp(buf, "ERR "))
+			die(_("remote error: %s"), buf + 4);
+		die(_("git archive: protocol error"));
+	}
+
+	len = packet_read_line(fd[0], buf, sizeof(buf));
+	if (len)
+		die(_("git archive: expected a flush"));
+
+	/* Now, start reading from fd[0] and spit it out to stdout */
+	rv = recv_sideband("archive", fd[0], 1);
+	rv |= transport_disconnect(transport);
+
+	return !!rv;
+}
+
+#define PARSE_OPT_KEEP_ALL ( PARSE_OPT_KEEP_DASHDASH | 	\
+			     PARSE_OPT_KEEP_ARGV0 | 	\
+			     PARSE_OPT_KEEP_UNKNOWN |	\
+			     PARSE_OPT_NO_INTERNAL_HELP	)
+
+int cmd_archive(int argc, const char **argv, const char *prefix)
+{
+	const char *exec = "git-upload-archive";
+	const char *output = NULL;
+	const char *remote = NULL;
+	struct option local_opts[] = {
+		OPT_STRING('o', "output", &output, "file",
+			"write the archive to this file"),
+		OPT_STRING(0, "remote", &remote, "repo",
+			"retrieve the archive from remote repository <repo>"),
+		OPT_STRING(0, "exec", &exec, "cmd",
+			"path to the remote git-upload-archive command"),
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, prefix, local_opts, NULL,
+			     PARSE_OPT_KEEP_ALL);
+
+	if (output)
+		create_output_file(output);
+
+	if (remote)
+		return run_remote_archiver(argc, argv, remote, exec, output);
+
+	setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
+
+	return write_archive(argc, argv, prefix, 1, output, 0);
+}
diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
new file mode 100644
index 0000000..8d325a5
--- /dev/null
+++ b/builtin/bisect--helper.c
@@ -0,0 +1,31 @@
+#include "builtin.h"
+#include "cache.h"
+#include "parse-options.h"
+#include "bisect.h"
+
+static const char * const git_bisect_helper_usage[] = {
+	"git bisect--helper --next-all [--no-checkout]",
+	NULL
+};
+
+int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
+{
+	int next_all = 0;
+	int no_checkout = 0;
+	struct option options[] = {
+		OPT_BOOLEAN(0, "next-all", &next_all,
+			    "perform 'git bisect next'"),
+		OPT_BOOLEAN(0, "no-checkout", &no_checkout,
+			    "update BISECT_HEAD instead of checking out the current commit"),
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, prefix, options,
+			     git_bisect_helper_usage, 0);
+
+	if (!next_all)
+		usage_with_options(git_bisect_helper_usage, options);
+
+	/* next-all */
+	return bisect_next_all(prefix, no_checkout);
+}
diff --git a/builtin/blame.c b/builtin/blame.c
new file mode 100644
index 0000000..0d50273
--- /dev/null
+++ b/builtin/blame.c
@@ -0,0 +1,2587 @@
+/*
+ * Blame
+ *
+ * Copyright (c) 2006, Junio C Hamano
+ */
+
+#include "cache.h"
+#include "builtin.h"
+#include "blob.h"
+#include "commit.h"
+#include "tag.h"
+#include "tree-walk.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "revision.h"
+#include "quote.h"
+#include "xdiff-interface.h"
+#include "cache-tree.h"
+#include "string-list.h"
+#include "mailmap.h"
+#include "parse-options.h"
+#include "utf8.h"
+#include "userdiff.h"
+
+static char blame_usage[] = "git blame [options] [rev-opts] [rev] [--] file";
+
+static const char *blame_opt_usage[] = {
+	blame_usage,
+	"",
+	"[rev-opts] are documented in git-rev-list(1)",
+	NULL
+};
+
+static int longest_file;
+static int longest_author;
+static int max_orig_digits;
+static int max_digits;
+static int max_score_digits;
+static int show_root;
+static int reverse;
+static int blank_boundary;
+static int incremental;
+static int xdl_opts;
+static int abbrev = -1;
+
+static enum date_mode blame_date_mode = DATE_ISO8601;
+static size_t blame_date_width;
+
+static struct string_list mailmap;
+
+#ifndef DEBUG
+#define DEBUG 0
+#endif
+
+/* stats */
+static int num_read_blob;
+static int num_get_patch;
+static int num_commits;
+
+#define PICKAXE_BLAME_MOVE		01
+#define PICKAXE_BLAME_COPY		02
+#define PICKAXE_BLAME_COPY_HARDER	04
+#define PICKAXE_BLAME_COPY_HARDEST	010
+
+/*
+ * blame for a blame_entry with score lower than these thresholds
+ * is not passed to the parent using move/copy logic.
+ */
+static unsigned blame_move_score;
+static unsigned blame_copy_score;
+#define BLAME_DEFAULT_MOVE_SCORE	20
+#define BLAME_DEFAULT_COPY_SCORE	40
+
+/* bits #0..7 in revision.h, #8..11 used for merge_bases() in commit.c */
+#define METAINFO_SHOWN		(1u<<12)
+#define MORE_THAN_ONE_PATH	(1u<<13)
+
+/*
+ * One blob in a commit that is being suspected
+ */
+struct origin {
+	int refcnt;
+	struct origin *previous;
+	struct commit *commit;
+	mmfile_t file;
+	unsigned char blob_sha1[20];
+	unsigned mode;
+	char path[FLEX_ARRAY];
+};
+
+static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b, long ctxlen,
+		      xdl_emit_hunk_consume_func_t hunk_func, void *cb_data)
+{
+	xpparam_t xpp = {0};
+	xdemitconf_t xecfg = {0};
+	xdemitcb_t ecb = {NULL};
+
+	xpp.flags = xdl_opts;
+	xecfg.ctxlen = ctxlen;
+	xecfg.hunk_func = hunk_func;
+	ecb.priv = cb_data;
+	return xdi_diff(file_a, file_b, &xpp, &xecfg, &ecb);
+}
+
+/*
+ * Prepare diff_filespec and convert it using diff textconv API
+ * if the textconv driver exists.
+ * Return 1 if the conversion succeeds, 0 otherwise.
+ */
+int textconv_object(const char *path,
+		    unsigned mode,
+		    const unsigned char *sha1,
+		    char **buf,
+		    unsigned long *buf_size)
+{
+	struct diff_filespec *df;
+	struct userdiff_driver *textconv;
+
+	df = alloc_filespec(path);
+	fill_filespec(df, sha1, mode);
+	textconv = get_textconv(df);
+	if (!textconv) {
+		free_filespec(df);
+		return 0;
+	}
+
+	*buf_size = fill_textconv(textconv, df, buf);
+	free_filespec(df);
+	return 1;
+}
+
+/*
+ * Given an origin, prepare mmfile_t structure to be used by the
+ * diff machinery
+ */
+static void fill_origin_blob(struct diff_options *opt,
+			     struct origin *o, mmfile_t *file)
+{
+	if (!o->file.ptr) {
+		enum object_type type;
+		unsigned long file_size;
+
+		num_read_blob++;
+		if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
+		    textconv_object(o->path, o->mode, o->blob_sha1, &file->ptr, &file_size))
+			;
+		else
+			file->ptr = read_sha1_file(o->blob_sha1, &type, &file_size);
+		file->size = file_size;
+
+		if (!file->ptr)
+			die("Cannot read blob %s for path %s",
+			    sha1_to_hex(o->blob_sha1),
+			    o->path);
+		o->file = *file;
+	}
+	else
+		*file = o->file;
+}
+
+/*
+ * Origin is refcounted and usually we keep the blob contents to be
+ * reused.
+ */
+static inline struct origin *origin_incref(struct origin *o)
+{
+	if (o)
+		o->refcnt++;
+	return o;
+}
+
+static void origin_decref(struct origin *o)
+{
+	if (o && --o->refcnt <= 0) {
+		if (o->previous)
+			origin_decref(o->previous);
+		free(o->file.ptr);
+		free(o);
+	}
+}
+
+static void drop_origin_blob(struct origin *o)
+{
+	if (o->file.ptr) {
+		free(o->file.ptr);
+		o->file.ptr = NULL;
+	}
+}
+
+/*
+ * Each group of lines is described by a blame_entry; it can be split
+ * as we pass blame to the parents.  They form a linked list in the
+ * scoreboard structure, sorted by the target line number.
+ */
+struct blame_entry {
+	struct blame_entry *prev;
+	struct blame_entry *next;
+
+	/* the first line of this group in the final image;
+	 * internally all line numbers are 0 based.
+	 */
+	int lno;
+
+	/* how many lines this group has */
+	int num_lines;
+
+	/* the commit that introduced this group into the final image */
+	struct origin *suspect;
+
+	/* true if the suspect is truly guilty; false while we have not
+	 * checked if the group came from one of its parents.
+	 */
+	char guilty;
+
+	/* true if the entry has been scanned for copies in the current parent
+	 */
+	char scanned;
+
+	/* the line number of the first line of this group in the
+	 * suspect's file; internally all line numbers are 0 based.
+	 */
+	int s_lno;
+
+	/* how significant this entry is -- cached to avoid
+	 * scanning the lines over and over.
+	 */
+	unsigned score;
+};
+
+/*
+ * The current state of the blame assignment.
+ */
+struct scoreboard {
+	/* the final commit (i.e. where we started digging from) */
+	struct commit *final;
+	struct rev_info *revs;
+	const char *path;
+
+	/*
+	 * The contents in the final image.
+	 * Used by many functions to obtain contents of the nth line,
+	 * indexed with scoreboard.lineno[blame_entry.lno].
+	 */
+	const char *final_buf;
+	unsigned long final_buf_size;
+
+	/* linked list of blames */
+	struct blame_entry *ent;
+
+	/* look-up a line in the final buffer */
+	int num_lines;
+	int *lineno;
+};
+
+static inline int same_suspect(struct origin *a, struct origin *b)
+{
+	if (a == b)
+		return 1;
+	if (a->commit != b->commit)
+		return 0;
+	return !strcmp(a->path, b->path);
+}
+
+static void sanity_check_refcnt(struct scoreboard *);
+
+/*
+ * If two blame entries that are next to each other came from
+ * contiguous lines in the same origin (i.e. <commit, path> pair),
+ * merge them together.
+ */
+static void coalesce(struct scoreboard *sb)
+{
+	struct blame_entry *ent, *next;
+
+	for (ent = sb->ent; ent && (next = ent->next); ent = next) {
+		if (same_suspect(ent->suspect, next->suspect) &&
+		    ent->guilty == next->guilty &&
+		    ent->s_lno + ent->num_lines == next->s_lno) {
+			ent->num_lines += next->num_lines;
+			ent->next = next->next;
+			if (ent->next)
+				ent->next->prev = ent;
+			origin_decref(next->suspect);
+			free(next);
+			ent->score = 0;
+			next = ent; /* again */
+		}
+	}
+
+	if (DEBUG) /* sanity */
+		sanity_check_refcnt(sb);
+}
+
+/*
+ * Given a commit and a path in it, create a new origin structure.
+ * The callers that add blame to the scoreboard should use
+ * get_origin() to obtain shared, refcounted copy instead of calling
+ * this function directly.
+ */
+static struct origin *make_origin(struct commit *commit, const char *path)
+{
+	struct origin *o;
+	o = xcalloc(1, sizeof(*o) + strlen(path) + 1);
+	o->commit = commit;
+	o->refcnt = 1;
+	strcpy(o->path, path);
+	return o;
+}
+
+/*
+ * Locate an existing origin or create a new one.
+ */
+static struct origin *get_origin(struct scoreboard *sb,
+				 struct commit *commit,
+				 const char *path)
+{
+	struct blame_entry *e;
+
+	for (e = sb->ent; e; e = e->next) {
+		if (e->suspect->commit == commit &&
+		    !strcmp(e->suspect->path, path))
+			return origin_incref(e->suspect);
+	}
+	return make_origin(commit, path);
+}
+
+/*
+ * Fill the blob_sha1 field of an origin if it hasn't, so that later
+ * call to fill_origin_blob() can use it to locate the data.  blob_sha1
+ * for an origin is also used to pass the blame for the entire file to
+ * the parent to detect the case where a child's blob is identical to
+ * that of its parent's.
+ *
+ * This also fills origin->mode for corresponding tree path.
+ */
+static int fill_blob_sha1_and_mode(struct origin *origin)
+{
+	if (!is_null_sha1(origin->blob_sha1))
+		return 0;
+	if (get_tree_entry(origin->commit->object.sha1,
+			   origin->path,
+			   origin->blob_sha1, &origin->mode))
+		goto error_out;
+	if (sha1_object_info(origin->blob_sha1, NULL) != OBJ_BLOB)
+		goto error_out;
+	return 0;
+ error_out:
+	hashclr(origin->blob_sha1);
+	origin->mode = S_IFINVALID;
+	return -1;
+}
+
+/*
+ * We have an origin -- check if the same path exists in the
+ * parent and return an origin structure to represent it.
+ */
+static struct origin *find_origin(struct scoreboard *sb,
+				  struct commit *parent,
+				  struct origin *origin)
+{
+	struct origin *porigin = NULL;
+	struct diff_options diff_opts;
+	const char *paths[2];
+
+	if (parent->util) {
+		/*
+		 * Each commit object can cache one origin in that
+		 * commit.  This is a freestanding copy of origin and
+		 * not refcounted.
+		 */
+		struct origin *cached = parent->util;
+		if (!strcmp(cached->path, origin->path)) {
+			/*
+			 * The same path between origin and its parent
+			 * without renaming -- the most common case.
+			 */
+			porigin = get_origin(sb, parent, cached->path);
+
+			/*
+			 * If the origin was newly created (i.e. get_origin
+			 * would call make_origin if none is found in the
+			 * scoreboard), it does not know the blob_sha1/mode,
+			 * so copy it.  Otherwise porigin was in the
+			 * scoreboard and already knows blob_sha1/mode.
+			 */
+			if (porigin->refcnt == 1) {
+				hashcpy(porigin->blob_sha1, cached->blob_sha1);
+				porigin->mode = cached->mode;
+			}
+			return porigin;
+		}
+		/* otherwise it was not very useful; free it */
+		free(parent->util);
+		parent->util = NULL;
+	}
+
+	/* See if the origin->path is different between parent
+	 * and origin first.  Most of the time they are the
+	 * same and diff-tree is fairly efficient about this.
+	 */
+	diff_setup(&diff_opts);
+	DIFF_OPT_SET(&diff_opts, RECURSIVE);
+	diff_opts.detect_rename = 0;
+	diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
+	paths[0] = origin->path;
+	paths[1] = NULL;
+
+	diff_tree_setup_paths(paths, &diff_opts);
+	if (diff_setup_done(&diff_opts) < 0)
+		die("diff-setup");
+
+	if (is_null_sha1(origin->commit->object.sha1))
+		do_diff_cache(parent->tree->object.sha1, &diff_opts);
+	else
+		diff_tree_sha1(parent->tree->object.sha1,
+			       origin->commit->tree->object.sha1,
+			       "", &diff_opts);
+	diffcore_std(&diff_opts);
+
+	if (!diff_queued_diff.nr) {
+		/* The path is the same as parent */
+		porigin = get_origin(sb, parent, origin->path);
+		hashcpy(porigin->blob_sha1, origin->blob_sha1);
+		porigin->mode = origin->mode;
+	} else {
+		/*
+		 * Since origin->path is a pathspec, if the parent
+		 * commit had it as a directory, we will see a whole
+		 * bunch of deletion of files in the directory that we
+		 * do not care about.
+		 */
+		int i;
+		struct diff_filepair *p = NULL;
+		for (i = 0; i < diff_queued_diff.nr; i++) {
+			const char *name;
+			p = diff_queued_diff.queue[i];
+			name = p->one->path ? p->one->path : p->two->path;
+			if (!strcmp(name, origin->path))
+				break;
+		}
+		if (!p)
+			die("internal error in blame::find_origin");
+		switch (p->status) {
+		default:
+			die("internal error in blame::find_origin (%c)",
+			    p->status);
+		case 'M':
+			porigin = get_origin(sb, parent, origin->path);
+			hashcpy(porigin->blob_sha1, p->one->sha1);
+			porigin->mode = p->one->mode;
+			break;
+		case 'A':
+		case 'T':
+			/* Did not exist in parent, or type changed */
+			break;
+		}
+	}
+	diff_flush(&diff_opts);
+	diff_tree_release_paths(&diff_opts);
+	if (porigin) {
+		/*
+		 * Create a freestanding copy that is not part of
+		 * the refcounted origin found in the scoreboard, and
+		 * cache it in the commit.
+		 */
+		struct origin *cached;
+
+		cached = make_origin(porigin->commit, porigin->path);
+		hashcpy(cached->blob_sha1, porigin->blob_sha1);
+		cached->mode = porigin->mode;
+		parent->util = cached;
+	}
+	return porigin;
+}
+
+/*
+ * We have an origin -- find the path that corresponds to it in its
+ * parent and return an origin structure to represent it.
+ */
+static struct origin *find_rename(struct scoreboard *sb,
+				  struct commit *parent,
+				  struct origin *origin)
+{
+	struct origin *porigin = NULL;
+	struct diff_options diff_opts;
+	int i;
+	const char *paths[2];
+
+	diff_setup(&diff_opts);
+	DIFF_OPT_SET(&diff_opts, RECURSIVE);
+	diff_opts.detect_rename = DIFF_DETECT_RENAME;
+	diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
+	diff_opts.single_follow = origin->path;
+	paths[0] = NULL;
+	diff_tree_setup_paths(paths, &diff_opts);
+	if (diff_setup_done(&diff_opts) < 0)
+		die("diff-setup");
+
+	if (is_null_sha1(origin->commit->object.sha1))
+		do_diff_cache(parent->tree->object.sha1, &diff_opts);
+	else
+		diff_tree_sha1(parent->tree->object.sha1,
+			       origin->commit->tree->object.sha1,
+			       "", &diff_opts);
+	diffcore_std(&diff_opts);
+
+	for (i = 0; i < diff_queued_diff.nr; i++) {
+		struct diff_filepair *p = diff_queued_diff.queue[i];
+		if ((p->status == 'R' || p->status == 'C') &&
+		    !strcmp(p->two->path, origin->path)) {
+			porigin = get_origin(sb, parent, p->one->path);
+			hashcpy(porigin->blob_sha1, p->one->sha1);
+			porigin->mode = p->one->mode;
+			break;
+		}
+	}
+	diff_flush(&diff_opts);
+	diff_tree_release_paths(&diff_opts);
+	return porigin;
+}
+
+/*
+ * Link in a new blame entry to the scoreboard.  Entries that cover the
+ * same line range have been removed from the scoreboard previously.
+ */
+static void add_blame_entry(struct scoreboard *sb, struct blame_entry *e)
+{
+	struct blame_entry *ent, *prev = NULL;
+
+	origin_incref(e->suspect);
+
+	for (ent = sb->ent; ent && ent->lno < e->lno; ent = ent->next)
+		prev = ent;
+
+	/* prev, if not NULL, is the last one that is below e */
+	e->prev = prev;
+	if (prev) {
+		e->next = prev->next;
+		prev->next = e;
+	}
+	else {
+		e->next = sb->ent;
+		sb->ent = e;
+	}
+	if (e->next)
+		e->next->prev = e;
+}
+
+/*
+ * src typically is on-stack; we want to copy the information in it to
+ * a malloced blame_entry that is already on the linked list of the
+ * scoreboard.  The origin of dst loses a refcnt while the origin of src
+ * gains one.
+ */
+static void dup_entry(struct blame_entry *dst, struct blame_entry *src)
+{
+	struct blame_entry *p, *n;
+
+	p = dst->prev;
+	n = dst->next;
+	origin_incref(src->suspect);
+	origin_decref(dst->suspect);
+	memcpy(dst, src, sizeof(*src));
+	dst->prev = p;
+	dst->next = n;
+	dst->score = 0;
+}
+
+static const char *nth_line(struct scoreboard *sb, int lno)
+{
+	return sb->final_buf + sb->lineno[lno];
+}
+
+/*
+ * It is known that lines between tlno to same came from parent, and e
+ * has an overlap with that range.  it also is known that parent's
+ * line plno corresponds to e's line tlno.
+ *
+ *                <---- e ----->
+ *                   <------>
+ *                   <------------>
+ *             <------------>
+ *             <------------------>
+ *
+ * Split e into potentially three parts; before this chunk, the chunk
+ * to be blamed for the parent, and after that portion.
+ */
+static void split_overlap(struct blame_entry *split,
+			  struct blame_entry *e,
+			  int tlno, int plno, int same,
+			  struct origin *parent)
+{
+	int chunk_end_lno;
+	memset(split, 0, sizeof(struct blame_entry [3]));
+
+	if (e->s_lno < tlno) {
+		/* there is a pre-chunk part not blamed on parent */
+		split[0].suspect = origin_incref(e->suspect);
+		split[0].lno = e->lno;
+		split[0].s_lno = e->s_lno;
+		split[0].num_lines = tlno - e->s_lno;
+		split[1].lno = e->lno + tlno - e->s_lno;
+		split[1].s_lno = plno;
+	}
+	else {
+		split[1].lno = e->lno;
+		split[1].s_lno = plno + (e->s_lno - tlno);
+	}
+
+	if (same < e->s_lno + e->num_lines) {
+		/* there is a post-chunk part not blamed on parent */
+		split[2].suspect = origin_incref(e->suspect);
+		split[2].lno = e->lno + (same - e->s_lno);
+		split[2].s_lno = e->s_lno + (same - e->s_lno);
+		split[2].num_lines = e->s_lno + e->num_lines - same;
+		chunk_end_lno = split[2].lno;
+	}
+	else
+		chunk_end_lno = e->lno + e->num_lines;
+	split[1].num_lines = chunk_end_lno - split[1].lno;
+
+	/*
+	 * if it turns out there is nothing to blame the parent for,
+	 * forget about the splitting.  !split[1].suspect signals this.
+	 */
+	if (split[1].num_lines < 1)
+		return;
+	split[1].suspect = origin_incref(parent);
+}
+
+/*
+ * split_overlap() divided an existing blame e into up to three parts
+ * in split.  Adjust the linked list of blames in the scoreboard to
+ * reflect the split.
+ */
+static void split_blame(struct scoreboard *sb,
+			struct blame_entry *split,
+			struct blame_entry *e)
+{
+	struct blame_entry *new_entry;
+
+	if (split[0].suspect && split[2].suspect) {
+		/* The first part (reuse storage for the existing entry e) */
+		dup_entry(e, &split[0]);
+
+		/* The last part -- me */
+		new_entry = xmalloc(sizeof(*new_entry));
+		memcpy(new_entry, &(split[2]), sizeof(struct blame_entry));
+		add_blame_entry(sb, new_entry);
+
+		/* ... and the middle part -- parent */
+		new_entry = xmalloc(sizeof(*new_entry));
+		memcpy(new_entry, &(split[1]), sizeof(struct blame_entry));
+		add_blame_entry(sb, new_entry);
+	}
+	else if (!split[0].suspect && !split[2].suspect)
+		/*
+		 * The parent covers the entire area; reuse storage for
+		 * e and replace it with the parent.
+		 */
+		dup_entry(e, &split[1]);
+	else if (split[0].suspect) {
+		/* me and then parent */
+		dup_entry(e, &split[0]);
+
+		new_entry = xmalloc(sizeof(*new_entry));
+		memcpy(new_entry, &(split[1]), sizeof(struct blame_entry));
+		add_blame_entry(sb, new_entry);
+	}
+	else {
+		/* parent and then me */
+		dup_entry(e, &split[1]);
+
+		new_entry = xmalloc(sizeof(*new_entry));
+		memcpy(new_entry, &(split[2]), sizeof(struct blame_entry));
+		add_blame_entry(sb, new_entry);
+	}
+
+	if (DEBUG) { /* sanity */
+		struct blame_entry *ent;
+		int lno = sb->ent->lno, corrupt = 0;
+
+		for (ent = sb->ent; ent; ent = ent->next) {
+			if (lno != ent->lno)
+				corrupt = 1;
+			if (ent->s_lno < 0)
+				corrupt = 1;
+			lno += ent->num_lines;
+		}
+		if (corrupt) {
+			lno = sb->ent->lno;
+			for (ent = sb->ent; ent; ent = ent->next) {
+				printf("L %8d l %8d n %8d\n",
+				       lno, ent->lno, ent->num_lines);
+				lno = ent->lno + ent->num_lines;
+			}
+			die("oops");
+		}
+	}
+}
+
+/*
+ * After splitting the blame, the origins used by the
+ * on-stack blame_entry should lose one refcnt each.
+ */
+static void decref_split(struct blame_entry *split)
+{
+	int i;
+
+	for (i = 0; i < 3; i++)
+		origin_decref(split[i].suspect);
+}
+
+/*
+ * Helper for blame_chunk().  blame_entry e is known to overlap with
+ * the patch hunk; split it and pass blame to the parent.
+ */
+static void blame_overlap(struct scoreboard *sb, struct blame_entry *e,
+			  int tlno, int plno, int same,
+			  struct origin *parent)
+{
+	struct blame_entry split[3];
+
+	split_overlap(split, e, tlno, plno, same, parent);
+	if (split[1].suspect)
+		split_blame(sb, split, e);
+	decref_split(split);
+}
+
+/*
+ * Find the line number of the last line the target is suspected for.
+ */
+static int find_last_in_target(struct scoreboard *sb, struct origin *target)
+{
+	struct blame_entry *e;
+	int last_in_target = -1;
+
+	for (e = sb->ent; e; e = e->next) {
+		if (e->guilty || !same_suspect(e->suspect, target))
+			continue;
+		if (last_in_target < e->s_lno + e->num_lines)
+			last_in_target = e->s_lno + e->num_lines;
+	}
+	return last_in_target;
+}
+
+/*
+ * Process one hunk from the patch between the current suspect for
+ * blame_entry e and its parent.  Find and split the overlap, and
+ * pass blame to the overlapping part to the parent.
+ */
+static void blame_chunk(struct scoreboard *sb,
+			int tlno, int plno, int same,
+			struct origin *target, struct origin *parent)
+{
+	struct blame_entry *e;
+
+	for (e = sb->ent; e; e = e->next) {
+		if (e->guilty || !same_suspect(e->suspect, target))
+			continue;
+		if (same <= e->s_lno)
+			continue;
+		if (tlno < e->s_lno + e->num_lines)
+			blame_overlap(sb, e, tlno, plno, same, parent);
+	}
+}
+
+struct blame_chunk_cb_data {
+	struct scoreboard *sb;
+	struct origin *target;
+	struct origin *parent;
+	long plno;
+	long tlno;
+};
+
+static int blame_chunk_cb(long start_a, long count_a,
+			  long start_b, long count_b, void *data)
+{
+	struct blame_chunk_cb_data *d = data;
+	blame_chunk(d->sb, d->tlno, d->plno, start_b, d->target, d->parent);
+	d->plno = start_a + count_a;
+	d->tlno = start_b + count_b;
+	return 0;
+}
+
+/*
+ * We are looking at the origin 'target' and aiming to pass blame
+ * for the lines it is suspected to its parent.  Run diff to find
+ * which lines came from parent and pass blame for them.
+ */
+static int pass_blame_to_parent(struct scoreboard *sb,
+				struct origin *target,
+				struct origin *parent)
+{
+	int last_in_target;
+	mmfile_t file_p, file_o;
+	struct blame_chunk_cb_data d;
+
+	memset(&d, 0, sizeof(d));
+	d.sb = sb; d.target = target; d.parent = parent;
+	last_in_target = find_last_in_target(sb, target);
+	if (last_in_target < 0)
+		return 1; /* nothing remains for this target */
+
+	fill_origin_blob(&sb->revs->diffopt, parent, &file_p);
+	fill_origin_blob(&sb->revs->diffopt, target, &file_o);
+	num_get_patch++;
+
+	diff_hunks(&file_p, &file_o, 0, blame_chunk_cb, &d);
+	/* The rest (i.e. anything after tlno) are the same as the parent */
+	blame_chunk(sb, d.tlno, d.plno, last_in_target, target, parent);
+
+	return 0;
+}
+
+/*
+ * The lines in blame_entry after splitting blames many times can become
+ * very small and trivial, and at some point it becomes pointless to
+ * blame the parents.  E.g. "\t\t}\n\t}\n\n" appears everywhere in any
+ * ordinary C program, and it is not worth to say it was copied from
+ * totally unrelated file in the parent.
+ *
+ * Compute how trivial the lines in the blame_entry are.
+ */
+static unsigned ent_score(struct scoreboard *sb, struct blame_entry *e)
+{
+	unsigned score;
+	const char *cp, *ep;
+
+	if (e->score)
+		return e->score;
+
+	score = 1;
+	cp = nth_line(sb, e->lno);
+	ep = nth_line(sb, e->lno + e->num_lines);
+	while (cp < ep) {
+		unsigned ch = *((unsigned char *)cp);
+		if (isalnum(ch))
+			score++;
+		cp++;
+	}
+	e->score = score;
+	return score;
+}
+
+/*
+ * best_so_far[] and this[] are both a split of an existing blame_entry
+ * that passes blame to the parent.  Maintain best_so_far the best split
+ * so far, by comparing this and best_so_far and copying this into
+ * bst_so_far as needed.
+ */
+static void copy_split_if_better(struct scoreboard *sb,
+				 struct blame_entry *best_so_far,
+				 struct blame_entry *this)
+{
+	int i;
+
+	if (!this[1].suspect)
+		return;
+	if (best_so_far[1].suspect) {
+		if (ent_score(sb, &this[1]) < ent_score(sb, &best_so_far[1]))
+			return;
+	}
+
+	for (i = 0; i < 3; i++)
+		origin_incref(this[i].suspect);
+	decref_split(best_so_far);
+	memcpy(best_so_far, this, sizeof(struct blame_entry [3]));
+}
+
+/*
+ * We are looking at a part of the final image represented by
+ * ent (tlno and same are offset by ent->s_lno).
+ * tlno is where we are looking at in the final image.
+ * up to (but not including) same match preimage.
+ * plno is where we are looking at in the preimage.
+ *
+ * <-------------- final image ---------------------->
+ *       <------ent------>
+ *         ^tlno ^same
+ *    <---------preimage----->
+ *         ^plno
+ *
+ * All line numbers are 0-based.
+ */
+static void handle_split(struct scoreboard *sb,
+			 struct blame_entry *ent,
+			 int tlno, int plno, int same,
+			 struct origin *parent,
+			 struct blame_entry *split)
+{
+	if (ent->num_lines <= tlno)
+		return;
+	if (tlno < same) {
+		struct blame_entry this[3];
+		tlno += ent->s_lno;
+		same += ent->s_lno;
+		split_overlap(this, ent, tlno, plno, same, parent);
+		copy_split_if_better(sb, split, this);
+		decref_split(this);
+	}
+}
+
+struct handle_split_cb_data {
+	struct scoreboard *sb;
+	struct blame_entry *ent;
+	struct origin *parent;
+	struct blame_entry *split;
+	long plno;
+	long tlno;
+};
+
+static int handle_split_cb(long start_a, long count_a,
+			   long start_b, long count_b, void *data)
+{
+	struct handle_split_cb_data *d = data;
+	handle_split(d->sb, d->ent, d->tlno, d->plno, start_b, d->parent,
+		     d->split);
+	d->plno = start_a + count_a;
+	d->tlno = start_b + count_b;
+	return 0;
+}
+
+/*
+ * Find the lines from parent that are the same as ent so that
+ * we can pass blames to it.  file_p has the blob contents for
+ * the parent.
+ */
+static void find_copy_in_blob(struct scoreboard *sb,
+			      struct blame_entry *ent,
+			      struct origin *parent,
+			      struct blame_entry *split,
+			      mmfile_t *file_p)
+{
+	const char *cp;
+	int cnt;
+	mmfile_t file_o;
+	struct handle_split_cb_data d;
+
+	memset(&d, 0, sizeof(d));
+	d.sb = sb; d.ent = ent; d.parent = parent; d.split = split;
+	/*
+	 * Prepare mmfile that contains only the lines in ent.
+	 */
+	cp = nth_line(sb, ent->lno);
+	file_o.ptr = (char *) cp;
+	cnt = ent->num_lines;
+
+	while (cnt && cp < sb->final_buf + sb->final_buf_size) {
+		if (*cp++ == '\n')
+			cnt--;
+	}
+	file_o.size = cp - file_o.ptr;
+
+	/*
+	 * file_o is a part of final image we are annotating.
+	 * file_p partially may match that image.
+	 */
+	memset(split, 0, sizeof(struct blame_entry [3]));
+	diff_hunks(file_p, &file_o, 1, handle_split_cb, &d);
+	/* remainder, if any, all match the preimage */
+	handle_split(sb, ent, d.tlno, d.plno, ent->num_lines, parent, split);
+}
+
+/*
+ * See if lines currently target is suspected for can be attributed to
+ * parent.
+ */
+static int find_move_in_parent(struct scoreboard *sb,
+			       struct origin *target,
+			       struct origin *parent)
+{
+	int last_in_target, made_progress;
+	struct blame_entry *e, split[3];
+	mmfile_t file_p;
+
+	last_in_target = find_last_in_target(sb, target);
+	if (last_in_target < 0)
+		return 1; /* nothing remains for this target */
+
+	fill_origin_blob(&sb->revs->diffopt, parent, &file_p);
+	if (!file_p.ptr)
+		return 0;
+
+	made_progress = 1;
+	while (made_progress) {
+		made_progress = 0;
+		for (e = sb->ent; e; e = e->next) {
+			if (e->guilty || !same_suspect(e->suspect, target) ||
+			    ent_score(sb, e) < blame_move_score)
+				continue;
+			find_copy_in_blob(sb, e, parent, split, &file_p);
+			if (split[1].suspect &&
+			    blame_move_score < ent_score(sb, &split[1])) {
+				split_blame(sb, split, e);
+				made_progress = 1;
+			}
+			decref_split(split);
+		}
+	}
+	return 0;
+}
+
+struct blame_list {
+	struct blame_entry *ent;
+	struct blame_entry split[3];
+};
+
+/*
+ * Count the number of entries the target is suspected for,
+ * and prepare a list of entry and the best split.
+ */
+static struct blame_list *setup_blame_list(struct scoreboard *sb,
+					   struct origin *target,
+					   int min_score,
+					   int *num_ents_p)
+{
+	struct blame_entry *e;
+	int num_ents, i;
+	struct blame_list *blame_list = NULL;
+
+	for (e = sb->ent, num_ents = 0; e; e = e->next)
+		if (!e->scanned && !e->guilty &&
+		    same_suspect(e->suspect, target) &&
+		    min_score < ent_score(sb, e))
+			num_ents++;
+	if (num_ents) {
+		blame_list = xcalloc(num_ents, sizeof(struct blame_list));
+		for (e = sb->ent, i = 0; e; e = e->next)
+			if (!e->scanned && !e->guilty &&
+			    same_suspect(e->suspect, target) &&
+			    min_score < ent_score(sb, e))
+				blame_list[i++].ent = e;
+	}
+	*num_ents_p = num_ents;
+	return blame_list;
+}
+
+/*
+ * Reset the scanned status on all entries.
+ */
+static void reset_scanned_flag(struct scoreboard *sb)
+{
+	struct blame_entry *e;
+	for (e = sb->ent; e; e = e->next)
+		e->scanned = 0;
+}
+
+/*
+ * For lines target is suspected for, see if we can find code movement
+ * across file boundary from the parent commit.  porigin is the path
+ * in the parent we already tried.
+ */
+static int find_copy_in_parent(struct scoreboard *sb,
+			       struct origin *target,
+			       struct commit *parent,
+			       struct origin *porigin,
+			       int opt)
+{
+	struct diff_options diff_opts;
+	const char *paths[1];
+	int i, j;
+	int retval;
+	struct blame_list *blame_list;
+	int num_ents;
+
+	blame_list = setup_blame_list(sb, target, blame_copy_score, &num_ents);
+	if (!blame_list)
+		return 1; /* nothing remains for this target */
+
+	diff_setup(&diff_opts);
+	DIFF_OPT_SET(&diff_opts, RECURSIVE);
+	diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
+
+	paths[0] = NULL;
+	diff_tree_setup_paths(paths, &diff_opts);
+	if (diff_setup_done(&diff_opts) < 0)
+		die("diff-setup");
+
+	/* Try "find copies harder" on new path if requested;
+	 * we do not want to use diffcore_rename() actually to
+	 * match things up; find_copies_harder is set only to
+	 * force diff_tree_sha1() to feed all filepairs to diff_queue,
+	 * and this code needs to be after diff_setup_done(), which
+	 * usually makes find-copies-harder imply copy detection.
+	 */
+	if ((opt & PICKAXE_BLAME_COPY_HARDEST)
+	    || ((opt & PICKAXE_BLAME_COPY_HARDER)
+		&& (!porigin || strcmp(target->path, porigin->path))))
+		DIFF_OPT_SET(&diff_opts, FIND_COPIES_HARDER);
+
+	if (is_null_sha1(target->commit->object.sha1))
+		do_diff_cache(parent->tree->object.sha1, &diff_opts);
+	else
+		diff_tree_sha1(parent->tree->object.sha1,
+			       target->commit->tree->object.sha1,
+			       "", &diff_opts);
+
+	if (!DIFF_OPT_TST(&diff_opts, FIND_COPIES_HARDER))
+		diffcore_std(&diff_opts);
+
+	retval = 0;
+	while (1) {
+		int made_progress = 0;
+
+		for (i = 0; i < diff_queued_diff.nr; i++) {
+			struct diff_filepair *p = diff_queued_diff.queue[i];
+			struct origin *norigin;
+			mmfile_t file_p;
+			struct blame_entry this[3];
+
+			if (!DIFF_FILE_VALID(p->one))
+				continue; /* does not exist in parent */
+			if (S_ISGITLINK(p->one->mode))
+				continue; /* ignore git links */
+			if (porigin && !strcmp(p->one->path, porigin->path))
+				/* find_move already dealt with this path */
+				continue;
+
+			norigin = get_origin(sb, parent, p->one->path);
+			hashcpy(norigin->blob_sha1, p->one->sha1);
+			norigin->mode = p->one->mode;
+			fill_origin_blob(&sb->revs->diffopt, norigin, &file_p);
+			if (!file_p.ptr)
+				continue;
+
+			for (j = 0; j < num_ents; j++) {
+				find_copy_in_blob(sb, blame_list[j].ent,
+						  norigin, this, &file_p);
+				copy_split_if_better(sb, blame_list[j].split,
+						     this);
+				decref_split(this);
+			}
+			origin_decref(norigin);
+		}
+
+		for (j = 0; j < num_ents; j++) {
+			struct blame_entry *split = blame_list[j].split;
+			if (split[1].suspect &&
+			    blame_copy_score < ent_score(sb, &split[1])) {
+				split_blame(sb, split, blame_list[j].ent);
+				made_progress = 1;
+			}
+			else
+				blame_list[j].ent->scanned = 1;
+			decref_split(split);
+		}
+		free(blame_list);
+
+		if (!made_progress)
+			break;
+		blame_list = setup_blame_list(sb, target, blame_copy_score, &num_ents);
+		if (!blame_list) {
+			retval = 1;
+			break;
+		}
+	}
+	reset_scanned_flag(sb);
+	diff_flush(&diff_opts);
+	diff_tree_release_paths(&diff_opts);
+	return retval;
+}
+
+/*
+ * The blobs of origin and porigin exactly match, so everything
+ * origin is suspected for can be blamed on the parent.
+ */
+static void pass_whole_blame(struct scoreboard *sb,
+			     struct origin *origin, struct origin *porigin)
+{
+	struct blame_entry *e;
+
+	if (!porigin->file.ptr && origin->file.ptr) {
+		/* Steal its file */
+		porigin->file = origin->file;
+		origin->file.ptr = NULL;
+	}
+	for (e = sb->ent; e; e = e->next) {
+		if (!same_suspect(e->suspect, origin))
+			continue;
+		origin_incref(porigin);
+		origin_decref(e->suspect);
+		e->suspect = porigin;
+	}
+}
+
+/*
+ * We pass blame from the current commit to its parents.  We keep saying
+ * "parent" (and "porigin"), but what we mean is to find scapegoat to
+ * exonerate ourselves.
+ */
+static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit *commit)
+{
+	if (!reverse)
+		return commit->parents;
+	return lookup_decoration(&revs->children, &commit->object);
+}
+
+static int num_scapegoats(struct rev_info *revs, struct commit *commit)
+{
+	int cnt;
+	struct commit_list *l = first_scapegoat(revs, commit);
+	for (cnt = 0; l; l = l->next)
+		cnt++;
+	return cnt;
+}
+
+#define MAXSG 16
+
+static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
+{
+	struct rev_info *revs = sb->revs;
+	int i, pass, num_sg;
+	struct commit *commit = origin->commit;
+	struct commit_list *sg;
+	struct origin *sg_buf[MAXSG];
+	struct origin *porigin, **sg_origin = sg_buf;
+
+	num_sg = num_scapegoats(revs, commit);
+	if (!num_sg)
+		goto finish;
+	else if (num_sg < ARRAY_SIZE(sg_buf))
+		memset(sg_buf, 0, sizeof(sg_buf));
+	else
+		sg_origin = xcalloc(num_sg, sizeof(*sg_origin));
+
+	/*
+	 * The first pass looks for unrenamed path to optimize for
+	 * common cases, then we look for renames in the second pass.
+	 */
+	for (pass = 0; pass < 2; pass++) {
+		struct origin *(*find)(struct scoreboard *,
+				       struct commit *, struct origin *);
+		find = pass ? find_rename : find_origin;
+
+		for (i = 0, sg = first_scapegoat(revs, commit);
+		     i < num_sg && sg;
+		     sg = sg->next, i++) {
+			struct commit *p = sg->item;
+			int j, same;
+
+			if (sg_origin[i])
+				continue;
+			if (parse_commit(p))
+				continue;
+			porigin = find(sb, p, origin);
+			if (!porigin)
+				continue;
+			if (!hashcmp(porigin->blob_sha1, origin->blob_sha1)) {
+				pass_whole_blame(sb, origin, porigin);
+				origin_decref(porigin);
+				goto finish;
+			}
+			for (j = same = 0; j < i; j++)
+				if (sg_origin[j] &&
+				    !hashcmp(sg_origin[j]->blob_sha1,
+					     porigin->blob_sha1)) {
+					same = 1;
+					break;
+				}
+			if (!same)
+				sg_origin[i] = porigin;
+			else
+				origin_decref(porigin);
+		}
+	}
+
+	num_commits++;
+	for (i = 0, sg = first_scapegoat(revs, commit);
+	     i < num_sg && sg;
+	     sg = sg->next, i++) {
+		struct origin *porigin = sg_origin[i];
+		if (!porigin)
+			continue;
+		if (!origin->previous) {
+			origin_incref(porigin);
+			origin->previous = porigin;
+		}
+		if (pass_blame_to_parent(sb, origin, porigin))
+			goto finish;
+	}
+
+	/*
+	 * Optionally find moves in parents' files.
+	 */
+	if (opt & PICKAXE_BLAME_MOVE)
+		for (i = 0, sg = first_scapegoat(revs, commit);
+		     i < num_sg && sg;
+		     sg = sg->next, i++) {
+			struct origin *porigin = sg_origin[i];
+			if (!porigin)
+				continue;
+			if (find_move_in_parent(sb, origin, porigin))
+				goto finish;
+		}
+
+	/*
+	 * Optionally find copies from parents' files.
+	 */
+	if (opt & PICKAXE_BLAME_COPY)
+		for (i = 0, sg = first_scapegoat(revs, commit);
+		     i < num_sg && sg;
+		     sg = sg->next, i++) {
+			struct origin *porigin = sg_origin[i];
+			if (find_copy_in_parent(sb, origin, sg->item,
+						porigin, opt))
+				goto finish;
+		}
+
+ finish:
+	for (i = 0; i < num_sg; i++) {
+		if (sg_origin[i]) {
+			drop_origin_blob(sg_origin[i]);
+			origin_decref(sg_origin[i]);
+		}
+	}
+	drop_origin_blob(origin);
+	if (sg_buf != sg_origin)
+		free(sg_origin);
+}
+
+/*
+ * Information on commits, used for output.
+ */
+struct commit_info {
+	const char *author;
+	const char *author_mail;
+	unsigned long author_time;
+	const char *author_tz;
+
+	/* filled only when asked for details */
+	const char *committer;
+	const char *committer_mail;
+	unsigned long committer_time;
+	const char *committer_tz;
+
+	const char *summary;
+};
+
+/*
+ * Parse author/committer line in the commit object buffer
+ */
+static void get_ac_line(const char *inbuf, const char *what,
+			int person_len, char *person,
+			int mail_len, char *mail,
+			unsigned long *time, const char **tz)
+{
+	int len, tzlen, maillen;
+	char *tmp, *endp, *timepos, *mailpos;
+
+	tmp = strstr(inbuf, what);
+	if (!tmp)
+		goto error_out;
+	tmp += strlen(what);
+	endp = strchr(tmp, '\n');
+	if (!endp)
+		len = strlen(tmp);
+	else
+		len = endp - tmp;
+	if (person_len <= len) {
+	error_out:
+		/* Ugh */
+		*tz = "(unknown)";
+		strcpy(person, *tz);
+		strcpy(mail, *tz);
+		*time = 0;
+		return;
+	}
+	memcpy(person, tmp, len);
+
+	tmp = person;
+	tmp += len;
+	*tmp = 0;
+	while (person < tmp && *tmp != ' ')
+		tmp--;
+	if (tmp <= person)
+		goto error_out;
+	*tz = tmp+1;
+	tzlen = (person+len)-(tmp+1);
+
+	*tmp = 0;
+	while (person < tmp && *tmp != ' ')
+		tmp--;
+	if (tmp <= person)
+		goto error_out;
+	*time = strtoul(tmp, NULL, 10);
+	timepos = tmp;
+
+	*tmp = 0;
+	while (person < tmp && !(*tmp == ' ' && tmp[1] == '<'))
+		tmp--;
+	if (tmp <= person)
+		return;
+	mailpos = tmp + 1;
+	*tmp = 0;
+	maillen = timepos - tmp;
+	memcpy(mail, mailpos, maillen);
+
+	if (!mailmap.nr)
+		return;
+
+	/*
+	 * mailmap expansion may make the name longer.
+	 * make room by pushing stuff down.
+	 */
+	tmp = person + person_len - (tzlen + 1);
+	memmove(tmp, *tz, tzlen);
+	tmp[tzlen] = 0;
+	*tz = tmp;
+
+	/*
+	 * Now, convert both name and e-mail using mailmap
+	 */
+	if (map_user(&mailmap, mail+1, mail_len-1, person, tmp-person-1)) {
+		/* Add a trailing '>' to email, since map_user returns plain emails
+		   Note: It already has '<', since we replace from mail+1 */
+		mailpos = memchr(mail, '\0', mail_len);
+		if (mailpos && mailpos-mail < mail_len - 1) {
+			*mailpos = '>';
+			*(mailpos+1) = '\0';
+		}
+	}
+}
+
+static void get_commit_info(struct commit *commit,
+			    struct commit_info *ret,
+			    int detailed)
+{
+	int len;
+	const char *subject;
+	char *reencoded, *message;
+	static char author_name[1024];
+	static char author_mail[1024];
+	static char committer_name[1024];
+	static char committer_mail[1024];
+	static char summary_buf[1024];
+
+	/*
+	 * We've operated without save_commit_buffer, so
+	 * we now need to populate them for output.
+	 */
+	if (!commit->buffer) {
+		enum object_type type;
+		unsigned long size;
+		commit->buffer =
+			read_sha1_file(commit->object.sha1, &type, &size);
+		if (!commit->buffer)
+			die("Cannot read commit %s",
+			    sha1_to_hex(commit->object.sha1));
+	}
+	reencoded = reencode_commit_message(commit, NULL);
+	message   = reencoded ? reencoded : commit->buffer;
+	ret->author = author_name;
+	ret->author_mail = author_mail;
+	get_ac_line(message, "\nauthor ",
+		    sizeof(author_name), author_name,
+		    sizeof(author_mail), author_mail,
+		    &ret->author_time, &ret->author_tz);
+
+	if (!detailed) {
+		free(reencoded);
+		return;
+	}
+
+	ret->committer = committer_name;
+	ret->committer_mail = committer_mail;
+	get_ac_line(message, "\ncommitter ",
+		    sizeof(committer_name), committer_name,
+		    sizeof(committer_mail), committer_mail,
+		    &ret->committer_time, &ret->committer_tz);
+
+	ret->summary = summary_buf;
+	len = find_commit_subject(message, &subject);
+	if (len && len < sizeof(summary_buf)) {
+		memcpy(summary_buf, subject, len);
+		summary_buf[len] = 0;
+	} else {
+		sprintf(summary_buf, "(%s)", sha1_to_hex(commit->object.sha1));
+	}
+	free(reencoded);
+}
+
+/*
+ * To allow LF and other nonportable characters in pathnames,
+ * they are c-style quoted as needed.
+ */
+static void write_filename_info(const char *path)
+{
+	printf("filename ");
+	write_name_quoted(path, stdout, '\n');
+}
+
+/*
+ * Porcelain/Incremental format wants to show a lot of details per
+ * commit.  Instead of repeating this every line, emit it only once,
+ * the first time each commit appears in the output (unless the
+ * user has specifically asked for us to repeat).
+ */
+static int emit_one_suspect_detail(struct origin *suspect, int repeat)
+{
+	struct commit_info ci;
+
+	if (!repeat && (suspect->commit->object.flags & METAINFO_SHOWN))
+		return 0;
+
+	suspect->commit->object.flags |= METAINFO_SHOWN;
+	get_commit_info(suspect->commit, &ci, 1);
+	printf("author %s\n", ci.author);
+	printf("author-mail %s\n", ci.author_mail);
+	printf("author-time %lu\n", ci.author_time);
+	printf("author-tz %s\n", ci.author_tz);
+	printf("committer %s\n", ci.committer);
+	printf("committer-mail %s\n", ci.committer_mail);
+	printf("committer-time %lu\n", ci.committer_time);
+	printf("committer-tz %s\n", ci.committer_tz);
+	printf("summary %s\n", ci.summary);
+	if (suspect->commit->object.flags & UNINTERESTING)
+		printf("boundary\n");
+	if (suspect->previous) {
+		struct origin *prev = suspect->previous;
+		printf("previous %s ", sha1_to_hex(prev->commit->object.sha1));
+		write_name_quoted(prev->path, stdout, '\n');
+	}
+	return 1;
+}
+
+/*
+ * The blame_entry is found to be guilty for the range.  Mark it
+ * as such, and show it in incremental output.
+ */
+static void found_guilty_entry(struct blame_entry *ent)
+{
+	if (ent->guilty)
+		return;
+	ent->guilty = 1;
+	if (incremental) {
+		struct origin *suspect = ent->suspect;
+
+		printf("%s %d %d %d\n",
+		       sha1_to_hex(suspect->commit->object.sha1),
+		       ent->s_lno + 1, ent->lno + 1, ent->num_lines);
+		emit_one_suspect_detail(suspect, 0);
+		write_filename_info(suspect->path);
+		maybe_flush_or_die(stdout, "stdout");
+	}
+}
+
+/*
+ * The main loop -- while the scoreboard has lines whose true origin
+ * is still unknown, pick one blame_entry, and allow its current
+ * suspect to pass blames to its parents.
+ */
+static void assign_blame(struct scoreboard *sb, int opt)
+{
+	struct rev_info *revs = sb->revs;
+
+	while (1) {
+		struct blame_entry *ent;
+		struct commit *commit;
+		struct origin *suspect = NULL;
+
+		/* find one suspect to break down */
+		for (ent = sb->ent; !suspect && ent; ent = ent->next)
+			if (!ent->guilty)
+				suspect = ent->suspect;
+		if (!suspect)
+			return; /* all done */
+
+		/*
+		 * We will use this suspect later in the loop,
+		 * so hold onto it in the meantime.
+		 */
+		origin_incref(suspect);
+		commit = suspect->commit;
+		if (!commit->object.parsed)
+			parse_commit(commit);
+		if (reverse ||
+		    (!(commit->object.flags & UNINTERESTING) &&
+		     !(revs->max_age != -1 && commit->date < revs->max_age)))
+			pass_blame(sb, suspect, opt);
+		else {
+			commit->object.flags |= UNINTERESTING;
+			if (commit->object.parsed)
+				mark_parents_uninteresting(commit);
+		}
+		/* treat root commit as boundary */
+		if (!commit->parents && !show_root)
+			commit->object.flags |= UNINTERESTING;
+
+		/* Take responsibility for the remaining entries */
+		for (ent = sb->ent; ent; ent = ent->next)
+			if (same_suspect(ent->suspect, suspect))
+				found_guilty_entry(ent);
+		origin_decref(suspect);
+
+		if (DEBUG) /* sanity */
+			sanity_check_refcnt(sb);
+	}
+}
+
+static const char *format_time(unsigned long time, const char *tz_str,
+			       int show_raw_time)
+{
+	static char time_buf[128];
+	const char *time_str;
+	int time_len;
+	int tz;
+
+	if (show_raw_time) {
+		snprintf(time_buf, sizeof(time_buf), "%lu %s", time, tz_str);
+	}
+	else {
+		tz = atoi(tz_str);
+		time_str = show_date(time, tz, blame_date_mode);
+		time_len = strlen(time_str);
+		memcpy(time_buf, time_str, time_len);
+		memset(time_buf + time_len, ' ', blame_date_width - time_len);
+	}
+	return time_buf;
+}
+
+#define OUTPUT_ANNOTATE_COMPAT	001
+#define OUTPUT_LONG_OBJECT_NAME	002
+#define OUTPUT_RAW_TIMESTAMP	004
+#define OUTPUT_PORCELAIN	010
+#define OUTPUT_SHOW_NAME	020
+#define OUTPUT_SHOW_NUMBER	040
+#define OUTPUT_SHOW_SCORE      0100
+#define OUTPUT_NO_AUTHOR       0200
+#define OUTPUT_SHOW_EMAIL	0400
+#define OUTPUT_LINE_PORCELAIN 01000
+
+static void emit_porcelain_details(struct origin *suspect, int repeat)
+{
+	if (emit_one_suspect_detail(suspect, repeat) ||
+	    (suspect->commit->object.flags & MORE_THAN_ONE_PATH))
+		write_filename_info(suspect->path);
+}
+
+static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent,
+			   int opt)
+{
+	int repeat = opt & OUTPUT_LINE_PORCELAIN;
+	int cnt;
+	const char *cp;
+	struct origin *suspect = ent->suspect;
+	char hex[41];
+
+	strcpy(hex, sha1_to_hex(suspect->commit->object.sha1));
+	printf("%s%c%d %d %d\n",
+	       hex,
+	       ent->guilty ? ' ' : '*', /* purely for debugging */
+	       ent->s_lno + 1,
+	       ent->lno + 1,
+	       ent->num_lines);
+	emit_porcelain_details(suspect, repeat);
+
+	cp = nth_line(sb, ent->lno);
+	for (cnt = 0; cnt < ent->num_lines; cnt++) {
+		char ch;
+		if (cnt) {
+			printf("%s %d %d\n", hex,
+			       ent->s_lno + 1 + cnt,
+			       ent->lno + 1 + cnt);
+			if (repeat)
+				emit_porcelain_details(suspect, 1);
+		}
+		putchar('\t');
+		do {
+			ch = *cp++;
+			putchar(ch);
+		} while (ch != '\n' &&
+			 cp < sb->final_buf + sb->final_buf_size);
+	}
+
+	if (sb->final_buf_size && cp[-1] != '\n')
+		putchar('\n');
+}
+
+static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
+{
+	int cnt;
+	const char *cp;
+	struct origin *suspect = ent->suspect;
+	struct commit_info ci;
+	char hex[41];
+	int show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP);
+
+	get_commit_info(suspect->commit, &ci, 1);
+	strcpy(hex, sha1_to_hex(suspect->commit->object.sha1));
+
+	cp = nth_line(sb, ent->lno);
+	for (cnt = 0; cnt < ent->num_lines; cnt++) {
+		char ch;
+		int length = (opt & OUTPUT_LONG_OBJECT_NAME) ? 40 : abbrev;
+
+		if (suspect->commit->object.flags & UNINTERESTING) {
+			if (blank_boundary)
+				memset(hex, ' ', length);
+			else if (!(opt & OUTPUT_ANNOTATE_COMPAT)) {
+				length--;
+				putchar('^');
+			}
+		}
+
+		printf("%.*s", length, hex);
+		if (opt & OUTPUT_ANNOTATE_COMPAT) {
+			const char *name;
+			if (opt & OUTPUT_SHOW_EMAIL)
+				name = ci.author_mail;
+			else
+				name = ci.author;
+			printf("\t(%10s\t%10s\t%d)", name,
+			       format_time(ci.author_time, ci.author_tz,
+					   show_raw_time),
+			       ent->lno + 1 + cnt);
+		} else {
+			if (opt & OUTPUT_SHOW_SCORE)
+				printf(" %*d %02d",
+				       max_score_digits, ent->score,
+				       ent->suspect->refcnt);
+			if (opt & OUTPUT_SHOW_NAME)
+				printf(" %-*.*s", longest_file, longest_file,
+				       suspect->path);
+			if (opt & OUTPUT_SHOW_NUMBER)
+				printf(" %*d", max_orig_digits,
+				       ent->s_lno + 1 + cnt);
+
+			if (!(opt & OUTPUT_NO_AUTHOR)) {
+				const char *name;
+				int pad;
+				if (opt & OUTPUT_SHOW_EMAIL)
+					name = ci.author_mail;
+				else
+					name = ci.author;
+				pad = longest_author - utf8_strwidth(name);
+				printf(" (%s%*s %10s",
+				       name, pad, "",
+				       format_time(ci.author_time,
+						   ci.author_tz,
+						   show_raw_time));
+			}
+			printf(" %*d) ",
+			       max_digits, ent->lno + 1 + cnt);
+		}
+		do {
+			ch = *cp++;
+			putchar(ch);
+		} while (ch != '\n' &&
+			 cp < sb->final_buf + sb->final_buf_size);
+	}
+
+	if (sb->final_buf_size && cp[-1] != '\n')
+		putchar('\n');
+}
+
+static void output(struct scoreboard *sb, int option)
+{
+	struct blame_entry *ent;
+
+	if (option & OUTPUT_PORCELAIN) {
+		for (ent = sb->ent; ent; ent = ent->next) {
+			struct blame_entry *oth;
+			struct origin *suspect = ent->suspect;
+			struct commit *commit = suspect->commit;
+			if (commit->object.flags & MORE_THAN_ONE_PATH)
+				continue;
+			for (oth = ent->next; oth; oth = oth->next) {
+				if ((oth->suspect->commit != commit) ||
+				    !strcmp(oth->suspect->path, suspect->path))
+					continue;
+				commit->object.flags |= MORE_THAN_ONE_PATH;
+				break;
+			}
+		}
+	}
+
+	for (ent = sb->ent; ent; ent = ent->next) {
+		if (option & OUTPUT_PORCELAIN)
+			emit_porcelain(sb, ent, option);
+		else {
+			emit_other(sb, ent, option);
+		}
+	}
+}
+
+/*
+ * To allow quick access to the contents of nth line in the
+ * final image, prepare an index in the scoreboard.
+ */
+static int prepare_lines(struct scoreboard *sb)
+{
+	const char *buf = sb->final_buf;
+	unsigned long len = sb->final_buf_size;
+	int num = 0, incomplete = 0, bol = 1;
+
+	if (len && buf[len-1] != '\n')
+		incomplete++; /* incomplete line at the end */
+	while (len--) {
+		if (bol) {
+			sb->lineno = xrealloc(sb->lineno,
+					      sizeof(int *) * (num + 1));
+			sb->lineno[num] = buf - sb->final_buf;
+			bol = 0;
+		}
+		if (*buf++ == '\n') {
+			num++;
+			bol = 1;
+		}
+	}
+	sb->lineno = xrealloc(sb->lineno,
+			      sizeof(int *) * (num + incomplete + 1));
+	sb->lineno[num + incomplete] = buf - sb->final_buf;
+	sb->num_lines = num + incomplete;
+	return sb->num_lines;
+}
+
+/*
+ * Add phony grafts for use with -S; this is primarily to
+ * support git's cvsserver that wants to give a linear history
+ * to its clients.
+ */
+static int read_ancestry(const char *graft_file)
+{
+	FILE *fp = fopen(graft_file, "r");
+	char buf[1024];
+	if (!fp)
+		return -1;
+	while (fgets(buf, sizeof(buf), fp)) {
+		/* The format is just "Commit Parent1 Parent2 ...\n" */
+		int len = strlen(buf);
+		struct commit_graft *graft = read_graft_line(buf, len);
+		if (graft)
+			register_commit_graft(graft, 0);
+	}
+	fclose(fp);
+	return 0;
+}
+
+static int update_auto_abbrev(int auto_abbrev, struct origin *suspect)
+{
+	const char *uniq = find_unique_abbrev(suspect->commit->object.sha1,
+					      auto_abbrev);
+	int len = strlen(uniq);
+	if (auto_abbrev < len)
+		return len;
+	return auto_abbrev;
+}
+
+/*
+ * How many columns do we need to show line numbers, authors,
+ * and filenames?
+ */
+static void find_alignment(struct scoreboard *sb, int *option)
+{
+	int longest_src_lines = 0;
+	int longest_dst_lines = 0;
+	unsigned largest_score = 0;
+	struct blame_entry *e;
+	int compute_auto_abbrev = (abbrev < 0);
+	int auto_abbrev = default_abbrev;
+
+	for (e = sb->ent; e; e = e->next) {
+		struct origin *suspect = e->suspect;
+		struct commit_info ci;
+		int num;
+
+		if (compute_auto_abbrev)
+			auto_abbrev = update_auto_abbrev(auto_abbrev, suspect);
+		if (strcmp(suspect->path, sb->path))
+			*option |= OUTPUT_SHOW_NAME;
+		num = strlen(suspect->path);
+		if (longest_file < num)
+			longest_file = num;
+		if (!(suspect->commit->object.flags & METAINFO_SHOWN)) {
+			suspect->commit->object.flags |= METAINFO_SHOWN;
+			get_commit_info(suspect->commit, &ci, 1);
+			if (*option & OUTPUT_SHOW_EMAIL)
+				num = utf8_strwidth(ci.author_mail);
+			else
+				num = utf8_strwidth(ci.author);
+			if (longest_author < num)
+				longest_author = num;
+		}
+		num = e->s_lno + e->num_lines;
+		if (longest_src_lines < num)
+			longest_src_lines = num;
+		num = e->lno + e->num_lines;
+		if (longest_dst_lines < num)
+			longest_dst_lines = num;
+		if (largest_score < ent_score(sb, e))
+			largest_score = ent_score(sb, e);
+	}
+	max_orig_digits = decimal_width(longest_src_lines);
+	max_digits = decimal_width(longest_dst_lines);
+	max_score_digits = decimal_width(largest_score);
+
+	if (compute_auto_abbrev)
+		/* one more abbrev length is needed for the boundary commit */
+		abbrev = auto_abbrev + 1;
+}
+
+/*
+ * For debugging -- origin is refcounted, and this asserts that
+ * we do not underflow.
+ */
+static void sanity_check_refcnt(struct scoreboard *sb)
+{
+	int baa = 0;
+	struct blame_entry *ent;
+
+	for (ent = sb->ent; ent; ent = ent->next) {
+		/* Nobody should have zero or negative refcnt */
+		if (ent->suspect->refcnt <= 0) {
+			fprintf(stderr, "%s in %s has negative refcnt %d\n",
+				ent->suspect->path,
+				sha1_to_hex(ent->suspect->commit->object.sha1),
+				ent->suspect->refcnt);
+			baa = 1;
+		}
+	}
+	if (baa) {
+		int opt = 0160;
+		find_alignment(sb, &opt);
+		output(sb, opt);
+		die("Baa %d!", baa);
+	}
+}
+
+/*
+ * Used for the command line parsing; check if the path exists
+ * in the working tree.
+ */
+static int has_string_in_work_tree(const char *path)
+{
+	struct stat st;
+	return !lstat(path, &st);
+}
+
+static unsigned parse_score(const char *arg)
+{
+	char *end;
+	unsigned long score = strtoul(arg, &end, 10);
+	if (*end)
+		return 0;
+	return score;
+}
+
+static const char *add_prefix(const char *prefix, const char *path)
+{
+	return prefix_path(prefix, prefix ? strlen(prefix) : 0, path);
+}
+
+/*
+ * Parsing of (comma separated) one item in the -L option
+ */
+static const char *parse_loc(const char *spec,
+			     struct scoreboard *sb, long lno,
+			     long begin, long *ret)
+{
+	char *term;
+	const char *line;
+	long num;
+	int reg_error;
+	regex_t regexp;
+	regmatch_t match[1];
+
+	/* Allow "-L <something>,+20" to mean starting at <something>
+	 * for 20 lines, or "-L <something>,-5" for 5 lines ending at
+	 * <something>.
+	 */
+	if (1 < begin && (spec[0] == '+' || spec[0] == '-')) {
+		num = strtol(spec + 1, &term, 10);
+		if (term != spec + 1) {
+			if (spec[0] == '-')
+				num = 0 - num;
+			if (0 < num)
+				*ret = begin + num - 2;
+			else if (!num)
+				*ret = begin;
+			else
+				*ret = begin + num;
+			return term;
+		}
+		return spec;
+	}
+	num = strtol(spec, &term, 10);
+	if (term != spec) {
+		*ret = num;
+		return term;
+	}
+	if (spec[0] != '/')
+		return spec;
+
+	/* it could be a regexp of form /.../ */
+	for (term = (char *) spec + 1; *term && *term != '/'; term++) {
+		if (*term == '\\')
+			term++;
+	}
+	if (*term != '/')
+		return spec;
+
+	/* try [spec+1 .. term-1] as regexp */
+	*term = 0;
+	begin--; /* input is in human terms */
+	line = nth_line(sb, begin);
+
+	if (!(reg_error = regcomp(&regexp, spec + 1, REG_NEWLINE)) &&
+	    !(reg_error = regexec(&regexp, line, 1, match, 0))) {
+		const char *cp = line + match[0].rm_so;
+		const char *nline;
+
+		while (begin++ < lno) {
+			nline = nth_line(sb, begin);
+			if (line <= cp && cp < nline)
+				break;
+			line = nline;
+		}
+		*ret = begin;
+		regfree(&regexp);
+		*term++ = '/';
+		return term;
+	}
+	else {
+		char errbuf[1024];
+		regerror(reg_error, &regexp, errbuf, 1024);
+		die("-L parameter '%s': %s", spec + 1, errbuf);
+	}
+}
+
+/*
+ * Parsing of -L option
+ */
+static void prepare_blame_range(struct scoreboard *sb,
+				const char *bottomtop,
+				long lno,
+				long *bottom, long *top)
+{
+	const char *term;
+
+	term = parse_loc(bottomtop, sb, lno, 1, bottom);
+	if (*term == ',') {
+		term = parse_loc(term + 1, sb, lno, *bottom + 1, top);
+		if (*term)
+			usage(blame_usage);
+	}
+	if (*term)
+		usage(blame_usage);
+}
+
+static int git_blame_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, "blame.showroot")) {
+		show_root = git_config_bool(var, value);
+		return 0;
+	}
+	if (!strcmp(var, "blame.blankboundary")) {
+		blank_boundary = git_config_bool(var, value);
+		return 0;
+	}
+	if (!strcmp(var, "blame.date")) {
+		if (!value)
+			return config_error_nonbool(var);
+		blame_date_mode = parse_date_format(value);
+		return 0;
+	}
+
+	if (userdiff_config(var, value) < 0)
+		return -1;
+
+	return git_default_config(var, value, cb);
+}
+
+/*
+ * Prepare a dummy commit that represents the work tree (or staged) item.
+ * Note that annotating work tree item never works in the reverse.
+ */
+static struct commit *fake_working_tree_commit(struct diff_options *opt,
+					       const char *path,
+					       const char *contents_from)
+{
+	struct commit *commit;
+	struct origin *origin;
+	unsigned char head_sha1[20];
+	struct strbuf buf = STRBUF_INIT;
+	const char *ident;
+	time_t now;
+	int size, len;
+	struct cache_entry *ce;
+	unsigned mode;
+
+	if (get_sha1("HEAD", head_sha1))
+		die("No such ref: HEAD");
+
+	time(&now);
+	commit = xcalloc(1, sizeof(*commit));
+	commit->parents = xcalloc(1, sizeof(*commit->parents));
+	commit->parents->item = lookup_commit_reference(head_sha1);
+	commit->object.parsed = 1;
+	commit->date = now;
+	commit->object.type = OBJ_COMMIT;
+
+	origin = make_origin(commit, path);
+
+	if (!contents_from || strcmp("-", contents_from)) {
+		struct stat st;
+		const char *read_from;
+		char *buf_ptr;
+		unsigned long buf_len;
+
+		if (contents_from) {
+			if (stat(contents_from, &st) < 0)
+				die_errno("Cannot stat '%s'", contents_from);
+			read_from = contents_from;
+		}
+		else {
+			if (lstat(path, &st) < 0)
+				die_errno("Cannot lstat '%s'", path);
+			read_from = path;
+		}
+		mode = canon_mode(st.st_mode);
+
+		switch (st.st_mode & S_IFMT) {
+		case S_IFREG:
+			if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
+			    textconv_object(read_from, mode, null_sha1, &buf_ptr, &buf_len))
+				strbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1);
+			else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
+				die_errno("cannot open or read '%s'", read_from);
+			break;
+		case S_IFLNK:
+			if (strbuf_readlink(&buf, read_from, st.st_size) < 0)
+				die_errno("cannot readlink '%s'", read_from);
+			break;
+		default:
+			die("unsupported file type %s", read_from);
+		}
+	}
+	else {
+		/* Reading from stdin */
+		contents_from = "standard input";
+		mode = 0;
+		if (strbuf_read(&buf, 0, 0) < 0)
+			die_errno("failed to read from stdin");
+	}
+	convert_to_git(path, buf.buf, buf.len, &buf, 0);
+	origin->file.ptr = buf.buf;
+	origin->file.size = buf.len;
+	pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_sha1);
+	commit->util = origin;
+
+	/*
+	 * Read the current index, replace the path entry with
+	 * origin->blob_sha1 without mucking with its mode or type
+	 * bits; we are not going to write this index out -- we just
+	 * want to run "diff-index --cached".
+	 */
+	discard_cache();
+	read_cache();
+
+	len = strlen(path);
+	if (!mode) {
+		int pos = cache_name_pos(path, len);
+		if (0 <= pos)
+			mode = active_cache[pos]->ce_mode;
+		else
+			/* Let's not bother reading from HEAD tree */
+			mode = S_IFREG | 0644;
+	}
+	size = cache_entry_size(len);
+	ce = xcalloc(1, size);
+	hashcpy(ce->sha1, origin->blob_sha1);
+	memcpy(ce->name, path, len);
+	ce->ce_flags = create_ce_flags(0);
+	ce->ce_namelen = len;
+	ce->ce_mode = create_ce_mode(mode);
+	add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
+
+	/*
+	 * We are not going to write this out, so this does not matter
+	 * right now, but someday we might optimize diff-index --cached
+	 * with cache-tree information.
+	 */
+	cache_tree_invalidate_path(active_cache_tree, path);
+
+	commit->buffer = xmalloc(400);
+	ident = fmt_ident("Not Committed Yet", "not.committed.yet", NULL, 0);
+	snprintf(commit->buffer, 400,
+		"tree 0000000000000000000000000000000000000000\n"
+		"parent %s\n"
+		"author %s\n"
+		"committer %s\n\n"
+		"Version of %s from %s\n",
+		sha1_to_hex(head_sha1),
+		ident, ident, path, contents_from ? contents_from : path);
+	return commit;
+}
+
+static const char *prepare_final(struct scoreboard *sb)
+{
+	int i;
+	const char *final_commit_name = NULL;
+	struct rev_info *revs = sb->revs;
+
+	/*
+	 * There must be one and only one positive commit in the
+	 * revs->pending array.
+	 */
+	for (i = 0; i < revs->pending.nr; i++) {
+		struct object *obj = revs->pending.objects[i].item;
+		if (obj->flags & UNINTERESTING)
+			continue;
+		while (obj->type == OBJ_TAG)
+			obj = deref_tag(obj, NULL, 0);
+		if (obj->type != OBJ_COMMIT)
+			die("Non commit %s?", revs->pending.objects[i].name);
+		if (sb->final)
+			die("More than one commit to dig from %s and %s?",
+			    revs->pending.objects[i].name,
+			    final_commit_name);
+		sb->final = (struct commit *) obj;
+		final_commit_name = revs->pending.objects[i].name;
+	}
+	return final_commit_name;
+}
+
+static const char *prepare_initial(struct scoreboard *sb)
+{
+	int i;
+	const char *final_commit_name = NULL;
+	struct rev_info *revs = sb->revs;
+
+	/*
+	 * There must be one and only one negative commit, and it must be
+	 * the boundary.
+	 */
+	for (i = 0; i < revs->pending.nr; i++) {
+		struct object *obj = revs->pending.objects[i].item;
+		if (!(obj->flags & UNINTERESTING))
+			continue;
+		while (obj->type == OBJ_TAG)
+			obj = deref_tag(obj, NULL, 0);
+		if (obj->type != OBJ_COMMIT)
+			die("Non commit %s?", revs->pending.objects[i].name);
+		if (sb->final)
+			die("More than one commit to dig down to %s and %s?",
+			    revs->pending.objects[i].name,
+			    final_commit_name);
+		sb->final = (struct commit *) obj;
+		final_commit_name = revs->pending.objects[i].name;
+	}
+	if (!final_commit_name)
+		die("No commit to dig down to?");
+	return final_commit_name;
+}
+
+static int blame_copy_callback(const struct option *option, const char *arg, int unset)
+{
+	int *opt = option->value;
+
+	/*
+	 * -C enables copy from removed files;
+	 * -C -C enables copy from existing files, but only
+	 *       when blaming a new file;
+	 * -C -C -C enables copy from existing files for
+	 *          everybody
+	 */
+	if (*opt & PICKAXE_BLAME_COPY_HARDER)
+		*opt |= PICKAXE_BLAME_COPY_HARDEST;
+	if (*opt & PICKAXE_BLAME_COPY)
+		*opt |= PICKAXE_BLAME_COPY_HARDER;
+	*opt |= PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE;
+
+	if (arg)
+		blame_copy_score = parse_score(arg);
+	return 0;
+}
+
+static int blame_move_callback(const struct option *option, const char *arg, int unset)
+{
+	int *opt = option->value;
+
+	*opt |= PICKAXE_BLAME_MOVE;
+
+	if (arg)
+		blame_move_score = parse_score(arg);
+	return 0;
+}
+
+static int blame_bottomtop_callback(const struct option *option, const char *arg, int unset)
+{
+	const char **bottomtop = option->value;
+	if (!arg)
+		return -1;
+	if (*bottomtop)
+		die("More than one '-L n,m' option given");
+	*bottomtop = arg;
+	return 0;
+}
+
+int cmd_blame(int argc, const char **argv, const char *prefix)
+{
+	struct rev_info revs;
+	const char *path;
+	struct scoreboard sb;
+	struct origin *o;
+	struct blame_entry *ent;
+	long dashdash_pos, bottom, top, lno;
+	const char *final_commit_name = NULL;
+	enum object_type type;
+
+	static const char *bottomtop = NULL;
+	static int output_option = 0, opt = 0;
+	static int show_stats = 0;
+	static const char *revs_file = NULL;
+	static const char *contents_from = NULL;
+	static const struct option options[] = {
+		OPT_BOOLEAN(0, "incremental", &incremental, "Show blame entries as we find them, incrementally"),
+		OPT_BOOLEAN('b', NULL, &blank_boundary, "Show blank SHA-1 for boundary commits (Default: off)"),
+		OPT_BOOLEAN(0, "root", &show_root, "Do not treat root commits as boundaries (Default: off)"),
+		OPT_BOOLEAN(0, "show-stats", &show_stats, "Show work cost statistics"),
+		OPT_BIT(0, "score-debug", &output_option, "Show output score for blame entries", OUTPUT_SHOW_SCORE),
+		OPT_BIT('f', "show-name", &output_option, "Show original filename (Default: auto)", OUTPUT_SHOW_NAME),
+		OPT_BIT('n', "show-number", &output_option, "Show original linenumber (Default: off)", OUTPUT_SHOW_NUMBER),
+		OPT_BIT('p', "porcelain", &output_option, "Show in a format designed for machine consumption", OUTPUT_PORCELAIN),
+		OPT_BIT(0, "line-porcelain", &output_option, "Show porcelain format with per-line commit information", OUTPUT_PORCELAIN|OUTPUT_LINE_PORCELAIN),
+		OPT_BIT('c', NULL, &output_option, "Use the same output mode as git-annotate (Default: off)", OUTPUT_ANNOTATE_COMPAT),
+		OPT_BIT('t', NULL, &output_option, "Show raw timestamp (Default: off)", OUTPUT_RAW_TIMESTAMP),
+		OPT_BIT('l', NULL, &output_option, "Show long commit SHA1 (Default: off)", OUTPUT_LONG_OBJECT_NAME),
+		OPT_BIT('s', NULL, &output_option, "Suppress author name and timestamp (Default: off)", OUTPUT_NO_AUTHOR),
+		OPT_BIT('e', "show-email", &output_option, "Show author email instead of name (Default: off)", OUTPUT_SHOW_EMAIL),
+		OPT_BIT('w', NULL, &xdl_opts, "Ignore whitespace differences", XDF_IGNORE_WHITESPACE),
+		OPT_BIT(0, "minimal", &xdl_opts, "Spend extra cycles to find better match", XDF_NEED_MINIMAL),
+		OPT_STRING('S', NULL, &revs_file, "file", "Use revisions from <file> instead of calling git-rev-list"),
+		OPT_STRING(0, "contents", &contents_from, "file", "Use <file>'s contents as the final image"),
+		{ OPTION_CALLBACK, 'C', NULL, &opt, "score", "Find line copies within and across files", PARSE_OPT_OPTARG, blame_copy_callback },
+		{ OPTION_CALLBACK, 'M', NULL, &opt, "score", "Find line movements within and across files", PARSE_OPT_OPTARG, blame_move_callback },
+		OPT_CALLBACK('L', NULL, &bottomtop, "n,m", "Process only line range n,m, counting from 1", blame_bottomtop_callback),
+		OPT__ABBREV(&abbrev),
+		OPT_END()
+	};
+
+	struct parse_opt_ctx_t ctx;
+	int cmd_is_annotate = !strcmp(argv[0], "annotate");
+
+	git_config(git_blame_config, NULL);
+	init_revisions(&revs, NULL);
+	revs.date_mode = blame_date_mode;
+	DIFF_OPT_SET(&revs.diffopt, ALLOW_TEXTCONV);
+
+	save_commit_buffer = 0;
+	dashdash_pos = 0;
+
+	parse_options_start(&ctx, argc, argv, prefix, options,
+			    PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0);
+	for (;;) {
+		switch (parse_options_step(&ctx, options, blame_opt_usage)) {
+		case PARSE_OPT_HELP:
+			exit(129);
+		case PARSE_OPT_DONE:
+			if (ctx.argv[0])
+				dashdash_pos = ctx.cpidx;
+			goto parse_done;
+		}
+
+		if (!strcmp(ctx.argv[0], "--reverse")) {
+			ctx.argv[0] = "--children";
+			reverse = 1;
+		}
+		parse_revision_opt(&revs, &ctx, options, blame_opt_usage);
+	}
+parse_done:
+	argc = parse_options_end(&ctx);
+
+	if (0 < abbrev)
+		/* one more abbrev length is needed for the boundary commit */
+		abbrev++;
+
+	if (revs_file && read_ancestry(revs_file))
+		die_errno("reading graft file '%s' failed", revs_file);
+
+	if (cmd_is_annotate) {
+		output_option |= OUTPUT_ANNOTATE_COMPAT;
+		blame_date_mode = DATE_ISO8601;
+	} else {
+		blame_date_mode = revs.date_mode;
+	}
+
+	/* The maximum width used to show the dates */
+	switch (blame_date_mode) {
+	case DATE_RFC2822:
+		blame_date_width = sizeof("Thu, 19 Oct 2006 16:00:04 -0700");
+		break;
+	case DATE_ISO8601:
+		blame_date_width = sizeof("2006-10-19 16:00:04 -0700");
+		break;
+	case DATE_RAW:
+		blame_date_width = sizeof("1161298804 -0700");
+		break;
+	case DATE_SHORT:
+		blame_date_width = sizeof("2006-10-19");
+		break;
+	case DATE_RELATIVE:
+		/* "normal" is used as the fallback for "relative" */
+	case DATE_LOCAL:
+	case DATE_NORMAL:
+		blame_date_width = sizeof("Thu Oct 19 16:00:04 2006 -0700");
+		break;
+	}
+	blame_date_width -= 1; /* strip the null */
+
+	if (DIFF_OPT_TST(&revs.diffopt, FIND_COPIES_HARDER))
+		opt |= (PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE |
+			PICKAXE_BLAME_COPY_HARDER);
+
+	if (!blame_move_score)
+		blame_move_score = BLAME_DEFAULT_MOVE_SCORE;
+	if (!blame_copy_score)
+		blame_copy_score = BLAME_DEFAULT_COPY_SCORE;
+
+	/*
+	 * We have collected options unknown to us in argv[1..unk]
+	 * which are to be passed to revision machinery if we are
+	 * going to do the "bottom" processing.
+	 *
+	 * The remaining are:
+	 *
+	 * (1) if dashdash_pos != 0, it is either
+	 *     "blame [revisions] -- <path>" or
+	 *     "blame -- <path> <rev>"
+	 *
+	 * (2) otherwise, it is one of the two:
+	 *     "blame [revisions] <path>"
+	 *     "blame <path> <rev>"
+	 *
+	 * Note that we must strip out <path> from the arguments: we do not
+	 * want the path pruning but we may want "bottom" processing.
+	 */
+	if (dashdash_pos) {
+		switch (argc - dashdash_pos - 1) {
+		case 2: /* (1b) */
+			if (argc != 4)
+				usage_with_options(blame_opt_usage, options);
+			/* reorder for the new way: <rev> -- <path> */
+			argv[1] = argv[3];
+			argv[3] = argv[2];
+			argv[2] = "--";
+			/* FALLTHROUGH */
+		case 1: /* (1a) */
+			path = add_prefix(prefix, argv[--argc]);
+			argv[argc] = NULL;
+			break;
+		default:
+			usage_with_options(blame_opt_usage, options);
+		}
+	} else {
+		if (argc < 2)
+			usage_with_options(blame_opt_usage, options);
+		path = add_prefix(prefix, argv[argc - 1]);
+		if (argc == 3 && !has_string_in_work_tree(path)) { /* (2b) */
+			path = add_prefix(prefix, argv[1]);
+			argv[1] = argv[2];
+		}
+		argv[argc - 1] = "--";
+
+		setup_work_tree();
+		if (!has_string_in_work_tree(path))
+			die_errno("cannot stat path '%s'", path);
+	}
+
+	revs.disable_stdin = 1;
+	setup_revisions(argc, argv, &revs, NULL);
+	memset(&sb, 0, sizeof(sb));
+
+	sb.revs = &revs;
+	if (!reverse)
+		final_commit_name = prepare_final(&sb);
+	else if (contents_from)
+		die("--contents and --children do not blend well.");
+	else
+		final_commit_name = prepare_initial(&sb);
+
+	if (!sb.final) {
+		/*
+		 * "--not A B -- path" without anything positive;
+		 * do not default to HEAD, but use the working tree
+		 * or "--contents".
+		 */
+		setup_work_tree();
+		sb.final = fake_working_tree_commit(&sb.revs->diffopt,
+						    path, contents_from);
+		add_pending_object(&revs, &(sb.final->object), ":");
+	}
+	else if (contents_from)
+		die("Cannot use --contents with final commit object name");
+
+	/*
+	 * If we have bottom, this will mark the ancestors of the
+	 * bottom commits we would reach while traversing as
+	 * uninteresting.
+	 */
+	if (prepare_revision_walk(&revs))
+		die("revision walk setup failed");
+
+	if (is_null_sha1(sb.final->object.sha1)) {
+		char *buf;
+		o = sb.final->util;
+		buf = xmalloc(o->file.size + 1);
+		memcpy(buf, o->file.ptr, o->file.size + 1);
+		sb.final_buf = buf;
+		sb.final_buf_size = o->file.size;
+	}
+	else {
+		o = get_origin(&sb, sb.final, path);
+		if (fill_blob_sha1_and_mode(o))
+			die("no such path %s in %s", path, final_commit_name);
+
+		if (DIFF_OPT_TST(&sb.revs->diffopt, ALLOW_TEXTCONV) &&
+		    textconv_object(path, o->mode, o->blob_sha1, (char **) &sb.final_buf,
+				    &sb.final_buf_size))
+			;
+		else
+			sb.final_buf = read_sha1_file(o->blob_sha1, &type,
+						      &sb.final_buf_size);
+
+		if (!sb.final_buf)
+			die("Cannot read blob %s for path %s",
+			    sha1_to_hex(o->blob_sha1),
+			    path);
+	}
+	num_read_blob++;
+	lno = prepare_lines(&sb);
+
+	bottom = top = 0;
+	if (bottomtop)
+		prepare_blame_range(&sb, bottomtop, lno, &bottom, &top);
+	if (bottom && top && top < bottom) {
+		long tmp;
+		tmp = top; top = bottom; bottom = tmp;
+	}
+	if (bottom < 1)
+		bottom = 1;
+	if (top < 1)
+		top = lno;
+	bottom--;
+	if (lno < top || lno < bottom)
+		die("file %s has only %lu lines", path, lno);
+
+	ent = xcalloc(1, sizeof(*ent));
+	ent->lno = bottom;
+	ent->num_lines = top - bottom;
+	ent->suspect = o;
+	ent->s_lno = bottom;
+
+	sb.ent = ent;
+	sb.path = path;
+
+	read_mailmap(&mailmap, NULL);
+
+	if (!incremental)
+		setup_pager();
+
+	assign_blame(&sb, opt);
+
+	if (incremental)
+		return 0;
+
+	coalesce(&sb);
+
+	if (!(output_option & OUTPUT_PORCELAIN))
+		find_alignment(&sb, &output_option);
+
+	output(&sb, output_option);
+	free((void *)sb.final_buf);
+	for (ent = sb.ent; ent; ) {
+		struct blame_entry *e = ent->next;
+		free(ent);
+		ent = e;
+	}
+
+	if (show_stats) {
+		printf("num read blob: %d\n", num_read_blob);
+		printf("num get patch: %d\n", num_get_patch);
+		printf("num commits: %d\n", num_commits);
+	}
+	return 0;
+}
diff --git a/builtin/branch.c b/builtin/branch.c
new file mode 100644
index 0000000..0e060f2
--- /dev/null
+++ b/builtin/branch.c
@@ -0,0 +1,864 @@
+/*
+ * Builtin "git branch"
+ *
+ * Copyright (c) 2006 Kristian Høgsberg <krh@redhat.com>
+ * Based on git-branch.sh by Junio C Hamano.
+ */
+
+#include "cache.h"
+#include "color.h"
+#include "refs.h"
+#include "commit.h"
+#include "builtin.h"
+#include "remote.h"
+#include "parse-options.h"
+#include "branch.h"
+#include "diff.h"
+#include "revision.h"
+#include "string-list.h"
+#include "column.h"
+
+static const char * const builtin_branch_usage[] = {
+	"git branch [options] [-r | -a] [--merged | --no-merged]",
+	"git branch [options] [-l] [-f] <branchname> [<start-point>]",
+	"git branch [options] [-r] (-d | -D) <branchname>...",
+	"git branch [options] (-m | -M) [<oldbranch>] <newbranch>",
+	NULL
+};
+
+#define REF_LOCAL_BRANCH    0x01
+#define REF_REMOTE_BRANCH   0x02
+
+static const char *head;
+static unsigned char head_sha1[20];
+
+static int branch_use_color = -1;
+static char branch_colors[][COLOR_MAXLEN] = {
+	GIT_COLOR_RESET,
+	GIT_COLOR_NORMAL,	/* PLAIN */
+	GIT_COLOR_RED,		/* REMOTE */
+	GIT_COLOR_NORMAL,	/* LOCAL */
+	GIT_COLOR_GREEN,	/* CURRENT */
+};
+enum color_branch {
+	BRANCH_COLOR_RESET = 0,
+	BRANCH_COLOR_PLAIN = 1,
+	BRANCH_COLOR_REMOTE = 2,
+	BRANCH_COLOR_LOCAL = 3,
+	BRANCH_COLOR_CURRENT = 4
+};
+
+static enum merge_filter {
+	NO_FILTER = 0,
+	SHOW_NOT_MERGED,
+	SHOW_MERGED
+} merge_filter;
+static unsigned char merge_filter_ref[20];
+
+static struct string_list output = STRING_LIST_INIT_DUP;
+static unsigned int colopts;
+
+static int parse_branch_color_slot(const char *var, int ofs)
+{
+	if (!strcasecmp(var+ofs, "plain"))
+		return BRANCH_COLOR_PLAIN;
+	if (!strcasecmp(var+ofs, "reset"))
+		return BRANCH_COLOR_RESET;
+	if (!strcasecmp(var+ofs, "remote"))
+		return BRANCH_COLOR_REMOTE;
+	if (!strcasecmp(var+ofs, "local"))
+		return BRANCH_COLOR_LOCAL;
+	if (!strcasecmp(var+ofs, "current"))
+		return BRANCH_COLOR_CURRENT;
+	return -1;
+}
+
+static int git_branch_config(const char *var, const char *value, void *cb)
+{
+	if (!prefixcmp(var, "column."))
+		return git_column_config(var, value, "branch", &colopts);
+	if (!strcmp(var, "color.branch")) {
+		branch_use_color = git_config_colorbool(var, value);
+		return 0;
+	}
+	if (!prefixcmp(var, "color.branch.")) {
+		int slot = parse_branch_color_slot(var, 13);
+		if (slot < 0)
+			return 0;
+		if (!value)
+			return config_error_nonbool(var);
+		color_parse(value, var, branch_colors[slot]);
+		return 0;
+	}
+	return git_color_default_config(var, value, cb);
+}
+
+static const char *branch_get_color(enum color_branch ix)
+{
+	if (want_color(branch_use_color))
+		return branch_colors[ix];
+	return "";
+}
+
+static int branch_merged(int kind, const char *name,
+			 struct commit *rev, struct commit *head_rev)
+{
+	/*
+	 * This checks whether the merge bases of branch and HEAD (or
+	 * the other branch this branch builds upon) contains the
+	 * branch, which means that the branch has already been merged
+	 * safely to HEAD (or the other branch).
+	 */
+	struct commit *reference_rev = NULL;
+	const char *reference_name = NULL;
+	void *reference_name_to_free = NULL;
+	int merged;
+
+	if (kind == REF_LOCAL_BRANCH) {
+		struct branch *branch = branch_get(name);
+		unsigned char sha1[20];
+
+		if (branch &&
+		    branch->merge &&
+		    branch->merge[0] &&
+		    branch->merge[0]->dst &&
+		    (reference_name = reference_name_to_free =
+		     resolve_refdup(branch->merge[0]->dst, sha1, 1, NULL)) != NULL)
+			reference_rev = lookup_commit_reference(sha1);
+	}
+	if (!reference_rev)
+		reference_rev = head_rev;
+
+	merged = in_merge_bases(rev, &reference_rev, 1);
+
+	/*
+	 * After the safety valve is fully redefined to "check with
+	 * upstream, if any, otherwise with HEAD", we should just
+	 * return the result of the in_merge_bases() above without
+	 * any of the following code, but during the transition period,
+	 * a gentle reminder is in order.
+	 */
+	if ((head_rev != reference_rev) &&
+	    in_merge_bases(rev, &head_rev, 1) != merged) {
+		if (merged)
+			warning(_("deleting branch '%s' that has been merged to\n"
+				"         '%s', but not yet merged to HEAD."),
+				name, reference_name);
+		else
+			warning(_("not deleting branch '%s' that is not yet merged to\n"
+				"         '%s', even though it is merged to HEAD."),
+				name, reference_name);
+	}
+	free(reference_name_to_free);
+	return merged;
+}
+
+static int delete_branches(int argc, const char **argv, int force, int kinds,
+			   int quiet)
+{
+	struct commit *rev, *head_rev = NULL;
+	unsigned char sha1[20];
+	char *name = NULL;
+	const char *fmt;
+	int i;
+	int ret = 0;
+	int remote_branch = 0;
+	struct strbuf bname = STRBUF_INIT;
+
+	switch (kinds) {
+	case REF_REMOTE_BRANCH:
+		fmt = "refs/remotes/%s";
+		/* For subsequent UI messages */
+		remote_branch = 1;
+
+		force = 1;
+		break;
+	case REF_LOCAL_BRANCH:
+		fmt = "refs/heads/%s";
+		break;
+	default:
+		die(_("cannot use -a with -d"));
+	}
+
+	if (!force) {
+		head_rev = lookup_commit_reference(head_sha1);
+		if (!head_rev)
+			die(_("Couldn't look up commit object for HEAD"));
+	}
+	for (i = 0; i < argc; i++, strbuf_release(&bname)) {
+		strbuf_branchname(&bname, argv[i]);
+		if (kinds == REF_LOCAL_BRANCH && !strcmp(head, bname.buf)) {
+			error(_("Cannot delete the branch '%s' "
+			      "which you are currently on."), bname.buf);
+			ret = 1;
+			continue;
+		}
+
+		free(name);
+
+		name = xstrdup(mkpath(fmt, bname.buf));
+		if (read_ref(name, sha1)) {
+			error(remote_branch
+			      ? _("remote branch '%s' not found.")
+			      : _("branch '%s' not found."), bname.buf);
+			ret = 1;
+			continue;
+		}
+
+		rev = lookup_commit_reference(sha1);
+		if (!rev) {
+			error(_("Couldn't look up commit object for '%s'"), name);
+			ret = 1;
+			continue;
+		}
+
+		if (!force && !branch_merged(kinds, bname.buf, rev, head_rev)) {
+			error(_("The branch '%s' is not fully merged.\n"
+			      "If you are sure you want to delete it, "
+			      "run 'git branch -D %s'."), bname.buf, bname.buf);
+			ret = 1;
+			continue;
+		}
+
+		if (delete_ref(name, sha1, 0)) {
+			error(remote_branch
+			      ? _("Error deleting remote branch '%s'")
+			      : _("Error deleting branch '%s'"),
+			      bname.buf);
+			ret = 1;
+		} else {
+			struct strbuf buf = STRBUF_INIT;
+			if (!quiet)
+				printf(remote_branch
+				       ? _("Deleted remote branch %s (was %s).\n")
+				       : _("Deleted branch %s (was %s).\n"),
+				       bname.buf,
+				       find_unique_abbrev(sha1, DEFAULT_ABBREV));
+			strbuf_addf(&buf, "branch.%s", bname.buf);
+			if (git_config_rename_section(buf.buf, NULL) < 0)
+				warning(_("Update of config-file failed"));
+			strbuf_release(&buf);
+		}
+	}
+
+	free(name);
+
+	return(ret);
+}
+
+struct ref_item {
+	char *name;
+	char *dest;
+	unsigned int kind, len;
+	struct commit *commit;
+};
+
+struct ref_list {
+	struct rev_info revs;
+	int index, alloc, maxwidth, verbose, abbrev;
+	struct ref_item *list;
+	struct commit_list *with_commit;
+	int kinds;
+};
+
+static char *resolve_symref(const char *src, const char *prefix)
+{
+	unsigned char sha1[20];
+	int flag;
+	const char *dst, *cp;
+
+	dst = resolve_ref_unsafe(src, sha1, 0, &flag);
+	if (!(dst && (flag & REF_ISSYMREF)))
+		return NULL;
+	if (prefix && (cp = skip_prefix(dst, prefix)))
+		dst = cp;
+	return xstrdup(dst);
+}
+
+struct append_ref_cb {
+	struct ref_list *ref_list;
+	const char **pattern;
+	int ret;
+};
+
+static int match_patterns(const char **pattern, const char *refname)
+{
+	if (!*pattern)
+		return 1; /* no pattern always matches */
+	while (*pattern) {
+		if (!fnmatch(*pattern, refname, 0))
+			return 1;
+		pattern++;
+	}
+	return 0;
+}
+
+static int append_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
+{
+	struct append_ref_cb *cb = (struct append_ref_cb *)(cb_data);
+	struct ref_list *ref_list = cb->ref_list;
+	struct ref_item *newitem;
+	struct commit *commit;
+	int kind, i;
+	const char *prefix, *orig_refname = refname;
+
+	static struct {
+		int kind;
+		const char *prefix;
+		int pfxlen;
+	} ref_kind[] = {
+		{ REF_LOCAL_BRANCH, "refs/heads/", 11 },
+		{ REF_REMOTE_BRANCH, "refs/remotes/", 13 },
+	};
+
+	/* Detect kind */
+	for (i = 0; i < ARRAY_SIZE(ref_kind); i++) {
+		prefix = ref_kind[i].prefix;
+		if (strncmp(refname, prefix, ref_kind[i].pfxlen))
+			continue;
+		kind = ref_kind[i].kind;
+		refname += ref_kind[i].pfxlen;
+		break;
+	}
+	if (ARRAY_SIZE(ref_kind) <= i)
+		return 0;
+
+	/* Don't add types the caller doesn't want */
+	if ((kind & ref_list->kinds) == 0)
+		return 0;
+
+	if (!match_patterns(cb->pattern, refname))
+		return 0;
+
+	commit = NULL;
+	if (ref_list->verbose || ref_list->with_commit || merge_filter != NO_FILTER) {
+		commit = lookup_commit_reference_gently(sha1, 1);
+		if (!commit) {
+			cb->ret = error(_("branch '%s' does not point at a commit"), refname);
+			return 0;
+		}
+
+		/* Filter with with_commit if specified */
+		if (!is_descendant_of(commit, ref_list->with_commit))
+			return 0;
+
+		if (merge_filter != NO_FILTER)
+			add_pending_object(&ref_list->revs,
+					   (struct object *)commit, refname);
+	}
+
+	ALLOC_GROW(ref_list->list, ref_list->index + 1, ref_list->alloc);
+
+	/* Record the new item */
+	newitem = &(ref_list->list[ref_list->index++]);
+	newitem->name = xstrdup(refname);
+	newitem->kind = kind;
+	newitem->commit = commit;
+	newitem->len = strlen(refname);
+	newitem->dest = resolve_symref(orig_refname, prefix);
+	/* adjust for "remotes/" */
+	if (newitem->kind == REF_REMOTE_BRANCH &&
+	    ref_list->kinds != REF_REMOTE_BRANCH)
+		newitem->len += 8;
+	if (newitem->len > ref_list->maxwidth)
+		ref_list->maxwidth = newitem->len;
+
+	return 0;
+}
+
+static void free_ref_list(struct ref_list *ref_list)
+{
+	int i;
+
+	for (i = 0; i < ref_list->index; i++) {
+		free(ref_list->list[i].name);
+		free(ref_list->list[i].dest);
+	}
+	free(ref_list->list);
+}
+
+static int ref_cmp(const void *r1, const void *r2)
+{
+	struct ref_item *c1 = (struct ref_item *)(r1);
+	struct ref_item *c2 = (struct ref_item *)(r2);
+
+	if (c1->kind != c2->kind)
+		return c1->kind - c2->kind;
+	return strcmp(c1->name, c2->name);
+}
+
+static void fill_tracking_info(struct strbuf *stat, const char *branch_name,
+		int show_upstream_ref)
+{
+	int ours, theirs;
+	char *ref = NULL;
+	struct branch *branch = branch_get(branch_name);
+
+	if (!stat_tracking_info(branch, &ours, &theirs)) {
+		if (branch && branch->merge && branch->merge[0]->dst &&
+		    show_upstream_ref)
+			strbuf_addf(stat, "[%s] ",
+			    shorten_unambiguous_ref(branch->merge[0]->dst, 0));
+		return;
+	}
+
+	if (show_upstream_ref)
+		ref = shorten_unambiguous_ref(branch->merge[0]->dst, 0);
+	if (!ours) {
+		if (ref)
+			strbuf_addf(stat, _("[%s: behind %d]"), ref, theirs);
+		else
+			strbuf_addf(stat, _("[behind %d]"), theirs);
+
+	} else if (!theirs) {
+		if (ref)
+			strbuf_addf(stat, _("[%s: ahead %d]"), ref, ours);
+		else
+			strbuf_addf(stat, _("[ahead %d]"), ours);
+	} else {
+		if (ref)
+			strbuf_addf(stat, _("[%s: ahead %d, behind %d]"),
+				    ref, ours, theirs);
+		else
+			strbuf_addf(stat, _("[ahead %d, behind %d]"),
+				    ours, theirs);
+	}
+	strbuf_addch(stat, ' ');
+	free(ref);
+}
+
+static int matches_merge_filter(struct commit *commit)
+{
+	int is_merged;
+
+	if (merge_filter == NO_FILTER)
+		return 1;
+
+	is_merged = !!(commit->object.flags & UNINTERESTING);
+	return (is_merged == (merge_filter == SHOW_MERGED));
+}
+
+static void add_verbose_info(struct strbuf *out, struct ref_item *item,
+			     int verbose, int abbrev)
+{
+	struct strbuf subject = STRBUF_INIT, stat = STRBUF_INIT;
+	const char *sub = " **** invalid ref ****";
+	struct commit *commit = item->commit;
+
+	if (commit && !parse_commit(commit)) {
+		pp_commit_easy(CMIT_FMT_ONELINE, commit, &subject);
+		sub = subject.buf;
+	}
+
+	if (item->kind == REF_LOCAL_BRANCH)
+		fill_tracking_info(&stat, item->name, verbose > 1);
+
+	strbuf_addf(out, " %s %s%s",
+		find_unique_abbrev(item->commit->object.sha1, abbrev),
+		stat.buf, sub);
+	strbuf_release(&stat);
+	strbuf_release(&subject);
+}
+
+static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
+			   int abbrev, int current, char *prefix)
+{
+	char c;
+	int color;
+	struct commit *commit = item->commit;
+	struct strbuf out = STRBUF_INIT, name = STRBUF_INIT;
+
+	if (!matches_merge_filter(commit))
+		return;
+
+	switch (item->kind) {
+	case REF_LOCAL_BRANCH:
+		color = BRANCH_COLOR_LOCAL;
+		break;
+	case REF_REMOTE_BRANCH:
+		color = BRANCH_COLOR_REMOTE;
+		break;
+	default:
+		color = BRANCH_COLOR_PLAIN;
+		break;
+	}
+
+	c = ' ';
+	if (current) {
+		c = '*';
+		color = BRANCH_COLOR_CURRENT;
+	}
+
+	strbuf_addf(&name, "%s%s", prefix, item->name);
+	if (verbose)
+		strbuf_addf(&out, "%c %s%-*s%s", c, branch_get_color(color),
+			    maxwidth, name.buf,
+			    branch_get_color(BRANCH_COLOR_RESET));
+	else
+		strbuf_addf(&out, "%c %s%s%s", c, branch_get_color(color),
+			    name.buf, branch_get_color(BRANCH_COLOR_RESET));
+
+	if (item->dest)
+		strbuf_addf(&out, " -> %s", item->dest);
+	else if (verbose)
+		/* " f7c0c00 [ahead 58, behind 197] vcs-svn: drop obj_pool.h" */
+		add_verbose_info(&out, item, verbose, abbrev);
+	if (column_active(colopts)) {
+		assert(!verbose && "--column and --verbose are incompatible");
+		string_list_append(&output, out.buf);
+	} else {
+		printf("%s\n", out.buf);
+	}
+	strbuf_release(&name);
+	strbuf_release(&out);
+}
+
+static int calc_maxwidth(struct ref_list *refs)
+{
+	int i, w = 0;
+	for (i = 0; i < refs->index; i++) {
+		if (!matches_merge_filter(refs->list[i].commit))
+			continue;
+		if (refs->list[i].len > w)
+			w = refs->list[i].len;
+	}
+	return w;
+}
+
+
+static void show_detached(struct ref_list *ref_list)
+{
+	struct commit *head_commit = lookup_commit_reference_gently(head_sha1, 1);
+
+	if (head_commit && is_descendant_of(head_commit, ref_list->with_commit)) {
+		struct ref_item item;
+		item.name = xstrdup(_("(no branch)"));
+		item.len = strlen(item.name);
+		item.kind = REF_LOCAL_BRANCH;
+		item.dest = NULL;
+		item.commit = head_commit;
+		if (item.len > ref_list->maxwidth)
+			ref_list->maxwidth = item.len;
+		print_ref_item(&item, ref_list->maxwidth, ref_list->verbose, ref_list->abbrev, 1, "");
+		free(item.name);
+	}
+}
+
+static int print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit, const char **pattern)
+{
+	int i;
+	struct append_ref_cb cb;
+	struct ref_list ref_list;
+
+	memset(&ref_list, 0, sizeof(ref_list));
+	ref_list.kinds = kinds;
+	ref_list.verbose = verbose;
+	ref_list.abbrev = abbrev;
+	ref_list.with_commit = with_commit;
+	if (merge_filter != NO_FILTER)
+		init_revisions(&ref_list.revs, NULL);
+	cb.ref_list = &ref_list;
+	cb.pattern = pattern;
+	cb.ret = 0;
+	for_each_rawref(append_ref, &cb);
+	if (merge_filter != NO_FILTER) {
+		struct commit *filter;
+		filter = lookup_commit_reference_gently(merge_filter_ref, 0);
+		if (!filter)
+			die("object '%s' does not point to a commit",
+			    sha1_to_hex(merge_filter_ref));
+
+		filter->object.flags |= UNINTERESTING;
+		add_pending_object(&ref_list.revs,
+				   (struct object *) filter, "");
+		ref_list.revs.limited = 1;
+		prepare_revision_walk(&ref_list.revs);
+		if (verbose)
+			ref_list.maxwidth = calc_maxwidth(&ref_list);
+	}
+
+	qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp);
+
+	detached = (detached && (kinds & REF_LOCAL_BRANCH));
+	if (detached && match_patterns(pattern, "HEAD"))
+		show_detached(&ref_list);
+
+	for (i = 0; i < ref_list.index; i++) {
+		int current = !detached &&
+			(ref_list.list[i].kind == REF_LOCAL_BRANCH) &&
+			!strcmp(ref_list.list[i].name, head);
+		char *prefix = (kinds != REF_REMOTE_BRANCH &&
+				ref_list.list[i].kind == REF_REMOTE_BRANCH)
+				? "remotes/" : "";
+		print_ref_item(&ref_list.list[i], ref_list.maxwidth, verbose,
+			       abbrev, current, prefix);
+	}
+
+	free_ref_list(&ref_list);
+
+	if (cb.ret)
+		error(_("some refs could not be read"));
+
+	return cb.ret;
+}
+
+static void rename_branch(const char *oldname, const char *newname, int force)
+{
+	struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT;
+	struct strbuf oldsection = STRBUF_INIT, newsection = STRBUF_INIT;
+	int recovery = 0;
+	int clobber_head_ok;
+
+	if (!oldname)
+		die(_("cannot rename the current branch while not on any."));
+
+	if (strbuf_check_branch_ref(&oldref, oldname)) {
+		/*
+		 * Bad name --- this could be an attempt to rename a
+		 * ref that we used to allow to be created by accident.
+		 */
+		if (ref_exists(oldref.buf))
+			recovery = 1;
+		else
+			die(_("Invalid branch name: '%s'"), oldname);
+	}
+
+	/*
+	 * A command like "git branch -M currentbranch currentbranch" cannot
+	 * cause the worktree to become inconsistent with HEAD, so allow it.
+	 */
+	clobber_head_ok = !strcmp(oldname, newname);
+
+	validate_new_branchname(newname, &newref, force, clobber_head_ok);
+
+	strbuf_addf(&logmsg, "Branch: renamed %s to %s",
+		 oldref.buf, newref.buf);
+
+	if (rename_ref(oldref.buf, newref.buf, logmsg.buf))
+		die(_("Branch rename failed"));
+	strbuf_release(&logmsg);
+
+	if (recovery)
+		warning(_("Renamed a misnamed branch '%s' away"), oldref.buf + 11);
+
+	/* no need to pass logmsg here as HEAD didn't really move */
+	if (!strcmp(oldname, head) && create_symref("HEAD", newref.buf, NULL))
+		die(_("Branch renamed to %s, but HEAD is not updated!"), newname);
+
+	strbuf_addf(&oldsection, "branch.%s", oldref.buf + 11);
+	strbuf_release(&oldref);
+	strbuf_addf(&newsection, "branch.%s", newref.buf + 11);
+	strbuf_release(&newref);
+	if (git_config_rename_section(oldsection.buf, newsection.buf) < 0)
+		die(_("Branch is renamed, but update of config-file failed"));
+	strbuf_release(&oldsection);
+	strbuf_release(&newsection);
+}
+
+static int opt_parse_merge_filter(const struct option *opt, const char *arg, int unset)
+{
+	merge_filter = ((opt->long_name[0] == 'n')
+			? SHOW_NOT_MERGED
+			: SHOW_MERGED);
+	if (unset)
+		merge_filter = SHOW_NOT_MERGED; /* b/c for --no-merged */
+	if (!arg)
+		arg = "HEAD";
+	if (get_sha1(arg, merge_filter_ref))
+		die(_("malformed object name %s"), arg);
+	return 0;
+}
+
+static const char edit_description[] = "BRANCH_DESCRIPTION";
+
+static int edit_branch_description(const char *branch_name)
+{
+	FILE *fp;
+	int status;
+	struct strbuf buf = STRBUF_INIT;
+	struct strbuf name = STRBUF_INIT;
+
+	read_branch_desc(&buf, branch_name);
+	if (!buf.len || buf.buf[buf.len-1] != '\n')
+		strbuf_addch(&buf, '\n');
+	strbuf_addf(&buf,
+		    "# Please edit the description for the branch\n"
+		    "#   %s\n"
+		    "# Lines starting with '#' will be stripped.\n",
+		    branch_name);
+	fp = fopen(git_path(edit_description), "w");
+	if ((fwrite(buf.buf, 1, buf.len, fp) < buf.len) || fclose(fp)) {
+		strbuf_release(&buf);
+		return error(_("could not write branch description template: %s"),
+			     strerror(errno));
+	}
+	strbuf_reset(&buf);
+	if (launch_editor(git_path(edit_description), &buf, NULL)) {
+		strbuf_release(&buf);
+		return -1;
+	}
+	stripspace(&buf, 1);
+
+	strbuf_addf(&name, "branch.%s.description", branch_name);
+	status = git_config_set(name.buf, buf.buf);
+	strbuf_release(&name);
+	strbuf_release(&buf);
+
+	return status;
+}
+
+int cmd_branch(int argc, const char **argv, const char *prefix)
+{
+	int delete = 0, rename = 0, force_create = 0, list = 0;
+	int verbose = 0, abbrev = -1, detached = 0;
+	int reflog = 0, edit_description = 0;
+	int quiet = 0;
+	enum branch_track track;
+	int kinds = REF_LOCAL_BRANCH;
+	struct commit_list *with_commit = NULL;
+
+	struct option options[] = {
+		OPT_GROUP("Generic options"),
+		OPT__VERBOSE(&verbose,
+			"show hash and subject, give twice for upstream branch"),
+		OPT__QUIET(&quiet, "suppress informational messages"),
+		OPT_SET_INT('t', "track",  &track, "set up tracking mode (see git-pull(1))",
+			BRANCH_TRACK_EXPLICIT),
+		OPT_SET_INT( 0, "set-upstream",  &track, "change upstream info",
+			BRANCH_TRACK_OVERRIDE),
+		OPT__COLOR(&branch_use_color, "use colored output"),
+		OPT_SET_INT('r', "remotes",     &kinds, "act on remote-tracking branches",
+			REF_REMOTE_BRANCH),
+		{
+			OPTION_CALLBACK, 0, "contains", &with_commit, "commit",
+			"print only branches that contain the commit",
+			PARSE_OPT_LASTARG_DEFAULT,
+			parse_opt_with_commit, (intptr_t)"HEAD",
+		},
+		{
+			OPTION_CALLBACK, 0, "with", &with_commit, "commit",
+			"print only branches that contain the commit",
+			PARSE_OPT_HIDDEN | PARSE_OPT_LASTARG_DEFAULT,
+			parse_opt_with_commit, (intptr_t) "HEAD",
+		},
+		OPT__ABBREV(&abbrev),
+
+		OPT_GROUP("Specific git-branch actions:"),
+		OPT_SET_INT('a', "all", &kinds, "list both remote-tracking and local branches",
+			REF_REMOTE_BRANCH | REF_LOCAL_BRANCH),
+		OPT_BIT('d', "delete", &delete, "delete fully merged branch", 1),
+		OPT_BIT('D', NULL, &delete, "delete branch (even if not merged)", 2),
+		OPT_BIT('m', "move", &rename, "move/rename a branch and its reflog", 1),
+		OPT_BIT('M', NULL, &rename, "move/rename a branch, even if target exists", 2),
+		OPT_BOOLEAN(0, "list", &list, "list branch names"),
+		OPT_BOOLEAN('l', "create-reflog", &reflog, "create the branch's reflog"),
+		OPT_BOOLEAN(0, "edit-description", &edit_description,
+			    "edit the description for the branch"),
+		OPT__FORCE(&force_create, "force creation (when already exists)"),
+		{
+			OPTION_CALLBACK, 0, "no-merged", &merge_filter_ref,
+			"commit", "print only not merged branches",
+			PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG,
+			opt_parse_merge_filter, (intptr_t) "HEAD",
+		},
+		{
+			OPTION_CALLBACK, 0, "merged", &merge_filter_ref,
+			"commit", "print only merged branches",
+			PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG,
+			opt_parse_merge_filter, (intptr_t) "HEAD",
+		},
+		OPT_COLUMN(0, "column", &colopts, "list branches in columns"),
+		OPT_END(),
+	};
+
+	if (argc == 2 && !strcmp(argv[1], "-h"))
+		usage_with_options(builtin_branch_usage, options);
+
+	git_config(git_branch_config, NULL);
+
+	track = git_branch_track;
+
+	head = resolve_refdup("HEAD", head_sha1, 0, NULL);
+	if (!head)
+		die(_("Failed to resolve HEAD as a valid ref."));
+	if (!strcmp(head, "HEAD")) {
+		detached = 1;
+	} else {
+		if (prefixcmp(head, "refs/heads/"))
+			die(_("HEAD not found below refs/heads!"));
+		head += 11;
+	}
+	hashcpy(merge_filter_ref, head_sha1);
+
+
+	argc = parse_options(argc, argv, prefix, options, builtin_branch_usage,
+			     0);
+
+	if (!delete && !rename && !edit_description && argc == 0)
+		list = 1;
+
+	if (!!delete + !!rename + !!force_create + !!list > 1)
+		usage_with_options(builtin_branch_usage, options);
+
+	if (abbrev == -1)
+		abbrev = DEFAULT_ABBREV;
+	finalize_colopts(&colopts, -1);
+	if (verbose) {
+		if (explicitly_enable_column(colopts))
+			die(_("--column and --verbose are incompatible"));
+		colopts = 0;
+	}
+
+	if (delete)
+		return delete_branches(argc, argv, delete > 1, kinds, quiet);
+	else if (list) {
+		int ret = print_ref_list(kinds, detached, verbose, abbrev,
+					 with_commit, argv);
+		print_columns(&output, colopts, NULL);
+		string_list_clear(&output, 0);
+		return ret;
+	}
+	else if (edit_description) {
+		const char *branch_name;
+		struct strbuf branch_ref = STRBUF_INIT;
+
+		if (detached)
+			die("Cannot give description to detached HEAD");
+		if (!argc)
+			branch_name = head;
+		else if (argc == 1)
+			branch_name = argv[0];
+		else
+			usage_with_options(builtin_branch_usage, options);
+
+		strbuf_addf(&branch_ref, "refs/heads/%s", branch_name);
+		if (!ref_exists(branch_ref.buf)) {
+			strbuf_release(&branch_ref);
+
+			if (!argc)
+				return error("No commit on branch '%s' yet.",
+					     branch_name);
+			else
+				return error("No such branch '%s'.", branch_name);
+		}
+		strbuf_release(&branch_ref);
+
+		if (edit_branch_description(branch_name))
+			return 1;
+	} else if (rename) {
+		if (argc == 1)
+			rename_branch(head, argv[0], rename > 1);
+		else if (argc == 2)
+			rename_branch(argv[0], argv[1], rename > 1);
+		else
+			usage_with_options(builtin_branch_usage, options);
+	} else if (argc > 0 && argc <= 2) {
+		if (kinds != REF_LOCAL_BRANCH)
+			die(_("-a and -r options to 'git branch' do not make sense with a branch name"));
+		create_branch(head, argv[0], (argc == 2) ? argv[1] : head,
+			      force_create, reflog, 0, quiet, track);
+	} else
+		usage_with_options(builtin_branch_usage, options);
+
+	return 0;
+}
diff --git a/builtin/bundle.c b/builtin/bundle.c
new file mode 100644
index 0000000..92a8a60
--- /dev/null
+++ b/builtin/bundle.c
@@ -0,0 +1,65 @@
+#include "builtin.h"
+#include "cache.h"
+#include "bundle.h"
+
+/*
+ * Basic handler for bundle files to connect repositories via sneakernet.
+ * Invocation must include action.
+ * This function can create a bundle or provide information on an existing
+ * bundle supporting "fetch", "pull", and "ls-remote".
+ */
+
+static const char builtin_bundle_usage[] =
+  "git bundle create <file> <git-rev-list args>\n"
+  "   or: git bundle verify <file>\n"
+  "   or: git bundle list-heads <file> [<refname>...]\n"
+  "   or: git bundle unbundle <file> [<refname>...]";
+
+int cmd_bundle(int argc, const char **argv, const char *prefix)
+{
+	struct bundle_header header;
+	const char *cmd, *bundle_file;
+	int bundle_fd = -1;
+	char buffer[PATH_MAX];
+
+	if (argc < 3)
+		usage(builtin_bundle_usage);
+
+	cmd = argv[1];
+	bundle_file = argv[2];
+	argc -= 2;
+	argv += 2;
+
+	if (prefix && bundle_file[0] != '/') {
+		snprintf(buffer, sizeof(buffer), "%s/%s", prefix, bundle_file);
+		bundle_file = buffer;
+	}
+
+	memset(&header, 0, sizeof(header));
+	if (strcmp(cmd, "create") && (bundle_fd =
+				read_bundle_header(bundle_file, &header)) < 0)
+		return 1;
+
+	if (!strcmp(cmd, "verify")) {
+		close(bundle_fd);
+		if (verify_bundle(&header, 1))
+			return 1;
+		fprintf(stderr, _("%s is okay\n"), bundle_file);
+		return 0;
+	}
+	if (!strcmp(cmd, "list-heads")) {
+		close(bundle_fd);
+		return !!list_bundle_refs(&header, argc, argv);
+	}
+	if (!strcmp(cmd, "create")) {
+		if (!startup_info->have_repository)
+			die(_("Need a repository to create a bundle."));
+		return !!create_bundle(&header, bundle_file, argc, argv);
+	} else if (!strcmp(cmd, "unbundle")) {
+		if (!startup_info->have_repository)
+			die(_("Need a repository to unbundle."));
+		return !!unbundle(&header, bundle_fd, 0) ||
+			list_bundle_refs(&header, argc, argv);
+	} else
+		usage(builtin_bundle_usage);
+}
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
new file mode 100644
index 0000000..af74e77
--- /dev/null
+++ b/builtin/cat-file.c
@@ -0,0 +1,311 @@
+/*
+ * GIT - The information manager from hell
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ */
+#include "cache.h"
+#include "exec_cmd.h"
+#include "tag.h"
+#include "tree.h"
+#include "builtin.h"
+#include "parse-options.h"
+#include "diff.h"
+#include "userdiff.h"
+#include "streaming.h"
+
+#define BATCH 1
+#define BATCH_CHECK 2
+
+static void pprint_tag(const unsigned char *sha1, const char *buf, unsigned long size)
+{
+	/* the parser in tag.c is useless here. */
+	const char *endp = buf + size;
+	const char *cp = buf;
+
+	while (cp < endp) {
+		char c = *cp++;
+		if (c != '\n')
+			continue;
+		if (7 <= endp - cp && !memcmp("tagger ", cp, 7)) {
+			const char *tagger = cp;
+
+			/* Found the tagger line.  Copy out the contents
+			 * of the buffer so far.
+			 */
+			write_or_die(1, buf, cp - buf);
+
+			/*
+			 * Do something intelligent, like pretty-printing
+			 * the date.
+			 */
+			while (cp < endp) {
+				if (*cp++ == '\n') {
+					/* tagger to cp is a line
+					 * that has ident and time.
+					 */
+					const char *sp = tagger;
+					char *ep;
+					unsigned long date;
+					long tz;
+					while (sp < cp && *sp != '>')
+						sp++;
+					if (sp == cp) {
+						/* give up */
+						write_or_die(1, tagger,
+							     cp - tagger);
+						break;
+					}
+					while (sp < cp &&
+					       !('0' <= *sp && *sp <= '9'))
+						sp++;
+					write_or_die(1, tagger, sp - tagger);
+					date = strtoul(sp, &ep, 10);
+					tz = strtol(ep, NULL, 10);
+					sp = show_date(date, tz, 0);
+					write_or_die(1, sp, strlen(sp));
+					xwrite(1, "\n", 1);
+					break;
+				}
+			}
+			break;
+		}
+		if (cp < endp && *cp == '\n')
+			/* end of header */
+			break;
+	}
+	/* At this point, we have copied out the header up to the end of
+	 * the tagger line and cp points at one past \n.  It could be the
+	 * next header line after the tagger line, or it could be another
+	 * \n that marks the end of the headers.  We need to copy out the
+	 * remainder as is.
+	 */
+	if (cp < endp)
+		write_or_die(1, cp, endp - cp);
+}
+
+static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
+{
+	unsigned char sha1[20];
+	enum object_type type;
+	char *buf;
+	unsigned long size;
+	struct object_context obj_context;
+
+	if (get_sha1_with_context(obj_name, 0, sha1, &obj_context))
+		die("Not a valid object name %s", obj_name);
+
+	buf = NULL;
+	switch (opt) {
+	case 't':
+		type = sha1_object_info(sha1, NULL);
+		if (type > 0) {
+			printf("%s\n", typename(type));
+			return 0;
+		}
+		break;
+
+	case 's':
+		type = sha1_object_info(sha1, &size);
+		if (type > 0) {
+			printf("%lu\n", size);
+			return 0;
+		}
+		break;
+
+	case 'e':
+		return !has_sha1_file(sha1);
+
+	case 'p':
+		type = sha1_object_info(sha1, NULL);
+		if (type < 0)
+			die("Not a valid object name %s", obj_name);
+
+		/* custom pretty-print here */
+		if (type == OBJ_TREE) {
+			const char *ls_args[3] = { NULL };
+			ls_args[0] =  "ls-tree";
+			ls_args[1] =  obj_name;
+			return cmd_ls_tree(2, ls_args, NULL);
+		}
+
+		if (type == OBJ_BLOB)
+			return stream_blob_to_fd(1, sha1, NULL, 0);
+		buf = read_sha1_file(sha1, &type, &size);
+		if (!buf)
+			die("Cannot read object %s", obj_name);
+		if (type == OBJ_TAG) {
+			pprint_tag(sha1, buf, size);
+			return 0;
+		}
+
+		/* otherwise just spit out the data */
+		break;
+
+	case 'c':
+		if (!obj_context.path[0])
+			die("git cat-file --textconv %s: <object> must be <sha1:path>",
+			    obj_name);
+
+		if (!textconv_object(obj_context.path, obj_context.mode, sha1, &buf, &size))
+			die("git cat-file --textconv: unable to run textconv on %s",
+			    obj_name);
+		break;
+
+	case 0:
+		if (type_from_string(exp_type) == OBJ_BLOB) {
+			unsigned char blob_sha1[20];
+			if (sha1_object_info(sha1, NULL) == OBJ_TAG) {
+				enum object_type type;
+				unsigned long size;
+				char *buffer = read_sha1_file(sha1, &type, &size);
+				if (memcmp(buffer, "object ", 7) ||
+				    get_sha1_hex(buffer + 7, blob_sha1))
+					die("%s not a valid tag", sha1_to_hex(sha1));
+				free(buffer);
+			} else
+				hashcpy(blob_sha1, sha1);
+
+			if (sha1_object_info(blob_sha1, NULL) == OBJ_BLOB)
+				return stream_blob_to_fd(1, blob_sha1, NULL, 0);
+			/*
+			 * we attempted to dereference a tag to a blob
+			 * and failed; there may be new dereference
+			 * mechanisms this code is not aware of.
+			 * fall-back to the usual case.
+			 */
+		}
+		buf = read_object_with_reference(sha1, exp_type, &size, NULL);
+		break;
+
+	default:
+		die("git cat-file: unknown option: %s", exp_type);
+	}
+
+	if (!buf)
+		die("git cat-file %s: bad file", obj_name);
+
+	write_or_die(1, buf, size);
+	return 0;
+}
+
+static int batch_one_object(const char *obj_name, int print_contents)
+{
+	unsigned char sha1[20];
+	enum object_type type = 0;
+	unsigned long size;
+	void *contents = contents;
+
+	if (!obj_name)
+	   return 1;
+
+	if (get_sha1(obj_name, sha1)) {
+		printf("%s missing\n", obj_name);
+		fflush(stdout);
+		return 0;
+	}
+
+	if (print_contents == BATCH)
+		contents = read_sha1_file(sha1, &type, &size);
+	else
+		type = sha1_object_info(sha1, &size);
+
+	if (type <= 0) {
+		printf("%s missing\n", obj_name);
+		fflush(stdout);
+		if (print_contents == BATCH)
+			free(contents);
+		return 0;
+	}
+
+	printf("%s %s %lu\n", sha1_to_hex(sha1), typename(type), size);
+	fflush(stdout);
+
+	if (print_contents == BATCH) {
+		write_or_die(1, contents, size);
+		printf("\n");
+		fflush(stdout);
+		free(contents);
+	}
+
+	return 0;
+}
+
+static int batch_objects(int print_contents)
+{
+	struct strbuf buf = STRBUF_INIT;
+
+	while (strbuf_getline(&buf, stdin, '\n') != EOF) {
+		int error = batch_one_object(buf.buf, print_contents);
+		if (error)
+			return error;
+	}
+
+	return 0;
+}
+
+static const char * const cat_file_usage[] = {
+	"git cat-file (-t|-s|-e|-p|<type>|--textconv) <object>",
+	"git cat-file (--batch|--batch-check) < <list_of_objects>",
+	NULL
+};
+
+static int git_cat_file_config(const char *var, const char *value, void *cb)
+{
+	if (userdiff_config(var, value) < 0)
+		return -1;
+
+	return git_default_config(var, value, cb);
+}
+
+int cmd_cat_file(int argc, const char **argv, const char *prefix)
+{
+	int opt = 0, batch = 0;
+	const char *exp_type = NULL, *obj_name = NULL;
+
+	const struct option options[] = {
+		OPT_GROUP("<type> can be one of: blob, tree, commit, tag"),
+		OPT_SET_INT('t', NULL, &opt, "show object type", 't'),
+		OPT_SET_INT('s', NULL, &opt, "show object size", 's'),
+		OPT_SET_INT('e', NULL, &opt,
+			    "exit with zero when there's no error", 'e'),
+		OPT_SET_INT('p', NULL, &opt, "pretty-print object's content", 'p'),
+		OPT_SET_INT(0, "textconv", &opt,
+			    "for blob objects, run textconv on object's content", 'c'),
+		OPT_SET_INT(0, "batch", &batch,
+			    "show info and content of objects fed from the standard input",
+			    BATCH),
+		OPT_SET_INT(0, "batch-check", &batch,
+			    "show info about objects fed from the standard input",
+			    BATCH_CHECK),
+		OPT_END()
+	};
+
+	git_config(git_cat_file_config, NULL);
+
+	if (argc != 3 && argc != 2)
+		usage_with_options(cat_file_usage, options);
+
+	argc = parse_options(argc, argv, prefix, options, cat_file_usage, 0);
+
+	if (opt) {
+		if (argc == 1)
+			obj_name = argv[0];
+		else
+			usage_with_options(cat_file_usage, options);
+	}
+	if (!opt && !batch) {
+		if (argc == 2) {
+			exp_type = argv[0];
+			obj_name = argv[1];
+		} else
+			usage_with_options(cat_file_usage, options);
+	}
+	if (batch && (opt || argc)) {
+		usage_with_options(cat_file_usage, options);
+	}
+
+	if (batch)
+		return batch_objects(batch);
+
+	return cat_one_file(opt, exp_type, obj_name);
+}
diff --git a/builtin/check-attr.c b/builtin/check-attr.c
new file mode 100644
index 0000000..44c421e
--- /dev/null
+++ b/builtin/check-attr.c
@@ -0,0 +1,175 @@
+#include "builtin.h"
+#include "cache.h"
+#include "attr.h"
+#include "quote.h"
+#include "parse-options.h"
+
+static int all_attrs;
+static int cached_attrs;
+static int stdin_paths;
+static const char * const check_attr_usage[] = {
+"git check-attr [-a | --all | attr...] [--] pathname...",
+"git check-attr --stdin [-a | --all | attr...] < <list-of-paths>",
+NULL
+};
+
+static int null_term_line;
+
+static const struct option check_attr_options[] = {
+	OPT_BOOLEAN('a', "all", &all_attrs, "report all attributes set on file"),
+	OPT_BOOLEAN(0,  "cached", &cached_attrs, "use .gitattributes only from the index"),
+	OPT_BOOLEAN(0 , "stdin", &stdin_paths, "read file names from stdin"),
+	OPT_BOOLEAN('z', NULL, &null_term_line,
+		"input paths are terminated by a null character"),
+	OPT_END()
+};
+
+static void output_attr(int cnt, struct git_attr_check *check,
+	const char *file)
+{
+	int j;
+	for (j = 0; j < cnt; j++) {
+		const char *value = check[j].value;
+
+		if (ATTR_TRUE(value))
+			value = "set";
+		else if (ATTR_FALSE(value))
+			value = "unset";
+		else if (ATTR_UNSET(value))
+			value = "unspecified";
+
+		quote_c_style(file, NULL, stdout, 0);
+		printf(": %s: %s\n", git_attr_name(check[j].attr), value);
+	}
+}
+
+static void check_attr(const char *prefix, int cnt,
+	struct git_attr_check *check, const char *file)
+{
+	char *full_path =
+		prefix_path(prefix, prefix ? strlen(prefix) : 0, file);
+	if (check != NULL) {
+		if (git_check_attr(full_path, cnt, check))
+			die("git_check_attr died");
+		output_attr(cnt, check, file);
+	} else {
+		if (git_all_attrs(full_path, &cnt, &check))
+			die("git_all_attrs died");
+		output_attr(cnt, check, file);
+		free(check);
+	}
+	free(full_path);
+}
+
+static void check_attr_stdin_paths(const char *prefix, int cnt,
+	struct git_attr_check *check)
+{
+	struct strbuf buf, nbuf;
+	int line_termination = null_term_line ? 0 : '\n';
+
+	strbuf_init(&buf, 0);
+	strbuf_init(&nbuf, 0);
+	while (strbuf_getline(&buf, stdin, line_termination) != EOF) {
+		if (line_termination && buf.buf[0] == '"') {
+			strbuf_reset(&nbuf);
+			if (unquote_c_style(&nbuf, buf.buf, NULL))
+				die("line is badly quoted");
+			strbuf_swap(&buf, &nbuf);
+		}
+		check_attr(prefix, cnt, check, buf.buf);
+		maybe_flush_or_die(stdout, "attribute to stdout");
+	}
+	strbuf_release(&buf);
+	strbuf_release(&nbuf);
+}
+
+static NORETURN void error_with_usage(const char *msg)
+{
+	error("%s", msg);
+	usage_with_options(check_attr_usage, check_attr_options);
+}
+
+int cmd_check_attr(int argc, const char **argv, const char *prefix)
+{
+	struct git_attr_check *check;
+	int cnt, i, doubledash, filei;
+
+	git_config(git_default_config, NULL);
+
+	argc = parse_options(argc, argv, prefix, check_attr_options,
+			     check_attr_usage, PARSE_OPT_KEEP_DASHDASH);
+
+	if (read_cache() < 0) {
+		die("invalid cache");
+	}
+
+	if (cached_attrs)
+		git_attr_set_direction(GIT_ATTR_INDEX, NULL);
+
+	doubledash = -1;
+	for (i = 0; doubledash < 0 && i < argc; i++) {
+		if (!strcmp(argv[i], "--"))
+			doubledash = i;
+	}
+
+	/* Process --all and/or attribute arguments: */
+	if (all_attrs) {
+		if (doubledash >= 1)
+			error_with_usage("Attributes and --all both specified");
+
+		cnt = 0;
+		filei = doubledash + 1;
+	} else if (doubledash == 0) {
+		error_with_usage("No attribute specified");
+	} else if (doubledash < 0) {
+		if (!argc)
+			error_with_usage("No attribute specified");
+
+		if (stdin_paths) {
+			/* Treat all arguments as attribute names. */
+			cnt = argc;
+			filei = argc;
+		} else {
+			/* Treat exactly one argument as an attribute name. */
+			cnt = 1;
+			filei = 1;
+		}
+	} else {
+		cnt = doubledash;
+		filei = doubledash + 1;
+	}
+
+	/* Check file argument(s): */
+	if (stdin_paths) {
+		if (filei < argc)
+			error_with_usage("Can't specify files with --stdin");
+	} else {
+		if (filei >= argc)
+			error_with_usage("No file specified");
+	}
+
+	if (all_attrs) {
+		check = NULL;
+	} else {
+		check = xcalloc(cnt, sizeof(*check));
+		for (i = 0; i < cnt; i++) {
+			const char *name;
+			struct git_attr *a;
+			name = argv[i];
+			a = git_attr(name);
+			if (!a)
+				return error("%s: not a valid attribute name",
+					name);
+			check[i].attr = a;
+		}
+	}
+
+	if (stdin_paths)
+		check_attr_stdin_paths(prefix, cnt, check);
+	else {
+		for (i = filei; i < argc; i++)
+			check_attr(prefix, cnt, check, argv[i]);
+		maybe_flush_or_die(stdout, "attribute to stdout");
+	}
+	return 0;
+}
diff --git a/builtin/check-ref-format.c b/builtin/check-ref-format.c
new file mode 100644
index 0000000..28a7320
--- /dev/null
+++ b/builtin/check-ref-format.c
@@ -0,0 +1,88 @@
+/*
+ * GIT - The information manager from hell
+ */
+
+#include "cache.h"
+#include "refs.h"
+#include "builtin.h"
+#include "strbuf.h"
+
+static const char builtin_check_ref_format_usage[] =
+"git check-ref-format [--normalize] [options] <refname>\n"
+"   or: git check-ref-format --branch <branchname-shorthand>";
+
+/*
+ * Return a copy of refname but with leading slashes removed and runs
+ * of adjacent slashes replaced with single slashes.
+ *
+ * This function is similar to normalize_path_copy(), but stripped down
+ * to meet check_ref_format's simpler needs.
+ */
+static char *collapse_slashes(const char *refname)
+{
+	char *ret = xmalloc(strlen(refname) + 1);
+	char ch;
+	char prev = '/';
+	char *cp = ret;
+
+	while ((ch = *refname++) != '\0') {
+		if (prev == '/' && ch == prev)
+			continue;
+
+		*cp++ = ch;
+		prev = ch;
+	}
+	*cp = '\0';
+	return ret;
+}
+
+static int check_ref_format_branch(const char *arg)
+{
+	struct strbuf sb = STRBUF_INIT;
+	int nongit;
+
+	setup_git_directory_gently(&nongit);
+	if (strbuf_check_branch_ref(&sb, arg))
+		die("'%s' is not a valid branch name", arg);
+	printf("%s\n", sb.buf + 11);
+	return 0;
+}
+
+int cmd_check_ref_format(int argc, const char **argv, const char *prefix)
+{
+	int i;
+	int normalize = 0;
+	int flags = 0;
+	const char *refname;
+
+	if (argc == 2 && !strcmp(argv[1], "-h"))
+		usage(builtin_check_ref_format_usage);
+
+	if (argc == 3 && !strcmp(argv[1], "--branch"))
+		return check_ref_format_branch(argv[2]);
+
+	for (i = 1; i < argc && argv[i][0] == '-'; i++) {
+		if (!strcmp(argv[i], "--normalize") || !strcmp(argv[i], "--print"))
+			normalize = 1;
+		else if (!strcmp(argv[i], "--allow-onelevel"))
+			flags |= REFNAME_ALLOW_ONELEVEL;
+		else if (!strcmp(argv[i], "--no-allow-onelevel"))
+			flags &= ~REFNAME_ALLOW_ONELEVEL;
+		else if (!strcmp(argv[i], "--refspec-pattern"))
+			flags |= REFNAME_REFSPEC_PATTERN;
+		else
+			usage(builtin_check_ref_format_usage);
+	}
+	if (! (i == argc - 1))
+		usage(builtin_check_ref_format_usage);
+
+	refname = argv[i];
+	if (normalize)
+		refname = collapse_slashes(refname);
+	if (check_refname_format(refname, flags))
+		return 1;
+	if (normalize)
+		printf("%s\n", refname);
+
+	return 0;
+}
diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c
new file mode 100644
index 0000000..c16d82b
--- /dev/null
+++ b/builtin/checkout-index.c
@@ -0,0 +1,286 @@
+/*
+ * Check-out files from the "current cache directory"
+ *
+ * Copyright (C) 2005 Linus Torvalds
+ *
+ */
+#include "builtin.h"
+#include "cache.h"
+#include "quote.h"
+#include "cache-tree.h"
+#include "parse-options.h"
+
+#define CHECKOUT_ALL 4
+static int line_termination = '\n';
+static int checkout_stage; /* default to checkout stage0 */
+static int to_tempfile;
+static char topath[4][PATH_MAX + 1];
+
+static struct checkout state;
+
+static void write_tempfile_record(const char *name, int prefix_length)
+{
+	int i;
+
+	if (CHECKOUT_ALL == checkout_stage) {
+		for (i = 1; i < 4; i++) {
+			if (i > 1)
+				putchar(' ');
+			if (topath[i][0])
+				fputs(topath[i], stdout);
+			else
+				putchar('.');
+		}
+	} else
+		fputs(topath[checkout_stage], stdout);
+
+	putchar('\t');
+	write_name_quoted(name + prefix_length, stdout, line_termination);
+
+	for (i = 0; i < 4; i++) {
+		topath[i][0] = 0;
+	}
+}
+
+static int checkout_file(const char *name, int prefix_length)
+{
+	int namelen = strlen(name);
+	int pos = cache_name_pos(name, namelen);
+	int has_same_name = 0;
+	int did_checkout = 0;
+	int errs = 0;
+
+	if (pos < 0)
+		pos = -pos - 1;
+
+	while (pos < active_nr) {
+		struct cache_entry *ce = active_cache[pos];
+		if (ce_namelen(ce) != namelen ||
+		    memcmp(ce->name, name, namelen))
+			break;
+		has_same_name = 1;
+		pos++;
+		if (ce_stage(ce) != checkout_stage
+		    && (CHECKOUT_ALL != checkout_stage || !ce_stage(ce)))
+			continue;
+		did_checkout = 1;
+		if (checkout_entry(ce, &state,
+		    to_tempfile ? topath[ce_stage(ce)] : NULL) < 0)
+			errs++;
+	}
+
+	if (did_checkout) {
+		if (to_tempfile)
+			write_tempfile_record(name, prefix_length);
+		return errs > 0 ? -1 : 0;
+	}
+
+	if (!state.quiet) {
+		fprintf(stderr, "git checkout-index: %s ", name);
+		if (!has_same_name)
+			fprintf(stderr, "is not in the cache");
+		else if (checkout_stage)
+			fprintf(stderr, "does not exist at stage %d",
+				checkout_stage);
+		else
+			fprintf(stderr, "is unmerged");
+		fputc('\n', stderr);
+	}
+	return -1;
+}
+
+static void checkout_all(const char *prefix, int prefix_length)
+{
+	int i, errs = 0;
+	struct cache_entry *last_ce = NULL;
+
+	for (i = 0; i < active_nr ; i++) {
+		struct cache_entry *ce = active_cache[i];
+		if (ce_stage(ce) != checkout_stage
+		    && (CHECKOUT_ALL != checkout_stage || !ce_stage(ce)))
+			continue;
+		if (prefix && *prefix &&
+		    (ce_namelen(ce) <= prefix_length ||
+		     memcmp(prefix, ce->name, prefix_length)))
+			continue;
+		if (last_ce && to_tempfile) {
+			if (ce_namelen(last_ce) != ce_namelen(ce)
+			    || memcmp(last_ce->name, ce->name, ce_namelen(ce)))
+				write_tempfile_record(last_ce->name, prefix_length);
+		}
+		if (checkout_entry(ce, &state,
+		    to_tempfile ? topath[ce_stage(ce)] : NULL) < 0)
+			errs++;
+		last_ce = ce;
+	}
+	if (last_ce && to_tempfile)
+		write_tempfile_record(last_ce->name, prefix_length);
+	if (errs)
+		/* we have already done our error reporting.
+		 * exit with the same code as die().
+		 */
+		exit(128);
+}
+
+static const char * const builtin_checkout_index_usage[] = {
+	"git checkout-index [options] [--] [<file>...]",
+	NULL
+};
+
+static struct lock_file lock_file;
+
+static int option_parse_u(const struct option *opt,
+			      const char *arg, int unset)
+{
+	int *newfd = opt->value;
+
+	state.refresh_cache = 1;
+	if (*newfd < 0)
+		*newfd = hold_locked_index(&lock_file, 1);
+	return 0;
+}
+
+static int option_parse_z(const struct option *opt,
+			  const char *arg, int unset)
+{
+	if (unset)
+		line_termination = '\n';
+	else
+		line_termination = 0;
+	return 0;
+}
+
+static int option_parse_prefix(const struct option *opt,
+			       const char *arg, int unset)
+{
+	state.base_dir = arg;
+	state.base_dir_len = strlen(arg);
+	return 0;
+}
+
+static int option_parse_stage(const struct option *opt,
+			      const char *arg, int unset)
+{
+	if (!strcmp(arg, "all")) {
+		to_tempfile = 1;
+		checkout_stage = CHECKOUT_ALL;
+	} else {
+		int ch = arg[0];
+		if ('1' <= ch && ch <= '3')
+			checkout_stage = arg[0] - '0';
+		else
+			die("stage should be between 1 and 3 or all");
+	}
+	return 0;
+}
+
+int cmd_checkout_index(int argc, const char **argv, const char *prefix)
+{
+	int i;
+	int newfd = -1;
+	int all = 0;
+	int read_from_stdin = 0;
+	int prefix_length;
+	int force = 0, quiet = 0, not_new = 0;
+	struct option builtin_checkout_index_options[] = {
+		OPT_BOOLEAN('a', "all", &all,
+			"checks out all files in the index"),
+		OPT__FORCE(&force, "forces overwrite of existing files"),
+		OPT__QUIET(&quiet,
+			"no warning for existing files and files not in index"),
+		OPT_BOOLEAN('n', "no-create", &not_new,
+			"don't checkout new files"),
+		{ OPTION_CALLBACK, 'u', "index", &newfd, NULL,
+			"update stat information in the index file",
+			PARSE_OPT_NOARG, option_parse_u },
+		{ OPTION_CALLBACK, 'z', NULL, NULL, NULL,
+			"paths are separated with NUL character",
+			PARSE_OPT_NOARG, option_parse_z },
+		OPT_BOOLEAN(0, "stdin", &read_from_stdin,
+			"read list of paths from the standard input"),
+		OPT_BOOLEAN(0, "temp", &to_tempfile,
+			"write the content to temporary files"),
+		OPT_CALLBACK(0, "prefix", NULL, "string",
+			"when creating files, prepend <string>",
+			option_parse_prefix),
+		OPT_CALLBACK(0, "stage", NULL, NULL,
+			"copy out the files from named stage",
+			option_parse_stage),
+		OPT_END()
+	};
+
+	if (argc == 2 && !strcmp(argv[1], "-h"))
+		usage_with_options(builtin_checkout_index_usage,
+				   builtin_checkout_index_options);
+	git_config(git_default_config, NULL);
+	state.base_dir = "";
+	prefix_length = prefix ? strlen(prefix) : 0;
+
+	if (read_cache() < 0) {
+		die("invalid cache");
+	}
+
+	argc = parse_options(argc, argv, prefix, builtin_checkout_index_options,
+			builtin_checkout_index_usage, 0);
+	state.force = force;
+	state.quiet = quiet;
+	state.not_new = not_new;
+
+	if (state.base_dir_len || to_tempfile) {
+		/* when --prefix is specified we do not
+		 * want to update cache.
+		 */
+		if (state.refresh_cache) {
+			rollback_lock_file(&lock_file);
+			newfd = -1;
+		}
+		state.refresh_cache = 0;
+	}
+
+	/* Check out named files first */
+	for (i = 0; i < argc; i++) {
+		const char *arg = argv[i];
+		const char *p;
+
+		if (all)
+			die("git checkout-index: don't mix '--all' and explicit filenames");
+		if (read_from_stdin)
+			die("git checkout-index: don't mix '--stdin' and explicit filenames");
+		p = prefix_path(prefix, prefix_length, arg);
+		checkout_file(p, prefix_length);
+		if (p < arg || p > arg + strlen(arg))
+			free((char *)p);
+	}
+
+	if (read_from_stdin) {
+		struct strbuf buf = STRBUF_INIT, nbuf = STRBUF_INIT;
+
+		if (all)
+			die("git checkout-index: don't mix '--all' and '--stdin'");
+
+		while (strbuf_getline(&buf, stdin, line_termination) != EOF) {
+			const char *p;
+			if (line_termination && buf.buf[0] == '"') {
+				strbuf_reset(&nbuf);
+				if (unquote_c_style(&nbuf, buf.buf, NULL))
+					die("line is badly quoted");
+				strbuf_swap(&buf, &nbuf);
+			}
+			p = prefix_path(prefix, prefix_length, buf.buf);
+			checkout_file(p, prefix_length);
+			if (p < buf.buf || p > buf.buf + buf.len)
+				free((char *)p);
+		}
+		strbuf_release(&nbuf);
+		strbuf_release(&buf);
+	}
+
+	if (all)
+		checkout_all(prefix, prefix_length);
+
+	if (0 <= newfd &&
+	    (write_cache(newfd, active_cache, active_nr) ||
+	     commit_locked_index(&lock_file)))
+		die("Unable to write new index file");
+	return 0;
+}
diff --git a/builtin/checkout.c b/builtin/checkout.c
new file mode 100644
index 0000000..6acca75
--- /dev/null
+++ b/builtin/checkout.c
@@ -0,0 +1,1107 @@
+#include "cache.h"
+#include "builtin.h"
+#include "parse-options.h"
+#include "refs.h"
+#include "commit.h"
+#include "tree.h"
+#include "tree-walk.h"
+#include "cache-tree.h"
+#include "unpack-trees.h"
+#include "dir.h"
+#include "run-command.h"
+#include "merge-recursive.h"
+#include "branch.h"
+#include "diff.h"
+#include "revision.h"
+#include "remote.h"
+#include "blob.h"
+#include "xdiff-interface.h"
+#include "ll-merge.h"
+#include "resolve-undo.h"
+#include "submodule.h"
+#include "argv-array.h"
+
+static const char * const checkout_usage[] = {
+	"git checkout [options] <branch>",
+	"git checkout [options] [<branch>] -- <file>...",
+	NULL,
+};
+
+struct checkout_opts {
+	int quiet;
+	int merge;
+	int force;
+	int force_detach;
+	int writeout_stage;
+	int writeout_error;
+	int overwrite_ignore;
+
+	/* not set by parse_options */
+	int branch_exists;
+
+	const char *new_branch;
+	const char *new_branch_force;
+	const char *new_orphan_branch;
+	int new_branch_log;
+	enum branch_track track;
+	struct diff_options diff_options;
+};
+
+static int post_checkout_hook(struct commit *old, struct commit *new,
+			      int changed)
+{
+	return run_hook(NULL, "post-checkout",
+			sha1_to_hex(old ? old->object.sha1 : null_sha1),
+			sha1_to_hex(new ? new->object.sha1 : null_sha1),
+			changed ? "1" : "0", NULL);
+	/* "new" can be NULL when checking out from the index before
+	   a commit exists. */
+
+}
+
+static int update_some(const unsigned char *sha1, const char *base, int baselen,
+		const char *pathname, unsigned mode, int stage, void *context)
+{
+	int len;
+	struct cache_entry *ce;
+
+	if (S_ISDIR(mode))
+		return READ_TREE_RECURSIVE;
+
+	len = baselen + strlen(pathname);
+	ce = xcalloc(1, cache_entry_size(len));
+	hashcpy(ce->sha1, sha1);
+	memcpy(ce->name, base, baselen);
+	memcpy(ce->name + baselen, pathname, len - baselen);
+	ce->ce_flags = create_ce_flags(0) | CE_UPDATE;
+	ce->ce_namelen = len;
+	ce->ce_mode = create_ce_mode(mode);
+	add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
+	return 0;
+}
+
+static int read_tree_some(struct tree *tree, const char **pathspec)
+{
+	struct pathspec ps;
+	init_pathspec(&ps, pathspec);
+	read_tree_recursive(tree, "", 0, 0, &ps, update_some, NULL);
+	free_pathspec(&ps);
+
+	/* update the index with the given tree's info
+	 * for all args, expanding wildcards, and exit
+	 * with any non-zero return code.
+	 */
+	return 0;
+}
+
+static int skip_same_name(struct cache_entry *ce, int pos)
+{
+	while (++pos < active_nr &&
+	       !strcmp(active_cache[pos]->name, ce->name))
+		; /* skip */
+	return pos;
+}
+
+static int check_stage(int stage, struct cache_entry *ce, int pos)
+{
+	while (pos < active_nr &&
+	       !strcmp(active_cache[pos]->name, ce->name)) {
+		if (ce_stage(active_cache[pos]) == stage)
+			return 0;
+		pos++;
+	}
+	if (stage == 2)
+		return error(_("path '%s' does not have our version"), ce->name);
+	else
+		return error(_("path '%s' does not have their version"), ce->name);
+}
+
+static int check_stages(unsigned stages, struct cache_entry *ce, int pos)
+{
+	unsigned seen = 0;
+	const char *name = ce->name;
+
+	while (pos < active_nr) {
+		ce = active_cache[pos];
+		if (strcmp(name, ce->name))
+			break;
+		seen |= (1 << ce_stage(ce));
+		pos++;
+	}
+	if ((stages & seen) != stages)
+		return error(_("path '%s' does not have all necessary versions"),
+			     name);
+	return 0;
+}
+
+static int checkout_stage(int stage, struct cache_entry *ce, int pos,
+			  struct checkout *state)
+{
+	while (pos < active_nr &&
+	       !strcmp(active_cache[pos]->name, ce->name)) {
+		if (ce_stage(active_cache[pos]) == stage)
+			return checkout_entry(active_cache[pos], state, NULL);
+		pos++;
+	}
+	if (stage == 2)
+		return error(_("path '%s' does not have our version"), ce->name);
+	else
+		return error(_("path '%s' does not have their version"), ce->name);
+}
+
+static int checkout_merged(int pos, struct checkout *state)
+{
+	struct cache_entry *ce = active_cache[pos];
+	const char *path = ce->name;
+	mmfile_t ancestor, ours, theirs;
+	int status;
+	unsigned char sha1[20];
+	mmbuffer_t result_buf;
+	unsigned char threeway[3][20];
+	unsigned mode = 0;
+
+	memset(threeway, 0, sizeof(threeway));
+	while (pos < active_nr) {
+		int stage;
+		stage = ce_stage(ce);
+		if (!stage || strcmp(path, ce->name))
+			break;
+		hashcpy(threeway[stage - 1], ce->sha1);
+		if (stage == 2)
+			mode = create_ce_mode(ce->ce_mode);
+		pos++;
+		ce = active_cache[pos];
+	}
+	if (is_null_sha1(threeway[1]) || is_null_sha1(threeway[2]))
+		return error(_("path '%s' does not have necessary versions"), path);
+
+	read_mmblob(&ancestor, threeway[0]);
+	read_mmblob(&ours, threeway[1]);
+	read_mmblob(&theirs, threeway[2]);
+
+	/*
+	 * NEEDSWORK: re-create conflicts from merges with
+	 * merge.renormalize set, too
+	 */
+	status = ll_merge(&result_buf, path, &ancestor, "base",
+			  &ours, "ours", &theirs, "theirs", NULL);
+	free(ancestor.ptr);
+	free(ours.ptr);
+	free(theirs.ptr);
+	if (status < 0 || !result_buf.ptr) {
+		free(result_buf.ptr);
+		return error(_("path '%s': cannot merge"), path);
+	}
+
+	/*
+	 * NEEDSWORK:
+	 * There is absolutely no reason to write this as a blob object
+	 * and create a phony cache entry just to leak.  This hack is
+	 * primarily to get to the write_entry() machinery that massages
+	 * the contents to work-tree format and writes out which only
+	 * allows it for a cache entry.  The code in write_entry() needs
+	 * to be refactored to allow us to feed a <buffer, size, mode>
+	 * instead of a cache entry.  Such a refactoring would help
+	 * merge_recursive as well (it also writes the merge result to the
+	 * object database even when it may contain conflicts).
+	 */
+	if (write_sha1_file(result_buf.ptr, result_buf.size,
+			    blob_type, sha1))
+		die(_("Unable to add merge result for '%s'"), path);
+	ce = make_cache_entry(mode, sha1, path, 2, 0);
+	if (!ce)
+		die(_("make_cache_entry failed for path '%s'"), path);
+	status = checkout_entry(ce, state, NULL);
+	return status;
+}
+
+static int checkout_paths(struct tree *source_tree, const char **pathspec,
+			  const char *prefix, struct checkout_opts *opts)
+{
+	int pos;
+	struct checkout state;
+	static char *ps_matched;
+	unsigned char rev[20];
+	int flag;
+	struct commit *head;
+	int errs = 0;
+	int stage = opts->writeout_stage;
+	int merge = opts->merge;
+	int newfd;
+	struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
+
+	newfd = hold_locked_index(lock_file, 1);
+	if (read_cache_preload(pathspec) < 0)
+		return error(_("corrupt index file"));
+
+	if (source_tree)
+		read_tree_some(source_tree, pathspec);
+
+	for (pos = 0; pathspec[pos]; pos++)
+		;
+	ps_matched = xcalloc(1, pos);
+
+	for (pos = 0; pos < active_nr; pos++) {
+		struct cache_entry *ce = active_cache[pos];
+		if (source_tree && !(ce->ce_flags & CE_UPDATE))
+			continue;
+		match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, ps_matched);
+	}
+
+	if (report_path_error(ps_matched, pathspec, prefix))
+		return 1;
+
+	/* "checkout -m path" to recreate conflicted state */
+	if (opts->merge)
+		unmerge_cache(pathspec);
+
+	/* Any unmerged paths? */
+	for (pos = 0; pos < active_nr; pos++) {
+		struct cache_entry *ce = active_cache[pos];
+		if (match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL)) {
+			if (!ce_stage(ce))
+				continue;
+			if (opts->force) {
+				warning(_("path '%s' is unmerged"), ce->name);
+			} else if (stage) {
+				errs |= check_stage(stage, ce, pos);
+			} else if (opts->merge) {
+				errs |= check_stages((1<<2) | (1<<3), ce, pos);
+			} else {
+				errs = 1;
+				error(_("path '%s' is unmerged"), ce->name);
+			}
+			pos = skip_same_name(ce, pos) - 1;
+		}
+	}
+	if (errs)
+		return 1;
+
+	/* Now we are committed to check them out */
+	memset(&state, 0, sizeof(state));
+	state.force = 1;
+	state.refresh_cache = 1;
+	for (pos = 0; pos < active_nr; pos++) {
+		struct cache_entry *ce = active_cache[pos];
+		if (source_tree && !(ce->ce_flags & CE_UPDATE))
+			continue;
+		if (match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL)) {
+			if (!ce_stage(ce)) {
+				errs |= checkout_entry(ce, &state, NULL);
+				continue;
+			}
+			if (stage)
+				errs |= checkout_stage(stage, ce, pos, &state);
+			else if (merge)
+				errs |= checkout_merged(pos, &state);
+			pos = skip_same_name(ce, pos) - 1;
+		}
+	}
+
+	if (write_cache(newfd, active_cache, active_nr) ||
+	    commit_locked_index(lock_file))
+		die(_("unable to write new index file"));
+
+	read_ref_full("HEAD", rev, 0, &flag);
+	head = lookup_commit_reference_gently(rev, 1);
+
+	errs |= post_checkout_hook(head, head, 0);
+	return errs;
+}
+
+static void show_local_changes(struct object *head, struct diff_options *opts)
+{
+	struct rev_info rev;
+	/* I think we want full paths, even if we're in a subdirectory. */
+	init_revisions(&rev, NULL);
+	rev.diffopt.flags = opts->flags;
+	rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS;
+	if (diff_setup_done(&rev.diffopt) < 0)
+		die(_("diff_setup_done failed"));
+	add_pending_object(&rev, head, NULL);
+	run_diff_index(&rev, 0);
+}
+
+static void describe_detached_head(const char *msg, struct commit *commit)
+{
+	struct strbuf sb = STRBUF_INIT;
+	parse_commit(commit);
+	pp_commit_easy(CMIT_FMT_ONELINE, commit, &sb);
+	fprintf(stderr, "%s %s... %s\n", msg,
+		find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV), sb.buf);
+	strbuf_release(&sb);
+}
+
+static int reset_tree(struct tree *tree, struct checkout_opts *o, int worktree)
+{
+	struct unpack_trees_options opts;
+	struct tree_desc tree_desc;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.head_idx = -1;
+	opts.update = worktree;
+	opts.skip_unmerged = !worktree;
+	opts.reset = 1;
+	opts.merge = 1;
+	opts.fn = oneway_merge;
+	opts.verbose_update = !o->quiet && isatty(2);
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+	parse_tree(tree);
+	init_tree_desc(&tree_desc, tree->buffer, tree->size);
+	switch (unpack_trees(1, &tree_desc, &opts)) {
+	case -2:
+		o->writeout_error = 1;
+		/*
+		 * We return 0 nevertheless, as the index is all right
+		 * and more importantly we have made best efforts to
+		 * update paths in the work tree, and we cannot revert
+		 * them.
+		 */
+	case 0:
+		return 0;
+	default:
+		return 128;
+	}
+}
+
+struct branch_info {
+	const char *name; /* The short name used */
+	const char *path; /* The full name of a real branch */
+	struct commit *commit; /* The named commit */
+};
+
+static void setup_branch_path(struct branch_info *branch)
+{
+	struct strbuf buf = STRBUF_INIT;
+
+	strbuf_branchname(&buf, branch->name);
+	if (strcmp(buf.buf, branch->name))
+		branch->name = xstrdup(buf.buf);
+	strbuf_splice(&buf, 0, 0, "refs/heads/", 11);
+	branch->path = strbuf_detach(&buf, NULL);
+}
+
+static int merge_working_tree(struct checkout_opts *opts,
+			      struct branch_info *old, struct branch_info *new)
+{
+	int ret;
+	struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
+	int newfd = hold_locked_index(lock_file, 1);
+
+	if (read_cache_preload(NULL) < 0)
+		return error(_("corrupt index file"));
+
+	resolve_undo_clear();
+	if (opts->force) {
+		ret = reset_tree(new->commit->tree, opts, 1);
+		if (ret)
+			return ret;
+	} else {
+		struct tree_desc trees[2];
+		struct tree *tree;
+		struct unpack_trees_options topts;
+
+		memset(&topts, 0, sizeof(topts));
+		topts.head_idx = -1;
+		topts.src_index = &the_index;
+		topts.dst_index = &the_index;
+
+		setup_unpack_trees_porcelain(&topts, "checkout");
+
+		refresh_cache(REFRESH_QUIET);
+
+		if (unmerged_cache()) {
+			error(_("you need to resolve your current index first"));
+			return 1;
+		}
+
+		/* 2-way merge to the new branch */
+		topts.initial_checkout = is_cache_unborn();
+		topts.update = 1;
+		topts.merge = 1;
+		topts.gently = opts->merge && old->commit;
+		topts.verbose_update = !opts->quiet && isatty(2);
+		topts.fn = twoway_merge;
+		if (opts->overwrite_ignore) {
+			topts.dir = xcalloc(1, sizeof(*topts.dir));
+			topts.dir->flags |= DIR_SHOW_IGNORED;
+			setup_standard_excludes(topts.dir);
+		}
+		tree = parse_tree_indirect(old->commit ?
+					   old->commit->object.sha1 :
+					   EMPTY_TREE_SHA1_BIN);
+		init_tree_desc(&trees[0], tree->buffer, tree->size);
+		tree = parse_tree_indirect(new->commit->object.sha1);
+		init_tree_desc(&trees[1], tree->buffer, tree->size);
+
+		ret = unpack_trees(2, trees, &topts);
+		if (ret == -1) {
+			/*
+			 * Unpack couldn't do a trivial merge; either
+			 * give up or do a real merge, depending on
+			 * whether the merge flag was used.
+			 */
+			struct tree *result;
+			struct tree *work;
+			struct merge_options o;
+			if (!opts->merge)
+				return 1;
+
+			/*
+			 * Without old->commit, the below is the same as
+			 * the two-tree unpack we already tried and failed.
+			 */
+			if (!old->commit)
+				return 1;
+
+			/* Do more real merge */
+
+			/*
+			 * We update the index fully, then write the
+			 * tree from the index, then merge the new
+			 * branch with the current tree, with the old
+			 * branch as the base. Then we reset the index
+			 * (but not the working tree) to the new
+			 * branch, leaving the working tree as the
+			 * merged version, but skipping unmerged
+			 * entries in the index.
+			 */
+
+			add_files_to_cache(NULL, NULL, 0);
+			/*
+			 * NEEDSWORK: carrying over local changes
+			 * when branches have different end-of-line
+			 * normalization (or clean+smudge rules) is
+			 * a pain; plumb in an option to set
+			 * o.renormalize?
+			 */
+			init_merge_options(&o);
+			o.verbosity = 0;
+			work = write_tree_from_memory(&o);
+
+			ret = reset_tree(new->commit->tree, opts, 1);
+			if (ret)
+				return ret;
+			o.ancestor = old->name;
+			o.branch1 = new->name;
+			o.branch2 = "local";
+			merge_trees(&o, new->commit->tree, work,
+				old->commit->tree, &result);
+			ret = reset_tree(new->commit->tree, opts, 0);
+			if (ret)
+				return ret;
+		}
+	}
+
+	if (write_cache(newfd, active_cache, active_nr) ||
+	    commit_locked_index(lock_file))
+		die(_("unable to write new index file"));
+
+	if (!opts->force && !opts->quiet)
+		show_local_changes(&new->commit->object, &opts->diff_options);
+
+	return 0;
+}
+
+static void report_tracking(struct branch_info *new)
+{
+	struct strbuf sb = STRBUF_INIT;
+	struct branch *branch = branch_get(new->name);
+
+	if (!format_tracking_info(branch, &sb))
+		return;
+	fputs(sb.buf, stdout);
+	strbuf_release(&sb);
+}
+
+static void update_refs_for_switch(struct checkout_opts *opts,
+				   struct branch_info *old,
+				   struct branch_info *new)
+{
+	struct strbuf msg = STRBUF_INIT;
+	const char *old_desc;
+	if (opts->new_branch) {
+		if (opts->new_orphan_branch) {
+			if (opts->new_branch_log && !log_all_ref_updates) {
+				int temp;
+				char log_file[PATH_MAX];
+				char *ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch);
+
+				temp = log_all_ref_updates;
+				log_all_ref_updates = 1;
+				if (log_ref_setup(ref_name, log_file, sizeof(log_file))) {
+					fprintf(stderr, _("Can not do reflog for '%s'\n"),
+					    opts->new_orphan_branch);
+					log_all_ref_updates = temp;
+					return;
+				}
+				log_all_ref_updates = temp;
+			}
+		}
+		else
+			create_branch(old->name, opts->new_branch, new->name,
+				      opts->new_branch_force ? 1 : 0,
+				      opts->new_branch_log,
+				      opts->new_branch_force ? 1 : 0,
+				      opts->quiet,
+				      opts->track);
+		new->name = opts->new_branch;
+		setup_branch_path(new);
+	}
+
+	old_desc = old->name;
+	if (!old_desc && old->commit)
+		old_desc = sha1_to_hex(old->commit->object.sha1);
+	strbuf_addf(&msg, "checkout: moving from %s to %s",
+		    old_desc ? old_desc : "(invalid)", new->name);
+
+	if (!strcmp(new->name, "HEAD") && !new->path && !opts->force_detach) {
+		/* Nothing to do. */
+	} else if (opts->force_detach || !new->path) {	/* No longer on any branch. */
+		update_ref(msg.buf, "HEAD", new->commit->object.sha1, NULL,
+			   REF_NODEREF, DIE_ON_ERR);
+		if (!opts->quiet) {
+			if (old->path && advice_detached_head)
+				detach_advice(new->name);
+			describe_detached_head(_("HEAD is now at"), new->commit);
+		}
+	} else if (new->path) {	/* Switch branches. */
+		create_symref("HEAD", new->path, msg.buf);
+		if (!opts->quiet) {
+			if (old->path && !strcmp(new->path, old->path)) {
+				if (opts->new_branch_force)
+					fprintf(stderr, _("Reset branch '%s'\n"),
+						new->name);
+				else
+					fprintf(stderr, _("Already on '%s'\n"),
+						new->name);
+			} else if (opts->new_branch) {
+				if (opts->branch_exists)
+					fprintf(stderr, _("Switched to and reset branch '%s'\n"), new->name);
+				else
+					fprintf(stderr, _("Switched to a new branch '%s'\n"), new->name);
+			} else {
+				fprintf(stderr, _("Switched to branch '%s'\n"),
+					new->name);
+			}
+		}
+		if (old->path && old->name) {
+			char log_file[PATH_MAX], ref_file[PATH_MAX];
+
+			git_snpath(log_file, sizeof(log_file), "logs/%s", old->path);
+			git_snpath(ref_file, sizeof(ref_file), "%s", old->path);
+			if (!file_exists(ref_file) && file_exists(log_file))
+				remove_path(log_file);
+		}
+	}
+	remove_branch_state();
+	strbuf_release(&msg);
+	if (!opts->quiet &&
+	    (new->path || (!opts->force_detach && !strcmp(new->name, "HEAD"))))
+		report_tracking(new);
+}
+
+static int add_pending_uninteresting_ref(const char *refname,
+					 const unsigned char *sha1,
+					 int flags, void *cb_data)
+{
+	add_pending_sha1(cb_data, refname, sha1, flags | UNINTERESTING);
+	return 0;
+}
+
+static void describe_one_orphan(struct strbuf *sb, struct commit *commit)
+{
+	parse_commit(commit);
+	strbuf_addstr(sb, "  ");
+	strbuf_addstr(sb,
+		find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));
+	strbuf_addch(sb, ' ');
+	pp_commit_easy(CMIT_FMT_ONELINE, commit, sb);
+	strbuf_addch(sb, '\n');
+}
+
+#define ORPHAN_CUTOFF 4
+static void suggest_reattach(struct commit *commit, struct rev_info *revs)
+{
+	struct commit *c, *last = NULL;
+	struct strbuf sb = STRBUF_INIT;
+	int lost = 0;
+	while ((c = get_revision(revs)) != NULL) {
+		if (lost < ORPHAN_CUTOFF)
+			describe_one_orphan(&sb, c);
+		last = c;
+		lost++;
+	}
+	if (ORPHAN_CUTOFF < lost) {
+		int more = lost - ORPHAN_CUTOFF;
+		if (more == 1)
+			describe_one_orphan(&sb, last);
+		else
+			strbuf_addf(&sb, _(" ... and %d more.\n"), more);
+	}
+
+	fprintf(stderr,
+		Q_(
+		/* The singular version */
+		"Warning: you are leaving %d commit behind, "
+		"not connected to\n"
+		"any of your branches:\n\n"
+		"%s\n",
+		/* The plural version */
+		"Warning: you are leaving %d commits behind, "
+		"not connected to\n"
+		"any of your branches:\n\n"
+		"%s\n",
+		/* Give ngettext() the count */
+		lost),
+		lost,
+		sb.buf);
+	strbuf_release(&sb);
+
+	if (advice_detached_head)
+		fprintf(stderr,
+			_(
+			"If you want to keep them by creating a new branch, "
+			"this may be a good time\nto do so with:\n\n"
+			" git branch new_branch_name %s\n\n"),
+			sha1_to_hex(commit->object.sha1));
+}
+
+/*
+ * We are about to leave commit that was at the tip of a detached
+ * HEAD.  If it is not reachable from any ref, this is the last chance
+ * for the user to do so without resorting to reflog.
+ */
+static void orphaned_commit_warning(struct commit *old, struct commit *new)
+{
+	struct rev_info revs;
+	struct object *object = &old->object;
+	struct object_array refs;
+
+	init_revisions(&revs, NULL);
+	setup_revisions(0, NULL, &revs, NULL);
+
+	object->flags &= ~UNINTERESTING;
+	add_pending_object(&revs, object, sha1_to_hex(object->sha1));
+
+	for_each_ref(add_pending_uninteresting_ref, &revs);
+	add_pending_sha1(&revs, "HEAD", new->object.sha1, UNINTERESTING);
+
+	refs = revs.pending;
+	revs.leak_pending = 1;
+
+	if (prepare_revision_walk(&revs))
+		die(_("internal error in revision walk"));
+	if (!(old->object.flags & UNINTERESTING))
+		suggest_reattach(old, &revs);
+	else
+		describe_detached_head(_("Previous HEAD position was"), old);
+
+	clear_commit_marks_for_object_array(&refs, ALL_REV_FLAGS);
+	free(refs.objects);
+}
+
+static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
+{
+	int ret = 0;
+	struct branch_info old;
+	void *path_to_free;
+	unsigned char rev[20];
+	int flag;
+	memset(&old, 0, sizeof(old));
+	old.path = path_to_free = resolve_refdup("HEAD", rev, 0, &flag);
+	old.commit = lookup_commit_reference_gently(rev, 1);
+	if (!(flag & REF_ISSYMREF))
+		old.path = NULL;
+
+	if (old.path && !prefixcmp(old.path, "refs/heads/"))
+		old.name = old.path + strlen("refs/heads/");
+
+	if (!new->name) {
+		new->name = "HEAD";
+		new->commit = old.commit;
+		if (!new->commit)
+			die(_("You are on a branch yet to be born"));
+		parse_commit(new->commit);
+	}
+
+	ret = merge_working_tree(opts, &old, new);
+	if (ret) {
+		free(path_to_free);
+		return ret;
+	}
+
+	if (!opts->quiet && !old.path && old.commit && new->commit != old.commit)
+		orphaned_commit_warning(old.commit, new->commit);
+
+	update_refs_for_switch(opts, &old, new);
+
+	ret = post_checkout_hook(old.commit, new->commit, 1);
+	free(path_to_free);
+	return ret || opts->writeout_error;
+}
+
+static int git_checkout_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, "diff.ignoresubmodules")) {
+		struct checkout_opts *opts = cb;
+		handle_ignore_submodules_arg(&opts->diff_options, value);
+		return 0;
+	}
+
+	if (!prefixcmp(var, "submodule."))
+		return parse_submodule_config_option(var, value);
+
+	return git_xmerge_config(var, value, NULL);
+}
+
+static int interactive_checkout(const char *revision, const char **pathspec,
+				struct checkout_opts *opts)
+{
+	return run_add_interactive(revision, "--patch=checkout", pathspec);
+}
+
+struct tracking_name_data {
+	const char *name;
+	char *remote;
+	int unique;
+};
+
+static int check_tracking_name(const char *refname, const unsigned char *sha1,
+			       int flags, void *cb_data)
+{
+	struct tracking_name_data *cb = cb_data;
+	const char *slash;
+
+	if (prefixcmp(refname, "refs/remotes/"))
+		return 0;
+	slash = strchr(refname + 13, '/');
+	if (!slash || strcmp(slash + 1, cb->name))
+		return 0;
+	if (cb->remote) {
+		cb->unique = 0;
+		return 0;
+	}
+	cb->remote = xstrdup(refname);
+	return 0;
+}
+
+static const char *unique_tracking_name(const char *name)
+{
+	struct tracking_name_data cb_data = { NULL, NULL, 1 };
+	cb_data.name = name;
+	for_each_ref(check_tracking_name, &cb_data);
+	if (cb_data.unique)
+		return cb_data.remote;
+	free(cb_data.remote);
+	return NULL;
+}
+
+static int parse_branchname_arg(int argc, const char **argv,
+				int dwim_new_local_branch_ok,
+				struct branch_info *new,
+				struct tree **source_tree,
+				unsigned char rev[20],
+				const char **new_branch)
+{
+	int argcount = 0;
+	unsigned char branch_rev[20];
+	const char *arg;
+	int has_dash_dash;
+
+	/*
+	 * case 1: git checkout <ref> -- [<paths>]
+	 *
+	 *   <ref> must be a valid tree, everything after the '--' must be
+	 *   a path.
+	 *
+	 * case 2: git checkout -- [<paths>]
+	 *
+	 *   everything after the '--' must be paths.
+	 *
+	 * case 3: git checkout <something> [<paths>]
+	 *
+	 *   With no paths, if <something> is a commit, that is to
+	 *   switch to the branch or detach HEAD at it.  As a special case,
+	 *   if <something> is A...B (missing A or B means HEAD but you can
+	 *   omit at most one side), and if there is a unique merge base
+	 *   between A and B, A...B names that merge base.
+	 *
+	 *   With no paths, if <something> is _not_ a commit, no -t nor -b
+	 *   was given, and there is a tracking branch whose name is
+	 *   <something> in one and only one remote, then this is a short-hand
+	 *   to fork local <something> from that remote-tracking branch.
+	 *
+	 *   Otherwise <something> shall not be ambiguous.
+	 *   - If it's *only* a reference, treat it like case (1).
+	 *   - If it's only a path, treat it like case (2).
+	 *   - else: fail.
+	 *
+	 */
+	if (!argc)
+		return 0;
+
+	if (!strcmp(argv[0], "--"))	/* case (2) */
+		return 1;
+
+	arg = argv[0];
+	has_dash_dash = (argc > 1) && !strcmp(argv[1], "--");
+
+	if (!strcmp(arg, "-"))
+		arg = "@{-1}";
+
+	if (get_sha1_mb(arg, rev)) {
+		if (has_dash_dash)          /* case (1) */
+			die(_("invalid reference: %s"), arg);
+		if (dwim_new_local_branch_ok &&
+		    !check_filename(NULL, arg) &&
+		    argc == 1) {
+			const char *remote = unique_tracking_name(arg);
+			if (!remote || get_sha1(remote, rev))
+				return argcount;
+			*new_branch = arg;
+			arg = remote;
+			/* DWIMmed to create local branch */
+		} else {
+			return argcount;
+		}
+	}
+
+	/* we can't end up being in (2) anymore, eat the argument */
+	argcount++;
+	argv++;
+	argc--;
+
+	new->name = arg;
+	setup_branch_path(new);
+
+	if (!check_refname_format(new->path, 0) &&
+	    !read_ref(new->path, branch_rev))
+		hashcpy(rev, branch_rev);
+	else
+		new->path = NULL; /* not an existing branch */
+
+	new->commit = lookup_commit_reference_gently(rev, 1);
+	if (!new->commit) {
+		/* not a commit */
+		*source_tree = parse_tree_indirect(rev);
+	} else {
+		parse_commit(new->commit);
+		*source_tree = new->commit->tree;
+	}
+
+	if (!*source_tree)                   /* case (1): want a tree */
+		die(_("reference is not a tree: %s"), arg);
+	if (!has_dash_dash) {/* case (3 -> 1) */
+		/*
+		 * Do not complain the most common case
+		 *	git checkout branch
+		 * even if there happen to be a file called 'branch';
+		 * it would be extremely annoying.
+		 */
+		if (argc)
+			verify_non_filename(NULL, arg);
+	} else {
+		argcount++;
+		argv++;
+		argc--;
+	}
+
+	return argcount;
+}
+
+static int switch_unborn_to_new_branch(struct checkout_opts *opts)
+{
+	int status;
+	struct strbuf branch_ref = STRBUF_INIT;
+
+	if (!opts->new_branch)
+		die(_("You are on a branch yet to be born"));
+	strbuf_addf(&branch_ref, "refs/heads/%s", opts->new_branch);
+	status = create_symref("HEAD", branch_ref.buf, "checkout -b");
+	strbuf_release(&branch_ref);
+	return status;
+}
+
+int cmd_checkout(int argc, const char **argv, const char *prefix)
+{
+	struct checkout_opts opts;
+	unsigned char rev[20];
+	struct branch_info new;
+	struct tree *source_tree = NULL;
+	char *conflict_style = NULL;
+	int patch_mode = 0;
+	int dwim_new_local_branch = 1;
+	struct option options[] = {
+		OPT__QUIET(&opts.quiet, "suppress progress reporting"),
+		OPT_STRING('b', NULL, &opts.new_branch, "branch",
+			   "create and checkout a new branch"),
+		OPT_STRING('B', NULL, &opts.new_branch_force, "branch",
+			   "create/reset and checkout a branch"),
+		OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "create reflog for new branch"),
+		OPT_BOOLEAN(0, "detach", &opts.force_detach, "detach the HEAD at named commit"),
+		OPT_SET_INT('t', "track",  &opts.track, "set upstream info for new branch",
+			BRANCH_TRACK_EXPLICIT),
+		OPT_STRING(0, "orphan", &opts.new_orphan_branch, "new branch", "new unparented branch"),
+		OPT_SET_INT('2', "ours", &opts.writeout_stage, "checkout our version for unmerged files",
+			    2),
+		OPT_SET_INT('3', "theirs", &opts.writeout_stage, "checkout their version for unmerged files",
+			    3),
+		OPT__FORCE(&opts.force, "force checkout (throw away local modifications)"),
+		OPT_BOOLEAN('m', "merge", &opts.merge, "perform a 3-way merge with the new branch"),
+		OPT_BOOLEAN(0, "overwrite-ignore", &opts.overwrite_ignore, "update ignored files (default)"),
+		OPT_STRING(0, "conflict", &conflict_style, "style",
+			   "conflict style (merge or diff3)"),
+		OPT_BOOLEAN('p', "patch", &patch_mode, "select hunks interactively"),
+		{ OPTION_BOOLEAN, 0, "guess", &dwim_new_local_branch, NULL,
+		  "second guess 'git checkout no-such-branch'",
+		  PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
+		OPT_END(),
+	};
+
+	memset(&opts, 0, sizeof(opts));
+	memset(&new, 0, sizeof(new));
+	opts.overwrite_ignore = 1;
+
+	gitmodules_config();
+	git_config(git_checkout_config, &opts);
+
+	opts.track = BRANCH_TRACK_UNSPECIFIED;
+
+	argc = parse_options(argc, argv, prefix, options, checkout_usage,
+			     PARSE_OPT_KEEP_DASHDASH);
+
+	/* we can assume from now on new_branch = !new_branch_force */
+	if (opts.new_branch && opts.new_branch_force)
+		die(_("-B cannot be used with -b"));
+
+	/* copy -B over to -b, so that we can just check the latter */
+	if (opts.new_branch_force)
+		opts.new_branch = opts.new_branch_force;
+
+	if (patch_mode && (opts.track > 0 || opts.new_branch
+			   || opts.new_branch_log || opts.merge || opts.force
+			   || opts.force_detach))
+		die (_("--patch is incompatible with all other options"));
+
+	if (opts.force_detach && (opts.new_branch || opts.new_orphan_branch))
+		die(_("--detach cannot be used with -b/-B/--orphan"));
+	if (opts.force_detach && 0 < opts.track)
+		die(_("--detach cannot be used with -t"));
+
+	/* --track without -b should DWIM */
+	if (0 < opts.track && !opts.new_branch) {
+		const char *argv0 = argv[0];
+		if (!argc || !strcmp(argv0, "--"))
+			die (_("--track needs a branch name"));
+		if (!prefixcmp(argv0, "refs/"))
+			argv0 += 5;
+		if (!prefixcmp(argv0, "remotes/"))
+			argv0 += 8;
+		argv0 = strchr(argv0, '/');
+		if (!argv0 || !argv0[1])
+			die (_("Missing branch name; try -b"));
+		opts.new_branch = argv0 + 1;
+	}
+
+	if (opts.new_orphan_branch) {
+		if (opts.new_branch)
+			die(_("--orphan and -b|-B are mutually exclusive"));
+		if (opts.track > 0)
+			die(_("--orphan cannot be used with -t"));
+		opts.new_branch = opts.new_orphan_branch;
+	}
+
+	if (conflict_style) {
+		opts.merge = 1; /* implied */
+		git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
+	}
+
+	if (opts.force && opts.merge)
+		die(_("git checkout: -f and -m are incompatible"));
+
+	/*
+	 * Extract branch name from command line arguments, so
+	 * all that is left is pathspecs.
+	 *
+	 * Handle
+	 *
+	 *  1) git checkout <tree> -- [<paths>]
+	 *  2) git checkout -- [<paths>]
+	 *  3) git checkout <something> [<paths>]
+	 *
+	 * including "last branch" syntax and DWIM-ery for names of
+	 * remote branches, erroring out for invalid or ambiguous cases.
+	 */
+	if (argc) {
+		int dwim_ok =
+			!patch_mode &&
+			dwim_new_local_branch &&
+			opts.track == BRANCH_TRACK_UNSPECIFIED &&
+			!opts.new_branch;
+		int n = parse_branchname_arg(argc, argv, dwim_ok,
+				&new, &source_tree, rev, &opts.new_branch);
+		argv += n;
+		argc -= n;
+	}
+
+	if (opts.track == BRANCH_TRACK_UNSPECIFIED)
+		opts.track = git_branch_track;
+
+	if (argc) {
+		const char **pathspec = get_pathspec(prefix, argv);
+
+		if (!pathspec)
+			die(_("invalid path specification"));
+
+		if (patch_mode)
+			return interactive_checkout(new.name, pathspec, &opts);
+
+		/* Checkout paths */
+		if (opts.new_branch) {
+			if (argc == 1) {
+				die(_("git checkout: updating paths is incompatible with switching branches.\nDid you intend to checkout '%s' which can not be resolved as commit?"), argv[0]);
+			} else {
+				die(_("git checkout: updating paths is incompatible with switching branches."));
+			}
+		}
+
+		if (opts.force_detach)
+			die(_("git checkout: --detach does not take a path argument"));
+
+		if (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)
+			die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\nchecking out of the index."));
+
+		return checkout_paths(source_tree, pathspec, prefix, &opts);
+	}
+
+	if (patch_mode)
+		return interactive_checkout(new.name, NULL, &opts);
+
+	if (opts.new_branch) {
+		struct strbuf buf = STRBUF_INIT;
+
+		opts.branch_exists = validate_new_branchname(opts.new_branch, &buf,
+							     !!opts.new_branch_force,
+							     !!opts.new_branch_force);
+
+		strbuf_release(&buf);
+	}
+
+	if (new.name && !new.commit) {
+		die(_("Cannot switch branch to a non-commit."));
+	}
+	if (opts.writeout_stage)
+		die(_("--ours/--theirs is incompatible with switching branches."));
+
+	if (!new.commit && opts.new_branch) {
+		unsigned char rev[20];
+		int flag;
+
+		if (!read_ref_full("HEAD", rev, 0, &flag) &&
+		    (flag & REF_ISSYMREF) && is_null_sha1(rev))
+			return switch_unborn_to_new_branch(&opts);
+	}
+	return switch_branches(&opts, &new);
+}
diff --git a/builtin/clean.c b/builtin/clean.c
new file mode 100644
index 0000000..0c7b3d0
--- /dev/null
+++ b/builtin/clean.c
@@ -0,0 +1,192 @@
+/*
+ * "git clean" builtin command
+ *
+ * Copyright (C) 2007 Shawn Bohrer
+ *
+ * Based on git-clean.sh by Pavel Roskin
+ */
+
+#include "builtin.h"
+#include "cache.h"
+#include "dir.h"
+#include "parse-options.h"
+#include "string-list.h"
+#include "quote.h"
+
+static int force = -1; /* unset */
+
+static const char *const builtin_clean_usage[] = {
+	"git clean [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>...",
+	NULL
+};
+
+static int git_clean_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, "clean.requireforce"))
+		force = !git_config_bool(var, value);
+	return git_default_config(var, value, cb);
+}
+
+static int exclude_cb(const struct option *opt, const char *arg, int unset)
+{
+	struct string_list *exclude_list = opt->value;
+	string_list_append(exclude_list, arg);
+	return 0;
+}
+
+int cmd_clean(int argc, const char **argv, const char *prefix)
+{
+	int i;
+	int show_only = 0, remove_directories = 0, quiet = 0, ignored = 0;
+	int ignored_only = 0, config_set = 0, errors = 0;
+	int rm_flags = REMOVE_DIR_KEEP_NESTED_GIT;
+	struct strbuf directory = STRBUF_INIT;
+	struct dir_struct dir;
+	static const char **pathspec;
+	struct strbuf buf = STRBUF_INIT;
+	struct string_list exclude_list = STRING_LIST_INIT_NODUP;
+	const char *qname;
+	char *seen = NULL;
+	struct option options[] = {
+		OPT__QUIET(&quiet, "do not print names of files removed"),
+		OPT__DRY_RUN(&show_only, "dry run"),
+		OPT__FORCE(&force, "force"),
+		OPT_BOOLEAN('d', NULL, &remove_directories,
+				"remove whole directories"),
+		{ OPTION_CALLBACK, 'e', "exclude", &exclude_list, "pattern",
+		  "add <pattern> to ignore rules", PARSE_OPT_NONEG, exclude_cb },
+		OPT_BOOLEAN('x', NULL, &ignored, "remove ignored files, too"),
+		OPT_BOOLEAN('X', NULL, &ignored_only,
+				"remove only ignored files"),
+		OPT_END()
+	};
+
+	git_config(git_clean_config, NULL);
+	if (force < 0)
+		force = 0;
+	else
+		config_set = 1;
+
+	argc = parse_options(argc, argv, prefix, options, builtin_clean_usage,
+			     0);
+
+	memset(&dir, 0, sizeof(dir));
+	if (ignored_only)
+		dir.flags |= DIR_SHOW_IGNORED;
+
+	if (ignored && ignored_only)
+		die(_("-x and -X cannot be used together"));
+
+	if (!show_only && !force) {
+		if (config_set)
+			die(_("clean.requireForce set to true and neither -n nor -f given; "
+				  "refusing to clean"));
+		else
+			die(_("clean.requireForce defaults to true and neither -n nor -f given; "
+				  "refusing to clean"));
+	}
+
+	if (force > 1)
+		rm_flags = 0;
+
+	dir.flags |= DIR_SHOW_OTHER_DIRECTORIES;
+
+	if (read_cache() < 0)
+		die(_("index file corrupt"));
+
+	if (!ignored)
+		setup_standard_excludes(&dir);
+
+	for (i = 0; i < exclude_list.nr; i++)
+		add_exclude(exclude_list.items[i].string, "", 0,
+			    &dir.exclude_list[EXC_CMDL]);
+
+	pathspec = get_pathspec(prefix, argv);
+
+	fill_directory(&dir, pathspec);
+
+	if (pathspec)
+		seen = xmalloc(argc > 0 ? argc : 1);
+
+	for (i = 0; i < dir.nr; i++) {
+		struct dir_entry *ent = dir.entries[i];
+		int len, pos;
+		int matches = 0;
+		struct cache_entry *ce;
+		struct stat st;
+
+		/*
+		 * Remove the '/' at the end that directory
+		 * walking adds for directory entries.
+		 */
+		len = ent->len;
+		if (len && ent->name[len-1] == '/')
+			len--;
+		pos = cache_name_pos(ent->name, len);
+		if (0 <= pos)
+			continue;	/* exact match */
+		pos = -pos - 1;
+		if (pos < active_nr) {
+			ce = active_cache[pos];
+			if (ce_namelen(ce) == len &&
+			    !memcmp(ce->name, ent->name, len))
+				continue; /* Yup, this one exists unmerged */
+		}
+
+		/*
+		 * we might have removed this as part of earlier
+		 * recursive directory removal, so lstat() here could
+		 * fail with ENOENT.
+		 */
+		if (lstat(ent->name, &st))
+			continue;
+
+		if (pathspec) {
+			memset(seen, 0, argc > 0 ? argc : 1);
+			matches = match_pathspec(pathspec, ent->name, len,
+						 0, seen);
+		}
+
+		if (S_ISDIR(st.st_mode)) {
+			strbuf_addstr(&directory, ent->name);
+			qname = quote_path_relative(directory.buf, directory.len, &buf, prefix);
+			if (show_only && (remove_directories ||
+			    (matches == MATCHED_EXACTLY))) {
+				printf(_("Would remove %s\n"), qname);
+			} else if (remove_directories ||
+				   (matches == MATCHED_EXACTLY)) {
+				if (!quiet)
+					printf(_("Removing %s\n"), qname);
+				if (remove_dir_recursively(&directory,
+							   rm_flags) != 0) {
+					warning(_("failed to remove %s"), qname);
+					errors++;
+				}
+			} else if (show_only) {
+				printf(_("Would not remove %s\n"), qname);
+			} else {
+				printf(_("Not removing %s\n"), qname);
+			}
+			strbuf_reset(&directory);
+		} else {
+			if (pathspec && !matches)
+				continue;
+			qname = quote_path_relative(ent->name, -1, &buf, prefix);
+			if (show_only) {
+				printf(_("Would remove %s\n"), qname);
+				continue;
+			} else if (!quiet) {
+				printf(_("Removing %s\n"), qname);
+			}
+			if (unlink(ent->name) != 0) {
+				warning(_("failed to remove %s"), qname);
+				errors++;
+			}
+		}
+	}
+	free(seen);
+
+	strbuf_release(&directory);
+	string_list_clear(&exclude_list, 0);
+	return (errors != 0);
+}
diff --git a/builtin/clone.c b/builtin/clone.c
new file mode 100644
index 0000000..e314b0b
--- /dev/null
+++ b/builtin/clone.c
@@ -0,0 +1,877 @@
+/*
+ * Builtin "git clone"
+ *
+ * Copyright (c) 2007 Kristian Høgsberg <krh@redhat.com>,
+ *		 2008 Daniel Barkalow <barkalow@iabervon.org>
+ * Based on git-commit.sh by Junio C Hamano and Linus Torvalds
+ *
+ * Clone a repository into a different directory that does not yet exist.
+ */
+
+#include "builtin.h"
+#include "parse-options.h"
+#include "fetch-pack.h"
+#include "refs.h"
+#include "tree.h"
+#include "tree-walk.h"
+#include "unpack-trees.h"
+#include "transport.h"
+#include "strbuf.h"
+#include "dir.h"
+#include "pack-refs.h"
+#include "sigchain.h"
+#include "branch.h"
+#include "remote.h"
+#include "run-command.h"
+
+/*
+ * Overall FIXMEs:
+ *  - respect DB_ENVIRONMENT for .git/objects.
+ *
+ * Implementation notes:
+ *  - dropping use-separate-remote and no-separate-remote compatibility
+ *
+ */
+static const char * const builtin_clone_usage[] = {
+	"git clone [options] [--] <repo> [<dir>]",
+	NULL
+};
+
+static int option_no_checkout, option_bare, option_mirror, option_single_branch = -1;
+static int option_local = -1, option_no_hardlinks, option_shared, option_recursive;
+static char *option_template, *option_depth;
+static char *option_origin = NULL;
+static char *option_branch = NULL;
+static const char *real_git_dir;
+static char *option_upload_pack = "git-upload-pack";
+static int option_verbosity;
+static int option_progress = -1;
+static struct string_list option_config;
+static struct string_list option_reference;
+
+static int opt_parse_reference(const struct option *opt, const char *arg, int unset)
+{
+	struct string_list *option_reference = opt->value;
+	if (!arg)
+		return -1;
+	string_list_append(option_reference, arg);
+	return 0;
+}
+
+static struct option builtin_clone_options[] = {
+	OPT__VERBOSITY(&option_verbosity),
+	OPT_BOOL(0, "progress", &option_progress,
+		 "force progress reporting"),
+	OPT_BOOLEAN('n', "no-checkout", &option_no_checkout,
+		    "don't create a checkout"),
+	OPT_BOOLEAN(0, "bare", &option_bare, "create a bare repository"),
+	{ OPTION_BOOLEAN, 0, "naked", &option_bare, NULL,
+		"create a bare repository",
+		PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
+	OPT_BOOLEAN(0, "mirror", &option_mirror,
+		    "create a mirror repository (implies bare)"),
+	OPT_BOOL('l', "local", &option_local,
+		"to clone from a local repository"),
+	OPT_BOOLEAN(0, "no-hardlinks", &option_no_hardlinks,
+		    "don't use local hardlinks, always copy"),
+	OPT_BOOLEAN('s', "shared", &option_shared,
+		    "setup as shared repository"),
+	OPT_BOOLEAN(0, "recursive", &option_recursive,
+		    "initialize submodules in the clone"),
+	OPT_BOOLEAN(0, "recurse-submodules", &option_recursive,
+		    "initialize submodules in the clone"),
+	OPT_STRING(0, "template", &option_template, "template-directory",
+		   "directory from which templates will be used"),
+	OPT_CALLBACK(0 , "reference", &option_reference, "repo",
+		     "reference repository", &opt_parse_reference),
+	OPT_STRING('o', "origin", &option_origin, "name",
+		   "use <name> instead of 'origin' to track upstream"),
+	OPT_STRING('b', "branch", &option_branch, "branch",
+		   "checkout <branch> instead of the remote's HEAD"),
+	OPT_STRING('u', "upload-pack", &option_upload_pack, "path",
+		   "path to git-upload-pack on the remote"),
+	OPT_STRING(0, "depth", &option_depth, "depth",
+		    "create a shallow clone of that depth"),
+	OPT_BOOL(0, "single-branch", &option_single_branch,
+		    "clone only one branch, HEAD or --branch"),
+	OPT_STRING(0, "separate-git-dir", &real_git_dir, "gitdir",
+		   "separate git dir from working tree"),
+	OPT_STRING_LIST('c', "config", &option_config, "key=value",
+			"set config inside the new repository"),
+	OPT_END()
+};
+
+static const char *argv_submodule[] = {
+	"submodule", "update", "--init", "--recursive", NULL
+};
+
+static char *get_repo_path(const char *repo, int *is_bundle)
+{
+	static char *suffix[] = { "/.git", "", ".git/.git", ".git" };
+	static char *bundle_suffix[] = { ".bundle", "" };
+	struct stat st;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(suffix); i++) {
+		const char *path;
+		path = mkpath("%s%s", repo, suffix[i]);
+		if (stat(path, &st))
+			continue;
+		if (S_ISDIR(st.st_mode) && is_git_directory(path)) {
+			*is_bundle = 0;
+			return xstrdup(absolute_path(path));
+		} else if (S_ISREG(st.st_mode) && st.st_size > 8) {
+			/* Is it a "gitfile"? */
+			char signature[8];
+			int len, fd = open(path, O_RDONLY);
+			if (fd < 0)
+				continue;
+			len = read_in_full(fd, signature, 8);
+			close(fd);
+			if (len != 8 || strncmp(signature, "gitdir: ", 8))
+				continue;
+			path = read_gitfile(path);
+			if (path) {
+				*is_bundle = 0;
+				return xstrdup(absolute_path(path));
+			}
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(bundle_suffix); i++) {
+		const char *path;
+		path = mkpath("%s%s", repo, bundle_suffix[i]);
+		if (!stat(path, &st) && S_ISREG(st.st_mode)) {
+			*is_bundle = 1;
+			return xstrdup(absolute_path(path));
+		}
+	}
+
+	return NULL;
+}
+
+static char *guess_dir_name(const char *repo, int is_bundle, int is_bare)
+{
+	const char *end = repo + strlen(repo), *start;
+	char *dir;
+
+	/*
+	 * Strip trailing spaces, slashes and /.git
+	 */
+	while (repo < end && (is_dir_sep(end[-1]) || isspace(end[-1])))
+		end--;
+	if (end - repo > 5 && is_dir_sep(end[-5]) &&
+	    !strncmp(end - 4, ".git", 4)) {
+		end -= 5;
+		while (repo < end && is_dir_sep(end[-1]))
+			end--;
+	}
+
+	/*
+	 * Find last component, but be prepared that repo could have
+	 * the form  "remote.example.com:foo.git", i.e. no slash
+	 * in the directory part.
+	 */
+	start = end;
+	while (repo < start && !is_dir_sep(start[-1]) && start[-1] != ':')
+		start--;
+
+	/*
+	 * Strip .{bundle,git}.
+	 */
+	if (is_bundle) {
+		if (end - start > 7 && !strncmp(end - 7, ".bundle", 7))
+			end -= 7;
+	} else {
+		if (end - start > 4 && !strncmp(end - 4, ".git", 4))
+			end -= 4;
+	}
+
+	if (is_bare) {
+		struct strbuf result = STRBUF_INIT;
+		strbuf_addf(&result, "%.*s.git", (int)(end - start), start);
+		dir = strbuf_detach(&result, NULL);
+	} else
+		dir = xstrndup(start, end - start);
+	/*
+	 * Replace sequences of 'control' characters and whitespace
+	 * with one ascii space, remove leading and trailing spaces.
+	 */
+	if (*dir) {
+		char *out = dir;
+		int prev_space = 1 /* strip leading whitespace */;
+		for (end = dir; *end; ++end) {
+			char ch = *end;
+			if ((unsigned char)ch < '\x20')
+				ch = '\x20';
+			if (isspace(ch)) {
+				if (prev_space)
+					continue;
+				prev_space = 1;
+			} else
+				prev_space = 0;
+			*out++ = ch;
+		}
+		*out = '\0';
+		if (out > dir && prev_space)
+			out[-1] = '\0';
+	}
+	return dir;
+}
+
+static void strip_trailing_slashes(char *dir)
+{
+	char *end = dir + strlen(dir);
+
+	while (dir < end - 1 && is_dir_sep(end[-1]))
+		end--;
+	*end = '\0';
+}
+
+static int add_one_reference(struct string_list_item *item, void *cb_data)
+{
+	char *ref_git;
+	struct strbuf alternate = STRBUF_INIT;
+
+	/* Beware: real_path() and mkpath() return static buffer */
+	ref_git = xstrdup(real_path(item->string));
+	if (is_directory(mkpath("%s/.git/objects", ref_git))) {
+		char *ref_git_git = xstrdup(mkpath("%s/.git", ref_git));
+		free(ref_git);
+		ref_git = ref_git_git;
+	} else if (!is_directory(mkpath("%s/objects", ref_git)))
+		die(_("reference repository '%s' is not a local directory."),
+		    item->string);
+
+	strbuf_addf(&alternate, "%s/objects", ref_git);
+	add_to_alternates_file(alternate.buf);
+	strbuf_release(&alternate);
+	free(ref_git);
+	return 0;
+}
+
+static void setup_reference(void)
+{
+	for_each_string_list(&option_reference, add_one_reference, NULL);
+}
+
+static void copy_alternates(struct strbuf *src, struct strbuf *dst,
+			    const char *src_repo)
+{
+	/*
+	 * Read from the source objects/info/alternates file
+	 * and copy the entries to corresponding file in the
+	 * destination repository with add_to_alternates_file().
+	 * Both src and dst have "$path/objects/info/alternates".
+	 *
+	 * Instead of copying bit-for-bit from the original,
+	 * we need to append to existing one so that the already
+	 * created entry via "clone -s" is not lost, and also
+	 * to turn entries with paths relative to the original
+	 * absolute, so that they can be used in the new repository.
+	 */
+	FILE *in = fopen(src->buf, "r");
+	struct strbuf line = STRBUF_INIT;
+
+	while (strbuf_getline(&line, in, '\n') != EOF) {
+		char *abs_path, abs_buf[PATH_MAX];
+		if (!line.len || line.buf[0] == '#')
+			continue;
+		if (is_absolute_path(line.buf)) {
+			add_to_alternates_file(line.buf);
+			continue;
+		}
+		abs_path = mkpath("%s/objects/%s", src_repo, line.buf);
+		normalize_path_copy(abs_buf, abs_path);
+		add_to_alternates_file(abs_buf);
+	}
+	strbuf_release(&line);
+	fclose(in);
+}
+
+static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
+				   const char *src_repo, int src_baselen)
+{
+	struct dirent *de;
+	struct stat buf;
+	int src_len, dest_len;
+	DIR *dir;
+
+	dir = opendir(src->buf);
+	if (!dir)
+		die_errno(_("failed to open '%s'"), src->buf);
+
+	if (mkdir(dest->buf, 0777)) {
+		if (errno != EEXIST)
+			die_errno(_("failed to create directory '%s'"), dest->buf);
+		else if (stat(dest->buf, &buf))
+			die_errno(_("failed to stat '%s'"), dest->buf);
+		else if (!S_ISDIR(buf.st_mode))
+			die(_("%s exists and is not a directory"), dest->buf);
+	}
+
+	strbuf_addch(src, '/');
+	src_len = src->len;
+	strbuf_addch(dest, '/');
+	dest_len = dest->len;
+
+	while ((de = readdir(dir)) != NULL) {
+		strbuf_setlen(src, src_len);
+		strbuf_addstr(src, de->d_name);
+		strbuf_setlen(dest, dest_len);
+		strbuf_addstr(dest, de->d_name);
+		if (stat(src->buf, &buf)) {
+			warning (_("failed to stat %s\n"), src->buf);
+			continue;
+		}
+		if (S_ISDIR(buf.st_mode)) {
+			if (de->d_name[0] != '.')
+				copy_or_link_directory(src, dest,
+						       src_repo, src_baselen);
+			continue;
+		}
+
+		/* Files that cannot be copied bit-for-bit... */
+		if (!strcmp(src->buf + src_baselen, "/info/alternates")) {
+			copy_alternates(src, dest, src_repo);
+			continue;
+		}
+
+		if (unlink(dest->buf) && errno != ENOENT)
+			die_errno(_("failed to unlink '%s'"), dest->buf);
+		if (!option_no_hardlinks) {
+			if (!link(src->buf, dest->buf))
+				continue;
+			if (option_local > 0)
+				die_errno(_("failed to create link '%s'"), dest->buf);
+			option_no_hardlinks = 1;
+		}
+		if (copy_file_with_time(dest->buf, src->buf, 0666))
+			die_errno(_("failed to copy file to '%s'"), dest->buf);
+	}
+	closedir(dir);
+}
+
+static void clone_local(const char *src_repo, const char *dest_repo)
+{
+	if (option_shared) {
+		struct strbuf alt = STRBUF_INIT;
+		strbuf_addf(&alt, "%s/objects", src_repo);
+		add_to_alternates_file(alt.buf);
+		strbuf_release(&alt);
+	} else {
+		struct strbuf src = STRBUF_INIT;
+		struct strbuf dest = STRBUF_INIT;
+		strbuf_addf(&src, "%s/objects", src_repo);
+		strbuf_addf(&dest, "%s/objects", dest_repo);
+		copy_or_link_directory(&src, &dest, src_repo, src.len);
+		strbuf_release(&src);
+		strbuf_release(&dest);
+	}
+
+	if (0 <= option_verbosity)
+		printf(_("done.\n"));
+}
+
+static const char *junk_work_tree;
+static const char *junk_git_dir;
+static pid_t junk_pid;
+
+static void remove_junk(void)
+{
+	struct strbuf sb = STRBUF_INIT;
+	if (getpid() != junk_pid)
+		return;
+	if (junk_git_dir) {
+		strbuf_addstr(&sb, junk_git_dir);
+		remove_dir_recursively(&sb, 0);
+		strbuf_reset(&sb);
+	}
+	if (junk_work_tree) {
+		strbuf_addstr(&sb, junk_work_tree);
+		remove_dir_recursively(&sb, 0);
+		strbuf_reset(&sb);
+	}
+}
+
+static void remove_junk_on_signal(int signo)
+{
+	remove_junk();
+	sigchain_pop(signo);
+	raise(signo);
+}
+
+static struct ref *find_remote_branch(const struct ref *refs, const char *branch)
+{
+	struct ref *ref;
+	struct strbuf head = STRBUF_INIT;
+	strbuf_addstr(&head, "refs/heads/");
+	strbuf_addstr(&head, branch);
+	ref = find_ref_by_name(refs, head.buf);
+	strbuf_release(&head);
+
+	if (ref)
+		return ref;
+
+	strbuf_addstr(&head, "refs/tags/");
+	strbuf_addstr(&head, branch);
+	ref = find_ref_by_name(refs, head.buf);
+	strbuf_release(&head);
+
+	return ref;
+}
+
+static struct ref *wanted_peer_refs(const struct ref *refs,
+		struct refspec *refspec)
+{
+	struct ref *head = copy_ref(find_ref_by_name(refs, "HEAD"));
+	struct ref *local_refs = head;
+	struct ref **tail = head ? &head->next : &local_refs;
+
+	if (option_single_branch) {
+		struct ref *remote_head = NULL;
+
+		if (!option_branch)
+			remote_head = guess_remote_head(head, refs, 0);
+		else {
+			local_refs = NULL;
+			tail = &local_refs;
+			remote_head = copy_ref(find_remote_branch(refs, option_branch));
+		}
+
+		if (!remote_head && option_branch)
+			warning(_("Could not find remote branch %s to clone."),
+				option_branch);
+		else {
+			get_fetch_map(remote_head, refspec, &tail, 0);
+
+			/* if --branch=tag, pull the requested tag explicitly */
+			get_fetch_map(remote_head, tag_refspec, &tail, 0);
+		}
+	} else
+		get_fetch_map(refs, refspec, &tail, 0);
+
+	if (!option_mirror && !option_single_branch)
+		get_fetch_map(refs, tag_refspec, &tail, 0);
+
+	return local_refs;
+}
+
+static void write_remote_refs(const struct ref *local_refs)
+{
+	const struct ref *r;
+
+	for (r = local_refs; r; r = r->next) {
+		if (!r->peer_ref)
+			continue;
+		add_packed_ref(r->peer_ref->name, r->old_sha1);
+	}
+
+	pack_refs(PACK_REFS_ALL);
+}
+
+static void write_followtags(const struct ref *refs, const char *msg)
+{
+	const struct ref *ref;
+	for (ref = refs; ref; ref = ref->next) {
+		if (prefixcmp(ref->name, "refs/tags/"))
+			continue;
+		if (!suffixcmp(ref->name, "^{}"))
+			continue;
+		if (!has_sha1_file(ref->old_sha1))
+			continue;
+		update_ref(msg, ref->name, ref->old_sha1,
+			   NULL, 0, DIE_ON_ERR);
+	}
+}
+
+static void update_remote_refs(const struct ref *refs,
+			       const struct ref *mapped_refs,
+			       const struct ref *remote_head_points_at,
+			       const char *branch_top,
+			       const char *msg)
+{
+	if (refs) {
+		write_remote_refs(mapped_refs);
+		if (option_single_branch)
+			write_followtags(refs, msg);
+	}
+
+	if (remote_head_points_at && !option_bare) {
+		struct strbuf head_ref = STRBUF_INIT;
+		strbuf_addstr(&head_ref, branch_top);
+		strbuf_addstr(&head_ref, "HEAD");
+		create_symref(head_ref.buf,
+			      remote_head_points_at->peer_ref->name,
+			      msg);
+	}
+}
+
+static void update_head(const struct ref *our, const struct ref *remote,
+			const char *msg)
+{
+	if (our && !prefixcmp(our->name, "refs/heads/")) {
+		/* Local default branch link */
+		create_symref("HEAD", our->name, NULL);
+		if (!option_bare) {
+			const char *head = skip_prefix(our->name, "refs/heads/");
+			update_ref(msg, "HEAD", our->old_sha1, NULL, 0, DIE_ON_ERR);
+			install_branch_config(0, head, option_origin, our->name);
+		}
+	} else if (our) {
+		struct commit *c = lookup_commit_reference(our->old_sha1);
+		/* --branch specifies a non-branch (i.e. tags), detach HEAD */
+		update_ref(msg, "HEAD", c->object.sha1,
+			   NULL, REF_NODEREF, DIE_ON_ERR);
+	} else if (remote) {
+		/*
+		 * We know remote HEAD points to a non-branch, or
+		 * HEAD points to a branch but we don't know which one.
+		 * Detach HEAD in all these cases.
+		 */
+		update_ref(msg, "HEAD", remote->old_sha1,
+			   NULL, REF_NODEREF, DIE_ON_ERR);
+	}
+}
+
+static int checkout(void)
+{
+	unsigned char sha1[20];
+	char *head;
+	struct lock_file *lock_file;
+	struct unpack_trees_options opts;
+	struct tree *tree;
+	struct tree_desc t;
+	int err = 0, fd;
+
+	if (option_no_checkout)
+		return 0;
+
+	head = resolve_refdup("HEAD", sha1, 1, NULL);
+	if (!head) {
+		warning(_("remote HEAD refers to nonexistent ref, "
+			  "unable to checkout.\n"));
+		return 0;
+	}
+	if (!strcmp(head, "HEAD")) {
+		if (advice_detached_head)
+			detach_advice(sha1_to_hex(sha1));
+	} else {
+		if (prefixcmp(head, "refs/heads/"))
+			die(_("HEAD not found below refs/heads!"));
+	}
+	free(head);
+
+	/* We need to be in the new work tree for the checkout */
+	setup_work_tree();
+
+	lock_file = xcalloc(1, sizeof(struct lock_file));
+	fd = hold_locked_index(lock_file, 1);
+
+	memset(&opts, 0, sizeof opts);
+	opts.update = 1;
+	opts.merge = 1;
+	opts.fn = oneway_merge;
+	opts.verbose_update = (option_verbosity >= 0);
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+
+	tree = parse_tree_indirect(sha1);
+	parse_tree(tree);
+	init_tree_desc(&t, tree->buffer, tree->size);
+	unpack_trees(1, &t, &opts);
+
+	if (write_cache(fd, active_cache, active_nr) ||
+	    commit_locked_index(lock_file))
+		die(_("unable to write new index file"));
+
+	err |= run_hook(NULL, "post-checkout", sha1_to_hex(null_sha1),
+			sha1_to_hex(sha1), "1", NULL);
+
+	if (!err && option_recursive)
+		err = run_command_v_opt(argv_submodule, RUN_GIT_CMD);
+
+	return err;
+}
+
+static int write_one_config(const char *key, const char *value, void *data)
+{
+	return git_config_set_multivar(key, value ? value : "true", "^$", 0);
+}
+
+static void write_config(struct string_list *config)
+{
+	int i;
+
+	for (i = 0; i < config->nr; i++) {
+		if (git_config_parse_parameter(config->items[i].string,
+					       write_one_config, NULL) < 0)
+			die("unable to write parameters to config file");
+	}
+}
+
+int cmd_clone(int argc, const char **argv, const char *prefix)
+{
+	int is_bundle = 0, is_local;
+	struct stat buf;
+	const char *repo_name, *repo, *work_tree, *git_dir;
+	char *path, *dir;
+	int dest_exists;
+	const struct ref *refs, *remote_head;
+	const struct ref *remote_head_points_at;
+	const struct ref *our_head_points_at;
+	struct ref *mapped_refs;
+	const struct ref *ref;
+	struct strbuf key = STRBUF_INIT, value = STRBUF_INIT;
+	struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;
+	struct transport *transport = NULL;
+	const char *src_ref_prefix = "refs/heads/";
+	struct remote *remote;
+	int err = 0, complete_refs_before_fetch = 1;
+
+	struct refspec *refspec;
+	const char *fetch_pattern;
+
+	junk_pid = getpid();
+
+	packet_trace_identity("clone");
+	argc = parse_options(argc, argv, prefix, builtin_clone_options,
+			     builtin_clone_usage, 0);
+
+	if (argc > 2)
+		usage_msg_opt(_("Too many arguments."),
+			builtin_clone_usage, builtin_clone_options);
+
+	if (argc == 0)
+		usage_msg_opt(_("You must specify a repository to clone."),
+			builtin_clone_usage, builtin_clone_options);
+
+	if (option_single_branch == -1)
+		option_single_branch = option_depth ? 1 : 0;
+
+	if (option_mirror)
+		option_bare = 1;
+
+	if (option_bare) {
+		if (option_origin)
+			die(_("--bare and --origin %s options are incompatible."),
+			    option_origin);
+		option_no_checkout = 1;
+	}
+
+	if (!option_origin)
+		option_origin = "origin";
+
+	repo_name = argv[0];
+
+	path = get_repo_path(repo_name, &is_bundle);
+	if (path)
+		repo = xstrdup(absolute_path(repo_name));
+	else if (!strchr(repo_name, ':'))
+		die(_("repository '%s' does not exist"), repo_name);
+	else
+		repo = repo_name;
+	is_local = option_local != 0 && path && !is_bundle;
+	if (is_local && option_depth)
+		warning(_("--depth is ignored in local clones; use file:// instead."));
+
+	if (argc == 2)
+		dir = xstrdup(argv[1]);
+	else
+		dir = guess_dir_name(repo_name, is_bundle, option_bare);
+	strip_trailing_slashes(dir);
+
+	dest_exists = !stat(dir, &buf);
+	if (dest_exists && !is_empty_dir(dir))
+		die(_("destination path '%s' already exists and is not "
+			"an empty directory."), dir);
+
+	strbuf_addf(&reflog_msg, "clone: from %s", repo);
+
+	if (option_bare)
+		work_tree = NULL;
+	else {
+		work_tree = getenv("GIT_WORK_TREE");
+		if (work_tree && !stat(work_tree, &buf))
+			die(_("working tree '%s' already exists."), work_tree);
+	}
+
+	if (option_bare || work_tree)
+		git_dir = xstrdup(dir);
+	else {
+		work_tree = dir;
+		git_dir = xstrdup(mkpath("%s/.git", dir));
+	}
+
+	if (!option_bare) {
+		junk_work_tree = work_tree;
+		if (safe_create_leading_directories_const(work_tree) < 0)
+			die_errno(_("could not create leading directories of '%s'"),
+				  work_tree);
+		if (!dest_exists && mkdir(work_tree, 0777))
+			die_errno(_("could not create work tree dir '%s'."),
+				  work_tree);
+		set_git_work_tree(work_tree);
+	}
+	junk_git_dir = git_dir;
+	atexit(remove_junk);
+	sigchain_push_common(remove_junk_on_signal);
+
+	setenv(CONFIG_ENVIRONMENT, mkpath("%s/config", git_dir), 1);
+
+	if (safe_create_leading_directories_const(git_dir) < 0)
+		die(_("could not create leading directories of '%s'"), git_dir);
+
+	set_git_dir_init(git_dir, real_git_dir, 0);
+	if (real_git_dir)
+		git_dir = real_git_dir;
+
+	if (0 <= option_verbosity) {
+		if (option_bare)
+			printf(_("Cloning into bare repository '%s'...\n"), dir);
+		else
+			printf(_("Cloning into '%s'...\n"), dir);
+	}
+	init_db(option_template, INIT_DB_QUIET);
+	write_config(&option_config);
+
+	/*
+	 * At this point, the config exists, so we do not need the
+	 * environment variable.  We actually need to unset it, too, to
+	 * re-enable parsing of the global configs.
+	 */
+	unsetenv(CONFIG_ENVIRONMENT);
+
+	git_config(git_default_config, NULL);
+
+	if (option_bare) {
+		if (option_mirror)
+			src_ref_prefix = "refs/";
+		strbuf_addstr(&branch_top, src_ref_prefix);
+
+		git_config_set("core.bare", "true");
+	} else {
+		strbuf_addf(&branch_top, "refs/remotes/%s/", option_origin);
+	}
+
+	strbuf_addf(&value, "+%s*:%s*", src_ref_prefix, branch_top.buf);
+
+	if (option_mirror || !option_bare) {
+		/* Configure the remote */
+		strbuf_addf(&key, "remote.%s.fetch", option_origin);
+		git_config_set_multivar(key.buf, value.buf, "^$", 0);
+		strbuf_reset(&key);
+
+		if (option_mirror) {
+			strbuf_addf(&key, "remote.%s.mirror", option_origin);
+			git_config_set(key.buf, "true");
+			strbuf_reset(&key);
+		}
+	}
+
+	strbuf_addf(&key, "remote.%s.url", option_origin);
+	git_config_set(key.buf, repo);
+	strbuf_reset(&key);
+
+	if (option_reference.nr)
+		setup_reference();
+
+	fetch_pattern = value.buf;
+	refspec = parse_fetch_refspec(1, &fetch_pattern);
+
+	strbuf_reset(&value);
+
+	remote = remote_get(option_origin);
+	transport = transport_get(remote, remote->url[0]);
+
+	if (!is_local) {
+		if (!transport->get_refs_list || !transport->fetch)
+			die(_("Don't know how to clone %s"), transport->url);
+
+		transport_set_option(transport, TRANS_OPT_KEEP, "yes");
+
+		if (option_depth)
+			transport_set_option(transport, TRANS_OPT_DEPTH,
+					     option_depth);
+		if (option_single_branch)
+			transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1");
+
+		transport_set_verbosity(transport, option_verbosity, option_progress);
+
+		if (option_upload_pack)
+			transport_set_option(transport, TRANS_OPT_UPLOADPACK,
+					     option_upload_pack);
+	}
+
+	refs = transport_get_remote_refs(transport);
+
+	if (refs) {
+		mapped_refs = wanted_peer_refs(refs, refspec);
+		/*
+		 * transport_get_remote_refs() may return refs with null sha-1
+		 * in mapped_refs (see struct transport->get_refs_list
+		 * comment). In that case we need fetch it early because
+		 * remote_head code below relies on it.
+		 *
+		 * for normal clones, transport_get_remote_refs() should
+		 * return reliable ref set, we can delay cloning until after
+		 * remote HEAD check.
+		 */
+		for (ref = refs; ref; ref = ref->next)
+			if (is_null_sha1(ref->old_sha1)) {
+				complete_refs_before_fetch = 0;
+				break;
+			}
+
+		if (!is_local && !complete_refs_before_fetch)
+			transport_fetch_refs(transport, mapped_refs);
+
+		remote_head = find_ref_by_name(refs, "HEAD");
+		remote_head_points_at =
+			guess_remote_head(remote_head, mapped_refs, 0);
+
+		if (option_branch) {
+			our_head_points_at =
+				find_remote_branch(mapped_refs, option_branch);
+
+			if (!our_head_points_at)
+				die(_("Remote branch %s not found in upstream %s"),
+				    option_branch, option_origin);
+		}
+		else
+			our_head_points_at = remote_head_points_at;
+	}
+	else {
+		warning(_("You appear to have cloned an empty repository."));
+		mapped_refs = NULL;
+		our_head_points_at = NULL;
+		remote_head_points_at = NULL;
+		remote_head = NULL;
+		option_no_checkout = 1;
+		if (!option_bare)
+			install_branch_config(0, "master", option_origin,
+					      "refs/heads/master");
+	}
+
+	if (is_local)
+		clone_local(path, git_dir);
+	else if (refs && complete_refs_before_fetch)
+		transport_fetch_refs(transport, mapped_refs);
+
+	update_remote_refs(refs, mapped_refs, remote_head_points_at,
+			   branch_top.buf, reflog_msg.buf);
+
+	update_head(our_head_points_at, remote_head, reflog_msg.buf);
+
+	transport_unlock_pack(transport);
+	transport_disconnect(transport);
+
+	err = checkout();
+
+	strbuf_release(&reflog_msg);
+	strbuf_release(&branch_top);
+	strbuf_release(&key);
+	strbuf_release(&value);
+	junk_pid = 0;
+	return err;
+}
diff --git a/builtin/column.c b/builtin/column.c
new file mode 100644
index 0000000..5ea798a
--- /dev/null
+++ b/builtin/column.c
@@ -0,0 +1,59 @@
+#include "builtin.h"
+#include "cache.h"
+#include "strbuf.h"
+#include "parse-options.h"
+#include "string-list.h"
+#include "column.h"
+
+static const char * const builtin_column_usage[] = {
+	"git column [options]",
+	NULL
+};
+static unsigned int colopts;
+
+static int column_config(const char *var, const char *value, void *cb)
+{
+	return git_column_config(var, value, cb, &colopts);
+}
+
+int cmd_column(int argc, const char **argv, const char *prefix)
+{
+	struct string_list list = STRING_LIST_INIT_DUP;
+	struct strbuf sb = STRBUF_INIT;
+	struct column_options copts;
+	const char *command = NULL, *real_command = NULL;
+	struct option options[] = {
+		OPT_STRING(0, "command", &real_command, "name", "lookup config vars"),
+		OPT_COLUMN(0, "mode", &colopts, "layout to use"),
+		OPT_INTEGER(0, "raw-mode", &colopts, "layout to use"),
+		OPT_INTEGER(0, "width", &copts.width, "Maximum width"),
+		OPT_STRING(0, "indent", &copts.indent, "string", "Padding space on left border"),
+		OPT_INTEGER(0, "nl", &copts.nl, "Padding space on right border"),
+		OPT_INTEGER(0, "padding", &copts.padding, "Padding space between columns"),
+		OPT_END()
+	};
+
+	/* This one is special and must be the first one */
+	if (argc > 1 && !prefixcmp(argv[1], "--command=")) {
+		command = argv[1] + 10;
+		git_config(column_config, (void *)command);
+	} else
+		git_config(column_config, NULL);
+
+	memset(&copts, 0, sizeof(copts));
+	copts.width = term_columns();
+	copts.padding = 1;
+	argc = parse_options(argc, argv, "", options, builtin_column_usage, 0);
+	if (argc)
+		usage_with_options(builtin_column_usage, options);
+	if (real_command || command) {
+		if (!real_command || !command || strcmp(real_command, command))
+			die(_("--command must be the first argument"));
+	}
+	finalize_colopts(&colopts, -1);
+	while (!strbuf_getline(&sb, stdin, '\n'))
+		string_list_append(&list, sb.buf);
+
+	print_columns(&list, colopts, &copts);
+	return 0;
+}
diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c
new file mode 100644
index 0000000..eac901a
--- /dev/null
+++ b/builtin/commit-tree.c
@@ -0,0 +1,125 @@
+/*
+ * GIT - The information manager from hell
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ */
+#include "cache.h"
+#include "commit.h"
+#include "tree.h"
+#include "builtin.h"
+#include "utf8.h"
+#include "gpg-interface.h"
+
+static const char commit_tree_usage[] = "git commit-tree [(-p <sha1>)...] [-S<signer>] [-m <message>] [-F <file>] <sha1> <changelog";
+
+static void new_parent(struct commit *parent, struct commit_list **parents_p)
+{
+	unsigned char *sha1 = parent->object.sha1;
+	struct commit_list *parents;
+	for (parents = *parents_p; parents; parents = parents->next) {
+		if (parents->item == parent) {
+			error("duplicate parent %s ignored", sha1_to_hex(sha1));
+			return;
+		}
+		parents_p = &parents->next;
+	}
+	commit_list_insert(parent, parents_p);
+}
+
+static int commit_tree_config(const char *var, const char *value, void *cb)
+{
+	int status = git_gpg_config(var, value, NULL);
+	if (status)
+		return status;
+	return git_default_config(var, value, cb);
+}
+
+int cmd_commit_tree(int argc, const char **argv, const char *prefix)
+{
+	int i, got_tree = 0;
+	struct commit_list *parents = NULL;
+	unsigned char tree_sha1[20];
+	unsigned char commit_sha1[20];
+	struct strbuf buffer = STRBUF_INIT;
+	const char *sign_commit = NULL;
+
+	git_config(commit_tree_config, NULL);
+
+	if (argc < 2 || !strcmp(argv[1], "-h"))
+		usage(commit_tree_usage);
+
+	for (i = 1; i < argc; i++) {
+		const char *arg = argv[i];
+		if (!strcmp(arg, "-p")) {
+			unsigned char sha1[20];
+			if (argc <= ++i)
+				usage(commit_tree_usage);
+			if (get_sha1_commit(argv[i], sha1))
+				die("Not a valid object name %s", argv[i]);
+			assert_sha1_type(sha1, OBJ_COMMIT);
+			new_parent(lookup_commit(sha1), &parents);
+			continue;
+		}
+
+		if (!memcmp(arg, "-S", 2)) {
+			sign_commit = arg + 2;
+			continue;
+		}
+
+		if (!strcmp(arg, "-m")) {
+			if (argc <= ++i)
+				usage(commit_tree_usage);
+			if (buffer.len)
+				strbuf_addch(&buffer, '\n');
+			strbuf_addstr(&buffer, argv[i]);
+			strbuf_complete_line(&buffer);
+			continue;
+		}
+
+		if (!strcmp(arg, "-F")) {
+			int fd;
+
+			if (argc <= ++i)
+				usage(commit_tree_usage);
+			if (buffer.len)
+				strbuf_addch(&buffer, '\n');
+			if (!strcmp(argv[i], "-"))
+				fd = 0;
+			else {
+				fd = open(argv[i], O_RDONLY);
+				if (fd < 0)
+					die_errno("git commit-tree: failed to open '%s'",
+						  argv[i]);
+			}
+			if (strbuf_read(&buffer, fd, 0) < 0)
+				die_errno("git commit-tree: failed to read '%s'",
+					  argv[i]);
+			if (fd && close(fd))
+				die_errno("git commit-tree: failed to close '%s'",
+					  argv[i]);
+			strbuf_complete_line(&buffer);
+			continue;
+		}
+
+		if (get_sha1_tree(arg, tree_sha1))
+			die("Not a valid object name %s", arg);
+		if (got_tree)
+			die("Cannot give more than one trees");
+		got_tree = 1;
+	}
+
+	if (!buffer.len) {
+		if (strbuf_read(&buffer, 0, 0) < 0)
+			die_errno("git commit-tree: failed to read");
+	}
+
+	if (commit_tree(&buffer, tree_sha1, parents, commit_sha1,
+			NULL, sign_commit)) {
+		strbuf_release(&buffer);
+		return 1;
+	}
+
+	printf("%s\n", sha1_to_hex(commit_sha1));
+	strbuf_release(&buffer);
+	return 0;
+}
diff --git a/builtin/commit.c b/builtin/commit.c
new file mode 100644
index 0000000..95eeab1
--- /dev/null
+++ b/builtin/commit.c
@@ -0,0 +1,1610 @@
+/*
+ * Builtin "git commit"
+ *
+ * Copyright (c) 2007 Kristian Høgsberg <krh@redhat.com>
+ * Based on git-commit.sh by Junio C Hamano and Linus Torvalds
+ */
+
+#include "cache.h"
+#include "cache-tree.h"
+#include "color.h"
+#include "dir.h"
+#include "builtin.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "commit.h"
+#include "revision.h"
+#include "wt-status.h"
+#include "run-command.h"
+#include "refs.h"
+#include "log-tree.h"
+#include "strbuf.h"
+#include "utf8.h"
+#include "parse-options.h"
+#include "string-list.h"
+#include "rerere.h"
+#include "unpack-trees.h"
+#include "quote.h"
+#include "submodule.h"
+#include "gpg-interface.h"
+#include "column.h"
+
+static const char * const builtin_commit_usage[] = {
+	"git commit [options] [--] <filepattern>...",
+	NULL
+};
+
+static const char * const builtin_status_usage[] = {
+	"git status [options] [--] <filepattern>...",
+	NULL
+};
+
+static const char implicit_ident_advice[] =
+N_("Your name and email address were configured automatically based\n"
+"on your username and hostname. Please check that they are accurate.\n"
+"You can suppress this message by setting them explicitly:\n"
+"\n"
+"    git config --global user.name \"Your Name\"\n"
+"    git config --global user.email you@example.com\n"
+"\n"
+"After doing this, you may fix the identity used for this commit with:\n"
+"\n"
+"    git commit --amend --reset-author\n");
+
+static const char empty_amend_advice[] =
+N_("You asked to amend the most recent commit, but doing so would make\n"
+"it empty. You can repeat your command with --allow-empty, or you can\n"
+"remove the commit entirely with \"git reset HEAD^\".\n");
+
+static const char empty_cherry_pick_advice[] =
+N_("The previous cherry-pick is now empty, possibly due to conflict resolution.\n"
+"If you wish to commit it anyway, use:\n"
+"\n"
+"    git commit --allow-empty\n"
+"\n"
+"Otherwise, please use 'git reset'\n");
+
+static const char *use_message_buffer;
+static const char commit_editmsg[] = "COMMIT_EDITMSG";
+static struct lock_file index_lock; /* real index */
+static struct lock_file false_lock; /* used only for partial commits */
+static enum {
+	COMMIT_AS_IS = 1,
+	COMMIT_NORMAL,
+	COMMIT_PARTIAL
+} commit_style;
+
+static const char *logfile, *force_author;
+static const char *template_file;
+/*
+ * The _message variables are commit names from which to take
+ * the commit message and/or authorship.
+ */
+static const char *author_message, *author_message_buffer;
+static char *edit_message, *use_message;
+static char *fixup_message, *squash_message;
+static int all, also, interactive, patch_interactive, only, amend, signoff;
+static int edit_flag = -1; /* unspecified */
+static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
+static int no_post_rewrite, allow_empty_message;
+static char *untracked_files_arg, *force_date, *ignore_submodule_arg;
+static char *sign_commit;
+
+/*
+ * The default commit message cleanup mode will remove the lines
+ * beginning with # (shell comments) and leading and trailing
+ * whitespaces (empty lines or containing only whitespaces)
+ * if editor is used, and only the whitespaces if the message
+ * is specified explicitly.
+ */
+static enum {
+	CLEANUP_SPACE,
+	CLEANUP_NONE,
+	CLEANUP_ALL
+} cleanup_mode;
+static char *cleanup_arg;
+
+static enum commit_whence whence;
+static int use_editor = 1, include_status = 1;
+static int show_ignored_in_status;
+static const char *only_include_assumed;
+static struct strbuf message = STRBUF_INIT;
+
+static enum {
+	STATUS_FORMAT_LONG,
+	STATUS_FORMAT_SHORT,
+	STATUS_FORMAT_PORCELAIN
+} status_format = STATUS_FORMAT_LONG;
+
+static int opt_parse_m(const struct option *opt, const char *arg, int unset)
+{
+	struct strbuf *buf = opt->value;
+	if (unset)
+		strbuf_setlen(buf, 0);
+	else {
+		strbuf_addstr(buf, arg);
+		strbuf_addstr(buf, "\n\n");
+	}
+	return 0;
+}
+
+static void determine_whence(struct wt_status *s)
+{
+	if (file_exists(git_path("MERGE_HEAD")))
+		whence = FROM_MERGE;
+	else if (file_exists(git_path("CHERRY_PICK_HEAD")))
+		whence = FROM_CHERRY_PICK;
+	else
+		whence = FROM_COMMIT;
+	if (s)
+		s->whence = whence;
+}
+
+static void rollback_index_files(void)
+{
+	switch (commit_style) {
+	case COMMIT_AS_IS:
+		break; /* nothing to do */
+	case COMMIT_NORMAL:
+		rollback_lock_file(&index_lock);
+		break;
+	case COMMIT_PARTIAL:
+		rollback_lock_file(&index_lock);
+		rollback_lock_file(&false_lock);
+		break;
+	}
+}
+
+static int commit_index_files(void)
+{
+	int err = 0;
+
+	switch (commit_style) {
+	case COMMIT_AS_IS:
+		break; /* nothing to do */
+	case COMMIT_NORMAL:
+		err = commit_lock_file(&index_lock);
+		break;
+	case COMMIT_PARTIAL:
+		err = commit_lock_file(&index_lock);
+		rollback_lock_file(&false_lock);
+		break;
+	}
+
+	return err;
+}
+
+/*
+ * Take a union of paths in the index and the named tree (typically, "HEAD"),
+ * and return the paths that match the given pattern in list.
+ */
+static int list_paths(struct string_list *list, const char *with_tree,
+		      const char *prefix, const char **pattern)
+{
+	int i;
+	char *m;
+
+	if (!pattern)
+		return 0;
+
+	for (i = 0; pattern[i]; i++)
+		;
+	m = xcalloc(1, i);
+
+	if (with_tree) {
+		char *max_prefix = common_prefix(pattern);
+		overlay_tree_on_cache(with_tree, max_prefix ? max_prefix : prefix);
+		free(max_prefix);
+	}
+
+	for (i = 0; i < active_nr; i++) {
+		struct cache_entry *ce = active_cache[i];
+		struct string_list_item *item;
+
+		if (ce->ce_flags & CE_UPDATE)
+			continue;
+		if (!match_pathspec(pattern, ce->name, ce_namelen(ce), 0, m))
+			continue;
+		item = string_list_insert(list, ce->name);
+		if (ce_skip_worktree(ce))
+			item->util = item; /* better a valid pointer than a fake one */
+	}
+
+	return report_path_error(m, pattern, prefix);
+}
+
+static void add_remove_files(struct string_list *list)
+{
+	int i;
+	for (i = 0; i < list->nr; i++) {
+		struct stat st;
+		struct string_list_item *p = &(list->items[i]);
+
+		/* p->util is skip-worktree */
+		if (p->util)
+			continue;
+
+		if (!lstat(p->string, &st)) {
+			if (add_to_cache(p->string, &st, 0))
+				die(_("updating files failed"));
+		} else
+			remove_file_from_cache(p->string);
+	}
+}
+
+static void create_base_index(const struct commit *current_head)
+{
+	struct tree *tree;
+	struct unpack_trees_options opts;
+	struct tree_desc t;
+
+	if (!current_head) {
+		discard_cache();
+		return;
+	}
+
+	memset(&opts, 0, sizeof(opts));
+	opts.head_idx = 1;
+	opts.index_only = 1;
+	opts.merge = 1;
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+
+	opts.fn = oneway_merge;
+	tree = parse_tree_indirect(current_head->object.sha1);
+	if (!tree)
+		die(_("failed to unpack HEAD tree object"));
+	parse_tree(tree);
+	init_tree_desc(&t, tree->buffer, tree->size);
+	if (unpack_trees(1, &t, &opts))
+		exit(128); /* We've already reported the error, finish dying */
+}
+
+static void refresh_cache_or_die(int refresh_flags)
+{
+	/*
+	 * refresh_flags contains REFRESH_QUIET, so the only errors
+	 * are for unmerged entries.
+	 */
+	if (refresh_cache(refresh_flags | REFRESH_IN_PORCELAIN))
+		die_resolve_conflict("commit");
+}
+
+static char *prepare_index(int argc, const char **argv, const char *prefix,
+			   const struct commit *current_head, int is_status)
+{
+	int fd;
+	struct string_list partial;
+	const char **pathspec = NULL;
+	char *old_index_env = NULL;
+	int refresh_flags = REFRESH_QUIET;
+
+	if (is_status)
+		refresh_flags |= REFRESH_UNMERGED;
+
+	if (*argv)
+		pathspec = get_pathspec(prefix, argv);
+
+	if (read_cache_preload(pathspec) < 0)
+		die(_("index file corrupt"));
+
+	if (interactive) {
+		fd = hold_locked_index(&index_lock, 1);
+
+		refresh_cache_or_die(refresh_flags);
+
+		if (write_cache(fd, active_cache, active_nr) ||
+		    close_lock_file(&index_lock))
+			die(_("unable to create temporary index"));
+
+		old_index_env = getenv(INDEX_ENVIRONMENT);
+		setenv(INDEX_ENVIRONMENT, index_lock.filename, 1);
+
+		if (interactive_add(argc, argv, prefix, patch_interactive) != 0)
+			die(_("interactive add failed"));
+
+		if (old_index_env && *old_index_env)
+			setenv(INDEX_ENVIRONMENT, old_index_env, 1);
+		else
+			unsetenv(INDEX_ENVIRONMENT);
+
+		discard_cache();
+		read_cache_from(index_lock.filename);
+
+		commit_style = COMMIT_NORMAL;
+		return index_lock.filename;
+	}
+
+	/*
+	 * Non partial, non as-is commit.
+	 *
+	 * (1) get the real index;
+	 * (2) update the_index as necessary;
+	 * (3) write the_index out to the real index (still locked);
+	 * (4) return the name of the locked index file.
+	 *
+	 * The caller should run hooks on the locked real index, and
+	 * (A) if all goes well, commit the real index;
+	 * (B) on failure, rollback the real index.
+	 */
+	if (all || (also && pathspec && *pathspec)) {
+		fd = hold_locked_index(&index_lock, 1);
+		add_files_to_cache(also ? prefix : NULL, pathspec, 0);
+		refresh_cache_or_die(refresh_flags);
+		update_main_cache_tree(WRITE_TREE_SILENT);
+		if (write_cache(fd, active_cache, active_nr) ||
+		    close_lock_file(&index_lock))
+			die(_("unable to write new_index file"));
+		commit_style = COMMIT_NORMAL;
+		return index_lock.filename;
+	}
+
+	/*
+	 * As-is commit.
+	 *
+	 * (1) return the name of the real index file.
+	 *
+	 * The caller should run hooks on the real index,
+	 * and create commit from the_index.
+	 * We still need to refresh the index here.
+	 */
+	if (!only && (!pathspec || !*pathspec)) {
+		fd = hold_locked_index(&index_lock, 1);
+		refresh_cache_or_die(refresh_flags);
+		if (active_cache_changed) {
+			update_main_cache_tree(WRITE_TREE_SILENT);
+			if (write_cache(fd, active_cache, active_nr) ||
+			    commit_locked_index(&index_lock))
+				die(_("unable to write new_index file"));
+		} else {
+			rollback_lock_file(&index_lock);
+		}
+		commit_style = COMMIT_AS_IS;
+		return get_index_file();
+	}
+
+	/*
+	 * A partial commit.
+	 *
+	 * (0) find the set of affected paths;
+	 * (1) get lock on the real index file;
+	 * (2) update the_index with the given paths;
+	 * (3) write the_index out to the real index (still locked);
+	 * (4) get lock on the false index file;
+	 * (5) reset the_index from HEAD;
+	 * (6) update the_index the same way as (2);
+	 * (7) write the_index out to the false index file;
+	 * (8) return the name of the false index file (still locked);
+	 *
+	 * The caller should run hooks on the locked false index, and
+	 * create commit from it.  Then
+	 * (A) if all goes well, commit the real index;
+	 * (B) on failure, rollback the real index;
+	 * In either case, rollback the false index.
+	 */
+	commit_style = COMMIT_PARTIAL;
+
+	if (whence != FROM_COMMIT) {
+		if (whence == FROM_MERGE)
+			die(_("cannot do a partial commit during a merge."));
+		else if (whence == FROM_CHERRY_PICK)
+			die(_("cannot do a partial commit during a cherry-pick."));
+	}
+
+	memset(&partial, 0, sizeof(partial));
+	partial.strdup_strings = 1;
+	if (list_paths(&partial, !current_head ? NULL : "HEAD", prefix, pathspec))
+		exit(1);
+
+	discard_cache();
+	if (read_cache() < 0)
+		die(_("cannot read the index"));
+
+	fd = hold_locked_index(&index_lock, 1);
+	add_remove_files(&partial);
+	refresh_cache(REFRESH_QUIET);
+	if (write_cache(fd, active_cache, active_nr) ||
+	    close_lock_file(&index_lock))
+		die(_("unable to write new_index file"));
+
+	fd = hold_lock_file_for_update(&false_lock,
+				       git_path("next-index-%"PRIuMAX,
+						(uintmax_t) getpid()),
+				       LOCK_DIE_ON_ERROR);
+
+	create_base_index(current_head);
+	add_remove_files(&partial);
+	refresh_cache(REFRESH_QUIET);
+
+	if (write_cache(fd, active_cache, active_nr) ||
+	    close_lock_file(&false_lock))
+		die(_("unable to write temporary index file"));
+
+	discard_cache();
+	read_cache_from(false_lock.filename);
+
+	return false_lock.filename;
+}
+
+static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn,
+		      struct wt_status *s)
+{
+	unsigned char sha1[20];
+
+	if (s->relative_paths)
+		s->prefix = prefix;
+
+	if (amend) {
+		s->amend = 1;
+		s->reference = "HEAD^1";
+	}
+	s->verbose = verbose;
+	s->index_file = index_file;
+	s->fp = fp;
+	s->nowarn = nowarn;
+	s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0;
+
+	wt_status_collect(s);
+
+	switch (status_format) {
+	case STATUS_FORMAT_SHORT:
+		wt_shortstatus_print(s);
+		break;
+	case STATUS_FORMAT_PORCELAIN:
+		wt_porcelain_print(s);
+		break;
+	case STATUS_FORMAT_LONG:
+		wt_status_print(s);
+		break;
+	}
+
+	return s->commitable;
+}
+
+static int is_a_merge(const struct commit *current_head)
+{
+	return !!(current_head->parents && current_head->parents->next);
+}
+
+static const char sign_off_header[] = "Signed-off-by: ";
+
+static void export_one(const char *var, const char *s, const char *e, int hack)
+{
+	struct strbuf buf = STRBUF_INIT;
+	if (hack)
+		strbuf_addch(&buf, hack);
+	strbuf_addf(&buf, "%.*s", (int)(e - s), s);
+	setenv(var, buf.buf, 1);
+	strbuf_release(&buf);
+}
+
+static void determine_author_info(struct strbuf *author_ident)
+{
+	char *name, *email, *date;
+	struct ident_split author;
+
+	name = getenv("GIT_AUTHOR_NAME");
+	email = getenv("GIT_AUTHOR_EMAIL");
+	date = getenv("GIT_AUTHOR_DATE");
+
+	if (author_message) {
+		const char *a, *lb, *rb, *eol;
+		size_t len;
+
+		a = strstr(author_message_buffer, "\nauthor ");
+		if (!a)
+			die(_("invalid commit: %s"), author_message);
+
+		lb = strchrnul(a + strlen("\nauthor "), '<');
+		rb = strchrnul(lb, '>');
+		eol = strchrnul(rb, '\n');
+		if (!*lb || !*rb || !*eol)
+			die(_("invalid commit: %s"), author_message);
+
+		if (lb == a + strlen("\nauthor "))
+			/* \nauthor <foo@example.com> */
+			name = xcalloc(1, 1);
+		else
+			name = xmemdupz(a + strlen("\nauthor "),
+					(lb - strlen(" ") -
+					 (a + strlen("\nauthor "))));
+		email = xmemdupz(lb + strlen("<"), rb - (lb + strlen("<")));
+		date = xmemdupz(rb + strlen("> "), eol - (rb + strlen("> ")));
+		len = eol - (rb + strlen("> "));
+		date = xmalloc(len + 2);
+		*date = '@';
+		memcpy(date + 1, rb + strlen("> "), len);
+		date[len + 1] = '\0';
+	}
+
+	if (force_author) {
+		const char *lb = strstr(force_author, " <");
+		const char *rb = strchr(force_author, '>');
+
+		if (!lb || !rb)
+			die(_("malformed --author parameter"));
+		name = xstrndup(force_author, lb - force_author);
+		email = xstrndup(lb + 2, rb - (lb + 2));
+	}
+
+	if (force_date)
+		date = force_date;
+	strbuf_addstr(author_ident, fmt_ident(name, email, date, IDENT_STRICT));
+	if (!split_ident_line(&author, author_ident->buf, author_ident->len)) {
+		export_one("GIT_AUTHOR_NAME", author.name_begin, author.name_end, 0);
+		export_one("GIT_AUTHOR_EMAIL", author.mail_begin, author.mail_end, 0);
+		export_one("GIT_AUTHOR_DATE", author.date_begin, author.tz_end, '@');
+	}
+}
+
+static int ends_rfc2822_footer(struct strbuf *sb)
+{
+	int ch;
+	int hit = 0;
+	int i, j, k;
+	int len = sb->len;
+	int first = 1;
+	const char *buf = sb->buf;
+
+	for (i = len - 1; i > 0; i--) {
+		if (hit && buf[i] == '\n')
+			break;
+		hit = (buf[i] == '\n');
+	}
+
+	while (i < len - 1 && buf[i] == '\n')
+		i++;
+
+	for (; i < len; i = k) {
+		for (k = i; k < len && buf[k] != '\n'; k++)
+			; /* do nothing */
+		k++;
+
+		if ((buf[k] == ' ' || buf[k] == '\t') && !first)
+			continue;
+
+		first = 0;
+
+		for (j = 0; i + j < len; j++) {
+			ch = buf[i + j];
+			if (ch == ':')
+				break;
+			if (isalnum(ch) ||
+			    (ch == '-'))
+				continue;
+			return 0;
+		}
+	}
+	return 1;
+}
+
+static char *cut_ident_timestamp_part(char *string)
+{
+	char *ket = strrchr(string, '>');
+	if (!ket || ket[1] != ' ')
+		die(_("Malformed ident string: '%s'"), string);
+	*++ket = '\0';
+	return ket;
+}
+
+static int prepare_to_commit(const char *index_file, const char *prefix,
+			     struct commit *current_head,
+			     struct wt_status *s,
+			     struct strbuf *author_ident)
+{
+	struct stat statbuf;
+	struct strbuf committer_ident = STRBUF_INIT;
+	int commitable, saved_color_setting;
+	struct strbuf sb = STRBUF_INIT;
+	char *buffer;
+	const char *hook_arg1 = NULL;
+	const char *hook_arg2 = NULL;
+	int ident_shown = 0;
+	int clean_message_contents = (cleanup_mode != CLEANUP_NONE);
+
+	/* This checks and barfs if author is badly specified */
+	determine_author_info(author_ident);
+
+	if (!no_verify && run_hook(index_file, "pre-commit", NULL))
+		return 0;
+
+	if (squash_message) {
+		/*
+		 * Insert the proper subject line before other commit
+		 * message options add their content.
+		 */
+		if (use_message && !strcmp(use_message, squash_message))
+			strbuf_addstr(&sb, "squash! ");
+		else {
+			struct pretty_print_context ctx = {0};
+			struct commit *c;
+			c = lookup_commit_reference_by_name(squash_message);
+			if (!c)
+				die(_("could not lookup commit %s"), squash_message);
+			ctx.output_encoding = get_commit_output_encoding();
+			format_commit_message(c, "squash! %s\n\n", &sb,
+					      &ctx);
+		}
+	}
+
+	if (message.len) {
+		strbuf_addbuf(&sb, &message);
+		hook_arg1 = "message";
+	} else if (logfile && !strcmp(logfile, "-")) {
+		if (isatty(0))
+			fprintf(stderr, _("(reading log message from standard input)\n"));
+		if (strbuf_read(&sb, 0, 0) < 0)
+			die_errno(_("could not read log from standard input"));
+		hook_arg1 = "message";
+	} else if (logfile) {
+		if (strbuf_read_file(&sb, logfile, 0) < 0)
+			die_errno(_("could not read log file '%s'"),
+				  logfile);
+		hook_arg1 = "message";
+	} else if (use_message) {
+		buffer = strstr(use_message_buffer, "\n\n");
+		if (!use_editor && (!buffer || buffer[2] == '\0'))
+			die(_("commit has empty message"));
+		strbuf_add(&sb, buffer + 2, strlen(buffer + 2));
+		hook_arg1 = "commit";
+		hook_arg2 = use_message;
+	} else if (fixup_message) {
+		struct pretty_print_context ctx = {0};
+		struct commit *commit;
+		commit = lookup_commit_reference_by_name(fixup_message);
+		if (!commit)
+			die(_("could not lookup commit %s"), fixup_message);
+		ctx.output_encoding = get_commit_output_encoding();
+		format_commit_message(commit, "fixup! %s\n\n",
+				      &sb, &ctx);
+		hook_arg1 = "message";
+	} else if (!stat(git_path("MERGE_MSG"), &statbuf)) {
+		if (strbuf_read_file(&sb, git_path("MERGE_MSG"), 0) < 0)
+			die_errno(_("could not read MERGE_MSG"));
+		hook_arg1 = "merge";
+	} else if (!stat(git_path("SQUASH_MSG"), &statbuf)) {
+		if (strbuf_read_file(&sb, git_path("SQUASH_MSG"), 0) < 0)
+			die_errno(_("could not read SQUASH_MSG"));
+		hook_arg1 = "squash";
+	} else if (template_file) {
+		if (strbuf_read_file(&sb, template_file, 0) < 0)
+			die_errno(_("could not read '%s'"), template_file);
+		hook_arg1 = "template";
+		clean_message_contents = 0;
+	}
+
+	/*
+	 * The remaining cases don't modify the template message, but
+	 * just set the argument(s) to the prepare-commit-msg hook.
+	 */
+	else if (whence == FROM_MERGE)
+		hook_arg1 = "merge";
+	else if (whence == FROM_CHERRY_PICK) {
+		hook_arg1 = "commit";
+		hook_arg2 = "CHERRY_PICK_HEAD";
+	}
+
+	if (squash_message) {
+		/*
+		 * If squash_commit was used for the commit subject,
+		 * then we're possibly hijacking other commit log options.
+		 * Reset the hook args to tell the real story.
+		 */
+		hook_arg1 = "message";
+		hook_arg2 = "";
+	}
+
+	s->fp = fopen(git_path(commit_editmsg), "w");
+	if (s->fp == NULL)
+		die_errno(_("could not open '%s'"), git_path(commit_editmsg));
+
+	if (clean_message_contents)
+		stripspace(&sb, 0);
+
+	if (signoff) {
+		struct strbuf sob = STRBUF_INIT;
+		int i;
+
+		strbuf_addstr(&sob, sign_off_header);
+		strbuf_addstr(&sob, fmt_name(getenv("GIT_COMMITTER_NAME"),
+					     getenv("GIT_COMMITTER_EMAIL")));
+		strbuf_addch(&sob, '\n');
+		for (i = sb.len - 1; i > 0 && sb.buf[i - 1] != '\n'; i--)
+			; /* do nothing */
+		if (prefixcmp(sb.buf + i, sob.buf)) {
+			if (!i || !ends_rfc2822_footer(&sb))
+				strbuf_addch(&sb, '\n');
+			strbuf_addbuf(&sb, &sob);
+		}
+		strbuf_release(&sob);
+	}
+
+	if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len)
+		die_errno(_("could not write commit template"));
+
+	strbuf_release(&sb);
+
+	/* This checks if committer ident is explicitly given */
+	strbuf_addstr(&committer_ident, git_committer_info(0));
+	if (use_editor && include_status) {
+		char *ai_tmp, *ci_tmp;
+		if (whence != FROM_COMMIT)
+			status_printf_ln(s, GIT_COLOR_NORMAL,
+			    whence == FROM_MERGE
+				? _("\n"
+					"It looks like you may be committing a merge.\n"
+					"If this is not correct, please remove the file\n"
+					"	%s\n"
+					"and try again.\n")
+				: _("\n"
+					"It looks like you may be committing a cherry-pick.\n"
+					"If this is not correct, please remove the file\n"
+					"	%s\n"
+					"and try again.\n"),
+				git_path(whence == FROM_MERGE
+					 ? "MERGE_HEAD"
+					 : "CHERRY_PICK_HEAD"));
+
+		fprintf(s->fp, "\n");
+		if (cleanup_mode == CLEANUP_ALL)
+			status_printf(s, GIT_COLOR_NORMAL,
+				_("Please enter the commit message for your changes."
+				" Lines starting\nwith '#' will be ignored, and an empty"
+				" message aborts the commit.\n"));
+		else /* CLEANUP_SPACE, that is. */
+			status_printf(s, GIT_COLOR_NORMAL,
+				_("Please enter the commit message for your changes."
+				" Lines starting\n"
+				"with '#' will be kept; you may remove them"
+				" yourself if you want to.\n"
+				"An empty message aborts the commit.\n"));
+		if (only_include_assumed)
+			status_printf_ln(s, GIT_COLOR_NORMAL,
+					"%s", only_include_assumed);
+
+		ai_tmp = cut_ident_timestamp_part(author_ident->buf);
+		ci_tmp = cut_ident_timestamp_part(committer_ident.buf);
+		if (strcmp(author_ident->buf, committer_ident.buf))
+			status_printf_ln(s, GIT_COLOR_NORMAL,
+				_("%s"
+				"Author:    %s"),
+				ident_shown++ ? "" : "\n",
+				author_ident->buf);
+
+		if (!user_ident_sufficiently_given())
+			status_printf_ln(s, GIT_COLOR_NORMAL,
+				_("%s"
+				"Committer: %s"),
+				ident_shown++ ? "" : "\n",
+				committer_ident.buf);
+
+		if (ident_shown)
+			status_printf_ln(s, GIT_COLOR_NORMAL, "");
+
+		saved_color_setting = s->use_color;
+		s->use_color = 0;
+		commitable = run_status(s->fp, index_file, prefix, 1, s);
+		s->use_color = saved_color_setting;
+
+		*ai_tmp = ' ';
+		*ci_tmp = ' ';
+	} else {
+		unsigned char sha1[20];
+		const char *parent = "HEAD";
+
+		if (!active_nr && read_cache() < 0)
+			die(_("Cannot read index"));
+
+		if (amend)
+			parent = "HEAD^1";
+
+		if (get_sha1(parent, sha1))
+			commitable = !!active_nr;
+		else
+			commitable = index_differs_from(parent, 0);
+	}
+	strbuf_release(&committer_ident);
+
+	fclose(s->fp);
+
+	/*
+	 * Reject an attempt to record a non-merge empty commit without
+	 * explicit --allow-empty. In the cherry-pick case, it may be
+	 * empty due to conflict resolution, which the user should okay.
+	 */
+	if (!commitable && whence != FROM_MERGE && !allow_empty &&
+	    !(amend && is_a_merge(current_head))) {
+		run_status(stdout, index_file, prefix, 0, s);
+		if (amend)
+			fputs(_(empty_amend_advice), stderr);
+		else if (whence == FROM_CHERRY_PICK)
+			fputs(_(empty_cherry_pick_advice), stderr);
+		return 0;
+	}
+
+	/*
+	 * Re-read the index as pre-commit hook could have updated it,
+	 * and write it out as a tree.  We must do this before we invoke
+	 * the editor and after we invoke run_status above.
+	 */
+	discard_cache();
+	read_cache_from(index_file);
+	if (update_main_cache_tree(0)) {
+		error(_("Error building trees"));
+		return 0;
+	}
+
+	if (run_hook(index_file, "prepare-commit-msg",
+		     git_path(commit_editmsg), hook_arg1, hook_arg2, NULL))
+		return 0;
+
+	if (use_editor) {
+		char index[PATH_MAX];
+		const char *env[2] = { NULL };
+		env[0] =  index;
+		snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
+		if (launch_editor(git_path(commit_editmsg), NULL, env)) {
+			fprintf(stderr,
+			_("Please supply the message using either -m or -F option.\n"));
+			exit(1);
+		}
+	}
+
+	if (!no_verify &&
+	    run_hook(index_file, "commit-msg", git_path(commit_editmsg), NULL)) {
+		return 0;
+	}
+
+	return 1;
+}
+
+static int rest_is_empty(struct strbuf *sb, int start)
+{
+	int i, eol;
+	const char *nl;
+
+	/* Check if the rest is just whitespace and Signed-of-by's. */
+	for (i = start; i < sb->len; i++) {
+		nl = memchr(sb->buf + i, '\n', sb->len - i);
+		if (nl)
+			eol = nl - sb->buf;
+		else
+			eol = sb->len;
+
+		if (strlen(sign_off_header) <= eol - i &&
+		    !prefixcmp(sb->buf + i, sign_off_header)) {
+			i = eol;
+			continue;
+		}
+		while (i < eol)
+			if (!isspace(sb->buf[i++]))
+				return 0;
+	}
+
+	return 1;
+}
+
+/*
+ * Find out if the message in the strbuf contains only whitespace and
+ * Signed-off-by lines.
+ */
+static int message_is_empty(struct strbuf *sb)
+{
+	if (cleanup_mode == CLEANUP_NONE && sb->len)
+		return 0;
+	return rest_is_empty(sb, 0);
+}
+
+/*
+ * See if the user edited the message in the editor or left what
+ * was in the template intact
+ */
+static int template_untouched(struct strbuf *sb)
+{
+	struct strbuf tmpl = STRBUF_INIT;
+	char *start;
+
+	if (cleanup_mode == CLEANUP_NONE && sb->len)
+		return 0;
+
+	if (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)
+		return 0;
+
+	stripspace(&tmpl, cleanup_mode == CLEANUP_ALL);
+	start = (char *)skip_prefix(sb->buf, tmpl.buf);
+	if (!start)
+		start = sb->buf;
+	strbuf_release(&tmpl);
+	return rest_is_empty(sb, start - sb->buf);
+}
+
+static const char *find_author_by_nickname(const char *name)
+{
+	struct rev_info revs;
+	struct commit *commit;
+	struct strbuf buf = STRBUF_INIT;
+	const char *av[20];
+	int ac = 0;
+
+	init_revisions(&revs, NULL);
+	strbuf_addf(&buf, "--author=%s", name);
+	av[++ac] = "--all";
+	av[++ac] = "-i";
+	av[++ac] = buf.buf;
+	av[++ac] = NULL;
+	setup_revisions(ac, av, &revs, NULL);
+	prepare_revision_walk(&revs);
+	commit = get_revision(&revs);
+	if (commit) {
+		struct pretty_print_context ctx = {0};
+		ctx.date_mode = DATE_NORMAL;
+		strbuf_release(&buf);
+		format_commit_message(commit, "%an <%ae>", &buf, &ctx);
+		return strbuf_detach(&buf, NULL);
+	}
+	die(_("No existing author found with '%s'"), name);
+}
+
+
+static void handle_untracked_files_arg(struct wt_status *s)
+{
+	if (!untracked_files_arg)
+		; /* default already initialized */
+	else if (!strcmp(untracked_files_arg, "no"))
+		s->show_untracked_files = SHOW_NO_UNTRACKED_FILES;
+	else if (!strcmp(untracked_files_arg, "normal"))
+		s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
+	else if (!strcmp(untracked_files_arg, "all"))
+		s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
+	else
+		die(_("Invalid untracked files mode '%s'"), untracked_files_arg);
+}
+
+static const char *read_commit_message(const char *name)
+{
+	const char *out_enc, *out;
+	struct commit *commit;
+
+	commit = lookup_commit_reference_by_name(name);
+	if (!commit)
+		die(_("could not lookup commit %s"), name);
+	out_enc = get_commit_output_encoding();
+	out = logmsg_reencode(commit, out_enc);
+
+	/*
+	 * If we failed to reencode the buffer, just copy it
+	 * byte for byte so the user can try to fix it up.
+	 * This also handles the case where input and output
+	 * encodings are identical.
+	 */
+	if (out == NULL)
+		out = xstrdup(commit->buffer);
+	return out;
+}
+
+static int parse_and_validate_options(int argc, const char *argv[],
+				      const struct option *options,
+				      const char * const usage[],
+				      const char *prefix,
+				      struct commit *current_head,
+				      struct wt_status *s)
+{
+	int f = 0;
+
+	argc = parse_options(argc, argv, prefix, options, usage, 0);
+
+	if (force_author && !strchr(force_author, '>'))
+		force_author = find_author_by_nickname(force_author);
+
+	if (force_author && renew_authorship)
+		die(_("Using both --reset-author and --author does not make sense"));
+
+	if (logfile || message.len || use_message || fixup_message)
+		use_editor = 0;
+	if (0 <= edit_flag)
+		use_editor = edit_flag;
+	if (!use_editor)
+		setenv("GIT_EDITOR", ":", 1);
+
+	/* Sanity check options */
+	if (amend && !current_head)
+		die(_("You have nothing to amend."));
+	if (amend && whence != FROM_COMMIT) {
+		if (whence == FROM_MERGE)
+			die(_("You are in the middle of a merge -- cannot amend."));
+		else if (whence == FROM_CHERRY_PICK)
+			die(_("You are in the middle of a cherry-pick -- cannot amend."));
+	}
+	if (fixup_message && squash_message)
+		die(_("Options --squash and --fixup cannot be used together"));
+	if (use_message)
+		f++;
+	if (edit_message)
+		f++;
+	if (fixup_message)
+		f++;
+	if (logfile)
+		f++;
+	if (f > 1)
+		die(_("Only one of -c/-C/-F/--fixup can be used."));
+	if (message.len && f > 0)
+		die((_("Option -m cannot be combined with -c/-C/-F/--fixup.")));
+	if (f || message.len)
+		template_file = NULL;
+	if (edit_message)
+		use_message = edit_message;
+	if (amend && !use_message && !fixup_message)
+		use_message = "HEAD";
+	if (!use_message && whence != FROM_CHERRY_PICK && renew_authorship)
+		die(_("--reset-author can be used only with -C, -c or --amend."));
+	if (use_message) {
+		use_message_buffer = read_commit_message(use_message);
+		if (!renew_authorship) {
+			author_message = use_message;
+			author_message_buffer = use_message_buffer;
+		}
+	}
+	if (whence == FROM_CHERRY_PICK && !renew_authorship) {
+		author_message = "CHERRY_PICK_HEAD";
+		author_message_buffer = read_commit_message(author_message);
+	}
+
+	if (patch_interactive)
+		interactive = 1;
+
+	if (!!also + !!only + !!all + !!interactive > 1)
+		die(_("Only one of --include/--only/--all/--interactive/--patch can be used."));
+	if (argc == 0 && (also || (only && !amend)))
+		die(_("No paths with --include/--only does not make sense."));
+	if (argc == 0 && only && amend)
+		only_include_assumed = _("Clever... amending the last one with dirty index.");
+	if (argc > 0 && !also && !only)
+		only_include_assumed = _("Explicit paths specified without -i nor -o; assuming --only paths...");
+	if (!cleanup_arg || !strcmp(cleanup_arg, "default"))
+		cleanup_mode = use_editor ? CLEANUP_ALL : CLEANUP_SPACE;
+	else if (!strcmp(cleanup_arg, "verbatim"))
+		cleanup_mode = CLEANUP_NONE;
+	else if (!strcmp(cleanup_arg, "whitespace"))
+		cleanup_mode = CLEANUP_SPACE;
+	else if (!strcmp(cleanup_arg, "strip"))
+		cleanup_mode = CLEANUP_ALL;
+	else
+		die(_("Invalid cleanup mode %s"), cleanup_arg);
+
+	handle_untracked_files_arg(s);
+
+	if (all && argc > 0)
+		die(_("Paths with -a does not make sense."));
+
+	if (s->null_termination && status_format == STATUS_FORMAT_LONG)
+		status_format = STATUS_FORMAT_PORCELAIN;
+	if (status_format != STATUS_FORMAT_LONG)
+		dry_run = 1;
+
+	return argc;
+}
+
+static int dry_run_commit(int argc, const char **argv, const char *prefix,
+			  const struct commit *current_head, struct wt_status *s)
+{
+	int commitable;
+	const char *index_file;
+
+	index_file = prepare_index(argc, argv, prefix, current_head, 1);
+	commitable = run_status(stdout, index_file, prefix, 0, s);
+	rollback_index_files();
+
+	return commitable ? 0 : 1;
+}
+
+static int parse_status_slot(const char *var, int offset)
+{
+	if (!strcasecmp(var+offset, "header"))
+		return WT_STATUS_HEADER;
+	if (!strcasecmp(var+offset, "branch"))
+		return WT_STATUS_ONBRANCH;
+	if (!strcasecmp(var+offset, "updated")
+		|| !strcasecmp(var+offset, "added"))
+		return WT_STATUS_UPDATED;
+	if (!strcasecmp(var+offset, "changed"))
+		return WT_STATUS_CHANGED;
+	if (!strcasecmp(var+offset, "untracked"))
+		return WT_STATUS_UNTRACKED;
+	if (!strcasecmp(var+offset, "nobranch"))
+		return WT_STATUS_NOBRANCH;
+	if (!strcasecmp(var+offset, "unmerged"))
+		return WT_STATUS_UNMERGED;
+	return -1;
+}
+
+static int git_status_config(const char *k, const char *v, void *cb)
+{
+	struct wt_status *s = cb;
+
+	if (!prefixcmp(k, "column."))
+		return git_column_config(k, v, "status", &s->colopts);
+	if (!strcmp(k, "status.submodulesummary")) {
+		int is_bool;
+		s->submodule_summary = git_config_bool_or_int(k, v, &is_bool);
+		if (is_bool && s->submodule_summary)
+			s->submodule_summary = -1;
+		return 0;
+	}
+	if (!strcmp(k, "status.color") || !strcmp(k, "color.status")) {
+		s->use_color = git_config_colorbool(k, v);
+		return 0;
+	}
+	if (!prefixcmp(k, "status.color.") || !prefixcmp(k, "color.status.")) {
+		int slot = parse_status_slot(k, 13);
+		if (slot < 0)
+			return 0;
+		if (!v)
+			return config_error_nonbool(k);
+		color_parse(v, k, s->color_palette[slot]);
+		return 0;
+	}
+	if (!strcmp(k, "status.relativepaths")) {
+		s->relative_paths = git_config_bool(k, v);
+		return 0;
+	}
+	if (!strcmp(k, "status.showuntrackedfiles")) {
+		if (!v)
+			return config_error_nonbool(k);
+		else if (!strcmp(v, "no"))
+			s->show_untracked_files = SHOW_NO_UNTRACKED_FILES;
+		else if (!strcmp(v, "normal"))
+			s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
+		else if (!strcmp(v, "all"))
+			s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
+		else
+			return error(_("Invalid untracked files mode '%s'"), v);
+		return 0;
+	}
+	return git_diff_ui_config(k, v, NULL);
+}
+
+int cmd_status(int argc, const char **argv, const char *prefix)
+{
+	static struct wt_status s;
+	int fd;
+	unsigned char sha1[20];
+	static struct option builtin_status_options[] = {
+		OPT__VERBOSE(&verbose, "be verbose"),
+		OPT_SET_INT('s', "short", &status_format,
+			    "show status concisely", STATUS_FORMAT_SHORT),
+		OPT_BOOLEAN('b', "branch", &s.show_branch,
+			    "show branch information"),
+		OPT_SET_INT(0, "porcelain", &status_format,
+			    "machine-readable output",
+			    STATUS_FORMAT_PORCELAIN),
+		OPT_BOOLEAN('z', "null", &s.null_termination,
+			    "terminate entries with NUL"),
+		{ OPTION_STRING, 'u', "untracked-files", &untracked_files_arg,
+		  "mode",
+		  "show untracked files, optional modes: all, normal, no. (Default: all)",
+		  PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
+		OPT_BOOLEAN(0, "ignored", &show_ignored_in_status,
+			    "show ignored files"),
+		{ OPTION_STRING, 0, "ignore-submodules", &ignore_submodule_arg, "when",
+		  "ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)",
+		  PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
+		OPT_COLUMN(0, "column", &s.colopts, "list untracked files in columns"),
+		OPT_END(),
+	};
+
+	if (argc == 2 && !strcmp(argv[1], "-h"))
+		usage_with_options(builtin_status_usage, builtin_status_options);
+
+	wt_status_prepare(&s);
+	gitmodules_config();
+	git_config(git_status_config, &s);
+	determine_whence(&s);
+	argc = parse_options(argc, argv, prefix,
+			     builtin_status_options,
+			     builtin_status_usage, 0);
+	finalize_colopts(&s.colopts, -1);
+
+	if (s.null_termination && status_format == STATUS_FORMAT_LONG)
+		status_format = STATUS_FORMAT_PORCELAIN;
+
+	handle_untracked_files_arg(&s);
+	if (show_ignored_in_status)
+		s.show_ignored_files = 1;
+	if (*argv)
+		s.pathspec = get_pathspec(prefix, argv);
+
+	read_cache_preload(s.pathspec);
+	refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, s.pathspec, NULL, NULL);
+
+	fd = hold_locked_index(&index_lock, 0);
+	if (0 <= fd)
+		update_index_if_able(&the_index, &index_lock);
+
+	s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0;
+	s.ignore_submodule_arg = ignore_submodule_arg;
+	wt_status_collect(&s);
+
+	if (s.relative_paths)
+		s.prefix = prefix;
+
+	switch (status_format) {
+	case STATUS_FORMAT_SHORT:
+		wt_shortstatus_print(&s);
+		break;
+	case STATUS_FORMAT_PORCELAIN:
+		wt_porcelain_print(&s);
+		break;
+	case STATUS_FORMAT_LONG:
+		s.verbose = verbose;
+		s.ignore_submodule_arg = ignore_submodule_arg;
+		wt_status_print(&s);
+		break;
+	}
+	return 0;
+}
+
+static void print_summary(const char *prefix, const unsigned char *sha1,
+			  int initial_commit)
+{
+	struct rev_info rev;
+	struct commit *commit;
+	struct strbuf format = STRBUF_INIT;
+	unsigned char junk_sha1[20];
+	const char *head;
+	struct pretty_print_context pctx = {0};
+	struct strbuf author_ident = STRBUF_INIT;
+	struct strbuf committer_ident = STRBUF_INIT;
+
+	commit = lookup_commit(sha1);
+	if (!commit)
+		die(_("couldn't look up newly created commit"));
+	if (!commit || parse_commit(commit))
+		die(_("could not parse newly created commit"));
+
+	strbuf_addstr(&format, "format:%h] %s");
+
+	format_commit_message(commit, "%an <%ae>", &author_ident, &pctx);
+	format_commit_message(commit, "%cn <%ce>", &committer_ident, &pctx);
+	if (strbuf_cmp(&author_ident, &committer_ident)) {
+		strbuf_addstr(&format, "\n Author: ");
+		strbuf_addbuf_percentquote(&format, &author_ident);
+	}
+	if (!user_ident_sufficiently_given()) {
+		strbuf_addstr(&format, "\n Committer: ");
+		strbuf_addbuf_percentquote(&format, &committer_ident);
+		if (advice_implicit_identity) {
+			strbuf_addch(&format, '\n');
+			strbuf_addstr(&format, _(implicit_ident_advice));
+		}
+	}
+	strbuf_release(&author_ident);
+	strbuf_release(&committer_ident);
+
+	init_revisions(&rev, prefix);
+	setup_revisions(0, NULL, &rev, NULL);
+
+	rev.diff = 1;
+	rev.diffopt.output_format =
+		DIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;
+
+	rev.verbose_header = 1;
+	rev.show_root_diff = 1;
+	get_commit_format(format.buf, &rev);
+	rev.always_show_header = 0;
+	rev.diffopt.detect_rename = 1;
+	rev.diffopt.break_opt = 0;
+	diff_setup_done(&rev.diffopt);
+
+	head = resolve_ref_unsafe("HEAD", junk_sha1, 0, NULL);
+	printf("[%s%s ",
+		!prefixcmp(head, "refs/heads/") ?
+			head + 11 :
+			!strcmp(head, "HEAD") ?
+				_("detached HEAD") :
+				head,
+		initial_commit ? _(" (root-commit)") : "");
+
+	if (!log_tree_commit(&rev, commit)) {
+		rev.always_show_header = 1;
+		rev.use_terminator = 1;
+		log_tree_commit(&rev, commit);
+	}
+
+	strbuf_release(&format);
+}
+
+static int git_commit_config(const char *k, const char *v, void *cb)
+{
+	struct wt_status *s = cb;
+	int status;
+
+	if (!strcmp(k, "commit.template"))
+		return git_config_pathname(&template_file, k, v);
+	if (!strcmp(k, "commit.status")) {
+		include_status = git_config_bool(k, v);
+		return 0;
+	}
+
+	status = git_gpg_config(k, v, NULL);
+	if (status)
+		return status;
+	return git_status_config(k, v, s);
+}
+
+static const char post_rewrite_hook[] = "hooks/post-rewrite";
+
+static int run_rewrite_hook(const unsigned char *oldsha1,
+			    const unsigned char *newsha1)
+{
+	/* oldsha1 SP newsha1 LF NUL */
+	static char buf[2*40 + 3];
+	struct child_process proc;
+	const char *argv[3];
+	int code;
+	size_t n;
+
+	if (access(git_path(post_rewrite_hook), X_OK) < 0)
+		return 0;
+
+	argv[0] = git_path(post_rewrite_hook);
+	argv[1] = "amend";
+	argv[2] = NULL;
+
+	memset(&proc, 0, sizeof(proc));
+	proc.argv = argv;
+	proc.in = -1;
+	proc.stdout_to_stderr = 1;
+
+	code = start_command(&proc);
+	if (code)
+		return code;
+	n = snprintf(buf, sizeof(buf), "%s %s\n",
+		     sha1_to_hex(oldsha1), sha1_to_hex(newsha1));
+	write_in_full(proc.in, buf, n);
+	close(proc.in);
+	return finish_command(&proc);
+}
+
+int cmd_commit(int argc, const char **argv, const char *prefix)
+{
+	static struct wt_status s;
+	static struct option builtin_commit_options[] = {
+		OPT__QUIET(&quiet, "suppress summary after successful commit"),
+		OPT__VERBOSE(&verbose, "show diff in commit message template"),
+
+		OPT_GROUP("Commit message options"),
+		OPT_FILENAME('F', "file", &logfile, "read message from file"),
+		OPT_STRING(0, "author", &force_author, "author", "override author for commit"),
+		OPT_STRING(0, "date", &force_date, "date", "override date for commit"),
+		OPT_CALLBACK('m', "message", &message, "message", "commit message", opt_parse_m),
+		OPT_STRING('c', "reedit-message", &edit_message, "commit", "reuse and edit message from specified commit"),
+		OPT_STRING('C', "reuse-message", &use_message, "commit", "reuse message from specified commit"),
+		OPT_STRING(0, "fixup", &fixup_message, "commit", "use autosquash formatted message to fixup specified commit"),
+		OPT_STRING(0, "squash", &squash_message, "commit", "use autosquash formatted message to squash specified commit"),
+		OPT_BOOLEAN(0, "reset-author", &renew_authorship, "the commit is authored by me now (used with -C/-c/--amend)"),
+		OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
+		OPT_FILENAME('t', "template", &template_file, "use specified template file"),
+		OPT_BOOL('e', "edit", &edit_flag, "force edit of commit"),
+		OPT_STRING(0, "cleanup", &cleanup_arg, "default", "how to strip spaces and #comments from message"),
+		OPT_BOOLEAN(0, "status", &include_status, "include status in commit message template"),
+		{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, "key id",
+		  "GPG sign commit", PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+		/* end commit message options */
+
+		OPT_GROUP("Commit contents options"),
+		OPT_BOOLEAN('a', "all", &all, "commit all changed files"),
+		OPT_BOOLEAN('i', "include", &also, "add specified files to index for commit"),
+		OPT_BOOLEAN(0, "interactive", &interactive, "interactively add files"),
+		OPT_BOOLEAN('p', "patch", &patch_interactive, "interactively add changes"),
+		OPT_BOOLEAN('o', "only", &only, "commit only specified files"),
+		OPT_BOOLEAN('n', "no-verify", &no_verify, "bypass pre-commit hook"),
+		OPT_BOOLEAN(0, "dry-run", &dry_run, "show what would be committed"),
+		OPT_SET_INT(0, "short", &status_format, "show status concisely",
+			    STATUS_FORMAT_SHORT),
+		OPT_BOOLEAN(0, "branch", &s.show_branch, "show branch information"),
+		OPT_SET_INT(0, "porcelain", &status_format,
+			    "machine-readable output", STATUS_FORMAT_PORCELAIN),
+		OPT_BOOLEAN('z', "null", &s.null_termination,
+			    "terminate entries with NUL"),
+		OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
+		OPT_BOOLEAN(0, "no-post-rewrite", &no_post_rewrite, "bypass post-rewrite hook"),
+		{ OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
+		/* end commit contents options */
+
+		{ OPTION_BOOLEAN, 0, "allow-empty", &allow_empty, NULL,
+		  "ok to record an empty change",
+		  PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
+		{ OPTION_BOOLEAN, 0, "allow-empty-message", &allow_empty_message, NULL,
+		  "ok to record a change with an empty message",
+		  PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
+
+		OPT_END()
+	};
+
+	struct strbuf sb = STRBUF_INIT;
+	struct strbuf author_ident = STRBUF_INIT;
+	const char *index_file, *reflog_msg;
+	char *nl, *p;
+	unsigned char sha1[20];
+	struct ref_lock *ref_lock;
+	struct commit_list *parents = NULL, **pptr = &parents;
+	struct stat statbuf;
+	int allow_fast_forward = 1;
+	struct commit *current_head = NULL;
+	struct commit_extra_header *extra = NULL;
+
+	if (argc == 2 && !strcmp(argv[1], "-h"))
+		usage_with_options(builtin_commit_usage, builtin_commit_options);
+
+	wt_status_prepare(&s);
+	git_config(git_commit_config, &s);
+	determine_whence(&s);
+	s.colopts = 0;
+
+	if (get_sha1("HEAD", sha1))
+		current_head = NULL;
+	else {
+		current_head = lookup_commit_or_die(sha1, "HEAD");
+		if (!current_head || parse_commit(current_head))
+			die(_("could not parse HEAD commit"));
+	}
+	argc = parse_and_validate_options(argc, argv, builtin_commit_options,
+					  builtin_commit_usage,
+					  prefix, current_head, &s);
+	if (dry_run)
+		return dry_run_commit(argc, argv, prefix, current_head, &s);
+	index_file = prepare_index(argc, argv, prefix, current_head, 0);
+
+	/* Set up everything for writing the commit object.  This includes
+	   running hooks, writing the trees, and interacting with the user.  */
+	if (!prepare_to_commit(index_file, prefix,
+			       current_head, &s, &author_ident)) {
+		rollback_index_files();
+		return 1;
+	}
+
+	/* Determine parents */
+	reflog_msg = getenv("GIT_REFLOG_ACTION");
+	if (!current_head) {
+		if (!reflog_msg)
+			reflog_msg = "commit (initial)";
+	} else if (amend) {
+		struct commit_list *c;
+
+		if (!reflog_msg)
+			reflog_msg = "commit (amend)";
+		for (c = current_head->parents; c; c = c->next)
+			pptr = &commit_list_insert(c->item, pptr)->next;
+	} else if (whence == FROM_MERGE) {
+		struct strbuf m = STRBUF_INIT;
+		FILE *fp;
+
+		if (!reflog_msg)
+			reflog_msg = "commit (merge)";
+		pptr = &commit_list_insert(current_head, pptr)->next;
+		fp = fopen(git_path("MERGE_HEAD"), "r");
+		if (fp == NULL)
+			die_errno(_("could not open '%s' for reading"),
+				  git_path("MERGE_HEAD"));
+		while (strbuf_getline(&m, fp, '\n') != EOF) {
+			struct commit *parent;
+
+			parent = get_merge_parent(m.buf);
+			if (!parent)
+				die(_("Corrupt MERGE_HEAD file (%s)"), m.buf);
+			pptr = &commit_list_insert(parent, pptr)->next;
+		}
+		fclose(fp);
+		strbuf_release(&m);
+		if (!stat(git_path("MERGE_MODE"), &statbuf)) {
+			if (strbuf_read_file(&sb, git_path("MERGE_MODE"), 0) < 0)
+				die_errno(_("could not read MERGE_MODE"));
+			if (!strcmp(sb.buf, "no-ff"))
+				allow_fast_forward = 0;
+		}
+		if (allow_fast_forward)
+			parents = reduce_heads(parents);
+	} else {
+		if (!reflog_msg)
+			reflog_msg = (whence == FROM_CHERRY_PICK)
+					? "commit (cherry-pick)"
+					: "commit";
+		pptr = &commit_list_insert(current_head, pptr)->next;
+	}
+
+	/* Finally, get the commit message */
+	strbuf_reset(&sb);
+	if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) {
+		int saved_errno = errno;
+		rollback_index_files();
+		die(_("could not read commit message: %s"), strerror(saved_errno));
+	}
+
+	/* Truncate the message just before the diff, if any. */
+	if (verbose) {
+		p = strstr(sb.buf, "\ndiff --git ");
+		if (p != NULL)
+			strbuf_setlen(&sb, p - sb.buf + 1);
+	}
+
+	if (cleanup_mode != CLEANUP_NONE)
+		stripspace(&sb, cleanup_mode == CLEANUP_ALL);
+	if (template_untouched(&sb) && !allow_empty_message) {
+		rollback_index_files();
+		fprintf(stderr, _("Aborting commit; you did not edit the message.\n"));
+		exit(1);
+	}
+	if (message_is_empty(&sb) && !allow_empty_message) {
+		rollback_index_files();
+		fprintf(stderr, _("Aborting commit due to empty commit message.\n"));
+		exit(1);
+	}
+
+	if (amend) {
+		const char *exclude_gpgsig[2] = { "gpgsig", NULL };
+		extra = read_commit_extra_headers(current_head, exclude_gpgsig);
+	} else {
+		struct commit_extra_header **tail = &extra;
+		append_merge_tag_headers(parents, &tail);
+	}
+
+	if (commit_tree_extended(&sb, active_cache_tree->sha1, parents, sha1,
+				 author_ident.buf, sign_commit, extra)) {
+		rollback_index_files();
+		die(_("failed to write commit object"));
+	}
+	strbuf_release(&author_ident);
+	free_commit_extra_headers(extra);
+
+	ref_lock = lock_any_ref_for_update("HEAD",
+					   !current_head
+					   ? NULL
+					   : current_head->object.sha1,
+					   0);
+
+	nl = strchr(sb.buf, '\n');
+	if (nl)
+		strbuf_setlen(&sb, nl + 1 - sb.buf);
+	else
+		strbuf_addch(&sb, '\n');
+	strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));
+	strbuf_insert(&sb, strlen(reflog_msg), ": ", 2);
+
+	if (!ref_lock) {
+		rollback_index_files();
+		die(_("cannot lock HEAD ref"));
+	}
+	if (write_ref_sha1(ref_lock, sha1, sb.buf) < 0) {
+		rollback_index_files();
+		die(_("cannot update HEAD ref"));
+	}
+
+	unlink(git_path("CHERRY_PICK_HEAD"));
+	unlink(git_path("REVERT_HEAD"));
+	unlink(git_path("MERGE_HEAD"));
+	unlink(git_path("MERGE_MSG"));
+	unlink(git_path("MERGE_MODE"));
+	unlink(git_path("SQUASH_MSG"));
+
+	if (commit_index_files())
+		die (_("Repository has been updated, but unable to write\n"
+		     "new_index file. Check that disk is not full or quota is\n"
+		     "not exceeded, and then \"git reset HEAD\" to recover."));
+
+	rerere(0);
+	run_hook(get_index_file(), "post-commit", NULL);
+	if (amend && !no_post_rewrite) {
+		struct notes_rewrite_cfg *cfg;
+		cfg = init_copy_notes_for_rewrite("amend");
+		if (cfg) {
+			/* we are amending, so current_head is not NULL */
+			copy_note_for_rewrite(cfg, current_head->object.sha1, sha1);
+			finish_copy_notes_for_rewrite(cfg);
+		}
+		run_rewrite_hook(current_head->object.sha1, sha1);
+	}
+	if (!quiet)
+		print_summary(prefix, sha1, !current_head);
+
+	return 0;
+}
diff --git a/builtin/config.c b/builtin/config.c
new file mode 100644
index 0000000..8cd08da
--- /dev/null
+++ b/builtin/config.c
@@ -0,0 +1,569 @@
+#include "builtin.h"
+#include "cache.h"
+#include "color.h"
+#include "parse-options.h"
+
+static const char *const builtin_config_usage[] = {
+	"git config [options]",
+	NULL
+};
+
+static char *key;
+static regex_t *key_regexp;
+static regex_t *regexp;
+static int show_keys;
+static int use_key_regexp;
+static int do_all;
+static int do_not_match;
+static int seen;
+static char delim = '=';
+static char key_delim = ' ';
+static char term = '\n';
+
+static int use_global_config, use_system_config, use_local_config;
+static const char *given_config_file;
+static int actions, types;
+static const char *get_color_slot, *get_colorbool_slot;
+static int end_null;
+static int respect_includes = -1;
+
+#define ACTION_GET (1<<0)
+#define ACTION_GET_ALL (1<<1)
+#define ACTION_GET_REGEXP (1<<2)
+#define ACTION_REPLACE_ALL (1<<3)
+#define ACTION_ADD (1<<4)
+#define ACTION_UNSET (1<<5)
+#define ACTION_UNSET_ALL (1<<6)
+#define ACTION_RENAME_SECTION (1<<7)
+#define ACTION_REMOVE_SECTION (1<<8)
+#define ACTION_LIST (1<<9)
+#define ACTION_EDIT (1<<10)
+#define ACTION_SET (1<<11)
+#define ACTION_SET_ALL (1<<12)
+#define ACTION_GET_COLOR (1<<13)
+#define ACTION_GET_COLORBOOL (1<<14)
+
+#define TYPE_BOOL (1<<0)
+#define TYPE_INT (1<<1)
+#define TYPE_BOOL_OR_INT (1<<2)
+#define TYPE_PATH (1<<3)
+
+static struct option builtin_config_options[] = {
+	OPT_GROUP("Config file location"),
+	OPT_BOOLEAN(0, "global", &use_global_config, "use global config file"),
+	OPT_BOOLEAN(0, "system", &use_system_config, "use system config file"),
+	OPT_BOOLEAN(0, "local", &use_local_config, "use repository config file"),
+	OPT_STRING('f', "file", &given_config_file, "file", "use given config file"),
+	OPT_GROUP("Action"),
+	OPT_BIT(0, "get", &actions, "get value: name [value-regex]", ACTION_GET),
+	OPT_BIT(0, "get-all", &actions, "get all values: key [value-regex]", ACTION_GET_ALL),
+	OPT_BIT(0, "get-regexp", &actions, "get values for regexp: name-regex [value-regex]", ACTION_GET_REGEXP),
+	OPT_BIT(0, "replace-all", &actions, "replace all matching variables: name value [value_regex]", ACTION_REPLACE_ALL),
+	OPT_BIT(0, "add", &actions, "adds a new variable: name value", ACTION_ADD),
+	OPT_BIT(0, "unset", &actions, "removes a variable: name [value-regex]", ACTION_UNSET),
+	OPT_BIT(0, "unset-all", &actions, "removes all matches: name [value-regex]", ACTION_UNSET_ALL),
+	OPT_BIT(0, "rename-section", &actions, "rename section: old-name new-name", ACTION_RENAME_SECTION),
+	OPT_BIT(0, "remove-section", &actions, "remove a section: name", ACTION_REMOVE_SECTION),
+	OPT_BIT('l', "list", &actions, "list all", ACTION_LIST),
+	OPT_BIT('e', "edit", &actions, "opens an editor", ACTION_EDIT),
+	OPT_STRING(0, "get-color", &get_color_slot, "slot", "find the color configured: [default]"),
+	OPT_STRING(0, "get-colorbool", &get_colorbool_slot, "slot", "find the color setting: [stdout-is-tty]"),
+	OPT_GROUP("Type"),
+	OPT_BIT(0, "bool", &types, "value is \"true\" or \"false\"", TYPE_BOOL),
+	OPT_BIT(0, "int", &types, "value is decimal number", TYPE_INT),
+	OPT_BIT(0, "bool-or-int", &types, "value is --bool or --int", TYPE_BOOL_OR_INT),
+	OPT_BIT(0, "path", &types, "value is a path (file or directory name)", TYPE_PATH),
+	OPT_GROUP("Other"),
+	OPT_BOOLEAN('z', "null", &end_null, "terminate values with NUL byte"),
+	OPT_BOOL(0, "includes", &respect_includes, "respect include directives on lookup"),
+	OPT_END(),
+};
+
+static void check_argc(int argc, int min, int max) {
+	if (argc >= min && argc <= max)
+		return;
+	error("wrong number of arguments");
+	usage_with_options(builtin_config_usage, builtin_config_options);
+}
+
+static int show_all_config(const char *key_, const char *value_, void *cb)
+{
+	if (value_)
+		printf("%s%c%s%c", key_, delim, value_, term);
+	else
+		printf("%s%c", key_, term);
+	return 0;
+}
+
+static int show_config(const char *key_, const char *value_, void *cb)
+{
+	char value[256];
+	const char *vptr = value;
+	int must_free_vptr = 0;
+	int dup_error = 0;
+	int must_print_delim = 0;
+
+	if (!use_key_regexp && strcmp(key_, key))
+		return 0;
+	if (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0))
+		return 0;
+	if (regexp != NULL &&
+	    (do_not_match ^ !!regexec(regexp, (value_?value_:""), 0, NULL, 0)))
+		return 0;
+
+	if (show_keys) {
+		printf("%s", key_);
+		must_print_delim = 1;
+	}
+	if (seen && !do_all)
+		dup_error = 1;
+	if (types == TYPE_INT)
+		sprintf(value, "%d", git_config_int(key_, value_?value_:""));
+	else if (types == TYPE_BOOL)
+		vptr = git_config_bool(key_, value_) ? "true" : "false";
+	else if (types == TYPE_BOOL_OR_INT) {
+		int is_bool, v;
+		v = git_config_bool_or_int(key_, value_, &is_bool);
+		if (is_bool)
+			vptr = v ? "true" : "false";
+		else
+			sprintf(value, "%d", v);
+	} else if (types == TYPE_PATH) {
+		git_config_pathname(&vptr, key_, value_);
+		must_free_vptr = 1;
+	} else if (value_) {
+		vptr = value_;
+	} else {
+		/* Just show the key name */
+		vptr = "";
+		must_print_delim = 0;
+	}
+	seen++;
+	if (dup_error) {
+		error("More than one value for the key %s: %s",
+				key_, vptr);
+	}
+	else {
+		if (must_print_delim)
+			printf("%c", key_delim);
+		printf("%s%c", vptr, term);
+	}
+	if (must_free_vptr)
+		/* If vptr must be freed, it's a pointer to a
+		 * dynamically allocated buffer, it's safe to cast to
+		 * const.
+		*/
+		free((char *)vptr);
+
+	return 0;
+}
+
+static int get_value(const char *key_, const char *regex_)
+{
+	int ret = -1;
+	char *global = NULL, *xdg = NULL, *repo_config = NULL;
+	const char *system_wide = NULL, *local;
+	struct config_include_data inc = CONFIG_INCLUDE_INIT;
+	config_fn_t fn;
+	void *data;
+
+	local = given_config_file;
+	if (!local) {
+		local = repo_config = git_pathdup("config");
+		if (git_config_system())
+			system_wide = git_etc_gitconfig();
+		home_config_paths(&global, &xdg, "config");
+	}
+
+	if (use_key_regexp) {
+		char *tl;
+
+		/*
+		 * NEEDSWORK: this naive pattern lowercasing obviously does not
+		 * work for more complex patterns like "^[^.]*Foo.*bar".
+		 * Perhaps we should deprecate this altogether someday.
+		 */
+
+		key = xstrdup(key_);
+		for (tl = key + strlen(key) - 1;
+		     tl >= key && *tl != '.';
+		     tl--)
+			*tl = tolower(*tl);
+		for (tl = key; *tl && *tl != '.'; tl++)
+			*tl = tolower(*tl);
+
+		key_regexp = (regex_t*)xmalloc(sizeof(regex_t));
+		if (regcomp(key_regexp, key, REG_EXTENDED)) {
+			fprintf(stderr, "Invalid key pattern: %s\n", key_);
+			free(key);
+			goto free_strings;
+		}
+	} else {
+		if (git_config_parse_key(key_, &key, NULL))
+			goto free_strings;
+	}
+
+	if (regex_) {
+		if (regex_[0] == '!') {
+			do_not_match = 1;
+			regex_++;
+		}
+
+		regexp = (regex_t*)xmalloc(sizeof(regex_t));
+		if (regcomp(regexp, regex_, REG_EXTENDED)) {
+			fprintf(stderr, "Invalid pattern: %s\n", regex_);
+			goto free_strings;
+		}
+	}
+
+	fn = show_config;
+	data = NULL;
+	if (respect_includes) {
+		inc.fn = fn;
+		inc.data = data;
+		fn = git_config_include;
+		data = &inc;
+	}
+
+	if (do_all && system_wide)
+		git_config_from_file(fn, system_wide, data);
+	if (do_all && xdg)
+		git_config_from_file(fn, xdg, data);
+	if (do_all && global)
+		git_config_from_file(fn, global, data);
+	if (do_all)
+		git_config_from_file(fn, local, data);
+	git_config_from_parameters(fn, data);
+	if (!do_all && !seen)
+		git_config_from_file(fn, local, data);
+	if (!do_all && !seen && global)
+		git_config_from_file(fn, global, data);
+	if (!do_all && !seen && xdg)
+		git_config_from_file(fn, xdg, data);
+	if (!do_all && !seen && system_wide)
+		git_config_from_file(fn, system_wide, data);
+
+	free(key);
+	if (regexp) {
+		regfree(regexp);
+		free(regexp);
+	}
+
+	if (do_all)
+		ret = !seen;
+	else
+		ret = (seen == 1) ? 0 : seen > 1 ? 2 : 1;
+
+free_strings:
+	free(repo_config);
+	free(global);
+	free(xdg);
+	return ret;
+}
+
+static char *normalize_value(const char *key, const char *value)
+{
+	char *normalized;
+
+	if (!value)
+		return NULL;
+
+	if (types == 0 || types == TYPE_PATH)
+		/*
+		 * We don't do normalization for TYPE_PATH here: If
+		 * the path is like ~/foobar/, we prefer to store
+		 * "~/foobar/" in the config file, and to expand the ~
+		 * when retrieving the value.
+		 */
+		normalized = xstrdup(value);
+	else {
+		normalized = xmalloc(64);
+		if (types == TYPE_INT) {
+			int v = git_config_int(key, value);
+			sprintf(normalized, "%d", v);
+		}
+		else if (types == TYPE_BOOL)
+			sprintf(normalized, "%s",
+				git_config_bool(key, value) ? "true" : "false");
+		else if (types == TYPE_BOOL_OR_INT) {
+			int is_bool, v;
+			v = git_config_bool_or_int(key, value, &is_bool);
+			if (!is_bool)
+				sprintf(normalized, "%d", v);
+			else
+				sprintf(normalized, "%s", v ? "true" : "false");
+		}
+	}
+
+	return normalized;
+}
+
+static int get_color_found;
+static const char *get_color_slot;
+static const char *get_colorbool_slot;
+static char parsed_color[COLOR_MAXLEN];
+
+static int git_get_color_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, get_color_slot)) {
+		if (!value)
+			config_error_nonbool(var);
+		color_parse(value, var, parsed_color);
+		get_color_found = 1;
+	}
+	return 0;
+}
+
+static void get_color(const char *def_color)
+{
+	get_color_found = 0;
+	parsed_color[0] = '\0';
+	git_config_with_options(git_get_color_config, NULL,
+				given_config_file, respect_includes);
+
+	if (!get_color_found && def_color)
+		color_parse(def_color, "command line", parsed_color);
+
+	fputs(parsed_color, stdout);
+}
+
+static int get_colorbool_found;
+static int get_diff_color_found;
+static int get_color_ui_found;
+static int git_get_colorbool_config(const char *var, const char *value,
+		void *cb)
+{
+	if (!strcmp(var, get_colorbool_slot))
+		get_colorbool_found = git_config_colorbool(var, value);
+	else if (!strcmp(var, "diff.color"))
+		get_diff_color_found = git_config_colorbool(var, value);
+	else if (!strcmp(var, "color.ui"))
+		get_color_ui_found = git_config_colorbool(var, value);
+	return 0;
+}
+
+static int get_colorbool(int print)
+{
+	get_colorbool_found = -1;
+	get_diff_color_found = -1;
+	git_config_with_options(git_get_colorbool_config, NULL,
+				given_config_file, respect_includes);
+
+	if (get_colorbool_found < 0) {
+		if (!strcmp(get_colorbool_slot, "color.diff"))
+			get_colorbool_found = get_diff_color_found;
+		if (get_colorbool_found < 0)
+			get_colorbool_found = get_color_ui_found;
+	}
+
+	get_colorbool_found = want_color(get_colorbool_found);
+
+	if (print) {
+		printf("%s\n", get_colorbool_found ? "true" : "false");
+		return 0;
+	} else
+		return get_colorbool_found ? 0 : 1;
+}
+
+int cmd_config(int argc, const char **argv, const char *prefix)
+{
+	int nongit = !startup_info->have_repository;
+	char *value;
+
+	given_config_file = getenv(CONFIG_ENVIRONMENT);
+
+	argc = parse_options(argc, argv, prefix, builtin_config_options,
+			     builtin_config_usage,
+			     PARSE_OPT_STOP_AT_NON_OPTION);
+
+	if (use_global_config + use_system_config + use_local_config + !!given_config_file > 1) {
+		error("only one config file at a time.");
+		usage_with_options(builtin_config_usage, builtin_config_options);
+	}
+
+	if (use_global_config) {
+		char *user_config = NULL;
+		char *xdg_config = NULL;
+
+		home_config_paths(&user_config, &xdg_config, "config");
+
+		if (!user_config)
+			/*
+			 * It is unknown if HOME/.gitconfig exists, so
+			 * we do not know if we should write to XDG
+			 * location; error out even if XDG_CONFIG_HOME
+			 * is set and points at a sane location.
+			 */
+			die("$HOME not set");
+
+		if (access(user_config, R_OK) &&
+		    xdg_config && !access(xdg_config, R_OK))
+			given_config_file = xdg_config;
+		else
+			given_config_file = user_config;
+	}
+	else if (use_system_config)
+		given_config_file = git_etc_gitconfig();
+	else if (use_local_config)
+		given_config_file = git_pathdup("config");
+	else if (given_config_file) {
+		if (!is_absolute_path(given_config_file) && prefix)
+			given_config_file =
+				xstrdup(prefix_filename(prefix,
+							strlen(prefix),
+							given_config_file));
+	}
+
+	if (respect_includes == -1)
+		respect_includes = !given_config_file;
+
+	if (end_null) {
+		term = '\0';
+		delim = '\n';
+		key_delim = '\n';
+	}
+
+	if (HAS_MULTI_BITS(types)) {
+		error("only one type at a time.");
+		usage_with_options(builtin_config_usage, builtin_config_options);
+	}
+
+	if (get_color_slot)
+	    actions |= ACTION_GET_COLOR;
+	if (get_colorbool_slot)
+	    actions |= ACTION_GET_COLORBOOL;
+
+	if ((get_color_slot || get_colorbool_slot) && types) {
+		error("--get-color and variable type are incoherent");
+		usage_with_options(builtin_config_usage, builtin_config_options);
+	}
+
+	if (HAS_MULTI_BITS(actions)) {
+		error("only one action at a time.");
+		usage_with_options(builtin_config_usage, builtin_config_options);
+	}
+	if (actions == 0)
+		switch (argc) {
+		case 1: actions = ACTION_GET; break;
+		case 2: actions = ACTION_SET; break;
+		case 3: actions = ACTION_SET_ALL; break;
+		default:
+			usage_with_options(builtin_config_usage, builtin_config_options);
+		}
+
+	if (actions == ACTION_LIST) {
+		check_argc(argc, 0, 0);
+		if (git_config_with_options(show_all_config, NULL,
+					    given_config_file,
+					    respect_includes) < 0) {
+			if (given_config_file)
+				die_errno("unable to read config file '%s'",
+					  given_config_file);
+			else
+				die("error processing config file(s)");
+		}
+	}
+	else if (actions == ACTION_EDIT) {
+		check_argc(argc, 0, 0);
+		if (!given_config_file && nongit)
+			die("not in a git directory");
+		git_config(git_default_config, NULL);
+		launch_editor(given_config_file ?
+			      given_config_file : git_path("config"),
+			      NULL, NULL);
+	}
+	else if (actions == ACTION_SET) {
+		int ret;
+		check_argc(argc, 2, 2);
+		value = normalize_value(argv[0], argv[1]);
+		ret = git_config_set_in_file(given_config_file, argv[0], value);
+		if (ret == CONFIG_NOTHING_SET)
+			error("cannot overwrite multiple values with a single value\n"
+			"       Use a regexp, --add or --replace-all to change %s.", argv[0]);
+		return ret;
+	}
+	else if (actions == ACTION_SET_ALL) {
+		check_argc(argc, 2, 3);
+		value = normalize_value(argv[0], argv[1]);
+		return git_config_set_multivar_in_file(given_config_file,
+						       argv[0], value, argv[2], 0);
+	}
+	else if (actions == ACTION_ADD) {
+		check_argc(argc, 2, 2);
+		value = normalize_value(argv[0], argv[1]);
+		return git_config_set_multivar_in_file(given_config_file,
+						       argv[0], value, "^$", 0);
+	}
+	else if (actions == ACTION_REPLACE_ALL) {
+		check_argc(argc, 2, 3);
+		value = normalize_value(argv[0], argv[1]);
+		return git_config_set_multivar_in_file(given_config_file,
+						       argv[0], value, argv[2], 1);
+	}
+	else if (actions == ACTION_GET) {
+		check_argc(argc, 1, 2);
+		return get_value(argv[0], argv[1]);
+	}
+	else if (actions == ACTION_GET_ALL) {
+		do_all = 1;
+		check_argc(argc, 1, 2);
+		return get_value(argv[0], argv[1]);
+	}
+	else if (actions == ACTION_GET_REGEXP) {
+		show_keys = 1;
+		use_key_regexp = 1;
+		do_all = 1;
+		check_argc(argc, 1, 2);
+		return get_value(argv[0], argv[1]);
+	}
+	else if (actions == ACTION_UNSET) {
+		check_argc(argc, 1, 2);
+		if (argc == 2)
+			return git_config_set_multivar_in_file(given_config_file,
+							       argv[0], NULL, argv[1], 0);
+		else
+			return git_config_set_in_file(given_config_file,
+						      argv[0], NULL);
+	}
+	else if (actions == ACTION_UNSET_ALL) {
+		check_argc(argc, 1, 2);
+		return git_config_set_multivar_in_file(given_config_file,
+						       argv[0], NULL, argv[1], 1);
+	}
+	else if (actions == ACTION_RENAME_SECTION) {
+		int ret;
+		check_argc(argc, 2, 2);
+		ret = git_config_rename_section_in_file(given_config_file,
+							argv[0], argv[1]);
+		if (ret < 0)
+			return ret;
+		if (ret == 0)
+			die("No such section!");
+	}
+	else if (actions == ACTION_REMOVE_SECTION) {
+		int ret;
+		check_argc(argc, 1, 1);
+		ret = git_config_rename_section_in_file(given_config_file,
+							argv[0], NULL);
+		if (ret < 0)
+			return ret;
+		if (ret == 0)
+			die("No such section!");
+	}
+	else if (actions == ACTION_GET_COLOR) {
+		get_color(argv[0]);
+	}
+	else if (actions == ACTION_GET_COLORBOOL) {
+		if (argc == 1)
+			color_stdout_is_tty = git_config_bool("command line", argv[0]);
+		return get_colorbool(argc != 0);
+	}
+
+	return 0;
+}
+
+int cmd_repo_config(int argc, const char **argv, const char *prefix)
+{
+	fprintf(stderr, "WARNING: git repo-config is deprecated in favor of git config.\n");
+	return cmd_config(argc, argv, prefix);
+}
diff --git a/builtin/count-objects.c b/builtin/count-objects.c
new file mode 100644
index 0000000..c37cb98
--- /dev/null
+++ b/builtin/count-objects.c
@@ -0,0 +1,130 @@
+/*
+ * Builtin "git count-objects".
+ *
+ * Copyright (c) 2006 Junio C Hamano
+ */
+
+#include "cache.h"
+#include "dir.h"
+#include "builtin.h"
+#include "parse-options.h"
+
+static void count_objects(DIR *d, char *path, int len, int verbose,
+			  unsigned long *loose,
+			  off_t *loose_size,
+			  unsigned long *packed_loose,
+			  unsigned long *garbage)
+{
+	struct dirent *ent;
+	while ((ent = readdir(d)) != NULL) {
+		char hex[41];
+		unsigned char sha1[20];
+		const char *cp;
+		int bad = 0;
+
+		if (is_dot_or_dotdot(ent->d_name))
+			continue;
+		for (cp = ent->d_name; *cp; cp++) {
+			int ch = *cp;
+			if (('0' <= ch && ch <= '9') ||
+			    ('a' <= ch && ch <= 'f'))
+				continue;
+			bad = 1;
+			break;
+		}
+		if (cp - ent->d_name != 38)
+			bad = 1;
+		else {
+			struct stat st;
+			memcpy(path + len + 3, ent->d_name, 38);
+			path[len + 2] = '/';
+			path[len + 41] = 0;
+			if (lstat(path, &st) || !S_ISREG(st.st_mode))
+				bad = 1;
+			else
+				(*loose_size) += xsize_t(on_disk_bytes(st));
+		}
+		if (bad) {
+			if (verbose) {
+				error("garbage found: %.*s/%s",
+				      len + 2, path, ent->d_name);
+				(*garbage)++;
+			}
+			continue;
+		}
+		(*loose)++;
+		if (!verbose)
+			continue;
+		memcpy(hex, path+len, 2);
+		memcpy(hex+2, ent->d_name, 38);
+		hex[40] = 0;
+		if (get_sha1_hex(hex, sha1))
+			die("internal error");
+		if (has_sha1_pack(sha1))
+			(*packed_loose)++;
+	}
+}
+
+static char const * const count_objects_usage[] = {
+	"git count-objects [-v]",
+	NULL
+};
+
+int cmd_count_objects(int argc, const char **argv, const char *prefix)
+{
+	int i, verbose = 0;
+	const char *objdir = get_object_directory();
+	int len = strlen(objdir);
+	char *path = xmalloc(len + 50);
+	unsigned long loose = 0, packed = 0, packed_loose = 0, garbage = 0;
+	off_t loose_size = 0;
+	struct option opts[] = {
+		OPT__VERBOSE(&verbose, "be verbose"),
+		OPT_END(),
+	};
+
+	argc = parse_options(argc, argv, prefix, opts, count_objects_usage, 0);
+	/* we do not take arguments other than flags for now */
+	if (argc)
+		usage_with_options(count_objects_usage, opts);
+	memcpy(path, objdir, len);
+	if (len && objdir[len-1] != '/')
+		path[len++] = '/';
+	for (i = 0; i < 256; i++) {
+		DIR *d;
+		sprintf(path + len, "%02x", i);
+		d = opendir(path);
+		if (!d)
+			continue;
+		count_objects(d, path, len, verbose,
+			      &loose, &loose_size, &packed_loose, &garbage);
+		closedir(d);
+	}
+	if (verbose) {
+		struct packed_git *p;
+		unsigned long num_pack = 0;
+		off_t size_pack = 0;
+		if (!packed_git)
+			prepare_packed_git();
+		for (p = packed_git; p; p = p->next) {
+			if (!p->pack_local)
+				continue;
+			if (open_pack_index(p))
+				continue;
+			packed += p->num_objects;
+			size_pack += p->pack_size + p->index_size;
+			num_pack++;
+		}
+		printf("count: %lu\n", loose);
+		printf("size: %lu\n", (unsigned long) (loose_size / 1024));
+		printf("in-pack: %lu\n", packed);
+		printf("packs: %lu\n", num_pack);
+		printf("size-pack: %lu\n", (unsigned long) (size_pack / 1024));
+		printf("prune-packable: %lu\n", packed_loose);
+		printf("garbage: %lu\n", garbage);
+	}
+	else
+		printf("%lu objects, %lu kilobytes\n",
+		       loose, (unsigned long) (loose_size / 1024));
+	return 0;
+}
diff --git a/builtin/credential.c b/builtin/credential.c
new file mode 100644
index 0000000..0412fa0
--- /dev/null
+++ b/builtin/credential.c
@@ -0,0 +1,31 @@
+#include "git-compat-util.h"
+#include "credential.h"
+#include "builtin.h"
+
+static const char usage_msg[] =
+	"git credential [fill|approve|reject]";
+
+int cmd_credential(int argc, const char **argv, const char *prefix)
+{
+	const char *op;
+	struct credential c = CREDENTIAL_INIT;
+
+	op = argv[1];
+	if (!op)
+		usage(usage_msg);
+
+	if (credential_read(&c, stdin) < 0)
+		die("unable to read credential from stdin");
+
+	if (!strcmp(op, "fill")) {
+		credential_fill(&c);
+		credential_write(&c, stdout);
+	} else if (!strcmp(op, "approve")) {
+		credential_approve(&c);
+	} else if (!strcmp(op, "reject")) {
+		credential_reject(&c);
+	} else {
+		usage(usage_msg);
+	}
+	return 0;
+}
diff --git a/builtin/describe.c b/builtin/describe.c
new file mode 100644
index 0000000..9f63067
--- /dev/null
+++ b/builtin/describe.c
@@ -0,0 +1,489 @@
+#include "cache.h"
+#include "commit.h"
+#include "tag.h"
+#include "refs.h"
+#include "builtin.h"
+#include "exec_cmd.h"
+#include "parse-options.h"
+#include "diff.h"
+#include "hash.h"
+
+#define SEEN		(1u<<0)
+#define MAX_TAGS	(FLAG_BITS - 1)
+
+static const char * const describe_usage[] = {
+	"git describe [options] <committish>*",
+	"git describe [options] --dirty",
+	NULL
+};
+
+static int debug;	/* Display lots of verbose info */
+static int all;	/* Any valid ref can be used */
+static int tags;	/* Allow lightweight tags */
+static int longformat;
+static int abbrev = -1; /* unspecified */
+static int max_candidates = 10;
+static struct hash_table names;
+static int have_util;
+static const char *pattern;
+static int always;
+static const char *dirty;
+
+/* diff-index command arguments to check if working tree is dirty. */
+static const char *diff_index_args[] = {
+	"diff-index", "--quiet", "HEAD", "--", NULL
+};
+
+
+struct commit_name {
+	struct commit_name *next;
+	unsigned char peeled[20];
+	struct tag *tag;
+	unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */
+	unsigned name_checked:1;
+	unsigned char sha1[20];
+	const char *path;
+};
+static const char *prio_names[] = {
+	"head", "lightweight", "annotated",
+};
+
+static inline unsigned int hash_sha1(const unsigned char *sha1)
+{
+	unsigned int hash;
+	memcpy(&hash, sha1, sizeof(hash));
+	return hash;
+}
+
+static inline struct commit_name *find_commit_name(const unsigned char *peeled)
+{
+	struct commit_name *n = lookup_hash(hash_sha1(peeled), &names);
+	while (n && !!hashcmp(peeled, n->peeled))
+		n = n->next;
+	return n;
+}
+
+static int set_util(void *chain, void *data)
+{
+	struct commit_name *n;
+	for (n = chain; n; n = n->next) {
+		struct commit *c = lookup_commit_reference_gently(n->peeled, 1);
+		if (c)
+			c->util = n;
+	}
+	return 0;
+}
+
+static int replace_name(struct commit_name *e,
+			       int prio,
+			       const unsigned char *sha1,
+			       struct tag **tag)
+{
+	if (!e || e->prio < prio)
+		return 1;
+
+	if (e->prio == 2 && prio == 2) {
+		/* Multiple annotated tags point to the same commit.
+		 * Select one to keep based upon their tagger date.
+		 */
+		struct tag *t;
+
+		if (!e->tag) {
+			t = lookup_tag(e->sha1);
+			if (!t || parse_tag(t))
+				return 1;
+			e->tag = t;
+		}
+
+		t = lookup_tag(sha1);
+		if (!t || parse_tag(t))
+			return 0;
+		*tag = t;
+
+		if (e->tag->date < t->date)
+			return 1;
+	}
+
+	return 0;
+}
+
+static void add_to_known_names(const char *path,
+			       const unsigned char *peeled,
+			       int prio,
+			       const unsigned char *sha1)
+{
+	struct commit_name *e = find_commit_name(peeled);
+	struct tag *tag = NULL;
+	if (replace_name(e, prio, sha1, &tag)) {
+		if (!e) {
+			void **pos;
+			e = xmalloc(sizeof(struct commit_name));
+			hashcpy(e->peeled, peeled);
+			pos = insert_hash(hash_sha1(peeled), e, &names);
+			if (pos) {
+				e->next = *pos;
+				*pos = e;
+			} else {
+				e->next = NULL;
+			}
+		}
+		e->tag = tag;
+		e->prio = prio;
+		e->name_checked = 0;
+		hashcpy(e->sha1, sha1);
+		e->path = path;
+	}
+}
+
+static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+{
+	int might_be_tag = !prefixcmp(path, "refs/tags/");
+	unsigned char peeled[20];
+	int is_tag, prio;
+
+	if (!all && !might_be_tag)
+		return 0;
+
+	if (!peel_ref(path, peeled) && !is_null_sha1(peeled)) {
+		is_tag = !!hashcmp(sha1, peeled);
+	} else {
+		hashcpy(peeled, sha1);
+		is_tag = 0;
+	}
+
+	/* If --all, then any refs are used.
+	 * If --tags, then any tags are used.
+	 * Otherwise only annotated tags are used.
+	 */
+	if (might_be_tag) {
+		if (is_tag)
+			prio = 2;
+		else
+			prio = 1;
+
+		if (pattern && fnmatch(pattern, path + 10, 0))
+			prio = 0;
+	}
+	else
+		prio = 0;
+
+	if (!all) {
+		if (!prio)
+			return 0;
+	}
+	add_to_known_names(all ? path + 5 : path + 10, peeled, prio, sha1);
+	return 0;
+}
+
+struct possible_tag {
+	struct commit_name *name;
+	int depth;
+	int found_order;
+	unsigned flag_within;
+};
+
+static int compare_pt(const void *a_, const void *b_)
+{
+	struct possible_tag *a = (struct possible_tag *)a_;
+	struct possible_tag *b = (struct possible_tag *)b_;
+	if (a->depth != b->depth)
+		return a->depth - b->depth;
+	if (a->found_order != b->found_order)
+		return a->found_order - b->found_order;
+	return 0;
+}
+
+static unsigned long finish_depth_computation(
+	struct commit_list **list,
+	struct possible_tag *best)
+{
+	unsigned long seen_commits = 0;
+	while (*list) {
+		struct commit *c = pop_commit(list);
+		struct commit_list *parents = c->parents;
+		seen_commits++;
+		if (c->object.flags & best->flag_within) {
+			struct commit_list *a = *list;
+			while (a) {
+				struct commit *i = a->item;
+				if (!(i->object.flags & best->flag_within))
+					break;
+				a = a->next;
+			}
+			if (!a)
+				break;
+		} else
+			best->depth++;
+		while (parents) {
+			struct commit *p = parents->item;
+			parse_commit(p);
+			if (!(p->object.flags & SEEN))
+				commit_list_insert_by_date(p, list);
+			p->object.flags |= c->object.flags;
+			parents = parents->next;
+		}
+	}
+	return seen_commits;
+}
+
+static void display_name(struct commit_name *n)
+{
+	if (n->prio == 2 && !n->tag) {
+		n->tag = lookup_tag(n->sha1);
+		if (!n->tag || parse_tag(n->tag))
+			die(_("annotated tag %s not available"), n->path);
+	}
+	if (n->tag && !n->name_checked) {
+		if (!n->tag->tag)
+			die(_("annotated tag %s has no embedded name"), n->path);
+		if (strcmp(n->tag->tag, all ? n->path + 5 : n->path))
+			warning(_("tag '%s' is really '%s' here"), n->tag->tag, n->path);
+		n->name_checked = 1;
+	}
+
+	if (n->tag)
+		printf("%s", n->tag->tag);
+	else
+		printf("%s", n->path);
+}
+
+static void show_suffix(int depth, const unsigned char *sha1)
+{
+	printf("-%d-g%s", depth, find_unique_abbrev(sha1, abbrev));
+}
+
+static void describe(const char *arg, int last_one)
+{
+	unsigned char sha1[20];
+	struct commit *cmit, *gave_up_on = NULL;
+	struct commit_list *list;
+	struct commit_name *n;
+	struct possible_tag all_matches[MAX_TAGS];
+	unsigned int match_cnt = 0, annotated_cnt = 0, cur_match;
+	unsigned long seen_commits = 0;
+	unsigned int unannotated_cnt = 0;
+
+	if (get_sha1(arg, sha1))
+		die(_("Not a valid object name %s"), arg);
+	cmit = lookup_commit_reference(sha1);
+	if (!cmit)
+		die(_("%s is not a valid '%s' object"), arg, commit_type);
+
+	n = find_commit_name(cmit->object.sha1);
+	if (n && (tags || all || n->prio == 2)) {
+		/*
+		 * Exact match to an existing ref.
+		 */
+		display_name(n);
+		if (longformat)
+			show_suffix(0, n->tag ? n->tag->tagged->sha1 : sha1);
+		if (dirty)
+			printf("%s", dirty);
+		printf("\n");
+		return;
+	}
+
+	if (!max_candidates)
+		die(_("no tag exactly matches '%s'"), sha1_to_hex(cmit->object.sha1));
+	if (debug)
+		fprintf(stderr, _("searching to describe %s\n"), arg);
+
+	if (!have_util) {
+		for_each_hash(&names, set_util, NULL);
+		have_util = 1;
+	}
+
+	list = NULL;
+	cmit->object.flags = SEEN;
+	commit_list_insert(cmit, &list);
+	while (list) {
+		struct commit *c = pop_commit(&list);
+		struct commit_list *parents = c->parents;
+		seen_commits++;
+		n = c->util;
+		if (n) {
+			if (!tags && !all && n->prio < 2) {
+				unannotated_cnt++;
+			} else if (match_cnt < max_candidates) {
+				struct possible_tag *t = &all_matches[match_cnt++];
+				t->name = n;
+				t->depth = seen_commits - 1;
+				t->flag_within = 1u << match_cnt;
+				t->found_order = match_cnt;
+				c->object.flags |= t->flag_within;
+				if (n->prio == 2)
+					annotated_cnt++;
+			}
+			else {
+				gave_up_on = c;
+				break;
+			}
+		}
+		for (cur_match = 0; cur_match < match_cnt; cur_match++) {
+			struct possible_tag *t = &all_matches[cur_match];
+			if (!(c->object.flags & t->flag_within))
+				t->depth++;
+		}
+		if (annotated_cnt && !list) {
+			if (debug)
+				fprintf(stderr, _("finished search at %s\n"),
+					sha1_to_hex(c->object.sha1));
+			break;
+		}
+		while (parents) {
+			struct commit *p = parents->item;
+			parse_commit(p);
+			if (!(p->object.flags & SEEN))
+				commit_list_insert_by_date(p, &list);
+			p->object.flags |= c->object.flags;
+			parents = parents->next;
+		}
+	}
+
+	if (!match_cnt) {
+		const unsigned char *sha1 = cmit->object.sha1;
+		if (always) {
+			printf("%s", find_unique_abbrev(sha1, abbrev));
+			if (dirty)
+				printf("%s", dirty);
+			printf("\n");
+			return;
+		}
+		if (unannotated_cnt)
+			die(_("No annotated tags can describe '%s'.\n"
+			    "However, there were unannotated tags: try --tags."),
+			    sha1_to_hex(sha1));
+		else
+			die(_("No tags can describe '%s'.\n"
+			    "Try --always, or create some tags."),
+			    sha1_to_hex(sha1));
+	}
+
+	qsort(all_matches, match_cnt, sizeof(all_matches[0]), compare_pt);
+
+	if (gave_up_on) {
+		commit_list_insert_by_date(gave_up_on, &list);
+		seen_commits--;
+	}
+	seen_commits += finish_depth_computation(&list, &all_matches[0]);
+	free_commit_list(list);
+
+	if (debug) {
+		for (cur_match = 0; cur_match < match_cnt; cur_match++) {
+			struct possible_tag *t = &all_matches[cur_match];
+			fprintf(stderr, " %-11s %8d %s\n",
+				prio_names[t->name->prio],
+				t->depth, t->name->path);
+		}
+		fprintf(stderr, _("traversed %lu commits\n"), seen_commits);
+		if (gave_up_on) {
+			fprintf(stderr,
+				_("more than %i tags found; listed %i most recent\n"
+				"gave up search at %s\n"),
+				max_candidates, max_candidates,
+				sha1_to_hex(gave_up_on->object.sha1));
+		}
+	}
+
+	display_name(all_matches[0].name);
+	if (abbrev)
+		show_suffix(all_matches[0].depth, cmit->object.sha1);
+	if (dirty)
+		printf("%s", dirty);
+	printf("\n");
+
+	if (!last_one)
+		clear_commit_marks(cmit, -1);
+}
+
+int cmd_describe(int argc, const char **argv, const char *prefix)
+{
+	int contains = 0;
+	struct option options[] = {
+		OPT_BOOLEAN(0, "contains",   &contains, "find the tag that comes after the commit"),
+		OPT_BOOLEAN(0, "debug",      &debug, "debug search strategy on stderr"),
+		OPT_BOOLEAN(0, "all",        &all, "use any ref in .git/refs"),
+		OPT_BOOLEAN(0, "tags",       &tags, "use any tag in .git/refs/tags"),
+		OPT_BOOLEAN(0, "long",       &longformat, "always use long format"),
+		OPT__ABBREV(&abbrev),
+		OPT_SET_INT(0, "exact-match", &max_candidates,
+			    "only output exact matches", 0),
+		OPT_INTEGER(0, "candidates", &max_candidates,
+			    "consider <n> most recent tags (default: 10)"),
+		OPT_STRING(0, "match",       &pattern, "pattern",
+			   "only consider tags matching <pattern>"),
+		OPT_BOOLEAN(0, "always",     &always,
+			   "show abbreviated commit object as fallback"),
+		{OPTION_STRING, 0, "dirty",  &dirty, "mark",
+			   "append <mark> on dirty working tree (default: \"-dirty\")",
+		 PARSE_OPT_OPTARG, NULL, (intptr_t) "-dirty"},
+		OPT_END(),
+	};
+
+	git_config(git_default_config, NULL);
+	argc = parse_options(argc, argv, prefix, options, describe_usage, 0);
+	if (abbrev < 0)
+		abbrev = DEFAULT_ABBREV;
+
+	if (max_candidates < 0)
+		max_candidates = 0;
+	else if (max_candidates > MAX_TAGS)
+		max_candidates = MAX_TAGS;
+
+	save_commit_buffer = 0;
+
+	if (longformat && abbrev == 0)
+		die(_("--long is incompatible with --abbrev=0"));
+
+	if (contains) {
+		const char **args = xmalloc((7 + argc) * sizeof(char *));
+		int i = 0;
+		args[i++] = "name-rev";
+		args[i++] = "--name-only";
+		args[i++] = "--no-undefined";
+		if (always)
+			args[i++] = "--always";
+		if (!all) {
+			args[i++] = "--tags";
+			if (pattern) {
+				char *s = xmalloc(strlen("--refs=refs/tags/") + strlen(pattern) + 1);
+				sprintf(s, "--refs=refs/tags/%s", pattern);
+				args[i++] = s;
+			}
+		}
+		memcpy(args + i, argv, argc * sizeof(char *));
+		args[i + argc] = NULL;
+		return cmd_name_rev(i + argc, args, prefix);
+	}
+
+	init_hash(&names);
+	for_each_rawref(get_name, NULL);
+	if (!names.nr && !always)
+		die(_("No names found, cannot describe anything."));
+
+	if (argc == 0) {
+		if (dirty) {
+			static struct lock_file index_lock;
+			int fd;
+
+			read_cache_preload(NULL);
+			refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED,
+				      NULL, NULL, NULL);
+			fd = hold_locked_index(&index_lock, 0);
+			if (0 <= fd)
+				update_index_if_able(&the_index, &index_lock);
+
+			if (!cmd_diff_index(ARRAY_SIZE(diff_index_args) - 1,
+					    diff_index_args, prefix))
+				dirty = NULL;
+		}
+		describe("HEAD", 1);
+	} else if (dirty) {
+		die(_("--dirty is incompatible with committishes"));
+	} else {
+		while (argc-- > 0) {
+			describe(*argv++, argc == 0);
+		}
+	}
+	return 0;
+}
diff --git a/builtin/diff-files.c b/builtin/diff-files.c
new file mode 100644
index 0000000..46085f8
--- /dev/null
+++ b/builtin/diff-files.c
@@ -0,0 +1,70 @@
+/*
+ * GIT - The information manager from hell
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ */
+#include "cache.h"
+#include "diff.h"
+#include "commit.h"
+#include "revision.h"
+#include "builtin.h"
+#include "submodule.h"
+
+static const char diff_files_usage[] =
+"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, const char *prefix)
+{
+	struct rev_info rev;
+	int result;
+	unsigned options = 0;
+
+	init_revisions(&rev, prefix);
+	gitmodules_config();
+	git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
+	rev.abbrev = 0;
+
+	argc = setup_revisions(argc, argv, &rev, NULL);
+	while (1 < argc && argv[1][0] == '-') {
+		if (!strcmp(argv[1], "--base"))
+			rev.max_count = 1;
+		else if (!strcmp(argv[1], "--ours"))
+			rev.max_count = 2;
+		else if (!strcmp(argv[1], "--theirs"))
+			rev.max_count = 3;
+		else if (!strcmp(argv[1], "-q"))
+			options |= DIFF_SILENT_ON_REMOVED;
+		else
+			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), and
+	 * there is no other revision filtering parameters.
+	 */
+	if (rev.pending.nr ||
+	    rev.min_age != -1 || rev.max_age != -1 ||
+	    3 < rev.max_count)
+		usage(diff_files_usage);
+
+	/*
+	 * "diff-files --base -p" should not combine merges because it
+	 * was not asked to.  "diff-files -c -p" should not densify
+	 * (the user should ask with "diff-files --cc" explicitly).
+	 */
+	if (rev.max_count == -1 && !rev.combine_merges &&
+	    (rev.diffopt.output_format & DIFF_FORMAT_PATCH))
+		rev.combine_merges = rev.dense_combined_merges = 1;
+
+	if (read_cache_preload(rev.diffopt.pathspec.raw) < 0) {
+		perror("read_cache_preload");
+		return -1;
+	}
+	result = run_diff_files(&rev, options);
+	return diff_result_code(&rev.diffopt, result);
+}
diff --git a/builtin/diff-index.c b/builtin/diff-index.c
new file mode 100644
index 0000000..2eb32bd
--- /dev/null
+++ b/builtin/diff-index.c
@@ -0,0 +1,52 @@
+#include "cache.h"
+#include "diff.h"
+#include "commit.h"
+#include "revision.h"
+#include "builtin.h"
+#include "submodule.h"
+
+static const char diff_cache_usage[] =
+"git diff-index [-m] [--cached] "
+"[<common diff options>] <tree-ish> [<path>...]"
+COMMON_DIFF_OPTIONS_HELP;
+
+int cmd_diff_index(int argc, const char **argv, const char *prefix)
+{
+	struct rev_info rev;
+	int cached = 0;
+	int i;
+	int result;
+
+	init_revisions(&rev, prefix);
+	gitmodules_config();
+	git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
+	rev.abbrev = 0;
+
+	argc = setup_revisions(argc, argv, &rev, NULL);
+	for (i = 1; i < argc; i++) {
+		const char *arg = argv[i];
+
+		if (!strcmp(arg, "--cached"))
+			cached = 1;
+		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.
+	 */
+	if (rev.pending.nr != 1 ||
+	    rev.max_count != -1 || rev.min_age != -1 || rev.max_age != -1)
+		usage(diff_cache_usage);
+	if (!cached)
+		setup_work_tree();
+	if (read_cache() < 0) {
+		perror("read_cache");
+		return -1;
+	}
+	result = run_diff_index(&rev, cached);
+	return diff_result_code(&rev.diffopt, result);
+}
diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c
new file mode 100644
index 0000000..be6417d
--- /dev/null
+++ b/builtin/diff-tree.c
@@ -0,0 +1,192 @@
+#include "cache.h"
+#include "diff.h"
+#include "commit.h"
+#include "log-tree.h"
+#include "builtin.h"
+#include "submodule.h"
+
+static struct rev_info log_tree_opt;
+
+static int diff_tree_commit_sha1(const unsigned char *sha1)
+{
+	struct commit *commit = lookup_commit_reference(sha1);
+	if (!commit)
+		return -1;
+	return log_tree_commit(&log_tree_opt, commit);
+}
+
+/* Diff one or more commits. */
+static int stdin_diff_commit(struct commit *commit, char *line, int len)
+{
+	unsigned char sha1[20];
+	if (isspace(line[40]) && !get_sha1_hex(line+41, sha1)) {
+		/* Graft the fake parents locally to the commit */
+		int pos = 41;
+		struct commit_list **pptr, *parents;
+
+		/* Free the real parent list */
+		for (parents = commit->parents; parents; ) {
+			struct commit_list *tmp = parents->next;
+			free(parents);
+			parents = tmp;
+		}
+		commit->parents = NULL;
+		pptr = &(commit->parents);
+		while (line[pos] && !get_sha1_hex(line + pos, sha1)) {
+			struct commit *parent = lookup_commit(sha1);
+			if (parent) {
+				pptr = &commit_list_insert(parent, pptr)->next;
+			}
+			pos += 41;
+		}
+	}
+	return log_tree_commit(&log_tree_opt, commit);
+}
+
+/* Diff two trees. */
+static int stdin_diff_trees(struct tree *tree1, char *line, int len)
+{
+	unsigned char sha1[20];
+	struct tree *tree2;
+	if (len != 82 || !isspace(line[40]) || get_sha1_hex(line + 41, sha1))
+		return error("Need exactly two trees, separated by a space");
+	tree2 = lookup_tree(sha1);
+	if (!tree2 || parse_tree(tree2))
+		return -1;
+	printf("%s %s\n", sha1_to_hex(tree1->object.sha1),
+			  sha1_to_hex(tree2->object.sha1));
+	diff_tree_sha1(tree1->object.sha1, tree2->object.sha1,
+		       "", &log_tree_opt.diffopt);
+	log_tree_diff_flush(&log_tree_opt);
+	return 0;
+}
+
+static int diff_tree_stdin(char *line)
+{
+	int len = strlen(line);
+	unsigned char sha1[20];
+	struct object *obj;
+
+	if (!len || line[len-1] != '\n')
+		return -1;
+	line[len-1] = 0;
+	if (get_sha1_hex(line, sha1))
+		return -1;
+	obj = lookup_unknown_object(sha1);
+	if (!obj || !obj->parsed)
+		obj = parse_object(sha1);
+	if (!obj)
+		return -1;
+	if (obj->type == OBJ_COMMIT)
+		return stdin_diff_commit((struct commit *)obj, line, len);
+	if (obj->type == OBJ_TREE)
+		return stdin_diff_trees((struct tree *)obj, line, len);
+	error("Object %s is a %s, not a commit or tree",
+	      sha1_to_hex(sha1), typename(obj->type));
+	return -1;
+}
+
+static const char diff_tree_usage[] =
+"git diff-tree [--stdin] [-m] [-c] [--cc] [-s] [-v] [--pretty] [-t] [-r] [--root] "
+"[<common diff options>] <tree-ish> [<tree-ish>] [<path>...]\n"
+"  -r            diff recursively\n"
+"  --root        include the initial commit as diff against /dev/null\n"
+COMMON_DIFF_OPTIONS_HELP;
+
+static void diff_tree_tweak_rev(struct rev_info *rev, struct setup_revision_opt *opt)
+{
+	if (!rev->diffopt.output_format) {
+		if (rev->dense_combined_merges)
+			rev->diffopt.output_format = DIFF_FORMAT_PATCH;
+		else
+			rev->diffopt.output_format = DIFF_FORMAT_RAW;
+	}
+}
+
+int cmd_diff_tree(int argc, const char **argv, const char *prefix)
+{
+	int nr_sha1;
+	char line[1000];
+	struct object *tree1, *tree2;
+	static struct rev_info *opt = &log_tree_opt;
+	struct setup_revision_opt s_r_opt;
+	int read_stdin = 0;
+
+	init_revisions(opt, prefix);
+	gitmodules_config();
+	git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
+	opt->abbrev = 0;
+	opt->diff = 1;
+	opt->disable_stdin = 1;
+	memset(&s_r_opt, 0, sizeof(s_r_opt));
+	s_r_opt.tweak = diff_tree_tweak_rev;
+	argc = setup_revisions(argc, argv, opt, &s_r_opt);
+
+	while (--argc > 0) {
+		const char *arg = *++argv;
+
+		if (!strcmp(arg, "--stdin")) {
+			read_stdin = 1;
+			continue;
+		}
+		usage(diff_tree_usage);
+	}
+
+	/*
+	 * NOTE! We expect "a ^b" to be equal to "a..b", so we
+	 * reverse the order of the objects if the second one
+	 * is marked UNINTERESTING.
+	 */
+	nr_sha1 = opt->pending.nr;
+	switch (nr_sha1) {
+	case 0:
+		if (!read_stdin)
+			usage(diff_tree_usage);
+		break;
+	case 1:
+		tree1 = opt->pending.objects[0].item;
+		diff_tree_commit_sha1(tree1->sha1);
+		break;
+	case 2:
+		tree1 = opt->pending.objects[0].item;
+		tree2 = opt->pending.objects[1].item;
+		if (tree2->flags & UNINTERESTING) {
+			struct object *tmp = tree2;
+			tree2 = tree1;
+			tree1 = tmp;
+		}
+		diff_tree_sha1(tree1->sha1,
+			       tree2->sha1,
+			       "", &opt->diffopt);
+		log_tree_diff_flush(opt);
+		break;
+	}
+
+	if (read_stdin) {
+		int saved_nrl = 0;
+		int saved_dcctc = 0;
+
+		if (opt->diffopt.detect_rename)
+			opt->diffopt.setup |= (DIFF_SETUP_USE_SIZE_CACHE |
+					       DIFF_SETUP_USE_CACHE);
+		while (fgets(line, sizeof(line), stdin)) {
+			unsigned char sha1[20];
+
+			if (get_sha1_hex(line, sha1)) {
+				fputs(line, stdout);
+				fflush(stdout);
+			}
+			else {
+				diff_tree_stdin(line);
+				if (saved_nrl < opt->diffopt.needed_rename_limit)
+					saved_nrl = opt->diffopt.needed_rename_limit;
+				if (opt->diffopt.degraded_cc_to_c)
+					saved_dcctc = 1;
+			}
+		}
+		opt->diffopt.degraded_cc_to_c = saved_dcctc;
+		opt->diffopt.needed_rename_limit = saved_nrl;
+	}
+
+	return diff_result_code(&opt->diffopt, 0);
+}
diff --git a/builtin/diff.c b/builtin/diff.c
new file mode 100644
index 0000000..da8f6aa
--- /dev/null
+++ b/builtin/diff.c
@@ -0,0 +1,433 @@
+/*
+ * Builtin "git diff"
+ *
+ * Copyright (c) 2006 Junio C Hamano
+ */
+#include "cache.h"
+#include "color.h"
+#include "commit.h"
+#include "blob.h"
+#include "tag.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "revision.h"
+#include "log-tree.h"
+#include "builtin.h"
+#include "submodule.h"
+#include "sha1-array.h"
+
+struct blobinfo {
+	unsigned char sha1[20];
+	const char *name;
+	unsigned mode;
+};
+
+static const char builtin_diff_usage[] =
+"git diff [<options>] [<commit> [<commit>]] [--] [<path>...]";
+
+static void stuff_change(struct diff_options *opt,
+			 unsigned old_mode, unsigned new_mode,
+			 const unsigned char *old_sha1,
+			 const unsigned char *new_sha1,
+			 const char *old_name,
+			 const char *new_name)
+{
+	struct diff_filespec *one, *two;
+
+	if (!is_null_sha1(old_sha1) && !is_null_sha1(new_sha1) &&
+	    !hashcmp(old_sha1, new_sha1) && (old_mode == new_mode))
+		return;
+
+	if (DIFF_OPT_TST(opt, REVERSE_DIFF)) {
+		unsigned tmp;
+		const unsigned char *tmp_u;
+		const char *tmp_c;
+		tmp = old_mode; old_mode = new_mode; new_mode = tmp;
+		tmp_u = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_u;
+		tmp_c = old_name; old_name = new_name; new_name = tmp_c;
+	}
+
+	if (opt->prefix &&
+	    (strncmp(old_name, opt->prefix, opt->prefix_length) ||
+	     strncmp(new_name, opt->prefix, opt->prefix_length)))
+		return;
+
+	one = alloc_filespec(old_name);
+	two = alloc_filespec(new_name);
+	fill_filespec(one, old_sha1, old_mode);
+	fill_filespec(two, new_sha1, new_mode);
+
+	diff_queue(&diff_queued_diff, one, two);
+}
+
+static int builtin_diff_b_f(struct rev_info *revs,
+			    int argc, const char **argv,
+			    struct blobinfo *blob,
+			    const char *path)
+{
+	/* Blob vs file in the working tree*/
+	struct stat st;
+
+	if (argc > 1)
+		usage(builtin_diff_usage);
+
+	if (lstat(path, &st))
+		die_errno(_("failed to stat '%s'"), path);
+	if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)))
+		die(_("'%s': not a regular file or symlink"), path);
+
+	diff_set_mnemonic_prefix(&revs->diffopt, "o/", "w/");
+
+	if (blob[0].mode == S_IFINVALID)
+		blob[0].mode = canon_mode(st.st_mode);
+
+	stuff_change(&revs->diffopt,
+		     blob[0].mode, canon_mode(st.st_mode),
+		     blob[0].sha1, null_sha1,
+		     path, path);
+	diffcore_std(&revs->diffopt);
+	diff_flush(&revs->diffopt);
+	return 0;
+}
+
+static int builtin_diff_blobs(struct rev_info *revs,
+			      int argc, const char **argv,
+			      struct blobinfo *blob)
+{
+	unsigned mode = canon_mode(S_IFREG | 0644);
+
+	if (argc > 1)
+		usage(builtin_diff_usage);
+
+	if (blob[0].mode == S_IFINVALID)
+		blob[0].mode = mode;
+
+	if (blob[1].mode == S_IFINVALID)
+		blob[1].mode = mode;
+
+	stuff_change(&revs->diffopt,
+		     blob[0].mode, blob[1].mode,
+		     blob[0].sha1, blob[1].sha1,
+		     blob[0].name, blob[1].name);
+	diffcore_std(&revs->diffopt);
+	diff_flush(&revs->diffopt);
+	return 0;
+}
+
+static int builtin_diff_index(struct rev_info *revs,
+			      int argc, const char **argv)
+{
+	int cached = 0;
+	while (1 < argc) {
+		const char *arg = argv[1];
+		if (!strcmp(arg, "--cached") || !strcmp(arg, "--staged"))
+			cached = 1;
+		else
+			usage(builtin_diff_usage);
+		argv++; argc--;
+	}
+	if (!cached)
+		setup_work_tree();
+	/*
+	 * Make sure there is one revision (i.e. pending object),
+	 * and there is no revision filtering parameters.
+	 */
+	if (revs->pending.nr != 1 ||
+	    revs->max_count != -1 || revs->min_age != -1 ||
+	    revs->max_age != -1)
+		usage(builtin_diff_usage);
+	if (read_cache_preload(revs->diffopt.pathspec.raw) < 0) {
+		perror("read_cache_preload");
+		return -1;
+	}
+	return run_diff_index(revs, cached);
+}
+
+static int builtin_diff_tree(struct rev_info *revs,
+			     int argc, const char **argv,
+			     struct object_array_entry *ent)
+{
+	const unsigned char *(sha1[2]);
+	int swap = 0;
+
+	if (argc > 1)
+		usage(builtin_diff_usage);
+
+	/* We saw two trees, ent[0] and ent[1].
+	 * if ent[1] is uninteresting, they are swapped
+	 */
+	if (ent[1].item->flags & UNINTERESTING)
+		swap = 1;
+	sha1[swap] = ent[0].item->sha1;
+	sha1[1-swap] = ent[1].item->sha1;
+	diff_tree_sha1(sha1[0], sha1[1], "", &revs->diffopt);
+	log_tree_diff_flush(revs);
+	return 0;
+}
+
+static int builtin_diff_combined(struct rev_info *revs,
+				 int argc, const char **argv,
+				 struct object_array_entry *ent,
+				 int ents)
+{
+	struct sha1_array parents = SHA1_ARRAY_INIT;
+	int i;
+
+	if (argc > 1)
+		usage(builtin_diff_usage);
+
+	if (!revs->dense_combined_merges && !revs->combine_merges)
+		revs->dense_combined_merges = revs->combine_merges = 1;
+	for (i = 1; i < ents; i++)
+		sha1_array_append(&parents, ent[i].item->sha1);
+	diff_tree_combined(ent[0].item->sha1, &parents,
+			   revs->dense_combined_merges, revs);
+	sha1_array_clear(&parents);
+	return 0;
+}
+
+static void refresh_index_quietly(void)
+{
+	struct lock_file *lock_file;
+	int fd;
+
+	lock_file = xcalloc(1, sizeof(struct lock_file));
+	fd = hold_locked_index(lock_file, 0);
+	if (fd < 0)
+		return;
+	discard_cache();
+	read_cache();
+	refresh_cache(REFRESH_QUIET|REFRESH_UNMERGED);
+	update_index_if_able(&the_index, lock_file);
+}
+
+static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv)
+{
+	unsigned int options = 0;
+
+	while (1 < argc && argv[1][0] == '-') {
+		if (!strcmp(argv[1], "--base"))
+			revs->max_count = 1;
+		else if (!strcmp(argv[1], "--ours"))
+			revs->max_count = 2;
+		else if (!strcmp(argv[1], "--theirs"))
+			revs->max_count = 3;
+		else if (!strcmp(argv[1], "-q"))
+			options |= DIFF_SILENT_ON_REMOVED;
+		else if (!strcmp(argv[1], "-h"))
+			usage(builtin_diff_usage);
+		else
+			return error(_("invalid option: %s"), argv[1]);
+		argv++; argc--;
+	}
+
+	/*
+	 * "diff --base" should not combine merges because it was not
+	 * asked to.  "diff -c" should not densify (if the user wants
+	 * dense one, --cc can be explicitly asked for, or just rely
+	 * on the default).
+	 */
+	if (revs->max_count == -1 && !revs->combine_merges &&
+	    (revs->diffopt.output_format & DIFF_FORMAT_PATCH))
+		revs->combine_merges = revs->dense_combined_merges = 1;
+
+	setup_work_tree();
+	if (read_cache_preload(revs->diffopt.pathspec.raw) < 0) {
+		perror("read_cache_preload");
+		return -1;
+	}
+	return run_diff_files(revs, options);
+}
+
+int cmd_diff(int argc, const char **argv, const char *prefix)
+{
+	int i;
+	struct rev_info rev;
+	struct object_array_entry ent[100];
+	int ents = 0, blobs = 0, paths = 0;
+	const char *path = NULL;
+	struct blobinfo blob[2];
+	int nongit;
+	int result = 0;
+
+	/*
+	 * We could get N tree-ish in the rev.pending_objects list.
+	 * Also there could be M blobs there, and P pathspecs.
+	 *
+	 * N=0, M=0:
+	 *	cache vs files (diff-files)
+	 * N=0, M=2:
+	 *      compare two random blobs.  P must be zero.
+	 * N=0, M=1, P=1:
+	 *	compare a blob with a working tree file.
+	 *
+	 * N=1, M=0:
+	 *      tree vs cache (diff-index --cached)
+	 *
+	 * N=2, M=0:
+	 *      tree vs tree (diff-tree)
+	 *
+	 * N=0, M=0, P=2:
+	 *      compare two filesystem entities (aka --no-index).
+	 *
+	 * Other cases are errors.
+	 */
+
+	prefix = setup_git_directory_gently(&nongit);
+	gitmodules_config();
+	git_config(git_diff_ui_config, NULL);
+
+	init_revisions(&rev, prefix);
+
+	/* If this is a no-index diff, just run it and exit there. */
+	diff_no_index(&rev, argc, argv, nongit, prefix);
+
+	/* Otherwise, we are doing the usual "git" diff */
+	rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index;
+
+	/* Scale to real terminal size and respect statGraphWidth config */
+	rev.diffopt.stat_width = -1;
+	rev.diffopt.stat_graph_width = -1;
+
+	/* Default to let external and textconv be used */
+	DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL);
+	DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV);
+
+	if (nongit)
+		die(_("Not a git repository"));
+	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"));
+	}
+
+	DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
+
+	setup_diff_pager(&rev.diffopt);
+
+	/*
+	 * Do we have --cached and not have a pending object, then
+	 * default to HEAD by hand.  Eek.
+	 */
+	if (!rev.pending.nr) {
+		int i;
+		for (i = 1; i < argc; i++) {
+			const char *arg = argv[i];
+			if (!strcmp(arg, "--"))
+				break;
+			else if (!strcmp(arg, "--cached") ||
+				 !strcmp(arg, "--staged")) {
+				add_head_to_pending(&rev);
+				if (!rev.pending.nr) {
+					struct tree *tree;
+					tree = lookup_tree(EMPTY_TREE_SHA1_BIN);
+					add_pending_object(&rev, &tree->object, "HEAD");
+				}
+				break;
+			}
+		}
+	}
+
+	for (i = 0; i < rev.pending.nr; i++) {
+		struct object_array_entry *list = rev.pending.objects+i;
+		struct object *obj = list->item;
+		const char *name = list->name;
+		int flags = (obj->flags & UNINTERESTING);
+		if (!obj->parsed)
+			obj = parse_object(obj->sha1);
+		obj = deref_tag(obj, NULL, 0);
+		if (!obj)
+			die(_("invalid object '%s' given."), name);
+		if (obj->type == OBJ_COMMIT)
+			obj = &((struct commit *)obj)->tree->object;
+		if (obj->type == OBJ_TREE) {
+			if (ARRAY_SIZE(ent) <= ents)
+				die(_("more than %d trees given: '%s'"),
+				    (int) ARRAY_SIZE(ent), name);
+			obj->flags |= flags;
+			ent[ents].item = obj;
+			ent[ents].name = name;
+			ents++;
+			continue;
+		}
+		if (obj->type == OBJ_BLOB) {
+			if (2 <= blobs)
+				die(_("more than two blobs given: '%s'"), name);
+			hashcpy(blob[blobs].sha1, obj->sha1);
+			blob[blobs].name = name;
+			blob[blobs].mode = list->mode;
+			blobs++;
+			continue;
+
+		}
+		die(_("unhandled object '%s' given."), name);
+	}
+	if (rev.prune_data.nr) {
+		if (!path)
+			path = rev.prune_data.items[0].match;
+		paths += rev.prune_data.nr;
+	}
+
+	/*
+	 * Now, do the arguments look reasonable?
+	 */
+	if (!ents) {
+		switch (blobs) {
+		case 0:
+			result = builtin_diff_files(&rev, argc, argv);
+			break;
+		case 1:
+			if (paths != 1)
+				usage(builtin_diff_usage);
+			result = builtin_diff_b_f(&rev, argc, argv, blob, path);
+			break;
+		case 2:
+			if (paths)
+				usage(builtin_diff_usage);
+			result = builtin_diff_blobs(&rev, argc, argv, blob);
+			break;
+		default:
+			usage(builtin_diff_usage);
+		}
+	}
+	else if (blobs)
+		usage(builtin_diff_usage);
+	else if (ents == 1)
+		result = builtin_diff_index(&rev, argc, argv);
+	else if (ents == 2)
+		result = builtin_diff_tree(&rev, argc, argv, ent);
+	else if (ent[0].item->flags & UNINTERESTING) {
+		/*
+		 * diff A...B where there is at least one merge base
+		 * between A and B.  We have ent[0] == merge-base,
+		 * ent[ents-2] == A, and ent[ents-1] == B.  Show diff
+		 * between the base and B.  Note that we pick one
+		 * merge base at random if there are more than one.
+		 */
+		ent[1] = ent[ents-1];
+		result = builtin_diff_tree(&rev, argc, argv, ent);
+	} else
+		result = builtin_diff_combined(&rev, argc, argv,
+					       ent, ents);
+	result = diff_result_code(&rev.diffopt, result);
+	if (1 < rev.diffopt.skip_stat_unmatch)
+		refresh_index_quietly();
+	return result;
+}
+
+void setup_diff_pager(struct diff_options *opt)
+{
+	/*
+	 * If the user asked for our exit code, then either they want --quiet
+	 * or --exit-code. We should definitely not bother with a pager in the
+	 * former case, as we will generate no output. Since we still properly
+	 * report our exit code even when a pager is run, we _could_ run a
+	 * pager with --exit-code. But since we have not done so historically,
+	 * and because it is easy to find people oneline advising "git diff
+	 * --exit-code" in hooks and other scripts, we do not do so.
+	 */
+	if (!DIFF_OPT_TST(opt, EXIT_WITH_STATUS) &&
+	    check_pager_config("diff") != 0)
+		setup_pager();
+}
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
new file mode 100644
index 0000000..9ab6db3
--- /dev/null
+++ b/builtin/fast-export.c
@@ -0,0 +1,705 @@
+/*
+ * "git fast-export" builtin command
+ *
+ * Copyright (C) 2007 Johannes E. Schindelin
+ */
+#include "builtin.h"
+#include "cache.h"
+#include "commit.h"
+#include "object.h"
+#include "tag.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "log-tree.h"
+#include "revision.h"
+#include "decorate.h"
+#include "string-list.h"
+#include "utf8.h"
+#include "parse-options.h"
+#include "quote.h"
+
+static const char *fast_export_usage[] = {
+	"git fast-export [rev-list-opts]",
+	NULL
+};
+
+static int progress;
+static enum { ABORT, VERBATIM, WARN, STRIP } signed_tag_mode = ABORT;
+static enum { ERROR, DROP, REWRITE } tag_of_filtered_mode = ERROR;
+static int fake_missing_tagger;
+static int use_done_feature;
+static int no_data;
+static int full_tree;
+
+static int parse_opt_signed_tag_mode(const struct option *opt,
+				     const char *arg, int unset)
+{
+	if (unset || !strcmp(arg, "abort"))
+		signed_tag_mode = ABORT;
+	else if (!strcmp(arg, "verbatim") || !strcmp(arg, "ignore"))
+		signed_tag_mode = VERBATIM;
+	else if (!strcmp(arg, "warn"))
+		signed_tag_mode = WARN;
+	else if (!strcmp(arg, "strip"))
+		signed_tag_mode = STRIP;
+	else
+		return error("Unknown signed-tag mode: %s", arg);
+	return 0;
+}
+
+static int parse_opt_tag_of_filtered_mode(const struct option *opt,
+					  const char *arg, int unset)
+{
+	if (unset || !strcmp(arg, "abort"))
+		tag_of_filtered_mode = ERROR;
+	else if (!strcmp(arg, "drop"))
+		tag_of_filtered_mode = DROP;
+	else if (!strcmp(arg, "rewrite"))
+		tag_of_filtered_mode = REWRITE;
+	else
+		return error("Unknown tag-of-filtered mode: %s", arg);
+	return 0;
+}
+
+static struct decoration idnums;
+static uint32_t last_idnum;
+
+static int has_unshown_parent(struct commit *commit)
+{
+	struct commit_list *parent;
+
+	for (parent = commit->parents; parent; parent = parent->next)
+		if (!(parent->item->object.flags & SHOWN) &&
+		    !(parent->item->object.flags & UNINTERESTING))
+			return 1;
+	return 0;
+}
+
+/* Since intptr_t is C99, we do not use it here */
+static inline uint32_t *mark_to_ptr(uint32_t mark)
+{
+	return ((uint32_t *)NULL) + mark;
+}
+
+static inline uint32_t ptr_to_mark(void * mark)
+{
+	return (uint32_t *)mark - (uint32_t *)NULL;
+}
+
+static inline void mark_object(struct object *object, uint32_t mark)
+{
+	add_decoration(&idnums, object, mark_to_ptr(mark));
+}
+
+static inline void mark_next_object(struct object *object)
+{
+	mark_object(object, ++last_idnum);
+}
+
+static int get_object_mark(struct object *object)
+{
+	void *decoration = lookup_decoration(&idnums, object);
+	if (!decoration)
+		return 0;
+	return ptr_to_mark(decoration);
+}
+
+static void show_progress(void)
+{
+	static int counter = 0;
+	if (!progress)
+		return;
+	if ((++counter % progress) == 0)
+		printf("progress %d objects\n", counter);
+}
+
+static void handle_object(const unsigned char *sha1)
+{
+	unsigned long size;
+	enum object_type type;
+	char *buf;
+	struct object *object;
+
+	if (no_data)
+		return;
+
+	if (is_null_sha1(sha1))
+		return;
+
+	object = parse_object(sha1);
+	if (!object)
+		die ("Could not read blob %s", sha1_to_hex(sha1));
+
+	if (object->flags & SHOWN)
+		return;
+
+	buf = read_sha1_file(sha1, &type, &size);
+	if (!buf)
+		die ("Could not read blob %s", sha1_to_hex(sha1));
+
+	mark_next_object(object);
+
+	printf("blob\nmark :%"PRIu32"\ndata %lu\n", last_idnum, size);
+	if (size && fwrite(buf, size, 1, stdout) != 1)
+		die_errno ("Could not write blob '%s'", sha1_to_hex(sha1));
+	printf("\n");
+
+	show_progress();
+
+	object->flags |= SHOWN;
+	free(buf);
+}
+
+static int depth_first(const void *a_, const void *b_)
+{
+	const struct diff_filepair *a = *((const struct diff_filepair **)a_);
+	const struct diff_filepair *b = *((const struct diff_filepair **)b_);
+	const char *name_a, *name_b;
+	int len_a, len_b, len;
+	int cmp;
+
+	name_a = a->one ? a->one->path : a->two->path;
+	name_b = b->one ? b->one->path : b->two->path;
+
+	len_a = strlen(name_a);
+	len_b = strlen(name_b);
+	len = (len_a < len_b) ? len_a : len_b;
+
+	/* strcmp will sort 'd' before 'd/e', we want 'd/e' before 'd' */
+	cmp = memcmp(name_a, name_b, len);
+	if (cmp)
+		return cmp;
+	cmp = len_b - len_a;
+	if (cmp)
+		return cmp;
+	/*
+	 * Move 'R'ename entries last so that all references of the file
+	 * appear in the output before it is renamed (e.g., when a file
+	 * was copied and renamed in the same commit).
+	 */
+	return (a->status == 'R') - (b->status == 'R');
+}
+
+static void print_path(const char *path)
+{
+	int need_quote = quote_c_style(path, NULL, NULL, 0);
+	if (need_quote)
+		quote_c_style(path, NULL, stdout, 0);
+	else if (strchr(path, ' '))
+		printf("\"%s\"", path);
+	else
+		printf("%s", path);
+}
+
+static void show_filemodify(struct diff_queue_struct *q,
+			    struct diff_options *options, void *data)
+{
+	int i;
+
+	/*
+	 * Handle files below a directory first, in case they are all deleted
+	 * and the directory changes to a file or symlink.
+	 */
+	qsort(q->queue, q->nr, sizeof(q->queue[0]), depth_first);
+
+	for (i = 0; i < q->nr; i++) {
+		struct diff_filespec *ospec = q->queue[i]->one;
+		struct diff_filespec *spec = q->queue[i]->two;
+
+		switch (q->queue[i]->status) {
+		case DIFF_STATUS_DELETED:
+			printf("D ");
+			print_path(spec->path);
+			putchar('\n');
+			break;
+
+		case DIFF_STATUS_COPIED:
+		case DIFF_STATUS_RENAMED:
+			printf("%c ", q->queue[i]->status);
+			print_path(ospec->path);
+			putchar(' ');
+			print_path(spec->path);
+			putchar('\n');
+
+			if (!hashcmp(ospec->sha1, spec->sha1) &&
+			    ospec->mode == spec->mode)
+				break;
+			/* fallthrough */
+
+		case DIFF_STATUS_TYPE_CHANGED:
+		case DIFF_STATUS_MODIFIED:
+		case DIFF_STATUS_ADDED:
+			/*
+			 * Links refer to objects in another repositories;
+			 * output the SHA-1 verbatim.
+			 */
+			if (no_data || S_ISGITLINK(spec->mode))
+				printf("M %06o %s ", spec->mode,
+				       sha1_to_hex(spec->sha1));
+			else {
+				struct object *object = lookup_object(spec->sha1);
+				printf("M %06o :%d ", spec->mode,
+				       get_object_mark(object));
+			}
+			print_path(spec->path);
+			putchar('\n');
+			break;
+
+		default:
+			die("Unexpected comparison status '%c' for %s, %s",
+				q->queue[i]->status,
+				ospec->path ? ospec->path : "none",
+				spec->path ? spec->path : "none");
+		}
+	}
+}
+
+static const char *find_encoding(const char *begin, const char *end)
+{
+	const char *needle = "\nencoding ";
+	char *bol, *eol;
+
+	bol = memmem(begin, end ? end - begin : strlen(begin),
+		     needle, strlen(needle));
+	if (!bol)
+		return git_commit_encoding;
+	bol += strlen(needle);
+	eol = strchrnul(bol, '\n');
+	*eol = '\0';
+	return bol;
+}
+
+static void handle_commit(struct commit *commit, struct rev_info *rev)
+{
+	int saved_output_format = rev->diffopt.output_format;
+	const char *author, *author_end, *committer, *committer_end;
+	const char *encoding, *message;
+	char *reencoded = NULL;
+	struct commit_list *p;
+	int i;
+
+	rev->diffopt.output_format = DIFF_FORMAT_CALLBACK;
+
+	parse_commit(commit);
+	author = strstr(commit->buffer, "\nauthor ");
+	if (!author)
+		die ("Could not find author in commit %s",
+		     sha1_to_hex(commit->object.sha1));
+	author++;
+	author_end = strchrnul(author, '\n');
+	committer = strstr(author_end, "\ncommitter ");
+	if (!committer)
+		die ("Could not find committer in commit %s",
+		     sha1_to_hex(commit->object.sha1));
+	committer++;
+	committer_end = strchrnul(committer, '\n');
+	message = strstr(committer_end, "\n\n");
+	encoding = find_encoding(committer_end, message);
+	if (message)
+		message += 2;
+
+	if (commit->parents &&
+	    get_object_mark(&commit->parents->item->object) != 0 &&
+	    !full_tree) {
+		parse_commit(commit->parents->item);
+		diff_tree_sha1(commit->parents->item->tree->object.sha1,
+			       commit->tree->object.sha1, "", &rev->diffopt);
+	}
+	else
+		diff_root_tree_sha1(commit->tree->object.sha1,
+				    "", &rev->diffopt);
+
+	/* Export the referenced blobs, and remember the marks. */
+	for (i = 0; i < diff_queued_diff.nr; i++)
+		if (!S_ISGITLINK(diff_queued_diff.queue[i]->two->mode))
+			handle_object(diff_queued_diff.queue[i]->two->sha1);
+
+	mark_next_object(&commit->object);
+	if (!is_encoding_utf8(encoding))
+		reencoded = reencode_string(message, "UTF-8", encoding);
+	if (!commit->parents)
+		printf("reset %s\n", (const char*)commit->util);
+	printf("commit %s\nmark :%"PRIu32"\n%.*s\n%.*s\ndata %u\n%s",
+	       (const char *)commit->util, last_idnum,
+	       (int)(author_end - author), author,
+	       (int)(committer_end - committer), committer,
+	       (unsigned)(reencoded
+			  ? strlen(reencoded) : message
+			  ? strlen(message) : 0),
+	       reencoded ? reencoded : message ? message : "");
+	free(reencoded);
+
+	for (i = 0, p = commit->parents; p; p = p->next) {
+		int mark = get_object_mark(&p->item->object);
+		if (!mark)
+			continue;
+		if (i == 0)
+			printf("from :%d\n", mark);
+		else
+			printf("merge :%d\n", mark);
+		i++;
+	}
+
+	if (full_tree)
+		printf("deleteall\n");
+	log_tree_diff_flush(rev);
+	rev->diffopt.output_format = saved_output_format;
+
+	printf("\n");
+
+	show_progress();
+}
+
+static void handle_tail(struct object_array *commits, struct rev_info *revs)
+{
+	struct commit *commit;
+	while (commits->nr) {
+		commit = (struct commit *)commits->objects[commits->nr - 1].item;
+		if (has_unshown_parent(commit))
+			return;
+		handle_commit(commit, revs);
+		commits->nr--;
+	}
+}
+
+static void handle_tag(const char *name, struct tag *tag)
+{
+	unsigned long size;
+	enum object_type type;
+	char *buf;
+	const char *tagger, *tagger_end, *message;
+	size_t message_size = 0;
+	struct object *tagged;
+	int tagged_mark;
+	struct commit *p;
+
+	/* Trees have no identifer in fast-export output, thus we have no way
+	 * to output tags of trees, tags of tags of trees, etc.  Simply omit
+	 * such tags.
+	 */
+	tagged = tag->tagged;
+	while (tagged->type == OBJ_TAG) {
+		tagged = ((struct tag *)tagged)->tagged;
+	}
+	if (tagged->type == OBJ_TREE) {
+		warning("Omitting tag %s,\nsince tags of trees (or tags of tags of trees, etc.) are not supported.",
+			sha1_to_hex(tag->object.sha1));
+		return;
+	}
+
+	buf = read_sha1_file(tag->object.sha1, &type, &size);
+	if (!buf)
+		die ("Could not read tag %s", sha1_to_hex(tag->object.sha1));
+	message = memmem(buf, size, "\n\n", 2);
+	if (message) {
+		message += 2;
+		message_size = strlen(message);
+	}
+	tagger = memmem(buf, message ? message - buf : size, "\ntagger ", 8);
+	if (!tagger) {
+		if (fake_missing_tagger)
+			tagger = "tagger Unspecified Tagger "
+				"<unspecified-tagger> 0 +0000";
+		else
+			tagger = "";
+		tagger_end = tagger + strlen(tagger);
+	} else {
+		tagger++;
+		tagger_end = strchrnul(tagger, '\n');
+	}
+
+	/* handle signed tags */
+	if (message) {
+		const char *signature = strstr(message,
+					       "\n-----BEGIN PGP SIGNATURE-----\n");
+		if (signature)
+			switch(signed_tag_mode) {
+			case ABORT:
+				die ("Encountered signed tag %s; use "
+				     "--signed-tag=<mode> to handle it.",
+				     sha1_to_hex(tag->object.sha1));
+			case WARN:
+				warning ("Exporting signed tag %s",
+					 sha1_to_hex(tag->object.sha1));
+				/* fallthru */
+			case VERBATIM:
+				break;
+			case STRIP:
+				message_size = signature + 1 - message;
+				break;
+			}
+	}
+
+	/* handle tag->tagged having been filtered out due to paths specified */
+	tagged = tag->tagged;
+	tagged_mark = get_object_mark(tagged);
+	if (!tagged_mark) {
+		switch(tag_of_filtered_mode) {
+		case ABORT:
+			die ("Tag %s tags unexported object; use "
+			     "--tag-of-filtered-object=<mode> to handle it.",
+			     sha1_to_hex(tag->object.sha1));
+		case DROP:
+			/* Ignore this tag altogether */
+			return;
+		case REWRITE:
+			if (tagged->type != OBJ_COMMIT) {
+				die ("Tag %s tags unexported %s!",
+				     sha1_to_hex(tag->object.sha1),
+				     typename(tagged->type));
+			}
+			p = (struct commit *)tagged;
+			for (;;) {
+				if (p->parents && p->parents->next)
+					break;
+				if (p->object.flags & UNINTERESTING)
+					break;
+				if (!(p->object.flags & TREESAME))
+					break;
+				if (!p->parents)
+					die ("Can't find replacement commit for tag %s\n",
+					     sha1_to_hex(tag->object.sha1));
+				p = p->parents->item;
+			}
+			tagged_mark = get_object_mark(&p->object);
+		}
+	}
+
+	if (!prefixcmp(name, "refs/tags/"))
+		name += 10;
+	printf("tag %s\nfrom :%d\n%.*s%sdata %d\n%.*s\n",
+	       name, tagged_mark,
+	       (int)(tagger_end - tagger), tagger,
+	       tagger == tagger_end ? "" : "\n",
+	       (int)message_size, (int)message_size, message ? message : "");
+}
+
+static void get_tags_and_duplicates(struct object_array *pending,
+				    struct string_list *extra_refs)
+{
+	struct tag *tag;
+	int i;
+
+	for (i = 0; i < pending->nr; i++) {
+		struct object_array_entry *e = pending->objects + i;
+		unsigned char sha1[20];
+		struct commit *commit = commit;
+		char *full_name;
+
+		if (dwim_ref(e->name, strlen(e->name), sha1, &full_name) != 1)
+			continue;
+
+		switch (e->item->type) {
+		case OBJ_COMMIT:
+			commit = (struct commit *)e->item;
+			break;
+		case OBJ_TAG:
+			tag = (struct tag *)e->item;
+
+			/* handle nested tags */
+			while (tag && tag->object.type == OBJ_TAG) {
+				parse_object(tag->object.sha1);
+				string_list_append(extra_refs, full_name)->util = tag;
+				tag = (struct tag *)tag->tagged;
+			}
+			if (!tag)
+				die ("Tag %s points nowhere?", e->name);
+			switch(tag->object.type) {
+			case OBJ_COMMIT:
+				commit = (struct commit *)tag;
+				break;
+			case OBJ_BLOB:
+				handle_object(tag->object.sha1);
+				continue;
+			default: /* OBJ_TAG (nested tags) is already handled */
+				warning("Tag points to object of unexpected type %s, skipping.",
+					typename(tag->object.type));
+				continue;
+			}
+			break;
+		default:
+			warning("%s: Unexpected object of type %s, skipping.",
+				e->name,
+				typename(e->item->type));
+			continue;
+		}
+		if (commit->util)
+			/* more than one name for the same object */
+			string_list_append(extra_refs, full_name)->util = commit;
+		else
+			commit->util = full_name;
+	}
+}
+
+static void handle_tags_and_duplicates(struct string_list *extra_refs)
+{
+	struct commit *commit;
+	int i;
+
+	for (i = extra_refs->nr - 1; i >= 0; i--) {
+		const char *name = extra_refs->items[i].string;
+		struct object *object = extra_refs->items[i].util;
+		switch (object->type) {
+		case OBJ_TAG:
+			handle_tag(name, (struct tag *)object);
+			break;
+		case OBJ_COMMIT:
+			/* create refs pointing to already seen commits */
+			commit = (struct commit *)object;
+			printf("reset %s\nfrom :%d\n\n", name,
+			       get_object_mark(&commit->object));
+			show_progress();
+			break;
+		}
+	}
+}
+
+static void export_marks(char *file)
+{
+	unsigned int i;
+	uint32_t mark;
+	struct object_decoration *deco = idnums.hash;
+	FILE *f;
+	int e = 0;
+
+	f = fopen(file, "w");
+	if (!f)
+		die_errno("Unable to open marks file %s for writing.", file);
+
+	for (i = 0; i < idnums.size; i++) {
+		if (deco->base && deco->base->type == 1) {
+			mark = ptr_to_mark(deco->decoration);
+			if (fprintf(f, ":%"PRIu32" %s\n", mark,
+				sha1_to_hex(deco->base->sha1)) < 0) {
+			    e = 1;
+			    break;
+			}
+		}
+		deco++;
+	}
+
+	e |= ferror(f);
+	e |= fclose(f);
+	if (e)
+		error("Unable to write marks file %s.", file);
+}
+
+static void import_marks(char *input_file)
+{
+	char line[512];
+	FILE *f = fopen(input_file, "r");
+	if (!f)
+		die_errno("cannot read '%s'", input_file);
+
+	while (fgets(line, sizeof(line), f)) {
+		uint32_t mark;
+		char *line_end, *mark_end;
+		unsigned char sha1[20];
+		struct object *object;
+
+		line_end = strchr(line, '\n');
+		if (line[0] != ':' || !line_end)
+			die("corrupt mark line: %s", line);
+		*line_end = '\0';
+
+		mark = strtoumax(line + 1, &mark_end, 10);
+		if (!mark || mark_end == line + 1
+			|| *mark_end != ' ' || get_sha1(mark_end + 1, sha1))
+			die("corrupt mark line: %s", line);
+
+		object = parse_object(sha1);
+		if (!object)
+			die ("Could not read blob %s", sha1_to_hex(sha1));
+
+		if (object->flags & SHOWN)
+			error("Object %s already has a mark", sha1_to_hex(sha1));
+
+		mark_object(object, mark);
+		if (last_idnum < mark)
+			last_idnum = mark;
+
+		object->flags |= SHOWN;
+	}
+	fclose(f);
+}
+
+int cmd_fast_export(int argc, const char **argv, const char *prefix)
+{
+	struct rev_info revs;
+	struct object_array commits = OBJECT_ARRAY_INIT;
+	struct string_list extra_refs = STRING_LIST_INIT_NODUP;
+	struct commit *commit;
+	char *export_filename = NULL, *import_filename = NULL;
+	struct option options[] = {
+		OPT_INTEGER(0, "progress", &progress,
+			    "show progress after <n> objects"),
+		OPT_CALLBACK(0, "signed-tags", &signed_tag_mode, "mode",
+			     "select handling of signed tags",
+			     parse_opt_signed_tag_mode),
+		OPT_CALLBACK(0, "tag-of-filtered-object", &tag_of_filtered_mode, "mode",
+			     "select handling of tags that tag filtered objects",
+			     parse_opt_tag_of_filtered_mode),
+		OPT_STRING(0, "export-marks", &export_filename, "file",
+			     "Dump marks to this file"),
+		OPT_STRING(0, "import-marks", &import_filename, "file",
+			     "Import marks from this file"),
+		OPT_BOOLEAN(0, "fake-missing-tagger", &fake_missing_tagger,
+			     "Fake a tagger when tags lack one"),
+		OPT_BOOLEAN(0, "full-tree", &full_tree,
+			     "Output full tree for each commit"),
+		OPT_BOOLEAN(0, "use-done-feature", &use_done_feature,
+			     "Use the done feature to terminate the stream"),
+		OPT_BOOL(0, "no-data", &no_data, "Skip output of blob data"),
+		OPT_END()
+	};
+
+	if (argc == 1)
+		usage_with_options (fast_export_usage, options);
+
+	/* we handle encodings */
+	git_config(git_default_config, NULL);
+
+	init_revisions(&revs, prefix);
+	revs.topo_order = 1;
+	revs.show_source = 1;
+	revs.rewrite_parents = 1;
+	argc = setup_revisions(argc, argv, &revs, NULL);
+	argc = parse_options(argc, argv, prefix, options, fast_export_usage, 0);
+	if (argc > 1)
+		usage_with_options (fast_export_usage, options);
+
+	if (use_done_feature)
+		printf("feature done\n");
+
+	if (import_filename)
+		import_marks(import_filename);
+
+	if (import_filename && revs.prune_data.nr)
+		full_tree = 1;
+
+	get_tags_and_duplicates(&revs.pending, &extra_refs);
+
+	if (prepare_revision_walk(&revs))
+		die("revision walk setup failed");
+	revs.diffopt.format_callback = show_filemodify;
+	DIFF_OPT_SET(&revs.diffopt, RECURSIVE);
+	while ((commit = get_revision(&revs))) {
+		if (has_unshown_parent(commit)) {
+			add_object_array(&commit->object, NULL, &commits);
+		}
+		else {
+			handle_commit(commit, &revs);
+			handle_tail(&commits, &revs);
+		}
+	}
+
+	handle_tags_and_duplicates(&extra_refs);
+
+	if (export_filename)
+		export_marks(export_filename);
+
+	if (use_done_feature)
+		printf("done\n");
+
+	return 0;
+}
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
new file mode 100644
index 0000000..149db88
--- /dev/null
+++ b/builtin/fetch-pack.c
@@ -0,0 +1,1124 @@
+#include "builtin.h"
+#include "refs.h"
+#include "pkt-line.h"
+#include "commit.h"
+#include "tag.h"
+#include "exec_cmd.h"
+#include "pack.h"
+#include "sideband.h"
+#include "fetch-pack.h"
+#include "remote.h"
+#include "run-command.h"
+#include "transport.h"
+
+static int transfer_unpack_limit = -1;
+static int fetch_unpack_limit = -1;
+static int unpack_limit = 100;
+static int prefer_ofs_delta = 1;
+static int no_done;
+static int fetch_fsck_objects = -1;
+static int transfer_fsck_objects = -1;
+static struct fetch_pack_args args = {
+	/* .uploadpack = */ "git-upload-pack",
+};
+
+static const char fetch_pack_usage[] =
+"git fetch-pack [--all] [--stdin] [--quiet|-q] [--keep|-k] [--thin] "
+"[--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] "
+"[--no-progress] [-v] [<host>:]<directory> [<refs>...]";
+
+#define COMPLETE	(1U << 0)
+#define COMMON		(1U << 1)
+#define COMMON_REF	(1U << 2)
+#define SEEN		(1U << 3)
+#define POPPED		(1U << 4)
+
+static int marked;
+
+/*
+ * After sending this many "have"s if we do not get any new ACK , we
+ * give up traversing our history.
+ */
+#define MAX_IN_VAIN 256
+
+static struct commit_list *rev_list;
+static int non_common_revs, multi_ack, use_sideband;
+
+static void rev_list_push(struct commit *commit, int mark)
+{
+	if (!(commit->object.flags & mark)) {
+		commit->object.flags |= mark;
+
+		if (!(commit->object.parsed))
+			if (parse_commit(commit))
+				return;
+
+		commit_list_insert_by_date(commit, &rev_list);
+
+		if (!(commit->object.flags & COMMON))
+			non_common_revs++;
+	}
+}
+
+static int rev_list_insert_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+{
+	struct object *o = deref_tag(parse_object(sha1), refname, 0);
+
+	if (o && o->type == OBJ_COMMIT)
+		rev_list_push((struct commit *)o, SEEN);
+
+	return 0;
+}
+
+static int clear_marks(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+{
+	struct object *o = deref_tag(parse_object(sha1), refname, 0);
+
+	if (o && o->type == OBJ_COMMIT)
+		clear_commit_marks((struct commit *)o,
+				   COMMON | COMMON_REF | SEEN | POPPED);
+	return 0;
+}
+
+/*
+   This function marks a rev and its ancestors as common.
+   In some cases, it is desirable to mark only the ancestors (for example
+   when only the server does not yet know that they are common).
+*/
+
+static void mark_common(struct commit *commit,
+		int ancestors_only, int dont_parse)
+{
+	if (commit != NULL && !(commit->object.flags & COMMON)) {
+		struct object *o = (struct object *)commit;
+
+		if (!ancestors_only)
+			o->flags |= COMMON;
+
+		if (!(o->flags & SEEN))
+			rev_list_push(commit, SEEN);
+		else {
+			struct commit_list *parents;
+
+			if (!ancestors_only && !(o->flags & POPPED))
+				non_common_revs--;
+			if (!o->parsed && !dont_parse)
+				if (parse_commit(commit))
+					return;
+
+			for (parents = commit->parents;
+					parents;
+					parents = parents->next)
+				mark_common(parents->item, 0, dont_parse);
+		}
+	}
+}
+
+/*
+  Get the next rev to send, ignoring the common.
+*/
+
+static const unsigned char *get_rev(void)
+{
+	struct commit *commit = NULL;
+
+	while (commit == NULL) {
+		unsigned int mark;
+		struct commit_list *parents;
+
+		if (rev_list == NULL || non_common_revs == 0)
+			return NULL;
+
+		commit = rev_list->item;
+		if (!commit->object.parsed)
+			parse_commit(commit);
+		parents = commit->parents;
+
+		commit->object.flags |= POPPED;
+		if (!(commit->object.flags & COMMON))
+			non_common_revs--;
+
+		if (commit->object.flags & COMMON) {
+			/* do not send "have", and ignore ancestors */
+			commit = NULL;
+			mark = COMMON | SEEN;
+		} else if (commit->object.flags & COMMON_REF)
+			/* send "have", and ignore ancestors */
+			mark = COMMON | SEEN;
+		else
+			/* send "have", also for its ancestors */
+			mark = SEEN;
+
+		while (parents) {
+			if (!(parents->item->object.flags & SEEN))
+				rev_list_push(parents->item, mark);
+			if (mark & COMMON)
+				mark_common(parents->item, 1, 0);
+			parents = parents->next;
+		}
+
+		rev_list = rev_list->next;
+	}
+
+	return commit->object.sha1;
+}
+
+enum ack_type {
+	NAK = 0,
+	ACK,
+	ACK_continue,
+	ACK_common,
+	ACK_ready
+};
+
+static void consume_shallow_list(int fd)
+{
+	if (args.stateless_rpc && args.depth > 0) {
+		/* If we sent a depth we will get back "duplicate"
+		 * shallow and unshallow commands every time there
+		 * is a block of have lines exchanged.
+		 */
+		char line[1000];
+		while (packet_read_line(fd, line, sizeof(line))) {
+			if (!prefixcmp(line, "shallow "))
+				continue;
+			if (!prefixcmp(line, "unshallow "))
+				continue;
+			die("git fetch-pack: expected shallow list");
+		}
+	}
+}
+
+struct write_shallow_data {
+	struct strbuf *out;
+	int use_pack_protocol;
+	int count;
+};
+
+static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
+{
+	struct write_shallow_data *data = cb_data;
+	const char *hex = sha1_to_hex(graft->sha1);
+	data->count++;
+	if (data->use_pack_protocol)
+		packet_buf_write(data->out, "shallow %s", hex);
+	else {
+		strbuf_addstr(data->out, hex);
+		strbuf_addch(data->out, '\n');
+	}
+	return 0;
+}
+
+static int write_shallow_commits(struct strbuf *out, int use_pack_protocol)
+{
+	struct write_shallow_data data;
+	data.out = out;
+	data.use_pack_protocol = use_pack_protocol;
+	data.count = 0;
+	for_each_commit_graft(write_one_shallow, &data);
+	return data.count;
+}
+
+static enum ack_type get_ack(int fd, unsigned char *result_sha1)
+{
+	static char line[1000];
+	int len = packet_read_line(fd, line, sizeof(line));
+
+	if (!len)
+		die("git fetch-pack: expected ACK/NAK, got EOF");
+	if (line[len-1] == '\n')
+		line[--len] = 0;
+	if (!strcmp(line, "NAK"))
+		return NAK;
+	if (!prefixcmp(line, "ACK ")) {
+		if (!get_sha1_hex(line+4, result_sha1)) {
+			if (strstr(line+45, "continue"))
+				return ACK_continue;
+			if (strstr(line+45, "common"))
+				return ACK_common;
+			if (strstr(line+45, "ready"))
+				return ACK_ready;
+			return ACK;
+		}
+	}
+	die("git fetch_pack: expected ACK/NAK, got '%s'", line);
+}
+
+static void send_request(int fd, struct strbuf *buf)
+{
+	if (args.stateless_rpc) {
+		send_sideband(fd, -1, buf->buf, buf->len, LARGE_PACKET_MAX);
+		packet_flush(fd);
+	} else
+		safe_write(fd, buf->buf, buf->len);
+}
+
+static void insert_one_alternate_ref(const struct ref *ref, void *unused)
+{
+	rev_list_insert_ref(NULL, ref->old_sha1, 0, NULL);
+}
+
+#define INITIAL_FLUSH 16
+#define PIPESAFE_FLUSH 32
+#define LARGE_FLUSH 1024
+
+static int next_flush(int count)
+{
+	int flush_limit = args.stateless_rpc ? LARGE_FLUSH : PIPESAFE_FLUSH;
+
+	if (count < flush_limit)
+		count <<= 1;
+	else
+		count += flush_limit;
+	return count;
+}
+
+static int find_common(int fd[2], unsigned char *result_sha1,
+		       struct ref *refs)
+{
+	int fetching;
+	int count = 0, flushes = 0, flush_at = INITIAL_FLUSH, retval;
+	const unsigned char *sha1;
+	unsigned in_vain = 0;
+	int got_continue = 0;
+	int got_ready = 0;
+	struct strbuf req_buf = STRBUF_INIT;
+	size_t state_len = 0;
+
+	if (args.stateless_rpc && multi_ack == 1)
+		die("--stateless-rpc requires multi_ack_detailed");
+	if (marked)
+		for_each_ref(clear_marks, NULL);
+	marked = 1;
+
+	for_each_ref(rev_list_insert_ref, NULL);
+	for_each_alternate_ref(insert_one_alternate_ref, NULL);
+
+	fetching = 0;
+	for ( ; refs ; refs = refs->next) {
+		unsigned char *remote = refs->old_sha1;
+		const char *remote_hex;
+		struct object *o;
+
+		/*
+		 * If that object is complete (i.e. it is an ancestor of a
+		 * local ref), we tell them we have it but do not have to
+		 * tell them about its ancestors, which they already know
+		 * about.
+		 *
+		 * We use lookup_object here because we are only
+		 * interested in the case we *know* the object is
+		 * reachable and we have already scanned it.
+		 */
+		if (((o = lookup_object(remote)) != NULL) &&
+				(o->flags & COMPLETE)) {
+			continue;
+		}
+
+		remote_hex = sha1_to_hex(remote);
+		if (!fetching) {
+			struct strbuf c = STRBUF_INIT;
+			if (multi_ack == 2)     strbuf_addstr(&c, " multi_ack_detailed");
+			if (multi_ack == 1)     strbuf_addstr(&c, " multi_ack");
+			if (no_done)            strbuf_addstr(&c, " no-done");
+			if (use_sideband == 2)  strbuf_addstr(&c, " side-band-64k");
+			if (use_sideband == 1)  strbuf_addstr(&c, " side-band");
+			if (args.use_thin_pack) strbuf_addstr(&c, " thin-pack");
+			if (args.no_progress)   strbuf_addstr(&c, " no-progress");
+			if (args.include_tag)   strbuf_addstr(&c, " include-tag");
+			if (prefer_ofs_delta)   strbuf_addstr(&c, " ofs-delta");
+			packet_buf_write(&req_buf, "want %s%s\n", remote_hex, c.buf);
+			strbuf_release(&c);
+		} else
+			packet_buf_write(&req_buf, "want %s\n", remote_hex);
+		fetching++;
+	}
+
+	if (!fetching) {
+		strbuf_release(&req_buf);
+		packet_flush(fd[1]);
+		return 1;
+	}
+
+	if (is_repository_shallow())
+		write_shallow_commits(&req_buf, 1);
+	if (args.depth > 0)
+		packet_buf_write(&req_buf, "deepen %d", args.depth);
+	packet_buf_flush(&req_buf);
+	state_len = req_buf.len;
+
+	if (args.depth > 0) {
+		char line[1024];
+		unsigned char sha1[20];
+
+		send_request(fd[1], &req_buf);
+		while (packet_read_line(fd[0], line, sizeof(line))) {
+			if (!prefixcmp(line, "shallow ")) {
+				if (get_sha1_hex(line + 8, sha1))
+					die("invalid shallow line: %s", line);
+				register_shallow(sha1);
+				continue;
+			}
+			if (!prefixcmp(line, "unshallow ")) {
+				if (get_sha1_hex(line + 10, sha1))
+					die("invalid unshallow line: %s", line);
+				if (!lookup_object(sha1))
+					die("object not found: %s", line);
+				/* make sure that it is parsed as shallow */
+				if (!parse_object(sha1))
+					die("error in object: %s", line);
+				if (unregister_shallow(sha1))
+					die("no shallow found: %s", line);
+				continue;
+			}
+			die("expected shallow/unshallow, got %s", line);
+		}
+	} else if (!args.stateless_rpc)
+		send_request(fd[1], &req_buf);
+
+	if (!args.stateless_rpc) {
+		/* If we aren't using the stateless-rpc interface
+		 * we don't need to retain the headers.
+		 */
+		strbuf_setlen(&req_buf, 0);
+		state_len = 0;
+	}
+
+	flushes = 0;
+	retval = -1;
+	while ((sha1 = get_rev())) {
+		packet_buf_write(&req_buf, "have %s\n", sha1_to_hex(sha1));
+		if (args.verbose)
+			fprintf(stderr, "have %s\n", sha1_to_hex(sha1));
+		in_vain++;
+		if (flush_at <= ++count) {
+			int ack;
+
+			packet_buf_flush(&req_buf);
+			send_request(fd[1], &req_buf);
+			strbuf_setlen(&req_buf, state_len);
+			flushes++;
+			flush_at = next_flush(count);
+
+			/*
+			 * We keep one window "ahead" of the other side, and
+			 * will wait for an ACK only on the next one
+			 */
+			if (!args.stateless_rpc && count == INITIAL_FLUSH)
+				continue;
+
+			consume_shallow_list(fd[0]);
+			do {
+				ack = get_ack(fd[0], result_sha1);
+				if (args.verbose && ack)
+					fprintf(stderr, "got ack %d %s\n", ack,
+							sha1_to_hex(result_sha1));
+				switch (ack) {
+				case ACK:
+					flushes = 0;
+					multi_ack = 0;
+					retval = 0;
+					goto done;
+				case ACK_common:
+				case ACK_ready:
+				case ACK_continue: {
+					struct commit *commit =
+						lookup_commit(result_sha1);
+					if (!commit)
+						die("invalid commit %s", sha1_to_hex(result_sha1));
+					if (args.stateless_rpc
+					 && ack == ACK_common
+					 && !(commit->object.flags & COMMON)) {
+						/* We need to replay the have for this object
+						 * on the next RPC request so the peer knows
+						 * it is in common with us.
+						 */
+						const char *hex = sha1_to_hex(result_sha1);
+						packet_buf_write(&req_buf, "have %s\n", hex);
+						state_len = req_buf.len;
+					}
+					mark_common(commit, 0, 1);
+					retval = 0;
+					in_vain = 0;
+					got_continue = 1;
+					if (ack == ACK_ready) {
+						rev_list = NULL;
+						got_ready = 1;
+					}
+					break;
+					}
+				}
+			} while (ack);
+			flushes--;
+			if (got_continue && MAX_IN_VAIN < in_vain) {
+				if (args.verbose)
+					fprintf(stderr, "giving up\n");
+				break; /* give up */
+			}
+		}
+	}
+done:
+	if (!got_ready || !no_done) {
+		packet_buf_write(&req_buf, "done\n");
+		send_request(fd[1], &req_buf);
+	}
+	if (args.verbose)
+		fprintf(stderr, "done\n");
+	if (retval != 0) {
+		multi_ack = 0;
+		flushes++;
+	}
+	strbuf_release(&req_buf);
+
+	consume_shallow_list(fd[0]);
+	while (flushes || multi_ack) {
+		int ack = get_ack(fd[0], result_sha1);
+		if (ack) {
+			if (args.verbose)
+				fprintf(stderr, "got ack (%d) %s\n", ack,
+					sha1_to_hex(result_sha1));
+			if (ack == ACK)
+				return 0;
+			multi_ack = 1;
+			continue;
+		}
+		flushes--;
+	}
+	/* it is no error to fetch into a completely empty repo */
+	return count ? retval : 0;
+}
+
+static struct commit_list *complete;
+
+static int mark_complete(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+{
+	struct object *o = parse_object(sha1);
+
+	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 == OBJ_COMMIT) {
+		struct commit *commit = (struct commit *)o;
+		if (!(commit->object.flags & COMPLETE)) {
+			commit->object.flags |= COMPLETE;
+			commit_list_insert_by_date(commit, &complete);
+		}
+	}
+	return 0;
+}
+
+static void mark_recent_complete_commits(unsigned long cutoff)
+{
+	while (complete && cutoff <= complete->item->date) {
+		if (args.verbose)
+			fprintf(stderr, "Marking %s as complete\n",
+				sha1_to_hex(complete->item->object.sha1));
+		pop_most_recent_commit(&complete, COMPLETE);
+	}
+}
+
+static void filter_refs(struct ref **refs, int nr_match, char **match)
+{
+	struct ref **return_refs;
+	struct ref *newlist = NULL;
+	struct ref **newtail = &newlist;
+	struct ref *ref, *next;
+	struct ref *fastarray[32];
+	int match_pos;
+
+	if (nr_match && !args.fetch_all) {
+		if (ARRAY_SIZE(fastarray) < nr_match)
+			return_refs = xcalloc(nr_match, sizeof(struct ref *));
+		else {
+			return_refs = fastarray;
+			memset(return_refs, 0, sizeof(struct ref *) * nr_match);
+		}
+	}
+	else
+		return_refs = NULL;
+
+	match_pos = 0;
+	for (ref = *refs; ref; ref = next) {
+		next = ref->next;
+		if (!memcmp(ref->name, "refs/", 5) &&
+		    check_refname_format(ref->name + 5, 0))
+			; /* trash */
+		else if (args.fetch_all &&
+			 (!args.depth || prefixcmp(ref->name, "refs/tags/") )) {
+			*newtail = ref;
+			ref->next = NULL;
+			newtail = &ref->next;
+			continue;
+		}
+		else {
+			int cmp = -1;
+			while (match_pos < nr_match) {
+				cmp = strcmp(ref->name, match[match_pos]);
+				if (cmp < 0) /* definitely do not have it */
+					break;
+				else if (cmp == 0) { /* definitely have it */
+					match[match_pos][0] = '\0';
+					return_refs[match_pos] = ref;
+					break;
+				}
+				else /* might have it; keep looking */
+					match_pos++;
+			}
+			if (!cmp)
+				continue; /* we will link it later */
+		}
+		free(ref);
+	}
+
+	if (!args.fetch_all) {
+		int i;
+		for (i = 0; i < nr_match; i++) {
+			ref = return_refs[i];
+			if (ref) {
+				*newtail = ref;
+				ref->next = NULL;
+				newtail = &ref->next;
+			}
+		}
+		if (return_refs != fastarray)
+			free(return_refs);
+	}
+	*refs = newlist;
+}
+
+static void mark_alternate_complete(const struct ref *ref, void *unused)
+{
+	mark_complete(NULL, ref->old_sha1, 0, NULL);
+}
+
+static int everything_local(struct ref **refs, int nr_match, char **match)
+{
+	struct ref *ref;
+	int retval;
+	unsigned long cutoff = 0;
+
+	save_commit_buffer = 0;
+
+	for (ref = *refs; ref; ref = ref->next) {
+		struct object *o;
+
+		o = parse_object(ref->old_sha1);
+		if (!o)
+			continue;
+
+		/* We already have it -- which may mean that we were
+		 * in sync with the other side at some time after
+		 * that (it is OK if we guess wrong here).
+		 */
+		if (o->type == OBJ_COMMIT) {
+			struct commit *commit = (struct commit *)o;
+			if (!cutoff || cutoff < commit->date)
+				cutoff = commit->date;
+		}
+	}
+
+	if (!args.depth) {
+		for_each_ref(mark_complete, NULL);
+		for_each_alternate_ref(mark_alternate_complete, NULL);
+		if (cutoff)
+			mark_recent_complete_commits(cutoff);
+	}
+
+	/*
+	 * Mark all complete remote refs as common refs.
+	 * Don't mark them common yet; the server has to be told so first.
+	 */
+	for (ref = *refs; ref; ref = ref->next) {
+		struct object *o = deref_tag(lookup_object(ref->old_sha1),
+					     NULL, 0);
+
+		if (!o || o->type != OBJ_COMMIT || !(o->flags & COMPLETE))
+			continue;
+
+		if (!(o->flags & SEEN)) {
+			rev_list_push((struct commit *)o, COMMON_REF | SEEN);
+
+			mark_common((struct commit *)o, 1, 1);
+		}
+	}
+
+	filter_refs(refs, nr_match, match);
+
+	for (retval = 1, ref = *refs; ref ; ref = ref->next) {
+		const unsigned char *remote = ref->old_sha1;
+		unsigned char local[20];
+		struct object *o;
+
+		o = lookup_object(remote);
+		if (!o || !(o->flags & COMPLETE)) {
+			retval = 0;
+			if (!args.verbose)
+				continue;
+			fprintf(stderr,
+				"want %s (%s)\n", sha1_to_hex(remote),
+				ref->name);
+			continue;
+		}
+
+		hashcpy(ref->new_sha1, local);
+		if (!args.verbose)
+			continue;
+		fprintf(stderr,
+			"already have %s (%s)\n", sha1_to_hex(remote),
+			ref->name);
+	}
+	return retval;
+}
+
+static int sideband_demux(int in, int out, void *data)
+{
+	int *xd = data;
+
+	int ret = recv_sideband("fetch-pack", xd[0], out);
+	close(out);
+	return ret;
+}
+
+static int get_pack(int xd[2], char **pack_lockfile)
+{
+	struct async demux;
+	const char *argv[20];
+	char keep_arg[256];
+	char hdr_arg[256];
+	const char **av;
+	int do_keep = args.keep_pack;
+	struct child_process cmd;
+
+	memset(&demux, 0, sizeof(demux));
+	if (use_sideband) {
+		/* xd[] is talking with upload-pack; subprocess reads from
+		 * xd[0], spits out band#2 to stderr, and feeds us band#1
+		 * through demux->out.
+		 */
+		demux.proc = sideband_demux;
+		demux.data = xd;
+		demux.out = -1;
+		if (start_async(&demux))
+			die("fetch-pack: unable to fork off sideband"
+			    " demultiplexer");
+	}
+	else
+		demux.out = xd[0];
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.argv = argv;
+	av = argv;
+	*hdr_arg = 0;
+	if (!args.keep_pack && unpack_limit) {
+		struct pack_header header;
+
+		if (read_pack_header(demux.out, &header))
+			die("protocol error: bad pack header");
+		snprintf(hdr_arg, sizeof(hdr_arg),
+			 "--pack_header=%"PRIu32",%"PRIu32,
+			 ntohl(header.hdr_version), ntohl(header.hdr_entries));
+		if (ntohl(header.hdr_entries) < unpack_limit)
+			do_keep = 0;
+		else
+			do_keep = 1;
+	}
+
+	if (do_keep) {
+		if (pack_lockfile)
+			cmd.out = -1;
+		*av++ = "index-pack";
+		*av++ = "--stdin";
+		if (!args.quiet && !args.no_progress)
+			*av++ = "-v";
+		if (args.use_thin_pack)
+			*av++ = "--fix-thin";
+		if (args.lock_pack || unpack_limit) {
+			int s = sprintf(keep_arg,
+					"--keep=fetch-pack %"PRIuMAX " on ", (uintmax_t) getpid());
+			if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
+				strcpy(keep_arg + s, "localhost");
+			*av++ = keep_arg;
+		}
+	}
+	else {
+		*av++ = "unpack-objects";
+		if (args.quiet || args.no_progress)
+			*av++ = "-q";
+	}
+	if (*hdr_arg)
+		*av++ = hdr_arg;
+	if (fetch_fsck_objects >= 0
+	    ? fetch_fsck_objects
+	    : transfer_fsck_objects >= 0
+	    ? transfer_fsck_objects
+	    : 0)
+		*av++ = "--strict";
+	*av++ = NULL;
+
+	cmd.in = demux.out;
+	cmd.git_cmd = 1;
+	if (start_command(&cmd))
+		die("fetch-pack: unable to fork off %s", argv[0]);
+	if (do_keep && pack_lockfile) {
+		*pack_lockfile = index_pack_lockfile(cmd.out);
+		close(cmd.out);
+	}
+
+	if (finish_command(&cmd))
+		die("%s failed", argv[0]);
+	if (use_sideband && finish_async(&demux))
+		die("error in sideband demultiplexer");
+	return 0;
+}
+
+static struct ref *do_fetch_pack(int fd[2],
+		const struct ref *orig_ref,
+		int nr_match,
+		char **match,
+		char **pack_lockfile)
+{
+	struct ref *ref = copy_ref_list(orig_ref);
+	unsigned char sha1[20];
+
+	sort_ref_list(&ref, ref_compare_name);
+
+	if (is_repository_shallow() && !server_supports("shallow"))
+		die("Server does not support shallow clients");
+	if (server_supports("multi_ack_detailed")) {
+		if (args.verbose)
+			fprintf(stderr, "Server supports multi_ack_detailed\n");
+		multi_ack = 2;
+		if (server_supports("no-done")) {
+			if (args.verbose)
+				fprintf(stderr, "Server supports no-done\n");
+			if (args.stateless_rpc)
+				no_done = 1;
+		}
+	}
+	else if (server_supports("multi_ack")) {
+		if (args.verbose)
+			fprintf(stderr, "Server supports multi_ack\n");
+		multi_ack = 1;
+	}
+	if (server_supports("side-band-64k")) {
+		if (args.verbose)
+			fprintf(stderr, "Server supports side-band-64k\n");
+		use_sideband = 2;
+	}
+	else if (server_supports("side-band")) {
+		if (args.verbose)
+			fprintf(stderr, "Server supports side-band\n");
+		use_sideband = 1;
+	}
+	if (server_supports("ofs-delta")) {
+		if (args.verbose)
+			fprintf(stderr, "Server supports ofs-delta\n");
+	} else
+		prefer_ofs_delta = 0;
+	if (everything_local(&ref, nr_match, match)) {
+		packet_flush(fd[1]);
+		goto all_done;
+	}
+	if (find_common(fd, sha1, ref) < 0)
+		if (!args.keep_pack)
+			/* When cloning, it is not unusual to have
+			 * no common commit.
+			 */
+			warning("no common commits");
+
+	if (args.stateless_rpc)
+		packet_flush(fd[1]);
+	if (get_pack(fd, pack_lockfile))
+		die("git fetch-pack: fetch failed.");
+
+ all_done:
+	return ref;
+}
+
+static int remove_duplicates(int nr_heads, char **heads)
+{
+	int src, dst;
+
+	if (!nr_heads)
+		return 0;
+
+	for (src = dst = 1; src < nr_heads; src++)
+		if (strcmp(heads[src], heads[dst-1]))
+			heads[dst++] = heads[src];
+	return dst;
+}
+
+static int fetch_pack_config(const char *var, const char *value, void *cb)
+{
+	if (strcmp(var, "fetch.unpacklimit") == 0) {
+		fetch_unpack_limit = git_config_int(var, value);
+		return 0;
+	}
+
+	if (strcmp(var, "transfer.unpacklimit") == 0) {
+		transfer_unpack_limit = git_config_int(var, value);
+		return 0;
+	}
+
+	if (strcmp(var, "repack.usedeltabaseoffset") == 0) {
+		prefer_ofs_delta = git_config_bool(var, value);
+		return 0;
+	}
+
+	if (!strcmp(var, "fetch.fsckobjects")) {
+		fetch_fsck_objects = git_config_bool(var, value);
+		return 0;
+	}
+
+	if (!strcmp(var, "transfer.fsckobjects")) {
+		transfer_fsck_objects = git_config_bool(var, value);
+		return 0;
+	}
+
+	return git_default_config(var, value, cb);
+}
+
+static struct lock_file lock;
+
+static void fetch_pack_setup(void)
+{
+	static int did_setup;
+	if (did_setup)
+		return;
+	git_config(fetch_pack_config, NULL);
+	if (0 <= transfer_unpack_limit)
+		unpack_limit = transfer_unpack_limit;
+	else if (0 <= fetch_unpack_limit)
+		unpack_limit = fetch_unpack_limit;
+	did_setup = 1;
+}
+
+int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
+{
+	int i, ret;
+	struct ref *ref = NULL;
+	const char *dest = NULL;
+	int alloc_heads = 0, nr_heads = 0;
+	char **heads = NULL;
+	int fd[2];
+	char *pack_lockfile = NULL;
+	char **pack_lockfile_ptr = NULL;
+	struct child_process *conn;
+
+	packet_trace_identity("fetch-pack");
+
+	for (i = 1; i < argc && *argv[i] == '-'; i++) {
+		const char *arg = argv[i];
+
+		if (!prefixcmp(arg, "--upload-pack=")) {
+			args.uploadpack = arg + 14;
+			continue;
+		}
+		if (!prefixcmp(arg, "--exec=")) {
+			args.uploadpack = arg + 7;
+			continue;
+		}
+		if (!strcmp("--quiet", arg) || !strcmp("-q", arg)) {
+			args.quiet = 1;
+			continue;
+		}
+		if (!strcmp("--keep", arg) || !strcmp("-k", arg)) {
+			args.lock_pack = args.keep_pack;
+			args.keep_pack = 1;
+			continue;
+		}
+		if (!strcmp("--thin", arg)) {
+			args.use_thin_pack = 1;
+			continue;
+		}
+		if (!strcmp("--include-tag", arg)) {
+			args.include_tag = 1;
+			continue;
+		}
+		if (!strcmp("--all", arg)) {
+			args.fetch_all = 1;
+			continue;
+		}
+		if (!strcmp("--stdin", arg)) {
+			args.stdin_refs = 1;
+			continue;
+		}
+		if (!strcmp("-v", arg)) {
+			args.verbose = 1;
+			continue;
+		}
+		if (!prefixcmp(arg, "--depth=")) {
+			args.depth = strtol(arg + 8, NULL, 0);
+			continue;
+		}
+		if (!strcmp("--no-progress", arg)) {
+			args.no_progress = 1;
+			continue;
+		}
+		if (!strcmp("--stateless-rpc", arg)) {
+			args.stateless_rpc = 1;
+			continue;
+		}
+		if (!strcmp("--lock-pack", arg)) {
+			args.lock_pack = 1;
+			pack_lockfile_ptr = &pack_lockfile;
+			continue;
+		}
+		usage(fetch_pack_usage);
+	}
+
+	if (i < argc)
+		dest = argv[i++];
+	else
+		usage(fetch_pack_usage);
+
+	/*
+	 * Copy refs from cmdline to growable list, then append any
+	 * refs from the standard input:
+	 */
+	ALLOC_GROW(heads, argc - i, alloc_heads);
+	for (; i < argc; i++)
+		heads[nr_heads++] = xstrdup(argv[i]);
+	if (args.stdin_refs) {
+		if (args.stateless_rpc) {
+			/* in stateless RPC mode we use pkt-line to read
+			 * from stdin, until we get a flush packet
+			 */
+			static char line[1000];
+			for (;;) {
+				int n = packet_read_line(0, line, sizeof(line));
+				if (!n)
+					break;
+				if (line[n-1] == '\n')
+					n--;
+				ALLOC_GROW(heads, nr_heads + 1, alloc_heads);
+				heads[nr_heads++] = xmemdupz(line, n);
+			}
+		}
+		else {
+			/* read from stdin one ref per line, until EOF */
+			struct strbuf line = STRBUF_INIT;
+			while (strbuf_getline(&line, stdin, '\n') != EOF) {
+				ALLOC_GROW(heads, nr_heads + 1, alloc_heads);
+				heads[nr_heads++] = strbuf_detach(&line, NULL);
+			}
+			strbuf_release(&line);
+		}
+	}
+
+	if (args.stateless_rpc) {
+		conn = NULL;
+		fd[0] = 0;
+		fd[1] = 1;
+	} else {
+		conn = git_connect(fd, dest, args.uploadpack,
+				   args.verbose ? CONNECT_VERBOSE : 0);
+	}
+
+	get_remote_heads(fd[0], &ref, 0, NULL);
+
+	ref = fetch_pack(&args, fd, conn, ref, dest,
+		nr_heads, heads, pack_lockfile_ptr);
+	if (pack_lockfile) {
+		printf("lock %s\n", pack_lockfile);
+		fflush(stdout);
+	}
+	close(fd[0]);
+	close(fd[1]);
+	if (finish_connect(conn))
+		ref = NULL;
+	ret = !ref;
+
+	if (!ret && nr_heads) {
+		/* If the heads to pull were given, we should have
+		 * consumed all of them by matching the remote.
+		 * Otherwise, 'git fetch remote no-such-ref' would
+		 * silently succeed without issuing an error.
+		 */
+		for (i = 0; i < nr_heads; i++)
+			if (heads[i] && heads[i][0]) {
+				error("no such remote ref %s", heads[i]);
+				ret = 1;
+			}
+	}
+	while (ref) {
+		printf("%s %s\n",
+		       sha1_to_hex(ref->old_sha1), ref->name);
+		ref = ref->next;
+	}
+
+	return ret;
+}
+
+static int compare_heads(const void *a, const void *b)
+{
+	return strcmp(*(const char **)a, *(const char **)b);
+}
+
+struct ref *fetch_pack(struct fetch_pack_args *my_args,
+		       int fd[], struct child_process *conn,
+		       const struct ref *ref,
+		const char *dest,
+		int nr_heads,
+		char **heads,
+		char **pack_lockfile)
+{
+	struct stat st;
+	struct ref *ref_cpy;
+
+	fetch_pack_setup();
+	if (&args != my_args)
+		memcpy(&args, my_args, sizeof(args));
+	if (args.depth > 0) {
+		if (stat(git_path("shallow"), &st))
+			st.st_mtime = 0;
+	}
+
+	if (heads && nr_heads) {
+		qsort(heads, nr_heads, sizeof(*heads), compare_heads);
+		nr_heads = remove_duplicates(nr_heads, heads);
+	}
+
+	if (!ref) {
+		packet_flush(fd[1]);
+		die("no matching remote head");
+	}
+	ref_cpy = do_fetch_pack(fd, ref, nr_heads, heads, pack_lockfile);
+
+	if (args.depth > 0) {
+		struct cache_time mtime;
+		struct strbuf sb = STRBUF_INIT;
+		char *shallow = git_path("shallow");
+		int fd;
+
+		mtime.sec = st.st_mtime;
+		mtime.nsec = ST_MTIME_NSEC(st);
+		if (stat(shallow, &st)) {
+			if (mtime.sec)
+				die("shallow file was removed during fetch");
+		} else if (st.st_mtime != mtime.sec
+#ifdef USE_NSEC
+				|| ST_MTIME_NSEC(st) != mtime.nsec
+#endif
+			  )
+			die("shallow file was changed during fetch");
+
+		fd = hold_lock_file_for_update(&lock, shallow,
+					       LOCK_DIE_ON_ERROR);
+		if (!write_shallow_commits(&sb, 0)
+		 || write_in_full(fd, sb.buf, sb.len) != sb.len) {
+			unlink_or_warn(shallow);
+			rollback_lock_file(&lock);
+		} else {
+			commit_lock_file(&lock);
+		}
+		strbuf_release(&sb);
+	}
+
+	reprepare_packed_git();
+	return ref_cpy;
+}
diff --git a/builtin/fetch.c b/builtin/fetch.c
new file mode 100644
index 0000000..bb9a074
--- /dev/null
+++ b/builtin/fetch.c
@@ -0,0 +1,1024 @@
+/*
+ * "git fetch"
+ */
+#include "cache.h"
+#include "refs.h"
+#include "commit.h"
+#include "builtin.h"
+#include "string-list.h"
+#include "remote.h"
+#include "transport.h"
+#include "run-command.h"
+#include "parse-options.h"
+#include "sigchain.h"
+#include "transport.h"
+#include "submodule.h"
+#include "connected.h"
+
+static const char * const builtin_fetch_usage[] = {
+	"git fetch [<options>] [<repository> [<refspec>...]]",
+	"git fetch [<options>] <group>",
+	"git fetch --multiple [<options>] [(<repository> | <group>)...]",
+	"git fetch --all [<options>]",
+	NULL
+};
+
+enum {
+	TAGS_UNSET = 0,
+	TAGS_DEFAULT = 1,
+	TAGS_SET = 2
+};
+
+static int all, append, dry_run, force, keep, multiple, prune, update_head_ok, verbosity;
+static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
+static int tags = TAGS_DEFAULT;
+static const char *depth;
+static const char *upload_pack;
+static struct strbuf default_rla = STRBUF_INIT;
+static struct transport *transport;
+static const char *submodule_prefix = "";
+static const char *recurse_submodules_default;
+
+static int option_parse_recurse_submodules(const struct option *opt,
+				   const char *arg, int unset)
+{
+	if (unset) {
+		recurse_submodules = RECURSE_SUBMODULES_OFF;
+	} else {
+		if (arg)
+			recurse_submodules = parse_fetch_recurse_submodules_arg(opt->long_name, arg);
+		else
+			recurse_submodules = RECURSE_SUBMODULES_ON;
+	}
+	return 0;
+}
+
+static struct option builtin_fetch_options[] = {
+	OPT__VERBOSITY(&verbosity),
+	OPT_BOOLEAN(0, "all", &all,
+		    "fetch from all remotes"),
+	OPT_BOOLEAN('a', "append", &append,
+		    "append to .git/FETCH_HEAD instead of overwriting"),
+	OPT_STRING(0, "upload-pack", &upload_pack, "path",
+		   "path to upload pack on remote end"),
+	OPT__FORCE(&force, "force overwrite of local branch"),
+	OPT_BOOLEAN('m', "multiple", &multiple,
+		    "fetch from multiple remotes"),
+	OPT_SET_INT('t', "tags", &tags,
+		    "fetch all tags and associated objects", TAGS_SET),
+	OPT_SET_INT('n', NULL, &tags,
+		    "do not fetch all tags (--no-tags)", TAGS_UNSET),
+	OPT_BOOLEAN('p', "prune", &prune,
+		    "prune remote-tracking branches no longer on remote"),
+	{ OPTION_CALLBACK, 0, "recurse-submodules", NULL, "on-demand",
+		    "control recursive fetching of submodules",
+		    PARSE_OPT_OPTARG, option_parse_recurse_submodules },
+	OPT_BOOLEAN(0, "dry-run", &dry_run,
+		    "dry run"),
+	OPT_BOOLEAN('k', "keep", &keep, "keep downloaded pack"),
+	OPT_BOOLEAN('u', "update-head-ok", &update_head_ok,
+		    "allow updating of HEAD ref"),
+	OPT_BOOL(0, "progress", &progress, "force progress reporting"),
+	OPT_STRING(0, "depth", &depth, "depth",
+		   "deepen history of shallow clone"),
+	{ OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, "dir",
+		   "prepend this to submodule path output", PARSE_OPT_HIDDEN },
+	{ OPTION_STRING, 0, "recurse-submodules-default",
+		   &recurse_submodules_default, NULL,
+		   "default mode for recursion", PARSE_OPT_HIDDEN },
+	OPT_END()
+};
+
+static void unlock_pack(void)
+{
+	if (transport)
+		transport_unlock_pack(transport);
+}
+
+static void unlock_pack_on_signal(int signo)
+{
+	unlock_pack();
+	sigchain_pop(signo);
+	raise(signo);
+}
+
+static void add_merge_config(struct ref **head,
+			   const struct ref *remote_refs,
+		           struct branch *branch,
+		           struct ref ***tail)
+{
+	int i;
+
+	for (i = 0; i < branch->merge_nr; i++) {
+		struct ref *rm, **old_tail = *tail;
+		struct refspec refspec;
+
+		for (rm = *head; rm; rm = rm->next) {
+			if (branch_merge_matches(branch, i, rm->name)) {
+				rm->merge = 1;
+				break;
+			}
+		}
+		if (rm)
+			continue;
+
+		/*
+		 * Not fetched to a remote-tracking branch?  We need to fetch
+		 * it anyway to allow this branch's "branch.$name.merge"
+		 * to be honored by 'git pull', but we do not have to
+		 * fail if branch.$name.merge is misconfigured to point
+		 * at a nonexisting branch.  If we were indeed called by
+		 * 'git pull', it will notice the misconfiguration because
+		 * there is no entry in the resulting FETCH_HEAD marked
+		 * for merging.
+		 */
+		memset(&refspec, 0, sizeof(refspec));
+		refspec.src = branch->merge[i]->src;
+		get_fetch_map(remote_refs, &refspec, tail, 1);
+		for (rm = *old_tail; rm; rm = rm->next)
+			rm->merge = 1;
+	}
+}
+
+static void find_non_local_tags(struct transport *transport,
+			struct ref **head,
+			struct ref ***tail);
+
+static struct ref *get_ref_map(struct transport *transport,
+			       struct refspec *refs, int ref_count, int tags,
+			       int *autotags)
+{
+	int i;
+	struct ref *rm;
+	struct ref *ref_map = NULL;
+	struct ref **tail = &ref_map;
+
+	const struct ref *remote_refs = transport_get_remote_refs(transport);
+
+	if (ref_count || tags == TAGS_SET) {
+		for (i = 0; i < ref_count; i++) {
+			get_fetch_map(remote_refs, &refs[i], &tail, 0);
+			if (refs[i].dst && refs[i].dst[0])
+				*autotags = 1;
+		}
+		/* Merge everything on the command line, but not --tags */
+		for (rm = ref_map; rm; rm = rm->next)
+			rm->merge = 1;
+		if (tags == TAGS_SET)
+			get_fetch_map(remote_refs, tag_refspec, &tail, 0);
+	} else {
+		/* Use the defaults */
+		struct remote *remote = transport->remote;
+		struct branch *branch = branch_get(NULL);
+		int has_merge = branch_has_merge_config(branch);
+		if (remote &&
+		    (remote->fetch_refspec_nr ||
+		     /* Note: has_merge implies non-NULL branch->remote_name */
+		     (has_merge && !strcmp(branch->remote_name, remote->name)))) {
+			for (i = 0; i < remote->fetch_refspec_nr; i++) {
+				get_fetch_map(remote_refs, &remote->fetch[i], &tail, 0);
+				if (remote->fetch[i].dst &&
+				    remote->fetch[i].dst[0])
+					*autotags = 1;
+				if (!i && !has_merge && ref_map &&
+				    !remote->fetch[0].pattern)
+					ref_map->merge = 1;
+			}
+			/*
+			 * if the remote we're fetching from is the same
+			 * as given in branch.<name>.remote, we add the
+			 * ref given in branch.<name>.merge, too.
+			 *
+			 * Note: has_merge implies non-NULL branch->remote_name
+			 */
+			if (has_merge &&
+			    !strcmp(branch->remote_name, remote->name))
+				add_merge_config(&ref_map, remote_refs, branch, &tail);
+		} else {
+			ref_map = get_remote_ref(remote_refs, "HEAD");
+			if (!ref_map)
+				die(_("Couldn't find remote ref HEAD"));
+			ref_map->merge = 1;
+			tail = &ref_map->next;
+		}
+	}
+	if (tags == TAGS_DEFAULT && *autotags)
+		find_non_local_tags(transport, &ref_map, &tail);
+	ref_remove_duplicates(ref_map);
+
+	return ref_map;
+}
+
+#define STORE_REF_ERROR_OTHER 1
+#define STORE_REF_ERROR_DF_CONFLICT 2
+
+static int s_update_ref(const char *action,
+			struct ref *ref,
+			int check_old)
+{
+	char msg[1024];
+	char *rla = getenv("GIT_REFLOG_ACTION");
+	static struct ref_lock *lock;
+
+	if (dry_run)
+		return 0;
+	if (!rla)
+		rla = default_rla.buf;
+	snprintf(msg, sizeof(msg), "%s: %s", rla, action);
+	lock = lock_any_ref_for_update(ref->name,
+				       check_old ? ref->old_sha1 : NULL, 0);
+	if (!lock)
+		return errno == ENOTDIR ? STORE_REF_ERROR_DF_CONFLICT :
+					  STORE_REF_ERROR_OTHER;
+	if (write_ref_sha1(lock, ref->new_sha1, msg) < 0)
+		return errno == ENOTDIR ? STORE_REF_ERROR_DF_CONFLICT :
+					  STORE_REF_ERROR_OTHER;
+	return 0;
+}
+
+#define REFCOL_WIDTH  10
+
+static int update_local_ref(struct ref *ref,
+			    const char *remote,
+			    const struct ref *remote_ref,
+			    struct strbuf *display)
+{
+	struct commit *current = NULL, *updated;
+	enum object_type type;
+	struct branch *current_branch = branch_get(NULL);
+	const char *pretty_ref = prettify_refname(ref->name);
+
+	type = sha1_object_info(ref->new_sha1, NULL);
+	if (type < 0)
+		die(_("object %s not found"), sha1_to_hex(ref->new_sha1));
+
+	if (!hashcmp(ref->old_sha1, ref->new_sha1)) {
+		if (verbosity > 0)
+			strbuf_addf(display, "= %-*s %-*s -> %s",
+				    TRANSPORT_SUMMARY_WIDTH,
+				    _("[up to date]"), REFCOL_WIDTH,
+				    remote, pretty_ref);
+		return 0;
+	}
+
+	if (current_branch &&
+	    !strcmp(ref->name, current_branch->name) &&
+	    !(update_head_ok || is_bare_repository()) &&
+	    !is_null_sha1(ref->old_sha1)) {
+		/*
+		 * If this is the head, and it's not okay to update
+		 * the head, and the old value of the head isn't empty...
+		 */
+		strbuf_addf(display,
+			    _("! %-*s %-*s -> %s  (can't fetch in current branch)"),
+			    TRANSPORT_SUMMARY_WIDTH, _("[rejected]"),
+			    REFCOL_WIDTH, remote, pretty_ref);
+		return 1;
+	}
+
+	if (!is_null_sha1(ref->old_sha1) &&
+	    !prefixcmp(ref->name, "refs/tags/")) {
+		int r;
+		r = s_update_ref("updating tag", ref, 0);
+		strbuf_addf(display, "%c %-*s %-*s -> %s%s",
+			    r ? '!' : '-',
+			    TRANSPORT_SUMMARY_WIDTH, _("[tag update]"),
+			    REFCOL_WIDTH, remote, pretty_ref,
+			    r ? _("  (unable to update local ref)") : "");
+		return r;
+	}
+
+	current = lookup_commit_reference_gently(ref->old_sha1, 1);
+	updated = lookup_commit_reference_gently(ref->new_sha1, 1);
+	if (!current || !updated) {
+		const char *msg;
+		const char *what;
+		int r;
+		/*
+		 * Nicely describe the new ref we're fetching.
+		 * Base this on the remote's ref name, as it's
+		 * more likely to follow a standard layout.
+		 */
+		const char *name = remote_ref ? remote_ref->name : "";
+		if (!prefixcmp(name, "refs/tags/")) {
+			msg = "storing tag";
+			what = _("[new tag]");
+		} else if (!prefixcmp(name, "refs/heads/")) {
+			msg = "storing head";
+			what = _("[new branch]");
+		} else {
+			msg = "storing ref";
+			what = _("[new ref]");
+		}
+
+		if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
+		    (recurse_submodules != RECURSE_SUBMODULES_ON))
+			check_for_new_submodule_commits(ref->new_sha1);
+		r = s_update_ref(msg, ref, 0);
+		strbuf_addf(display, "%c %-*s %-*s -> %s%s",
+			    r ? '!' : '*',
+			    TRANSPORT_SUMMARY_WIDTH, what,
+			    REFCOL_WIDTH, remote, pretty_ref,
+			    r ? _("  (unable to update local ref)") : "");
+		return r;
+	}
+
+	if (in_merge_bases(current, &updated, 1)) {
+		char quickref[83];
+		int r;
+		strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));
+		strcat(quickref, "..");
+		strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));
+		if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
+		    (recurse_submodules != RECURSE_SUBMODULES_ON))
+			check_for_new_submodule_commits(ref->new_sha1);
+		r = s_update_ref("fast-forward", ref, 1);
+		strbuf_addf(display, "%c %-*s %-*s -> %s%s",
+			    r ? '!' : ' ',
+			    TRANSPORT_SUMMARY_WIDTH, quickref,
+			    REFCOL_WIDTH, remote, pretty_ref,
+			    r ? _("  (unable to update local ref)") : "");
+		return r;
+	} else if (force || ref->force) {
+		char quickref[84];
+		int r;
+		strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));
+		strcat(quickref, "...");
+		strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));
+		if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
+		    (recurse_submodules != RECURSE_SUBMODULES_ON))
+			check_for_new_submodule_commits(ref->new_sha1);
+		r = s_update_ref("forced-update", ref, 1);
+		strbuf_addf(display, "%c %-*s %-*s -> %s  (%s)",
+			    r ? '!' : '+',
+			    TRANSPORT_SUMMARY_WIDTH, quickref,
+			    REFCOL_WIDTH, remote, pretty_ref,
+			    r ? _("unable to update local ref") : _("forced update"));
+		return r;
+	} else {
+		strbuf_addf(display, "! %-*s %-*s -> %s  %s",
+			    TRANSPORT_SUMMARY_WIDTH, _("[rejected]"),
+			    REFCOL_WIDTH, remote, pretty_ref,
+			    _("(non-fast-forward)"));
+		return 1;
+	}
+}
+
+static int iterate_ref_map(void *cb_data, unsigned char sha1[20])
+{
+	struct ref **rm = cb_data;
+	struct ref *ref = *rm;
+
+	if (!ref)
+		return -1; /* end of the list */
+	*rm = ref->next;
+	hashcpy(sha1, ref->old_sha1);
+	return 0;
+}
+
+static int store_updated_refs(const char *raw_url, const char *remote_name,
+		struct ref *ref_map)
+{
+	FILE *fp;
+	struct commit *commit;
+	int url_len, i, shown_url = 0, rc = 0;
+	struct strbuf note = STRBUF_INIT;
+	const char *what, *kind;
+	struct ref *rm;
+	char *url, *filename = dry_run ? "/dev/null" : git_path("FETCH_HEAD");
+	int want_merge;
+
+	fp = fopen(filename, "a");
+	if (!fp)
+		return error(_("cannot open %s: %s\n"), filename, strerror(errno));
+
+	if (raw_url)
+		url = transport_anonymize_url(raw_url);
+	else
+		url = xstrdup("foreign");
+
+	rm = ref_map;
+	if (check_everything_connected(iterate_ref_map, 0, &rm)) {
+		rc = error(_("%s did not send all necessary objects\n"), url);
+		goto abort;
+	}
+
+	/*
+	 * The first pass writes objects to be merged and then the
+	 * second pass writes the rest, in order to allow using
+	 * FETCH_HEAD as a refname to refer to the ref to be merged.
+	 */
+	for (want_merge = 1; 0 <= want_merge; want_merge--) {
+		for (rm = ref_map; rm; rm = rm->next) {
+			struct ref *ref = NULL;
+
+			commit = lookup_commit_reference_gently(rm->old_sha1, 1);
+			if (!commit)
+				rm->merge = 0;
+
+			if (rm->merge != want_merge)
+				continue;
+
+			if (rm->peer_ref) {
+				ref = xcalloc(1, sizeof(*ref) + strlen(rm->peer_ref->name) + 1);
+				strcpy(ref->name, rm->peer_ref->name);
+				hashcpy(ref->old_sha1, rm->peer_ref->old_sha1);
+				hashcpy(ref->new_sha1, rm->old_sha1);
+				ref->force = rm->peer_ref->force;
+			}
+
+
+			if (!strcmp(rm->name, "HEAD")) {
+				kind = "";
+				what = "";
+			}
+			else if (!prefixcmp(rm->name, "refs/heads/")) {
+				kind = "branch";
+				what = rm->name + 11;
+			}
+			else if (!prefixcmp(rm->name, "refs/tags/")) {
+				kind = "tag";
+				what = rm->name + 10;
+			}
+			else if (!prefixcmp(rm->name, "refs/remotes/")) {
+				kind = "remote-tracking branch";
+				what = rm->name + 13;
+			}
+			else {
+				kind = "";
+				what = rm->name;
+			}
+
+			url_len = strlen(url);
+			for (i = url_len - 1; url[i] == '/' && 0 <= i; i--)
+				;
+			url_len = i + 1;
+			if (4 < i && !strncmp(".git", url + i - 3, 4))
+				url_len = i - 3;
+
+			strbuf_reset(&note);
+			if (*what) {
+				if (*kind)
+					strbuf_addf(&note, "%s ", kind);
+				strbuf_addf(&note, "'%s' of ", what);
+			}
+			fprintf(fp, "%s\t%s\t%s",
+				sha1_to_hex(rm->old_sha1),
+				rm->merge ? "" : "not-for-merge",
+				note.buf);
+			for (i = 0; i < url_len; ++i)
+				if ('\n' == url[i])
+					fputs("\\n", fp);
+				else
+					fputc(url[i], fp);
+			fputc('\n', fp);
+
+			strbuf_reset(&note);
+			if (ref) {
+				rc |= update_local_ref(ref, what, rm, &note);
+				free(ref);
+			} else
+				strbuf_addf(&note, "* %-*s %-*s -> FETCH_HEAD",
+					    TRANSPORT_SUMMARY_WIDTH,
+					    *kind ? kind : "branch",
+					    REFCOL_WIDTH,
+					    *what ? what : "HEAD");
+			if (note.len) {
+				if (verbosity >= 0 && !shown_url) {
+					fprintf(stderr, _("From %.*s\n"),
+							url_len, url);
+					shown_url = 1;
+				}
+				if (verbosity >= 0)
+					fprintf(stderr, " %s\n", note.buf);
+			}
+		}
+	}
+
+	if (rc & STORE_REF_ERROR_DF_CONFLICT)
+		error(_("some local refs could not be updated; try running\n"
+		      " 'git remote prune %s' to remove any old, conflicting "
+		      "branches"), remote_name);
+
+ abort:
+	strbuf_release(&note);
+	free(url);
+	fclose(fp);
+	return rc;
+}
+
+/*
+ * We would want to bypass the object transfer altogether if
+ * everything we are going to fetch already exists and is connected
+ * locally.
+ */
+static int quickfetch(struct ref *ref_map)
+{
+	struct ref *rm = ref_map;
+
+	/*
+	 * If we are deepening a shallow clone we already have these
+	 * objects reachable.  Running rev-list here will return with
+	 * a good (0) exit status and we'll bypass the fetch that we
+	 * really need to perform.  Claiming failure now will ensure
+	 * we perform the network exchange to deepen our history.
+	 */
+	if (depth)
+		return -1;
+	return check_everything_connected(iterate_ref_map, 1, &rm);
+}
+
+static int fetch_refs(struct transport *transport, struct ref *ref_map)
+{
+	int ret = quickfetch(ref_map);
+	if (ret)
+		ret = transport_fetch_refs(transport, ref_map);
+	if (!ret)
+		ret |= store_updated_refs(transport->url,
+				transport->remote->name,
+				ref_map);
+	transport_unlock_pack(transport);
+	return ret;
+}
+
+static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map)
+{
+	int result = 0;
+	struct ref *ref, *stale_refs = get_stale_heads(refs, ref_count, ref_map);
+	const char *dangling_msg = dry_run
+		? _("   (%s will become dangling)")
+		: _("   (%s has become dangling)");
+
+	for (ref = stale_refs; ref; ref = ref->next) {
+		if (!dry_run)
+			result |= delete_ref(ref->name, NULL, 0);
+		if (verbosity >= 0) {
+			fprintf(stderr, " x %-*s %-*s -> %s\n",
+				TRANSPORT_SUMMARY_WIDTH, _("[deleted]"),
+				REFCOL_WIDTH, _("(none)"), prettify_refname(ref->name));
+			warn_dangling_symref(stderr, dangling_msg, ref->name);
+		}
+	}
+	free_refs(stale_refs);
+	return result;
+}
+
+static int add_existing(const char *refname, const unsigned char *sha1,
+			int flag, void *cbdata)
+{
+	struct string_list *list = (struct string_list *)cbdata;
+	struct string_list_item *item = string_list_insert(list, refname);
+	item->util = (void *)sha1;
+	return 0;
+}
+
+static int will_fetch(struct ref **head, const unsigned char *sha1)
+{
+	struct ref *rm = *head;
+	while (rm) {
+		if (!hashcmp(rm->old_sha1, sha1))
+			return 1;
+		rm = rm->next;
+	}
+	return 0;
+}
+
+static void find_non_local_tags(struct transport *transport,
+			struct ref **head,
+			struct ref ***tail)
+{
+	struct string_list existing_refs = STRING_LIST_INIT_NODUP;
+	struct string_list remote_refs = STRING_LIST_INIT_NODUP;
+	const struct ref *ref;
+	struct string_list_item *item = NULL;
+
+	for_each_ref(add_existing, &existing_refs);
+	for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) {
+		if (prefixcmp(ref->name, "refs/tags/"))
+			continue;
+
+		/*
+		 * The peeled ref always follows the matching base
+		 * ref, so if we see a peeled ref that we don't want
+		 * to fetch then we can mark the ref entry in the list
+		 * as one to ignore by setting util to NULL.
+		 */
+		if (!suffixcmp(ref->name, "^{}")) {
+			if (item && !has_sha1_file(ref->old_sha1) &&
+			    !will_fetch(head, ref->old_sha1) &&
+			    !has_sha1_file(item->util) &&
+			    !will_fetch(head, item->util))
+				item->util = NULL;
+			item = NULL;
+			continue;
+		}
+
+		/*
+		 * If item is non-NULL here, then we previously saw a
+		 * ref not followed by a peeled reference, so we need
+		 * to check if it is a lightweight tag that we want to
+		 * fetch.
+		 */
+		if (item && !has_sha1_file(item->util) &&
+		    !will_fetch(head, item->util))
+			item->util = NULL;
+
+		item = NULL;
+
+		/* skip duplicates and refs that we already have */
+		if (string_list_has_string(&remote_refs, ref->name) ||
+		    string_list_has_string(&existing_refs, ref->name))
+			continue;
+
+		item = string_list_insert(&remote_refs, ref->name);
+		item->util = (void *)ref->old_sha1;
+	}
+	string_list_clear(&existing_refs, 0);
+
+	/*
+	 * We may have a final lightweight tag that needs to be
+	 * checked to see if it needs fetching.
+	 */
+	if (item && !has_sha1_file(item->util) &&
+	    !will_fetch(head, item->util))
+		item->util = NULL;
+
+	/*
+	 * For all the tags in the remote_refs string list,
+	 * add them to the list of refs to be fetched
+	 */
+	for_each_string_list_item(item, &remote_refs) {
+		/* Unless we have already decided to ignore this item... */
+		if (item->util)
+		{
+			struct ref *rm = alloc_ref(item->string);
+			rm->peer_ref = alloc_ref(item->string);
+			hashcpy(rm->old_sha1, item->util);
+			**tail = rm;
+			*tail = &rm->next;
+		}
+	}
+
+	string_list_clear(&remote_refs, 0);
+}
+
+static void check_not_current_branch(struct ref *ref_map)
+{
+	struct branch *current_branch = branch_get(NULL);
+
+	if (is_bare_repository() || !current_branch)
+		return;
+
+	for (; ref_map; ref_map = ref_map->next)
+		if (ref_map->peer_ref && !strcmp(current_branch->refname,
+					ref_map->peer_ref->name))
+			die(_("Refusing to fetch into current branch %s "
+			    "of non-bare repository"), current_branch->refname);
+}
+
+static int truncate_fetch_head(void)
+{
+	char *filename = git_path("FETCH_HEAD");
+	FILE *fp = fopen(filename, "w");
+
+	if (!fp)
+		return error(_("cannot open %s: %s\n"), filename, strerror(errno));
+	fclose(fp);
+	return 0;
+}
+
+static int do_fetch(struct transport *transport,
+		    struct refspec *refs, int ref_count)
+{
+	struct string_list existing_refs = STRING_LIST_INIT_NODUP;
+	struct string_list_item *peer_item = NULL;
+	struct ref *ref_map;
+	struct ref *rm;
+	int autotags = (transport->remote->fetch_tags == 1);
+
+	for_each_ref(add_existing, &existing_refs);
+
+	if (tags == TAGS_DEFAULT) {
+		if (transport->remote->fetch_tags == 2)
+			tags = TAGS_SET;
+		if (transport->remote->fetch_tags == -1)
+			tags = TAGS_UNSET;
+	}
+
+	if (!transport->get_refs_list || !transport->fetch)
+		die(_("Don't know how to fetch from %s"), transport->url);
+
+	/* if not appending, truncate FETCH_HEAD */
+	if (!append && !dry_run) {
+		int errcode = truncate_fetch_head();
+		if (errcode)
+			return errcode;
+	}
+
+	ref_map = get_ref_map(transport, refs, ref_count, tags, &autotags);
+	if (!update_head_ok)
+		check_not_current_branch(ref_map);
+
+	for (rm = ref_map; rm; rm = rm->next) {
+		if (rm->peer_ref) {
+			peer_item = string_list_lookup(&existing_refs,
+						       rm->peer_ref->name);
+			if (peer_item)
+				hashcpy(rm->peer_ref->old_sha1,
+					peer_item->util);
+		}
+	}
+
+	if (tags == TAGS_DEFAULT && autotags)
+		transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1");
+	if (fetch_refs(transport, ref_map)) {
+		free_refs(ref_map);
+		return 1;
+	}
+	if (prune) {
+		/* If --tags was specified, pretend the user gave us the canonical tags refspec */
+		if (tags == TAGS_SET) {
+			const char *tags_str = "refs/tags/*:refs/tags/*";
+			struct refspec *tags_refspec, *refspec;
+
+			/* Copy the refspec and add the tags to it */
+			refspec = xcalloc(ref_count + 1, sizeof(struct refspec));
+			tags_refspec = parse_fetch_refspec(1, &tags_str);
+			memcpy(refspec, refs, ref_count * sizeof(struct refspec));
+			memcpy(&refspec[ref_count], tags_refspec, sizeof(struct refspec));
+			ref_count++;
+
+			prune_refs(refspec, ref_count, ref_map);
+
+			ref_count--;
+			/* The rest of the strings belong to fetch_one */
+			free_refspec(1, tags_refspec);
+			free(refspec);
+		} else if (ref_count) {
+			prune_refs(refs, ref_count, ref_map);
+		} else {
+			prune_refs(transport->remote->fetch, transport->remote->fetch_refspec_nr, ref_map);
+		}
+	}
+	free_refs(ref_map);
+
+	/* if neither --no-tags nor --tags was specified, do automated tag
+	 * following ... */
+	if (tags == TAGS_DEFAULT && autotags) {
+		struct ref **tail = &ref_map;
+		ref_map = NULL;
+		find_non_local_tags(transport, &ref_map, &tail);
+		if (ref_map) {
+			transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
+			transport_set_option(transport, TRANS_OPT_DEPTH, "0");
+			fetch_refs(transport, ref_map);
+		}
+		free_refs(ref_map);
+	}
+
+	return 0;
+}
+
+static void set_option(const char *name, const char *value)
+{
+	int r = transport_set_option(transport, name, value);
+	if (r < 0)
+		die(_("Option \"%s\" value \"%s\" is not valid for %s"),
+			name, value, transport->url);
+	if (r > 0)
+		warning(_("Option \"%s\" is ignored for %s\n"),
+			name, transport->url);
+}
+
+static int get_one_remote_for_fetch(struct remote *remote, void *priv)
+{
+	struct string_list *list = priv;
+	if (!remote->skip_default_update)
+		string_list_append(list, remote->name);
+	return 0;
+}
+
+struct remote_group_data {
+	const char *name;
+	struct string_list *list;
+};
+
+static int get_remote_group(const char *key, const char *value, void *priv)
+{
+	struct remote_group_data *g = priv;
+
+	if (!prefixcmp(key, "remotes.") &&
+			!strcmp(key + 8, g->name)) {
+		/* split list by white space */
+		int space = strcspn(value, " \t\n");
+		while (*value) {
+			if (space > 1) {
+				string_list_append(g->list,
+						   xstrndup(value, space));
+			}
+			value += space + (value[space] != '\0');
+			space = strcspn(value, " \t\n");
+		}
+	}
+
+	return 0;
+}
+
+static int add_remote_or_group(const char *name, struct string_list *list)
+{
+	int prev_nr = list->nr;
+	struct remote_group_data g;
+	g.name = name; g.list = list;
+
+	git_config(get_remote_group, &g);
+	if (list->nr == prev_nr) {
+		struct remote *remote;
+		if (!remote_is_configured(name))
+			return 0;
+		remote = remote_get(name);
+		string_list_append(list, remote->name);
+	}
+	return 1;
+}
+
+static void add_options_to_argv(int *argc, const char **argv)
+{
+	if (dry_run)
+		argv[(*argc)++] = "--dry-run";
+	if (prune)
+		argv[(*argc)++] = "--prune";
+	if (update_head_ok)
+		argv[(*argc)++] = "--update-head-ok";
+	if (force)
+		argv[(*argc)++] = "--force";
+	if (keep)
+		argv[(*argc)++] = "--keep";
+	if (recurse_submodules == RECURSE_SUBMODULES_ON)
+		argv[(*argc)++] = "--recurse-submodules";
+	else if (recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND)
+		argv[(*argc)++] = "--recurse-submodules=on-demand";
+	if (verbosity >= 2)
+		argv[(*argc)++] = "-v";
+	if (verbosity >= 1)
+		argv[(*argc)++] = "-v";
+	else if (verbosity < 0)
+		argv[(*argc)++] = "-q";
+
+}
+
+static int fetch_multiple(struct string_list *list)
+{
+	int i, result = 0;
+	const char *argv[12] = { "fetch", "--append" };
+	int argc = 2;
+
+	add_options_to_argv(&argc, argv);
+
+	if (!append && !dry_run) {
+		int errcode = truncate_fetch_head();
+		if (errcode)
+			return errcode;
+	}
+
+	for (i = 0; i < list->nr; i++) {
+		const char *name = list->items[i].string;
+		argv[argc] = name;
+		argv[argc + 1] = NULL;
+		if (verbosity >= 0)
+			printf(_("Fetching %s\n"), name);
+		if (run_command_v_opt(argv, RUN_GIT_CMD)) {
+			error(_("Could not fetch %s"), name);
+			result = 1;
+		}
+	}
+
+	return result;
+}
+
+static int fetch_one(struct remote *remote, int argc, const char **argv)
+{
+	int i;
+	static const char **refs = NULL;
+	struct refspec *refspec;
+	int ref_nr = 0;
+	int exit_code;
+
+	if (!remote)
+		die(_("No remote repository specified.  Please, specify either a URL or a\n"
+		    "remote name from which new revisions should be fetched."));
+
+	transport = transport_get(remote, NULL);
+	transport_set_verbosity(transport, verbosity, progress);
+	if (upload_pack)
+		set_option(TRANS_OPT_UPLOADPACK, upload_pack);
+	if (keep)
+		set_option(TRANS_OPT_KEEP, "yes");
+	if (depth)
+		set_option(TRANS_OPT_DEPTH, depth);
+
+	if (argc > 0) {
+		int j = 0;
+		refs = xcalloc(argc + 1, sizeof(const char *));
+		for (i = 0; i < argc; i++) {
+			if (!strcmp(argv[i], "tag")) {
+				char *ref;
+				i++;
+				if (i >= argc)
+					die(_("You need to specify a tag name."));
+				ref = xmalloc(strlen(argv[i]) * 2 + 22);
+				strcpy(ref, "refs/tags/");
+				strcat(ref, argv[i]);
+				strcat(ref, ":refs/tags/");
+				strcat(ref, argv[i]);
+				refs[j++] = ref;
+			} else
+				refs[j++] = argv[i];
+		}
+		refs[j] = NULL;
+		ref_nr = j;
+	}
+
+	sigchain_push_common(unlock_pack_on_signal);
+	atexit(unlock_pack);
+	refspec = parse_fetch_refspec(ref_nr, refs);
+	exit_code = do_fetch(transport, refspec, ref_nr);
+	free_refspec(ref_nr, refspec);
+	transport_disconnect(transport);
+	transport = NULL;
+	return exit_code;
+}
+
+int cmd_fetch(int argc, const char **argv, const char *prefix)
+{
+	int i;
+	struct string_list list = STRING_LIST_INIT_NODUP;
+	struct remote *remote;
+	int result = 0;
+
+	packet_trace_identity("fetch");
+
+	/* Record the command line for the reflog */
+	strbuf_addstr(&default_rla, "fetch");
+	for (i = 1; i < argc; i++)
+		strbuf_addf(&default_rla, " %s", argv[i]);
+
+	argc = parse_options(argc, argv, prefix,
+			     builtin_fetch_options, builtin_fetch_usage, 0);
+
+	if (recurse_submodules != RECURSE_SUBMODULES_OFF) {
+		if (recurse_submodules_default) {
+			int arg = parse_fetch_recurse_submodules_arg("--recurse-submodules-default", recurse_submodules_default);
+			set_config_fetch_recurse_submodules(arg);
+		}
+		gitmodules_config();
+		git_config(submodule_config, NULL);
+	}
+
+	if (all) {
+		if (argc == 1)
+			die(_("fetch --all does not take a repository argument"));
+		else if (argc > 1)
+			die(_("fetch --all does not make sense with refspecs"));
+		(void) for_each_remote(get_one_remote_for_fetch, &list);
+		result = fetch_multiple(&list);
+	} else if (argc == 0) {
+		/* No arguments -- use default remote */
+		remote = remote_get(NULL);
+		result = fetch_one(remote, argc, argv);
+	} else if (multiple) {
+		/* All arguments are assumed to be remotes or groups */
+		for (i = 0; i < argc; i++)
+			if (!add_remote_or_group(argv[i], &list))
+				die(_("No such remote or remote group: %s"), argv[i]);
+		result = fetch_multiple(&list);
+	} else {
+		/* Single remote or group */
+		(void) add_remote_or_group(argv[0], &list);
+		if (list.nr > 1) {
+			/* More than one remote */
+			if (argc > 1)
+				die(_("Fetching a group and specifying refspecs does not make sense"));
+			result = fetch_multiple(&list);
+		} else {
+			/* Zero or one remotes */
+			remote = remote_get(argv[0]);
+			result = fetch_one(remote, argc-1, argv+1);
+		}
+	}
+
+	if (!result && (recurse_submodules != RECURSE_SUBMODULES_OFF)) {
+		const char *options[10];
+		int num_options = 0;
+		add_options_to_argv(&num_options, options);
+		result = fetch_populated_submodules(num_options, options,
+						    submodule_prefix,
+						    recurse_submodules,
+						    verbosity < 0);
+	}
+
+	/* All names were strdup()ed or strndup()ed */
+	list.strdup_strings = 1;
+	string_list_clear(&list, 0);
+
+	return result;
+}
diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c
new file mode 100644
index 0000000..2c4d435
--- /dev/null
+++ b/builtin/fmt-merge-msg.c
@@ -0,0 +1,700 @@
+#include "builtin.h"
+#include "cache.h"
+#include "commit.h"
+#include "diff.h"
+#include "revision.h"
+#include "tag.h"
+#include "string-list.h"
+#include "branch.h"
+#include "fmt-merge-msg.h"
+#include "gpg-interface.h"
+
+static const char * const fmt_merge_msg_usage[] = {
+	"git fmt-merge-msg [-m <message>] [--log[=<n>]|--no-log] [--file <file>]",
+	NULL
+};
+
+static int use_branch_desc;
+
+int fmt_merge_msg_config(const char *key, const char *value, void *cb)
+{
+	if (!strcmp(key, "merge.log") || !strcmp(key, "merge.summary")) {
+		int is_bool;
+		merge_log_config = git_config_bool_or_int(key, value, &is_bool);
+		if (!is_bool && merge_log_config < 0)
+			return error("%s: negative length %s", key, value);
+		if (is_bool && merge_log_config)
+			merge_log_config = DEFAULT_MERGE_LOG_LEN;
+	} else if (!strcmp(key, "merge.branchdesc")) {
+		use_branch_desc = git_config_bool(key, value);
+	} else {
+		return git_default_config(key, value, cb);
+	}
+	return 0;
+}
+
+/* merge data per repository where the merged tips came from */
+struct src_data {
+	struct string_list branch, tag, r_branch, generic;
+	int head_status;
+};
+
+struct origin_data {
+	unsigned char sha1[20];
+	unsigned is_local_branch:1;
+};
+
+static void init_src_data(struct src_data *data)
+{
+	data->branch.strdup_strings = 1;
+	data->tag.strdup_strings = 1;
+	data->r_branch.strdup_strings = 1;
+	data->generic.strdup_strings = 1;
+}
+
+static struct string_list srcs = STRING_LIST_INIT_DUP;
+static struct string_list origins = STRING_LIST_INIT_DUP;
+
+struct merge_parents {
+	int alloc, nr;
+	struct merge_parent {
+		unsigned char given[20];
+		unsigned char commit[20];
+		unsigned char used;
+	} *item;
+};
+
+/*
+ * I know, I know, this is inefficient, but you won't be pulling and merging
+ * hundreds of heads at a time anyway.
+ */
+static struct merge_parent *find_merge_parent(struct merge_parents *table,
+					      unsigned char *given,
+					      unsigned char *commit)
+{
+	int i;
+	for (i = 0; i < table->nr; i++) {
+		if (given && hashcmp(table->item[i].given, given))
+			continue;
+		if (commit && hashcmp(table->item[i].commit, commit))
+			continue;
+		return &table->item[i];
+	}
+	return NULL;
+}
+
+static void add_merge_parent(struct merge_parents *table,
+			     unsigned char *given,
+			     unsigned char *commit)
+{
+	if (table->nr && find_merge_parent(table, given, commit))
+		return;
+	ALLOC_GROW(table->item, table->nr + 1, table->alloc);
+	hashcpy(table->item[table->nr].given, given);
+	hashcpy(table->item[table->nr].commit, commit);
+	table->item[table->nr].used = 0;
+	table->nr++;
+}
+
+static int handle_line(char *line, struct merge_parents *merge_parents)
+{
+	int i, len = strlen(line);
+	struct origin_data *origin_data;
+	char *src, *origin;
+	struct src_data *src_data;
+	struct string_list_item *item;
+	int pulling_head = 0;
+	unsigned char sha1[20];
+
+	if (len < 43 || line[40] != '\t')
+		return 1;
+
+	if (!prefixcmp(line + 41, "not-for-merge"))
+		return 0;
+
+	if (line[41] != '\t')
+		return 2;
+
+	i = get_sha1_hex(line, sha1);
+	if (i)
+		return 3;
+
+	if (!find_merge_parent(merge_parents, sha1, NULL))
+		return 0; /* subsumed by other parents */
+
+	origin_data = xcalloc(1, sizeof(struct origin_data));
+	hashcpy(origin_data->sha1, sha1);
+
+	if (line[len - 1] == '\n')
+		line[len - 1] = 0;
+	line += 42;
+
+	/*
+	 * At this point, line points at the beginning of comment e.g.
+	 * "branch 'frotz' of git://that/repository.git".
+	 * Find the repository name and point it with src.
+	 */
+	src = strstr(line, " of ");
+	if (src) {
+		*src = 0;
+		src += 4;
+		pulling_head = 0;
+	} else {
+		src = line;
+		pulling_head = 1;
+	}
+
+	item = unsorted_string_list_lookup(&srcs, src);
+	if (!item) {
+		item = string_list_append(&srcs, src);
+		item->util = xcalloc(1, sizeof(struct src_data));
+		init_src_data(item->util);
+	}
+	src_data = item->util;
+
+	if (pulling_head) {
+		origin = src;
+		src_data->head_status |= 1;
+	} else if (!prefixcmp(line, "branch ")) {
+		origin_data->is_local_branch = 1;
+		origin = line + 7;
+		string_list_append(&src_data->branch, origin);
+		src_data->head_status |= 2;
+	} else if (!prefixcmp(line, "tag ")) {
+		origin = line;
+		string_list_append(&src_data->tag, origin + 4);
+		src_data->head_status |= 2;
+	} else if (!prefixcmp(line, "remote-tracking branch ")) {
+		origin = line + strlen("remote-tracking branch ");
+		string_list_append(&src_data->r_branch, origin);
+		src_data->head_status |= 2;
+	} else {
+		origin = src;
+		string_list_append(&src_data->generic, line);
+		src_data->head_status |= 2;
+	}
+
+	if (!strcmp(".", src) || !strcmp(src, origin)) {
+		int len = strlen(origin);
+		if (origin[0] == '\'' && origin[len - 1] == '\'')
+			origin = xmemdupz(origin + 1, len - 2);
+	} else {
+		char *new_origin = xmalloc(strlen(origin) + strlen(src) + 5);
+		sprintf(new_origin, "%s of %s", origin, src);
+		origin = new_origin;
+	}
+	if (strcmp(".", src))
+		origin_data->is_local_branch = 0;
+	string_list_append(&origins, origin)->util = origin_data;
+	return 0;
+}
+
+static void print_joined(const char *singular, const char *plural,
+		struct string_list *list, struct strbuf *out)
+{
+	if (list->nr == 0)
+		return;
+	if (list->nr == 1) {
+		strbuf_addf(out, "%s%s", singular, list->items[0].string);
+	} else {
+		int i;
+		strbuf_addstr(out, plural);
+		for (i = 0; i < list->nr - 1; i++)
+			strbuf_addf(out, "%s%s", i > 0 ? ", " : "",
+				    list->items[i].string);
+		strbuf_addf(out, " and %s", list->items[list->nr - 1].string);
+	}
+}
+
+static void add_branch_desc(struct strbuf *out, const char *name)
+{
+	struct strbuf desc = STRBUF_INIT;
+
+	if (!read_branch_desc(&desc, name)) {
+		const char *bp = desc.buf;
+		while (*bp) {
+			const char *ep = strchrnul(bp, '\n');
+			if (*ep)
+				ep++;
+			strbuf_addf(out, "  : %.*s", (int)(ep - bp), bp);
+			bp = ep;
+		}
+		if (out->buf[out->len - 1] != '\n')
+			strbuf_addch(out, '\n');
+	}
+	strbuf_release(&desc);
+}
+
+#define util_as_integral(elem) ((intptr_t)((elem)->util))
+
+static void record_person(int which, struct string_list *people,
+			  struct commit *commit)
+{
+	char *name_buf, *name, *name_end;
+	struct string_list_item *elem;
+	const char *field = (which == 'a') ? "\nauthor " : "\ncommitter ";
+
+	name = strstr(commit->buffer, field);
+	if (!name)
+		return;
+	name += strlen(field);
+	name_end = strchrnul(name, '<');
+	if (*name_end)
+		name_end--;
+	while (isspace(*name_end) && name <= name_end)
+		name_end--;
+	if (name_end < name)
+		return;
+	name_buf = xmemdupz(name, name_end - name + 1);
+
+	elem = string_list_lookup(people, name_buf);
+	if (!elem) {
+		elem = string_list_insert(people, name_buf);
+		elem->util = (void *)0;
+	}
+	elem->util = (void*)(util_as_integral(elem) + 1);
+	free(name_buf);
+}
+
+static int cmp_string_list_util_as_integral(const void *a_, const void *b_)
+{
+	const struct string_list_item *a = a_, *b = b_;
+	return util_as_integral(b) - util_as_integral(a);
+}
+
+static void add_people_count(struct strbuf *out, struct string_list *people)
+{
+	if (people->nr == 1)
+		strbuf_addf(out, "%s", people->items[0].string);
+	else if (people->nr == 2)
+		strbuf_addf(out, "%s (%d) and %s (%d)",
+			    people->items[0].string,
+			    (int)util_as_integral(&people->items[0]),
+			    people->items[1].string,
+			    (int)util_as_integral(&people->items[1]));
+	else if (people->nr)
+		strbuf_addf(out, "%s (%d) and others",
+			    people->items[0].string,
+			    (int)util_as_integral(&people->items[0]));
+}
+
+static void credit_people(struct strbuf *out,
+			  struct string_list *them,
+			  int kind)
+{
+	const char *label;
+	const char *me;
+
+	if (kind == 'a') {
+		label = "\n# By ";
+		me = git_author_info(IDENT_NO_DATE);
+	} else {
+		label = "\n# Via ";
+		me = git_committer_info(IDENT_NO_DATE);
+	}
+
+	if (!them->nr ||
+	    (them->nr == 1 &&
+	     me &&
+	     (me = skip_prefix(me, them->items->string)) != NULL &&
+	     skip_prefix(me, " <")))
+		return;
+	strbuf_addstr(out, label);
+	add_people_count(out, them);
+}
+
+static void add_people_info(struct strbuf *out,
+			    struct string_list *authors,
+			    struct string_list *committers)
+{
+	if (authors->nr)
+		qsort(authors->items,
+		      authors->nr, sizeof(authors->items[0]),
+		      cmp_string_list_util_as_integral);
+	if (committers->nr)
+		qsort(committers->items,
+		      committers->nr, sizeof(committers->items[0]),
+		      cmp_string_list_util_as_integral);
+
+	credit_people(out, authors, 'a');
+	credit_people(out, committers, 'c');
+}
+
+static void shortlog(const char *name,
+		     struct origin_data *origin_data,
+		     struct commit *head,
+		     struct rev_info *rev, int limit,
+		     struct strbuf *out)
+{
+	int i, count = 0;
+	struct commit *commit;
+	struct object *branch;
+	struct string_list subjects = STRING_LIST_INIT_DUP;
+	struct string_list authors = STRING_LIST_INIT_DUP;
+	struct string_list committers = STRING_LIST_INIT_DUP;
+	int flags = UNINTERESTING | TREESAME | SEEN | SHOWN | ADDED;
+	struct strbuf sb = STRBUF_INIT;
+	const unsigned char *sha1 = origin_data->sha1;
+
+	branch = deref_tag(parse_object(sha1), sha1_to_hex(sha1), 40);
+	if (!branch || branch->type != OBJ_COMMIT)
+		return;
+
+	setup_revisions(0, NULL, rev, NULL);
+	add_pending_object(rev, branch, name);
+	add_pending_object(rev, &head->object, "^HEAD");
+	head->object.flags |= UNINTERESTING;
+	if (prepare_revision_walk(rev))
+		die("revision walk setup failed");
+	while ((commit = get_revision(rev)) != NULL) {
+		struct pretty_print_context ctx = {0};
+
+		if (commit->parents && commit->parents->next) {
+			/* do not list a merge but count committer */
+			record_person('c', &committers, commit);
+			continue;
+		}
+		if (!count)
+			/* the 'tip' committer */
+			record_person('c', &committers, commit);
+		record_person('a', &authors, commit);
+		count++;
+		if (subjects.nr > limit)
+			continue;
+
+		format_commit_message(commit, "%s", &sb, &ctx);
+		strbuf_ltrim(&sb);
+
+		if (!sb.len)
+			string_list_append(&subjects,
+					   sha1_to_hex(commit->object.sha1));
+		else
+			string_list_append(&subjects, strbuf_detach(&sb, NULL));
+	}
+
+	add_people_info(out, &authors, &committers);
+	if (count > limit)
+		strbuf_addf(out, "\n* %s: (%d commits)\n", name, count);
+	else
+		strbuf_addf(out, "\n* %s:\n", name);
+
+	if (origin_data->is_local_branch && use_branch_desc)
+		add_branch_desc(out, name);
+
+	for (i = 0; i < subjects.nr; i++)
+		if (i >= limit)
+			strbuf_addf(out, "  ...\n");
+		else
+			strbuf_addf(out, "  %s\n", subjects.items[i].string);
+
+	clear_commit_marks((struct commit *)branch, flags);
+	clear_commit_marks(head, flags);
+	free_commit_list(rev->commits);
+	rev->commits = NULL;
+	rev->pending.nr = 0;
+
+	string_list_clear(&authors, 0);
+	string_list_clear(&committers, 0);
+	string_list_clear(&subjects, 0);
+}
+
+static void fmt_merge_msg_title(struct strbuf *out,
+	const char *current_branch) {
+	int i = 0;
+	char *sep = "";
+
+	strbuf_addstr(out, "Merge ");
+	for (i = 0; i < srcs.nr; i++) {
+		struct src_data *src_data = srcs.items[i].util;
+		const char *subsep = "";
+
+		strbuf_addstr(out, sep);
+		sep = "; ";
+
+		if (src_data->head_status == 1) {
+			strbuf_addstr(out, srcs.items[i].string);
+			continue;
+		}
+		if (src_data->head_status == 3) {
+			subsep = ", ";
+			strbuf_addstr(out, "HEAD");
+		}
+		if (src_data->branch.nr) {
+			strbuf_addstr(out, subsep);
+			subsep = ", ";
+			print_joined("branch ", "branches ", &src_data->branch,
+					out);
+		}
+		if (src_data->r_branch.nr) {
+			strbuf_addstr(out, subsep);
+			subsep = ", ";
+			print_joined("remote-tracking branch ", "remote-tracking branches ",
+					&src_data->r_branch, out);
+		}
+		if (src_data->tag.nr) {
+			strbuf_addstr(out, subsep);
+			subsep = ", ";
+			print_joined("tag ", "tags ", &src_data->tag, out);
+		}
+		if (src_data->generic.nr) {
+			strbuf_addstr(out, subsep);
+			print_joined("commit ", "commits ", &src_data->generic,
+					out);
+		}
+		if (strcmp(".", srcs.items[i].string))
+			strbuf_addf(out, " of %s", srcs.items[i].string);
+	}
+
+	if (!strcmp("master", current_branch))
+		strbuf_addch(out, '\n');
+	else
+		strbuf_addf(out, " into %s\n", current_branch);
+}
+
+static void fmt_tag_signature(struct strbuf *tagbuf,
+			      struct strbuf *sig,
+			      const char *buf,
+			      unsigned long len)
+{
+	const char *tag_body = strstr(buf, "\n\n");
+	if (tag_body) {
+		tag_body += 2;
+		strbuf_add(tagbuf, tag_body, buf + len - tag_body);
+	}
+	strbuf_complete_line(tagbuf);
+	if (sig->len) {
+		strbuf_addch(tagbuf, '\n');
+		strbuf_add_lines(tagbuf, "# ", sig->buf, sig->len);
+	}
+}
+
+static void fmt_merge_msg_sigs(struct strbuf *out)
+{
+	int i, tag_number = 0, first_tag = 0;
+	struct strbuf tagbuf = STRBUF_INIT;
+
+	for (i = 0; i < origins.nr; i++) {
+		unsigned char *sha1 = origins.items[i].util;
+		enum object_type type;
+		unsigned long size, len;
+		char *buf = read_sha1_file(sha1, &type, &size);
+		struct strbuf sig = STRBUF_INIT;
+
+		if (!buf || type != OBJ_TAG)
+			goto next;
+		len = parse_signature(buf, size);
+
+		if (size == len)
+			; /* merely annotated */
+		else if (verify_signed_buffer(buf, len, buf + len, size - len, &sig)) {
+			if (!sig.len)
+				strbuf_addstr(&sig, "gpg verification failed.\n");
+		}
+
+		if (!tag_number++) {
+			fmt_tag_signature(&tagbuf, &sig, buf, len);
+			first_tag = i;
+		} else {
+			if (tag_number == 2) {
+				struct strbuf tagline = STRBUF_INIT;
+				strbuf_addf(&tagline, "\n# %s\n",
+					    origins.items[first_tag].string);
+				strbuf_insert(&tagbuf, 0, tagline.buf,
+					      tagline.len);
+				strbuf_release(&tagline);
+			}
+			strbuf_addf(&tagbuf, "\n# %s\n",
+				    origins.items[i].string);
+			fmt_tag_signature(&tagbuf, &sig, buf, len);
+		}
+		strbuf_release(&sig);
+	next:
+		free(buf);
+	}
+	if (tagbuf.len) {
+		strbuf_addch(out, '\n');
+		strbuf_addbuf(out, &tagbuf);
+	}
+	strbuf_release(&tagbuf);
+}
+
+static void find_merge_parents(struct merge_parents *result,
+			       struct strbuf *in, unsigned char *head)
+{
+	struct commit_list *parents, *next;
+	struct commit *head_commit;
+	int pos = 0, i, j;
+
+	parents = NULL;
+	while (pos < in->len) {
+		int len;
+		char *p = in->buf + pos;
+		char *newline = strchr(p, '\n');
+		unsigned char sha1[20];
+		struct commit *parent;
+		struct object *obj;
+
+		len = newline ? newline - p : strlen(p);
+		pos += len + !!newline;
+
+		if (len < 43 ||
+		    get_sha1_hex(p, sha1) ||
+		    p[40] != '\t' ||
+		    p[41] != '\t')
+			continue; /* skip not-for-merge */
+		/*
+		 * Do not use get_merge_parent() here; we do not have
+		 * "name" here and we do not want to contaminate its
+		 * util field yet.
+		 */
+		obj = parse_object(sha1);
+		parent = (struct commit *)peel_to_type(NULL, 0, obj, OBJ_COMMIT);
+		if (!parent)
+			continue;
+		commit_list_insert(parent, &parents);
+		add_merge_parent(result, obj->sha1, parent->object.sha1);
+	}
+	head_commit = lookup_commit(head);
+	if (head_commit)
+		commit_list_insert(head_commit, &parents);
+	parents = reduce_heads(parents);
+
+	while (parents) {
+		for (i = 0; i < result->nr; i++)
+			if (!hashcmp(result->item[i].commit,
+				     parents->item->object.sha1))
+				result->item[i].used = 1;
+		next = parents->next;
+		free(parents);
+		parents = next;
+	}
+
+	for (i = j = 0; i < result->nr; i++) {
+		if (result->item[i].used) {
+			if (i != j)
+				result->item[j] = result->item[i];
+			j++;
+		}
+	}
+	result->nr = j;
+}
+
+int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
+		  struct fmt_merge_msg_opts *opts)
+{
+	int i = 0, pos = 0;
+	unsigned char head_sha1[20];
+	const char *current_branch;
+	void *current_branch_to_free;
+	struct merge_parents merge_parents;
+
+	memset(&merge_parents, 0, sizeof(merge_parents));
+
+	/* get current branch */
+	current_branch = current_branch_to_free =
+		resolve_refdup("HEAD", head_sha1, 1, NULL);
+	if (!current_branch)
+		die("No current branch");
+	if (!prefixcmp(current_branch, "refs/heads/"))
+		current_branch += 11;
+
+	find_merge_parents(&merge_parents, in, head_sha1);
+
+	/* get a line */
+	while (pos < in->len) {
+		int len;
+		char *newline, *p = in->buf + pos;
+
+		newline = strchr(p, '\n');
+		len = newline ? newline - p : strlen(p);
+		pos += len + !!newline;
+		i++;
+		p[len] = 0;
+		if (handle_line(p, &merge_parents))
+			die ("Error in line %d: %.*s", i, len, p);
+	}
+
+	if (opts->add_title && srcs.nr)
+		fmt_merge_msg_title(out, current_branch);
+
+	if (origins.nr)
+		fmt_merge_msg_sigs(out);
+
+	if (opts->shortlog_len) {
+		struct commit *head;
+		struct rev_info rev;
+
+		head = lookup_commit_or_die(head_sha1, "HEAD");
+		init_revisions(&rev, NULL);
+		rev.commit_format = CMIT_FMT_ONELINE;
+		rev.ignore_merges = 1;
+		rev.limited = 1;
+
+		strbuf_complete_line(out);
+
+		for (i = 0; i < origins.nr; i++)
+			shortlog(origins.items[i].string,
+				 origins.items[i].util,
+				 head, &rev, opts->shortlog_len, out);
+	}
+
+	strbuf_complete_line(out);
+	free(current_branch_to_free);
+	free(merge_parents.item);
+	return 0;
+}
+
+int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
+{
+	const char *inpath = NULL;
+	const char *message = NULL;
+	int shortlog_len = -1;
+	struct option options[] = {
+		{ OPTION_INTEGER, 0, "log", &shortlog_len, "n",
+		  "populate log with at most <n> entries from shortlog",
+		  PARSE_OPT_OPTARG, NULL, DEFAULT_MERGE_LOG_LEN },
+		{ OPTION_INTEGER, 0, "summary", &shortlog_len, "n",
+		  "alias for --log (deprecated)",
+		  PARSE_OPT_OPTARG | PARSE_OPT_HIDDEN, NULL,
+		  DEFAULT_MERGE_LOG_LEN },
+		OPT_STRING('m', "message", &message, "text",
+			"use <text> as start of message"),
+		OPT_FILENAME('F', "file", &inpath, "file to read from"),
+		OPT_END()
+	};
+
+	FILE *in = stdin;
+	struct strbuf input = STRBUF_INIT, output = STRBUF_INIT;
+	int ret;
+	struct fmt_merge_msg_opts opts;
+
+	git_config(fmt_merge_msg_config, NULL);
+	argc = parse_options(argc, argv, prefix, options, fmt_merge_msg_usage,
+			     0);
+	if (argc > 0)
+		usage_with_options(fmt_merge_msg_usage, options);
+	if (shortlog_len < 0)
+		shortlog_len = (merge_log_config > 0) ? merge_log_config : 0;
+
+	if (inpath && strcmp(inpath, "-")) {
+		in = fopen(inpath, "r");
+		if (!in)
+			die_errno("cannot open '%s'", inpath);
+	}
+
+	if (strbuf_read(&input, fileno(in), 0) < 0)
+		die_errno("could not read input file");
+
+	if (message)
+		strbuf_addstr(&output, message);
+
+	memset(&opts, 0, sizeof(opts));
+	opts.add_title = !message;
+	opts.shortlog_len = shortlog_len;
+
+	ret = fmt_merge_msg(&input, &output, &opts);
+	if (ret)
+		return ret;
+	write_in_full(STDOUT_FILENO, output.buf, output.len);
+	return 0;
+}
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
new file mode 100644
index 0000000..b01d76a
--- /dev/null
+++ b/builtin/for-each-ref.c
@@ -0,0 +1,1040 @@
+#include "builtin.h"
+#include "cache.h"
+#include "refs.h"
+#include "object.h"
+#include "tag.h"
+#include "commit.h"
+#include "tree.h"
+#include "blob.h"
+#include "quote.h"
+#include "parse-options.h"
+#include "remote.h"
+
+/* Quoting styles */
+#define QUOTE_NONE 0
+#define QUOTE_SHELL 1
+#define QUOTE_PERL 2
+#define QUOTE_PYTHON 4
+#define QUOTE_TCL 8
+
+typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;
+
+struct atom_value {
+	const char *s;
+	unsigned long ul; /* used for sorting when not FIELD_STR */
+};
+
+struct ref_sort {
+	struct ref_sort *next;
+	int atom; /* index into used_atom array */
+	unsigned reverse : 1;
+};
+
+struct refinfo {
+	char *refname;
+	unsigned char objectname[20];
+	int flag;
+	const char *symref;
+	struct atom_value *value;
+};
+
+static struct {
+	const char *name;
+	cmp_type cmp_type;
+} valid_atom[] = {
+	{ "refname" },
+	{ "objecttype" },
+	{ "objectsize", FIELD_ULONG },
+	{ "objectname" },
+	{ "tree" },
+	{ "parent" },
+	{ "numparent", FIELD_ULONG },
+	{ "object" },
+	{ "type" },
+	{ "tag" },
+	{ "author" },
+	{ "authorname" },
+	{ "authoremail" },
+	{ "authordate", FIELD_TIME },
+	{ "committer" },
+	{ "committername" },
+	{ "committeremail" },
+	{ "committerdate", FIELD_TIME },
+	{ "tagger" },
+	{ "taggername" },
+	{ "taggeremail" },
+	{ "taggerdate", FIELD_TIME },
+	{ "creator" },
+	{ "creatordate", FIELD_TIME },
+	{ "subject" },
+	{ "body" },
+	{ "contents" },
+	{ "contents:subject" },
+	{ "contents:body" },
+	{ "contents:signature" },
+	{ "upstream" },
+	{ "symref" },
+	{ "flag" },
+};
+
+/*
+ * An atom is a valid field atom listed above, possibly prefixed with
+ * a "*" to denote deref_tag().
+ *
+ * We parse given format string and sort specifiers, and make a list
+ * of properties that we need to extract out of objects.  refinfo
+ * structure will hold an array of values extracted that can be
+ * indexed with the "atom number", which is an index into this
+ * array.
+ */
+static const char **used_atom;
+static cmp_type *used_atom_type;
+static int used_atom_cnt, sort_atom_limit, need_tagged, need_symref;
+
+/*
+ * Used to parse format string and sort specifiers
+ */
+static int parse_atom(const char *atom, const char *ep)
+{
+	const char *sp;
+	int i, at;
+
+	sp = atom;
+	if (*sp == '*' && sp < ep)
+		sp++; /* deref */
+	if (ep <= sp)
+		die("malformed field name: %.*s", (int)(ep-atom), atom);
+
+	/* Do we have the atom already used elsewhere? */
+	for (i = 0; i < used_atom_cnt; i++) {
+		int len = strlen(used_atom[i]);
+		if (len == ep - atom && !memcmp(used_atom[i], atom, len))
+			return i;
+	}
+
+	/* Is the atom a valid one? */
+	for (i = 0; i < ARRAY_SIZE(valid_atom); i++) {
+		int len = strlen(valid_atom[i].name);
+		/*
+		 * If the atom name has a colon, strip it and everything after
+		 * it off - it specifies the format for this entry, and
+		 * shouldn't be used for checking against the valid_atom
+		 * table.
+		 */
+		const char *formatp = strchr(sp, ':');
+		if (!formatp || ep < formatp)
+			formatp = ep;
+		if (len == formatp - sp && !memcmp(valid_atom[i].name, sp, len))
+			break;
+	}
+
+	if (ARRAY_SIZE(valid_atom) <= i)
+		die("unknown field name: %.*s", (int)(ep-atom), atom);
+
+	/* Add it in, including the deref prefix */
+	at = used_atom_cnt;
+	used_atom_cnt++;
+	used_atom = xrealloc(used_atom,
+			     (sizeof *used_atom) * used_atom_cnt);
+	used_atom_type = xrealloc(used_atom_type,
+				  (sizeof(*used_atom_type) * used_atom_cnt));
+	used_atom[at] = xmemdupz(atom, ep - atom);
+	used_atom_type[at] = valid_atom[i].cmp_type;
+	if (*atom == '*')
+		need_tagged = 1;
+	if (!strcmp(used_atom[at], "symref"))
+		need_symref = 1;
+	return at;
+}
+
+/*
+ * In a format string, find the next occurrence of %(atom).
+ */
+static const char *find_next(const char *cp)
+{
+	while (*cp) {
+		if (*cp == '%') {
+			/*
+			 * %( is the start of an atom;
+			 * %% is a quoted per-cent.
+			 */
+			if (cp[1] == '(')
+				return cp;
+			else if (cp[1] == '%')
+				cp++; /* skip over two % */
+			/* otherwise this is a singleton, literal % */
+		}
+		cp++;
+	}
+	return NULL;
+}
+
+/*
+ * Make sure the format string is well formed, and parse out
+ * the used atoms.
+ */
+static int verify_format(const char *format)
+{
+	const char *cp, *sp;
+	for (cp = format; *cp && (sp = find_next(cp)); ) {
+		const char *ep = strchr(sp, ')');
+		if (!ep)
+			return error("malformed format string %s", sp);
+		/* sp points at "%(" and ep points at the closing ")" */
+		parse_atom(sp + 2, ep);
+		cp = ep + 1;
+	}
+	return 0;
+}
+
+/*
+ * Given an object name, read the object data and size, and return a
+ * "struct object".  If the object data we are returning is also borrowed
+ * by the "struct object" representation, set *eaten as well---it is a
+ * signal from parse_object_buffer to us not to free the buffer.
+ */
+static void *get_obj(const unsigned char *sha1, struct object **obj, unsigned long *sz, int *eaten)
+{
+	enum object_type type;
+	void *buf = read_sha1_file(sha1, &type, sz);
+
+	if (buf)
+		*obj = parse_object_buffer(sha1, type, *sz, buf, eaten);
+	else
+		*obj = NULL;
+	return buf;
+}
+
+/* See grab_values */
+static void grab_common_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
+{
+	int i;
+
+	for (i = 0; i < used_atom_cnt; i++) {
+		const char *name = used_atom[i];
+		struct atom_value *v = &val[i];
+		if (!!deref != (*name == '*'))
+			continue;
+		if (deref)
+			name++;
+		if (!strcmp(name, "objecttype"))
+			v->s = typename(obj->type);
+		else if (!strcmp(name, "objectsize")) {
+			char *s = xmalloc(40);
+			sprintf(s, "%lu", sz);
+			v->ul = sz;
+			v->s = s;
+		}
+		else if (!strcmp(name, "objectname")) {
+			char *s = xmalloc(41);
+			strcpy(s, sha1_to_hex(obj->sha1));
+			v->s = s;
+		}
+		else if (!strcmp(name, "objectname:short")) {
+			v->s = xstrdup(find_unique_abbrev(obj->sha1,
+							  DEFAULT_ABBREV));
+		}
+	}
+}
+
+/* See grab_values */
+static void grab_tag_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
+{
+	int i;
+	struct tag *tag = (struct tag *) obj;
+
+	for (i = 0; i < used_atom_cnt; i++) {
+		const char *name = used_atom[i];
+		struct atom_value *v = &val[i];
+		if (!!deref != (*name == '*'))
+			continue;
+		if (deref)
+			name++;
+		if (!strcmp(name, "tag"))
+			v->s = tag->tag;
+		else if (!strcmp(name, "type") && tag->tagged)
+			v->s = typename(tag->tagged->type);
+		else if (!strcmp(name, "object") && tag->tagged) {
+			char *s = xmalloc(41);
+			strcpy(s, sha1_to_hex(tag->tagged->sha1));
+			v->s = s;
+		}
+	}
+}
+
+static int num_parents(struct commit *commit)
+{
+	struct commit_list *parents;
+	int i;
+
+	for (i = 0, parents = commit->parents;
+	     parents;
+	     parents = parents->next)
+		i++;
+	return i;
+}
+
+/* See grab_values */
+static void grab_commit_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
+{
+	int i;
+	struct commit *commit = (struct commit *) obj;
+
+	for (i = 0; i < used_atom_cnt; i++) {
+		const char *name = used_atom[i];
+		struct atom_value *v = &val[i];
+		if (!!deref != (*name == '*'))
+			continue;
+		if (deref)
+			name++;
+		if (!strcmp(name, "tree")) {
+			char *s = xmalloc(41);
+			strcpy(s, sha1_to_hex(commit->tree->object.sha1));
+			v->s = s;
+		}
+		if (!strcmp(name, "numparent")) {
+			char *s = xmalloc(40);
+			v->ul = num_parents(commit);
+			sprintf(s, "%lu", v->ul);
+			v->s = s;
+		}
+		else if (!strcmp(name, "parent")) {
+			int num = num_parents(commit);
+			int i;
+			struct commit_list *parents;
+			char *s = xmalloc(41 * num + 1);
+			v->s = s;
+			for (i = 0, parents = commit->parents;
+			     parents;
+			     parents = parents->next, i = i + 41) {
+				struct commit *parent = parents->item;
+				strcpy(s+i, sha1_to_hex(parent->object.sha1));
+				if (parents->next)
+					s[i+40] = ' ';
+			}
+			if (!i)
+				*s = '\0';
+		}
+	}
+}
+
+static const char *find_wholine(const char *who, int wholen, const char *buf, unsigned long sz)
+{
+	const char *eol;
+	while (*buf) {
+		if (!strncmp(buf, who, wholen) &&
+		    buf[wholen] == ' ')
+			return buf + wholen + 1;
+		eol = strchr(buf, '\n');
+		if (!eol)
+			return "";
+		eol++;
+		if (*eol == '\n')
+			return ""; /* end of header */
+		buf = eol;
+	}
+	return "";
+}
+
+static const char *copy_line(const char *buf)
+{
+	const char *eol = strchrnul(buf, '\n');
+	return xmemdupz(buf, eol - buf);
+}
+
+static const char *copy_name(const char *buf)
+{
+	const char *cp;
+	for (cp = buf; *cp && *cp != '\n'; cp++) {
+		if (!strncmp(cp, " <", 2))
+			return xmemdupz(buf, cp - buf);
+	}
+	return "";
+}
+
+static const char *copy_email(const char *buf)
+{
+	const char *email = strchr(buf, '<');
+	const char *eoemail;
+	if (!email)
+		return "";
+	eoemail = strchr(email, '>');
+	if (!eoemail)
+		return "";
+	return xmemdupz(email, eoemail + 1 - email);
+}
+
+static char *copy_subject(const char *buf, unsigned long len)
+{
+	char *r = xmemdupz(buf, len);
+	int i;
+
+	for (i = 0; i < len; i++)
+		if (r[i] == '\n')
+			r[i] = ' ';
+
+	return r;
+}
+
+static void grab_date(const char *buf, struct atom_value *v, const char *atomname)
+{
+	const char *eoemail = strstr(buf, "> ");
+	char *zone;
+	unsigned long timestamp;
+	long tz;
+	enum date_mode date_mode = DATE_NORMAL;
+	const char *formatp;
+
+	/*
+	 * We got here because atomname ends in "date" or "date<something>";
+	 * it's not possible that <something> is not ":<format>" because
+	 * parse_atom() wouldn't have allowed it, so we can assume that no
+	 * ":" means no format is specified, and use the default.
+	 */
+	formatp = strchr(atomname, ':');
+	if (formatp != NULL) {
+		formatp++;
+		date_mode = parse_date_format(formatp);
+	}
+
+	if (!eoemail)
+		goto bad;
+	timestamp = strtoul(eoemail + 2, &zone, 10);
+	if (timestamp == ULONG_MAX)
+		goto bad;
+	tz = strtol(zone, NULL, 10);
+	if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)
+		goto bad;
+	v->s = xstrdup(show_date(timestamp, tz, date_mode));
+	v->ul = timestamp;
+	return;
+ bad:
+	v->s = "";
+	v->ul = 0;
+}
+
+/* See grab_values */
+static void grab_person(const char *who, struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
+{
+	int i;
+	int wholen = strlen(who);
+	const char *wholine = NULL;
+
+	for (i = 0; i < used_atom_cnt; i++) {
+		const char *name = used_atom[i];
+		struct atom_value *v = &val[i];
+		if (!!deref != (*name == '*'))
+			continue;
+		if (deref)
+			name++;
+		if (strncmp(who, name, wholen))
+			continue;
+		if (name[wholen] != 0 &&
+		    strcmp(name + wholen, "name") &&
+		    strcmp(name + wholen, "email") &&
+		    prefixcmp(name + wholen, "date"))
+			continue;
+		if (!wholine)
+			wholine = find_wholine(who, wholen, buf, sz);
+		if (!wholine)
+			return; /* no point looking for it */
+		if (name[wholen] == 0)
+			v->s = copy_line(wholine);
+		else if (!strcmp(name + wholen, "name"))
+			v->s = copy_name(wholine);
+		else if (!strcmp(name + wholen, "email"))
+			v->s = copy_email(wholine);
+		else if (!prefixcmp(name + wholen, "date"))
+			grab_date(wholine, v, name);
+	}
+
+	/*
+	 * For a tag or a commit object, if "creator" or "creatordate" is
+	 * requested, do something special.
+	 */
+	if (strcmp(who, "tagger") && strcmp(who, "committer"))
+		return; /* "author" for commit object is not wanted */
+	if (!wholine)
+		wholine = find_wholine(who, wholen, buf, sz);
+	if (!wholine)
+		return;
+	for (i = 0; i < used_atom_cnt; i++) {
+		const char *name = used_atom[i];
+		struct atom_value *v = &val[i];
+		if (!!deref != (*name == '*'))
+			continue;
+		if (deref)
+			name++;
+
+		if (!prefixcmp(name, "creatordate"))
+			grab_date(wholine, v, name);
+		else if (!strcmp(name, "creator"))
+			v->s = copy_line(wholine);
+	}
+}
+
+static void find_subpos(const char *buf, unsigned long sz,
+			const char **sub, unsigned long *sublen,
+			const char **body, unsigned long *bodylen,
+			unsigned long *nonsiglen,
+			const char **sig, unsigned long *siglen)
+{
+	const char *eol;
+	/* skip past header until we hit empty line */
+	while (*buf && *buf != '\n') {
+		eol = strchrnul(buf, '\n');
+		if (*eol)
+			eol++;
+		buf = eol;
+	}
+	/* skip any empty lines */
+	while (*buf == '\n')
+		buf++;
+
+	/* parse signature first; we might not even have a subject line */
+	*sig = buf + parse_signature(buf, strlen(buf));
+	*siglen = strlen(*sig);
+
+	/* subject is first non-empty line */
+	*sub = buf;
+	/* subject goes to first empty line */
+	while (buf < *sig && *buf && *buf != '\n') {
+		eol = strchrnul(buf, '\n');
+		if (*eol)
+			eol++;
+		buf = eol;
+	}
+	*sublen = buf - *sub;
+	/* drop trailing newline, if present */
+	if (*sublen && (*sub)[*sublen - 1] == '\n')
+		*sublen -= 1;
+
+	/* skip any empty lines */
+	while (*buf == '\n')
+		buf++;
+	*body = buf;
+	*bodylen = strlen(buf);
+	*nonsiglen = *sig - buf;
+}
+
+/* See grab_values */
+static void grab_sub_body_contents(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
+{
+	int i;
+	const char *subpos = NULL, *bodypos = NULL, *sigpos = NULL;
+	unsigned long sublen = 0, bodylen = 0, nonsiglen = 0, siglen = 0;
+
+	for (i = 0; i < used_atom_cnt; i++) {
+		const char *name = used_atom[i];
+		struct atom_value *v = &val[i];
+		if (!!deref != (*name == '*'))
+			continue;
+		if (deref)
+			name++;
+		if (strcmp(name, "subject") &&
+		    strcmp(name, "body") &&
+		    strcmp(name, "contents") &&
+		    strcmp(name, "contents:subject") &&
+		    strcmp(name, "contents:body") &&
+		    strcmp(name, "contents:signature"))
+			continue;
+		if (!subpos)
+			find_subpos(buf, sz,
+				    &subpos, &sublen,
+				    &bodypos, &bodylen, &nonsiglen,
+				    &sigpos, &siglen);
+
+		if (!strcmp(name, "subject"))
+			v->s = copy_subject(subpos, sublen);
+		else if (!strcmp(name, "contents:subject"))
+			v->s = copy_subject(subpos, sublen);
+		else if (!strcmp(name, "body"))
+			v->s = xmemdupz(bodypos, bodylen);
+		else if (!strcmp(name, "contents:body"))
+			v->s = xmemdupz(bodypos, nonsiglen);
+		else if (!strcmp(name, "contents:signature"))
+			v->s = xmemdupz(sigpos, siglen);
+		else if (!strcmp(name, "contents"))
+			v->s = xstrdup(subpos);
+	}
+}
+
+/*
+ * We want to have empty print-string for field requests
+ * that do not apply (e.g. "authordate" for a tag object)
+ */
+static void fill_missing_values(struct atom_value *val)
+{
+	int i;
+	for (i = 0; i < used_atom_cnt; i++) {
+		struct atom_value *v = &val[i];
+		if (v->s == NULL)
+			v->s = "";
+	}
+}
+
+/*
+ * val is a list of atom_value to hold returned values.  Extract
+ * the values for atoms in used_atom array out of (obj, buf, sz).
+ * when deref is false, (obj, buf, sz) is the object that is
+ * pointed at by the ref itself; otherwise it is the object the
+ * ref (which is a tag) refers to.
+ */
+static void grab_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
+{
+	grab_common_values(val, deref, obj, buf, sz);
+	switch (obj->type) {
+	case OBJ_TAG:
+		grab_tag_values(val, deref, obj, buf, sz);
+		grab_sub_body_contents(val, deref, obj, buf, sz);
+		grab_person("tagger", val, deref, obj, buf, sz);
+		break;
+	case OBJ_COMMIT:
+		grab_commit_values(val, deref, obj, buf, sz);
+		grab_sub_body_contents(val, deref, obj, buf, sz);
+		grab_person("author", val, deref, obj, buf, sz);
+		grab_person("committer", val, deref, obj, buf, sz);
+		break;
+	case OBJ_TREE:
+		/* grab_tree_values(val, deref, obj, buf, sz); */
+		break;
+	case OBJ_BLOB:
+		/* grab_blob_values(val, deref, obj, buf, sz); */
+		break;
+	default:
+		die("Eh?  Object of type %d?", obj->type);
+	}
+}
+
+static inline char *copy_advance(char *dst, const char *src)
+{
+	while (*src)
+		*dst++ = *src++;
+	return dst;
+}
+
+/*
+ * Parse the object referred by ref, and grab needed value.
+ */
+static void populate_value(struct refinfo *ref)
+{
+	void *buf;
+	struct object *obj;
+	int eaten, i;
+	unsigned long size;
+	const unsigned char *tagged;
+
+	ref->value = xcalloc(sizeof(struct atom_value), used_atom_cnt);
+
+	if (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) {
+		unsigned char unused1[20];
+		ref->symref = resolve_refdup(ref->refname, unused1, 1, NULL);
+		if (!ref->symref)
+			ref->symref = "";
+	}
+
+	/* Fill in specials first */
+	for (i = 0; i < used_atom_cnt; i++) {
+		const char *name = used_atom[i];
+		struct atom_value *v = &ref->value[i];
+		int deref = 0;
+		const char *refname;
+		const char *formatp;
+
+		if (*name == '*') {
+			deref = 1;
+			name++;
+		}
+
+		if (!prefixcmp(name, "refname"))
+			refname = ref->refname;
+		else if (!prefixcmp(name, "symref"))
+			refname = ref->symref ? ref->symref : "";
+		else if (!prefixcmp(name, "upstream")) {
+			struct branch *branch;
+			/* only local branches may have an upstream */
+			if (prefixcmp(ref->refname, "refs/heads/"))
+				continue;
+			branch = branch_get(ref->refname + 11);
+
+			if (!branch || !branch->merge || !branch->merge[0] ||
+			    !branch->merge[0]->dst)
+				continue;
+			refname = branch->merge[0]->dst;
+		}
+		else if (!strcmp(name, "flag")) {
+			char buf[256], *cp = buf;
+			if (ref->flag & REF_ISSYMREF)
+				cp = copy_advance(cp, ",symref");
+			if (ref->flag & REF_ISPACKED)
+				cp = copy_advance(cp, ",packed");
+			if (cp == buf)
+				v->s = "";
+			else {
+				*cp = '\0';
+				v->s = xstrdup(buf + 1);
+			}
+			continue;
+		}
+		else
+			continue;
+
+		formatp = strchr(name, ':');
+		/* look for "short" refname format */
+		if (formatp) {
+			formatp++;
+			if (!strcmp(formatp, "short"))
+				refname = shorten_unambiguous_ref(refname,
+						      warn_ambiguous_refs);
+			else
+				die("unknown %.*s format %s",
+				    (int)(formatp - name), name, formatp);
+		}
+
+		if (!deref)
+			v->s = refname;
+		else {
+			int len = strlen(refname);
+			char *s = xmalloc(len + 4);
+			sprintf(s, "%s^{}", refname);
+			v->s = s;
+		}
+	}
+
+	for (i = 0; i < used_atom_cnt; i++) {
+		struct atom_value *v = &ref->value[i];
+		if (v->s == NULL)
+			goto need_obj;
+	}
+	return;
+
+ need_obj:
+	buf = get_obj(ref->objectname, &obj, &size, &eaten);
+	if (!buf)
+		die("missing object %s for %s",
+		    sha1_to_hex(ref->objectname), ref->refname);
+	if (!obj)
+		die("parse_object_buffer failed on %s for %s",
+		    sha1_to_hex(ref->objectname), ref->refname);
+
+	grab_values(ref->value, 0, obj, buf, size);
+	if (!eaten)
+		free(buf);
+
+	/*
+	 * If there is no atom that wants to know about tagged
+	 * object, we are done.
+	 */
+	if (!need_tagged || (obj->type != OBJ_TAG))
+		return;
+
+	/*
+	 * If it is a tag object, see if we use a value that derefs
+	 * the object, and if we do grab the object it refers to.
+	 */
+	tagged = ((struct tag *)obj)->tagged->sha1;
+
+	/*
+	 * NEEDSWORK: This derefs tag only once, which
+	 * is good to deal with chains of trust, but
+	 * is not consistent with what deref_tag() does
+	 * which peels the onion to the core.
+	 */
+	buf = get_obj(tagged, &obj, &size, &eaten);
+	if (!buf)
+		die("missing object %s for %s",
+		    sha1_to_hex(tagged), ref->refname);
+	if (!obj)
+		die("parse_object_buffer failed on %s for %s",
+		    sha1_to_hex(tagged), ref->refname);
+	grab_values(ref->value, 1, obj, buf, size);
+	if (!eaten)
+		free(buf);
+}
+
+/*
+ * Given a ref, return the value for the atom.  This lazily gets value
+ * out of the object by calling populate value.
+ */
+static void get_value(struct refinfo *ref, int atom, struct atom_value **v)
+{
+	if (!ref->value) {
+		populate_value(ref);
+		fill_missing_values(ref->value);
+	}
+	*v = &ref->value[atom];
+}
+
+struct grab_ref_cbdata {
+	struct refinfo **grab_array;
+	const char **grab_pattern;
+	int grab_cnt;
+};
+
+/*
+ * A call-back given to for_each_ref().  Filter refs and keep them for
+ * later object processing.
+ */
+static int grab_single_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+{
+	struct grab_ref_cbdata *cb = cb_data;
+	struct refinfo *ref;
+	int cnt;
+
+	if (*cb->grab_pattern) {
+		const char **pattern;
+		int namelen = strlen(refname);
+		for (pattern = cb->grab_pattern; *pattern; pattern++) {
+			const char *p = *pattern;
+			int plen = strlen(p);
+
+			if ((plen <= namelen) &&
+			    !strncmp(refname, p, plen) &&
+			    (refname[plen] == '\0' ||
+			     refname[plen] == '/' ||
+			     p[plen-1] == '/'))
+				break;
+			if (!fnmatch(p, refname, FNM_PATHNAME))
+				break;
+		}
+		if (!*pattern)
+			return 0;
+	}
+
+	/*
+	 * We do not open the object yet; sort may only need refname
+	 * to do its job and the resulting list may yet to be pruned
+	 * by maxcount logic.
+	 */
+	ref = xcalloc(1, sizeof(*ref));
+	ref->refname = xstrdup(refname);
+	hashcpy(ref->objectname, sha1);
+	ref->flag = flag;
+
+	cnt = cb->grab_cnt;
+	cb->grab_array = xrealloc(cb->grab_array,
+				  sizeof(*cb->grab_array) * (cnt + 1));
+	cb->grab_array[cnt++] = ref;
+	cb->grab_cnt = cnt;
+	return 0;
+}
+
+static int cmp_ref_sort(struct ref_sort *s, struct refinfo *a, struct refinfo *b)
+{
+	struct atom_value *va, *vb;
+	int cmp;
+	cmp_type cmp_type = used_atom_type[s->atom];
+
+	get_value(a, s->atom, &va);
+	get_value(b, s->atom, &vb);
+	switch (cmp_type) {
+	case FIELD_STR:
+		cmp = strcmp(va->s, vb->s);
+		break;
+	default:
+		if (va->ul < vb->ul)
+			cmp = -1;
+		else if (va->ul == vb->ul)
+			cmp = 0;
+		else
+			cmp = 1;
+		break;
+	}
+	return (s->reverse) ? -cmp : cmp;
+}
+
+static struct ref_sort *ref_sort;
+static int compare_refs(const void *a_, const void *b_)
+{
+	struct refinfo *a = *((struct refinfo **)a_);
+	struct refinfo *b = *((struct refinfo **)b_);
+	struct ref_sort *s;
+
+	for (s = ref_sort; s; s = s->next) {
+		int cmp = cmp_ref_sort(s, a, b);
+		if (cmp)
+			return cmp;
+	}
+	return 0;
+}
+
+static void sort_refs(struct ref_sort *sort, struct refinfo **refs, int num_refs)
+{
+	ref_sort = sort;
+	qsort(refs, num_refs, sizeof(struct refinfo *), compare_refs);
+}
+
+static void print_value(struct refinfo *ref, int atom, int quote_style)
+{
+	struct atom_value *v;
+	get_value(ref, atom, &v);
+	switch (quote_style) {
+	case QUOTE_NONE:
+		fputs(v->s, stdout);
+		break;
+	case QUOTE_SHELL:
+		sq_quote_print(stdout, v->s);
+		break;
+	case QUOTE_PERL:
+		perl_quote_print(stdout, v->s);
+		break;
+	case QUOTE_PYTHON:
+		python_quote_print(stdout, v->s);
+		break;
+	case QUOTE_TCL:
+		tcl_quote_print(stdout, v->s);
+		break;
+	}
+}
+
+static int hex1(char ch)
+{
+	if ('0' <= ch && ch <= '9')
+		return ch - '0';
+	else if ('a' <= ch && ch <= 'f')
+		return ch - 'a' + 10;
+	else if ('A' <= ch && ch <= 'F')
+		return ch - 'A' + 10;
+	return -1;
+}
+static int hex2(const char *cp)
+{
+	if (cp[0] && cp[1])
+		return (hex1(cp[0]) << 4) | hex1(cp[1]);
+	else
+		return -1;
+}
+
+static void emit(const char *cp, const char *ep)
+{
+	while (*cp && (!ep || cp < ep)) {
+		if (*cp == '%') {
+			if (cp[1] == '%')
+				cp++;
+			else {
+				int ch = hex2(cp + 1);
+				if (0 <= ch) {
+					putchar(ch);
+					cp += 3;
+					continue;
+				}
+			}
+		}
+		putchar(*cp);
+		cp++;
+	}
+}
+
+static void show_ref(struct refinfo *info, const char *format, int quote_style)
+{
+	const char *cp, *sp, *ep;
+
+	for (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) {
+		ep = strchr(sp, ')');
+		if (cp < sp)
+			emit(cp, sp);
+		print_value(info, parse_atom(sp + 2, ep), quote_style);
+	}
+	if (*cp) {
+		sp = cp + strlen(cp);
+		emit(cp, sp);
+	}
+	putchar('\n');
+}
+
+static struct ref_sort *default_sort(void)
+{
+	static const char cstr_name[] = "refname";
+
+	struct ref_sort *sort = xcalloc(1, sizeof(*sort));
+
+	sort->next = NULL;
+	sort->atom = parse_atom(cstr_name, cstr_name + strlen(cstr_name));
+	return sort;
+}
+
+static int opt_parse_sort(const struct option *opt, const char *arg, int unset)
+{
+	struct ref_sort **sort_tail = opt->value;
+	struct ref_sort *s;
+	int len;
+
+	if (!arg) /* should --no-sort void the list ? */
+		return -1;
+
+	*sort_tail = s = xcalloc(1, sizeof(*s));
+
+	if (*arg == '-') {
+		s->reverse = 1;
+		arg++;
+	}
+	len = strlen(arg);
+	s->atom = parse_atom(arg, arg+len);
+	return 0;
+}
+
+static char const * const for_each_ref_usage[] = {
+	"git for-each-ref [options] [<pattern>]",
+	NULL
+};
+
+int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
+{
+	int i, num_refs;
+	const char *format = "%(objectname) %(objecttype)\t%(refname)";
+	struct ref_sort *sort = NULL, **sort_tail = &sort;
+	int maxcount = 0, quote_style = 0;
+	struct refinfo **refs;
+	struct grab_ref_cbdata cbdata;
+
+	struct option opts[] = {
+		OPT_BIT('s', "shell", &quote_style,
+		        "quote placeholders suitably for shells", QUOTE_SHELL),
+		OPT_BIT('p', "perl",  &quote_style,
+		        "quote placeholders suitably for perl", QUOTE_PERL),
+		OPT_BIT(0 , "python", &quote_style,
+		        "quote placeholders suitably for python", QUOTE_PYTHON),
+		OPT_BIT(0 , "tcl",  &quote_style,
+		        "quote placeholders suitably for tcl", QUOTE_TCL),
+
+		OPT_GROUP(""),
+		OPT_INTEGER( 0 , "count", &maxcount, "show only <n> matched refs"),
+		OPT_STRING(  0 , "format", &format, "format", "format to use for the output"),
+		OPT_CALLBACK(0 , "sort", sort_tail, "key",
+		            "field name to sort on", &opt_parse_sort),
+		OPT_END(),
+	};
+
+	parse_options(argc, argv, prefix, opts, for_each_ref_usage, 0);
+	if (maxcount < 0) {
+		error("invalid --count argument: `%d'", maxcount);
+		usage_with_options(for_each_ref_usage, opts);
+	}
+	if (HAS_MULTI_BITS(quote_style)) {
+		error("more than one quoting style?");
+		usage_with_options(for_each_ref_usage, opts);
+	}
+	if (verify_format(format))
+		usage_with_options(for_each_ref_usage, opts);
+
+	if (!sort)
+		sort = default_sort();
+	sort_atom_limit = used_atom_cnt;
+
+	/* for warn_ambiguous_refs */
+	git_config(git_default_config, NULL);
+
+	memset(&cbdata, 0, sizeof(cbdata));
+	cbdata.grab_pattern = argv;
+	for_each_rawref(grab_single_ref, &cbdata);
+	refs = cbdata.grab_array;
+	num_refs = cbdata.grab_cnt;
+
+	sort_refs(sort, refs, num_refs);
+
+	if (!maxcount || num_refs < maxcount)
+		maxcount = num_refs;
+	for (i = 0; i < maxcount; i++)
+		show_ref(refs[i], format, quote_style);
+	return 0;
+}
diff --git a/builtin/fsck.c b/builtin/fsck.c
new file mode 100644
index 0000000..a710227
--- /dev/null
+++ b/builtin/fsck.c
@@ -0,0 +1,738 @@
+#include "builtin.h"
+#include "cache.h"
+#include "commit.h"
+#include "tree.h"
+#include "blob.h"
+#include "tag.h"
+#include "refs.h"
+#include "pack.h"
+#include "cache-tree.h"
+#include "tree-walk.h"
+#include "fsck.h"
+#include "parse-options.h"
+#include "dir.h"
+#include "progress.h"
+#include "streaming.h"
+
+#define REACHABLE 0x0001
+#define SEEN      0x0002
+
+static int show_root;
+static int show_tags;
+static int show_unreachable;
+static int include_reflogs = 1;
+static int check_full = 1;
+static int check_strict;
+static int keep_cache_objects;
+static unsigned char head_sha1[20];
+static const char *head_points_at;
+static int errors_found;
+static int write_lost_and_found;
+static int verbose;
+static int show_progress = -1;
+static int show_dangling = 1;
+#define ERROR_OBJECT 01
+#define ERROR_REACHABLE 02
+#define ERROR_PACK 04
+
+#ifdef NO_D_INO_IN_DIRENT
+#define SORT_DIRENT 0
+#define DIRENT_SORT_HINT(de) 0
+#else
+#define SORT_DIRENT 1
+#define DIRENT_SORT_HINT(de) ((de)->d_ino)
+#endif
+
+static void objreport(struct object *obj, const char *severity,
+                      const char *err, va_list params)
+{
+	fprintf(stderr, "%s in %s %s: ",
+	        severity, typename(obj->type), sha1_to_hex(obj->sha1));
+	vfprintf(stderr, err, params);
+	fputs("\n", stderr);
+}
+
+__attribute__((format (printf, 2, 3)))
+static int objerror(struct object *obj, const char *err, ...)
+{
+	va_list params;
+	va_start(params, err);
+	errors_found |= ERROR_OBJECT;
+	objreport(obj, "error", err, params);
+	va_end(params);
+	return -1;
+}
+
+__attribute__((format (printf, 3, 4)))
+static int fsck_error_func(struct object *obj, int type, const char *err, ...)
+{
+	va_list params;
+	va_start(params, err);
+	objreport(obj, (type == FSCK_WARN) ? "warning" : "error", err, params);
+	va_end(params);
+	return (type == FSCK_WARN) ? 0 : 1;
+}
+
+static struct object_array pending;
+
+static int mark_object(struct object *obj, int type, void *data)
+{
+	struct object *parent = data;
+
+	/*
+	 * The only case data is NULL or type is OBJ_ANY is when
+	 * mark_object_reachable() calls us.  All the callers of
+	 * that function has non-NULL obj hence ...
+	 */
+	if (!obj) {
+		/* ... these references to parent->fld are safe here */
+		printf("broken link from %7s %s\n",
+			   typename(parent->type), sha1_to_hex(parent->sha1));
+		printf("broken link from %7s %s\n",
+			   (type == OBJ_ANY ? "unknown" : typename(type)), "unknown");
+		errors_found |= ERROR_REACHABLE;
+		return 1;
+	}
+
+	if (type != OBJ_ANY && obj->type != type)
+		/* ... and the reference to parent is safe here */
+		objerror(parent, "wrong object type in link");
+
+	if (obj->flags & REACHABLE)
+		return 0;
+	obj->flags |= REACHABLE;
+	if (!obj->parsed) {
+		if (parent && !has_sha1_file(obj->sha1)) {
+			printf("broken link from %7s %s\n",
+				 typename(parent->type), sha1_to_hex(parent->sha1));
+			printf("              to %7s %s\n",
+				 typename(obj->type), sha1_to_hex(obj->sha1));
+			errors_found |= ERROR_REACHABLE;
+		}
+		return 1;
+	}
+
+	add_object_array(obj, (void *) parent, &pending);
+	return 0;
+}
+
+static void mark_object_reachable(struct object *obj)
+{
+	mark_object(obj, OBJ_ANY, NULL);
+}
+
+static int traverse_one_object(struct object *obj)
+{
+	int result;
+	struct tree *tree = NULL;
+
+	if (obj->type == OBJ_TREE) {
+		obj->parsed = 0;
+		tree = (struct tree *)obj;
+		if (parse_tree(tree) < 0)
+			return 1; /* error already displayed */
+	}
+	result = fsck_walk(obj, mark_object, obj);
+	if (tree) {
+		free(tree->buffer);
+		tree->buffer = NULL;
+	}
+	return result;
+}
+
+static int traverse_reachable(void)
+{
+	struct progress *progress = NULL;
+	unsigned int nr = 0;
+	int result = 0;
+	if (show_progress)
+		progress = start_progress_delay("Checking connectivity", 0, 0, 2);
+	while (pending.nr) {
+		struct object_array_entry *entry;
+		struct object *obj;
+
+		entry = pending.objects + --pending.nr;
+		obj = entry->item;
+		result |= traverse_one_object(obj);
+		display_progress(progress, ++nr);
+	}
+	stop_progress(&progress);
+	return !!result;
+}
+
+static int mark_used(struct object *obj, int type, void *data)
+{
+	if (!obj)
+		return 1;
+	obj->used = 1;
+	return 0;
+}
+
+/*
+ * Check a single reachable object
+ */
+static void check_reachable_object(struct object *obj)
+{
+	/*
+	 * We obviously want the object to be parsed,
+	 * except if it was in a pack-file and we didn't
+	 * do a full fsck
+	 */
+	if (!obj->parsed) {
+		if (has_sha1_pack(obj->sha1))
+			return; /* it is in pack - forget about it */
+		printf("missing %s %s\n", typename(obj->type), sha1_to_hex(obj->sha1));
+		errors_found |= ERROR_REACHABLE;
+		return;
+	}
+}
+
+/*
+ * Check a single unreachable object
+ */
+static void check_unreachable_object(struct object *obj)
+{
+	/*
+	 * Missing unreachable object? Ignore it. It's not like
+	 * we miss it (since it can't be reached), nor do we want
+	 * to complain about it being unreachable (since it does
+	 * not exist).
+	 */
+	if (!obj->parsed)
+		return;
+
+	/*
+	 * Unreachable object that exists? Show it if asked to,
+	 * since this is something that is prunable.
+	 */
+	if (show_unreachable) {
+		printf("unreachable %s %s\n", typename(obj->type), sha1_to_hex(obj->sha1));
+		return;
+	}
+
+	/*
+	 * "!used" means that nothing at all points to it, including
+	 * other unreachable objects. In other words, it's the "tip"
+	 * of some set of unreachable objects, usually a commit that
+	 * got dropped.
+	 *
+	 * Such starting points are more interesting than some random
+	 * set of unreachable objects, so we show them even if the user
+	 * hasn't asked for _all_ unreachable objects. If you have
+	 * deleted a branch by mistake, this is a prime candidate to
+	 * start looking at, for example.
+	 */
+	if (!obj->used) {
+		if (show_dangling)
+			printf("dangling %s %s\n", typename(obj->type),
+			       sha1_to_hex(obj->sha1));
+		if (write_lost_and_found) {
+			char *filename = git_path("lost-found/%s/%s",
+				obj->type == OBJ_COMMIT ? "commit" : "other",
+				sha1_to_hex(obj->sha1));
+			FILE *f;
+
+			if (safe_create_leading_directories(filename)) {
+				error("Could not create lost-found");
+				return;
+			}
+			if (!(f = fopen(filename, "w")))
+				die_errno("Could not open '%s'", filename);
+			if (obj->type == OBJ_BLOB) {
+				if (stream_blob_to_fd(fileno(f), obj->sha1, NULL, 1))
+					die_errno("Could not write '%s'", filename);
+			} else
+				fprintf(f, "%s\n", sha1_to_hex(obj->sha1));
+			if (fclose(f))
+				die_errno("Could not finish '%s'",
+					  filename);
+		}
+		return;
+	}
+
+	/*
+	 * Otherwise? It's there, it's unreachable, and some other unreachable
+	 * object points to it. Ignore it - it's not interesting, and we showed
+	 * all the interesting cases above.
+	 */
+}
+
+static void check_object(struct object *obj)
+{
+	if (verbose)
+		fprintf(stderr, "Checking %s\n", sha1_to_hex(obj->sha1));
+
+	if (obj->flags & REACHABLE)
+		check_reachable_object(obj);
+	else
+		check_unreachable_object(obj);
+}
+
+static void check_connectivity(void)
+{
+	int i, max;
+
+	/* Traverse the pending reachable objects */
+	traverse_reachable();
+
+	/* Look up all the requirements, warn about missing objects.. */
+	max = get_max_object_index();
+	if (verbose)
+		fprintf(stderr, "Checking connectivity (%d objects)\n", max);
+
+	for (i = 0; i < max; i++) {
+		struct object *obj = get_indexed_object(i);
+
+		if (obj)
+			check_object(obj);
+	}
+}
+
+static int fsck_obj(struct object *obj)
+{
+	if (obj->flags & SEEN)
+		return 0;
+	obj->flags |= SEEN;
+
+	if (verbose)
+		fprintf(stderr, "Checking %s %s\n",
+			typename(obj->type), sha1_to_hex(obj->sha1));
+
+	if (fsck_walk(obj, mark_used, NULL))
+		objerror(obj, "broken links");
+	if (fsck_object(obj, check_strict, fsck_error_func))
+		return -1;
+
+	if (obj->type == OBJ_TREE) {
+		struct tree *item = (struct tree *) obj;
+
+		free(item->buffer);
+		item->buffer = NULL;
+	}
+
+	if (obj->type == OBJ_COMMIT) {
+		struct commit *commit = (struct commit *) obj;
+
+		free(commit->buffer);
+		commit->buffer = NULL;
+
+		if (!commit->parents && show_root)
+			printf("root %s\n", sha1_to_hex(commit->object.sha1));
+	}
+
+	if (obj->type == OBJ_TAG) {
+		struct tag *tag = (struct tag *) obj;
+
+		if (show_tags && tag->tagged) {
+			printf("tagged %s %s", typename(tag->tagged->type), sha1_to_hex(tag->tagged->sha1));
+			printf(" (%s) in %s\n", tag->tag, sha1_to_hex(tag->object.sha1));
+		}
+	}
+
+	return 0;
+}
+
+static int fsck_sha1(const unsigned char *sha1)
+{
+	struct object *obj = parse_object(sha1);
+	if (!obj) {
+		errors_found |= ERROR_OBJECT;
+		return error("%s: object corrupt or missing",
+			     sha1_to_hex(sha1));
+	}
+	return fsck_obj(obj);
+}
+
+static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type,
+			   unsigned long size, void *buffer, int *eaten)
+{
+	struct object *obj;
+	obj = parse_object_buffer(sha1, type, size, buffer, eaten);
+	if (!obj) {
+		errors_found |= ERROR_OBJECT;
+		return error("%s: object corrupt or missing", sha1_to_hex(sha1));
+	}
+	return fsck_obj(obj);
+}
+
+/*
+ * This is the sorting chunk size: make it reasonably
+ * big so that we can sort well..
+ */
+#define MAX_SHA1_ENTRIES (1024)
+
+struct sha1_entry {
+	unsigned long ino;
+	unsigned char sha1[20];
+};
+
+static struct {
+	unsigned long nr;
+	struct sha1_entry *entry[MAX_SHA1_ENTRIES];
+} sha1_list;
+
+static int ino_compare(const void *_a, const void *_b)
+{
+	const struct sha1_entry *a = _a, *b = _b;
+	unsigned long ino1 = a->ino, ino2 = b->ino;
+	return ino1 < ino2 ? -1 : ino1 > ino2 ? 1 : 0;
+}
+
+static void fsck_sha1_list(void)
+{
+	int i, nr = sha1_list.nr;
+
+	if (SORT_DIRENT)
+		qsort(sha1_list.entry, nr,
+		      sizeof(struct sha1_entry *), ino_compare);
+	for (i = 0; i < nr; i++) {
+		struct sha1_entry *entry = sha1_list.entry[i];
+		unsigned char *sha1 = entry->sha1;
+
+		sha1_list.entry[i] = NULL;
+		fsck_sha1(sha1);
+		free(entry);
+	}
+	sha1_list.nr = 0;
+}
+
+static void add_sha1_list(unsigned char *sha1, unsigned long ino)
+{
+	struct sha1_entry *entry = xmalloc(sizeof(*entry));
+	int nr;
+
+	entry->ino = ino;
+	hashcpy(entry->sha1, sha1);
+	nr = sha1_list.nr;
+	if (nr == MAX_SHA1_ENTRIES) {
+		fsck_sha1_list();
+		nr = 0;
+	}
+	sha1_list.entry[nr] = entry;
+	sha1_list.nr = ++nr;
+}
+
+static inline int is_loose_object_file(struct dirent *de,
+				       char *name, unsigned char *sha1)
+{
+	if (strlen(de->d_name) != 38)
+		return 0;
+	memcpy(name + 2, de->d_name, 39);
+	return !get_sha1_hex(name, sha1);
+}
+
+static void fsck_dir(int i, char *path)
+{
+	DIR *dir = opendir(path);
+	struct dirent *de;
+	char name[100];
+
+	if (!dir)
+		return;
+
+	if (verbose)
+		fprintf(stderr, "Checking directory %s\n", path);
+
+	sprintf(name, "%02x", i);
+	while ((de = readdir(dir)) != NULL) {
+		unsigned char sha1[20];
+
+		if (is_dot_or_dotdot(de->d_name))
+			continue;
+		if (is_loose_object_file(de, name, sha1)) {
+			add_sha1_list(sha1, DIRENT_SORT_HINT(de));
+			continue;
+		}
+		if (!prefixcmp(de->d_name, "tmp_obj_"))
+			continue;
+		fprintf(stderr, "bad sha1 file: %s/%s\n", path, de->d_name);
+	}
+	closedir(dir);
+}
+
+static int default_refs;
+
+static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
+		const char *email, unsigned long timestamp, int tz,
+		const char *message, void *cb_data)
+{
+	struct object *obj;
+
+	if (verbose)
+		fprintf(stderr, "Checking reflog %s->%s\n",
+			sha1_to_hex(osha1), sha1_to_hex(nsha1));
+
+	if (!is_null_sha1(osha1)) {
+		obj = lookup_object(osha1);
+		if (obj) {
+			obj->used = 1;
+			mark_object_reachable(obj);
+		}
+	}
+	obj = lookup_object(nsha1);
+	if (obj) {
+		obj->used = 1;
+		mark_object_reachable(obj);
+	}
+	return 0;
+}
+
+static int fsck_handle_reflog(const char *logname, const unsigned char *sha1, int flag, void *cb_data)
+{
+	for_each_reflog_ent(logname, fsck_handle_reflog_ent, NULL);
+	return 0;
+}
+
+static int is_branch(const char *refname)
+{
+	return !strcmp(refname, "HEAD") || !prefixcmp(refname, "refs/heads/");
+}
+
+static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+{
+	struct object *obj;
+
+	obj = parse_object(sha1);
+	if (!obj) {
+		error("%s: invalid sha1 pointer %s", refname, sha1_to_hex(sha1));
+		/* We'll continue with the rest despite the error.. */
+		return 0;
+	}
+	if (obj->type != OBJ_COMMIT && is_branch(refname))
+		error("%s: not a commit", refname);
+	default_refs++;
+	obj->used = 1;
+	mark_object_reachable(obj);
+
+	return 0;
+}
+
+static void get_default_heads(void)
+{
+	if (head_points_at && !is_null_sha1(head_sha1))
+		fsck_handle_ref("HEAD", head_sha1, 0, NULL);
+	for_each_ref(fsck_handle_ref, NULL);
+	if (include_reflogs)
+		for_each_reflog(fsck_handle_reflog, NULL);
+
+	/*
+	 * Not having any default heads isn't really fatal, but
+	 * it does mean that "--unreachable" no longer makes any
+	 * sense (since in this case everything will obviously
+	 * be unreachable by definition.
+	 *
+	 * Showing dangling objects is valid, though (as those
+	 * dangling objects are likely lost heads).
+	 *
+	 * So we just print a warning about it, and clear the
+	 * "show_unreachable" flag.
+	 */
+	if (!default_refs) {
+		fprintf(stderr, "notice: No default references\n");
+		show_unreachable = 0;
+	}
+}
+
+static void fsck_object_dir(const char *path)
+{
+	int i;
+	struct progress *progress = NULL;
+
+	if (verbose)
+		fprintf(stderr, "Checking object directory\n");
+
+	if (show_progress)
+		progress = start_progress("Checking object directories", 256);
+	for (i = 0; i < 256; i++) {
+		static char dir[4096];
+		sprintf(dir, "%s/%02x", path, i);
+		fsck_dir(i, dir);
+		display_progress(progress, i+1);
+	}
+	stop_progress(&progress);
+	fsck_sha1_list();
+}
+
+static int fsck_head_link(void)
+{
+	int flag;
+	int null_is_error = 0;
+
+	if (verbose)
+		fprintf(stderr, "Checking HEAD link\n");
+
+	head_points_at = resolve_ref_unsafe("HEAD", head_sha1, 0, &flag);
+	if (!head_points_at)
+		return error("Invalid HEAD");
+	if (!strcmp(head_points_at, "HEAD"))
+		/* detached HEAD */
+		null_is_error = 1;
+	else if (prefixcmp(head_points_at, "refs/heads/"))
+		return error("HEAD points to something strange (%s)",
+			     head_points_at);
+	if (is_null_sha1(head_sha1)) {
+		if (null_is_error)
+			return error("HEAD: detached HEAD points at nothing");
+		fprintf(stderr, "notice: HEAD points to an unborn branch (%s)\n",
+			head_points_at + 11);
+	}
+	return 0;
+}
+
+static int fsck_cache_tree(struct cache_tree *it)
+{
+	int i;
+	int err = 0;
+
+	if (verbose)
+		fprintf(stderr, "Checking cache tree\n");
+
+	if (0 <= it->entry_count) {
+		struct object *obj = parse_object(it->sha1);
+		if (!obj) {
+			error("%s: invalid sha1 pointer in cache-tree",
+			      sha1_to_hex(it->sha1));
+			return 1;
+		}
+		obj->used = 1;
+		mark_object_reachable(obj);
+		if (obj->type != OBJ_TREE)
+			err |= objerror(obj, "non-tree in cache-tree");
+	}
+	for (i = 0; i < it->subtree_nr; i++)
+		err |= fsck_cache_tree(it->down[i]->cache_tree);
+	return err;
+}
+
+static char const * const fsck_usage[] = {
+	"git fsck [options] [<object>...]",
+	NULL
+};
+
+static struct option fsck_opts[] = {
+	OPT__VERBOSE(&verbose, "be verbose"),
+	OPT_BOOLEAN(0, "unreachable", &show_unreachable, "show unreachable objects"),
+	OPT_BOOL(0, "dangling", &show_dangling, "show dangling objects"),
+	OPT_BOOLEAN(0, "tags", &show_tags, "report tags"),
+	OPT_BOOLEAN(0, "root", &show_root, "report root nodes"),
+	OPT_BOOLEAN(0, "cache", &keep_cache_objects, "make index objects head nodes"),
+	OPT_BOOLEAN(0, "reflogs", &include_reflogs, "make reflogs head nodes (default)"),
+	OPT_BOOLEAN(0, "full", &check_full, "also consider packs and alternate objects"),
+	OPT_BOOLEAN(0, "strict", &check_strict, "enable more strict checking"),
+	OPT_BOOLEAN(0, "lost-found", &write_lost_and_found,
+				"write dangling objects in .git/lost-found"),
+	OPT_BOOL(0, "progress", &show_progress, "show progress"),
+	OPT_END(),
+};
+
+int cmd_fsck(int argc, const char **argv, const char *prefix)
+{
+	int i, heads;
+	struct alternate_object_database *alt;
+
+	errors_found = 0;
+	read_replace_refs = 0;
+
+	argc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0);
+
+	if (show_progress == -1)
+		show_progress = isatty(2);
+	if (verbose)
+		show_progress = 0;
+
+	if (write_lost_and_found) {
+		check_full = 1;
+		include_reflogs = 0;
+	}
+
+	fsck_head_link();
+	fsck_object_dir(get_object_directory());
+
+	prepare_alt_odb();
+	for (alt = alt_odb_list; alt; alt = alt->next) {
+		char namebuf[PATH_MAX];
+		int namelen = alt->name - alt->base;
+		memcpy(namebuf, alt->base, namelen);
+		namebuf[namelen - 1] = 0;
+		fsck_object_dir(namebuf);
+	}
+
+	if (check_full) {
+		struct packed_git *p;
+		uint32_t total = 0, count = 0;
+		struct progress *progress = NULL;
+
+		prepare_packed_git();
+
+		if (show_progress) {
+			for (p = packed_git; p; p = p->next) {
+				if (open_pack_index(p))
+					continue;
+				total += p->num_objects;
+			}
+
+			progress = start_progress("Checking objects", total);
+		}
+		for (p = packed_git; p; p = p->next) {
+			/* verify gives error messages itself */
+			if (verify_pack(p, fsck_obj_buffer,
+					progress, count))
+				errors_found |= ERROR_PACK;
+			count += p->num_objects;
+		}
+		stop_progress(&progress);
+	}
+
+	heads = 0;
+	for (i = 0; i < argc; i++) {
+		const char *arg = argv[i];
+		unsigned char sha1[20];
+		if (!get_sha1(arg, sha1)) {
+			struct object *obj = lookup_object(sha1);
+
+			/* Error is printed by lookup_object(). */
+			if (!obj)
+				continue;
+
+			obj->used = 1;
+			mark_object_reachable(obj);
+			heads++;
+			continue;
+		}
+		error("invalid parameter: expected sha1, got '%s'", arg);
+	}
+
+	/*
+	 * If we've not been given any explicit head information, do the
+	 * default ones from .git/refs. We also consider the index file
+	 * in this case (ie this implies --cache).
+	 */
+	if (!heads) {
+		get_default_heads();
+		keep_cache_objects = 1;
+	}
+
+	if (keep_cache_objects) {
+		read_cache();
+		for (i = 0; i < active_nr; i++) {
+			unsigned int mode;
+			struct blob *blob;
+			struct object *obj;
+
+			mode = active_cache[i]->ce_mode;
+			if (S_ISGITLINK(mode))
+				continue;
+			blob = lookup_blob(active_cache[i]->sha1);
+			if (!blob)
+				continue;
+			obj = &blob->object;
+			obj->used = 1;
+			mark_object_reachable(obj);
+		}
+		if (active_cache_tree)
+			fsck_cache_tree(active_cache_tree);
+	}
+
+	check_connectivity();
+	return errors_found;
+}
diff --git a/builtin/gc.c b/builtin/gc.c
new file mode 100644
index 0000000..9b4232c
--- /dev/null
+++ b/builtin/gc.c
@@ -0,0 +1,255 @@
+/*
+ * git gc builtin command
+ *
+ * Cleanup unreachable files and optimize the repository.
+ *
+ * Copyright (c) 2007 James Bowes
+ *
+ * Based on git-gc.sh, which is
+ *
+ * Copyright (c) 2006 Shawn O. Pearce
+ */
+
+#include "builtin.h"
+#include "cache.h"
+#include "parse-options.h"
+#include "run-command.h"
+#include "argv-array.h"
+
+#define FAILED_RUN "failed to run %s"
+
+static const char * const builtin_gc_usage[] = {
+	"git gc [options]",
+	NULL
+};
+
+static int pack_refs = 1;
+static int aggressive_window = 250;
+static int gc_auto_threshold = 6700;
+static int gc_auto_pack_limit = 50;
+static const char *prune_expire = "2.weeks.ago";
+
+static struct argv_array pack_refs_cmd = ARGV_ARRAY_INIT;
+static struct argv_array reflog = ARGV_ARRAY_INIT;
+static struct argv_array repack = ARGV_ARRAY_INIT;
+static struct argv_array prune = ARGV_ARRAY_INIT;
+static struct argv_array rerere = ARGV_ARRAY_INIT;
+
+static int gc_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, "gc.packrefs")) {
+		if (value && !strcmp(value, "notbare"))
+			pack_refs = -1;
+		else
+			pack_refs = git_config_bool(var, value);
+		return 0;
+	}
+	if (!strcmp(var, "gc.aggressivewindow")) {
+		aggressive_window = git_config_int(var, value);
+		return 0;
+	}
+	if (!strcmp(var, "gc.auto")) {
+		gc_auto_threshold = git_config_int(var, value);
+		return 0;
+	}
+	if (!strcmp(var, "gc.autopacklimit")) {
+		gc_auto_pack_limit = git_config_int(var, value);
+		return 0;
+	}
+	if (!strcmp(var, "gc.pruneexpire")) {
+		if (value && strcmp(value, "now")) {
+			unsigned long now = approxidate("now");
+			if (approxidate(value) >= now)
+				return error(_("Invalid %s: '%s'"), var, value);
+		}
+		return git_config_string(&prune_expire, var, value);
+	}
+	return git_default_config(var, value, cb);
+}
+
+static int too_many_loose_objects(void)
+{
+	/*
+	 * Quickly check if a "gc" is needed, by estimating how
+	 * many loose objects there are.  Because SHA-1 is evenly
+	 * distributed, we can check only one and get a reasonable
+	 * estimate.
+	 */
+	char path[PATH_MAX];
+	const char *objdir = get_object_directory();
+	DIR *dir;
+	struct dirent *ent;
+	int auto_threshold;
+	int num_loose = 0;
+	int needed = 0;
+
+	if (gc_auto_threshold <= 0)
+		return 0;
+
+	if (sizeof(path) <= snprintf(path, sizeof(path), "%s/17", objdir)) {
+		warning(_("insanely long object directory %.*s"), 50, objdir);
+		return 0;
+	}
+	dir = opendir(path);
+	if (!dir)
+		return 0;
+
+	auto_threshold = (gc_auto_threshold + 255) / 256;
+	while ((ent = readdir(dir)) != NULL) {
+		if (strspn(ent->d_name, "0123456789abcdef") != 38 ||
+		    ent->d_name[38] != '\0')
+			continue;
+		if (++num_loose > auto_threshold) {
+			needed = 1;
+			break;
+		}
+	}
+	closedir(dir);
+	return needed;
+}
+
+static int too_many_packs(void)
+{
+	struct packed_git *p;
+	int cnt;
+
+	if (gc_auto_pack_limit <= 0)
+		return 0;
+
+	prepare_packed_git();
+	for (cnt = 0, p = packed_git; p; p = p->next) {
+		if (!p->pack_local)
+			continue;
+		if (p->pack_keep)
+			continue;
+		/*
+		 * Perhaps check the size of the pack and count only
+		 * very small ones here?
+		 */
+		cnt++;
+	}
+	return gc_auto_pack_limit <= cnt;
+}
+
+static void add_repack_all_option(void)
+{
+	if (prune_expire && !strcmp(prune_expire, "now"))
+		argv_array_push(&repack, "-a");
+	else {
+		argv_array_push(&repack, "-A");
+		if (prune_expire)
+			argv_array_pushf(&repack, "--unpack-unreachable=%s", prune_expire);
+	}
+}
+
+static int need_to_gc(void)
+{
+	/*
+	 * Setting gc.auto to 0 or negative can disable the
+	 * automatic gc.
+	 */
+	if (gc_auto_threshold <= 0)
+		return 0;
+
+	/*
+	 * If there are too many loose objects, but not too many
+	 * packs, we run "repack -d -l".  If there are too many packs,
+	 * we run "repack -A -d -l".  Otherwise we tell the caller
+	 * there is no need.
+	 */
+	if (too_many_packs())
+		add_repack_all_option();
+	else if (!too_many_loose_objects())
+		return 0;
+
+	if (run_hook(NULL, "pre-auto-gc", NULL))
+		return 0;
+	return 1;
+}
+
+int cmd_gc(int argc, const char **argv, const char *prefix)
+{
+	int aggressive = 0;
+	int auto_gc = 0;
+	int quiet = 0;
+
+	struct option builtin_gc_options[] = {
+		OPT__QUIET(&quiet, "suppress progress reporting"),
+		{ OPTION_STRING, 0, "prune", &prune_expire, "date",
+			"prune unreferenced objects",
+			PARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire },
+		OPT_BOOLEAN(0, "aggressive", &aggressive, "be more thorough (increased runtime)"),
+		OPT_BOOLEAN(0, "auto", &auto_gc, "enable auto-gc mode"),
+		OPT_END()
+	};
+
+	if (argc == 2 && !strcmp(argv[1], "-h"))
+		usage_with_options(builtin_gc_usage, builtin_gc_options);
+
+	argv_array_pushl(&pack_refs_cmd, "pack-refs", "--all", "--prune", NULL);
+	argv_array_pushl(&reflog, "reflog", "expire", "--all", NULL);
+	argv_array_pushl(&repack, "repack", "-d", "-l", NULL);
+	argv_array_pushl(&prune, "prune", "--expire", NULL );
+	argv_array_pushl(&rerere, "rerere", "gc", NULL);
+
+	git_config(gc_config, NULL);
+
+	if (pack_refs < 0)
+		pack_refs = !is_bare_repository();
+
+	argc = parse_options(argc, argv, prefix, builtin_gc_options,
+			     builtin_gc_usage, 0);
+	if (argc > 0)
+		usage_with_options(builtin_gc_usage, builtin_gc_options);
+
+	if (aggressive) {
+		argv_array_push(&repack, "-f");
+		argv_array_push(&repack, "--depth=250");
+		if (aggressive_window > 0)
+			argv_array_pushf(&repack, "--window=%d", aggressive_window);
+	}
+	if (quiet)
+		argv_array_push(&repack, "-q");
+
+	if (auto_gc) {
+		/*
+		 * Auto-gc should be least intrusive as possible.
+		 */
+		if (!need_to_gc())
+			return 0;
+		if (quiet)
+			fprintf(stderr, _("Auto packing the repository for optimum performance.\n"));
+		else
+			fprintf(stderr,
+					_("Auto packing the repository for optimum performance. You may also\n"
+					"run \"git gc\" manually. See "
+					"\"git help gc\" for more information.\n"));
+	} else
+		add_repack_all_option();
+
+	if (pack_refs && run_command_v_opt(pack_refs_cmd.argv, RUN_GIT_CMD))
+		return error(FAILED_RUN, pack_refs_cmd.argv[0]);
+
+	if (run_command_v_opt(reflog.argv, RUN_GIT_CMD))
+		return error(FAILED_RUN, reflog.argv[0]);
+
+	if (run_command_v_opt(repack.argv, RUN_GIT_CMD))
+		return error(FAILED_RUN, repack.argv[0]);
+
+	if (prune_expire) {
+		argv_array_push(&prune, prune_expire);
+		if (quiet)
+			argv_array_push(&prune, "--no-progress");
+		if (run_command_v_opt(prune.argv, RUN_GIT_CMD))
+			return error(FAILED_RUN, prune.argv[0]);
+	}
+
+	if (run_command_v_opt(rerere.argv, RUN_GIT_CMD))
+		return error(FAILED_RUN, rerere.argv[0]);
+
+	if (auto_gc && too_many_loose_objects())
+		warning(_("There are too many unreachable loose objects; "
+			"run 'git prune' to remove them."));
+
+	return 0;
+}
diff --git a/builtin/grep.c b/builtin/grep.c
new file mode 100644
index 0000000..29adb0a
--- /dev/null
+++ b/builtin/grep.c
@@ -0,0 +1,989 @@
+/*
+ * Builtin "git grep"
+ *
+ * Copyright (c) 2006 Junio C Hamano
+ */
+#include "cache.h"
+#include "blob.h"
+#include "tree.h"
+#include "commit.h"
+#include "tag.h"
+#include "tree-walk.h"
+#include "builtin.h"
+#include "parse-options.h"
+#include "string-list.h"
+#include "run-command.h"
+#include "userdiff.h"
+#include "grep.h"
+#include "quote.h"
+#include "dir.h"
+
+static char const * const grep_usage[] = {
+	"git grep [options] [-e] <pattern> [<rev>...] [[--] <path>...]",
+	NULL
+};
+
+static int use_threads = 1;
+
+#ifndef NO_PTHREADS
+#define THREADS 8
+static pthread_t threads[THREADS];
+
+/* We use one producer thread and THREADS consumer
+ * threads. The producer adds struct work_items to 'todo' and the
+ * consumers pick work items from the same array.
+ */
+struct work_item {
+	struct grep_source source;
+	char done;
+	struct strbuf out;
+};
+
+/* In the range [todo_done, todo_start) in 'todo' we have work_items
+ * that have been or are processed by a consumer thread. We haven't
+ * written the result for these to stdout yet.
+ *
+ * The work_items in [todo_start, todo_end) are waiting to be picked
+ * up by a consumer thread.
+ *
+ * The ranges are modulo TODO_SIZE.
+ */
+#define TODO_SIZE 128
+static struct work_item todo[TODO_SIZE];
+static int todo_start;
+static int todo_end;
+static int todo_done;
+
+/* Has all work items been added? */
+static int all_work_added;
+
+/* This lock protects all the variables above. */
+static pthread_mutex_t grep_mutex;
+
+static inline void grep_lock(void)
+{
+	if (use_threads)
+		pthread_mutex_lock(&grep_mutex);
+}
+
+static inline void grep_unlock(void)
+{
+	if (use_threads)
+		pthread_mutex_unlock(&grep_mutex);
+}
+
+/* Signalled when a new work_item is added to todo. */
+static pthread_cond_t cond_add;
+
+/* Signalled when the result from one work_item is written to
+ * stdout.
+ */
+static pthread_cond_t cond_write;
+
+/* Signalled when we are finished with everything. */
+static pthread_cond_t cond_result;
+
+static int skip_first_line;
+
+static void add_work(struct grep_opt *opt, enum grep_source_type type,
+		     const char *name, const void *id)
+{
+	grep_lock();
+
+	while ((todo_end+1) % ARRAY_SIZE(todo) == todo_done) {
+		pthread_cond_wait(&cond_write, &grep_mutex);
+	}
+
+	grep_source_init(&todo[todo_end].source, type, name, id);
+	if (opt->binary != GREP_BINARY_TEXT)
+		grep_source_load_driver(&todo[todo_end].source);
+	todo[todo_end].done = 0;
+	strbuf_reset(&todo[todo_end].out);
+	todo_end = (todo_end + 1) % ARRAY_SIZE(todo);
+
+	pthread_cond_signal(&cond_add);
+	grep_unlock();
+}
+
+static struct work_item *get_work(void)
+{
+	struct work_item *ret;
+
+	grep_lock();
+	while (todo_start == todo_end && !all_work_added) {
+		pthread_cond_wait(&cond_add, &grep_mutex);
+	}
+
+	if (todo_start == todo_end && all_work_added) {
+		ret = NULL;
+	} else {
+		ret = &todo[todo_start];
+		todo_start = (todo_start + 1) % ARRAY_SIZE(todo);
+	}
+	grep_unlock();
+	return ret;
+}
+
+static void work_done(struct work_item *w)
+{
+	int old_done;
+
+	grep_lock();
+	w->done = 1;
+	old_done = todo_done;
+	for(; todo[todo_done].done && todo_done != todo_start;
+	    todo_done = (todo_done+1) % ARRAY_SIZE(todo)) {
+		w = &todo[todo_done];
+		if (w->out.len) {
+			const char *p = w->out.buf;
+			size_t len = w->out.len;
+
+			/* Skip the leading hunk mark of the first file. */
+			if (skip_first_line) {
+				while (len) {
+					len--;
+					if (*p++ == '\n')
+						break;
+				}
+				skip_first_line = 0;
+			}
+
+			write_or_die(1, p, len);
+		}
+		grep_source_clear(&w->source);
+	}
+
+	if (old_done != todo_done)
+		pthread_cond_signal(&cond_write);
+
+	if (all_work_added && todo_done == todo_end)
+		pthread_cond_signal(&cond_result);
+
+	grep_unlock();
+}
+
+static void *run(void *arg)
+{
+	int hit = 0;
+	struct grep_opt *opt = arg;
+
+	while (1) {
+		struct work_item *w = get_work();
+		if (!w)
+			break;
+
+		opt->output_priv = w;
+		hit |= grep_source(opt, &w->source);
+		grep_source_clear_data(&w->source);
+		work_done(w);
+	}
+	free_grep_patterns(arg);
+	free(arg);
+
+	return (void*) (intptr_t) hit;
+}
+
+static void strbuf_out(struct grep_opt *opt, const void *buf, size_t size)
+{
+	struct work_item *w = opt->output_priv;
+	strbuf_add(&w->out, buf, size);
+}
+
+static void start_threads(struct grep_opt *opt)
+{
+	int i;
+
+	pthread_mutex_init(&grep_mutex, NULL);
+	pthread_mutex_init(&grep_read_mutex, NULL);
+	pthread_mutex_init(&grep_attr_mutex, NULL);
+	pthread_cond_init(&cond_add, NULL);
+	pthread_cond_init(&cond_write, NULL);
+	pthread_cond_init(&cond_result, NULL);
+	grep_use_locks = 1;
+
+	for (i = 0; i < ARRAY_SIZE(todo); i++) {
+		strbuf_init(&todo[i].out, 0);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(threads); i++) {
+		int err;
+		struct grep_opt *o = grep_opt_dup(opt);
+		o->output = strbuf_out;
+		compile_grep_patterns(o);
+		err = pthread_create(&threads[i], NULL, run, o);
+
+		if (err)
+			die(_("grep: failed to create thread: %s"),
+			    strerror(err));
+	}
+}
+
+static int wait_all(void)
+{
+	int hit = 0;
+	int i;
+
+	grep_lock();
+	all_work_added = 1;
+
+	/* Wait until all work is done. */
+	while (todo_done != todo_end)
+		pthread_cond_wait(&cond_result, &grep_mutex);
+
+	/* Wake up all the consumer threads so they can see that there
+	 * is no more work to do.
+	 */
+	pthread_cond_broadcast(&cond_add);
+	grep_unlock();
+
+	for (i = 0; i < ARRAY_SIZE(threads); i++) {
+		void *h;
+		pthread_join(threads[i], &h);
+		hit |= (int) (intptr_t) h;
+	}
+
+	pthread_mutex_destroy(&grep_mutex);
+	pthread_mutex_destroy(&grep_read_mutex);
+	pthread_mutex_destroy(&grep_attr_mutex);
+	pthread_cond_destroy(&cond_add);
+	pthread_cond_destroy(&cond_write);
+	pthread_cond_destroy(&cond_result);
+	grep_use_locks = 0;
+
+	return hit;
+}
+#else /* !NO_PTHREADS */
+
+static int wait_all(void)
+{
+	return 0;
+}
+#endif
+
+static int grep_config(const char *var, const char *value, void *cb)
+{
+	struct grep_opt *opt = cb;
+	char *color = NULL;
+
+	if (userdiff_config(var, value) < 0)
+		return -1;
+
+	if (!strcmp(var, "grep.extendedregexp")) {
+		if (git_config_bool(var, value))
+			opt->regflags |= REG_EXTENDED;
+		else
+			opt->regflags &= ~REG_EXTENDED;
+		return 0;
+	}
+
+	if (!strcmp(var, "grep.linenumber")) {
+		opt->linenum = git_config_bool(var, value);
+		return 0;
+	}
+
+	if (!strcmp(var, "color.grep"))
+		opt->color = git_config_colorbool(var, value);
+	else if (!strcmp(var, "color.grep.context"))
+		color = opt->color_context;
+	else if (!strcmp(var, "color.grep.filename"))
+		color = opt->color_filename;
+	else if (!strcmp(var, "color.grep.function"))
+		color = opt->color_function;
+	else if (!strcmp(var, "color.grep.linenumber"))
+		color = opt->color_lineno;
+	else if (!strcmp(var, "color.grep.match"))
+		color = opt->color_match;
+	else if (!strcmp(var, "color.grep.selected"))
+		color = opt->color_selected;
+	else if (!strcmp(var, "color.grep.separator"))
+		color = opt->color_sep;
+	else
+		return git_color_default_config(var, value, cb);
+	if (color) {
+		if (!value)
+			return config_error_nonbool(var);
+		color_parse(value, var, color);
+	}
+	return 0;
+}
+
+static void *lock_and_read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size)
+{
+	void *data;
+
+	grep_read_lock();
+	data = read_sha1_file(sha1, type, size);
+	grep_read_unlock();
+	return data;
+}
+
+static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1,
+		     const char *filename, int tree_name_len)
+{
+	struct strbuf pathbuf = STRBUF_INIT;
+
+	if (opt->relative && opt->prefix_length) {
+		quote_path_relative(filename + tree_name_len, -1, &pathbuf,
+				    opt->prefix);
+		strbuf_insert(&pathbuf, 0, filename, tree_name_len);
+	} else {
+		strbuf_addstr(&pathbuf, filename);
+	}
+
+#ifndef NO_PTHREADS
+	if (use_threads) {
+		add_work(opt, GREP_SOURCE_SHA1, pathbuf.buf, sha1);
+		strbuf_release(&pathbuf);
+		return 0;
+	} else
+#endif
+	{
+		struct grep_source gs;
+		int hit;
+
+		grep_source_init(&gs, GREP_SOURCE_SHA1, pathbuf.buf, sha1);
+		strbuf_release(&pathbuf);
+		hit = grep_source(opt, &gs);
+
+		grep_source_clear(&gs);
+		return hit;
+	}
+}
+
+static int grep_file(struct grep_opt *opt, const char *filename)
+{
+	struct strbuf buf = STRBUF_INIT;
+
+	if (opt->relative && opt->prefix_length)
+		quote_path_relative(filename, -1, &buf, opt->prefix);
+	else
+		strbuf_addstr(&buf, filename);
+
+#ifndef NO_PTHREADS
+	if (use_threads) {
+		add_work(opt, GREP_SOURCE_FILE, buf.buf, filename);
+		strbuf_release(&buf);
+		return 0;
+	} else
+#endif
+	{
+		struct grep_source gs;
+		int hit;
+
+		grep_source_init(&gs, GREP_SOURCE_FILE, buf.buf, filename);
+		strbuf_release(&buf);
+		hit = grep_source(opt, &gs);
+
+		grep_source_clear(&gs);
+		return hit;
+	}
+}
+
+static void append_path(struct grep_opt *opt, const void *data, size_t len)
+{
+	struct string_list *path_list = opt->output_priv;
+
+	if (len == 1 && *(const char *)data == '\0')
+		return;
+	string_list_append(path_list, xstrndup(data, len));
+}
+
+static void run_pager(struct grep_opt *opt, const char *prefix)
+{
+	struct string_list *path_list = opt->output_priv;
+	const char **argv = xmalloc(sizeof(const char *) * (path_list->nr + 1));
+	int i, status;
+
+	for (i = 0; i < path_list->nr; i++)
+		argv[i] = path_list->items[i].string;
+	argv[path_list->nr] = NULL;
+
+	if (prefix && chdir(prefix))
+		die(_("Failed to chdir: %s"), prefix);
+	status = run_command_v_opt(argv, RUN_USING_SHELL);
+	if (status)
+		exit(status);
+	free(argv);
+}
+
+static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int cached)
+{
+	int hit = 0;
+	int nr;
+	read_cache();
+
+	for (nr = 0; nr < active_nr; nr++) {
+		struct cache_entry *ce = active_cache[nr];
+		if (!S_ISREG(ce->ce_mode))
+			continue;
+		if (!match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, NULL))
+			continue;
+		/*
+		 * If CE_VALID is on, we assume worktree file and its cache entry
+		 * are identical, even if worktree file has been modified, so use
+		 * cache version instead
+		 */
+		if (cached || (ce->ce_flags & CE_VALID) || ce_skip_worktree(ce)) {
+			if (ce_stage(ce))
+				continue;
+			hit |= grep_sha1(opt, ce->sha1, ce->name, 0);
+		}
+		else
+			hit |= grep_file(opt, ce->name);
+		if (ce_stage(ce)) {
+			do {
+				nr++;
+			} while (nr < active_nr &&
+				 !strcmp(ce->name, active_cache[nr]->name));
+			nr--; /* compensate for loop control */
+		}
+		if (hit && opt->status_only)
+			break;
+	}
+	return hit;
+}
+
+static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
+		     struct tree_desc *tree, struct strbuf *base, int tn_len)
+{
+	int hit = 0;
+	enum interesting match = entry_not_interesting;
+	struct name_entry entry;
+	int old_baselen = base->len;
+
+	while (tree_entry(tree, &entry)) {
+		int te_len = tree_entry_len(&entry);
+
+		if (match != all_entries_interesting) {
+			match = tree_entry_interesting(&entry, base, tn_len, pathspec);
+			if (match == all_entries_not_interesting)
+				break;
+			if (match == entry_not_interesting)
+				continue;
+		}
+
+		strbuf_add(base, entry.path, te_len);
+
+		if (S_ISREG(entry.mode)) {
+			hit |= grep_sha1(opt, entry.sha1, base->buf, tn_len);
+		}
+		else if (S_ISDIR(entry.mode)) {
+			enum object_type type;
+			struct tree_desc sub;
+			void *data;
+			unsigned long size;
+
+			data = lock_and_read_sha1_file(entry.sha1, &type, &size);
+			if (!data)
+				die(_("unable to read tree (%s)"),
+				    sha1_to_hex(entry.sha1));
+
+			strbuf_addch(base, '/');
+			init_tree_desc(&sub, data, size);
+			hit |= grep_tree(opt, pathspec, &sub, base, tn_len);
+			free(data);
+		}
+		strbuf_setlen(base, old_baselen);
+
+		if (hit && opt->status_only)
+			break;
+	}
+	return hit;
+}
+
+static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
+		       struct object *obj, const char *name)
+{
+	if (obj->type == OBJ_BLOB)
+		return grep_sha1(opt, obj->sha1, name, 0);
+	if (obj->type == OBJ_COMMIT || obj->type == OBJ_TREE) {
+		struct tree_desc tree;
+		void *data;
+		unsigned long size;
+		struct strbuf base;
+		int hit, len;
+
+		grep_read_lock();
+		data = read_object_with_reference(obj->sha1, tree_type,
+						  &size, NULL);
+		grep_read_unlock();
+
+		if (!data)
+			die(_("unable to read tree (%s)"), sha1_to_hex(obj->sha1));
+
+		len = name ? strlen(name) : 0;
+		strbuf_init(&base, PATH_MAX + len + 1);
+		if (len) {
+			strbuf_add(&base, name, len);
+			strbuf_addch(&base, ':');
+		}
+		init_tree_desc(&tree, data, size);
+		hit = grep_tree(opt, pathspec, &tree, &base, base.len);
+		strbuf_release(&base);
+		free(data);
+		return hit;
+	}
+	die(_("unable to grep from object of type %s"), typename(obj->type));
+}
+
+static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec,
+			const struct object_array *list)
+{
+	unsigned int i;
+	int hit = 0;
+	const unsigned int nr = list->nr;
+
+	for (i = 0; i < nr; i++) {
+		struct object *real_obj;
+		real_obj = deref_tag(list->objects[i].item, NULL, 0);
+		if (grep_object(opt, pathspec, real_obj, list->objects[i].name)) {
+			hit = 1;
+			if (opt->status_only)
+				break;
+		}
+	}
+	return hit;
+}
+
+static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec,
+			  int exc_std)
+{
+	struct dir_struct dir;
+	int i, hit = 0;
+
+	memset(&dir, 0, sizeof(dir));
+	if (exc_std)
+		setup_standard_excludes(&dir);
+
+	fill_directory(&dir, pathspec->raw);
+	for (i = 0; i < dir.nr; i++) {
+		const char *name = dir.entries[i]->name;
+		int namelen = strlen(name);
+		if (!match_pathspec_depth(pathspec, name, namelen, 0, NULL))
+			continue;
+		hit |= grep_file(opt, dir.entries[i]->name);
+		if (hit && opt->status_only)
+			break;
+	}
+	return hit;
+}
+
+static int context_callback(const struct option *opt, const char *arg,
+			    int unset)
+{
+	struct grep_opt *grep_opt = opt->value;
+	int value;
+	const char *endp;
+
+	if (unset) {
+		grep_opt->pre_context = grep_opt->post_context = 0;
+		return 0;
+	}
+	value = strtol(arg, (char **)&endp, 10);
+	if (*endp) {
+		return error(_("switch `%c' expects a numerical value"),
+			     opt->short_name);
+	}
+	grep_opt->pre_context = grep_opt->post_context = value;
+	return 0;
+}
+
+static int file_callback(const struct option *opt, const char *arg, int unset)
+{
+	struct grep_opt *grep_opt = opt->value;
+	int from_stdin = !strcmp(arg, "-");
+	FILE *patterns;
+	int lno = 0;
+	struct strbuf sb = STRBUF_INIT;
+
+	patterns = from_stdin ? stdin : fopen(arg, "r");
+	if (!patterns)
+		die_errno(_("cannot open '%s'"), arg);
+	while (strbuf_getline(&sb, patterns, '\n') == 0) {
+		/* ignore empty line like grep does */
+		if (sb.len == 0)
+			continue;
+
+		append_grep_pat(grep_opt, sb.buf, sb.len, arg, ++lno,
+				GREP_PATTERN);
+	}
+	if (!from_stdin)
+		fclose(patterns);
+	strbuf_release(&sb);
+	return 0;
+}
+
+static int not_callback(const struct option *opt, const char *arg, int unset)
+{
+	struct grep_opt *grep_opt = opt->value;
+	append_grep_pattern(grep_opt, "--not", "command line", 0, GREP_NOT);
+	return 0;
+}
+
+static int and_callback(const struct option *opt, const char *arg, int unset)
+{
+	struct grep_opt *grep_opt = opt->value;
+	append_grep_pattern(grep_opt, "--and", "command line", 0, GREP_AND);
+	return 0;
+}
+
+static int open_callback(const struct option *opt, const char *arg, int unset)
+{
+	struct grep_opt *grep_opt = opt->value;
+	append_grep_pattern(grep_opt, "(", "command line", 0, GREP_OPEN_PAREN);
+	return 0;
+}
+
+static int close_callback(const struct option *opt, const char *arg, int unset)
+{
+	struct grep_opt *grep_opt = opt->value;
+	append_grep_pattern(grep_opt, ")", "command line", 0, GREP_CLOSE_PAREN);
+	return 0;
+}
+
+static int pattern_callback(const struct option *opt, const char *arg,
+			    int unset)
+{
+	struct grep_opt *grep_opt = opt->value;
+	append_grep_pattern(grep_opt, arg, "-e option", 0, GREP_PATTERN);
+	return 0;
+}
+
+static int help_callback(const struct option *opt, const char *arg, int unset)
+{
+	return -1;
+}
+
+int cmd_grep(int argc, const char **argv, const char *prefix)
+{
+	int hit = 0;
+	int cached = 0, untracked = 0, opt_exclude = -1;
+	int seen_dashdash = 0;
+	int external_grep_allowed__ignored;
+	const char *show_in_pager = NULL, *default_pager = "dummy";
+	struct grep_opt opt;
+	struct object_array list = OBJECT_ARRAY_INIT;
+	const char **paths = NULL;
+	struct pathspec pathspec;
+	struct string_list path_list = STRING_LIST_INIT_NODUP;
+	int i;
+	int dummy;
+	int use_index = 1;
+	enum {
+		pattern_type_unspecified = 0,
+		pattern_type_bre,
+		pattern_type_ere,
+		pattern_type_fixed,
+		pattern_type_pcre,
+	};
+	int pattern_type = pattern_type_unspecified;
+
+	struct option options[] = {
+		OPT_BOOLEAN(0, "cached", &cached,
+			"search in index instead of in the work tree"),
+		OPT_NEGBIT(0, "no-index", &use_index,
+			 "finds in contents not managed by git", 1),
+		OPT_BOOLEAN(0, "untracked", &untracked,
+			"search in both tracked and untracked files"),
+		OPT_SET_INT(0, "exclude-standard", &opt_exclude,
+			    "search also in ignored files", 1),
+		OPT_GROUP(""),
+		OPT_BOOLEAN('v', "invert-match", &opt.invert,
+			"show non-matching lines"),
+		OPT_BOOLEAN('i', "ignore-case", &opt.ignore_case,
+			"case insensitive matching"),
+		OPT_BOOLEAN('w', "word-regexp", &opt.word_regexp,
+			"match patterns only at word boundaries"),
+		OPT_SET_INT('a', "text", &opt.binary,
+			"process binary files as text", GREP_BINARY_TEXT),
+		OPT_SET_INT('I', NULL, &opt.binary,
+			"don't match patterns in binary files",
+			GREP_BINARY_NOMATCH),
+		{ OPTION_INTEGER, 0, "max-depth", &opt.max_depth, "depth",
+			"descend at most <depth> levels", PARSE_OPT_NONEG,
+			NULL, 1 },
+		OPT_GROUP(""),
+		OPT_SET_INT('E', "extended-regexp", &pattern_type,
+			    "use extended POSIX regular expressions",
+			    pattern_type_ere),
+		OPT_SET_INT('G', "basic-regexp", &pattern_type,
+			    "use basic POSIX regular expressions (default)",
+			    pattern_type_bre),
+		OPT_SET_INT('F', "fixed-strings", &pattern_type,
+			    "interpret patterns as fixed strings",
+			    pattern_type_fixed),
+		OPT_SET_INT('P', "perl-regexp", &pattern_type,
+			    "use Perl-compatible regular expressions",
+			    pattern_type_pcre),
+		OPT_GROUP(""),
+		OPT_BOOLEAN('n', "line-number", &opt.linenum, "show line numbers"),
+		OPT_NEGBIT('h', NULL, &opt.pathname, "don't show filenames", 1),
+		OPT_BIT('H', NULL, &opt.pathname, "show filenames", 1),
+		OPT_NEGBIT(0, "full-name", &opt.relative,
+			"show filenames relative to top directory", 1),
+		OPT_BOOLEAN('l', "files-with-matches", &opt.name_only,
+			"show only filenames instead of matching lines"),
+		OPT_BOOLEAN(0, "name-only", &opt.name_only,
+			"synonym for --files-with-matches"),
+		OPT_BOOLEAN('L', "files-without-match",
+			&opt.unmatch_name_only,
+			"show only the names of files without match"),
+		OPT_BOOLEAN('z', "null", &opt.null_following_name,
+			"print NUL after filenames"),
+		OPT_BOOLEAN('c', "count", &opt.count,
+			"show the number of matches instead of matching lines"),
+		OPT__COLOR(&opt.color, "highlight matches"),
+		OPT_BOOLEAN(0, "break", &opt.file_break,
+			"print empty line between matches from different files"),
+		OPT_BOOLEAN(0, "heading", &opt.heading,
+			"show filename only once above matches from same file"),
+		OPT_GROUP(""),
+		OPT_CALLBACK('C', "context", &opt, "n",
+			"show <n> context lines before and after matches",
+			context_callback),
+		OPT_INTEGER('B', "before-context", &opt.pre_context,
+			"show <n> context lines before matches"),
+		OPT_INTEGER('A', "after-context", &opt.post_context,
+			"show <n> context lines after matches"),
+		OPT_NUMBER_CALLBACK(&opt, "shortcut for -C NUM",
+			context_callback),
+		OPT_BOOLEAN('p', "show-function", &opt.funcname,
+			"show a line with the function name before matches"),
+		OPT_BOOLEAN('W', "function-context", &opt.funcbody,
+			"show the surrounding function"),
+		OPT_GROUP(""),
+		OPT_CALLBACK('f', NULL, &opt, "file",
+			"read patterns from file", file_callback),
+		{ OPTION_CALLBACK, 'e', NULL, &opt, "pattern",
+			"match <pattern>", PARSE_OPT_NONEG, pattern_callback },
+		{ OPTION_CALLBACK, 0, "and", &opt, NULL,
+		  "combine patterns specified with -e",
+		  PARSE_OPT_NOARG | PARSE_OPT_NONEG, and_callback },
+		OPT_BOOLEAN(0, "or", &dummy, ""),
+		{ OPTION_CALLBACK, 0, "not", &opt, NULL, "",
+		  PARSE_OPT_NOARG | PARSE_OPT_NONEG, not_callback },
+		{ OPTION_CALLBACK, '(', NULL, &opt, NULL, "",
+		  PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH,
+		  open_callback },
+		{ OPTION_CALLBACK, ')', NULL, &opt, NULL, "",
+		  PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH,
+		  close_callback },
+		OPT__QUIET(&opt.status_only,
+			   "indicate hit with exit status without output"),
+		OPT_BOOLEAN(0, "all-match", &opt.all_match,
+			"show only matches from files that match all patterns"),
+		OPT_GROUP(""),
+		{ OPTION_STRING, 'O', "open-files-in-pager", &show_in_pager,
+			"pager", "show matching files in the pager",
+			PARSE_OPT_OPTARG, NULL, (intptr_t)default_pager },
+		OPT_BOOLEAN(0, "ext-grep", &external_grep_allowed__ignored,
+			    "allow calling of grep(1) (ignored by this build)"),
+		{ OPTION_CALLBACK, 0, "help-all", &options, NULL, "show usage",
+		  PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, help_callback },
+		OPT_END()
+	};
+
+	/*
+	 * 'git grep -h', unlike 'git grep -h <pattern>', is a request
+	 * to show usage information and exit.
+	 */
+	if (argc == 2 && !strcmp(argv[1], "-h"))
+		usage_with_options(grep_usage, options);
+
+	memset(&opt, 0, sizeof(opt));
+	opt.prefix = prefix;
+	opt.prefix_length = (prefix && *prefix) ? strlen(prefix) : 0;
+	opt.relative = 1;
+	opt.pathname = 1;
+	opt.pattern_tail = &opt.pattern_list;
+	opt.header_tail = &opt.header_list;
+	opt.regflags = REG_NEWLINE;
+	opt.max_depth = -1;
+
+	strcpy(opt.color_context, "");
+	strcpy(opt.color_filename, "");
+	strcpy(opt.color_function, "");
+	strcpy(opt.color_lineno, "");
+	strcpy(opt.color_match, GIT_COLOR_BOLD_RED);
+	strcpy(opt.color_selected, "");
+	strcpy(opt.color_sep, GIT_COLOR_CYAN);
+	opt.color = -1;
+	git_config(grep_config, &opt);
+
+	/*
+	 * If there is no -- then the paths must exist in the working
+	 * tree.  If there is no explicit pattern specified with -e or
+	 * -f, we take the first unrecognized non option to be the
+	 * 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
+	 * unrecognized non option is the beginning of the refs list
+	 * that continues up to the -- (if exists), and then paths.
+	 */
+	argc = parse_options(argc, argv, prefix, options, grep_usage,
+			     PARSE_OPT_KEEP_DASHDASH |
+			     PARSE_OPT_STOP_AT_NON_OPTION |
+			     PARSE_OPT_NO_INTERNAL_HELP);
+	switch (pattern_type) {
+	case pattern_type_fixed:
+		opt.fixed = 1;
+		opt.pcre = 0;
+		break;
+	case pattern_type_bre:
+		opt.fixed = 0;
+		opt.pcre = 0;
+		opt.regflags &= ~REG_EXTENDED;
+		break;
+	case pattern_type_ere:
+		opt.fixed = 0;
+		opt.pcre = 0;
+		opt.regflags |= REG_EXTENDED;
+		break;
+	case pattern_type_pcre:
+		opt.fixed = 0;
+		opt.pcre = 1;
+		break;
+	default:
+		break; /* nothing */
+	}
+
+	if (use_index && !startup_info->have_repository)
+		/* die the same way as if we did it at the beginning */
+		setup_git_directory();
+
+	/*
+	 * skip a -- separator; we know it cannot be
+	 * separating revisions from pathnames if
+	 * we haven't even had any patterns yet
+	 */
+	if (argc > 0 && !opt.pattern_list && !strcmp(argv[0], "--")) {
+		argv++;
+		argc--;
+	}
+
+	/* First unrecognized non-option token */
+	if (argc > 0 && !opt.pattern_list) {
+		append_grep_pattern(&opt, argv[0], "command line", 0,
+				    GREP_PATTERN);
+		argv++;
+		argc--;
+	}
+
+	if (show_in_pager == default_pager)
+		show_in_pager = git_pager(1);
+	if (show_in_pager) {
+		opt.color = 0;
+		opt.name_only = 1;
+		opt.null_following_name = 1;
+		opt.output_priv = &path_list;
+		opt.output = append_path;
+		string_list_append(&path_list, show_in_pager);
+		use_threads = 0;
+	}
+
+	if (!opt.pattern_list)
+		die(_("no pattern given."));
+	if (!opt.fixed && opt.ignore_case)
+		opt.regflags |= REG_ICASE;
+
+	compile_grep_patterns(&opt);
+
+	/* Check revs and then paths */
+	for (i = 0; i < argc; i++) {
+		const char *arg = argv[i];
+		unsigned char sha1[20];
+		/* Is it a rev? */
+		if (!get_sha1(arg, sha1)) {
+			struct object *object = parse_object(sha1);
+			if (!object)
+				die(_("bad object %s"), arg);
+			add_object_array(object, arg, &list);
+			continue;
+		}
+		if (!strcmp(arg, "--")) {
+			i++;
+			seen_dashdash = 1;
+		}
+		break;
+	}
+
+#ifndef NO_PTHREADS
+	if (list.nr || cached || online_cpus() == 1)
+		use_threads = 0;
+#else
+	use_threads = 0;
+#endif
+
+#ifndef NO_PTHREADS
+	if (use_threads) {
+		if (!(opt.name_only || opt.unmatch_name_only || opt.count)
+		    && (opt.pre_context || opt.post_context ||
+			opt.file_break || opt.funcbody))
+			skip_first_line = 1;
+		start_threads(&opt);
+	}
+#endif
+
+	/* The rest are paths */
+	if (!seen_dashdash) {
+		int j;
+		for (j = i; j < argc; j++)
+			verify_filename(prefix, argv[j], j == i);
+	}
+
+	paths = get_pathspec(prefix, argv + i);
+	init_pathspec(&pathspec, paths);
+	pathspec.max_depth = opt.max_depth;
+	pathspec.recursive = 1;
+
+	if (show_in_pager && (cached || list.nr))
+		die(_("--open-files-in-pager only works on the worktree"));
+
+	if (show_in_pager && opt.pattern_list && !opt.pattern_list->next) {
+		const char *pager = path_list.items[0].string;
+		int len = strlen(pager);
+
+		if (len > 4 && is_dir_sep(pager[len - 5]))
+			pager += len - 4;
+
+		if (!strcmp("less", pager) || !strcmp("vi", pager)) {
+			struct strbuf buf = STRBUF_INIT;
+			strbuf_addf(&buf, "+/%s%s",
+					strcmp("less", pager) ? "" : "*",
+					opt.pattern_list->pattern);
+			string_list_append(&path_list, buf.buf);
+			strbuf_detach(&buf, NULL);
+		}
+	}
+
+	if (!show_in_pager)
+		setup_pager();
+
+	if (!use_index && (untracked || cached))
+		die(_("--cached or --untracked cannot be used with --no-index."));
+
+	if (!use_index || untracked) {
+		int use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude;
+		if (list.nr)
+			die(_("--no-index or --untracked cannot be used with revs."));
+		hit = grep_directory(&opt, &pathspec, use_exclude);
+	} else if (0 <= opt_exclude) {
+		die(_("--[no-]exclude-standard cannot be used for tracked contents."));
+	} else if (!list.nr) {
+		if (!cached)
+			setup_work_tree();
+
+		hit = grep_cache(&opt, &pathspec, cached);
+	} else {
+		if (cached)
+			die(_("both --cached and trees are given."));
+		hit = grep_objects(&opt, &pathspec, &list);
+	}
+
+	if (use_threads)
+		hit |= wait_all();
+	if (hit && show_in_pager)
+		run_pager(&opt, prefix);
+	free_grep_patterns(&opt);
+	return !hit;
+}
diff --git a/builtin/hash-object.c b/builtin/hash-object.c
new file mode 100644
index 0000000..33911fd
--- /dev/null
+++ b/builtin/hash-object.c
@@ -0,0 +1,137 @@
+/*
+ * GIT - The information manager from hell
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ * Copyright (C) Junio C Hamano, 2005
+ */
+#include "builtin.h"
+#include "blob.h"
+#include "quote.h"
+#include "parse-options.h"
+#include "exec_cmd.h"
+
+static void hash_fd(int fd, const char *type, int write_object, const char *path)
+{
+	struct stat st;
+	unsigned char sha1[20];
+	unsigned flags = (HASH_FORMAT_CHECK |
+			  (write_object ? HASH_WRITE_OBJECT : 0));
+
+	if (fstat(fd, &st) < 0 ||
+	    index_fd(sha1, fd, &st, type_from_string(type), path, flags))
+		die(write_object
+		    ? "Unable to add %s to database"
+		    : "Unable to hash %s", path);
+	printf("%s\n", sha1_to_hex(sha1));
+	maybe_flush_or_die(stdout, "hash to stdout");
+}
+
+static void hash_object(const char *path, const char *type, int write_object,
+			const char *vpath)
+{
+	int fd;
+	fd = open(path, O_RDONLY);
+	if (fd < 0)
+		die_errno("Cannot open '%s'", path);
+	hash_fd(fd, type, write_object, vpath);
+}
+
+static int no_filters;
+
+static void hash_stdin_paths(const char *type, int write_objects)
+{
+	struct strbuf buf = STRBUF_INIT, nbuf = STRBUF_INIT;
+
+	while (strbuf_getline(&buf, stdin, '\n') != EOF) {
+		if (buf.buf[0] == '"') {
+			strbuf_reset(&nbuf);
+			if (unquote_c_style(&nbuf, buf.buf, NULL))
+				die("line is badly quoted");
+			strbuf_swap(&buf, &nbuf);
+		}
+		hash_object(buf.buf, type, write_objects,
+		    no_filters ? NULL : buf.buf);
+	}
+	strbuf_release(&buf);
+	strbuf_release(&nbuf);
+}
+
+static const char * const hash_object_usage[] = {
+	"git hash-object [-t <type>] [-w] [--path=<file>|--no-filters] [--stdin] [--] <file>...",
+	"git hash-object  --stdin-paths < <list-of-paths>",
+	NULL
+};
+
+static const char *type;
+static int write_object;
+static int hashstdin;
+static int stdin_paths;
+static const char *vpath;
+
+static const struct option hash_object_options[] = {
+	OPT_STRING('t', NULL, &type, "type", "object type"),
+	OPT_BOOLEAN('w', NULL, &write_object, "write the object into the object database"),
+	OPT_BOOLEAN( 0 , "stdin", &hashstdin, "read the object from stdin"),
+	OPT_BOOLEAN( 0 , "stdin-paths", &stdin_paths, "read file names from stdin"),
+	OPT_BOOLEAN( 0 , "no-filters", &no_filters, "store file as is without filters"),
+	OPT_STRING( 0 , "path", &vpath, "file", "process file as it were from this path"),
+	OPT_END()
+};
+
+int cmd_hash_object(int argc, const char **argv, const char *prefix)
+{
+	int i;
+	int prefix_length = -1;
+	const char *errstr = NULL;
+
+	type = blob_type;
+
+	argc = parse_options(argc, argv, NULL, hash_object_options,
+			     hash_object_usage, 0);
+
+	if (write_object) {
+		prefix = setup_git_directory();
+		prefix_length = prefix ? strlen(prefix) : 0;
+		if (vpath && prefix)
+			vpath = prefix_filename(prefix, prefix_length, vpath);
+	}
+
+	git_config(git_default_config, NULL);
+
+	if (stdin_paths) {
+		if (hashstdin)
+			errstr = "Can't use --stdin-paths with --stdin";
+		else if (argc)
+			errstr = "Can't specify files with --stdin-paths";
+		else if (vpath)
+			errstr = "Can't use --stdin-paths with --path";
+	}
+	else {
+		if (hashstdin > 1)
+			errstr = "Multiple --stdin arguments are not supported";
+		if (vpath && no_filters)
+			errstr = "Can't use --path with --no-filters";
+	}
+
+	if (errstr) {
+		error("%s", errstr);
+		usage_with_options(hash_object_usage, hash_object_options);
+	}
+
+	if (hashstdin)
+		hash_fd(0, type, write_object, vpath);
+
+	for (i = 0 ; i < argc; i++) {
+		const char *arg = argv[i];
+
+		if (0 <= prefix_length)
+			arg = prefix_filename(prefix, prefix_length, arg);
+		hash_object(arg, type, write_object,
+			    no_filters ? NULL : vpath ? vpath : arg);
+	}
+
+	if (stdin_paths)
+		hash_stdin_paths(type, write_object);
+
+	return 0;
+}
diff --git a/builtin/help.c b/builtin/help.c
new file mode 100644
index 0000000..efea4f5
--- /dev/null
+++ b/builtin/help.c
@@ -0,0 +1,488 @@
+/*
+ * builtin-help.c
+ *
+ * Builtin help command
+ */
+#include "cache.h"
+#include "builtin.h"
+#include "exec_cmd.h"
+#include "common-cmds.h"
+#include "parse-options.h"
+#include "run-command.h"
+#include "column.h"
+#include "help.h"
+
+#ifndef DEFAULT_HELP_FORMAT
+#define DEFAULT_HELP_FORMAT "man"
+#endif
+
+static struct man_viewer_list {
+	struct man_viewer_list *next;
+	char name[FLEX_ARRAY];
+} *man_viewer_list;
+
+static struct man_viewer_info_list {
+	struct man_viewer_info_list *next;
+	const char *info;
+	char name[FLEX_ARRAY];
+} *man_viewer_info_list;
+
+enum help_format {
+	HELP_FORMAT_NONE,
+	HELP_FORMAT_MAN,
+	HELP_FORMAT_INFO,
+	HELP_FORMAT_WEB
+};
+
+static const char *html_path;
+
+static int show_all = 0;
+static unsigned int colopts;
+static enum help_format help_format = HELP_FORMAT_NONE;
+static struct option builtin_help_options[] = {
+	OPT_BOOLEAN('a', "all", &show_all, "print all available commands"),
+	OPT_SET_INT('m', "man", &help_format, "show man page", HELP_FORMAT_MAN),
+	OPT_SET_INT('w', "web", &help_format, "show manual in web browser",
+			HELP_FORMAT_WEB),
+	OPT_SET_INT('i', "info", &help_format, "show info page",
+			HELP_FORMAT_INFO),
+	OPT_END(),
+};
+
+static const char * const builtin_help_usage[] = {
+	"git help [--all] [--man|--web|--info] [command]",
+	NULL
+};
+
+static enum help_format parse_help_format(const char *format)
+{
+	if (!strcmp(format, "man"))
+		return HELP_FORMAT_MAN;
+	if (!strcmp(format, "info"))
+		return HELP_FORMAT_INFO;
+	if (!strcmp(format, "web") || !strcmp(format, "html"))
+		return HELP_FORMAT_WEB;
+	die(_("unrecognized help format '%s'"), format);
+}
+
+static const char *get_man_viewer_info(const char *name)
+{
+	struct man_viewer_info_list *viewer;
+
+	for (viewer = man_viewer_info_list; viewer; viewer = viewer->next)
+	{
+		if (!strcasecmp(name, viewer->name))
+			return viewer->info;
+	}
+	return NULL;
+}
+
+static int check_emacsclient_version(void)
+{
+	struct strbuf buffer = STRBUF_INIT;
+	struct child_process ec_process;
+	const char *argv_ec[] = { "emacsclient", "--version", NULL };
+	int version;
+
+	/* emacsclient prints its version number on stderr */
+	memset(&ec_process, 0, sizeof(ec_process));
+	ec_process.argv = argv_ec;
+	ec_process.err = -1;
+	ec_process.stdout_to_stderr = 1;
+	if (start_command(&ec_process))
+		return error(_("Failed to start emacsclient."));
+
+	strbuf_read(&buffer, ec_process.err, 20);
+	close(ec_process.err);
+
+	/*
+	 * Don't bother checking return value, because "emacsclient --version"
+	 * seems to always exits with code 1.
+	 */
+	finish_command(&ec_process);
+
+	if (prefixcmp(buffer.buf, "emacsclient")) {
+		strbuf_release(&buffer);
+		return error(_("Failed to parse emacsclient version."));
+	}
+
+	strbuf_remove(&buffer, 0, strlen("emacsclient"));
+	version = atoi(buffer.buf);
+
+	if (version < 22) {
+		strbuf_release(&buffer);
+		return error(_("emacsclient version '%d' too old (< 22)."),
+			version);
+	}
+
+	strbuf_release(&buffer);
+	return 0;
+}
+
+static void exec_woman_emacs(const char *path, const char *page)
+{
+	if (!check_emacsclient_version()) {
+		/* This works only with emacsclient version >= 22. */
+		struct strbuf man_page = STRBUF_INIT;
+
+		if (!path)
+			path = "emacsclient";
+		strbuf_addf(&man_page, "(woman \"%s\")", page);
+		execlp(path, "emacsclient", "-e", man_page.buf, (char *)NULL);
+		warning(_("failed to exec '%s': %s"), path, strerror(errno));
+	}
+}
+
+static void exec_man_konqueror(const char *path, const char *page)
+{
+	const char *display = getenv("DISPLAY");
+	if (display && *display) {
+		struct strbuf man_page = STRBUF_INIT;
+		const char *filename = "kfmclient";
+
+		/* It's simpler to launch konqueror using kfmclient. */
+		if (path) {
+			const char *file = strrchr(path, '/');
+			if (file && !strcmp(file + 1, "konqueror")) {
+				char *new = xstrdup(path);
+				char *dest = strrchr(new, '/');
+
+				/* strlen("konqueror") == strlen("kfmclient") */
+				strcpy(dest + 1, "kfmclient");
+				path = new;
+			}
+			if (file)
+				filename = file;
+		} else
+			path = "kfmclient";
+		strbuf_addf(&man_page, "man:%s(1)", page);
+		execlp(path, filename, "newTab", man_page.buf, (char *)NULL);
+		warning(_("failed to exec '%s': %s"), path, strerror(errno));
+	}
+}
+
+static void exec_man_man(const char *path, const char *page)
+{
+	if (!path)
+		path = "man";
+	execlp(path, "man", page, (char *)NULL);
+	warning(_("failed to exec '%s': %s"), path, strerror(errno));
+}
+
+static void exec_man_cmd(const char *cmd, const char *page)
+{
+	struct strbuf shell_cmd = STRBUF_INIT;
+	strbuf_addf(&shell_cmd, "%s %s", cmd, page);
+	execl("/bin/sh", "sh", "-c", shell_cmd.buf, (char *)NULL);
+	warning(_("failed to exec '%s': %s"), cmd, strerror(errno));
+}
+
+static void add_man_viewer(const char *name)
+{
+	struct man_viewer_list **p = &man_viewer_list;
+	size_t len = strlen(name);
+
+	while (*p)
+		p = &((*p)->next);
+	*p = xcalloc(1, (sizeof(**p) + len + 1));
+	strncpy((*p)->name, name, len);
+}
+
+static int supported_man_viewer(const char *name, size_t len)
+{
+	return (!strncasecmp("man", name, len) ||
+		!strncasecmp("woman", name, len) ||
+		!strncasecmp("konqueror", name, len));
+}
+
+static void do_add_man_viewer_info(const char *name,
+				   size_t len,
+				   const char *value)
+{
+	struct man_viewer_info_list *new = xcalloc(1, sizeof(*new) + len + 1);
+
+	strncpy(new->name, name, len);
+	new->info = xstrdup(value);
+	new->next = man_viewer_info_list;
+	man_viewer_info_list = new;
+}
+
+static int add_man_viewer_path(const char *name,
+			       size_t len,
+			       const char *value)
+{
+	if (supported_man_viewer(name, len))
+		do_add_man_viewer_info(name, len, value);
+	else
+		warning(_("'%s': path for unsupported man viewer.\n"
+			  "Please consider using 'man.<tool>.cmd' instead."),
+			name);
+
+	return 0;
+}
+
+static int add_man_viewer_cmd(const char *name,
+			      size_t len,
+			      const char *value)
+{
+	if (supported_man_viewer(name, len))
+		warning(_("'%s': cmd for supported man viewer.\n"
+			  "Please consider using 'man.<tool>.path' instead."),
+			name);
+	else
+		do_add_man_viewer_info(name, len, value);
+
+	return 0;
+}
+
+static int add_man_viewer_info(const char *var, const char *value)
+{
+	const char *name = var + 4;
+	const char *subkey = strrchr(name, '.');
+
+	if (!subkey)
+		return 0;
+
+	if (!strcmp(subkey, ".path")) {
+		if (!value)
+			return config_error_nonbool(var);
+		return add_man_viewer_path(name, subkey - name, value);
+	}
+	if (!strcmp(subkey, ".cmd")) {
+		if (!value)
+			return config_error_nonbool(var);
+		return add_man_viewer_cmd(name, subkey - name, value);
+	}
+
+	return 0;
+}
+
+static int git_help_config(const char *var, const char *value, void *cb)
+{
+	if (!prefixcmp(var, "column."))
+		return git_column_config(var, value, "help", &colopts);
+	if (!strcmp(var, "help.format")) {
+		if (!value)
+			return config_error_nonbool(var);
+		help_format = parse_help_format(value);
+		return 0;
+	}
+	if (!strcmp(var, "help.htmlpath")) {
+		if (!value)
+			return config_error_nonbool(var);
+		html_path = xstrdup(value);
+		return 0;
+	}
+	if (!strcmp(var, "man.viewer")) {
+		if (!value)
+			return config_error_nonbool(var);
+		add_man_viewer(value);
+		return 0;
+	}
+	if (!prefixcmp(var, "man."))
+		return add_man_viewer_info(var, value);
+
+	return git_default_config(var, value, cb);
+}
+
+static struct cmdnames main_cmds, other_cmds;
+
+void list_common_cmds_help(void)
+{
+	int i, longest = 0;
+
+	for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
+		if (longest < strlen(common_cmds[i].name))
+			longest = strlen(common_cmds[i].name);
+	}
+
+	puts(_("The most commonly used git commands are:"));
+	for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
+		printf("   %s   ", common_cmds[i].name);
+		mput_char(' ', longest - strlen(common_cmds[i].name));
+		puts(_(common_cmds[i].help));
+	}
+}
+
+static int is_git_command(const char *s)
+{
+	return is_in_cmdlist(&main_cmds, s) ||
+		is_in_cmdlist(&other_cmds, s);
+}
+
+static const char *prepend(const char *prefix, const char *cmd)
+{
+	size_t pre_len = strlen(prefix);
+	size_t cmd_len = strlen(cmd);
+	char *p = xmalloc(pre_len + cmd_len + 1);
+	memcpy(p, prefix, pre_len);
+	strcpy(p + pre_len, cmd);
+	return p;
+}
+
+static const char *cmd_to_page(const char *git_cmd)
+{
+	if (!git_cmd)
+		return "git";
+	else if (!prefixcmp(git_cmd, "git"))
+		return git_cmd;
+	else if (is_git_command(git_cmd))
+		return prepend("git-", git_cmd);
+	else
+		return prepend("git", git_cmd);
+}
+
+static void setup_man_path(void)
+{
+	struct strbuf new_path = STRBUF_INIT;
+	const char *old_path = getenv("MANPATH");
+
+	/* We should always put ':' after our path. If there is no
+	 * old_path, the ':' at the end will let 'man' to try
+	 * system-wide paths after ours to find the manual page. If
+	 * there is old_path, we need ':' as delimiter. */
+	strbuf_addstr(&new_path, system_path(GIT_MAN_PATH));
+	strbuf_addch(&new_path, ':');
+	if (old_path)
+		strbuf_addstr(&new_path, old_path);
+
+	setenv("MANPATH", new_path.buf, 1);
+
+	strbuf_release(&new_path);
+}
+
+static void exec_viewer(const char *name, const char *page)
+{
+	const char *info = get_man_viewer_info(name);
+
+	if (!strcasecmp(name, "man"))
+		exec_man_man(info, page);
+	else if (!strcasecmp(name, "woman"))
+		exec_woman_emacs(info, page);
+	else if (!strcasecmp(name, "konqueror"))
+		exec_man_konqueror(info, page);
+	else if (info)
+		exec_man_cmd(info, page);
+	else
+		warning(_("'%s': unknown man viewer."), name);
+}
+
+static void show_man_page(const char *git_cmd)
+{
+	struct man_viewer_list *viewer;
+	const char *page = cmd_to_page(git_cmd);
+	const char *fallback = getenv("GIT_MAN_VIEWER");
+
+	setup_man_path();
+	for (viewer = man_viewer_list; viewer; viewer = viewer->next)
+	{
+		exec_viewer(viewer->name, page); /* will return when unable */
+	}
+	if (fallback)
+		exec_viewer(fallback, page);
+	exec_viewer("man", page);
+	die(_("no man viewer handled the request"));
+}
+
+static void show_info_page(const char *git_cmd)
+{
+	const char *page = cmd_to_page(git_cmd);
+	setenv("INFOPATH", system_path(GIT_INFO_PATH), 1);
+	execlp("info", "info", "gitman", page, (char *)NULL);
+	die(_("no info viewer handled the request"));
+}
+
+static void get_html_page_path(struct strbuf *page_path, const char *page)
+{
+	struct stat st;
+	if (!html_path)
+		html_path = system_path(GIT_HTML_PATH);
+
+	/* Check that we have a git documentation directory. */
+	if (!strstr(html_path, "://")) {
+		if (stat(mkpath("%s/git.html", html_path), &st)
+		    || !S_ISREG(st.st_mode))
+			die("'%s': not a documentation directory.", html_path);
+	}
+
+	strbuf_init(page_path, 0);
+	strbuf_addf(page_path, "%s/%s.html", html_path, page);
+}
+
+/*
+ * If open_html is not defined in a platform-specific way (see for
+ * example compat/mingw.h), we use the script web--browse to display
+ * HTML.
+ */
+#ifndef open_html
+static void open_html(const char *path)
+{
+	execl_git_cmd("web--browse", "-c", "help.browser", path, (char *)NULL);
+}
+#endif
+
+static void show_html_page(const char *git_cmd)
+{
+	const char *page = cmd_to_page(git_cmd);
+	struct strbuf page_path; /* it leaks but we exec bellow */
+
+	get_html_page_path(&page_path, page);
+
+	open_html(page_path.buf);
+}
+
+int cmd_help(int argc, const char **argv, const char *prefix)
+{
+	int nongit;
+	const char *alias;
+	enum help_format parsed_help_format;
+	load_command_list("git-", &main_cmds, &other_cmds);
+
+	argc = parse_options(argc, argv, prefix, builtin_help_options,
+			builtin_help_usage, 0);
+	parsed_help_format = help_format;
+
+	if (show_all) {
+		git_config(git_help_config, NULL);
+		printf(_("usage: %s%s"), _(git_usage_string), "\n\n");
+		list_commands(colopts, &main_cmds, &other_cmds);
+		printf("%s\n", _(git_more_info_string));
+		return 0;
+	}
+
+	if (!argv[0]) {
+		printf(_("usage: %s%s"), _(git_usage_string), "\n\n");
+		list_common_cmds_help();
+		printf("\n%s\n", _(git_more_info_string));
+		return 0;
+	}
+
+	setup_git_directory_gently(&nongit);
+	git_config(git_help_config, NULL);
+
+	if (parsed_help_format != HELP_FORMAT_NONE)
+		help_format = parsed_help_format;
+	if (help_format == HELP_FORMAT_NONE)
+		help_format = parse_help_format(DEFAULT_HELP_FORMAT);
+
+	alias = alias_lookup(argv[0]);
+	if (alias && !is_git_command(argv[0])) {
+		printf_ln(_("`git %s' is aliased to `%s'"), argv[0], alias);
+		return 0;
+	}
+
+	switch (help_format) {
+	case HELP_FORMAT_NONE:
+	case HELP_FORMAT_MAN:
+		show_man_page(argv[0]);
+		break;
+	case HELP_FORMAT_INFO:
+		show_info_page(argv[0]);
+		break;
+	case HELP_FORMAT_WEB:
+		show_html_page(argv[0]);
+		break;
+	}
+
+	return 0;
+}
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
new file mode 100644
index 0000000..953dd30
--- /dev/null
+++ b/builtin/index-pack.c
@@ -0,0 +1,1633 @@
+#include "builtin.h"
+#include "delta.h"
+#include "pack.h"
+#include "csum-file.h"
+#include "blob.h"
+#include "commit.h"
+#include "tag.h"
+#include "tree.h"
+#include "progress.h"
+#include "fsck.h"
+#include "exec_cmd.h"
+#include "streaming.h"
+#include "thread-utils.h"
+
+static const char index_pack_usage[] =
+"git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--verify] [--strict] (<pack-file> | --stdin [--fix-thin] [<pack-file>])";
+
+struct object_entry {
+	struct pack_idx_entry idx;
+	unsigned long size;
+	unsigned int hdr_size;
+	enum object_type type;
+	enum object_type real_type;
+	unsigned delta_depth;
+	int base_object_no;
+};
+
+union delta_base {
+	unsigned char sha1[20];
+	off_t offset;
+};
+
+struct base_data {
+	struct base_data *base;
+	struct base_data *child;
+	struct object_entry *obj;
+	void *data;
+	unsigned long size;
+	int ref_first, ref_last;
+	int ofs_first, ofs_last;
+};
+
+#if !defined(NO_PTHREADS) && defined(NO_THREAD_SAFE_PREAD)
+/* pread() emulation is not thread-safe. Disable threading. */
+#define NO_PTHREADS
+#endif
+
+struct thread_local {
+#ifndef NO_PTHREADS
+	pthread_t thread;
+#endif
+	struct base_data *base_cache;
+	size_t base_cache_used;
+};
+
+/*
+ * Even if sizeof(union delta_base) == 24 on 64-bit archs, we really want
+ * to memcmp() only the first 20 bytes.
+ */
+#define UNION_BASE_SZ	20
+
+#define FLAG_LINK (1u<<20)
+#define FLAG_CHECKED (1u<<21)
+
+struct delta_entry {
+	union delta_base base;
+	int obj_no;
+};
+
+static struct object_entry *objects;
+static struct delta_entry *deltas;
+static struct thread_local nothread_data;
+static int nr_objects;
+static int nr_deltas;
+static int nr_resolved_deltas;
+static int nr_threads;
+
+static int from_stdin;
+static int strict;
+static int verbose;
+
+static struct progress *progress;
+
+/* We always read in 4kB chunks. */
+static unsigned char input_buffer[4096];
+static unsigned int input_offset, input_len;
+static off_t consumed_bytes;
+static unsigned deepest_delta;
+static git_SHA_CTX input_ctx;
+static uint32_t input_crc32;
+static int input_fd, output_fd, pack_fd;
+
+#ifndef NO_PTHREADS
+
+static struct thread_local *thread_data;
+static int nr_dispatched;
+static int threads_active;
+
+static pthread_mutex_t read_mutex;
+#define read_lock()		lock_mutex(&read_mutex)
+#define read_unlock()		unlock_mutex(&read_mutex)
+
+static pthread_mutex_t counter_mutex;
+#define counter_lock()		lock_mutex(&counter_mutex)
+#define counter_unlock()	unlock_mutex(&counter_mutex)
+
+static pthread_mutex_t work_mutex;
+#define work_lock()		lock_mutex(&work_mutex)
+#define work_unlock()		unlock_mutex(&work_mutex)
+
+static pthread_key_t key;
+
+static inline void lock_mutex(pthread_mutex_t *mutex)
+{
+	if (threads_active)
+		pthread_mutex_lock(mutex);
+}
+
+static inline void unlock_mutex(pthread_mutex_t *mutex)
+{
+	if (threads_active)
+		pthread_mutex_unlock(mutex);
+}
+
+/*
+ * Mutex and conditional variable can't be statically-initialized on Windows.
+ */
+static void init_thread(void)
+{
+	init_recursive_mutex(&read_mutex);
+	pthread_mutex_init(&counter_mutex, NULL);
+	pthread_mutex_init(&work_mutex, NULL);
+	pthread_key_create(&key, NULL);
+	thread_data = xcalloc(nr_threads, sizeof(*thread_data));
+	threads_active = 1;
+}
+
+static void cleanup_thread(void)
+{
+	if (!threads_active)
+		return;
+	threads_active = 0;
+	pthread_mutex_destroy(&read_mutex);
+	pthread_mutex_destroy(&counter_mutex);
+	pthread_mutex_destroy(&work_mutex);
+	pthread_key_delete(key);
+	free(thread_data);
+}
+
+#else
+
+#define read_lock()
+#define read_unlock()
+
+#define counter_lock()
+#define counter_unlock()
+
+#define work_lock()
+#define work_unlock()
+
+#endif
+
+
+static int mark_link(struct object *obj, int type, void *data)
+{
+	if (!obj)
+		return -1;
+
+	if (type != OBJ_ANY && obj->type != type)
+		die(_("object type mismatch at %s"), sha1_to_hex(obj->sha1));
+
+	obj->flags |= FLAG_LINK;
+	return 0;
+}
+
+/* The content of each linked object must have been checked
+   or it must be already present in the object database */
+static void check_object(struct object *obj)
+{
+	if (!obj)
+		return;
+
+	if (!(obj->flags & FLAG_LINK))
+		return;
+
+	if (!(obj->flags & FLAG_CHECKED)) {
+		unsigned long size;
+		int type = sha1_object_info(obj->sha1, &size);
+		if (type != obj->type || type <= 0)
+			die(_("object of unexpected type"));
+		obj->flags |= FLAG_CHECKED;
+		return;
+	}
+}
+
+static void check_objects(void)
+{
+	unsigned i, max;
+
+	max = get_max_object_index();
+	for (i = 0; i < max; i++)
+		check_object(get_indexed_object(i));
+}
+
+
+/* Discard current buffer used content. */
+static void flush(void)
+{
+	if (input_offset) {
+		if (output_fd >= 0)
+			write_or_die(output_fd, input_buffer, input_offset);
+		git_SHA1_Update(&input_ctx, input_buffer, input_offset);
+		memmove(input_buffer, input_buffer + input_offset, input_len);
+		input_offset = 0;
+	}
+}
+
+/*
+ * Make sure at least "min" bytes are available in the buffer, and
+ * return the pointer to the buffer.
+ */
+static void *fill(int min)
+{
+	if (min <= input_len)
+		return input_buffer + input_offset;
+	if (min > sizeof(input_buffer))
+		die(Q_("cannot fill %d byte",
+		       "cannot fill %d bytes",
+		       min),
+		    min);
+	flush();
+	do {
+		ssize_t ret = xread(input_fd, input_buffer + input_len,
+				sizeof(input_buffer) - input_len);
+		if (ret <= 0) {
+			if (!ret)
+				die(_("early EOF"));
+			die_errno(_("read error on input"));
+		}
+		input_len += ret;
+		if (from_stdin)
+			display_throughput(progress, consumed_bytes + input_len);
+	} while (input_len < min);
+	return input_buffer;
+}
+
+static void use(int bytes)
+{
+	if (bytes > input_len)
+		die(_("used more bytes than were available"));
+	input_crc32 = crc32(input_crc32, input_buffer + input_offset, bytes);
+	input_len -= bytes;
+	input_offset += bytes;
+
+	/* make sure off_t is sufficiently large not to wrap */
+	if (signed_add_overflows(consumed_bytes, bytes))
+		die(_("pack too large for current definition of off_t"));
+	consumed_bytes += bytes;
+}
+
+static const char *open_pack_file(const char *pack_name)
+{
+	if (from_stdin) {
+		input_fd = 0;
+		if (!pack_name) {
+			static char tmp_file[PATH_MAX];
+			output_fd = odb_mkstemp(tmp_file, sizeof(tmp_file),
+						"pack/tmp_pack_XXXXXX");
+			pack_name = xstrdup(tmp_file);
+		} else
+			output_fd = open(pack_name, O_CREAT|O_EXCL|O_RDWR, 0600);
+		if (output_fd < 0)
+			die_errno(_("unable to create '%s'"), pack_name);
+		pack_fd = output_fd;
+	} else {
+		input_fd = open(pack_name, O_RDONLY);
+		if (input_fd < 0)
+			die_errno(_("cannot open packfile '%s'"), pack_name);
+		output_fd = -1;
+		pack_fd = input_fd;
+	}
+	git_SHA1_Init(&input_ctx);
+	return pack_name;
+}
+
+static void parse_pack_header(void)
+{
+	struct pack_header *hdr = fill(sizeof(struct pack_header));
+
+	/* Header consistency check */
+	if (hdr->hdr_signature != htonl(PACK_SIGNATURE))
+		die(_("pack signature mismatch"));
+	if (!pack_version_ok(hdr->hdr_version))
+		die("pack version %"PRIu32" unsupported",
+			ntohl(hdr->hdr_version));
+
+	nr_objects = ntohl(hdr->hdr_entries);
+	use(sizeof(struct pack_header));
+}
+
+static NORETURN void bad_object(unsigned long offset, const char *format,
+		       ...) __attribute__((format (printf, 2, 3)));
+
+static NORETURN void bad_object(unsigned long offset, const char *format, ...)
+{
+	va_list params;
+	char buf[1024];
+
+	va_start(params, format);
+	vsnprintf(buf, sizeof(buf), format, params);
+	va_end(params);
+	die(_("pack has bad object at offset %lu: %s"), offset, buf);
+}
+
+static inline struct thread_local *get_thread_data(void)
+{
+#ifndef NO_PTHREADS
+	if (threads_active)
+		return pthread_getspecific(key);
+	assert(!threads_active &&
+	       "This should only be reached when all threads are gone");
+#endif
+	return &nothread_data;
+}
+
+#ifndef NO_PTHREADS
+static void set_thread_data(struct thread_local *data)
+{
+	if (threads_active)
+		pthread_setspecific(key, data);
+}
+#endif
+
+static struct base_data *alloc_base_data(void)
+{
+	struct base_data *base = xmalloc(sizeof(struct base_data));
+	memset(base, 0, sizeof(*base));
+	base->ref_last = -1;
+	base->ofs_last = -1;
+	return base;
+}
+
+static void free_base_data(struct base_data *c)
+{
+	if (c->data) {
+		free(c->data);
+		c->data = NULL;
+		get_thread_data()->base_cache_used -= c->size;
+	}
+}
+
+static void prune_base_data(struct base_data *retain)
+{
+	struct base_data *b;
+	struct thread_local *data = get_thread_data();
+	for (b = data->base_cache;
+	     data->base_cache_used > delta_base_cache_limit && b;
+	     b = b->child) {
+		if (b->data && b != retain)
+			free_base_data(b);
+	}
+}
+
+static void link_base_data(struct base_data *base, struct base_data *c)
+{
+	if (base)
+		base->child = c;
+	else
+		get_thread_data()->base_cache = c;
+
+	c->base = base;
+	c->child = NULL;
+	if (c->data)
+		get_thread_data()->base_cache_used += c->size;
+	prune_base_data(c);
+}
+
+static void unlink_base_data(struct base_data *c)
+{
+	struct base_data *base = c->base;
+	if (base)
+		base->child = NULL;
+	else
+		get_thread_data()->base_cache = NULL;
+	free_base_data(c);
+}
+
+static int is_delta_type(enum object_type type)
+{
+	return (type == OBJ_REF_DELTA || type == OBJ_OFS_DELTA);
+}
+
+static void *unpack_entry_data(unsigned long offset, unsigned long size,
+			       enum object_type type, unsigned char *sha1)
+{
+	static char fixed_buf[8192];
+	int status;
+	git_zstream stream;
+	void *buf;
+	git_SHA_CTX c;
+	char hdr[32];
+	int hdrlen;
+
+	if (!is_delta_type(type)) {
+		hdrlen = sprintf(hdr, "%s %lu", typename(type), size) + 1;
+		git_SHA1_Init(&c);
+		git_SHA1_Update(&c, hdr, hdrlen);
+	} else
+		sha1 = NULL;
+	if (type == OBJ_BLOB && size > big_file_threshold)
+		buf = fixed_buf;
+	else
+		buf = xmalloc(size);
+
+	memset(&stream, 0, sizeof(stream));
+	git_inflate_init(&stream);
+	stream.next_out = buf;
+	stream.avail_out = buf == fixed_buf ? sizeof(fixed_buf) : size;
+
+	do {
+		unsigned char *last_out = stream.next_out;
+		stream.next_in = fill(1);
+		stream.avail_in = input_len;
+		status = git_inflate(&stream, 0);
+		use(input_len - stream.avail_in);
+		if (sha1)
+			git_SHA1_Update(&c, last_out, stream.next_out - last_out);
+		if (buf == fixed_buf) {
+			stream.next_out = buf;
+			stream.avail_out = sizeof(fixed_buf);
+		}
+	} while (status == Z_OK);
+	if (stream.total_out != size || status != Z_STREAM_END)
+		bad_object(offset, _("inflate returned %d"), status);
+	git_inflate_end(&stream);
+	if (sha1)
+		git_SHA1_Final(sha1, &c);
+	return buf == fixed_buf ? NULL : buf;
+}
+
+static void *unpack_raw_entry(struct object_entry *obj,
+			      union delta_base *delta_base,
+			      unsigned char *sha1)
+{
+	unsigned char *p;
+	unsigned long size, c;
+	off_t base_offset;
+	unsigned shift;
+	void *data;
+
+	obj->idx.offset = consumed_bytes;
+	input_crc32 = crc32(0, NULL, 0);
+
+	p = fill(1);
+	c = *p;
+	use(1);
+	obj->type = (c >> 4) & 7;
+	size = (c & 15);
+	shift = 4;
+	while (c & 0x80) {
+		p = fill(1);
+		c = *p;
+		use(1);
+		size += (c & 0x7f) << shift;
+		shift += 7;
+	}
+	obj->size = size;
+
+	switch (obj->type) {
+	case OBJ_REF_DELTA:
+		hashcpy(delta_base->sha1, fill(20));
+		use(20);
+		break;
+	case OBJ_OFS_DELTA:
+		memset(delta_base, 0, sizeof(*delta_base));
+		p = fill(1);
+		c = *p;
+		use(1);
+		base_offset = c & 127;
+		while (c & 128) {
+			base_offset += 1;
+			if (!base_offset || MSB(base_offset, 7))
+				bad_object(obj->idx.offset, _("offset value overflow for delta base object"));
+			p = fill(1);
+			c = *p;
+			use(1);
+			base_offset = (base_offset << 7) + (c & 127);
+		}
+		delta_base->offset = obj->idx.offset - base_offset;
+		if (delta_base->offset <= 0 || delta_base->offset >= obj->idx.offset)
+			bad_object(obj->idx.offset, _("delta base offset is out of bound"));
+		break;
+	case OBJ_COMMIT:
+	case OBJ_TREE:
+	case OBJ_BLOB:
+	case OBJ_TAG:
+		break;
+	default:
+		bad_object(obj->idx.offset, _("unknown object type %d"), obj->type);
+	}
+	obj->hdr_size = consumed_bytes - obj->idx.offset;
+
+	data = unpack_entry_data(obj->idx.offset, obj->size, obj->type, sha1);
+	obj->idx.crc32 = input_crc32;
+	return data;
+}
+
+static void *unpack_data(struct object_entry *obj,
+			 int (*consume)(const unsigned char *, unsigned long, void *),
+			 void *cb_data)
+{
+	off_t from = obj[0].idx.offset + obj[0].hdr_size;
+	unsigned long len = obj[1].idx.offset - from;
+	unsigned char *data, *inbuf;
+	git_zstream stream;
+	int status;
+
+	data = xmalloc(consume ? 64*1024 : obj->size);
+	inbuf = xmalloc((len < 64*1024) ? len : 64*1024);
+
+	memset(&stream, 0, sizeof(stream));
+	git_inflate_init(&stream);
+	stream.next_out = data;
+	stream.avail_out = consume ? 64*1024 : obj->size;
+
+	do {
+		ssize_t n = (len < 64*1024) ? len : 64*1024;
+		n = pread(pack_fd, inbuf, n, from);
+		if (n < 0)
+			die_errno(_("cannot pread pack file"));
+		if (!n)
+			die(Q_("premature end of pack file, %lu byte missing",
+			       "premature end of pack file, %lu bytes missing",
+			       len),
+			    len);
+		from += n;
+		len -= n;
+		stream.next_in = inbuf;
+		stream.avail_in = n;
+		if (!consume)
+			status = git_inflate(&stream, 0);
+		else {
+			do {
+				status = git_inflate(&stream, 0);
+				if (consume(data, stream.next_out - data, cb_data)) {
+					free(inbuf);
+					free(data);
+					return NULL;
+				}
+				stream.next_out = data;
+				stream.avail_out = 64*1024;
+			} while (status == Z_OK && stream.avail_in);
+		}
+	} while (len && status == Z_OK && !stream.avail_in);
+
+	/* This has been inflated OK when first encountered, so... */
+	if (status != Z_STREAM_END || stream.total_out != obj->size)
+		die(_("serious inflate inconsistency"));
+
+	git_inflate_end(&stream);
+	free(inbuf);
+	if (consume) {
+		free(data);
+		data = NULL;
+	}
+	return data;
+}
+
+static void *get_data_from_pack(struct object_entry *obj)
+{
+	return unpack_data(obj, NULL, NULL);
+}
+
+static int compare_delta_bases(const union delta_base *base1,
+			       const union delta_base *base2,
+			       enum object_type type1,
+			       enum object_type type2)
+{
+	int cmp = type1 - type2;
+	if (cmp)
+		return cmp;
+	return memcmp(base1, base2, UNION_BASE_SZ);
+}
+
+static int find_delta(const union delta_base *base, enum object_type type)
+{
+	int first = 0, last = nr_deltas;
+
+        while (first < last) {
+                int next = (first + last) / 2;
+                struct delta_entry *delta = &deltas[next];
+                int cmp;
+
+		cmp = compare_delta_bases(base, &delta->base,
+					  type, objects[delta->obj_no].type);
+                if (!cmp)
+                        return next;
+                if (cmp < 0) {
+                        last = next;
+                        continue;
+                }
+                first = next+1;
+        }
+        return -first-1;
+}
+
+static void find_delta_children(const union delta_base *base,
+				int *first_index, int *last_index,
+				enum object_type type)
+{
+	int first = find_delta(base, type);
+	int last = first;
+	int end = nr_deltas - 1;
+
+	if (first < 0) {
+		*first_index = 0;
+		*last_index = -1;
+		return;
+	}
+	while (first > 0 && !memcmp(&deltas[first - 1].base, base, UNION_BASE_SZ))
+		--first;
+	while (last < end && !memcmp(&deltas[last + 1].base, base, UNION_BASE_SZ))
+		++last;
+	*first_index = first;
+	*last_index = last;
+}
+
+struct compare_data {
+	struct object_entry *entry;
+	struct git_istream *st;
+	unsigned char *buf;
+	unsigned long buf_size;
+};
+
+static int compare_objects(const unsigned char *buf, unsigned long size,
+			   void *cb_data)
+{
+	struct compare_data *data = cb_data;
+
+	if (data->buf_size < size) {
+		free(data->buf);
+		data->buf = xmalloc(size);
+		data->buf_size = size;
+	}
+
+	while (size) {
+		ssize_t len = read_istream(data->st, data->buf, size);
+		if (len == 0)
+			die(_("SHA1 COLLISION FOUND WITH %s !"),
+			    sha1_to_hex(data->entry->idx.sha1));
+		if (len < 0)
+			die(_("unable to read %s"),
+			    sha1_to_hex(data->entry->idx.sha1));
+		if (memcmp(buf, data->buf, len))
+			die(_("SHA1 COLLISION FOUND WITH %s !"),
+			    sha1_to_hex(data->entry->idx.sha1));
+		size -= len;
+		buf += len;
+	}
+	return 0;
+}
+
+static int check_collison(struct object_entry *entry)
+{
+	struct compare_data data;
+	enum object_type type;
+	unsigned long size;
+
+	if (entry->size <= big_file_threshold || entry->type != OBJ_BLOB)
+		return -1;
+
+	memset(&data, 0, sizeof(data));
+	data.entry = entry;
+	data.st = open_istream(entry->idx.sha1, &type, &size, NULL);
+	if (!data.st)
+		return -1;
+	if (size != entry->size || type != entry->type)
+		die(_("SHA1 COLLISION FOUND WITH %s !"),
+		    sha1_to_hex(entry->idx.sha1));
+	unpack_data(entry, compare_objects, &data);
+	close_istream(data.st);
+	free(data.buf);
+	return 0;
+}
+
+static void sha1_object(const void *data, struct object_entry *obj_entry,
+			unsigned long size, enum object_type type,
+			const unsigned char *sha1)
+{
+	void *new_data = NULL;
+	int collision_test_needed;
+
+	assert(data || obj_entry);
+
+	read_lock();
+	collision_test_needed = has_sha1_file(sha1);
+	read_unlock();
+
+	if (collision_test_needed && !data) {
+		read_lock();
+		if (!check_collison(obj_entry))
+			collision_test_needed = 0;
+		read_unlock();
+	}
+	if (collision_test_needed) {
+		void *has_data;
+		enum object_type has_type;
+		unsigned long has_size;
+		read_lock();
+		has_type = sha1_object_info(sha1, &has_size);
+		if (has_type != type || has_size != size)
+			die(_("SHA1 COLLISION FOUND WITH %s !"), sha1_to_hex(sha1));
+		has_data = read_sha1_file(sha1, &has_type, &has_size);
+		read_unlock();
+		if (!data)
+			data = new_data = get_data_from_pack(obj_entry);
+		if (!has_data)
+			die(_("cannot read existing object %s"), sha1_to_hex(sha1));
+		if (size != has_size || type != has_type ||
+		    memcmp(data, has_data, size) != 0)
+			die(_("SHA1 COLLISION FOUND WITH %s !"), sha1_to_hex(sha1));
+		free(has_data);
+	}
+
+	if (strict) {
+		read_lock();
+		if (type == OBJ_BLOB) {
+			struct blob *blob = lookup_blob(sha1);
+			if (blob)
+				blob->object.flags |= FLAG_CHECKED;
+			else
+				die(_("invalid blob object %s"), sha1_to_hex(sha1));
+		} else {
+			struct object *obj;
+			int eaten;
+			void *buf = (void *) data;
+
+			if (!buf)
+				buf = new_data = get_data_from_pack(obj_entry);
+
+			/*
+			 * we do not need to free the memory here, as the
+			 * buf is deleted by the caller.
+			 */
+			obj = parse_object_buffer(sha1, type, size, buf, &eaten);
+			if (!obj)
+				die(_("invalid %s"), typename(type));
+			if (fsck_object(obj, 1, fsck_error_function))
+				die(_("Error in object"));
+			if (fsck_walk(obj, mark_link, NULL))
+				die(_("Not all child objects of %s are reachable"), sha1_to_hex(obj->sha1));
+
+			if (obj->type == OBJ_TREE) {
+				struct tree *item = (struct tree *) obj;
+				item->buffer = NULL;
+			}
+			if (obj->type == OBJ_COMMIT) {
+				struct commit *commit = (struct commit *) obj;
+				commit->buffer = NULL;
+			}
+			obj->flags |= FLAG_CHECKED;
+		}
+		read_unlock();
+	}
+
+	free(new_data);
+}
+
+/*
+ * This function is part of find_unresolved_deltas(). There are two
+ * walkers going in the opposite ways.
+ *
+ * The first one in find_unresolved_deltas() traverses down from
+ * parent node to children, deflating nodes along the way. However,
+ * memory for deflated nodes is limited by delta_base_cache_limit, so
+ * at some point parent node's deflated content may be freed.
+ *
+ * The second walker is this function, which goes from current node up
+ * to top parent if necessary to deflate the node. In normal
+ * situation, its parent node would be already deflated, so it just
+ * needs to apply delta.
+ *
+ * In the worst case scenario, parent node is no longer deflated because
+ * we're running out of delta_base_cache_limit; we need to re-deflate
+ * parents, possibly up to the top base.
+ *
+ * All deflated objects here are subject to be freed if we exceed
+ * delta_base_cache_limit, just like in find_unresolved_deltas(), we
+ * just need to make sure the last node is not freed.
+ */
+static void *get_base_data(struct base_data *c)
+{
+	if (!c->data) {
+		struct object_entry *obj = c->obj;
+		struct base_data **delta = NULL;
+		int delta_nr = 0, delta_alloc = 0;
+
+		while (is_delta_type(c->obj->type) && !c->data) {
+			ALLOC_GROW(delta, delta_nr + 1, delta_alloc);
+			delta[delta_nr++] = c;
+			c = c->base;
+		}
+		if (!delta_nr) {
+			c->data = get_data_from_pack(obj);
+			c->size = obj->size;
+			get_thread_data()->base_cache_used += c->size;
+			prune_base_data(c);
+		}
+		for (; delta_nr > 0; delta_nr--) {
+			void *base, *raw;
+			c = delta[delta_nr - 1];
+			obj = c->obj;
+			base = get_base_data(c->base);
+			raw = get_data_from_pack(obj);
+			c->data = patch_delta(
+				base, c->base->size,
+				raw, obj->size,
+				&c->size);
+			free(raw);
+			if (!c->data)
+				bad_object(obj->idx.offset, _("failed to apply delta"));
+			get_thread_data()->base_cache_used += c->size;
+			prune_base_data(c);
+		}
+		free(delta);
+	}
+	return c->data;
+}
+
+static void resolve_delta(struct object_entry *delta_obj,
+			  struct base_data *base, struct base_data *result)
+{
+	void *base_data, *delta_data;
+
+	delta_obj->real_type = base->obj->real_type;
+	delta_obj->delta_depth = base->obj->delta_depth + 1;
+	if (deepest_delta < delta_obj->delta_depth)
+		deepest_delta = delta_obj->delta_depth;
+	delta_obj->base_object_no = base->obj - objects;
+	delta_data = get_data_from_pack(delta_obj);
+	base_data = get_base_data(base);
+	result->obj = delta_obj;
+	result->data = patch_delta(base_data, base->size,
+				   delta_data, delta_obj->size, &result->size);
+	free(delta_data);
+	if (!result->data)
+		bad_object(delta_obj->idx.offset, _("failed to apply delta"));
+	hash_sha1_file(result->data, result->size,
+		       typename(delta_obj->real_type), delta_obj->idx.sha1);
+	sha1_object(result->data, NULL, result->size, delta_obj->real_type,
+		    delta_obj->idx.sha1);
+	counter_lock();
+	nr_resolved_deltas++;
+	counter_unlock();
+}
+
+static struct base_data *find_unresolved_deltas_1(struct base_data *base,
+						  struct base_data *prev_base)
+{
+	if (base->ref_last == -1 && base->ofs_last == -1) {
+		union delta_base base_spec;
+
+		hashcpy(base_spec.sha1, base->obj->idx.sha1);
+		find_delta_children(&base_spec,
+				    &base->ref_first, &base->ref_last, OBJ_REF_DELTA);
+
+		memset(&base_spec, 0, sizeof(base_spec));
+		base_spec.offset = base->obj->idx.offset;
+		find_delta_children(&base_spec,
+				    &base->ofs_first, &base->ofs_last, OBJ_OFS_DELTA);
+
+		if (base->ref_last == -1 && base->ofs_last == -1) {
+			free(base->data);
+			return NULL;
+		}
+
+		link_base_data(prev_base, base);
+	}
+
+	if (base->ref_first <= base->ref_last) {
+		struct object_entry *child = objects + deltas[base->ref_first].obj_no;
+		struct base_data *result = alloc_base_data();
+
+		assert(child->real_type == OBJ_REF_DELTA);
+		resolve_delta(child, base, result);
+		if (base->ref_first == base->ref_last && base->ofs_last == -1)
+			free_base_data(base);
+
+		base->ref_first++;
+		return result;
+	}
+
+	if (base->ofs_first <= base->ofs_last) {
+		struct object_entry *child = objects + deltas[base->ofs_first].obj_no;
+		struct base_data *result = alloc_base_data();
+
+		assert(child->real_type == OBJ_OFS_DELTA);
+		resolve_delta(child, base, result);
+		if (base->ofs_first == base->ofs_last)
+			free_base_data(base);
+
+		base->ofs_first++;
+		return result;
+	}
+
+	unlink_base_data(base);
+	return NULL;
+}
+
+static void find_unresolved_deltas(struct base_data *base)
+{
+	struct base_data *new_base, *prev_base = NULL;
+	for (;;) {
+		new_base = find_unresolved_deltas_1(base, prev_base);
+
+		if (new_base) {
+			prev_base = base;
+			base = new_base;
+		} else {
+			free(base);
+			base = prev_base;
+			if (!base)
+				return;
+			prev_base = base->base;
+		}
+	}
+}
+
+static int compare_delta_entry(const void *a, const void *b)
+{
+	const struct delta_entry *delta_a = a;
+	const struct delta_entry *delta_b = b;
+
+	/* group by type (ref vs ofs) and then by value (sha-1 or offset) */
+	return compare_delta_bases(&delta_a->base, &delta_b->base,
+				   objects[delta_a->obj_no].type,
+				   objects[delta_b->obj_no].type);
+}
+
+static void resolve_base(struct object_entry *obj)
+{
+	struct base_data *base_obj = alloc_base_data();
+	base_obj->obj = obj;
+	base_obj->data = NULL;
+	find_unresolved_deltas(base_obj);
+}
+
+#ifndef NO_PTHREADS
+static void *threaded_second_pass(void *data)
+{
+	set_thread_data(data);
+	for (;;) {
+		int i;
+		work_lock();
+		display_progress(progress, nr_resolved_deltas);
+		while (nr_dispatched < nr_objects &&
+		       is_delta_type(objects[nr_dispatched].type))
+			nr_dispatched++;
+		if (nr_dispatched >= nr_objects) {
+			work_unlock();
+			break;
+		}
+		i = nr_dispatched++;
+		work_unlock();
+
+		resolve_base(&objects[i]);
+	}
+	return NULL;
+}
+#endif
+
+/*
+ * First pass:
+ * - find locations of all objects;
+ * - calculate SHA1 of all non-delta objects;
+ * - remember base (SHA1 or offset) for all deltas.
+ */
+static void parse_pack_objects(unsigned char *sha1)
+{
+	int i, nr_delays = 0;
+	struct delta_entry *delta = deltas;
+	struct stat st;
+
+	if (verbose)
+		progress = start_progress(
+				from_stdin ? _("Receiving objects") : _("Indexing objects"),
+				nr_objects);
+	for (i = 0; i < nr_objects; i++) {
+		struct object_entry *obj = &objects[i];
+		void *data = unpack_raw_entry(obj, &delta->base, obj->idx.sha1);
+		obj->real_type = obj->type;
+		if (is_delta_type(obj->type)) {
+			nr_deltas++;
+			delta->obj_no = i;
+			delta++;
+		} else if (!data) {
+			/* large blobs, check later */
+			obj->real_type = OBJ_BAD;
+			nr_delays++;
+		} else
+			sha1_object(data, NULL, obj->size, obj->type, obj->idx.sha1);
+		free(data);
+		display_progress(progress, i+1);
+	}
+	objects[i].idx.offset = consumed_bytes;
+	stop_progress(&progress);
+
+	/* Check pack integrity */
+	flush();
+	git_SHA1_Final(sha1, &input_ctx);
+	if (hashcmp(fill(20), sha1))
+		die(_("pack is corrupted (SHA1 mismatch)"));
+	use(20);
+
+	/* If input_fd is a file, we should have reached its end now. */
+	if (fstat(input_fd, &st))
+		die_errno(_("cannot fstat packfile"));
+	if (S_ISREG(st.st_mode) &&
+			lseek(input_fd, 0, SEEK_CUR) - input_len != st.st_size)
+		die(_("pack has junk at the end"));
+
+	for (i = 0; i < nr_objects; i++) {
+		struct object_entry *obj = &objects[i];
+		if (obj->real_type != OBJ_BAD)
+			continue;
+		obj->real_type = obj->type;
+		sha1_object(NULL, obj, obj->size, obj->type, obj->idx.sha1);
+		nr_delays--;
+	}
+	if (nr_delays)
+		die(_("confusion beyond insanity in parse_pack_objects()"));
+}
+
+/*
+ * Second pass:
+ * - for all non-delta objects, look if it is used as a base for
+ *   deltas;
+ * - if used as a base, uncompress the object and apply all deltas,
+ *   recursively checking if the resulting object is used as a base
+ *   for some more deltas.
+ */
+static void resolve_deltas(void)
+{
+	int i;
+
+	if (!nr_deltas)
+		return;
+
+	/* Sort deltas by base SHA1/offset for fast searching */
+	qsort(deltas, nr_deltas, sizeof(struct delta_entry),
+	      compare_delta_entry);
+
+	if (verbose)
+		progress = start_progress(_("Resolving deltas"), nr_deltas);
+
+#ifndef NO_PTHREADS
+	nr_dispatched = 0;
+	if (nr_threads > 1 || getenv("GIT_FORCE_THREADS")) {
+		init_thread();
+		for (i = 0; i < nr_threads; i++) {
+			int ret = pthread_create(&thread_data[i].thread, NULL,
+						 threaded_second_pass, thread_data + i);
+			if (ret)
+				die("unable to create thread: %s", strerror(ret));
+		}
+		for (i = 0; i < nr_threads; i++)
+			pthread_join(thread_data[i].thread, NULL);
+		cleanup_thread();
+		return;
+	}
+#endif
+
+	for (i = 0; i < nr_objects; i++) {
+		struct object_entry *obj = &objects[i];
+
+		if (is_delta_type(obj->type))
+			continue;
+		resolve_base(obj);
+		display_progress(progress, nr_resolved_deltas);
+	}
+}
+
+/*
+ * Third pass:
+ * - append objects to convert thin pack to full pack if required
+ * - write the final 20-byte SHA-1
+ */
+static void fix_unresolved_deltas(struct sha1file *f, int nr_unresolved);
+static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned char *pack_sha1)
+{
+	if (nr_deltas == nr_resolved_deltas) {
+		stop_progress(&progress);
+		/* Flush remaining pack final 20-byte SHA1. */
+		flush();
+		return;
+	}
+
+	if (fix_thin_pack) {
+		struct sha1file *f;
+		unsigned char read_sha1[20], tail_sha1[20];
+		char msg[48];
+		int nr_unresolved = nr_deltas - nr_resolved_deltas;
+		int nr_objects_initial = nr_objects;
+		if (nr_unresolved <= 0)
+			die(_("confusion beyond insanity"));
+		objects = xrealloc(objects,
+				   (nr_objects + nr_unresolved + 1)
+				   * sizeof(*objects));
+		f = sha1fd(output_fd, curr_pack);
+		fix_unresolved_deltas(f, nr_unresolved);
+		sprintf(msg, "completed with %d local objects",
+			nr_objects - nr_objects_initial);
+		stop_progress_msg(&progress, msg);
+		sha1close(f, tail_sha1, 0);
+		hashcpy(read_sha1, pack_sha1);
+		fixup_pack_header_footer(output_fd, pack_sha1,
+					 curr_pack, nr_objects,
+					 read_sha1, consumed_bytes-20);
+		if (hashcmp(read_sha1, tail_sha1) != 0)
+			die("Unexpected tail checksum for %s "
+			    "(disk corruption?)", curr_pack);
+	}
+	if (nr_deltas != nr_resolved_deltas)
+		die(Q_("pack has %d unresolved delta",
+		       "pack has %d unresolved deltas",
+		       nr_deltas - nr_resolved_deltas),
+		    nr_deltas - nr_resolved_deltas);
+}
+
+static int write_compressed(struct sha1file *f, void *in, unsigned int size)
+{
+	git_zstream stream;
+	int status;
+	unsigned char outbuf[4096];
+
+	memset(&stream, 0, sizeof(stream));
+	git_deflate_init(&stream, zlib_compression_level);
+	stream.next_in = in;
+	stream.avail_in = size;
+
+	do {
+		stream.next_out = outbuf;
+		stream.avail_out = sizeof(outbuf);
+		status = git_deflate(&stream, Z_FINISH);
+		sha1write(f, outbuf, sizeof(outbuf) - stream.avail_out);
+	} while (status == Z_OK);
+
+	if (status != Z_STREAM_END)
+		die(_("unable to deflate appended object (%d)"), status);
+	size = stream.total_out;
+	git_deflate_end(&stream);
+	return size;
+}
+
+static struct object_entry *append_obj_to_pack(struct sha1file *f,
+			       const unsigned char *sha1, void *buf,
+			       unsigned long size, enum object_type type)
+{
+	struct object_entry *obj = &objects[nr_objects++];
+	unsigned char header[10];
+	unsigned long s = size;
+	int n = 0;
+	unsigned char c = (type << 4) | (s & 15);
+	s >>= 4;
+	while (s) {
+		header[n++] = c | 0x80;
+		c = s & 0x7f;
+		s >>= 7;
+	}
+	header[n++] = c;
+	crc32_begin(f);
+	sha1write(f, header, n);
+	obj[0].size = size;
+	obj[0].hdr_size = n;
+	obj[0].type = type;
+	obj[0].real_type = type;
+	obj[1].idx.offset = obj[0].idx.offset + n;
+	obj[1].idx.offset += write_compressed(f, buf, size);
+	obj[0].idx.crc32 = crc32_end(f);
+	sha1flush(f);
+	hashcpy(obj->idx.sha1, sha1);
+	return obj;
+}
+
+static int delta_pos_compare(const void *_a, const void *_b)
+{
+	struct delta_entry *a = *(struct delta_entry **)_a;
+	struct delta_entry *b = *(struct delta_entry **)_b;
+	return a->obj_no - b->obj_no;
+}
+
+static void fix_unresolved_deltas(struct sha1file *f, int nr_unresolved)
+{
+	struct delta_entry **sorted_by_pos;
+	int i, n = 0;
+
+	/*
+	 * Since many unresolved deltas may well be themselves base objects
+	 * for more unresolved deltas, we really want to include the
+	 * smallest number of base objects that would cover as much delta
+	 * as possible by picking the
+	 * trunc deltas first, allowing for other deltas to resolve without
+	 * additional base objects.  Since most base objects are to be found
+	 * before deltas depending on them, a good heuristic is to start
+	 * resolving deltas in the same order as their position in the pack.
+	 */
+	sorted_by_pos = xmalloc(nr_unresolved * sizeof(*sorted_by_pos));
+	for (i = 0; i < nr_deltas; i++) {
+		if (objects[deltas[i].obj_no].real_type != OBJ_REF_DELTA)
+			continue;
+		sorted_by_pos[n++] = &deltas[i];
+	}
+	qsort(sorted_by_pos, n, sizeof(*sorted_by_pos), delta_pos_compare);
+
+	for (i = 0; i < n; i++) {
+		struct delta_entry *d = sorted_by_pos[i];
+		enum object_type type;
+		struct base_data *base_obj = alloc_base_data();
+
+		if (objects[d->obj_no].real_type != OBJ_REF_DELTA)
+			continue;
+		base_obj->data = read_sha1_file(d->base.sha1, &type, &base_obj->size);
+		if (!base_obj->data)
+			continue;
+
+		if (check_sha1_signature(d->base.sha1, base_obj->data,
+				base_obj->size, typename(type)))
+			die(_("local object %s is corrupt"), sha1_to_hex(d->base.sha1));
+		base_obj->obj = append_obj_to_pack(f, d->base.sha1,
+					base_obj->data, base_obj->size, type);
+		find_unresolved_deltas(base_obj);
+		display_progress(progress, nr_resolved_deltas);
+	}
+	free(sorted_by_pos);
+}
+
+static void final(const char *final_pack_name, const char *curr_pack_name,
+		  const char *final_index_name, const char *curr_index_name,
+		  const char *keep_name, const char *keep_msg,
+		  unsigned char *sha1)
+{
+	const char *report = "pack";
+	char name[PATH_MAX];
+	int err;
+
+	if (!from_stdin) {
+		close(input_fd);
+	} else {
+		fsync_or_die(output_fd, curr_pack_name);
+		err = close(output_fd);
+		if (err)
+			die_errno(_("error while closing pack file"));
+	}
+
+	if (keep_msg) {
+		int keep_fd, keep_msg_len = strlen(keep_msg);
+
+		if (!keep_name)
+			keep_fd = odb_pack_keep(name, sizeof(name), sha1);
+		else
+			keep_fd = open(keep_name, O_RDWR|O_CREAT|O_EXCL, 0600);
+
+		if (keep_fd < 0) {
+			if (errno != EEXIST)
+				die_errno(_("cannot write keep file '%s'"),
+					  keep_name);
+		} else {
+			if (keep_msg_len > 0) {
+				write_or_die(keep_fd, keep_msg, keep_msg_len);
+				write_or_die(keep_fd, "\n", 1);
+			}
+			if (close(keep_fd) != 0)
+				die_errno(_("cannot close written keep file '%s'"),
+				    keep_name);
+			report = "keep";
+		}
+	}
+
+	if (final_pack_name != curr_pack_name) {
+		if (!final_pack_name) {
+			snprintf(name, sizeof(name), "%s/pack/pack-%s.pack",
+				 get_object_directory(), sha1_to_hex(sha1));
+			final_pack_name = name;
+		}
+		if (move_temp_to_file(curr_pack_name, final_pack_name))
+			die(_("cannot store pack file"));
+	} else if (from_stdin)
+		chmod(final_pack_name, 0444);
+
+	if (final_index_name != curr_index_name) {
+		if (!final_index_name) {
+			snprintf(name, sizeof(name), "%s/pack/pack-%s.idx",
+				 get_object_directory(), sha1_to_hex(sha1));
+			final_index_name = name;
+		}
+		if (move_temp_to_file(curr_index_name, final_index_name))
+			die(_("cannot store index file"));
+	} else
+		chmod(final_index_name, 0444);
+
+	if (!from_stdin) {
+		printf("%s\n", sha1_to_hex(sha1));
+	} else {
+		char buf[48];
+		int len = snprintf(buf, sizeof(buf), "%s\t%s\n",
+				   report, sha1_to_hex(sha1));
+		write_or_die(1, buf, len);
+
+		/*
+		 * Let's just mimic git-unpack-objects here and write
+		 * the last part of the input buffer to stdout.
+		 */
+		while (input_len) {
+			err = xwrite(1, input_buffer + input_offset, input_len);
+			if (err <= 0)
+				break;
+			input_len -= err;
+			input_offset += err;
+		}
+	}
+}
+
+static int git_index_pack_config(const char *k, const char *v, void *cb)
+{
+	struct pack_idx_option *opts = cb;
+
+	if (!strcmp(k, "pack.indexversion")) {
+		opts->version = git_config_int(k, v);
+		if (opts->version > 2)
+			die("bad pack.indexversion=%"PRIu32, opts->version);
+		return 0;
+	}
+	if (!strcmp(k, "pack.threads")) {
+		nr_threads = git_config_int(k, v);
+		if (nr_threads < 0)
+			die("invalid number of threads specified (%d)",
+			    nr_threads);
+#ifdef NO_PTHREADS
+		if (nr_threads != 1)
+			warning("no threads support, ignoring %s", k);
+		nr_threads = 1;
+#endif
+		return 0;
+	}
+	return git_default_config(k, v, cb);
+}
+
+static int cmp_uint32(const void *a_, const void *b_)
+{
+	uint32_t a = *((uint32_t *)a_);
+	uint32_t b = *((uint32_t *)b_);
+
+	return (a < b) ? -1 : (a != b);
+}
+
+static void read_v2_anomalous_offsets(struct packed_git *p,
+				      struct pack_idx_option *opts)
+{
+	const uint32_t *idx1, *idx2;
+	uint32_t i;
+
+	/* The address of the 4-byte offset table */
+	idx1 = (((const uint32_t *)p->index_data)
+		+ 2 /* 8-byte header */
+		+ 256 /* fan out */
+		+ 5 * p->num_objects /* 20-byte SHA-1 table */
+		+ p->num_objects /* CRC32 table */
+		);
+
+	/* The address of the 8-byte offset table */
+	idx2 = idx1 + p->num_objects;
+
+	for (i = 0; i < p->num_objects; i++) {
+		uint32_t off = ntohl(idx1[i]);
+		if (!(off & 0x80000000))
+			continue;
+		off = off & 0x7fffffff;
+		if (idx2[off * 2])
+			continue;
+		/*
+		 * The real offset is ntohl(idx2[off * 2]) in high 4
+		 * octets, and ntohl(idx2[off * 2 + 1]) in low 4
+		 * octets.  But idx2[off * 2] is Zero!!!
+		 */
+		ALLOC_GROW(opts->anomaly, opts->anomaly_nr + 1, opts->anomaly_alloc);
+		opts->anomaly[opts->anomaly_nr++] = ntohl(idx2[off * 2 + 1]);
+	}
+
+	if (1 < opts->anomaly_nr)
+		qsort(opts->anomaly, opts->anomaly_nr, sizeof(uint32_t), cmp_uint32);
+}
+
+static void read_idx_option(struct pack_idx_option *opts, const char *pack_name)
+{
+	struct packed_git *p = add_packed_git(pack_name, strlen(pack_name), 1);
+
+	if (!p)
+		die(_("Cannot open existing pack file '%s'"), pack_name);
+	if (open_pack_index(p))
+		die(_("Cannot open existing pack idx file for '%s'"), pack_name);
+
+	/* Read the attributes from the existing idx file */
+	opts->version = p->index_version;
+
+	if (opts->version == 2)
+		read_v2_anomalous_offsets(p, opts);
+
+	/*
+	 * Get rid of the idx file as we do not need it anymore.
+	 * NEEDSWORK: extract this bit from free_pack_by_name() in
+	 * sha1_file.c, perhaps?  It shouldn't matter very much as we
+	 * know we haven't installed this pack (hence we never have
+	 * read anything from it).
+	 */
+	close_pack_index(p);
+	free(p);
+}
+
+static void show_pack_info(int stat_only)
+{
+	int i, baseobjects = nr_objects - nr_deltas;
+	unsigned long *chain_histogram = NULL;
+
+	if (deepest_delta)
+		chain_histogram = xcalloc(deepest_delta, sizeof(unsigned long));
+
+	for (i = 0; i < nr_objects; i++) {
+		struct object_entry *obj = &objects[i];
+
+		if (is_delta_type(obj->type))
+			chain_histogram[obj->delta_depth - 1]++;
+		if (stat_only)
+			continue;
+		printf("%s %-6s %lu %lu %"PRIuMAX,
+		       sha1_to_hex(obj->idx.sha1),
+		       typename(obj->real_type), obj->size,
+		       (unsigned long)(obj[1].idx.offset - obj->idx.offset),
+		       (uintmax_t)obj->idx.offset);
+		if (is_delta_type(obj->type)) {
+			struct object_entry *bobj = &objects[obj->base_object_no];
+			printf(" %u %s", obj->delta_depth, sha1_to_hex(bobj->idx.sha1));
+		}
+		putchar('\n');
+	}
+
+	if (baseobjects)
+		printf_ln(Q_("non delta: %d object",
+			     "non delta: %d objects",
+			     baseobjects),
+			  baseobjects);
+	for (i = 0; i < deepest_delta; i++) {
+		if (!chain_histogram[i])
+			continue;
+		printf_ln(Q_("chain length = %d: %lu object",
+			     "chain length = %d: %lu objects",
+			     chain_histogram[i]),
+			  i + 1,
+			  chain_histogram[i]);
+	}
+}
+
+int cmd_index_pack(int argc, const char **argv, const char *prefix)
+{
+	int i, fix_thin_pack = 0, verify = 0, stat_only = 0, stat = 0;
+	const char *curr_pack, *curr_index;
+	const char *index_name = NULL, *pack_name = NULL;
+	const char *keep_name = NULL, *keep_msg = NULL;
+	char *index_name_buf = NULL, *keep_name_buf = NULL;
+	struct pack_idx_entry **idx_objects;
+	struct pack_idx_option opts;
+	unsigned char pack_sha1[20];
+
+	if (argc == 2 && !strcmp(argv[1], "-h"))
+		usage(index_pack_usage);
+
+	read_replace_refs = 0;
+
+	reset_pack_idx_option(&opts);
+	git_config(git_index_pack_config, &opts);
+	if (prefix && chdir(prefix))
+		die(_("Cannot come back to cwd"));
+
+	for (i = 1; i < argc; i++) {
+		const char *arg = argv[i];
+
+		if (*arg == '-') {
+			if (!strcmp(arg, "--stdin")) {
+				from_stdin = 1;
+			} else if (!strcmp(arg, "--fix-thin")) {
+				fix_thin_pack = 1;
+			} else if (!strcmp(arg, "--strict")) {
+				strict = 1;
+			} else if (!strcmp(arg, "--verify")) {
+				verify = 1;
+			} else if (!strcmp(arg, "--verify-stat")) {
+				verify = 1;
+				stat = 1;
+			} else if (!strcmp(arg, "--verify-stat-only")) {
+				verify = 1;
+				stat = 1;
+				stat_only = 1;
+			} else if (!strcmp(arg, "--keep")) {
+				keep_msg = "";
+			} else if (!prefixcmp(arg, "--keep=")) {
+				keep_msg = arg + 7;
+			} else if (!prefixcmp(arg, "--threads=")) {
+				char *end;
+				nr_threads = strtoul(arg+10, &end, 0);
+				if (!arg[10] || *end || nr_threads < 0)
+					usage(index_pack_usage);
+#ifdef NO_PTHREADS
+				if (nr_threads != 1)
+					warning("no threads support, "
+						"ignoring %s", arg);
+				nr_threads = 1;
+#endif
+			} else if (!prefixcmp(arg, "--pack_header=")) {
+				struct pack_header *hdr;
+				char *c;
+
+				hdr = (struct pack_header *)input_buffer;
+				hdr->hdr_signature = htonl(PACK_SIGNATURE);
+				hdr->hdr_version = htonl(strtoul(arg + 14, &c, 10));
+				if (*c != ',')
+					die(_("bad %s"), arg);
+				hdr->hdr_entries = htonl(strtoul(c + 1, &c, 10));
+				if (*c)
+					die(_("bad %s"), arg);
+				input_len = sizeof(*hdr);
+			} else if (!strcmp(arg, "-v")) {
+				verbose = 1;
+			} else if (!strcmp(arg, "-o")) {
+				if (index_name || (i+1) >= argc)
+					usage(index_pack_usage);
+				index_name = argv[++i];
+			} else if (!prefixcmp(arg, "--index-version=")) {
+				char *c;
+				opts.version = strtoul(arg + 16, &c, 10);
+				if (opts.version > 2)
+					die(_("bad %s"), arg);
+				if (*c == ',')
+					opts.off32_limit = strtoul(c+1, &c, 0);
+				if (*c || opts.off32_limit & 0x80000000)
+					die(_("bad %s"), arg);
+			} else
+				usage(index_pack_usage);
+			continue;
+		}
+
+		if (pack_name)
+			usage(index_pack_usage);
+		pack_name = arg;
+	}
+
+	if (!pack_name && !from_stdin)
+		usage(index_pack_usage);
+	if (fix_thin_pack && !from_stdin)
+		die(_("--fix-thin cannot be used without --stdin"));
+	if (!index_name && pack_name) {
+		int len = strlen(pack_name);
+		if (!has_extension(pack_name, ".pack"))
+			die(_("packfile name '%s' does not end with '.pack'"),
+			    pack_name);
+		index_name_buf = xmalloc(len);
+		memcpy(index_name_buf, pack_name, len - 5);
+		strcpy(index_name_buf + len - 5, ".idx");
+		index_name = index_name_buf;
+	}
+	if (keep_msg && !keep_name && pack_name) {
+		int len = strlen(pack_name);
+		if (!has_extension(pack_name, ".pack"))
+			die(_("packfile name '%s' does not end with '.pack'"),
+			    pack_name);
+		keep_name_buf = xmalloc(len);
+		memcpy(keep_name_buf, pack_name, len - 5);
+		strcpy(keep_name_buf + len - 5, ".keep");
+		keep_name = keep_name_buf;
+	}
+	if (verify) {
+		if (!index_name)
+			die(_("--verify with no packfile name given"));
+		read_idx_option(&opts, index_name);
+		opts.flags |= WRITE_IDX_VERIFY | WRITE_IDX_STRICT;
+	}
+	if (strict)
+		opts.flags |= WRITE_IDX_STRICT;
+
+#ifndef NO_PTHREADS
+	if (!nr_threads) {
+		nr_threads = online_cpus();
+		/* An experiment showed that more threads does not mean faster */
+		if (nr_threads > 3)
+			nr_threads = 3;
+	}
+#endif
+
+	curr_pack = open_pack_file(pack_name);
+	parse_pack_header();
+	objects = xcalloc(nr_objects + 1, sizeof(struct object_entry));
+	deltas = xcalloc(nr_objects, sizeof(struct delta_entry));
+	parse_pack_objects(pack_sha1);
+	resolve_deltas();
+	conclude_pack(fix_thin_pack, curr_pack, pack_sha1);
+	free(deltas);
+	if (strict)
+		check_objects();
+
+	if (stat)
+		show_pack_info(stat_only);
+
+	idx_objects = xmalloc((nr_objects) * sizeof(struct pack_idx_entry *));
+	for (i = 0; i < nr_objects; i++)
+		idx_objects[i] = &objects[i].idx;
+	curr_index = write_idx_file(index_name, idx_objects, nr_objects, &opts, pack_sha1);
+	free(idx_objects);
+
+	if (!verify)
+		final(pack_name, curr_pack,
+		      index_name, curr_index,
+		      keep_name, keep_msg,
+		      pack_sha1);
+	else
+		close(input_fd);
+	free(objects);
+	free(index_name_buf);
+	free(keep_name_buf);
+	if (pack_name == NULL)
+		free((void *) curr_pack);
+	if (index_name == NULL)
+		free((void *) curr_index);
+
+	return 0;
+}
diff --git a/builtin/init-db.c b/builtin/init-db.c
new file mode 100644
index 0000000..244fb7f
--- /dev/null
+++ b/builtin/init-db.c
@@ -0,0 +1,597 @@
+/*
+ * GIT - The information manager from hell
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ */
+#include "cache.h"
+#include "builtin.h"
+#include "exec_cmd.h"
+#include "parse-options.h"
+
+#ifndef DEFAULT_GIT_TEMPLATE_DIR
+#define DEFAULT_GIT_TEMPLATE_DIR "/usr/share/git-core/templates"
+#endif
+
+#ifdef NO_TRUSTABLE_FILEMODE
+#define TEST_FILEMODE 0
+#else
+#define TEST_FILEMODE 1
+#endif
+
+static int init_is_bare_repository = 0;
+static int init_shared_repository = -1;
+static const char *init_db_template_dir;
+static const char *git_link;
+
+static void safe_create_dir(const char *dir, int share)
+{
+	if (mkdir(dir, 0777) < 0) {
+		if (errno != EEXIST) {
+			perror(dir);
+			exit(1);
+		}
+	}
+	else if (share && adjust_shared_perm(dir))
+		die(_("Could not make %s writable by group"), dir);
+}
+
+static void copy_templates_1(char *path, int baselen,
+			     char *template, int template_baselen,
+			     DIR *dir)
+{
+	struct dirent *de;
+
+	/* Note: if ".git/hooks" file exists in the repository being
+	 * re-initialized, /etc/core-git/templates/hooks/update would
+	 * cause "git init" to fail here.  I think this is sane but
+	 * it means that the set of templates we ship by default, along
+	 * with the way the namespace under .git/ is organized, should
+	 * be really carefully chosen.
+	 */
+	safe_create_dir(path, 1);
+	while ((de = readdir(dir)) != NULL) {
+		struct stat st_git, st_template;
+		int namelen;
+		int exists = 0;
+
+		if (de->d_name[0] == '.')
+			continue;
+		namelen = strlen(de->d_name);
+		if ((PATH_MAX <= baselen + namelen) ||
+		    (PATH_MAX <= template_baselen + namelen))
+			die(_("insanely long template name %s"), de->d_name);
+		memcpy(path + baselen, de->d_name, namelen+1);
+		memcpy(template + template_baselen, de->d_name, namelen+1);
+		if (lstat(path, &st_git)) {
+			if (errno != ENOENT)
+				die_errno(_("cannot stat '%s'"), path);
+		}
+		else
+			exists = 1;
+
+		if (lstat(template, &st_template))
+			die_errno(_("cannot stat template '%s'"), template);
+
+		if (S_ISDIR(st_template.st_mode)) {
+			DIR *subdir = opendir(template);
+			int baselen_sub = baselen + namelen;
+			int template_baselen_sub = template_baselen + namelen;
+			if (!subdir)
+				die_errno(_("cannot opendir '%s'"), template);
+			path[baselen_sub++] =
+				template[template_baselen_sub++] = '/';
+			path[baselen_sub] =
+				template[template_baselen_sub] = 0;
+			copy_templates_1(path, baselen_sub,
+					 template, template_baselen_sub,
+					 subdir);
+			closedir(subdir);
+		}
+		else if (exists)
+			continue;
+		else if (S_ISLNK(st_template.st_mode)) {
+			char lnk[256];
+			int len;
+			len = readlink(template, lnk, sizeof(lnk));
+			if (len < 0)
+				die_errno(_("cannot readlink '%s'"), template);
+			if (sizeof(lnk) <= len)
+				die(_("insanely long symlink %s"), template);
+			lnk[len] = 0;
+			if (symlink(lnk, path))
+				die_errno(_("cannot symlink '%s' '%s'"), lnk, path);
+		}
+		else if (S_ISREG(st_template.st_mode)) {
+			if (copy_file(path, template, st_template.st_mode))
+				die_errno(_("cannot copy '%s' to '%s'"), template,
+					  path);
+		}
+		else
+			error(_("ignoring template %s"), template);
+	}
+}
+
+static void copy_templates(const char *template_dir)
+{
+	char path[PATH_MAX];
+	char template_path[PATH_MAX];
+	int template_len;
+	DIR *dir;
+	const char *git_dir = get_git_dir();
+	int len = strlen(git_dir);
+
+	if (!template_dir)
+		template_dir = getenv(TEMPLATE_DIR_ENVIRONMENT);
+	if (!template_dir)
+		template_dir = init_db_template_dir;
+	if (!template_dir)
+		template_dir = system_path(DEFAULT_GIT_TEMPLATE_DIR);
+	if (!template_dir[0])
+		return;
+	template_len = strlen(template_dir);
+	if (PATH_MAX <= (template_len+strlen("/config")))
+		die(_("insanely long template path %s"), template_dir);
+	strcpy(template_path, template_dir);
+	if (template_path[template_len-1] != '/') {
+		template_path[template_len++] = '/';
+		template_path[template_len] = 0;
+	}
+	dir = opendir(template_path);
+	if (!dir) {
+		warning(_("templates not found %s"), template_dir);
+		return;
+	}
+
+	/* Make sure that template is from the correct vintage */
+	strcpy(template_path + template_len, "config");
+	repository_format_version = 0;
+	git_config_from_file(check_repository_format_version,
+			     template_path, NULL);
+	template_path[template_len] = 0;
+
+	if (repository_format_version &&
+	    repository_format_version != GIT_REPO_VERSION) {
+		warning(_("not copying templates of "
+			"a wrong format version %d from '%s'"),
+			repository_format_version,
+			template_dir);
+		closedir(dir);
+		return;
+	}
+
+	memcpy(path, git_dir, len);
+	if (len && path[len - 1] != '/')
+		path[len++] = '/';
+	path[len] = 0;
+	copy_templates_1(path, len,
+			 template_path, template_len,
+			 dir);
+	closedir(dir);
+}
+
+static int git_init_db_config(const char *k, const char *v, void *cb)
+{
+	if (!strcmp(k, "init.templatedir"))
+		return git_config_pathname(&init_db_template_dir, k, v);
+
+	return 0;
+}
+
+static int create_default_files(const char *template_path)
+{
+	const char *git_dir = get_git_dir();
+	unsigned len = strlen(git_dir);
+	static char path[PATH_MAX];
+	struct stat st1;
+	char repo_version_string[10];
+	char junk[2];
+	int reinit;
+	int filemode;
+
+	if (len > sizeof(path)-50)
+		die(_("insane git directory %s"), git_dir);
+	memcpy(path, git_dir, len);
+
+	if (len && path[len-1] != '/')
+		path[len++] = '/';
+
+	/*
+	 * Create .git/refs/{heads,tags}
+	 */
+	safe_create_dir(git_path("refs"), 1);
+	safe_create_dir(git_path("refs/heads"), 1);
+	safe_create_dir(git_path("refs/tags"), 1);
+
+	/* Just look for `init.templatedir` */
+	git_config(git_init_db_config, NULL);
+
+	/* First copy the templates -- we might have the default
+	 * config file there, in which case we would want to read
+	 * from it after installing.
+	 */
+	copy_templates(template_path);
+
+	git_config(git_default_config, NULL);
+	is_bare_repository_cfg = init_is_bare_repository;
+
+	/* reading existing config may have overwrote it */
+	if (init_shared_repository != -1)
+		shared_repository = init_shared_repository;
+
+	/*
+	 * We would have created the above under user's umask -- under
+	 * shared-repository settings, we would need to fix them up.
+	 */
+	if (shared_repository) {
+		adjust_shared_perm(get_git_dir());
+		adjust_shared_perm(git_path("refs"));
+		adjust_shared_perm(git_path("refs/heads"));
+		adjust_shared_perm(git_path("refs/tags"));
+	}
+
+	/*
+	 * Create the default symlink from ".git/HEAD" to the "master"
+	 * branch, if it does not exist yet.
+	 */
+	strcpy(path + len, "HEAD");
+	reinit = (!access(path, R_OK)
+		  || readlink(path, junk, sizeof(junk)-1) != -1);
+	if (!reinit) {
+		if (create_symref("HEAD", "refs/heads/master", NULL) < 0)
+			exit(1);
+	}
+
+	/* This forces creation of new config file */
+	sprintf(repo_version_string, "%d", GIT_REPO_VERSION);
+	git_config_set("core.repositoryformatversion", repo_version_string);
+
+	path[len] = 0;
+	strcpy(path + len, "config");
+
+	/* Check filemode trustability */
+	filemode = TEST_FILEMODE;
+	if (TEST_FILEMODE && !lstat(path, &st1)) {
+		struct stat st2;
+		filemode = (!chmod(path, st1.st_mode ^ S_IXUSR) &&
+				!lstat(path, &st2) &&
+				st1.st_mode != st2.st_mode);
+	}
+	git_config_set("core.filemode", filemode ? "true" : "false");
+
+	if (is_bare_repository())
+		git_config_set("core.bare", "true");
+	else {
+		const char *work_tree = get_git_work_tree();
+		git_config_set("core.bare", "false");
+		/* allow template config file to override the default */
+		if (log_all_ref_updates == -1)
+		    git_config_set("core.logallrefupdates", "true");
+		if (prefixcmp(git_dir, work_tree) ||
+		    strcmp(git_dir + strlen(work_tree), "/.git")) {
+			git_config_set("core.worktree", work_tree);
+		}
+	}
+
+	if (!reinit) {
+		/* Check if symlink is supported in the work tree */
+		path[len] = 0;
+		strcpy(path + len, "tXXXXXX");
+		if (!close(xmkstemp(path)) &&
+		    !unlink(path) &&
+		    !symlink("testing", path) &&
+		    !lstat(path, &st1) &&
+		    S_ISLNK(st1.st_mode))
+			unlink(path); /* good */
+		else
+			git_config_set("core.symlinks", "false");
+
+		/* Check if the filesystem is case-insensitive */
+		path[len] = 0;
+		strcpy(path + len, "CoNfIg");
+		if (!access(path, F_OK))
+			git_config_set("core.ignorecase", "true");
+		probe_utf8_pathname_composition(path, len);
+	}
+
+	return reinit;
+}
+
+static void create_object_directory(void)
+{
+	const char *object_directory = get_object_directory();
+	int len = strlen(object_directory);
+	char *path = xmalloc(len + 40);
+
+	memcpy(path, object_directory, len);
+
+	safe_create_dir(object_directory, 1);
+	strcpy(path+len, "/pack");
+	safe_create_dir(path, 1);
+	strcpy(path+len, "/info");
+	safe_create_dir(path, 1);
+
+	free(path);
+}
+
+int set_git_dir_init(const char *git_dir, const char *real_git_dir,
+		     int exist_ok)
+{
+	if (real_git_dir) {
+		struct stat st;
+
+		if (!exist_ok && !stat(git_dir, &st))
+			die(_("%s already exists"), git_dir);
+
+		if (!exist_ok && !stat(real_git_dir, &st))
+			die(_("%s already exists"), real_git_dir);
+
+		/*
+		 * make sure symlinks are resolved because we'll be
+		 * moving the target repo later on in separate_git_dir()
+		 */
+		git_link = xstrdup(real_path(git_dir));
+	}
+	else {
+		real_git_dir = real_path(git_dir);
+		git_link = NULL;
+	}
+	set_git_dir(real_path(real_git_dir));
+	return 0;
+}
+
+static void separate_git_dir(const char *git_dir)
+{
+	struct stat st;
+	FILE *fp;
+
+	if (!stat(git_link, &st)) {
+		const char *src;
+
+		if (S_ISREG(st.st_mode))
+			src = read_gitfile(git_link);
+		else if (S_ISDIR(st.st_mode))
+			src = git_link;
+		else
+			die(_("unable to handle file type %d"), (int)st.st_mode);
+
+		if (rename(src, git_dir))
+			die_errno(_("unable to move %s to %s"), src, git_dir);
+	}
+
+	fp = fopen(git_link, "w");
+	if (!fp)
+		die(_("Could not create git link %s"), git_link);
+	fprintf(fp, "gitdir: %s\n", git_dir);
+	fclose(fp);
+}
+
+int init_db(const char *template_dir, unsigned int flags)
+{
+	int reinit;
+	const char *git_dir = get_git_dir();
+
+	if (git_link)
+		separate_git_dir(git_dir);
+
+	safe_create_dir(git_dir, 0);
+
+	init_is_bare_repository = is_bare_repository();
+
+	/* Check to see if the repository version is right.
+	 * Note that a newly created repository does not have
+	 * config file, so this will not fail.  What we are catching
+	 * is an attempt to reinitialize new repository with an old tool.
+	 */
+	check_repository_format();
+
+	reinit = create_default_files(template_dir);
+
+	create_object_directory();
+
+	if (shared_repository) {
+		char buf[10];
+		/* We do not spell "group" and such, so that
+		 * the configuration can be read by older version
+		 * of git. Note, we use octal numbers for new share modes,
+		 * and compatibility values for PERM_GROUP and
+		 * PERM_EVERYBODY.
+		 */
+		if (shared_repository < 0)
+			/* force to the mode value */
+			sprintf(buf, "0%o", -shared_repository);
+		else if (shared_repository == PERM_GROUP)
+			sprintf(buf, "%d", OLD_PERM_GROUP);
+		else if (shared_repository == PERM_EVERYBODY)
+			sprintf(buf, "%d", OLD_PERM_EVERYBODY);
+		else
+			die("oops");
+		git_config_set("core.sharedrepository", buf);
+		git_config_set("receive.denyNonFastforwards", "true");
+	}
+
+	if (!(flags & INIT_DB_QUIET)) {
+		int len = strlen(git_dir);
+
+		/*
+		 * TRANSLATORS: The first '%s' is either "Reinitialized
+		 * existing" or "Initialized empty", the second " shared" or
+		 * "", and the last '%s%s' is the verbatim directory name.
+		 */
+		printf(_("%s%s Git repository in %s%s\n"),
+		       reinit ? _("Reinitialized existing") : _("Initialized empty"),
+		       shared_repository ? _(" shared") : "",
+		       git_dir, len && git_dir[len-1] != '/' ? "/" : "");
+	}
+
+	return 0;
+}
+
+static int guess_repository_type(const char *git_dir)
+{
+	char cwd[PATH_MAX];
+	const char *slash;
+
+	/*
+	 * "GIT_DIR=. git init" is always bare.
+	 * "GIT_DIR=`pwd` git init" too.
+	 */
+	if (!strcmp(".", git_dir))
+		return 1;
+	if (!getcwd(cwd, sizeof(cwd)))
+		die_errno(_("cannot tell cwd"));
+	if (!strcmp(git_dir, cwd))
+		return 1;
+	/*
+	 * "GIT_DIR=.git or GIT_DIR=something/.git is usually not.
+	 */
+	if (!strcmp(git_dir, ".git"))
+		return 0;
+	slash = strrchr(git_dir, '/');
+	if (slash && !strcmp(slash, "/.git"))
+		return 0;
+
+	/*
+	 * Otherwise it is often bare.  At this point
+	 * we are just guessing.
+	 */
+	return 1;
+}
+
+static int shared_callback(const struct option *opt, const char *arg, int unset)
+{
+	*((int *) opt->value) = (arg) ? git_config_perm("arg", arg) : PERM_GROUP;
+	return 0;
+}
+
+static const char *const init_db_usage[] = {
+	"git init [-q | --quiet] [--bare] [--template=<template-directory>] [--shared[=<permissions>]] [directory]",
+	NULL
+};
+
+/*
+ * If you want to, you can share the DB area with any number of branches.
+ * That has advantages: you can save space by sharing all the SHA1 objects.
+ * 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, const char *prefix)
+{
+	const char *git_dir;
+	const char *real_git_dir = NULL;
+	const char *work_tree;
+	const char *template_dir = NULL;
+	unsigned int flags = 0;
+	const struct option init_db_options[] = {
+		OPT_STRING(0, "template", &template_dir, "template-directory",
+				"directory from which templates will be used"),
+		OPT_SET_INT(0, "bare", &is_bare_repository_cfg,
+				"create a bare repository", 1),
+		{ OPTION_CALLBACK, 0, "shared", &init_shared_repository,
+			"permissions",
+			"specify that the git repository is to be shared amongst several users",
+			PARSE_OPT_OPTARG | PARSE_OPT_NONEG, shared_callback, 0},
+		OPT_BIT('q', "quiet", &flags, "be quiet", INIT_DB_QUIET),
+		OPT_STRING(0, "separate-git-dir", &real_git_dir, "gitdir",
+			   "separate git dir from working tree"),
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0);
+
+	if (real_git_dir && !is_absolute_path(real_git_dir))
+		real_git_dir = xstrdup(real_path(real_git_dir));
+
+	if (argc == 1) {
+		int mkdir_tried = 0;
+	retry:
+		if (chdir(argv[0]) < 0) {
+			if (!mkdir_tried) {
+				int saved;
+				/*
+				 * At this point we haven't read any configuration,
+				 * and we know shared_repository should always be 0;
+				 * but just in case we play safe.
+				 */
+				saved = shared_repository;
+				shared_repository = 0;
+				switch (safe_create_leading_directories_const(argv[0])) {
+				case -3:
+					errno = EEXIST;
+					/* fallthru */
+				case -1:
+					die_errno(_("cannot mkdir %s"), argv[0]);
+					break;
+				default:
+					break;
+				}
+				shared_repository = saved;
+				if (mkdir(argv[0], 0777) < 0)
+					die_errno(_("cannot mkdir %s"), argv[0]);
+				mkdir_tried = 1;
+				goto retry;
+			}
+			die_errno(_("cannot chdir to %s"), argv[0]);
+		}
+	} else if (0 < argc) {
+		usage(init_db_usage[0]);
+	}
+	if (is_bare_repository_cfg == 1) {
+		static char git_dir[PATH_MAX+1];
+
+		setenv(GIT_DIR_ENVIRONMENT,
+			getcwd(git_dir, sizeof(git_dir)), argc > 0);
+	}
+
+	if (init_shared_repository != -1)
+		shared_repository = init_shared_repository;
+
+	/*
+	 * GIT_WORK_TREE makes sense only in conjunction with GIT_DIR
+	 * without --bare.  Catch the error early.
+	 */
+	git_dir = getenv(GIT_DIR_ENVIRONMENT);
+	work_tree = getenv(GIT_WORK_TREE_ENVIRONMENT);
+	if ((!git_dir || is_bare_repository_cfg == 1) && work_tree)
+		die(_("%s (or --work-tree=<directory>) not allowed without "
+			  "specifying %s (or --git-dir=<directory>)"),
+		    GIT_WORK_TREE_ENVIRONMENT,
+		    GIT_DIR_ENVIRONMENT);
+
+	/*
+	 * Set up the default .git directory contents
+	 */
+	if (!git_dir)
+		git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
+
+	if (is_bare_repository_cfg < 0)
+		is_bare_repository_cfg = guess_repository_type(git_dir);
+
+	if (!is_bare_repository_cfg) {
+		const char *git_dir_parent = strrchr(git_dir, '/');
+		if (git_dir_parent) {
+			char *rel = xstrndup(git_dir, git_dir_parent - git_dir);
+			git_work_tree_cfg = xstrdup(real_path(rel));
+			free(rel);
+		}
+		if (!git_work_tree_cfg) {
+			git_work_tree_cfg = xcalloc(PATH_MAX, 1);
+			if (!getcwd(git_work_tree_cfg, PATH_MAX))
+				die_errno (_("Cannot access current working directory"));
+		}
+		if (work_tree)
+			set_git_work_tree(real_path(work_tree));
+		else
+			set_git_work_tree(git_work_tree_cfg);
+		if (access(get_git_work_tree(), X_OK))
+			die_errno (_("Cannot access work tree '%s'"),
+				   get_git_work_tree());
+	}
+	else {
+		if (work_tree)
+			set_git_work_tree(real_path(work_tree));
+	}
+
+	set_git_dir_init(git_dir, real_git_dir, 1);
+
+	return init_db(template_dir, flags);
+}
diff --git a/builtin/log.c b/builtin/log.c
new file mode 100644
index 0000000..ecc2793
--- /dev/null
+++ b/builtin/log.c
@@ -0,0 +1,1556 @@
+/*
+ * Builtin "git log" and related commands (show, whatchanged)
+ *
+ * (C) Copyright 2006 Linus Torvalds
+ *		 2006 Junio Hamano
+ */
+#include "cache.h"
+#include "color.h"
+#include "commit.h"
+#include "diff.h"
+#include "revision.h"
+#include "log-tree.h"
+#include "builtin.h"
+#include "tag.h"
+#include "reflog-walk.h"
+#include "patch-ids.h"
+#include "run-command.h"
+#include "shortlog.h"
+#include "remote.h"
+#include "string-list.h"
+#include "parse-options.h"
+#include "branch.h"
+#include "streaming.h"
+#include "version.h"
+
+/* Set a default date-time format for git log ("log.date" config variable) */
+static const char *default_date_mode = NULL;
+
+static int default_abbrev_commit;
+static int default_show_root = 1;
+static int decoration_style;
+static int decoration_given;
+static const char *fmt_patch_subject_prefix = "PATCH";
+static const char *fmt_pretty;
+
+static const char * const builtin_log_usage[] = {
+	"git log [<options>] [<since>..<until>] [[--] <path>...]\n"
+	"   or: git show [options] <object>...",
+	NULL
+};
+
+static int parse_decoration_style(const char *var, const char *value)
+{
+	switch (git_config_maybe_bool(var, value)) {
+	case 1:
+		return DECORATE_SHORT_REFS;
+	case 0:
+		return 0;
+	default:
+		break;
+	}
+	if (!strcmp(value, "full"))
+		return DECORATE_FULL_REFS;
+	else if (!strcmp(value, "short"))
+		return DECORATE_SHORT_REFS;
+	return -1;
+}
+
+static int decorate_callback(const struct option *opt, const char *arg, int unset)
+{
+	if (unset)
+		decoration_style = 0;
+	else if (arg)
+		decoration_style = parse_decoration_style("command line", arg);
+	else
+		decoration_style = DECORATE_SHORT_REFS;
+
+	if (decoration_style < 0)
+		die("invalid --decorate option: %s", arg);
+
+	decoration_given = 1;
+
+	return 0;
+}
+
+static void cmd_log_init_defaults(struct rev_info *rev)
+{
+	if (fmt_pretty)
+		get_commit_format(fmt_pretty, rev);
+	rev->verbose_header = 1;
+	DIFF_OPT_SET(&rev->diffopt, RECURSIVE);
+	rev->diffopt.stat_width = -1; /* use full terminal width */
+	rev->diffopt.stat_graph_width = -1; /* respect statGraphWidth config */
+	rev->abbrev_commit = default_abbrev_commit;
+	rev->show_root_diff = default_show_root;
+	rev->subject_prefix = fmt_patch_subject_prefix;
+	DIFF_OPT_SET(&rev->diffopt, ALLOW_TEXTCONV);
+
+	if (default_date_mode)
+		rev->date_mode = parse_date_format(default_date_mode);
+}
+
+static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
+			 struct rev_info *rev, struct setup_revision_opt *opt)
+{
+	struct userformat_want w;
+	int quiet = 0, source = 0;
+
+	const struct option builtin_log_options[] = {
+		OPT_BOOLEAN(0, "quiet", &quiet, "suppress diff output"),
+		OPT_BOOLEAN(0, "source", &source, "show source"),
+		{ OPTION_CALLBACK, 0, "decorate", NULL, NULL, "decorate options",
+		  PARSE_OPT_OPTARG, decorate_callback},
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, prefix,
+			     builtin_log_options, builtin_log_usage,
+			     PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN |
+			     PARSE_OPT_KEEP_DASHDASH);
+
+	argc = setup_revisions(argc, argv, rev, opt);
+	if (quiet)
+		rev->diffopt.output_format |= DIFF_FORMAT_NO_OUTPUT;
+
+	/* Any arguments at this point are not recognized */
+	if (argc > 1)
+		die("unrecognized argument: %s", argv[1]);
+
+	memset(&w, 0, sizeof(w));
+	userformat_find_requirements(NULL, &w);
+
+	if (!rev->show_notes_given && (!rev->pretty_given || w.notes))
+		rev->show_notes = 1;
+	if (rev->show_notes)
+		init_display_notes(&rev->notes_opt);
+
+	if (rev->diffopt.pickaxe || rev->diffopt.filter)
+		rev->always_show_header = 0;
+	if (DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES)) {
+		rev->always_show_header = 0;
+		if (rev->diffopt.pathspec.nr != 1)
+			usage("git logs can only follow renames on one pathname at a time");
+	}
+
+	if (source)
+		rev->show_source = 1;
+
+	if (rev->pretty_given && rev->commit_format == CMIT_FMT_RAW) {
+		/*
+		 * "log --pretty=raw" is special; ignore UI oriented
+		 * configuration variables such as decoration.
+		 */
+		if (!decoration_given)
+			decoration_style = 0;
+		if (!rev->abbrev_commit_given)
+			rev->abbrev_commit = 0;
+	}
+
+	if (decoration_style) {
+		rev->show_decorations = 1;
+		load_ref_decorations(decoration_style);
+	}
+	setup_pager();
+}
+
+static void cmd_log_init(int argc, const char **argv, const char *prefix,
+			 struct rev_info *rev, struct setup_revision_opt *opt)
+{
+	cmd_log_init_defaults(rev);
+	cmd_log_init_finish(argc, argv, prefix, rev, opt);
+}
+
+/*
+ * This gives a rough estimate for how many commits we
+ * will print out in the list.
+ */
+static int estimate_commit_count(struct rev_info *rev, struct commit_list *list)
+{
+	int n = 0;
+
+	while (list) {
+		struct commit *commit = list->item;
+		unsigned int flags = commit->object.flags;
+		list = list->next;
+		if (!(flags & (TREESAME | UNINTERESTING)))
+			n++;
+	}
+	return n;
+}
+
+static void show_early_header(struct rev_info *rev, const char *stage, int nr)
+{
+	if (rev->shown_one) {
+		rev->shown_one = 0;
+		if (rev->commit_format != CMIT_FMT_ONELINE)
+			putchar(rev->diffopt.line_termination);
+	}
+	printf(_("Final output: %d %s\n"), nr, stage);
+}
+
+static struct itimerval early_output_timer;
+
+static void log_show_early(struct rev_info *revs, struct commit_list *list)
+{
+	int i = revs->early_output;
+	int show_header = 1;
+
+	sort_in_topological_order(&list, revs->lifo);
+	while (list && i) {
+		struct commit *commit = list->item;
+		switch (simplify_commit(revs, commit)) {
+		case commit_show:
+			if (show_header) {
+				int n = estimate_commit_count(revs, list);
+				show_early_header(revs, "incomplete", n);
+				show_header = 0;
+			}
+			log_tree_commit(revs, commit);
+			i--;
+			break;
+		case commit_ignore:
+			break;
+		case commit_error:
+			return;
+		}
+		list = list->next;
+	}
+
+	/* Did we already get enough commits for the early output? */
+	if (!i)
+		return;
+
+	/*
+	 * ..if no, then repeat it twice a second until we
+	 * do.
+	 *
+	 * NOTE! We don't use "it_interval", because if the
+	 * reader isn't listening, we want our output to be
+	 * throttled by the writing, and not have the timer
+	 * trigger every second even if we're blocked on a
+	 * reader!
+	 */
+	early_output_timer.it_value.tv_sec = 0;
+	early_output_timer.it_value.tv_usec = 500000;
+	setitimer(ITIMER_REAL, &early_output_timer, NULL);
+}
+
+static void early_output(int signal)
+{
+	show_early_output = log_show_early;
+}
+
+static void setup_early_output(struct rev_info *rev)
+{
+	struct sigaction sa;
+
+	/*
+	 * Set up the signal handler, minimally intrusively:
+	 * we only set a single volatile integer word (not
+	 * using sigatomic_t - trying to avoid unnecessary
+	 * system dependencies and headers), and using
+	 * SA_RESTART.
+	 */
+	memset(&sa, 0, sizeof(sa));
+	sa.sa_handler = early_output;
+	sigemptyset(&sa.sa_mask);
+	sa.sa_flags = SA_RESTART;
+	sigaction(SIGALRM, &sa, NULL);
+
+	/*
+	 * If we can get the whole output in less than a
+	 * tenth of a second, don't even bother doing the
+	 * early-output thing..
+	 *
+	 * This is a one-time-only trigger.
+	 */
+	early_output_timer.it_value.tv_sec = 0;
+	early_output_timer.it_value.tv_usec = 100000;
+	setitimer(ITIMER_REAL, &early_output_timer, NULL);
+}
+
+static void finish_early_output(struct rev_info *rev)
+{
+	int n = estimate_commit_count(rev, rev->commits);
+	signal(SIGALRM, SIG_IGN);
+	show_early_header(rev, "done", n);
+}
+
+static int cmd_log_walk(struct rev_info *rev)
+{
+	struct commit *commit;
+	int saved_nrl = 0;
+	int saved_dcctc = 0;
+
+	if (rev->early_output)
+		setup_early_output(rev);
+
+	if (prepare_revision_walk(rev))
+		die(_("revision walk setup failed"));
+
+	if (rev->early_output)
+		finish_early_output(rev);
+
+	/*
+	 * For --check and --exit-code, the exit code is based on CHECK_FAILED
+	 * and HAS_CHANGES being accumulated in rev->diffopt, so be careful to
+	 * retain that state information if replacing rev->diffopt in this loop
+	 */
+	while ((commit = get_revision(rev)) != NULL) {
+		if (!log_tree_commit(rev, commit) &&
+		    rev->max_count >= 0)
+			/*
+			 * We decremented max_count in get_revision,
+			 * but we didn't actually show the commit.
+			 */
+			rev->max_count++;
+		if (!rev->reflog_info) {
+			/* we allow cycles in reflog ancestry */
+			free(commit->buffer);
+			commit->buffer = NULL;
+		}
+		free_commit_list(commit->parents);
+		commit->parents = NULL;
+		if (saved_nrl < rev->diffopt.needed_rename_limit)
+			saved_nrl = rev->diffopt.needed_rename_limit;
+		if (rev->diffopt.degraded_cc_to_c)
+			saved_dcctc = 1;
+	}
+	rev->diffopt.degraded_cc_to_c = saved_dcctc;
+	rev->diffopt.needed_rename_limit = saved_nrl;
+
+	if (rev->diffopt.output_format & DIFF_FORMAT_CHECKDIFF &&
+	    DIFF_OPT_TST(&rev->diffopt, CHECK_FAILED)) {
+		return 02;
+	}
+	return diff_result_code(&rev->diffopt, 0);
+}
+
+static int git_log_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, "format.pretty"))
+		return git_config_string(&fmt_pretty, var, value);
+	if (!strcmp(var, "format.subjectprefix"))
+		return git_config_string(&fmt_patch_subject_prefix, var, value);
+	if (!strcmp(var, "log.abbrevcommit")) {
+		default_abbrev_commit = git_config_bool(var, value);
+		return 0;
+	}
+	if (!strcmp(var, "log.date"))
+		return git_config_string(&default_date_mode, var, value);
+	if (!strcmp(var, "log.decorate")) {
+		decoration_style = parse_decoration_style(var, value);
+		if (decoration_style < 0)
+			decoration_style = 0; /* maybe warn? */
+		return 0;
+	}
+	if (!strcmp(var, "log.showroot")) {
+		default_show_root = git_config_bool(var, value);
+		return 0;
+	}
+	if (!prefixcmp(var, "color.decorate."))
+		return parse_decorate_color_config(var, 15, value);
+
+	return git_diff_ui_config(var, value, cb);
+}
+
+int cmd_whatchanged(int argc, const char **argv, const char *prefix)
+{
+	struct rev_info rev;
+	struct setup_revision_opt opt;
+
+	git_config(git_log_config, NULL);
+
+	init_revisions(&rev, prefix);
+	rev.diff = 1;
+	rev.simplify_history = 0;
+	memset(&opt, 0, sizeof(opt));
+	opt.def = "HEAD";
+	opt.revarg_opt = REVARG_COMMITTISH;
+	cmd_log_init(argc, argv, prefix, &rev, &opt);
+	if (!rev.diffopt.output_format)
+		rev.diffopt.output_format = DIFF_FORMAT_RAW;
+	return cmd_log_walk(&rev);
+}
+
+static void show_tagger(char *buf, int len, struct rev_info *rev)
+{
+	struct strbuf out = STRBUF_INIT;
+	struct pretty_print_context pp = {0};
+
+	pp.fmt = rev->commit_format;
+	pp.date_mode = rev->date_mode;
+	pp_user_info(&pp, "Tagger", &out, buf, get_log_output_encoding());
+	printf("%s", out.buf);
+	strbuf_release(&out);
+}
+
+static int show_blob_object(const unsigned char *sha1, struct rev_info *rev)
+{
+	fflush(stdout);
+	return stream_blob_to_fd(1, sha1, NULL, 0);
+}
+
+static int show_tag_object(const unsigned char *sha1, struct rev_info *rev)
+{
+	unsigned long size;
+	enum object_type type;
+	char *buf = read_sha1_file(sha1, &type, &size);
+	int offset = 0;
+
+	if (!buf)
+		return error(_("Could not read object %s"), sha1_to_hex(sha1));
+
+	assert(type == OBJ_TAG);
+	while (offset < size && buf[offset] != '\n') {
+		int new_offset = offset + 1;
+		while (new_offset < size && buf[new_offset++] != '\n')
+			; /* do nothing */
+		if (!prefixcmp(buf + offset, "tagger "))
+			show_tagger(buf + offset + 7,
+				    new_offset - offset - 7, rev);
+		offset = new_offset;
+	}
+
+	if (offset < size)
+		fwrite(buf + offset, size - offset, 1, stdout);
+	free(buf);
+	return 0;
+}
+
+static int show_tree_object(const unsigned char *sha1,
+		const char *base, int baselen,
+		const char *pathname, unsigned mode, int stage, void *context)
+{
+	printf("%s%s\n", pathname, S_ISDIR(mode) ? "/" : "");
+	return 0;
+}
+
+static void show_rev_tweak_rev(struct rev_info *rev, struct setup_revision_opt *opt)
+{
+	if (rev->ignore_merges) {
+		/* There was no "-m" on the command line */
+		rev->ignore_merges = 0;
+		if (!rev->first_parent_only && !rev->combine_merges) {
+			/* No "--first-parent", "-c", nor "--cc" */
+			rev->combine_merges = 1;
+			rev->dense_combined_merges = 1;
+		}
+	}
+	if (!rev->diffopt.output_format)
+		rev->diffopt.output_format = DIFF_FORMAT_PATCH;
+}
+
+int cmd_show(int argc, const char **argv, const char *prefix)
+{
+	struct rev_info rev;
+	struct object_array_entry *objects;
+	struct setup_revision_opt opt;
+	struct pathspec match_all;
+	int i, count, ret = 0;
+
+	git_config(git_log_config, NULL);
+
+	init_pathspec(&match_all, NULL);
+	init_revisions(&rev, prefix);
+	rev.diff = 1;
+	rev.always_show_header = 1;
+	rev.no_walk = 1;
+	rev.diffopt.stat_width = -1; 	/* Scale to real terminal size */
+
+	memset(&opt, 0, sizeof(opt));
+	opt.def = "HEAD";
+	opt.tweak = show_rev_tweak_rev;
+	cmd_log_init(argc, argv, prefix, &rev, &opt);
+
+	if (!rev.no_walk)
+		return cmd_log_walk(&rev);
+
+	count = rev.pending.nr;
+	objects = rev.pending.objects;
+	for (i = 0; i < count && !ret; i++) {
+		struct object *o = objects[i].item;
+		const char *name = objects[i].name;
+		switch (o->type) {
+		case OBJ_BLOB:
+			ret = show_blob_object(o->sha1, NULL);
+			break;
+		case OBJ_TAG: {
+			struct tag *t = (struct tag *)o;
+
+			if (rev.shown_one)
+				putchar('\n');
+			printf("%stag %s%s\n",
+					diff_get_color_opt(&rev.diffopt, DIFF_COMMIT),
+					t->tag,
+					diff_get_color_opt(&rev.diffopt, DIFF_RESET));
+			ret = show_tag_object(o->sha1, &rev);
+			rev.shown_one = 1;
+			if (ret)
+				break;
+			o = parse_object(t->tagged->sha1);
+			if (!o)
+				ret = error(_("Could not read object %s"),
+					    sha1_to_hex(t->tagged->sha1));
+			objects[i].item = o;
+			i--;
+			break;
+		}
+		case OBJ_TREE:
+			if (rev.shown_one)
+				putchar('\n');
+			printf("%stree %s%s\n\n",
+					diff_get_color_opt(&rev.diffopt, DIFF_COMMIT),
+					name,
+					diff_get_color_opt(&rev.diffopt, DIFF_RESET));
+			read_tree_recursive((struct tree *)o, "", 0, 0, &match_all,
+					show_tree_object, NULL);
+			rev.shown_one = 1;
+			break;
+		case OBJ_COMMIT:
+			rev.pending.nr = rev.pending.alloc = 0;
+			rev.pending.objects = NULL;
+			add_object_array(o, name, &rev.pending);
+			ret = cmd_log_walk(&rev);
+			break;
+		default:
+			ret = error(_("Unknown type: %d"), o->type);
+		}
+	}
+	free(objects);
+	return ret;
+}
+
+/*
+ * This is equivalent to "git log -g --abbrev-commit --pretty=oneline"
+ */
+int cmd_log_reflog(int argc, const char **argv, const char *prefix)
+{
+	struct rev_info rev;
+	struct setup_revision_opt opt;
+
+	git_config(git_log_config, NULL);
+
+	init_revisions(&rev, prefix);
+	init_reflog_walk(&rev.reflog_info);
+	rev.verbose_header = 1;
+	memset(&opt, 0, sizeof(opt));
+	opt.def = "HEAD";
+	cmd_log_init_defaults(&rev);
+	rev.abbrev_commit = 1;
+	rev.commit_format = CMIT_FMT_ONELINE;
+	rev.use_terminator = 1;
+	rev.always_show_header = 1;
+	cmd_log_init_finish(argc, argv, prefix, &rev, &opt);
+
+	return cmd_log_walk(&rev);
+}
+
+int cmd_log(int argc, const char **argv, const char *prefix)
+{
+	struct rev_info rev;
+	struct setup_revision_opt opt;
+
+	git_config(git_log_config, NULL);
+
+	init_revisions(&rev, prefix);
+	rev.always_show_header = 1;
+	memset(&opt, 0, sizeof(opt));
+	opt.def = "HEAD";
+	opt.revarg_opt = REVARG_COMMITTISH;
+	cmd_log_init(argc, argv, prefix, &rev, &opt);
+	return cmd_log_walk(&rev);
+}
+
+/* format-patch */
+
+static const char *fmt_patch_suffix = ".patch";
+static int numbered = 0;
+static int auto_number = 1;
+
+static char *default_attach = NULL;
+
+static struct string_list extra_hdr;
+static struct string_list extra_to;
+static struct string_list extra_cc;
+
+static void add_header(const char *value)
+{
+	struct string_list_item *item;
+	int len = strlen(value);
+	while (len && value[len - 1] == '\n')
+		len--;
+
+	if (!strncasecmp(value, "to: ", 4)) {
+		item = string_list_append(&extra_to, value + 4);
+		len -= 4;
+	} else if (!strncasecmp(value, "cc: ", 4)) {
+		item = string_list_append(&extra_cc, value + 4);
+		len -= 4;
+	} else {
+		item = string_list_append(&extra_hdr, value);
+	}
+
+	item->string[len] = '\0';
+}
+
+#define THREAD_SHALLOW 1
+#define THREAD_DEEP 2
+static int thread;
+static int do_signoff;
+static const char *signature = git_version_string;
+
+static int git_format_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, "format.headers")) {
+		if (!value)
+			die(_("format.headers without value"));
+		add_header(value);
+		return 0;
+	}
+	if (!strcmp(var, "format.suffix"))
+		return git_config_string(&fmt_patch_suffix, var, value);
+	if (!strcmp(var, "format.to")) {
+		if (!value)
+			return config_error_nonbool(var);
+		string_list_append(&extra_to, value);
+		return 0;
+	}
+	if (!strcmp(var, "format.cc")) {
+		if (!value)
+			return config_error_nonbool(var);
+		string_list_append(&extra_cc, value);
+		return 0;
+	}
+	if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff") ||
+	    !strcmp(var, "color.ui")) {
+		return 0;
+	}
+	if (!strcmp(var, "format.numbered")) {
+		if (value && !strcasecmp(value, "auto")) {
+			auto_number = 1;
+			return 0;
+		}
+		numbered = git_config_bool(var, value);
+		auto_number = auto_number && numbered;
+		return 0;
+	}
+	if (!strcmp(var, "format.attach")) {
+		if (value && *value)
+			default_attach = xstrdup(value);
+		else
+			default_attach = xstrdup(git_version_string);
+		return 0;
+	}
+	if (!strcmp(var, "format.thread")) {
+		if (value && !strcasecmp(value, "deep")) {
+			thread = THREAD_DEEP;
+			return 0;
+		}
+		if (value && !strcasecmp(value, "shallow")) {
+			thread = THREAD_SHALLOW;
+			return 0;
+		}
+		thread = git_config_bool(var, value) && THREAD_SHALLOW;
+		return 0;
+	}
+	if (!strcmp(var, "format.signoff")) {
+		do_signoff = git_config_bool(var, value);
+		return 0;
+	}
+	if (!strcmp(var, "format.signature"))
+		return git_config_string(&signature, var, value);
+
+	return git_log_config(var, value, cb);
+}
+
+static FILE *realstdout = NULL;
+static const char *output_directory = NULL;
+static int outdir_offset;
+
+static int reopen_stdout(struct commit *commit, const char *subject,
+			 struct rev_info *rev, int quiet)
+{
+	struct strbuf filename = STRBUF_INIT;
+	int suffix_len = strlen(fmt_patch_suffix) + 1;
+
+	if (output_directory) {
+		strbuf_addstr(&filename, output_directory);
+		if (filename.len >=
+		    PATH_MAX - FORMAT_PATCH_NAME_MAX - suffix_len)
+			return error(_("name of output directory is too long"));
+		if (filename.buf[filename.len - 1] != '/')
+			strbuf_addch(&filename, '/');
+	}
+
+	get_patch_filename(commit, subject, rev->nr, fmt_patch_suffix, &filename);
+
+	if (!quiet)
+		fprintf(realstdout, "%s\n", filename.buf + outdir_offset);
+
+	if (freopen(filename.buf, "w", stdout) == NULL)
+		return error(_("Cannot open patch file %s"), filename.buf);
+
+	strbuf_release(&filename);
+	return 0;
+}
+
+static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const char *prefix)
+{
+	struct rev_info check_rev;
+	struct commit *commit;
+	struct object *o1, *o2;
+	unsigned flags1, flags2;
+
+	if (rev->pending.nr != 2)
+		die(_("Need exactly one range."));
+
+	o1 = rev->pending.objects[0].item;
+	flags1 = o1->flags;
+	o2 = rev->pending.objects[1].item;
+	flags2 = o2->flags;
+
+	if ((flags1 & UNINTERESTING) == (flags2 & UNINTERESTING))
+		die(_("Not a range."));
+
+	init_patch_ids(ids);
+
+	/* given a range a..b get all patch ids for b..a */
+	init_revisions(&check_rev, prefix);
+	o1->flags ^= UNINTERESTING;
+	o2->flags ^= UNINTERESTING;
+	add_pending_object(&check_rev, o1, "o1");
+	add_pending_object(&check_rev, o2, "o2");
+	if (prepare_revision_walk(&check_rev))
+		die(_("revision walk setup failed"));
+
+	while ((commit = get_revision(&check_rev)) != NULL) {
+		/* ignore merges */
+		if (commit->parents && commit->parents->next)
+			continue;
+
+		add_commit_patch_id(commit, ids);
+	}
+
+	/* reset for next revision walk */
+	clear_commit_marks((struct commit *)o1,
+			SEEN | UNINTERESTING | SHOWN | ADDED);
+	clear_commit_marks((struct commit *)o2,
+			SEEN | UNINTERESTING | SHOWN | ADDED);
+	o1->flags = flags1;
+	o2->flags = flags2;
+}
+
+static void gen_message_id(struct rev_info *info, char *base)
+{
+	struct strbuf buf = STRBUF_INIT;
+	strbuf_addf(&buf, "%s.%lu.git.%s", base,
+		    (unsigned long) time(NULL),
+		    git_committer_info(IDENT_NO_NAME|IDENT_NO_DATE|IDENT_STRICT));
+	info->message_id = strbuf_detach(&buf, NULL);
+}
+
+static void print_signature(void)
+{
+	if (signature && *signature)
+		printf("-- \n%s\n\n", signature);
+}
+
+static void add_branch_description(struct strbuf *buf, const char *branch_name)
+{
+	struct strbuf desc = STRBUF_INIT;
+	if (!branch_name || !*branch_name)
+		return;
+	read_branch_desc(&desc, branch_name);
+	if (desc.len) {
+		strbuf_addch(buf, '\n');
+		strbuf_add(buf, desc.buf, desc.len);
+		strbuf_addch(buf, '\n');
+	}
+}
+
+static void make_cover_letter(struct rev_info *rev, int use_stdout,
+			      int numbered, int numbered_files,
+			      struct commit *origin,
+			      int nr, struct commit **list, struct commit *head,
+			      const char *branch_name,
+			      int quiet)
+{
+	const char *committer;
+	const char *body = "*** SUBJECT HERE ***\n\n*** BLURB HERE ***\n";
+	const char *msg;
+	struct shortlog log;
+	struct strbuf sb = STRBUF_INIT;
+	int i;
+	const char *encoding = "UTF-8";
+	struct diff_options opts;
+	int need_8bit_cte = 0;
+	struct pretty_print_context pp = {0};
+
+	if (rev->commit_format != CMIT_FMT_EMAIL)
+		die(_("Cover letter needs email format"));
+
+	committer = git_committer_info(0);
+
+	if (!use_stdout &&
+	    reopen_stdout(NULL, numbered_files ? NULL : "cover-letter", rev, quiet))
+		return;
+
+	log_write_email_headers(rev, head, &pp.subject, &pp.after_subject,
+				&need_8bit_cte);
+
+	for (i = 0; !need_8bit_cte && i < nr; i++)
+		if (has_non_ascii(list[i]->buffer))
+			need_8bit_cte = 1;
+
+	msg = body;
+	pp.fmt = CMIT_FMT_EMAIL;
+	pp.date_mode = DATE_RFC2822;
+	pp_user_info(&pp, NULL, &sb, committer, encoding);
+	pp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte);
+	pp_remainder(&pp, &msg, &sb, 0);
+	add_branch_description(&sb, branch_name);
+	printf("%s\n", sb.buf);
+
+	strbuf_release(&sb);
+
+	shortlog_init(&log);
+	log.wrap_lines = 1;
+	log.wrap = 72;
+	log.in1 = 2;
+	log.in2 = 4;
+	for (i = 0; i < nr; i++)
+		shortlog_add_commit(&log, list[i]);
+
+	shortlog_output(&log);
+
+	/*
+	 * We can only do diffstat with a unique reference point
+	 */
+	if (!origin)
+		return;
+
+	memcpy(&opts, &rev->diffopt, sizeof(opts));
+	opts.output_format = DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
+
+	diff_setup_done(&opts);
+
+	diff_tree_sha1(origin->tree->object.sha1,
+		       head->tree->object.sha1,
+		       "", &opts);
+	diffcore_std(&opts);
+	diff_flush(&opts);
+
+	printf("\n");
+	print_signature();
+}
+
+static const char *clean_message_id(const char *msg_id)
+{
+	char ch;
+	const char *a, *z, *m;
+
+	m = msg_id;
+	while ((ch = *m) && (isspace(ch) || (ch == '<')))
+		m++;
+	a = m;
+	z = NULL;
+	while ((ch = *m)) {
+		if (!isspace(ch) && (ch != '>'))
+			z = m;
+		m++;
+	}
+	if (!z)
+		die(_("insane in-reply-to: %s"), msg_id);
+	if (++z == m)
+		return a;
+	return xmemdupz(a, z - a);
+}
+
+static const char *set_outdir(const char *prefix, const char *output_directory)
+{
+	if (output_directory && is_absolute_path(output_directory))
+		return output_directory;
+
+	if (!prefix || !*prefix) {
+		if (output_directory)
+			return output_directory;
+		/* The user did not explicitly ask for "./" */
+		outdir_offset = 2;
+		return "./";
+	}
+
+	outdir_offset = strlen(prefix);
+	if (!output_directory)
+		return prefix;
+
+	return xstrdup(prefix_filename(prefix, outdir_offset,
+				       output_directory));
+}
+
+static const char * const builtin_format_patch_usage[] = {
+	"git format-patch [options] [<since> | <revision range>]",
+	NULL
+};
+
+static int keep_subject = 0;
+
+static int keep_callback(const struct option *opt, const char *arg, int unset)
+{
+	((struct rev_info *)opt->value)->total = -1;
+	keep_subject = 1;
+	return 0;
+}
+
+static int subject_prefix = 0;
+
+static int subject_prefix_callback(const struct option *opt, const char *arg,
+			    int unset)
+{
+	subject_prefix = 1;
+	((struct rev_info *)opt->value)->subject_prefix = arg;
+	return 0;
+}
+
+static int numbered_cmdline_opt = 0;
+
+static int numbered_callback(const struct option *opt, const char *arg,
+			     int unset)
+{
+	*(int *)opt->value = numbered_cmdline_opt = unset ? 0 : 1;
+	if (unset)
+		auto_number =  0;
+	return 0;
+}
+
+static int no_numbered_callback(const struct option *opt, const char *arg,
+				int unset)
+{
+	return numbered_callback(opt, arg, 1);
+}
+
+static int output_directory_callback(const struct option *opt, const char *arg,
+			      int unset)
+{
+	const char **dir = (const char **)opt->value;
+	if (*dir)
+		die(_("Two output directories?"));
+	*dir = arg;
+	return 0;
+}
+
+static int thread_callback(const struct option *opt, const char *arg, int unset)
+{
+	int *thread = (int *)opt->value;
+	if (unset)
+		*thread = 0;
+	else if (!arg || !strcmp(arg, "shallow"))
+		*thread = THREAD_SHALLOW;
+	else if (!strcmp(arg, "deep"))
+		*thread = THREAD_DEEP;
+	else
+		return 1;
+	return 0;
+}
+
+static int attach_callback(const struct option *opt, const char *arg, int unset)
+{
+	struct rev_info *rev = (struct rev_info *)opt->value;
+	if (unset)
+		rev->mime_boundary = NULL;
+	else if (arg)
+		rev->mime_boundary = arg;
+	else
+		rev->mime_boundary = git_version_string;
+	rev->no_inline = unset ? 0 : 1;
+	return 0;
+}
+
+static int inline_callback(const struct option *opt, const char *arg, int unset)
+{
+	struct rev_info *rev = (struct rev_info *)opt->value;
+	if (unset)
+		rev->mime_boundary = NULL;
+	else if (arg)
+		rev->mime_boundary = arg;
+	else
+		rev->mime_boundary = git_version_string;
+	rev->no_inline = 0;
+	return 0;
+}
+
+static int header_callback(const struct option *opt, const char *arg, int unset)
+{
+	if (unset) {
+		string_list_clear(&extra_hdr, 0);
+		string_list_clear(&extra_to, 0);
+		string_list_clear(&extra_cc, 0);
+	} else {
+	    add_header(arg);
+	}
+	return 0;
+}
+
+static int to_callback(const struct option *opt, const char *arg, int unset)
+{
+	if (unset)
+		string_list_clear(&extra_to, 0);
+	else
+		string_list_append(&extra_to, arg);
+	return 0;
+}
+
+static int cc_callback(const struct option *opt, const char *arg, int unset)
+{
+	if (unset)
+		string_list_clear(&extra_cc, 0);
+	else
+		string_list_append(&extra_cc, arg);
+	return 0;
+}
+
+static char *find_branch_name(struct rev_info *rev)
+{
+	int i, positive = -1;
+	unsigned char branch_sha1[20];
+	struct strbuf buf = STRBUF_INIT;
+	const char *branch;
+
+	for (i = 0; i < rev->cmdline.nr; i++) {
+		if (rev->cmdline.rev[i].flags & UNINTERESTING)
+			continue;
+		if (positive < 0)
+			positive = i;
+		else
+			return NULL;
+	}
+	if (positive < 0)
+		return NULL;
+	strbuf_addf(&buf, "refs/heads/%s", rev->cmdline.rev[positive].name);
+	branch = resolve_ref_unsafe(buf.buf, branch_sha1, 1, NULL);
+	if (!branch ||
+	    prefixcmp(branch, "refs/heads/") ||
+	    hashcmp(rev->cmdline.rev[positive].item->sha1, branch_sha1))
+		branch = NULL;
+	strbuf_release(&buf);
+	if (branch)
+		return xstrdup(rev->cmdline.rev[positive].name);
+	return NULL;
+}
+
+int cmd_format_patch(int argc, const char **argv, const char *prefix)
+{
+	struct commit *commit;
+	struct commit **list = NULL;
+	struct rev_info rev;
+	struct setup_revision_opt s_r_opt;
+	int nr = 0, total, i;
+	int use_stdout = 0;
+	int start_number = -1;
+	int numbered_files = 0;		/* _just_ numbers */
+	int ignore_if_in_upstream = 0;
+	int cover_letter = 0;
+	int boundary_count = 0;
+	int no_binary_diff = 0;
+	struct commit *origin = NULL, *head = NULL;
+	const char *in_reply_to = NULL;
+	struct patch_ids ids;
+	char *add_signoff = NULL;
+	struct strbuf buf = STRBUF_INIT;
+	int use_patch_format = 0;
+	int quiet = 0;
+	char *branch_name = NULL;
+	const struct option builtin_format_patch_options[] = {
+		{ OPTION_CALLBACK, 'n', "numbered", &numbered, NULL,
+			    "use [PATCH n/m] even with a single patch",
+			    PARSE_OPT_NOARG, numbered_callback },
+		{ OPTION_CALLBACK, 'N', "no-numbered", &numbered, NULL,
+			    "use [PATCH] even with multiple patches",
+			    PARSE_OPT_NOARG, no_numbered_callback },
+		OPT_BOOLEAN('s', "signoff", &do_signoff, "add Signed-off-by:"),
+		OPT_BOOLEAN(0, "stdout", &use_stdout,
+			    "print patches to standard out"),
+		OPT_BOOLEAN(0, "cover-letter", &cover_letter,
+			    "generate a cover letter"),
+		OPT_BOOLEAN(0, "numbered-files", &numbered_files,
+			    "use simple number sequence for output file names"),
+		OPT_STRING(0, "suffix", &fmt_patch_suffix, "sfx",
+			    "use <sfx> instead of '.patch'"),
+		OPT_INTEGER(0, "start-number", &start_number,
+			    "start numbering patches at <n> instead of 1"),
+		{ OPTION_CALLBACK, 0, "subject-prefix", &rev, "prefix",
+			    "Use [<prefix>] instead of [PATCH]",
+			    PARSE_OPT_NONEG, subject_prefix_callback },
+		{ OPTION_CALLBACK, 'o', "output-directory", &output_directory,
+			    "dir", "store resulting files in <dir>",
+			    PARSE_OPT_NONEG, output_directory_callback },
+		{ OPTION_CALLBACK, 'k', "keep-subject", &rev, NULL,
+			    "don't strip/add [PATCH]",
+			    PARSE_OPT_NOARG | PARSE_OPT_NONEG, keep_callback },
+		OPT_BOOLEAN(0, "no-binary", &no_binary_diff,
+			    "don't output binary diffs"),
+		OPT_BOOLEAN(0, "ignore-if-in-upstream", &ignore_if_in_upstream,
+			    "don't include a patch matching a commit upstream"),
+		{ OPTION_BOOLEAN, 'p', "no-stat", &use_patch_format, NULL,
+		  "show patch format instead of default (patch + stat)",
+		  PARSE_OPT_NONEG | PARSE_OPT_NOARG },
+		OPT_GROUP("Messaging"),
+		{ OPTION_CALLBACK, 0, "add-header", NULL, "header",
+			    "add email header", 0, header_callback },
+		{ OPTION_CALLBACK, 0, "to", NULL, "email", "add To: header",
+			    0, to_callback },
+		{ OPTION_CALLBACK, 0, "cc", NULL, "email", "add Cc: header",
+			    0, cc_callback },
+		OPT_STRING(0, "in-reply-to", &in_reply_to, "message-id",
+			    "make first mail a reply to <message-id>"),
+		{ OPTION_CALLBACK, 0, "attach", &rev, "boundary",
+			    "attach the patch", PARSE_OPT_OPTARG,
+			    attach_callback },
+		{ OPTION_CALLBACK, 0, "inline", &rev, "boundary",
+			    "inline the patch",
+			    PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
+			    inline_callback },
+		{ OPTION_CALLBACK, 0, "thread", &thread, "style",
+			    "enable message threading, styles: shallow, deep",
+			    PARSE_OPT_OPTARG, thread_callback },
+		OPT_STRING(0, "signature", &signature, "signature",
+			    "add a signature"),
+		OPT_BOOLEAN(0, "quiet", &quiet,
+			    "don't print the patch filenames"),
+		OPT_END()
+	};
+
+	extra_hdr.strdup_strings = 1;
+	extra_to.strdup_strings = 1;
+	extra_cc.strdup_strings = 1;
+	git_config(git_format_config, NULL);
+	init_revisions(&rev, prefix);
+	rev.commit_format = CMIT_FMT_EMAIL;
+	rev.verbose_header = 1;
+	rev.diff = 1;
+	rev.max_parents = 1;
+	DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
+	rev.subject_prefix = fmt_patch_subject_prefix;
+	memset(&s_r_opt, 0, sizeof(s_r_opt));
+	s_r_opt.def = "HEAD";
+	s_r_opt.revarg_opt = REVARG_COMMITTISH;
+
+	if (default_attach) {
+		rev.mime_boundary = default_attach;
+		rev.no_inline = 1;
+	}
+
+	/*
+	 * Parse the arguments before setup_revisions(), or something
+	 * like "git format-patch -o a123 HEAD^.." may fail; a123 is
+	 * possibly a valid SHA1.
+	 */
+	argc = parse_options(argc, argv, prefix, builtin_format_patch_options,
+			     builtin_format_patch_usage,
+			     PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN |
+			     PARSE_OPT_KEEP_DASHDASH);
+
+	if (do_signoff) {
+		const char *committer;
+		const char *endpos;
+		committer = git_committer_info(IDENT_STRICT);
+		endpos = strchr(committer, '>');
+		if (!endpos)
+			die(_("bogus committer info %s"), committer);
+		add_signoff = xmemdupz(committer, endpos - committer + 1);
+	}
+
+	for (i = 0; i < extra_hdr.nr; i++) {
+		strbuf_addstr(&buf, extra_hdr.items[i].string);
+		strbuf_addch(&buf, '\n');
+	}
+
+	if (extra_to.nr)
+		strbuf_addstr(&buf, "To: ");
+	for (i = 0; i < extra_to.nr; i++) {
+		if (i)
+			strbuf_addstr(&buf, "    ");
+		strbuf_addstr(&buf, extra_to.items[i].string);
+		if (i + 1 < extra_to.nr)
+			strbuf_addch(&buf, ',');
+		strbuf_addch(&buf, '\n');
+	}
+
+	if (extra_cc.nr)
+		strbuf_addstr(&buf, "Cc: ");
+	for (i = 0; i < extra_cc.nr; i++) {
+		if (i)
+			strbuf_addstr(&buf, "    ");
+		strbuf_addstr(&buf, extra_cc.items[i].string);
+		if (i + 1 < extra_cc.nr)
+			strbuf_addch(&buf, ',');
+		strbuf_addch(&buf, '\n');
+	}
+
+	rev.extra_headers = strbuf_detach(&buf, NULL);
+
+	if (start_number < 0)
+		start_number = 1;
+
+	/*
+	 * If numbered is set solely due to format.numbered in config,
+	 * and it would conflict with --keep-subject (-k) from the
+	 * command line, reset "numbered".
+	 */
+	if (numbered && keep_subject && !numbered_cmdline_opt)
+		numbered = 0;
+
+	if (numbered && keep_subject)
+		die (_("-n and -k are mutually exclusive."));
+	if (keep_subject && subject_prefix)
+		die (_("--subject-prefix and -k are mutually exclusive."));
+	rev.preserve_subject = keep_subject;
+
+	argc = setup_revisions(argc, argv, &rev, &s_r_opt);
+	if (argc > 1)
+		die (_("unrecognized argument: %s"), argv[1]);
+
+	if (rev.diffopt.output_format & DIFF_FORMAT_NAME)
+		die(_("--name-only does not make sense"));
+	if (rev.diffopt.output_format & DIFF_FORMAT_NAME_STATUS)
+		die(_("--name-status does not make sense"));
+	if (rev.diffopt.output_format & DIFF_FORMAT_CHECKDIFF)
+		die(_("--check does not make sense"));
+
+	if (!use_patch_format &&
+		(!rev.diffopt.output_format ||
+		 rev.diffopt.output_format == DIFF_FORMAT_PATCH))
+		rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_SUMMARY;
+
+	/* Always generate a patch */
+	rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
+
+	if (!DIFF_OPT_TST(&rev.diffopt, TEXT) && !no_binary_diff)
+		DIFF_OPT_SET(&rev.diffopt, BINARY);
+
+	if (rev.show_notes)
+		init_display_notes(&rev.notes_opt);
+
+	if (!use_stdout)
+		output_directory = set_outdir(prefix, output_directory);
+	else
+		setup_pager();
+
+	if (output_directory) {
+		if (use_stdout)
+			die(_("standard output, or directory, which one?"));
+		if (mkdir(output_directory, 0777) < 0 && errno != EEXIST)
+			die_errno(_("Could not create directory '%s'"),
+				  output_directory);
+	}
+
+	if (rev.pending.nr == 1) {
+		if (rev.max_count < 0 && !rev.show_root_diff) {
+			/*
+			 * This is traditional behaviour of "git format-patch
+			 * origin" that prepares what the origin side still
+			 * does not have.
+			 */
+			unsigned char sha1[20];
+			const char *ref;
+
+			rev.pending.objects[0].item->flags |= UNINTERESTING;
+			add_head_to_pending(&rev);
+			ref = resolve_ref_unsafe("HEAD", sha1, 1, NULL);
+			if (ref && !prefixcmp(ref, "refs/heads/"))
+				branch_name = xstrdup(ref + strlen("refs/heads/"));
+			else
+				branch_name = xstrdup(""); /* no branch */
+		}
+		/*
+		 * Otherwise, it is "format-patch -22 HEAD", and/or
+		 * "format-patch --root HEAD".  The user wants
+		 * get_revision() to do the usual traversal.
+		 */
+	}
+
+	/*
+	 * We cannot move this anywhere earlier because we do want to
+	 * know if --root was given explicitly from the command line.
+	 */
+	rev.show_root_diff = 1;
+
+	if (cover_letter) {
+		/*
+		 * NEEDSWORK:randomly pick one positive commit to show
+		 * diffstat; this is often the tip and the command
+		 * happens to do the right thing in most cases, but a
+		 * complex command like "--cover-letter a b c ^bottom"
+		 * picks "c" and shows diffstat between bottom..c
+		 * which may not match what the series represents at
+		 * all and totally broken.
+		 */
+		int i;
+		for (i = 0; i < rev.pending.nr; i++) {
+			struct object *o = rev.pending.objects[i].item;
+			if (!(o->flags & UNINTERESTING))
+				head = (struct commit *)o;
+		}
+		/* There is nothing to show; it is not an error, though. */
+		if (!head)
+			return 0;
+		if (!branch_name)
+			branch_name = find_branch_name(&rev);
+	}
+
+	if (ignore_if_in_upstream) {
+		/* Don't say anything if head and upstream are the same. */
+		if (rev.pending.nr == 2) {
+			struct object_array_entry *o = rev.pending.objects;
+			if (hashcmp(o[0].item->sha1, o[1].item->sha1) == 0)
+				return 0;
+		}
+		get_patch_ids(&rev, &ids, prefix);
+	}
+
+	if (!use_stdout)
+		realstdout = xfdopen(xdup(1), "w");
+
+	if (prepare_revision_walk(&rev))
+		die(_("revision walk setup failed"));
+	rev.boundary = 1;
+	while ((commit = get_revision(&rev)) != NULL) {
+		if (commit->object.flags & BOUNDARY) {
+			boundary_count++;
+			origin = (boundary_count == 1) ? commit : NULL;
+			continue;
+		}
+
+		if (ignore_if_in_upstream &&
+				has_commit_patch_id(commit, &ids))
+			continue;
+
+		nr++;
+		list = xrealloc(list, nr * sizeof(list[0]));
+		list[nr - 1] = commit;
+	}
+	total = nr;
+	if (!keep_subject && auto_number && total > 1)
+		numbered = 1;
+	if (numbered)
+		rev.total = total + start_number - 1;
+	if (in_reply_to || thread || cover_letter)
+		rev.ref_message_ids = xcalloc(1, sizeof(struct string_list));
+	if (in_reply_to) {
+		const char *msgid = clean_message_id(in_reply_to);
+		string_list_append(rev.ref_message_ids, msgid);
+	}
+	rev.numbered_files = numbered_files;
+	rev.patch_suffix = fmt_patch_suffix;
+	if (cover_letter) {
+		if (thread)
+			gen_message_id(&rev, "cover");
+		make_cover_letter(&rev, use_stdout, numbered, numbered_files,
+				  origin, nr, list, head, branch_name, quiet);
+		total++;
+		start_number--;
+	}
+	rev.add_signoff = add_signoff;
+	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) {
+			/* Have we already had a message ID? */
+			if (rev.message_id) {
+				/*
+				 * For deep threading: make every mail
+				 * a reply to the previous one, no
+				 * matter what other options are set.
+				 *
+				 * For shallow threading:
+				 *
+				 * Without --cover-letter and
+				 * --in-reply-to, make every mail a
+				 * reply to the one before.
+				 *
+				 * With --in-reply-to but no
+				 * --cover-letter, make every mail a
+				 * reply to the <reply-to>.
+				 *
+				 * With --cover-letter, make every
+				 * mail but the cover letter a reply
+				 * to the cover letter.  The cover
+				 * letter is a reply to the
+				 * --in-reply-to, if specified.
+				 */
+				if (thread == THREAD_SHALLOW
+				    && rev.ref_message_ids->nr > 0
+				    && (!cover_letter || rev.nr > 1))
+					free(rev.message_id);
+				else
+					string_list_append(rev.ref_message_ids,
+							   rev.message_id);
+			}
+			gen_message_id(&rev, sha1_to_hex(commit->object.sha1));
+		}
+
+		if (!use_stdout &&
+		    reopen_stdout(numbered_files ? NULL : commit, NULL, &rev, quiet))
+			die(_("Failed to create output files"));
+		shown = log_tree_commit(&rev, commit);
+		free(commit->buffer);
+		commit->buffer = NULL;
+
+		/* We put one extra blank line between formatted
+		 * patches and this flag is used by log-tree code
+		 * to see if it needs to emit a LF before showing
+		 * the log; when using one file per patch, we do
+		 * not want the extra blank line.
+		 */
+		if (!use_stdout)
+			rev.shown_one = 0;
+		if (shown) {
+			if (rev.mime_boundary)
+				printf("\n--%s%s--\n\n\n",
+				       mime_boundary_leader,
+				       rev.mime_boundary);
+			else
+				print_signature();
+		}
+		if (!use_stdout)
+			fclose(stdout);
+	}
+	free(list);
+	free(branch_name);
+	string_list_clear(&extra_to, 0);
+	string_list_clear(&extra_cc, 0);
+	string_list_clear(&extra_hdr, 0);
+	if (ignore_if_in_upstream)
+		free_patch_ids(&ids);
+	return 0;
+}
+
+static int add_pending_commit(const char *arg, struct rev_info *revs, int flags)
+{
+	unsigned char sha1[20];
+	if (get_sha1(arg, sha1) == 0) {
+		struct commit *commit = lookup_commit_reference(sha1);
+		if (commit) {
+			commit->object.flags |= flags;
+			add_pending_object(revs, &commit->object, arg);
+			return 0;
+		}
+	}
+	return -1;
+}
+
+static const char * const cherry_usage[] = {
+	"git cherry [-v] [<upstream> [<head> [<limit>]]]",
+	NULL
+};
+
+static void print_commit(char sign, struct commit *commit, int verbose,
+			 int abbrev)
+{
+	if (!verbose) {
+		printf("%c %s\n", sign,
+		       find_unique_abbrev(commit->object.sha1, abbrev));
+	} else {
+		struct strbuf buf = STRBUF_INIT;
+		pp_commit_easy(CMIT_FMT_ONELINE, commit, &buf);
+		printf("%c %s %s\n", sign,
+		       find_unique_abbrev(commit->object.sha1, abbrev),
+		       buf.buf);
+		strbuf_release(&buf);
+	}
+}
+
+int cmd_cherry(int argc, const char **argv, const char *prefix)
+{
+	struct rev_info revs;
+	struct patch_ids ids;
+	struct commit *commit;
+	struct commit_list *list = NULL;
+	struct branch *current_branch;
+	const char *upstream;
+	const char *head = "HEAD";
+	const char *limit = NULL;
+	int verbose = 0, abbrev = 0;
+
+	struct option options[] = {
+		OPT__ABBREV(&abbrev),
+		OPT__VERBOSE(&verbose, "be verbose"),
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, prefix, options, cherry_usage, 0);
+
+	switch (argc) {
+	case 3:
+		limit = argv[2];
+		/* FALLTHROUGH */
+	case 2:
+		head = argv[1];
+		/* FALLTHROUGH */
+	case 1:
+		upstream = argv[0];
+		break;
+	default:
+		current_branch = branch_get(NULL);
+		if (!current_branch || !current_branch->merge
+					|| !current_branch->merge[0]
+					|| !current_branch->merge[0]->dst) {
+			fprintf(stderr, _("Could not find a tracked"
+					" remote branch, please"
+					" specify <upstream> manually.\n"));
+			usage_with_options(cherry_usage, options);
+		}
+
+		upstream = current_branch->merge[0]->dst;
+	}
+
+	init_revisions(&revs, prefix);
+	revs.diff = 1;
+	revs.combine_merges = 0;
+	revs.ignore_merges = 1;
+	DIFF_OPT_SET(&revs.diffopt, RECURSIVE);
+
+	if (add_pending_commit(head, &revs, 0))
+		die(_("Unknown commit %s"), head);
+	if (add_pending_commit(upstream, &revs, UNINTERESTING))
+		die(_("Unknown commit %s"), upstream);
+
+	/* Don't say anything if head and upstream are the same. */
+	if (revs.pending.nr == 2) {
+		struct object_array_entry *o = revs.pending.objects;
+		if (hashcmp(o[0].item->sha1, o[1].item->sha1) == 0)
+			return 0;
+	}
+
+	get_patch_ids(&revs, &ids, prefix);
+
+	if (limit && add_pending_commit(limit, &revs, UNINTERESTING))
+		die(_("Unknown commit %s"), limit);
+
+	/* reverse the list of commits */
+	if (prepare_revision_walk(&revs))
+		die(_("revision walk setup failed"));
+	while ((commit = get_revision(&revs)) != NULL) {
+		/* ignore merges */
+		if (commit->parents && commit->parents->next)
+			continue;
+
+		commit_list_insert(commit, &list);
+	}
+
+	while (list) {
+		char sign = '+';
+
+		commit = list->item;
+		if (has_commit_patch_id(commit, &ids))
+			sign = '-';
+		print_commit(sign, commit, verbose, abbrev);
+		list = list->next;
+	}
+
+	free_patch_ids(&ids);
+	return 0;
+}
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
new file mode 100644
index 0000000..31b3f2d
--- /dev/null
+++ b/builtin/ls-files.c
@@ -0,0 +1,603 @@
+/*
+ * This merges the file listing in the directory cache index
+ * with the actual working directory list, and shows different
+ * combinations of the two.
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ */
+#include "cache.h"
+#include "quote.h"
+#include "dir.h"
+#include "builtin.h"
+#include "tree.h"
+#include "parse-options.h"
+#include "resolve-undo.h"
+#include "string-list.h"
+
+static int abbrev;
+static int show_deleted;
+static int show_cached;
+static int show_others;
+static int show_stage;
+static int show_unmerged;
+static int show_resolve_undo;
+static int show_modified;
+static int show_killed;
+static int show_valid_bit;
+static int line_terminator = '\n';
+static int debug_mode;
+
+static const char *prefix;
+static int max_prefix_len;
+static int prefix_len;
+static const char **pathspec;
+static int error_unmatch;
+static char *ps_matched;
+static const char *with_tree;
+static int exc_given;
+
+static const char *tag_cached = "";
+static const char *tag_unmerged = "";
+static const char *tag_removed = "";
+static const char *tag_other = "";
+static const char *tag_killed = "";
+static const char *tag_modified = "";
+static const char *tag_skip_worktree = "";
+static const char *tag_resolve_undo = "";
+
+static void write_name(const char* name, size_t len)
+{
+	write_name_quoted_relative(name, len, prefix, prefix_len, stdout,
+			line_terminator);
+}
+
+static void show_dir_entry(const char *tag, struct dir_entry *ent)
+{
+	int len = max_prefix_len;
+
+	if (len >= ent->len)
+		die("git ls-files: internal error - directory entry not superset of prefix");
+
+	if (!match_pathspec(pathspec, ent->name, ent->len, len, ps_matched))
+		return;
+
+	fputs(tag, stdout);
+	write_name(ent->name, ent->len);
+}
+
+static void show_other_files(struct dir_struct *dir)
+{
+	int i;
+
+	for (i = 0; i < dir->nr; i++) {
+		struct dir_entry *ent = dir->entries[i];
+		if (!cache_name_is_other(ent->name, ent->len))
+			continue;
+		show_dir_entry(tag_other, ent);
+	}
+}
+
+static void show_killed_files(struct dir_struct *dir)
+{
+	int i;
+	for (i = 0; i < dir->nr; i++) {
+		struct dir_entry *ent = dir->entries[i];
+		char *cp, *sp;
+		int pos, len, killed = 0;
+
+		for (cp = ent->name; cp - ent->name < ent->len; cp = sp + 1) {
+			sp = strchr(cp, '/');
+			if (!sp) {
+				/* If ent->name is prefix of an entry in the
+				 * cache, it will be killed.
+				 */
+				pos = cache_name_pos(ent->name, ent->len);
+				if (0 <= pos)
+					die("bug in show-killed-files");
+				pos = -pos - 1;
+				while (pos < active_nr &&
+				       ce_stage(active_cache[pos]))
+					pos++; /* skip unmerged */
+				if (active_nr <= pos)
+					break;
+				/* pos points at a name immediately after
+				 * ent->name in the cache.  Does it expect
+				 * ent->name to be a directory?
+				 */
+				len = ce_namelen(active_cache[pos]);
+				if ((ent->len < len) &&
+				    !strncmp(active_cache[pos]->name,
+					     ent->name, ent->len) &&
+				    active_cache[pos]->name[ent->len] == '/')
+					killed = 1;
+				break;
+			}
+			if (0 <= cache_name_pos(ent->name, sp - ent->name)) {
+				/* If any of the leading directories in
+				 * ent->name is registered in the cache,
+				 * ent->name will be killed.
+				 */
+				killed = 1;
+				break;
+			}
+		}
+		if (killed)
+			show_dir_entry(tag_killed, dir->entries[i]);
+	}
+}
+
+static void show_ce_entry(const char *tag, struct cache_entry *ce)
+{
+	int len = max_prefix_len;
+
+	if (len >= ce_namelen(ce))
+		die("git ls-files: internal error - cache entry not superset of prefix");
+
+	if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), len, ps_matched))
+		return;
+
+	if (tag && *tag && show_valid_bit &&
+	    (ce->ce_flags & CE_VALID)) {
+		static char alttag[4];
+		memcpy(alttag, tag, 3);
+		if (isalpha(tag[0]))
+			alttag[0] = tolower(tag[0]);
+		else if (tag[0] == '?')
+			alttag[0] = '!';
+		else {
+			alttag[0] = 'v';
+			alttag[1] = tag[0];
+			alttag[2] = ' ';
+			alttag[3] = 0;
+		}
+		tag = alttag;
+	}
+
+	if (!show_stage) {
+		fputs(tag, stdout);
+	} else {
+		printf("%s%06o %s %d\t",
+		       tag,
+		       ce->ce_mode,
+		       find_unique_abbrev(ce->sha1,abbrev),
+		       ce_stage(ce));
+	}
+	write_name(ce->name, ce_namelen(ce));
+	if (debug_mode) {
+		printf("  ctime: %d:%d\n", ce->ce_ctime.sec, ce->ce_ctime.nsec);
+		printf("  mtime: %d:%d\n", ce->ce_mtime.sec, ce->ce_mtime.nsec);
+		printf("  dev: %d\tino: %d\n", ce->ce_dev, ce->ce_ino);
+		printf("  uid: %d\tgid: %d\n", ce->ce_uid, ce->ce_gid);
+		printf("  size: %d\tflags: %x\n", ce->ce_size, ce->ce_flags);
+	}
+}
+
+static void show_ru_info(void)
+{
+	struct string_list_item *item;
+
+	if (!the_index.resolve_undo)
+		return;
+
+	for_each_string_list_item(item, the_index.resolve_undo) {
+		const char *path = item->string;
+		struct resolve_undo_info *ui = item->util;
+		int i, len;
+
+		len = strlen(path);
+		if (len < max_prefix_len)
+			continue; /* outside of the prefix */
+		if (!match_pathspec(pathspec, path, len, max_prefix_len, ps_matched))
+			continue; /* uninterested */
+		for (i = 0; i < 3; i++) {
+			if (!ui->mode[i])
+				continue;
+			printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i],
+			       find_unique_abbrev(ui->sha1[i], abbrev),
+			       i + 1);
+			write_name(path, len);
+		}
+	}
+}
+
+static int ce_excluded(struct path_exclude_check *check, struct cache_entry *ce)
+{
+	int dtype = ce_to_dtype(ce);
+	return path_excluded(check, ce->name, ce_namelen(ce), &dtype);
+}
+
+static void show_files(struct dir_struct *dir)
+{
+	int i;
+	struct path_exclude_check check;
+
+	if ((dir->flags & DIR_SHOW_IGNORED))
+		path_exclude_check_init(&check, dir);
+
+	/* For cached/deleted files we don't need to even do the readdir */
+	if (show_others || show_killed) {
+		fill_directory(dir, pathspec);
+		if (show_others)
+			show_other_files(dir);
+		if (show_killed)
+			show_killed_files(dir);
+	}
+	if (show_cached | show_stage) {
+		for (i = 0; i < active_nr; i++) {
+			struct cache_entry *ce = active_cache[i];
+			if ((dir->flags & DIR_SHOW_IGNORED) &&
+			    !ce_excluded(&check, ce))
+				continue;
+			if (show_unmerged && !ce_stage(ce))
+				continue;
+			if (ce->ce_flags & CE_UPDATE)
+				continue;
+			show_ce_entry(ce_stage(ce) ? tag_unmerged :
+				(ce_skip_worktree(ce) ? tag_skip_worktree : tag_cached), ce);
+		}
+	}
+	if (show_deleted | show_modified) {
+		for (i = 0; i < active_nr; i++) {
+			struct cache_entry *ce = active_cache[i];
+			struct stat st;
+			int err;
+			if ((dir->flags & DIR_SHOW_IGNORED) &&
+			    !ce_excluded(&check, ce))
+				continue;
+			if (ce->ce_flags & CE_UPDATE)
+				continue;
+			if (ce_skip_worktree(ce))
+				continue;
+			err = lstat(ce->name, &st);
+			if (show_deleted && err)
+				show_ce_entry(tag_removed, ce);
+			if (show_modified && ce_modified(ce, &st, 0))
+				show_ce_entry(tag_modified, ce);
+		}
+	}
+
+	if ((dir->flags & DIR_SHOW_IGNORED))
+		path_exclude_check_clear(&check);
+}
+
+/*
+ * Prune the index to only contain stuff starting with "prefix"
+ */
+static void prune_cache(const char *prefix)
+{
+	int pos = cache_name_pos(prefix, max_prefix_len);
+	unsigned int first, last;
+
+	if (pos < 0)
+		pos = -pos-1;
+	memmove(active_cache, active_cache + pos,
+		(active_nr - pos) * sizeof(struct cache_entry *));
+	active_nr -= pos;
+	first = 0;
+	last = active_nr;
+	while (last > first) {
+		int next = (last + first) >> 1;
+		struct cache_entry *ce = active_cache[next];
+		if (!strncmp(ce->name, prefix, max_prefix_len)) {
+			first = next+1;
+			continue;
+		}
+		last = next;
+	}
+	active_nr = last;
+}
+
+static void strip_trailing_slash_from_submodules(void)
+{
+	const char **p;
+
+	for (p = pathspec; *p != NULL; p++) {
+		int len = strlen(*p), pos;
+
+		if (len < 1 || (*p)[len - 1] != '/')
+			continue;
+		pos = cache_name_pos(*p, len - 1);
+		if (pos >= 0 && S_ISGITLINK(active_cache[pos]->ce_mode))
+			*p = xstrndup(*p, len - 1);
+	}
+}
+
+/*
+ * Read the tree specified with --with-tree option
+ * (typically, HEAD) into stage #1 and then
+ * squash them down to stage #0.  This is used for
+ * --error-unmatch to list and check the path patterns
+ * that were given from the command line.  We are not
+ * going to write this index out.
+ */
+void overlay_tree_on_cache(const char *tree_name, const char *prefix)
+{
+	struct tree *tree;
+	unsigned char sha1[20];
+	struct pathspec pathspec;
+	struct cache_entry *last_stage0 = NULL;
+	int i;
+
+	if (get_sha1(tree_name, sha1))
+		die("tree-ish %s not found.", tree_name);
+	tree = parse_tree_indirect(sha1);
+	if (!tree)
+		die("bad tree-ish %s", tree_name);
+
+	/* Hoist the unmerged entries up to stage #3 to make room */
+	for (i = 0; i < active_nr; i++) {
+		struct cache_entry *ce = active_cache[i];
+		if (!ce_stage(ce))
+			continue;
+		ce->ce_flags |= CE_STAGEMASK;
+	}
+
+	if (prefix) {
+		static const char *(matchbuf[2]);
+		matchbuf[0] = prefix;
+		matchbuf[1] = NULL;
+		init_pathspec(&pathspec, matchbuf);
+		pathspec.items[0].use_wildcard = 0;
+	} else
+		init_pathspec(&pathspec, NULL);
+	if (read_tree(tree, 1, &pathspec))
+		die("unable to read tree entries %s", tree_name);
+
+	for (i = 0; i < active_nr; i++) {
+		struct cache_entry *ce = active_cache[i];
+		switch (ce_stage(ce)) {
+		case 0:
+			last_stage0 = ce;
+			/* fallthru */
+		default:
+			continue;
+		case 1:
+			/*
+			 * If there is stage #0 entry for this, we do not
+			 * need to show it.  We use CE_UPDATE bit to mark
+			 * such an entry.
+			 */
+			if (last_stage0 &&
+			    !strcmp(last_stage0->name, ce->name))
+				ce->ce_flags |= CE_UPDATE;
+		}
+	}
+}
+
+int report_path_error(const char *ps_matched, const char **pathspec, const char *prefix)
+{
+	/*
+	 * Make sure all pathspec matched; otherwise it is an error.
+	 */
+	struct strbuf sb = STRBUF_INIT;
+	const char *name;
+	int num, errors = 0;
+	for (num = 0; pathspec[num]; num++) {
+		int other, found_dup;
+
+		if (ps_matched[num])
+			continue;
+		/*
+		 * The caller might have fed identical pathspec
+		 * twice.  Do not barf on such a mistake.
+		 */
+		for (found_dup = other = 0;
+		     !found_dup && pathspec[other];
+		     other++) {
+			if (other == num || !ps_matched[other])
+				continue;
+			if (!strcmp(pathspec[other], pathspec[num]))
+				/*
+				 * Ok, we have a match already.
+				 */
+				found_dup = 1;
+		}
+		if (found_dup)
+			continue;
+
+		name = quote_path_relative(pathspec[num], -1, &sb, prefix);
+		error("pathspec '%s' did not match any file(s) known to git.",
+		      name);
+		errors++;
+	}
+	strbuf_release(&sb);
+	return errors;
+}
+
+static const char * const ls_files_usage[] = {
+	"git ls-files [options] [<file>...]",
+	NULL
+};
+
+static int option_parse_z(const struct option *opt,
+			  const char *arg, int unset)
+{
+	line_terminator = unset ? '\n' : '\0';
+
+	return 0;
+}
+
+static int option_parse_exclude(const struct option *opt,
+				const char *arg, int unset)
+{
+	struct exclude_list *list = opt->value;
+
+	exc_given = 1;
+	add_exclude(arg, "", 0, list);
+
+	return 0;
+}
+
+static int option_parse_exclude_from(const struct option *opt,
+				     const char *arg, int unset)
+{
+	struct dir_struct *dir = opt->value;
+
+	exc_given = 1;
+	add_excludes_from_file(dir, arg);
+
+	return 0;
+}
+
+static int option_parse_exclude_standard(const struct option *opt,
+					 const char *arg, int unset)
+{
+	struct dir_struct *dir = opt->value;
+
+	exc_given = 1;
+	setup_standard_excludes(dir);
+
+	return 0;
+}
+
+int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
+{
+	int require_work_tree = 0, show_tag = 0;
+	const char *max_prefix;
+	struct dir_struct dir;
+	struct option builtin_ls_files_options[] = {
+		{ OPTION_CALLBACK, 'z', NULL, NULL, NULL,
+			"paths are separated with NUL character",
+			PARSE_OPT_NOARG, option_parse_z },
+		OPT_BOOLEAN('t', NULL, &show_tag,
+			"identify the file status with tags"),
+		OPT_BOOLEAN('v', NULL, &show_valid_bit,
+			"use lowercase letters for 'assume unchanged' files"),
+		OPT_BOOLEAN('c', "cached", &show_cached,
+			"show cached files in the output (default)"),
+		OPT_BOOLEAN('d', "deleted", &show_deleted,
+			"show deleted files in the output"),
+		OPT_BOOLEAN('m', "modified", &show_modified,
+			"show modified files in the output"),
+		OPT_BOOLEAN('o', "others", &show_others,
+			"show other files in the output"),
+		OPT_BIT('i', "ignored", &dir.flags,
+			"show ignored files in the output",
+			DIR_SHOW_IGNORED),
+		OPT_BOOLEAN('s', "stage", &show_stage,
+			"show staged contents' object name in the output"),
+		OPT_BOOLEAN('k', "killed", &show_killed,
+			"show files on the filesystem that need to be removed"),
+		OPT_BIT(0, "directory", &dir.flags,
+			"show 'other' directories' name only",
+			DIR_SHOW_OTHER_DIRECTORIES),
+		OPT_NEGBIT(0, "empty-directory", &dir.flags,
+			"don't show empty directories",
+			DIR_HIDE_EMPTY_DIRECTORIES),
+		OPT_BOOLEAN('u', "unmerged", &show_unmerged,
+			"show unmerged files in the output"),
+		OPT_BOOLEAN(0, "resolve-undo", &show_resolve_undo,
+			    "show resolve-undo information"),
+		{ OPTION_CALLBACK, 'x', "exclude", &dir.exclude_list[EXC_CMDL], "pattern",
+			"skip files matching pattern",
+			0, option_parse_exclude },
+		{ OPTION_CALLBACK, 'X', "exclude-from", &dir, "file",
+			"exclude patterns are read from <file>",
+			0, option_parse_exclude_from },
+		OPT_STRING(0, "exclude-per-directory", &dir.exclude_per_dir, "file",
+			"read additional per-directory exclude patterns in <file>"),
+		{ OPTION_CALLBACK, 0, "exclude-standard", &dir, NULL,
+			"add the standard git exclusions",
+			PARSE_OPT_NOARG, option_parse_exclude_standard },
+		{ OPTION_SET_INT, 0, "full-name", &prefix_len, NULL,
+			"make the output relative to the project top directory",
+			PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL },
+		OPT_BOOLEAN(0, "error-unmatch", &error_unmatch,
+			"if any <file> is not in the index, treat this as an error"),
+		OPT_STRING(0, "with-tree", &with_tree, "tree-ish",
+			"pretend that paths removed since <tree-ish> are still present"),
+		OPT__ABBREV(&abbrev),
+		OPT_BOOLEAN(0, "debug", &debug_mode, "show debugging data"),
+		OPT_END()
+	};
+
+	if (argc == 2 && !strcmp(argv[1], "-h"))
+		usage_with_options(ls_files_usage, builtin_ls_files_options);
+
+	memset(&dir, 0, sizeof(dir));
+	prefix = cmd_prefix;
+	if (prefix)
+		prefix_len = strlen(prefix);
+	git_config(git_default_config, NULL);
+
+	if (read_cache() < 0)
+		die("index file corrupt");
+
+	argc = parse_options(argc, argv, prefix, builtin_ls_files_options,
+			ls_files_usage, 0);
+	if (show_tag || show_valid_bit) {
+		tag_cached = "H ";
+		tag_unmerged = "M ";
+		tag_removed = "R ";
+		tag_modified = "C ";
+		tag_other = "? ";
+		tag_killed = "K ";
+		tag_skip_worktree = "S ";
+		tag_resolve_undo = "U ";
+	}
+	if (show_modified || show_others || show_deleted || (dir.flags & DIR_SHOW_IGNORED) || show_killed)
+		require_work_tree = 1;
+	if (show_unmerged)
+		/*
+		 * There's no point in showing unmerged unless
+		 * you also show the stage information.
+		 */
+		show_stage = 1;
+	if (dir.exclude_per_dir)
+		exc_given = 1;
+
+	if (require_work_tree && !is_inside_work_tree())
+		setup_work_tree();
+
+	pathspec = get_pathspec(prefix, argv);
+
+	/* be nice with submodule paths ending in a slash */
+	if (pathspec)
+		strip_trailing_slash_from_submodules();
+
+	/* Find common prefix for all pathspec's */
+	max_prefix = common_prefix(pathspec);
+	max_prefix_len = max_prefix ? strlen(max_prefix) : 0;
+
+	/* Treat unmatching pathspec elements as errors */
+	if (pathspec && error_unmatch) {
+		int num;
+		for (num = 0; pathspec[num]; num++)
+			;
+		ps_matched = xcalloc(1, num);
+	}
+
+	if ((dir.flags & DIR_SHOW_IGNORED) && !exc_given)
+		die("ls-files --ignored needs some exclude pattern");
+
+	/* With no flags, we default to showing the cached files */
+	if (!(show_stage | show_deleted | show_others | show_unmerged |
+	      show_killed | show_modified | show_resolve_undo))
+		show_cached = 1;
+
+	if (max_prefix)
+		prune_cache(max_prefix);
+	if (with_tree) {
+		/*
+		 * Basic sanity check; show-stages and show-unmerged
+		 * would not make any sense with this option.
+		 */
+		if (show_stage || show_unmerged)
+			die("ls-files --with-tree is incompatible with -s or -u");
+		overlay_tree_on_cache(with_tree, max_prefix);
+	}
+	show_files(&dir);
+	if (show_resolve_undo)
+		show_ru_info();
+
+	if (ps_matched) {
+		int bad;
+		bad = report_path_error(ps_matched, pathspec, prefix);
+		if (bad)
+			fprintf(stderr, "Did you forget to 'git add'?\n");
+
+		return bad ? 1 : 0;
+	}
+
+	return 0;
+}
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
new file mode 100644
index 0000000..41c88a9
--- /dev/null
+++ b/builtin/ls-remote.c
@@ -0,0 +1,136 @@
+#include "builtin.h"
+#include "cache.h"
+#include "transport.h"
+#include "remote.h"
+
+static const char ls_remote_usage[] =
+"git ls-remote [--heads] [--tags]  [-u <exec> | --upload-pack <exec>]\n"
+"                     [-q|--quiet] [--exit-code] [<repository> [<refs>...]]";
+
+/*
+ * Is there one among the list of patterns that match the tail part
+ * of the path?
+ */
+static int tail_match(const char **pattern, const char *path)
+{
+	const char *p;
+	char pathbuf[PATH_MAX];
+
+	if (!pattern)
+		return 1; /* no restriction */
+
+	if (snprintf(pathbuf, sizeof(pathbuf), "/%s", path) > sizeof(pathbuf))
+		return error("insanely long ref %.*s...", 20, path);
+	while ((p = *(pattern++)) != NULL) {
+		if (!fnmatch(p, pathbuf, 0))
+			return 1;
+	}
+	return 0;
+}
+
+int cmd_ls_remote(int argc, const char **argv, const char *prefix)
+{
+	int i;
+	const char *dest = NULL;
+	unsigned flags = 0;
+	int get_url = 0;
+	int quiet = 0;
+	int status = 0;
+	const char *uploadpack = NULL;
+	const char **pattern = NULL;
+
+	struct remote *remote;
+	struct transport *transport;
+	const struct ref *ref;
+
+	if (argc == 2 && !strcmp("-h", argv[1]))
+		usage(ls_remote_usage);
+
+	for (i = 1; i < argc; i++) {
+		const char *arg = argv[i];
+
+		if (*arg == '-') {
+			if (!prefixcmp(arg, "--upload-pack=")) {
+				uploadpack = arg + 14;
+				continue;
+			}
+			if (!prefixcmp(arg, "--exec=")) {
+				uploadpack = arg + 7;
+				continue;
+			}
+			if (!strcmp("--tags", arg) || !strcmp("-t", arg)) {
+				flags |= REF_TAGS;
+				continue;
+			}
+			if (!strcmp("--heads", arg) || !strcmp("-h", arg)) {
+				flags |= REF_HEADS;
+				continue;
+			}
+			if (!strcmp("--refs", arg)) {
+				flags |= REF_NORMAL;
+				continue;
+			}
+			if (!strcmp("--quiet", arg) || !strcmp("-q", arg)) {
+				quiet = 1;
+				continue;
+			}
+			if (!strcmp("--get-url", arg)) {
+				get_url = 1;
+				continue;
+			}
+			if (!strcmp("--exit-code", arg)) {
+				/* return this code if no refs are reported */
+				status = 2;
+				continue;
+			}
+			usage(ls_remote_usage);
+		}
+		dest = arg;
+		i++;
+		break;
+	}
+
+	if (argv[i]) {
+		int j;
+		pattern = xcalloc(sizeof(const char *), argc - i + 1);
+		for (j = i; j < argc; j++) {
+			int len = strlen(argv[j]);
+			char *p = xmalloc(len + 3);
+			sprintf(p, "*/%s", argv[j]);
+			pattern[j - i] = p;
+		}
+	}
+	remote = remote_get(dest);
+	if (!remote) {
+		if (dest)
+			die("bad repository '%s'", dest);
+		die("No remote configured to list refs from.");
+	}
+	if (!remote->url_nr)
+		die("remote %s has no configured URL", dest);
+
+	if (get_url) {
+		printf("%s\n", *remote->url);
+		return 0;
+	}
+
+	transport = transport_get(remote, NULL);
+	if (uploadpack != NULL)
+		transport_set_option(transport, TRANS_OPT_UPLOADPACK, uploadpack);
+
+	ref = transport_get_remote_refs(transport);
+	if (transport_disconnect(transport))
+		return 1;
+
+	if (!dest && !quiet)
+		fprintf(stderr, "From %s\n", *remote->url);
+	for ( ; ref; ref = ref->next) {
+		if (!check_ref_type(ref, flags))
+			continue;
+		if (!tail_match(pattern, ref->name))
+			continue;
+		printf("%s	%s\n", sha1_to_hex(ref->old_sha1), ref->name);
+		status = 0; /* we found something */
+	}
+	return status;
+}
diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c
new file mode 100644
index 0000000..6b666e1
--- /dev/null
+++ b/builtin/ls-tree.c
@@ -0,0 +1,177 @@
+/*
+ * GIT - The information manager from hell
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ */
+#include "cache.h"
+#include "blob.h"
+#include "tree.h"
+#include "commit.h"
+#include "quote.h"
+#include "builtin.h"
+#include "parse-options.h"
+
+static int line_termination = '\n';
+#define LS_RECURSIVE 1
+#define LS_TREE_ONLY 2
+#define LS_SHOW_TREES 4
+#define LS_NAME_ONLY 8
+#define LS_SHOW_SIZE 16
+static int abbrev;
+static int ls_options;
+static struct pathspec pathspec;
+static int chomp_prefix;
+static const char *ls_tree_prefix;
+
+static const  char * const ls_tree_usage[] = {
+	"git ls-tree [<options>] <tree-ish> [<path>...]",
+	NULL
+};
+
+static int show_recursive(const char *base, int baselen, const char *pathname)
+{
+	const char **s;
+
+	if (ls_options & LS_RECURSIVE)
+		return 1;
+
+	s = pathspec.raw;
+	if (!s)
+		return 0;
+
+	for (;;) {
+		const char *spec = *s++;
+		int len, speclen;
+
+		if (!spec)
+			return 0;
+		if (strncmp(base, spec, baselen))
+			continue;
+		len = strlen(pathname);
+		spec += baselen;
+		speclen = strlen(spec);
+		if (speclen <= len)
+			continue;
+		if (spec[len] && spec[len] != '/')
+			continue;
+		if (memcmp(pathname, spec, len))
+			continue;
+		return 1;
+	}
+}
+
+static int show_tree(const unsigned char *sha1, const char *base, int baselen,
+		const char *pathname, unsigned mode, int stage, void *context)
+{
+	int retval = 0;
+	const char *type = blob_type;
+
+	if (S_ISGITLINK(mode)) {
+		/*
+		 * Maybe we want to have some recursive version here?
+		 *
+		 * Something similar to this incomplete example:
+		 *
+		if (show_subprojects(base, baselen, pathname))
+			retval = READ_TREE_RECURSIVE;
+		 *
+		 */
+		type = commit_type;
+	} else if (S_ISDIR(mode)) {
+		if (show_recursive(base, baselen, pathname)) {
+			retval = READ_TREE_RECURSIVE;
+			if (!(ls_options & LS_SHOW_TREES))
+				return retval;
+		}
+		type = tree_type;
+	}
+	else if (ls_options & LS_TREE_ONLY)
+		return 0;
+
+	if (chomp_prefix &&
+	    (baselen < chomp_prefix || memcmp(ls_tree_prefix, base, chomp_prefix)))
+		return 0;
+
+	if (!(ls_options & LS_NAME_ONLY)) {
+		if (ls_options & LS_SHOW_SIZE) {
+			char size_text[24];
+			if (!strcmp(type, blob_type)) {
+				unsigned long size;
+				if (sha1_object_info(sha1, &size) == OBJ_BAD)
+					strcpy(size_text, "BAD");
+				else
+					snprintf(size_text, sizeof(size_text),
+						 "%lu", size);
+			} else
+				strcpy(size_text, "-");
+			printf("%06o %s %s %7s\t", mode, type,
+			       find_unique_abbrev(sha1, abbrev),
+			       size_text);
+		} else
+			printf("%06o %s %s\t", mode, type,
+			       find_unique_abbrev(sha1, abbrev));
+	}
+	write_name_quotedpfx(base + chomp_prefix, baselen - chomp_prefix,
+			  pathname, stdout, line_termination);
+	return retval;
+}
+
+int cmd_ls_tree(int argc, const char **argv, const char *prefix)
+{
+	unsigned char sha1[20];
+	struct tree *tree;
+	int i, full_tree = 0;
+	const struct option ls_tree_options[] = {
+		OPT_BIT('d', NULL, &ls_options, "only show trees",
+			LS_TREE_ONLY),
+		OPT_BIT('r', NULL, &ls_options, "recurse into subtrees",
+			LS_RECURSIVE),
+		OPT_BIT('t', NULL, &ls_options, "show trees when recursing",
+			LS_SHOW_TREES),
+		OPT_SET_INT('z', NULL, &line_termination,
+			    "terminate entries with NUL byte", 0),
+		OPT_BIT('l', "long", &ls_options, "include object size",
+			LS_SHOW_SIZE),
+		OPT_BIT(0, "name-only", &ls_options, "list only filenames",
+			LS_NAME_ONLY),
+		OPT_BIT(0, "name-status", &ls_options, "list only filenames",
+			LS_NAME_ONLY),
+		OPT_SET_INT(0, "full-name", &chomp_prefix,
+			    "use full path names", 0),
+		OPT_BOOLEAN(0, "full-tree", &full_tree,
+			    "list entire tree; not just current directory "
+			    "(implies --full-name)"),
+		OPT__ABBREV(&abbrev),
+		OPT_END()
+	};
+
+	git_config(git_default_config, NULL);
+	ls_tree_prefix = prefix;
+	if (prefix && *prefix)
+		chomp_prefix = strlen(prefix);
+
+	argc = parse_options(argc, argv, prefix, ls_tree_options,
+			     ls_tree_usage, 0);
+	if (full_tree) {
+		ls_tree_prefix = prefix = NULL;
+		chomp_prefix = 0;
+	}
+	/* -d -r should imply -t, but -d by itself should not have to. */
+	if ( (LS_TREE_ONLY|LS_RECURSIVE) ==
+	    ((LS_TREE_ONLY|LS_RECURSIVE) & ls_options))
+		ls_options |= LS_SHOW_TREES;
+
+	if (argc < 1)
+		usage_with_options(ls_tree_usage, ls_tree_options);
+	if (get_sha1(argv[0], sha1))
+		die("Not a valid object name %s", argv[0]);
+
+	init_pathspec(&pathspec, get_pathspec(prefix, argv + 1));
+	for (i = 0; i < pathspec.nr; i++)
+		pathspec.items[i].use_wildcard = 0;
+	pathspec.has_wildcard = 0;
+	tree = parse_tree_indirect(sha1);
+	if (!tree)
+		die("not a tree object");
+	return !!read_tree_recursive(tree, "", 0, 0, &pathspec, show_tree, NULL);
+}
diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
new file mode 100644
index 0000000..eaf9e15
--- /dev/null
+++ b/builtin/mailinfo.c
@@ -0,0 +1,1073 @@
+/*
+ * Another stupid program, this one parsing the headers of an
+ * email to figure out authorship and subject
+ */
+#include "cache.h"
+#include "builtin.h"
+#include "utf8.h"
+#include "strbuf.h"
+
+static FILE *cmitmsg, *patchfile, *fin, *fout;
+
+static int keep_subject;
+static int keep_non_patch_brackets_in_subject;
+static const char *metainfo_charset;
+static struct strbuf line = STRBUF_INIT;
+static struct strbuf name = STRBUF_INIT;
+static struct strbuf email = STRBUF_INIT;
+
+static enum  {
+	TE_DONTCARE, TE_QP, TE_BASE64
+} transfer_encoding;
+static enum  {
+	TYPE_TEXT, TYPE_OTHER
+} message_type;
+
+static struct strbuf charset = STRBUF_INIT;
+static int patch_lines;
+static struct strbuf **p_hdr_data, **s_hdr_data;
+static int use_scissors;
+static int use_inbody_headers = 1;
+
+#define MAX_HDR_PARSED 10
+#define MAX_BOUNDARIES 5
+
+static void cleanup_space(struct strbuf *sb);
+
+
+static void get_sane_name(struct strbuf *out, struct strbuf *name, struct strbuf *email)
+{
+	struct strbuf *src = name;
+	if (name->len < 3 || 60 < name->len || strchr(name->buf, '@') ||
+		strchr(name->buf, '<') || strchr(name->buf, '>'))
+		src = email;
+	else if (name == out)
+		return;
+	strbuf_reset(out);
+	strbuf_addbuf(out, src);
+}
+
+static void parse_bogus_from(const struct strbuf *line)
+{
+	/* John Doe <johndoe> */
+
+	char *bra, *ket;
+	/* This is fallback, so do not bother if we already have an
+	 * e-mail address.
+	 */
+	if (email.len)
+		return;
+
+	bra = strchr(line->buf, '<');
+	if (!bra)
+		return;
+	ket = strchr(bra, '>');
+	if (!ket)
+		return;
+
+	strbuf_reset(&email);
+	strbuf_add(&email, bra + 1, ket - bra - 1);
+
+	strbuf_reset(&name);
+	strbuf_add(&name, line->buf, bra - line->buf);
+	strbuf_trim(&name);
+	get_sane_name(&name, &name, &email);
+}
+
+static void handle_from(const struct strbuf *from)
+{
+	char *at;
+	size_t el;
+	struct strbuf f;
+
+	strbuf_init(&f, from->len);
+	strbuf_addbuf(&f, from);
+
+	at = strchr(f.buf, '@');
+	if (!at) {
+		parse_bogus_from(from);
+		return;
+	}
+
+	/*
+	 * If we already have one email, don't take any confusing lines
+	 */
+	if (email.len && strchr(at + 1, '@')) {
+		strbuf_release(&f);
+		return;
+	}
+
+	/* Pick up the string around '@', possibly delimited with <>
+	 * pair; that is the email part.
+	 */
+	while (at > f.buf) {
+		char c = at[-1];
+		if (isspace(c))
+			break;
+		if (c == '<') {
+			at[-1] = ' ';
+			break;
+		}
+		at--;
+	}
+	el = strcspn(at, " \n\t\r\v\f>");
+	strbuf_reset(&email);
+	strbuf_add(&email, at, el);
+	strbuf_remove(&f, at - f.buf, el + (at[el] ? 1 : 0));
+
+	/* The remainder is name.  It could be
+	 *
+	 * - "John Doe <john.doe@xz>"			(a), or
+	 * - "john.doe@xz (John Doe)"			(b), or
+	 * - "John (zzz) Doe <john.doe@xz> (Comment)"	(c)
+	 *
+	 * but we have removed the email part, so
+	 *
+	 * - remove extra spaces which could stay after email (case 'c'), and
+	 * - trim from both ends, possibly removing the () pair at the end
+	 *   (cases 'a' and 'b').
+	 */
+	cleanup_space(&f);
+	strbuf_trim(&f);
+	if (f.buf[0] == '(' && f.len && f.buf[f.len - 1] == ')') {
+		strbuf_remove(&f, 0, 1);
+		strbuf_setlen(&f, f.len - 1);
+	}
+
+	get_sane_name(&name, &f, &email);
+	strbuf_release(&f);
+}
+
+static void handle_header(struct strbuf **out, const struct strbuf *line)
+{
+	if (!*out) {
+		*out = xmalloc(sizeof(struct strbuf));
+		strbuf_init(*out, line->len);
+	} else
+		strbuf_reset(*out);
+
+	strbuf_addbuf(*out, line);
+}
+
+/* NOTE NOTE NOTE.  We do not claim we do full MIME.  We just attempt
+ * to have enough heuristics to grok MIME encoded patches often found
+ * on our mailing lists.  For example, we do not even treat header lines
+ * case insensitively.
+ */
+
+static int slurp_attr(const char *line, const char *name, struct strbuf *attr)
+{
+	const char *ends, *ap = strcasestr(line, name);
+	size_t sz;
+
+	if (!ap) {
+		strbuf_setlen(attr, 0);
+		return 0;
+	}
+	ap += strlen(name);
+	if (*ap == '"') {
+		ap++;
+		ends = "\"";
+	}
+	else
+		ends = "; \t";
+	sz = strcspn(ap, ends);
+	strbuf_add(attr, ap, sz);
+	return 1;
+}
+
+static struct strbuf *content[MAX_BOUNDARIES];
+
+static struct strbuf **content_top = content;
+
+static void handle_content_type(struct strbuf *line)
+{
+	struct strbuf *boundary = xmalloc(sizeof(struct strbuf));
+	strbuf_init(boundary, line->len);
+
+	if (!strcasestr(line->buf, "text/"))
+		 message_type = TYPE_OTHER;
+	if (slurp_attr(line->buf, "boundary=", boundary)) {
+		strbuf_insert(boundary, 0, "--", 2);
+		if (++content_top > &content[MAX_BOUNDARIES]) {
+			fprintf(stderr, "Too many boundaries to handle\n");
+			exit(1);
+		}
+		*content_top = boundary;
+		boundary = NULL;
+	}
+	slurp_attr(line->buf, "charset=", &charset);
+
+	if (boundary) {
+		strbuf_release(boundary);
+		free(boundary);
+	}
+}
+
+static void handle_content_transfer_encoding(const struct strbuf *line)
+{
+	if (strcasestr(line->buf, "base64"))
+		transfer_encoding = TE_BASE64;
+	else if (strcasestr(line->buf, "quoted-printable"))
+		transfer_encoding = TE_QP;
+	else
+		transfer_encoding = TE_DONTCARE;
+}
+
+static int is_multipart_boundary(const struct strbuf *line)
+{
+	return (((*content_top)->len <= line->len) &&
+		!memcmp(line->buf, (*content_top)->buf, (*content_top)->len));
+}
+
+static void cleanup_subject(struct strbuf *subject)
+{
+	size_t at = 0;
+
+	while (at < subject->len) {
+		char *pos;
+		size_t remove;
+
+		switch (subject->buf[at]) {
+		case 'r': case 'R':
+			if (subject->len <= at + 3)
+				break;
+			if (!memcmp(subject->buf + at + 1, "e:", 2)) {
+				strbuf_remove(subject, at, 3);
+				continue;
+			}
+			at++;
+			break;
+		case ' ': case '\t': case ':':
+			strbuf_remove(subject, at, 1);
+			continue;
+		case '[':
+			pos = strchr(subject->buf + at, ']');
+			if (!pos)
+				break;
+			remove = pos - subject->buf + at + 1;
+			if (!keep_non_patch_brackets_in_subject ||
+			    (7 <= remove &&
+			     memmem(subject->buf + at, remove, "PATCH", 5)))
+				strbuf_remove(subject, at, remove);
+			else {
+				at += remove;
+				/*
+				 * If the input had a space after the ], keep
+				 * it.  We don't bother with finding the end of
+				 * the space, since we later normalize it
+				 * anyway.
+				 */
+				if (isspace(subject->buf[at]))
+					at += 1;
+			}
+			continue;
+		}
+		break;
+	}
+	strbuf_trim(subject);
+}
+
+static void cleanup_space(struct strbuf *sb)
+{
+	size_t pos, cnt;
+	for (pos = 0; pos < sb->len; pos++) {
+		if (isspace(sb->buf[pos])) {
+			sb->buf[pos] = ' ';
+			for (cnt = 0; isspace(sb->buf[pos + cnt + 1]); cnt++);
+			strbuf_remove(sb, pos + 1, cnt);
+		}
+	}
+}
+
+static void decode_header(struct strbuf *line);
+static const char *header[MAX_HDR_PARSED] = {
+	"From","Subject","Date",
+};
+
+static inline int cmp_header(const struct strbuf *line, const char *hdr)
+{
+	int len = strlen(hdr);
+	return !strncasecmp(line->buf, hdr, len) && line->len > len &&
+			line->buf[len] == ':' && isspace(line->buf[len + 1]);
+}
+
+static int check_header(const struct strbuf *line,
+				struct strbuf *hdr_data[], int overwrite)
+{
+	int i, ret = 0, len;
+	struct strbuf sb = STRBUF_INIT;
+	/* search for the interesting parts */
+	for (i = 0; header[i]; i++) {
+		int len = strlen(header[i]);
+		if ((!hdr_data[i] || overwrite) && cmp_header(line, header[i])) {
+			/* Unwrap inline B and Q encoding, and optionally
+			 * normalize the meta information to utf8.
+			 */
+			strbuf_add(&sb, line->buf + len + 2, line->len - len - 2);
+			decode_header(&sb);
+			handle_header(&hdr_data[i], &sb);
+			ret = 1;
+			goto check_header_out;
+		}
+	}
+
+	/* Content stuff */
+	if (cmp_header(line, "Content-Type")) {
+		len = strlen("Content-Type: ");
+		strbuf_add(&sb, line->buf + len, line->len - len);
+		decode_header(&sb);
+		strbuf_insert(&sb, 0, "Content-Type: ", len);
+		handle_content_type(&sb);
+		ret = 1;
+		goto check_header_out;
+	}
+	if (cmp_header(line, "Content-Transfer-Encoding")) {
+		len = strlen("Content-Transfer-Encoding: ");
+		strbuf_add(&sb, line->buf + len, line->len - len);
+		decode_header(&sb);
+		handle_content_transfer_encoding(&sb);
+		ret = 1;
+		goto check_header_out;
+	}
+
+	/* for inbody stuff */
+	if (!prefixcmp(line->buf, ">From") && isspace(line->buf[5])) {
+		ret = 1; /* Should this return 0? */
+		goto check_header_out;
+	}
+	if (!prefixcmp(line->buf, "[PATCH]") && isspace(line->buf[7])) {
+		for (i = 0; header[i]; i++) {
+			if (!memcmp("Subject", header[i], 7)) {
+				handle_header(&hdr_data[i], line);
+				ret = 1;
+				goto check_header_out;
+			}
+		}
+	}
+
+check_header_out:
+	strbuf_release(&sb);
+	return ret;
+}
+
+static int is_rfc2822_header(const struct strbuf *line)
+{
+	/*
+	 * The section that defines the loosest possible
+	 * field name is "3.6.8 Optional fields".
+	 *
+	 * optional-field = field-name ":" unstructured CRLF
+	 * field-name = 1*ftext
+	 * ftext = %d33-57 / %59-126
+	 */
+	int ch;
+	char *cp = line->buf;
+
+	/* Count mbox From headers as headers */
+	if (!prefixcmp(cp, "From ") || !prefixcmp(cp, ">From "))
+		return 1;
+
+	while ((ch = *cp++)) {
+		if (ch == ':')
+			return 1;
+		if ((33 <= ch && ch <= 57) ||
+		    (59 <= ch && ch <= 126))
+			continue;
+		break;
+	}
+	return 0;
+}
+
+static int read_one_header_line(struct strbuf *line, FILE *in)
+{
+	/* Get the first part of the line. */
+	if (strbuf_getline(line, in, '\n'))
+		return 0;
+
+	/*
+	 * Is it an empty line or not a valid rfc2822 header?
+	 * If so, stop here, and return false ("not a header")
+	 */
+	strbuf_rtrim(line);
+	if (!line->len || !is_rfc2822_header(line)) {
+		/* Re-add the newline */
+		strbuf_addch(line, '\n');
+		return 0;
+	}
+
+	/*
+	 * Now we need to eat all the continuation lines..
+	 * Yuck, 2822 header "folding"
+	 */
+	for (;;) {
+		int peek;
+		struct strbuf continuation = STRBUF_INIT;
+
+		peek = fgetc(in); ungetc(peek, in);
+		if (peek != ' ' && peek != '\t')
+			break;
+		if (strbuf_getline(&continuation, in, '\n'))
+			break;
+		continuation.buf[0] = ' ';
+		strbuf_rtrim(&continuation);
+		strbuf_addbuf(line, &continuation);
+	}
+
+	return 1;
+}
+
+static struct strbuf *decode_q_segment(const struct strbuf *q_seg, int rfc2047)
+{
+	const char *in = q_seg->buf;
+	int c;
+	struct strbuf *out = xmalloc(sizeof(struct strbuf));
+	strbuf_init(out, q_seg->len);
+
+	while ((c = *in++) != 0) {
+		if (c == '=') {
+			int d = *in++;
+			if (d == '\n' || !d)
+				break; /* drop trailing newline */
+			strbuf_addch(out, (hexval(d) << 4) | hexval(*in++));
+			continue;
+		}
+		if (rfc2047 && c == '_') /* rfc2047 4.2 (2) */
+			c = 0x20;
+		strbuf_addch(out, c);
+	}
+	return out;
+}
+
+static struct strbuf *decode_b_segment(const struct strbuf *b_seg)
+{
+	/* Decode in..ep, possibly in-place to ot */
+	int c, pos = 0, acc = 0;
+	const char *in = b_seg->buf;
+	struct strbuf *out = xmalloc(sizeof(struct strbuf));
+	strbuf_init(out, b_seg->len);
+
+	while ((c = *in++) != 0) {
+		if (c == '+')
+			c = 62;
+		else if (c == '/')
+			c = 63;
+		else if ('A' <= c && c <= 'Z')
+			c -= 'A';
+		else if ('a' <= c && c <= 'z')
+			c -= 'a' - 26;
+		else if ('0' <= c && c <= '9')
+			c -= '0' - 52;
+		else
+			continue; /* garbage */
+		switch (pos++) {
+		case 0:
+			acc = (c << 2);
+			break;
+		case 1:
+			strbuf_addch(out, (acc | (c >> 4)));
+			acc = (c & 15) << 4;
+			break;
+		case 2:
+			strbuf_addch(out, (acc | (c >> 2)));
+			acc = (c & 3) << 6;
+			break;
+		case 3:
+			strbuf_addch(out, (acc | c));
+			acc = pos = 0;
+			break;
+		}
+	}
+	return out;
+}
+
+/*
+ * When there is no known charset, guess.
+ *
+ * Right now we assume that if the target is UTF-8 (the default),
+ * and it already looks like UTF-8 (which includes US-ASCII as its
+ * subset, of course) then that is what it is and there is nothing
+ * to do.
+ *
+ * Otherwise, we default to assuming it is Latin1 for historical
+ * reasons.
+ */
+static const char *guess_charset(const struct strbuf *line, const char *target_charset)
+{
+	if (is_encoding_utf8(target_charset)) {
+		if (is_utf8(line->buf))
+			return NULL;
+	}
+	return "ISO8859-1";
+}
+
+static void convert_to_utf8(struct strbuf *line, const char *charset)
+{
+	char *out;
+
+	if (!charset || !*charset) {
+		charset = guess_charset(line, metainfo_charset);
+		if (!charset)
+			return;
+	}
+
+	if (!strcasecmp(metainfo_charset, charset))
+		return;
+	out = reencode_string(line->buf, metainfo_charset, charset);
+	if (!out)
+		die("cannot convert from %s to %s",
+		    charset, metainfo_charset);
+	strbuf_attach(line, out, strlen(out), strlen(out));
+}
+
+static int decode_header_bq(struct strbuf *it)
+{
+	char *in, *ep, *cp;
+	struct strbuf outbuf = STRBUF_INIT, *dec;
+	struct strbuf charset_q = STRBUF_INIT, piecebuf = STRBUF_INIT;
+	int rfc2047 = 0;
+
+	in = it->buf;
+	while (in - it->buf <= it->len && (ep = strstr(in, "=?")) != NULL) {
+		int encoding;
+		strbuf_reset(&charset_q);
+		strbuf_reset(&piecebuf);
+		rfc2047 = 1;
+
+		if (in != ep) {
+			/*
+			 * We are about to process an encoded-word
+			 * that begins at ep, but there is something
+			 * before the encoded word.
+			 */
+			char *scan;
+			for (scan = in; scan < ep; scan++)
+				if (!isspace(*scan))
+					break;
+
+			if (scan != ep || in == it->buf) {
+				/*
+				 * We should not lose that "something",
+				 * unless we have just processed an
+				 * encoded-word, and there is only LWS
+				 * before the one we are about to process.
+				 */
+				strbuf_add(&outbuf, in, ep - in);
+			}
+		}
+		/* E.g.
+		 * ep : "=?iso-2022-jp?B?GyR...?= foo"
+		 * ep : "=?ISO-8859-1?Q?Foo=FCbar?= baz"
+		 */
+		ep += 2;
+
+		if (ep - it->buf >= it->len || !(cp = strchr(ep, '?')))
+			goto decode_header_bq_out;
+
+		if (cp + 3 - it->buf > it->len)
+			goto decode_header_bq_out;
+		strbuf_add(&charset_q, ep, cp - ep);
+
+		encoding = cp[1];
+		if (!encoding || cp[2] != '?')
+			goto decode_header_bq_out;
+		ep = strstr(cp + 3, "?=");
+		if (!ep)
+			goto decode_header_bq_out;
+		strbuf_add(&piecebuf, cp + 3, ep - cp - 3);
+		switch (tolower(encoding)) {
+		default:
+			goto decode_header_bq_out;
+		case 'b':
+			dec = decode_b_segment(&piecebuf);
+			break;
+		case 'q':
+			dec = decode_q_segment(&piecebuf, 1);
+			break;
+		}
+		if (metainfo_charset)
+			convert_to_utf8(dec, charset_q.buf);
+
+		strbuf_addbuf(&outbuf, dec);
+		strbuf_release(dec);
+		free(dec);
+		in = ep + 2;
+	}
+	strbuf_addstr(&outbuf, in);
+	strbuf_reset(it);
+	strbuf_addbuf(it, &outbuf);
+decode_header_bq_out:
+	strbuf_release(&outbuf);
+	strbuf_release(&charset_q);
+	strbuf_release(&piecebuf);
+	return rfc2047;
+}
+
+static void decode_header(struct strbuf *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(struct strbuf *line)
+{
+	struct strbuf *ret;
+
+	switch (transfer_encoding) {
+	case TE_QP:
+		ret = decode_q_segment(line, 0);
+		break;
+	case TE_BASE64:
+		ret = decode_b_segment(line);
+		break;
+	case TE_DONTCARE:
+	default:
+		return;
+	}
+	strbuf_reset(line);
+	strbuf_addbuf(line, ret);
+	strbuf_release(ret);
+	free(ret);
+}
+
+static void handle_filter(struct strbuf *line);
+
+static int find_boundary(void)
+{
+	while (!strbuf_getline(&line, fin, '\n')) {
+		if (*content_top && is_multipart_boundary(&line))
+			return 1;
+	}
+	return 0;
+}
+
+static int handle_boundary(void)
+{
+	struct strbuf newline = STRBUF_INIT;
+
+	strbuf_addch(&newline, '\n');
+again:
+	if (line.len >= (*content_top)->len + 2 &&
+	    !memcmp(line.buf + (*content_top)->len, "--", 2)) {
+		/* we hit an end boundary */
+		/* pop the current boundary off the stack */
+		strbuf_release(*content_top);
+		free(*content_top);
+		*content_top = NULL;
+
+		/* technically won't happen as is_multipart_boundary()
+		   will fail first.  But just in case..
+		 */
+		if (--content_top < content) {
+			fprintf(stderr, "Detected mismatched boundaries, "
+					"can't recover\n");
+			exit(1);
+		}
+		handle_filter(&newline);
+		strbuf_release(&newline);
+
+		/* skip to the next boundary */
+		if (!find_boundary())
+			return 0;
+		goto again;
+	}
+
+	/* set some defaults */
+	transfer_encoding = TE_DONTCARE;
+	strbuf_reset(&charset);
+	message_type = TYPE_TEXT;
+
+	/* slurp in this section's info */
+	while (read_one_header_line(&line, fin))
+		check_header(&line, p_hdr_data, 0);
+
+	strbuf_release(&newline);
+	/* replenish line */
+	if (strbuf_getline(&line, fin, '\n'))
+		return 0;
+	strbuf_addch(&line, '\n');
+	return 1;
+}
+
+static inline int patchbreak(const struct strbuf *line)
+{
+	size_t i;
+
+	/* Beginning of a "diff -" header? */
+	if (!prefixcmp(line->buf, "diff -"))
+		return 1;
+
+	/* CVS "Index: " line? */
+	if (!prefixcmp(line->buf, "Index: "))
+		return 1;
+
+	/*
+	 * "--- <filename>" starts patches without headers
+	 * "---<sp>*" is a manual separator
+	 */
+	if (line->len < 4)
+		return 0;
+
+	if (!prefixcmp(line->buf, "---")) {
+		/* space followed by a filename? */
+		if (line->buf[3] == ' ' && !isspace(line->buf[4]))
+			return 1;
+		/* Just whitespace? */
+		for (i = 3; i < line->len; i++) {
+			unsigned char c = line->buf[i];
+			if (c == '\n')
+				return 1;
+			if (!isspace(c))
+				break;
+		}
+		return 0;
+	}
+	return 0;
+}
+
+static int is_scissors_line(const struct strbuf *line)
+{
+	size_t i, len = line->len;
+	int scissors = 0, gap = 0;
+	int first_nonblank = -1;
+	int last_nonblank = 0, visible, perforation = 0, in_perforation = 0;
+	const char *buf = line->buf;
+
+	for (i = 0; i < len; i++) {
+		if (isspace(buf[i])) {
+			if (in_perforation) {
+				perforation++;
+				gap++;
+			}
+			continue;
+		}
+		last_nonblank = i;
+		if (first_nonblank < 0)
+			first_nonblank = i;
+		if (buf[i] == '-') {
+			in_perforation = 1;
+			perforation++;
+			continue;
+		}
+		if (i + 1 < len &&
+		    (!memcmp(buf + i, ">8", 2) || !memcmp(buf + i, "8<", 2) ||
+		     !memcmp(buf + i, ">%", 2) || !memcmp(buf + i, "%<", 2))) {
+			in_perforation = 1;
+			perforation += 2;
+			scissors += 2;
+			i++;
+			continue;
+		}
+		in_perforation = 0;
+	}
+
+	/*
+	 * The mark must be at least 8 bytes long (e.g. "-- >8 --").
+	 * Even though there can be arbitrary cruft on the same line
+	 * (e.g. "cut here"), in order to avoid misidentification, the
+	 * perforation must occupy more than a third of the visible
+	 * width of the line, and dashes and scissors must occupy more
+	 * than half of the perforation.
+	 */
+
+	visible = last_nonblank - first_nonblank + 1;
+	return (scissors && 8 <= visible &&
+		visible < perforation * 3 &&
+		gap * 2 < perforation);
+}
+
+static int handle_commit_msg(struct strbuf *line)
+{
+	static int still_looking = 1;
+
+	if (!cmitmsg)
+		return 0;
+
+	if (still_looking) {
+		if (!line->len || (line->len == 1 && line->buf[0] == '\n'))
+			return 0;
+	}
+
+	if (use_inbody_headers && still_looking) {
+		still_looking = check_header(line, s_hdr_data, 0);
+		if (still_looking)
+			return 0;
+	} else
+		/* Only trim the first (blank) line of the commit message
+		 * when ignoring in-body headers.
+		 */
+		still_looking = 0;
+
+	/* normalize the log message to UTF-8. */
+	if (metainfo_charset)
+		convert_to_utf8(line, charset.buf);
+
+	if (use_scissors && is_scissors_line(line)) {
+		int i;
+		if (fseek(cmitmsg, 0L, SEEK_SET))
+			die_errno("Could not rewind output message file");
+		if (ftruncate(fileno(cmitmsg), 0))
+			die_errno("Could not truncate output message file at scissors");
+		still_looking = 1;
+
+		/*
+		 * We may have already read "secondary headers"; purge
+		 * them to give ourselves a clean restart.
+		 */
+		for (i = 0; header[i]; i++) {
+			if (s_hdr_data[i])
+				strbuf_release(s_hdr_data[i]);
+			s_hdr_data[i] = NULL;
+		}
+		return 0;
+	}
+
+	if (patchbreak(line)) {
+		fclose(cmitmsg);
+		cmitmsg = NULL;
+		return 1;
+	}
+
+	fputs(line->buf, cmitmsg);
+	return 0;
+}
+
+static void handle_patch(const struct strbuf *line)
+{
+	fwrite(line->buf, 1, line->len, patchfile);
+	patch_lines++;
+}
+
+static void handle_filter(struct strbuf *line)
+{
+	static int filter = 0;
+
+	/* filter tells us which part we left off on */
+	switch (filter) {
+	case 0:
+		if (!handle_commit_msg(line))
+			break;
+		filter++;
+	case 1:
+		handle_patch(line);
+		break;
+	}
+}
+
+static void handle_body(void)
+{
+	struct strbuf prev = STRBUF_INIT;
+
+	/* Skip up to the first boundary */
+	if (*content_top) {
+		if (!find_boundary())
+			goto handle_body_out;
+	}
+
+	do {
+		/* process any boundary lines */
+		if (*content_top && is_multipart_boundary(&line)) {
+			/* flush any leftover */
+			if (prev.len) {
+				handle_filter(&prev);
+				strbuf_reset(&prev);
+			}
+			if (!handle_boundary())
+				goto handle_body_out;
+		}
+
+		/* Unwrap transfer encoding */
+		decode_transfer_encoding(&line);
+
+		switch (transfer_encoding) {
+		case TE_BASE64:
+		case TE_QP:
+		{
+			struct strbuf **lines, **it, *sb;
+
+			/* Prepend any previous partial lines */
+			strbuf_insert(&line, 0, prev.buf, prev.len);
+			strbuf_reset(&prev);
+
+			/* binary data most likely doesn't have newlines */
+			if (message_type != TYPE_TEXT) {
+				handle_filter(&line);
+				break;
+			}
+			/*
+			 * This is a decoded line that may contain
+			 * multiple new lines.  Pass only one chunk
+			 * at a time to handle_filter()
+			 */
+			lines = strbuf_split(&line, '\n');
+			for (it = lines; (sb = *it); it++) {
+				if (*(it + 1) == NULL) /* The last line */
+					if (sb->buf[sb->len - 1] != '\n') {
+						/* Partial line, save it for later. */
+						strbuf_addbuf(&prev, sb);
+						break;
+					}
+				handle_filter(sb);
+			}
+			/*
+			 * The partial chunk is saved in "prev" and will be
+			 * appended by the next iteration of read_line_with_nul().
+			 */
+			strbuf_list_free(lines);
+			break;
+		}
+		default:
+			handle_filter(&line);
+		}
+
+	} while (!strbuf_getwholeline(&line, fin, '\n'));
+
+handle_body_out:
+	strbuf_release(&prev);
+}
+
+static void output_header_lines(FILE *fout, const char *hdr, const struct strbuf *data)
+{
+	const char *sp = data->buf;
+	while (1) {
+		char *ep = strchr(sp, '\n');
+		int len;
+		if (!ep)
+			len = strlen(sp);
+		else
+			len = ep - sp;
+		fprintf(fout, "%s: %.*s\n", hdr, len, sp);
+		if (!ep)
+			break;
+		sp = ep + 1;
+	}
+}
+
+static void handle_info(void)
+{
+	struct strbuf *hdr;
+	int i;
+
+	for (i = 0; header[i]; i++) {
+		/* only print inbody headers if we output a patch file */
+		if (patch_lines && s_hdr_data[i])
+			hdr = s_hdr_data[i];
+		else if (p_hdr_data[i])
+			hdr = p_hdr_data[i];
+		else
+			continue;
+
+		if (!memcmp(header[i], "Subject", 7)) {
+			if (!keep_subject) {
+				cleanup_subject(hdr);
+				cleanup_space(hdr);
+			}
+			output_header_lines(fout, "Subject", hdr);
+		} else if (!memcmp(header[i], "From", 4)) {
+			cleanup_space(hdr);
+			handle_from(hdr);
+			fprintf(fout, "Author: %s\n", name.buf);
+			fprintf(fout, "Email: %s\n", email.buf);
+		} else {
+			cleanup_space(hdr);
+			fprintf(fout, "%s: %s\n", header[i], hdr->buf);
+		}
+	}
+	fprintf(fout, "\n");
+}
+
+static int mailinfo(FILE *in, FILE *out, const char *msg, const char *patch)
+{
+	int peek;
+	fin = in;
+	fout = out;
+
+	cmitmsg = fopen(msg, "w");
+	if (!cmitmsg) {
+		perror(msg);
+		return -1;
+	}
+	patchfile = fopen(patch, "w");
+	if (!patchfile) {
+		perror(patch);
+		fclose(cmitmsg);
+		return -1;
+	}
+
+	p_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*p_hdr_data));
+	s_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*s_hdr_data));
+
+	do {
+		peek = fgetc(in);
+	} while (isspace(peek));
+	ungetc(peek, in);
+
+	/* process the email header */
+	while (read_one_header_line(&line, fin))
+		check_header(&line, p_hdr_data, 1);
+
+	handle_body();
+	handle_info();
+
+	return 0;
+}
+
+static int git_mailinfo_config(const char *var, const char *value, void *unused)
+{
+	if (prefixcmp(var, "mailinfo."))
+		return git_default_config(var, value, unused);
+	if (!strcmp(var, "mailinfo.scissors")) {
+		use_scissors = git_config_bool(var, value);
+		return 0;
+	}
+	/* perhaps others here */
+	return 0;
+}
+
+static const char mailinfo_usage[] =
+	"git mailinfo [-k|-b] [-u | --encoding=<encoding> | -n] [--scissors | --no-scissors] msg patch < mail >info";
+
+int cmd_mailinfo(int argc, const char **argv, const char *prefix)
+{
+	const char *def_charset;
+
+	/* NEEDSWORK: might want to do the optional .git/ directory
+	 * discovery
+	 */
+	git_config(git_mailinfo_config, NULL);
+
+	def_charset = get_commit_output_encoding();
+	metainfo_charset = def_charset;
+
+	while (1 < argc && argv[1][0] == '-') {
+		if (!strcmp(argv[1], "-k"))
+			keep_subject = 1;
+		else if (!strcmp(argv[1], "-b"))
+			keep_non_patch_brackets_in_subject = 1;
+		else if (!strcmp(argv[1], "-u"))
+			metainfo_charset = def_charset;
+		else if (!strcmp(argv[1], "-n"))
+			metainfo_charset = NULL;
+		else if (!prefixcmp(argv[1], "--encoding="))
+			metainfo_charset = argv[1] + 11;
+		else if (!strcmp(argv[1], "--scissors"))
+			use_scissors = 1;
+		else if (!strcmp(argv[1], "--no-scissors"))
+			use_scissors = 0;
+		else if (!strcmp(argv[1], "--no-inbody-headers"))
+			use_inbody_headers = 0;
+		else
+			usage(mailinfo_usage);
+		argc--; argv++;
+	}
+
+	if (argc != 3)
+		usage(mailinfo_usage);
+
+	return !!mailinfo(stdin, stdout, argv[1], argv[2]);
+}
diff --git a/builtin/mailsplit.c b/builtin/mailsplit.c
new file mode 100644
index 0000000..2d43278
--- /dev/null
+++ b/builtin/mailsplit.c
@@ -0,0 +1,309 @@
+/*
+ * Totally braindamaged mbox splitter program.
+ *
+ * It just splits a mbox into a list of files: "0001" "0002" ..
+ * so you can process them further from there.
+ */
+#include "cache.h"
+#include "builtin.h"
+#include "string-list.h"
+#include "strbuf.h"
+
+static const char git_mailsplit_usage[] =
+"git mailsplit [-d<prec>] [-f<n>] [-b] [--keep-cr] -o<directory> [(<mbox>|<Maildir>)...]";
+
+static int is_from_line(const char *line, int len)
+{
+	const char *colon;
+
+	if (len < 20 || memcmp("From ", line, 5))
+		return 0;
+
+	colon = line + len - 2;
+	line += 5;
+	for (;;) {
+		if (colon < line)
+			return 0;
+		if (*--colon == ':')
+			break;
+	}
+
+	if (!isdigit(colon[-4]) ||
+	    !isdigit(colon[-2]) ||
+	    !isdigit(colon[-1]) ||
+	    !isdigit(colon[ 1]) ||
+	    !isdigit(colon[ 2]))
+		return 0;
+
+	/* year */
+	if (strtol(colon+3, NULL, 10) <= 90)
+		return 0;
+
+	/* Ok, close enough */
+	return 1;
+}
+
+static struct strbuf buf = STRBUF_INIT;
+static int keep_cr;
+
+/* Called with the first line (potentially partial)
+ * already in buf[] -- normally that should begin with
+ * the Unix "From " line.  Write it into the specified
+ * file.
+ */
+static int split_one(FILE *mbox, const char *name, int allow_bare)
+{
+	FILE *output = NULL;
+	int fd;
+	int status = 0;
+	int is_bare = !is_from_line(buf.buf, buf.len);
+
+	if (is_bare && !allow_bare)
+		goto corrupt;
+
+	fd = open(name, O_WRONLY | O_CREAT | O_EXCL, 0666);
+	if (fd < 0)
+		die_errno("cannot open output file '%s'", name);
+	output = xfdopen(fd, "w");
+
+	/* Copy it out, while searching for a line that begins with
+	 * "From " and having something that looks like a date format.
+	 */
+	for (;;) {
+		if (!keep_cr && buf.len > 1 && buf.buf[buf.len-1] == '\n' &&
+			buf.buf[buf.len-2] == '\r') {
+			strbuf_setlen(&buf, buf.len-2);
+			strbuf_addch(&buf, '\n');
+		}
+
+		if (fwrite(buf.buf, 1, buf.len, output) != buf.len)
+			die_errno("cannot write output");
+
+		if (strbuf_getwholeline(&buf, mbox, '\n')) {
+			if (feof(mbox)) {
+				status = 1;
+				break;
+			}
+			die_errno("cannot read mbox");
+		}
+		if (!is_bare && is_from_line(buf.buf, buf.len))
+			break; /* done with one message */
+	}
+	fclose(output);
+	return status;
+
+ corrupt:
+	if (output)
+		fclose(output);
+	unlink(name);
+	fprintf(stderr, "corrupt mailbox\n");
+	exit(1);
+}
+
+static int populate_maildir_list(struct string_list *list, const char *path)
+{
+	DIR *dir;
+	struct dirent *dent;
+	char name[PATH_MAX];
+	char *subs[] = { "cur", "new", NULL };
+	char **sub;
+
+	for (sub = subs; *sub; ++sub) {
+		snprintf(name, sizeof(name), "%s/%s", path, *sub);
+		if ((dir = opendir(name)) == NULL) {
+			if (errno == ENOENT)
+				continue;
+			error("cannot opendir %s (%s)", name, strerror(errno));
+			return -1;
+		}
+
+		while ((dent = readdir(dir)) != NULL) {
+			if (dent->d_name[0] == '.')
+				continue;
+			snprintf(name, sizeof(name), "%s/%s", *sub, dent->d_name);
+			string_list_insert(list, name);
+		}
+
+		closedir(dir);
+	}
+
+	return 0;
+}
+
+static int split_maildir(const char *maildir, const char *dir,
+	int nr_prec, int skip)
+{
+	char file[PATH_MAX];
+	char name[PATH_MAX];
+	int ret = -1;
+	int i;
+	struct string_list list = STRING_LIST_INIT_DUP;
+
+	if (populate_maildir_list(&list, maildir) < 0)
+		goto out;
+
+	for (i = 0; i < list.nr; i++) {
+		FILE *f;
+		snprintf(file, sizeof(file), "%s/%s", maildir, list.items[i].string);
+		f = fopen(file, "r");
+		if (!f) {
+			error("cannot open mail %s (%s)", file, strerror(errno));
+			goto out;
+		}
+
+		if (strbuf_getwholeline(&buf, f, '\n')) {
+			error("cannot read mail %s (%s)", file, strerror(errno));
+			goto out;
+		}
+
+		sprintf(name, "%s/%0*d", dir, nr_prec, ++skip);
+		split_one(f, name, 1);
+
+		fclose(f);
+	}
+
+	ret = skip;
+out:
+	string_list_clear(&list, 1);
+	return ret;
+}
+
+static int split_mbox(const char *file, const char *dir, int allow_bare,
+		      int nr_prec, int skip)
+{
+	char name[PATH_MAX];
+	int ret = -1;
+	int peek;
+
+	FILE *f = !strcmp(file, "-") ? stdin : fopen(file, "r");
+	int file_done = 0;
+
+	if (!f) {
+		error("cannot open mbox %s", file);
+		goto out;
+	}
+
+	do {
+		peek = fgetc(f);
+	} while (isspace(peek));
+	ungetc(peek, f);
+
+	if (strbuf_getwholeline(&buf, f, '\n')) {
+		/* empty stdin is OK */
+		if (f != stdin) {
+			error("cannot read mbox %s", file);
+			goto out;
+		}
+		file_done = 1;
+	}
+
+	while (!file_done) {
+		sprintf(name, "%s/%0*d", dir, nr_prec, ++skip);
+		file_done = split_one(f, name, allow_bare);
+	}
+
+	if (f != stdin)
+		fclose(f);
+
+	ret = skip;
+out:
+	return ret;
+}
+
+int cmd_mailsplit(int argc, const char **argv, const char *prefix)
+{
+	int nr = 0, nr_prec = 4, num = 0;
+	int allow_bare = 0;
+	const char *dir = NULL;
+	const char **argp;
+	static const char *stdin_only[] = { "-", NULL };
+
+	for (argp = argv+1; *argp; argp++) {
+		const char *arg = *argp;
+
+		if (arg[0] != '-')
+			break;
+		/* do flags here */
+		if ( arg[1] == 'd' ) {
+			nr_prec = strtol(arg+2, NULL, 10);
+			if (nr_prec < 3 || 10 <= nr_prec)
+				usage(git_mailsplit_usage);
+			continue;
+		} else if ( arg[1] == 'f' ) {
+			nr = strtol(arg+2, NULL, 10);
+		} else if ( arg[1] == 'h' ) {
+			usage(git_mailsplit_usage);
+		} else if ( arg[1] == 'b' && !arg[2] ) {
+			allow_bare = 1;
+		} else if (!strcmp(arg, "--keep-cr")) {
+			keep_cr = 1;
+		} else if ( arg[1] == 'o' && arg[2] ) {
+			dir = arg+2;
+		} else if ( arg[1] == '-' && !arg[2] ) {
+			argp++;	/* -- marks end of options */
+			break;
+		} else {
+			die("unknown option: %s", arg);
+		}
+	}
+
+	if ( !dir ) {
+		/* Backwards compatibility: if no -o specified, accept
+		   <mbox> <dir> or just <dir> */
+		switch (argc - (argp-argv)) {
+		case 1:
+			dir = argp[0];
+			argp = stdin_only;
+			break;
+		case 2:
+			stdin_only[0] = argp[0];
+			dir = argp[1];
+			argp = stdin_only;
+			break;
+		default:
+			usage(git_mailsplit_usage);
+		}
+	} else {
+		/* New usage: if no more argument, parse stdin */
+		if ( !*argp )
+			argp = stdin_only;
+	}
+
+	while (*argp) {
+		const char *arg = *argp++;
+		struct stat argstat;
+		int ret = 0;
+
+		if (arg[0] == '-' && arg[1] == 0) {
+			ret = split_mbox(arg, dir, allow_bare, nr_prec, nr);
+			if (ret < 0) {
+				error("cannot split patches from stdin");
+				return 1;
+			}
+			num += (ret - nr);
+			nr = ret;
+			continue;
+		}
+
+		if (stat(arg, &argstat) == -1) {
+			error("cannot stat %s (%s)", arg, strerror(errno));
+			return 1;
+		}
+
+		if (S_ISDIR(argstat.st_mode))
+			ret = split_maildir(arg, dir, nr_prec, nr);
+		else
+			ret = split_mbox(arg, dir, allow_bare, nr_prec, nr);
+
+		if (ret < 0) {
+			error("cannot split patches from %s", arg);
+			return 1;
+		}
+		num += (ret - nr);
+		nr = ret;
+	}
+
+	printf("%d\n", num);
+
+	return 0;
+}
diff --git a/builtin/merge-base.c b/builtin/merge-base.c
new file mode 100644
index 0000000..4f30f1b
--- /dev/null
+++ b/builtin/merge-base.c
@@ -0,0 +1,102 @@
+#include "builtin.h"
+#include "cache.h"
+#include "commit.h"
+#include "parse-options.h"
+
+static int show_merge_base(struct commit **rev, int rev_nr, int show_all)
+{
+	struct commit_list *result;
+
+	result = get_merge_bases_many(rev[0], rev_nr - 1, rev + 1, 0);
+
+	if (!result)
+		return 1;
+
+	while (result) {
+		printf("%s\n", sha1_to_hex(result->item->object.sha1));
+		if (!show_all)
+			return 0;
+		result = result->next;
+	}
+
+	return 0;
+}
+
+static const char * const merge_base_usage[] = {
+	"git merge-base [-a|--all] <commit> <commit>...",
+	"git merge-base [-a|--all] --octopus <commit>...",
+	"git merge-base --independent <commit>...",
+	NULL
+};
+
+static struct commit *get_commit_reference(const char *arg)
+{
+	unsigned char revkey[20];
+	struct commit *r;
+
+	if (get_sha1(arg, revkey))
+		die("Not a valid object name %s", arg);
+	r = lookup_commit_reference(revkey);
+	if (!r)
+		die("Not a valid commit name %s", arg);
+
+	return r;
+}
+
+static int handle_octopus(int count, const char **args, int reduce, int show_all)
+{
+	struct commit_list *revs = NULL;
+	struct commit_list *result;
+	int i;
+
+	if (reduce)
+		show_all = 1;
+
+	for (i = count - 1; i >= 0; i--)
+		commit_list_insert(get_commit_reference(args[i]), &revs);
+
+	result = reduce ? reduce_heads(revs) : get_octopus_merge_bases(revs);
+
+	if (!result)
+		return 1;
+
+	while (result) {
+		printf("%s\n", sha1_to_hex(result->item->object.sha1));
+		if (!show_all)
+			return 0;
+		result = result->next;
+	}
+
+	return 0;
+}
+
+int cmd_merge_base(int argc, const char **argv, const char *prefix)
+{
+	struct commit **rev;
+	int rev_nr = 0;
+	int show_all = 0;
+	int octopus = 0;
+	int reduce = 0;
+
+	struct option options[] = {
+		OPT_BOOLEAN('a', "all", &show_all, "output all common ancestors"),
+		OPT_BOOLEAN(0, "octopus", &octopus, "find ancestors for a single n-way merge"),
+		OPT_BOOLEAN(0, "independent", &reduce, "list revs not reachable from others"),
+		OPT_END()
+	};
+
+	git_config(git_default_config, NULL);
+	argc = parse_options(argc, argv, prefix, options, merge_base_usage, 0);
+	if (!octopus && !reduce && argc < 2)
+		usage_with_options(merge_base_usage, options);
+	if (reduce && (show_all || octopus))
+		die("--independent cannot be used with other options");
+
+	if (octopus || reduce)
+		return handle_octopus(argc, argv, reduce, show_all);
+
+	rev = xmalloc(argc * sizeof(*rev));
+	while (argc-- > 0)
+		rev[rev_nr++] = get_commit_reference(*argv++);
+	return show_merge_base(rev, rev_nr, show_all);
+}
diff --git a/builtin/merge-file.c b/builtin/merge-file.c
new file mode 100644
index 0000000..6f0efef
--- /dev/null
+++ b/builtin/merge-file.c
@@ -0,0 +1,106 @@
+#include "builtin.h"
+#include "cache.h"
+#include "xdiff/xdiff.h"
+#include "xdiff-interface.h"
+#include "parse-options.h"
+
+static const char *const merge_file_usage[] = {
+	"git merge-file [options] [-L name1 [-L orig [-L name2]]] file1 orig_file file2",
+	NULL
+};
+
+static int label_cb(const struct option *opt, const char *arg, int unset)
+{
+	static int label_count = 0;
+	const char **names = (const char **)opt->value;
+
+	if (label_count >= 3)
+		return error("too many labels on the command line");
+	names[label_count++] = arg;
+	return 0;
+}
+
+int cmd_merge_file(int argc, const char **argv, const char *prefix)
+{
+	const char *names[3] = { NULL, NULL, NULL };
+	mmfile_t mmfs[3];
+	mmbuffer_t result = {NULL, 0};
+	xmparam_t xmp = {{0}};
+	int ret = 0, i = 0, to_stdout = 0;
+	int quiet = 0;
+	int prefixlen = 0;
+	struct option options[] = {
+		OPT_BOOLEAN('p', "stdout", &to_stdout, "send results to standard output"),
+		OPT_SET_INT(0, "diff3", &xmp.style, "use a diff3 based merge", XDL_MERGE_DIFF3),
+		OPT_SET_INT(0, "ours", &xmp.favor, "for conflicts, use our version",
+			    XDL_MERGE_FAVOR_OURS),
+		OPT_SET_INT(0, "theirs", &xmp.favor, "for conflicts, use their version",
+			    XDL_MERGE_FAVOR_THEIRS),
+		OPT_SET_INT(0, "union", &xmp.favor, "for conflicts, use a union version",
+			    XDL_MERGE_FAVOR_UNION),
+		OPT_INTEGER(0, "marker-size", &xmp.marker_size,
+			    "for conflicts, use this marker size"),
+		OPT__QUIET(&quiet, "do not warn about conflicts"),
+		OPT_CALLBACK('L', NULL, names, "name",
+			     "set labels for file1/orig_file/file2", &label_cb),
+		OPT_END(),
+	};
+
+	xmp.level = XDL_MERGE_ZEALOUS_ALNUM;
+	xmp.style = 0;
+	xmp.favor = 0;
+
+	if (startup_info->have_repository) {
+		/* Read the configuration file */
+		git_config(git_xmerge_config, NULL);
+		if (0 <= git_xmerge_style)
+			xmp.style = git_xmerge_style;
+	}
+
+	argc = parse_options(argc, argv, prefix, options, merge_file_usage, 0);
+	if (argc != 3)
+		usage_with_options(merge_file_usage, options);
+	if (quiet) {
+		if (!freopen("/dev/null", "w", stderr))
+			return error("failed to redirect stderr to /dev/null: "
+				     "%s", strerror(errno));
+	}
+
+	if (prefix)
+		prefixlen = strlen(prefix);
+
+	for (i = 0; i < 3; i++) {
+		const char *fname = prefix_filename(prefix, prefixlen, argv[i]);
+		if (!names[i])
+			names[i] = argv[i];
+		if (read_mmfile(mmfs + i, fname))
+			return -1;
+		if (buffer_is_binary(mmfs[i].ptr, mmfs[i].size))
+			return error("Cannot merge binary files: %s",
+					argv[i]);
+	}
+
+	xmp.ancestor = names[1];
+	xmp.file1 = names[0];
+	xmp.file2 = names[2];
+	ret = xdl_merge(mmfs + 1, mmfs + 0, mmfs + 2, &xmp, &result);
+
+	for (i = 0; i < 3; i++)
+		free(mmfs[i].ptr);
+
+	if (ret >= 0) {
+		const char *filename = argv[0];
+		FILE *f = to_stdout ? stdout : fopen(filename, "wb");
+
+		if (!f)
+			ret = error("Could not open %s for writing", filename);
+		else if (result.size &&
+			 fwrite(result.ptr, result.size, 1, f) != 1)
+			ret = error("Could not write to %s", filename);
+		else if (fclose(f))
+			ret = error("Could not close %s", filename);
+		free(result.ptr);
+	}
+
+	return ret;
+}
diff --git a/builtin/merge-index.c b/builtin/merge-index.c
new file mode 100644
index 0000000..2338832
--- /dev/null
+++ b/builtin/merge-index.c
@@ -0,0 +1,110 @@
+#include "builtin.h"
+#include "run-command.h"
+
+static const char *pgm;
+static int one_shot, quiet;
+static int err;
+
+static int merge_entry(int pos, const char *path)
+{
+	int found;
+	const char *arguments[] = { pgm, "", "", "", path, "", "", "", NULL };
+	char hexbuf[4][60];
+	char ownbuf[4][60];
+
+	if (pos >= active_nr)
+		die("git merge-index: %s not in the cache", path);
+	found = 0;
+	do {
+		struct cache_entry *ce = active_cache[pos];
+		int stage = ce_stage(ce);
+
+		if (strcmp(ce->name, path))
+			break;
+		found++;
+		strcpy(hexbuf[stage], sha1_to_hex(ce->sha1));
+		sprintf(ownbuf[stage], "%o", ce->ce_mode);
+		arguments[stage] = hexbuf[stage];
+		arguments[stage + 4] = ownbuf[stage];
+	} while (++pos < active_nr);
+	if (!found)
+		die("git merge-index: %s not in the cache", path);
+
+	if (run_command_v_opt(arguments, 0)) {
+		if (one_shot)
+			err++;
+		else {
+			if (!quiet)
+				die("merge program failed");
+			exit(1);
+		}
+	}
+	return found;
+}
+
+static void merge_file(const char *path)
+{
+	int pos = cache_name_pos(path, strlen(path));
+
+	/*
+	 * If it already exists in the cache as stage0, it's
+	 * already merged and there is nothing to do.
+	 */
+	if (pos < 0)
+		merge_entry(-pos-1, path);
+}
+
+static void merge_all(void)
+{
+	int i;
+	for (i = 0; i < active_nr; i++) {
+		struct cache_entry *ce = active_cache[i];
+		if (!ce_stage(ce))
+			continue;
+		i += merge_entry(i, ce->name)-1;
+	}
+}
+
+int cmd_merge_index(int argc, const char **argv, const char *prefix)
+{
+	int i, force_file = 0;
+
+	/* Without this we cannot rely on waitpid() to tell
+	 * what happened to our children.
+	 */
+	signal(SIGCHLD, SIG_DFL);
+
+	if (argc < 3)
+		usage("git merge-index [-o] [-q] <merge-program> (-a | [--] <filename>*)");
+
+	read_cache();
+
+	i = 1;
+	if (!strcmp(argv[i], "-o")) {
+		one_shot = 1;
+		i++;
+	}
+	if (!strcmp(argv[i], "-q")) {
+		quiet = 1;
+		i++;
+	}
+	pgm = argv[i++];
+	for (; i < argc; i++) {
+		const char *arg = argv[i];
+		if (!force_file && *arg == '-') {
+			if (!strcmp(arg, "--")) {
+				force_file = 1;
+				continue;
+			}
+			if (!strcmp(arg, "-a")) {
+				merge_all();
+				continue;
+			}
+			die("git merge-index: unknown option %s", arg);
+		}
+		merge_file(arg);
+	}
+	if (err && !quiet)
+		die("merge program failed");
+	return err;
+}
diff --git a/builtin/merge-ours.c b/builtin/merge-ours.c
new file mode 100644
index 0000000..6844116
--- /dev/null
+++ b/builtin/merge-ours.c
@@ -0,0 +1,34 @@
+/*
+ * Implementation of git-merge-ours.sh as builtin
+ *
+ * Copyright (c) 2007 Thomas Harning Jr
+ * Original:
+ * Original Copyright (c) 2005 Junio C Hamano
+ *
+ * Pretend we resolved the heads, but declare our tree trumps everybody else.
+ */
+#include "git-compat-util.h"
+#include "builtin.h"
+
+static const char builtin_merge_ours_usage[] =
+	"git merge-ours <base>... -- HEAD <remote>...";
+
+static const char *diff_index_args[] = {
+	"diff-index", "--quiet", "--cached", "HEAD", "--", NULL
+};
+#define NARGS (ARRAY_SIZE(diff_index_args) - 1)
+
+int cmd_merge_ours(int argc, const char **argv, const char *prefix)
+{
+	if (argc == 2 && !strcmp(argv[1], "-h"))
+		usage(builtin_merge_ours_usage);
+
+	/*
+	 * We need to exit with 2 if the index does not match our HEAD tree,
+	 * because the current index is what we will be committing as the
+	 * merge result.
+	 */
+	if (cmd_diff_index(NARGS, diff_index_args, prefix))
+		exit(2);
+	exit(0);
+}
diff --git a/builtin/merge-recursive.c b/builtin/merge-recursive.c
new file mode 100644
index 0000000..3a64f5d
--- /dev/null
+++ b/builtin/merge-recursive.c
@@ -0,0 +1,80 @@
+#include "builtin.h"
+#include "commit.h"
+#include "tag.h"
+#include "merge-recursive.h"
+#include "xdiff-interface.h"
+
+static const char builtin_merge_recursive_usage[] =
+	"git %s <base>... -- <head> <remote> ...";
+
+static const char *better_branch_name(const char *branch)
+{
+	static char githead_env[8 + 40 + 1];
+	char *name;
+
+	if (strlen(branch) != 40)
+		return branch;
+	sprintf(githead_env, "GITHEAD_%s", branch);
+	name = getenv(githead_env);
+	return name ? name : branch;
+}
+
+int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
+{
+	const unsigned char *bases[21];
+	unsigned bases_count = 0;
+	int i, failed;
+	unsigned char h1[20], h2[20];
+	struct merge_options o;
+	struct commit *result;
+
+	init_merge_options(&o);
+	if (argv[0] && !suffixcmp(argv[0], "-subtree"))
+		o.subtree_shift = "";
+
+	if (argc < 4)
+		usagef(builtin_merge_recursive_usage, argv[0]);
+
+	for (i = 1; i < argc; ++i) {
+		const char *arg = argv[i];
+
+		if (!prefixcmp(arg, "--")) {
+			if (!arg[2])
+				break;
+			if (parse_merge_opt(&o, arg + 2))
+				die("Unknown option %s", arg);
+			continue;
+		}
+		if (bases_count < ARRAY_SIZE(bases)-1) {
+			unsigned char *sha = xmalloc(20);
+			if (get_sha1(argv[i], sha))
+				die("Could not parse object '%s'", argv[i]);
+			bases[bases_count++] = sha;
+		}
+		else
+			warning("Cannot handle more than %d bases. "
+				"Ignoring %s.",
+				(int)ARRAY_SIZE(bases)-1, argv[i]);
+	}
+	if (argc - i != 3) /* "--" "<head>" "<remote>" */
+		die("Not handling anything other than two heads merge.");
+
+	o.branch1 = argv[++i];
+	o.branch2 = argv[++i];
+
+	if (get_sha1(o.branch1, h1))
+		die("Could not resolve ref '%s'", o.branch1);
+	if (get_sha1(o.branch2, h2))
+		die("Could not resolve ref '%s'", o.branch2);
+
+	o.branch1 = better_branch_name(o.branch1);
+	o.branch2 = better_branch_name(o.branch2);
+
+	if (o.verbosity >= 3)
+		printf("Merging %s with %s\n", o.branch1, o.branch2);
+
+	failed = merge_recursive_generic(&o, h1, h2, bases_count, bases, &result);
+	if (failed < 0)
+		return 128; /* die() error code */
+	return failed;
+}
diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c
new file mode 100644
index 0000000..897a563
--- /dev/null
+++ b/builtin/merge-tree.c
@@ -0,0 +1,358 @@
+#include "builtin.h"
+#include "tree-walk.h"
+#include "xdiff-interface.h"
+#include "blob.h"
+#include "exec_cmd.h"
+#include "merge-file.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";
+}
+
+static void *result(struct merge_list *entry, unsigned long *size)
+{
+	enum object_type type;
+	struct blob *base, *our, *their;
+	const char *path = entry->path;
+
+	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(path, base, our, their, size);
+}
+
+static void *origin(struct merge_list *entry, unsigned long *size)
+{
+	enum object_type type;
+	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 = 0;
+	memset(&xecfg, 0, sizeof(xecfg));
+	xecfg.ctxlen = 3;
+	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;
+	xdi_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)
+{
+	return	a->sha1 &&
+		b->sha1 &&
+		!hashcmp(a->sha1, b->sha1) &&
+		a->mode == b->mode;
+}
+
+static struct merge_list *create_entry(unsigned stage, unsigned mode, const unsigned char *sha1, const char *path)
+{
+	struct merge_list *res = xcalloc(1, sizeof(*res));
+
+	res->stage = stage;
+	res->path = path;
+	res->mode = mode;
+	res->blob = lookup_blob(sha1);
+	return res;
+}
+
+static char *traverse_path(const struct traverse_info *info, const struct name_entry *n)
+{
+	char *path = xmalloc(traverse_path_len(info, n) + 1);
+	return make_traverse_path(path, info, n);
+}
+
+static void resolve(const struct traverse_info *info, 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;
+
+	path = traverse_path(info, result);
+	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 struct traverse_info *info, struct name_entry n[3])
+{
+	char *newbase;
+	struct name_entry *p;
+	struct tree_desc t[3];
+	void *buf0, *buf1, *buf2;
+
+	if (!resolve_directories)
+		return 0;
+	p = n;
+	if (!p->mode) {
+		p++;
+		if (!p->mode)
+			p++;
+	}
+	if (!S_ISDIR(p->mode))
+		return 0;
+	newbase = traverse_path(info, p);
+	buf0 = fill_tree_descriptor(t+0, n[0].sha1);
+	buf1 = fill_tree_descriptor(t+1, n[1].sha1);
+	buf2 = fill_tree_descriptor(t+2, n[2].sha1);
+	merge_trees(t, newbase);
+
+	free(buf0);
+	free(buf1);
+	free(buf2);
+	free(newbase);
+	return 1;
+}
+
+
+static struct merge_list *link_entry(unsigned stage, const struct traverse_info *info, 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 = traverse_path(info, n);
+	link = create_entry(stage, n->mode, n->sha1, path);
+	link->link = entry;
+	return link;
+}
+
+static void unresolved(const struct traverse_info *info, struct name_entry n[3])
+{
+	struct merge_list *entry = NULL;
+
+	if (unresolved_directory(info, n))
+		return;
+
+	/*
+	 * 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, info, n + 2, entry);
+	entry = link_entry(2, info, n + 1, entry);
+	entry = link_entry(1, info, n + 0, entry);
+
+	add_merge_entry(entry);
+}
+
+/*
+ * Merge two trees together (t[1] and t[2]), using a common base (t[0])
+ * as the origin.
+ *
+ * This walks the (sorted) trees in lock-step, checking every possible
+ * name. Note that directories automatically sort differently from other
+ * files (see "base_name_compare"), so you'll never see file/directory
+ * conflicts, because they won't ever compare the same.
+ *
+ * IOW, if a directory changes to a filename, it will automatically be
+ * seen as the directory going away, and the filename being created.
+ *
+ * Think of this as a three-way diff.
+ *
+ * The output will be either:
+ *  - successful merge
+ *	 "0 mode sha1 filename"
+ *    NOTE NOTE NOTE! FIXME! We really really need to walk the index
+ *    in parallel with this too!
+ *
+ *  - conflict:
+ *	"1 mode sha1 filename"
+ *	"2 mode sha1 filename"
+ *	"3 mode sha1 filename"
+ *    where not all of the 1/2/3 lines may exist, of course.
+ *
+ * The successful merge rules are the same as for the three-way merge
+ * in git-read-tree.
+ */
+static int threeway_callback(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *info)
+{
+	/* Same in both? */
+	if (same_entry(entry+1, entry+2)) {
+		if (entry[0].sha1) {
+			resolve(info, NULL, entry+1);
+			return mask;
+		}
+	}
+
+	if (same_entry(entry+0, entry+1)) {
+		if (entry[2].sha1 && !S_ISDIR(entry[2].mode)) {
+			resolve(info, entry+1, entry+2);
+			return mask;
+		}
+	}
+
+	if (same_entry(entry+0, entry+2)) {
+		if (entry[1].sha1 && !S_ISDIR(entry[1].mode)) {
+			resolve(info, NULL, entry+1);
+			return mask;
+		}
+	}
+
+	unresolved(info, entry);
+	return mask;
+}
+
+static void merge_trees(struct tree_desc t[3], const char *base)
+{
+	struct traverse_info info;
+
+	setup_traverse_info(&info, base);
+	info.fn = threeway_callback;
+	traverse_trees(3, t, &info);
+}
+
+static void *get_tree_descriptor(struct tree_desc *desc, const char *rev)
+{
+	unsigned char sha1[20];
+	void *buf;
+
+	if (get_sha1(rev, sha1))
+		die("unknown rev %s", rev);
+	buf = fill_tree_descriptor(desc, sha1);
+	if (!buf)
+		die("%s is not a tree", rev);
+	return buf;
+}
+
+int cmd_merge_tree(int argc, const char **argv, const char *prefix)
+{
+	struct tree_desc t[3];
+	void *buf1, *buf2, *buf3;
+
+	if (argc != 4)
+		usage(merge_tree_usage);
+
+	buf1 = get_tree_descriptor(t+0, argv[1]);
+	buf2 = get_tree_descriptor(t+1, argv[2]);
+	buf3 = get_tree_descriptor(t+2, argv[3]);
+	merge_trees(t, "");
+	free(buf1);
+	free(buf2);
+	free(buf3);
+
+	show_result();
+	return 0;
+}
diff --git a/builtin/merge.c b/builtin/merge.c
new file mode 100644
index 0000000..dd50a0c
--- /dev/null
+++ b/builtin/merge.c
@@ -0,0 +1,1614 @@
+/*
+ * Builtin "git merge"
+ *
+ * Copyright (c) 2008 Miklos Vajna <vmiklos@frugalware.org>
+ *
+ * Based on git-merge.sh by Junio C Hamano.
+ */
+
+#include "cache.h"
+#include "parse-options.h"
+#include "builtin.h"
+#include "run-command.h"
+#include "diff.h"
+#include "refs.h"
+#include "commit.h"
+#include "diffcore.h"
+#include "revision.h"
+#include "unpack-trees.h"
+#include "cache-tree.h"
+#include "dir.h"
+#include "utf8.h"
+#include "log-tree.h"
+#include "color.h"
+#include "rerere.h"
+#include "help.h"
+#include "merge-recursive.h"
+#include "resolve-undo.h"
+#include "remote.h"
+#include "fmt-merge-msg.h"
+#include "gpg-interface.h"
+
+#define DEFAULT_TWOHEAD (1<<0)
+#define DEFAULT_OCTOPUS (1<<1)
+#define NO_FAST_FORWARD (1<<2)
+#define NO_TRIVIAL      (1<<3)
+
+struct strategy {
+	const char *name;
+	unsigned attr;
+};
+
+static const char * const builtin_merge_usage[] = {
+	"git merge [options] [<commit>...]",
+	"git merge [options] <msg> HEAD <commit>",
+	"git merge --abort",
+	NULL
+};
+
+static int show_diffstat = 1, shortlog_len = -1, squash;
+static int option_commit = 1, allow_fast_forward = 1;
+static int fast_forward_only, option_edit = -1;
+static int allow_trivial = 1, have_message;
+static int overwrite_ignore = 1;
+static struct strbuf merge_msg = STRBUF_INIT;
+static struct strategy **use_strategies;
+static size_t use_strategies_nr, use_strategies_alloc;
+static const char **xopts;
+static size_t xopts_nr, xopts_alloc;
+static const char *branch;
+static char *branch_mergeoptions;
+static int option_renormalize;
+static int verbosity;
+static int allow_rerere_auto;
+static int abort_current_merge;
+static int show_progress = -1;
+static int default_to_upstream;
+static const char *sign_commit;
+
+static struct strategy all_strategy[] = {
+	{ "recursive",  DEFAULT_TWOHEAD | NO_TRIVIAL },
+	{ "octopus",    DEFAULT_OCTOPUS },
+	{ "resolve",    0 },
+	{ "ours",       NO_FAST_FORWARD | NO_TRIVIAL },
+	{ "subtree",    NO_FAST_FORWARD | NO_TRIVIAL },
+};
+
+static const char *pull_twohead, *pull_octopus;
+
+static int option_parse_message(const struct option *opt,
+				const char *arg, int unset)
+{
+	struct strbuf *buf = opt->value;
+
+	if (unset)
+		strbuf_setlen(buf, 0);
+	else if (arg) {
+		strbuf_addf(buf, "%s%s", buf->len ? "\n\n" : "", arg);
+		have_message = 1;
+	} else
+		return error(_("switch `m' requires a value"));
+	return 0;
+}
+
+static struct strategy *get_strategy(const char *name)
+{
+	int i;
+	struct strategy *ret;
+	static struct cmdnames main_cmds, other_cmds;
+	static int loaded;
+
+	if (!name)
+		return NULL;
+
+	for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
+		if (!strcmp(name, all_strategy[i].name))
+			return &all_strategy[i];
+
+	if (!loaded) {
+		struct cmdnames not_strategies;
+		loaded = 1;
+
+		memset(&not_strategies, 0, sizeof(struct cmdnames));
+		load_command_list("git-merge-", &main_cmds, &other_cmds);
+		for (i = 0; i < main_cmds.cnt; i++) {
+			int j, found = 0;
+			struct cmdname *ent = main_cmds.names[i];
+			for (j = 0; j < ARRAY_SIZE(all_strategy); j++)
+				if (!strncmp(ent->name, all_strategy[j].name, ent->len)
+						&& !all_strategy[j].name[ent->len])
+					found = 1;
+			if (!found)
+				add_cmdname(&not_strategies, ent->name, ent->len);
+		}
+		exclude_cmds(&main_cmds, &not_strategies);
+	}
+	if (!is_in_cmdlist(&main_cmds, name) && !is_in_cmdlist(&other_cmds, name)) {
+		fprintf(stderr, _("Could not find merge strategy '%s'.\n"), name);
+		fprintf(stderr, _("Available strategies are:"));
+		for (i = 0; i < main_cmds.cnt; i++)
+			fprintf(stderr, " %s", main_cmds.names[i]->name);
+		fprintf(stderr, ".\n");
+		if (other_cmds.cnt) {
+			fprintf(stderr, _("Available custom strategies are:"));
+			for (i = 0; i < other_cmds.cnt; i++)
+				fprintf(stderr, " %s", other_cmds.names[i]->name);
+			fprintf(stderr, ".\n");
+		}
+		exit(1);
+	}
+
+	ret = xcalloc(1, sizeof(struct strategy));
+	ret->name = xstrdup(name);
+	ret->attr = NO_TRIVIAL;
+	return ret;
+}
+
+static void append_strategy(struct strategy *s)
+{
+	ALLOC_GROW(use_strategies, use_strategies_nr + 1, use_strategies_alloc);
+	use_strategies[use_strategies_nr++] = s;
+}
+
+static int option_parse_strategy(const struct option *opt,
+				 const char *name, int unset)
+{
+	if (unset)
+		return 0;
+
+	append_strategy(get_strategy(name));
+	return 0;
+}
+
+static int option_parse_x(const struct option *opt,
+			  const char *arg, int unset)
+{
+	if (unset)
+		return 0;
+
+	ALLOC_GROW(xopts, xopts_nr + 1, xopts_alloc);
+	xopts[xopts_nr++] = xstrdup(arg);
+	return 0;
+}
+
+static int option_parse_n(const struct option *opt,
+			  const char *arg, int unset)
+{
+	show_diffstat = unset;
+	return 0;
+}
+
+static struct option builtin_merge_options[] = {
+	{ OPTION_CALLBACK, 'n', NULL, NULL, NULL,
+		"do not show a diffstat at the end of the merge",
+		PARSE_OPT_NOARG, option_parse_n },
+	OPT_BOOLEAN(0, "stat", &show_diffstat,
+		"show a diffstat at the end of the merge"),
+	OPT_BOOLEAN(0, "summary", &show_diffstat, "(synonym to --stat)"),
+	{ OPTION_INTEGER, 0, "log", &shortlog_len, "n",
+	  "add (at most <n>) entries from shortlog to merge commit message",
+	  PARSE_OPT_OPTARG, NULL, DEFAULT_MERGE_LOG_LEN },
+	OPT_BOOLEAN(0, "squash", &squash,
+		"create a single commit instead of doing a merge"),
+	OPT_BOOLEAN(0, "commit", &option_commit,
+		"perform a commit if the merge succeeds (default)"),
+	OPT_BOOL('e', "edit", &option_edit,
+		"edit message before committing"),
+	OPT_BOOLEAN(0, "ff", &allow_fast_forward,
+		"allow fast-forward (default)"),
+	OPT_BOOLEAN(0, "ff-only", &fast_forward_only,
+		"abort if fast-forward is not possible"),
+	OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
+	OPT_CALLBACK('s', "strategy", &use_strategies, "strategy",
+		"merge strategy to use", option_parse_strategy),
+	OPT_CALLBACK('X', "strategy-option", &xopts, "option=value",
+		"option for selected merge strategy", option_parse_x),
+	OPT_CALLBACK('m', "message", &merge_msg, "message",
+		"merge commit message (for a non-fast-forward merge)",
+		option_parse_message),
+	OPT__VERBOSITY(&verbosity),
+	OPT_BOOLEAN(0, "abort", &abort_current_merge,
+		"abort the current in-progress merge"),
+	OPT_SET_INT(0, "progress", &show_progress, "force progress reporting", 1),
+	{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, "key id",
+	  "GPG sign commit", PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+	OPT_BOOLEAN(0, "overwrite-ignore", &overwrite_ignore, "update ignored files (default)"),
+	OPT_END()
+};
+
+/* Cleans up metadata that is uninteresting after a succeeded merge. */
+static void drop_save(void)
+{
+	unlink(git_path("MERGE_HEAD"));
+	unlink(git_path("MERGE_MSG"));
+	unlink(git_path("MERGE_MODE"));
+}
+
+static int save_state(unsigned char *stash)
+{
+	int len;
+	struct child_process cp;
+	struct strbuf buffer = STRBUF_INIT;
+	const char *argv[] = {"stash", "create", NULL};
+
+	memset(&cp, 0, sizeof(cp));
+	cp.argv = argv;
+	cp.out = -1;
+	cp.git_cmd = 1;
+
+	if (start_command(&cp))
+		die(_("could not run stash."));
+	len = strbuf_read(&buffer, cp.out, 1024);
+	close(cp.out);
+
+	if (finish_command(&cp) || len < 0)
+		die(_("stash failed"));
+	else if (!len)		/* no changes */
+		return -1;
+	strbuf_setlen(&buffer, buffer.len-1);
+	if (get_sha1(buffer.buf, stash))
+		die(_("not a valid object: %s"), buffer.buf);
+	return 0;
+}
+
+static void read_empty(unsigned const char *sha1, int verbose)
+{
+	int i = 0;
+	const char *args[7];
+
+	args[i++] = "read-tree";
+	if (verbose)
+		args[i++] = "-v";
+	args[i++] = "-m";
+	args[i++] = "-u";
+	args[i++] = EMPTY_TREE_SHA1_HEX;
+	args[i++] = sha1_to_hex(sha1);
+	args[i] = NULL;
+
+	if (run_command_v_opt(args, RUN_GIT_CMD))
+		die(_("read-tree failed"));
+}
+
+static void reset_hard(unsigned const char *sha1, int verbose)
+{
+	int i = 0;
+	const char *args[6];
+
+	args[i++] = "read-tree";
+	if (verbose)
+		args[i++] = "-v";
+	args[i++] = "--reset";
+	args[i++] = "-u";
+	args[i++] = sha1_to_hex(sha1);
+	args[i] = NULL;
+
+	if (run_command_v_opt(args, RUN_GIT_CMD))
+		die(_("read-tree failed"));
+}
+
+static void restore_state(const unsigned char *head,
+			  const unsigned char *stash)
+{
+	struct strbuf sb = STRBUF_INIT;
+	const char *args[] = { "stash", "apply", NULL, NULL };
+
+	if (is_null_sha1(stash))
+		return;
+
+	reset_hard(head, 1);
+
+	args[2] = sha1_to_hex(stash);
+
+	/*
+	 * It is OK to ignore error here, for example when there was
+	 * nothing to restore.
+	 */
+	run_command_v_opt(args, RUN_GIT_CMD);
+
+	strbuf_release(&sb);
+	refresh_cache(REFRESH_QUIET);
+}
+
+/* This is called when no merge was necessary. */
+static void finish_up_to_date(const char *msg)
+{
+	if (verbosity >= 0)
+		printf("%s%s\n", squash ? _(" (nothing to squash)") : "", msg);
+	drop_save();
+}
+
+static void squash_message(struct commit *commit, struct commit_list *remoteheads)
+{
+	struct rev_info rev;
+	struct strbuf out = STRBUF_INIT;
+	struct commit_list *j;
+	const char *filename;
+	int fd;
+	struct pretty_print_context ctx = {0};
+
+	printf(_("Squash commit -- not updating HEAD\n"));
+	filename = git_path("SQUASH_MSG");
+	fd = open(filename, O_WRONLY | O_CREAT, 0666);
+	if (fd < 0)
+		die_errno(_("Could not write to '%s'"), filename);
+
+	init_revisions(&rev, NULL);
+	rev.ignore_merges = 1;
+	rev.commit_format = CMIT_FMT_MEDIUM;
+
+	commit->object.flags |= UNINTERESTING;
+	add_pending_object(&rev, &commit->object, NULL);
+
+	for (j = remoteheads; j; j = j->next)
+		add_pending_object(&rev, &j->item->object, NULL);
+
+	setup_revisions(0, NULL, &rev, NULL);
+	if (prepare_revision_walk(&rev))
+		die(_("revision walk setup failed"));
+
+	ctx.abbrev = rev.abbrev;
+	ctx.date_mode = rev.date_mode;
+	ctx.fmt = rev.commit_format;
+
+	strbuf_addstr(&out, "Squashed commit of the following:\n");
+	while ((commit = get_revision(&rev)) != NULL) {
+		strbuf_addch(&out, '\n');
+		strbuf_addf(&out, "commit %s\n",
+			sha1_to_hex(commit->object.sha1));
+		pretty_print_commit(&ctx, commit, &out);
+	}
+	if (write(fd, out.buf, out.len) < 0)
+		die_errno(_("Writing SQUASH_MSG"));
+	if (close(fd))
+		die_errno(_("Finishing SQUASH_MSG"));
+	strbuf_release(&out);
+}
+
+static void finish(struct commit *head_commit,
+		   struct commit_list *remoteheads,
+		   const unsigned char *new_head, const char *msg)
+{
+	struct strbuf reflog_message = STRBUF_INIT;
+	const unsigned char *head = head_commit->object.sha1;
+
+	if (!msg)
+		strbuf_addstr(&reflog_message, getenv("GIT_REFLOG_ACTION"));
+	else {
+		if (verbosity >= 0)
+			printf("%s\n", msg);
+		strbuf_addf(&reflog_message, "%s: %s",
+			getenv("GIT_REFLOG_ACTION"), msg);
+	}
+	if (squash) {
+		squash_message(head_commit, remoteheads);
+	} else {
+		if (verbosity >= 0 && !merge_msg.len)
+			printf(_("No merge message -- not updating HEAD\n"));
+		else {
+			const char *argv_gc_auto[] = { "gc", "--auto", NULL };
+			update_ref(reflog_message.buf, "HEAD",
+				new_head, head, 0,
+				DIE_ON_ERR);
+			/*
+			 * We ignore errors in 'gc --auto', since the
+			 * user should see them.
+			 */
+			run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
+		}
+	}
+	if (new_head && show_diffstat) {
+		struct diff_options opts;
+		diff_setup(&opts);
+		opts.stat_width = -1; /* use full terminal width */
+		opts.stat_graph_width = -1; /* respect statGraphWidth config */
+		opts.output_format |=
+			DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
+		opts.detect_rename = DIFF_DETECT_RENAME;
+		if (diff_setup_done(&opts) < 0)
+			die(_("diff_setup_done failed"));
+		diff_tree_sha1(head, new_head, "", &opts);
+		diffcore_std(&opts);
+		diff_flush(&opts);
+	}
+
+	/* Run a post-merge hook */
+	run_hook(NULL, "post-merge", squash ? "1" : "0", NULL);
+
+	strbuf_release(&reflog_message);
+}
+
+/* Get the name for the merge commit's message. */
+static void merge_name(const char *remote, struct strbuf *msg)
+{
+	struct commit *remote_head;
+	unsigned char branch_head[20];
+	struct strbuf buf = STRBUF_INIT;
+	struct strbuf bname = STRBUF_INIT;
+	const char *ptr;
+	char *found_ref;
+	int len, early;
+
+	strbuf_branchname(&bname, remote);
+	remote = bname.buf;
+
+	memset(branch_head, 0, sizeof(branch_head));
+	remote_head = get_merge_parent(remote);
+	if (!remote_head)
+		die(_("'%s' does not point to a commit"), remote);
+
+	if (dwim_ref(remote, strlen(remote), branch_head, &found_ref) > 0) {
+		if (!prefixcmp(found_ref, "refs/heads/")) {
+			strbuf_addf(msg, "%s\t\tbranch '%s' of .\n",
+				    sha1_to_hex(branch_head), remote);
+			goto cleanup;
+		}
+		if (!prefixcmp(found_ref, "refs/tags/")) {
+			strbuf_addf(msg, "%s\t\ttag '%s' of .\n",
+				    sha1_to_hex(branch_head), remote);
+			goto cleanup;
+		}
+		if (!prefixcmp(found_ref, "refs/remotes/")) {
+			strbuf_addf(msg, "%s\t\tremote-tracking branch '%s' of .\n",
+				    sha1_to_hex(branch_head), remote);
+			goto cleanup;
+		}
+	}
+
+	/* See if remote matches <name>^^^.. or <name>~<number> */
+	for (len = 0, ptr = remote + strlen(remote);
+	     remote < ptr && ptr[-1] == '^';
+	     ptr--)
+		len++;
+	if (len)
+		early = 1;
+	else {
+		early = 0;
+		ptr = strrchr(remote, '~');
+		if (ptr) {
+			int seen_nonzero = 0;
+
+			len++; /* count ~ */
+			while (*++ptr && isdigit(*ptr)) {
+				seen_nonzero |= (*ptr != '0');
+				len++;
+			}
+			if (*ptr)
+				len = 0; /* not ...~<number> */
+			else if (seen_nonzero)
+				early = 1;
+			else if (len == 1)
+				early = 1; /* "name~" is "name~1"! */
+		}
+	}
+	if (len) {
+		struct strbuf truname = STRBUF_INIT;
+		strbuf_addstr(&truname, "refs/heads/");
+		strbuf_addstr(&truname, remote);
+		strbuf_setlen(&truname, truname.len - len);
+		if (ref_exists(truname.buf)) {
+			strbuf_addf(msg,
+				    "%s\t\tbranch '%s'%s of .\n",
+				    sha1_to_hex(remote_head->object.sha1),
+				    truname.buf + 11,
+				    (early ? " (early part)" : ""));
+			strbuf_release(&truname);
+			goto cleanup;
+		}
+	}
+
+	if (!strcmp(remote, "FETCH_HEAD") &&
+			!access(git_path("FETCH_HEAD"), R_OK)) {
+		const char *filename;
+		FILE *fp;
+		struct strbuf line = STRBUF_INIT;
+		char *ptr;
+
+		filename = git_path("FETCH_HEAD");
+		fp = fopen(filename, "r");
+		if (!fp)
+			die_errno(_("could not open '%s' for reading"),
+				  filename);
+		strbuf_getline(&line, fp, '\n');
+		fclose(fp);
+		ptr = strstr(line.buf, "\tnot-for-merge\t");
+		if (ptr)
+			strbuf_remove(&line, ptr-line.buf+1, 13);
+		strbuf_addbuf(msg, &line);
+		strbuf_release(&line);
+		goto cleanup;
+	}
+	strbuf_addf(msg, "%s\t\tcommit '%s'\n",
+		sha1_to_hex(remote_head->object.sha1), remote);
+cleanup:
+	strbuf_release(&buf);
+	strbuf_release(&bname);
+}
+
+static void parse_branch_merge_options(char *bmo)
+{
+	const char **argv;
+	int argc;
+
+	if (!bmo)
+		return;
+	argc = split_cmdline(bmo, &argv);
+	if (argc < 0)
+		die(_("Bad branch.%s.mergeoptions string: %s"), branch,
+		    split_cmdline_strerror(argc));
+	argv = xrealloc(argv, sizeof(*argv) * (argc + 2));
+	memmove(argv + 1, argv, sizeof(*argv) * (argc + 1));
+	argc++;
+	argv[0] = "branch.*.mergeoptions";
+	parse_options(argc, argv, NULL, builtin_merge_options,
+		      builtin_merge_usage, 0);
+	free(argv);
+}
+
+static int git_merge_config(const char *k, const char *v, void *cb)
+{
+	int status;
+
+	if (branch && !prefixcmp(k, "branch.") &&
+		!prefixcmp(k + 7, branch) &&
+		!strcmp(k + 7 + strlen(branch), ".mergeoptions")) {
+		free(branch_mergeoptions);
+		branch_mergeoptions = xstrdup(v);
+		return 0;
+	}
+
+	if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat"))
+		show_diffstat = git_config_bool(k, v);
+	else if (!strcmp(k, "pull.twohead"))
+		return git_config_string(&pull_twohead, k, v);
+	else if (!strcmp(k, "pull.octopus"))
+		return git_config_string(&pull_octopus, k, v);
+	else if (!strcmp(k, "merge.renormalize"))
+		option_renormalize = git_config_bool(k, v);
+	else if (!strcmp(k, "merge.ff")) {
+		int boolval = git_config_maybe_bool(k, v);
+		if (0 <= boolval) {
+			allow_fast_forward = boolval;
+		} else if (v && !strcmp(v, "only")) {
+			allow_fast_forward = 1;
+			fast_forward_only = 1;
+		} /* do not barf on values from future versions of git */
+		return 0;
+	} else if (!strcmp(k, "merge.defaulttoupstream")) {
+		default_to_upstream = git_config_bool(k, v);
+		return 0;
+	}
+
+	status = fmt_merge_msg_config(k, v, cb);
+	if (status)
+		return status;
+	status = git_gpg_config(k, v, NULL);
+	if (status)
+		return status;
+	return git_diff_ui_config(k, v, cb);
+}
+
+static int read_tree_trivial(unsigned char *common, unsigned char *head,
+			     unsigned char *one)
+{
+	int i, nr_trees = 0;
+	struct tree *trees[MAX_UNPACK_TREES];
+	struct tree_desc t[MAX_UNPACK_TREES];
+	struct unpack_trees_options opts;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.head_idx = 2;
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+	opts.update = 1;
+	opts.verbose_update = 1;
+	opts.trivial_merges_only = 1;
+	opts.merge = 1;
+	trees[nr_trees] = parse_tree_indirect(common);
+	if (!trees[nr_trees++])
+		return -1;
+	trees[nr_trees] = parse_tree_indirect(head);
+	if (!trees[nr_trees++])
+		return -1;
+	trees[nr_trees] = parse_tree_indirect(one);
+	if (!trees[nr_trees++])
+		return -1;
+	opts.fn = threeway_merge;
+	cache_tree_free(&active_cache_tree);
+	for (i = 0; i < nr_trees; i++) {
+		parse_tree(trees[i]);
+		init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
+	}
+	if (unpack_trees(nr_trees, t, &opts))
+		return -1;
+	return 0;
+}
+
+static void write_tree_trivial(unsigned char *sha1)
+{
+	if (write_cache_as_tree(sha1, 0, NULL))
+		die(_("git write-tree failed to write a tree"));
+}
+
+static const char *merge_argument(struct commit *commit)
+{
+	if (commit)
+		return sha1_to_hex(commit->object.sha1);
+	else
+		return EMPTY_TREE_SHA1_HEX;
+}
+
+int try_merge_command(const char *strategy, size_t xopts_nr,
+		      const char **xopts, struct commit_list *common,
+		      const char *head_arg, struct commit_list *remotes)
+{
+	const char **args;
+	int i = 0, x = 0, ret;
+	struct commit_list *j;
+	struct strbuf buf = STRBUF_INIT;
+
+	args = xmalloc((4 + xopts_nr + commit_list_count(common) +
+			commit_list_count(remotes)) * sizeof(char *));
+	strbuf_addf(&buf, "merge-%s", strategy);
+	args[i++] = buf.buf;
+	for (x = 0; x < xopts_nr; x++) {
+		char *s = xmalloc(strlen(xopts[x])+2+1);
+		strcpy(s, "--");
+		strcpy(s+2, xopts[x]);
+		args[i++] = s;
+	}
+	for (j = common; j; j = j->next)
+		args[i++] = xstrdup(merge_argument(j->item));
+	args[i++] = "--";
+	args[i++] = head_arg;
+	for (j = remotes; j; j = j->next)
+		args[i++] = xstrdup(merge_argument(j->item));
+	args[i] = NULL;
+	ret = run_command_v_opt(args, RUN_GIT_CMD);
+	strbuf_release(&buf);
+	i = 1;
+	for (x = 0; x < xopts_nr; x++)
+		free((void *)args[i++]);
+	for (j = common; j; j = j->next)
+		free((void *)args[i++]);
+	i += 2;
+	for (j = remotes; j; j = j->next)
+		free((void *)args[i++]);
+	free(args);
+	discard_cache();
+	if (read_cache() < 0)
+		die(_("failed to read the cache"));
+	resolve_undo_clear();
+
+	return ret;
+}
+
+static int try_merge_strategy(const char *strategy, struct commit_list *common,
+			      struct commit_list *remoteheads,
+			      struct commit *head, const char *head_arg)
+{
+	int index_fd;
+	struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
+
+	index_fd = hold_locked_index(lock, 1);
+	refresh_cache(REFRESH_QUIET);
+	if (active_cache_changed &&
+			(write_cache(index_fd, active_cache, active_nr) ||
+			 commit_locked_index(lock)))
+		return error(_("Unable to write index."));
+	rollback_lock_file(lock);
+
+	if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree")) {
+		int clean, x;
+		struct commit *result;
+		struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
+		int index_fd;
+		struct commit_list *reversed = NULL;
+		struct merge_options o;
+		struct commit_list *j;
+
+		if (remoteheads->next) {
+			error(_("Not handling anything other than two heads merge."));
+			return 2;
+		}
+
+		init_merge_options(&o);
+		if (!strcmp(strategy, "subtree"))
+			o.subtree_shift = "";
+
+		o.renormalize = option_renormalize;
+		o.show_rename_progress =
+			show_progress == -1 ? isatty(2) : show_progress;
+
+		for (x = 0; x < xopts_nr; x++)
+			if (parse_merge_opt(&o, xopts[x]))
+				die(_("Unknown option for merge-recursive: -X%s"), xopts[x]);
+
+		o.branch1 = head_arg;
+		o.branch2 = merge_remote_util(remoteheads->item)->name;
+
+		for (j = common; j; j = j->next)
+			commit_list_insert(j->item, &reversed);
+
+		index_fd = hold_locked_index(lock, 1);
+		clean = merge_recursive(&o, head,
+				remoteheads->item, reversed, &result);
+		if (active_cache_changed &&
+				(write_cache(index_fd, active_cache, active_nr) ||
+				 commit_locked_index(lock)))
+			die (_("unable to write %s"), get_index_file());
+		rollback_lock_file(lock);
+		return clean ? 0 : 1;
+	} else {
+		return try_merge_command(strategy, xopts_nr, xopts,
+						common, head_arg, remoteheads);
+	}
+}
+
+static void count_diff_files(struct diff_queue_struct *q,
+			     struct diff_options *opt, void *data)
+{
+	int *count = data;
+
+	(*count) += q->nr;
+}
+
+static int count_unmerged_entries(void)
+{
+	int i, ret = 0;
+
+	for (i = 0; i < active_nr; i++)
+		if (ce_stage(active_cache[i]))
+			ret++;
+
+	return ret;
+}
+
+int checkout_fast_forward(const unsigned char *head, const unsigned char *remote)
+{
+	struct tree *trees[MAX_UNPACK_TREES];
+	struct unpack_trees_options opts;
+	struct tree_desc t[MAX_UNPACK_TREES];
+	int i, fd, nr_trees = 0;
+	struct dir_struct dir;
+	struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
+
+	refresh_cache(REFRESH_QUIET);
+
+	fd = hold_locked_index(lock_file, 1);
+
+	memset(&trees, 0, sizeof(trees));
+	memset(&opts, 0, sizeof(opts));
+	memset(&t, 0, sizeof(t));
+	if (overwrite_ignore) {
+		memset(&dir, 0, sizeof(dir));
+		dir.flags |= DIR_SHOW_IGNORED;
+		setup_standard_excludes(&dir);
+		opts.dir = &dir;
+	}
+
+	opts.head_idx = 1;
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+	opts.update = 1;
+	opts.verbose_update = 1;
+	opts.merge = 1;
+	opts.fn = twoway_merge;
+	setup_unpack_trees_porcelain(&opts, "merge");
+
+	trees[nr_trees] = parse_tree_indirect(head);
+	if (!trees[nr_trees++])
+		return -1;
+	trees[nr_trees] = parse_tree_indirect(remote);
+	if (!trees[nr_trees++])
+		return -1;
+	for (i = 0; i < nr_trees; i++) {
+		parse_tree(trees[i]);
+		init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
+	}
+	if (unpack_trees(nr_trees, t, &opts))
+		return -1;
+	if (write_cache(fd, active_cache, active_nr) ||
+		commit_locked_index(lock_file))
+		die(_("unable to write new index file"));
+	return 0;
+}
+
+static void split_merge_strategies(const char *string, struct strategy **list,
+				   int *nr, int *alloc)
+{
+	char *p, *q, *buf;
+
+	if (!string)
+		return;
+
+	buf = xstrdup(string);
+	q = buf;
+	for (;;) {
+		p = strchr(q, ' ');
+		if (!p) {
+			ALLOC_GROW(*list, *nr + 1, *alloc);
+			(*list)[(*nr)++].name = xstrdup(q);
+			free(buf);
+			return;
+		} else {
+			*p = '\0';
+			ALLOC_GROW(*list, *nr + 1, *alloc);
+			(*list)[(*nr)++].name = xstrdup(q);
+			q = ++p;
+		}
+	}
+}
+
+static void add_strategies(const char *string, unsigned attr)
+{
+	struct strategy *list = NULL;
+	int list_alloc = 0, list_nr = 0, i;
+
+	memset(&list, 0, sizeof(list));
+	split_merge_strategies(string, &list, &list_nr, &list_alloc);
+	if (list) {
+		for (i = 0; i < list_nr; i++)
+			append_strategy(get_strategy(list[i].name));
+		return;
+	}
+	for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
+		if (all_strategy[i].attr & attr)
+			append_strategy(&all_strategy[i]);
+
+}
+
+static void write_merge_msg(struct strbuf *msg)
+{
+	const char *filename = git_path("MERGE_MSG");
+	int fd = open(filename, O_WRONLY | O_CREAT, 0666);
+	if (fd < 0)
+		die_errno(_("Could not open '%s' for writing"),
+			  filename);
+	if (write_in_full(fd, msg->buf, msg->len) != msg->len)
+		die_errno(_("Could not write to '%s'"), filename);
+	close(fd);
+}
+
+static void read_merge_msg(struct strbuf *msg)
+{
+	const char *filename = git_path("MERGE_MSG");
+	strbuf_reset(msg);
+	if (strbuf_read_file(msg, filename, 0) < 0)
+		die_errno(_("Could not read from '%s'"), filename);
+}
+
+static void write_merge_state(struct commit_list *);
+static void abort_commit(struct commit_list *remoteheads, const char *err_msg)
+{
+	if (err_msg)
+		error("%s", err_msg);
+	fprintf(stderr,
+		_("Not committing merge; use 'git commit' to complete the merge.\n"));
+	write_merge_state(remoteheads);
+	exit(1);
+}
+
+static const char merge_editor_comment[] =
+N_("Please enter a commit message to explain why this merge is necessary,\n"
+   "especially if it merges an updated upstream into a topic branch.\n"
+   "\n"
+   "Lines starting with '#' will be ignored, and an empty message aborts\n"
+   "the commit.\n");
+
+static void prepare_to_commit(struct commit_list *remoteheads)
+{
+	struct strbuf msg = STRBUF_INIT;
+	const char *comment = _(merge_editor_comment);
+	strbuf_addbuf(&msg, &merge_msg);
+	strbuf_addch(&msg, '\n');
+	if (0 < option_edit)
+		strbuf_add_lines(&msg, "# ", comment, strlen(comment));
+	write_merge_msg(&msg);
+	run_hook(get_index_file(), "prepare-commit-msg",
+		 git_path("MERGE_MSG"), "merge", NULL, NULL);
+	if (0 < option_edit) {
+		if (launch_editor(git_path("MERGE_MSG"), NULL, NULL))
+			abort_commit(remoteheads, NULL);
+	}
+	read_merge_msg(&msg);
+	stripspace(&msg, 0 < option_edit);
+	if (!msg.len)
+		abort_commit(remoteheads, _("Empty commit message."));
+	strbuf_release(&merge_msg);
+	strbuf_addbuf(&merge_msg, &msg);
+	strbuf_release(&msg);
+}
+
+static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
+{
+	unsigned char result_tree[20], result_commit[20];
+	struct commit_list *parent = xmalloc(sizeof(*parent));
+
+	write_tree_trivial(result_tree);
+	printf(_("Wonderful.\n"));
+	parent->item = head;
+	parent->next = xmalloc(sizeof(*parent->next));
+	parent->next->item = remoteheads->item;
+	parent->next->next = NULL;
+	prepare_to_commit(remoteheads);
+	if (commit_tree(&merge_msg, result_tree, parent, result_commit, NULL,
+			sign_commit))
+		die(_("failed to write commit object"));
+	finish(head, remoteheads, result_commit, "In-index merge");
+	drop_save();
+	return 0;
+}
+
+static int finish_automerge(struct commit *head,
+			    int head_subsumed,
+			    struct commit_list *common,
+			    struct commit_list *remoteheads,
+			    unsigned char *result_tree,
+			    const char *wt_strategy)
+{
+	struct commit_list *parents = NULL;
+	struct strbuf buf = STRBUF_INIT;
+	unsigned char result_commit[20];
+
+	free_commit_list(common);
+	parents = remoteheads;
+	if (!head_subsumed || !allow_fast_forward)
+		commit_list_insert(head, &parents);
+	strbuf_addch(&merge_msg, '\n');
+	prepare_to_commit(remoteheads);
+	if (commit_tree(&merge_msg, result_tree, parents, result_commit,
+			NULL, sign_commit))
+		die(_("failed to write commit object"));
+	strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy);
+	finish(head, remoteheads, result_commit, buf.buf);
+	strbuf_release(&buf);
+	drop_save();
+	return 0;
+}
+
+static int suggest_conflicts(int renormalizing)
+{
+	const char *filename;
+	FILE *fp;
+	int pos;
+
+	filename = git_path("MERGE_MSG");
+	fp = fopen(filename, "a");
+	if (!fp)
+		die_errno(_("Could not open '%s' for writing"), filename);
+	fprintf(fp, "\nConflicts:\n");
+	for (pos = 0; pos < active_nr; pos++) {
+		struct cache_entry *ce = active_cache[pos];
+
+		if (ce_stage(ce)) {
+			fprintf(fp, "\t%s\n", ce->name);
+			while (pos + 1 < active_nr &&
+					!strcmp(ce->name,
+						active_cache[pos + 1]->name))
+				pos++;
+		}
+	}
+	fclose(fp);
+	rerere(allow_rerere_auto);
+	printf(_("Automatic merge failed; "
+			"fix conflicts and then commit the result.\n"));
+	return 1;
+}
+
+static struct commit *is_old_style_invocation(int argc, const char **argv,
+					      const unsigned char *head)
+{
+	struct commit *second_token = NULL;
+	if (argc > 2) {
+		unsigned char second_sha1[20];
+
+		if (get_sha1(argv[1], second_sha1))
+			return NULL;
+		second_token = lookup_commit_reference_gently(second_sha1, 0);
+		if (!second_token)
+			die(_("'%s' is not a commit"), argv[1]);
+		if (hashcmp(second_token->object.sha1, head))
+			return NULL;
+	}
+	return second_token;
+}
+
+static int evaluate_result(void)
+{
+	int cnt = 0;
+	struct rev_info rev;
+
+	/* Check how many files differ. */
+	init_revisions(&rev, "");
+	setup_revisions(0, NULL, &rev, NULL);
+	rev.diffopt.output_format |=
+		DIFF_FORMAT_CALLBACK;
+	rev.diffopt.format_callback = count_diff_files;
+	rev.diffopt.format_callback_data = &cnt;
+	run_diff_files(&rev, 0);
+
+	/*
+	 * Check how many unmerged entries are
+	 * there.
+	 */
+	cnt += count_unmerged_entries();
+
+	return cnt;
+}
+
+/*
+ * Pretend as if the user told us to merge with the tracking
+ * branch we have for the upstream of the current branch
+ */
+static int setup_with_upstream(const char ***argv)
+{
+	struct branch *branch = branch_get(NULL);
+	int i;
+	const char **args;
+
+	if (!branch)
+		die(_("No current branch."));
+	if (!branch->remote)
+		die(_("No remote for the current branch."));
+	if (!branch->merge_nr)
+		die(_("No default upstream defined for the current branch."));
+
+	args = xcalloc(branch->merge_nr + 1, sizeof(char *));
+	for (i = 0; i < branch->merge_nr; i++) {
+		if (!branch->merge[i]->dst)
+			die(_("No remote tracking branch for %s from %s"),
+			    branch->merge[i]->src, branch->remote_name);
+		args[i] = branch->merge[i]->dst;
+	}
+	args[i] = NULL;
+	*argv = args;
+	return i;
+}
+
+static void write_merge_state(struct commit_list *remoteheads)
+{
+	const char *filename;
+	int fd;
+	struct commit_list *j;
+	struct strbuf buf = STRBUF_INIT;
+
+	for (j = remoteheads; j; j = j->next) {
+		unsigned const char *sha1;
+		struct commit *c = j->item;
+		if (c->util && merge_remote_util(c)->obj) {
+			sha1 = merge_remote_util(c)->obj->sha1;
+		} else {
+			sha1 = c->object.sha1;
+		}
+		strbuf_addf(&buf, "%s\n", sha1_to_hex(sha1));
+	}
+	filename = git_path("MERGE_HEAD");
+	fd = open(filename, O_WRONLY | O_CREAT, 0666);
+	if (fd < 0)
+		die_errno(_("Could not open '%s' for writing"), filename);
+	if (write_in_full(fd, buf.buf, buf.len) != buf.len)
+		die_errno(_("Could not write to '%s'"), filename);
+	close(fd);
+	strbuf_addch(&merge_msg, '\n');
+	write_merge_msg(&merge_msg);
+
+	filename = git_path("MERGE_MODE");
+	fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+	if (fd < 0)
+		die_errno(_("Could not open '%s' for writing"), filename);
+	strbuf_reset(&buf);
+	if (!allow_fast_forward)
+		strbuf_addf(&buf, "no-ff");
+	if (write_in_full(fd, buf.buf, buf.len) != buf.len)
+		die_errno(_("Could not write to '%s'"), filename);
+	close(fd);
+}
+
+static int default_edit_option(void)
+{
+	static const char name[] = "GIT_MERGE_AUTOEDIT";
+	const char *e = getenv(name);
+	struct stat st_stdin, st_stdout;
+
+	if (have_message)
+		/* an explicit -m msg without --[no-]edit */
+		return 0;
+
+	if (e) {
+		int v = git_config_maybe_bool(name, e);
+		if (v < 0)
+			die("Bad value '%s' in environment '%s'", e, name);
+		return v;
+	}
+
+	/* Use editor if stdin and stdout are the same and is a tty */
+	return (!fstat(0, &st_stdin) &&
+		!fstat(1, &st_stdout) &&
+		isatty(0) && isatty(1) &&
+		st_stdin.st_dev == st_stdout.st_dev &&
+		st_stdin.st_ino == st_stdout.st_ino &&
+		st_stdin.st_mode == st_stdout.st_mode);
+}
+
+static struct commit_list *collect_parents(struct commit *head_commit,
+					   int *head_subsumed,
+					   int argc, const char **argv)
+{
+	int i;
+	struct commit_list *remoteheads = NULL, *parents, *next;
+	struct commit_list **remotes = &remoteheads;
+
+	if (head_commit)
+		remotes = &commit_list_insert(head_commit, remotes)->next;
+	for (i = 0; i < argc; i++) {
+		struct commit *commit = get_merge_parent(argv[i]);
+		if (!commit)
+			die(_("%s - not something we can merge"), argv[i]);
+		remotes = &commit_list_insert(commit, remotes)->next;
+	}
+	*remotes = NULL;
+
+	parents = reduce_heads(remoteheads);
+
+	*head_subsumed = 1; /* we will flip this to 0 when we find it */
+	for (remoteheads = NULL, remotes = &remoteheads;
+	     parents;
+	     parents = next) {
+		struct commit *commit = parents->item;
+		next = parents->next;
+		if (commit == head_commit)
+			*head_subsumed = 0;
+		else
+			remotes = &commit_list_insert(commit, remotes)->next;
+	}
+	return remoteheads;
+}
+
+int cmd_merge(int argc, const char **argv, const char *prefix)
+{
+	unsigned char result_tree[20];
+	unsigned char stash[20];
+	unsigned char head_sha1[20];
+	struct commit *head_commit;
+	struct strbuf buf = STRBUF_INIT;
+	const char *head_arg;
+	int flag, i, ret = 0, head_subsumed;
+	int best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0;
+	struct commit_list *common = NULL;
+	const char *best_strategy = NULL, *wt_strategy = NULL;
+	struct commit_list *remoteheads, *p;
+	void *branch_to_free;
+
+	if (argc == 2 && !strcmp(argv[1], "-h"))
+		usage_with_options(builtin_merge_usage, builtin_merge_options);
+
+	/*
+	 * Check if we are _not_ on a detached HEAD, i.e. if there is a
+	 * current branch.
+	 */
+	branch = branch_to_free = resolve_refdup("HEAD", head_sha1, 0, &flag);
+	if (branch && !prefixcmp(branch, "refs/heads/"))
+		branch += 11;
+	if (!branch || is_null_sha1(head_sha1))
+		head_commit = NULL;
+	else
+		head_commit = lookup_commit_or_die(head_sha1, "HEAD");
+
+	git_config(git_merge_config, NULL);
+
+	if (branch_mergeoptions)
+		parse_branch_merge_options(branch_mergeoptions);
+	argc = parse_options(argc, argv, prefix, builtin_merge_options,
+			builtin_merge_usage, 0);
+	if (shortlog_len < 0)
+		shortlog_len = (merge_log_config > 0) ? merge_log_config : 0;
+
+	if (verbosity < 0 && show_progress == -1)
+		show_progress = 0;
+
+	if (abort_current_merge) {
+		int nargc = 2;
+		const char *nargv[] = {"reset", "--merge", NULL};
+
+		if (!file_exists(git_path("MERGE_HEAD")))
+			die(_("There is no merge to abort (MERGE_HEAD missing)."));
+
+		/* Invoke 'git reset --merge' */
+		ret = cmd_reset(nargc, nargv, prefix);
+		goto done;
+	}
+
+	if (read_cache_unmerged())
+		die_resolve_conflict("merge");
+
+	if (file_exists(git_path("MERGE_HEAD"))) {
+		/*
+		 * There is no unmerged entry, don't advise 'git
+		 * add/rm <file>', just 'git commit'.
+		 */
+		if (advice_resolve_conflict)
+			die(_("You have not concluded your merge (MERGE_HEAD exists).\n"
+				  "Please, commit your changes before you can merge."));
+		else
+			die(_("You have not concluded your merge (MERGE_HEAD exists)."));
+	}
+	if (file_exists(git_path("CHERRY_PICK_HEAD"))) {
+		if (advice_resolve_conflict)
+			die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
+			    "Please, commit your changes before you can merge."));
+		else
+			die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."));
+	}
+	resolve_undo_clear();
+
+	if (verbosity < 0)
+		show_diffstat = 0;
+
+	if (squash) {
+		if (!allow_fast_forward)
+			die(_("You cannot combine --squash with --no-ff."));
+		option_commit = 0;
+	}
+
+	if (!allow_fast_forward && fast_forward_only)
+		die(_("You cannot combine --no-ff with --ff-only."));
+
+	if (!abort_current_merge) {
+		if (!argc) {
+			if (default_to_upstream)
+				argc = setup_with_upstream(&argv);
+			else
+				die(_("No commit specified and merge.defaultToUpstream not set."));
+		} else if (argc == 1 && !strcmp(argv[0], "-"))
+			argv[0] = "@{-1}";
+	}
+	if (!argc)
+		usage_with_options(builtin_merge_usage,
+			builtin_merge_options);
+
+	/*
+	 * This could be traditional "merge <msg> HEAD <commit>..."  and
+	 * the way we can tell it is to see if the second token is HEAD,
+	 * but some people might have misused the interface and used a
+	 * committish that is the same as HEAD there instead.
+	 * Traditional format never would have "-m" so it is an
+	 * additional safety measure to check for it.
+	 */
+
+	if (!have_message && head_commit &&
+	    is_old_style_invocation(argc, argv, head_commit->object.sha1)) {
+		strbuf_addstr(&merge_msg, argv[0]);
+		head_arg = argv[1];
+		argv += 2;
+		argc -= 2;
+		remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv);
+	} else if (!head_commit) {
+		struct commit *remote_head;
+		/*
+		 * If the merged head is a valid one there is no reason
+		 * to forbid "git merge" into a branch yet to be born.
+		 * We do the same for "git pull".
+		 */
+		if (argc != 1)
+			die(_("Can merge only exactly one commit into "
+				"empty head"));
+		if (squash)
+			die(_("Squash commit into empty head not supported yet"));
+		if (!allow_fast_forward)
+			die(_("Non-fast-forward commit does not make sense into "
+			    "an empty head"));
+		remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv);
+		remote_head = remoteheads->item;
+		if (!remote_head)
+			die(_("%s - not something we can merge"), argv[0]);
+		read_empty(remote_head->object.sha1, 0);
+		update_ref("initial pull", "HEAD", remote_head->object.sha1,
+			   NULL, 0, DIE_ON_ERR);
+		goto done;
+	} else {
+		struct strbuf merge_names = STRBUF_INIT;
+
+		/* We are invoked directly as the first-class UI. */
+		head_arg = "HEAD";
+
+		/*
+		 * All the rest are the commits being merged; prepare
+		 * the standard merge summary message to be appended
+		 * to the given message.
+		 */
+		remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv);
+		for (p = remoteheads; p; p = p->next)
+			merge_name(merge_remote_util(p->item)->name, &merge_names);
+
+		if (!have_message || shortlog_len) {
+			struct fmt_merge_msg_opts opts;
+			memset(&opts, 0, sizeof(opts));
+			opts.add_title = !have_message;
+			opts.shortlog_len = shortlog_len;
+
+			fmt_merge_msg(&merge_names, &merge_msg, &opts);
+			if (merge_msg.len)
+				strbuf_setlen(&merge_msg, merge_msg.len - 1);
+		}
+	}
+
+	if (!head_commit || !argc)
+		usage_with_options(builtin_merge_usage,
+			builtin_merge_options);
+
+	strbuf_addstr(&buf, "merge");
+	for (p = remoteheads; p; p = p->next)
+		strbuf_addf(&buf, " %s", merge_remote_util(p->item)->name);
+	setenv("GIT_REFLOG_ACTION", buf.buf, 0);
+	strbuf_reset(&buf);
+
+	for (p = remoteheads; p; p = p->next) {
+		struct commit *commit = p->item;
+		strbuf_addf(&buf, "GITHEAD_%s",
+			    sha1_to_hex(commit->object.sha1));
+		setenv(buf.buf, merge_remote_util(commit)->name, 1);
+		strbuf_reset(&buf);
+		if (!fast_forward_only &&
+		    merge_remote_util(commit) &&
+		    merge_remote_util(commit)->obj &&
+		    merge_remote_util(commit)->obj->type == OBJ_TAG)
+			allow_fast_forward = 0;
+	}
+
+	if (option_edit < 0)
+		option_edit = default_edit_option();
+
+	if (!use_strategies) {
+		if (!remoteheads)
+			; /* already up-to-date */
+		else if (!remoteheads->next)
+			add_strategies(pull_twohead, DEFAULT_TWOHEAD);
+		else
+			add_strategies(pull_octopus, DEFAULT_OCTOPUS);
+	}
+
+	for (i = 0; i < use_strategies_nr; i++) {
+		if (use_strategies[i]->attr & NO_FAST_FORWARD)
+			allow_fast_forward = 0;
+		if (use_strategies[i]->attr & NO_TRIVIAL)
+			allow_trivial = 0;
+	}
+
+	if (!remoteheads)
+		; /* already up-to-date */
+	else if (!remoteheads->next)
+		common = get_merge_bases(head_commit, remoteheads->item, 1);
+	else {
+		struct commit_list *list = remoteheads;
+		commit_list_insert(head_commit, &list);
+		common = get_octopus_merge_bases(list);
+		free(list);
+	}
+
+	update_ref("updating ORIG_HEAD", "ORIG_HEAD", head_commit->object.sha1,
+		   NULL, 0, DIE_ON_ERR);
+
+	if (remoteheads && !common)
+		; /* No common ancestors found. We need a real merge. */
+	else if (!remoteheads ||
+		 (!remoteheads->next && !common->next &&
+		  common->item == remoteheads->item)) {
+		/*
+		 * If head can reach all the merge then we are up to date.
+		 * but first the most common case of merging one remote.
+		 */
+		finish_up_to_date("Already up-to-date.");
+		goto done;
+	} else if (allow_fast_forward && !remoteheads->next &&
+			!common->next &&
+			!hashcmp(common->item->object.sha1, head_commit->object.sha1)) {
+		/* Again the most common case of merging one remote. */
+		struct strbuf msg = STRBUF_INIT;
+		struct commit *commit;
+		char hex[41];
+
+		strcpy(hex, find_unique_abbrev(head_commit->object.sha1, DEFAULT_ABBREV));
+
+		if (verbosity >= 0)
+			printf(_("Updating %s..%s\n"),
+				hex,
+				find_unique_abbrev(remoteheads->item->object.sha1,
+				DEFAULT_ABBREV));
+		strbuf_addstr(&msg, "Fast-forward");
+		if (have_message)
+			strbuf_addstr(&msg,
+				" (no commit created; -m option ignored)");
+		commit = remoteheads->item;
+		if (!commit) {
+			ret = 1;
+			goto done;
+		}
+
+		if (checkout_fast_forward(head_commit->object.sha1,
+					  commit->object.sha1)) {
+			ret = 1;
+			goto done;
+		}
+
+		finish(head_commit, remoteheads, commit->object.sha1, msg.buf);
+		drop_save();
+		goto done;
+	} else if (!remoteheads->next && common->next)
+		;
+		/*
+		 * We are not doing octopus and not fast-forward.  Need
+		 * a real merge.
+		 */
+	else if (!remoteheads->next && !common->next && option_commit) {
+		/*
+		 * We are not doing octopus, not fast-forward, and have
+		 * only one common.
+		 */
+		refresh_cache(REFRESH_QUIET);
+		if (allow_trivial && !fast_forward_only) {
+			/* See if it is really trivial. */
+			git_committer_info(IDENT_STRICT);
+			printf(_("Trying really trivial in-index merge...\n"));
+			if (!read_tree_trivial(common->item->object.sha1,
+					       head_commit->object.sha1,
+					       remoteheads->item->object.sha1)) {
+				ret = merge_trivial(head_commit, remoteheads);
+				goto done;
+			}
+			printf(_("Nope.\n"));
+		}
+	} else {
+		/*
+		 * An octopus.  If we can reach all the remote we are up
+		 * to date.
+		 */
+		int up_to_date = 1;
+		struct commit_list *j;
+
+		for (j = remoteheads; j; j = j->next) {
+			struct commit_list *common_one;
+
+			/*
+			 * Here we *have* to calculate the individual
+			 * merge_bases again, otherwise "git merge HEAD^
+			 * HEAD^^" would be missed.
+			 */
+			common_one = get_merge_bases(head_commit, j->item, 1);
+			if (hashcmp(common_one->item->object.sha1,
+				j->item->object.sha1)) {
+				up_to_date = 0;
+				break;
+			}
+		}
+		if (up_to_date) {
+			finish_up_to_date("Already up-to-date. Yeeah!");
+			goto done;
+		}
+	}
+
+	if (fast_forward_only)
+		die(_("Not possible to fast-forward, aborting."));
+
+	/* We are going to make a new commit. */
+	git_committer_info(IDENT_STRICT);
+
+	/*
+	 * At this point, we need a real merge.  No matter what strategy
+	 * we use, it would operate on the index, possibly affecting the
+	 * working tree, and when resolved cleanly, have the desired
+	 * tree in the index -- this means that the index must be in
+	 * sync with the head commit.  The strategies are responsible
+	 * to ensure this.
+	 */
+	if (use_strategies_nr == 1 ||
+	    /*
+	     * Stash away the local changes so that we can try more than one.
+	     */
+	    save_state(stash))
+		hashcpy(stash, null_sha1);
+
+	for (i = 0; i < use_strategies_nr; i++) {
+		int ret;
+		if (i) {
+			printf(_("Rewinding the tree to pristine...\n"));
+			restore_state(head_commit->object.sha1, stash);
+		}
+		if (use_strategies_nr != 1)
+			printf(_("Trying merge strategy %s...\n"),
+				use_strategies[i]->name);
+		/*
+		 * Remember which strategy left the state in the working
+		 * tree.
+		 */
+		wt_strategy = use_strategies[i]->name;
+
+		ret = try_merge_strategy(use_strategies[i]->name,
+					 common, remoteheads,
+					 head_commit, head_arg);
+		if (!option_commit && !ret) {
+			merge_was_ok = 1;
+			/*
+			 * This is necessary here just to avoid writing
+			 * the tree, but later we will *not* exit with
+			 * status code 1 because merge_was_ok is set.
+			 */
+			ret = 1;
+		}
+
+		if (ret) {
+			/*
+			 * The backend exits with 1 when conflicts are
+			 * left to be resolved, with 2 when it does not
+			 * handle the given merge at all.
+			 */
+			if (ret == 1) {
+				int cnt = evaluate_result();
+
+				if (best_cnt <= 0 || cnt <= best_cnt) {
+					best_strategy = use_strategies[i]->name;
+					best_cnt = cnt;
+				}
+			}
+			if (merge_was_ok)
+				break;
+			else
+				continue;
+		}
+
+		/* Automerge succeeded. */
+		write_tree_trivial(result_tree);
+		automerge_was_ok = 1;
+		break;
+	}
+
+	/*
+	 * If we have a resulting tree, that means the strategy module
+	 * auto resolved the merge cleanly.
+	 */
+	if (automerge_was_ok) {
+		ret = finish_automerge(head_commit, head_subsumed,
+				       common, remoteheads,
+				       result_tree, wt_strategy);
+		goto done;
+	}
+
+	/*
+	 * Pick the result from the best strategy and have the user fix
+	 * it up.
+	 */
+	if (!best_strategy) {
+		restore_state(head_commit->object.sha1, stash);
+		if (use_strategies_nr > 1)
+			fprintf(stderr,
+				_("No merge strategy handled the merge.\n"));
+		else
+			fprintf(stderr, _("Merge with strategy %s failed.\n"),
+				use_strategies[0]->name);
+		ret = 2;
+		goto done;
+	} else if (best_strategy == wt_strategy)
+		; /* We already have its result in the working tree. */
+	else {
+		printf(_("Rewinding the tree to pristine...\n"));
+		restore_state(head_commit->object.sha1, stash);
+		printf(_("Using the %s to prepare resolving by hand.\n"),
+			best_strategy);
+		try_merge_strategy(best_strategy, common, remoteheads,
+				   head_commit, head_arg);
+	}
+
+	if (squash)
+		finish(head_commit, remoteheads, NULL, NULL);
+	else
+		write_merge_state(remoteheads);
+
+	if (merge_was_ok)
+		fprintf(stderr, _("Automatic merge went well; "
+			"stopped before committing as requested\n"));
+	else
+		ret = suggest_conflicts(option_renormalize);
+
+done:
+	free(branch_to_free);
+	return ret;
+}
diff --git a/builtin/mktag.c b/builtin/mktag.c
new file mode 100644
index 0000000..640ab64
--- /dev/null
+++ b/builtin/mktag.c
@@ -0,0 +1,174 @@
+#include "builtin.h"
+#include "tag.h"
+
+/*
+ * 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 four lines are guaranteed to be at least 83 bytes:
+ * "object <sha1>\n" is 48 bytes, "type tag\n" at 9 bytes is the
+ * shortest possible type-line, "tag .\n" at 6 bytes is the shortest
+ * single-character-tag line, and "tagger . <> 0 +0000\n" at 20 bytes is
+ * the shortest possible tagger-line.
+ */
+
+/*
+ * We refuse to tag something we can't verify. Just because.
+ */
+static int verify_object(const unsigned char *sha1, const char *expected_type)
+{
+	int ret = -1;
+	enum object_type type;
+	unsigned long size;
+	void *buffer = read_sha1_file(sha1, &type, &size);
+	const unsigned char *repl = lookup_replace_object(sha1);
+
+	if (buffer) {
+		if (type == type_from_string(expected_type))
+			ret = check_sha1_signature(repl, buffer, size, expected_type);
+		free(buffer);
+	}
+	return ret;
+}
+
+static int verify_tag(char *buffer, unsigned long size)
+{
+	int typelen;
+	char type[20];
+	unsigned char sha1[20];
+	const char *object, *type_line, *tag_line, *tagger_line, *lb, *rb;
+	size_t len;
+
+	if (size < 84)
+		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 \"", 0);
+
+	if (get_sha1_hex(object + 7, sha1))
+		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 \"", 47);
+
+	/* Verify tag-line */
+	tag_line = strchr(type_line, '\n');
+	if (!tag_line)
+		return error("char%"PRIuMAX": could not find next \"\\n\"",
+				(uintmax_t) (type_line - buffer));
+	tag_line++;
+	if (memcmp(tag_line, "tag ", 4) || tag_line[4] == '\n')
+		return error("char%"PRIuMAX": no \"tag \" found",
+				(uintmax_t) (tag_line - buffer));
+
+	/* Get the actual type */
+	typelen = tag_line - type_line - strlen("type \n");
+	if (typelen >= sizeof(type))
+		return error("char%"PRIuMAX": type too long",
+				(uintmax_t) (type_line+5 - buffer));
+
+	memcpy(type, type_line+5, typelen);
+	type[typelen] = 0;
+
+	/* Verify that the object matches */
+	if (verify_object(sha1, type))
+		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;
+	for (;;) {
+		unsigned char c = *tag_line++;
+		if (c == '\n')
+			break;
+		if (c > ' ')
+			continue;
+		return error("char%"PRIuMAX": could not verify tag name",
+				(uintmax_t) (tag_line - buffer));
+	}
+
+	/* Verify the tagger line */
+	tagger_line = tag_line;
+
+	if (memcmp(tagger_line, "tagger ", 7))
+		return error("char%"PRIuMAX": could not find \"tagger \"",
+			(uintmax_t) (tagger_line - buffer));
+
+	/*
+	 * Check for correct form for name and email
+	 * i.e. " <" followed by "> " on _this_ line
+	 * No angle brackets within the name or email address fields.
+	 * No spaces within the email address field.
+	 */
+	tagger_line += 7;
+	if (!(lb = strstr(tagger_line, " <")) || !(rb = strstr(lb+2, "> ")) ||
+		strpbrk(tagger_line, "<>\n") != lb+1 ||
+		strpbrk(lb+2, "><\n ") != rb)
+		return error("char%"PRIuMAX": malformed tagger field",
+			(uintmax_t) (tagger_line - buffer));
+
+	/* Check for author name, at least one character, space is acceptable */
+	if (lb == tagger_line)
+		return error("char%"PRIuMAX": missing tagger name",
+			(uintmax_t) (tagger_line - buffer));
+
+	/* timestamp, 1 or more digits followed by space */
+	tagger_line = rb + 2;
+	if (!(len = strspn(tagger_line, "0123456789")))
+		return error("char%"PRIuMAX": missing tag timestamp",
+			(uintmax_t) (tagger_line - buffer));
+	tagger_line += len;
+	if (*tagger_line != ' ')
+		return error("char%"PRIuMAX": malformed tag timestamp",
+			(uintmax_t) (tagger_line - buffer));
+	tagger_line++;
+
+	/* timezone, 5 digits [+-]hhmm, max. 1400 */
+	if (!((tagger_line[0] == '+' || tagger_line[0] == '-') &&
+	      strspn(tagger_line+1, "0123456789") == 4 &&
+	      tagger_line[5] == '\n' && atoi(tagger_line+1) <= 1400))
+		return error("char%"PRIuMAX": malformed tag timezone",
+			(uintmax_t) (tagger_line - buffer));
+	tagger_line += 6;
+
+	/* Verify the blank line separating the header from the body */
+	if (*tagger_line != '\n')
+		return error("char%"PRIuMAX": trailing garbage in tag header",
+			(uintmax_t) (tagger_line - buffer));
+
+	/* The actual stuff afterwards we don't care about.. */
+	return 0;
+}
+
+int cmd_mktag(int argc, const char **argv, const char *prefix)
+{
+	struct strbuf buf = STRBUF_INIT;
+	unsigned char result_sha1[20];
+
+	if (argc != 1)
+		usage("git mktag < signaturefile");
+
+	if (strbuf_read(&buf, 0, 4096) < 0) {
+		die_errno("could not read from stdin");
+	}
+
+	/* Verify it for some basic sanity: it needs to start with
+	   "object <sha1>\ntype\ntagger " */
+	if (verify_tag(buf.buf, buf.len) < 0)
+		die("invalid tag signature file");
+
+	if (write_sha1_file(buf.buf, buf.len, tag_type, result_sha1) < 0)
+		die("unable to write tag file");
+
+	strbuf_release(&buf);
+	printf("%s\n", sha1_to_hex(result_sha1));
+	return 0;
+}
diff --git a/builtin/mktree.c b/builtin/mktree.c
new file mode 100644
index 0000000..4ae1c41
--- /dev/null
+++ b/builtin/mktree.c
@@ -0,0 +1,191 @@
+/*
+ * GIT - the stupid content tracker
+ *
+ * Copyright (c) Junio C Hamano, 2006, 2009
+ */
+#include "builtin.h"
+#include "quote.h"
+#include "tree.h"
+#include "parse-options.h"
+
+static struct treeent {
+	unsigned mode;
+	unsigned char sha1[20];
+	int len;
+	char name[FLEX_ARRAY];
+} **entries;
+static int alloc, used;
+
+static void append_to_tree(unsigned mode, unsigned char *sha1, char *path)
+{
+	struct treeent *ent;
+	int len = strlen(path);
+	if (strchr(path, '/'))
+		die("path %s contains slash", path);
+
+	if (alloc <= used) {
+		alloc = alloc_nr(used);
+		entries = xrealloc(entries, sizeof(*entries) * alloc);
+	}
+	ent = entries[used++] = xmalloc(sizeof(**entries) + len + 1);
+	ent->mode = mode;
+	ent->len = len;
+	hashcpy(ent->sha1, sha1);
+	memcpy(ent->name, path, len+1);
+}
+
+static int ent_compare(const void *a_, const void *b_)
+{
+	struct treeent *a = *(struct treeent **)a_;
+	struct treeent *b = *(struct treeent **)b_;
+	return base_name_compare(a->name, a->len, a->mode,
+				 b->name, b->len, b->mode);
+}
+
+static void write_tree(unsigned char *sha1)
+{
+	struct strbuf buf;
+	size_t size;
+	int i;
+
+	qsort(entries, used, sizeof(*entries), ent_compare);
+	for (size = i = 0; i < used; i++)
+		size += 32 + entries[i]->len;
+
+	strbuf_init(&buf, size);
+	for (i = 0; i < used; i++) {
+		struct treeent *ent = entries[i];
+		strbuf_addf(&buf, "%o %s%c", ent->mode, ent->name, '\0');
+		strbuf_add(&buf, ent->sha1, 20);
+	}
+
+	write_sha1_file(buf.buf, buf.len, tree_type, sha1);
+	strbuf_release(&buf);
+}
+
+static const char *mktree_usage[] = {
+	"git mktree [-z] [--missing] [--batch]",
+	NULL
+};
+
+static void mktree_line(char *buf, size_t len, int line_termination, int allow_missing)
+{
+	char *ptr, *ntr;
+	unsigned mode;
+	enum object_type mode_type; /* object type derived from mode */
+	enum object_type obj_type; /* object type derived from sha */
+	char *path;
+	unsigned char sha1[20];
+
+	ptr = buf;
+	/*
+	 * Read non-recursive ls-tree output format:
+	 *     mode SP type SP sha1 TAB name
+	 */
+	mode = strtoul(ptr, &ntr, 8);
+	if (ptr == ntr || !ntr || *ntr != ' ')
+		die("input format error: %s", buf);
+	ptr = ntr + 1; /* type */
+	ntr = strchr(ptr, ' ');
+	if (!ntr || buf + len <= ntr + 40 ||
+	    ntr[41] != '\t' ||
+	    get_sha1_hex(ntr + 1, sha1))
+		die("input format error: %s", buf);
+
+	/* It is perfectly normal if we do not have a commit from a submodule */
+	if (S_ISGITLINK(mode))
+		allow_missing = 1;
+
+
+	*ntr++ = 0; /* now at the beginning of SHA1 */
+
+	path = ntr + 41;  /* at the beginning of name */
+	if (line_termination && path[0] == '"') {
+		struct strbuf p_uq = STRBUF_INIT;
+		if (unquote_c_style(&p_uq, path, NULL))
+			die("invalid quoting");
+		path = strbuf_detach(&p_uq, NULL);
+	}
+
+	/*
+	 * Object type is redundantly derivable three ways.
+	 * These should all agree.
+	 */
+	mode_type = object_type(mode);
+	if (mode_type != type_from_string(ptr)) {
+		die("entry '%s' object type (%s) doesn't match mode type (%s)",
+			path, ptr, typename(mode_type));
+	}
+
+	/* Check the type of object identified by sha1 */
+	obj_type = sha1_object_info(sha1, NULL);
+	if (obj_type < 0) {
+		if (allow_missing) {
+			; /* no problem - missing objects are presumed to be of the right type */
+		} else {
+			die("entry '%s' object %s is unavailable", path, sha1_to_hex(sha1));
+		}
+	} else {
+		if (obj_type != mode_type) {
+			/*
+			 * The object exists but is of the wrong type.
+			 * This is a problem regardless of allow_missing
+			 * because the new tree entry will never be correct.
+			 */
+			die("entry '%s' object %s is a %s but specified type was (%s)",
+				path, sha1_to_hex(sha1), typename(obj_type), typename(mode_type));
+		}
+	}
+
+	append_to_tree(mode, sha1, path);
+}
+
+int cmd_mktree(int ac, const char **av, const char *prefix)
+{
+	struct strbuf sb = STRBUF_INIT;
+	unsigned char sha1[20];
+	int line_termination = '\n';
+	int allow_missing = 0;
+	int is_batch_mode = 0;
+	int got_eof = 0;
+
+	const struct option option[] = {
+		OPT_SET_INT('z', NULL, &line_termination, "input is NUL terminated", '\0'),
+		OPT_SET_INT( 0 , "missing", &allow_missing, "allow missing objects", 1),
+		OPT_SET_INT( 0 , "batch", &is_batch_mode, "allow creation of more than one tree", 1),
+		OPT_END()
+	};
+
+	ac = parse_options(ac, av, prefix, option, mktree_usage, 0);
+
+	while (!got_eof) {
+		while (1) {
+			if (strbuf_getline(&sb, stdin, line_termination) == EOF) {
+				got_eof = 1;
+				break;
+			}
+			if (sb.buf[0] == '\0') {
+				/* empty lines denote tree boundaries in batch mode */
+				if (is_batch_mode)
+					break;
+				die("input format error: (blank line only valid in batch mode)");
+			}
+			mktree_line(sb.buf, sb.len, line_termination, allow_missing);
+		}
+		if (is_batch_mode && got_eof && used < 1) {
+			/*
+			 * Execution gets here if the last tree entry is terminated with a
+			 * new-line.  The final new-line has been made optional to be
+			 * consistent with the original non-batch behaviour of mktree.
+			 */
+			; /* skip creating an empty tree */
+		} else {
+			write_tree(sha1);
+			puts(sha1_to_hex(sha1));
+			fflush(stdout);
+		}
+		used=0; /* reset tree entry buffer for re-use in batch mode */
+	}
+	strbuf_release(&sb);
+	exit(0);
+}
diff --git a/builtin/mv.c b/builtin/mv.c
new file mode 100644
index 0000000..2a144b0
--- /dev/null
+++ b/builtin/mv.c
@@ -0,0 +1,233 @@
+/*
+ * "git mv" builtin command
+ *
+ * Copyright (C) 2006 Johannes Schindelin
+ */
+#include "cache.h"
+#include "builtin.h"
+#include "dir.h"
+#include "cache-tree.h"
+#include "string-list.h"
+#include "parse-options.h"
+
+static const char * const builtin_mv_usage[] = {
+	"git mv [options] <source>... <destination>",
+	NULL
+};
+
+static const char **copy_pathspec(const char *prefix, const char **pathspec,
+				  int count, int base_name)
+{
+	int i;
+	const char **result = xmalloc((count + 1) * sizeof(const char *));
+	memcpy(result, pathspec, count * sizeof(const char *));
+	result[count] = NULL;
+	for (i = 0; i < count; i++) {
+		int length = strlen(result[i]);
+		int to_copy = length;
+		while (to_copy > 0 && is_dir_sep(result[i][to_copy - 1]))
+			to_copy--;
+		if (to_copy != length || base_name) {
+			char *it = xmemdupz(result[i], to_copy);
+			if (base_name) {
+				result[i] = xstrdup(basename(it));
+				free(it);
+			} else
+				result[i] = it;
+		}
+	}
+	return get_pathspec(prefix, result);
+}
+
+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;
+	int verbose = 0, show_only = 0, force = 0, ignore_errors = 0;
+	struct option builtin_mv_options[] = {
+		OPT__VERBOSE(&verbose, "be verbose"),
+		OPT__DRY_RUN(&show_only, "dry run"),
+		OPT__FORCE(&force, "force move/rename even if target exists"),
+		OPT_BOOLEAN('k', NULL, &ignore_errors, "skip move/rename errors"),
+		OPT_END(),
+	};
+	const char **source, **destination, **dest_path;
+	enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes;
+	struct stat st;
+	struct string_list src_for_dst = STRING_LIST_INIT_NODUP;
+
+	git_config(git_default_config, NULL);
+
+	argc = parse_options(argc, argv, prefix, builtin_mv_options,
+			     builtin_mv_usage, 0);
+	if (--argc < 1)
+		usage_with_options(builtin_mv_usage, builtin_mv_options);
+
+	newfd = hold_locked_index(&lock_file, 1);
+	if (read_cache() < 0)
+		die(_("index file corrupt"));
+
+	source = copy_pathspec(prefix, argv, argc, 0);
+	modes = xcalloc(argc, sizeof(enum update_mode));
+	dest_path = copy_pathspec(prefix, argv + argc, 1, 0);
+
+	if (dest_path[0][0] == '\0')
+		/* special case: "." was normalized to "" */
+		destination = copy_pathspec(dest_path[0], argv, argc, 1);
+	else 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, argc, 1);
+	} else {
+		if (argc != 1)
+			die("destination '%s' is not a directory", dest_path[0]);
+		destination = dest_path;
+	}
+
+	/* Checking */
+	for (i = 0; i < argc; i++) {
+		const char *src = source[i], *dst = destination[i];
+		int length, src_is_dir;
+		const char *bad = NULL;
+
+		if (show_only)
+			printf(_("Checking rename of '%s' to '%s'\n"), src, dst);
+
+		length = strlen(src);
+		if (lstat(src, &st) < 0)
+			bad = _("bad source");
+		else if (!strncmp(src, dst, length) &&
+				(dst[length] == 0 || dst[length] == '/')) {
+			bad = _("can not move directory into itself");
+		} else if ((src_is_dir = S_ISDIR(st.st_mode))
+				&& lstat(dst, &st) == 0)
+			bad = _("cannot move directory over file");
+		else if (src_is_dir) {
+			const char *src_w_slash = add_slash(src);
+			int len_w_slash = length + 1;
+			int first, last;
+
+			modes[i] = WORKING_DIRECTORY;
+
+			first = cache_name_pos(src_w_slash, len_w_slash);
+			if (first >= 0)
+				die (_("Huh? %.*s is in index?"),
+						len_w_slash, src_w_slash);
+
+			first = -1 - first;
+			for (last = first; last < active_nr; last++) {
+				const char *path = active_cache[last]->name;
+				if (strncmp(path, src_w_slash, len_w_slash))
+					break;
+			}
+			free((char *)src_w_slash);
+
+			if (last - first < 1)
+				bad = _("source directory is empty");
+			else {
+				int j, dst_len;
+
+				if (last - first > 0) {
+					source = xrealloc(source,
+							(argc + last - first)
+							* sizeof(char *));
+					destination = xrealloc(destination,
+							(argc + last - first)
+							* sizeof(char *));
+					modes = xrealloc(modes,
+							(argc + last - first)
+							* sizeof(enum update_mode));
+				}
+
+				dst = add_slash(dst);
+				dst_len = strlen(dst);
+
+				for (j = 0; j < last - first; j++) {
+					const char *path =
+						active_cache[first + j]->name;
+					source[argc + j] = path;
+					destination[argc + j] =
+						prefix_path(dst, dst_len,
+							path + length + 1);
+					modes[argc + j] = INDEX;
+				}
+				argc += last - first;
+			}
+		} else if (cache_name_pos(src, length) < 0)
+			bad = _("not under version control");
+		else if (lstat(dst, &st) == 0) {
+			bad = _("destination exists");
+			if (force) {
+				/*
+				 * only files can overwrite each other:
+				 * check both source and destination
+				 */
+				if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
+					if (verbose)
+						warning(_("overwriting '%s'"), dst);
+					bad = NULL;
+				} else
+					bad = _("Cannot overwrite");
+			}
+		} else if (string_list_has_string(&src_for_dst, dst))
+			bad = _("multiple sources for the same target");
+		else
+			string_list_insert(&src_for_dst, dst);
+
+		if (bad) {
+			if (ignore_errors) {
+				if (--argc > 0) {
+					memmove(source + i, source + i + 1,
+						(argc - i) * sizeof(char *));
+					memmove(destination + i,
+						destination + i + 1,
+						(argc - i) * sizeof(char *));
+					i--;
+				}
+			} else
+				die (_("%s, source=%s, destination=%s"),
+				     bad, src, dst);
+		}
+	}
+
+	for (i = 0; i < argc; i++) {
+		const char *src = source[i], *dst = destination[i];
+		enum update_mode mode = modes[i];
+		int pos;
+		if (show_only || verbose)
+			printf(_("Renaming %s to %s\n"), src, dst);
+		if (!show_only && mode != INDEX &&
+				rename(src, dst) < 0 && !ignore_errors)
+			die_errno (_("renaming '%s' failed"), src);
+
+		if (mode == WORKING_DIRECTORY)
+			continue;
+
+		pos = cache_name_pos(src, strlen(src));
+		assert(pos >= 0);
+		if (!show_only)
+			rename_cache_entry_at(pos, dst);
+	}
+
+	if (active_cache_changed) {
+		if (write_cache(newfd, active_cache, active_nr) ||
+		    commit_locked_index(&lock_file))
+			die(_("Unable to write new index file"));
+	}
+
+	return 0;
+}
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
new file mode 100644
index 0000000..1b37458
--- /dev/null
+++ b/builtin/name-rev.c
@@ -0,0 +1,307 @@
+#include "builtin.h"
+#include "cache.h"
+#include "commit.h"
+#include "tag.h"
+#include "refs.h"
+#include "parse-options.h"
+
+#define CUTOFF_DATE_SLOP 86400 /* one day */
+
+typedef struct rev_name {
+	const char *tip_name;
+	int generation;
+	int distance;
+} rev_name;
+
+static long cutoff = LONG_MAX;
+
+/* How many generations are maximally preferred over _one_ merge traversal? */
+#define MERGE_TRAVERSAL_WEIGHT 65535
+
+static void name_rev(struct commit *commit,
+		const char *tip_name, int generation, int distance,
+		int deref)
+{
+	struct rev_name *name = (struct rev_name *)commit->util;
+	struct commit_list *parents;
+	int parent_number = 1;
+
+	if (!commit->object.parsed)
+		parse_commit(commit);
+
+	if (commit->date < cutoff)
+		return;
+
+	if (deref) {
+		char *new_name = xmalloc(strlen(tip_name)+3);
+		strcpy(new_name, tip_name);
+		strcat(new_name, "^0");
+		tip_name = new_name;
+
+		if (generation)
+			die("generation: %d, but deref?", generation);
+	}
+
+	if (name == NULL) {
+		name = xmalloc(sizeof(rev_name));
+		commit->util = name;
+		goto copy_data;
+	} else if (name->distance > distance) {
+copy_data:
+		name->tip_name = tip_name;
+		name->generation = generation;
+		name->distance = distance;
+	} else
+		return;
+
+	for (parents = commit->parents;
+			parents;
+			parents = parents->next, parent_number++) {
+		if (parent_number > 1) {
+			int len = strlen(tip_name);
+			char *new_name = xmalloc(len +
+				1 + decimal_length(generation) +  /* ~<n> */
+				1 + 2 +				  /* ^NN */
+				1);
+
+			if (len > 2 && !strcmp(tip_name + len - 2, "^0"))
+				len -= 2;
+			if (generation > 0)
+				sprintf(new_name, "%.*s~%d^%d", len, tip_name,
+						generation, parent_number);
+			else
+				sprintf(new_name, "%.*s^%d", len, tip_name,
+						parent_number);
+
+			name_rev(parents->item, new_name, 0,
+				distance + MERGE_TRAVERSAL_WEIGHT, 0);
+		} else {
+			name_rev(parents->item, tip_name, generation + 1,
+				distance + 1, 0);
+		}
+	}
+}
+
+struct name_ref_data {
+	int tags_only;
+	int name_only;
+	const char *ref_filter;
+};
+
+static int name_ref(const char *path, const unsigned char *sha1, int flags, void *cb_data)
+{
+	struct object *o = parse_object(sha1);
+	struct name_ref_data *data = cb_data;
+	int deref = 0;
+
+	if (data->tags_only && prefixcmp(path, "refs/tags/"))
+		return 0;
+
+	if (data->ref_filter && fnmatch(data->ref_filter, path, 0))
+		return 0;
+
+	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 == OBJ_COMMIT) {
+		struct commit *commit = (struct commit *)o;
+
+		if (!prefixcmp(path, "refs/heads/"))
+			path = path + 11;
+		else if (data->tags_only
+		    && data->name_only
+		    && !prefixcmp(path, "refs/tags/"))
+			path = path + 10;
+		else if (!prefixcmp(path, "refs/"))
+			path = path + 5;
+
+		name_rev(commit, xstrdup(path), 0, 0, deref);
+	}
+	return 0;
+}
+
+/* returns a static buffer */
+static const char *get_rev_name(const struct object *o)
+{
+	static char buffer[1024];
+	struct rev_name *n;
+	struct commit *c;
+
+	if (o->type != OBJ_COMMIT)
+		return NULL;
+	c = (struct commit *) o;
+	n = c->util;
+	if (!n)
+		return NULL;
+
+	if (!n->generation)
+		return n->tip_name;
+	else {
+		int len = strlen(n->tip_name);
+		if (len > 2 && !strcmp(n->tip_name + len - 2, "^0"))
+			len -= 2;
+		snprintf(buffer, sizeof(buffer), "%.*s~%d", len, n->tip_name,
+				n->generation);
+
+		return buffer;
+	}
+}
+
+static void show_name(const struct object *obj,
+		      const char *caller_name,
+		      int always, int allow_undefined, int name_only)
+{
+	const char *name;
+	const unsigned char *sha1 = obj->sha1;
+
+	if (!name_only)
+		printf("%s ", caller_name ? caller_name : sha1_to_hex(sha1));
+	name = get_rev_name(obj);
+	if (name)
+		printf("%s\n", name);
+	else if (allow_undefined)
+		printf("undefined\n");
+	else if (always)
+		printf("%s\n", find_unique_abbrev(sha1, DEFAULT_ABBREV));
+	else
+		die("cannot describe '%s'", sha1_to_hex(sha1));
+}
+
+static char const * const name_rev_usage[] = {
+	"git name-rev [options] <commit>...",
+	"git name-rev [options] --all",
+	"git name-rev [options] --stdin",
+	NULL
+};
+
+static void name_rev_line(char *p, struct name_ref_data *data)
+{
+	int forty = 0;
+	char *p_start;
+	for (p_start = p; *p; p++) {
+#define ishex(x) (isdigit((x)) || ((x) >= 'a' && (x) <= 'f'))
+		if (!ishex(*p))
+			forty = 0;
+		else if (++forty == 40 &&
+			 !ishex(*(p+1))) {
+			unsigned char sha1[40];
+			const char *name = NULL;
+			char c = *(p+1);
+			int p_len = p - p_start + 1;
+
+			forty = 0;
+
+			*(p+1) = 0;
+			if (!get_sha1(p - 39, sha1)) {
+				struct object *o =
+					lookup_object(sha1);
+				if (o)
+					name = get_rev_name(o);
+			}
+			*(p+1) = c;
+
+			if (!name)
+				continue;
+
+			if (data->name_only)
+				printf("%.*s%s", p_len - 40, p_start, name);
+			else
+				printf("%.*s (%s)", p_len, p_start, name);
+			p_start = p + 1;
+		}
+	}
+
+	/* flush */
+	if (p_start != p)
+		fwrite(p_start, p - p_start, 1, stdout);
+}
+
+int cmd_name_rev(int argc, const char **argv, const char *prefix)
+{
+	struct object_array revs = OBJECT_ARRAY_INIT;
+	int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0;
+	struct name_ref_data data = { 0, 0, NULL };
+	struct option opts[] = {
+		OPT_BOOLEAN(0, "name-only", &data.name_only, "print only names (no SHA-1)"),
+		OPT_BOOLEAN(0, "tags", &data.tags_only, "only use tags to name the commits"),
+		OPT_STRING(0, "refs", &data.ref_filter, "pattern",
+				   "only use refs matching <pattern>"),
+		OPT_GROUP(""),
+		OPT_BOOLEAN(0, "all", &all, "list all commits reachable from all refs"),
+		OPT_BOOLEAN(0, "stdin", &transform_stdin, "read from stdin"),
+		OPT_BOOLEAN(0, "undefined", &allow_undefined, "allow to print `undefined` names"),
+		OPT_BOOLEAN(0, "always",     &always,
+			   "show abbreviated commit object as fallback"),
+		OPT_END(),
+	};
+
+	git_config(git_default_config, NULL);
+	argc = parse_options(argc, argv, prefix, opts, name_rev_usage, 0);
+	if (!!all + !!transform_stdin + !!argc > 1) {
+		error("Specify either a list, or --all, not both!");
+		usage_with_options(name_rev_usage, opts);
+	}
+	if (all || transform_stdin)
+		cutoff = 0;
+
+	for (; argc; argc--, argv++) {
+		unsigned char sha1[20];
+		struct object *o;
+		struct commit *commit;
+
+		if (get_sha1(*argv, sha1)) {
+			fprintf(stderr, "Could not get sha1 for %s. Skipping.\n",
+					*argv);
+			continue;
+		}
+
+		o = deref_tag(parse_object(sha1), *argv, 0);
+		if (!o || o->type != OBJ_COMMIT) {
+			fprintf(stderr, "Could not get commit for %s. Skipping.\n",
+					*argv);
+			continue;
+		}
+
+		commit = (struct commit *)o;
+		if (cutoff > commit->date)
+			cutoff = commit->date;
+		add_object_array((struct object *)commit, *argv, &revs);
+	}
+
+	if (cutoff)
+		cutoff = cutoff - CUTOFF_DATE_SLOP;
+	for_each_ref(name_ref, &data);
+
+	if (transform_stdin) {
+		char buffer[2048];
+
+		while (!feof(stdin)) {
+			char *p = fgets(buffer, sizeof(buffer), stdin);
+			if (!p)
+				break;
+			name_rev_line(p, &data);
+		}
+	} else if (all) {
+		int i, max;
+
+		max = get_max_object_index();
+		for (i = 0; i < max; i++) {
+			struct object *obj = get_indexed_object(i);
+			if (!obj || obj->type != OBJ_COMMIT)
+				continue;
+			show_name(obj, NULL,
+				  always, allow_undefined, data.name_only);
+		}
+	} else {
+		int i;
+		for (i = 0; i < revs.nr; i++)
+			show_name(revs.objects[i].item, revs.objects[i].name,
+				  always, allow_undefined, data.name_only);
+	}
+
+	return 0;
+}
diff --git a/builtin/notes.c b/builtin/notes.c
new file mode 100644
index 0000000..3644d14
--- /dev/null
+++ b/builtin/notes.c
@@ -0,0 +1,1108 @@
+/*
+ * Builtin "git notes"
+ *
+ * Copyright (c) 2010 Johan Herland <johan@herland.net>
+ *
+ * Based on git-notes.sh by Johannes Schindelin,
+ * and builtin-tag.c by Kristian Høgsberg and Carlos Rica.
+ */
+
+#include "cache.h"
+#include "builtin.h"
+#include "notes.h"
+#include "blob.h"
+#include "commit.h"
+#include "refs.h"
+#include "exec_cmd.h"
+#include "run-command.h"
+#include "parse-options.h"
+#include "string-list.h"
+#include "notes-merge.h"
+
+static const char * const git_notes_usage[] = {
+	"git notes [--ref <notes_ref>] [list [<object>]]",
+	"git notes [--ref <notes_ref>] add [-f] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]",
+	"git notes [--ref <notes_ref>] copy [-f] <from-object> <to-object>",
+	"git notes [--ref <notes_ref>] append [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]",
+	"git notes [--ref <notes_ref>] edit [<object>]",
+	"git notes [--ref <notes_ref>] show [<object>]",
+	"git notes [--ref <notes_ref>] merge [-v | -q] [-s <strategy> ] <notes_ref>",
+	"git notes merge --commit [-v | -q]",
+	"git notes merge --abort [-v | -q]",
+	"git notes [--ref <notes_ref>] remove [<object>...]",
+	"git notes [--ref <notes_ref>] prune [-n | -v]",
+	"git notes [--ref <notes_ref>] get-ref",
+	NULL
+};
+
+static const char * const git_notes_list_usage[] = {
+	"git notes [list [<object>]]",
+	NULL
+};
+
+static const char * const git_notes_add_usage[] = {
+	"git notes add [<options>] [<object>]",
+	NULL
+};
+
+static const char * const git_notes_copy_usage[] = {
+	"git notes copy [<options>] <from-object> <to-object>",
+	"git notes copy --stdin [<from-object> <to-object>]...",
+	NULL
+};
+
+static const char * const git_notes_append_usage[] = {
+	"git notes append [<options>] [<object>]",
+	NULL
+};
+
+static const char * const git_notes_edit_usage[] = {
+	"git notes edit [<object>]",
+	NULL
+};
+
+static const char * const git_notes_show_usage[] = {
+	"git notes show [<object>]",
+	NULL
+};
+
+static const char * const git_notes_merge_usage[] = {
+	"git notes merge [<options>] <notes_ref>",
+	"git notes merge --commit [<options>]",
+	"git notes merge --abort [<options>]",
+	NULL
+};
+
+static const char * const git_notes_remove_usage[] = {
+	"git notes remove [<object>]",
+	NULL
+};
+
+static const char * const git_notes_prune_usage[] = {
+	"git notes prune [<options>]",
+	NULL
+};
+
+static const char * const git_notes_get_ref_usage[] = {
+	"git notes get-ref",
+	NULL
+};
+
+static const char note_template[] =
+	"\n"
+	"#\n"
+	"# Write/edit the notes for the following object:\n"
+	"#\n";
+
+struct msg_arg {
+	int given;
+	int use_editor;
+	struct strbuf buf;
+};
+
+static int list_each_note(const unsigned char *object_sha1,
+		const unsigned char *note_sha1, char *note_path,
+		void *cb_data)
+{
+	printf("%s %s\n", sha1_to_hex(note_sha1), sha1_to_hex(object_sha1));
+	return 0;
+}
+
+static void write_note_data(int fd, const unsigned char *sha1)
+{
+	unsigned long size;
+	enum object_type type;
+	char *buf = read_sha1_file(sha1, &type, &size);
+	if (buf) {
+		if (size)
+			write_or_die(fd, buf, size);
+		free(buf);
+	}
+}
+
+static void write_commented_object(int fd, const unsigned char *object)
+{
+	const char *show_args[5] =
+		{"show", "--stat", "--no-notes", sha1_to_hex(object), NULL};
+	struct child_process show;
+	struct strbuf buf = STRBUF_INIT;
+	FILE *show_out;
+
+	/* Invoke "git show --stat --no-notes $object" */
+	memset(&show, 0, sizeof(show));
+	show.argv = show_args;
+	show.no_stdin = 1;
+	show.out = -1;
+	show.err = 0;
+	show.git_cmd = 1;
+	if (start_command(&show))
+		die(_("unable to start 'show' for object '%s'"),
+		    sha1_to_hex(object));
+
+	/* Open the output as FILE* so strbuf_getline() can be used. */
+	show_out = xfdopen(show.out, "r");
+	if (show_out == NULL)
+		die_errno(_("can't fdopen 'show' output fd"));
+
+	/* Prepend "# " to each output line and write result to 'fd' */
+	while (strbuf_getline(&buf, show_out, '\n') != EOF) {
+		write_or_die(fd, "# ", 2);
+		write_or_die(fd, buf.buf, buf.len);
+		write_or_die(fd, "\n", 1);
+	}
+	strbuf_release(&buf);
+	if (fclose(show_out))
+		die_errno(_("failed to close pipe to 'show' for object '%s'"),
+			  sha1_to_hex(object));
+	if (finish_command(&show))
+		die(_("failed to finish 'show' for object '%s'"),
+		    sha1_to_hex(object));
+}
+
+static void create_note(const unsigned char *object, struct msg_arg *msg,
+			int append_only, const unsigned char *prev,
+			unsigned char *result)
+{
+	char *path = NULL;
+
+	if (msg->use_editor || !msg->given) {
+		int fd;
+
+		/* write the template message before editing: */
+		path = git_pathdup("NOTES_EDITMSG");
+		fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
+		if (fd < 0)
+			die_errno(_("could not create file '%s'"), path);
+
+		if (msg->given)
+			write_or_die(fd, msg->buf.buf, msg->buf.len);
+		else if (prev && !append_only)
+			write_note_data(fd, prev);
+		write_or_die(fd, note_template, strlen(note_template));
+
+		write_commented_object(fd, object);
+
+		close(fd);
+		strbuf_reset(&(msg->buf));
+
+		if (launch_editor(path, &(msg->buf), NULL)) {
+			die(_("Please supply the note contents using either -m" \
+			    " or -F option"));
+		}
+		stripspace(&(msg->buf), 1);
+	}
+
+	if (prev && append_only) {
+		/* Append buf to previous note contents */
+		unsigned long size;
+		enum object_type type;
+		char *prev_buf = read_sha1_file(prev, &type, &size);
+
+		strbuf_grow(&(msg->buf), size + 1);
+		if (msg->buf.len && prev_buf && size)
+			strbuf_insert(&(msg->buf), 0, "\n", 1);
+		if (prev_buf && size)
+			strbuf_insert(&(msg->buf), 0, prev_buf, size);
+		free(prev_buf);
+	}
+
+	if (!msg->buf.len) {
+		fprintf(stderr, _("Removing note for object %s\n"),
+			sha1_to_hex(object));
+		hashclr(result);
+	} else {
+		if (write_sha1_file(msg->buf.buf, msg->buf.len, blob_type, result)) {
+			error(_("unable to write note object"));
+			if (path)
+				error(_("The note contents has been left in %s"),
+				      path);
+			exit(128);
+		}
+	}
+
+	if (path) {
+		unlink_or_warn(path);
+		free(path);
+	}
+}
+
+static int parse_msg_arg(const struct option *opt, const char *arg, int unset)
+{
+	struct msg_arg *msg = opt->value;
+
+	strbuf_grow(&(msg->buf), strlen(arg) + 2);
+	if (msg->buf.len)
+		strbuf_addch(&(msg->buf), '\n');
+	strbuf_addstr(&(msg->buf), arg);
+	stripspace(&(msg->buf), 0);
+
+	msg->given = 1;
+	return 0;
+}
+
+static int parse_file_arg(const struct option *opt, const char *arg, int unset)
+{
+	struct msg_arg *msg = opt->value;
+
+	if (msg->buf.len)
+		strbuf_addch(&(msg->buf), '\n');
+	if (!strcmp(arg, "-")) {
+		if (strbuf_read(&(msg->buf), 0, 1024) < 0)
+			die_errno(_("cannot read '%s'"), arg);
+	} else if (strbuf_read_file(&(msg->buf), arg, 1024) < 0)
+		die_errno(_("could not open or read '%s'"), arg);
+	stripspace(&(msg->buf), 0);
+
+	msg->given = 1;
+	return 0;
+}
+
+static int parse_reuse_arg(const struct option *opt, const char *arg, int unset)
+{
+	struct msg_arg *msg = opt->value;
+	char *buf;
+	unsigned char object[20];
+	enum object_type type;
+	unsigned long len;
+
+	if (msg->buf.len)
+		strbuf_addch(&(msg->buf), '\n');
+
+	if (get_sha1(arg, object))
+		die(_("Failed to resolve '%s' as a valid ref."), arg);
+	if (!(buf = read_sha1_file(object, &type, &len)) || !len) {
+		free(buf);
+		die(_("Failed to read object '%s'."), arg);;
+	}
+	strbuf_add(&(msg->buf), buf, len);
+	free(buf);
+
+	msg->given = 1;
+	return 0;
+}
+
+static int parse_reedit_arg(const struct option *opt, const char *arg, int unset)
+{
+	struct msg_arg *msg = opt->value;
+	msg->use_editor = 1;
+	return parse_reuse_arg(opt, arg, unset);
+}
+
+void commit_notes(struct notes_tree *t, const char *msg)
+{
+	struct strbuf buf = STRBUF_INIT;
+	unsigned char commit_sha1[20];
+
+	if (!t)
+		t = &default_notes_tree;
+	if (!t->initialized || !t->ref || !*t->ref)
+		die(_("Cannot commit uninitialized/unreferenced notes tree"));
+	if (!t->dirty)
+		return; /* don't have to commit an unchanged tree */
+
+	/* Prepare commit message and reflog message */
+	strbuf_addstr(&buf, msg);
+	if (buf.buf[buf.len - 1] != '\n')
+		strbuf_addch(&buf, '\n'); /* Make sure msg ends with newline */
+
+	create_notes_commit(t, NULL, &buf, commit_sha1);
+	strbuf_insert(&buf, 0, "notes: ", 7); /* commit message starts at index 7 */
+	update_ref(buf.buf, t->ref, commit_sha1, NULL, 0, DIE_ON_ERR);
+
+	strbuf_release(&buf);
+}
+
+combine_notes_fn parse_combine_notes_fn(const char *v)
+{
+	if (!strcasecmp(v, "overwrite"))
+		return combine_notes_overwrite;
+	else if (!strcasecmp(v, "ignore"))
+		return combine_notes_ignore;
+	else if (!strcasecmp(v, "concatenate"))
+		return combine_notes_concatenate;
+	else if (!strcasecmp(v, "cat_sort_uniq"))
+		return combine_notes_cat_sort_uniq;
+	else
+		return NULL;
+}
+
+static int notes_rewrite_config(const char *k, const char *v, void *cb)
+{
+	struct notes_rewrite_cfg *c = cb;
+	if (!prefixcmp(k, "notes.rewrite.") && !strcmp(k+14, c->cmd)) {
+		c->enabled = git_config_bool(k, v);
+		return 0;
+	} else if (!c->mode_from_env && !strcmp(k, "notes.rewritemode")) {
+		if (!v)
+			config_error_nonbool(k);
+		c->combine = parse_combine_notes_fn(v);
+		if (!c->combine) {
+			error(_("Bad notes.rewriteMode value: '%s'"), v);
+			return 1;
+		}
+		return 0;
+	} else if (!c->refs_from_env && !strcmp(k, "notes.rewriteref")) {
+		/* note that a refs/ prefix is implied in the
+		 * underlying for_each_glob_ref */
+		if (!prefixcmp(v, "refs/notes/"))
+			string_list_add_refs_by_glob(c->refs, v);
+		else
+			warning(_("Refusing to rewrite notes in %s"
+				" (outside of refs/notes/)"), v);
+		return 0;
+	}
+
+	return 0;
+}
+
+
+struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd)
+{
+	struct notes_rewrite_cfg *c = xmalloc(sizeof(struct notes_rewrite_cfg));
+	const char *rewrite_mode_env = getenv(GIT_NOTES_REWRITE_MODE_ENVIRONMENT);
+	const char *rewrite_refs_env = getenv(GIT_NOTES_REWRITE_REF_ENVIRONMENT);
+	c->cmd = cmd;
+	c->enabled = 1;
+	c->combine = combine_notes_concatenate;
+	c->refs = xcalloc(1, sizeof(struct string_list));
+	c->refs->strdup_strings = 1;
+	c->refs_from_env = 0;
+	c->mode_from_env = 0;
+	if (rewrite_mode_env) {
+		c->mode_from_env = 1;
+		c->combine = parse_combine_notes_fn(rewrite_mode_env);
+		if (!c->combine)
+			/* TRANSLATORS: The first %s is the name of the
+			   environment variable, the second %s is its value */
+			error(_("Bad %s value: '%s'"), GIT_NOTES_REWRITE_MODE_ENVIRONMENT,
+					rewrite_mode_env);
+	}
+	if (rewrite_refs_env) {
+		c->refs_from_env = 1;
+		string_list_add_refs_from_colon_sep(c->refs, rewrite_refs_env);
+	}
+	git_config(notes_rewrite_config, c);
+	if (!c->enabled || !c->refs->nr) {
+		string_list_clear(c->refs, 0);
+		free(c->refs);
+		free(c);
+		return NULL;
+	}
+	c->trees = load_notes_trees(c->refs);
+	string_list_clear(c->refs, 0);
+	free(c->refs);
+	return c;
+}
+
+int copy_note_for_rewrite(struct notes_rewrite_cfg *c,
+			  const unsigned char *from_obj, const unsigned char *to_obj)
+{
+	int ret = 0;
+	int i;
+	for (i = 0; c->trees[i]; i++)
+		ret = copy_note(c->trees[i], from_obj, to_obj, 1, c->combine) || ret;
+	return ret;
+}
+
+void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c)
+{
+	int i;
+	for (i = 0; c->trees[i]; i++) {
+		commit_notes(c->trees[i], "Notes added by 'git notes copy'");
+		free_notes(c->trees[i]);
+	}
+	free(c->trees);
+	free(c);
+}
+
+static int notes_copy_from_stdin(int force, const char *rewrite_cmd)
+{
+	struct strbuf buf = STRBUF_INIT;
+	struct notes_rewrite_cfg *c = NULL;
+	struct notes_tree *t = NULL;
+	int ret = 0;
+
+	if (rewrite_cmd) {
+		c = init_copy_notes_for_rewrite(rewrite_cmd);
+		if (!c)
+			return 0;
+	} else {
+		init_notes(NULL, NULL, NULL, 0);
+		t = &default_notes_tree;
+	}
+
+	while (strbuf_getline(&buf, stdin, '\n') != EOF) {
+		unsigned char from_obj[20], to_obj[20];
+		struct strbuf **split;
+		int err;
+
+		split = strbuf_split(&buf, ' ');
+		if (!split[0] || !split[1])
+			die(_("Malformed input line: '%s'."), buf.buf);
+		strbuf_rtrim(split[0]);
+		strbuf_rtrim(split[1]);
+		if (get_sha1(split[0]->buf, from_obj))
+			die(_("Failed to resolve '%s' as a valid ref."), split[0]->buf);
+		if (get_sha1(split[1]->buf, to_obj))
+			die(_("Failed to resolve '%s' as a valid ref."), split[1]->buf);
+
+		if (rewrite_cmd)
+			err = copy_note_for_rewrite(c, from_obj, to_obj);
+		else
+			err = copy_note(t, from_obj, to_obj, force,
+					combine_notes_overwrite);
+
+		if (err) {
+			error(_("Failed to copy notes from '%s' to '%s'"),
+			      split[0]->buf, split[1]->buf);
+			ret = 1;
+		}
+
+		strbuf_list_free(split);
+	}
+
+	if (!rewrite_cmd) {
+		commit_notes(t, "Notes added by 'git notes copy'");
+		free_notes(t);
+	} else {
+		finish_copy_notes_for_rewrite(c);
+	}
+	return ret;
+}
+
+static struct notes_tree *init_notes_check(const char *subcommand)
+{
+	struct notes_tree *t;
+	init_notes(NULL, NULL, NULL, 0);
+	t = &default_notes_tree;
+
+	if (prefixcmp(t->ref, "refs/notes/"))
+		die("Refusing to %s notes in %s (outside of refs/notes/)",
+		    subcommand, t->ref);
+	return t;
+}
+
+static int list(int argc, const char **argv, const char *prefix)
+{
+	struct notes_tree *t;
+	unsigned char object[20];
+	const unsigned char *note;
+	int retval = -1;
+	struct option options[] = {
+		OPT_END()
+	};
+
+	if (argc)
+		argc = parse_options(argc, argv, prefix, options,
+				     git_notes_list_usage, 0);
+
+	if (1 < argc) {
+		error(_("too many parameters"));
+		usage_with_options(git_notes_list_usage, options);
+	}
+
+	t = init_notes_check("list");
+	if (argc) {
+		if (get_sha1(argv[0], object))
+			die(_("Failed to resolve '%s' as a valid ref."), argv[0]);
+		note = get_note(t, object);
+		if (note) {
+			puts(sha1_to_hex(note));
+			retval = 0;
+		} else
+			retval = error(_("No note found for object %s."),
+				       sha1_to_hex(object));
+	} else
+		retval = for_each_note(t, 0, list_each_note, NULL);
+
+	free_notes(t);
+	return retval;
+}
+
+static int append_edit(int argc, const char **argv, const char *prefix);
+
+static int add(int argc, const char **argv, const char *prefix)
+{
+	int retval = 0, force = 0;
+	const char *object_ref;
+	struct notes_tree *t;
+	unsigned char object[20], new_note[20];
+	char logmsg[100];
+	const unsigned char *note;
+	struct msg_arg msg = { 0, 0, STRBUF_INIT };
+	struct option options[] = {
+		{ OPTION_CALLBACK, 'm', "message", &msg, "msg",
+			"note contents as a string", PARSE_OPT_NONEG,
+			parse_msg_arg},
+		{ OPTION_CALLBACK, 'F', "file", &msg, "file",
+			"note contents in a file", PARSE_OPT_NONEG,
+			parse_file_arg},
+		{ OPTION_CALLBACK, 'c', "reedit-message", &msg, "object",
+			"reuse and edit specified note object", PARSE_OPT_NONEG,
+			parse_reedit_arg},
+		{ OPTION_CALLBACK, 'C', "reuse-message", &msg, "object",
+			"reuse specified note object", PARSE_OPT_NONEG,
+			parse_reuse_arg},
+		OPT__FORCE(&force, "replace existing notes"),
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, prefix, options, git_notes_add_usage,
+			     PARSE_OPT_KEEP_ARGV0);
+
+	if (2 < argc) {
+		error(_("too many parameters"));
+		usage_with_options(git_notes_add_usage, options);
+	}
+
+	object_ref = argc > 1 ? argv[1] : "HEAD";
+
+	if (get_sha1(object_ref, object))
+		die(_("Failed to resolve '%s' as a valid ref."), object_ref);
+
+	t = init_notes_check("add");
+	note = get_note(t, object);
+
+	if (note) {
+		if (!force) {
+			if (!msg.given) {
+				/*
+				 * Redirect to "edit" subcommand.
+				 *
+				 * We only end up here if none of -m/-F/-c/-C
+				 * or -f are given. The original args are
+				 * therefore still in argv[0-1].
+				 */
+				argv[0] = "edit";
+				free_notes(t);
+				return append_edit(argc, argv, prefix);
+			}
+			retval = error(_("Cannot add notes. Found existing notes "
+				       "for object %s. Use '-f' to overwrite "
+				       "existing notes"), sha1_to_hex(object));
+			goto out;
+		}
+		fprintf(stderr, _("Overwriting existing notes for object %s\n"),
+			sha1_to_hex(object));
+	}
+
+	create_note(object, &msg, 0, note, new_note);
+
+	if (is_null_sha1(new_note))
+		remove_note(t, object);
+	else if (add_note(t, object, new_note, combine_notes_overwrite))
+		die("BUG: combine_notes_overwrite failed");
+
+	snprintf(logmsg, sizeof(logmsg), "Notes %s by 'git notes %s'",
+		 is_null_sha1(new_note) ? "removed" : "added", "add");
+	commit_notes(t, logmsg);
+out:
+	free_notes(t);
+	strbuf_release(&(msg.buf));
+	return retval;
+}
+
+static int copy(int argc, const char **argv, const char *prefix)
+{
+	int retval = 0, force = 0, from_stdin = 0;
+	const unsigned char *from_note, *note;
+	const char *object_ref;
+	unsigned char object[20], from_obj[20];
+	struct notes_tree *t;
+	const char *rewrite_cmd = NULL;
+	struct option options[] = {
+		OPT__FORCE(&force, "replace existing notes"),
+		OPT_BOOLEAN(0, "stdin", &from_stdin, "read objects from stdin"),
+		OPT_STRING(0, "for-rewrite", &rewrite_cmd, "command",
+			   "load rewriting config for <command> (implies "
+			   "--stdin)"),
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, prefix, options, git_notes_copy_usage,
+			     0);
+
+	if (from_stdin || rewrite_cmd) {
+		if (argc) {
+			error(_("too many parameters"));
+			usage_with_options(git_notes_copy_usage, options);
+		} else {
+			return notes_copy_from_stdin(force, rewrite_cmd);
+		}
+	}
+
+	if (argc < 2) {
+		error(_("too few parameters"));
+		usage_with_options(git_notes_copy_usage, options);
+	}
+	if (2 < argc) {
+		error(_("too many parameters"));
+		usage_with_options(git_notes_copy_usage, options);
+	}
+
+	if (get_sha1(argv[0], from_obj))
+		die(_("Failed to resolve '%s' as a valid ref."), argv[0]);
+
+	object_ref = 1 < argc ? argv[1] : "HEAD";
+
+	if (get_sha1(object_ref, object))
+		die(_("Failed to resolve '%s' as a valid ref."), object_ref);
+
+	t = init_notes_check("copy");
+	note = get_note(t, object);
+
+	if (note) {
+		if (!force) {
+			retval = error(_("Cannot copy notes. Found existing "
+				       "notes for object %s. Use '-f' to "
+				       "overwrite existing notes"),
+				       sha1_to_hex(object));
+			goto out;
+		}
+		fprintf(stderr, _("Overwriting existing notes for object %s\n"),
+			sha1_to_hex(object));
+	}
+
+	from_note = get_note(t, from_obj);
+	if (!from_note) {
+		retval = error(_("Missing notes on source object %s. Cannot "
+			       "copy."), sha1_to_hex(from_obj));
+		goto out;
+	}
+
+	if (add_note(t, object, from_note, combine_notes_overwrite))
+		die("BUG: combine_notes_overwrite failed");
+	commit_notes(t, "Notes added by 'git notes copy'");
+out:
+	free_notes(t);
+	return retval;
+}
+
+static int append_edit(int argc, const char **argv, const char *prefix)
+{
+	const char *object_ref;
+	struct notes_tree *t;
+	unsigned char object[20], new_note[20];
+	const unsigned char *note;
+	char logmsg[100];
+	const char * const *usage;
+	struct msg_arg msg = { 0, 0, STRBUF_INIT };
+	struct option options[] = {
+		{ OPTION_CALLBACK, 'm', "message", &msg, "msg",
+			"note contents as a string", PARSE_OPT_NONEG,
+			parse_msg_arg},
+		{ OPTION_CALLBACK, 'F', "file", &msg, "file",
+			"note contents in a file", PARSE_OPT_NONEG,
+			parse_file_arg},
+		{ OPTION_CALLBACK, 'c', "reedit-message", &msg, "object",
+			"reuse and edit specified note object", PARSE_OPT_NONEG,
+			parse_reedit_arg},
+		{ OPTION_CALLBACK, 'C', "reuse-message", &msg, "object",
+			"reuse specified note object", PARSE_OPT_NONEG,
+			parse_reuse_arg},
+		OPT_END()
+	};
+	int edit = !strcmp(argv[0], "edit");
+
+	usage = edit ? git_notes_edit_usage : git_notes_append_usage;
+	argc = parse_options(argc, argv, prefix, options, usage,
+			     PARSE_OPT_KEEP_ARGV0);
+
+	if (2 < argc) {
+		error(_("too many parameters"));
+		usage_with_options(usage, options);
+	}
+
+	if (msg.given && edit)
+		fprintf(stderr, _("The -m/-F/-c/-C options have been deprecated "
+			"for the 'edit' subcommand.\n"
+			"Please use 'git notes add -f -m/-F/-c/-C' instead.\n"));
+
+	object_ref = 1 < argc ? argv[1] : "HEAD";
+
+	if (get_sha1(object_ref, object))
+		die(_("Failed to resolve '%s' as a valid ref."), object_ref);
+
+	t = init_notes_check(argv[0]);
+	note = get_note(t, object);
+
+	create_note(object, &msg, !edit, note, new_note);
+
+	if (is_null_sha1(new_note))
+		remove_note(t, object);
+	else if (add_note(t, object, new_note, combine_notes_overwrite))
+		die("BUG: combine_notes_overwrite failed");
+
+	snprintf(logmsg, sizeof(logmsg), "Notes %s by 'git notes %s'",
+		 is_null_sha1(new_note) ? "removed" : "added", argv[0]);
+	commit_notes(t, logmsg);
+	free_notes(t);
+	strbuf_release(&(msg.buf));
+	return 0;
+}
+
+static int show(int argc, const char **argv, const char *prefix)
+{
+	const char *object_ref;
+	struct notes_tree *t;
+	unsigned char object[20];
+	const unsigned char *note;
+	int retval;
+	struct option options[] = {
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, prefix, options, git_notes_show_usage,
+			     0);
+
+	if (1 < argc) {
+		error(_("too many parameters"));
+		usage_with_options(git_notes_show_usage, options);
+	}
+
+	object_ref = argc ? argv[0] : "HEAD";
+
+	if (get_sha1(object_ref, object))
+		die(_("Failed to resolve '%s' as a valid ref."), object_ref);
+
+	t = init_notes_check("show");
+	note = get_note(t, object);
+
+	if (!note)
+		retval = error(_("No note found for object %s."),
+			       sha1_to_hex(object));
+	else {
+		const char *show_args[3] = {"show", sha1_to_hex(note), NULL};
+		retval = execv_git_cmd(show_args);
+	}
+	free_notes(t);
+	return retval;
+}
+
+static int merge_abort(struct notes_merge_options *o)
+{
+	int ret = 0;
+
+	/*
+	 * Remove .git/NOTES_MERGE_PARTIAL and .git/NOTES_MERGE_REF, and call
+	 * notes_merge_abort() to remove .git/NOTES_MERGE_WORKTREE.
+	 */
+
+	if (delete_ref("NOTES_MERGE_PARTIAL", NULL, 0))
+		ret += error("Failed to delete ref NOTES_MERGE_PARTIAL");
+	if (delete_ref("NOTES_MERGE_REF", NULL, REF_NODEREF))
+		ret += error("Failed to delete ref NOTES_MERGE_REF");
+	if (notes_merge_abort(o))
+		ret += error("Failed to remove 'git notes merge' worktree");
+	return ret;
+}
+
+static int merge_commit(struct notes_merge_options *o)
+{
+	struct strbuf msg = STRBUF_INIT;
+	unsigned char sha1[20], parent_sha1[20];
+	struct notes_tree *t;
+	struct commit *partial;
+	struct pretty_print_context pretty_ctx;
+	void *local_ref_to_free;
+	int ret;
+
+	/*
+	 * Read partial merge result from .git/NOTES_MERGE_PARTIAL,
+	 * and target notes ref from .git/NOTES_MERGE_REF.
+	 */
+
+	if (get_sha1("NOTES_MERGE_PARTIAL", sha1))
+		die("Failed to read ref NOTES_MERGE_PARTIAL");
+	else if (!(partial = lookup_commit_reference(sha1)))
+		die("Could not find commit from NOTES_MERGE_PARTIAL.");
+	else if (parse_commit(partial))
+		die("Could not parse commit from NOTES_MERGE_PARTIAL.");
+
+	if (partial->parents)
+		hashcpy(parent_sha1, partial->parents->item->object.sha1);
+	else
+		hashclr(parent_sha1);
+
+	t = xcalloc(1, sizeof(struct notes_tree));
+	init_notes(t, "NOTES_MERGE_PARTIAL", combine_notes_overwrite, 0);
+
+	o->local_ref = local_ref_to_free =
+		resolve_refdup("NOTES_MERGE_REF", sha1, 0, NULL);
+	if (!o->local_ref)
+		die("Failed to resolve NOTES_MERGE_REF");
+
+	if (notes_merge_commit(o, t, partial, sha1))
+		die("Failed to finalize notes merge");
+
+	/* Reuse existing commit message in reflog message */
+	memset(&pretty_ctx, 0, sizeof(pretty_ctx));
+	format_commit_message(partial, "%s", &msg, &pretty_ctx);
+	strbuf_trim(&msg);
+	strbuf_insert(&msg, 0, "notes: ", 7);
+	update_ref(msg.buf, o->local_ref, sha1,
+		   is_null_sha1(parent_sha1) ? NULL : parent_sha1,
+		   0, DIE_ON_ERR);
+
+	free_notes(t);
+	strbuf_release(&msg);
+	ret = merge_abort(o);
+	free(local_ref_to_free);
+	return ret;
+}
+
+static int merge(int argc, const char **argv, const char *prefix)
+{
+	struct strbuf remote_ref = STRBUF_INIT, msg = STRBUF_INIT;
+	unsigned char result_sha1[20];
+	struct notes_tree *t;
+	struct notes_merge_options o;
+	int do_merge = 0, do_commit = 0, do_abort = 0;
+	int verbosity = 0, result;
+	const char *strategy = NULL;
+	struct option options[] = {
+		OPT_GROUP("General options"),
+		OPT__VERBOSITY(&verbosity),
+		OPT_GROUP("Merge options"),
+		OPT_STRING('s', "strategy", &strategy, "strategy",
+			   "resolve notes conflicts using the given strategy "
+			   "(manual/ours/theirs/union/cat_sort_uniq)"),
+		OPT_GROUP("Committing unmerged notes"),
+		{ OPTION_BOOLEAN, 0, "commit", &do_commit, NULL,
+			"finalize notes merge by committing unmerged notes",
+			PARSE_OPT_NOARG | PARSE_OPT_NONEG },
+		OPT_GROUP("Aborting notes merge resolution"),
+		{ OPTION_BOOLEAN, 0, "abort", &do_abort, NULL,
+			"abort notes merge",
+			PARSE_OPT_NOARG | PARSE_OPT_NONEG },
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, prefix, options,
+			     git_notes_merge_usage, 0);
+
+	if (strategy || do_commit + do_abort == 0)
+		do_merge = 1;
+	if (do_merge + do_commit + do_abort != 1) {
+		error("cannot mix --commit, --abort or -s/--strategy");
+		usage_with_options(git_notes_merge_usage, options);
+	}
+
+	if (do_merge && argc != 1) {
+		error("Must specify a notes ref to merge");
+		usage_with_options(git_notes_merge_usage, options);
+	} else if (!do_merge && argc) {
+		error("too many parameters");
+		usage_with_options(git_notes_merge_usage, options);
+	}
+
+	init_notes_merge_options(&o);
+	o.verbosity = verbosity + NOTES_MERGE_VERBOSITY_DEFAULT;
+
+	if (do_abort)
+		return merge_abort(&o);
+	if (do_commit)
+		return merge_commit(&o);
+
+	o.local_ref = default_notes_ref();
+	strbuf_addstr(&remote_ref, argv[0]);
+	expand_notes_ref(&remote_ref);
+	o.remote_ref = remote_ref.buf;
+
+	if (strategy) {
+		if (!strcmp(strategy, "manual"))
+			o.strategy = NOTES_MERGE_RESOLVE_MANUAL;
+		else if (!strcmp(strategy, "ours"))
+			o.strategy = NOTES_MERGE_RESOLVE_OURS;
+		else if (!strcmp(strategy, "theirs"))
+			o.strategy = NOTES_MERGE_RESOLVE_THEIRS;
+		else if (!strcmp(strategy, "union"))
+			o.strategy = NOTES_MERGE_RESOLVE_UNION;
+		else if (!strcmp(strategy, "cat_sort_uniq"))
+			o.strategy = NOTES_MERGE_RESOLVE_CAT_SORT_UNIQ;
+		else {
+			error("Unknown -s/--strategy: %s", strategy);
+			usage_with_options(git_notes_merge_usage, options);
+		}
+	}
+
+	t = init_notes_check("merge");
+
+	strbuf_addf(&msg, "notes: Merged notes from %s into %s",
+		    remote_ref.buf, default_notes_ref());
+	strbuf_add(&(o.commit_msg), msg.buf + 7, msg.len - 7); /* skip "notes: " */
+
+	result = notes_merge(&o, t, result_sha1);
+
+	if (result >= 0) /* Merge resulted (trivially) in result_sha1 */
+		/* Update default notes ref with new commit */
+		update_ref(msg.buf, default_notes_ref(), result_sha1, NULL,
+			   0, DIE_ON_ERR);
+	else { /* Merge has unresolved conflicts */
+		/* Update .git/NOTES_MERGE_PARTIAL with partial merge result */
+		update_ref(msg.buf, "NOTES_MERGE_PARTIAL", result_sha1, NULL,
+			   0, DIE_ON_ERR);
+		/* Store ref-to-be-updated into .git/NOTES_MERGE_REF */
+		if (create_symref("NOTES_MERGE_REF", default_notes_ref(), NULL))
+			die("Failed to store link to current notes ref (%s)",
+			    default_notes_ref());
+		printf("Automatic notes merge failed. Fix conflicts in %s and "
+		       "commit the result with 'git notes merge --commit', or "
+		       "abort the merge with 'git notes merge --abort'.\n",
+		       git_path(NOTES_MERGE_WORKTREE));
+	}
+
+	free_notes(t);
+	strbuf_release(&remote_ref);
+	strbuf_release(&msg);
+	return result < 0; /* return non-zero on conflicts */
+}
+
+#define IGNORE_MISSING 1
+
+static int remove_one_note(struct notes_tree *t, const char *name, unsigned flag)
+{
+	int status;
+	unsigned char sha1[20];
+	if (get_sha1(name, sha1))
+		return error(_("Failed to resolve '%s' as a valid ref."), name);
+	status = remove_note(t, sha1);
+	if (status)
+		fprintf(stderr, _("Object %s has no note\n"), name);
+	else
+		fprintf(stderr, _("Removing note for object %s\n"), name);
+	return (flag & IGNORE_MISSING) ? 0 : status;
+}
+
+static int remove_cmd(int argc, const char **argv, const char *prefix)
+{
+	unsigned flag = 0;
+	int from_stdin = 0;
+	struct option options[] = {
+		OPT_BIT(0, "ignore-missing", &flag,
+			"attempt to remove non-existent note is not an error",
+			IGNORE_MISSING),
+		OPT_BOOLEAN(0, "stdin", &from_stdin,
+			    "read object names from the standard input"),
+		OPT_END()
+	};
+	struct notes_tree *t;
+	int retval = 0;
+
+	argc = parse_options(argc, argv, prefix, options,
+			     git_notes_remove_usage, 0);
+
+	t = init_notes_check("remove");
+
+	if (!argc && !from_stdin) {
+		retval = remove_one_note(t, "HEAD", flag);
+	} else {
+		while (*argv) {
+			retval |= remove_one_note(t, *argv, flag);
+			argv++;
+		}
+	}
+	if (from_stdin) {
+		struct strbuf sb = STRBUF_INIT;
+		while (strbuf_getwholeline(&sb, stdin, '\n') != EOF) {
+			strbuf_rtrim(&sb);
+			retval |= remove_one_note(t, sb.buf, flag);
+		}
+		strbuf_release(&sb);
+	}
+	if (!retval)
+		commit_notes(t, "Notes removed by 'git notes remove'");
+	free_notes(t);
+	return retval;
+}
+
+static int prune(int argc, const char **argv, const char *prefix)
+{
+	struct notes_tree *t;
+	int show_only = 0, verbose = 0;
+	struct option options[] = {
+		OPT__DRY_RUN(&show_only, "do not remove, show only"),
+		OPT__VERBOSE(&verbose, "report pruned notes"),
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, prefix, options, git_notes_prune_usage,
+			     0);
+
+	if (argc) {
+		error(_("too many parameters"));
+		usage_with_options(git_notes_prune_usage, options);
+	}
+
+	t = init_notes_check("prune");
+
+	prune_notes(t, (verbose ? NOTES_PRUNE_VERBOSE : 0) |
+		(show_only ? NOTES_PRUNE_VERBOSE|NOTES_PRUNE_DRYRUN : 0) );
+	if (!show_only)
+		commit_notes(t, "Notes removed by 'git notes prune'");
+	free_notes(t);
+	return 0;
+}
+
+static int get_ref(int argc, const char **argv, const char *prefix)
+{
+	struct option options[] = { OPT_END() };
+	argc = parse_options(argc, argv, prefix, options,
+			     git_notes_get_ref_usage, 0);
+
+	if (argc) {
+		error("too many parameters");
+		usage_with_options(git_notes_get_ref_usage, options);
+	}
+
+	puts(default_notes_ref());
+	return 0;
+}
+
+int cmd_notes(int argc, const char **argv, const char *prefix)
+{
+	int result;
+	const char *override_notes_ref = NULL;
+	struct option options[] = {
+		OPT_STRING(0, "ref", &override_notes_ref, "notes_ref",
+			   "use notes from <notes_ref>"),
+		OPT_END()
+	};
+
+	git_config(git_default_config, NULL);
+	argc = parse_options(argc, argv, prefix, options, git_notes_usage,
+			     PARSE_OPT_STOP_AT_NON_OPTION);
+
+	if (override_notes_ref) {
+		struct strbuf sb = STRBUF_INIT;
+		strbuf_addstr(&sb, override_notes_ref);
+		expand_notes_ref(&sb);
+		setenv("GIT_NOTES_REF", sb.buf, 1);
+		strbuf_release(&sb);
+	}
+
+	if (argc < 1 || !strcmp(argv[0], "list"))
+		result = list(argc, argv, prefix);
+	else if (!strcmp(argv[0], "add"))
+		result = add(argc, argv, prefix);
+	else if (!strcmp(argv[0], "copy"))
+		result = copy(argc, argv, prefix);
+	else if (!strcmp(argv[0], "append") || !strcmp(argv[0], "edit"))
+		result = append_edit(argc, argv, prefix);
+	else if (!strcmp(argv[0], "show"))
+		result = show(argc, argv, prefix);
+	else if (!strcmp(argv[0], "merge"))
+		result = merge(argc, argv, prefix);
+	else if (!strcmp(argv[0], "remove"))
+		result = remove_cmd(argc, argv, prefix);
+	else if (!strcmp(argv[0], "prune"))
+		result = prune(argc, argv, prefix);
+	else if (!strcmp(argv[0], "get-ref"))
+		result = get_ref(argc, argv, prefix);
+	else {
+		result = error(_("Unknown subcommand: %s"), argv[0]);
+		usage_with_options(git_notes_usage, options);
+	}
+
+	return result ? 1 : 0;
+}
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
new file mode 100644
index 0000000..782e7d0
--- /dev/null
+++ b/builtin/pack-objects.c
@@ -0,0 +1,2603 @@
+#include "builtin.h"
+#include "cache.h"
+#include "attr.h"
+#include "object.h"
+#include "blob.h"
+#include "commit.h"
+#include "tag.h"
+#include "tree.h"
+#include "delta.h"
+#include "pack.h"
+#include "pack-revindex.h"
+#include "csum-file.h"
+#include "tree-walk.h"
+#include "diff.h"
+#include "revision.h"
+#include "list-objects.h"
+#include "progress.h"
+#include "refs.h"
+#include "streaming.h"
+#include "thread-utils.h"
+
+static const char *pack_usage[] = {
+	"git pack-objects --stdout [options...] [< ref-list | < object-list]",
+	"git pack-objects [options...] base-name [< ref-list | < object-list]",
+	NULL
+};
+
+struct object_entry {
+	struct pack_idx_entry idx;
+	unsigned long size;	/* uncompressed size */
+	struct packed_git *in_pack; 	/* already in pack */
+	off_t in_pack_offset;
+	struct object_entry *delta;	/* delta base object */
+	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
+					     */
+	void *delta_data;	/* cached delta (uncompressed) */
+	unsigned long delta_size;	/* delta data size (uncompressed) */
+	unsigned long z_delta_size;	/* delta data size (compressed) */
+	unsigned int hash;	/* name hint hash */
+	enum object_type type;
+	enum object_type in_pack_type;	/* could be delta */
+	unsigned char in_pack_header_size;
+	unsigned char preferred_base; /* we do not pack this, but is available
+				       * to be used as the base object to delta
+				       * objects against.
+				       */
+	unsigned char no_try_delta;
+	unsigned char tagged; /* near the very tip of refs */
+	unsigned char filled; /* assigned write-order */
+};
+
+/*
+ * 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.
+ */
+static struct object_entry *objects;
+static struct pack_idx_entry **written_list;
+static uint32_t nr_objects, nr_alloc, nr_result, nr_written;
+
+static int non_empty;
+static int reuse_delta = 1, reuse_object = 1;
+static int keep_unreachable, unpack_unreachable, include_tag;
+static unsigned long unpack_unreachable_expiration;
+static int local;
+static int incremental;
+static int ignore_packed_keep;
+static int allow_ofs_delta;
+static struct pack_idx_option pack_idx_opts;
+static const char *base_name;
+static int progress = 1;
+static int window = 10;
+static unsigned long pack_size_limit;
+static int depth = 50;
+static int delta_search_threads;
+static int pack_to_stdout;
+static int num_preferred_base;
+static struct progress *progress_state;
+static int pack_compression_level = Z_DEFAULT_COMPRESSION;
+static int pack_compression_seen;
+
+static unsigned long delta_cache_size = 0;
+static unsigned long max_delta_cache_size = 256 * 1024 * 1024;
+static unsigned long cache_max_small_delta_size = 1000;
+
+static unsigned long window_memory_limit = 0;
+
+/*
+ * The object names in objects array are hashed with this hashtable,
+ * to help looking up the entry by object name.
+ * This hashtable is built after all the objects are seen.
+ */
+static int *object_ix;
+static int object_ix_hashsz;
+static struct object_entry *locate_object_entry(const unsigned char *sha1);
+
+/*
+ * stats
+ */
+static uint32_t written, written_delta;
+static uint32_t reused, reused_delta;
+
+
+static void *get_delta(struct object_entry *entry)
+{
+	unsigned long size, base_size, delta_size;
+	void *buf, *base_buf, *delta_buf;
+	enum object_type type;
+
+	buf = read_sha1_file(entry->idx.sha1, &type, &size);
+	if (!buf)
+		die("unable to read %s", sha1_to_hex(entry->idx.sha1));
+	base_buf = read_sha1_file(entry->delta->idx.sha1, &type, &base_size);
+	if (!base_buf)
+		die("unable to read %s", sha1_to_hex(entry->delta->idx.sha1));
+	delta_buf = diff_delta(base_buf, base_size,
+			       buf, size, &delta_size, 0);
+	if (!delta_buf || delta_size != entry->delta_size)
+		die("delta size changed");
+	free(buf);
+	free(base_buf);
+	return delta_buf;
+}
+
+static unsigned long do_compress(void **pptr, unsigned long size)
+{
+	git_zstream stream;
+	void *in, *out;
+	unsigned long maxsize;
+
+	memset(&stream, 0, sizeof(stream));
+	git_deflate_init(&stream, pack_compression_level);
+	maxsize = git_deflate_bound(&stream, size);
+
+	in = *pptr;
+	out = xmalloc(maxsize);
+	*pptr = out;
+
+	stream.next_in = in;
+	stream.avail_in = size;
+	stream.next_out = out;
+	stream.avail_out = maxsize;
+	while (git_deflate(&stream, Z_FINISH) == Z_OK)
+		; /* nothing */
+	git_deflate_end(&stream);
+
+	free(in);
+	return stream.total_out;
+}
+
+static unsigned long write_large_blob_data(struct git_istream *st, struct sha1file *f,
+					   const unsigned char *sha1)
+{
+	git_zstream stream;
+	unsigned char ibuf[1024 * 16];
+	unsigned char obuf[1024 * 16];
+	unsigned long olen = 0;
+
+	memset(&stream, 0, sizeof(stream));
+	git_deflate_init(&stream, pack_compression_level);
+
+	for (;;) {
+		ssize_t readlen;
+		int zret = Z_OK;
+		readlen = read_istream(st, ibuf, sizeof(ibuf));
+		if (readlen == -1)
+			die(_("unable to read %s"), sha1_to_hex(sha1));
+
+		stream.next_in = ibuf;
+		stream.avail_in = readlen;
+		while ((stream.avail_in || readlen == 0) &&
+		       (zret == Z_OK || zret == Z_BUF_ERROR)) {
+			stream.next_out = obuf;
+			stream.avail_out = sizeof(obuf);
+			zret = git_deflate(&stream, readlen ? 0 : Z_FINISH);
+			sha1write(f, obuf, stream.next_out - obuf);
+			olen += stream.next_out - obuf;
+		}
+		if (stream.avail_in)
+			die(_("deflate error (%d)"), zret);
+		if (readlen == 0) {
+			if (zret != Z_STREAM_END)
+				die(_("deflate error (%d)"), zret);
+			break;
+		}
+	}
+	git_deflate_end(&stream);
+	return olen;
+}
+
+/*
+ * we are going to reuse the existing object data as is.  make
+ * sure it is not corrupt.
+ */
+static int check_pack_inflate(struct packed_git *p,
+		struct pack_window **w_curs,
+		off_t offset,
+		off_t len,
+		unsigned long expect)
+{
+	git_zstream stream;
+	unsigned char fakebuf[4096], *in;
+	int st;
+
+	memset(&stream, 0, sizeof(stream));
+	git_inflate_init(&stream);
+	do {
+		in = use_pack(p, w_curs, offset, &stream.avail_in);
+		stream.next_in = in;
+		stream.next_out = fakebuf;
+		stream.avail_out = sizeof(fakebuf);
+		st = git_inflate(&stream, Z_FINISH);
+		offset += stream.next_in - in;
+	} while (st == Z_OK || st == Z_BUF_ERROR);
+	git_inflate_end(&stream);
+	return (st == Z_STREAM_END &&
+		stream.total_out == expect &&
+		stream.total_in == len) ? 0 : -1;
+}
+
+static void copy_pack_data(struct sha1file *f,
+		struct packed_git *p,
+		struct pack_window **w_curs,
+		off_t offset,
+		off_t len)
+{
+	unsigned char *in;
+	unsigned long avail;
+
+	while (len) {
+		in = use_pack(p, w_curs, offset, &avail);
+		if (avail > len)
+			avail = (unsigned long)len;
+		sha1write(f, in, avail);
+		offset += avail;
+		len -= avail;
+	}
+}
+
+/* Return 0 if we will bust the pack-size limit */
+static unsigned long write_no_reuse_object(struct sha1file *f, struct object_entry *entry,
+					   unsigned long limit, int usable_delta)
+{
+	unsigned long size, datalen;
+	unsigned char header[10], dheader[10];
+	unsigned hdrlen;
+	enum object_type type;
+	void *buf;
+	struct git_istream *st = NULL;
+
+	if (!usable_delta) {
+		if (entry->type == OBJ_BLOB &&
+		    entry->size > big_file_threshold &&
+		    (st = open_istream(entry->idx.sha1, &type, &size, NULL)) != NULL)
+			buf = NULL;
+		else {
+			buf = read_sha1_file(entry->idx.sha1, &type, &size);
+			if (!buf)
+				die(_("unable to read %s"), sha1_to_hex(entry->idx.sha1));
+		}
+		/*
+		 * make sure no cached delta data remains from a
+		 * previous attempt before a pack split occurred.
+		 */
+		free(entry->delta_data);
+		entry->delta_data = NULL;
+		entry->z_delta_size = 0;
+	} else if (entry->delta_data) {
+		size = entry->delta_size;
+		buf = entry->delta_data;
+		entry->delta_data = NULL;
+		type = (allow_ofs_delta && entry->delta->idx.offset) ?
+			OBJ_OFS_DELTA : OBJ_REF_DELTA;
+	} else {
+		buf = get_delta(entry);
+		size = entry->delta_size;
+		type = (allow_ofs_delta && entry->delta->idx.offset) ?
+			OBJ_OFS_DELTA : OBJ_REF_DELTA;
+	}
+
+	if (st)	/* large blob case, just assume we don't compress well */
+		datalen = size;
+	else if (entry->z_delta_size)
+		datalen = entry->z_delta_size;
+	else
+		datalen = do_compress(&buf, size);
+
+	/*
+	 * The object header is a byte of 'type' followed by zero or
+	 * more bytes of length.
+	 */
+	hdrlen = encode_in_pack_object_header(type, size, header);
+
+	if (type == OBJ_OFS_DELTA) {
+		/*
+		 * Deltas with relative base contain an additional
+		 * encoding of the relative offset for the delta
+		 * base from this object's position in the pack.
+		 */
+		off_t ofs = entry->idx.offset - entry->delta->idx.offset;
+		unsigned pos = sizeof(dheader) - 1;
+		dheader[pos] = ofs & 127;
+		while (ofs >>= 7)
+			dheader[--pos] = 128 | (--ofs & 127);
+		if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit) {
+			if (st)
+				close_istream(st);
+			free(buf);
+			return 0;
+		}
+		sha1write(f, header, hdrlen);
+		sha1write(f, dheader + pos, sizeof(dheader) - pos);
+		hdrlen += sizeof(dheader) - pos;
+	} else if (type == OBJ_REF_DELTA) {
+		/*
+		 * Deltas with a base reference contain
+		 * an additional 20 bytes for the base sha1.
+		 */
+		if (limit && hdrlen + 20 + datalen + 20 >= limit) {
+			if (st)
+				close_istream(st);
+			free(buf);
+			return 0;
+		}
+		sha1write(f, header, hdrlen);
+		sha1write(f, entry->delta->idx.sha1, 20);
+		hdrlen += 20;
+	} else {
+		if (limit && hdrlen + datalen + 20 >= limit) {
+			if (st)
+				close_istream(st);
+			free(buf);
+			return 0;
+		}
+		sha1write(f, header, hdrlen);
+	}
+	if (st) {
+		datalen = write_large_blob_data(st, f, entry->idx.sha1);
+		close_istream(st);
+	} else {
+		sha1write(f, buf, datalen);
+		free(buf);
+	}
+
+	return hdrlen + datalen;
+}
+
+/* Return 0 if we will bust the pack-size limit */
+static unsigned long write_reuse_object(struct sha1file *f, struct object_entry *entry,
+					unsigned long limit, int usable_delta)
+{
+	struct packed_git *p = entry->in_pack;
+	struct pack_window *w_curs = NULL;
+	struct revindex_entry *revidx;
+	off_t offset;
+	enum object_type type = entry->type;
+	unsigned long datalen;
+	unsigned char header[10], dheader[10];
+	unsigned hdrlen;
+
+	if (entry->delta)
+		type = (allow_ofs_delta && entry->delta->idx.offset) ?
+			OBJ_OFS_DELTA : OBJ_REF_DELTA;
+	hdrlen = encode_in_pack_object_header(type, entry->size, header);
+
+	offset = entry->in_pack_offset;
+	revidx = find_pack_revindex(p, offset);
+	datalen = revidx[1].offset - offset;
+	if (!pack_to_stdout && p->index_version > 1 &&
+	    check_pack_crc(p, &w_curs, offset, datalen, revidx->nr)) {
+		error("bad packed object CRC for %s", sha1_to_hex(entry->idx.sha1));
+		unuse_pack(&w_curs);
+		return write_no_reuse_object(f, entry, limit, usable_delta);
+	}
+
+	offset += entry->in_pack_header_size;
+	datalen -= entry->in_pack_header_size;
+
+	if (!pack_to_stdout && p->index_version == 1 &&
+	    check_pack_inflate(p, &w_curs, offset, datalen, entry->size)) {
+		error("corrupt packed object for %s", sha1_to_hex(entry->idx.sha1));
+		unuse_pack(&w_curs);
+		return write_no_reuse_object(f, entry, limit, usable_delta);
+	}
+
+	if (type == OBJ_OFS_DELTA) {
+		off_t ofs = entry->idx.offset - entry->delta->idx.offset;
+		unsigned pos = sizeof(dheader) - 1;
+		dheader[pos] = ofs & 127;
+		while (ofs >>= 7)
+			dheader[--pos] = 128 | (--ofs & 127);
+		if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit) {
+			unuse_pack(&w_curs);
+			return 0;
+		}
+		sha1write(f, header, hdrlen);
+		sha1write(f, dheader + pos, sizeof(dheader) - pos);
+		hdrlen += sizeof(dheader) - pos;
+		reused_delta++;
+	} else if (type == OBJ_REF_DELTA) {
+		if (limit && hdrlen + 20 + datalen + 20 >= limit) {
+			unuse_pack(&w_curs);
+			return 0;
+		}
+		sha1write(f, header, hdrlen);
+		sha1write(f, entry->delta->idx.sha1, 20);
+		hdrlen += 20;
+		reused_delta++;
+	} else {
+		if (limit && hdrlen + datalen + 20 >= limit) {
+			unuse_pack(&w_curs);
+			return 0;
+		}
+		sha1write(f, header, hdrlen);
+	}
+	copy_pack_data(f, p, &w_curs, offset, datalen);
+	unuse_pack(&w_curs);
+	reused++;
+	return hdrlen + datalen;
+}
+
+/* Return 0 if we will bust the pack-size limit */
+static unsigned long write_object(struct sha1file *f,
+				  struct object_entry *entry,
+				  off_t write_offset)
+{
+	unsigned long limit, len;
+	int usable_delta, to_reuse;
+
+	if (!pack_to_stdout)
+		crc32_begin(f);
+
+	/* apply size limit if limited packsize and not first object */
+	if (!pack_size_limit || !nr_written)
+		limit = 0;
+	else if (pack_size_limit <= write_offset)
+		/*
+		 * the earlier object did not fit the limit; avoid
+		 * mistaking this with unlimited (i.e. limit = 0).
+		 */
+		limit = 1;
+	else
+		limit = pack_size_limit - write_offset;
+
+	if (!entry->delta)
+		usable_delta = 0;	/* no delta */
+	else if (!pack_size_limit)
+	       usable_delta = 1;	/* unlimited packfile */
+	else if (entry->delta->idx.offset == (off_t)-1)
+		usable_delta = 0;	/* base was written to another pack */
+	else if (entry->delta->idx.offset)
+		usable_delta = 1;	/* base already exists in this pack */
+	else
+		usable_delta = 0;	/* base could end up in another pack */
+
+	if (!reuse_object)
+		to_reuse = 0;	/* explicit */
+	else if (!entry->in_pack)
+		to_reuse = 0;	/* can't reuse what we don't have */
+	else if (entry->type == OBJ_REF_DELTA || entry->type == OBJ_OFS_DELTA)
+				/* check_object() decided it for us ... */
+		to_reuse = usable_delta;
+				/* ... but pack split may override that */
+	else if (entry->type != entry->in_pack_type)
+		to_reuse = 0;	/* pack has delta which is unusable */
+	else if (entry->delta)
+		to_reuse = 0;	/* we want to pack afresh */
+	else
+		to_reuse = 1;	/* we have it in-pack undeltified,
+				 * and we do not need to deltify it.
+				 */
+
+	if (!to_reuse)
+		len = write_no_reuse_object(f, entry, limit, usable_delta);
+	else
+		len = write_reuse_object(f, entry, limit, usable_delta);
+	if (!len)
+		return 0;
+
+	if (usable_delta)
+		written_delta++;
+	written++;
+	if (!pack_to_stdout)
+		entry->idx.crc32 = crc32_end(f);
+	return len;
+}
+
+enum write_one_status {
+	WRITE_ONE_SKIP = -1, /* already written */
+	WRITE_ONE_BREAK = 0, /* writing this will bust the limit; not written */
+	WRITE_ONE_WRITTEN = 1, /* normal */
+	WRITE_ONE_RECURSIVE = 2 /* already scheduled to be written */
+};
+
+static enum write_one_status write_one(struct sha1file *f,
+				       struct object_entry *e,
+				       off_t *offset)
+{
+	unsigned long size;
+	int recursing;
+
+	/*
+	 * we set offset to 1 (which is an impossible value) to mark
+	 * the fact that this object is involved in "write its base
+	 * first before writing a deltified object" recursion.
+	 */
+	recursing = (e->idx.offset == 1);
+	if (recursing) {
+		warning("recursive delta detected for object %s",
+			sha1_to_hex(e->idx.sha1));
+		return WRITE_ONE_RECURSIVE;
+	} else if (e->idx.offset || e->preferred_base) {
+		/* offset is non zero if object is written already. */
+		return WRITE_ONE_SKIP;
+	}
+
+	/* if we are deltified, write out base object first. */
+	if (e->delta) {
+		e->idx.offset = 1; /* now recurse */
+		switch (write_one(f, e->delta, offset)) {
+		case WRITE_ONE_RECURSIVE:
+			/* we cannot depend on this one */
+			e->delta = NULL;
+			break;
+		default:
+			break;
+		case WRITE_ONE_BREAK:
+			e->idx.offset = recursing;
+			return WRITE_ONE_BREAK;
+		}
+	}
+
+	e->idx.offset = *offset;
+	size = write_object(f, e, *offset);
+	if (!size) {
+		e->idx.offset = recursing;
+		return WRITE_ONE_BREAK;
+	}
+	written_list[nr_written++] = &e->idx;
+
+	/* make sure off_t is sufficiently large not to wrap */
+	if (signed_add_overflows(*offset, size))
+		die("pack too large for current definition of off_t");
+	*offset += size;
+	return WRITE_ONE_WRITTEN;
+}
+
+static int mark_tagged(const char *path, const unsigned char *sha1, int flag,
+		       void *cb_data)
+{
+	unsigned char peeled[20];
+	struct object_entry *entry = locate_object_entry(sha1);
+
+	if (entry)
+		entry->tagged = 1;
+	if (!peel_ref(path, peeled)) {
+		entry = locate_object_entry(peeled);
+		if (entry)
+			entry->tagged = 1;
+	}
+	return 0;
+}
+
+static inline void add_to_write_order(struct object_entry **wo,
+			       unsigned int *endp,
+			       struct object_entry *e)
+{
+	if (e->filled)
+		return;
+	wo[(*endp)++] = e;
+	e->filled = 1;
+}
+
+static void add_descendants_to_write_order(struct object_entry **wo,
+					   unsigned int *endp,
+					   struct object_entry *e)
+{
+	int add_to_order = 1;
+	while (e) {
+		if (add_to_order) {
+			struct object_entry *s;
+			/* add this node... */
+			add_to_write_order(wo, endp, e);
+			/* all its siblings... */
+			for (s = e->delta_sibling; s; s = s->delta_sibling) {
+				add_to_write_order(wo, endp, s);
+			}
+		}
+		/* drop down a level to add left subtree nodes if possible */
+		if (e->delta_child) {
+			add_to_order = 1;
+			e = e->delta_child;
+		} else {
+			add_to_order = 0;
+			/* our sibling might have some children, it is next */
+			if (e->delta_sibling) {
+				e = e->delta_sibling;
+				continue;
+			}
+			/* go back to our parent node */
+			e = e->delta;
+			while (e && !e->delta_sibling) {
+				/* we're on the right side of a subtree, keep
+				 * going up until we can go right again */
+				e = e->delta;
+			}
+			if (!e) {
+				/* done- we hit our original root node */
+				return;
+			}
+			/* pass it off to sibling at this level */
+			e = e->delta_sibling;
+		}
+	};
+}
+
+static void add_family_to_write_order(struct object_entry **wo,
+				      unsigned int *endp,
+				      struct object_entry *e)
+{
+	struct object_entry *root;
+
+	for (root = e; root->delta; root = root->delta)
+		; /* nothing */
+	add_descendants_to_write_order(wo, endp, root);
+}
+
+static struct object_entry **compute_write_order(void)
+{
+	unsigned int i, wo_end, last_untagged;
+
+	struct object_entry **wo = xmalloc(nr_objects * sizeof(*wo));
+
+	for (i = 0; i < nr_objects; i++) {
+		objects[i].tagged = 0;
+		objects[i].filled = 0;
+		objects[i].delta_child = NULL;
+		objects[i].delta_sibling = NULL;
+	}
+
+	/*
+	 * Fully connect delta_child/delta_sibling network.
+	 * Make sure delta_sibling is sorted in the original
+	 * recency order.
+	 */
+	for (i = nr_objects; i > 0;) {
+		struct object_entry *e = &objects[--i];
+		if (!e->delta)
+			continue;
+		/* Mark me as the first child */
+		e->delta_sibling = e->delta->delta_child;
+		e->delta->delta_child = e;
+	}
+
+	/*
+	 * Mark objects that are at the tip of tags.
+	 */
+	for_each_tag_ref(mark_tagged, NULL);
+
+	/*
+	 * Give the objects in the original recency order until
+	 * we see a tagged tip.
+	 */
+	for (i = wo_end = 0; i < nr_objects; i++) {
+		if (objects[i].tagged)
+			break;
+		add_to_write_order(wo, &wo_end, &objects[i]);
+	}
+	last_untagged = i;
+
+	/*
+	 * Then fill all the tagged tips.
+	 */
+	for (; i < nr_objects; i++) {
+		if (objects[i].tagged)
+			add_to_write_order(wo, &wo_end, &objects[i]);
+	}
+
+	/*
+	 * And then all remaining commits and tags.
+	 */
+	for (i = last_untagged; i < nr_objects; i++) {
+		if (objects[i].type != OBJ_COMMIT &&
+		    objects[i].type != OBJ_TAG)
+			continue;
+		add_to_write_order(wo, &wo_end, &objects[i]);
+	}
+
+	/*
+	 * And then all the trees.
+	 */
+	for (i = last_untagged; i < nr_objects; i++) {
+		if (objects[i].type != OBJ_TREE)
+			continue;
+		add_to_write_order(wo, &wo_end, &objects[i]);
+	}
+
+	/*
+	 * Finally all the rest in really tight order
+	 */
+	for (i = last_untagged; i < nr_objects; i++) {
+		if (!objects[i].filled)
+			add_family_to_write_order(wo, &wo_end, &objects[i]);
+	}
+
+	if (wo_end != nr_objects)
+		die("ordered %u objects, expected %"PRIu32, wo_end, nr_objects);
+
+	return wo;
+}
+
+static void write_pack_file(void)
+{
+	uint32_t i = 0, j;
+	struct sha1file *f;
+	off_t offset;
+	uint32_t nr_remaining = nr_result;
+	time_t last_mtime = 0;
+	struct object_entry **write_order;
+
+	if (progress > pack_to_stdout)
+		progress_state = start_progress("Writing objects", nr_result);
+	written_list = xmalloc(nr_objects * sizeof(*written_list));
+	write_order = compute_write_order();
+
+	do {
+		unsigned char sha1[20];
+		char *pack_tmp_name = NULL;
+
+		if (pack_to_stdout)
+			f = sha1fd_throughput(1, "<stdout>", progress_state);
+		else
+			f = create_tmp_packfile(&pack_tmp_name);
+
+		offset = write_pack_header(f, nr_remaining);
+		if (!offset)
+			die_errno("unable to write pack header");
+		nr_written = 0;
+		for (; i < nr_objects; i++) {
+			struct object_entry *e = write_order[i];
+			if (write_one(f, e, &offset) == WRITE_ONE_BREAK)
+				break;
+			display_progress(progress_state, written);
+		}
+
+		/*
+		 * Did we write the wrong # entries in the header?
+		 * If so, rewrite it like in fast-import
+		 */
+		if (pack_to_stdout) {
+			sha1close(f, sha1, CSUM_CLOSE);
+		} else if (nr_written == nr_remaining) {
+			sha1close(f, sha1, CSUM_FSYNC);
+		} else {
+			int fd = sha1close(f, sha1, 0);
+			fixup_pack_header_footer(fd, sha1, pack_tmp_name,
+						 nr_written, sha1, offset);
+			close(fd);
+		}
+
+		if (!pack_to_stdout) {
+			struct stat st;
+			char tmpname[PATH_MAX];
+
+			/*
+			 * Packs are runtime accessed in their mtime
+			 * order since newer packs are more likely to contain
+			 * younger objects.  So if we are creating multiple
+			 * packs then we should modify the mtime of later ones
+			 * to preserve this property.
+			 */
+			if (stat(pack_tmp_name, &st) < 0) {
+				warning("failed to stat %s: %s",
+					pack_tmp_name, strerror(errno));
+			} else if (!last_mtime) {
+				last_mtime = st.st_mtime;
+			} else {
+				struct utimbuf utb;
+				utb.actime = st.st_atime;
+				utb.modtime = --last_mtime;
+				if (utime(pack_tmp_name, &utb) < 0)
+					warning("failed utime() on %s: %s",
+						tmpname, strerror(errno));
+			}
+
+			/* Enough space for "-<sha-1>.pack"? */
+			if (sizeof(tmpname) <= strlen(base_name) + 50)
+				die("pack base name '%s' too long", base_name);
+			snprintf(tmpname, sizeof(tmpname), "%s-", base_name);
+			finish_tmp_packfile(tmpname, pack_tmp_name,
+					    written_list, nr_written,
+					    &pack_idx_opts, sha1);
+			free(pack_tmp_name);
+			puts(sha1_to_hex(sha1));
+		}
+
+		/* mark written objects as written to previous pack */
+		for (j = 0; j < nr_written; j++) {
+			written_list[j]->offset = (off_t)-1;
+		}
+		nr_remaining -= nr_written;
+	} while (nr_remaining && i < nr_objects);
+
+	free(written_list);
+	free(write_order);
+	stop_progress(&progress_state);
+	if (written != nr_result)
+		die("wrote %"PRIu32" objects while expecting %"PRIu32,
+			written, nr_result);
+}
+
+static int locate_object_entry_hash(const unsigned char *sha1)
+{
+	int i;
+	unsigned int ui;
+	memcpy(&ui, sha1, sizeof(unsigned int));
+	i = ui % object_ix_hashsz;
+	while (0 < object_ix[i]) {
+		if (!hashcmp(sha1, objects[object_ix[i] - 1].idx.sha1))
+			return i;
+		if (++i == object_ix_hashsz)
+			i = 0;
+	}
+	return -1 - i;
+}
+
+static struct object_entry *locate_object_entry(const unsigned char *sha1)
+{
+	int i;
+
+	if (!object_ix_hashsz)
+		return NULL;
+
+	i = locate_object_entry_hash(sha1);
+	if (0 <= i)
+		return &objects[object_ix[i]-1];
+	return NULL;
+}
+
+static void rehash_objects(void)
+{
+	uint32_t i;
+	struct object_entry *oe;
+
+	object_ix_hashsz = nr_objects * 3;
+	if (object_ix_hashsz < 1024)
+		object_ix_hashsz = 1024;
+	object_ix = xrealloc(object_ix, sizeof(int) * object_ix_hashsz);
+	memset(object_ix, 0, sizeof(int) * object_ix_hashsz);
+	for (i = 0, oe = objects; i < nr_objects; i++, oe++) {
+		int ix = locate_object_entry_hash(oe->idx.sha1);
+		if (0 <= ix)
+			continue;
+		ix = -1 - ix;
+		object_ix[ix] = i + 1;
+	}
+}
+
+static unsigned name_hash(const char *name)
+{
+	unsigned c, hash = 0;
+
+	if (!name)
+		return 0;
+
+	/*
+	 * This effectively just creates a sortable number from the
+	 * last sixteen non-whitespace characters. Last characters
+	 * count "most", so things that end in ".c" sort together.
+	 */
+	while ((c = *name++) != 0) {
+		if (isspace(c))
+			continue;
+		hash = (hash >> 2) + (c << 24);
+	}
+	return hash;
+}
+
+static void setup_delta_attr_check(struct git_attr_check *check)
+{
+	static struct git_attr *attr_delta;
+
+	if (!attr_delta)
+		attr_delta = git_attr("delta");
+
+	check[0].attr = attr_delta;
+}
+
+static int no_try_delta(const char *path)
+{
+	struct git_attr_check check[1];
+
+	setup_delta_attr_check(check);
+	if (git_check_attr(path, ARRAY_SIZE(check), check))
+		return 0;
+	if (ATTR_FALSE(check->value))
+		return 1;
+	return 0;
+}
+
+static int add_object_entry(const unsigned char *sha1, enum object_type type,
+			    const char *name, int exclude)
+{
+	struct object_entry *entry;
+	struct packed_git *p, *found_pack = NULL;
+	off_t found_offset = 0;
+	int ix;
+	unsigned hash = name_hash(name);
+
+	ix = nr_objects ? locate_object_entry_hash(sha1) : -1;
+	if (ix >= 0) {
+		if (exclude) {
+			entry = objects + object_ix[ix] - 1;
+			if (!entry->preferred_base)
+				nr_result--;
+			entry->preferred_base = 1;
+		}
+		return 0;
+	}
+
+	if (!exclude && local && has_loose_object_nonlocal(sha1))
+		return 0;
+
+	for (p = packed_git; p; p = p->next) {
+		off_t offset = find_pack_entry_one(sha1, p);
+		if (offset) {
+			if (!found_pack) {
+				if (!is_pack_valid(p)) {
+					warning("packfile %s cannot be accessed", p->pack_name);
+					continue;
+				}
+				found_offset = offset;
+				found_pack = p;
+			}
+			if (exclude)
+				break;
+			if (incremental)
+				return 0;
+			if (local && !p->pack_local)
+				return 0;
+			if (ignore_packed_keep && p->pack_local && p->pack_keep)
+				return 0;
+		}
+	}
+
+	if (nr_objects >= nr_alloc) {
+		nr_alloc = (nr_alloc  + 1024) * 3 / 2;
+		objects = xrealloc(objects, nr_alloc * sizeof(*entry));
+	}
+
+	entry = objects + nr_objects++;
+	memset(entry, 0, sizeof(*entry));
+	hashcpy(entry->idx.sha1, sha1);
+	entry->hash = hash;
+	if (type)
+		entry->type = type;
+	if (exclude)
+		entry->preferred_base = 1;
+	else
+		nr_result++;
+	if (found_pack) {
+		entry->in_pack = found_pack;
+		entry->in_pack_offset = found_offset;
+	}
+
+	if (object_ix_hashsz * 3 <= nr_objects * 4)
+		rehash_objects();
+	else
+		object_ix[-1 - ix] = nr_objects;
+
+	display_progress(progress_state, nr_objects);
+
+	if (name && no_try_delta(name))
+		entry->no_try_delta = 1;
+
+	return 1;
+}
+
+struct pbase_tree_cache {
+	unsigned char sha1[20];
+	int ref;
+	int temporary;
+	void *tree_data;
+	unsigned long tree_size;
+};
+
+static struct pbase_tree_cache *(pbase_tree_cache[256]);
+static int pbase_tree_cache_ix(const unsigned char *sha1)
+{
+	return sha1[0] % ARRAY_SIZE(pbase_tree_cache);
+}
+static int pbase_tree_cache_ix_incr(int ix)
+{
+	return (ix+1) % ARRAY_SIZE(pbase_tree_cache);
+}
+
+static struct pbase_tree {
+	struct pbase_tree *next;
+	/* This is a phony "cache" entry; we are not
+	 * going to evict it nor find it through _get()
+	 * mechanism -- this is for the toplevel node that
+	 * would almost always change with any commit.
+	 */
+	struct pbase_tree_cache pcache;
+} *pbase_tree;
+
+static struct pbase_tree_cache *pbase_tree_get(const unsigned char *sha1)
+{
+	struct pbase_tree_cache *ent, *nent;
+	void *data;
+	unsigned long size;
+	enum object_type type;
+	int neigh;
+	int my_ix = pbase_tree_cache_ix(sha1);
+	int available_ix = -1;
+
+	/* pbase-tree-cache acts as a limited hashtable.
+	 * your object will be found at your index or within a few
+	 * slots after that slot if it is cached.
+	 */
+	for (neigh = 0; neigh < 8; neigh++) {
+		ent = pbase_tree_cache[my_ix];
+		if (ent && !hashcmp(ent->sha1, sha1)) {
+			ent->ref++;
+			return ent;
+		}
+		else if (((available_ix < 0) && (!ent || !ent->ref)) ||
+			 ((0 <= available_ix) &&
+			  (!ent && pbase_tree_cache[available_ix])))
+			available_ix = my_ix;
+		if (!ent)
+			break;
+		my_ix = pbase_tree_cache_ix_incr(my_ix);
+	}
+
+	/* Did not find one.  Either we got a bogus request or
+	 * we need to read and perhaps cache.
+	 */
+	data = read_sha1_file(sha1, &type, &size);
+	if (!data)
+		return NULL;
+	if (type != OBJ_TREE) {
+		free(data);
+		return NULL;
+	}
+
+	/* We need to either cache or return a throwaway copy */
+
+	if (available_ix < 0)
+		ent = NULL;
+	else {
+		ent = pbase_tree_cache[available_ix];
+		my_ix = available_ix;
+	}
+
+	if (!ent) {
+		nent = xmalloc(sizeof(*nent));
+		nent->temporary = (available_ix < 0);
+	}
+	else {
+		/* evict and reuse */
+		free(ent->tree_data);
+		nent = ent;
+	}
+	hashcpy(nent->sha1, sha1);
+	nent->tree_data = data;
+	nent->tree_size = size;
+	nent->ref = 1;
+	if (!nent->temporary)
+		pbase_tree_cache[my_ix] = nent;
+	return nent;
+}
+
+static void pbase_tree_put(struct pbase_tree_cache *cache)
+{
+	if (!cache->temporary) {
+		cache->ref--;
+		return;
+	}
+	free(cache->tree_data);
+	free(cache);
+}
+
+static int name_cmp_len(const char *name)
+{
+	int i;
+	for (i = 0; name[i] && name[i] != '\n' && name[i] != '/'; i++)
+		;
+	return i;
+}
+
+static void add_pbase_object(struct tree_desc *tree,
+			     const char *name,
+			     int cmplen,
+			     const char *fullname)
+{
+	struct name_entry entry;
+	int cmp;
+
+	while (tree_entry(tree,&entry)) {
+		if (S_ISGITLINK(entry.mode))
+			continue;
+		cmp = tree_entry_len(&entry) != cmplen ? 1 :
+		      memcmp(name, entry.path, cmplen);
+		if (cmp > 0)
+			continue;
+		if (cmp < 0)
+			return;
+		if (name[cmplen] != '/') {
+			add_object_entry(entry.sha1,
+					 object_type(entry.mode),
+					 fullname, 1);
+			return;
+		}
+		if (S_ISDIR(entry.mode)) {
+			struct tree_desc sub;
+			struct pbase_tree_cache *tree;
+			const char *down = name+cmplen+1;
+			int downlen = name_cmp_len(down);
+
+			tree = pbase_tree_get(entry.sha1);
+			if (!tree)
+				return;
+			init_tree_desc(&sub, tree->tree_data, tree->tree_size);
+
+			add_pbase_object(&sub, down, downlen, fullname);
+			pbase_tree_put(tree);
+		}
+	}
+}
+
+static unsigned *done_pbase_paths;
+static int done_pbase_paths_num;
+static int done_pbase_paths_alloc;
+static int done_pbase_path_pos(unsigned hash)
+{
+	int lo = 0;
+	int hi = done_pbase_paths_num;
+	while (lo < hi) {
+		int mi = (hi + lo) / 2;
+		if (done_pbase_paths[mi] == hash)
+			return mi;
+		if (done_pbase_paths[mi] < hash)
+			hi = mi;
+		else
+			lo = mi + 1;
+	}
+	return -lo-1;
+}
+
+static int check_pbase_path(unsigned hash)
+{
+	int pos = (!done_pbase_paths) ? -1 : done_pbase_path_pos(hash);
+	if (0 <= pos)
+		return 1;
+	pos = -pos - 1;
+	if (done_pbase_paths_alloc <= done_pbase_paths_num) {
+		done_pbase_paths_alloc = alloc_nr(done_pbase_paths_alloc);
+		done_pbase_paths = xrealloc(done_pbase_paths,
+					    done_pbase_paths_alloc *
+					    sizeof(unsigned));
+	}
+	done_pbase_paths_num++;
+	if (pos < done_pbase_paths_num)
+		memmove(done_pbase_paths + pos + 1,
+			done_pbase_paths + pos,
+			(done_pbase_paths_num - pos - 1) * sizeof(unsigned));
+	done_pbase_paths[pos] = hash;
+	return 0;
+}
+
+static void add_preferred_base_object(const char *name)
+{
+	struct pbase_tree *it;
+	int cmplen;
+	unsigned hash = name_hash(name);
+
+	if (!num_preferred_base || check_pbase_path(hash))
+		return;
+
+	cmplen = name_cmp_len(name);
+	for (it = pbase_tree; it; it = it->next) {
+		if (cmplen == 0) {
+			add_object_entry(it->pcache.sha1, OBJ_TREE, NULL, 1);
+		}
+		else {
+			struct tree_desc tree;
+			init_tree_desc(&tree, it->pcache.tree_data, it->pcache.tree_size);
+			add_pbase_object(&tree, name, cmplen, name);
+		}
+	}
+}
+
+static void add_preferred_base(unsigned char *sha1)
+{
+	struct pbase_tree *it;
+	void *data;
+	unsigned long size;
+	unsigned char tree_sha1[20];
+
+	if (window <= num_preferred_base++)
+		return;
+
+	data = read_object_with_reference(sha1, tree_type, &size, tree_sha1);
+	if (!data)
+		return;
+
+	for (it = pbase_tree; it; it = it->next) {
+		if (!hashcmp(it->pcache.sha1, tree_sha1)) {
+			free(data);
+			return;
+		}
+	}
+
+	it = xcalloc(1, sizeof(*it));
+	it->next = pbase_tree;
+	pbase_tree = it;
+
+	hashcpy(it->pcache.sha1, tree_sha1);
+	it->pcache.tree_data = data;
+	it->pcache.tree_size = size;
+}
+
+static void cleanup_preferred_base(void)
+{
+	struct pbase_tree *it;
+	unsigned i;
+
+	it = pbase_tree;
+	pbase_tree = NULL;
+	while (it) {
+		struct pbase_tree *this = it;
+		it = this->next;
+		free(this->pcache.tree_data);
+		free(this);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(pbase_tree_cache); i++) {
+		if (!pbase_tree_cache[i])
+			continue;
+		free(pbase_tree_cache[i]->tree_data);
+		free(pbase_tree_cache[i]);
+		pbase_tree_cache[i] = NULL;
+	}
+
+	free(done_pbase_paths);
+	done_pbase_paths = NULL;
+	done_pbase_paths_num = done_pbase_paths_alloc = 0;
+}
+
+static void check_object(struct object_entry *entry)
+{
+	if (entry->in_pack) {
+		struct packed_git *p = entry->in_pack;
+		struct pack_window *w_curs = NULL;
+		const unsigned char *base_ref = NULL;
+		struct object_entry *base_entry;
+		unsigned long used, used_0;
+		unsigned long avail;
+		off_t ofs;
+		unsigned char *buf, c;
+
+		buf = use_pack(p, &w_curs, entry->in_pack_offset, &avail);
+
+		/*
+		 * We want in_pack_type even if we do not reuse delta
+		 * since non-delta representations could still be reused.
+		 */
+		used = unpack_object_header_buffer(buf, avail,
+						   &entry->in_pack_type,
+						   &entry->size);
+		if (used == 0)
+			goto give_up;
+
+		/*
+		 * Determine if this is a delta and if so whether we can
+		 * reuse it or not.  Otherwise let's find out as cheaply as
+		 * possible what the actual type and size for this object is.
+		 */
+		switch (entry->in_pack_type) {
+		default:
+			/* Not a delta hence we've already got all we need. */
+			entry->type = entry->in_pack_type;
+			entry->in_pack_header_size = used;
+			if (entry->type < OBJ_COMMIT || entry->type > OBJ_BLOB)
+				goto give_up;
+			unuse_pack(&w_curs);
+			return;
+		case OBJ_REF_DELTA:
+			if (reuse_delta && !entry->preferred_base)
+				base_ref = use_pack(p, &w_curs,
+						entry->in_pack_offset + used, NULL);
+			entry->in_pack_header_size = used + 20;
+			break;
+		case OBJ_OFS_DELTA:
+			buf = use_pack(p, &w_curs,
+				       entry->in_pack_offset + used, NULL);
+			used_0 = 0;
+			c = buf[used_0++];
+			ofs = c & 127;
+			while (c & 128) {
+				ofs += 1;
+				if (!ofs || MSB(ofs, 7)) {
+					error("delta base offset overflow in pack for %s",
+					      sha1_to_hex(entry->idx.sha1));
+					goto give_up;
+				}
+				c = buf[used_0++];
+				ofs = (ofs << 7) + (c & 127);
+			}
+			ofs = entry->in_pack_offset - ofs;
+			if (ofs <= 0 || ofs >= entry->in_pack_offset) {
+				error("delta base offset out of bound for %s",
+				      sha1_to_hex(entry->idx.sha1));
+				goto give_up;
+			}
+			if (reuse_delta && !entry->preferred_base) {
+				struct revindex_entry *revidx;
+				revidx = find_pack_revindex(p, ofs);
+				if (!revidx)
+					goto give_up;
+				base_ref = nth_packed_object_sha1(p, revidx->nr);
+			}
+			entry->in_pack_header_size = used + used_0;
+			break;
+		}
+
+		if (base_ref && (base_entry = locate_object_entry(base_ref))) {
+			/*
+			 * If base_ref was set above that means we wish to
+			 * reuse delta data, and we even found that base
+			 * in the list of objects we want to pack. Goodie!
+			 *
+			 * Depth value does not matter - find_deltas() will
+			 * never consider reused delta as the base object to
+			 * deltify other objects against, in order to avoid
+			 * circular deltas.
+			 */
+			entry->type = entry->in_pack_type;
+			entry->delta = base_entry;
+			entry->delta_size = entry->size;
+			entry->delta_sibling = base_entry->delta_child;
+			base_entry->delta_child = entry;
+			unuse_pack(&w_curs);
+			return;
+		}
+
+		if (entry->type) {
+			/*
+			 * This must be a delta and we already know what the
+			 * final object type is.  Let's extract the actual
+			 * object size from the delta header.
+			 */
+			entry->size = get_size_from_delta(p, &w_curs,
+					entry->in_pack_offset + entry->in_pack_header_size);
+			if (entry->size == 0)
+				goto give_up;
+			unuse_pack(&w_curs);
+			return;
+		}
+
+		/*
+		 * No choice but to fall back to the recursive delta walk
+		 * with sha1_object_info() to find about the object type
+		 * at this point...
+		 */
+		give_up:
+		unuse_pack(&w_curs);
+	}
+
+	entry->type = sha1_object_info(entry->idx.sha1, &entry->size);
+	/*
+	 * The error condition is checked in prepare_pack().  This is
+	 * to permit a missing preferred base object to be ignored
+	 * as a preferred base.  Doing so can result in a larger
+	 * pack file, but the transfer will still take place.
+	 */
+}
+
+static int pack_offset_sort(const void *_a, const void *_b)
+{
+	const struct object_entry *a = *(struct object_entry **)_a;
+	const struct object_entry *b = *(struct object_entry **)_b;
+
+	/* avoid filesystem trashing with loose objects */
+	if (!a->in_pack && !b->in_pack)
+		return hashcmp(a->idx.sha1, b->idx.sha1);
+
+	if (a->in_pack < b->in_pack)
+		return -1;
+	if (a->in_pack > b->in_pack)
+		return 1;
+	return a->in_pack_offset < b->in_pack_offset ? -1 :
+			(a->in_pack_offset > b->in_pack_offset);
+}
+
+static void get_object_details(void)
+{
+	uint32_t i;
+	struct object_entry **sorted_by_offset;
+
+	sorted_by_offset = xcalloc(nr_objects, sizeof(struct object_entry *));
+	for (i = 0; i < nr_objects; i++)
+		sorted_by_offset[i] = objects + i;
+	qsort(sorted_by_offset, nr_objects, sizeof(*sorted_by_offset), pack_offset_sort);
+
+	for (i = 0; i < nr_objects; i++) {
+		struct object_entry *entry = sorted_by_offset[i];
+		check_object(entry);
+		if (big_file_threshold < entry->size)
+			entry->no_try_delta = 1;
+	}
+
+	free(sorted_by_offset);
+}
+
+/*
+ * We search for deltas in a list sorted by type, by filename hash, and then
+ * by size, so that we see progressively smaller and smaller files.
+ * That's because we prefer deltas to be from the bigger file
+ * to the smaller -- deletes are potentially cheaper, but perhaps
+ * more importantly, the bigger file is likely the more recent
+ * one.  The deepest deltas are therefore the oldest objects which are
+ * less susceptible to be accessed often.
+ */
+static int type_size_sort(const void *_a, const void *_b)
+{
+	const struct object_entry *a = *(struct object_entry **)_a;
+	const struct object_entry *b = *(struct object_entry **)_b;
+
+	if (a->type > b->type)
+		return -1;
+	if (a->type < b->type)
+		return 1;
+	if (a->hash > b->hash)
+		return -1;
+	if (a->hash < b->hash)
+		return 1;
+	if (a->preferred_base > b->preferred_base)
+		return -1;
+	if (a->preferred_base < b->preferred_base)
+		return 1;
+	if (a->size > b->size)
+		return -1;
+	if (a->size < b->size)
+		return 1;
+	return a < b ? -1 : (a > b);  /* newest first */
+}
+
+struct unpacked {
+	struct object_entry *entry;
+	void *data;
+	struct delta_index *index;
+	unsigned depth;
+};
+
+static int delta_cacheable(unsigned long src_size, unsigned long trg_size,
+			   unsigned long delta_size)
+{
+	if (max_delta_cache_size && delta_cache_size + delta_size > max_delta_cache_size)
+		return 0;
+
+	if (delta_size < cache_max_small_delta_size)
+		return 1;
+
+	/* cache delta, if objects are large enough compared to delta size */
+	if ((src_size >> 20) + (trg_size >> 21) > (delta_size >> 10))
+		return 1;
+
+	return 0;
+}
+
+#ifndef NO_PTHREADS
+
+static pthread_mutex_t read_mutex;
+#define read_lock()		pthread_mutex_lock(&read_mutex)
+#define read_unlock()		pthread_mutex_unlock(&read_mutex)
+
+static pthread_mutex_t cache_mutex;
+#define cache_lock()		pthread_mutex_lock(&cache_mutex)
+#define cache_unlock()		pthread_mutex_unlock(&cache_mutex)
+
+static pthread_mutex_t progress_mutex;
+#define progress_lock()		pthread_mutex_lock(&progress_mutex)
+#define progress_unlock()	pthread_mutex_unlock(&progress_mutex)
+
+#else
+
+#define read_lock()		(void)0
+#define read_unlock()		(void)0
+#define cache_lock()		(void)0
+#define cache_unlock()		(void)0
+#define progress_lock()		(void)0
+#define progress_unlock()	(void)0
+
+#endif
+
+static int try_delta(struct unpacked *trg, struct unpacked *src,
+		     unsigned max_depth, unsigned long *mem_usage)
+{
+	struct object_entry *trg_entry = trg->entry;
+	struct object_entry *src_entry = src->entry;
+	unsigned long trg_size, src_size, delta_size, sizediff, max_size, sz;
+	unsigned ref_depth;
+	enum object_type type;
+	void *delta_buf;
+
+	/* Don't bother doing diffs between different types */
+	if (trg_entry->type != src_entry->type)
+		return -1;
+
+	/*
+	 * We do not bother to try a delta that we discarded on an
+	 * earlier try, but only when reusing delta data.  Note that
+	 * src_entry that is marked as the preferred_base should always
+	 * be considered, as even if we produce a suboptimal delta against
+	 * it, we will still save the transfer cost, as we already know
+	 * the other side has it and we won't send src_entry at all.
+	 */
+	if (reuse_delta && trg_entry->in_pack &&
+	    trg_entry->in_pack == src_entry->in_pack &&
+	    !src_entry->preferred_base &&
+	    trg_entry->in_pack_type != OBJ_REF_DELTA &&
+	    trg_entry->in_pack_type != OBJ_OFS_DELTA)
+		return 0;
+
+	/* Let's not bust the allowed depth. */
+	if (src->depth >= max_depth)
+		return 0;
+
+	/* Now some size filtering heuristics. */
+	trg_size = trg_entry->size;
+	if (!trg_entry->delta) {
+		max_size = trg_size/2 - 20;
+		ref_depth = 1;
+	} else {
+		max_size = trg_entry->delta_size;
+		ref_depth = trg->depth;
+	}
+	max_size = (uint64_t)max_size * (max_depth - src->depth) /
+						(max_depth - ref_depth + 1);
+	if (max_size == 0)
+		return 0;
+	src_size = src_entry->size;
+	sizediff = src_size < trg_size ? trg_size - src_size : 0;
+	if (sizediff >= max_size)
+		return 0;
+	if (trg_size < src_size / 32)
+		return 0;
+
+	/* Load data if not already done */
+	if (!trg->data) {
+		read_lock();
+		trg->data = read_sha1_file(trg_entry->idx.sha1, &type, &sz);
+		read_unlock();
+		if (!trg->data)
+			die("object %s cannot be read",
+			    sha1_to_hex(trg_entry->idx.sha1));
+		if (sz != trg_size)
+			die("object %s inconsistent object length (%lu vs %lu)",
+			    sha1_to_hex(trg_entry->idx.sha1), sz, trg_size);
+		*mem_usage += sz;
+	}
+	if (!src->data) {
+		read_lock();
+		src->data = read_sha1_file(src_entry->idx.sha1, &type, &sz);
+		read_unlock();
+		if (!src->data) {
+			if (src_entry->preferred_base) {
+				static int warned = 0;
+				if (!warned++)
+					warning("object %s cannot be read",
+						sha1_to_hex(src_entry->idx.sha1));
+				/*
+				 * Those objects are not included in the
+				 * resulting pack.  Be resilient and ignore
+				 * them if they can't be read, in case the
+				 * pack could be created nevertheless.
+				 */
+				return 0;
+			}
+			die("object %s cannot be read",
+			    sha1_to_hex(src_entry->idx.sha1));
+		}
+		if (sz != src_size)
+			die("object %s inconsistent object length (%lu vs %lu)",
+			    sha1_to_hex(src_entry->idx.sha1), sz, src_size);
+		*mem_usage += sz;
+	}
+	if (!src->index) {
+		src->index = create_delta_index(src->data, src_size);
+		if (!src->index) {
+			static int warned = 0;
+			if (!warned++)
+				warning("suboptimal pack - out of memory");
+			return 0;
+		}
+		*mem_usage += sizeof_delta_index(src->index);
+	}
+
+	delta_buf = create_delta(src->index, trg->data, trg_size, &delta_size, max_size);
+	if (!delta_buf)
+		return 0;
+
+	if (trg_entry->delta) {
+		/* Prefer only shallower same-sized deltas. */
+		if (delta_size == trg_entry->delta_size &&
+		    src->depth + 1 >= trg->depth) {
+			free(delta_buf);
+			return 0;
+		}
+	}
+
+	/*
+	 * Handle memory allocation outside of the cache
+	 * accounting lock.  Compiler will optimize the strangeness
+	 * away when NO_PTHREADS is defined.
+	 */
+	free(trg_entry->delta_data);
+	cache_lock();
+	if (trg_entry->delta_data) {
+		delta_cache_size -= trg_entry->delta_size;
+		trg_entry->delta_data = NULL;
+	}
+	if (delta_cacheable(src_size, trg_size, delta_size)) {
+		delta_cache_size += delta_size;
+		cache_unlock();
+		trg_entry->delta_data = xrealloc(delta_buf, delta_size);
+	} else {
+		cache_unlock();
+		free(delta_buf);
+	}
+
+	trg_entry->delta = src_entry;
+	trg_entry->delta_size = delta_size;
+	trg->depth = src->depth + 1;
+
+	return 1;
+}
+
+static unsigned int check_delta_limit(struct object_entry *me, unsigned int n)
+{
+	struct object_entry *child = me->delta_child;
+	unsigned int m = n;
+	while (child) {
+		unsigned int c = check_delta_limit(child, n + 1);
+		if (m < c)
+			m = c;
+		child = child->delta_sibling;
+	}
+	return m;
+}
+
+static unsigned long free_unpacked(struct unpacked *n)
+{
+	unsigned long freed_mem = sizeof_delta_index(n->index);
+	free_delta_index(n->index);
+	n->index = NULL;
+	if (n->data) {
+		freed_mem += n->entry->size;
+		free(n->data);
+		n->data = NULL;
+	}
+	n->entry = NULL;
+	n->depth = 0;
+	return freed_mem;
+}
+
+static void find_deltas(struct object_entry **list, unsigned *list_size,
+			int window, int depth, unsigned *processed)
+{
+	uint32_t i, idx = 0, count = 0;
+	struct unpacked *array;
+	unsigned long mem_usage = 0;
+
+	array = xcalloc(window, sizeof(struct unpacked));
+
+	for (;;) {
+		struct object_entry *entry;
+		struct unpacked *n = array + idx;
+		int j, max_depth, best_base = -1;
+
+		progress_lock();
+		if (!*list_size) {
+			progress_unlock();
+			break;
+		}
+		entry = *list++;
+		(*list_size)--;
+		if (!entry->preferred_base) {
+			(*processed)++;
+			display_progress(progress_state, *processed);
+		}
+		progress_unlock();
+
+		mem_usage -= free_unpacked(n);
+		n->entry = entry;
+
+		while (window_memory_limit &&
+		       mem_usage > window_memory_limit &&
+		       count > 1) {
+			uint32_t tail = (idx + window - count) % window;
+			mem_usage -= free_unpacked(array + tail);
+			count--;
+		}
+
+		/* We do not compute delta to *create* objects we are not
+		 * going to pack.
+		 */
+		if (entry->preferred_base)
+			goto next;
+
+		/*
+		 * If the current object is at pack edge, take the depth the
+		 * objects that depend on the current object into account
+		 * otherwise they would become too deep.
+		 */
+		max_depth = depth;
+		if (entry->delta_child) {
+			max_depth -= check_delta_limit(entry, 0);
+			if (max_depth <= 0)
+				goto next;
+		}
+
+		j = window;
+		while (--j > 0) {
+			int ret;
+			uint32_t other_idx = idx + j;
+			struct unpacked *m;
+			if (other_idx >= window)
+				other_idx -= window;
+			m = array + other_idx;
+			if (!m->entry)
+				break;
+			ret = try_delta(n, m, max_depth, &mem_usage);
+			if (ret < 0)
+				break;
+			else if (ret > 0)
+				best_base = other_idx;
+		}
+
+		/*
+		 * If we decided to cache the delta data, then it is best
+		 * to compress it right away.  First because we have to do
+		 * it anyway, and doing it here while we're threaded will
+		 * save a lot of time in the non threaded write phase,
+		 * as well as allow for caching more deltas within
+		 * the same cache size limit.
+		 * ...
+		 * But only if not writing to stdout, since in that case
+		 * the network is most likely throttling writes anyway,
+		 * and therefore it is best to go to the write phase ASAP
+		 * instead, as we can afford spending more time compressing
+		 * between writes at that moment.
+		 */
+		if (entry->delta_data && !pack_to_stdout) {
+			entry->z_delta_size = do_compress(&entry->delta_data,
+							  entry->delta_size);
+			cache_lock();
+			delta_cache_size -= entry->delta_size;
+			delta_cache_size += entry->z_delta_size;
+			cache_unlock();
+		}
+
+		/* if we made n a delta, and if n is already at max
+		 * depth, leaving it in the window is pointless.  we
+		 * should evict it first.
+		 */
+		if (entry->delta && max_depth <= n->depth)
+			continue;
+
+		/*
+		 * Move the best delta base up in the window, after the
+		 * currently deltified object, to keep it longer.  It will
+		 * be the first base object to be attempted next.
+		 */
+		if (entry->delta) {
+			struct unpacked swap = array[best_base];
+			int dist = (window + idx - best_base) % window;
+			int dst = best_base;
+			while (dist--) {
+				int src = (dst + 1) % window;
+				array[dst] = array[src];
+				dst = src;
+			}
+			array[dst] = swap;
+		}
+
+		next:
+		idx++;
+		if (count + 1 < window)
+			count++;
+		if (idx >= window)
+			idx = 0;
+	}
+
+	for (i = 0; i < window; ++i) {
+		free_delta_index(array[i].index);
+		free(array[i].data);
+	}
+	free(array);
+}
+
+#ifndef NO_PTHREADS
+
+static void try_to_free_from_threads(size_t size)
+{
+	read_lock();
+	release_pack_memory(size, -1);
+	read_unlock();
+}
+
+static try_to_free_t old_try_to_free_routine;
+
+/*
+ * The main thread waits on the condition that (at least) one of the workers
+ * has stopped working (which is indicated in the .working member of
+ * struct thread_params).
+ * When a work thread has completed its work, it sets .working to 0 and
+ * signals the main thread and waits on the condition that .data_ready
+ * becomes 1.
+ */
+
+struct thread_params {
+	pthread_t thread;
+	struct object_entry **list;
+	unsigned list_size;
+	unsigned remaining;
+	int window;
+	int depth;
+	int working;
+	int data_ready;
+	pthread_mutex_t mutex;
+	pthread_cond_t cond;
+	unsigned *processed;
+};
+
+static pthread_cond_t progress_cond;
+
+/*
+ * Mutex and conditional variable can't be statically-initialized on Windows.
+ */
+static void init_threaded_search(void)
+{
+	init_recursive_mutex(&read_mutex);
+	pthread_mutex_init(&cache_mutex, NULL);
+	pthread_mutex_init(&progress_mutex, NULL);
+	pthread_cond_init(&progress_cond, NULL);
+	old_try_to_free_routine = set_try_to_free_routine(try_to_free_from_threads);
+}
+
+static void cleanup_threaded_search(void)
+{
+	set_try_to_free_routine(old_try_to_free_routine);
+	pthread_cond_destroy(&progress_cond);
+	pthread_mutex_destroy(&read_mutex);
+	pthread_mutex_destroy(&cache_mutex);
+	pthread_mutex_destroy(&progress_mutex);
+}
+
+static void *threaded_find_deltas(void *arg)
+{
+	struct thread_params *me = arg;
+
+	while (me->remaining) {
+		find_deltas(me->list, &me->remaining,
+			    me->window, me->depth, me->processed);
+
+		progress_lock();
+		me->working = 0;
+		pthread_cond_signal(&progress_cond);
+		progress_unlock();
+
+		/*
+		 * We must not set ->data_ready before we wait on the
+		 * condition because the main thread may have set it to 1
+		 * before we get here. In order to be sure that new
+		 * work is available if we see 1 in ->data_ready, it
+		 * was initialized to 0 before this thread was spawned
+		 * and we reset it to 0 right away.
+		 */
+		pthread_mutex_lock(&me->mutex);
+		while (!me->data_ready)
+			pthread_cond_wait(&me->cond, &me->mutex);
+		me->data_ready = 0;
+		pthread_mutex_unlock(&me->mutex);
+	}
+	/* leave ->working 1 so that this doesn't get more work assigned */
+	return NULL;
+}
+
+static void ll_find_deltas(struct object_entry **list, unsigned list_size,
+			   int window, int depth, unsigned *processed)
+{
+	struct thread_params *p;
+	int i, ret, active_threads = 0;
+
+	init_threaded_search();
+
+	if (!delta_search_threads)	/* --threads=0 means autodetect */
+		delta_search_threads = online_cpus();
+	if (delta_search_threads <= 1) {
+		find_deltas(list, &list_size, window, depth, processed);
+		cleanup_threaded_search();
+		return;
+	}
+	if (progress > pack_to_stdout)
+		fprintf(stderr, "Delta compression using up to %d threads.\n",
+				delta_search_threads);
+	p = xcalloc(delta_search_threads, sizeof(*p));
+
+	/* Partition the work amongst work threads. */
+	for (i = 0; i < delta_search_threads; i++) {
+		unsigned sub_size = list_size / (delta_search_threads - i);
+
+		/* don't use too small segments or no deltas will be found */
+		if (sub_size < 2*window && i+1 < delta_search_threads)
+			sub_size = 0;
+
+		p[i].window = window;
+		p[i].depth = depth;
+		p[i].processed = processed;
+		p[i].working = 1;
+		p[i].data_ready = 0;
+
+		/* try to split chunks on "path" boundaries */
+		while (sub_size && sub_size < list_size &&
+		       list[sub_size]->hash &&
+		       list[sub_size]->hash == list[sub_size-1]->hash)
+			sub_size++;
+
+		p[i].list = list;
+		p[i].list_size = sub_size;
+		p[i].remaining = sub_size;
+
+		list += sub_size;
+		list_size -= sub_size;
+	}
+
+	/* Start work threads. */
+	for (i = 0; i < delta_search_threads; i++) {
+		if (!p[i].list_size)
+			continue;
+		pthread_mutex_init(&p[i].mutex, NULL);
+		pthread_cond_init(&p[i].cond, NULL);
+		ret = pthread_create(&p[i].thread, NULL,
+				     threaded_find_deltas, &p[i]);
+		if (ret)
+			die("unable to create thread: %s", strerror(ret));
+		active_threads++;
+	}
+
+	/*
+	 * Now let's wait for work completion.  Each time a thread is done
+	 * with its work, we steal half of the remaining work from the
+	 * thread with the largest number of unprocessed objects and give
+	 * it to that newly idle thread.  This ensure good load balancing
+	 * until the remaining object list segments are simply too short
+	 * to be worth splitting anymore.
+	 */
+	while (active_threads) {
+		struct thread_params *target = NULL;
+		struct thread_params *victim = NULL;
+		unsigned sub_size = 0;
+
+		progress_lock();
+		for (;;) {
+			for (i = 0; !target && i < delta_search_threads; i++)
+				if (!p[i].working)
+					target = &p[i];
+			if (target)
+				break;
+			pthread_cond_wait(&progress_cond, &progress_mutex);
+		}
+
+		for (i = 0; i < delta_search_threads; i++)
+			if (p[i].remaining > 2*window &&
+			    (!victim || victim->remaining < p[i].remaining))
+				victim = &p[i];
+		if (victim) {
+			sub_size = victim->remaining / 2;
+			list = victim->list + victim->list_size - sub_size;
+			while (sub_size && list[0]->hash &&
+			       list[0]->hash == list[-1]->hash) {
+				list++;
+				sub_size--;
+			}
+			if (!sub_size) {
+				/*
+				 * It is possible for some "paths" to have
+				 * so many objects that no hash boundary
+				 * might be found.  Let's just steal the
+				 * exact half in that case.
+				 */
+				sub_size = victim->remaining / 2;
+				list -= sub_size;
+			}
+			target->list = list;
+			victim->list_size -= sub_size;
+			victim->remaining -= sub_size;
+		}
+		target->list_size = sub_size;
+		target->remaining = sub_size;
+		target->working = 1;
+		progress_unlock();
+
+		pthread_mutex_lock(&target->mutex);
+		target->data_ready = 1;
+		pthread_cond_signal(&target->cond);
+		pthread_mutex_unlock(&target->mutex);
+
+		if (!sub_size) {
+			pthread_join(target->thread, NULL);
+			pthread_cond_destroy(&target->cond);
+			pthread_mutex_destroy(&target->mutex);
+			active_threads--;
+		}
+	}
+	cleanup_threaded_search();
+	free(p);
+}
+
+#else
+#define ll_find_deltas(l, s, w, d, p)	find_deltas(l, &s, w, d, p)
+#endif
+
+static int add_ref_tag(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+{
+	unsigned char peeled[20];
+
+	if (!prefixcmp(path, "refs/tags/") && /* is a tag? */
+	    !peel_ref(path, peeled)        && /* peelable? */
+	    !is_null_sha1(peeled)          && /* annotated tag? */
+	    locate_object_entry(peeled))      /* object packed? */
+		add_object_entry(sha1, OBJ_TAG, NULL, 0);
+	return 0;
+}
+
+static void prepare_pack(int window, int depth)
+{
+	struct object_entry **delta_list;
+	uint32_t i, nr_deltas;
+	unsigned n;
+
+	get_object_details();
+
+	/*
+	 * If we're locally repacking then we need to be doubly careful
+	 * from now on in order to make sure no stealth corruption gets
+	 * propagated to the new pack.  Clients receiving streamed packs
+	 * should validate everything they get anyway so no need to incur
+	 * the additional cost here in that case.
+	 */
+	if (!pack_to_stdout)
+		do_check_packed_object_crc = 1;
+
+	if (!nr_objects || !window || !depth)
+		return;
+
+	delta_list = xmalloc(nr_objects * sizeof(*delta_list));
+	nr_deltas = n = 0;
+
+	for (i = 0; i < nr_objects; i++) {
+		struct object_entry *entry = objects + i;
+
+		if (entry->delta)
+			/* This happens if we decided to reuse existing
+			 * delta from a pack.  "reuse_delta &&" is implied.
+			 */
+			continue;
+
+		if (entry->size < 50)
+			continue;
+
+		if (entry->no_try_delta)
+			continue;
+
+		if (!entry->preferred_base) {
+			nr_deltas++;
+			if (entry->type < 0)
+				die("unable to get type of object %s",
+				    sha1_to_hex(entry->idx.sha1));
+		} else {
+			if (entry->type < 0) {
+				/*
+				 * This object is not found, but we
+				 * don't have to include it anyway.
+				 */
+				continue;
+			}
+		}
+
+		delta_list[n++] = entry;
+	}
+
+	if (nr_deltas && n > 1) {
+		unsigned nr_done = 0;
+		if (progress)
+			progress_state = start_progress("Compressing objects",
+							nr_deltas);
+		qsort(delta_list, n, sizeof(*delta_list), type_size_sort);
+		ll_find_deltas(delta_list, n, window+1, depth, &nr_done);
+		stop_progress(&progress_state);
+		if (nr_done != nr_deltas)
+			die("inconsistency with delta count");
+	}
+	free(delta_list);
+}
+
+static int git_pack_config(const char *k, const char *v, void *cb)
+{
+	if (!strcmp(k, "pack.window")) {
+		window = git_config_int(k, v);
+		return 0;
+	}
+	if (!strcmp(k, "pack.windowmemory")) {
+		window_memory_limit = git_config_ulong(k, v);
+		return 0;
+	}
+	if (!strcmp(k, "pack.depth")) {
+		depth = git_config_int(k, v);
+		return 0;
+	}
+	if (!strcmp(k, "pack.compression")) {
+		int level = git_config_int(k, v);
+		if (level == -1)
+			level = Z_DEFAULT_COMPRESSION;
+		else if (level < 0 || level > Z_BEST_COMPRESSION)
+			die("bad pack compression level %d", level);
+		pack_compression_level = level;
+		pack_compression_seen = 1;
+		return 0;
+	}
+	if (!strcmp(k, "pack.deltacachesize")) {
+		max_delta_cache_size = git_config_int(k, v);
+		return 0;
+	}
+	if (!strcmp(k, "pack.deltacachelimit")) {
+		cache_max_small_delta_size = git_config_int(k, v);
+		return 0;
+	}
+	if (!strcmp(k, "pack.threads")) {
+		delta_search_threads = git_config_int(k, v);
+		if (delta_search_threads < 0)
+			die("invalid number of threads specified (%d)",
+			    delta_search_threads);
+#ifdef NO_PTHREADS
+		if (delta_search_threads != 1)
+			warning("no threads support, ignoring %s", k);
+#endif
+		return 0;
+	}
+	if (!strcmp(k, "pack.indexversion")) {
+		pack_idx_opts.version = git_config_int(k, v);
+		if (pack_idx_opts.version > 2)
+			die("bad pack.indexversion=%"PRIu32,
+			    pack_idx_opts.version);
+		return 0;
+	}
+	return git_default_config(k, v, cb);
+}
+
+static void read_object_list_from_stdin(void)
+{
+	char line[40 + 1 + PATH_MAX + 2];
+	unsigned char sha1[20];
+
+	for (;;) {
+		if (!fgets(line, sizeof(line), stdin)) {
+			if (feof(stdin))
+				break;
+			if (!ferror(stdin))
+				die("fgets returned NULL, not EOF, not error!");
+			if (errno != EINTR)
+				die_errno("fgets");
+			clearerr(stdin);
+			continue;
+		}
+		if (line[0] == '-') {
+			if (get_sha1_hex(line+1, sha1))
+				die("expected edge sha1, got garbage:\n %s",
+				    line);
+			add_preferred_base(sha1);
+			continue;
+		}
+		if (get_sha1_hex(line, sha1))
+			die("expected sha1, got garbage:\n %s", line);
+
+		add_preferred_base_object(line+41);
+		add_object_entry(sha1, 0, line+41, 0);
+	}
+}
+
+#define OBJECT_ADDED (1u<<20)
+
+static void show_commit(struct commit *commit, void *data)
+{
+	add_object_entry(commit->object.sha1, OBJ_COMMIT, NULL, 0);
+	commit->object.flags |= OBJECT_ADDED;
+}
+
+static void show_object(struct object *obj,
+			const struct name_path *path, const char *last,
+			void *data)
+{
+	char *name = path_name(path, last);
+
+	add_preferred_base_object(name);
+	add_object_entry(obj->sha1, obj->type, name, 0);
+	obj->flags |= OBJECT_ADDED;
+
+	/*
+	 * We will have generated the hash from the name,
+	 * but not saved a pointer to it - we can free it
+	 */
+	free((char *)name);
+}
+
+static void show_edge(struct commit *commit)
+{
+	add_preferred_base(commit->object.sha1);
+}
+
+struct in_pack_object {
+	off_t offset;
+	struct object *object;
+};
+
+struct in_pack {
+	int alloc;
+	int nr;
+	struct in_pack_object *array;
+};
+
+static void mark_in_pack_object(struct object *object, struct packed_git *p, struct in_pack *in_pack)
+{
+	in_pack->array[in_pack->nr].offset = find_pack_entry_one(object->sha1, p);
+	in_pack->array[in_pack->nr].object = object;
+	in_pack->nr++;
+}
+
+/*
+ * Compare the objects in the offset order, in order to emulate the
+ * "git rev-list --objects" output that produced the pack originally.
+ */
+static int ofscmp(const void *a_, const void *b_)
+{
+	struct in_pack_object *a = (struct in_pack_object *)a_;
+	struct in_pack_object *b = (struct in_pack_object *)b_;
+
+	if (a->offset < b->offset)
+		return -1;
+	else if (a->offset > b->offset)
+		return 1;
+	else
+		return hashcmp(a->object->sha1, b->object->sha1);
+}
+
+static void add_objects_in_unpacked_packs(struct rev_info *revs)
+{
+	struct packed_git *p;
+	struct in_pack in_pack;
+	uint32_t i;
+
+	memset(&in_pack, 0, sizeof(in_pack));
+
+	for (p = packed_git; p; p = p->next) {
+		const unsigned char *sha1;
+		struct object *o;
+
+		if (!p->pack_local || p->pack_keep)
+			continue;
+		if (open_pack_index(p))
+			die("cannot open pack index");
+
+		ALLOC_GROW(in_pack.array,
+			   in_pack.nr + p->num_objects,
+			   in_pack.alloc);
+
+		for (i = 0; i < p->num_objects; i++) {
+			sha1 = nth_packed_object_sha1(p, i);
+			o = lookup_unknown_object(sha1);
+			if (!(o->flags & OBJECT_ADDED))
+				mark_in_pack_object(o, p, &in_pack);
+			o->flags |= OBJECT_ADDED;
+		}
+	}
+
+	if (in_pack.nr) {
+		qsort(in_pack.array, in_pack.nr, sizeof(in_pack.array[0]),
+		      ofscmp);
+		for (i = 0; i < in_pack.nr; i++) {
+			struct object *o = in_pack.array[i].object;
+			add_object_entry(o->sha1, o->type, "", 0);
+		}
+	}
+	free(in_pack.array);
+}
+
+static int has_sha1_pack_kept_or_nonlocal(const unsigned char *sha1)
+{
+	static struct packed_git *last_found = (void *)1;
+	struct packed_git *p;
+
+	p = (last_found != (void *)1) ? last_found : packed_git;
+
+	while (p) {
+		if ((!p->pack_local || p->pack_keep) &&
+			find_pack_entry_one(sha1, p)) {
+			last_found = p;
+			return 1;
+		}
+		if (p == last_found)
+			p = packed_git;
+		else
+			p = p->next;
+		if (p == last_found)
+			p = p->next;
+	}
+	return 0;
+}
+
+static void loosen_unused_packed_objects(struct rev_info *revs)
+{
+	struct packed_git *p;
+	uint32_t i;
+	const unsigned char *sha1;
+
+	for (p = packed_git; p; p = p->next) {
+		if (!p->pack_local || p->pack_keep)
+			continue;
+
+		if (unpack_unreachable_expiration &&
+		    p->mtime < unpack_unreachable_expiration)
+			continue;
+
+		if (open_pack_index(p))
+			die("cannot open pack index");
+
+		for (i = 0; i < p->num_objects; i++) {
+			sha1 = nth_packed_object_sha1(p, i);
+			if (!locate_object_entry(sha1) &&
+				!has_sha1_pack_kept_or_nonlocal(sha1))
+				if (force_object_loose(sha1, p->mtime))
+					die("unable to force loose object");
+		}
+	}
+}
+
+static void get_object_list(int ac, const char **av)
+{
+	struct rev_info revs;
+	char line[1000];
+	int flags = 0;
+
+	init_revisions(&revs, NULL);
+	save_commit_buffer = 0;
+	setup_revisions(ac, av, &revs, NULL);
+
+	while (fgets(line, sizeof(line), stdin) != NULL) {
+		int len = strlen(line);
+		if (len && line[len - 1] == '\n')
+			line[--len] = 0;
+		if (!len)
+			break;
+		if (*line == '-') {
+			if (!strcmp(line, "--not")) {
+				flags ^= UNINTERESTING;
+				continue;
+			}
+			die("not a rev '%s'", line);
+		}
+		if (handle_revision_arg(line, &revs, flags, REVARG_CANNOT_BE_FILENAME))
+			die("bad revision '%s'", line);
+	}
+
+	if (prepare_revision_walk(&revs))
+		die("revision walk setup failed");
+	mark_edges_uninteresting(revs.commits, &revs, show_edge);
+	traverse_commit_list(&revs, show_commit, show_object, NULL);
+
+	if (keep_unreachable)
+		add_objects_in_unpacked_packs(&revs);
+	if (unpack_unreachable)
+		loosen_unused_packed_objects(&revs);
+}
+
+static int option_parse_index_version(const struct option *opt,
+				      const char *arg, int unset)
+{
+	char *c;
+	const char *val = arg;
+	pack_idx_opts.version = strtoul(val, &c, 10);
+	if (pack_idx_opts.version > 2)
+		die(_("unsupported index version %s"), val);
+	if (*c == ',' && c[1])
+		pack_idx_opts.off32_limit = strtoul(c+1, &c, 0);
+	if (*c || pack_idx_opts.off32_limit & 0x80000000)
+		die(_("bad index version '%s'"), val);
+	return 0;
+}
+
+static int option_parse_unpack_unreachable(const struct option *opt,
+					   const char *arg, int unset)
+{
+	if (unset) {
+		unpack_unreachable = 0;
+		unpack_unreachable_expiration = 0;
+	}
+	else {
+		unpack_unreachable = 1;
+		if (arg)
+			unpack_unreachable_expiration = approxidate(arg);
+	}
+	return 0;
+}
+
+static int option_parse_ulong(const struct option *opt,
+			      const char *arg, int unset)
+{
+	if (unset)
+		die(_("option %s does not accept negative form"),
+		    opt->long_name);
+
+	if (!git_parse_ulong(arg, opt->value))
+		die(_("unable to parse value '%s' for option %s"),
+		    arg, opt->long_name);
+	return 0;
+}
+
+#define OPT_ULONG(s, l, v, h) \
+	{ OPTION_CALLBACK, (s), (l), (v), "n", (h),	\
+	  PARSE_OPT_NONEG, option_parse_ulong }
+
+int cmd_pack_objects(int argc, const char **argv, const char *prefix)
+{
+	int use_internal_rev_list = 0;
+	int thin = 0;
+	int all_progress_implied = 0;
+	const char *rp_av[6];
+	int rp_ac = 0;
+	int rev_list_unpacked = 0, rev_list_all = 0, rev_list_reflog = 0;
+	struct option pack_objects_options[] = {
+		OPT_SET_INT('q', "quiet", &progress,
+			    "do not show progress meter", 0),
+		OPT_SET_INT(0, "progress", &progress,
+			    "show progress meter", 1),
+		OPT_SET_INT(0, "all-progress", &progress,
+			    "show progress meter during object writing phase", 2),
+		OPT_BOOL(0, "all-progress-implied",
+			 &all_progress_implied,
+			 "similar to --all-progress when progress meter is shown"),
+		{ OPTION_CALLBACK, 0, "index-version", NULL, "version[,offset]",
+		  "write the pack index file in the specified idx format version",
+		  0, option_parse_index_version },
+		OPT_ULONG(0, "max-pack-size", &pack_size_limit,
+			  "maximum size of each output pack file"),
+		OPT_BOOL(0, "local", &local,
+			 "ignore borrowed objects from alternate object store"),
+		OPT_BOOL(0, "incremental", &incremental,
+			 "ignore packed objects"),
+		OPT_INTEGER(0, "window", &window,
+			    "limit pack window by objects"),
+		OPT_ULONG(0, "window-memory", &window_memory_limit,
+			  "limit pack window by memory in addition to object limit"),
+		OPT_INTEGER(0, "depth", &depth,
+			    "maximum length of delta chain allowed in the resulting pack"),
+		OPT_BOOL(0, "reuse-delta", &reuse_delta,
+			 "reuse existing deltas"),
+		OPT_BOOL(0, "reuse-object", &reuse_object,
+			 "reuse existing objects"),
+		OPT_BOOL(0, "delta-base-offset", &allow_ofs_delta,
+			 "use OFS_DELTA objects"),
+		OPT_INTEGER(0, "threads", &delta_search_threads,
+			    "use threads when searching for best delta matches"),
+		OPT_BOOL(0, "non-empty", &non_empty,
+			 "do not create an empty pack output"),
+		OPT_BOOL(0, "revs", &use_internal_rev_list,
+			 "read revision arguments from standard input"),
+		{ OPTION_SET_INT, 0, "unpacked", &rev_list_unpacked, NULL,
+		  "limit the objects to those that are not yet packed",
+		  PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 },
+		{ OPTION_SET_INT, 0, "all", &rev_list_all, NULL,
+		  "include objects reachable from any reference",
+		  PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 },
+		{ OPTION_SET_INT, 0, "reflog", &rev_list_reflog, NULL,
+		  "include objects referred by reflog entries",
+		  PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 },
+		OPT_BOOL(0, "stdout", &pack_to_stdout,
+			 "output pack to stdout"),
+		OPT_BOOL(0, "include-tag", &include_tag,
+			 "include tag objects that refer to objects to be packed"),
+		OPT_BOOL(0, "keep-unreachable", &keep_unreachable,
+			 "keep unreachable objects"),
+		{ OPTION_CALLBACK, 0, "unpack-unreachable", NULL, "time",
+		  "unpack unreachable objects newer than <time>",
+		  PARSE_OPT_OPTARG, option_parse_unpack_unreachable },
+		OPT_BOOL(0, "thin", &thin,
+			 "create thin packs"),
+		OPT_BOOL(0, "honor-pack-keep", &ignore_packed_keep,
+			 "ignore packs that have companion .keep file"),
+		OPT_INTEGER(0, "compression", &pack_compression_level,
+			    "pack compression level"),
+		OPT_SET_INT(0, "keep-true-parents", &grafts_replace_parents,
+			    "do not hide commits by grafts", 0),
+		OPT_END(),
+	};
+
+	read_replace_refs = 0;
+
+	reset_pack_idx_option(&pack_idx_opts);
+	git_config(git_pack_config, NULL);
+	if (!pack_compression_seen && core_compression_seen)
+		pack_compression_level = core_compression_level;
+
+	progress = isatty(2);
+	argc = parse_options(argc, argv, prefix, pack_objects_options,
+			     pack_usage, 0);
+
+	if (argc) {
+		base_name = argv[0];
+		argc--;
+	}
+	if (pack_to_stdout != !base_name || argc)
+		usage_with_options(pack_usage, pack_objects_options);
+
+	rp_av[rp_ac++] = "pack-objects";
+	if (thin) {
+		use_internal_rev_list = 1;
+		rp_av[rp_ac++] = "--objects-edge";
+	} else
+		rp_av[rp_ac++] = "--objects";
+
+	if (rev_list_all) {
+		use_internal_rev_list = 1;
+		rp_av[rp_ac++] = "--all";
+	}
+	if (rev_list_reflog) {
+		use_internal_rev_list = 1;
+		rp_av[rp_ac++] = "--reflog";
+	}
+	if (rev_list_unpacked) {
+		use_internal_rev_list = 1;
+		rp_av[rp_ac++] = "--unpacked";
+	}
+
+	if (!reuse_object)
+		reuse_delta = 0;
+	if (pack_compression_level == -1)
+		pack_compression_level = Z_DEFAULT_COMPRESSION;
+	else if (pack_compression_level < 0 || pack_compression_level > Z_BEST_COMPRESSION)
+		die("bad pack compression level %d", pack_compression_level);
+#ifdef NO_PTHREADS
+	if (delta_search_threads != 1)
+		warning("no threads support, ignoring --threads");
+#endif
+	if (!pack_to_stdout && !pack_size_limit)
+		pack_size_limit = pack_size_limit_cfg;
+	if (pack_to_stdout && pack_size_limit)
+		die("--max-pack-size cannot be used to build a pack for transfer.");
+	if (pack_size_limit && pack_size_limit < 1024*1024) {
+		warning("minimum pack size limit is 1 MiB");
+		pack_size_limit = 1024*1024;
+	}
+
+	if (!pack_to_stdout && thin)
+		die("--thin cannot be used to build an indexable pack.");
+
+	if (keep_unreachable && unpack_unreachable)
+		die("--keep-unreachable and --unpack-unreachable are incompatible.");
+
+	if (progress && all_progress_implied)
+		progress = 2;
+
+	prepare_packed_git();
+
+	if (progress)
+		progress_state = start_progress("Counting objects", 0);
+	if (!use_internal_rev_list)
+		read_object_list_from_stdin();
+	else {
+		rp_av[rp_ac] = NULL;
+		get_object_list(rp_ac, rp_av);
+	}
+	cleanup_preferred_base();
+	if (include_tag && nr_result)
+		for_each_ref(add_ref_tag, NULL);
+	stop_progress(&progress_state);
+
+	if (non_empty && !nr_result)
+		return 0;
+	if (nr_result)
+		prepare_pack(window, depth);
+	write_pack_file();
+	if (progress)
+		fprintf(stderr, "Total %"PRIu32" (delta %"PRIu32"),"
+			" reused %"PRIu32" (delta %"PRIu32")\n",
+			written, written_delta, reused, reused_delta);
+	return 0;
+}
diff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c
new file mode 100644
index 0000000..f5c6afc
--- /dev/null
+++ b/builtin/pack-redundant.c
@@ -0,0 +1,695 @@
+/*
+*
+* Copyright 2005, Lukas Sandstrom <lukass@etek.chalmers.se>
+*
+* This file is licensed under the GPL v2.
+*
+*/
+
+#include "builtin.h"
+
+#define BLKSIZE 512
+
+static const char pack_redundant_usage[] =
+"git pack-redundant [ --verbose ] [ --alt-odb ] < --all | <.pack filename> ...>";
+
+static int load_all_packs, verbose, alt_odb;
+
+struct llist_item {
+	struct llist_item *next;
+	const unsigned char *sha1;
+};
+static struct llist {
+	struct llist_item *front;
+	struct llist_item *back;
+	size_t size;
+} *all_objects; /* all objects which must be present in local packfiles */
+
+static struct pack_list {
+	struct pack_list *next;
+	struct packed_git *pack;
+	struct llist *unique_objects;
+	struct llist *all_objects;
+} *local_packs = NULL, *altodb_packs = NULL;
+
+struct pll {
+	struct pll *next;
+	struct pack_list *pl;
+};
+
+static struct llist_item *free_nodes;
+
+static inline void llist_item_put(struct llist_item *item)
+{
+	item->next = free_nodes;
+	free_nodes = item;
+}
+
+static inline struct llist_item *llist_item_get(void)
+{
+	struct llist_item *new;
+	if ( free_nodes ) {
+		new = free_nodes;
+		free_nodes = free_nodes->next;
+	} else {
+		int i = 1;
+		new = xmalloc(sizeof(struct llist_item) * BLKSIZE);
+		for (; i < BLKSIZE; i++)
+			llist_item_put(&new[i]);
+	}
+	return new;
+}
+
+static void llist_free(struct llist *list)
+{
+	while ((list->back = list->front)) {
+		list->front = list->front->next;
+		llist_item_put(list->back);
+	}
+	free(list);
+}
+
+static inline void llist_init(struct llist **list)
+{
+	*list = xmalloc(sizeof(struct llist));
+	(*list)->front = (*list)->back = NULL;
+	(*list)->size = 0;
+}
+
+static struct llist * llist_copy(struct llist *list)
+{
+	struct llist *ret;
+	struct llist_item *new, *old, *prev;
+
+	llist_init(&ret);
+
+	if ((ret->size = list->size) == 0)
+		return ret;
+
+	new = ret->front = llist_item_get();
+	new->sha1 = list->front->sha1;
+
+	old = list->front->next;
+	while (old) {
+		prev = new;
+		new = llist_item_get();
+		prev->next = new;
+		new->sha1 = old->sha1;
+		old = old->next;
+	}
+	new->next = NULL;
+	ret->back = new;
+
+	return ret;
+}
+
+static inline struct llist_item *llist_insert(struct llist *list,
+					      struct llist_item *after,
+					       const unsigned char *sha1)
+{
+	struct llist_item *new = llist_item_get();
+	new->sha1 = sha1;
+	new->next = NULL;
+
+	if (after != NULL) {
+		new->next = after->next;
+		after->next = new;
+		if (after == list->back)
+			list->back = new;
+	} else {/* insert in front */
+		if (list->size == 0)
+			list->back = new;
+		else
+			new->next = list->front;
+		list->front = new;
+	}
+	list->size++;
+	return new;
+}
+
+static inline struct llist_item *llist_insert_back(struct llist *list,
+						   const unsigned char *sha1)
+{
+	return llist_insert(list, list->back, sha1);
+}
+
+static inline struct llist_item *llist_insert_sorted_unique(struct llist *list,
+			const unsigned char *sha1, struct llist_item *hint)
+{
+	struct llist_item *prev = NULL, *l;
+
+	l = (hint == NULL) ? list->front : hint;
+	while (l) {
+		int cmp = hashcmp(l->sha1, sha1);
+		if (cmp > 0) { /* we insert before this entry */
+			return llist_insert(list, prev, sha1);
+		}
+		if (!cmp) { /* already exists */
+			return l;
+		}
+		prev = l;
+		l = l->next;
+	}
+	/* insert at the end */
+	return llist_insert_back(list, sha1);
+}
+
+/* returns a pointer to an item in front of sha1 */
+static inline struct llist_item * llist_sorted_remove(struct llist *list, const unsigned char *sha1, struct llist_item *hint)
+{
+	struct llist_item *prev, *l;
+
+redo_from_start:
+	l = (hint == NULL) ? list->front : hint;
+	prev = NULL;
+	while (l) {
+		int cmp = hashcmp(l->sha1, sha1);
+		if (cmp > 0) /* not in list, since sorted */
+			return prev;
+		if (!cmp) { /* found */
+			if (prev == NULL) {
+				if (hint != NULL && hint != list->front) {
+					/* we don't know the previous element */
+					hint = NULL;
+					goto redo_from_start;
+				}
+				list->front = l->next;
+			} else
+				prev->next = l->next;
+			if (l == list->back)
+				list->back = prev;
+			llist_item_put(l);
+			list->size--;
+			return prev;
+		}
+		prev = l;
+		l = l->next;
+	}
+	return prev;
+}
+
+/* computes A\B */
+static void llist_sorted_difference_inplace(struct llist *A,
+				     struct llist *B)
+{
+	struct llist_item *hint, *b;
+
+	hint = NULL;
+	b = B->front;
+
+	while (b) {
+		hint = llist_sorted_remove(A, b->sha1, hint);
+		b = b->next;
+	}
+}
+
+static inline struct pack_list * pack_list_insert(struct pack_list **pl,
+					   struct pack_list *entry)
+{
+	struct pack_list *p = xmalloc(sizeof(struct pack_list));
+	memcpy(p, entry, sizeof(struct pack_list));
+	p->next = *pl;
+	*pl = p;
+	return p;
+}
+
+static inline size_t pack_list_size(struct pack_list *pl)
+{
+	size_t ret = 0;
+	while (pl) {
+		ret++;
+		pl = pl->next;
+	}
+	return ret;
+}
+
+static struct pack_list * pack_list_difference(const struct pack_list *A,
+					       const struct pack_list *B)
+{
+	struct pack_list *ret;
+	const struct pack_list *pl;
+
+	if (A == NULL)
+		return NULL;
+
+	pl = B;
+	while (pl != NULL) {
+		if (A->pack == pl->pack)
+			return pack_list_difference(A->next, B);
+		pl = pl->next;
+	}
+	ret = xmalloc(sizeof(struct pack_list));
+	memcpy(ret, A, sizeof(struct pack_list));
+	ret->next = pack_list_difference(A->next, B);
+	return ret;
+}
+
+static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)
+{
+	unsigned long p1_off = 0, p2_off = 0, p1_step, p2_step;
+	const unsigned char *p1_base, *p2_base;
+	struct llist_item *p1_hint = NULL, *p2_hint = NULL;
+
+	p1_base = p1->pack->index_data;
+	p2_base = p2->pack->index_data;
+	p1_base += 256 * 4 + ((p1->pack->index_version < 2) ? 4 : 8);
+	p2_base += 256 * 4 + ((p2->pack->index_version < 2) ? 4 : 8);
+	p1_step = (p1->pack->index_version < 2) ? 24 : 20;
+	p2_step = (p2->pack->index_version < 2) ? 24 : 20;
+
+	while (p1_off < p1->pack->num_objects * p1_step &&
+	       p2_off < p2->pack->num_objects * p2_step)
+	{
+		int cmp = hashcmp(p1_base + p1_off, p2_base + p2_off);
+		/* cmp ~ p1 - p2 */
+		if (cmp == 0) {
+			p1_hint = llist_sorted_remove(p1->unique_objects,
+					p1_base + p1_off, p1_hint);
+			p2_hint = llist_sorted_remove(p2->unique_objects,
+					p1_base + p1_off, p2_hint);
+			p1_off += p1_step;
+			p2_off += p2_step;
+			continue;
+		}
+		if (cmp < 0) { /* p1 has the object, p2 doesn't */
+			p1_off += p1_step;
+		} else { /* p2 has the object, p1 doesn't */
+			p2_off += p2_step;
+		}
+	}
+}
+
+static void pll_free(struct pll *l)
+{
+	struct pll *old;
+	struct pack_list *opl;
+
+	while (l) {
+		old = l;
+		while (l->pl) {
+			opl = l->pl;
+			l->pl = opl->next;
+			free(opl);
+		}
+		l = l->next;
+		free(old);
+	}
+}
+
+/* all the permutations have to be free()d at the same time,
+ * since they refer to each other
+ */
+static struct pll * get_permutations(struct pack_list *list, int n)
+{
+	struct pll *subset, *ret = NULL, *new_pll = NULL, *pll;
+
+	if (list == NULL || pack_list_size(list) < n || n == 0)
+		return NULL;
+
+	if (n == 1) {
+		while (list) {
+			new_pll = xmalloc(sizeof(pll));
+			new_pll->pl = NULL;
+			pack_list_insert(&new_pll->pl, list);
+			new_pll->next = ret;
+			ret = new_pll;
+			list = list->next;
+		}
+		return ret;
+	}
+
+	while (list->next) {
+		subset = get_permutations(list->next, n - 1);
+		while (subset) {
+			new_pll = xmalloc(sizeof(pll));
+			new_pll->pl = subset->pl;
+			pack_list_insert(&new_pll->pl, list);
+			new_pll->next = ret;
+			ret = new_pll;
+			subset = subset->next;
+		}
+		list = list->next;
+	}
+	return ret;
+}
+
+static int is_superset(struct pack_list *pl, struct llist *list)
+{
+	struct llist *diff;
+
+	diff = llist_copy(list);
+
+	while (pl) {
+		llist_sorted_difference_inplace(diff, pl->all_objects);
+		if (diff->size == 0) { /* we're done */
+			llist_free(diff);
+			return 1;
+		}
+		pl = pl->next;
+	}
+	llist_free(diff);
+	return 0;
+}
+
+static size_t sizeof_union(struct packed_git *p1, struct packed_git *p2)
+{
+	size_t ret = 0;
+	unsigned long p1_off = 0, p2_off = 0, p1_step, p2_step;
+	const unsigned char *p1_base, *p2_base;
+
+	p1_base = p1->index_data;
+	p2_base = p2->index_data;
+	p1_base += 256 * 4 + ((p1->index_version < 2) ? 4 : 8);
+	p2_base += 256 * 4 + ((p2->index_version < 2) ? 4 : 8);
+	p1_step = (p1->index_version < 2) ? 24 : 20;
+	p2_step = (p2->index_version < 2) ? 24 : 20;
+
+	while (p1_off < p1->num_objects * p1_step &&
+	       p2_off < p2->num_objects * p2_step)
+	{
+		int cmp = hashcmp(p1_base + p1_off, p2_base + p2_off);
+		/* cmp ~ p1 - p2 */
+		if (cmp == 0) {
+			ret++;
+			p1_off += p1_step;
+			p2_off += p2_step;
+			continue;
+		}
+		if (cmp < 0) { /* p1 has the object, p2 doesn't */
+			p1_off += p1_step;
+		} else { /* p2 has the object, p1 doesn't */
+			p2_off += p2_step;
+		}
+	}
+	return ret;
+}
+
+/* another O(n^2) function ... */
+static size_t get_pack_redundancy(struct pack_list *pl)
+{
+	struct pack_list *subset;
+	size_t ret = 0;
+
+	if (pl == NULL)
+		return 0;
+
+	while ((subset = pl->next)) {
+		while (subset) {
+			ret += sizeof_union(pl->pack, subset->pack);
+			subset = subset->next;
+		}
+		pl = pl->next;
+	}
+	return ret;
+}
+
+static inline off_t pack_set_bytecount(struct pack_list *pl)
+{
+	off_t ret = 0;
+	while (pl) {
+		ret += pl->pack->pack_size;
+		ret += pl->pack->index_size;
+		pl = pl->next;
+	}
+	return ret;
+}
+
+static void minimize(struct pack_list **min)
+{
+	struct pack_list *pl, *unique = NULL,
+		*non_unique = NULL, *min_perm = NULL;
+	struct pll *perm, *perm_all, *perm_ok = NULL, *new_perm;
+	struct llist *missing;
+	off_t min_perm_size = 0, perm_size;
+	int n;
+
+	pl = local_packs;
+	while (pl) {
+		if (pl->unique_objects->size)
+			pack_list_insert(&unique, pl);
+		else
+			pack_list_insert(&non_unique, pl);
+		pl = pl->next;
+	}
+	/* find out which objects are missing from the set of unique packs */
+	missing = llist_copy(all_objects);
+	pl = unique;
+	while (pl) {
+		llist_sorted_difference_inplace(missing, pl->all_objects);
+		pl = pl->next;
+	}
+
+	/* return if there are no objects missing from the unique set */
+	if (missing->size == 0) {
+		*min = unique;
+		return;
+	}
+
+	/* find the permutations which contain all missing objects */
+	for (n = 1; n <= pack_list_size(non_unique) && !perm_ok; n++) {
+		perm_all = perm = get_permutations(non_unique, n);
+		while (perm) {
+			if (is_superset(perm->pl, missing)) {
+				new_perm = xmalloc(sizeof(struct pll));
+				memcpy(new_perm, perm, sizeof(struct pll));
+				new_perm->next = perm_ok;
+				perm_ok = new_perm;
+			}
+			perm = perm->next;
+		}
+		if (perm_ok)
+			break;
+		pll_free(perm_all);
+	}
+	if (perm_ok == NULL)
+		die("Internal error: No complete sets found!");
+
+	/* find the permutation with the smallest size */
+	perm = perm_ok;
+	while (perm) {
+		perm_size = pack_set_bytecount(perm->pl);
+		if (!min_perm_size || min_perm_size > perm_size) {
+			min_perm_size = perm_size;
+			min_perm = perm->pl;
+		}
+		perm = perm->next;
+	}
+	*min = min_perm;
+	/* add the unique packs to the list */
+	pl = unique;
+	while (pl) {
+		pack_list_insert(min, pl);
+		pl = pl->next;
+	}
+}
+
+static void load_all_objects(void)
+{
+	struct pack_list *pl = local_packs;
+	struct llist_item *hint, *l;
+
+	llist_init(&all_objects);
+
+	while (pl) {
+		hint = NULL;
+		l = pl->all_objects->front;
+		while (l) {
+			hint = llist_insert_sorted_unique(all_objects,
+							  l->sha1, hint);
+			l = l->next;
+		}
+		pl = pl->next;
+	}
+	/* remove objects present in remote packs */
+	pl = altodb_packs;
+	while (pl) {
+		llist_sorted_difference_inplace(all_objects, pl->all_objects);
+		pl = pl->next;
+	}
+}
+
+/* this scales like O(n^2) */
+static void cmp_local_packs(void)
+{
+	struct pack_list *subset, *pl = local_packs;
+
+	while ((subset = pl)) {
+		while ((subset = subset->next))
+			cmp_two_packs(pl, subset);
+		pl = pl->next;
+	}
+}
+
+static void scan_alt_odb_packs(void)
+{
+	struct pack_list *local, *alt;
+
+	alt = altodb_packs;
+	while (alt) {
+		local = local_packs;
+		while (local) {
+			llist_sorted_difference_inplace(local->unique_objects,
+							alt->all_objects);
+			local = local->next;
+		}
+		llist_sorted_difference_inplace(all_objects, alt->all_objects);
+		alt = alt->next;
+	}
+}
+
+static struct pack_list * add_pack(struct packed_git *p)
+{
+	struct pack_list l;
+	unsigned long off = 0, step;
+	const unsigned char *base;
+
+	if (!p->pack_local && !(alt_odb || verbose))
+		return NULL;
+
+	l.pack = p;
+	llist_init(&l.all_objects);
+
+	if (open_pack_index(p))
+		return NULL;
+
+	base = p->index_data;
+	base += 256 * 4 + ((p->index_version < 2) ? 4 : 8);
+	step = (p->index_version < 2) ? 24 : 20;
+	while (off < p->num_objects * step) {
+		llist_insert_back(l.all_objects, base + off);
+		off += step;
+	}
+	/* this list will be pruned in cmp_two_packs later */
+	l.unique_objects = llist_copy(l.all_objects);
+	if (p->pack_local)
+		return pack_list_insert(&local_packs, &l);
+	else
+		return pack_list_insert(&altodb_packs, &l);
+}
+
+static struct pack_list * add_pack_file(const char *filename)
+{
+	struct packed_git *p = packed_git;
+
+	if (strlen(filename) < 40)
+		die("Bad pack filename: %s", filename);
+
+	while (p) {
+		if (strstr(p->pack_name, filename))
+			return add_pack(p);
+		p = p->next;
+	}
+	die("Filename %s not found in packed_git", filename);
+}
+
+static void load_all(void)
+{
+	struct packed_git *p = packed_git;
+
+	while (p) {
+		add_pack(p);
+		p = p->next;
+	}
+}
+
+int cmd_pack_redundant(int argc, const char **argv, const char *prefix)
+{
+	int i;
+	struct pack_list *min, *red, *pl;
+	struct llist *ignore;
+	unsigned char *sha1;
+	char buf[42]; /* 40 byte sha1 + \n + \0 */
+
+	if (argc == 2 && !strcmp(argv[1], "-h"))
+		usage(pack_redundant_usage);
+
+	for (i = 1; i < argc; i++) {
+		const char *arg = argv[i];
+		if (!strcmp(arg, "--")) {
+			i++;
+			break;
+		}
+		if (!strcmp(arg, "--all")) {
+			load_all_packs = 1;
+			continue;
+		}
+		if (!strcmp(arg, "--verbose")) {
+			verbose = 1;
+			continue;
+		}
+		if (!strcmp(arg, "--alt-odb")) {
+			alt_odb = 1;
+			continue;
+		}
+		if (*arg == '-')
+			usage(pack_redundant_usage);
+		else
+			break;
+	}
+
+	prepare_packed_git();
+
+	if (load_all_packs)
+		load_all();
+	else
+		while (*(argv + i) != NULL)
+			add_pack_file(*(argv + i++));
+
+	if (local_packs == NULL)
+		die("Zero packs found!");
+
+	load_all_objects();
+
+	cmp_local_packs();
+	if (alt_odb)
+		scan_alt_odb_packs();
+
+	/* ignore objects given on stdin */
+	llist_init(&ignore);
+	if (!isatty(0)) {
+		while (fgets(buf, sizeof(buf), stdin)) {
+			sha1 = xmalloc(20);
+			if (get_sha1_hex(buf, sha1))
+				die("Bad sha1 on stdin: %s", buf);
+			llist_insert_sorted_unique(ignore, sha1, NULL);
+		}
+	}
+	llist_sorted_difference_inplace(all_objects, ignore);
+	pl = local_packs;
+	while (pl) {
+		llist_sorted_difference_inplace(pl->unique_objects, ignore);
+		pl = pl->next;
+	}
+
+	minimize(&min);
+
+	if (verbose) {
+		fprintf(stderr, "There are %lu packs available in alt-odbs.\n",
+			(unsigned long)pack_list_size(altodb_packs));
+		fprintf(stderr, "The smallest (bytewise) set of packs is:\n");
+		pl = min;
+		while (pl) {
+			fprintf(stderr, "\t%s\n", pl->pack->pack_name);
+			pl = pl->next;
+		}
+		fprintf(stderr, "containing %lu duplicate objects "
+				"with a total size of %lukb.\n",
+			(unsigned long)get_pack_redundancy(min),
+			(unsigned long)pack_set_bytecount(min)/1024);
+		fprintf(stderr, "A total of %lu unique objects were considered.\n",
+			(unsigned long)all_objects->size);
+		fprintf(stderr, "Redundant packs (with indexes):\n");
+	}
+	pl = red = pack_list_difference(local_packs, min);
+	while (pl) {
+		printf("%s\n%s\n",
+		       sha1_pack_index_name(pl->pack->sha1),
+		       pl->pack->pack_name);
+		pl = pl->next;
+	}
+	if (verbose)
+		fprintf(stderr, "%luMB of redundant packs in total.\n",
+			(unsigned long)pack_set_bytecount(red)/(1024*1024));
+
+	return 0;
+}
diff --git a/builtin/pack-refs.c b/builtin/pack-refs.c
new file mode 100644
index 0000000..39a9d89
--- /dev/null
+++ b/builtin/pack-refs.c
@@ -0,0 +1,21 @@
+#include "builtin.h"
+#include "parse-options.h"
+#include "pack-refs.h"
+
+static char const * const pack_refs_usage[] = {
+	"git pack-refs [options]",
+	NULL
+};
+
+int cmd_pack_refs(int argc, const char **argv, const char *prefix)
+{
+	unsigned int flags = PACK_REFS_PRUNE;
+	struct option opts[] = {
+		OPT_BIT(0, "all",   &flags, "pack everything", PACK_REFS_ALL),
+		OPT_BIT(0, "prune", &flags, "prune loose refs (default)", PACK_REFS_PRUNE),
+		OPT_END(),
+	};
+	if (parse_options(argc, argv, prefix, opts, pack_refs_usage, 0))
+		usage_with_options(pack_refs_usage, opts);
+	return pack_refs(flags);
+}
diff --git a/builtin/patch-id.c b/builtin/patch-id.c
new file mode 100644
index 0000000..3cfe02d
--- /dev/null
+++ b/builtin/patch-id.c
@@ -0,0 +1,157 @@
+#include "builtin.h"
+
+static void flush_current_id(int patchlen, unsigned char *id, git_SHA_CTX *c)
+{
+	unsigned char result[20];
+	char name[50];
+
+	if (!patchlen)
+		return;
+
+	git_SHA1_Final(result, c);
+	memcpy(name, sha1_to_hex(id), 41);
+	printf("%s %s\n", sha1_to_hex(result), name);
+	git_SHA1_Init(c);
+}
+
+static int remove_space(char *line)
+{
+	char *src = line;
+	char *dst = line;
+	unsigned char c;
+
+	while ((c = *src++) != '\0') {
+		if (!isspace(c))
+			*dst++ = c;
+	}
+	return dst - line;
+}
+
+static int scan_hunk_header(const char *p, int *p_before, int *p_after)
+{
+	static const char digits[] = "0123456789";
+	const char *q, *r;
+	int n;
+
+	q = p + 4;
+	n = strspn(q, digits);
+	if (q[n] == ',') {
+		q += n + 1;
+		n = strspn(q, digits);
+	}
+	if (n == 0 || q[n] != ' ' || q[n+1] != '+')
+		return 0;
+
+	r = q + n + 2;
+	n = strspn(r, digits);
+	if (r[n] == ',') {
+		r += n + 1;
+		n = strspn(r, digits);
+	}
+	if (n == 0)
+		return 0;
+
+	*p_before = atoi(q);
+	*p_after = atoi(r);
+	return 1;
+}
+
+static int get_one_patchid(unsigned char *next_sha1, git_SHA_CTX *ctx, struct strbuf *line_buf)
+{
+	int patchlen = 0, found_next = 0;
+	int before = -1, after = -1;
+
+	while (strbuf_getwholeline(line_buf, stdin, '\n') != EOF) {
+		char *line = line_buf->buf;
+		char *p = line;
+		int len;
+
+		if (!memcmp(line, "diff-tree ", 10))
+			p += 10;
+		else if (!memcmp(line, "commit ", 7))
+			p += 7;
+		else if (!memcmp(line, "From ", 5))
+			p += 5;
+		else if (!memcmp(line, "\\ ", 2) && 12 < strlen(line))
+			continue;
+
+		if (!get_sha1_hex(p, next_sha1)) {
+			found_next = 1;
+			break;
+		}
+
+		/* Ignore commit comments */
+		if (!patchlen && memcmp(line, "diff ", 5))
+			continue;
+
+		/* Parsing diff header?  */
+		if (before == -1) {
+			if (!memcmp(line, "index ", 6))
+				continue;
+			else if (!memcmp(line, "--- ", 4))
+				before = after = 1;
+			else if (!isalpha(line[0]))
+				break;
+		}
+
+		/* Looking for a valid hunk header?  */
+		if (before == 0 && after == 0) {
+			if (!memcmp(line, "@@ -", 4)) {
+				/* Parse next hunk, but ignore line numbers.  */
+				scan_hunk_header(line, &before, &after);
+				continue;
+			}
+
+			/* Split at the end of the patch.  */
+			if (memcmp(line, "diff ", 5))
+				break;
+
+			/* Else we're parsing another header.  */
+			before = after = -1;
+		}
+
+		/* If we get here, we're inside a hunk.  */
+		if (line[0] == '-' || line[0] == ' ')
+			before--;
+		if (line[0] == '+' || line[0] == ' ')
+			after--;
+
+		/* Compute the sha without whitespace */
+		len = remove_space(line);
+		patchlen += len;
+		git_SHA1_Update(ctx, line, len);
+	}
+
+	if (!found_next)
+		hashclr(next_sha1);
+
+	return patchlen;
+}
+
+static void generate_id_list(void)
+{
+	unsigned char sha1[20], n[20];
+	git_SHA_CTX ctx;
+	int patchlen;
+	struct strbuf line_buf = STRBUF_INIT;
+
+	git_SHA1_Init(&ctx);
+	hashclr(sha1);
+	while (!feof(stdin)) {
+		patchlen = get_one_patchid(n, &ctx, &line_buf);
+		flush_current_id(patchlen, sha1, &ctx);
+		hashcpy(sha1, n);
+	}
+	strbuf_release(&line_buf);
+}
+
+static const char patch_id_usage[] = "git patch-id < patch";
+
+int cmd_patch_id(int argc, const char **argv, const char *prefix)
+{
+	if (argc != 1)
+		usage(patch_id_usage);
+
+	generate_id_list();
+	return 0;
+}
diff --git a/builtin/prune-packed.c b/builtin/prune-packed.c
new file mode 100644
index 0000000..b58a2e1
--- /dev/null
+++ b/builtin/prune-packed.c
@@ -0,0 +1,86 @@
+#include "builtin.h"
+#include "cache.h"
+#include "progress.h"
+#include "parse-options.h"
+
+static const char * const prune_packed_usage[] = {
+	"git prune-packed [-n|--dry-run] [-q|--quiet]",
+	NULL
+};
+
+#define DRY_RUN 01
+#define VERBOSE 02
+
+static struct progress *progress;
+
+static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts)
+{
+	struct dirent *de;
+	char hex[40];
+
+	sprintf(hex, "%02x", i);
+	while ((de = readdir(dir)) != NULL) {
+		unsigned char sha1[20];
+		if (strlen(de->d_name) != 38)
+			continue;
+		memcpy(hex+2, de->d_name, 38);
+		if (get_sha1_hex(hex, sha1))
+			continue;
+		if (!has_sha1_pack(sha1))
+			continue;
+		memcpy(pathname + len, de->d_name, 38);
+		if (opts & DRY_RUN)
+			printf("rm -f %s\n", pathname);
+		else
+			unlink_or_warn(pathname);
+		display_progress(progress, i + 1);
+	}
+}
+
+void prune_packed_objects(int opts)
+{
+	int i;
+	static char pathname[PATH_MAX];
+	const char *dir = get_object_directory();
+	int len = strlen(dir);
+
+	if (opts == VERBOSE)
+		progress = start_progress_delay("Removing duplicate objects",
+			256, 95, 2);
+
+	if (len > PATH_MAX - 42)
+		die("impossible object directory");
+	memcpy(pathname, dir, len);
+	if (len && pathname[len-1] != '/')
+		pathname[len++] = '/';
+	for (i = 0; i < 256; i++) {
+		DIR *d;
+
+		display_progress(progress, i + 1);
+		sprintf(pathname + len, "%02x/", i);
+		d = opendir(pathname);
+		if (!d)
+			continue;
+		prune_dir(i, d, pathname, len + 3, opts);
+		closedir(d);
+		pathname[len + 2] = '\0';
+		rmdir(pathname);
+	}
+	stop_progress(&progress);
+}
+
+int cmd_prune_packed(int argc, const char **argv, const char *prefix)
+{
+	int opts = isatty(2) ? VERBOSE : 0;
+	const struct option prune_packed_options[] = {
+		OPT_BIT('n', "dry-run", &opts, "dry run", DRY_RUN),
+		OPT_NEGBIT('q', "quiet", &opts, "be quiet", VERBOSE),
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, prefix, prune_packed_options,
+			     prune_packed_usage, 0);
+
+	prune_packed_objects(opts);
+	return 0;
+}
diff --git a/builtin/prune.c b/builtin/prune.c
new file mode 100644
index 0000000..b99b635
--- /dev/null
+++ b/builtin/prune.c
@@ -0,0 +1,175 @@
+#include "cache.h"
+#include "commit.h"
+#include "diff.h"
+#include "revision.h"
+#include "builtin.h"
+#include "reachable.h"
+#include "parse-options.h"
+#include "progress.h"
+#include "dir.h"
+
+static const char * const prune_usage[] = {
+	"git prune [-n] [-v] [--expire <time>] [--] [<head>...]",
+	NULL
+};
+static int show_only;
+static int verbose;
+static unsigned long expire;
+static int show_progress = -1;
+
+static int prune_tmp_object(const char *path, const char *filename)
+{
+	const char *fullpath = mkpath("%s/%s", path, filename);
+	struct stat st;
+	if (lstat(fullpath, &st))
+		return error("Could not stat '%s'", fullpath);
+	if (st.st_mtime > expire)
+		return 0;
+	printf("Removing stale temporary file %s\n", fullpath);
+	if (!show_only)
+		unlink_or_warn(fullpath);
+	return 0;
+}
+
+static int prune_object(char *path, const char *filename, const unsigned char *sha1)
+{
+	const char *fullpath = mkpath("%s/%s", path, filename);
+	struct stat st;
+	if (lstat(fullpath, &st))
+		return error("Could not stat '%s'", fullpath);
+	if (st.st_mtime > expire)
+		return 0;
+	if (show_only || verbose) {
+		enum object_type type = sha1_object_info(sha1, NULL);
+		printf("%s %s\n", sha1_to_hex(sha1),
+		       (type > 0) ? typename(type) : "unknown");
+	}
+	if (!show_only)
+		unlink_or_warn(fullpath);
+	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];
+
+		if (is_dot_or_dotdot(de->d_name))
+			continue;
+		if (strlen(de->d_name) == 38) {
+			sprintf(name, "%02x", i);
+			memcpy(name+2, de->d_name, 39);
+			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;
+		}
+		if (!prefixcmp(de->d_name, "tmp_obj_")) {
+			prune_tmp_object(path, de->d_name);
+			continue;
+		}
+		fprintf(stderr, "bad sha1 file: %s/%s\n", path, de->d_name);
+	}
+	closedir(dir);
+	if (!show_only)
+		rmdir(path);
+	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);
+	}
+}
+
+/*
+ * Write errors (particularly out of space) can result in
+ * failed temporary packs (and more rarely indexes and other
+ * files beginning with "tmp_") accumulating in the object
+ * and the pack directories.
+ */
+static void remove_temporary_files(const char *path)
+{
+	DIR *dir;
+	struct dirent *de;
+
+	dir = opendir(path);
+	if (!dir) {
+		fprintf(stderr, "Unable to open directory %s\n", path);
+		return;
+	}
+	while ((de = readdir(dir)) != NULL)
+		if (!prefixcmp(de->d_name, "tmp_"))
+			prune_tmp_object(path, de->d_name);
+	closedir(dir);
+}
+
+int cmd_prune(int argc, const char **argv, const char *prefix)
+{
+	struct rev_info revs;
+	struct progress *progress = NULL;
+	const struct option options[] = {
+		OPT__DRY_RUN(&show_only, "do not remove, show only"),
+		OPT__VERBOSE(&verbose, "report pruned objects"),
+		OPT_BOOL(0, "progress", &show_progress, "show progress"),
+		OPT_DATE(0, "expire", &expire,
+			 "expire objects older than <time>"),
+		OPT_END()
+	};
+	char *s;
+
+	expire = ULONG_MAX;
+	save_commit_buffer = 0;
+	read_replace_refs = 0;
+	init_revisions(&revs, prefix);
+
+	argc = parse_options(argc, argv, prefix, options, prune_usage, 0);
+	while (argc--) {
+		unsigned char sha1[20];
+		const char *name = *argv++;
+
+		if (!get_sha1(name, sha1)) {
+			struct object *object = parse_object(sha1);
+			if (!object)
+				die("bad object: %s", name);
+			add_pending_object(&revs, object, "");
+		}
+		else
+			die("unrecognized argument: %s", name);
+	}
+
+	if (show_progress == -1)
+		show_progress = isatty(2);
+	if (show_progress)
+		progress = start_progress_delay("Checking connectivity", 0, 0, 2);
+
+	mark_reachable_objects(&revs, 1, progress);
+	stop_progress(&progress);
+	prune_object_dir(get_object_directory());
+
+	prune_packed_objects(show_only);
+	remove_temporary_files(get_object_directory());
+	s = xstrdup(mkpath("%s/pack", get_object_directory()));
+	remove_temporary_files(s);
+	free(s);
+	return 0;
+}
diff --git a/builtin/push.c b/builtin/push.c
new file mode 100644
index 0000000..fdfcc6c
--- /dev/null
+++ b/builtin/push.c
@@ -0,0 +1,402 @@
+/*
+ * "git push"
+ */
+#include "cache.h"
+#include "refs.h"
+#include "run-command.h"
+#include "builtin.h"
+#include "remote.h"
+#include "transport.h"
+#include "parse-options.h"
+#include "submodule.h"
+
+static const char * const push_usage[] = {
+	"git push [<options>] [<repository> [<refspec>...]]",
+	NULL,
+};
+
+static int thin;
+static int deleterefs;
+static const char *receivepack;
+static int verbosity;
+static int progress = -1;
+
+static const char **refspec;
+static int refspec_nr;
+static int refspec_alloc;
+static int default_matching_used;
+
+static void add_refspec(const char *ref)
+{
+	refspec_nr++;
+	ALLOC_GROW(refspec, refspec_nr, refspec_alloc);
+	refspec[refspec_nr-1] = ref;
+}
+
+static void set_refspecs(const char **refs, int nr)
+{
+	int i;
+	for (i = 0; i < nr; i++) {
+		const char *ref = refs[i];
+		if (!strcmp("tag", ref)) {
+			char *tag;
+			int len;
+			if (nr <= ++i)
+				die(_("tag shorthand without <tag>"));
+			len = strlen(refs[i]) + 11;
+			if (deleterefs) {
+				tag = xmalloc(len+1);
+				strcpy(tag, ":refs/tags/");
+			} else {
+				tag = xmalloc(len);
+				strcpy(tag, "refs/tags/");
+			}
+			strcat(tag, refs[i]);
+			ref = tag;
+		} else if (deleterefs && !strchr(ref, ':')) {
+			char *delref;
+			int len = strlen(ref)+1;
+			delref = xmalloc(len+1);
+			strcpy(delref, ":");
+			strcat(delref, ref);
+			ref = delref;
+		} else if (deleterefs)
+			die(_("--delete only accepts plain target ref names"));
+		add_refspec(ref);
+	}
+}
+
+static int push_url_of_remote(struct remote *remote, const char ***url_p)
+{
+	if (remote->pushurl_nr) {
+		*url_p = remote->pushurl;
+		return remote->pushurl_nr;
+	}
+	*url_p = remote->url;
+	return remote->url_nr;
+}
+
+static NORETURN int die_push_simple(struct branch *branch, struct remote *remote) {
+	/*
+	 * There's no point in using shorten_unambiguous_ref here,
+	 * as the ambiguity would be on the remote side, not what
+	 * we have locally. Plus, this is supposed to be the simple
+	 * mode. If the user is doing something crazy like setting
+	 * upstream to a non-branch, we should probably be showing
+	 * them the big ugly fully qualified ref.
+	 */
+	const char *advice_maybe = "";
+	const char *short_upstream =
+		skip_prefix(branch->merge[0]->src, "refs/heads/");
+
+	if (!short_upstream)
+		short_upstream = branch->merge[0]->src;
+	/*
+	 * Don't show advice for people who explicitely set
+	 * push.default.
+	 */
+	if (push_default == PUSH_DEFAULT_UNSPECIFIED)
+		advice_maybe = _("\n"
+				 "To choose either option permanently, "
+				 "see push.default in 'git help config'.");
+	die(_("The upstream branch of your current branch does not match\n"
+	      "the name of your current branch.  To push to the upstream branch\n"
+	      "on the remote, use\n"
+	      "\n"
+	      "    git push %s HEAD:%s\n"
+	      "\n"
+	      "To push to the branch of the same name on the remote, use\n"
+	      "\n"
+	      "    git push %s %s\n"
+	      "%s"),
+	    remote->name, short_upstream,
+	    remote->name, branch->name, advice_maybe);
+}
+
+static void setup_push_upstream(struct remote *remote, int simple)
+{
+	struct strbuf refspec = STRBUF_INIT;
+	struct branch *branch = branch_get(NULL);
+	if (!branch)
+		die(_("You are not currently on a branch.\n"
+		    "To push the history leading to the current (detached HEAD)\n"
+		    "state now, use\n"
+		    "\n"
+		    "    git push %s HEAD:<name-of-remote-branch>\n"),
+		    remote->name);
+	if (!branch->merge_nr || !branch->merge || !branch->remote_name)
+		die(_("The current branch %s has no upstream branch.\n"
+		    "To push the current branch and set the remote as upstream, use\n"
+		    "\n"
+		    "    git push --set-upstream %s %s\n"),
+		    branch->name,
+		    remote->name,
+		    branch->name);
+	if (branch->merge_nr != 1)
+		die(_("The current branch %s has multiple upstream branches, "
+		    "refusing to push."), branch->name);
+	if (strcmp(branch->remote_name, remote->name))
+		die(_("You are pushing to remote '%s', which is not the upstream of\n"
+		      "your current branch '%s', without telling me what to push\n"
+		      "to update which remote branch."),
+		    remote->name, branch->name);
+	if (simple && strcmp(branch->refname, branch->merge[0]->src))
+		die_push_simple(branch, remote);
+
+	strbuf_addf(&refspec, "%s:%s", branch->name, branch->merge[0]->src);
+	add_refspec(refspec.buf);
+}
+
+static void setup_default_push_refspecs(struct remote *remote)
+{
+	switch (push_default) {
+	default:
+	case PUSH_DEFAULT_UNSPECIFIED:
+		default_matching_used = 1;
+		/* fallthru */
+	case PUSH_DEFAULT_MATCHING:
+		add_refspec(":");
+		break;
+
+	case PUSH_DEFAULT_SIMPLE:
+		setup_push_upstream(remote, 1);
+		break;
+
+	case PUSH_DEFAULT_UPSTREAM:
+		setup_push_upstream(remote, 0);
+		break;
+
+	case PUSH_DEFAULT_CURRENT:
+		add_refspec("HEAD");
+		break;
+
+	case PUSH_DEFAULT_NOTHING:
+		die(_("You didn't specify any refspecs to push, and "
+		    "push.default is \"nothing\"."));
+		break;
+	}
+}
+
+static const char message_advice_pull_before_push[] =
+	N_("Updates were rejected because the tip of your current branch is behind\n"
+	   "its remote counterpart. Merge the remote changes (e.g. 'git pull')\n"
+	   "before pushing again.\n"
+	   "See the 'Note about fast-forwards' in 'git push --help' for details.");
+
+static const char message_advice_use_upstream[] =
+	N_("Updates were rejected because a pushed branch tip is behind its remote\n"
+	   "counterpart. If you did not intend to push that branch, you may want to\n"
+	   "specify branches to push or set the 'push.default' configuration\n"
+	   "variable to 'current' or 'upstream' to push only the current branch.");
+
+static const char message_advice_checkout_pull_push[] =
+	N_("Updates were rejected because a pushed branch tip is behind its remote\n"
+	   "counterpart. Check out this branch and merge the remote changes\n"
+	   "(e.g. 'git pull') before pushing again.\n"
+	   "See the 'Note about fast-forwards' in 'git push --help' for details.");
+
+static void advise_pull_before_push(void)
+{
+	if (!advice_push_non_ff_current || !advice_push_nonfastforward)
+		return;
+	advise(_(message_advice_pull_before_push));
+}
+
+static void advise_use_upstream(void)
+{
+	if (!advice_push_non_ff_default || !advice_push_nonfastforward)
+		return;
+	advise(_(message_advice_use_upstream));
+}
+
+static void advise_checkout_pull_push(void)
+{
+	if (!advice_push_non_ff_matching || !advice_push_nonfastforward)
+		return;
+	advise(_(message_advice_checkout_pull_push));
+}
+
+static int push_with_options(struct transport *transport, int flags)
+{
+	int err;
+	int nonfastforward;
+
+	transport_set_verbosity(transport, verbosity, progress);
+
+	if (receivepack)
+		transport_set_option(transport,
+				     TRANS_OPT_RECEIVEPACK, receivepack);
+	if (thin)
+		transport_set_option(transport, TRANS_OPT_THIN, "yes");
+
+	if (verbosity > 0)
+		fprintf(stderr, _("Pushing to %s\n"), transport->url);
+	err = transport_push(transport, refspec_nr, refspec, flags,
+			     &nonfastforward);
+	if (err != 0)
+		error(_("failed to push some refs to '%s'"), transport->url);
+
+	err |= transport_disconnect(transport);
+	if (!err)
+		return 0;
+
+	switch (nonfastforward) {
+	default:
+		break;
+	case NON_FF_HEAD:
+		advise_pull_before_push();
+		break;
+	case NON_FF_OTHER:
+		if (default_matching_used)
+			advise_use_upstream();
+		else
+			advise_checkout_pull_push();
+		break;
+	}
+
+	return 1;
+}
+
+static int do_push(const char *repo, int flags)
+{
+	int i, errs;
+	struct remote *remote = remote_get(repo);
+	const char **url;
+	int url_nr;
+
+	if (!remote) {
+		if (repo)
+			die(_("bad repository '%s'"), repo);
+		die(_("No configured push destination.\n"
+		    "Either specify the URL from the command-line or configure a remote repository using\n"
+		    "\n"
+		    "    git remote add <name> <url>\n"
+		    "\n"
+		    "and then push using the remote name\n"
+		    "\n"
+		    "    git push <name>\n"));
+	}
+
+	if (remote->mirror)
+		flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE);
+
+	if ((flags & TRANSPORT_PUSH_ALL) && refspec) {
+		if (!strcmp(*refspec, "refs/tags/*"))
+			return error(_("--all and --tags are incompatible"));
+		return error(_("--all can't be combined with refspecs"));
+	}
+
+	if ((flags & TRANSPORT_PUSH_MIRROR) && refspec) {
+		if (!strcmp(*refspec, "refs/tags/*"))
+			return error(_("--mirror and --tags are incompatible"));
+		return error(_("--mirror can't be combined with refspecs"));
+	}
+
+	if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) ==
+				(TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) {
+		return error(_("--all and --mirror are incompatible"));
+	}
+
+	if (!refspec && !(flags & TRANSPORT_PUSH_ALL)) {
+		if (remote->push_refspec_nr) {
+			refspec = remote->push_refspec;
+			refspec_nr = remote->push_refspec_nr;
+		} else if (!(flags & TRANSPORT_PUSH_MIRROR))
+			setup_default_push_refspecs(remote);
+	}
+	errs = 0;
+	url_nr = push_url_of_remote(remote, &url);
+	if (url_nr) {
+		for (i = 0; i < url_nr; i++) {
+			struct transport *transport =
+				transport_get(remote, url[i]);
+			if (push_with_options(transport, flags))
+				errs++;
+		}
+	} else {
+		struct transport *transport =
+			transport_get(remote, NULL);
+
+		if (push_with_options(transport, flags))
+			errs++;
+	}
+	return !!errs;
+}
+
+static int option_parse_recurse_submodules(const struct option *opt,
+				   const char *arg, int unset)
+{
+	int *flags = opt->value;
+
+	if (*flags & (TRANSPORT_RECURSE_SUBMODULES_CHECK |
+		      TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND))
+		die("%s can only be used once.", opt->long_name);
+
+	if (arg) {
+		if (!strcmp(arg, "check"))
+			*flags |= TRANSPORT_RECURSE_SUBMODULES_CHECK;
+		else if (!strcmp(arg, "on-demand"))
+			*flags |= TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND;
+		else
+			die("bad %s argument: %s", opt->long_name, arg);
+	} else
+		die("option %s needs an argument (check|on-demand)",
+				opt->long_name);
+
+	return 0;
+}
+
+int cmd_push(int argc, const char **argv, const char *prefix)
+{
+	int flags = 0;
+	int tags = 0;
+	int rc;
+	const char *repo = NULL;	/* default repository */
+	struct option options[] = {
+		OPT__VERBOSITY(&verbosity),
+		OPT_STRING( 0 , "repo", &repo, "repository", "repository"),
+		OPT_BIT( 0 , "all", &flags, "push all refs", TRANSPORT_PUSH_ALL),
+		OPT_BIT( 0 , "mirror", &flags, "mirror all refs",
+			    (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE)),
+		OPT_BOOLEAN( 0, "delete", &deleterefs, "delete refs"),
+		OPT_BOOLEAN( 0 , "tags", &tags, "push tags (can't be used with --all or --mirror)"),
+		OPT_BIT('n' , "dry-run", &flags, "dry run", TRANSPORT_PUSH_DRY_RUN),
+		OPT_BIT( 0,  "porcelain", &flags, "machine-readable output", TRANSPORT_PUSH_PORCELAIN),
+		OPT_BIT('f', "force", &flags, "force updates", TRANSPORT_PUSH_FORCE),
+		{ OPTION_CALLBACK, 0, "recurse-submodules", &flags, "check",
+			"controls recursive pushing of submodules",
+			PARSE_OPT_OPTARG, option_parse_recurse_submodules },
+		OPT_BOOLEAN( 0 , "thin", &thin, "use thin pack"),
+		OPT_STRING( 0 , "receive-pack", &receivepack, "receive-pack", "receive pack program"),
+		OPT_STRING( 0 , "exec", &receivepack, "receive-pack", "receive pack program"),
+		OPT_BIT('u', "set-upstream", &flags, "set upstream for git pull/status",
+			TRANSPORT_PUSH_SET_UPSTREAM),
+		OPT_BOOL(0, "progress", &progress, "force progress reporting"),
+		OPT_BIT(0, "prune", &flags, "prune locally removed refs",
+			TRANSPORT_PUSH_PRUNE),
+		OPT_END()
+	};
+
+	packet_trace_identity("push");
+	git_config(git_default_config, NULL);
+	argc = parse_options(argc, argv, prefix, options, push_usage, 0);
+
+	if (deleterefs && (tags || (flags & (TRANSPORT_PUSH_ALL | TRANSPORT_PUSH_MIRROR))))
+		die(_("--delete is incompatible with --all, --mirror and --tags"));
+	if (deleterefs && argc < 2)
+		die(_("--delete doesn't make sense without any refs"));
+
+	if (tags)
+		add_refspec("refs/tags/*");
+
+	if (argc > 0) {
+		repo = argv[0];
+		set_refspecs(argv + 1, argc - 1);
+	}
+
+	rc = do_push(repo, flags);
+	if (rc == -1)
+		usage_with_options(push_usage, options);
+	else
+		return rc;
+}
diff --git a/builtin/read-tree.c b/builtin/read-tree.c
new file mode 100644
index 0000000..df6c4c8
--- /dev/null
+++ b/builtin/read-tree.c
@@ -0,0 +1,239 @@
+/*
+ * GIT - The information manager from hell
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ */
+
+#include "cache.h"
+#include "object.h"
+#include "tree.h"
+#include "tree-walk.h"
+#include "cache-tree.h"
+#include "unpack-trees.h"
+#include "dir.h"
+#include "builtin.h"
+#include "parse-options.h"
+#include "resolve-undo.h"
+
+static int nr_trees;
+static int read_empty;
+static struct tree *trees[MAX_UNPACK_TREES];
+
+static int list_tree(unsigned char *sha1)
+{
+	struct tree *tree;
+
+	if (nr_trees >= MAX_UNPACK_TREES)
+		die("I cannot read more than %d trees", MAX_UNPACK_TREES);
+	tree = parse_tree_indirect(sha1);
+	if (!tree)
+		return -1;
+	trees[nr_trees++] = tree;
+	return 0;
+}
+
+static const char * const read_tree_usage[] = {
+	"git read-tree [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>] [-u [--exclude-per-directory=<gitignore>] | -i]] [--no-sparse-checkout] [--index-output=<file>] (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])",
+	NULL
+};
+
+static int index_output_cb(const struct option *opt, const char *arg,
+				 int unset)
+{
+	set_alternate_index_output(arg);
+	return 0;
+}
+
+static int exclude_per_directory_cb(const struct option *opt, const char *arg,
+				    int unset)
+{
+	struct dir_struct *dir;
+	struct unpack_trees_options *opts;
+
+	opts = (struct unpack_trees_options *)opt->value;
+
+	if (opts->dir)
+		die("more than one --exclude-per-directory given.");
+
+	dir = xcalloc(1, sizeof(*opts->dir));
+	dir->flags |= DIR_SHOW_IGNORED;
+	dir->exclude_per_dir = arg;
+	opts->dir = dir;
+	/* We do not need to nor want to do read-directory
+	 * here; we are merely interested in reusing the
+	 * per directory ignore stack mechanism.
+	 */
+	return 0;
+}
+
+static void debug_stage(const char *label, struct cache_entry *ce,
+			struct unpack_trees_options *o)
+{
+	printf("%s ", label);
+	if (!ce)
+		printf("(missing)\n");
+	else if (ce == o->df_conflict_entry)
+		printf("(conflict)\n");
+	else
+		printf("%06o #%d %s %.8s\n",
+		       ce->ce_mode, ce_stage(ce), ce->name,
+		       sha1_to_hex(ce->sha1));
+}
+
+static int debug_merge(struct cache_entry **stages, struct unpack_trees_options *o)
+{
+	int i;
+
+	printf("* %d-way merge\n", o->merge_size);
+	debug_stage("index", stages[0], o);
+	for (i = 1; i <= o->merge_size; i++) {
+		char buf[24];
+		sprintf(buf, "ent#%d", i);
+		debug_stage(buf, stages[i], o);
+	}
+	return 0;
+}
+
+static struct lock_file lock_file;
+
+int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
+{
+	int i, newfd, stage = 0;
+	unsigned char sha1[20];
+	struct tree_desc t[MAX_UNPACK_TREES];
+	struct unpack_trees_options opts;
+	int prefix_set = 0;
+	const struct option read_tree_options[] = {
+		{ OPTION_CALLBACK, 0, "index-output", NULL, "file",
+		  "write resulting index to <file>",
+		  PARSE_OPT_NONEG, index_output_cb },
+		OPT_SET_INT(0, "empty", &read_empty,
+			    "only empty the index", 1),
+		OPT__VERBOSE(&opts.verbose_update, "be verbose"),
+		OPT_GROUP("Merging"),
+		OPT_SET_INT('m', NULL, &opts.merge,
+			    "perform a merge in addition to a read", 1),
+		OPT_SET_INT(0, "trivial", &opts.trivial_merges_only,
+			    "3-way merge if no file level merging required", 1),
+		OPT_SET_INT(0, "aggressive", &opts.aggressive,
+			    "3-way merge in presence of adds and removes", 1),
+		OPT_SET_INT(0, "reset", &opts.reset,
+			    "same as -m, but discard unmerged entries", 1),
+		{ OPTION_STRING, 0, "prefix", &opts.prefix, "<subdirectory>/",
+		  "read the tree into the index under <subdirectory>/",
+		  PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP },
+		OPT_SET_INT('u', NULL, &opts.update,
+			    "update working tree with merge result", 1),
+		{ OPTION_CALLBACK, 0, "exclude-per-directory", &opts,
+		  "gitignore",
+		  "allow explicitly ignored files to be overwritten",
+		  PARSE_OPT_NONEG, exclude_per_directory_cb },
+		OPT_SET_INT('i', NULL, &opts.index_only,
+			    "don't check the working tree after merging", 1),
+		OPT__DRY_RUN(&opts.dry_run, "don't update the index or the work tree"),
+		OPT_SET_INT(0, "no-sparse-checkout", &opts.skip_sparse_checkout,
+			    "skip applying sparse checkout filter", 1),
+		OPT_SET_INT(0, "debug-unpack", &opts.debug_unpack,
+			    "debug unpack-trees", 1),
+		OPT_END()
+	};
+
+	memset(&opts, 0, sizeof(opts));
+	opts.head_idx = -1;
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+
+	git_config(git_default_config, NULL);
+
+	argc = parse_options(argc, argv, unused_prefix, read_tree_options,
+			     read_tree_usage, 0);
+
+	newfd = hold_locked_index(&lock_file, 1);
+
+	prefix_set = opts.prefix ? 1 : 0;
+	if (1 < opts.merge + opts.reset + prefix_set)
+		die("Which one? -m, --reset, or --prefix?");
+
+	if (opts.reset || opts.merge || opts.prefix) {
+		if (read_cache_unmerged() && (opts.prefix || opts.merge))
+			die("You need to resolve your current index first");
+		stage = opts.merge = 1;
+	}
+	resolve_undo_clear();
+
+	for (i = 0; i < argc; i++) {
+		const char *arg = argv[i];
+
+		if (get_sha1(arg, sha1))
+			die("Not a valid object name %s", arg);
+		if (list_tree(sha1) < 0)
+			die("failed to unpack tree object %s", arg);
+		stage++;
+	}
+	if (nr_trees == 0 && !read_empty)
+		warning("read-tree: emptying the index with no arguments is deprecated; use --empty");
+	else if (nr_trees > 0 && read_empty)
+		die("passing trees as arguments contradicts --empty");
+
+	if (1 < opts.index_only + opts.update)
+		die("-u and -i at the same time makes no sense");
+	if ((opts.update||opts.index_only) && !opts.merge)
+		die("%s is meaningless without -m, --reset, or --prefix",
+		    opts.update ? "-u" : "-i");
+	if ((opts.dir && !opts.update))
+		die("--exclude-per-directory is meaningless unless -u");
+	if (opts.merge && !opts.index_only)
+		setup_work_tree();
+
+	if (opts.merge) {
+		if (stage < 2)
+			die("just how do you expect me to merge %d trees?", stage-1);
+		switch (stage - 1) {
+		case 1:
+			opts.fn = opts.prefix ? bind_merge : oneway_merge;
+			break;
+		case 2:
+			opts.fn = twoway_merge;
+			opts.initial_checkout = is_cache_unborn();
+			break;
+		case 3:
+		default:
+			opts.fn = threeway_merge;
+			break;
+		}
+
+		if (stage - 1 >= 3)
+			opts.head_idx = stage - 2;
+		else
+			opts.head_idx = 1;
+	}
+
+	if (opts.debug_unpack)
+		opts.fn = debug_merge;
+
+	cache_tree_free(&active_cache_tree);
+	for (i = 0; i < nr_trees; i++) {
+		struct tree *tree = trees[i];
+		parse_tree(tree);
+		init_tree_desc(t+i, tree->buffer, tree->size);
+	}
+	if (unpack_trees(nr_trees, t, &opts))
+		return 128;
+
+	if (opts.debug_unpack || opts.dry_run)
+		return 0; /* do not write the index out */
+
+	/*
+	 * When reading only one tree (either the most basic form,
+	 * "-m ent" or "--reset ent" form), we can obtain a fully
+	 * valid cache-tree because the index must match exactly
+	 * what came from the tree.
+	 */
+	if (nr_trees == 1 && !opts.prefix)
+		prime_cache_tree(&active_cache_tree, trees[0]);
+
+	if (write_cache(newfd, active_cache, active_nr) ||
+	    commit_locked_index(&lock_file))
+		die("unable to write new index file");
+	return 0;
+}
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
new file mode 100644
index 0000000..0afb8b2
--- /dev/null
+++ b/builtin/receive-pack.c
@@ -0,0 +1,988 @@
+#include "builtin.h"
+#include "pack.h"
+#include "refs.h"
+#include "pkt-line.h"
+#include "sideband.h"
+#include "run-command.h"
+#include "exec_cmd.h"
+#include "commit.h"
+#include "object.h"
+#include "remote.h"
+#include "transport.h"
+#include "string-list.h"
+#include "sha1-array.h"
+#include "connected.h"
+
+static const char receive_pack_usage[] = "git receive-pack <git-dir>";
+
+enum deny_action {
+	DENY_UNCONFIGURED,
+	DENY_IGNORE,
+	DENY_WARN,
+	DENY_REFUSE
+};
+
+static int deny_deletes;
+static int deny_non_fast_forwards;
+static enum deny_action deny_current_branch = DENY_UNCONFIGURED;
+static enum deny_action deny_delete_current = DENY_UNCONFIGURED;
+static int receive_fsck_objects = -1;
+static int transfer_fsck_objects = -1;
+static int receive_unpack_limit = -1;
+static int transfer_unpack_limit = -1;
+static int unpack_limit = 100;
+static int report_status;
+static int use_sideband;
+static int quiet;
+static int prefer_ofs_delta = 1;
+static int auto_update_server_info;
+static int auto_gc = 1;
+static const char *head_name;
+static void *head_name_to_free;
+static int sent_capabilities;
+
+static enum deny_action parse_deny_action(const char *var, const char *value)
+{
+	if (value) {
+		if (!strcasecmp(value, "ignore"))
+			return DENY_IGNORE;
+		if (!strcasecmp(value, "warn"))
+			return DENY_WARN;
+		if (!strcasecmp(value, "refuse"))
+			return DENY_REFUSE;
+	}
+	if (git_config_bool(var, value))
+		return DENY_REFUSE;
+	return DENY_IGNORE;
+}
+
+static int receive_pack_config(const char *var, const char *value, void *cb)
+{
+	if (strcmp(var, "receive.denydeletes") == 0) {
+		deny_deletes = git_config_bool(var, value);
+		return 0;
+	}
+
+	if (strcmp(var, "receive.denynonfastforwards") == 0) {
+		deny_non_fast_forwards = git_config_bool(var, value);
+		return 0;
+	}
+
+	if (strcmp(var, "receive.unpacklimit") == 0) {
+		receive_unpack_limit = git_config_int(var, value);
+		return 0;
+	}
+
+	if (strcmp(var, "transfer.unpacklimit") == 0) {
+		transfer_unpack_limit = git_config_int(var, value);
+		return 0;
+	}
+
+	if (strcmp(var, "receive.fsckobjects") == 0) {
+		receive_fsck_objects = git_config_bool(var, value);
+		return 0;
+	}
+
+	if (strcmp(var, "transfer.fsckobjects") == 0) {
+		transfer_fsck_objects = git_config_bool(var, value);
+		return 0;
+	}
+
+	if (!strcmp(var, "receive.denycurrentbranch")) {
+		deny_current_branch = parse_deny_action(var, value);
+		return 0;
+	}
+
+	if (strcmp(var, "receive.denydeletecurrent") == 0) {
+		deny_delete_current = parse_deny_action(var, value);
+		return 0;
+	}
+
+	if (strcmp(var, "repack.usedeltabaseoffset") == 0) {
+		prefer_ofs_delta = git_config_bool(var, value);
+		return 0;
+	}
+
+	if (strcmp(var, "receive.updateserverinfo") == 0) {
+		auto_update_server_info = git_config_bool(var, value);
+		return 0;
+	}
+
+	if (strcmp(var, "receive.autogc") == 0) {
+		auto_gc = git_config_bool(var, value);
+		return 0;
+	}
+
+	return git_default_config(var, value, cb);
+}
+
+static void show_ref(const char *path, const unsigned char *sha1)
+{
+	if (sent_capabilities)
+		packet_write(1, "%s %s\n", sha1_to_hex(sha1), path);
+	else
+		packet_write(1, "%s %s%c%s%s\n",
+			     sha1_to_hex(sha1), path, 0,
+			     " report-status delete-refs side-band-64k quiet",
+			     prefer_ofs_delta ? " ofs-delta" : "");
+	sent_capabilities = 1;
+}
+
+static int show_ref_cb(const char *path, const unsigned char *sha1, int flag, void *unused)
+{
+	path = strip_namespace(path);
+	/*
+	 * Advertise refs outside our current namespace as ".have"
+	 * refs, so that the client can use them to minimize data
+	 * transfer but will otherwise ignore them. This happens to
+	 * cover ".have" that are thrown in by add_one_alternate_ref()
+	 * to mark histories that are complete in our alternates as
+	 * well.
+	 */
+	if (!path)
+		path = ".have";
+	show_ref(path, sha1);
+	return 0;
+}
+
+static void show_one_alternate_sha1(const unsigned char sha1[20], void *unused)
+{
+	show_ref(".have", sha1);
+}
+
+static void collect_one_alternate_ref(const struct ref *ref, void *data)
+{
+	struct sha1_array *sa = data;
+	sha1_array_append(sa, ref->old_sha1);
+}
+
+static void write_head_info(void)
+{
+	struct sha1_array sa = SHA1_ARRAY_INIT;
+	for_each_alternate_ref(collect_one_alternate_ref, &sa);
+	sha1_array_for_each_unique(&sa, show_one_alternate_sha1, NULL);
+	sha1_array_clear(&sa);
+	for_each_ref(show_ref_cb, NULL);
+	if (!sent_capabilities)
+		show_ref("capabilities^{}", null_sha1);
+
+	/* EOF */
+	packet_flush(1);
+}
+
+struct command {
+	struct command *next;
+	const char *error_string;
+	unsigned int skip_update:1,
+		     did_not_exist:1;
+	unsigned char old_sha1[20];
+	unsigned char new_sha1[20];
+	char ref_name[FLEX_ARRAY]; /* more */
+};
+
+static const char pre_receive_hook[] = "hooks/pre-receive";
+static const char post_receive_hook[] = "hooks/post-receive";
+
+static void rp_error(const char *err, ...) __attribute__((format (printf, 1, 2)));
+static void rp_warning(const char *err, ...) __attribute__((format (printf, 1, 2)));
+
+static void report_message(const char *prefix, const char *err, va_list params)
+{
+	int sz = strlen(prefix);
+	char msg[4096];
+
+	strncpy(msg, prefix, sz);
+	sz += vsnprintf(msg + sz, sizeof(msg) - sz, err, params);
+	if (sz > (sizeof(msg) - 1))
+		sz = sizeof(msg) - 1;
+	msg[sz++] = '\n';
+
+	if (use_sideband)
+		send_sideband(1, 2, msg, sz, use_sideband);
+	else
+		xwrite(2, msg, sz);
+}
+
+static void rp_warning(const char *err, ...)
+{
+	va_list params;
+	va_start(params, err);
+	report_message("warning: ", err, params);
+	va_end(params);
+}
+
+static void rp_error(const char *err, ...)
+{
+	va_list params;
+	va_start(params, err);
+	report_message("error: ", err, params);
+	va_end(params);
+}
+
+static int copy_to_sideband(int in, int out, void *arg)
+{
+	char data[128];
+	while (1) {
+		ssize_t sz = xread(in, data, sizeof(data));
+		if (sz <= 0)
+			break;
+		send_sideband(1, 2, data, sz, use_sideband);
+	}
+	close(in);
+	return 0;
+}
+
+typedef int (*feed_fn)(void *, const char **, size_t *);
+static int run_and_feed_hook(const char *hook_name, feed_fn feed, void *feed_state)
+{
+	struct child_process proc;
+	struct async muxer;
+	const char *argv[2];
+	int code;
+
+	if (access(hook_name, X_OK) < 0)
+		return 0;
+
+	argv[0] = hook_name;
+	argv[1] = NULL;
+
+	memset(&proc, 0, sizeof(proc));
+	proc.argv = argv;
+	proc.in = -1;
+	proc.stdout_to_stderr = 1;
+
+	if (use_sideband) {
+		memset(&muxer, 0, sizeof(muxer));
+		muxer.proc = copy_to_sideband;
+		muxer.in = -1;
+		code = start_async(&muxer);
+		if (code)
+			return code;
+		proc.err = muxer.in;
+	}
+
+	code = start_command(&proc);
+	if (code) {
+		if (use_sideband)
+			finish_async(&muxer);
+		return code;
+	}
+
+	while (1) {
+		const char *buf;
+		size_t n;
+		if (feed(feed_state, &buf, &n))
+			break;
+		if (write_in_full(proc.in, buf, n) != n)
+			break;
+	}
+	close(proc.in);
+	if (use_sideband)
+		finish_async(&muxer);
+	return finish_command(&proc);
+}
+
+struct receive_hook_feed_state {
+	struct command *cmd;
+	int skip_broken;
+	struct strbuf buf;
+};
+
+static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep)
+{
+	struct receive_hook_feed_state *state = state_;
+	struct command *cmd = state->cmd;
+
+	while (cmd &&
+	       state->skip_broken && (cmd->error_string || cmd->did_not_exist))
+		cmd = cmd->next;
+	if (!cmd)
+		return -1; /* EOF */
+	strbuf_reset(&state->buf);
+	strbuf_addf(&state->buf, "%s %s %s\n",
+		    sha1_to_hex(cmd->old_sha1), sha1_to_hex(cmd->new_sha1),
+		    cmd->ref_name);
+	state->cmd = cmd->next;
+	if (bufp) {
+		*bufp = state->buf.buf;
+		*sizep = state->buf.len;
+	}
+	return 0;
+}
+
+static int run_receive_hook(struct command *commands, const char *hook_name,
+			    int skip_broken)
+{
+	struct receive_hook_feed_state state;
+	int status;
+
+	strbuf_init(&state.buf, 0);
+	state.cmd = commands;
+	state.skip_broken = skip_broken;
+	if (feed_receive_hook(&state, NULL, NULL))
+		return 0;
+	state.cmd = commands;
+	status = run_and_feed_hook(hook_name, feed_receive_hook, &state);
+	strbuf_release(&state.buf);
+	return status;
+}
+
+static int run_update_hook(struct command *cmd)
+{
+	static const char update_hook[] = "hooks/update";
+	const char *argv[5];
+	struct child_process proc;
+	int code;
+
+	if (access(update_hook, X_OK) < 0)
+		return 0;
+
+	argv[0] = update_hook;
+	argv[1] = cmd->ref_name;
+	argv[2] = sha1_to_hex(cmd->old_sha1);
+	argv[3] = sha1_to_hex(cmd->new_sha1);
+	argv[4] = NULL;
+
+	memset(&proc, 0, sizeof(proc));
+	proc.no_stdin = 1;
+	proc.stdout_to_stderr = 1;
+	proc.err = use_sideband ? -1 : 0;
+	proc.argv = argv;
+
+	code = start_command(&proc);
+	if (code)
+		return code;
+	if (use_sideband)
+		copy_to_sideband(proc.err, -1, NULL);
+	return finish_command(&proc);
+}
+
+static int is_ref_checked_out(const char *ref)
+{
+	if (is_bare_repository())
+		return 0;
+
+	if (!head_name)
+		return 0;
+	return !strcmp(head_name, ref);
+}
+
+static char *refuse_unconfigured_deny_msg[] = {
+	"By default, updating the current branch in a non-bare repository",
+	"is denied, because it will make the index and work tree inconsistent",
+	"with what you pushed, and will require 'git reset --hard' to match",
+	"the work tree to HEAD.",
+	"",
+	"You can set 'receive.denyCurrentBranch' configuration variable to",
+	"'ignore' or 'warn' in the remote repository to allow pushing into",
+	"its current branch; however, this is not recommended unless you",
+	"arranged to update its work tree to match what you pushed in some",
+	"other way.",
+	"",
+	"To squelch this message and still keep the default behaviour, set",
+	"'receive.denyCurrentBranch' configuration variable to 'refuse'."
+};
+
+static void refuse_unconfigured_deny(void)
+{
+	int i;
+	for (i = 0; i < ARRAY_SIZE(refuse_unconfigured_deny_msg); i++)
+		rp_error("%s", refuse_unconfigured_deny_msg[i]);
+}
+
+static char *refuse_unconfigured_deny_delete_current_msg[] = {
+	"By default, deleting the current branch is denied, because the next",
+	"'git clone' won't result in any file checked out, causing confusion.",
+	"",
+	"You can set 'receive.denyDeleteCurrent' configuration variable to",
+	"'warn' or 'ignore' in the remote repository to allow deleting the",
+	"current branch, with or without a warning message.",
+	"",
+	"To squelch this message, you can set it to 'refuse'."
+};
+
+static void refuse_unconfigured_deny_delete_current(void)
+{
+	int i;
+	for (i = 0;
+	     i < ARRAY_SIZE(refuse_unconfigured_deny_delete_current_msg);
+	     i++)
+		rp_error("%s", refuse_unconfigured_deny_delete_current_msg[i]);
+}
+
+static const char *update(struct command *cmd)
+{
+	const char *name = cmd->ref_name;
+	struct strbuf namespaced_name_buf = STRBUF_INIT;
+	const char *namespaced_name;
+	unsigned char *old_sha1 = cmd->old_sha1;
+	unsigned char *new_sha1 = cmd->new_sha1;
+	struct ref_lock *lock;
+
+	/* only refs/... are allowed */
+	if (prefixcmp(name, "refs/") || check_refname_format(name + 5, 0)) {
+		rp_error("refusing to create funny ref '%s' remotely", name);
+		return "funny refname";
+	}
+
+	strbuf_addf(&namespaced_name_buf, "%s%s", get_git_namespace(), name);
+	namespaced_name = strbuf_detach(&namespaced_name_buf, NULL);
+
+	if (is_ref_checked_out(namespaced_name)) {
+		switch (deny_current_branch) {
+		case DENY_IGNORE:
+			break;
+		case DENY_WARN:
+			rp_warning("updating the current branch");
+			break;
+		case DENY_REFUSE:
+		case DENY_UNCONFIGURED:
+			rp_error("refusing to update checked out branch: %s", name);
+			if (deny_current_branch == DENY_UNCONFIGURED)
+				refuse_unconfigured_deny();
+			return "branch is currently checked out";
+		}
+	}
+
+	if (!is_null_sha1(new_sha1) && !has_sha1_file(new_sha1)) {
+		error("unpack should have generated %s, "
+		      "but I can't find it!", sha1_to_hex(new_sha1));
+		return "bad pack";
+	}
+
+	if (!is_null_sha1(old_sha1) && is_null_sha1(new_sha1)) {
+		if (deny_deletes && !prefixcmp(name, "refs/heads/")) {
+			rp_error("denying ref deletion for %s", name);
+			return "deletion prohibited";
+		}
+
+		if (!strcmp(namespaced_name, head_name)) {
+			switch (deny_delete_current) {
+			case DENY_IGNORE:
+				break;
+			case DENY_WARN:
+				rp_warning("deleting the current branch");
+				break;
+			case DENY_REFUSE:
+			case DENY_UNCONFIGURED:
+				if (deny_delete_current == DENY_UNCONFIGURED)
+					refuse_unconfigured_deny_delete_current();
+				rp_error("refusing to delete the current branch: %s", name);
+				return "deletion of the current branch prohibited";
+			}
+		}
+	}
+
+	if (deny_non_fast_forwards && !is_null_sha1(new_sha1) &&
+	    !is_null_sha1(old_sha1) &&
+	    !prefixcmp(name, "refs/heads/")) {
+		struct object *old_object, *new_object;
+		struct commit *old_commit, *new_commit;
+		struct commit_list *bases, *ent;
+
+		old_object = parse_object(old_sha1);
+		new_object = parse_object(new_sha1);
+
+		if (!old_object || !new_object ||
+		    old_object->type != OBJ_COMMIT ||
+		    new_object->type != OBJ_COMMIT) {
+			error("bad sha1 objects for %s", name);
+			return "bad ref";
+		}
+		old_commit = (struct commit *)old_object;
+		new_commit = (struct commit *)new_object;
+		bases = get_merge_bases(old_commit, new_commit, 1);
+		for (ent = bases; ent; ent = ent->next)
+			if (!hashcmp(old_sha1, ent->item->object.sha1))
+				break;
+		free_commit_list(bases);
+		if (!ent) {
+			rp_error("denying non-fast-forward %s"
+				 " (you should pull first)", name);
+			return "non-fast-forward";
+		}
+	}
+	if (run_update_hook(cmd)) {
+		rp_error("hook declined to update %s", name);
+		return "hook declined";
+	}
+
+	if (is_null_sha1(new_sha1)) {
+		if (!parse_object(old_sha1)) {
+			old_sha1 = NULL;
+			if (ref_exists(name)) {
+				rp_warning("Allowing deletion of corrupt ref.");
+			} else {
+				rp_warning("Deleting a non-existent ref.");
+				cmd->did_not_exist = 1;
+			}
+		}
+		if (delete_ref(namespaced_name, old_sha1, 0)) {
+			rp_error("failed to delete %s", name);
+			return "failed to delete";
+		}
+		return NULL; /* good */
+	}
+	else {
+		lock = lock_any_ref_for_update(namespaced_name, old_sha1, 0);
+		if (!lock) {
+			rp_error("failed to lock %s", name);
+			return "failed to lock";
+		}
+		if (write_ref_sha1(lock, new_sha1, "push")) {
+			return "failed to write"; /* error() already called */
+		}
+		return NULL; /* good */
+	}
+}
+
+static char update_post_hook[] = "hooks/post-update";
+
+static void run_update_post_hook(struct command *commands)
+{
+	struct command *cmd;
+	int argc;
+	const char **argv;
+	struct child_process proc;
+
+	for (argc = 0, cmd = commands; cmd; cmd = cmd->next) {
+		if (cmd->error_string || cmd->did_not_exist)
+			continue;
+		argc++;
+	}
+	if (!argc || access(update_post_hook, X_OK) < 0)
+		return;
+	argv = xmalloc(sizeof(*argv) * (2 + argc));
+	argv[0] = update_post_hook;
+
+	for (argc = 1, cmd = commands; cmd; cmd = cmd->next) {
+		char *p;
+		if (cmd->error_string || cmd->did_not_exist)
+			continue;
+		p = xmalloc(strlen(cmd->ref_name) + 1);
+		strcpy(p, cmd->ref_name);
+		argv[argc] = p;
+		argc++;
+	}
+	argv[argc] = NULL;
+
+	memset(&proc, 0, sizeof(proc));
+	proc.no_stdin = 1;
+	proc.stdout_to_stderr = 1;
+	proc.err = use_sideband ? -1 : 0;
+	proc.argv = argv;
+
+	if (!start_command(&proc)) {
+		if (use_sideband)
+			copy_to_sideband(proc.err, -1, NULL);
+		finish_command(&proc);
+	}
+}
+
+static void check_aliased_update(struct command *cmd, struct string_list *list)
+{
+	struct strbuf buf = STRBUF_INIT;
+	const char *dst_name;
+	struct string_list_item *item;
+	struct command *dst_cmd;
+	unsigned char sha1[20];
+	char cmd_oldh[41], cmd_newh[41], dst_oldh[41], dst_newh[41];
+	int flag;
+
+	strbuf_addf(&buf, "%s%s", get_git_namespace(), cmd->ref_name);
+	dst_name = resolve_ref_unsafe(buf.buf, sha1, 0, &flag);
+	strbuf_release(&buf);
+
+	if (!(flag & REF_ISSYMREF))
+		return;
+
+	dst_name = strip_namespace(dst_name);
+	if (!dst_name) {
+		rp_error("refusing update to broken symref '%s'", cmd->ref_name);
+		cmd->skip_update = 1;
+		cmd->error_string = "broken symref";
+		return;
+	}
+
+	if ((item = string_list_lookup(list, dst_name)) == NULL)
+		return;
+
+	cmd->skip_update = 1;
+
+	dst_cmd = (struct command *) item->util;
+
+	if (!hashcmp(cmd->old_sha1, dst_cmd->old_sha1) &&
+	    !hashcmp(cmd->new_sha1, dst_cmd->new_sha1))
+		return;
+
+	dst_cmd->skip_update = 1;
+
+	strcpy(cmd_oldh, find_unique_abbrev(cmd->old_sha1, DEFAULT_ABBREV));
+	strcpy(cmd_newh, find_unique_abbrev(cmd->new_sha1, DEFAULT_ABBREV));
+	strcpy(dst_oldh, find_unique_abbrev(dst_cmd->old_sha1, DEFAULT_ABBREV));
+	strcpy(dst_newh, find_unique_abbrev(dst_cmd->new_sha1, DEFAULT_ABBREV));
+	rp_error("refusing inconsistent update between symref '%s' (%s..%s) and"
+		 " its target '%s' (%s..%s)",
+		 cmd->ref_name, cmd_oldh, cmd_newh,
+		 dst_cmd->ref_name, dst_oldh, dst_newh);
+
+	cmd->error_string = dst_cmd->error_string =
+		"inconsistent aliased update";
+}
+
+static void check_aliased_updates(struct command *commands)
+{
+	struct command *cmd;
+	struct string_list ref_list = STRING_LIST_INIT_NODUP;
+
+	for (cmd = commands; cmd; cmd = cmd->next) {
+		struct string_list_item *item =
+			string_list_append(&ref_list, cmd->ref_name);
+		item->util = (void *)cmd;
+	}
+	sort_string_list(&ref_list);
+
+	for (cmd = commands; cmd; cmd = cmd->next) {
+		if (!cmd->error_string)
+			check_aliased_update(cmd, &ref_list);
+	}
+
+	string_list_clear(&ref_list, 0);
+}
+
+static int command_singleton_iterator(void *cb_data, unsigned char sha1[20])
+{
+	struct command **cmd_list = cb_data;
+	struct command *cmd = *cmd_list;
+
+	if (!cmd || is_null_sha1(cmd->new_sha1))
+		return -1; /* end of list */
+	*cmd_list = NULL; /* this returns only one */
+	hashcpy(sha1, cmd->new_sha1);
+	return 0;
+}
+
+static void set_connectivity_errors(struct command *commands)
+{
+	struct command *cmd;
+
+	for (cmd = commands; cmd; cmd = cmd->next) {
+		struct command *singleton = cmd;
+		if (!check_everything_connected(command_singleton_iterator,
+						0, &singleton))
+			continue;
+		cmd->error_string = "missing necessary objects";
+	}
+}
+
+static int iterate_receive_command_list(void *cb_data, unsigned char sha1[20])
+{
+	struct command **cmd_list = cb_data;
+	struct command *cmd = *cmd_list;
+
+	while (cmd) {
+		if (!is_null_sha1(cmd->new_sha1)) {
+			hashcpy(sha1, cmd->new_sha1);
+			*cmd_list = cmd->next;
+			return 0;
+		}
+		cmd = cmd->next;
+	}
+	*cmd_list = NULL;
+	return -1; /* end of list */
+}
+
+static void execute_commands(struct command *commands, const char *unpacker_error)
+{
+	struct command *cmd;
+	unsigned char sha1[20];
+
+	if (unpacker_error) {
+		for (cmd = commands; cmd; cmd = cmd->next)
+			cmd->error_string = "n/a (unpacker error)";
+		return;
+	}
+
+	cmd = commands;
+	if (check_everything_connected(iterate_receive_command_list,
+				       0, &cmd))
+		set_connectivity_errors(commands);
+
+	if (run_receive_hook(commands, pre_receive_hook, 0)) {
+		for (cmd = commands; cmd; cmd = cmd->next) {
+			if (!cmd->error_string)
+				cmd->error_string = "pre-receive hook declined";
+		}
+		return;
+	}
+
+	check_aliased_updates(commands);
+
+	free(head_name_to_free);
+	head_name = head_name_to_free = resolve_refdup("HEAD", sha1, 0, NULL);
+
+	for (cmd = commands; cmd; cmd = cmd->next) {
+		if (cmd->error_string)
+			continue;
+
+		if (cmd->skip_update)
+			continue;
+
+		cmd->error_string = update(cmd);
+	}
+}
+
+static struct command *read_head_info(void)
+{
+	struct command *commands = NULL;
+	struct command **p = &commands;
+	for (;;) {
+		static char line[1000];
+		unsigned char old_sha1[20], new_sha1[20];
+		struct command *cmd;
+		char *refname;
+		int len, reflen;
+
+		len = packet_read_line(0, line, sizeof(line));
+		if (!len)
+			break;
+		if (line[len-1] == '\n')
+			line[--len] = 0;
+		if (len < 83 ||
+		    line[40] != ' ' ||
+		    line[81] != ' ' ||
+		    get_sha1_hex(line, old_sha1) ||
+		    get_sha1_hex(line + 41, new_sha1))
+			die("protocol error: expected old/new/ref, got '%s'",
+			    line);
+
+		refname = line + 82;
+		reflen = strlen(refname);
+		if (reflen + 82 < len) {
+			const char *feature_list = refname + reflen + 1;
+			if (parse_feature_request(feature_list, "report-status"))
+				report_status = 1;
+			if (parse_feature_request(feature_list, "side-band-64k"))
+				use_sideband = LARGE_PACKET_MAX;
+			if (parse_feature_request(feature_list, "quiet"))
+				quiet = 1;
+		}
+		cmd = xcalloc(1, sizeof(struct command) + len - 80);
+		hashcpy(cmd->old_sha1, old_sha1);
+		hashcpy(cmd->new_sha1, new_sha1);
+		memcpy(cmd->ref_name, line + 82, len - 81);
+		*p = cmd;
+		p = &cmd->next;
+	}
+	return commands;
+}
+
+static const char *parse_pack_header(struct pack_header *hdr)
+{
+	switch (read_pack_header(0, hdr)) {
+	case PH_ERROR_EOF:
+		return "eof before pack header was fully read";
+
+	case PH_ERROR_PACK_SIGNATURE:
+		return "protocol error (pack signature mismatch detected)";
+
+	case PH_ERROR_PROTOCOL:
+		return "protocol error (pack version unsupported)";
+
+	default:
+		return "unknown error in parse_pack_header";
+
+	case 0:
+		return NULL;
+	}
+}
+
+static const char *pack_lockfile;
+
+static const char *unpack(void)
+{
+	struct pack_header hdr;
+	const char *hdr_err;
+	char hdr_arg[38];
+	int fsck_objects = (receive_fsck_objects >= 0
+			    ? receive_fsck_objects
+			    : transfer_fsck_objects >= 0
+			    ? transfer_fsck_objects
+			    : 0);
+
+	hdr_err = parse_pack_header(&hdr);
+	if (hdr_err)
+		return hdr_err;
+	snprintf(hdr_arg, sizeof(hdr_arg),
+			"--pack_header=%"PRIu32",%"PRIu32,
+			ntohl(hdr.hdr_version), ntohl(hdr.hdr_entries));
+
+	if (ntohl(hdr.hdr_entries) < unpack_limit) {
+		int code, i = 0;
+		const char *unpacker[5];
+		unpacker[i++] = "unpack-objects";
+		if (quiet)
+			unpacker[i++] = "-q";
+		if (fsck_objects)
+			unpacker[i++] = "--strict";
+		unpacker[i++] = hdr_arg;
+		unpacker[i++] = NULL;
+		code = run_command_v_opt(unpacker, RUN_GIT_CMD);
+		if (!code)
+			return NULL;
+		return "unpack-objects abnormal exit";
+	} else {
+		const char *keeper[7];
+		int s, status, i = 0;
+		char keep_arg[256];
+		struct child_process ip;
+
+		s = sprintf(keep_arg, "--keep=receive-pack %"PRIuMAX" on ", (uintmax_t) getpid());
+		if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
+			strcpy(keep_arg + s, "localhost");
+
+		keeper[i++] = "index-pack";
+		keeper[i++] = "--stdin";
+		if (fsck_objects)
+			keeper[i++] = "--strict";
+		keeper[i++] = "--fix-thin";
+		keeper[i++] = hdr_arg;
+		keeper[i++] = keep_arg;
+		keeper[i++] = NULL;
+		memset(&ip, 0, sizeof(ip));
+		ip.argv = keeper;
+		ip.out = -1;
+		ip.git_cmd = 1;
+		status = start_command(&ip);
+		if (status) {
+			return "index-pack fork failed";
+		}
+		pack_lockfile = index_pack_lockfile(ip.out);
+		close(ip.out);
+		status = finish_command(&ip);
+		if (!status) {
+			reprepare_packed_git();
+			return NULL;
+		}
+		return "index-pack abnormal exit";
+	}
+}
+
+static void report(struct command *commands, const char *unpack_status)
+{
+	struct command *cmd;
+	struct strbuf buf = STRBUF_INIT;
+
+	packet_buf_write(&buf, "unpack %s\n",
+			 unpack_status ? unpack_status : "ok");
+	for (cmd = commands; cmd; cmd = cmd->next) {
+		if (!cmd->error_string)
+			packet_buf_write(&buf, "ok %s\n",
+					 cmd->ref_name);
+		else
+			packet_buf_write(&buf, "ng %s %s\n",
+					 cmd->ref_name, cmd->error_string);
+	}
+	packet_buf_flush(&buf);
+
+	if (use_sideband)
+		send_sideband(1, 1, buf.buf, buf.len, use_sideband);
+	else
+		safe_write(1, buf.buf, buf.len);
+	strbuf_release(&buf);
+}
+
+static int delete_only(struct command *commands)
+{
+	struct command *cmd;
+	for (cmd = commands; cmd; cmd = cmd->next) {
+		if (!is_null_sha1(cmd->new_sha1))
+			return 0;
+	}
+	return 1;
+}
+
+int cmd_receive_pack(int argc, const char **argv, const char *prefix)
+{
+	int advertise_refs = 0;
+	int stateless_rpc = 0;
+	int i;
+	char *dir = NULL;
+	struct command *commands;
+
+	packet_trace_identity("receive-pack");
+
+	argv++;
+	for (i = 1; i < argc; i++) {
+		const char *arg = *argv++;
+
+		if (*arg == '-') {
+			if (!strcmp(arg, "--quiet")) {
+				quiet = 1;
+				continue;
+			}
+
+			if (!strcmp(arg, "--advertise-refs")) {
+				advertise_refs = 1;
+				continue;
+			}
+			if (!strcmp(arg, "--stateless-rpc")) {
+				stateless_rpc = 1;
+				continue;
+			}
+
+			usage(receive_pack_usage);
+		}
+		if (dir)
+			usage(receive_pack_usage);
+		dir = xstrdup(arg);
+	}
+	if (!dir)
+		usage(receive_pack_usage);
+
+	setup_path();
+
+	if (!enter_repo(dir, 0))
+		die("'%s' does not appear to be a git repository", dir);
+
+	if (is_repository_shallow())
+		die("attempt to push into a shallow repository");
+
+	git_config(receive_pack_config, NULL);
+
+	if (0 <= transfer_unpack_limit)
+		unpack_limit = transfer_unpack_limit;
+	else if (0 <= receive_unpack_limit)
+		unpack_limit = receive_unpack_limit;
+
+	if (advertise_refs || !stateless_rpc) {
+		write_head_info();
+	}
+	if (advertise_refs)
+		return 0;
+
+	if ((commands = read_head_info()) != NULL) {
+		const char *unpack_status = NULL;
+
+		if (!delete_only(commands))
+			unpack_status = unpack();
+		execute_commands(commands, unpack_status);
+		if (pack_lockfile)
+			unlink_or_warn(pack_lockfile);
+		if (report_status)
+			report(commands, unpack_status);
+		run_receive_hook(commands, post_receive_hook, 1);
+		run_update_post_hook(commands);
+		if (auto_gc) {
+			const char *argv_gc_auto[] = {
+				"gc", "--auto", "--quiet", NULL,
+			};
+			run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
+		}
+		if (auto_update_server_info)
+			update_server_info(0);
+	}
+	if (use_sideband)
+		packet_flush(1);
+	return 0;
+}
diff --git a/builtin/reflog.c b/builtin/reflog.c
new file mode 100644
index 0000000..b3c9e27
--- /dev/null
+++ b/builtin/reflog.c
@@ -0,0 +1,783 @@
+#include "cache.h"
+#include "builtin.h"
+#include "commit.h"
+#include "refs.h"
+#include "dir.h"
+#include "tree-walk.h"
+#include "diff.h"
+#include "revision.h"
+#include "reachable.h"
+
+/*
+ * reflog expire
+ */
+
+static const char reflog_expire_usage[] =
+"git reflog expire [--verbose] [--dry-run] [--stale-fix] [--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>...";
+static const char reflog_delete_usage[] =
+"git reflog delete [--verbose] [--dry-run] [--rewrite] [--updateref] <refs>...";
+
+static unsigned long default_reflog_expire;
+static unsigned long default_reflog_expire_unreachable;
+
+struct cmd_reflog_expire_cb {
+	struct rev_info revs;
+	int dry_run;
+	int stalefix;
+	int rewrite;
+	int updateref;
+	int verbose;
+	unsigned long expire_total;
+	unsigned long expire_unreachable;
+	int recno;
+};
+
+struct expire_reflog_cb {
+	FILE *newlog;
+	enum {
+		UE_NORMAL,
+		UE_ALWAYS,
+		UE_HEAD
+	} unreachable_expire_kind;
+	struct commit_list *mark_list;
+	unsigned long mark_limit;
+	struct cmd_reflog_expire_cb *cmd;
+	unsigned char last_kept_sha1[20];
+};
+
+struct collected_reflog {
+	unsigned char sha1[20];
+	char reflog[FLEX_ARRAY];
+};
+struct collect_reflog_cb {
+	struct collected_reflog **e;
+	int alloc;
+	int nr;
+};
+
+#define INCOMPLETE	(1u<<10)
+#define STUDYING	(1u<<11)
+#define REACHABLE	(1u<<12)
+
+static int tree_is_complete(const unsigned char *sha1)
+{
+	struct tree_desc desc;
+	struct name_entry entry;
+	int complete;
+	struct tree *tree;
+
+	tree = lookup_tree(sha1);
+	if (!tree)
+		return 0;
+	if (tree->object.flags & SEEN)
+		return 1;
+	if (tree->object.flags & INCOMPLETE)
+		return 0;
+
+	if (!tree->buffer) {
+		enum object_type type;
+		unsigned long size;
+		void *data = read_sha1_file(sha1, &type, &size);
+		if (!data) {
+			tree->object.flags |= INCOMPLETE;
+			return 0;
+		}
+		tree->buffer = data;
+		tree->size = size;
+	}
+	init_tree_desc(&desc, tree->buffer, tree->size);
+	complete = 1;
+	while (tree_entry(&desc, &entry)) {
+		if (!has_sha1_file(entry.sha1) ||
+		    (S_ISDIR(entry.mode) && !tree_is_complete(entry.sha1))) {
+			tree->object.flags |= INCOMPLETE;
+			complete = 0;
+		}
+	}
+	free(tree->buffer);
+	tree->buffer = NULL;
+
+	if (complete)
+		tree->object.flags |= SEEN;
+	return complete;
+}
+
+static int commit_is_complete(struct commit *commit)
+{
+	struct object_array study;
+	struct object_array found;
+	int is_incomplete = 0;
+	int i;
+
+	/* early return */
+	if (commit->object.flags & SEEN)
+		return 1;
+	if (commit->object.flags & INCOMPLETE)
+		return 0;
+	/*
+	 * Find all commits that are reachable and are not marked as
+	 * SEEN.  Then make sure the trees and blobs contained are
+	 * complete.  After that, mark these commits also as SEEN.
+	 * If some of the objects that are needed to complete this
+	 * commit are missing, mark this commit as INCOMPLETE.
+	 */
+	memset(&study, 0, sizeof(study));
+	memset(&found, 0, sizeof(found));
+	add_object_array(&commit->object, NULL, &study);
+	add_object_array(&commit->object, NULL, &found);
+	commit->object.flags |= STUDYING;
+	while (study.nr) {
+		struct commit *c;
+		struct commit_list *parent;
+
+		c = (struct commit *)study.objects[--study.nr].item;
+		if (!c->object.parsed && !parse_object(c->object.sha1))
+			c->object.flags |= INCOMPLETE;
+
+		if (c->object.flags & INCOMPLETE) {
+			is_incomplete = 1;
+			break;
+		}
+		else if (c->object.flags & SEEN)
+			continue;
+		for (parent = c->parents; parent; parent = parent->next) {
+			struct commit *p = parent->item;
+			if (p->object.flags & STUDYING)
+				continue;
+			p->object.flags |= STUDYING;
+			add_object_array(&p->object, NULL, &study);
+			add_object_array(&p->object, NULL, &found);
+		}
+	}
+	if (!is_incomplete) {
+		/*
+		 * make sure all commits in "found" array have all the
+		 * necessary objects.
+		 */
+		for (i = 0; i < found.nr; i++) {
+			struct commit *c =
+				(struct commit *)found.objects[i].item;
+			if (!tree_is_complete(c->tree->object.sha1)) {
+				is_incomplete = 1;
+				c->object.flags |= INCOMPLETE;
+			}
+		}
+		if (!is_incomplete) {
+			/* mark all found commits as complete, iow SEEN */
+			for (i = 0; i < found.nr; i++)
+				found.objects[i].item->flags |= SEEN;
+		}
+	}
+	/* clear flags from the objects we traversed */
+	for (i = 0; i < found.nr; i++)
+		found.objects[i].item->flags &= ~STUDYING;
+	if (is_incomplete)
+		commit->object.flags |= INCOMPLETE;
+	else {
+		/*
+		 * If we come here, we have (1) traversed the ancestry chain
+		 * from the "commit" until we reach SEEN commits (which are
+		 * known to be complete), and (2) made sure that the commits
+		 * encountered during the above traversal refer to trees that
+		 * are complete.  Which means that we know *all* the commits
+		 * we have seen during this process are complete.
+		 */
+		for (i = 0; i < found.nr; i++)
+			found.objects[i].item->flags |= SEEN;
+	}
+	/* free object arrays */
+	free(study.objects);
+	free(found.objects);
+	return !is_incomplete;
+}
+
+static int keep_entry(struct commit **it, unsigned char *sha1)
+{
+	struct commit *commit;
+
+	if (is_null_sha1(sha1))
+		return 1;
+	commit = lookup_commit_reference_gently(sha1, 1);
+	if (!commit)
+		return 0;
+
+	/*
+	 * Make sure everything in this commit exists.
+	 *
+	 * We have walked all the objects reachable from the refs
+	 * and cache earlier.  The commits reachable by this commit
+	 * must meet SEEN commits -- and then we should mark them as
+	 * SEEN as well.
+	 */
+	if (!commit_is_complete(commit))
+		return 0;
+	*it = commit;
+	return 1;
+}
+
+/*
+ * Starting from commits in the cb->mark_list, mark commits that are
+ * reachable from them.  Stop the traversal at commits older than
+ * the expire_limit and queue them back, so that the caller can call
+ * us again to restart the traversal with longer expire_limit.
+ */
+static void mark_reachable(struct expire_reflog_cb *cb)
+{
+	struct commit *commit;
+	struct commit_list *pending;
+	unsigned long expire_limit = cb->mark_limit;
+	struct commit_list *leftover = NULL;
+
+	for (pending = cb->mark_list; pending; pending = pending->next)
+		pending->item->object.flags &= ~REACHABLE;
+
+	pending = cb->mark_list;
+	while (pending) {
+		struct commit_list *entry = pending;
+		struct commit_list *parent;
+		pending = entry->next;
+		commit = entry->item;
+		free(entry);
+		if (commit->object.flags & REACHABLE)
+			continue;
+		if (parse_commit(commit))
+			continue;
+		commit->object.flags |= REACHABLE;
+		if (commit->date < expire_limit) {
+			commit_list_insert(commit, &leftover);
+			continue;
+		}
+		commit->object.flags |= REACHABLE;
+		parent = commit->parents;
+		while (parent) {
+			commit = parent->item;
+			parent = parent->next;
+			if (commit->object.flags & REACHABLE)
+				continue;
+			commit_list_insert(commit, &pending);
+		}
+	}
+	cb->mark_list = leftover;
+}
+
+static int unreachable(struct expire_reflog_cb *cb, struct commit *commit, unsigned char *sha1)
+{
+	/*
+	 * We may or may not have the commit yet - if not, look it
+	 * up using the supplied sha1.
+	 */
+	if (!commit) {
+		if (is_null_sha1(sha1))
+			return 0;
+
+		commit = lookup_commit_reference_gently(sha1, 1);
+
+		/* Not a commit -- keep it */
+		if (!commit)
+			return 0;
+	}
+
+	/* Reachable from the current ref?  Don't prune. */
+	if (commit->object.flags & REACHABLE)
+		return 0;
+
+	if (cb->mark_list && cb->mark_limit) {
+		cb->mark_limit = 0; /* dig down to the root */
+		mark_reachable(cb);
+	}
+
+	return !(commit->object.flags & REACHABLE);
+}
+
+static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
+		const char *email, unsigned long timestamp, int tz,
+		const char *message, void *cb_data)
+{
+	struct expire_reflog_cb *cb = cb_data;
+	struct commit *old, *new;
+
+	if (timestamp < cb->cmd->expire_total)
+		goto prune;
+
+	if (cb->cmd->rewrite)
+		osha1 = cb->last_kept_sha1;
+
+	old = new = NULL;
+	if (cb->cmd->stalefix &&
+	    (!keep_entry(&old, osha1) || !keep_entry(&new, nsha1)))
+		goto prune;
+
+	if (timestamp < cb->cmd->expire_unreachable) {
+		if (cb->unreachable_expire_kind == UE_ALWAYS)
+			goto prune;
+		if (unreachable(cb, old, osha1) || unreachable(cb, new, nsha1))
+			goto prune;
+	}
+
+	if (cb->cmd->recno && --(cb->cmd->recno) == 0)
+		goto prune;
+
+	if (cb->newlog) {
+		char sign = (tz < 0) ? '-' : '+';
+		int zone = (tz < 0) ? (-tz) : tz;
+		fprintf(cb->newlog, "%s %s %s %lu %c%04d\t%s",
+			sha1_to_hex(osha1), sha1_to_hex(nsha1),
+			email, timestamp, sign, zone,
+			message);
+		hashcpy(cb->last_kept_sha1, nsha1);
+	}
+	if (cb->cmd->verbose)
+		printf("keep %s", message);
+	return 0;
+ prune:
+	if (!cb->newlog)
+		printf("would prune %s", message);
+	else if (cb->cmd->verbose)
+		printf("prune %s", message);
+	return 0;
+}
+
+static int push_tip_to_list(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
+{
+	struct commit_list **list = cb_data;
+	struct commit *tip_commit;
+	if (flags & REF_ISSYMREF)
+		return 0;
+	tip_commit = lookup_commit_reference_gently(sha1, 1);
+	if (!tip_commit)
+		return 0;
+	commit_list_insert(tip_commit, list);
+	return 0;
+}
+
+static int expire_reflog(const char *ref, const unsigned char *sha1, int unused, void *cb_data)
+{
+	struct cmd_reflog_expire_cb *cmd = cb_data;
+	struct expire_reflog_cb cb;
+	struct ref_lock *lock;
+	char *log_file, *newlog_path = NULL;
+	struct commit *tip_commit;
+	struct commit_list *tips;
+	int status = 0;
+
+	memset(&cb, 0, sizeof(cb));
+
+	/*
+	 * we take the lock for the ref itself to prevent it from
+	 * getting updated.
+	 */
+	lock = lock_any_ref_for_update(ref, sha1, 0);
+	if (!lock)
+		return error("cannot lock ref '%s'", ref);
+	log_file = git_pathdup("logs/%s", ref);
+	if (!file_exists(log_file))
+		goto finish;
+	if (!cmd->dry_run) {
+		newlog_path = git_pathdup("logs/%s.lock", ref);
+		cb.newlog = fopen(newlog_path, "w");
+	}
+
+	cb.cmd = cmd;
+
+	if (!cmd->expire_unreachable || !strcmp(ref, "HEAD")) {
+		tip_commit = NULL;
+		cb.unreachable_expire_kind = UE_HEAD;
+	} else {
+		tip_commit = lookup_commit_reference_gently(sha1, 1);
+		if (!tip_commit)
+			cb.unreachable_expire_kind = UE_ALWAYS;
+		else
+			cb.unreachable_expire_kind = UE_NORMAL;
+	}
+
+	if (cmd->expire_unreachable <= cmd->expire_total)
+		cb.unreachable_expire_kind = UE_ALWAYS;
+
+	cb.mark_list = NULL;
+	tips = NULL;
+	if (cb.unreachable_expire_kind != UE_ALWAYS) {
+		if (cb.unreachable_expire_kind == UE_HEAD) {
+			struct commit_list *elem;
+			for_each_ref(push_tip_to_list, &tips);
+			for (elem = tips; elem; elem = elem->next)
+				commit_list_insert(elem->item, &cb.mark_list);
+		} else {
+			commit_list_insert(tip_commit, &cb.mark_list);
+		}
+		cb.mark_limit = cmd->expire_total;
+		mark_reachable(&cb);
+	}
+
+	for_each_reflog_ent(ref, expire_reflog_ent, &cb);
+
+	if (cb.unreachable_expire_kind != UE_ALWAYS) {
+		if (cb.unreachable_expire_kind == UE_HEAD) {
+			struct commit_list *elem;
+			for (elem = tips; elem; elem = elem->next)
+				clear_commit_marks(tip_commit, REACHABLE);
+			free_commit_list(tips);
+		} else {
+			clear_commit_marks(tip_commit, REACHABLE);
+		}
+	}
+ finish:
+	if (cb.newlog) {
+		if (fclose(cb.newlog)) {
+			status |= error("%s: %s", strerror(errno),
+					newlog_path);
+			unlink(newlog_path);
+		} else if (cmd->updateref &&
+			(write_in_full(lock->lock_fd,
+				sha1_to_hex(cb.last_kept_sha1), 40) != 40 ||
+			 write_str_in_full(lock->lock_fd, "\n") != 1 ||
+			 close_ref(lock) < 0)) {
+			status |= error("Couldn't write %s",
+				lock->lk->filename);
+			unlink(newlog_path);
+		} else if (rename(newlog_path, log_file)) {
+			status |= error("cannot rename %s to %s",
+					newlog_path, log_file);
+			unlink(newlog_path);
+		} else if (cmd->updateref && commit_ref(lock)) {
+			status |= error("Couldn't set %s", lock->ref_name);
+		} else {
+			adjust_shared_perm(log_file);
+		}
+	}
+	free(newlog_path);
+	free(log_file);
+	unlock_ref(lock);
+	return status;
+}
+
+static int collect_reflog(const char *ref, const unsigned char *sha1, int unused, void *cb_data)
+{
+	struct collected_reflog *e;
+	struct collect_reflog_cb *cb = cb_data;
+	size_t namelen = strlen(ref);
+
+	e = xmalloc(sizeof(*e) + namelen + 1);
+	hashcpy(e->sha1, sha1);
+	memcpy(e->reflog, ref, namelen + 1);
+	ALLOC_GROW(cb->e, cb->nr + 1, cb->alloc);
+	cb->e[cb->nr++] = e;
+	return 0;
+}
+
+static struct reflog_expire_cfg {
+	struct reflog_expire_cfg *next;
+	unsigned long expire_total;
+	unsigned long expire_unreachable;
+	size_t len;
+	char pattern[FLEX_ARRAY];
+} *reflog_expire_cfg, **reflog_expire_cfg_tail;
+
+static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len)
+{
+	struct reflog_expire_cfg *ent;
+
+	if (!reflog_expire_cfg_tail)
+		reflog_expire_cfg_tail = &reflog_expire_cfg;
+
+	for (ent = reflog_expire_cfg; ent; ent = ent->next)
+		if (ent->len == len &&
+		    !memcmp(ent->pattern, pattern, len))
+			return ent;
+
+	ent = xcalloc(1, (sizeof(*ent) + len));
+	memcpy(ent->pattern, pattern, len);
+	ent->len = len;
+	*reflog_expire_cfg_tail = ent;
+	reflog_expire_cfg_tail = &(ent->next);
+	return ent;
+}
+
+static int parse_expire_cfg_value(const char *var, const char *value, unsigned long *expire)
+{
+	if (!value)
+		return config_error_nonbool(var);
+	if (!strcmp(value, "never") || !strcmp(value, "false")) {
+		*expire = 0;
+		return 0;
+	}
+	*expire = approxidate(value);
+	return 0;
+}
+
+/* expiry timer slot */
+#define EXPIRE_TOTAL   01
+#define EXPIRE_UNREACH 02
+
+static int reflog_expire_config(const char *var, const char *value, void *cb)
+{
+	const char *lastdot = strrchr(var, '.');
+	unsigned long expire;
+	int slot;
+	struct reflog_expire_cfg *ent;
+
+	if (!lastdot || prefixcmp(var, "gc."))
+		return git_default_config(var, value, cb);
+
+	if (!strcmp(lastdot, ".reflogexpire")) {
+		slot = EXPIRE_TOTAL;
+		if (parse_expire_cfg_value(var, value, &expire))
+			return -1;
+	} else if (!strcmp(lastdot, ".reflogexpireunreachable")) {
+		slot = EXPIRE_UNREACH;
+		if (parse_expire_cfg_value(var, value, &expire))
+			return -1;
+	} else
+		return git_default_config(var, value, cb);
+
+	if (lastdot == var + 2) {
+		switch (slot) {
+		case EXPIRE_TOTAL:
+			default_reflog_expire = expire;
+			break;
+		case EXPIRE_UNREACH:
+			default_reflog_expire_unreachable = expire;
+			break;
+		}
+		return 0;
+	}
+
+	ent = find_cfg_ent(var + 3, lastdot - (var+3));
+	if (!ent)
+		return -1;
+	switch (slot) {
+	case EXPIRE_TOTAL:
+		ent->expire_total = expire;
+		break;
+	case EXPIRE_UNREACH:
+		ent->expire_unreachable = expire;
+		break;
+	}
+	return 0;
+}
+
+static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, int slot, const char *ref)
+{
+	struct reflog_expire_cfg *ent;
+
+	if (slot == (EXPIRE_TOTAL|EXPIRE_UNREACH))
+		return; /* both given explicitly -- nothing to tweak */
+
+	for (ent = reflog_expire_cfg; ent; ent = ent->next) {
+		if (!fnmatch(ent->pattern, ref, 0)) {
+			if (!(slot & EXPIRE_TOTAL))
+				cb->expire_total = ent->expire_total;
+			if (!(slot & EXPIRE_UNREACH))
+				cb->expire_unreachable = ent->expire_unreachable;
+			return;
+		}
+	}
+
+	/*
+	 * If unconfigured, make stash never expire
+	 */
+	if (!strcmp(ref, "refs/stash")) {
+		if (!(slot & EXPIRE_TOTAL))
+			cb->expire_total = 0;
+		if (!(slot & EXPIRE_UNREACH))
+			cb->expire_unreachable = 0;
+		return;
+	}
+
+	/* Nothing matched -- use the default value */
+	if (!(slot & EXPIRE_TOTAL))
+		cb->expire_total = default_reflog_expire;
+	if (!(slot & EXPIRE_UNREACH))
+		cb->expire_unreachable = default_reflog_expire_unreachable;
+}
+
+static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
+{
+	struct cmd_reflog_expire_cb cb;
+	unsigned long now = time(NULL);
+	int i, status, do_all;
+	int explicit_expiry = 0;
+
+	default_reflog_expire_unreachable = now - 30 * 24 * 3600;
+	default_reflog_expire = now - 90 * 24 * 3600;
+	git_config(reflog_expire_config, NULL);
+
+	save_commit_buffer = 0;
+	do_all = status = 0;
+	memset(&cb, 0, sizeof(cb));
+
+	cb.expire_total = default_reflog_expire;
+	cb.expire_unreachable = default_reflog_expire_unreachable;
+
+	for (i = 1; i < argc; i++) {
+		const char *arg = argv[i];
+		if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
+			cb.dry_run = 1;
+		else if (!prefixcmp(arg, "--expire=")) {
+			cb.expire_total = approxidate(arg + 9);
+			explicit_expiry |= EXPIRE_TOTAL;
+		}
+		else if (!prefixcmp(arg, "--expire-unreachable=")) {
+			cb.expire_unreachable = approxidate(arg + 21);
+			explicit_expiry |= EXPIRE_UNREACH;
+		}
+		else if (!strcmp(arg, "--stale-fix"))
+			cb.stalefix = 1;
+		else if (!strcmp(arg, "--rewrite"))
+			cb.rewrite = 1;
+		else if (!strcmp(arg, "--updateref"))
+			cb.updateref = 1;
+		else if (!strcmp(arg, "--all"))
+			do_all = 1;
+		else if (!strcmp(arg, "--verbose"))
+			cb.verbose = 1;
+		else if (!strcmp(arg, "--")) {
+			i++;
+			break;
+		}
+		else if (arg[0] == '-')
+			usage(reflog_expire_usage);
+		else
+			break;
+	}
+
+	/*
+	 * We can trust the commits and objects reachable from refs
+	 * even in older repository.  We cannot trust what's reachable
+	 * from reflog if the repository was pruned with older git.
+	 */
+	if (cb.stalefix) {
+		init_revisions(&cb.revs, prefix);
+		if (cb.verbose)
+			printf("Marking reachable objects...");
+		mark_reachable_objects(&cb.revs, 0, NULL);
+		if (cb.verbose)
+			putchar('\n');
+	}
+
+	if (do_all) {
+		struct collect_reflog_cb collected;
+		int i;
+
+		memset(&collected, 0, sizeof(collected));
+		for_each_reflog(collect_reflog, &collected);
+		for (i = 0; i < collected.nr; i++) {
+			struct collected_reflog *e = collected.e[i];
+			set_reflog_expiry_param(&cb, explicit_expiry, e->reflog);
+			status |= expire_reflog(e->reflog, e->sha1, 0, &cb);
+			free(e);
+		}
+		free(collected.e);
+	}
+
+	for (; i < argc; i++) {
+		char *ref;
+		unsigned char sha1[20];
+		if (!dwim_log(argv[i], strlen(argv[i]), sha1, &ref)) {
+			status |= error("%s points nowhere!", argv[i]);
+			continue;
+		}
+		set_reflog_expiry_param(&cb, explicit_expiry, ref);
+		status |= expire_reflog(ref, sha1, 0, &cb);
+	}
+	return status;
+}
+
+static int count_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
+		const char *email, unsigned long timestamp, int tz,
+		const char *message, void *cb_data)
+{
+	struct cmd_reflog_expire_cb *cb = cb_data;
+	if (!cb->expire_total || timestamp < cb->expire_total)
+		cb->recno++;
+	return 0;
+}
+
+static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
+{
+	struct cmd_reflog_expire_cb cb;
+	int i, status = 0;
+
+	memset(&cb, 0, sizeof(cb));
+
+	for (i = 1; i < argc; i++) {
+		const char *arg = argv[i];
+		if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
+			cb.dry_run = 1;
+		else if (!strcmp(arg, "--rewrite"))
+			cb.rewrite = 1;
+		else if (!strcmp(arg, "--updateref"))
+			cb.updateref = 1;
+		else if (!strcmp(arg, "--verbose"))
+			cb.verbose = 1;
+		else if (!strcmp(arg, "--")) {
+			i++;
+			break;
+		}
+		else if (arg[0] == '-')
+			usage(reflog_delete_usage);
+		else
+			break;
+	}
+
+	if (argc - i < 1)
+		return error("Nothing to delete?");
+
+	for ( ; i < argc; i++) {
+		const char *spec = strstr(argv[i], "@{");
+		unsigned char sha1[20];
+		char *ep, *ref;
+		int recno;
+
+		if (!spec) {
+			status |= error("Not a reflog: %s", argv[i]);
+			continue;
+		}
+
+		if (!dwim_log(argv[i], spec - argv[i], sha1, &ref)) {
+			status |= error("no reflog for '%s'", argv[i]);
+			continue;
+		}
+
+		recno = strtoul(spec + 2, &ep, 10);
+		if (*ep == '}') {
+			cb.recno = -recno;
+			for_each_reflog_ent(ref, count_reflog_ent, &cb);
+		} else {
+			cb.expire_total = approxidate(spec + 2);
+			for_each_reflog_ent(ref, count_reflog_ent, &cb);
+			cb.expire_total = 0;
+		}
+
+		status |= expire_reflog(ref, sha1, 0, &cb);
+		free(ref);
+	}
+	return status;
+}
+
+/*
+ * main "reflog"
+ */
+
+static const char reflog_usage[] =
+"git reflog [ show | expire | delete ]";
+
+int cmd_reflog(int argc, const char **argv, const char *prefix)
+{
+	if (argc > 1 && !strcmp(argv[1], "-h"))
+		usage(reflog_usage);
+
+	/* With no command, we default to showing it. */
+	if (argc < 2 || *argv[1] == '-')
+		return cmd_log_reflog(argc, argv, prefix);
+
+	if (!strcmp(argv[1], "show"))
+		return cmd_log_reflog(argc - 1, argv + 1, prefix);
+
+	if (!strcmp(argv[1], "expire"))
+		return cmd_reflog_expire(argc - 1, argv + 1, prefix);
+
+	if (!strcmp(argv[1], "delete"))
+		return cmd_reflog_delete(argc - 1, argv + 1, prefix);
+
+	return cmd_log_reflog(argc, argv, prefix);
+}
diff --git a/builtin/remote-ext.c b/builtin/remote-ext.c
new file mode 100644
index 0000000..692c834
--- /dev/null
+++ b/builtin/remote-ext.c
@@ -0,0 +1,242 @@
+#include "builtin.h"
+#include "transport.h"
+#include "run-command.h"
+
+/*
+ * URL syntax:
+ *	'command [arg1 [arg2 [...]]]'	Invoke command with given arguments.
+ *	Special characters:
+ *	'% ': Literal space in argument.
+ *	'%%': Literal percent sign.
+ *	'%S': Name of service (git-upload-pack/git-upload-archive/
+ *		git-receive-pack.
+ *	'%s': Same as \s, but with possible git- prefix stripped.
+ *	'%G': Only allowed as first 'character' of argument. Do not pass this
+ *		Argument to command, instead send this as name of repository
+ *		in in-line git://-style request (also activates sending this
+ *		style of request).
+ *	'%V': Only allowed as first 'character' of argument. Used in
+ *		conjunction with '%G': Do not pass this argument to command,
+ *		instead send this as vhost in git://-style request (note: does
+ *		not activate sending git:// style request).
+ */
+
+static char *git_req;
+static char *git_req_vhost;
+
+static char *strip_escapes(const char *str, const char *service,
+	const char **next)
+{
+	size_t rpos = 0;
+	int escape = 0;
+	char special = 0;
+	size_t psoff = 0;
+	struct strbuf ret = STRBUF_INIT;
+
+	/* Calculate prefix length for \s and lengths for \s and \S */
+	if (!strncmp(service, "git-", 4))
+		psoff = 4;
+
+	/* Pass the service to command. */
+	setenv("GIT_EXT_SERVICE", service, 1);
+	setenv("GIT_EXT_SERVICE_NOPREFIX", service + psoff, 1);
+
+	/* Scan the length of argument. */
+	while (str[rpos] && (escape || str[rpos] != ' ')) {
+		if (escape) {
+			switch (str[rpos]) {
+			case ' ':
+			case '%':
+			case 's':
+			case 'S':
+				break;
+			case 'G':
+			case 'V':
+				special = str[rpos];
+				if (rpos == 1)
+					break;
+				/* Fall-through to error. */
+			default:
+				die("Bad remote-ext placeholder '%%%c'.",
+					str[rpos]);
+			}
+			escape = 0;
+		} else
+			escape = (str[rpos] == '%');
+		rpos++;
+	}
+	if (escape && !str[rpos])
+		die("remote-ext command has incomplete placeholder");
+	*next = str + rpos;
+	if (**next == ' ')
+		++*next;	/* Skip over space */
+
+	/*
+	 * Do the actual placeholder substitution. The string will be short
+	 * enough not to overflow integers.
+	 */
+	rpos = special ? 2 : 0;		/* Skip first 2 bytes in specials. */
+	escape = 0;
+	while (str[rpos] && (escape || str[rpos] != ' ')) {
+		if (escape) {
+			switch (str[rpos]) {
+			case ' ':
+			case '%':
+				strbuf_addch(&ret, str[rpos]);
+				break;
+			case 's':
+				strbuf_addstr(&ret, service + psoff);
+				break;
+			case 'S':
+				strbuf_addstr(&ret, service);
+				break;
+			}
+			escape = 0;
+		} else
+			switch (str[rpos]) {
+			case '%':
+				escape = 1;
+				break;
+			default:
+				strbuf_addch(&ret, str[rpos]);
+				break;
+			}
+		rpos++;
+	}
+	switch (special) {
+	case 'G':
+		git_req = strbuf_detach(&ret, NULL);
+		return NULL;
+	case 'V':
+		git_req_vhost = strbuf_detach(&ret, NULL);
+		return NULL;
+	default:
+		return strbuf_detach(&ret, NULL);
+	}
+}
+
+/* Should be enough... */
+#define MAXARGUMENTS 256
+
+static const char **parse_argv(const char *arg, const char *service)
+{
+	int arguments = 0;
+	int i;
+	const char **ret;
+	char *temparray[MAXARGUMENTS + 1];
+
+	while (*arg) {
+		char *expanded;
+		if (arguments == MAXARGUMENTS)
+			die("remote-ext command has too many arguments");
+		expanded = strip_escapes(arg, service, &arg);
+		if (expanded)
+			temparray[arguments++] = expanded;
+	}
+
+	ret = xmalloc((arguments + 1) * sizeof(char *));
+	for (i = 0; i < arguments; i++)
+		ret[i] = temparray[i];
+	ret[arguments] = NULL;
+	return ret;
+}
+
+static void send_git_request(int stdin_fd, const char *serv, const char *repo,
+	const char *vhost)
+{
+	size_t bufferspace;
+	size_t wpos = 0;
+	char *buffer;
+
+	/*
+	 * Request needs 12 bytes extra if there is vhost (xxxx \0host=\0) and
+	 * 6 bytes extra (xxxx \0) if there is no vhost.
+	 */
+	if (vhost)
+		bufferspace = strlen(serv) + strlen(repo) + strlen(vhost) + 12;
+	else
+		bufferspace = strlen(serv) + strlen(repo) + 6;
+
+	if (bufferspace > 0xFFFF)
+		die("Request too large to send");
+	buffer = xmalloc(bufferspace);
+
+	/* Make the packet. */
+	wpos = sprintf(buffer, "%04x%s %s%c", (unsigned)bufferspace,
+		serv, repo, 0);
+
+	/* Add vhost if any. */
+	if (vhost)
+		sprintf(buffer + wpos, "host=%s%c", vhost, 0);
+
+	/* Send the request */
+	if (write_in_full(stdin_fd, buffer, bufferspace) < 0)
+		die_errno("Failed to send request");
+
+	free(buffer);
+}
+
+static int run_child(const char *arg, const char *service)
+{
+	int r;
+	struct child_process child;
+
+	memset(&child, 0, sizeof(child));
+	child.in = -1;
+	child.out = -1;
+	child.err = 0;
+	child.argv = parse_argv(arg, service);
+
+	if (start_command(&child) < 0)
+		die("Can't run specified command");
+
+	if (git_req)
+		send_git_request(child.in, service, git_req, git_req_vhost);
+
+	r = bidirectional_transfer_loop(child.out, child.in);
+	if (!r)
+		r = finish_command(&child);
+	else
+		finish_command(&child);
+	return r;
+}
+
+#define MAXCOMMAND 4096
+
+static int command_loop(const char *child)
+{
+	char buffer[MAXCOMMAND];
+
+	while (1) {
+		size_t i;
+		if (!fgets(buffer, MAXCOMMAND - 1, stdin)) {
+			if (ferror(stdin))
+				die("Comammand input error");
+			exit(0);
+		}
+		/* Strip end of line characters. */
+		i = strlen(buffer);
+		while (i > 0 && isspace(buffer[i - 1]))
+			buffer[--i] = 0;
+
+		if (!strcmp(buffer, "capabilities")) {
+			printf("*connect\n\n");
+			fflush(stdout);
+		} else if (!strncmp(buffer, "connect ", 8)) {
+			printf("\n");
+			fflush(stdout);
+			return run_child(child, buffer + 8);
+		} else {
+			fprintf(stderr, "Bad command");
+			return 1;
+		}
+	}
+}
+
+int cmd_remote_ext(int argc, const char **argv, const char *prefix)
+{
+	if (argc != 3)
+		die("Expected two arguments");
+
+	return command_loop(argv[2]);
+}
diff --git a/builtin/remote-fd.c b/builtin/remote-fd.c
new file mode 100644
index 0000000..08d7121
--- /dev/null
+++ b/builtin/remote-fd.c
@@ -0,0 +1,79 @@
+#include "builtin.h"
+#include "transport.h"
+
+/*
+ * URL syntax:
+ *	'fd::<inoutfd>[/<anything>]'		Read/write socket pair
+ *						<inoutfd>.
+ *	'fd::<infd>,<outfd>[/<anything>]'	Read pipe <infd> and write
+ *						pipe <outfd>.
+ *	[foo] indicates 'foo' is optional. <anything> is any string.
+ *
+ * The data output to <outfd>/<inoutfd> should be passed unmolested to
+ * git-receive-pack/git-upload-pack/git-upload-archive and output of
+ * git-receive-pack/git-upload-pack/git-upload-archive should be passed
+ * unmolested to <infd>/<inoutfd>.
+ *
+ */
+
+#define MAXCOMMAND 4096
+
+static void command_loop(int input_fd, int output_fd)
+{
+	char buffer[MAXCOMMAND];
+
+	while (1) {
+		size_t i;
+		if (!fgets(buffer, MAXCOMMAND - 1, stdin)) {
+			if (ferror(stdin))
+				die("Input error");
+			return;
+		}
+		/* Strip end of line characters. */
+		i = strlen(buffer);
+		while (i > 0 && isspace(buffer[i - 1]))
+			buffer[--i] = 0;
+
+		if (!strcmp(buffer, "capabilities")) {
+			printf("*connect\n\n");
+			fflush(stdout);
+		} else if (!strncmp(buffer, "connect ", 8)) {
+			printf("\n");
+			fflush(stdout);
+			if (bidirectional_transfer_loop(input_fd,
+				output_fd))
+				die("Copying data between file descriptors failed");
+			return;
+		} else {
+			die("Bad command: %s", buffer);
+		}
+	}
+}
+
+int cmd_remote_fd(int argc, const char **argv, const char *prefix)
+{
+	int input_fd = -1;
+	int output_fd = -1;
+	char *end;
+
+	if (argc != 3)
+		die("Expected two arguments");
+
+	input_fd = (int)strtoul(argv[2], &end, 10);
+
+	if ((end == argv[2]) || (*end != ',' && *end != '/' && *end))
+		die("Bad URL syntax");
+
+	if (*end == '/' || !*end) {
+		output_fd = input_fd;
+	} else {
+		char *end2;
+		output_fd = (int)strtoul(end + 1, &end2, 10);
+
+		if ((end2 == end + 1) || (*end2 != '/' && *end2))
+			die("Bad URL syntax");
+	}
+
+	command_loop(input_fd, output_fd);
+	return 0;
+}
diff --git a/builtin/remote.c b/builtin/remote.c
new file mode 100644
index 0000000..920262d
--- /dev/null
+++ b/builtin/remote.c
@@ -0,0 +1,1603 @@
+#include "builtin.h"
+#include "parse-options.h"
+#include "transport.h"
+#include "remote.h"
+#include "string-list.h"
+#include "strbuf.h"
+#include "run-command.h"
+#include "refs.h"
+
+static const char * const builtin_remote_usage[] = {
+	"git remote [-v | --verbose]",
+	"git remote add [-t <branch>] [-m <master>] [-f] [--tags|--no-tags] [--mirror=<fetch|push>] <name> <url>",
+	"git remote rename <old> <new>",
+	"git remote rm <name>",
+	"git remote set-head <name> (-a | -d | <branch>)",
+	"git remote [-v | --verbose] show [-n] <name>",
+	"git remote prune [-n | --dry-run] <name>",
+	"git remote [-v | --verbose] update [-p | --prune] [(<group> | <remote>)...]",
+	"git remote set-branches [--add] <name> <branch>...",
+	"git remote set-url [--push] <name> <newurl> [<oldurl>]",
+	"git remote set-url --add <name> <newurl>",
+	"git remote set-url --delete <name> <url>",
+	NULL
+};
+
+static const char * const builtin_remote_add_usage[] = {
+	"git remote add [<options>] <name> <url>",
+	NULL
+};
+
+static const char * const builtin_remote_rename_usage[] = {
+	"git remote rename <old> <new>",
+	NULL
+};
+
+static const char * const builtin_remote_rm_usage[] = {
+	"git remote rm <name>",
+	NULL
+};
+
+static const char * const builtin_remote_sethead_usage[] = {
+	"git remote set-head <name> (-a | -d | <branch>])",
+	NULL
+};
+
+static const char * const builtin_remote_setbranches_usage[] = {
+	"git remote set-branches <name> <branch>...",
+	"git remote set-branches --add <name> <branch>...",
+	NULL
+};
+
+static const char * const builtin_remote_show_usage[] = {
+	"git remote show [<options>] <name>",
+	NULL
+};
+
+static const char * const builtin_remote_prune_usage[] = {
+	"git remote prune [<options>] <name>",
+	NULL
+};
+
+static const char * const builtin_remote_update_usage[] = {
+	"git remote update [<options>] [<group> | <remote>]...",
+	NULL
+};
+
+static const char * const builtin_remote_seturl_usage[] = {
+	"git remote set-url [--push] <name> <newurl> [<oldurl>]",
+	"git remote set-url --add <name> <newurl>",
+	"git remote set-url --delete <name> <url>",
+	NULL
+};
+
+#define GET_REF_STATES (1<<0)
+#define GET_HEAD_NAMES (1<<1)
+#define GET_PUSH_REF_STATES (1<<2)
+
+static int verbose;
+
+static int show_all(void);
+static int prune_remote(const char *remote, int dry_run);
+
+static inline int postfixcmp(const char *string, const char *postfix)
+{
+	int len1 = strlen(string), len2 = strlen(postfix);
+	if (len1 < len2)
+		return 1;
+	return strcmp(string + len1 - len2, postfix);
+}
+
+static int fetch_remote(const char *name)
+{
+	const char *argv[] = { "fetch", name, NULL, NULL };
+	if (verbose) {
+		argv[1] = "-v";
+		argv[2] = name;
+	}
+	printf_ln(_("Updating %s"), name);
+	if (run_command_v_opt(argv, RUN_GIT_CMD))
+		return error(_("Could not fetch %s"), name);
+	return 0;
+}
+
+enum {
+	TAGS_UNSET = 0,
+	TAGS_DEFAULT = 1,
+	TAGS_SET = 2
+};
+
+#define MIRROR_NONE 0
+#define MIRROR_FETCH 1
+#define MIRROR_PUSH 2
+#define MIRROR_BOTH (MIRROR_FETCH|MIRROR_PUSH)
+
+static int add_branch(const char *key, const char *branchname,
+		const char *remotename, int mirror, struct strbuf *tmp)
+{
+	strbuf_reset(tmp);
+	strbuf_addch(tmp, '+');
+	if (mirror)
+		strbuf_addf(tmp, "refs/%s:refs/%s",
+				branchname, branchname);
+	else
+		strbuf_addf(tmp, "refs/heads/%s:refs/remotes/%s/%s",
+				branchname, remotename, branchname);
+	return git_config_set_multivar(key, tmp->buf, "^$", 0);
+}
+
+static const char mirror_advice[] =
+N_("--mirror is dangerous and deprecated; please\n"
+   "\t use --mirror=fetch or --mirror=push instead");
+
+static int parse_mirror_opt(const struct option *opt, const char *arg, int not)
+{
+	unsigned *mirror = opt->value;
+	if (not)
+		*mirror = MIRROR_NONE;
+	else if (!arg) {
+		warning("%s", _(mirror_advice));
+		*mirror = MIRROR_BOTH;
+	}
+	else if (!strcmp(arg, "fetch"))
+		*mirror = MIRROR_FETCH;
+	else if (!strcmp(arg, "push"))
+		*mirror = MIRROR_PUSH;
+	else
+		return error(_("unknown mirror argument: %s"), arg);
+	return 0;
+}
+
+static int add(int argc, const char **argv)
+{
+	int fetch = 0, fetch_tags = TAGS_DEFAULT;
+	unsigned mirror = MIRROR_NONE;
+	struct string_list track = STRING_LIST_INIT_NODUP;
+	const char *master = NULL;
+	struct remote *remote;
+	struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT;
+	const char *name, *url;
+	int i;
+
+	struct option options[] = {
+		OPT_BOOLEAN('f', "fetch", &fetch, "fetch the remote branches"),
+		OPT_SET_INT(0, "tags", &fetch_tags,
+			    "import all tags and associated objects when fetching",
+			    TAGS_SET),
+		OPT_SET_INT(0, NULL, &fetch_tags,
+			    "or do not fetch any tag at all (--no-tags)", TAGS_UNSET),
+		OPT_STRING_LIST('t', "track", &track, "branch",
+				"branch(es) to track"),
+		OPT_STRING('m', "master", &master, "branch", "master branch"),
+		{ OPTION_CALLBACK, 0, "mirror", &mirror, "push|fetch",
+			"set up remote as a mirror to push to or fetch from",
+			PARSE_OPT_OPTARG, parse_mirror_opt },
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, NULL, options, builtin_remote_add_usage,
+			     0);
+
+	if (argc < 2)
+		usage_with_options(builtin_remote_add_usage, options);
+
+	if (mirror && master)
+		die(_("specifying a master branch makes no sense with --mirror"));
+	if (mirror && !(mirror & MIRROR_FETCH) && track.nr)
+		die(_("specifying branches to track makes sense only with fetch mirrors"));
+
+	name = argv[0];
+	url = argv[1];
+
+	remote = remote_get(name);
+	if (remote && (remote->url_nr > 1 || strcmp(name, remote->url[0]) ||
+			remote->fetch_refspec_nr))
+		die(_("remote %s already exists."), name);
+
+	strbuf_addf(&buf2, "refs/heads/test:refs/remotes/%s/test", name);
+	if (!valid_fetch_refspec(buf2.buf))
+		die(_("'%s' is not a valid remote name"), name);
+
+	strbuf_addf(&buf, "remote.%s.url", name);
+	if (git_config_set(buf.buf, url))
+		return 1;
+
+	if (!mirror || mirror & MIRROR_FETCH) {
+		strbuf_reset(&buf);
+		strbuf_addf(&buf, "remote.%s.fetch", name);
+		if (track.nr == 0)
+			string_list_append(&track, "*");
+		for (i = 0; i < track.nr; i++) {
+			if (add_branch(buf.buf, track.items[i].string,
+				       name, mirror, &buf2))
+				return 1;
+		}
+	}
+
+	if (mirror & MIRROR_PUSH) {
+		strbuf_reset(&buf);
+		strbuf_addf(&buf, "remote.%s.mirror", name);
+		if (git_config_set(buf.buf, "true"))
+			return 1;
+	}
+
+	if (fetch_tags != TAGS_DEFAULT) {
+		strbuf_reset(&buf);
+		strbuf_addf(&buf, "remote.%s.tagopt", name);
+		if (git_config_set(buf.buf,
+			fetch_tags == TAGS_SET ? "--tags" : "--no-tags"))
+			return 1;
+	}
+
+	if (fetch && fetch_remote(name))
+		return 1;
+
+	if (master) {
+		strbuf_reset(&buf);
+		strbuf_addf(&buf, "refs/remotes/%s/HEAD", name);
+
+		strbuf_reset(&buf2);
+		strbuf_addf(&buf2, "refs/remotes/%s/%s", name, master);
+
+		if (create_symref(buf.buf, buf2.buf, "remote add"))
+			return error(_("Could not setup master '%s'"), master);
+	}
+
+	strbuf_release(&buf);
+	strbuf_release(&buf2);
+	string_list_clear(&track, 0);
+
+	return 0;
+}
+
+struct branch_info {
+	char *remote_name;
+	struct string_list merge;
+	int rebase;
+};
+
+static struct string_list branch_list;
+
+static const char *abbrev_ref(const char *name, const char *prefix)
+{
+	const char *abbrev = skip_prefix(name, prefix);
+	if (abbrev)
+		return abbrev;
+	return name;
+}
+#define abbrev_branch(name) abbrev_ref((name), "refs/heads/")
+
+static int config_read_branches(const char *key, const char *value, void *cb)
+{
+	if (!prefixcmp(key, "branch.")) {
+		const char *orig_key = key;
+		char *name;
+		struct string_list_item *item;
+		struct branch_info *info;
+		enum { REMOTE, MERGE, REBASE } type;
+
+		key += 7;
+		if (!postfixcmp(key, ".remote")) {
+			name = xstrndup(key, strlen(key) - 7);
+			type = REMOTE;
+		} else if (!postfixcmp(key, ".merge")) {
+			name = xstrndup(key, strlen(key) - 6);
+			type = MERGE;
+		} else if (!postfixcmp(key, ".rebase")) {
+			name = xstrndup(key, strlen(key) - 7);
+			type = REBASE;
+		} else
+			return 0;
+
+		item = string_list_insert(&branch_list, name);
+
+		if (!item->util)
+			item->util = xcalloc(sizeof(struct branch_info), 1);
+		info = item->util;
+		if (type == REMOTE) {
+			if (info->remote_name)
+				warning(_("more than one %s"), orig_key);
+			info->remote_name = xstrdup(value);
+		} else if (type == MERGE) {
+			char *space = strchr(value, ' ');
+			value = abbrev_branch(value);
+			while (space) {
+				char *merge;
+				merge = xstrndup(value, space - value);
+				string_list_append(&info->merge, merge);
+				value = abbrev_branch(space + 1);
+				space = strchr(value, ' ');
+			}
+			string_list_append(&info->merge, xstrdup(value));
+		} else
+			info->rebase = git_config_bool(orig_key, value);
+	}
+	return 0;
+}
+
+static void read_branches(void)
+{
+	if (branch_list.nr)
+		return;
+	git_config(config_read_branches, NULL);
+}
+
+struct ref_states {
+	struct remote *remote;
+	struct string_list new, stale, tracked, heads, push;
+	int queried;
+};
+
+static int get_ref_states(const struct ref *remote_refs, struct ref_states *states)
+{
+	struct ref *fetch_map = NULL, **tail = &fetch_map;
+	struct ref *ref, *stale_refs;
+	int i;
+
+	for (i = 0; i < states->remote->fetch_refspec_nr; i++)
+		if (get_fetch_map(remote_refs, states->remote->fetch + i, &tail, 1))
+			die(_("Could not get fetch map for refspec %s"),
+				states->remote->fetch_refspec[i]);
+
+	states->new.strdup_strings = 1;
+	states->tracked.strdup_strings = 1;
+	states->stale.strdup_strings = 1;
+	for (ref = fetch_map; ref; ref = ref->next) {
+		if (!ref->peer_ref || !ref_exists(ref->peer_ref->name))
+			string_list_append(&states->new, abbrev_branch(ref->name));
+		else
+			string_list_append(&states->tracked, abbrev_branch(ref->name));
+	}
+	stale_refs = get_stale_heads(states->remote->fetch,
+				     states->remote->fetch_refspec_nr, fetch_map);
+	for (ref = stale_refs; ref; ref = ref->next) {
+		struct string_list_item *item =
+			string_list_append(&states->stale, abbrev_branch(ref->name));
+		item->util = xstrdup(ref->name);
+	}
+	free_refs(stale_refs);
+	free_refs(fetch_map);
+
+	sort_string_list(&states->new);
+	sort_string_list(&states->tracked);
+	sort_string_list(&states->stale);
+
+	return 0;
+}
+
+struct push_info {
+	char *dest;
+	int forced;
+	enum {
+		PUSH_STATUS_CREATE = 0,
+		PUSH_STATUS_DELETE,
+		PUSH_STATUS_UPTODATE,
+		PUSH_STATUS_FASTFORWARD,
+		PUSH_STATUS_OUTOFDATE,
+		PUSH_STATUS_NOTQUERIED
+	} status;
+};
+
+static int get_push_ref_states(const struct ref *remote_refs,
+	struct ref_states *states)
+{
+	struct remote *remote = states->remote;
+	struct ref *ref, *local_refs, *push_map;
+	if (remote->mirror)
+		return 0;
+
+	local_refs = get_local_heads();
+	push_map = copy_ref_list(remote_refs);
+
+	match_push_refs(local_refs, &push_map, remote->push_refspec_nr,
+			remote->push_refspec, MATCH_REFS_NONE);
+
+	states->push.strdup_strings = 1;
+	for (ref = push_map; ref; ref = ref->next) {
+		struct string_list_item *item;
+		struct push_info *info;
+
+		if (!ref->peer_ref)
+			continue;
+		hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
+
+		item = string_list_append(&states->push,
+					  abbrev_branch(ref->peer_ref->name));
+		item->util = xcalloc(sizeof(struct push_info), 1);
+		info = item->util;
+		info->forced = ref->force;
+		info->dest = xstrdup(abbrev_branch(ref->name));
+
+		if (is_null_sha1(ref->new_sha1)) {
+			info->status = PUSH_STATUS_DELETE;
+		} else if (!hashcmp(ref->old_sha1, ref->new_sha1))
+			info->status = PUSH_STATUS_UPTODATE;
+		else if (is_null_sha1(ref->old_sha1))
+			info->status = PUSH_STATUS_CREATE;
+		else if (has_sha1_file(ref->old_sha1) &&
+			 ref_newer(ref->new_sha1, ref->old_sha1))
+			info->status = PUSH_STATUS_FASTFORWARD;
+		else
+			info->status = PUSH_STATUS_OUTOFDATE;
+	}
+	free_refs(local_refs);
+	free_refs(push_map);
+	return 0;
+}
+
+static int get_push_ref_states_noquery(struct ref_states *states)
+{
+	int i;
+	struct remote *remote = states->remote;
+	struct string_list_item *item;
+	struct push_info *info;
+
+	if (remote->mirror)
+		return 0;
+
+	states->push.strdup_strings = 1;
+	if (!remote->push_refspec_nr) {
+		item = string_list_append(&states->push, _("(matching)"));
+		info = item->util = xcalloc(sizeof(struct push_info), 1);
+		info->status = PUSH_STATUS_NOTQUERIED;
+		info->dest = xstrdup(item->string);
+	}
+	for (i = 0; i < remote->push_refspec_nr; i++) {
+		struct refspec *spec = remote->push + i;
+		if (spec->matching)
+			item = string_list_append(&states->push, _("(matching)"));
+		else if (strlen(spec->src))
+			item = string_list_append(&states->push, spec->src);
+		else
+			item = string_list_append(&states->push, _("(delete)"));
+
+		info = item->util = xcalloc(sizeof(struct push_info), 1);
+		info->forced = spec->force;
+		info->status = PUSH_STATUS_NOTQUERIED;
+		info->dest = xstrdup(spec->dst ? spec->dst : item->string);
+	}
+	return 0;
+}
+
+static int get_head_names(const struct ref *remote_refs, struct ref_states *states)
+{
+	struct ref *ref, *matches;
+	struct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map;
+	struct refspec refspec;
+
+	refspec.force = 0;
+	refspec.pattern = 1;
+	refspec.src = refspec.dst = "refs/heads/*";
+	states->heads.strdup_strings = 1;
+	get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0);
+	matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
+				    fetch_map, 1);
+	for (ref = matches; ref; ref = ref->next)
+		string_list_append(&states->heads, abbrev_branch(ref->name));
+
+	free_refs(fetch_map);
+	free_refs(matches);
+
+	return 0;
+}
+
+struct known_remote {
+	struct known_remote *next;
+	struct remote *remote;
+};
+
+struct known_remotes {
+	struct remote *to_delete;
+	struct known_remote *list;
+};
+
+static int add_known_remote(struct remote *remote, void *cb_data)
+{
+	struct known_remotes *all = cb_data;
+	struct known_remote *r;
+
+	if (!strcmp(all->to_delete->name, remote->name))
+		return 0;
+
+	r = xmalloc(sizeof(*r));
+	r->remote = remote;
+	r->next = all->list;
+	all->list = r;
+	return 0;
+}
+
+struct branches_for_remote {
+	struct remote *remote;
+	struct string_list *branches, *skipped;
+	struct known_remotes *keep;
+};
+
+static int add_branch_for_removal(const char *refname,
+	const unsigned char *sha1, int flags, void *cb_data)
+{
+	struct branches_for_remote *branches = cb_data;
+	struct refspec refspec;
+	struct string_list_item *item;
+	struct known_remote *kr;
+
+	memset(&refspec, 0, sizeof(refspec));
+	refspec.dst = (char *)refname;
+	if (remote_find_tracking(branches->remote, &refspec))
+		return 0;
+
+	/* don't delete a branch if another remote also uses it */
+	for (kr = branches->keep->list; kr; kr = kr->next) {
+		memset(&refspec, 0, sizeof(refspec));
+		refspec.dst = (char *)refname;
+		if (!remote_find_tracking(kr->remote, &refspec))
+			return 0;
+	}
+
+	/* don't delete non-remote-tracking refs */
+	if (prefixcmp(refname, "refs/remotes/")) {
+		/* advise user how to delete local branches */
+		if (!prefixcmp(refname, "refs/heads/"))
+			string_list_append(branches->skipped,
+					   abbrev_branch(refname));
+		/* silently skip over other non-remote refs */
+		return 0;
+	}
+
+	/* make sure that symrefs are deleted */
+	if (flags & REF_ISSYMREF)
+		return unlink(git_path("%s", refname));
+
+	item = string_list_append(branches->branches, refname);
+	item->util = xmalloc(20);
+	hashcpy(item->util, sha1);
+
+	return 0;
+}
+
+struct rename_info {
+	const char *old;
+	const char *new;
+	struct string_list *remote_branches;
+};
+
+static int read_remote_branches(const char *refname,
+	const unsigned char *sha1, int flags, void *cb_data)
+{
+	struct rename_info *rename = cb_data;
+	struct strbuf buf = STRBUF_INIT;
+	struct string_list_item *item;
+	int flag;
+	unsigned char orig_sha1[20];
+	const char *symref;
+
+	strbuf_addf(&buf, "refs/remotes/%s/", rename->old);
+	if (!prefixcmp(refname, buf.buf)) {
+		item = string_list_append(rename->remote_branches, xstrdup(refname));
+		symref = resolve_ref_unsafe(refname, orig_sha1, 1, &flag);
+		if (flag & REF_ISSYMREF)
+			item->util = xstrdup(symref);
+		else
+			item->util = NULL;
+	}
+
+	return 0;
+}
+
+static int migrate_file(struct remote *remote)
+{
+	struct strbuf buf = STRBUF_INIT;
+	int i;
+	char *path = NULL;
+
+	strbuf_addf(&buf, "remote.%s.url", remote->name);
+	for (i = 0; i < remote->url_nr; i++)
+		if (git_config_set_multivar(buf.buf, remote->url[i], "^$", 0))
+			return error(_("Could not append '%s' to '%s'"),
+					remote->url[i], buf.buf);
+	strbuf_reset(&buf);
+	strbuf_addf(&buf, "remote.%s.push", remote->name);
+	for (i = 0; i < remote->push_refspec_nr; i++)
+		if (git_config_set_multivar(buf.buf, remote->push_refspec[i], "^$", 0))
+			return error(_("Could not append '%s' to '%s'"),
+					remote->push_refspec[i], buf.buf);
+	strbuf_reset(&buf);
+	strbuf_addf(&buf, "remote.%s.fetch", remote->name);
+	for (i = 0; i < remote->fetch_refspec_nr; i++)
+		if (git_config_set_multivar(buf.buf, remote->fetch_refspec[i], "^$", 0))
+			return error(_("Could not append '%s' to '%s'"),
+					remote->fetch_refspec[i], buf.buf);
+	if (remote->origin == REMOTE_REMOTES)
+		path = git_path("remotes/%s", remote->name);
+	else if (remote->origin == REMOTE_BRANCHES)
+		path = git_path("branches/%s", remote->name);
+	if (path)
+		unlink_or_warn(path);
+	return 0;
+}
+
+static int mv(int argc, const char **argv)
+{
+	struct option options[] = {
+		OPT_END()
+	};
+	struct remote *oldremote, *newremote;
+	struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT, buf3 = STRBUF_INIT,
+		old_remote_context = STRBUF_INIT;
+	struct string_list remote_branches = STRING_LIST_INIT_NODUP;
+	struct rename_info rename;
+	int i, refspec_updated = 0;
+
+	if (argc != 3)
+		usage_with_options(builtin_remote_rename_usage, options);
+
+	rename.old = argv[1];
+	rename.new = argv[2];
+	rename.remote_branches = &remote_branches;
+
+	oldremote = remote_get(rename.old);
+	if (!oldremote)
+		die(_("No such remote: %s"), rename.old);
+
+	if (!strcmp(rename.old, rename.new) && oldremote->origin != REMOTE_CONFIG)
+		return migrate_file(oldremote);
+
+	newremote = remote_get(rename.new);
+	if (newremote && (newremote->url_nr > 1 || newremote->fetch_refspec_nr))
+		die(_("remote %s already exists."), rename.new);
+
+	strbuf_addf(&buf, "refs/heads/test:refs/remotes/%s/test", rename.new);
+	if (!valid_fetch_refspec(buf.buf))
+		die(_("'%s' is not a valid remote name"), rename.new);
+
+	strbuf_reset(&buf);
+	strbuf_addf(&buf, "remote.%s", rename.old);
+	strbuf_addf(&buf2, "remote.%s", rename.new);
+	if (git_config_rename_section(buf.buf, buf2.buf) < 1)
+		return error(_("Could not rename config section '%s' to '%s'"),
+				buf.buf, buf2.buf);
+
+	strbuf_reset(&buf);
+	strbuf_addf(&buf, "remote.%s.fetch", rename.new);
+	if (git_config_set_multivar(buf.buf, NULL, NULL, 1))
+		return error(_("Could not remove config section '%s'"), buf.buf);
+	strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old);
+	for (i = 0; i < oldremote->fetch_refspec_nr; i++) {
+		char *ptr;
+
+		strbuf_reset(&buf2);
+		strbuf_addstr(&buf2, oldremote->fetch_refspec[i]);
+		ptr = strstr(buf2.buf, old_remote_context.buf);
+		if (ptr) {
+			refspec_updated = 1;
+			strbuf_splice(&buf2,
+				      ptr-buf2.buf + strlen(":refs/remotes/"),
+				      strlen(rename.old), rename.new,
+				      strlen(rename.new));
+		} else
+			warning(_("Not updating non-default fetch refspec\n"
+				  "\t%s\n"
+				  "\tPlease update the configuration manually if necessary."),
+				buf2.buf);
+
+		if (git_config_set_multivar(buf.buf, buf2.buf, "^$", 0))
+			return error(_("Could not append '%s'"), buf.buf);
+	}
+
+	read_branches();
+	for (i = 0; i < branch_list.nr; i++) {
+		struct string_list_item *item = branch_list.items + i;
+		struct branch_info *info = item->util;
+		if (info->remote_name && !strcmp(info->remote_name, rename.old)) {
+			strbuf_reset(&buf);
+			strbuf_addf(&buf, "branch.%s.remote", item->string);
+			if (git_config_set(buf.buf, rename.new)) {
+				return error(_("Could not set '%s'"), buf.buf);
+			}
+		}
+	}
+
+	if (!refspec_updated)
+		return 0;
+
+	/*
+	 * First remove symrefs, then rename the rest, finally create
+	 * the new symrefs.
+	 */
+	for_each_ref(read_remote_branches, &rename);
+	for (i = 0; i < remote_branches.nr; i++) {
+		struct string_list_item *item = remote_branches.items + i;
+		int flag = 0;
+		unsigned char sha1[20];
+
+		read_ref_full(item->string, sha1, 1, &flag);
+		if (!(flag & REF_ISSYMREF))
+			continue;
+		if (delete_ref(item->string, NULL, REF_NODEREF))
+			die(_("deleting '%s' failed"), item->string);
+	}
+	for (i = 0; i < remote_branches.nr; i++) {
+		struct string_list_item *item = remote_branches.items + i;
+
+		if (item->util)
+			continue;
+		strbuf_reset(&buf);
+		strbuf_addstr(&buf, item->string);
+		strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old),
+				rename.new, strlen(rename.new));
+		strbuf_reset(&buf2);
+		strbuf_addf(&buf2, "remote: renamed %s to %s",
+				item->string, buf.buf);
+		if (rename_ref(item->string, buf.buf, buf2.buf))
+			die(_("renaming '%s' failed"), item->string);
+	}
+	for (i = 0; i < remote_branches.nr; i++) {
+		struct string_list_item *item = remote_branches.items + i;
+
+		if (!item->util)
+			continue;
+		strbuf_reset(&buf);
+		strbuf_addstr(&buf, item->string);
+		strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old),
+				rename.new, strlen(rename.new));
+		strbuf_reset(&buf2);
+		strbuf_addstr(&buf2, item->util);
+		strbuf_splice(&buf2, strlen("refs/remotes/"), strlen(rename.old),
+				rename.new, strlen(rename.new));
+		strbuf_reset(&buf3);
+		strbuf_addf(&buf3, "remote: renamed %s to %s",
+				item->string, buf.buf);
+		if (create_symref(buf.buf, buf2.buf, buf3.buf))
+			die(_("creating '%s' failed"), buf.buf);
+	}
+	return 0;
+}
+
+static int remove_branches(struct string_list *branches)
+{
+	int i, result = 0;
+	for (i = 0; i < branches->nr; i++) {
+		struct string_list_item *item = branches->items + i;
+		const char *refname = item->string;
+		unsigned char *sha1 = item->util;
+
+		if (delete_ref(refname, sha1, 0))
+			result |= error(_("Could not remove branch %s"), refname);
+	}
+	return result;
+}
+
+static int rm(int argc, const char **argv)
+{
+	struct option options[] = {
+		OPT_END()
+	};
+	struct remote *remote;
+	struct strbuf buf = STRBUF_INIT;
+	struct known_remotes known_remotes = { NULL, NULL };
+	struct string_list branches = STRING_LIST_INIT_DUP;
+	struct string_list skipped = STRING_LIST_INIT_DUP;
+	struct branches_for_remote cb_data;
+	int i, result;
+
+	memset(&cb_data, 0, sizeof(cb_data));
+	cb_data.branches = &branches;
+	cb_data.skipped = &skipped;
+	cb_data.keep = &known_remotes;
+
+	if (argc != 2)
+		usage_with_options(builtin_remote_rm_usage, options);
+
+	remote = remote_get(argv[1]);
+	if (!remote)
+		die(_("No such remote: %s"), argv[1]);
+
+	known_remotes.to_delete = remote;
+	for_each_remote(add_known_remote, &known_remotes);
+
+	strbuf_addf(&buf, "remote.%s", remote->name);
+	if (git_config_rename_section(buf.buf, NULL) < 1)
+		return error(_("Could not remove config section '%s'"), buf.buf);
+
+	read_branches();
+	for (i = 0; i < branch_list.nr; i++) {
+		struct string_list_item *item = branch_list.items + i;
+		struct branch_info *info = item->util;
+		if (info->remote_name && !strcmp(info->remote_name, remote->name)) {
+			const char *keys[] = { "remote", "merge", NULL }, **k;
+			for (k = keys; *k; k++) {
+				strbuf_reset(&buf);
+				strbuf_addf(&buf, "branch.%s.%s",
+						item->string, *k);
+				if (git_config_set(buf.buf, NULL)) {
+					strbuf_release(&buf);
+					return -1;
+				}
+			}
+		}
+	}
+
+	/*
+	 * We cannot just pass a function to for_each_ref() which deletes
+	 * the branches one by one, since for_each_ref() relies on cached
+	 * refs, which are invalidated when deleting a branch.
+	 */
+	cb_data.remote = remote;
+	result = for_each_ref(add_branch_for_removal, &cb_data);
+	strbuf_release(&buf);
+
+	if (!result)
+		result = remove_branches(&branches);
+	string_list_clear(&branches, 1);
+
+	if (skipped.nr) {
+		fprintf_ln(stderr,
+			   Q_("Note: A branch outside the refs/remotes/ hierarchy was not removed;\n"
+			      "to delete it, use:",
+			      "Note: Some branches outside the refs/remotes/ hierarchy were not removed;\n"
+			      "to delete them, use:",
+			      skipped.nr));
+		for (i = 0; i < skipped.nr; i++)
+			fprintf(stderr, "  git branch -d %s\n",
+				skipped.items[i].string);
+	}
+	string_list_clear(&skipped, 0);
+
+	return result;
+}
+
+static void clear_push_info(void *util, const char *string)
+{
+	struct push_info *info = util;
+	free(info->dest);
+	free(info);
+}
+
+static void free_remote_ref_states(struct ref_states *states)
+{
+	string_list_clear(&states->new, 0);
+	string_list_clear(&states->stale, 1);
+	string_list_clear(&states->tracked, 0);
+	string_list_clear(&states->heads, 0);
+	string_list_clear_func(&states->push, clear_push_info);
+}
+
+static int append_ref_to_tracked_list(const char *refname,
+	const unsigned char *sha1, int flags, void *cb_data)
+{
+	struct ref_states *states = cb_data;
+	struct refspec refspec;
+
+	if (flags & REF_ISSYMREF)
+		return 0;
+
+	memset(&refspec, 0, sizeof(refspec));
+	refspec.dst = (char *)refname;
+	if (!remote_find_tracking(states->remote, &refspec))
+		string_list_append(&states->tracked, abbrev_branch(refspec.src));
+
+	return 0;
+}
+
+static int get_remote_ref_states(const char *name,
+				 struct ref_states *states,
+				 int query)
+{
+	struct transport *transport;
+	const struct ref *remote_refs;
+
+	states->remote = remote_get(name);
+	if (!states->remote)
+		return error(_("No such remote: %s"), name);
+
+	read_branches();
+
+	if (query) {
+		transport = transport_get(states->remote, states->remote->url_nr > 0 ?
+			states->remote->url[0] : NULL);
+		remote_refs = transport_get_remote_refs(transport);
+		transport_disconnect(transport);
+
+		states->queried = 1;
+		if (query & GET_REF_STATES)
+			get_ref_states(remote_refs, states);
+		if (query & GET_HEAD_NAMES)
+			get_head_names(remote_refs, states);
+		if (query & GET_PUSH_REF_STATES)
+			get_push_ref_states(remote_refs, states);
+	} else {
+		for_each_ref(append_ref_to_tracked_list, states);
+		sort_string_list(&states->tracked);
+		get_push_ref_states_noquery(states);
+	}
+
+	return 0;
+}
+
+struct show_info {
+	struct string_list *list;
+	struct ref_states *states;
+	int width, width2;
+	int any_rebase;
+};
+
+static int add_remote_to_show_info(struct string_list_item *item, void *cb_data)
+{
+	struct show_info *info = cb_data;
+	int n = strlen(item->string);
+	if (n > info->width)
+		info->width = n;
+	string_list_insert(info->list, item->string);
+	return 0;
+}
+
+static int show_remote_info_item(struct string_list_item *item, void *cb_data)
+{
+	struct show_info *info = cb_data;
+	struct ref_states *states = info->states;
+	const char *name = item->string;
+
+	if (states->queried) {
+		const char *fmt = "%s";
+		const char *arg = "";
+		if (string_list_has_string(&states->new, name)) {
+			fmt = _(" new (next fetch will store in remotes/%s)");
+			arg = states->remote->name;
+		} else if (string_list_has_string(&states->tracked, name))
+			arg = _(" tracked");
+		else if (string_list_has_string(&states->stale, name))
+			arg = _(" stale (use 'git remote prune' to remove)");
+		else
+			arg = _(" ???");
+		printf("    %-*s", info->width, name);
+		printf(fmt, arg);
+		printf("\n");
+	} else
+		printf("    %s\n", name);
+
+	return 0;
+}
+
+static int add_local_to_show_info(struct string_list_item *branch_item, void *cb_data)
+{
+	struct show_info *show_info = cb_data;
+	struct ref_states *states = show_info->states;
+	struct branch_info *branch_info = branch_item->util;
+	struct string_list_item *item;
+	int n;
+
+	if (!branch_info->merge.nr || !branch_info->remote_name ||
+	    strcmp(states->remote->name, branch_info->remote_name))
+		return 0;
+	if ((n = strlen(branch_item->string)) > show_info->width)
+		show_info->width = n;
+	if (branch_info->rebase)
+		show_info->any_rebase = 1;
+
+	item = string_list_insert(show_info->list, branch_item->string);
+	item->util = branch_info;
+
+	return 0;
+}
+
+static int show_local_info_item(struct string_list_item *item, void *cb_data)
+{
+	struct show_info *show_info = cb_data;
+	struct branch_info *branch_info = item->util;
+	struct string_list *merge = &branch_info->merge;
+	const char *also;
+	int i;
+
+	if (branch_info->rebase && branch_info->merge.nr > 1) {
+		error(_("invalid branch.%s.merge; cannot rebase onto > 1 branch"),
+			item->string);
+		return 0;
+	}
+
+	printf("    %-*s ", show_info->width, item->string);
+	if (branch_info->rebase) {
+		printf_ln(_("rebases onto remote %s"), merge->items[0].string);
+		return 0;
+	} else if (show_info->any_rebase) {
+		printf_ln(_(" merges with remote %s"), merge->items[0].string);
+		also = _("    and with remote");
+	} else {
+		printf_ln(_("merges with remote %s"), merge->items[0].string);
+		also = _("   and with remote");
+	}
+	for (i = 1; i < merge->nr; i++)
+		printf("    %-*s %s %s\n", show_info->width, "", also,
+		       merge->items[i].string);
+
+	return 0;
+}
+
+static int add_push_to_show_info(struct string_list_item *push_item, void *cb_data)
+{
+	struct show_info *show_info = cb_data;
+	struct push_info *push_info = push_item->util;
+	struct string_list_item *item;
+	int n;
+	if ((n = strlen(push_item->string)) > show_info->width)
+		show_info->width = n;
+	if ((n = strlen(push_info->dest)) > show_info->width2)
+		show_info->width2 = n;
+	item = string_list_append(show_info->list, push_item->string);
+	item->util = push_item->util;
+	return 0;
+}
+
+/*
+ * Sorting comparison for a string list that has push_info
+ * structs in its util field
+ */
+static int cmp_string_with_push(const void *va, const void *vb)
+{
+	const struct string_list_item *a = va;
+	const struct string_list_item *b = vb;
+	const struct push_info *a_push = a->util;
+	const struct push_info *b_push = b->util;
+	int cmp = strcmp(a->string, b->string);
+	return cmp ? cmp : strcmp(a_push->dest, b_push->dest);
+}
+
+static int show_push_info_item(struct string_list_item *item, void *cb_data)
+{
+	struct show_info *show_info = cb_data;
+	struct push_info *push_info = item->util;
+	const char *src = item->string, *status = NULL;
+
+	switch (push_info->status) {
+	case PUSH_STATUS_CREATE:
+		status = _("create");
+		break;
+	case PUSH_STATUS_DELETE:
+		status = _("delete");
+		src = _("(none)");
+		break;
+	case PUSH_STATUS_UPTODATE:
+		status = _("up to date");
+		break;
+	case PUSH_STATUS_FASTFORWARD:
+		status = _("fast-forwardable");
+		break;
+	case PUSH_STATUS_OUTOFDATE:
+		status = _("local out of date");
+		break;
+	case PUSH_STATUS_NOTQUERIED:
+		break;
+	}
+	if (status) {
+		if (push_info->forced)
+			printf_ln(_("    %-*s forces to %-*s (%s)"), show_info->width, src,
+			       show_info->width2, push_info->dest, status);
+		else
+			printf_ln(_("    %-*s pushes to %-*s (%s)"), show_info->width, src,
+			       show_info->width2, push_info->dest, status);
+	} else {
+		if (push_info->forced)
+			printf_ln(_("    %-*s forces to %s"), show_info->width, src,
+			       push_info->dest);
+		else
+			printf_ln(_("    %-*s pushes to %s"), show_info->width, src,
+			       push_info->dest);
+	}
+	return 0;
+}
+
+static int show(int argc, const char **argv)
+{
+	int no_query = 0, result = 0, query_flag = 0;
+	struct option options[] = {
+		OPT_BOOLEAN('n', NULL, &no_query, "do not query remotes"),
+		OPT_END()
+	};
+	struct ref_states states;
+	struct string_list info_list = STRING_LIST_INIT_NODUP;
+	struct show_info info;
+
+	argc = parse_options(argc, argv, NULL, options, builtin_remote_show_usage,
+			     0);
+
+	if (argc < 1)
+		return show_all();
+
+	if (!no_query)
+		query_flag = (GET_REF_STATES | GET_HEAD_NAMES | GET_PUSH_REF_STATES);
+
+	memset(&states, 0, sizeof(states));
+	memset(&info, 0, sizeof(info));
+	info.states = &states;
+	info.list = &info_list;
+	for (; argc; argc--, argv++) {
+		int i;
+		const char **url;
+		int url_nr;
+
+		get_remote_ref_states(*argv, &states, query_flag);
+
+		printf_ln(_("* remote %s"), *argv);
+		printf_ln(_("  Fetch URL: %s"), states.remote->url_nr > 0 ?
+		       states.remote->url[0] : _("(no URL)"));
+		if (states.remote->pushurl_nr) {
+			url = states.remote->pushurl;
+			url_nr = states.remote->pushurl_nr;
+		} else {
+			url = states.remote->url;
+			url_nr = states.remote->url_nr;
+		}
+		for (i = 0; i < url_nr; i++)
+			printf_ln(_("  Push  URL: %s"), url[i]);
+		if (!i)
+			printf_ln(_("  Push  URL: %s"), "(no URL)");
+		if (no_query)
+			printf_ln(_("  HEAD branch: %s"), "(not queried)");
+		else if (!states.heads.nr)
+			printf_ln(_("  HEAD branch: %s"), "(unknown)");
+		else if (states.heads.nr == 1)
+			printf_ln(_("  HEAD branch: %s"), states.heads.items[0].string);
+		else {
+			printf(_("  HEAD branch (remote HEAD is ambiguous,"
+				 " may be one of the following):\n"));
+			for (i = 0; i < states.heads.nr; i++)
+				printf("    %s\n", states.heads.items[i].string);
+		}
+
+		/* remote branch info */
+		info.width = 0;
+		for_each_string_list(&states.new, add_remote_to_show_info, &info);
+		for_each_string_list(&states.tracked, add_remote_to_show_info, &info);
+		for_each_string_list(&states.stale, add_remote_to_show_info, &info);
+		if (info.list->nr)
+			printf_ln(Q_("  Remote branch:%s",
+				     "  Remote branches:%s",
+				     info.list->nr),
+				  no_query ? _(" (status not queried)") : "");
+		for_each_string_list(info.list, show_remote_info_item, &info);
+		string_list_clear(info.list, 0);
+
+		/* git pull info */
+		info.width = 0;
+		info.any_rebase = 0;
+		for_each_string_list(&branch_list, add_local_to_show_info, &info);
+		if (info.list->nr)
+			printf_ln(Q_("  Local branch configured for 'git pull':",
+				     "  Local branches configured for 'git pull':",
+				     info.list->nr));
+		for_each_string_list(info.list, show_local_info_item, &info);
+		string_list_clear(info.list, 0);
+
+		/* git push info */
+		if (states.remote->mirror)
+			printf_ln(_("  Local refs will be mirrored by 'git push'"));
+
+		info.width = info.width2 = 0;
+		for_each_string_list(&states.push, add_push_to_show_info, &info);
+		qsort(info.list->items, info.list->nr,
+			sizeof(*info.list->items), cmp_string_with_push);
+		if (info.list->nr)
+			printf_ln(Q_("  Local ref configured for 'git push'%s:",
+				     "  Local refs configured for 'git push'%s:",
+				     info.list->nr),
+				  no_query ? _(" (status not queried)") : "");
+		for_each_string_list(info.list, show_push_info_item, &info);
+		string_list_clear(info.list, 0);
+
+		free_remote_ref_states(&states);
+	}
+
+	return result;
+}
+
+static int set_head(int argc, const char **argv)
+{
+	int i, opt_a = 0, opt_d = 0, result = 0;
+	struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT;
+	char *head_name = NULL;
+
+	struct option options[] = {
+		OPT_BOOLEAN('a', "auto", &opt_a,
+			    "set refs/remotes/<name>/HEAD according to remote"),
+		OPT_BOOLEAN('d', "delete", &opt_d,
+			    "delete refs/remotes/<name>/HEAD"),
+		OPT_END()
+	};
+	argc = parse_options(argc, argv, NULL, options, builtin_remote_sethead_usage,
+			     0);
+	if (argc)
+		strbuf_addf(&buf, "refs/remotes/%s/HEAD", argv[0]);
+
+	if (!opt_a && !opt_d && argc == 2) {
+		head_name = xstrdup(argv[1]);
+	} else if (opt_a && !opt_d && argc == 1) {
+		struct ref_states states;
+		memset(&states, 0, sizeof(states));
+		get_remote_ref_states(argv[0], &states, GET_HEAD_NAMES);
+		if (!states.heads.nr)
+			result |= error(_("Cannot determine remote HEAD"));
+		else if (states.heads.nr > 1) {
+			result |= error(_("Multiple remote HEAD branches. "
+					  "Please choose one explicitly with:"));
+			for (i = 0; i < states.heads.nr; i++)
+				fprintf(stderr, "  git remote set-head %s %s\n",
+					argv[0], states.heads.items[i].string);
+		} else
+			head_name = xstrdup(states.heads.items[0].string);
+		free_remote_ref_states(&states);
+	} else if (opt_d && !opt_a && argc == 1) {
+		if (delete_ref(buf.buf, NULL, REF_NODEREF))
+			result |= error(_("Could not delete %s"), buf.buf);
+	} else
+		usage_with_options(builtin_remote_sethead_usage, options);
+
+	if (head_name) {
+		strbuf_addf(&buf2, "refs/remotes/%s/%s", argv[0], head_name);
+		/* make sure it's valid */
+		if (!ref_exists(buf2.buf))
+			result |= error(_("Not a valid ref: %s"), buf2.buf);
+		else if (create_symref(buf.buf, buf2.buf, "remote set-head"))
+			result |= error(_("Could not setup %s"), buf.buf);
+		if (opt_a)
+			printf("%s/HEAD set to %s\n", argv[0], head_name);
+		free(head_name);
+	}
+
+	strbuf_release(&buf);
+	strbuf_release(&buf2);
+	return result;
+}
+
+static int prune(int argc, const char **argv)
+{
+	int dry_run = 0, result = 0;
+	struct option options[] = {
+		OPT__DRY_RUN(&dry_run, "dry run"),
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, NULL, options, builtin_remote_prune_usage,
+			     0);
+
+	if (argc < 1)
+		usage_with_options(builtin_remote_prune_usage, options);
+
+	for (; argc; argc--, argv++)
+		result |= prune_remote(*argv, dry_run);
+
+	return result;
+}
+
+static int prune_remote(const char *remote, int dry_run)
+{
+	int result = 0, i;
+	struct ref_states states;
+	const char *dangling_msg = dry_run
+		? _(" %s will become dangling!")
+		: _(" %s has become dangling!");
+
+	memset(&states, 0, sizeof(states));
+	get_remote_ref_states(remote, &states, GET_REF_STATES);
+
+	if (states.stale.nr) {
+		printf_ln(_("Pruning %s"), remote);
+		printf_ln(_("URL: %s"),
+		       states.remote->url_nr
+		       ? states.remote->url[0]
+		       : _("(no URL)"));
+	}
+
+	for (i = 0; i < states.stale.nr; i++) {
+		const char *refname = states.stale.items[i].util;
+
+		if (!dry_run)
+			result |= delete_ref(refname, NULL, 0);
+
+		if (dry_run)
+			printf_ln(_(" * [would prune] %s"),
+			       abbrev_ref(refname, "refs/remotes/"));
+		else
+			printf_ln(_(" * [pruned] %s"),
+			       abbrev_ref(refname, "refs/remotes/"));
+		warn_dangling_symref(stdout, dangling_msg, refname);
+	}
+
+	free_remote_ref_states(&states);
+	return result;
+}
+
+static int get_remote_default(const char *key, const char *value, void *priv)
+{
+	if (strcmp(key, "remotes.default") == 0) {
+		int *found = priv;
+		*found = 1;
+	}
+	return 0;
+}
+
+static int update(int argc, const char **argv)
+{
+	int i, prune = 0;
+	struct option options[] = {
+		OPT_BOOLEAN('p', "prune", &prune,
+			    "prune remotes after fetching"),
+		OPT_END()
+	};
+	const char **fetch_argv;
+	int fetch_argc = 0;
+	int default_defined = 0;
+
+	fetch_argv = xmalloc(sizeof(char *) * (argc+5));
+
+	argc = parse_options(argc, argv, NULL, options, builtin_remote_update_usage,
+			     PARSE_OPT_KEEP_ARGV0);
+
+	fetch_argv[fetch_argc++] = "fetch";
+
+	if (prune)
+		fetch_argv[fetch_argc++] = "--prune";
+	if (verbose)
+		fetch_argv[fetch_argc++] = "-v";
+	fetch_argv[fetch_argc++] = "--multiple";
+	if (argc < 2)
+		fetch_argv[fetch_argc++] = "default";
+	for (i = 1; i < argc; i++)
+		fetch_argv[fetch_argc++] = argv[i];
+
+	if (strcmp(fetch_argv[fetch_argc-1], "default") == 0) {
+		git_config(get_remote_default, &default_defined);
+		if (!default_defined)
+			fetch_argv[fetch_argc-1] = "--all";
+	}
+
+	fetch_argv[fetch_argc] = NULL;
+
+	return run_command_v_opt(fetch_argv, RUN_GIT_CMD);
+}
+
+static int remove_all_fetch_refspecs(const char *remote, const char *key)
+{
+	return git_config_set_multivar(key, NULL, NULL, 1);
+}
+
+static int add_branches(struct remote *remote, const char **branches,
+			const char *key)
+{
+	const char *remotename = remote->name;
+	int mirror = remote->mirror;
+	struct strbuf refspec = STRBUF_INIT;
+
+	for (; *branches; branches++)
+		if (add_branch(key, *branches, remotename, mirror, &refspec)) {
+			strbuf_release(&refspec);
+			return 1;
+		}
+
+	strbuf_release(&refspec);
+	return 0;
+}
+
+static int set_remote_branches(const char *remotename, const char **branches,
+				int add_mode)
+{
+	struct strbuf key = STRBUF_INIT;
+	struct remote *remote;
+
+	strbuf_addf(&key, "remote.%s.fetch", remotename);
+
+	if (!remote_is_configured(remotename))
+		die(_("No such remote '%s'"), remotename);
+	remote = remote_get(remotename);
+
+	if (!add_mode && remove_all_fetch_refspecs(remotename, key.buf)) {
+		strbuf_release(&key);
+		return 1;
+	}
+	if (add_branches(remote, branches, key.buf)) {
+		strbuf_release(&key);
+		return 1;
+	}
+
+	strbuf_release(&key);
+	return 0;
+}
+
+static int set_branches(int argc, const char **argv)
+{
+	int add_mode = 0;
+	struct option options[] = {
+		OPT_BOOLEAN('\0', "add", &add_mode, "add branch"),
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, NULL, options,
+			     builtin_remote_setbranches_usage, 0);
+	if (argc == 0) {
+		error(_("no remote specified"));
+		usage_with_options(builtin_remote_setbranches_usage, options);
+	}
+	argv[argc] = NULL;
+
+	return set_remote_branches(argv[0], argv + 1, add_mode);
+}
+
+static int set_url(int argc, const char **argv)
+{
+	int i, push_mode = 0, add_mode = 0, delete_mode = 0;
+	int matches = 0, negative_matches = 0;
+	const char *remotename = NULL;
+	const char *newurl = NULL;
+	const char *oldurl = NULL;
+	struct remote *remote;
+	regex_t old_regex;
+	const char **urlset;
+	int urlset_nr;
+	struct strbuf name_buf = STRBUF_INIT;
+	struct option options[] = {
+		OPT_BOOLEAN('\0', "push", &push_mode,
+			    "manipulate push URLs"),
+		OPT_BOOLEAN('\0', "add", &add_mode,
+			    "add URL"),
+		OPT_BOOLEAN('\0', "delete", &delete_mode,
+			    "delete URLs"),
+		OPT_END()
+	};
+	argc = parse_options(argc, argv, NULL, options, builtin_remote_seturl_usage,
+			     PARSE_OPT_KEEP_ARGV0);
+
+	if (add_mode && delete_mode)
+		die(_("--add --delete doesn't make sense"));
+
+	if (argc < 3 || argc > 4 || ((add_mode || delete_mode) && argc != 3))
+		usage_with_options(builtin_remote_seturl_usage, options);
+
+	remotename = argv[1];
+	newurl = argv[2];
+	if (argc > 3)
+		oldurl = argv[3];
+
+	if (delete_mode)
+		oldurl = newurl;
+
+	if (!remote_is_configured(remotename))
+		die(_("No such remote '%s'"), remotename);
+	remote = remote_get(remotename);
+
+	if (push_mode) {
+		strbuf_addf(&name_buf, "remote.%s.pushurl", remotename);
+		urlset = remote->pushurl;
+		urlset_nr = remote->pushurl_nr;
+	} else {
+		strbuf_addf(&name_buf, "remote.%s.url", remotename);
+		urlset = remote->url;
+		urlset_nr = remote->url_nr;
+	}
+
+	/* Special cases that add new entry. */
+	if ((!oldurl && !delete_mode) || add_mode) {
+		if (add_mode)
+			git_config_set_multivar(name_buf.buf, newurl,
+				"^$", 0);
+		else
+			git_config_set(name_buf.buf, newurl);
+		strbuf_release(&name_buf);
+		return 0;
+	}
+
+	/* Old URL specified. Demand that one matches. */
+	if (regcomp(&old_regex, oldurl, REG_EXTENDED))
+		die(_("Invalid old URL pattern: %s"), oldurl);
+
+	for (i = 0; i < urlset_nr; i++)
+		if (!regexec(&old_regex, urlset[i], 0, NULL, 0))
+			matches++;
+		else
+			negative_matches++;
+	if (!delete_mode && !matches)
+		die(_("No such URL found: %s"), oldurl);
+	if (delete_mode && !negative_matches && !push_mode)
+		die(_("Will not delete all non-push URLs"));
+
+	regfree(&old_regex);
+
+	if (!delete_mode)
+		git_config_set_multivar(name_buf.buf, newurl, oldurl, 0);
+	else
+		git_config_set_multivar(name_buf.buf, NULL, oldurl, 1);
+	return 0;
+}
+
+static int get_one_entry(struct remote *remote, void *priv)
+{
+	struct string_list *list = priv;
+	struct strbuf url_buf = STRBUF_INIT;
+	const char **url;
+	int i, url_nr;
+
+	if (remote->url_nr > 0) {
+		strbuf_addf(&url_buf, "%s (fetch)", remote->url[0]);
+		string_list_append(list, remote->name)->util =
+				strbuf_detach(&url_buf, NULL);
+	} else
+		string_list_append(list, remote->name)->util = NULL;
+	if (remote->pushurl_nr) {
+		url = remote->pushurl;
+		url_nr = remote->pushurl_nr;
+	} else {
+		url = remote->url;
+		url_nr = remote->url_nr;
+	}
+	for (i = 0; i < url_nr; i++)
+	{
+		strbuf_addf(&url_buf, "%s (push)", url[i]);
+		string_list_append(list, remote->name)->util =
+				strbuf_detach(&url_buf, NULL);
+	}
+
+	return 0;
+}
+
+static int show_all(void)
+{
+	struct string_list list = STRING_LIST_INIT_NODUP;
+	int result;
+
+	list.strdup_strings = 1;
+	result = for_each_remote(get_one_entry, &list);
+
+	if (!result) {
+		int i;
+
+		sort_string_list(&list);
+		for (i = 0; i < list.nr; i++) {
+			struct string_list_item *item = list.items + i;
+			if (verbose)
+				printf("%s\t%s\n", item->string,
+					item->util ? (const char *)item->util : "");
+			else {
+				if (i && !strcmp((item - 1)->string, item->string))
+					continue;
+				printf("%s\n", item->string);
+			}
+		}
+	}
+	string_list_clear(&list, 1);
+	return result;
+}
+
+int cmd_remote(int argc, const char **argv, const char *prefix)
+{
+	struct option options[] = {
+		OPT__VERBOSE(&verbose, "be verbose; must be placed before a subcommand"),
+		OPT_END()
+	};
+	int result;
+
+	argc = parse_options(argc, argv, prefix, options, builtin_remote_usage,
+		PARSE_OPT_STOP_AT_NON_OPTION);
+
+	if (argc < 1)
+		result = show_all();
+	else if (!strcmp(argv[0], "add"))
+		result = add(argc, argv);
+	else if (!strcmp(argv[0], "rename"))
+		result = mv(argc, argv);
+	else if (!strcmp(argv[0], "rm"))
+		result = rm(argc, argv);
+	else if (!strcmp(argv[0], "set-head"))
+		result = set_head(argc, argv);
+	else if (!strcmp(argv[0], "set-branches"))
+		result = set_branches(argc, argv);
+	else if (!strcmp(argv[0], "set-url"))
+		result = set_url(argc, argv);
+	else if (!strcmp(argv[0], "show"))
+		result = show(argc, argv);
+	else if (!strcmp(argv[0], "prune"))
+		result = prune(argc, argv);
+	else if (!strcmp(argv[0], "update"))
+		result = update(argc, argv);
+	else {
+		error(_("Unknown subcommand: %s"), argv[0]);
+		usage_with_options(builtin_remote_usage, options);
+	}
+
+	return result ? 1 : 0;
+}
diff --git a/builtin/replace.c b/builtin/replace.c
new file mode 100644
index 0000000..4a8970e
--- /dev/null
+++ b/builtin/replace.c
@@ -0,0 +1,159 @@
+/*
+ * Builtin "git replace"
+ *
+ * Copyright (c) 2008 Christian Couder <chriscool@tuxfamily.org>
+ *
+ * Based on builtin-tag.c by Kristian Høgsberg <krh@redhat.com>
+ * and Carlos Rica <jasampler@gmail.com> that was itself based on
+ * git-tag.sh and mktag.c by Linus Torvalds.
+ */
+
+#include "cache.h"
+#include "builtin.h"
+#include "refs.h"
+#include "parse-options.h"
+
+static const char * const git_replace_usage[] = {
+	"git replace [-f] <object> <replacement>",
+	"git replace -d <object>...",
+	"git replace -l [<pattern>]",
+	NULL
+};
+
+static int show_reference(const char *refname, const unsigned char *sha1,
+			  int flag, void *cb_data)
+{
+	const char *pattern = cb_data;
+
+	if (!fnmatch(pattern, refname, 0))
+		printf("%s\n", refname);
+
+	return 0;
+}
+
+static int list_replace_refs(const char *pattern)
+{
+	if (pattern == NULL)
+		pattern = "*";
+
+	for_each_replace_ref(show_reference, (void *) pattern);
+
+	return 0;
+}
+
+typedef int (*each_replace_name_fn)(const char *name, const char *ref,
+				    const unsigned char *sha1);
+
+static int for_each_replace_name(const char **argv, each_replace_name_fn fn)
+{
+	const char **p;
+	char ref[PATH_MAX];
+	int had_error = 0;
+	unsigned char sha1[20];
+
+	for (p = argv; *p; p++) {
+		if (snprintf(ref, sizeof(ref), "refs/replace/%s", *p)
+					>= sizeof(ref)) {
+			error("replace ref name too long: %.*s...", 50, *p);
+			had_error = 1;
+			continue;
+		}
+		if (read_ref(ref, sha1)) {
+			error("replace ref '%s' not found.", *p);
+			had_error = 1;
+			continue;
+		}
+		if (fn(*p, ref, sha1))
+			had_error = 1;
+	}
+	return had_error;
+}
+
+static int delete_replace_ref(const char *name, const char *ref,
+			      const unsigned char *sha1)
+{
+	if (delete_ref(ref, sha1, 0))
+		return 1;
+	printf("Deleted replace ref '%s'\n", name);
+	return 0;
+}
+
+static int replace_object(const char *object_ref, const char *replace_ref,
+			  int force)
+{
+	unsigned char object[20], prev[20], repl[20];
+	char ref[PATH_MAX];
+	struct ref_lock *lock;
+
+	if (get_sha1(object_ref, object))
+		die("Failed to resolve '%s' as a valid ref.", object_ref);
+	if (get_sha1(replace_ref, repl))
+		die("Failed to resolve '%s' as a valid ref.", replace_ref);
+
+	if (snprintf(ref, sizeof(ref),
+		     "refs/replace/%s",
+		     sha1_to_hex(object)) > sizeof(ref) - 1)
+		die("replace ref name too long: %.*s...", 50, ref);
+	if (check_refname_format(ref, 0))
+		die("'%s' is not a valid ref name.", ref);
+
+	if (read_ref(ref, prev))
+		hashclr(prev);
+	else if (!force)
+		die("replace ref '%s' already exists", ref);
+
+	lock = lock_any_ref_for_update(ref, prev, 0);
+	if (!lock)
+		die("%s: cannot lock the ref", ref);
+	if (write_ref_sha1(lock, repl, NULL) < 0)
+		die("%s: cannot update the ref", ref);
+
+	return 0;
+}
+
+int cmd_replace(int argc, const char **argv, const char *prefix)
+{
+	int list = 0, delete = 0, force = 0;
+	struct option options[] = {
+		OPT_BOOLEAN('l', NULL, &list, "list replace refs"),
+		OPT_BOOLEAN('d', NULL, &delete, "delete replace refs"),
+		OPT_BOOLEAN('f', NULL, &force, "replace the ref if it exists"),
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, prefix, options, git_replace_usage, 0);
+
+	if (list && delete)
+		usage_msg_opt("-l and -d cannot be used together",
+			      git_replace_usage, options);
+
+	if (force && (list || delete))
+		usage_msg_opt("-f cannot be used with -d or -l",
+			      git_replace_usage, options);
+
+	/* Delete refs */
+	if (delete) {
+		if (argc < 1)
+			usage_msg_opt("-d needs at least one argument",
+				      git_replace_usage, options);
+		return for_each_replace_name(argv, delete_replace_ref);
+	}
+
+	/* Replace object */
+	if (!list && argc) {
+		if (argc != 2)
+			usage_msg_opt("bad number of arguments",
+				      git_replace_usage, options);
+		return replace_object(argv[0], argv[1], force);
+	}
+
+	/* List refs, even if "list" is not set */
+	if (argc > 1)
+		usage_msg_opt("only one pattern can be given with -l",
+			      git_replace_usage, options);
+	if (force)
+		usage_msg_opt("-f needs some arguments",
+			      git_replace_usage, options);
+
+	return list_replace_refs(argv[0]);
+}
diff --git a/builtin/rerere.c b/builtin/rerere.c
new file mode 100644
index 0000000..08213c7
--- /dev/null
+++ b/builtin/rerere.c
@@ -0,0 +1,110 @@
+#include "builtin.h"
+#include "cache.h"
+#include "dir.h"
+#include "parse-options.h"
+#include "string-list.h"
+#include "rerere.h"
+#include "xdiff/xdiff.h"
+#include "xdiff-interface.h"
+
+static const char * const rerere_usage[] = {
+	"git rerere [clear | forget path... | status | remaining | diff | gc]",
+	NULL,
+};
+
+static int outf(void *dummy, mmbuffer_t *ptr, int nbuf)
+{
+	int i;
+	for (i = 0; i < nbuf; i++)
+		if (write_in_full(1, ptr[i].ptr, ptr[i].size) != ptr[i].size)
+			return -1;
+	return 0;
+}
+
+static int diff_two(const char *file1, const char *label1,
+		const char *file2, const char *label2)
+{
+	xpparam_t xpp;
+	xdemitconf_t xecfg;
+	xdemitcb_t ecb;
+	mmfile_t minus, plus;
+
+	if (read_mmfile(&minus, file1) || read_mmfile(&plus, file2))
+		return 1;
+
+	printf("--- a/%s\n+++ b/%s\n", label1, label2);
+	fflush(stdout);
+	memset(&xpp, 0, sizeof(xpp));
+	xpp.flags = 0;
+	memset(&xecfg, 0, sizeof(xecfg));
+	xecfg.ctxlen = 3;
+	ecb.outf = outf;
+	xdi_diff(&minus, &plus, &xpp, &xecfg, &ecb);
+
+	free(minus.ptr);
+	free(plus.ptr);
+	return 0;
+}
+
+int cmd_rerere(int argc, const char **argv, const char *prefix)
+{
+	struct string_list merge_rr = STRING_LIST_INIT_DUP;
+	int i, fd, autoupdate = -1, flags = 0;
+
+	struct option options[] = {
+		OPT_SET_INT(0, "rerere-autoupdate", &autoupdate,
+			"register clean resolutions in index", 1),
+		OPT_END(),
+	};
+
+	argc = parse_options(argc, argv, prefix, options, rerere_usage, 0);
+
+	if (autoupdate == 1)
+		flags = RERERE_AUTOUPDATE;
+	if (autoupdate == 0)
+		flags = RERERE_NOAUTOUPDATE;
+
+	if (argc < 1)
+		return rerere(flags);
+
+	if (!strcmp(argv[0], "forget")) {
+		const char **pathspec;
+		if (argc < 2)
+			warning("'git rerere forget' without paths is deprecated");
+		pathspec = get_pathspec(prefix, argv + 1);
+		return rerere_forget(pathspec);
+	}
+
+	fd = setup_rerere(&merge_rr, flags);
+	if (fd < 0)
+		return 0;
+
+	if (!strcmp(argv[0], "clear")) {
+		rerere_clear(&merge_rr);
+	} else if (!strcmp(argv[0], "gc"))
+		rerere_gc(&merge_rr);
+	else if (!strcmp(argv[0], "status"))
+		for (i = 0; i < merge_rr.nr; i++)
+			printf("%s\n", merge_rr.items[i].string);
+	else if (!strcmp(argv[0], "remaining")) {
+		rerere_remaining(&merge_rr);
+		for (i = 0; i < merge_rr.nr; i++) {
+			if (merge_rr.items[i].util != RERERE_RESOLVED)
+				printf("%s\n", merge_rr.items[i].string);
+			else
+				/* prepare for later call to
+				 * string_list_clear() */
+				merge_rr.items[i].util = NULL;
+		}
+	} else if (!strcmp(argv[0], "diff"))
+		for (i = 0; i < merge_rr.nr; i++) {
+			const char *path = merge_rr.items[i].string;
+			const char *name = (const char *)merge_rr.items[i].util;
+			diff_two(rerere_path(name, "preimage"), path, path, path);
+		}
+	else
+		usage_with_options(rerere_usage, options);
+
+	string_list_clear(&merge_rr, 1);
+	return 0;
+}
diff --git a/builtin/reset.c b/builtin/reset.c
new file mode 100644
index 0000000..74442bd
--- /dev/null
+++ b/builtin/reset.c
@@ -0,0 +1,382 @@
+/*
+ * "git reset" builtin command
+ *
+ * Copyright (c) 2007 Carlos Rica
+ *
+ * Based on git-reset.sh, which is
+ *
+ * Copyright (c) 2005, 2006 Linus Torvalds and Junio C Hamano
+ */
+#include "builtin.h"
+#include "tag.h"
+#include "object.h"
+#include "commit.h"
+#include "run-command.h"
+#include "refs.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "tree.h"
+#include "branch.h"
+#include "parse-options.h"
+#include "unpack-trees.h"
+#include "cache-tree.h"
+
+static const char * const git_reset_usage[] = {
+	"git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]",
+	"git reset [-q] <commit> [--] <paths>...",
+	"git reset --patch [<commit>] [--] [<paths>...]",
+	NULL
+};
+
+enum reset_type { MIXED, SOFT, HARD, MERGE, KEEP, NONE };
+static const char *reset_type_names[] = {
+	N_("mixed"), N_("soft"), N_("hard"), N_("merge"), N_("keep"), NULL
+};
+
+static inline int is_merge(void)
+{
+	return !access(git_path("MERGE_HEAD"), F_OK);
+}
+
+static int reset_index_file(const unsigned char *sha1, int reset_type, int quiet)
+{
+	int nr = 1;
+	int newfd;
+	struct tree_desc desc[2];
+	struct tree *tree;
+	struct unpack_trees_options opts;
+	struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
+
+	memset(&opts, 0, sizeof(opts));
+	opts.head_idx = 1;
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+	opts.fn = oneway_merge;
+	opts.merge = 1;
+	if (!quiet)
+		opts.verbose_update = 1;
+	switch (reset_type) {
+	case KEEP:
+	case MERGE:
+		opts.update = 1;
+		break;
+	case HARD:
+		opts.update = 1;
+		/* fallthrough */
+	default:
+		opts.reset = 1;
+	}
+
+	newfd = hold_locked_index(lock, 1);
+
+	read_cache_unmerged();
+
+	if (reset_type == KEEP) {
+		unsigned char head_sha1[20];
+		if (get_sha1("HEAD", head_sha1))
+			return error(_("You do not have a valid HEAD."));
+		if (!fill_tree_descriptor(desc, head_sha1))
+			return error(_("Failed to find tree of HEAD."));
+		nr++;
+		opts.fn = twoway_merge;
+	}
+
+	if (!fill_tree_descriptor(desc + nr - 1, sha1))
+		return error(_("Failed to find tree of %s."), sha1_to_hex(sha1));
+	if (unpack_trees(nr, desc, &opts))
+		return -1;
+
+	if (reset_type == MIXED || reset_type == HARD) {
+		tree = parse_tree_indirect(sha1);
+		prime_cache_tree(&active_cache_tree, tree);
+	}
+
+	if (write_cache(newfd, active_cache, active_nr) ||
+	    commit_locked_index(lock))
+		return error(_("Could not write new index file."));
+
+	return 0;
+}
+
+static void print_new_head_line(struct commit *commit)
+{
+	const char *hex, *body;
+
+	hex = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
+	printf(_("HEAD is now at %s"), hex);
+	body = strstr(commit->buffer, "\n\n");
+	if (body) {
+		const char *eol;
+		size_t len;
+		body += 2;
+		eol = strchr(body, '\n');
+		len = eol ? eol - body : strlen(body);
+		printf(" %.*s\n", (int) len, body);
+	}
+	else
+		printf("\n");
+}
+
+static int update_index_refresh(int fd, struct lock_file *index_lock, int flags)
+{
+	int result;
+
+	if (!index_lock) {
+		index_lock = xcalloc(1, sizeof(struct lock_file));
+		fd = hold_locked_index(index_lock, 1);
+	}
+
+	if (read_cache() < 0)
+		return error(_("Could not read index"));
+
+	result = refresh_index(&the_index, (flags), NULL, NULL,
+			       _("Unstaged changes after reset:")) ? 1 : 0;
+	if (write_cache(fd, active_cache, active_nr) ||
+			commit_locked_index(index_lock))
+		return error ("Could not refresh index");
+	return result;
+}
+
+static void update_index_from_diff(struct diff_queue_struct *q,
+		struct diff_options *opt, void *data)
+{
+	int i;
+	int *discard_flag = data;
+
+	/* do_diff_cache() mangled the index */
+	discard_cache();
+	*discard_flag = 1;
+	read_cache();
+
+	for (i = 0; i < q->nr; i++) {
+		struct diff_filespec *one = q->queue[i]->one;
+		if (one->mode && !is_null_sha1(one->sha1)) {
+			struct cache_entry *ce;
+			ce = make_cache_entry(one->mode, one->sha1, one->path,
+				0, 0);
+			if (!ce)
+				die(_("make_cache_entry failed for path '%s'"),
+				    one->path);
+			add_cache_entry(ce, ADD_CACHE_OK_TO_ADD |
+				ADD_CACHE_OK_TO_REPLACE);
+		} else
+			remove_file_from_cache(one->path);
+	}
+}
+
+static int interactive_reset(const char *revision, const char **argv,
+			     const char *prefix)
+{
+	const char **pathspec = NULL;
+
+	if (*argv)
+		pathspec = get_pathspec(prefix, argv);
+
+	return run_add_interactive(revision, "--patch=reset", pathspec);
+}
+
+static int read_from_tree(const char *prefix, const char **argv,
+		unsigned char *tree_sha1, int refresh_flags)
+{
+	struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
+	int index_fd, index_was_discarded = 0;
+	struct diff_options opt;
+
+	memset(&opt, 0, sizeof(opt));
+	diff_tree_setup_paths(get_pathspec(prefix, (const char **)argv), &opt);
+	opt.output_format = DIFF_FORMAT_CALLBACK;
+	opt.format_callback = update_index_from_diff;
+	opt.format_callback_data = &index_was_discarded;
+
+	index_fd = hold_locked_index(lock, 1);
+	index_was_discarded = 0;
+	read_cache();
+	if (do_diff_cache(tree_sha1, &opt))
+		return 1;
+	diffcore_std(&opt);
+	diff_flush(&opt);
+	diff_tree_release_paths(&opt);
+
+	if (!index_was_discarded)
+		/* The index is still clobbered from do_diff_cache() */
+		discard_cache();
+	return update_index_refresh(index_fd, lock, refresh_flags);
+}
+
+static void set_reflog_message(struct strbuf *sb, const char *action,
+			       const char *rev)
+{
+	const char *rla = getenv("GIT_REFLOG_ACTION");
+
+	strbuf_reset(sb);
+	if (rla)
+		strbuf_addf(sb, "%s: %s", rla, action);
+	else if (rev)
+		strbuf_addf(sb, "reset: moving to %s", rev);
+	else
+		strbuf_addf(sb, "reset: %s", action);
+}
+
+static void die_if_unmerged_cache(int reset_type)
+{
+	if (is_merge() || read_cache() < 0 || unmerged_cache())
+		die(_("Cannot do a %s reset in the middle of a merge."),
+		    _(reset_type_names[reset_type]));
+
+}
+
+int cmd_reset(int argc, const char **argv, const char *prefix)
+{
+	int i = 0, reset_type = NONE, update_ref_status = 0, quiet = 0;
+	int patch_mode = 0;
+	const char *rev = "HEAD";
+	unsigned char sha1[20], *orig = NULL, sha1_orig[20],
+				*old_orig = NULL, sha1_old_orig[20];
+	struct commit *commit;
+	struct strbuf msg = STRBUF_INIT;
+	const struct option options[] = {
+		OPT__QUIET(&quiet, "be quiet, only report errors"),
+		OPT_SET_INT(0, "mixed", &reset_type,
+						"reset HEAD and index", MIXED),
+		OPT_SET_INT(0, "soft", &reset_type, "reset only HEAD", SOFT),
+		OPT_SET_INT(0, "hard", &reset_type,
+				"reset HEAD, index and working tree", HARD),
+		OPT_SET_INT(0, "merge", &reset_type,
+				"reset HEAD, index and working tree", MERGE),
+		OPT_SET_INT(0, "keep", &reset_type,
+				"reset HEAD but keep local changes", KEEP),
+		OPT_BOOLEAN('p', "patch", &patch_mode, "select hunks interactively"),
+		OPT_END()
+	};
+
+	git_config(git_default_config, NULL);
+
+	argc = parse_options(argc, argv, prefix, options, git_reset_usage,
+						PARSE_OPT_KEEP_DASHDASH);
+
+	/*
+	 * Possible arguments are:
+	 *
+	 * git reset [-opts] <rev> <paths>...
+	 * git reset [-opts] <rev> -- <paths>...
+	 * git reset [-opts] -- <paths>...
+	 * git reset [-opts] <paths>...
+	 *
+	 * At this point, argv[i] points immediately after [-opts].
+	 */
+
+	if (i < argc) {
+		if (!strcmp(argv[i], "--")) {
+			i++; /* reset to HEAD, possibly with paths */
+		} else if (i + 1 < argc && !strcmp(argv[i+1], "--")) {
+			rev = argv[i];
+			i += 2;
+		}
+		/*
+		 * Otherwise, argv[i] could be either <rev> or <paths> and
+		 * has to be unambiguous.
+		 */
+		else if (!get_sha1_committish(argv[i], sha1)) {
+			/*
+			 * Ok, argv[i] looks like a rev; it should not
+			 * be a filename.
+			 */
+			verify_non_filename(prefix, argv[i]);
+			rev = argv[i++];
+		} else {
+			/* Otherwise we treat this as a filename */
+			verify_filename(prefix, argv[i], 1);
+		}
+	}
+
+	if (get_sha1_committish(rev, sha1))
+		die(_("Failed to resolve '%s' as a valid ref."), rev);
+
+	/*
+	 * NOTE: As "git reset $treeish -- $path" should be usable on
+	 * any tree-ish, this is not strictly correct. We are not
+	 * moving the HEAD to any commit; we are merely resetting the
+	 * entries in the index to that of a treeish.
+	 */
+	commit = lookup_commit_reference(sha1);
+	if (!commit)
+		die(_("Could not parse object '%s'."), rev);
+	hashcpy(sha1, commit->object.sha1);
+
+	if (patch_mode) {
+		if (reset_type != NONE)
+			die(_("--patch is incompatible with --{hard,mixed,soft}"));
+		return interactive_reset(rev, argv + i, prefix);
+	}
+
+	/* git reset tree [--] paths... can be used to
+	 * load chosen paths from the tree into the index without
+	 * affecting the working tree nor HEAD. */
+	if (i < argc) {
+		if (reset_type == MIXED)
+			warning(_("--mixed with paths is deprecated; use 'git reset -- <paths>' instead."));
+		else if (reset_type != NONE)
+			die(_("Cannot do %s reset with paths."),
+					_(reset_type_names[reset_type]));
+		return read_from_tree(prefix, argv + i, sha1,
+				quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
+	}
+	if (reset_type == NONE)
+		reset_type = MIXED; /* by default */
+
+	if (reset_type != SOFT && reset_type != MIXED)
+		setup_work_tree();
+
+	if (reset_type == MIXED && is_bare_repository())
+		die(_("%s reset is not allowed in a bare repository"),
+		    _(reset_type_names[reset_type]));
+
+	/* Soft reset does not touch the index file nor the working tree
+	 * at all, but requires them in a good order.  Other resets reset
+	 * the index file to the tree object we are switching to. */
+	if (reset_type == SOFT)
+		die_if_unmerged_cache(reset_type);
+	else {
+		int err;
+		if (reset_type == KEEP)
+			die_if_unmerged_cache(reset_type);
+		err = reset_index_file(sha1, reset_type, quiet);
+		if (reset_type == KEEP)
+			err = err || reset_index_file(sha1, MIXED, quiet);
+		if (err)
+			die(_("Could not reset index file to revision '%s'."), rev);
+	}
+
+	/* Any resets update HEAD to the head being switched to,
+	 * saving the previous head in ORIG_HEAD before. */
+	if (!get_sha1("ORIG_HEAD", sha1_old_orig))
+		old_orig = sha1_old_orig;
+	if (!get_sha1("HEAD", sha1_orig)) {
+		orig = sha1_orig;
+		set_reflog_message(&msg, "updating ORIG_HEAD", NULL);
+		update_ref(msg.buf, "ORIG_HEAD", orig, old_orig, 0, MSG_ON_ERR);
+	}
+	else if (old_orig)
+		delete_ref("ORIG_HEAD", old_orig, 0);
+	set_reflog_message(&msg, "updating HEAD", rev);
+	update_ref_status = update_ref(msg.buf, "HEAD", sha1, orig, 0, MSG_ON_ERR);
+
+	switch (reset_type) {
+	case HARD:
+		if (!update_ref_status && !quiet)
+			print_new_head_line(commit);
+		break;
+	case SOFT: /* Nothing else to do. */
+		break;
+	case MIXED: /* Report what has not been updated. */
+		update_index_refresh(0, NULL,
+				quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
+		break;
+	}
+
+	remove_branch_state();
+
+	strbuf_release(&msg);
+
+	return update_ref_status;
+}
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
new file mode 100644
index 0000000..ff5a383
--- /dev/null
+++ b/builtin/rev-list.c
@@ -0,0 +1,413 @@
+#include "cache.h"
+#include "commit.h"
+#include "diff.h"
+#include "revision.h"
+#include "list-objects.h"
+#include "builtin.h"
+#include "log-tree.h"
+#include "graph.h"
+#include "bisect.h"
+
+static const char rev_list_usage[] =
+"git rev-list [OPTION] <commit-id>... [ -- paths... ]\n"
+"  limiting output:\n"
+"    --max-count=<n>\n"
+"    --max-age=<epoch>\n"
+"    --min-age=<epoch>\n"
+"    --sparse\n"
+"    --no-merges\n"
+"    --min-parents=<n>\n"
+"    --no-min-parents\n"
+"    --max-parents=<n>\n"
+"    --no-max-parents\n"
+"    --remove-empty\n"
+"    --all\n"
+"    --branches\n"
+"    --tags\n"
+"    --remotes\n"
+"    --stdin\n"
+"    --quiet\n"
+"  ordering output:\n"
+"    --topo-order\n"
+"    --date-order\n"
+"    --reverse\n"
+"  formatting output:\n"
+"    --parents\n"
+"    --children\n"
+"    --objects | --objects-edge\n"
+"    --unpacked\n"
+"    --header | --pretty\n"
+"    --abbrev=<n> | --no-abbrev\n"
+"    --abbrev-commit\n"
+"    --left-right\n"
+"  special purpose:\n"
+"    --bisect\n"
+"    --bisect-vars\n"
+"    --bisect-all"
+;
+
+static void finish_commit(struct commit *commit, void *data);
+static void show_commit(struct commit *commit, void *data)
+{
+	struct rev_list_info *info = data;
+	struct rev_info *revs = info->revs;
+
+	if (info->flags & REV_LIST_QUIET) {
+		finish_commit(commit, data);
+		return;
+	}
+
+	graph_show_commit(revs->graph);
+
+	if (revs->count) {
+		if (commit->object.flags & PATCHSAME)
+			revs->count_same++;
+		else if (commit->object.flags & SYMMETRIC_LEFT)
+			revs->count_left++;
+		else
+			revs->count_right++;
+		finish_commit(commit, data);
+		return;
+	}
+
+	if (info->show_timestamp)
+		printf("%lu ", commit->date);
+	if (info->header_prefix)
+		fputs(info->header_prefix, stdout);
+
+	if (!revs->graph)
+		fputs(get_revision_mark(revs, commit), stdout);
+	if (revs->abbrev_commit && revs->abbrev)
+		fputs(find_unique_abbrev(commit->object.sha1, revs->abbrev),
+		      stdout);
+	else
+		fputs(sha1_to_hex(commit->object.sha1), stdout);
+	if (revs->print_parents) {
+		struct commit_list *parents = commit->parents;
+		while (parents) {
+			printf(" %s", sha1_to_hex(parents->item->object.sha1));
+			parents = parents->next;
+		}
+	}
+	if (revs->children.name) {
+		struct commit_list *children;
+
+		children = lookup_decoration(&revs->children, &commit->object);
+		while (children) {
+			printf(" %s", sha1_to_hex(children->item->object.sha1));
+			children = children->next;
+		}
+	}
+	show_decorations(revs, commit);
+	if (revs->commit_format == CMIT_FMT_ONELINE)
+		putchar(' ');
+	else
+		putchar('\n');
+
+	if (revs->verbose_header && commit->buffer) {
+		struct strbuf buf = STRBUF_INIT;
+		struct pretty_print_context ctx = {0};
+		ctx.abbrev = revs->abbrev;
+		ctx.date_mode = revs->date_mode;
+		ctx.date_mode_explicit = revs->date_mode_explicit;
+		ctx.fmt = revs->commit_format;
+		pretty_print_commit(&ctx, commit, &buf);
+		if (revs->graph) {
+			if (buf.len) {
+				if (revs->commit_format != CMIT_FMT_ONELINE)
+					graph_show_oneline(revs->graph);
+
+				graph_show_commit_msg(revs->graph, &buf);
+
+				/*
+				 * Add a newline after the commit message.
+				 *
+				 * Usually, this newline produces a blank
+				 * padding line between entries, in which case
+				 * we need to add graph padding on this line.
+				 *
+				 * However, the commit message may not end in a
+				 * newline.  In this case the newline simply
+				 * ends the last line of the commit message,
+				 * and we don't need any graph output.  (This
+				 * always happens with CMIT_FMT_ONELINE, and it
+				 * happens with CMIT_FMT_USERFORMAT when the
+				 * format doesn't explicitly end in a newline.)
+				 */
+				if (buf.len && buf.buf[buf.len - 1] == '\n')
+					graph_show_padding(revs->graph);
+				putchar('\n');
+			} else {
+				/*
+				 * If the message buffer is empty, just show
+				 * the rest of the graph output for this
+				 * commit.
+				 */
+				if (graph_show_remainder(revs->graph))
+					putchar('\n');
+				if (revs->commit_format == CMIT_FMT_ONELINE)
+					putchar('\n');
+			}
+		} else {
+			if (revs->commit_format != CMIT_FMT_USERFORMAT ||
+			    buf.len) {
+				fwrite(buf.buf, 1, buf.len, stdout);
+				putchar(info->hdr_termination);
+			}
+		}
+		strbuf_release(&buf);
+	} else {
+		if (graph_show_remainder(revs->graph))
+			putchar('\n');
+	}
+	maybe_flush_or_die(stdout, "stdout");
+	finish_commit(commit, data);
+}
+
+static void finish_commit(struct commit *commit, void *data)
+{
+	if (commit->parents) {
+		free_commit_list(commit->parents);
+		commit->parents = NULL;
+	}
+	free(commit->buffer);
+	commit->buffer = NULL;
+}
+
+static void finish_object(struct object *obj,
+			  const struct name_path *path, const char *name,
+			  void *cb_data)
+{
+	struct rev_list_info *info = cb_data;
+	if (obj->type == OBJ_BLOB && !has_sha1_file(obj->sha1))
+		die("missing blob object '%s'", sha1_to_hex(obj->sha1));
+	if (info->revs->verify_objects && !obj->parsed && obj->type != OBJ_COMMIT)
+		parse_object(obj->sha1);
+}
+
+static void show_object(struct object *obj,
+			const struct name_path *path, const char *component,
+			void *cb_data)
+{
+	struct rev_list_info *info = cb_data;
+	finish_object(obj, path, component, cb_data);
+	if (info->flags & REV_LIST_QUIET)
+		return;
+	show_object_with_name(stdout, obj, path, component);
+}
+
+static void show_edge(struct commit *commit)
+{
+	printf("-%s\n", sha1_to_hex(commit->object.sha1));
+}
+
+static inline int log2i(int n)
+{
+	int log2 = 0;
+
+	for (; n > 1; n >>= 1)
+		log2++;
+
+	return log2;
+}
+
+static inline int exp2i(int n)
+{
+	return 1 << n;
+}
+
+/*
+ * Estimate the number of bisect steps left (after the current step)
+ *
+ * For any x between 0 included and 2^n excluded, the probability for
+ * n - 1 steps left looks like:
+ *
+ * P(2^n + x) == (2^n - x) / (2^n + x)
+ *
+ * and P(2^n + x) < 0.5 means 2^n < 3x
+ */
+int estimate_bisect_steps(int all)
+{
+	int n, x, e;
+
+	if (all < 3)
+		return 0;
+
+	n = log2i(all);
+	e = exp2i(n);
+	x = all - e;
+
+	return (e < 3 * x) ? n : n - 1;
+}
+
+void print_commit_list(struct commit_list *list,
+		       const char *format_cur,
+		       const char *format_last)
+{
+	for ( ; list; list = list->next) {
+		const char *format = list->next ? format_cur : format_last;
+		printf(format, sha1_to_hex(list->item->object.sha1));
+	}
+}
+
+static void print_var_str(const char *var, const char *val)
+{
+	printf("%s='%s'\n", var, val);
+}
+
+static void print_var_int(const char *var, int val)
+{
+	printf("%s=%d\n", var, val);
+}
+
+static int show_bisect_vars(struct rev_list_info *info, int reaches, int all)
+{
+	int cnt, flags = info->flags;
+	char hex[41] = "";
+	struct commit_list *tried;
+	struct rev_info *revs = info->revs;
+
+	if (!revs->commits)
+		return 1;
+
+	revs->commits = filter_skipped(revs->commits, &tried,
+				       flags & BISECT_SHOW_ALL,
+				       NULL, NULL);
+
+	/*
+	 * revs->commits can reach "reaches" commits among
+	 * "all" commits.  If it is good, then there are
+	 * (all-reaches) commits left to be bisected.
+	 * On the other hand, if it is bad, then the set
+	 * to bisect is "reaches".
+	 * A bisect set of size N has (N-1) commits further
+	 * to test, as we already know one bad one.
+	 */
+	cnt = all - reaches;
+	if (cnt < reaches)
+		cnt = reaches;
+
+	if (revs->commits)
+		strcpy(hex, sha1_to_hex(revs->commits->item->object.sha1));
+
+	if (flags & BISECT_SHOW_ALL) {
+		traverse_commit_list(revs, show_commit, show_object, info);
+		printf("------\n");
+	}
+
+	print_var_str("bisect_rev", hex);
+	print_var_int("bisect_nr", cnt - 1);
+	print_var_int("bisect_good", all - reaches - 1);
+	print_var_int("bisect_bad", reaches - 1);
+	print_var_int("bisect_all", all);
+	print_var_int("bisect_steps", estimate_bisect_steps(all));
+
+	return 0;
+}
+
+int cmd_rev_list(int argc, const char **argv, const char *prefix)
+{
+	struct rev_info revs;
+	struct rev_list_info info;
+	int i;
+	int bisect_list = 0;
+	int bisect_show_vars = 0;
+	int bisect_find_all = 0;
+
+	git_config(git_default_config, NULL);
+	init_revisions(&revs, prefix);
+	revs.abbrev = DEFAULT_ABBREV;
+	revs.commit_format = CMIT_FMT_UNSPECIFIED;
+	argc = setup_revisions(argc, argv, &revs, NULL);
+
+	memset(&info, 0, sizeof(info));
+	info.revs = &revs;
+	if (revs.bisect)
+		bisect_list = 1;
+
+	if (DIFF_OPT_TST(&revs.diffopt, QUICK))
+		info.flags |= REV_LIST_QUIET;
+	for (i = 1 ; i < argc; i++) {
+		const char *arg = argv[i];
+
+		if (!strcmp(arg, "--header")) {
+			revs.verbose_header = 1;
+			continue;
+		}
+		if (!strcmp(arg, "--timestamp")) {
+			info.show_timestamp = 1;
+			continue;
+		}
+		if (!strcmp(arg, "--bisect")) {
+			bisect_list = 1;
+			continue;
+		}
+		if (!strcmp(arg, "--bisect-all")) {
+			bisect_list = 1;
+			bisect_find_all = 1;
+			info.flags |= BISECT_SHOW_ALL;
+			revs.show_decorations = 1;
+			continue;
+		}
+		if (!strcmp(arg, "--bisect-vars")) {
+			bisect_list = 1;
+			bisect_show_vars = 1;
+			continue;
+		}
+		usage(rev_list_usage);
+
+	}
+	if (revs.commit_format != CMIT_FMT_UNSPECIFIED) {
+		/* The command line has a --pretty  */
+		info.hdr_termination = '\n';
+		if (revs.commit_format == CMIT_FMT_ONELINE)
+			info.header_prefix = "";
+		else
+			info.header_prefix = "commit ";
+	}
+	else if (revs.verbose_header)
+		/* Only --header was specified */
+		revs.commit_format = CMIT_FMT_RAW;
+
+	if ((!revs.commits &&
+	     (!(revs.tag_objects||revs.tree_objects||revs.blob_objects) &&
+	      !revs.pending.nr)) ||
+	    revs.diff)
+		usage(rev_list_usage);
+
+	save_commit_buffer = (revs.verbose_header ||
+			      revs.grep_filter.pattern_list ||
+			      revs.grep_filter.header_list);
+	if (bisect_list)
+		revs.limited = 1;
+
+	if (prepare_revision_walk(&revs))
+		die("revision walk setup failed");
+	if (revs.tree_objects)
+		mark_edges_uninteresting(revs.commits, &revs, show_edge);
+
+	if (bisect_list) {
+		int reaches = reaches, all = all;
+
+		revs.commits = find_bisection(revs.commits, &reaches, &all,
+					      bisect_find_all);
+
+		if (bisect_show_vars)
+			return show_bisect_vars(&info, reaches, all);
+	}
+
+	traverse_commit_list(&revs, show_commit, show_object, &info);
+
+	if (revs.count) {
+		if (revs.left_right && revs.cherry_mark)
+			printf("%d\t%d\t%d\n", revs.count_left, revs.count_right, revs.count_same);
+		else if (revs.left_right)
+			printf("%d\t%d\n", revs.count_left, revs.count_right);
+		else if (revs.cherry_mark)
+			printf("%d\t%d\n", revs.count_left + revs.count_right, revs.count_same);
+		else
+			printf("%d\n", revs.count_left + revs.count_right);
+	}
+
+	return 0;
+}
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
new file mode 100644
index 0000000..32788a9
--- /dev/null
+++ b/builtin/rev-parse.c
@@ -0,0 +1,759 @@
+/*
+ * rev-parse.c
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ */
+#include "cache.h"
+#include "commit.h"
+#include "refs.h"
+#include "quote.h"
+#include "builtin.h"
+#include "parse-options.h"
+
+#define DO_REVS		1
+#define DO_NOREV	2
+#define DO_FLAGS	4
+#define DO_NONFLAGS	8
+static int filter = ~0;
+
+static const char *def;
+
+#define NORMAL 0
+#define REVERSED 1
+static int show_type = NORMAL;
+
+#define SHOW_SYMBOLIC_ASIS 1
+#define SHOW_SYMBOLIC_FULL 2
+static int symbolic;
+static int abbrev;
+static int abbrev_ref;
+static int abbrev_ref_strict;
+static int output_sq;
+
+/*
+ * Some arguments are relevant "revision" arguments,
+ * others are about output format or other details.
+ * This sorts it all out.
+ */
+static int is_rev_argument(const char *arg)
+{
+	static const char *rev_args[] = {
+		"--all",
+		"--bisect",
+		"--dense",
+		"--branches=",
+		"--branches",
+		"--header",
+		"--ignore-missing",
+		"--max-age=",
+		"--max-count=",
+		"--min-age=",
+		"--no-merges",
+		"--min-parents=",
+		"--no-min-parents",
+		"--max-parents=",
+		"--no-max-parents",
+		"--objects",
+		"--objects-edge",
+		"--parents",
+		"--pretty",
+		"--remotes=",
+		"--remotes",
+		"--glob=",
+		"--sparse",
+		"--tags=",
+		"--tags",
+		"--topo-order",
+		"--date-order",
+		"--unpacked",
+		NULL
+	};
+	const char **p = rev_args;
+
+	/* accept -<digit>, like traditional "head" */
+	if ((*arg == '-') && isdigit(arg[1]))
+		return 1;
+
+	for (;;) {
+		const char *str = *p++;
+		int len;
+		if (!str)
+			return 0;
+		len = strlen(str);
+		if (!strcmp(arg, str) ||
+		    (str[len-1] == '=' && !strncmp(arg, str, len)))
+			return 1;
+	}
+}
+
+/* Output argument as a string, either SQ or normal */
+static void show(const char *arg)
+{
+	if (output_sq) {
+		int sq = '\'', ch;
+
+		putchar(sq);
+		while ((ch = *arg++)) {
+			if (ch == sq)
+				fputs("'\\'", stdout);
+			putchar(ch);
+		}
+		putchar(sq);
+		putchar(' ');
+	}
+	else
+		puts(arg);
+}
+
+/* Like show(), but with a negation prefix according to type */
+static void show_with_type(int type, const char *arg)
+{
+	if (type != show_type)
+		putchar('^');
+	show(arg);
+}
+
+/* Output a revision, only if filter allows it */
+static void show_rev(int type, const unsigned char *sha1, const char *name)
+{
+	if (!(filter & DO_REVS))
+		return;
+	def = NULL;
+
+	if ((symbolic || abbrev_ref) && name) {
+		if (symbolic == SHOW_SYMBOLIC_FULL || abbrev_ref) {
+			unsigned char discard[20];
+			char *full;
+
+			switch (dwim_ref(name, strlen(name), discard, &full)) {
+			case 0:
+				/*
+				 * Not found -- not a ref.  We could
+				 * emit "name" here, but symbolic-full
+				 * users are interested in finding the
+				 * refs spelled in full, and they would
+				 * need to filter non-refs if we did so.
+				 */
+				break;
+			case 1: /* happy */
+				if (abbrev_ref)
+					full = shorten_unambiguous_ref(full,
+						abbrev_ref_strict);
+				show_with_type(type, full);
+				break;
+			default: /* ambiguous */
+				error("refname '%s' is ambiguous", name);
+				break;
+			}
+		} else {
+			show_with_type(type, name);
+		}
+	}
+	else if (abbrev)
+		show_with_type(type, find_unique_abbrev(sha1, abbrev));
+	else
+		show_with_type(type, sha1_to_hex(sha1));
+}
+
+/* Output a flag, only if filter allows it. */
+static int show_flag(const char *arg)
+{
+	if (!(filter & DO_FLAGS))
+		return 0;
+	if (filter & (is_rev_argument(arg) ? DO_REVS : DO_NOREV)) {
+		show(arg);
+		return 1;
+	}
+	return 0;
+}
+
+static int show_default(void)
+{
+	const char *s = def;
+
+	if (s) {
+		unsigned char sha1[20];
+
+		def = NULL;
+		if (!get_sha1(s, sha1)) {
+			show_rev(NORMAL, sha1, s);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static int show_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+{
+	show_rev(NORMAL, sha1, refname);
+	return 0;
+}
+
+static int anti_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+{
+	show_rev(REVERSED, sha1, refname);
+	return 0;
+}
+
+static int show_abbrev(const unsigned char *sha1, void *cb_data)
+{
+	show_rev(NORMAL, sha1, NULL);
+	return 0;
+}
+
+static void show_datestring(const char *flag, const char *datestr)
+{
+	static char buffer[100];
+
+	/* date handling requires both flags and revs */
+	if ((filter & (DO_FLAGS | DO_REVS)) != (DO_FLAGS | DO_REVS))
+		return;
+	snprintf(buffer, sizeof(buffer), "%s%lu", flag, approxidate(datestr));
+	show(buffer);
+}
+
+static int show_file(const char *arg)
+{
+	show_default();
+	if ((filter & (DO_NONFLAGS|DO_NOREV)) == (DO_NONFLAGS|DO_NOREV)) {
+		show(arg);
+		return 1;
+	}
+	return 0;
+}
+
+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_committish(this, sha1) && !get_sha1_committish(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;
+}
+
+static int try_parent_shorthands(const char *arg)
+{
+	char *dotdot;
+	unsigned char sha1[20];
+	struct commit *commit;
+	struct commit_list *parents;
+	int parents_only;
+
+	if ((dotdot = strstr(arg, "^!")))
+		parents_only = 0;
+	else if ((dotdot = strstr(arg, "^@")))
+		parents_only = 1;
+
+	if (!dotdot || dotdot[2])
+		return 0;
+
+	*dotdot = 0;
+	if (get_sha1_committish(arg, sha1))
+		return 0;
+
+	if (!parents_only)
+		show_rev(NORMAL, sha1, arg);
+	commit = lookup_commit_reference(sha1);
+	for (parents = commit->parents; parents; parents = parents->next)
+		show_rev(parents_only ? NORMAL : REVERSED,
+				parents->item->object.sha1, arg);
+
+	return 1;
+}
+
+static int parseopt_dump(const struct option *o, const char *arg, int unset)
+{
+	struct strbuf *parsed = o->value;
+	if (unset)
+		strbuf_addf(parsed, " --no-%s", o->long_name);
+	else if (o->short_name)
+		strbuf_addf(parsed, " -%c", o->short_name);
+	else
+		strbuf_addf(parsed, " --%s", o->long_name);
+	if (arg) {
+		strbuf_addch(parsed, ' ');
+		sq_quote_buf(parsed, arg);
+	}
+	return 0;
+}
+
+static const char *skipspaces(const char *s)
+{
+	while (isspace(*s))
+		s++;
+	return s;
+}
+
+static int cmd_parseopt(int argc, const char **argv, const char *prefix)
+{
+	static int keep_dashdash = 0, stop_at_non_option = 0;
+	static char const * const parseopt_usage[] = {
+		"git rev-parse --parseopt [options] -- [<args>...]",
+		NULL
+	};
+	static struct option parseopt_opts[] = {
+		OPT_BOOLEAN(0, "keep-dashdash", &keep_dashdash,
+					"keep the `--` passed as an arg"),
+		OPT_BOOLEAN(0, "stop-at-non-option", &stop_at_non_option,
+					"stop parsing after the "
+					"first non-option argument"),
+		OPT_END(),
+	};
+
+	struct strbuf sb = STRBUF_INIT, parsed = STRBUF_INIT;
+	const char **usage = NULL;
+	struct option *opts = NULL;
+	int onb = 0, osz = 0, unb = 0, usz = 0;
+
+	strbuf_addstr(&parsed, "set --");
+	argc = parse_options(argc, argv, prefix, parseopt_opts, parseopt_usage,
+	                     PARSE_OPT_KEEP_DASHDASH);
+	if (argc < 1 || strcmp(argv[0], "--"))
+		usage_with_options(parseopt_usage, parseopt_opts);
+
+	/* get the usage up to the first line with a -- on it */
+	for (;;) {
+		if (strbuf_getline(&sb, stdin, '\n') == EOF)
+			die("premature end of input");
+		ALLOC_GROW(usage, unb + 1, usz);
+		if (!strcmp("--", sb.buf)) {
+			if (unb < 1)
+				die("no usage string given before the `--' separator");
+			usage[unb] = NULL;
+			break;
+		}
+		usage[unb++] = strbuf_detach(&sb, NULL);
+	}
+
+	/* parse: (<short>|<short>,<long>|<long>)[=?]? SP+ <help> */
+	while (strbuf_getline(&sb, stdin, '\n') != EOF) {
+		const char *s;
+		struct option *o;
+
+		if (!sb.len)
+			continue;
+
+		ALLOC_GROW(opts, onb + 1, osz);
+		memset(opts + onb, 0, sizeof(opts[onb]));
+
+		o = &opts[onb++];
+		s = strchr(sb.buf, ' ');
+		if (!s || *sb.buf == ' ') {
+			o->type = OPTION_GROUP;
+			o->help = xstrdup(skipspaces(sb.buf));
+			continue;
+		}
+
+		o->type = OPTION_CALLBACK;
+		o->help = xstrdup(skipspaces(s));
+		o->value = &parsed;
+		o->flags = PARSE_OPT_NOARG;
+		o->callback = &parseopt_dump;
+		while (s > sb.buf && strchr("*=?!", s[-1])) {
+			switch (*--s) {
+			case '=':
+				o->flags &= ~PARSE_OPT_NOARG;
+				break;
+			case '?':
+				o->flags &= ~PARSE_OPT_NOARG;
+				o->flags |= PARSE_OPT_OPTARG;
+				break;
+			case '!':
+				o->flags |= PARSE_OPT_NONEG;
+				break;
+			case '*':
+				o->flags |= PARSE_OPT_HIDDEN;
+				break;
+			}
+		}
+
+		if (s - sb.buf == 1) /* short option only */
+			o->short_name = *sb.buf;
+		else if (sb.buf[1] != ',') /* long option only */
+			o->long_name = xmemdupz(sb.buf, s - sb.buf);
+		else {
+			o->short_name = *sb.buf;
+			o->long_name = xmemdupz(sb.buf + 2, s - sb.buf - 2);
+		}
+	}
+	strbuf_release(&sb);
+
+	/* put an OPT_END() */
+	ALLOC_GROW(opts, onb + 1, osz);
+	memset(opts + onb, 0, sizeof(opts[onb]));
+	argc = parse_options(argc, argv, prefix, opts, usage,
+			(keep_dashdash ? PARSE_OPT_KEEP_DASHDASH : 0) |
+			(stop_at_non_option ? PARSE_OPT_STOP_AT_NON_OPTION : 0) |
+			PARSE_OPT_SHELL_EVAL);
+
+	strbuf_addf(&parsed, " --");
+	sq_quote_argv(&parsed, argv, 0);
+	puts(parsed.buf);
+	return 0;
+}
+
+static int cmd_sq_quote(int argc, const char **argv)
+{
+	struct strbuf buf = STRBUF_INIT;
+
+	if (argc)
+		sq_quote_argv(&buf, argv, 0);
+	printf("%s\n", buf.buf);
+	strbuf_release(&buf);
+
+	return 0;
+}
+
+static void die_no_single_rev(int quiet)
+{
+	if (quiet)
+		exit(1);
+	else
+		die("Needed a single revision");
+}
+
+static const char builtin_rev_parse_usage[] =
+"git rev-parse --parseopt [options] -- [<args>...]\n"
+"   or: git rev-parse --sq-quote [<arg>...]\n"
+"   or: git rev-parse [options] [<arg>...]\n"
+"\n"
+"Run \"git rev-parse --parseopt -h\" for more information on the first usage.";
+
+int cmd_rev_parse(int argc, const char **argv, const char *prefix)
+{
+	int i, as_is = 0, verify = 0, quiet = 0, revs_count = 0, type = 0;
+	unsigned char sha1[20];
+	const char *name = NULL;
+
+	if (argc > 1 && !strcmp("--parseopt", argv[1]))
+		return cmd_parseopt(argc - 1, argv + 1, prefix);
+
+	if (argc > 1 && !strcmp("--sq-quote", argv[1]))
+		return cmd_sq_quote(argc - 2, argv + 2);
+
+	if (argc == 2 && !strcmp("--local-env-vars", argv[1])) {
+		int i;
+		for (i = 0; local_repo_env[i]; i++)
+			printf("%s\n", local_repo_env[i]);
+		return 0;
+	}
+
+	if (argc > 2 && !strcmp(argv[1], "--resolve-git-dir")) {
+		const char *gitdir = resolve_gitdir(argv[2]);
+		if (!gitdir)
+			die("not a gitdir '%s'", argv[2]);
+		puts(gitdir);
+		return 0;
+	}
+
+	if (argc > 1 && !strcmp("-h", argv[1]))
+		usage(builtin_rev_parse_usage);
+
+	prefix = setup_git_directory();
+	git_config(git_default_config, NULL);
+	for (i = 1; i < argc; i++) {
+		const char *arg = argv[i];
+
+		if (as_is) {
+			if (show_file(arg) && as_is < 2)
+				verify_filename(prefix, arg, 0);
+			continue;
+		}
+		if (!strcmp(arg,"-n")) {
+			if (++i >= argc)
+				die("-n requires an argument");
+			if ((filter & DO_FLAGS) && (filter & DO_REVS)) {
+				show(arg);
+				show(argv[i]);
+			}
+			continue;
+		}
+		if (!prefixcmp(arg, "-n")) {
+			if ((filter & DO_FLAGS) && (filter & DO_REVS))
+				show(arg);
+			continue;
+		}
+
+		if (*arg == '-') {
+			if (!strcmp(arg, "--")) {
+				as_is = 2;
+				/* Pass on the "--" if we show anything but files.. */
+				if (filter & (DO_FLAGS | DO_REVS))
+					show_file(arg);
+				continue;
+			}
+			if (!strcmp(arg, "--default")) {
+				def = argv[i+1];
+				i++;
+				continue;
+			}
+			if (!strcmp(arg, "--revs-only")) {
+				filter &= ~DO_NOREV;
+				continue;
+			}
+			if (!strcmp(arg, "--no-revs")) {
+				filter &= ~DO_REVS;
+				continue;
+			}
+			if (!strcmp(arg, "--flags")) {
+				filter &= ~DO_NONFLAGS;
+				continue;
+			}
+			if (!strcmp(arg, "--no-flags")) {
+				filter &= ~DO_FLAGS;
+				continue;
+			}
+			if (!strcmp(arg, "--verify")) {
+				filter &= ~(DO_FLAGS|DO_NOREV);
+				verify = 1;
+				continue;
+			}
+			if (!strcmp(arg, "--quiet") || !strcmp(arg, "-q")) {
+				quiet = 1;
+				continue;
+			}
+			if (!strcmp(arg, "--short") ||
+			    !prefixcmp(arg, "--short=")) {
+				filter &= ~(DO_FLAGS|DO_NOREV);
+				verify = 1;
+				abbrev = DEFAULT_ABBREV;
+				if (arg[7] == '=')
+					abbrev = strtoul(arg + 8, NULL, 10);
+				if (abbrev < MINIMUM_ABBREV)
+					abbrev = MINIMUM_ABBREV;
+				else if (40 <= abbrev)
+					abbrev = 40;
+				continue;
+			}
+			if (!strcmp(arg, "--sq")) {
+				output_sq = 1;
+				continue;
+			}
+			if (!strcmp(arg, "--not")) {
+				show_type ^= REVERSED;
+				continue;
+			}
+			if (!strcmp(arg, "--symbolic")) {
+				symbolic = SHOW_SYMBOLIC_ASIS;
+				continue;
+			}
+			if (!strcmp(arg, "--symbolic-full-name")) {
+				symbolic = SHOW_SYMBOLIC_FULL;
+				continue;
+			}
+			if (!prefixcmp(arg, "--abbrev-ref") &&
+			    (!arg[12] || arg[12] == '=')) {
+				abbrev_ref = 1;
+				abbrev_ref_strict = warn_ambiguous_refs;
+				if (arg[12] == '=') {
+					if (!strcmp(arg + 13, "strict"))
+						abbrev_ref_strict = 1;
+					else if (!strcmp(arg + 13, "loose"))
+						abbrev_ref_strict = 0;
+					else
+						die("unknown mode for %s", arg);
+				}
+				continue;
+			}
+			if (!strcmp(arg, "--all")) {
+				for_each_ref(show_reference, NULL);
+				continue;
+			}
+			if (!prefixcmp(arg, "--disambiguate=")) {
+				for_each_abbrev(arg + 15, show_abbrev, NULL);
+				continue;
+			}
+			if (!strcmp(arg, "--bisect")) {
+				for_each_ref_in("refs/bisect/bad", show_reference, NULL);
+				for_each_ref_in("refs/bisect/good", anti_reference, NULL);
+				continue;
+			}
+			if (!prefixcmp(arg, "--branches=")) {
+				for_each_glob_ref_in(show_reference, arg + 11,
+					"refs/heads/", NULL);
+				continue;
+			}
+			if (!strcmp(arg, "--branches")) {
+				for_each_branch_ref(show_reference, NULL);
+				continue;
+			}
+			if (!prefixcmp(arg, "--tags=")) {
+				for_each_glob_ref_in(show_reference, arg + 7,
+					"refs/tags/", NULL);
+				continue;
+			}
+			if (!strcmp(arg, "--tags")) {
+				for_each_tag_ref(show_reference, NULL);
+				continue;
+			}
+			if (!prefixcmp(arg, "--glob=")) {
+				for_each_glob_ref(show_reference, arg + 7, NULL);
+				continue;
+			}
+			if (!prefixcmp(arg, "--remotes=")) {
+				for_each_glob_ref_in(show_reference, arg + 10,
+					"refs/remotes/", NULL);
+				continue;
+			}
+			if (!strcmp(arg, "--remotes")) {
+				for_each_remote_ref(show_reference, NULL);
+				continue;
+			}
+			if (!strcmp(arg, "--show-toplevel")) {
+				const char *work_tree = get_git_work_tree();
+				if (work_tree)
+					puts(work_tree);
+				continue;
+			}
+			if (!strcmp(arg, "--show-prefix")) {
+				if (prefix)
+					puts(prefix);
+				else
+					putchar('\n');
+				continue;
+			}
+			if (!strcmp(arg, "--show-cdup")) {
+				const char *pfx = prefix;
+				if (!is_inside_work_tree()) {
+					const char *work_tree =
+						get_git_work_tree();
+					if (work_tree)
+						printf("%s\n", work_tree);
+					continue;
+				}
+				while (pfx) {
+					pfx = strchr(pfx, '/');
+					if (pfx) {
+						pfx++;
+						printf("../");
+					}
+				}
+				putchar('\n');
+				continue;
+			}
+			if (!strcmp(arg, "--git-dir")) {
+				const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
+				static char cwd[PATH_MAX];
+				int len;
+				if (gitdir) {
+					puts(gitdir);
+					continue;
+				}
+				if (!prefix) {
+					puts(".git");
+					continue;
+				}
+				if (!getcwd(cwd, PATH_MAX))
+					die_errno("unable to get current working directory");
+				len = strlen(cwd);
+				printf("%s%s.git\n", cwd, len && cwd[len-1] != '/' ? "/" : "");
+				continue;
+			}
+			if (!strcmp(arg, "--is-inside-git-dir")) {
+				printf("%s\n", is_inside_git_dir() ? "true"
+						: "false");
+				continue;
+			}
+			if (!strcmp(arg, "--is-inside-work-tree")) {
+				printf("%s\n", is_inside_work_tree() ? "true"
+						: "false");
+				continue;
+			}
+			if (!strcmp(arg, "--is-bare-repository")) {
+				printf("%s\n", is_bare_repository() ? "true"
+						: "false");
+				continue;
+			}
+			if (!prefixcmp(arg, "--since=")) {
+				show_datestring("--max-age=", arg+8);
+				continue;
+			}
+			if (!prefixcmp(arg, "--after=")) {
+				show_datestring("--max-age=", arg+8);
+				continue;
+			}
+			if (!prefixcmp(arg, "--before=")) {
+				show_datestring("--min-age=", arg+9);
+				continue;
+			}
+			if (!prefixcmp(arg, "--until=")) {
+				show_datestring("--min-age=", arg+8);
+				continue;
+			}
+			if (show_flag(arg) && verify)
+				die_no_single_rev(quiet);
+			continue;
+		}
+
+		/* Not a flag argument */
+		if (try_difference(arg))
+			continue;
+		if (try_parent_shorthands(arg))
+			continue;
+		name = arg;
+		type = NORMAL;
+		if (*arg == '^') {
+			name++;
+			type = REVERSED;
+		}
+		if (!get_sha1(name, sha1)) {
+			if (verify)
+				revs_count++;
+			else
+				show_rev(type, sha1, name);
+			continue;
+		}
+		if (verify)
+			die_no_single_rev(quiet);
+		as_is = 1;
+		if (!show_file(arg))
+			continue;
+		verify_filename(prefix, arg, 1);
+	}
+	if (verify) {
+		if (revs_count == 1) {
+			show_rev(type, sha1, name);
+			return 0;
+		} else if (revs_count == 0 && show_default())
+			return 0;
+		die_no_single_rev(quiet);
+	} else
+		show_default();
+	return 0;
+}
diff --git a/builtin/revert.c b/builtin/revert.c
new file mode 100644
index 0000000..82d1bf8
--- /dev/null
+++ b/builtin/revert.c
@@ -0,0 +1,238 @@
+#include "cache.h"
+#include "builtin.h"
+#include "parse-options.h"
+#include "diff.h"
+#include "revision.h"
+#include "rerere.h"
+#include "dir.h"
+#include "sequencer.h"
+
+/*
+ * This implements the builtins revert and cherry-pick.
+ *
+ * Copyright (c) 2007 Johannes E. Schindelin
+ *
+ * Based on git-revert.sh, which is
+ *
+ * Copyright (c) 2005 Linus Torvalds
+ * Copyright (c) 2005 Junio C Hamano
+ */
+
+static const char * const revert_usage[] = {
+	"git revert [options] <commit-ish>",
+	"git revert <subcommand>",
+	NULL
+};
+
+static const char * const cherry_pick_usage[] = {
+	"git cherry-pick [options] <commit-ish>",
+	"git cherry-pick <subcommand>",
+	NULL
+};
+
+static const char *action_name(const struct replay_opts *opts)
+{
+	return opts->action == REPLAY_REVERT ? "revert" : "cherry-pick";
+}
+
+static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts)
+{
+	return opts->action == REPLAY_REVERT ? revert_usage : cherry_pick_usage;
+}
+
+static int option_parse_x(const struct option *opt,
+			  const char *arg, int unset)
+{
+	struct replay_opts **opts_ptr = opt->value;
+	struct replay_opts *opts = *opts_ptr;
+
+	if (unset)
+		return 0;
+
+	ALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc);
+	opts->xopts[opts->xopts_nr++] = xstrdup(arg);
+	return 0;
+}
+
+static void verify_opt_compatible(const char *me, const char *base_opt, ...)
+{
+	const char *this_opt;
+	va_list ap;
+
+	va_start(ap, base_opt);
+	while ((this_opt = va_arg(ap, const char *))) {
+		if (va_arg(ap, int))
+			break;
+	}
+	va_end(ap);
+
+	if (this_opt)
+		die(_("%s: %s cannot be used with %s"), me, this_opt, base_opt);
+}
+
+static void verify_opt_mutually_compatible(const char *me, ...)
+{
+	const char *opt1, *opt2 = NULL;
+	va_list ap;
+
+	va_start(ap, me);
+	while ((opt1 = va_arg(ap, const char *))) {
+		if (va_arg(ap, int))
+			break;
+	}
+	if (opt1) {
+		while ((opt2 = va_arg(ap, const char *))) {
+			if (va_arg(ap, int))
+				break;
+		}
+	}
+	va_end(ap);
+
+	if (opt1 && opt2)
+		die(_("%s: %s cannot be used with %s"),	me, opt1, opt2);
+}
+
+static void parse_args(int argc, const char **argv, struct replay_opts *opts)
+{
+	const char * const * usage_str = revert_or_cherry_pick_usage(opts);
+	const char *me = action_name(opts);
+	int remove_state = 0;
+	int contin = 0;
+	int rollback = 0;
+	struct option options[] = {
+		OPT_BOOLEAN(0, "quit", &remove_state, "end revert or cherry-pick sequence"),
+		OPT_BOOLEAN(0, "continue", &contin, "resume revert or cherry-pick sequence"),
+		OPT_BOOLEAN(0, "abort", &rollback, "cancel revert or cherry-pick sequence"),
+		OPT_BOOLEAN('n', "no-commit", &opts->no_commit, "don't automatically commit"),
+		OPT_BOOLEAN('e', "edit", &opts->edit, "edit the commit message"),
+		OPT_NOOP_NOARG('r', NULL),
+		OPT_BOOLEAN('s', "signoff", &opts->signoff, "add Signed-off-by:"),
+		OPT_INTEGER('m', "mainline", &opts->mainline, "parent number"),
+		OPT_RERERE_AUTOUPDATE(&opts->allow_rerere_auto),
+		OPT_STRING(0, "strategy", &opts->strategy, "strategy", "merge strategy"),
+		OPT_CALLBACK('X', "strategy-option", &opts, "option",
+			"option for merge strategy", option_parse_x),
+		OPT_END(),
+		OPT_END(),
+		OPT_END(),
+		OPT_END(),
+		OPT_END(),
+	};
+
+	if (opts->action == REPLAY_PICK) {
+		struct option cp_extra[] = {
+			OPT_BOOLEAN('x', NULL, &opts->record_origin, "append commit name"),
+			OPT_BOOLEAN(0, "ff", &opts->allow_ff, "allow fast-forward"),
+			OPT_BOOLEAN(0, "allow-empty", &opts->allow_empty, "preserve initially empty commits"),
+			OPT_BOOLEAN(0, "keep-redundant-commits", &opts->keep_redundant_commits, "keep redundant, empty commits"),
+			OPT_END(),
+		};
+		if (parse_options_concat(options, ARRAY_SIZE(options), cp_extra))
+			die(_("program error"));
+	}
+
+	argc = parse_options(argc, argv, NULL, options, usage_str,
+			PARSE_OPT_KEEP_ARGV0 |
+			PARSE_OPT_KEEP_UNKNOWN);
+
+	/* Check for incompatible subcommands */
+	verify_opt_mutually_compatible(me,
+				"--quit", remove_state,
+				"--continue", contin,
+				"--abort", rollback,
+				NULL);
+
+	/* implies allow_empty */
+	if (opts->keep_redundant_commits)
+		opts->allow_empty = 1;
+
+	/* Set the subcommand */
+	if (remove_state)
+		opts->subcommand = REPLAY_REMOVE_STATE;
+	else if (contin)
+		opts->subcommand = REPLAY_CONTINUE;
+	else if (rollback)
+		opts->subcommand = REPLAY_ROLLBACK;
+	else
+		opts->subcommand = REPLAY_NONE;
+
+	/* Check for incompatible command line arguments */
+	if (opts->subcommand != REPLAY_NONE) {
+		char *this_operation;
+		if (opts->subcommand == REPLAY_REMOVE_STATE)
+			this_operation = "--quit";
+		else if (opts->subcommand == REPLAY_CONTINUE)
+			this_operation = "--continue";
+		else {
+			assert(opts->subcommand == REPLAY_ROLLBACK);
+			this_operation = "--abort";
+		}
+
+		verify_opt_compatible(me, this_operation,
+				"--no-commit", opts->no_commit,
+				"--signoff", opts->signoff,
+				"--mainline", opts->mainline,
+				"--strategy", opts->strategy ? 1 : 0,
+				"--strategy-option", opts->xopts ? 1 : 0,
+				"-x", opts->record_origin,
+				"--ff", opts->allow_ff,
+				NULL);
+	}
+
+	if (opts->allow_ff)
+		verify_opt_compatible(me, "--ff",
+				"--signoff", opts->signoff,
+				"--no-commit", opts->no_commit,
+				"-x", opts->record_origin,
+				"--edit", opts->edit,
+				NULL);
+
+	if (opts->subcommand != REPLAY_NONE) {
+		opts->revs = NULL;
+	} else {
+		struct setup_revision_opt s_r_opt;
+		opts->revs = xmalloc(sizeof(*opts->revs));
+		init_revisions(opts->revs, NULL);
+		opts->revs->no_walk = 1;
+		if (argc < 2)
+			usage_with_options(usage_str, options);
+		memset(&s_r_opt, 0, sizeof(s_r_opt));
+		s_r_opt.assume_dashdash = 1;
+		argc = setup_revisions(argc, argv, opts->revs, &s_r_opt);
+	}
+
+	if (argc > 1)
+		usage_with_options(usage_str, options);
+}
+
+int cmd_revert(int argc, const char **argv, const char *prefix)
+{
+	struct replay_opts opts;
+	int res;
+
+	memset(&opts, 0, sizeof(opts));
+	if (isatty(0))
+		opts.edit = 1;
+	opts.action = REPLAY_REVERT;
+	git_config(git_default_config, NULL);
+	parse_args(argc, argv, &opts);
+	res = sequencer_pick_revisions(&opts);
+	if (res < 0)
+		die(_("revert failed"));
+	return res;
+}
+
+int cmd_cherry_pick(int argc, const char **argv, const char *prefix)
+{
+	struct replay_opts opts;
+	int res;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.action = REPLAY_PICK;
+	git_config(git_default_config, NULL);
+	parse_args(argc, argv, &opts);
+	res = sequencer_pick_revisions(&opts);
+	if (res < 0)
+		die(_("cherry-pick failed"));
+	return res;
+}
diff --git a/builtin/rm.c b/builtin/rm.c
new file mode 100644
index 0000000..90c8a50
--- /dev/null
+++ b/builtin/rm.c
@@ -0,0 +1,264 @@
+/*
+ * "git rm" builtin command
+ *
+ * Copyright (C) Linus Torvalds 2006
+ */
+#include "cache.h"
+#include "builtin.h"
+#include "dir.h"
+#include "cache-tree.h"
+#include "tree-walk.h"
+#include "parse-options.h"
+
+static const char * const builtin_rm_usage[] = {
+	"git rm [options] [--] <file>...",
+	NULL
+};
+
+static struct {
+	int nr, alloc;
+	const char **name;
+} list;
+
+static int check_local_mod(unsigned char *head, int index_only)
+{
+	/*
+	 * Items in list are already sorted in the cache order,
+	 * so we could do this a lot more efficiently by using
+	 * tree_desc based traversal if we wanted to, but I am
+	 * lazy, and who cares if removal of files is a tad
+	 * slower than the theoretical maximum speed?
+	 */
+	int i, no_head;
+	int errs = 0;
+
+	no_head = is_null_sha1(head);
+	for (i = 0; i < list.nr; i++) {
+		struct stat st;
+		int pos;
+		struct cache_entry *ce;
+		const char *name = list.name[i];
+		unsigned char sha1[20];
+		unsigned mode;
+		int local_changes = 0;
+		int staged_changes = 0;
+
+		pos = cache_name_pos(name, strlen(name));
+		if (pos < 0)
+			continue; /* removing unmerged entry */
+		ce = active_cache[pos];
+
+		if (lstat(ce->name, &st) < 0) {
+			if (errno != ENOENT)
+				warning("'%s': %s", ce->name, strerror(errno));
+			/* It already vanished from the working tree */
+			continue;
+		}
+		else if (S_ISDIR(st.st_mode)) {
+			/* if a file was removed and it is now a
+			 * directory, that is the same as ENOENT as
+			 * far as git is concerned; we do not track
+			 * directories.
+			 */
+			continue;
+		}
+
+		/*
+		 * "rm" of a path that has changes need to be treated
+		 * carefully not to allow losing local changes
+		 * accidentally.  A local change could be (1) file in
+		 * work tree is different since the index; and/or (2)
+		 * the user staged a content that is different from
+		 * the current commit in the index.
+		 *
+		 * In such a case, you would need to --force the
+		 * removal.  However, "rm --cached" (remove only from
+		 * the index) is safe if the index matches the file in
+		 * the work tree or the HEAD commit, as it means that
+		 * the content being removed is available elsewhere.
+		 */
+
+		/*
+		 * Is the index different from the file in the work tree?
+		 */
+		if (ce_match_stat(ce, &st, 0))
+			local_changes = 1;
+
+		/*
+		 * Is the index different from the HEAD commit?  By
+		 * definition, before the very initial commit,
+		 * anything staged in the index is treated by the same
+		 * way as changed from the HEAD.
+		 */
+		if (no_head
+		     || get_tree_entry(head, name, sha1, &mode)
+		     || ce->ce_mode != create_ce_mode(mode)
+		     || hashcmp(ce->sha1, sha1))
+			staged_changes = 1;
+
+		/*
+		 * If the index does not match the file in the work
+		 * tree and if it does not match the HEAD commit
+		 * either, (1) "git rm" without --cached definitely
+		 * will lose information; (2) "git rm --cached" will
+		 * lose information unless it is about removing an
+		 * "intent to add" entry.
+		 */
+		if (local_changes && staged_changes) {
+			if (!index_only || !(ce->ce_flags & CE_INTENT_TO_ADD))
+				errs = error(_("'%s' has staged content different "
+					     "from both the file and the HEAD\n"
+					     "(use -f to force removal)"), name);
+		}
+		else if (!index_only) {
+			if (staged_changes)
+				errs = error(_("'%s' has changes staged in the index\n"
+					     "(use --cached to keep the file, "
+					     "or -f to force removal)"), name);
+			if (local_changes)
+				errs = error(_("'%s' has local modifications\n"
+					     "(use --cached to keep the file, "
+					     "or -f to force removal)"), name);
+		}
+	}
+	return errs;
+}
+
+static struct lock_file lock_file;
+
+static int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0;
+static int ignore_unmatch = 0;
+
+static struct option builtin_rm_options[] = {
+	OPT__DRY_RUN(&show_only, "dry run"),
+	OPT__QUIET(&quiet, "do not list removed files"),
+	OPT_BOOLEAN( 0 , "cached",         &index_only, "only remove from the index"),
+	OPT__FORCE(&force, "override the up-to-date check"),
+	OPT_BOOLEAN('r', NULL,             &recursive,  "allow recursive removal"),
+	OPT_BOOLEAN( 0 , "ignore-unmatch", &ignore_unmatch,
+				"exit with a zero status even if nothing matched"),
+	OPT_END(),
+};
+
+int cmd_rm(int argc, const char **argv, const char *prefix)
+{
+	int i, newfd;
+	const char **pathspec;
+	char *seen;
+
+	git_config(git_default_config, NULL);
+
+	argc = parse_options(argc, argv, prefix, builtin_rm_options,
+			     builtin_rm_usage, 0);
+	if (!argc)
+		usage_with_options(builtin_rm_usage, builtin_rm_options);
+
+	if (!index_only)
+		setup_work_tree();
+
+	newfd = hold_locked_index(&lock_file, 1);
+
+	if (read_cache() < 0)
+		die(_("index file corrupt"));
+
+	pathspec = get_pathspec(prefix, argv);
+	refresh_index(&the_index, REFRESH_QUIET, pathspec, NULL, NULL);
+
+	seen = NULL;
+	for (i = 0; pathspec[i] ; i++)
+		/* nothing */;
+	seen = xcalloc(i, 1);
+
+	for (i = 0; i < active_nr; i++) {
+		struct cache_entry *ce = active_cache[i];
+		if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen))
+			continue;
+		ALLOC_GROW(list.name, list.nr + 1, list.alloc);
+		list.name[list.nr++] = ce->name;
+	}
+
+	if (pathspec) {
+		const char *match;
+		int seen_any = 0;
+		for (i = 0; (match = pathspec[i]) != NULL ; i++) {
+			if (!seen[i]) {
+				if (!ignore_unmatch) {
+					die(_("pathspec '%s' did not match any files"),
+					    match);
+				}
+			}
+			else {
+				seen_any = 1;
+			}
+			if (!recursive && seen[i] == MATCHED_RECURSIVELY)
+				die(_("not removing '%s' recursively without -r"),
+				    *match ? match : ".");
+		}
+
+		if (! seen_any)
+			exit(0);
+	}
+
+	/*
+	 * If not forced, the file, the index and the HEAD (if exists)
+	 * must match; but the file can already been removed, since
+	 * this sequence is a natural "novice" way:
+	 *
+	 *	rm F; git rm F
+	 *
+	 * Further, if HEAD commit exists, "diff-index --cached" must
+	 * report no changes unless forced.
+	 */
+	if (!force) {
+		unsigned char sha1[20];
+		if (get_sha1("HEAD", sha1))
+			hashclr(sha1);
+		if (check_local_mod(sha1, index_only))
+			exit(1);
+	}
+
+	/*
+	 * First remove the names from the index: we won't commit
+	 * the index unless all of them succeed.
+	 */
+	for (i = 0; i < list.nr; i++) {
+		const char *path = list.name[i];
+		if (!quiet)
+			printf("rm '%s'\n", path);
+
+		if (remove_file_from_cache(path))
+			die(_("git rm: unable to remove %s"), path);
+	}
+
+	if (show_only)
+		return 0;
+
+	/*
+	 * Then, unless we used "--cached", remove the filenames from
+	 * the workspace. If we fail to remove the first one, we
+	 * abort the "git rm" (but once we've successfully removed
+	 * any file at all, we'll go ahead and commit to it all:
+	 * by then we've already committed ourselves and can't fail
+	 * in the middle)
+	 */
+	if (!index_only) {
+		int removed = 0;
+		for (i = 0; i < list.nr; i++) {
+			const char *path = list.name[i];
+			if (!remove_path(path)) {
+				removed = 1;
+				continue;
+			}
+			if (!removed)
+				die_errno("git rm: '%s'", path);
+		}
+	}
+
+	if (active_cache_changed) {
+		if (write_cache(newfd, active_cache, active_nr) ||
+		    commit_locked_index(&lock_file))
+			die(_("Unable to write new index file"));
+	}
+
+	return 0;
+}
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
new file mode 100644
index 0000000..d5d7105
--- /dev/null
+++ b/builtin/send-pack.c
@@ -0,0 +1,562 @@
+#include "builtin.h"
+#include "commit.h"
+#include "refs.h"
+#include "pkt-line.h"
+#include "sideband.h"
+#include "run-command.h"
+#include "remote.h"
+#include "send-pack.h"
+#include "quote.h"
+#include "transport.h"
+
+static const char send_pack_usage[] =
+"git send-pack [--all | --mirror] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n"
+"  --all and explicit <ref> specification are mutually exclusive.";
+
+static struct send_pack_args args;
+
+static int feed_object(const unsigned char *sha1, int fd, int negative)
+{
+	char buf[42];
+
+	if (negative && !has_sha1_file(sha1))
+		return 1;
+
+	memcpy(buf + negative, sha1_to_hex(sha1), 40);
+	if (negative)
+		buf[0] = '^';
+	buf[40 + negative] = '\n';
+	return write_or_whine(fd, buf, 41 + negative, "send-pack: send refs");
+}
+
+/*
+ * Make a pack stream and spit it out into file descriptor fd
+ */
+static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *extra, struct send_pack_args *args)
+{
+	/*
+	 * The child becomes pack-objects --revs; we feed
+	 * the revision parameters to it via its stdin and
+	 * let its stdout go back to the other end.
+	 */
+	const char *argv[] = {
+		"pack-objects",
+		"--all-progress-implied",
+		"--revs",
+		"--stdout",
+		NULL,
+		NULL,
+		NULL,
+		NULL,
+		NULL,
+	};
+	struct child_process po;
+	int i;
+
+	i = 4;
+	if (args->use_thin_pack)
+		argv[i++] = "--thin";
+	if (args->use_ofs_delta)
+		argv[i++] = "--delta-base-offset";
+	if (args->quiet || !args->progress)
+		argv[i++] = "-q";
+	if (args->progress)
+		argv[i++] = "--progress";
+	memset(&po, 0, sizeof(po));
+	po.argv = argv;
+	po.in = -1;
+	po.out = args->stateless_rpc ? -1 : fd;
+	po.git_cmd = 1;
+	if (start_command(&po))
+		die_errno("git pack-objects failed");
+
+	/*
+	 * We feed the pack-objects we just spawned with revision
+	 * parameters by writing to the pipe.
+	 */
+	for (i = 0; i < extra->nr; i++)
+		if (!feed_object(extra->array[i], po.in, 1))
+			break;
+
+	while (refs) {
+		if (!is_null_sha1(refs->old_sha1) &&
+		    !feed_object(refs->old_sha1, po.in, 1))
+			break;
+		if (!is_null_sha1(refs->new_sha1) &&
+		    !feed_object(refs->new_sha1, po.in, 0))
+			break;
+		refs = refs->next;
+	}
+
+	close(po.in);
+
+	if (args->stateless_rpc) {
+		char *buf = xmalloc(LARGE_PACKET_MAX);
+		while (1) {
+			ssize_t n = xread(po.out, buf, LARGE_PACKET_MAX);
+			if (n <= 0)
+				break;
+			send_sideband(fd, -1, buf, n, LARGE_PACKET_MAX);
+		}
+		free(buf);
+		close(po.out);
+		po.out = -1;
+	}
+
+	if (finish_command(&po))
+		return -1;
+	return 0;
+}
+
+static int receive_status(int in, struct ref *refs)
+{
+	struct ref *hint;
+	char line[1000];
+	int ret = 0;
+	int len = packet_read_line(in, line, sizeof(line));
+	if (len < 10 || memcmp(line, "unpack ", 7))
+		return error("did not receive remote status");
+	if (memcmp(line, "unpack ok\n", 10)) {
+		char *p = line + strlen(line) - 1;
+		if (*p == '\n')
+			*p = '\0';
+		error("unpack failed: %s", line + 7);
+		ret = -1;
+	}
+	hint = NULL;
+	while (1) {
+		char *refname;
+		char *msg;
+		len = packet_read_line(in, line, sizeof(line));
+		if (!len)
+			break;
+		if (len < 3 ||
+		    (memcmp(line, "ok ", 3) && memcmp(line, "ng ", 3))) {
+			fprintf(stderr, "protocol error: %s\n", line);
+			ret = -1;
+			break;
+		}
+
+		line[strlen(line)-1] = '\0';
+		refname = line + 3;
+		msg = strchr(refname, ' ');
+		if (msg)
+			*msg++ = '\0';
+
+		/* first try searching at our hint, falling back to all refs */
+		if (hint)
+			hint = find_ref_by_name(hint, refname);
+		if (!hint)
+			hint = find_ref_by_name(refs, refname);
+		if (!hint) {
+			warning("remote reported status on unknown ref: %s",
+					refname);
+			continue;
+		}
+		if (hint->status != REF_STATUS_EXPECTING_REPORT) {
+			warning("remote reported status on unexpected ref: %s",
+					refname);
+			continue;
+		}
+
+		if (line[0] == 'o' && line[1] == 'k')
+			hint->status = REF_STATUS_OK;
+		else {
+			hint->status = REF_STATUS_REMOTE_REJECT;
+			ret = -1;
+		}
+		if (msg)
+			hint->remote_status = xstrdup(msg);
+		/* start our next search from the next ref */
+		hint = hint->next;
+	}
+	return ret;
+}
+
+static void print_helper_status(struct ref *ref)
+{
+	struct strbuf buf = STRBUF_INIT;
+
+	for (; ref; ref = ref->next) {
+		const char *msg = NULL;
+		const char *res;
+
+		switch(ref->status) {
+		case REF_STATUS_NONE:
+			res = "error";
+			msg = "no match";
+			break;
+
+		case REF_STATUS_OK:
+			res = "ok";
+			break;
+
+		case REF_STATUS_UPTODATE:
+			res = "ok";
+			msg = "up to date";
+			break;
+
+		case REF_STATUS_REJECT_NONFASTFORWARD:
+			res = "error";
+			msg = "non-fast forward";
+			break;
+
+		case REF_STATUS_REJECT_NODELETE:
+		case REF_STATUS_REMOTE_REJECT:
+			res = "error";
+			break;
+
+		case REF_STATUS_EXPECTING_REPORT:
+		default:
+			continue;
+		}
+
+		strbuf_reset(&buf);
+		strbuf_addf(&buf, "%s %s", res, ref->name);
+		if (ref->remote_status)
+			msg = ref->remote_status;
+		if (msg) {
+			strbuf_addch(&buf, ' ');
+			quote_two_c_style(&buf, "", msg, 0);
+		}
+		strbuf_addch(&buf, '\n');
+
+		safe_write(1, buf.buf, buf.len);
+	}
+	strbuf_release(&buf);
+}
+
+static int sideband_demux(int in, int out, void *data)
+{
+	int *fd = data, ret;
+#ifdef NO_PTHREADS
+	close(fd[1]);
+#endif
+	ret = recv_sideband("send-pack", fd[0], out);
+	close(out);
+	return ret;
+}
+
+int send_pack(struct send_pack_args *args,
+	      int fd[], struct child_process *conn,
+	      struct ref *remote_refs,
+	      struct extra_have_objects *extra_have)
+{
+	int in = fd[0];
+	int out = fd[1];
+	struct strbuf req_buf = STRBUF_INIT;
+	struct ref *ref;
+	int new_refs;
+	int allow_deleting_refs = 0;
+	int status_report = 0;
+	int use_sideband = 0;
+	int quiet_supported = 0;
+	unsigned cmds_sent = 0;
+	int ret;
+	struct async demux;
+
+	/* Does the other end support the reporting? */
+	if (server_supports("report-status"))
+		status_report = 1;
+	if (server_supports("delete-refs"))
+		allow_deleting_refs = 1;
+	if (server_supports("ofs-delta"))
+		args->use_ofs_delta = 1;
+	if (server_supports("side-band-64k"))
+		use_sideband = 1;
+	if (server_supports("quiet"))
+		quiet_supported = 1;
+
+	if (!remote_refs) {
+		fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
+			"Perhaps you should specify a branch such as 'master'.\n");
+		return 0;
+	}
+
+	/*
+	 * Finally, tell the other end!
+	 */
+	new_refs = 0;
+	for (ref = remote_refs; ref; ref = ref->next) {
+		if (!ref->peer_ref && !args->send_mirror)
+			continue;
+
+		/* Check for statuses set by set_ref_status_for_push() */
+		switch (ref->status) {
+		case REF_STATUS_REJECT_NONFASTFORWARD:
+		case REF_STATUS_UPTODATE:
+			continue;
+		default:
+			; /* do nothing */
+		}
+
+		if (ref->deletion && !allow_deleting_refs) {
+			ref->status = REF_STATUS_REJECT_NODELETE;
+			continue;
+		}
+
+		if (!ref->deletion)
+			new_refs++;
+
+		if (args->dry_run) {
+			ref->status = REF_STATUS_OK;
+		} else {
+			char *old_hex = sha1_to_hex(ref->old_sha1);
+			char *new_hex = sha1_to_hex(ref->new_sha1);
+			int quiet = quiet_supported && (args->quiet || !args->progress);
+
+			if (!cmds_sent && (status_report || use_sideband || args->quiet)) {
+				packet_buf_write(&req_buf, "%s %s %s%c%s%s%s",
+						 old_hex, new_hex, ref->name, 0,
+						 status_report ? " report-status" : "",
+						 use_sideband ? " side-band-64k" : "",
+						 quiet ? " quiet" : "");
+			}
+			else
+				packet_buf_write(&req_buf, "%s %s %s",
+						 old_hex, new_hex, ref->name);
+			ref->status = status_report ?
+				REF_STATUS_EXPECTING_REPORT :
+				REF_STATUS_OK;
+			cmds_sent++;
+		}
+	}
+
+	if (args->stateless_rpc) {
+		if (!args->dry_run && cmds_sent) {
+			packet_buf_flush(&req_buf);
+			send_sideband(out, -1, req_buf.buf, req_buf.len, LARGE_PACKET_MAX);
+		}
+	} else {
+		safe_write(out, req_buf.buf, req_buf.len);
+		packet_flush(out);
+	}
+	strbuf_release(&req_buf);
+
+	if (use_sideband && cmds_sent) {
+		memset(&demux, 0, sizeof(demux));
+		demux.proc = sideband_demux;
+		demux.data = fd;
+		demux.out = -1;
+		if (start_async(&demux))
+			die("send-pack: unable to fork off sideband demultiplexer");
+		in = demux.out;
+	}
+
+	if (new_refs && cmds_sent) {
+		if (pack_objects(out, remote_refs, extra_have, args) < 0) {
+			for (ref = remote_refs; ref; ref = ref->next)
+				ref->status = REF_STATUS_NONE;
+			if (args->stateless_rpc)
+				close(out);
+			if (git_connection_is_socket(conn))
+				shutdown(fd[0], SHUT_WR);
+			if (use_sideband)
+				finish_async(&demux);
+			return -1;
+		}
+	}
+	if (args->stateless_rpc && cmds_sent)
+		packet_flush(out);
+
+	if (status_report && cmds_sent)
+		ret = receive_status(in, remote_refs);
+	else
+		ret = 0;
+	if (args->stateless_rpc)
+		packet_flush(out);
+
+	if (use_sideband && cmds_sent) {
+		if (finish_async(&demux)) {
+			error("error in sideband demultiplexer");
+			ret = -1;
+		}
+		close(demux.out);
+	}
+
+	if (ret < 0)
+		return ret;
+
+	if (args->porcelain)
+		return 0;
+
+	for (ref = remote_refs; ref; ref = ref->next) {
+		switch (ref->status) {
+		case REF_STATUS_NONE:
+		case REF_STATUS_UPTODATE:
+		case REF_STATUS_OK:
+			break;
+		default:
+			return -1;
+		}
+	}
+	return 0;
+}
+
+int cmd_send_pack(int argc, const char **argv, const char *prefix)
+{
+	int i, nr_refspecs = 0;
+	const char **refspecs = NULL;
+	const char *remote_name = NULL;
+	struct remote *remote = NULL;
+	const char *dest = NULL;
+	int fd[2];
+	struct child_process *conn;
+	struct extra_have_objects extra_have;
+	struct ref *remote_refs, *local_refs;
+	int ret;
+	int helper_status = 0;
+	int send_all = 0;
+	const char *receivepack = "git-receive-pack";
+	int flags;
+	int nonfastforward = 0;
+	int progress = -1;
+
+	argv++;
+	for (i = 1; i < argc; i++, argv++) {
+		const char *arg = *argv;
+
+		if (*arg == '-') {
+			if (!prefixcmp(arg, "--receive-pack=")) {
+				receivepack = arg + 15;
+				continue;
+			}
+			if (!prefixcmp(arg, "--exec=")) {
+				receivepack = arg + 7;
+				continue;
+			}
+			if (!prefixcmp(arg, "--remote=")) {
+				remote_name = arg + 9;
+				continue;
+			}
+			if (!strcmp(arg, "--all")) {
+				send_all = 1;
+				continue;
+			}
+			if (!strcmp(arg, "--dry-run")) {
+				args.dry_run = 1;
+				continue;
+			}
+			if (!strcmp(arg, "--mirror")) {
+				args.send_mirror = 1;
+				continue;
+			}
+			if (!strcmp(arg, "--force")) {
+				args.force_update = 1;
+				continue;
+			}
+			if (!strcmp(arg, "--quiet")) {
+				args.quiet = 1;
+				continue;
+			}
+			if (!strcmp(arg, "--verbose")) {
+				args.verbose = 1;
+				continue;
+			}
+			if (!strcmp(arg, "--progress")) {
+				progress = 1;
+				continue;
+			}
+			if (!strcmp(arg, "--no-progress")) {
+				progress = 0;
+				continue;
+			}
+			if (!strcmp(arg, "--thin")) {
+				args.use_thin_pack = 1;
+				continue;
+			}
+			if (!strcmp(arg, "--stateless-rpc")) {
+				args.stateless_rpc = 1;
+				continue;
+			}
+			if (!strcmp(arg, "--helper-status")) {
+				helper_status = 1;
+				continue;
+			}
+			usage(send_pack_usage);
+		}
+		if (!dest) {
+			dest = arg;
+			continue;
+		}
+		refspecs = (const char **) argv;
+		nr_refspecs = argc - i;
+		break;
+	}
+	if (!dest)
+		usage(send_pack_usage);
+	/*
+	 * --all and --mirror are incompatible; neither makes sense
+	 * with any refspecs.
+	 */
+	if ((refspecs && (send_all || args.send_mirror)) ||
+	    (send_all && args.send_mirror))
+		usage(send_pack_usage);
+
+	if (remote_name) {
+		remote = remote_get(remote_name);
+		if (!remote_has_url(remote, dest)) {
+			die("Destination %s is not a uri for %s",
+			    dest, remote_name);
+		}
+	}
+
+	if (progress == -1)
+		progress = !args.quiet && isatty(2);
+	args.progress = progress;
+
+	if (args.stateless_rpc) {
+		conn = NULL;
+		fd[0] = 0;
+		fd[1] = 1;
+	} else {
+		conn = git_connect(fd, dest, receivepack,
+			args.verbose ? CONNECT_VERBOSE : 0);
+	}
+
+	memset(&extra_have, 0, sizeof(extra_have));
+
+	get_remote_heads(fd[0], &remote_refs, REF_NORMAL, &extra_have);
+
+	transport_verify_remote_names(nr_refspecs, refspecs);
+
+	local_refs = get_local_heads();
+
+	flags = MATCH_REFS_NONE;
+
+	if (send_all)
+		flags |= MATCH_REFS_ALL;
+	if (args.send_mirror)
+		flags |= MATCH_REFS_MIRROR;
+
+	/* match them up */
+	if (match_push_refs(local_refs, &remote_refs, nr_refspecs, refspecs, flags))
+		return -1;
+
+	set_ref_status_for_push(remote_refs, args.send_mirror,
+		args.force_update);
+
+	ret = send_pack(&args, fd, conn, remote_refs, &extra_have);
+
+	if (helper_status)
+		print_helper_status(remote_refs);
+
+	close(fd[1]);
+	close(fd[0]);
+
+	ret |= finish_connect(conn);
+
+	if (!helper_status)
+		transport_print_push_status(dest, remote_refs, args.verbose, 0, &nonfastforward);
+
+	if (!args.dry_run && remote) {
+		struct ref *ref;
+		for (ref = remote_refs; ref; ref = ref->next)
+			transport_update_tracking_ref(remote, ref, args.verbose);
+	}
+
+	if (!ret && !transport_refs_pushed(remote_refs))
+		fprintf(stderr, "Everything up-to-date\n");
+
+	return ret;
+}
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
new file mode 100644
index 0000000..37f3193
--- /dev/null
+++ b/builtin/shortlog.c
@@ -0,0 +1,353 @@
+#include "builtin.h"
+#include "cache.h"
+#include "commit.h"
+#include "diff.h"
+#include "string-list.h"
+#include "revision.h"
+#include "utf8.h"
+#include "mailmap.h"
+#include "shortlog.h"
+#include "parse-options.h"
+
+static char const * const shortlog_usage[] = {
+	"git shortlog [-n] [-s] [-e] [-w] [rev-opts] [--] [<commit-id>... ]",
+	"",
+	"[rev-opts] are documented in git-rev-list(1)",
+	NULL
+};
+
+static int compare_by_number(const void *a1, const void *a2)
+{
+	const struct string_list_item *i1 = a1, *i2 = a2;
+	const struct string_list *l1 = i1->util, *l2 = i2->util;
+
+	if (l1->nr < l2->nr)
+		return 1;
+	else if (l1->nr == l2->nr)
+		return 0;
+	else
+		return -1;
+}
+
+static void insert_one_record(struct shortlog *log,
+			      const char *author,
+			      const char *oneline)
+{
+	const char *dot3 = log->common_repo_prefix;
+	char *buffer, *p;
+	struct string_list_item *item;
+	char namebuf[1024];
+	char emailbuf[1024];
+	size_t len;
+	const char *eol;
+	const char *boemail, *eoemail;
+	struct strbuf subject = STRBUF_INIT;
+
+	boemail = strchr(author, '<');
+	if (!boemail)
+		return;
+	eoemail = strchr(boemail, '>');
+	if (!eoemail)
+		return;
+
+	/* copy author name to namebuf, to support matching on both name and email */
+	memcpy(namebuf, author, boemail - author);
+	len = boemail - author;
+	while (len > 0 && isspace(namebuf[len-1]))
+		len--;
+	namebuf[len] = 0;
+
+	/* copy email name to emailbuf, to allow email replacement as well */
+	memcpy(emailbuf, boemail+1, eoemail - boemail);
+	emailbuf[eoemail - boemail - 1] = 0;
+
+	if (!map_user(&log->mailmap, emailbuf, sizeof(emailbuf), namebuf, sizeof(namebuf))) {
+		while (author < boemail && isspace(*author))
+			author++;
+		for (len = 0;
+		     len < sizeof(namebuf) - 1 && author + len < boemail;
+		     len++)
+			namebuf[len] = author[len];
+		while (0 < len && isspace(namebuf[len-1]))
+			len--;
+		namebuf[len] = '\0';
+	}
+	else
+		len = strlen(namebuf);
+
+	if (log->email) {
+		size_t room = sizeof(namebuf) - len - 1;
+		int maillen = strlen(emailbuf);
+		snprintf(namebuf + len, room, " <%.*s>", maillen, emailbuf);
+	}
+
+	item = string_list_insert(&log->list, namebuf);
+	if (item->util == NULL)
+		item->util = xcalloc(1, sizeof(struct string_list));
+
+	/* Skip any leading whitespace, including any blank lines. */
+	while (*oneline && isspace(*oneline))
+		oneline++;
+	eol = strchr(oneline, '\n');
+	if (!eol)
+		eol = oneline + strlen(oneline);
+	if (!prefixcmp(oneline, "[PATCH")) {
+		char *eob = strchr(oneline, ']');
+		if (eob && (!eol || eob < eol))
+			oneline = eob + 1;
+	}
+	while (*oneline && isspace(*oneline) && *oneline != '\n')
+		oneline++;
+	format_subject(&subject, oneline, " ");
+	buffer = strbuf_detach(&subject, NULL);
+
+	if (dot3) {
+		int dot3len = strlen(dot3);
+		if (dot3len > 5) {
+			while ((p = strstr(buffer, dot3)) != NULL) {
+				int taillen = strlen(p) - dot3len;
+				memcpy(p, "/.../", 5);
+				memmove(p + 5, p + dot3len, taillen + 1);
+			}
+		}
+	}
+
+	string_list_append(item->util, buffer);
+}
+
+static void read_from_stdin(struct shortlog *log)
+{
+	char author[1024], oneline[1024];
+
+	while (fgets(author, sizeof(author), stdin) != NULL) {
+		if (!(author[0] == 'A' || author[0] == 'a') ||
+		    prefixcmp(author + 1, "uthor: "))
+			continue;
+		while (fgets(oneline, sizeof(oneline), stdin) &&
+		       oneline[0] != '\n')
+			; /* discard headers */
+		while (fgets(oneline, sizeof(oneline), stdin) &&
+		       oneline[0] == '\n')
+			; /* discard blanks */
+		insert_one_record(log, author + 8, oneline);
+	}
+}
+
+void shortlog_add_commit(struct shortlog *log, struct commit *commit)
+{
+	const char *author = NULL, *buffer;
+	struct strbuf buf = STRBUF_INIT;
+	struct strbuf ufbuf = STRBUF_INIT;
+
+	pp_commit_easy(CMIT_FMT_RAW, commit, &buf);
+	buffer = buf.buf;
+	while (*buffer && *buffer != '\n') {
+		const char *eol = strchr(buffer, '\n');
+
+		if (eol == NULL)
+			eol = buffer + strlen(buffer);
+		else
+			eol++;
+
+		if (!prefixcmp(buffer, "author "))
+			author = buffer + 7;
+		buffer = eol;
+	}
+	if (!author)
+		die(_("Missing author: %s"),
+		    sha1_to_hex(commit->object.sha1));
+	if (log->user_format) {
+		struct pretty_print_context ctx = {0};
+		ctx.fmt = CMIT_FMT_USERFORMAT;
+		ctx.abbrev = log->abbrev;
+		ctx.subject = "";
+		ctx.after_subject = "";
+		ctx.date_mode = DATE_NORMAL;
+		pretty_print_commit(&ctx, commit, &ufbuf);
+		buffer = ufbuf.buf;
+	} else if (*buffer) {
+		buffer++;
+	}
+	insert_one_record(log, author, !*buffer ? "<none>" : buffer);
+	strbuf_release(&ufbuf);
+	strbuf_release(&buf);
+}
+
+static void get_from_rev(struct rev_info *rev, struct shortlog *log)
+{
+	struct commit *commit;
+
+	if (prepare_revision_walk(rev))
+		die(_("revision walk setup failed"));
+	while ((commit = get_revision(rev)) != NULL)
+		shortlog_add_commit(log, commit);
+}
+
+static int parse_uint(char const **arg, int comma, int defval)
+{
+	unsigned long ul;
+	int ret;
+	char *endp;
+
+	ul = strtoul(*arg, &endp, 10);
+	if (*endp && *endp != comma)
+		return -1;
+	if (ul > INT_MAX)
+		return -1;
+	ret = *arg == endp ? defval : (int)ul;
+	*arg = *endp ? endp + 1 : endp;
+	return ret;
+}
+
+static const char wrap_arg_usage[] = "-w[<width>[,<indent1>[,<indent2>]]]";
+#define DEFAULT_WRAPLEN 76
+#define DEFAULT_INDENT1 6
+#define DEFAULT_INDENT2 9
+
+static int parse_wrap_args(const struct option *opt, const char *arg, int unset)
+{
+	struct shortlog *log = opt->value;
+
+	log->wrap_lines = !unset;
+	if (unset)
+		return 0;
+	if (!arg) {
+		log->wrap = DEFAULT_WRAPLEN;
+		log->in1 = DEFAULT_INDENT1;
+		log->in2 = DEFAULT_INDENT2;
+		return 0;
+	}
+
+	log->wrap = parse_uint(&arg, ',', DEFAULT_WRAPLEN);
+	log->in1 = parse_uint(&arg, ',', DEFAULT_INDENT1);
+	log->in2 = parse_uint(&arg, '\0', DEFAULT_INDENT2);
+	if (log->wrap < 0 || log->in1 < 0 || log->in2 < 0)
+		return error(wrap_arg_usage);
+	if (log->wrap &&
+	    ((log->in1 && log->wrap <= log->in1) ||
+	     (log->in2 && log->wrap <= log->in2)))
+		return error(wrap_arg_usage);
+	return 0;
+}
+
+void shortlog_init(struct shortlog *log)
+{
+	memset(log, 0, sizeof(*log));
+
+	read_mailmap(&log->mailmap, &log->common_repo_prefix);
+
+	log->list.strdup_strings = 1;
+	log->wrap = DEFAULT_WRAPLEN;
+	log->in1 = DEFAULT_INDENT1;
+	log->in2 = DEFAULT_INDENT2;
+}
+
+int cmd_shortlog(int argc, const char **argv, const char *prefix)
+{
+	static struct shortlog log;
+	static struct rev_info rev;
+	int nongit = !startup_info->have_repository;
+
+	static const struct option options[] = {
+		OPT_BOOLEAN('n', "numbered", &log.sort_by_number,
+			    "sort output according to the number of commits per author"),
+		OPT_BOOLEAN('s', "summary", &log.summary,
+			    "Suppress commit descriptions, only provides commit count"),
+		OPT_BOOLEAN('e', "email", &log.email,
+			    "Show the email address of each author"),
+		{ OPTION_CALLBACK, 'w', NULL, &log, "w[,i1[,i2]]",
+			"Linewrap output", PARSE_OPT_OPTARG, &parse_wrap_args },
+		OPT_END(),
+	};
+
+	struct parse_opt_ctx_t ctx;
+
+	git_config(git_default_config, NULL);
+	shortlog_init(&log);
+	init_revisions(&rev, prefix);
+	parse_options_start(&ctx, argc, argv, prefix, options,
+			    PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0);
+
+	for (;;) {
+		switch (parse_options_step(&ctx, options, shortlog_usage)) {
+		case PARSE_OPT_HELP:
+			exit(129);
+		case PARSE_OPT_DONE:
+			goto parse_done;
+		}
+		parse_revision_opt(&rev, &ctx, options, shortlog_usage);
+	}
+parse_done:
+	argc = parse_options_end(&ctx);
+
+	if (setup_revisions(argc, argv, &rev, NULL) != 1) {
+		error(_("unrecognized argument: %s"), argv[1]);
+		usage_with_options(shortlog_usage, options);
+	}
+
+	log.user_format = rev.commit_format == CMIT_FMT_USERFORMAT;
+	log.abbrev = rev.abbrev;
+
+	/* assume HEAD if from a tty */
+	if (!nongit && !rev.pending.nr && isatty(0))
+		add_head_to_pending(&rev);
+	if (rev.pending.nr == 0) {
+		if (isatty(0))
+			fprintf(stderr, _("(reading log message from standard input)\n"));
+		read_from_stdin(&log);
+	}
+	else
+		get_from_rev(&rev, &log);
+
+	shortlog_output(&log);
+	return 0;
+}
+
+static void add_wrapped_shortlog_msg(struct strbuf *sb, const char *s,
+				     const struct shortlog *log)
+{
+	int col = strbuf_add_wrapped_text(sb, s, log->in1, log->in2, log->wrap);
+	if (col != log->wrap)
+		strbuf_addch(sb, '\n');
+}
+
+void shortlog_output(struct shortlog *log)
+{
+	int i, j;
+	struct strbuf sb = STRBUF_INIT;
+
+	if (log->sort_by_number)
+		qsort(log->list.items, log->list.nr, sizeof(struct string_list_item),
+			compare_by_number);
+	for (i = 0; i < log->list.nr; i++) {
+		struct string_list *onelines = log->list.items[i].util;
+
+		if (log->summary) {
+			printf("%6d\t%s\n", onelines->nr, log->list.items[i].string);
+		} else {
+			printf("%s (%d):\n", log->list.items[i].string, onelines->nr);
+			for (j = onelines->nr - 1; j >= 0; j--) {
+				const char *msg = onelines->items[j].string;
+
+				if (log->wrap_lines) {
+					strbuf_reset(&sb);
+					add_wrapped_shortlog_msg(&sb, msg, log);
+					fwrite(sb.buf, sb.len, 1, stdout);
+				}
+				else
+					printf("      %s\n", msg);
+			}
+			putchar('\n');
+		}
+
+		onelines->strdup_strings = 1;
+		string_list_clear(onelines, 0);
+		free(onelines);
+		log->list.items[i].util = NULL;
+	}
+
+	strbuf_release(&sb);
+	log->list.strdup_strings = 1;
+	string_list_clear(&log->list, 1);
+	clear_mailmap(&log->mailmap);
+}
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
new file mode 100644
index 0000000..a59e088
--- /dev/null
+++ b/builtin/show-branch.c
@@ -0,0 +1,952 @@
+#include "cache.h"
+#include "commit.h"
+#include "refs.h"
+#include "builtin.h"
+#include "color.h"
+#include "parse-options.h"
+
+static const char* show_branch_usage[] = {
+    "git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order] [--current] [--color[=<when>] | --no-color] [--sparse] [--more=<n> | --list | --independent | --merge-base] [--no-name | --sha1-name] [--topics] [(<rev> | <glob>)...]",
+    "git show-branch (-g|--reflog)[=<n>[,<base>]] [--list] [<ref>]",
+    NULL
+};
+
+static int showbranch_use_color = -1;
+
+static int default_num;
+static int default_alloc;
+static const char **default_arg;
+
+#define UNINTERESTING	01
+
+#define REV_SHIFT	 2
+#define MAX_REVS	(FLAG_BITS - REV_SHIFT) /* should not exceed bits_per_int - REV_SHIFT */
+
+#define DEFAULT_REFLOG	4
+
+static const char *get_color_code(int idx)
+{
+	if (want_color(showbranch_use_color))
+		return column_colors_ansi[idx % column_colors_ansi_max];
+	return "";
+}
+
+static const char *get_color_reset_code(void)
+{
+	if (want_color(showbranch_use_color))
+		return GIT_COLOR_RESET;
+	return "";
+}
+
+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;
+}
+
+static struct commit *pop_one_commit(struct commit_list **list_p)
+{
+	struct commit *commit;
+	struct commit_list *list;
+	list = *list_p;
+	commit = list->item;
+	*list_p = list->next;
+	free(list);
+	return commit;
+}
+
+struct commit_name {
+	const char *head_name; /* which head's ancestor? */
+	int generation; /* how many parents away from head_name */
+};
+
+/* Name the commit as nth generation ancestor of head_name;
+ * we count only the first-parent relationship for naming purposes.
+ */
+static void name_commit(struct commit *commit, const char *head_name, int nth)
+{
+	struct commit_name *name;
+	if (!commit->util)
+		commit->util = xmalloc(sizeof(struct commit_name));
+	name = commit->util;
+	name->head_name = head_name;
+	name->generation = nth;
+}
+
+/* Parent is the first parent of the commit.  We may name it
+ * as (n+1)th generation ancestor of the same head_name as
+ * commit is nth generation ancestor of, if that generation
+ * number is better than the name it already has.
+ */
+static void name_parent(struct commit *commit, struct commit *parent)
+{
+	struct commit_name *commit_name = commit->util;
+	struct commit_name *parent_name = parent->util;
+	if (!commit_name)
+		return;
+	if (!parent_name ||
+	    commit_name->generation + 1 < parent_name->generation)
+		name_commit(parent, commit_name->head_name,
+			    commit_name->generation + 1);
+}
+
+static int name_first_parent_chain(struct commit *c)
+{
+	int i = 0;
+	while (c) {
+		struct commit *p;
+		if (!c->util)
+			break;
+		if (!c->parents)
+			break;
+		p = c->parents->item;
+		if (!p->util) {
+			name_parent(c, p);
+			i++;
+		}
+		else
+			break;
+		c = p;
+	}
+	return i;
+}
+
+static void name_commits(struct commit_list *list,
+			 struct commit **rev,
+			 char **ref_name,
+			 int num_rev)
+{
+	struct commit_list *cl;
+	struct commit *c;
+	int i;
+
+	/* First give names to the given heads */
+	for (cl = list; cl; cl = cl->next) {
+		c = cl->item;
+		if (c->util)
+			continue;
+		for (i = 0; i < num_rev; i++) {
+			if (rev[i] == c) {
+				name_commit(c, ref_name[i], 0);
+				break;
+			}
+		}
+	}
+
+	/* Then commits on the first parent ancestry chain */
+	do {
+		i = 0;
+		for (cl = list; cl; cl = cl->next) {
+			i += name_first_parent_chain(cl->item);
+		}
+	} while (i);
+
+	/* Finally, any unnamed commits */
+	do {
+		i = 0;
+		for (cl = list; cl; cl = cl->next) {
+			struct commit_list *parents;
+			struct commit_name *n;
+			int nth;
+			c = cl->item;
+			if (!c->util)
+				continue;
+			n = c->util;
+			parents = c->parents;
+			nth = 0;
+			while (parents) {
+				struct commit *p = parents->item;
+				char newname[1000], *en;
+				parents = parents->next;
+				nth++;
+				if (p->util)
+					continue;
+				en = newname;
+				switch (n->generation) {
+				case 0:
+					en += sprintf(en, "%s", n->head_name);
+					break;
+				case 1:
+					en += sprintf(en, "%s^", n->head_name);
+					break;
+				default:
+					en += sprintf(en, "%s~%d",
+						n->head_name, n->generation);
+					break;
+				}
+				if (nth == 1)
+					en += sprintf(en, "^");
+				else
+					en += sprintf(en, "^%d", nth);
+				name_commit(p, xstrdup(newname), 0);
+				i++;
+				name_first_parent_chain(p);
+			}
+		}
+	} while (i);
+}
+
+static int mark_seen(struct commit *commit, struct commit_list **seen_p)
+{
+	if (!commit->object.flags) {
+		commit_list_insert(commit, seen_p);
+		return 1;
+	}
+	return 0;
+}
+
+static void join_revs(struct commit_list **list_p,
+		      struct commit_list **seen_p,
+		      int num_rev, int extra)
+{
+	int all_mask = ((1u << (REV_SHIFT + num_rev)) - 1);
+	int all_revs = all_mask & ~((1u << REV_SHIFT) - 1);
+
+	while (*list_p) {
+		struct commit_list *parents;
+		int still_interesting = !!interesting(*list_p);
+		struct commit *commit = pop_one_commit(list_p);
+		int flags = commit->object.flags & all_mask;
+
+		if (!still_interesting && extra <= 0)
+			break;
+
+		mark_seen(commit, seen_p);
+		if ((flags & all_revs) == all_revs)
+			flags |= UNINTERESTING;
+		parents = commit->parents;
+
+		while (parents) {
+			struct commit *p = parents->item;
+			int this_flag = p->object.flags;
+			parents = parents->next;
+			if ((this_flag & flags) == flags)
+				continue;
+			if (!p->object.parsed)
+				parse_commit(p);
+			if (mark_seen(p, seen_p) && !still_interesting)
+				extra--;
+			p->object.flags |= flags;
+			commit_list_insert_by_date(p, list_p);
+		}
+	}
+
+	/*
+	 * Postprocess to complete well-poisoning.
+	 *
+	 * At this point we have all the commits we have seen in
+	 * seen_p list.  Mark anything that can be reached from
+	 * uninteresting commits not interesting.
+	 */
+	for (;;) {
+		int changed = 0;
+		struct commit_list *s;
+		for (s = *seen_p; s; s = s->next) {
+			struct commit *c = s->item;
+			struct commit_list *parents;
+
+			if (((c->object.flags & all_revs) != all_revs) &&
+			    !(c->object.flags & UNINTERESTING))
+				continue;
+
+			/* The current commit is either a merge base or
+			 * already uninteresting one.  Mark its parents
+			 * as uninteresting commits _only_ if they are
+			 * already parsed.  No reason to find new ones
+			 * here.
+			 */
+			parents = c->parents;
+			while (parents) {
+				struct commit *p = parents->item;
+				parents = parents->next;
+				if (!(p->object.flags & UNINTERESTING)) {
+					p->object.flags |= UNINTERESTING;
+					changed = 1;
+				}
+			}
+		}
+		if (!changed)
+			break;
+	}
+}
+
+static void show_one_commit(struct commit *commit, int no_name)
+{
+	struct strbuf pretty = STRBUF_INIT;
+	const char *pretty_str = "(unavailable)";
+	struct commit_name *name = commit->util;
+
+	if (commit->object.parsed) {
+		pp_commit_easy(CMIT_FMT_ONELINE, commit, &pretty);
+		pretty_str = pretty.buf;
+	}
+	if (!prefixcmp(pretty_str, "[PATCH] "))
+		pretty_str += 8;
+
+	if (!no_name) {
+		if (name && name->head_name) {
+			printf("[%s", name->head_name);
+			if (name->generation) {
+				if (name->generation == 1)
+					printf("^");
+				else
+					printf("~%d", name->generation);
+			}
+			printf("] ");
+		}
+		else
+			printf("[%s] ",
+			       find_unique_abbrev(commit->object.sha1,
+						  DEFAULT_ABBREV));
+	}
+	puts(pretty_str);
+	strbuf_release(&pretty);
+}
+
+static char *ref_name[MAX_REVS + 1];
+static int ref_name_cnt;
+
+static const char *find_digit_prefix(const char *s, int *v)
+{
+	const char *p;
+	int ver;
+	char ch;
+
+	for (p = s, ver = 0;
+	     '0' <= (ch = *p) && ch <= '9';
+	     p++)
+		ver = ver * 10 + ch - '0';
+	*v = ver;
+	return p;
+}
+
+
+static int version_cmp(const char *a, const char *b)
+{
+	while (1) {
+		int va, vb;
+
+		a = find_digit_prefix(a, &va);
+		b = find_digit_prefix(b, &vb);
+		if (va != vb)
+			return va - vb;
+
+		while (1) {
+			int ca = *a;
+			int cb = *b;
+			if ('0' <= ca && ca <= '9')
+				ca = 0;
+			if ('0' <= cb && cb <= '9')
+				cb = 0;
+			if (ca != cb)
+				return ca - cb;
+			if (!ca)
+				break;
+			a++;
+			b++;
+		}
+		if (!*a && !*b)
+			return 0;
+	}
+}
+
+static int compare_ref_name(const void *a_, const void *b_)
+{
+	const char * const*a = a_, * const*b = b_;
+	return version_cmp(*a, *b);
+}
+
+static void sort_ref_range(int bottom, int top)
+{
+	qsort(ref_name + bottom, top - bottom, sizeof(ref_name[0]),
+	      compare_ref_name);
+}
+
+static int append_ref(const char *refname, const unsigned char *sha1,
+		      int allow_dups)
+{
+	struct commit *commit = lookup_commit_reference_gently(sha1, 1);
+	int i;
+
+	if (!commit)
+		return 0;
+
+	if (!allow_dups) {
+		/* Avoid adding the same thing twice */
+		for (i = 0; i < ref_name_cnt; i++)
+			if (!strcmp(refname, ref_name[i]))
+				return 0;
+	}
+	if (MAX_REVS <= ref_name_cnt) {
+		warning("ignoring %s; cannot handle more than %d refs",
+			refname, MAX_REVS);
+		return 0;
+	}
+	ref_name[ref_name_cnt++] = xstrdup(refname);
+	ref_name[ref_name_cnt] = NULL;
+	return 0;
+}
+
+static int append_head_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+{
+	unsigned char tmp[20];
+	int ofs = 11;
+	if (prefixcmp(refname, "refs/heads/"))
+		return 0;
+	/* If both heads/foo and tags/foo exists, get_sha1 would
+	 * get confused.
+	 */
+	if (get_sha1(refname + ofs, tmp) || hashcmp(tmp, sha1))
+		ofs = 5;
+	return append_ref(refname + ofs, sha1, 0);
+}
+
+static int append_remote_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+{
+	unsigned char tmp[20];
+	int ofs = 13;
+	if (prefixcmp(refname, "refs/remotes/"))
+		return 0;
+	/* If both heads/foo and tags/foo exists, get_sha1 would
+	 * get confused.
+	 */
+	if (get_sha1(refname + ofs, tmp) || hashcmp(tmp, sha1))
+		ofs = 5;
+	return append_ref(refname + ofs, sha1, 0);
+}
+
+static int append_tag_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+{
+	if (prefixcmp(refname, "refs/tags/"))
+		return 0;
+	return append_ref(refname + 5, sha1, 0);
+}
+
+static const char *match_ref_pattern = NULL;
+static int match_ref_slash = 0;
+static int count_slash(const char *s)
+{
+	int cnt = 0;
+	while (*s)
+		if (*s++ == '/')
+			cnt++;
+	return cnt;
+}
+
+static int append_matching_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+{
+	/* we want to allow pattern hold/<asterisk> to show all
+	 * branches under refs/heads/hold/, and v0.99.9? to show
+	 * refs/tags/v0.99.9a and friends.
+	 */
+	const char *tail;
+	int slash = count_slash(refname);
+	for (tail = refname; *tail && match_ref_slash < slash; )
+		if (*tail++ == '/')
+			slash--;
+	if (!*tail)
+		return 0;
+	if (fnmatch(match_ref_pattern, tail, 0))
+		return 0;
+	if (!prefixcmp(refname, "refs/heads/"))
+		return append_head_ref(refname, sha1, flag, cb_data);
+	if (!prefixcmp(refname, "refs/tags/"))
+		return append_tag_ref(refname, sha1, flag, cb_data);
+	return append_ref(refname, sha1, 0);
+}
+
+static void snarf_refs(int head, int remotes)
+{
+	if (head) {
+		int orig_cnt = ref_name_cnt;
+		for_each_ref(append_head_ref, NULL);
+		sort_ref_range(orig_cnt, ref_name_cnt);
+	}
+	if (remotes) {
+		int orig_cnt = ref_name_cnt;
+		for_each_ref(append_remote_ref, NULL);
+		sort_ref_range(orig_cnt, ref_name_cnt);
+	}
+}
+
+static int rev_is_head(char *head, int headlen, char *name,
+		       unsigned char *head_sha1, unsigned char *sha1)
+{
+	if ((!head[0]) ||
+	    (head_sha1 && sha1 && hashcmp(head_sha1, sha1)))
+		return 0;
+	if (!prefixcmp(head, "refs/heads/"))
+		head += 11;
+	if (!prefixcmp(name, "refs/heads/"))
+		name += 11;
+	else if (!prefixcmp(name, "heads/"))
+		name += 6;
+	return !strcmp(head, name);
+}
+
+static int show_merge_base(struct commit_list *seen, int num_rev)
+{
+	int all_mask = ((1u << (REV_SHIFT + num_rev)) - 1);
+	int all_revs = all_mask & ~((1u << REV_SHIFT) - 1);
+	int exit_status = 1;
+
+	while (seen) {
+		struct commit *commit = pop_one_commit(&seen);
+		int flags = commit->object.flags & all_mask;
+		if (!(flags & UNINTERESTING) &&
+		    ((flags & all_revs) == all_revs)) {
+			puts(sha1_to_hex(commit->object.sha1));
+			exit_status = 0;
+			commit->object.flags |= UNINTERESTING;
+		}
+	}
+	return exit_status;
+}
+
+static int show_independent(struct commit **rev,
+			    int num_rev,
+			    char **ref_name,
+			    unsigned int *rev_mask)
+{
+	int i;
+
+	for (i = 0; i < num_rev; i++) {
+		struct commit *commit = rev[i];
+		unsigned int flag = rev_mask[i];
+
+		if (commit->object.flags == flag)
+			puts(sha1_to_hex(commit->object.sha1));
+		commit->object.flags |= UNINTERESTING;
+	}
+	return 0;
+}
+
+static void append_one_rev(const char *av)
+{
+	unsigned char revkey[20];
+	if (!get_sha1(av, revkey)) {
+		append_ref(av, revkey, 0);
+		return;
+	}
+	if (strchr(av, '*') || strchr(av, '?') || strchr(av, '[')) {
+		/* glob style match */
+		int saved_matches = ref_name_cnt;
+		match_ref_pattern = av;
+		match_ref_slash = count_slash(av);
+		for_each_ref(append_matching_ref, NULL);
+		if (saved_matches == ref_name_cnt &&
+		    ref_name_cnt < MAX_REVS)
+			error("no matching refs with %s", av);
+		if (saved_matches + 1 < ref_name_cnt)
+			sort_ref_range(saved_matches, ref_name_cnt);
+		return;
+	}
+	die("bad sha1 reference %s", av);
+}
+
+static int git_show_branch_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, "showbranch.default")) {
+		if (!value)
+			return config_error_nonbool(var);
+		/*
+		 * default_arg is now passed to parse_options(), so we need to
+		 * mimic the real argv a bit better.
+		 */
+		if (!default_num) {
+			default_alloc = 20;
+			default_arg = xcalloc(default_alloc, sizeof(*default_arg));
+			default_arg[default_num++] = "show-branch";
+		} else if (default_alloc <= default_num + 1) {
+			default_alloc = default_alloc * 3 / 2 + 20;
+			default_arg = xrealloc(default_arg, sizeof *default_arg * default_alloc);
+		}
+		default_arg[default_num++] = xstrdup(value);
+		default_arg[default_num] = NULL;
+		return 0;
+	}
+
+	if (!strcmp(var, "color.showbranch")) {
+		showbranch_use_color = git_config_colorbool(var, value);
+		return 0;
+	}
+
+	return git_color_default_config(var, value, cb);
+}
+
+static int omit_in_dense(struct commit *commit, struct commit **rev, int n)
+{
+	/* If the commit is tip of the named branches, do not
+	 * omit it.
+	 * Otherwise, if it is a merge that is reachable from only one
+	 * tip, it is not that interesting.
+	 */
+	int i, flag, count;
+	for (i = 0; i < n; i++)
+		if (rev[i] == commit)
+			return 0;
+	flag = commit->object.flags;
+	for (i = count = 0; i < n; i++) {
+		if (flag & (1u << (i + REV_SHIFT)))
+			count++;
+	}
+	if (count == 1)
+		return 1;
+	return 0;
+}
+
+static int reflog = 0;
+
+static int parse_reflog_param(const struct option *opt, const char *arg,
+			      int unset)
+{
+	char *ep;
+	const char **base = (const char **)opt->value;
+	if (!arg)
+		arg = "";
+	reflog = strtoul(arg, &ep, 10);
+	if (*ep == ',')
+		*base = ep + 1;
+	else if (*ep)
+		return error("unrecognized reflog param '%s'", arg);
+	else
+		*base = NULL;
+	if (reflog <= 0)
+		reflog = DEFAULT_REFLOG;
+	return 0;
+}
+
+int cmd_show_branch(int ac, const char **av, const char *prefix)
+{
+	struct commit *rev[MAX_REVS], *commit;
+	char *reflog_msg[MAX_REVS];
+	struct commit_list *list = NULL, *seen = NULL;
+	unsigned int rev_mask[MAX_REVS];
+	int num_rev, i, extra = 0;
+	int all_heads = 0, all_remotes = 0;
+	int all_mask, all_revs;
+	int lifo = 1;
+	char head[128];
+	const char *head_p;
+	int head_len;
+	unsigned char head_sha1[20];
+	int merge_base = 0;
+	int independent = 0;
+	int no_name = 0;
+	int sha1_name = 0;
+	int shown_merge_point = 0;
+	int with_current_branch = 0;
+	int head_at = -1;
+	int topics = 0;
+	int dense = 1;
+	const char *reflog_base = NULL;
+	struct option builtin_show_branch_options[] = {
+		OPT_BOOLEAN('a', "all", &all_heads,
+			    "show remote-tracking and local branches"),
+		OPT_BOOLEAN('r', "remotes", &all_remotes,
+			    "show remote-tracking branches"),
+		OPT__COLOR(&showbranch_use_color,
+			    "color '*!+-' corresponding to the branch"),
+		{ OPTION_INTEGER, 0, "more", &extra, "n",
+			    "show <n> more commits after the common ancestor",
+			    PARSE_OPT_OPTARG, NULL, (intptr_t)1 },
+		OPT_SET_INT(0, "list", &extra, "synonym to more=-1", -1),
+		OPT_BOOLEAN(0, "no-name", &no_name, "suppress naming strings"),
+		OPT_BOOLEAN(0, "current", &with_current_branch,
+			    "include the current branch"),
+		OPT_BOOLEAN(0, "sha1-name", &sha1_name,
+			    "name commits with their object names"),
+		OPT_BOOLEAN(0, "merge-base", &merge_base,
+			    "show possible merge bases"),
+		OPT_BOOLEAN(0, "independent", &independent,
+			    "show refs unreachable from any other ref"),
+		OPT_BOOLEAN(0, "topo-order", &lifo,
+			    "show commits in topological order"),
+		OPT_BOOLEAN(0, "topics", &topics,
+			    "show only commits not on the first branch"),
+		OPT_SET_INT(0, "sparse", &dense,
+			    "show merges reachable from only one tip", 0),
+		OPT_SET_INT(0, "date-order", &lifo,
+			    "show commits where no parent comes before its "
+			    "children", 0),
+		{ OPTION_CALLBACK, 'g', "reflog", &reflog_base, "<n>[,<base>]",
+			    "show <n> most recent ref-log entries starting at "
+			    "base",
+			    PARSE_OPT_OPTARG | PARSE_OPT_LITERAL_ARGHELP,
+			    parse_reflog_param },
+		OPT_END()
+	};
+
+	git_config(git_show_branch_config, NULL);
+
+	/* If nothing is specified, try the default first */
+	if (ac == 1 && default_num) {
+		ac = default_num;
+		av = default_arg;
+	}
+
+	ac = parse_options(ac, av, prefix, builtin_show_branch_options,
+			   show_branch_usage, PARSE_OPT_STOP_AT_NON_OPTION);
+	if (all_heads)
+		all_remotes = 1;
+
+	if (extra || reflog) {
+		/* "listing" mode is incompatible with
+		 * independent nor merge-base modes.
+		 */
+		if (independent || merge_base)
+			usage_with_options(show_branch_usage,
+					   builtin_show_branch_options);
+		if (reflog && ((0 < extra) || all_heads || all_remotes))
+			/*
+			 * Asking for --more in reflog mode does not
+			 * make sense.  --list is Ok.
+			 *
+			 * Also --all and --remotes do not make sense either.
+			 */
+			die("--reflog is incompatible with --all, --remotes, "
+			    "--independent or --merge-base");
+	}
+
+	/* If nothing is specified, show all branches by default */
+	if (ac + all_heads + all_remotes == 0)
+		all_heads = 1;
+
+	if (reflog) {
+		unsigned char sha1[20];
+		char nth_desc[256];
+		char *ref;
+		int base = 0;
+
+		if (ac == 0) {
+			static const char *fake_av[2];
+
+			fake_av[0] = resolve_refdup("HEAD", sha1, 1, NULL);
+			fake_av[1] = NULL;
+			av = fake_av;
+			ac = 1;
+		}
+		if (ac != 1)
+			die("--reflog option needs one branch name");
+
+		if (MAX_REVS < reflog)
+			die("Only %d entries can be shown at one time.",
+			    MAX_REVS);
+		if (!dwim_ref(*av, strlen(*av), sha1, &ref))
+			die("No such ref %s", *av);
+
+		/* Has the base been specified? */
+		if (reflog_base) {
+			char *ep;
+			base = strtoul(reflog_base, &ep, 10);
+			if (*ep) {
+				/* Ah, that is a date spec... */
+				unsigned long at;
+				at = approxidate(reflog_base);
+				read_ref_at(ref, at, -1, sha1, NULL,
+					    NULL, NULL, &base);
+			}
+		}
+
+		for (i = 0; i < reflog; i++) {
+			char *logmsg, *m;
+			const char *msg;
+			unsigned long timestamp;
+			int tz;
+
+			if (read_ref_at(ref, 0, base+i, sha1, &logmsg,
+					&timestamp, &tz, NULL)) {
+				reflog = i;
+				break;
+			}
+			msg = strchr(logmsg, '\t');
+			if (!msg)
+				msg = "(none)";
+			else
+				msg++;
+			m = xmalloc(strlen(msg) + 200);
+			sprintf(m, "(%s) %s",
+				show_date(timestamp, tz, 1),
+				msg);
+			reflog_msg[i] = m;
+			free(logmsg);
+			sprintf(nth_desc, "%s@{%d}", *av, base+i);
+			append_ref(nth_desc, sha1, 1);
+		}
+	}
+	else if (all_heads + all_remotes)
+		snarf_refs(all_heads, all_remotes);
+	else {
+		while (0 < ac) {
+			append_one_rev(*av);
+			ac--; av++;
+		}
+	}
+
+	head_p = resolve_ref_unsafe("HEAD", head_sha1, 1, NULL);
+	if (head_p) {
+		head_len = strlen(head_p);
+		memcpy(head, head_p, head_len + 1);
+	}
+	else {
+		head_len = 0;
+		head[0] = 0;
+	}
+
+	if (with_current_branch && head_p) {
+		int has_head = 0;
+		for (i = 0; !has_head && i < ref_name_cnt; i++) {
+			/* We are only interested in adding the branch
+			 * HEAD points at.
+			 */
+			if (rev_is_head(head,
+					head_len,
+					ref_name[i],
+					head_sha1, NULL))
+				has_head++;
+		}
+		if (!has_head) {
+			int offset = !prefixcmp(head, "refs/heads/") ? 11 : 0;
+			append_one_rev(head + offset);
+		}
+	}
+
+	if (!ref_name_cnt) {
+		fprintf(stderr, "No revs to be shown.\n");
+		exit(0);
+	}
+
+	for (num_rev = 0; ref_name[num_rev]; num_rev++) {
+		unsigned char revkey[20];
+		unsigned int flag = 1u << (num_rev + REV_SHIFT);
+
+		if (MAX_REVS <= num_rev)
+			die("cannot handle more than %d revs.", MAX_REVS);
+		if (get_sha1(ref_name[num_rev], revkey))
+			die("'%s' is not a valid ref.", ref_name[num_rev]);
+		commit = lookup_commit_reference(revkey);
+		if (!commit)
+			die("cannot find commit %s (%s)",
+			    ref_name[num_rev], revkey);
+		parse_commit(commit);
+		mark_seen(commit, &seen);
+
+		/* rev#0 uses bit REV_SHIFT, rev#1 uses bit REV_SHIFT+1,
+		 * and so on.  REV_SHIFT bits from bit 0 are used for
+		 * internal bookkeeping.
+		 */
+		commit->object.flags |= flag;
+		if (commit->object.flags == flag)
+			commit_list_insert_by_date(commit, &list);
+		rev[num_rev] = commit;
+	}
+	for (i = 0; i < num_rev; i++)
+		rev_mask[i] = rev[i]->object.flags;
+
+	if (0 <= extra)
+		join_revs(&list, &seen, num_rev, extra);
+
+	commit_list_sort_by_date(&seen);
+
+	if (merge_base)
+		return show_merge_base(seen, num_rev);
+
+	if (independent)
+		return show_independent(rev, num_rev, ref_name, rev_mask);
+
+	/* Show list; --more=-1 means list-only */
+	if (1 < num_rev || extra < 0) {
+		for (i = 0; i < num_rev; i++) {
+			int j;
+			int is_head = rev_is_head(head,
+						  head_len,
+						  ref_name[i],
+						  head_sha1,
+						  rev[i]->object.sha1);
+			if (extra < 0)
+				printf("%c [%s] ",
+				       is_head ? '*' : ' ', ref_name[i]);
+			else {
+				for (j = 0; j < i; j++)
+					putchar(' ');
+				printf("%s%c%s [%s] ",
+				       get_color_code(i),
+				       is_head ? '*' : '!',
+				       get_color_reset_code(), ref_name[i]);
+			}
+
+			if (!reflog) {
+				/* header lines never need name */
+				show_one_commit(rev[i], 1);
+			}
+			else
+				puts(reflog_msg[i]);
+
+			if (is_head)
+				head_at = i;
+		}
+		if (0 <= extra) {
+			for (i = 0; i < num_rev; i++)
+				putchar('-');
+			putchar('\n');
+		}
+	}
+	if (extra < 0)
+		exit(0);
+
+	/* Sort topologically */
+	sort_in_topological_order(&seen, lifo);
+
+	/* Give names to commits */
+	if (!sha1_name && !no_name)
+		name_commits(seen, rev, ref_name, num_rev);
+
+	all_mask = ((1u << (REV_SHIFT + num_rev)) - 1);
+	all_revs = all_mask & ~((1u << REV_SHIFT) - 1);
+
+	while (seen) {
+		struct commit *commit = pop_one_commit(&seen);
+		int this_flag = commit->object.flags;
+		int is_merge_point = ((this_flag & all_revs) == all_revs);
+
+		shown_merge_point |= is_merge_point;
+
+		if (1 < num_rev) {
+			int is_merge = !!(commit->parents &&
+					  commit->parents->next);
+			if (topics &&
+			    !is_merge_point &&
+			    (this_flag & (1u << REV_SHIFT)))
+				continue;
+			if (dense && is_merge &&
+			    omit_in_dense(commit, rev, num_rev))
+				continue;
+			for (i = 0; i < num_rev; i++) {
+				int mark;
+				if (!(this_flag & (1u << (i + REV_SHIFT))))
+					mark = ' ';
+				else if (is_merge)
+					mark = '-';
+				else if (i == head_at)
+					mark = '*';
+				else
+					mark = '+';
+				printf("%s%c%s",
+				       get_color_code(i),
+				       mark, get_color_reset_code());
+			}
+			putchar(' ');
+		}
+		show_one_commit(commit, no_name);
+
+		if (shown_merge_point && --extra < 0)
+			break;
+	}
+	return 0;
+}
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
new file mode 100644
index 0000000..3911661
--- /dev/null
+++ b/builtin/show-ref.c
@@ -0,0 +1,250 @@
+#include "builtin.h"
+#include "cache.h"
+#include "refs.h"
+#include "object.h"
+#include "tag.h"
+#include "string-list.h"
+#include "parse-options.h"
+
+static const char * const show_ref_usage[] = {
+	"git show-ref [-q|--quiet] [--verify] [--head] [-d|--dereference] [-s|--hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [pattern*] ",
+	"git show-ref --exclude-existing[=pattern] < ref-list",
+	NULL
+};
+
+static int deref_tags, show_head, tags_only, heads_only, found_match, verify,
+	   quiet, hash_only, abbrev, exclude_arg;
+static const char **pattern;
+static const char *exclude_existing_arg;
+
+static void show_one(const char *refname, const unsigned char *sha1)
+{
+	const char *hex = find_unique_abbrev(sha1, abbrev);
+	if (hash_only)
+		printf("%s\n", hex);
+	else
+		printf("%s %s\n", hex, refname);
+}
+
+static int show_ref(const char *refname, const unsigned char *sha1, int flag, void *cbdata)
+{
+	struct object *obj;
+	const char *hex;
+	unsigned char peeled[20];
+
+	if (tags_only || heads_only) {
+		int match;
+
+		match = heads_only && !prefixcmp(refname, "refs/heads/");
+		match |= tags_only && !prefixcmp(refname, "refs/tags/");
+		if (!match)
+			return 0;
+	}
+	if (pattern) {
+		int reflen = strlen(refname);
+		const char **p = pattern, *m;
+		while ((m = *p++) != NULL) {
+			int len = strlen(m);
+			if (len > reflen)
+				continue;
+			if (memcmp(m, refname + reflen - len, len))
+				continue;
+			if (len == reflen)
+				goto match;
+			/* "--verify" requires an exact match */
+			if (verify)
+				continue;
+			if (refname[reflen - len - 1] == '/')
+				goto match;
+		}
+		return 0;
+	}
+
+match:
+	found_match++;
+
+	/* This changes the semantics slightly that even under quiet we
+	 * detect and return error if the repository is corrupt and
+	 * ref points at a nonexistent object.
+	 */
+	if (!has_sha1_file(sha1))
+		die("git show-ref: bad ref %s (%s)", refname,
+		    sha1_to_hex(sha1));
+
+	if (quiet)
+		return 0;
+
+	show_one(refname, sha1);
+
+	if (!deref_tags)
+		return 0;
+
+	if ((flag & REF_ISPACKED) && !peel_ref(refname, peeled)) {
+		if (!is_null_sha1(peeled)) {
+			hex = find_unique_abbrev(peeled, abbrev);
+			printf("%s %s^{}\n", hex, refname);
+		}
+	}
+	else {
+		obj = parse_object(sha1);
+		if (!obj)
+			die("git show-ref: bad ref %s (%s)", refname,
+			    sha1_to_hex(sha1));
+		if (obj->type == OBJ_TAG) {
+			obj = deref_tag(obj, refname, 0);
+			if (!obj)
+				die("git show-ref: bad tag at ref %s (%s)", refname,
+				    sha1_to_hex(sha1));
+			hex = find_unique_abbrev(obj->sha1, abbrev);
+			printf("%s %s^{}\n", hex, refname);
+		}
+	}
+	return 0;
+}
+
+static int add_existing(const char *refname, const unsigned char *sha1, int flag, void *cbdata)
+{
+	struct string_list *list = (struct string_list *)cbdata;
+	string_list_insert(list, refname);
+	return 0;
+}
+
+/*
+ * read "^(?:<anything>\s)?<refname>(?:\^\{\})?$" from the standard input,
+ * and
+ * (1) strip "^{}" at the end of line if any;
+ * (2) ignore if match is provided and does not head-match refname;
+ * (3) warn if refname is not a well-formed refname and skip;
+ * (4) ignore if refname is a ref that exists in the local repository;
+ * (5) otherwise output the line.
+ */
+static int exclude_existing(const char *match)
+{
+	static struct string_list existing_refs = STRING_LIST_INIT_NODUP;
+	char buf[1024];
+	int matchlen = match ? strlen(match) : 0;
+
+	for_each_ref(add_existing, &existing_refs);
+	while (fgets(buf, sizeof(buf), stdin)) {
+		char *ref;
+		int len = strlen(buf);
+
+		if (len > 0 && buf[len - 1] == '\n')
+			buf[--len] = '\0';
+		if (3 <= len && !strcmp(buf + len - 3, "^{}")) {
+			len -= 3;
+			buf[len] = '\0';
+		}
+		for (ref = buf + len; buf < ref; ref--)
+			if (isspace(ref[-1]))
+				break;
+		if (match) {
+			int reflen = buf + len - ref;
+			if (reflen < matchlen)
+				continue;
+			if (strncmp(ref, match, matchlen))
+				continue;
+		}
+		if (check_refname_format(ref, 0)) {
+			warning("ref '%s' ignored", ref);
+			continue;
+		}
+		if (!string_list_has_string(&existing_refs, ref)) {
+			printf("%s\n", buf);
+		}
+	}
+	return 0;
+}
+
+static int hash_callback(const struct option *opt, const char *arg, int unset)
+{
+	hash_only = 1;
+	/* Use full length SHA1 if no argument */
+	if (!arg)
+		return 0;
+	return parse_opt_abbrev_cb(opt, arg, unset);
+}
+
+static int exclude_existing_callback(const struct option *opt, const char *arg,
+				     int unset)
+{
+	exclude_arg = 1;
+	*(const char **)opt->value = arg;
+	return 0;
+}
+
+static int help_callback(const struct option *opt, const char *arg, int unset)
+{
+	return -1;
+}
+
+static const struct option show_ref_options[] = {
+	OPT_BOOLEAN(0, "tags", &tags_only, "only show tags (can be combined with heads)"),
+	OPT_BOOLEAN(0, "heads", &heads_only, "only show heads (can be combined with tags)"),
+	OPT_BOOLEAN(0, "verify", &verify, "stricter reference checking, "
+		    "requires exact ref path"),
+	{ OPTION_BOOLEAN, 'h', NULL, &show_head, NULL,
+	  "show the HEAD reference",
+	  PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
+	OPT_BOOLEAN(0, "head", &show_head, "show the HEAD reference"),
+	OPT_BOOLEAN('d', "dereference", &deref_tags,
+		    "dereference tags into object IDs"),
+	{ OPTION_CALLBACK, 's', "hash", &abbrev, "n",
+	  "only show SHA1 hash using <n> digits",
+	  PARSE_OPT_OPTARG, &hash_callback },
+	OPT__ABBREV(&abbrev),
+	OPT__QUIET(&quiet,
+		   "do not print results to stdout (useful with --verify)"),
+	{ OPTION_CALLBACK, 0, "exclude-existing", &exclude_existing_arg,
+	  "pattern", "show refs from stdin that aren't in local repository",
+	  PARSE_OPT_OPTARG | PARSE_OPT_NONEG, exclude_existing_callback },
+	{ OPTION_CALLBACK, 0, "help-all", NULL, NULL, "show usage",
+	  PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, help_callback },
+	OPT_END()
+};
+
+int cmd_show_ref(int argc, const char **argv, const char *prefix)
+{
+	if (argc == 2 && !strcmp(argv[1], "-h"))
+		usage_with_options(show_ref_usage, show_ref_options);
+
+	argc = parse_options(argc, argv, prefix, show_ref_options,
+			     show_ref_usage, PARSE_OPT_NO_INTERNAL_HELP);
+
+	if (exclude_arg)
+		return exclude_existing(exclude_existing_arg);
+
+	pattern = argv;
+	if (!*pattern)
+		pattern = NULL;
+
+	if (verify) {
+		if (!pattern)
+			die("--verify requires a reference");
+		while (*pattern) {
+			unsigned char sha1[20];
+
+			if (!prefixcmp(*pattern, "refs/") &&
+			    !read_ref(*pattern, sha1)) {
+				if (!quiet)
+					show_one(*pattern, sha1);
+			}
+			else if (!quiet)
+				die("'%s' - not a valid ref", *pattern);
+			else
+				return 1;
+			pattern++;
+		}
+		return 0;
+	}
+
+	if (show_head)
+		head_ref(show_ref, NULL);
+	for_each_ref(show_ref, NULL);
+	if (!found_match) {
+		if (verify && !quiet)
+			die("No match");
+		return 1;
+	}
+	return 0;
+}
diff --git a/builtin/stripspace.c b/builtin/stripspace.c
new file mode 100644
index 0000000..f16986c
--- /dev/null
+++ b/builtin/stripspace.c
@@ -0,0 +1,88 @@
+#include "builtin.h"
+#include "cache.h"
+
+/*
+ * Returns the length of a line, without trailing spaces.
+ *
+ * If the line ends with newline, it will be removed too.
+ */
+static size_t cleanup(char *line, size_t len)
+{
+	while (len) {
+		unsigned char c = line[len - 1];
+		if (!isspace(c))
+			break;
+		len--;
+	}
+
+	return len;
+}
+
+/*
+ * Remove empty lines from the beginning and end
+ * and also trailing spaces from every line.
+ *
+ * Turn multiple consecutive empty lines between paragraphs
+ * into just one empty line.
+ *
+ * If the input has only empty lines and spaces,
+ * no output will be produced.
+ *
+ * If last line does not have a newline at the end, one is added.
+ *
+ * Enable skip_comments to skip every line starting with "#".
+ */
+void stripspace(struct strbuf *sb, int skip_comments)
+{
+	int empties = 0;
+	size_t i, j, len, newlen;
+	char *eol;
+
+	/* We may have to add a newline. */
+	strbuf_grow(sb, 1);
+
+	for (i = j = 0; i < sb->len; i += len, j += newlen) {
+		eol = memchr(sb->buf + i, '\n', sb->len - i);
+		len = eol ? eol - (sb->buf + i) + 1 : sb->len - i;
+
+		if (skip_comments && len && sb->buf[i] == '#') {
+			newlen = 0;
+			continue;
+		}
+		newlen = cleanup(sb->buf + i, len);
+
+		/* Not just an empty line? */
+		if (newlen) {
+			if (empties > 0 && j > 0)
+				sb->buf[j++] = '\n';
+			empties = 0;
+			memmove(sb->buf + j, sb->buf + i, newlen);
+			sb->buf[newlen + j++] = '\n';
+		} else {
+			empties++;
+		}
+	}
+
+	strbuf_setlen(sb, j);
+}
+
+int cmd_stripspace(int argc, const char **argv, const char *prefix)
+{
+	struct strbuf buf = STRBUF_INIT;
+	int strip_comments = 0;
+
+	if (argc == 2 && (!strcmp(argv[1], "-s") ||
+				!strcmp(argv[1], "--strip-comments")))
+		strip_comments = 1;
+	else if (argc > 1)
+		usage("git stripspace [-s | --strip-comments] < input");
+
+	if (strbuf_read(&buf, 0, 1024) < 0)
+		die_errno("could not read the input");
+
+	stripspace(&buf, strip_comments);
+
+	write_or_die(1, buf.buf, buf.len);
+	strbuf_release(&buf);
+	return 0;
+}
diff --git a/builtin/symbolic-ref.c b/builtin/symbolic-ref.c
new file mode 100644
index 0000000..801d62e
--- /dev/null
+++ b/builtin/symbolic-ref.c
@@ -0,0 +1,63 @@
+#include "builtin.h"
+#include "cache.h"
+#include "refs.h"
+#include "parse-options.h"
+
+static const char * const git_symbolic_ref_usage[] = {
+	"git symbolic-ref [options] name [ref]",
+	NULL
+};
+
+static int shorten;
+
+static void check_symref(const char *HEAD, int quiet)
+{
+	unsigned char sha1[20];
+	int flag;
+	const char *refname = resolve_ref_unsafe(HEAD, sha1, 0, &flag);
+
+	if (!refname)
+		die("No such ref: %s", HEAD);
+	else if (!(flag & REF_ISSYMREF)) {
+		if (!quiet)
+			die("ref %s is not a symbolic ref", HEAD);
+		else
+			exit(1);
+	}
+	if (shorten)
+		refname = shorten_unambiguous_ref(refname, 0);
+	puts(refname);
+}
+
+int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
+{
+	int quiet = 0;
+	const char *msg = NULL;
+	struct option options[] = {
+		OPT__QUIET(&quiet,
+			"suppress error message for non-symbolic (detached) refs"),
+		OPT_BOOL(0, "short", &shorten, "shorten ref output"),
+		OPT_STRING('m', NULL, &msg, "reason", "reason of the update"),
+		OPT_END(),
+	};
+
+	git_config(git_default_config, NULL);
+	argc = parse_options(argc, argv, prefix, options,
+			     git_symbolic_ref_usage, 0);
+	if (msg &&!*msg)
+		die("Refusing to perform update with empty message");
+	switch (argc) {
+	case 1:
+		check_symref(argv[0], quiet);
+		break;
+	case 2:
+		if (!strcmp(argv[0], "HEAD") &&
+		    prefixcmp(argv[1], "refs/"))
+			die("Refusing to point HEAD outside of refs/");
+		create_symref(argv[0], argv[1], msg);
+		break;
+	default:
+		usage_with_options(git_symbolic_ref_usage, options);
+	}
+	return 0;
+}
diff --git a/builtin/tag.c b/builtin/tag.c
new file mode 100644
index 0000000..7b1be85
--- /dev/null
+++ b/builtin/tag.c
@@ -0,0 +1,593 @@
+/*
+ * Builtin "git tag"
+ *
+ * Copyright (c) 2007 Kristian Høgsberg <krh@redhat.com>,
+ *                    Carlos Rica <jasampler@gmail.com>
+ * Based on git-tag.sh and mktag.c by Linus Torvalds.
+ */
+
+#include "cache.h"
+#include "builtin.h"
+#include "refs.h"
+#include "tag.h"
+#include "run-command.h"
+#include "parse-options.h"
+#include "diff.h"
+#include "revision.h"
+#include "gpg-interface.h"
+#include "sha1-array.h"
+#include "column.h"
+
+static const char * const git_tag_usage[] = {
+	"git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]",
+	"git tag -d <tagname>...",
+	"git tag -l [-n[<num>]] [--contains <commit>] [--points-at <object>] "
+		"\n\t\t[<pattern>...]",
+	"git tag -v <tagname>...",
+	NULL
+};
+
+struct tag_filter {
+	const char **patterns;
+	int lines;
+	struct commit_list *with_commit;
+};
+
+static struct sha1_array points_at;
+static unsigned int colopts;
+
+static int match_pattern(const char **patterns, const char *ref)
+{
+	/* no pattern means match everything */
+	if (!*patterns)
+		return 1;
+	for (; *patterns; patterns++)
+		if (!fnmatch(*patterns, ref, 0))
+			return 1;
+	return 0;
+}
+
+static const unsigned char *match_points_at(const char *refname,
+					    const unsigned char *sha1)
+{
+	const unsigned char *tagged_sha1 = NULL;
+	struct object *obj;
+
+	if (sha1_array_lookup(&points_at, sha1) >= 0)
+		return sha1;
+	obj = parse_object(sha1);
+	if (!obj)
+		die(_("malformed object at '%s'"), refname);
+	if (obj->type == OBJ_TAG)
+		tagged_sha1 = ((struct tag *)obj)->tagged->sha1;
+	if (tagged_sha1 && sha1_array_lookup(&points_at, tagged_sha1) >= 0)
+		return tagged_sha1;
+	return NULL;
+}
+
+static int in_commit_list(const struct commit_list *want, struct commit *c)
+{
+	for (; want; want = want->next)
+		if (!hashcmp(want->item->object.sha1, c->object.sha1))
+			return 1;
+	return 0;
+}
+
+static int contains_recurse(struct commit *candidate,
+			    const struct commit_list *want)
+{
+	struct commit_list *p;
+
+	/* was it previously marked as containing a want commit? */
+	if (candidate->object.flags & TMP_MARK)
+		return 1;
+	/* or marked as not possibly containing a want commit? */
+	if (candidate->object.flags & UNINTERESTING)
+		return 0;
+	/* or are we it? */
+	if (in_commit_list(want, candidate))
+		return 1;
+
+	if (parse_commit(candidate) < 0)
+		return 0;
+
+	/* Otherwise recurse and mark ourselves for future traversals. */
+	for (p = candidate->parents; p; p = p->next) {
+		if (contains_recurse(p->item, want)) {
+			candidate->object.flags |= TMP_MARK;
+			return 1;
+		}
+	}
+	candidate->object.flags |= UNINTERESTING;
+	return 0;
+}
+
+static int contains(struct commit *candidate, const struct commit_list *want)
+{
+	return contains_recurse(candidate, want);
+}
+
+static void show_tag_lines(const unsigned char *sha1, int lines)
+{
+	int i;
+	unsigned long size;
+	enum object_type type;
+	char *buf, *sp, *eol;
+	size_t len;
+
+	buf = read_sha1_file(sha1, &type, &size);
+	if (!buf)
+		die_errno("unable to read object %s", sha1_to_hex(sha1));
+	if (type != OBJ_COMMIT && type != OBJ_TAG)
+		goto free_return;
+	if (!size)
+		die("an empty %s object %s?",
+		    typename(type), sha1_to_hex(sha1));
+
+	/* skip header */
+	sp = strstr(buf, "\n\n");
+	if (!sp)
+		goto free_return;
+
+	/* only take up to "lines" lines, and strip the signature from a tag */
+	if (type == OBJ_TAG)
+		size = parse_signature(buf, size);
+	for (i = 0, sp += 2; i < lines && sp < buf + size; i++) {
+		if (i)
+			printf("\n    ");
+		eol = memchr(sp, '\n', size - (sp - buf));
+		len = eol ? eol - sp : size - (sp - buf);
+		fwrite(sp, len, 1, stdout);
+		if (!eol)
+			break;
+		sp = eol + 1;
+	}
+free_return:
+	free(buf);
+}
+
+static int show_reference(const char *refname, const unsigned char *sha1,
+			  int flag, void *cb_data)
+{
+	struct tag_filter *filter = cb_data;
+
+	if (match_pattern(filter->patterns, refname)) {
+		if (filter->with_commit) {
+			struct commit *commit;
+
+			commit = lookup_commit_reference_gently(sha1, 1);
+			if (!commit)
+				return 0;
+			if (!contains(commit, filter->with_commit))
+				return 0;
+		}
+
+		if (points_at.nr && !match_points_at(refname, sha1))
+			return 0;
+
+		if (!filter->lines) {
+			printf("%s\n", refname);
+			return 0;
+		}
+		printf("%-15s ", refname);
+		show_tag_lines(sha1, filter->lines);
+		putchar('\n');
+	}
+
+	return 0;
+}
+
+static int list_tags(const char **patterns, int lines,
+			struct commit_list *with_commit)
+{
+	struct tag_filter filter;
+
+	filter.patterns = patterns;
+	filter.lines = lines;
+	filter.with_commit = with_commit;
+
+	for_each_tag_ref(show_reference, (void *) &filter);
+
+	return 0;
+}
+
+typedef int (*each_tag_name_fn)(const char *name, const char *ref,
+				const unsigned char *sha1);
+
+static int for_each_tag_name(const char **argv, each_tag_name_fn fn)
+{
+	const char **p;
+	char ref[PATH_MAX];
+	int had_error = 0;
+	unsigned char sha1[20];
+
+	for (p = argv; *p; p++) {
+		if (snprintf(ref, sizeof(ref), "refs/tags/%s", *p)
+					>= sizeof(ref)) {
+			error(_("tag name too long: %.*s..."), 50, *p);
+			had_error = 1;
+			continue;
+		}
+		if (read_ref(ref, sha1)) {
+			error(_("tag '%s' not found."), *p);
+			had_error = 1;
+			continue;
+		}
+		if (fn(*p, ref, sha1))
+			had_error = 1;
+	}
+	return had_error;
+}
+
+static int delete_tag(const char *name, const char *ref,
+				const unsigned char *sha1)
+{
+	if (delete_ref(ref, sha1, 0))
+		return 1;
+	printf(_("Deleted tag '%s' (was %s)\n"), name, find_unique_abbrev(sha1, DEFAULT_ABBREV));
+	return 0;
+}
+
+static int verify_tag(const char *name, const char *ref,
+				const unsigned char *sha1)
+{
+	const char *argv_verify_tag[] = {"verify-tag",
+					"-v", "SHA1_HEX", NULL};
+	argv_verify_tag[2] = sha1_to_hex(sha1);
+
+	if (run_command_v_opt(argv_verify_tag, RUN_GIT_CMD))
+		return error(_("could not verify the tag '%s'"), name);
+	return 0;
+}
+
+static int do_sign(struct strbuf *buffer)
+{
+	return sign_buffer(buffer, buffer, get_signing_key());
+}
+
+static const char tag_template[] =
+	N_("\n"
+	"#\n"
+	"# Write a tag message\n"
+	"# Lines starting with '#' will be ignored.\n"
+	"#\n");
+
+static const char tag_template_nocleanup[] =
+	N_("\n"
+	"#\n"
+	"# Write a tag message\n"
+	"# Lines starting with '#' will be kept; you may remove them"
+	" yourself if you want to.\n"
+	"#\n");
+
+static int git_tag_config(const char *var, const char *value, void *cb)
+{
+	int status = git_gpg_config(var, value, cb);
+	if (status)
+		return status;
+	if (!prefixcmp(var, "column."))
+		return git_column_config(var, value, "tag", &colopts);
+	return git_default_config(var, value, cb);
+}
+
+static void write_tag_body(int fd, const unsigned char *sha1)
+{
+	unsigned long size;
+	enum object_type type;
+	char *buf, *sp;
+
+	buf = read_sha1_file(sha1, &type, &size);
+	if (!buf)
+		return;
+	/* skip header */
+	sp = strstr(buf, "\n\n");
+
+	if (!sp || !size || type != OBJ_TAG) {
+		free(buf);
+		return;
+	}
+	sp += 2; /* skip the 2 LFs */
+	write_or_die(fd, sp, parse_signature(sp, buf + size - sp));
+
+	free(buf);
+}
+
+static int build_tag_object(struct strbuf *buf, int sign, unsigned char *result)
+{
+	if (sign && do_sign(buf) < 0)
+		return error(_("unable to sign the tag"));
+	if (write_sha1_file(buf->buf, buf->len, tag_type, result) < 0)
+		return error(_("unable to write tag file"));
+	return 0;
+}
+
+struct create_tag_options {
+	unsigned int message_given:1;
+	unsigned int sign;
+	enum {
+		CLEANUP_NONE,
+		CLEANUP_SPACE,
+		CLEANUP_ALL
+	} cleanup_mode;
+};
+
+static void create_tag(const unsigned char *object, const char *tag,
+		       struct strbuf *buf, struct create_tag_options *opt,
+		       unsigned char *prev, unsigned char *result)
+{
+	enum object_type type;
+	char header_buf[1024];
+	int header_len;
+	char *path = NULL;
+
+	type = sha1_object_info(object, NULL);
+	if (type <= OBJ_NONE)
+	    die(_("bad object type."));
+
+	header_len = snprintf(header_buf, sizeof(header_buf),
+			  "object %s\n"
+			  "type %s\n"
+			  "tag %s\n"
+			  "tagger %s\n\n",
+			  sha1_to_hex(object),
+			  typename(type),
+			  tag,
+			  git_committer_info(IDENT_STRICT));
+
+	if (header_len > sizeof(header_buf) - 1)
+		die(_("tag header too big."));
+
+	if (!opt->message_given) {
+		int fd;
+
+		/* write the template message before editing: */
+		path = git_pathdup("TAG_EDITMSG");
+		fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
+		if (fd < 0)
+			die_errno(_("could not create file '%s'"), path);
+
+		if (!is_null_sha1(prev))
+			write_tag_body(fd, prev);
+		else if (opt->cleanup_mode == CLEANUP_ALL)
+			write_or_die(fd, _(tag_template),
+					strlen(_(tag_template)));
+		else
+			write_or_die(fd, _(tag_template_nocleanup),
+					strlen(_(tag_template_nocleanup)));
+		close(fd);
+
+		if (launch_editor(path, buf, NULL)) {
+			fprintf(stderr,
+			_("Please supply the message using either -m or -F option.\n"));
+			exit(1);
+		}
+	}
+
+	if (opt->cleanup_mode != CLEANUP_NONE)
+		stripspace(buf, opt->cleanup_mode == CLEANUP_ALL);
+
+	if (!opt->message_given && !buf->len)
+		die(_("no tag message?"));
+
+	strbuf_insert(buf, 0, header_buf, header_len);
+
+	if (build_tag_object(buf, opt->sign, result) < 0) {
+		if (path)
+			fprintf(stderr, _("The tag message has been left in %s\n"),
+				path);
+		exit(128);
+	}
+	if (path) {
+		unlink_or_warn(path);
+		free(path);
+	}
+}
+
+struct msg_arg {
+	int given;
+	struct strbuf buf;
+};
+
+static int parse_msg_arg(const struct option *opt, const char *arg, int unset)
+{
+	struct msg_arg *msg = opt->value;
+
+	if (!arg)
+		return -1;
+	if (msg->buf.len)
+		strbuf_addstr(&(msg->buf), "\n\n");
+	strbuf_addstr(&(msg->buf), arg);
+	msg->given = 1;
+	return 0;
+}
+
+static int strbuf_check_tag_ref(struct strbuf *sb, const char *name)
+{
+	if (name[0] == '-')
+		return -1;
+
+	strbuf_reset(sb);
+	strbuf_addf(sb, "refs/tags/%s", name);
+
+	return check_refname_format(sb->buf, 0);
+}
+
+static int parse_opt_points_at(const struct option *opt __attribute__((unused)),
+			const char *arg, int unset)
+{
+	unsigned char sha1[20];
+
+	if (unset) {
+		sha1_array_clear(&points_at);
+		return 0;
+	}
+	if (!arg)
+		return error(_("switch 'points-at' requires an object"));
+	if (get_sha1(arg, sha1))
+		return error(_("malformed object name '%s'"), arg);
+	sha1_array_append(&points_at, sha1);
+	return 0;
+}
+
+int cmd_tag(int argc, const char **argv, const char *prefix)
+{
+	struct strbuf buf = STRBUF_INIT;
+	struct strbuf ref = STRBUF_INIT;
+	unsigned char object[20], prev[20];
+	const char *object_ref, *tag;
+	struct ref_lock *lock;
+	struct create_tag_options opt;
+	char *cleanup_arg = NULL;
+	int annotate = 0, force = 0, lines = -1, list = 0,
+		delete = 0, verify = 0;
+	const char *msgfile = NULL, *keyid = NULL;
+	struct msg_arg msg = { 0, STRBUF_INIT };
+	struct commit_list *with_commit = NULL;
+	struct option options[] = {
+		OPT_BOOLEAN('l', "list", &list, "list tag names"),
+		{ OPTION_INTEGER, 'n', NULL, &lines, "n",
+				"print <n> lines of each tag message",
+				PARSE_OPT_OPTARG, NULL, 1 },
+		OPT_BOOLEAN('d', "delete", &delete, "delete tags"),
+		OPT_BOOLEAN('v', "verify", &verify, "verify tags"),
+
+		OPT_GROUP("Tag creation options"),
+		OPT_BOOLEAN('a', "annotate", &annotate,
+					"annotated tag, needs a message"),
+		OPT_CALLBACK('m', "message", &msg, "message",
+			     "tag message", parse_msg_arg),
+		OPT_FILENAME('F', "file", &msgfile, "read message from file"),
+		OPT_BOOLEAN('s', "sign", &opt.sign, "annotated and GPG-signed tag"),
+		OPT_STRING(0, "cleanup", &cleanup_arg, "mode",
+			"how to strip spaces and #comments from message"),
+		OPT_STRING('u', "local-user", &keyid, "key-id",
+					"use another key to sign the tag"),
+		OPT__FORCE(&force, "replace the tag if exists"),
+		OPT_COLUMN(0, "column", &colopts, "show tag list in columns"),
+
+		OPT_GROUP("Tag listing options"),
+		{
+			OPTION_CALLBACK, 0, "contains", &with_commit, "commit",
+			"print only tags that contain the commit",
+			PARSE_OPT_LASTARG_DEFAULT,
+			parse_opt_with_commit, (intptr_t)"HEAD",
+		},
+		{
+			OPTION_CALLBACK, 0, "points-at", NULL, "object",
+			"print only tags of the object", 0, parse_opt_points_at
+		},
+		OPT_END()
+	};
+
+	git_config(git_tag_config, NULL);
+
+	memset(&opt, 0, sizeof(opt));
+
+	argc = parse_options(argc, argv, prefix, options, git_tag_usage, 0);
+
+	if (keyid) {
+		opt.sign = 1;
+		set_signing_key(keyid);
+	}
+	if (opt.sign)
+		annotate = 1;
+	if (argc == 0 && !(delete || verify))
+		list = 1;
+
+	if ((annotate || msg.given || msgfile || force) &&
+	    (list || delete || verify))
+		usage_with_options(git_tag_usage, options);
+
+	if (list + delete + verify > 1)
+		usage_with_options(git_tag_usage, options);
+	finalize_colopts(&colopts, -1);
+	if (list && lines != -1) {
+		if (explicitly_enable_column(colopts))
+			die(_("--column and -n are incompatible"));
+		colopts = 0;
+	}
+	if (list) {
+		int ret;
+		if (column_active(colopts)) {
+			struct column_options copts;
+			memset(&copts, 0, sizeof(copts));
+			copts.padding = 2;
+			run_column_filter(colopts, &copts);
+		}
+		ret = list_tags(argv, lines == -1 ? 0 : lines, with_commit);
+		if (column_active(colopts))
+			stop_column_filter();
+		return ret;
+	}
+	if (lines != -1)
+		die(_("-n option is only allowed with -l."));
+	if (with_commit)
+		die(_("--contains option is only allowed with -l."));
+	if (points_at.nr)
+		die(_("--points-at option is only allowed with -l."));
+	if (delete)
+		return for_each_tag_name(argv, delete_tag);
+	if (verify)
+		return for_each_tag_name(argv, verify_tag);
+
+	if (msg.given || msgfile) {
+		if (msg.given && msgfile)
+			die(_("only one -F or -m option is allowed."));
+		annotate = 1;
+		if (msg.given)
+			strbuf_addbuf(&buf, &(msg.buf));
+		else {
+			if (!strcmp(msgfile, "-")) {
+				if (strbuf_read(&buf, 0, 1024) < 0)
+					die_errno(_("cannot read '%s'"), msgfile);
+			} else {
+				if (strbuf_read_file(&buf, msgfile, 1024) < 0)
+					die_errno(_("could not open or read '%s'"),
+						msgfile);
+			}
+		}
+	}
+
+	tag = argv[0];
+
+	object_ref = argc == 2 ? argv[1] : "HEAD";
+	if (argc > 2)
+		die(_("too many params"));
+
+	if (get_sha1(object_ref, object))
+		die(_("Failed to resolve '%s' as a valid ref."), object_ref);
+
+	if (strbuf_check_tag_ref(&ref, tag))
+		die(_("'%s' is not a valid tag name."), tag);
+
+	if (read_ref(ref.buf, prev))
+		hashclr(prev);
+	else if (!force)
+		die(_("tag '%s' already exists"), tag);
+
+	opt.message_given = msg.given || msgfile;
+
+	if (!cleanup_arg || !strcmp(cleanup_arg, "strip"))
+		opt.cleanup_mode = CLEANUP_ALL;
+	else if (!strcmp(cleanup_arg, "verbatim"))
+		opt.cleanup_mode = CLEANUP_NONE;
+	else if (!strcmp(cleanup_arg, "whitespace"))
+		opt.cleanup_mode = CLEANUP_SPACE;
+	else
+		die(_("Invalid cleanup mode %s"), cleanup_arg);
+
+	if (annotate)
+		create_tag(object, tag, &buf, &opt, prev, object);
+
+	lock = lock_any_ref_for_update(ref.buf, prev, 0);
+	if (!lock)
+		die(_("%s: cannot lock the ref"), ref.buf);
+	if (write_ref_sha1(lock, object, NULL) < 0)
+		die(_("%s: cannot update the ref"), ref.buf);
+	if (force && hashcmp(prev, object))
+		printf(_("Updated tag '%s' (was %s)\n"), tag, find_unique_abbrev(prev, DEFAULT_ABBREV));
+
+	strbuf_release(&buf);
+	strbuf_release(&ref);
+	return 0;
+}
diff --git a/builtin/tar-tree.c b/builtin/tar-tree.c
new file mode 100644
index 0000000..3f1e701
--- /dev/null
+++ b/builtin/tar-tree.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2005, 2006 Rene Scharfe
+ */
+#include "cache.h"
+#include "commit.h"
+#include "tar.h"
+#include "builtin.h"
+#include "quote.h"
+
+static const char tar_tree_usage[] =
+"git tar-tree [--remote=<repo>] <tree-ish> [basedir]\n"
+"*** Note that this command is now deprecated; use \"git archive\" instead.";
+
+static const char builtin_get_tar_commit_id_usage[] =
+"git get-tar-commit-id < <tarfile>";
+
+int cmd_tar_tree(int argc, const char **argv, const char *prefix)
+{
+	/*
+	 * "git tar-tree" is now a wrapper around "git archive --format=tar"
+	 *
+	 * $0 --remote=<repo> arg... ==>
+	 *	git archive --format=tar --remote=<repo> arg...
+	 * $0 tree-ish ==>
+	 *	git archive --format=tar tree-ish
+	 * $0 tree-ish basedir ==>
+	 * 	git archive --format-tar --prefix=basedir tree-ish
+	 */
+	int i;
+	const char **nargv = xcalloc(sizeof(*nargv), argc + 3);
+	char *basedir_arg;
+	int nargc = 0;
+
+	nargv[nargc++] = "archive";
+	nargv[nargc++] = "--format=tar";
+
+	if (2 <= argc && !prefixcmp(argv[1], "--remote=")) {
+		nargv[nargc++] = argv[1];
+		argv++;
+		argc--;
+	}
+
+	/*
+	 * Because it's just a compatibility wrapper, tar-tree supports only
+	 * the old behaviour of reading attributes from the work tree.
+	 */
+	nargv[nargc++] = "--worktree-attributes";
+
+	switch (argc) {
+	default:
+		usage(tar_tree_usage);
+		break;
+	case 3:
+		/* base-path */
+		basedir_arg = xmalloc(strlen(argv[2]) + 11);
+		sprintf(basedir_arg, "--prefix=%s/", argv[2]);
+		nargv[nargc++] = basedir_arg;
+		/* fallthru */
+	case 2:
+		/* tree-ish */
+		nargv[nargc++] = argv[1];
+	}
+	nargv[nargc] = NULL;
+
+	fprintf(stderr,
+		"*** \"git tar-tree\" is now deprecated.\n"
+		"*** Running \"git archive\" instead.\n***");
+	for (i = 0; i < nargc; i++) {
+		fputc(' ', stderr);
+		sq_quote_print(stderr, nargv[i]);
+	}
+	fputc('\n', stderr);
+	return cmd_archive(nargc, nargv, prefix);
+}
+
+/* ustar header + extended global header content */
+#define RECORDSIZE	(512)
+#define HEADERSIZE (2 * RECORDSIZE)
+
+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;
+	char *content = buffer + RECORDSIZE;
+	ssize_t n;
+
+	if (argc != 1)
+		usage(builtin_get_tar_commit_id_usage);
+
+	n = read_in_full(0, buffer, HEADERSIZE);
+	if (n < HEADERSIZE)
+		die("git get-tar-commit-id: read error");
+	if (header->typeflag[0] != 'g')
+		return 1;
+	if (memcmp(content, "52 comment=", 11))
+		return 1;
+
+	n = write_in_full(1, content + 11, 41);
+	if (n < 41)
+		die_errno("git get-tar-commit-id: write error");
+
+	return 0;
+}
diff --git a/builtin/unpack-file.c b/builtin/unpack-file.c
new file mode 100644
index 0000000..1920029
--- /dev/null
+++ b/builtin/unpack-file.c
@@ -0,0 +1,36 @@
+#include "builtin.h"
+
+static char *create_temp_file(unsigned char *sha1)
+{
+	static char path[50];
+	void *buf;
+	enum object_type type;
+	unsigned long size;
+	int fd;
+
+	buf = read_sha1_file(sha1, &type, &size);
+	if (!buf || type != OBJ_BLOB)
+		die("unable to read blob object %s", sha1_to_hex(sha1));
+
+	strcpy(path, ".merge_file_XXXXXX");
+	fd = xmkstemp(path);
+	if (write_in_full(fd, buf, size) != size)
+		die_errno("unable to write temp-file");
+	close(fd);
+	return path;
+}
+
+int cmd_unpack_file(int argc, const char **argv, const char *prefix)
+{
+	unsigned char sha1[20];
+
+	if (argc != 2 || !strcmp(argv[1], "-h"))
+		usage("git unpack-file <sha1>");
+	if (get_sha1(argv[1], sha1))
+		die("Not a valid object name %s", argv[1]);
+
+	git_config(git_default_config, NULL);
+
+	puts(create_temp_file(sha1));
+	return 0;
+}
diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c
new file mode 100644
index 0000000..2217d7b
--- /dev/null
+++ b/builtin/unpack-objects.c
@@ -0,0 +1,568 @@
+#include "builtin.h"
+#include "cache.h"
+#include "object.h"
+#include "delta.h"
+#include "pack.h"
+#include "blob.h"
+#include "commit.h"
+#include "tag.h"
+#include "tree.h"
+#include "tree-walk.h"
+#include "progress.h"
+#include "decorate.h"
+#include "fsck.h"
+
+static int dry_run, quiet, recover, has_errors, strict;
+static const char unpack_usage[] = "git unpack-objects [-n] [-q] [-r] [--strict] < pack-file";
+
+/* We always read in 4kB chunks. */
+static unsigned char buffer[4096];
+static unsigned int offset, len;
+static off_t consumed_bytes;
+static git_SHA_CTX ctx;
+
+/*
+ * When running under --strict mode, objects whose reachability are
+ * suspect are kept in core without getting written in the object
+ * store.
+ */
+struct obj_buffer {
+	char *buffer;
+	unsigned long size;
+};
+
+static struct decoration obj_decorate;
+
+static struct obj_buffer *lookup_object_buffer(struct object *base)
+{
+	return lookup_decoration(&obj_decorate, base);
+}
+
+static void add_object_buffer(struct object *object, char *buffer, unsigned long size)
+{
+	struct obj_buffer *obj;
+	obj = xcalloc(1, sizeof(struct obj_buffer));
+	obj->buffer = buffer;
+	obj->size = size;
+	if (add_decoration(&obj_decorate, object, obj))
+		die("object %s tried to add buffer twice!", sha1_to_hex(object->sha1));
+}
+
+/*
+ * Make sure at least "min" bytes are available in the buffer, and
+ * return the pointer to the buffer.
+ */
+static void *fill(int min)
+{
+	if (min <= len)
+		return buffer + offset;
+	if (min > sizeof(buffer))
+		die("cannot fill %d bytes", min);
+	if (offset) {
+		git_SHA1_Update(&ctx, buffer, offset);
+		memmove(buffer, buffer + offset, len);
+		offset = 0;
+	}
+	do {
+		ssize_t ret = xread(0, buffer + len, sizeof(buffer) - len);
+		if (ret <= 0) {
+			if (!ret)
+				die("early EOF");
+			die_errno("read error on input");
+		}
+		len += ret;
+	} while (len < min);
+	return buffer;
+}
+
+static void use(int bytes)
+{
+	if (bytes > len)
+		die("used more bytes than were available");
+	len -= bytes;
+	offset += bytes;
+
+	/* make sure off_t is sufficiently large not to wrap */
+	if (signed_add_overflows(consumed_bytes, bytes))
+		die("pack too large for current definition of off_t");
+	consumed_bytes += bytes;
+}
+
+static void *get_data(unsigned long size)
+{
+	git_zstream stream;
+	void *buf = xmalloc(size);
+
+	memset(&stream, 0, sizeof(stream));
+
+	stream.next_out = buf;
+	stream.avail_out = size;
+	stream.next_in = fill(1);
+	stream.avail_in = len;
+	git_inflate_init(&stream);
+
+	for (;;) {
+		int ret = git_inflate(&stream, 0);
+		use(len - stream.avail_in);
+		if (stream.total_out == size && ret == Z_STREAM_END)
+			break;
+		if (ret != Z_OK) {
+			error("inflate returned %d", ret);
+			free(buf);
+			buf = NULL;
+			if (!recover)
+				exit(1);
+			has_errors = 1;
+			break;
+		}
+		stream.next_in = fill(1);
+		stream.avail_in = len;
+	}
+	git_inflate_end(&stream);
+	return buf;
+}
+
+struct delta_info {
+	unsigned char base_sha1[20];
+	unsigned nr;
+	off_t base_offset;
+	unsigned long size;
+	void *delta;
+	struct delta_info *next;
+};
+
+static struct delta_info *delta_list;
+
+static void add_delta_to_list(unsigned nr, unsigned const char *base_sha1,
+			      off_t base_offset,
+			      void *delta, unsigned long size)
+{
+	struct delta_info *info = xmalloc(sizeof(*info));
+
+	hashcpy(info->base_sha1, base_sha1);
+	info->base_offset = base_offset;
+	info->size = size;
+	info->delta = delta;
+	info->nr = nr;
+	info->next = delta_list;
+	delta_list = info;
+}
+
+struct obj_info {
+	off_t offset;
+	unsigned char sha1[20];
+	struct object *obj;
+};
+
+#define FLAG_OPEN (1u<<20)
+#define FLAG_WRITTEN (1u<<21)
+
+static struct obj_info *obj_list;
+static unsigned nr_objects;
+
+/*
+ * Called only from check_object() after it verified this object
+ * is Ok.
+ */
+static void write_cached_object(struct object *obj)
+{
+	unsigned char sha1[20];
+	struct obj_buffer *obj_buf = lookup_object_buffer(obj);
+	if (write_sha1_file(obj_buf->buffer, obj_buf->size, typename(obj->type), sha1) < 0)
+		die("failed to write object %s", sha1_to_hex(obj->sha1));
+	obj->flags |= FLAG_WRITTEN;
+}
+
+/*
+ * At the very end of the processing, write_rest() scans the objects
+ * that have reachability requirements and calls this function.
+ * Verify its reachability and validity recursively and write it out.
+ */
+static int check_object(struct object *obj, int type, void *data)
+{
+	if (!obj)
+		return 1;
+
+	if (obj->flags & FLAG_WRITTEN)
+		return 0;
+
+	if (type != OBJ_ANY && obj->type != type)
+		die("object type mismatch");
+
+	if (!(obj->flags & FLAG_OPEN)) {
+		unsigned long size;
+		int type = sha1_object_info(obj->sha1, &size);
+		if (type != obj->type || type <= 0)
+			die("object of unexpected type");
+		obj->flags |= FLAG_WRITTEN;
+		return 0;
+	}
+
+	if (fsck_object(obj, 1, fsck_error_function))
+		die("Error in object");
+	if (fsck_walk(obj, check_object, NULL))
+		die("Error on reachable objects of %s", sha1_to_hex(obj->sha1));
+	write_cached_object(obj);
+	return 0;
+}
+
+static void write_rest(void)
+{
+	unsigned i;
+	for (i = 0; i < nr_objects; i++) {
+		if (obj_list[i].obj)
+			check_object(obj_list[i].obj, OBJ_ANY, NULL);
+	}
+}
+
+static void added_object(unsigned nr, enum object_type type,
+			 void *data, unsigned long size);
+
+/*
+ * Write out nr-th object from the list, now we know the contents
+ * of it.  Under --strict, this buffers structured objects in-core,
+ * to be checked at the end.
+ */
+static void write_object(unsigned nr, enum object_type type,
+			 void *buf, unsigned long size)
+{
+	if (!strict) {
+		if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0)
+			die("failed to write object");
+		added_object(nr, type, buf, size);
+		free(buf);
+		obj_list[nr].obj = NULL;
+	} else if (type == OBJ_BLOB) {
+		struct blob *blob;
+		if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0)
+			die("failed to write object");
+		added_object(nr, type, buf, size);
+		free(buf);
+
+		blob = lookup_blob(obj_list[nr].sha1);
+		if (blob)
+			blob->object.flags |= FLAG_WRITTEN;
+		else
+			die("invalid blob object");
+		obj_list[nr].obj = NULL;
+	} else {
+		struct object *obj;
+		int eaten;
+		hash_sha1_file(buf, size, typename(type), obj_list[nr].sha1);
+		added_object(nr, type, buf, size);
+		obj = parse_object_buffer(obj_list[nr].sha1, type, size, buf, &eaten);
+		if (!obj)
+			die("invalid %s", typename(type));
+		add_object_buffer(obj, buf, size);
+		obj->flags |= FLAG_OPEN;
+		obj_list[nr].obj = obj;
+	}
+}
+
+static void resolve_delta(unsigned nr, enum object_type type,
+			  void *base, unsigned long base_size,
+			  void *delta, unsigned long delta_size)
+{
+	void *result;
+	unsigned long result_size;
+
+	result = patch_delta(base, base_size,
+			     delta, delta_size,
+			     &result_size);
+	if (!result)
+		die("failed to apply delta");
+	free(delta);
+	write_object(nr, type, result, result_size);
+}
+
+/*
+ * We now know the contents of an object (which is nr-th in the pack);
+ * resolve all the deltified objects that are based on it.
+ */
+static void added_object(unsigned nr, enum object_type type,
+			 void *data, unsigned long size)
+{
+	struct delta_info **p = &delta_list;
+	struct delta_info *info;
+
+	while ((info = *p) != NULL) {
+		if (!hashcmp(info->base_sha1, obj_list[nr].sha1) ||
+		    info->base_offset == obj_list[nr].offset) {
+			*p = info->next;
+			p = &delta_list;
+			resolve_delta(info->nr, type, data, size,
+				      info->delta, info->size);
+			free(info);
+			continue;
+		}
+		p = &info->next;
+	}
+}
+
+static void unpack_non_delta_entry(enum object_type type, unsigned long size,
+				   unsigned nr)
+{
+	void *buf = get_data(size);
+
+	if (!dry_run && buf)
+		write_object(nr, type, buf, size);
+	else
+		free(buf);
+}
+
+static int resolve_against_held(unsigned nr, const unsigned char *base,
+				void *delta_data, unsigned long delta_size)
+{
+	struct object *obj;
+	struct obj_buffer *obj_buffer;
+	obj = lookup_object(base);
+	if (!obj)
+		return 0;
+	obj_buffer = lookup_object_buffer(obj);
+	if (!obj_buffer)
+		return 0;
+	resolve_delta(nr, obj->type, obj_buffer->buffer,
+		      obj_buffer->size, delta_data, delta_size);
+	return 1;
+}
+
+static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
+			       unsigned nr)
+{
+	void *delta_data, *base;
+	unsigned long base_size;
+	unsigned char base_sha1[20];
+
+	if (type == OBJ_REF_DELTA) {
+		hashcpy(base_sha1, fill(20));
+		use(20);
+		delta_data = get_data(delta_size);
+		if (dry_run || !delta_data) {
+			free(delta_data);
+			return;
+		}
+		if (has_sha1_file(base_sha1))
+			; /* Ok we have this one */
+		else if (resolve_against_held(nr, base_sha1,
+					      delta_data, delta_size))
+			return; /* we are done */
+		else {
+			/* cannot resolve yet --- queue it */
+			hashcpy(obj_list[nr].sha1, null_sha1);
+			add_delta_to_list(nr, base_sha1, 0, delta_data, delta_size);
+			return;
+		}
+	} else {
+		unsigned base_found = 0;
+		unsigned char *pack, c;
+		off_t base_offset;
+		unsigned lo, mid, hi;
+
+		pack = fill(1);
+		c = *pack;
+		use(1);
+		base_offset = c & 127;
+		while (c & 128) {
+			base_offset += 1;
+			if (!base_offset || MSB(base_offset, 7))
+				die("offset value overflow for delta base object");
+			pack = fill(1);
+			c = *pack;
+			use(1);
+			base_offset = (base_offset << 7) + (c & 127);
+		}
+		base_offset = obj_list[nr].offset - base_offset;
+		if (base_offset <= 0 || base_offset >= obj_list[nr].offset)
+			die("offset value out of bound for delta base object");
+
+		delta_data = get_data(delta_size);
+		if (dry_run || !delta_data) {
+			free(delta_data);
+			return;
+		}
+		lo = 0;
+		hi = nr;
+		while (lo < hi) {
+			mid = (lo + hi)/2;
+			if (base_offset < obj_list[mid].offset) {
+				hi = mid;
+			} else if (base_offset > obj_list[mid].offset) {
+				lo = mid + 1;
+			} else {
+				hashcpy(base_sha1, obj_list[mid].sha1);
+				base_found = !is_null_sha1(base_sha1);
+				break;
+			}
+		}
+		if (!base_found) {
+			/*
+			 * The delta base object is itself a delta that
+			 * has not been resolved yet.
+			 */
+			hashcpy(obj_list[nr].sha1, null_sha1);
+			add_delta_to_list(nr, null_sha1, base_offset, delta_data, delta_size);
+			return;
+		}
+	}
+
+	if (resolve_against_held(nr, base_sha1, delta_data, delta_size))
+		return;
+
+	base = read_sha1_file(base_sha1, &type, &base_size);
+	if (!base) {
+		error("failed to read delta-pack base object %s",
+		      sha1_to_hex(base_sha1));
+		if (!recover)
+			exit(1);
+		has_errors = 1;
+		return;
+	}
+	resolve_delta(nr, type, base, base_size, delta_data, delta_size);
+	free(base);
+}
+
+static void unpack_one(unsigned nr)
+{
+	unsigned shift;
+	unsigned char *pack;
+	unsigned long size, c;
+	enum object_type type;
+
+	obj_list[nr].offset = consumed_bytes;
+
+	pack = fill(1);
+	c = *pack;
+	use(1);
+	type = (c >> 4) & 7;
+	size = (c & 15);
+	shift = 4;
+	while (c & 0x80) {
+		pack = fill(1);
+		c = *pack;
+		use(1);
+		size += (c & 0x7f) << shift;
+		shift += 7;
+	}
+
+	switch (type) {
+	case OBJ_COMMIT:
+	case OBJ_TREE:
+	case OBJ_BLOB:
+	case OBJ_TAG:
+		unpack_non_delta_entry(type, size, nr);
+		return;
+	case OBJ_REF_DELTA:
+	case OBJ_OFS_DELTA:
+		unpack_delta_entry(type, size, nr);
+		return;
+	default:
+		error("bad object type %d", type);
+		has_errors = 1;
+		if (recover)
+			return;
+		exit(1);
+	}
+}
+
+static void unpack_all(void)
+{
+	int i;
+	struct progress *progress = NULL;
+	struct pack_header *hdr = fill(sizeof(struct pack_header));
+
+	nr_objects = ntohl(hdr->hdr_entries);
+
+	if (ntohl(hdr->hdr_signature) != PACK_SIGNATURE)
+		die("bad pack file");
+	if (!pack_version_ok(hdr->hdr_version))
+		die("unknown pack file version %"PRIu32,
+			ntohl(hdr->hdr_version));
+	use(sizeof(struct pack_header));
+
+	if (!quiet)
+		progress = start_progress("Unpacking objects", nr_objects);
+	obj_list = xcalloc(nr_objects, sizeof(*obj_list));
+	for (i = 0; i < nr_objects; i++) {
+		unpack_one(i);
+		display_progress(progress, i + 1);
+	}
+	stop_progress(&progress);
+
+	if (delta_list)
+		die("unresolved deltas left after unpacking");
+}
+
+int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
+{
+	int i;
+	unsigned char sha1[20];
+
+	read_replace_refs = 0;
+
+	git_config(git_default_config, NULL);
+
+	quiet = !isatty(2);
+
+	for (i = 1 ; i < argc; i++) {
+		const char *arg = argv[i];
+
+		if (*arg == '-') {
+			if (!strcmp(arg, "-n")) {
+				dry_run = 1;
+				continue;
+			}
+			if (!strcmp(arg, "-q")) {
+				quiet = 1;
+				continue;
+			}
+			if (!strcmp(arg, "-r")) {
+				recover = 1;
+				continue;
+			}
+			if (!strcmp(arg, "--strict")) {
+				strict = 1;
+				continue;
+			}
+			if (!prefixcmp(arg, "--pack_header=")) {
+				struct pack_header *hdr;
+				char *c;
+
+				hdr = (struct pack_header *)buffer;
+				hdr->hdr_signature = htonl(PACK_SIGNATURE);
+				hdr->hdr_version = htonl(strtoul(arg + 14, &c, 10));
+				if (*c != ',')
+					die("bad %s", arg);
+				hdr->hdr_entries = htonl(strtoul(c + 1, &c, 10));
+				if (*c)
+					die("bad %s", arg);
+				len = sizeof(*hdr);
+				continue;
+			}
+			usage(unpack_usage);
+		}
+
+		/* We don't take any non-flag arguments now.. Maybe some day */
+		usage(unpack_usage);
+	}
+	git_SHA1_Init(&ctx);
+	unpack_all();
+	git_SHA1_Update(&ctx, buffer, offset);
+	git_SHA1_Final(sha1, &ctx);
+	if (strict)
+		write_rest();
+	if (hashcmp(fill(20), sha1))
+		die("final sha1 did not match");
+	use(20);
+
+	/* Write the last part of the buffer to stdout */
+	while (len) {
+		int ret = xwrite(1, buffer + offset, len);
+		if (ret <= 0)
+			break;
+		len -= ret;
+		offset += ret;
+	}
+
+	/* All done */
+	return has_errors;
+}
diff --git a/builtin/update-index.c b/builtin/update-index.c
new file mode 100644
index 0000000..4ce341c
--- /dev/null
+++ b/builtin/update-index.c
@@ -0,0 +1,903 @@
+/*
+ * GIT - The information manager from hell
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ */
+#include "cache.h"
+#include "quote.h"
+#include "cache-tree.h"
+#include "tree-walk.h"
+#include "builtin.h"
+#include "refs.h"
+#include "resolve-undo.h"
+#include "parse-options.h"
+
+/*
+ * Default to not allowing changes to the list of files. The
+ * tool doesn't actually care, but this makes it harder to add
+ * files to the revision control by mistake by doing something
+ * like "git update-index *" and suddenly having all the object
+ * files be revision controlled.
+ */
+static int allow_add;
+static int allow_remove;
+static int allow_replace;
+static int info_only;
+static int force_remove;
+static int verbose;
+static int mark_valid_only;
+static int mark_skip_worktree_only;
+#define MARK_FLAG 1
+#define UNMARK_FLAG 2
+
+__attribute__((format (printf, 1, 2)))
+static void report(const char *fmt, ...)
+{
+	va_list vp;
+
+	if (!verbose)
+		return;
+
+	va_start(vp, fmt);
+	vprintf(fmt, vp);
+	putchar('\n');
+	va_end(vp);
+}
+
+static int mark_ce_flags(const char *path, int flag, int mark)
+{
+	int namelen = strlen(path);
+	int pos = cache_name_pos(path, namelen);
+	if (0 <= pos) {
+		if (mark)
+			active_cache[pos]->ce_flags |= flag;
+		else
+			active_cache[pos]->ce_flags &= ~flag;
+		cache_tree_invalidate_path(active_cache_tree, path);
+		active_cache_changed = 1;
+		return 0;
+	}
+	return -1;
+}
+
+static int remove_one_path(const char *path)
+{
+	if (!allow_remove)
+		return error("%s: does not exist and --remove not passed", path);
+	if (remove_file_from_cache(path))
+		return error("%s: cannot remove from the index", path);
+	return 0;
+}
+
+/*
+ * Handle a path that couldn't be lstat'ed. It's either:
+ *  - missing file (ENOENT or ENOTDIR). That's ok if we're
+ *    supposed to be removing it and the removal actually
+ *    succeeds.
+ *  - permission error. That's never ok.
+ */
+static int process_lstat_error(const char *path, int err)
+{
+	if (err == ENOENT || err == ENOTDIR)
+		return remove_one_path(path);
+	return error("lstat(\"%s\"): %s", path, strerror(errno));
+}
+
+static int add_one_path(struct cache_entry *old, const char *path, int len, struct stat *st)
+{
+	int option, size;
+	struct cache_entry *ce;
+
+	/* Was the old index entry already up-to-date? */
+	if (old && !ce_stage(old) && !ce_match_stat(old, st, 0))
+		return 0;
+
+	size = cache_entry_size(len);
+	ce = xcalloc(1, size);
+	memcpy(ce->name, path, len);
+	ce->ce_flags = create_ce_flags(0);
+	ce->ce_namelen = len;
+	fill_stat_cache_info(ce, st);
+	ce->ce_mode = ce_mode_from_stat(old, st->st_mode);
+
+	if (index_path(ce->sha1, path, st,
+		       info_only ? 0 : HASH_WRITE_OBJECT)) {
+		free(ce);
+		return -1;
+	}
+	option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
+	option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
+	if (add_cache_entry(ce, option))
+		return error("%s: cannot add to the index - missing --add option?", path);
+	return 0;
+}
+
+/*
+ * Handle a path that was a directory. Four cases:
+ *
+ *  - it's already a gitlink in the index, and we keep it that
+ *    way, and update it if we can (if we cannot find the HEAD,
+ *    we're going to keep it unchanged in the index!)
+ *
+ *  - it's a *file* in the index, in which case it should be
+ *    removed as a file if removal is allowed, since it doesn't
+ *    exist as such any more. If removal isn't allowed, it's
+ *    an error.
+ *
+ *    (NOTE! This is old and arguably fairly strange behaviour.
+ *    We might want to make this an error unconditionally, and
+ *    use "--force-remove" if you actually want to force removal).
+ *
+ *  - it used to exist as a subdirectory (ie multiple files with
+ *    this particular prefix) in the index, in which case it's wrong
+ *    to try to update it as a directory.
+ *
+ *  - it doesn't exist at all in the index, but it is a valid
+ *    git directory, and it should be *added* as a gitlink.
+ */
+static int process_directory(const char *path, int len, struct stat *st)
+{
+	unsigned char sha1[20];
+	int pos = cache_name_pos(path, len);
+
+	/* Exact match: file or existing gitlink */
+	if (pos >= 0) {
+		struct cache_entry *ce = active_cache[pos];
+		if (S_ISGITLINK(ce->ce_mode)) {
+
+			/* Do nothing to the index if there is no HEAD! */
+			if (resolve_gitlink_ref(path, "HEAD", sha1) < 0)
+				return 0;
+
+			return add_one_path(ce, path, len, st);
+		}
+		/* Should this be an unconditional error? */
+		return remove_one_path(path);
+	}
+
+	/* Inexact match: is there perhaps a subdirectory match? */
+	pos = -pos-1;
+	while (pos < active_nr) {
+		struct cache_entry *ce = active_cache[pos++];
+
+		if (strncmp(ce->name, path, len))
+			break;
+		if (ce->name[len] > '/')
+			break;
+		if (ce->name[len] < '/')
+			continue;
+
+		/* Subdirectory match - error out */
+		return error("%s: is a directory - add individual files instead", path);
+	}
+
+	/* No match - should we add it as a gitlink? */
+	if (!resolve_gitlink_ref(path, "HEAD", sha1))
+		return add_one_path(NULL, path, len, st);
+
+	/* Error out. */
+	return error("%s: is a directory - add files inside instead", path);
+}
+
+static int process_path(const char *path)
+{
+	int pos, len;
+	struct stat st;
+	struct cache_entry *ce;
+
+	len = strlen(path);
+	if (has_symlink_leading_path(path, len))
+		return error("'%s' is beyond a symbolic link", path);
+
+	pos = cache_name_pos(path, len);
+	ce = pos < 0 ? NULL : active_cache[pos];
+	if (ce && ce_skip_worktree(ce)) {
+		/*
+		 * working directory version is assumed "good"
+		 * so updating it does not make sense.
+		 * On the other hand, removing it from index should work
+		 */
+		if (allow_remove && remove_file_from_cache(path))
+			return error("%s: cannot remove from the index", path);
+		return 0;
+	}
+
+	/*
+	 * First things first: get the stat information, to decide
+	 * what to do about the pathname!
+	 */
+	if (lstat(path, &st) < 0)
+		return process_lstat_error(path, errno);
+
+	if (S_ISDIR(st.st_mode))
+		return process_directory(path, len, &st);
+
+	return add_one_path(ce, path, len, &st);
+}
+
+static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
+			 const char *path, int stage)
+{
+	int size, len, option;
+	struct cache_entry *ce;
+
+	if (!verify_path(path))
+		return error("Invalid path '%s'", path);
+
+	len = strlen(path);
+	size = cache_entry_size(len);
+	ce = xcalloc(1, size);
+
+	hashcpy(ce->sha1, sha1);
+	memcpy(ce->name, path, len);
+	ce->ce_flags = create_ce_flags(stage);
+	ce->ce_namelen = len;
+	ce->ce_mode = create_ce_mode(mode);
+	if (assume_unchanged)
+		ce->ce_flags |= CE_VALID;
+	option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
+	option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
+	if (add_cache_entry(ce, option))
+		return error("%s: cannot add to the index - missing --add option?",
+			     path);
+	report("add '%s'", path);
+	return 0;
+}
+
+static void chmod_path(int flip, const char *path)
+{
+	int pos;
+	struct cache_entry *ce;
+	unsigned int mode;
+
+	pos = cache_name_pos(path, strlen(path));
+	if (pos < 0)
+		goto fail;
+	ce = active_cache[pos];
+	mode = ce->ce_mode;
+	if (!S_ISREG(mode))
+		goto fail;
+	switch (flip) {
+	case '+':
+		ce->ce_mode |= 0111; break;
+	case '-':
+		ce->ce_mode &= ~0111; break;
+	default:
+		goto fail;
+	}
+	cache_tree_invalidate_path(active_cache_tree, path);
+	active_cache_changed = 1;
+	report("chmod %cx '%s'", flip, path);
+	return;
+ fail:
+	die("git update-index: cannot chmod %cx '%s'", flip, path);
+}
+
+static void update_one(const char *path, const char *prefix, int prefix_length)
+{
+	const char *p = prefix_path(prefix, prefix_length, path);
+	if (!verify_path(p)) {
+		fprintf(stderr, "Ignoring path %s\n", path);
+		goto free_return;
+	}
+	if (mark_valid_only) {
+		if (mark_ce_flags(p, CE_VALID, mark_valid_only == MARK_FLAG))
+			die("Unable to mark file %s", path);
+		goto free_return;
+	}
+	if (mark_skip_worktree_only) {
+		if (mark_ce_flags(p, CE_SKIP_WORKTREE, mark_skip_worktree_only == MARK_FLAG))
+			die("Unable to mark file %s", path);
+		goto free_return;
+	}
+
+	if (force_remove) {
+		if (remove_file_from_cache(p))
+			die("git update-index: unable to remove %s", path);
+		report("remove '%s'", path);
+		goto free_return;
+	}
+	if (process_path(p))
+		die("Unable to process path %s", path);
+	report("add '%s'", path);
+ free_return:
+	if (p < path || p > path + strlen(path))
+		free((char *)p);
+}
+
+static void read_index_info(int line_termination)
+{
+	struct strbuf buf = STRBUF_INIT;
+	struct strbuf uq = STRBUF_INIT;
+
+	while (strbuf_getline(&buf, stdin, line_termination) != EOF) {
+		char *ptr, *tab;
+		char *path_name;
+		unsigned char sha1[20];
+		unsigned int mode;
+		unsigned long ul;
+		int stage;
+
+		/* This reads lines formatted in one of three formats:
+		 *
+		 * (1) mode         SP sha1          TAB path
+		 * The first format is what "git apply --index-info"
+		 * reports, and used to reconstruct a partial tree
+		 * that is used for phony merge base tree when falling
+		 * back on 3-way merge.
+		 *
+		 * (2) mode SP type SP sha1          TAB path
+		 * The second format is to stuff "git ls-tree" output
+		 * into the index file.
+		 *
+		 * (3) mode         SP sha1 SP stage TAB path
+		 * This format is to put higher order stages into the
+		 * index file and matches "git ls-files --stage" output.
+		 */
+		errno = 0;
+		ul = strtoul(buf.buf, &ptr, 8);
+		if (ptr == buf.buf || *ptr != ' '
+		    || errno || (unsigned int) ul != ul)
+			goto bad_line;
+		mode = ul;
+
+		tab = strchr(ptr, '\t');
+		if (!tab || tab - ptr < 41)
+			goto bad_line;
+
+		if (tab[-2] == ' ' && '0' <= tab[-1] && tab[-1] <= '3') {
+			stage = tab[-1] - '0';
+			ptr = tab + 1; /* point at the head of path */
+			tab = tab - 2; /* point at tail of sha1 */
+		}
+		else {
+			stage = 0;
+			ptr = tab + 1; /* point at the head of path */
+		}
+
+		if (get_sha1_hex(tab - 40, sha1) || tab[-41] != ' ')
+			goto bad_line;
+
+		path_name = ptr;
+		if (line_termination && path_name[0] == '"') {
+			strbuf_reset(&uq);
+			if (unquote_c_style(&uq, path_name, NULL)) {
+				die("git update-index: bad quoting of path name");
+			}
+			path_name = uq.buf;
+		}
+
+		if (!verify_path(path_name)) {
+			fprintf(stderr, "Ignoring path %s\n", path_name);
+			continue;
+		}
+
+		if (!mode) {
+			/* mode == 0 means there is no such path -- remove */
+			if (remove_file_from_cache(path_name))
+				die("git update-index: unable to remove %s",
+				    ptr);
+		}
+		else {
+			/* mode ' ' sha1 '\t' name
+			 * ptr[-1] points at tab,
+			 * ptr[-41] is at the beginning of sha1
+			 */
+			ptr[-42] = ptr[-1] = 0;
+			if (add_cacheinfo(mode, sha1, path_name, stage))
+				die("git update-index: unable to update %s",
+				    path_name);
+		}
+		continue;
+
+	bad_line:
+		die("malformed index info %s", buf.buf);
+	}
+	strbuf_release(&buf);
+	strbuf_release(&uq);
+}
+
+static const char * const update_index_usage[] = {
+	"git update-index [options] [--] [<file>...]",
+	NULL
+};
+
+static unsigned char head_sha1[20];
+static unsigned char merge_head_sha1[20];
+
+static struct cache_entry *read_one_ent(const char *which,
+					unsigned char *ent, const char *path,
+					int namelen, int stage)
+{
+	unsigned mode;
+	unsigned char sha1[20];
+	int size;
+	struct cache_entry *ce;
+
+	if (get_tree_entry(ent, path, sha1, &mode)) {
+		if (which)
+			error("%s: not in %s branch.", path, which);
+		return NULL;
+	}
+	if (mode == S_IFDIR) {
+		if (which)
+			error("%s: not a blob in %s branch.", path, which);
+		return NULL;
+	}
+	size = cache_entry_size(namelen);
+	ce = xcalloc(1, size);
+
+	hashcpy(ce->sha1, sha1);
+	memcpy(ce->name, path, namelen);
+	ce->ce_flags = create_ce_flags(stage);
+	ce->ce_namelen = namelen;
+	ce->ce_mode = create_ce_mode(mode);
+	return ce;
+}
+
+static int unresolve_one(const char *path)
+{
+	int namelen = strlen(path);
+	int pos;
+	int ret = 0;
+	struct cache_entry *ce_2 = NULL, *ce_3 = NULL;
+
+	/* See if there is such entry in the index. */
+	pos = cache_name_pos(path, namelen);
+	if (0 <= pos) {
+		/* already merged */
+		pos = unmerge_cache_entry_at(pos);
+		if (pos < active_nr) {
+			struct cache_entry *ce = active_cache[pos];
+			if (ce_stage(ce) &&
+			    ce_namelen(ce) == namelen &&
+			    !memcmp(ce->name, path, namelen))
+				return 0;
+		}
+		/* no resolve-undo information; fall back */
+	} else {
+		/* If there isn't, either it is unmerged, or
+		 * resolved as "removed" by mistake.  We do not
+		 * want to do anything in the former case.
+		 */
+		pos = -pos-1;
+		if (pos < active_nr) {
+			struct cache_entry *ce = active_cache[pos];
+			if (ce_namelen(ce) == namelen &&
+			    !memcmp(ce->name, path, namelen)) {
+				fprintf(stderr,
+					"%s: skipping still unmerged path.\n",
+					path);
+				goto free_return;
+			}
+		}
+	}
+
+	/* Grab blobs from given path from HEAD and MERGE_HEAD,
+	 * stuff HEAD version in stage #2,
+	 * stuff MERGE_HEAD version in stage #3.
+	 */
+	ce_2 = read_one_ent("our", head_sha1, path, namelen, 2);
+	ce_3 = read_one_ent("their", merge_head_sha1, path, namelen, 3);
+
+	if (!ce_2 || !ce_3) {
+		ret = -1;
+		goto free_return;
+	}
+	if (!hashcmp(ce_2->sha1, ce_3->sha1) &&
+	    ce_2->ce_mode == ce_3->ce_mode) {
+		fprintf(stderr, "%s: identical in both, skipping.\n",
+			path);
+		goto free_return;
+	}
+
+	remove_file_from_cache(path);
+	if (add_cache_entry(ce_2, ADD_CACHE_OK_TO_ADD)) {
+		error("%s: cannot add our version to the index.", path);
+		ret = -1;
+		goto free_return;
+	}
+	if (!add_cache_entry(ce_3, ADD_CACHE_OK_TO_ADD))
+		return 0;
+	error("%s: cannot add their version to the index.", path);
+	ret = -1;
+ free_return:
+	free(ce_2);
+	free(ce_3);
+	return ret;
+}
+
+static void read_head_pointers(void)
+{
+	if (read_ref("HEAD", head_sha1))
+		die("No HEAD -- no initial commit yet?");
+	if (read_ref("MERGE_HEAD", merge_head_sha1)) {
+		fprintf(stderr, "Not in the middle of a merge.\n");
+		exit(0);
+	}
+}
+
+static int do_unresolve(int ac, const char **av,
+			const char *prefix, int prefix_length)
+{
+	int i;
+	int err = 0;
+
+	/* Read HEAD and MERGE_HEAD; if MERGE_HEAD does not exist, we
+	 * are not doing a merge, so exit with success status.
+	 */
+	read_head_pointers();
+
+	for (i = 1; i < ac; i++) {
+		const char *arg = av[i];
+		const char *p = prefix_path(prefix, prefix_length, arg);
+		err |= unresolve_one(p);
+		if (p < arg || p > arg + strlen(arg))
+			free((char *)p);
+	}
+	return err;
+}
+
+static int do_reupdate(int ac, const char **av,
+		       const char *prefix, int prefix_length)
+{
+	/* Read HEAD and run update-index on paths that are
+	 * merged and already different between index and HEAD.
+	 */
+	int pos;
+	int has_head = 1;
+	const char **paths = get_pathspec(prefix, av + 1);
+	struct pathspec pathspec;
+
+	init_pathspec(&pathspec, paths);
+
+	if (read_ref("HEAD", head_sha1))
+		/* If there is no HEAD, that means it is an initial
+		 * commit.  Update everything in the index.
+		 */
+		has_head = 0;
+ redo:
+	for (pos = 0; pos < active_nr; pos++) {
+		struct cache_entry *ce = active_cache[pos];
+		struct cache_entry *old = NULL;
+		int save_nr;
+
+		if (ce_stage(ce) || !ce_path_match(ce, &pathspec))
+			continue;
+		if (has_head)
+			old = read_one_ent(NULL, head_sha1,
+					   ce->name, ce_namelen(ce), 0);
+		if (old && ce->ce_mode == old->ce_mode &&
+		    !hashcmp(ce->sha1, old->sha1)) {
+			free(old);
+			continue; /* unchanged */
+		}
+		/* Be careful.  The working tree may not have the
+		 * path anymore, in which case, under 'allow_remove',
+		 * or worse yet 'allow_replace', active_nr may decrease.
+		 */
+		save_nr = active_nr;
+		update_one(ce->name + prefix_length, prefix, prefix_length);
+		if (save_nr != active_nr)
+			goto redo;
+	}
+	free_pathspec(&pathspec);
+	return 0;
+}
+
+struct refresh_params {
+	unsigned int flags;
+	int *has_errors;
+};
+
+static int refresh(struct refresh_params *o, unsigned int flag)
+{
+	setup_work_tree();
+	*o->has_errors |= refresh_cache(o->flags | flag);
+	return 0;
+}
+
+static int refresh_callback(const struct option *opt,
+				const char *arg, int unset)
+{
+	return refresh(opt->value, 0);
+}
+
+static int really_refresh_callback(const struct option *opt,
+				const char *arg, int unset)
+{
+	return refresh(opt->value, REFRESH_REALLY);
+}
+
+static int chmod_callback(const struct option *opt,
+				const char *arg, int unset)
+{
+	char *flip = opt->value;
+	if ((arg[0] != '-' && arg[0] != '+') || arg[1] != 'x' || arg[2])
+		return error("option 'chmod' expects \"+x\" or \"-x\"");
+	*flip = arg[0];
+	return 0;
+}
+
+static int resolve_undo_clear_callback(const struct option *opt,
+				const char *arg, int unset)
+{
+	resolve_undo_clear();
+	return 0;
+}
+
+static int cacheinfo_callback(struct parse_opt_ctx_t *ctx,
+				const struct option *opt, int unset)
+{
+	unsigned char sha1[20];
+	unsigned int mode;
+
+	if (ctx->argc <= 3)
+		return error("option 'cacheinfo' expects three arguments");
+	if (strtoul_ui(*++ctx->argv, 8, &mode) ||
+	    get_sha1_hex(*++ctx->argv, sha1) ||
+	    add_cacheinfo(mode, sha1, *++ctx->argv, 0))
+		die("git update-index: --cacheinfo cannot add %s", *ctx->argv);
+	ctx->argc -= 3;
+	return 0;
+}
+
+static int stdin_cacheinfo_callback(struct parse_opt_ctx_t *ctx,
+			      const struct option *opt, int unset)
+{
+	int *line_termination = opt->value;
+
+	if (ctx->argc != 1)
+		return error("option '%s' must be the last argument", opt->long_name);
+	allow_add = allow_replace = allow_remove = 1;
+	read_index_info(*line_termination);
+	return 0;
+}
+
+static int stdin_callback(struct parse_opt_ctx_t *ctx,
+				const struct option *opt, int unset)
+{
+	int *read_from_stdin = opt->value;
+
+	if (ctx->argc != 1)
+		return error("option '%s' must be the last argument", opt->long_name);
+	*read_from_stdin = 1;
+	return 0;
+}
+
+static int unresolve_callback(struct parse_opt_ctx_t *ctx,
+				const struct option *opt, int flags)
+{
+	int *has_errors = opt->value;
+	const char *prefix = startup_info->prefix;
+
+	/* consume remaining arguments. */
+	*has_errors = do_unresolve(ctx->argc, ctx->argv,
+				prefix, prefix ? strlen(prefix) : 0);
+	if (*has_errors)
+		active_cache_changed = 0;
+
+	ctx->argv += ctx->argc - 1;
+	ctx->argc = 1;
+	return 0;
+}
+
+static int reupdate_callback(struct parse_opt_ctx_t *ctx,
+				const struct option *opt, int flags)
+{
+	int *has_errors = opt->value;
+	const char *prefix = startup_info->prefix;
+
+	/* consume remaining arguments. */
+	setup_work_tree();
+	*has_errors = do_reupdate(ctx->argc, ctx->argv,
+				prefix, prefix ? strlen(prefix) : 0);
+	if (*has_errors)
+		active_cache_changed = 0;
+
+	ctx->argv += ctx->argc - 1;
+	ctx->argc = 1;
+	return 0;
+}
+
+int cmd_update_index(int argc, const char **argv, const char *prefix)
+{
+	int newfd, entries, has_errors = 0, line_termination = '\n';
+	int read_from_stdin = 0;
+	int prefix_length = prefix ? strlen(prefix) : 0;
+	int preferred_index_format = 0;
+	char set_executable_bit = 0;
+	struct refresh_params refresh_args = {0, &has_errors};
+	int lock_error = 0;
+	struct lock_file *lock_file;
+	struct parse_opt_ctx_t ctx;
+	int parseopt_state = PARSE_OPT_UNKNOWN;
+	struct option options[] = {
+		OPT_BIT('q', NULL, &refresh_args.flags,
+			"continue refresh even when index needs update",
+			REFRESH_QUIET),
+		OPT_BIT(0, "ignore-submodules", &refresh_args.flags,
+			"refresh: ignore submodules",
+			REFRESH_IGNORE_SUBMODULES),
+		OPT_SET_INT(0, "add", &allow_add,
+			"do not ignore new files", 1),
+		OPT_SET_INT(0, "replace", &allow_replace,
+			"let files replace directories and vice-versa", 1),
+		OPT_SET_INT(0, "remove", &allow_remove,
+			"notice files missing from worktree", 1),
+		OPT_BIT(0, "unmerged", &refresh_args.flags,
+			"refresh even if index contains unmerged entries",
+			REFRESH_UNMERGED),
+		{OPTION_CALLBACK, 0, "refresh", &refresh_args, NULL,
+			"refresh stat information",
+			PARSE_OPT_NOARG | PARSE_OPT_NONEG,
+			refresh_callback},
+		{OPTION_CALLBACK, 0, "really-refresh", &refresh_args, NULL,
+			"like --refresh, but ignore assume-unchanged setting",
+			PARSE_OPT_NOARG | PARSE_OPT_NONEG,
+			really_refresh_callback},
+		{OPTION_LOWLEVEL_CALLBACK, 0, "cacheinfo", NULL,
+			"<mode> <object> <path>",
+			"add the specified entry to the index",
+			PARSE_OPT_NOARG |	/* disallow --cacheinfo=<mode> form */
+			PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
+			(parse_opt_cb *) cacheinfo_callback},
+		{OPTION_CALLBACK, 0, "chmod", &set_executable_bit, "(+/-)x",
+			"override the executable bit of the listed files",
+			PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
+			chmod_callback},
+		{OPTION_SET_INT, 0, "assume-unchanged", &mark_valid_only, NULL,
+			"mark files as \"not changing\"",
+			PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG},
+		{OPTION_SET_INT, 0, "no-assume-unchanged", &mark_valid_only, NULL,
+			"clear assumed-unchanged bit",
+			PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG},
+		{OPTION_SET_INT, 0, "skip-worktree", &mark_skip_worktree_only, NULL,
+			"mark files as \"index-only\"",
+			PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG},
+		{OPTION_SET_INT, 0, "no-skip-worktree", &mark_skip_worktree_only, NULL,
+			"clear skip-worktree bit",
+			PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG},
+		OPT_SET_INT(0, "info-only", &info_only,
+			"add to index only; do not add content to object database", 1),
+		OPT_SET_INT(0, "force-remove", &force_remove,
+			"remove named paths even if present in worktree", 1),
+		OPT_SET_INT('z', NULL, &line_termination,
+			"with --stdin: input lines are terminated by null bytes", '\0'),
+		{OPTION_LOWLEVEL_CALLBACK, 0, "stdin", &read_from_stdin, NULL,
+			"read list of paths to be updated from standard input",
+			PARSE_OPT_NONEG | PARSE_OPT_NOARG,
+			(parse_opt_cb *) stdin_callback},
+		{OPTION_LOWLEVEL_CALLBACK, 0, "index-info", &line_termination, NULL,
+			"add entries from standard input to the index",
+			PARSE_OPT_NONEG | PARSE_OPT_NOARG,
+			(parse_opt_cb *) stdin_cacheinfo_callback},
+		{OPTION_LOWLEVEL_CALLBACK, 0, "unresolve", &has_errors, NULL,
+			"repopulate stages #2 and #3 for the listed paths",
+			PARSE_OPT_NONEG | PARSE_OPT_NOARG,
+			(parse_opt_cb *) unresolve_callback},
+		{OPTION_LOWLEVEL_CALLBACK, 'g', "again", &has_errors, NULL,
+			"only update entries that differ from HEAD",
+			PARSE_OPT_NONEG | PARSE_OPT_NOARG,
+			(parse_opt_cb *) reupdate_callback},
+		OPT_BIT(0, "ignore-missing", &refresh_args.flags,
+			"ignore files missing from worktree",
+			REFRESH_IGNORE_MISSING),
+		OPT_SET_INT(0, "verbose", &verbose,
+			"report actions to standard output", 1),
+		{OPTION_CALLBACK, 0, "clear-resolve-undo", NULL, NULL,
+			"(for porcelains) forget saved unresolved conflicts",
+			PARSE_OPT_NOARG | PARSE_OPT_NONEG,
+			resolve_undo_clear_callback},
+		OPT_INTEGER(0, "index-version", &preferred_index_format,
+			    "write index in this format"),
+		OPT_END()
+	};
+
+	if (argc == 2 && !strcmp(argv[1], "-h"))
+		usage(update_index_usage[0]);
+
+	git_config(git_default_config, NULL);
+
+	/* 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_locked_index(lock_file, 0);
+	if (newfd < 0)
+		lock_error = errno;
+
+	entries = read_cache();
+	if (entries < 0)
+		die("cache corrupted");
+
+	/*
+	 * Custom copy of parse_options() because we want to handle
+	 * filename arguments as they come.
+	 */
+	parse_options_start(&ctx, argc, argv, prefix,
+			    options, PARSE_OPT_STOP_AT_NON_OPTION);
+	while (ctx.argc) {
+		if (parseopt_state != PARSE_OPT_DONE)
+			parseopt_state = parse_options_step(&ctx, options,
+							    update_index_usage);
+		if (!ctx.argc)
+			break;
+		switch (parseopt_state) {
+		case PARSE_OPT_HELP:
+			exit(129);
+		case PARSE_OPT_NON_OPTION:
+		case PARSE_OPT_DONE:
+		{
+			const char *path = ctx.argv[0];
+			const char *p;
+
+			setup_work_tree();
+			p = prefix_path(prefix, prefix_length, path);
+			update_one(p, NULL, 0);
+			if (set_executable_bit)
+				chmod_path(set_executable_bit, p);
+			if (p < path || p > path + strlen(path))
+				free((char *)p);
+			ctx.argc--;
+			ctx.argv++;
+			break;
+		}
+		case PARSE_OPT_UNKNOWN:
+			if (ctx.argv[0][1] == '-')
+				error("unknown option '%s'", ctx.argv[0] + 2);
+			else
+				error("unknown switch '%c'", *ctx.opt);
+			usage_with_options(update_index_usage, options);
+		}
+	}
+	argc = parse_options_end(&ctx);
+	if (preferred_index_format) {
+		if (preferred_index_format < INDEX_FORMAT_LB ||
+		    INDEX_FORMAT_UB < preferred_index_format)
+			die("index-version %d not in range: %d..%d",
+			    preferred_index_format,
+			    INDEX_FORMAT_LB, INDEX_FORMAT_UB);
+
+		if (the_index.version != preferred_index_format)
+			active_cache_changed = 1;
+		the_index.version = preferred_index_format;
+	}
+
+	if (read_from_stdin) {
+		struct strbuf buf = STRBUF_INIT, nbuf = STRBUF_INIT;
+
+		setup_work_tree();
+		while (strbuf_getline(&buf, stdin, line_termination) != EOF) {
+			const char *p;
+			if (line_termination && buf.buf[0] == '"') {
+				strbuf_reset(&nbuf);
+				if (unquote_c_style(&nbuf, buf.buf, NULL))
+					die("line is badly quoted");
+				strbuf_swap(&buf, &nbuf);
+			}
+			p = prefix_path(prefix, prefix_length, buf.buf);
+			update_one(p, NULL, 0);
+			if (set_executable_bit)
+				chmod_path(set_executable_bit, p);
+			if (p < buf.buf || p > buf.buf + buf.len)
+				free((char *)p);
+		}
+		strbuf_release(&nbuf);
+		strbuf_release(&buf);
+	}
+
+	if (active_cache_changed) {
+		if (newfd < 0) {
+			if (refresh_args.flags & REFRESH_QUIET)
+				exit(128);
+			unable_to_lock_index_die(get_index_file(), lock_error);
+		}
+		if (write_cache(newfd, active_cache, active_nr) ||
+		    commit_locked_index(lock_file))
+			die("Unable to write new index file");
+	}
+
+	rollback_lock_file(lock_file);
+
+	return has_errors ? 1 : 0;
+}
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
new file mode 100644
index 0000000..835c62a
--- /dev/null
+++ b/builtin/update-ref.c
@@ -0,0 +1,58 @@
+#include "cache.h"
+#include "refs.h"
+#include "builtin.h"
+#include "parse-options.h"
+
+static const char * const git_update_ref_usage[] = {
+	"git update-ref [options] -d <refname> [<oldval>]",
+	"git update-ref [options]    <refname> <newval> [<oldval>]",
+	NULL
+};
+
+int cmd_update_ref(int argc, const char **argv, const char *prefix)
+{
+	const char *refname, *oldval, *msg = NULL;
+	unsigned char sha1[20], oldsha1[20];
+	int delete = 0, no_deref = 0, flags = 0;
+	struct option options[] = {
+		OPT_STRING( 'm', NULL, &msg, "reason", "reason of the update"),
+		OPT_BOOLEAN('d', NULL, &delete, "deletes the reference"),
+		OPT_BOOLEAN( 0 , "no-deref", &no_deref,
+					"update <refname> not the one it points to"),
+		OPT_END(),
+	};
+
+	git_config(git_default_config, NULL);
+	argc = parse_options(argc, argv, prefix, options, git_update_ref_usage,
+			     0);
+	if (msg && !*msg)
+		die("Refusing to perform update with empty message.");
+
+	if (delete) {
+		if (argc < 1 || argc > 2)
+			usage_with_options(git_update_ref_usage, options);
+		refname = argv[0];
+		oldval = argv[1];
+	} else {
+		const char *value;
+		if (argc < 2 || argc > 3)
+			usage_with_options(git_update_ref_usage, options);
+		refname = argv[0];
+		value = argv[1];
+		oldval = argv[2];
+		if (get_sha1(value, sha1))
+			die("%s: not a valid SHA1", value);
+	}
+
+	hashclr(oldsha1); /* all-zero hash in case oldval is the empty string */
+	if (oldval && *oldval && get_sha1(oldval, oldsha1))
+		die("%s: not a valid old SHA1", oldval);
+
+	if (no_deref)
+		flags = REF_NODEREF;
+	if (delete)
+		return delete_ref(refname, oldval ? oldsha1 : NULL, flags);
+	else
+		return update_ref(msg, refname, sha1, oldval ? oldsha1 : NULL,
+				  flags, DIE_ON_ERR);
+}
diff --git a/builtin/update-server-info.c b/builtin/update-server-info.c
new file mode 100644
index 0000000..0d63c44
--- /dev/null
+++ b/builtin/update-server-info.c
@@ -0,0 +1,25 @@
+#include "cache.h"
+#include "builtin.h"
+#include "parse-options.h"
+
+static const char * const update_server_info_usage[] = {
+	"git update-server-info [--force]",
+	NULL
+};
+
+int cmd_update_server_info(int argc, const char **argv, const char *prefix)
+{
+	int force = 0;
+	struct option options[] = {
+		OPT__FORCE(&force, "update the info files from scratch"),
+		OPT_END()
+	};
+
+	git_config(git_default_config, NULL);
+	argc = parse_options(argc, argv, prefix, options,
+			     update_server_info_usage, 0);
+	if (argc > 0)
+		usage_with_options(update_server_info_usage, options);
+
+	return !!update_server_info(force);
+}
diff --git a/builtin/upload-archive.c b/builtin/upload-archive.c
new file mode 100644
index 0000000..b928beb
--- /dev/null
+++ b/builtin/upload-archive.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2006 Franck Bui-Huu
+ */
+#include "cache.h"
+#include "builtin.h"
+#include "archive.h"
+#include "pkt-line.h"
+#include "sideband.h"
+#include "run-command.h"
+
+static const char upload_archive_usage[] =
+	"git upload-archive <repo>";
+
+static const char deadchild[] =
+"git upload-archive: archiver died with error";
+
+#define MAX_ARGS (64)
+
+int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix)
+{
+	const char *sent_argv[MAX_ARGS];
+	const char *arg_cmd = "argument ";
+	char *p, buf[4096];
+	int sent_argc;
+	int len;
+
+	if (argc != 2)
+		usage(upload_archive_usage);
+
+	if (strlen(argv[1]) + 1 > sizeof(buf))
+		die("insanely long repository name");
+
+	strcpy(buf, argv[1]); /* enter-repo smudges its argument */
+
+	if (!enter_repo(buf, 0))
+		die("'%s' does not appear to be a git repository", buf);
+
+	/* put received options in sent_argv[] */
+	sent_argc = 1;
+	sent_argv[0] = "git-upload-archive";
+	for (p = buf;;) {
+		/* This will die if not enough free space in buf */
+		len = packet_read_line(0, p, (buf + sizeof buf) - p);
+		if (len == 0)
+			break;	/* got a flush */
+		if (sent_argc > MAX_ARGS - 2)
+			die("Too many options (>%d)", MAX_ARGS - 2);
+
+		if (p[len-1] == '\n') {
+			p[--len] = 0;
+		}
+		if (len < strlen(arg_cmd) ||
+		    strncmp(arg_cmd, p, strlen(arg_cmd)))
+			die("'argument' token or flush expected");
+
+		len -= strlen(arg_cmd);
+		memmove(p, p + strlen(arg_cmd), len);
+		sent_argv[sent_argc++] = p;
+		p += len;
+		*p++ = 0;
+	}
+	sent_argv[sent_argc] = NULL;
+
+	/* parse all options sent by the client */
+	return write_archive(sent_argc, sent_argv, prefix, 0, NULL, 1);
+}
+
+__attribute__((format (printf, 1, 2)))
+static void error_clnt(const char *fmt, ...)
+{
+	char buf[1024];
+	va_list params;
+	int len;
+
+	va_start(params, fmt);
+	len = vsprintf(buf, fmt, params);
+	va_end(params);
+	send_sideband(1, 3, buf, len, LARGE_PACKET_MAX);
+	die("sent error to the client: %s", buf);
+}
+
+static ssize_t process_input(int child_fd, int band)
+{
+	char buf[16384];
+	ssize_t sz = read(child_fd, buf, sizeof(buf));
+	if (sz < 0) {
+		if (errno != EAGAIN && errno != EINTR)
+			error_clnt("read error: %s\n", strerror(errno));
+		return sz;
+	}
+	send_sideband(1, band, buf, sz, LARGE_PACKET_MAX);
+	return sz;
+}
+
+int cmd_upload_archive(int argc, const char **argv, const char *prefix)
+{
+	struct child_process writer = { argv };
+
+	/*
+	 * Set up sideband subprocess.
+	 *
+	 * We (parent) monitor and read from child, sending its fd#1 and fd#2
+	 * multiplexed out to our fd#1.  If the child dies, we tell the other
+	 * end over channel #3.
+	 */
+	argv[0] = "upload-archive--writer";
+	writer.out = writer.err = -1;
+	writer.git_cmd = 1;
+	if (start_command(&writer)) {
+		int err = errno;
+		packet_write(1, "NACK unable to spawn subprocess\n");
+		die("upload-archive: %s", strerror(err));
+	}
+
+	packet_write(1, "ACK\n");
+	packet_flush(1);
+
+	while (1) {
+		struct pollfd pfd[2];
+
+		pfd[0].fd = writer.out;
+		pfd[0].events = POLLIN;
+		pfd[1].fd = writer.err;
+		pfd[1].events = POLLIN;
+		if (poll(pfd, 2, -1) < 0) {
+			if (errno != EINTR) {
+				error("poll failed resuming: %s",
+				      strerror(errno));
+				sleep(1);
+			}
+			continue;
+		}
+		if (pfd[1].revents & POLLIN)
+			/* Status stream ready */
+			if (process_input(pfd[1].fd, 2))
+				continue;
+		if (pfd[0].revents & POLLIN)
+			/* Data stream ready */
+			if (process_input(pfd[0].fd, 1))
+				continue;
+
+		if (finish_command(&writer))
+			error_clnt("%s", deadchild);
+		packet_flush(1);
+		break;
+	}
+	return 0;
+}
diff --git a/builtin/var.c b/builtin/var.c
new file mode 100644
index 0000000..aedbb53
--- /dev/null
+++ b/builtin/var.c
@@ -0,0 +1,93 @@
+/*
+ * GIT - The information manager from hell
+ *
+ * Copyright (C) Eric Biederman, 2005
+ */
+#include "builtin.h"
+
+static const char var_usage[] = "git var (-l | <variable>)";
+
+static const char *editor(int flag)
+{
+	const char *pgm = git_editor();
+
+	if (!pgm && flag & IDENT_STRICT)
+		die("Terminal is dumb, but EDITOR unset");
+
+	return pgm;
+}
+
+static const char *pager(int flag)
+{
+	const char *pgm = git_pager(1);
+
+	if (!pgm)
+		pgm = "cat";
+	return pgm;
+}
+
+struct git_var {
+	const char *name;
+	const char *(*read)(int);
+};
+static struct git_var git_vars[] = {
+	{ "GIT_COMMITTER_IDENT", git_committer_info },
+	{ "GIT_AUTHOR_IDENT",   git_author_info },
+	{ "GIT_EDITOR", editor },
+	{ "GIT_PAGER", pager },
+	{ "", NULL },
+};
+
+static void list_vars(void)
+{
+	struct git_var *ptr;
+	const char *val;
+
+	for (ptr = git_vars; ptr->read; ptr++)
+		if ((val = ptr->read(0)))
+			printf("%s=%s\n", ptr->name, val);
+}
+
+static const char *read_var(const char *var)
+{
+	struct git_var *ptr;
+	const char *val;
+	val = NULL;
+	for (ptr = git_vars; ptr->read; ptr++) {
+		if (strcmp(var, ptr->name) == 0) {
+			val = ptr->read(IDENT_STRICT);
+			break;
+		}
+	}
+	return val;
+}
+
+static int show_config(const char *var, const char *value, void *cb)
+{
+	if (value)
+		printf("%s=%s\n", var, value);
+	else
+		printf("%s\n", var);
+	return git_default_config(var, value, cb);
+}
+
+int cmd_var(int argc, const char **argv, const char *prefix)
+{
+	const char *val = NULL;
+	if (argc != 2)
+		usage(var_usage);
+
+	if (strcmp(argv[1], "-l") == 0) {
+		git_config(show_config, NULL);
+		list_vars();
+		return 0;
+	}
+	git_config(git_default_config, NULL);
+	val = read_var(argv[1]);
+	if (!val)
+		usage(var_usage);
+
+	printf("%s\n", val);
+
+	return 0;
+}
diff --git a/builtin/verify-pack.c b/builtin/verify-pack.c
new file mode 100644
index 0000000..e841b4a
--- /dev/null
+++ b/builtin/verify-pack.c
@@ -0,0 +1,84 @@
+#include "builtin.h"
+#include "cache.h"
+#include "run-command.h"
+#include "parse-options.h"
+
+#define VERIFY_PACK_VERBOSE 01
+#define VERIFY_PACK_STAT_ONLY 02
+
+static int verify_one_pack(const char *path, unsigned int flags)
+{
+	struct child_process index_pack;
+	const char *argv[] = {"index-pack", NULL, NULL, NULL };
+	struct strbuf arg = STRBUF_INIT;
+	int verbose = flags & VERIFY_PACK_VERBOSE;
+	int stat_only = flags & VERIFY_PACK_STAT_ONLY;
+	int err;
+
+	if (stat_only)
+		argv[1] = "--verify-stat-only";
+	else if (verbose)
+		argv[1] = "--verify-stat";
+	else
+		argv[1] = "--verify";
+
+	/*
+	 * In addition to "foo.pack" we accept "foo.idx" and "foo";
+	 * normalize these forms to "foo.pack" for "index-pack --verify".
+	 */
+	strbuf_addstr(&arg, path);
+	if (has_extension(arg.buf, ".idx"))
+		strbuf_splice(&arg, arg.len - 3, 3, "pack", 4);
+	else if (!has_extension(arg.buf, ".pack"))
+		strbuf_add(&arg, ".pack", 5);
+	argv[2] = arg.buf;
+
+	memset(&index_pack, 0, sizeof(index_pack));
+	index_pack.argv = argv;
+	index_pack.git_cmd = 1;
+
+	err = run_command(&index_pack);
+
+	if (verbose || stat_only) {
+		if (err)
+			printf("%s: bad\n", arg.buf);
+		else {
+			if (!stat_only)
+				printf("%s: ok\n", arg.buf);
+		}
+	}
+	strbuf_release(&arg);
+
+	return err;
+}
+
+static const char * const verify_pack_usage[] = {
+	"git verify-pack [-v|--verbose] [-s|--stat-only] <pack>...",
+	NULL
+};
+
+int cmd_verify_pack(int argc, const char **argv, const char *prefix)
+{
+	int err = 0;
+	unsigned int flags = 0;
+	int i;
+	const struct option verify_pack_options[] = {
+		OPT_BIT('v', "verbose", &flags, "verbose",
+			VERIFY_PACK_VERBOSE),
+		OPT_BIT('s', "stat-only", &flags, "show statistics only",
+			VERIFY_PACK_STAT_ONLY),
+		OPT_END()
+	};
+
+	git_config(git_default_config, NULL);
+	argc = parse_options(argc, argv, prefix, verify_pack_options,
+			     verify_pack_usage, 0);
+	if (argc < 1)
+		usage_with_options(verify_pack_usage, verify_pack_options);
+	for (i = 0; i < argc; i++) {
+		if (verify_one_pack(argv[i], flags))
+			err = 1;
+	}
+
+	return err;
+}
diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c
new file mode 100644
index 0000000..986789f
--- /dev/null
+++ b/builtin/verify-tag.c
@@ -0,0 +1,91 @@
+/*
+ * Builtin "git verify-tag"
+ *
+ * Copyright (c) 2007 Carlos Rica <jasampler@gmail.com>
+ *
+ * Based on git-verify-tag.sh
+ */
+#include "cache.h"
+#include "builtin.h"
+#include "tag.h"
+#include "run-command.h"
+#include <signal.h>
+#include "parse-options.h"
+#include "gpg-interface.h"
+
+static const char * const verify_tag_usage[] = {
+		"git verify-tag [-v|--verbose] <tag>...",
+		NULL
+};
+
+static int run_gpg_verify(const char *buf, unsigned long size, int verbose)
+{
+	int len;
+
+	len = parse_signature(buf, size);
+	if (verbose)
+		write_in_full(1, buf, len);
+
+	if (size == len)
+		return error("no signature found");
+
+	return verify_signed_buffer(buf, len, buf + len, size - len, NULL);
+}
+
+static int verify_tag(const char *name, int verbose)
+{
+	enum object_type type;
+	unsigned char sha1[20];
+	char *buf;
+	unsigned long size;
+	int ret;
+
+	if (get_sha1(name, sha1))
+		return error("tag '%s' not found.", name);
+
+	type = sha1_object_info(sha1, NULL);
+	if (type != OBJ_TAG)
+		return error("%s: cannot verify a non-tag object of type %s.",
+				name, typename(type));
+
+	buf = read_sha1_file(sha1, &type, &size);
+	if (!buf)
+		return error("%s: unable to read file.", name);
+
+	ret = run_gpg_verify(buf, size, verbose);
+
+	free(buf);
+	return ret;
+}
+
+static int git_verify_tag_config(const char *var, const char *value, void *cb)
+{
+	int status = git_gpg_config(var, value, cb);
+	if (status)
+		return status;
+	return git_default_config(var, value, cb);
+}
+
+int cmd_verify_tag(int argc, const char **argv, const char *prefix)
+{
+	int i = 1, verbose = 0, had_error = 0;
+	const struct option verify_tag_options[] = {
+		OPT__VERBOSE(&verbose, "print tag contents"),
+		OPT_END()
+	};
+
+	git_config(git_verify_tag_config, NULL);
+
+	argc = parse_options(argc, argv, prefix, verify_tag_options,
+			     verify_tag_usage, PARSE_OPT_KEEP_ARGV0);
+	if (argc <= i)
+		usage_with_options(verify_tag_usage, verify_tag_options);
+
+	/* sometimes the program was terminated because this signal
+	 * was received in the process of writing the gpg input: */
+	signal(SIGPIPE, SIG_IGN);
+	while (i < argc)
+		if (verify_tag(argv[i++], verbose))
+			had_error = 1;
+	return had_error;
+}
diff --git a/builtin-write-tree.c b/builtin/write-tree.c
similarity index 100%
rename from builtin-write-tree.c
rename to builtin/write-tree.c
diff --git a/bulk-checkin.c b/bulk-checkin.c
new file mode 100644
index 0000000..6b0b6d4
--- /dev/null
+++ b/bulk-checkin.c
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2011, Google Inc.
+ */
+#include "bulk-checkin.h"
+#include "csum-file.h"
+#include "pack.h"
+
+static int pack_compression_level = Z_DEFAULT_COMPRESSION;
+
+static struct bulk_checkin_state {
+	unsigned plugged:1;
+
+	char *pack_tmp_name;
+	struct sha1file *f;
+	off_t offset;
+	struct pack_idx_option pack_idx_opts;
+
+	struct pack_idx_entry **written;
+	uint32_t alloc_written;
+	uint32_t nr_written;
+} state;
+
+static void finish_bulk_checkin(struct bulk_checkin_state *state)
+{
+	unsigned char sha1[20];
+	char packname[PATH_MAX];
+	int i;
+
+	if (!state->f)
+		return;
+
+	if (state->nr_written == 0) {
+		close(state->f->fd);
+		unlink(state->pack_tmp_name);
+		goto clear_exit;
+	} else if (state->nr_written == 1) {
+		sha1close(state->f, sha1, CSUM_FSYNC);
+	} else {
+		int fd = sha1close(state->f, sha1, 0);
+		fixup_pack_header_footer(fd, sha1, state->pack_tmp_name,
+					 state->nr_written, sha1,
+					 state->offset);
+		close(fd);
+	}
+
+	sprintf(packname, "%s/pack/pack-", get_object_directory());
+	finish_tmp_packfile(packname, state->pack_tmp_name,
+			    state->written, state->nr_written,
+			    &state->pack_idx_opts, sha1);
+	for (i = 0; i < state->nr_written; i++)
+		free(state->written[i]);
+
+clear_exit:
+	free(state->written);
+	memset(state, 0, sizeof(*state));
+
+	/* Make objects we just wrote available to ourselves */
+	reprepare_packed_git();
+}
+
+static int already_written(struct bulk_checkin_state *state, unsigned char sha1[])
+{
+	int i;
+
+	/* The object may already exist in the repository */
+	if (has_sha1_file(sha1))
+		return 1;
+
+	/* Might want to keep the list sorted */
+	for (i = 0; i < state->nr_written; i++)
+		if (!hashcmp(state->written[i]->sha1, sha1))
+			return 1;
+
+	/* This is a new object we need to keep */
+	return 0;
+}
+
+/*
+ * Read the contents from fd for size bytes, streaming it to the
+ * packfile in state while updating the hash in ctx. Signal a failure
+ * by returning a negative value when the resulting pack would exceed
+ * the pack size limit and this is not the first object in the pack,
+ * so that the caller can discard what we wrote from the current pack
+ * by truncating it and opening a new one. The caller will then call
+ * us again after rewinding the input fd.
+ *
+ * The already_hashed_to pointer is kept untouched by the caller to
+ * make sure we do not hash the same byte when we are called
+ * again. This way, the caller does not have to checkpoint its hash
+ * status before calling us just in case we ask it to call us again
+ * with a new pack.
+ */
+static int stream_to_pack(struct bulk_checkin_state *state,
+			  git_SHA_CTX *ctx, off_t *already_hashed_to,
+			  int fd, size_t size, enum object_type type,
+			  const char *path, unsigned flags)
+{
+	git_zstream s;
+	unsigned char obuf[16384];
+	unsigned hdrlen;
+	int status = Z_OK;
+	int write_object = (flags & HASH_WRITE_OBJECT);
+	off_t offset = 0;
+
+	memset(&s, 0, sizeof(s));
+	git_deflate_init(&s, pack_compression_level);
+
+	hdrlen = encode_in_pack_object_header(type, size, obuf);
+	s.next_out = obuf + hdrlen;
+	s.avail_out = sizeof(obuf) - hdrlen;
+
+	while (status != Z_STREAM_END) {
+		unsigned char ibuf[16384];
+
+		if (size && !s.avail_in) {
+			ssize_t rsize = size < sizeof(ibuf) ? size : sizeof(ibuf);
+			if (xread(fd, ibuf, rsize) != rsize)
+				die("failed to read %d bytes from '%s'",
+				    (int)rsize, path);
+			offset += rsize;
+			if (*already_hashed_to < offset) {
+				size_t hsize = offset - *already_hashed_to;
+				if (rsize < hsize)
+					hsize = rsize;
+				if (hsize)
+					git_SHA1_Update(ctx, ibuf, hsize);
+				*already_hashed_to = offset;
+			}
+			s.next_in = ibuf;
+			s.avail_in = rsize;
+			size -= rsize;
+		}
+
+		status = git_deflate(&s, size ? 0 : Z_FINISH);
+
+		if (!s.avail_out || status == Z_STREAM_END) {
+			if (write_object) {
+				size_t written = s.next_out - obuf;
+
+				/* would we bust the size limit? */
+				if (state->nr_written &&
+				    pack_size_limit_cfg &&
+				    pack_size_limit_cfg < state->offset + written) {
+					git_deflate_abort(&s);
+					return -1;
+				}
+
+				sha1write(state->f, obuf, written);
+				state->offset += written;
+			}
+			s.next_out = obuf;
+			s.avail_out = sizeof(obuf);
+		}
+
+		switch (status) {
+		case Z_OK:
+		case Z_BUF_ERROR:
+		case Z_STREAM_END:
+			continue;
+		default:
+			die("unexpected deflate failure: %d", status);
+		}
+	}
+	git_deflate_end(&s);
+	return 0;
+}
+
+/* Lazily create backing packfile for the state */
+static void prepare_to_stream(struct bulk_checkin_state *state,
+			      unsigned flags)
+{
+	if (!(flags & HASH_WRITE_OBJECT) || state->f)
+		return;
+
+	state->f = create_tmp_packfile(&state->pack_tmp_name);
+	reset_pack_idx_option(&state->pack_idx_opts);
+
+	/* Pretend we are going to write only one object */
+	state->offset = write_pack_header(state->f, 1);
+	if (!state->offset)
+		die_errno("unable to write pack header");
+}
+
+static int deflate_to_pack(struct bulk_checkin_state *state,
+			   unsigned char result_sha1[],
+			   int fd, size_t size,
+			   enum object_type type, const char *path,
+			   unsigned flags)
+{
+	off_t seekback, already_hashed_to;
+	git_SHA_CTX ctx;
+	unsigned char obuf[16384];
+	unsigned header_len;
+	struct sha1file_checkpoint checkpoint;
+	struct pack_idx_entry *idx = NULL;
+
+	seekback = lseek(fd, 0, SEEK_CUR);
+	if (seekback == (off_t) -1)
+		return error("cannot find the current offset");
+
+	header_len = sprintf((char *)obuf, "%s %" PRIuMAX,
+			     typename(type), (uintmax_t)size) + 1;
+	git_SHA1_Init(&ctx);
+	git_SHA1_Update(&ctx, obuf, header_len);
+
+	/* Note: idx is non-NULL when we are writing */
+	if ((flags & HASH_WRITE_OBJECT) != 0)
+		idx = xcalloc(1, sizeof(*idx));
+
+	already_hashed_to = 0;
+
+	while (1) {
+		prepare_to_stream(state, flags);
+		if (idx) {
+			sha1file_checkpoint(state->f, &checkpoint);
+			idx->offset = state->offset;
+			crc32_begin(state->f);
+		}
+		if (!stream_to_pack(state, &ctx, &already_hashed_to,
+				    fd, size, type, path, flags))
+			break;
+		/*
+		 * Writing this object to the current pack will make
+		 * it too big; we need to truncate it, start a new
+		 * pack, and write into it.
+		 */
+		if (!idx)
+			die("BUG: should not happen");
+		sha1file_truncate(state->f, &checkpoint);
+		state->offset = checkpoint.offset;
+		finish_bulk_checkin(state);
+		if (lseek(fd, seekback, SEEK_SET) == (off_t) -1)
+			return error("cannot seek back");
+	}
+	git_SHA1_Final(result_sha1, &ctx);
+	if (!idx)
+		return 0;
+
+	idx->crc32 = crc32_end(state->f);
+	if (already_written(state, result_sha1)) {
+		sha1file_truncate(state->f, &checkpoint);
+		state->offset = checkpoint.offset;
+		free(idx);
+	} else {
+		hashcpy(idx->sha1, result_sha1);
+		ALLOC_GROW(state->written,
+			   state->nr_written + 1,
+			   state->alloc_written);
+		state->written[state->nr_written++] = idx;
+	}
+	return 0;
+}
+
+int index_bulk_checkin(unsigned char *sha1,
+		       int fd, size_t size, enum object_type type,
+		       const char *path, unsigned flags)
+{
+	int status = deflate_to_pack(&state, sha1, fd, size, type,
+				     path, flags);
+	if (!state.plugged)
+		finish_bulk_checkin(&state);
+	return status;
+}
+
+void plug_bulk_checkin(void)
+{
+	state.plugged = 1;
+}
+
+void unplug_bulk_checkin(void)
+{
+	state.plugged = 0;
+	if (state.f)
+		finish_bulk_checkin(&state);
+}
diff --git a/bulk-checkin.h b/bulk-checkin.h
new file mode 100644
index 0000000..4f599f8
--- /dev/null
+++ b/bulk-checkin.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2011, Google Inc.
+ */
+#ifndef BULK_CHECKIN_H
+#define BULK_CHECKIN_H
+
+#include "cache.h"
+
+extern int index_bulk_checkin(unsigned char sha1[],
+			      int fd, size_t size, enum object_type type,
+			      const char *path, unsigned flags);
+
+extern void plug_bulk_checkin(void);
+extern void unplug_bulk_checkin(void);
+
+#endif
diff --git a/bundle.c b/bundle.c
index e4b2aa9..8d12816 100644
--- a/bundle.c
+++ b/bundle.c
@@ -23,50 +23,85 @@
 	list->nr++;
 }
 
-/* returns an fd */
+static int parse_bundle_header(int fd, struct bundle_header *header,
+			       const char *report_path)
+{
+	struct strbuf buf = STRBUF_INIT;
+	int status = 0;
+
+	/* The bundle header begins with the signature */
+	if (strbuf_getwholeline_fd(&buf, fd, '\n') ||
+	    strcmp(buf.buf, bundle_signature)) {
+		if (report_path)
+			error(_("'%s' does not look like a v2 bundle file"),
+			      report_path);
+		status = -1;
+		goto abort;
+	}
+
+	/* The bundle header ends with an empty line */
+	while (!strbuf_getwholeline_fd(&buf, fd, '\n') &&
+	       buf.len && buf.buf[0] != '\n') {
+		unsigned char sha1[20];
+		int is_prereq = 0;
+
+		if (*buf.buf == '-') {
+			is_prereq = 1;
+			strbuf_remove(&buf, 0, 1);
+		}
+		strbuf_rtrim(&buf);
+
+		/*
+		 * Tip lines have object name, SP, and refname.
+		 * Prerequisites have object name that is optionally
+		 * followed by SP and subject line.
+		 */
+		if (get_sha1_hex(buf.buf, sha1) ||
+		    (40 <= buf.len && !isspace(buf.buf[40])) ||
+		    (!is_prereq && buf.len <= 40)) {
+			if (report_path)
+				error(_("unrecognized header: %s%s (%d)"),
+				      (is_prereq ? "-" : ""), buf.buf, (int)buf.len);
+			status = -1;
+			break;
+		} else {
+			if (is_prereq)
+				add_to_ref_list(sha1, "", &header->prerequisites);
+			else
+				add_to_ref_list(sha1, buf.buf + 41, &header->references);
+		}
+	}
+
+ abort:
+	if (status) {
+		close(fd);
+		fd = -1;
+	}
+	strbuf_release(&buf);
+	return fd;
+}
+
 int read_bundle_header(const char *path, struct bundle_header *header)
 {
-	char buffer[1024];
-	int fd;
-	long fpos;
-	FILE *ffd = fopen(path, "rb");
+	int fd = open(path, O_RDONLY);
 
-	if (!ffd)
-		return error("could not open '%s'", path);
-	if (!fgets(buffer, sizeof(buffer), ffd) ||
-			strcmp(buffer, bundle_signature)) {
-		fclose(ffd);
-		return error("'%s' does not look like a v2 bundle file", path);
-	}
-	while (fgets(buffer, sizeof(buffer), ffd)
-			&& buffer[0] != '\n') {
-		int is_prereq = buffer[0] == '-';
-		int offset = is_prereq ? 1 : 0;
-		int len = strlen(buffer);
-		unsigned char sha1[20];
-		struct ref_list *list = is_prereq ? &header->prerequisites
-			: &header->references;
-		char delim;
-
-		if (len && buffer[len - 1] == '\n')
-			buffer[len - 1] = '\0';
-		if (get_sha1_hex(buffer + offset, sha1)) {
-			warning("unrecognized header: %s", buffer);
-			continue;
-		}
-		delim = buffer[40 + offset];
-		if (!isspace(delim) && (delim != '\0' || !is_prereq))
-			die ("invalid header: %s", buffer);
-		add_to_ref_list(sha1, isspace(delim) ?
-				buffer + 41 + offset : "", list);
-	}
-	fpos = ftell(ffd);
-	fclose(ffd);
-	fd = open(path, O_RDONLY);
 	if (fd < 0)
-		return error("could not open '%s'", path);
-	lseek(fd, fpos, SEEK_SET);
-	return fd;
+		return error(_("could not open '%s'"), path);
+	return parse_bundle_header(fd, header, path);
+}
+
+int is_bundle(const char *path, int quiet)
+{
+	struct bundle_header header;
+	int fd = open(path, O_RDONLY);
+
+	if (fd < 0)
+		return 0;
+	memset(&header, 0, sizeof(header));
+	fd = parse_bundle_header(fd, &header, quiet ? NULL : path);
+	if (fd >= 0)
+		close(fd);
+	return (fd >= 0);
 }
 
 static int list_refs(struct ref_list *r, int argc, const char **argv)
@@ -102,7 +137,7 @@
 	struct object_array refs;
 	struct commit *commit;
 	int i, ret = 0, req_nr;
-	const char *message = "Repository lacks these prerequisite commits:";
+	const char *message = _("Repository lacks these prerequisite commits:");
 
 	init_revisions(&revs, NULL);
 	for (i = 0; i < p->nr; i++) {
@@ -122,14 +157,11 @@
 	req_nr = revs.pending.nr;
 	setup_revisions(2, argv, &revs, NULL);
 
-	memset(&refs, 0, sizeof(struct object_array));
-	for (i = 0; i < revs.pending.nr; i++) {
-		struct object_array_entry *e = revs.pending.objects + i;
-		add_object_array(e->item, e->name, &refs);
-	}
+	refs = revs.pending;
+	revs.leak_pending = 1;
 
 	if (prepare_revision_walk(&revs))
-		die("revision walk setup failed");
+		die(_("revision walk setup failed"));
 
 	i = req_nr;
 	while (i && (commit = get_revision(&revs)))
@@ -144,20 +176,28 @@
 				refs.objects[i].name);
 		}
 
-	for (i = 0; i < refs.nr; i++)
-		clear_commit_marks((struct commit *)refs.objects[i].item, -1);
+	clear_commit_marks_for_object_array(&refs, ALL_REV_FLAGS);
+	free(refs.objects);
 
 	if (verbose) {
 		struct ref_list *r;
 
 		r = &header->references;
-		printf("The bundle contains %d ref%s\n",
-		       r->nr, (1 < r->nr) ? "s" : "");
+		printf_ln(Q_("The bundle contains %d ref",
+			     "The bundle contains %d refs",
+			     r->nr),
+			  r->nr);
 		list_refs(r, 0, NULL);
-		r = &header->prerequisites;
-		printf("The bundle requires these %d ref%s\n",
-		       r->nr, (1 < r->nr) ? "s" : "");
-		list_refs(r, 0, NULL);
+		if (!r->nr) {
+			printf_ln(_("The bundle records a complete history."));
+		} else {
+			r = &header->prerequisites;
+			printf_ln(Q_("The bundle requires this ref",
+				     "The bundle requires these %d refs",
+				     r->nr),
+				  r->nr);
+			list_refs(r, 0, NULL);
+		}
 	}
 	return ret;
 }
@@ -200,11 +240,10 @@
 	int bundle_fd = -1;
 	int bundle_to_stdout;
 	const char **argv_boundary = xmalloc((argc + 4) * sizeof(const char *));
-	const char **argv_pack = xmalloc(5 * sizeof(const char *));
+	const char **argv_pack = xmalloc(6 * sizeof(const char *));
 	int i, ref_count = 0;
-	char buffer[1024];
+	struct strbuf buf = STRBUF_INIT;
 	struct rev_info revs;
-	int read_from_stdin = 0;
 	struct child_process rls;
 	FILE *rls_fout;
 
@@ -234,37 +273,31 @@
 	rls.git_cmd = 1;
 	if (start_command(&rls))
 		return -1;
-	rls_fout = fdopen(rls.out, "r");
-	while (fgets(buffer, sizeof(buffer), rls_fout)) {
+	rls_fout = xfdopen(rls.out, "r");
+	while (strbuf_getwholeline(&buf, rls_fout, '\n') != EOF) {
 		unsigned char sha1[20];
-		if (buffer[0] == '-') {
-			write_or_die(bundle_fd, buffer, strlen(buffer));
-			if (!get_sha1_hex(buffer + 1, sha1)) {
+		if (buf.len > 0 && buf.buf[0] == '-') {
+			write_or_die(bundle_fd, buf.buf, buf.len);
+			if (!get_sha1_hex(buf.buf + 1, sha1)) {
 				struct object *object = parse_object(sha1);
 				object->flags |= UNINTERESTING;
-				add_pending_object(&revs, object, buffer);
+				add_pending_object(&revs, object, xstrdup(buf.buf));
 			}
-		} else if (!get_sha1_hex(buffer, sha1)) {
+		} else if (!get_sha1_hex(buf.buf, sha1)) {
 			struct object *object = parse_object(sha1);
 			object->flags |= SHOWN;
 		}
 	}
+	strbuf_release(&buf);
 	fclose(rls_fout);
 	if (finish_command(&rls))
-		return error("rev-list died");
+		return error(_("rev-list died"));
 
 	/* write references */
 	argc = setup_revisions(argc, argv, &revs, NULL);
 
-	for (i = 1; i < argc; i++) {
-		if (!strcmp(argv[i], "--stdin")) {
-			if (read_from_stdin++)
-				die("--stdin given twice?");
-			read_revisions_from_stdin(&revs);
-			continue;
-		}
-		return error("unrecognized argument: %s'", argv[i]);
-	}
+	if (argc > 1)
+		return error(_("unrecognized argument: %s"), argv[1]);
 
 	object_array_remove_duplicates(&revs.pending);
 
@@ -279,7 +312,7 @@
 			continue;
 		if (dwim_ref(e->name, strlen(e->name), sha1, &ref) != 1)
 			continue;
-		if (!resolve_ref(e->name, sha1, 1, &flag))
+		if (read_ref_full(e->name, sha1, 1, &flag))
 			flag = 0;
 		display_ref = (flag & REF_ISSYMREF) ? e->name : ref;
 
@@ -299,7 +332,7 @@
 		 * constraints.
 		 */
 		if (!(e->item->flags & SHOWN) && e->item->type == OBJ_COMMIT) {
-			warning("ref '%s' is excluded by the rev-list options",
+			warning(_("ref '%s' is excluded by the rev-list options"),
 				e->name);
 			free(ref);
 			continue;
@@ -344,24 +377,25 @@
 		free(ref);
 	}
 	if (!ref_count)
-		die ("Refusing to create empty bundle.");
+		die(_("Refusing to create empty bundle."));
 
 	/* end header */
 	write_or_die(bundle_fd, "\n", 1);
 
 	/* write pack */
 	argv_pack[0] = "pack-objects";
-	argv_pack[1] = "--all-progress";
+	argv_pack[1] = "--all-progress-implied";
 	argv_pack[2] = "--stdout";
 	argv_pack[3] = "--thin";
-	argv_pack[4] = NULL;
+	argv_pack[4] = "--delta-base-offset";
+	argv_pack[5] = NULL;
 	memset(&rls, 0, sizeof(rls));
 	rls.argv = argv_pack;
 	rls.in = -1;
 	rls.out = bundle_fd;
 	rls.git_cmd = 1;
 	if (start_command(&rls))
-		return error("Could not spawn pack-objects");
+		return error(_("Could not spawn pack-objects"));
 
 	/*
 	 * start_command closed bundle_fd if it was > 1
@@ -379,18 +413,23 @@
 	}
 	close(rls.in);
 	if (finish_command(&rls))
-		return error ("pack-objects died");
-	if (!bundle_to_stdout)
-		commit_lock_file(&lock);
+		return error(_("pack-objects died"));
+	if (!bundle_to_stdout) {
+		if (commit_lock_file(&lock))
+			die_errno(_("cannot create '%s'"), path);
+	}
 	return 0;
 }
 
-int unbundle(struct bundle_header *header, int bundle_fd)
+int unbundle(struct bundle_header *header, int bundle_fd, int flags)
 {
 	const char *argv_index_pack[] = {"index-pack",
-		"--fix-thin", "--stdin", NULL};
+					 "--fix-thin", "--stdin", NULL, NULL};
 	struct child_process ip;
 
+	if (flags & BUNDLE_VERBOSE)
+		argv_index_pack[3] = "-v";
+
 	if (verify_bundle(header, 0))
 		return -1;
 	memset(&ip, 0, sizeof(ip));
@@ -399,6 +438,6 @@
 	ip.no_stdout = 1;
 	ip.git_cmd = 1;
 	if (run_command(&ip))
-		return error("index-pack died");
+		return error(_("index-pack died"));
 	return 0;
 }
diff --git a/bundle.h b/bundle.h
index e2aedd6..1584e4d 100644
--- a/bundle.h
+++ b/bundle.h
@@ -14,11 +14,13 @@
 	struct ref_list references;
 };
 
+int is_bundle(const char *path, int quiet);
 int read_bundle_header(const char *path, struct bundle_header *header);
 int create_bundle(struct bundle_header *header, const char *path,
 		int argc, const char **argv);
 int verify_bundle(struct bundle_header *header, int verbose);
-int unbundle(struct bundle_header *header, int bundle_fd);
+#define BUNDLE_VERBOSE 1
+int unbundle(struct bundle_header *header, int bundle_fd, int flags);
 int list_bundle_refs(struct bundle_header *header,
 		int argc, const char **argv);
 
diff --git a/cache-tree.c b/cache-tree.c
index d917437..28ed657 100644
--- a/cache-tree.c
+++ b/cache-tree.c
@@ -22,8 +22,10 @@
 	if (!it)
 		return;
 	for (i = 0; i < it->subtree_nr; i++)
-		if (it->down[i])
+		if (it->down[i]) {
 			cache_tree_free(&it->down[i]->cache_tree);
+			free(it->down[i]);
+		}
 	free(it->down);
 	free(it);
 	*it_p = NULL;
@@ -148,15 +150,18 @@
 }
 
 static int verify_cache(struct cache_entry **cache,
-			int entries)
+			int entries, int flags)
 {
 	int i, funny;
+	int silent = flags & WRITE_TREE_SILENT;
 
 	/* Verify that the tree is merged */
 	funny = 0;
 	for (i = 0; i < entries; i++) {
 		struct cache_entry *ce = cache[i];
-		if (ce_stage(ce) || (ce->ce_flags & CE_INTENT_TO_ADD)) {
+		if (ce_stage(ce)) {
+			if (silent)
+				return -1;
 			if (10 < ++funny) {
 				fprintf(stderr, "...\n");
 				break;
@@ -237,10 +242,11 @@
 		      int entries,
 		      const char *base,
 		      int baselen,
-		      int missing_ok,
-		      int dryrun)
+		      int flags)
 {
 	struct strbuf buffer;
+	int missing_ok = flags & WRITE_TREE_MISSING_OK;
+	int dryrun = flags & WRITE_TREE_DRY_RUN;
 	int i;
 
 	if (0 <= it->entry_count && has_sha1_file(it->sha1))
@@ -284,8 +290,7 @@
 				    cache + i, entries - i,
 				    path,
 				    baselen + sublen + 1,
-				    missing_ok,
-				    dryrun);
+				    flags);
 		if (subcnt < 0)
 			return subcnt;
 		i += subcnt - 1;
@@ -328,12 +333,14 @@
 			mode = ce->ce_mode;
 			entlen = pathlen - baselen;
 		}
-		if (mode != S_IFGITLINK && !missing_ok && !has_sha1_file(sha1))
+		if (mode != S_IFGITLINK && !missing_ok && !has_sha1_file(sha1)) {
+			strbuf_release(&buffer);
 			return error("invalid object %06o %s for '%.*s'",
 				mode, sha1_to_hex(sha1), entlen+baselen, path);
+		}
 
-		if (ce->ce_flags & CE_REMOVE)
-			continue; /* entry being removed */
+		if (ce->ce_flags & (CE_REMOVE | CE_INTENT_TO_ADD))
+			continue; /* entry being removed or placeholder */
 
 		strbuf_grow(&buffer, entlen + 100);
 		strbuf_addf(&buffer, "%o %.*s%c", mode, entlen, path + baselen, '\0');
@@ -365,14 +372,13 @@
 int cache_tree_update(struct cache_tree *it,
 		      struct cache_entry **cache,
 		      int entries,
-		      int missing_ok,
-		      int dryrun)
+		      int flags)
 {
 	int i;
-	i = verify_cache(cache, entries);
+	i = verify_cache(cache, entries, flags);
 	if (i)
 		return i;
-	i = update_one(it, cache, entries, "", 0, missing_ok, dryrun);
+	i = update_one(it, cache, entries, "", 0, flags);
 	if (i < 0)
 		return i;
 	return 0;
@@ -565,11 +571,9 @@
 
 	was_valid = cache_tree_fully_valid(active_cache_tree);
 	if (!was_valid) {
-		int missing_ok = flags & WRITE_TREE_MISSING_OK;
-
 		if (cache_tree_update(active_cache_tree,
 				      active_cache, active_nr,
-				      missing_ok, 0) < 0)
+				      flags) < 0)
 			return WRITE_TREE_UNMERGED_INDEX;
 		if (0 <= newfd) {
 			if (!write_cache(newfd, active_cache, active_nr) &&
@@ -664,3 +668,11 @@
 		return it->entry_count;
 	return 0;
 }
+
+int update_main_cache_tree(int flags)
+{
+	if (!the_index.cache_tree)
+		the_index.cache_tree = cache_tree();
+	return cache_tree_update(the_index.cache_tree,
+				 the_index.cache, the_index.cache_nr, flags);
+}
diff --git a/cache-tree.h b/cache-tree.h
index 3df641f..d8cb2e9 100644
--- a/cache-tree.h
+++ b/cache-tree.h
@@ -29,11 +29,15 @@
 struct cache_tree *cache_tree_read(const char *buffer, unsigned long size);
 
 int cache_tree_fully_valid(struct cache_tree *);
-int cache_tree_update(struct cache_tree *, struct cache_entry **, int, int, int);
+int cache_tree_update(struct cache_tree *, struct cache_entry **, int, int);
+
+int update_main_cache_tree(int);
 
 /* bitmasks to write_cache_as_tree flags */
 #define WRITE_TREE_MISSING_OK 1
 #define WRITE_TREE_IGNORE_CACHE_TREE 2
+#define WRITE_TREE_DRY_RUN 4
+#define WRITE_TREE_SILENT 8
 
 /* error return codes */
 #define WRITE_TREE_UNREADABLE_INDEX (-1)
diff --git a/cache.h b/cache.h
index e6c7f33..67f28b4 100644
--- a/cache.h
+++ b/cache.h
@@ -4,6 +4,9 @@
 #include "git-compat-util.h"
 #include "strbuf.h"
 #include "hash.h"
+#include "advice.h"
+#include "gettext.h"
+#include "convert.h"
 
 #include SHA1_HEADER
 #ifndef git_SHA_CTX
@@ -14,13 +17,28 @@
 #endif
 
 #include <zlib.h>
-#if defined(NO_DEFLATE_BOUND) || ZLIB_VERNUM < 0x1200
-#define deflateBound(c,s)  ((s) + (((s) + 7) >> 3) + (((s) + 63) >> 6) + 11)
-#endif
+typedef struct git_zstream {
+	z_stream z;
+	unsigned long avail_in;
+	unsigned long avail_out;
+	unsigned long total_in;
+	unsigned long total_out;
+	unsigned char *next_in;
+	unsigned char *next_out;
+} git_zstream;
 
-void git_inflate_init(z_streamp strm);
-void git_inflate_end(z_streamp strm);
-int git_inflate(z_streamp strm, int flush);
+void git_inflate_init(git_zstream *);
+void git_inflate_init_gzip_only(git_zstream *);
+void git_inflate_end(git_zstream *);
+int git_inflate(git_zstream *, int flush);
+
+void git_deflate_init(git_zstream *, int level);
+void git_deflate_init_gzip(git_zstream *, int level);
+void git_deflate_end(git_zstream *);
+int git_deflate_abort(git_zstream *);
+int git_deflate_end_gently(git_zstream *);
+int git_deflate(git_zstream *, int flush);
+unsigned long git_deflate_bound(git_zstream *, unsigned long);
 
 #if defined(DT_UNKNOWN) && !defined(NO_D_TYPE_IN_DIRENT)
 #define DTYPE(de)	((de)->d_type)
@@ -87,6 +105,9 @@
 	unsigned int hdr_entries;
 };
 
+#define INDEX_FORMAT_LB 2
+#define INDEX_FORMAT_UB 4
+
 /*
  * The "cache_time" is just the low 32 bits of the
  * time. It doesn't matter if it overflows - we only
@@ -97,48 +118,6 @@
 	unsigned int nsec;
 };
 
-/*
- * dev/ino/uid/gid/size are also just tracked to the low 32 bits
- * Again - this is just a (very strong in practice) heuristic that
- * the inode hasn't changed.
- *
- * We save the fields in big-endian order to allow using the
- * index file over NFS transparently.
- */
-struct ondisk_cache_entry {
-	struct cache_time ctime;
-	struct cache_time mtime;
-	unsigned int dev;
-	unsigned int ino;
-	unsigned int mode;
-	unsigned int uid;
-	unsigned int gid;
-	unsigned int size;
-	unsigned char sha1[20];
-	unsigned short flags;
-	char name[FLEX_ARRAY]; /* more */
-};
-
-/*
- * This struct is used when CE_EXTENDED bit is 1
- * The struct must match ondisk_cache_entry exactly from
- * ctime till flags
- */
-struct ondisk_cache_entry_extended {
-	struct cache_time ctime;
-	struct cache_time mtime;
-	unsigned int dev;
-	unsigned int ino;
-	unsigned int mode;
-	unsigned int uid;
-	unsigned int gid;
-	unsigned int size;
-	unsigned char sha1[20];
-	unsigned short flags;
-	unsigned short flags2;
-	char name[FLEX_ARRAY]; /* more */
-};
-
 struct cache_entry {
 	struct cache_time ce_ctime;
 	struct cache_time ce_mtime;
@@ -149,12 +128,13 @@
 	unsigned int ce_gid;
 	unsigned int ce_size;
 	unsigned int ce_flags;
+	unsigned int ce_namelen;
 	unsigned char sha1[20];
 	struct cache_entry *next;
+	struct cache_entry *dir_next;
 	char name[FLEX_ARRAY]; /* more */
 };
 
-#define CE_NAMEMASK  (0x0fff)
 #define CE_STAGEMASK (0x3000)
 #define CE_EXTENDED  (0x4000)
 #define CE_VALID     (0x8000)
@@ -169,22 +149,28 @@
  *
  * In-memory only flags
  */
-#define CE_UPDATE    (0x10000)
-#define CE_REMOVE    (0x20000)
-#define CE_UPTODATE  (0x40000)
-#define CE_ADDED     (0x80000)
+#define CE_UPDATE            (1 << 16)
+#define CE_REMOVE            (1 << 17)
+#define CE_UPTODATE          (1 << 18)
+#define CE_ADDED             (1 << 19)
 
-#define CE_HASHED    (0x100000)
-#define CE_UNHASHED  (0x200000)
+#define CE_HASHED            (1 << 20)
+#define CE_UNHASHED          (1 << 21)
+#define CE_WT_REMOVE         (1 << 22) /* remove in work directory */
+#define CE_CONFLICTED        (1 << 23)
+
+#define CE_UNPACKED          (1 << 24)
+#define CE_NEW_SKIP_WORKTREE (1 << 25)
 
 /*
  * Extended on-disk flags
  */
-#define CE_INTENT_TO_ADD 0x20000000
+#define CE_INTENT_TO_ADD     (1 << 29)
+#define CE_SKIP_WORKTREE     (1 << 30)
 /* CE_EXTENDED2 is for future extension */
-#define CE_EXTENDED2 0x80000000
+#define CE_EXTENDED2         (1 << 31)
 
-#define CE_EXTENDED_FLAGS (CE_INTENT_TO_ADD)
+#define CE_EXTENDED_FLAGS (CE_INTENT_TO_ADD | CE_SKIP_WORKTREE)
 
 /*
  * Safeguard to avoid saving wrong flags:
@@ -212,27 +198,16 @@
 	dst->ce_flags = (dst->ce_flags & ~CE_STATE_MASK) | state;
 }
 
-static inline unsigned create_ce_flags(size_t len, unsigned stage)
+static inline unsigned create_ce_flags(unsigned stage)
 {
-	if (len >= CE_NAMEMASK)
-		len = CE_NAMEMASK;
-	return (len | (stage << CE_STAGESHIFT));
+	return (stage << CE_STAGESHIFT);
 }
 
-static inline size_t ce_namelen(const struct cache_entry *ce)
-{
-	size_t len = ce->ce_flags & CE_NAMEMASK;
-	if (len < CE_NAMEMASK)
-		return len;
-	return strlen(ce->name + CE_NAMEMASK) + CE_NAMEMASK;
-}
-
+#define ce_namelen(ce) ((ce)->ce_namelen)
 #define ce_size(ce) cache_entry_size(ce_namelen(ce))
-#define ondisk_ce_size(ce) (((ce)->ce_flags & CE_EXTENDED) ? \
-			    ondisk_cache_entry_extended_size(ce_namelen(ce)) : \
-			    ondisk_cache_entry_size(ce_namelen(ce)))
 #define ce_stage(ce) ((CE_STAGEMASK & (ce)->ce_flags) >> CE_STAGESHIFT)
 #define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)
+#define ce_skip_worktree(ce) ((ce)->ce_flags & CE_SKIP_WORKTREE)
 #define ce_mark_uptodate(ce) ((ce)->ce_flags |= CE_UPTODATE)
 
 #define ce_permissions(mode) (((mode) & 0100) ? 0755 : 0644)
@@ -269,21 +244,26 @@
 	else
 		return DT_UNKNOWN;
 }
-#define canon_mode(mode) \
-	(S_ISREG(mode) ? (S_IFREG | ce_permissions(mode)) : \
-	S_ISLNK(mode) ? S_IFLNK : S_ISDIR(mode) ? S_IFDIR : S_IFGITLINK)
+static inline unsigned int canon_mode(unsigned int mode)
+{
+	if (S_ISREG(mode))
+		return S_IFREG | ce_permissions(mode);
+	if (S_ISLNK(mode))
+		return S_IFLNK;
+	if (S_ISDIR(mode))
+		return S_IFDIR;
+	return S_IFGITLINK;
+}
 
-#define flexible_size(STRUCT,len) ((offsetof(struct STRUCT,name) + (len) + 8) & ~7)
-#define cache_entry_size(len) flexible_size(cache_entry,len)
-#define ondisk_cache_entry_size(len) flexible_size(ondisk_cache_entry,len)
-#define ondisk_cache_entry_extended_size(len) flexible_size(ondisk_cache_entry_extended,len)
+#define cache_entry_size(len) (offsetof(struct cache_entry,name) + (len) + 1)
 
 struct index_state {
 	struct cache_entry **cache;
+	unsigned int version;
 	unsigned int cache_nr, cache_alloc, cache_changed;
+	struct string_list *resolve_undo;
 	struct cache_tree *cache_tree;
 	struct cache_time timestamp;
-	void *alloc;
 	unsigned name_hash_initialized : 1,
 		 initialized : 1;
 	struct hash_table name_hash;
@@ -330,11 +310,14 @@
 #define remove_file_from_cache(path) remove_file_from_index(&the_index, (path))
 #define add_to_cache(path, st, flags) add_to_index(&the_index, (path), (st), (flags))
 #define add_file_to_cache(path, flags) add_file_to_index(&the_index, (path), (flags))
-#define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL)
+#define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL, NULL)
 #define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))
 #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
 #define cache_name_exists(name, namelen, igncase) index_name_exists(&the_index, (name), (namelen), (igncase))
 #define cache_name_is_other(name, namelen) index_name_is_other(&the_index, (name), (namelen))
+#define resolve_undo_clear() resolve_undo_clear_index(&the_index)
+#define unmerge_cache_entry_at(at) unmerge_index_entry_at(&the_index, at)
+#define unmerge_cache(pathspec) unmerge_index(&the_index, pathspec)
 #endif
 
 enum object_type {
@@ -348,7 +331,7 @@
 	OBJ_OFS_DELTA = 6,
 	OBJ_REF_DELTA = 7,
 	OBJ_ANY,
-	OBJ_MAX,
+	OBJ_MAX
 };
 
 static inline enum object_type object_type(unsigned int mode)
@@ -359,6 +342,7 @@
 }
 
 #define GIT_DIR_ENVIRONMENT "GIT_DIR"
+#define GIT_NAMESPACE_ENVIRONMENT "GIT_NAMESPACE"
 #define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
 #define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
 #define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
@@ -366,11 +350,27 @@
 #define GRAFT_ENVIRONMENT "GIT_GRAFT_FILE"
 #define TEMPLATE_DIR_ENVIRONMENT "GIT_TEMPLATE_DIR"
 #define CONFIG_ENVIRONMENT "GIT_CONFIG"
+#define CONFIG_DATA_ENVIRONMENT "GIT_CONFIG_PARAMETERS"
 #define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH"
 #define CEILING_DIRECTORIES_ENVIRONMENT "GIT_CEILING_DIRECTORIES"
+#define NO_REPLACE_OBJECTS_ENVIRONMENT "GIT_NO_REPLACE_OBJECTS"
 #define GITATTRIBUTES_FILE ".gitattributes"
 #define INFOATTRIBUTES_FILE "info/attributes"
 #define ATTRIBUTE_MACRO_PREFIX "[attr]"
+#define GIT_NOTES_REF_ENVIRONMENT "GIT_NOTES_REF"
+#define GIT_NOTES_DEFAULT_REF "refs/notes/commits"
+#define GIT_NOTES_DISPLAY_REF_ENVIRONMENT "GIT_NOTES_DISPLAY_REF"
+#define GIT_NOTES_REWRITE_REF_ENVIRONMENT "GIT_NOTES_REWRITE_REF"
+#define GIT_NOTES_REWRITE_MODE_ENVIRONMENT "GIT_NOTES_REWRITE_MODE"
+
+/*
+ * Repository-local GIT_* environment variables
+ * The array is NULL-terminated to simplify its usage in contexts such
+ * environment creation or simple walk of the list.
+ * The number of non-NULL entries is available as a macro.
+ */
+#define LOCAL_REPO_ENV_SIZE 9
+extern const char *const local_repo_env[LOCAL_REPO_ENV_SIZE + 1];
 
 extern int is_bare_repository_cfg;
 extern int is_bare_repository(void);
@@ -379,12 +379,16 @@
 extern int is_inside_work_tree(void);
 extern int have_git_dir(void);
 extern const char *get_git_dir(void);
+extern int is_git_directory(const char *path);
 extern char *get_object_directory(void);
 extern char *get_index_file(void);
 extern char *get_graft_file(void);
 extern int set_git_dir(const char *path);
+extern const char *get_git_namespace(void);
+extern const char *strip_namespace(const char *namespaced_ref);
 extern const char *get_git_work_tree(void);
-extern const char *read_gitfile_gently(const char *path);
+extern const char *read_gitfile(const char *path);
+extern const char *resolve_gitdir(const char *suspect);
 extern void set_git_work_tree(const char *tree);
 
 #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
@@ -393,13 +397,18 @@
 extern void setup_work_tree(void);
 extern const char *setup_git_directory_gently(int *);
 extern const char *setup_git_directory(void);
-extern const char *prefix_path(const char *prefix, int len, const char *path);
+extern char *prefix_path(const char *prefix, int len, const char *path);
 extern const char *prefix_filename(const char *prefix, int len, const char *path);
-extern void verify_filename(const char *prefix, const char *name);
+extern int check_filename(const char *prefix, const char *name);
+extern void verify_filename(const char *prefix,
+			    const char *name,
+			    int diagnose_misspelt_rev);
 extern void verify_non_filename(const char *prefix, const char *name);
+extern int path_inside_repo(const char *prefix, const char *path);
 
 #define INIT_DB_QUIET 0x0001
 
+extern int set_git_dir_init(const char *git_dir, const char *real_git_dir, int);
 extern int init_db(const char *template_dir, unsigned int flags);
 
 #define alloc_nr(x) (((x)+16)*3/2)
@@ -409,7 +418,7 @@
  * at least 'nr' entries; the number of entries currently allocated
  * is 'alloc', using the standard growing factor alloc_nr() macro.
  *
- * DO NOT USE any expression with side-effect for 'x' or 'alloc'.
+ * DO NOT USE any expression with side-effect for 'x', 'nr', or 'alloc'.
  */
 #define ALLOC_GROW(x, nr, alloc) \
 	do { \
@@ -420,7 +429,7 @@
 				alloc = alloc_nr(alloc); \
 			x = xrealloc((x), alloc * sizeof(*(x))); \
 		} \
-	} while(0)
+	} while (0)
 
 /* Initialize and use the cache information */
 extern int read_index(struct index_state *);
@@ -433,6 +442,7 @@
 extern int unmerged_index(const struct index_state *);
 extern int verify_path(const char *path);
 extern struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen, int igncase);
+extern int index_name_stage_pos(const struct index_state *, const char *name, int namelen, int stage);
 extern int index_name_pos(const struct index_state *, const char *name, int namelen);
 #define ADD_CACHE_OK_TO_ADD 1		/* Ok to add */
 #define ADD_CACHE_OK_TO_REPLACE 2	/* Ok to replace file/directory */
@@ -440,7 +450,6 @@
 #define ADD_CACHE_JUST_APPEND 8		/* Append only; tree.c::read_tree() */
 #define ADD_CACHE_NEW_ONLY 16		/* Do not replace existing ones */
 extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
-extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
 extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
 extern int remove_index_entry_at(struct index_state *, int pos);
 extern void remove_marked_cache_entries(struct index_state *istate);
@@ -459,13 +468,33 @@
 /* do stat comparison even if CE_VALID is true */
 #define CE_MATCH_IGNORE_VALID		01
 /* do not check the contents but report dirty on racily-clean entries */
-#define CE_MATCH_RACY_IS_DIRTY	02
+#define CE_MATCH_RACY_IS_DIRTY		02
+/* do stat comparison even if CE_SKIP_WORKTREE is true */
+#define CE_MATCH_IGNORE_SKIP_WORKTREE	04
 extern int ie_match_stat(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
 extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
 
-extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
-extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path);
-extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
+struct pathspec {
+	const char **raw; /* get_pathspec() result, not freed by free_pathspec() */
+	int nr;
+	unsigned int has_wildcard:1;
+	unsigned int recursive:1;
+	int max_depth;
+	struct pathspec_item {
+		const char *match;
+		int len;
+		unsigned int use_wildcard:1;
+	} *items;
+};
+
+extern int init_pathspec(struct pathspec *, const char **);
+extern void free_pathspec(struct pathspec *);
+extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec);
+
+#define HASH_WRITE_OBJECT 1
+#define HASH_FORMAT_CHECK 2
+extern int index_fd(unsigned char *sha1, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
+extern int index_path(unsigned char *sha1, const char *path, struct stat *st, unsigned flags);
 extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
 
 #define REFRESH_REALLY		0x0001	/* ignore_valid */
@@ -473,8 +502,8 @@
 #define REFRESH_QUIET		0x0004	/* be quiet about it */
 #define REFRESH_IGNORE_MISSING	0x0008	/* ignore non-existent */
 #define REFRESH_IGNORE_SUBMODULES	0x0010	/* ignore submodules */
-#define REFRESH_SAY_CHANGED	0x0020	/* say "changed" not "needs update" */
-extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen);
+#define REFRESH_IN_PORCELAIN	0x0020	/* user friendly output, not "needs update" */
+extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen, const char *header_msg);
 
 struct lock_file {
 	struct lock_file *next;
@@ -485,10 +514,12 @@
 };
 #define LOCK_DIE_ON_ERROR 1
 #define LOCK_NODEREF 2
+extern int unable_to_lock_error(const char *path, int err);
 extern NORETURN void unable_to_lock_index_die(const char *path, int err);
 extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
 extern int hold_lock_file_for_append(struct lock_file *, const char *path, int);
 extern int commit_lock_file(struct lock_file *);
+extern void update_index_if_able(struct index_state *, struct lock_file *);
 
 extern int hold_locked_index(struct lock_file *, int);
 extern int commit_locked_index(struct lock_file *);
@@ -502,6 +533,7 @@
 extern int trust_ctime;
 extern int quote_path_fully;
 extern int has_symlinks;
+extern int minimum_abbrev, default_abbrev;
 extern int ignore_case;
 extern int assume_unchanged;
 extern int prefer_symlink_refs;
@@ -509,23 +541,21 @@
 extern int warn_ambiguous_refs;
 extern int shared_repository;
 extern const char *apply_default_whitespace;
+extern const char *apply_default_ignorewhitespace;
+extern const char *git_attributes_file;
 extern int zlib_compression_level;
 extern int core_compression_level;
 extern int core_compression_seen;
 extern size_t packed_git_window_size;
 extern size_t packed_git_limit;
 extern size_t delta_base_cache_limit;
-extern int auto_crlf;
+extern unsigned long big_file_threshold;
+extern unsigned long pack_size_limit_cfg;
+extern int read_replace_refs;
 extern int fsync_object_files;
 extern int core_preload_index;
-
-enum safe_crlf {
-	SAFE_CRLF_FALSE = 0,
-	SAFE_CRLF_FAIL = 1,
-	SAFE_CRLF_WARN = 2,
-};
-
-extern enum safe_crlf safe_crlf;
+extern int core_apply_sparse_checkout;
+extern int precomposed_unicode;
 
 enum branch_track {
 	BRANCH_TRACK_UNSPECIFIED = -1,
@@ -533,20 +563,23 @@
 	BRANCH_TRACK_REMOTE,
 	BRANCH_TRACK_ALWAYS,
 	BRANCH_TRACK_EXPLICIT,
+	BRANCH_TRACK_OVERRIDE
 };
 
 enum rebase_setup_type {
 	AUTOREBASE_NEVER = 0,
 	AUTOREBASE_LOCAL,
 	AUTOREBASE_REMOTE,
-	AUTOREBASE_ALWAYS,
+	AUTOREBASE_ALWAYS
 };
 
 enum push_default_type {
 	PUSH_DEFAULT_NOTHING = 0,
 	PUSH_DEFAULT_MATCHING,
-	PUSH_DEFAULT_TRACKING,
+	PUSH_DEFAULT_SIMPLE,
+	PUSH_DEFAULT_UPSTREAM,
 	PUSH_DEFAULT_CURRENT,
+	PUSH_DEFAULT_UNSPECIFIED
 };
 
 extern enum branch_track git_branch_track;
@@ -555,11 +588,13 @@
 
 enum object_creation_mode {
 	OBJECT_CREATION_USES_HARDLINKS = 0,
-	OBJECT_CREATION_USES_RENAMES = 1,
+	OBJECT_CREATION_USES_RENAMES = 1
 };
 
 extern enum object_creation_mode object_creation_mode;
 
+extern char *notes_ref_name;
+
 extern int grafts_replace_parents;
 
 #define GIT_REPO_VERSION 0
@@ -580,23 +615,38 @@
 	__attribute__((format (printf, 3, 4)));
 extern char *git_pathdup(const char *fmt, ...)
 	__attribute__((format (printf, 1, 2)));
+extern char *mkpathdup(const char *fmt, ...)
+	__attribute__((format (printf, 1, 2)));
 
 /* Return a statically allocated filename matching the sha1 signature */
 extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
 extern char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
+extern char *git_path_submodule(const char *path, const char *fmt, ...)
+	__attribute__((format (printf, 2, 3)));
+
 extern char *sha1_file_name(const unsigned char *sha1);
 extern char *sha1_pack_name(const unsigned char *sha1);
 extern char *sha1_pack_index_name(const unsigned char *sha1);
 extern const char *find_unique_abbrev(const unsigned char *sha1, int);
 extern const unsigned char null_sha1[20];
-static inline int is_null_sha1(const unsigned char *sha1)
-{
-	return !memcmp(sha1, null_sha1, 20);
-}
+
 static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)
 {
-	return memcmp(sha1, sha2, 20);
+	int i;
+
+	for (i = 0; i < 20; i++, sha1++, sha2++) {
+		if (*sha1 != *sha2)
+			return *sha1 - *sha2;
+	}
+
+	return 0;
 }
+
+static inline int is_null_sha1(const unsigned char *sha1)
+{
+	return !hashcmp(sha1, null_sha1);
+}
+
 static inline void hashcpy(unsigned char *sha_dst, const unsigned char *sha_src)
 {
 	memcpy(sha_dst, sha_src, 20);
@@ -605,18 +655,36 @@
 {
 	memset(hash, 0, 20);
 }
-extern int is_empty_blob_sha1(const unsigned char *sha1);
 
 #define EMPTY_TREE_SHA1_HEX \
 	"4b825dc642cb6eb9a060e54bf8d69288fbee4904"
-#define EMPTY_TREE_SHA1_BIN \
+#define EMPTY_TREE_SHA1_BIN_LITERAL \
 	 "\x4b\x82\x5d\xc6\x42\xcb\x6e\xb9\xa0\x60" \
 	 "\xe5\x4b\xf8\xd6\x92\x88\xfb\xee\x49\x04"
+#define EMPTY_TREE_SHA1_BIN \
+	 ((const unsigned char *) EMPTY_TREE_SHA1_BIN_LITERAL)
+
+#define EMPTY_BLOB_SHA1_HEX \
+	"e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"
+#define EMPTY_BLOB_SHA1_BIN_LITERAL \
+	"\xe6\x9d\xe2\x9b\xb2\xd1\xd6\x43\x4b\x8b" \
+	"\x29\xae\x77\x5a\xd8\xc2\xe4\x8c\x53\x91"
+#define EMPTY_BLOB_SHA1_BIN \
+	((const unsigned char *) EMPTY_BLOB_SHA1_BIN_LITERAL)
+
+static inline int is_empty_blob_sha1(const unsigned char *sha1)
+{
+	return !hashcmp(sha1, EMPTY_BLOB_SHA1_BIN);
+}
 
 int git_mkstemp(char *path, size_t n, const char *template);
 
 int git_mkstemps(char *path, size_t n, const char *template, int suffix_len);
 
+/* set default permissions by passing mode arguments to open(2) */
+int git_mkstemps_mode(char *pattern, int suffix_len, int mode);
+int git_mkstemp_mode(char *pattern, int mode);
+
 /*
  * NOTE NOTE NOTE!!
  *
@@ -630,37 +698,62 @@
 	OLD_PERM_GROUP      = 1,
 	OLD_PERM_EVERYBODY  = 2,
 	PERM_GROUP          = 0660,
-	PERM_EVERYBODY      = 0664,
+	PERM_EVERYBODY      = 0664
 };
 int git_config_perm(const char *var, const char *value);
 int set_shared_perm(const char *path, int mode);
 #define adjust_shared_perm(path) set_shared_perm((path), 0)
 int safe_create_leading_directories(char *path);
 int safe_create_leading_directories_const(const char *path);
-char *enter_repo(char *path, int strict);
+int mkdir_in_gitdir(const char *path);
+extern void home_config_paths(char **global, char **xdg, char *file);
+extern char *expand_user_path(const char *path);
+const char *enter_repo(const char *path, int strict);
 static inline int is_absolute_path(const char *path)
 {
-	return path[0] == '/' || has_dos_drive_prefix(path);
+	return is_dir_sep(path[0]) || has_dos_drive_prefix(path);
 }
 int is_directory(const char *);
-const char *make_absolute_path(const char *path);
-const char *make_nonrelative_path(const char *path);
-const char *make_relative_path(const char *abs, const char *base);
+const char *real_path(const char *path);
+const char *absolute_path(const char *path);
+const char *relative_path(const char *abs, const char *base);
 int normalize_path_copy(char *dst, const char *src);
 int longest_ancestor_length(const char *path, const char *prefix_list);
 char *strip_path_suffix(const char *path, const char *suffix);
+int daemon_avoid_alias(const char *path);
+int offset_1st_component(const char *path);
+
+/* object replacement */
+#define READ_SHA1_FILE_REPLACE 1
+extern void *read_sha1_file_extended(const unsigned char *sha1, enum object_type *type, unsigned long *size, unsigned flag);
+static inline void *read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size)
+{
+	return read_sha1_file_extended(sha1, type, size, READ_SHA1_FILE_REPLACE);
+}
+extern const unsigned char *do_lookup_replace_object(const unsigned char *sha1);
+static inline const unsigned char *lookup_replace_object(const unsigned char *sha1)
+{
+	if (!read_replace_refs)
+		return sha1;
+	return do_lookup_replace_object(sha1);
+}
 
 /* Read and unpack a sha1 file into memory, write memory to a sha1 file */
 extern int sha1_object_info(const unsigned char *, unsigned long *);
-extern void * read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size);
 extern int hash_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1);
-extern int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
+extern int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
 extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
 extern int force_object_loose(const unsigned char *sha1, time_t mtime);
+extern void *map_sha1_file(const unsigned char *sha1, unsigned long *size);
+extern int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
+extern int parse_sha1_header(const char *hdr, unsigned long *sizep);
 
 /* global flag to enable extra checks when accessing packed objects */
 extern int do_check_packed_object_crc;
 
+/* for development: log offset of pack access */
+extern const char *log_pack_access;
+
 extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type);
 
 extern int move_temp_to_file(const char *tmpfile, const char *filename);
@@ -669,9 +762,10 @@
 extern int has_sha1_file(const unsigned char *sha1);
 extern int has_loose_object_nonlocal(const unsigned char *sha1);
 
-extern int has_pack_file(const unsigned char *sha1);
 extern int has_pack_index(const unsigned char *sha1);
 
+extern void assert_sha1_type(const unsigned char *sha1, enum object_type expect);
+
 extern const signed char hexval_table[256];
 static inline unsigned int hexval(unsigned char c)
 {
@@ -679,22 +773,90 @@
 }
 
 /* Convert to/from hex/sha1 representation */
-#define MINIMUM_ABBREV 4
-#define DEFAULT_ABBREV 7
+#define MINIMUM_ABBREV minimum_abbrev
+#define DEFAULT_ABBREV default_abbrev
+
+struct object_context {
+	unsigned char tree[20];
+	char path[PATH_MAX];
+	unsigned mode;
+};
+
+#define GET_SHA1_QUIETLY        01
+#define GET_SHA1_COMMIT         02
+#define GET_SHA1_COMMITTISH     04
+#define GET_SHA1_TREE          010
+#define GET_SHA1_TREEISH       020
+#define GET_SHA1_BLOB	       040
+#define GET_SHA1_ONLY_TO_DIE 04000
 
 extern int get_sha1(const char *str, unsigned char *sha1);
-extern int get_sha1_with_mode(const char *str, unsigned char *sha1, unsigned *mode);
+extern int get_sha1_commit(const char *str, unsigned char *sha1);
+extern int get_sha1_committish(const char *str, unsigned char *sha1);
+extern int get_sha1_tree(const char *str, unsigned char *sha1);
+extern int get_sha1_treeish(const char *str, unsigned char *sha1);
+extern int get_sha1_blob(const char *str, unsigned char *sha1);
+extern void maybe_die_on_misspelt_object_name(const char *name, const char *prefix);
+extern int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *orc);
+
+typedef int each_abbrev_fn(const unsigned char *sha1, void *);
+extern int for_each_abbrev(const char *prefix, each_abbrev_fn, void *);
+
+/*
+ * Try to read a SHA1 in hexadecimal format from the 40 characters
+ * starting at hex.  Write the 20-byte result to sha1 in binary form.
+ * Return 0 on success.  Reading stops if a NUL is encountered in the
+ * input, so it is safe to pass this function an arbitrary
+ * null-terminated string.
+ */
 extern int get_sha1_hex(const char *hex, unsigned char *sha1);
+
 extern char *sha1_to_hex(const unsigned char *sha1);	/* static buffer result! */
-extern int read_ref(const char *filename, unsigned char *sha1);
-extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *);
+extern int read_ref_full(const char *refname, unsigned char *sha1,
+			 int reading, int *flags);
+extern int read_ref(const char *refname, unsigned char *sha1);
+
+/*
+ * Resolve a reference, recursively following symbolic refererences.
+ *
+ * Store the referred-to object's name in sha1 and return the name of
+ * the non-symbolic reference that ultimately pointed at it.  The
+ * return value, if not NULL, is a pointer into either a static buffer
+ * or the input ref.
+ *
+ * If the reference cannot be resolved to an object, the behavior
+ * depends on the "reading" argument:
+ *
+ * - If reading is set, return NULL.
+ *
+ * - If reading is not set, clear sha1 and return the name of the last
+ *   reference name in the chain, which will either be a non-symbolic
+ *   reference or an undefined reference.  If this is a prelude to
+ *   "writing" to the ref, the return value is the name of the ref
+ *   that will actually be created or changed.
+ *
+ * If flag is non-NULL, set the value that it points to the
+ * combination of REF_ISPACKED (if the reference was found among the
+ * packed references) and REF_ISSYMREF (if the initial reference was a
+ * symbolic reference).
+ *
+ * If ref is not a properly-formatted, normalized reference, return
+ * NULL.  If more than MAXDEPTH recursive symbolic lookups are needed,
+ * give up and return NULL.
+ *
+ * errno is sometimes set on errors, but not always.
+ */
+extern const char *resolve_ref_unsafe(const char *ref, unsigned char *sha1, int reading, int *flag);
+extern char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag);
+
 extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
 extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
 extern int interpret_branch_name(const char *str, struct strbuf *);
+extern int get_sha1_mb(const char *str, unsigned char *sha1);
 
 extern int refname_match(const char *abbrev_name, const char *full_name, const char **rules);
 extern const char *ref_rev_parse_rules[];
-extern const char *ref_fetch_rules[];
+#define ref_fetch_rules ref_rev_parse_rules
 
 extern int create_symref(const char *ref, const char *refs_heads_master, const char *logmsg);
 extern int validate_headref(const char *ref);
@@ -702,6 +864,7 @@
 extern int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
 extern int df_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
 extern int cache_name_compare(const char *name1, int len1, const char *name2, int len2);
+extern int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2);
 
 extern void *read_object_with_reference(const unsigned char *sha1,
 					const char *required_type,
@@ -722,18 +885,45 @@
 };
 
 const char *show_date(unsigned long time, int timezone, enum date_mode mode);
+void show_date_relative(unsigned long time, int tz, const struct timeval *now,
+			struct strbuf *timebuf);
 int parse_date(const char *date, char *buf, int bufsize);
+int parse_date_basic(const char *date, unsigned long *timestamp, int *offset);
 void datestamp(char *buf, int bufsize);
-unsigned long approxidate(const char *);
+#define approxidate(s) approxidate_careful((s), NULL)
+unsigned long approxidate_careful(const char *, int *);
+unsigned long approxidate_relative(const char *date, const struct timeval *now);
 enum date_mode parse_date_format(const char *format);
 
-#define IDENT_WARN_ON_NO_NAME  1
-#define IDENT_ERROR_ON_NO_NAME 2
-#define IDENT_NO_DATE	       4
+#define IDENT_STRICT	       1
+#define IDENT_NO_DATE	       2
+#define IDENT_NO_NAME	       4
 extern const char *git_author_info(int);
 extern const char *git_committer_info(int);
 extern const char *fmt_ident(const char *name, const char *email, const char *date_str, int);
 extern const char *fmt_name(const char *name, const char *email);
+extern const char *ident_default_name(void);
+extern const char *ident_default_email(void);
+extern const char *ident_default_date(void);
+extern const char *git_editor(void);
+extern const char *git_pager(int stdout_is_tty);
+extern int git_ident_config(const char *, const char *, void *);
+
+struct ident_split {
+	const char *name_begin;
+	const char *name_end;
+	const char *mail_begin;
+	const char *mail_end;
+	const char *date_begin;
+	const char *date_end;
+	const char *tz_begin;
+	const char *tz_end;
+};
+/*
+ * Signals an success with 0, but time part of the result may be NULL
+ * if the input lacks timestamp and zone
+ */
+extern int split_ident_line(struct ident_split *, const char *, int);
 
 struct checkout {
 	const char *base_dir;
@@ -756,10 +946,10 @@
 
 extern int has_symlink_leading_path(const char *name, int len);
 extern int threaded_has_symlink_leading_path(struct cache_def *, const char *, int);
-extern int has_symlink_or_noent_leading_path(const char *name, int len);
+extern int check_leading_path(const char *name, int len);
+extern int threaded_check_leading_path(struct cache_def *cache, const char *name, int len);
 extern int has_dirs_only_path(const char *name, int len, int prefix_len);
-extern void invalidate_lstat_cache(const char *name, int len);
-extern void clear_lstat_cache(void);
+extern int threaded_has_dirs_only_path(struct cache_def *cache, const char *name, int len, int prefix_len);
 extern void schedule_dir_for_removal(const char *name, int len);
 extern void remove_scheduled_dirs(void);
 
@@ -769,6 +959,7 @@
 	char base[FLEX_ARRAY]; /* more */
 } *alt_odb_list;
 extern void prepare_alt_odb(void);
+extern void read_info_alternates(const char * relative_base, int depth);
 extern void add_to_alternates_file(const char *reference);
 typedef int alt_odb_fn(struct alternate_object_database *, void *);
 extern void foreach_alt_odb(alt_odb_fn, void*);
@@ -795,7 +986,8 @@
 	time_t mtime;
 	int pack_fd;
 	unsigned pack_local:1,
-		 pack_keep:1;
+		 pack_keep:1,
+		 do_not_close:1;
 	unsigned char sha1[20];
 	/* something like ".git/objects/pack/xxxxx.pack" */
 	char pack_name[FLEX_ARRAY]; /* more */
@@ -823,7 +1015,7 @@
 		REF_STATUS_REJECT_NODELETE,
 		REF_STATUS_UPTODATE,
 		REF_STATUS_REMOTE_REJECT,
-		REF_STATUS_EXPECTING_REPORT,
+		REF_STATUS_EXPECTING_REPORT
 	} status;
 	char *remote_status;
 	struct ref *peer_ref; /* when renaming */
@@ -839,16 +1031,16 @@
 #define CONNECT_VERBOSE       (1u << 0)
 extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags);
 extern int finish_connect(struct child_process *conn);
-extern int path_match(const char *path, int nr, char **match);
-extern int get_ack(int fd, unsigned char *result_sha1);
+extern int git_connection_is_socket(struct child_process *conn);
 struct extra_have_objects {
 	int nr, alloc;
 	unsigned char (*array)[20];
 };
-extern struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match, unsigned int flags, struct extra_have_objects *);
+extern struct ref **get_remote_heads(int in, struct ref **list, unsigned int flags, struct extra_have_objects *);
 extern int server_supports(const char *feature);
+extern const char *parse_feature_request(const char *features, const char *feature);
 
-extern struct packed_git *parse_pack_index(unsigned char *sha1);
+extern struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path);
 
 extern void prepare_packed_git(void);
 extern void reprepare_packed_git(void);
@@ -859,7 +1051,8 @@
 
 extern void pack_report(void);
 extern int open_pack_index(struct packed_git *);
-extern unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned int *);
+extern void close_pack_index(struct packed_git *);
+extern unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned long *);
 extern void close_pack_windows(struct packed_git *);
 extern void unuse_pack(struct pack_window **);
 extern void free_pack_by_name(const char *);
@@ -868,38 +1061,101 @@
 extern const unsigned char *nth_packed_object_sha1(struct packed_git *, uint32_t);
 extern off_t nth_packed_object_offset(const struct packed_git *, uint32_t);
 extern off_t find_pack_entry_one(const unsigned char *, struct packed_git *);
+extern int is_pack_valid(struct packed_git *);
 extern void *unpack_entry(struct packed_git *, off_t, enum object_type *, unsigned long *);
 extern unsigned long unpack_object_header_buffer(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
 extern unsigned long get_size_from_delta(struct packed_git *, struct pack_window **, off_t);
-extern const char *packed_object_info_detail(struct packed_git *, off_t, unsigned long *, unsigned long *, unsigned int *, unsigned char *);
+extern int unpack_object_header(struct packed_git *, struct pack_window **, off_t *, unsigned long *);
+
+struct object_info {
+	/* Request */
+	unsigned long *sizep;
+
+	/* Response */
+	enum {
+		OI_CACHED,
+		OI_LOOSE,
+		OI_PACKED,
+		OI_DBCACHED
+	} whence;
+	union {
+		/*
+		 * struct {
+		 * 	... Nothing to expose in this case
+		 * } cached;
+		 * struct {
+		 * 	... Nothing to expose in this case
+		 * } loose;
+		 */
+		struct {
+			struct packed_git *pack;
+			off_t offset;
+			unsigned int is_delta;
+		} packed;
+	} u;
+};
+extern int sha1_object_info_extended(const unsigned char *, struct object_info *);
 
 /* Dumb servers support */
 extern int update_server_info(int);
 
+/* git_config_parse_key() returns these negated: */
+#define CONFIG_INVALID_KEY 1
+#define CONFIG_NO_SECTION_OR_NAME 2
+/* git_config_set(), git_config_set_multivar() return the above or these: */
+#define CONFIG_NO_LOCK -1
+#define CONFIG_INVALID_FILE 3
+#define CONFIG_NO_WRITE 4
+#define CONFIG_NOTHING_SET 5
+#define CONFIG_INVALID_PATTERN 6
+
 typedef int (*config_fn_t)(const char *, const char *, void *);
 extern int git_default_config(const char *, const char *, void *);
 extern int git_config_from_file(config_fn_t fn, const char *, void *);
+extern void git_config_push_parameter(const char *text);
+extern int git_config_from_parameters(config_fn_t fn, void *data);
 extern int git_config(config_fn_t fn, void *);
+extern int git_config_with_options(config_fn_t fn, void *,
+				   const char *filename, int respect_includes);
+extern int git_config_early(config_fn_t fn, void *, const char *repo_config);
 extern int git_parse_ulong(const char *, unsigned long *);
 extern int git_config_int(const char *, const char *);
 extern unsigned long git_config_ulong(const char *, const char *);
 extern int git_config_bool_or_int(const char *, const char *, int *);
 extern int git_config_bool(const char *, const char *);
+extern int git_config_maybe_bool(const char *, const char *);
 extern int git_config_string(const char **, const char *, const char *);
+extern int git_config_pathname(const char **, const char *, const char *);
+extern int git_config_set_in_file(const char *, const char *, const char *);
 extern int git_config_set(const char *, const char *);
+extern int git_config_parse_key(const char *, char **, int *);
 extern int git_config_set_multivar(const char *, const char *, const char *, int);
+extern int git_config_set_multivar_in_file(const char *, const char *, const char *, const char *, int);
 extern int git_config_rename_section(const char *, const char *);
+extern int git_config_rename_section_in_file(const char *, const char *, const char *);
 extern const char *git_etc_gitconfig(void);
 extern int check_repository_format_version(const char *var, const char *value, void *cb);
+extern int git_env_bool(const char *, int);
 extern int git_config_system(void);
-extern int git_config_global(void);
 extern int config_error_nonbool(const char *);
-extern const char *config_exclusive_filename;
+extern const char *get_log_output_encoding(void);
+extern const char *get_commit_output_encoding(void);
 
-#define MAX_GITNAME (1000)
-extern char git_default_email[MAX_GITNAME];
-extern char git_default_name[MAX_GITNAME];
+extern int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
+
+struct config_include_data {
+	int depth;
+	config_fn_t fn;
+	void *data;
+};
+#define CONFIG_INCLUDE_INIT { 0 }
+extern int git_config_include(const char *name, const char *value, void *data);
+
+#define IDENT_NAME_GIVEN 01
+#define IDENT_MAIL_GIVEN 02
+#define IDENT_ALL_GIVEN (IDENT_NAME_GIVEN|IDENT_MAIL_GIVEN)
 extern int user_ident_explicitly_given;
+extern int user_ident_sufficiently_given(void);
 
 extern const char *git_commit_encoding;
 extern const char *git_log_output_encoding;
@@ -909,20 +1165,29 @@
 extern void maybe_flush_or_die(FILE *, const char *);
 extern int copy_fd(int ifd, int ofd);
 extern int copy_file(const char *dst, const char *src, int mode);
-extern ssize_t read_in_full(int fd, void *buf, size_t count);
-extern ssize_t write_in_full(int fd, const void *buf, size_t count);
+extern int copy_file_with_time(const char *dst, const char *src, int mode);
 extern void write_or_die(int fd, const void *buf, size_t count);
 extern int write_or_whine(int fd, const void *buf, size_t count, const char *msg);
 extern int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg);
 extern void fsync_or_die(int fd, const char *);
 
+extern ssize_t read_in_full(int fd, void *buf, size_t count);
+extern ssize_t write_in_full(int fd, const void *buf, size_t count);
+static inline ssize_t write_str_in_full(int fd, const char *str)
+{
+	return write_in_full(fd, str, strlen(str));
+}
+
 /* pager.c */
 extern void setup_pager(void);
 extern const char *pager_program;
 extern int pager_in_use(void);
 extern int pager_use_color;
+extern int term_columns(void);
+extern int decimal_width(int);
 
 extern const char *editor_program;
+extern const char *askpass_program;
 extern const char *excludes_file;
 
 /* base85 */
@@ -938,14 +1203,16 @@
 extern void alloc_report(void);
 
 /* trace.c */
+__attribute__((format (printf, 1, 2)))
 extern void trace_printf(const char *format, ...);
+extern void trace_vprintf(const char *key, const char *format, va_list ap);
+__attribute__((format (printf, 2, 3)))
 extern void trace_argv_printf(const char **argv, const char *format, ...);
+extern void trace_repo_setup(const char *prefix);
+extern int trace_want(const char *key);
+extern void trace_strbuf(const char *key, const struct strbuf *buf);
 
-/* convert.c */
-/* returns 1 if *dst was used */
-extern int convert_to_git(const char *path, const char *src, size_t len,
-                          struct strbuf *dst, enum safe_crlf checksafe);
-extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst);
+void packet_trace_identity(const char *prog);
 
 /* add */
 /*
@@ -959,30 +1226,51 @@
 
 /* match-trees.c */
 void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, int);
+void shift_tree_by(const unsigned char *, const unsigned char *, unsigned char *, const char *);
 
 /*
  * whitespace rules.
  * used by both diff and apply
+ * last two digits are tab width
  */
-#define WS_TRAILING_SPACE	01
-#define WS_SPACE_BEFORE_TAB	02
-#define WS_INDENT_WITH_NON_TAB	04
-#define WS_CR_AT_EOL           010
-#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB)
+#define WS_BLANK_AT_EOL         0100
+#define WS_SPACE_BEFORE_TAB     0200
+#define WS_INDENT_WITH_NON_TAB  0400
+#define WS_CR_AT_EOL           01000
+#define WS_BLANK_AT_EOF        02000
+#define WS_TAB_IN_INDENT       04000
+#define WS_TRAILING_SPACE      (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF)
+#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB|8)
+#define WS_TAB_WIDTH_MASK        077
 extern unsigned whitespace_rule_cfg;
 extern unsigned whitespace_rule(const char *);
 extern unsigned parse_whitespace_rule(const char *);
 extern unsigned ws_check(const char *line, int len, unsigned ws_rule);
 extern void ws_check_emit(const char *line, int len, unsigned ws_rule, FILE *stream, const char *set, const char *reset, const char *ws);
 extern char *whitespace_error_string(unsigned ws);
-extern int ws_fix_copy(char *, const char *, int, unsigned, int *);
+extern void ws_fix_copy(struct strbuf *, const char *, int, unsigned, int *);
 extern int ws_blank_line(const char *line, int len, unsigned ws_rule);
+#define ws_tab_width(rule)     ((rule) & WS_TAB_WIDTH_MASK)
 
 /* ls-files */
-int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset);
+int report_path_error(const char *ps_matched, const char **pathspec, const char *prefix);
 void overlay_tree_on_cache(const char *tree_name, const char *prefix);
 
 char *alias_lookup(const char *alias);
 int split_cmdline(char *cmdline, const char ***argv);
+/* Takes a negative value returned by split_cmdline */
+const char *split_cmdline_strerror(int cmdline_errno);
+
+/* git.c */
+struct startup_info {
+	int have_repository;
+	const char *prefix;
+};
+extern struct startup_info *startup_info;
+
+/* builtin/merge.c */
+int checkout_fast_forward(const unsigned char *from, const unsigned char *to);
+
+int sane_execvp(const char *file, char *const argv[]);
 
 #endif /* CACHE_H */
diff --git a/color.c b/color.c
index 62977f4..e8e2681 100644
--- a/color.c
+++ b/color.c
@@ -1,7 +1,30 @@
 #include "cache.h"
 #include "color.h"
 
-int git_use_color_default = 0;
+static int git_use_color_default = 0;
+int color_stdout_is_tty = -1;
+
+/*
+ * The list of available column colors.
+ */
+const char *column_colors_ansi[] = {
+	GIT_COLOR_RED,
+	GIT_COLOR_GREEN,
+	GIT_COLOR_YELLOW,
+	GIT_COLOR_BLUE,
+	GIT_COLOR_MAGENTA,
+	GIT_COLOR_CYAN,
+	GIT_COLOR_BOLD_RED,
+	GIT_COLOR_BOLD_GREEN,
+	GIT_COLOR_BOLD_YELLOW,
+	GIT_COLOR_BOLD_BLUE,
+	GIT_COLOR_BOLD_MAGENTA,
+	GIT_COLOR_BOLD_CYAN,
+	GIT_COLOR_RESET,
+};
+
+/* Ignore the RESET at the end when giving the size */
+const int column_colors_ansi_max = ARRAY_SIZE(column_colors_ansi) - 1;
 
 static int parse_color(const char *name, int len)
 {
@@ -47,7 +70,7 @@
 {
 	const char *ptr = value;
 	int len = value_len;
-	int attr = -1;
+	unsigned int attr = 0;
 	int fg = -2;
 	int bg = -2;
 
@@ -56,7 +79,7 @@
 		return;
 	}
 
-	/* [fg [bg]] [attr] */
+	/* [fg [bg]] [attr]... */
 	while (len > 0) {
 		const char *word = ptr;
 		int val, wordlen = 0;
@@ -85,19 +108,27 @@
 			goto bad;
 		}
 		val = parse_attr(word, wordlen);
-		if (val < 0 || attr != -1)
+		if (0 <= val)
+			attr |= (1 << val);
+		else
 			goto bad;
-		attr = val;
 	}
 
-	if (attr >= 0 || fg >= 0 || bg >= 0) {
+	if (attr || fg >= 0 || bg >= 0) {
 		int sep = 0;
+		int i;
 
 		*dst++ = '\033';
 		*dst++ = '[';
-		if (attr >= 0) {
-			*dst++ = '0' + attr;
-			sep++;
+
+		for (i = 0; attr; i++) {
+			unsigned bit = (1 << i);
+			if (!(attr & bit))
+				continue;
+			attr &= ~bit;
+			if (sep++)
+				*dst++ = ';';
+			*dst++ = '0' + i;
 		}
 		if (fg >= 0) {
 			if (sep++)
@@ -127,7 +158,7 @@
 	die("bad color value '%.*s' for variable '%s'", value_len, value, var);
 }
 
-int git_config_colorbool(const char *var, const char *value, int stdout_is_tty)
+int git_config_colorbool(const char *var, const char *value)
 {
 	if (value) {
 		if (!strcasecmp(value, "never"))
@@ -135,18 +166,25 @@
 		if (!strcasecmp(value, "always"))
 			return 1;
 		if (!strcasecmp(value, "auto"))
-			goto auto_color;
+			return GIT_COLOR_AUTO;
 	}
 
+	if (!var)
+		return -1;
+
 	/* Missing or explicit false to turn off colorization */
 	if (!git_config_bool(var, value))
 		return 0;
 
 	/* any normal truth value defaults to 'auto' */
- auto_color:
-	if (stdout_is_tty < 0)
-		stdout_is_tty = isatty(1);
-	if (stdout_is_tty || (pager_in_use() && pager_use_color)) {
+	return GIT_COLOR_AUTO;
+}
+
+static int check_auto_color(void)
+{
+	if (color_stdout_is_tty < 0)
+		color_stdout_is_tty = isatty(1);
+	if (color_stdout_is_tty || (pager_in_use() && pager_use_color)) {
 		char *term = getenv("TERM");
 		if (term && strcmp(term, "dumb"))
 			return 1;
@@ -154,16 +192,48 @@
 	return 0;
 }
 
-int git_color_default_config(const char *var, const char *value, void *cb)
+int want_color(int var)
+{
+	static int want_auto = -1;
+
+	if (var < 0)
+		var = git_use_color_default;
+
+	if (var == GIT_COLOR_AUTO) {
+		if (want_auto < 0)
+			want_auto = check_auto_color();
+		return want_auto;
+	}
+	return var;
+}
+
+int git_color_config(const char *var, const char *value, void *cb)
 {
 	if (!strcmp(var, "color.ui")) {
-		git_use_color_default = git_config_colorbool(var, value, -1);
+		git_use_color_default = git_config_colorbool(var, value);
 		return 0;
 	}
 
+	return 0;
+}
+
+int git_color_default_config(const char *var, const char *value, void *cb)
+{
+	if (git_color_config(var, value, cb) < 0)
+		return -1;
+
 	return git_default_config(var, value, cb);
 }
 
+void color_print_strbuf(FILE *fp, const char *color, const struct strbuf *sb)
+{
+	if (*color)
+		fprintf(fp, "%s", color);
+	fprintf(fp, "%s", sb->buf);
+	if (*color)
+		fprintf(fp, "%s", GIT_COLOR_RESET);
+}
+
 static int color_vfprintf(FILE *fp, const char *color, const char *fmt,
 		va_list args, const char *trail)
 {
@@ -201,30 +271,7 @@
 	return r;
 }
 
-/*
- * This function splits the buffer by newlines and colors the lines individually.
- *
- * Returns 0 on success.
- */
-int color_fwrite_lines(FILE *fp, const char *color,
-		size_t count, const char *buf)
+int color_is_nil(const char *c)
 {
-	if (!*color)
-		return fwrite(buf, count, 1, fp) != 1;
-	while (count) {
-		char *p = memchr(buf, '\n', count);
-		if (p != buf && (fputs(color, fp) < 0 ||
-				fwrite(buf, p ? p - buf : count, 1, fp) != 1 ||
-				fputs(GIT_COLOR_RESET, fp) < 0))
-			return -1;
-		if (!p)
-			return 0;
-		if (fputc('\n', fp) < 0)
-			return -1;
-		count -= p + 1 - buf;
-		buf = p + 1;
-	}
-	return 0;
+	return !strcmp(c, "NIL");
 }
-
-
diff --git a/color.h b/color.h
index 18abeb7..9a8495b 100644
--- a/color.h
+++ b/color.h
@@ -1,9 +1,28 @@
 #ifndef COLOR_H
 #define COLOR_H
 
-/* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */
-#define COLOR_MAXLEN 24
+struct strbuf;
 
+/*  2 + (2 * num_attrs) + 8 + 1 + 8 + 'm' + NUL */
+/* "\033[1;2;4;5;7;38;5;2xx;48;5;2xxm\0" */
+/*
+ * The maximum length of ANSI color sequence we would generate:
+ * - leading ESC '['            2
+ * - attr + ';'                 2 * 8 (e.g. "1;")
+ * - fg color + ';'             9 (e.g. "38;5;2xx;")
+ * - fg color + ';'             9 (e.g. "48;5;2xx;")
+ * - terminating 'm' NUL        2
+ *
+ * The above overcounts attr (we only use 5 not 8) and one semicolon
+ * but it is close enough.
+ */
+#define COLOR_MAXLEN 40
+
+/*
+ * IMPORTANT: Due to the way these color codes are emulated on Windows,
+ * write them only using printf(), fprintf(), and fputs(). In particular,
+ * do not use puts() or write().
+ */
 #define GIT_COLOR_NORMAL	""
 #define GIT_COLOR_RESET		"\033[m"
 #define GIT_COLOR_BOLD		"\033[1m"
@@ -13,24 +32,59 @@
 #define GIT_COLOR_BLUE		"\033[34m"
 #define GIT_COLOR_MAGENTA	"\033[35m"
 #define GIT_COLOR_CYAN		"\033[36m"
+#define GIT_COLOR_BOLD_RED	"\033[1;31m"
+#define GIT_COLOR_BOLD_GREEN	"\033[1;32m"
+#define GIT_COLOR_BOLD_YELLOW	"\033[1;33m"
+#define GIT_COLOR_BOLD_BLUE	"\033[1;34m"
+#define GIT_COLOR_BOLD_MAGENTA	"\033[1;35m"
+#define GIT_COLOR_BOLD_CYAN	"\033[1;36m"
 #define GIT_COLOR_BG_RED	"\033[41m"
+#define GIT_COLOR_BG_GREEN	"\033[42m"
+#define GIT_COLOR_BG_YELLOW	"\033[43m"
+#define GIT_COLOR_BG_BLUE	"\033[44m"
+#define GIT_COLOR_BG_MAGENTA	"\033[45m"
+#define GIT_COLOR_BG_CYAN	"\033[46m"
+
+/* A special value meaning "no color selected" */
+#define GIT_COLOR_NIL "NIL"
 
 /*
- * This variable stores the value of color.ui
+ * The first three are chosen to match common usage in the code, and what is
+ * returned from git_config_colorbool. The "auto" value can be returned from
+ * config_colorbool, and will be converted by want_color() into either 0 or 1.
  */
-extern int git_use_color_default;
+#define GIT_COLOR_UNKNOWN -1
+#define GIT_COLOR_NEVER  0
+#define GIT_COLOR_ALWAYS 1
+#define GIT_COLOR_AUTO   2
 
+/* A default list of colors to use for commit graphs and show-branch output */
+extern const char *column_colors_ansi[];
+extern const int column_colors_ansi_max;
 
 /*
- * Use this instead of git_default_config if you need the value of color.ui.
+ * Generally the color code will lazily figure this out itself, but
+ * this provides a mechanism for callers to override autodetection.
  */
+extern int color_stdout_is_tty;
+
+/*
+ * Use the first one if you need only color config; the second is a convenience
+ * if you are just going to change to git_default_config, too.
+ */
+int git_color_config(const char *var, const char *value, void *cb);
 int git_color_default_config(const char *var, const char *value, void *cb);
 
-int git_config_colorbool(const char *var, const char *value, int stdout_is_tty);
+int git_config_colorbool(const char *var, const char *value);
+int want_color(int var);
 void color_parse(const char *value, const char *var, char *dst);
 void color_parse_mem(const char *value, int len, const char *var, char *dst);
+__attribute__((format (printf, 3, 4)))
 int color_fprintf(FILE *fp, const char *color, const char *fmt, ...);
+__attribute__((format (printf, 3, 4)))
 int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...);
-int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf);
+void color_print_strbuf(FILE *fp, const char *color, const struct strbuf *sb);
+
+int color_is_nil(const char *color);
 
 #endif /* COLOR_H */
diff --git a/column.c b/column.c
new file mode 100644
index 0000000..9367ba5
--- /dev/null
+++ b/column.c
@@ -0,0 +1,434 @@
+#include "cache.h"
+#include "column.h"
+#include "string-list.h"
+#include "parse-options.h"
+#include "run-command.h"
+#include "utf8.h"
+
+#define XY2LINEAR(d, x, y) (COL_LAYOUT((d)->colopts) == COL_COLUMN ? \
+			    (x) * (d)->rows + (y) : \
+			    (y) * (d)->cols + (x))
+
+struct column_data {
+	const struct string_list *list;
+	unsigned int colopts;
+	struct column_options opts;
+
+	int rows, cols;
+	int *len;		/* cell length */
+	int *width;	      /* index to the longest row in column */
+};
+
+/* return length of 's' in letters, ANSI escapes stripped */
+static int item_length(unsigned int colopts, const char *s)
+{
+	int len, i = 0;
+	struct strbuf str = STRBUF_INIT;
+
+	strbuf_addstr(&str, s);
+	while ((s = strstr(str.buf + i, "\033[")) != NULL) {
+		int len = strspn(s + 2, "0123456789;");
+		i = s - str.buf;
+		strbuf_remove(&str, i, len + 3); /* \033[<len><func char> */
+	}
+	len = utf8_strwidth(str.buf);
+	strbuf_release(&str);
+	return len;
+}
+
+/*
+ * Calculate cell width, rows and cols for a table of equal cells, given
+ * table width and how many spaces between cells.
+ */
+static void layout(struct column_data *data, int *width)
+{
+	int i;
+
+	*width = 0;
+	for (i = 0; i < data->list->nr; i++)
+		if (*width < data->len[i])
+			*width = data->len[i];
+
+	*width += data->opts.padding;
+
+	data->cols = (data->opts.width - strlen(data->opts.indent)) / *width;
+	if (data->cols == 0)
+		data->cols = 1;
+
+	data->rows = DIV_ROUND_UP(data->list->nr, data->cols);
+}
+
+static void compute_column_width(struct column_data *data)
+{
+	int i, x, y;
+	for (x = 0; x < data->cols; x++) {
+		data->width[x] = XY2LINEAR(data, x, 0);
+		for (y = 0; y < data->rows; y++) {
+			i = XY2LINEAR(data, x, y);
+			if (i < data->list->nr &&
+			    data->len[data->width[x]] < data->len[i])
+				data->width[x] = i;
+		}
+	}
+}
+
+/*
+ * Shrink all columns by shortening them one row each time (and adding
+ * more columns along the way). Hopefully the longest cell will be
+ * moved to the next column, column is shrunk so we have more space
+ * for new columns. The process ends when the whole thing no longer
+ * fits in data->total_width.
+ */
+static void shrink_columns(struct column_data *data)
+{
+	data->width = xrealloc(data->width,
+			       sizeof(*data->width) * data->cols);
+	while (data->rows > 1) {
+		int x, total_width, cols, rows;
+		rows = data->rows;
+		cols = data->cols;
+
+		data->rows--;
+		data->cols = DIV_ROUND_UP(data->list->nr, data->rows);
+		if (data->cols != cols)
+			data->width = xrealloc(data->width,
+					       sizeof(*data->width) * data->cols);
+		compute_column_width(data);
+
+		total_width = strlen(data->opts.indent);
+		for (x = 0; x < data->cols; x++) {
+			total_width += data->len[data->width[x]];
+			total_width += data->opts.padding;
+		}
+		if (total_width > data->opts.width) {
+			data->rows = rows;
+			data->cols = cols;
+			break;
+		}
+	}
+	compute_column_width(data);
+}
+
+/* Display without layout when not enabled */
+static void display_plain(const struct string_list *list,
+			  const char *indent, const char *nl)
+{
+	int i;
+
+	for (i = 0; i < list->nr; i++)
+		printf("%s%s%s", indent, list->items[i].string, nl);
+}
+
+/* Print a cell to stdout with all necessary leading/traling space */
+static int display_cell(struct column_data *data, int initial_width,
+			const char *empty_cell, int x, int y)
+{
+	int i, len, newline;
+
+	i = XY2LINEAR(data, x, y);
+	if (i >= data->list->nr)
+		return -1;
+
+	len = data->len[i];
+	if (data->width && data->len[data->width[x]] < initial_width) {
+		/*
+		 * empty_cell has initial_width chars, if real column
+		 * is narrower, increase len a bit so we fill less
+		 * space.
+		 */
+		len += initial_width - data->len[data->width[x]];
+		len -= data->opts.padding;
+	}
+
+	if (COL_LAYOUT(data->colopts) == COL_COLUMN)
+		newline = i + data->rows >= data->list->nr;
+	else
+		newline = x == data->cols - 1 || i == data->list->nr - 1;
+
+	printf("%s%s%s",
+	       x == 0 ? data->opts.indent : "",
+	       data->list->items[i].string,
+	       newline ? data->opts.nl : empty_cell + len);
+	return 0;
+}
+
+/* Display COL_COLUMN or COL_ROW */
+static void display_table(const struct string_list *list,
+			  unsigned int colopts,
+			  const struct column_options *opts)
+{
+	struct column_data data;
+	int x, y, i, initial_width;
+	char *empty_cell;
+
+	memset(&data, 0, sizeof(data));
+	data.list = list;
+	data.colopts = colopts;
+	data.opts = *opts;
+
+	data.len = xmalloc(sizeof(*data.len) * list->nr);
+	for (i = 0; i < list->nr; i++)
+		data.len[i] = item_length(colopts, list->items[i].string);
+
+	layout(&data, &initial_width);
+
+	if (colopts & COL_DENSE)
+		shrink_columns(&data);
+
+	empty_cell = xmalloc(initial_width + 1);
+	memset(empty_cell, ' ', initial_width);
+	empty_cell[initial_width] = '\0';
+	for (y = 0; y < data.rows; y++) {
+		for (x = 0; x < data.cols; x++)
+			if (display_cell(&data, initial_width, empty_cell, x, y))
+				break;
+	}
+
+	free(data.len);
+	free(data.width);
+	free(empty_cell);
+}
+
+void print_columns(const struct string_list *list, unsigned int colopts,
+		   const struct column_options *opts)
+{
+	struct column_options nopts;
+
+	if (!list->nr)
+		return;
+	assert((colopts & COL_ENABLE_MASK) != COL_AUTO);
+
+	memset(&nopts, 0, sizeof(nopts));
+	nopts.indent = opts && opts->indent ? opts->indent : "";
+	nopts.nl = opts && opts->nl ? opts->nl : "\n";
+	nopts.padding = opts ? opts->padding : 1;
+	nopts.width = opts && opts->width ? opts->width : term_columns() - 1;
+	if (!column_active(colopts)) {
+		display_plain(list, "", "\n");
+		return;
+	}
+	switch (COL_LAYOUT(colopts)) {
+	case COL_PLAIN:
+		display_plain(list, nopts.indent, nopts.nl);
+		break;
+	case COL_ROW:
+	case COL_COLUMN:
+		display_table(list, colopts, &nopts);
+		break;
+	default:
+		die("BUG: invalid layout mode %d", COL_LAYOUT(colopts));
+	}
+}
+
+int finalize_colopts(unsigned int *colopts, int stdout_is_tty)
+{
+	if ((*colopts & COL_ENABLE_MASK) == COL_AUTO) {
+		if (stdout_is_tty < 0)
+			stdout_is_tty = isatty(1);
+		*colopts &= ~COL_ENABLE_MASK;
+		if (stdout_is_tty)
+			*colopts |= COL_ENABLED;
+	}
+	return 0;
+}
+
+struct colopt {
+	const char *name;
+	unsigned int value;
+	unsigned int mask;
+};
+
+#define LAYOUT_SET 1
+#define ENABLE_SET 2
+
+static int parse_option(const char *arg, int len, unsigned int *colopts,
+			int *group_set)
+{
+	struct colopt opts[] = {
+		{ "always", COL_ENABLED,  COL_ENABLE_MASK },
+		{ "never",  COL_DISABLED, COL_ENABLE_MASK },
+		{ "auto",   COL_AUTO,     COL_ENABLE_MASK },
+		{ "plain",  COL_PLAIN,    COL_LAYOUT_MASK },
+		{ "column", COL_COLUMN,   COL_LAYOUT_MASK },
+		{ "row",    COL_ROW,      COL_LAYOUT_MASK },
+		{ "dense",  COL_DENSE,    0 },
+	};
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(opts); i++) {
+		int set = 1, arg_len = len, name_len;
+		const char *arg_str = arg;
+
+		if (!opts[i].mask) {
+			if (arg_len > 2 && !strncmp(arg_str, "no", 2)) {
+				arg_str += 2;
+				arg_len -= 2;
+				set = 0;
+			}
+		}
+
+		name_len = strlen(opts[i].name);
+		if (arg_len != name_len ||
+		    strncmp(arg_str, opts[i].name, name_len))
+			continue;
+
+		switch (opts[i].mask) {
+		case COL_ENABLE_MASK:
+			*group_set |= ENABLE_SET;
+			break;
+		case COL_LAYOUT_MASK:
+			*group_set |= LAYOUT_SET;
+			break;
+		}
+
+		if (opts[i].mask)
+			*colopts = (*colopts & ~opts[i].mask) | opts[i].value;
+		else {
+			if (set)
+				*colopts |= opts[i].value;
+			else
+				*colopts &= ~opts[i].value;
+		}
+		return 0;
+	}
+
+	return error("unsupported option '%s'", arg);
+}
+
+static int parse_config(unsigned int *colopts, const char *value)
+{
+	const char *sep = " ,";
+	int group_set = 0;
+
+	while (*value) {
+		int len = strcspn(value, sep);
+		if (len) {
+			if (parse_option(value, len, colopts, &group_set))
+				return -1;
+
+			value += len;
+		}
+		value += strspn(value, sep);
+	}
+	/*
+	 * Setting layout implies "always" if neither always, never
+	 * nor auto is specified.
+	 *
+	 * Current value in COL_ENABLE_MASK is disregarded. This means if
+	 * you set column.ui = auto and pass --column=row, then "auto"
+	 * will become "always".
+	 */
+	if ((group_set & LAYOUT_SET) && !(group_set & ENABLE_SET))
+		*colopts = (*colopts & ~COL_ENABLE_MASK) | COL_ENABLED;
+	return 0;
+}
+
+static int column_config(const char *var, const char *value,
+			 const char *key, unsigned int *colopts)
+{
+	if (!value)
+		return config_error_nonbool(var);
+	if (parse_config(colopts, value))
+		return error("invalid column.%s mode %s", key, value);
+	return 0;
+}
+
+int git_column_config(const char *var, const char *value,
+		      const char *command, unsigned int *colopts)
+{
+	const char *it = skip_prefix(var, "column.");
+	if (!it)
+		return 0;
+
+	if (!strcmp(it, "ui"))
+		return column_config(var, value, "ui", colopts);
+
+	if (command && !strcmp(it, command))
+		return column_config(var, value, it, colopts);
+
+	return 0;
+}
+
+int parseopt_column_callback(const struct option *opt,
+			     const char *arg, int unset)
+{
+	unsigned int *colopts = opt->value;
+	*colopts |= COL_PARSEOPT;
+	*colopts &= ~COL_ENABLE_MASK;
+	if (unset)		/* --no-column == never */
+		return 0;
+	/* --column == always unless "arg" states otherwise */
+	*colopts |= COL_ENABLED;
+	if (arg)
+		return parse_config(colopts, arg);
+
+	return 0;
+}
+
+static int fd_out = -1;
+static struct child_process column_process;
+
+int run_column_filter(int colopts, const struct column_options *opts)
+{
+	const char *av[10];
+	int ret, ac = 0;
+	struct strbuf sb_colopt  = STRBUF_INIT;
+	struct strbuf sb_width   = STRBUF_INIT;
+	struct strbuf sb_padding = STRBUF_INIT;
+
+	if (fd_out != -1)
+		return -1;
+
+	av[ac++] = "column";
+	strbuf_addf(&sb_colopt, "--raw-mode=%d", colopts);
+	av[ac++] = sb_colopt.buf;
+	if (opts && opts->width) {
+		strbuf_addf(&sb_width, "--width=%d", opts->width);
+		av[ac++] = sb_width.buf;
+	}
+	if (opts && opts->indent) {
+		av[ac++] = "--indent";
+		av[ac++] = opts->indent;
+	}
+	if (opts && opts->padding) {
+		strbuf_addf(&sb_padding, "--padding=%d", opts->padding);
+		av[ac++] = sb_padding.buf;
+	}
+	av[ac] = NULL;
+
+	fflush(stdout);
+	memset(&column_process, 0, sizeof(column_process));
+	column_process.in = -1;
+	column_process.out = dup(1);
+	column_process.git_cmd = 1;
+	column_process.argv = av;
+
+	ret = start_command(&column_process);
+
+	strbuf_release(&sb_colopt);
+	strbuf_release(&sb_width);
+	strbuf_release(&sb_padding);
+
+	if (ret)
+		return -2;
+
+	fd_out = dup(1);
+	close(1);
+	dup2(column_process.in, 1);
+	close(column_process.in);
+	return 0;
+}
+
+int stop_column_filter(void)
+{
+	if (fd_out == -1)
+		return -1;
+
+	fflush(stdout);
+	close(1);
+	finish_command(&column_process);
+	dup2(fd_out, 1);
+	close(fd_out);
+	fd_out = -1;
+	return 0;
+}
diff --git a/column.h b/column.h
new file mode 100644
index 0000000..0a61917
--- /dev/null
+++ b/column.h
@@ -0,0 +1,45 @@
+#ifndef COLUMN_H
+#define COLUMN_H
+
+#define COL_LAYOUT_MASK   0x000F
+#define COL_ENABLE_MASK   0x0030   /* always, never or auto */
+#define COL_PARSEOPT      0x0040   /* --column is given from cmdline */
+#define COL_DENSE         0x0080   /* Shrink columns when possible,
+				      making space for more columns */
+
+#define COL_DISABLED      0x0000   /* must be zero */
+#define COL_ENABLED       0x0010
+#define COL_AUTO          0x0020
+
+#define COL_LAYOUT(c) ((c) & COL_LAYOUT_MASK)
+#define COL_COLUMN             0   /* Fill columns before rows */
+#define COL_ROW                1   /* Fill rows before columns */
+#define COL_PLAIN             15   /* one column */
+
+#define explicitly_enable_column(c) \
+	(((c) & COL_PARSEOPT) && column_active(c))
+
+struct column_options {
+	int width;
+	int padding;
+	const char *indent;
+	const char *nl;
+};
+
+struct option;
+extern int parseopt_column_callback(const struct option *, const char *, int);
+extern int git_column_config(const char *var, const char *value,
+			     const char *command, unsigned int *colopts);
+extern int finalize_colopts(unsigned int *colopts, int stdout_is_tty);
+static inline int column_active(unsigned int colopts)
+{
+	return (colopts & COL_ENABLE_MASK) == COL_ENABLED;
+}
+
+extern void print_columns(const struct string_list *list, unsigned int colopts,
+			  const struct column_options *opts);
+
+extern int run_column_filter(int colopts, const struct column_options *);
+extern int stop_column_filter(void);
+
+#endif
diff --git a/combine-diff.c b/combine-diff.c
index 5b63af1..9786680 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -7,6 +7,8 @@
 #include "xdiff-interface.h"
 #include "log-tree.h"
 #include "refs.h"
+#include "userdiff.h"
+#include "sha1-array.h"
 
 static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr, int n, int num_parent)
 {
@@ -92,7 +94,9 @@
 	unsigned long *p_lno;
 };
 
-static char *grab_blob(const unsigned char *sha1, unsigned int mode, unsigned long *size)
+static char *grab_blob(const unsigned char *sha1, unsigned int mode,
+		       unsigned long *size, struct userdiff_driver *textconv,
+		       const char *path)
 {
 	char *blob;
 	enum object_type type;
@@ -105,6 +109,11 @@
 		/* deleted blob */
 		*size = 0;
 		return xcalloc(1, 1);
+	} else if (textconv) {
+		struct diff_filespec *df = alloc_filespec(path);
+		fill_filespec(df, sha1, mode);
+		*size = fill_textconv(textconv, df, &blob);
+		free_filespec(df);
 	} else {
 		blob = read_sha1_file(sha1, &type, size);
 		if (type != OBJ_BLOB)
@@ -204,24 +213,25 @@
 static void combine_diff(const unsigned char *parent, unsigned int mode,
 			 mmfile_t *result_file,
 			 struct sline *sline, unsigned int cnt, int n,
-			 int num_parent)
+			 int num_parent, int result_deleted,
+			 struct userdiff_driver *textconv,
+			 const char *path)
 {
 	unsigned int p_lno, lno;
 	unsigned long nmask = (1UL << n);
 	xpparam_t xpp;
 	xdemitconf_t xecfg;
 	mmfile_t parent_file;
-	xdemitcb_t ecb;
 	struct combine_diff_state state;
 	unsigned long sz;
 
-	if (!cnt)
+	if (result_deleted)
 		return; /* result deleted */
 
-	parent_file.ptr = grab_blob(parent, mode, &sz);
+	parent_file.ptr = grab_blob(parent, mode, &sz, textconv, path);
 	parent_file.size = sz;
 	memset(&xpp, 0, sizeof(xpp));
-	xpp.flags = XDF_NEED_MINIMAL;
+	xpp.flags = 0;
 	memset(&xecfg, 0, sizeof(xecfg));
 	memset(&state, 0, sizeof(state));
 	state.nmask = nmask;
@@ -231,7 +241,7 @@
 	state.n = n;
 
 	xdi_diff_outf(&parent_file, result_file, consume_line, &state,
-		      &xpp, &xecfg, &ecb);
+		      &xpp, &xecfg);
 	free(parent_file.ptr);
 
 	/* Assign line numbers for this parent.
@@ -413,7 +423,7 @@
 						     hunk_begin, j);
 				la = (la + context < cnt + 1) ?
 					(la + context) : cnt + 1;
-				while (j <= --la) {
+				while (la && j <= --la) {
 					if (sline[la].flag & mark) {
 						contin = 1;
 						break;
@@ -517,19 +527,20 @@
 }
 
 static void dump_sline(struct sline *sline, unsigned long cnt, int num_parent,
-		       int use_color)
+		       int use_color, int result_deleted)
 {
 	unsigned long mark = (1UL<<num_parent);
 	unsigned long no_pre_delete = (2UL<<num_parent);
 	int i;
 	unsigned long lno = 0;
 	const char *c_frag = diff_get_color(use_color, DIFF_FRAGINFO);
+	const char *c_func = diff_get_color(use_color, DIFF_FUNCINFO);
 	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)
+	if (result_deleted)
 		return; /* result deleted */
 
 	while (1) {
@@ -588,7 +599,9 @@
 				    comment_end = i;
 			}
 			if (comment_end)
-				putchar(' ');
+				printf("%s%s %s%s", c_reset,
+						    c_plain, c_reset,
+						    c_func);
 			for (i = 0; i < comment_end; i++)
 				putchar(hunk_comment[i]);
 		}
@@ -679,27 +692,108 @@
 	puts(buf.buf);
 }
 
+static void show_combined_header(struct combine_diff_path *elem,
+				 int num_parent,
+				 int dense,
+				 struct rev_info *rev,
+				 int mode_differs,
+				 int show_file_header)
+{
+	struct diff_options *opt = &rev->diffopt;
+	int abbrev = DIFF_OPT_TST(opt, FULL_INDEX) ? 40 : DEFAULT_ABBREV;
+	const char *a_prefix = opt->a_prefix ? opt->a_prefix : "a/";
+	const char *b_prefix = opt->b_prefix ? opt->b_prefix : "b/";
+	const char *c_meta = diff_get_color_opt(opt, DIFF_METAINFO);
+	const char *c_reset = diff_get_color_opt(opt, DIFF_RESET);
+	const char *abb;
+	int added = 0;
+	int deleted = 0;
+	int i;
+
+	if (rev->loginfo && !rev->no_commit_id)
+		show_log(rev);
+
+	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%s\n", abb, c_reset);
+
+	if (mode_differs) {
+		deleted = !elem->mode;
+
+		/* We say it was added if nobody had it */
+		added = !deleted;
+		for (i = 0; added && i < num_parent; i++)
+			if (elem->parent[i].status !=
+			    DIFF_STATUS_ADDED)
+				added = 0;
+		if (added)
+			printf("%snew file mode %06o",
+			       c_meta, elem->mode);
+		else {
+			if (deleted)
+				printf("%sdeleted file ", c_meta);
+			printf("mode ");
+			for (i = 0; i < num_parent; i++) {
+				printf("%s%06o", i ? "," : "",
+				       elem->parent[i].mode);
+			}
+			if (elem->mode)
+				printf("..%06o", elem->mode);
+		}
+		printf("%s\n", c_reset);
+	}
+
+	if (!show_file_header)
+		return;
+
+	if (added)
+		dump_quoted_path("--- ", "", "/dev/null",
+				 c_meta, c_reset);
+	else
+		dump_quoted_path("--- ", a_prefix, elem->path,
+				 c_meta, c_reset);
+	if (deleted)
+		dump_quoted_path("+++ ", "", "/dev/null",
+				 c_meta, c_reset);
+	else
+		dump_quoted_path("+++ ", b_prefix, elem->path,
+				 c_meta, c_reset);
+}
+
 static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
-			    int dense, struct rev_info *rev)
+			    int dense, int working_tree_file,
+			    struct rev_info *rev)
 {
 	struct diff_options *opt = &rev->diffopt;
 	unsigned long result_size, cnt, lno;
+	int result_deleted = 0;
 	char *result, *cp;
 	struct sline *sline; /* survived lines */
 	int mode_differs = 0;
 	int i, show_hunks;
-	int working_tree_file = is_null_sha1(elem->sha1);
-	int abbrev = DIFF_OPT_TST(opt, FULL_INDEX) ? 40 : DEFAULT_ABBREV;
-	const char *a_prefix, *b_prefix;
 	mmfile_t result_file;
+	struct userdiff_driver *userdiff;
+	struct userdiff_driver *textconv = NULL;
+	int is_binary;
 
 	context = opt->context;
-	a_prefix = opt->a_prefix ? opt->a_prefix : "a/";
-	b_prefix = opt->b_prefix ? opt->b_prefix : "b/";
+	userdiff = userdiff_find_by_path(elem->path);
+	if (!userdiff)
+		userdiff = userdiff_find_by_name("default");
+	if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV))
+		textconv = userdiff_get_textconv(userdiff);
 
 	/* Read the result of merge first */
 	if (!working_tree_file)
-		result = grab_blob(elem->sha1, elem->mode, &result_size);
+		result = grab_blob(elem->sha1, elem->mode, &result_size,
+				   textconv, elem->path);
 	else {
 		/* Used by diff-tree to read from the working tree */
 		struct stat st;
@@ -722,9 +816,16 @@
 		} else if (S_ISDIR(st.st_mode)) {
 			unsigned char sha1[20];
 			if (resolve_gitlink_ref(elem->path, "HEAD", sha1) < 0)
-				result = grab_blob(elem->sha1, elem->mode, &result_size);
+				result = grab_blob(elem->sha1, elem->mode,
+						   &result_size, NULL, NULL);
 			else
-				result = grab_blob(sha1, elem->mode, &result_size);
+				result = grab_blob(sha1, elem->mode,
+						   &result_size, NULL, NULL);
+		} else if (textconv) {
+			struct diff_filespec *df = alloc_filespec(elem->path);
+			fill_filespec(df, null_sha1, st.st_mode);
+			result_size = fill_textconv(textconv, df, &result);
+			free_filespec(df);
 		} else if (0 <= (fd = open(elem->path, O_RDONLY))) {
 			size_t len = xsize_t(st.st_size);
 			ssize_t done;
@@ -764,6 +865,7 @@
 		}
 		else {
 		deleted_file:
+			result_deleted = 1;
 			result_size = 0;
 			elem->mode = 0;
 			result = xcalloc(1, 1);
@@ -773,6 +875,38 @@
 			close(fd);
 	}
 
+	for (i = 0; i < num_parent; i++) {
+		if (elem->parent[i].mode != elem->mode) {
+			mode_differs = 1;
+			break;
+		}
+	}
+
+	if (textconv)
+		is_binary = 0;
+	else if (userdiff->binary != -1)
+		is_binary = userdiff->binary;
+	else {
+		is_binary = buffer_is_binary(result, result_size);
+		for (i = 0; !is_binary && i < num_parent; i++) {
+			char *buf;
+			unsigned long size;
+			buf = grab_blob(elem->parent[i].sha1,
+					elem->parent[i].mode,
+					&size, NULL, NULL);
+			if (buffer_is_binary(buf, size))
+				is_binary = 1;
+			free(buf);
+		}
+	}
+	if (is_binary) {
+		show_combined_header(elem, num_parent, dense, rev,
+				     mode_differs, 0);
+		printf("Binary files differ\n");
+		free(result);
+		return;
+	}
+
 	for (cnt = 0, cp = result; cp < result + result_size; cp++) {
 		if (*cp == '\n')
 			cnt++;
@@ -820,73 +954,17 @@
 			combine_diff(elem->parent[i].sha1,
 				     elem->parent[i].mode,
 				     &result_file, sline,
-				     cnt, i, num_parent);
-		if (elem->parent[i].mode != elem->mode)
-			mode_differs = 1;
+				     cnt, i, num_parent, result_deleted,
+				     textconv, elem->path);
 	}
 
 	show_hunks = make_hunks(sline, cnt, num_parent, dense);
 
 	if (show_hunks || mode_differs || working_tree_file) {
-		const char *abb;
-		int use_color = DIFF_OPT_TST(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);
-		int added = 0;
-		int deleted = 0;
-
-		if (rev->loginfo && !rev->no_commit_id)
-			show_log(rev);
-		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%s\n", abb, c_reset);
-
-		if (mode_differs) {
-			deleted = !elem->mode;
-
-			/* We say it was added if nobody had it */
-			added = !deleted;
-			for (i = 0; added && i < num_parent; i++)
-				if (elem->parent[i].status !=
-				    DIFF_STATUS_ADDED)
-					added = 0;
-			if (added)
-				printf("%snew file mode %06o",
-				       c_meta, elem->mode);
-			else {
-				if (deleted)
-					printf("%sdeleted file ", c_meta);
-				printf("mode ");
-				for (i = 0; i < num_parent; i++) {
-					printf("%s%06o", i ? "," : "",
-					       elem->parent[i].mode);
-				}
-				if (elem->mode)
-					printf("..%06o", elem->mode);
-			}
-			printf("%s\n", c_reset);
-		}
-		if (added)
-			dump_quoted_path("--- ", "", "/dev/null",
-					 c_meta, c_reset);
-		else
-			dump_quoted_path("--- ", a_prefix, elem->path,
-					 c_meta, c_reset);
-		if (deleted)
-			dump_quoted_path("+++ ", "", "/dev/null",
-					 c_meta, c_reset);
-		else
-			dump_quoted_path("+++ ", b_prefix, elem->path,
-					 c_meta, c_reset);
+		show_combined_header(elem, num_parent, dense, rev,
+				     mode_differs, 1);
 		dump_sline(sline, cnt, num_parent,
-			   DIFF_OPT_TST(opt, COLOR_DIFF));
+			   opt->use_color, result_deleted);
 	}
 	free(result);
 
@@ -950,6 +1028,12 @@
 	write_name_quoted(p->path, stdout, line_termination);
 }
 
+/*
+ * The result (p->elem) is from the working tree and their
+ * parents are typically from multiple stages during a merge
+ * (i.e. diff-files) or the state in HEAD and in the index
+ * (i.e. diff-index).
+ */
 void show_combined_diff(struct combine_diff_path *p,
 		       int num_parent,
 		       int dense,
@@ -963,19 +1047,84 @@
 				  DIFF_FORMAT_NAME_STATUS))
 		show_raw_diff(p, num_parent, rev);
 	else if (opt->output_format & DIFF_FORMAT_PATCH)
-		show_patch_diff(p, num_parent, dense, rev);
+		show_patch_diff(p, num_parent, dense, 1, rev);
+}
+
+static void free_combined_pair(struct diff_filepair *pair)
+{
+	free(pair->two);
+	free(pair);
+}
+
+/*
+ * A combine_diff_path expresses N parents on the LHS against 1 merge
+ * result. Synthesize a diff_filepair that has N entries on the "one"
+ * side and 1 entry on the "two" side.
+ *
+ * In the future, we might want to add more data to combine_diff_path
+ * so that we can fill fields we are ignoring (most notably, size) here,
+ * but currently nobody uses it, so this should suffice for now.
+ */
+static struct diff_filepair *combined_pair(struct combine_diff_path *p,
+					   int num_parent)
+{
+	int i;
+	struct diff_filepair *pair;
+	struct diff_filespec *pool;
+
+	pair = xmalloc(sizeof(*pair));
+	pool = xcalloc(num_parent + 1, sizeof(struct diff_filespec));
+	pair->one = pool + 1;
+	pair->two = pool;
+
+	for (i = 0; i < num_parent; i++) {
+		pair->one[i].path = p->path;
+		pair->one[i].mode = p->parent[i].mode;
+		hashcpy(pair->one[i].sha1, p->parent[i].sha1);
+		pair->one[i].sha1_valid = !is_null_sha1(p->parent[i].sha1);
+		pair->one[i].has_more_entries = 1;
+	}
+	pair->one[num_parent - 1].has_more_entries = 0;
+
+	pair->two->path = p->path;
+	pair->two->mode = p->mode;
+	hashcpy(pair->two->sha1, p->sha1);
+	pair->two->sha1_valid = !is_null_sha1(p->sha1);
+	return pair;
+}
+
+static void handle_combined_callback(struct diff_options *opt,
+				     struct combine_diff_path *paths,
+				     int num_parent,
+				     int num_paths)
+{
+	struct combine_diff_path *p;
+	struct diff_queue_struct q;
+	int i;
+
+	q.queue = xcalloc(num_paths, sizeof(struct diff_filepair *));
+	q.alloc = num_paths;
+	q.nr = num_paths;
+	for (i = 0, p = paths; p; p = p->next) {
+		if (!p->len)
+			continue;
+		q.queue[i++] = combined_pair(p, num_parent);
+	}
+	opt->format_callback(&q, opt, opt->format_callback_data);
+	for (i = 0; i < num_paths; i++)
+		free_combined_pair(q.queue[i]);
+	free(q.queue);
 }
 
 void diff_tree_combined(const unsigned char *sha1,
-			const unsigned char parent[][20],
-			int num_parent,
+			const struct sha1_array *parents,
 			int dense,
 			struct rev_info *rev)
 {
 	struct diff_options *opt = &rev->diffopt;
 	struct diff_options diffopts;
 	struct combine_diff_path *p, *paths = NULL;
-	int i, num_paths, needsep, show_log_first;
+	int i, num_paths, needsep, show_log_first, num_parent = parents->nr;
 
 	diffopts = *opt;
 	diffopts.output_format = DIFF_FORMAT_NO_OUTPUT;
@@ -995,7 +1144,7 @@
 			diffopts.output_format = stat_opt;
 		else
 			diffopts.output_format = DIFF_FORMAT_NO_OUTPUT;
-		diff_tree_sha1(parent[i], sha1, "", &diffopts);
+		diff_tree_sha1(parents->sha1[i], sha1, "", &diffopts);
 		diffcore_std(&diffopts);
 		paths = intersect_paths(paths, i, num_parent);
 
@@ -1025,13 +1174,16 @@
 		else if (opt->output_format &
 			 (DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIFFSTAT))
 			needsep = 1;
+		else if (opt->output_format & DIFF_FORMAT_CALLBACK)
+			handle_combined_callback(opt, paths, num_parent, num_paths);
+
 		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);
+							0, rev);
 			}
 		}
 	}
@@ -1044,25 +1196,16 @@
 	}
 }
 
-void diff_tree_combined_merge(const unsigned char *sha1,
-			     int dense, struct rev_info *rev)
+void diff_tree_combined_merge(const struct commit *commit, int dense,
+			      struct rev_info *rev)
 {
-	int num_parent;
-	const unsigned char (*parent)[20];
-	struct commit *commit = lookup_commit(sha1);
-	struct commit_list *parents;
+	struct commit_list *parent = commit->parents;
+	struct sha1_array parents = SHA1_ARRAY_INIT;
 
-	/* count parents */
-	for (parents = commit->parents, num_parent = 0;
-	     parents;
-	     parents = parents->next, num_parent++)
-		; /* nothing */
-
-	parent = xmalloc(num_parent * sizeof(*parent));
-	for (parents = commit->parents, num_parent = 0;
-	     parents;
-	     parents = parents->next, num_parent++)
-		hashcpy((unsigned char *)(parent + num_parent),
-			parents->item->object.sha1);
-	diff_tree_combined(sha1, parent, num_parent, dense, rev);
+	while (parent) {
+		sha1_array_append(&parents, parent->item->object.sha1);
+		parent = parent->next;
+	}
+	diff_tree_combined(commit->object.sha1, &parents, dense, rev);
+	sha1_array_clear(&parents);
 }
diff --git a/command-list.txt b/command-list.txt
index fb03a2e..14ea67a 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -20,6 +20,7 @@
 git-citool                              mainporcelain
 git-clean                               mainporcelain
 git-clone                               mainporcelain common
+git-column                              purehelpers
 git-commit                              mainporcelain common
 git-commit-tree                         plumbingmanipulators
 git-config                              ancillarymanipulators
@@ -49,6 +50,7 @@
 git-gui                                 mainporcelain
 git-hash-object                         plumbingmanipulators
 git-help				ancillaryinterrogators
+git-http-backend                        synchingrepositories
 git-http-fetch                          synchelpers
 git-http-push                           synchelpers
 git-imap-send                           foreignscminterface
@@ -74,6 +76,8 @@
 git-mktree                              plumbingmanipulators
 git-mv                                  mainporcelain common
 git-name-rev                            plumbinginterrogators
+git-notes                               mainporcelain
+git-p4                                  foreignscminterface
 git-pack-objects                        plumbingmanipulators
 git-pack-redundant                      plumbinginterrogators
 git-pack-refs                           ancillarymanipulators
@@ -92,6 +96,7 @@
 git-relink                              ancillarymanipulators
 git-remote                              ancillarymanipulators
 git-repack                              ancillarymanipulators
+git-replace                             ancillarymanipulators
 git-repo-config                         ancillarymanipulators	deprecated
 git-request-pull                        foreignscminterface
 git-rerere                              ancillaryinterrogators
@@ -127,5 +132,6 @@
 git-var                                 plumbinginterrogators
 git-verify-pack                         plumbinginterrogators
 git-verify-tag                          ancillaryinterrogators
+gitweb                                  ancillaryinterrogators
 git-whatchanged                         ancillaryinterrogators
 git-write-tree                          plumbingmanipulators
diff --git a/commit.c b/commit.c
index e2bcbe8..42af4c1 100644
--- a/commit.c
+++ b/commit.c
@@ -5,6 +5,9 @@
 #include "utf8.h"
 #include "diff.h"
 #include "revision.h"
+#include "notes.h"
+#include "gpg-interface.h"
+#include "mergesort.h"
 
 int save_commit_buffer = 1;
 
@@ -38,6 +41,18 @@
 	return lookup_commit_reference_gently(sha1, 0);
 }
 
+struct commit *lookup_commit_or_die(const unsigned char *sha1, const char *ref_name)
+{
+	struct commit *c = lookup_commit_reference(sha1);
+	if (!c)
+		die(_("could not parse %s"), ref_name);
+	if (hashcmp(sha1, c->object.sha1)) {
+		warning(_("%s %s is not a commit!"),
+			ref_name, sha1_to_hex(sha1));
+	}
+	return c;
+}
+
 struct commit *lookup_commit(const unsigned char *sha1)
 {
 	struct object *obj = lookup_object(sha1);
@@ -48,6 +63,19 @@
 	return check_commit(obj, sha1, 0);
 }
 
+struct commit *lookup_commit_reference_by_name(const char *name)
+{
+	unsigned char sha1[20];
+	struct commit *commit;
+
+	if (get_sha1_committish(name, sha1))
+		return NULL;
+	commit = lookup_commit_reference(sha1);
+	if (!commit || parse_commit(commit))
+		return NULL;
+	return commit;
+}
+
 static unsigned long parse_commit_date(const char *buf, const char *tail)
 {
 	const char *dateptr;
@@ -132,16 +160,12 @@
 	int i;
 	struct commit_graft *graft = NULL;
 
-	if (buf[len-1] == '\n')
-		buf[--len] = 0;
+	while (len && isspace(buf[len-1]))
+		buf[--len] = '\0';
 	if (buf[0] == '#' || buf[0] == '\0')
 		return NULL;
-	if ((len + 1) % 41) {
-	bad_graft_data:
-		error("bad graft data: %s", buf);
-		free(graft);
-		return NULL;
-	}
+	if ((len + 1) % 41)
+		goto bad_graft_data;
 	i = (len + 1) / 41 - 1;
 	graft = xmalloc(sizeof(*graft) + 20 * i);
 	graft->nr_parent = i;
@@ -154,6 +178,11 @@
 			goto bad_graft_data;
 	}
 	return graft;
+
+bad_graft_data:
+	error("bad graft data: %s", buf);
+	free(graft);
+	return NULL;
 }
 
 static int read_graft_file(const char *graft_file)
@@ -199,24 +228,12 @@
 	return commit_graft[pos];
 }
 
-int write_shallow_commits(int fd, int use_pack_protocol)
+int for_each_commit_graft(each_commit_graft_fn fn, void *cb_data)
 {
-	int i, count = 0;
-	for (i = 0; i < commit_graft_nr; i++)
-		if (commit_graft[i]->nr_parent < 0) {
-			const char *hex =
-				sha1_to_hex(commit_graft[i]->sha1);
-			count++;
-			if (use_pack_protocol)
-				packet_write(fd, "shallow %s", hex);
-			else {
-				if (write_in_full(fd, hex,  40) != 40)
-					break;
-				if (write_in_full(fd, "\n", 1) != 1)
-					break;
-			}
-		}
-	return count;
+	int i, ret;
+	for (i = ret = 0; i < commit_graft_nr && !ret; i++)
+		ret = fn(commit_graft[i], cb_data);
+	return ret;
 }
 
 int unregister_shallow(const unsigned char *sha1)
@@ -225,17 +242,17 @@
 	if (pos < 0)
 		return -1;
 	if (pos + 1 < commit_graft_nr)
-		memcpy(commit_graft + pos, commit_graft + pos + 1,
+		memmove(commit_graft + pos, commit_graft + pos + 1,
 				sizeof(struct commit_graft *)
 				* (commit_graft_nr - pos - 1));
 	commit_graft_nr--;
 	return 0;
 }
 
-int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size)
+int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long size)
 {
-	char *tail = buffer;
-	char *bufptr = buffer;
+	const char *tail = buffer;
+	const char *bufptr = buffer;
 	unsigned char parent[20];
 	struct commit_list **pptr;
 	struct commit_graft *graft;
@@ -316,6 +333,25 @@
 	return ret;
 }
 
+int find_commit_subject(const char *commit_buffer, const char **subject)
+{
+	const char *eol;
+	const char *p = commit_buffer;
+
+	while (*p && (*p != '\n' || p[1] != '\n'))
+		p++;
+	if (*p) {
+		p += 2;
+		for (eol = p; *eol && *eol != '\n'; eol++)
+			; /* do nothing */
+	} else
+		eol = p;
+
+	*subject = p;
+
+	return eol - p;
+}
+
 struct commit_list *commit_list_insert(struct commit *item, struct commit_list **list_p)
 {
 	struct commit_list *new_list = xmalloc(sizeof(struct commit_list));
@@ -342,7 +378,7 @@
 	}
 }
 
-struct commit_list * insert_by_date(struct commit *item, struct commit_list **list)
+struct commit_list * commit_list_insert_by_date(struct commit *item, struct commit_list **list)
 {
 	struct commit_list **pp = list;
 	struct commit_list *p;
@@ -355,15 +391,31 @@
 	return commit_list_insert(item, pp);
 }
 
-
-void sort_by_date(struct commit_list **list)
+static int commit_list_compare_by_date(const void *a, const void *b)
 {
-	struct commit_list *ret = NULL;
-	while (*list) {
-		insert_by_date((*list)->item, &ret);
-		*list = (*list)->next;
-	}
-	*list = ret;
+	unsigned long a_date = ((const struct commit_list *)a)->item->date;
+	unsigned long b_date = ((const struct commit_list *)b)->item->date;
+	if (a_date < b_date)
+		return 1;
+	if (a_date > b_date)
+		return -1;
+	return 0;
+}
+
+static void *commit_list_get_next(const void *a)
+{
+	return ((const struct commit_list *)a)->next;
+}
+
+static void commit_list_set_next(void *a, void *next)
+{
+	((struct commit_list *)a)->next = next;
+}
+
+void commit_list_sort_by_date(struct commit_list **list)
+{
+	*list = llist_mergesort(*list, commit_list_get_next, commit_list_set_next,
+				commit_list_compare_by_date);
 }
 
 struct commit *pop_most_recent_commit(struct commit_list **list,
@@ -380,14 +432,15 @@
 		struct commit *commit = parents->item;
 		if (!parse_commit(commit) && !(commit->object.flags & mark)) {
 			commit->object.flags |= mark;
-			insert_by_date(commit, list);
+			commit_list_insert_by_date(commit, list);
 		}
 		parents = parents->next;
 	}
 	return ret;
 }
 
-void clear_commit_marks(struct commit *commit, unsigned int mark)
+static void clear_commit_marks_1(struct commit_list **plist,
+				 struct commit *commit, unsigned int mark)
 {
 	while (commit) {
 		struct commit_list *parents;
@@ -402,12 +455,34 @@
 			return;
 
 		while ((parents = parents->next))
-			clear_commit_marks(parents->item, mark);
+			commit_list_insert(parents->item, plist);
 
 		commit = commit->parents->item;
 	}
 }
 
+void clear_commit_marks(struct commit *commit, unsigned int mark)
+{
+	struct commit_list *list = NULL;
+	commit_list_insert(commit, &list);
+	while (list)
+		clear_commit_marks_1(&list, pop_commit(&list), mark);
+}
+
+void clear_commit_marks_for_object_array(struct object_array *a, unsigned mark)
+{
+	struct object *object;
+	struct commit *commit;
+	unsigned int i;
+
+	for (i = 0; i < a->nr; i++) {
+		object = a->objects[i].item;
+		commit = lookup_commit_reference_gently(object->sha1, 1);
+		if (commit)
+			clear_commit_marks(commit, mark);
+	}
+}
+
 struct commit *pop_commit(struct commit_list **stack)
 {
 	struct commit_list *top = *stack;
@@ -469,7 +544,7 @@
 
 	/* process the list in topological order */
 	if (!lifo)
-		sort_by_date(&work);
+		commit_list_sort_by_date(&work);
 
 	pptr = list;
 	*list = NULL;
@@ -483,7 +558,7 @@
 
 		commit = work_item->item;
 		for (parents = commit->parents; parents ; parents = parents->next) {
-			struct commit *parent=parents->item;
+			struct commit *parent = parents->item;
 
 			if (!parent->indegree)
 				continue;
@@ -495,7 +570,7 @@
 			 */
 			if (--parent->indegree == 1) {
 				if (!lifo)
-					insert_by_date(parent, &work);
+					commit_list_insert_by_date(parent, &work);
 				else
 					commit_list_insert(parent, &work);
 			}
@@ -555,28 +630,28 @@
 	}
 
 	one->object.flags |= PARENT1;
-	insert_by_date(one, &list);
+	commit_list_insert_by_date(one, &list);
 	for (i = 0; i < n; i++) {
 		twos[i]->object.flags |= PARENT2;
-		insert_by_date(twos[i], &list);
+		commit_list_insert_by_date(twos[i], &list);
 	}
 
 	while (interesting(list)) {
 		struct commit *commit;
 		struct commit_list *parents;
-		struct commit_list *n;
+		struct commit_list *next;
 		int flags;
 
 		commit = list->item;
-		n = list->next;
+		next = list->next;
 		free(list);
-		list = n;
+		list = next;
 
 		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);
+				commit_list_insert_by_date(commit, &result);
 			}
 			/* Mark parents of a found merge stale */
 			flags |= STALE;
@@ -590,7 +665,7 @@
 			if (parse_commit(p))
 				return NULL;
 			p->object.flags |= flags;
-			insert_by_date(p, &list);
+			commit_list_insert_by_date(p, &list);
 		}
 	}
 
@@ -598,11 +673,11 @@
 	free_commit_list(list);
 	list = result; result = NULL;
 	while (list) {
-		struct commit_list *n = list->next;
+		struct commit_list *next = list->next;
 		if (!(list->item->object.flags & STALE))
-			insert_by_date(list->item, &result);
+			commit_list_insert_by_date(list->item, &result);
 		free(list);
-		list = n;
+		list = next;
 	}
 	return result;
 }
@@ -693,7 +768,7 @@
 	result = NULL;
 	for (i = 0; i < cnt; i++) {
 		if (rslt[i])
-			insert_by_date(rslt[i], &result);
+			commit_list_insert_by_date(rslt[i], &result);
 	}
 	free(rslt);
 	return result;
@@ -791,3 +866,363 @@
 	free(other);
 	return result;
 }
+
+static const char gpg_sig_header[] = "gpgsig";
+static const int gpg_sig_header_len = sizeof(gpg_sig_header) - 1;
+
+static int do_sign_commit(struct strbuf *buf, const char *keyid)
+{
+	struct strbuf sig = STRBUF_INIT;
+	int inspos, copypos;
+
+	/* find the end of the header */
+	inspos = strstr(buf->buf, "\n\n") - buf->buf + 1;
+
+	if (!keyid || !*keyid)
+		keyid = get_signing_key();
+	if (sign_buffer(buf, &sig, keyid)) {
+		strbuf_release(&sig);
+		return -1;
+	}
+
+	for (copypos = 0; sig.buf[copypos]; ) {
+		const char *bol = sig.buf + copypos;
+		const char *eol = strchrnul(bol, '\n');
+		int len = (eol - bol) + !!*eol;
+
+		if (!copypos) {
+			strbuf_insert(buf, inspos, gpg_sig_header, gpg_sig_header_len);
+			inspos += gpg_sig_header_len;
+		}
+		strbuf_insert(buf, inspos++, " ", 1);
+		strbuf_insert(buf, inspos, bol, len);
+		inspos += len;
+		copypos += len;
+	}
+	strbuf_release(&sig);
+	return 0;
+}
+
+int parse_signed_commit(const unsigned char *sha1,
+			struct strbuf *payload, struct strbuf *signature)
+{
+	unsigned long size;
+	enum object_type type;
+	char *buffer = read_sha1_file(sha1, &type, &size);
+	int in_signature, saw_signature = -1;
+	char *line, *tail;
+
+	if (!buffer || type != OBJ_COMMIT)
+		goto cleanup;
+
+	line = buffer;
+	tail = buffer + size;
+	in_signature = 0;
+	saw_signature = 0;
+	while (line < tail) {
+		const char *sig = NULL;
+		char *next = memchr(line, '\n', tail - line);
+
+		next = next ? next + 1 : tail;
+		if (in_signature && line[0] == ' ')
+			sig = line + 1;
+		else if (!prefixcmp(line, gpg_sig_header) &&
+			 line[gpg_sig_header_len] == ' ')
+			sig = line + gpg_sig_header_len + 1;
+		if (sig) {
+			strbuf_add(signature, sig, next - sig);
+			saw_signature = 1;
+			in_signature = 1;
+		} else {
+			if (*line == '\n')
+				/* dump the whole remainder of the buffer */
+				next = tail;
+			strbuf_add(payload, line, next - line);
+			in_signature = 0;
+		}
+		line = next;
+	}
+ cleanup:
+	free(buffer);
+	return saw_signature;
+}
+
+static void handle_signed_tag(struct commit *parent, struct commit_extra_header ***tail)
+{
+	struct merge_remote_desc *desc;
+	struct commit_extra_header *mergetag;
+	char *buf;
+	unsigned long size, len;
+	enum object_type type;
+
+	desc = merge_remote_util(parent);
+	if (!desc || !desc->obj)
+		return;
+	buf = read_sha1_file(desc->obj->sha1, &type, &size);
+	if (!buf || type != OBJ_TAG)
+		goto free_return;
+	len = parse_signature(buf, size);
+	if (size == len)
+		goto free_return;
+	/*
+	 * We could verify this signature and either omit the tag when
+	 * it does not validate, but the integrator may not have the
+	 * public key of the signer of the tag he is merging, while a
+	 * later auditor may have it while auditing, so let's not run
+	 * verify-signed-buffer here for now...
+	 *
+	 * if (verify_signed_buffer(buf, len, buf + len, size - len, ...))
+	 *	warn("warning: signed tag unverified.");
+	 */
+	mergetag = xcalloc(1, sizeof(*mergetag));
+	mergetag->key = xstrdup("mergetag");
+	mergetag->value = buf;
+	mergetag->len = size;
+
+	**tail = mergetag;
+	*tail = &mergetag->next;
+	return;
+
+free_return:
+	free(buf);
+}
+
+void append_merge_tag_headers(struct commit_list *parents,
+			      struct commit_extra_header ***tail)
+{
+	while (parents) {
+		struct commit *parent = parents->item;
+		handle_signed_tag(parent, tail);
+		parents = parents->next;
+	}
+}
+
+static void add_extra_header(struct strbuf *buffer,
+			     struct commit_extra_header *extra)
+{
+	strbuf_addstr(buffer, extra->key);
+	if (extra->len)
+		strbuf_add_lines(buffer, " ", extra->value, extra->len);
+	else
+		strbuf_addch(buffer, '\n');
+}
+
+struct commit_extra_header *read_commit_extra_headers(struct commit *commit,
+						      const char **exclude)
+{
+	struct commit_extra_header *extra = NULL;
+	unsigned long size;
+	enum object_type type;
+	char *buffer = read_sha1_file(commit->object.sha1, &type, &size);
+	if (buffer && type == OBJ_COMMIT)
+		extra = read_commit_extra_header_lines(buffer, size, exclude);
+	free(buffer);
+	return extra;
+}
+
+static inline int standard_header_field(const char *field, size_t len)
+{
+	return ((len == 4 && !memcmp(field, "tree ", 5)) ||
+		(len == 6 && !memcmp(field, "parent ", 7)) ||
+		(len == 6 && !memcmp(field, "author ", 7)) ||
+		(len == 9 && !memcmp(field, "committer ", 10)) ||
+		(len == 8 && !memcmp(field, "encoding ", 9)));
+}
+
+static int excluded_header_field(const char *field, size_t len, const char **exclude)
+{
+	if (!exclude)
+		return 0;
+
+	while (*exclude) {
+		size_t xlen = strlen(*exclude);
+		if (len == xlen &&
+		    !memcmp(field, *exclude, xlen) && field[xlen] == ' ')
+			return 1;
+		exclude++;
+	}
+	return 0;
+}
+
+struct commit_extra_header *read_commit_extra_header_lines(const char *buffer, size_t size,
+							   const char **exclude)
+{
+	struct commit_extra_header *extra = NULL, **tail = &extra, *it = NULL;
+	const char *line, *next, *eof, *eob;
+	struct strbuf buf = STRBUF_INIT;
+
+	for (line = buffer, eob = line + size;
+	     line < eob && *line != '\n';
+	     line = next) {
+		next = memchr(line, '\n', eob - line);
+		next = next ? next + 1 : eob;
+		if (*line == ' ') {
+			/* continuation */
+			if (it)
+				strbuf_add(&buf, line + 1, next - (line + 1));
+			continue;
+		}
+		if (it)
+			it->value = strbuf_detach(&buf, &it->len);
+		strbuf_reset(&buf);
+		it = NULL;
+
+		eof = strchr(line, ' ');
+		if (next <= eof)
+			eof = next;
+
+		if (standard_header_field(line, eof - line) ||
+		    excluded_header_field(line, eof - line, exclude))
+			continue;
+
+		it = xcalloc(1, sizeof(*it));
+		it->key = xmemdupz(line, eof-line);
+		*tail = it;
+		tail = &it->next;
+		if (eof + 1 < next)
+			strbuf_add(&buf, eof + 1, next - (eof + 1));
+	}
+	if (it)
+		it->value = strbuf_detach(&buf, &it->len);
+	return extra;
+}
+
+void free_commit_extra_headers(struct commit_extra_header *extra)
+{
+	while (extra) {
+		struct commit_extra_header *next = extra->next;
+		free(extra->key);
+		free(extra->value);
+		free(extra);
+		extra = next;
+	}
+}
+
+int commit_tree(const struct strbuf *msg, unsigned char *tree,
+		struct commit_list *parents, unsigned char *ret,
+		const char *author, const char *sign_commit)
+{
+	struct commit_extra_header *extra = NULL, **tail = &extra;
+	int result;
+
+	append_merge_tag_headers(parents, &tail);
+	result = commit_tree_extended(msg, tree, parents, ret,
+				      author, sign_commit, extra);
+	free_commit_extra_headers(extra);
+	return result;
+}
+
+static const char commit_utf8_warn[] =
+"Warning: commit message does not conform to UTF-8.\n"
+"You may want to amend it after fixing the message, or set the config\n"
+"variable i18n.commitencoding to the encoding your project uses.\n";
+
+int commit_tree_extended(const struct strbuf *msg, unsigned char *tree,
+			 struct commit_list *parents, unsigned char *ret,
+			 const char *author, const char *sign_commit,
+			 struct commit_extra_header *extra)
+{
+	int result;
+	int encoding_is_utf8;
+	struct strbuf buffer;
+
+	assert_sha1_type(tree, OBJ_TREE);
+
+	if (memchr(msg->buf, '\0', msg->len))
+		return error("a NUL byte in commit log message not allowed.");
+
+	/* Not having i18n.commitencoding is the same as having utf-8 */
+	encoding_is_utf8 = is_encoding_utf8(git_commit_encoding);
+
+	strbuf_init(&buffer, 8192); /* should avoid reallocs for the headers */
+	strbuf_addf(&buffer, "tree %s\n", sha1_to_hex(tree));
+
+	/*
+	 * NOTE! This ordering means that the same exact tree merged with a
+	 * different order of parents will be a _different_ changeset even
+	 * if everything else stays the same.
+	 */
+	while (parents) {
+		struct commit_list *next = parents->next;
+		struct commit *parent = parents->item;
+
+		strbuf_addf(&buffer, "parent %s\n",
+			    sha1_to_hex(parent->object.sha1));
+		free(parents);
+		parents = next;
+	}
+
+	/* Person/date information */
+	if (!author)
+		author = git_author_info(IDENT_STRICT);
+	strbuf_addf(&buffer, "author %s\n", author);
+	strbuf_addf(&buffer, "committer %s\n", git_committer_info(IDENT_STRICT));
+	if (!encoding_is_utf8)
+		strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding);
+
+	while (extra) {
+		add_extra_header(&buffer, extra);
+		extra = extra->next;
+	}
+	strbuf_addch(&buffer, '\n');
+
+	/* And add the comment */
+	strbuf_addbuf(&buffer, msg);
+
+	/* And check the encoding */
+	if (encoding_is_utf8 && !is_utf8(buffer.buf))
+		fprintf(stderr, commit_utf8_warn);
+
+	if (sign_commit && do_sign_commit(&buffer, sign_commit))
+		return -1;
+
+	result = write_sha1_file(buffer.buf, buffer.len, commit_type, ret);
+	strbuf_release(&buffer);
+	return result;
+}
+
+struct commit *get_merge_parent(const char *name)
+{
+	struct object *obj;
+	struct commit *commit;
+	unsigned char sha1[20];
+	if (get_sha1(name, sha1))
+		return NULL;
+	obj = parse_object(sha1);
+	commit = (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT);
+	if (commit && !commit->util) {
+		struct merge_remote_desc *desc;
+		desc = xmalloc(sizeof(*desc));
+		desc->obj = obj;
+		desc->name = strdup(name);
+		commit->util = desc;
+	}
+	return commit;
+}
+
+/*
+ * Append a commit to the end of the commit_list.
+ *
+ * next starts by pointing to the variable that holds the head of an
+ * empty commit_list, and is updated to point to the "next" field of
+ * the last item on the list as new commits are appended.
+ *
+ * Usage example:
+ *
+ *     struct commit_list *list;
+ *     struct commit_list **next = &list;
+ *
+ *     next = commit_list_append(c1, next);
+ *     next = commit_list_append(c2, next);
+ *     assert(commit_list_count(list) == 2);
+ *     return list;
+ */
+struct commit_list **commit_list_append(struct commit *commit,
+					struct commit_list **next)
+{
+	struct commit_list *new = xmalloc(sizeof(struct commit_list));
+	new->item = commit;
+	*next = new;
+	new->next = NULL;
+	return &new->next;
+}
diff --git a/commit.h b/commit.h
index ba9f638..d617fa3 100644
--- a/commit.h
+++ b/commit.h
@@ -28,6 +28,7 @@
 extern struct decoration name_decoration;
 struct name_decoration {
 	struct name_decoration *next;
+	int type;
 	char name[1];
 };
 
@@ -35,19 +36,32 @@
 struct commit *lookup_commit_reference(const unsigned char *sha1);
 struct commit *lookup_commit_reference_gently(const unsigned char *sha1,
 					      int quiet);
+struct commit *lookup_commit_reference_by_name(const char *name);
 
-int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size);
+/*
+ * Look up object named by "sha1", dereference tag as necessary,
+ * get a commit and return it. If "sha1" does not dereference to
+ * a commit, use ref_name to report an error and die.
+ */
+struct commit *lookup_commit_or_die(const unsigned char *sha1, const char *ref_name);
 
+int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long size);
 int parse_commit(struct commit *item);
 
-struct commit_list * commit_list_insert(struct commit *item, struct commit_list **list_p);
+/* Find beginning and length of commit subject. */
+int find_commit_subject(const char *commit_buffer, const char **subject);
+
+struct commit_list *commit_list_insert(struct commit *item,
+					struct commit_list **list);
+struct commit_list **commit_list_append(struct commit *commit,
+					struct commit_list **next);
 unsigned commit_list_count(const struct commit_list *l);
-struct commit_list * insert_by_date(struct commit *item, struct commit_list **list);
+struct commit_list *commit_list_insert_by_date(struct commit *item,
+				    struct commit_list **list);
+void commit_list_sort_by_date(struct commit_list **list);
 
 void free_commit_list(struct commit_list *list);
 
-void sort_by_date(struct commit_list **list);
-
 /* Commit formats */
 enum cmit_fmt {
 	CMIT_FMT_RAW,
@@ -60,33 +74,54 @@
 	CMIT_FMT_EMAIL,
 	CMIT_FMT_USERFORMAT,
 
-	CMIT_FMT_UNSPECIFIED,
+	CMIT_FMT_UNSPECIFIED
 };
 
-extern int non_ascii(int);
+struct pretty_print_context {
+	enum cmit_fmt fmt;
+	int abbrev;
+	const char *subject;
+	const char *after_subject;
+	int preserve_subject;
+	enum date_mode date_mode;
+	unsigned date_mode_explicit:1;
+	int need_8bit_cte;
+	int show_notes;
+	struct reflog_walk_info *reflog_info;
+	const char *output_encoding;
+};
+
+struct userformat_want {
+	unsigned notes:1;
+};
+
+extern int has_non_ascii(const char *text);
 struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */
+extern char *logmsg_reencode(const struct commit *commit,
+			     const char *output_encoding);
 extern char *reencode_commit_message(const struct commit *commit,
 				     const char **encoding_p);
 extern void get_commit_format(const char *arg, struct rev_info *);
+extern const char *format_subject(struct strbuf *sb, const char *msg,
+				  const char *line_separator);
+extern void userformat_find_requirements(const char *fmt, struct userformat_want *w);
 extern void format_commit_message(const struct commit *commit,
-				  const void *format, struct strbuf *sb,
-				  enum date_mode dmode);
-extern void pretty_print_commit(enum cmit_fmt fmt, const struct commit*,
-                                struct strbuf *,
-                                int abbrev, const char *subject,
-                                const char *after_subject, enum date_mode,
-				int need_8bit_cte);
-void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
-		   const char *line, enum date_mode dmode,
-		   const char *encoding);
-void pp_title_line(enum cmit_fmt fmt,
+				  const char *format, struct strbuf *sb,
+				  const struct pretty_print_context *context);
+extern void pretty_print_commit(const struct pretty_print_context *pp,
+				const struct commit *commit,
+				struct strbuf *sb);
+extern void pp_commit_easy(enum cmit_fmt fmt, const struct commit *commit,
+			   struct strbuf *sb);
+void pp_user_info(const struct pretty_print_context *pp,
+		  const char *what, struct strbuf *sb,
+		  const char *line, const char *encoding);
+void pp_title_line(const struct pretty_print_context *pp,
 		   const char **msg_p,
 		   struct strbuf *sb,
-		   const char *subject,
-		   const char *after_subject,
 		   const char *encoding,
 		   int need_8bit_cte);
-void pp_remainder(enum cmit_fmt fmt,
+void pp_remainder(const struct pretty_print_context *pp,
 		  const char **msg_p,
 		  struct strbuf *sb,
 		  int indent);
@@ -101,6 +136,7 @@
 struct commit *pop_commit(struct commit_list **stack);
 
 void clear_commit_marks(struct commit *commit, unsigned int mark);
+void clear_commit_marks_for_object_array(struct object_array *a, unsigned mark);
 
 /*
  * Performs an in-place topological sort of list supplied.
@@ -117,6 +153,7 @@
 	int nr_parent; /* < 0 if shallow commit */
 	unsigned char parent[FLEX_ARRAY][20]; /* more */
 };
+typedef int (*each_commit_graft_fn)(const struct commit_graft *, void *);
 
 struct commit_graft *read_graft_line(char *buf, int len);
 int register_commit_graft(struct commit_graft *, int);
@@ -128,7 +165,7 @@
 
 extern int register_shallow(const unsigned char *sha1);
 extern int unregister_shallow(const unsigned char *sha1);
-extern int write_shallow_commits(int fd, int use_pack_protocol);
+extern int for_each_commit_graft(each_commit_graft_fn, void *);
 extern int is_repository_shallow(void);
 extern struct commit_list *get_shallow_commits(struct object_array *heads,
 		int depth, int shallow_flag, int not_shallow_flag);
@@ -136,7 +173,9 @@
 int is_descendant_of(struct commit *, struct commit_list *);
 int in_merge_bases(struct commit *, struct commit **, int);
 
-extern int interactive_add(int argc, const char **argv, const char *prefix);
+extern int interactive_add(int argc, const char **argv, const char *prefix, int patch);
+extern int run_add_interactive(const char *revision, const char *patch_mode,
+			       const char **pathspec);
 
 static inline int single_parent(struct commit *commit)
 {
@@ -145,4 +184,43 @@
 
 struct commit_list *reduce_heads(struct commit_list *heads);
 
+struct commit_extra_header {
+	struct commit_extra_header *next;
+	char *key;
+	char *value;
+	size_t len;
+};
+
+extern void append_merge_tag_headers(struct commit_list *parents,
+				     struct commit_extra_header ***tail);
+
+extern int commit_tree(const struct strbuf *msg, unsigned char *tree,
+		       struct commit_list *parents, unsigned char *ret,
+		       const char *author, const char *sign_commit);
+
+extern int commit_tree_extended(const struct strbuf *msg, unsigned char *tree,
+				struct commit_list *parents, unsigned char *ret,
+				const char *author, const char *sign_commit,
+				struct commit_extra_header *);
+
+extern struct commit_extra_header *read_commit_extra_headers(struct commit *, const char **);
+extern struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
+
+extern void free_commit_extra_headers(struct commit_extra_header *extra);
+
+struct merge_remote_desc {
+	struct object *obj; /* the named object, could be a tag */
+	const char *name;
+};
+#define merge_remote_util(commit) ((struct merge_remote_desc *)((commit)->util))
+
+/*
+ * Given "name" from the command line to merge, find the commit object
+ * and return it, while storing merge_remote_desc in its ->util field,
+ * to allow callers to tell if we are told to merge a tag.
+ */
+struct commit *get_merge_parent(const char *name);
+
+extern int parse_signed_commit(const unsigned char *sha1,
+			       struct strbuf *message, struct strbuf *signature);
 #endif /* COMMIT_H */
diff --git a/compat/bswap.h b/compat/bswap.h
index 7246a12..5061214 100644
--- a/compat/bswap.h
+++ b/compat/bswap.h
@@ -9,7 +9,7 @@
  * Default version that the compiler ought to optimize properly with
  * constant values.
  */
-static inline unsigned int default_swab32(unsigned int val)
+static inline uint32_t default_swab32(uint32_t val)
 {
 	return (((val & 0xff000000) >> 24) |
 		((val & 0x00ff0000) >>  8) |
@@ -17,16 +17,30 @@
 		((val & 0x000000ff) << 24));
 }
 
+#undef bswap32
+
 #if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
 
-#define bswap32(x) ({ \
-	unsigned int __res; \
-	if (__builtin_constant_p(x)) { \
-		__res = default_swab32(x); \
-	} else { \
-		__asm__("bswap %0" : "=r" (__res) : "0" (x)); \
-	} \
-	__res; })
+#define bswap32 git_bswap32
+static inline uint32_t git_bswap32(uint32_t x)
+{
+	uint32_t result;
+	if (__builtin_constant_p(x))
+		result = default_swab32(x);
+	else
+		__asm__("bswap %0" : "=r" (result) : "0" (x));
+	return result;
+}
+
+#elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
+
+#include <stdlib.h>
+
+#define bswap32(x) _byteswap_ulong(x)
+
+#endif
+
+#ifdef bswap32
 
 #undef ntohl
 #undef htonl
diff --git a/compat/cygwin.c b/compat/cygwin.c
index b4a51b9..dfe9b30 100644
--- a/compat/cygwin.c
+++ b/compat/cygwin.c
@@ -101,7 +101,7 @@
  * and calling git_default_config() from here would break such variables.
  */
 static int native_stat = 1;
-static int core_filemode;
+static int core_filemode = 1; /* matches trust_executable_bit default */
 
 static int git_cygwin_config(const char *var, const char *value, void *cb)
 {
@@ -114,8 +114,7 @@
 
 static int init_stat(void)
 {
-	if (have_git_dir()) {
-		git_config(git_cygwin_config, NULL);
+	if (have_git_dir() && git_config(git_cygwin_config,NULL)) {
 		if (!core_filemode && native_stat) {
 			cygwin_stat_fn = cygwin_stat;
 			cygwin_lstat_fn = cygwin_lstat;
diff --git a/compat/fnmatch/fnmatch.c b/compat/fnmatch/fnmatch.c
index 14feac7..9473aed 100644
--- a/compat/fnmatch/fnmatch.c
+++ b/compat/fnmatch/fnmatch.c
@@ -127,6 +127,10 @@
 extern int errno;
 # endif
 
+# ifndef NULL
+#  define NULL 0
+# endif
+
 /* This function doesn't exist on most systems.  */
 
 # if !defined HAVE___STRCHRNUL && !defined _LIBC
diff --git a/compat/inet_ntop.c b/compat/inet_ntop.c
index f444982..90b7cc4 100644
--- a/compat/inet_ntop.c
+++ b/compat/inet_ntop.c
@@ -15,13 +15,7 @@
  * SOFTWARE.
  */
 
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <stdio.h>
-#include <string.h>
+#include "../git-compat-util.h"
 
 #ifndef NS_INADDRSZ
 #define NS_INADDRSZ	4
@@ -50,10 +44,7 @@
  *	Paul Vixie, 1996.
  */
 static const char *
-inet_ntop4(src, dst, size)
-	const u_char *src;
-	char *dst;
-	size_t size;
+inet_ntop4(const u_char *src, char *dst, size_t size)
 {
 	static const char fmt[] = "%u.%u.%u.%u";
 	char tmp[sizeof "255.255.255.255"];
@@ -78,10 +69,7 @@
  *	Paul Vixie, 1996.
  */
 static const char *
-inet_ntop6(src, dst, size)
-	const u_char *src;
-	char *dst;
-	size_t size;
+inet_ntop6(const u_char *src, char *dst, size_t size)
 {
 	/*
 	 * Note that int32_t and int16_t need only be "at least" large enough
@@ -104,7 +92,9 @@
 	for (i = 0; i < NS_IN6ADDRSZ; i++)
 		words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
 	best.base = -1;
+	best.len = 0;
 	cur.base = -1;
+	cur.len = 0;
 	for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
 		if (words[i] == 0) {
 			if (cur.base == -1)
@@ -178,11 +168,7 @@
  *	Paul Vixie, 1996.
  */
 const char *
-inet_ntop(af, src, dst, size)
-	int af;
-	const void *src;
-	char *dst;
-	size_t size;
+inet_ntop(int af, const void *src, char *dst, size_t size)
 {
 	switch (af) {
 	case AF_INET:
diff --git a/compat/inet_pton.c b/compat/inet_pton.c
index 4078fc0..2b9a0a4 100644
--- a/compat/inet_pton.c
+++ b/compat/inet_pton.c
@@ -15,13 +15,7 @@
  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <stdio.h>
-#include <string.h>
+#include "../git-compat-util.h"
 
 #ifndef NS_INT16SZ
 #define NS_INT16SZ       2
@@ -41,7 +35,9 @@
  */
 
 static int inet_pton4(const char *src, unsigned char *dst);
+#ifndef NO_IPV6
 static int inet_pton6(const char *src, unsigned char *dst);
+#endif
 
 /* int
  * inet_pton4(src, dst)
diff --git a/compat/mingw.c b/compat/mingw.c
index bed4178..afc892d 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -2,10 +2,11 @@
 #include "win32.h"
 #include <conio.h>
 #include "../strbuf.h"
+#include "../run-command.h"
 
-unsigned int _CRT_fmode = _O_BINARY;
+static const int delay[] = { 0, 1, 10, 20, 40 };
 
-static int err_win_to_posix(DWORD winerr)
+int err_win_to_posix(DWORD winerr)
 {
 	int error = ENOSYS;
 	switch(winerr) {
@@ -118,18 +119,181 @@
 	return error;
 }
 
+static inline int is_file_in_use_error(DWORD errcode)
+{
+	switch (errcode) {
+	case ERROR_SHARING_VIOLATION:
+	case ERROR_ACCESS_DENIED:
+		return 1;
+	}
+
+	return 0;
+}
+
+static int read_yes_no_answer(void)
+{
+	char answer[1024];
+
+	if (fgets(answer, sizeof(answer), stdin)) {
+		size_t answer_len = strlen(answer);
+		int got_full_line = 0, c;
+
+		/* remove the newline */
+		if (answer_len >= 2 && answer[answer_len-2] == '\r') {
+			answer[answer_len-2] = '\0';
+			got_full_line = 1;
+		} else if (answer_len >= 1 && answer[answer_len-1] == '\n') {
+			answer[answer_len-1] = '\0';
+			got_full_line = 1;
+		}
+		/* flush the buffer in case we did not get the full line */
+		if (!got_full_line)
+			while ((c = getchar()) != EOF && c != '\n')
+				;
+	} else
+		/* we could not read, return the
+		 * default answer which is no */
+		return 0;
+
+	if (tolower(answer[0]) == 'y' && !answer[1])
+		return 1;
+	if (!strncasecmp(answer, "yes", sizeof(answer)))
+		return 1;
+	if (tolower(answer[0]) == 'n' && !answer[1])
+		return 0;
+	if (!strncasecmp(answer, "no", sizeof(answer)))
+		return 0;
+
+	/* did not find an answer we understand */
+	return -1;
+}
+
+static int ask_yes_no_if_possible(const char *format, ...)
+{
+	char question[4096];
+	const char *retry_hook[] = { NULL, NULL, NULL };
+	va_list args;
+
+	va_start(args, format);
+	vsnprintf(question, sizeof(question), format, args);
+	va_end(args);
+
+	if ((retry_hook[0] = mingw_getenv("GIT_ASK_YESNO"))) {
+		retry_hook[1] = question;
+		return !run_command_v_opt(retry_hook, 0);
+	}
+
+	if (!isatty(_fileno(stdin)) || !isatty(_fileno(stderr)))
+		return 0;
+
+	while (1) {
+		int answer;
+		fprintf(stderr, "%s (y/n) ", question);
+
+		if ((answer = read_yes_no_answer()) >= 0)
+			return answer;
+
+		fprintf(stderr, "Sorry, I did not understand your answer. "
+				"Please type 'y' or 'n'\n");
+	}
+}
+
+#undef unlink
+int mingw_unlink(const char *pathname)
+{
+	int ret, tries = 0;
+
+	/* read-only files cannot be removed */
+	chmod(pathname, 0666);
+	while ((ret = unlink(pathname)) == -1 && tries < ARRAY_SIZE(delay)) {
+		if (!is_file_in_use_error(GetLastError()))
+			break;
+		/*
+		 * We assume that some other process had the source or
+		 * destination file open at the wrong moment and retry.
+		 * In order to give the other process a higher chance to
+		 * complete its operation, we give up our time slice now.
+		 * If we have to retry again, we do sleep a bit.
+		 */
+		Sleep(delay[tries]);
+		tries++;
+	}
+	while (ret == -1 && is_file_in_use_error(GetLastError()) &&
+	       ask_yes_no_if_possible("Unlink of file '%s' failed. "
+			"Should I try again?", pathname))
+	       ret = unlink(pathname);
+	return ret;
+}
+
+static int is_dir_empty(const char *path)
+{
+	struct strbuf buf = STRBUF_INIT;
+	WIN32_FIND_DATAA findbuf;
+	HANDLE handle;
+
+	strbuf_addf(&buf, "%s\\*", path);
+	handle = FindFirstFileA(buf.buf, &findbuf);
+	if (handle == INVALID_HANDLE_VALUE) {
+		strbuf_release(&buf);
+		return GetLastError() == ERROR_NO_MORE_FILES;
+	}
+
+	while (!strcmp(findbuf.cFileName, ".") ||
+			!strcmp(findbuf.cFileName, ".."))
+		if (!FindNextFile(handle, &findbuf)) {
+			strbuf_release(&buf);
+			return GetLastError() == ERROR_NO_MORE_FILES;
+		}
+	FindClose(handle);
+	strbuf_release(&buf);
+	return 0;
+}
+
+#undef rmdir
+int mingw_rmdir(const char *pathname)
+{
+	int ret, tries = 0;
+
+	while ((ret = rmdir(pathname)) == -1 && tries < ARRAY_SIZE(delay)) {
+		if (!is_file_in_use_error(GetLastError()))
+			break;
+		if (!is_dir_empty(pathname)) {
+			errno = ENOTEMPTY;
+			break;
+		}
+		/*
+		 * We assume that some other process had the source or
+		 * destination file open at the wrong moment and retry.
+		 * In order to give the other process a higher chance to
+		 * complete its operation, we give up our time slice now.
+		 * If we have to retry again, we do sleep a bit.
+		 */
+		Sleep(delay[tries]);
+		tries++;
+	}
+	while (ret == -1 && is_file_in_use_error(GetLastError()) &&
+	       ask_yes_no_if_possible("Deletion of directory '%s' failed. "
+			"Should I try again?", pathname))
+	       ret = rmdir(pathname);
+	return ret;
+}
+
 #undef open
 int mingw_open (const char *filename, int oflags, ...)
 {
 	va_list args;
 	unsigned mode;
+	int fd;
+
 	va_start(args, oflags);
 	mode = va_arg(args, int);
 	va_end(args);
 
-	if (!strcmp(filename, "/dev/null"))
+	if (filename && !strcmp(filename, "/dev/null"))
 		filename = "nul";
-	int fd = open(filename, oflags, mode);
+
+	fd = open(filename, oflags, mode);
+
 	if (fd < 0 && (oflags & O_CREAT) && errno == EACCES) {
 		DWORD attrs = GetFileAttributes(filename);
 		if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))
@@ -138,23 +302,68 @@
 	return fd;
 }
 
-static inline time_t filetime_to_time_t(const FILETIME *ft)
+#undef write
+ssize_t mingw_write(int fd, const void *buf, size_t count)
+{
+	/*
+	 * While write() calls to a file on a local disk are translated
+	 * into WriteFile() calls with a maximum size of 64KB on Windows
+	 * XP and 256KB on Vista, no such cap is placed on writes to
+	 * files over the network on Windows XP.  Unfortunately, there
+	 * seems to be a limit of 32MB-28KB on X64 and 64MB-32KB on x86;
+	 * bigger writes fail on Windows XP.
+	 * So we cap to a nice 31MB here to avoid write failures over
+	 * the net without changing the number of WriteFile() calls in
+	 * the local case.
+	 */
+	return write(fd, buf, min(count, 31 * 1024 * 1024));
+}
+
+#undef fopen
+FILE *mingw_fopen (const char *filename, const char *otype)
+{
+	if (filename && !strcmp(filename, "/dev/null"))
+		filename = "nul";
+	return fopen(filename, otype);
+}
+
+#undef freopen
+FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
+{
+	if (filename && !strcmp(filename, "/dev/null"))
+		filename = "nul";
+	return freopen(filename, otype, stream);
+}
+
+/*
+ * The unit of FILETIME is 100-nanoseconds since January 1, 1601, UTC.
+ * Returns the 100-nanoseconds ("hekto nanoseconds") since the epoch.
+ */
+static inline long long filetime_to_hnsec(const FILETIME *ft)
 {
 	long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime;
-	winTime -= 116444736000000000LL; /* Windows to Unix Epoch conversion */
-	winTime /= 10000000;		 /* Nano to seconds resolution */
-	return (time_t)winTime;
+	/* Windows to Unix Epoch conversion */
+	return winTime - 116444736000000000LL;
+}
+
+static inline time_t filetime_to_time_t(const FILETIME *ft)
+{
+	return (time_t)(filetime_to_hnsec(ft) / 10000000);
 }
 
 /* We keep the do_lstat code in a separate function to avoid recursion.
  * When a path ends with a slash, the stat will fail with ENOENT. In
  * this case, we strip the trailing slashes and stat again.
+ *
+ * If follow is true then act like stat() and report on the link
+ * target. Otherwise report on the link itself.
  */
-static int do_lstat(const char *file_name, struct stat *buf)
+static int do_lstat(int follow, const char *file_name, struct stat *buf)
 {
+	int err;
 	WIN32_FILE_ATTRIBUTE_DATA fdata;
 
-	if (!(errno = get_file_attr(file_name, &fdata))) {
+	if (!(err = get_file_attr(file_name, &fdata))) {
 		buf->st_ino = 0;
 		buf->st_gid = 0;
 		buf->st_uid = 0;
@@ -166,8 +375,28 @@
 		buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
 		buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
 		buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
+		if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
+			WIN32_FIND_DATAA findbuf;
+			HANDLE handle = FindFirstFileA(file_name, &findbuf);
+			if (handle != INVALID_HANDLE_VALUE) {
+				if ((findbuf.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
+						(findbuf.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) {
+					if (follow) {
+						char buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+						buf->st_size = readlink(file_name, buffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
+					} else {
+						buf->st_mode = S_IFLNK;
+					}
+					buf->st_mode |= S_IREAD;
+					if (!(findbuf.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
+						buf->st_mode |= S_IWRITE;
+				}
+				FindClose(handle);
+			}
+		}
 		return 0;
 	}
+	errno = err;
 	return -1;
 }
 
@@ -177,12 +406,12 @@
  * complete. Note that Git stat()s are redirected to mingw_lstat()
  * too, since Windows doesn't really handle symlinks that well.
  */
-int mingw_lstat(const char *file_name, struct stat *buf)
+static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
 {
 	int namelen;
 	static char alt_name[PATH_MAX];
 
-	if (!do_lstat(file_name, buf))
+	if (!do_lstat(follow, file_name, buf))
 		return 0;
 
 	/* if file_name ended in a '/', Windows returned ENOENT;
@@ -201,7 +430,16 @@
 
 	memcpy(alt_name, file_name, namelen);
 	alt_name[namelen] = 0;
-	return do_lstat(alt_name, buf);
+	return do_lstat(follow, alt_name, buf);
+}
+
+int mingw_lstat(const char *file_name, struct stat *buf)
+{
+	return do_stat_internal(0, file_name, buf);
+}
+int mingw_stat(const char *file_name, struct stat *buf)
+{
+	return do_stat_internal(1, file_name, buf);
 }
 
 #undef fstat
@@ -249,17 +487,38 @@
 	int fh, rc;
 
 	/* must have write permission */
-	if ((fh = open(file_name, O_RDWR | O_BINARY)) < 0)
-		return -1;
+	DWORD attrs = GetFileAttributes(file_name);
+	if (attrs != INVALID_FILE_ATTRIBUTES &&
+	    (attrs & FILE_ATTRIBUTE_READONLY)) {
+		/* ignore errors here; open() will report them */
+		SetFileAttributes(file_name, attrs & ~FILE_ATTRIBUTE_READONLY);
+	}
 
-	time_t_to_filetime(times->modtime, &mft);
-	time_t_to_filetime(times->actime, &aft);
+	if ((fh = open(file_name, O_RDWR | O_BINARY)) < 0) {
+		rc = -1;
+		goto revert_attrs;
+	}
+
+	if (times) {
+		time_t_to_filetime(times->modtime, &mft);
+		time_t_to_filetime(times->actime, &aft);
+	} else {
+		GetSystemTimeAsFileTime(&mft);
+		aft = mft;
+	}
 	if (!SetFileTime((HANDLE)_get_osfhandle(fh), NULL, &aft, &mft)) {
 		errno = EINVAL;
 		rc = -1;
 	} else
 		rc = 0;
 	close(fh);
+
+revert_attrs:
+	if (attrs != INVALID_FILE_ATTRIBUTES &&
+	    (attrs & FILE_ATTRIBUTE_READONLY)) {
+		/* ignore errors again */
+		SetFileAttributes(file_name, attrs);
+	}
 	return rc;
 }
 
@@ -279,129 +538,37 @@
 
 int gettimeofday(struct timeval *tv, void *tz)
 {
-	SYSTEMTIME st;
-	struct tm tm;
-	GetSystemTime(&st);
-	tm.tm_year = st.wYear-1900;
-	tm.tm_mon = st.wMonth-1;
-	tm.tm_mday = st.wDay;
-	tm.tm_hour = st.wHour;
-	tm.tm_min = st.wMinute;
-	tm.tm_sec = st.wSecond;
-	tv->tv_sec = tm_to_time_t(&tm);
-	if (tv->tv_sec < 0)
-		return -1;
-	tv->tv_usec = st.wMilliseconds*1000;
+	FILETIME ft;
+	long long hnsec;
+
+	GetSystemTimeAsFileTime(&ft);
+	hnsec = filetime_to_hnsec(&ft);
+	tv->tv_sec = hnsec / 10000000;
+	tv->tv_usec = (hnsec % 10000000) / 10;
 	return 0;
 }
 
 int pipe(int filedes[2])
 {
-	int fd;
-	HANDLE h[2], parent;
+	HANDLE h[2];
 
-	if (_pipe(filedes, 8192, 0) < 0)
-		return -1;
-
-	parent = GetCurrentProcess();
-
-	if (!DuplicateHandle (parent, (HANDLE)_get_osfhandle(filedes[0]),
-			parent, &h[0], 0, FALSE, DUPLICATE_SAME_ACCESS)) {
-		close(filedes[0]);
-		close(filedes[1]);
+	/* this creates non-inheritable handles */
+	if (!CreatePipe(&h[0], &h[1], NULL, 8192)) {
+		errno = err_win_to_posix(GetLastError());
 		return -1;
 	}
-	if (!DuplicateHandle (parent, (HANDLE)_get_osfhandle(filedes[1]),
-			parent, &h[1], 0, FALSE, DUPLICATE_SAME_ACCESS)) {
-		close(filedes[0]);
-		close(filedes[1]);
-		CloseHandle(h[0]);
-		return -1;
-	}
-	fd = _open_osfhandle((int)h[0], O_NOINHERIT);
-	if (fd < 0) {
-		close(filedes[0]);
-		close(filedes[1]);
+	filedes[0] = _open_osfhandle((int)h[0], O_NOINHERIT);
+	if (filedes[0] < 0) {
 		CloseHandle(h[0]);
 		CloseHandle(h[1]);
 		return -1;
 	}
-	close(filedes[0]);
-	filedes[0] = fd;
-	fd = _open_osfhandle((int)h[1], O_NOINHERIT);
-	if (fd < 0) {
+	filedes[1] = _open_osfhandle((int)h[1], O_NOINHERIT);
+	if (filedes[0] < 0) {
 		close(filedes[0]);
-		close(filedes[1]);
 		CloseHandle(h[1]);
 		return -1;
 	}
-	close(filedes[1]);
-	filedes[1] = fd;
-	return 0;
-}
-
-int poll(struct pollfd *ufds, unsigned int nfds, int timeout)
-{
-	int i, pending;
-
-	if (timeout >= 0) {
-		if (nfds == 0) {
-			Sleep(timeout);
-			return 0;
-		}
-		return errno = EINVAL, error("poll timeout not supported");
-	}
-
-	/* When there is only one fd to wait for, then we pretend that
-	 * input is available and let the actual wait happen when the
-	 * caller invokes read().
-	 */
-	if (nfds == 1) {
-		if (!(ufds[0].events & POLLIN))
-			return errno = EINVAL, error("POLLIN not set");
-		ufds[0].revents = POLLIN;
-		return 0;
-	}
-
-repeat:
-	pending = 0;
-	for (i = 0; i < nfds; i++) {
-		DWORD avail = 0;
-		HANDLE h = (HANDLE) _get_osfhandle(ufds[i].fd);
-		if (h == INVALID_HANDLE_VALUE)
-			return -1;	/* errno was set */
-
-		if (!(ufds[i].events & POLLIN))
-			return errno = EINVAL, error("POLLIN not set");
-
-		/* this emulation works only for pipes */
-		if (!PeekNamedPipe(h, NULL, 0, NULL, &avail, NULL)) {
-			int err = GetLastError();
-			if (err == ERROR_BROKEN_PIPE) {
-				ufds[i].revents = POLLHUP;
-				pending++;
-			} else {
-				errno = EINVAL;
-				return error("PeekNamedPipe failed,"
-					" GetLastError: %u", err);
-			}
-		} else if (avail) {
-			ufds[i].revents = POLLIN;
-			pending++;
-		} else
-			ufds[i].revents = 0;
-	}
-	if (!pending) {
-		/* The only times that we spin here is when the process
-		 * that is connected through the pipes is waiting for
-		 * its own input data to become available. But since
-		 * the process (pack-objects) is itself CPU intensive,
-		 * it will happily pick up the time slice that we are
-		 * relinquishing here.
-		 */
-		Sleep(0);
-		goto repeat;
-	}
 	return 0;
 }
 
@@ -432,19 +599,6 @@
 	return ret;
 }
 
-#undef getenv
-char *mingw_getenv(const char *name)
-{
-	char *result = getenv(name);
-	if (!result && !strcmp(name, "TMPDIR")) {
-		/* on Windows it is TMP and TEMP */
-		result = getenv("TMP");
-		if (!result)
-			result = getenv("TEMP");
-	}
-	return result;
-}
-
 /*
  * See http://msdn2.microsoft.com/en-us/library/17w5ykft(vs.71).aspx
  * (Parsing C++ Command-Line Arguments)
@@ -544,7 +698,7 @@
  */
 static char **get_path_split(void)
 {
-	char *p, **path, *envpath = getenv("PATH");
+	char *p, **path, *envpath = mingw_getenv("PATH");
 	int i, n = 0;
 
 	if (!envpath || !*envpath)
@@ -580,10 +734,11 @@
 
 static void free_path_split(char **path)
 {
+	char **p = path;
+
 	if (!path)
 		return;
 
-	char **p = path;
 	while (*p)
 		free(*p++);
 	free(path);
@@ -608,7 +763,7 @@
 }
 
 /*
- * Determines the absolute path of cmd using the the split path in path.
+ * Determines the absolute path of cmd using the split path in path.
  * If cmd contains a slash or backslash, no lookup is performed.
  */
 static char *path_lookup(const char *cmd, char **path, int exe_only)
@@ -633,8 +788,17 @@
 	return strcasecmp(*ea, *eb);
 }
 
-static pid_t mingw_spawnve(const char *cmd, const char **argv, char **env,
-			   int prepend_cmd)
+struct pinfo_t {
+	struct pinfo_t *next;
+	pid_t pid;
+	HANDLE proc;
+} pinfo_t;
+struct pinfo_t *pinfo = NULL;
+CRITICAL_SECTION pinfo_cs;
+
+static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env,
+			      const char *dir,
+			      int prepend_cmd, int fhin, int fhout, int fherr)
 {
 	STARTUPINFO si;
 	PROCESS_INFORMATION pi;
@@ -670,9 +834,9 @@
 	memset(&si, 0, sizeof(si));
 	si.cb = sizeof(si);
 	si.dwFlags = STARTF_USESTDHANDLES;
-	si.hStdInput = (HANDLE) _get_osfhandle(0);
-	si.hStdOutput = (HANDLE) _get_osfhandle(1);
-	si.hStdError = (HANDLE) _get_osfhandle(2);
+	si.hStdInput = (HANDLE) _get_osfhandle(fhin);
+	si.hStdOutput = (HANDLE) _get_osfhandle(fhout);
+	si.hStdError = (HANDLE) _get_osfhandle(fherr);
 
 	/* concatenate argv, quoting args as we go */
 	strbuf_init(&args, 0);
@@ -713,7 +877,7 @@
 
 	memset(&pi, 0, sizeof(pi));
 	ret = CreateProcess(cmd, args.buf, NULL, NULL, TRUE, flags,
-		env ? envblk.buf : NULL, NULL, &si, &pi);
+		env ? envblk.buf : NULL, dir, &si, &pi);
 
 	if (env)
 		strbuf_release(&envblk);
@@ -724,10 +888,37 @@
 		return -1;
 	}
 	CloseHandle(pi.hThread);
-	return (pid_t)pi.hProcess;
+
+	/*
+	 * The process ID is the human-readable identifier of the process
+	 * that we want to present in log and error messages. The handle
+	 * is not useful for this purpose. But we cannot close it, either,
+	 * because it is not possible to turn a process ID into a process
+	 * handle after the process terminated.
+	 * Keep the handle in a list for waitpid.
+	 */
+	EnterCriticalSection(&pinfo_cs);
+	{
+		struct pinfo_t *info = xmalloc(sizeof(struct pinfo_t));
+		info->pid = pi.dwProcessId;
+		info->proc = pi.hProcess;
+		info->next = pinfo;
+		pinfo = info;
+	}
+	LeaveCriticalSection(&pinfo_cs);
+
+	return (pid_t)pi.dwProcessId;
 }
 
-pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env)
+static pid_t mingw_spawnve(const char *cmd, const char **argv, char **env,
+			   int prepend_cmd)
+{
+	return mingw_spawnve_fd(cmd, argv, env, NULL, prepend_cmd, 0, 1, 2);
+}
+
+pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env,
+		     const char *dir,
+		     int fhin, int fhout, int fherr)
 {
 	pid_t pid;
 	char **path = get_path_split();
@@ -749,13 +940,15 @@
 				pid = -1;
 			}
 			else {
-				pid = mingw_spawnve(iprog, argv, env, 1);
+				pid = mingw_spawnve_fd(iprog, argv, env, dir, 1,
+						       fhin, fhout, fherr);
 				free(iprog);
 			}
 			argv[0] = argv0;
 		}
 		else
-			pid = mingw_spawnve(prog, argv, env, 0);
+			pid = mingw_spawnve_fd(prog, argv, env, dir, 0,
+					       fhin, fhout, fherr);
 		free(prog);
 	}
 	free_path_split(path);
@@ -810,7 +1003,7 @@
 	}
 }
 
-void mingw_execvp(const char *cmd, char *const *argv)
+int mingw_execvp(const char *cmd, char *const *argv)
 {
 	char **path = get_path_split();
 	char *prog = path_lookup(cmd, path, 0);
@@ -822,9 +1015,35 @@
 		errno = ENOENT;
 
 	free_path_split(path);
+	return -1;
 }
 
-char **copy_environ()
+int mingw_execv(const char *cmd, char *const *argv)
+{
+	mingw_execve(cmd, argv, environ);
+	return -1;
+}
+
+int mingw_kill(pid_t pid, int sig)
+{
+	if (pid > 0 && sig == SIGTERM) {
+		HANDLE h = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
+
+		if (TerminateProcess(h, -1)) {
+			CloseHandle(h);
+			return 0;
+		}
+
+		errno = err_win_to_posix(GetLastError());
+		CloseHandle(h);
+		return -1;
+	}
+
+	errno = EINVAL;
+	return -1;
+}
+
+static char **copy_environ(void)
 {
 	char **env;
 	int i = 0;
@@ -861,7 +1080,7 @@
 /*
  * If name contains '=', then sets the variable, otherwise it unsets it
  */
-char **env_setenv(char **env, const char *name)
+static char **env_setenv(char **env, const char *name)
 {
 	char *eq = strchrnul(name, '=');
 	int i = lookup_env(env, name, eq-name);
@@ -886,23 +1105,265 @@
 	return env;
 }
 
-/* this is the first function to call into WS_32; initialize it */
-#undef gethostbyname
-struct hostent *mingw_gethostbyname(const char *host)
+/*
+ * Copies global environ and adjusts variables as specified by vars.
+ */
+char **make_augmented_environ(const char *const *vars)
+{
+	char **env = copy_environ();
+
+	while (*vars)
+		env = env_setenv(env, *vars++);
+	return env;
+}
+
+#undef getenv
+
+/*
+ * The system's getenv looks up the name in a case-insensitive manner.
+ * This version tries a case-sensitive lookup and falls back to
+ * case-insensitive if nothing was found.  This is necessary because,
+ * as a prominent example, CMD sets 'Path', but not 'PATH'.
+ * Warning: not thread-safe.
+ */
+static char *getenv_cs(const char *name)
+{
+	size_t len = strlen(name);
+	int i = lookup_env(environ, name, len);
+	if (i >= 0)
+		return environ[i] + len + 1;	/* skip past name and '=' */
+	return getenv(name);
+}
+
+char *mingw_getenv(const char *name)
+{
+	char *result = getenv_cs(name);
+	if (!result && !strcmp(name, "TMPDIR")) {
+		/* on Windows it is TMP and TEMP */
+		result = getenv_cs("TMP");
+		if (!result)
+			result = getenv_cs("TEMP");
+	}
+	return result;
+}
+
+/*
+ * Note, this isn't a complete replacement for getaddrinfo. It assumes
+ * that service contains a numerical port, or that it is null. It
+ * does a simple search using gethostbyname, and returns one IPv4 host
+ * if one was found.
+ */
+static int WSAAPI getaddrinfo_stub(const char *node, const char *service,
+				   const struct addrinfo *hints,
+				   struct addrinfo **res)
+{
+	struct hostent *h = NULL;
+	struct addrinfo *ai;
+	struct sockaddr_in *sin;
+
+	if (node) {
+		h = gethostbyname(node);
+		if (!h)
+			return WSAGetLastError();
+	}
+
+	ai = xmalloc(sizeof(struct addrinfo));
+	*res = ai;
+	ai->ai_flags = 0;
+	ai->ai_family = AF_INET;
+	ai->ai_socktype = hints ? hints->ai_socktype : 0;
+	switch (ai->ai_socktype) {
+	case SOCK_STREAM:
+		ai->ai_protocol = IPPROTO_TCP;
+		break;
+	case SOCK_DGRAM:
+		ai->ai_protocol = IPPROTO_UDP;
+		break;
+	default:
+		ai->ai_protocol = 0;
+		break;
+	}
+	ai->ai_addrlen = sizeof(struct sockaddr_in);
+	if (hints && (hints->ai_flags & AI_CANONNAME))
+		ai->ai_canonname = h ? xstrdup(h->h_name) : NULL;
+	else
+		ai->ai_canonname = NULL;
+
+	sin = xmalloc(ai->ai_addrlen);
+	memset(sin, 0, ai->ai_addrlen);
+	sin->sin_family = AF_INET;
+	/* Note: getaddrinfo is supposed to allow service to be a string,
+	 * which should be looked up using getservbyname. This is
+	 * currently not implemented */
+	if (service)
+		sin->sin_port = htons(atoi(service));
+	if (h)
+		sin->sin_addr = *(struct in_addr *)h->h_addr;
+	else if (hints && (hints->ai_flags & AI_PASSIVE))
+		sin->sin_addr.s_addr = INADDR_ANY;
+	else
+		sin->sin_addr.s_addr = INADDR_LOOPBACK;
+	ai->ai_addr = (struct sockaddr *)sin;
+	ai->ai_next = 0;
+	return 0;
+}
+
+static void WSAAPI freeaddrinfo_stub(struct addrinfo *res)
+{
+	free(res->ai_canonname);
+	free(res->ai_addr);
+	free(res);
+}
+
+static int WSAAPI getnameinfo_stub(const struct sockaddr *sa, socklen_t salen,
+				   char *host, DWORD hostlen,
+				   char *serv, DWORD servlen, int flags)
+{
+	const struct sockaddr_in *sin = (const struct sockaddr_in *)sa;
+	if (sa->sa_family != AF_INET)
+		return EAI_FAMILY;
+	if (!host && !serv)
+		return EAI_NONAME;
+
+	if (host && hostlen > 0) {
+		struct hostent *ent = NULL;
+		if (!(flags & NI_NUMERICHOST))
+			ent = gethostbyaddr((const char *)&sin->sin_addr,
+					    sizeof(sin->sin_addr), AF_INET);
+
+		if (ent)
+			snprintf(host, hostlen, "%s", ent->h_name);
+		else if (flags & NI_NAMEREQD)
+			return EAI_NONAME;
+		else
+			snprintf(host, hostlen, "%s", inet_ntoa(sin->sin_addr));
+	}
+
+	if (serv && servlen > 0) {
+		struct servent *ent = NULL;
+		if (!(flags & NI_NUMERICSERV))
+			ent = getservbyport(sin->sin_port,
+					    flags & NI_DGRAM ? "udp" : "tcp");
+
+		if (ent)
+			snprintf(serv, servlen, "%s", ent->s_name);
+		else
+			snprintf(serv, servlen, "%d", ntohs(sin->sin_port));
+	}
+
+	return 0;
+}
+
+static HMODULE ipv6_dll = NULL;
+static void (WSAAPI *ipv6_freeaddrinfo)(struct addrinfo *res);
+static int (WSAAPI *ipv6_getaddrinfo)(const char *node, const char *service,
+				      const struct addrinfo *hints,
+				      struct addrinfo **res);
+static int (WSAAPI *ipv6_getnameinfo)(const struct sockaddr *sa, socklen_t salen,
+				      char *host, DWORD hostlen,
+				      char *serv, DWORD servlen, int flags);
+/*
+ * gai_strerror is an inline function in the ws2tcpip.h header, so we
+ * don't need to try to load that one dynamically.
+ */
+
+static void socket_cleanup(void)
+{
+	WSACleanup();
+	if (ipv6_dll)
+		FreeLibrary(ipv6_dll);
+	ipv6_dll = NULL;
+	ipv6_freeaddrinfo = freeaddrinfo_stub;
+	ipv6_getaddrinfo = getaddrinfo_stub;
+	ipv6_getnameinfo = getnameinfo_stub;
+}
+
+static void ensure_socket_initialization(void)
 {
 	WSADATA wsa;
+	static int initialized = 0;
+	const char *libraries[] = { "ws2_32.dll", "wship6.dll", NULL };
+	const char **name;
+
+	if (initialized)
+		return;
 
 	if (WSAStartup(MAKEWORD(2,2), &wsa))
 		die("unable to initialize winsock subsystem, error %d",
 			WSAGetLastError());
-	atexit((void(*)(void)) WSACleanup);
+
+	for (name = libraries; *name; name++) {
+		ipv6_dll = LoadLibrary(*name);
+		if (!ipv6_dll)
+			continue;
+
+		ipv6_freeaddrinfo = (void (WSAAPI *)(struct addrinfo *))
+			GetProcAddress(ipv6_dll, "freeaddrinfo");
+		ipv6_getaddrinfo = (int (WSAAPI *)(const char *, const char *,
+						   const struct addrinfo *,
+						   struct addrinfo **))
+			GetProcAddress(ipv6_dll, "getaddrinfo");
+		ipv6_getnameinfo = (int (WSAAPI *)(const struct sockaddr *,
+						   socklen_t, char *, DWORD,
+						   char *, DWORD, int))
+			GetProcAddress(ipv6_dll, "getnameinfo");
+		if (!ipv6_freeaddrinfo || !ipv6_getaddrinfo || !ipv6_getnameinfo) {
+			FreeLibrary(ipv6_dll);
+			ipv6_dll = NULL;
+		} else
+			break;
+	}
+	if (!ipv6_freeaddrinfo || !ipv6_getaddrinfo || !ipv6_getnameinfo) {
+		ipv6_freeaddrinfo = freeaddrinfo_stub;
+		ipv6_getaddrinfo = getaddrinfo_stub;
+		ipv6_getnameinfo = getnameinfo_stub;
+	}
+
+	atexit(socket_cleanup);
+	initialized = 1;
+}
+
+#undef gethostname
+int mingw_gethostname(char *name, int namelen)
+{
+    ensure_socket_initialization();
+    return gethostname(name, namelen);
+}
+
+#undef gethostbyname
+struct hostent *mingw_gethostbyname(const char *host)
+{
+	ensure_socket_initialization();
 	return gethostbyname(host);
 }
 
+void mingw_freeaddrinfo(struct addrinfo *res)
+{
+	ipv6_freeaddrinfo(res);
+}
+
+int mingw_getaddrinfo(const char *node, const char *service,
+		      const struct addrinfo *hints, struct addrinfo **res)
+{
+	ensure_socket_initialization();
+	return ipv6_getaddrinfo(node, service, hints, res);
+}
+
+int mingw_getnameinfo(const struct sockaddr *sa, socklen_t salen,
+		      char *host, DWORD hostlen, char *serv, DWORD servlen,
+		      int flags)
+{
+	ensure_socket_initialization();
+	return ipv6_getnameinfo(sa, salen, host, hostlen, serv, servlen, flags);
+}
+
 int mingw_socket(int domain, int type, int protocol)
 {
 	int sockfd;
-	SOCKET s = WSASocket(domain, type, protocol, NULL, 0, 0);
+	SOCKET s;
+
+	ensure_socket_initialization();
+	s = WSASocket(domain, type, protocol, NULL, 0, 0);
 	if (s == INVALID_SOCKET) {
 		/*
 		 * WSAGetLastError() values are regular BSD error codes
@@ -932,12 +1393,57 @@
 	return connect(s, sa, sz);
 }
 
+#undef bind
+int mingw_bind(int sockfd, struct sockaddr *sa, size_t sz)
+{
+	SOCKET s = (SOCKET)_get_osfhandle(sockfd);
+	return bind(s, sa, sz);
+}
+
+#undef setsockopt
+int mingw_setsockopt(int sockfd, int lvl, int optname, void *optval, int optlen)
+{
+	SOCKET s = (SOCKET)_get_osfhandle(sockfd);
+	return setsockopt(s, lvl, optname, (const char*)optval, optlen);
+}
+
+#undef shutdown
+int mingw_shutdown(int sockfd, int how)
+{
+	SOCKET s = (SOCKET)_get_osfhandle(sockfd);
+	return shutdown(s, how);
+}
+
+#undef listen
+int mingw_listen(int sockfd, int backlog)
+{
+	SOCKET s = (SOCKET)_get_osfhandle(sockfd);
+	return listen(s, backlog);
+}
+
+#undef accept
+int mingw_accept(int sockfd1, struct sockaddr *sa, socklen_t *sz)
+{
+	int sockfd2;
+
+	SOCKET s1 = (SOCKET)_get_osfhandle(sockfd1);
+	SOCKET s2 = accept(s1, sa, sz);
+
+	/* convert into a file descriptor */
+	if ((sockfd2 = _open_osfhandle(s2, O_RDWR|O_BINARY)) < 0) {
+		int err = errno;
+		closesocket(s2);
+		return error("unable to make a socket file descriptor: %s",
+			strerror(err));
+	}
+	return sockfd2;
+}
+
 #undef rename
 int mingw_rename(const char *pold, const char *pnew)
 {
 	DWORD attrs, gle;
 	int tries = 0;
-	static const int delay[] = { 0, 1, 10, 20, 40 };
 
 	/*
 	 * Try native rename() first to get errno right.
@@ -979,10 +1485,27 @@
 		tries++;
 		goto repeat;
 	}
+	if (gle == ERROR_ACCESS_DENIED &&
+	       ask_yes_no_if_possible("Rename from '%s' to '%s' failed. "
+		       "Should I try again?", pold, pnew))
+		goto repeat;
+
 	errno = EACCES;
 	return -1;
 }
 
+/*
+ * Note that this doesn't return the actual pagesize, but
+ * the allocation granularity. If future Windows specific git code
+ * needs the real getpagesize function, we need to find another solution.
+ */
+int mingw_getpagesize(void)
+{
+	SYSTEM_INFO si;
+	GetSystemInfo(&si);
+	return si.dwAllocationGranularity;
+}
+
 struct passwd *getpwuid(int uid)
 {
 	static char user_name[100];
@@ -1012,7 +1535,7 @@
  * length to call the signal handler.
  */
 
-static __stdcall unsigned ticktack(void *dummy)
+static unsigned __stdcall ticktack(void *dummy)
 {
 	while (WaitForSingleObject(timer_event, timer_interval) == WAIT_TIMEOUT) {
 		if (timer_fn == SIG_DFL)
@@ -1108,9 +1631,9 @@
 #undef signal
 sig_handler_t mingw_signal(int sig, sig_handler_t handler)
 {
+	sig_handler_t old = timer_fn;
 	if (sig != SIGALRM)
 		return signal(sig, handler);
-	sig_handler_t old = timer_fn;
 	timer_fn = handler;
 	return old;
 }
@@ -1133,13 +1656,31 @@
 void mingw_open_html(const char *unixpath)
 {
 	const char *htmlpath = make_backslash_path(unixpath);
+	typedef HINSTANCE (WINAPI *T)(HWND, const char *,
+			const char *, const char *, const char *, INT);
+	T ShellExecute;
+	HMODULE shell32;
+	int r;
+
+	shell32 = LoadLibrary("shell32.dll");
+	if (!shell32)
+		die("cannot load shell32.dll");
+	ShellExecute = (T)GetProcAddress(shell32, "ShellExecuteA");
+	if (!ShellExecute)
+		die("cannot run browser");
+
 	printf("Launching default browser to display HTML ...\n");
-	ShellExecute(NULL, "open", htmlpath, NULL, "\\", 0);
+	r = (int)ShellExecute(NULL, "open", htmlpath, NULL, "\\", SW_SHOWNORMAL);
+	FreeLibrary(shell32);
+	/* see the MSDN documentation referring to the result codes here */
+	if (r <= 32) {
+		die("failed to launch browser for %.*s", MAX_PATH, unixpath);
+	}
 }
 
 int link(const char *oldpath, const char *newpath)
 {
-	typedef BOOL WINAPI (*T)(const char*, const char*, LPSECURITY_ATTRIBUTES);
+	typedef BOOL (WINAPI *T)(const char*, const char*, LPSECURITY_ATTRIBUTES);
 	static T create_hard_link = NULL;
 	if (!create_hard_link) {
 		create_hard_link = (T) GetProcAddress(
@@ -1173,61 +1714,54 @@
 	return strbuf_detach(&buf, NULL);
 }
 
-#ifndef NO_MINGW_REPLACE_READDIR
-/* MinGW readdir implementation to avoid extra lstats for Git */
-struct mingw_DIR
+pid_t waitpid(pid_t pid, int *status, int options)
 {
-	struct _finddata_t	dd_dta;		/* disk transfer area for this dir */
-	struct mingw_dirent	dd_dir;		/* Our own implementation, including d_type */
-	long			dd_handle;	/* _findnext handle */
-	int			dd_stat; 	/* 0 = next entry to read is first entry, -1 = off the end, positive = 0 based index of next entry */
-	char			dd_name[1]; 	/* given path for dir with search pattern (struct is extended) */
-};
-
-struct dirent *mingw_readdir(DIR *dir)
-{
-	WIN32_FIND_DATAA buf;
-	HANDLE handle;
-	struct mingw_DIR *mdir = (struct mingw_DIR*)dir;
-
-	if (!dir->dd_handle) {
-		errno = EBADF; /* No set_errno for mingw */
-		return NULL;
+	HANDLE h = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION,
+	    FALSE, pid);
+	if (!h) {
+		errno = ECHILD;
+		return -1;
 	}
 
-	if (dir->dd_handle == (long)INVALID_HANDLE_VALUE && dir->dd_stat == 0)
-	{
-		handle = FindFirstFileA(dir->dd_name, &buf);
-		DWORD lasterr = GetLastError();
-		dir->dd_handle = (long)handle;
-		if (handle == INVALID_HANDLE_VALUE && (lasterr != ERROR_NO_MORE_FILES)) {
-			errno = err_win_to_posix(lasterr);
-			return NULL;
+	if (pid > 0 && options & WNOHANG) {
+		if (WAIT_OBJECT_0 != WaitForSingleObject(h, 0)) {
+			CloseHandle(h);
+			return 0;
 		}
-	} else if (dir->dd_handle == (long)INVALID_HANDLE_VALUE) {
-		return NULL;
-	} else if (!FindNextFileA((HANDLE)dir->dd_handle, &buf)) {
-		DWORD lasterr = GetLastError();
-		FindClose((HANDLE)dir->dd_handle);
-		dir->dd_handle = (long)INVALID_HANDLE_VALUE;
-		/* POSIX says you shouldn't set errno when readdir can't
-		   find any more files; so, if another error we leave it set. */
-		if (lasterr != ERROR_NO_MORE_FILES)
-			errno = err_win_to_posix(lasterr);
-		return NULL;
+		options &= ~WNOHANG;
 	}
 
-	/* We get here if `buf' contains valid data.  */
-	strcpy(dir->dd_dir.d_name, buf.cFileName);
-	++dir->dd_stat;
+	if (options == 0) {
+		struct pinfo_t **ppinfo;
+		if (WaitForSingleObject(h, INFINITE) != WAIT_OBJECT_0) {
+			CloseHandle(h);
+			return 0;
+		}
 
-	/* Set file type, based on WIN32_FIND_DATA */
-	mdir->dd_dir.d_type = 0;
-	if (buf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
-		mdir->dd_dir.d_type |= DT_DIR;
-	else
-		mdir->dd_dir.d_type |= DT_REG;
+		if (status)
+			GetExitCodeProcess(h, (LPDWORD)status);
 
-	return (struct dirent*)&dir->dd_dir;
+		EnterCriticalSection(&pinfo_cs);
+
+		ppinfo = &pinfo;
+		while (*ppinfo) {
+			struct pinfo_t *info = *ppinfo;
+			if (info->pid == pid) {
+				CloseHandle(info->proc);
+				*ppinfo = info->next;
+				free(info);
+				break;
+			}
+			ppinfo = &info->next;
+		}
+
+		LeaveCriticalSection(&pinfo_cs);
+
+		CloseHandle(h);
+		return pid;
+	}
+	CloseHandle(h);
+
+	errno = EINVAL;
+	return -1;
 }
-#endif // !NO_MINGW_REPLACE_READDIR
diff --git a/compat/mingw.h b/compat/mingw.h
index c1859c5..61a6521 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -1,25 +1,39 @@
 #include <winsock2.h>
+#include <ws2tcpip.h>
 
 /*
  * things that are not available in header files
  */
 
 typedef int pid_t;
+typedef int uid_t;
+typedef int socklen_t;
 #define hstrerror strerror
 
 #define S_IFLNK    0120000 /* Symbolic link */
 #define S_ISLNK(x) (((x) & S_IFMT) == S_IFLNK)
 #define S_ISSOCK(x) 0
+
 #define S_IRGRP 0
 #define S_IWGRP 0
 #define S_IXGRP 0
-#define S_ISGID 0
+#define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
 #define S_IROTH 0
+#define S_IWOTH 0
 #define S_IXOTH 0
+#define S_IRWXO (S_IROTH | S_IWOTH | S_IXOTH)
 
-#define WIFEXITED(x) ((unsigned)(x) < 259)	/* STILL_ACTIVE */
+#define S_ISUID 0004000
+#define S_ISGID 0002000
+#define S_ISVTX 0001000
+
+#define WIFEXITED(x) 1
+#define WIFSIGNALED(x) 0
 #define WEXITSTATUS(x) ((x) & 0xff)
-#define WIFSIGNALED(x) ((unsigned)(x) > 259)
+#define WTERMSIG(x) SIGTERM
+
+#define EWOULDBLOCK EAGAIN
+#define SHUT_WR SD_SEND
 
 #define SIGHUP 1
 #define SIGQUIT 3
@@ -32,6 +46,9 @@
 #define F_SETFD 2
 #define FD_CLOEXEC 0x1
 
+#define EAFNOSUPPORT WSAEAFNOSUPPORT
+#define ECONNABORTED WSAECONNABORTED
+
 struct passwd {
 	char *pw_name;
 	char *pw_gecos;
@@ -40,14 +57,6 @@
 
 extern char *getpass(const char *prompt);
 
-struct pollfd {
-	int fd;           /* file descriptor */
-	short events;     /* requested events */
-	short revents;    /* returned events */
-};
-#define POLLIN 1
-#define POLLHUP 2
-
 typedef void (__cdecl *sig_handler_t)(int);
 struct sigaction {
 	sig_handler_t sa_handler;
@@ -62,6 +71,12 @@
 #define ITIMER_REAL 0
 
 /*
+ * sanitize preprocessor namespace polluted by Windows headers defining
+ * macros which collide with git local versions
+ */
+#undef HELP_COMMAND /* from winuser.h */
+
+/*
  * trivial stubs
  */
 
@@ -71,21 +86,21 @@
 { errno = ENOSYS; return -1; }
 static inline int fchmod(int fildes, mode_t mode)
 { errno = ENOSYS; return -1; }
-static inline int fork(void)
+static inline pid_t fork(void)
 { errno = ENOSYS; return -1; }
 static inline unsigned int alarm(unsigned int seconds)
 { return 0; }
 static inline int fsync(int fd)
-{ return 0; }
-static inline int getppid(void)
+{ return _commit(fd); }
+static inline pid_t getppid(void)
 { return 1; }
 static inline void sync(void)
 {}
-static inline int getuid()
+static inline uid_t getuid(void)
 { return 1; }
 static inline struct passwd *getpwnam(const char *name)
 { return NULL; }
-static inline int fcntl(int fd, int cmd, long arg)
+static inline int fcntl(int fd, int cmd, ...)
 {
 	if (cmd == F_GETFD || cmd == F_SETFD)
 		return 0;
@@ -105,21 +120,32 @@
 }
 #define mkdir mingw_mkdir
 
-static inline int mingw_unlink(const char *pathname)
-{
-	/* read-only files cannot be removed */
-	chmod(pathname, 0666);
-	return unlink(pathname);
-}
-#define unlink mingw_unlink
+#define WNOHANG 1
+pid_t waitpid(pid_t pid, int *status, int options);
 
-static inline int waitpid(pid_t pid, int *status, unsigned options)
+#define kill mingw_kill
+int mingw_kill(pid_t pid, int sig);
+
+#ifndef NO_OPENSSL
+#include <openssl/ssl.h>
+static inline int mingw_SSL_set_fd(SSL *ssl, int fd)
 {
-	if (options == 0)
-		return _cwait(status, pid, 0);
-	errno = EINVAL;
-	return -1;
+	return SSL_set_fd(ssl, _get_osfhandle(fd));
 }
+#define SSL_set_fd mingw_SSL_set_fd
+
+static inline int mingw_SSL_set_rfd(SSL *ssl, int fd)
+{
+	return SSL_set_rfd(ssl, _get_osfhandle(fd));
+}
+#define SSL_set_rfd mingw_SSL_set_rfd
+
+static inline int mingw_SSL_set_wfd(SSL *ssl, int fd)
+{
+	return SSL_set_wfd(ssl, _get_osfhandle(fd));
+}
+#define SSL_set_wfd mingw_SSL_set_wfd
+#endif
 
 /*
  * implementations of missing functions
@@ -129,11 +155,10 @@
 unsigned int sleep (unsigned int seconds);
 int mkstemp(char *template);
 int gettimeofday(struct timeval *tv, void *tz);
-int poll(struct pollfd *ufds, unsigned int nfds, int timeout);
 struct tm *gmtime_r(const time_t *timep, struct tm *result);
 struct tm *localtime_r(const time_t *timep, struct tm *result);
 int getpagesize(void);	/* defined in MinGW's libgcc.a */
-struct passwd *getpwuid(int uid);
+struct passwd *getpwuid(uid_t uid);
 int setitimer(int type, struct itimerval *in, struct itimerval *out);
 int sigaction(int sig, struct sigaction *in, struct sigaction *out);
 int link(const char *oldpath, const char *newpath);
@@ -142,50 +167,118 @@
  * replacements of existing functions
  */
 
+int mingw_unlink(const char *pathname);
+#define unlink mingw_unlink
+
+int mingw_rmdir(const char *path);
+#define rmdir mingw_rmdir
+
 int mingw_open (const char *filename, int oflags, ...);
 #define open mingw_open
 
+ssize_t mingw_write(int fd, const void *buf, size_t count);
+#define write mingw_write
+
+FILE *mingw_fopen (const char *filename, const char *otype);
+#define fopen mingw_fopen
+
+FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream);
+#define freopen mingw_freopen
+
 char *mingw_getcwd(char *pointer, int len);
 #define getcwd mingw_getcwd
 
 char *mingw_getenv(const char *name);
 #define getenv mingw_getenv
 
+int mingw_gethostname(char *host, int namelen);
+#define gethostname mingw_gethostname
+
 struct hostent *mingw_gethostbyname(const char *host);
 #define gethostbyname mingw_gethostbyname
 
+void mingw_freeaddrinfo(struct addrinfo *res);
+#define freeaddrinfo mingw_freeaddrinfo
+
+int mingw_getaddrinfo(const char *node, const char *service,
+		      const struct addrinfo *hints, struct addrinfo **res);
+#define getaddrinfo mingw_getaddrinfo
+
+int mingw_getnameinfo(const struct sockaddr *sa, socklen_t salen,
+		      char *host, DWORD hostlen, char *serv, DWORD servlen,
+		      int flags);
+#define getnameinfo mingw_getnameinfo
+
 int mingw_socket(int domain, int type, int protocol);
 #define socket mingw_socket
 
 int mingw_connect(int sockfd, struct sockaddr *sa, size_t sz);
 #define connect mingw_connect
 
+int mingw_bind(int sockfd, struct sockaddr *sa, size_t sz);
+#define bind mingw_bind
+
+int mingw_setsockopt(int sockfd, int lvl, int optname, void *optval, int optlen);
+#define setsockopt mingw_setsockopt
+
+int mingw_shutdown(int sockfd, int how);
+#define shutdown mingw_shutdown
+
+int mingw_listen(int sockfd, int backlog);
+#define listen mingw_listen
+
+int mingw_accept(int sockfd, struct sockaddr *sa, socklen_t *sz);
+#define accept mingw_accept
+
 int mingw_rename(const char*, const char*);
 #define rename mingw_rename
 
-#ifdef USE_WIN32_MMAP
+#if defined(USE_WIN32_MMAP) || defined(_MSC_VER)
 int mingw_getpagesize(void);
 #define getpagesize mingw_getpagesize
 #endif
 
+struct rlimit {
+	unsigned int rlim_cur;
+};
+#define RLIMIT_NOFILE 0
+
+static inline int getrlimit(int resource, struct rlimit *rlp)
+{
+	if (resource != RLIMIT_NOFILE) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	rlp->rlim_cur = 2048;
+	return 0;
+}
+
 /* Use mingw_lstat() instead of lstat()/stat() and
  * mingw_fstat() instead of fstat() on Windows.
  */
 #define off_t off64_t
-#define stat _stati64
 #define lseek _lseeki64
+#ifndef ALREADY_DECLARED_STAT_FUNCS
+#define stat _stati64
 int mingw_lstat(const char *file_name, struct stat *buf);
+int mingw_stat(const char *file_name, struct stat *buf);
 int mingw_fstat(int fd, struct stat *buf);
 #define fstat mingw_fstat
 #define lstat mingw_lstat
-#define _stati64(x,y) mingw_lstat(x,y)
+#define _stati64(x,y) mingw_stat(x,y)
+#endif
 
 int mingw_utime(const char *file_name, const struct utimbuf *times);
 #define utime mingw_utime
 
-pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env);
-void mingw_execvp(const char *cmd, char *const *argv);
+pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env,
+		     const char *dir,
+		     int fhin, int fhout, int fherr);
+int mingw_execvp(const char *cmd, char *const *argv);
 #define execvp mingw_execvp
+int mingw_execv(const char *cmd, char *const *argv);
+#define execv mingw_execv
 
 static inline unsigned int git_ntohl(unsigned int x)
 { return (unsigned int)ntohl(x); }
@@ -211,6 +304,15 @@
 
 #define has_dos_drive_prefix(path) (isalpha(*(path)) && (path)[1] == ':')
 #define is_dir_sep(c) ((c) == '/' || (c) == '\\')
+static inline char *mingw_find_last_dir_sep(const char *path)
+{
+	char *ret = NULL;
+	for (; *path; ++path)
+		if (is_dir_sep(*path))
+			ret = (char *)path;
+	return ret;
+}
+#define find_last_dir_sep mingw_find_last_dir_sep
 #define PATH_SEP ';'
 #define PRIuMAX "I64u"
 
@@ -221,48 +323,30 @@
  * helpers
  */
 
-char **copy_environ(void);
+char **make_augmented_environ(const char *const *vars);
 void free_environ(char **env);
-char **env_setenv(char **env, const char *name);
 
 /*
  * A replacement of main() that ensures that argv[0] has a path
+ * and that default fmode and std(in|out|err) are in binary mode
  */
 
 #define main(c,v) dummy_decl_mingw_main(); \
 static int mingw_main(); \
 int main(int argc, const char **argv) \
 { \
+	extern CRITICAL_SECTION pinfo_cs; \
+	_fmode = _O_BINARY; \
+	_setmode(_fileno(stdin), _O_BINARY); \
+	_setmode(_fileno(stdout), _O_BINARY); \
+	_setmode(_fileno(stderr), _O_BINARY); \
 	argv[0] = xstrdup(_pgmptr); \
+	InitializeCriticalSection(&pinfo_cs); \
 	return mingw_main(argc, argv); \
 } \
 static int mingw_main(c,v)
 
-#ifndef NO_MINGW_REPLACE_READDIR
 /*
- * A replacement of readdir, to ensure that it reads the file type at
- * the same time. This avoid extra unneeded lstats in git on MinGW
+ * Used by Pthread API implementation for Windows
  */
-#undef DT_UNKNOWN
-#undef DT_DIR
-#undef DT_REG
-#undef DT_LNK
-#define DT_UNKNOWN	0
-#define DT_DIR		1
-#define DT_REG		2
-#define DT_LNK		3
-
-struct mingw_dirent
-{
-	long		d_ino;			/* Always zero. */
-	union {
-		unsigned short	d_reclen;	/* Always zero. */
-		unsigned char   d_type;		/* Reimplementation adds this */
-	};
-	unsigned short	d_namlen;		/* Length of name in d_name. */
-	char		d_name[FILENAME_MAX];	/* File name. */
-};
-#define dirent mingw_dirent
-#define readdir(x) mingw_readdir(x)
-struct dirent *mingw_readdir(DIR *dir);
-#endif // !NO_MINGW_REPLACE_READDIR
+extern int err_win_to_posix(DWORD winerr);
diff --git a/compat/mkdtemp.c b/compat/mkdtemp.c
index 34d4b49..1136119 100644
--- a/compat/mkdtemp.c
+++ b/compat/mkdtemp.c
@@ -2,7 +2,7 @@
 
 char *gitmkdtemp(char *template)
 {
-	if (!mktemp(template) || mkdir(template, 0700))
+	if (!*mktemp(template) || mkdir(template, 0700))
 		return NULL;
 	return template;
 }
diff --git a/compat/mkstemps.c b/compat/mkstemps.c
deleted file mode 100644
index 14179c8..0000000
--- a/compat/mkstemps.c
+++ /dev/null
@@ -1,70 +0,0 @@
-#include "../git-compat-util.h"
-
-/* Adapted from libiberty's mkstemp.c. */
-
-#undef TMP_MAX
-#define TMP_MAX 16384
-
-int gitmkstemps(char *pattern, int suffix_len)
-{
-	static const char letters[] =
-		"abcdefghijklmnopqrstuvwxyz"
-		"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-		"0123456789";
-	static const int num_letters = 62;
-	uint64_t value;
-	struct timeval tv;
-	char *template;
-	size_t len;
-	int fd, count;
-
-	len = strlen(pattern);
-
-	if (len < 6 + suffix_len) {
-		errno = EINVAL;
-		return -1;
-	}
-
-	if (strncmp(&pattern[len - 6 - suffix_len], "XXXXXX", 6)) {
-		errno = EINVAL;
-		return -1;
-	}
-
-	/*
-	 * Replace pattern's XXXXXX characters with randomness.
-	 * Try TMP_MAX different filenames.
-	 */
-	gettimeofday(&tv, NULL);
-	value = ((size_t)(tv.tv_usec << 16)) ^ tv.tv_sec ^ getpid();
-	template = &pattern[len - 6 - suffix_len];
-	for (count = 0; count < TMP_MAX; ++count) {
-		uint64_t v = value;
-		/* Fill in the random bits. */
-		template[0] = letters[v % num_letters]; v /= num_letters;
-		template[1] = letters[v % num_letters]; v /= num_letters;
-		template[2] = letters[v % num_letters]; v /= num_letters;
-		template[3] = letters[v % num_letters]; v /= num_letters;
-		template[4] = letters[v % num_letters]; v /= num_letters;
-		template[5] = letters[v % num_letters]; v /= num_letters;
-
-		fd = open(pattern, O_CREAT | O_EXCL | O_RDWR, 0600);
-		if (fd > 0)
-			return fd;
-		/*
-		 * Fatal error (EPERM, ENOSPC etc).
-		 * It doesn't make sense to loop.
-		 */
-		if (errno != EEXIST)
-			break;
-		/*
-		 * This is a random value.  It is only necessary that
-		 * the next TMP_MAX values generated by adding 7777 to
-		 * VALUE are different with (module 2^32).
-		 */
-		value += 7777;
-	}
-	/* We return the null string if we can't find a unique file name.  */
-	pattern[0] = '\0';
-	errno = EINVAL;
-	return -1;
-}
diff --git a/compat/msvc.c b/compat/msvc.c
new file mode 100644
index 0000000..71843d7
--- /dev/null
+++ b/compat/msvc.c
@@ -0,0 +1,6 @@
+#include "../git-compat-util.h"
+#include "win32.h"
+#include <conio.h>
+#include "../strbuf.h"
+
+#include "mingw.c"
diff --git a/compat/msvc.h b/compat/msvc.h
new file mode 100644
index 0000000..aa4b563
--- /dev/null
+++ b/compat/msvc.h
@@ -0,0 +1,42 @@
+#ifndef __MSVC__HEAD
+#define __MSVC__HEAD
+
+#include <direct.h>
+#include <process.h>
+#include <malloc.h>
+#include <io.h>
+
+/* porting function */
+#define inline __inline
+#define __inline__ __inline
+#define __attribute__(x)
+#define strncasecmp  _strnicmp
+#define ftruncate    _chsize
+
+static __inline int strcasecmp (const char *s1, const char *s2)
+{
+	int size1 = strlen(s1);
+	int sisz2 = strlen(s2);
+	return _strnicmp(s1, s2, sisz2 > size1 ? sisz2 : size1);
+}
+
+#undef ERROR
+
+/* Use mingw_lstat() instead of lstat()/stat() and mingw_fstat() instead
+ * of fstat(). We add the declaration of these functions here, suppressing
+ * the corresponding declarations in mingw.h, so that we can use the
+ * appropriate structure type (and function) names from the msvc headers.
+ */
+#define stat _stat64
+int mingw_lstat(const char *file_name, struct stat *buf);
+int mingw_fstat(int fd, struct stat *buf);
+#define fstat mingw_fstat
+#define lstat mingw_lstat
+#define _stat64(x,y) mingw_lstat(x,y)
+#define ALREADY_DECLARED_STAT_FUNCS
+
+#include "compat/mingw.h"
+
+#undef ALREADY_DECLARED_STAT_FUNCS
+
+#endif
diff --git a/compat/nedmalloc/Readme.txt b/compat/nedmalloc/Readme.txt
index 8763656..e46d8f1 100644
--- a/compat/nedmalloc/Readme.txt
+++ b/compat/nedmalloc/Readme.txt
@@ -100,7 +100,7 @@
 Thanks to Dmitry Chichkov for reporting this. Futher thanks to Aleksey Sanin.
  * Fixed realloc(0, <size>) segfaulting. Thanks to Dmitry Chichkov for
 reporting this.
- * Made config defines #ifndef so they can be overriden by the build system.
+ * Made config defines #ifndef so they can be overridden by the build system.
 Thanks to Aleksey Sanin for suggesting this.
  * Fixed deadlock in nedprealloc() due to unnecessary locking of preferred
 thread mspace when mspace_realloc() always uses the original block's mspace
diff --git a/compat/nedmalloc/malloc.c.h b/compat/nedmalloc/malloc.c.h
index 74c42e3..ff7c2c4 100644
--- a/compat/nedmalloc/malloc.c.h
+++ b/compat/nedmalloc/malloc.c.h
@@ -100,7 +100,7 @@
 
        If you don't like either of these options, you can define
        CORRUPTION_ERROR_ACTION and USAGE_ERROR_ACTION to do anything
-       else. And if if you are sure that your program using malloc has
+       else. And if you are sure that your program using malloc has
        no errors or vulnerabilities, you can define INSECURE to 1,
        which might (or might not) provide a small performance improvement.
 
@@ -2069,7 +2069,7 @@
   Each freshly allocated chunk must have both cinuse and pinuse set.
   That is, each allocated chunk borders either a previously allocated
   and still in-use chunk, or the base of its memory arena. This is
-  ensured by making all allocations from the the `lowest' part of any
+  ensured by making all allocations from the `lowest' part of any
   found chunk.  Further, no free chunk physically borders another one,
   so each free chunk is known to be preceded and followed by either
   inuse chunks or the ends of memory.
@@ -2279,12 +2279,12 @@
   of the same size are arranged in a circularly-linked list, with only
   the oldest chunk (the next to be used, in our FIFO ordering)
   actually in the tree.  (Tree members are distinguished by a non-null
-  parent pointer.)  If a chunk with the same size an an existing node
+  parent pointer.)  If a chunk with the same size as an existing node
   is inserted, it is linked off the existing node using pointers that
   work in the same way as fd/bk pointers of small chunks.
 
   Each tree contains a power of 2 sized range of chunk sizes (the
-  smallest is 0x100 <= x < 0x180), which is is divided in half at each
+  smallest is 0x100 <= x < 0x180), which is divided in half at each
   tree level, with the chunks in the smaller half of the range (0x100
   <= x < 0x140 for the top nose) in the left subtree and the larger
   half (0x140 <= x < 0x180) in the right subtree.  This is, of course,
@@ -3943,7 +3943,7 @@
     least-preferred order):
     1. A call to MORECORE that can normally contiguously extend memory.
        (disabled if not MORECORE_CONTIGUOUS or not HAVE_MORECORE or
-       or main space is mmapped or a previous contiguous call failed)
+       main space is mmapped or a previous contiguous call failed)
     2. A call to MMAP new space (disabled if not HAVE_MMAP).
        Note that under the default settings, if MORECORE is unable to
        fulfill a request, and HAVE_MMAP is true, then mmap is
@@ -5748,5 +5748,3 @@
 	 structure of old version,  but most details differ.)
 
 */
-
-
diff --git a/compat/obstack.c b/compat/obstack.c
new file mode 100644
index 0000000..e276ccd
--- /dev/null
+++ b/compat/obstack.c
@@ -0,0 +1,413 @@
+/* obstack.c - subroutines used implicitly by object stack macros
+   Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998,
+   1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+#include "git-compat-util.h"
+#include <gettext.h>
+#include "obstack.h"
+
+/* NOTE BEFORE MODIFYING THIS FILE: This version number must be
+   incremented whenever callers compiled using an old obstack.h can no
+   longer properly call the functions in this obstack.c.  */
+#define OBSTACK_INTERFACE_VERSION 1
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+   actually compiling the library itself, and the installed library
+   supports the same library interface we do.  This code is part of the GNU
+   C Library, but also included in many other GNU distributions.  Compiling
+   and linking in this code is a waste when using the GNU C library
+   (especially if it is a shared library).  Rather than having every GNU
+   program understand `configure --with-gnu-libc' and omit the object
+   files, it is simpler to just do this in the source for each such file.  */
+
+#include <stdio.h>		/* Random thing to get __GNU_LIBRARY__.  */
+#if !defined _LIBC && defined __GNU_LIBRARY__ && __GNU_LIBRARY__ > 1
+# include <gnu-versions.h>
+# if _GNU_OBSTACK_INTERFACE_VERSION == OBSTACK_INTERFACE_VERSION
+#  define ELIDE_CODE
+# endif
+#endif
+
+#include <stddef.h>
+
+#ifndef ELIDE_CODE
+
+
+# if HAVE_INTTYPES_H
+#  include <inttypes.h>
+# endif
+# if HAVE_STDINT_H || defined _LIBC
+#  include <stdint.h>
+# endif
+
+/* Determine default alignment.  */
+union fooround
+{
+  uintmax_t i;
+  long double d;
+  void *p;
+};
+struct fooalign
+{
+  char c;
+  union fooround u;
+};
+/* If malloc were really smart, it would round addresses to DEFAULT_ALIGNMENT.
+   But in fact it might be less smart and round addresses to as much as
+   DEFAULT_ROUNDING.  So we prepare for it to do that.  */
+enum
+  {
+    DEFAULT_ALIGNMENT = offsetof (struct fooalign, u),
+    DEFAULT_ROUNDING = sizeof (union fooround)
+  };
+
+/* When we copy a long block of data, this is the unit to do it with.
+   On some machines, copying successive ints does not work;
+   in such a case, redefine COPYING_UNIT to `long' (if that works)
+   or `char' as a last resort.  */
+# ifndef COPYING_UNIT
+#  define COPYING_UNIT int
+# endif
+
+
+/* The functions allocating more room by calling `obstack_chunk_alloc'
+   jump to the handler pointed to by `obstack_alloc_failed_handler'.
+   This can be set to a user defined function which should either
+   abort gracefully or use longjump - but shouldn't return.  This
+   variable by default points to the internal function
+   `print_and_abort'.  */
+static void print_and_abort (void);
+void (*obstack_alloc_failed_handler) (void) = print_and_abort;
+
+# ifdef _LIBC
+#  if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3_4)
+/* A looong time ago (before 1994, anyway; we're not sure) this global variable
+   was used by non-GNU-C macros to avoid multiple evaluation.  The GNU C
+   library still exports it because somebody might use it.  */
+struct obstack *_obstack_compat;
+compat_symbol (libc, _obstack_compat, _obstack, GLIBC_2_0);
+#  endif
+# endif
+
+/* Define a macro that either calls functions with the traditional malloc/free
+   calling interface, or calls functions with the mmalloc/mfree interface
+   (that adds an extra first argument), based on the state of use_extra_arg.
+   For free, do not use ?:, since some compilers, like the MIPS compilers,
+   do not allow (expr) ? void : void.  */
+
+# define CALL_CHUNKFUN(h, size) \
+  (((h) -> use_extra_arg) \
+   ? (*(h)->chunkfun) ((h)->extra_arg, (size)) \
+   : (*(struct _obstack_chunk *(*) (long)) (h)->chunkfun) ((size)))
+
+# define CALL_FREEFUN(h, old_chunk) \
+  do { \
+    if ((h) -> use_extra_arg) \
+      (*(h)->freefun) ((h)->extra_arg, (old_chunk)); \
+    else \
+      (*(void (*) (void *)) (h)->freefun) ((old_chunk)); \
+  } while (0)
+
+
+/* Initialize an obstack H for use.  Specify chunk size SIZE (0 means default).
+   Objects start on multiples of ALIGNMENT (0 means use default).
+   CHUNKFUN is the function to use to allocate chunks,
+   and FREEFUN the function to free them.
+
+   Return nonzero if successful, calls obstack_alloc_failed_handler if
+   allocation fails.  */
+
+int
+_obstack_begin (struct obstack *h,
+		int size, int alignment,
+		void *(*chunkfun) (long),
+		void (*freefun) (void *))
+{
+  register struct _obstack_chunk *chunk; /* points to new chunk */
+
+  if (alignment == 0)
+    alignment = DEFAULT_ALIGNMENT;
+  if (size == 0)
+    /* Default size is what GNU malloc can fit in a 4096-byte block.  */
+    {
+      /* 12 is sizeof (mhead) and 4 is EXTRA from GNU malloc.
+	 Use the values for range checking, because if range checking is off,
+	 the extra bytes won't be missed terribly, but if range checking is on
+	 and we used a larger request, a whole extra 4096 bytes would be
+	 allocated.
+
+	 These number are irrelevant to the new GNU malloc.  I suspect it is
+	 less sensitive to the size of the request.  */
+      int extra = ((((12 + DEFAULT_ROUNDING - 1) & ~(DEFAULT_ROUNDING - 1))
+		    + 4 + DEFAULT_ROUNDING - 1)
+		   & ~(DEFAULT_ROUNDING - 1));
+      size = 4096 - extra;
+    }
+
+  h->chunkfun = (struct _obstack_chunk * (*)(void *, long)) chunkfun;
+  h->freefun = (void (*) (void *, struct _obstack_chunk *)) freefun;
+  h->chunk_size = size;
+  h->alignment_mask = alignment - 1;
+  h->use_extra_arg = 0;
+
+  chunk = h->chunk = CALL_CHUNKFUN (h, h -> chunk_size);
+  if (!chunk)
+    (*obstack_alloc_failed_handler) ();
+  h->next_free = h->object_base = __PTR_ALIGN ((char *) chunk, chunk->contents,
+					       alignment - 1);
+  h->chunk_limit = chunk->limit
+    = (char *) chunk + h->chunk_size;
+  chunk->prev = NULL;
+  /* The initial chunk now contains no empty object.  */
+  h->maybe_empty_object = 0;
+  h->alloc_failed = 0;
+  return 1;
+}
+
+int
+_obstack_begin_1 (struct obstack *h, int size, int alignment,
+		  void *(*chunkfun) (void *, long),
+		  void (*freefun) (void *, void *),
+		  void *arg)
+{
+  register struct _obstack_chunk *chunk; /* points to new chunk */
+
+  if (alignment == 0)
+    alignment = DEFAULT_ALIGNMENT;
+  if (size == 0)
+    /* Default size is what GNU malloc can fit in a 4096-byte block.  */
+    {
+      /* 12 is sizeof (mhead) and 4 is EXTRA from GNU malloc.
+	 Use the values for range checking, because if range checking is off,
+	 the extra bytes won't be missed terribly, but if range checking is on
+	 and we used a larger request, a whole extra 4096 bytes would be
+	 allocated.
+
+	 These number are irrelevant to the new GNU malloc.  I suspect it is
+	 less sensitive to the size of the request.  */
+      int extra = ((((12 + DEFAULT_ROUNDING - 1) & ~(DEFAULT_ROUNDING - 1))
+		    + 4 + DEFAULT_ROUNDING - 1)
+		   & ~(DEFAULT_ROUNDING - 1));
+      size = 4096 - extra;
+    }
+
+  h->chunkfun = (struct _obstack_chunk * (*)(void *,long)) chunkfun;
+  h->freefun = (void (*) (void *, struct _obstack_chunk *)) freefun;
+  h->chunk_size = size;
+  h->alignment_mask = alignment - 1;
+  h->extra_arg = arg;
+  h->use_extra_arg = 1;
+
+  chunk = h->chunk = CALL_CHUNKFUN (h, h -> chunk_size);
+  if (!chunk)
+    (*obstack_alloc_failed_handler) ();
+  h->next_free = h->object_base = __PTR_ALIGN ((char *) chunk, chunk->contents,
+					       alignment - 1);
+  h->chunk_limit = chunk->limit
+    = (char *) chunk + h->chunk_size;
+  chunk->prev = NULL;
+  /* The initial chunk now contains no empty object.  */
+  h->maybe_empty_object = 0;
+  h->alloc_failed = 0;
+  return 1;
+}
+
+/* Allocate a new current chunk for the obstack *H
+   on the assumption that LENGTH bytes need to be added
+   to the current object, or a new object of length LENGTH allocated.
+   Copies any partial object from the end of the old chunk
+   to the beginning of the new one.  */
+
+void
+_obstack_newchunk (struct obstack *h, int length)
+{
+  register struct _obstack_chunk *old_chunk = h->chunk;
+  register struct _obstack_chunk *new_chunk;
+  register long	new_size;
+  register long obj_size = h->next_free - h->object_base;
+  register long i;
+  long already;
+  char *object_base;
+
+  /* Compute size for new chunk.  */
+  new_size = (obj_size + length) + (obj_size >> 3) + h->alignment_mask + 100;
+  if (new_size < h->chunk_size)
+    new_size = h->chunk_size;
+
+  /* Allocate and initialize the new chunk.  */
+  new_chunk = CALL_CHUNKFUN (h, new_size);
+  if (!new_chunk)
+    (*obstack_alloc_failed_handler) ();
+  h->chunk = new_chunk;
+  new_chunk->prev = old_chunk;
+  new_chunk->limit = h->chunk_limit = (char *) new_chunk + new_size;
+
+  /* Compute an aligned object_base in the new chunk */
+  object_base =
+    __PTR_ALIGN ((char *) new_chunk, new_chunk->contents, h->alignment_mask);
+
+  /* Move the existing object to the new chunk.
+     Word at a time is fast and is safe if the object
+     is sufficiently aligned.  */
+  if (h->alignment_mask + 1 >= DEFAULT_ALIGNMENT)
+    {
+      for (i = obj_size / sizeof (COPYING_UNIT) - 1;
+	   i >= 0; i--)
+	((COPYING_UNIT *)object_base)[i]
+	  = ((COPYING_UNIT *)h->object_base)[i];
+      /* We used to copy the odd few remaining bytes as one extra COPYING_UNIT,
+	 but that can cross a page boundary on a machine
+	 which does not do strict alignment for COPYING_UNITS.  */
+      already = obj_size / sizeof (COPYING_UNIT) * sizeof (COPYING_UNIT);
+    }
+  else
+    already = 0;
+  /* Copy remaining bytes one by one.  */
+  for (i = already; i < obj_size; i++)
+    object_base[i] = h->object_base[i];
+
+  /* If the object just copied was the only data in OLD_CHUNK,
+     free that chunk and remove it from the chain.
+     But not if that chunk might contain an empty object.  */
+  if (! h->maybe_empty_object
+      && (h->object_base
+	  == __PTR_ALIGN ((char *) old_chunk, old_chunk->contents,
+			  h->alignment_mask)))
+    {
+      new_chunk->prev = old_chunk->prev;
+      CALL_FREEFUN (h, old_chunk);
+    }
+
+  h->object_base = object_base;
+  h->next_free = h->object_base + obj_size;
+  /* The new chunk certainly contains no empty object yet.  */
+  h->maybe_empty_object = 0;
+}
+# ifdef _LIBC
+libc_hidden_def (_obstack_newchunk)
+# endif
+
+/* Return nonzero if object OBJ has been allocated from obstack H.
+   This is here for debugging.
+   If you use it in a program, you are probably losing.  */
+
+/* Suppress -Wmissing-prototypes warning.  We don't want to declare this in
+   obstack.h because it is just for debugging.  */
+int _obstack_allocated_p (struct obstack *h, void *obj);
+
+int
+_obstack_allocated_p (struct obstack *h, void *obj)
+{
+  register struct _obstack_chunk *lp;	/* below addr of any objects in this chunk */
+  register struct _obstack_chunk *plp;	/* point to previous chunk if any */
+
+  lp = (h)->chunk;
+  /* We use >= rather than > since the object cannot be exactly at
+     the beginning of the chunk but might be an empty object exactly
+     at the end of an adjacent chunk.  */
+  while (lp != NULL && ((void *) lp >= obj || (void *) (lp)->limit < obj))
+    {
+      plp = lp->prev;
+      lp = plp;
+    }
+  return lp != NULL;
+}
+
+/* Free objects in obstack H, including OBJ and everything allocate
+   more recently than OBJ.  If OBJ is zero, free everything in H.  */
+
+# undef obstack_free
+
+void
+obstack_free (struct obstack *h, void *obj)
+{
+  register struct _obstack_chunk *lp;	/* below addr of any objects in this chunk */
+  register struct _obstack_chunk *plp;	/* point to previous chunk if any */
+
+  lp = h->chunk;
+  /* We use >= because there cannot be an object at the beginning of a chunk.
+     But there can be an empty object at that address
+     at the end of another chunk.  */
+  while (lp != NULL && ((void *) lp >= obj || (void *) (lp)->limit < obj))
+    {
+      plp = lp->prev;
+      CALL_FREEFUN (h, lp);
+      lp = plp;
+      /* If we switch chunks, we can't tell whether the new current
+	 chunk contains an empty object, so assume that it may.  */
+      h->maybe_empty_object = 1;
+    }
+  if (lp)
+    {
+      h->object_base = h->next_free = (char *) (obj);
+      h->chunk_limit = lp->limit;
+      h->chunk = lp;
+    }
+  else if (obj != NULL)
+    /* obj is not in any of the chunks! */
+    abort ();
+}
+
+# ifdef _LIBC
+/* Older versions of libc used a function _obstack_free intended to be
+   called by non-GCC compilers.  */
+strong_alias (obstack_free, _obstack_free)
+# endif
+
+int
+_obstack_memory_used (struct obstack *h)
+{
+  register struct _obstack_chunk* lp;
+  register int nbytes = 0;
+
+  for (lp = h->chunk; lp != NULL; lp = lp->prev)
+    {
+      nbytes += lp->limit - (char *) lp;
+    }
+  return nbytes;
+}
+
+# ifdef _LIBC
+#  include <libio/iolibio.h>
+# endif
+
+# ifndef __attribute__
+/* This feature is available in gcc versions 2.5 and later.  */
+#  if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5)
+#   define __attribute__(Spec) /* empty */
+#  endif
+# endif
+
+static void
+print_and_abort (void)
+{
+  /* Don't change any of these strings.  Yes, it would be possible to add
+     the newline to the string and use fputs or so.  But this must not
+     happen because the "memory exhausted" message appears in other places
+     like this and the translation should be reused instead of creating
+     a very similar string which requires a separate translation.  */
+# ifdef _LIBC
+  (void) __fxprintf (NULL, "%s\n", _("memory exhausted"));
+# else
+  fprintf (stderr, "%s\n", _("memory exhausted"));
+# endif
+  exit (1);
+}
+
+#endif	/* !ELIDE_CODE */
diff --git a/compat/obstack.h b/compat/obstack.h
new file mode 100644
index 0000000..d178bd6
--- /dev/null
+++ b/compat/obstack.h
@@ -0,0 +1,506 @@
+/* obstack.h - object stack macros
+   Copyright (C) 1988-1994,1996-1999,2003,2004,2005,2009
+	Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+/* Summary:
+
+All the apparent functions defined here are macros. The idea
+is that you would use these pre-tested macros to solve a
+very specific set of problems, and they would run fast.
+Caution: no side-effects in arguments please!! They may be
+evaluated MANY times!!
+
+These macros operate a stack of objects.  Each object starts life
+small, and may grow to maturity.  (Consider building a word syllable
+by syllable.)  An object can move while it is growing.  Once it has
+been "finished" it never changes address again.  So the "top of the
+stack" is typically an immature growing object, while the rest of the
+stack is of mature, fixed size and fixed address objects.
+
+These routines grab large chunks of memory, using a function you
+supply, called `obstack_chunk_alloc'.  On occasion, they free chunks,
+by calling `obstack_chunk_free'.  You must define them and declare
+them before using any obstack macros.
+
+Each independent stack is represented by a `struct obstack'.
+Each of the obstack macros expects a pointer to such a structure
+as the first argument.
+
+One motivation for this package is the problem of growing char strings
+in symbol tables.  Unless you are "fascist pig with a read-only mind"
+--Gosper's immortal quote from HAKMEM item 154, out of context--you
+would not like to put any arbitrary upper limit on the length of your
+symbols.
+
+In practice this often means you will build many short symbols and a
+few long symbols.  At the time you are reading a symbol you don't know
+how long it is.  One traditional method is to read a symbol into a
+buffer, realloc()ating the buffer every time you try to read a symbol
+that is longer than the buffer.  This is beaut, but you still will
+want to copy the symbol from the buffer to a more permanent
+symbol-table entry say about half the time.
+
+With obstacks, you can work differently.  Use one obstack for all symbol
+names.  As you read a symbol, grow the name in the obstack gradually.
+When the name is complete, finalize it.  Then, if the symbol exists already,
+free the newly read name.
+
+The way we do this is to take a large chunk, allocating memory from
+low addresses.  When you want to build a symbol in the chunk you just
+add chars above the current "high water mark" in the chunk.  When you
+have finished adding chars, because you got to the end of the symbol,
+you know how long the chars are, and you can create a new object.
+Mostly the chars will not burst over the highest address of the chunk,
+because you would typically expect a chunk to be (say) 100 times as
+long as an average object.
+
+In case that isn't clear, when we have enough chars to make up
+the object, THEY ARE ALREADY CONTIGUOUS IN THE CHUNK (guaranteed)
+so we just point to it where it lies.  No moving of chars is
+needed and this is the second win: potentially long strings need
+never be explicitly shuffled. Once an object is formed, it does not
+change its address during its lifetime.
+
+When the chars burst over a chunk boundary, we allocate a larger
+chunk, and then copy the partly formed object from the end of the old
+chunk to the beginning of the new larger chunk.  We then carry on
+accreting characters to the end of the object as we normally would.
+
+A special macro is provided to add a single char at a time to a
+growing object.  This allows the use of register variables, which
+break the ordinary 'growth' macro.
+
+Summary:
+	We allocate large chunks.
+	We carve out one object at a time from the current chunk.
+	Once carved, an object never moves.
+	We are free to append data of any size to the currently
+	  growing object.
+	Exactly one object is growing in an obstack at any one time.
+	You can run one obstack per control block.
+	You may have as many control blocks as you dare.
+	Because of the way we do it, you can `unwind' an obstack
+	  back to a previous state. (You may remove objects much
+	  as you would with a stack.)
+*/
+
+
+/* Don't do the contents of this file more than once.  */
+
+#ifndef _OBSTACK_H
+#define _OBSTACK_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* We need the type of a pointer subtraction.  If __PTRDIFF_TYPE__ is
+   defined, as with GNU C, use that; that way we don't pollute the
+   namespace with <stddef.h>'s symbols.  Otherwise, include <stddef.h>
+   and use ptrdiff_t.  */
+
+#ifdef __PTRDIFF_TYPE__
+# define PTR_INT_TYPE __PTRDIFF_TYPE__
+#else
+# include <stddef.h>
+# define PTR_INT_TYPE ptrdiff_t
+#endif
+
+/* If B is the base of an object addressed by P, return the result of
+   aligning P to the next multiple of A + 1.  B and P must be of type
+   char *.  A + 1 must be a power of 2.  */
+
+#define __BPTR_ALIGN(B, P, A) ((B) + (((P) - (B) + (A)) & ~(A)))
+
+/* Similiar to _BPTR_ALIGN (B, P, A), except optimize the common case
+   where pointers can be converted to integers, aligned as integers,
+   and converted back again.  If PTR_INT_TYPE is narrower than a
+   pointer (e.g., the AS/400), play it safe and compute the alignment
+   relative to B.  Otherwise, use the faster strategy of computing the
+   alignment relative to 0.  */
+
+#define __PTR_ALIGN(B, P, A)						    \
+  __BPTR_ALIGN (sizeof (PTR_INT_TYPE) < sizeof (void *) ? (B) : (char *) 0, \
+		P, A)
+
+#include <string.h>
+
+struct _obstack_chunk		/* Lives at front of each chunk. */
+{
+  char  *limit;			/* 1 past end of this chunk */
+  struct _obstack_chunk *prev;	/* address of prior chunk or NULL */
+  char	contents[4];		/* objects begin here */
+};
+
+struct obstack		/* control current object in current chunk */
+{
+  long	chunk_size;		/* preferred size to allocate chunks in */
+  struct _obstack_chunk *chunk;	/* address of current struct obstack_chunk */
+  char	*object_base;		/* address of object we are building */
+  char	*next_free;		/* where to add next char to current object */
+  char	*chunk_limit;		/* address of char after current chunk */
+  union
+  {
+    PTR_INT_TYPE tempint;
+    void *tempptr;
+  } temp;			/* Temporary for some macros.  */
+  int   alignment_mask;		/* Mask of alignment for each object. */
+  /* These prototypes vary based on `use_extra_arg', and we use
+     casts to the prototypeless function type in all assignments,
+     but having prototypes here quiets -Wstrict-prototypes.  */
+  struct _obstack_chunk *(*chunkfun) (void *, long);
+  void (*freefun) (void *, struct _obstack_chunk *);
+  void *extra_arg;		/* first arg for chunk alloc/dealloc funcs */
+  unsigned use_extra_arg:1;	/* chunk alloc/dealloc funcs take extra arg */
+  unsigned maybe_empty_object:1;/* There is a possibility that the current
+				   chunk contains a zero-length object.  This
+				   prevents freeing the chunk if we allocate
+				   a bigger chunk to replace it. */
+  unsigned alloc_failed:1;	/* No longer used, as we now call the failed
+				   handler on error, but retained for binary
+				   compatibility.  */
+};
+
+/* Declare the external functions we use; they are in obstack.c.  */
+
+extern void _obstack_newchunk (struct obstack *, int);
+extern int _obstack_begin (struct obstack *, int, int,
+			    void *(*) (long), void (*) (void *));
+extern int _obstack_begin_1 (struct obstack *, int, int,
+			     void *(*) (void *, long),
+			     void (*) (void *, void *), void *);
+extern int _obstack_memory_used (struct obstack *);
+
+void obstack_free (struct obstack *, void *);
+
+
+/* Error handler called when `obstack_chunk_alloc' failed to allocate
+   more memory.  This can be set to a user defined function which
+   should either abort gracefully or use longjump - but shouldn't
+   return.  The default action is to print a message and abort.  */
+extern void (*obstack_alloc_failed_handler) (void);
+
+/* Pointer to beginning of object being allocated or to be allocated next.
+   Note that this might not be the final address of the object
+   because a new chunk might be needed to hold the final size.  */
+
+#define obstack_base(h) ((void *) (h)->object_base)
+
+/* Size for allocating ordinary chunks.  */
+
+#define obstack_chunk_size(h) ((h)->chunk_size)
+
+/* Pointer to next byte not yet allocated in current chunk.  */
+
+#define obstack_next_free(h)	((h)->next_free)
+
+/* Mask specifying low bits that should be clear in address of an object.  */
+
+#define obstack_alignment_mask(h) ((h)->alignment_mask)
+
+/* To prevent prototype warnings provide complete argument list.  */
+#define obstack_init(h)						\
+  _obstack_begin ((h), 0, 0,					\
+		  (void *(*) (long)) obstack_chunk_alloc,	\
+		  (void (*) (void *)) obstack_chunk_free)
+
+#define obstack_begin(h, size)					\
+  _obstack_begin ((h), (size), 0,				\
+		  (void *(*) (long)) obstack_chunk_alloc,	\
+		  (void (*) (void *)) obstack_chunk_free)
+
+#define obstack_specify_allocation(h, size, alignment, chunkfun, freefun)  \
+  _obstack_begin ((h), (size), (alignment),				   \
+		  (void *(*) (long)) (chunkfun),			   \
+		  (void (*) (void *)) (freefun))
+
+#define obstack_specify_allocation_with_arg(h, size, alignment, chunkfun, freefun, arg) \
+  _obstack_begin_1 ((h), (size), (alignment),				\
+		    (void *(*) (void *, long)) (chunkfun),		\
+		    (void (*) (void *, void *)) (freefun), (arg))
+
+#define obstack_chunkfun(h, newchunkfun) \
+  ((h) -> chunkfun = (struct _obstack_chunk *(*)(void *, long)) (newchunkfun))
+
+#define obstack_freefun(h, newfreefun) \
+  ((h) -> freefun = (void (*)(void *, struct _obstack_chunk *)) (newfreefun))
+
+#define obstack_1grow_fast(h,achar) (*((h)->next_free)++ = (achar))
+
+#define obstack_blank_fast(h,n) ((h)->next_free += (n))
+
+#define obstack_memory_used(h) _obstack_memory_used (h)
+
+#if defined __GNUC__ && defined __STDC__ && __STDC__
+/* NextStep 2.0 cc is really gcc 1.93 but it defines __GNUC__ = 2 and
+   does not implement __extension__.  But that compiler doesn't define
+   __GNUC_MINOR__.  */
+# if __GNUC__ < 2 || (__NeXT__ && !__GNUC_MINOR__)
+#  define __extension__
+# endif
+
+/* For GNU C, if not -traditional,
+   we can define these macros to compute all args only once
+   without using a global variable.
+   Also, we can avoid using the `temp' slot, to make faster code.  */
+
+# define obstack_object_size(OBSTACK)					\
+  __extension__								\
+  ({ struct obstack const *__o = (OBSTACK);				\
+     (unsigned) (__o->next_free - __o->object_base); })
+
+# define obstack_room(OBSTACK)						\
+  __extension__								\
+  ({ struct obstack const *__o = (OBSTACK);				\
+     (unsigned) (__o->chunk_limit - __o->next_free); })
+
+# define obstack_make_room(OBSTACK,length)				\
+__extension__								\
+({ struct obstack *__o = (OBSTACK);					\
+   int __len = (length);						\
+   if (__o->chunk_limit - __o->next_free < __len)			\
+     _obstack_newchunk (__o, __len);					\
+   (void) 0; })
+
+# define obstack_empty_p(OBSTACK)					\
+  __extension__								\
+  ({ struct obstack const *__o = (OBSTACK);				\
+     (__o->chunk->prev == 0						\
+      && __o->next_free == __PTR_ALIGN ((char *) __o->chunk,		\
+					__o->chunk->contents,		\
+					__o->alignment_mask)); })
+
+# define obstack_grow(OBSTACK,where,length)				\
+__extension__								\
+({ struct obstack *__o = (OBSTACK);					\
+   int __len = (length);						\
+   if (__o->next_free + __len > __o->chunk_limit)			\
+     _obstack_newchunk (__o, __len);					\
+   memcpy (__o->next_free, where, __len);				\
+   __o->next_free += __len;						\
+   (void) 0; })
+
+# define obstack_grow0(OBSTACK,where,length)				\
+__extension__								\
+({ struct obstack *__o = (OBSTACK);					\
+   int __len = (length);						\
+   if (__o->next_free + __len + 1 > __o->chunk_limit)			\
+     _obstack_newchunk (__o, __len + 1);				\
+   memcpy (__o->next_free, where, __len);				\
+   __o->next_free += __len;						\
+   *(__o->next_free)++ = 0;						\
+   (void) 0; })
+
+# define obstack_1grow(OBSTACK,datum)					\
+__extension__								\
+({ struct obstack *__o = (OBSTACK);					\
+   if (__o->next_free + 1 > __o->chunk_limit)				\
+     _obstack_newchunk (__o, 1);					\
+   obstack_1grow_fast (__o, datum);					\
+   (void) 0; })
+
+/* These assume that the obstack alignment is good enough for pointers
+   or ints, and that the data added so far to the current object
+   shares that much alignment.  */
+
+# define obstack_ptr_grow(OBSTACK,datum)				\
+__extension__								\
+({ struct obstack *__o = (OBSTACK);					\
+   if (__o->next_free + sizeof (void *) > __o->chunk_limit)		\
+     _obstack_newchunk (__o, sizeof (void *));				\
+   obstack_ptr_grow_fast (__o, datum); })				\
+
+# define obstack_int_grow(OBSTACK,datum)				\
+__extension__								\
+({ struct obstack *__o = (OBSTACK);					\
+   if (__o->next_free + sizeof (int) > __o->chunk_limit)		\
+     _obstack_newchunk (__o, sizeof (int));				\
+   obstack_int_grow_fast (__o, datum); })
+
+# define obstack_ptr_grow_fast(OBSTACK,aptr)				\
+__extension__								\
+({ struct obstack *__o1 = (OBSTACK);					\
+   *(const void **) __o1->next_free = (aptr);				\
+   __o1->next_free += sizeof (const void *);				\
+   (void) 0; })
+
+# define obstack_int_grow_fast(OBSTACK,aint)				\
+__extension__								\
+({ struct obstack *__o1 = (OBSTACK);					\
+   *(int *) __o1->next_free = (aint);					\
+   __o1->next_free += sizeof (int);					\
+   (void) 0; })
+
+# define obstack_blank(OBSTACK,length)					\
+__extension__								\
+({ struct obstack *__o = (OBSTACK);					\
+   int __len = (length);						\
+   if (__o->chunk_limit - __o->next_free < __len)			\
+     _obstack_newchunk (__o, __len);					\
+   obstack_blank_fast (__o, __len);					\
+   (void) 0; })
+
+# define obstack_alloc(OBSTACK,length)					\
+__extension__								\
+({ struct obstack *__h = (OBSTACK);					\
+   obstack_blank (__h, (length));					\
+   obstack_finish (__h); })
+
+# define obstack_copy(OBSTACK,where,length)				\
+__extension__								\
+({ struct obstack *__h = (OBSTACK);					\
+   obstack_grow (__h, (where), (length));				\
+   obstack_finish (__h); })
+
+# define obstack_copy0(OBSTACK,where,length)				\
+__extension__								\
+({ struct obstack *__h = (OBSTACK);					\
+   obstack_grow0 (__h, (where), (length));				\
+   obstack_finish (__h); })
+
+/* The local variable is named __o1 to avoid a name conflict
+   when obstack_blank is called.  */
+# define obstack_finish(OBSTACK)					\
+__extension__								\
+({ struct obstack *__o1 = (OBSTACK);					\
+   void *__value = (void *) __o1->object_base;				\
+   if (__o1->next_free == __value)					\
+     __o1->maybe_empty_object = 1;					\
+   __o1->next_free							\
+     = __PTR_ALIGN (__o1->object_base, __o1->next_free,			\
+		    __o1->alignment_mask);				\
+   if (__o1->next_free - (char *)__o1->chunk				\
+       > __o1->chunk_limit - (char *)__o1->chunk)			\
+     __o1->next_free = __o1->chunk_limit;				\
+   __o1->object_base = __o1->next_free;					\
+   __value; })
+
+# define obstack_free(OBSTACK, OBJ)					\
+__extension__								\
+({ struct obstack *__o = (OBSTACK);					\
+   void *__obj = (OBJ);							\
+   if (__obj > (void *)__o->chunk && __obj < (void *)__o->chunk_limit)  \
+     __o->next_free = __o->object_base = (char *)__obj;			\
+   else (obstack_free) (__o, __obj); })
+
+#else /* not __GNUC__ or not __STDC__ */
+
+# define obstack_object_size(h) \
+ (unsigned) ((h)->next_free - (h)->object_base)
+
+# define obstack_room(h)		\
+ (unsigned) ((h)->chunk_limit - (h)->next_free)
+
+# define obstack_empty_p(h) \
+ ((h)->chunk->prev == 0							\
+  && (h)->next_free == __PTR_ALIGN ((char *) (h)->chunk,		\
+				    (h)->chunk->contents,		\
+				    (h)->alignment_mask))
+
+/* Note that the call to _obstack_newchunk is enclosed in (..., 0)
+   so that we can avoid having void expressions
+   in the arms of the conditional expression.
+   Casting the third operand to void was tried before,
+   but some compilers won't accept it.  */
+
+# define obstack_make_room(h,length)					\
+( (h)->temp.tempint = (length),						\
+  (((h)->next_free + (h)->temp.tempint > (h)->chunk_limit)		\
+   ? (_obstack_newchunk ((h), (h)->temp.tempint), 0) : 0))
+
+# define obstack_grow(h,where,length)					\
+( (h)->temp.tempint = (length),						\
+  (((h)->next_free + (h)->temp.tempint > (h)->chunk_limit)		\
+   ? (_obstack_newchunk ((h), (h)->temp.tempint), 0) : 0),		\
+  memcpy ((h)->next_free, where, (h)->temp.tempint),			\
+  (h)->next_free += (h)->temp.tempint)
+
+# define obstack_grow0(h,where,length)					\
+( (h)->temp.tempint = (length),						\
+  (((h)->next_free + (h)->temp.tempint + 1 > (h)->chunk_limit)		\
+   ? (_obstack_newchunk ((h), (h)->temp.tempint + 1), 0) : 0),		\
+  memcpy ((h)->next_free, where, (h)->temp.tempint),			\
+  (h)->next_free += (h)->temp.tempint,					\
+  *((h)->next_free)++ = 0)
+
+# define obstack_1grow(h,datum)						\
+( (((h)->next_free + 1 > (h)->chunk_limit)				\
+   ? (_obstack_newchunk ((h), 1), 0) : 0),				\
+  obstack_1grow_fast (h, datum))
+
+# define obstack_ptr_grow(h,datum)					\
+( (((h)->next_free + sizeof (char *) > (h)->chunk_limit)		\
+   ? (_obstack_newchunk ((h), sizeof (char *)), 0) : 0),		\
+  obstack_ptr_grow_fast (h, datum))
+
+# define obstack_int_grow(h,datum)					\
+( (((h)->next_free + sizeof (int) > (h)->chunk_limit)			\
+   ? (_obstack_newchunk ((h), sizeof (int)), 0) : 0),			\
+  obstack_int_grow_fast (h, datum))
+
+# define obstack_ptr_grow_fast(h,aptr)					\
+  (((const void **) ((h)->next_free += sizeof (void *)))[-1] = (aptr))
+
+# define obstack_int_grow_fast(h,aint)					\
+  (((int *) ((h)->next_free += sizeof (int)))[-1] = (aint))
+
+# define obstack_blank(h,length)					\
+( (h)->temp.tempint = (length),						\
+  (((h)->chunk_limit - (h)->next_free < (h)->temp.tempint)		\
+   ? (_obstack_newchunk ((h), (h)->temp.tempint), 0) : 0),		\
+  obstack_blank_fast (h, (h)->temp.tempint))
+
+# define obstack_alloc(h,length)					\
+ (obstack_blank ((h), (length)), obstack_finish ((h)))
+
+# define obstack_copy(h,where,length)					\
+ (obstack_grow ((h), (where), (length)), obstack_finish ((h)))
+
+# define obstack_copy0(h,where,length)					\
+ (obstack_grow0 ((h), (where), (length)), obstack_finish ((h)))
+
+# define obstack_finish(h)						\
+( ((h)->next_free == (h)->object_base					\
+   ? (((h)->maybe_empty_object = 1), 0)					\
+   : 0),								\
+  (h)->temp.tempptr = (h)->object_base,					\
+  (h)->next_free							\
+    = __PTR_ALIGN ((h)->object_base, (h)->next_free,			\
+		   (h)->alignment_mask),				\
+  (((h)->next_free - (char *) (h)->chunk				\
+    > (h)->chunk_limit - (char *) (h)->chunk)				\
+   ? ((h)->next_free = (h)->chunk_limit) : 0),				\
+  (h)->object_base = (h)->next_free,					\
+  (h)->temp.tempptr)
+
+# define obstack_free(h,obj)						\
+( (h)->temp.tempint = (char *) (obj) - (char *) (h)->chunk,		\
+  ((((h)->temp.tempint > 0						\
+    && (h)->temp.tempint < (h)->chunk_limit - (char *) (h)->chunk))	\
+   ? (int) ((h)->next_free = (h)->object_base				\
+	    = (h)->temp.tempint + (char *) (h)->chunk)			\
+   : (((obstack_free) ((h), (h)->temp.tempint + (char *) (h)->chunk), 0), 0)))
+
+#endif /* not __GNUC__ or not __STDC__ */
+
+#ifdef __cplusplus
+}	/* C++ */
+#endif
+
+#endif /* obstack.h */
diff --git a/compat/precompose_utf8.c b/compat/precompose_utf8.c
new file mode 100644
index 0000000..d40d1b3
--- /dev/null
+++ b/compat/precompose_utf8.c
@@ -0,0 +1,190 @@
+/*
+ * Converts filenames from decomposed unicode into precomposed unicode.
+ * Used on MacOS X.
+*/
+
+
+#define PRECOMPOSE_UNICODE_C
+
+#include "cache.h"
+#include "utf8.h"
+#include "precompose_utf8.h"
+
+typedef char *iconv_ibp;
+const static char *repo_encoding = "UTF-8";
+const static char *path_encoding = "UTF-8-MAC";
+
+
+static size_t has_utf8(const char *s, size_t maxlen, size_t *strlen_c)
+{
+	const uint8_t *utf8p = (const uint8_t*) s;
+	size_t strlen_chars = 0;
+	size_t ret = 0;
+
+	if ((!utf8p) || (!*utf8p)) {
+		return 0;
+	}
+
+	while((*utf8p) && maxlen) {
+		if (*utf8p & 0x80)
+			ret++;
+		strlen_chars++;
+		utf8p++;
+		maxlen--;
+	}
+	if (strlen_c)
+		*strlen_c = strlen_chars;
+
+	return ret;
+}
+
+
+void probe_utf8_pathname_composition(char *path, int len)
+{
+	const static char *auml_nfc = "\xc3\xa4";
+	const static char *auml_nfd = "\x61\xcc\x88";
+	int output_fd;
+	if (precomposed_unicode != -1)
+		return; /* We found it defined in the global config, respect it */
+	path[len] = 0;
+	strcpy(path + len, auml_nfc);
+	output_fd = open(path, O_CREAT|O_EXCL|O_RDWR, 0600);
+	if (output_fd >=0) {
+		close(output_fd);
+		path[len] = 0;
+		strcpy(path + len, auml_nfd);
+		/* Indicate to the user, that we can configure it to true */
+		if (0 == access(path, R_OK))
+			git_config_set("core.precomposeunicode", "false");
+			/* To be backward compatible, set precomposed_unicode to 0 */
+		precomposed_unicode = 0;
+		path[len] = 0;
+		strcpy(path + len, auml_nfc);
+		unlink(path);
+	}
+}
+
+
+void precompose_argv(int argc, const char **argv)
+{
+	int i = 0;
+	const char *oldarg;
+	char *newarg;
+	iconv_t ic_precompose;
+
+	if (precomposed_unicode != 1)
+		return;
+
+	ic_precompose = iconv_open(repo_encoding, path_encoding);
+	if (ic_precompose == (iconv_t) -1)
+		return;
+
+	while (i < argc) {
+		size_t namelen;
+		oldarg = argv[i];
+		if (has_utf8(oldarg, (size_t)-1, &namelen)) {
+			newarg = reencode_string_iconv(oldarg, namelen, ic_precompose);
+			if (newarg)
+				argv[i] = newarg;
+		}
+		i++;
+	}
+	iconv_close(ic_precompose);
+}
+
+
+PREC_DIR *precompose_utf8_opendir(const char *dirname)
+{
+	PREC_DIR *prec_dir = xmalloc(sizeof(PREC_DIR));
+	prec_dir->dirent_nfc = xmalloc(sizeof(dirent_prec_psx));
+	prec_dir->dirent_nfc->max_name_len = sizeof(prec_dir->dirent_nfc->d_name);
+
+	prec_dir->dirp = opendir(dirname);
+	if (!prec_dir->dirp) {
+		free(prec_dir->dirent_nfc);
+		free(prec_dir);
+		return NULL;
+	} else {
+		int ret_errno = errno;
+		prec_dir->ic_precompose = iconv_open(repo_encoding, path_encoding);
+		/* if iconv_open() fails, die() in readdir() if needed */
+		errno = ret_errno;
+	}
+
+	return prec_dir;
+}
+
+struct dirent_prec_psx *precompose_utf8_readdir(PREC_DIR *prec_dir)
+{
+	struct dirent *res;
+	res = readdir(prec_dir->dirp);
+	if (res) {
+		size_t namelenz = strlen(res->d_name) + 1; /* \0 */
+		size_t new_maxlen = namelenz;
+
+		int ret_errno = errno;
+
+		if (new_maxlen > prec_dir->dirent_nfc->max_name_len) {
+			size_t new_len = sizeof(dirent_prec_psx) + new_maxlen -
+				sizeof(prec_dir->dirent_nfc->d_name);
+
+			prec_dir->dirent_nfc = xrealloc(prec_dir->dirent_nfc, new_len);
+			prec_dir->dirent_nfc->max_name_len = new_maxlen;
+		}
+
+		prec_dir->dirent_nfc->d_ino  = res->d_ino;
+		prec_dir->dirent_nfc->d_type = res->d_type;
+
+		if ((precomposed_unicode == 1) && has_utf8(res->d_name, (size_t)-1, NULL)) {
+			if (prec_dir->ic_precompose == (iconv_t)-1) {
+				die("iconv_open(%s,%s) failed, but needed:\n"
+						"    precomposed unicode is not supported.\n"
+						"    If you wnat to use decomposed unicode, run\n"
+						"    \"git config core.precomposeunicode false\"\n",
+						repo_encoding, path_encoding);
+			} else {
+				iconv_ibp	cp = (iconv_ibp)res->d_name;
+				size_t inleft = namelenz;
+				char *outpos = &prec_dir->dirent_nfc->d_name[0];
+				size_t outsz = prec_dir->dirent_nfc->max_name_len;
+				size_t cnt;
+				errno = 0;
+				cnt = iconv(prec_dir->ic_precompose, &cp, &inleft, &outpos, &outsz);
+				if (errno || inleft) {
+					/*
+					 * iconv() failed and errno could be E2BIG, EILSEQ, EINVAL, EBADF
+					 * MacOS X avoids illegal byte sequemces.
+					 * If they occur on a mounted drive (e.g. NFS) it is not worth to
+					 * die() for that, but rather let the user see the original name
+					*/
+					namelenz = 0; /* trigger strlcpy */
+				}
+			}
+		}
+		else
+			namelenz = 0;
+
+		if (!namelenz)
+			strlcpy(prec_dir->dirent_nfc->d_name, res->d_name,
+							prec_dir->dirent_nfc->max_name_len);
+
+		errno = ret_errno;
+		return prec_dir->dirent_nfc;
+	}
+	return NULL;
+}
+
+
+int precompose_utf8_closedir(PREC_DIR *prec_dir)
+{
+	int ret_value;
+	int ret_errno;
+	ret_value = closedir(prec_dir->dirp);
+	ret_errno = errno;
+	if (prec_dir->ic_precompose != (iconv_t)-1)
+		iconv_close(prec_dir->ic_precompose);
+	free(prec_dir->dirent_nfc);
+	free(prec_dir);
+	errno = ret_errno;
+	return ret_value;
+}
diff --git a/compat/precompose_utf8.h b/compat/precompose_utf8.h
new file mode 100644
index 0000000..3b73585
--- /dev/null
+++ b/compat/precompose_utf8.h
@@ -0,0 +1,45 @@
+#ifndef PRECOMPOSE_UNICODE_H
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <iconv.h>
+
+
+typedef struct dirent_prec_psx {
+	ino_t d_ino;            /* Posix */
+	size_t max_name_len;    /* See below */
+	unsigned char d_type;   /* available on all systems git runs on */
+
+	/*
+	 * See http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/dirent.h.html
+	 * NAME_MAX + 1 should be enough, but some systems have
+	 * NAME_MAX=255 and strlen(d_name) may return 508 or 510
+	 * Solution: allocate more when needed, see precompose_utf8_readdir()
+	 */
+	char   d_name[NAME_MAX+1];
+} dirent_prec_psx;
+
+
+typedef struct {
+	iconv_t ic_precompose;
+	DIR *dirp;
+	struct dirent_prec_psx *dirent_nfc;
+} PREC_DIR;
+
+void precompose_argv(int argc, const char **argv);
+void probe_utf8_pathname_composition(char *, int);
+
+PREC_DIR *precompose_utf8_opendir(const char *dirname);
+struct dirent_prec_psx *precompose_utf8_readdir(PREC_DIR *dirp);
+int precompose_utf8_closedir(PREC_DIR *dirp);
+
+#ifndef PRECOMPOSE_UNICODE_C
+#define dirent dirent_prec_psx
+#define opendir(n) precompose_utf8_opendir(n)
+#define readdir(d) precompose_utf8_readdir(d)
+#define closedir(d) precompose_utf8_closedir(d)
+#define DIR PREC_DIR
+#endif /* PRECOMPOSE_UNICODE_C */
+
+#define  PRECOMPOSE_UNICODE_H
+#endif /* PRECOMPOSE_UNICODE_H */
diff --git a/compat/qsort.c b/compat/qsort.c
index d93dce2..9574d53 100644
--- a/compat/qsort.c
+++ b/compat/qsort.c
@@ -55,7 +55,7 @@
 		msort_with_tmp(b, n, s, cmp, buf);
 	} else {
 		/* It's somewhat large, so malloc it.  */
-		char *tmp = malloc(size);
+		char *tmp = xmalloc(size);
 		msort_with_tmp(b, n, s, cmp, tmp);
 		free(tmp);
 	}
diff --git a/compat/regex/regcomp.c b/compat/regex/regcomp.c
new file mode 100644
index 0000000..8c96ed9
--- /dev/null
+++ b/compat/regex/regcomp.c
@@ -0,0 +1,3884 @@
+/* Extended regular expression matching and search library.
+   Copyright (C) 2002-2007,2009,2010 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA.  */
+
+static reg_errcode_t re_compile_internal (regex_t *preg, const char * pattern,
+					  size_t length, reg_syntax_t syntax);
+static void re_compile_fastmap_iter (regex_t *bufp,
+				     const re_dfastate_t *init_state,
+				     char *fastmap);
+static reg_errcode_t init_dfa (re_dfa_t *dfa, size_t pat_len);
+#ifdef RE_ENABLE_I18N
+static void free_charset (re_charset_t *cset);
+#endif /* RE_ENABLE_I18N */
+static void free_workarea_compile (regex_t *preg);
+static reg_errcode_t create_initial_state (re_dfa_t *dfa);
+#ifdef RE_ENABLE_I18N
+static void optimize_utf8 (re_dfa_t *dfa);
+#endif
+static reg_errcode_t analyze (regex_t *preg);
+static reg_errcode_t preorder (bin_tree_t *root,
+			       reg_errcode_t (fn (void *, bin_tree_t *)),
+			       void *extra);
+static reg_errcode_t postorder (bin_tree_t *root,
+				reg_errcode_t (fn (void *, bin_tree_t *)),
+				void *extra);
+static reg_errcode_t optimize_subexps (void *extra, bin_tree_t *node);
+static reg_errcode_t lower_subexps (void *extra, bin_tree_t *node);
+static bin_tree_t *lower_subexp (reg_errcode_t *err, regex_t *preg,
+				 bin_tree_t *node);
+static reg_errcode_t calc_first (void *extra, bin_tree_t *node);
+static reg_errcode_t calc_next (void *extra, bin_tree_t *node);
+static reg_errcode_t link_nfa_nodes (void *extra, bin_tree_t *node);
+static int duplicate_node (re_dfa_t *dfa, int org_idx, unsigned int constraint);
+static int search_duplicated_node (const re_dfa_t *dfa, int org_node,
+				   unsigned int constraint);
+static reg_errcode_t calc_eclosure (re_dfa_t *dfa);
+static reg_errcode_t calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa,
+					 int node, int root);
+static reg_errcode_t calc_inveclosure (re_dfa_t *dfa);
+static int fetch_number (re_string_t *input, re_token_t *token,
+			 reg_syntax_t syntax);
+static int peek_token (re_token_t *token, re_string_t *input,
+			reg_syntax_t syntax) internal_function;
+static bin_tree_t *parse (re_string_t *regexp, regex_t *preg,
+			  reg_syntax_t syntax, reg_errcode_t *err);
+static bin_tree_t *parse_reg_exp (re_string_t *regexp, regex_t *preg,
+				  re_token_t *token, reg_syntax_t syntax,
+				  int nest, reg_errcode_t *err);
+static bin_tree_t *parse_branch (re_string_t *regexp, regex_t *preg,
+				 re_token_t *token, reg_syntax_t syntax,
+				 int nest, reg_errcode_t *err);
+static bin_tree_t *parse_expression (re_string_t *regexp, regex_t *preg,
+				     re_token_t *token, reg_syntax_t syntax,
+				     int nest, reg_errcode_t *err);
+static bin_tree_t *parse_sub_exp (re_string_t *regexp, regex_t *preg,
+				  re_token_t *token, reg_syntax_t syntax,
+				  int nest, reg_errcode_t *err);
+static bin_tree_t *parse_dup_op (bin_tree_t *dup_elem, re_string_t *regexp,
+				 re_dfa_t *dfa, re_token_t *token,
+				 reg_syntax_t syntax, reg_errcode_t *err);
+static bin_tree_t *parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa,
+				      re_token_t *token, reg_syntax_t syntax,
+				      reg_errcode_t *err);
+static reg_errcode_t parse_bracket_element (bracket_elem_t *elem,
+					    re_string_t *regexp,
+					    re_token_t *token, int token_len,
+					    re_dfa_t *dfa,
+					    reg_syntax_t syntax,
+					    int accept_hyphen);
+static reg_errcode_t parse_bracket_symbol (bracket_elem_t *elem,
+					  re_string_t *regexp,
+					  re_token_t *token);
+#ifdef RE_ENABLE_I18N
+static reg_errcode_t build_equiv_class (bitset_t sbcset,
+					re_charset_t *mbcset,
+					int *equiv_class_alloc,
+					const unsigned char *name);
+static reg_errcode_t build_charclass (RE_TRANSLATE_TYPE trans,
+				      bitset_t sbcset,
+				      re_charset_t *mbcset,
+				      int *char_class_alloc,
+				      const char *class_name,
+				      reg_syntax_t syntax);
+#else  /* not RE_ENABLE_I18N */
+static reg_errcode_t build_equiv_class (bitset_t sbcset,
+					const unsigned char *name);
+static reg_errcode_t build_charclass (RE_TRANSLATE_TYPE trans,
+				      bitset_t sbcset,
+				      const char *class_name,
+				      reg_syntax_t syntax);
+#endif /* not RE_ENABLE_I18N */
+static bin_tree_t *build_charclass_op (re_dfa_t *dfa,
+				       RE_TRANSLATE_TYPE trans,
+				       const char *class_name,
+				       const char *extra,
+				       int non_match, reg_errcode_t *err);
+static bin_tree_t *create_tree (re_dfa_t *dfa,
+				bin_tree_t *left, bin_tree_t *right,
+				re_token_type_t type);
+static bin_tree_t *create_token_tree (re_dfa_t *dfa,
+				      bin_tree_t *left, bin_tree_t *right,
+				      const re_token_t *token);
+static bin_tree_t *duplicate_tree (const bin_tree_t *src, re_dfa_t *dfa);
+static void free_token (re_token_t *node);
+static reg_errcode_t free_tree (void *extra, bin_tree_t *node);
+static reg_errcode_t mark_opt_subexp (void *extra, bin_tree_t *node);
+
+/* This table gives an error message for each of the error codes listed
+   in regex.h.  Obviously the order here has to be same as there.
+   POSIX doesn't require that we do anything for REG_NOERROR,
+   but why not be nice?  */
+
+const char __re_error_msgid[] attribute_hidden =
+  {
+#define REG_NOERROR_IDX	0
+    gettext_noop ("Success")	/* REG_NOERROR */
+    "\0"
+#define REG_NOMATCH_IDX (REG_NOERROR_IDX + sizeof "Success")
+    gettext_noop ("No match")	/* REG_NOMATCH */
+    "\0"
+#define REG_BADPAT_IDX	(REG_NOMATCH_IDX + sizeof "No match")
+    gettext_noop ("Invalid regular expression") /* REG_BADPAT */
+    "\0"
+#define REG_ECOLLATE_IDX (REG_BADPAT_IDX + sizeof "Invalid regular expression")
+    gettext_noop ("Invalid collation character") /* REG_ECOLLATE */
+    "\0"
+#define REG_ECTYPE_IDX	(REG_ECOLLATE_IDX + sizeof "Invalid collation character")
+    gettext_noop ("Invalid character class name") /* REG_ECTYPE */
+    "\0"
+#define REG_EESCAPE_IDX	(REG_ECTYPE_IDX + sizeof "Invalid character class name")
+    gettext_noop ("Trailing backslash") /* REG_EESCAPE */
+    "\0"
+#define REG_ESUBREG_IDX	(REG_EESCAPE_IDX + sizeof "Trailing backslash")
+    gettext_noop ("Invalid back reference") /* REG_ESUBREG */
+    "\0"
+#define REG_EBRACK_IDX	(REG_ESUBREG_IDX + sizeof "Invalid back reference")
+    gettext_noop ("Unmatched [ or [^")	/* REG_EBRACK */
+    "\0"
+#define REG_EPAREN_IDX	(REG_EBRACK_IDX + sizeof "Unmatched [ or [^")
+    gettext_noop ("Unmatched ( or \\(") /* REG_EPAREN */
+    "\0"
+#define REG_EBRACE_IDX	(REG_EPAREN_IDX + sizeof "Unmatched ( or \\(")
+    gettext_noop ("Unmatched \\{") /* REG_EBRACE */
+    "\0"
+#define REG_BADBR_IDX	(REG_EBRACE_IDX + sizeof "Unmatched \\{")
+    gettext_noop ("Invalid content of \\{\\}") /* REG_BADBR */
+    "\0"
+#define REG_ERANGE_IDX	(REG_BADBR_IDX + sizeof "Invalid content of \\{\\}")
+    gettext_noop ("Invalid range end")	/* REG_ERANGE */
+    "\0"
+#define REG_ESPACE_IDX	(REG_ERANGE_IDX + sizeof "Invalid range end")
+    gettext_noop ("Memory exhausted") /* REG_ESPACE */
+    "\0"
+#define REG_BADRPT_IDX	(REG_ESPACE_IDX + sizeof "Memory exhausted")
+    gettext_noop ("Invalid preceding regular expression") /* REG_BADRPT */
+    "\0"
+#define REG_EEND_IDX	(REG_BADRPT_IDX + sizeof "Invalid preceding regular expression")
+    gettext_noop ("Premature end of regular expression") /* REG_EEND */
+    "\0"
+#define REG_ESIZE_IDX	(REG_EEND_IDX + sizeof "Premature end of regular expression")
+    gettext_noop ("Regular expression too big") /* REG_ESIZE */
+    "\0"
+#define REG_ERPAREN_IDX	(REG_ESIZE_IDX + sizeof "Regular expression too big")
+    gettext_noop ("Unmatched ) or \\)") /* REG_ERPAREN */
+  };
+
+const size_t __re_error_msgid_idx[] attribute_hidden =
+  {
+    REG_NOERROR_IDX,
+    REG_NOMATCH_IDX,
+    REG_BADPAT_IDX,
+    REG_ECOLLATE_IDX,
+    REG_ECTYPE_IDX,
+    REG_EESCAPE_IDX,
+    REG_ESUBREG_IDX,
+    REG_EBRACK_IDX,
+    REG_EPAREN_IDX,
+    REG_EBRACE_IDX,
+    REG_BADBR_IDX,
+    REG_ERANGE_IDX,
+    REG_ESPACE_IDX,
+    REG_BADRPT_IDX,
+    REG_EEND_IDX,
+    REG_ESIZE_IDX,
+    REG_ERPAREN_IDX
+  };
+
+/* Entry points for GNU code.  */
+
+
+#ifdef ZOS_USS
+
+/* For ZOS USS we must define btowc */
+
+wchar_t 
+btowc (int c)
+{
+   wchar_t wtmp[2];
+   char tmp[2];
+
+   tmp[0] = c;
+   tmp[1] = 0;
+
+   mbtowc (wtmp, tmp, 1);
+   return wtmp[0];
+}
+#endif
+
+/* re_compile_pattern is the GNU regular expression compiler: it
+   compiles PATTERN (of length LENGTH) and puts the result in BUFP.
+   Returns 0 if the pattern was valid, otherwise an error string.
+
+   Assumes the `allocated' (and perhaps `buffer') and `translate' fields
+   are set in BUFP on entry.  */
+
+const char *
+re_compile_pattern (const char *pattern,
+		    size_t length,
+		    struct re_pattern_buffer *bufp)
+{
+  reg_errcode_t ret;
+
+  /* And GNU code determines whether or not to get register information
+     by passing null for the REGS argument to re_match, etc., not by
+     setting no_sub, unless RE_NO_SUB is set.  */
+  bufp->no_sub = !!(re_syntax_options & RE_NO_SUB);
+
+  /* Match anchors at newline.  */
+  bufp->newline_anchor = 1;
+
+  ret = re_compile_internal (bufp, pattern, length, re_syntax_options);
+
+  if (!ret)
+    return NULL;
+  return gettext (__re_error_msgid + __re_error_msgid_idx[(int) ret]);
+}
+#ifdef _LIBC
+weak_alias (__re_compile_pattern, re_compile_pattern)
+#endif
+
+/* Set by `re_set_syntax' to the current regexp syntax to recognize.  Can
+   also be assigned to arbitrarily: each pattern buffer stores its own
+   syntax, so it can be changed between regex compilations.  */
+/* This has no initializer because initialized variables in Emacs
+   become read-only after dumping.  */
+reg_syntax_t re_syntax_options;
+
+
+/* Specify the precise syntax of regexps for compilation.  This provides
+   for compatibility for various utilities which historically have
+   different, incompatible syntaxes.
+
+   The argument SYNTAX is a bit mask comprised of the various bits
+   defined in regex.h.  We return the old syntax.  */
+
+reg_syntax_t
+re_set_syntax (reg_syntax_t syntax)
+{
+  reg_syntax_t ret = re_syntax_options;
+
+  re_syntax_options = syntax;
+  return ret;
+}
+#ifdef _LIBC
+weak_alias (__re_set_syntax, re_set_syntax)
+#endif
+
+int
+re_compile_fastmap (struct re_pattern_buffer *bufp)
+{
+  re_dfa_t *dfa = (re_dfa_t *) bufp->buffer;
+  char *fastmap = bufp->fastmap;
+
+  memset (fastmap, '\0', sizeof (char) * SBC_MAX);
+  re_compile_fastmap_iter (bufp, dfa->init_state, fastmap);
+  if (dfa->init_state != dfa->init_state_word)
+    re_compile_fastmap_iter (bufp, dfa->init_state_word, fastmap);
+  if (dfa->init_state != dfa->init_state_nl)
+    re_compile_fastmap_iter (bufp, dfa->init_state_nl, fastmap);
+  if (dfa->init_state != dfa->init_state_begbuf)
+    re_compile_fastmap_iter (bufp, dfa->init_state_begbuf, fastmap);
+  bufp->fastmap_accurate = 1;
+  return 0;
+}
+#ifdef _LIBC
+weak_alias (__re_compile_fastmap, re_compile_fastmap)
+#endif
+
+static inline void
+__attribute ((always_inline))
+re_set_fastmap (char *fastmap, int icase, int ch)
+{
+  fastmap[ch] = 1;
+  if (icase)
+    fastmap[tolower (ch)] = 1;
+}
+
+/* Helper function for re_compile_fastmap.
+   Compile fastmap for the initial_state INIT_STATE.  */
+
+static void
+re_compile_fastmap_iter (regex_t *bufp, const re_dfastate_t *init_state,
+			 char *fastmap)
+{
+  volatile re_dfa_t *dfa = (re_dfa_t *) bufp->buffer;
+  int node_cnt;
+  int icase = (dfa->mb_cur_max == 1 && (bufp->syntax & RE_ICASE));
+  for (node_cnt = 0; node_cnt < init_state->nodes.nelem; ++node_cnt)
+    {
+      int node = init_state->nodes.elems[node_cnt];
+      re_token_type_t type = dfa->nodes[node].type;
+
+      if (type == CHARACTER)
+	{
+	  re_set_fastmap (fastmap, icase, dfa->nodes[node].opr.c);
+#ifdef RE_ENABLE_I18N
+	  if ((bufp->syntax & RE_ICASE) && dfa->mb_cur_max > 1)
+	    {
+	      unsigned char *buf = re_malloc (unsigned char, dfa->mb_cur_max), *p;
+	      wchar_t wc;
+	      mbstate_t state;
+
+	      p = buf;
+	      *p++ = dfa->nodes[node].opr.c;
+	      while (++node < dfa->nodes_len
+		     &&	dfa->nodes[node].type == CHARACTER
+		     && dfa->nodes[node].mb_partial)
+		*p++ = dfa->nodes[node].opr.c;
+	      memset (&state, '\0', sizeof (state));
+	      if (__mbrtowc (&wc, (const char *) buf, p - buf,
+			     &state) == p - buf
+		  && (__wcrtomb ((char *) buf, towlower (wc), &state)
+		      != (size_t) -1))
+		re_set_fastmap (fastmap, 0, buf[0]);
+	      re_free (buf);
+	    }
+#endif
+	}
+      else if (type == SIMPLE_BRACKET)
+	{
+	  int i, ch;
+	  for (i = 0, ch = 0; i < BITSET_WORDS; ++i)
+	    {
+	      int j;
+	      bitset_word_t w = dfa->nodes[node].opr.sbcset[i];
+	      for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch)
+		if (w & ((bitset_word_t) 1 << j))
+		  re_set_fastmap (fastmap, icase, ch);
+	    }
+	}
+#ifdef RE_ENABLE_I18N
+      else if (type == COMPLEX_BRACKET)
+	{
+	  re_charset_t *cset = dfa->nodes[node].opr.mbcset;
+	  int i;
+
+# ifdef _LIBC
+	  /* See if we have to try all bytes which start multiple collation
+	     elements.
+	     e.g. In da_DK, we want to catch 'a' since "aa" is a valid
+		  collation element, and don't catch 'b' since 'b' is
+		  the only collation element which starts from 'b' (and
+		  it is caught by SIMPLE_BRACKET).  */
+	      if (_NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES) != 0
+		  && (cset->ncoll_syms || cset->nranges))
+		{
+		  const int32_t *table = (const int32_t *)
+		    _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+		  for (i = 0; i < SBC_MAX; ++i)
+		    if (table[i] < 0)
+		      re_set_fastmap (fastmap, icase, i);
+		}
+# endif /* _LIBC */
+
+	  /* See if we have to start the match at all multibyte characters,
+	     i.e. where we would not find an invalid sequence.  This only
+	     applies to multibyte character sets; for single byte character
+	     sets, the SIMPLE_BRACKET again suffices.  */
+	  if (dfa->mb_cur_max > 1
+	      && (cset->nchar_classes || cset->non_match || cset->nranges
+# ifdef _LIBC
+		  || cset->nequiv_classes
+# endif /* _LIBC */
+		 ))
+	    {
+	      unsigned char c = 0;
+	      do
+		{
+		  mbstate_t mbs;
+		  memset (&mbs, 0, sizeof (mbs));
+		  if (__mbrtowc (NULL, (char *) &c, 1, &mbs) == (size_t) -2)
+		    re_set_fastmap (fastmap, false, (int) c);
+		}
+	      while (++c != 0);
+	    }
+
+	  else
+	    {
+	      /* ... Else catch all bytes which can start the mbchars.  */
+	      for (i = 0; i < cset->nmbchars; ++i)
+		{
+		  char buf[256];
+		  mbstate_t state;
+		  memset (&state, '\0', sizeof (state));
+		  if (__wcrtomb (buf, cset->mbchars[i], &state) != (size_t) -1)
+		    re_set_fastmap (fastmap, icase, *(unsigned char *) buf);
+		  if ((bufp->syntax & RE_ICASE) && dfa->mb_cur_max > 1)
+		    {
+		      if (__wcrtomb (buf, towlower (cset->mbchars[i]), &state)
+			  != (size_t) -1)
+			re_set_fastmap (fastmap, false, *(unsigned char *) buf);
+		    }
+		}
+	    }
+	}
+#endif /* RE_ENABLE_I18N */
+      else if (type == OP_PERIOD
+#ifdef RE_ENABLE_I18N
+	       || type == OP_UTF8_PERIOD
+#endif /* RE_ENABLE_I18N */
+	       || type == END_OF_RE)
+	{
+	  memset (fastmap, '\1', sizeof (char) * SBC_MAX);
+	  if (type == END_OF_RE)
+	    bufp->can_be_null = 1;
+	  return;
+	}
+    }
+}
+
+/* Entry point for POSIX code.  */
+/* regcomp takes a regular expression as a string and compiles it.
+
+   PREG is a regex_t *.  We do not expect any fields to be initialized,
+   since POSIX says we shouldn't.  Thus, we set
+
+     `buffer' to the compiled pattern;
+     `used' to the length of the compiled pattern;
+     `syntax' to RE_SYNTAX_POSIX_EXTENDED if the
+       REG_EXTENDED bit in CFLAGS is set; otherwise, to
+       RE_SYNTAX_POSIX_BASIC;
+     `newline_anchor' to REG_NEWLINE being set in CFLAGS;
+     `fastmap' to an allocated space for the fastmap;
+     `fastmap_accurate' to zero;
+     `re_nsub' to the number of subexpressions in PATTERN.
+
+   PATTERN is the address of the pattern string.
+
+   CFLAGS is a series of bits which affect compilation.
+
+     If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we
+     use POSIX basic syntax.
+
+     If REG_NEWLINE is set, then . and [^...] don't match newline.
+     Also, regexec will try a match beginning after every newline.
+
+     If REG_ICASE is set, then we considers upper- and lowercase
+     versions of letters to be equivalent when matching.
+
+     If REG_NOSUB is set, then when PREG is passed to regexec, that
+     routine will report only success or failure, and nothing about the
+     registers.
+
+   It returns 0 if it succeeds, nonzero if it doesn't.  (See regex.h for
+   the return codes and their meanings.)  */
+
+int
+regcomp (regex_t *__restrict preg,
+	 const char *__restrict pattern,
+	 int cflags)
+{
+  reg_errcode_t ret;
+  reg_syntax_t syntax = ((cflags & REG_EXTENDED) ? RE_SYNTAX_POSIX_EXTENDED
+			 : RE_SYNTAX_POSIX_BASIC);
+
+  preg->buffer = NULL;
+  preg->allocated = 0;
+  preg->used = 0;
+
+  /* Try to allocate space for the fastmap.  */
+  preg->fastmap = re_malloc (char, SBC_MAX);
+  if (BE (preg->fastmap == NULL, 0))
+    return REG_ESPACE;
+
+  syntax |= (cflags & REG_ICASE) ? RE_ICASE : 0;
+
+  /* If REG_NEWLINE is set, newlines are treated differently.  */
+  if (cflags & REG_NEWLINE)
+    { /* REG_NEWLINE implies neither . nor [^...] match newline.  */
+      syntax &= ~RE_DOT_NEWLINE;
+      syntax |= RE_HAT_LISTS_NOT_NEWLINE;
+      /* It also changes the matching behavior.  */
+      preg->newline_anchor = 1;
+    }
+  else
+    preg->newline_anchor = 0;
+  preg->no_sub = !!(cflags & REG_NOSUB);
+  preg->translate = NULL;
+
+  ret = re_compile_internal (preg, pattern, strlen (pattern), syntax);
+
+  /* POSIX doesn't distinguish between an unmatched open-group and an
+     unmatched close-group: both are REG_EPAREN.  */
+  if (ret == REG_ERPAREN)
+    ret = REG_EPAREN;
+
+  /* We have already checked preg->fastmap != NULL.  */
+  if (BE (ret == REG_NOERROR, 1))
+    /* Compute the fastmap now, since regexec cannot modify the pattern
+       buffer.  This function never fails in this implementation.  */
+    (void) re_compile_fastmap (preg);
+  else
+    {
+      /* Some error occurred while compiling the expression.  */
+      re_free (preg->fastmap);
+      preg->fastmap = NULL;
+    }
+
+  return (int) ret;
+}
+#ifdef _LIBC
+weak_alias (__regcomp, regcomp)
+#endif
+
+/* Returns a message corresponding to an error code, ERRCODE, returned
+   from either regcomp or regexec.   We don't use PREG here.  */
+
+size_t
+regerror(int errcode, const regex_t *__restrict preg,
+	 char *__restrict errbuf, size_t errbuf_size)
+{
+  const char *msg;
+  size_t msg_size;
+
+  if (BE (errcode < 0
+	  || errcode >= (int) (sizeof (__re_error_msgid_idx)
+			       / sizeof (__re_error_msgid_idx[0])), 0))
+    /* Only error codes returned by the rest of the code should be passed
+       to this routine.  If we are given anything else, or if other regex
+       code generates an invalid error code, then the program has a bug.
+       Dump core so we can fix it.  */
+    abort ();
+
+  msg = gettext (__re_error_msgid + __re_error_msgid_idx[errcode]);
+
+  msg_size = strlen (msg) + 1; /* Includes the null.  */
+
+  if (BE (errbuf_size != 0, 1))
+    {
+      if (BE (msg_size > errbuf_size, 0))
+	{
+	  memcpy (errbuf, msg, errbuf_size - 1);
+	  errbuf[errbuf_size - 1] = 0;
+	}
+      else
+	memcpy (errbuf, msg, msg_size);
+    }
+
+  return msg_size;
+}
+#ifdef _LIBC
+weak_alias (__regerror, regerror)
+#endif
+
+
+#ifdef RE_ENABLE_I18N
+/* This static array is used for the map to single-byte characters when
+   UTF-8 is used.  Otherwise we would allocate memory just to initialize
+   it the same all the time.  UTF-8 is the preferred encoding so this is
+   a worthwhile optimization.  */
+#if __GNUC__ >= 3
+static const bitset_t utf8_sb_map = {
+  /* Set the first 128 bits.  */
+  [0 ... 0x80 / BITSET_WORD_BITS - 1] = BITSET_WORD_MAX
+};
+#else /* ! (__GNUC__ >= 3) */
+static bitset_t utf8_sb_map;
+#endif /* __GNUC__ >= 3 */
+#endif /* RE_ENABLE_I18N */
+
+
+static void
+free_dfa_content (re_dfa_t *dfa)
+{
+  int i, j;
+
+  if (dfa->nodes)
+    for (i = 0; i < dfa->nodes_len; ++i)
+      free_token (dfa->nodes + i);
+  re_free (dfa->nexts);
+  for (i = 0; i < dfa->nodes_len; ++i)
+    {
+      if (dfa->eclosures != NULL)
+	re_node_set_free (dfa->eclosures + i);
+      if (dfa->inveclosures != NULL)
+	re_node_set_free (dfa->inveclosures + i);
+      if (dfa->edests != NULL)
+	re_node_set_free (dfa->edests + i);
+    }
+  re_free (dfa->edests);
+  re_free (dfa->eclosures);
+  re_free (dfa->inveclosures);
+  re_free (dfa->nodes);
+
+  if (dfa->state_table)
+    for (i = 0; i <= dfa->state_hash_mask; ++i)
+      {
+	struct re_state_table_entry *entry = dfa->state_table + i;
+	for (j = 0; j < entry->num; ++j)
+	  {
+	    re_dfastate_t *state = entry->array[j];
+	    free_state (state);
+	  }
+	re_free (entry->array);
+      }
+  re_free (dfa->state_table);
+#ifdef RE_ENABLE_I18N
+  if (dfa->sb_char != utf8_sb_map)
+    re_free (dfa->sb_char);
+#endif
+  re_free (dfa->subexp_map);
+#ifdef DEBUG
+  re_free (dfa->re_str);
+#endif
+
+  re_free (dfa);
+}
+
+
+/* Free dynamically allocated space used by PREG.  */
+
+void
+regfree (regex_t *preg)
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  if (BE (dfa != NULL, 1))
+    free_dfa_content (dfa);
+  preg->buffer = NULL;
+  preg->allocated = 0;
+
+  re_free (preg->fastmap);
+  preg->fastmap = NULL;
+
+  re_free (preg->translate);
+  preg->translate = NULL;
+}
+#ifdef _LIBC
+weak_alias (__regfree, regfree)
+#endif
+
+/* Entry points compatible with 4.2 BSD regex library.  We don't define
+   them unless specifically requested.  */
+
+#if defined _REGEX_RE_COMP || defined _LIBC
+
+/* BSD has one and only one pattern buffer.  */
+static struct re_pattern_buffer re_comp_buf;
+
+char *
+# ifdef _LIBC
+/* Make these definitions weak in libc, so POSIX programs can redefine
+   these names if they don't use our functions, and still use
+   regcomp/regexec above without link errors.  */
+weak_function
+# endif
+re_comp (s)
+     const char *s;
+{
+  reg_errcode_t ret;
+  char *fastmap;
+
+  if (!s)
+    {
+      if (!re_comp_buf.buffer)
+	return gettext ("No previous regular expression");
+      return 0;
+    }
+
+  if (re_comp_buf.buffer)
+    {
+      fastmap = re_comp_buf.fastmap;
+      re_comp_buf.fastmap = NULL;
+      __regfree (&re_comp_buf);
+      memset (&re_comp_buf, '\0', sizeof (re_comp_buf));
+      re_comp_buf.fastmap = fastmap;
+    }
+
+  if (re_comp_buf.fastmap == NULL)
+    {
+      re_comp_buf.fastmap = (char *) malloc (SBC_MAX);
+      if (re_comp_buf.fastmap == NULL)
+	return (char *) gettext (__re_error_msgid
+				 + __re_error_msgid_idx[(int) REG_ESPACE]);
+    }
+
+  /* Since `re_exec' always passes NULL for the `regs' argument, we
+     don't need to initialize the pattern buffer fields which affect it.  */
+
+  /* Match anchors at newlines.  */
+  re_comp_buf.newline_anchor = 1;
+
+  ret = re_compile_internal (&re_comp_buf, s, strlen (s), re_syntax_options);
+
+  if (!ret)
+    return NULL;
+
+  /* Yes, we're discarding `const' here if !HAVE_LIBINTL.  */
+  return (char *) gettext (__re_error_msgid + __re_error_msgid_idx[(int) ret]);
+}
+
+#ifdef _LIBC
+libc_freeres_fn (free_mem)
+{
+  __regfree (&re_comp_buf);
+}
+#endif
+
+#endif /* _REGEX_RE_COMP */
+
+/* Internal entry point.
+   Compile the regular expression PATTERN, whose length is LENGTH.
+   SYNTAX indicate regular expression's syntax.  */
+
+static reg_errcode_t
+re_compile_internal (regex_t *preg, const char * pattern, size_t length,
+		     reg_syntax_t syntax)
+{
+  reg_errcode_t err = REG_NOERROR;
+  re_dfa_t *dfa;
+  re_string_t regexp;
+
+  /* Initialize the pattern buffer.  */
+  preg->fastmap_accurate = 0;
+  preg->syntax = syntax;
+  preg->not_bol = preg->not_eol = 0;
+  preg->used = 0;
+  preg->re_nsub = 0;
+  preg->can_be_null = 0;
+  preg->regs_allocated = REGS_UNALLOCATED;
+
+  /* Initialize the dfa.  */
+  dfa = (re_dfa_t *) preg->buffer;
+  if (BE (preg->allocated < sizeof (re_dfa_t), 0))
+    {
+      /* If zero allocated, but buffer is non-null, try to realloc
+	 enough space.  This loses if buffer's address is bogus, but
+	 that is the user's responsibility.  If ->buffer is NULL this
+	 is a simple allocation.  */
+      dfa = re_realloc (preg->buffer, re_dfa_t, 1);
+      if (dfa == NULL)
+	return REG_ESPACE;
+      preg->allocated = sizeof (re_dfa_t);
+      preg->buffer = (unsigned char *) dfa;
+    }
+  preg->used = sizeof (re_dfa_t);
+
+  err = init_dfa (dfa, length);
+  if (BE (err != REG_NOERROR, 0))
+    {
+      free_dfa_content (dfa);
+      preg->buffer = NULL;
+      preg->allocated = 0;
+      return err;
+    }
+#ifdef DEBUG
+  /* Note: length+1 will not overflow since it is checked in init_dfa.  */
+  dfa->re_str = re_malloc (char, length + 1);
+  strncpy (dfa->re_str, pattern, length + 1);
+#endif
+
+  __libc_lock_init (dfa->lock);
+
+  err = re_string_construct (&regexp, pattern, length, preg->translate,
+			     syntax & RE_ICASE, dfa);
+  if (BE (err != REG_NOERROR, 0))
+    {
+    re_compile_internal_free_return:
+      free_workarea_compile (preg);
+      re_string_destruct (&regexp);
+      free_dfa_content (dfa);
+      preg->buffer = NULL;
+      preg->allocated = 0;
+      return err;
+    }
+
+  /* Parse the regular expression, and build a structure tree.  */
+  preg->re_nsub = 0;
+  dfa->str_tree = parse (&regexp, preg, syntax, &err);
+  if (BE (dfa->str_tree == NULL, 0))
+    goto re_compile_internal_free_return;
+
+  /* Analyze the tree and create the nfa.  */
+  err = analyze (preg);
+  if (BE (err != REG_NOERROR, 0))
+    goto re_compile_internal_free_return;
+
+#ifdef RE_ENABLE_I18N
+  /* If possible, do searching in single byte encoding to speed things up.  */
+  if (dfa->is_utf8 && !(syntax & RE_ICASE) && preg->translate == NULL)
+    optimize_utf8 (dfa);
+#endif
+
+  /* Then create the initial state of the dfa.  */
+  err = create_initial_state (dfa);
+
+  /* Release work areas.  */
+  free_workarea_compile (preg);
+  re_string_destruct (&regexp);
+
+  if (BE (err != REG_NOERROR, 0))
+    {
+      free_dfa_content (dfa);
+      preg->buffer = NULL;
+      preg->allocated = 0;
+    }
+
+  return err;
+}
+
+/* Initialize DFA.  We use the length of the regular expression PAT_LEN
+   as the initial length of some arrays.  */
+
+static reg_errcode_t
+init_dfa (re_dfa_t *dfa, size_t pat_len)
+{
+  unsigned int table_size;
+#ifndef _LIBC
+  char *codeset_name;
+#endif
+
+  memset (dfa, '\0', sizeof (re_dfa_t));
+
+  /* Force allocation of str_tree_storage the first time.  */
+  dfa->str_tree_storage_idx = BIN_TREE_STORAGE_SIZE;
+
+  /* Avoid overflows.  */
+  if (pat_len == SIZE_MAX)
+    return REG_ESPACE;
+
+  dfa->nodes_alloc = pat_len + 1;
+  dfa->nodes = re_malloc (re_token_t, dfa->nodes_alloc);
+
+  /*  table_size = 2 ^ ceil(log pat_len) */
+  for (table_size = 1; ; table_size <<= 1)
+    if (table_size > pat_len)
+      break;
+
+  dfa->state_table = calloc (sizeof (struct re_state_table_entry), table_size);
+  dfa->state_hash_mask = table_size - 1;
+
+  dfa->mb_cur_max = MB_CUR_MAX;
+#ifdef _LIBC
+  if (dfa->mb_cur_max == 6
+      && strcmp (_NL_CURRENT (LC_CTYPE, _NL_CTYPE_CODESET_NAME), "UTF-8") == 0)
+    dfa->is_utf8 = 1;
+  dfa->map_notascii = (_NL_CURRENT_WORD (LC_CTYPE, _NL_CTYPE_MAP_TO_NONASCII)
+		       != 0);
+#else
+# ifdef HAVE_LANGINFO_CODESET
+  codeset_name = nl_langinfo (CODESET);
+# else
+  codeset_name = getenv ("LC_ALL");
+  if (codeset_name == NULL || codeset_name[0] == '\0')
+    codeset_name = getenv ("LC_CTYPE");
+  if (codeset_name == NULL || codeset_name[0] == '\0')
+    codeset_name = getenv ("LANG");
+  if (codeset_name == NULL)
+    codeset_name = "";
+  else if (strchr (codeset_name, '.') !=  NULL)
+    codeset_name = strchr (codeset_name, '.') + 1;
+# endif
+
+  /* strcasecmp isn't a standard interface. brute force check */
+#if 0
+  if (strcasecmp (codeset_name, "UTF-8") == 0
+      || strcasecmp (codeset_name, "UTF8") == 0)
+    dfa->is_utf8 = 1;
+#else
+  if (   (codeset_name[0] == 'U' || codeset_name[0] == 'u')
+      && (codeset_name[1] == 'T' || codeset_name[1] == 't')
+      && (codeset_name[2] == 'F' || codeset_name[2] == 'f')
+      && (codeset_name[3] == '-'
+          ? codeset_name[4] == '8' && codeset_name[5] == '\0'
+          : codeset_name[3] == '8' && codeset_name[4] == '\0'))
+    dfa->is_utf8 = 1;
+#endif
+
+  /* We check exhaustively in the loop below if this charset is a
+     superset of ASCII.  */
+  dfa->map_notascii = 0;
+#endif
+
+#ifdef RE_ENABLE_I18N
+  if (dfa->mb_cur_max > 1)
+    {
+      if (dfa->is_utf8)
+        {
+#if !defined(__GNUC__) || __GNUC__ < 3
+	  static short utf8_sb_map_inited = 0;
+
+	  if (! utf8_sb_map_inited)
+	    {
+		int i;
+
+	  	utf8_sb_map_inited = 0;
+		for (i = 0; i <= 0x80 / BITSET_WORD_BITS - 1; i++)
+		  utf8_sb_map[i] = BITSET_WORD_MAX;
+	    }
+#endif
+	  dfa->sb_char = (re_bitset_ptr_t) utf8_sb_map;
+	}
+      else
+	{
+	  int i, j, ch;
+
+	  dfa->sb_char = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1);
+	  if (BE (dfa->sb_char == NULL, 0))
+	    return REG_ESPACE;
+
+	  /* Set the bits corresponding to single byte chars.  */
+	  for (i = 0, ch = 0; i < BITSET_WORDS; ++i)
+	    for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch)
+	      {
+		wint_t wch = __btowc (ch);
+		if (wch != WEOF)
+		  dfa->sb_char[i] |= (bitset_word_t) 1 << j;
+# ifndef _LIBC
+		if (isascii (ch) && wch != ch)
+		  dfa->map_notascii = 1;
+# endif
+	      }
+	}
+    }
+#endif
+
+  if (BE (dfa->nodes == NULL || dfa->state_table == NULL, 0))
+    return REG_ESPACE;
+  return REG_NOERROR;
+}
+
+/* Initialize WORD_CHAR table, which indicate which character is
+   "word".  In this case "word" means that it is the word construction
+   character used by some operators like "\<", "\>", etc.  */
+
+static void
+internal_function
+init_word_char (re_dfa_t *dfa)
+{
+  int i, j, ch;
+  dfa->word_ops_used = 1;
+  for (i = 0, ch = 0; i < BITSET_WORDS; ++i)
+    for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch)
+      if (isalnum (ch) || ch == '_')
+	dfa->word_char[i] |= (bitset_word_t) 1 << j;
+}
+
+/* Free the work area which are only used while compiling.  */
+
+static void
+free_workarea_compile (regex_t *preg)
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  bin_tree_storage_t *storage, *next;
+  for (storage = dfa->str_tree_storage; storage; storage = next)
+    {
+      next = storage->next;
+      re_free (storage);
+    }
+  dfa->str_tree_storage = NULL;
+  dfa->str_tree_storage_idx = BIN_TREE_STORAGE_SIZE;
+  dfa->str_tree = NULL;
+  re_free (dfa->org_indices);
+  dfa->org_indices = NULL;
+}
+
+/* Create initial states for all contexts.  */
+
+static reg_errcode_t
+create_initial_state (re_dfa_t *dfa)
+{
+  int first, i;
+  reg_errcode_t err;
+  re_node_set init_nodes;
+
+  /* Initial states have the epsilon closure of the node which is
+     the first node of the regular expression.  */
+  first = dfa->str_tree->first->node_idx;
+  dfa->init_node = first;
+  err = re_node_set_init_copy (&init_nodes, dfa->eclosures + first);
+  if (BE (err != REG_NOERROR, 0))
+    return err;
+
+  /* The back-references which are in initial states can epsilon transit,
+     since in this case all of the subexpressions can be null.
+     Then we add epsilon closures of the nodes which are the next nodes of
+     the back-references.  */
+  if (dfa->nbackref > 0)
+    for (i = 0; i < init_nodes.nelem; ++i)
+      {
+	int node_idx = init_nodes.elems[i];
+	re_token_type_t type = dfa->nodes[node_idx].type;
+
+	int clexp_idx;
+	if (type != OP_BACK_REF)
+	  continue;
+	for (clexp_idx = 0; clexp_idx < init_nodes.nelem; ++clexp_idx)
+	  {
+	    re_token_t *clexp_node;
+	    clexp_node = dfa->nodes + init_nodes.elems[clexp_idx];
+	    if (clexp_node->type == OP_CLOSE_SUBEXP
+		&& clexp_node->opr.idx == dfa->nodes[node_idx].opr.idx)
+	      break;
+	  }
+	if (clexp_idx == init_nodes.nelem)
+	  continue;
+
+	if (type == OP_BACK_REF)
+	  {
+	    int dest_idx = dfa->edests[node_idx].elems[0];
+	    if (!re_node_set_contains (&init_nodes, dest_idx))
+	      {
+		reg_errcode_t err = re_node_set_merge (&init_nodes,
+						       dfa->eclosures
+						       + dest_idx);
+		if (err != REG_NOERROR)
+		  return err;
+		i = 0;
+	      }
+	  }
+      }
+
+  /* It must be the first time to invoke acquire_state.  */
+  dfa->init_state = re_acquire_state_context (&err, dfa, &init_nodes, 0);
+  /* We don't check ERR here, since the initial state must not be NULL.  */
+  if (BE (dfa->init_state == NULL, 0))
+    return err;
+  if (dfa->init_state->has_constraint)
+    {
+      dfa->init_state_word = re_acquire_state_context (&err, dfa, &init_nodes,
+						       CONTEXT_WORD);
+      dfa->init_state_nl = re_acquire_state_context (&err, dfa, &init_nodes,
+						     CONTEXT_NEWLINE);
+      dfa->init_state_begbuf = re_acquire_state_context (&err, dfa,
+							 &init_nodes,
+							 CONTEXT_NEWLINE
+							 | CONTEXT_BEGBUF);
+      if (BE (dfa->init_state_word == NULL || dfa->init_state_nl == NULL
+	      || dfa->init_state_begbuf == NULL, 0))
+	return err;
+    }
+  else
+    dfa->init_state_word = dfa->init_state_nl
+      = dfa->init_state_begbuf = dfa->init_state;
+
+  re_node_set_free (&init_nodes);
+  return REG_NOERROR;
+}
+
+#ifdef RE_ENABLE_I18N
+/* If it is possible to do searching in single byte encoding instead of UTF-8
+   to speed things up, set dfa->mb_cur_max to 1, clear is_utf8 and change
+   DFA nodes where needed.  */
+
+static void
+optimize_utf8 (re_dfa_t *dfa)
+{
+  int node, i, mb_chars = 0, has_period = 0;
+
+  for (node = 0; node < dfa->nodes_len; ++node)
+    switch (dfa->nodes[node].type)
+      {
+      case CHARACTER:
+	if (dfa->nodes[node].opr.c >= 0x80)
+	  mb_chars = 1;
+	break;
+      case ANCHOR:
+	switch (dfa->nodes[node].opr.ctx_type)
+	  {
+	  case LINE_FIRST:
+	  case LINE_LAST:
+	  case BUF_FIRST:
+	  case BUF_LAST:
+	    break;
+	  default:
+	    /* Word anchors etc. cannot be handled.  It's okay to test
+	       opr.ctx_type since constraints (for all DFA nodes) are
+	       created by ORing one or more opr.ctx_type values.  */
+	    return;
+	  }
+	break;
+      case OP_PERIOD:
+	has_period = 1;
+	break;
+      case OP_BACK_REF:
+      case OP_ALT:
+      case END_OF_RE:
+      case OP_DUP_ASTERISK:
+      case OP_OPEN_SUBEXP:
+      case OP_CLOSE_SUBEXP:
+	break;
+      case COMPLEX_BRACKET:
+	return;
+      case SIMPLE_BRACKET:
+	/* Just double check.  The non-ASCII range starts at 0x80.  */
+	assert (0x80 % BITSET_WORD_BITS == 0);
+	for (i = 0x80 / BITSET_WORD_BITS; i < BITSET_WORDS; ++i)
+	  if (dfa->nodes[node].opr.sbcset[i])
+	    return;
+	break;
+      default:
+	abort ();
+      }
+
+  if (mb_chars || has_period)
+    for (node = 0; node < dfa->nodes_len; ++node)
+      {
+	if (dfa->nodes[node].type == CHARACTER
+	    && dfa->nodes[node].opr.c >= 0x80)
+	  dfa->nodes[node].mb_partial = 0;
+	else if (dfa->nodes[node].type == OP_PERIOD)
+	  dfa->nodes[node].type = OP_UTF8_PERIOD;
+      }
+
+  /* The search can be in single byte locale.  */
+  dfa->mb_cur_max = 1;
+  dfa->is_utf8 = 0;
+  dfa->has_mb_node = dfa->nbackref > 0 || has_period;
+}
+#endif
+
+/* Analyze the structure tree, and calculate "first", "next", "edest",
+   "eclosure", and "inveclosure".  */
+
+static reg_errcode_t
+analyze (regex_t *preg)
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  reg_errcode_t ret;
+
+  /* Allocate arrays.  */
+  dfa->nexts = re_malloc (int, dfa->nodes_alloc);
+  dfa->org_indices = re_malloc (int, dfa->nodes_alloc);
+  dfa->edests = re_malloc (re_node_set, dfa->nodes_alloc);
+  dfa->eclosures = re_malloc (re_node_set, dfa->nodes_alloc);
+  if (BE (dfa->nexts == NULL || dfa->org_indices == NULL || dfa->edests == NULL
+	  || dfa->eclosures == NULL, 0))
+    return REG_ESPACE;
+
+  dfa->subexp_map = re_malloc (int, preg->re_nsub);
+  if (dfa->subexp_map != NULL)
+    {
+      int i;
+      for (i = 0; i < preg->re_nsub; i++)
+	dfa->subexp_map[i] = i;
+      preorder (dfa->str_tree, optimize_subexps, dfa);
+      for (i = 0; i < preg->re_nsub; i++)
+	if (dfa->subexp_map[i] != i)
+	  break;
+      if (i == preg->re_nsub)
+	{
+	  free (dfa->subexp_map);
+	  dfa->subexp_map = NULL;
+	}
+    }
+
+  ret = postorder (dfa->str_tree, lower_subexps, preg);
+  if (BE (ret != REG_NOERROR, 0))
+    return ret;
+  ret = postorder (dfa->str_tree, calc_first, dfa);
+  if (BE (ret != REG_NOERROR, 0))
+    return ret;
+  preorder (dfa->str_tree, calc_next, dfa);
+  ret = preorder (dfa->str_tree, link_nfa_nodes, dfa);
+  if (BE (ret != REG_NOERROR, 0))
+    return ret;
+  ret = calc_eclosure (dfa);
+  if (BE (ret != REG_NOERROR, 0))
+    return ret;
+
+  /* We only need this during the prune_impossible_nodes pass in regexec.c;
+     skip it if p_i_n will not run, as calc_inveclosure can be quadratic.  */
+  if ((!preg->no_sub && preg->re_nsub > 0 && dfa->has_plural_match)
+      || dfa->nbackref)
+    {
+      dfa->inveclosures = re_malloc (re_node_set, dfa->nodes_len);
+      if (BE (dfa->inveclosures == NULL, 0))
+	return REG_ESPACE;
+      ret = calc_inveclosure (dfa);
+    }
+
+  return ret;
+}
+
+/* Our parse trees are very unbalanced, so we cannot use a stack to
+   implement parse tree visits.  Instead, we use parent pointers and
+   some hairy code in these two functions.  */
+static reg_errcode_t
+postorder (bin_tree_t *root, reg_errcode_t (fn (void *, bin_tree_t *)),
+	   void *extra)
+{
+  bin_tree_t *node, *prev;
+
+  for (node = root; ; )
+    {
+      /* Descend down the tree, preferably to the left (or to the right
+	 if that's the only child).  */
+      while (node->left || node->right)
+	if (node->left)
+	  node = node->left;
+	else
+	  node = node->right;
+
+      do
+	{
+	  reg_errcode_t err = fn (extra, node);
+	  if (BE (err != REG_NOERROR, 0))
+	    return err;
+	  if (node->parent == NULL)
+	    return REG_NOERROR;
+	  prev = node;
+	  node = node->parent;
+	}
+      /* Go up while we have a node that is reached from the right.  */
+      while (node->right == prev || node->right == NULL);
+      node = node->right;
+    }
+}
+
+static reg_errcode_t
+preorder (bin_tree_t *root, reg_errcode_t (fn (void *, bin_tree_t *)),
+	  void *extra)
+{
+  bin_tree_t *node;
+
+  for (node = root; ; )
+    {
+      reg_errcode_t err = fn (extra, node);
+      if (BE (err != REG_NOERROR, 0))
+	return err;
+
+      /* Go to the left node, or up and to the right.  */
+      if (node->left)
+	node = node->left;
+      else
+	{
+	  bin_tree_t *prev = NULL;
+	  while (node->right == prev || node->right == NULL)
+	    {
+	      prev = node;
+	      node = node->parent;
+	      if (!node)
+		return REG_NOERROR;
+	    }
+	  node = node->right;
+	}
+    }
+}
+
+/* Optimization pass: if a SUBEXP is entirely contained, strip it and tell
+   re_search_internal to map the inner one's opr.idx to this one's.  Adjust
+   backreferences as well.  Requires a preorder visit.  */
+static reg_errcode_t
+optimize_subexps (void *extra, bin_tree_t *node)
+{
+  re_dfa_t *dfa = (re_dfa_t *) extra;
+
+  if (node->token.type == OP_BACK_REF && dfa->subexp_map)
+    {
+      int idx = node->token.opr.idx;
+      node->token.opr.idx = dfa->subexp_map[idx];
+      dfa->used_bkref_map |= 1 << node->token.opr.idx;
+    }
+
+  else if (node->token.type == SUBEXP
+	   && node->left && node->left->token.type == SUBEXP)
+    {
+      int other_idx = node->left->token.opr.idx;
+
+      node->left = node->left->left;
+      if (node->left)
+	node->left->parent = node;
+
+      dfa->subexp_map[other_idx] = dfa->subexp_map[node->token.opr.idx];
+      if (other_idx < BITSET_WORD_BITS)
+	  dfa->used_bkref_map &= ~((bitset_word_t) 1 << other_idx);
+    }
+
+  return REG_NOERROR;
+}
+
+/* Lowering pass: Turn each SUBEXP node into the appropriate concatenation
+   of OP_OPEN_SUBEXP, the body of the SUBEXP (if any) and OP_CLOSE_SUBEXP.  */
+static reg_errcode_t
+lower_subexps (void *extra, bin_tree_t *node)
+{
+  regex_t *preg = (regex_t *) extra;
+  reg_errcode_t err = REG_NOERROR;
+
+  if (node->left && node->left->token.type == SUBEXP)
+    {
+      node->left = lower_subexp (&err, preg, node->left);
+      if (node->left)
+	node->left->parent = node;
+    }
+  if (node->right && node->right->token.type == SUBEXP)
+    {
+      node->right = lower_subexp (&err, preg, node->right);
+      if (node->right)
+	node->right->parent = node;
+    }
+
+  return err;
+}
+
+static bin_tree_t *
+lower_subexp (reg_errcode_t *err, regex_t *preg, bin_tree_t *node)
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  bin_tree_t *body = node->left;
+  bin_tree_t *op, *cls, *tree1, *tree;
+
+  if (preg->no_sub
+      /* We do not optimize empty subexpressions, because otherwise we may
+	 have bad CONCAT nodes with NULL children.  This is obviously not
+	 very common, so we do not lose much.  An example that triggers
+	 this case is the sed "script" /\(\)/x.  */
+      && node->left != NULL
+      && (node->token.opr.idx >= BITSET_WORD_BITS
+	  || !(dfa->used_bkref_map
+	       & ((bitset_word_t) 1 << node->token.opr.idx))))
+    return node->left;
+
+  /* Convert the SUBEXP node to the concatenation of an
+     OP_OPEN_SUBEXP, the contents, and an OP_CLOSE_SUBEXP.  */
+  op = create_tree (dfa, NULL, NULL, OP_OPEN_SUBEXP);
+  cls = create_tree (dfa, NULL, NULL, OP_CLOSE_SUBEXP);
+  tree1 = body ? create_tree (dfa, body, cls, CONCAT) : cls;
+  tree = create_tree (dfa, op, tree1, CONCAT);
+  if (BE (tree == NULL || tree1 == NULL || op == NULL || cls == NULL, 0))
+    {
+      *err = REG_ESPACE;
+      return NULL;
+    }
+
+  op->token.opr.idx = cls->token.opr.idx = node->token.opr.idx;
+  op->token.opt_subexp = cls->token.opt_subexp = node->token.opt_subexp;
+  return tree;
+}
+
+/* Pass 1 in building the NFA: compute FIRST and create unlinked automaton
+   nodes.  Requires a postorder visit.  */
+static reg_errcode_t
+calc_first (void *extra, bin_tree_t *node)
+{
+  re_dfa_t *dfa = (re_dfa_t *) extra;
+  if (node->token.type == CONCAT)
+    {
+      node->first = node->left->first;
+      node->node_idx = node->left->node_idx;
+    }
+  else
+    {
+      node->first = node;
+      node->node_idx = re_dfa_add_node (dfa, node->token);
+      if (BE (node->node_idx == -1, 0))
+	return REG_ESPACE;
+      if (node->token.type == ANCHOR)
+	dfa->nodes[node->node_idx].constraint = node->token.opr.ctx_type;
+    }
+  return REG_NOERROR;
+}
+
+/* Pass 2: compute NEXT on the tree.  Preorder visit.  */
+static reg_errcode_t
+calc_next (void *extra, bin_tree_t *node)
+{
+  switch (node->token.type)
+    {
+    case OP_DUP_ASTERISK:
+      node->left->next = node;
+      break;
+    case CONCAT:
+      node->left->next = node->right->first;
+      node->right->next = node->next;
+      break;
+    default:
+      if (node->left)
+	node->left->next = node->next;
+      if (node->right)
+	node->right->next = node->next;
+      break;
+    }
+  return REG_NOERROR;
+}
+
+/* Pass 3: link all DFA nodes to their NEXT node (any order will do).  */
+static reg_errcode_t
+link_nfa_nodes (void *extra, bin_tree_t *node)
+{
+  re_dfa_t *dfa = (re_dfa_t *) extra;
+  int idx = node->node_idx;
+  reg_errcode_t err = REG_NOERROR;
+
+  switch (node->token.type)
+    {
+    case CONCAT:
+      break;
+
+    case END_OF_RE:
+      assert (node->next == NULL);
+      break;
+
+    case OP_DUP_ASTERISK:
+    case OP_ALT:
+      {
+	int left, right;
+	dfa->has_plural_match = 1;
+	if (node->left != NULL)
+	  left = node->left->first->node_idx;
+	else
+	  left = node->next->node_idx;
+	if (node->right != NULL)
+	  right = node->right->first->node_idx;
+	else
+	  right = node->next->node_idx;
+	assert (left > -1);
+	assert (right > -1);
+	err = re_node_set_init_2 (dfa->edests + idx, left, right);
+      }
+      break;
+
+    case ANCHOR:
+    case OP_OPEN_SUBEXP:
+    case OP_CLOSE_SUBEXP:
+      err = re_node_set_init_1 (dfa->edests + idx, node->next->node_idx);
+      break;
+
+    case OP_BACK_REF:
+      dfa->nexts[idx] = node->next->node_idx;
+      if (node->token.type == OP_BACK_REF)
+	err = re_node_set_init_1 (dfa->edests + idx, dfa->nexts[idx]);
+      break;
+
+    default:
+      assert (!IS_EPSILON_NODE (node->token.type));
+      dfa->nexts[idx] = node->next->node_idx;
+      break;
+    }
+
+  return err;
+}
+
+/* Duplicate the epsilon closure of the node ROOT_NODE.
+   Note that duplicated nodes have constraint INIT_CONSTRAINT in addition
+   to their own constraint.  */
+
+static reg_errcode_t
+internal_function
+duplicate_node_closure (re_dfa_t *dfa, int top_org_node, int top_clone_node,
+			int root_node, unsigned int init_constraint)
+{
+  int org_node, clone_node, ret;
+  unsigned int constraint = init_constraint;
+  for (org_node = top_org_node, clone_node = top_clone_node;;)
+    {
+      int org_dest, clone_dest;
+      if (dfa->nodes[org_node].type == OP_BACK_REF)
+	{
+	  /* If the back reference epsilon-transit, its destination must
+	     also have the constraint.  Then duplicate the epsilon closure
+	     of the destination of the back reference, and store it in
+	     edests of the back reference.  */
+	  org_dest = dfa->nexts[org_node];
+	  re_node_set_empty (dfa->edests + clone_node);
+	  clone_dest = duplicate_node (dfa, org_dest, constraint);
+	  if (BE (clone_dest == -1, 0))
+	    return REG_ESPACE;
+	  dfa->nexts[clone_node] = dfa->nexts[org_node];
+	  ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+	  if (BE (ret < 0, 0))
+	    return REG_ESPACE;
+	}
+      else if (dfa->edests[org_node].nelem == 0)
+	{
+	  /* In case of the node can't epsilon-transit, don't duplicate the
+	     destination and store the original destination as the
+	     destination of the node.  */
+	  dfa->nexts[clone_node] = dfa->nexts[org_node];
+	  break;
+	}
+      else if (dfa->edests[org_node].nelem == 1)
+	{
+	  /* In case of the node can epsilon-transit, and it has only one
+	     destination.  */
+	  org_dest = dfa->edests[org_node].elems[0];
+	  re_node_set_empty (dfa->edests + clone_node);
+	  /* If the node is root_node itself, it means the epsilon clsoure
+	     has a loop.   Then tie it to the destination of the root_node.  */
+	  if (org_node == root_node && clone_node != org_node)
+	    {
+	      ret = re_node_set_insert (dfa->edests + clone_node, org_dest);
+	      if (BE (ret < 0, 0))
+		return REG_ESPACE;
+	      break;
+	    }
+	  /* In case of the node has another constraint, add it.  */
+	  constraint |= dfa->nodes[org_node].constraint;
+	  clone_dest = duplicate_node (dfa, org_dest, constraint);
+	  if (BE (clone_dest == -1, 0))
+	    return REG_ESPACE;
+	  ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+	  if (BE (ret < 0, 0))
+	    return REG_ESPACE;
+	}
+      else /* dfa->edests[org_node].nelem == 2 */
+	{
+	  /* In case of the node can epsilon-transit, and it has two
+	     destinations. In the bin_tree_t and DFA, that's '|' and '*'.   */
+	  org_dest = dfa->edests[org_node].elems[0];
+	  re_node_set_empty (dfa->edests + clone_node);
+	  /* Search for a duplicated node which satisfies the constraint.  */
+	  clone_dest = search_duplicated_node (dfa, org_dest, constraint);
+	  if (clone_dest == -1)
+	    {
+	      /* There is no such duplicated node, create a new one.  */
+	      reg_errcode_t err;
+	      clone_dest = duplicate_node (dfa, org_dest, constraint);
+	      if (BE (clone_dest == -1, 0))
+		return REG_ESPACE;
+	      ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+	      if (BE (ret < 0, 0))
+		return REG_ESPACE;
+	      err = duplicate_node_closure (dfa, org_dest, clone_dest,
+					    root_node, constraint);
+	      if (BE (err != REG_NOERROR, 0))
+		return err;
+	    }
+	  else
+	    {
+	      /* There is a duplicated node which satisfies the constraint,
+		 use it to avoid infinite loop.  */
+	      ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+	      if (BE (ret < 0, 0))
+		return REG_ESPACE;
+	    }
+
+	  org_dest = dfa->edests[org_node].elems[1];
+	  clone_dest = duplicate_node (dfa, org_dest, constraint);
+	  if (BE (clone_dest == -1, 0))
+	    return REG_ESPACE;
+	  ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+	  if (BE (ret < 0, 0))
+	    return REG_ESPACE;
+	}
+      org_node = org_dest;
+      clone_node = clone_dest;
+    }
+  return REG_NOERROR;
+}
+
+/* Search for a node which is duplicated from the node ORG_NODE, and
+   satisfies the constraint CONSTRAINT.  */
+
+static int
+search_duplicated_node (const re_dfa_t *dfa, int org_node,
+			unsigned int constraint)
+{
+  int idx;
+  for (idx = dfa->nodes_len - 1; dfa->nodes[idx].duplicated && idx > 0; --idx)
+    {
+      if (org_node == dfa->org_indices[idx]
+	  && constraint == dfa->nodes[idx].constraint)
+	return idx; /* Found.  */
+    }
+  return -1; /* Not found.  */
+}
+
+/* Duplicate the node whose index is ORG_IDX and set the constraint CONSTRAINT.
+   Return the index of the new node, or -1 if insufficient storage is
+   available.  */
+
+static int
+duplicate_node (re_dfa_t *dfa, int org_idx, unsigned int constraint)
+{
+  int dup_idx = re_dfa_add_node (dfa, dfa->nodes[org_idx]);
+  if (BE (dup_idx != -1, 1))
+    {
+      dfa->nodes[dup_idx].constraint = constraint;
+      dfa->nodes[dup_idx].constraint |= dfa->nodes[org_idx].constraint;
+      dfa->nodes[dup_idx].duplicated = 1;
+
+      /* Store the index of the original node.  */
+      dfa->org_indices[dup_idx] = org_idx;
+    }
+  return dup_idx;
+}
+
+static reg_errcode_t
+calc_inveclosure (re_dfa_t *dfa)
+{
+  int src, idx, ret;
+  for (idx = 0; idx < dfa->nodes_len; ++idx)
+    re_node_set_init_empty (dfa->inveclosures + idx);
+
+  for (src = 0; src < dfa->nodes_len; ++src)
+    {
+      int *elems = dfa->eclosures[src].elems;
+      for (idx = 0; idx < dfa->eclosures[src].nelem; ++idx)
+	{
+	  ret = re_node_set_insert_last (dfa->inveclosures + elems[idx], src);
+	  if (BE (ret == -1, 0))
+	    return REG_ESPACE;
+	}
+    }
+
+  return REG_NOERROR;
+}
+
+/* Calculate "eclosure" for all the node in DFA.  */
+
+static reg_errcode_t
+calc_eclosure (re_dfa_t *dfa)
+{
+  int node_idx, incomplete;
+#ifdef DEBUG
+  assert (dfa->nodes_len > 0);
+#endif
+  incomplete = 0;
+  /* For each nodes, calculate epsilon closure.  */
+  for (node_idx = 0; ; ++node_idx)
+    {
+      reg_errcode_t err;
+      re_node_set eclosure_elem;
+      if (node_idx == dfa->nodes_len)
+	{
+	  if (!incomplete)
+	    break;
+	  incomplete = 0;
+	  node_idx = 0;
+	}
+
+#ifdef DEBUG
+      assert (dfa->eclosures[node_idx].nelem != -1);
+#endif
+
+      /* If we have already calculated, skip it.  */
+      if (dfa->eclosures[node_idx].nelem != 0)
+	continue;
+      /* Calculate epsilon closure of `node_idx'.  */
+      err = calc_eclosure_iter (&eclosure_elem, dfa, node_idx, 1);
+      if (BE (err != REG_NOERROR, 0))
+	return err;
+
+      if (dfa->eclosures[node_idx].nelem == 0)
+	{
+	  incomplete = 1;
+	  re_node_set_free (&eclosure_elem);
+	}
+    }
+  return REG_NOERROR;
+}
+
+/* Calculate epsilon closure of NODE.  */
+
+static reg_errcode_t
+calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa, int node, int root)
+{
+  reg_errcode_t err;
+  int i;
+  re_node_set eclosure;
+  int ret;
+  int incomplete = 0;
+  err = re_node_set_alloc (&eclosure, dfa->edests[node].nelem + 1);
+  if (BE (err != REG_NOERROR, 0))
+    return err;
+
+  /* This indicates that we are calculating this node now.
+     We reference this value to avoid infinite loop.  */
+  dfa->eclosures[node].nelem = -1;
+
+  /* If the current node has constraints, duplicate all nodes
+     since they must inherit the constraints.  */
+  if (dfa->nodes[node].constraint
+      && dfa->edests[node].nelem
+      && !dfa->nodes[dfa->edests[node].elems[0]].duplicated)
+    {
+      err = duplicate_node_closure (dfa, node, node, node,
+				    dfa->nodes[node].constraint);
+      if (BE (err != REG_NOERROR, 0))
+	return err;
+    }
+
+  /* Expand each epsilon destination nodes.  */
+  if (IS_EPSILON_NODE(dfa->nodes[node].type))
+    for (i = 0; i < dfa->edests[node].nelem; ++i)
+      {
+	re_node_set eclosure_elem;
+	int edest = dfa->edests[node].elems[i];
+	/* If calculating the epsilon closure of `edest' is in progress,
+	   return intermediate result.  */
+	if (dfa->eclosures[edest].nelem == -1)
+	  {
+	    incomplete = 1;
+	    continue;
+	  }
+	/* If we haven't calculated the epsilon closure of `edest' yet,
+	   calculate now. Otherwise use calculated epsilon closure.  */
+	if (dfa->eclosures[edest].nelem == 0)
+	  {
+	    err = calc_eclosure_iter (&eclosure_elem, dfa, edest, 0);
+	    if (BE (err != REG_NOERROR, 0))
+	      return err;
+	  }
+	else
+	  eclosure_elem = dfa->eclosures[edest];
+	/* Merge the epsilon closure of `edest'.  */
+	err = re_node_set_merge (&eclosure, &eclosure_elem);
+	if (BE (err != REG_NOERROR, 0))
+	  return err;
+	/* If the epsilon closure of `edest' is incomplete,
+	   the epsilon closure of this node is also incomplete.  */
+	if (dfa->eclosures[edest].nelem == 0)
+	  {
+	    incomplete = 1;
+	    re_node_set_free (&eclosure_elem);
+	  }
+      }
+
+  /* An epsilon closure includes itself.  */
+  ret = re_node_set_insert (&eclosure, node);
+  if (BE (ret < 0, 0))
+    return REG_ESPACE;
+  if (incomplete && !root)
+    dfa->eclosures[node].nelem = 0;
+  else
+    dfa->eclosures[node] = eclosure;
+  *new_set = eclosure;
+  return REG_NOERROR;
+}
+
+/* Functions for token which are used in the parser.  */
+
+/* Fetch a token from INPUT.
+   We must not use this function inside bracket expressions.  */
+
+static void
+internal_function
+fetch_token (re_token_t *result, re_string_t *input, reg_syntax_t syntax)
+{
+  re_string_skip_bytes (input, peek_token (result, input, syntax));
+}
+
+/* Peek a token from INPUT, and return the length of the token.
+   We must not use this function inside bracket expressions.  */
+
+static int
+internal_function
+peek_token (re_token_t *token, re_string_t *input, reg_syntax_t syntax)
+{
+  unsigned char c;
+
+  if (re_string_eoi (input))
+    {
+      token->type = END_OF_RE;
+      return 0;
+    }
+
+  c = re_string_peek_byte (input, 0);
+  token->opr.c = c;
+
+  token->word_char = 0;
+#ifdef RE_ENABLE_I18N
+  token->mb_partial = 0;
+  if (input->mb_cur_max > 1 &&
+      !re_string_first_byte (input, re_string_cur_idx (input)))
+    {
+      token->type = CHARACTER;
+      token->mb_partial = 1;
+      return 1;
+    }
+#endif
+  if (c == '\\')
+    {
+      unsigned char c2;
+      if (re_string_cur_idx (input) + 1 >= re_string_length (input))
+	{
+	  token->type = BACK_SLASH;
+	  return 1;
+	}
+
+      c2 = re_string_peek_byte_case (input, 1);
+      token->opr.c = c2;
+      token->type = CHARACTER;
+#ifdef RE_ENABLE_I18N
+      if (input->mb_cur_max > 1)
+	{
+	  wint_t wc = re_string_wchar_at (input,
+					  re_string_cur_idx (input) + 1);
+	  token->word_char = IS_WIDE_WORD_CHAR (wc) != 0;
+	}
+      else
+#endif
+	token->word_char = IS_WORD_CHAR (c2) != 0;
+
+      switch (c2)
+	{
+	case '|':
+	  if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_NO_BK_VBAR))
+	    token->type = OP_ALT;
+	  break;
+	case '1': case '2': case '3': case '4': case '5':
+	case '6': case '7': case '8': case '9':
+	  if (!(syntax & RE_NO_BK_REFS))
+	    {
+	      token->type = OP_BACK_REF;
+	      token->opr.idx = c2 - '1';
+	    }
+	  break;
+	case '<':
+	  if (!(syntax & RE_NO_GNU_OPS))
+	    {
+	      token->type = ANCHOR;
+	      token->opr.ctx_type = WORD_FIRST;
+	    }
+	  break;
+	case '>':
+	  if (!(syntax & RE_NO_GNU_OPS))
+	    {
+	      token->type = ANCHOR;
+	      token->opr.ctx_type = WORD_LAST;
+	    }
+	  break;
+	case 'b':
+	  if (!(syntax & RE_NO_GNU_OPS))
+	    {
+	      token->type = ANCHOR;
+	      token->opr.ctx_type = WORD_DELIM;
+	    }
+	  break;
+	case 'B':
+	  if (!(syntax & RE_NO_GNU_OPS))
+	    {
+	      token->type = ANCHOR;
+	      token->opr.ctx_type = NOT_WORD_DELIM;
+	    }
+	  break;
+	case 'w':
+	  if (!(syntax & RE_NO_GNU_OPS))
+	    token->type = OP_WORD;
+	  break;
+	case 'W':
+	  if (!(syntax & RE_NO_GNU_OPS))
+	    token->type = OP_NOTWORD;
+	  break;
+	case 's':
+	  if (!(syntax & RE_NO_GNU_OPS))
+	    token->type = OP_SPACE;
+	  break;
+	case 'S':
+	  if (!(syntax & RE_NO_GNU_OPS))
+	    token->type = OP_NOTSPACE;
+	  break;
+	case '`':
+	  if (!(syntax & RE_NO_GNU_OPS))
+	    {
+	      token->type = ANCHOR;
+	      token->opr.ctx_type = BUF_FIRST;
+	    }
+	  break;
+	case '\'':
+	  if (!(syntax & RE_NO_GNU_OPS))
+	    {
+	      token->type = ANCHOR;
+	      token->opr.ctx_type = BUF_LAST;
+	    }
+	  break;
+	case '(':
+	  if (!(syntax & RE_NO_BK_PARENS))
+	    token->type = OP_OPEN_SUBEXP;
+	  break;
+	case ')':
+	  if (!(syntax & RE_NO_BK_PARENS))
+	    token->type = OP_CLOSE_SUBEXP;
+	  break;
+	case '+':
+	  if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_BK_PLUS_QM))
+	    token->type = OP_DUP_PLUS;
+	  break;
+	case '?':
+	  if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_BK_PLUS_QM))
+	    token->type = OP_DUP_QUESTION;
+	  break;
+	case '{':
+	  if ((syntax & RE_INTERVALS) && (!(syntax & RE_NO_BK_BRACES)))
+	    token->type = OP_OPEN_DUP_NUM;
+	  break;
+	case '}':
+	  if ((syntax & RE_INTERVALS) && (!(syntax & RE_NO_BK_BRACES)))
+	    token->type = OP_CLOSE_DUP_NUM;
+	  break;
+	default:
+	  break;
+	}
+      return 2;
+    }
+
+  token->type = CHARACTER;
+#ifdef RE_ENABLE_I18N
+  if (input->mb_cur_max > 1)
+    {
+      wint_t wc = re_string_wchar_at (input, re_string_cur_idx (input));
+      token->word_char = IS_WIDE_WORD_CHAR (wc) != 0;
+    }
+  else
+#endif
+    token->word_char = IS_WORD_CHAR (token->opr.c);
+
+  switch (c)
+    {
+    case '\n':
+      if (syntax & RE_NEWLINE_ALT)
+	token->type = OP_ALT;
+      break;
+    case '|':
+      if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_NO_BK_VBAR))
+	token->type = OP_ALT;
+      break;
+    case '*':
+      token->type = OP_DUP_ASTERISK;
+      break;
+    case '+':
+      if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_BK_PLUS_QM))
+	token->type = OP_DUP_PLUS;
+      break;
+    case '?':
+      if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_BK_PLUS_QM))
+	token->type = OP_DUP_QUESTION;
+      break;
+    case '{':
+      if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
+	token->type = OP_OPEN_DUP_NUM;
+      break;
+    case '}':
+      if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
+	token->type = OP_CLOSE_DUP_NUM;
+      break;
+    case '(':
+      if (syntax & RE_NO_BK_PARENS)
+	token->type = OP_OPEN_SUBEXP;
+      break;
+    case ')':
+      if (syntax & RE_NO_BK_PARENS)
+	token->type = OP_CLOSE_SUBEXP;
+      break;
+    case '[':
+      token->type = OP_OPEN_BRACKET;
+      break;
+    case '.':
+      token->type = OP_PERIOD;
+      break;
+    case '^':
+      if (!(syntax & (RE_CONTEXT_INDEP_ANCHORS | RE_CARET_ANCHORS_HERE)) &&
+	  re_string_cur_idx (input) != 0)
+	{
+	  char prev = re_string_peek_byte (input, -1);
+	  if (!(syntax & RE_NEWLINE_ALT) || prev != '\n')
+	    break;
+	}
+      token->type = ANCHOR;
+      token->opr.ctx_type = LINE_FIRST;
+      break;
+    case '$':
+      if (!(syntax & RE_CONTEXT_INDEP_ANCHORS) &&
+	  re_string_cur_idx (input) + 1 != re_string_length (input))
+	{
+	  re_token_t next;
+	  re_string_skip_bytes (input, 1);
+	  peek_token (&next, input, syntax);
+	  re_string_skip_bytes (input, -1);
+	  if (next.type != OP_ALT && next.type != OP_CLOSE_SUBEXP)
+	    break;
+	}
+      token->type = ANCHOR;
+      token->opr.ctx_type = LINE_LAST;
+      break;
+    default:
+      break;
+    }
+  return 1;
+}
+
+/* Peek a token from INPUT, and return the length of the token.
+   We must not use this function out of bracket expressions.  */
+
+static int
+internal_function
+peek_token_bracket (re_token_t *token, re_string_t *input, reg_syntax_t syntax)
+{
+  unsigned char c;
+  if (re_string_eoi (input))
+    {
+      token->type = END_OF_RE;
+      return 0;
+    }
+  c = re_string_peek_byte (input, 0);
+  token->opr.c = c;
+
+#ifdef RE_ENABLE_I18N
+  if (input->mb_cur_max > 1 &&
+      !re_string_first_byte (input, re_string_cur_idx (input)))
+    {
+      token->type = CHARACTER;
+      return 1;
+    }
+#endif /* RE_ENABLE_I18N */
+
+  if (c == '\\' && (syntax & RE_BACKSLASH_ESCAPE_IN_LISTS)
+      && re_string_cur_idx (input) + 1 < re_string_length (input))
+    {
+      /* In this case, '\' escape a character.  */
+      unsigned char c2;
+      re_string_skip_bytes (input, 1);
+      c2 = re_string_peek_byte (input, 0);
+      token->opr.c = c2;
+      token->type = CHARACTER;
+      return 1;
+    }
+  if (c == '[') /* '[' is a special char in a bracket exps.  */
+    {
+      unsigned char c2;
+      int token_len;
+      if (re_string_cur_idx (input) + 1 < re_string_length (input))
+	c2 = re_string_peek_byte (input, 1);
+      else
+	c2 = 0;
+      token->opr.c = c2;
+      token_len = 2;
+      switch (c2)
+	{
+	case '.':
+	  token->type = OP_OPEN_COLL_ELEM;
+	  break;
+	case '=':
+	  token->type = OP_OPEN_EQUIV_CLASS;
+	  break;
+	case ':':
+	  if (syntax & RE_CHAR_CLASSES)
+	    {
+	      token->type = OP_OPEN_CHAR_CLASS;
+	      break;
+	    }
+	  /* else fall through.  */
+	default:
+	  token->type = CHARACTER;
+	  token->opr.c = c;
+	  token_len = 1;
+	  break;
+	}
+      return token_len;
+    }
+  switch (c)
+    {
+    case '-':
+      token->type = OP_CHARSET_RANGE;
+      break;
+    case ']':
+      token->type = OP_CLOSE_BRACKET;
+      break;
+    case '^':
+      token->type = OP_NON_MATCH_LIST;
+      break;
+    default:
+      token->type = CHARACTER;
+    }
+  return 1;
+}
+
+/* Functions for parser.  */
+
+/* Entry point of the parser.
+   Parse the regular expression REGEXP and return the structure tree.
+   If an error is occured, ERR is set by error code, and return NULL.
+   This function build the following tree, from regular expression <reg_exp>:
+	   CAT
+	   / \
+	  /   \
+   <reg_exp>  EOR
+
+   CAT means concatenation.
+   EOR means end of regular expression.  */
+
+static bin_tree_t *
+parse (re_string_t *regexp, regex_t *preg, reg_syntax_t syntax,
+       reg_errcode_t *err)
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  bin_tree_t *tree, *eor, *root;
+  re_token_t current_token;
+  dfa->syntax = syntax;
+  fetch_token (&current_token, regexp, syntax | RE_CARET_ANCHORS_HERE);
+  tree = parse_reg_exp (regexp, preg, &current_token, syntax, 0, err);
+  if (BE (*err != REG_NOERROR && tree == NULL, 0))
+    return NULL;
+  eor = create_tree (dfa, NULL, NULL, END_OF_RE);
+  if (tree != NULL)
+    root = create_tree (dfa, tree, eor, CONCAT);
+  else
+    root = eor;
+  if (BE (eor == NULL || root == NULL, 0))
+    {
+      *err = REG_ESPACE;
+      return NULL;
+    }
+  return root;
+}
+
+/* This function build the following tree, from regular expression
+   <branch1>|<branch2>:
+	   ALT
+	   / \
+	  /   \
+   <branch1> <branch2>
+
+   ALT means alternative, which represents the operator `|'.  */
+
+static bin_tree_t *
+parse_reg_exp (re_string_t *regexp, regex_t *preg, re_token_t *token,
+	       reg_syntax_t syntax, int nest, reg_errcode_t *err)
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  bin_tree_t *tree, *branch = NULL;
+  tree = parse_branch (regexp, preg, token, syntax, nest, err);
+  if (BE (*err != REG_NOERROR && tree == NULL, 0))
+    return NULL;
+
+  while (token->type == OP_ALT)
+    {
+      fetch_token (token, regexp, syntax | RE_CARET_ANCHORS_HERE);
+      if (token->type != OP_ALT && token->type != END_OF_RE
+	  && (nest == 0 || token->type != OP_CLOSE_SUBEXP))
+	{
+	  branch = parse_branch (regexp, preg, token, syntax, nest, err);
+	  if (BE (*err != REG_NOERROR && branch == NULL, 0))
+	    return NULL;
+	}
+      else
+	branch = NULL;
+      tree = create_tree (dfa, tree, branch, OP_ALT);
+      if (BE (tree == NULL, 0))
+	{
+	  *err = REG_ESPACE;
+	  return NULL;
+	}
+    }
+  return tree;
+}
+
+/* This function build the following tree, from regular expression
+   <exp1><exp2>:
+	CAT
+	/ \
+       /   \
+   <exp1> <exp2>
+
+   CAT means concatenation.  */
+
+static bin_tree_t *
+parse_branch (re_string_t *regexp, regex_t *preg, re_token_t *token,
+	      reg_syntax_t syntax, int nest, reg_errcode_t *err)
+{
+  bin_tree_t *tree, *exp;
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  tree = parse_expression (regexp, preg, token, syntax, nest, err);
+  if (BE (*err != REG_NOERROR && tree == NULL, 0))
+    return NULL;
+
+  while (token->type != OP_ALT && token->type != END_OF_RE
+	 && (nest == 0 || token->type != OP_CLOSE_SUBEXP))
+    {
+      exp = parse_expression (regexp, preg, token, syntax, nest, err);
+      if (BE (*err != REG_NOERROR && exp == NULL, 0))
+	{
+	  return NULL;
+	}
+      if (tree != NULL && exp != NULL)
+	{
+	  tree = create_tree (dfa, tree, exp, CONCAT);
+	  if (tree == NULL)
+	    {
+	      *err = REG_ESPACE;
+	      return NULL;
+	    }
+	}
+      else if (tree == NULL)
+	tree = exp;
+      /* Otherwise exp == NULL, we don't need to create new tree.  */
+    }
+  return tree;
+}
+
+/* This function build the following tree, from regular expression a*:
+	 *
+	 |
+	 a
+*/
+
+static bin_tree_t *
+parse_expression (re_string_t *regexp, regex_t *preg, re_token_t *token,
+		  reg_syntax_t syntax, int nest, reg_errcode_t *err)
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  bin_tree_t *tree;
+  switch (token->type)
+    {
+    case CHARACTER:
+      tree = create_token_tree (dfa, NULL, NULL, token);
+      if (BE (tree == NULL, 0))
+	{
+	  *err = REG_ESPACE;
+	  return NULL;
+	}
+#ifdef RE_ENABLE_I18N
+      if (dfa->mb_cur_max > 1)
+	{
+	  while (!re_string_eoi (regexp)
+		 && !re_string_first_byte (regexp, re_string_cur_idx (regexp)))
+	    {
+	      bin_tree_t *mbc_remain;
+	      fetch_token (token, regexp, syntax);
+	      mbc_remain = create_token_tree (dfa, NULL, NULL, token);
+	      tree = create_tree (dfa, tree, mbc_remain, CONCAT);
+	      if (BE (mbc_remain == NULL || tree == NULL, 0))
+		{
+		  *err = REG_ESPACE;
+		  return NULL;
+		}
+	    }
+	}
+#endif
+      break;
+    case OP_OPEN_SUBEXP:
+      tree = parse_sub_exp (regexp, preg, token, syntax, nest + 1, err);
+      if (BE (*err != REG_NOERROR && tree == NULL, 0))
+	return NULL;
+      break;
+    case OP_OPEN_BRACKET:
+      tree = parse_bracket_exp (regexp, dfa, token, syntax, err);
+      if (BE (*err != REG_NOERROR && tree == NULL, 0))
+	return NULL;
+      break;
+    case OP_BACK_REF:
+      if (!BE (dfa->completed_bkref_map & (1 << token->opr.idx), 1))
+	{
+	  *err = REG_ESUBREG;
+	  return NULL;
+	}
+      dfa->used_bkref_map |= 1 << token->opr.idx;
+      tree = create_token_tree (dfa, NULL, NULL, token);
+      if (BE (tree == NULL, 0))
+	{
+	  *err = REG_ESPACE;
+	  return NULL;
+	}
+      ++dfa->nbackref;
+      dfa->has_mb_node = 1;
+      break;
+    case OP_OPEN_DUP_NUM:
+      if (syntax & RE_CONTEXT_INVALID_DUP)
+	{
+	  *err = REG_BADRPT;
+	  return NULL;
+	}
+      /* FALLTHROUGH */
+    case OP_DUP_ASTERISK:
+    case OP_DUP_PLUS:
+    case OP_DUP_QUESTION:
+      if (syntax & RE_CONTEXT_INVALID_OPS)
+	{
+	  *err = REG_BADRPT;
+	  return NULL;
+	}
+      else if (syntax & RE_CONTEXT_INDEP_OPS)
+	{
+	  fetch_token (token, regexp, syntax);
+	  return parse_expression (regexp, preg, token, syntax, nest, err);
+	}
+      /* else fall through  */
+    case OP_CLOSE_SUBEXP:
+      if ((token->type == OP_CLOSE_SUBEXP) &&
+	  !(syntax & RE_UNMATCHED_RIGHT_PAREN_ORD))
+	{
+	  *err = REG_ERPAREN;
+	  return NULL;
+	}
+      /* else fall through  */
+    case OP_CLOSE_DUP_NUM:
+      /* We treat it as a normal character.  */
+
+      /* Then we can these characters as normal characters.  */
+      token->type = CHARACTER;
+      /* mb_partial and word_char bits should be initialized already
+	 by peek_token.  */
+      tree = create_token_tree (dfa, NULL, NULL, token);
+      if (BE (tree == NULL, 0))
+	{
+	  *err = REG_ESPACE;
+	  return NULL;
+	}
+      break;
+    case ANCHOR:
+      if ((token->opr.ctx_type
+	   & (WORD_DELIM | NOT_WORD_DELIM | WORD_FIRST | WORD_LAST))
+	  && dfa->word_ops_used == 0)
+	init_word_char (dfa);
+      if (token->opr.ctx_type == WORD_DELIM
+	  || token->opr.ctx_type == NOT_WORD_DELIM)
+	{
+	  bin_tree_t *tree_first, *tree_last;
+	  if (token->opr.ctx_type == WORD_DELIM)
+	    {
+	      token->opr.ctx_type = WORD_FIRST;
+	      tree_first = create_token_tree (dfa, NULL, NULL, token);
+	      token->opr.ctx_type = WORD_LAST;
+	    }
+	  else
+	    {
+	      token->opr.ctx_type = INSIDE_WORD;
+	      tree_first = create_token_tree (dfa, NULL, NULL, token);
+	      token->opr.ctx_type = INSIDE_NOTWORD;
+	    }
+	  tree_last = create_token_tree (dfa, NULL, NULL, token);
+	  tree = create_tree (dfa, tree_first, tree_last, OP_ALT);
+	  if (BE (tree_first == NULL || tree_last == NULL || tree == NULL, 0))
+	    {
+	      *err = REG_ESPACE;
+	      return NULL;
+	    }
+	}
+      else
+	{
+	  tree = create_token_tree (dfa, NULL, NULL, token);
+	  if (BE (tree == NULL, 0))
+	    {
+	      *err = REG_ESPACE;
+	      return NULL;
+	    }
+	}
+      /* We must return here, since ANCHORs can't be followed
+	 by repetition operators.
+	 eg. RE"^*" is invalid or "<ANCHOR(^)><CHAR(*)>",
+	     it must not be "<ANCHOR(^)><REPEAT(*)>".  */
+      fetch_token (token, regexp, syntax);
+      return tree;
+    case OP_PERIOD:
+      tree = create_token_tree (dfa, NULL, NULL, token);
+      if (BE (tree == NULL, 0))
+	{
+	  *err = REG_ESPACE;
+	  return NULL;
+	}
+      if (dfa->mb_cur_max > 1)
+	dfa->has_mb_node = 1;
+      break;
+    case OP_WORD:
+    case OP_NOTWORD:
+      tree = build_charclass_op (dfa, regexp->trans,
+				 "alnum",
+				 "_",
+				 token->type == OP_NOTWORD, err);
+      if (BE (*err != REG_NOERROR && tree == NULL, 0))
+	return NULL;
+      break;
+    case OP_SPACE:
+    case OP_NOTSPACE:
+      tree = build_charclass_op (dfa, regexp->trans,
+				 "space",
+				 "",
+				 token->type == OP_NOTSPACE, err);
+      if (BE (*err != REG_NOERROR && tree == NULL, 0))
+	return NULL;
+      break;
+    case OP_ALT:
+    case END_OF_RE:
+      return NULL;
+    case BACK_SLASH:
+      *err = REG_EESCAPE;
+      return NULL;
+    default:
+      /* Must not happen?  */
+#ifdef DEBUG
+      assert (0);
+#endif
+      return NULL;
+    }
+  fetch_token (token, regexp, syntax);
+
+  while (token->type == OP_DUP_ASTERISK || token->type == OP_DUP_PLUS
+	 || token->type == OP_DUP_QUESTION || token->type == OP_OPEN_DUP_NUM)
+    {
+      tree = parse_dup_op (tree, regexp, dfa, token, syntax, err);
+      if (BE (*err != REG_NOERROR && tree == NULL, 0))
+	return NULL;
+      /* In BRE consecutive duplications are not allowed.  */
+      if ((syntax & RE_CONTEXT_INVALID_DUP)
+	  && (token->type == OP_DUP_ASTERISK
+	      || token->type == OP_OPEN_DUP_NUM))
+	{
+	  *err = REG_BADRPT;
+	  return NULL;
+	}
+    }
+
+  return tree;
+}
+
+/* This function build the following tree, from regular expression
+   (<reg_exp>):
+	 SUBEXP
+	    |
+	<reg_exp>
+*/
+
+static bin_tree_t *
+parse_sub_exp (re_string_t *regexp, regex_t *preg, re_token_t *token,
+	       reg_syntax_t syntax, int nest, reg_errcode_t *err)
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  bin_tree_t *tree;
+  size_t cur_nsub;
+  cur_nsub = preg->re_nsub++;
+
+  fetch_token (token, regexp, syntax | RE_CARET_ANCHORS_HERE);
+
+  /* The subexpression may be a null string.  */
+  if (token->type == OP_CLOSE_SUBEXP)
+    tree = NULL;
+  else
+    {
+      tree = parse_reg_exp (regexp, preg, token, syntax, nest, err);
+      if (BE (*err == REG_NOERROR && token->type != OP_CLOSE_SUBEXP, 0))
+	*err = REG_EPAREN;
+      if (BE (*err != REG_NOERROR, 0))
+	return NULL;
+    }
+
+  if (cur_nsub <= '9' - '1')
+    dfa->completed_bkref_map |= 1 << cur_nsub;
+
+  tree = create_tree (dfa, tree, NULL, SUBEXP);
+  if (BE (tree == NULL, 0))
+    {
+      *err = REG_ESPACE;
+      return NULL;
+    }
+  tree->token.opr.idx = cur_nsub;
+  return tree;
+}
+
+/* This function parse repetition operators like "*", "+", "{1,3}" etc.  */
+
+static bin_tree_t *
+parse_dup_op (bin_tree_t *elem, re_string_t *regexp, re_dfa_t *dfa,
+	      re_token_t *token, reg_syntax_t syntax, reg_errcode_t *err)
+{
+  bin_tree_t *tree = NULL, *old_tree = NULL;
+  int i, start, end, start_idx = re_string_cur_idx (regexp);
+#ifndef RE_TOKEN_INIT_BUG
+  re_token_t start_token = *token;
+#else
+  re_token_t start_token;
+
+  memcpy ((void *) &start_token, (void *) token, sizeof start_token);
+#endif
+
+  if (token->type == OP_OPEN_DUP_NUM)
+    {
+      end = 0;
+      start = fetch_number (regexp, token, syntax);
+      if (start == -1)
+	{
+	  if (token->type == CHARACTER && token->opr.c == ',')
+	    start = 0; /* We treat "{,m}" as "{0,m}".  */
+	  else
+	    {
+	      *err = REG_BADBR; /* <re>{} is invalid.  */
+	      return NULL;
+	    }
+	}
+      if (BE (start != -2, 1))
+	{
+	  /* We treat "{n}" as "{n,n}".  */
+	  end = ((token->type == OP_CLOSE_DUP_NUM) ? start
+		 : ((token->type == CHARACTER && token->opr.c == ',')
+		    ? fetch_number (regexp, token, syntax) : -2));
+	}
+      if (BE (start == -2 || end == -2, 0))
+	{
+	  /* Invalid sequence.  */
+	  if (BE (!(syntax & RE_INVALID_INTERVAL_ORD), 0))
+	    {
+	      if (token->type == END_OF_RE)
+		*err = REG_EBRACE;
+	      else
+		*err = REG_BADBR;
+
+	      return NULL;
+	    }
+
+	  /* If the syntax bit is set, rollback.  */
+	  re_string_set_index (regexp, start_idx);
+	  *token = start_token;
+	  token->type = CHARACTER;
+	  /* mb_partial and word_char bits should be already initialized by
+	     peek_token.  */
+	  return elem;
+	}
+
+      if (BE ((end != -1 && start > end) || token->type != OP_CLOSE_DUP_NUM, 0))
+	{
+	  /* First number greater than second.  */
+	  *err = REG_BADBR;
+	  return NULL;
+	}
+    }
+  else
+    {
+      start = (token->type == OP_DUP_PLUS) ? 1 : 0;
+      end = (token->type == OP_DUP_QUESTION) ? 1 : -1;
+    }
+
+  fetch_token (token, regexp, syntax);
+
+  if (BE (elem == NULL, 0))
+    return NULL;
+  if (BE (start == 0 && end == 0, 0))
+    {
+      postorder (elem, free_tree, NULL);
+      return NULL;
+    }
+
+  /* Extract "<re>{n,m}" to "<re><re>...<re><re>{0,<m-n>}".  */
+  if (BE (start > 0, 0))
+    {
+      tree = elem;
+      for (i = 2; i <= start; ++i)
+	{
+	  elem = duplicate_tree (elem, dfa);
+	  tree = create_tree (dfa, tree, elem, CONCAT);
+	  if (BE (elem == NULL || tree == NULL, 0))
+	    goto parse_dup_op_espace;
+	}
+
+      if (start == end)
+	return tree;
+
+      /* Duplicate ELEM before it is marked optional.  */
+      elem = duplicate_tree (elem, dfa);
+      old_tree = tree;
+    }
+  else
+    old_tree = NULL;
+
+  if (elem->token.type == SUBEXP)
+    postorder (elem, mark_opt_subexp, (void *) (long) elem->token.opr.idx);
+
+  tree = create_tree (dfa, elem, NULL, (end == -1 ? OP_DUP_ASTERISK : OP_ALT));
+  if (BE (tree == NULL, 0))
+    goto parse_dup_op_espace;
+
+  /* This loop is actually executed only when end != -1,
+     to rewrite <re>{0,n} as (<re>(<re>...<re>?)?)?...  We have
+     already created the start+1-th copy.  */
+  for (i = start + 2; i <= end; ++i)
+    {
+      elem = duplicate_tree (elem, dfa);
+      tree = create_tree (dfa, tree, elem, CONCAT);
+      if (BE (elem == NULL || tree == NULL, 0))
+	goto parse_dup_op_espace;
+
+      tree = create_tree (dfa, tree, NULL, OP_ALT);
+      if (BE (tree == NULL, 0))
+	goto parse_dup_op_espace;
+    }
+
+  if (old_tree)
+    tree = create_tree (dfa, old_tree, tree, CONCAT);
+
+  return tree;
+
+ parse_dup_op_espace:
+  *err = REG_ESPACE;
+  return NULL;
+}
+
+/* Size of the names for collating symbol/equivalence_class/character_class.
+   I'm not sure, but maybe enough.  */
+#define BRACKET_NAME_BUF_SIZE 32
+
+#ifndef _LIBC
+  /* Local function for parse_bracket_exp only used in case of NOT _LIBC.
+     Build the range expression which starts from START_ELEM, and ends
+     at END_ELEM.  The result are written to MBCSET and SBCSET.
+     RANGE_ALLOC is the allocated size of mbcset->range_starts, and
+     mbcset->range_ends, is a pointer argument sinse we may
+     update it.  */
+
+static reg_errcode_t
+internal_function
+# ifdef RE_ENABLE_I18N
+build_range_exp (bitset_t sbcset, re_charset_t *mbcset, int *range_alloc,
+		 bracket_elem_t *start_elem, bracket_elem_t *end_elem)
+# else /* not RE_ENABLE_I18N */
+build_range_exp (bitset_t sbcset, bracket_elem_t *start_elem,
+		 bracket_elem_t *end_elem)
+# endif /* not RE_ENABLE_I18N */
+{
+  unsigned int start_ch, end_ch;
+  /* Equivalence Classes and Character Classes can't be a range start/end.  */
+  if (BE (start_elem->type == EQUIV_CLASS || start_elem->type == CHAR_CLASS
+	  || end_elem->type == EQUIV_CLASS || end_elem->type == CHAR_CLASS,
+	  0))
+    return REG_ERANGE;
+
+  /* We can handle no multi character collating elements without libc
+     support.  */
+  if (BE ((start_elem->type == COLL_SYM
+	   && strlen ((char *) start_elem->opr.name) > 1)
+	  || (end_elem->type == COLL_SYM
+	      && strlen ((char *) end_elem->opr.name) > 1), 0))
+    return REG_ECOLLATE;
+
+# ifdef RE_ENABLE_I18N
+  {
+    wchar_t wc;
+    wint_t start_wc;
+    wint_t end_wc;
+    wchar_t cmp_buf[6] = {L'\0', L'\0', L'\0', L'\0', L'\0', L'\0'};
+
+    start_ch = ((start_elem->type == SB_CHAR) ? start_elem->opr.ch
+		: ((start_elem->type == COLL_SYM) ? start_elem->opr.name[0]
+		   : 0));
+    end_ch = ((end_elem->type == SB_CHAR) ? end_elem->opr.ch
+	      : ((end_elem->type == COLL_SYM) ? end_elem->opr.name[0]
+		 : 0));
+#ifdef GAWK
+    /*
+     * Fedora Core 2, maybe others, have broken `btowc' that returns -1
+     * for any value > 127. Sigh. Note that `start_ch' and `end_ch' are
+     * unsigned, so we don't have sign extension problems.
+     */
+    start_wc = ((start_elem->type == SB_CHAR || start_elem->type == COLL_SYM)
+		? start_ch : start_elem->opr.wch);
+    end_wc = ((end_elem->type == SB_CHAR || end_elem->type == COLL_SYM)
+	      ? end_ch : end_elem->opr.wch);
+#else
+    start_wc = ((start_elem->type == SB_CHAR || start_elem->type == COLL_SYM)
+		? __btowc (start_ch) : start_elem->opr.wch);
+    end_wc = ((end_elem->type == SB_CHAR || end_elem->type == COLL_SYM)
+	      ? __btowc (end_ch) : end_elem->opr.wch);
+#endif
+    if (start_wc == WEOF || end_wc == WEOF)
+      return REG_ECOLLATE;
+    cmp_buf[0] = start_wc;
+    cmp_buf[4] = end_wc;
+    if (wcscoll (cmp_buf, cmp_buf + 4) > 0)
+      return REG_ERANGE;
+
+    /* Got valid collation sequence values, add them as a new entry.
+       However, for !_LIBC we have no collation elements: if the
+       character set is single byte, the single byte character set
+       that we build below suffices.  parse_bracket_exp passes
+       no MBCSET if dfa->mb_cur_max == 1.  */
+    if (mbcset)
+      {
+	/* Check the space of the arrays.  */
+	if (BE (*range_alloc == mbcset->nranges, 0))
+	  {
+	    /* There is not enough space, need realloc.  */
+	    wchar_t *new_array_start, *new_array_end;
+	    int new_nranges;
+
+	    /* +1 in case of mbcset->nranges is 0.  */
+	    new_nranges = 2 * mbcset->nranges + 1;
+	    /* Use realloc since mbcset->range_starts and mbcset->range_ends
+	       are NULL if *range_alloc == 0.  */
+	    new_array_start = re_realloc (mbcset->range_starts, wchar_t,
+					  new_nranges);
+	    new_array_end = re_realloc (mbcset->range_ends, wchar_t,
+					new_nranges);
+
+	    if (BE (new_array_start == NULL || new_array_end == NULL, 0))
+	      return REG_ESPACE;
+
+	    mbcset->range_starts = new_array_start;
+	    mbcset->range_ends = new_array_end;
+	    *range_alloc = new_nranges;
+	  }
+
+	mbcset->range_starts[mbcset->nranges] = start_wc;
+	mbcset->range_ends[mbcset->nranges++] = end_wc;
+      }
+
+    /* Build the table for single byte characters.  */
+    for (wc = 0; wc < SBC_MAX; ++wc)
+      {
+	cmp_buf[2] = wc;
+	if (wcscoll (cmp_buf, cmp_buf + 2) <= 0
+	    && wcscoll (cmp_buf + 2, cmp_buf + 4) <= 0)
+	  bitset_set (sbcset, wc);
+      }
+  }
+# else /* not RE_ENABLE_I18N */
+  {
+    unsigned int ch;
+    start_ch = ((start_elem->type == SB_CHAR ) ? start_elem->opr.ch
+		: ((start_elem->type == COLL_SYM) ? start_elem->opr.name[0]
+		   : 0));
+    end_ch = ((end_elem->type == SB_CHAR ) ? end_elem->opr.ch
+	      : ((end_elem->type == COLL_SYM) ? end_elem->opr.name[0]
+		 : 0));
+    if (start_ch > end_ch)
+      return REG_ERANGE;
+    /* Build the table for single byte characters.  */
+    for (ch = 0; ch < SBC_MAX; ++ch)
+      if (start_ch <= ch  && ch <= end_ch)
+	bitset_set (sbcset, ch);
+  }
+# endif /* not RE_ENABLE_I18N */
+  return REG_NOERROR;
+}
+#endif /* not _LIBC */
+
+#ifndef _LIBC
+/* Helper function for parse_bracket_exp only used in case of NOT _LIBC..
+   Build the collating element which is represented by NAME.
+   The result are written to MBCSET and SBCSET.
+   COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a
+   pointer argument since we may update it.  */
+
+static reg_errcode_t
+internal_function
+# ifdef RE_ENABLE_I18N
+build_collating_symbol (bitset_t sbcset, re_charset_t *mbcset,
+			int *coll_sym_alloc, const unsigned char *name)
+# else /* not RE_ENABLE_I18N */
+build_collating_symbol (bitset_t sbcset, const unsigned char *name)
+# endif /* not RE_ENABLE_I18N */
+{
+  size_t name_len = strlen ((const char *) name);
+  if (BE (name_len != 1, 0))
+    return REG_ECOLLATE;
+  else
+    {
+      bitset_set (sbcset, name[0]);
+      return REG_NOERROR;
+    }
+}
+#endif /* not _LIBC */
+
+/* This function parse bracket expression like "[abc]", "[a-c]",
+   "[[.a-a.]]" etc.  */
+
+static bin_tree_t *
+parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa, re_token_t *token,
+		   reg_syntax_t syntax, reg_errcode_t *err)
+{
+#ifdef _LIBC
+  const unsigned char *collseqmb;
+  const char *collseqwc;
+  uint32_t nrules;
+  int32_t table_size;
+  const int32_t *symb_table;
+  const unsigned char *extra;
+
+  /* Local function for parse_bracket_exp used in _LIBC environement.
+     Seek the collating symbol entry correspondings to NAME.
+     Return the index of the symbol in the SYMB_TABLE.  */
+
+  auto inline int32_t
+  __attribute ((always_inline))
+  seek_collating_symbol_entry (name, name_len)
+	 const unsigned char *name;
+	 size_t name_len;
+    {
+      int32_t hash = elem_hash ((const char *) name, name_len);
+      int32_t elem = hash % table_size;
+      if (symb_table[2 * elem] != 0)
+	{
+	  int32_t second = hash % (table_size - 2) + 1;
+
+	  do
+	    {
+	      /* First compare the hashing value.  */
+	      if (symb_table[2 * elem] == hash
+		  /* Compare the length of the name.  */
+		  && name_len == extra[symb_table[2 * elem + 1]]
+		  /* Compare the name.  */
+		  && memcmp (name, &extra[symb_table[2 * elem + 1] + 1],
+			     name_len) == 0)
+		{
+		  /* Yep, this is the entry.  */
+		  break;
+		}
+
+	      /* Next entry.  */
+	      elem += second;
+	    }
+	  while (symb_table[2 * elem] != 0);
+	}
+      return elem;
+    }
+
+  /* Local function for parse_bracket_exp used in _LIBC environment.
+     Look up the collation sequence value of BR_ELEM.
+     Return the value if succeeded, UINT_MAX otherwise.  */
+
+  auto inline unsigned int
+  __attribute ((always_inline))
+  lookup_collation_sequence_value (br_elem)
+	 bracket_elem_t *br_elem;
+    {
+      if (br_elem->type == SB_CHAR)
+	{
+	  /*
+	  if (MB_CUR_MAX == 1)
+	  */
+	  if (nrules == 0)
+	    return collseqmb[br_elem->opr.ch];
+	  else
+	    {
+	      wint_t wc = __btowc (br_elem->opr.ch);
+	      return __collseq_table_lookup (collseqwc, wc);
+	    }
+	}
+      else if (br_elem->type == MB_CHAR)
+	{
+	  if (nrules != 0)
+	    return __collseq_table_lookup (collseqwc, br_elem->opr.wch);
+	}
+      else if (br_elem->type == COLL_SYM)
+	{
+	  size_t sym_name_len = strlen ((char *) br_elem->opr.name);
+	  if (nrules != 0)
+	    {
+	      int32_t elem, idx;
+	      elem = seek_collating_symbol_entry (br_elem->opr.name,
+						  sym_name_len);
+	      if (symb_table[2 * elem] != 0)
+		{
+		  /* We found the entry.  */
+		  idx = symb_table[2 * elem + 1];
+		  /* Skip the name of collating element name.  */
+		  idx += 1 + extra[idx];
+		  /* Skip the byte sequence of the collating element.  */
+		  idx += 1 + extra[idx];
+		  /* Adjust for the alignment.  */
+		  idx = (idx + 3) & ~3;
+		  /* Skip the multibyte collation sequence value.  */
+		  idx += sizeof (unsigned int);
+		  /* Skip the wide char sequence of the collating element.  */
+		  idx += sizeof (unsigned int) *
+		    (1 + *(unsigned int *) (extra + idx));
+		  /* Return the collation sequence value.  */
+		  return *(unsigned int *) (extra + idx);
+		}
+	      else if (symb_table[2 * elem] == 0 && sym_name_len == 1)
+		{
+		  /* No valid character.  Match it as a single byte
+		     character.  */
+		  return collseqmb[br_elem->opr.name[0]];
+		}
+	    }
+	  else if (sym_name_len == 1)
+	    return collseqmb[br_elem->opr.name[0]];
+	}
+      return UINT_MAX;
+    }
+
+  /* Local function for parse_bracket_exp used in _LIBC environement.
+     Build the range expression which starts from START_ELEM, and ends
+     at END_ELEM.  The result are written to MBCSET and SBCSET.
+     RANGE_ALLOC is the allocated size of mbcset->range_starts, and
+     mbcset->range_ends, is a pointer argument sinse we may
+     update it.  */
+
+  auto inline reg_errcode_t
+  __attribute ((always_inline))
+  build_range_exp (sbcset, mbcset, range_alloc, start_elem, end_elem)
+	 re_charset_t *mbcset;
+	 int *range_alloc;
+	 bitset_t sbcset;
+	 bracket_elem_t *start_elem, *end_elem;
+    {
+      unsigned int ch;
+      uint32_t start_collseq;
+      uint32_t end_collseq;
+
+      /* Equivalence Classes and Character Classes can't be a range
+	 start/end.  */
+      if (BE (start_elem->type == EQUIV_CLASS || start_elem->type == CHAR_CLASS
+	      || end_elem->type == EQUIV_CLASS || end_elem->type == CHAR_CLASS,
+	      0))
+	return REG_ERANGE;
+
+      start_collseq = lookup_collation_sequence_value (start_elem);
+      end_collseq = lookup_collation_sequence_value (end_elem);
+      /* Check start/end collation sequence values.  */
+      if (BE (start_collseq == UINT_MAX || end_collseq == UINT_MAX, 0))
+	return REG_ECOLLATE;
+      if (BE ((syntax & RE_NO_EMPTY_RANGES) && start_collseq > end_collseq, 0))
+	return REG_ERANGE;
+
+      /* Got valid collation sequence values, add them as a new entry.
+	 However, if we have no collation elements, and the character set
+	 is single byte, the single byte character set that we
+	 build below suffices. */
+      if (nrules > 0 || dfa->mb_cur_max > 1)
+	{
+	  /* Check the space of the arrays.  */
+	  if (BE (*range_alloc == mbcset->nranges, 0))
+	    {
+	      /* There is not enough space, need realloc.  */
+	      uint32_t *new_array_start;
+	      uint32_t *new_array_end;
+	      int new_nranges;
+
+	      /* +1 in case of mbcset->nranges is 0.  */
+	      new_nranges = 2 * mbcset->nranges + 1;
+	      new_array_start = re_realloc (mbcset->range_starts, uint32_t,
+					    new_nranges);
+	      new_array_end = re_realloc (mbcset->range_ends, uint32_t,
+					  new_nranges);
+
+	      if (BE (new_array_start == NULL || new_array_end == NULL, 0))
+		return REG_ESPACE;
+
+	      mbcset->range_starts = new_array_start;
+	      mbcset->range_ends = new_array_end;
+	      *range_alloc = new_nranges;
+	    }
+
+	  mbcset->range_starts[mbcset->nranges] = start_collseq;
+	  mbcset->range_ends[mbcset->nranges++] = end_collseq;
+	}
+
+      /* Build the table for single byte characters.  */
+      for (ch = 0; ch < SBC_MAX; ch++)
+	{
+	  uint32_t ch_collseq;
+	  /*
+	  if (MB_CUR_MAX == 1)
+	  */
+	  if (nrules == 0)
+	    ch_collseq = collseqmb[ch];
+	  else
+	    ch_collseq = __collseq_table_lookup (collseqwc, __btowc (ch));
+	  if (start_collseq <= ch_collseq && ch_collseq <= end_collseq)
+	    bitset_set (sbcset, ch);
+	}
+      return REG_NOERROR;
+    }
+
+  /* Local function for parse_bracket_exp used in _LIBC environement.
+     Build the collating element which is represented by NAME.
+     The result are written to MBCSET and SBCSET.
+     COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a
+     pointer argument sinse we may update it.  */
+
+  auto inline reg_errcode_t
+  __attribute ((always_inline))
+  build_collating_symbol (sbcset, mbcset, coll_sym_alloc, name)
+	 re_charset_t *mbcset;
+	 int *coll_sym_alloc;
+	 bitset_t sbcset;
+	 const unsigned char *name;
+    {
+      int32_t elem, idx;
+      size_t name_len = strlen ((const char *) name);
+      if (nrules != 0)
+	{
+	  elem = seek_collating_symbol_entry (name, name_len);
+	  if (symb_table[2 * elem] != 0)
+	    {
+	      /* We found the entry.  */
+	      idx = symb_table[2 * elem + 1];
+	      /* Skip the name of collating element name.  */
+	      idx += 1 + extra[idx];
+	    }
+	  else if (symb_table[2 * elem] == 0 && name_len == 1)
+	    {
+	      /* No valid character, treat it as a normal
+		 character.  */
+	      bitset_set (sbcset, name[0]);
+	      return REG_NOERROR;
+	    }
+	  else
+	    return REG_ECOLLATE;
+
+	  /* Got valid collation sequence, add it as a new entry.  */
+	  /* Check the space of the arrays.  */
+	  if (BE (*coll_sym_alloc == mbcset->ncoll_syms, 0))
+	    {
+	      /* Not enough, realloc it.  */
+	      /* +1 in case of mbcset->ncoll_syms is 0.  */
+	      int new_coll_sym_alloc = 2 * mbcset->ncoll_syms + 1;
+	      /* Use realloc since mbcset->coll_syms is NULL
+		 if *alloc == 0.  */
+	      int32_t *new_coll_syms = re_realloc (mbcset->coll_syms, int32_t,
+						   new_coll_sym_alloc);
+	      if (BE (new_coll_syms == NULL, 0))
+		return REG_ESPACE;
+	      mbcset->coll_syms = new_coll_syms;
+	      *coll_sym_alloc = new_coll_sym_alloc;
+	    }
+	  mbcset->coll_syms[mbcset->ncoll_syms++] = idx;
+	  return REG_NOERROR;
+	}
+      else
+	{
+	  if (BE (name_len != 1, 0))
+	    return REG_ECOLLATE;
+	  else
+	    {
+	      bitset_set (sbcset, name[0]);
+	      return REG_NOERROR;
+	    }
+	}
+    }
+#endif
+
+  re_token_t br_token;
+  re_bitset_ptr_t sbcset;
+#ifdef RE_ENABLE_I18N
+  re_charset_t *mbcset;
+  int coll_sym_alloc = 0, range_alloc = 0, mbchar_alloc = 0;
+  int equiv_class_alloc = 0, char_class_alloc = 0;
+#endif /* not RE_ENABLE_I18N */
+  int non_match = 0;
+  bin_tree_t *work_tree;
+  int token_len;
+  int first_round = 1;
+#ifdef _LIBC
+  collseqmb = (const unsigned char *)
+    _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB);
+  nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+  if (nrules)
+    {
+      /*
+      if (MB_CUR_MAX > 1)
+      */
+      collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC);
+      table_size = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_SYMB_HASH_SIZEMB);
+      symb_table = (const int32_t *) _NL_CURRENT (LC_COLLATE,
+						  _NL_COLLATE_SYMB_TABLEMB);
+      extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE,
+						   _NL_COLLATE_SYMB_EXTRAMB);
+    }
+#endif
+  sbcset = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1);
+#ifdef RE_ENABLE_I18N
+  mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1);
+#endif /* RE_ENABLE_I18N */
+#ifdef RE_ENABLE_I18N
+  if (BE (sbcset == NULL || mbcset == NULL, 0))
+#else
+  if (BE (sbcset == NULL, 0))
+#endif /* RE_ENABLE_I18N */
+    {
+      *err = REG_ESPACE;
+      return NULL;
+    }
+
+  token_len = peek_token_bracket (token, regexp, syntax);
+  if (BE (token->type == END_OF_RE, 0))
+    {
+      *err = REG_BADPAT;
+      goto parse_bracket_exp_free_return;
+    }
+  if (token->type == OP_NON_MATCH_LIST)
+    {
+#ifdef RE_ENABLE_I18N
+      mbcset->non_match = 1;
+#endif /* not RE_ENABLE_I18N */
+      non_match = 1;
+      if (syntax & RE_HAT_LISTS_NOT_NEWLINE)
+	bitset_set (sbcset, '\n');
+      re_string_skip_bytes (regexp, token_len); /* Skip a token.  */
+      token_len = peek_token_bracket (token, regexp, syntax);
+      if (BE (token->type == END_OF_RE, 0))
+	{
+	  *err = REG_BADPAT;
+	  goto parse_bracket_exp_free_return;
+	}
+    }
+
+  /* We treat the first ']' as a normal character.  */
+  if (token->type == OP_CLOSE_BRACKET)
+    token->type = CHARACTER;
+
+  while (1)
+    {
+      bracket_elem_t start_elem, end_elem;
+      unsigned char start_name_buf[BRACKET_NAME_BUF_SIZE];
+      unsigned char end_name_buf[BRACKET_NAME_BUF_SIZE];
+      reg_errcode_t ret;
+      int token_len2 = 0, is_range_exp = 0;
+      re_token_t token2;
+
+      start_elem.opr.name = start_name_buf;
+      ret = parse_bracket_element (&start_elem, regexp, token, token_len, dfa,
+				   syntax, first_round);
+      if (BE (ret != REG_NOERROR, 0))
+	{
+	  *err = ret;
+	  goto parse_bracket_exp_free_return;
+	}
+      first_round = 0;
+
+      /* Get information about the next token.  We need it in any case.  */
+      token_len = peek_token_bracket (token, regexp, syntax);
+
+      /* Do not check for ranges if we know they are not allowed.  */
+      if (start_elem.type != CHAR_CLASS && start_elem.type != EQUIV_CLASS)
+	{
+	  if (BE (token->type == END_OF_RE, 0))
+	    {
+	      *err = REG_EBRACK;
+	      goto parse_bracket_exp_free_return;
+	    }
+	  if (token->type == OP_CHARSET_RANGE)
+	    {
+	      re_string_skip_bytes (regexp, token_len); /* Skip '-'.  */
+	      token_len2 = peek_token_bracket (&token2, regexp, syntax);
+	      if (BE (token2.type == END_OF_RE, 0))
+		{
+		  *err = REG_EBRACK;
+		  goto parse_bracket_exp_free_return;
+		}
+	      if (token2.type == OP_CLOSE_BRACKET)
+		{
+		  /* We treat the last '-' as a normal character.  */
+		  re_string_skip_bytes (regexp, -token_len);
+		  token->type = CHARACTER;
+		}
+	      else
+		is_range_exp = 1;
+	    }
+	}
+
+      if (is_range_exp == 1)
+	{
+	  end_elem.opr.name = end_name_buf;
+	  ret = parse_bracket_element (&end_elem, regexp, &token2, token_len2,
+				       dfa, syntax, 1);
+	  if (BE (ret != REG_NOERROR, 0))
+	    {
+	      *err = ret;
+	      goto parse_bracket_exp_free_return;
+	    }
+
+	  token_len = peek_token_bracket (token, regexp, syntax);
+
+#ifdef _LIBC
+	  *err = build_range_exp (sbcset, mbcset, &range_alloc,
+				  &start_elem, &end_elem);
+#else
+# ifdef RE_ENABLE_I18N
+	  *err = build_range_exp (sbcset,
+				  dfa->mb_cur_max > 1 ? mbcset : NULL,
+				  &range_alloc, &start_elem, &end_elem);
+# else
+	  *err = build_range_exp (sbcset, &start_elem, &end_elem);
+# endif
+#endif /* RE_ENABLE_I18N */
+	  if (BE (*err != REG_NOERROR, 0))
+	    goto parse_bracket_exp_free_return;
+	}
+      else
+	{
+	  switch (start_elem.type)
+	    {
+	    case SB_CHAR:
+	      bitset_set (sbcset, start_elem.opr.ch);
+	      break;
+#ifdef RE_ENABLE_I18N
+	    case MB_CHAR:
+	      /* Check whether the array has enough space.  */
+	      if (BE (mbchar_alloc == mbcset->nmbchars, 0))
+		{
+		  wchar_t *new_mbchars;
+		  /* Not enough, realloc it.  */
+		  /* +1 in case of mbcset->nmbchars is 0.  */
+		  mbchar_alloc = 2 * mbcset->nmbchars + 1;
+		  /* Use realloc since array is NULL if *alloc == 0.  */
+		  new_mbchars = re_realloc (mbcset->mbchars, wchar_t,
+					    mbchar_alloc);
+		  if (BE (new_mbchars == NULL, 0))
+		    goto parse_bracket_exp_espace;
+		  mbcset->mbchars = new_mbchars;
+		}
+	      mbcset->mbchars[mbcset->nmbchars++] = start_elem.opr.wch;
+	      break;
+#endif /* RE_ENABLE_I18N */
+	    case EQUIV_CLASS:
+	      *err = build_equiv_class (sbcset,
+#ifdef RE_ENABLE_I18N
+					mbcset, &equiv_class_alloc,
+#endif /* RE_ENABLE_I18N */
+					start_elem.opr.name);
+	      if (BE (*err != REG_NOERROR, 0))
+		goto parse_bracket_exp_free_return;
+	      break;
+	    case COLL_SYM:
+	      *err = build_collating_symbol (sbcset,
+#ifdef RE_ENABLE_I18N
+					     mbcset, &coll_sym_alloc,
+#endif /* RE_ENABLE_I18N */
+					     start_elem.opr.name);
+	      if (BE (*err != REG_NOERROR, 0))
+		goto parse_bracket_exp_free_return;
+	      break;
+	    case CHAR_CLASS:
+	      *err = build_charclass (regexp->trans, sbcset,
+#ifdef RE_ENABLE_I18N
+				      mbcset, &char_class_alloc,
+#endif /* RE_ENABLE_I18N */
+				      (const char *) start_elem.opr.name, syntax);
+	      if (BE (*err != REG_NOERROR, 0))
+	       goto parse_bracket_exp_free_return;
+	      break;
+	    default:
+	      assert (0);
+	      break;
+	    }
+	}
+      if (BE (token->type == END_OF_RE, 0))
+	{
+	  *err = REG_EBRACK;
+	  goto parse_bracket_exp_free_return;
+	}
+      if (token->type == OP_CLOSE_BRACKET)
+	break;
+    }
+
+  re_string_skip_bytes (regexp, token_len); /* Skip a token.  */
+
+  /* If it is non-matching list.  */
+  if (non_match)
+    bitset_not (sbcset);
+
+#ifdef RE_ENABLE_I18N
+  /* Ensure only single byte characters are set.  */
+  if (dfa->mb_cur_max > 1)
+    bitset_mask (sbcset, dfa->sb_char);
+
+  if (mbcset->nmbchars || mbcset->ncoll_syms || mbcset->nequiv_classes
+      || mbcset->nranges || (dfa->mb_cur_max > 1 && (mbcset->nchar_classes
+						     || mbcset->non_match)))
+    {
+      bin_tree_t *mbc_tree;
+      int sbc_idx;
+      /* Build a tree for complex bracket.  */
+      dfa->has_mb_node = 1;
+      br_token.type = COMPLEX_BRACKET;
+      br_token.opr.mbcset = mbcset;
+      mbc_tree = create_token_tree (dfa, NULL, NULL, &br_token);
+      if (BE (mbc_tree == NULL, 0))
+	goto parse_bracket_exp_espace;
+      for (sbc_idx = 0; sbc_idx < BITSET_WORDS; ++sbc_idx)
+	if (sbcset[sbc_idx])
+	  break;
+      /* If there are no bits set in sbcset, there is no point
+	 of having both SIMPLE_BRACKET and COMPLEX_BRACKET.  */
+      if (sbc_idx < BITSET_WORDS)
+	{
+	  /* Build a tree for simple bracket.  */
+	  br_token.type = SIMPLE_BRACKET;
+	  br_token.opr.sbcset = sbcset;
+	  work_tree = create_token_tree (dfa, NULL, NULL, &br_token);
+	  if (BE (work_tree == NULL, 0))
+	    goto parse_bracket_exp_espace;
+
+	  /* Then join them by ALT node.  */
+	  work_tree = create_tree (dfa, work_tree, mbc_tree, OP_ALT);
+	  if (BE (work_tree == NULL, 0))
+	    goto parse_bracket_exp_espace;
+	}
+      else
+	{
+	  re_free (sbcset);
+	  work_tree = mbc_tree;
+	}
+    }
+  else
+#endif /* not RE_ENABLE_I18N */
+    {
+#ifdef RE_ENABLE_I18N
+      free_charset (mbcset);
+#endif
+      /* Build a tree for simple bracket.  */
+      br_token.type = SIMPLE_BRACKET;
+      br_token.opr.sbcset = sbcset;
+      work_tree = create_token_tree (dfa, NULL, NULL, &br_token);
+      if (BE (work_tree == NULL, 0))
+	goto parse_bracket_exp_espace;
+    }
+  return work_tree;
+
+ parse_bracket_exp_espace:
+  *err = REG_ESPACE;
+ parse_bracket_exp_free_return:
+  re_free (sbcset);
+#ifdef RE_ENABLE_I18N
+  free_charset (mbcset);
+#endif /* RE_ENABLE_I18N */
+  return NULL;
+}
+
+/* Parse an element in the bracket expression.  */
+
+static reg_errcode_t
+parse_bracket_element (bracket_elem_t *elem, re_string_t *regexp,
+		       re_token_t *token, int token_len, re_dfa_t *dfa,
+		       reg_syntax_t syntax, int accept_hyphen)
+{
+#ifdef RE_ENABLE_I18N
+  int cur_char_size;
+  cur_char_size = re_string_char_size_at (regexp, re_string_cur_idx (regexp));
+  if (cur_char_size > 1)
+    {
+      elem->type = MB_CHAR;
+      elem->opr.wch = re_string_wchar_at (regexp, re_string_cur_idx (regexp));
+      re_string_skip_bytes (regexp, cur_char_size);
+      return REG_NOERROR;
+    }
+#endif /* RE_ENABLE_I18N */
+  re_string_skip_bytes (regexp, token_len); /* Skip a token.  */
+  if (token->type == OP_OPEN_COLL_ELEM || token->type == OP_OPEN_CHAR_CLASS
+      || token->type == OP_OPEN_EQUIV_CLASS)
+    return parse_bracket_symbol (elem, regexp, token);
+  if (BE (token->type == OP_CHARSET_RANGE, 0) && !accept_hyphen)
+    {
+      /* A '-' must only appear as anything but a range indicator before
+	 the closing bracket.  Everything else is an error.  */
+      re_token_t token2;
+      (void) peek_token_bracket (&token2, regexp, syntax);
+      if (token2.type != OP_CLOSE_BRACKET)
+	/* The actual error value is not standardized since this whole
+	   case is undefined.  But ERANGE makes good sense.  */
+	return REG_ERANGE;
+    }
+  elem->type = SB_CHAR;
+  elem->opr.ch = token->opr.c;
+  return REG_NOERROR;
+}
+
+/* Parse a bracket symbol in the bracket expression.  Bracket symbols are
+   such as [:<character_class>:], [.<collating_element>.], and
+   [=<equivalent_class>=].  */
+
+static reg_errcode_t
+parse_bracket_symbol (bracket_elem_t *elem, re_string_t *regexp,
+		      re_token_t *token)
+{
+  unsigned char ch, delim = token->opr.c;
+  int i = 0;
+  if (re_string_eoi(regexp))
+    return REG_EBRACK;
+  for (;; ++i)
+    {
+      if (i >= BRACKET_NAME_BUF_SIZE)
+	return REG_EBRACK;
+      if (token->type == OP_OPEN_CHAR_CLASS)
+	ch = re_string_fetch_byte_case (regexp);
+      else
+	ch = re_string_fetch_byte (regexp);
+      if (re_string_eoi(regexp))
+	return REG_EBRACK;
+      if (ch == delim && re_string_peek_byte (regexp, 0) == ']')
+	break;
+      elem->opr.name[i] = ch;
+    }
+  re_string_skip_bytes (regexp, 1);
+  elem->opr.name[i] = '\0';
+  switch (token->type)
+    {
+    case OP_OPEN_COLL_ELEM:
+      elem->type = COLL_SYM;
+      break;
+    case OP_OPEN_EQUIV_CLASS:
+      elem->type = EQUIV_CLASS;
+      break;
+    case OP_OPEN_CHAR_CLASS:
+      elem->type = CHAR_CLASS;
+      break;
+    default:
+      break;
+    }
+  return REG_NOERROR;
+}
+
+  /* Helper function for parse_bracket_exp.
+     Build the equivalence class which is represented by NAME.
+     The result are written to MBCSET and SBCSET.
+     EQUIV_CLASS_ALLOC is the allocated size of mbcset->equiv_classes,
+     is a pointer argument sinse we may update it.  */
+
+static reg_errcode_t
+#ifdef RE_ENABLE_I18N
+build_equiv_class (bitset_t sbcset, re_charset_t *mbcset,
+		   int *equiv_class_alloc, const unsigned char *name)
+#else /* not RE_ENABLE_I18N */
+build_equiv_class (bitset_t sbcset, const unsigned char *name)
+#endif /* not RE_ENABLE_I18N */
+{
+#ifdef _LIBC
+  uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+  if (nrules != 0)
+    {
+      const int32_t *table, *indirect;
+      const unsigned char *weights, *extra, *cp;
+      unsigned char char_buf[2];
+      int32_t idx1, idx2;
+      unsigned int ch;
+      size_t len;
+      /* This #include defines a local function!  */
+# include <locale/weight.h>
+      /* Calculate the index for equivalence class.  */
+      cp = name;
+      table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+      weights = (const unsigned char *) _NL_CURRENT (LC_COLLATE,
+					       _NL_COLLATE_WEIGHTMB);
+      extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE,
+						   _NL_COLLATE_EXTRAMB);
+      indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE,
+						_NL_COLLATE_INDIRECTMB);
+      idx1 = findidx (&cp);
+      if (BE (idx1 == 0 || cp < name + strlen ((const char *) name), 0))
+	/* This isn't a valid character.  */
+	return REG_ECOLLATE;
+
+      /* Build single byte matcing table for this equivalence class.  */
+      char_buf[1] = (unsigned char) '\0';
+      len = weights[idx1 & 0xffffff];
+      for (ch = 0; ch < SBC_MAX; ++ch)
+	{
+	  char_buf[0] = ch;
+	  cp = char_buf;
+	  idx2 = findidx (&cp);
+/*
+	  idx2 = table[ch];
+*/
+	  if (idx2 == 0)
+	    /* This isn't a valid character.  */
+	    continue;
+	  /* Compare only if the length matches and the collation rule
+	     index is the same.  */
+	  if (len == weights[idx2 & 0xffffff] && (idx1 >> 24) == (idx2 >> 24))
+	    {
+	      int cnt = 0;
+
+	      while (cnt <= len &&
+		     weights[(idx1 & 0xffffff) + 1 + cnt]
+		     == weights[(idx2 & 0xffffff) + 1 + cnt])
+		++cnt;
+
+	      if (cnt > len)
+		bitset_set (sbcset, ch);
+	    }
+	}
+      /* Check whether the array has enough space.  */
+      if (BE (*equiv_class_alloc == mbcset->nequiv_classes, 0))
+	{
+	  /* Not enough, realloc it.  */
+	  /* +1 in case of mbcset->nequiv_classes is 0.  */
+	  int new_equiv_class_alloc = 2 * mbcset->nequiv_classes + 1;
+	  /* Use realloc since the array is NULL if *alloc == 0.  */
+	  int32_t *new_equiv_classes = re_realloc (mbcset->equiv_classes,
+						   int32_t,
+						   new_equiv_class_alloc);
+	  if (BE (new_equiv_classes == NULL, 0))
+	    return REG_ESPACE;
+	  mbcset->equiv_classes = new_equiv_classes;
+	  *equiv_class_alloc = new_equiv_class_alloc;
+	}
+      mbcset->equiv_classes[mbcset->nequiv_classes++] = idx1;
+    }
+  else
+#endif /* _LIBC */
+    {
+      if (BE (strlen ((const char *) name) != 1, 0))
+	return REG_ECOLLATE;
+      bitset_set (sbcset, *name);
+    }
+  return REG_NOERROR;
+}
+
+  /* Helper function for parse_bracket_exp.
+     Build the character class which is represented by NAME.
+     The result are written to MBCSET and SBCSET.
+     CHAR_CLASS_ALLOC is the allocated size of mbcset->char_classes,
+     is a pointer argument sinse we may update it.  */
+
+static reg_errcode_t
+#ifdef RE_ENABLE_I18N
+build_charclass (RE_TRANSLATE_TYPE trans, bitset_t sbcset,
+		 re_charset_t *mbcset, int *char_class_alloc,
+		 const char *class_name, reg_syntax_t syntax)
+#else /* not RE_ENABLE_I18N */
+build_charclass (RE_TRANSLATE_TYPE trans, bitset_t sbcset,
+		 const char *class_name, reg_syntax_t syntax)
+#endif /* not RE_ENABLE_I18N */
+{
+  int i;
+
+  /* In case of REG_ICASE "upper" and "lower" match the both of
+     upper and lower cases.  */
+  if ((syntax & RE_ICASE)
+      && (strcmp (class_name, "upper") == 0 || strcmp (class_name, "lower") == 0))
+    class_name = "alpha";
+
+#ifdef RE_ENABLE_I18N
+  /* Check the space of the arrays.  */
+  if (BE (*char_class_alloc == mbcset->nchar_classes, 0))
+    {
+      /* Not enough, realloc it.  */
+      /* +1 in case of mbcset->nchar_classes is 0.  */
+      int new_char_class_alloc = 2 * mbcset->nchar_classes + 1;
+      /* Use realloc since array is NULL if *alloc == 0.  */
+      wctype_t *new_char_classes = re_realloc (mbcset->char_classes, wctype_t,
+					       new_char_class_alloc);
+      if (BE (new_char_classes == NULL, 0))
+	return REG_ESPACE;
+      mbcset->char_classes = new_char_classes;
+      *char_class_alloc = new_char_class_alloc;
+    }
+  mbcset->char_classes[mbcset->nchar_classes++] = __wctype (class_name);
+#endif /* RE_ENABLE_I18N */
+
+#define BUILD_CHARCLASS_LOOP(ctype_func)	\
+  do {						\
+    if (BE (trans != NULL, 0))			\
+      {						\
+	for (i = 0; i < SBC_MAX; ++i)		\
+  	  if (ctype_func (i))			\
+	    bitset_set (sbcset, trans[i]);	\
+      }						\
+    else					\
+      {						\
+	for (i = 0; i < SBC_MAX; ++i)		\
+  	  if (ctype_func (i))			\
+	    bitset_set (sbcset, i);		\
+      }						\
+  } while (0)
+
+  if (strcmp (class_name, "alnum") == 0)
+    BUILD_CHARCLASS_LOOP (isalnum);
+  else if (strcmp (class_name, "cntrl") == 0)
+    BUILD_CHARCLASS_LOOP (iscntrl);
+  else if (strcmp (class_name, "lower") == 0)
+    BUILD_CHARCLASS_LOOP (islower);
+  else if (strcmp (class_name, "space") == 0)
+    BUILD_CHARCLASS_LOOP (isspace);
+  else if (strcmp (class_name, "alpha") == 0)
+    BUILD_CHARCLASS_LOOP (isalpha);
+  else if (strcmp (class_name, "digit") == 0)
+    BUILD_CHARCLASS_LOOP (isdigit);
+  else if (strcmp (class_name, "print") == 0)
+    BUILD_CHARCLASS_LOOP (isprint);
+  else if (strcmp (class_name, "upper") == 0)
+    BUILD_CHARCLASS_LOOP (isupper);
+  else if (strcmp (class_name, "blank") == 0)
+#ifndef GAWK
+    BUILD_CHARCLASS_LOOP (isblank);
+#else
+    /* see comments above */
+    BUILD_CHARCLASS_LOOP (is_blank);
+#endif
+  else if (strcmp (class_name, "graph") == 0)
+    BUILD_CHARCLASS_LOOP (isgraph);
+  else if (strcmp (class_name, "punct") == 0)
+    BUILD_CHARCLASS_LOOP (ispunct);
+  else if (strcmp (class_name, "xdigit") == 0)
+    BUILD_CHARCLASS_LOOP (isxdigit);
+  else
+    return REG_ECTYPE;
+
+  return REG_NOERROR;
+}
+
+static bin_tree_t *
+build_charclass_op (re_dfa_t *dfa, RE_TRANSLATE_TYPE trans,
+		    const char *class_name,
+		    const char *extra, int non_match,
+		    reg_errcode_t *err)
+{
+  re_bitset_ptr_t sbcset;
+#ifdef RE_ENABLE_I18N
+  re_charset_t *mbcset;
+  int alloc = 0;
+#endif /* not RE_ENABLE_I18N */
+  reg_errcode_t ret;
+  re_token_t br_token;
+  bin_tree_t *tree;
+
+  sbcset = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1);
+#ifdef RE_ENABLE_I18N
+  mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1);
+#endif /* RE_ENABLE_I18N */
+
+#ifdef RE_ENABLE_I18N
+  if (BE (sbcset == NULL || mbcset == NULL, 0))
+#else /* not RE_ENABLE_I18N */
+  if (BE (sbcset == NULL, 0))
+#endif /* not RE_ENABLE_I18N */
+    {
+      *err = REG_ESPACE;
+      return NULL;
+    }
+
+  if (non_match)
+    {
+#ifdef RE_ENABLE_I18N
+      mbcset->non_match = 1;
+#endif /* not RE_ENABLE_I18N */
+    }
+
+  /* We don't care the syntax in this case.  */
+  ret = build_charclass (trans, sbcset,
+#ifdef RE_ENABLE_I18N
+			 mbcset, &alloc,
+#endif /* RE_ENABLE_I18N */
+			 class_name, 0);
+
+  if (BE (ret != REG_NOERROR, 0))
+    {
+      re_free (sbcset);
+#ifdef RE_ENABLE_I18N
+      free_charset (mbcset);
+#endif /* RE_ENABLE_I18N */
+      *err = ret;
+      return NULL;
+    }
+  /* \w match '_' also.  */
+  for (; *extra; extra++)
+    bitset_set (sbcset, *extra);
+
+  /* If it is non-matching list.  */
+  if (non_match)
+    bitset_not (sbcset);
+
+#ifdef RE_ENABLE_I18N
+  /* Ensure only single byte characters are set.  */
+  if (dfa->mb_cur_max > 1)
+    bitset_mask (sbcset, dfa->sb_char);
+#endif
+
+  /* Build a tree for simple bracket.  */
+  br_token.type = SIMPLE_BRACKET;
+  br_token.opr.sbcset = sbcset;
+  tree = create_token_tree (dfa, NULL, NULL, &br_token);
+  if (BE (tree == NULL, 0))
+    goto build_word_op_espace;
+
+#ifdef RE_ENABLE_I18N
+  if (dfa->mb_cur_max > 1)
+    {
+      bin_tree_t *mbc_tree;
+      /* Build a tree for complex bracket.  */
+      br_token.type = COMPLEX_BRACKET;
+      br_token.opr.mbcset = mbcset;
+      dfa->has_mb_node = 1;
+      mbc_tree = create_token_tree (dfa, NULL, NULL, &br_token);
+      if (BE (mbc_tree == NULL, 0))
+	goto build_word_op_espace;
+      /* Then join them by ALT node.  */
+      tree = create_tree (dfa, tree, mbc_tree, OP_ALT);
+      if (BE (mbc_tree != NULL, 1))
+	return tree;
+    }
+  else
+    {
+      free_charset (mbcset);
+      return tree;
+    }
+#else /* not RE_ENABLE_I18N */
+  return tree;
+#endif /* not RE_ENABLE_I18N */
+
+ build_word_op_espace:
+  re_free (sbcset);
+#ifdef RE_ENABLE_I18N
+  free_charset (mbcset);
+#endif /* RE_ENABLE_I18N */
+  *err = REG_ESPACE;
+  return NULL;
+}
+
+/* This is intended for the expressions like "a{1,3}".
+   Fetch a number from `input', and return the number.
+   Return -1, if the number field is empty like "{,1}".
+   Return -2, If an error is occured.  */
+
+static int
+fetch_number (re_string_t *input, re_token_t *token, reg_syntax_t syntax)
+{
+  int num = -1;
+  unsigned char c;
+  while (1)
+    {
+      fetch_token (token, input, syntax);
+      c = token->opr.c;
+      if (BE (token->type == END_OF_RE, 0))
+	return -2;
+      if (token->type == OP_CLOSE_DUP_NUM || c == ',')
+	break;
+      num = ((token->type != CHARACTER || c < '0' || '9' < c || num == -2)
+	     ? -2 : ((num == -1) ? c - '0' : num * 10 + c - '0'));
+      num = (num > RE_DUP_MAX) ? -2 : num;
+    }
+  return num;
+}
+
+#ifdef RE_ENABLE_I18N
+static void
+free_charset (re_charset_t *cset)
+{
+  re_free (cset->mbchars);
+# ifdef _LIBC
+  re_free (cset->coll_syms);
+  re_free (cset->equiv_classes);
+  re_free (cset->range_starts);
+  re_free (cset->range_ends);
+# endif
+  re_free (cset->char_classes);
+  re_free (cset);
+}
+#endif /* RE_ENABLE_I18N */
+
+/* Functions for binary tree operation.  */
+
+/* Create a tree node.  */
+
+static bin_tree_t *
+create_tree (re_dfa_t *dfa, bin_tree_t *left, bin_tree_t *right,
+	     re_token_type_t type)
+{
+  re_token_t t;
+  t.type = type;
+  return create_token_tree (dfa, left, right, &t);
+}
+
+static bin_tree_t *
+create_token_tree (re_dfa_t *dfa, bin_tree_t *left, bin_tree_t *right,
+		   const re_token_t *token)
+{
+  bin_tree_t *tree;
+  if (BE (dfa->str_tree_storage_idx == BIN_TREE_STORAGE_SIZE, 0))
+    {
+      bin_tree_storage_t *storage = re_malloc (bin_tree_storage_t, 1);
+
+      if (storage == NULL)
+	return NULL;
+      storage->next = dfa->str_tree_storage;
+      dfa->str_tree_storage = storage;
+      dfa->str_tree_storage_idx = 0;
+    }
+  tree = &dfa->str_tree_storage->data[dfa->str_tree_storage_idx++];
+
+  tree->parent = NULL;
+  tree->left = left;
+  tree->right = right;
+  tree->token = *token;
+  tree->token.duplicated = 0;
+  tree->token.opt_subexp = 0;
+  tree->first = NULL;
+  tree->next = NULL;
+  tree->node_idx = -1;
+
+  if (left != NULL)
+    left->parent = tree;
+  if (right != NULL)
+    right->parent = tree;
+  return tree;
+}
+
+/* Mark the tree SRC as an optional subexpression.
+   To be called from preorder or postorder.  */
+
+static reg_errcode_t
+mark_opt_subexp (void *extra, bin_tree_t *node)
+{
+  int idx = (int) (long) extra;
+  if (node->token.type == SUBEXP && node->token.opr.idx == idx)
+    node->token.opt_subexp = 1;
+
+  return REG_NOERROR;
+}
+
+/* Free the allocated memory inside NODE. */
+
+static void
+free_token (re_token_t *node)
+{
+#ifdef RE_ENABLE_I18N
+  if (node->type == COMPLEX_BRACKET && node->duplicated == 0)
+    free_charset (node->opr.mbcset);
+  else
+#endif /* RE_ENABLE_I18N */
+    if (node->type == SIMPLE_BRACKET && node->duplicated == 0)
+      re_free (node->opr.sbcset);
+}
+
+/* Worker function for tree walking.  Free the allocated memory inside NODE
+   and its children. */
+
+static reg_errcode_t
+free_tree (void *extra, bin_tree_t *node)
+{
+  free_token (&node->token);
+  return REG_NOERROR;
+}
+
+
+/* Duplicate the node SRC, and return new node.  This is a preorder
+   visit similar to the one implemented by the generic visitor, but
+   we need more infrastructure to maintain two parallel trees --- so,
+   it's easier to duplicate.  */
+
+static bin_tree_t *
+duplicate_tree (const bin_tree_t *root, re_dfa_t *dfa)
+{
+  const bin_tree_t *node;
+  bin_tree_t *dup_root;
+  bin_tree_t **p_new = &dup_root, *dup_node = root->parent;
+
+  for (node = root; ; )
+    {
+      /* Create a new tree and link it back to the current parent.  */
+      *p_new = create_token_tree (dfa, NULL, NULL, &node->token);
+      if (*p_new == NULL)
+	return NULL;
+      (*p_new)->parent = dup_node;
+      (*p_new)->token.duplicated = 1;
+      dup_node = *p_new;
+
+      /* Go to the left node, or up and to the right.  */
+      if (node->left)
+	{
+	  node = node->left;
+	  p_new = &dup_node->left;
+	}
+      else
+	{
+	  const bin_tree_t *prev = NULL;
+	  while (node->right == prev || node->right == NULL)
+	    {
+	      prev = node;
+	      node = node->parent;
+	      dup_node = dup_node->parent;
+	      if (!node)
+		return dup_root;
+	    }
+	  node = node->right;
+	  p_new = &dup_node->right;
+	}
+    }
+}
diff --git a/compat/regex/regex.c b/compat/regex/regex.c
index 5ea0075..3dd8dfa 100644
--- a/compat/regex/regex.c
+++ b/compat/regex/regex.c
@@ -1,4927 +1,87 @@
-/* Extended regular expression matching and search library,
-   version 0.12.
-   (Implements POSIX draft P10003.2/D11.2, except for
-   internationalization features.)
+/* Extended regular expression matching and search library.
+   Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
 
-   Copyright (C) 1993 Free Software Foundation, Inc.
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
 
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2, or (at your option)
-   any later version.
-
-   This program is distributed in the hope that it will be useful,
+   The GNU C Library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
 
-   You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA.  */
 
-/* AIX requires this to be the first thing in the file. */
-#if defined (_AIX) && !defined (REGEX_MALLOC)
-  #pragma alloca
+#ifdef HAVE_CONFIG_H
+#include "config.h"
 #endif
 
-#define _GNU_SOURCE
-
-/* We need this for `regex.h', and perhaps for the Emacs include files.  */
-#include <sys/types.h>
-
-/* We used to test for `BSTRING' here, but only GCC and Emacs define
-   `BSTRING', as far as I know, and neither of them use this code.  */
-#include <string.h>
-#ifndef bcmp
-#define bcmp(s1, s2, n)	memcmp ((s1), (s2), (n))
-#endif
-#ifndef bcopy
-#define bcopy(s, d, n)	memcpy ((d), (s), (n))
-#endif
-#ifndef bzero
-#define bzero(s, n)	memset ((s), 0, (n))
+/* Make sure noone compiles this code with a C++ compiler.  */
+#ifdef __cplusplus
+# error "This is C code, use a C compiler"
 #endif
 
-#include <stdlib.h>
+#ifdef _LIBC
+/* We have to keep the namespace clean.  */
+# define regfree(preg) __regfree (preg)
+# define regexec(pr, st, nm, pm, ef) __regexec (pr, st, nm, pm, ef)
+# define regcomp(preg, pattern, cflags) __regcomp (preg, pattern, cflags)
+# define regerror(errcode, preg, errbuf, errbuf_size) \
+	__regerror(errcode, preg, errbuf, errbuf_size)
+# define re_set_registers(bu, re, nu, st, en) \
+	__re_set_registers (bu, re, nu, st, en)
+# define re_match_2(bufp, string1, size1, string2, size2, pos, regs, stop) \
+	__re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
+# define re_match(bufp, string, size, pos, regs) \
+	__re_match (bufp, string, size, pos, regs)
+# define re_search(bufp, string, size, startpos, range, regs) \
+	__re_search (bufp, string, size, startpos, range, regs)
+# define re_compile_pattern(pattern, length, bufp) \
+	__re_compile_pattern (pattern, length, bufp)
+# define re_set_syntax(syntax) __re_set_syntax (syntax)
+# define re_search_2(bufp, st1, s1, st2, s2, startpos, range, regs, stop) \
+	__re_search_2 (bufp, st1, s1, st2, s2, startpos, range, regs, stop)
+# define re_compile_fastmap(bufp) __re_compile_fastmap (bufp)
 
-
-/* Define the syntax stuff for \<, \>, etc.  */
-
-/* This must be nonzero for the wordchar and notwordchar pattern
-   commands in re_match_2.  */
-#ifndef Sword
-#define Sword 1
+# include "../locale/localeinfo.h"
 #endif
 
-#ifdef SYNTAX_TABLE
-
-extern char *re_syntax_table;
-
-#else /* not SYNTAX_TABLE */
-
-/* How many characters in the character set.  */
-#define CHAR_SET_SIZE 256
-
-static char re_syntax_table[CHAR_SET_SIZE];
-
-static void
-init_syntax_once ()
-{
-   register int c;
-   static int done = 0;
-
-   if (done)
-     return;
-
-   bzero (re_syntax_table, sizeof re_syntax_table);
-
-   for (c = 'a'; c <= 'z'; c++)
-     re_syntax_table[c] = Sword;
-
-   for (c = 'A'; c <= 'Z'; c++)
-     re_syntax_table[c] = Sword;
-
-   for (c = '0'; c <= '9'; c++)
-     re_syntax_table[c] = Sword;
-
-   re_syntax_table['_'] = Sword;
-
-   done = 1;
-}
-
-#endif /* not SYNTAX_TABLE */
-
-#define SYNTAX(c) re_syntax_table[c]
-
-
-/* Get the interface, including the syntax bits.  */
-#include "regex.h"
-
-/* isalpha etc. are used for the character classes.  */
-#include <ctype.h>
-
-#ifndef isascii
-#define isascii(c) 1
+#if defined (_MSC_VER)
+#include <stdio.h> /* for size_t */
 #endif
 
-#ifdef isblank
-#define ISBLANK(c) (isascii (c) && isblank (c))
-#else
-#define ISBLANK(c) ((c) == ' ' || (c) == '\t')
+/* On some systems, limits.h sets RE_DUP_MAX to a lower value than
+   GNU regex allows.  Include it before <regex.h>, which correctly
+   #undefs RE_DUP_MAX and sets it to the right value.  */
+#include <limits.h>
+
+#ifdef GAWK
+#undef alloca
+#define alloca alloca_is_bad_you_should_never_use_it
 #endif
-#ifdef isgraph
-#define ISGRAPH(c) (isascii (c) && isgraph (c))
-#else
-#define ISGRAPH(c) (isascii (c) && isprint (c) && !isspace (c))
+#include <regex.h>
+#include "regex_internal.h"
+
+#include "regex_internal.c"
+#ifdef GAWK
+#define bool int
+#define true (1)
+#define false (0)
 #endif
+#include "regcomp.c"
+#include "regexec.c"
 
-#define ISPRINT(c) (isascii (c) && isprint (c))
-#define ISDIGIT(c) (isascii (c) && isdigit (c))
-#define ISALNUM(c) (isascii (c) && isalnum (c))
-#define ISALPHA(c) (isascii (c) && isalpha (c))
-#define ISCNTRL(c) (isascii (c) && iscntrl (c))
-#define ISLOWER(c) (isascii (c) && islower (c))
-#define ISPUNCT(c) (isascii (c) && ispunct (c))
-#define ISSPACE(c) (isascii (c) && isspace (c))
-#define ISUPPER(c) (isascii (c) && isupper (c))
-#define ISXDIGIT(c) (isascii (c) && isxdigit (c))
-
-#ifndef NULL
-#define NULL 0
-#endif
-
-/* We remove any previous definition of `SIGN_EXTEND_CHAR',
-   since ours (we hope) works properly with all combinations of
-   machines, compilers, `char' and `unsigned char' argument types.
-   (Per Bothner suggested the basic approach.)  */
-#undef SIGN_EXTEND_CHAR
-#if __STDC__
-#define SIGN_EXTEND_CHAR(c) ((signed char) (c))
-#else  /* not __STDC__ */
-/* As in Harbison and Steele.  */
-#define SIGN_EXTEND_CHAR(c) ((((unsigned char) (c)) ^ 128) - 128)
-#endif
-
-/* Should we use malloc or alloca?  If REGEX_MALLOC is not defined, we
-   use `alloca' instead of `malloc'.  This is because using malloc in
-   re_search* or re_match* could cause memory leaks when C-g is used in
-   Emacs; also, malloc is slower and causes storage fragmentation.  On
-   the other hand, malloc is more portable, and easier to debug.
-
-   Because we sometimes use alloca, some routines have to be macros,
-   not functions -- `alloca'-allocated space disappears at the end of the
-   function it is called in.  */
-
-#ifdef REGEX_MALLOC
-
-#define REGEX_ALLOCATE malloc
-#define REGEX_REALLOCATE(source, osize, nsize) realloc (source, nsize)
-
-#else /* not REGEX_MALLOC  */
-
-/* Emacs already defines alloca, sometimes.  */
-#ifndef alloca
-
-/* Make alloca work the best possible way.  */
-#ifdef __GNUC__
-#define alloca __builtin_alloca
-#else /* not __GNUC__ */
-#if HAVE_ALLOCA_H
-#include <alloca.h>
-#else /* not __GNUC__ or HAVE_ALLOCA_H */
-#ifndef _AIX /* Already did AIX, up at the top.  */
-char *alloca ();
-#endif /* not _AIX */
-#endif /* not HAVE_ALLOCA_H */
-#endif /* not __GNUC__ */
-
-#endif /* not alloca */
-
-#define REGEX_ALLOCATE alloca
-
-/* Assumes a `char *destination' variable.  */
-#define REGEX_REALLOCATE(source, osize, nsize)				\
-  (destination = (char *) alloca (nsize),				\
-   bcopy (source, destination, osize),					\
-   destination)
-
-#endif /* not REGEX_MALLOC */
-
-
-/* True if `size1' is non-NULL and PTR is pointing anywhere inside
-   `string1' or just past its end.  This works if PTR is NULL, which is
-   a good thing.  */
-#define FIRST_STRING_P(ptr) 					\
-  (size1 && string1 <= (ptr) && (ptr) <= string1 + size1)
-
-/* (Re)Allocate N items of type T using malloc, or fail.  */
-#define TALLOC(n, t) ((t *) malloc ((n) * sizeof (t)))
-#define RETALLOC(addr, n, t) ((addr) = (t *) realloc (addr, (n) * sizeof (t)))
-#define REGEX_TALLOC(n, t) ((t *) REGEX_ALLOCATE ((n) * sizeof (t)))
-
-#define BYTEWIDTH 8 /* In bits.  */
-
-#define STREQ(s1, s2) ((strcmp (s1, s2) == 0))
-
-#define MAX(a, b) ((a) > (b) ? (a) : (b))
-#define MIN(a, b) ((a) < (b) ? (a) : (b))
-
-typedef char boolean;
-#define false 0
-#define true 1
-
-/* These are the command codes that appear in compiled regular
-   expressions.  Some opcodes are followed by argument bytes.  A
-   command code can specify any interpretation whatsoever for its
-   arguments.  Zero bytes may appear in the compiled regular expression.
-
-   The value of `exactn' is needed in search.c (search_buffer) in Emacs.
-   So regex.h defines a symbol `RE_EXACTN_VALUE' to be 1; the value of
-   `exactn' we use here must also be 1.  */
-
-typedef enum
-{
-  no_op = 0,
-
-	/* Followed by one byte giving n, then by n literal bytes.  */
-  exactn = 1,
-
-	/* Matches any (more or less) character.  */
-  anychar,
-
-	/* Matches any one char belonging to specified set.  First
-	   following byte is number of bitmap bytes.  Then come bytes
-	   for a bitmap saying which chars are in.  Bits in each byte
-	   are ordered low-bit-first.  A character is in the set if its
-	   bit is 1.  A character too large to have a bit in the map is
-	   automatically not in the set.  */
-  charset,
-
-	/* Same parameters as charset, but match any character that is
-	   not one of those specified.  */
-  charset_not,
-
-	/* Start remembering the text that is matched, for storing in a
-	   register.  Followed by one byte with the register number, in
-	   the range 0 to one less than the pattern buffer's re_nsub
-	   field.  Then followed by one byte with the number of groups
-	   inner to this one.  (This last has to be part of the
-	   start_memory only because we need it in the on_failure_jump
-	   of re_match_2.)  */
-  start_memory,
-
-	/* Stop remembering the text that is matched and store it in a
-	   memory register.  Followed by one byte with the register
-	   number, in the range 0 to one less than `re_nsub' in the
-	   pattern buffer, and one byte with the number of inner groups,
-	   just like `start_memory'.  (We need the number of inner
-	   groups here because we don't have any easy way of finding the
-	   corresponding start_memory when we're at a stop_memory.)  */
-  stop_memory,
-
-	/* Match a duplicate of something remembered. Followed by one
-	   byte containing the register number.  */
-  duplicate,
-
-	/* Fail unless at beginning of line.  */
-  begline,
-
-	/* Fail unless at end of line.  */
-  endline,
-
-	/* Succeeds if at beginning of buffer (if emacs) or at beginning
-	   of string to be matched (if not).  */
-  begbuf,
-
-	/* Analogously, for end of buffer/string.  */
-  endbuf,
-
-	/* Followed by two byte relative address to which to jump.  */
-  jump,
-
-	/* Same as jump, but marks the end of an alternative.  */
-  jump_past_alt,
-
-	/* Followed by two-byte relative address of place to resume at
-	   in case of failure.  */
-  on_failure_jump,
-
-	/* Like on_failure_jump, but pushes a placeholder instead of the
-	   current string position when executed.  */
-  on_failure_keep_string_jump,
-
-	/* Throw away latest failure point and then jump to following
-	   two-byte relative address.  */
-  pop_failure_jump,
-
-	/* Change to pop_failure_jump if know won't have to backtrack to
-	   match; otherwise change to jump.  This is used to jump
-	   back to the beginning of a repeat.  If what follows this jump
-	   clearly won't match what the repeat does, such that we can be
-	   sure that there is no use backtracking out of repetitions
-	   already matched, then we change it to a pop_failure_jump.
-	   Followed by two-byte address.  */
-  maybe_pop_jump,
-
-	/* Jump to following two-byte address, and push a dummy failure
-	   point. This failure point will be thrown away if an attempt
-	   is made to use it for a failure.  A `+' construct makes this
-	   before the first repeat.  Also used as an intermediary kind
-	   of jump when compiling an alternative.  */
-  dummy_failure_jump,
-
-	/* Push a dummy failure point and continue.  Used at the end of
-	   alternatives.  */
-  push_dummy_failure,
-
-	/* Followed by two-byte relative address and two-byte number n.
-	   After matching N times, jump to the address upon failure.  */
-  succeed_n,
-
-	/* Followed by two-byte relative address, and two-byte number n.
-	   Jump to the address N times, then fail.  */
-  jump_n,
-
-	/* Set the following two-byte relative address to the
-	   subsequent two-byte number.  The address *includes* the two
-	   bytes of number.  */
-  set_number_at,
-
-  wordchar,	/* Matches any word-constituent character.  */
-  notwordchar,	/* Matches any char that is not a word-constituent.  */
-
-  wordbeg,	/* Succeeds if at word beginning.  */
-  wordend,	/* Succeeds if at word end.  */
-
-  wordbound,	/* Succeeds if at a word boundary.  */
-  notwordbound	/* Succeeds if not at a word boundary.  */
-
-#ifdef emacs
-  ,before_dot,	/* Succeeds if before point.  */
-  at_dot,	/* Succeeds if at point.  */
-  after_dot,	/* Succeeds if after point.  */
-
-	/* Matches any character whose syntax is specified.  Followed by
-	   a byte which contains a syntax code, e.g., Sword.  */
-  syntaxspec,
-
-	/* Matches any character whose syntax is not that specified.  */
-  notsyntaxspec
-#endif /* emacs */
-} re_opcode_t;
-
-/* Common operations on the compiled pattern.  */
-
-/* Store NUMBER in two contiguous bytes starting at DESTINATION.  */
-
-#define STORE_NUMBER(destination, number)				\
-  do {									\
-    (destination)[0] = (number) & 0377;					\
-    (destination)[1] = (number) >> 8;					\
-  } while (0)
-
-/* Same as STORE_NUMBER, except increment DESTINATION to
-   the byte after where the number is stored.  Therefore, DESTINATION
-   must be an lvalue.  */
-
-#define STORE_NUMBER_AND_INCR(destination, number)			\
-  do {									\
-    STORE_NUMBER (destination, number);					\
-    (destination) += 2;							\
-  } while (0)
-
-/* Put into DESTINATION a number stored in two contiguous bytes starting
-   at SOURCE.  */
-
-#define EXTRACT_NUMBER(destination, source)				\
-  do {									\
-    (destination) = *(source) & 0377;					\
-    (destination) += SIGN_EXTEND_CHAR (*((source) + 1)) << 8;		\
-  } while (0)
-
-#ifdef DEBUG
-static void
-extract_number (dest, source)
-    int *dest;
-    unsigned char *source;
-{
-  int temp = SIGN_EXTEND_CHAR (*(source + 1));
-  *dest = *source & 0377;
-  *dest += temp << 8;
-}
-
-#ifndef EXTRACT_MACROS /* To debug the macros.  */
-#undef EXTRACT_NUMBER
-#define EXTRACT_NUMBER(dest, src) extract_number (&dest, src)
-#endif /* not EXTRACT_MACROS */
-
-#endif /* DEBUG */
-
-/* Same as EXTRACT_NUMBER, except increment SOURCE to after the number.
-   SOURCE must be an lvalue.  */
-
-#define EXTRACT_NUMBER_AND_INCR(destination, source)			\
-  do {									\
-    EXTRACT_NUMBER (destination, source);				\
-    (source) += 2; 							\
-  } while (0)
-
-#ifdef DEBUG
-static void
-extract_number_and_incr (destination, source)
-    int *destination;
-    unsigned char **source;
-{
-  extract_number (destination, *source);
-  *source += 2;
-}
-
-#ifndef EXTRACT_MACROS
-#undef EXTRACT_NUMBER_AND_INCR
-#define EXTRACT_NUMBER_AND_INCR(dest, src) \
-  extract_number_and_incr (&dest, &src)
-#endif /* not EXTRACT_MACROS */
-
-#endif /* DEBUG */
-
-/* If DEBUG is defined, Regex prints many voluminous messages about what
-   it is doing (if the variable `debug' is nonzero).  If linked with the
-   main program in `iregex.c', you can enter patterns and strings
-   interactively.  And if linked with the main program in `main.c' and
-   the other test files, you can run the already-written tests.  */
-
-#ifdef DEBUG
-
-/* We use standard I/O for debugging.  */
-#include <stdio.h>
-
-/* It is useful to test things that ``must'' be true when debugging.  */
-#include <assert.h>
-
-static int debug = 0;
-
-#define DEBUG_STATEMENT(e) e
-#define DEBUG_PRINT1(x) if (debug) printf (x)
-#define DEBUG_PRINT2(x1, x2) if (debug) printf (x1, x2)
-#define DEBUG_PRINT3(x1, x2, x3) if (debug) printf (x1, x2, x3)
-#define DEBUG_PRINT4(x1, x2, x3, x4) if (debug) printf (x1, x2, x3, x4)
-#define DEBUG_PRINT_COMPILED_PATTERN(p, s, e) 				\
-  if (debug) print_partial_compiled_pattern (s, e)
-#define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2)			\
-  if (debug) print_double_string (w, s1, sz1, s2, sz2)
-
-
-extern void printchar ();
-
-/* Print the fastmap in human-readable form.  */
-
-void
-print_fastmap (fastmap)
-    char *fastmap;
-{
-  unsigned was_a_range = 0;
-  unsigned i = 0;
-
-  while (i < (1 << BYTEWIDTH))
-    {
-      if (fastmap[i++])
-	{
-	  was_a_range = 0;
-	  printchar (i - 1);
-	  while (i < (1 << BYTEWIDTH)  &&  fastmap[i])
-	    {
-	      was_a_range = 1;
-	      i++;
-	    }
-	  if (was_a_range)
-	    {
-	      printf ("-");
-	      printchar (i - 1);
-	    }
-	}
-    }
-  putchar ('\n');
-}
-
-
-/* Print a compiled pattern string in human-readable form, starting at
-   the START pointer into it and ending just before the pointer END.  */
-
-void
-print_partial_compiled_pattern (start, end)
-    unsigned char *start;
-    unsigned char *end;
-{
-  int mcnt, mcnt2;
-  unsigned char *p = start;
-  unsigned char *pend = end;
-
-  if (start == NULL)
-    {
-      printf ("(null)\n");
-      return;
-    }
-
-  /* Loop over pattern commands.  */
-  while (p < pend)
-    {
-      switch ((re_opcode_t) *p++)
-	{
-	case no_op:
-	  printf ("/no_op");
-	  break;
-
-	case exactn:
-	  mcnt = *p++;
-	  printf ("/exactn/%d", mcnt);
-	  do
-	    {
-	      putchar ('/');
-	      printchar (*p++);
-	    }
-	  while (--mcnt);
-	  break;
-
-	case start_memory:
-	  mcnt = *p++;
-	  printf ("/start_memory/%d/%d", mcnt, *p++);
-	  break;
-
-	case stop_memory:
-	  mcnt = *p++;
-	  printf ("/stop_memory/%d/%d", mcnt, *p++);
-	  break;
-
-	case duplicate:
-	  printf ("/duplicate/%d", *p++);
-	  break;
-
-	case anychar:
-	  printf ("/anychar");
-	  break;
-
-	case charset:
-	case charset_not:
-	  {
-	    register int c;
-
-	    printf ("/charset%s",
-		    (re_opcode_t) *(p - 1) == charset_not ? "_not" : "");
-
-	    assert (p + *p < pend);
-
-	    for (c = 0; c < *p; c++)
-	      {
-		unsigned bit;
-		unsigned char map_byte = p[1 + c];
-
-		putchar ('/');
-
-		for (bit = 0; bit < BYTEWIDTH; bit++)
-		  if (map_byte & (1 << bit))
-		    printchar (c * BYTEWIDTH + bit);
-	      }
-	    p += 1 + *p;
-	    break;
-	  }
-
-	case begline:
-	  printf ("/begline");
-	  break;
-
-	case endline:
-	  printf ("/endline");
-	  break;
-
-	case on_failure_jump:
-	  extract_number_and_incr (&mcnt, &p);
-	  printf ("/on_failure_jump/0/%d", mcnt);
-	  break;
-
-	case on_failure_keep_string_jump:
-	  extract_number_and_incr (&mcnt, &p);
-	  printf ("/on_failure_keep_string_jump/0/%d", mcnt);
-	  break;
-
-	case dummy_failure_jump:
-	  extract_number_and_incr (&mcnt, &p);
-	  printf ("/dummy_failure_jump/0/%d", mcnt);
-	  break;
-
-	case push_dummy_failure:
-	  printf ("/push_dummy_failure");
-	  break;
-
-	case maybe_pop_jump:
-	  extract_number_and_incr (&mcnt, &p);
-	  printf ("/maybe_pop_jump/0/%d", mcnt);
-	  break;
-
-	case pop_failure_jump:
-	  extract_number_and_incr (&mcnt, &p);
-	  printf ("/pop_failure_jump/0/%d", mcnt);
-	  break;
-
-	case jump_past_alt:
-	  extract_number_and_incr (&mcnt, &p);
-	  printf ("/jump_past_alt/0/%d", mcnt);
-	  break;
-
-	case jump:
-	  extract_number_and_incr (&mcnt, &p);
-	  printf ("/jump/0/%d", mcnt);
-	  break;
-
-	case succeed_n:
-	  extract_number_and_incr (&mcnt, &p);
-	  extract_number_and_incr (&mcnt2, &p);
-	  printf ("/succeed_n/0/%d/0/%d", mcnt, mcnt2);
-	  break;
-
-	case jump_n:
-	  extract_number_and_incr (&mcnt, &p);
-	  extract_number_and_incr (&mcnt2, &p);
-	  printf ("/jump_n/0/%d/0/%d", mcnt, mcnt2);
-	  break;
-
-	case set_number_at:
-	  extract_number_and_incr (&mcnt, &p);
-	  extract_number_and_incr (&mcnt2, &p);
-	  printf ("/set_number_at/0/%d/0/%d", mcnt, mcnt2);
-	  break;
-
-	case wordbound:
-	  printf ("/wordbound");
-	  break;
-
-	case notwordbound:
-	  printf ("/notwordbound");
-	  break;
-
-	case wordbeg:
-	  printf ("/wordbeg");
-	  break;
-
-	case wordend:
-	  printf ("/wordend");
-
-#ifdef emacs
-	case before_dot:
-	  printf ("/before_dot");
-	  break;
-
-	case at_dot:
-	  printf ("/at_dot");
-	  break;
-
-	case after_dot:
-	  printf ("/after_dot");
-	  break;
-
-	case syntaxspec:
-	  printf ("/syntaxspec");
-	  mcnt = *p++;
-	  printf ("/%d", mcnt);
-	  break;
-
-	case notsyntaxspec:
-	  printf ("/notsyntaxspec");
-	  mcnt = *p++;
-	  printf ("/%d", mcnt);
-	  break;
-#endif /* emacs */
-
-	case wordchar:
-	  printf ("/wordchar");
-	  break;
-
-	case notwordchar:
-	  printf ("/notwordchar");
-	  break;
-
-	case begbuf:
-	  printf ("/begbuf");
-	  break;
-
-	case endbuf:
-	  printf ("/endbuf");
-	  break;
-
-	default:
-	  printf ("?%d", *(p-1));
-	}
-    }
-  printf ("/\n");
-}
-
-
-void
-print_compiled_pattern (bufp)
-    struct re_pattern_buffer *bufp;
-{
-  unsigned char *buffer = bufp->buffer;
-
-  print_partial_compiled_pattern (buffer, buffer + bufp->used);
-  printf ("%d bytes used/%d bytes allocated.\n", bufp->used, bufp->allocated);
-
-  if (bufp->fastmap_accurate && bufp->fastmap)
-    {
-      printf ("fastmap: ");
-      print_fastmap (bufp->fastmap);
-    }
-
-  printf ("re_nsub: %d\t", bufp->re_nsub);
-  printf ("regs_alloc: %d\t", bufp->regs_allocated);
-  printf ("can_be_null: %d\t", bufp->can_be_null);
-  printf ("newline_anchor: %d\n", bufp->newline_anchor);
-  printf ("no_sub: %d\t", bufp->no_sub);
-  printf ("not_bol: %d\t", bufp->not_bol);
-  printf ("not_eol: %d\t", bufp->not_eol);
-  printf ("syntax: %d\n", bufp->syntax);
-  /* Perhaps we should print the translate table?  */
-}
-
-
-void
-print_double_string (where, string1, size1, string2, size2)
-    const char *where;
-    const char *string1;
-    const char *string2;
-    int size1;
-    int size2;
-{
-  unsigned this_char;
-
-  if (where == NULL)
-    printf ("(null)");
-  else
-    {
-      if (FIRST_STRING_P (where))
-	{
-	  for (this_char = where - string1; this_char < size1; this_char++)
-	    printchar (string1[this_char]);
-
-	  where = string2;
-	}
-
-      for (this_char = where - string2; this_char < size2; this_char++)
-	printchar (string2[this_char]);
-    }
-}
-
-#else /* not DEBUG */
-
-#undef assert
-#define assert(e)
-
-#define DEBUG_STATEMENT(e)
-#define DEBUG_PRINT1(x)
-#define DEBUG_PRINT2(x1, x2)
-#define DEBUG_PRINT3(x1, x2, x3)
-#define DEBUG_PRINT4(x1, x2, x3, x4)
-#define DEBUG_PRINT_COMPILED_PATTERN(p, s, e)
-#define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2)
-
-#endif /* not DEBUG */
-
-/* Set by `re_set_syntax' to the current regexp syntax to recognize.  Can
-   also be assigned to arbitrarily: each pattern buffer stores its own
-   syntax, so it can be changed between regex compilations.  */
-reg_syntax_t re_syntax_options = RE_SYNTAX_EMACS;
-
-
-/* Specify the precise syntax of regexps for compilation.  This provides
-   for compatibility for various utilities which historically have
-   different, incompatible syntaxes.
-
-   The argument SYNTAX is a bit mask comprised of the various bits
-   defined in regex.h.  We return the old syntax.  */
-
-reg_syntax_t
-re_set_syntax (syntax)
-    reg_syntax_t syntax;
-{
-  reg_syntax_t ret = re_syntax_options;
-
-  re_syntax_options = syntax;
-  return ret;
-}
-
-/* This table gives an error message for each of the error codes listed
-   in regex.h.  Obviously the order here has to be same as there.  */
-
-static const char *re_error_msg[] =
-  { NULL,					/* REG_NOERROR */
-    "No match",					/* REG_NOMATCH */
-    "Invalid regular expression",		/* REG_BADPAT */
-    "Invalid collation character",		/* REG_ECOLLATE */
-    "Invalid character class name",		/* REG_ECTYPE */
-    "Trailing backslash",			/* REG_EESCAPE */
-    "Invalid back reference",			/* REG_ESUBREG */
-    "Unmatched [ or [^",			/* REG_EBRACK */
-    "Unmatched ( or \\(",			/* REG_EPAREN */
-    "Unmatched \\{",				/* REG_EBRACE */
-    "Invalid content of \\{\\}",		/* REG_BADBR */
-    "Invalid range end",			/* REG_ERANGE */
-    "Memory exhausted",				/* REG_ESPACE */
-    "Invalid preceding regular expression",	/* REG_BADRPT */
-    "Premature end of regular expression",	/* REG_EEND */
-    "Regular expression too big",		/* REG_ESIZE */
-    "Unmatched ) or \\)",			/* REG_ERPAREN */
-  };
-
-/* Subroutine declarations and macros for regex_compile.  */
-
-static void store_op1 (), store_op2 ();
-static void insert_op1 (), insert_op2 ();
-static boolean at_begline_loc_p (), at_endline_loc_p ();
-static boolean group_in_compile_stack ();
-static reg_errcode_t compile_range ();
-
-/* Fetch the next character in the uncompiled pattern---translating it
-   if necessary.  Also cast from a signed character in the constant
-   string passed to us by the user to an unsigned char that we can use
-   as an array index (in, e.g., `translate').  */
-#define PATFETCH(c)							\
-  do {if (p == pend) return REG_EEND;					\
-    c = (unsigned char) *p++;						\
-    if (translate) c = translate[c]; 					\
-  } while (0)
-
-/* Fetch the next character in the uncompiled pattern, with no
-   translation.  */
-#define PATFETCH_RAW(c)							\
-  do {if (p == pend) return REG_EEND;					\
-    c = (unsigned char) *p++; 						\
-  } while (0)
-
-/* Go backwards one character in the pattern.  */
-#define PATUNFETCH p--
-
-
-/* If `translate' is non-null, return translate[D], else just D.  We
-   cast the subscript to translate because some data is declared as
-   `char *', to avoid warnings when a string constant is passed.  But
-   when we use a character as a subscript we must make it unsigned.  */
-#define TRANSLATE(d) (translate ? translate[(unsigned char) (d)] : (d))
-
-
-/* Macros for outputting the compiled pattern into `buffer'.  */
-
-/* If the buffer isn't allocated when it comes in, use this.  */
-#define INIT_BUF_SIZE  32
-
-/* Make sure we have at least N more bytes of space in buffer.  */
-#define GET_BUFFER_SPACE(n)						\
-    while (b - bufp->buffer + (n) > bufp->allocated)			\
-      EXTEND_BUFFER ()
-
-/* Make sure we have one more byte of buffer space and then add C to it.  */
-#define BUF_PUSH(c)							\
-  do {									\
-    GET_BUFFER_SPACE (1);						\
-    *b++ = (unsigned char) (c);						\
-  } while (0)
-
-
-/* Ensure we have two more bytes of buffer space and then append C1 and C2.  */
-#define BUF_PUSH_2(c1, c2)						\
-  do {									\
-    GET_BUFFER_SPACE (2);						\
-    *b++ = (unsigned char) (c1);					\
-    *b++ = (unsigned char) (c2);					\
-  } while (0)
-
-
-/* As with BUF_PUSH_2, except for three bytes.  */
-#define BUF_PUSH_3(c1, c2, c3)						\
-  do {									\
-    GET_BUFFER_SPACE (3);						\
-    *b++ = (unsigned char) (c1);					\
-    *b++ = (unsigned char) (c2);					\
-    *b++ = (unsigned char) (c3);					\
-  } while (0)
-
-
-/* Store a jump with opcode OP at LOC to location TO.  We store a
-   relative address offset by the three bytes the jump itself occupies.  */
-#define STORE_JUMP(op, loc, to) \
-  store_op1 (op, loc, (to) - (loc) - 3)
-
-/* Likewise, for a two-argument jump.  */
-#define STORE_JUMP2(op, loc, to, arg) \
-  store_op2 (op, loc, (to) - (loc) - 3, arg)
-
-/* Like `STORE_JUMP', but for inserting.  Assume `b' is the buffer end.  */
-#define INSERT_JUMP(op, loc, to) \
-  insert_op1 (op, loc, (to) - (loc) - 3, b)
-
-/* Like `STORE_JUMP2', but for inserting.  Assume `b' is the buffer end.  */
-#define INSERT_JUMP2(op, loc, to, arg) \
-  insert_op2 (op, loc, (to) - (loc) - 3, arg, b)
-
-
-/* This is not an arbitrary limit: the arguments which represent offsets
-   into the pattern are two bytes long.  So if 2^16 bytes turns out to
-   be too small, many things would have to change.  */
-#define MAX_BUF_SIZE (1L << 16)
-
-
-/* Extend the buffer by twice its current size via realloc and
-   reset the pointers that pointed into the old block to point to the
-   correct places in the new one.  If extending the buffer results in it
-   being larger than MAX_BUF_SIZE, then flag memory exhausted.  */
-#define EXTEND_BUFFER()							\
-  do { 									\
-    unsigned char *old_buffer = bufp->buffer;				\
-    if (bufp->allocated == MAX_BUF_SIZE) 				\
-      return REG_ESIZE;							\
-    bufp->allocated <<= 1;						\
-    if (bufp->allocated > MAX_BUF_SIZE)					\
-      bufp->allocated = MAX_BUF_SIZE; 					\
-    bufp->buffer = (unsigned char *) realloc (bufp->buffer, bufp->allocated);\
-    if (bufp->buffer == NULL)						\
-      return REG_ESPACE;						\
-    /* If the buffer moved, move all the pointers into it.  */		\
-    if (old_buffer != bufp->buffer)					\
-      {									\
-	b = (b - old_buffer) + bufp->buffer;				\
-	begalt = (begalt - old_buffer) + bufp->buffer;			\
-	if (fixup_alt_jump)						\
-	  fixup_alt_jump = (fixup_alt_jump - old_buffer) + bufp->buffer;\
-	if (laststart)							\
-	  laststart = (laststart - old_buffer) + bufp->buffer;		\
-	if (pending_exact)						\
-	  pending_exact = (pending_exact - old_buffer) + bufp->buffer;	\
-      }									\
-  } while (0)
-
-
-/* Since we have one byte reserved for the register number argument to
-   {start,stop}_memory, the maximum number of groups we can report
-   things about is what fits in that byte.  */
-#define MAX_REGNUM 255
-
-/* But patterns can have more than `MAX_REGNUM' registers.  We just
-   ignore the excess.  */
-typedef unsigned regnum_t;
-
-
-/* Macros for the compile stack.  */
-
-/* Since offsets can go either forwards or backwards, this type needs to
-   be able to hold values from -(MAX_BUF_SIZE - 1) to MAX_BUF_SIZE - 1.  */
-typedef int pattern_offset_t;
-
-typedef struct
-{
-  pattern_offset_t begalt_offset;
-  pattern_offset_t fixup_alt_jump;
-  pattern_offset_t inner_group_offset;
-  pattern_offset_t laststart_offset;
-  regnum_t regnum;
-} compile_stack_elt_t;
-
-
-typedef struct
-{
-  compile_stack_elt_t *stack;
-  unsigned size;
-  unsigned avail;			/* Offset of next open position.  */
-} compile_stack_type;
-
-
-#define INIT_COMPILE_STACK_SIZE 32
-
-#define COMPILE_STACK_EMPTY  (compile_stack.avail == 0)
-#define COMPILE_STACK_FULL  (compile_stack.avail == compile_stack.size)
-
-/* The next available element.  */
-#define COMPILE_STACK_TOP (compile_stack.stack[compile_stack.avail])
-
-
-/* Set the bit for character C in a list.  */
-#define SET_LIST_BIT(c)                               \
-  (b[((unsigned char) (c)) / BYTEWIDTH]               \
-   |= 1 << (((unsigned char) c) % BYTEWIDTH))
-
-
-/* Get the next unsigned number in the uncompiled pattern.  */
-#define GET_UNSIGNED_NUMBER(num) 					\
-  { if (p != pend)							\
-     {									\
-       PATFETCH (c); 							\
-       while (ISDIGIT (c)) 						\
-	 { 								\
-	   if (num < 0)							\
-	      num = 0;							\
-	   num = num * 10 + c - '0'; 					\
-	   if (p == pend) 						\
-	      break; 							\
-	   PATFETCH (c);						\
-	 } 								\
-       } 								\
-    }
-
-#define CHAR_CLASS_MAX_LENGTH  6 /* Namely, `xdigit'.  */
-
-#define IS_CHAR_CLASS(string)						\
-   (STREQ (string, "alpha") || STREQ (string, "upper")			\
-    || STREQ (string, "lower") || STREQ (string, "digit")		\
-    || STREQ (string, "alnum") || STREQ (string, "xdigit")		\
-    || STREQ (string, "space") || STREQ (string, "print")		\
-    || STREQ (string, "punct") || STREQ (string, "graph")		\
-    || STREQ (string, "cntrl") || STREQ (string, "blank"))
-
-/* `regex_compile' compiles PATTERN (of length SIZE) according to SYNTAX.
-   Returns one of error codes defined in `regex.h', or zero for success.
-
-   Assumes the `allocated' (and perhaps `buffer') and `translate'
-   fields are set in BUFP on entry.
-
-   If it succeeds, results are put in BUFP (if it returns an error, the
-   contents of BUFP are undefined):
-     `buffer' is the compiled pattern;
-     `syntax' is set to SYNTAX;
-     `used' is set to the length of the compiled pattern;
-     `fastmap_accurate' is zero;
-     `re_nsub' is the number of subexpressions in PATTERN;
-     `not_bol' and `not_eol' are zero;
-
-   The `fastmap' and `newline_anchor' fields are neither
-   examined nor set.  */
-
-static reg_errcode_t
-regex_compile (pattern, size, syntax, bufp)
-     const char *pattern;
-     int size;
-     reg_syntax_t syntax;
-     struct re_pattern_buffer *bufp;
-{
-  /* We fetch characters from PATTERN here.  Even though PATTERN is
-     `char *' (i.e., signed), we declare these variables as unsigned, so
-     they can be reliably used as array indices.  */
-  register unsigned char c, c1;
-
-  /* A random temporary spot in PATTERN.  */
-  const char *p1;
-
-  /* Points to the end of the buffer, where we should append.  */
-  register unsigned char *b;
-
-  /* Keeps track of unclosed groups.  */
-  compile_stack_type compile_stack;
-
-  /* Points to the current (ending) position in the pattern.  */
-  const char *p = pattern;
-  const char *pend = pattern + size;
-
-  /* How to translate the characters in the pattern.  */
-  char *translate = bufp->translate;
-
-  /* Address of the count-byte of the most recently inserted `exactn'
-     command.  This makes it possible to tell if a new exact-match
-     character can be added to that command or if the character requires
-     a new `exactn' command.  */
-  unsigned char *pending_exact = 0;
-
-  /* Address of start of the most recently finished expression.
-     This tells, e.g., postfix * where to find the start of its
-     operand.  Reset at the beginning of groups and alternatives.  */
-  unsigned char *laststart = 0;
-
-  /* Address of beginning of regexp, or inside of last group.  */
-  unsigned char *begalt;
-
-  /* Place in the uncompiled pattern (i.e., the {) to
-     which to go back if the interval is invalid.  */
-  const char *beg_interval;
-
-  /* Address of the place where a forward jump should go to the end of
-     the containing expression.  Each alternative of an `or' -- except the
-     last -- ends with a forward jump of this sort.  */
-  unsigned char *fixup_alt_jump = 0;
-
-  /* Counts open-groups as they are encountered.  Remembered for the
-     matching close-group on the compile stack, so the same register
-     number is put in the stop_memory as the start_memory.  */
-  regnum_t regnum = 0;
-
-#ifdef DEBUG
-  DEBUG_PRINT1 ("\nCompiling pattern: ");
-  if (debug)
-    {
-      unsigned debug_count;
-
-      for (debug_count = 0; debug_count < size; debug_count++)
-	printchar (pattern[debug_count]);
-      putchar ('\n');
-    }
-#endif /* DEBUG */
-
-  /* Initialize the compile stack.  */
-  compile_stack.stack = TALLOC (INIT_COMPILE_STACK_SIZE, compile_stack_elt_t);
-  if (compile_stack.stack == NULL)
-    return REG_ESPACE;
-
-  compile_stack.size = INIT_COMPILE_STACK_SIZE;
-  compile_stack.avail = 0;
-
-  /* Initialize the pattern buffer.  */
-  bufp->syntax = syntax;
-  bufp->fastmap_accurate = 0;
-  bufp->not_bol = bufp->not_eol = 0;
-
-  /* Set `used' to zero, so that if we return an error, the pattern
-     printer (for debugging) will think there's no pattern.  We reset it
-     at the end.  */
-  bufp->used = 0;
-
-  /* Always count groups, whether or not bufp->no_sub is set.  */
-  bufp->re_nsub = 0;
-
-#if !defined (emacs) && !defined (SYNTAX_TABLE)
-  /* Initialize the syntax table.  */
-   init_syntax_once ();
-#endif
-
-  if (bufp->allocated == 0)
-    {
-      if (bufp->buffer)
-	{ /* If zero allocated, but buffer is non-null, try to realloc
-	     enough space.  This loses if buffer's address is bogus, but
-	     that is the user's responsibility.  */
-	  RETALLOC (bufp->buffer, INIT_BUF_SIZE, unsigned char);
-	}
-      else
-	{ /* Caller did not allocate a buffer.  Do it for them.  */
-	  bufp->buffer = TALLOC (INIT_BUF_SIZE, unsigned char);
-	}
-      if (!bufp->buffer) return REG_ESPACE;
-
-      bufp->allocated = INIT_BUF_SIZE;
-    }
-
-  begalt = b = bufp->buffer;
-
-  /* Loop through the uncompiled pattern until we're at the end.  */
-  while (p != pend)
-    {
-      PATFETCH (c);
-
-      switch (c)
-	{
-	case '^':
-	  {
-	    if (   /* If at start of pattern, it's an operator.  */
-		   p == pattern + 1
-		   /* If context independent, it's an operator.  */
-		|| syntax & RE_CONTEXT_INDEP_ANCHORS
-		   /* Otherwise, depends on what's come before.  */
-		|| at_begline_loc_p (pattern, p, syntax))
-	      BUF_PUSH (begline);
-	    else
-	      goto normal_char;
-	  }
-	  break;
-
-
-	case '$':
-	  {
-	    if (   /* If at end of pattern, it's an operator.  */
-		   p == pend
-		   /* If context independent, it's an operator.  */
-		|| syntax & RE_CONTEXT_INDEP_ANCHORS
-		   /* Otherwise, depends on what's next.  */
-		|| at_endline_loc_p (p, pend, syntax))
-	       BUF_PUSH (endline);
-	     else
-	       goto normal_char;
-	   }
-	   break;
-
-
-	case '+':
-	case '?':
-	  if ((syntax & RE_BK_PLUS_QM)
-	      || (syntax & RE_LIMITED_OPS))
-	    goto normal_char;
-	handle_plus:
-	case '*':
-	  /* If there is no previous pattern... */
-	  if (!laststart)
-	    {
-	      if (syntax & RE_CONTEXT_INVALID_OPS)
-		return REG_BADRPT;
-	      else if (!(syntax & RE_CONTEXT_INDEP_OPS))
-		goto normal_char;
-	    }
-
-	  {
-	    /* Are we optimizing this jump?  */
-	    boolean keep_string_p = false;
-
-	    /* 1 means zero (many) matches is allowed.  */
-	    char zero_times_ok = 0, many_times_ok = 0;
-
-	    /* If there is a sequence of repetition chars, collapse it
-	       down to just one (the right one).  We can't combine
-	       interval operators with these because of, e.g., `a{2}*',
-	       which should only match an even number of `a's.  */
-
-	    for (;;)
-	      {
-		zero_times_ok |= c != '+';
-		many_times_ok |= c != '?';
-
-		if (p == pend)
-		  break;
-
-		PATFETCH (c);
-
-		if (c == '*'
-		    || (!(syntax & RE_BK_PLUS_QM) && (c == '+' || c == '?')))
-		  ;
-
-		else if (syntax & RE_BK_PLUS_QM  &&  c == '\\')
-		  {
-		    if (p == pend) return REG_EESCAPE;
-
-		    PATFETCH (c1);
-		    if (!(c1 == '+' || c1 == '?'))
-		      {
-			PATUNFETCH;
-			PATUNFETCH;
-			break;
-		      }
-
-		    c = c1;
-		  }
-		else
-		  {
-		    PATUNFETCH;
-		    break;
-		  }
-
-		/* If we get here, we found another repeat character.  */
-	       }
-
-	    /* Star, etc. applied to an empty pattern is equivalent
-	       to an empty pattern.  */
-	    if (!laststart)
-	      break;
-
-	    /* Now we know whether or not zero matches is allowed
-	       and also whether or not two or more matches is allowed.  */
-	    if (many_times_ok)
-	      { /* More than one repetition is allowed, so put in at the
-		   end a backward relative jump from `b' to before the next
-		   jump we're going to put in below (which jumps from
-		   laststart to after this jump).
-
-		   But if we are at the `*' in the exact sequence `.*\n',
-		   insert an unconditional jump backwards to the .,
-		   instead of the beginning of the loop.  This way we only
-		   push a failure point once, instead of every time
-		   through the loop.  */
-		assert (p - 1 > pattern);
-
-		/* Allocate the space for the jump.  */
-		GET_BUFFER_SPACE (3);
-
-		/* We know we are not at the first character of the pattern,
-		   because laststart was nonzero.  And we've already
-		   incremented `p', by the way, to be the character after
-		   the `*'.  Do we have to do something analogous here
-		   for null bytes, because of RE_DOT_NOT_NULL?  */
-		if (TRANSLATE (*(p - 2)) == TRANSLATE ('.')
-		    && zero_times_ok
-		    && p < pend && TRANSLATE (*p) == TRANSLATE ('\n')
-		    && !(syntax & RE_DOT_NEWLINE))
-		  { /* We have .*\n.  */
-		    STORE_JUMP (jump, b, laststart);
-		    keep_string_p = true;
-		  }
-		else
-		  /* Anything else.  */
-		  STORE_JUMP (maybe_pop_jump, b, laststart - 3);
-
-		/* We've added more stuff to the buffer.  */
-		b += 3;
-	      }
-
-	    /* On failure, jump from laststart to b + 3, which will be the
-	       end of the buffer after this jump is inserted.  */
-	    GET_BUFFER_SPACE (3);
-	    INSERT_JUMP (keep_string_p ? on_failure_keep_string_jump
-				       : on_failure_jump,
-			 laststart, b + 3);
-	    pending_exact = 0;
-	    b += 3;
-
-	    if (!zero_times_ok)
-	      {
-		/* At least one repetition is required, so insert a
-		   `dummy_failure_jump' before the initial
-		   `on_failure_jump' instruction of the loop. This
-		   effects a skip over that instruction the first time
-		   we hit that loop.  */
-		GET_BUFFER_SPACE (3);
-		INSERT_JUMP (dummy_failure_jump, laststart, laststart + 6);
-		b += 3;
-	      }
-	    }
-	  break;
-
-
-	case '.':
-	  laststart = b;
-	  BUF_PUSH (anychar);
-	  break;
-
-
-	case '[':
-	  {
-	    boolean had_char_class = false;
-
-	    if (p == pend) return REG_EBRACK;
-
-	    /* Ensure that we have enough space to push a charset: the
-	       opcode, the length count, and the bitset; 34 bytes in all.  */
-	    GET_BUFFER_SPACE (34);
-
-	    laststart = b;
-
-	    /* We test `*p == '^' twice, instead of using an if
-	       statement, so we only need one BUF_PUSH.  */
-	    BUF_PUSH (*p == '^' ? charset_not : charset);
-	    if (*p == '^')
-	      p++;
-
-	    /* Remember the first position in the bracket expression.  */
-	    p1 = p;
-
-	    /* Push the number of bytes in the bitmap.  */
-	    BUF_PUSH ((1 << BYTEWIDTH) / BYTEWIDTH);
-
-	    /* Clear the whole map.  */
-	    bzero (b, (1 << BYTEWIDTH) / BYTEWIDTH);
-
-	    /* charset_not matches newline according to a syntax bit.  */
-	    if ((re_opcode_t) b[-2] == charset_not
-		&& (syntax & RE_HAT_LISTS_NOT_NEWLINE))
-	      SET_LIST_BIT ('\n');
-
-	    /* Read in characters and ranges, setting map bits.  */
-	    for (;;)
-	      {
-		if (p == pend) return REG_EBRACK;
-
-		PATFETCH (c);
-
-		/* \ might escape characters inside [...] and [^...].  */
-		if ((syntax & RE_BACKSLASH_ESCAPE_IN_LISTS) && c == '\\')
-		  {
-		    if (p == pend) return REG_EESCAPE;
-
-		    PATFETCH (c1);
-		    SET_LIST_BIT (c1);
-		    continue;
-		  }
-
-		/* Could be the end of the bracket expression.  If it's
-		   not (i.e., when the bracket expression is `[]' so
-		   far), the ']' character bit gets set way below.  */
-		if (c == ']' && p != p1 + 1)
-		  break;
-
-		/* Look ahead to see if it's a range when the last thing
-		   was a character class.  */
-		if (had_char_class && c == '-' && *p != ']')
-		  return REG_ERANGE;
-
-		/* Look ahead to see if it's a range when the last thing
-		   was a character: if this is a hyphen not at the
-		   beginning or the end of a list, then it's the range
-		   operator.  */
-		if (c == '-'
-		    && !(p - 2 >= pattern && p[-2] == '[')
-		    && !(p - 3 >= pattern && p[-3] == '[' && p[-2] == '^')
-		    && *p != ']')
-		  {
-		    reg_errcode_t ret
-		      = compile_range (&p, pend, translate, syntax, b);
-		    if (ret != REG_NOERROR) return ret;
-		  }
-
-		else if (p[0] == '-' && p[1] != ']')
-		  { /* This handles ranges made up of characters only.  */
-		    reg_errcode_t ret;
-
-		    /* Move past the `-'.  */
-		    PATFETCH (c1);
-
-		    ret = compile_range (&p, pend, translate, syntax, b);
-		    if (ret != REG_NOERROR) return ret;
-		  }
-
-		/* See if we're at the beginning of a possible character
-		   class.  */
-
-		else if (syntax & RE_CHAR_CLASSES && c == '[' && *p == ':')
-		  { /* Leave room for the null.  */
-		    char str[CHAR_CLASS_MAX_LENGTH + 1];
-
-		    PATFETCH (c);
-		    c1 = 0;
-
-		    /* If pattern is `[[:'.  */
-		    if (p == pend) return REG_EBRACK;
-
-		    for (;;)
-		      {
-			PATFETCH (c);
-			if (c == ':' || c == ']' || p == pend
-			    || c1 == CHAR_CLASS_MAX_LENGTH)
-			  break;
-			str[c1++] = c;
-		      }
-		    str[c1] = '\0';
-
-		    /* If isn't a word bracketed by `[:' and:`]':
-		       undo the ending character, the letters, and leave
-		       the leading `:' and `[' (but set bits for them).  */
-		    if (c == ':' && *p == ']')
-		      {
-			int ch;
-			boolean is_alnum = STREQ (str, "alnum");
-			boolean is_alpha = STREQ (str, "alpha");
-			boolean is_blank = STREQ (str, "blank");
-			boolean is_cntrl = STREQ (str, "cntrl");
-			boolean is_digit = STREQ (str, "digit");
-			boolean is_graph = STREQ (str, "graph");
-			boolean is_lower = STREQ (str, "lower");
-			boolean is_print = STREQ (str, "print");
-			boolean is_punct = STREQ (str, "punct");
-			boolean is_space = STREQ (str, "space");
-			boolean is_upper = STREQ (str, "upper");
-			boolean is_xdigit = STREQ (str, "xdigit");
-
-			if (!IS_CHAR_CLASS (str)) return REG_ECTYPE;
-
-			/* Throw away the ] at the end of the character
-			   class.  */
-			PATFETCH (c);
-
-			if (p == pend) return REG_EBRACK;
-
-			for (ch = 0; ch < 1 << BYTEWIDTH; ch++)
-			  {
-			    if (   (is_alnum  && ISALNUM (ch))
-				|| (is_alpha  && ISALPHA (ch))
-				|| (is_blank  && ISBLANK (ch))
-				|| (is_cntrl  && ISCNTRL (ch))
-				|| (is_digit  && ISDIGIT (ch))
-				|| (is_graph  && ISGRAPH (ch))
-				|| (is_lower  && ISLOWER (ch))
-				|| (is_print  && ISPRINT (ch))
-				|| (is_punct  && ISPUNCT (ch))
-				|| (is_space  && ISSPACE (ch))
-				|| (is_upper  && ISUPPER (ch))
-				|| (is_xdigit && ISXDIGIT (ch)))
-			    SET_LIST_BIT (ch);
-			  }
-			had_char_class = true;
-		      }
-		    else
-		      {
-			c1++;
-			while (c1--)
-			  PATUNFETCH;
-			SET_LIST_BIT ('[');
-			SET_LIST_BIT (':');
-			had_char_class = false;
-		      }
-		  }
-		else
-		  {
-		    had_char_class = false;
-		    SET_LIST_BIT (c);
-		  }
-	      }
-
-	    /* Discard any (non)matching list bytes that are all 0 at the
-	       end of the map.  Decrease the map-length byte too.  */
-	    while ((int) b[-1] > 0 && b[b[-1] - 1] == 0)
-	      b[-1]--;
-	    b += b[-1];
-	  }
-	  break;
-
-
-	case '(':
-	  if (syntax & RE_NO_BK_PARENS)
-	    goto handle_open;
-	  else
-	    goto normal_char;
-
-
-	case ')':
-	  if (syntax & RE_NO_BK_PARENS)
-	    goto handle_close;
-	  else
-	    goto normal_char;
-
-
-	case '\n':
-	  if (syntax & RE_NEWLINE_ALT)
-	    goto handle_alt;
-	  else
-	    goto normal_char;
-
-
-	case '|':
-	  if (syntax & RE_NO_BK_VBAR)
-	    goto handle_alt;
-	  else
-	    goto normal_char;
-
-
-	case '{':
-	   if (syntax & RE_INTERVALS && syntax & RE_NO_BK_BRACES)
-	     goto handle_interval;
-	   else
-	     goto normal_char;
-
-
-	case '\\':
-	  if (p == pend) return REG_EESCAPE;
-
-	  /* Do not translate the character after the \, so that we can
-	     distinguish, e.g., \B from \b, even if we normally would
-	     translate, e.g., B to b.  */
-	  PATFETCH_RAW (c);
-
-	  switch (c)
-	    {
-	    case '(':
-	      if (syntax & RE_NO_BK_PARENS)
-		goto normal_backslash;
-
-	    handle_open:
-	      bufp->re_nsub++;
-	      regnum++;
-
-	      if (COMPILE_STACK_FULL)
-		{
-		  RETALLOC (compile_stack.stack, compile_stack.size << 1,
-			    compile_stack_elt_t);
-		  if (compile_stack.stack == NULL) return REG_ESPACE;
-
-		  compile_stack.size <<= 1;
-		}
-
-	      /* These are the values to restore when we hit end of this
-		 group.  They are all relative offsets, so that if the
-		 whole pattern moves because of realloc, they will still
-		 be valid.  */
-	      COMPILE_STACK_TOP.begalt_offset = begalt - bufp->buffer;
-	      COMPILE_STACK_TOP.fixup_alt_jump
-		= fixup_alt_jump ? fixup_alt_jump - bufp->buffer + 1 : 0;
-	      COMPILE_STACK_TOP.laststart_offset = b - bufp->buffer;
-	      COMPILE_STACK_TOP.regnum = regnum;
-
-	      /* We will eventually replace the 0 with the number of
-		 groups inner to this one.  But do not push a
-		 start_memory for groups beyond the last one we can
-		 represent in the compiled pattern.  */
-	      if (regnum <= MAX_REGNUM)
-		{
-		  COMPILE_STACK_TOP.inner_group_offset = b - bufp->buffer + 2;
-		  BUF_PUSH_3 (start_memory, regnum, 0);
-		}
-
-	      compile_stack.avail++;
-
-	      fixup_alt_jump = 0;
-	      laststart = 0;
-	      begalt = b;
-	      /* If we've reached MAX_REGNUM groups, then this open
-		 won't actually generate any code, so we'll have to
-		 clear pending_exact explicitly.  */
-	      pending_exact = 0;
-	      break;
-
-
-	    case ')':
-	      if (syntax & RE_NO_BK_PARENS) goto normal_backslash;
-
-	      if (COMPILE_STACK_EMPTY)
-	      {
-		if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)
-		  goto normal_backslash;
-		else
-		  return REG_ERPAREN;
-	      }
-
-	    handle_close:
-	      if (fixup_alt_jump)
-		{ /* Push a dummy failure point at the end of the
-		     alternative for a possible future
-		     `pop_failure_jump' to pop.  See comments at
-		     `push_dummy_failure' in `re_match_2'.  */
-		  BUF_PUSH (push_dummy_failure);
-
-		  /* We allocated space for this jump when we assigned
-		     to `fixup_alt_jump', in the `handle_alt' case below.  */
-		  STORE_JUMP (jump_past_alt, fixup_alt_jump, b - 1);
-		}
-
-	      /* See similar code for backslashed left paren above.  */
-	      if (COMPILE_STACK_EMPTY)
-	      {
-		if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)
-		  goto normal_char;
-		else
-		  return REG_ERPAREN;
-	      }
-
-	      /* Since we just checked for an empty stack above, this
-		 ``can't happen''.  */
-	      assert (compile_stack.avail != 0);
-	      {
-		/* We don't just want to restore into `regnum', because
-		   later groups should continue to be numbered higher,
-		   as in `(ab)c(de)' -- the second group is #2.  */
-		regnum_t this_group_regnum;
-
-		compile_stack.avail--;
-		begalt = bufp->buffer + COMPILE_STACK_TOP.begalt_offset;
-		fixup_alt_jump
-		  = COMPILE_STACK_TOP.fixup_alt_jump
-		    ? bufp->buffer + COMPILE_STACK_TOP.fixup_alt_jump - 1
-		    : 0;
-		laststart = bufp->buffer + COMPILE_STACK_TOP.laststart_offset;
-		this_group_regnum = COMPILE_STACK_TOP.regnum;
-		/* If we've reached MAX_REGNUM groups, then this open
-		   won't actually generate any code, so we'll have to
-		   clear pending_exact explicitly.  */
-		pending_exact = 0;
-
-		/* We're at the end of the group, so now we know how many
-		   groups were inside this one.  */
-		if (this_group_regnum <= MAX_REGNUM)
-		  {
-		    unsigned char *inner_group_loc
-		      = bufp->buffer + COMPILE_STACK_TOP.inner_group_offset;
-
-		    *inner_group_loc = regnum - this_group_regnum;
-		    BUF_PUSH_3 (stop_memory, this_group_regnum,
-				regnum - this_group_regnum);
-		  }
-	      }
-	      break;
-
-
-	    case '|':					/* `\|'.  */
-	      if (syntax & RE_LIMITED_OPS || syntax & RE_NO_BK_VBAR)
-		goto normal_backslash;
-	    handle_alt:
-	      if (syntax & RE_LIMITED_OPS)
-		goto normal_char;
-
-	      /* Insert before the previous alternative a jump which
-		 jumps to this alternative if the former fails.  */
-	      GET_BUFFER_SPACE (3);
-	      INSERT_JUMP (on_failure_jump, begalt, b + 6);
-	      pending_exact = 0;
-	      b += 3;
-
-	      /* The alternative before this one has a jump after it
-		 which gets executed if it gets matched.  Adjust that
-		 jump so it will jump to this alternative's analogous
-		 jump (put in below, which in turn will jump to the next
-		 (if any) alternative's such jump, etc.).  The last such
-		 jump jumps to the correct final destination.  A picture:
-			  _____ _____
-			  |   | |   |
-			  |   v |   v
-			 a | b   | c
-
-		 If we are at `b', then fixup_alt_jump right now points to a
-		 three-byte space after `a'.  We'll put in the jump, set
-		 fixup_alt_jump to right after `b', and leave behind three
-		 bytes which we'll fill in when we get to after `c'.  */
-
-	      if (fixup_alt_jump)
-		STORE_JUMP (jump_past_alt, fixup_alt_jump, b);
-
-	      /* Mark and leave space for a jump after this alternative,
-		 to be filled in later either by next alternative or
-		 when know we're at the end of a series of alternatives.  */
-	      fixup_alt_jump = b;
-	      GET_BUFFER_SPACE (3);
-	      b += 3;
-
-	      laststart = 0;
-	      begalt = b;
-	      break;
-
-
-	    case '{':
-	      /* If \{ is a literal.  */
-	      if (!(syntax & RE_INTERVALS)
-		     /* If we're at `\{' and it's not the open-interval
-			operator.  */
-		  || ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
-		  || (p - 2 == pattern  &&  p == pend))
-		goto normal_backslash;
-
-	    handle_interval:
-	      {
-		/* If got here, then the syntax allows intervals.  */
-
-		/* At least (most) this many matches must be made.  */
-		int lower_bound = -1, upper_bound = -1;
-
-		beg_interval = p - 1;
-
-		if (p == pend)
-		  {
-		    if (syntax & RE_NO_BK_BRACES)
-		      goto unfetch_interval;
-		    else
-		      return REG_EBRACE;
-		  }
-
-		GET_UNSIGNED_NUMBER (lower_bound);
-
-		if (c == ',')
-		  {
-		    GET_UNSIGNED_NUMBER (upper_bound);
-		    if (upper_bound < 0) upper_bound = RE_DUP_MAX;
-		  }
-		else
-		  /* Interval such as `{1}' => match exactly once. */
-		  upper_bound = lower_bound;
-
-		if (lower_bound < 0 || upper_bound > RE_DUP_MAX
-		    || lower_bound > upper_bound)
-		  {
-		    if (syntax & RE_NO_BK_BRACES)
-		      goto unfetch_interval;
-		    else
-		      return REG_BADBR;
-		  }
-
-		if (!(syntax & RE_NO_BK_BRACES))
-		  {
-		    if (c != '\\') return REG_EBRACE;
-
-		    PATFETCH (c);
-		  }
-
-		if (c != '}')
-		  {
-		    if (syntax & RE_NO_BK_BRACES)
-		      goto unfetch_interval;
-		    else
-		      return REG_BADBR;
-		  }
-
-		/* We just parsed a valid interval.  */
-
-		/* If it's invalid to have no preceding re.  */
-		if (!laststart)
-		  {
-		    if (syntax & RE_CONTEXT_INVALID_OPS)
-		      return REG_BADRPT;
-		    else if (syntax & RE_CONTEXT_INDEP_OPS)
-		      laststart = b;
-		    else
-		      goto unfetch_interval;
-		  }
-
-		/* If the upper bound is zero, don't want to succeed at
-		   all; jump from `laststart' to `b + 3', which will be
-		   the end of the buffer after we insert the jump.  */
-		 if (upper_bound == 0)
-		   {
-		     GET_BUFFER_SPACE (3);
-		     INSERT_JUMP (jump, laststart, b + 3);
-		     b += 3;
-		   }
-
-		 /* Otherwise, we have a nontrivial interval.  When
-		    we're all done, the pattern will look like:
-		      set_number_at <jump count> <upper bound>
-		      set_number_at <succeed_n count> <lower bound>
-		      succeed_n <after jump addr> <succeed_n count>
-		      <body of loop>
-		      jump_n <succeed_n addr> <jump count>
-		    (The upper bound and `jump_n' are omitted if
-		    `upper_bound' is 1, though.)  */
-		 else
-		   { /* If the upper bound is > 1, we need to insert
-			more at the end of the loop.  */
-		     unsigned nbytes = 10 + (upper_bound > 1) * 10;
-
-		     GET_BUFFER_SPACE (nbytes);
-
-		     /* Initialize lower bound of the `succeed_n', even
-			though it will be set during matching by its
-			attendant `set_number_at' (inserted next),
-			because `re_compile_fastmap' needs to know.
-			Jump to the `jump_n' we might insert below.  */
-		     INSERT_JUMP2 (succeed_n, laststart,
-				   b + 5 + (upper_bound > 1) * 5,
-				   lower_bound);
-		     b += 5;
-
-		     /* Code to initialize the lower bound.  Insert
-			before the `succeed_n'.  The `5' is the last two
-			bytes of this `set_number_at', plus 3 bytes of
-			the following `succeed_n'.  */
-		     insert_op2 (set_number_at, laststart, 5, lower_bound, b);
-		     b += 5;
-
-		     if (upper_bound > 1)
-		       { /* More than one repetition is allowed, so
-			    append a backward jump to the `succeed_n'
-			    that starts this interval.
-
-			    When we've reached this during matching,
-			    we'll have matched the interval once, so
-			    jump back only `upper_bound - 1' times.  */
-			 STORE_JUMP2 (jump_n, b, laststart + 5,
-				      upper_bound - 1);
-			 b += 5;
-
-			 /* The location we want to set is the second
-			    parameter of the `jump_n'; that is `b-2' as
-			    an absolute address.  `laststart' will be
-			    the `set_number_at' we're about to insert;
-			    `laststart+3' the number to set, the source
-			    for the relative address.  But we are
-			    inserting into the middle of the pattern --
-			    so everything is getting moved up by 5.
-			    Conclusion: (b - 2) - (laststart + 3) + 5,
-			    i.e., b - laststart.
-
-			    We insert this at the beginning of the loop
-			    so that if we fail during matching, we'll
-			    reinitialize the bounds.  */
-			 insert_op2 (set_number_at, laststart, b - laststart,
-				     upper_bound - 1, b);
-			 b += 5;
-		       }
-		   }
-		pending_exact = 0;
-		beg_interval = NULL;
-	      }
-	      break;
-
-	    unfetch_interval:
-	      /* If an invalid interval, match the characters as literals.  */
-	       assert (beg_interval);
-	       p = beg_interval;
-	       beg_interval = NULL;
-
-	       /* normal_char and normal_backslash need `c'.  */
-	       PATFETCH (c);
-
-	       if (!(syntax & RE_NO_BK_BRACES))
-		 {
-		   if (p > pattern  &&  p[-1] == '\\')
-		     goto normal_backslash;
-		 }
-	       goto normal_char;
-
-#ifdef emacs
-	    /* There is no way to specify the before_dot and after_dot
-	       operators.  rms says this is ok.  --karl  */
-	    case '=':
-	      BUF_PUSH (at_dot);
-	      break;
-
-	    case 's':
-	      laststart = b;
-	      PATFETCH (c);
-	      BUF_PUSH_2 (syntaxspec, syntax_spec_code[c]);
-	      break;
-
-	    case 'S':
-	      laststart = b;
-	      PATFETCH (c);
-	      BUF_PUSH_2 (notsyntaxspec, syntax_spec_code[c]);
-	      break;
-#endif /* emacs */
-
-
-	    case 'w':
-	      laststart = b;
-	      BUF_PUSH (wordchar);
-	      break;
-
-
-	    case 'W':
-	      laststart = b;
-	      BUF_PUSH (notwordchar);
-	      break;
-
-
-	    case '<':
-	      BUF_PUSH (wordbeg);
-	      break;
-
-	    case '>':
-	      BUF_PUSH (wordend);
-	      break;
-
-	    case 'b':
-	      BUF_PUSH (wordbound);
-	      break;
-
-	    case 'B':
-	      BUF_PUSH (notwordbound);
-	      break;
-
-	    case '`':
-	      BUF_PUSH (begbuf);
-	      break;
-
-	    case '\'':
-	      BUF_PUSH (endbuf);
-	      break;
-
-	    case '1': case '2': case '3': case '4': case '5':
-	    case '6': case '7': case '8': case '9':
-	      if (syntax & RE_NO_BK_REFS)
-		goto normal_char;
-
-	      c1 = c - '0';
-
-	      if (c1 > regnum)
-		return REG_ESUBREG;
-
-	      /* Can't back reference to a subexpression if inside of it.  */
-	      if (group_in_compile_stack (compile_stack, c1))
-		goto normal_char;
-
-	      laststart = b;
-	      BUF_PUSH_2 (duplicate, c1);
-	      break;
-
-
-	    case '+':
-	    case '?':
-	      if (syntax & RE_BK_PLUS_QM)
-		goto handle_plus;
-	      else
-		goto normal_backslash;
-
-	    default:
-	    normal_backslash:
-	      /* You might think it would be useful for \ to mean
-		 not to translate; but if we don't translate it
-		 it will never match anything.  */
-	      c = TRANSLATE (c);
-	      goto normal_char;
-	    }
-	  break;
-
-
-	default:
-	/* Expects the character in `c'.  */
-	normal_char:
-	      /* If no exactn currently being built.  */
-	  if (!pending_exact
-
-	      /* If last exactn not at current position.  */
-	      || pending_exact + *pending_exact + 1 != b
-
-	      /* We have only one byte following the exactn for the count.  */
-	      || *pending_exact == (1 << BYTEWIDTH) - 1
-
-	      /* If followed by a repetition operator.  */
-	      || *p == '*' || *p == '^'
-	      || ((syntax & RE_BK_PLUS_QM)
-		  ? *p == '\\' && (p[1] == '+' || p[1] == '?')
-		  : (*p == '+' || *p == '?'))
-	      || ((syntax & RE_INTERVALS)
-		  && ((syntax & RE_NO_BK_BRACES)
-		      ? *p == '{'
-		      : (p[0] == '\\' && p[1] == '{'))))
-	    {
-	      /* Start building a new exactn.  */
-
-	      laststart = b;
-
-	      BUF_PUSH_2 (exactn, 0);
-	      pending_exact = b - 1;
-	    }
-
-	  BUF_PUSH (c);
-	  (*pending_exact)++;
-	  break;
-	} /* switch (c) */
-    } /* while p != pend */
-
-
-  /* Through the pattern now.  */
-
-  if (fixup_alt_jump)
-    STORE_JUMP (jump_past_alt, fixup_alt_jump, b);
-
-  if (!COMPILE_STACK_EMPTY)
-    return REG_EPAREN;
-
-  free (compile_stack.stack);
-
-  /* We have succeeded; set the length of the buffer.  */
-  bufp->used = b - bufp->buffer;
-
-#ifdef DEBUG
-  if (debug)
-    {
-      DEBUG_PRINT1 ("\nCompiled pattern: ");
-      print_compiled_pattern (bufp);
-    }
-#endif /* DEBUG */
-
-  return REG_NOERROR;
-} /* regex_compile */
-
-/* Subroutines for `regex_compile'.  */
-
-/* Store OP at LOC followed by two-byte integer parameter ARG.  */
-
-static void
-store_op1 (op, loc, arg)
-    re_opcode_t op;
-    unsigned char *loc;
-    int arg;
-{
-  *loc = (unsigned char) op;
-  STORE_NUMBER (loc + 1, arg);
-}
-
-
-/* Like `store_op1', but for two two-byte parameters ARG1 and ARG2.  */
-
-static void
-store_op2 (op, loc, arg1, arg2)
-    re_opcode_t op;
-    unsigned char *loc;
-    int arg1, arg2;
-{
-  *loc = (unsigned char) op;
-  STORE_NUMBER (loc + 1, arg1);
-  STORE_NUMBER (loc + 3, arg2);
-}
-
-
-/* Copy the bytes from LOC to END to open up three bytes of space at LOC
-   for OP followed by two-byte integer parameter ARG.  */
-
-static void
-insert_op1 (op, loc, arg, end)
-    re_opcode_t op;
-    unsigned char *loc;
-    int arg;
-    unsigned char *end;
-{
-  register unsigned char *pfrom = end;
-  register unsigned char *pto = end + 3;
-
-  while (pfrom != loc)
-    *--pto = *--pfrom;
-
-  store_op1 (op, loc, arg);
-}
-
-
-/* Like `insert_op1', but for two two-byte parameters ARG1 and ARG2.  */
-
-static void
-insert_op2 (op, loc, arg1, arg2, end)
-    re_opcode_t op;
-    unsigned char *loc;
-    int arg1, arg2;
-    unsigned char *end;
-{
-  register unsigned char *pfrom = end;
-  register unsigned char *pto = end + 5;
-
-  while (pfrom != loc)
-    *--pto = *--pfrom;
-
-  store_op2 (op, loc, arg1, arg2);
-}
-
-
-/* P points to just after a ^ in PATTERN.  Return true if that ^ comes
-   after an alternative or a begin-subexpression.  We assume there is at
-   least one character before the ^.  */
-
-static boolean
-at_begline_loc_p (pattern, p, syntax)
-    const char *pattern, *p;
-    reg_syntax_t syntax;
-{
-  const char *prev = p - 2;
-  boolean prev_prev_backslash = prev > pattern && prev[-1] == '\\';
-
-  return
-       /* After a subexpression?  */
-       (*prev == '(' && (syntax & RE_NO_BK_PARENS || prev_prev_backslash))
-       /* After an alternative?  */
-    || (*prev == '|' && (syntax & RE_NO_BK_VBAR || prev_prev_backslash));
-}
-
-
-/* The dual of at_begline_loc_p.  This one is for $.  We assume there is
-   at least one character after the $, i.e., `P < PEND'.  */
-
-static boolean
-at_endline_loc_p (p, pend, syntax)
-    const char *p, *pend;
-    int syntax;
-{
-  const char *next = p;
-  boolean next_backslash = *next == '\\';
-  const char *next_next = p + 1 < pend ? p + 1 : NULL;
-
-  return
-       /* Before a subexpression?  */
-       (syntax & RE_NO_BK_PARENS ? *next == ')'
-	: next_backslash && next_next && *next_next == ')')
-       /* Before an alternative?  */
-    || (syntax & RE_NO_BK_VBAR ? *next == '|'
-	: next_backslash && next_next && *next_next == '|');
-}
-
-
-/* Returns true if REGNUM is in one of COMPILE_STACK's elements and
-   false if it's not.  */
-
-static boolean
-group_in_compile_stack (compile_stack, regnum)
-    compile_stack_type compile_stack;
-    regnum_t regnum;
-{
-  int this_element;
-
-  for (this_element = compile_stack.avail - 1;
-       this_element >= 0;
-       this_element--)
-    if (compile_stack.stack[this_element].regnum == regnum)
-      return true;
-
-  return false;
-}
-
-
-/* Read the ending character of a range (in a bracket expression) from the
-   uncompiled pattern *P_PTR (which ends at PEND).  We assume the
-   starting character is in `P[-2]'.  (`P[-1]' is the character `-'.)
-   Then we set the translation of all bits between the starting and
-   ending characters (inclusive) in the compiled pattern B.
-
-   Return an error code.
-
-   We use these short variable names so we can use the same macros as
-   `regex_compile' itself.  */
-
-static reg_errcode_t
-compile_range (p_ptr, pend, translate, syntax, b)
-    const char **p_ptr, *pend;
-    char *translate;
-    reg_syntax_t syntax;
-    unsigned char *b;
-{
-  unsigned this_char;
-
-  const char *p = *p_ptr;
-  int range_start, range_end;
-
-  if (p == pend)
-    return REG_ERANGE;
-
-  /* Even though the pattern is a signed `char *', we need to fetch
-     with unsigned char *'s; if the high bit of the pattern character
-     is set, the range endpoints will be negative if we fetch using a
-     signed char *.
-
-     We also want to fetch the endpoints without translating them; the
-     appropriate translation is done in the bit-setting loop below.  */
-  range_start = ((unsigned char *) p)[-2];
-  range_end   = ((unsigned char *) p)[0];
-
-  /* Have to increment the pointer into the pattern string, so the
-     caller isn't still at the ending character.  */
-  (*p_ptr)++;
-
-  /* If the start is after the end, the range is empty.  */
-  if (range_start > range_end)
-    return syntax & RE_NO_EMPTY_RANGES ? REG_ERANGE : REG_NOERROR;
-
-  /* Here we see why `this_char' has to be larger than an `unsigned
-     char' -- the range is inclusive, so if `range_end' == 0xff
-     (assuming 8-bit characters), we would otherwise go into an infinite
-     loop, since all characters <= 0xff.  */
-  for (this_char = range_start; this_char <= range_end; this_char++)
-    {
-      SET_LIST_BIT (TRANSLATE (this_char));
-    }
-
-  return REG_NOERROR;
-}
-
-/* Failure stack declarations and macros; both re_compile_fastmap and
-   re_match_2 use a failure stack.  These have to be macros because of
-   REGEX_ALLOCATE.  */
-
-
-/* Number of failure points for which to initially allocate space
-   when matching.  If this number is exceeded, we allocate more
-   space, so it is not a hard limit.  */
-#ifndef INIT_FAILURE_ALLOC
-#define INIT_FAILURE_ALLOC 5
-#endif
-
-/* Roughly the maximum number of failure points on the stack.  Would be
-   exactly that if always used MAX_FAILURE_SPACE each time we failed.
-   This is a variable only so users of regex can assign to it; we never
-   change it ourselves.  */
+/* Binary backward compatibility.  */
+#if _LIBC
+# include <shlib-compat.h>
+# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3)
+link_warning (re_max_failures, "the 're_max_failures' variable is obsolete and will go away.")
 int re_max_failures = 2000;
-
-typedef const unsigned char *fail_stack_elt_t;
-
-typedef struct
-{
-  fail_stack_elt_t *stack;
-  unsigned size;
-  unsigned avail;			/* Offset of next open position.  */
-} fail_stack_type;
-
-#define FAIL_STACK_EMPTY()     (fail_stack.avail == 0)
-#define FAIL_STACK_PTR_EMPTY() (fail_stack_ptr->avail == 0)
-#define FAIL_STACK_FULL()      (fail_stack.avail == fail_stack.size)
-#define FAIL_STACK_TOP()       (fail_stack.stack[fail_stack.avail])
-
-
-/* Initialize `fail_stack'.  Do `return -2' if the alloc fails.  */
-
-#define INIT_FAIL_STACK()						\
-  do {									\
-    fail_stack.stack = (fail_stack_elt_t *)				\
-      REGEX_ALLOCATE (INIT_FAILURE_ALLOC * sizeof (fail_stack_elt_t));	\
-									\
-    if (fail_stack.stack == NULL)					\
-      return -2;							\
-									\
-    fail_stack.size = INIT_FAILURE_ALLOC;				\
-    fail_stack.avail = 0;						\
-  } while (0)
-
-
-/* Double the size of FAIL_STACK, up to approximately `re_max_failures' items.
-
-   Return 1 if succeeds, and 0 if either ran out of memory
-   allocating space for it or it was already too large.
-
-   REGEX_REALLOCATE requires `destination' be declared.   */
-
-#define DOUBLE_FAIL_STACK(fail_stack)					\
-  ((fail_stack).size > re_max_failures * MAX_FAILURE_ITEMS		\
-   ? 0									\
-   : ((fail_stack).stack = (fail_stack_elt_t *)				\
-	REGEX_REALLOCATE ((fail_stack).stack, 				\
-	  (fail_stack).size * sizeof (fail_stack_elt_t),		\
-	  ((fail_stack).size << 1) * sizeof (fail_stack_elt_t)),	\
-									\
-      (fail_stack).stack == NULL					\
-      ? 0								\
-      : ((fail_stack).size <<= 1, 					\
-	 1)))
-
-
-/* Push PATTERN_OP on FAIL_STACK.
-
-   Return 1 if was able to do so and 0 if ran out of memory allocating
-   space to do so.  */
-#define PUSH_PATTERN_OP(pattern_op, fail_stack)				\
-  ((FAIL_STACK_FULL ()							\
-    && !DOUBLE_FAIL_STACK (fail_stack))					\
-    ? 0									\
-    : ((fail_stack).stack[(fail_stack).avail++] = pattern_op,		\
-       1))
-
-/* This pushes an item onto the failure stack.  Must be a four-byte
-   value.  Assumes the variable `fail_stack'.  Probably should only
-   be called from within `PUSH_FAILURE_POINT'.  */
-#define PUSH_FAILURE_ITEM(item)						\
-  fail_stack.stack[fail_stack.avail++] = (fail_stack_elt_t) item
-
-/* The complement operation.  Assumes `fail_stack' is nonempty.  */
-#define POP_FAILURE_ITEM() fail_stack.stack[--fail_stack.avail]
-
-/* Used to omit pushing failure point id's when we're not debugging.  */
-#ifdef DEBUG
-#define DEBUG_PUSH PUSH_FAILURE_ITEM
-#define DEBUG_POP(item_addr) *(item_addr) = POP_FAILURE_ITEM ()
-#else
-#define DEBUG_PUSH(item)
-#define DEBUG_POP(item_addr)
+# endif
 #endif
-
-
-/* Push the information about the state we will need
-   if we ever fail back to it.
-
-   Requires variables fail_stack, regstart, regend, reg_info, and
-   num_regs be declared.  DOUBLE_FAIL_STACK requires `destination' be
-   declared.
-
-   Does `return FAILURE_CODE' if runs out of memory.  */
-
-#define PUSH_FAILURE_POINT(pattern_place, string_place, failure_code)	\
-  do {									\
-    char *destination;							\
-    /* Must be int, so when we don't save any registers, the arithmetic	\
-       of 0 + -1 isn't done as unsigned.  */				\
-    int this_reg;							\
-									\
-    DEBUG_STATEMENT (failure_id++);					\
-    DEBUG_STATEMENT (nfailure_points_pushed++);				\
-    DEBUG_PRINT2 ("\nPUSH_FAILURE_POINT #%u:\n", failure_id);		\
-    DEBUG_PRINT2 ("  Before push, next avail: %d\n", (fail_stack).avail);\
-    DEBUG_PRINT2 ("                     size: %d\n", (fail_stack).size);\
-									\
-    DEBUG_PRINT2 ("  slots needed: %d\n", NUM_FAILURE_ITEMS);		\
-    DEBUG_PRINT2 ("     available: %d\n", REMAINING_AVAIL_SLOTS);	\
-									\
-    /* Ensure we have enough space allocated for what we will push.  */	\
-    while (REMAINING_AVAIL_SLOTS < NUM_FAILURE_ITEMS)			\
-      {									\
-	if (!DOUBLE_FAIL_STACK (fail_stack))			\
-	  return failure_code;						\
-									\
-	DEBUG_PRINT2 ("\n  Doubled stack; size now: %d\n",		\
-		       (fail_stack).size);				\
-	DEBUG_PRINT2 ("  slots available: %d\n", REMAINING_AVAIL_SLOTS);\
-      }									\
-									\
-    /* Push the info, starting with the registers.  */			\
-    DEBUG_PRINT1 ("\n");						\
-									\
-    for (this_reg = lowest_active_reg; this_reg <= highest_active_reg;	\
-	 this_reg++)							\
-      {									\
-	DEBUG_PRINT2 ("  Pushing reg: %d\n", this_reg);			\
-	DEBUG_STATEMENT (num_regs_pushed++);				\
-									\
-	DEBUG_PRINT2 ("    start: 0x%x\n", regstart[this_reg]);		\
-	PUSH_FAILURE_ITEM (regstart[this_reg]);				\
-									\
-	DEBUG_PRINT2 ("    end: 0x%x\n", regend[this_reg]);		\
-	PUSH_FAILURE_ITEM (regend[this_reg]);				\
-									\
-	DEBUG_PRINT2 ("    info: 0x%x\n      ", reg_info[this_reg]);	\
-	DEBUG_PRINT2 (" match_null=%d",					\
-		      REG_MATCH_NULL_STRING_P (reg_info[this_reg]));	\
-	DEBUG_PRINT2 (" active=%d", IS_ACTIVE (reg_info[this_reg]));	\
-	DEBUG_PRINT2 (" matched_something=%d",				\
-		      MATCHED_SOMETHING (reg_info[this_reg]));		\
-	DEBUG_PRINT2 (" ever_matched=%d",				\
-		      EVER_MATCHED_SOMETHING (reg_info[this_reg]));	\
-	DEBUG_PRINT1 ("\n");						\
-	PUSH_FAILURE_ITEM (reg_info[this_reg].word);			\
-      }									\
-									\
-    DEBUG_PRINT2 ("  Pushing  low active reg: %d\n", lowest_active_reg);\
-    PUSH_FAILURE_ITEM (lowest_active_reg);				\
-									\
-    DEBUG_PRINT2 ("  Pushing high active reg: %d\n", highest_active_reg);\
-    PUSH_FAILURE_ITEM (highest_active_reg);				\
-									\
-    DEBUG_PRINT2 ("  Pushing pattern 0x%x: ", pattern_place);		\
-    DEBUG_PRINT_COMPILED_PATTERN (bufp, pattern_place, pend);		\
-    PUSH_FAILURE_ITEM (pattern_place);					\
-									\
-    DEBUG_PRINT2 ("  Pushing string 0x%x: `", string_place);		\
-    DEBUG_PRINT_DOUBLE_STRING (string_place, string1, size1, string2,   \
-				 size2);				\
-    DEBUG_PRINT1 ("'\n");						\
-    PUSH_FAILURE_ITEM (string_place);					\
-									\
-    DEBUG_PRINT2 ("  Pushing failure id: %u\n", failure_id);		\
-    DEBUG_PUSH (failure_id);						\
-  } while (0)
-
-/* This is the number of items that are pushed and popped on the stack
-   for each register.  */
-#define NUM_REG_ITEMS  3
-
-/* Individual items aside from the registers.  */
-#ifdef DEBUG
-#define NUM_NONREG_ITEMS 5 /* Includes failure point id.  */
-#else
-#define NUM_NONREG_ITEMS 4
-#endif
-
-/* We push at most this many items on the stack.  */
-#define MAX_FAILURE_ITEMS ((num_regs - 1) * NUM_REG_ITEMS + NUM_NONREG_ITEMS)
-
-/* We actually push this many items.  */
-#define NUM_FAILURE_ITEMS						\
-  ((highest_active_reg - lowest_active_reg + 1) * NUM_REG_ITEMS 	\
-    + NUM_NONREG_ITEMS)
-
-/* How many items can still be added to the stack without overflowing it.  */
-#define REMAINING_AVAIL_SLOTS ((fail_stack).size - (fail_stack).avail)
-
-
-/* Pops what PUSH_FAIL_STACK pushes.
-
-   We restore into the parameters, all of which should be lvalues:
-     STR -- the saved data position.
-     PAT -- the saved pattern position.
-     LOW_REG, HIGH_REG -- the highest and lowest active registers.
-     REGSTART, REGEND -- arrays of string positions.
-     REG_INFO -- array of information about each subexpression.
-
-   Also assumes the variables `fail_stack' and (if debugging), `bufp',
-   `pend', `string1', `size1', `string2', and `size2'.  */
-
-#define POP_FAILURE_POINT(str, pat, low_reg, high_reg, regstart, regend, reg_info)\
-{									\
-  DEBUG_STATEMENT (fail_stack_elt_t failure_id;)			\
-  int this_reg;								\
-  const unsigned char *string_temp;					\
-									\
-  assert (!FAIL_STACK_EMPTY ());					\
-									\
-  /* Remove failure points and point to how many regs pushed.  */	\
-  DEBUG_PRINT1 ("POP_FAILURE_POINT:\n");				\
-  DEBUG_PRINT2 ("  Before pop, next avail: %d\n", fail_stack.avail);	\
-  DEBUG_PRINT2 ("                    size: %d\n", fail_stack.size);	\
-									\
-  assert (fail_stack.avail >= NUM_NONREG_ITEMS);			\
-									\
-  DEBUG_POP (&failure_id);						\
-  DEBUG_PRINT2 ("  Popping failure id: %u\n", failure_id);		\
-									\
-  /* If the saved string location is NULL, it came from an		\
-     on_failure_keep_string_jump opcode, and we want to throw away the	\
-     saved NULL, thus retaining our current position in the string.  */	\
-  string_temp = POP_FAILURE_ITEM ();					\
-  if (string_temp != NULL)						\
-    str = (const char *) string_temp;					\
-									\
-  DEBUG_PRINT2 ("  Popping string 0x%x: `", str);			\
-  DEBUG_PRINT_DOUBLE_STRING (str, string1, size1, string2, size2);	\
-  DEBUG_PRINT1 ("'\n");							\
-									\
-  pat = (unsigned char *) POP_FAILURE_ITEM ();				\
-  DEBUG_PRINT2 ("  Popping pattern 0x%x: ", pat);			\
-  DEBUG_PRINT_COMPILED_PATTERN (bufp, pat, pend);			\
-									\
-  /* Restore register info.  */						\
-  high_reg = (unsigned) POP_FAILURE_ITEM ();				\
-  DEBUG_PRINT2 ("  Popping high active reg: %d\n", high_reg);		\
-									\
-  low_reg = (unsigned) POP_FAILURE_ITEM ();				\
-  DEBUG_PRINT2 ("  Popping  low active reg: %d\n", low_reg);		\
-									\
-  for (this_reg = high_reg; this_reg >= low_reg; this_reg--)		\
-    {									\
-      DEBUG_PRINT2 ("    Popping reg: %d\n", this_reg);			\
-									\
-      reg_info[this_reg].word = POP_FAILURE_ITEM ();			\
-      DEBUG_PRINT2 ("      info: 0x%x\n", reg_info[this_reg]);		\
-									\
-      regend[this_reg] = (const char *) POP_FAILURE_ITEM ();		\
-      DEBUG_PRINT2 ("      end: 0x%x\n", regend[this_reg]);		\
-									\
-      regstart[this_reg] = (const char *) POP_FAILURE_ITEM ();		\
-      DEBUG_PRINT2 ("      start: 0x%x\n", regstart[this_reg]);		\
-    }									\
-									\
-  DEBUG_STATEMENT (nfailure_points_popped++);				\
-} /* POP_FAILURE_POINT */
-
-/* re_compile_fastmap computes a ``fastmap'' for the compiled pattern in
-   BUFP.  A fastmap records which of the (1 << BYTEWIDTH) possible
-   characters can start a string that matches the pattern.  This fastmap
-   is used by re_search to skip quickly over impossible starting points.
-
-   The caller must supply the address of a (1 << BYTEWIDTH)-byte data
-   area as BUFP->fastmap.
-
-   We set the `fastmap', `fastmap_accurate', and `can_be_null' fields in
-   the pattern buffer.
-
-   Returns 0 if we succeed, -2 if an internal error.   */
-
-int
-re_compile_fastmap (bufp)
-     struct re_pattern_buffer *bufp;
-{
-  int j, k;
-  fail_stack_type fail_stack;
-#ifndef REGEX_MALLOC
-  char *destination;
-#endif
-  /* We don't push any register information onto the failure stack.  */
-  unsigned num_regs = 0;
-
-  register char *fastmap = bufp->fastmap;
-  unsigned char *pattern = bufp->buffer;
-  unsigned long size = bufp->used;
-  const unsigned char *p = pattern;
-  register unsigned char *pend = pattern + size;
-
-  /* Assume that each path through the pattern can be null until
-     proven otherwise.  We set this false at the bottom of switch
-     statement, to which we get only if a particular path doesn't
-     match the empty string.  */
-  boolean path_can_be_null = true;
-
-  /* We aren't doing a `succeed_n' to begin with.  */
-  boolean succeed_n_p = false;
-
-  assert (fastmap != NULL && p != NULL);
-
-  INIT_FAIL_STACK ();
-  bzero (fastmap, 1 << BYTEWIDTH);  /* Assume nothing's valid.  */
-  bufp->fastmap_accurate = 1;	    /* It will be when we're done.  */
-  bufp->can_be_null = 0;
-
-  while (p != pend || !FAIL_STACK_EMPTY ())
-    {
-      if (p == pend)
-	{
-	  bufp->can_be_null |= path_can_be_null;
-
-	  /* Reset for next path.  */
-	  path_can_be_null = true;
-
-	  p = fail_stack.stack[--fail_stack.avail];
-	}
-
-      /* We should never be about to go beyond the end of the pattern.  */
-      assert (p < pend);
-
-#ifdef SWITCH_ENUM_BUG
-      switch ((int) ((re_opcode_t) *p++))
-#else
-      switch ((re_opcode_t) *p++)
-#endif
-	{
-
-	/* I guess the idea here is to simply not bother with a fastmap
-	   if a backreference is used, since it's too hard to figure out
-	   the fastmap for the corresponding group.  Setting
-	   `can_be_null' stops `re_search_2' from using the fastmap, so
-	   that is all we do.  */
-	case duplicate:
-	  bufp->can_be_null = 1;
-	  return 0;
-
-
-      /* Following are the cases which match a character.  These end
-	 with `break'.  */
-
-	case exactn:
-	  fastmap[p[1]] = 1;
-	  break;
-
-
-	case charset:
-	  for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
-	    if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))
-	      fastmap[j] = 1;
-	  break;
-
-
-	case charset_not:
-	  /* Chars beyond end of map must be allowed.  */
-	  for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++)
-	    fastmap[j] = 1;
-
-	  for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
-	    if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))))
-	      fastmap[j] = 1;
-	  break;
-
-
-	case wordchar:
-	  for (j = 0; j < (1 << BYTEWIDTH); j++)
-	    if (SYNTAX (j) == Sword)
-	      fastmap[j] = 1;
-	  break;
-
-
-	case notwordchar:
-	  for (j = 0; j < (1 << BYTEWIDTH); j++)
-	    if (SYNTAX (j) != Sword)
-	      fastmap[j] = 1;
-	  break;
-
-
-	case anychar:
-	  /* `.' matches anything ...  */
-	  for (j = 0; j < (1 << BYTEWIDTH); j++)
-	    fastmap[j] = 1;
-
-	  /* ... except perhaps newline.  */
-	  if (!(bufp->syntax & RE_DOT_NEWLINE))
-	    fastmap['\n'] = 0;
-
-	  /* Return if we have already set `can_be_null'; if we have,
-	     then the fastmap is irrelevant.  Something's wrong here.  */
-	  else if (bufp->can_be_null)
-	    return 0;
-
-	  /* Otherwise, have to check alternative paths.  */
-	  break;
-
-
-#ifdef emacs
-	case syntaxspec:
-	  k = *p++;
-	  for (j = 0; j < (1 << BYTEWIDTH); j++)
-	    if (SYNTAX (j) == (enum syntaxcode) k)
-	      fastmap[j] = 1;
-	  break;
-
-
-	case notsyntaxspec:
-	  k = *p++;
-	  for (j = 0; j < (1 << BYTEWIDTH); j++)
-	    if (SYNTAX (j) != (enum syntaxcode) k)
-	      fastmap[j] = 1;
-	  break;
-
-
-      /* All cases after this match the empty string.  These end with
-	 `continue'.  */
-
-
-	case before_dot:
-	case at_dot:
-	case after_dot:
-	  continue;
-#endif /* not emacs */
-
-
-	case no_op:
-	case begline:
-	case endline:
-	case begbuf:
-	case endbuf:
-	case wordbound:
-	case notwordbound:
-	case wordbeg:
-	case wordend:
-	case push_dummy_failure:
-	  continue;
-
-
-	case jump_n:
-	case pop_failure_jump:
-	case maybe_pop_jump:
-	case jump:
-	case jump_past_alt:
-	case dummy_failure_jump:
-	  EXTRACT_NUMBER_AND_INCR (j, p);
-	  p += j;
-	  if (j > 0)
-	    continue;
-
-	  /* Jump backward implies we just went through the body of a
-	     loop and matched nothing.  Opcode jumped to should be
-	     `on_failure_jump' or `succeed_n'.  Just treat it like an
-	     ordinary jump.  For a * loop, it has pushed its failure
-	     point already; if so, discard that as redundant.  */
-	  if ((re_opcode_t) *p != on_failure_jump
-	      && (re_opcode_t) *p != succeed_n)
-	    continue;
-
-	  p++;
-	  EXTRACT_NUMBER_AND_INCR (j, p);
-	  p += j;
-
-	  /* If what's on the stack is where we are now, pop it.  */
-	  if (!FAIL_STACK_EMPTY ()
-	      && fail_stack.stack[fail_stack.avail - 1] == p)
-	    fail_stack.avail--;
-
-	  continue;
-
-
-	case on_failure_jump:
-	case on_failure_keep_string_jump:
-	handle_on_failure_jump:
-	  EXTRACT_NUMBER_AND_INCR (j, p);
-
-	  /* For some patterns, e.g., `(a?)?', `p+j' here points to the
-	     end of the pattern.  We don't want to push such a point,
-	     since when we restore it above, entering the switch will
-	     increment `p' past the end of the pattern.  We don't need
-	     to push such a point since we obviously won't find any more
-	     fastmap entries beyond `pend'.  Such a pattern can match
-	     the null string, though.  */
-	  if (p + j < pend)
-	    {
-	      if (!PUSH_PATTERN_OP (p + j, fail_stack))
-		return -2;
-	    }
-	  else
-	    bufp->can_be_null = 1;
-
-	  if (succeed_n_p)
-	    {
-	      EXTRACT_NUMBER_AND_INCR (k, p);	/* Skip the n.  */
-	      succeed_n_p = false;
-	    }
-
-	  continue;
-
-
-	case succeed_n:
-	  /* Get to the number of times to succeed.  */
-	  p += 2;
-
-	  /* Increment p past the n for when k != 0.  */
-	  EXTRACT_NUMBER_AND_INCR (k, p);
-	  if (k == 0)
-	    {
-	      p -= 4;
-	      succeed_n_p = true;  /* Spaghetti code alert.  */
-	      goto handle_on_failure_jump;
-	    }
-	  continue;
-
-
-	case set_number_at:
-	  p += 4;
-	  continue;
-
-
-	case start_memory:
-	case stop_memory:
-	  p += 2;
-	  continue;
-
-
-	default:
-	  abort (); /* We have listed all the cases.  */
-	} /* switch *p++ */
-
-      /* Getting here means we have found the possible starting
-	 characters for one path of the pattern -- and that the empty
-	 string does not match.  We need not follow this path further.
-	 Instead, look at the next alternative (remembered on the
-	 stack), or quit if no more.  The test at the top of the loop
-	 does these things.  */
-      path_can_be_null = false;
-      p = pend;
-    } /* while p */
-
-  /* Set `can_be_null' for the last path (also the first path, if the
-     pattern is empty).  */
-  bufp->can_be_null |= path_can_be_null;
-  return 0;
-} /* re_compile_fastmap */
-
-/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
-   ENDS.  Subsequent matches using PATTERN_BUFFER and REGS will use
-   this memory for recording register information.  STARTS and ENDS
-   must be allocated using the malloc library routine, and must each
-   be at least NUM_REGS * sizeof (regoff_t) bytes long.
-
-   If NUM_REGS == 0, then subsequent matches should allocate their own
-   register data.
-
-   Unless this function is called, the first search or match using
-   PATTERN_BUFFER will allocate its own register data, without
-   freeing the old data.  */
-
-void
-re_set_registers (bufp, regs, num_regs, starts, ends)
-    struct re_pattern_buffer *bufp;
-    struct re_registers *regs;
-    unsigned num_regs;
-    regoff_t *starts, *ends;
-{
-  if (num_regs)
-    {
-      bufp->regs_allocated = REGS_REALLOCATE;
-      regs->num_regs = num_regs;
-      regs->start = starts;
-      regs->end = ends;
-    }
-  else
-    {
-      bufp->regs_allocated = REGS_UNALLOCATED;
-      regs->num_regs = 0;
-      regs->start = regs->end = (regoff_t) 0;
-    }
-}
-
-/* Searching routines.  */
-
-/* Like re_search_2, below, but only one string is specified, and
-   doesn't let you say where to stop matching. */
-
-int
-re_search (bufp, string, size, startpos, range, regs)
-     struct re_pattern_buffer *bufp;
-     const char *string;
-     int size, startpos, range;
-     struct re_registers *regs;
-{
-  return re_search_2 (bufp, NULL, 0, string, size, startpos, range,
-		      regs, size);
-}
-
-
-/* Using the compiled pattern in BUFP->buffer, first tries to match the
-   virtual concatenation of STRING1 and STRING2, starting first at index
-   STARTPOS, then at STARTPOS + 1, and so on.
-
-   STRING1 and STRING2 have length SIZE1 and SIZE2, respectively.
-
-   RANGE is how far to scan while trying to match.  RANGE = 0 means try
-   only at STARTPOS; in general, the last start tried is STARTPOS +
-   RANGE.
-
-   In REGS, return the indices of the virtual concatenation of STRING1
-   and STRING2 that matched the entire BUFP->buffer and its contained
-   subexpressions.
-
-   Do not consider matching one past the index STOP in the virtual
-   concatenation of STRING1 and STRING2.
-
-   We return either the position in the strings at which the match was
-   found, -1 if no match, or -2 if error (such as failure
-   stack overflow).  */
-
-int
-re_search_2 (bufp, string1, size1, string2, size2, startpos, range, regs, stop)
-     struct re_pattern_buffer *bufp;
-     const char *string1, *string2;
-     int size1, size2;
-     int startpos;
-     int range;
-     struct re_registers *regs;
-     int stop;
-{
-  int val;
-  register char *fastmap = bufp->fastmap;
-  register char *translate = bufp->translate;
-  int total_size = size1 + size2;
-  int endpos = startpos + range;
-
-  /* Check for out-of-range STARTPOS.  */
-  if (startpos < 0 || startpos > total_size)
-    return -1;
-
-  /* Fix up RANGE if it might eventually take us outside
-     the virtual concatenation of STRING1 and STRING2.  */
-  if (endpos < -1)
-    range = -1 - startpos;
-  else if (endpos > total_size)
-    range = total_size - startpos;
-
-  /* If the search isn't to be a backwards one, don't waste time in a
-     search for a pattern that must be anchored.  */
-  if (bufp->used > 0 && (re_opcode_t) bufp->buffer[0] == begbuf && range > 0)
-    {
-      if (startpos > 0)
-	return -1;
-      else
-	range = 1;
-    }
-
-  /* Update the fastmap now if not correct already.  */
-  if (fastmap && !bufp->fastmap_accurate)
-    if (re_compile_fastmap (bufp) == -2)
-      return -2;
-
-  /* Loop through the string, looking for a place to start matching.  */
-  for (;;)
-    {
-      /* If a fastmap is supplied, skip quickly over characters that
-	 cannot be the start of a match.  If the pattern can match the
-	 null string, however, we don't need to skip characters; we want
-	 the first null string.  */
-      if (fastmap && startpos < total_size && !bufp->can_be_null)
-	{
-	  if (range > 0)	/* Searching forwards.  */
-	    {
-	      register const char *d;
-	      register int lim = 0;
-	      int irange = range;
-
-	      if (startpos < size1 && startpos + range >= size1)
-		lim = range - (size1 - startpos);
-
-	      d = (startpos >= size1 ? string2 - size1 : string1) + startpos;
-
-	      /* Written out as an if-else to avoid testing `translate'
-		 inside the loop.  */
-	      if (translate)
-		while (range > lim
-		       && !fastmap[(unsigned char)
-				   translate[(unsigned char) *d++]])
-		  range--;
-	      else
-		while (range > lim && !fastmap[(unsigned char) *d++])
-		  range--;
-
-	      startpos += irange - range;
-	    }
-	  else				/* Searching backwards.  */
-	    {
-	      register char c = (size1 == 0 || startpos >= size1
-				 ? string2[startpos - size1]
-				 : string1[startpos]);
-
-	      if (!fastmap[(unsigned char) TRANSLATE (c)])
-		goto advance;
-	    }
-	}
-
-      /* If can't match the null string, and that's all we have left, fail.  */
-      if (range >= 0 && startpos == total_size && fastmap
-	  && !bufp->can_be_null)
-	return -1;
-
-      val = re_match_2 (bufp, string1, size1, string2, size2,
-			startpos, regs, stop);
-      if (val >= 0)
-	return startpos;
-
-      if (val == -2)
-	return -2;
-
-    advance:
-      if (!range)
-	break;
-      else if (range > 0)
-	{
-	  range--;
-	  startpos++;
-	}
-      else
-	{
-	  range++;
-	  startpos--;
-	}
-    }
-  return -1;
-} /* re_search_2 */
-
-/* Declarations and macros for re_match_2.  */
-
-static int bcmp_translate ();
-static boolean alt_match_null_string_p (),
-	       common_op_match_null_string_p (),
-	       group_match_null_string_p ();
-
-/* Structure for per-register (a.k.a. per-group) information.
-   This must not be longer than one word, because we push this value
-   onto the failure stack.  Other register information, such as the
-   starting and ending positions (which are addresses), and the list of
-   inner groups (which is a bits list) are maintained in separate
-   variables.
-
-   We are making a (strictly speaking) nonportable assumption here: that
-   the compiler will pack our bit fields into something that fits into
-   the type of `word', i.e., is something that fits into one item on the
-   failure stack.  */
-typedef union
-{
-  fail_stack_elt_t word;
-  struct
-  {
-      /* This field is one if this group can match the empty string,
-	 zero if not.  If not yet determined,  `MATCH_NULL_UNSET_VALUE'.  */
-#define MATCH_NULL_UNSET_VALUE 3
-    unsigned match_null_string_p : 2;
-    unsigned is_active : 1;
-    unsigned matched_something : 1;
-    unsigned ever_matched_something : 1;
-  } bits;
-} register_info_type;
-
-#define REG_MATCH_NULL_STRING_P(R)  ((R).bits.match_null_string_p)
-#define IS_ACTIVE(R)  ((R).bits.is_active)
-#define MATCHED_SOMETHING(R)  ((R).bits.matched_something)
-#define EVER_MATCHED_SOMETHING(R)  ((R).bits.ever_matched_something)
-
-
-/* Call this when have matched a real character; it sets `matched' flags
-   for the subexpressions which we are currently inside.  Also records
-   that those subexprs have matched.  */
-#define SET_REGS_MATCHED()						\
-  do									\
-    {									\
-      unsigned r;							\
-      for (r = lowest_active_reg; r <= highest_active_reg; r++)		\
-	{								\
-	  MATCHED_SOMETHING (reg_info[r])				\
-	    = EVER_MATCHED_SOMETHING (reg_info[r])			\
-	    = 1;							\
-	}								\
-    }									\
-  while (0)
-
-
-/* This converts PTR, a pointer into one of the search strings `string1'
-   and `string2' into an offset from the beginning of that string.  */
-#define POINTER_TO_OFFSET(ptr)						\
-  (FIRST_STRING_P (ptr) ? (ptr) - string1 : (ptr) - string2 + size1)
-
-/* Registers are set to a sentinel when they haven't yet matched.  */
-#define REG_UNSET_VALUE ((char *) -1)
-#define REG_UNSET(e) ((e) == REG_UNSET_VALUE)
-
-
-/* Macros for dealing with the split strings in re_match_2.  */
-
-#define MATCHING_IN_FIRST_STRING  (dend == end_match_1)
-
-/* Call before fetching a character with *d.  This switches over to
-   string2 if necessary.  */
-#define PREFETCH()							\
-  while (d == dend)						    	\
-    {									\
-      /* End of string2 => fail.  */					\
-      if (dend == end_match_2) 						\
-	goto fail;							\
-      /* End of string1 => advance to string2.  */ 			\
-      d = string2;						        \
-      dend = end_match_2;						\
-    }
-
-
-/* Test if at very beginning or at very end of the virtual concatenation
-   of `string1' and `string2'.  If only one string, it's `string2'.  */
-#define AT_STRINGS_BEG(d) ((d) == (size1 ? string1 : string2) || !size2)
-#define AT_STRINGS_END(d) ((d) == end2)
-
-
-/* Test if D points to a character which is word-constituent.  We have
-   two special cases to check for: if past the end of string1, look at
-   the first character in string2; and if before the beginning of
-   string2, look at the last character in string1.  */
-#define WORDCHAR_P(d)							\
-  (SYNTAX ((d) == end1 ? *string2					\
-	   : (d) == string2 - 1 ? *(end1 - 1) : *(d))			\
-   == Sword)
-
-/* Test if the character before D and the one at D differ with respect
-   to being word-constituent.  */
-#define AT_WORD_BOUNDARY(d)						\
-  (AT_STRINGS_BEG (d) || AT_STRINGS_END (d)				\
-   || WORDCHAR_P (d - 1) != WORDCHAR_P (d))
-
-
-/* Free everything we malloc.  */
-#ifdef REGEX_MALLOC
-#define FREE_VAR(var) if (var) free (var); var = NULL
-#define FREE_VARIABLES()						\
-  do {									\
-    FREE_VAR (fail_stack.stack);					\
-    FREE_VAR (regstart);						\
-    FREE_VAR (regend);							\
-    FREE_VAR (old_regstart);						\
-    FREE_VAR (old_regend);						\
-    FREE_VAR (best_regstart);						\
-    FREE_VAR (best_regend);						\
-    FREE_VAR (reg_info);						\
-    FREE_VAR (reg_dummy);						\
-    FREE_VAR (reg_info_dummy);						\
-  } while (0)
-#else /* not REGEX_MALLOC */
-/* Some MIPS systems (at least) want this to free alloca'd storage.  */
-#define FREE_VARIABLES() alloca (0)
-#endif /* not REGEX_MALLOC */
-
-
-/* These values must meet several constraints.  They must not be valid
-   register values; since we have a limit of 255 registers (because
-   we use only one byte in the pattern for the register number), we can
-   use numbers larger than 255.  They must differ by 1, because of
-   NUM_FAILURE_ITEMS above.  And the value for the lowest register must
-   be larger than the value for the highest register, so we do not try
-   to actually save any registers when none are active.  */
-#define NO_HIGHEST_ACTIVE_REG (1 << BYTEWIDTH)
-#define NO_LOWEST_ACTIVE_REG (NO_HIGHEST_ACTIVE_REG + 1)
-
-/* Matching routines.  */
-
-#ifndef emacs   /* Emacs never uses this.  */
-/* re_match is like re_match_2 except it takes only a single string.  */
-
-int
-re_match (bufp, string, size, pos, regs)
-     struct re_pattern_buffer *bufp;
-     const char *string;
-     int size, pos;
-     struct re_registers *regs;
- {
-  return re_match_2 (bufp, NULL, 0, string, size, pos, regs, size);
-}
-#endif /* not emacs */
-
-
-/* re_match_2 matches the compiled pattern in BUFP against the
-   the (virtual) concatenation of STRING1 and STRING2 (of length SIZE1
-   and SIZE2, respectively).  We start matching at POS, and stop
-   matching at STOP.
-
-   If REGS is non-null and the `no_sub' field of BUFP is nonzero, we
-   store offsets for the substring each group matched in REGS.  See the
-   documentation for exactly how many groups we fill.
-
-   We return -1 if no match, -2 if an internal error (such as the
-   failure stack overflowing).  Otherwise, we return the length of the
-   matched substring.  */
-
-int
-re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
-     struct re_pattern_buffer *bufp;
-     const char *string1, *string2;
-     int size1, size2;
-     int pos;
-     struct re_registers *regs;
-     int stop;
-{
-  /* General temporaries.  */
-  int mcnt;
-  unsigned char *p1;
-
-  /* Just past the end of the corresponding string.  */
-  const char *end1, *end2;
-
-  /* Pointers into string1 and string2, just past the last characters in
-     each to consider matching.  */
-  const char *end_match_1, *end_match_2;
-
-  /* Where we are in the data, and the end of the current string.  */
-  const char *d, *dend;
-
-  /* Where we are in the pattern, and the end of the pattern.  */
-  unsigned char *p = bufp->buffer;
-  register unsigned char *pend = p + bufp->used;
-
-  /* We use this to map every character in the string.  */
-  char *translate = bufp->translate;
-
-  /* Failure point stack.  Each place that can handle a failure further
-     down the line pushes a failure point on this stack.  It consists of
-     restart, regend, and reg_info for all registers corresponding to
-     the subexpressions we're currently inside, plus the number of such
-     registers, and, finally, two char *'s.  The first char * is where
-     to resume scanning the pattern; the second one is where to resume
-     scanning the strings.  If the latter is zero, the failure point is
-     a ``dummy''; if a failure happens and the failure point is a dummy,
-     it gets discarded and the next next one is tried.  */
-  fail_stack_type fail_stack;
-#ifdef DEBUG
-  static unsigned failure_id = 0;
-  unsigned nfailure_points_pushed = 0, nfailure_points_popped = 0;
-#endif
-
-  /* We fill all the registers internally, independent of what we
-     return, for use in backreferences.  The number here includes
-     an element for register zero.  */
-  unsigned num_regs = bufp->re_nsub + 1;
-
-  /* The currently active registers.  */
-  unsigned lowest_active_reg = NO_LOWEST_ACTIVE_REG;
-  unsigned highest_active_reg = NO_HIGHEST_ACTIVE_REG;
-
-  /* Information on the contents of registers. These are pointers into
-     the input strings; they record just what was matched (on this
-     attempt) by a subexpression part of the pattern, that is, the
-     regnum-th regstart pointer points to where in the pattern we began
-     matching and the regnum-th regend points to right after where we
-     stopped matching the regnum-th subexpression.  (The zeroth register
-     keeps track of what the whole pattern matches.)  */
-  const char **regstart = NULL, **regend = NULL;
-
-  /* If a group that's operated upon by a repetition operator fails to
-     match anything, then the register for its start will need to be
-     restored because it will have been set to wherever in the string we
-     are when we last see its open-group operator.  Similarly for a
-     register's end.  */
-  const char **old_regstart = NULL, **old_regend = NULL;
-
-  /* The is_active field of reg_info helps us keep track of which (possibly
-     nested) subexpressions we are currently in. The matched_something
-     field of reg_info[reg_num] helps us tell whether or not we have
-     matched any of the pattern so far this time through the reg_num-th
-     subexpression.  These two fields get reset each time through any
-     loop their register is in.  */
-  register_info_type *reg_info = NULL;
-
-  /* The following record the register info as found in the above
-     variables when we find a match better than any we've seen before.
-     This happens as we backtrack through the failure points, which in
-     turn happens only if we have not yet matched the entire string. */
-  unsigned best_regs_set = false;
-  const char **best_regstart = NULL, **best_regend = NULL;
-
-  /* Logically, this is `best_regend[0]'.  But we don't want to have to
-     allocate space for that if we're not allocating space for anything
-     else (see below).  Also, we never need info about register 0 for
-     any of the other register vectors, and it seems rather a kludge to
-     treat `best_regend' differently than the rest.  So we keep track of
-     the end of the best match so far in a separate variable.  We
-     initialize this to NULL so that when we backtrack the first time
-     and need to test it, it's not garbage.  */
-  const char *match_end = NULL;
-
-  /* Used when we pop values we don't care about.  */
-  const char **reg_dummy = NULL;
-  register_info_type *reg_info_dummy = NULL;
-
-#ifdef DEBUG
-  /* Counts the total number of registers pushed.  */
-  unsigned num_regs_pushed = 0;
-#endif
-
-  DEBUG_PRINT1 ("\n\nEntering re_match_2.\n");
-
-  INIT_FAIL_STACK ();
-
-  /* Do not bother to initialize all the register variables if there are
-     no groups in the pattern, as it takes a fair amount of time.  If
-     there are groups, we include space for register 0 (the whole
-     pattern), even though we never use it, since it simplifies the
-     array indexing.  We should fix this.  */
-  if (bufp->re_nsub)
-    {
-      regstart = REGEX_TALLOC (num_regs, const char *);
-      regend = REGEX_TALLOC (num_regs, const char *);
-      old_regstart = REGEX_TALLOC (num_regs, const char *);
-      old_regend = REGEX_TALLOC (num_regs, const char *);
-      best_regstart = REGEX_TALLOC (num_regs, const char *);
-      best_regend = REGEX_TALLOC (num_regs, const char *);
-      reg_info = REGEX_TALLOC (num_regs, register_info_type);
-      reg_dummy = REGEX_TALLOC (num_regs, const char *);
-      reg_info_dummy = REGEX_TALLOC (num_regs, register_info_type);
-
-      if (!(regstart && regend && old_regstart && old_regend && reg_info
-	    && best_regstart && best_regend && reg_dummy && reg_info_dummy))
-	{
-	  FREE_VARIABLES ();
-	  return -2;
-	}
-    }
-#ifdef REGEX_MALLOC
-  else
-    {
-      /* We must initialize all our variables to NULL, so that
-	 `FREE_VARIABLES' doesn't try to free them.  */
-      regstart = regend = old_regstart = old_regend = best_regstart
-	= best_regend = reg_dummy = NULL;
-      reg_info = reg_info_dummy = (register_info_type *) NULL;
-    }
-#endif /* REGEX_MALLOC */
-
-  /* The starting position is bogus.  */
-  if (pos < 0 || pos > size1 + size2)
-    {
-      FREE_VARIABLES ();
-      return -1;
-    }
-
-  /* Initialize subexpression text positions to -1 to mark ones that no
-     start_memory/stop_memory has been seen for. Also initialize the
-     register information struct.  */
-  for (mcnt = 1; mcnt < num_regs; mcnt++)
-    {
-      regstart[mcnt] = regend[mcnt]
-	= old_regstart[mcnt] = old_regend[mcnt] = REG_UNSET_VALUE;
-
-      REG_MATCH_NULL_STRING_P (reg_info[mcnt]) = MATCH_NULL_UNSET_VALUE;
-      IS_ACTIVE (reg_info[mcnt]) = 0;
-      MATCHED_SOMETHING (reg_info[mcnt]) = 0;
-      EVER_MATCHED_SOMETHING (reg_info[mcnt]) = 0;
-    }
-
-  /* We move `string1' into `string2' if the latter's empty -- but not if
-     `string1' is null.  */
-  if (size2 == 0 && string1 != NULL)
-    {
-      string2 = string1;
-      size2 = size1;
-      string1 = 0;
-      size1 = 0;
-    }
-  end1 = string1 + size1;
-  end2 = string2 + size2;
-
-  /* Compute where to stop matching, within the two strings.  */
-  if (stop <= size1)
-    {
-      end_match_1 = string1 + stop;
-      end_match_2 = string2;
-    }
-  else
-    {
-      end_match_1 = end1;
-      end_match_2 = string2 + stop - size1;
-    }
-
-  /* `p' scans through the pattern as `d' scans through the data.
-     `dend' is the end of the input string that `d' points within.  `d'
-     is advanced into the following input string whenever necessary, but
-     this happens before fetching; therefore, at the beginning of the
-     loop, `d' can be pointing at the end of a string, but it cannot
-     equal `string2'.  */
-  if (size1 > 0 && pos <= size1)
-    {
-      d = string1 + pos;
-      dend = end_match_1;
-    }
-  else
-    {
-      d = string2 + pos - size1;
-      dend = end_match_2;
-    }
-
-  DEBUG_PRINT1 ("The compiled pattern is: ");
-  DEBUG_PRINT_COMPILED_PATTERN (bufp, p, pend);
-  DEBUG_PRINT1 ("The string to match is: `");
-  DEBUG_PRINT_DOUBLE_STRING (d, string1, size1, string2, size2);
-  DEBUG_PRINT1 ("'\n");
-
-  /* This loops over pattern commands.  It exits by returning from the
-     function if the match is complete, or it drops through if the match
-     fails at this starting point in the input data.  */
-  for (;;)
-    {
-      DEBUG_PRINT2 ("\n0x%x: ", p);
-
-      if (p == pend)
-	{ /* End of pattern means we might have succeeded.  */
-	  DEBUG_PRINT1 ("end of pattern ... ");
-
-	  /* If we haven't matched the entire string, and we want the
-	     longest match, try backtracking.  */
-	  if (d != end_match_2)
-	    {
-	      DEBUG_PRINT1 ("backtracking.\n");
-
-	      if (!FAIL_STACK_EMPTY ())
-		{ /* More failure points to try.  */
-		  boolean same_str_p = (FIRST_STRING_P (match_end)
-					== MATCHING_IN_FIRST_STRING);
-
-		  /* If exceeds best match so far, save it.  */
-		  if (!best_regs_set
-		      || (same_str_p && d > match_end)
-		      || (!same_str_p && !MATCHING_IN_FIRST_STRING))
-		    {
-		      best_regs_set = true;
-		      match_end = d;
-
-		      DEBUG_PRINT1 ("\nSAVING match as best so far.\n");
-
-		      for (mcnt = 1; mcnt < num_regs; mcnt++)
-			{
-			  best_regstart[mcnt] = regstart[mcnt];
-			  best_regend[mcnt] = regend[mcnt];
-			}
-		    }
-		  goto fail;
-		}
-
-	      /* If no failure points, don't restore garbage.  */
-	      else if (best_regs_set)
-		{
-		restore_best_regs:
-		  /* Restore best match.  It may happen that `dend ==
-		     end_match_1' while the restored d is in string2.
-		     For example, the pattern `x.*y.*z' against the
-		     strings `x-' and `y-z-', if the two strings are
-		     not consecutive in memory.  */
-		  DEBUG_PRINT1 ("Restoring best registers.\n");
-
-		  d = match_end;
-		  dend = ((d >= string1 && d <= end1)
-			   ? end_match_1 : end_match_2);
-
-		  for (mcnt = 1; mcnt < num_regs; mcnt++)
-		    {
-		      regstart[mcnt] = best_regstart[mcnt];
-		      regend[mcnt] = best_regend[mcnt];
-		    }
-		}
-	    } /* d != end_match_2 */
-
-	  DEBUG_PRINT1 ("Accepting match.\n");
-
-	  /* If caller wants register contents data back, do it.  */
-	  if (regs && !bufp->no_sub)
-	    {
-	      /* Have the register data arrays been allocated?  */
-	      if (bufp->regs_allocated == REGS_UNALLOCATED)
-		{ /* No.  So allocate them with malloc.  We need one
-		     extra element beyond `num_regs' for the `-1' marker
-		     GNU code uses.  */
-		  regs->num_regs = MAX (RE_NREGS, num_regs + 1);
-		  regs->start = TALLOC (regs->num_regs, regoff_t);
-		  regs->end = TALLOC (regs->num_regs, regoff_t);
-		  if (regs->start == NULL || regs->end == NULL)
-		    return -2;
-		  bufp->regs_allocated = REGS_REALLOCATE;
-		}
-	      else if (bufp->regs_allocated == REGS_REALLOCATE)
-		{ /* Yes.  If we need more elements than were already
-		     allocated, reallocate them.  If we need fewer, just
-		     leave it alone.  */
-		  if (regs->num_regs < num_regs + 1)
-		    {
-		      regs->num_regs = num_regs + 1;
-		      RETALLOC (regs->start, regs->num_regs, regoff_t);
-		      RETALLOC (regs->end, regs->num_regs, regoff_t);
-		      if (regs->start == NULL || regs->end == NULL)
-			return -2;
-		    }
-		}
-	      else
-		assert (bufp->regs_allocated == REGS_FIXED);
-
-	      /* Convert the pointer data in `regstart' and `regend' to
-		 indices.  Register zero has to be set differently,
-		 since we haven't kept track of any info for it.  */
-	      if (regs->num_regs > 0)
-		{
-		  regs->start[0] = pos;
-		  regs->end[0] = (MATCHING_IN_FIRST_STRING ? d - string1
-				  : d - string2 + size1);
-		}
-
-	      /* Go through the first `min (num_regs, regs->num_regs)'
-		 registers, since that is all we initialized.  */
-	      for (mcnt = 1; mcnt < MIN (num_regs, regs->num_regs); mcnt++)
-		{
-		  if (REG_UNSET (regstart[mcnt]) || REG_UNSET (regend[mcnt]))
-		    regs->start[mcnt] = regs->end[mcnt] = -1;
-		  else
-		    {
-		      regs->start[mcnt] = POINTER_TO_OFFSET (regstart[mcnt]);
-		      regs->end[mcnt] = POINTER_TO_OFFSET (regend[mcnt]);
-		    }
-		}
-
-	      /* If the regs structure we return has more elements than
-		 were in the pattern, set the extra elements to -1.  If
-		 we (re)allocated the registers, this is the case,
-		 because we always allocate enough to have at least one
-		 -1 at the end.  */
-	      for (mcnt = num_regs; mcnt < regs->num_regs; mcnt++)
-		regs->start[mcnt] = regs->end[mcnt] = -1;
-	    } /* regs && !bufp->no_sub */
-
-	  FREE_VARIABLES ();
-	  DEBUG_PRINT4 ("%u failure points pushed, %u popped (%u remain).\n",
-			nfailure_points_pushed, nfailure_points_popped,
-			nfailure_points_pushed - nfailure_points_popped);
-	  DEBUG_PRINT2 ("%u registers pushed.\n", num_regs_pushed);
-
-	  mcnt = d - pos - (MATCHING_IN_FIRST_STRING
-			    ? string1
-			    : string2 - size1);
-
-	  DEBUG_PRINT2 ("Returning %d from re_match_2.\n", mcnt);
-
-	  return mcnt;
-	}
-
-      /* Otherwise match next pattern command.  */
-#ifdef SWITCH_ENUM_BUG
-      switch ((int) ((re_opcode_t) *p++))
-#else
-      switch ((re_opcode_t) *p++)
-#endif
-	{
-	/* Ignore these.  Used to ignore the n of succeed_n's which
-	   currently have n == 0.  */
-	case no_op:
-	  DEBUG_PRINT1 ("EXECUTING no_op.\n");
-	  break;
-
-
-	/* Match the next n pattern characters exactly.  The following
-	   byte in the pattern defines n, and the n bytes after that
-	   are the characters to match.  */
-	case exactn:
-	  mcnt = *p++;
-	  DEBUG_PRINT2 ("EXECUTING exactn %d.\n", mcnt);
-
-	  /* This is written out as an if-else so we don't waste time
-	     testing `translate' inside the loop.  */
-	  if (translate)
-	    {
-	      do
-		{
-		  PREFETCH ();
-		  if (translate[(unsigned char) *d++] != (char) *p++)
-		    goto fail;
-		}
-	      while (--mcnt);
-	    }
-	  else
-	    {
-	      do
-		{
-		  PREFETCH ();
-		  if (*d++ != (char) *p++) goto fail;
-		}
-	      while (--mcnt);
-	    }
-	  SET_REGS_MATCHED ();
-	  break;
-
-
-	/* Match any character except possibly a newline or a null.  */
-	case anychar:
-	  DEBUG_PRINT1 ("EXECUTING anychar.\n");
-
-	  PREFETCH ();
-
-	  if ((!(bufp->syntax & RE_DOT_NEWLINE) && TRANSLATE (*d) == '\n')
-	      || (bufp->syntax & RE_DOT_NOT_NULL && TRANSLATE (*d) == '\000'))
-	    goto fail;
-
-	  SET_REGS_MATCHED ();
-	  DEBUG_PRINT2 ("  Matched `%d'.\n", *d);
-	  d++;
-	  break;
-
-
-	case charset:
-	case charset_not:
-	  {
-	    register unsigned char c;
-	    boolean not = (re_opcode_t) *(p - 1) == charset_not;
-
-	    DEBUG_PRINT2 ("EXECUTING charset%s.\n", not ? "_not" : "");
-
-	    PREFETCH ();
-	    c = TRANSLATE (*d); /* The character to match.  */
-
-	    /* Cast to `unsigned' instead of `unsigned char' in case the
-	       bit list is a full 32 bytes long.  */
-	    if (c < (unsigned) (*p * BYTEWIDTH)
-		&& p[1 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
-	      not = !not;
-
-	    p += 1 + *p;
-
-	    if (!not) goto fail;
-
-	    SET_REGS_MATCHED ();
-	    d++;
-	    break;
-	  }
-
-
-	/* The beginning of a group is represented by start_memory.
-	   The arguments are the register number in the next byte, and the
-	   number of groups inner to this one in the next.  The text
-	   matched within the group is recorded (in the internal
-	   registers data structure) under the register number.  */
-	case start_memory:
-	  DEBUG_PRINT3 ("EXECUTING start_memory %d (%d):\n", *p, p[1]);
-
-	  /* Find out if this group can match the empty string.  */
-	  p1 = p;		/* To send to group_match_null_string_p.  */
-
-	  if (REG_MATCH_NULL_STRING_P (reg_info[*p]) == MATCH_NULL_UNSET_VALUE)
-	    REG_MATCH_NULL_STRING_P (reg_info[*p])
-	      = group_match_null_string_p (&p1, pend, reg_info);
-
-	  /* Save the position in the string where we were the last time
-	     we were at this open-group operator in case the group is
-	     operated upon by a repetition operator, e.g., with `(a*)*b'
-	     against `ab'; then we want to ignore where we are now in
-	     the string in case this attempt to match fails.  */
-	  old_regstart[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p])
-			     ? REG_UNSET (regstart[*p]) ? d : regstart[*p]
-			     : regstart[*p];
-	  DEBUG_PRINT2 ("  old_regstart: %d\n",
-			 POINTER_TO_OFFSET (old_regstart[*p]));
-
-	  regstart[*p] = d;
-	  DEBUG_PRINT2 ("  regstart: %d\n", POINTER_TO_OFFSET (regstart[*p]));
-
-	  IS_ACTIVE (reg_info[*p]) = 1;
-	  MATCHED_SOMETHING (reg_info[*p]) = 0;
-
-	  /* This is the new highest active register.  */
-	  highest_active_reg = *p;
-
-	  /* If nothing was active before, this is the new lowest active
-	     register.  */
-	  if (lowest_active_reg == NO_LOWEST_ACTIVE_REG)
-	    lowest_active_reg = *p;
-
-	  /* Move past the register number and inner group count.  */
-	  p += 2;
-	  break;
-
-
-	/* The stop_memory opcode represents the end of a group.  Its
-	   arguments are the same as start_memory's: the register
-	   number, and the number of inner groups.  */
-	case stop_memory:
-	  DEBUG_PRINT3 ("EXECUTING stop_memory %d (%d):\n", *p, p[1]);
-
-	  /* We need to save the string position the last time we were at
-	     this close-group operator in case the group is operated
-	     upon by a repetition operator, e.g., with `((a*)*(b*)*)*'
-	     against `aba'; then we want to ignore where we are now in
-	     the string in case this attempt to match fails.  */
-	  old_regend[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p])
-			   ? REG_UNSET (regend[*p]) ? d : regend[*p]
-			   : regend[*p];
-	  DEBUG_PRINT2 ("      old_regend: %d\n",
-			 POINTER_TO_OFFSET (old_regend[*p]));
-
-	  regend[*p] = d;
-	  DEBUG_PRINT2 ("      regend: %d\n", POINTER_TO_OFFSET (regend[*p]));
-
-	  /* This register isn't active anymore.  */
-	  IS_ACTIVE (reg_info[*p]) = 0;
-
-	  /* If this was the only register active, nothing is active
-	     anymore.  */
-	  if (lowest_active_reg == highest_active_reg)
-	    {
-	      lowest_active_reg = NO_LOWEST_ACTIVE_REG;
-	      highest_active_reg = NO_HIGHEST_ACTIVE_REG;
-	    }
-	  else
-	    { /* We must scan for the new highest active register, since
-		 it isn't necessarily one less than now: consider
-		 (a(b)c(d(e)f)g).  When group 3 ends, after the f), the
-		 new highest active register is 1.  */
-	      unsigned char r = *p - 1;
-	      while (r > 0 && !IS_ACTIVE (reg_info[r]))
-		r--;
-
-	      /* If we end up at register zero, that means that we saved
-		 the registers as the result of an `on_failure_jump', not
-		 a `start_memory', and we jumped to past the innermost
-		 `stop_memory'.  For example, in ((.)*) we save
-		 registers 1 and 2 as a result of the *, but when we pop
-		 back to the second ), we are at the stop_memory 1.
-		 Thus, nothing is active.  */
-	      if (r == 0)
-		{
-		  lowest_active_reg = NO_LOWEST_ACTIVE_REG;
-		  highest_active_reg = NO_HIGHEST_ACTIVE_REG;
-		}
-	      else
-		highest_active_reg = r;
-	    }
-
-	  /* If just failed to match something this time around with a
-	     group that's operated on by a repetition operator, try to
-	     force exit from the ``loop'', and restore the register
-	     information for this group that we had before trying this
-	     last match.  */
-	  if ((!MATCHED_SOMETHING (reg_info[*p])
-	       || (re_opcode_t) p[-3] == start_memory)
-	      && (p + 2) < pend)
-	    {
-	      boolean is_a_jump_n = false;
-
-	      p1 = p + 2;
-	      mcnt = 0;
-	      switch ((re_opcode_t) *p1++)
-		{
-		  case jump_n:
-		    is_a_jump_n = true;
-		  case pop_failure_jump:
-		  case maybe_pop_jump:
-		  case jump:
-		  case dummy_failure_jump:
-		    EXTRACT_NUMBER_AND_INCR (mcnt, p1);
-		    if (is_a_jump_n)
-		      p1 += 2;
-		    break;
-
-		  default:
-		    /* do nothing */ ;
-		}
-	      p1 += mcnt;
-
-	      /* If the next operation is a jump backwards in the pattern
-		 to an on_failure_jump right before the start_memory
-		 corresponding to this stop_memory, exit from the loop
-		 by forcing a failure after pushing on the stack the
-		 on_failure_jump's jump in the pattern, and d.  */
-	      if (mcnt < 0 && (re_opcode_t) *p1 == on_failure_jump
-		  && (re_opcode_t) p1[3] == start_memory && p1[4] == *p)
-		{
-		  /* If this group ever matched anything, then restore
-		     what its registers were before trying this last
-		     failed match, e.g., with `(a*)*b' against `ab' for
-		     regstart[1], and, e.g., with `((a*)*(b*)*)*'
-		     against `aba' for regend[3].
-
-		     Also restore the registers for inner groups for,
-		     e.g., `((a*)(b*))*' against `aba' (register 3 would
-		     otherwise get trashed).  */
-
-		  if (EVER_MATCHED_SOMETHING (reg_info[*p]))
-		    {
-		      unsigned r;
-
-		      EVER_MATCHED_SOMETHING (reg_info[*p]) = 0;
-
-		      /* Restore this and inner groups' (if any) registers.  */
-		      for (r = *p; r < *p + *(p + 1); r++)
-			{
-			  regstart[r] = old_regstart[r];
-
-			  /* xx why this test?  */
-			  if ((int) old_regend[r] >= (int) regstart[r])
-			    regend[r] = old_regend[r];
-			}
-		    }
-		  p1++;
-		  EXTRACT_NUMBER_AND_INCR (mcnt, p1);
-		  PUSH_FAILURE_POINT (p1 + mcnt, d, -2);
-
-		  goto fail;
-		}
-	    }
-
-	  /* Move past the register number and the inner group count.  */
-	  p += 2;
-	  break;
-
-
-	/* \<digit> has been turned into a `duplicate' command which is
-	   followed by the numeric value of <digit> as the register number.  */
-	case duplicate:
-	  {
-	    register const char *d2, *dend2;
-	    int regno = *p++;   /* Get which register to match against.  */
-	    DEBUG_PRINT2 ("EXECUTING duplicate %d.\n", regno);
-
-	    /* Can't back reference a group which we've never matched.  */
-	    if (REG_UNSET (regstart[regno]) || REG_UNSET (regend[regno]))
-	      goto fail;
-
-	    /* Where in input to try to start matching.  */
-	    d2 = regstart[regno];
-
-	    /* Where to stop matching; if both the place to start and
-	       the place to stop matching are in the same string, then
-	       set to the place to stop, otherwise, for now have to use
-	       the end of the first string.  */
-
-	    dend2 = ((FIRST_STRING_P (regstart[regno])
-		      == FIRST_STRING_P (regend[regno]))
-		     ? regend[regno] : end_match_1);
-	    for (;;)
-	      {
-		/* If necessary, advance to next segment in register
-		   contents.  */
-		while (d2 == dend2)
-		  {
-		    if (dend2 == end_match_2) break;
-		    if (dend2 == regend[regno]) break;
-
-		    /* End of string1 => advance to string2. */
-		    d2 = string2;
-		    dend2 = regend[regno];
-		  }
-		/* At end of register contents => success */
-		if (d2 == dend2) break;
-
-		/* If necessary, advance to next segment in data.  */
-		PREFETCH ();
-
-		/* How many characters left in this segment to match.  */
-		mcnt = dend - d;
-
-		/* Want how many consecutive characters we can match in
-		   one shot, so, if necessary, adjust the count.  */
-		if (mcnt > dend2 - d2)
-		  mcnt = dend2 - d2;
-
-		/* Compare that many; failure if mismatch, else move
-		   past them.  */
-		if (translate
-		    ? bcmp_translate (d, d2, mcnt, translate)
-		    : bcmp (d, d2, mcnt))
-		  goto fail;
-		d += mcnt, d2 += mcnt;
-	      }
-	  }
-	  break;
-
-
-	/* begline matches the empty string at the beginning of the string
-	   (unless `not_bol' is set in `bufp'), and, if
-	   `newline_anchor' is set, after newlines.  */
-	case begline:
-	  DEBUG_PRINT1 ("EXECUTING begline.\n");
-
-	  if (AT_STRINGS_BEG (d))
-	    {
-	      if (!bufp->not_bol) break;
-	    }
-	  else if (d[-1] == '\n' && bufp->newline_anchor)
-	    {
-	      break;
-	    }
-	  /* In all other cases, we fail.  */
-	  goto fail;
-
-
-	/* endline is the dual of begline.  */
-	case endline:
-	  DEBUG_PRINT1 ("EXECUTING endline.\n");
-
-	  if (AT_STRINGS_END (d))
-	    {
-	      if (!bufp->not_eol) break;
-	    }
-
-	  /* We have to ``prefetch'' the next character.  */
-	  else if ((d == end1 ? *string2 : *d) == '\n'
-		   && bufp->newline_anchor)
-	    {
-	      break;
-	    }
-	  goto fail;
-
-
-	/* Match at the very beginning of the data.  */
-	case begbuf:
-	  DEBUG_PRINT1 ("EXECUTING begbuf.\n");
-	  if (AT_STRINGS_BEG (d))
-	    break;
-	  goto fail;
-
-
-	/* Match at the very end of the data.  */
-	case endbuf:
-	  DEBUG_PRINT1 ("EXECUTING endbuf.\n");
-	  if (AT_STRINGS_END (d))
-	    break;
-	  goto fail;
-
-
-	/* on_failure_keep_string_jump is used to optimize `.*\n'.  It
-	   pushes NULL as the value for the string on the stack.  Then
-	   `pop_failure_point' will keep the current value for the
-	   string, instead of restoring it.  To see why, consider
-	   matching `foo\nbar' against `.*\n'.  The .* matches the foo;
-	   then the . fails against the \n.  But the next thing we want
-	   to do is match the \n against the \n; if we restored the
-	   string value, we would be back at the foo.
-
-	   Because this is used only in specific cases, we don't need to
-	   check all the things that `on_failure_jump' does, to make
-	   sure the right things get saved on the stack.  Hence we don't
-	   share its code.  The only reason to push anything on the
-	   stack at all is that otherwise we would have to change
-	   `anychar's code to do something besides goto fail in this
-	   case; that seems worse than this.  */
-	case on_failure_keep_string_jump:
-	  DEBUG_PRINT1 ("EXECUTING on_failure_keep_string_jump");
-
-	  EXTRACT_NUMBER_AND_INCR (mcnt, p);
-	  DEBUG_PRINT3 (" %d (to 0x%x):\n", mcnt, p + mcnt);
-
-	  PUSH_FAILURE_POINT (p + mcnt, NULL, -2);
-	  break;
-
-
-	/* Uses of on_failure_jump:
-
-	   Each alternative starts with an on_failure_jump that points
-	   to the beginning of the next alternative.  Each alternative
-	   except the last ends with a jump that in effect jumps past
-	   the rest of the alternatives.  (They really jump to the
-	   ending jump of the following alternative, because tensioning
-	   these jumps is a hassle.)
-
-	   Repeats start with an on_failure_jump that points past both
-	   the repetition text and either the following jump or
-	   pop_failure_jump back to this on_failure_jump.  */
-	case on_failure_jump:
-	on_failure:
-	  DEBUG_PRINT1 ("EXECUTING on_failure_jump");
-
-	  EXTRACT_NUMBER_AND_INCR (mcnt, p);
-	  DEBUG_PRINT3 (" %d (to 0x%x)", mcnt, p + mcnt);
-
-	  /* If this on_failure_jump comes right before a group (i.e.,
-	     the original * applied to a group), save the information
-	     for that group and all inner ones, so that if we fail back
-	     to this point, the group's information will be correct.
-	     For example, in \(a*\)*\1, we need the preceding group,
-	     and in \(\(a*\)b*\)\2, we need the inner group.  */
-
-	  /* We can't use `p' to check ahead because we push
-	     a failure point to `p + mcnt' after we do this.  */
-	  p1 = p;
-
-	  /* We need to skip no_op's before we look for the
-	     start_memory in case this on_failure_jump is happening as
-	     the result of a completed succeed_n, as in \(a\)\{1,3\}b\1
-	     against aba.  */
-	  while (p1 < pend && (re_opcode_t) *p1 == no_op)
-	    p1++;
-
-	  if (p1 < pend && (re_opcode_t) *p1 == start_memory)
-	    {
-	      /* We have a new highest active register now.  This will
-		 get reset at the start_memory we are about to get to,
-		 but we will have saved all the registers relevant to
-		 this repetition op, as described above.  */
-	      highest_active_reg = *(p1 + 1) + *(p1 + 2);
-	      if (lowest_active_reg == NO_LOWEST_ACTIVE_REG)
-		lowest_active_reg = *(p1 + 1);
-	    }
-
-	  DEBUG_PRINT1 (":\n");
-	  PUSH_FAILURE_POINT (p + mcnt, d, -2);
-	  break;
-
-
-	/* A smart repeat ends with `maybe_pop_jump'.
-	   We change it to either `pop_failure_jump' or `jump'.  */
-	case maybe_pop_jump:
-	  EXTRACT_NUMBER_AND_INCR (mcnt, p);
-	  DEBUG_PRINT2 ("EXECUTING maybe_pop_jump %d.\n", mcnt);
-	  {
-	    register unsigned char *p2 = p;
-
-	    /* Compare the beginning of the repeat with what in the
-	       pattern follows its end. If we can establish that there
-	       is nothing that they would both match, i.e., that we
-	       would have to backtrack because of (as in, e.g., `a*a')
-	       then we can change to pop_failure_jump, because we'll
-	       never have to backtrack.
-
-	       This is not true in the case of alternatives: in
-	       `(a|ab)*' we do need to backtrack to the `ab' alternative
-	       (e.g., if the string was `ab').  But instead of trying to
-	       detect that here, the alternative has put on a dummy
-	       failure point which is what we will end up popping.  */
-
-	    /* Skip over open/close-group commands.  */
-	    while (p2 + 2 < pend
-		   && ((re_opcode_t) *p2 == stop_memory
-		       || (re_opcode_t) *p2 == start_memory))
-	      p2 += 3;			/* Skip over args, too.  */
-
-	    /* If we're at the end of the pattern, we can change.  */
-	    if (p2 == pend)
-	      {
-		/* Consider what happens when matching ":\(.*\)"
-		   against ":/".  I don't really understand this code
-		   yet.  */
-		p[-3] = (unsigned char) pop_failure_jump;
-		DEBUG_PRINT1
-		  ("  End of pattern: change to `pop_failure_jump'.\n");
-	      }
-
-	    else if ((re_opcode_t) *p2 == exactn
-		     || (bufp->newline_anchor && (re_opcode_t) *p2 == endline))
-	      {
-		register unsigned char c
-		  = *p2 == (unsigned char) endline ? '\n' : p2[2];
-		p1 = p + mcnt;
-
-		/* p1[0] ... p1[2] are the `on_failure_jump' corresponding
-		   to the `maybe_finalize_jump' of this case.  Examine what
-		   follows.  */
-		if ((re_opcode_t) p1[3] == exactn && p1[5] != c)
-		  {
-		    p[-3] = (unsigned char) pop_failure_jump;
-		    DEBUG_PRINT3 ("  %c != %c => pop_failure_jump.\n",
-				  c, p1[5]);
-		  }
-
-		else if ((re_opcode_t) p1[3] == charset
-			 || (re_opcode_t) p1[3] == charset_not)
-		  {
-		    int not = (re_opcode_t) p1[3] == charset_not;
-
-		    if (c < (unsigned char) (p1[4] * BYTEWIDTH)
-			&& p1[5 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
-		      not = !not;
-
-		    /* `not' is equal to 1 if c would match, which means
-			that we can't change to pop_failure_jump.  */
-		    if (!not)
-		      {
-			p[-3] = (unsigned char) pop_failure_jump;
-			DEBUG_PRINT1 ("  No match => pop_failure_jump.\n");
-		      }
-		  }
-	      }
-	  }
-	  p -= 2;		/* Point at relative address again.  */
-	  if ((re_opcode_t) p[-1] != pop_failure_jump)
-	    {
-	      p[-1] = (unsigned char) jump;
-	      DEBUG_PRINT1 ("  Match => jump.\n");
-	      goto unconditional_jump;
-	    }
-	/* Note fall through.  */
-
-
-	/* The end of a simple repeat has a pop_failure_jump back to
-	   its matching on_failure_jump, where the latter will push a
-	   failure point.  The pop_failure_jump takes off failure
-	   points put on by this pop_failure_jump's matching
-	   on_failure_jump; we got through the pattern to here from the
-	   matching on_failure_jump, so didn't fail.  */
-	case pop_failure_jump:
-	  {
-	    /* We need to pass separate storage for the lowest and
-	       highest registers, even though we don't care about the
-	       actual values.  Otherwise, we will restore only one
-	       register from the stack, since lowest will == highest in
-	       `pop_failure_point'.  */
-	    unsigned dummy_low_reg, dummy_high_reg;
-	    unsigned char *pdummy;
-	    const char *sdummy;
-
-	    DEBUG_PRINT1 ("EXECUTING pop_failure_jump.\n");
-	    POP_FAILURE_POINT (sdummy, pdummy,
-			       dummy_low_reg, dummy_high_reg,
-			       reg_dummy, reg_dummy, reg_info_dummy);
-	  }
-	  /* Note fall through.  */
-
-
-	/* Unconditionally jump (without popping any failure points).  */
-	case jump:
-	unconditional_jump:
-	  EXTRACT_NUMBER_AND_INCR (mcnt, p);	/* Get the amount to jump.  */
-	  DEBUG_PRINT2 ("EXECUTING jump %d ", mcnt);
-	  p += mcnt;				/* Do the jump.  */
-	  DEBUG_PRINT2 ("(to 0x%x).\n", p);
-	  break;
-
-
-	/* We need this opcode so we can detect where alternatives end
-	   in `group_match_null_string_p' et al.  */
-	case jump_past_alt:
-	  DEBUG_PRINT1 ("EXECUTING jump_past_alt.\n");
-	  goto unconditional_jump;
-
-
-	/* Normally, the on_failure_jump pushes a failure point, which
-	   then gets popped at pop_failure_jump.  We will end up at
-	   pop_failure_jump, also, and with a pattern of, say, `a+', we
-	   are skipping over the on_failure_jump, so we have to push
-	   something meaningless for pop_failure_jump to pop.  */
-	case dummy_failure_jump:
-	  DEBUG_PRINT1 ("EXECUTING dummy_failure_jump.\n");
-	  /* It doesn't matter what we push for the string here.  What
-	     the code at `fail' tests is the value for the pattern.  */
-	  PUSH_FAILURE_POINT (0, 0, -2);
-	  goto unconditional_jump;
-
-
-	/* At the end of an alternative, we need to push a dummy failure
-	   point in case we are followed by a `pop_failure_jump', because
-	   we don't want the failure point for the alternative to be
-	   popped.  For example, matching `(a|ab)*' against `aab'
-	   requires that we match the `ab' alternative.  */
-	case push_dummy_failure:
-	  DEBUG_PRINT1 ("EXECUTING push_dummy_failure.\n");
-	  /* See comments just above at `dummy_failure_jump' about the
-	     two zeroes.  */
-	  PUSH_FAILURE_POINT (0, 0, -2);
-	  break;
-
-	/* Have to succeed matching what follows at least n times.
-	   After that, handle like `on_failure_jump'.  */
-	case succeed_n:
-	  EXTRACT_NUMBER (mcnt, p + 2);
-	  DEBUG_PRINT2 ("EXECUTING succeed_n %d.\n", mcnt);
-
-	  assert (mcnt >= 0);
-	  /* Originally, this is how many times we HAVE to succeed.  */
-	  if (mcnt > 0)
-	    {
-	       mcnt--;
-	       p += 2;
-	       STORE_NUMBER_AND_INCR (p, mcnt);
-	       DEBUG_PRINT3 ("  Setting 0x%x to %d.\n", p, mcnt);
-	    }
-	  else if (mcnt == 0)
-	    {
-	      DEBUG_PRINT2 ("  Setting two bytes from 0x%x to no_op.\n", p+2);
-	      p[2] = (unsigned char) no_op;
-	      p[3] = (unsigned char) no_op;
-	      goto on_failure;
-	    }
-	  break;
-
-	case jump_n:
-	  EXTRACT_NUMBER (mcnt, p + 2);
-	  DEBUG_PRINT2 ("EXECUTING jump_n %d.\n", mcnt);
-
-	  /* Originally, this is how many times we CAN jump.  */
-	  if (mcnt)
-	    {
-	       mcnt--;
-	       STORE_NUMBER (p + 2, mcnt);
-	       goto unconditional_jump;
-	    }
-	  /* If don't have to jump any more, skip over the rest of command.  */
-	  else
-	    p += 4;
-	  break;
-
-	case set_number_at:
-	  {
-	    DEBUG_PRINT1 ("EXECUTING set_number_at.\n");
-
-	    EXTRACT_NUMBER_AND_INCR (mcnt, p);
-	    p1 = p + mcnt;
-	    EXTRACT_NUMBER_AND_INCR (mcnt, p);
-	    DEBUG_PRINT3 ("  Setting 0x%x to %d.\n", p1, mcnt);
-	    STORE_NUMBER (p1, mcnt);
-	    break;
-	  }
-
-	case wordbound:
-	  DEBUG_PRINT1 ("EXECUTING wordbound.\n");
-	  if (AT_WORD_BOUNDARY (d))
-	    break;
-	  goto fail;
-
-	case notwordbound:
-	  DEBUG_PRINT1 ("EXECUTING notwordbound.\n");
-	  if (AT_WORD_BOUNDARY (d))
-	    goto fail;
-	  break;
-
-	case wordbeg:
-	  DEBUG_PRINT1 ("EXECUTING wordbeg.\n");
-	  if (WORDCHAR_P (d) && (AT_STRINGS_BEG (d) || !WORDCHAR_P (d - 1)))
-	    break;
-	  goto fail;
-
-	case wordend:
-	  DEBUG_PRINT1 ("EXECUTING wordend.\n");
-	  if (!AT_STRINGS_BEG (d) && WORDCHAR_P (d - 1)
-	      && (!WORDCHAR_P (d) || AT_STRINGS_END (d)))
-	    break;
-	  goto fail;
-
-#ifdef emacs
-#ifdef emacs19
-	case before_dot:
-	  DEBUG_PRINT1 ("EXECUTING before_dot.\n");
-	  if (PTR_CHAR_POS ((unsigned char *) d) >= point)
-	    goto fail;
-	  break;
-
-	case at_dot:
-	  DEBUG_PRINT1 ("EXECUTING at_dot.\n");
-	  if (PTR_CHAR_POS ((unsigned char *) d) != point)
-	    goto fail;
-	  break;
-
-	case after_dot:
-	  DEBUG_PRINT1 ("EXECUTING after_dot.\n");
-	  if (PTR_CHAR_POS ((unsigned char *) d) <= point)
-	    goto fail;
-	  break;
-#else /* not emacs19 */
-	case at_dot:
-	  DEBUG_PRINT1 ("EXECUTING at_dot.\n");
-	  if (PTR_CHAR_POS ((unsigned char *) d) + 1 != point)
-	    goto fail;
-	  break;
-#endif /* not emacs19 */
-
-	case syntaxspec:
-	  DEBUG_PRINT2 ("EXECUTING syntaxspec %d.\n", mcnt);
-	  mcnt = *p++;
-	  goto matchsyntax;
-
-	case wordchar:
-	  DEBUG_PRINT1 ("EXECUTING Emacs wordchar.\n");
-	  mcnt = (int) Sword;
-	matchsyntax:
-	  PREFETCH ();
-	  if (SYNTAX (*d++) != (enum syntaxcode) mcnt)
-	    goto fail;
-	  SET_REGS_MATCHED ();
-	  break;
-
-	case notsyntaxspec:
-	  DEBUG_PRINT2 ("EXECUTING notsyntaxspec %d.\n", mcnt);
-	  mcnt = *p++;
-	  goto matchnotsyntax;
-
-	case notwordchar:
-	  DEBUG_PRINT1 ("EXECUTING Emacs notwordchar.\n");
-	  mcnt = (int) Sword;
-	matchnotsyntax:
-	  PREFETCH ();
-	  if (SYNTAX (*d++) == (enum syntaxcode) mcnt)
-	    goto fail;
-	  SET_REGS_MATCHED ();
-	  break;
-
-#else /* not emacs */
-	case wordchar:
-	  DEBUG_PRINT1 ("EXECUTING non-Emacs wordchar.\n");
-	  PREFETCH ();
-	  if (!WORDCHAR_P (d))
-	    goto fail;
-	  SET_REGS_MATCHED ();
-	  d++;
-	  break;
-
-	case notwordchar:
-	  DEBUG_PRINT1 ("EXECUTING non-Emacs notwordchar.\n");
-	  PREFETCH ();
-	  if (WORDCHAR_P (d))
-	    goto fail;
-	  SET_REGS_MATCHED ();
-	  d++;
-	  break;
-#endif /* not emacs */
-
-	default:
-	  abort ();
-	}
-      continue;  /* Successfully executed one pattern command; keep going.  */
-
-
-    /* We goto here if a matching operation fails. */
-    fail:
-      if (!FAIL_STACK_EMPTY ())
-	{ /* A restart point is known.  Restore to that state.  */
-	  DEBUG_PRINT1 ("\nFAIL:\n");
-	  POP_FAILURE_POINT (d, p,
-			     lowest_active_reg, highest_active_reg,
-			     regstart, regend, reg_info);
-
-	  /* If this failure point is a dummy, try the next one.  */
-	  if (!p)
-	    goto fail;
-
-	  /* If we failed to the end of the pattern, don't examine *p.  */
-	  assert (p <= pend);
-	  if (p < pend)
-	    {
-	      boolean is_a_jump_n = false;
-
-	      /* If failed to a backwards jump that's part of a repetition
-		 loop, need to pop this failure point and use the next one.  */
-	      switch ((re_opcode_t) *p)
-		{
-		case jump_n:
-		  is_a_jump_n = true;
-		case maybe_pop_jump:
-		case pop_failure_jump:
-		case jump:
-		  p1 = p + 1;
-		  EXTRACT_NUMBER_AND_INCR (mcnt, p1);
-		  p1 += mcnt;
-
-		  if ((is_a_jump_n && (re_opcode_t) *p1 == succeed_n)
-		      || (!is_a_jump_n
-			  && (re_opcode_t) *p1 == on_failure_jump))
-		    goto fail;
-		  break;
-		default:
-		  /* do nothing */ ;
-		}
-	    }
-
-	  if (d >= string1 && d <= end1)
-	    dend = end_match_1;
-	}
-      else
-	break;   /* Matching at this starting point really fails.  */
-    } /* for (;;) */
-
-  if (best_regs_set)
-    goto restore_best_regs;
-
-  FREE_VARIABLES ();
-
-  return -1;         			/* Failure to match.  */
-} /* re_match_2 */
-
-/* Subroutine definitions for re_match_2.  */
-
-
-/* We are passed P pointing to a register number after a start_memory.
-
-   Return true if the pattern up to the corresponding stop_memory can
-   match the empty string, and false otherwise.
-
-   If we find the matching stop_memory, sets P to point to one past its number.
-   Otherwise, sets P to an undefined byte less than or equal to END.
-
-   We don't handle duplicates properly (yet).  */
-
-static boolean
-group_match_null_string_p (p, end, reg_info)
-    unsigned char **p, *end;
-    register_info_type *reg_info;
-{
-  int mcnt;
-  /* Point to after the args to the start_memory.  */
-  unsigned char *p1 = *p + 2;
-
-  while (p1 < end)
-    {
-      /* Skip over opcodes that can match nothing, and return true or
-	 false, as appropriate, when we get to one that can't, or to the
-	 matching stop_memory.  */
-
-      switch ((re_opcode_t) *p1)
-	{
-	/* Could be either a loop or a series of alternatives.  */
-	case on_failure_jump:
-	  p1++;
-	  EXTRACT_NUMBER_AND_INCR (mcnt, p1);
-
-	  /* If the next operation is not a jump backwards in the
-	     pattern.  */
-
-	  if (mcnt >= 0)
-	    {
-	      /* Go through the on_failure_jumps of the alternatives,
-		 seeing if any of the alternatives cannot match nothing.
-		 The last alternative starts with only a jump,
-		 whereas the rest start with on_failure_jump and end
-		 with a jump, e.g., here is the pattern for `a|b|c':
-
-		 /on_failure_jump/0/6/exactn/1/a/jump_past_alt/0/6
-		 /on_failure_jump/0/6/exactn/1/b/jump_past_alt/0/3
-		 /exactn/1/c
-
-		 So, we have to first go through the first (n-1)
-		 alternatives and then deal with the last one separately.  */
-
-
-	      /* Deal with the first (n-1) alternatives, which start
-		 with an on_failure_jump (see above) that jumps to right
-		 past a jump_past_alt.  */
-
-	      while ((re_opcode_t) p1[mcnt-3] == jump_past_alt)
-		{
-		  /* `mcnt' holds how many bytes long the alternative
-		     is, including the ending `jump_past_alt' and
-		     its number.  */
-
-		  if (!alt_match_null_string_p (p1, p1 + mcnt - 3,
-						      reg_info))
-		    return false;
-
-		  /* Move to right after this alternative, including the
-		     jump_past_alt.  */
-		  p1 += mcnt;
-
-		  /* Break if it's the beginning of an n-th alternative
-		     that doesn't begin with an on_failure_jump.  */
-		  if ((re_opcode_t) *p1 != on_failure_jump)
-		    break;
-
-		  /* Still have to check that it's not an n-th
-		     alternative that starts with an on_failure_jump.  */
-		  p1++;
-		  EXTRACT_NUMBER_AND_INCR (mcnt, p1);
-		  if ((re_opcode_t) p1[mcnt-3] != jump_past_alt)
-		    {
-		      /* Get to the beginning of the n-th alternative.  */
-		      p1 -= 3;
-		      break;
-		    }
-		}
-
-	      /* Deal with the last alternative: go back and get number
-		 of the `jump_past_alt' just before it.  `mcnt' contains
-		 the length of the alternative.  */
-	      EXTRACT_NUMBER (mcnt, p1 - 2);
-
-	      if (!alt_match_null_string_p (p1, p1 + mcnt, reg_info))
-		return false;
-
-	      p1 += mcnt;	/* Get past the n-th alternative.  */
-	    } /* if mcnt > 0 */
-	  break;
-
-
-	case stop_memory:
-	  assert (p1[1] == **p);
-	  *p = p1 + 2;
-	  return true;
-
-
-	default:
-	  if (!common_op_match_null_string_p (&p1, end, reg_info))
-	    return false;
-	}
-    } /* while p1 < end */
-
-  return false;
-} /* group_match_null_string_p */
-
-
-/* Similar to group_match_null_string_p, but doesn't deal with alternatives:
-   It expects P to be the first byte of a single alternative and END one
-   byte past the last. The alternative can contain groups.  */
-
-static boolean
-alt_match_null_string_p (p, end, reg_info)
-    unsigned char *p, *end;
-    register_info_type *reg_info;
-{
-  int mcnt;
-  unsigned char *p1 = p;
-
-  while (p1 < end)
-    {
-      /* Skip over opcodes that can match nothing, and break when we get
-	 to one that can't.  */
-
-      switch ((re_opcode_t) *p1)
-	{
-	/* It's a loop.  */
-	case on_failure_jump:
-	  p1++;
-	  EXTRACT_NUMBER_AND_INCR (mcnt, p1);
-	  p1 += mcnt;
-	  break;
-
-	default:
-	  if (!common_op_match_null_string_p (&p1, end, reg_info))
-	    return false;
-	}
-    }  /* while p1 < end */
-
-  return true;
-} /* alt_match_null_string_p */
-
-
-/* Deals with the ops common to group_match_null_string_p and
-   alt_match_null_string_p.
-
-   Sets P to one after the op and its arguments, if any.  */
-
-static boolean
-common_op_match_null_string_p (p, end, reg_info)
-    unsigned char **p, *end;
-    register_info_type *reg_info;
-{
-  int mcnt;
-  boolean ret;
-  int reg_no;
-  unsigned char *p1 = *p;
-
-  switch ((re_opcode_t) *p1++)
-    {
-    case no_op:
-    case begline:
-    case endline:
-    case begbuf:
-    case endbuf:
-    case wordbeg:
-    case wordend:
-    case wordbound:
-    case notwordbound:
-#ifdef emacs
-    case before_dot:
-    case at_dot:
-    case after_dot:
-#endif
-      break;
-
-    case start_memory:
-      reg_no = *p1;
-      assert (reg_no > 0 && reg_no <= MAX_REGNUM);
-      ret = group_match_null_string_p (&p1, end, reg_info);
-
-      /* Have to set this here in case we're checking a group which
-	 contains a group and a back reference to it.  */
-
-      if (REG_MATCH_NULL_STRING_P (reg_info[reg_no]) == MATCH_NULL_UNSET_VALUE)
-	REG_MATCH_NULL_STRING_P (reg_info[reg_no]) = ret;
-
-      if (!ret)
-	return false;
-      break;
-
-    /* If this is an optimized succeed_n for zero times, make the jump.  */
-    case jump:
-      EXTRACT_NUMBER_AND_INCR (mcnt, p1);
-      if (mcnt >= 0)
-	p1 += mcnt;
-      else
-	return false;
-      break;
-
-    case succeed_n:
-      /* Get to the number of times to succeed.  */
-      p1 += 2;
-      EXTRACT_NUMBER_AND_INCR (mcnt, p1);
-
-      if (mcnt == 0)
-	{
-	  p1 -= 4;
-	  EXTRACT_NUMBER_AND_INCR (mcnt, p1);
-	  p1 += mcnt;
-	}
-      else
-	return false;
-      break;
-
-    case duplicate:
-      if (!REG_MATCH_NULL_STRING_P (reg_info[*p1]))
-	return false;
-      break;
-
-    case set_number_at:
-      p1 += 4;
-
-    default:
-      /* All other opcodes mean we cannot match the empty string.  */
-      return false;
-  }
-
-  *p = p1;
-  return true;
-} /* common_op_match_null_string_p */
-
-
-/* Return zero if TRANSLATE[S1] and TRANSLATE[S2] are identical for LEN
-   bytes; nonzero otherwise.  */
-
-static int
-bcmp_translate(
-     unsigned char *s1,
-     unsigned char *s2,
-     int len,
-     char *translate
-)
-{
-  register unsigned char *p1 = s1, *p2 = s2;
-  while (len)
-    {
-      if (translate[*p1++] != translate[*p2++]) return 1;
-      len--;
-    }
-  return 0;
-}
-
-/* Entry points for GNU code.  */
-
-/* re_compile_pattern is the GNU regular expression compiler: it
-   compiles PATTERN (of length SIZE) and puts the result in BUFP.
-   Returns 0 if the pattern was valid, otherwise an error string.
-
-   Assumes the `allocated' (and perhaps `buffer') and `translate' fields
-   are set in BUFP on entry.
-
-   We call regex_compile to do the actual compilation.  */
-
-const char *
-re_compile_pattern (pattern, length, bufp)
-     const char *pattern;
-     int length;
-     struct re_pattern_buffer *bufp;
-{
-  reg_errcode_t ret;
-
-  /* GNU code is written to assume at least RE_NREGS registers will be set
-     (and at least one extra will be -1).  */
-  bufp->regs_allocated = REGS_UNALLOCATED;
-
-  /* And GNU code determines whether or not to get register information
-     by passing null for the REGS argument to re_match, etc., not by
-     setting no_sub.  */
-  bufp->no_sub = 0;
-
-  /* Match anchors at newline.  */
-  bufp->newline_anchor = 1;
-
-  ret = regex_compile (pattern, length, re_syntax_options, bufp);
-
-  return re_error_msg[(int) ret];
-}
-
-/* Entry points compatible with 4.2 BSD regex library.  We don't define
-   them if this is an Emacs or POSIX compilation.  */
-
-#if !defined (emacs) && !defined (_POSIX_SOURCE)
-
-/* BSD has one and only one pattern buffer.  */
-static struct re_pattern_buffer re_comp_buf;
-
-char *
-re_comp (s)
-    const char *s;
-{
-  reg_errcode_t ret;
-
-  if (!s)
-    {
-      if (!re_comp_buf.buffer)
-	return "No previous regular expression";
-      return 0;
-    }
-
-  if (!re_comp_buf.buffer)
-    {
-      re_comp_buf.buffer = (unsigned char *) malloc (200);
-      if (re_comp_buf.buffer == NULL)
-	return "Memory exhausted";
-      re_comp_buf.allocated = 200;
-
-      re_comp_buf.fastmap = (char *) malloc (1 << BYTEWIDTH);
-      if (re_comp_buf.fastmap == NULL)
-	return "Memory exhausted";
-    }
-
-  /* Since `re_exec' always passes NULL for the `regs' argument, we
-     don't need to initialize the pattern buffer fields which affect it.  */
-
-  /* Match anchors at newlines.  */
-  re_comp_buf.newline_anchor = 1;
-
-  ret = regex_compile (s, strlen (s), re_syntax_options, &re_comp_buf);
-
-  /* Yes, we're discarding `const' here.  */
-  return (char *) re_error_msg[(int) ret];
-}
-
-
-int
-re_exec (s)
-    const char *s;
-{
-  const int len = strlen (s);
-  return
-    0 <= re_search (&re_comp_buf, s, len, 0, len, (struct re_registers *) 0);
-}
-#endif /* not emacs and not _POSIX_SOURCE */
-
-/* POSIX.2 functions.  Don't define these for Emacs.  */
-
-#ifndef emacs
-
-/* regcomp takes a regular expression as a string and compiles it.
-
-   PREG is a regex_t *.  We do not expect any fields to be initialized,
-   since POSIX says we shouldn't.  Thus, we set
-
-     `buffer' to the compiled pattern;
-     `used' to the length of the compiled pattern;
-     `syntax' to RE_SYNTAX_POSIX_EXTENDED if the
-       REG_EXTENDED bit in CFLAGS is set; otherwise, to
-       RE_SYNTAX_POSIX_BASIC;
-     `newline_anchor' to REG_NEWLINE being set in CFLAGS;
-     `fastmap' and `fastmap_accurate' to zero;
-     `re_nsub' to the number of subexpressions in PATTERN.
-
-   PATTERN is the address of the pattern string.
-
-   CFLAGS is a series of bits which affect compilation.
-
-     If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we
-     use POSIX basic syntax.
-
-     If REG_NEWLINE is set, then . and [^...] don't match newline.
-     Also, regexec will try a match beginning after every newline.
-
-     If REG_ICASE is set, then we considers upper- and lowercase
-     versions of letters to be equivalent when matching.
-
-     If REG_NOSUB is set, then when PREG is passed to regexec, that
-     routine will report only success or failure, and nothing about the
-     registers.
-
-   It returns 0 if it succeeds, nonzero if it doesn't.  (See regex.h for
-   the return codes and their meanings.)  */
-
-int
-regcomp (preg, pattern, cflags)
-    regex_t *preg;
-    const char *pattern;
-    int cflags;
-{
-  reg_errcode_t ret;
-  unsigned syntax
-    = (cflags & REG_EXTENDED) ?
-      RE_SYNTAX_POSIX_EXTENDED : RE_SYNTAX_POSIX_BASIC;
-
-  /* regex_compile will allocate the space for the compiled pattern.  */
-  preg->buffer = 0;
-  preg->allocated = 0;
-
-  /* Don't bother to use a fastmap when searching.  This simplifies the
-     REG_NEWLINE case: if we used a fastmap, we'd have to put all the
-     characters after newlines into the fastmap.  This way, we just try
-     every character.  */
-  preg->fastmap = 0;
-
-  if (cflags & REG_ICASE)
-    {
-      unsigned i;
-
-      preg->translate = (char *) malloc (CHAR_SET_SIZE);
-      if (preg->translate == NULL)
-	return (int) REG_ESPACE;
-
-      /* Map uppercase characters to corresponding lowercase ones.  */
-      for (i = 0; i < CHAR_SET_SIZE; i++)
-	preg->translate[i] = ISUPPER (i) ? tolower (i) : i;
-    }
-  else
-    preg->translate = NULL;
-
-  /* If REG_NEWLINE is set, newlines are treated differently.  */
-  if (cflags & REG_NEWLINE)
-    { /* REG_NEWLINE implies neither . nor [^...] match newline.  */
-      syntax &= ~RE_DOT_NEWLINE;
-      syntax |= RE_HAT_LISTS_NOT_NEWLINE;
-      /* It also changes the matching behavior.  */
-      preg->newline_anchor = 1;
-    }
-  else
-    preg->newline_anchor = 0;
-
-  preg->no_sub = !!(cflags & REG_NOSUB);
-
-  /* POSIX says a null character in the pattern terminates it, so we
-     can use strlen here in compiling the pattern.  */
-  ret = regex_compile (pattern, strlen (pattern), syntax, preg);
-
-  /* POSIX doesn't distinguish between an unmatched open-group and an
-     unmatched close-group: both are REG_EPAREN.  */
-  if (ret == REG_ERPAREN) ret = REG_EPAREN;
-
-  return (int) ret;
-}
-
-
-/* regexec searches for a given pattern, specified by PREG, in the
-   string STRING.
-
-   If NMATCH is zero or REG_NOSUB was set in the cflags argument to
-   `regcomp', we ignore PMATCH.  Otherwise, we assume PMATCH has at
-   least NMATCH elements, and we set them to the offsets of the
-   corresponding matched substrings.
-
-   EFLAGS specifies `execution flags' which affect matching: if
-   REG_NOTBOL is set, then ^ does not match at the beginning of the
-   string; if REG_NOTEOL is set, then $ does not match at the end.
-
-   We return 0 if we find a match and REG_NOMATCH if not.  */
-
-int
-regexec (preg, string, nmatch, pmatch, eflags)
-    const regex_t *preg;
-    const char *string;
-    size_t nmatch;
-    regmatch_t pmatch[];
-    int eflags;
-{
-  int ret;
-  struct re_registers regs;
-  regex_t private_preg;
-  int len = strlen (string);
-  boolean want_reg_info = !preg->no_sub && nmatch > 0;
-
-  private_preg = *preg;
-
-  private_preg.not_bol = !!(eflags & REG_NOTBOL);
-  private_preg.not_eol = !!(eflags & REG_NOTEOL);
-
-  /* The user has told us exactly how many registers to return
-     information about, via `nmatch'.  We have to pass that on to the
-     matching routines.  */
-  private_preg.regs_allocated = REGS_FIXED;
-
-  if (want_reg_info)
-    {
-      regs.num_regs = nmatch;
-      regs.start = TALLOC (nmatch, regoff_t);
-      regs.end = TALLOC (nmatch, regoff_t);
-      if (regs.start == NULL || regs.end == NULL)
-	return (int) REG_NOMATCH;
-    }
-
-  /* Perform the searching operation.  */
-  ret = re_search (&private_preg, string, len,
-		   /* start: */ 0, /* range: */ len,
-		   want_reg_info ? &regs : (struct re_registers *) 0);
-
-  /* Copy the register information to the POSIX structure.  */
-  if (want_reg_info)
-    {
-      if (ret >= 0)
-	{
-	  unsigned r;
-
-	  for (r = 0; r < nmatch; r++)
-	    {
-	      pmatch[r].rm_so = regs.start[r];
-	      pmatch[r].rm_eo = regs.end[r];
-	    }
-	}
-
-      /* If we needed the temporary register info, free the space now.  */
-      free (regs.start);
-      free (regs.end);
-    }
-
-  /* We want zero return to mean success, unlike `re_search'.  */
-  return ret >= 0 ? (int) REG_NOERROR : (int) REG_NOMATCH;
-}
-
-
-/* Returns a message corresponding to an error code, ERRCODE, returned
-   from either regcomp or regexec.   We don't use PREG here.  */
-
-size_t
-regerror (errcode, preg, errbuf, errbuf_size)
-    int errcode;
-    const regex_t *preg;
-    char *errbuf;
-    size_t errbuf_size;
-{
-  const char *msg;
-  size_t msg_size;
-
-  if (errcode < 0
-      || errcode >= (sizeof (re_error_msg) / sizeof (re_error_msg[0])))
-    /* Only error codes returned by the rest of the code should be passed
-       to this routine.  If we are given anything else, or if other regex
-       code generates an invalid error code, then the program has a bug.
-       Dump core so we can fix it.  */
-    abort ();
-
-  msg = re_error_msg[errcode];
-
-  /* POSIX doesn't require that we do anything in this case, but why
-     not be nice.  */
-  if (! msg)
-    msg = "Success";
-
-  msg_size = strlen (msg) + 1; /* Includes the null.  */
-
-  if (errbuf_size != 0)
-    {
-      if (msg_size > errbuf_size)
-	{
-	  strncpy (errbuf, msg, errbuf_size - 1);
-	  errbuf[errbuf_size - 1] = 0;
-	}
-      else
-	strcpy (errbuf, msg);
-    }
-
-  return msg_size;
-}
-
-
-/* Free dynamically allocated space used by PREG.  */
-
-void
-regfree (preg)
-    regex_t *preg;
-{
-  if (preg->buffer != NULL)
-    free (preg->buffer);
-  preg->buffer = NULL;
-
-  preg->allocated = 0;
-  preg->used = 0;
-
-  if (preg->fastmap != NULL)
-    free (preg->fastmap);
-  preg->fastmap = NULL;
-  preg->fastmap_accurate = 0;
-
-  if (preg->translate != NULL)
-    free (preg->translate);
-  preg->translate = NULL;
-}
-
-#endif /* not emacs  */
-
-/*
-Local variables:
-make-backup-files: t
-version-control: t
-trim-versions-without-asking: nil
-End:
-*/
diff --git a/compat/regex/regex.h b/compat/regex/regex.h
index 6eb64f1..61c9683 100644
--- a/compat/regex/regex.h
+++ b/compat/regex/regex.h
@@ -1,70 +1,90 @@
+#include <stdio.h>
+#include <stddef.h>
+
 /* Definitions for data structures and routines for the regular
-   expression library, version 0.12.
+   expression library.
+   Copyright (C) 1985,1989-93,1995-98,2000,2001,2002,2003,2005,2006,2008
+   Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
 
-   Copyright (C) 1985, 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
 
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2, or (at your option)
-   any later version.
-
-   This program is distributed in the hope that it will be useful,
+   The GNU C Library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
 
-   You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA.  */
 
-#ifndef __REGEXP_LIBRARY_H__
-#define __REGEXP_LIBRARY_H__
+#ifndef _REGEX_H
+#define _REGEX_H 1
 
-/* POSIX says that <sys/types.h> must be included (by the caller) before
-   <regex.h>.  */
-
-#ifdef VMS
-/* VMS doesn't have `size_t' in <sys/types.h>, even though POSIX says it
-   should be there.  */
+#ifdef HAVE_STDDEF_H
 #include <stddef.h>
 #endif
 
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifndef _LIBC
+#define __USE_GNU	1
+#endif
+
+/* Allow the use in C++ code.  */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The following two types have to be signed and unsigned integer type
+   wide enough to hold a value of a pointer.  For most ANSI compilers
+   ptrdiff_t and size_t should be likely OK.  Still size of these two
+   types is 2 for Microsoft C.  Ugh... */
+typedef long int s_reg_t;
+typedef unsigned long int active_reg_t;
 
 /* The following bits are used to determine the regexp syntax we
    recognize.  The set/not-set meanings are chosen so that Emacs syntax
    remains the value 0.  The bits are given in alphabetical order, and
    the definitions shifted by one from the previous bit; thus, when we
    add or remove a bit, only one other definition need change.  */
-typedef unsigned reg_syntax_t;
+typedef unsigned long int reg_syntax_t;
 
+#ifdef __USE_GNU
 /* If this bit is not set, then \ inside a bracket expression is literal.
    If set, then such a \ quotes the following character.  */
-#define RE_BACKSLASH_ESCAPE_IN_LISTS (1)
+# define RE_BACKSLASH_ESCAPE_IN_LISTS ((unsigned long int) 1)
 
 /* If this bit is not set, then + and ? are operators, and \+ and \? are
      literals.
    If set, then \+ and \? are operators and + and ? are literals.  */
-#define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1)
+# define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1)
 
 /* If this bit is set, then character classes are supported.  They are:
      [:alpha:], [:upper:], [:lower:],  [:digit:], [:alnum:], [:xdigit:],
      [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:].
    If not set, then character classes are not supported.  */
-#define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1)
+# define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1)
 
 /* If this bit is set, then ^ and $ are always anchors (outside bracket
      expressions, of course).
    If this bit is not set, then it depends:
-	^  is an anchor if it is at the beginning of a regular
-	   expression or after an open-group or an alternation operator;
-	$  is an anchor if it is at the end of a regular expression, or
-	   before a close-group or an alternation operator.
+        ^  is an anchor if it is at the beginning of a regular
+           expression or after an open-group or an alternation operator;
+        $  is an anchor if it is at the end of a regular expression, or
+           before a close-group or an alternation operator.
 
    This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because
    POSIX draft 11.2 says that * etc. in leading positions is undefined.
    We already implemented a previous draft which made those constructs
    invalid, though, so we haven't changed the code back.  */
-#define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1)
+# define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1)
 
 /* If this bit is set, then special characters are always special
      regardless of where they are in the pattern.
@@ -72,63 +92,94 @@
      some contexts; otherwise they are ordinary.  Specifically,
      * + ? and intervals are only special when not after the beginning,
      open-group, or alternation operator.  */
-#define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1)
+# define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1)
 
 /* If this bit is set, then *, +, ?, and { cannot be first in an re or
      immediately after an alternation or begin-group operator.  */
-#define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1)
+# define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1)
 
 /* If this bit is set, then . matches newline.
    If not set, then it doesn't.  */
-#define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1)
+# define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1)
 
 /* If this bit is set, then . doesn't match NUL.
    If not set, then it does.  */
-#define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1)
+# define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1)
 
 /* If this bit is set, nonmatching lists [^...] do not match newline.
    If not set, they do.  */
-#define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1)
+# define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1)
 
 /* If this bit is set, either \{...\} or {...} defines an
      interval, depending on RE_NO_BK_BRACES.
    If not set, \{, \}, {, and } are literals.  */
-#define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1)
+# define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1)
 
 /* If this bit is set, +, ? and | aren't recognized as operators.
    If not set, they are.  */
-#define RE_LIMITED_OPS (RE_INTERVALS << 1)
+# define RE_LIMITED_OPS (RE_INTERVALS << 1)
 
 /* If this bit is set, newline is an alternation operator.
    If not set, newline is literal.  */
-#define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1)
+# define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1)
 
 /* If this bit is set, then `{...}' defines an interval, and \{ and \}
      are literals.
   If not set, then `\{...\}' defines an interval.  */
-#define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1)
+# define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1)
 
 /* If this bit is set, (...) defines a group, and \( and \) are literals.
    If not set, \(...\) defines a group, and ( and ) are literals.  */
-#define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1)
+# define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1)
 
 /* If this bit is set, then \<digit> matches <digit>.
    If not set, then \<digit> is a back-reference.  */
-#define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1)
+# define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1)
 
 /* If this bit is set, then | is an alternation operator, and \| is literal.
    If not set, then \| is an alternation operator, and | is literal.  */
-#define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1)
+# define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1)
 
 /* If this bit is set, then an ending range point collating higher
      than the starting range point, as in [z-a], is invalid.
    If not set, then when ending range point collates higher than the
      starting range point, the range is ignored.  */
-#define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1)
+# define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1)
 
 /* If this bit is set, then an unmatched ) is ordinary.
    If not set, then an unmatched ) is invalid.  */
-#define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1)
+# define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1)
+
+/* If this bit is set, succeed as soon as we match the whole pattern,
+   without further backtracking.  */
+# define RE_NO_POSIX_BACKTRACKING (RE_UNMATCHED_RIGHT_PAREN_ORD << 1)
+
+/* If this bit is set, do not process the GNU regex operators.
+   If not set, then the GNU regex operators are recognized. */
+# define RE_NO_GNU_OPS (RE_NO_POSIX_BACKTRACKING << 1)
+
+/* If this bit is set, a syntactically invalid interval is treated as
+   a string of ordinary characters.  For example, the ERE 'a{1' is
+   treated as 'a\{1'.  */
+# define RE_INVALID_INTERVAL_ORD (RE_NO_GNU_OPS << 1)
+
+/* If this bit is set, then ignore case when matching.
+   If not set, then case is significant.  */
+# define RE_ICASE (RE_INVALID_INTERVAL_ORD << 1)
+
+/* This bit is used internally like RE_CONTEXT_INDEP_ANCHORS but only
+   for ^, because it is difficult to scan the regex backwards to find
+   whether ^ should be special.  */
+# define RE_CARET_ANCHORS_HERE (RE_ICASE << 1)
+
+/* If this bit is set, then \{ cannot be first in an bre or
+   immediately after an alternation or begin-group operator.  */
+# define RE_CONTEXT_INVALID_DUP (RE_CARET_ANCHORS_HERE << 1)
+
+/* If this bit is set, then no_sub will be set to 1 during
+   re_compile_pattern.  */
+#define RE_NO_SUB (RE_CONTEXT_INVALID_DUP << 1)
+#endif
 
 /* This global variable defines the particular regexp syntax to use (for
    some interfaces).  When a regexp is compiled, the syntax used is
@@ -136,6 +187,7 @@
    already-compiled regexps.  */
 extern reg_syntax_t re_syntax_options;
 
+#ifdef __USE_GNU
 /* Define combinations of the above bits for the standard possibilities.
    (The [[[ comments delimit what gets put into the Texinfo file, so
    don't delete them!)  */
@@ -143,13 +195,22 @@
 #define RE_SYNTAX_EMACS 0
 
 #define RE_SYNTAX_AWK							\
-  (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL			\
-   | RE_NO_BK_PARENS            | RE_NO_BK_REFS				\
-   | RE_NO_BK_VBAR               | RE_NO_EMPTY_RANGES			\
-   | RE_UNMATCHED_RIGHT_PAREN_ORD)
+  (RE_BACKSLASH_ESCAPE_IN_LISTS   | RE_DOT_NOT_NULL			\
+   | RE_NO_BK_PARENS              | RE_NO_BK_REFS			\
+   | RE_NO_BK_VBAR                | RE_NO_EMPTY_RANGES			\
+   | RE_DOT_NEWLINE		  | RE_CONTEXT_INDEP_ANCHORS		\
+   | RE_UNMATCHED_RIGHT_PAREN_ORD | RE_NO_GNU_OPS)
 
-#define RE_SYNTAX_POSIX_AWK 						\
-  (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS)
+#define RE_SYNTAX_GNU_AWK						\
+  ((RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS		\
+   | RE_INVALID_INTERVAL_ORD)						\
+   & ~(RE_DOT_NOT_NULL | RE_CONTEXT_INDEP_OPS				\
+       | RE_CONTEXT_INVALID_OPS ))
+
+#define RE_SYNTAX_POSIX_AWK						\
+  (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS		\
+   | RE_INTERVALS	    | RE_NO_GNU_OPS				\
+   | RE_INVALID_INTERVAL_ORD)
 
 #define RE_SYNTAX_GREP							\
   (RE_BK_PLUS_QM              | RE_CHAR_CLASSES				\
@@ -163,7 +224,8 @@
    | RE_NO_BK_VBAR)
 
 #define RE_SYNTAX_POSIX_EGREP						\
-  (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES)
+  (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES			\
+   | RE_INVALID_INTERVAL_ORD)
 
 /* P1003.2/D11.2, section 4.20.7.1, lines 5078ff.  */
 #define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC
@@ -176,7 +238,7 @@
    | RE_INTERVALS  | RE_NO_EMPTY_RANGES)
 
 #define RE_SYNTAX_POSIX_BASIC						\
-  (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM)
+  (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM | RE_CONTEXT_INVALID_DUP)
 
 /* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes
    RE_LIMITED_OPS, i.e., \? \+ \| are not recognized.  Actually, this
@@ -185,13 +247,13 @@
   (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS)
 
 #define RE_SYNTAX_POSIX_EXTENDED					\
-  (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS			\
-   | RE_CONTEXT_INDEP_OPS  | RE_NO_BK_BRACES				\
-   | RE_NO_BK_PARENS       | RE_NO_BK_VBAR				\
-   | RE_UNMATCHED_RIGHT_PAREN_ORD)
+  (_RE_SYNTAX_POSIX_COMMON  | RE_CONTEXT_INDEP_ANCHORS			\
+   | RE_CONTEXT_INDEP_OPS   | RE_NO_BK_BRACES				\
+   | RE_NO_BK_PARENS        | RE_NO_BK_VBAR				\
+   | RE_CONTEXT_INVALID_OPS | RE_UNMATCHED_RIGHT_PAREN_ORD)
 
-/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INVALID_OPS
-   replaces RE_CONTEXT_INDEP_OPS and RE_NO_BK_REFS is added.  */
+/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INDEP_OPS is
+   removed and RE_NO_BK_REFS is added.  */
 #define RE_SYNTAX_POSIX_MINIMAL_EXTENDED				\
   (_RE_SYNTAX_POSIX_COMMON  | RE_CONTEXT_INDEP_ANCHORS			\
    | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES				\
@@ -202,10 +264,12 @@
 /* Maximum number of duplicates an interval can allow.  Some systems
    (erroneously) define this in other header files, but we want our
    value, so remove any previous define.  */
-#ifdef RE_DUP_MAX
-#undef RE_DUP_MAX
+# ifdef RE_DUP_MAX
+#  undef RE_DUP_MAX
+# endif
+/* If sizeof(int) == 2, then ((1 << 15) - 1) overflows.  */
+# define RE_DUP_MAX (0x7fff)
 #endif
-#define RE_DUP_MAX ((1 << 15) - 1)
 
 
 /* POSIX `cflags' bits (i.e., information for `regcomp').  */
@@ -240,18 +304,26 @@
 /* Like REG_NOTBOL, except for the end-of-line.  */
 #define REG_NOTEOL (1 << 1)
 
+/* Use PMATCH[0] to delimit the start and end of the search in the
+   buffer.  */
+#define REG_STARTEND (1 << 2)
+
 
 /* If any error codes are removed, changed, or added, update the
    `re_error_msg' table in regex.c.  */
 typedef enum
 {
+#if defined _XOPEN_SOURCE || defined __USE_XOPEN2K
+  REG_ENOSYS = -1,	/* This will never happen for this implementation.  */
+#endif
+
   REG_NOERROR = 0,	/* Success.  */
   REG_NOMATCH,		/* Didn't find a match (for regexec).  */
 
   /* POSIX regcomp return error codes.  (In the order listed in the
      standard.)  */
   REG_BADPAT,		/* Invalid pattern.  */
-  REG_ECOLLATE,		/* Not implemented.  */
+  REG_ECOLLATE,		/* Inalid collating element.  */
   REG_ECTYPE,		/* Invalid character class name.  */
   REG_EESCAPE,		/* Trailing backslash.  */
   REG_ESUBREG,		/* Invalid back reference.  */
@@ -275,85 +347,92 @@
    compiled, the `re_nsub' field is available.  All other fields are
    private to the regex routines.  */
 
+#ifndef RE_TRANSLATE_TYPE
+# define __RE_TRANSLATE_TYPE unsigned char *
+# ifdef __USE_GNU
+#  define RE_TRANSLATE_TYPE __RE_TRANSLATE_TYPE
+# endif
+#endif
+
+#ifdef __USE_GNU
+# define __REPB_PREFIX(name) name
+#else
+# define __REPB_PREFIX(name) __##name
+#endif
+
 struct re_pattern_buffer
 {
-/* [[[begin pattern_buffer]]] */
-	/* Space that holds the compiled pattern.  It is declared as
-	  `unsigned char *' because its elements are
-	   sometimes used as array indexes.  */
-  unsigned char *buffer;
+  /* Space that holds the compiled pattern.  It is declared as
+     `unsigned char *' because its elements are sometimes used as
+     array indexes.  */
+  unsigned char *__REPB_PREFIX(buffer);
 
-	/* Number of bytes to which `buffer' points.  */
-  unsigned long allocated;
+  /* Number of bytes to which `buffer' points.  */
+  unsigned long int __REPB_PREFIX(allocated);
 
-	/* Number of bytes actually used in `buffer'.  */
-  unsigned long used;
+  /* Number of bytes actually used in `buffer'.  */
+  unsigned long int __REPB_PREFIX(used);
 
-	/* Syntax setting with which the pattern was compiled.  */
-  reg_syntax_t syntax;
+  /* Syntax setting with which the pattern was compiled.  */
+  reg_syntax_t __REPB_PREFIX(syntax);
 
-	/* Pointer to a fastmap, if any, otherwise zero.  re_search uses
-	   the fastmap, if there is one, to skip over impossible
-	   starting points for matches.  */
-  char *fastmap;
+  /* Pointer to a fastmap, if any, otherwise zero.  re_search uses the
+     fastmap, if there is one, to skip over impossible starting points
+     for matches.  */
+  char *__REPB_PREFIX(fastmap);
 
-	/* Either a translate table to apply to all characters before
-	   comparing them, or zero for no translation.  The translation
-	   is applied to a pattern when it is compiled and to a string
-	   when it is matched.  */
-  char *translate;
+  /* Either a translate table to apply to all characters before
+     comparing them, or zero for no translation.  The translation is
+     applied to a pattern when it is compiled and to a string when it
+     is matched.  */
+  __RE_TRANSLATE_TYPE __REPB_PREFIX(translate);
 
-	/* Number of subexpressions found by the compiler.  */
+  /* Number of subexpressions found by the compiler.  */
   size_t re_nsub;
 
-	/* Zero if this pattern cannot match the empty string, one else.
-	   Well, in truth it's used only in `re_search_2', to see
-	   whether or not we should use the fastmap, so we don't set
-	   this absolutely perfectly; see `re_compile_fastmap' (the
-	   `duplicate' case).  */
-  unsigned can_be_null : 1;
+  /* Zero if this pattern cannot match the empty string, one else.
+     Well, in truth it's used only in `re_search_2', to see whether or
+     not we should use the fastmap, so we don't set this absolutely
+     perfectly; see `re_compile_fastmap' (the `duplicate' case).  */
+  unsigned __REPB_PREFIX(can_be_null) : 1;
 
-	/* If REGS_UNALLOCATED, allocate space in the `regs' structure
-	     for `max (RE_NREGS, re_nsub + 1)' groups.
-	   If REGS_REALLOCATE, reallocate space if necessary.
-	   If REGS_FIXED, use what's there.  */
-#define REGS_UNALLOCATED 0
-#define REGS_REALLOCATE 1
-#define REGS_FIXED 2
-  unsigned regs_allocated : 2;
+  /* If REGS_UNALLOCATED, allocate space in the `regs' structure
+     for `max (RE_NREGS, re_nsub + 1)' groups.
+     If REGS_REALLOCATE, reallocate space if necessary.
+     If REGS_FIXED, use what's there.  */
+#ifdef __USE_GNU
+# define REGS_UNALLOCATED 0
+# define REGS_REALLOCATE 1
+# define REGS_FIXED 2
+#endif
+  unsigned __REPB_PREFIX(regs_allocated) : 2;
 
-	/* Set to zero when `regex_compile' compiles a pattern; set to one
-	   by `re_compile_fastmap' if it updates the fastmap.  */
-  unsigned fastmap_accurate : 1;
+  /* Set to zero when `regex_compile' compiles a pattern; set to one
+     by `re_compile_fastmap' if it updates the fastmap.  */
+  unsigned __REPB_PREFIX(fastmap_accurate) : 1;
 
-	/* If set, `re_match_2' does not return information about
-	   subexpressions.  */
-  unsigned no_sub : 1;
+  /* If set, `re_match_2' does not return information about
+     subexpressions.  */
+  unsigned __REPB_PREFIX(no_sub) : 1;
 
-	/* If set, a beginning-of-line anchor doesn't match at the
-	   beginning of the string.  */
-  unsigned not_bol : 1;
+  /* If set, a beginning-of-line anchor doesn't match at the beginning
+     of the string.  */
+  unsigned __REPB_PREFIX(not_bol) : 1;
 
-	/* Similarly for an end-of-line anchor.  */
-  unsigned not_eol : 1;
+  /* Similarly for an end-of-line anchor.  */
+  unsigned __REPB_PREFIX(not_eol) : 1;
 
-	/* If true, an anchor at a newline matches.  */
-  unsigned newline_anchor : 1;
-
-/* [[[end pattern_buffer]]] */
+  /* If true, an anchor at a newline matches.  */
+  unsigned __REPB_PREFIX(newline_anchor) : 1;
 };
 
 typedef struct re_pattern_buffer regex_t;
-
-
-/* search.c (search_buffer) in Emacs needs this one opcode value.  It is
-   defined both in `regex.c' and here.  */
-#define RE_EXACTN_VALUE 1
 
 /* Type for byte offsets within the string.  POSIX mandates this.  */
 typedef int regoff_t;
 
 
+#ifdef __USE_GNU
 /* This is the structure we store register match data in.  See
    regex.texinfo for a full description of what registers match.  */
 struct re_registers
@@ -367,8 +446,9 @@
 /* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer,
    `re_match_2' returns information about at least this many registers
    the first time a `regs' structure is passed.  */
-#ifndef RE_NREGS
-#define RE_NREGS 30
+# ifndef RE_NREGS
+#  define RE_NREGS 30
+# endif
 #endif
 
 
@@ -383,38 +463,22 @@
 
 /* Declarations for routines.  */
 
-/* To avoid duplicating every routine declaration -- once with a
-   prototype (if we are ANSI), and once without (if we aren't) -- we
-   use the following macro to declare argument types.  This
-   unfortunately clutters up the declarations a bit, but I think it's
-   worth it.  */
-
-#if __STDC__
-
-#define _RE_ARGS(args) args
-
-#else /* not __STDC__ */
-
-#define _RE_ARGS(args) ()
-
-#endif /* not __STDC__ */
-
+#ifdef __USE_GNU
 /* Sets the current default syntax to SYNTAX, and return the old syntax.
    You can also simply assign to the `re_syntax_options' variable.  */
-extern reg_syntax_t re_set_syntax _RE_ARGS ((reg_syntax_t syntax));
+extern reg_syntax_t re_set_syntax (reg_syntax_t __syntax);
 
 /* Compile the regular expression PATTERN, with length LENGTH
    and syntax given by the global `re_syntax_options', into the buffer
    BUFFER.  Return NULL if successful, and an error string if not.  */
-extern const char *re_compile_pattern
-  _RE_ARGS ((const char *pattern, int length,
-	     struct re_pattern_buffer *buffer));
+extern const char *re_compile_pattern (const char *__pattern, size_t __length,
+				       struct re_pattern_buffer *__buffer);
 
 
 /* Compile a fastmap for the compiled pattern in BUFFER; used to
    accelerate searches.  Return 0 if successful and -2 if was an
    internal error.  */
-extern int re_compile_fastmap _RE_ARGS ((struct re_pattern_buffer *buffer));
+extern int re_compile_fastmap (struct re_pattern_buffer *__buffer);
 
 
 /* Search in the string STRING (with length LENGTH) for the pattern
@@ -422,31 +486,30 @@
    characters.  Return the starting position of the match, -1 for no
    match, or -2 for an internal error.  Also return register
    information in REGS (if REGS and BUFFER->no_sub are nonzero).  */
-extern int re_search
-  _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
-	    int length, int start, int range, struct re_registers *regs));
+extern int re_search (struct re_pattern_buffer *__buffer, const char *__cstring,
+		      int __length, int __start, int __range,
+		      struct re_registers *__regs);
 
 
 /* Like `re_search', but search in the concatenation of STRING1 and
    STRING2.  Also, stop searching at index START + STOP.  */
-extern int re_search_2
-  _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
-	     int length1, const char *string2, int length2,
-	     int start, int range, struct re_registers *regs, int stop));
+extern int re_search_2 (struct re_pattern_buffer *__buffer,
+			const char *__string1, int __length1,
+			const char *__string2, int __length2, int __start,
+			int __range, struct re_registers *__regs, int __stop);
 
 
 /* Like `re_search', but return how many characters in STRING the regexp
    in BUFFER matched, starting at position START.  */
-extern int re_match
-  _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
-	     int length, int start, struct re_registers *regs));
+extern int re_match (struct re_pattern_buffer *__buffer, const char *__cstring,
+		     int __length, int __start, struct re_registers *__regs);
 
 
 /* Relates to `re_match' as `re_search_2' relates to `re_search'.  */
-extern int re_match_2
-  _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
-	     int length1, const char *string2, int length2,
-	     int start, struct re_registers *regs, int stop));
+extern int re_match_2 (struct re_pattern_buffer *__buffer,
+		       const char *__string1, int __length1,
+		       const char *__string2, int __length2, int __start,
+		       struct re_registers *__regs, int __stop);
 
 
 /* Set REGS to hold NUM_REGS registers, storing them in STARTS and
@@ -461,30 +524,59 @@
    Unless this function is called, the first search or match using
    PATTERN_BUFFER will allocate its own register data, without
    freeing the old data.  */
-extern void re_set_registers
-  _RE_ARGS ((struct re_pattern_buffer *buffer, struct re_registers *regs,
-	     unsigned num_regs, regoff_t *starts, regoff_t *ends));
+extern void re_set_registers (struct re_pattern_buffer *__buffer,
+			      struct re_registers *__regs,
+			      unsigned int __num_regs,
+			      regoff_t *__starts, regoff_t *__ends);
+#endif	/* Use GNU */
 
+#if defined _REGEX_RE_COMP || (defined _LIBC && defined __USE_BSD)
+# ifndef _CRAY
 /* 4.2 bsd compatibility.  */
-extern char *re_comp _RE_ARGS ((const char *));
-extern int re_exec _RE_ARGS ((const char *));
+extern char *re_comp (const char *);
+extern int re_exec (const char *);
+# endif
+#endif
+
+/* GCC 2.95 and later have "__restrict"; C99 compilers have
+   "restrict", and "configure" may have defined "restrict".  */
+#ifndef __restrict
+# if ! (2 < __GNUC__ || (2 == __GNUC__ && 95 <= __GNUC_MINOR__))
+#  if defined restrict || 199901L <= __STDC_VERSION__
+#   define __restrict restrict
+#  else
+#   define __restrict
+#  endif
+# endif
+#endif
+/* gcc 3.1 and up support the [restrict] syntax.  */
+#ifndef __restrict_arr
+# if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) \
+     && !defined __GNUG__
+#  define __restrict_arr __restrict
+# else
+#  define __restrict_arr
+# endif
+#endif
 
 /* POSIX compatibility.  */
-extern int regcomp _RE_ARGS ((regex_t *preg, const char *pattern, int cflags));
-extern int regexec
-  _RE_ARGS ((const regex_t *preg, const char *string, size_t nmatch,
-	     regmatch_t pmatch[], int eflags));
-extern size_t regerror
-  _RE_ARGS ((int errcode, const regex_t *preg, char *errbuf,
-	     size_t errbuf_size));
-extern void regfree _RE_ARGS ((regex_t *preg));
+extern int regcomp (regex_t *__restrict __preg,
+		    const char *__restrict __pattern,
+		    int __cflags);
 
-#endif /* not __REGEXP_LIBRARY_H__ */
-
-/*
-Local variables:
-make-backup-files: t
-version-control: t
-trim-versions-without-asking: nil
-End:
-*/
+extern int regexec (const regex_t *__restrict __preg,
+		    const char *__restrict __cstring, size_t __nmatch,
+		    regmatch_t __pmatch[__restrict_arr],
+		    int __eflags);
+
+extern size_t regerror (int __errcode, const regex_t *__restrict __preg,
+			char *__restrict __errbuf, size_t __errbuf_size);
+
+extern void regfree (regex_t *__preg);
+
+
+#ifdef __cplusplus
+}
+#endif	/* C++ */
+
+#endif /* regex.h */
diff --git a/compat/regex/regex_internal.c b/compat/regex/regex_internal.c
new file mode 100644
index 0000000..193854c
--- /dev/null
+++ b/compat/regex/regex_internal.c
@@ -0,0 +1,1744 @@
+/* Extended regular expression matching and search library.
+   Copyright (C) 2002-2006, 2010 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA.  */
+
+static void re_string_construct_common (const char *str, int len,
+					re_string_t *pstr,
+					RE_TRANSLATE_TYPE trans, int icase,
+					const re_dfa_t *dfa) internal_function;
+static re_dfastate_t *create_ci_newstate (const re_dfa_t *dfa,
+					  const re_node_set *nodes,
+					  unsigned int hash) internal_function;
+static re_dfastate_t *create_cd_newstate (const re_dfa_t *dfa,
+					  const re_node_set *nodes,
+					  unsigned int context,
+					  unsigned int hash) internal_function;
+
+#ifdef GAWK
+#undef MAX	/* safety */
+static int
+MAX(size_t a, size_t b)
+{
+	return (a > b ? a : b);
+}
+#endif
+
+/* Functions for string operation.  */
+
+/* This function allocate the buffers.  It is necessary to call
+   re_string_reconstruct before using the object.  */
+
+static reg_errcode_t
+internal_function
+re_string_allocate (re_string_t *pstr, const char *str, int len, int init_len,
+		    RE_TRANSLATE_TYPE trans, int icase, const re_dfa_t *dfa)
+{
+  reg_errcode_t ret;
+  int init_buf_len;
+
+  /* Ensure at least one character fits into the buffers.  */
+  if (init_len < dfa->mb_cur_max)
+    init_len = dfa->mb_cur_max;
+  init_buf_len = (len + 1 < init_len) ? len + 1: init_len;
+  re_string_construct_common (str, len, pstr, trans, icase, dfa);
+
+  ret = re_string_realloc_buffers (pstr, init_buf_len);
+  if (BE (ret != REG_NOERROR, 0))
+    return ret;
+
+  pstr->word_char = dfa->word_char;
+  pstr->word_ops_used = dfa->word_ops_used;
+  pstr->mbs = pstr->mbs_allocated ? pstr->mbs : (unsigned char *) str;
+  pstr->valid_len = (pstr->mbs_allocated || dfa->mb_cur_max > 1) ? 0 : len;
+  pstr->valid_raw_len = pstr->valid_len;
+  return REG_NOERROR;
+}
+
+/* This function allocate the buffers, and initialize them.  */
+
+static reg_errcode_t
+internal_function
+re_string_construct (re_string_t *pstr, const char *str, int len,
+		     RE_TRANSLATE_TYPE trans, int icase, const re_dfa_t *dfa)
+{
+  reg_errcode_t ret;
+  memset (pstr, '\0', sizeof (re_string_t));
+  re_string_construct_common (str, len, pstr, trans, icase, dfa);
+
+  if (len > 0)
+    {
+      ret = re_string_realloc_buffers (pstr, len + 1);
+      if (BE (ret != REG_NOERROR, 0))
+	return ret;
+    }
+  pstr->mbs = pstr->mbs_allocated ? pstr->mbs : (unsigned char *) str;
+
+  if (icase)
+    {
+#ifdef RE_ENABLE_I18N
+      if (dfa->mb_cur_max > 1)
+	{
+	  while (1)
+	    {
+	      ret = build_wcs_upper_buffer (pstr);
+	      if (BE (ret != REG_NOERROR, 0))
+		return ret;
+	      if (pstr->valid_raw_len >= len)
+		break;
+	      if (pstr->bufs_len > pstr->valid_len + dfa->mb_cur_max)
+		break;
+	      ret = re_string_realloc_buffers (pstr, pstr->bufs_len * 2);
+	      if (BE (ret != REG_NOERROR, 0))
+		return ret;
+	    }
+	}
+      else
+#endif /* RE_ENABLE_I18N  */
+	build_upper_buffer (pstr);
+    }
+  else
+    {
+#ifdef RE_ENABLE_I18N
+      if (dfa->mb_cur_max > 1)
+	build_wcs_buffer (pstr);
+      else
+#endif /* RE_ENABLE_I18N  */
+	{
+	  if (trans != NULL)
+	    re_string_translate_buffer (pstr);
+	  else
+	    {
+	      pstr->valid_len = pstr->bufs_len;
+	      pstr->valid_raw_len = pstr->bufs_len;
+	    }
+	}
+    }
+
+  return REG_NOERROR;
+}
+
+/* Helper functions for re_string_allocate, and re_string_construct.  */
+
+static reg_errcode_t
+internal_function
+re_string_realloc_buffers (re_string_t *pstr, int new_buf_len)
+{
+#ifdef RE_ENABLE_I18N
+  if (pstr->mb_cur_max > 1)
+    {
+      wint_t *new_wcs;
+
+      /* Avoid overflow in realloc.  */
+      const size_t max_object_size = MAX (sizeof (wint_t), sizeof (int));
+      if (BE (SIZE_MAX / max_object_size < new_buf_len, 0))
+	return REG_ESPACE;
+
+      new_wcs = re_realloc (pstr->wcs, wint_t, new_buf_len);
+      if (BE (new_wcs == NULL, 0))
+	return REG_ESPACE;
+      pstr->wcs = new_wcs;
+      if (pstr->offsets != NULL)
+	{
+	  int *new_offsets = re_realloc (pstr->offsets, int, new_buf_len);
+	  if (BE (new_offsets == NULL, 0))
+	    return REG_ESPACE;
+	  pstr->offsets = new_offsets;
+	}
+    }
+#endif /* RE_ENABLE_I18N  */
+  if (pstr->mbs_allocated)
+    {
+      unsigned char *new_mbs = re_realloc (pstr->mbs, unsigned char,
+					   new_buf_len);
+      if (BE (new_mbs == NULL, 0))
+	return REG_ESPACE;
+      pstr->mbs = new_mbs;
+    }
+  pstr->bufs_len = new_buf_len;
+  return REG_NOERROR;
+}
+
+
+static void
+internal_function
+re_string_construct_common (const char *str, int len, re_string_t *pstr,
+			    RE_TRANSLATE_TYPE trans, int icase,
+			    const re_dfa_t *dfa)
+{
+  pstr->raw_mbs = (const unsigned char *) str;
+  pstr->len = len;
+  pstr->raw_len = len;
+  pstr->trans = trans;
+  pstr->icase = icase ? 1 : 0;
+  pstr->mbs_allocated = (trans != NULL || icase);
+  pstr->mb_cur_max = dfa->mb_cur_max;
+  pstr->is_utf8 = dfa->is_utf8;
+  pstr->map_notascii = dfa->map_notascii;
+  pstr->stop = pstr->len;
+  pstr->raw_stop = pstr->stop;
+}
+
+#ifdef RE_ENABLE_I18N
+
+/* Build wide character buffer PSTR->WCS.
+   If the byte sequence of the string are:
+     <mb1>(0), <mb1>(1), <mb2>(0), <mb2>(1), <sb3>
+   Then wide character buffer will be:
+     <wc1>   , WEOF    , <wc2>   , WEOF    , <wc3>
+   We use WEOF for padding, they indicate that the position isn't
+   a first byte of a multibyte character.
+
+   Note that this function assumes PSTR->VALID_LEN elements are already
+   built and starts from PSTR->VALID_LEN.  */
+
+static void
+internal_function
+build_wcs_buffer (re_string_t *pstr)
+{
+#ifdef _LIBC
+  unsigned char buf[MB_LEN_MAX];
+  assert (MB_LEN_MAX >= pstr->mb_cur_max);
+#else
+  unsigned char buf[64];
+#endif
+  mbstate_t prev_st;
+  int byte_idx, end_idx, remain_len;
+  size_t mbclen;
+
+  /* Build the buffers from pstr->valid_len to either pstr->len or
+     pstr->bufs_len.  */
+  end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len;
+  for (byte_idx = pstr->valid_len; byte_idx < end_idx;)
+    {
+      wchar_t wc;
+      const char *p;
+
+      remain_len = end_idx - byte_idx;
+      prev_st = pstr->cur_state;
+      /* Apply the translation if we need.  */
+      if (BE (pstr->trans != NULL, 0))
+	{
+	  int i, ch;
+
+	  for (i = 0; i < pstr->mb_cur_max && i < remain_len; ++i)
+	    {
+	      ch = pstr->raw_mbs [pstr->raw_mbs_idx + byte_idx + i];
+	      buf[i] = pstr->mbs[byte_idx + i] = pstr->trans[ch];
+	    }
+	  p = (const char *) buf;
+	}
+      else
+	p = (const char *) pstr->raw_mbs + pstr->raw_mbs_idx + byte_idx;
+      mbclen = __mbrtowc (&wc, p, remain_len, &pstr->cur_state);
+      if (BE (mbclen == (size_t) -2, 0))
+	{
+	  /* The buffer doesn't have enough space, finish to build.  */
+	  pstr->cur_state = prev_st;
+	  break;
+	}
+      else if (BE (mbclen == (size_t) -1 || mbclen == 0, 0))
+	{
+	  /* We treat these cases as a singlebyte character.  */
+	  mbclen = 1;
+	  wc = (wchar_t) pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx];
+	  if (BE (pstr->trans != NULL, 0))
+	    wc = pstr->trans[wc];
+	  pstr->cur_state = prev_st;
+	}
+
+      /* Write wide character and padding.  */
+      pstr->wcs[byte_idx++] = wc;
+      /* Write paddings.  */
+      for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;)
+	pstr->wcs[byte_idx++] = WEOF;
+    }
+  pstr->valid_len = byte_idx;
+  pstr->valid_raw_len = byte_idx;
+}
+
+/* Build wide character buffer PSTR->WCS like build_wcs_buffer,
+   but for REG_ICASE.  */
+
+static reg_errcode_t
+internal_function
+build_wcs_upper_buffer (re_string_t *pstr)
+{
+  mbstate_t prev_st;
+  int src_idx, byte_idx, end_idx, remain_len;
+  size_t mbclen;
+#ifdef _LIBC
+  char buf[MB_LEN_MAX];
+  assert (MB_LEN_MAX >= pstr->mb_cur_max);
+#else
+  char buf[64];
+#endif
+
+  byte_idx = pstr->valid_len;
+  end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len;
+
+  /* The following optimization assumes that ASCII characters can be
+     mapped to wide characters with a simple cast.  */
+  if (! pstr->map_notascii && pstr->trans == NULL && !pstr->offsets_needed)
+    {
+      while (byte_idx < end_idx)
+	{
+	  wchar_t wc;
+
+	  if (isascii (pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx])
+	      && mbsinit (&pstr->cur_state))
+	    {
+	      /* In case of a singlebyte character.  */
+	      pstr->mbs[byte_idx]
+		= toupper (pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]);
+	      /* The next step uses the assumption that wchar_t is encoded
+		 ASCII-safe: all ASCII values can be converted like this.  */
+	      pstr->wcs[byte_idx] = (wchar_t) pstr->mbs[byte_idx];
+	      ++byte_idx;
+	      continue;
+	    }
+
+	  remain_len = end_idx - byte_idx;
+	  prev_st = pstr->cur_state;
+	  mbclen = __mbrtowc (&wc,
+			      ((const char *) pstr->raw_mbs + pstr->raw_mbs_idx
+			       + byte_idx), remain_len, &pstr->cur_state);
+	  if (BE (mbclen + 2 > 2, 1))
+	    {
+	      wchar_t wcu = wc;
+	      if (iswlower (wc))
+		{
+		  size_t mbcdlen;
+
+		  wcu = towupper (wc);
+		  mbcdlen = wcrtomb (buf, wcu, &prev_st);
+		  if (BE (mbclen == mbcdlen, 1))
+		    memcpy (pstr->mbs + byte_idx, buf, mbclen);
+		  else
+		    {
+		      src_idx = byte_idx;
+		      goto offsets_needed;
+		    }
+		}
+	      else
+		memcpy (pstr->mbs + byte_idx,
+			pstr->raw_mbs + pstr->raw_mbs_idx + byte_idx, mbclen);
+	      pstr->wcs[byte_idx++] = wcu;
+	      /* Write paddings.  */
+	      for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;)
+		pstr->wcs[byte_idx++] = WEOF;
+	    }
+	  else if (mbclen == (size_t) -1 || mbclen == 0)
+	    {
+	      /* It is an invalid character or '\0'.  Just use the byte.  */
+	      int ch = pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx];
+	      pstr->mbs[byte_idx] = ch;
+	      /* And also cast it to wide char.  */
+	      pstr->wcs[byte_idx++] = (wchar_t) ch;
+	      if (BE (mbclen == (size_t) -1, 0))
+		pstr->cur_state = prev_st;
+	    }
+	  else
+	    {
+	      /* The buffer doesn't have enough space, finish to build.  */
+	      pstr->cur_state = prev_st;
+	      break;
+	    }
+	}
+      pstr->valid_len = byte_idx;
+      pstr->valid_raw_len = byte_idx;
+      return REG_NOERROR;
+    }
+  else
+    for (src_idx = pstr->valid_raw_len; byte_idx < end_idx;)
+      {
+	wchar_t wc;
+	const char *p;
+      offsets_needed:
+	remain_len = end_idx - byte_idx;
+	prev_st = pstr->cur_state;
+	if (BE (pstr->trans != NULL, 0))
+	  {
+	    int i, ch;
+
+	    for (i = 0; i < pstr->mb_cur_max && i < remain_len; ++i)
+	      {
+		ch = pstr->raw_mbs [pstr->raw_mbs_idx + src_idx + i];
+		buf[i] = pstr->trans[ch];
+	      }
+	    p = (const char *) buf;
+	  }
+	else
+	  p = (const char *) pstr->raw_mbs + pstr->raw_mbs_idx + src_idx;
+	mbclen = __mbrtowc (&wc, p, remain_len, &pstr->cur_state);
+	if (BE (mbclen + 2 > 2, 1))
+	  {
+	    wchar_t wcu = wc;
+	    if (iswlower (wc))
+	      {
+		size_t mbcdlen;
+
+		wcu = towupper (wc);
+		mbcdlen = wcrtomb ((char *) buf, wcu, &prev_st);
+		if (BE (mbclen == mbcdlen, 1))
+		  memcpy (pstr->mbs + byte_idx, buf, mbclen);
+		else if (mbcdlen != (size_t) -1)
+		  {
+		    size_t i;
+
+		    if (byte_idx + mbcdlen > pstr->bufs_len)
+		      {
+			pstr->cur_state = prev_st;
+			break;
+		      }
+
+		    if (pstr->offsets == NULL)
+		      {
+			pstr->offsets = re_malloc (int, pstr->bufs_len);
+
+			if (pstr->offsets == NULL)
+			  return REG_ESPACE;
+		      }
+		    if (!pstr->offsets_needed)
+		      {
+			for (i = 0; i < (size_t) byte_idx; ++i)
+			  pstr->offsets[i] = i;
+			pstr->offsets_needed = 1;
+		      }
+
+		    memcpy (pstr->mbs + byte_idx, buf, mbcdlen);
+		    pstr->wcs[byte_idx] = wcu;
+		    pstr->offsets[byte_idx] = src_idx;
+		    for (i = 1; i < mbcdlen; ++i)
+		      {
+			pstr->offsets[byte_idx + i]
+			  = src_idx + (i < mbclen ? i : mbclen - 1);
+			pstr->wcs[byte_idx + i] = WEOF;
+		      }
+		    pstr->len += mbcdlen - mbclen;
+		    if (pstr->raw_stop > src_idx)
+		      pstr->stop += mbcdlen - mbclen;
+		    end_idx = (pstr->bufs_len > pstr->len)
+			      ? pstr->len : pstr->bufs_len;
+		    byte_idx += mbcdlen;
+		    src_idx += mbclen;
+		    continue;
+		  }
+		else
+		  memcpy (pstr->mbs + byte_idx, p, mbclen);
+	      }
+	    else
+	      memcpy (pstr->mbs + byte_idx, p, mbclen);
+
+	    if (BE (pstr->offsets_needed != 0, 0))
+	      {
+		size_t i;
+		for (i = 0; i < mbclen; ++i)
+		  pstr->offsets[byte_idx + i] = src_idx + i;
+	      }
+	    src_idx += mbclen;
+
+	    pstr->wcs[byte_idx++] = wcu;
+	    /* Write paddings.  */
+	    for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;)
+	      pstr->wcs[byte_idx++] = WEOF;
+	  }
+	else if (mbclen == (size_t) -1 || mbclen == 0)
+	  {
+	    /* It is an invalid character or '\0'.  Just use the byte.  */
+	    int ch = pstr->raw_mbs[pstr->raw_mbs_idx + src_idx];
+
+	    if (BE (pstr->trans != NULL, 0))
+	      ch = pstr->trans [ch];
+	    pstr->mbs[byte_idx] = ch;
+
+	    if (BE (pstr->offsets_needed != 0, 0))
+	      pstr->offsets[byte_idx] = src_idx;
+	    ++src_idx;
+
+	    /* And also cast it to wide char.  */
+	    pstr->wcs[byte_idx++] = (wchar_t) ch;
+	    if (BE (mbclen == (size_t) -1, 0))
+	      pstr->cur_state = prev_st;
+	  }
+	else
+	  {
+	    /* The buffer doesn't have enough space, finish to build.  */
+	    pstr->cur_state = prev_st;
+	    break;
+	  }
+      }
+  pstr->valid_len = byte_idx;
+  pstr->valid_raw_len = src_idx;
+  return REG_NOERROR;
+}
+
+/* Skip characters until the index becomes greater than NEW_RAW_IDX.
+   Return the index.  */
+
+static int
+internal_function
+re_string_skip_chars (re_string_t *pstr, int new_raw_idx, wint_t *last_wc)
+{
+  mbstate_t prev_st;
+  int rawbuf_idx;
+  size_t mbclen;
+  wint_t wc = WEOF;
+
+  /* Skip the characters which are not necessary to check.  */
+  for (rawbuf_idx = pstr->raw_mbs_idx + pstr->valid_raw_len;
+       rawbuf_idx < new_raw_idx;)
+    {
+      wchar_t wc2;
+      int remain_len = pstr->len - rawbuf_idx;
+      prev_st = pstr->cur_state;
+      mbclen = __mbrtowc (&wc2, (const char *) pstr->raw_mbs + rawbuf_idx,
+			  remain_len, &pstr->cur_state);
+      if (BE (mbclen == (size_t) -2 || mbclen == (size_t) -1 || mbclen == 0, 0))
+	{
+	  /* We treat these cases as a single byte character.  */
+	  if (mbclen == 0 || remain_len == 0)
+	    wc = L'\0';
+	  else
+	    wc = *(unsigned char *) (pstr->raw_mbs + rawbuf_idx);
+	  mbclen = 1;
+	  pstr->cur_state = prev_st;
+	}
+      else
+	wc = (wint_t) wc2;
+      /* Then proceed the next character.  */
+      rawbuf_idx += mbclen;
+    }
+  *last_wc = (wint_t) wc;
+  return rawbuf_idx;
+}
+#endif /* RE_ENABLE_I18N  */
+
+/* Build the buffer PSTR->MBS, and apply the translation if we need.
+   This function is used in case of REG_ICASE.  */
+
+static void
+internal_function
+build_upper_buffer (re_string_t *pstr)
+{
+  int char_idx, end_idx;
+  end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len;
+
+  for (char_idx = pstr->valid_len; char_idx < end_idx; ++char_idx)
+    {
+      int ch = pstr->raw_mbs[pstr->raw_mbs_idx + char_idx];
+      if (BE (pstr->trans != NULL, 0))
+	ch = pstr->trans[ch];
+      if (islower (ch))
+	pstr->mbs[char_idx] = toupper (ch);
+      else
+	pstr->mbs[char_idx] = ch;
+    }
+  pstr->valid_len = char_idx;
+  pstr->valid_raw_len = char_idx;
+}
+
+/* Apply TRANS to the buffer in PSTR.  */
+
+static void
+internal_function
+re_string_translate_buffer (re_string_t *pstr)
+{
+  int buf_idx, end_idx;
+  end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len;
+
+  for (buf_idx = pstr->valid_len; buf_idx < end_idx; ++buf_idx)
+    {
+      int ch = pstr->raw_mbs[pstr->raw_mbs_idx + buf_idx];
+      pstr->mbs[buf_idx] = pstr->trans[ch];
+    }
+
+  pstr->valid_len = buf_idx;
+  pstr->valid_raw_len = buf_idx;
+}
+
+/* This function re-construct the buffers.
+   Concretely, convert to wide character in case of pstr->mb_cur_max > 1,
+   convert to upper case in case of REG_ICASE, apply translation.  */
+
+static reg_errcode_t
+internal_function
+re_string_reconstruct (re_string_t *pstr, int idx, int eflags)
+{
+  int offset = idx - pstr->raw_mbs_idx;
+  if (BE (offset < 0, 0))
+    {
+      /* Reset buffer.  */
+#ifdef RE_ENABLE_I18N
+      if (pstr->mb_cur_max > 1)
+	memset (&pstr->cur_state, '\0', sizeof (mbstate_t));
+#endif /* RE_ENABLE_I18N */
+      pstr->len = pstr->raw_len;
+      pstr->stop = pstr->raw_stop;
+      pstr->valid_len = 0;
+      pstr->raw_mbs_idx = 0;
+      pstr->valid_raw_len = 0;
+      pstr->offsets_needed = 0;
+      pstr->tip_context = ((eflags & REG_NOTBOL) ? CONTEXT_BEGBUF
+			   : CONTEXT_NEWLINE | CONTEXT_BEGBUF);
+      if (!pstr->mbs_allocated)
+	pstr->mbs = (unsigned char *) pstr->raw_mbs;
+      offset = idx;
+    }
+
+  if (BE (offset != 0, 1))
+    {
+      /* Should the already checked characters be kept?  */
+      if (BE (offset < pstr->valid_raw_len, 1))
+	{
+	  /* Yes, move them to the front of the buffer.  */
+#ifdef RE_ENABLE_I18N
+	  if (BE (pstr->offsets_needed, 0))
+	    {
+	      int low = 0, high = pstr->valid_len, mid;
+	      do
+		{
+		  mid = (high + low) / 2;
+		  if (pstr->offsets[mid] > offset)
+		    high = mid;
+		  else if (pstr->offsets[mid] < offset)
+		    low = mid + 1;
+		  else
+		    break;
+		}
+	      while (low < high);
+	      if (pstr->offsets[mid] < offset)
+		++mid;
+	      pstr->tip_context = re_string_context_at (pstr, mid - 1,
+							eflags);
+	      /* This can be quite complicated, so handle specially
+		 only the common and easy case where the character with
+		 different length representation of lower and upper
+		 case is present at or after offset.  */
+	      if (pstr->valid_len > offset
+		  && mid == offset && pstr->offsets[mid] == offset)
+		{
+		  memmove (pstr->wcs, pstr->wcs + offset,
+			   (pstr->valid_len - offset) * sizeof (wint_t));
+		  memmove (pstr->mbs, pstr->mbs + offset, pstr->valid_len - offset);
+		  pstr->valid_len -= offset;
+		  pstr->valid_raw_len -= offset;
+		  for (low = 0; low < pstr->valid_len; low++)
+		    pstr->offsets[low] = pstr->offsets[low + offset] - offset;
+		}
+	      else
+		{
+		  /* Otherwise, just find out how long the partial multibyte
+		     character at offset is and fill it with WEOF/255.  */
+		  pstr->len = pstr->raw_len - idx + offset;
+		  pstr->stop = pstr->raw_stop - idx + offset;
+		  pstr->offsets_needed = 0;
+		  while (mid > 0 && pstr->offsets[mid - 1] == offset)
+		    --mid;
+		  while (mid < pstr->valid_len)
+		    if (pstr->wcs[mid] != WEOF)
+		      break;
+		    else
+		      ++mid;
+		  if (mid == pstr->valid_len)
+		    pstr->valid_len = 0;
+		  else
+		    {
+		      pstr->valid_len = pstr->offsets[mid] - offset;
+		      if (pstr->valid_len)
+			{
+			  for (low = 0; low < pstr->valid_len; ++low)
+			    pstr->wcs[low] = WEOF;
+			  memset (pstr->mbs, 255, pstr->valid_len);
+			}
+		    }
+		  pstr->valid_raw_len = pstr->valid_len;
+		}
+	    }
+	  else
+#endif
+	    {
+	      pstr->tip_context = re_string_context_at (pstr, offset - 1,
+							eflags);
+#ifdef RE_ENABLE_I18N
+	      if (pstr->mb_cur_max > 1)
+		memmove (pstr->wcs, pstr->wcs + offset,
+			 (pstr->valid_len - offset) * sizeof (wint_t));
+#endif /* RE_ENABLE_I18N */
+	      if (BE (pstr->mbs_allocated, 0))
+		memmove (pstr->mbs, pstr->mbs + offset,
+			 pstr->valid_len - offset);
+	      pstr->valid_len -= offset;
+	      pstr->valid_raw_len -= offset;
+#if DEBUG
+	      assert (pstr->valid_len > 0);
+#endif
+	    }
+	}
+      else
+	{
+#ifdef RE_ENABLE_I18N
+	  /* No, skip all characters until IDX.  */
+	  int prev_valid_len = pstr->valid_len;
+
+	  if (BE (pstr->offsets_needed, 0))
+	    {
+	      pstr->len = pstr->raw_len - idx + offset;
+	      pstr->stop = pstr->raw_stop - idx + offset;
+	      pstr->offsets_needed = 0;
+	    }
+#endif
+	  pstr->valid_len = 0;
+#ifdef RE_ENABLE_I18N
+	  if (pstr->mb_cur_max > 1)
+	    {
+	      int wcs_idx;
+	      wint_t wc = WEOF;
+
+	      if (pstr->is_utf8)
+		{
+		  const unsigned char *raw, *p, *end;
+
+		  /* Special case UTF-8.  Multi-byte chars start with any
+		     byte other than 0x80 - 0xbf.  */
+		  raw = pstr->raw_mbs + pstr->raw_mbs_idx;
+		  end = raw + (offset - pstr->mb_cur_max);
+		  if (end < pstr->raw_mbs)
+		    end = pstr->raw_mbs;
+		  p = raw + offset - 1;
+#ifdef _LIBC
+		  /* We know the wchar_t encoding is UCS4, so for the simple
+		     case, ASCII characters, skip the conversion step.  */
+		  if (isascii (*p) && BE (pstr->trans == NULL, 1))
+		    {
+		      memset (&pstr->cur_state, '\0', sizeof (mbstate_t));
+		      /* pstr->valid_len = 0; */
+		      wc = (wchar_t) *p;
+		    }
+		  else
+#endif
+		    for (; p >= end; --p)
+		      if ((*p & 0xc0) != 0x80)
+			{
+			  mbstate_t cur_state;
+			  wchar_t wc2;
+			  int mlen = raw + pstr->len - p;
+			  unsigned char buf[6];
+			  size_t mbclen;
+
+			  if (BE (pstr->trans != NULL, 0))
+			    {
+			      int i = mlen < 6 ? mlen : 6;
+			      while (--i >= 0)
+				buf[i] = pstr->trans[p[i]];
+			    }
+			  /* XXX Don't use mbrtowc, we know which conversion
+			     to use (UTF-8 -> UCS4).  */
+			  memset (&cur_state, 0, sizeof (cur_state));
+			  mbclen = __mbrtowc (&wc2, (const char *) p, mlen,
+					      &cur_state);
+			  if (raw + offset - p <= mbclen
+			      && mbclen < (size_t) -2)
+			    {
+			      memset (&pstr->cur_state, '\0',
+				      sizeof (mbstate_t));
+			      pstr->valid_len = mbclen - (raw + offset - p);
+			      wc = wc2;
+			    }
+			  break;
+			}
+		}
+
+	      if (wc == WEOF)
+		pstr->valid_len = re_string_skip_chars (pstr, idx, &wc) - idx;
+	      if (wc == WEOF)
+		pstr->tip_context
+		  = re_string_context_at (pstr, prev_valid_len - 1, eflags);
+	      else
+		pstr->tip_context = ((BE (pstr->word_ops_used != 0, 0)
+				      && IS_WIDE_WORD_CHAR (wc))
+				     ? CONTEXT_WORD
+				     : ((IS_WIDE_NEWLINE (wc)
+					 && pstr->newline_anchor)
+					? CONTEXT_NEWLINE : 0));
+	      if (BE (pstr->valid_len, 0))
+		{
+		  for (wcs_idx = 0; wcs_idx < pstr->valid_len; ++wcs_idx)
+		    pstr->wcs[wcs_idx] = WEOF;
+		  if (pstr->mbs_allocated)
+		    memset (pstr->mbs, 255, pstr->valid_len);
+		}
+	      pstr->valid_raw_len = pstr->valid_len;
+	    }
+	  else
+#endif /* RE_ENABLE_I18N */
+	    {
+	      int c = pstr->raw_mbs[pstr->raw_mbs_idx + offset - 1];
+	      pstr->valid_raw_len = 0;
+	      if (pstr->trans)
+		c = pstr->trans[c];
+	      pstr->tip_context = (bitset_contain (pstr->word_char, c)
+				   ? CONTEXT_WORD
+				   : ((IS_NEWLINE (c) && pstr->newline_anchor)
+				      ? CONTEXT_NEWLINE : 0));
+	    }
+	}
+      if (!BE (pstr->mbs_allocated, 0))
+	pstr->mbs += offset;
+    }
+  pstr->raw_mbs_idx = idx;
+  pstr->len -= offset;
+  pstr->stop -= offset;
+
+  /* Then build the buffers.  */
+#ifdef RE_ENABLE_I18N
+  if (pstr->mb_cur_max > 1)
+    {
+      if (pstr->icase)
+	{
+	  reg_errcode_t ret = build_wcs_upper_buffer (pstr);
+	  if (BE (ret != REG_NOERROR, 0))
+	    return ret;
+	}
+      else
+	build_wcs_buffer (pstr);
+    }
+  else
+#endif /* RE_ENABLE_I18N */
+    if (BE (pstr->mbs_allocated, 0))
+      {
+	if (pstr->icase)
+	  build_upper_buffer (pstr);
+	else if (pstr->trans != NULL)
+	  re_string_translate_buffer (pstr);
+      }
+    else
+      pstr->valid_len = pstr->len;
+
+  pstr->cur_idx = 0;
+  return REG_NOERROR;
+}
+
+static unsigned char
+internal_function __attribute ((pure))
+re_string_peek_byte_case (const re_string_t *pstr, int idx)
+{
+  int ch, off;
+
+  /* Handle the common (easiest) cases first.  */
+  if (BE (!pstr->mbs_allocated, 1))
+    return re_string_peek_byte (pstr, idx);
+
+#ifdef RE_ENABLE_I18N
+  if (pstr->mb_cur_max > 1
+      && ! re_string_is_single_byte_char (pstr, pstr->cur_idx + idx))
+    return re_string_peek_byte (pstr, idx);
+#endif
+
+  off = pstr->cur_idx + idx;
+#ifdef RE_ENABLE_I18N
+  if (pstr->offsets_needed)
+    off = pstr->offsets[off];
+#endif
+
+  ch = pstr->raw_mbs[pstr->raw_mbs_idx + off];
+
+#ifdef RE_ENABLE_I18N
+  /* Ensure that e.g. for tr_TR.UTF-8 BACKSLASH DOTLESS SMALL LETTER I
+     this function returns CAPITAL LETTER I instead of first byte of
+     DOTLESS SMALL LETTER I.  The latter would confuse the parser,
+     since peek_byte_case doesn't advance cur_idx in any way.  */
+  if (pstr->offsets_needed && !isascii (ch))
+    return re_string_peek_byte (pstr, idx);
+#endif
+
+  return ch;
+}
+
+static unsigned char
+internal_function __attribute ((pure))
+re_string_fetch_byte_case (re_string_t *pstr)
+{
+  if (BE (!pstr->mbs_allocated, 1))
+    return re_string_fetch_byte (pstr);
+
+#ifdef RE_ENABLE_I18N
+  if (pstr->offsets_needed)
+    {
+      int off, ch;
+
+      /* For tr_TR.UTF-8 [[:islower:]] there is
+	 [[: CAPITAL LETTER I WITH DOT lower:]] in mbs.  Skip
+	 in that case the whole multi-byte character and return
+	 the original letter.  On the other side, with
+	 [[: DOTLESS SMALL LETTER I return [[:I, as doing
+	 anything else would complicate things too much.  */
+
+      if (!re_string_first_byte (pstr, pstr->cur_idx))
+	return re_string_fetch_byte (pstr);
+
+      off = pstr->offsets[pstr->cur_idx];
+      ch = pstr->raw_mbs[pstr->raw_mbs_idx + off];
+
+      if (! isascii (ch))
+	return re_string_fetch_byte (pstr);
+
+      re_string_skip_bytes (pstr,
+			    re_string_char_size_at (pstr, pstr->cur_idx));
+      return ch;
+    }
+#endif
+
+  return pstr->raw_mbs[pstr->raw_mbs_idx + pstr->cur_idx++];
+}
+
+static void
+internal_function
+re_string_destruct (re_string_t *pstr)
+{
+#ifdef RE_ENABLE_I18N
+  re_free (pstr->wcs);
+  re_free (pstr->offsets);
+#endif /* RE_ENABLE_I18N  */
+  if (pstr->mbs_allocated)
+    re_free (pstr->mbs);
+}
+
+/* Return the context at IDX in INPUT.  */
+
+static unsigned int
+internal_function
+re_string_context_at (const re_string_t *input, int idx, int eflags)
+{
+  int c;
+  if (BE (idx < 0, 0))
+    /* In this case, we use the value stored in input->tip_context,
+       since we can't know the character in input->mbs[-1] here.  */
+    return input->tip_context;
+  if (BE (idx == input->len, 0))
+    return ((eflags & REG_NOTEOL) ? CONTEXT_ENDBUF
+	    : CONTEXT_NEWLINE | CONTEXT_ENDBUF);
+#ifdef RE_ENABLE_I18N
+  if (input->mb_cur_max > 1)
+    {
+      wint_t wc;
+      int wc_idx = idx;
+      while(input->wcs[wc_idx] == WEOF)
+	{
+#ifdef DEBUG
+	  /* It must not happen.  */
+	  assert (wc_idx >= 0);
+#endif
+	  --wc_idx;
+	  if (wc_idx < 0)
+	    return input->tip_context;
+	}
+      wc = input->wcs[wc_idx];
+      if (BE (input->word_ops_used != 0, 0) && IS_WIDE_WORD_CHAR (wc))
+	return CONTEXT_WORD;
+      return (IS_WIDE_NEWLINE (wc) && input->newline_anchor
+	      ? CONTEXT_NEWLINE : 0);
+    }
+  else
+#endif
+    {
+      c = re_string_byte_at (input, idx);
+      if (bitset_contain (input->word_char, c))
+	return CONTEXT_WORD;
+      return IS_NEWLINE (c) && input->newline_anchor ? CONTEXT_NEWLINE : 0;
+    }
+}
+
+/* Functions for set operation.  */
+
+static reg_errcode_t
+internal_function
+re_node_set_alloc (re_node_set *set, int size)
+{
+  /*
+   * ADR: valgrind says size can be 0, which then doesn't
+   * free the block of size 0.  Harumph. This seems
+   * to work ok, though.
+   */
+  if (size == 0)
+    {
+       memset(set, 0, sizeof(*set));
+       return REG_NOERROR;
+    }
+  set->alloc = size;
+  set->nelem = 0;
+  set->elems = re_malloc (int, size);
+  if (BE (set->elems == NULL, 0))
+    return REG_ESPACE;
+  return REG_NOERROR;
+}
+
+static reg_errcode_t
+internal_function
+re_node_set_init_1 (re_node_set *set, int elem)
+{
+  set->alloc = 1;
+  set->nelem = 1;
+  set->elems = re_malloc (int, 1);
+  if (BE (set->elems == NULL, 0))
+    {
+      set->alloc = set->nelem = 0;
+      return REG_ESPACE;
+    }
+  set->elems[0] = elem;
+  return REG_NOERROR;
+}
+
+static reg_errcode_t
+internal_function
+re_node_set_init_2 (re_node_set *set, int elem1, int elem2)
+{
+  set->alloc = 2;
+  set->elems = re_malloc (int, 2);
+  if (BE (set->elems == NULL, 0))
+    return REG_ESPACE;
+  if (elem1 == elem2)
+    {
+      set->nelem = 1;
+      set->elems[0] = elem1;
+    }
+  else
+    {
+      set->nelem = 2;
+      if (elem1 < elem2)
+	{
+	  set->elems[0] = elem1;
+	  set->elems[1] = elem2;
+	}
+      else
+	{
+	  set->elems[0] = elem2;
+	  set->elems[1] = elem1;
+	}
+    }
+  return REG_NOERROR;
+}
+
+static reg_errcode_t
+internal_function
+re_node_set_init_copy (re_node_set *dest, const re_node_set *src)
+{
+  dest->nelem = src->nelem;
+  if (src->nelem > 0)
+    {
+      dest->alloc = dest->nelem;
+      dest->elems = re_malloc (int, dest->alloc);
+      if (BE (dest->elems == NULL, 0))
+	{
+	  dest->alloc = dest->nelem = 0;
+	  return REG_ESPACE;
+	}
+      memcpy (dest->elems, src->elems, src->nelem * sizeof (int));
+    }
+  else
+    re_node_set_init_empty (dest);
+  return REG_NOERROR;
+}
+
+/* Calculate the intersection of the sets SRC1 and SRC2. And merge it to
+   DEST. Return value indicate the error code or REG_NOERROR if succeeded.
+   Note: We assume dest->elems is NULL, when dest->alloc is 0.  */
+
+static reg_errcode_t
+internal_function
+re_node_set_add_intersect (re_node_set *dest, const re_node_set *src1,
+			   const re_node_set *src2)
+{
+  int i1, i2, is, id, delta, sbase;
+  if (src1->nelem == 0 || src2->nelem == 0)
+    return REG_NOERROR;
+
+  /* We need dest->nelem + 2 * elems_in_intersection; this is a
+     conservative estimate.  */
+  if (src1->nelem + src2->nelem + dest->nelem > dest->alloc)
+    {
+      int new_alloc = src1->nelem + src2->nelem + dest->alloc;
+      int *new_elems = re_realloc (dest->elems, int, new_alloc);
+      if (BE (new_elems == NULL, 0))
+	return REG_ESPACE;
+      dest->elems = new_elems;
+      dest->alloc = new_alloc;
+    }
+
+  /* Find the items in the intersection of SRC1 and SRC2, and copy
+     into the top of DEST those that are not already in DEST itself.  */
+  sbase = dest->nelem + src1->nelem + src2->nelem;
+  i1 = src1->nelem - 1;
+  i2 = src2->nelem - 1;
+  id = dest->nelem - 1;
+  for (;;)
+    {
+      if (src1->elems[i1] == src2->elems[i2])
+	{
+	  /* Try to find the item in DEST.  Maybe we could binary search?  */
+	  while (id >= 0 && dest->elems[id] > src1->elems[i1])
+	    --id;
+
+	  if (id < 0 || dest->elems[id] != src1->elems[i1])
+	    dest->elems[--sbase] = src1->elems[i1];
+
+	  if (--i1 < 0 || --i2 < 0)
+	    break;
+	}
+
+      /* Lower the highest of the two items.  */
+      else if (src1->elems[i1] < src2->elems[i2])
+	{
+	  if (--i2 < 0)
+	    break;
+	}
+      else
+	{
+	  if (--i1 < 0)
+	    break;
+	}
+    }
+
+  id = dest->nelem - 1;
+  is = dest->nelem + src1->nelem + src2->nelem - 1;
+  delta = is - sbase + 1;
+
+  /* Now copy.  When DELTA becomes zero, the remaining
+     DEST elements are already in place; this is more or
+     less the same loop that is in re_node_set_merge.  */
+  dest->nelem += delta;
+  if (delta > 0 && id >= 0)
+    for (;;)
+      {
+	if (dest->elems[is] > dest->elems[id])
+	  {
+	    /* Copy from the top.  */
+	    dest->elems[id + delta--] = dest->elems[is--];
+	    if (delta == 0)
+	      break;
+	  }
+	else
+	  {
+	    /* Slide from the bottom.  */
+	    dest->elems[id + delta] = dest->elems[id];
+	    if (--id < 0)
+	      break;
+	  }
+      }
+
+  /* Copy remaining SRC elements.  */
+  memcpy (dest->elems, dest->elems + sbase, delta * sizeof (int));
+
+  return REG_NOERROR;
+}
+
+/* Calculate the union set of the sets SRC1 and SRC2. And store it to
+   DEST. Return value indicate the error code or REG_NOERROR if succeeded.  */
+
+static reg_errcode_t
+internal_function
+re_node_set_init_union (re_node_set *dest, const re_node_set *src1,
+			const re_node_set *src2)
+{
+  int i1, i2, id;
+  if (src1 != NULL && src1->nelem > 0 && src2 != NULL && src2->nelem > 0)
+    {
+      dest->alloc = src1->nelem + src2->nelem;
+      dest->elems = re_malloc (int, dest->alloc);
+      if (BE (dest->elems == NULL, 0))
+	return REG_ESPACE;
+    }
+  else
+    {
+      if (src1 != NULL && src1->nelem > 0)
+	return re_node_set_init_copy (dest, src1);
+      else if (src2 != NULL && src2->nelem > 0)
+	return re_node_set_init_copy (dest, src2);
+      else
+	re_node_set_init_empty (dest);
+      return REG_NOERROR;
+    }
+  for (i1 = i2 = id = 0 ; i1 < src1->nelem && i2 < src2->nelem ;)
+    {
+      if (src1->elems[i1] > src2->elems[i2])
+	{
+	  dest->elems[id++] = src2->elems[i2++];
+	  continue;
+	}
+      if (src1->elems[i1] == src2->elems[i2])
+	++i2;
+      dest->elems[id++] = src1->elems[i1++];
+    }
+  if (i1 < src1->nelem)
+    {
+      memcpy (dest->elems + id, src1->elems + i1,
+	     (src1->nelem - i1) * sizeof (int));
+      id += src1->nelem - i1;
+    }
+  else if (i2 < src2->nelem)
+    {
+      memcpy (dest->elems + id, src2->elems + i2,
+	     (src2->nelem - i2) * sizeof (int));
+      id += src2->nelem - i2;
+    }
+  dest->nelem = id;
+  return REG_NOERROR;
+}
+
+/* Calculate the union set of the sets DEST and SRC. And store it to
+   DEST. Return value indicate the error code or REG_NOERROR if succeeded.  */
+
+static reg_errcode_t
+internal_function
+re_node_set_merge (re_node_set *dest, const re_node_set *src)
+{
+  int is, id, sbase, delta;
+  if (src == NULL || src->nelem == 0)
+    return REG_NOERROR;
+  if (dest->alloc < 2 * src->nelem + dest->nelem)
+    {
+      int new_alloc = 2 * (src->nelem + dest->alloc);
+      int *new_buffer = re_realloc (dest->elems, int, new_alloc);
+      if (BE (new_buffer == NULL, 0))
+	return REG_ESPACE;
+      dest->elems = new_buffer;
+      dest->alloc = new_alloc;
+    }
+
+  if (BE (dest->nelem == 0, 0))
+    {
+      dest->nelem = src->nelem;
+      memcpy (dest->elems, src->elems, src->nelem * sizeof (int));
+      return REG_NOERROR;
+    }
+
+  /* Copy into the top of DEST the items of SRC that are not
+     found in DEST.  Maybe we could binary search in DEST?  */
+  for (sbase = dest->nelem + 2 * src->nelem,
+       is = src->nelem - 1, id = dest->nelem - 1; is >= 0 && id >= 0; )
+    {
+      if (dest->elems[id] == src->elems[is])
+	is--, id--;
+      else if (dest->elems[id] < src->elems[is])
+	dest->elems[--sbase] = src->elems[is--];
+      else /* if (dest->elems[id] > src->elems[is]) */
+	--id;
+    }
+
+  if (is >= 0)
+    {
+      /* If DEST is exhausted, the remaining items of SRC must be unique.  */
+      sbase -= is + 1;
+      memcpy (dest->elems + sbase, src->elems, (is + 1) * sizeof (int));
+    }
+
+  id = dest->nelem - 1;
+  is = dest->nelem + 2 * src->nelem - 1;
+  delta = is - sbase + 1;
+  if (delta == 0)
+    return REG_NOERROR;
+
+  /* Now copy.  When DELTA becomes zero, the remaining
+     DEST elements are already in place.  */
+  dest->nelem += delta;
+  for (;;)
+    {
+      if (dest->elems[is] > dest->elems[id])
+	{
+	  /* Copy from the top.  */
+	  dest->elems[id + delta--] = dest->elems[is--];
+	  if (delta == 0)
+	    break;
+	}
+      else
+	{
+	  /* Slide from the bottom.  */
+	  dest->elems[id + delta] = dest->elems[id];
+	  if (--id < 0)
+	    {
+	      /* Copy remaining SRC elements.  */
+	      memcpy (dest->elems, dest->elems + sbase,
+		      delta * sizeof (int));
+	      break;
+	    }
+	}
+    }
+
+  return REG_NOERROR;
+}
+
+/* Insert the new element ELEM to the re_node_set* SET.
+   SET should not already have ELEM.
+   return -1 if an error is occured, return 1 otherwise.  */
+
+static int
+internal_function
+re_node_set_insert (re_node_set *set, int elem)
+{
+  int idx;
+  /* In case the set is empty.  */
+  if (set->alloc == 0)
+    {
+      if (BE (re_node_set_init_1 (set, elem) == REG_NOERROR, 1))
+	return 1;
+      else
+	return -1;
+    }
+
+  if (BE (set->nelem, 0) == 0)
+    {
+      /* We already guaranteed above that set->alloc != 0.  */
+      set->elems[0] = elem;
+      ++set->nelem;
+      return 1;
+    }
+
+  /* Realloc if we need.  */
+  if (set->alloc == set->nelem)
+    {
+      int *new_elems;
+      set->alloc = set->alloc * 2;
+      new_elems = re_realloc (set->elems, int, set->alloc);
+      if (BE (new_elems == NULL, 0))
+	return -1;
+      set->elems = new_elems;
+    }
+
+  /* Move the elements which follows the new element.  Test the
+     first element separately to skip a check in the inner loop.  */
+  if (elem < set->elems[0])
+    {
+      idx = 0;
+      for (idx = set->nelem; idx > 0; idx--)
+	set->elems[idx] = set->elems[idx - 1];
+    }
+  else
+    {
+      for (idx = set->nelem; set->elems[idx - 1] > elem; idx--)
+	set->elems[idx] = set->elems[idx - 1];
+    }
+
+  /* Insert the new element.  */
+  set->elems[idx] = elem;
+  ++set->nelem;
+  return 1;
+}
+
+/* Insert the new element ELEM to the re_node_set* SET.
+   SET should not already have any element greater than or equal to ELEM.
+   Return -1 if an error is occured, return 1 otherwise.  */
+
+static int
+internal_function
+re_node_set_insert_last (re_node_set *set, int elem)
+{
+  /* Realloc if we need.  */
+  if (set->alloc == set->nelem)
+    {
+      int *new_elems;
+      set->alloc = (set->alloc + 1) * 2;
+      new_elems = re_realloc (set->elems, int, set->alloc);
+      if (BE (new_elems == NULL, 0))
+	return -1;
+      set->elems = new_elems;
+    }
+
+  /* Insert the new element.  */
+  set->elems[set->nelem++] = elem;
+  return 1;
+}
+
+/* Compare two node sets SET1 and SET2.
+   return 1 if SET1 and SET2 are equivalent, return 0 otherwise.  */
+
+static int
+internal_function __attribute ((pure))
+re_node_set_compare (const re_node_set *set1, const re_node_set *set2)
+{
+  int i;
+  if (set1 == NULL || set2 == NULL || set1->nelem != set2->nelem)
+    return 0;
+  for (i = set1->nelem ; --i >= 0 ; )
+    if (set1->elems[i] != set2->elems[i])
+      return 0;
+  return 1;
+}
+
+/* Return (idx + 1) if SET contains the element ELEM, return 0 otherwise.  */
+
+static int
+internal_function __attribute ((pure))
+re_node_set_contains (const re_node_set *set, int elem)
+{
+  unsigned int idx, right, mid;
+  if (set->nelem <= 0)
+    return 0;
+
+  /* Binary search the element.  */
+  idx = 0;
+  right = set->nelem - 1;
+  while (idx < right)
+    {
+      mid = (idx + right) / 2;
+      if (set->elems[mid] < elem)
+	idx = mid + 1;
+      else
+	right = mid;
+    }
+  return set->elems[idx] == elem ? idx + 1 : 0;
+}
+
+static void
+internal_function
+re_node_set_remove_at (re_node_set *set, int idx)
+{
+  if (idx < 0 || idx >= set->nelem)
+    return;
+  --set->nelem;
+  for (; idx < set->nelem; idx++)
+    set->elems[idx] = set->elems[idx + 1];
+}
+
+
+/* Add the token TOKEN to dfa->nodes, and return the index of the token.
+   Or return -1, if an error will be occured.  */
+
+static int
+internal_function
+re_dfa_add_node (re_dfa_t *dfa, re_token_t token)
+{
+  if (BE (dfa->nodes_len >= dfa->nodes_alloc, 0))
+    {
+      size_t new_nodes_alloc = dfa->nodes_alloc * 2;
+      int *new_nexts, *new_indices;
+      re_node_set *new_edests, *new_eclosures;
+      re_token_t *new_nodes;
+
+      /* Avoid overflows in realloc.  */
+      const size_t max_object_size = MAX (sizeof (re_token_t),
+					  MAX (sizeof (re_node_set),
+					       sizeof (int)));
+      if (BE (SIZE_MAX / max_object_size < new_nodes_alloc, 0))
+	return -1;
+
+      new_nodes = re_realloc (dfa->nodes, re_token_t, new_nodes_alloc);
+      if (BE (new_nodes == NULL, 0))
+	return -1;
+      dfa->nodes = new_nodes;
+      new_nexts = re_realloc (dfa->nexts, int, new_nodes_alloc);
+      new_indices = re_realloc (dfa->org_indices, int, new_nodes_alloc);
+      new_edests = re_realloc (dfa->edests, re_node_set, new_nodes_alloc);
+      new_eclosures = re_realloc (dfa->eclosures, re_node_set, new_nodes_alloc);
+      if (BE (new_nexts == NULL || new_indices == NULL
+	      || new_edests == NULL || new_eclosures == NULL, 0))
+	return -1;
+      dfa->nexts = new_nexts;
+      dfa->org_indices = new_indices;
+      dfa->edests = new_edests;
+      dfa->eclosures = new_eclosures;
+      dfa->nodes_alloc = new_nodes_alloc;
+    }
+  dfa->nodes[dfa->nodes_len] = token;
+  dfa->nodes[dfa->nodes_len].constraint = 0;
+#ifdef RE_ENABLE_I18N
+  dfa->nodes[dfa->nodes_len].accept_mb =
+    (token.type == OP_PERIOD && dfa->mb_cur_max > 1) || token.type == COMPLEX_BRACKET;
+#endif
+  dfa->nexts[dfa->nodes_len] = -1;
+  re_node_set_init_empty (dfa->edests + dfa->nodes_len);
+  re_node_set_init_empty (dfa->eclosures + dfa->nodes_len);
+  return dfa->nodes_len++;
+}
+
+static inline unsigned int
+internal_function
+calc_state_hash (const re_node_set *nodes, unsigned int context)
+{
+  unsigned int hash = nodes->nelem + context;
+  int i;
+  for (i = 0 ; i < nodes->nelem ; i++)
+    hash += nodes->elems[i];
+  return hash;
+}
+
+/* Search for the state whose node_set is equivalent to NODES.
+   Return the pointer to the state, if we found it in the DFA.
+   Otherwise create the new one and return it.  In case of an error
+   return NULL and set the error code in ERR.
+   Note: - We assume NULL as the invalid state, then it is possible that
+	   return value is NULL and ERR is REG_NOERROR.
+	 - We never return non-NULL value in case of any errors, it is for
+	   optimization.  */
+
+static re_dfastate_t *
+internal_function
+re_acquire_state (reg_errcode_t *err, const re_dfa_t *dfa,
+		  const re_node_set *nodes)
+{
+  unsigned int hash;
+  re_dfastate_t *new_state;
+  struct re_state_table_entry *spot;
+  int i;
+  if (BE (nodes->nelem == 0, 0))
+    {
+      *err = REG_NOERROR;
+      return NULL;
+    }
+  hash = calc_state_hash (nodes, 0);
+  spot = dfa->state_table + (hash & dfa->state_hash_mask);
+
+  for (i = 0 ; i < spot->num ; i++)
+    {
+      re_dfastate_t *state = spot->array[i];
+      if (hash != state->hash)
+	continue;
+      if (re_node_set_compare (&state->nodes, nodes))
+	return state;
+    }
+
+  /* There are no appropriate state in the dfa, create the new one.  */
+  new_state = create_ci_newstate (dfa, nodes, hash);
+  if (BE (new_state == NULL, 0))
+    *err = REG_ESPACE;
+
+  return new_state;
+}
+
+/* Search for the state whose node_set is equivalent to NODES and
+   whose context is equivalent to CONTEXT.
+   Return the pointer to the state, if we found it in the DFA.
+   Otherwise create the new one and return it.  In case of an error
+   return NULL and set the error code in ERR.
+   Note: - We assume NULL as the invalid state, then it is possible that
+	   return value is NULL and ERR is REG_NOERROR.
+	 - We never return non-NULL value in case of any errors, it is for
+	   optimization.  */
+
+static re_dfastate_t *
+internal_function
+re_acquire_state_context (reg_errcode_t *err, const re_dfa_t *dfa,
+			  const re_node_set *nodes, unsigned int context)
+{
+  unsigned int hash;
+  re_dfastate_t *new_state;
+  struct re_state_table_entry *spot;
+  int i;
+  if (nodes->nelem == 0)
+    {
+      *err = REG_NOERROR;
+      return NULL;
+    }
+  hash = calc_state_hash (nodes, context);
+  spot = dfa->state_table + (hash & dfa->state_hash_mask);
+
+  for (i = 0 ; i < spot->num ; i++)
+    {
+      re_dfastate_t *state = spot->array[i];
+      if (state->hash == hash
+	  && state->context == context
+	  && re_node_set_compare (state->entrance_nodes, nodes))
+	return state;
+    }
+  /* There are no appropriate state in `dfa', create the new one.  */
+  new_state = create_cd_newstate (dfa, nodes, context, hash);
+  if (BE (new_state == NULL, 0))
+    *err = REG_ESPACE;
+
+  return new_state;
+}
+
+/* Finish initialization of the new state NEWSTATE, and using its hash value
+   HASH put in the appropriate bucket of DFA's state table.  Return value
+   indicates the error code if failed.  */
+
+static reg_errcode_t
+register_state (const re_dfa_t *dfa, re_dfastate_t *newstate,
+		unsigned int hash)
+{
+  struct re_state_table_entry *spot;
+  reg_errcode_t err;
+  int i;
+
+  newstate->hash = hash;
+  err = re_node_set_alloc (&newstate->non_eps_nodes, newstate->nodes.nelem);
+  if (BE (err != REG_NOERROR, 0))
+    return REG_ESPACE;
+  for (i = 0; i < newstate->nodes.nelem; i++)
+    {
+      int elem = newstate->nodes.elems[i];
+      if (!IS_EPSILON_NODE (dfa->nodes[elem].type))
+	if (re_node_set_insert_last (&newstate->non_eps_nodes, elem) < 0)
+	  return REG_ESPACE;
+    }
+
+  spot = dfa->state_table + (hash & dfa->state_hash_mask);
+  if (BE (spot->alloc <= spot->num, 0))
+    {
+      int new_alloc = 2 * spot->num + 2;
+      re_dfastate_t **new_array = re_realloc (spot->array, re_dfastate_t *,
+					      new_alloc);
+      if (BE (new_array == NULL, 0))
+	return REG_ESPACE;
+      spot->array = new_array;
+      spot->alloc = new_alloc;
+    }
+  spot->array[spot->num++] = newstate;
+  return REG_NOERROR;
+}
+
+static void
+free_state (re_dfastate_t *state)
+{
+  re_node_set_free (&state->non_eps_nodes);
+  re_node_set_free (&state->inveclosure);
+  if (state->entrance_nodes != &state->nodes)
+    {
+      re_node_set_free (state->entrance_nodes);
+      re_free (state->entrance_nodes);
+    }
+  re_node_set_free (&state->nodes);
+  re_free (state->word_trtable);
+  re_free (state->trtable);
+  re_free (state);
+}
+
+/* Create the new state which is independ of contexts.
+   Return the new state if succeeded, otherwise return NULL.  */
+
+static re_dfastate_t *
+internal_function
+create_ci_newstate (const re_dfa_t *dfa, const re_node_set *nodes,
+		    unsigned int hash)
+{
+  int i;
+  reg_errcode_t err;
+  re_dfastate_t *newstate;
+
+  newstate = (re_dfastate_t *) calloc (sizeof (re_dfastate_t), 1);
+  if (BE (newstate == NULL, 0))
+    return NULL;
+  err = re_node_set_init_copy (&newstate->nodes, nodes);
+  if (BE (err != REG_NOERROR, 0))
+    {
+      re_free (newstate);
+      return NULL;
+    }
+
+  newstate->entrance_nodes = &newstate->nodes;
+  for (i = 0 ; i < nodes->nelem ; i++)
+    {
+      re_token_t *node = dfa->nodes + nodes->elems[i];
+      re_token_type_t type = node->type;
+      if (type == CHARACTER && !node->constraint)
+	continue;
+#ifdef RE_ENABLE_I18N
+      newstate->accept_mb |= node->accept_mb;
+#endif /* RE_ENABLE_I18N */
+
+      /* If the state has the halt node, the state is a halt state.  */
+      if (type == END_OF_RE)
+	newstate->halt = 1;
+      else if (type == OP_BACK_REF)
+	newstate->has_backref = 1;
+      else if (type == ANCHOR || node->constraint)
+	newstate->has_constraint = 1;
+    }
+  err = register_state (dfa, newstate, hash);
+  if (BE (err != REG_NOERROR, 0))
+    {
+      free_state (newstate);
+      newstate = NULL;
+    }
+  return newstate;
+}
+
+/* Create the new state which is depend on the context CONTEXT.
+   Return the new state if succeeded, otherwise return NULL.  */
+
+static re_dfastate_t *
+internal_function
+create_cd_newstate (const re_dfa_t *dfa, const re_node_set *nodes,
+		    unsigned int context, unsigned int hash)
+{
+  int i, nctx_nodes = 0;
+  reg_errcode_t err;
+  re_dfastate_t *newstate;
+
+  newstate = (re_dfastate_t *) calloc (sizeof (re_dfastate_t), 1);
+  if (BE (newstate == NULL, 0))
+    return NULL;
+  err = re_node_set_init_copy (&newstate->nodes, nodes);
+  if (BE (err != REG_NOERROR, 0))
+    {
+      re_free (newstate);
+      return NULL;
+    }
+
+  newstate->context = context;
+  newstate->entrance_nodes = &newstate->nodes;
+
+  for (i = 0 ; i < nodes->nelem ; i++)
+    {
+      re_token_t *node = dfa->nodes + nodes->elems[i];
+      re_token_type_t type = node->type;
+      unsigned int constraint = node->constraint;
+
+      if (type == CHARACTER && !constraint)
+	continue;
+#ifdef RE_ENABLE_I18N
+      newstate->accept_mb |= node->accept_mb;
+#endif /* RE_ENABLE_I18N */
+
+      /* If the state has the halt node, the state is a halt state.  */
+      if (type == END_OF_RE)
+	newstate->halt = 1;
+      else if (type == OP_BACK_REF)
+	newstate->has_backref = 1;
+
+      if (constraint)
+	{
+	  if (newstate->entrance_nodes == &newstate->nodes)
+	    {
+	      newstate->entrance_nodes = re_malloc (re_node_set, 1);
+	      if (BE (newstate->entrance_nodes == NULL, 0))
+		{
+		  free_state (newstate);
+		  return NULL;
+		}
+	      if (re_node_set_init_copy (newstate->entrance_nodes, nodes)
+		  != REG_NOERROR)
+		return NULL;
+	      nctx_nodes = 0;
+	      newstate->has_constraint = 1;
+	    }
+
+	  if (NOT_SATISFY_PREV_CONSTRAINT (constraint,context))
+	    {
+	      re_node_set_remove_at (&newstate->nodes, i - nctx_nodes);
+	      ++nctx_nodes;
+	    }
+	}
+    }
+  err = register_state (dfa, newstate, hash);
+  if (BE (err != REG_NOERROR, 0))
+    {
+      free_state (newstate);
+      newstate = NULL;
+    }
+  return  newstate;
+}
diff --git a/compat/regex/regex_internal.h b/compat/regex/regex_internal.h
new file mode 100644
index 0000000..4184d7f
--- /dev/null
+++ b/compat/regex/regex_internal.h
@@ -0,0 +1,810 @@
+/* Extended regular expression matching and search library.
+   Copyright (C) 2002-2005, 2007, 2008, 2010 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#ifndef _REGEX_INTERNAL_H
+#define _REGEX_INTERNAL_H 1
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if defined HAVE_LANGINFO_H || defined HAVE_LANGINFO_CODESET || defined _LIBC
+# include <langinfo.h>
+#endif
+#if defined HAVE_LOCALE_H || defined _LIBC
+# include <locale.h>
+#endif
+#if defined HAVE_WCHAR_H || defined _LIBC
+# include <wchar.h>
+#endif /* HAVE_WCHAR_H || _LIBC */
+#if defined HAVE_WCTYPE_H || defined _LIBC
+# include <wctype.h>
+#endif /* HAVE_WCTYPE_H || _LIBC */
+#if defined HAVE_STDBOOL_H || defined _LIBC
+# include <stdbool.h>
+#endif /* HAVE_STDBOOL_H || _LIBC */
+#if !defined(ZOS_USS)
+#if defined HAVE_STDINT_H || defined _LIBC
+# include <stdint.h>
+#endif /* HAVE_STDINT_H || _LIBC */
+#endif /* !ZOS_USS */
+#if defined _LIBC
+# include <bits/libc-lock.h>
+#else
+# define __libc_lock_define(CLASS,NAME)
+# define __libc_lock_init(NAME) do { } while (0)
+# define __libc_lock_lock(NAME) do { } while (0)
+# define __libc_lock_unlock(NAME) do { } while (0)
+#endif
+
+#ifndef GAWK
+/* In case that the system doesn't have isblank().  */
+#if !defined _LIBC && !defined HAVE_ISBLANK && !defined isblank
+# define isblank(ch) ((ch) == ' ' || (ch) == '\t')
+#endif
+#else /* GAWK */
+/*
+ * This is a freaking mess. On glibc systems you have to define
+ * a magic constant to get isblank() out of <ctype.h>, since it's
+ * a C99 function.  To heck with all that and borrow a page from
+ * dfa.c's book.
+ */
+
+static int
+is_blank (int c)
+{
+   return (c == ' ' || c == '\t');
+}
+#endif /* GAWK */
+
+#ifdef _LIBC
+# ifndef _RE_DEFINE_LOCALE_FUNCTIONS
+#  define _RE_DEFINE_LOCALE_FUNCTIONS 1
+#   include <locale/localeinfo.h>
+#   include <locale/elem-hash.h>
+#   include <locale/coll-lookup.h>
+# endif
+#endif
+
+/* This is for other GNU distributions with internationalized messages.  */
+#if (HAVE_LIBINTL_H && ENABLE_NLS) || defined _LIBC
+# include <libintl.h>
+# ifdef _LIBC
+#  undef gettext
+#  define gettext(msgid) \
+  INTUSE(__dcgettext) (_libc_intl_domainname, msgid, LC_MESSAGES)
+# endif
+#else
+# define gettext(msgid) (msgid)
+#endif
+
+#ifndef gettext_noop
+/* This define is so xgettext can find the internationalizable
+   strings.  */
+# define gettext_noop(String) String
+#endif
+
+/* For loser systems without the definition.  */
+#ifndef SIZE_MAX
+# define SIZE_MAX ((size_t) -1)
+#endif
+
+#ifndef NO_MBSUPPORT
+#include "mbsupport.h" /* gawk */
+#endif
+#ifndef MB_CUR_MAX
+#define MB_CUR_MAX 1
+#endif
+
+#if (defined MBS_SUPPORT) || _LIBC
+# define RE_ENABLE_I18N
+#endif
+
+#if __GNUC__ >= 3
+# define BE(expr, val) __builtin_expect (expr, val)
+#else
+# define BE(expr, val) (expr)
+# ifdef inline
+# undef inline
+# endif
+# define inline
+#endif
+
+/* Number of single byte character.  */
+#define SBC_MAX 256
+
+#define COLL_ELEM_LEN_MAX 8
+
+/* The character which represents newline.  */
+#define NEWLINE_CHAR '\n'
+#define WIDE_NEWLINE_CHAR L'\n'
+
+/* Rename to standard API for using out of glibc.  */
+#ifndef _LIBC
+# ifdef __wctype
+# undef __wctype
+# endif
+# define __wctype wctype
+# ifdef __iswctype
+# undef __iswctype
+# endif
+# define __iswctype iswctype
+# define __btowc btowc
+# define __mbrtowc mbrtowc
+#undef __mempcpy	/* GAWK */
+# define __mempcpy mempcpy
+# define __wcrtomb wcrtomb
+# define __regfree regfree
+# define attribute_hidden
+#endif /* not _LIBC */
+
+#ifdef __GNUC__
+# define __attribute(arg) __attribute__ (arg)
+#else
+# define __attribute(arg)
+#endif
+
+extern const char __re_error_msgid[] attribute_hidden;
+extern const size_t __re_error_msgid_idx[] attribute_hidden;
+
+/* An integer used to represent a set of bits.  It must be unsigned,
+   and must be at least as wide as unsigned int.  */
+typedef unsigned long int bitset_word_t;
+/* All bits set in a bitset_word_t.  */
+#define BITSET_WORD_MAX ULONG_MAX
+/* Number of bits in a bitset_word_t.  */
+#define BITSET_WORD_BITS (sizeof (bitset_word_t) * CHAR_BIT)
+/* Number of bitset_word_t in a bit_set.  */
+#define BITSET_WORDS (SBC_MAX / BITSET_WORD_BITS)
+typedef bitset_word_t bitset_t[BITSET_WORDS];
+typedef bitset_word_t *re_bitset_ptr_t;
+typedef const bitset_word_t *re_const_bitset_ptr_t;
+
+#define bitset_set(set,i) \
+  (set[i / BITSET_WORD_BITS] |= (bitset_word_t) 1 << i % BITSET_WORD_BITS)
+#define bitset_clear(set,i) \
+  (set[i / BITSET_WORD_BITS] &= ~((bitset_word_t) 1 << i % BITSET_WORD_BITS))
+#define bitset_contain(set,i) \
+  (set[i / BITSET_WORD_BITS] & ((bitset_word_t) 1 << i % BITSET_WORD_BITS))
+#define bitset_empty(set) memset (set, '\0', sizeof (bitset_t))
+#define bitset_set_all(set) memset (set, '\xff', sizeof (bitset_t))
+#define bitset_copy(dest,src) memcpy (dest, src, sizeof (bitset_t))
+
+#define PREV_WORD_CONSTRAINT 0x0001
+#define PREV_NOTWORD_CONSTRAINT 0x0002
+#define NEXT_WORD_CONSTRAINT 0x0004
+#define NEXT_NOTWORD_CONSTRAINT 0x0008
+#define PREV_NEWLINE_CONSTRAINT 0x0010
+#define NEXT_NEWLINE_CONSTRAINT 0x0020
+#define PREV_BEGBUF_CONSTRAINT 0x0040
+#define NEXT_ENDBUF_CONSTRAINT 0x0080
+#define WORD_DELIM_CONSTRAINT 0x0100
+#define NOT_WORD_DELIM_CONSTRAINT 0x0200
+
+typedef enum
+{
+  INSIDE_WORD = PREV_WORD_CONSTRAINT | NEXT_WORD_CONSTRAINT,
+  WORD_FIRST = PREV_NOTWORD_CONSTRAINT | NEXT_WORD_CONSTRAINT,
+  WORD_LAST = PREV_WORD_CONSTRAINT | NEXT_NOTWORD_CONSTRAINT,
+  INSIDE_NOTWORD = PREV_NOTWORD_CONSTRAINT | NEXT_NOTWORD_CONSTRAINT,
+  LINE_FIRST = PREV_NEWLINE_CONSTRAINT,
+  LINE_LAST = NEXT_NEWLINE_CONSTRAINT,
+  BUF_FIRST = PREV_BEGBUF_CONSTRAINT,
+  BUF_LAST = NEXT_ENDBUF_CONSTRAINT,
+  WORD_DELIM = WORD_DELIM_CONSTRAINT,
+  NOT_WORD_DELIM = NOT_WORD_DELIM_CONSTRAINT
+} re_context_type;
+
+typedef struct
+{
+  int alloc;
+  int nelem;
+  int *elems;
+} re_node_set;
+
+typedef enum
+{
+  NON_TYPE = 0,
+
+  /* Node type, These are used by token, node, tree.  */
+  CHARACTER = 1,
+  END_OF_RE = 2,
+  SIMPLE_BRACKET = 3,
+  OP_BACK_REF = 4,
+  OP_PERIOD = 5,
+#ifdef RE_ENABLE_I18N
+  COMPLEX_BRACKET = 6,
+  OP_UTF8_PERIOD = 7,
+#endif /* RE_ENABLE_I18N */
+
+  /* We define EPSILON_BIT as a macro so that OP_OPEN_SUBEXP is used
+     when the debugger shows values of this enum type.  */
+#define EPSILON_BIT 8
+  OP_OPEN_SUBEXP = EPSILON_BIT | 0,
+  OP_CLOSE_SUBEXP = EPSILON_BIT | 1,
+  OP_ALT = EPSILON_BIT | 2,
+  OP_DUP_ASTERISK = EPSILON_BIT | 3,
+  ANCHOR = EPSILON_BIT | 4,
+
+  /* Tree type, these are used only by tree. */
+  CONCAT = 16,
+  SUBEXP = 17,
+
+  /* Token type, these are used only by token.  */
+  OP_DUP_PLUS = 18,
+  OP_DUP_QUESTION,
+  OP_OPEN_BRACKET,
+  OP_CLOSE_BRACKET,
+  OP_CHARSET_RANGE,
+  OP_OPEN_DUP_NUM,
+  OP_CLOSE_DUP_NUM,
+  OP_NON_MATCH_LIST,
+  OP_OPEN_COLL_ELEM,
+  OP_CLOSE_COLL_ELEM,
+  OP_OPEN_EQUIV_CLASS,
+  OP_CLOSE_EQUIV_CLASS,
+  OP_OPEN_CHAR_CLASS,
+  OP_CLOSE_CHAR_CLASS,
+  OP_WORD,
+  OP_NOTWORD,
+  OP_SPACE,
+  OP_NOTSPACE,
+  BACK_SLASH
+
+} re_token_type_t;
+
+#ifdef RE_ENABLE_I18N
+typedef struct
+{
+  /* Multibyte characters.  */
+  wchar_t *mbchars;
+
+  /* Collating symbols.  */
+# ifdef _LIBC
+  int32_t *coll_syms;
+# endif
+
+  /* Equivalence classes. */
+# ifdef _LIBC
+  int32_t *equiv_classes;
+# endif
+
+  /* Range expressions. */
+# ifdef _LIBC
+  uint32_t *range_starts;
+  uint32_t *range_ends;
+# else /* not _LIBC */
+  wchar_t *range_starts;
+  wchar_t *range_ends;
+# endif /* not _LIBC */
+
+  /* Character classes. */
+  wctype_t *char_classes;
+
+  /* If this character set is the non-matching list.  */
+  unsigned int non_match : 1;
+
+  /* # of multibyte characters.  */
+  int nmbchars;
+
+  /* # of collating symbols.  */
+  int ncoll_syms;
+
+  /* # of equivalence classes. */
+  int nequiv_classes;
+
+  /* # of range expressions. */
+  int nranges;
+
+  /* # of character classes. */
+  int nchar_classes;
+} re_charset_t;
+#endif /* RE_ENABLE_I18N */
+
+typedef struct
+{
+  union
+  {
+    unsigned char c;		/* for CHARACTER */
+    re_bitset_ptr_t sbcset;	/* for SIMPLE_BRACKET */
+#ifdef RE_ENABLE_I18N
+    re_charset_t *mbcset;	/* for COMPLEX_BRACKET */
+#endif /* RE_ENABLE_I18N */
+    int idx;			/* for BACK_REF */
+    re_context_type ctx_type;	/* for ANCHOR */
+  } opr;
+#if __GNUC__ >= 2
+  re_token_type_t type : 8;
+#else
+  re_token_type_t type;
+#endif
+  unsigned int constraint : 10;	/* context constraint */
+  unsigned int duplicated : 1;
+  unsigned int opt_subexp : 1;
+#ifdef RE_ENABLE_I18N
+  unsigned int accept_mb : 1;
+  /* These 2 bits can be moved into the union if needed (e.g. if running out
+     of bits; move opr.c to opr.c.c and move the flags to opr.c.flags).  */
+  unsigned int mb_partial : 1;
+#endif
+  unsigned int word_char : 1;
+} re_token_t;
+
+#define IS_EPSILON_NODE(type) ((type) & EPSILON_BIT)
+
+struct re_string_t
+{
+  /* Indicate the raw buffer which is the original string passed as an
+     argument of regexec(), re_search(), etc..  */
+  const unsigned char *raw_mbs;
+  /* Store the multibyte string.  In case of "case insensitive mode" like
+     REG_ICASE, upper cases of the string are stored, otherwise MBS points
+     the same address that RAW_MBS points.  */
+  unsigned char *mbs;
+#ifdef RE_ENABLE_I18N
+  /* Store the wide character string which is corresponding to MBS.  */
+  wint_t *wcs;
+  int *offsets;
+  mbstate_t cur_state;
+#endif
+  /* Index in RAW_MBS.  Each character mbs[i] corresponds to
+     raw_mbs[raw_mbs_idx + i].  */
+  int raw_mbs_idx;
+  /* The length of the valid characters in the buffers.  */
+  int valid_len;
+  /* The corresponding number of bytes in raw_mbs array.  */
+  int valid_raw_len;
+  /* The length of the buffers MBS and WCS.  */
+  int bufs_len;
+  /* The index in MBS, which is updated by re_string_fetch_byte.  */
+  int cur_idx;
+  /* length of RAW_MBS array.  */
+  int raw_len;
+  /* This is RAW_LEN - RAW_MBS_IDX + VALID_LEN - VALID_RAW_LEN.  */
+  int len;
+  /* End of the buffer may be shorter than its length in the cases such
+     as re_match_2, re_search_2.  Then, we use STOP for end of the buffer
+     instead of LEN.  */
+  int raw_stop;
+  /* This is RAW_STOP - RAW_MBS_IDX adjusted through OFFSETS.  */
+  int stop;
+
+  /* The context of mbs[0].  We store the context independently, since
+     the context of mbs[0] may be different from raw_mbs[0], which is
+     the beginning of the input string.  */
+  unsigned int tip_context;
+  /* The translation passed as a part of an argument of re_compile_pattern.  */
+  RE_TRANSLATE_TYPE trans;
+  /* Copy of re_dfa_t's word_char.  */
+  re_const_bitset_ptr_t word_char;
+  /* 1 if REG_ICASE.  */
+  unsigned char icase;
+  unsigned char is_utf8;
+  unsigned char map_notascii;
+  unsigned char mbs_allocated;
+  unsigned char offsets_needed;
+  unsigned char newline_anchor;
+  unsigned char word_ops_used;
+  int mb_cur_max;
+};
+typedef struct re_string_t re_string_t;
+
+
+struct re_dfa_t;
+typedef struct re_dfa_t re_dfa_t;
+
+#ifndef _LIBC
+# ifdef __i386__
+#  define internal_function   __attribute ((regparm (3), stdcall))
+# else
+#  define internal_function
+# endif
+#endif
+
+#ifndef NOT_IN_libc
+static reg_errcode_t re_string_realloc_buffers (re_string_t *pstr,
+						int new_buf_len)
+     internal_function;
+# ifdef RE_ENABLE_I18N
+static void build_wcs_buffer (re_string_t *pstr) internal_function;
+static reg_errcode_t build_wcs_upper_buffer (re_string_t *pstr)
+  internal_function;
+# endif /* RE_ENABLE_I18N */
+static void build_upper_buffer (re_string_t *pstr) internal_function;
+static void re_string_translate_buffer (re_string_t *pstr) internal_function;
+static unsigned int re_string_context_at (const re_string_t *input, int idx,
+					  int eflags)
+     internal_function __attribute ((pure));
+#endif
+#define re_string_peek_byte(pstr, offset) \
+  ((pstr)->mbs[(pstr)->cur_idx + offset])
+#define re_string_fetch_byte(pstr) \
+  ((pstr)->mbs[(pstr)->cur_idx++])
+#define re_string_first_byte(pstr, idx) \
+  ((idx) == (pstr)->valid_len || (pstr)->wcs[idx] != WEOF)
+#define re_string_is_single_byte_char(pstr, idx) \
+  ((pstr)->wcs[idx] != WEOF && ((pstr)->valid_len == (idx) + 1 \
+				|| (pstr)->wcs[(idx) + 1] != WEOF))
+#define re_string_eoi(pstr) ((pstr)->stop <= (pstr)->cur_idx)
+#define re_string_cur_idx(pstr) ((pstr)->cur_idx)
+#define re_string_get_buffer(pstr) ((pstr)->mbs)
+#define re_string_length(pstr) ((pstr)->len)
+#define re_string_byte_at(pstr,idx) ((pstr)->mbs[idx])
+#define re_string_skip_bytes(pstr,idx) ((pstr)->cur_idx += (idx))
+#define re_string_set_index(pstr,idx) ((pstr)->cur_idx = (idx))
+
+#ifndef _LIBC
+# if HAVE_ALLOCA
+#  if (_MSC_VER)
+#   include <malloc.h>
+#   define __libc_use_alloca(n) 0
+#  else
+#   include <alloca.h>
+/* The OS usually guarantees only one guard page at the bottom of the stack,
+   and a page size can be as small as 4096 bytes.  So we cannot safely
+   allocate anything larger than 4096 bytes.  Also care for the possibility
+   of a few compiler-allocated temporary stack slots.  */
+#  define __libc_use_alloca(n) ((n) < 4032)
+#  endif
+# else
+/* alloca is implemented with malloc, so just use malloc.  */
+#  define __libc_use_alloca(n) 0
+# endif
+#endif
+
+#define re_malloc(t,n) ((t *) malloc ((n) * sizeof (t)))
+/* SunOS 4.1.x realloc doesn't accept null pointers: pre-Standard C. Sigh. */
+#define re_realloc(p,t,n) ((p != NULL) ? (t *) realloc (p,(n)*sizeof(t)) : (t *) calloc(n,sizeof(t)))
+#define re_free(p) free (p)
+
+struct bin_tree_t
+{
+  struct bin_tree_t *parent;
+  struct bin_tree_t *left;
+  struct bin_tree_t *right;
+  struct bin_tree_t *first;
+  struct bin_tree_t *next;
+
+  re_token_t token;
+
+  /* `node_idx' is the index in dfa->nodes, if `type' == 0.
+     Otherwise `type' indicate the type of this node.  */
+  int node_idx;
+};
+typedef struct bin_tree_t bin_tree_t;
+
+#define BIN_TREE_STORAGE_SIZE \
+  ((1024 - sizeof (void *)) / sizeof (bin_tree_t))
+
+struct bin_tree_storage_t
+{
+  struct bin_tree_storage_t *next;
+  bin_tree_t data[BIN_TREE_STORAGE_SIZE];
+};
+typedef struct bin_tree_storage_t bin_tree_storage_t;
+
+#define CONTEXT_WORD 1
+#define CONTEXT_NEWLINE (CONTEXT_WORD << 1)
+#define CONTEXT_BEGBUF (CONTEXT_NEWLINE << 1)
+#define CONTEXT_ENDBUF (CONTEXT_BEGBUF << 1)
+
+#define IS_WORD_CONTEXT(c) ((c) & CONTEXT_WORD)
+#define IS_NEWLINE_CONTEXT(c) ((c) & CONTEXT_NEWLINE)
+#define IS_BEGBUF_CONTEXT(c) ((c) & CONTEXT_BEGBUF)
+#define IS_ENDBUF_CONTEXT(c) ((c) & CONTEXT_ENDBUF)
+#define IS_ORDINARY_CONTEXT(c) ((c) == 0)
+
+#define IS_WORD_CHAR(ch) (isalnum (ch) || (ch) == '_')
+#define IS_NEWLINE(ch) ((ch) == NEWLINE_CHAR)
+#define IS_WIDE_WORD_CHAR(ch) (iswalnum (ch) || (ch) == L'_')
+#define IS_WIDE_NEWLINE(ch) ((ch) == WIDE_NEWLINE_CHAR)
+
+#define NOT_SATISFY_PREV_CONSTRAINT(constraint,context) \
+ ((((constraint) & PREV_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \
+  || ((constraint & PREV_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \
+  || ((constraint & PREV_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context))\
+  || ((constraint & PREV_BEGBUF_CONSTRAINT) && !IS_BEGBUF_CONTEXT (context)))
+
+#define NOT_SATISFY_NEXT_CONSTRAINT(constraint,context) \
+ ((((constraint) & NEXT_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \
+  || (((constraint) & NEXT_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \
+  || (((constraint) & NEXT_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context)) \
+  || (((constraint) & NEXT_ENDBUF_CONSTRAINT) && !IS_ENDBUF_CONTEXT (context)))
+
+struct re_dfastate_t
+{
+  unsigned int hash;
+  re_node_set nodes;
+  re_node_set non_eps_nodes;
+  re_node_set inveclosure;
+  re_node_set *entrance_nodes;
+  struct re_dfastate_t **trtable, **word_trtable;
+  unsigned int context : 4;
+  unsigned int halt : 1;
+  /* If this state can accept `multi byte'.
+     Note that we refer to multibyte characters, and multi character
+     collating elements as `multi byte'.  */
+  unsigned int accept_mb : 1;
+  /* If this state has backreference node(s).  */
+  unsigned int has_backref : 1;
+  unsigned int has_constraint : 1;
+};
+typedef struct re_dfastate_t re_dfastate_t;
+
+struct re_state_table_entry
+{
+  int num;
+  int alloc;
+  re_dfastate_t **array;
+};
+
+/* Array type used in re_sub_match_last_t and re_sub_match_top_t.  */
+
+typedef struct
+{
+  int next_idx;
+  int alloc;
+  re_dfastate_t **array;
+} state_array_t;
+
+/* Store information about the node NODE whose type is OP_CLOSE_SUBEXP.  */
+
+typedef struct
+{
+  int node;
+  int str_idx; /* The position NODE match at.  */
+  state_array_t path;
+} re_sub_match_last_t;
+
+/* Store information about the node NODE whose type is OP_OPEN_SUBEXP.
+   And information about the node, whose type is OP_CLOSE_SUBEXP,
+   corresponding to NODE is stored in LASTS.  */
+
+typedef struct
+{
+  int str_idx;
+  int node;
+  state_array_t *path;
+  int alasts; /* Allocation size of LASTS.  */
+  int nlasts; /* The number of LASTS.  */
+  re_sub_match_last_t **lasts;
+} re_sub_match_top_t;
+
+struct re_backref_cache_entry
+{
+  int node;
+  int str_idx;
+  int subexp_from;
+  int subexp_to;
+  char more;
+  char unused;
+  unsigned short int eps_reachable_subexps_map;
+};
+
+typedef struct
+{
+  /* The string object corresponding to the input string.  */
+  re_string_t input;
+#if defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L)
+  const re_dfa_t *const dfa;
+#else
+  const re_dfa_t *dfa;
+#endif
+  /* EFLAGS of the argument of regexec.  */
+  int eflags;
+  /* Where the matching ends.  */
+  int match_last;
+  int last_node;
+  /* The state log used by the matcher.  */
+  re_dfastate_t **state_log;
+  int state_log_top;
+  /* Back reference cache.  */
+  int nbkref_ents;
+  int abkref_ents;
+  struct re_backref_cache_entry *bkref_ents;
+  int max_mb_elem_len;
+  int nsub_tops;
+  int asub_tops;
+  re_sub_match_top_t **sub_tops;
+} re_match_context_t;
+
+typedef struct
+{
+  re_dfastate_t **sifted_states;
+  re_dfastate_t **limited_states;
+  int last_node;
+  int last_str_idx;
+  re_node_set limits;
+} re_sift_context_t;
+
+struct re_fail_stack_ent_t
+{
+  int idx;
+  int node;
+  regmatch_t *regs;
+  re_node_set eps_via_nodes;
+};
+
+struct re_fail_stack_t
+{
+  int num;
+  int alloc;
+  struct re_fail_stack_ent_t *stack;
+};
+
+struct re_dfa_t
+{
+  re_token_t *nodes;
+  size_t nodes_alloc;
+  size_t nodes_len;
+  int *nexts;
+  int *org_indices;
+  re_node_set *edests;
+  re_node_set *eclosures;
+  re_node_set *inveclosures;
+  struct re_state_table_entry *state_table;
+  re_dfastate_t *init_state;
+  re_dfastate_t *init_state_word;
+  re_dfastate_t *init_state_nl;
+  re_dfastate_t *init_state_begbuf;
+  bin_tree_t *str_tree;
+  bin_tree_storage_t *str_tree_storage;
+  re_bitset_ptr_t sb_char;
+  int str_tree_storage_idx;
+
+  /* number of subexpressions `re_nsub' is in regex_t.  */
+  unsigned int state_hash_mask;
+  int init_node;
+  int nbackref; /* The number of backreference in this dfa.  */
+
+  /* Bitmap expressing which backreference is used.  */
+  bitset_word_t used_bkref_map;
+  bitset_word_t completed_bkref_map;
+
+  unsigned int has_plural_match : 1;
+  /* If this dfa has "multibyte node", which is a backreference or
+     a node which can accept multibyte character or multi character
+     collating element.  */
+  unsigned int has_mb_node : 1;
+  unsigned int is_utf8 : 1;
+  unsigned int map_notascii : 1;
+  unsigned int word_ops_used : 1;
+  int mb_cur_max;
+  bitset_t word_char;
+  reg_syntax_t syntax;
+  int *subexp_map;
+#ifdef DEBUG
+  char* re_str;
+#endif
+#if defined _LIBC
+  __libc_lock_define (, lock)
+#endif
+};
+
+#define re_node_set_init_empty(set) memset (set, '\0', sizeof (re_node_set))
+#define re_node_set_remove(set,id) \
+  (re_node_set_remove_at (set, re_node_set_contains (set, id) - 1))
+#define re_node_set_empty(p) ((p)->nelem = 0)
+#define re_node_set_free(set) re_free ((set)->elems)
+
+
+typedef enum
+{
+  SB_CHAR,
+  MB_CHAR,
+  EQUIV_CLASS,
+  COLL_SYM,
+  CHAR_CLASS
+} bracket_elem_type;
+
+typedef struct
+{
+  bracket_elem_type type;
+  union
+  {
+    unsigned char ch;
+    unsigned char *name;
+    wchar_t wch;
+  } opr;
+} bracket_elem_t;
+
+
+/* Inline functions for bitset operation.  */
+static inline void
+bitset_not (bitset_t set)
+{
+  int bitset_i;
+  for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i)
+    set[bitset_i] = ~set[bitset_i];
+}
+
+static inline void
+bitset_merge (bitset_t dest, const bitset_t src)
+{
+  int bitset_i;
+  for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i)
+    dest[bitset_i] |= src[bitset_i];
+}
+
+static inline void
+bitset_mask (bitset_t dest, const bitset_t src)
+{
+  int bitset_i;
+  for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i)
+    dest[bitset_i] &= src[bitset_i];
+}
+
+#ifdef RE_ENABLE_I18N
+/* Inline functions for re_string.  */
+static inline int
+internal_function __attribute ((pure))
+re_string_char_size_at (const re_string_t *pstr, int idx)
+{
+  int byte_idx;
+  if (pstr->mb_cur_max == 1)
+    return 1;
+  for (byte_idx = 1; idx + byte_idx < pstr->valid_len; ++byte_idx)
+    if (pstr->wcs[idx + byte_idx] != WEOF)
+      break;
+  return byte_idx;
+}
+
+static inline wint_t
+internal_function __attribute ((pure))
+re_string_wchar_at (const re_string_t *pstr, int idx)
+{
+  if (pstr->mb_cur_max == 1)
+    return (wint_t) pstr->mbs[idx];
+  return (wint_t) pstr->wcs[idx];
+}
+
+# ifndef NOT_IN_libc
+static int
+internal_function __attribute ((pure))
+re_string_elem_size_at (const re_string_t *pstr, int idx)
+{
+#  ifdef _LIBC
+  const unsigned char *p, *extra;
+  const int32_t *table, *indirect;
+  int32_t tmp;
+#   include <locale/weight.h>
+  uint_fast32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+
+  if (nrules != 0)
+    {
+      table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+      extra = (const unsigned char *)
+	_NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB);
+      indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE,
+						_NL_COLLATE_INDIRECTMB);
+      p = pstr->mbs + idx;
+      tmp = findidx (&p);
+      return p - pstr->mbs - idx;
+    }
+  else
+#  endif /* _LIBC */
+    return 1;
+}
+# endif
+#endif /* RE_ENABLE_I18N */
+
+#endif /*  _REGEX_INTERNAL_H */
diff --git a/compat/regex/regexec.c b/compat/regex/regexec.c
new file mode 100644
index 0000000..0194965
--- /dev/null
+++ b/compat/regex/regexec.c
@@ -0,0 +1,4369 @@
+/* Extended regular expression matching and search library.
+   Copyright (C) 2002-2005, 2007, 2009, 2010 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA.  */
+
+static reg_errcode_t match_ctx_init (re_match_context_t *cache, int eflags,
+				     int n) internal_function;
+static void match_ctx_clean (re_match_context_t *mctx) internal_function;
+static void match_ctx_free (re_match_context_t *cache) internal_function;
+static reg_errcode_t match_ctx_add_entry (re_match_context_t *cache, int node,
+					  int str_idx, int from, int to)
+     internal_function;
+static int search_cur_bkref_entry (const re_match_context_t *mctx, int str_idx)
+     internal_function;
+static reg_errcode_t match_ctx_add_subtop (re_match_context_t *mctx, int node,
+					   int str_idx) internal_function;
+static re_sub_match_last_t * match_ctx_add_sublast (re_sub_match_top_t *subtop,
+						   int node, int str_idx)
+     internal_function;
+static void sift_ctx_init (re_sift_context_t *sctx, re_dfastate_t **sifted_sts,
+			   re_dfastate_t **limited_sts, int last_node,
+			   int last_str_idx)
+     internal_function;
+static reg_errcode_t re_search_internal (const regex_t *preg,
+					 const char *string, int length,
+					 int start, int range, int stop,
+					 size_t nmatch, regmatch_t pmatch[],
+					 int eflags);
+static int re_search_2_stub (struct re_pattern_buffer *bufp,
+			     const char *string1, int length1,
+			     const char *string2, int length2,
+			     int start, int range, struct re_registers *regs,
+			     int stop, int ret_len);
+static int re_search_stub (struct re_pattern_buffer *bufp,
+			   const char *string, int length, int start,
+			   int range, int stop, struct re_registers *regs,
+			   int ret_len);
+static unsigned re_copy_regs (struct re_registers *regs, regmatch_t *pmatch,
+			      int nregs, int regs_allocated);
+static reg_errcode_t prune_impossible_nodes (re_match_context_t *mctx);
+static int check_matching (re_match_context_t *mctx, int fl_longest_match,
+			   int *p_match_first) internal_function;
+static int check_halt_state_context (const re_match_context_t *mctx,
+				     const re_dfastate_t *state, int idx)
+     internal_function;
+static void update_regs (const re_dfa_t *dfa, regmatch_t *pmatch,
+			 regmatch_t *prev_idx_match, int cur_node,
+			 int cur_idx, int nmatch) internal_function;
+static reg_errcode_t push_fail_stack (struct re_fail_stack_t *fs,
+				      int str_idx, int dest_node, int nregs,
+				      regmatch_t *regs,
+				      re_node_set *eps_via_nodes)
+     internal_function;
+static reg_errcode_t set_regs (const regex_t *preg,
+			       const re_match_context_t *mctx,
+			       size_t nmatch, regmatch_t *pmatch,
+			       int fl_backtrack) internal_function;
+static reg_errcode_t free_fail_stack_return (struct re_fail_stack_t *fs)
+     internal_function;
+
+#ifdef RE_ENABLE_I18N
+static int sift_states_iter_mb (const re_match_context_t *mctx,
+				re_sift_context_t *sctx,
+				int node_idx, int str_idx, int max_str_idx)
+     internal_function;
+#endif /* RE_ENABLE_I18N */
+static reg_errcode_t sift_states_backward (const re_match_context_t *mctx,
+					   re_sift_context_t *sctx)
+     internal_function;
+static reg_errcode_t build_sifted_states (const re_match_context_t *mctx,
+					  re_sift_context_t *sctx, int str_idx,
+					  re_node_set *cur_dest)
+     internal_function;
+static reg_errcode_t update_cur_sifted_state (const re_match_context_t *mctx,
+					      re_sift_context_t *sctx,
+					      int str_idx,
+					      re_node_set *dest_nodes)
+     internal_function;
+static reg_errcode_t add_epsilon_src_nodes (const re_dfa_t *dfa,
+					    re_node_set *dest_nodes,
+					    const re_node_set *candidates)
+     internal_function;
+static int check_dst_limits (const re_match_context_t *mctx,
+			     re_node_set *limits,
+			     int dst_node, int dst_idx, int src_node,
+			     int src_idx) internal_function;
+static int check_dst_limits_calc_pos_1 (const re_match_context_t *mctx,
+					int boundaries, int subexp_idx,
+					int from_node, int bkref_idx)
+     internal_function;
+static int check_dst_limits_calc_pos (const re_match_context_t *mctx,
+				      int limit, int subexp_idx,
+				      int node, int str_idx,
+				      int bkref_idx) internal_function;
+static reg_errcode_t check_subexp_limits (const re_dfa_t *dfa,
+					  re_node_set *dest_nodes,
+					  const re_node_set *candidates,
+					  re_node_set *limits,
+					  struct re_backref_cache_entry *bkref_ents,
+					  int str_idx) internal_function;
+static reg_errcode_t sift_states_bkref (const re_match_context_t *mctx,
+					re_sift_context_t *sctx,
+					int str_idx, const re_node_set *candidates)
+     internal_function;
+static reg_errcode_t merge_state_array (const re_dfa_t *dfa,
+					re_dfastate_t **dst,
+					re_dfastate_t **src, int num)
+     internal_function;
+static re_dfastate_t *find_recover_state (reg_errcode_t *err,
+					 re_match_context_t *mctx) internal_function;
+static re_dfastate_t *transit_state (reg_errcode_t *err,
+				     re_match_context_t *mctx,
+				     re_dfastate_t *state) internal_function;
+static re_dfastate_t *merge_state_with_log (reg_errcode_t *err,
+					    re_match_context_t *mctx,
+					    re_dfastate_t *next_state)
+     internal_function;
+static reg_errcode_t check_subexp_matching_top (re_match_context_t *mctx,
+						re_node_set *cur_nodes,
+						int str_idx) internal_function;
+#if 0
+static re_dfastate_t *transit_state_sb (reg_errcode_t *err,
+					re_match_context_t *mctx,
+					re_dfastate_t *pstate)
+     internal_function;
+#endif
+#ifdef RE_ENABLE_I18N
+static reg_errcode_t transit_state_mb (re_match_context_t *mctx,
+				       re_dfastate_t *pstate)
+     internal_function;
+#endif /* RE_ENABLE_I18N */
+static reg_errcode_t transit_state_bkref (re_match_context_t *mctx,
+					  const re_node_set *nodes)
+     internal_function;
+static reg_errcode_t get_subexp (re_match_context_t *mctx,
+				 int bkref_node, int bkref_str_idx)
+     internal_function;
+static reg_errcode_t get_subexp_sub (re_match_context_t *mctx,
+				     const re_sub_match_top_t *sub_top,
+				     re_sub_match_last_t *sub_last,
+				     int bkref_node, int bkref_str)
+     internal_function;
+static int find_subexp_node (const re_dfa_t *dfa, const re_node_set *nodes,
+			     int subexp_idx, int type) internal_function;
+static reg_errcode_t check_arrival (re_match_context_t *mctx,
+				    state_array_t *path, int top_node,
+				    int top_str, int last_node, int last_str,
+				    int type) internal_function;
+static reg_errcode_t check_arrival_add_next_nodes (re_match_context_t *mctx,
+						   int str_idx,
+						   re_node_set *cur_nodes,
+						   re_node_set *next_nodes)
+     internal_function;
+static reg_errcode_t check_arrival_expand_ecl (const re_dfa_t *dfa,
+					       re_node_set *cur_nodes,
+					       int ex_subexp, int type)
+     internal_function;
+static reg_errcode_t check_arrival_expand_ecl_sub (const re_dfa_t *dfa,
+						   re_node_set *dst_nodes,
+						   int target, int ex_subexp,
+						   int type) internal_function;
+static reg_errcode_t expand_bkref_cache (re_match_context_t *mctx,
+					 re_node_set *cur_nodes, int cur_str,
+					 int subexp_num, int type)
+     internal_function;
+static int build_trtable (const re_dfa_t *dfa,
+			  re_dfastate_t *state) internal_function;
+#ifdef RE_ENABLE_I18N
+static int check_node_accept_bytes (const re_dfa_t *dfa, int node_idx,
+				    const re_string_t *input, int idx)
+     internal_function;
+# ifdef _LIBC
+static unsigned int find_collation_sequence_value (const unsigned char *mbs,
+						   size_t name_len)
+     internal_function;
+# endif /* _LIBC */
+#endif /* RE_ENABLE_I18N */
+static int group_nodes_into_DFAstates (const re_dfa_t *dfa,
+				       const re_dfastate_t *state,
+				       re_node_set *states_node,
+				       bitset_t *states_ch) internal_function;
+static int check_node_accept (const re_match_context_t *mctx,
+			      const re_token_t *node, int idx)
+     internal_function;
+static reg_errcode_t extend_buffers (re_match_context_t *mctx)
+     internal_function;
+
+/* Entry point for POSIX code.  */
+
+/* regexec searches for a given pattern, specified by PREG, in the
+   string STRING.
+
+   If NMATCH is zero or REG_NOSUB was set in the cflags argument to
+   `regcomp', we ignore PMATCH.  Otherwise, we assume PMATCH has at
+   least NMATCH elements, and we set them to the offsets of the
+   corresponding matched substrings.
+
+   EFLAGS specifies `execution flags' which affect matching: if
+   REG_NOTBOL is set, then ^ does not match at the beginning of the
+   string; if REG_NOTEOL is set, then $ does not match at the end.
+
+   We return 0 if we find a match and REG_NOMATCH if not.  */
+
+int
+regexec (
+	const regex_t *__restrict preg,
+	const char *__restrict string,
+	size_t nmatch,
+	regmatch_t pmatch[],
+	int eflags)
+{
+  reg_errcode_t err;
+  int start, length;
+
+  if (eflags & ~(REG_NOTBOL | REG_NOTEOL | REG_STARTEND))
+    return REG_BADPAT;
+
+  if (eflags & REG_STARTEND)
+    {
+      start = pmatch[0].rm_so;
+      length = pmatch[0].rm_eo;
+    }
+  else
+    {
+      start = 0;
+      length = strlen (string);
+    }
+
+  __libc_lock_lock (dfa->lock);
+  if (preg->no_sub)
+    err = re_search_internal (preg, string, length, start, length - start,
+			      length, 0, NULL, eflags);
+  else
+    err = re_search_internal (preg, string, length, start, length - start,
+			      length, nmatch, pmatch, eflags);
+  __libc_lock_unlock (dfa->lock);
+  return err != REG_NOERROR;
+}
+
+#ifdef _LIBC
+# include <shlib-compat.h>
+versioned_symbol (libc, __regexec, regexec, GLIBC_2_3_4);
+
+# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3_4)
+__typeof__ (__regexec) __compat_regexec;
+
+int
+attribute_compat_text_section
+__compat_regexec (const regex_t *__restrict preg,
+		  const char *__restrict string, size_t nmatch,
+		  regmatch_t pmatch[], int eflags)
+{
+  return regexec (preg, string, nmatch, pmatch,
+		  eflags & (REG_NOTBOL | REG_NOTEOL));
+}
+compat_symbol (libc, __compat_regexec, regexec, GLIBC_2_0);
+# endif
+#endif
+
+/* Entry points for GNU code.  */
+
+/* re_match, re_search, re_match_2, re_search_2
+
+   The former two functions operate on STRING with length LENGTH,
+   while the later two operate on concatenation of STRING1 and STRING2
+   with lengths LENGTH1 and LENGTH2, respectively.
+
+   re_match() matches the compiled pattern in BUFP against the string,
+   starting at index START.
+
+   re_search() first tries matching at index START, then it tries to match
+   starting from index START + 1, and so on.  The last start position tried
+   is START + RANGE.  (Thus RANGE = 0 forces re_search to operate the same
+   way as re_match().)
+
+   The parameter STOP of re_{match,search}_2 specifies that no match exceeding
+   the first STOP characters of the concatenation of the strings should be
+   concerned.
+
+   If REGS is not NULL, and BUFP->no_sub is not set, the offsets of the match
+   and all groups is stroed in REGS.  (For the "_2" variants, the offsets are
+   computed relative to the concatenation, not relative to the individual
+   strings.)
+
+   On success, re_match* functions return the length of the match, re_search*
+   return the position of the start of the match.  Return value -1 means no
+   match was found and -2 indicates an internal error.  */
+
+int
+re_match (struct re_pattern_buffer *bufp,
+	  const char *string,
+	  int length,
+	  int start,
+	  struct re_registers *regs)
+{
+  return re_search_stub (bufp, string, length, start, 0, length, regs, 1);
+}
+#ifdef _LIBC
+weak_alias (__re_match, re_match)
+#endif
+
+int
+re_search (struct re_pattern_buffer *bufp,
+	   const char *string,
+	   int length, int start, int range,
+	   struct re_registers *regs)
+{
+  return re_search_stub (bufp, string, length, start, range, length, regs, 0);
+}
+#ifdef _LIBC
+weak_alias (__re_search, re_search)
+#endif
+
+int
+re_match_2 (struct re_pattern_buffer *bufp,
+	    const char *string1, int length1,
+	    const char *string2, int length2, int start,
+	    struct re_registers *regs, int stop)
+{
+  return re_search_2_stub (bufp, string1, length1, string2, length2,
+			   start, 0, regs, stop, 1);
+}
+#ifdef _LIBC
+weak_alias (__re_match_2, re_match_2)
+#endif
+
+int
+re_search_2 (struct re_pattern_buffer *bufp,
+	     const char *string1, int length1,
+	     const char *string2, int length2, int start,
+	     int range, struct re_registers *regs,  int stop)
+{
+  return re_search_2_stub (bufp, string1, length1, string2, length2,
+			   start, range, regs, stop, 0);
+}
+#ifdef _LIBC
+weak_alias (__re_search_2, re_search_2)
+#endif
+
+static int
+re_search_2_stub (struct re_pattern_buffer *bufp,
+		  const char *string1, int length1,
+		  const char *string2, int length2, int start,
+		  int range, struct re_registers *regs,
+		  int stop, int ret_len)
+{
+  const char *str;
+  int rval;
+  int len = length1 + length2;
+  int free_str = 0;
+
+  if (BE (length1 < 0 || length2 < 0 || stop < 0, 0))
+    return -2;
+
+  /* Concatenate the strings.  */
+  if (length2 > 0)
+    if (length1 > 0)
+      {
+	char *s = re_malloc (char, len);
+
+	if (BE (s == NULL, 0))
+	  return -2;
+	memcpy (s, string1, length1);
+	memcpy (s + length1, string2, length2);
+	str = s;
+	free_str = 1;
+      }
+    else
+      str = string2;
+  else
+    str = string1;
+
+  rval = re_search_stub (bufp, str, len, start, range, stop, regs, ret_len);
+  if (free_str)
+    re_free ((char *) str);
+  return rval;
+}
+
+/* The parameters have the same meaning as those of re_search.
+   Additional parameters:
+   If RET_LEN is nonzero the length of the match is returned (re_match style);
+   otherwise the position of the match is returned.  */
+
+static int
+re_search_stub (struct re_pattern_buffer *bufp,
+		const char *string, int length, int start,
+		int range, int stop,
+		struct re_registers *regs, int ret_len)
+{
+  reg_errcode_t result;
+  regmatch_t *pmatch;
+  int nregs, rval;
+  int eflags = 0;
+
+  /* Check for out-of-range.  */
+  if (BE (start < 0 || start > length, 0))
+    return -1;
+  if (BE (start + range > length, 0))
+    range = length - start;
+  else if (BE (start + range < 0, 0))
+    range = -start;
+
+  __libc_lock_lock (dfa->lock);
+
+  eflags |= (bufp->not_bol) ? REG_NOTBOL : 0;
+  eflags |= (bufp->not_eol) ? REG_NOTEOL : 0;
+
+  /* Compile fastmap if we haven't yet.  */
+  if (range > 0 && bufp->fastmap != NULL && !bufp->fastmap_accurate)
+    re_compile_fastmap (bufp);
+
+  if (BE (bufp->no_sub, 0))
+    regs = NULL;
+
+  /* We need at least 1 register.  */
+  if (regs == NULL)
+    nregs = 1;
+  else if (BE (bufp->regs_allocated == REGS_FIXED &&
+	       regs->num_regs < bufp->re_nsub + 1, 0))
+    {
+      nregs = regs->num_regs;
+      if (BE (nregs < 1, 0))
+	{
+	  /* Nothing can be copied to regs.  */
+	  regs = NULL;
+	  nregs = 1;
+	}
+    }
+  else
+    nregs = bufp->re_nsub + 1;
+  pmatch = re_malloc (regmatch_t, nregs);
+  if (BE (pmatch == NULL, 0))
+    {
+      rval = -2;
+      goto out;
+    }
+
+  result = re_search_internal (bufp, string, length, start, range, stop,
+			       nregs, pmatch, eflags);
+
+  rval = 0;
+
+  /* I hope we needn't fill ther regs with -1's when no match was found.  */
+  if (result != REG_NOERROR)
+    rval = -1;
+  else if (regs != NULL)
+    {
+      /* If caller wants register contents data back, copy them.  */
+      bufp->regs_allocated = re_copy_regs (regs, pmatch, nregs,
+					   bufp->regs_allocated);
+      if (BE (bufp->regs_allocated == REGS_UNALLOCATED, 0))
+	rval = -2;
+    }
+
+  if (BE (rval == 0, 1))
+    {
+      if (ret_len)
+	{
+	  assert (pmatch[0].rm_so == start);
+	  rval = pmatch[0].rm_eo - start;
+	}
+      else
+	rval = pmatch[0].rm_so;
+    }
+  re_free (pmatch);
+ out:
+  __libc_lock_unlock (dfa->lock);
+  return rval;
+}
+
+static unsigned
+re_copy_regs (struct re_registers *regs,
+	      regmatch_t *pmatch,
+	      int nregs, int regs_allocated)
+{
+  int rval = REGS_REALLOCATE;
+  int i;
+  int need_regs = nregs + 1;
+  /* We need one extra element beyond `num_regs' for the `-1' marker GNU code
+     uses.  */
+
+  /* Have the register data arrays been allocated?  */
+  if (regs_allocated == REGS_UNALLOCATED)
+    { /* No.  So allocate them with malloc.  */
+      regs->start = re_malloc (regoff_t, need_regs);
+      if (BE (regs->start == NULL, 0))
+	return REGS_UNALLOCATED;
+      regs->end = re_malloc (regoff_t, need_regs);
+      if (BE (regs->end == NULL, 0))
+	{
+	  re_free (regs->start);
+	  return REGS_UNALLOCATED;
+	}
+      regs->num_regs = need_regs;
+    }
+  else if (regs_allocated == REGS_REALLOCATE)
+    { /* Yes.  If we need more elements than were already
+	 allocated, reallocate them.  If we need fewer, just
+	 leave it alone.  */
+      if (BE (need_regs > regs->num_regs, 0))
+	{
+	  regoff_t *new_start = re_realloc (regs->start, regoff_t, need_regs);
+	  regoff_t *new_end;
+	  if (BE (new_start == NULL, 0))
+	    return REGS_UNALLOCATED;
+	  new_end = re_realloc (regs->end, regoff_t, need_regs);
+	  if (BE (new_end == NULL, 0))
+	    {
+	      re_free (new_start);
+	      return REGS_UNALLOCATED;
+	    }
+	  regs->start = new_start;
+	  regs->end = new_end;
+	  regs->num_regs = need_regs;
+	}
+    }
+  else
+    {
+      assert (regs_allocated == REGS_FIXED);
+      /* This function may not be called with REGS_FIXED and nregs too big.  */
+      assert (regs->num_regs >= nregs);
+      rval = REGS_FIXED;
+    }
+
+  /* Copy the regs.  */
+  for (i = 0; i < nregs; ++i)
+    {
+      regs->start[i] = pmatch[i].rm_so;
+      regs->end[i] = pmatch[i].rm_eo;
+    }
+  for ( ; i < regs->num_regs; ++i)
+    regs->start[i] = regs->end[i] = -1;
+
+  return rval;
+}
+
+/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
+   ENDS.  Subsequent matches using PATTERN_BUFFER and REGS will use
+   this memory for recording register information.  STARTS and ENDS
+   must be allocated using the malloc library routine, and must each
+   be at least NUM_REGS * sizeof (regoff_t) bytes long.
+
+   If NUM_REGS == 0, then subsequent matches should allocate their own
+   register data.
+
+   Unless this function is called, the first search or match using
+   PATTERN_BUFFER will allocate its own register data, without
+   freeing the old data.  */
+
+void
+re_set_registers (struct re_pattern_buffer *bufp,
+		  struct re_registers *regs,
+		  unsigned num_regs,
+		  regoff_t *starts,
+		  regoff_t *ends)
+{
+  if (num_regs)
+    {
+      bufp->regs_allocated = REGS_REALLOCATE;
+      regs->num_regs = num_regs;
+      regs->start = starts;
+      regs->end = ends;
+    }
+  else
+    {
+      bufp->regs_allocated = REGS_UNALLOCATED;
+      regs->num_regs = 0;
+      regs->start = regs->end = (regoff_t *) 0;
+    }
+}
+#ifdef _LIBC
+weak_alias (__re_set_registers, re_set_registers)
+#endif
+
+/* Entry points compatible with 4.2 BSD regex library.  We don't define
+   them unless specifically requested.  */
+
+#if defined _REGEX_RE_COMP || defined _LIBC
+int
+# ifdef _LIBC
+weak_function
+# endif
+re_exec (s)
+     const char *s;
+{
+  return 0 == regexec (&re_comp_buf, s, 0, NULL, 0);
+}
+#endif /* _REGEX_RE_COMP */
+
+/* Internal entry point.  */
+
+/* Searches for a compiled pattern PREG in the string STRING, whose
+   length is LENGTH.  NMATCH, PMATCH, and EFLAGS have the same
+   mingings with regexec.  START, and RANGE have the same meanings
+   with re_search.
+   Return REG_NOERROR if we find a match, and REG_NOMATCH if not,
+   otherwise return the error code.
+   Note: We assume front end functions already check ranges.
+   (START + RANGE >= 0 && START + RANGE <= LENGTH)  */
+
+static reg_errcode_t
+re_search_internal (const regex_t *preg,
+		    const char *string,
+		    int length, int start, int range, int stop,
+		    size_t nmatch, regmatch_t pmatch[],
+		    int eflags)
+{
+  reg_errcode_t err;
+  const re_dfa_t *dfa = (const re_dfa_t *) preg->buffer;
+  int left_lim, right_lim, incr;
+  int fl_longest_match, match_first, match_kind, match_last = -1;
+  int extra_nmatch;
+  int sb, ch;
+#if defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L)
+  re_match_context_t mctx = { .dfa = dfa };
+#else
+  re_match_context_t mctx;
+#endif
+  char *fastmap = (preg->fastmap != NULL && preg->fastmap_accurate
+		   && range && !preg->can_be_null) ? preg->fastmap : NULL;
+  RE_TRANSLATE_TYPE t = preg->translate;
+
+#if !(defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L))
+  memset (&mctx, '\0', sizeof (re_match_context_t));
+  mctx.dfa = dfa;
+#endif
+
+  extra_nmatch = (nmatch > preg->re_nsub) ? nmatch - (preg->re_nsub + 1) : 0;
+  nmatch -= extra_nmatch;
+
+  /* Check if the DFA haven't been compiled.  */
+  if (BE (preg->used == 0 || dfa->init_state == NULL
+	  || dfa->init_state_word == NULL || dfa->init_state_nl == NULL
+	  || dfa->init_state_begbuf == NULL, 0))
+    return REG_NOMATCH;
+
+#ifdef DEBUG
+  /* We assume front-end functions already check them.  */
+  assert (start + range >= 0 && start + range <= length);
+#endif
+
+  /* If initial states with non-begbuf contexts have no elements,
+     the regex must be anchored.  If preg->newline_anchor is set,
+     we'll never use init_state_nl, so do not check it.  */
+  if (dfa->init_state->nodes.nelem == 0
+      && dfa->init_state_word->nodes.nelem == 0
+      && (dfa->init_state_nl->nodes.nelem == 0
+	  || !preg->newline_anchor))
+    {
+      if (start != 0 && start + range != 0)
+	return REG_NOMATCH;
+      start = range = 0;
+    }
+
+  /* We must check the longest matching, if nmatch > 0.  */
+  fl_longest_match = (nmatch != 0 || dfa->nbackref);
+
+  err = re_string_allocate (&mctx.input, string, length, dfa->nodes_len + 1,
+			    preg->translate, preg->syntax & RE_ICASE, dfa);
+  if (BE (err != REG_NOERROR, 0))
+    goto free_return;
+  mctx.input.stop = stop;
+  mctx.input.raw_stop = stop;
+  mctx.input.newline_anchor = preg->newline_anchor;
+
+  err = match_ctx_init (&mctx, eflags, dfa->nbackref * 2);
+  if (BE (err != REG_NOERROR, 0))
+    goto free_return;
+
+  /* We will log all the DFA states through which the dfa pass,
+     if nmatch > 1, or this dfa has "multibyte node", which is a
+     back-reference or a node which can accept multibyte character or
+     multi character collating element.  */
+  if (nmatch > 1 || dfa->has_mb_node)
+    {
+      /* Avoid overflow.  */
+      if (BE (SIZE_MAX / sizeof (re_dfastate_t *) <= mctx.input.bufs_len, 0))
+	{
+	  err = REG_ESPACE;
+	  goto free_return;
+	}
+
+      mctx.state_log = re_malloc (re_dfastate_t *, mctx.input.bufs_len + 1);
+      if (BE (mctx.state_log == NULL, 0))
+	{
+	  err = REG_ESPACE;
+	  goto free_return;
+	}
+    }
+  else
+    mctx.state_log = NULL;
+
+  match_first = start;
+  mctx.input.tip_context = (eflags & REG_NOTBOL) ? CONTEXT_BEGBUF
+			   : CONTEXT_NEWLINE | CONTEXT_BEGBUF;
+
+  /* Check incrementally whether of not the input string match.  */
+  incr = (range < 0) ? -1 : 1;
+  left_lim = (range < 0) ? start + range : start;
+  right_lim = (range < 0) ? start : start + range;
+  sb = dfa->mb_cur_max == 1;
+  match_kind =
+    (fastmap
+     ? ((sb || !(preg->syntax & RE_ICASE || t) ? 4 : 0)
+	| (range >= 0 ? 2 : 0)
+	| (t != NULL ? 1 : 0))
+     : 8);
+
+  for (;; match_first += incr)
+    {
+      err = REG_NOMATCH;
+      if (match_first < left_lim || right_lim < match_first)
+	goto free_return;
+
+      /* Advance as rapidly as possible through the string, until we
+	 find a plausible place to start matching.  This may be done
+	 with varying efficiency, so there are various possibilities:
+	 only the most common of them are specialized, in order to
+	 save on code size.  We use a switch statement for speed.  */
+      switch (match_kind)
+	{
+	case 8:
+	  /* No fastmap.  */
+	  break;
+
+	case 7:
+	  /* Fastmap with single-byte translation, match forward.  */
+	  while (BE (match_first < right_lim, 1)
+		 && !fastmap[t[(unsigned char) string[match_first]]])
+	    ++match_first;
+	  goto forward_match_found_start_or_reached_end;
+
+	case 6:
+	  /* Fastmap without translation, match forward.  */
+	  while (BE (match_first < right_lim, 1)
+		 && !fastmap[(unsigned char) string[match_first]])
+	    ++match_first;
+
+	forward_match_found_start_or_reached_end:
+	  if (BE (match_first == right_lim, 0))
+	    {
+	      ch = match_first >= length
+		       ? 0 : (unsigned char) string[match_first];
+	      if (!fastmap[t ? t[ch] : ch])
+		goto free_return;
+	    }
+	  break;
+
+	case 4:
+	case 5:
+	  /* Fastmap without multi-byte translation, match backwards.  */
+	  while (match_first >= left_lim)
+	    {
+	      ch = match_first >= length
+		       ? 0 : (unsigned char) string[match_first];
+	      if (fastmap[t ? t[ch] : ch])
+		break;
+	      --match_first;
+	    }
+	  if (match_first < left_lim)
+	    goto free_return;
+	  break;
+
+	default:
+	  /* In this case, we can't determine easily the current byte,
+	     since it might be a component byte of a multibyte
+	     character.  Then we use the constructed buffer instead.  */
+	  for (;;)
+	    {
+	      /* If MATCH_FIRST is out of the valid range, reconstruct the
+		 buffers.  */
+	      unsigned int offset = match_first - mctx.input.raw_mbs_idx;
+	      if (BE (offset >= (unsigned int) mctx.input.valid_raw_len, 0))
+		{
+		  err = re_string_reconstruct (&mctx.input, match_first,
+					       eflags);
+		  if (BE (err != REG_NOERROR, 0))
+		    goto free_return;
+
+		  offset = match_first - mctx.input.raw_mbs_idx;
+		}
+	      /* If MATCH_FIRST is out of the buffer, leave it as '\0'.
+		 Note that MATCH_FIRST must not be smaller than 0.  */
+	      ch = (match_first >= length
+		    ? 0 : re_string_byte_at (&mctx.input, offset));
+	      if (fastmap[ch])
+		break;
+	      match_first += incr;
+	      if (match_first < left_lim || match_first > right_lim)
+		{
+		  err = REG_NOMATCH;
+		  goto free_return;
+		}
+	    }
+	  break;
+	}
+
+      /* Reconstruct the buffers so that the matcher can assume that
+	 the matching starts from the beginning of the buffer.  */
+      err = re_string_reconstruct (&mctx.input, match_first, eflags);
+      if (BE (err != REG_NOERROR, 0))
+	goto free_return;
+
+#ifdef RE_ENABLE_I18N
+     /* Don't consider this char as a possible match start if it part,
+	yet isn't the head, of a multibyte character.  */
+      if (!sb && !re_string_first_byte (&mctx.input, 0))
+	continue;
+#endif
+
+      /* It seems to be appropriate one, then use the matcher.  */
+      /* We assume that the matching starts from 0.  */
+      mctx.state_log_top = mctx.nbkref_ents = mctx.max_mb_elem_len = 0;
+      match_last = check_matching (&mctx, fl_longest_match,
+				   range >= 0 ? &match_first : NULL);
+      if (match_last != -1)
+	{
+	  if (BE (match_last == -2, 0))
+	    {
+	      err = REG_ESPACE;
+	      goto free_return;
+	    }
+	  else
+	    {
+	      mctx.match_last = match_last;
+	      if ((!preg->no_sub && nmatch > 1) || dfa->nbackref)
+		{
+		  re_dfastate_t *pstate = mctx.state_log[match_last];
+		  mctx.last_node = check_halt_state_context (&mctx, pstate,
+							     match_last);
+		}
+	      if ((!preg->no_sub && nmatch > 1 && dfa->has_plural_match)
+		  || dfa->nbackref)
+		{
+		  err = prune_impossible_nodes (&mctx);
+		  if (err == REG_NOERROR)
+		    break;
+		  if (BE (err != REG_NOMATCH, 0))
+		    goto free_return;
+		  match_last = -1;
+		}
+	      else
+		break; /* We found a match.  */
+	    }
+	}
+
+      match_ctx_clean (&mctx);
+    }
+
+#ifdef DEBUG
+  assert (match_last != -1);
+  assert (err == REG_NOERROR);
+#endif
+
+  /* Set pmatch[] if we need.  */
+  if (nmatch > 0)
+    {
+      int reg_idx;
+
+      /* Initialize registers.  */
+      for (reg_idx = 1; reg_idx < nmatch; ++reg_idx)
+	pmatch[reg_idx].rm_so = pmatch[reg_idx].rm_eo = -1;
+
+      /* Set the points where matching start/end.  */
+      pmatch[0].rm_so = 0;
+      pmatch[0].rm_eo = mctx.match_last;
+
+      if (!preg->no_sub && nmatch > 1)
+	{
+	  err = set_regs (preg, &mctx, nmatch, pmatch,
+			  dfa->has_plural_match && dfa->nbackref > 0);
+	  if (BE (err != REG_NOERROR, 0))
+	    goto free_return;
+	}
+
+      /* At last, add the offset to the each registers, since we slided
+	 the buffers so that we could assume that the matching starts
+	 from 0.  */
+      for (reg_idx = 0; reg_idx < nmatch; ++reg_idx)
+	if (pmatch[reg_idx].rm_so != -1)
+	  {
+#ifdef RE_ENABLE_I18N
+	    if (BE (mctx.input.offsets_needed != 0, 0))
+	      {
+		pmatch[reg_idx].rm_so =
+		  (pmatch[reg_idx].rm_so == mctx.input.valid_len
+		   ? mctx.input.valid_raw_len
+		   : mctx.input.offsets[pmatch[reg_idx].rm_so]);
+		pmatch[reg_idx].rm_eo =
+		  (pmatch[reg_idx].rm_eo == mctx.input.valid_len
+		   ? mctx.input.valid_raw_len
+		   : mctx.input.offsets[pmatch[reg_idx].rm_eo]);
+	      }
+#else
+	    assert (mctx.input.offsets_needed == 0);
+#endif
+	    pmatch[reg_idx].rm_so += match_first;
+	    pmatch[reg_idx].rm_eo += match_first;
+	  }
+      for (reg_idx = 0; reg_idx < extra_nmatch; ++reg_idx)
+	{
+	  pmatch[nmatch + reg_idx].rm_so = -1;
+	  pmatch[nmatch + reg_idx].rm_eo = -1;
+	}
+
+      if (dfa->subexp_map)
+	for (reg_idx = 0; reg_idx + 1 < nmatch; reg_idx++)
+	  if (dfa->subexp_map[reg_idx] != reg_idx)
+	    {
+	      pmatch[reg_idx + 1].rm_so
+		= pmatch[dfa->subexp_map[reg_idx] + 1].rm_so;
+	      pmatch[reg_idx + 1].rm_eo
+		= pmatch[dfa->subexp_map[reg_idx] + 1].rm_eo;
+	    }
+    }
+
+ free_return:
+  re_free (mctx.state_log);
+  if (dfa->nbackref)
+    match_ctx_free (&mctx);
+  re_string_destruct (&mctx.input);
+  return err;
+}
+
+static reg_errcode_t
+prune_impossible_nodes (re_match_context_t *mctx)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  int halt_node, match_last;
+  reg_errcode_t ret;
+  re_dfastate_t **sifted_states;
+  re_dfastate_t **lim_states = NULL;
+  re_sift_context_t sctx;
+#ifdef DEBUG
+  assert (mctx->state_log != NULL);
+#endif
+  match_last = mctx->match_last;
+  halt_node = mctx->last_node;
+
+  /* Avoid overflow.  */
+  if (BE (SIZE_MAX / sizeof (re_dfastate_t *) <= match_last, 0))
+    return REG_ESPACE;
+
+  sifted_states = re_malloc (re_dfastate_t *, match_last + 1);
+  if (BE (sifted_states == NULL, 0))
+    {
+      ret = REG_ESPACE;
+      goto free_return;
+    }
+  if (dfa->nbackref)
+    {
+      lim_states = re_malloc (re_dfastate_t *, match_last + 1);
+      if (BE (lim_states == NULL, 0))
+	{
+	  ret = REG_ESPACE;
+	  goto free_return;
+	}
+      while (1)
+	{
+	  memset (lim_states, '\0',
+		  sizeof (re_dfastate_t *) * (match_last + 1));
+	  sift_ctx_init (&sctx, sifted_states, lim_states, halt_node,
+			 match_last);
+	  ret = sift_states_backward (mctx, &sctx);
+	  re_node_set_free (&sctx.limits);
+	  if (BE (ret != REG_NOERROR, 0))
+	      goto free_return;
+	  if (sifted_states[0] != NULL || lim_states[0] != NULL)
+	    break;
+	  do
+	    {
+	      --match_last;
+	      if (match_last < 0)
+		{
+		  ret = REG_NOMATCH;
+		  goto free_return;
+		}
+	    } while (mctx->state_log[match_last] == NULL
+		     || !mctx->state_log[match_last]->halt);
+	  halt_node = check_halt_state_context (mctx,
+						mctx->state_log[match_last],
+						match_last);
+	}
+      ret = merge_state_array (dfa, sifted_states, lim_states,
+			       match_last + 1);
+      re_free (lim_states);
+      lim_states = NULL;
+      if (BE (ret != REG_NOERROR, 0))
+	goto free_return;
+    }
+  else
+    {
+      sift_ctx_init (&sctx, sifted_states, lim_states, halt_node, match_last);
+      ret = sift_states_backward (mctx, &sctx);
+      re_node_set_free (&sctx.limits);
+      if (BE (ret != REG_NOERROR, 0))
+	goto free_return;
+      if (sifted_states[0] == NULL)
+	{
+	  ret = REG_NOMATCH;
+	  goto free_return;
+	}
+    }
+  re_free (mctx->state_log);
+  mctx->state_log = sifted_states;
+  sifted_states = NULL;
+  mctx->last_node = halt_node;
+  mctx->match_last = match_last;
+  ret = REG_NOERROR;
+ free_return:
+  re_free (sifted_states);
+  re_free (lim_states);
+  return ret;
+}
+
+/* Acquire an initial state and return it.
+   We must select appropriate initial state depending on the context,
+   since initial states may have constraints like "\<", "^", etc..  */
+
+static inline re_dfastate_t *
+__attribute ((always_inline)) internal_function
+acquire_init_state_context (reg_errcode_t *err, const re_match_context_t *mctx,
+			    int idx)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  if (dfa->init_state->has_constraint)
+    {
+      unsigned int context;
+      context = re_string_context_at (&mctx->input, idx - 1, mctx->eflags);
+      if (IS_WORD_CONTEXT (context))
+	return dfa->init_state_word;
+      else if (IS_ORDINARY_CONTEXT (context))
+	return dfa->init_state;
+      else if (IS_BEGBUF_CONTEXT (context) && IS_NEWLINE_CONTEXT (context))
+	return dfa->init_state_begbuf;
+      else if (IS_NEWLINE_CONTEXT (context))
+	return dfa->init_state_nl;
+      else if (IS_BEGBUF_CONTEXT (context))
+	{
+	  /* It is relatively rare case, then calculate on demand.  */
+	  return re_acquire_state_context (err, dfa,
+					   dfa->init_state->entrance_nodes,
+					   context);
+	}
+      else
+	/* Must not happen?  */
+	return dfa->init_state;
+    }
+  else
+    return dfa->init_state;
+}
+
+/* Check whether the regular expression match input string INPUT or not,
+   and return the index where the matching end, return -1 if not match,
+   or return -2 in case of an error.
+   FL_LONGEST_MATCH means we want the POSIX longest matching.
+   If P_MATCH_FIRST is not NULL, and the match fails, it is set to the
+   next place where we may want to try matching.
+   Note that the matcher assume that the maching starts from the current
+   index of the buffer.  */
+
+static int
+internal_function
+check_matching (re_match_context_t *mctx, int fl_longest_match,
+		int *p_match_first)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  reg_errcode_t err;
+  int match = 0;
+  int match_last = -1;
+  int cur_str_idx = re_string_cur_idx (&mctx->input);
+  re_dfastate_t *cur_state;
+  int at_init_state = p_match_first != NULL;
+  int next_start_idx = cur_str_idx;
+
+  err = REG_NOERROR;
+  cur_state = acquire_init_state_context (&err, mctx, cur_str_idx);
+  /* An initial state must not be NULL (invalid).  */
+  if (BE (cur_state == NULL, 0))
+    {
+      assert (err == REG_ESPACE);
+      return -2;
+    }
+
+  if (mctx->state_log != NULL)
+    {
+      mctx->state_log[cur_str_idx] = cur_state;
+
+      /* Check OP_OPEN_SUBEXP in the initial state in case that we use them
+	 later.  E.g. Processing back references.  */
+      if (BE (dfa->nbackref, 0))
+	{
+	  at_init_state = 0;
+	  err = check_subexp_matching_top (mctx, &cur_state->nodes, 0);
+	  if (BE (err != REG_NOERROR, 0))
+	    return err;
+
+	  if (cur_state->has_backref)
+	    {
+	      err = transit_state_bkref (mctx, &cur_state->nodes);
+	      if (BE (err != REG_NOERROR, 0))
+		return err;
+	    }
+	}
+    }
+
+  /* If the RE accepts NULL string.  */
+  if (BE (cur_state->halt, 0))
+    {
+      if (!cur_state->has_constraint
+	  || check_halt_state_context (mctx, cur_state, cur_str_idx))
+	{
+	  if (!fl_longest_match)
+	    return cur_str_idx;
+	  else
+	    {
+	      match_last = cur_str_idx;
+	      match = 1;
+	    }
+	}
+    }
+
+  while (!re_string_eoi (&mctx->input))
+    {
+      re_dfastate_t *old_state = cur_state;
+      int next_char_idx = re_string_cur_idx (&mctx->input) + 1;
+
+      if (BE (next_char_idx >= mctx->input.bufs_len, 0)
+	  || (BE (next_char_idx >= mctx->input.valid_len, 0)
+	      && mctx->input.valid_len < mctx->input.len))
+	{
+	  err = extend_buffers (mctx);
+	  if (BE (err != REG_NOERROR, 0))
+	    {
+	      assert (err == REG_ESPACE);
+	      return -2;
+	    }
+	}
+
+      cur_state = transit_state (&err, mctx, cur_state);
+      if (mctx->state_log != NULL)
+	cur_state = merge_state_with_log (&err, mctx, cur_state);
+
+      if (cur_state == NULL)
+	{
+	  /* Reached the invalid state or an error.  Try to recover a valid
+	     state using the state log, if available and if we have not
+	     already found a valid (even if not the longest) match.  */
+	  if (BE (err != REG_NOERROR, 0))
+	    return -2;
+
+	  if (mctx->state_log == NULL
+	      || (match && !fl_longest_match)
+	      || (cur_state = find_recover_state (&err, mctx)) == NULL)
+	    break;
+	}
+
+      if (BE (at_init_state, 0))
+	{
+	  if (old_state == cur_state)
+	    next_start_idx = next_char_idx;
+	  else
+	    at_init_state = 0;
+	}
+
+      if (cur_state->halt)
+	{
+	  /* Reached a halt state.
+	     Check the halt state can satisfy the current context.  */
+	  if (!cur_state->has_constraint
+	      || check_halt_state_context (mctx, cur_state,
+					   re_string_cur_idx (&mctx->input)))
+	    {
+	      /* We found an appropriate halt state.  */
+	      match_last = re_string_cur_idx (&mctx->input);
+	      match = 1;
+
+	      /* We found a match, do not modify match_first below.  */
+	      p_match_first = NULL;
+	      if (!fl_longest_match)
+		break;
+	    }
+	}
+    }
+
+  if (p_match_first)
+    *p_match_first += next_start_idx;
+
+  return match_last;
+}
+
+/* Check NODE match the current context.  */
+
+static int
+internal_function
+check_halt_node_context (const re_dfa_t *dfa, int node, unsigned int context)
+{
+  re_token_type_t type = dfa->nodes[node].type;
+  unsigned int constraint = dfa->nodes[node].constraint;
+  if (type != END_OF_RE)
+    return 0;
+  if (!constraint)
+    return 1;
+  if (NOT_SATISFY_NEXT_CONSTRAINT (constraint, context))
+    return 0;
+  return 1;
+}
+
+/* Check the halt state STATE match the current context.
+   Return 0 if not match, if the node, STATE has, is a halt node and
+   match the context, return the node.  */
+
+static int
+internal_function
+check_halt_state_context (const re_match_context_t *mctx,
+			  const re_dfastate_t *state, int idx)
+{
+  int i;
+  unsigned int context;
+#ifdef DEBUG
+  assert (state->halt);
+#endif
+  context = re_string_context_at (&mctx->input, idx, mctx->eflags);
+  for (i = 0; i < state->nodes.nelem; ++i)
+    if (check_halt_node_context (mctx->dfa, state->nodes.elems[i], context))
+      return state->nodes.elems[i];
+  return 0;
+}
+
+/* Compute the next node to which "NFA" transit from NODE("NFA" is a NFA
+   corresponding to the DFA).
+   Return the destination node, and update EPS_VIA_NODES, return -1 in case
+   of errors.  */
+
+static int
+internal_function
+proceed_next_node (const re_match_context_t *mctx, int nregs, regmatch_t *regs,
+		   int *pidx, int node, re_node_set *eps_via_nodes,
+		   struct re_fail_stack_t *fs)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  int i, err;
+  if (IS_EPSILON_NODE (dfa->nodes[node].type))
+    {
+      re_node_set *cur_nodes = &mctx->state_log[*pidx]->nodes;
+      re_node_set *edests = &dfa->edests[node];
+      int dest_node;
+      err = re_node_set_insert (eps_via_nodes, node);
+      if (BE (err < 0, 0))
+	return -2;
+      /* Pick up a valid destination, or return -1 if none is found.  */
+      for (dest_node = -1, i = 0; i < edests->nelem; ++i)
+	{
+	  int candidate = edests->elems[i];
+	  if (!re_node_set_contains (cur_nodes, candidate))
+	    continue;
+	  if (dest_node == -1)
+	    dest_node = candidate;
+
+	  else
+	    {
+	      /* In order to avoid infinite loop like "(a*)*", return the second
+		 epsilon-transition if the first was already considered.  */
+	      if (re_node_set_contains (eps_via_nodes, dest_node))
+		return candidate;
+
+	      /* Otherwise, push the second epsilon-transition on the fail stack.  */
+	      else if (fs != NULL
+		       && push_fail_stack (fs, *pidx, candidate, nregs, regs,
+					   eps_via_nodes))
+		return -2;
+
+	      /* We know we are going to exit.  */
+	      break;
+	    }
+	}
+      return dest_node;
+    }
+  else
+    {
+      int naccepted = 0;
+      re_token_type_t type = dfa->nodes[node].type;
+
+#ifdef RE_ENABLE_I18N
+      if (dfa->nodes[node].accept_mb)
+	naccepted = check_node_accept_bytes (dfa, node, &mctx->input, *pidx);
+      else
+#endif /* RE_ENABLE_I18N */
+      if (type == OP_BACK_REF)
+	{
+	  int subexp_idx = dfa->nodes[node].opr.idx + 1;
+	  naccepted = regs[subexp_idx].rm_eo - regs[subexp_idx].rm_so;
+	  if (fs != NULL)
+	    {
+	      if (regs[subexp_idx].rm_so == -1 || regs[subexp_idx].rm_eo == -1)
+		return -1;
+	      else if (naccepted)
+		{
+		  char *buf = (char *) re_string_get_buffer (&mctx->input);
+		  if (memcmp (buf + regs[subexp_idx].rm_so, buf + *pidx,
+			      naccepted) != 0)
+		    return -1;
+		}
+	    }
+
+	  if (naccepted == 0)
+	    {
+	      int dest_node;
+	      err = re_node_set_insert (eps_via_nodes, node);
+	      if (BE (err < 0, 0))
+		return -2;
+	      dest_node = dfa->edests[node].elems[0];
+	      if (re_node_set_contains (&mctx->state_log[*pidx]->nodes,
+					dest_node))
+		return dest_node;
+	    }
+	}
+
+      if (naccepted != 0
+	  || check_node_accept (mctx, dfa->nodes + node, *pidx))
+	{
+	  int dest_node = dfa->nexts[node];
+	  *pidx = (naccepted == 0) ? *pidx + 1 : *pidx + naccepted;
+	  if (fs && (*pidx > mctx->match_last || mctx->state_log[*pidx] == NULL
+		     || !re_node_set_contains (&mctx->state_log[*pidx]->nodes,
+					       dest_node)))
+	    return -1;
+	  re_node_set_empty (eps_via_nodes);
+	  return dest_node;
+	}
+    }
+  return -1;
+}
+
+static reg_errcode_t
+internal_function
+push_fail_stack (struct re_fail_stack_t *fs, int str_idx, int dest_node,
+		 int nregs, regmatch_t *regs, re_node_set *eps_via_nodes)
+{
+  reg_errcode_t err;
+  int num = fs->num++;
+  if (fs->num == fs->alloc)
+    {
+      struct re_fail_stack_ent_t *new_array;
+      new_array = realloc (fs->stack, (sizeof (struct re_fail_stack_ent_t)
+				       * fs->alloc * 2));
+      if (new_array == NULL)
+	return REG_ESPACE;
+      fs->alloc *= 2;
+      fs->stack = new_array;
+    }
+  fs->stack[num].idx = str_idx;
+  fs->stack[num].node = dest_node;
+  fs->stack[num].regs = re_malloc (regmatch_t, nregs);
+  if (fs->stack[num].regs == NULL)
+    return REG_ESPACE;
+  memcpy (fs->stack[num].regs, regs, sizeof (regmatch_t) * nregs);
+  err = re_node_set_init_copy (&fs->stack[num].eps_via_nodes, eps_via_nodes);
+  return err;
+}
+
+static int
+internal_function
+pop_fail_stack (struct re_fail_stack_t *fs, int *pidx, int nregs,
+		regmatch_t *regs, re_node_set *eps_via_nodes)
+{
+  int num = --fs->num;
+  assert (num >= 0);
+  *pidx = fs->stack[num].idx;
+  memcpy (regs, fs->stack[num].regs, sizeof (regmatch_t) * nregs);
+  re_node_set_free (eps_via_nodes);
+  re_free (fs->stack[num].regs);
+  *eps_via_nodes = fs->stack[num].eps_via_nodes;
+  return fs->stack[num].node;
+}
+
+/* Set the positions where the subexpressions are starts/ends to registers
+   PMATCH.
+   Note: We assume that pmatch[0] is already set, and
+   pmatch[i].rm_so == pmatch[i].rm_eo == -1 for 0 < i < nmatch.  */
+
+static reg_errcode_t
+internal_function
+set_regs (const regex_t *preg, const re_match_context_t *mctx, size_t nmatch,
+	  regmatch_t *pmatch, int fl_backtrack)
+{
+  const re_dfa_t *dfa = (const re_dfa_t *) preg->buffer;
+  int idx, cur_node;
+  re_node_set eps_via_nodes;
+  struct re_fail_stack_t *fs;
+  struct re_fail_stack_t fs_body = { 0, 2, NULL };
+  regmatch_t *prev_idx_match;
+  int prev_idx_match_malloced = 0;
+
+#ifdef DEBUG
+  assert (nmatch > 1);
+  assert (mctx->state_log != NULL);
+#endif
+  if (fl_backtrack)
+    {
+      fs = &fs_body;
+      fs->stack = re_malloc (struct re_fail_stack_ent_t, fs->alloc);
+      if (fs->stack == NULL)
+	return REG_ESPACE;
+    }
+  else
+    fs = NULL;
+
+  cur_node = dfa->init_node;
+  re_node_set_init_empty (&eps_via_nodes);
+
+#ifdef HAVE_ALLOCA
+  if (__libc_use_alloca (nmatch * sizeof (regmatch_t)))
+    prev_idx_match = (regmatch_t *) alloca (nmatch * sizeof (regmatch_t));
+  else
+#endif
+    {
+      prev_idx_match = re_malloc (regmatch_t, nmatch);
+      if (prev_idx_match == NULL)
+	{
+	  free_fail_stack_return (fs);
+	  return REG_ESPACE;
+	}
+      prev_idx_match_malloced = 1;
+    }
+  memcpy (prev_idx_match, pmatch, sizeof (regmatch_t) * nmatch);
+
+  for (idx = pmatch[0].rm_so; idx <= pmatch[0].rm_eo ;)
+    {
+      update_regs (dfa, pmatch, prev_idx_match, cur_node, idx, nmatch);
+
+      if (idx == pmatch[0].rm_eo && cur_node == mctx->last_node)
+	{
+	  int reg_idx;
+	  if (fs)
+	    {
+	      for (reg_idx = 0; reg_idx < nmatch; ++reg_idx)
+		if (pmatch[reg_idx].rm_so > -1 && pmatch[reg_idx].rm_eo == -1)
+		  break;
+	      if (reg_idx == nmatch)
+		{
+		  re_node_set_free (&eps_via_nodes);
+		  if (prev_idx_match_malloced)
+		    re_free (prev_idx_match);
+		  return free_fail_stack_return (fs);
+		}
+	      cur_node = pop_fail_stack (fs, &idx, nmatch, pmatch,
+					 &eps_via_nodes);
+	    }
+	  else
+	    {
+	      re_node_set_free (&eps_via_nodes);
+	      if (prev_idx_match_malloced)
+		re_free (prev_idx_match);
+	      return REG_NOERROR;
+	    }
+	}
+
+      /* Proceed to next node.  */
+      cur_node = proceed_next_node (mctx, nmatch, pmatch, &idx, cur_node,
+				    &eps_via_nodes, fs);
+
+      if (BE (cur_node < 0, 0))
+	{
+	  if (BE (cur_node == -2, 0))
+	    {
+	      re_node_set_free (&eps_via_nodes);
+	      if (prev_idx_match_malloced)
+		re_free (prev_idx_match);
+	      free_fail_stack_return (fs);
+	      return REG_ESPACE;
+	    }
+	  if (fs)
+	    cur_node = pop_fail_stack (fs, &idx, nmatch, pmatch,
+				       &eps_via_nodes);
+	  else
+	    {
+	      re_node_set_free (&eps_via_nodes);
+	      if (prev_idx_match_malloced)
+		re_free (prev_idx_match);
+	      return REG_NOMATCH;
+	    }
+	}
+    }
+  re_node_set_free (&eps_via_nodes);
+  if (prev_idx_match_malloced)
+    re_free (prev_idx_match);
+  return free_fail_stack_return (fs);
+}
+
+static reg_errcode_t
+internal_function
+free_fail_stack_return (struct re_fail_stack_t *fs)
+{
+  if (fs)
+    {
+      int fs_idx;
+      for (fs_idx = 0; fs_idx < fs->num; ++fs_idx)
+	{
+	  re_node_set_free (&fs->stack[fs_idx].eps_via_nodes);
+	  re_free (fs->stack[fs_idx].regs);
+	}
+      re_free (fs->stack);
+    }
+  return REG_NOERROR;
+}
+
+static void
+internal_function
+update_regs (const re_dfa_t *dfa, regmatch_t *pmatch,
+	     regmatch_t *prev_idx_match, int cur_node, int cur_idx, int nmatch)
+{
+  int type = dfa->nodes[cur_node].type;
+  if (type == OP_OPEN_SUBEXP)
+    {
+      int reg_num = dfa->nodes[cur_node].opr.idx + 1;
+
+      /* We are at the first node of this sub expression.  */
+      if (reg_num < nmatch)
+	{
+	  pmatch[reg_num].rm_so = cur_idx;
+	  pmatch[reg_num].rm_eo = -1;
+	}
+    }
+  else if (type == OP_CLOSE_SUBEXP)
+    {
+      int reg_num = dfa->nodes[cur_node].opr.idx + 1;
+      if (reg_num < nmatch)
+	{
+	  /* We are at the last node of this sub expression.  */
+	  if (pmatch[reg_num].rm_so < cur_idx)
+	    {
+	      pmatch[reg_num].rm_eo = cur_idx;
+	      /* This is a non-empty match or we are not inside an optional
+		 subexpression.  Accept this right away.  */
+	      memcpy (prev_idx_match, pmatch, sizeof (regmatch_t) * nmatch);
+	    }
+	  else
+	    {
+	      if (dfa->nodes[cur_node].opt_subexp
+		  && prev_idx_match[reg_num].rm_so != -1)
+		/* We transited through an empty match for an optional
+		   subexpression, like (a?)*, and this is not the subexp's
+		   first match.  Copy back the old content of the registers
+		   so that matches of an inner subexpression are undone as
+		   well, like in ((a?))*.  */
+		memcpy (pmatch, prev_idx_match, sizeof (regmatch_t) * nmatch);
+	      else
+		/* We completed a subexpression, but it may be part of
+		   an optional one, so do not update PREV_IDX_MATCH.  */
+		pmatch[reg_num].rm_eo = cur_idx;
+	    }
+	}
+    }
+}
+
+/* This function checks the STATE_LOG from the SCTX->last_str_idx to 0
+   and sift the nodes in each states according to the following rules.
+   Updated state_log will be wrote to STATE_LOG.
+
+   Rules: We throw away the Node `a' in the STATE_LOG[STR_IDX] if...
+     1. When STR_IDX == MATCH_LAST(the last index in the state_log):
+	If `a' isn't the LAST_NODE and `a' can't epsilon transit to
+	the LAST_NODE, we throw away the node `a'.
+     2. When 0 <= STR_IDX < MATCH_LAST and `a' accepts
+	string `s' and transit to `b':
+	i. If 'b' isn't in the STATE_LOG[STR_IDX+strlen('s')], we throw
+	   away the node `a'.
+	ii. If 'b' is in the STATE_LOG[STR_IDX+strlen('s')] but 'b' is
+	    thrown away, we throw away the node `a'.
+     3. When 0 <= STR_IDX < MATCH_LAST and 'a' epsilon transit to 'b':
+	i. If 'b' isn't in the STATE_LOG[STR_IDX], we throw away the
+	   node `a'.
+	ii. If 'b' is in the STATE_LOG[STR_IDX] but 'b' is thrown away,
+	    we throw away the node `a'.  */
+
+#define STATE_NODE_CONTAINS(state,node) \
+  ((state) != NULL && re_node_set_contains (&(state)->nodes, node))
+
+static reg_errcode_t
+internal_function
+sift_states_backward (const re_match_context_t *mctx, re_sift_context_t *sctx)
+{
+  reg_errcode_t err;
+  int null_cnt = 0;
+  int str_idx = sctx->last_str_idx;
+  re_node_set cur_dest;
+
+#ifdef DEBUG
+  assert (mctx->state_log != NULL && mctx->state_log[str_idx] != NULL);
+#endif
+
+  /* Build sifted state_log[str_idx].  It has the nodes which can epsilon
+     transit to the last_node and the last_node itself.  */
+  err = re_node_set_init_1 (&cur_dest, sctx->last_node);
+  if (BE (err != REG_NOERROR, 0))
+    return err;
+  err = update_cur_sifted_state (mctx, sctx, str_idx, &cur_dest);
+  if (BE (err != REG_NOERROR, 0))
+    goto free_return;
+
+  /* Then check each states in the state_log.  */
+  while (str_idx > 0)
+    {
+      /* Update counters.  */
+      null_cnt = (sctx->sifted_states[str_idx] == NULL) ? null_cnt + 1 : 0;
+      if (null_cnt > mctx->max_mb_elem_len)
+	{
+	  memset (sctx->sifted_states, '\0',
+		  sizeof (re_dfastate_t *) * str_idx);
+	  re_node_set_free (&cur_dest);
+	  return REG_NOERROR;
+	}
+      re_node_set_empty (&cur_dest);
+      --str_idx;
+
+      if (mctx->state_log[str_idx])
+	{
+	  err = build_sifted_states (mctx, sctx, str_idx, &cur_dest);
+	  if (BE (err != REG_NOERROR, 0))
+	    goto free_return;
+	}
+
+      /* Add all the nodes which satisfy the following conditions:
+	 - It can epsilon transit to a node in CUR_DEST.
+	 - It is in CUR_SRC.
+	 And update state_log.  */
+      err = update_cur_sifted_state (mctx, sctx, str_idx, &cur_dest);
+      if (BE (err != REG_NOERROR, 0))
+	goto free_return;
+    }
+  err = REG_NOERROR;
+ free_return:
+  re_node_set_free (&cur_dest);
+  return err;
+}
+
+static reg_errcode_t
+internal_function
+build_sifted_states (const re_match_context_t *mctx, re_sift_context_t *sctx,
+		     int str_idx, re_node_set *cur_dest)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  const re_node_set *cur_src = &mctx->state_log[str_idx]->non_eps_nodes;
+  int i;
+
+  /* Then build the next sifted state.
+     We build the next sifted state on `cur_dest', and update
+     `sifted_states[str_idx]' with `cur_dest'.
+     Note:
+     `cur_dest' is the sifted state from `state_log[str_idx + 1]'.
+     `cur_src' points the node_set of the old `state_log[str_idx]'
+     (with the epsilon nodes pre-filtered out).  */
+  for (i = 0; i < cur_src->nelem; i++)
+    {
+      int prev_node = cur_src->elems[i];
+      int naccepted = 0;
+      int ret;
+
+#ifdef DEBUG
+      re_token_type_t type = dfa->nodes[prev_node].type;
+      assert (!IS_EPSILON_NODE (type));
+#endif
+#ifdef RE_ENABLE_I18N
+      /* If the node may accept `multi byte'.  */
+      if (dfa->nodes[prev_node].accept_mb)
+	naccepted = sift_states_iter_mb (mctx, sctx, prev_node,
+					 str_idx, sctx->last_str_idx);
+#endif /* RE_ENABLE_I18N */
+
+      /* We don't check backreferences here.
+	 See update_cur_sifted_state().  */
+      if (!naccepted
+	  && check_node_accept (mctx, dfa->nodes + prev_node, str_idx)
+	  && STATE_NODE_CONTAINS (sctx->sifted_states[str_idx + 1],
+				  dfa->nexts[prev_node]))
+	naccepted = 1;
+
+      if (naccepted == 0)
+	continue;
+
+      if (sctx->limits.nelem)
+	{
+	  int to_idx = str_idx + naccepted;
+	  if (check_dst_limits (mctx, &sctx->limits,
+				dfa->nexts[prev_node], to_idx,
+				prev_node, str_idx))
+	    continue;
+	}
+      ret = re_node_set_insert (cur_dest, prev_node);
+      if (BE (ret == -1, 0))
+	return REG_ESPACE;
+    }
+
+  return REG_NOERROR;
+}
+
+/* Helper functions.  */
+
+static reg_errcode_t
+internal_function
+clean_state_log_if_needed (re_match_context_t *mctx, int next_state_log_idx)
+{
+  int top = mctx->state_log_top;
+
+  if (next_state_log_idx >= mctx->input.bufs_len
+      || (next_state_log_idx >= mctx->input.valid_len
+	  && mctx->input.valid_len < mctx->input.len))
+    {
+      reg_errcode_t err;
+      err = extend_buffers (mctx);
+      if (BE (err != REG_NOERROR, 0))
+	return err;
+    }
+
+  if (top < next_state_log_idx)
+    {
+      memset (mctx->state_log + top + 1, '\0',
+	      sizeof (re_dfastate_t *) * (next_state_log_idx - top));
+      mctx->state_log_top = next_state_log_idx;
+    }
+  return REG_NOERROR;
+}
+
+static reg_errcode_t
+internal_function
+merge_state_array (const re_dfa_t *dfa, re_dfastate_t **dst,
+		   re_dfastate_t **src, int num)
+{
+  int st_idx;
+  reg_errcode_t err;
+  for (st_idx = 0; st_idx < num; ++st_idx)
+    {
+      if (dst[st_idx] == NULL)
+	dst[st_idx] = src[st_idx];
+      else if (src[st_idx] != NULL)
+	{
+	  re_node_set merged_set;
+	  err = re_node_set_init_union (&merged_set, &dst[st_idx]->nodes,
+					&src[st_idx]->nodes);
+	  if (BE (err != REG_NOERROR, 0))
+	    return err;
+	  dst[st_idx] = re_acquire_state (&err, dfa, &merged_set);
+	  re_node_set_free (&merged_set);
+	  if (BE (err != REG_NOERROR, 0))
+	    return err;
+	}
+    }
+  return REG_NOERROR;
+}
+
+static reg_errcode_t
+internal_function
+update_cur_sifted_state (const re_match_context_t *mctx,
+			 re_sift_context_t *sctx, int str_idx,
+			 re_node_set *dest_nodes)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  reg_errcode_t err = REG_NOERROR;
+  const re_node_set *candidates;
+  candidates = ((mctx->state_log[str_idx] == NULL) ? NULL
+		: &mctx->state_log[str_idx]->nodes);
+
+  if (dest_nodes->nelem == 0)
+    sctx->sifted_states[str_idx] = NULL;
+  else
+    {
+      if (candidates)
+	{
+	  /* At first, add the nodes which can epsilon transit to a node in
+	     DEST_NODE.  */
+	  err = add_epsilon_src_nodes (dfa, dest_nodes, candidates);
+	  if (BE (err != REG_NOERROR, 0))
+	    return err;
+
+	  /* Then, check the limitations in the current sift_context.  */
+	  if (sctx->limits.nelem)
+	    {
+	      err = check_subexp_limits (dfa, dest_nodes, candidates, &sctx->limits,
+					 mctx->bkref_ents, str_idx);
+	      if (BE (err != REG_NOERROR, 0))
+		return err;
+	    }
+	}
+
+      sctx->sifted_states[str_idx] = re_acquire_state (&err, dfa, dest_nodes);
+      if (BE (err != REG_NOERROR, 0))
+	return err;
+    }
+
+  if (candidates && mctx->state_log[str_idx]->has_backref)
+    {
+      err = sift_states_bkref (mctx, sctx, str_idx, candidates);
+      if (BE (err != REG_NOERROR, 0))
+	return err;
+    }
+  return REG_NOERROR;
+}
+
+static reg_errcode_t
+internal_function
+add_epsilon_src_nodes (const re_dfa_t *dfa, re_node_set *dest_nodes,
+		       const re_node_set *candidates)
+{
+  reg_errcode_t err = REG_NOERROR;
+  int i;
+
+  re_dfastate_t *state = re_acquire_state (&err, dfa, dest_nodes);
+  if (BE (err != REG_NOERROR, 0))
+    return err;
+
+  if (!state->inveclosure.alloc)
+    {
+      err = re_node_set_alloc (&state->inveclosure, dest_nodes->nelem);
+      if (BE (err != REG_NOERROR, 0))
+	return REG_ESPACE;
+      for (i = 0; i < dest_nodes->nelem; i++)
+	{
+	  err = re_node_set_merge (&state->inveclosure,
+				   dfa->inveclosures + dest_nodes->elems[i]);
+	  if (BE (err != REG_NOERROR, 0))
+	    return REG_ESPACE;
+	}
+    }
+  return re_node_set_add_intersect (dest_nodes, candidates,
+				    &state->inveclosure);
+}
+
+static reg_errcode_t
+internal_function
+sub_epsilon_src_nodes (const re_dfa_t *dfa, int node, re_node_set *dest_nodes,
+		       const re_node_set *candidates)
+{
+    int ecl_idx;
+    reg_errcode_t err;
+    re_node_set *inv_eclosure = dfa->inveclosures + node;
+    re_node_set except_nodes;
+    re_node_set_init_empty (&except_nodes);
+    for (ecl_idx = 0; ecl_idx < inv_eclosure->nelem; ++ecl_idx)
+      {
+	int cur_node = inv_eclosure->elems[ecl_idx];
+	if (cur_node == node)
+	  continue;
+	if (IS_EPSILON_NODE (dfa->nodes[cur_node].type))
+	  {
+	    int edst1 = dfa->edests[cur_node].elems[0];
+	    int edst2 = ((dfa->edests[cur_node].nelem > 1)
+			 ? dfa->edests[cur_node].elems[1] : -1);
+	    if ((!re_node_set_contains (inv_eclosure, edst1)
+		 && re_node_set_contains (dest_nodes, edst1))
+		|| (edst2 > 0
+		    && !re_node_set_contains (inv_eclosure, edst2)
+		    && re_node_set_contains (dest_nodes, edst2)))
+	      {
+		err = re_node_set_add_intersect (&except_nodes, candidates,
+						 dfa->inveclosures + cur_node);
+		if (BE (err != REG_NOERROR, 0))
+		  {
+		    re_node_set_free (&except_nodes);
+		    return err;
+		  }
+	      }
+	  }
+      }
+    for (ecl_idx = 0; ecl_idx < inv_eclosure->nelem; ++ecl_idx)
+      {
+	int cur_node = inv_eclosure->elems[ecl_idx];
+	if (!re_node_set_contains (&except_nodes, cur_node))
+	  {
+	    int idx = re_node_set_contains (dest_nodes, cur_node) - 1;
+	    re_node_set_remove_at (dest_nodes, idx);
+	  }
+      }
+    re_node_set_free (&except_nodes);
+    return REG_NOERROR;
+}
+
+static int
+internal_function
+check_dst_limits (const re_match_context_t *mctx, re_node_set *limits,
+		  int dst_node, int dst_idx, int src_node, int src_idx)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  int lim_idx, src_pos, dst_pos;
+
+  int dst_bkref_idx = search_cur_bkref_entry (mctx, dst_idx);
+  int src_bkref_idx = search_cur_bkref_entry (mctx, src_idx);
+  for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx)
+    {
+      int subexp_idx;
+      struct re_backref_cache_entry *ent;
+      ent = mctx->bkref_ents + limits->elems[lim_idx];
+      subexp_idx = dfa->nodes[ent->node].opr.idx;
+
+      dst_pos = check_dst_limits_calc_pos (mctx, limits->elems[lim_idx],
+					   subexp_idx, dst_node, dst_idx,
+					   dst_bkref_idx);
+      src_pos = check_dst_limits_calc_pos (mctx, limits->elems[lim_idx],
+					   subexp_idx, src_node, src_idx,
+					   src_bkref_idx);
+
+      /* In case of:
+	 <src> <dst> ( <subexp> )
+	 ( <subexp> ) <src> <dst>
+	 ( <subexp1> <src> <subexp2> <dst> <subexp3> )  */
+      if (src_pos == dst_pos)
+	continue; /* This is unrelated limitation.  */
+      else
+	return 1;
+    }
+  return 0;
+}
+
+static int
+internal_function
+check_dst_limits_calc_pos_1 (const re_match_context_t *mctx, int boundaries,
+			     int subexp_idx, int from_node, int bkref_idx)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  const re_node_set *eclosures = dfa->eclosures + from_node;
+  int node_idx;
+
+  /* Else, we are on the boundary: examine the nodes on the epsilon
+     closure.  */
+  for (node_idx = 0; node_idx < eclosures->nelem; ++node_idx)
+    {
+      int node = eclosures->elems[node_idx];
+      switch (dfa->nodes[node].type)
+	{
+	case OP_BACK_REF:
+	  if (bkref_idx != -1)
+	    {
+	      struct re_backref_cache_entry *ent = mctx->bkref_ents + bkref_idx;
+	      do
+		{
+		  int dst, cpos;
+
+		  if (ent->node != node)
+		    continue;
+
+		  if (subexp_idx < BITSET_WORD_BITS
+		      && !(ent->eps_reachable_subexps_map
+			   & ((bitset_word_t) 1 << subexp_idx)))
+		    continue;
+
+		  /* Recurse trying to reach the OP_OPEN_SUBEXP and
+		     OP_CLOSE_SUBEXP cases below.  But, if the
+		     destination node is the same node as the source
+		     node, don't recurse because it would cause an
+		     infinite loop: a regex that exhibits this behavior
+		     is ()\1*\1*  */
+		  dst = dfa->edests[node].elems[0];
+		  if (dst == from_node)
+		    {
+		      if (boundaries & 1)
+			return -1;
+		      else /* if (boundaries & 2) */
+			return 0;
+		    }
+
+		  cpos =
+		    check_dst_limits_calc_pos_1 (mctx, boundaries, subexp_idx,
+						 dst, bkref_idx);
+		  if (cpos == -1 /* && (boundaries & 1) */)
+		    return -1;
+		  if (cpos == 0 && (boundaries & 2))
+		    return 0;
+
+		  if (subexp_idx < BITSET_WORD_BITS)
+		    ent->eps_reachable_subexps_map
+		      &= ~((bitset_word_t) 1 << subexp_idx);
+		}
+	      while (ent++->more);
+	    }
+	  break;
+
+	case OP_OPEN_SUBEXP:
+	  if ((boundaries & 1) && subexp_idx == dfa->nodes[node].opr.idx)
+	    return -1;
+	  break;
+
+	case OP_CLOSE_SUBEXP:
+	  if ((boundaries & 2) && subexp_idx == dfa->nodes[node].opr.idx)
+	    return 0;
+	  break;
+
+	default:
+	    break;
+	}
+    }
+
+  return (boundaries & 2) ? 1 : 0;
+}
+
+static int
+internal_function
+check_dst_limits_calc_pos (const re_match_context_t *mctx, int limit,
+			   int subexp_idx, int from_node, int str_idx,
+			   int bkref_idx)
+{
+  struct re_backref_cache_entry *lim = mctx->bkref_ents + limit;
+  int boundaries;
+
+  /* If we are outside the range of the subexpression, return -1 or 1.  */
+  if (str_idx < lim->subexp_from)
+    return -1;
+
+  if (lim->subexp_to < str_idx)
+    return 1;
+
+  /* If we are within the subexpression, return 0.  */
+  boundaries = (str_idx == lim->subexp_from);
+  boundaries |= (str_idx == lim->subexp_to) << 1;
+  if (boundaries == 0)
+    return 0;
+
+  /* Else, examine epsilon closure.  */
+  return check_dst_limits_calc_pos_1 (mctx, boundaries, subexp_idx,
+				      from_node, bkref_idx);
+}
+
+/* Check the limitations of sub expressions LIMITS, and remove the nodes
+   which are against limitations from DEST_NODES. */
+
+static reg_errcode_t
+internal_function
+check_subexp_limits (const re_dfa_t *dfa, re_node_set *dest_nodes,
+		     const re_node_set *candidates, re_node_set *limits,
+		     struct re_backref_cache_entry *bkref_ents, int str_idx)
+{
+  reg_errcode_t err;
+  int node_idx, lim_idx;
+
+  for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx)
+    {
+      int subexp_idx;
+      struct re_backref_cache_entry *ent;
+      ent = bkref_ents + limits->elems[lim_idx];
+
+      if (str_idx <= ent->subexp_from || ent->str_idx < str_idx)
+	continue; /* This is unrelated limitation.  */
+
+      subexp_idx = dfa->nodes[ent->node].opr.idx;
+      if (ent->subexp_to == str_idx)
+	{
+	  int ops_node = -1;
+	  int cls_node = -1;
+	  for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx)
+	    {
+	      int node = dest_nodes->elems[node_idx];
+	      re_token_type_t type = dfa->nodes[node].type;
+	      if (type == OP_OPEN_SUBEXP
+		  && subexp_idx == dfa->nodes[node].opr.idx)
+		ops_node = node;
+	      else if (type == OP_CLOSE_SUBEXP
+		       && subexp_idx == dfa->nodes[node].opr.idx)
+		cls_node = node;
+	    }
+
+	  /* Check the limitation of the open subexpression.  */
+	  /* Note that (ent->subexp_to = str_idx != ent->subexp_from).  */
+	  if (ops_node >= 0)
+	    {
+	      err = sub_epsilon_src_nodes (dfa, ops_node, dest_nodes,
+					   candidates);
+	      if (BE (err != REG_NOERROR, 0))
+		return err;
+	    }
+
+	  /* Check the limitation of the close subexpression.  */
+	  if (cls_node >= 0)
+	    for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx)
+	      {
+		int node = dest_nodes->elems[node_idx];
+		if (!re_node_set_contains (dfa->inveclosures + node,
+					   cls_node)
+		    && !re_node_set_contains (dfa->eclosures + node,
+					      cls_node))
+		  {
+		    /* It is against this limitation.
+		       Remove it form the current sifted state.  */
+		    err = sub_epsilon_src_nodes (dfa, node, dest_nodes,
+						 candidates);
+		    if (BE (err != REG_NOERROR, 0))
+		      return err;
+		    --node_idx;
+		  }
+	      }
+	}
+      else /* (ent->subexp_to != str_idx)  */
+	{
+	  for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx)
+	    {
+	      int node = dest_nodes->elems[node_idx];
+	      re_token_type_t type = dfa->nodes[node].type;
+	      if (type == OP_CLOSE_SUBEXP || type == OP_OPEN_SUBEXP)
+		{
+		  if (subexp_idx != dfa->nodes[node].opr.idx)
+		    continue;
+		  /* It is against this limitation.
+		     Remove it form the current sifted state.  */
+		  err = sub_epsilon_src_nodes (dfa, node, dest_nodes,
+					       candidates);
+		  if (BE (err != REG_NOERROR, 0))
+		    return err;
+		}
+	    }
+	}
+    }
+  return REG_NOERROR;
+}
+
+static reg_errcode_t
+internal_function
+sift_states_bkref (const re_match_context_t *mctx, re_sift_context_t *sctx,
+		   int str_idx, const re_node_set *candidates)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  reg_errcode_t err;
+  int node_idx, node;
+  re_sift_context_t local_sctx;
+  int first_idx = search_cur_bkref_entry (mctx, str_idx);
+
+  if (first_idx == -1)
+    return REG_NOERROR;
+
+  local_sctx.sifted_states = NULL; /* Mark that it hasn't been initialized.  */
+
+  for (node_idx = 0; node_idx < candidates->nelem; ++node_idx)
+    {
+      int enabled_idx;
+      re_token_type_t type;
+      struct re_backref_cache_entry *entry;
+      node = candidates->elems[node_idx];
+      type = dfa->nodes[node].type;
+      /* Avoid infinite loop for the REs like "()\1+".  */
+      if (node == sctx->last_node && str_idx == sctx->last_str_idx)
+	continue;
+      if (type != OP_BACK_REF)
+	continue;
+
+      entry = mctx->bkref_ents + first_idx;
+      enabled_idx = first_idx;
+      do
+	{
+	  int subexp_len;
+	  int to_idx;
+	  int dst_node;
+	  int ret;
+	  re_dfastate_t *cur_state;
+
+	  if (entry->node != node)
+	    continue;
+	  subexp_len = entry->subexp_to - entry->subexp_from;
+	  to_idx = str_idx + subexp_len;
+	  dst_node = (subexp_len ? dfa->nexts[node]
+		      : dfa->edests[node].elems[0]);
+
+	  if (to_idx > sctx->last_str_idx
+	      || sctx->sifted_states[to_idx] == NULL
+	      || !STATE_NODE_CONTAINS (sctx->sifted_states[to_idx], dst_node)
+	      || check_dst_limits (mctx, &sctx->limits, node,
+				   str_idx, dst_node, to_idx))
+	    continue;
+
+	  if (local_sctx.sifted_states == NULL)
+	    {
+	      local_sctx = *sctx;
+	      err = re_node_set_init_copy (&local_sctx.limits, &sctx->limits);
+	      if (BE (err != REG_NOERROR, 0))
+		goto free_return;
+	    }
+	  local_sctx.last_node = node;
+	  local_sctx.last_str_idx = str_idx;
+	  ret = re_node_set_insert (&local_sctx.limits, enabled_idx);
+	  if (BE (ret < 0, 0))
+	    {
+	      err = REG_ESPACE;
+	      goto free_return;
+	    }
+	  cur_state = local_sctx.sifted_states[str_idx];
+	  err = sift_states_backward (mctx, &local_sctx);
+	  if (BE (err != REG_NOERROR, 0))
+	    goto free_return;
+	  if (sctx->limited_states != NULL)
+	    {
+	      err = merge_state_array (dfa, sctx->limited_states,
+				       local_sctx.sifted_states,
+				       str_idx + 1);
+	      if (BE (err != REG_NOERROR, 0))
+		goto free_return;
+	    }
+	  local_sctx.sifted_states[str_idx] = cur_state;
+	  re_node_set_remove (&local_sctx.limits, enabled_idx);
+
+	  /* mctx->bkref_ents may have changed, reload the pointer.  */
+	  entry = mctx->bkref_ents + enabled_idx;
+	}
+      while (enabled_idx++, entry++->more);
+    }
+  err = REG_NOERROR;
+ free_return:
+  if (local_sctx.sifted_states != NULL)
+    {
+      re_node_set_free (&local_sctx.limits);
+    }
+
+  return err;
+}
+
+
+#ifdef RE_ENABLE_I18N
+static int
+internal_function
+sift_states_iter_mb (const re_match_context_t *mctx, re_sift_context_t *sctx,
+		     int node_idx, int str_idx, int max_str_idx)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  int naccepted;
+  /* Check the node can accept `multi byte'.  */
+  naccepted = check_node_accept_bytes (dfa, node_idx, &mctx->input, str_idx);
+  if (naccepted > 0 && str_idx + naccepted <= max_str_idx &&
+      !STATE_NODE_CONTAINS (sctx->sifted_states[str_idx + naccepted],
+			    dfa->nexts[node_idx]))
+    /* The node can't accept the `multi byte', or the
+       destination was already thrown away, then the node
+       could't accept the current input `multi byte'.   */
+    naccepted = 0;
+  /* Otherwise, it is sure that the node could accept
+     `naccepted' bytes input.  */
+  return naccepted;
+}
+#endif /* RE_ENABLE_I18N */
+
+
+/* Functions for state transition.  */
+
+/* Return the next state to which the current state STATE will transit by
+   accepting the current input byte, and update STATE_LOG if necessary.
+   If STATE can accept a multibyte char/collating element/back reference
+   update the destination of STATE_LOG.  */
+
+static re_dfastate_t *
+internal_function
+transit_state (reg_errcode_t *err, re_match_context_t *mctx,
+	       re_dfastate_t *state)
+{
+  re_dfastate_t **trtable;
+  unsigned char ch;
+
+#ifdef RE_ENABLE_I18N
+  /* If the current state can accept multibyte.  */
+  if (BE (state->accept_mb, 0))
+    {
+      *err = transit_state_mb (mctx, state);
+      if (BE (*err != REG_NOERROR, 0))
+	return NULL;
+    }
+#endif /* RE_ENABLE_I18N */
+
+  /* Then decide the next state with the single byte.  */
+#if 0
+  if (0)
+    /* don't use transition table  */
+    return transit_state_sb (err, mctx, state);
+#endif
+
+  /* Use transition table  */
+  ch = re_string_fetch_byte (&mctx->input);
+  for (;;)
+    {
+      trtable = state->trtable;
+      if (BE (trtable != NULL, 1))
+	return trtable[ch];
+
+      trtable = state->word_trtable;
+      if (BE (trtable != NULL, 1))
+	{
+	  unsigned int context;
+	  context
+	    = re_string_context_at (&mctx->input,
+				    re_string_cur_idx (&mctx->input) - 1,
+				    mctx->eflags);
+	  if (IS_WORD_CONTEXT (context))
+	    return trtable[ch + SBC_MAX];
+	  else
+	    return trtable[ch];
+	}
+
+      if (!build_trtable (mctx->dfa, state))
+	{
+	  *err = REG_ESPACE;
+	  return NULL;
+	}
+
+      /* Retry, we now have a transition table.  */
+    }
+}
+
+/* Update the state_log if we need */
+re_dfastate_t *
+internal_function
+merge_state_with_log (reg_errcode_t *err, re_match_context_t *mctx,
+		      re_dfastate_t *next_state)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  int cur_idx = re_string_cur_idx (&mctx->input);
+
+  if (cur_idx > mctx->state_log_top)
+    {
+      mctx->state_log[cur_idx] = next_state;
+      mctx->state_log_top = cur_idx;
+    }
+  else if (mctx->state_log[cur_idx] == 0)
+    {
+      mctx->state_log[cur_idx] = next_state;
+    }
+  else
+    {
+      re_dfastate_t *pstate;
+      unsigned int context;
+      re_node_set next_nodes, *log_nodes, *table_nodes = NULL;
+      /* If (state_log[cur_idx] != 0), it implies that cur_idx is
+	 the destination of a multibyte char/collating element/
+	 back reference.  Then the next state is the union set of
+	 these destinations and the results of the transition table.  */
+      pstate = mctx->state_log[cur_idx];
+      log_nodes = pstate->entrance_nodes;
+      if (next_state != NULL)
+	{
+	  table_nodes = next_state->entrance_nodes;
+	  *err = re_node_set_init_union (&next_nodes, table_nodes,
+					     log_nodes);
+	  if (BE (*err != REG_NOERROR, 0))
+	    return NULL;
+	}
+      else
+	next_nodes = *log_nodes;
+      /* Note: We already add the nodes of the initial state,
+	 then we don't need to add them here.  */
+
+      context = re_string_context_at (&mctx->input,
+				      re_string_cur_idx (&mctx->input) - 1,
+				      mctx->eflags);
+      next_state = mctx->state_log[cur_idx]
+	= re_acquire_state_context (err, dfa, &next_nodes, context);
+      /* We don't need to check errors here, since the return value of
+	 this function is next_state and ERR is already set.  */
+
+      if (table_nodes != NULL)
+	re_node_set_free (&next_nodes);
+    }
+
+  if (BE (dfa->nbackref, 0) && next_state != NULL)
+    {
+      /* Check OP_OPEN_SUBEXP in the current state in case that we use them
+	 later.  We must check them here, since the back references in the
+	 next state might use them.  */
+      *err = check_subexp_matching_top (mctx, &next_state->nodes,
+					cur_idx);
+      if (BE (*err != REG_NOERROR, 0))
+	return NULL;
+
+      /* If the next state has back references.  */
+      if (next_state->has_backref)
+	{
+	  *err = transit_state_bkref (mctx, &next_state->nodes);
+	  if (BE (*err != REG_NOERROR, 0))
+	    return NULL;
+	  next_state = mctx->state_log[cur_idx];
+	}
+    }
+
+  return next_state;
+}
+
+/* Skip bytes in the input that correspond to part of a
+   multi-byte match, then look in the log for a state
+   from which to restart matching.  */
+re_dfastate_t *
+internal_function
+find_recover_state (reg_errcode_t *err, re_match_context_t *mctx)
+{
+  re_dfastate_t *cur_state;
+  do
+    {
+      int max = mctx->state_log_top;
+      int cur_str_idx = re_string_cur_idx (&mctx->input);
+
+      do
+	{
+	  if (++cur_str_idx > max)
+	    return NULL;
+	  re_string_skip_bytes (&mctx->input, 1);
+	}
+      while (mctx->state_log[cur_str_idx] == NULL);
+
+      cur_state = merge_state_with_log (err, mctx, NULL);
+    }
+  while (*err == REG_NOERROR && cur_state == NULL);
+  return cur_state;
+}
+
+/* Helper functions for transit_state.  */
+
+/* From the node set CUR_NODES, pick up the nodes whose types are
+   OP_OPEN_SUBEXP and which have corresponding back references in the regular
+   expression. And register them to use them later for evaluating the
+   correspoding back references.  */
+
+static reg_errcode_t
+internal_function
+check_subexp_matching_top (re_match_context_t *mctx, re_node_set *cur_nodes,
+			   int str_idx)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  int node_idx;
+  reg_errcode_t err;
+
+  /* TODO: This isn't efficient.
+	   Because there might be more than one nodes whose types are
+	   OP_OPEN_SUBEXP and whose index is SUBEXP_IDX, we must check all
+	   nodes.
+	   E.g. RE: (a){2}  */
+  for (node_idx = 0; node_idx < cur_nodes->nelem; ++node_idx)
+    {
+      int node = cur_nodes->elems[node_idx];
+      if (dfa->nodes[node].type == OP_OPEN_SUBEXP
+	  && dfa->nodes[node].opr.idx < BITSET_WORD_BITS
+	  && (dfa->used_bkref_map
+	      & ((bitset_word_t) 1 << dfa->nodes[node].opr.idx)))
+	{
+	  err = match_ctx_add_subtop (mctx, node, str_idx);
+	  if (BE (err != REG_NOERROR, 0))
+	    return err;
+	}
+    }
+  return REG_NOERROR;
+}
+
+#if 0
+/* Return the next state to which the current state STATE will transit by
+   accepting the current input byte.  */
+
+static re_dfastate_t *
+transit_state_sb (reg_errcode_t *err, re_match_context_t *mctx,
+		  re_dfastate_t *state)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  re_node_set next_nodes;
+  re_dfastate_t *next_state;
+  int node_cnt, cur_str_idx = re_string_cur_idx (&mctx->input);
+  unsigned int context;
+
+  *err = re_node_set_alloc (&next_nodes, state->nodes.nelem + 1);
+  if (BE (*err != REG_NOERROR, 0))
+    return NULL;
+  for (node_cnt = 0; node_cnt < state->nodes.nelem; ++node_cnt)
+    {
+      int cur_node = state->nodes.elems[node_cnt];
+      if (check_node_accept (mctx, dfa->nodes + cur_node, cur_str_idx))
+	{
+	  *err = re_node_set_merge (&next_nodes,
+				    dfa->eclosures + dfa->nexts[cur_node]);
+	  if (BE (*err != REG_NOERROR, 0))
+	    {
+	      re_node_set_free (&next_nodes);
+	      return NULL;
+	    }
+	}
+    }
+  context = re_string_context_at (&mctx->input, cur_str_idx, mctx->eflags);
+  next_state = re_acquire_state_context (err, dfa, &next_nodes, context);
+  /* We don't need to check errors here, since the return value of
+     this function is next_state and ERR is already set.  */
+
+  re_node_set_free (&next_nodes);
+  re_string_skip_bytes (&mctx->input, 1);
+  return next_state;
+}
+#endif
+
+#ifdef RE_ENABLE_I18N
+static reg_errcode_t
+internal_function
+transit_state_mb (re_match_context_t *mctx, re_dfastate_t *pstate)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  reg_errcode_t err;
+  int i;
+
+  for (i = 0; i < pstate->nodes.nelem; ++i)
+    {
+      re_node_set dest_nodes, *new_nodes;
+      int cur_node_idx = pstate->nodes.elems[i];
+      int naccepted, dest_idx;
+      unsigned int context;
+      re_dfastate_t *dest_state;
+
+      if (!dfa->nodes[cur_node_idx].accept_mb)
+	continue;
+
+      if (dfa->nodes[cur_node_idx].constraint)
+	{
+	  context = re_string_context_at (&mctx->input,
+					  re_string_cur_idx (&mctx->input),
+					  mctx->eflags);
+	  if (NOT_SATISFY_NEXT_CONSTRAINT (dfa->nodes[cur_node_idx].constraint,
+					   context))
+	    continue;
+	}
+
+      /* How many bytes the node can accept?  */
+      naccepted = check_node_accept_bytes (dfa, cur_node_idx, &mctx->input,
+					   re_string_cur_idx (&mctx->input));
+      if (naccepted == 0)
+	continue;
+
+      /* The node can accepts `naccepted' bytes.  */
+      dest_idx = re_string_cur_idx (&mctx->input) + naccepted;
+      mctx->max_mb_elem_len = ((mctx->max_mb_elem_len < naccepted) ? naccepted
+			       : mctx->max_mb_elem_len);
+      err = clean_state_log_if_needed (mctx, dest_idx);
+      if (BE (err != REG_NOERROR, 0))
+	return err;
+#ifdef DEBUG
+      assert (dfa->nexts[cur_node_idx] != -1);
+#endif
+      new_nodes = dfa->eclosures + dfa->nexts[cur_node_idx];
+
+      dest_state = mctx->state_log[dest_idx];
+      if (dest_state == NULL)
+	dest_nodes = *new_nodes;
+      else
+	{
+	  err = re_node_set_init_union (&dest_nodes,
+					dest_state->entrance_nodes, new_nodes);
+	  if (BE (err != REG_NOERROR, 0))
+	    return err;
+	}
+      context = re_string_context_at (&mctx->input, dest_idx - 1,
+				      mctx->eflags);
+      mctx->state_log[dest_idx]
+	= re_acquire_state_context (&err, dfa, &dest_nodes, context);
+      if (dest_state != NULL)
+	re_node_set_free (&dest_nodes);
+      if (BE (mctx->state_log[dest_idx] == NULL && err != REG_NOERROR, 0))
+	return err;
+    }
+  return REG_NOERROR;
+}
+#endif /* RE_ENABLE_I18N */
+
+static reg_errcode_t
+internal_function
+transit_state_bkref (re_match_context_t *mctx, const re_node_set *nodes)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  reg_errcode_t err;
+  int i;
+  int cur_str_idx = re_string_cur_idx (&mctx->input);
+
+  for (i = 0; i < nodes->nelem; ++i)
+    {
+      int dest_str_idx, prev_nelem, bkc_idx;
+      int node_idx = nodes->elems[i];
+      unsigned int context;
+      const re_token_t *node = dfa->nodes + node_idx;
+      re_node_set *new_dest_nodes;
+
+      /* Check whether `node' is a backreference or not.  */
+      if (node->type != OP_BACK_REF)
+	continue;
+
+      if (node->constraint)
+	{
+	  context = re_string_context_at (&mctx->input, cur_str_idx,
+					  mctx->eflags);
+	  if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context))
+	    continue;
+	}
+
+      /* `node' is a backreference.
+	 Check the substring which the substring matched.  */
+      bkc_idx = mctx->nbkref_ents;
+      err = get_subexp (mctx, node_idx, cur_str_idx);
+      if (BE (err != REG_NOERROR, 0))
+	goto free_return;
+
+      /* And add the epsilon closures (which is `new_dest_nodes') of
+	 the backreference to appropriate state_log.  */
+#ifdef DEBUG
+      assert (dfa->nexts[node_idx] != -1);
+#endif
+      for (; bkc_idx < mctx->nbkref_ents; ++bkc_idx)
+	{
+	  int subexp_len;
+	  re_dfastate_t *dest_state;
+	  struct re_backref_cache_entry *bkref_ent;
+	  bkref_ent = mctx->bkref_ents + bkc_idx;
+	  if (bkref_ent->node != node_idx || bkref_ent->str_idx != cur_str_idx)
+	    continue;
+	  subexp_len = bkref_ent->subexp_to - bkref_ent->subexp_from;
+	  new_dest_nodes = (subexp_len == 0
+			    ? dfa->eclosures + dfa->edests[node_idx].elems[0]
+			    : dfa->eclosures + dfa->nexts[node_idx]);
+	  dest_str_idx = (cur_str_idx + bkref_ent->subexp_to
+			  - bkref_ent->subexp_from);
+	  context = re_string_context_at (&mctx->input, dest_str_idx - 1,
+					  mctx->eflags);
+	  dest_state = mctx->state_log[dest_str_idx];
+	  prev_nelem = ((mctx->state_log[cur_str_idx] == NULL) ? 0
+			: mctx->state_log[cur_str_idx]->nodes.nelem);
+	  /* Add `new_dest_node' to state_log.  */
+	  if (dest_state == NULL)
+	    {
+	      mctx->state_log[dest_str_idx]
+		= re_acquire_state_context (&err, dfa, new_dest_nodes,
+					    context);
+	      if (BE (mctx->state_log[dest_str_idx] == NULL
+		      && err != REG_NOERROR, 0))
+		goto free_return;
+	    }
+	  else
+	    {
+	      re_node_set dest_nodes;
+	      err = re_node_set_init_union (&dest_nodes,
+					    dest_state->entrance_nodes,
+					    new_dest_nodes);
+	      if (BE (err != REG_NOERROR, 0))
+		{
+		  re_node_set_free (&dest_nodes);
+		  goto free_return;
+		}
+	      mctx->state_log[dest_str_idx]
+		= re_acquire_state_context (&err, dfa, &dest_nodes, context);
+	      re_node_set_free (&dest_nodes);
+	      if (BE (mctx->state_log[dest_str_idx] == NULL
+		      && err != REG_NOERROR, 0))
+		goto free_return;
+	    }
+	  /* We need to check recursively if the backreference can epsilon
+	     transit.  */
+	  if (subexp_len == 0
+	      && mctx->state_log[cur_str_idx]->nodes.nelem > prev_nelem)
+	    {
+	      err = check_subexp_matching_top (mctx, new_dest_nodes,
+					       cur_str_idx);
+	      if (BE (err != REG_NOERROR, 0))
+		goto free_return;
+	      err = transit_state_bkref (mctx, new_dest_nodes);
+	      if (BE (err != REG_NOERROR, 0))
+		goto free_return;
+	    }
+	}
+    }
+  err = REG_NOERROR;
+ free_return:
+  return err;
+}
+
+/* Enumerate all the candidates which the backreference BKREF_NODE can match
+   at BKREF_STR_IDX, and register them by match_ctx_add_entry().
+   Note that we might collect inappropriate candidates here.
+   However, the cost of checking them strictly here is too high, then we
+   delay these checking for prune_impossible_nodes().  */
+
+static reg_errcode_t
+internal_function
+get_subexp (re_match_context_t *mctx, int bkref_node, int bkref_str_idx)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  int subexp_num, sub_top_idx;
+  const char *buf = (const char *) re_string_get_buffer (&mctx->input);
+  /* Return if we have already checked BKREF_NODE at BKREF_STR_IDX.  */
+  int cache_idx = search_cur_bkref_entry (mctx, bkref_str_idx);
+  if (cache_idx != -1)
+    {
+      const struct re_backref_cache_entry *entry
+	= mctx->bkref_ents + cache_idx;
+      do
+	if (entry->node == bkref_node)
+	  return REG_NOERROR; /* We already checked it.  */
+      while (entry++->more);
+    }
+
+  subexp_num = dfa->nodes[bkref_node].opr.idx;
+
+  /* For each sub expression  */
+  for (sub_top_idx = 0; sub_top_idx < mctx->nsub_tops; ++sub_top_idx)
+    {
+      reg_errcode_t err;
+      re_sub_match_top_t *sub_top = mctx->sub_tops[sub_top_idx];
+      re_sub_match_last_t *sub_last;
+      int sub_last_idx, sl_str, bkref_str_off;
+
+      if (dfa->nodes[sub_top->node].opr.idx != subexp_num)
+	continue; /* It isn't related.  */
+
+      sl_str = sub_top->str_idx;
+      bkref_str_off = bkref_str_idx;
+      /* At first, check the last node of sub expressions we already
+	 evaluated.  */
+      for (sub_last_idx = 0; sub_last_idx < sub_top->nlasts; ++sub_last_idx)
+	{
+	  int sl_str_diff;
+	  sub_last = sub_top->lasts[sub_last_idx];
+	  sl_str_diff = sub_last->str_idx - sl_str;
+	  /* The matched string by the sub expression match with the substring
+	     at the back reference?  */
+	  if (sl_str_diff > 0)
+	    {
+	      if (BE (bkref_str_off + sl_str_diff > mctx->input.valid_len, 0))
+		{
+		  /* Not enough chars for a successful match.  */
+		  if (bkref_str_off + sl_str_diff > mctx->input.len)
+		    break;
+
+		  err = clean_state_log_if_needed (mctx,
+						   bkref_str_off
+						   + sl_str_diff);
+		  if (BE (err != REG_NOERROR, 0))
+		    return err;
+		  buf = (const char *) re_string_get_buffer (&mctx->input);
+		}
+	      if (memcmp (buf + bkref_str_off, buf + sl_str, sl_str_diff) != 0)
+		/* We don't need to search this sub expression any more.  */
+		break;
+	    }
+	  bkref_str_off += sl_str_diff;
+	  sl_str += sl_str_diff;
+	  err = get_subexp_sub (mctx, sub_top, sub_last, bkref_node,
+				bkref_str_idx);
+
+	  /* Reload buf, since the preceding call might have reallocated
+	     the buffer.  */
+	  buf = (const char *) re_string_get_buffer (&mctx->input);
+
+	  if (err == REG_NOMATCH)
+	    continue;
+	  if (BE (err != REG_NOERROR, 0))
+	    return err;
+	}
+
+      if (sub_last_idx < sub_top->nlasts)
+	continue;
+      if (sub_last_idx > 0)
+	++sl_str;
+      /* Then, search for the other last nodes of the sub expression.  */
+      for (; sl_str <= bkref_str_idx; ++sl_str)
+	{
+	  int cls_node, sl_str_off;
+	  const re_node_set *nodes;
+	  sl_str_off = sl_str - sub_top->str_idx;
+	  /* The matched string by the sub expression match with the substring
+	     at the back reference?  */
+	  if (sl_str_off > 0)
+	    {
+	      if (BE (bkref_str_off >= mctx->input.valid_len, 0))
+		{
+		  /* If we are at the end of the input, we cannot match.  */
+		  if (bkref_str_off >= mctx->input.len)
+		    break;
+
+		  err = extend_buffers (mctx);
+		  if (BE (err != REG_NOERROR, 0))
+		    return err;
+
+		  buf = (const char *) re_string_get_buffer (&mctx->input);
+		}
+	      if (buf [bkref_str_off++] != buf[sl_str - 1])
+		break; /* We don't need to search this sub expression
+			  any more.  */
+	    }
+	  if (mctx->state_log[sl_str] == NULL)
+	    continue;
+	  /* Does this state have a ')' of the sub expression?  */
+	  nodes = &mctx->state_log[sl_str]->nodes;
+	  cls_node = find_subexp_node (dfa, nodes, subexp_num,
+				       OP_CLOSE_SUBEXP);
+	  if (cls_node == -1)
+	    continue; /* No.  */
+	  if (sub_top->path == NULL)
+	    {
+	      sub_top->path = calloc (sizeof (state_array_t),
+				      sl_str - sub_top->str_idx + 1);
+	      if (sub_top->path == NULL)
+		return REG_ESPACE;
+	    }
+	  /* Can the OP_OPEN_SUBEXP node arrive the OP_CLOSE_SUBEXP node
+	     in the current context?  */
+	  err = check_arrival (mctx, sub_top->path, sub_top->node,
+			       sub_top->str_idx, cls_node, sl_str,
+			       OP_CLOSE_SUBEXP);
+	  if (err == REG_NOMATCH)
+	      continue;
+	  if (BE (err != REG_NOERROR, 0))
+	      return err;
+	  sub_last = match_ctx_add_sublast (sub_top, cls_node, sl_str);
+	  if (BE (sub_last == NULL, 0))
+	    return REG_ESPACE;
+	  err = get_subexp_sub (mctx, sub_top, sub_last, bkref_node,
+				bkref_str_idx);
+	  if (err == REG_NOMATCH)
+	    continue;
+	}
+    }
+  return REG_NOERROR;
+}
+
+/* Helper functions for get_subexp().  */
+
+/* Check SUB_LAST can arrive to the back reference BKREF_NODE at BKREF_STR.
+   If it can arrive, register the sub expression expressed with SUB_TOP
+   and SUB_LAST.  */
+
+static reg_errcode_t
+internal_function
+get_subexp_sub (re_match_context_t *mctx, const re_sub_match_top_t *sub_top,
+		re_sub_match_last_t *sub_last, int bkref_node, int bkref_str)
+{
+  reg_errcode_t err;
+  int to_idx;
+  /* Can the subexpression arrive the back reference?  */
+  err = check_arrival (mctx, &sub_last->path, sub_last->node,
+		       sub_last->str_idx, bkref_node, bkref_str,
+		       OP_OPEN_SUBEXP);
+  if (err != REG_NOERROR)
+    return err;
+  err = match_ctx_add_entry (mctx, bkref_node, bkref_str, sub_top->str_idx,
+			     sub_last->str_idx);
+  if (BE (err != REG_NOERROR, 0))
+    return err;
+  to_idx = bkref_str + sub_last->str_idx - sub_top->str_idx;
+  return clean_state_log_if_needed (mctx, to_idx);
+}
+
+/* Find the first node which is '(' or ')' and whose index is SUBEXP_IDX.
+   Search '(' if FL_OPEN, or search ')' otherwise.
+   TODO: This function isn't efficient...
+	 Because there might be more than one nodes whose types are
+	 OP_OPEN_SUBEXP and whose index is SUBEXP_IDX, we must check all
+	 nodes.
+	 E.g. RE: (a){2}  */
+
+static int
+internal_function
+find_subexp_node (const re_dfa_t *dfa, const re_node_set *nodes,
+		  int subexp_idx, int type)
+{
+  int cls_idx;
+  for (cls_idx = 0; cls_idx < nodes->nelem; ++cls_idx)
+    {
+      int cls_node = nodes->elems[cls_idx];
+      const re_token_t *node = dfa->nodes + cls_node;
+      if (node->type == type
+	  && node->opr.idx == subexp_idx)
+	return cls_node;
+    }
+  return -1;
+}
+
+/* Check whether the node TOP_NODE at TOP_STR can arrive to the node
+   LAST_NODE at LAST_STR.  We record the path onto PATH since it will be
+   heavily reused.
+   Return REG_NOERROR if it can arrive, or REG_NOMATCH otherwise.  */
+
+static reg_errcode_t
+internal_function
+check_arrival (re_match_context_t *mctx, state_array_t *path, int top_node,
+	       int top_str, int last_node, int last_str, int type)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  reg_errcode_t err = REG_NOERROR;
+  int subexp_num, backup_cur_idx, str_idx, null_cnt;
+  re_dfastate_t *cur_state = NULL;
+  re_node_set *cur_nodes, next_nodes;
+  re_dfastate_t **backup_state_log;
+  unsigned int context;
+
+  subexp_num = dfa->nodes[top_node].opr.idx;
+  /* Extend the buffer if we need.  */
+  if (BE (path->alloc < last_str + mctx->max_mb_elem_len + 1, 0))
+    {
+      re_dfastate_t **new_array;
+      int old_alloc = path->alloc;
+      path->alloc += last_str + mctx->max_mb_elem_len + 1;
+      new_array = re_realloc (path->array, re_dfastate_t *, path->alloc);
+      if (BE (new_array == NULL, 0))
+	{
+	  path->alloc = old_alloc;
+	  return REG_ESPACE;
+	}
+      path->array = new_array;
+      memset (new_array + old_alloc, '\0',
+	      sizeof (re_dfastate_t *) * (path->alloc - old_alloc));
+    }
+
+  str_idx = path->next_idx ? path->next_idx : top_str;
+
+  /* Temporary modify MCTX.  */
+  backup_state_log = mctx->state_log;
+  backup_cur_idx = mctx->input.cur_idx;
+  mctx->state_log = path->array;
+  mctx->input.cur_idx = str_idx;
+
+  /* Setup initial node set.  */
+  context = re_string_context_at (&mctx->input, str_idx - 1, mctx->eflags);
+  if (str_idx == top_str)
+    {
+      err = re_node_set_init_1 (&next_nodes, top_node);
+      if (BE (err != REG_NOERROR, 0))
+	return err;
+      err = check_arrival_expand_ecl (dfa, &next_nodes, subexp_num, type);
+      if (BE (err != REG_NOERROR, 0))
+	{
+	  re_node_set_free (&next_nodes);
+	  return err;
+	}
+    }
+  else
+    {
+      cur_state = mctx->state_log[str_idx];
+      if (cur_state && cur_state->has_backref)
+	{
+	  err = re_node_set_init_copy (&next_nodes, &cur_state->nodes);
+	  if (BE (err != REG_NOERROR, 0))
+	    return err;
+	}
+      else
+	re_node_set_init_empty (&next_nodes);
+    }
+  if (str_idx == top_str || (cur_state && cur_state->has_backref))
+    {
+      if (next_nodes.nelem)
+	{
+	  err = expand_bkref_cache (mctx, &next_nodes, str_idx,
+				    subexp_num, type);
+	  if (BE (err != REG_NOERROR, 0))
+	    {
+	      re_node_set_free (&next_nodes);
+	      return err;
+	    }
+	}
+      cur_state = re_acquire_state_context (&err, dfa, &next_nodes, context);
+      if (BE (cur_state == NULL && err != REG_NOERROR, 0))
+	{
+	  re_node_set_free (&next_nodes);
+	  return err;
+	}
+      mctx->state_log[str_idx] = cur_state;
+    }
+
+  for (null_cnt = 0; str_idx < last_str && null_cnt <= mctx->max_mb_elem_len;)
+    {
+      re_node_set_empty (&next_nodes);
+      if (mctx->state_log[str_idx + 1])
+	{
+	  err = re_node_set_merge (&next_nodes,
+				   &mctx->state_log[str_idx + 1]->nodes);
+	  if (BE (err != REG_NOERROR, 0))
+	    {
+	      re_node_set_free (&next_nodes);
+	      return err;
+	    }
+	}
+      if (cur_state)
+	{
+	  err = check_arrival_add_next_nodes (mctx, str_idx,
+					      &cur_state->non_eps_nodes,
+					      &next_nodes);
+	  if (BE (err != REG_NOERROR, 0))
+	    {
+	      re_node_set_free (&next_nodes);
+	      return err;
+	    }
+	}
+      ++str_idx;
+      if (next_nodes.nelem)
+	{
+	  err = check_arrival_expand_ecl (dfa, &next_nodes, subexp_num, type);
+	  if (BE (err != REG_NOERROR, 0))
+	    {
+	      re_node_set_free (&next_nodes);
+	      return err;
+	    }
+	  err = expand_bkref_cache (mctx, &next_nodes, str_idx,
+				    subexp_num, type);
+	  if (BE (err != REG_NOERROR, 0))
+	    {
+	      re_node_set_free (&next_nodes);
+	      return err;
+	    }
+	}
+      context = re_string_context_at (&mctx->input, str_idx - 1, mctx->eflags);
+      cur_state = re_acquire_state_context (&err, dfa, &next_nodes, context);
+      if (BE (cur_state == NULL && err != REG_NOERROR, 0))
+	{
+	  re_node_set_free (&next_nodes);
+	  return err;
+	}
+      mctx->state_log[str_idx] = cur_state;
+      null_cnt = cur_state == NULL ? null_cnt + 1 : 0;
+    }
+  re_node_set_free (&next_nodes);
+  cur_nodes = (mctx->state_log[last_str] == NULL ? NULL
+	       : &mctx->state_log[last_str]->nodes);
+  path->next_idx = str_idx;
+
+  /* Fix MCTX.  */
+  mctx->state_log = backup_state_log;
+  mctx->input.cur_idx = backup_cur_idx;
+
+  /* Then check the current node set has the node LAST_NODE.  */
+  if (cur_nodes != NULL && re_node_set_contains (cur_nodes, last_node))
+    return REG_NOERROR;
+
+  return REG_NOMATCH;
+}
+
+/* Helper functions for check_arrival.  */
+
+/* Calculate the destination nodes of CUR_NODES at STR_IDX, and append them
+   to NEXT_NODES.
+   TODO: This function is similar to the functions transit_state*(),
+	 however this function has many additional works.
+	 Can't we unify them?  */
+
+static reg_errcode_t
+internal_function
+check_arrival_add_next_nodes (re_match_context_t *mctx, int str_idx,
+			      re_node_set *cur_nodes, re_node_set *next_nodes)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  int result;
+  int cur_idx;
+#ifdef RE_ENABLE_I18N
+  reg_errcode_t err = REG_NOERROR;
+#endif
+  re_node_set union_set;
+  re_node_set_init_empty (&union_set);
+  for (cur_idx = 0; cur_idx < cur_nodes->nelem; ++cur_idx)
+    {
+      int naccepted = 0;
+      int cur_node = cur_nodes->elems[cur_idx];
+#ifdef DEBUG
+      re_token_type_t type = dfa->nodes[cur_node].type;
+      assert (!IS_EPSILON_NODE (type));
+#endif
+#ifdef RE_ENABLE_I18N
+      /* If the node may accept `multi byte'.  */
+      if (dfa->nodes[cur_node].accept_mb)
+	{
+	  naccepted = check_node_accept_bytes (dfa, cur_node, &mctx->input,
+					       str_idx);
+	  if (naccepted > 1)
+	    {
+	      re_dfastate_t *dest_state;
+	      int next_node = dfa->nexts[cur_node];
+	      int next_idx = str_idx + naccepted;
+	      dest_state = mctx->state_log[next_idx];
+	      re_node_set_empty (&union_set);
+	      if (dest_state)
+		{
+		  err = re_node_set_merge (&union_set, &dest_state->nodes);
+		  if (BE (err != REG_NOERROR, 0))
+		    {
+		      re_node_set_free (&union_set);
+		      return err;
+		    }
+		}
+	      result = re_node_set_insert (&union_set, next_node);
+	      if (BE (result < 0, 0))
+		{
+		  re_node_set_free (&union_set);
+		  return REG_ESPACE;
+		}
+	      mctx->state_log[next_idx] = re_acquire_state (&err, dfa,
+							    &union_set);
+	      if (BE (mctx->state_log[next_idx] == NULL
+		      && err != REG_NOERROR, 0))
+		{
+		  re_node_set_free (&union_set);
+		  return err;
+		}
+	    }
+	}
+#endif /* RE_ENABLE_I18N */
+      if (naccepted
+	  || check_node_accept (mctx, dfa->nodes + cur_node, str_idx))
+	{
+	  result = re_node_set_insert (next_nodes, dfa->nexts[cur_node]);
+	  if (BE (result < 0, 0))
+	    {
+	      re_node_set_free (&union_set);
+	      return REG_ESPACE;
+	    }
+	}
+    }
+  re_node_set_free (&union_set);
+  return REG_NOERROR;
+}
+
+/* For all the nodes in CUR_NODES, add the epsilon closures of them to
+   CUR_NODES, however exclude the nodes which are:
+    - inside the sub expression whose number is EX_SUBEXP, if FL_OPEN.
+    - out of the sub expression whose number is EX_SUBEXP, if !FL_OPEN.
+*/
+
+static reg_errcode_t
+internal_function
+check_arrival_expand_ecl (const re_dfa_t *dfa, re_node_set *cur_nodes,
+			  int ex_subexp, int type)
+{
+  reg_errcode_t err;
+  int idx, outside_node;
+  re_node_set new_nodes;
+#ifdef DEBUG
+  assert (cur_nodes->nelem);
+#endif
+  err = re_node_set_alloc (&new_nodes, cur_nodes->nelem);
+  if (BE (err != REG_NOERROR, 0))
+    return err;
+  /* Create a new node set NEW_NODES with the nodes which are epsilon
+     closures of the node in CUR_NODES.  */
+
+  for (idx = 0; idx < cur_nodes->nelem; ++idx)
+    {
+      int cur_node = cur_nodes->elems[idx];
+      const re_node_set *eclosure = dfa->eclosures + cur_node;
+      outside_node = find_subexp_node (dfa, eclosure, ex_subexp, type);
+      if (outside_node == -1)
+	{
+	  /* There are no problematic nodes, just merge them.  */
+	  err = re_node_set_merge (&new_nodes, eclosure);
+	  if (BE (err != REG_NOERROR, 0))
+	    {
+	      re_node_set_free (&new_nodes);
+	      return err;
+	    }
+	}
+      else
+	{
+	  /* There are problematic nodes, re-calculate incrementally.  */
+	  err = check_arrival_expand_ecl_sub (dfa, &new_nodes, cur_node,
+					      ex_subexp, type);
+	  if (BE (err != REG_NOERROR, 0))
+	    {
+	      re_node_set_free (&new_nodes);
+	      return err;
+	    }
+	}
+    }
+  re_node_set_free (cur_nodes);
+  *cur_nodes = new_nodes;
+  return REG_NOERROR;
+}
+
+/* Helper function for check_arrival_expand_ecl.
+   Check incrementally the epsilon closure of TARGET, and if it isn't
+   problematic append it to DST_NODES.  */
+
+static reg_errcode_t
+internal_function
+check_arrival_expand_ecl_sub (const re_dfa_t *dfa, re_node_set *dst_nodes,
+			      int target, int ex_subexp, int type)
+{
+  int cur_node;
+  for (cur_node = target; !re_node_set_contains (dst_nodes, cur_node);)
+    {
+      int err;
+
+      if (dfa->nodes[cur_node].type == type
+	  && dfa->nodes[cur_node].opr.idx == ex_subexp)
+	{
+	  if (type == OP_CLOSE_SUBEXP)
+	    {
+	      err = re_node_set_insert (dst_nodes, cur_node);
+	      if (BE (err == -1, 0))
+		return REG_ESPACE;
+	    }
+	  break;
+	}
+      err = re_node_set_insert (dst_nodes, cur_node);
+      if (BE (err == -1, 0))
+	return REG_ESPACE;
+      if (dfa->edests[cur_node].nelem == 0)
+	break;
+      if (dfa->edests[cur_node].nelem == 2)
+	{
+	  err = check_arrival_expand_ecl_sub (dfa, dst_nodes,
+					      dfa->edests[cur_node].elems[1],
+					      ex_subexp, type);
+	  if (BE (err != REG_NOERROR, 0))
+	    return err;
+	}
+      cur_node = dfa->edests[cur_node].elems[0];
+    }
+  return REG_NOERROR;
+}
+
+
+/* For all the back references in the current state, calculate the
+   destination of the back references by the appropriate entry
+   in MCTX->BKREF_ENTS.  */
+
+static reg_errcode_t
+internal_function
+expand_bkref_cache (re_match_context_t *mctx, re_node_set *cur_nodes,
+		    int cur_str, int subexp_num, int type)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  reg_errcode_t err;
+  int cache_idx_start = search_cur_bkref_entry (mctx, cur_str);
+  struct re_backref_cache_entry *ent;
+
+  if (cache_idx_start == -1)
+    return REG_NOERROR;
+
+ restart:
+  ent = mctx->bkref_ents + cache_idx_start;
+  do
+    {
+      int to_idx, next_node;
+
+      /* Is this entry ENT is appropriate?  */
+      if (!re_node_set_contains (cur_nodes, ent->node))
+	continue; /* No.  */
+
+      to_idx = cur_str + ent->subexp_to - ent->subexp_from;
+      /* Calculate the destination of the back reference, and append it
+	 to MCTX->STATE_LOG.  */
+      if (to_idx == cur_str)
+	{
+	  /* The backreference did epsilon transit, we must re-check all the
+	     node in the current state.  */
+	  re_node_set new_dests;
+	  reg_errcode_t err2, err3;
+	  next_node = dfa->edests[ent->node].elems[0];
+	  if (re_node_set_contains (cur_nodes, next_node))
+	    continue;
+	  err = re_node_set_init_1 (&new_dests, next_node);
+	  err2 = check_arrival_expand_ecl (dfa, &new_dests, subexp_num, type);
+	  err3 = re_node_set_merge (cur_nodes, &new_dests);
+	  re_node_set_free (&new_dests);
+	  if (BE (err != REG_NOERROR || err2 != REG_NOERROR
+		  || err3 != REG_NOERROR, 0))
+	    {
+	      err = (err != REG_NOERROR ? err
+		     : (err2 != REG_NOERROR ? err2 : err3));
+	      return err;
+	    }
+	  /* TODO: It is still inefficient...  */
+	  goto restart;
+	}
+      else
+	{
+	  re_node_set union_set;
+	  next_node = dfa->nexts[ent->node];
+	  if (mctx->state_log[to_idx])
+	    {
+	      int ret;
+	      if (re_node_set_contains (&mctx->state_log[to_idx]->nodes,
+					next_node))
+		continue;
+	      err = re_node_set_init_copy (&union_set,
+					   &mctx->state_log[to_idx]->nodes);
+	      ret = re_node_set_insert (&union_set, next_node);
+	      if (BE (err != REG_NOERROR || ret < 0, 0))
+		{
+		  re_node_set_free (&union_set);
+		  err = err != REG_NOERROR ? err : REG_ESPACE;
+		  return err;
+		}
+	    }
+	  else
+	    {
+	      err = re_node_set_init_1 (&union_set, next_node);
+	      if (BE (err != REG_NOERROR, 0))
+		return err;
+	    }
+	  mctx->state_log[to_idx] = re_acquire_state (&err, dfa, &union_set);
+	  re_node_set_free (&union_set);
+	  if (BE (mctx->state_log[to_idx] == NULL
+		  && err != REG_NOERROR, 0))
+	    return err;
+	}
+    }
+  while (ent++->more);
+  return REG_NOERROR;
+}
+
+/* Build transition table for the state.
+   Return 1 if succeeded, otherwise return NULL.  */
+
+static int
+internal_function
+build_trtable (const re_dfa_t *dfa, re_dfastate_t *state)
+{
+  reg_errcode_t err;
+  int i, j, ch, need_word_trtable = 0;
+  bitset_word_t elem, mask;
+  bool dests_node_malloced = false;
+  bool dest_states_malloced = false;
+  int ndests; /* Number of the destination states from `state'.  */
+  re_dfastate_t **trtable;
+  re_dfastate_t **dest_states = NULL, **dest_states_word, **dest_states_nl;
+  re_node_set follows, *dests_node;
+  bitset_t *dests_ch;
+  bitset_t acceptable;
+
+  struct dests_alloc
+  {
+    re_node_set dests_node[SBC_MAX];
+    bitset_t dests_ch[SBC_MAX];
+  } *dests_alloc;
+
+  /* We build DFA states which corresponds to the destination nodes
+     from `state'.  `dests_node[i]' represents the nodes which i-th
+     destination state contains, and `dests_ch[i]' represents the
+     characters which i-th destination state accepts.  */
+#ifdef HAVE_ALLOCA
+  if (__libc_use_alloca (sizeof (struct dests_alloc)))
+    dests_alloc = (struct dests_alloc *) alloca (sizeof (struct dests_alloc));
+  else
+#endif
+    {
+      dests_alloc = re_malloc (struct dests_alloc, 1);
+      if (BE (dests_alloc == NULL, 0))
+	return 0;
+      dests_node_malloced = true;
+    }
+  dests_node = dests_alloc->dests_node;
+  dests_ch = dests_alloc->dests_ch;
+
+  /* Initialize transiton table.  */
+  state->word_trtable = state->trtable = NULL;
+
+  /* At first, group all nodes belonging to `state' into several
+     destinations.  */
+  ndests = group_nodes_into_DFAstates (dfa, state, dests_node, dests_ch);
+  if (BE (ndests <= 0, 0))
+    {
+      if (dests_node_malloced)
+	free (dests_alloc);
+      /* Return 0 in case of an error, 1 otherwise.  */
+      if (ndests == 0)
+	{
+	  state->trtable = (re_dfastate_t **)
+	    calloc (sizeof (re_dfastate_t *), SBC_MAX);
+	  return 1;
+	}
+      return 0;
+    }
+
+  err = re_node_set_alloc (&follows, ndests + 1);
+  if (BE (err != REG_NOERROR, 0))
+    goto out_free;
+
+  /* Avoid arithmetic overflow in size calculation.  */
+  if (BE ((((SIZE_MAX - (sizeof (re_node_set) + sizeof (bitset_t)) * SBC_MAX)
+	    / (3 * sizeof (re_dfastate_t *)))
+	   < ndests),
+	  0))
+    goto out_free;
+
+#ifdef HAVE_ALLOCA
+  if (__libc_use_alloca ((sizeof (re_node_set) + sizeof (bitset_t)) * SBC_MAX
+			 + ndests * 3 * sizeof (re_dfastate_t *)))
+    dest_states = (re_dfastate_t **)
+      alloca (ndests * 3 * sizeof (re_dfastate_t *));
+  else
+#endif
+    {
+      dest_states = (re_dfastate_t **)
+	malloc (ndests * 3 * sizeof (re_dfastate_t *));
+      if (BE (dest_states == NULL, 0))
+	{
+out_free:
+	  if (dest_states_malloced)
+	    free (dest_states);
+	  re_node_set_free (&follows);
+	  for (i = 0; i < ndests; ++i)
+	    re_node_set_free (dests_node + i);
+	  if (dests_node_malloced)
+	    free (dests_alloc);
+	  return 0;
+	}
+      dest_states_malloced = true;
+    }
+  dest_states_word = dest_states + ndests;
+  dest_states_nl = dest_states_word + ndests;
+  bitset_empty (acceptable);
+
+  /* Then build the states for all destinations.  */
+  for (i = 0; i < ndests; ++i)
+    {
+      int next_node;
+      re_node_set_empty (&follows);
+      /* Merge the follows of this destination states.  */
+      for (j = 0; j < dests_node[i].nelem; ++j)
+	{
+	  next_node = dfa->nexts[dests_node[i].elems[j]];
+	  if (next_node != -1)
+	    {
+	      err = re_node_set_merge (&follows, dfa->eclosures + next_node);
+	      if (BE (err != REG_NOERROR, 0))
+		goto out_free;
+	    }
+	}
+      dest_states[i] = re_acquire_state_context (&err, dfa, &follows, 0);
+      if (BE (dest_states[i] == NULL && err != REG_NOERROR, 0))
+	goto out_free;
+      /* If the new state has context constraint,
+	 build appropriate states for these contexts.  */
+      if (dest_states[i]->has_constraint)
+	{
+	  dest_states_word[i] = re_acquire_state_context (&err, dfa, &follows,
+							  CONTEXT_WORD);
+	  if (BE (dest_states_word[i] == NULL && err != REG_NOERROR, 0))
+	    goto out_free;
+
+	  if (dest_states[i] != dest_states_word[i] && dfa->mb_cur_max > 1)
+	    need_word_trtable = 1;
+
+	  dest_states_nl[i] = re_acquire_state_context (&err, dfa, &follows,
+							CONTEXT_NEWLINE);
+	  if (BE (dest_states_nl[i] == NULL && err != REG_NOERROR, 0))
+	    goto out_free;
+ 	}
+      else
+	{
+	  dest_states_word[i] = dest_states[i];
+	  dest_states_nl[i] = dest_states[i];
+	}
+      bitset_merge (acceptable, dests_ch[i]);
+    }
+
+  if (!BE (need_word_trtable, 0))
+    {
+      /* We don't care about whether the following character is a word
+	 character, or we are in a single-byte character set so we can
+	 discern by looking at the character code: allocate a
+	 256-entry transition table.  */
+      trtable = state->trtable =
+	(re_dfastate_t **) calloc (sizeof (re_dfastate_t *), SBC_MAX);
+      if (BE (trtable == NULL, 0))
+	goto out_free;
+
+      /* For all characters ch...:  */
+      for (i = 0; i < BITSET_WORDS; ++i)
+	for (ch = i * BITSET_WORD_BITS, elem = acceptable[i], mask = 1;
+	     elem;
+	     mask <<= 1, elem >>= 1, ++ch)
+	  if (BE (elem & 1, 0))
+	    {
+	      /* There must be exactly one destination which accepts
+		 character ch.  See group_nodes_into_DFAstates.  */
+	      for (j = 0; (dests_ch[j][i] & mask) == 0; ++j)
+		;
+
+	      /* j-th destination accepts the word character ch.  */
+	      if (dfa->word_char[i] & mask)
+		trtable[ch] = dest_states_word[j];
+	      else
+		trtable[ch] = dest_states[j];
+	    }
+    }
+  else
+    {
+      /* We care about whether the following character is a word
+	 character, and we are in a multi-byte character set: discern
+	 by looking at the character code: build two 256-entry
+	 transition tables, one starting at trtable[0] and one
+	 starting at trtable[SBC_MAX].  */
+      trtable = state->word_trtable =
+	(re_dfastate_t **) calloc (sizeof (re_dfastate_t *), 2 * SBC_MAX);
+      if (BE (trtable == NULL, 0))
+	goto out_free;
+
+      /* For all characters ch...:  */
+      for (i = 0; i < BITSET_WORDS; ++i)
+	for (ch = i * BITSET_WORD_BITS, elem = acceptable[i], mask = 1;
+	     elem;
+	     mask <<= 1, elem >>= 1, ++ch)
+	  if (BE (elem & 1, 0))
+	    {
+	      /* There must be exactly one destination which accepts
+		 character ch.  See group_nodes_into_DFAstates.  */
+	      for (j = 0; (dests_ch[j][i] & mask) == 0; ++j)
+		;
+
+	      /* j-th destination accepts the word character ch.  */
+	      trtable[ch] = dest_states[j];
+	      trtable[ch + SBC_MAX] = dest_states_word[j];
+	    }
+    }
+
+  /* new line */
+  if (bitset_contain (acceptable, NEWLINE_CHAR))
+    {
+      /* The current state accepts newline character.  */
+      for (j = 0; j < ndests; ++j)
+	if (bitset_contain (dests_ch[j], NEWLINE_CHAR))
+	  {
+	    /* k-th destination accepts newline character.  */
+	    trtable[NEWLINE_CHAR] = dest_states_nl[j];
+	    if (need_word_trtable)
+	      trtable[NEWLINE_CHAR + SBC_MAX] = dest_states_nl[j];
+	    /* There must be only one destination which accepts
+	       newline.  See group_nodes_into_DFAstates.  */
+	    break;
+	  }
+    }
+
+  if (dest_states_malloced)
+    free (dest_states);
+
+  re_node_set_free (&follows);
+  for (i = 0; i < ndests; ++i)
+    re_node_set_free (dests_node + i);
+
+  if (dests_node_malloced)
+    free (dests_alloc);
+
+  return 1;
+}
+
+/* Group all nodes belonging to STATE into several destinations.
+   Then for all destinations, set the nodes belonging to the destination
+   to DESTS_NODE[i] and set the characters accepted by the destination
+   to DEST_CH[i].  This function return the number of destinations.  */
+
+static int
+internal_function
+group_nodes_into_DFAstates (const re_dfa_t *dfa, const re_dfastate_t *state,
+			    re_node_set *dests_node, bitset_t *dests_ch)
+{
+  reg_errcode_t err;
+  int result;
+  int i, j, k;
+  int ndests; /* Number of the destinations from `state'.  */
+  bitset_t accepts; /* Characters a node can accept.  */
+  const re_node_set *cur_nodes = &state->nodes;
+  bitset_empty (accepts);
+  ndests = 0;
+
+  /* For all the nodes belonging to `state',  */
+  for (i = 0; i < cur_nodes->nelem; ++i)
+    {
+      re_token_t *node = &dfa->nodes[cur_nodes->elems[i]];
+      re_token_type_t type = node->type;
+      unsigned int constraint = node->constraint;
+
+      /* Enumerate all single byte character this node can accept.  */
+      if (type == CHARACTER)
+	bitset_set (accepts, node->opr.c);
+      else if (type == SIMPLE_BRACKET)
+	{
+	  bitset_merge (accepts, node->opr.sbcset);
+	}
+      else if (type == OP_PERIOD)
+	{
+#ifdef RE_ENABLE_I18N
+	  if (dfa->mb_cur_max > 1)
+	    bitset_merge (accepts, dfa->sb_char);
+	  else
+#endif
+	    bitset_set_all (accepts);
+	  if (!(dfa->syntax & RE_DOT_NEWLINE))
+	    bitset_clear (accepts, '\n');
+	  if (dfa->syntax & RE_DOT_NOT_NULL)
+	    bitset_clear (accepts, '\0');
+	}
+#ifdef RE_ENABLE_I18N
+      else if (type == OP_UTF8_PERIOD)
+	{
+	  memset (accepts, '\xff', sizeof (bitset_t) / 2);
+	  if (!(dfa->syntax & RE_DOT_NEWLINE))
+	    bitset_clear (accepts, '\n');
+	  if (dfa->syntax & RE_DOT_NOT_NULL)
+	    bitset_clear (accepts, '\0');
+	}
+#endif
+      else
+	continue;
+
+      /* Check the `accepts' and sift the characters which are not
+	 match it the context.  */
+      if (constraint)
+	{
+	  if (constraint & NEXT_NEWLINE_CONSTRAINT)
+	    {
+	      bool accepts_newline = bitset_contain (accepts, NEWLINE_CHAR);
+	      bitset_empty (accepts);
+	      if (accepts_newline)
+		bitset_set (accepts, NEWLINE_CHAR);
+	      else
+		continue;
+	    }
+	  if (constraint & NEXT_ENDBUF_CONSTRAINT)
+	    {
+	      bitset_empty (accepts);
+	      continue;
+	    }
+
+	  if (constraint & NEXT_WORD_CONSTRAINT)
+	    {
+	      bitset_word_t any_set = 0;
+	      if (type == CHARACTER && !node->word_char)
+		{
+		  bitset_empty (accepts);
+		  continue;
+		}
+#ifdef RE_ENABLE_I18N
+	      if (dfa->mb_cur_max > 1)
+		for (j = 0; j < BITSET_WORDS; ++j)
+		  any_set |= (accepts[j] &= (dfa->word_char[j] | ~dfa->sb_char[j]));
+	      else
+#endif
+		for (j = 0; j < BITSET_WORDS; ++j)
+		  any_set |= (accepts[j] &= dfa->word_char[j]);
+	      if (!any_set)
+		continue;
+	    }
+	  if (constraint & NEXT_NOTWORD_CONSTRAINT)
+	    {
+	      bitset_word_t any_set = 0;
+	      if (type == CHARACTER && node->word_char)
+		{
+		  bitset_empty (accepts);
+		  continue;
+		}
+#ifdef RE_ENABLE_I18N
+	      if (dfa->mb_cur_max > 1)
+		for (j = 0; j < BITSET_WORDS; ++j)
+		  any_set |= (accepts[j] &= ~(dfa->word_char[j] & dfa->sb_char[j]));
+	      else
+#endif
+		for (j = 0; j < BITSET_WORDS; ++j)
+		  any_set |= (accepts[j] &= ~dfa->word_char[j]);
+	      if (!any_set)
+		continue;
+	    }
+	}
+
+      /* Then divide `accepts' into DFA states, or create a new
+	 state.  Above, we make sure that accepts is not empty.  */
+      for (j = 0; j < ndests; ++j)
+	{
+	  bitset_t intersec; /* Intersection sets, see below.  */
+	  bitset_t remains;
+	  /* Flags, see below.  */
+	  bitset_word_t has_intersec, not_subset, not_consumed;
+
+	  /* Optimization, skip if this state doesn't accept the character.  */
+	  if (type == CHARACTER && !bitset_contain (dests_ch[j], node->opr.c))
+	    continue;
+
+	  /* Enumerate the intersection set of this state and `accepts'.  */
+	  has_intersec = 0;
+	  for (k = 0; k < BITSET_WORDS; ++k)
+	    has_intersec |= intersec[k] = accepts[k] & dests_ch[j][k];
+	  /* And skip if the intersection set is empty.  */
+	  if (!has_intersec)
+	    continue;
+
+	  /* Then check if this state is a subset of `accepts'.  */
+	  not_subset = not_consumed = 0;
+	  for (k = 0; k < BITSET_WORDS; ++k)
+	    {
+	      not_subset |= remains[k] = ~accepts[k] & dests_ch[j][k];
+	      not_consumed |= accepts[k] = accepts[k] & ~dests_ch[j][k];
+	    }
+
+	  /* If this state isn't a subset of `accepts', create a
+	     new group state, which has the `remains'. */
+	  if (not_subset)
+	    {
+	      bitset_copy (dests_ch[ndests], remains);
+	      bitset_copy (dests_ch[j], intersec);
+	      err = re_node_set_init_copy (dests_node + ndests, &dests_node[j]);
+	      if (BE (err != REG_NOERROR, 0))
+		goto error_return;
+	      ++ndests;
+	    }
+
+	  /* Put the position in the current group. */
+	  result = re_node_set_insert (&dests_node[j], cur_nodes->elems[i]);
+	  if (BE (result < 0, 0))
+	    goto error_return;
+
+	  /* If all characters are consumed, go to next node. */
+	  if (!not_consumed)
+	    break;
+	}
+      /* Some characters remain, create a new group. */
+      if (j == ndests)
+	{
+	  bitset_copy (dests_ch[ndests], accepts);
+	  err = re_node_set_init_1 (dests_node + ndests, cur_nodes->elems[i]);
+	  if (BE (err != REG_NOERROR, 0))
+	    goto error_return;
+	  ++ndests;
+	  bitset_empty (accepts);
+	}
+    }
+  return ndests;
+ error_return:
+  for (j = 0; j < ndests; ++j)
+    re_node_set_free (dests_node + j);
+  return -1;
+}
+
+#ifdef RE_ENABLE_I18N
+/* Check how many bytes the node `dfa->nodes[node_idx]' accepts.
+   Return the number of the bytes the node accepts.
+   STR_IDX is the current index of the input string.
+
+   This function handles the nodes which can accept one character, or
+   one collating element like '.', '[a-z]', opposite to the other nodes
+   can only accept one byte.  */
+
+static int
+internal_function
+check_node_accept_bytes (const re_dfa_t *dfa, int node_idx,
+			 const re_string_t *input, int str_idx)
+{
+  const re_token_t *node = dfa->nodes + node_idx;
+  int char_len, elem_len;
+  int i;
+  wint_t wc;
+
+  if (BE (node->type == OP_UTF8_PERIOD, 0))
+    {
+      unsigned char c = re_string_byte_at (input, str_idx), d;
+      if (BE (c < 0xc2, 1))
+	return 0;
+
+      if (str_idx + 2 > input->len)
+	return 0;
+
+      d = re_string_byte_at (input, str_idx + 1);
+      if (c < 0xe0)
+	return (d < 0x80 || d > 0xbf) ? 0 : 2;
+      else if (c < 0xf0)
+	{
+	  char_len = 3;
+	  if (c == 0xe0 && d < 0xa0)
+	    return 0;
+	}
+      else if (c < 0xf8)
+	{
+	  char_len = 4;
+	  if (c == 0xf0 && d < 0x90)
+	    return 0;
+	}
+      else if (c < 0xfc)
+	{
+	  char_len = 5;
+	  if (c == 0xf8 && d < 0x88)
+	    return 0;
+	}
+      else if (c < 0xfe)
+	{
+	  char_len = 6;
+	  if (c == 0xfc && d < 0x84)
+	    return 0;
+	}
+      else
+	return 0;
+
+      if (str_idx + char_len > input->len)
+	return 0;
+
+      for (i = 1; i < char_len; ++i)
+	{
+	  d = re_string_byte_at (input, str_idx + i);
+	  if (d < 0x80 || d > 0xbf)
+	    return 0;
+	}
+      return char_len;
+    }
+
+  char_len = re_string_char_size_at (input, str_idx);
+  if (node->type == OP_PERIOD)
+    {
+      if (char_len <= 1)
+	return 0;
+      /* FIXME: I don't think this if is needed, as both '\n'
+	 and '\0' are char_len == 1.  */
+      /* '.' accepts any one character except the following two cases.  */
+      if ((!(dfa->syntax & RE_DOT_NEWLINE) &&
+	   re_string_byte_at (input, str_idx) == '\n') ||
+	  ((dfa->syntax & RE_DOT_NOT_NULL) &&
+	   re_string_byte_at (input, str_idx) == '\0'))
+	return 0;
+      return char_len;
+    }
+
+  elem_len = re_string_elem_size_at (input, str_idx);
+  wc = __btowc(*(input->mbs+str_idx));
+  if (((elem_len <= 1 && char_len <= 1) || char_len == 0) && (wc != WEOF && wc < SBC_MAX))
+    return 0;
+
+  if (node->type == COMPLEX_BRACKET)
+    {
+      const re_charset_t *cset = node->opr.mbcset;
+# ifdef _LIBC
+      const unsigned char *pin
+	= ((const unsigned char *) re_string_get_buffer (input) + str_idx);
+      int j;
+      uint32_t nrules;
+# endif /* _LIBC */
+      int match_len = 0;
+      wchar_t wc = ((cset->nranges || cset->nchar_classes || cset->nmbchars)
+		    ? re_string_wchar_at (input, str_idx) : 0);
+
+      /* match with multibyte character?  */
+      for (i = 0; i < cset->nmbchars; ++i)
+	if (wc == cset->mbchars[i])
+	  {
+	    match_len = char_len;
+	    goto check_node_accept_bytes_match;
+	  }
+      /* match with character_class?  */
+      for (i = 0; i < cset->nchar_classes; ++i)
+	{
+	  wctype_t wt = cset->char_classes[i];
+	  if (__iswctype (wc, wt))
+	    {
+	      match_len = char_len;
+	      goto check_node_accept_bytes_match;
+	    }
+	}
+
+# ifdef _LIBC
+      nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+      if (nrules != 0)
+	{
+	  unsigned int in_collseq = 0;
+	  const int32_t *table, *indirect;
+	  const unsigned char *weights, *extra;
+	  const char *collseqwc;
+	  /* This #include defines a local function!  */
+#  include <locale/weight.h>
+
+	  /* match with collating_symbol?  */
+	  if (cset->ncoll_syms)
+	    extra = (const unsigned char *)
+	      _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB);
+	  for (i = 0; i < cset->ncoll_syms; ++i)
+	    {
+	      const unsigned char *coll_sym = extra + cset->coll_syms[i];
+	      /* Compare the length of input collating element and
+		 the length of current collating element.  */
+	      if (*coll_sym != elem_len)
+		continue;
+	      /* Compare each bytes.  */
+	      for (j = 0; j < *coll_sym; j++)
+		if (pin[j] != coll_sym[1 + j])
+		  break;
+	      if (j == *coll_sym)
+		{
+		  /* Match if every bytes is equal.  */
+		  match_len = j;
+		  goto check_node_accept_bytes_match;
+		}
+	    }
+
+	  if (cset->nranges)
+	    {
+	      if (elem_len <= char_len)
+		{
+		  collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC);
+		  in_collseq = __collseq_table_lookup (collseqwc, wc);
+		}
+	      else
+		in_collseq = find_collation_sequence_value (pin, elem_len);
+	    }
+	  /* match with range expression?  */
+	  for (i = 0; i < cset->nranges; ++i)
+	    if (cset->range_starts[i] <= in_collseq
+		&& in_collseq <= cset->range_ends[i])
+	      {
+		match_len = elem_len;
+		goto check_node_accept_bytes_match;
+	      }
+
+	  /* match with equivalence_class?  */
+	  if (cset->nequiv_classes)
+	    {
+	      const unsigned char *cp = pin;
+	      table = (const int32_t *)
+		_NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+	      weights = (const unsigned char *)
+		_NL_CURRENT (LC_COLLATE, _NL_COLLATE_WEIGHTMB);
+	      extra = (const unsigned char *)
+		_NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB);
+	      indirect = (const int32_t *)
+		_NL_CURRENT (LC_COLLATE, _NL_COLLATE_INDIRECTMB);
+	      int32_t idx = findidx (&cp);
+	      if (idx > 0)
+		for (i = 0; i < cset->nequiv_classes; ++i)
+		  {
+		    int32_t equiv_class_idx = cset->equiv_classes[i];
+		    size_t weight_len = weights[idx & 0xffffff];
+		    if (weight_len == weights[equiv_class_idx & 0xffffff]
+			&& (idx >> 24) == (equiv_class_idx >> 24))
+		      {
+			int cnt = 0;
+
+			idx &= 0xffffff;
+			equiv_class_idx &= 0xffffff;
+
+			while (cnt <= weight_len
+			       && (weights[equiv_class_idx + 1 + cnt]
+				   == weights[idx + 1 + cnt]))
+			  ++cnt;
+			if (cnt > weight_len)
+			  {
+			    match_len = elem_len;
+			    goto check_node_accept_bytes_match;
+			  }
+		      }
+		  }
+	    }
+	}
+      else
+# endif /* _LIBC */
+	{
+	  /* match with range expression?  */
+#if __GNUC__ >= 2
+	  wchar_t cmp_buf[] = {L'\0', L'\0', wc, L'\0', L'\0', L'\0'};
+#else
+	  wchar_t cmp_buf[] = {L'\0', L'\0', L'\0', L'\0', L'\0', L'\0'};
+	  cmp_buf[2] = wc;
+#endif
+	  for (i = 0; i < cset->nranges; ++i)
+	    {
+	      cmp_buf[0] = cset->range_starts[i];
+	      cmp_buf[4] = cset->range_ends[i];
+	      if (wcscoll (cmp_buf, cmp_buf + 2) <= 0
+		  && wcscoll (cmp_buf + 2, cmp_buf + 4) <= 0)
+		{
+		  match_len = char_len;
+		  goto check_node_accept_bytes_match;
+		}
+	    }
+	}
+    check_node_accept_bytes_match:
+      if (!cset->non_match)
+	return match_len;
+      else
+	{
+	  if (match_len > 0)
+	    return 0;
+	  else
+	    return (elem_len > char_len) ? elem_len : char_len;
+	}
+    }
+  return 0;
+}
+
+# ifdef _LIBC
+static unsigned int
+internal_function
+find_collation_sequence_value (const unsigned char *mbs, size_t mbs_len)
+{
+  uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+  if (nrules == 0)
+    {
+      if (mbs_len == 1)
+	{
+	  /* No valid character.  Match it as a single byte character.  */
+	  const unsigned char *collseq = (const unsigned char *)
+	    _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB);
+	  return collseq[mbs[0]];
+	}
+      return UINT_MAX;
+    }
+  else
+    {
+      int32_t idx;
+      const unsigned char *extra = (const unsigned char *)
+	_NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB);
+      int32_t extrasize = (const unsigned char *)
+	_NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB + 1) - extra;
+
+      for (idx = 0; idx < extrasize;)
+	{
+	  int mbs_cnt, found = 0;
+	  int32_t elem_mbs_len;
+	  /* Skip the name of collating element name.  */
+	  idx = idx + extra[idx] + 1;
+	  elem_mbs_len = extra[idx++];
+	  if (mbs_len == elem_mbs_len)
+	    {
+	      for (mbs_cnt = 0; mbs_cnt < elem_mbs_len; ++mbs_cnt)
+		if (extra[idx + mbs_cnt] != mbs[mbs_cnt])
+		  break;
+	      if (mbs_cnt == elem_mbs_len)
+		/* Found the entry.  */
+		found = 1;
+	    }
+	  /* Skip the byte sequence of the collating element.  */
+	  idx += elem_mbs_len;
+	  /* Adjust for the alignment.  */
+	  idx = (idx + 3) & ~3;
+	  /* Skip the collation sequence value.  */
+	  idx += sizeof (uint32_t);
+	  /* Skip the wide char sequence of the collating element.  */
+	  idx = idx + sizeof (uint32_t) * (extra[idx] + 1);
+	  /* If we found the entry, return the sequence value.  */
+	  if (found)
+	    return *(uint32_t *) (extra + idx);
+	  /* Skip the collation sequence value.  */
+	  idx += sizeof (uint32_t);
+	}
+      return UINT_MAX;
+    }
+}
+# endif /* _LIBC */
+#endif /* RE_ENABLE_I18N */
+
+/* Check whether the node accepts the byte which is IDX-th
+   byte of the INPUT.  */
+
+static int
+internal_function
+check_node_accept (const re_match_context_t *mctx, const re_token_t *node,
+		   int idx)
+{
+  unsigned char ch;
+  ch = re_string_byte_at (&mctx->input, idx);
+  switch (node->type)
+    {
+    case CHARACTER:
+      if (node->opr.c != ch)
+	return 0;
+      break;
+
+    case SIMPLE_BRACKET:
+      if (!bitset_contain (node->opr.sbcset, ch))
+	return 0;
+      break;
+
+#ifdef RE_ENABLE_I18N
+    case OP_UTF8_PERIOD:
+      if (ch >= 0x80)
+	return 0;
+      /* FALLTHROUGH */
+#endif
+    case OP_PERIOD:
+      if ((ch == '\n' && !(mctx->dfa->syntax & RE_DOT_NEWLINE))
+	  || (ch == '\0' && (mctx->dfa->syntax & RE_DOT_NOT_NULL)))
+	return 0;
+      break;
+
+    default:
+      return 0;
+    }
+
+  if (node->constraint)
+    {
+      /* The node has constraints.  Check whether the current context
+	 satisfies the constraints.  */
+      unsigned int context = re_string_context_at (&mctx->input, idx,
+						   mctx->eflags);
+      if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context))
+	return 0;
+    }
+
+  return 1;
+}
+
+/* Extend the buffers, if the buffers have run out.  */
+
+static reg_errcode_t
+internal_function
+extend_buffers (re_match_context_t *mctx)
+{
+  reg_errcode_t ret;
+  re_string_t *pstr = &mctx->input;
+
+  /* Avoid overflow.  */
+  if (BE (INT_MAX / 2 / sizeof (re_dfastate_t *) <= pstr->bufs_len, 0))
+    return REG_ESPACE;
+
+  /* Double the lengthes of the buffers.  */
+  ret = re_string_realloc_buffers (pstr, pstr->bufs_len * 2);
+  if (BE (ret != REG_NOERROR, 0))
+    return ret;
+
+  if (mctx->state_log != NULL)
+    {
+      /* And double the length of state_log.  */
+      /* XXX We have no indication of the size of this buffer.  If this
+	 allocation fail we have no indication that the state_log array
+	 does not have the right size.  */
+      re_dfastate_t **new_array = re_realloc (mctx->state_log, re_dfastate_t *,
+					      pstr->bufs_len + 1);
+      if (BE (new_array == NULL, 0))
+	return REG_ESPACE;
+      mctx->state_log = new_array;
+    }
+
+  /* Then reconstruct the buffers.  */
+  if (pstr->icase)
+    {
+#ifdef RE_ENABLE_I18N
+      if (pstr->mb_cur_max > 1)
+	{
+	  ret = build_wcs_upper_buffer (pstr);
+	  if (BE (ret != REG_NOERROR, 0))
+	    return ret;
+	}
+      else
+#endif /* RE_ENABLE_I18N  */
+	build_upper_buffer (pstr);
+    }
+  else
+    {
+#ifdef RE_ENABLE_I18N
+      if (pstr->mb_cur_max > 1)
+	build_wcs_buffer (pstr);
+      else
+#endif /* RE_ENABLE_I18N  */
+	{
+	  if (pstr->trans != NULL)
+	    re_string_translate_buffer (pstr);
+	}
+    }
+  return REG_NOERROR;
+}
+
+
+/* Functions for matching context.  */
+
+/* Initialize MCTX.  */
+
+static reg_errcode_t
+internal_function
+match_ctx_init (re_match_context_t *mctx, int eflags, int n)
+{
+  mctx->eflags = eflags;
+  mctx->match_last = -1;
+  if (n > 0)
+    {
+      mctx->bkref_ents = re_malloc (struct re_backref_cache_entry, n);
+      mctx->sub_tops = re_malloc (re_sub_match_top_t *, n);
+      if (BE (mctx->bkref_ents == NULL || mctx->sub_tops == NULL, 0))
+	return REG_ESPACE;
+    }
+  /* Already zero-ed by the caller.
+     else
+       mctx->bkref_ents = NULL;
+     mctx->nbkref_ents = 0;
+     mctx->nsub_tops = 0;  */
+  mctx->abkref_ents = n;
+  mctx->max_mb_elem_len = 1;
+  mctx->asub_tops = n;
+  return REG_NOERROR;
+}
+
+/* Clean the entries which depend on the current input in MCTX.
+   This function must be invoked when the matcher changes the start index
+   of the input, or changes the input string.  */
+
+static void
+internal_function
+match_ctx_clean (re_match_context_t *mctx)
+{
+  int st_idx;
+  for (st_idx = 0; st_idx < mctx->nsub_tops; ++st_idx)
+    {
+      int sl_idx;
+      re_sub_match_top_t *top = mctx->sub_tops[st_idx];
+      for (sl_idx = 0; sl_idx < top->nlasts; ++sl_idx)
+	{
+	  re_sub_match_last_t *last = top->lasts[sl_idx];
+	  re_free (last->path.array);
+	  re_free (last);
+	}
+      re_free (top->lasts);
+      if (top->path)
+	{
+	  re_free (top->path->array);
+	  re_free (top->path);
+	}
+      free (top);
+    }
+
+  mctx->nsub_tops = 0;
+  mctx->nbkref_ents = 0;
+}
+
+/* Free all the memory associated with MCTX.  */
+
+static void
+internal_function
+match_ctx_free (re_match_context_t *mctx)
+{
+  /* First, free all the memory associated with MCTX->SUB_TOPS.  */
+  match_ctx_clean (mctx);
+  re_free (mctx->sub_tops);
+  re_free (mctx->bkref_ents);
+}
+
+/* Add a new backreference entry to MCTX.
+   Note that we assume that caller never call this function with duplicate
+   entry, and call with STR_IDX which isn't smaller than any existing entry.
+*/
+
+static reg_errcode_t
+internal_function
+match_ctx_add_entry (re_match_context_t *mctx, int node, int str_idx, int from,
+		     int to)
+{
+  if (mctx->nbkref_ents >= mctx->abkref_ents)
+    {
+      struct re_backref_cache_entry* new_entry;
+      new_entry = re_realloc (mctx->bkref_ents, struct re_backref_cache_entry,
+			      mctx->abkref_ents * 2);
+      if (BE (new_entry == NULL, 0))
+	{
+	  re_free (mctx->bkref_ents);
+	  return REG_ESPACE;
+	}
+      mctx->bkref_ents = new_entry;
+      memset (mctx->bkref_ents + mctx->nbkref_ents, '\0',
+	      sizeof (struct re_backref_cache_entry) * mctx->abkref_ents);
+      mctx->abkref_ents *= 2;
+    }
+  if (mctx->nbkref_ents > 0
+      && mctx->bkref_ents[mctx->nbkref_ents - 1].str_idx == str_idx)
+    mctx->bkref_ents[mctx->nbkref_ents - 1].more = 1;
+
+  mctx->bkref_ents[mctx->nbkref_ents].node = node;
+  mctx->bkref_ents[mctx->nbkref_ents].str_idx = str_idx;
+  mctx->bkref_ents[mctx->nbkref_ents].subexp_from = from;
+  mctx->bkref_ents[mctx->nbkref_ents].subexp_to = to;
+
+  /* This is a cache that saves negative results of check_dst_limits_calc_pos.
+     If bit N is clear, means that this entry won't epsilon-transition to
+     an OP_OPEN_SUBEXP or OP_CLOSE_SUBEXP for the N+1-th subexpression.  If
+     it is set, check_dst_limits_calc_pos_1 will recurse and try to find one
+     such node.
+
+     A backreference does not epsilon-transition unless it is empty, so set
+     to all zeros if FROM != TO.  */
+  mctx->bkref_ents[mctx->nbkref_ents].eps_reachable_subexps_map
+    = (from == to ? ~0 : 0);
+
+  mctx->bkref_ents[mctx->nbkref_ents++].more = 0;
+  if (mctx->max_mb_elem_len < to - from)
+    mctx->max_mb_elem_len = to - from;
+  return REG_NOERROR;
+}
+
+/* Search for the first entry which has the same str_idx, or -1 if none is
+   found.  Note that MCTX->BKREF_ENTS is already sorted by MCTX->STR_IDX.  */
+
+static int
+internal_function
+search_cur_bkref_entry (const re_match_context_t *mctx, int str_idx)
+{
+  int left, right, mid, last;
+  last = right = mctx->nbkref_ents;
+  for (left = 0; left < right;)
+    {
+      mid = (left + right) / 2;
+      if (mctx->bkref_ents[mid].str_idx < str_idx)
+	left = mid + 1;
+      else
+	right = mid;
+    }
+  if (left < last && mctx->bkref_ents[left].str_idx == str_idx)
+    return left;
+  else
+    return -1;
+}
+
+/* Register the node NODE, whose type is OP_OPEN_SUBEXP, and which matches
+   at STR_IDX.  */
+
+static reg_errcode_t
+internal_function
+match_ctx_add_subtop (re_match_context_t *mctx, int node, int str_idx)
+{
+#ifdef DEBUG
+  assert (mctx->sub_tops != NULL);
+  assert (mctx->asub_tops > 0);
+#endif
+  if (BE (mctx->nsub_tops == mctx->asub_tops, 0))
+    {
+      int new_asub_tops = mctx->asub_tops * 2;
+      re_sub_match_top_t **new_array = re_realloc (mctx->sub_tops,
+						   re_sub_match_top_t *,
+						   new_asub_tops);
+      if (BE (new_array == NULL, 0))
+	return REG_ESPACE;
+      mctx->sub_tops = new_array;
+      mctx->asub_tops = new_asub_tops;
+    }
+  mctx->sub_tops[mctx->nsub_tops] = calloc (1, sizeof (re_sub_match_top_t));
+  if (BE (mctx->sub_tops[mctx->nsub_tops] == NULL, 0))
+    return REG_ESPACE;
+  mctx->sub_tops[mctx->nsub_tops]->node = node;
+  mctx->sub_tops[mctx->nsub_tops++]->str_idx = str_idx;
+  return REG_NOERROR;
+}
+
+/* Register the node NODE, whose type is OP_CLOSE_SUBEXP, and which matches
+   at STR_IDX, whose corresponding OP_OPEN_SUBEXP is SUB_TOP.  */
+
+static re_sub_match_last_t *
+internal_function
+match_ctx_add_sublast (re_sub_match_top_t *subtop, int node, int str_idx)
+{
+  re_sub_match_last_t *new_entry;
+  if (BE (subtop->nlasts == subtop->alasts, 0))
+    {
+      int new_alasts = 2 * subtop->alasts + 1;
+      re_sub_match_last_t **new_array = re_realloc (subtop->lasts,
+						    re_sub_match_last_t *,
+						    new_alasts);
+      if (BE (new_array == NULL, 0))
+	return NULL;
+      subtop->lasts = new_array;
+      subtop->alasts = new_alasts;
+    }
+  new_entry = calloc (1, sizeof (re_sub_match_last_t));
+  if (BE (new_entry != NULL, 1))
+    {
+      subtop->lasts[subtop->nlasts] = new_entry;
+      new_entry->node = node;
+      new_entry->str_idx = str_idx;
+      ++subtop->nlasts;
+    }
+  return new_entry;
+}
+
+static void
+internal_function
+sift_ctx_init (re_sift_context_t *sctx, re_dfastate_t **sifted_sts,
+	       re_dfastate_t **limited_sts, int last_node, int last_str_idx)
+{
+  sctx->sifted_states = sifted_sts;
+  sctx->limited_states = limited_sts;
+  sctx->last_node = last_node;
+  sctx->last_str_idx = last_str_idx;
+  re_node_set_init_empty (&sctx->limits);
+}
diff --git a/compat/setenv.c b/compat/setenv.c
index 3a22ea7..fc1439a 100644
--- a/compat/setenv.c
+++ b/compat/setenv.c
@@ -6,7 +6,10 @@
 	size_t namelen, valuelen;
 	char *envstr;
 
-	if (!name || !value) return -1;
+	if (!name || strchr(name, '=') || !value) {
+		errno = EINVAL;
+		return -1;
+	}
 	if (!replace) {
 		char *oldval = NULL;
 		oldval = getenv(name);
@@ -16,7 +19,10 @@
 	namelen = strlen(name);
 	valuelen = strlen(value);
 	envstr = malloc((namelen + valuelen + 2));
-	if (!envstr) return -1;
+	if (!envstr) {
+		errno = ENOMEM;
+		return -1;
+	}
 
 	memcpy(envstr, name, namelen);
 	envstr[namelen] = '=';
diff --git a/compat/snprintf.c b/compat/snprintf.c
index 6c0fb05..42ea1ac 100644
--- a/compat/snprintf.c
+++ b/compat/snprintf.c
@@ -2,11 +2,14 @@
 
 /*
  * The size parameter specifies the available space, i.e. includes
- * the trailing NUL byte; but Windows's vsnprintf expects the
- * number of characters to write without the trailing NUL.
+ * the trailing NUL byte; but Windows's vsnprintf uses the entire
+ * buffer and avoids the trailing NUL, should the buffer be exactly
+ * big enough for the result. Defining SNPRINTF_SIZE_CORR to 1 will
+ * therefore remove 1 byte from the reported buffer size, so we
+ * always have room for a trailing NUL byte.
  */
 #ifndef SNPRINTF_SIZE_CORR
-#if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ < 4
+#if defined(WIN32) && (!defined(__GNUC__) || __GNUC__ < 4)
 #define SNPRINTF_SIZE_CORR 1
 #else
 #define SNPRINTF_SIZE_CORR 0
@@ -16,11 +19,14 @@
 #undef vsnprintf
 int git_vsnprintf(char *str, size_t maxsize, const char *format, va_list ap)
 {
+	va_list cp;
 	char *s;
 	int ret = -1;
 
 	if (maxsize > 0) {
-		ret = vsnprintf(str, maxsize-SNPRINTF_SIZE_CORR, format, ap);
+		va_copy(cp, ap);
+		ret = vsnprintf(str, maxsize-SNPRINTF_SIZE_CORR, format, cp);
+		va_end(cp);
 		if (ret == maxsize-1)
 			ret = -1;
 		/* Windows does not NUL-terminate if result fills buffer */
@@ -39,7 +45,9 @@
 		if (! str)
 			break;
 		s = str;
-		ret = vsnprintf(str, maxsize-SNPRINTF_SIZE_CORR, format, ap);
+		va_copy(cp, ap);
+		ret = vsnprintf(str, maxsize-SNPRINTF_SIZE_CORR, format, cp);
+		va_end(cp);
 		if (ret == maxsize-1)
 			ret = -1;
 	}
diff --git a/compat/strtoimax.c b/compat/strtoimax.c
new file mode 100644
index 0000000..ac09ed8
--- /dev/null
+++ b/compat/strtoimax.c
@@ -0,0 +1,10 @@
+#include "../git-compat-util.h"
+
+intmax_t gitstrtoimax (const char *nptr, char **endptr, int base)
+{
+#if defined(NO_STRTOULL)
+	return strtol(nptr, endptr, base);
+#else
+	return strtoll(nptr, endptr, base);
+#endif
+}
diff --git a/compat/strtok_r.c b/compat/strtok_r.c
new file mode 100644
index 0000000..7b5d568
--- /dev/null
+++ b/compat/strtok_r.c
@@ -0,0 +1,61 @@
+/* Reentrant string tokenizer.  Generic version.
+   Copyright (C) 1991,1996-1999,2001,2004 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include "../git-compat-util.h"
+
+/* Parse S into tokens separated by characters in DELIM.
+   If S is NULL, the saved pointer in SAVE_PTR is used as
+   the next starting point.  For example:
+	char s[] = "-abc-=-def";
+	char *sp;
+	x = strtok_r(s, "-", &sp);	// x = "abc", sp = "=-def"
+	x = strtok_r(NULL, "-=", &sp);	// x = "def", sp = NULL
+	x = strtok_r(NULL, "=", &sp);	// x = NULL
+		// s = "abc\0-def\0"
+*/
+char *
+gitstrtok_r (char *s, const char *delim, char **save_ptr)
+{
+  char *token;
+
+  if (s == NULL)
+    s = *save_ptr;
+
+  /* Scan leading delimiters.  */
+  s += strspn (s, delim);
+  if (*s == '\0')
+    {
+      *save_ptr = s;
+      return NULL;
+    }
+
+  /* Find the end of the token.  */
+  token = s;
+  s = strpbrk (token, delim);
+  if (s == NULL)
+    /* This token finishes the string.  */
+    *save_ptr = token + strlen (token);
+  else
+    {
+      /* Terminate the token and make *SAVE_PTR point past it.  */
+      *s = '\0';
+      *save_ptr = s + 1;
+    }
+  return token;
+}
diff --git a/compat/terminal.c b/compat/terminal.c
new file mode 100644
index 0000000..6d16c8f
--- /dev/null
+++ b/compat/terminal.c
@@ -0,0 +1,81 @@
+#include "git-compat-util.h"
+#include "compat/terminal.h"
+#include "sigchain.h"
+#include "strbuf.h"
+
+#ifdef HAVE_DEV_TTY
+
+static int term_fd = -1;
+static struct termios old_term;
+
+static void restore_term(void)
+{
+	if (term_fd < 0)
+		return;
+
+	tcsetattr(term_fd, TCSAFLUSH, &old_term);
+	term_fd = -1;
+}
+
+static void restore_term_on_signal(int sig)
+{
+	restore_term();
+	sigchain_pop(sig);
+	raise(sig);
+}
+
+char *git_terminal_prompt(const char *prompt, int echo)
+{
+	static struct strbuf buf = STRBUF_INIT;
+	int r;
+	FILE *fh;
+
+	fh = fopen("/dev/tty", "w+");
+	if (!fh)
+		return NULL;
+
+	if (!echo) {
+		struct termios t;
+
+		if (tcgetattr(fileno(fh), &t) < 0) {
+			fclose(fh);
+			return NULL;
+		}
+
+		old_term = t;
+		term_fd = fileno(fh);
+		sigchain_push_common(restore_term_on_signal);
+
+		t.c_lflag &= ~ECHO;
+		if (tcsetattr(fileno(fh), TCSAFLUSH, &t) < 0) {
+			term_fd = -1;
+			fclose(fh);
+			return NULL;
+		}
+	}
+
+	fputs(prompt, fh);
+	fflush(fh);
+
+	r = strbuf_getline(&buf, fh, '\n');
+	if (!echo) {
+		putc('\n', fh);
+		fflush(fh);
+	}
+
+	restore_term();
+	fclose(fh);
+
+	if (r == EOF)
+		return NULL;
+	return buf.buf;
+}
+
+#else
+
+char *git_terminal_prompt(const char *prompt, int echo)
+{
+	return getpass(prompt);
+}
+
+#endif
diff --git a/compat/terminal.h b/compat/terminal.h
new file mode 100644
index 0000000..97db7cd
--- /dev/null
+++ b/compat/terminal.h
@@ -0,0 +1,6 @@
+#ifndef COMPAT_TERMINAL_H
+#define COMPAT_TERMINAL_H
+
+char *git_terminal_prompt(const char *prompt, int echo);
+
+#endif /* COMPAT_TERMINAL_H */
diff --git a/compat/vcbuild/README b/compat/vcbuild/README
new file mode 100644
index 0000000..df8a657
--- /dev/null
+++ b/compat/vcbuild/README
@@ -0,0 +1,50 @@
+The Steps of Build Git with VS2008
+
+1. You need the build environment, which contains the Git dependencies
+   to be able to compile, link and run Git with MSVC.
+
+   You can either use the binary repository:
+
+       WWW: http://repo.or.cz/w/msvcgit.git
+       Git: git clone git://repo.or.cz/msvcgit.git
+       Zip: http://repo.or.cz/w/msvcgit.git?a=snapshot;h=master;sf=zip
+
+   and call the setup_32bit_env.cmd batch script before compiling Git,
+   (see repo/package README for details), or the source repository:
+
+       WWW: http://repo.or.cz/w/gitbuild.git
+       Git: git clone git://repo.or.cz/gitbuild.git
+       Zip: (None, as it's a project with submodules)
+
+   and build the support libs as instructed in that repo/package.
+
+2. Ensure you have the msysgit environment in your path, so you have
+   GNU Make, bash and perl available.
+
+       WWW: http://repo.or.cz/w/msysgit.git
+       Git: git clone git://repo.or.cz/msysgit.git
+       Zip: http://repo.or.cz/w/msysgit.git?a=snapshot;h=master;sf=zip
+
+   This environment is also needed when you use the resulting
+   executables, since Git might need to run scripts which are part of
+   the git operations.
+
+3. Inside Git's directory run the command:
+       make common-cmds.h
+   to generate the common-cmds.h file needed to compile git.
+
+4. Then either build Git with the GNU Make Makefile in the Git projects
+   root
+       make MSVC=1
+   or generate Visual Studio solution/projects (.sln/.vcproj) with the
+   command
+       perl contrib/buildsystems/generate -g Vcproj
+   and open and build the solution with the IDE
+       devenv git.sln /useenv
+   or build with the IDE build engine directly from the command line
+       devenv git.sln /useenv /build "Release|Win32"
+   The /useenv option is required, so Visual Studio picks up the
+   environment variables for the support libraries required to build
+   Git, which you set up in step 1.
+
+Done!
diff --git a/compat/vcbuild/include/alloca.h b/compat/vcbuild/include/alloca.h
new file mode 100644
index 0000000..c0d7985
--- /dev/null
+++ b/compat/vcbuild/include/alloca.h
@@ -0,0 +1 @@
+#include <malloc.h>
diff --git a/compat/vcbuild/include/sys/param.h b/compat/vcbuild/include/sys/param.h
new file mode 100644
index 0000000..0d8552a
--- /dev/null
+++ b/compat/vcbuild/include/sys/param.h
@@ -0,0 +1 @@
+/* Intentionally empty file to support building git with MSVC */
diff --git a/compat/vcbuild/include/sys/poll.h b/compat/vcbuild/include/sys/poll.h
new file mode 100644
index 0000000..0d8552a
--- /dev/null
+++ b/compat/vcbuild/include/sys/poll.h
@@ -0,0 +1 @@
+/* Intentionally empty file to support building git with MSVC */
diff --git a/compat/vcbuild/include/sys/time.h b/compat/vcbuild/include/sys/time.h
new file mode 100644
index 0000000..0d8552a
--- /dev/null
+++ b/compat/vcbuild/include/sys/time.h
@@ -0,0 +1 @@
+/* Intentionally empty file to support building git with MSVC */
diff --git a/compat/vcbuild/include/sys/utime.h b/compat/vcbuild/include/sys/utime.h
new file mode 100644
index 0000000..582589c
--- /dev/null
+++ b/compat/vcbuild/include/sys/utime.h
@@ -0,0 +1,34 @@
+#ifndef	_UTIME_H_
+#define	_UTIME_H_
+/*
+ * UTIME.H
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is a part of the mingw-runtime package.
+ *
+ * The mingw-runtime package and its code is distributed in the hope that it
+ * will be useful but WITHOUT ANY WARRANTY.  ALL WARRANTIES, EXPRESSED OR
+ * IMPLIED ARE HEREBY DISCLAIMED.  This includes but is not limited to
+ * warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * You are free to use this package and its code without limitation.
+ */
+
+/*
+ * Structure used by _utime function.
+ */
+struct _utimbuf
+{
+	time_t	actime;		/* Access time */
+	time_t	modtime;	/* Modification time */
+};
+
+#ifndef	_NO_OLDNAMES
+/* NOTE: Must be the same as _utimbuf above. */
+struct utimbuf
+{
+	time_t	actime;
+	time_t	modtime;
+};
+#endif	/* Not _NO_OLDNAMES */
+
+#endif
diff --git a/compat/vcbuild/include/unistd.h b/compat/vcbuild/include/unistd.h
new file mode 100644
index 0000000..b14fcf9
--- /dev/null
+++ b/compat/vcbuild/include/unistd.h
@@ -0,0 +1,96 @@
+#ifndef _UNISTD_
+#define _UNISTD_
+
+/* Win32 define for porting git*/
+
+#ifndef _MODE_T_
+#define	_MODE_T_
+typedef unsigned short _mode_t;
+
+#ifndef	_NO_OLDNAMES
+typedef _mode_t	mode_t;
+#endif
+#endif	/* Not _MODE_T_ */
+
+#ifndef _SSIZE_T_
+#define _SSIZE_T_
+typedef long _ssize_t;
+
+#ifndef	_OFF_T_
+#define	_OFF_T_
+typedef long _off_t;
+
+#ifndef	_NO_OLDNAMES
+typedef _off_t	off_t;
+#endif
+#endif	/* Not _OFF_T_ */
+
+
+#ifndef	_NO_OLDNAMES
+typedef _ssize_t ssize_t;
+#endif
+#endif /* Not _SSIZE_T_ */
+
+typedef signed char int8_t;
+typedef unsigned char   uint8_t;
+typedef short  int16_t;
+typedef unsigned short  uint16_t;
+typedef int  int32_t;
+typedef unsigned   uint32_t;
+typedef long long  int64_t;
+typedef unsigned long long   uint64_t;
+
+typedef long long  intmax_t;
+typedef unsigned long long uintmax_t;
+
+typedef int64_t off64_t;
+
+#define INTMAX_MIN  _I64_MIN
+#define INTMAX_MAX  _I64_MAX
+#define UINTMAX_MAX _UI64_MAX
+
+#define STDOUT_FILENO 1
+#define STDERR_FILENO 2
+
+/* Some defines for _access nAccessMode (MS doesn't define them, but
+ * it doesn't seem to hurt to add them). */
+#define	F_OK	0	/* Check for file existence */
+/* Well maybe it does hurt.  On newer versions of MSVCRT, an access mode
+   of 1 causes invalid parameter error. */
+#define	X_OK	0	/* MS access() doesn't check for execute permission. */
+#define	W_OK	2	/* Check for write permission */
+#define	R_OK	4	/* Check for read permission */
+
+#define	_S_IFIFO	0x1000	/* FIFO */
+#define	_S_IFCHR	0x2000	/* Character */
+#define	_S_IFBLK	0x3000	/* Block: Is this ever set under w32? */
+#define	_S_IFDIR	0x4000	/* Directory */
+#define	_S_IFREG	0x8000	/* Regular */
+
+#define	_S_IFMT		0xF000	/* File type mask */
+
+#define	_S_IXUSR	_S_IEXEC
+#define	_S_IWUSR	_S_IWRITE
+#define	_S_IRUSR	_S_IREAD
+#define	_S_ISDIR(m)	(((m) & _S_IFMT) == _S_IFDIR)
+
+#define	S_IFIFO		_S_IFIFO
+#define	S_IFCHR		_S_IFCHR
+#define	S_IFBLK		_S_IFBLK
+#define	S_IFDIR		_S_IFDIR
+#define	S_IFREG		_S_IFREG
+#define	S_IFMT		_S_IFMT
+#define	S_IEXEC		_S_IEXEC
+#define	S_IWRITE	_S_IWRITE
+#define	S_IREAD		_S_IREAD
+#define	S_IRWXU		_S_IRWXU
+#define	S_IXUSR		_S_IXUSR
+#define	S_IWUSR		_S_IWUSR
+#define	S_IRUSR		_S_IRUSR
+
+
+#define	S_ISDIR(m)	(((m) & S_IFMT) == S_IFDIR)
+#define	S_ISREG(m)	(((m) & S_IFMT) == S_IFREG)
+#define	S_ISFIFO(m)	(((m) & S_IFMT) == S_IFIFO)
+
+#endif
diff --git a/compat/vcbuild/include/utime.h b/compat/vcbuild/include/utime.h
new file mode 100644
index 0000000..8285f38
--- /dev/null
+++ b/compat/vcbuild/include/utime.h
@@ -0,0 +1 @@
+#include <sys/utime.h>
diff --git a/compat/vcbuild/scripts/clink.pl b/compat/vcbuild/scripts/clink.pl
new file mode 100644
index 0000000..4374771
--- /dev/null
+++ b/compat/vcbuild/scripts/clink.pl
@@ -0,0 +1,52 @@
+#!/usr/bin/perl -w
+######################################################################
+# Compiles or links files
+#
+# This is a wrapper to facilitate the compilation of Git with MSVC
+# using GNU Make as the build system. So, instead of manipulating the
+# Makefile into something nasty, just to support non-space arguments
+# etc, we use this wrapper to fix the command line options
+#
+# Copyright (C) 2009 Marius Storm-Olsen <mstormo@gmail.com>
+######################################################################
+use strict;
+my @args = ();
+my @cflags = ();
+my $is_linking = 0;
+while (@ARGV) {
+	my $arg = shift @ARGV;
+	if ("$arg" =~ /^-[DIMGO]/) {
+		push(@cflags, $arg);
+	} elsif ("$arg" eq "-o") {
+		my $file_out = shift @ARGV;
+		if ("$file_out" =~ /exe$/) {
+			$is_linking = 1;
+			push(@args, "-OUT:$file_out");
+		} else {
+			push(@args, "-Fo$file_out");
+		}
+	} elsif ("$arg" eq "-lz") {
+		push(@args, "zlib.lib");
+	} elsif ("$arg" eq "-liconv") {
+		push(@args, "iconv.lib");
+	} elsif ("$arg" eq "-lcrypto") {
+		push(@args, "libeay32.lib");
+	} elsif ("$arg" eq "-lssl") {
+		push(@args, "ssleay32.lib");
+	} elsif ("$arg" =~ /^-L/ && "$arg" ne "-LTCG") {
+		$arg =~ s/^-L/-LIBPATH:/;
+		push(@args, $arg);
+	} elsif ("$arg" =~ /^-R/) {
+		# eat
+	} else {
+		push(@args, $arg);
+	}
+}
+if ($is_linking) {
+	unshift(@args, "link.exe");
+} else {
+	unshift(@args, "cl.exe");
+	push(@args, @cflags);
+}
+#printf("**** @args\n");
+exit (system(@args) != 0);
diff --git a/compat/vcbuild/scripts/lib.pl b/compat/vcbuild/scripts/lib.pl
new file mode 100644
index 0000000..d8054e4
--- /dev/null
+++ b/compat/vcbuild/scripts/lib.pl
@@ -0,0 +1,26 @@
+#!/usr/bin/perl -w
+######################################################################
+# Libifies files on Windows
+#
+# This is a wrapper to facilitate the compilation of Git with MSVC
+# using GNU Make as the build system. So, instead of manipulating the
+# Makefile into something nasty, just to support non-space arguments
+# etc, we use this wrapper to fix the command line options
+#
+# Copyright (C) 2009 Marius Storm-Olsen <mstormo@gmail.com>
+######################################################################
+use strict;
+my @args = ();
+while (@ARGV) {
+	my $arg = shift @ARGV;
+	if ("$arg" eq "rcs") {
+		# Consume the rcs option
+	} elsif ("$arg" =~ /\.a$/) {
+		push(@args, "-OUT:$arg");
+	} else {
+		push(@args, $arg);
+	}
+}
+unshift(@args, "lib.exe");
+# printf("**** @args\n");
+exit (system(@args) != 0);
diff --git a/compat/win32.h b/compat/win32.h
index c26384e..8ce9104 100644
--- a/compat/win32.h
+++ b/compat/win32.h
@@ -1,5 +1,10 @@
+#ifndef WIN32_H
+#define WIN32_H
+
 /* common Win32 functions for MinGW and Cygwin */
+#ifndef WIN32         /* Not defined by Cygwin */
 #include <windows.h>
+#endif
 
 static inline int file_attr_to_st_mode (DWORD attr)
 {
@@ -32,3 +37,5 @@
 		return ENOENT;
 	}
 }
+
+#endif
diff --git a/compat/win32/dirent.c b/compat/win32/dirent.c
new file mode 100644
index 0000000..7a0debe
--- /dev/null
+++ b/compat/win32/dirent.c
@@ -0,0 +1,108 @@
+#include "../git-compat-util.h"
+#include "dirent.h"
+
+struct DIR {
+	struct dirent dd_dir; /* includes d_type */
+	HANDLE dd_handle;     /* FindFirstFile handle */
+	int dd_stat;          /* 0-based index */
+	char dd_name[1];      /* extend struct */
+};
+
+DIR *opendir(const char *name)
+{
+	DWORD attrs = GetFileAttributesA(name);
+	int len;
+	DIR *p;
+
+	/* check for valid path */
+	if (attrs == INVALID_FILE_ATTRIBUTES) {
+		errno = ENOENT;
+		return NULL;
+	}
+
+	/* check if it's a directory */
+	if (!(attrs & FILE_ATTRIBUTE_DIRECTORY)) {
+		errno = ENOTDIR;
+		return NULL;
+	}
+
+	/* check that the pattern won't be too long for FindFirstFileA */
+	len = strlen(name);
+	if (is_dir_sep(name[len - 1]))
+		len--;
+	if (len + 2 >= MAX_PATH) {
+		errno = ENAMETOOLONG;
+		return NULL;
+	}
+
+	p = malloc(sizeof(DIR) + len + 2);
+	if (!p)
+		return NULL;
+
+	memset(p, 0, sizeof(DIR) + len + 2);
+	strcpy(p->dd_name, name);
+	p->dd_name[len] = '/';
+	p->dd_name[len+1] = '*';
+
+	p->dd_handle = INVALID_HANDLE_VALUE;
+	return p;
+}
+
+struct dirent *readdir(DIR *dir)
+{
+	WIN32_FIND_DATAA buf;
+	HANDLE handle;
+
+	if (!dir || !dir->dd_handle) {
+		errno = EBADF; /* No set_errno for mingw */
+		return NULL;
+	}
+
+	if (dir->dd_handle == INVALID_HANDLE_VALUE && dir->dd_stat == 0) {
+		DWORD lasterr;
+		handle = FindFirstFileA(dir->dd_name, &buf);
+		lasterr = GetLastError();
+		dir->dd_handle = handle;
+		if (handle == INVALID_HANDLE_VALUE && (lasterr != ERROR_NO_MORE_FILES)) {
+			errno = err_win_to_posix(lasterr);
+			return NULL;
+		}
+	} else if (dir->dd_handle == INVALID_HANDLE_VALUE) {
+		return NULL;
+	} else if (!FindNextFileA(dir->dd_handle, &buf)) {
+		DWORD lasterr = GetLastError();
+		FindClose(dir->dd_handle);
+		dir->dd_handle = INVALID_HANDLE_VALUE;
+		/* POSIX says you shouldn't set errno when readdir can't
+		   find any more files; so, if another error we leave it set. */
+		if (lasterr != ERROR_NO_MORE_FILES)
+			errno = err_win_to_posix(lasterr);
+		return NULL;
+	}
+
+	/* We get here if `buf' contains valid data.  */
+	strcpy(dir->dd_dir.d_name, buf.cFileName);
+	++dir->dd_stat;
+
+	/* Set file type, based on WIN32_FIND_DATA */
+	dir->dd_dir.d_type = 0;
+	if (buf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+		dir->dd_dir.d_type |= DT_DIR;
+	else
+		dir->dd_dir.d_type |= DT_REG;
+
+	return &dir->dd_dir;
+}
+
+int closedir(DIR *dir)
+{
+	if (!dir) {
+		errno = EBADF;
+		return -1;
+	}
+
+	if (dir->dd_handle != INVALID_HANDLE_VALUE)
+		FindClose(dir->dd_handle);
+	free(dir);
+	return 0;
+}
diff --git a/compat/win32/dirent.h b/compat/win32/dirent.h
new file mode 100644
index 0000000..927a25c
--- /dev/null
+++ b/compat/win32/dirent.h
@@ -0,0 +1,24 @@
+#ifndef DIRENT_H
+#define DIRENT_H
+
+typedef struct DIR DIR;
+
+#define DT_UNKNOWN 0
+#define DT_DIR     1
+#define DT_REG     2
+#define DT_LNK     3
+
+struct dirent {
+	long d_ino;                      /* Always zero. */
+	char d_name[FILENAME_MAX];       /* File name. */
+	union {
+		unsigned short d_reclen; /* Always zero. */
+		unsigned char  d_type;   /* Reimplementation adds this */
+	};
+};
+
+DIR *opendir(const char *dirname);
+struct dirent *readdir(DIR *dir);
+int closedir(DIR *dir);
+
+#endif /* DIRENT_H */
diff --git a/compat/win32/poll.c b/compat/win32/poll.c
new file mode 100644
index 0000000..403eaa7
--- /dev/null
+++ b/compat/win32/poll.c
@@ -0,0 +1,606 @@
+/* Emulation for poll(2)
+   Contributed by Paolo Bonzini.
+
+   Copyright 2001-2003, 2006-2011 Free Software Foundation, Inc.
+
+   This file is part of gnulib.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License along
+   with this program; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+/* Tell gcc not to warn about the (nfd < 0) tests, below.  */
+#if (__GNUC__ == 4 && 3 <= __GNUC_MINOR__) || 4 < __GNUC__
+# pragma GCC diagnostic ignored "-Wtype-limits"
+#endif
+
+#include <malloc.h>
+
+#include <sys/types.h>
+
+/* Specification.  */
+#include <poll.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <assert.h>
+
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+# define WIN32_NATIVE
+# if defined (_MSC_VER)
+#  define _WIN32_WINNT 0x0502
+# endif
+# include <winsock2.h>
+# include <windows.h>
+# include <io.h>
+# include <stdio.h>
+# include <conio.h>
+#else
+# include <sys/time.h>
+# include <sys/socket.h>
+# include <sys/select.h>
+# include <unistd.h>
+#endif
+
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_FILIO_H
+# include <sys/filio.h>
+#endif
+
+#include <time.h>
+
+#ifndef INFTIM
+# define INFTIM (-1)
+#endif
+
+/* BeOS does not have MSG_PEEK.  */
+#ifndef MSG_PEEK
+# define MSG_PEEK 0
+#endif
+
+#ifdef WIN32_NATIVE
+
+#define IsConsoleHandle(h) (((long) (h) & 3) == 3)
+
+static BOOL
+IsSocketHandle (HANDLE h)
+{
+  WSANETWORKEVENTS ev;
+
+  if (IsConsoleHandle (h))
+    return FALSE;
+
+  /* Under Wine, it seems that getsockopt returns 0 for pipes too.
+     WSAEnumNetworkEvents instead distinguishes the two correctly.  */
+  ev.lNetworkEvents = 0xDEADBEEF;
+  WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev);
+  return ev.lNetworkEvents != 0xDEADBEEF;
+}
+
+/* Declare data structures for ntdll functions.  */
+typedef struct _FILE_PIPE_LOCAL_INFORMATION {
+  ULONG NamedPipeType;
+  ULONG NamedPipeConfiguration;
+  ULONG MaximumInstances;
+  ULONG CurrentInstances;
+  ULONG InboundQuota;
+  ULONG ReadDataAvailable;
+  ULONG OutboundQuota;
+  ULONG WriteQuotaAvailable;
+  ULONG NamedPipeState;
+  ULONG NamedPipeEnd;
+} FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION;
+
+typedef struct _IO_STATUS_BLOCK
+{
+  union {
+    DWORD Status;
+    PVOID Pointer;
+  } u;
+  ULONG_PTR Information;
+} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
+
+typedef enum _FILE_INFORMATION_CLASS {
+  FilePipeLocalInformation = 24
+} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
+
+typedef DWORD (WINAPI *PNtQueryInformationFile)
+	 (HANDLE, IO_STATUS_BLOCK *, VOID *, ULONG, FILE_INFORMATION_CLASS);
+
+# ifndef PIPE_BUF
+#  define PIPE_BUF      512
+# endif
+
+/* Compute revents values for file handle H.  If some events cannot happen
+   for the handle, eliminate them from *P_SOUGHT.  */
+
+static int
+win32_compute_revents (HANDLE h, int *p_sought)
+{
+  int i, ret, happened;
+  INPUT_RECORD *irbuffer;
+  DWORD avail, nbuffer;
+  BOOL bRet;
+  IO_STATUS_BLOCK iosb;
+  FILE_PIPE_LOCAL_INFORMATION fpli;
+  static PNtQueryInformationFile NtQueryInformationFile;
+  static BOOL once_only;
+
+  switch (GetFileType (h))
+    {
+    case FILE_TYPE_PIPE:
+      if (!once_only)
+	{
+	  NtQueryInformationFile = (PNtQueryInformationFile)
+	    GetProcAddress (GetModuleHandle ("ntdll.dll"),
+			    "NtQueryInformationFile");
+	  once_only = TRUE;
+	}
+
+      happened = 0;
+      if (PeekNamedPipe (h, NULL, 0, NULL, &avail, NULL) != 0)
+	{
+	  if (avail)
+	    happened |= *p_sought & (POLLIN | POLLRDNORM);
+	}
+      else if (GetLastError () == ERROR_BROKEN_PIPE)
+	happened |= POLLHUP;
+
+      else
+	{
+	  /* It was the write-end of the pipe.  Check if it is writable.
+	     If NtQueryInformationFile fails, optimistically assume the pipe is
+	     writable.  This could happen on Win9x, where NtQueryInformationFile
+	     is not available, or if we inherit a pipe that doesn't permit
+	     FILE_READ_ATTRIBUTES access on the write end (I think this should
+	     not happen since WinXP SP2; WINE seems fine too).  Otherwise,
+	     ensure that enough space is available for atomic writes.  */
+	  memset (&iosb, 0, sizeof (iosb));
+	  memset (&fpli, 0, sizeof (fpli));
+
+	  if (!NtQueryInformationFile
+	      || NtQueryInformationFile (h, &iosb, &fpli, sizeof (fpli),
+					 FilePipeLocalInformation)
+	      || fpli.WriteQuotaAvailable >= PIPE_BUF
+	      || (fpli.OutboundQuota < PIPE_BUF &&
+		  fpli.WriteQuotaAvailable == fpli.OutboundQuota))
+	    happened |= *p_sought & (POLLOUT | POLLWRNORM | POLLWRBAND);
+	}
+      return happened;
+
+    case FILE_TYPE_CHAR:
+      ret = WaitForSingleObject (h, 0);
+      if (!IsConsoleHandle (h))
+	return ret == WAIT_OBJECT_0 ? *p_sought & ~(POLLPRI | POLLRDBAND) : 0;
+
+      nbuffer = avail = 0;
+      bRet = GetNumberOfConsoleInputEvents (h, &nbuffer);
+      if (bRet)
+	{
+	  /* Input buffer.  */
+	  *p_sought &= POLLIN | POLLRDNORM;
+	  if (nbuffer == 0)
+	    return POLLHUP;
+	  if (!*p_sought)
+	    return 0;
+
+	  irbuffer = (INPUT_RECORD *) alloca (nbuffer * sizeof (INPUT_RECORD));
+	  bRet = PeekConsoleInput (h, irbuffer, nbuffer, &avail);
+	  if (!bRet || avail == 0)
+	    return POLLHUP;
+
+	  for (i = 0; i < avail; i++)
+	    if (irbuffer[i].EventType == KEY_EVENT)
+	      return *p_sought;
+	  return 0;
+	}
+      else
+	{
+	  /* Screen buffer.  */
+	  *p_sought &= POLLOUT | POLLWRNORM | POLLWRBAND;
+	  return *p_sought;
+	}
+
+    default:
+      ret = WaitForSingleObject (h, 0);
+      if (ret == WAIT_OBJECT_0)
+	return *p_sought & ~(POLLPRI | POLLRDBAND);
+
+      return *p_sought & (POLLOUT | POLLWRNORM | POLLWRBAND);
+    }
+}
+
+/* Convert fd_sets returned by select into revents values.  */
+
+static int
+win32_compute_revents_socket (SOCKET h, int sought, long lNetworkEvents)
+{
+  int happened = 0;
+
+  if ((lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE)) == FD_ACCEPT)
+    happened |= (POLLIN | POLLRDNORM) & sought;
+
+  else if (lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE))
+    {
+      int r, error;
+
+      char data[64];
+      WSASetLastError (0);
+      r = recv (h, data, sizeof (data), MSG_PEEK);
+      error = WSAGetLastError ();
+      WSASetLastError (0);
+
+      if (r > 0 || error == WSAENOTCONN)
+	happened |= (POLLIN | POLLRDNORM) & sought;
+
+      /* Distinguish hung-up sockets from other errors.  */
+      else if (r == 0 || error == WSAESHUTDOWN || error == WSAECONNRESET
+	       || error == WSAECONNABORTED || error == WSAENETRESET)
+	happened |= POLLHUP;
+
+      else
+	happened |= POLLERR;
+    }
+
+  if (lNetworkEvents & (FD_WRITE | FD_CONNECT))
+    happened |= (POLLOUT | POLLWRNORM | POLLWRBAND) & sought;
+
+  if (lNetworkEvents & FD_OOB)
+    happened |= (POLLPRI | POLLRDBAND) & sought;
+
+  return happened;
+}
+
+#else /* !MinGW */
+
+/* Convert select(2) returned fd_sets into poll(2) revents values.  */
+static int
+compute_revents (int fd, int sought, fd_set *rfds, fd_set *wfds, fd_set *efds)
+{
+  int happened = 0;
+  if (FD_ISSET (fd, rfds))
+    {
+      int r;
+      int socket_errno;
+
+# if defined __MACH__ && defined __APPLE__
+      /* There is a bug in Mac OS X that causes it to ignore MSG_PEEK
+	 for some kinds of descriptors.  Detect if this descriptor is a
+	 connected socket, a server socket, or something else using a
+	 0-byte recv, and use ioctl(2) to detect POLLHUP.  */
+      r = recv (fd, NULL, 0, MSG_PEEK);
+      socket_errno = (r < 0) ? errno : 0;
+      if (r == 0 || socket_errno == ENOTSOCK)
+	ioctl (fd, FIONREAD, &r);
+# else
+      char data[64];
+      r = recv (fd, data, sizeof (data), MSG_PEEK);
+      socket_errno = (r < 0) ? errno : 0;
+# endif
+      if (r == 0)
+	happened |= POLLHUP;
+
+      /* If the event happened on an unconnected server socket,
+	 that's fine. */
+      else if (r > 0 || ( /* (r == -1) && */ socket_errno == ENOTCONN))
+	happened |= (POLLIN | POLLRDNORM) & sought;
+
+      /* Distinguish hung-up sockets from other errors.  */
+      else if (socket_errno == ESHUTDOWN || socket_errno == ECONNRESET
+	       || socket_errno == ECONNABORTED || socket_errno == ENETRESET)
+	happened |= POLLHUP;
+
+      else
+	happened |= POLLERR;
+    }
+
+  if (FD_ISSET (fd, wfds))
+    happened |= (POLLOUT | POLLWRNORM | POLLWRBAND) & sought;
+
+  if (FD_ISSET (fd, efds))
+    happened |= (POLLPRI | POLLRDBAND) & sought;
+
+  return happened;
+}
+#endif /* !MinGW */
+
+int
+poll (struct pollfd *pfd, nfds_t nfd, int timeout)
+{
+#ifndef WIN32_NATIVE
+  fd_set rfds, wfds, efds;
+  struct timeval tv;
+  struct timeval *ptv;
+  int maxfd, rc;
+  nfds_t i;
+
+# ifdef _SC_OPEN_MAX
+  static int sc_open_max = -1;
+
+  if (nfd < 0
+      || (nfd > sc_open_max
+	  && (sc_open_max != -1
+	      || nfd > (sc_open_max = sysconf (_SC_OPEN_MAX)))))
+    {
+      errno = EINVAL;
+      return -1;
+    }
+# else /* !_SC_OPEN_MAX */
+#  ifdef OPEN_MAX
+  if (nfd < 0 || nfd > OPEN_MAX)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+#  endif /* OPEN_MAX -- else, no check is needed */
+# endif /* !_SC_OPEN_MAX */
+
+  /* EFAULT is not necessary to implement, but let's do it in the
+     simplest case. */
+  if (!pfd)
+    {
+      errno = EFAULT;
+      return -1;
+    }
+
+  /* convert timeout number into a timeval structure */
+  if (timeout == 0)
+    {
+      ptv = &tv;
+      ptv->tv_sec = 0;
+      ptv->tv_usec = 0;
+    }
+  else if (timeout > 0)
+    {
+      ptv = &tv;
+      ptv->tv_sec = timeout / 1000;
+      ptv->tv_usec = (timeout % 1000) * 1000;
+    }
+  else if (timeout == INFTIM)
+    /* wait forever */
+    ptv = NULL;
+  else
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  /* create fd sets and determine max fd */
+  maxfd = -1;
+  FD_ZERO (&rfds);
+  FD_ZERO (&wfds);
+  FD_ZERO (&efds);
+  for (i = 0; i < nfd; i++)
+    {
+      if (pfd[i].fd < 0)
+	continue;
+
+      if (pfd[i].events & (POLLIN | POLLRDNORM))
+	FD_SET (pfd[i].fd, &rfds);
+
+      /* see select(2): "the only exceptional condition detectable
+	 is out-of-band data received on a socket", hence we push
+	 POLLWRBAND events onto wfds instead of efds. */
+      if (pfd[i].events & (POLLOUT | POLLWRNORM | POLLWRBAND))
+	FD_SET (pfd[i].fd, &wfds);
+      if (pfd[i].events & (POLLPRI | POLLRDBAND))
+	FD_SET (pfd[i].fd, &efds);
+      if (pfd[i].fd >= maxfd
+	  && (pfd[i].events & (POLLIN | POLLOUT | POLLPRI
+			       | POLLRDNORM | POLLRDBAND
+			       | POLLWRNORM | POLLWRBAND)))
+	{
+	  maxfd = pfd[i].fd;
+	  if (maxfd > FD_SETSIZE)
+	    {
+	      errno = EOVERFLOW;
+	      return -1;
+	    }
+	}
+    }
+
+  /* examine fd sets */
+  rc = select (maxfd + 1, &rfds, &wfds, &efds, ptv);
+  if (rc < 0)
+    return rc;
+
+  /* establish results */
+  rc = 0;
+  for (i = 0; i < nfd; i++)
+    if (pfd[i].fd < 0)
+      pfd[i].revents = 0;
+    else
+      {
+	int happened = compute_revents (pfd[i].fd, pfd[i].events,
+					&rfds, &wfds, &efds);
+	if (happened)
+	  {
+	    pfd[i].revents = happened;
+	    rc++;
+	  }
+      }
+
+  return rc;
+#else
+  static struct timeval tv0;
+  static HANDLE hEvent;
+  WSANETWORKEVENTS ev;
+  HANDLE h, handle_array[FD_SETSIZE + 2];
+  DWORD ret, wait_timeout, nhandles;
+  fd_set rfds, wfds, xfds;
+  BOOL poll_again;
+  MSG msg;
+  int rc = 0;
+  nfds_t i;
+
+  if (nfd < 0 || timeout < -1)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  if (!hEvent)
+    hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
+
+restart:
+  handle_array[0] = hEvent;
+  nhandles = 1;
+  FD_ZERO (&rfds);
+  FD_ZERO (&wfds);
+  FD_ZERO (&xfds);
+
+  /* Classify socket handles and create fd sets. */
+  for (i = 0; i < nfd; i++)
+    {
+      int sought = pfd[i].events;
+      pfd[i].revents = 0;
+      if (pfd[i].fd < 0)
+	continue;
+      if (!(sought & (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM | POLLWRBAND
+		      | POLLPRI | POLLRDBAND)))
+	continue;
+
+      h = (HANDLE) _get_osfhandle (pfd[i].fd);
+      assert (h != NULL);
+      if (IsSocketHandle (h))
+	{
+	  int requested = FD_CLOSE;
+
+	  /* see above; socket handles are mapped onto select.  */
+	  if (sought & (POLLIN | POLLRDNORM))
+	    {
+	      requested |= FD_READ | FD_ACCEPT;
+	      FD_SET ((SOCKET) h, &rfds);
+	    }
+	  if (sought & (POLLOUT | POLLWRNORM | POLLWRBAND))
+	    {
+	      requested |= FD_WRITE | FD_CONNECT;
+	      FD_SET ((SOCKET) h, &wfds);
+	    }
+	  if (sought & (POLLPRI | POLLRDBAND))
+	    {
+	      requested |= FD_OOB;
+	      FD_SET ((SOCKET) h, &xfds);
+	    }
+
+	  if (requested)
+	    WSAEventSelect ((SOCKET) h, hEvent, requested);
+	}
+      else
+	{
+	  /* Poll now.  If we get an event, do not poll again.  Also,
+	     screen buffer handles are waitable, and they'll block until
+	     a character is available.  win32_compute_revents eliminates
+	     bits for the "wrong" direction. */
+	  pfd[i].revents = win32_compute_revents (h, &sought);
+	  if (sought)
+	    handle_array[nhandles++] = h;
+	  if (pfd[i].revents)
+	    timeout = 0;
+	}
+    }
+
+  if (select (0, &rfds, &wfds, &xfds, &tv0) > 0)
+    {
+      /* Do MsgWaitForMultipleObjects anyway to dispatch messages, but
+	 no need to call select again.  */
+      poll_again = FALSE;
+      wait_timeout = 0;
+    }
+  else
+    {
+      poll_again = TRUE;
+      if (timeout == INFTIM)
+	wait_timeout = INFINITE;
+      else
+	wait_timeout = timeout;
+    }
+
+  for (;;)
+    {
+      ret = MsgWaitForMultipleObjects (nhandles, handle_array, FALSE,
+				       wait_timeout, QS_ALLINPUT);
+
+      if (ret == WAIT_OBJECT_0 + nhandles)
+	{
+	  /* new input of some other kind */
+	  BOOL bRet;
+	  while ((bRet = PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) != 0)
+	    {
+	      TranslateMessage (&msg);
+	      DispatchMessage (&msg);
+	    }
+	}
+      else
+	break;
+    }
+
+  if (poll_again)
+    select (0, &rfds, &wfds, &xfds, &tv0);
+
+  /* Place a sentinel at the end of the array.  */
+  handle_array[nhandles] = NULL;
+  nhandles = 1;
+  for (i = 0; i < nfd; i++)
+    {
+      int happened;
+
+      if (pfd[i].fd < 0)
+	continue;
+      if (!(pfd[i].events & (POLLIN | POLLRDNORM |
+			     POLLOUT | POLLWRNORM | POLLWRBAND)))
+	continue;
+
+      h = (HANDLE) _get_osfhandle (pfd[i].fd);
+      if (h != handle_array[nhandles])
+	{
+	  /* It's a socket.  */
+	  WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev);
+	  WSAEventSelect ((SOCKET) h, 0, 0);
+
+	  /* If we're lucky, WSAEnumNetworkEvents already provided a way
+	     to distinguish FD_READ and FD_ACCEPT; this saves a recv later.  */
+	  if (FD_ISSET ((SOCKET) h, &rfds)
+	      && !(ev.lNetworkEvents & (FD_READ | FD_ACCEPT)))
+	    ev.lNetworkEvents |= FD_READ | FD_ACCEPT;
+	  if (FD_ISSET ((SOCKET) h, &wfds))
+	    ev.lNetworkEvents |= FD_WRITE | FD_CONNECT;
+	  if (FD_ISSET ((SOCKET) h, &xfds))
+	    ev.lNetworkEvents |= FD_OOB;
+
+	  happened = win32_compute_revents_socket ((SOCKET) h, pfd[i].events,
+						   ev.lNetworkEvents);
+	}
+      else
+	{
+	  /* Not a socket.  */
+	  int sought = pfd[i].events;
+	  happened = win32_compute_revents (h, &sought);
+	  nhandles++;
+	}
+
+       if ((pfd[i].revents |= happened) != 0)
+	rc++;
+    }
+
+  if (!rc && timeout == INFTIM)
+    {
+      SwitchToThread();
+      goto restart;
+    }
+
+  return rc;
+#endif
+}
diff --git a/compat/win32/poll.h b/compat/win32/poll.h
new file mode 100644
index 0000000..b7aa59d
--- /dev/null
+++ b/compat/win32/poll.h
@@ -0,0 +1,53 @@
+/* Header for poll(2) emulation
+   Contributed by Paolo Bonzini.
+
+   Copyright 2001, 2002, 2003, 2007, 2009, 2010 Free Software Foundation, Inc.
+
+   This file is part of gnulib.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License along
+   with this program; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+#ifndef _GL_POLL_H
+#define _GL_POLL_H
+
+/* fake a poll(2) environment */
+#define POLLIN      0x0001      /* any readable data available   */
+#define POLLPRI     0x0002      /* OOB/Urgent readable data      */
+#define POLLOUT     0x0004      /* file descriptor is writeable  */
+#define POLLERR     0x0008      /* some poll error occurred      */
+#define POLLHUP     0x0010      /* file descriptor was "hung up" */
+#define POLLNVAL    0x0020      /* requested events "invalid"    */
+#define POLLRDNORM  0x0040
+#define POLLRDBAND  0x0080
+#define POLLWRNORM  0x0100
+#define POLLWRBAND  0x0200
+
+struct pollfd
+{
+  int fd;                       /* which file descriptor to poll */
+  short events;                 /* events we are interested in   */
+  short revents;                /* events found on return        */
+};
+
+typedef unsigned long nfds_t;
+
+extern int poll (struct pollfd *pfd, nfds_t nfd, int timeout);
+
+/* Define INFTIM only if doing so conforms to POSIX.  */
+#if !defined (_POSIX_C_SOURCE) && !defined (_XOPEN_SOURCE)
+#define INFTIM (-1)
+#endif
+
+#endif /* _GL_POLL_H */
diff --git a/compat/win32/pthread.c b/compat/win32/pthread.c
new file mode 100644
index 0000000..010e875
--- /dev/null
+++ b/compat/win32/pthread.c
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2009 Andrzej K. Haczewski <ahaczewski@gmail.com>
+ *
+ * DISCLAIMER: The implementation is Git-specific, it is subset of original
+ * Pthreads API, without lots of other features that Git doesn't use.
+ * Git also makes sure that the passed arguments are valid, so there's
+ * no need for double-checking.
+ */
+
+#include "../../git-compat-util.h"
+#include "pthread.h"
+
+#include <errno.h>
+#include <limits.h>
+
+static unsigned __stdcall win32_start_routine(void *arg)
+{
+	pthread_t *thread = arg;
+	thread->tid = GetCurrentThreadId();
+	thread->arg = thread->start_routine(thread->arg);
+	return 0;
+}
+
+int pthread_create(pthread_t *thread, const void *unused,
+		   void *(*start_routine)(void*), void *arg)
+{
+	thread->arg = arg;
+	thread->start_routine = start_routine;
+	thread->handle = (HANDLE)
+		_beginthreadex(NULL, 0, win32_start_routine, thread, 0, NULL);
+
+	if (!thread->handle)
+		return errno;
+	else
+		return 0;
+}
+
+int win32_pthread_join(pthread_t *thread, void **value_ptr)
+{
+	DWORD result = WaitForSingleObject(thread->handle, INFINITE);
+	switch (result) {
+		case WAIT_OBJECT_0:
+			if (value_ptr)
+				*value_ptr = thread->arg;
+			return 0;
+		case WAIT_ABANDONED:
+			return EINVAL;
+		default:
+			return err_win_to_posix(GetLastError());
+	}
+}
+
+pthread_t pthread_self(void)
+{
+	pthread_t t = { 0 };
+	t.tid = GetCurrentThreadId();
+	return t;
+}
+
+int pthread_cond_init(pthread_cond_t *cond, const void *unused)
+{
+	cond->waiters = 0;
+	cond->was_broadcast = 0;
+	InitializeCriticalSection(&cond->waiters_lock);
+
+	cond->sema = CreateSemaphore(NULL, 0, LONG_MAX, NULL);
+	if (!cond->sema)
+		die("CreateSemaphore() failed");
+
+	cond->continue_broadcast = CreateEvent(NULL,	/* security */
+				FALSE,			/* auto-reset */
+				FALSE,			/* not signaled */
+				NULL);			/* name */
+	if (!cond->continue_broadcast)
+		die("CreateEvent() failed");
+
+	return 0;
+}
+
+int pthread_cond_destroy(pthread_cond_t *cond)
+{
+	CloseHandle(cond->sema);
+	CloseHandle(cond->continue_broadcast);
+	DeleteCriticalSection(&cond->waiters_lock);
+	return 0;
+}
+
+int pthread_cond_wait(pthread_cond_t *cond, CRITICAL_SECTION *mutex)
+{
+	int last_waiter;
+
+	EnterCriticalSection(&cond->waiters_lock);
+	cond->waiters++;
+	LeaveCriticalSection(&cond->waiters_lock);
+
+	/*
+	 * Unlock external mutex and wait for signal.
+	 * NOTE: we've held mutex locked long enough to increment
+	 * waiters count above, so there's no problem with
+	 * leaving mutex unlocked before we wait on semaphore.
+	 */
+	LeaveCriticalSection(mutex);
+
+	/* let's wait - ignore return value */
+	WaitForSingleObject(cond->sema, INFINITE);
+
+	/*
+	 * Decrease waiters count. If we are the last waiter, then we must
+	 * notify the broadcasting thread that it can continue.
+	 * But if we continued due to cond_signal, we do not have to do that
+	 * because the signaling thread knows that only one waiter continued.
+	 */
+	EnterCriticalSection(&cond->waiters_lock);
+	cond->waiters--;
+	last_waiter = cond->was_broadcast && cond->waiters == 0;
+	LeaveCriticalSection(&cond->waiters_lock);
+
+	if (last_waiter) {
+		/*
+		 * cond_broadcast was issued while mutex was held. This means
+		 * that all other waiters have continued, but are contending
+		 * for the mutex at the end of this function because the
+		 * broadcasting thread did not leave cond_broadcast, yet.
+		 * (This is so that it can be sure that each waiter has
+		 * consumed exactly one slice of the semaphor.)
+		 * The last waiter must tell the broadcasting thread that it
+		 * can go on.
+		 */
+		SetEvent(cond->continue_broadcast);
+		/*
+		 * Now we go on to contend with all other waiters for
+		 * the mutex. Auf in den Kampf!
+		 */
+	}
+	/* lock external mutex again */
+	EnterCriticalSection(mutex);
+
+	return 0;
+}
+
+/*
+ * IMPORTANT: This implementation requires that pthread_cond_signal
+ * is called while the mutex is held that is used in the corresponding
+ * pthread_cond_wait calls!
+ */
+int pthread_cond_signal(pthread_cond_t *cond)
+{
+	int have_waiters;
+
+	EnterCriticalSection(&cond->waiters_lock);
+	have_waiters = cond->waiters > 0;
+	LeaveCriticalSection(&cond->waiters_lock);
+
+	/*
+	 * Signal only when there are waiters
+	 */
+	if (have_waiters)
+		return ReleaseSemaphore(cond->sema, 1, NULL) ?
+			0 : err_win_to_posix(GetLastError());
+	else
+		return 0;
+}
+
+/*
+ * DOUBLY IMPORTANT: This implementation requires that pthread_cond_broadcast
+ * is called while the mutex is held that is used in the corresponding
+ * pthread_cond_wait calls!
+ */
+int pthread_cond_broadcast(pthread_cond_t *cond)
+{
+	EnterCriticalSection(&cond->waiters_lock);
+
+	if ((cond->was_broadcast = cond->waiters > 0)) {
+		/* wake up all waiters */
+		ReleaseSemaphore(cond->sema, cond->waiters, NULL);
+		LeaveCriticalSection(&cond->waiters_lock);
+		/*
+		 * At this point all waiters continue. Each one takes its
+		 * slice of the semaphor. Now it's our turn to wait: Since
+		 * the external mutex is held, no thread can leave cond_wait,
+		 * yet. For this reason, we can be sure that no thread gets
+		 * a chance to eat *more* than one slice. OTOH, it means
+		 * that the last waiter must send us a wake-up.
+		 */
+		WaitForSingleObject(cond->continue_broadcast, INFINITE);
+		/*
+		 * Since the external mutex is held, no thread can enter
+		 * cond_wait, and, hence, it is safe to reset this flag
+		 * without cond->waiters_lock held.
+		 */
+		cond->was_broadcast = 0;
+	} else {
+		LeaveCriticalSection(&cond->waiters_lock);
+	}
+	return 0;
+}
diff --git a/compat/win32/pthread.h b/compat/win32/pthread.h
new file mode 100644
index 0000000..8ad1873
--- /dev/null
+++ b/compat/win32/pthread.h
@@ -0,0 +1,104 @@
+/*
+ * Header used to adapt pthread-based POSIX code to Windows API threads.
+ *
+ * Copyright (C) 2009 Andrzej K. Haczewski <ahaczewski@gmail.com>
+ */
+
+#ifndef PTHREAD_H
+#define PTHREAD_H
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+
+#include <windows.h>
+
+/*
+ * Defines that adapt Windows API threads to pthreads API
+ */
+#define pthread_mutex_t CRITICAL_SECTION
+
+#define pthread_mutex_init(a,b) (InitializeCriticalSection((a)), 0)
+#define pthread_mutex_destroy(a) DeleteCriticalSection((a))
+#define pthread_mutex_lock EnterCriticalSection
+#define pthread_mutex_unlock LeaveCriticalSection
+
+typedef int pthread_mutexattr_t;
+#define pthread_mutexattr_init(a) (*(a) = 0)
+#define pthread_mutexattr_destroy(a) do {} while (0)
+#define pthread_mutexattr_settype(a, t) 0
+#define PTHREAD_MUTEX_RECURSIVE 0
+
+/*
+ * Implement simple condition variable for Windows threads, based on ACE
+ * implementation.
+ *
+ * See original implementation: http://bit.ly/1vkDjo
+ * ACE homepage: http://www.cse.wustl.edu/~schmidt/ACE.html
+ * See also: http://www.cse.wustl.edu/~schmidt/win32-cv-1.html
+ */
+typedef struct {
+	LONG waiters;
+	int was_broadcast;
+	CRITICAL_SECTION waiters_lock;
+	HANDLE sema;
+	HANDLE continue_broadcast;
+} pthread_cond_t;
+
+extern int pthread_cond_init(pthread_cond_t *cond, const void *unused);
+extern int pthread_cond_destroy(pthread_cond_t *cond);
+extern int pthread_cond_wait(pthread_cond_t *cond, CRITICAL_SECTION *mutex);
+extern int pthread_cond_signal(pthread_cond_t *cond);
+extern int pthread_cond_broadcast(pthread_cond_t *cond);
+
+/*
+ * Simple thread creation implementation using pthread API
+ */
+typedef struct {
+	HANDLE handle;
+	void *(*start_routine)(void*);
+	void *arg;
+	DWORD tid;
+} pthread_t;
+
+extern int pthread_create(pthread_t *thread, const void *unused,
+			  void *(*start_routine)(void*), void *arg);
+
+/*
+ * To avoid the need of copying a struct, we use small macro wrapper to pass
+ * pointer to win32_pthread_join instead.
+ */
+#define pthread_join(a, b) win32_pthread_join(&(a), (b))
+
+extern int win32_pthread_join(pthread_t *thread, void **value_ptr);
+
+#define pthread_equal(t1, t2) ((t1).tid == (t2).tid)
+extern pthread_t pthread_self(void);
+
+static inline int pthread_exit(void *ret)
+{
+	ExitThread((DWORD)ret);
+}
+
+typedef DWORD pthread_key_t;
+static inline int pthread_key_create(pthread_key_t *keyp, void (*destructor)(void *value))
+{
+	return (*keyp = TlsAlloc()) == TLS_OUT_OF_INDEXES ? EAGAIN : 0;
+}
+
+static inline int pthread_key_delete(pthread_key_t key)
+{
+	return TlsFree(key) ? 0 : EINVAL;
+}
+
+static inline int pthread_setspecific(pthread_key_t key, const void *value)
+{
+	return TlsSetValue(key, (void *)value) ? 0 : EINVAL;
+}
+
+static inline void *pthread_getspecific(pthread_key_t key)
+{
+	return TlsGetValue(key);
+}
+
+#endif /* PTHREAD_H */
diff --git a/compat/win32/syslog.c b/compat/win32/syslog.c
new file mode 100644
index 0000000..d015e43
--- /dev/null
+++ b/compat/win32/syslog.c
@@ -0,0 +1,78 @@
+#include "../../git-compat-util.h"
+
+static HANDLE ms_eventlog;
+
+void openlog(const char *ident, int logopt, int facility)
+{
+	if (ms_eventlog)
+		return;
+
+	ms_eventlog = RegisterEventSourceA(NULL, ident);
+
+	if (!ms_eventlog)
+		warning("RegisterEventSource() failed: %lu", GetLastError());
+}
+
+void syslog(int priority, const char *fmt, ...)
+{
+	WORD logtype;
+	char *str, *pos;
+	int str_len;
+	va_list ap;
+
+	if (!ms_eventlog)
+		return;
+
+	va_start(ap, fmt);
+	str_len = vsnprintf(NULL, 0, fmt, ap);
+	va_end(ap);
+
+	if (str_len < 0) {
+		warning("vsnprintf failed: '%s'", strerror(errno));
+		return;
+	}
+
+	str = malloc(str_len + 1);
+	if (!str) {
+		warning("malloc failed: '%s'", strerror(errno));
+		return;
+	}
+
+	va_start(ap, fmt);
+	vsnprintf(str, str_len + 1, fmt, ap);
+	va_end(ap);
+
+	while ((pos = strstr(str, "%1")) != NULL) {
+		str = realloc(str, ++str_len + 1);
+		if (!str) {
+			warning("realloc failed: '%s'", strerror(errno));
+			return;
+		}
+		memmove(pos + 2, pos + 1, strlen(pos));
+		pos[1] = ' ';
+	}
+
+	switch (priority) {
+	case LOG_EMERG:
+	case LOG_ALERT:
+	case LOG_CRIT:
+	case LOG_ERR:
+		logtype = EVENTLOG_ERROR_TYPE;
+		break;
+
+	case LOG_WARNING:
+		logtype = EVENTLOG_WARNING_TYPE;
+		break;
+
+	case LOG_NOTICE:
+	case LOG_INFO:
+	case LOG_DEBUG:
+	default:
+		logtype = EVENTLOG_INFORMATION_TYPE;
+		break;
+	}
+
+	ReportEventA(ms_eventlog, logtype, 0, 0, NULL, 1, 0,
+	    (const char **)&str, NULL);
+	free(str);
+}
diff --git a/compat/win32/syslog.h b/compat/win32/syslog.h
new file mode 100644
index 0000000..70daa7c
--- /dev/null
+++ b/compat/win32/syslog.h
@@ -0,0 +1,20 @@
+#ifndef SYSLOG_H
+#define SYSLOG_H
+
+#define LOG_PID     0x01
+
+#define LOG_EMERG   0
+#define LOG_ALERT   1
+#define LOG_CRIT    2
+#define LOG_ERR     3
+#define LOG_WARNING 4
+#define LOG_NOTICE  5
+#define LOG_INFO    6
+#define LOG_DEBUG   7
+
+#define LOG_DAEMON  (3<<3)
+
+void openlog(const char *ident, int logopt, int facility);
+void syslog(int priority, const char *fmt, ...);
+
+#endif /* SYSLOG_H */
diff --git a/compat/win32mmap.c b/compat/win32mmap.c
index 779d796..61d2ef8 100644
--- a/compat/win32mmap.c
+++ b/compat/win32mmap.c
@@ -1,34 +1,22 @@
 #include "../git-compat-util.h"
 
-/*
- * Note that this doesn't return the actual pagesize, but
- * the allocation granularity. If future Windows specific git code
- * needs the real getpagesize function, we need to find another solution.
- */
-int mingw_getpagesize(void)
-{
-	SYSTEM_INFO si;
-	GetSystemInfo(&si);
-	return si.dwAllocationGranularity;
-}
-
 void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset)
 {
 	HANDLE hmap;
 	void *temp;
-	size_t len;
+	off_t len;
 	struct stat st;
 	uint64_t o = offset;
 	uint32_t l = o & 0xFFFFFFFF;
 	uint32_t h = (o >> 32) & 0xFFFFFFFF;
 
 	if (!fstat(fd, &st))
-		len = xsize_t(st.st_size);
+		len = st.st_size;
 	else
 		die("mmap: could not determine filesize");
 
 	if ((length + offset) > len)
-		length = len - offset;
+		length = xsize_t(len - offset);
 
 	if (!(flags & MAP_PRIVATE))
 		die("Invalid usage of mmap when built with USE_WIN32_MMAP");
@@ -42,7 +30,7 @@
 	temp = MapViewOfFileEx(hmap, FILE_MAP_COPY, h, l, length, start);
 
 	if (!CloseHandle(hmap))
-		warning("unable to close file mapping handle\n");
+		warning("unable to close file mapping handle");
 
 	return temp ? temp : MAP_FAILED;
 }
diff --git a/compat/winansi.c b/compat/winansi.c
index 9217c24..dedce21 100644
--- a/compat/winansi.c
+++ b/compat/winansi.c
@@ -2,7 +2,6 @@
  * Copyright 2008 Peter Harris <git@peter.is-a-geek.org>
  */
 
-#include <windows.h>
 #include "../git-compat-util.h"
 
 /*
diff --git a/config.c b/config.c
index e87edea..2b706ea 100644
--- a/config.c
+++ b/config.c
@@ -7,16 +7,166 @@
  */
 #include "cache.h"
 #include "exec_cmd.h"
+#include "strbuf.h"
+#include "quote.h"
 
 #define MAXNAME (256)
 
-static FILE *config_file;
-static const char *config_file_name;
-static int config_linenr;
-static int config_file_eof;
+typedef struct config_file {
+	struct config_file *prev;
+	FILE *f;
+	const char *name;
+	int linenr;
+	int eof;
+	struct strbuf value;
+	char var[MAXNAME];
+} config_file;
+
+static config_file *cf;
+
 static int zlib_compression_seen;
 
-const char *config_exclusive_filename = NULL;
+#define MAX_INCLUDE_DEPTH 10
+static const char include_depth_advice[] =
+"exceeded maximum include depth (%d) while including\n"
+"	%s\n"
+"from\n"
+"	%s\n"
+"Do you have circular includes?";
+static int handle_path_include(const char *path, struct config_include_data *inc)
+{
+	int ret = 0;
+	struct strbuf buf = STRBUF_INIT;
+	char *expanded = expand_user_path(path);
+
+	if (!expanded)
+		return error("Could not expand include path '%s'", path);
+	path = expanded;
+
+	/*
+	 * Use an absolute path as-is, but interpret relative paths
+	 * based on the including config file.
+	 */
+	if (!is_absolute_path(path)) {
+		char *slash;
+
+		if (!cf || !cf->name)
+			return error("relative config includes must come from files");
+
+		slash = find_last_dir_sep(cf->name);
+		if (slash)
+			strbuf_add(&buf, cf->name, slash - cf->name + 1);
+		strbuf_addstr(&buf, path);
+		path = buf.buf;
+	}
+
+	if (!access(path, R_OK)) {
+		if (++inc->depth > MAX_INCLUDE_DEPTH)
+			die(include_depth_advice, MAX_INCLUDE_DEPTH, path,
+			    cf && cf->name ? cf->name : "the command line");
+		ret = git_config_from_file(git_config_include, path, inc);
+		inc->depth--;
+	}
+	strbuf_release(&buf);
+	free(expanded);
+	return ret;
+}
+
+int git_config_include(const char *var, const char *value, void *data)
+{
+	struct config_include_data *inc = data;
+	const char *type;
+	int ret;
+
+	/*
+	 * Pass along all values, including "include" directives; this makes it
+	 * possible to query information on the includes themselves.
+	 */
+	ret = inc->fn(var, value, inc->data);
+	if (ret < 0)
+		return ret;
+
+	type = skip_prefix(var, "include.");
+	if (!type)
+		return ret;
+
+	if (!strcmp(type, "path"))
+		ret = handle_path_include(value, inc);
+	return ret;
+}
+
+static void lowercase(char *p)
+{
+	for (; *p; p++)
+		*p = tolower(*p);
+}
+
+void git_config_push_parameter(const char *text)
+{
+	struct strbuf env = STRBUF_INIT;
+	const char *old = getenv(CONFIG_DATA_ENVIRONMENT);
+	if (old) {
+		strbuf_addstr(&env, old);
+		strbuf_addch(&env, ' ');
+	}
+	sq_quote_buf(&env, text);
+	setenv(CONFIG_DATA_ENVIRONMENT, env.buf, 1);
+	strbuf_release(&env);
+}
+
+int git_config_parse_parameter(const char *text,
+			       config_fn_t fn, void *data)
+{
+	struct strbuf **pair;
+	pair = strbuf_split_str(text, '=', 2);
+	if (!pair[0])
+		return error("bogus config parameter: %s", text);
+	if (pair[0]->len && pair[0]->buf[pair[0]->len - 1] == '=')
+		strbuf_setlen(pair[0], pair[0]->len - 1);
+	strbuf_trim(pair[0]);
+	if (!pair[0]->len) {
+		strbuf_list_free(pair);
+		return error("bogus config parameter: %s", text);
+	}
+	lowercase(pair[0]->buf);
+	if (fn(pair[0]->buf, pair[1] ? pair[1]->buf : NULL, data) < 0) {
+		strbuf_list_free(pair);
+		return -1;
+	}
+	strbuf_list_free(pair);
+	return 0;
+}
+
+int git_config_from_parameters(config_fn_t fn, void *data)
+{
+	const char *env = getenv(CONFIG_DATA_ENVIRONMENT);
+	char *envw;
+	const char **argv = NULL;
+	int nr = 0, alloc = 0;
+	int i;
+
+	if (!env)
+		return 0;
+	/* sq_dequote will write over it */
+	envw = xstrdup(env);
+
+	if (sq_dequote_to_argv(envw, &argv, &nr, &alloc) < 0) {
+		free(envw);
+		return error("bogus format in " CONFIG_DATA_ENVIRONMENT);
+	}
+
+	for (i = 0; i < nr; i++) {
+		if (git_config_parse_parameter(argv[i], fn, data) < 0) {
+			free(argv);
+			free(envw);
+			return -1;
+		}
+	}
+
+	free(argv);
+	free(envw);
+	return nr > 0;
+}
 
 static int get_next_char(void)
 {
@@ -24,7 +174,7 @@
 	FILE *f;
 
 	c = '\n';
-	if ((f = config_file) != NULL) {
+	if (cf && ((f = cf->f) != NULL)) {
 		c = fgetc(f);
 		if (c == '\r') {
 			/* DOS like systems */
@@ -35,9 +185,9 @@
 			}
 		}
 		if (c == '\n')
-			config_linenr++;
+			cf->linenr++;
 		if (c == EOF) {
-			config_file_eof = 1;
+			cf->eof = 1;
 			c = '\n';
 		}
 	}
@@ -46,23 +196,22 @@
 
 static char *parse_value(void)
 {
-	static char value[1024];
-	int quote = 0, comment = 0, len = 0, space = 0;
+	int quote = 0, comment = 0, space = 0;
 
+	strbuf_reset(&cf->value);
 	for (;;) {
 		int c = get_next_char();
-		if (len >= sizeof(value) - 1)
-			return NULL;
 		if (c == '\n') {
-			if (quote)
+			if (quote) {
+				cf->linenr--;
 				return NULL;
-			value[len] = 0;
-			return value;
+			}
+			return cf->value.buf;
 		}
 		if (comment)
 			continue;
 		if (isspace(c) && !quote) {
-			if (len)
+			if (cf->value.len)
 				space++;
 			continue;
 		}
@@ -73,7 +222,7 @@
 			}
 		}
 		for (; space; space--)
-			value[len++] = ' ';
+			strbuf_addch(&cf->value, ' ');
 		if (c == '\\') {
 			c = get_next_char();
 			switch (c) {
@@ -95,14 +244,14 @@
 			default:
 				return NULL;
 			}
-			value[len++] = c;
+			strbuf_addch(&cf->value, c);
 			continue;
 		}
 		if (c == '"') {
 			quote = 1-quote;
 			continue;
 		}
-		value[len++] = c;
+		strbuf_addch(&cf->value, c);
 	}
 }
 
@@ -119,7 +268,7 @@
 	/* Get the full name */
 	for (;;) {
 		c = get_next_char();
-		if (config_file_eof)
+		if (cf->eof)
 			break;
 		if (!iskeychar(c))
 			break;
@@ -146,7 +295,7 @@
 {
 	do {
 		if (c == '\n')
-			return -1;
+			goto error_incomplete_line;
 		c = get_next_char();
 	} while (isspace(c));
 
@@ -158,13 +307,13 @@
 	for (;;) {
 		int c = get_next_char();
 		if (c == '\n')
-			return -1;
+			goto error_incomplete_line;
 		if (c == '"')
 			break;
 		if (c == '\\') {
 			c = get_next_char();
 			if (c == '\n')
-				return -1;
+				goto error_incomplete_line;
 		}
 		name[baselen++] = c;
 		if (baselen > MAXNAME / 2)
@@ -175,6 +324,9 @@
 	if (get_next_char() != ']')
 		return -1;
 	return baselen;
+error_incomplete_line:
+	cf->linenr--;
+	return -1;
 }
 
 static int get_base_var(char *name)
@@ -183,7 +335,7 @@
 
 	for (;;) {
 		int c = get_next_char();
-		if (config_file_eof)
+		if (cf->eof)
 			return -1;
 		if (c == ']')
 			return baselen;
@@ -201,7 +353,7 @@
 {
 	int comment = 0;
 	int baselen = 0;
-	static char var[MAXNAME];
+	char *var = cf->var;
 
 	/* U+FEFF Byte Order Mark in UTF8 */
 	static const unsigned char *utf8_bom = (unsigned char *) "\xef\xbb\xbf";
@@ -225,7 +377,7 @@
 			}
 		}
 		if (c == '\n') {
-			if (config_file_eof)
+			if (cf->eof)
 				return 0;
 			comment = 0;
 			continue;
@@ -250,10 +402,10 @@
 		if (get_value(fn, data, var, baselen+1) < 0)
 			break;
 	}
-	die("bad config file line %d in %s", config_linenr, config_file_name);
+	die("bad config file line %d in %s", cf->linenr, cf->name);
 }
 
-static int parse_unit_factor(const char *end, unsigned long *val)
+static int parse_unit_factor(const char *end, uintmax_t *val)
 {
 	if (!*end)
 		return 1;
@@ -276,11 +428,23 @@
 {
 	if (value && *value) {
 		char *end;
-		long val = strtol(value, &end, 0);
-		unsigned long factor = 1;
+		intmax_t val;
+		uintmax_t uval;
+		uintmax_t factor = 1;
+
+		errno = 0;
+		val = strtoimax(value, &end, 0);
+		if (errno == ERANGE)
+			return 0;
 		if (!parse_unit_factor(end, &factor))
 			return 0;
-		*ret = val * factor;
+		uval = abs(val);
+		uval *= factor;
+		if ((uval > maximum_signed_value_of_type(long)) ||
+		    (abs(val) > uval))
+			return 0;
+		val *= factor;
+		*ret = val;
 		return 1;
 	}
 	return 0;
@@ -290,9 +454,19 @@
 {
 	if (value && *value) {
 		char *end;
-		unsigned long val = strtoul(value, &end, 0);
+		uintmax_t val;
+		uintmax_t oldval;
+
+		errno = 0;
+		val = strtoumax(value, &end, 0);
+		if (errno == ERANGE)
+			return 0;
+		oldval = val;
 		if (!parse_unit_factor(end, &val))
 			return 0;
+		if ((val > maximum_unsigned_value_of_type(long)) ||
+		    (oldval > val))
+			return 0;
 		*ret = val;
 		return 1;
 	}
@@ -301,8 +475,8 @@
 
 static void die_bad_config(const char *name)
 {
-	if (config_file_name)
-		die("bad config value for '%s' in %s", name, config_file_name);
+	if (cf && cf->name)
+		die("bad config value for '%s' in %s", name, cf->name);
 	die("bad config value for '%s'", name);
 }
 
@@ -322,17 +496,40 @@
 	return ret;
 }
 
-int git_config_bool_or_int(const char *name, const char *value, int *is_bool)
+static int git_config_maybe_bool_text(const char *name, const char *value)
 {
-	*is_bool = 1;
 	if (!value)
 		return 1;
 	if (!*value)
 		return 0;
-	if (!strcasecmp(value, "true") || !strcasecmp(value, "yes") || !strcasecmp(value, "on"))
+	if (!strcasecmp(value, "true")
+	    || !strcasecmp(value, "yes")
+	    || !strcasecmp(value, "on"))
 		return 1;
-	if (!strcasecmp(value, "false") || !strcasecmp(value, "no") || !strcasecmp(value, "off"))
+	if (!strcasecmp(value, "false")
+	    || !strcasecmp(value, "no")
+	    || !strcasecmp(value, "off"))
 		return 0;
+	return -1;
+}
+
+int git_config_maybe_bool(const char *name, const char *value)
+{
+	long v = git_config_maybe_bool_text(name, value);
+	if (0 <= v)
+		return v;
+	if (git_parse_long(value, &v))
+		return !!v;
+	return -1;
+}
+
+int git_config_bool_or_int(const char *name, const char *value, int *is_bool)
+{
+	int v = git_config_maybe_bool_text(name, value);
+	if (0 <= v) {
+		*is_bool = 1;
+		return v;
+	}
 	*is_bool = 0;
 	return git_config_int(name, value);
 }
@@ -351,6 +548,16 @@
 	return 0;
 }
 
+int git_config_pathname(const char **dest, const char *var, const char *value)
+{
+	if (!value)
+		return config_error_nonbool(var);
+	*dest = expand_user_path(value);
+	if (!*dest)
+		die("Failed to expand user dir in: '%s'", value);
+	return 0;
+}
+
 static int git_default_core_config(const char *var, const char *value)
 {
 	/* This needs a better name */
@@ -378,6 +585,9 @@
 		return 0;
 	}
 
+	if (!strcmp(var, "core.attributesfile"))
+		return git_config_pathname(&git_attributes_file, var, value);
+
 	if (!strcmp(var, "core.bare")) {
 		is_bare_repository_cfg = git_config_bool(var, value);
 		return 0;
@@ -403,6 +613,14 @@
 		return 0;
 	}
 
+	if (!strcmp(var, "core.abbrev")) {
+		int abbrev = git_config_int(var, value);
+		if (abbrev < minimum_abbrev || abbrev > 40)
+			return -1;
+		default_abbrev = abbrev;
+		return 0;
+	}
+
 	if (!strcmp(var, "core.loosecompression")) {
 		int level = git_config_int(var, value);
 		if (level == -1)
@@ -429,7 +647,7 @@
 
 	if (!strcmp(var, "core.packedgitwindowsize")) {
 		int pgsz_x2 = getpagesize() * 2;
-		packed_git_window_size = git_config_int(var, value);
+		packed_git_window_size = git_config_ulong(var, value);
 
 		/* This value must be multiple of (pagesize * 2) */
 		packed_git_window_size /= pgsz_x2;
@@ -439,19 +657,29 @@
 		return 0;
 	}
 
+	if (!strcmp(var, "core.bigfilethreshold")) {
+		big_file_threshold = git_config_ulong(var, value);
+		return 0;
+	}
+
 	if (!strcmp(var, "core.packedgitlimit")) {
-		packed_git_limit = git_config_int(var, value);
+		packed_git_limit = git_config_ulong(var, value);
 		return 0;
 	}
 
 	if (!strcmp(var, "core.deltabasecachelimit")) {
-		delta_base_cache_limit = git_config_int(var, value);
+		delta_base_cache_limit = git_config_ulong(var, value);
 		return 0;
 	}
 
+	if (!strcmp(var, "core.logpackaccess"))
+		return git_config_string(&log_pack_access, var, value);
+
 	if (!strcmp(var, "core.autocrlf")) {
 		if (value && !strcasecmp(value, "input")) {
-			auto_crlf = -1;
+			if (core_eol == EOL_CRLF)
+				return error("core.autocrlf=input conflicts with core.eol=crlf");
+			auto_crlf = AUTO_CRLF_INPUT;
 			return 0;
 		}
 		auto_crlf = git_config_bool(var, value);
@@ -467,14 +695,36 @@
 		return 0;
 	}
 
+	if (!strcmp(var, "core.eol")) {
+		if (value && !strcasecmp(value, "lf"))
+			core_eol = EOL_LF;
+		else if (value && !strcasecmp(value, "crlf"))
+			core_eol = EOL_CRLF;
+		else if (value && !strcasecmp(value, "native"))
+			core_eol = EOL_NATIVE;
+		else
+			core_eol = EOL_UNSET;
+		if (core_eol == EOL_CRLF && auto_crlf == AUTO_CRLF_INPUT)
+			return error("core.autocrlf=input conflicts with core.eol=crlf");
+		return 0;
+	}
+
+	if (!strcmp(var, "core.notesref")) {
+		notes_ref_name = xstrdup(value);
+		return 0;
+	}
+
 	if (!strcmp(var, "core.pager"))
 		return git_config_string(&pager_program, var, value);
 
 	if (!strcmp(var, "core.editor"))
 		return git_config_string(&editor_program, var, value);
 
+	if (!strcmp(var, "core.askpass"))
+		return git_config_string(&askpass_program, var, value);
+
 	if (!strcmp(var, "core.excludesfile"))
-		return git_config_string(&excludes_file, var, value);
+		return git_config_pathname(&excludes_file, var, value);
 
 	if (!strcmp(var, "core.whitespace")) {
 		if (!value)
@@ -503,27 +753,13 @@
 		return 0;
 	}
 
-	/* Add other config variables here and to Documentation/config.txt. */
-	return 0;
-}
-
-static int git_default_user_config(const char *var, const char *value)
-{
-	if (!strcmp(var, "user.name")) {
-		if (!value)
-			return config_error_nonbool(var);
-		strlcpy(git_default_name, value, sizeof(git_default_name));
-		if (git_default_email[0])
-			user_ident_explicitly_given = 1;
+	if (!strcmp(var, "core.sparsecheckout")) {
+		core_apply_sparse_checkout = git_config_bool(var, value);
 		return 0;
 	}
 
-	if (!strcmp(var, "user.email")) {
-		if (!value)
-			return config_error_nonbool(var);
-		strlcpy(git_default_email, value, sizeof(git_default_email));
-		if (git_default_name[0])
-			user_ident_explicitly_given = 1;
+	if (!strcmp(var, "core.precomposeunicode")) {
+		precomposed_unicode = git_config_bool(var, value);
 		return 0;
 	}
 
@@ -582,14 +818,18 @@
 			push_default = PUSH_DEFAULT_NOTHING;
 		else if (!strcmp(value, "matching"))
 			push_default = PUSH_DEFAULT_MATCHING;
-		else if (!strcmp(value, "tracking"))
-			push_default = PUSH_DEFAULT_TRACKING;
+		else if (!strcmp(value, "simple"))
+			push_default = PUSH_DEFAULT_SIMPLE;
+		else if (!strcmp(value, "upstream"))
+			push_default = PUSH_DEFAULT_UPSTREAM;
+		else if (!strcmp(value, "tracking")) /* deprecated */
+			push_default = PUSH_DEFAULT_UPSTREAM;
 		else if (!strcmp(value, "current"))
 			push_default = PUSH_DEFAULT_CURRENT;
 		else {
 			error("Malformed value for %s: %s", var, value);
-			return error("Must be one of nothing, matching, "
-				     "tracking or current.");
+			return error("Must be one of nothing, matching, simple, "
+				     "upstream or current.");
 		}
 		return 0;
 	}
@@ -613,7 +853,7 @@
 		return git_default_core_config(var, value);
 
 	if (!prefixcmp(var, "user."))
-		return git_default_user_config(var, value);
+		return git_ident_config(var, value, dummy);
 
 	if (!prefixcmp(var, "i18n."))
 		return git_default_i18n_config(var, value);
@@ -627,11 +867,18 @@
 	if (!prefixcmp(var, "mailmap."))
 		return git_default_mailmap_config(var, value);
 
+	if (!prefixcmp(var, "advice."))
+		return git_default_advice_config(var, value);
+
 	if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) {
 		pager_use_color = git_config_bool(var,value);
 		return 0;
 	}
 
+	if (!strcmp(var, "pack.packsizelimit")) {
+		pack_size_limit_cfg = git_config_ulong(var, value);
+		return 0;
+	}
 	/* Add other config variables here and to Documentation/config.txt. */
 	return 0;
 }
@@ -643,13 +890,24 @@
 
 	ret = -1;
 	if (f) {
-		config_file = f;
-		config_file_name = filename;
-		config_linenr = 1;
-		config_file_eof = 0;
+		config_file top;
+
+		/* push config-file parsing state stack */
+		top.prev = cf;
+		top.f = f;
+		top.name = filename;
+		top.linenr = 1;
+		top.eof = 0;
+		strbuf_init(&top.value, 1024);
+		cf = &top;
+
 		ret = git_parse_file(fn, data);
+
+		/* pop config-file parsing state stack */
+		strbuf_release(&top.value);
+		cf = top.prev;
+
 		fclose(f);
-		config_file_name = NULL;
 	}
 	return ret;
 }
@@ -662,7 +920,7 @@
 	return system_wide;
 }
 
-static int git_env_bool(const char *k, int def)
+int git_env_bool(const char *k, int def)
 {
 	const char *v = getenv(k);
 	return v ? git_config_bool(k, v) : def;
@@ -673,47 +931,84 @@
 	return !git_env_bool("GIT_CONFIG_NOSYSTEM", 0);
 }
 
-int git_config_global(void)
-{
-	return !git_env_bool("GIT_CONFIG_NOGLOBAL", 0);
-}
-
-int git_config(config_fn_t fn, void *data)
+int git_config_early(config_fn_t fn, void *data, const char *repo_config)
 {
 	int ret = 0, found = 0;
-	char *repo_config = NULL;
-	const char *home = NULL;
+	char *xdg_config = NULL;
+	char *user_config = NULL;
 
-	/* Setting $GIT_CONFIG makes git read _only_ the given config file. */
-	if (config_exclusive_filename)
-		return git_config_from_file(fn, config_exclusive_filename, data);
+	home_config_paths(&user_config, &xdg_config, "config");
+
 	if (git_config_system() && !access(git_etc_gitconfig(), R_OK)) {
 		ret += git_config_from_file(fn, git_etc_gitconfig(),
 					    data);
 		found += 1;
 	}
 
-	home = getenv("HOME");
-	if (git_config_global() && home) {
-		char *user_config = xstrdup(mkpath("%s/.gitconfig", home));
-		if (!access(user_config, R_OK)) {
-			ret += git_config_from_file(fn, user_config, data);
-			found += 1;
-		}
-		free(user_config);
+	if (xdg_config && !access(xdg_config, R_OK)) {
+		ret += git_config_from_file(fn, xdg_config, data);
+		found += 1;
 	}
 
-	repo_config = git_pathdup("config");
-	if (!access(repo_config, R_OK)) {
+	if (user_config && !access(user_config, R_OK)) {
+		ret += git_config_from_file(fn, user_config, data);
+		found += 1;
+	}
+
+	if (repo_config && !access(repo_config, R_OK)) {
 		ret += git_config_from_file(fn, repo_config, data);
 		found += 1;
 	}
-	free(repo_config);
-	if (found == 0)
-		return -1;
+
+	switch (git_config_from_parameters(fn, data)) {
+	case -1: /* error */
+		die("unable to parse command-line config");
+		break;
+	case 0: /* found nothing */
+		break;
+	default: /* found at least one item */
+		found++;
+		break;
+	}
+
+	free(xdg_config);
+	free(user_config);
+	return ret == 0 ? found : ret;
+}
+
+int git_config_with_options(config_fn_t fn, void *data,
+			    const char *filename, int respect_includes)
+{
+	char *repo_config = NULL;
+	int ret;
+	struct config_include_data inc = CONFIG_INCLUDE_INIT;
+
+	if (respect_includes) {
+		inc.fn = fn;
+		inc.data = data;
+		fn = git_config_include;
+		data = &inc;
+	}
+
+	/*
+	 * If we have a specific filename, use it. Otherwise, follow the
+	 * regular lookup sequence.
+	 */
+	if (filename)
+		return git_config_from_file(fn, filename, data);
+
+	repo_config = git_pathdup("config");
+	ret = git_config_early(fn, data, repo_config);
+	if (repo_config)
+		free(repo_config);
 	return ret;
 }
 
+int git_config(config_fn_t fn, void *data)
+{
+	return git_config_with_options(fn, data, NULL, 1);
+}
+
 /*
  * Find all the stuff for git_config_set() below.
  */
@@ -743,6 +1038,7 @@
 {
 	const char *ep;
 	size_t section_len;
+	FILE *f = cf->f;
 
 	switch (store.state) {
 	case KEY_SEEN:
@@ -754,7 +1050,7 @@
 				return 1;
 			}
 
-			store.offset[store.seen] = ftell(config_file);
+			store.offset[store.seen] = ftell(f);
 			store.seen++;
 		}
 		break;
@@ -781,19 +1077,19 @@
 		 * Do not increment matches: this is no match, but we
 		 * just made sure we are in the desired section.
 		 */
-		store.offset[store.seen] = ftell(config_file);
+		store.offset[store.seen] = ftell(f);
 		/* fallthru */
 	case SECTION_END_SEEN:
 	case START:
 		if (matches(key, value)) {
-			store.offset[store.seen] = ftell(config_file);
+			store.offset[store.seen] = ftell(f);
 			store.state = KEY_SEEN;
 			store.seen++;
 		} else {
 			if (strrchr(key, '.') - key == store.baselen &&
 			      !strncmp(key, store.key, store.baselen)) {
 					store.state = SECTION_SEEN;
-					store.offset[store.seen] = ftell(config_file);
+					store.offset[store.seen] = ftell(f);
 			}
 		}
 	}
@@ -907,12 +1203,87 @@
 	return offset;
 }
 
+int git_config_set_in_file(const char *config_filename,
+			const char *key, const char *value)
+{
+	return git_config_set_multivar_in_file(config_filename, key, value, NULL, 0);
+}
+
 int git_config_set(const char *key, const char *value)
 {
 	return git_config_set_multivar(key, value, NULL, 0);
 }
 
 /*
+ * Auxiliary function to sanity-check and split the key into the section
+ * identifier and variable name.
+ *
+ * Returns 0 on success, -1 when there is an invalid character in the key and
+ * -2 if there is no section name in the key.
+ *
+ * store_key - pointer to char* which will hold a copy of the key with
+ *             lowercase section and variable name
+ * baselen - pointer to int which will hold the length of the
+ *           section + subsection part, can be NULL
+ */
+int git_config_parse_key(const char *key, char **store_key, int *baselen_)
+{
+	int i, dot, baselen;
+	const char *last_dot = strrchr(key, '.');
+
+	/*
+	 * Since "key" actually contains the section name and the real
+	 * key name separated by a dot, we have to know where the dot is.
+	 */
+
+	if (last_dot == NULL || last_dot == key) {
+		error("key does not contain a section: %s", key);
+		return -CONFIG_NO_SECTION_OR_NAME;
+	}
+
+	if (!last_dot[1]) {
+		error("key does not contain variable name: %s", key);
+		return -CONFIG_NO_SECTION_OR_NAME;
+	}
+
+	baselen = last_dot - key;
+	if (baselen_)
+		*baselen_ = baselen;
+
+	/*
+	 * Validate the key and while at it, lower case it for matching.
+	 */
+	*store_key = xmalloc(strlen(key) + 1);
+
+	dot = 0;
+	for (i = 0; key[i]; i++) {
+		unsigned char c = key[i];
+		if (c == '.')
+			dot = 1;
+		/* Leave the extended basename untouched.. */
+		if (!dot || i > baselen) {
+			if (!iskeychar(c) ||
+			    (i == baselen + 1 && !isalpha(c))) {
+				error("invalid key: %s", key);
+				goto out_free_ret_1;
+			}
+			c = tolower(c);
+		} else if (c == '\n') {
+			error("invalid key (newline): %s", key);
+			goto out_free_ret_1;
+		}
+		(*store_key)[i] = c;
+	}
+	(*store_key)[i] = 0;
+
+	return 0;
+
+out_free_ret_1:
+	free(*store_key);
+	return -CONFIG_INVALID_KEY;
+}
+
+/*
  * If value==NULL, unset in (remove from) config,
  * if value_regex!=NULL, disregard key/value pairs where value does not match.
  * if multi_replace==0, nothing, or only one matching key/value is replaced,
@@ -935,62 +1306,24 @@
  * - the config file is removed and the lock file rename()d to it.
  *
  */
-int git_config_set_multivar(const char *key, const char *value,
-	const char *value_regex, int multi_replace)
+int git_config_set_multivar_in_file(const char *config_filename,
+				const char *key, const char *value,
+				const char *value_regex, int multi_replace)
 {
-	int i, dot;
 	int fd = -1, in_fd;
 	int ret;
-	char *config_filename;
 	struct lock_file *lock = NULL;
-	const char *last_dot = strrchr(key, '.');
+	char *filename_buf = NULL;
 
-	if (config_exclusive_filename)
-		config_filename = xstrdup(config_exclusive_filename);
-	else
-		config_filename = git_pathdup("config");
-
-	/*
-	 * Since "key" actually contains the section name and the real
-	 * key name separated by a dot, we have to know where the dot is.
-	 */
-
-	if (last_dot == NULL) {
-		error("key does not contain a section: %s", key);
-		ret = 2;
+	/* parse-key returns negative; flip the sign to feed exit(3) */
+	ret = 0 - git_config_parse_key(key, &store.key, &store.baselen);
+	if (ret)
 		goto out_free;
-	}
-	store.baselen = last_dot - key;
 
 	store.multi_replace = multi_replace;
 
-	/*
-	 * Validate the key and while at it, lower case it for matching.
-	 */
-	store.key = xmalloc(strlen(key) + 1);
-	dot = 0;
-	for (i = 0; key[i]; i++) {
-		unsigned char c = key[i];
-		if (c == '.')
-			dot = 1;
-		/* Leave the extended basename untouched.. */
-		if (!dot || i > store.baselen) {
-			if (!iskeychar(c) || (i == store.baselen+1 && !isalpha(c))) {
-				error("invalid key: %s", key);
-				free(store.key);
-				ret = 1;
-				goto out_free;
-			}
-			c = tolower(c);
-		} else if (c == '\n') {
-			error("invalid key (newline): %s", key);
-			free(store.key);
-			ret = 1;
-			goto out_free;
-		}
-		store.key[i] = c;
-	}
-	store.key[i] = 0;
+	if (!config_filename)
+		config_filename = filename_buf = git_pathdup("config");
 
 	/*
 	 * The lock serves a purpose in addition to locking: the new
@@ -1001,7 +1334,7 @@
 	if (fd < 0) {
 		error("could not lock config file %s: %s", config_filename, strerror(errno));
 		free(store.key);
-		ret = -1;
+		ret = CONFIG_NO_LOCK;
 		goto out_free;
 	}
 
@@ -1015,12 +1348,12 @@
 		if ( ENOENT != errno ) {
 			error("opening %s: %s", config_filename,
 			      strerror(errno));
-			ret = 3; /* same as "invalid config file" */
+			ret = CONFIG_INVALID_FILE; /* same as "invalid config file" */
 			goto out_free;
 		}
 		/* if nothing to unset, error out */
 		if (value == NULL) {
-			ret = 5;
+			ret = CONFIG_NOTHING_SET;
 			goto out_free;
 		}
 
@@ -1048,7 +1381,7 @@
 					REG_EXTENDED)) {
 				error("invalid pattern: %s", value_regex);
 				free(store.value_regex);
-				ret = 6;
+				ret = CONFIG_INVALID_PATTERN;
 				goto out_free;
 			}
 		}
@@ -1070,7 +1403,7 @@
 				regfree(store.value_regex);
 				free(store.value_regex);
 			}
-			ret = 3;
+			ret = CONFIG_INVALID_FILE;
 			goto out_free;
 		}
 
@@ -1083,7 +1416,7 @@
 		/* if nothing to unset, or too many matches, error out */
 		if ((store.seen == 0 && value == NULL) ||
 				(store.seen > 1 && multi_replace == 0)) {
-			ret = 5;
+			ret = CONFIG_NOTHING_SET;
 			goto out_free;
 		}
 
@@ -1116,7 +1449,7 @@
 				    copy_end - copy_begin)
 					goto write_err_out;
 				if (new_line &&
-				    write_in_full(fd, "\n", 1) != 1)
+				    write_str_in_full(fd, "\n") != 1)
 					goto write_err_out;
 			}
 			copy_begin = store.offset[i];
@@ -1144,7 +1477,7 @@
 
 	if (commit_lock_file(lock) < 0) {
 		error("could not commit config file %s", config_filename);
-		ret = 4;
+		ret = CONFIG_NO_WRITE;
 		goto out_free;
 	}
 
@@ -1160,7 +1493,7 @@
 out_free:
 	if (lock)
 		rollback_lock_file(lock);
-	free(config_filename);
+	free(filename_buf);
 	return ret;
 
 write_err_out:
@@ -1169,6 +1502,13 @@
 
 }
 
+int git_config_set_multivar(const char *key, const char *value,
+			const char *value_regex, int multi_replace)
+{
+	return git_config_set_multivar_in_file(NULL, key, value, value_regex,
+					       multi_replace);
+}
+
 static int section_name_match (const char *buf, const char *name)
 {
 	int i = 0, j = 0, dot = 0;
@@ -1208,19 +1548,42 @@
 	return 0;
 }
 
+static int section_name_is_ok(const char *name)
+{
+	/* Empty section names are bogus. */
+	if (!*name)
+		return 0;
+
+	/*
+	 * Before a dot, we must be alphanumeric or dash. After the first dot,
+	 * anything goes, so we can stop checking.
+	 */
+	for (; *name && *name != '.'; name++)
+		if (*name != '-' && !isalnum(*name))
+			return 0;
+	return 1;
+}
+
 /* if new_name == NULL, the section is removed instead */
-int git_config_rename_section(const char *old_name, const char *new_name)
+int git_config_rename_section_in_file(const char *config_filename,
+				      const char *old_name, const char *new_name)
 {
 	int ret = 0, remove = 0;
-	char *config_filename;
-	struct lock_file *lock = xcalloc(sizeof(struct lock_file), 1);
+	char *filename_buf = NULL;
+	struct lock_file *lock;
 	int out_fd;
 	char buf[1024];
+	FILE *config_file;
 
-	if (config_exclusive_filename)
-		config_filename = xstrdup(config_exclusive_filename);
-	else
-		config_filename = git_pathdup("config");
+	if (new_name && !section_name_is_ok(new_name)) {
+		ret = error("invalid section name: %s", new_name);
+		goto out;
+	}
+
+	if (!config_filename)
+		config_filename = filename_buf = git_pathdup("config");
+
+	lock = xcalloc(sizeof(struct lock_file), 1);
 	out_fd = hold_lock_file_for_update(lock, config_filename, 0);
 	if (out_fd < 0) {
 		ret = error("could not lock config file %s", config_filename);
@@ -1280,14 +1643,19 @@
 		}
 	}
 	fclose(config_file);
- unlock_and_out:
+unlock_and_out:
 	if (commit_lock_file(lock) < 0)
 		ret = error("could not commit config file %s", config_filename);
- out:
-	free(config_filename);
+out:
+	free(filename_buf);
 	return ret;
 }
 
+int git_config_rename_section(const char *old_name, const char *new_name)
+{
+	return git_config_rename_section_in_file(NULL, old_name, new_name);
+}
+
 /*
  * Call this to report error for your variable that should not
  * get a boolean value (i.e. "[my] var" means "true").
diff --git a/config.mak.in b/config.mak.in
index 67b12f7..802d342 100644
--- a/config.mak.in
+++ b/config.mak.in
@@ -3,10 +3,12 @@
 
 CC = @CC@
 CFLAGS = @CFLAGS@
+CPPFLAGS = @CPPFLAGS@
 LDFLAGS = @LDFLAGS@
 CC_LD_DYNPATH = @CC_LD_DYNPATH@
 AR = @AR@
 TAR = @TAR@
+DIFF = @DIFF@
 #INSTALL = @INSTALL@		# needs install-sh or install.sh in sources
 TCLTK_PATH = @TCLTK_PATH@
 
@@ -16,6 +18,7 @@
 gitexecdir = @libexecdir@/git-core
 datarootdir = @datarootdir@
 template_dir = @datadir@/git-core/templates
+sysconfdir = @sysconfdir@
 
 mandir=@mandir@
 
@@ -25,12 +28,15 @@
 export exec_prefix mandir
 export srcdir VPATH
 
-ASCIIDOC8=@ASCIIDOC8@
 NEEDS_SSL_WITH_CRYPTO=@NEEDS_SSL_WITH_CRYPTO@
 NO_OPENSSL=@NO_OPENSSL@
 NO_CURL=@NO_CURL@
 NO_EXPAT=@NO_EXPAT@
 NO_LIBGEN_H=@NO_LIBGEN_H@
+HAVE_PATHS_H=@HAVE_PATHS_H@
+HAVE_LIBCHARSET_H=@HAVE_LIBCHARSET_H@
+NO_GETTEXT=@NO_GETTEXT@
+LIBC_CONTAINS_LIBINTL=@LIBC_CONTAINS_LIBINTL@
 NEEDS_LIBICONV=@NEEDS_LIBICONV@
 NEEDS_SOCKET=@NEEDS_SOCKET@
 NEEDS_RESOLV=@NEEDS_RESOLV@
@@ -40,8 +46,11 @@
 NO_D_TYPE_IN_DIRENT=@NO_D_TYPE_IN_DIRENT@
 NO_SOCKADDR_STORAGE=@NO_SOCKADDR_STORAGE@
 NO_IPV6=@NO_IPV6@
-NO_C99_FORMAT=@NO_C99_FORMAT@
+NO_HSTRERROR=@NO_HSTRERROR@
 NO_STRCASESTR=@NO_STRCASESTR@
+NO_STRTOK_R=@NO_STRTOK_R@
+NO_FNMATCH=@NO_FNMATCH@
+NO_FNMATCH_CASEFOLD=@NO_FNMATCH_CASEFOLD@
 NO_MEMMEM=@NO_MEMMEM@
 NO_STRLCPY=@NO_STRLCPY@
 NO_UINTMAX_T=@NO_UINTMAX_T@
@@ -50,11 +59,18 @@
 NO_UNSETENV=@NO_UNSETENV@
 NO_MKDTEMP=@NO_MKDTEMP@
 NO_MKSTEMPS=@NO_MKSTEMPS@
+NO_INET_NTOP=@NO_INET_NTOP@
+NO_INET_PTON=@NO_INET_PTON@
 NO_ICONV=@NO_ICONV@
 OLD_ICONV=@OLD_ICONV@
+NO_REGEX=@NO_REGEX@
+USE_LIBPCRE=@USE_LIBPCRE@
 NO_DEFLATE_BOUND=@NO_DEFLATE_BOUND@
+INLINE=@INLINE@
+SOCKLEN_T=@SOCKLEN_T@
 FREAD_READS_DIRECTORIES=@FREAD_READS_DIRECTORIES@
 SNPRINTF_RETURNS_BOGUS=@SNPRINTF_RETURNS_BOGUS@
 NO_PTHREADS=@NO_PTHREADS@
-THREADED_DELTA_SEARCH=@THREADED_DELTA_SEARCH@
+PTHREAD_CFLAGS=@PTHREAD_CFLAGS@
 PTHREAD_LIBS=@PTHREAD_LIBS@
+CHARSET_LIB=@CHARSET_LIB@
diff --git a/configure.ac b/configure.ac
index b09b8e4..df7e376 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,54 +1,67 @@
 #                                               -*- Autoconf -*-
 # Process this file with autoconf to produce a configure script.
 
-AC_PREREQ(2.59)
-AC_INIT([git], [@@GIT_VERSION@@], [git@vger.kernel.org])
+## Definitions of private macros.
 
-AC_CONFIG_SRCDIR([git.c])
+# GIT_CONF_SUBST(VAL, VAR)
+# ------------------------
+# Cause the line "VAR=VAL" to be eventually appended to ${config_file}.
+AC_DEFUN([GIT_CONF_SUBST],
+   [AC_REQUIRE([GIT_CONF_SUBST_INIT])
+   config_appended_defs="$config_appended_defs${newline}$1=$2"])
 
-config_file=config.mak.autogen
-config_append=config.mak.append
-config_in=config.mak.in
+# GIT_CONF_SUBST_INIT
+# -------------------
+# Prepare shell variables and autoconf machine required by later calls
+# to GIT_CONF_SUBST.
+AC_DEFUN([GIT_CONF_SUBST_INIT],
+    [config_appended_defs=; newline='
+'
+    AC_CONFIG_COMMANDS([$config_file],
+                       [echo "$config_appended_defs" >> "$config_file"],
+                       [config_file=$config_file
+                        config_appended_defs="$config_appended_defs"])])
 
-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
-#
 # GIT_ARG_SET_PATH(PROGRAM)
 # -------------------------
 # Provide --with-PROGRAM=PATH option to set PATH to PROGRAM
+# Optional second argument allows setting NO_PROGRAM=YesPlease if
+# --without-PROGRAM version used.
 AC_DEFUN([GIT_ARG_SET_PATH],
-[AC_ARG_WITH([$1],
- [AS_HELP_STRING([--with-$1=PATH],
-                 [provide PATH to $1])],
- [GIT_CONF_APPEND_PATH($1)],[])
-])# GIT_ARG_SET_PATH
-#
+    [AC_ARG_WITH([$1],
+        [AS_HELP_STRING([--with-$1=PATH],
+                        [provide PATH to $1])],
+        [GIT_CONF_APPEND_PATH([$1], [$2])],
+        [])])
+
 # GIT_CONF_APPEND_PATH(PROGRAM)
-# ------------------------------
+# -----------------------------
 # Parse --with-PROGRAM=PATH option to set PROGRAM_PATH=PATH
 # Used by GIT_ARG_SET_PATH(PROGRAM)
+# Optional second argument allows setting NO_PROGRAM=YesPlease if
+# --without-PROGRAM is used.
 AC_DEFUN([GIT_CONF_APPEND_PATH],
-[PROGRAM=m4_toupper($1); \
-if test "$withval" = "no"; then \
-	AC_MSG_ERROR([You cannot use git without $1]); \
-else \
-	if test "$withval" = "yes"; then \
-		AC_MSG_WARN([You should provide path for --with-$1=PATH]); \
-	else \
-		m4_toupper($1)_PATH=$withval; \
-		AC_MSG_NOTICE([Setting m4_toupper($1)_PATH to $withval]); \
-		GIT_CONF_APPEND_LINE(${PROGRAM}_PATH=$withval); \
-	fi; \
-fi; \
-]) # GIT_CONF_APPEND_PATH
-#
+    [m4_pushdef([GIT_UC_PROGRAM], m4_toupper([$1]))dnl
+    if test "$withval" = "no"; then
+	if test -n "$2"; then
+		GIT_UC_PROGRAM[]_PATH=$withval
+		AC_MSG_NOTICE([Disabling use of GIT_UC_PROGRAM])
+		GIT_CONF_SUBST([NO_]GIT_UC_PROGRAM, [YesPlease])
+		GIT_CONF_SUBST(GIT_UC_PROGRAM[]_PATH, [])
+	else
+		AC_MSG_ERROR([You cannot use git without $1])
+	fi
+    else
+	if test "$withval" = "yes"; then
+		AC_MSG_WARN([You should provide path for --with-$1=PATH])
+	else
+		GIT_UC_PROGRAM[]_PATH=$withval
+		AC_MSG_NOTICE([Setting GIT_UC_PROGRAM[]_PATH to $withval])
+		GIT_CONF_SUBST(GIT_UC_PROGRAM[]_PATH, [$withval])
+	fi
+    fi
+    m4_popdef([GIT_UC_PROGRAM])])
+
 # GIT_PARSE_WITH(PACKAGE)
 # -----------------------
 # For use in AC_ARG_WITH action-if-found, for packages default ON.
@@ -56,36 +69,55 @@
 # * Set PACKAGEDIR=PATH for --with-PACKAGE=PATH
 # * Unset NO_PACKAGE for --with-PACKAGE without ARG
 AC_DEFUN([GIT_PARSE_WITH],
-[PACKAGE=m4_toupper($1); \
-if test "$withval" = "no"; then \
-	m4_toupper(NO_$1)=YesPlease; \
-elif test "$withval" = "yes"; then \
-	m4_toupper(NO_$1)=; \
-else \
-	m4_toupper(NO_$1)=; \
-	m4_toupper($1)DIR=$withval; \
-	AC_MSG_NOTICE([Setting m4_toupper($1)DIR to $withval]); \
-	GIT_CONF_APPEND_LINE(${PACKAGE}DIR=$withval); \
-fi \
-])# GIT_PARSE_WITH
+    [m4_pushdef([GIT_UC_PACKAGE], m4_toupper([$1]))dnl
+    if test "$withval" = "no"; then
+	NO_[]GIT_UC_PACKAGE=YesPlease
+    elif test "$withval" = "yes"; then
+	NO_[]GIT_UC_PACKAGE=
+    else
+	NO_[]GIT_UC_PACKAGE=
+	GIT_UC_PACKAGE[]DIR=$withval
+	AC_MSG_NOTICE([Setting GIT_UC_PACKAGE[]DIR to $withval])
+	GIT_CONF_SUBST(GIT_UC_PACKAGE[DIR], [$withval])
+    fi
+    m4_popdef([GIT_UC_PACKAGE])])
 
-dnl
-dnl GIT_CHECK_FUNC(FUNCTION, IFTRUE, IFFALSE)
-dnl -----------------------------------------
-dnl Similar to AC_CHECK_FUNC, but on systems that do not generate
-dnl warnings for missing prototypes (e.g. FreeBSD when compiling without
-dnl -Wall), it does not work.  By looking for function definition in
-dnl libraries, this problem can be worked around.
+# GIT_PARSE_WITH_SET_MAKE_VAR(WITHNAME, VAR, HELP_TEXT)
+# -----------------------------------------------------
+# Set VAR to the value specied by --with-WITHNAME.
+# No verification of arguments is performed, but warnings are issued
+# if either 'yes' or 'no' is specified.
+# HELP_TEXT is presented when --help is called.
+# This is a direct way to allow setting variables in the Makefile.
+AC_DEFUN([GIT_PARSE_WITH_SET_MAKE_VAR],
+[AC_ARG_WITH([$1],
+ [AS_HELP_STRING([--with-$1=VALUE], $3)],
+ if test -n "$withval"; then
+  if test "$withval" = "yes" -o "$withval" = "no"; then
+    AC_MSG_WARN([You likely do not want either 'yes' or 'no' as]
+		     [a value for $1 ($2).  Maybe you do...?])
+  fi
+  AC_MSG_NOTICE([Setting $2 to $withval])
+  GIT_CONF_SUBST([$2], [$withval])
+ fi)])# GIT_PARSE_WITH_SET_MAKE_VAR
+
+#
+# GIT_CHECK_FUNC(FUNCTION, IFTRUE, IFFALSE)
+# -----------------------------------------
+# Similar to AC_CHECK_FUNC, but on systems that do not generate
+# warnings for missing prototypes (e.g. FreeBSD when compiling without
+# -Wall), it does not work.  By looking for function definition in
+# libraries, this problem can be worked around.
 AC_DEFUN([GIT_CHECK_FUNC],[AC_CHECK_FUNC([$1],[
   AC_SEARCH_LIBS([$1],,
   [$2],[$3])
 ],[$3])])
 
-dnl
-dnl GIT_STASH_FLAGS(BASEPATH_VAR)
-dnl -----------------------------
-dnl Allow for easy stashing of LDFLAGS and CPPFLAGS before running
-dnl tests that may want to take user settings into account.
+#
+# GIT_STASH_FLAGS(BASEPATH_VAR)
+# -----------------------------
+# Allow for easy stashing of LDFLAGS and CPPFLAGS before running
+# tests that may want to take user settings into account.
 AC_DEFUN([GIT_STASH_FLAGS],[
 if test -n "$1"; then
    old_CPPFLAGS="$CPPFLAGS"
@@ -106,6 +138,35 @@
 fi
 ])
 
+## Configure body starts here.
+
+AC_PREREQ(2.59)
+AC_INIT([git], [@@GIT_VERSION@@], [git@vger.kernel.org])
+
+AC_CONFIG_SRCDIR([git.c])
+
+config_file=config.mak.autogen
+config_in=config.mak.in
+
+GIT_CONF_SUBST([AUTOCONFIGURED], [YesPlease])
+
+# Directories holding "saner" versions of common or POSIX binaries.
+AC_ARG_WITH([sane-tool-path],
+  [AS_HELP_STRING(
+    [--with-sane-tool-path=DIR-1[[:DIR-2...:DIR-n]]],
+    [Directories to prepend to PATH in build system and generated scripts])],
+  [if test "$withval" = "no"; then
+    withval=''
+  else
+    AC_MSG_NOTICE([Setting SANE_TOOL_PATH to '$withval'])
+  fi
+  GIT_CONF_SUBST([SANE_TOOL_PATH], [$withval])],
+  [# If the "--with-sane-tool-path" option was not given, don't touch
+   # SANE_TOOL_PATH here, but let defaults in Makefile take care of it.
+   # This should minimize spurious differences in the behaviour of the
+   # Git build system when configure is used w.r.t. when it is not.
+   :])
+
 ## Site configuration related to programs (before tests)
 ## --with-PACKAGE[=ARG] and --without-PACKAGE
 #
@@ -113,14 +174,13 @@
 AC_ARG_WITH([lib],
  [AS_HELP_STRING([--with-lib=ARG],
                  [ARG specifies alternative name for lib directory])],
- [if test "$withval" = "no" || test "$withval" = "yes"; then \
-	AC_MSG_WARN([You should provide name for --with-lib=ARG]); \
-else \
-	lib=$withval; \
-	AC_MSG_NOTICE([Setting lib to '$lib']); \
-	GIT_CONF_APPEND_LINE(lib=$withval); \
-fi; \
-],[])
+ [if test "$withval" = "no" || test "$withval" = "yes"; then
+	AC_MSG_WARN([You should provide name for --with-lib=ARG])
+  else
+	lib=$withval
+	AC_MSG_NOTICE([Setting lib to '$lib'])
+	GIT_CONF_SUBST([lib], [$withval])
+  fi])
 
 if test -z "$lib"; then
    AC_MSG_NOTICE([Setting lib to 'lib' (the default)])
@@ -148,6 +208,26 @@
    AC_MSG_NOTICE([Will try -pthread then -lpthread to enable POSIX Threads.])
 ])
 
+# Define option to enable JavaScript minification
+AC_ARG_ENABLE([jsmin],
+[AS_HELP_STRING([--enable-jsmin=PATH],
+  [PATH is the name of a JavaScript minifier or the absolute path to one.])],
+[
+  JSMIN=$enableval;
+  AC_MSG_NOTICE([Setting JSMIN to '$JSMIN' to enable JavaScript minifying])
+  GIT_CONF_SUBST([JSMIN], [$enableval])
+])
+
+# Define option to enable CSS minification
+AC_ARG_ENABLE([cssmin],
+[AS_HELP_STRING([--enable-cssmin=PATH],
+  [PATH is the name of a CSS minifier or the absolute path to one.])],
+[
+  CSSMIN=$enableval;
+  AC_MSG_NOTICE([Setting CSSMIN to '$CSSMIN' to enable CSS minifying])
+  GIT_CONF_SUBST([CSSMIN], [$enableval])
+])
+
 ## Site configuration (override autodetection)
 ## --with-PACKAGE[=ARG] and --without-PACKAGE
 AC_MSG_NOTICE([CHECKS for site configuration])
@@ -166,8 +246,28 @@
 # /foo/bar/include and /foo/bar/lib directories.
 AC_ARG_WITH(openssl,
 AS_HELP_STRING([--with-openssl],[use OpenSSL library (default is YES)])
-AS_HELP_STRING([],              [ARG can be prefix for openssl library and headers]),\
-GIT_PARSE_WITH(openssl))
+AS_HELP_STRING([],              [ARG can be prefix for openssl library and headers]),
+GIT_PARSE_WITH([openssl]))
+
+# Define USE_LIBPCRE if you have and want to use libpcre. git-grep will be
+# able to use Perl-compatible regular expressions.
+#
+# Define LIBPCREDIR=/foo/bar if your libpcre header and library files are in
+# /foo/bar/include and /foo/bar/lib directories.
+#
+AC_ARG_WITH(libpcre,
+AS_HELP_STRING([--with-libpcre],[support Perl-compatible regexes (default is NO)])
+AS_HELP_STRING([],           [ARG can be also prefix for libpcre library and headers]),
+    if test "$withval" = "no"; then
+	USE_LIBPCRE=
+    elif test "$withval" = "yes"; then
+	USE_LIBPCRE=YesPlease
+    else
+	USE_LIBPCRE=YesPlease
+	LIBPCREDIR=$withval
+	AC_MSG_NOTICE([Setting LIBPCREDIR to $withval])
+	GIT_CONF_SUBST([LIBPCREDIR], [$withval])
+    fi)
 #
 # 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://
@@ -227,12 +327,46 @@
 # change being considered an inode change from the update-index perspective.
 
 #
+# Allow user to set ETC_GITCONFIG variable
+GIT_PARSE_WITH_SET_MAKE_VAR(gitconfig, ETC_GITCONFIG,
+			Use VALUE instead of /etc/gitconfig as the
+			global git configuration file.
+			If VALUE is not fully qualified it will be interpreted
+			as a path relative to the computed prefix at runtime.)
+
+#
+# Allow user to set ETC_GITATTRIBUTES variable
+GIT_PARSE_WITH_SET_MAKE_VAR(gitattributes, ETC_GITATTRIBUTES,
+			Use VALUE instead of /etc/gitattributes as the
+			global git attributes file.
+			If VALUE is not fully qualified it will be interpreted
+			as a path relative to the computed prefix at runtime.)
+
+#
+# Allow user to set the default pager
+GIT_PARSE_WITH_SET_MAKE_VAR(pager, DEFAULT_PAGER,
+			Use VALUE as the fall-back pager instead of 'less'.
+			This is used by things like 'git log' when the user
+			does not specify a pager to use through alternate
+			methods. eg: /usr/bin/pager)
+#
+# Allow user to set the default editor
+GIT_PARSE_WITH_SET_MAKE_VAR(editor, DEFAULT_EDITOR,
+			Use VALUE as the fall-back editor instead of 'vi'.
+			This is used by things like 'git commit' when the user
+			does not specify a preferred editor through other
+			methods. eg: /usr/bin/editor)
+
+#
 # Define SHELL_PATH to provide path to shell.
 GIT_ARG_SET_PATH(shell)
 #
 # Define PERL_PATH to provide path to Perl.
 GIT_ARG_SET_PATH(perl)
 #
+# Define PYTHON_PATH to provide path to Python.
+GIT_ARG_SET_PATH(python, allow-without)
+#
 # Define ZLIB_PATH to provide path to zlib.
 GIT_ARG_SET_PATH(zlib)
 #
@@ -241,7 +375,7 @@
 AS_HELP_STRING([--with-tcltk],[use Tcl/Tk GUI (default is YES)])
 AS_HELP_STRING([],[ARG is the full path to the Tcl/Tk interpreter.])
 AS_HELP_STRING([],[Bare --with-tcltk will make the GUI part only if])
-AS_HELP_STRING([],[Tcl/Tk interpreter will be found in a system.]),\
+AS_HELP_STRING([],[Tcl/Tk interpreter will be found in a system.]),
 GIT_PARSE_WITH(tcltk))
 #
 
@@ -250,11 +384,17 @@
 AC_MSG_NOTICE([CHECKS for programs])
 #
 AC_PROG_CC([cc gcc])
+AC_C_INLINE
+case $ac_cv_c_inline in
+  inline | yes | no)	;;
+  *)			AC_SUBST([INLINE], [$ac_cv_c_inline]) ;;
+esac
+
 # which switch to pass runtime path to dynamic libraries to the linker
 AC_CACHE_CHECK([if linker supports -R], git_cv_ld_dashr, [
    SAVE_LDFLAGS="${LDFLAGS}"
    LDFLAGS="${SAVE_LDFLAGS} -R /"
-   AC_LINK_IFELSE(AC_LANG_PROGRAM([], []), [git_cv_ld_dashr=yes], [git_cv_ld_dashr=no])
+   AC_LINK_IFELSE([AC_LANG_PROGRAM([], [])], [git_cv_ld_dashr=yes], [git_cv_ld_dashr=no])
    LDFLAGS="${SAVE_LDFLAGS}"
 ])
 if test "$git_cv_ld_dashr" = "yes"; then
@@ -263,7 +403,7 @@
    AC_CACHE_CHECK([if linker supports -Wl,-rpath,], git_cv_ld_wl_rpath, [
       SAVE_LDFLAGS="${LDFLAGS}"
       LDFLAGS="${SAVE_LDFLAGS} -Wl,-rpath,/"
-      AC_LINK_IFELSE(AC_LANG_PROGRAM([], []), [git_cv_ld_wl_rpath=yes], [git_cv_ld_wl_rpath=no])
+      AC_LINK_IFELSE([AC_LANG_PROGRAM([], [])], [git_cv_ld_wl_rpath=yes], [git_cv_ld_wl_rpath=no])
       LDFLAGS="${SAVE_LDFLAGS}"
    ])
    if test "$git_cv_ld_wl_rpath" = "yes"; then
@@ -272,7 +412,7 @@
       AC_CACHE_CHECK([if linker supports -rpath], git_cv_ld_rpath, [
          SAVE_LDFLAGS="${LDFLAGS}"
          LDFLAGS="${SAVE_LDFLAGS} -rpath /"
-         AC_LINK_IFELSE(AC_LANG_PROGRAM([], []), [git_cv_ld_rpath=yes], [git_cv_ld_rpath=no])
+         AC_LINK_IFELSE([AC_LANG_PROGRAM([], [])], [git_cv_ld_rpath=yes], [git_cv_ld_rpath=no])
          LDFLAGS="${SAVE_LDFLAGS}"
       ])
       if test "$git_cv_ld_rpath" = "yes"; then
@@ -285,6 +425,7 @@
 #AC_PROG_INSTALL		# needs install-sh or install.sh in sources
 AC_CHECK_TOOLS(AR, [gar ar], :)
 AC_CHECK_PROGS(TAR, [gtar tar])
+AC_CHECK_PROGS(DIFF, [gnudiff gdiff diff])
 # TCLTK_PATH will be set to some value if we want Tcl/Tk
 # or will be empty otherwise.
 if test -z "$NO_TCLTK"; then
@@ -307,20 +448,13 @@
 	asciidoc_version=`$ASCIIDOC --version 2>/dev/null`
 	case "${asciidoc_version}" in
 	asciidoc' '8*)
-		ASCIIDOC8=YesPlease
-		AC_MSG_RESULT([${asciidoc_version} > 7])
-		;;
-	asciidoc' '7*)
-		ASCIIDOC8=
 		AC_MSG_RESULT([${asciidoc_version}])
 		;;
 	*)
-		ASCIIDOC8=
 		AC_MSG_RESULT([${asciidoc_version} (unknown)])
 		;;
 	esac
 fi
-AC_SUBST(ASCIIDOC8)
 
 
 ## Checks for libraries.
@@ -343,6 +477,25 @@
 AC_SUBST(NO_OPENSSL)
 
 #
+# Define USE_LIBPCRE if you have and want to use libpcre. git-grep will be
+# able to use Perl-compatible regular expressions.
+#
+
+if test -n "$USE_LIBPCRE"; then
+
+GIT_STASH_FLAGS($LIBPCREDIR)
+
+AC_CHECK_LIB([pcre], [pcre_version],
+[USE_LIBPCRE=YesPlease],
+[USE_LIBPCRE=])
+
+GIT_UNSTASH_FLAGS($LIBPCREDIR)
+
+AC_SUBST(USE_LIBPCRE)
+
+fi
+
+#
 # Define NO_CURL if you do not have libcurl installed.  git-http-pull and
 # git-http-push are not built, and you cannot use http:// and https://
 # transports.
@@ -380,15 +533,9 @@
 
 GIT_STASH_FLAGS($ICONVDIR)
 
-AC_DEFUN([ICONVTEST_SRC], [
-#include <iconv.h>
-
-int main(void)
-{
-	iconv_open("", "");
-	return 0;
-}
-])
+AC_DEFUN([ICONVTEST_SRC],
+[AC_LANG_PROGRAM([#include <iconv.h>],
+ [iconv_open("", "");])])
 
 if test -n "$ICONVDIR"; then
    lib_order="-liconv -lc"
@@ -408,7 +555,7 @@
     old_LIBS="$LIBS"
     LIBS="$LIBS $l"
     AC_MSG_CHECKING([for iconv in $l])
-    AC_LINK_IFELSE(ICONVTEST_SRC,
+    AC_LINK_IFELSE([ICONVTEST_SRC],
 	[AC_MSG_RESULT([yes])
 	NO_ICONV=
 	break],
@@ -436,18 +583,12 @@
 GIT_STASH_FLAGS($ZLIB_PATH)
 
 AC_DEFUN([ZLIBTEST_SRC], [
-#include <zlib.h>
-
-int main(void)
-{
-	deflateBound(0, 0);
-	return 0;
-}
-])
+AC_LANG_PROGRAM([#include <zlib.h>],
+ [deflateBound(0, 0);])])
 AC_MSG_CHECKING([for deflateBound in -lz])
 old_LIBS="$LIBS"
 LIBS="$LIBS -lz"
-AC_LINK_IFELSE(ZLIBTEST_SRC,
+AC_LINK_IFELSE([ZLIBTEST_SRC],
 	[AC_MSG_RESULT([yes])],
 	[AC_MSG_RESULT([no])
 	NO_DEFLATE_BOUND=yes])
@@ -467,13 +608,47 @@
 test -n "$NEEDS_SOCKET" && LIBS="$LIBS -lsocket"
 
 #
-# Define NEEDS_RESOLV if linking with -lnsl and/or -lsocket is not enough.
-# Notably on Solaris hstrerror resides in libresolv and on Solaris 7
-# inet_ntop and inet_pton additionally reside there.
-AC_CHECK_LIB([c], [hstrerror],
-[NEEDS_RESOLV=],
-[NEEDS_RESOLV=YesPlease])
+# The next few tests will define NEEDS_RESOLV if linking with
+# libresolv provides some of the functions we would normally get
+# from libc.
+NEEDS_RESOLV=
 AC_SUBST(NEEDS_RESOLV)
+#
+# Define NO_INET_NTOP if linking with -lresolv is not enough.
+# Solaris 2.7 in particular hos inet_ntop in -lresolv.
+NO_INET_NTOP=
+AC_SUBST(NO_INET_NTOP)
+AC_CHECK_FUNC([inet_ntop],
+	[],
+    [AC_CHECK_LIB([resolv], [inet_ntop],
+	    [NEEDS_RESOLV=YesPlease],
+	[NO_INET_NTOP=YesPlease])
+])
+#
+# Define NO_INET_PTON if linking with -lresolv is not enough.
+# Solaris 2.7 in particular hos inet_pton in -lresolv.
+NO_INET_PTON=
+AC_SUBST(NO_INET_PTON)
+AC_CHECK_FUNC([inet_pton],
+	[],
+    [AC_CHECK_LIB([resolv], [inet_pton],
+	    [NEEDS_RESOLV=YesPlease],
+	[NO_INET_PTON=YesPlease])
+])
+#
+# Define NO_HSTRERROR if linking with -lresolv is not enough.
+# Solaris 2.6 in particular has no hstrerror, even in -lresolv.
+NO_HSTRERROR=
+AC_CHECK_FUNC([hstrerror],
+	[],
+    [AC_CHECK_LIB([resolv], [hstrerror],
+	    [NEEDS_RESOLV=YesPlease],
+	[NO_HSTRERROR=YesPlease])
+])
+AC_SUBST(NO_HSTRERROR)
+#
+# If any of the above tests determined that -lresolv is needed at
+# build-time, also set it here for remaining configure-time checks.
 test -n "$NEEDS_RESOLV" && LIBS="$LIBS -lresolv"
 
 AC_CHECK_LIB([c], [basename],
@@ -482,6 +657,23 @@
 AC_SUBST(NEEDS_LIBGEN)
 test -n "$NEEDS_LIBGEN" && LIBS="$LIBS -lgen"
 
+AC_CHECK_LIB([c], [gettext],
+[LIBC_CONTAINS_LIBINTL=YesPlease],
+[LIBC_CONTAINS_LIBINTL=])
+AC_SUBST(LIBC_CONTAINS_LIBINTL)
+
+#
+# Define NO_GETTEXT if you don't want Git output to be translated.
+# A translated Git requires GNU libintl or another gettext implementation
+AC_CHECK_HEADER([libintl.h],
+[NO_GETTEXT=],
+[NO_GETTEXT=YesPlease])
+AC_SUBST(NO_GETTEXT)
+
+if test -z "$NO_GETTEXT"; then
+    test -n "$LIBC_CONTAINS_LIBINTL" || LIBS="$LIBS -lintl"
+fi
+
 ## Checks for header files.
 AC_MSG_NOTICE([CHECKS for header files])
 #
@@ -491,25 +683,33 @@
 [NO_SYS_SELECT_H=UnfortunatelyYes])
 AC_SUBST(NO_SYS_SELECT_H)
 #
+# Define NO_SYS_POLL_H if you don't have sys/poll.h
+AC_CHECK_HEADER([sys/poll.h],
+[NO_SYS_POLL_H=],
+[NO_SYS_POLL_H=UnfortunatelyYes])
+AC_SUBST(NO_SYS_POLL_H)
+#
+# Define NO_INTTYPES_H if you don't have inttypes.h
+AC_CHECK_HEADER([inttypes.h],
+[NO_INTTYPES_H=],
+[NO_INTTYPES_H=UnfortunatelyYes])
+AC_SUBST(NO_INTTYPES_H)
+#
 # Define OLD_ICONV if your library has an old iconv(), where the second
 # (input buffer pointer) parameter is declared with type (const char **).
-AC_DEFUN([OLDICONVTEST_SRC], [[
+AC_DEFUN([OLDICONVTEST_SRC], [
+AC_LANG_PROGRAM([[
 #include <iconv.h>
 
 extern size_t iconv(iconv_t cd,
 		    char **inbuf, size_t *inbytesleft,
 		    char **outbuf, size_t *outbytesleft);
-
-int main(void)
-{
-	return 0;
-}
-]])
+]], [])])
 
 GIT_STASH_FLAGS($ICONVDIR)
 
 AC_MSG_CHECKING([for old iconv()])
-AC_COMPILE_IFELSE(OLDICONVTEST_SRC,
+AC_COMPILE_IFELSE([OLDICONVTEST_SRC],
 	[AC_MSG_RESULT([no])],
 	[AC_MSG_RESULT([yes])
 	OLD_ICONV=UnfortunatelyYes])
@@ -521,6 +721,12 @@
 ## Checks for typedefs, structures, and compiler characteristics.
 AC_MSG_NOTICE([CHECKS for typedefs, structures, and compiler characteristics])
 #
+TYPE_SOCKLEN_T
+case $ac_cv_type_socklen_t in
+  yes)	;;
+  *)  	AC_SUBST([SOCKLEN_T], [$git_cv_socklen_t_equiv]) ;;
+esac
+
 # Define NO_D_INO_IN_DIRENT if you don't have d_ino in your struct dirent.
 AC_CHECK_MEMBER(struct dirent.d_ino,
 [NO_D_INO_IN_DIRENT=],
@@ -558,29 +764,26 @@
 ])
 AC_SUBST(NO_IPV6)
 #
-# 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.
-AC_CACHE_CHECK([whether formatted IO functions support C99 size specifiers],
- [ac_cv_c_c99_format],
-[# Actually git uses only %z (%zu) in alloc.c, and %t (%td) in mktag.c
-AC_RUN_IFELSE(
-	[AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT],
-		[[char buf[64];
-		if (sprintf(buf, "%lld%hhd%jd%zd%td", (long long int)1, (char)2, (intmax_t)3, (size_t)4, (ptrdiff_t)5) != 5)
-		  return 1;
-		else if (strcmp(buf, "12345"))
-		  return 2;]])],
-	[ac_cv_c_c99_format=yes],
-	[ac_cv_c_c99_format=no])
+# Define NO_REGEX if you have no or inferior regex support in your C library.
+AC_CACHE_CHECK([whether the platform regex can handle null bytes],
+ [ac_cv_c_excellent_regex], [
+AC_EGREP_CPP(yippeeyeswehaveit,
+	AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT
+#include <regex.h>
+],
+[#ifdef REG_STARTEND
+yippeeyeswehaveit
+#endif
+]),
+	[ac_cv_c_excellent_regex=yes],
+	[ac_cv_c_excellent_regex=no])
 ])
-if test $ac_cv_c_c99_format = no; then
-	NO_C99_FORMAT=YesPlease
+if test $ac_cv_c_excellent_regex = yes; then
+	NO_REGEX=
 else
-	NO_C99_FORMAT=
+	NO_REGEX=YesPlease
 fi
-AC_SUBST(NO_C99_FORMAT)
+AC_SUBST(NO_REGEX)
 #
 # Define FREAD_READS_DIRECTORIES if your are on a system which succeeds
 # when attempting to read from an fopen'ed directory.
@@ -647,12 +850,68 @@
 [NO_LIBGEN_H=YesPlease])
 AC_SUBST(NO_LIBGEN_H)
 #
+# Define HAVE_PATHS_H if you have paths.h.
+AC_CHECK_HEADER([paths.h],
+[HAVE_PATHS_H=YesPlease],
+[HAVE_PATHS_H=])
+AC_SUBST(HAVE_PATHS_H)
+#
+# Define HAVE_LIBCHARSET_H if have libcharset.h
+AC_CHECK_HEADER([libcharset.h],
+[HAVE_LIBCHARSET_H=YesPlease],
+[HAVE_LIBCHARSET_H=])
+AC_SUBST(HAVE_LIBCHARSET_H)
+# Define CHARSET_LIB if libiconv does not export the locale_charset symbol
+# and libcharset does
+CHARSET_LIB=
+AC_CHECK_LIB([iconv], [locale_charset],
+       [],
+       [AC_CHECK_LIB([charset], [locale_charset],
+                     [CHARSET_LIB=-lcharset])
+       ]
+)
+AC_SUBST(CHARSET_LIB)
+#
 # Define NO_STRCASESTR if you don't have strcasestr.
 GIT_CHECK_FUNC(strcasestr,
 [NO_STRCASESTR=],
 [NO_STRCASESTR=YesPlease])
 AC_SUBST(NO_STRCASESTR)
 #
+# Define NO_STRTOK_R if you don't have strtok_r
+GIT_CHECK_FUNC(strtok_r,
+[NO_STRTOK_R=],
+[NO_STRTOK_R=YesPlease])
+AC_SUBST(NO_STRTOK_R)
+#
+# Define NO_FNMATCH if you don't have fnmatch
+GIT_CHECK_FUNC(fnmatch,
+[NO_FNMATCH=],
+[NO_FNMATCH=YesPlease])
+AC_SUBST(NO_FNMATCH)
+#
+# Define NO_FNMATCH_CASEFOLD if your fnmatch function doesn't have the
+# FNM_CASEFOLD GNU extension.
+AC_CACHE_CHECK([whether the fnmatch function supports the FNMATCH_CASEFOLD GNU extension],
+ [ac_cv_c_excellent_fnmatch], [
+AC_EGREP_CPP(yippeeyeswehaveit,
+	AC_LANG_PROGRAM([
+#include <fnmatch.h>
+],
+[#ifdef FNM_CASEFOLD
+yippeeyeswehaveit
+#endif
+]),
+	[ac_cv_c_excellent_fnmatch=yes],
+	[ac_cv_c_excellent_fnmatch=no])
+])
+if test $ac_cv_c_excellent_fnmatch = yes; then
+	NO_FNMATCH_CASEFOLD=
+else
+	NO_FNMATCH_CASEFOLD=YesPlease
+fi
+AC_SUBST(NO_FNMATCH_CASEFOLD)
+#
 # Define NO_MEMMEM if you don't have memmem.
 GIT_CHECK_FUNC(memmem,
 [NO_MEMMEM=],
@@ -703,6 +962,12 @@
 [NO_MKSTEMPS=YesPlease])
 AC_SUBST(NO_MKSTEMPS)
 #
+# Define NO_INITGROUPS if you don't have initgroups in the C library.
+GIT_CHECK_FUNC(initgroups,
+[NO_INITGROUPS=],
+[NO_INITGROUPS=YesPlease])
+AC_SUBST(NO_INITGROUPS)
+#
 #
 # Define NO_MMAP if you want to avoid mmap.
 #
@@ -716,19 +981,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_PTHREADS if we do not have pthreads
+# Define NO_PTHREADS if we do not have pthreads.
 #
-# Define PTHREAD_LIBS to the linker flag used for Pthread support and define
-# THREADED_DELTA_SEARCH if Pthreads are available.
+# Define PTHREAD_LIBS to the linker flag used for Pthread support.
 AC_DEFUN([PTHREADTEST_SRC], [
+AC_LANG_PROGRAM([[
 #include <pthread.h>
-
-int main(void)
-{
+]], [[
 	pthread_mutex_t test_mutex;
-	return (0);
-}
-])
+	pthread_key_t test_key;
+	int retcode = 0;
+	retcode |= pthread_key_create(&test_key, (void *)0);
+	retcode |= pthread_mutex_init(&test_mutex,(void *)0);
+	retcode |= pthread_mutex_lock(&test_mutex);
+	retcode |= pthread_mutex_unlock(&test_mutex);
+	return retcode;
+]])])
 
 dnl AC_LANG_CONFTEST([AC_LANG_PROGRAM(
 dnl   [[#include <pthread.h>]],
@@ -736,7 +1004,6 @@
 dnl )])
 
 NO_PTHREADS=UnfortunatelyYes
-THREADED_DELTA_SEARCH=
 PTHREAD_LIBS=
 
 if test -n "$USER_NOPTHREAD"; then
@@ -744,29 +1011,35 @@
 # handle these separately since PTHREAD_CFLAGS could be '-lpthreads
 # -D_REENTRANT' or some such.
 elif test -z "$PTHREAD_CFLAGS"; then
-  for opt in -pthread -lpthread; do
+  threads_found=no
+  for opt in -mt -pthread -lpthread; do
      old_CFLAGS="$CFLAGS"
      CFLAGS="$opt $CFLAGS"
      AC_MSG_CHECKING([Checking for POSIX Threads with '$opt'])
-     AC_LINK_IFELSE(PTHREADTEST_SRC,
+     AC_LINK_IFELSE([PTHREADTEST_SRC],
 	[AC_MSG_RESULT([yes])
 		NO_PTHREADS=
 		PTHREAD_LIBS="$opt"
-		THREADED_DELTA_SEARCH=YesPlease
+		PTHREAD_CFLAGS="$opt"
+		threads_found=yes
 		break
 	],
 	[AC_MSG_RESULT([no])])
       CFLAGS="$old_CFLAGS"
   done
+  if test $threads_found != yes; then
+    AC_CHECK_LIB([pthread], [pthread_create],
+	[PTHREAD_LIBS="-lpthread"],
+	[NO_PTHREADS=UnfortunatelyYes])
+  fi
 else
   old_CFLAGS="$CFLAGS"
   CFLAGS="$PTHREAD_CFLAGS $CFLAGS"
   AC_MSG_CHECKING([Checking for POSIX Threads with '$PTHREAD_CFLAGS'])
-  AC_LINK_IFELSE(PTHREADTEST_SRC,
+  AC_LINK_IFELSE([PTHREADTEST_SRC],
 	[AC_MSG_RESULT([yes])
 		NO_PTHREADS=
 		PTHREAD_LIBS="$PTHREAD_CFLAGS"
-		THREADED_DELTA_SEARCH=YesPlease
 	],
 	[AC_MSG_RESULT([no])])
 
@@ -775,14 +1048,10 @@
 
 CFLAGS="$old_CFLAGS"
 
+AC_SUBST(PTHREAD_CFLAGS)
 AC_SUBST(PTHREAD_LIBS)
 AC_SUBST(NO_PTHREADS)
-AC_SUBST(THREADED_DELTA_SEARCH)
 
 ## Output files
-AC_CONFIG_FILES(["${config_file}":"${config_in}":"${config_append}"])
+AC_CONFIG_FILES(["${config_file}":"${config_in}"])
 AC_OUTPUT
-
-
-## Cleanup
-rm -f "${config_append}"
diff --git a/connect.c b/connect.c
index 76e5427..55a85ad 100644
--- a/connect.c
+++ b/connect.c
@@ -5,6 +5,7 @@
 #include "refs.h"
 #include "run-command.h"
 #include "remote.h"
+#include "url.h"
 
 static char *server_capabilities;
 
@@ -21,7 +22,7 @@
 	len -= 5;
 
 	/* REF_NORMAL means that we don't want the magic fake tag refs */
-	if ((flags & REF_NORMAL) && check_ref_format(name) < 0)
+	if ((flags & REF_NORMAL) && check_refname_format(name, 0))
 		return 0;
 
 	/* REF_HEADS means that we want regular branch heads */
@@ -48,14 +49,25 @@
 	extra->nr++;
 }
 
+static void die_initial_contact(int got_at_least_one_head)
+{
+	if (got_at_least_one_head)
+		die("The remote end hung up upon initial contact");
+	else
+		die("Could not read from remote repository.\n\n"
+		    "Please make sure you have the correct access rights\n"
+		    "and the repository exists.");
+}
+
 /*
  * Read all the refs from the other end
  */
 struct ref **get_remote_heads(int in, struct ref **list,
-			      int nr_match, char **match,
 			      unsigned int flags,
 			      struct extra_have_objects *extra_have)
 {
+	int got_at_least_one_head = 0;
+
 	*list = NULL;
 	for (;;) {
 		struct ref *ref;
@@ -64,7 +76,10 @@
 		char *name;
 		int len, name_len;
 
-		len = packet_read_line(in, buffer, sizeof(buffer));
+		len = packet_read(in, buffer, sizeof(buffer));
+		if (len < 0)
+			die_initial_contact(got_at_least_one_head);
+
 		if (!len)
 			break;
 		if (buffer[len-1] == '\n')
@@ -91,68 +106,44 @@
 
 		if (!check_ref(name, name_len, flags))
 			continue;
-		if (nr_match && !path_match(name, nr_match, match))
-			continue;
 		ref = alloc_ref(buffer + 41);
 		hashcpy(ref->old_sha1, old_sha1);
 		*list = ref;
 		list = &ref->next;
+		got_at_least_one_head = 1;
 	}
 	return list;
 }
 
 int server_supports(const char *feature)
 {
-	return server_capabilities &&
-		strstr(server_capabilities, feature) != NULL;
+	return !!parse_feature_request(server_capabilities, feature);
 }
 
-int get_ack(int fd, unsigned char *result_sha1)
+const char *parse_feature_request(const char *feature_list, const char *feature)
 {
-	static char line[1000];
-	int len = packet_read_line(fd, line, sizeof(line));
+	int len;
 
-	if (!len)
-		die("git fetch-pack: expected ACK/NAK, got EOF");
-	if (line[len-1] == '\n')
-		line[--len] = 0;
-	if (!strcmp(line, "NAK"))
-		return 0;
-	if (!prefixcmp(line, "ACK ")) {
-		if (!get_sha1_hex(line+4, result_sha1)) {
-			if (strstr(line+45, "continue"))
-				return 2;
-			return 1;
-		}
+	if (!feature_list)
+		return NULL;
+
+	len = strlen(feature);
+	while (*feature_list) {
+		const char *found = strstr(feature_list, feature);
+		if (!found)
+			return NULL;
+		if ((feature_list == found || isspace(found[-1])) &&
+		    (!found[len] || isspace(found[len]) || found[len] == '='))
+			return found;
+		feature_list = found + 1;
 	}
-	die("git fetch_pack: expected ACK/NAK, got '%s'", line);
-}
-
-int path_match(const char *path, int nr, char **match)
-{
-	int i;
-	int pathlen = strlen(path);
-
-	for (i = 0; i < nr; i++) {
-		char *s = match[i];
-		int len = strlen(s);
-
-		if (!len || len > pathlen)
-			continue;
-		if (memcmp(path + pathlen - len, s, len))
-			continue;
-		if (pathlen > len && path[pathlen - len - 1] != '/')
-			continue;
-		*s = 0;
-		return (i + 1);
-	}
-	return 0;
+	return NULL;
 }
 
 enum protocol {
 	PROTO_LOCAL = 1,
 	PROTO_SSH,
-	PROTO_GIT,
+	PROTO_GIT
 };
 
 static enum protocol get_protocol(const char *name)
@@ -173,6 +164,37 @@
 #define STR_(s)	# s
 #define STR(s)	STR_(s)
 
+static void get_host_and_port(char **host, const char **port)
+{
+	char *colon, *end;
+
+	if (*host[0] == '[') {
+		end = strchr(*host + 1, ']');
+		if (end) {
+			*end = 0;
+			end++;
+			(*host)++;
+		} else
+			end = *host;
+	} else
+		end = *host;
+	colon = strchr(end, ':');
+
+	if (colon) {
+		*colon = 0;
+		*port = colon + 1;
+	}
+}
+
+static void enable_keepalive(int sockfd)
+{
+	int ka = 1;
+
+	if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &ka, sizeof(ka)) < 0)
+		fprintf(stderr, "unable to set SO_KEEPALIVE on socket: %s\n",
+			strerror(errno));
+}
+
 #ifndef NO_IPV6
 
 static const char *ai_name(const struct addrinfo *ai)
@@ -190,31 +212,16 @@
  */
 static int git_tcp_connect_sock(char *host, int flags)
 {
-	int sockfd = -1, saved_errno = 0;
-	char *colon, *end;
+	struct strbuf error_message = STRBUF_INIT;
+	int sockfd = -1;
 	const char *port = STR(DEFAULT_GIT_PORT);
 	struct addrinfo hints, *ai0, *ai;
 	int gai;
 	int cnt = 0;
 
-	if (host[0] == '[') {
-		end = strchr(host + 1, ']');
-		if (end) {
-			*end = 0;
-			end++;
-			host++;
-		} else
-			end = host;
-	} else
-		end = host;
-	colon = strchr(end, ':');
-
-	if (colon) {
-		*colon = 0;
-		port = colon + 1;
-		if (!*port)
-			port = "<none>";
-	}
+	get_host_and_port(&host, &port);
+	if (!*port)
+		port = "<none>";
 
 	memset(&hints, 0, sizeof(hints));
 	hints.ai_socktype = SOCK_STREAM;
@@ -230,21 +237,15 @@
 	if (flags & CONNECT_VERBOSE)
 		fprintf(stderr, "done.\nConnecting to %s (port %s) ... ", host, port);
 
-	for (ai0 = ai; ai; ai = ai->ai_next) {
+	for (ai0 = ai; ai; ai = ai->ai_next, cnt++) {
 		sockfd = socket(ai->ai_family,
 				ai->ai_socktype, ai->ai_protocol);
-		if (sockfd < 0) {
-			saved_errno = errno;
-			continue;
-		}
-		if (connect(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {
-			saved_errno = errno;
-			fprintf(stderr, "%s[%d: %s]: errno=%s\n",
-				host,
-				cnt,
-				ai_name(ai),
-				strerror(saved_errno));
-			close(sockfd);
+		if ((sockfd < 0) ||
+		    (connect(sockfd, ai->ai_addr, ai->ai_addrlen) < 0)) {
+			strbuf_addf(&error_message, "%s[%d: %s]: errno=%s\n",
+				    host, cnt, ai_name(ai), strerror(errno));
+			if (0 <= sockfd)
+				close(sockfd);
 			sockfd = -1;
 			continue;
 		}
@@ -256,11 +257,15 @@
 	freeaddrinfo(ai0);
 
 	if (sockfd < 0)
-		die("unable to connect a socket (%s)", strerror(saved_errno));
+		die("unable to connect to %s:\n%s", host, error_message.buf);
+
+	enable_keepalive(sockfd);
 
 	if (flags & CONNECT_VERBOSE)
 		fprintf(stderr, "done.\n");
 
+	strbuf_release(&error_message);
+
 	return sockfd;
 }
 
@@ -271,31 +276,17 @@
  */
 static int git_tcp_connect_sock(char *host, int flags)
 {
-	int sockfd = -1, saved_errno = 0;
-	char *colon, *end;
-	char *port = STR(DEFAULT_GIT_PORT), *ep;
+	struct strbuf error_message = STRBUF_INIT;
+	int sockfd = -1;
+	const char *port = STR(DEFAULT_GIT_PORT);
+	char *ep;
 	struct hostent *he;
 	struct sockaddr_in sa;
 	char **ap;
 	unsigned int nport;
 	int cnt;
 
-	if (host[0] == '[') {
-		end = strchr(host + 1, ']');
-		if (end) {
-			*end = 0;
-			end++;
-			host++;
-		} else
-			end = host;
-	} else
-		end = host;
-	colon = strchr(end, ':');
-
-	if (colon) {
-		*colon = 0;
-		port = colon + 1;
-	}
+	get_host_and_port(&host, &port);
 
 	if (flags & CONNECT_VERBOSE)
 		fprintf(stderr, "Looking up %s ... ", host);
@@ -316,25 +307,21 @@
 		fprintf(stderr, "done.\nConnecting to %s (port %s) ... ", host, port);
 
 	for (cnt = 0, ap = he->h_addr_list; *ap; ap++, cnt++) {
-		sockfd = socket(he->h_addrtype, SOCK_STREAM, 0);
-		if (sockfd < 0) {
-			saved_errno = errno;
-			continue;
-		}
-
 		memset(&sa, 0, sizeof sa);
 		sa.sin_family = he->h_addrtype;
 		sa.sin_port = htons(nport);
 		memcpy(&sa.sin_addr, *ap, he->h_length);
 
-		if (connect(sockfd, (struct sockaddr *)&sa, sizeof sa) < 0) {
-			saved_errno = errno;
-			fprintf(stderr, "%s[%d: %s]: errno=%s\n",
+		sockfd = socket(he->h_addrtype, SOCK_STREAM, 0);
+		if ((sockfd < 0) ||
+		    connect(sockfd, (struct sockaddr *)&sa, sizeof sa) < 0) {
+			strbuf_addf(&error_message, "%s[%d: %s]: errno=%s\n",
 				host,
 				cnt,
 				inet_ntoa(*(struct in_addr *)&sa.sin_addr),
-				strerror(saved_errno));
-			close(sockfd);
+				strerror(errno));
+			if (0 <= sockfd)
+				close(sockfd);
 			sockfd = -1;
 			continue;
 		}
@@ -345,7 +332,9 @@
 	}
 
 	if (sockfd < 0)
-		die("unable to connect a socket (%s)", strerror(saved_errno));
+		die("unable to connect to %s:\n%s", host, error_message.buf);
+
+	enable_keepalive(sockfd);
 
 	if (flags & CONNECT_VERBOSE)
 		fprintf(stderr, "done.\n");
@@ -424,42 +413,28 @@
 	return (git_proxy_command && *git_proxy_command);
 }
 
-static void git_proxy_connect(int fd[2], char *host)
+static struct child_process *git_proxy_connect(int fd[2], char *host)
 {
 	const char *port = STR(DEFAULT_GIT_PORT);
-	char *colon, *end;
-	const char *argv[4];
-	struct child_process proxy;
+	const char **argv;
+	struct child_process *proxy;
 
-	if (host[0] == '[') {
-		end = strchr(host + 1, ']');
-		if (end) {
-			*end = 0;
-			end++;
-			host++;
-		} else
-			end = host;
-	} else
-		end = host;
-	colon = strchr(end, ':');
+	get_host_and_port(&host, &port);
 
-	if (colon) {
-		*colon = 0;
-		port = colon + 1;
-	}
-
+	argv = xmalloc(sizeof(*argv) * 4);
 	argv[0] = git_proxy_command;
 	argv[1] = host;
 	argv[2] = port;
 	argv[3] = NULL;
-	memset(&proxy, 0, sizeof(proxy));
-	proxy.argv = argv;
-	proxy.in = -1;
-	proxy.out = -1;
-	if (start_command(&proxy))
+	proxy = xcalloc(1, sizeof(*proxy));
+	proxy->argv = argv;
+	proxy->in = -1;
+	proxy->out = -1;
+	if (start_command(proxy))
 		die("cannot start proxy %s", argv[0]);
-	fd[0] = proxy.out; /* read from proxy stdout */
-	fd[1] = proxy.in;  /* write to proxy stdin */
+	fd[0] = proxy->out; /* read from proxy stdout */
+	fd[1] = proxy->in;  /* write to proxy stdin */
+	return proxy;
 }
 
 #define MAX_CMD_LEN 1024
@@ -496,11 +471,11 @@
 struct child_process *git_connect(int fd[2], const char *url_orig,
 				  const char *prog, int flags)
 {
-	char *url = xstrdup(url_orig);
+	char *url;
 	char *host, *path;
 	char *end;
 	int c;
-	struct child_process *conn;
+	struct child_process *conn = &no_fork;
 	enum protocol protocol = PROTO_LOCAL;
 	int free_path = 0;
 	char *port = NULL;
@@ -512,8 +487,13 @@
 	 */
 	signal(SIGCHLD, SIG_DFL);
 
+	if (is_url(url_orig))
+		url = url_decode(url_orig);
+	else
+		url = xstrdup(url_orig);
+
 	host = strstr(url, "://");
-	if(host) {
+	if (host) {
 		*host = '\0';
 		protocol = get_protocol(url);
 		host += 3;
@@ -523,12 +503,18 @@
 		c = ':';
 	}
 
+	/*
+	 * Don't do destructive transforms with git:// as that
+	 * protocol code does '[]' unwrapping of its own.
+	 */
 	if (host[0] == '[') {
 		end = strchr(host + 1, ']');
 		if (end) {
-			*end = 0;
+			if (protocol != PROTO_GIT) {
+				*end = 0;
+				host++;
+			}
 			end++;
-			host++;
 		} else
 			end = host;
 	} else
@@ -566,7 +552,7 @@
 	 * Add support for ssh port: ssh://host.xy:<port>/...
 	 */
 	if (protocol == PROTO_SSH && host != url)
-		port = get_port(host);
+		port = get_port(end);
 
 	if (protocol == PROTO_GIT) {
 		/* These underlying connection commands die() if they
@@ -574,7 +560,7 @@
 		 */
 		char *target_host = xstrdup(host);
 		if (git_use_proxy(host))
-			git_proxy_connect(fd, host);
+			conn = git_proxy_connect(fd, host);
 		else
 			git_tcp_connect(fd, host, flags);
 		/*
@@ -592,7 +578,7 @@
 		free(url);
 		if (free_path)
 			free(path);
-		return &no_fork;
+		return conn;
 	}
 
 	conn = xcalloc(1, sizeof(*conn));
@@ -622,19 +608,9 @@
 		*arg++ = host;
 	}
 	else {
-		/* remove these from the environment */
-		const char *env[] = {
-			ALTERNATE_DB_ENVIRONMENT,
-			DB_ENVIRONMENT,
-			GIT_DIR_ENVIRONMENT,
-			GIT_WORK_TREE_ENVIRONMENT,
-			GRAFT_ENVIRONMENT,
-			INDEX_ENVIRONMENT,
-			NULL
-		};
-		conn->env = env;
-		*arg++ = "sh";
-		*arg++ = "-c";
+		/* remove repo-local variables from the environment */
+		conn->env = local_repo_env;
+		conn->use_shell = 1;
 	}
 	*arg++ = cmd.buf;
 	*arg = NULL;
@@ -651,10 +627,15 @@
 	return conn;
 }
 
+int git_connection_is_socket(struct child_process *conn)
+{
+	return conn == &no_fork;
+}
+
 int finish_connect(struct child_process *conn)
 {
 	int code;
-	if (!conn || conn == &no_fork)
+	if (!conn || git_connection_is_socket(conn))
 		return 0;
 
 	code = finish_command(conn);
diff --git a/connected.c b/connected.c
new file mode 100644
index 0000000..1e89c1c
--- /dev/null
+++ b/connected.c
@@ -0,0 +1,62 @@
+#include "cache.h"
+#include "run-command.h"
+#include "sigchain.h"
+#include "connected.h"
+
+/*
+ * If we feed all the commits we want to verify to this command
+ *
+ *  $ git rev-list --objects --stdin --not --all
+ *
+ * and if it does not error out, that means everything reachable from
+ * these commits locally exists and is connected to our existing refs.
+ * Note that this does _not_ validate the individual objects.
+ *
+ * Returns 0 if everything is connected, non-zero otherwise.
+ */
+int check_everything_connected(sha1_iterate_fn fn, int quiet, void *cb_data)
+{
+	struct child_process rev_list;
+	const char *argv[] = {"rev-list", "--objects",
+			      "--stdin", "--not", "--all", NULL, NULL};
+	char commit[41];
+	unsigned char sha1[20];
+	int err = 0;
+
+	if (fn(cb_data, sha1))
+		return err;
+
+	if (quiet)
+		argv[5] = "--quiet";
+
+	memset(&rev_list, 0, sizeof(rev_list));
+	rev_list.argv = argv;
+	rev_list.git_cmd = 1;
+	rev_list.in = -1;
+	rev_list.no_stdout = 1;
+	rev_list.no_stderr = quiet;
+	if (start_command(&rev_list))
+		return error(_("Could not run 'git rev-list'"));
+
+	sigchain_push(SIGPIPE, SIG_IGN);
+
+	commit[40] = '\n';
+	do {
+		memcpy(commit, sha1_to_hex(sha1), 40);
+		if (write_in_full(rev_list.in, commit, 41) < 0) {
+			if (errno != EPIPE && errno != EINVAL)
+				error(_("failed write to rev-list: %s"),
+				      strerror(errno));
+			err = -1;
+			break;
+		}
+	} while (!fn(cb_data, sha1));
+
+	if (close(rev_list.in)) {
+		error(_("failed to close rev-list's stdin: %s"), strerror(errno));
+		err = -1;
+	}
+
+	sigchain_pop(SIGPIPE);
+	return finish_command(&rev_list) || err;
+}
diff --git a/connected.h b/connected.h
new file mode 100644
index 0000000..7e4585a
--- /dev/null
+++ b/connected.h
@@ -0,0 +1,20 @@
+#ifndef CONNECTED_H
+#define CONNECTED_H
+
+/*
+ * Take callback data, and return next object name in the buffer.
+ * When called after returning the name for the last object, return -1
+ * to signal EOF, otherwise return 0.
+ */
+typedef int (*sha1_iterate_fn)(void *, unsigned char [20]);
+
+/*
+ * Make sure that our object store has all the commits necessary to
+ * connect the ancestry chain to some of our existing refs, and all
+ * the trees and blobs that these commits use.
+ *
+ * Return 0 if Ok, non zero otherwise (i.e. some missing objects)
+ */
+extern int check_everything_connected(sha1_iterate_fn, int quiet, void *cb_data);
+
+#endif /* CONNECTED_H */
diff --git a/contrib/buildsystems/Generators.pm b/contrib/buildsystems/Generators.pm
new file mode 100644
index 0000000..408ef71
--- /dev/null
+++ b/contrib/buildsystems/Generators.pm
@@ -0,0 +1,42 @@
+package Generators;
+require Exporter;
+
+use strict;
+use File::Basename;
+no strict 'refs';
+use vars qw($VERSION @AVAILABLE);
+
+our $VERSION = '1.00';
+our(@ISA, @EXPORT, @EXPORT_OK, @AVAILABLE);
+@ISA = qw(Exporter);
+
+BEGIN {
+    local(*D);
+    my $me = $INC{"Generators.pm"};
+    die "Couldn't find myself in \@INC, which is required to load the generators!" if ("$me" eq "");
+    $me = dirname($me);
+    if (opendir(D,"$me/Generators")) {
+        foreach my $gen (readdir(D)) {
+            next if ($gen  =~ /^\.\.?$/);
+            require "${me}/Generators/$gen";
+            $gen =~ s,\.pm,,;
+            push(@AVAILABLE, $gen);
+        }
+        closedir(D);
+        my $gens = join(', ', @AVAILABLE);
+    }
+
+    push @EXPORT_OK, qw(available);
+}
+
+sub available {
+    return @AVAILABLE;
+}
+
+sub generate {
+    my ($gen, $git_dir, $out_dir, $rel_dir, %build_structure) = @_;
+    return eval("Generators::${gen}::generate(\$git_dir, \$out_dir, \$rel_dir, \%build_structure)") if grep(/^$gen$/, @AVAILABLE);
+    die "Generator \"${gen}\" is not available!\nAvailable generators are: @AVAILABLE\n";
+}
+
+1;
diff --git a/contrib/buildsystems/Generators/QMake.pm b/contrib/buildsystems/Generators/QMake.pm
new file mode 100644
index 0000000..ff3b657
--- /dev/null
+++ b/contrib/buildsystems/Generators/QMake.pm
@@ -0,0 +1,189 @@
+package Generators::QMake;
+require Exporter;
+
+use strict;
+use vars qw($VERSION);
+
+our $VERSION = '1.00';
+our(@ISA, @EXPORT, @EXPORT_OK, @AVAILABLE);
+@ISA = qw(Exporter);
+
+BEGIN {
+    push @EXPORT_OK, qw(generate);
+}
+
+sub generate {
+    my ($git_dir, $out_dir, $rel_dir, %build_structure) = @_;
+
+    my @libs = @{$build_structure{"LIBS"}};
+    foreach (@libs) {
+        createLibProject($_, $git_dir, $out_dir, $rel_dir, %build_structure);
+    }
+
+    my @apps = @{$build_structure{"APPS"}};
+    foreach (@apps) {
+        createAppProject($_, $git_dir, $out_dir, $rel_dir, %build_structure);
+    }
+
+    createGlueProject($git_dir, $out_dir, $rel_dir, %build_structure);
+    return 0;
+}
+
+sub createLibProject {
+    my ($libname, $git_dir, $out_dir, $rel_dir, %build_structure) = @_;
+    print "Generate $libname lib project\n";
+    $rel_dir = "../$rel_dir";
+
+    my $sources = join(" \\\n\t", sort(map("$rel_dir/$_", @{$build_structure{"LIBS_${libname}_SOURCES"}})));
+    my $defines = join(" \\\n\t", sort(@{$build_structure{"LIBS_${libname}_DEFINES"}}));
+    my $includes= join(" \\\n\t", sort(map("$rel_dir/$_", @{$build_structure{"LIBS_${libname}_INCLUDES"}})));
+    my $cflags  = join(" ", sort(@{$build_structure{"LIBS_${libname}_CFLAGS"}}));
+
+    my $cflags_debug = $cflags;
+    $cflags_debug =~ s/-MT/-MTd/;
+    $cflags_debug =~ s/-O.//;
+
+    my $cflags_release = $cflags;
+    $cflags_release =~ s/-MTd/-MT/;
+
+    my @tmp  = @{$build_structure{"LIBS_${libname}_LFLAGS"}};
+    my @tmp2 = ();
+    foreach (@tmp) {
+        if (/^-LTCG/) {
+        } elsif (/^-L/) {
+            $_ =~ s/^-L/-LIBPATH:$rel_dir\//;
+        }
+        push(@tmp2, $_);
+    }
+    my $lflags = join(" ", sort(@tmp));
+
+    my $target = $libname;
+    $target =~ s/\//_/g;
+    $defines =~ s/-D//g;
+    $defines =~ s/"/\\\\"/g;
+    $includes =~ s/-I//g;
+    mkdir "$target" || die "Could not create the directory $target for lib project!\n";
+    open F, ">$target/$target.pro" || die "Could not open $target/$target.pro for writing!\n";
+    print F << "EOM";
+TEMPLATE = lib
+TARGET = $target
+DESTDIR = $rel_dir
+
+CONFIG -= qt
+CONFIG += static
+
+QMAKE_CFLAGS =
+QMAKE_CFLAGS_RELEASE = $cflags_release
+QMAKE_CFLAGS_DEBUG = $cflags_debug
+QMAKE_LIBFLAGS = $lflags
+
+DEFINES += \\
+        $defines
+
+INCLUDEPATH += \\
+        $includes
+
+SOURCES += \\
+        $sources
+EOM
+    close F;
+}
+
+sub createAppProject {
+    my ($appname, $git_dir, $out_dir, $rel_dir, %build_structure) = @_;
+    print "Generate $appname app project\n";
+    $rel_dir = "../$rel_dir";
+
+    my $sources = join(" \\\n\t", sort(map("$rel_dir/$_", @{$build_structure{"APPS_${appname}_SOURCES"}})));
+    my $defines = join(" \\\n\t", sort(@{$build_structure{"APPS_${appname}_DEFINES"}}));
+    my $includes= join(" \\\n\t", sort(map("$rel_dir/$_", @{$build_structure{"APPS_${appname}_INCLUDES"}})));
+    my $cflags  = join(" ", sort(@{$build_structure{"APPS_${appname}_CFLAGS"}}));
+
+    my $cflags_debug = $cflags;
+    $cflags_debug =~ s/-MT/-MTd/;
+    $cflags_debug =~ s/-O.//;
+
+    my $cflags_release = $cflags;
+    $cflags_release =~ s/-MTd/-MT/;
+
+    my $libs;
+    foreach (sort(@{$build_structure{"APPS_${appname}_LIBS"}})) {
+        $_ =~ s/\//_/g;
+        $libs .= " $_";
+    }
+    my @tmp  = @{$build_structure{"APPS_${appname}_LFLAGS"}};
+    my @tmp2 = ();
+    foreach (@tmp) {
+        # next if ($_ eq "-NODEFAULTLIB:MSVCRT.lib");
+        if (/^-LTCG/) {
+        } elsif (/^-L/) {
+            $_ =~ s/^-L/-LIBPATH:$rel_dir\//;
+        }
+        push(@tmp2, $_);
+    }
+    my $lflags = join(" ", sort(@tmp));
+
+    my $target = $appname;
+    $target =~ s/\.exe//;
+    $target =~ s/\//_/g;
+    $defines =~ s/-D//g;
+    $defines =~ s/"/\\\\"/g;
+    $includes =~ s/-I//g;
+    mkdir "$target" || die "Could not create the directory $target for app project!\n";
+    open F, ">$target/$target.pro" || die "Could not open $target/$target.pro for writing!\n";
+    print F << "EOM";
+TEMPLATE = app
+TARGET = $target
+DESTDIR = $rel_dir
+
+CONFIG -= qt embed_manifest_exe
+CONFIG += console
+
+QMAKE_CFLAGS =
+QMAKE_CFLAGS_RELEASE = $cflags_release
+QMAKE_CFLAGS_DEBUG = $cflags_debug
+QMAKE_LFLAGS = $lflags
+LIBS   = $libs
+
+DEFINES += \\
+        $defines
+
+INCLUDEPATH += \\
+        $includes
+
+win32:QMAKE_LFLAGS += -LIBPATH:$rel_dir
+else: QMAKE_LFLAGS += -L$rel_dir
+
+SOURCES += \\
+        $sources
+EOM
+    close F;
+}
+
+sub createGlueProject {
+    my ($git_dir, $out_dir, $rel_dir, %build_structure) = @_;
+    my $libs = join(" \\ \n", map("\t$_|$_.pro", @{$build_structure{"LIBS"}}));
+    my $apps = join(" \\ \n", map("\t$_|$_.pro", @{$build_structure{"APPS"}}));
+    $libs =~ s/\.a//g;
+    $libs =~ s/\//_/g;
+    $libs =~ s/\|/\//g;
+    $apps =~ s/\.exe//g;
+    $apps =~ s/\//_/g;
+    $apps =~ s/\|/\//g;
+
+    my $filename = $out_dir;
+    $filename =~ s/.*\/([^\/]+)$/$1/;
+    $filename =~ s/\/$//;
+    print "Generate glue project $filename.pro\n";
+    open F, ">$filename.pro" || die "Could not open $filename.pro for writing!\n";
+    print F << "EOM";
+TEMPLATE = subdirs
+CONFIG += ordered
+SUBDIRS += \\
+$libs \\
+$apps
+EOM
+    close F;
+}
+
+1;
diff --git a/contrib/buildsystems/Generators/Vcproj.pm b/contrib/buildsystems/Generators/Vcproj.pm
new file mode 100644
index 0000000..cfa74ad
--- /dev/null
+++ b/contrib/buildsystems/Generators/Vcproj.pm
@@ -0,0 +1,626 @@
+package Generators::Vcproj;
+require Exporter;
+
+use strict;
+use vars qw($VERSION);
+
+our $VERSION = '1.00';
+our(@ISA, @EXPORT, @EXPORT_OK, @AVAILABLE);
+@ISA = qw(Exporter);
+
+BEGIN {
+    push @EXPORT_OK, qw(generate);
+}
+
+my $guid_index = 0;
+my @GUIDS = (
+    "{E07B9989-2BF7-4F21-8918-BE22BA467AC3}",
+    "{278FFB51-0296-4A44-A81A-22B87B7C3592}",
+    "{7346A2C4-F0FD-444F-9EBE-1AF23B2B5650}",
+    "{67F421AC-EB34-4D49-820B-3196807B423F}",
+    "{385DCFE1-CC8C-4211-A451-80FCFC31CA51}",
+    "{97CC46C5-D2CC-4D26-B634-E75792B79916}",
+    "{C7CE21FE-6EF8-4012-A5C7-A22BCEDFBA11}",
+    "{51575134-3FDF-42D1-BABD-3FB12669C6C9}",
+    "{0AE195E4-9823-4B87-8E6F-20C5614AF2FF}",
+    "{4B918255-67CA-43BB-A46C-26704B666E6B}",
+    "{18CCFEEF-C8EE-4CC1-A265-26F95C9F4649}",
+    "{5D5D90FA-01B7-4973-AFE5-CA88C53AC197}",
+    "{1F054320-036D-49E1-B384-FB5DF0BC8AC0}",
+    "{7CED65EE-F2D9-4171-825B-C7D561FE5786}",
+    "{8D341679-0F07-4664-9A56-3BA0DE88B9BC}",
+    "{C189FEDC-2957-4BD7-9FA4-7622241EA145}",
+    "{66844203-1B9F-4C53-9274-164FFF95B847}",
+    "{E4FEA145-DECC-440D-AEEA-598CF381FD43}",
+    "{73300A8E-C8AC-41B0-B555-4F596B681BA7}",
+    "{873FDEB1-D01D-40BF-A1BF-8BBC58EC0F51}",
+    "{7922C8BE-76C5-4AC6-8BF7-885C0F93B782}",
+    "{E245D370-308B-4A49-BFC1-1E527827975F}",
+    "{F6FA957B-66FC-4ED7-B260-E59BBE4FE813}",
+    "{E6055070-0198-431A-BC49-8DB6CEE770AE}",
+    "{54159234-C3EB-43DA-906B-CE5DA5C74654}",
+    "{594CFC35-0B60-46F6-B8EF-9983ACC1187D}",
+    "{D93FCAB7-1F01-48D2-B832-F761B83231A5}",
+    "{DBA5E6AC-E7BE-42D3-8703-4E787141526E}",
+    "{6171953F-DD26-44C7-A3BE-CC45F86FC11F}",
+    "{9E19DDBE-F5E4-4A26-A2FE-0616E04879B8}",
+    "{AE81A615-99E3-4885-9CE0-D9CAA193E867}",
+    "{FBF4067E-1855-4F6C-8BCD-4D62E801A04D}",
+    "{17007948-6593-4AEB-8106-F7884B4F2C19}",
+    "{199D4C8D-8639-4DA6-82EF-08668C35DEE0}",
+    "{E085E50E-C140-4CF3-BE4B-094B14F0DDD6}",
+    "{00785268-A9CC-4E40-AC29-BAC0019159CE}",
+    "{4C06F56A-DCDB-46A6-B67C-02339935CF12}",
+    "{3A62D3FD-519E-4EC9-8171-D2C1BFEA022F}",
+    "{3A62D3FD-519E-4EC9-8171-D2C1BFEA022F}",
+    "{9392EB58-D7BA-410B-B1F0-B2FAA6BC89A7}",
+    "{2ACAB2D5-E0CE-4027-BCA0-D78B2D7A6C66}",
+    "{86E216C3-43CE-481A-BCB2-BE5E62850635}",
+    "{FB631291-7923-4B91-9A57-7B18FDBB7A42}",
+    "{0A176EC9-E934-45B8-B87F-16C7F4C80039}",
+    "{DF55CA80-46E8-4C53-B65B-4990A23DD444}",
+    "{3A0F9895-55D2-4710-BE5E-AD7498B5BF44}",
+    "{294BDC5A-F448-48B6-8110-DD0A81820F8C}",
+    "{4B9F66E9-FAC9-47AB-B1EF-C16756FBFD06}",
+    "{72EA49C6-2806-48BD-B81B-D4905102E19C}",
+    "{5728EB7E-8929-486C-8CD5-3238D060E768}"
+);
+
+sub generate {
+    my ($git_dir, $out_dir, $rel_dir, %build_structure) = @_;
+    my @libs = @{$build_structure{"LIBS"}};
+    foreach (@libs) {
+        createLibProject($_, $git_dir, $out_dir, $rel_dir, \%build_structure);
+    }
+
+    my @apps = @{$build_structure{"APPS"}};
+    foreach (@apps) {
+        createAppProject($_, $git_dir, $out_dir, $rel_dir, \%build_structure);
+    }
+
+    createGlueProject($git_dir, $out_dir, $rel_dir, %build_structure);
+    return 0;
+}
+
+sub createLibProject {
+    my ($libname, $git_dir, $out_dir, $rel_dir, $build_structure) = @_;
+    print "Generate $libname vcproj lib project\n";
+    $rel_dir = "..\\$rel_dir";
+    $rel_dir =~ s/\//\\/g;
+
+    my $target = $libname;
+    $target =~ s/\//_/g;
+    $target =~ s/\.a//;
+
+    my $uuid = $GUIDS[$guid_index];
+    $$build_structure{"LIBS_${target}_GUID"} = $uuid;
+    $guid_index += 1;
+
+    my @srcs = sort(map("$rel_dir\\$_", @{$$build_structure{"LIBS_${libname}_SOURCES"}}));
+    my @sources;
+    foreach (@srcs) {
+        $_ =~ s/\//\\/g;
+        push(@sources, $_);
+    }
+    my $defines = join(",", sort(@{$$build_structure{"LIBS_${libname}_DEFINES"}}));
+    my $includes= join(";", sort(map("&quot;$rel_dir\\$_&quot;", @{$$build_structure{"LIBS_${libname}_INCLUDES"}})));
+    my $cflags  = join(" ", sort(@{$$build_structure{"LIBS_${libname}_CFLAGS"}}));
+    $cflags =~ s/\"/&quot;/g;
+
+    my $cflags_debug = $cflags;
+    $cflags_debug =~ s/-MT/-MTd/;
+    $cflags_debug =~ s/-O.//;
+
+    my $cflags_release = $cflags;
+    $cflags_release =~ s/-MTd/-MT/;
+
+    my @tmp  = @{$$build_structure{"LIBS_${libname}_LFLAGS"}};
+    my @tmp2 = ();
+    foreach (@tmp) {
+        if (/^-LTCG/) {
+        } elsif (/^-L/) {
+            $_ =~ s/^-L/-LIBPATH:$rel_dir\//;
+        }
+        push(@tmp2, $_);
+    }
+    my $lflags = join(" ", sort(@tmp));
+
+    $defines =~ s/-D//g;
+    $defines =~ s/\"/\\&quot;/g;
+    $defines =~ s/\'//g;
+    $includes =~ s/-I//g;
+    mkdir "$target" || die "Could not create the directory $target for lib project!\n";
+    open F, ">$target/$target.vcproj" || die "Could not open $target/$target.pro for writing!\n";
+    binmode F, ":crlf";
+    print F << "EOM";
+<?xml version="1.0" encoding = "Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="9,00"
+	Name="$target"
+	ProjectGUID="$uuid">
+	<Platforms>
+		<Platform
+			Name="Win32"/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="$rel_dir"
+			ConfigurationType="4"
+			CharacterSet="0"
+			IntermediateDirectory="\$(ProjectDir)\$(ConfigurationName)"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				AdditionalOptions="$cflags_debug"
+				Optimization="0"
+				InlineFunctionExpansion="1"
+				AdditionalIncludeDirectories="$includes"
+				PreprocessorDefinitions="WIN32,_DEBUG,$defines"
+				MinimalRebuild="true"
+				RuntimeLibrary="1"
+				UsePrecompiledHeader="0"
+				ProgramDataBaseFileName="\$(IntDir)\\\$(TargetName).pdb"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				SuppressStartupBanner="true"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="$rel_dir"
+			ConfigurationType="4"
+			CharacterSet="0"
+			WholeProgramOptimization="1"
+			IntermediateDirectory="\$(ProjectDir)\$(ConfigurationName)"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				AdditionalOptions="$cflags_release"
+				Optimization="2"
+				InlineFunctionExpansion="1"
+				EnableIntrinsicFunctions="true"
+				AdditionalIncludeDirectories="$includes"
+				PreprocessorDefinitions="WIN32,NDEBUG,$defines"
+				RuntimeLibrary="0"
+				EnableFunctionLevelLinking="true"
+				UsePrecompiledHeader="0"
+				ProgramDataBaseFileName="\$(IntDir)\\\$(TargetName).pdb"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				SuppressStartupBanner="true"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+	</Configurations>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">
+EOM
+    foreach(@sources) {
+        print F << "EOM";
+			<File
+				RelativePath="$_"/>
+EOM
+    }
+    print F << "EOM";
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
+EOM
+    close F;
+}
+
+sub createAppProject {
+    my ($appname, $git_dir, $out_dir, $rel_dir, $build_structure) = @_;
+    print "Generate $appname vcproj app project\n";
+    $rel_dir = "..\\$rel_dir";
+    $rel_dir =~ s/\//\\/g;
+
+    my $target = $appname;
+    $target =~ s/\//_/g;
+    $target =~ s/\.exe//;
+
+    my $uuid = $GUIDS[$guid_index];
+    $$build_structure{"APPS_${target}_GUID"} = $uuid;
+    $guid_index += 1;
+
+    my @srcs = sort(map("$rel_dir\\$_", @{$$build_structure{"APPS_${appname}_SOURCES"}}));
+    my @sources;
+    foreach (@srcs) {
+        $_ =~ s/\//\\/g;
+        push(@sources, $_);
+    }
+    my $defines = join(",", sort(@{$$build_structure{"APPS_${appname}_DEFINES"}}));
+    my $includes= join(";", sort(map("&quot;$rel_dir\\$_&quot;", @{$$build_structure{"APPS_${appname}_INCLUDES"}})));
+    my $cflags  = join(" ", sort(@{$$build_structure{"APPS_${appname}_CFLAGS"}}));
+    $cflags =~ s/\"/&quot;/g;
+
+    my $cflags_debug = $cflags;
+    $cflags_debug =~ s/-MT/-MTd/;
+    $cflags_debug =~ s/-O.//;
+
+    my $cflags_release = $cflags;
+    $cflags_release =~ s/-MTd/-MT/;
+
+    my $libs;
+    foreach (sort(@{$$build_structure{"APPS_${appname}_LIBS"}})) {
+        $_ =~ s/\//_/g;
+        $libs .= " $_";
+    }
+    my @tmp  = @{$$build_structure{"APPS_${appname}_LFLAGS"}};
+    my @tmp2 = ();
+    foreach (@tmp) {
+        if (/^-LTCG/) {
+        } elsif (/^-L/) {
+            $_ =~ s/^-L/-LIBPATH:$rel_dir\//;
+        }
+        push(@tmp2, $_);
+    }
+    my $lflags = join(" ", sort(@tmp)) . " -LIBPATH:$rel_dir";
+
+    $defines =~ s/-D//g;
+    $defines =~ s/\"/\\&quot;/g;
+    $defines =~ s/\'//g;
+    $defines =~ s/\\\\/\\/g;
+    $includes =~ s/-I//g;
+    mkdir "$target" || die "Could not create the directory $target for lib project!\n";
+    open F, ">$target/$target.vcproj" || die "Could not open $target/$target.pro for writing!\n";
+    binmode F, ":crlf";
+    print F << "EOM";
+<?xml version="1.0" encoding = "Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="9,00"
+	Name="$target"
+	ProjectGUID="$uuid">
+	<Platforms>
+		<Platform
+			Name="Win32"/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="$rel_dir"
+			ConfigurationType="1"
+			CharacterSet="0"
+			IntermediateDirectory="\$(ProjectDir)\$(ConfigurationName)"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				AdditionalOptions="$cflags_debug"
+				Optimization="0"
+				InlineFunctionExpansion="1"
+				AdditionalIncludeDirectories="$includes"
+				PreprocessorDefinitions="WIN32,_DEBUG,$defines"
+				MinimalRebuild="true"
+				RuntimeLibrary="1"
+				UsePrecompiledHeader="0"
+				ProgramDataBaseFileName="\$(IntDir)\\\$(TargetName).pdb"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="$libs"
+				AdditionalOptions="$lflags"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="$rel_dir"
+			ConfigurationType="1"
+			CharacterSet="0"
+			WholeProgramOptimization="1"
+			IntermediateDirectory="\$(ProjectDir)\$(ConfigurationName)"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				AdditionalOptions="$cflags_release"
+				Optimization="2"
+				InlineFunctionExpansion="1"
+				EnableIntrinsicFunctions="true"
+				AdditionalIncludeDirectories="$includes"
+				PreprocessorDefinitions="WIN32,NDEBUG,$defines"
+				RuntimeLibrary="0"
+				EnableFunctionLevelLinking="true"
+				UsePrecompiledHeader="0"
+				ProgramDataBaseFileName="\$(IntDir)\\\$(TargetName).pdb"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="$libs"
+				AdditionalOptions="$lflags"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+	</Configurations>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">
+EOM
+    foreach(@sources) {
+        print F << "EOM";
+			<File
+				RelativePath="$_"/>
+EOM
+    }
+    print F << "EOM";
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
+EOM
+    close F;
+}
+
+sub createGlueProject {
+    my ($git_dir, $out_dir, $rel_dir, %build_structure) = @_;
+    print "Generate solutions file\n";
+    $rel_dir = "..\\$rel_dir";
+    $rel_dir =~ s/\//\\/g;
+    my $SLN_HEAD = "Microsoft Visual Studio Solution File, Format Version 10.00\n# Visual Studio 2008\n";
+    my $SLN_PRE  = "Project(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = ";
+    my $SLN_POST = "\nEndProject\n";
+
+    my @libs = @{$build_structure{"LIBS"}};
+    my @tmp;
+    foreach (@libs) {
+        $_ =~ s/\//_/g;
+        $_ =~ s/\.a//;
+        push(@tmp, $_);
+    }
+    @libs = @tmp;
+
+    my @apps = @{$build_structure{"APPS"}};
+    @tmp = ();
+    foreach (@apps) {
+        $_ =~ s/\//_/g;
+        $_ =~ s/\.exe//;
+        push(@tmp, $_);
+    }
+    @apps = @tmp;
+
+    open F, ">git.sln" || die "Could not open git.sln for writing!\n";
+    binmode F, ":crlf";
+    print F "$SLN_HEAD";
+    foreach (@libs) {
+        my $libname = $_;
+        my $uuid = $build_structure{"LIBS_${libname}_GUID"};
+        print F "$SLN_PRE";
+        print F "\"${libname}\", \"${libname}\\${libname}.vcproj\", \"${uuid}\"";
+        print F "$SLN_POST";
+    }
+    my $uuid_libgit = $build_structure{"LIBS_libgit_GUID"};
+    my $uuid_xdiff_lib = $build_structure{"LIBS_xdiff_lib_GUID"};
+    foreach (@apps) {
+        my $appname = $_;
+        my $uuid = $build_structure{"APPS_${appname}_GUID"};
+        print F "$SLN_PRE";
+        print F "\"${appname}\", \"${appname}\\${appname}.vcproj\", \"${uuid}\"\n";
+        print F "	ProjectSection(ProjectDependencies) = postProject\n";
+        print F "		${uuid_libgit} = ${uuid_libgit}\n";
+        print F "		${uuid_xdiff_lib} = ${uuid_xdiff_lib}\n";
+        print F "	EndProjectSection";
+        print F "$SLN_POST";
+    }
+
+    print F << "EOM";
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Win32 = Debug|Win32
+		Release|Win32 = Release|Win32
+	EndGlobalSection
+EOM
+    print F << "EOM";
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+EOM
+    foreach (@libs) {
+        my $libname = $_;
+        my $uuid = $build_structure{"LIBS_${libname}_GUID"};
+        print F "\t\t${uuid}.Debug|Win32.ActiveCfg = Debug|Win32\n";
+        print F "\t\t${uuid}.Debug|Win32.Build.0 = Debug|Win32\n";
+        print F "\t\t${uuid}.Release|Win32.ActiveCfg = Release|Win32\n";
+        print F "\t\t${uuid}.Release|Win32.Build.0 = Release|Win32\n";
+    }
+    foreach (@apps) {
+        my $appname = $_;
+        my $uuid = $build_structure{"APPS_${appname}_GUID"};
+        print F "\t\t${uuid}.Debug|Win32.ActiveCfg = Debug|Win32\n";
+        print F "\t\t${uuid}.Debug|Win32.Build.0 = Debug|Win32\n";
+        print F "\t\t${uuid}.Release|Win32.ActiveCfg = Release|Win32\n";
+        print F "\t\t${uuid}.Release|Win32.Build.0 = Release|Win32\n";
+    }
+
+    print F << "EOM";
+	EndGlobalSection
+EndGlobal
+EOM
+    close F;
+}
+
+1;
diff --git a/contrib/buildsystems/engine.pl b/contrib/buildsystems/engine.pl
new file mode 100644
index 0000000..23da787
--- /dev/null
+++ b/contrib/buildsystems/engine.pl
@@ -0,0 +1,359 @@
+#!/usr/bin/perl -w
+######################################################################
+# Do not call this script directly!
+#
+# The generate script ensures that @INC is correct before the engine
+# is executed.
+#
+# Copyright (C) 2009 Marius Storm-Olsen <mstormo@gmail.com>
+######################################################################
+use strict;
+use File::Basename;
+use File::Spec;
+use Cwd;
+use Generators;
+
+my (%build_structure, %compile_options, @makedry);
+my $out_dir = getcwd();
+my $git_dir = $out_dir;
+$git_dir =~ s=\\=/=g;
+$git_dir = dirname($git_dir) while (!-e "$git_dir/git.c" && "$git_dir" ne "");
+die "Couldn't find Git repo" if ("$git_dir" eq "");
+
+my @gens = Generators::available();
+my $gen = "Vcproj";
+
+sub showUsage
+{
+    my $genlist = join(', ', @gens);
+    print << "EOM";
+generate usage:
+  -g <GENERATOR>  --gen <GENERATOR> Specify the buildsystem generator    (default: $gen)
+                                    Available: $genlist
+  -o <PATH>       --out <PATH>      Specify output directory generation  (default: .)
+  -i <FILE>       --in <FILE>       Specify input file, instead of running GNU Make
+  -h,-?           --help            This help
+EOM
+    exit 0;
+}
+
+# Parse command-line options
+while (@ARGV) {
+    my $arg = shift @ARGV;
+    if ("$arg" eq "-h" || "$arg" eq "--help" || "$arg" eq "-?") {
+	showUsage();
+	exit(0);
+    } elsif("$arg" eq "--out" || "$arg" eq "-o") {
+	$out_dir = shift @ARGV;
+    } elsif("$arg" eq "--gen" || "$arg" eq "-g") {
+	$gen = shift @ARGV;
+    } elsif("$arg" eq "--in" || "$arg" eq "-i") {
+	my $infile = shift @ARGV;
+        open(F, "<$infile") || die "Couldn't open file $infile";
+        @makedry = <F>;
+        close(F);
+    }
+}
+
+# NOT using File::Spec->rel2abs($path, $base) here, as
+# it fails badly for me in the msysgit environment
+$git_dir = File::Spec->rel2abs($git_dir);
+$out_dir = File::Spec->rel2abs($out_dir);
+my $rel_dir = makeOutRel2Git($git_dir, $out_dir);
+
+# Print some information so the user feels informed
+print << "EOM";
+-----
+Generator: $gen
+Git dir:   $git_dir
+Out dir:   $out_dir
+-----
+Running GNU Make to figure out build structure...
+EOM
+
+# Pipe a make --dry-run into a variable, if not already loaded from file
+@makedry = `cd $git_dir && make -n MSVC=1 V=1 2>/dev/null` if !@makedry;
+
+# Parse the make output into usable info
+parseMakeOutput();
+
+# Finally, ask the generator to start generating..
+Generators::generate($gen, $git_dir, $out_dir, $rel_dir, %build_structure);
+
+# main flow ends here
+# -------------------------------------------------------------------------------------------------
+
+
+# 1) path: /foo/bar/baz        2) path: /foo/bar/baz   3) path: /foo/bar/baz
+#    base: /foo/bar/baz/temp      base: /foo/bar          base: /tmp
+#    rel:  ..                     rel:  baz               rel:  ../foo/bar/baz
+sub makeOutRel2Git
+{
+    my ($path, $base) = @_;
+    my $rel;
+    if ("$path" eq "$base") {
+        return ".";
+    } elsif ($base =~ /^$path/) {
+        # case 1
+        my $tmp = $base;
+        $tmp =~ s/^$path//;
+        foreach (split('/', $tmp)) {
+            $rel .= "../" if ("$_" ne "");
+        }
+    } elsif ($path =~ /^$base/) {
+        # case 2
+        $rel = $path;
+        $rel =~ s/^$base//;
+        $rel = "./$rel";
+    } else {
+        my $tmp = $base;
+        foreach (split('/', $tmp)) {
+            $rel .= "../" if ("$_" ne "");
+        }
+        $rel .= $path;
+    }
+    $rel =~ s/\/\//\//g; # simplify
+    $rel =~ s/\/$//;     # don't end with /
+    return $rel;
+}
+
+sub parseMakeOutput
+{
+    print "Parsing GNU Make output to figure out build structure...\n";
+    my $line = 0;
+    while (my $text = shift @makedry) {
+        my $ate_next;
+        do {
+            $ate_next = 0;
+            $line++;
+            chomp $text;
+            chop $text if ($text =~ /\r$/);
+            if ($text =~ /\\$/) {
+                $text =~ s/\\$//;
+                $text .= shift @makedry;
+                $ate_next = 1;
+            }
+        } while($ate_next);
+
+        if ($text =~ /^test /) {
+            # options to test (eg -o) may be mistaken for linker options
+            next;
+        }
+
+        if($text =~ / -c /) {
+            # compilation
+            handleCompileLine($text, $line);
+
+        } elsif ($text =~ / -o /) {
+            # linking executable
+            handleLinkLine($text, $line);
+
+        } elsif ($text =~ /\.o / && $text =~ /\.a /) {
+            # libifying
+            handleLibLine($text, $line);
+#
+#        } elsif ($text =~ /^cp /) {
+#            # copy file around
+#
+#        } elsif ($text =~ /^rm -f /) {
+#            # shell command
+#
+#        } elsif ($text =~ /^make[ \[]/) {
+#            # make output
+#
+#        } elsif ($text =~ /^echo /) {
+#            # echo to file
+#
+#        } elsif ($text =~ /^if /) {
+#            # shell conditional
+#
+#        } elsif ($text =~ /^tclsh /) {
+#            # translation stuff
+#
+#        } elsif ($text =~ /^umask /) {
+#            # handling boilerplates
+#
+#        } elsif ($text =~ /\$\(\:\)/) {
+#            # ignore
+#
+#        } elsif ($text =~ /^FLAGS=/) {
+#            # flags check for dependencies
+#
+#        } elsif ($text =~ /^'\/usr\/bin\/perl' -MError -e/) {
+#            # perl commands for copying files
+#
+#        } elsif ($text =~ /generate-cmdlist\.sh/) {
+#            # command for generating list of commands
+#
+#        } elsif ($text =~ /new locations or Tcl/) {
+#            # command for detecting Tcl/Tk changes
+#
+#        } elsif ($text =~ /mkdir -p/) {
+#            # command creating path
+#
+#        } elsif ($text =~ /: no custom templates yet/) {
+#            # whatever
+#
+#        } else {
+#            print "Unhandled (line: $line): $text\n";
+        }
+    }
+
+#    use Data::Dumper;
+#    print "Parsed build structure:\n";
+#    print Dumper(%build_structure);
+}
+
+# variables for the compilation part of each step
+my (@defines, @incpaths, @cflags, @sources);
+
+sub clearCompileStep
+{
+    @defines = ();
+    @incpaths = ();
+    @cflags = ();
+    @sources = ();
+}
+
+sub removeDuplicates
+{
+    my (%dupHash, $entry);
+    %dupHash = map { $_, 1 } @defines;
+    @defines = keys %dupHash;
+
+    %dupHash = map { $_, 1 } @incpaths;
+    @incpaths = keys %dupHash;
+
+    %dupHash = map { $_, 1 } @cflags;
+    @cflags = keys %dupHash;
+}
+
+sub handleCompileLine
+{
+    my ($line, $lineno) = @_;
+    my @parts = split(' ', $line);
+    my $sourcefile;
+    shift(@parts); # ignore cmd
+    while (my $part = shift @parts) {
+        if ("$part" eq "-o") {
+            # ignore object file
+            shift @parts;
+        } elsif ("$part" eq "-c") {
+            # ignore compile flag
+        } elsif ("$part" eq "-c") {
+        } elsif ($part =~ /^.?-I/) {
+            push(@incpaths, $part);
+        } elsif ($part =~ /^.?-D/) {
+            push(@defines, $part);
+        } elsif ($part =~ /^-/) {
+            push(@cflags, $part);
+        } elsif ($part =~ /\.(c|cc|cpp)$/) {
+            $sourcefile = $part;
+        } else {
+            die "Unhandled compiler option @ line $lineno: $part";
+        }
+    }
+    @{$compile_options{"${sourcefile}_CFLAGS"}} = @cflags;
+    @{$compile_options{"${sourcefile}_DEFINES"}} = @defines;
+    @{$compile_options{"${sourcefile}_INCPATHS"}} = @incpaths;
+    clearCompileStep();
+}
+
+sub handleLibLine
+{
+    my ($line, $lineno) = @_;
+    my (@objfiles, @lflags, $libout, $part);
+    # kill cmd and rm 'prefix'
+    $line =~ s/^rm -f .* && .* rcs //;
+    my @parts = split(' ', $line);
+    while ($part = shift @parts) {
+        if ($part =~ /^-/) {
+            push(@lflags, $part);
+        } elsif ($part =~ /\.(o|obj)$/) {
+            push(@objfiles, $part);
+        } elsif ($part =~ /\.(a|lib)$/) {
+            $libout = $part;
+            $libout =~ s/\.a$//;
+        } else {
+            die "Unhandled lib option @ line $lineno: $part";
+        }
+    }
+#    print "LibOut: '$libout'\nLFlags: @lflags\nOfiles: @objfiles\n";
+#    exit(1);
+    foreach (@objfiles) {
+        my $sourcefile = $_;
+        $sourcefile =~ s/\.o/.c/;
+        push(@sources, $sourcefile);
+        push(@cflags, @{$compile_options{"${sourcefile}_CFLAGS"}});
+        push(@defines, @{$compile_options{"${sourcefile}_DEFINES"}});
+        push(@incpaths, @{$compile_options{"${sourcefile}_INCPATHS"}});
+    }
+    removeDuplicates();
+
+    push(@{$build_structure{"LIBS"}}, $libout);
+    @{$build_structure{"LIBS_${libout}"}} = ("_DEFINES", "_INCLUDES", "_CFLAGS", "_SOURCES",
+                                             "_OBJECTS");
+    @{$build_structure{"LIBS_${libout}_DEFINES"}} = @defines;
+    @{$build_structure{"LIBS_${libout}_INCLUDES"}} = @incpaths;
+    @{$build_structure{"LIBS_${libout}_CFLAGS"}} = @cflags;
+    @{$build_structure{"LIBS_${libout}_LFLAGS"}} = @lflags;
+    @{$build_structure{"LIBS_${libout}_SOURCES"}} = @sources;
+    @{$build_structure{"LIBS_${libout}_OBJECTS"}} = @objfiles;
+    clearCompileStep();
+}
+
+sub handleLinkLine
+{
+    my ($line, $lineno) = @_;
+    my (@objfiles, @lflags, @libs, $appout, $part);
+    my @parts = split(' ', $line);
+    shift(@parts); # ignore cmd
+    while ($part = shift @parts) {
+        if ($part =~ /^-IGNORE/) {
+            push(@lflags, $part);
+        } elsif ($part =~ /^-[GRIMDO]/) {
+            # eat compiler flags
+        } elsif ("$part" eq "-o") {
+            $appout = shift @parts;
+        } elsif ("$part" eq "-lz") {
+            push(@libs, "zlib.lib");
+	} elsif ("$part" eq "-lcrypto") {
+            push(@libs, "libeay32.lib");
+        } elsif ("$part" eq "-lssl") {
+            push(@libs, "ssleay32.lib");
+        } elsif ($part =~ /^-/) {
+            push(@lflags, $part);
+        } elsif ($part =~ /\.(a|lib)$/) {
+            $part =~ s/\.a$/.lib/;
+            push(@libs, $part);
+        } elsif ($part =~ /\.(o|obj)$/) {
+            push(@objfiles, $part);
+        } else {
+            die "Unhandled lib option @ line $lineno: $part";
+        }
+    }
+#    print "AppOut: '$appout'\nLFlags: @lflags\nLibs  : @libs\nOfiles: @objfiles\n";
+#    exit(1);
+    foreach (@objfiles) {
+        my $sourcefile = $_;
+        $sourcefile =~ s/\.o/.c/;
+        push(@sources, $sourcefile);
+        push(@cflags, @{$compile_options{"${sourcefile}_CFLAGS"}});
+        push(@defines, @{$compile_options{"${sourcefile}_DEFINES"}});
+        push(@incpaths, @{$compile_options{"${sourcefile}_INCPATHS"}});
+    }
+    removeDuplicates();
+
+    removeDuplicates();
+    push(@{$build_structure{"APPS"}}, $appout);
+    @{$build_structure{"APPS_${appout}"}} = ("_DEFINES", "_INCLUDES", "_CFLAGS", "_LFLAGS",
+                                             "_SOURCES", "_OBJECTS", "_LIBS");
+    @{$build_structure{"APPS_${appout}_DEFINES"}} = @defines;
+    @{$build_structure{"APPS_${appout}_INCLUDES"}} = @incpaths;
+    @{$build_structure{"APPS_${appout}_CFLAGS"}} = @cflags;
+    @{$build_structure{"APPS_${appout}_LFLAGS"}} = @lflags;
+    @{$build_structure{"APPS_${appout}_SOURCES"}} = @sources;
+    @{$build_structure{"APPS_${appout}_OBJECTS"}} = @objfiles;
+    @{$build_structure{"APPS_${appout}_LIBS"}} = @libs;
+    clearCompileStep();
+}
diff --git a/contrib/buildsystems/generate b/contrib/buildsystems/generate
new file mode 100644
index 0000000..bc10f25
--- /dev/null
+++ b/contrib/buildsystems/generate
@@ -0,0 +1,29 @@
+#!/usr/bin/perl -w
+######################################################################
+# Generate buildsystem files
+#
+# This script generate buildsystem files based on the output of a
+# GNU Make --dry-run, enabling Windows users to develop Git with their
+# trusted IDE with native projects.
+#
+# Note:
+# It is not meant as *the* way of building Git with MSVC, but merely a
+# convenience. The correct way of building Git with MSVC is to use the
+# GNU Make tool to build with the maintained Makefile in the root of
+# the project. If you have the msysgit environment installed and
+# available in your current console, together with the Visual Studio
+# environment you wish to build for, all you have to do is run the
+# command:
+#     make MSVC=1
+#
+# Copyright (C) 2009 Marius Storm-Olsen <mstormo@gmail.com>
+######################################################################
+use strict;
+use File::Basename;
+use Cwd;
+
+my $git_dir = getcwd();
+$git_dir =~ s=\\=/=g;
+$git_dir = dirname($git_dir) while (!-e "$git_dir/git.c" && "$git_dir" ne "");
+die "Couldn't find Git repo" if ("$git_dir" eq "");
+exec join(" ", ("PERL5LIB=${git_dir}/contrib/buildsystems ${git_dir}/contrib/buildsystems/engine.pl", @ARGV));
diff --git a/contrib/buildsystems/parse.pl b/contrib/buildsystems/parse.pl
new file mode 100644
index 0000000..c9656ec
--- /dev/null
+++ b/contrib/buildsystems/parse.pl
@@ -0,0 +1,228 @@
+#!/usr/bin/perl -w
+######################################################################
+# Do not call this script directly!
+#
+# The generate script ensures that @INC is correct before the engine
+# is executed.
+#
+# Copyright (C) 2009 Marius Storm-Olsen <mstormo@gmail.com>
+######################################################################
+use strict;
+use File::Basename;
+use Cwd;
+
+my $file = $ARGV[0];
+die "No file provided!" if !defined $file;
+
+my ($cflags, $target, $type, $line);
+
+open(F, "<$file") || die "Couldn't open file $file";
+my @data = <F>;
+close(F);
+
+while (my $text = shift @data) {
+    my $ate_next;
+    do {
+        $ate_next = 0;
+        $line++;
+        chomp $text;
+        chop $text if ($text =~ /\r$/);
+        if ($text =~ /\\$/) {
+            $text =~ s/\\$//;
+            $text .= shift @data;
+            $ate_next = 1;
+        }
+    } while($ate_next);
+
+    if($text =~ / -c /) {
+        # compilation
+        handleCompileLine($text, $line);
+
+    } elsif ($text =~ / -o /) {
+        # linking executable
+        handleLinkLine($text, $line);
+
+    } elsif ($text =~ /\.o / && $text =~ /\.a /) {
+        # libifying
+        handleLibLine($text, $line);
+
+#    } elsif ($text =~ /^cp /) {
+#        # copy file around
+#
+#    } elsif ($text =~ /^rm -f /) {
+#        # shell command
+#
+#    } elsif ($text =~ /^make[ \[]/) {
+#        # make output
+#
+#    } elsif ($text =~ /^echo /) {
+#        # echo to file
+#
+#    } elsif ($text =~ /^if /) {
+#        # shell conditional
+#
+#    } elsif ($text =~ /^tclsh /) {
+#        # translation stuff
+#
+#    } elsif ($text =~ /^umask /) {
+#        # handling boilerplates
+#
+#    } elsif ($text =~ /\$\(\:\)/) {
+#        # ignore
+#
+#    } elsif ($text =~ /^FLAGS=/) {
+#        # flags check for dependencies
+#
+#    } elsif ($text =~ /^'\/usr\/bin\/perl' -MError -e/) {
+#        # perl commands for copying files
+#
+#    } elsif ($text =~ /generate-cmdlist\.sh/) {
+#        # command for generating list of commands
+#
+#    } elsif ($text =~ /^test / && $text =~ /|| rm -f /) {
+#        # commands removing executables, if they exist
+#
+#    } elsif ($text =~ /new locations or Tcl/) {
+#        # command for detecting Tcl/Tk changes
+#
+#    } elsif ($text =~ /mkdir -p/) {
+#        # command creating path
+#
+#    } elsif ($text =~ /: no custom templates yet/) {
+#        # whatever
+
+    } else {
+#        print "Unhandled (line: $line): $text\n";
+    }
+}
+close(F);
+
+# use Data::Dumper;
+# print "Parsed build structure:\n";
+# print Dumper(%build_structure);
+
+# -------------------------------------------------------------------
+# Functions under here
+# -------------------------------------------------------------------
+my (%build_structure, @defines, @incpaths, @cflags, @sources);
+
+sub clearCompileStep
+{
+    @defines = ();
+    @incpaths = ();
+    @cflags = ();
+    @sources = ();
+}
+
+sub removeDuplicates
+{
+    my (%dupHash, $entry);
+    %dupHash = map { $_, 1 } @defines;
+    @defines = keys %dupHash;
+
+    %dupHash = map { $_, 1 } @incpaths;
+    @incpaths = keys %dupHash;
+
+    %dupHash = map { $_, 1 } @cflags;
+    @cflags = keys %dupHash;
+
+    %dupHash = map { $_, 1 } @sources;
+    @sources = keys %dupHash;
+}
+
+sub handleCompileLine
+{
+    my ($line, $lineno) = @_;
+    my @parts = split(' ', $line);
+    shift(@parts); # ignore cmd
+    while (my $part = shift @parts) {
+        if ("$part" eq "-o") {
+            # ignore object file
+            shift @parts;
+        } elsif ("$part" eq "-c") {
+            # ignore compile flag
+        } elsif ("$part" eq "-c") {
+        } elsif ($part =~ /^.?-I/) {
+            push(@incpaths, $part);
+        } elsif ($part =~ /^.?-D/) {
+            push(@defines, $part);
+        } elsif ($part =~ /^-/) {
+            push(@cflags, $part);
+        } elsif ($part =~ /\.(c|cc|cpp)$/) {
+            push(@sources, $part);
+        } else {
+            die "Unhandled compiler option @ line $lineno: $part";
+        }
+    }
+    #print "Sources: @sources\nCFlags: @cflags\nDefine: @defines\nIncpat: @incpaths\n";
+    #exit(1);
+}
+
+sub handleLibLine
+{
+    my ($line, $lineno) = @_;
+    my (@objfiles, @lflags, $libout, $part);
+    # kill cmd and rm 'prefix'
+    $line =~ s/^rm -f .* && .* rcs //;
+    my @parts = split(' ', $line);
+    while ($part = shift @parts) {
+        if ($part =~ /^-/) {
+            push(@lflags, $part);
+        } elsif ($part =~ /\.(o|obj)$/) {
+            push(@objfiles, $part);
+        } elsif ($part =~ /\.(a|lib)$/) {
+            $libout = $part;
+        } else {
+            die "Unhandled lib option @ line $lineno: $part";
+        }
+    }
+    #print "LibOut: '$libout'\nLFlags: @lflags\nOfiles: @objfiles\n";
+    #exit(1);
+    removeDuplicates();
+    push(@{$build_structure{"LIBS"}}, $libout);
+    @{$build_structure{"LIBS_${libout}"}} = ("_DEFINES", "_INCLUDES", "_CFLAGS", "_SOURCES",
+                                             "_OBJECTS");
+    @{$build_structure{"LIBS_${libout}_DEFINES"}} = @defines;
+    @{$build_structure{"LIBS_${libout}_INCLUDES"}} = @incpaths;
+    @{$build_structure{"LIBS_${libout}_CFLAGS"}} = @cflags;
+    @{$build_structure{"LIBS_${libout}_SOURCES"}} = @sources;
+    @{$build_structure{"LIBS_${libout}_OBJECTS"}} = @objfiles;
+    clearCompileStep();
+}
+
+sub handleLinkLine
+{
+    my ($line, $lineno) = @_;
+    my (@objfiles, @lflags, @libs, $appout, $part);
+    my @parts = split(' ', $line);
+    shift(@parts); # ignore cmd
+    while ($part = shift @parts) {
+        if ($part =~ /^-[GRIDO]/) {
+            # eat compiler flags
+        } elsif ("$part" eq "-o") {
+            $appout = shift @parts;
+        } elsif ($part =~ /^-/) {
+            push(@lflags, $part);
+        } elsif ($part =~ /\.(a|lib)$/) {
+            push(@libs, $part);
+        } elsif ($part =~ /\.(o|obj)$/) {
+            push(@objfiles, $part);
+        } else {
+            die "Unhandled lib option @ line $lineno: $part";
+        }
+    }
+    #print "AppOut: '$appout'\nLFlags: @lflags\nLibs  : @libs\nOfiles: @objfiles\n";
+    #exit(1);
+    removeDuplicates();
+    push(@{$build_structure{"APPS"}}, $appout);
+    @{$build_structure{"APPS_${appout}"}} = ("_DEFINES", "_INCLUDES", "_CFLAGS", "_LFLAGS",
+                                             "_SOURCES", "_OBJECTS", "_LIBS");
+    @{$build_structure{"APPS_${appout}_DEFINES"}} = @defines;
+    @{$build_structure{"APPS_${appout}_INCLUDES"}} = @incpaths;
+    @{$build_structure{"APPS_${appout}_CFLAGS"}} = @cflags;
+    @{$build_structure{"APPS_${appout}_LFLAGS"}} = @lflags;
+    @{$build_structure{"APPS_${appout}_SOURCES"}} = @sources;
+    @{$build_structure{"APPS_${appout}_OBJECTS"}} = @objfiles;
+    @{$build_structure{"APPS_${appout}_LIBS"}} = @libs;
+    clearCompileStep();
+}
diff --git a/contrib/ciabot/README b/contrib/ciabot/README
new file mode 100644
index 0000000..3b916ac
--- /dev/null
+++ b/contrib/ciabot/README
@@ -0,0 +1,12 @@
+These are hook scripts for the CIA notification service at <http://cia.vc/>
+
+They are maintained by Eric S. Raymond <esr@thyrsus.com>.  There is an
+upstream resource page for them at <http://www.catb.org/esr/ciabot/>,
+but they are unlikely to change rapidly.
+
+You probably want the Python version; it's faster, more capable, and
+better documented.  The shell version is maintained only as a fallback
+for use on hosting sites that don't permit Python hook scripts.
+
+You will find installation instructions for each script in its comment
+header.
diff --git a/contrib/ciabot/ciabot.py b/contrib/ciabot/ciabot.py
new file mode 100755
index 0000000..9775dff
--- /dev/null
+++ b/contrib/ciabot/ciabot.py
@@ -0,0 +1,222 @@
+#!/usr/bin/env python
+# Copyright (c) 2010 Eric S. Raymond <esr@thyrsus.com>
+# Distributed under BSD terms.
+#
+# This script contains porcelain and porcelain byproducts.
+# It's Python because the Python standard libraries avoid portability/security
+# issues raised by callouts in the ancestral Perl and sh scripts.  It should
+# be compatible back to Python 2.1.5
+#
+# usage: ciabot.py [-V] [-n] [-p projectname]  [refname [commits...]]
+#
+# This script is meant to be run either in a post-commit hook or in an
+# update hook.  If there's nothing unusual about your hosting setup,
+# you can specify the project name with a -p option and avoid having
+# to modify this script.  Try it with -n to see the notification mail
+# dumped to stdout and verify that it looks sane. With -V it dumps its
+# version and exits.
+#
+# In post-commit, run it without arguments (other than possibly a -p
+# option). It will query for current HEAD and the latest commit ID to
+# get the information it needs.
+#
+# In update, call it with a refname followed by a list of commits:
+# You want to reverse the order git rev-list emits becxause it lists
+# from most recent to oldest.
+#
+# /path/to/ciabot.py ${refname} $(git rev-list ${oldhead}..${newhead} | tac)
+#
+# Note: this script uses mail, not XML-RPC, in order to avoid stalling
+# until timeout when the CIA XML-RPC server is down.
+#
+
+#
+# The project as known to CIA. You will either want to change this
+# or invoke the script with a -p option to set it.
+#
+project=None
+
+#
+# You may not need to change these:
+#
+import os, sys, commands, socket, urllib
+
+# Name of the repository.
+# You can hardwire this to make the script faster.
+repo = os.path.basename(os.getcwd())
+
+# Fully-qualified domain name of this host.
+# You can hardwire this to make the script faster.
+host = socket.getfqdn()
+
+# Changeset URL prefix for your repo: when the commit ID is appended
+# to this, it should point at a CGI that will display the commit
+# through gitweb or something similar. The defaults will probably
+# work if you have a typical gitweb/cgit setup.
+#
+#urlprefix="http://%(host)s/cgi-bin/gitweb.cgi?p=%(repo)s;a=commit;h="
+urlprefix="http://%(host)s/cgi-bin/cgit.cgi/%(repo)s/commit/?id="
+
+# The service used to turn your gitwebbish URL into a tinyurl so it
+# will take up less space on the IRC notification line.
+tinyifier = "http://tinyurl.com/api-create.php?url="
+
+# The template used to generate the XML messages to CIA.  You can make
+# visible changes to the IRC-bot notification lines by hacking this.
+# The default will produce a notfication line that looks like this:
+#
+# ${project}: ${author} ${repo}:${branch} * ${rev} ${files}: ${logmsg} ${url}
+#
+# By omitting $files you can collapse the files part to a single slash.
+xml = '''\
+<message>
+  <generator>
+    <name>CIA Python client for Git</name>
+    <version>%(gitver)s</version>
+    <url>%(generator)s</url>
+  </generator>
+  <source>
+    <project>%(project)s</project>
+    <branch>%(repo)s:%(branch)s</branch>
+  </source>
+  <timestamp>%(ts)s</timestamp>
+  <body>
+    <commit>
+      <author>%(author)s</author>
+      <revision>%(rev)s</revision>
+      <files>
+        %(files)s
+      </files>
+      <log>%(logmsg)s %(url)s</log>
+      <url>%(url)s</url>
+    </commit>
+  </body>
+</message>
+'''
+
+#
+# No user-serviceable parts below this line:
+#
+
+# Addresses for the e-mail. The from address is a dummy, since CIA
+# will never reply to this mail.
+fromaddr = "CIABOT-NOREPLY@" + host
+toaddr = "cia@cia.navi.cx"
+
+# Identify the generator script.
+# Should only change when the script itself gets a new home and maintainer.
+generator="http://www.catb.org/~esr/ciabot.py"
+
+def do(command):
+    return commands.getstatusoutput(command)[1]
+
+def report(refname, merged):
+    "Generate a commit notification to be reported to CIA"
+
+    # Try to tinyfy a reference to a web view for this commit.
+    try:
+        url = open(urllib.urlretrieve(tinyifier + urlprefix + merged)[0]).read()
+    except:
+        url = urlprefix + merged
+
+    branch = os.path.basename(refname)
+
+    # Compute a shortnane for the revision
+    rev = do("git describe '"+ merged +"' 2>/dev/null") or merged[:12]
+
+    # Extract the neta-information for the commit
+    rawcommit = do("git cat-file commit " + merged)
+    files=do("git diff-tree -r --name-only '"+ merged +"' | sed -e '1d' -e 's-.*-<file>&</file>-'")
+    inheader = True
+    headers = {}
+    logmsg = ""
+    for line in rawcommit.split("\n"):
+        if inheader:
+            if line:
+                fields = line.split()
+                headers[fields[0]] = " ".join(fields[1:])
+            else:
+                inheader = False
+        else:
+            logmsg = line
+            break
+    (author, ts) = headers["author"].split(">")
+
+    # This discards the part of the authors addrsss after @.
+    # Might be bnicece to ship the full email address, if not
+    # for spammers' address harvesters - getting this wrong
+    # would make the freenode #commits channel into harvester heaven.
+    author = author.replace("<", "").split("@")[0].split()[-1]
+
+    # This ignores the timezone.  Not clear what to do with it...
+    ts = ts.strip().split()[0]
+
+    context = locals()
+    context.update(globals())
+
+    out = xml % context
+
+    message = '''\
+Message-ID: <%(merged)s.%(author)s@%(project)s>
+From: %(fromaddr)s
+To: %(toaddr)s
+Content-type: text/xml
+Subject: DeliverXML
+
+%(out)s''' % locals()
+
+    return message
+
+if __name__ == "__main__":
+    import getopt
+
+    try:
+        (options, arguments) = getopt.getopt(sys.argv[1:], "np:V")
+    except getopt.GetoptError, msg:
+        print "ciabot.py: " + str(msg)
+        raise SystemExit, 1
+
+    mailit = True
+    for (switch, val) in options:
+        if switch == '-p':
+            project = val
+        elif switch == '-n':
+            mailit = False
+        elif switch == '-V':
+            print "ciabot.py: version 3.2"
+            sys.exit(0)
+
+    # Cough and die if user has not specified a project
+    if not project:
+        sys.stderr.write("ciabot.py: no project specified, bailing out.\n")
+        sys.exit(1)
+
+    # We'll need the git version number.
+    gitver = do("git --version").split()[0]
+
+    urlprefix = urlprefix % globals()
+
+    # The script wants a reference to head followed by the list of
+    # commit ID to report about.
+    if len(arguments) == 0:
+        refname = do("git symbolic-ref HEAD 2>/dev/null")
+        merges = [do("git rev-parse HEAD")]
+    else:
+        refname = arguments[0]
+        merges = arguments[1:]
+
+    if mailit:
+        import smtplib
+        server = smtplib.SMTP('localhost')
+
+    for merged in merges:
+        message = report(refname, merged)
+        if mailit:
+            server.sendmail(fromaddr, [toaddr], message)
+        else:
+            print message
+
+    if mailit:
+        server.quit()
+
+#End
diff --git a/contrib/ciabot/ciabot.sh b/contrib/ciabot/ciabot.sh
new file mode 100755
index 0000000..eb87bba
--- /dev/null
+++ b/contrib/ciabot/ciabot.sh
@@ -0,0 +1,192 @@
+#!/bin/sh
+# Distributed under the terms of the GNU General Public License v2
+# Copyright (c) 2006 Fernando J. Pereda <ferdy@gentoo.org>
+# Copyright (c) 2008 Natanael Copa <natanael.copa@gmail.com>
+# Copyright (c) 2010 Eric S. Raymond <esr@thyrsus.com>
+#
+# This is a version 3.x of ciabot.sh; use -V to find the exact
+# version.  Versions 1 and 2 were shipped in 2006 and 2008 and are not
+# version-stamped.  The version 2 maintainer has passed the baton.
+#
+# Note: This script should be considered obsolete.
+# There is a faster, better-documented rewrite in Python: find it as ciabot.py
+# Use this only if your hosting site forbids Python hooks.
+#
+# Originally based on Git ciabot.pl by Petr Baudis.
+# This script contains porcelain and porcelain byproducts.
+#
+# usage: ciabot.sh [-V] [-n] [-p projectname] [refname commit]
+#
+# This script is meant to be run either in a post-commit hook or in an
+# update hook.  If there's nothing unusual about your hosting setup,
+# you can specify the project name with a -p option and avoid having
+# to modify this script.  Try it with -n first to see the notification
+# mail dumped to stdout and verify that it looks sane.  Use -V to dump
+# the version and exit.
+#
+# In post-commit, run it without arguments (other than possibly a -p
+# option). It will query for current HEAD and the latest commit ID to
+# get the information it needs.
+#
+# In update, you have to call it once per merged commit:
+#
+#       refname=$1
+#       oldhead=$2
+#       newhead=$3
+#       for merged in $(git rev-list ${oldhead}..${newhead} | tac) ; do
+#               /path/to/ciabot.bash ${refname} ${merged}
+#       done
+#
+# The reason for the tac call ids that git rev-list emits commits from
+# most recent to least - better to ship notifactions from oldest to newest.
+#
+# Note: this script uses mail, not XML-RPC, in order to avoid stalling
+# until timeout when the CIA XML-RPC server is down.
+#
+
+#
+# The project as known to CIA. You will either want to change this
+# or set the project name with a -p option.
+#
+project=
+
+#
+# You may not need to change these:
+#
+
+# Name of the repository.
+# You can hardwire this to make the script faster.
+repo="`basename ${PWD}`"
+
+# Fully qualified domain name of the repo host.
+# You can hardwire this to make the script faster.
+host=`hostname --fqdn`
+
+# Changeset URL prefix for your repo: when the commit ID is appended
+# to this, it should point at a CGI that will display the commit
+# through gitweb or something similar. The defaults will probably
+# work if you have a typical gitweb/cgit setup.
+#urlprefix="http://${host}/cgi-bin/gitweb.cgi?p=${repo};a=commit;h="
+urlprefix="http://${host}/cgi-bin/cgit.cgi/${repo}/commit/?id="
+
+#
+# You probably will not need to change the following:
+#
+
+# Identify the script. Should change only when the script itself
+# gets a new home and maintainer.
+generator="http://www.catb.org/~esr/ciabot/ciabot.sh"
+
+# Addresses for the e-mail
+from="CIABOT-NOREPLY@${host}"
+to="cia@cia.navi.cx"
+
+# SMTP client to use - may need to edit the absolute pathname for your system
+sendmail="sendmail -t -f ${from}"
+
+#
+# No user-serviceable parts below this line:
+#
+
+# Should include all places sendmail is likely to lurk.
+PATH="$PATH:/usr/sbin/"
+
+mode=mailit
+while getopts pnV opt
+do
+    case $opt in
+	p) project=$2; shift ; shift ;;
+	n) mode=dumpit; shift ;;
+	V) echo "ciabot.sh: version 3.2"; exit 0; shift ;;
+    esac
+done
+
+# Cough and die if user has not specified a project
+if [ -z "$project" ]
+then
+    echo "ciabot.sh: no project specified, bailing out." >&2
+    exit 1
+fi
+
+if [ $# -eq 0 ] ; then
+	refname=$(git symbolic-ref HEAD 2>/dev/null)
+	merged=$(git rev-parse HEAD)
+else
+	refname=$1
+	merged=$2
+fi
+
+# This tries to turn your gitwebbish URL into a tinyurl so it will take up
+# less space on the IRC notification line. Some repo sites (I'm looking at
+# you, berlios.de!) forbid wget calls for security reasons.  On these,
+# the code will fall back to the full un-tinyfied URL.
+longurl=${urlprefix}${merged}
+url=$(wget -O - -q http://tinyurl.com/api-create.php?url=${longurl} 2>/dev/null)
+if [ -z "$url" ]; then
+	url="${longurl}"
+fi
+
+refname=${refname##refs/heads/}
+
+gitver=$(git --version)
+gitver=${gitver##* }
+
+rev=$(git describe ${merged} 2>/dev/null)
+# ${merged:0:12} was the only bashism left in the 2008 version of this
+# script, according to checkbashisms.  Replace it with ${merged} here
+# because it was just a fallback anyway, and it's worth accepting a
+# longer fallback for faster execution and removing the bash
+# dependency.
+[ -z ${rev} ] && rev=${merged}
+
+# This discards the part of the author's address after @.
+# Might be nice to ship the full email address, if not
+# for spammers' address harvesters - getting this wrong
+# would make the freenode #commits channel into harvester heaven.
+rawcommit=$(git cat-file commit ${merged})
+author=$(echo "$rawcommit" | sed -n -e '/^author .*<\([^@]*\).*$/s--\1-p')
+logmessage=$(echo "$rawcommit" | sed -e '1,/^$/d' | head -n 1)
+logmessage=$(echo "$logmessage" | sed 's/\&/&amp\;/g; s/</&lt\;/g; s/>/&gt\;/g')
+ts=$(echo "$rawcommit" | sed -n -e '/^author .*> \([0-9]\+\).*$/s--\1-p')
+files=$(git diff-tree -r --name-only ${merged} | sed -e '1d' -e 's-.*-<file>&</file>-')
+
+out="
+<message>
+  <generator>
+    <name>CIA Shell client for Git</name>
+    <version>${gitver}</version>
+    <url>${generator}</url>
+  </generator>
+  <source>
+    <project>${project}</project>
+    <branch>$repo:${refname}</branch>
+  </source>
+  <timestamp>${ts}</timestamp>
+  <body>
+    <commit>
+      <author>${author}</author>
+      <revision>${rev}</revision>
+      <files>
+	${files}
+      </files>
+      <log>${logmessage} ${url}</log>
+      <url>${url}</url>
+    </commit>
+  </body>
+</message>"
+
+if [ "$mode" = "dumpit" ]
+then
+    sendmail=cat
+fi
+
+${sendmail} << EOM
+Message-ID: <${merged}.${author}@${project}>
+From: ${from}
+To: ${to}
+Content-type: text/xml
+Subject: DeliverXML
+${out}
+EOM
+
+# vim: set tw=70 :
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
old mode 100755
new mode 100644
index 745b5fb..ffedce7
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1,6 +1,6 @@
 #!bash
 #
-# bash completion support for core Git.
+# bash/zsh completion support for core Git.
 #
 # Copyright (C) 2006,2007 Shawn O. Pearce <spearce@spearce.org>
 # Conceptually based on gitcompletion (http://gitweb.hawaga.org.uk/).
@@ -18,47 +18,14 @@
 # To use these routines:
 #
 #    1) Copy this file to somewhere (e.g. ~/.git-completion.sh).
-#    2) Added the following line to your .bashrc:
+#    2) Add the following line to your .bashrc/.zshrc:
 #        source ~/.git-completion.sh
-#
-#    3) You may want to make sure the git executable is available
-#       in your PATH before this script is sourced, as some caching
-#       is performed while the script loads.  If git isn't found
-#       at source time then all lookups will be done on demand,
-#       which may be slightly slower.
-#
-#    4) Consider changing your PS1 to also show the current branch:
-#        PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '
-#
-#       The argument to __git_ps1 will be displayed only if you
-#       are currently in a git repository.  The %s token will be
-#       the name of the current branch.
-#
-#       In addition, if you set GIT_PS1_SHOWDIRTYSTATE to a nonempty
-#       value, unstaged (*) and staged (+) changes will be shown next
-#       to the branch name.  You can configure this per-repository
-#       with the bash.showDirtyState variable, which defaults to true
-#       once GIT_PS1_SHOWDIRTYSTATE is enabled.
-#
-#       You can also see if currently something is stashed, by setting
-#       GIT_PS1_SHOWSTASHSTATE to a nonempty value. If something is stashed,
-#       then a '$' will be shown next to the branch name.
-#
-#       If you would like to see if there're untracked files, then you can
-#       set GIT_PS1_SHOWUNTRACKEDFILES to a nonempty value. If there're
-#       untracked files, then a '%' will be shown next to the branch name.
-#
-# To submit patches:
-#
-#    *) Read Documentation/SubmittingPatches
-#    *) Send all patches to the current maintainer:
-#
-#       "Shawn O. Pearce" <spearce@spearce.org>
-#
-#    *) Always CC the Git mailing list:
-#
-#       git@vger.kernel.org
-#
+#    3) Consider changing your PS1 to also show the current branch,
+#       see git-prompt.sh for details.
+
+if [[ -n ${ZSH_VERSION-} ]]; then
+	autoload -U +X bashcompinit && bashcompinit
+fi
 
 case "$COMP_WORDBREAKS" in
 *:*) : great ;;
@@ -69,9 +36,14 @@
 # returns location of .git repo
 __gitdir ()
 {
+	# Note: this function is duplicated in git-prompt.sh
+	# When updating it, make sure you update the other one to match.
 	if [ -z "${1-}" ]; then
 		if [ -n "${__git_dir-}" ]; then
 			echo "$__git_dir"
+		elif [ -n "${GIT_DIR-}" ]; then
+			test -d "${GIT_DIR-}" || return 1
+			echo "$GIT_DIR"
 		elif [ -d .git ]; then
 			echo .git
 		else
@@ -84,121 +56,187 @@
 	fi
 }
 
-# __git_ps1 accepts 0 or 1 arguments (i.e., format string)
-# returns text to add to bash PS1 prompt (includes branch name)
-__git_ps1 ()
-{
-	local g="$(__gitdir)"
-	if [ -n "$g" ]; then
-		local r
-		local b
-		if [ -f "$g/rebase-merge/interactive" ]; then
-			r="|REBASE-i"
-			b="$(cat "$g/rebase-merge/head-name")"
-		elif [ -d "$g/rebase-merge" ]; then
-			r="|REBASE-m"
-			b="$(cat "$g/rebase-merge/head-name")"
-		else
-			if [ -d "$g/rebase-apply" ]; then
-				if [ -f "$g/rebase-apply/rebasing" ]; then
-					r="|REBASE"
-				elif [ -f "$g/rebase-apply/applying" ]; then
-					r="|AM"
-				else
-					r="|AM/REBASE"
-				fi
-			elif [ -f "$g/MERGE_HEAD" ]; then
-				r="|MERGING"
-			elif [ -f "$g/BISECT_LOG" ]; then
-				r="|BISECTING"
-			fi
-
-			b="$(git symbolic-ref HEAD 2>/dev/null)" || {
-
-				b="$(
-				case "${GIT_PS1_DESCRIBE_STYLE-}" in
-				(contains)
-					git describe --contains HEAD ;;
-				(branch)
-					git describe --contains --all HEAD ;;
-				(describe)
-					git describe HEAD ;;
-				(* | default)
-					git describe --exact-match HEAD ;;
-				esac 2>/dev/null)" ||
-
-				b="$(cut -c1-7 "$g/HEAD" 2>/dev/null)..." ||
-				b="unknown"
-				b="($b)"
-			}
-		fi
-
-		local w
-		local i
-		local s
-		local u
-		local c
-
-		if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then
-			if [ "true" = "$(git rev-parse --is-bare-repository 2>/dev/null)" ]; then
-				c="BARE:"
-			else
-				b="GIT_DIR!"
-			fi
-		elif [ "true" = "$(git rev-parse --is-inside-work-tree 2>/dev/null)" ]; then
-			if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ]; then
-				if [ "$(git config --bool bash.showDirtyState)" != "false" ]; then
-					git diff --no-ext-diff --ignore-submodules \
-						--quiet --exit-code || w="*"
-					if git rev-parse --quiet --verify HEAD >/dev/null; then
-						git diff-index --cached --quiet \
-							--ignore-submodules HEAD -- || i="+"
-					else
-						i="#"
-					fi
-				fi
-			fi
-			if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ]; then
-			        git rev-parse --verify refs/stash >/dev/null 2>&1 && s="$"
-			fi
-
-			if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ]; then
-			   if [ -n "$(git ls-files --others --exclude-standard)" ]; then
-			      u="%"
-			   fi
-			fi
-		fi
-
-		if [ -n "${1-}" ]; then
-			printf "$1" "$c${b##refs/heads/}$w$i$s$u$r"
-		else
-			printf " (%s)" "$c${b##refs/heads/}$w$i$s$u$r"
-		fi
-	fi
-}
-
-# __gitcomp_1 requires 2 arguments
 __gitcomp_1 ()
 {
-	local c IFS=' '$'\t'$'\n'
+	local c IFS=$' \t\n'
 	for c in $1; do
-		case "$c$2" in
-		--*=*) printf %s$'\n' "$c$2" ;;
-		*.)    printf %s$'\n' "$c$2" ;;
-		*)     printf %s$'\n' "$c$2 " ;;
+		c="$c$2"
+		case $c in
+		--*=*|*.) ;;
+		*) c="$c " ;;
 		esac
+		printf '%s\n' "$c"
 	done
 }
 
-# __gitcomp accepts 1, 2, 3, or 4 arguments
-# generates completion reply with compgen
+# The following function is based on code from:
+#
+#   bash_completion - programmable completion functions for bash 3.2+
+#
+#   Copyright © 2006-2008, Ian Macdonald <ian@caliban.org>
+#             © 2009-2010, Bash Completion Maintainers
+#                     <bash-completion-devel@lists.alioth.debian.org>
+#
+#   This program is free software; you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as published by
+#   the Free Software Foundation; either version 2, or (at your option)
+#   any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with this program; if not, write to the Free Software Foundation,
+#   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+#   The latest version of this software can be obtained here:
+#
+#   http://bash-completion.alioth.debian.org/
+#
+#   RELEASE: 2.x
+
+# This function can be used to access a tokenized list of words
+# on the command line:
+#
+#	__git_reassemble_comp_words_by_ref '=:'
+#	if test "${words_[cword_-1]}" = -w
+#	then
+#		...
+#	fi
+#
+# The argument should be a collection of characters from the list of
+# word completion separators (COMP_WORDBREAKS) to treat as ordinary
+# characters.
+#
+# This is roughly equivalent to going back in time and setting
+# COMP_WORDBREAKS to exclude those characters.  The intent is to
+# make option types like --date=<type> and <rev>:<path> easy to
+# recognize by treating each shell word as a single token.
+#
+# It is best not to set COMP_WORDBREAKS directly because the value is
+# shared with other completion scripts.  By the time the completion
+# function gets called, COMP_WORDS has already been populated so local
+# changes to COMP_WORDBREAKS have no effect.
+#
+# Output: words_, cword_, cur_.
+
+__git_reassemble_comp_words_by_ref()
+{
+	local exclude i j first
+	# Which word separators to exclude?
+	exclude="${1//[^$COMP_WORDBREAKS]}"
+	cword_=$COMP_CWORD
+	if [ -z "$exclude" ]; then
+		words_=("${COMP_WORDS[@]}")
+		return
+	fi
+	# List of word completion separators has shrunk;
+	# re-assemble words to complete.
+	for ((i=0, j=0; i < ${#COMP_WORDS[@]}; i++, j++)); do
+		# Append each nonempty word consisting of just
+		# word separator characters to the current word.
+		first=t
+		while
+			[ $i -gt 0 ] &&
+			[ -n "${COMP_WORDS[$i]}" ] &&
+			# word consists of excluded word separators
+			[ "${COMP_WORDS[$i]//[^$exclude]}" = "${COMP_WORDS[$i]}" ]
+		do
+			# Attach to the previous token,
+			# unless the previous token is the command name.
+			if [ $j -ge 2 ] && [ -n "$first" ]; then
+				((j--))
+			fi
+			first=
+			words_[$j]=${words_[j]}${COMP_WORDS[i]}
+			if [ $i = $COMP_CWORD ]; then
+				cword_=$j
+			fi
+			if (($i < ${#COMP_WORDS[@]} - 1)); then
+				((i++))
+			else
+				# Done.
+				return
+			fi
+		done
+		words_[$j]=${words_[j]}${COMP_WORDS[i]}
+		if [ $i = $COMP_CWORD ]; then
+			cword_=$j
+		fi
+	done
+}
+
+if ! type _get_comp_words_by_ref >/dev/null 2>&1; then
+if [[ -z ${ZSH_VERSION:+set} ]]; then
+_get_comp_words_by_ref ()
+{
+	local exclude cur_ words_ cword_
+	if [ "$1" = "-n" ]; then
+		exclude=$2
+		shift 2
+	fi
+	__git_reassemble_comp_words_by_ref "$exclude"
+	cur_=${words_[cword_]}
+	while [ $# -gt 0 ]; do
+		case "$1" in
+		cur)
+			cur=$cur_
+			;;
+		prev)
+			prev=${words_[$cword_-1]}
+			;;
+		words)
+			words=("${words_[@]}")
+			;;
+		cword)
+			cword=$cword_
+			;;
+		esac
+		shift
+	done
+}
+else
+_get_comp_words_by_ref ()
+{
+	while [ $# -gt 0 ]; do
+		case "$1" in
+		cur)
+			cur=${COMP_WORDS[COMP_CWORD]}
+			;;
+		prev)
+			prev=${COMP_WORDS[COMP_CWORD-1]}
+			;;
+		words)
+			words=("${COMP_WORDS[@]}")
+			;;
+		cword)
+			cword=$COMP_CWORD
+			;;
+		-n)
+			# assume COMP_WORDBREAKS is already set sanely
+			shift
+			;;
+		esac
+		shift
+	done
+}
+fi
+fi
+
+# Generates completion reply with compgen, appending a space to possible
+# completion words, if necessary.
+# It accepts 1 to 4 arguments:
+# 1: List of possible completion words.
+# 2: A prefix to be added to each possible completion word (optional).
+# 3: Generate possible completion matches for this word (optional).
+# 4: A suffix to be appended to each possible completion word (optional).
 __gitcomp ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
-	if [ $# -gt 2 ]; then
-		cur="$3"
-	fi
-	case "$cur" in
+	local cur_="${3-$cur}"
+
+	case "$cur_" in
 	--*=)
 		COMPREPLY=()
 		;;
@@ -206,80 +244,108 @@
 		local IFS=$'\n'
 		COMPREPLY=($(compgen -P "${2-}" \
 			-W "$(__gitcomp_1 "${1-}" "${4-}")" \
-			-- "$cur"))
+			-- "$cur_"))
 		;;
 	esac
 }
 
-# __git_heads accepts 0 or 1 arguments (to pass to __gitdir)
+# Generates completion reply with compgen from newline-separated possible
+# completion words by appending a space to all of them.
+# It accepts 1 to 4 arguments:
+# 1: List of possible completion words, separated by a single newline.
+# 2: A prefix to be added to each possible completion word (optional).
+# 3: Generate possible completion matches for this word (optional).
+# 4: A suffix to be appended to each possible completion word instead of
+#    the default space (optional).  If specified but empty, nothing is
+#    appended.
+__gitcomp_nl ()
+{
+	local IFS=$'\n'
+	COMPREPLY=($(compgen -P "${2-}" -S "${4- }" -W "$1" -- "${3-$cur}"))
+}
+
 __git_heads ()
 {
-	local cmd i is_hash=y dir="$(__gitdir "${1-}")"
+	local dir="$(__gitdir)"
 	if [ -d "$dir" ]; then
 		git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
 			refs/heads
 		return
 	fi
-	for i in $(git ls-remote "${1-}" 2>/dev/null); do
-		case "$is_hash,$i" in
-		y,*) is_hash=n ;;
-		n,*^{}) is_hash=y ;;
-		n,refs/heads/*) is_hash=y; echo "${i#refs/heads/}" ;;
-		n,*) is_hash=y; echo "$i" ;;
-		esac
-	done
 }
 
-# __git_tags accepts 0 or 1 arguments (to pass to __gitdir)
 __git_tags ()
 {
-	local cmd i is_hash=y dir="$(__gitdir "${1-}")"
+	local dir="$(__gitdir)"
 	if [ -d "$dir" ]; then
 		git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
 			refs/tags
 		return
 	fi
-	for i in $(git ls-remote "${1-}" 2>/dev/null); do
-		case "$is_hash,$i" in
-		y,*) is_hash=n ;;
-		n,*^{}) is_hash=y ;;
-		n,refs/tags/*) is_hash=y; echo "${i#refs/tags/}" ;;
-		n,*) is_hash=y; echo "$i" ;;
-		esac
-	done
 }
 
-# __git_refs accepts 0 or 1 arguments (to pass to __gitdir)
+# __git_refs accepts 0, 1 (to pass to __gitdir), or 2 arguments
+# presence of 2nd argument means use the guess heuristic employed
+# by checkout for tracking branches
 __git_refs ()
 {
-	local i is_hash=y dir="$(__gitdir "${1-}")"
-	local cur="${COMP_WORDS[COMP_CWORD]}" format refs
+	local i hash dir="$(__gitdir "${1-}")" track="${2-}"
+	local format refs
 	if [ -d "$dir" ]; then
 		case "$cur" in
 		refs|refs/*)
 			format="refname"
 			refs="${cur%/*}"
+			track=""
 			;;
 		*)
-			if [ -e "$dir/HEAD" ]; then echo HEAD; fi
+			for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD; do
+				if [ -e "$dir/$i" ]; then echo $i; fi
+			done
 			format="refname:short"
 			refs="refs/tags refs/heads refs/remotes"
 			;;
 		esac
 		git --git-dir="$dir" for-each-ref --format="%($format)" \
 			$refs
+		if [ -n "$track" ]; then
+			# employ the heuristic used by git checkout
+			# Try to find a remote branch that matches the completion word
+			# but only output if the branch name is unique
+			local ref entry
+			git --git-dir="$dir" for-each-ref --shell --format="ref=%(refname:short)" \
+				"refs/remotes/" | \
+			while read -r entry; do
+				eval "$entry"
+				ref="${ref#*/}"
+				if [[ "$ref" == "$cur"* ]]; then
+					echo "$ref"
+				fi
+			done | uniq -u
+		fi
 		return
 	fi
-	for i in $(git ls-remote "$dir" 2>/dev/null); do
-		case "$is_hash,$i" in
-		y,*) is_hash=n ;;
-		n,*^{}) is_hash=y ;;
-		n,refs/tags/*) is_hash=y; echo "${i#refs/tags/}" ;;
-		n,refs/heads/*) is_hash=y; echo "${i#refs/heads/}" ;;
-		n,refs/remotes/*) is_hash=y; echo "${i#refs/remotes/}" ;;
-		n,*) is_hash=y; echo "$i" ;;
-		esac
-	done
+	case "$cur" in
+	refs|refs/*)
+		git ls-remote "$dir" "$cur*" 2>/dev/null | \
+		while read -r hash i; do
+			case "$i" in
+			*^{}) ;;
+			*) echo "$i" ;;
+			esac
+		done
+		;;
+	*)
+		git ls-remote "$dir" HEAD ORIG_HEAD 'refs/tags/*' 'refs/heads/*' 'refs/remotes/*' 2>/dev/null | \
+		while read -r hash i; do
+			case "$i" in
+			*^{}) ;;
+			refs/*) echo "${i#refs/*/}" ;;
+			*) echo "$i" ;;
+			esac
+		done
+		;;
+	esac
 }
 
 # __git_refs2 requires 1 argument (to pass to __git_refs)
@@ -294,46 +360,25 @@
 # __git_refs_remotes requires 1 argument (to pass to ls-remote)
 __git_refs_remotes ()
 {
-	local cmd i is_hash=y
-	for i in $(git ls-remote "$1" 2>/dev/null); do
-		case "$is_hash,$i" in
-		n,refs/heads/*)
-			is_hash=y
-			echo "$i:refs/remotes/$1/${i#refs/heads/}"
-			;;
-		y,*) is_hash=n ;;
-		n,*^{}) is_hash=y ;;
-		n,refs/tags/*) is_hash=y;;
-		n,*) is_hash=y; ;;
-		esac
+	local i hash
+	git ls-remote "$1" 'refs/heads/*' 2>/dev/null | \
+	while read -r hash i; do
+		echo "$i:refs/remotes/$1/${i#refs/heads/}"
 	done
 }
 
 __git_remotes ()
 {
-	local i ngoff IFS=$'\n' d="$(__gitdir)"
-	shopt -q nullglob || ngoff=1
-	shopt -s nullglob
-	for i in "$d/remotes"/*; do
-		echo ${i#$d/remotes/}
-	done
-	[ "$ngoff" ] && shopt -u nullglob
-	for i in $(git --git-dir="$d" config --list); do
-		case "$i" in
-		remote.*.url=*)
-			i="${i#remote.}"
-			echo "${i/.url=*/}"
-			;;
-		esac
+	local i IFS=$'\n' d="$(__gitdir)"
+	test -d "$d/remotes" && ls -1 "$d/remotes"
+	for i in $(git --git-dir="$d" config --get-regexp 'remote\..*\.url' 2>/dev/null); do
+		i="${i#remote.}"
+		echo "${i/.url*/}"
 	done
 }
 
-__git_merge_strategies ()
+__git_list_merge_strategies ()
 {
-	if [ -n "${__git_merge_strategylist-}" ]; then
-		echo "$__git_merge_strategylist"
-		return
-	fi
 	git merge -s help 2>&1 |
 	sed -n -e '/[Aa]vailable strategies are: /,/^$/{
 		s/\.$//
@@ -343,36 +388,47 @@
 		p
 	}'
 }
-__git_merge_strategylist=
-__git_merge_strategylist=$(__git_merge_strategies 2>/dev/null)
 
-__git_complete_file ()
+__git_merge_strategies=
+# 'git merge -s help' (and thus detection of the merge strategy
+# list) fails, unfortunately, if run outside of any git working
+# tree.  __git_merge_strategies is set to the empty string in
+# that case, and the detection will be repeated the next time it
+# is needed.
+__git_compute_merge_strategies ()
 {
-	local pfx ls ref cur="${COMP_WORDS[COMP_CWORD]}"
-	case "$cur" in
+	test -n "$__git_merge_strategies" ||
+	__git_merge_strategies=$(__git_list_merge_strategies)
+}
+
+__git_complete_revlist_file ()
+{
+	local pfx ls ref cur_="$cur"
+	case "$cur_" in
+	*..?*:*)
+		return
+		;;
 	?*:*)
-		ref="${cur%%:*}"
-		cur="${cur#*:}"
-		case "$cur" in
+		ref="${cur_%%:*}"
+		cur_="${cur_#*:}"
+		case "$cur_" in
 		?*/*)
-			pfx="${cur%/*}"
-			cur="${cur##*/}"
+			pfx="${cur_%/*}"
+			cur_="${cur_##*/}"
 			ls="$ref:$pfx"
 			pfx="$pfx/"
 			;;
 		*)
 			ls="$ref"
 			;;
-	    esac
+		esac
 
 		case "$COMP_WORDBREAKS" in
 		*:*) : great ;;
 		*)   pfx="$ref:$pfx" ;;
 		esac
 
-		local IFS=$'\n'
-		COMPREPLY=($(compgen -P "$pfx" \
-			-W "$(git --git-dir="$(__gitdir)" ls-tree "$ls" \
+		__gitcomp_nl "$(git --git-dir="$(__gitdir)" ls-tree "$ls" \
 				| sed '/^100... blob /{
 				           s,^.*	,,
 				           s,$, ,
@@ -386,50 +442,63 @@
 				           s,$,/,
 				       }
 				       s/^.*	//')" \
-			-- "$cur"))
+			"$pfx" "$cur_" ""
+		;;
+	*...*)
+		pfx="${cur_%...*}..."
+		cur_="${cur_#*...}"
+		__gitcomp_nl "$(__git_refs)" "$pfx" "$cur_"
+		;;
+	*..*)
+		pfx="${cur_%..*}.."
+		cur_="${cur_#*..}"
+		__gitcomp_nl "$(__git_refs)" "$pfx" "$cur_"
 		;;
 	*)
-		__gitcomp "$(__git_refs)"
+		__gitcomp_nl "$(__git_refs)"
 		;;
 	esac
 }
 
+
+__git_complete_file ()
+{
+	__git_complete_revlist_file
+}
+
 __git_complete_revlist ()
 {
-	local pfx cur="${COMP_WORDS[COMP_CWORD]}"
-	case "$cur" in
-	*...*)
-		pfx="${cur%...*}..."
-		cur="${cur#*...}"
-		__gitcomp "$(__git_refs)" "$pfx" "$cur"
-		;;
-	*..*)
-		pfx="${cur%..*}.."
-		cur="${cur#*..}"
-		__gitcomp "$(__git_refs)" "$pfx" "$cur"
-		;;
-	*)
-		__gitcomp "$(__git_refs)"
-		;;
-	esac
+	__git_complete_revlist_file
 }
 
 __git_complete_remote_or_refspec ()
 {
-	local cmd="${COMP_WORDS[1]}"
-	local cur="${COMP_WORDS[COMP_CWORD]}"
+	local cur_="$cur" cmd="${words[1]}"
 	local i c=2 remote="" pfx="" lhs=1 no_complete_refspec=0
-	while [ $c -lt $COMP_CWORD ]; do
-		i="${COMP_WORDS[c]}"
+	if [ "$cmd" = "remote" ]; then
+		((c++))
+	fi
+	while [ $c -lt $cword ]; do
+		i="${words[c]}"
 		case "$i" in
-		--all|--mirror) [ "$cmd" = "push" ] && no_complete_refspec=1 ;;
+		--mirror) [ "$cmd" = "push" ] && no_complete_refspec=1 ;;
+		--all)
+			case "$cmd" in
+			push) no_complete_refspec=1 ;;
+			fetch)
+				COMPREPLY=()
+				return
+				;;
+			*) ;;
+			esac
+			;;
 		-*) ;;
 		*) remote="$i"; break ;;
 		esac
-		c=$((++c))
+		((c++))
 	done
 	if [ -z "$remote" ]; then
-		__gitcomp "$(__git_remotes)"
+		__gitcomp_nl "$(__git_remotes)"
 		return
 	fi
 	if [ $no_complete_refspec = 1 ]; then
@@ -437,40 +506,40 @@
 		return
 	fi
 	[ "$remote" = "." ] && remote=
-	case "$cur" in
+	case "$cur_" in
 	*:*)
 		case "$COMP_WORDBREAKS" in
 		*:*) : great ;;
-		*)   pfx="${cur%%:*}:" ;;
+		*)   pfx="${cur_%%:*}:" ;;
 		esac
-		cur="${cur#*:}"
+		cur_="${cur_#*:}"
 		lhs=0
 		;;
 	+*)
 		pfx="+"
-		cur="${cur#+}"
+		cur_="${cur_#+}"
 		;;
 	esac
 	case "$cmd" in
 	fetch)
 		if [ $lhs = 1 ]; then
-			__gitcomp "$(__git_refs2 "$remote")" "$pfx" "$cur"
+			__gitcomp_nl "$(__git_refs2 "$remote")" "$pfx" "$cur_"
 		else
-			__gitcomp "$(__git_refs)" "$pfx" "$cur"
+			__gitcomp_nl "$(__git_refs)" "$pfx" "$cur_"
 		fi
 		;;
-	pull)
+	pull|remote)
 		if [ $lhs = 1 ]; then
-			__gitcomp "$(__git_refs "$remote")" "$pfx" "$cur"
+			__gitcomp_nl "$(__git_refs "$remote")" "$pfx" "$cur_"
 		else
-			__gitcomp "$(__git_refs)" "$pfx" "$cur"
+			__gitcomp_nl "$(__git_refs)" "$pfx" "$cur_"
 		fi
 		;;
 	push)
 		if [ $lhs = 1 ]; then
-			__gitcomp "$(__git_refs)" "$pfx" "$cur"
+			__gitcomp_nl "$(__git_refs)" "$pfx" "$cur_"
 		else
-			__gitcomp "$(__git_refs "$remote")" "$pfx" "$cur"
+			__gitcomp_nl "$(__git_refs "$remote")" "$pfx" "$cur_"
 		fi
 		;;
 	esac
@@ -478,29 +547,25 @@
 
 __git_complete_strategy ()
 {
-	case "${COMP_WORDS[COMP_CWORD-1]}" in
+	__git_compute_merge_strategies
+	case "$prev" in
 	-s|--strategy)
-		__gitcomp "$(__git_merge_strategies)"
+		__gitcomp "$__git_merge_strategies"
 		return 0
 	esac
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--strategy=*)
-		__gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}"
+		__gitcomp "$__git_merge_strategies" "" "${cur##--strategy=}"
 		return 0
 		;;
 	esac
 	return 1
 }
 
-__git_all_commands ()
+__git_list_all_commands ()
 {
-	if [ -n "${__git_all_commandlist-}" ]; then
-		echo "$__git_all_commandlist"
-		return
-	fi
 	local i IFS=" "$'\n'
-	for i in $(git help -a|egrep '^ ')
+	for i in $(git help -a|egrep '^  [a-zA-Z0-9]')
 	do
 		case $i in
 		*--*)             : helper pattern;;
@@ -508,17 +573,19 @@
 		esac
 	done
 }
-__git_all_commandlist=
-__git_all_commandlist="$(__git_all_commands 2>/dev/null)"
 
-__git_porcelain_commands ()
+__git_all_commands=
+__git_compute_all_commands ()
 {
-	if [ -n "${__git_porcelain_commandlist-}" ]; then
-		echo "$__git_porcelain_commandlist"
-		return
-	fi
+	test -n "$__git_all_commands" ||
+	__git_all_commands=$(__git_list_all_commands)
+}
+
+__git_list_porcelain_commands ()
+{
 	local i IFS=" "$'\n'
-	for i in "help" $(__git_all_commands)
+	__git_compute_all_commands
+	for i in "help" $__git_all_commands
 	do
 		case $i in
 		*--*)             : helper pattern;;
@@ -531,6 +598,8 @@
 		checkout-index)   : plumbing;;
 		commit-tree)      : plumbing;;
 		count-objects)    : infrequent;;
+		credential-cache) : credentials helper;;
+		credential-store) : credentials helper;;
 		cvsexportcommit)  : export;;
 		cvsimport)        : import;;
 		cvsserver)        : daemon;;
@@ -569,7 +638,7 @@
 		quiltimport)      : import;;
 		read-tree)        : plumbing;;
 		receive-pack)     : plumbing;;
-		reflog)           : plumbing;;
+		remote-*)         : transport;;
 		repo-config)      : deprecated;;
 		rerere)           : plumbing;;
 		rev-list)         : plumbing;;
@@ -599,17 +668,36 @@
 		esac
 	done
 }
-__git_porcelain_commandlist=
-__git_porcelain_commandlist="$(__git_porcelain_commands 2>/dev/null)"
+
+__git_porcelain_commands=
+__git_compute_porcelain_commands ()
+{
+	__git_compute_all_commands
+	test -n "$__git_porcelain_commands" ||
+	__git_porcelain_commands=$(__git_list_porcelain_commands)
+}
+
+__git_pretty_aliases ()
+{
+	local i IFS=$'\n'
+	for i in $(git --git-dir="$(__gitdir)" config --get-regexp "pretty\..*" 2>/dev/null); do
+		case "$i" in
+		pretty.*)
+			i="${i#pretty.}"
+			echo "${i/ */}"
+			;;
+		esac
+	done
+}
 
 __git_aliases ()
 {
 	local i IFS=$'\n'
-	for i in $(git --git-dir="$(__gitdir)" config --list); do
+	for i in $(git --git-dir="$(__gitdir)" config --get-regexp "alias\..*" 2>/dev/null); do
 		case "$i" in
 		alias.*)
 			i="${i#alias.}"
-			echo "${i/=*/}"
+			echo "${i/ */}"
 			;;
 		esac
 	done
@@ -621,38 +709,46 @@
 	local word cmdline=$(git --git-dir="$(__gitdir)" \
 		config --get "alias.$1")
 	for word in $cmdline; do
-		if [ "${word##-*}" ]; then
-			echo $word
+		case "$word" in
+		\!gitk|gitk)
+			echo "gitk"
 			return
-		fi
+			;;
+		\!*)	: shell command alias ;;
+		-*)	: option ;;
+		*=*)	: setting env ;;
+		git)	: git itself ;;
+		*)
+			echo "$word"
+			return
+		esac
 	done
 }
 
-# __git_find_subcommand requires 1 argument
-__git_find_subcommand ()
+# __git_find_on_cmdline requires 1 argument
+__git_find_on_cmdline ()
 {
 	local word subcommand c=1
-
-	while [ $c -lt $COMP_CWORD ]; do
-		word="${COMP_WORDS[c]}"
+	while [ $c -lt $cword ]; do
+		word="${words[c]}"
 		for subcommand in $1; do
 			if [ "$subcommand" = "$word" ]; then
 				echo "$subcommand"
 				return
 			fi
 		done
-		c=$((++c))
+		((c++))
 	done
 }
 
 __git_has_doubledash ()
 {
 	local c=1
-	while [ $c -lt $COMP_CWORD ]; do
-		if [ "--" = "${COMP_WORDS[c]}" ]; then
+	while [ $c -lt $cword ]; do
+		if [ "--" = "${words[c]}" ]; then
 			return 0
 		fi
-		c=$((++c))
+		((c++))
 	done
 	return 1
 }
@@ -661,9 +757,9 @@
 
 _git_am ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}" dir="$(__gitdir)"
+	local dir="$(__gitdir)"
 	if [ -d "$dir"/rebase-apply ]; then
-		__gitcomp "--skip --resolved --abort"
+		__gitcomp "--skip --continue --resolved --abort"
 		return
 	fi
 	case "$cur" in
@@ -674,8 +770,9 @@
 	--*)
 		__gitcomp "
 			--3way --committer-date-is-author-date --ignore-date
+			--ignore-whitespace --ignore-space-change
 			--interactive --keep --no-utf8 --signoff --utf8
-			--whitespace=
+			--whitespace= --scissors
 			"
 		return
 	esac
@@ -684,7 +781,6 @@
 
 _git_apply ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--whitespace=*)
 		__gitcomp "$__git_whitespacelist" "" "${cur##--whitespace=}"
@@ -695,6 +791,7 @@
 			--stat --numstat --summary --check --index
 			--cached --index-info --reverse --reject --unidiff-zero
 			--apply --no-add --exclude=
+			--ignore-whitespace --ignore-space-change
 			--whitespace= --inaccurate-eof --verbose
 			"
 		return
@@ -706,7 +803,6 @@
 {
 	__git_has_doubledash && return
 
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
 		__gitcomp "
@@ -720,14 +816,13 @@
 
 _git_archive ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--format=*)
 		__gitcomp "$(git archive --list)" "" "${cur##--format=}"
 		return
 		;;
 	--remote=*)
-		__gitcomp "$(__git_remotes)" "" "${cur##--remote=}"
+		__gitcomp_nl "$(__git_remotes)" "" "${cur##--remote=}"
 		return
 		;;
 	--*)
@@ -746,15 +841,19 @@
 	__git_has_doubledash && return
 
 	local subcommands="start bad good skip reset visualize replay log run"
-	local subcommand="$(__git_find_subcommand "$subcommands")"
+	local subcommand="$(__git_find_on_cmdline "$subcommands")"
 	if [ -z "$subcommand" ]; then
-		__gitcomp "$subcommands"
+		if [ -f "$(__gitdir)"/BISECT_START ]; then
+			__gitcomp "$subcommands"
+		else
+			__gitcomp "replay start"
+		fi
 		return
 	fi
 
 	case "$subcommand" in
-	bad|good|reset|skip)
-		__gitcomp "$(__git_refs)"
+	bad|good|reset|skip|start)
+		__gitcomp_nl "$(__git_refs)"
 		;;
 	*)
 		COMPREPLY=()
@@ -766,27 +865,28 @@
 {
 	local i c=1 only_local_ref="n" has_r="n"
 
-	while [ $c -lt $COMP_CWORD ]; do
-		i="${COMP_WORDS[c]}"
+	while [ $c -lt $cword ]; do
+		i="${words[c]}"
 		case "$i" in
 		-d|-m)	only_local_ref="y" ;;
 		-r)	has_r="y" ;;
 		esac
-		c=$((++c))
+		((c++))
 	done
 
-	case "${COMP_WORDS[COMP_CWORD]}" in
+	case "$cur" in
 	--*)
 		__gitcomp "
 			--color --no-color --verbose --abbrev= --no-abbrev
 			--track --no-track --contains --merged --no-merged
+			--set-upstream --edit-description --list
 			"
 		;;
 	*)
 		if [ $only_local_ref = "y" -a $has_r = "n" ]; then
-			__gitcomp "$(__git_heads)"
+			__gitcomp_nl "$(__git_heads)"
 		else
-			__gitcomp "$(__git_refs)"
+			__gitcomp_nl "$(__git_refs)"
 		fi
 		;;
 	esac
@@ -794,8 +894,8 @@
 
 _git_bundle ()
 {
-	local cmd="${COMP_WORDS[2]}"
-	case "$COMP_CWORD" in
+	local cmd="${words[2]}"
+	case "$cword" in
 	2)
 		__gitcomp "create list-heads verify unbundle"
 		;;
@@ -816,7 +916,26 @@
 {
 	__git_has_doubledash && return
 
-	__gitcomp "$(__git_refs)"
+	case "$cur" in
+	--conflict=*)
+		__gitcomp "diff3 merge" "" "${cur##--conflict=}"
+		;;
+	--*)
+		__gitcomp "
+			--quiet --ours --theirs --track --no-track --merge
+			--conflict= --orphan --patch
+			"
+		;;
+	*)
+		# check if --track, --no-track, or --no-guess was specified
+		# if so, disable DWIM mode
+		local flags="--track --no-track --no-guess" track=1
+		if [ -n "$(__git_find_on_cmdline "$flags")" ]; then
+			track=''
+		fi
+		__gitcomp_nl "$(__git_refs '' $track)"
+		;;
+	esac
 }
 
 _git_cherry ()
@@ -826,13 +945,12 @@
 
 _git_cherry_pick ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
 		__gitcomp "--edit --no-commit"
 		;;
 	*)
-		__gitcomp "$(__git_refs)"
+		__gitcomp_nl "$(__git_refs)"
 		;;
 	esac
 }
@@ -841,7 +959,6 @@
 {
 	__git_has_doubledash && return
 
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
 		__gitcomp "--dry-run --quiet"
@@ -853,7 +970,6 @@
 
 _git_clone ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
 		__gitcomp "
@@ -880,12 +996,29 @@
 {
 	__git_has_doubledash && return
 
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
+	--cleanup=*)
+		__gitcomp "default strip verbatim whitespace
+			" "" "${cur##--cleanup=}"
+		return
+		;;
+	--reuse-message=*|--reedit-message=*|\
+	--fixup=*|--squash=*)
+		__gitcomp_nl "$(__git_refs)" "" "${cur#*=}"
+		return
+		;;
+	--untracked-files=*)
+		__gitcomp "all no normal" "" "${cur##--untracked-files=}"
+		return
+		;;
 	--*)
 		__gitcomp "
 			--all --author= --signoff --verify --no-verify
 			--edit --amend --include --only --interactive
+			--dry-run --reuse-message= --reedit-message=
+			--reset-author --file= --message= --template=
+			--cleanup= --untracked-files --untracked-files=
+			--verbose --quiet --fixup= --squash=
 			"
 		return
 	esac
@@ -894,7 +1027,6 @@
 
 _git_describe ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
 		__gitcomp "
@@ -903,7 +1035,7 @@
 			"
 		return
 	esac
-	__gitcomp "$(__git_refs)"
+	__gitcomp_nl "$(__git_refs)"
 }
 
 __git_diff_common_options="--stat --numstat --shortstat --summary
@@ -918,53 +1050,58 @@
 			--inter-hunk-context=
 			--patience
 			--raw
+			--dirstat --dirstat= --dirstat-by-file
+			--dirstat-by-file= --cumulative
 "
 
 _git_diff ()
 {
 	__git_has_doubledash && return
 
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
 		__gitcomp "--cached --staged --pickaxe-all --pickaxe-regex
-			--base --ours --theirs
+			--base --ours --theirs --no-index
 			$__git_diff_common_options
 			"
 		return
 		;;
 	esac
-	__git_complete_file
+	__git_complete_revlist_file
 }
 
 __git_mergetools_common="diffuse ecmerge emerge kdiff3 meld opendiff
-			tkdiff vimdiff gvimdiff xxdiff araxis
+			tkdiff vimdiff gvimdiff xxdiff araxis p4merge bc3
 "
 
 _git_difftool ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
+	__git_has_doubledash && return
+
 	case "$cur" in
 	--tool=*)
 		__gitcomp "$__git_mergetools_common kompare" "" "${cur##--tool=}"
 		return
 		;;
 	--*)
-		__gitcomp "--tool="
+		__gitcomp "--cached --staged --pickaxe-all --pickaxe-regex
+			--base --ours --theirs
+			--no-renames --diff-filter= --find-copies-harder
+			--relative --ignore-submodules
+			--tool="
 		return
 		;;
 	esac
-	COMPREPLY=()
+	__git_complete_file
 }
 
 __git_fetch_options="
 	--quiet --verbose --append --upload-pack --force --keep --depth=
-	--tags --no-tags
+	--tags --no-tags --all --prune --dry-run
 "
 
 _git_fetch ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
 		__gitcomp "$__git_fetch_options"
@@ -976,7 +1113,6 @@
 
 _git_format_patch ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--thread=*)
 		__gitcomp "
@@ -991,7 +1127,7 @@
 			--numbered --start-number
 			--numbered-files
 			--keep-subject
-			--signoff
+			--signoff --signature --no-signature
 			--in-reply-to= --cc=
 			--full-index --binary
 			--not --all
@@ -1008,7 +1144,6 @@
 
 _git_fsck ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
 		__gitcomp "
@@ -1023,7 +1158,6 @@
 
 _git_gc ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
 		__gitcomp "--prune --aggressive"
@@ -1033,49 +1167,68 @@
 	COMPREPLY=()
 }
 
+_git_gitk ()
+{
+	_gitk
+}
+
+__git_match_ctag() {
+	awk "/^${1////\\/}/ { print \$1 }" "$2"
+}
+
 _git_grep ()
 {
 	__git_has_doubledash && return
 
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
 		__gitcomp "
 			--cached
 			--text --ignore-case --word-regexp --invert-match
-			--full-name
+			--full-name --line-number
 			--extended-regexp --basic-regexp --fixed-strings
+			--perl-regexp
 			--files-with-matches --name-only
 			--files-without-match
+			--max-depth
 			--count
 			--and --or --not --all-match
 			"
 		return
 		;;
 	esac
-	COMPREPLY=()
+
+	case "$cword,$prev" in
+	2,*|*,-*)
+		if test -r tags; then
+			__gitcomp_nl "$(__git_match_ctag "$cur" tags)"
+			return
+		fi
+		;;
+	esac
+
+	__gitcomp_nl "$(__git_refs)"
 }
 
 _git_help ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
 		__gitcomp "--all --info --man --web"
 		return
 		;;
 	esac
-	__gitcomp "$(__git_all_commands)
+	__git_compute_all_commands
+	__gitcomp "$__git_all_commands $(__git_aliases)
 		attributes cli core-tutorial cvs-migration
 		diffcore gitk glossary hooks ignore modules
-		repository-layout tutorial tutorial-2
+		namespaces repository-layout tutorial tutorial-2
 		workflows
 		"
 }
 
 _git_init ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--shared=*)
 		__gitcomp "
@@ -1095,7 +1248,6 @@
 {
 	__git_has_doubledash && return
 
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
 		__gitcomp "--cached --deleted --modified --others --ignored
@@ -1113,7 +1265,7 @@
 
 _git_ls_remote ()
 {
-	__gitcomp "$(__git_remotes)"
+	__gitcomp_nl "$(__git_remotes)"
 }
 
 _git_ls_tree ()
@@ -1129,12 +1281,14 @@
 	--max-count=
 	--max-age= --since= --after=
 	--min-age= --until= --before=
+	--min-parents= --max-parents=
+	--no-min-parents --no-max-parents
 "
 # Options that go well for log and gitk (not shortlog)
 __git_log_gitk_options="
 	--dense --sparse --full-history
 	--simplify-merges --simplify-by-decoration
-	--left-right
+	--left-right --notes --no-notes
 "
 # Options that go well for log and shortlog (not gitk)
 __git_log_shortlog_options="
@@ -1149,27 +1303,25 @@
 {
 	__git_has_doubledash && return
 
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	local g="$(git rev-parse --git-dir 2>/dev/null)"
 	local merge=""
 	if [ -f "$g/MERGE_HEAD" ]; then
 		merge="--merge"
 	fi
 	case "$cur" in
-	--pretty=*)
-		__gitcomp "$__git_log_pretty_formats
-			" "" "${cur##--pretty=}"
-		return
-		;;
-	--format=*)
-		__gitcomp "$__git_log_pretty_formats
-			" "" "${cur##--format=}"
+	--pretty=*|--format=*)
+		__gitcomp "$__git_log_pretty_formats $(__git_pretty_aliases)
+			" "" "${cur#*=}"
 		return
 		;;
 	--date=*)
 		__gitcomp "$__git_log_date_formats" "" "${cur##--date=}"
 		return
 		;;
+	--decorate=*)
+		__gitcomp "long short" "" "${cur##--decorate=}"
+		return
+		;;
 	--*)
 		__gitcomp "
 			$__git_log_common_options
@@ -1182,7 +1334,7 @@
 			--pretty= --format= --oneline
 			--cherry-pick
 			--graph
-			--decorate
+			--decorate --decorate=
 			--walk-reflogs
 			--parents --children
 			$merge
@@ -1197,25 +1349,23 @@
 
 __git_merge_options="
 	--no-commit --no-stat --log --no-log --squash --strategy
-	--commit --stat --no-squash --ff --no-ff
+	--commit --stat --no-squash --ff --no-ff --ff-only --edit --no-edit
 "
 
 _git_merge ()
 {
 	__git_complete_strategy && return
 
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
 		__gitcomp "$__git_merge_options"
 		return
 	esac
-	__gitcomp "$(__git_refs)"
+	__gitcomp_nl "$(__git_refs)"
 }
 
 _git_mergetool ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--tool=*)
 		__gitcomp "$__git_mergetools_common tortoisemerge" "" "${cur##--tool=}"
@@ -1231,12 +1381,11 @@
 
 _git_merge_base ()
 {
-	__gitcomp "$(__git_refs)"
+	__gitcomp_nl "$(__git_refs)"
 }
 
 _git_mv ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
 		__gitcomp "--dry-run"
@@ -1251,11 +1400,57 @@
 	__gitcomp "--tags --all --stdin"
 }
 
+_git_notes ()
+{
+	local subcommands='add append copy edit list prune remove show'
+	local subcommand="$(__git_find_on_cmdline "$subcommands")"
+
+	case "$subcommand,$cur" in
+	,--*)
+		__gitcomp '--ref'
+		;;
+	,*)
+		case "$prev" in
+		--ref)
+			__gitcomp_nl "$(__git_refs)"
+			;;
+		*)
+			__gitcomp "$subcommands --ref"
+			;;
+		esac
+		;;
+	add,--reuse-message=*|append,--reuse-message=*|\
+	add,--reedit-message=*|append,--reedit-message=*)
+		__gitcomp_nl "$(__git_refs)" "" "${cur#*=}"
+		;;
+	add,--*|append,--*)
+		__gitcomp '--file= --message= --reedit-message=
+				--reuse-message='
+		;;
+	copy,--*)
+		__gitcomp '--stdin'
+		;;
+	prune,--*)
+		__gitcomp '--dry-run --verbose'
+		;;
+	prune,*)
+		;;
+	*)
+		case "$prev" in
+		-m|-F)
+			;;
+		*)
+			__gitcomp_nl "$(__git_refs)"
+			;;
+		esac
+		;;
+	esac
+}
+
 _git_pull ()
 {
 	__git_complete_strategy && return
 
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
 		__gitcomp "
@@ -1271,21 +1466,20 @@
 
 _git_push ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
-	case "${COMP_WORDS[COMP_CWORD-1]}" in
+	case "$prev" in
 	--repo)
-		__gitcomp "$(__git_remotes)"
+		__gitcomp_nl "$(__git_remotes)"
 		return
 	esac
 	case "$cur" in
 	--repo=*)
-		__gitcomp "$(__git_remotes)" "" "${cur##--repo=}"
+		__gitcomp_nl "$(__git_remotes)" "" "${cur##--repo=}"
 		return
 		;;
 	--*)
 		__gitcomp "
 			--all --mirror --tags --dry-run --force --verbose
-			--receive-pack= --repo=
+			--receive-pack= --repo= --set-upstream
 		"
 		return
 		;;
@@ -1295,18 +1489,41 @@
 
 _git_rebase ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}" dir="$(__gitdir)"
+	local dir="$(__gitdir)"
 	if [ -d "$dir"/rebase-apply ] || [ -d "$dir"/rebase-merge ]; then
 		__gitcomp "--continue --skip --abort"
 		return
 	fi
 	__git_complete_strategy && return
 	case "$cur" in
+	--whitespace=*)
+		__gitcomp "$__git_whitespacelist" "" "${cur##--whitespace=}"
+		return
+		;;
 	--*)
-		__gitcomp "--onto --merge --strategy --interactive"
+		__gitcomp "
+			--onto --merge --strategy --interactive
+			--preserve-merges --stat --no-stat
+			--committer-date-is-author-date --ignore-date
+			--ignore-whitespace --whitespace=
+			--autosquash
+			"
+
 		return
 	esac
-	__gitcomp "$(__git_refs)"
+	__gitcomp_nl "$(__git_refs)"
+}
+
+_git_reflog ()
+{
+	local subcommands="show delete expire"
+	local subcommand="$(__git_find_on_cmdline "$subcommands")"
+
+	if [ -z "$subcommand" ]; then
+		__gitcomp "$subcommands"
+	else
+		__gitcomp_nl "$(__git_refs)"
+	fi
 }
 
 __git_send_email_confirm_options="always never auto cc compose"
@@ -1314,7 +1531,6 @@
 
 _git_send_email ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--confirm=*)
 		__gitcomp "
@@ -1349,11 +1565,16 @@
 	COMPREPLY=()
 }
 
+_git_stage ()
+{
+	_git_add
+}
+
 __git_config_get_set_variables ()
 {
-	local prevword word config_file= c=$COMP_CWORD
+	local prevword word config_file= c=$cword
 	while [ $c -gt 1 ]; do
-		word="${COMP_WORDS[c]}"
+		word="${words[c]}"
 		case "$word" in
 		--global|--system|--file=*)
 			config_file="$word"
@@ -1369,7 +1590,7 @@
 	done
 
 	git --git-dir="$(__gitdir)" config $config_file --list 2>/dev/null |
-	while read line
+	while read -r line
 	do
 		case "$line" in
 		*.*=*)
@@ -1381,33 +1602,36 @@
 
 _git_config ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
-	local prv="${COMP_WORDS[COMP_CWORD-1]}"
-	case "$prv" in
+	case "$prev" in
 	branch.*.remote)
-		__gitcomp "$(__git_remotes)"
+		__gitcomp_nl "$(__git_remotes)"
 		return
 		;;
 	branch.*.merge)
-		__gitcomp "$(__git_refs)"
+		__gitcomp_nl "$(__git_refs)"
 		return
 		;;
 	remote.*.fetch)
-		local remote="${prv#remote.}"
+		local remote="${prev#remote.}"
 		remote="${remote%.fetch}"
-		__gitcomp "$(__git_refs_remotes "$remote")"
+		if [ -z "$cur" ]; then
+			COMPREPLY=("refs/heads/")
+			return
+		fi
+		__gitcomp_nl "$(__git_refs_remotes "$remote")"
 		return
 		;;
 	remote.*.push)
-		local remote="${prv#remote.}"
+		local remote="${prev#remote.}"
 		remote="${remote%.push}"
-		__gitcomp "$(git --git-dir="$(__gitdir)" \
+		__gitcomp_nl "$(git --git-dir="$(__gitdir)" \
 			for-each-ref --format='%(refname):%(refname)' \
 			refs/heads)"
 		return
 		;;
 	pull.twohead|pull.octopus)
-		__gitcomp "$(__git_merge_strategies)"
+		__git_compute_merge_strategies
+		__gitcomp "$__git_merge_strategies"
 		return
 		;;
 	color.branch|color.diff|color.interactive|\
@@ -1447,7 +1671,7 @@
 		return
 		;;
 	--get|--get-all|--unset|--unset-all)
-		__gitcomp "$(__git_config_get_set_variables)"
+		__gitcomp_nl "$(__git_config_get_set_variables)"
 		return
 		;;
 	*.*)
@@ -1467,96 +1691,108 @@
 		return
 		;;
 	branch.*.*)
-		local pfx="${cur%.*}."
-		cur="${cur##*.}"
-		__gitcomp "remote merge mergeoptions rebase" "$pfx" "$cur"
+		local pfx="${cur%.*}." cur_="${cur##*.}"
+		__gitcomp "remote merge mergeoptions rebase" "$pfx" "$cur_"
 		return
 		;;
 	branch.*)
-		local pfx="${cur%.*}."
-		cur="${cur#*.}"
-		__gitcomp "$(__git_heads)" "$pfx" "$cur" "."
+		local pfx="${cur%.*}." cur_="${cur#*.}"
+		__gitcomp_nl "$(__git_heads)" "$pfx" "$cur_" "."
 		return
 		;;
 	guitool.*.*)
-		local pfx="${cur%.*}."
-		cur="${cur##*.}"
+		local pfx="${cur%.*}." cur_="${cur##*.}"
 		__gitcomp "
 			argprompt cmd confirm needsfile noconsole norescan
 			prompt revprompt revunmerged title
-			" "$pfx" "$cur"
+			" "$pfx" "$cur_"
 		return
 		;;
 	difftool.*.*)
-		local pfx="${cur%.*}."
-		cur="${cur##*.}"
-		__gitcomp "cmd path" "$pfx" "$cur"
+		local pfx="${cur%.*}." cur_="${cur##*.}"
+		__gitcomp "cmd path" "$pfx" "$cur_"
 		return
 		;;
 	man.*.*)
-		local pfx="${cur%.*}."
-		cur="${cur##*.}"
-		__gitcomp "cmd path" "$pfx" "$cur"
+		local pfx="${cur%.*}." cur_="${cur##*.}"
+		__gitcomp "cmd path" "$pfx" "$cur_"
 		return
 		;;
 	mergetool.*.*)
-		local pfx="${cur%.*}."
-		cur="${cur##*.}"
-		__gitcomp "cmd path trustExitCode" "$pfx" "$cur"
+		local pfx="${cur%.*}." cur_="${cur##*.}"
+		__gitcomp "cmd path trustExitCode" "$pfx" "$cur_"
 		return
 		;;
 	pager.*)
-		local pfx="${cur%.*}."
-		cur="${cur#*.}"
-		__gitcomp "$(__git_all_commands)" "$pfx" "$cur"
+		local pfx="${cur%.*}." cur_="${cur#*.}"
+		__git_compute_all_commands
+		__gitcomp_nl "$__git_all_commands" "$pfx" "$cur_"
 		return
 		;;
 	remote.*.*)
-		local pfx="${cur%.*}."
-		cur="${cur##*.}"
+		local pfx="${cur%.*}." cur_="${cur##*.}"
 		__gitcomp "
 			url proxy fetch push mirror skipDefaultUpdate
 			receivepack uploadpack tagopt pushurl
-			" "$pfx" "$cur"
+			" "$pfx" "$cur_"
 		return
 		;;
 	remote.*)
-		local pfx="${cur%.*}."
-		cur="${cur#*.}"
-		__gitcomp "$(__git_remotes)" "$pfx" "$cur" "."
+		local pfx="${cur%.*}." cur_="${cur#*.}"
+		__gitcomp_nl "$(__git_remotes)" "$pfx" "$cur_" "."
 		return
 		;;
 	url.*.*)
-		local pfx="${cur%.*}."
-		cur="${cur##*.}"
-		__gitcomp "insteadof" "$pfx" "$cur"
+		local pfx="${cur%.*}." cur_="${cur##*.}"
+		__gitcomp "insteadOf pushInsteadOf" "$pfx" "$cur_"
 		return
 		;;
 	esac
 	__gitcomp "
-		add.ignore-errors
+		add.ignoreErrors
+		advice.commitBeforeMerge
+		advice.detachedHead
+		advice.implicitIdentity
+		advice.pushNonFastForward
+		advice.resolveConflict
+		advice.statusHints
 		alias.
+		am.keepcr
+		apply.ignorewhitespace
 		apply.whitespace
 		branch.autosetupmerge
 		branch.autosetuprebase
+		browser.
 		clean.requireForce
 		color.branch
 		color.branch.current
 		color.branch.local
 		color.branch.plain
 		color.branch.remote
+		color.decorate.HEAD
+		color.decorate.branch
+		color.decorate.remoteBranch
+		color.decorate.stash
+		color.decorate.tag
 		color.diff
 		color.diff.commit
 		color.diff.frag
+		color.diff.func
 		color.diff.meta
 		color.diff.new
 		color.diff.old
 		color.diff.plain
 		color.diff.whitespace
 		color.grep
-		color.grep.external
+		color.grep.context
+		color.grep.filename
+		color.grep.function
+		color.grep.linenumber
 		color.grep.match
+		color.grep.selected
+		color.grep.separator
 		color.interactive
+		color.interactive.error
 		color.interactive.header
 		color.interactive.help
 		color.interactive.prompt
@@ -1570,21 +1806,29 @@
 		color.status.untracked
 		color.status.updated
 		color.ui
+		commit.status
 		commit.template
+		core.abbrev
+		core.askpass
+		core.attributesfile
 		core.autocrlf
 		core.bare
+		core.bigFileThreshold
 		core.compression
 		core.createObject
 		core.deltaBaseCacheLimit
 		core.editor
+		core.eol
 		core.excludesfile
 		core.fileMode
 		core.fsyncobjectfiles
 		core.gitProxy
 		core.ignoreCygwinFSTricks
 		core.ignoreStat
+		core.ignorecase
 		core.logAllRefUpdates
 		core.loosecompression
+		core.notesRef
 		core.packedGitLimit
 		core.packedGitWindowSize
 		core.pager
@@ -1594,32 +1838,39 @@
 		core.repositoryFormatVersion
 		core.safecrlf
 		core.sharedRepository
+		core.sparseCheckout
 		core.symlinks
 		core.trustctime
 		core.warnAmbiguousRefs
 		core.whitespace
 		core.worktree
 		diff.autorefreshindex
+		diff.statGraphWidth
 		diff.external
+		diff.ignoreSubmodules
 		diff.mnemonicprefix
+		diff.noprefix
 		diff.renameLimit
-		diff.renameLimit.
 		diff.renames
 		diff.suppressBlankEmpty
 		diff.tool
 		diff.wordRegex
 		difftool.
 		difftool.prompt
+		fetch.recurseSubmodules
 		fetch.unpackLimit
 		format.attach
 		format.cc
 		format.headers
 		format.numbered
 		format.pretty
+		format.signature
 		format.signoff
 		format.subjectprefix
 		format.suffix
 		format.thread
+		format.to
+		gc.
 		gc.aggressiveWindow
 		gc.auto
 		gc.autopacklimit
@@ -1657,15 +1908,20 @@
 		http.lowSpeedLimit
 		http.lowSpeedTime
 		http.maxRequests
+		http.minSessions
 		http.noEPSV
+		http.postBuffer
 		http.proxy
 		http.sslCAInfo
 		http.sslCAPath
 		http.sslCert
+		http.sslCertPasswordProtected
 		http.sslKey
 		http.sslVerify
+		http.useragent
 		i18n.commitEncoding
 		i18n.logOutputEncoding
+		imap.authMethod
 		imap.folder
 		imap.host
 		imap.pass
@@ -1674,6 +1930,7 @@
 		imap.sslverify
 		imap.tunnel
 		imap.user
+		init.templatedir
 		instaweb.browser
 		instaweb.httpd
 		instaweb.local
@@ -1681,19 +1938,29 @@
 		instaweb.port
 		interactive.singlekey
 		log.date
+		log.decorate
 		log.showroot
 		mailmap.file
 		man.
 		man.viewer
+		merge.
 		merge.conflictstyle
 		merge.log
 		merge.renameLimit
+		merge.renormalize
 		merge.stat
 		merge.tool
 		merge.verbosity
 		mergetool.
 		mergetool.keepBackup
+		mergetool.keepTemporaries
 		mergetool.prompt
+		notes.displayRef
+		notes.rewrite.
+		notes.rewrite.amend
+		notes.rewrite.rebase
+		notes.rewriteMode
+		notes.rewriteRef
 		pack.compression
 		pack.deltaCacheLimit
 		pack.deltaCacheSize
@@ -1704,31 +1971,42 @@
 		pack.window
 		pack.windowMemory
 		pager.
+		pretty.
 		pull.octopus
 		pull.twohead
 		push.default
+		rebase.autosquash
 		rebase.stat
+		receive.autogc
 		receive.denyCurrentBranch
+		receive.denyDeleteCurrent
 		receive.denyDeletes
 		receive.denyNonFastForwards
 		receive.fsckObjects
 		receive.unpackLimit
+		receive.updateserverinfo
+		remotes.
 		repack.usedeltabaseoffset
 		rerere.autoupdate
 		rerere.enabled
+		sendemail.
 		sendemail.aliasesfile
-		sendemail.aliasesfiletype
+		sendemail.aliasfiletype
 		sendemail.bcc
 		sendemail.cc
 		sendemail.cccmd
 		sendemail.chainreplyto
 		sendemail.confirm
 		sendemail.envelopesender
+		sendemail.from
+		sendemail.identity
 		sendemail.multiedit
 		sendemail.signedoffbycc
+		sendemail.smtpdomain
 		sendemail.smtpencryption
 		sendemail.smtppass
 		sendemail.smtpserver
+		sendemail.smtpserveroption
 		sendemail.smtpserverport
 		sendemail.smtpuser
 		sendemail.suppresscc
@@ -1739,6 +2017,8 @@
 		showbranch.default
 		status.relativePaths
 		status.showUntrackedFiles
+		status.submodulesummary
+		submodule.
 		tar.umask
 		transfer.unpackLimit
 		url.
@@ -1752,26 +2032,25 @@
 
 _git_remote ()
 {
-	local subcommands="add rename rm show prune update set-head"
-	local subcommand="$(__git_find_subcommand "$subcommands")"
+	local subcommands="add rename rm set-head set-branches set-url show prune update"
+	local subcommand="$(__git_find_on_cmdline "$subcommands")"
 	if [ -z "$subcommand" ]; then
 		__gitcomp "$subcommands"
 		return
 	fi
 
 	case "$subcommand" in
-	rename|rm|show|prune)
-		__gitcomp "$(__git_remotes)"
+	rename|rm|set-url|show|prune)
+		__gitcomp_nl "$(__git_remotes)"
+		;;
+	set-head|set-branches)
+		__git_complete_remote_or_refspec
 		;;
 	update)
 		local i c='' IFS=$'\n'
-		for i in $(git --git-dir="$(__gitdir)" config --list); do
-			case "$i" in
-			remotes.*)
-				i="${i#remotes.}"
-				c="$c ${i/=*/}"
-				;;
-			esac
+		for i in $(git --git-dir="$(__gitdir)" config --get-regexp "remotes\..*" 2>/dev/null); do
+			i="${i#remotes.}"
+			c="$c ${i/ */}"
 		done
 		__gitcomp "$c"
 		;;
@@ -1781,37 +2060,39 @@
 	esac
 }
 
+_git_replace ()
+{
+	__gitcomp_nl "$(__git_refs)"
+}
+
 _git_reset ()
 {
 	__git_has_doubledash && return
 
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
-		__gitcomp "--merge --mixed --hard --soft"
+		__gitcomp "--merge --mixed --hard --soft --patch"
 		return
 		;;
 	esac
-	__gitcomp "$(__git_refs)"
+	__gitcomp_nl "$(__git_refs)"
 }
 
 _git_revert ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
 		__gitcomp "--edit --mainline --no-edit --no-commit --signoff"
 		return
 		;;
 	esac
-	__gitcomp "$(__git_refs)"
+	__gitcomp_nl "$(__git_refs)"
 }
 
 _git_rm ()
 {
 	__git_has_doubledash && return
 
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
 		__gitcomp "--cached --dry-run --ignore-unmatch --quiet"
@@ -1825,7 +2106,6 @@
 {
 	__git_has_doubledash && return
 
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
 		__gitcomp "
@@ -1843,16 +2123,10 @@
 {
 	__git_has_doubledash && return
 
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
-	--pretty=*)
-		__gitcomp "$__git_log_pretty_formats
-			" "" "${cur##--pretty=}"
-		return
-		;;
-	--format=*)
-		__gitcomp "$__git_log_pretty_formats
-			" "" "${cur##--format=}"
+	--pretty=*|--format=*)
+		__gitcomp "$__git_log_pretty_formats $(__git_pretty_aliases)
+			" "" "${cur#*=}"
 		return
 		;;
 	--*)
@@ -1867,7 +2141,6 @@
 
 _git_show_branch ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
 		__gitcomp "
@@ -1884,24 +2157,35 @@
 
 _git_stash ()
 {
+	local save_opts='--keep-index --no-keep-index --quiet --patch'
 	local subcommands='save list show apply clear drop pop create branch'
-	local subcommand="$(__git_find_subcommand "$subcommands")"
+	local subcommand="$(__git_find_on_cmdline "$subcommands")"
 	if [ -z "$subcommand" ]; then
-		__gitcomp "$subcommands"
+		case "$cur" in
+		--*)
+			__gitcomp "$save_opts"
+			;;
+		*)
+			if [ -z "$(__git_find_on_cmdline "$save_opts")" ]; then
+				__gitcomp "$subcommands"
+			else
+				COMPREPLY=()
+			fi
+			;;
+		esac
 	else
-		local cur="${COMP_WORDS[COMP_CWORD]}"
 		case "$subcommand,$cur" in
 		save,--*)
-			__gitcomp "--keep-index"
+			__gitcomp "$save_opts"
 			;;
 		apply,--*|pop,--*)
-			__gitcomp "--index"
+			__gitcomp "--index --quiet"
 			;;
 		show,--*|drop,--*|branch,--*)
 			COMPREPLY=()
 			;;
 		show,*|apply,*|drop,*|pop,*|branch,*)
-			__gitcomp "$(git --git-dir="$(__gitdir)" stash list \
+			__gitcomp_nl "$(git --git-dir="$(__gitdir)" stash list \
 					| sed -n -e 's/:.*//p')"
 			;;
 		*)
@@ -1916,8 +2200,7 @@
 	__git_has_doubledash && return
 
 	local subcommands="add status init update summary foreach sync"
-	if [ -z "$(__git_find_subcommand "$subcommands")" ]; then
-		local cur="${COMP_WORDS[COMP_CWORD]}"
+	if [ -z "$(__git_find_on_cmdline "$subcommands")" ]; then
 		case "$cur" in
 		--*)
 			__gitcomp "--quiet --cached"
@@ -1936,9 +2219,9 @@
 		init fetch clone rebase dcommit log find-rev
 		set-tree commit-diff info create-ignore propget
 		proplist show-ignore show-externals branch tag blame
-		migrate
+		migrate mkdirs reset gc
 		"
-	local subcommand="$(__git_find_subcommand "$subcommands")"
+	local subcommand="$(__git_find_on_cmdline "$subcommands")"
 	if [ -z "$subcommand" ]; then
 		__gitcomp "$subcommands"
 	else
@@ -1961,7 +2244,6 @@
 			--edit --rmdir --find-copies-harder --copy-similarity=
 			"
 
-		local cur="${COMP_WORDS[COMP_CWORD]}"
 		case "$subcommand,$cur" in
 		fetch,--*)
 			__gitcomp "--revision= --fetch-all $fc_opts"
@@ -1976,14 +2258,14 @@
 			__gitcomp "
 				--merge --strategy= --verbose --dry-run
 				--fetch-all --no-rebase --commit-url
-				--revision $cmt_opts $fc_opts
+				--revision --interactive $cmt_opts $fc_opts
 				"
 			;;
 		set-tree,--*)
 			__gitcomp "--stdin $cmt_opts $fc_opts"
 			;;
 		create-ignore,--*|propget,--*|proplist,--*|show-ignore,--*|\
-		show-externals,--*)
+		show-externals,--*|mkdirs,--*)
 			__gitcomp "--revision="
 			;;
 		log,--*)
@@ -2020,6 +2302,9 @@
 				--no-auth-cache --username=
 				"
 			;;
+		reset,--*)
+			__gitcomp "--revision= --parent"
+			;;
 		*)
 			COMPREPLY=()
 			;;
@@ -2030,55 +2315,61 @@
 _git_tag ()
 {
 	local i c=1 f=0
-	while [ $c -lt $COMP_CWORD ]; do
-		i="${COMP_WORDS[c]}"
+	while [ $c -lt $cword ]; do
+		i="${words[c]}"
 		case "$i" in
 		-d|-v)
-			__gitcomp "$(__git_tags)"
+			__gitcomp_nl "$(__git_tags)"
 			return
 			;;
 		-f)
 			f=1
 			;;
 		esac
-		c=$((++c))
+		((c++))
 	done
 
-	case "${COMP_WORDS[COMP_CWORD-1]}" in
+	case "$prev" in
 	-m|-F)
 		COMPREPLY=()
 		;;
 	-*|tag)
 		if [ $f = 1 ]; then
-			__gitcomp "$(__git_tags)"
+			__gitcomp_nl "$(__git_tags)"
 		else
 			COMPREPLY=()
 		fi
 		;;
 	*)
-		__gitcomp "$(__git_refs)"
+		__gitcomp_nl "$(__git_refs)"
 		;;
 	esac
 }
 
-_git ()
+_git_whatchanged ()
+{
+	_git_log
+}
+
+__git_main ()
 {
 	local i c=1 command __git_dir
 
-	while [ $c -lt $COMP_CWORD ]; do
-		i="${COMP_WORDS[c]}"
+	while [ $c -lt $cword ]; do
+		i="${words[c]}"
 		case "$i" in
 		--git-dir=*) __git_dir="${i#--git-dir=}" ;;
 		--bare)      __git_dir="." ;;
-		--version|-p|--paginate) ;;
 		--help) command="help"; break ;;
+		-c) c=$((++c)) ;;
+		-*) ;;
 		*) command="$i"; break ;;
 		esac
-		c=$((++c))
+		((c++))
 	done
 
 	if [ -z "$command" ]; then
-		case "${COMP_WORDS[COMP_CWORD]}" in
+		case "$cur" in
 		--*)   __gitcomp "
 			--paginate
 			--no-pager
@@ -2086,79 +2377,35 @@
 			--bare
 			--version
 			--exec-path
+			--exec-path=
 			--html-path
+			--info-path
 			--work-tree=
+			--namespace=
+			--no-replace-objects
 			--help
 			"
 			;;
-		*)     __gitcomp "$(__git_porcelain_commands) $(__git_aliases)" ;;
+		*)     __git_compute_porcelain_commands
+		       __gitcomp "$__git_porcelain_commands $(__git_aliases)" ;;
 		esac
 		return
 	fi
 
-	local expansion=$(__git_aliased_command "$command")
-	[ "$expansion" ] && command="$expansion"
+	local completion_func="_git_${command//-/_}"
+	declare -f $completion_func >/dev/null && $completion_func && return
 
-	case "$command" in
-	am)          _git_am ;;
-	add)         _git_add ;;
-	apply)       _git_apply ;;
-	archive)     _git_archive ;;
-	bisect)      _git_bisect ;;
-	bundle)      _git_bundle ;;
-	branch)      _git_branch ;;
-	checkout)    _git_checkout ;;
-	cherry)      _git_cherry ;;
-	cherry-pick) _git_cherry_pick ;;
-	clean)       _git_clean ;;
-	clone)       _git_clone ;;
-	commit)      _git_commit ;;
-	config)      _git_config ;;
-	describe)    _git_describe ;;
-	diff)        _git_diff ;;
-	difftool)    _git_difftool ;;
-	fetch)       _git_fetch ;;
-	format-patch) _git_format_patch ;;
-	fsck)        _git_fsck ;;
-	gc)          _git_gc ;;
-	grep)        _git_grep ;;
-	help)        _git_help ;;
-	init)        _git_init ;;
-	log)         _git_log ;;
-	ls-files)    _git_ls_files ;;
-	ls-remote)   _git_ls_remote ;;
-	ls-tree)     _git_ls_tree ;;
-	merge)       _git_merge;;
-	mergetool)   _git_mergetool;;
-	merge-base)  _git_merge_base ;;
-	mv)          _git_mv ;;
-	name-rev)    _git_name_rev ;;
-	pull)        _git_pull ;;
-	push)        _git_push ;;
-	rebase)      _git_rebase ;;
-	remote)      _git_remote ;;
-	reset)       _git_reset ;;
-	revert)      _git_revert ;;
-	rm)          _git_rm ;;
-	send-email)  _git_send_email ;;
-	shortlog)    _git_shortlog ;;
-	show)        _git_show ;;
-	show-branch) _git_show_branch ;;
-	stash)       _git_stash ;;
-	stage)       _git_add ;;
-	submodule)   _git_submodule ;;
-	svn)         _git_svn ;;
-	tag)         _git_tag ;;
-	whatchanged) _git_log ;;
-	*)           COMPREPLY=() ;;
-	esac
+	local expansion=$(__git_aliased_command "$command")
+	if [ -n "$expansion" ]; then
+		completion_func="_git_${expansion//-/_}"
+		declare -f $completion_func >/dev/null && $completion_func
+	fi
 }
 
-_gitk ()
+__gitk_main ()
 {
 	__git_has_doubledash && return
 
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	local g="$(__gitdir)"
 	local merge=""
 	if [ -f "$g/MERGE_HEAD" ]; then
@@ -2177,16 +2424,55 @@
 	__git_complete_revlist
 }
 
-complete -o bashdefault -o default -o nospace -F _git git 2>/dev/null \
-	|| complete -o default -o nospace -F _git git
-complete -o bashdefault -o default -o nospace -F _gitk gitk 2>/dev/null \
-	|| complete -o default -o nospace -F _gitk gitk
+__git_func_wrap ()
+{
+	if [[ -n ${ZSH_VERSION-} ]]; then
+		emulate -L bash
+		setopt KSH_TYPESET
+
+		# workaround zsh's bug that leaves 'words' as a special
+		# variable in versions < 4.3.12
+		typeset -h words
+
+		# workaround zsh's bug that quotes spaces in the COMPREPLY
+		# array if IFS doesn't contain spaces.
+		typeset -h IFS
+	fi
+	local cur words cword prev
+	_get_comp_words_by_ref -n =: cur words cword prev
+	$1
+}
+
+# Setup completion for certain functions defined above by setting common
+# variables and workarounds.
+# This is NOT a public function; use at your own risk.
+__git_complete ()
+{
+	local wrapper="__git_wrap${2}"
+	eval "$wrapper () { __git_func_wrap $2 ; }"
+	complete -o bashdefault -o default -o nospace -F $wrapper $1 2>/dev/null \
+		|| complete -o default -o nospace -F $wrapper $1
+}
+
+# wrapper for backwards compatibility
+_git ()
+{
+	__git_wrap__git_main
+}
+
+# wrapper for backwards compatibility
+_gitk ()
+{
+	__git_wrap__gitk_main
+}
+
+__git_complete git __git_main
+__git_complete gitk __gitk_main
 
 # The following are necessary only for Cygwin, and only are needed
 # when the user has tab-completed the executable name and consequently
 # included the '.exe' suffix.
 #
 if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then
-complete -o bashdefault -o default -o nospace -F _git git.exe 2>/dev/null \
-	|| complete -o default -o nospace -F _git git.exe
+__git_complete git.exe __git_main
 fi
diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh
new file mode 100644
index 0000000..29b1ec9
--- /dev/null
+++ b/contrib/completion/git-prompt.sh
@@ -0,0 +1,289 @@
+# bash/zsh git prompt support
+#
+# Copyright (C) 2006,2007 Shawn O. Pearce <spearce@spearce.org>
+# Distributed under the GNU General Public License, version 2.0.
+#
+# This script allows you to see the current branch in your prompt.
+#
+# To enable:
+#
+#    1) Copy this file to somewhere (e.g. ~/.git-prompt.sh).
+#    2) Add the following line to your .bashrc/.zshrc:
+#        source ~/.git-prompt.sh
+#    3) Change your PS1 to also show the current branch:
+#         Bash: PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '
+#         ZSH:  PS1='[%n@%m %c$(__git_ps1 " (%s)")]\$ '
+#
+# The argument to __git_ps1 will be displayed only if you are currently
+# in a git repository.  The %s token will be the name of the current
+# branch.
+#
+# In addition, if you set GIT_PS1_SHOWDIRTYSTATE to a nonempty value,
+# unstaged (*) and staged (+) changes will be shown next to the branch
+# name.  You can configure this per-repository with the
+# bash.showDirtyState variable, which defaults to true once
+# GIT_PS1_SHOWDIRTYSTATE is enabled.
+#
+# You can also see if currently something is stashed, by setting
+# GIT_PS1_SHOWSTASHSTATE to a nonempty value. If something is stashed,
+# then a '$' will be shown next to the branch name.
+#
+# If you would like to see if there're untracked files, then you can set
+# GIT_PS1_SHOWUNTRACKEDFILES to a nonempty value. If there're untracked
+# files, then a '%' will be shown next to the branch name.
+#
+# If you would like to see the difference between HEAD and its upstream,
+# set GIT_PS1_SHOWUPSTREAM="auto".  A "<" indicates you are behind, ">"
+# indicates you are ahead, and "<>" indicates you have diverged.  You
+# can further control behaviour by setting GIT_PS1_SHOWUPSTREAM to a
+# space-separated list of values:
+#
+#     verbose       show number of commits ahead/behind (+/-) upstream
+#     legacy        don't use the '--count' option available in recent
+#                   versions of git-rev-list
+#     git           always compare HEAD to @{upstream}
+#     svn           always compare HEAD to your SVN upstream
+#
+# By default, __git_ps1 will compare HEAD to your SVN upstream if it can
+# find one, or @{upstream} otherwise.  Once you have set
+# GIT_PS1_SHOWUPSTREAM, you can override it on a per-repository basis by
+# setting the bash.showUpstream config variable.
+
+# __gitdir accepts 0 or 1 arguments (i.e., location)
+# returns location of .git repo
+__gitdir ()
+{
+	# Note: this function is duplicated in git-completion.bash
+	# When updating it, make sure you update the other one to match.
+	if [ -z "${1-}" ]; then
+		if [ -n "${__git_dir-}" ]; then
+			echo "$__git_dir"
+		elif [ -n "${GIT_DIR-}" ]; then
+			test -d "${GIT_DIR-}" || return 1
+			echo "$GIT_DIR"
+		elif [ -d .git ]; then
+			echo .git
+		else
+			git rev-parse --git-dir 2>/dev/null
+		fi
+	elif [ -d "$1/.git" ]; then
+		echo "$1/.git"
+	else
+		echo "$1"
+	fi
+}
+
+# stores the divergence from upstream in $p
+# used by GIT_PS1_SHOWUPSTREAM
+__git_ps1_show_upstream ()
+{
+	local key value
+	local svn_remote svn_url_pattern count n
+	local upstream=git legacy="" verbose=""
+
+	svn_remote=()
+	# get some config options from git-config
+	local output="$(git config -z --get-regexp '^(svn-remote\..*\.url|bash\.showupstream)$' 2>/dev/null | tr '\0\n' '\n ')"
+	while read -r key value; do
+		case "$key" in
+		bash.showupstream)
+			GIT_PS1_SHOWUPSTREAM="$value"
+			if [[ -z "${GIT_PS1_SHOWUPSTREAM}" ]]; then
+				p=""
+				return
+			fi
+			;;
+		svn-remote.*.url)
+			svn_remote[ $((${#svn_remote[@]} + 1)) ]="$value"
+			svn_url_pattern+="\\|$value"
+			upstream=svn+git # default upstream is SVN if available, else git
+			;;
+		esac
+	done <<< "$output"
+
+	# parse configuration values
+	for option in ${GIT_PS1_SHOWUPSTREAM}; do
+		case "$option" in
+		git|svn) upstream="$option" ;;
+		verbose) verbose=1 ;;
+		legacy)  legacy=1  ;;
+		esac
+	done
+
+	# Find our upstream
+	case "$upstream" in
+	git)    upstream="@{upstream}" ;;
+	svn*)
+		# get the upstream from the "git-svn-id: ..." in a commit message
+		# (git-svn uses essentially the same procedure internally)
+		local svn_upstream=($(git log --first-parent -1 \
+					--grep="^git-svn-id: \(${svn_url_pattern#??}\)" 2>/dev/null))
+		if [[ 0 -ne ${#svn_upstream[@]} ]]; then
+			svn_upstream=${svn_upstream[ ${#svn_upstream[@]} - 2 ]}
+			svn_upstream=${svn_upstream%@*}
+			local n_stop="${#svn_remote[@]}"
+			for ((n=1; n <= n_stop; n++)); do
+				svn_upstream=${svn_upstream#${svn_remote[$n]}}
+			done
+
+			if [[ -z "$svn_upstream" ]]; then
+				# default branch name for checkouts with no layout:
+				upstream=${GIT_SVN_ID:-git-svn}
+			else
+				upstream=${svn_upstream#/}
+			fi
+		elif [[ "svn+git" = "$upstream" ]]; then
+			upstream="@{upstream}"
+		fi
+		;;
+	esac
+
+	# Find how many commits we are ahead/behind our upstream
+	if [[ -z "$legacy" ]]; then
+		count="$(git rev-list --count --left-right \
+				"$upstream"...HEAD 2>/dev/null)"
+	else
+		# produce equivalent output to --count for older versions of git
+		local commits
+		if commits="$(git rev-list --left-right "$upstream"...HEAD 2>/dev/null)"
+		then
+			local commit behind=0 ahead=0
+			for commit in $commits
+			do
+				case "$commit" in
+				"<"*) ((behind++)) ;;
+				*)    ((ahead++))  ;;
+				esac
+			done
+			count="$behind	$ahead"
+		else
+			count=""
+		fi
+	fi
+
+	# calculate the result
+	if [[ -z "$verbose" ]]; then
+		case "$count" in
+		"") # no upstream
+			p="" ;;
+		"0	0") # equal to upstream
+			p="=" ;;
+		"0	"*) # ahead of upstream
+			p=">" ;;
+		*"	0") # behind upstream
+			p="<" ;;
+		*)	    # diverged from upstream
+			p="<>" ;;
+		esac
+	else
+		case "$count" in
+		"") # no upstream
+			p="" ;;
+		"0	0") # equal to upstream
+			p=" u=" ;;
+		"0	"*) # ahead of upstream
+			p=" u+${count#0	}" ;;
+		*"	0") # behind upstream
+			p=" u-${count%	0}" ;;
+		*)	    # diverged from upstream
+			p=" u+${count#*	}-${count%	*}" ;;
+		esac
+	fi
+
+}
+
+
+# __git_ps1 accepts 0 or 1 arguments (i.e., format string)
+# returns text to add to bash PS1 prompt (includes branch name)
+__git_ps1 ()
+{
+	local g="$(__gitdir)"
+	if [ -n "$g" ]; then
+		local r=""
+		local b=""
+		if [ -f "$g/rebase-merge/interactive" ]; then
+			r="|REBASE-i"
+			b="$(cat "$g/rebase-merge/head-name")"
+		elif [ -d "$g/rebase-merge" ]; then
+			r="|REBASE-m"
+			b="$(cat "$g/rebase-merge/head-name")"
+		else
+			if [ -d "$g/rebase-apply" ]; then
+				if [ -f "$g/rebase-apply/rebasing" ]; then
+					r="|REBASE"
+				elif [ -f "$g/rebase-apply/applying" ]; then
+					r="|AM"
+				else
+					r="|AM/REBASE"
+				fi
+			elif [ -f "$g/MERGE_HEAD" ]; then
+				r="|MERGING"
+			elif [ -f "$g/CHERRY_PICK_HEAD" ]; then
+				r="|CHERRY-PICKING"
+			elif [ -f "$g/BISECT_LOG" ]; then
+				r="|BISECTING"
+			fi
+
+			b="$(git symbolic-ref HEAD 2>/dev/null)" || {
+
+				b="$(
+				case "${GIT_PS1_DESCRIBE_STYLE-}" in
+				(contains)
+					git describe --contains HEAD ;;
+				(branch)
+					git describe --contains --all HEAD ;;
+				(describe)
+					git describe HEAD ;;
+				(* | default)
+					git describe --tags --exact-match HEAD ;;
+				esac 2>/dev/null)" ||
+
+				b="$(cut -c1-7 "$g/HEAD" 2>/dev/null)..." ||
+				b="unknown"
+				b="($b)"
+			}
+		fi
+
+		local w=""
+		local i=""
+		local s=""
+		local u=""
+		local c=""
+		local p=""
+
+		if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then
+			if [ "true" = "$(git rev-parse --is-bare-repository 2>/dev/null)" ]; then
+				c="BARE:"
+			else
+				b="GIT_DIR!"
+			fi
+		elif [ "true" = "$(git rev-parse --is-inside-work-tree 2>/dev/null)" ]; then
+			if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ]; then
+				if [ "$(git config --bool bash.showDirtyState)" != "false" ]; then
+					git diff --no-ext-diff --quiet --exit-code || w="*"
+					if git rev-parse --quiet --verify HEAD >/dev/null; then
+						git diff-index --cached --quiet HEAD -- || i="+"
+					else
+						i="#"
+					fi
+				fi
+			fi
+			if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ]; then
+				git rev-parse --verify refs/stash >/dev/null 2>&1 && s="$"
+			fi
+
+			if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ]; then
+				if [ -n "$(git ls-files --others --exclude-standard)" ]; then
+					u="%"
+				fi
+			fi
+
+			if [ -n "${GIT_PS1_SHOWUPSTREAM-}" ]; then
+				__git_ps1_show_upstream
+			fi
+		fi
+
+		local f="$w$i$s$u"
+		printf -- "${1:- (%s)}" "$c${b##refs/heads/}${f:+ $f}$r$p"
+	fi
+}
diff --git a/contrib/convert-objects/git-convert-objects.txt b/contrib/convert-objects/git-convert-objects.txt
index 9718abf..0565d83 100644
--- a/contrib/convert-objects/git-convert-objects.txt
+++ b/contrib/convert-objects/git-convert-objects.txt
@@ -8,6 +8,7 @@
 
 SYNOPSIS
 --------
+[verse]
 'git-convert-objects'
 
 DESCRIPTION
diff --git a/contrib/credential/osxkeychain/.gitignore b/contrib/credential/osxkeychain/.gitignore
new file mode 100644
index 0000000..6c5b702
--- /dev/null
+++ b/contrib/credential/osxkeychain/.gitignore
@@ -0,0 +1 @@
+git-credential-osxkeychain
diff --git a/contrib/credential/osxkeychain/Makefile b/contrib/credential/osxkeychain/Makefile
new file mode 100644
index 0000000..4b3a08a
--- /dev/null
+++ b/contrib/credential/osxkeychain/Makefile
@@ -0,0 +1,17 @@
+all:: git-credential-osxkeychain
+
+CC = gcc
+RM = rm -f
+CFLAGS = -g -O2 -Wall
+
+-include ../../../config.mak.autogen
+-include ../../../config.mak
+
+git-credential-osxkeychain: git-credential-osxkeychain.o
+	$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) -Wl,-framework -Wl,Security
+
+git-credential-osxkeychain.o: git-credential-osxkeychain.c
+	$(CC) -c $(CFLAGS) $<
+
+clean:
+	$(RM) git-credential-osxkeychain git-credential-osxkeychain.o
diff --git a/contrib/credential/osxkeychain/git-credential-osxkeychain.c b/contrib/credential/osxkeychain/git-credential-osxkeychain.c
new file mode 100644
index 0000000..6beed12
--- /dev/null
+++ b/contrib/credential/osxkeychain/git-credential-osxkeychain.c
@@ -0,0 +1,173 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <Security/Security.h>
+
+static SecProtocolType protocol;
+static char *host;
+static char *path;
+static char *username;
+static char *password;
+static UInt16 port;
+
+static void die(const char *err, ...)
+{
+	char msg[4096];
+	va_list params;
+	va_start(params, err);
+	vsnprintf(msg, sizeof(msg), err, params);
+	fprintf(stderr, "%s\n", msg);
+	va_end(params);
+	exit(1);
+}
+
+static void *xstrdup(const char *s1)
+{
+	void *ret = strdup(s1);
+	if (!ret)
+		die("Out of memory");
+	return ret;
+}
+
+#define KEYCHAIN_ITEM(x) (x ? strlen(x) : 0), x
+#define KEYCHAIN_ARGS \
+	NULL, /* default keychain */ \
+	KEYCHAIN_ITEM(host), \
+	0, NULL, /* account domain */ \
+	KEYCHAIN_ITEM(username), \
+	KEYCHAIN_ITEM(path), \
+	port, \
+	protocol, \
+	kSecAuthenticationTypeDefault
+
+static void write_item(const char *what, const char *buf, int len)
+{
+	printf("%s=", what);
+	fwrite(buf, 1, len, stdout);
+	putchar('\n');
+}
+
+static void find_username_in_item(SecKeychainItemRef item)
+{
+	SecKeychainAttributeList list;
+	SecKeychainAttribute attr;
+
+	list.count = 1;
+	list.attr = &attr;
+	attr.tag = kSecAccountItemAttr;
+
+	if (SecKeychainItemCopyContent(item, NULL, &list, NULL, NULL))
+		return;
+
+	write_item("username", attr.data, attr.length);
+	SecKeychainItemFreeContent(&list, NULL);
+}
+
+static void find_internet_password(void)
+{
+	void *buf;
+	UInt32 len;
+	SecKeychainItemRef item;
+
+	if (SecKeychainFindInternetPassword(KEYCHAIN_ARGS, &len, &buf, &item))
+		return;
+
+	write_item("password", buf, len);
+	if (!username)
+		find_username_in_item(item);
+
+	SecKeychainItemFreeContent(NULL, buf);
+}
+
+static void delete_internet_password(void)
+{
+	SecKeychainItemRef item;
+
+	/*
+	 * Require at least a protocol and host for removal, which is what git
+	 * will give us; if you want to do something more fancy, use the
+	 * Keychain manager.
+	 */
+	if (!protocol || !host)
+		return;
+
+	if (SecKeychainFindInternetPassword(KEYCHAIN_ARGS, 0, NULL, &item))
+		return;
+
+	SecKeychainItemDelete(item);
+}
+
+static void add_internet_password(void)
+{
+	/* Only store complete credentials */
+	if (!protocol || !host || !username || !password)
+		return;
+
+	if (SecKeychainAddInternetPassword(
+	      KEYCHAIN_ARGS,
+	      KEYCHAIN_ITEM(password),
+	      NULL))
+		return;
+}
+
+static void read_credential(void)
+{
+	char buf[1024];
+
+	while (fgets(buf, sizeof(buf), stdin)) {
+		char *v;
+
+		if (!strcmp(buf, "\n"))
+			break;
+		buf[strlen(buf)-1] = '\0';
+
+		v = strchr(buf, '=');
+		if (!v)
+			die("bad input: %s", buf);
+		*v++ = '\0';
+
+		if (!strcmp(buf, "protocol")) {
+			if (!strcmp(v, "https"))
+				protocol = kSecProtocolTypeHTTPS;
+			else if (!strcmp(v, "http"))
+				protocol = kSecProtocolTypeHTTP;
+			else /* we don't yet handle other protocols */
+				exit(0);
+		}
+		else if (!strcmp(buf, "host")) {
+			char *colon = strchr(v, ':');
+			if (colon) {
+				*colon++ = '\0';
+				port = atoi(colon);
+			}
+			host = xstrdup(v);
+		}
+		else if (!strcmp(buf, "path"))
+			path = xstrdup(v);
+		else if (!strcmp(buf, "username"))
+			username = xstrdup(v);
+		else if (!strcmp(buf, "password"))
+			password = xstrdup(v);
+	}
+}
+
+int main(int argc, const char **argv)
+{
+	const char *usage =
+		"Usage: git credential-osxkeychain <get|store|erase>";
+
+	if (!argv[1])
+		die(usage);
+
+	read_credential();
+
+	if (!strcmp(argv[1], "get"))
+		find_internet_password();
+	else if (!strcmp(argv[1], "store"))
+		add_internet_password();
+	else if (!strcmp(argv[1], "erase"))
+		delete_internet_password();
+	/* otherwise, ignore unknown action */
+
+	return 0;
+}
diff --git a/contrib/diff-highlight/README b/contrib/diff-highlight/README
new file mode 100644
index 0000000..502e03b
--- /dev/null
+++ b/contrib/diff-highlight/README
@@ -0,0 +1,152 @@
+diff-highlight
+==============
+
+Line oriented diffs are great for reviewing code, because for most
+hunks, you want to see the old and the new segments of code next to each
+other. Sometimes, though, when an old line and a new line are very
+similar, it's hard to immediately see the difference.
+
+You can use "--color-words" to highlight only the changed portions of
+lines. However, this can often be hard to read for code, as it loses
+the line structure, and you end up with oddly formatted bits.
+
+Instead, this script post-processes the line-oriented diff, finds pairs
+of lines, and highlights the differing segments.  It's currently very
+simple and stupid about doing these tasks. In particular:
+
+  1. It will only highlight hunks in which the number of removed and
+     added lines is the same, and it will pair lines within the hunk by
+     position (so the first removed line is compared to the first added
+     line, and so forth). This is simple and tends to work well in
+     practice. More complex changes don't highlight well, so we tend to
+     exclude them due to the "same number of removed and added lines"
+     restriction. Or even if we do try to highlight them, they end up
+     not highlighting because of our "don't highlight if the whole line
+     would be highlighted" rule.
+
+  2. It will find the common prefix and suffix of two lines, and
+     consider everything in the middle to be "different". It could
+     instead do a real diff of the characters between the two lines and
+     find common subsequences. However, the point of the highlight is to
+     call attention to a certain area. Even if some small subset of the
+     highlighted area actually didn't change, that's OK. In practice it
+     ends up being more readable to just have a single blob on the line
+     showing the interesting bit.
+
+The goal of the script is therefore not to be exact about highlighting
+changes, but to call attention to areas of interest without being
+visually distracting.  Non-diff lines and existing diff coloration is
+preserved; the intent is that the output should look exactly the same as
+the input, except for the occasional highlight.
+
+Use
+---
+
+You can try out the diff-highlight program with:
+
+---------------------------------------------
+git log -p --color | /path/to/diff-highlight
+---------------------------------------------
+
+If you want to use it all the time, drop it in your $PATH and put the
+following in your git configuration:
+
+---------------------------------------------
+[pager]
+	log = diff-highlight | less
+	show = diff-highlight | less
+	diff = diff-highlight | less
+---------------------------------------------
+
+Bugs
+----
+
+Because diff-highlight relies on heuristics to guess which parts of
+changes are important, there are some cases where the highlighting is
+more distracting than useful. Fortunately, these cases are rare in
+practice, and when they do occur, the worst case is simply a little
+extra highlighting. This section documents some cases known to be
+sub-optimal, in case somebody feels like working on improving the
+heuristics.
+
+1. Two changes on the same line get highlighted in a blob. For example,
+   highlighting:
+
+----------------------------------------------
+-foo(buf, size);
++foo(obj->buf, obj->size);
+----------------------------------------------
+
+   yields (where the inside of "+{}" would be highlighted):
+
+----------------------------------------------
+-foo(buf, size);
++foo(+{obj->buf, obj->}size);
+----------------------------------------------
+
+   whereas a more semantically meaningful output would be:
+
+----------------------------------------------
+-foo(buf, size);
++foo(+{obj->}buf, +{obj->}size);
+----------------------------------------------
+
+   Note that doing this right would probably involve a set of
+   content-specific boundary patterns, similar to word-diff. Otherwise
+   you get junk like:
+
+-----------------------------------------------------
+-this line has some -{i}nt-{ere}sti-{ng} text on it
++this line has some +{fa}nt+{a}sti+{c} text on it
+-----------------------------------------------------
+
+   which is less readable than the current output.
+
+2. The multi-line matching assumes that lines in the pre- and post-image
+   match by position. This is often the case, but can be fooled when a
+   line is removed from the top and a new one added at the bottom (or
+   vice versa). Unless the lines in the middle are also changed, diffs
+   will show this as two hunks, and it will not get highlighted at all
+   (which is good). But if the lines in the middle are changed, the
+   highlighting can be misleading. Here's a pathological case:
+
+-----------------------------------------------------
+-one
+-two
+-three
+-four
++two 2
++three 3
++four 4
++five 5
+-----------------------------------------------------
+
+   which gets highlighted as:
+
+-----------------------------------------------------
+-one
+-t-{wo}
+-three
+-f-{our}
++two 2
++t+{hree 3}
++four 4
++f+{ive 5}
+-----------------------------------------------------
+
+   because it matches "two" to "three 3", and so forth. It would be
+   nicer as:
+
+-----------------------------------------------------
+-one
+-two
+-three
+-four
++two +{2}
++three +{3}
++four +{4}
++five 5
+-----------------------------------------------------
+
+   which would probably involve pre-matching the lines into pairs
+   according to some heuristic.
diff --git a/contrib/diff-highlight/diff-highlight b/contrib/diff-highlight/diff-highlight
new file mode 100755
index 0000000..c4404d4
--- /dev/null
+++ b/contrib/diff-highlight/diff-highlight
@@ -0,0 +1,173 @@
+#!/usr/bin/perl
+
+use warnings FATAL => 'all';
+use strict;
+
+# Highlight by reversing foreground and background. You could do
+# other things like bold or underline if you prefer.
+my $HIGHLIGHT   = "\x1b[7m";
+my $UNHIGHLIGHT = "\x1b[27m";
+my $COLOR = qr/\x1b\[[0-9;]*m/;
+my $BORING = qr/$COLOR|\s/;
+
+my @removed;
+my @added;
+my $in_hunk;
+
+while (<>) {
+	if (!$in_hunk) {
+		print;
+		$in_hunk = /^$COLOR*\@/;
+	}
+	elsif (/^$COLOR*-/) {
+		push @removed, $_;
+	}
+	elsif (/^$COLOR*\+/) {
+		push @added, $_;
+	}
+	else {
+		show_hunk(\@removed, \@added);
+		@removed = ();
+		@added = ();
+
+		print;
+		$in_hunk = /^$COLOR*[\@ ]/;
+	}
+
+	# Most of the time there is enough output to keep things streaming,
+	# but for something like "git log -Sfoo", you can get one early
+	# commit and then many seconds of nothing. We want to show
+	# that one commit as soon as possible.
+	#
+	# Since we can receive arbitrary input, there's no optimal
+	# place to flush. Flushing on a blank line is a heuristic that
+	# happens to match git-log output.
+	if (!length) {
+		local $| = 1;
+	}
+}
+
+# Flush any queued hunk (this can happen when there is no trailing context in
+# the final diff of the input).
+show_hunk(\@removed, \@added);
+
+exit 0;
+
+sub show_hunk {
+	my ($a, $b) = @_;
+
+	# If one side is empty, then there is nothing to compare or highlight.
+	if (!@$a || !@$b) {
+		print @$a, @$b;
+		return;
+	}
+
+	# If we have mismatched numbers of lines on each side, we could try to
+	# be clever and match up similar lines. But for now we are simple and
+	# stupid, and only handle multi-line hunks that remove and add the same
+	# number of lines.
+	if (@$a != @$b) {
+		print @$a, @$b;
+		return;
+	}
+
+	my @queue;
+	for (my $i = 0; $i < @$a; $i++) {
+		my ($rm, $add) = highlight_pair($a->[$i], $b->[$i]);
+		print $rm;
+		push @queue, $add;
+	}
+	print @queue;
+}
+
+sub highlight_pair {
+	my @a = split_line(shift);
+	my @b = split_line(shift);
+
+	# Find common prefix, taking care to skip any ansi
+	# color codes.
+	my $seen_plusminus;
+	my ($pa, $pb) = (0, 0);
+	while ($pa < @a && $pb < @b) {
+		if ($a[$pa] =~ /$COLOR/) {
+			$pa++;
+		}
+		elsif ($b[$pb] =~ /$COLOR/) {
+			$pb++;
+		}
+		elsif ($a[$pa] eq $b[$pb]) {
+			$pa++;
+			$pb++;
+		}
+		elsif (!$seen_plusminus && $a[$pa] eq '-' && $b[$pb] eq '+') {
+			$seen_plusminus = 1;
+			$pa++;
+			$pb++;
+		}
+		else {
+			last;
+		}
+	}
+
+	# Find common suffix, ignoring colors.
+	my ($sa, $sb) = ($#a, $#b);
+	while ($sa >= $pa && $sb >= $pb) {
+		if ($a[$sa] =~ /$COLOR/) {
+			$sa--;
+		}
+		elsif ($b[$sb] =~ /$COLOR/) {
+			$sb--;
+		}
+		elsif ($a[$sa] eq $b[$sb]) {
+			$sa--;
+			$sb--;
+		}
+		else {
+			last;
+		}
+	}
+
+	if (is_pair_interesting(\@a, $pa, $sa, \@b, $pb, $sb)) {
+		return highlight_line(\@a, $pa, $sa),
+		       highlight_line(\@b, $pb, $sb);
+	}
+	else {
+		return join('', @a),
+		       join('', @b);
+	}
+}
+
+sub split_line {
+	local $_ = shift;
+	return map { /$COLOR/ ? $_ : (split //) }
+	       split /($COLOR*)/;
+}
+
+sub highlight_line {
+	my ($line, $prefix, $suffix) = @_;
+
+	return join('',
+		@{$line}[0..($prefix-1)],
+		$HIGHLIGHT,
+		@{$line}[$prefix..$suffix],
+		$UNHIGHLIGHT,
+		@{$line}[($suffix+1)..$#$line]
+	);
+}
+
+# Pairs are interesting to highlight only if we are going to end up
+# highlighting a subset (i.e., not the whole line). Otherwise, the highlighting
+# is just useless noise. We can detect this by finding either a matching prefix
+# or suffix (disregarding boring bits like whitespace and colorization).
+sub is_pair_interesting {
+	my ($a, $pa, $sa, $b, $pb, $sb) = @_;
+	my $prefix_a = join('', @$a[0..($pa-1)]);
+	my $prefix_b = join('', @$b[0..($pb-1)]);
+	my $suffix_a = join('', @$a[($sa+1)..$#$a]);
+	my $suffix_b = join('', @$b[($sb+1)..$#$b]);
+
+	return $prefix_a !~ /^$COLOR*-$BORING*$/ ||
+	       $prefix_b !~ /^$COLOR*\+$BORING*$/ ||
+	       $suffix_a !~ /^$BORING*$/ ||
+	       $suffix_b !~ /^$BORING*$/;
+}
diff --git a/contrib/diffall/README b/contrib/diffall/README
new file mode 100644
index 0000000..507f17d
--- /dev/null
+++ b/contrib/diffall/README
@@ -0,0 +1,31 @@
+The git-diffall script provides a directory based diff mechanism
+for git.
+
+To determine what diff viewer is used, the script requires either
+the 'diff.tool' or 'merge.tool' configuration option to be set.
+
+This script is compatible with most common forms used to specify a
+range of revisions to diff:
+
+  1. git diffall: shows diff between working tree and staged changes
+  2. git diffall --cached [<commit>]: shows diff between staged
+     changes and HEAD (or other named commit)
+  3. git diffall <commit>: shows diff between working tree and named
+     commit
+  4. git diffall <commit> <commit>: show diff between two named commits
+  5. git diffall <commit>..<commit>: same as above
+  6. git diffall <commit>...<commit>: show the changes on the branch
+     containing and up to the second, starting at a common ancestor
+     of both <commit>
+
+Note: all forms take an optional path limiter [-- <path>*]
+
+The '--extcmd=<command>' option allows the user to specify a custom
+command for viewing diffs.  When given, configured defaults are
+ignored and the script runs $command $LOCAL $REMOTE.  Additionally,
+$BASE is set in the environment.
+
+This script is based on an example provided by Thomas Rast on the
+Git list [1]:
+
+[1] http://thread.gmane.org/gmane.comp.version-control.git/124807
diff --git a/contrib/diffall/git-diffall b/contrib/diffall/git-diffall
new file mode 100755
index 0000000..84f2b65
--- /dev/null
+++ b/contrib/diffall/git-diffall
@@ -0,0 +1,257 @@
+#!/bin/sh
+# Copyright 2010 - 2012, Tim Henigan <tim.henigan@gmail.com>
+#
+# Perform a directory diff between commits in the repository using
+# the external diff or merge tool specified in the user's config.
+
+USAGE='[--cached] [--copy-back] [-x|--extcmd=<command>] <commit>{0,2} [-- <path>*]
+
+    --cached     Compare to the index rather than the working tree.
+
+    --copy-back  Copy files back to the working tree when the diff
+                 tool exits (in case they were modified by the
+                 user).  This option is only valid if the diff
+                 compared with the working tree.
+
+    -x=<command>
+    --extcmd=<command>  Specify a custom command for viewing diffs.
+                 git-diffall ignores the configured defaults and
+                 runs $command $LOCAL $REMOTE when this option is
+                 specified. Additionally, $BASE is set in the
+                 environment.
+'
+
+SUBDIRECTORY_OK=1
+. "$(git --exec-path)/git-sh-setup"
+
+TOOL_MODE=diff
+. "$(git --exec-path)/git-mergetool--lib"
+
+merge_tool="$(get_merge_tool)"
+if test -z "$merge_tool"
+then
+	echo "Error: Either the 'diff.tool' or 'merge.tool' option must be set."
+	usage
+fi
+
+start_dir=$(pwd)
+
+# All the file paths returned by the diff command are relative to the root
+# of the working copy. So if the script is called from a subdirectory, it
+# must switch to the root of working copy before trying to use those paths.
+cdup=$(git rev-parse --show-cdup) &&
+cd "$cdup" || {
+	echo >&2 "Cannot chdir to $cdup, the toplevel of the working tree"
+	exit 1
+}
+
+# set up temp dir
+tmp=$(perl -e 'use File::Temp qw(tempdir);
+	$t=tempdir("/tmp/git-diffall.XXXXX") or exit(1);
+	print $t') || exit 1
+trap 'rm -rf "$tmp"' EXIT
+
+left=
+right=
+paths=
+dashdash_seen=
+compare_staged=
+merge_base=
+left_dir=
+right_dir=
+diff_tool=
+copy_back=
+
+while test $# != 0
+do
+	case "$1" in
+	-h|--h|--he|--hel|--help)
+		usage
+		;;
+	--cached)
+		compare_staged=1
+		;;
+	--copy-back)
+		copy_back=1
+		;;
+	-x|--e|--ex|--ext|--extc|--extcm|--extcmd)
+		if test $# = 1
+		then
+			echo You must specify the tool for use with --extcmd
+			usage
+		else
+			diff_tool=$2
+			shift
+		fi
+		;;
+	--)
+		dashdash_seen=1
+		;;
+	-*)
+		echo Invalid option: "$1"
+		usage
+		;;
+	*)
+		# could be commit, commit range or path limiter
+		case "$1" in
+		*...*)
+			left=${1%...*}
+			right=${1#*...}
+			merge_base=1
+			;;
+		*..*)
+			left=${1%..*}
+			right=${1#*..}
+			;;
+		*)
+			if test -n "$dashdash_seen"
+			then
+				paths="$paths$1 "
+			elif test -z "$left"
+			then
+				left=$1
+			elif test -z "$right"
+			then
+				right=$1
+			else
+				paths="$paths$1 "
+			fi
+			;;
+		esac
+		;;
+	esac
+	shift
+done
+
+# Determine the set of files which changed
+if test -n "$left" && test -n "$right"
+then
+	left_dir="cmt-$(git rev-parse --short $left)"
+	right_dir="cmt-$(git rev-parse --short $right)"
+
+	if test -n "$compare_staged"
+	then
+		usage
+	elif test -n "$merge_base"
+	then
+		git diff --name-only "$left"..."$right" -- $paths >"$tmp/filelist"
+	else
+		git diff --name-only "$left" "$right" -- $paths >"$tmp/filelist"
+	fi
+elif test -n "$left"
+then
+	left_dir="cmt-$(git rev-parse --short $left)"
+
+	if test -n "$compare_staged"
+	then
+		right_dir="staged"
+		git diff --name-only --cached "$left" -- $paths >"$tmp/filelist"
+	else
+		right_dir="working_tree"
+		git diff --name-only "$left" -- $paths >"$tmp/filelist"
+	fi
+else
+	left_dir="HEAD"
+
+	if test -n "$compare_staged"
+	then
+		right_dir="staged"
+		git diff --name-only --cached -- $paths >"$tmp/filelist"
+	else
+		right_dir="working_tree"
+		git diff --name-only -- $paths >"$tmp/filelist"
+	fi
+fi
+
+# Exit immediately if there are no diffs
+if test ! -s "$tmp/filelist"
+then
+	exit 0
+fi
+
+if test -n "$copy_back" && test "$right_dir" != "working_tree"
+then
+	echo "--copy-back is only valid when diff includes the working tree."
+	exit 1
+fi
+
+# Create the named tmp directories that will hold the files to be compared
+mkdir -p "$tmp/$left_dir" "$tmp/$right_dir"
+
+# Populate the tmp/right_dir directory with the files to be compared
+while read name
+do
+	if test -n "$right"
+	then
+		ls_list=$(git ls-tree $right "$name")
+		if test -n "$ls_list"
+		then
+			mkdir -p "$tmp/$right_dir/$(dirname "$name")"
+			git show "$right":"$name" >"$tmp/$right_dir/$name" || true
+		fi
+	elif test -n "$compare_staged"
+	then
+		ls_list=$(git ls-files -- "$name")
+		if test -n "$ls_list"
+		then
+			mkdir -p "$tmp/$right_dir/$(dirname "$name")"
+			git show :"$name" >"$tmp/$right_dir/$name"
+		fi
+	else
+		if test -e "$name"
+		then
+			mkdir -p "$tmp/$right_dir/$(dirname "$name")"
+			cp "$name" "$tmp/$right_dir/$name"
+		fi
+	fi
+done < "$tmp/filelist"
+
+# Populate the tmp/left_dir directory with the files to be compared
+while read name
+do
+	if test -n "$left"
+	then
+		ls_list=$(git ls-tree $left "$name")
+		if test -n "$ls_list"
+		then
+			mkdir -p "$tmp/$left_dir/$(dirname "$name")"
+			git show "$left":"$name" >"$tmp/$left_dir/$name" || true
+		fi
+	else
+		if test -n "$compare_staged"
+		then
+			ls_list=$(git ls-tree HEAD "$name")
+			if test -n "$ls_list"
+			then
+				mkdir -p "$tmp/$left_dir/$(dirname "$name")"
+				git show HEAD:"$name" >"$tmp/$left_dir/$name"
+			fi
+		else
+			mkdir -p "$tmp/$left_dir/$(dirname "$name")"
+			git show :"$name" >"$tmp/$left_dir/$name"
+		fi
+	fi
+done < "$tmp/filelist"
+
+LOCAL="$tmp/$left_dir"
+REMOTE="$tmp/$right_dir"
+
+if test -n "$diff_tool"
+then
+	export BASE
+	eval $diff_tool '"$LOCAL"' '"$REMOTE"'
+else
+	run_merge_tool "$merge_tool" false
+fi
+
+# Copy files back to the working dir, if requested
+if test -n "$copy_back" && test "$right_dir" = "working_tree"
+then
+	cd "$start_dir"
+	git_top_dir=$(git rev-parse --show-toplevel)
+	find "$tmp/$right_dir" -type f |
+	while read file
+	do
+		cp "$file" "$git_top_dir/${file#$tmp/$right_dir/}"
+	done
+fi
diff --git a/contrib/emacs/git-blame.el b/contrib/emacs/git-blame.el
index 4fa70c5..e671f6c 100644
--- a/contrib/emacs/git-blame.el
+++ b/contrib/emacs/git-blame.el
@@ -79,6 +79,58 @@
 ;;; Code:
 
 (eval-when-compile (require 'cl))			      ; to use `push', `pop'
+(require 'format-spec)
+
+(defface git-blame-prefix-face
+  '((((background dark)) (:foreground "gray"
+                          :background "black"))
+    (((background light)) (:foreground "gray"
+                           :background "white"))
+    (t (:weight bold)))
+  "The face used for the hash prefix."
+  :group 'git-blame)
+
+(defgroup git-blame nil
+  "A minor mode showing Git blame information."
+  :group 'git
+  :link '(function-link git-blame-mode))
+
+
+(defcustom git-blame-use-colors t
+  "Use colors to indicate commits in `git-blame-mode'."
+  :type 'boolean
+  :group 'git-blame)
+
+(defcustom git-blame-prefix-format
+  "%h %20A:"
+  "The format of the prefix added to each line in `git-blame'
+mode. The format is passed to `format-spec' with the following format keys:
+
+  %h - the abbreviated hash
+  %H - the full hash
+  %a - the author name
+  %A - the author email
+  %c - the committer name
+  %C - the committer email
+  %s - the commit summary
+"
+  :group 'git-blame)
+
+(defcustom git-blame-mouseover-format
+  "%h %a %A: %s"
+  "The format of the description shown when pointing at a line in
+`git-blame' mode. The format string is passed to `format-spec'
+with the following format keys:
+
+  %h - the abbreviated hash
+  %H - the full hash
+  %a - the author name
+  %A - the author email
+  %c - the committer name
+  %C - the committer email
+  %s - the commit summary
+"
+  :group 'git-blame)
 
 
 (defun git-blame-color-scale (&rest elements)
@@ -252,7 +304,7 @@
 
 (defun git-blame-cleanup ()
   "Remove all blame properties"
-    (mapcar 'delete-overlay git-blame-overlays)
+    (mapc 'delete-overlay git-blame-overlays)
     (setq git-blame-overlays nil)
     (remove-git-blame-text-properties (point-min) (point-max)))
 
@@ -285,16 +337,16 @@
 (defvar in-blame-filter nil)
 
 (defun git-blame-filter (proc str)
-  (save-excursion
-    (set-buffer (process-buffer proc))
-    (goto-char (process-mark proc))
-    (insert-before-markers str)
-    (goto-char 0)
-    (unless in-blame-filter
-      (let ((more t)
-            (in-blame-filter t))
-        (while more
-          (setq more (git-blame-parse)))))))
+  (with-current-buffer (process-buffer proc)
+    (save-excursion
+      (goto-char (process-mark proc))
+      (insert-before-markers str)
+      (goto-char (point-min))
+      (unless in-blame-filter
+        (let ((more t)
+              (in-blame-filter t))
+          (while more
+            (setq more (git-blame-parse))))))))
 
 (defun git-blame-parse ()
   (cond ((looking-at "\\([0-9a-f]\\{40\\}\\) \\([0-9]+\\) \\([0-9]+\\) \\([0-9]+\\)\n")
@@ -302,72 +354,70 @@
                (src-line (string-to-number (match-string 2)))
                (res-line (string-to-number (match-string 3)))
                (num-lines (string-to-number (match-string 4))))
-           (setq git-blame-current
-                 (if (string= hash "0000000000000000000000000000000000000000")
-                     nil
-                   (git-blame-new-commit
-                    hash src-line res-line num-lines))))
-         (delete-region (point) (match-end 0))
-         t)
-        ((looking-at "filename \\(.+\\)\n")
-         (let ((filename (match-string 1)))
-           (git-blame-add-info "filename" filename))
-         (delete-region (point) (match-end 0))
+           (delete-region (point) (match-end 0))
+           (setq git-blame-current (list (git-blame-new-commit hash)
+                                         src-line res-line num-lines)))
          t)
         ((looking-at "\\([a-z-]+\\) \\(.+\\)\n")
          (let ((key (match-string 1))
                (value (match-string 2)))
-           (git-blame-add-info key value))
-         (delete-region (point) (match-end 0))
-         t)
-        ((looking-at "boundary\n")
-         (setq git-blame-current nil)
-         (delete-region (point) (match-end 0))
+           (delete-region (point) (match-end 0))
+           (git-blame-add-info (car git-blame-current) key value)
+           (when (string= key "filename")
+             (git-blame-create-overlay (car git-blame-current)
+                                       (caddr git-blame-current)
+                                       (cadddr git-blame-current))
+             (setq git-blame-current nil)))
          t)
         (t
          nil)))
 
-(defun git-blame-new-commit (hash src-line res-line num-lines)
-  (save-excursion
-    (set-buffer git-blame-file)
-    (let ((info (gethash hash git-blame-cache))
-          (inhibit-point-motion-hooks t)
-          (inhibit-modification-hooks t))
-      (when (not info)
-	;; Assign a random color to each new commit info
-	;; Take care not to select the same color multiple times
-	(let ((color (if git-blame-colors
-			 (git-blame-random-pop git-blame-colors)
-		       git-blame-ancient-color)))
-          (setq info (list hash src-line res-line num-lines
-                           (git-describe-commit hash)
-                           (cons 'color color))))
-        (puthash hash info git-blame-cache))
-      (goto-line res-line)
-      (while (> num-lines 0)
-        (if (get-text-property (point) 'git-blame)
-            (forward-line)
-          (let* ((start (point))
-                 (end (progn (forward-line 1) (point)))
-                 (ovl (make-overlay start end)))
-            (push ovl git-blame-overlays)
-            (overlay-put ovl 'git-blame info)
-            (overlay-put ovl 'help-echo hash)
-            (overlay-put ovl 'face (list :background
-                                         (cdr (assq 'color (nthcdr 5 info)))))
-            ;; the point-entered property doesn't seem to work in overlays
-            ;;(overlay-put ovl 'point-entered
-            ;;             `(lambda (x y) (git-blame-identify ,hash)))
-            (let ((modified (buffer-modified-p)))
-              (put-text-property (if (= start 1) start (1- start)) (1- end)
-                                 'point-entered
-                                 `(lambda (x y) (git-blame-identify ,hash)))
-              (set-buffer-modified-p modified))))
-        (setq num-lines (1- num-lines))))))
+(defun git-blame-new-commit (hash)
+  (with-current-buffer git-blame-file
+    (or (gethash hash git-blame-cache)
+        ;; Assign a random color to each new commit info
+        ;; Take care not to select the same color multiple times
+        (let* ((color (if git-blame-colors
+                          (git-blame-random-pop git-blame-colors)
+                        git-blame-ancient-color))
+               (info `(,hash (color . ,color))))
+          (puthash hash info git-blame-cache)
+          info))))
 
-(defun git-blame-add-info (key value)
-  (if git-blame-current
-      (nconc git-blame-current (list (cons (intern key) value)))))
+(defun git-blame-create-overlay (info start-line num-lines)
+  (with-current-buffer git-blame-file
+    (save-excursion
+      (let ((inhibit-point-motion-hooks t)
+            (inhibit-modification-hooks t))
+        (goto-char (point-min))
+        (forward-line (1- start-line))
+        (let* ((start (point))
+               (end (progn (forward-line num-lines) (point)))
+               (ovl (make-overlay start end))
+               (hash (car info))
+               (spec `((?h . ,(substring hash 0 6))
+                       (?H . ,hash)
+                       (?a . ,(git-blame-get-info info 'author))
+                       (?A . ,(git-blame-get-info info 'author-mail))
+                       (?c . ,(git-blame-get-info info 'committer))
+                       (?C . ,(git-blame-get-info info 'committer-mail))
+                       (?s . ,(git-blame-get-info info 'summary)))))
+          (push ovl git-blame-overlays)
+          (overlay-put ovl 'git-blame info)
+          (overlay-put ovl 'help-echo
+                       (format-spec git-blame-mouseover-format spec))
+          (if git-blame-use-colors
+              (overlay-put ovl 'face (list :background
+                                           (cdr (assq 'color (cdr info))))))
+          (overlay-put ovl 'line-prefix
+                       (propertize (format-spec git-blame-prefix-format spec)
+                                   'face 'git-blame-prefix-face)))))))
+
+(defun git-blame-add-info (info key value)
+  (nconc info (list (cons (intern key) value))))
+
+(defun git-blame-get-info (info key)
+  (cdr (assq key (cdr info))))
 
 (defun git-blame-current-commit ()
   (let ((info (get-char-property (point) 'git-blame)))
diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el
index eace9c1..65c95d9 100644
--- a/contrib/emacs/git.el
+++ b/contrib/emacs/git.el
@@ -429,16 +429,19 @@
     (git-get-string-sha1
      (git-call-process-string-display-error "write-tree"))))
 
-(defun git-commit-tree (buffer tree head)
-  "Call git-commit-tree with buffer as input and return the resulting commit SHA1."
+(defun git-commit-tree (buffer tree parent)
+  "Create a commit and possibly update HEAD.
+Create a commit with the message in BUFFER using the tree with hash TREE.
+Use PARENT as the parent of the new commit. If PARENT is the current \"HEAD\",
+update the \"HEAD\" reference to the new commit."
   (let ((author-name (git-get-committer-name))
         (author-email (git-get-committer-email))
         (subject "commit (initial): ")
         author-date log-start log-end args coding-system-for-write)
-    (when head
+    (when parent
       (setq subject "commit: ")
       (push "-p" args)
-      (push head args))
+      (push parent args))
     (with-current-buffer buffer
       (goto-char (point-min))
       (if
@@ -474,7 +477,7 @@
               (apply #'git-run-command-region
                      buffer log-start log-end env
                      "commit-tree" tree (nreverse args))))))
-      (when commit (git-update-ref "HEAD" commit head subject))
+      (when commit (git-update-ref "HEAD" commit parent subject))
       commit)))
 
 (defun git-empty-db-p ()
@@ -1043,7 +1046,7 @@
 (defun git-add-file ()
   "Add marked file(s) to the index cache."
   (interactive)
-  (let ((files (git-get-filenames (git-marked-files-state 'unknown 'ignored))))
+  (let ((files (git-get-filenames (git-marked-files-state 'unknown 'ignored 'unmerged))))
     ;; FIXME: add support for directories
     (unless files
       (push (file-relative-name (read-file-name "File to add: " nil nil t)) files))
@@ -1116,15 +1119,6 @@
               (when buffer (with-current-buffer buffer (revert-buffer t t t)))))
           (git-success-message "Reverted" names))))))
 
-(defun git-resolve-file ()
-  "Resolve conflicts in marked file(s)."
-  (interactive)
-  (let ((files (git-get-filenames (git-marked-files-state 'unmerged))))
-    (when files
-      (when (apply 'git-call-process-display-error "update-index" "--" files)
-        (git-update-status-files files)
-        (git-success-message "Resolved" files)))))
-
 (defun git-remove-handled ()
   "Remove handled files from the status list."
   (interactive)
@@ -1316,6 +1310,13 @@
       (when sign-off (git-append-sign-off committer-name committer-email)))
     buffer))
 
+(define-derived-mode git-log-edit-mode log-edit-mode "Git-Log-Edit"
+  "Major mode for editing git log messages.
+
+Set up git-specific `font-lock-keywords' for `log-edit-mode'."
+  (set (make-local-variable 'font-lock-defaults)
+       '(git-log-edit-font-lock-keywords t t)))
+
 (defun git-commit-file ()
   "Commit the marked file(s), asking for a commit message."
   (interactive)
@@ -1341,9 +1342,9 @@
         (git-setup-log-buffer buffer (git-get-merge-heads) author-name author-email subject date))
       (if (boundp 'log-edit-diff-function)
 	  (log-edit 'git-do-commit nil '((log-edit-listfun . git-log-edit-files)
-					 (log-edit-diff-function . git-log-edit-diff)) buffer)
-	(log-edit 'git-do-commit nil 'git-log-edit-files buffer))
-      (setq font-lock-keywords (font-lock-compile-keywords git-log-edit-font-lock-keywords))
+					 (log-edit-diff-function . git-log-edit-diff)) buffer 'git-log-edit-mode)
+	(log-edit 'git-do-commit nil 'git-log-edit-files buffer
+                  'git-log-edit-mode))
       (setq paragraph-separate (concat (regexp-quote git-log-msg-separator) "$\\|Author: \\|Date: \\|Merge: \\|Signed-off-by: \\|\f\\|[ 	]*$"))
       (setq buffer-file-coding-system coding-system)
       (re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t))))
@@ -1553,7 +1554,6 @@
     (define-key map "P"   'git-prev-unmerged-file)
     (define-key map "q"   'git-status-quit)
     (define-key map "r"   'git-remove-file)
-    (define-key map "R"   'git-resolve-file)
     (define-key map "t"    toggle-map)
     (define-key map "T"   'git-toggle-all-marks)
     (define-key map "u"   'git-unmark-file)
@@ -1595,7 +1595,6 @@
       ("Merge"
 	["Next Unmerged File" git-next-unmerged-file t]
 	["Prev Unmerged File" git-prev-unmerged-file t]
-	["Mark as Resolved" git-resolve-file t]
 	["Interactive Merge File" git-find-file-imerge t]
 	["Diff Against Common Base File" git-diff-file-base t]
 	["Diff Combined" git-diff-file-combined t]
diff --git a/contrib/examples/builtin-fetch--tool.c b/contrib/examples/builtin-fetch--tool.c
new file mode 100644
index 0000000..0d54aa7
--- /dev/null
+++ b/contrib/examples/builtin-fetch--tool.c
@@ -0,0 +1,574 @@
+#include "builtin.h"
+#include "cache.h"
+#include "refs.h"
+#include "commit.h"
+#include "sigchain.h"
+
+static char *get_stdin(void)
+{
+	struct strbuf buf = STRBUF_INIT;
+	if (strbuf_read(&buf, 0, 1024) < 0) {
+		die_errno("error reading standard input");
+	}
+	return strbuf_detach(&buf, NULL);
+}
+
+static void show_new(enum object_type type, unsigned char *sha1_new)
+{
+	fprintf(stderr, "  %s: %s\n", typename(type),
+		find_unique_abbrev(sha1_new, DEFAULT_ABBREV));
+}
+
+static int update_ref_env(const char *action,
+		      const char *refname,
+		      unsigned char *sha1,
+		      unsigned char *oldval)
+{
+	char msg[1024];
+	const char *rla = getenv("GIT_REFLOG_ACTION");
+
+	if (!rla)
+		rla = "(reflog update)";
+	if (snprintf(msg, sizeof(msg), "%s: %s", rla, action) >= sizeof(msg))
+		warning("reflog message too long: %.*s...", 50, msg);
+	return update_ref(msg, refname, sha1, oldval, 0, QUIET_ON_ERR);
+}
+
+static int update_local_ref(const char *name,
+			    const char *new_head,
+			    const char *note,
+			    int verbose, int force)
+{
+	unsigned char sha1_old[20], sha1_new[20];
+	char oldh[41], newh[41];
+	struct commit *current, *updated;
+	enum object_type type;
+
+	if (get_sha1_hex(new_head, sha1_new))
+		die("malformed object name %s", new_head);
+
+	type = sha1_object_info(sha1_new, NULL);
+	if (type < 0)
+		die("object %s not found", new_head);
+
+	if (!*name) {
+		/* Not storing */
+		if (verbose) {
+			fprintf(stderr, "* fetched %s\n", note);
+			show_new(type, sha1_new);
+		}
+		return 0;
+	}
+
+	if (get_sha1(name, sha1_old)) {
+		const char *msg;
+	just_store:
+		/* new ref */
+		if (!strncmp(name, "refs/tags/", 10))
+			msg = "storing tag";
+		else
+			msg = "storing head";
+		fprintf(stderr, "* %s: storing %s\n",
+			name, note);
+		show_new(type, sha1_new);
+		return update_ref_env(msg, name, sha1_new, NULL);
+	}
+
+	if (!hashcmp(sha1_old, sha1_new)) {
+		if (verbose) {
+			fprintf(stderr, "* %s: same as %s\n", name, note);
+			show_new(type, sha1_new);
+		}
+		return 0;
+	}
+
+	if (!strncmp(name, "refs/tags/", 10)) {
+		fprintf(stderr, "* %s: updating with %s\n", name, note);
+		show_new(type, sha1_new);
+		return update_ref_env("updating tag", name, sha1_new, NULL);
+	}
+
+	current = lookup_commit_reference(sha1_old);
+	updated = lookup_commit_reference(sha1_new);
+	if (!current || !updated)
+		goto just_store;
+
+	strcpy(oldh, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));
+	strcpy(newh, find_unique_abbrev(sha1_new, DEFAULT_ABBREV));
+
+	if (in_merge_bases(current, &updated, 1)) {
+		fprintf(stderr, "* %s: fast-forward to %s\n",
+			name, note);
+		fprintf(stderr, "  old..new: %s..%s\n", oldh, newh);
+		return update_ref_env("fast-forward", name, sha1_new, sha1_old);
+	}
+	if (!force) {
+		fprintf(stderr,
+			"* %s: not updating to non-fast-forward %s\n",
+			name, note);
+		fprintf(stderr,
+			"  old...new: %s...%s\n", oldh, newh);
+		return 1;
+	}
+	fprintf(stderr,
+		"* %s: forcing update to non-fast-forward %s\n",
+		name, note);
+	fprintf(stderr, "  old...new: %s...%s\n", oldh, newh);
+	return update_ref_env("forced-update", name, sha1_new, sha1_old);
+}
+
+static int append_fetch_head(FILE *fp,
+			     const char *head, const char *remote,
+			     const char *remote_name, const char *remote_nick,
+			     const char *local_name, int not_for_merge,
+			     int verbose, int force)
+{
+	struct commit *commit;
+	int remote_len, i, note_len;
+	unsigned char sha1[20];
+	char note[1024];
+	const char *what, *kind;
+
+	if (get_sha1(head, sha1))
+		return error("Not a valid object name: %s", head);
+	commit = lookup_commit_reference_gently(sha1, 1);
+	if (!commit)
+		not_for_merge = 1;
+
+	if (!strcmp(remote_name, "HEAD")) {
+		kind = "";
+		what = "";
+	}
+	else if (!strncmp(remote_name, "refs/heads/", 11)) {
+		kind = "branch";
+		what = remote_name + 11;
+	}
+	else if (!strncmp(remote_name, "refs/tags/", 10)) {
+		kind = "tag";
+		what = remote_name + 10;
+	}
+	else if (!strncmp(remote_name, "refs/remotes/", 13)) {
+		kind = "remote-tracking branch";
+		what = remote_name + 13;
+	}
+	else {
+		kind = "";
+		what = remote_name;
+	}
+
+	remote_len = strlen(remote);
+	for (i = remote_len - 1; remote[i] == '/' && 0 <= i; i--)
+		;
+	remote_len = i + 1;
+	if (4 < i && !strncmp(".git", remote + i - 3, 4))
+		remote_len = i - 3;
+
+	note_len = 0;
+	if (*what) {
+		if (*kind)
+			note_len += sprintf(note + note_len, "%s ", kind);
+		note_len += sprintf(note + note_len, "'%s' of ", what);
+	}
+	note_len += sprintf(note + note_len, "%.*s", remote_len, remote);
+	fprintf(fp, "%s\t%s\t%s\n",
+		sha1_to_hex(commit ? commit->object.sha1 : sha1),
+		not_for_merge ? "not-for-merge" : "",
+		note);
+	return update_local_ref(local_name, head, note, verbose, force);
+}
+
+static char *keep;
+static void remove_keep(void)
+{
+	if (keep && *keep)
+		unlink(keep);
+}
+
+static void remove_keep_on_signal(int signo)
+{
+	remove_keep();
+	sigchain_pop(signo);
+	raise(signo);
+}
+
+static char *find_local_name(const char *remote_name, const char *refs,
+			     int *force_p, int *not_for_merge_p)
+{
+	const char *ref = refs;
+	int len = strlen(remote_name);
+
+	while (ref) {
+		const char *next;
+		int single_force, not_for_merge;
+
+		while (*ref == '\n')
+			ref++;
+		if (!*ref)
+			break;
+		next = strchr(ref, '\n');
+
+		single_force = not_for_merge = 0;
+		if (*ref == '+') {
+			single_force = 1;
+			ref++;
+		}
+		if (*ref == '.') {
+			not_for_merge = 1;
+			ref++;
+			if (*ref == '+') {
+				single_force = 1;
+				ref++;
+			}
+		}
+		if (!strncmp(remote_name, ref, len) && ref[len] == ':') {
+			const char *local_part = ref + len + 1;
+			int retlen;
+
+			if (!next)
+				retlen = strlen(local_part);
+			else
+				retlen = next - local_part;
+			*force_p = single_force;
+			*not_for_merge_p = not_for_merge;
+			return xmemdupz(local_part, retlen);
+		}
+		ref = next;
+	}
+	return NULL;
+}
+
+static int fetch_native_store(FILE *fp,
+			      const char *remote,
+			      const char *remote_nick,
+			      const char *refs,
+			      int verbose, int force)
+{
+	char buffer[1024];
+	int err = 0;
+
+	sigchain_push_common(remove_keep_on_signal);
+	atexit(remove_keep);
+
+	while (fgets(buffer, sizeof(buffer), stdin)) {
+		int len;
+		char *cp;
+		char *local_name;
+		int single_force, not_for_merge;
+
+		for (cp = buffer; *cp && !isspace(*cp); cp++)
+			;
+		if (*cp)
+			*cp++ = 0;
+		len = strlen(cp);
+		if (len && cp[len-1] == '\n')
+			cp[--len] = 0;
+		if (!strcmp(buffer, "failed"))
+			die("Fetch failure: %s", remote);
+		if (!strcmp(buffer, "pack"))
+			continue;
+		if (!strcmp(buffer, "keep")) {
+			char *od = get_object_directory();
+			int len = strlen(od) + strlen(cp) + 50;
+			keep = xmalloc(len);
+			sprintf(keep, "%s/pack/pack-%s.keep", od, cp);
+			continue;
+		}
+
+		local_name = find_local_name(cp, refs,
+					     &single_force, &not_for_merge);
+		if (!local_name)
+			continue;
+		err |= append_fetch_head(fp,
+					 buffer, remote, cp, remote_nick,
+					 local_name, not_for_merge,
+					 verbose, force || single_force);
+	}
+	return err;
+}
+
+static int parse_reflist(const char *reflist)
+{
+	const char *ref;
+
+	printf("refs='");
+	for (ref = reflist; ref; ) {
+		const char *next;
+		while (*ref && isspace(*ref))
+			ref++;
+		if (!*ref)
+			break;
+		for (next = ref; *next && !isspace(*next); next++)
+			;
+		printf("\n%.*s", (int)(next - ref), ref);
+		ref = next;
+	}
+	printf("'\n");
+
+	printf("rref='");
+	for (ref = reflist; ref; ) {
+		const char *next, *colon;
+		while (*ref && isspace(*ref))
+			ref++;
+		if (!*ref)
+			break;
+		for (next = ref; *next && !isspace(*next); next++)
+			;
+		if (*ref == '.')
+			ref++;
+		if (*ref == '+')
+			ref++;
+		colon = strchr(ref, ':');
+		putchar('\n');
+		printf("%.*s", (int)((colon ? colon : next) - ref), ref);
+		ref = next;
+	}
+	printf("'\n");
+	return 0;
+}
+
+static int expand_refs_wildcard(const char *ls_remote_result, int numrefs,
+				const char **refs)
+{
+	int i, matchlen, replacelen;
+	int found_one = 0;
+	const char *remote = *refs++;
+	numrefs--;
+
+	if (numrefs == 0) {
+		fprintf(stderr, "Nothing specified for fetching with remote.%s.fetch\n",
+			remote);
+		printf("empty\n");
+	}
+
+	for (i = 0; i < numrefs; i++) {
+		const char *ref = refs[i];
+		const char *lref = ref;
+		const char *colon;
+		const char *tail;
+		const char *ls;
+		const char *next;
+
+		if (*lref == '+')
+			lref++;
+		colon = strchr(lref, ':');
+		tail = lref + strlen(lref);
+		if (!(colon &&
+		      2 < colon - lref &&
+		      colon[-1] == '*' &&
+		      colon[-2] == '/' &&
+		      2 < tail - (colon + 1) &&
+		      tail[-1] == '*' &&
+		      tail[-2] == '/')) {
+			/* not a glob */
+			if (!found_one++)
+				printf("explicit\n");
+			printf("%s\n", ref);
+			continue;
+		}
+
+		/* glob */
+		if (!found_one++)
+			printf("glob\n");
+
+		/* lref to colon-2 is remote hierarchy name;
+		 * colon+1 to tail-2 is local.
+		 */
+		matchlen = (colon-1) - lref;
+		replacelen = (tail-1) - (colon+1);
+		for (ls = ls_remote_result; ls; ls = next) {
+			const char *eol;
+			unsigned char sha1[20];
+			int namelen;
+
+			while (*ls && isspace(*ls))
+				ls++;
+			next = strchr(ls, '\n');
+			eol = !next ? (ls + strlen(ls)) : next;
+			if (!memcmp("^{}", eol-3, 3))
+				continue;
+			if (eol - ls < 40)
+				continue;
+			if (get_sha1_hex(ls, sha1))
+				continue;
+			ls += 40;
+			while (ls < eol && isspace(*ls))
+				ls++;
+			/* ls to next (or eol) is the name.
+			 * is it identical to lref to colon-2?
+			 */
+			if ((eol - ls) <= matchlen ||
+			    strncmp(ls, lref, matchlen))
+				continue;
+
+			/* Yes, it is a match */
+			namelen = eol - ls;
+			if (lref != ref)
+				putchar('+');
+			printf("%.*s:%.*s%.*s\n",
+			       namelen, ls,
+			       replacelen, colon + 1,
+			       namelen - matchlen, ls + matchlen);
+		}
+	}
+	return 0;
+}
+
+static int pick_rref(int sha1_only, const char *rref, const char *ls_remote_result)
+{
+	int err = 0;
+	int lrr_count = lrr_count, i, pass;
+	const char *cp;
+	struct lrr {
+		const char *line;
+		const char *name;
+		int namelen;
+		int shown;
+	} *lrr_list = lrr_list;
+
+	for (pass = 0; pass < 2; pass++) {
+		/* pass 0 counts and allocates, pass 1 fills... */
+		cp = ls_remote_result;
+		i = 0;
+		while (1) {
+			const char *np;
+			while (*cp && isspace(*cp))
+				cp++;
+			if (!*cp)
+				break;
+			np = strchrnul(cp, '\n');
+			if (pass) {
+				lrr_list[i].line = cp;
+				lrr_list[i].name = cp + 41;
+				lrr_list[i].namelen = np - (cp + 41);
+			}
+			i++;
+			cp = np;
+		}
+		if (!pass) {
+			lrr_count = i;
+			lrr_list = xcalloc(lrr_count, sizeof(*lrr_list));
+		}
+	}
+
+	while (1) {
+		const char *next;
+		int rreflen;
+		int i;
+
+		while (*rref && isspace(*rref))
+			rref++;
+		if (!*rref)
+			break;
+		next = strchrnul(rref, '\n');
+		rreflen = next - rref;
+
+		for (i = 0; i < lrr_count; i++) {
+			struct lrr *lrr = &(lrr_list[i]);
+
+			if (rreflen == lrr->namelen &&
+			    !memcmp(lrr->name, rref, rreflen)) {
+				if (!lrr->shown)
+					printf("%.*s\n",
+					       sha1_only ? 40 : lrr->namelen + 41,
+					       lrr->line);
+				lrr->shown = 1;
+				break;
+			}
+		}
+		if (lrr_count <= i) {
+			error("pick-rref: %.*s not found", rreflen, rref);
+			err = 1;
+		}
+		rref = next;
+	}
+	free(lrr_list);
+	return err;
+}
+
+int cmd_fetch__tool(int argc, const char **argv, const char *prefix)
+{
+	int verbose = 0;
+	int force = 0;
+	int sopt = 0;
+
+	while (1 < argc) {
+		const char *arg = argv[1];
+		if (!strcmp("-v", arg))
+			verbose = 1;
+		else if (!strcmp("-f", arg))
+			force = 1;
+		else if (!strcmp("-s", arg))
+			sopt = 1;
+		else
+			break;
+		argc--;
+		argv++;
+	}
+
+	if (argc <= 1)
+		return error("Missing subcommand");
+
+	if (!strcmp("append-fetch-head", argv[1])) {
+		int result;
+		FILE *fp;
+		char *filename;
+
+		if (argc != 8)
+			return error("append-fetch-head takes 6 args");
+		filename = git_path("FETCH_HEAD");
+		fp = fopen(filename, "a");
+		if (!fp)
+			return error("cannot open %s: %s", filename, strerror(errno));
+		result = append_fetch_head(fp, argv[2], argv[3],
+					   argv[4], argv[5],
+					   argv[6], !!argv[7][0],
+					   verbose, force);
+		fclose(fp);
+		return result;
+	}
+	if (!strcmp("native-store", argv[1])) {
+		int result;
+		FILE *fp;
+		char *filename;
+
+		if (argc != 5)
+			return error("fetch-native-store takes 3 args");
+		filename = git_path("FETCH_HEAD");
+		fp = fopen(filename, "a");
+		if (!fp)
+			return error("cannot open %s: %s", filename, strerror(errno));
+		result = fetch_native_store(fp, argv[2], argv[3], argv[4],
+					    verbose, force);
+		fclose(fp);
+		return result;
+	}
+	if (!strcmp("parse-reflist", argv[1])) {
+		const char *reflist;
+		if (argc != 3)
+			return error("parse-reflist takes 1 arg");
+		reflist = argv[2];
+		if (!strcmp(reflist, "-"))
+			reflist = get_stdin();
+		return parse_reflist(reflist);
+	}
+	if (!strcmp("pick-rref", argv[1])) {
+		const char *ls_remote_result;
+		if (argc != 4)
+			return error("pick-rref takes 2 args");
+		ls_remote_result = argv[3];
+		if (!strcmp(ls_remote_result, "-"))
+			ls_remote_result = get_stdin();
+		return pick_rref(sopt, argv[2], ls_remote_result);
+	}
+	if (!strcmp("expand-refs-wildcard", argv[1])) {
+		const char *reflist;
+		if (argc < 4)
+			return error("expand-refs-wildcard takes at least 2 args");
+		reflist = argv[2];
+		if (!strcmp(reflist, "-"))
+			reflist = get_stdin();
+		return expand_refs_wildcard(reflist, argc - 3, argv + 3);
+	}
+
+	return error("Unknown subcommand: %s", argv[1]);
+}
diff --git a/contrib/examples/git-commit.sh b/contrib/examples/git-commit.sh
index 5c72f65..23ffb02 100755
--- a/contrib/examples/git-commit.sh
+++ b/contrib/examples/git-commit.sh
@@ -631,7 +631,7 @@
 	if test -z "$quiet"
 	then
 		commit=`git diff-tree --always --shortstat --pretty="format:%h: %s"\
-		       --summary --root HEAD --`
+		       --abbrev --summary --root HEAD --`
 		echo "Created${initial_commit:+ initial} commit $commit"
 	fi
 fi
diff --git a/contrib/examples/git-fetch.sh b/contrib/examples/git-fetch.sh
index e44af2c..a314273 100755
--- a/contrib/examples/git-fetch.sh
+++ b/contrib/examples/git-fetch.sh
@@ -127,10 +127,12 @@
 	orig_head=$(git rev-parse --verify HEAD 2>/dev/null)
 fi
 
-# Allow --notags from remote.$1.tagopt
+# Allow --tags/--notags from remote.$1.tagopt
 case "$tags$no_tags" in
 '')
 	case "$(git config --get "remote.$1.tagopt")" in
+	--tags)
+		tags=t ;;
 	--no-tags)
 		no_tags=t ;;
 	esac
diff --git a/contrib/examples/git-merge.sh b/contrib/examples/git-merge.sh
index e9588ee..7b922c3 100755
--- a/contrib/examples/git-merge.sh
+++ b/contrib/examples/git-merge.sh
@@ -14,8 +14,11 @@
 log                  add list of one-line log to merge commit message
 squash               create a single commit instead of doing a merge
 commit               perform a commit if the merge succeeds (default)
-ff                   allow fast forward (default)
+ff                   allow fast-forward (default)
+ff-only              abort if fast-forward is not possible
+rerere-autoupdate    update index with any reused conflict resolution
 s,strategy=          merge strategy to use
+X=                   option for selected merge strategy
 m,message=           message to be used for the merge commit (if any)
 "
 
@@ -25,25 +28,32 @@
 cd_to_toplevel
 
 test -z "$(git ls-files -u)" ||
-	die "You are in the middle of a conflicted merge."
+	die "Merge is not possible because you have unmerged files."
+
+! test -e "$GIT_DIR/MERGE_HEAD" ||
+	die 'You have not concluded your merge (MERGE_HEAD exists).'
 
 LF='
 '
 
 all_strategies='recur recursive octopus resolve stupid ours subtree'
+all_strategies="$all_strategies recursive-ours recursive-theirs"
+not_strategies='base file index tree'
 default_twohead_strategies='recursive'
 default_octopus_strategies='octopus'
 no_fast_forward_strategies='subtree ours'
-no_trivial_strategies='recursive recur subtree ours'
+no_trivial_strategies='recursive recur subtree ours recursive-ours recursive-theirs'
 use_strategies=
+xopt=
 
 allow_fast_forward=t
+fast_forward_only=
 allow_trivial_merge=t
-squash= no_commit= log_arg=
+squash= no_commit= log_arg= rr_arg=
 
 dropsave() {
 	rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" \
-		 "$GIT_DIR/MERGE_STASH" || exit 1
+		 "$GIT_DIR/MERGE_STASH" "$GIT_DIR/MERGE_MODE" || exit 1
 }
 
 savestate() {
@@ -130,21 +140,34 @@
 merge_name () {
 	remote="$1"
 	rh=$(git rev-parse --verify "$remote^0" 2>/dev/null) || return
-	bh=$(git show-ref -s --verify "refs/heads/$remote" 2>/dev/null)
-	if test "$rh" = "$bh"
-	then
-		echo "$rh		branch '$remote' of ."
-	elif truname=$(expr "$remote" : '\(.*\)~[1-9][0-9]*$') &&
+	if truname=$(expr "$remote" : '\(.*\)~[0-9]*$') &&
 		git show-ref -q --verify "refs/heads/$truname" 2>/dev/null
 	then
 		echo "$rh		branch '$truname' (early part) of ."
-	elif test "$remote" = "FETCH_HEAD" -a -r "$GIT_DIR/FETCH_HEAD"
+		return
+	fi
+	if found_ref=$(git rev-parse --symbolic-full-name --verify \
+							"$remote" 2>/dev/null)
+	then
+		expanded=$(git check-ref-format --branch "$remote") ||
+			exit
+		if test "${found_ref#refs/heads/}" != "$found_ref"
+		then
+			echo "$rh		branch '$expanded' of ."
+			return
+		elif test "${found_ref#refs/remotes/}" != "$found_ref"
+		then
+			echo "$rh		remote branch '$expanded' of ."
+			return
+		fi
+	fi
+	if test "$remote" = "FETCH_HEAD" -a -r "$GIT_DIR/FETCH_HEAD"
 	then
 		sed -e 's/	not-for-merge	/		/' -e 1q \
 			"$GIT_DIR/FETCH_HEAD"
-	else
-		echo "$rh		commit '$remote'"
+		return
 	fi
+	echo "$rh		commit '$remote'"
 }
 
 parse_config () {
@@ -171,16 +194,36 @@
 		--no-ff)
 			test "$squash" != t ||
 				die "You cannot combine --squash with --no-ff."
+			test "$fast_forward_only" != t ||
+				die "You cannot combine --ff-only with --no-ff."
 			allow_fast_forward=f ;;
+		--ff-only)
+			test "$allow_fast_forward" != f ||
+				die "You cannot combine --ff-only with --no-ff."
+			fast_forward_only=t ;;
+		--rerere-autoupdate|--no-rerere-autoupdate)
+			rr_arg=$1 ;;
 		-s|--strategy)
 			shift
 			case " $all_strategies " in
 			*" $1 "*)
-				use_strategies="$use_strategies$1 " ;;
+				use_strategies="$use_strategies$1 "
+				;;
 			*)
-				die "available strategies are: $all_strategies" ;;
+				case " $not_strategies " in
+				*" $1 "*)
+					false
+				esac &&
+				type "git-merge-$1" >/dev/null 2>&1 ||
+					die "available strategies are: $all_strategies"
+				use_strategies="$use_strategies$1 "
+				;;
 			esac
 			;;
+		-X)
+			shift
+			xopt="${xopt:+$xopt }$(git rev-parse --sq-quote "--$1")"
+			;;
 		-m|--message)
 			shift
 			merge_msg="$1"
@@ -244,6 +287,10 @@
 		exit 1
 	fi
 
+	test "$squash" != t ||
+		die "Squash commit into empty head not supported yet"
+	test "$allow_fast_forward" = t ||
+		die "Non-fast-forward into an empty head does not make sense"
 	rh=$(git rev-parse --verify "$1^0") ||
 		die "$1 - not something we can merge"
 
@@ -260,12 +307,18 @@
 	# the given message.  If remote is invalid we will die
 	# later in the common codepath so we discard the error
 	# in this loop.
-	merge_name=$(for remote
+	merge_msg="$(
+		for remote
 		do
 			merge_name "$remote"
-		done | git fmt-merge-msg $log_arg
-	)
-	merge_msg="${merge_msg:+$merge_msg$LF$LF}$merge_name"
+		done |
+		if test "$have_message" = t
+		then
+			git fmt-merge-msg -m "$merge_msg" $log_arg
+		else
+			git fmt-merge-msg $log_arg
+		fi
+	)"
 fi
 head=$(git rev-parse --verify "$head_arg"^0) || usage
 
@@ -334,7 +387,7 @@
 	common=$(git merge-base --all $head "$@")
 	;;
 *)
-	common=$(git show-branch --merge-base $head "$@")
+	common=$(git merge-base --all --octopus $head "$@")
 	;;
 esac
 echo "$head" >"$GIT_DIR/ORIG_HEAD"
@@ -353,7 +406,7 @@
 	# Again the most common case of merging one remote.
 	echo "Updating $(git rev-parse --short $head)..$(git rev-parse --short $1)"
 	git update-index --refresh 2>/dev/null
-	msg="Fast forward"
+	msg="Fast-forward"
 	if test -n "$have_message"
 	then
 		msg="$msg (no commit created; -m option ignored)"
@@ -365,15 +418,15 @@
 	exit 0
 	;;
 ?,1,?*"$LF"?*,*)
-	# We are not doing octopus and not fast forward.  Need a
+	# We are not doing octopus and not fast-forward.  Need a
 	# real merge.
 	;;
 ?,1,*,)
-	# We are not doing octopus, not fast forward, and have only
+	# We are not doing octopus, not fast-forward, and have only
 	# one common.
 	git update-index --refresh 2>/dev/null
-	case "$allow_trivial_merge" in
-	t)
+	case "$allow_trivial_merge,$fast_forward_only" in
+	t,)
 		# See if it is really trivial.
 		git var GIT_COMMITTER_IDENT >/dev/null || exit
 		echo "Trying really trivial in-index merge..."
@@ -412,6 +465,11 @@
 	;;
 esac
 
+if test "$fast_forward_only" = t
+then
+	die "Not possible to fast-forward, aborting."
+fi
+
 # We are going to make a new commit.
 git var GIT_COMMITTER_IDENT >/dev/null || exit
 
@@ -450,7 +508,7 @@
     # Remember which strategy left the state in the working tree
     wt_strategy=$strategy
 
-    git-merge-$strategy $common -- "$head_arg" "$@"
+    eval 'git-merge-$strategy '"$xopt"' $common -- "$head_arg" "$@"'
     exit=$?
     if test "$no_commit" = t && test "$exit" = 0
     then
@@ -488,9 +546,9 @@
 then
     if test "$allow_fast_forward" = "t"
     then
-        parents=$(git show-branch --independent "$head" "$@")
+	parents=$(git merge-base --independent "$head" "$@")
     else
-        parents=$(git rev-parse "$head" "$@")
+	parents=$(git rev-parse "$head" "$@")
     fi
     parents=$(echo "$parents" | sed -e 's/^/-p /')
     result_commit=$(printf '%s\n' "$merge_msg" | git commit-tree $result_tree $parents) || exit
@@ -532,7 +590,15 @@
 	do
 		echo $remote
 	done >"$GIT_DIR/MERGE_HEAD"
-	printf '%s\n' "$merge_msg" >"$GIT_DIR/MERGE_MSG"
+	printf '%s\n' "$merge_msg" >"$GIT_DIR/MERGE_MSG" ||
+		die "Could not write to $GIT_DIR/MERGE_MSG"
+	if test "$allow_fast_forward" != t
+	then
+		printf "%s" no-ff
+	else
+		:
+	fi >"$GIT_DIR/MERGE_MODE" ||
+		die "Could not write to $GIT_DIR/MERGE_MODE"
 fi
 
 if test "$merge_was_ok" = t
@@ -549,6 +615,6 @@
 		sed -e 's/^[^	]*	/	/' |
 		uniq
 	} >>"$GIT_DIR/MERGE_MSG"
-	git rerere
+	git rerere $rr_arg
 	die "Automatic merge failed; fix conflicts and then commit the result."
 fi
diff --git a/contrib/examples/git-notes.sh b/contrib/examples/git-notes.sh
new file mode 100755
index 0000000..e642e47
--- /dev/null
+++ b/contrib/examples/git-notes.sh
@@ -0,0 +1,121 @@
+#!/bin/sh
+
+USAGE="(edit [-F <file> | -m <msg>] | show) [commit]"
+. git-sh-setup
+
+test -z "$1" && usage
+ACTION="$1"; shift
+
+test -z "$GIT_NOTES_REF" && GIT_NOTES_REF="$(git config core.notesref)"
+test -z "$GIT_NOTES_REF" && GIT_NOTES_REF="refs/notes/commits"
+
+MESSAGE=
+while test $# != 0
+do
+	case "$1" in
+	-m)
+		test "$ACTION" = "edit" || usage
+		shift
+		if test "$#" = "0"; then
+			die "error: option -m needs an argument"
+		else
+			if [ -z "$MESSAGE" ]; then
+				MESSAGE="$1"
+			else
+				MESSAGE="$MESSAGE
+
+$1"
+			fi
+			shift
+		fi
+		;;
+	-F)
+		test "$ACTION" = "edit" || usage
+		shift
+		if test "$#" = "0"; then
+			die "error: option -F needs an argument"
+		else
+			if [ -z "$MESSAGE" ]; then
+				MESSAGE="$(cat "$1")"
+			else
+				MESSAGE="$MESSAGE
+
+$(cat "$1")"
+			fi
+			shift
+		fi
+		;;
+	-*)
+		usage
+		;;
+	*)
+		break
+		;;
+	esac
+done
+
+COMMIT=$(git rev-parse --verify --default HEAD "$@") ||
+die "Invalid commit: $@"
+
+case "$ACTION" in
+edit)
+	if [ "${GIT_NOTES_REF#refs/notes/}" = "$GIT_NOTES_REF" ]; then
+		die "Refusing to edit notes in $GIT_NOTES_REF (outside of refs/notes/)"
+	fi
+
+	MSG_FILE="$GIT_DIR/new-notes-$COMMIT"
+	GIT_INDEX_FILE="$MSG_FILE.idx"
+	export GIT_INDEX_FILE
+
+	trap '
+		test -f "$MSG_FILE" && rm "$MSG_FILE"
+		test -f "$GIT_INDEX_FILE" && rm "$GIT_INDEX_FILE"
+	' 0
+
+	CURRENT_HEAD=$(git show-ref "$GIT_NOTES_REF" | cut -f 1 -d ' ')
+	if [ -z "$CURRENT_HEAD" ]; then
+		PARENT=
+	else
+		PARENT="-p $CURRENT_HEAD"
+		git read-tree "$GIT_NOTES_REF" || die "Could not read index"
+	fi
+
+	if [ -z "$MESSAGE" ]; then
+		GIT_NOTES_REF= git log -1 $COMMIT | sed "s/^/#/" > "$MSG_FILE"
+		if [ ! -z "$CURRENT_HEAD" ]; then
+			git cat-file blob :$COMMIT >> "$MSG_FILE" 2> /dev/null
+		fi
+		core_editor="$(git config core.editor)"
+		${GIT_EDITOR:-${core_editor:-${VISUAL:-${EDITOR:-vi}}}} "$MSG_FILE"
+	else
+		echo "$MESSAGE" > "$MSG_FILE"
+	fi
+
+	grep -v ^# < "$MSG_FILE" | git stripspace > "$MSG_FILE".processed
+	mv "$MSG_FILE".processed "$MSG_FILE"
+	if [ -s "$MSG_FILE" ]; then
+		BLOB=$(git hash-object -w "$MSG_FILE") ||
+			die "Could not write into object database"
+		git update-index --add --cacheinfo 0644 $BLOB $COMMIT ||
+			die "Could not write index"
+	else
+		test -z "$CURRENT_HEAD" &&
+			die "Will not initialise with empty tree"
+		git update-index --force-remove $COMMIT ||
+			die "Could not update index"
+	fi
+
+	TREE=$(git write-tree) || die "Could not write tree"
+	NEW_HEAD=$(echo Annotate $COMMIT | git commit-tree $TREE $PARENT) ||
+		die "Could not annotate"
+	git update-ref -m "Annotate $COMMIT" \
+		"$GIT_NOTES_REF" $NEW_HEAD $CURRENT_HEAD
+;;
+show)
+	git rev-parse -q --verify "$GIT_NOTES_REF":$COMMIT > /dev/null ||
+		die "No note for commit $COMMIT."
+	git show "$GIT_NOTES_REF":$COMMIT
+;;
+*)
+	usage
+esac
diff --git a/contrib/examples/git-resolve.sh b/contrib/examples/git-resolve.sh
index 0ee1bd8..8f98142 100755
--- a/contrib/examples/git-resolve.sh
+++ b/contrib/examples/git-resolve.sh
@@ -48,7 +48,7 @@
 "$head")
 	echo "Updating $(git rev-parse --short $head)..$(git rev-parse --short $merge)"
 	git read-tree -u -m $head $merge || exit 1
-	git update-ref -m "resolve $merge_name: Fast forward" \
+	git update-ref -m "resolve $merge_name: Fast-forward" \
 		HEAD "$merge" "$head"
 	git diff-tree -p $head $merge | git apply --stat
 	dropheads
diff --git a/contrib/examples/git-revert.sh b/contrib/examples/git-revert.sh
index 49f0032..6bf155c 100755
--- a/contrib/examples/git-revert.sh
+++ b/contrib/examples/git-revert.sh
@@ -26,6 +26,7 @@
 cd_to_toplevel
 
 no_commit=
+xopt=
 while case "$#" in 0) break ;; esac
 do
 	case "$1" in
@@ -44,6 +45,16 @@
 	-x|--i-really-want-to-expose-my-private-commit-object-name)
 		replay=
 		;;
+	-X?*)
+		xopt="$xopt$(git rev-parse --sq-quote "--${1#-X}")"
+		;;
+	--strategy-option=*)
+		xopt="$xopt$(git rev-parse --sq-quote "--${1#--strategy-option=}")"
+		;;
+	-X|--strategy-option)
+		shift
+		xopt="$xopt$(git rev-parse --sq-quote "--$1")"
+		;;
 	-*)
 		usage
 		;;
@@ -159,7 +170,7 @@
 # and $prev on top of us (when reverting), or the change between
 # $prev and $commit on top of us (when cherry-picking or replaying).
 
-git-merge-recursive $base -- $head $next &&
+eval "git merge-recursive $xopt $base -- $head $next" &&
 result=$(git-write-tree 2>/dev/null) || {
 	mv -f .msg "$GIT_DIR/MERGE_MSG"
 	{
@@ -181,7 +192,6 @@
 	esac
 	exit 1
 }
-echo >&2 "Finished one $me."
 
 # If we are cherry-pick, and if the merge did not result in
 # hand-editing, we will hit this commit and inherit the original
diff --git a/contrib/examples/git-svnimport.perl b/contrib/examples/git-svnimport.perl
index 4576c4a..b09ff8f 100755
--- a/contrib/examples/git-svnimport.perl
+++ b/contrib/examples/git-svnimport.perl
@@ -1,4 +1,4 @@
-#!/usr/bin/perl -w
+#!/usr/bin/perl
 
 # This tool is copyright (c) 2005, Matthias Urlichs.
 # It is released under the Gnu Public License, version 2.
@@ -289,7 +289,7 @@
 unless(-d $git_dir) {
 	system("git init");
 	die "Cannot init the GIT db at $git_tree: $?\n" if $?;
-	system("git read-tree");
+	system("git read-tree --empty");
 	die "Cannot init an empty tree: $?\n" if $?;
 
 	$last_branch = $opt_o;
diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4
deleted file mode 100755
index 342529d..0000000
--- a/contrib/fast-import/git-p4
+++ /dev/null
@@ -1,1895 +0,0 @@
-#!/usr/bin/env python
-#
-# git-p4.py -- A tool for bidirectional operation between a Perforce depot and git.
-#
-# Author: Simon Hausmann <simon@lst.de>
-# Copyright: 2007 Simon Hausmann <simon@lst.de>
-#            2007 Trolltech ASA
-# License: MIT <http://www.opensource.org/licenses/mit-license.php>
-#
-
-import optparse, sys, os, marshal, popen2, subprocess, shelve
-import tempfile, getopt, sha, os.path, time, platform
-import re
-
-from sets import Set;
-
-verbose = False
-
-
-def p4_build_cmd(cmd):
-    """Build a suitable p4 command line.
-
-    This consolidates building and returning a p4 command line into one
-    location. It means that hooking into the environment, or other configuration
-    can be done more easily.
-    """
-    real_cmd = "%s " % "p4"
-
-    user = gitConfig("git-p4.user")
-    if len(user) > 0:
-        real_cmd += "-u %s " % user
-
-    password = gitConfig("git-p4.password")
-    if len(password) > 0:
-        real_cmd += "-P %s " % password
-
-    port = gitConfig("git-p4.port")
-    if len(port) > 0:
-        real_cmd += "-p %s " % port
-
-    host = gitConfig("git-p4.host")
-    if len(host) > 0:
-        real_cmd += "-h %s " % host
-
-    client = gitConfig("git-p4.client")
-    if len(client) > 0:
-        real_cmd += "-c %s " % client
-
-    real_cmd += "%s" % (cmd)
-    if verbose:
-        print real_cmd
-    return real_cmd
-
-def chdir(dir):
-    if os.name == 'nt':
-        os.environ['PWD']=dir
-    os.chdir(dir)
-
-def die(msg):
-    if verbose:
-        raise Exception(msg)
-    else:
-        sys.stderr.write(msg + "\n")
-        sys.exit(1)
-
-def write_pipe(c, str):
-    if verbose:
-        sys.stderr.write('Writing pipe: %s\n' % c)
-
-    pipe = os.popen(c, 'w')
-    val = pipe.write(str)
-    if pipe.close():
-        die('Command failed: %s' % c)
-
-    return val
-
-def p4_write_pipe(c, str):
-    real_cmd = p4_build_cmd(c)
-    return write_pipe(real_cmd, str)
-
-def read_pipe(c, ignore_error=False):
-    if verbose:
-        sys.stderr.write('Reading pipe: %s\n' % c)
-
-    pipe = os.popen(c, 'rb')
-    val = pipe.read()
-    if pipe.close() and not ignore_error:
-        die('Command failed: %s' % c)
-
-    return val
-
-def p4_read_pipe(c, ignore_error=False):
-    real_cmd = p4_build_cmd(c)
-    return read_pipe(real_cmd, ignore_error)
-
-def read_pipe_lines(c):
-    if verbose:
-        sys.stderr.write('Reading pipe: %s\n' % c)
-    ## todo: check return status
-    pipe = os.popen(c, 'rb')
-    val = pipe.readlines()
-    if pipe.close():
-        die('Command failed: %s' % c)
-
-    return val
-
-def p4_read_pipe_lines(c):
-    """Specifically invoke p4 on the command supplied. """
-    real_cmd = p4_build_cmd(c)
-    return read_pipe_lines(real_cmd)
-
-def system(cmd):
-    if verbose:
-        sys.stderr.write("executing %s\n" % cmd)
-    if os.system(cmd) != 0:
-        die("command failed: %s" % cmd)
-
-def p4_system(cmd):
-    """Specifically invoke p4 as the system command. """
-    real_cmd = p4_build_cmd(cmd)
-    return system(real_cmd)
-
-def isP4Exec(kind):
-    """Determine if a Perforce 'kind' should have execute permission
-
-    'p4 help filetypes' gives a list of the types.  If it starts with 'x',
-    or x follows one of a few letters.  Otherwise, if there is an 'x' after
-    a plus sign, it is also executable"""
-    return (re.search(r"(^[cku]?x)|\+.*x", kind) != None)
-
-def setP4ExecBit(file, mode):
-    # Reopens an already open file and changes the execute bit to match
-    # the execute bit setting in the passed in mode.
-
-    p4Type = "+x"
-
-    if not isModeExec(mode):
-        p4Type = getP4OpenedType(file)
-        p4Type = re.sub('^([cku]?)x(.*)', '\\1\\2', p4Type)
-        p4Type = re.sub('(.*?\+.*?)x(.*?)', '\\1\\2', p4Type)
-        if p4Type[-1] == "+":
-            p4Type = p4Type[0:-1]
-
-    p4_system("reopen -t %s %s" % (p4Type, file))
-
-def getP4OpenedType(file):
-    # Returns the perforce file type for the given file.
-
-    result = p4_read_pipe("opened %s" % file)
-    match = re.match(".*\((.+)\)\r?$", result)
-    if match:
-        return match.group(1)
-    else:
-        die("Could not determine file type for %s (result: '%s')" % (file, result))
-
-def diffTreePattern():
-    # This is a simple generator for the diff tree regex pattern. This could be
-    # a class variable if this and parseDiffTreeEntry were a part of a class.
-    pattern = re.compile(':(\d+) (\d+) (\w+) (\w+) ([A-Z])(\d+)?\t(.*?)((\t(.*))|$)')
-    while True:
-        yield pattern
-
-def parseDiffTreeEntry(entry):
-    """Parses a single diff tree entry into its component elements.
-
-    See git-diff-tree(1) manpage for details about the format of the diff
-    output. This method returns a dictionary with the following elements:
-
-    src_mode - The mode of the source file
-    dst_mode - The mode of the destination file
-    src_sha1 - The sha1 for the source file
-    dst_sha1 - The sha1 fr the destination file
-    status - The one letter status of the diff (i.e. 'A', 'M', 'D', etc)
-    status_score - The score for the status (applicable for 'C' and 'R'
-                   statuses). This is None if there is no score.
-    src - The path for the source file.
-    dst - The path for the destination file. This is only present for
-          copy or renames. If it is not present, this is None.
-
-    If the pattern is not matched, None is returned."""
-
-    match = diffTreePattern().next().match(entry)
-    if match:
-        return {
-            'src_mode': match.group(1),
-            'dst_mode': match.group(2),
-            'src_sha1': match.group(3),
-            'dst_sha1': match.group(4),
-            'status': match.group(5),
-            'status_score': match.group(6),
-            'src': match.group(7),
-            'dst': match.group(10)
-        }
-    return None
-
-def isModeExec(mode):
-    # Returns True if the given git mode represents an executable file,
-    # otherwise False.
-    return mode[-3:] == "755"
-
-def isModeExecChanged(src_mode, dst_mode):
-    return isModeExec(src_mode) != isModeExec(dst_mode)
-
-def p4CmdList(cmd, stdin=None, stdin_mode='w+b'):
-    cmd = p4_build_cmd("-G %s" % (cmd))
-    if verbose:
-        sys.stderr.write("Opening pipe: %s\n" % cmd)
-
-    # Use a temporary file to avoid deadlocks without
-    # subprocess.communicate(), which would put another copy
-    # of stdout into memory.
-    stdin_file = None
-    if stdin is not None:
-        stdin_file = tempfile.TemporaryFile(prefix='p4-stdin', mode=stdin_mode)
-        stdin_file.write(stdin)
-        stdin_file.flush()
-        stdin_file.seek(0)
-
-    p4 = subprocess.Popen(cmd, shell=True,
-                          stdin=stdin_file,
-                          stdout=subprocess.PIPE)
-
-    result = []
-    try:
-        while True:
-            entry = marshal.load(p4.stdout)
-            result.append(entry)
-    except EOFError:
-        pass
-    exitCode = p4.wait()
-    if exitCode != 0:
-        entry = {}
-        entry["p4ExitCode"] = exitCode
-        result.append(entry)
-
-    return result
-
-def p4Cmd(cmd):
-    list = p4CmdList(cmd)
-    result = {}
-    for entry in list:
-        result.update(entry)
-    return result;
-
-def p4Where(depotPath):
-    if not depotPath.endswith("/"):
-        depotPath += "/"
-    depotPath = depotPath + "..."
-    outputList = p4CmdList("where %s" % depotPath)
-    output = None
-    for entry in outputList:
-        if "depotFile" in entry:
-            if entry["depotFile"] == depotPath:
-                output = entry
-                break
-        elif "data" in entry:
-            data = entry.get("data")
-            space = data.find(" ")
-            if data[:space] == depotPath:
-                output = entry
-                break
-    if output == None:
-        return ""
-    if output["code"] == "error":
-        return ""
-    clientPath = ""
-    if "path" in output:
-        clientPath = output.get("path")
-    elif "data" in output:
-        data = output.get("data")
-        lastSpace = data.rfind(" ")
-        clientPath = data[lastSpace + 1:]
-
-    if clientPath.endswith("..."):
-        clientPath = clientPath[:-3]
-    return clientPath
-
-def currentGitBranch():
-    return read_pipe("git name-rev HEAD").split(" ")[1].strip()
-
-def isValidGitDir(path):
-    if (os.path.exists(path + "/HEAD")
-        and os.path.exists(path + "/refs") and os.path.exists(path + "/objects")):
-        return True;
-    return False
-
-def parseRevision(ref):
-    return read_pipe("git rev-parse %s" % ref).strip()
-
-def extractLogMessageFromGitCommit(commit):
-    logMessage = ""
-
-    ## fixme: title is first line of commit, not 1st paragraph.
-    foundTitle = False
-    for log in read_pipe_lines("git cat-file commit %s" % commit):
-       if not foundTitle:
-           if len(log) == 1:
-               foundTitle = True
-           continue
-
-       logMessage += log
-    return logMessage
-
-def extractSettingsGitLog(log):
-    values = {}
-    for line in log.split("\n"):
-        line = line.strip()
-        m = re.search (r"^ *\[git-p4: (.*)\]$", line)
-        if not m:
-            continue
-
-        assignments = m.group(1).split (':')
-        for a in assignments:
-            vals = a.split ('=')
-            key = vals[0].strip()
-            val = ('='.join (vals[1:])).strip()
-            if val.endswith ('\"') and val.startswith('"'):
-                val = val[1:-1]
-
-            values[key] = val
-
-    paths = values.get("depot-paths")
-    if not paths:
-        paths = values.get("depot-path")
-    if paths:
-        values['depot-paths'] = paths.split(',')
-    return values
-
-def gitBranchExists(branch):
-    proc = subprocess.Popen(["git", "rev-parse", branch],
-                            stderr=subprocess.PIPE, stdout=subprocess.PIPE);
-    return proc.wait() == 0;
-
-_gitConfig = {}
-def gitConfig(key):
-    if not _gitConfig.has_key(key):
-        _gitConfig[key] = read_pipe("git config %s" % key, ignore_error=True).strip()
-    return _gitConfig[key]
-
-def p4BranchesInGit(branchesAreInRemotes = True):
-    branches = {}
-
-    cmdline = "git rev-parse --symbolic "
-    if branchesAreInRemotes:
-        cmdline += " --remotes"
-    else:
-        cmdline += " --branches"
-
-    for line in read_pipe_lines(cmdline):
-        line = line.strip()
-
-        ## only import to p4/
-        if not line.startswith('p4/') or line == "p4/HEAD":
-            continue
-        branch = line
-
-        # strip off p4
-        branch = re.sub ("^p4/", "", line)
-
-        branches[branch] = parseRevision(line)
-    return branches
-
-def findUpstreamBranchPoint(head = "HEAD"):
-    branches = p4BranchesInGit()
-    # map from depot-path to branch name
-    branchByDepotPath = {}
-    for branch in branches.keys():
-        tip = branches[branch]
-        log = extractLogMessageFromGitCommit(tip)
-        settings = extractSettingsGitLog(log)
-        if settings.has_key("depot-paths"):
-            paths = ",".join(settings["depot-paths"])
-            branchByDepotPath[paths] = "remotes/p4/" + branch
-
-    settings = None
-    parent = 0
-    while parent < 65535:
-        commit = head + "~%s" % parent
-        log = extractLogMessageFromGitCommit(commit)
-        settings = extractSettingsGitLog(log)
-        if settings.has_key("depot-paths"):
-            paths = ",".join(settings["depot-paths"])
-            if branchByDepotPath.has_key(paths):
-                return [branchByDepotPath[paths], settings]
-
-        parent = parent + 1
-
-    return ["", settings]
-
-def createOrUpdateBranchesFromOrigin(localRefPrefix = "refs/remotes/p4/", silent=True):
-    if not silent:
-        print ("Creating/updating branch(es) in %s based on origin branch(es)"
-               % localRefPrefix)
-
-    originPrefix = "origin/p4/"
-
-    for line in read_pipe_lines("git rev-parse --symbolic --remotes"):
-        line = line.strip()
-        if (not line.startswith(originPrefix)) or line.endswith("HEAD"):
-            continue
-
-        headName = line[len(originPrefix):]
-        remoteHead = localRefPrefix + headName
-        originHead = line
-
-        original = extractSettingsGitLog(extractLogMessageFromGitCommit(originHead))
-        if (not original.has_key('depot-paths')
-            or not original.has_key('change')):
-            continue
-
-        update = False
-        if not gitBranchExists(remoteHead):
-            if verbose:
-                print "creating %s" % remoteHead
-            update = True
-        else:
-            settings = extractSettingsGitLog(extractLogMessageFromGitCommit(remoteHead))
-            if settings.has_key('change') > 0:
-                if settings['depot-paths'] == original['depot-paths']:
-                    originP4Change = int(original['change'])
-                    p4Change = int(settings['change'])
-                    if originP4Change > p4Change:
-                        print ("%s (%s) is newer than %s (%s). "
-                               "Updating p4 branch from origin."
-                               % (originHead, originP4Change,
-                                  remoteHead, p4Change))
-                        update = True
-                else:
-                    print ("Ignoring: %s was imported from %s while "
-                           "%s was imported from %s"
-                           % (originHead, ','.join(original['depot-paths']),
-                              remoteHead, ','.join(settings['depot-paths'])))
-
-        if update:
-            system("git update-ref %s %s" % (remoteHead, originHead))
-
-def originP4BranchesExist():
-        return gitBranchExists("origin") or gitBranchExists("origin/p4") or gitBranchExists("origin/p4/master")
-
-def p4ChangesForPaths(depotPaths, changeRange):
-    assert depotPaths
-    output = p4_read_pipe_lines("changes " + ' '.join (["%s...%s" % (p, changeRange)
-                                                        for p in depotPaths]))
-
-    changes = {}
-    for line in output:
-	changeNum = int(line.split(" ")[1])
-	changes[changeNum] = True
-
-    changelist = changes.keys()
-    changelist.sort()
-    return changelist
-
-class Command:
-    def __init__(self):
-        self.usage = "usage: %prog [options]"
-        self.needsGit = True
-
-class P4Debug(Command):
-    def __init__(self):
-        Command.__init__(self)
-        self.options = [
-            optparse.make_option("--verbose", dest="verbose", action="store_true",
-                                 default=False),
-            ]
-        self.description = "A tool to debug the output of p4 -G."
-        self.needsGit = False
-        self.verbose = False
-
-    def run(self, args):
-        j = 0
-        for output in p4CmdList(" ".join(args)):
-            print 'Element: %d' % j
-            j += 1
-            print output
-        return True
-
-class P4RollBack(Command):
-    def __init__(self):
-        Command.__init__(self)
-        self.options = [
-            optparse.make_option("--verbose", dest="verbose", action="store_true"),
-            optparse.make_option("--local", dest="rollbackLocalBranches", action="store_true")
-        ]
-        self.description = "A tool to debug the multi-branch import. Don't use :)"
-        self.verbose = False
-        self.rollbackLocalBranches = False
-
-    def run(self, args):
-        if len(args) != 1:
-            return False
-        maxChange = int(args[0])
-
-        if "p4ExitCode" in p4Cmd("changes -m 1"):
-            die("Problems executing p4");
-
-        if self.rollbackLocalBranches:
-            refPrefix = "refs/heads/"
-            lines = read_pipe_lines("git rev-parse --symbolic --branches")
-        else:
-            refPrefix = "refs/remotes/"
-            lines = read_pipe_lines("git rev-parse --symbolic --remotes")
-
-        for line in lines:
-            if self.rollbackLocalBranches or (line.startswith("p4/") and line != "p4/HEAD\n"):
-                line = line.strip()
-                ref = refPrefix + line
-                log = extractLogMessageFromGitCommit(ref)
-                settings = extractSettingsGitLog(log)
-
-                depotPaths = settings['depot-paths']
-                change = settings['change']
-
-                changed = False
-
-                if len(p4Cmd("changes -m 1 "  + ' '.join (['%s...@%s' % (p, maxChange)
-                                                           for p in depotPaths]))) == 0:
-                    print "Branch %s did not exist at change %s, deleting." % (ref, maxChange)
-                    system("git update-ref -d %s `git rev-parse %s`" % (ref, ref))
-                    continue
-
-                while change and int(change) > maxChange:
-                    changed = True
-                    if self.verbose:
-                        print "%s is at %s ; rewinding towards %s" % (ref, change, maxChange)
-                    system("git update-ref %s \"%s^\"" % (ref, ref))
-                    log = extractLogMessageFromGitCommit(ref)
-                    settings =  extractSettingsGitLog(log)
-
-
-                    depotPaths = settings['depot-paths']
-                    change = settings['change']
-
-                if changed:
-                    print "%s rewound to %s" % (ref, change)
-
-        return True
-
-class P4Submit(Command):
-    def __init__(self):
-        Command.__init__(self)
-        self.options = [
-                optparse.make_option("--verbose", dest="verbose", action="store_true"),
-                optparse.make_option("--origin", dest="origin"),
-                optparse.make_option("-M", dest="detectRename", action="store_true"),
-        ]
-        self.description = "Submit changes from git to the perforce depot."
-        self.usage += " [name of git branch to submit into perforce depot]"
-        self.interactive = True
-        self.origin = ""
-        self.detectRename = False
-        self.verbose = False
-        self.isWindows = (platform.system() == "Windows")
-
-    def check(self):
-        if len(p4CmdList("opened ...")) > 0:
-            die("You have files opened with perforce! Close them before starting the sync.")
-
-    # replaces everything between 'Description:' and the next P4 submit template field with the
-    # commit message
-    def prepareLogMessage(self, template, message):
-        result = ""
-
-        inDescriptionSection = False
-
-        for line in template.split("\n"):
-            if line.startswith("#"):
-                result += line + "\n"
-                continue
-
-            if inDescriptionSection:
-                if line.startswith("Files:"):
-                    inDescriptionSection = False
-                else:
-                    continue
-            else:
-                if line.startswith("Description:"):
-                    inDescriptionSection = True
-                    line += "\n"
-                    for messageLine in message.split("\n"):
-                        line += "\t" + messageLine + "\n"
-
-            result += line + "\n"
-
-        return result
-
-    def prepareSubmitTemplate(self):
-        # remove lines in the Files section that show changes to files outside the depot path we're committing into
-        template = ""
-        inFilesSection = False
-        for line in p4_read_pipe_lines("change -o"):
-            if line.endswith("\r\n"):
-                line = line[:-2] + "\n"
-            if inFilesSection:
-                if line.startswith("\t"):
-                    # path starts and ends with a tab
-                    path = line[1:]
-                    lastTab = path.rfind("\t")
-                    if lastTab != -1:
-                        path = path[:lastTab]
-                        if not path.startswith(self.depotPath):
-                            continue
-                else:
-                    inFilesSection = False
-            else:
-                if line.startswith("Files:"):
-                    inFilesSection = True
-
-            template += line
-
-        return template
-
-    def applyCommit(self, id):
-        print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id))
-        diffOpts = ("", "-M")[self.detectRename]
-        diff = read_pipe_lines("git diff-tree -r %s \"%s^\" \"%s\"" % (diffOpts, id, id))
-        filesToAdd = set()
-        filesToDelete = set()
-        editedFiles = set()
-        filesToChangeExecBit = {}
-        for line in diff:
-            diff = parseDiffTreeEntry(line)
-            modifier = diff['status']
-            path = diff['src']
-            if modifier == "M":
-                p4_system("edit \"%s\"" % path)
-                if isModeExecChanged(diff['src_mode'], diff['dst_mode']):
-                    filesToChangeExecBit[path] = diff['dst_mode']
-                editedFiles.add(path)
-            elif modifier == "A":
-                filesToAdd.add(path)
-                filesToChangeExecBit[path] = diff['dst_mode']
-                if path in filesToDelete:
-                    filesToDelete.remove(path)
-            elif modifier == "D":
-                filesToDelete.add(path)
-                if path in filesToAdd:
-                    filesToAdd.remove(path)
-            elif modifier == "R":
-                src, dest = diff['src'], diff['dst']
-                p4_system("integrate -Dt \"%s\" \"%s\"" % (src, dest))
-                p4_system("edit \"%s\"" % (dest))
-                if isModeExecChanged(diff['src_mode'], diff['dst_mode']):
-                    filesToChangeExecBit[dest] = diff['dst_mode']
-                os.unlink(dest)
-                editedFiles.add(dest)
-                filesToDelete.add(src)
-            else:
-                die("unknown modifier %s for %s" % (modifier, path))
-
-        diffcmd = "git format-patch -k --stdout \"%s^\"..\"%s\"" % (id, id)
-        patchcmd = diffcmd + " | git apply "
-        tryPatchCmd = patchcmd + "--check -"
-        applyPatchCmd = patchcmd + "--check --apply -"
-
-        if os.system(tryPatchCmd) != 0:
-            print "Unfortunately applying the change failed!"
-            print "What do you want to do?"
-            response = "x"
-            while response != "s" and response != "a" and response != "w":
-                response = raw_input("[s]kip this patch / [a]pply the patch forcibly "
-                                     "and with .rej files / [w]rite the patch to a file (patch.txt) ")
-            if response == "s":
-                print "Skipping! Good luck with the next patches..."
-                for f in editedFiles:
-                    p4_system("revert \"%s\"" % f);
-                for f in filesToAdd:
-                    system("rm %s" %f)
-                return
-            elif response == "a":
-                os.system(applyPatchCmd)
-                if len(filesToAdd) > 0:
-                    print "You may also want to call p4 add on the following files:"
-                    print " ".join(filesToAdd)
-                if len(filesToDelete):
-                    print "The following files should be scheduled for deletion with p4 delete:"
-                    print " ".join(filesToDelete)
-                die("Please resolve and submit the conflict manually and "
-                    + "continue afterwards with git-p4 submit --continue")
-            elif response == "w":
-                system(diffcmd + " > patch.txt")
-                print "Patch saved to patch.txt in %s !" % self.clientPath
-                die("Please resolve and submit the conflict manually and "
-                    "continue afterwards with git-p4 submit --continue")
-
-        system(applyPatchCmd)
-
-        for f in filesToAdd:
-            p4_system("add \"%s\"" % f)
-        for f in filesToDelete:
-            p4_system("revert \"%s\"" % f)
-            p4_system("delete \"%s\"" % f)
-
-        # Set/clear executable bits
-        for f in filesToChangeExecBit.keys():
-            mode = filesToChangeExecBit[f]
-            setP4ExecBit(f, mode)
-
-        logMessage = extractLogMessageFromGitCommit(id)
-        logMessage = logMessage.strip()
-
-        template = self.prepareSubmitTemplate()
-
-        if self.interactive:
-            submitTemplate = self.prepareLogMessage(template, logMessage)
-            if os.environ.has_key("P4DIFF"):
-                del(os.environ["P4DIFF"])
-            diff = p4_read_pipe("diff -du ...")
-
-            newdiff = ""
-            for newFile in filesToAdd:
-                newdiff += "==== new file ====\n"
-                newdiff += "--- /dev/null\n"
-                newdiff += "+++ %s\n" % newFile
-                f = open(newFile, "r")
-                for line in f.readlines():
-                    newdiff += "+" + line
-                f.close()
-
-            separatorLine = "######## everything below this line is just the diff #######\n"
-
-            [handle, fileName] = tempfile.mkstemp()
-            tmpFile = os.fdopen(handle, "w+")
-            if self.isWindows:
-                submitTemplate = submitTemplate.replace("\n", "\r\n")
-                separatorLine = separatorLine.replace("\n", "\r\n")
-                newdiff = newdiff.replace("\n", "\r\n")
-            tmpFile.write(submitTemplate + separatorLine + diff + newdiff)
-            tmpFile.close()
-            mtime = os.stat(fileName).st_mtime
-            defaultEditor = "vi"
-            if platform.system() == "Windows":
-                defaultEditor = "notepad"
-            if os.environ.has_key("P4EDITOR"):
-                editor = os.environ.get("P4EDITOR")
-            else:
-                editor = os.environ.get("EDITOR", defaultEditor);
-            system(editor + " " + fileName)
-
-            response = "y"
-            if os.stat(fileName).st_mtime <= mtime:
-                response = "x"
-                while response != "y" and response != "n":
-                    response = raw_input("Submit template unchanged. Submit anyway? [y]es, [n]o (skip this patch) ")
-
-            if response == "y":
-                tmpFile = open(fileName, "rb")
-                message = tmpFile.read()
-                tmpFile.close()
-                submitTemplate = message[:message.index(separatorLine)]
-                if self.isWindows:
-                    submitTemplate = submitTemplate.replace("\r\n", "\n")
-                p4_write_pipe("submit -i", submitTemplate)
-            else:
-                for f in editedFiles:
-                    p4_system("revert \"%s\"" % f);
-                for f in filesToAdd:
-                    p4_system("revert \"%s\"" % f);
-                    system("rm %s" %f)
-
-            os.remove(fileName)
-        else:
-            fileName = "submit.txt"
-            file = open(fileName, "w+")
-            file.write(self.prepareLogMessage(template, logMessage))
-            file.close()
-            print ("Perforce submit template written as %s. "
-                   + "Please review/edit and then use p4 submit -i < %s to submit directly!"
-                   % (fileName, fileName))
-
-    def run(self, args):
-        if len(args) == 0:
-            self.master = currentGitBranch()
-            if len(self.master) == 0 or not gitBranchExists("refs/heads/%s" % self.master):
-                die("Detecting current git branch failed!")
-        elif len(args) == 1:
-            self.master = args[0]
-        else:
-            return False
-
-        allowSubmit = gitConfig("git-p4.allowSubmit")
-        if len(allowSubmit) > 0 and not self.master in allowSubmit.split(","):
-            die("%s is not in git-p4.allowSubmit" % self.master)
-
-        [upstream, settings] = findUpstreamBranchPoint()
-        self.depotPath = settings['depot-paths'][0]
-        if len(self.origin) == 0:
-            self.origin = upstream
-
-        if self.verbose:
-            print "Origin branch is " + self.origin
-
-        if len(self.depotPath) == 0:
-            print "Internal error: cannot locate perforce depot path from existing branches"
-            sys.exit(128)
-
-        self.clientPath = p4Where(self.depotPath)
-
-        if len(self.clientPath) == 0:
-            print "Error: Cannot locate perforce checkout of %s in client view" % self.depotPath
-            sys.exit(128)
-
-        print "Perforce checkout for depot path %s located at %s" % (self.depotPath, self.clientPath)
-        self.oldWorkingDirectory = os.getcwd()
-
-        chdir(self.clientPath)
-        print "Syncronizing p4 checkout..."
-        p4_system("sync ...")
-
-        self.check()
-
-        commits = []
-        for line in read_pipe_lines("git rev-list --no-merges %s..%s" % (self.origin, self.master)):
-            commits.append(line.strip())
-        commits.reverse()
-
-        while len(commits) > 0:
-            commit = commits[0]
-            commits = commits[1:]
-            self.applyCommit(commit)
-            if not self.interactive:
-                break
-
-        if len(commits) == 0:
-            print "All changes applied!"
-            chdir(self.oldWorkingDirectory)
-
-            sync = P4Sync()
-            sync.run([])
-
-            rebase = P4Rebase()
-            rebase.rebase()
-
-        return True
-
-class P4Sync(Command):
-    def __init__(self):
-        Command.__init__(self)
-        self.options = [
-                optparse.make_option("--branch", dest="branch"),
-                optparse.make_option("--detect-branches", dest="detectBranches", action="store_true"),
-                optparse.make_option("--changesfile", dest="changesFile"),
-                optparse.make_option("--silent", dest="silent", action="store_true"),
-                optparse.make_option("--detect-labels", dest="detectLabels", action="store_true"),
-                optparse.make_option("--verbose", dest="verbose", action="store_true"),
-                optparse.make_option("--import-local", dest="importIntoRemotes", action="store_false",
-                                     help="Import into refs/heads/ , not refs/remotes"),
-                optparse.make_option("--max-changes", dest="maxChanges"),
-                optparse.make_option("--keep-path", dest="keepRepoPath", action='store_true',
-                                     help="Keep entire BRANCH/DIR/SUBDIR prefix during import"),
-                optparse.make_option("--use-client-spec", dest="useClientSpec", action='store_true',
-                                     help="Only sync files that are included in the Perforce Client Spec")
-        ]
-        self.description = """Imports from Perforce into a git repository.\n
-    example:
-    //depot/my/project/ -- to import the current head
-    //depot/my/project/@all -- to import everything
-    //depot/my/project/@1,6 -- to import only from revision 1 to 6
-
-    (a ... is not needed in the path p4 specification, it's added implicitly)"""
-
-        self.usage += " //depot/path[@revRange]"
-        self.silent = False
-        self.createdBranches = Set()
-        self.committedChanges = Set()
-        self.branch = ""
-        self.detectBranches = False
-        self.detectLabels = False
-        self.changesFile = ""
-        self.syncWithOrigin = True
-        self.verbose = False
-        self.importIntoRemotes = True
-        self.maxChanges = ""
-        self.isWindows = (platform.system() == "Windows")
-        self.keepRepoPath = False
-        self.depotPaths = None
-        self.p4BranchesInGit = []
-        self.cloneExclude = []
-        self.useClientSpec = False
-        self.clientSpecDirs = []
-
-        if gitConfig("git-p4.syncFromOrigin") == "false":
-            self.syncWithOrigin = False
-
-    def extractFilesFromCommit(self, commit):
-        self.cloneExclude = [re.sub(r"\.\.\.$", "", path)
-                             for path in self.cloneExclude]
-        files = []
-        fnum = 0
-        while commit.has_key("depotFile%s" % fnum):
-            path =  commit["depotFile%s" % fnum]
-
-            if [p for p in self.cloneExclude
-                if path.startswith (p)]:
-                found = False
-            else:
-                found = [p for p in self.depotPaths
-                         if path.startswith (p)]
-            if not found:
-                fnum = fnum + 1
-                continue
-
-            file = {}
-            file["path"] = path
-            file["rev"] = commit["rev%s" % fnum]
-            file["action"] = commit["action%s" % fnum]
-            file["type"] = commit["type%s" % fnum]
-            files.append(file)
-            fnum = fnum + 1
-        return files
-
-    def stripRepoPath(self, path, prefixes):
-        if self.keepRepoPath:
-            prefixes = [re.sub("^(//[^/]+/).*", r'\1', prefixes[0])]
-
-        for p in prefixes:
-            if path.startswith(p):
-                path = path[len(p):]
-
-        return path
-
-    def splitFilesIntoBranches(self, commit):
-        branches = {}
-        fnum = 0
-        while commit.has_key("depotFile%s" % fnum):
-            path =  commit["depotFile%s" % fnum]
-            found = [p for p in self.depotPaths
-                     if path.startswith (p)]
-            if not found:
-                fnum = fnum + 1
-                continue
-
-            file = {}
-            file["path"] = path
-            file["rev"] = commit["rev%s" % fnum]
-            file["action"] = commit["action%s" % fnum]
-            file["type"] = commit["type%s" % fnum]
-            fnum = fnum + 1
-
-            relPath = self.stripRepoPath(path, self.depotPaths)
-
-            for branch in self.knownBranches.keys():
-
-                # add a trailing slash so that a commit into qt/4.2foo doesn't end up in qt/4.2
-                if relPath.startswith(branch + "/"):
-                    if branch not in branches:
-                        branches[branch] = []
-                    branches[branch].append(file)
-                    break
-
-        return branches
-
-    ## Should move this out, doesn't use SELF.
-    def readP4Files(self, files):
-        filesForCommit = []
-        filesToRead = []
-
-        for f in files:
-            includeFile = True
-            for val in self.clientSpecDirs:
-                if f['path'].startswith(val[0]):
-                    if val[1] <= 0:
-                        includeFile = False
-                    break
-
-            if includeFile:
-                filesForCommit.append(f)
-                if f['action'] not in ('delete', 'purge'):
-                    filesToRead.append(f)
-
-        filedata = []
-        if len(filesToRead) > 0:
-            filedata = p4CmdList('-x - print',
-                                 stdin='\n'.join(['%s#%s' % (f['path'], f['rev'])
-                                                  for f in filesToRead]),
-                                 stdin_mode='w+')
-
-            if "p4ExitCode" in filedata[0]:
-                die("Problems executing p4. Error: [%d]."
-                    % (filedata[0]['p4ExitCode']));
-
-        j = 0;
-        contents = {}
-        while j < len(filedata):
-            stat = filedata[j]
-            j += 1
-            text = ''
-            while j < len(filedata) and filedata[j]['code'] in ('text', 'unicode', 'binary'):
-                text += filedata[j]['data']
-                del filedata[j]['data']
-                j += 1
-
-            if not stat.has_key('depotFile'):
-                sys.stderr.write("p4 print fails with: %s\n" % repr(stat))
-                continue
-
-            if stat['type'] in ('text+ko', 'unicode+ko', 'binary+ko'):
-                text = re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', text)
-            elif stat['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'):
-                text = re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$\n]*\$',r'$\1$', text)
-
-            contents[stat['depotFile']] = text
-
-        for f in filesForCommit:
-            path = f['path']
-            if contents.has_key(path):
-                f['data'] = contents[path]
-
-        return filesForCommit
-
-    def commit(self, details, files, branch, branchPrefixes, parent = ""):
-        epoch = details["time"]
-        author = details["user"]
-
-        if self.verbose:
-            print "commit into %s" % branch
-
-        # start with reading files; if that fails, we should not
-        # create a commit.
-        new_files = []
-        for f in files:
-            if [p for p in branchPrefixes if f['path'].startswith(p)]:
-                new_files.append (f)
-            else:
-                sys.stderr.write("Ignoring file outside of prefix: %s\n" % path)
-        files = self.readP4Files(new_files)
-
-        self.gitStream.write("commit %s\n" % branch)
-#        gitStream.write("mark :%s\n" % details["change"])
-        self.committedChanges.add(int(details["change"]))
-        committer = ""
-        if author not in self.users:
-            self.getUserMapFromPerforceServer()
-        if author in self.users:
-            committer = "%s %s %s" % (self.users[author], epoch, self.tz)
-        else:
-            committer = "%s <a@b> %s %s" % (author, epoch, self.tz)
-
-        self.gitStream.write("committer %s\n" % committer)
-
-        self.gitStream.write("data <<EOT\n")
-        self.gitStream.write(details["desc"])
-        self.gitStream.write("\n[git-p4: depot-paths = \"%s\": change = %s"
-                             % (','.join (branchPrefixes), details["change"]))
-        if len(details['options']) > 0:
-            self.gitStream.write(": options = %s" % details['options'])
-        self.gitStream.write("]\nEOT\n\n")
-
-        if len(parent) > 0:
-            if self.verbose:
-                print "parent %s" % parent
-            self.gitStream.write("from %s\n" % parent)
-
-        for file in files:
-            if file["type"] == "apple":
-                print "\nfile %s is a strange apple file that forks. Ignoring!" % file['path']
-                continue
-
-            relPath = self.stripRepoPath(file['path'], branchPrefixes)
-            if file["action"] in ("delete", "purge"):
-                self.gitStream.write("D %s\n" % relPath)
-            else:
-                data = file['data']
-
-                mode = "644"
-                if isP4Exec(file["type"]):
-                    mode = "755"
-                elif file["type"] == "symlink":
-                    mode = "120000"
-                    # p4 print on a symlink contains "target\n", so strip it off
-                    data = data[:-1]
-
-                if self.isWindows and file["type"].endswith("text"):
-                    data = data.replace("\r\n", "\n")
-
-                self.gitStream.write("M %s inline %s\n" % (mode, relPath))
-                self.gitStream.write("data %s\n" % len(data))
-                self.gitStream.write(data)
-                self.gitStream.write("\n")
-
-        self.gitStream.write("\n")
-
-        change = int(details["change"])
-
-        if self.labels.has_key(change):
-            label = self.labels[change]
-            labelDetails = label[0]
-            labelRevisions = label[1]
-            if self.verbose:
-                print "Change %s is labelled %s" % (change, labelDetails)
-
-            files = p4CmdList("files " + ' '.join (["%s...@%s" % (p, change)
-                                                    for p in branchPrefixes]))
-
-            if len(files) == len(labelRevisions):
-
-                cleanedFiles = {}
-                for info in files:
-                    if info["action"] in ("delete", "purge"):
-                        continue
-                    cleanedFiles[info["depotFile"]] = info["rev"]
-
-                if cleanedFiles == labelRevisions:
-                    self.gitStream.write("tag tag_%s\n" % labelDetails["label"])
-                    self.gitStream.write("from %s\n" % branch)
-
-                    owner = labelDetails["Owner"]
-                    tagger = ""
-                    if author in self.users:
-                        tagger = "%s %s %s" % (self.users[owner], epoch, self.tz)
-                    else:
-                        tagger = "%s <a@b> %s %s" % (owner, epoch, self.tz)
-                    self.gitStream.write("tagger %s\n" % tagger)
-                    self.gitStream.write("data <<EOT\n")
-                    self.gitStream.write(labelDetails["Description"])
-                    self.gitStream.write("EOT\n\n")
-
-                else:
-                    if not self.silent:
-                        print ("Tag %s does not match with change %s: files do not match."
-                               % (labelDetails["label"], change))
-
-            else:
-                if not self.silent:
-                    print ("Tag %s does not match with change %s: file count is different."
-                           % (labelDetails["label"], change))
-
-    def getUserCacheFilename(self):
-        home = os.environ.get("HOME", os.environ.get("USERPROFILE"))
-        return home + "/.gitp4-usercache.txt"
-
-    def getUserMapFromPerforceServer(self):
-        if self.userMapFromPerforceServer:
-            return
-        self.users = {}
-
-        for output in p4CmdList("users"):
-            if not output.has_key("User"):
-                continue
-            self.users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">"
-
-
-        s = ''
-        for (key, val) in self.users.items():
-	    s += "%s\t%s\n" % (key.expandtabs(1), val.expandtabs(1))
-
-        open(self.getUserCacheFilename(), "wb").write(s)
-        self.userMapFromPerforceServer = True
-
-    def loadUserMapFromCache(self):
-        self.users = {}
-        self.userMapFromPerforceServer = False
-        try:
-            cache = open(self.getUserCacheFilename(), "rb")
-            lines = cache.readlines()
-            cache.close()
-            for line in lines:
-                entry = line.strip().split("\t")
-                self.users[entry[0]] = entry[1]
-        except IOError:
-            self.getUserMapFromPerforceServer()
-
-    def getLabels(self):
-        self.labels = {}
-
-        l = p4CmdList("labels %s..." % ' '.join (self.depotPaths))
-        if len(l) > 0 and not self.silent:
-            print "Finding files belonging to labels in %s" % `self.depotPaths`
-
-        for output in l:
-            label = output["label"]
-            revisions = {}
-            newestChange = 0
-            if self.verbose:
-                print "Querying files for label %s" % label
-            for file in p4CmdList("files "
-                                  +  ' '.join (["%s...@%s" % (p, label)
-                                                for p in self.depotPaths])):
-                revisions[file["depotFile"]] = file["rev"]
-                change = int(file["change"])
-                if change > newestChange:
-                    newestChange = change
-
-            self.labels[newestChange] = [output, revisions]
-
-        if self.verbose:
-            print "Label changes: %s" % self.labels.keys()
-
-    def guessProjectName(self):
-        for p in self.depotPaths:
-            if p.endswith("/"):
-                p = p[:-1]
-            p = p[p.strip().rfind("/") + 1:]
-            if not p.endswith("/"):
-               p += "/"
-            return p
-
-    def getBranchMapping(self):
-        lostAndFoundBranches = set()
-
-        for info in p4CmdList("branches"):
-            details = p4Cmd("branch -o %s" % info["branch"])
-            viewIdx = 0
-            while details.has_key("View%s" % viewIdx):
-                paths = details["View%s" % viewIdx].split(" ")
-                viewIdx = viewIdx + 1
-                # require standard //depot/foo/... //depot/bar/... mapping
-                if len(paths) != 2 or not paths[0].endswith("/...") or not paths[1].endswith("/..."):
-                    continue
-                source = paths[0]
-                destination = paths[1]
-                ## HACK
-                if source.startswith(self.depotPaths[0]) and destination.startswith(self.depotPaths[0]):
-                    source = source[len(self.depotPaths[0]):-4]
-                    destination = destination[len(self.depotPaths[0]):-4]
-
-                    if destination in self.knownBranches:
-                        if not self.silent:
-                            print "p4 branch %s defines a mapping from %s to %s" % (info["branch"], source, destination)
-                            print "but there exists another mapping from %s to %s already!" % (self.knownBranches[destination], destination)
-                        continue
-
-                    self.knownBranches[destination] = source
-
-                    lostAndFoundBranches.discard(destination)
-
-                    if source not in self.knownBranches:
-                        lostAndFoundBranches.add(source)
-
-
-        for branch in lostAndFoundBranches:
-            self.knownBranches[branch] = branch
-
-    def getBranchMappingFromGitBranches(self):
-        branches = p4BranchesInGit(self.importIntoRemotes)
-        for branch in branches.keys():
-            if branch == "master":
-                branch = "main"
-            else:
-                branch = branch[len(self.projectName):]
-            self.knownBranches[branch] = branch
-
-    def listExistingP4GitBranches(self):
-        # branches holds mapping from name to commit
-        branches = p4BranchesInGit(self.importIntoRemotes)
-        self.p4BranchesInGit = branches.keys()
-        for branch in branches.keys():
-            self.initialParents[self.refPrefix + branch] = branches[branch]
-
-    def updateOptionDict(self, d):
-        option_keys = {}
-        if self.keepRepoPath:
-            option_keys['keepRepoPath'] = 1
-
-        d["options"] = ' '.join(sorted(option_keys.keys()))
-
-    def readOptions(self, d):
-        self.keepRepoPath = (d.has_key('options')
-                             and ('keepRepoPath' in d['options']))
-
-    def gitRefForBranch(self, branch):
-        if branch == "main":
-            return self.refPrefix + "master"
-
-        if len(branch) <= 0:
-            return branch
-
-        return self.refPrefix + self.projectName + branch
-
-    def gitCommitByP4Change(self, ref, change):
-        if self.verbose:
-            print "looking in ref " + ref + " for change %s using bisect..." % change
-
-        earliestCommit = ""
-        latestCommit = parseRevision(ref)
-
-        while True:
-            if self.verbose:
-                print "trying: earliest %s latest %s" % (earliestCommit, latestCommit)
-            next = read_pipe("git rev-list --bisect %s %s" % (latestCommit, earliestCommit)).strip()
-            if len(next) == 0:
-                if self.verbose:
-                    print "argh"
-                return ""
-            log = extractLogMessageFromGitCommit(next)
-            settings = extractSettingsGitLog(log)
-            currentChange = int(settings['change'])
-            if self.verbose:
-                print "current change %s" % currentChange
-
-            if currentChange == change:
-                if self.verbose:
-                    print "found %s" % next
-                return next
-
-            if currentChange < change:
-                earliestCommit = "^%s" % next
-            else:
-                latestCommit = "%s" % next
-
-        return ""
-
-    def importNewBranch(self, branch, maxChange):
-        # make fast-import flush all changes to disk and update the refs using the checkpoint
-        # command so that we can try to find the branch parent in the git history
-        self.gitStream.write("checkpoint\n\n");
-        self.gitStream.flush();
-        branchPrefix = self.depotPaths[0] + branch + "/"
-        range = "@1,%s" % maxChange
-        #print "prefix" + branchPrefix
-        changes = p4ChangesForPaths([branchPrefix], range)
-        if len(changes) <= 0:
-            return False
-        firstChange = changes[0]
-        #print "first change in branch: %s" % firstChange
-        sourceBranch = self.knownBranches[branch]
-        sourceDepotPath = self.depotPaths[0] + sourceBranch
-        sourceRef = self.gitRefForBranch(sourceBranch)
-        #print "source " + sourceBranch
-
-        branchParentChange = int(p4Cmd("changes -m 1 %s...@1,%s" % (sourceDepotPath, firstChange))["change"])
-        #print "branch parent: %s" % branchParentChange
-        gitParent = self.gitCommitByP4Change(sourceRef, branchParentChange)
-        if len(gitParent) > 0:
-            self.initialParents[self.gitRefForBranch(branch)] = gitParent
-            #print "parent git commit: %s" % gitParent
-
-        self.importChanges(changes)
-        return True
-
-    def importChanges(self, changes):
-        cnt = 1
-        for change in changes:
-            description = p4Cmd("describe %s" % change)
-            self.updateOptionDict(description)
-
-            if not self.silent:
-                sys.stdout.write("\rImporting revision %s (%s%%)" % (change, cnt * 100 / len(changes)))
-                sys.stdout.flush()
-            cnt = cnt + 1
-
-            try:
-                if self.detectBranches:
-                    branches = self.splitFilesIntoBranches(description)
-                    for branch in branches.keys():
-                        ## HACK  --hwn
-                        branchPrefix = self.depotPaths[0] + branch + "/"
-
-                        parent = ""
-
-                        filesForCommit = branches[branch]
-
-                        if self.verbose:
-                            print "branch is %s" % branch
-
-                        self.updatedBranches.add(branch)
-
-                        if branch not in self.createdBranches:
-                            self.createdBranches.add(branch)
-                            parent = self.knownBranches[branch]
-                            if parent == branch:
-                                parent = ""
-                            else:
-                                fullBranch = self.projectName + branch
-                                if fullBranch not in self.p4BranchesInGit:
-                                    if not self.silent:
-                                        print("\n    Importing new branch %s" % fullBranch);
-                                    if self.importNewBranch(branch, change - 1):
-                                        parent = ""
-                                        self.p4BranchesInGit.append(fullBranch)
-                                    if not self.silent:
-                                        print("\n    Resuming with change %s" % change);
-
-                                if self.verbose:
-                                    print "parent determined through known branches: %s" % parent
-
-                        branch = self.gitRefForBranch(branch)
-                        parent = self.gitRefForBranch(parent)
-
-                        if self.verbose:
-                            print "looking for initial parent for %s; current parent is %s" % (branch, parent)
-
-                        if len(parent) == 0 and branch in self.initialParents:
-                            parent = self.initialParents[branch]
-                            del self.initialParents[branch]
-
-                        self.commit(description, filesForCommit, branch, [branchPrefix], parent)
-                else:
-                    files = self.extractFilesFromCommit(description)
-                    self.commit(description, files, self.branch, self.depotPaths,
-                                self.initialParent)
-                    self.initialParent = ""
-            except IOError:
-                print self.gitError.read()
-                sys.exit(1)
-
-    def importHeadRevision(self, revision):
-        print "Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), revision, self.branch)
-
-        details = { "user" : "git perforce import user", "time" : int(time.time()) }
-        details["desc"] = ("Initial import of %s from the state at revision %s"
-                           % (' '.join(self.depotPaths), revision))
-        details["change"] = revision
-        newestRevision = 0
-
-        fileCnt = 0
-        for info in p4CmdList("files "
-                              +  ' '.join(["%s...%s"
-                                           % (p, revision)
-                                           for p in self.depotPaths])):
-
-            if info['code'] == 'error':
-                sys.stderr.write("p4 returned an error: %s\n"
-                                 % info['data'])
-                sys.exit(1)
-
-
-            change = int(info["change"])
-            if change > newestRevision:
-                newestRevision = change
-
-            if info["action"] in ("delete", "purge"):
-                # don't increase the file cnt, otherwise details["depotFile123"] will have gaps!
-                #fileCnt = fileCnt + 1
-                continue
-
-            for prop in ["depotFile", "rev", "action", "type" ]:
-                details["%s%s" % (prop, fileCnt)] = info[prop]
-
-            fileCnt = fileCnt + 1
-
-        details["change"] = newestRevision
-        self.updateOptionDict(details)
-        try:
-            self.commit(details, self.extractFilesFromCommit(details), self.branch, self.depotPaths)
-        except IOError:
-            print "IO error with git fast-import. Is your git version recent enough?"
-            print self.gitError.read()
-
-
-    def getClientSpec(self):
-        specList = p4CmdList( "client -o" )
-        temp = {}
-        for entry in specList:
-            for k,v in entry.iteritems():
-                if k.startswith("View"):
-                    if v.startswith('"'):
-                        start = 1
-                    else:
-                        start = 0
-                    index = v.find("...")
-                    v = v[start:index]
-                    if v.startswith("-"):
-                        v = v[1:]
-                        temp[v] = -len(v)
-                    else:
-                        temp[v] = len(v)
-        self.clientSpecDirs = temp.items()
-        self.clientSpecDirs.sort( lambda x, y: abs( y[1] ) - abs( x[1] ) )
-
-    def run(self, args):
-        self.depotPaths = []
-        self.changeRange = ""
-        self.initialParent = ""
-        self.previousDepotPaths = []
-
-        # map from branch depot path to parent branch
-        self.knownBranches = {}
-        self.initialParents = {}
-        self.hasOrigin = originP4BranchesExist()
-        if not self.syncWithOrigin:
-            self.hasOrigin = False
-
-        if self.importIntoRemotes:
-            self.refPrefix = "refs/remotes/p4/"
-        else:
-            self.refPrefix = "refs/heads/p4/"
-
-        if self.syncWithOrigin and self.hasOrigin:
-            if not self.silent:
-                print "Syncing with origin first by calling git fetch origin"
-            system("git fetch origin")
-
-        if len(self.branch) == 0:
-            self.branch = self.refPrefix + "master"
-            if gitBranchExists("refs/heads/p4") and self.importIntoRemotes:
-                system("git update-ref %s refs/heads/p4" % self.branch)
-                system("git branch -D p4");
-            # create it /after/ importing, when master exists
-            if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes and gitBranchExists(self.branch):
-                system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch))
-
-        if self.useClientSpec or gitConfig("git-p4.useclientspec") == "true":
-            self.getClientSpec()
-
-        # TODO: should always look at previous commits,
-        # merge with previous imports, if possible.
-        if args == []:
-            if self.hasOrigin:
-                createOrUpdateBranchesFromOrigin(self.refPrefix, self.silent)
-            self.listExistingP4GitBranches()
-
-            if len(self.p4BranchesInGit) > 1:
-                if not self.silent:
-                    print "Importing from/into multiple branches"
-                self.detectBranches = True
-
-            if self.verbose:
-                print "branches: %s" % self.p4BranchesInGit
-
-            p4Change = 0
-            for branch in self.p4BranchesInGit:
-                logMsg =  extractLogMessageFromGitCommit(self.refPrefix + branch)
-
-                settings = extractSettingsGitLog(logMsg)
-
-                self.readOptions(settings)
-                if (settings.has_key('depot-paths')
-                    and settings.has_key ('change')):
-                    change = int(settings['change']) + 1
-                    p4Change = max(p4Change, change)
-
-                    depotPaths = sorted(settings['depot-paths'])
-                    if self.previousDepotPaths == []:
-                        self.previousDepotPaths = depotPaths
-                    else:
-                        paths = []
-                        for (prev, cur) in zip(self.previousDepotPaths, depotPaths):
-                            for i in range(0, min(len(cur), len(prev))):
-                                if cur[i] <> prev[i]:
-                                    i = i - 1
-                                    break
-
-                            paths.append (cur[:i + 1])
-
-                        self.previousDepotPaths = paths
-
-            if p4Change > 0:
-                self.depotPaths = sorted(self.previousDepotPaths)
-                self.changeRange = "@%s,#head" % p4Change
-                if not self.detectBranches:
-                    self.initialParent = parseRevision(self.branch)
-                if not self.silent and not self.detectBranches:
-                    print "Performing incremental import into %s git branch" % self.branch
-
-        if not self.branch.startswith("refs/"):
-            self.branch = "refs/heads/" + self.branch
-
-        if len(args) == 0 and self.depotPaths:
-            if not self.silent:
-                print "Depot paths: %s" % ' '.join(self.depotPaths)
-        else:
-            if self.depotPaths and self.depotPaths != args:
-                print ("previous import used depot path %s and now %s was specified. "
-                       "This doesn't work!" % (' '.join (self.depotPaths),
-                                               ' '.join (args)))
-                sys.exit(1)
-
-            self.depotPaths = sorted(args)
-
-        revision = ""
-        self.users = {}
-
-        newPaths = []
-        for p in self.depotPaths:
-            if p.find("@") != -1:
-                atIdx = p.index("@")
-                self.changeRange = p[atIdx:]
-                if self.changeRange == "@all":
-                    self.changeRange = ""
-                elif ',' not in self.changeRange:
-                    revision = self.changeRange
-                    self.changeRange = ""
-                p = p[:atIdx]
-            elif p.find("#") != -1:
-                hashIdx = p.index("#")
-                revision = p[hashIdx:]
-                p = p[:hashIdx]
-            elif self.previousDepotPaths == []:
-                revision = "#head"
-
-            p = re.sub ("\.\.\.$", "", p)
-            if not p.endswith("/"):
-                p += "/"
-
-            newPaths.append(p)
-
-        self.depotPaths = newPaths
-
-
-        self.loadUserMapFromCache()
-        self.labels = {}
-        if self.detectLabels:
-            self.getLabels();
-
-        if self.detectBranches:
-            ## FIXME - what's a P4 projectName ?
-            self.projectName = self.guessProjectName()
-
-            if self.hasOrigin:
-                self.getBranchMappingFromGitBranches()
-            else:
-                self.getBranchMapping()
-            if self.verbose:
-                print "p4-git branches: %s" % self.p4BranchesInGit
-                print "initial parents: %s" % self.initialParents
-            for b in self.p4BranchesInGit:
-                if b != "master":
-
-                    ## FIXME
-                    b = b[len(self.projectName):]
-                self.createdBranches.add(b)
-
-        self.tz = "%+03d%02d" % (- time.timezone / 3600, ((- time.timezone % 3600) / 60))
-
-        importProcess = subprocess.Popen(["git", "fast-import"],
-                                         stdin=subprocess.PIPE, stdout=subprocess.PIPE,
-                                         stderr=subprocess.PIPE);
-        self.gitOutput = importProcess.stdout
-        self.gitStream = importProcess.stdin
-        self.gitError = importProcess.stderr
-
-        if revision:
-            self.importHeadRevision(revision)
-        else:
-            changes = []
-
-            if len(self.changesFile) > 0:
-                output = open(self.changesFile).readlines()
-                changeSet = Set()
-                for line in output:
-                    changeSet.add(int(line))
-
-                for change in changeSet:
-                    changes.append(change)
-
-                changes.sort()
-            else:
-                if self.verbose:
-                    print "Getting p4 changes for %s...%s" % (', '.join(self.depotPaths),
-                                                              self.changeRange)
-                changes = p4ChangesForPaths(self.depotPaths, self.changeRange)
-
-                if len(self.maxChanges) > 0:
-                    changes = changes[:min(int(self.maxChanges), len(changes))]
-
-            if len(changes) == 0:
-                if not self.silent:
-                    print "No changes to import!"
-                return True
-
-            if not self.silent and not self.detectBranches:
-                print "Import destination: %s" % self.branch
-
-            self.updatedBranches = set()
-
-            self.importChanges(changes)
-
-            if not self.silent:
-                print ""
-                if len(self.updatedBranches) > 0:
-                    sys.stdout.write("Updated branches: ")
-                    for b in self.updatedBranches:
-                        sys.stdout.write("%s " % b)
-                    sys.stdout.write("\n")
-
-        self.gitStream.close()
-        if importProcess.wait() != 0:
-            die("fast-import failed: %s" % self.gitError.read())
-        self.gitOutput.close()
-        self.gitError.close()
-
-        return True
-
-class P4Rebase(Command):
-    def __init__(self):
-        Command.__init__(self)
-        self.options = [ ]
-        self.description = ("Fetches the latest revision from perforce and "
-                            + "rebases the current work (branch) against it")
-        self.verbose = False
-
-    def run(self, args):
-        sync = P4Sync()
-        sync.run([])
-
-        return self.rebase()
-
-    def rebase(self):
-        if os.system("git update-index --refresh") != 0:
-            die("Some files in your working directory are modified and different than what is in your index. You can use git update-index <filename> to bring the index up-to-date or stash away all your changes with git stash.");
-        if len(read_pipe("git diff-index HEAD --")) > 0:
-            die("You have uncommited changes. Please commit them before rebasing or stash them away with git stash.");
-
-        [upstream, settings] = findUpstreamBranchPoint()
-        if len(upstream) == 0:
-            die("Cannot find upstream branchpoint for rebase")
-
-        # the branchpoint may be p4/foo~3, so strip off the parent
-        upstream = re.sub("~[0-9]+$", "", upstream)
-
-        print "Rebasing the current branch onto %s" % upstream
-        oldHead = read_pipe("git rev-parse HEAD").strip()
-        system("git rebase %s" % upstream)
-        system("git diff-tree --stat --summary -M %s HEAD" % oldHead)
-        return True
-
-class P4Clone(P4Sync):
-    def __init__(self):
-        P4Sync.__init__(self)
-        self.description = "Creates a new git repository and imports from Perforce into it"
-        self.usage = "usage: %prog [options] //depot/path[@revRange]"
-        self.options += [
-            optparse.make_option("--destination", dest="cloneDestination",
-                                 action='store', default=None,
-                                 help="where to leave result of the clone"),
-            optparse.make_option("-/", dest="cloneExclude",
-                                 action="append", type="string",
-                                 help="exclude depot path")
-        ]
-        self.cloneDestination = None
-        self.needsGit = False
-
-    # This is required for the "append" cloneExclude action
-    def ensure_value(self, attr, value):
-        if not hasattr(self, attr) or getattr(self, attr) is None:
-            setattr(self, attr, value)
-        return getattr(self, attr)
-
-    def defaultDestination(self, args):
-        ## TODO: use common prefix of args?
-        depotPath = args[0]
-        depotDir = re.sub("(@[^@]*)$", "", depotPath)
-        depotDir = re.sub("(#[^#]*)$", "", depotDir)
-        depotDir = re.sub(r"\.\.\.$", "", depotDir)
-        depotDir = re.sub(r"/$", "", depotDir)
-        return os.path.split(depotDir)[1]
-
-    def run(self, args):
-        if len(args) < 1:
-            return False
-
-        if self.keepRepoPath and not self.cloneDestination:
-            sys.stderr.write("Must specify destination for --keep-path\n")
-            sys.exit(1)
-
-        depotPaths = args
-
-        if not self.cloneDestination and len(depotPaths) > 1:
-            self.cloneDestination = depotPaths[-1]
-            depotPaths = depotPaths[:-1]
-
-        self.cloneExclude = ["/"+p for p in self.cloneExclude]
-        for p in depotPaths:
-            if not p.startswith("//"):
-                return False
-
-        if not self.cloneDestination:
-            self.cloneDestination = self.defaultDestination(args)
-
-        print "Importing from %s into %s" % (', '.join(depotPaths), self.cloneDestination)
-        if not os.path.exists(self.cloneDestination):
-            os.makedirs(self.cloneDestination)
-        chdir(self.cloneDestination)
-        system("git init")
-        self.gitdir = os.getcwd() + "/.git"
-        if not P4Sync.run(self, depotPaths):
-            return False
-        if self.branch != "master":
-            if self.importIntoRemotes:
-                masterbranch = "refs/remotes/p4/master"
-            else:
-                masterbranch = "refs/heads/p4/master"
-            if gitBranchExists(masterbranch):
-                system("git branch master %s" % masterbranch)
-                system("git checkout -f")
-            else:
-                print "Could not detect main branch. No checkout/master branch created."
-
-        return True
-
-class P4Branches(Command):
-    def __init__(self):
-        Command.__init__(self)
-        self.options = [ ]
-        self.description = ("Shows the git branches that hold imports and their "
-                            + "corresponding perforce depot paths")
-        self.verbose = False
-
-    def run(self, args):
-        if originP4BranchesExist():
-            createOrUpdateBranchesFromOrigin()
-
-        cmdline = "git rev-parse --symbolic "
-        cmdline += " --remotes"
-
-        for line in read_pipe_lines(cmdline):
-            line = line.strip()
-
-            if not line.startswith('p4/') or line == "p4/HEAD":
-                continue
-            branch = line
-
-            log = extractLogMessageFromGitCommit("refs/remotes/%s" % branch)
-            settings = extractSettingsGitLog(log)
-
-            print "%s <= %s (%s)" % (branch, ",".join(settings["depot-paths"]), settings["change"])
-        return True
-
-class HelpFormatter(optparse.IndentedHelpFormatter):
-    def __init__(self):
-        optparse.IndentedHelpFormatter.__init__(self)
-
-    def format_description(self, description):
-        if description:
-            return description + "\n"
-        else:
-            return ""
-
-def printUsage(commands):
-    print "usage: %s <command> [options]" % sys.argv[0]
-    print ""
-    print "valid commands: %s" % ", ".join(commands)
-    print ""
-    print "Try %s <command> --help for command specific help." % sys.argv[0]
-    print ""
-
-commands = {
-    "debug" : P4Debug,
-    "submit" : P4Submit,
-    "commit" : P4Submit,
-    "sync" : P4Sync,
-    "rebase" : P4Rebase,
-    "clone" : P4Clone,
-    "rollback" : P4RollBack,
-    "branches" : P4Branches
-}
-
-
-def main():
-    if len(sys.argv[1:]) == 0:
-        printUsage(commands.keys())
-        sys.exit(2)
-
-    cmd = ""
-    cmdName = sys.argv[1]
-    try:
-        klass = commands[cmdName]
-        cmd = klass()
-    except KeyError:
-        print "unknown command %s" % cmdName
-        print ""
-        printUsage(commands.keys())
-        sys.exit(2)
-
-    options = cmd.options
-    cmd.gitdir = os.environ.get("GIT_DIR", None)
-
-    args = sys.argv[2:]
-
-    if len(options) > 0:
-        options.append(optparse.make_option("--git-dir", dest="gitdir"))
-
-        parser = optparse.OptionParser(cmd.usage.replace("%prog", "%prog " + cmdName),
-                                       options,
-                                       description = cmd.description,
-                                       formatter = HelpFormatter())
-
-        (cmd, args) = parser.parse_args(sys.argv[2:], cmd);
-    global verbose
-    verbose = cmd.verbose
-    if cmd.needsGit:
-        if cmd.gitdir == None:
-            cmd.gitdir = os.path.abspath(".git")
-            if not isValidGitDir(cmd.gitdir):
-                cmd.gitdir = read_pipe("git rev-parse --git-dir").strip()
-                if os.path.exists(cmd.gitdir):
-                    cdup = read_pipe("git rev-parse --show-cdup").strip()
-                    if len(cdup) > 0:
-                        chdir(cdup);
-
-        if not isValidGitDir(cmd.gitdir):
-            if isValidGitDir(cmd.gitdir + "/.git"):
-                cmd.gitdir += "/.git"
-            else:
-                die("fatal: cannot locate git repository at %s" % cmd.gitdir)
-
-        os.environ["GIT_DIR"] = cmd.gitdir
-
-    if not cmd.run(args):
-        parser.print_help()
-
-
-if __name__ == '__main__':
-    main()
diff --git a/contrib/fast-import/git-p4.README b/contrib/fast-import/git-p4.README
new file mode 100644
index 0000000..cec5ecf
--- /dev/null
+++ b/contrib/fast-import/git-p4.README
@@ -0,0 +1,12 @@
+The git-p4 script moved to the top-level of the git source directory.
+
+Invoke it as any other git command, like "git p4 clone", for instance.
+
+Note that the top-level git-p4.py script is now the source.  It is
+built using make to git-p4, which will be installed.
+
+Windows users can copy the git-p4.py source script directly, possibly
+invoking it through a batch file called "git-p4.bat" in the same folder.
+It should contain just one line:
+
+    @python "%~d0%~p0git-p4.py" %*
diff --git a/contrib/fast-import/git-p4.bat b/contrib/fast-import/git-p4.bat
deleted file mode 100644
index 9f97e88..0000000
--- a/contrib/fast-import/git-p4.bat
+++ /dev/null
@@ -1 +0,0 @@
-@python "%~d0%~p0git-p4" %*
diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt
deleted file mode 100644
index 49b3359..0000000
--- a/contrib/fast-import/git-p4.txt
+++ /dev/null
@@ -1,210 +0,0 @@
-git-p4 - Perforce <-> Git converter using git-fast-import
-
-Usage
-=====
-
-git-p4 can be used in two different ways:
-
-1) To import changes from Perforce to a Git repository, using "git-p4 sync".
-
-2) To submit changes from Git back to Perforce, using "git-p4 submit".
-
-Importing
-=========
-
-Simply start with
-
-  git-p4 clone //depot/path/project
-
-or
-
-  git-p4 clone //depot/path/project myproject
-
-This will:
-
-1) Create an empty git repository in a subdirectory called "project" (or
-"myproject" with the second command)
-
-2) Import the head revision from the given Perforce path into a git branch
-called "p4" (remotes/p4 actually)
-
-3) Create a master branch based on it and check it out.
-
-If you want the entire history (not just the head revision) then you can simply
-append a "@all" to the depot path:
-
-  git-p4 clone //depot/project/main@all myproject
-
-
-
-If you want more control you can also use the git-p4 sync command directly:
-
-  mkdir repo-git
-  cd repo-git
-  git init
-  git-p4 sync //path/in/your/perforce/depot
-
-This will import the current head revision of the specified depot path into a
-"remotes/p4/master" branch of your git repository. You can use the
---branch=mybranch option to import into a different branch.
-
-If you want to import the entire history of a given depot path simply use:
-
-  git-p4 sync //path/in/depot@all
-
-
-Note:
-
-To achieve optimal compression you may want to run 'git repack -a -d -f' after
-a big import. This may take a while.
-
-Incremental Imports
-===================
-
-After an initial import you can continue to synchronize your git repository
-with newer changes from the Perforce depot by just calling
-
-  git-p4 sync
-
-in your git repository. By default the "remotes/p4/master" branch is updated.
-
-Advanced Setup
-==============
-
-Suppose you have a periodically updated git repository somewhere, containing a
-complete import of a Perforce project. This repository can be cloned and used
-with git-p4. When updating the cloned repository with the "sync" command,
-git-p4 will try to fetch changes from the original repository first. The git
-protocol used with this is usually faster than importing from Perforce
-directly.
-
-This behaviour can be disabled by setting the "git-p4.syncFromOrigin" git
-configuration variable to "false".
-
-Updating
-========
-
-A common working pattern is to fetch the latest changes from the Perforce depot
-and merge them with local uncommitted changes. The recommended way is to use
-git's rebase mechanism to preserve linear history. git-p4 provides a convenient
-
-  git-p4 rebase
-
-command that calls git-p4 sync followed by git rebase to rebase the current
-working branch.
-
-Submitting
-==========
-
-git-p4 has support for submitting changes from a git repository back to the
-Perforce depot. This requires a Perforce checkout separate from your git
-repository. To submit all changes that are in the current git branch but not in
-the "p4" branch (or "origin" if "p4" doesn't exist) simply call
-
-    git-p4 submit
-
-in your git repository. If you want to submit changes in a specific branch that
-is not your current git branch you can also pass that as an argument:
-
-    git-p4 submit mytopicbranch
-
-You can override the reference branch with the --origin=mysourcebranch option.
-
-If a submit fails you may have to "p4 resolve" and submit manually. You can
-continue importing the remaining changes with
-
-  git-p4 submit --continue
-
-Example
-=======
-
-# Clone a repository
-  git-p4 clone //depot/path/project
-# Enter the newly cloned directory
-  cd project
-# Do some work...
-  vi foo.h
-# ... and commit locally to gi
-  git commit foo.h
-# In the meantime somebody submitted changes to the Perforce depot. Rebase your latest
-# changes against the latest changes in Perforce:
-  git-p4 rebase
-# Submit your locally committed changes back to Perforce
-  git-p4 submit
-# ... and synchronize with Perforce
-  git-p4 rebase
-
-
-Configuration parameters
-========================
-
-git-p4.user ($P4USER)
-
-Allows you to specify the username to use to connect to the Perforce repository.
-
-  git config [--global] git-p4.user public
-
-git-p4.password ($P4PASS)
-
-Allows you to specify the password to use to connect to the Perforce repository.
-Warning this password will be visible on the command-line invocation of the p4 binary.
-
-  git config [--global] git-p4.password public1234
-
-git-p4.port ($P4PORT)
-
-Specify the port to be used to contact the Perforce server. As this will be passed
-directly to the p4 binary, it may be in the format host:port as well.
-
-  git config [--global] git-p4.port codes.zimbra.com:2666
-
-git-p4.host ($P4HOST)
-
-Specify the host to contact for a Perforce repository.
-
-  git config [--global] git-p4.host perforce.example.com
-
-git-p4.client ($P4CLIENT)
-
-Specify the client name to use
-
-  git config [--global] git-p4.client public-view
-
-git-p4.allowSubmit
-
-  git config [--global] git-p4.allowSubmit false
-
-git-p4.syncFromOrigin
-
-A useful setup may be that you have a periodically updated git repository
-somewhere that contains a complete import of a Perforce project. That git
-repository can be used to clone the working repository from and one would
-import from Perforce directly after cloning using git-p4. If the connection to
-the Perforce server is slow and the working repository hasn't been synced for a
-while it may be desirable to fetch changes from the origin git repository using
-the efficient git protocol. git-p4 supports this setup by calling "git fetch origin"
-by default if there is an origin branch. You can disable this using:
-
-  git config [--global] git-p4.syncFromOrigin false
-
-git-p4.useclientspec
-
-  git config [--global] git-p4.useclientspec false
-
-Implementation Details...
-=========================
-
-* Changesets from Perforce are imported using git fast-import.
-* The import does not require anything from the Perforce client view as it just uses
-  "p4 print //depot/path/file#revision" to get the actual file contents.
-* Every imported changeset has a special [git-p4...] line at the
-  end of the log message that gives information about the corresponding
-  Perforce change number and is also used by git-p4 itself to find out
-  where to continue importing when doing incremental imports.
-  Basically when syncing it extracts the perforce change number of the
-  latest commit in the "p4" branch and uses "p4 changes //depot/path/...@changenum,#head"
-  to find out which changes need to be imported.
-* git-p4 submit uses "git rev-list" to pick the commits between the "p4" branch
-  and the current branch.
-  The commits themselves are applied using git diff/format-patch ... | git apply
-
diff --git a/contrib/fast-import/import-directories.perl b/contrib/fast-import/import-directories.perl
new file mode 100755
index 0000000..7f3afa5
--- /dev/null
+++ b/contrib/fast-import/import-directories.perl
@@ -0,0 +1,417 @@
+#!/usr/bin/perl
+#
+# Copyright 2008-2009 Peter Krefting <peter@softwolves.pp.se>
+#
+# ------------------------------------------------------------------------
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# ------------------------------------------------------------------------
+
+=pod
+
+=head1 NAME
+
+import-directories - Import bits and pieces to Git.
+
+=head1 SYNOPSIS
+
+B<import-directories.perl> F<configfile> F<outputfile>
+
+=head1 DESCRIPTION
+
+Script to import arbitrary projects version controlled by the "copy the
+source directory to a new location and edit it there"-version controlled
+projects into version control. Handles projects with arbitrary branching
+and version trees, taking a file describing the inputs and generating a
+file compatible with the L<git-fast-import(1)> format.
+
+=head1 CONFIGURATION FILE
+
+=head2 Format
+
+The configuration file is based on the standard I<.ini> format.
+
+ ; Comments start with semi-colons
+ [section]
+ key=value
+
+Please see below for information on how to escape special characters.
+
+=head2 Global configuration
+
+Global configuration is done in the B<[config]> section, which should be
+the first section in the file. Configuration can be changed by
+repeating configuration sections later on.
+
+ [config]
+ ; configure conversion of CRLFs. "convert" means that all CRLFs
+ ; should be converted into LFs (suitable for the core.autocrlf
+ ; setting set to true in Git). "none" means that all data is
+ ; treated as binary.
+ crlf=convert
+
+=head2 Revision configuration
+
+Each revision that is to be imported is described in three
+sections. Revisions should be defined in topological order, so
+that a revision's parent has always been defined when a new revision
+is introduced. All the sections for one revision must be defined
+before defining the next revision.
+
+Each revision is assigned a unique numerical identifier. The
+numbers do not need to be consecutive, nor monotonically
+increasing.
+
+For instance, if your configuration file contains only the two
+revisions 4711 and 42, where 4711 is the initial commit, the
+only requirement is that 4711 is completely defined before 42.
+
+=pod
+
+=head3 Revision description section
+
+A section whose section name is just an integer gives meta-data
+about the revision.
+
+ [3]
+ ; author sets the author of the revisions
+ author=Peter Krefting <peter@softwolves.pp.se>
+ ; branch sets the branch that the revision should be committed to
+ branch=master
+ ; parent describes the revision that is the parent of this commit
+ ; (optional)
+ parent=1
+ ; merges describes a revision that is merged into this commit
+ ; (optional; can be repeated)
+ merges=2
+ ; selects one file to take the timestamp from
+ ; (optional; if unspecified, the most recent file from the .files
+ ;  section is used)
+ timestamp=3/source.c
+
+=head3 Revision contents section
+
+A section whose section name is an integer followed by B<.files>
+describe all the files included in this revision. If a file that
+was available previously is not included in this revision, it will
+be removed.
+
+If an on-disk revision is incomplete, you can point to files from
+a previous revision. There are no restriction as to where the source
+files are located, nor to the names of them.
+
+ [3.files]
+ ; the key is the path inside the repository, the value is the path
+ ; as seen from the importer script.
+ source.c=ver-3.00/source.c
+ source.h=ver-2.99/source.h
+ readme.txt=ver-3.00/introduction to the project.txt
+
+File names are treated as byte strings (but please see below on
+quoting rules), and should be stored in the configuration file in
+the encoding that should be used in the generated repository.
+
+=head3 Revision commit message section
+
+A section whose section name is an integer followed by B<.message>
+gives the commit message. This section is read verbatim, up until
+the beginning of the next section. As such, a commit message may not
+contain a line that begins with an opening square bracket ("[") and
+ends with a closing square bracket ("]"), unless they are surrounded
+by whitespace or other characters.
+
+ [3.message]
+ Implement foobar.
+ ; trailing blank lines are ignored.
+
+=cut
+
+# Globals
+use strict;
+use warnings;
+use integer;
+my $crlfmode = 0;
+my @revs;
+my (%revmap, %message, %files, %author, %branch, %parent, %merges, %time, %timesource);
+my $sectiontype = 0;
+my $rev = 0;
+my $mark = 1;
+
+# Check command line
+if ($#ARGV < 1 || $ARGV[0] =~ /^--?h/)
+{
+    exec('perldoc', $0);
+    exit 1;
+}
+
+# Open configuration
+my $config = $ARGV[0];
+open CFG, '<', $config or die "Cannot open configuration file \"$config\": ";
+
+# Open output
+my $output = $ARGV[1];
+open OUT, '>', $output or die "Cannot create output file \"$output\": ";
+binmode OUT;
+
+LINE: while (my $line = <CFG>)
+{
+	$line =~ s/\r?\n$//;
+	next LINE if $sectiontype != 4 && $line eq '';
+	next LINE if $line =~ /^;/;
+	my $oldsectiontype = $sectiontype;
+	my $oldrev = $rev;
+
+	# Sections
+	if ($line =~ m"^\[(config|(\d+)(|\.files|\.message))\]$")
+	{
+		if ($1 eq 'config')
+		{
+			$sectiontype = 1;
+		}
+		elsif ($3 eq '')
+		{
+			$sectiontype = 2;
+			$rev = $2;
+			# Create a new revision
+			die "Duplicate rev: $line\n " if defined $revmap{$rev};
+			print "Reading revision $rev\n";
+			push @revs, $rev;
+			$revmap{$rev} = $mark ++;
+			$time{$revmap{$rev}} = 0;
+		}
+		elsif ($3 eq '.files')
+		{
+			$sectiontype = 3;
+			$rev = $2;
+			die "Revision mismatch: $line\n " unless $rev == $oldrev;
+		}
+		elsif ($3 eq '.message')
+		{
+			$sectiontype = 4;
+			$rev = $2;
+			die "Revision mismatch: $line\n " unless $rev == $oldrev;
+		}
+		else
+		{
+			die "Internal parse error: $line\n ";
+		}
+		next LINE;
+	}
+
+	# Parse data
+	if ($sectiontype != 4)
+	{
+		# Key and value
+		if ($line =~ m"^\s*([^\s].*=.*[^\s])\s*$")
+		{
+			my ($key, $value) = &parsekeyvaluepair($1);
+			# Global configuration
+			if (1 == $sectiontype)
+			{
+				if ($key eq 'crlf')
+				{
+					$crlfmode = 1, next LINE if $value eq 'convert';
+					$crlfmode = 0, next LINE if $value eq 'none';
+				}
+				die "Unknown configuration option: $line\n ";
+			}
+			# Revision specification
+			if (2 == $sectiontype)
+			{
+				my $current = $revmap{$rev};
+				$author{$current} = $value, next LINE if $key eq 'author';
+				$branch{$current} = $value, next LINE if $key eq 'branch';
+				$parent{$current} = $value, next LINE if $key eq 'parent';
+				$timesource{$current} = $value, next LINE if $key eq 'timestamp';
+				push(@{$merges{$current}}, $value), next LINE if $key eq 'merges';
+				die "Unknown revision option: $line\n ";
+			}
+			# Filespecs
+			if (3 == $sectiontype)
+			{
+				# Add the file and create a marker
+				die "File not found: $line\n " unless -f $value;
+				my $current = $revmap{$rev};
+				${$files{$current}}{$key} = $mark;
+				my $time = &fileblob($value, $crlfmode, $mark ++);
+
+				# Update revision timestamp if more recent than other
+				# files seen, or if this is the file we have selected
+				# to take the time stamp from using the "timestamp"
+				# directive.
+				if ((defined $timesource{$current} && $timesource{$current} eq $value)
+				    || $time > $time{$current})
+				{
+					$time{$current} = $time;
+				}
+			}
+		}
+		else
+		{
+			die "Parse error: $line\n ";
+		}
+	}
+	else
+	{
+		# Commit message
+		my $current = $revmap{$rev};
+		if (defined $message{$current})
+		{
+			$message{$current} .= "\n";
+		}
+		$message{$current} .= $line;
+	}
+}
+close CFG;
+
+# Start spewing out data for git-fast-import
+foreach my $commit (@revs)
+{
+	# Progress
+	print OUT "progress Creating revision $commit\n";
+
+	# Create commit header
+	my $mark = $revmap{$commit};
+
+	# Branch and commit id
+	print OUT "commit refs/heads/", $branch{$mark}, "\nmark :", $mark, "\n";
+
+	# Author and timestamp
+	die "No timestamp defined for $commit (no files?)\n" unless defined $time{$mark};
+	print OUT "committer ", $author{$mark}, " ", $time{$mark}, " +0100\n";
+
+	# Commit message
+	die "No message defined for $commit\n" unless defined $message{$mark};
+	my $message = $message{$mark};
+	$message =~ s/\n$//; # Kill trailing empty line
+	print OUT "data ", length($message), "\n", $message, "\n";
+
+	# Parent and any merges
+	print OUT "from :", $revmap{$parent{$mark}}, "\n" if defined $parent{$mark};
+	if (defined $merges{$mark})
+	{
+		foreach my $merge (@{$merges{$mark}})
+		{
+			print OUT "merge :", $revmap{$merge}, "\n";
+		}
+	}
+
+	# Output file marks
+	print OUT "deleteall\n"; # start from scratch
+	foreach my $file (sort keys %{$files{$mark}})
+	{
+		print OUT "M 644 :", ${$files{$mark}}{$file}, " $file\n";
+	}
+	print OUT "\n";
+}
+
+# Create one file blob
+sub fileblob
+{
+	my ($filename, $crlfmode, $mark) = @_;
+
+	# Import the file
+	print OUT "progress Importing $filename\nblob\nmark :$mark\n";
+	open FILE, '<', $filename or die "Cannot read $filename\n ";
+	binmode FILE;
+	my ($size, $mtime) = (stat(FILE))[7,9];
+	my $file;
+	read FILE, $file, $size;
+	close FILE;
+	$file =~ s/\r\n/\n/g if $crlfmode;
+	print OUT "data ", length($file), "\n", $file, "\n";
+
+	return $mtime;
+}
+
+# Parse a key=value pair
+sub parsekeyvaluepair
+{
+=pod
+
+=head2 Escaping special characters
+
+Key and value strings may be enclosed in quotes, in which case
+whitespace inside the quotes is preserved. Additionally, an equal
+sign may be included in the key by preceding it with a backslash.
+For example:
+
+ "key1 "=value1
+ key2=" value2"
+ key\=3=value3
+ key4=value=4
+ "key5""=value5
+
+Here the first key is "key1 " (note the trailing white-space) and the
+second value is " value2" (note the leading white-space). The third
+key contains an equal sign "key=3" and so does the fourth value, which
+does not need to be escaped. The fifth key contains a trailing quote,
+which does not need to be escaped since it is inside a surrounding
+quote.
+
+=cut
+	my $pair = shift;
+
+	# Separate key and value by the first non-quoted equal sign
+	my ($key, $value);
+	if ($pair =~ /^(.*[^\\])=(.*)$/)
+	{
+		($key, $value) = ($1, $2)
+	}
+	else
+	{
+		die "Parse error: $pair\n ";
+	}
+
+	# Unquote and unescape the key and value separately
+	return (&unescape($key), &unescape($value));
+}
+
+# Unquote and unescape
+sub unescape
+{
+	my $string = shift;
+
+	# First remove enclosing quotes. Backslash before the trailing
+	# quote leaves both.
+	if ($string =~ /^"(.*[^\\])"$/)
+	{
+		$string = $1;
+	}
+
+	# Second remove any backslashes inside the unquoted string.
+	# For later: Handle special sequences like \t ?
+	$string =~ s/\\(.)/$1/g;
+
+	return $string;
+}
+
+__END__
+
+=pod
+
+=head1 EXAMPLES
+
+B<import-directories.perl> F<project.import>
+
+=head1 AUTHOR
+
+Copyright 2008-2009 Peter Krefting E<lt>peter@softwolves.pp.se>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation.
+
+=cut
diff --git a/contrib/fast-import/import-tars.perl b/contrib/fast-import/import-tars.perl
index 78e40d2..95438e1 100755
--- a/contrib/fast-import/import-tars.perl
+++ b/contrib/fast-import/import-tars.perl
@@ -8,9 +8,20 @@
 ##  perl import-tars.perl *.tar.bz2
 ##  git whatchanged import-tars
 ##
+## Use --metainfo to specify the extension for a meta data file, where
+## import-tars can read the commit message and optionally author and
+## committer information.
+##
+##  echo 'This is the commit message' > myfile.tar.bz2.msg
+##  perl import-tars.perl --metainfo=msg myfile.tar.bz2
 
 use strict;
-die "usage: import-tars *.tar.{gz,bz2,Z}\n" unless @ARGV;
+use Getopt::Long;
+
+my $metaext = '';
+
+die "usage: import-tars [--metainfo=extension] *.tar.{gz,bz2,lzma,xz,Z}\n"
+	unless GetOptions('metainfo=s' => \$metaext) && @ARGV;
 
 my $branch_name = 'import-tars';
 my $branch_ref = "refs/heads/$branch_name";
@@ -38,6 +49,9 @@
 	} elsif ($tar_name =~ s/\.tar\.Z$//) {
 		open(I, '-|', 'uncompress', '-c', $tar_file)
 			or die "Unable to uncompress -c $tar_file: $!\n";
+	} elsif ($tar_name =~ s/\.(tar\.(lzma|xz)|(tlz|txz))$//) {
+		open(I, '-|', 'xz', '-dc', $tar_file)
+			or die "Unable to xz -dc $tar_file: $!\n";
 	} elsif ($tar_name =~ s/\.tar$//) {
 		open(I, $tar_file) or die "Unable to open $tar_file: $!\n";
 	} else {
@@ -109,12 +123,43 @@
 		$have_top_dir = 0 if $top_dir ne $1;
 	}
 
+	my $commit_msg = "Imported from $tar_file.";
+	my $this_committer_name = $committer_name;
+	my $this_committer_email = $committer_email;
+	my $this_author_name = $author_name;
+	my $this_author_email = $author_email;
+	if ($metaext ne '') {
+		# Optionally read a commit message from <filename.tar>.msg
+		# Add a line on the form "Committer: name <e-mail>" to override
+		# the committer and "Author: name <e-mail>" to override the
+		# author for this tar ball.
+		if (open MSG, '<', "${tar_file}.${metaext}") {
+			my $header_done = 0;
+			$commit_msg = '';
+			while (<MSG>) {
+				if (!$header_done && /^Committer:\s+([^<>]*)\s+<(.*)>\s*$/i) {
+					$this_committer_name = $1;
+					$this_committer_email = $2;
+				} elsif (!$header_done && /^Author:\s+([^<>]*)\s+<(.*)>\s*$/i) {
+					$this_author_name = $1;
+					$this_author_email = $2;
+				} elsif (!$header_done && /^$/) { # empty line ends header.
+					$header_done = 1;
+				} else {
+					$commit_msg .= $_;
+					$header_done = 1;
+				}
+			}
+			close MSG;
+		}
+	}
+
 	print FI <<EOF;
 commit $branch_ref
-author $author_name <$author_email> $author_time +0000
-committer $committer_name <$committer_email> $commit_time +0000
+author $this_author_name <$this_author_email> $author_time +0000
+committer $this_committer_name <$this_committer_email> $commit_time +0000
 data <<END_OF_COMMIT_MESSAGE
-Imported from $tar_file.
+$commit_msg
 END_OF_COMMIT_MESSAGE
 
 deleteall
diff --git a/contrib/fast-import/import-zips.py b/contrib/fast-import/import-zips.py
index 7051a83..82f5ed3 100755
--- a/contrib/fast-import/import-zips.py
+++ b/contrib/fast-import/import-zips.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python
 
 ## zip archive frontend for git-fast-import
 ##
diff --git a/contrib/git-jump/README b/contrib/git-jump/README
new file mode 100644
index 0000000..1cebc32
--- /dev/null
+++ b/contrib/git-jump/README
@@ -0,0 +1,92 @@
+git-jump
+========
+
+Git-jump is a script for helping you jump to "interesting" parts of your
+project in your editor. It works by outputting a set of interesting
+spots in the "quickfix" format, which editors like vim can use as a
+queue of places to visit (this feature is usually used to jump to errors
+produced by a compiler). For example, given a diff like this:
+
+------------------------------------
+diff --git a/foo.c b/foo.c
+index a655540..5a59044 100644
+--- a/foo.c
++++ b/foo.c
+@@ -1,3 +1,3 @@
+ int main(void) {
+-  printf("hello word!\n");
++  printf("hello world!\n");
+ }
+-----------------------------------
+
+git-jump will feed this to the editor:
+
+-----------------------------------
+foo.c:2: printf("hello word!\n");
+-----------------------------------
+
+Obviously this trivial case isn't that interesting; you could just open
+`foo.c` yourself. But when you have many changes scattered across a
+project, you can use the editor's support to "jump" from point to point.
+
+Git-jump can generate three types of interesting lists:
+
+  1. The beginning of any diff hunks.
+
+  2. The beginning of any merge conflict markers.
+
+  3. Any grep matches.
+
+
+Using git-jump
+--------------
+
+To use it, just drop git-jump in your PATH, and then invoke it like
+this:
+
+--------------------------------------------------
+# jump to changes not yet staged for commit
+git jump diff
+
+# jump to changes that are staged for commit; you can give
+# arbitrary diff options
+git jump diff --cached
+
+# jump to merge conflicts
+git jump merge
+
+# jump to all instances of foo_bar
+git jump grep foo_bar
+
+# same as above, but case-insensitive; you can give
+# arbitrary grep options
+git jump grep -i foo_bar
+--------------------------------------------------
+
+
+Related Programs
+----------------
+
+You can accomplish some of the same things with individual tools. For
+example, you can use `git mergetool` to start vimdiff on each unmerged
+file. `git jump merge` is for the vim-wielding luddite who just wants to
+jump straight to the conflict text with no fanfare.
+
+As of git v1.7.2, `git grep` knows the `--open-files-in-pager` option,
+which does something similar to `git jump grep`. However, it is limited
+to positioning the cursor to the correct line in only the first file,
+leaving you to locate subsequent hits in that file or other files using
+the editor or pager. By contrast, git-jump provides the editor with a
+complete list of files and line numbers for each match.
+
+
+Limitations
+-----------
+
+This scripts was written and tested with vim. Given that the quickfix
+format is the same as what gcc produces, I expect emacs users have a
+similar feature for iterating through the list, but I know nothing about
+how to activate it.
+
+The shell snippets to generate the quickfix lines will almost certainly
+choke on filenames with exotic characters (like newlines).
diff --git a/contrib/git-jump/git-jump b/contrib/git-jump/git-jump
new file mode 100755
index 0000000..a33674e
--- /dev/null
+++ b/contrib/git-jump/git-jump
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+usage() {
+	cat <<\EOF
+usage: git jump <mode> [<args>]
+
+Jump to interesting elements in an editor.
+The <mode> parameter is one of:
+
+diff: elements are diff hunks. Arguments are given to diff.
+
+merge: elements are merge conflicts. Arguments are ignored.
+
+grep: elements are grep hits. Arguments are given to grep.
+EOF
+}
+
+open_editor() {
+	editor=`git var GIT_EDITOR`
+	eval "$editor -q \$1"
+}
+
+mode_diff() {
+	git diff --relative "$@" |
+	perl -ne '
+	if (m{^\+\+\+ b/(.*)}) { $file = $1; next }
+	defined($file) or next;
+	if (m/^@@ .*\+(\d+)/) { $line = $1; next }
+	defined($line) or next;
+	if (/^ /) { $line++; next }
+	if (/^[-+]\s*(.*)/) {
+		print "$file:$line: $1\n";
+		$line = undef;
+	}
+	'
+}
+
+mode_merge() {
+	git ls-files -u |
+	perl -pe 's/^.*?\t//' |
+	sort -u |
+	while IFS= read fn; do
+		grep -Hn '^<<<<<<<' "$fn"
+	done
+}
+
+# Grep -n generates nice quickfix-looking lines by itself,
+# but let's clean up extra whitespace, so they look better if the
+# editor shows them to us in the status bar.
+mode_grep() {
+	git grep -n "$@" |
+	perl -pe '
+	s/[ \t]+/ /g;
+	s/^ *//;
+	'
+}
+
+if test $# -lt 1; then
+	usage >&2
+	exit 1
+fi
+mode=$1; shift
+
+trap 'rm -f "$tmp"' 0 1 2 3 15
+tmp=`mktemp -t git-jump.XXXXXX` || exit 1
+type "mode_$mode" >/dev/null 2>&1 || { usage >&2; exit 1; }
+"mode_$mode" "$@" >"$tmp"
+test -s "$tmp" || exit 0
+open_editor "$tmp"
diff --git a/contrib/git-resurrect.sh b/contrib/git-resurrect.sh
index c364dda..a4ed4c3 100755
--- a/contrib/git-resurrect.sh
+++ b/contrib/git-resurrect.sh
@@ -9,6 +9,7 @@
 is rather slow but allows you to resurrect other people's topic
 branches."
 
+OPTIONS_KEEPDASHDASH=
 OPTIONS_SPEC="\
 git resurrect $USAGE
 --
diff --git a/contrib/git-shell-commands/README b/contrib/git-shell-commands/README
new file mode 100644
index 0000000..438463b
--- /dev/null
+++ b/contrib/git-shell-commands/README
@@ -0,0 +1,18 @@
+Sample programs callable through git-shell.  Place a directory named
+'git-shell-commands' in the home directory of a user whose shell is
+git-shell.  Then anyone logging in as that user will be able to run
+executables in the 'git-shell-commands' directory.
+
+Provided commands:
+
+help: Prints out the names of available commands.  When run
+interactively, git-shell will automatically run 'help' on startup,
+provided it exists.
+
+list: Displays any bare repository whose name ends with ".git" under
+user's home directory.  No other git repositories are visible,
+although they might be clonable through git-shell.  'list' is designed
+to minimize the number of calls to git that must be made in finding
+available repositories; if your setup has additional repositories that
+should be user-discoverable, you may wish to modify 'list'
+accordingly.
diff --git a/contrib/git-shell-commands/help b/contrib/git-shell-commands/help
new file mode 100755
index 0000000..535770c
--- /dev/null
+++ b/contrib/git-shell-commands/help
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+if tty -s
+then
+	echo "Run 'help' for help, or 'exit' to leave.  Available commands:"
+else
+	echo "Run 'help' for help.  Available commands:"
+fi
+
+cd "$(dirname "$0")"
+
+for cmd in *
+do
+	case "$cmd" in
+	help) ;;
+	*) [ -f "$cmd" ] && [ -x "$cmd" ] && echo "$cmd" ;;
+	esac
+done
diff --git a/contrib/git-shell-commands/list b/contrib/git-shell-commands/list
new file mode 100755
index 0000000..6f89938
--- /dev/null
+++ b/contrib/git-shell-commands/list
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+print_if_bare_repo='
+	if "$(git --git-dir="$1" rev-parse --is-bare-repository)" = true
+	then
+		printf "%s\n" "${1#./}"
+	fi
+'
+
+find -type d -name "*.git" -exec sh -c "$print_if_bare_repo" -- \{} \; -prune 2>/dev/null
diff --git a/contrib/gitview/gitview.txt b/contrib/gitview/gitview.txt
index 77c29de..9e12f97 100644
--- a/contrib/gitview/gitview.txt
+++ b/contrib/gitview/gitview.txt
@@ -7,6 +7,7 @@
 
 SYNOPSIS
 --------
+[verse]
 'gitview' [options] [args]
 
 DESCRIPTION
diff --git a/contrib/hg-to-git/hg-to-git.py b/contrib/hg-to-git/hg-to-git.py
index 2a6839d..046cb2b 100755
--- a/contrib/hg-to-git/hg-to-git.py
+++ b/contrib/hg-to-git/hg-to-git.py
@@ -1,4 +1,4 @@
-#! /usr/bin/python
+#!/usr/bin/env python
 
 """ hg-to-git.py - A Mercurial to GIT converter
 
@@ -59,14 +59,14 @@
     elems = re.compile('(.*?)\s+<(.*)>').match(user)
     if elems:
         env += 'export GIT_AUTHOR_NAME="%s" ;' % elems.group(1)
-        env += 'export GIT_COMMITER_NAME="%s" ;' % elems.group(1)
+        env += 'export GIT_COMMITTER_NAME="%s" ;' % elems.group(1)
         env += 'export GIT_AUTHOR_EMAIL="%s" ;' % elems.group(2)
-        env += 'export GIT_COMMITER_EMAIL="%s" ;' % elems.group(2)
+        env += 'export GIT_COMMITTER_EMAIL="%s" ;' % elems.group(2)
     else:
         env += 'export GIT_AUTHOR_NAME="%s" ;' % user
-        env += 'export GIT_COMMITER_NAME="%s" ;' % user
+        env += 'export GIT_COMMITTER_NAME="%s" ;' % user
         env += 'export GIT_AUTHOR_EMAIL= ;'
-        env += 'export GIT_COMMITER_EMAIL= ;'
+        env += 'export GIT_COMMITTER_EMAIL= ;'
 
     env += 'export GIT_AUTHOR_DATE="%s" ;' % date
     env += 'export GIT_COMMITTER_DATE="%s" ;' % date
diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email
index 2a66063..01af9df 100755
--- a/contrib/hooks/post-receive-email
+++ b/contrib/hooks/post-receive-email
@@ -11,11 +11,11 @@
 # will have put this somewhere standard.  You should make this script
 # executable then link to it in the repository you would like to use it in.
 # For example, on debian the hook is stored in
-# /usr/share/doc/git-core/contrib/hooks/post-receive-email:
+# /usr/share/git-core/contrib/hooks/post-receive-email:
 #
 #  chmod a+x post-receive-email
 #  cd /path/to/your/repository.git
-#  ln -sf /usr/share/doc/git-core/contrib/hooks/post-receive-email hooks/post-receive
+#  ln -sf /usr/share/git-core/contrib/hooks/post-receive-email hooks/post-receive
 #
 # This hook script assumes it is enabled on the central repository of a
 # project, with all users pushing only to it and not between each other.  It
@@ -23,6 +23,13 @@
 # possible for the email to be from someone other than the person doing the
 # push.
 #
+# To help with debugging and use on pre-v1.5.1 git servers, this script will
+# also obey the interface of hooks/update, taking its arguments on the
+# command line.  Unfortunately, hooks/update is called once for each ref.
+# To avoid firing one email per ref, this script just prints its output to
+# the screen when used in this mode.  The output can then be redirected if
+# wanted.
+#
 # Config
 # ------
 # hooks.mailinglist
@@ -48,6 +55,16 @@
 #     "t=%s; printf 'http://.../?id=%%s' \$t; echo;echo; git show -C \$t; echo"
 #   Be careful if "..." contains things that will be expanded by shell "eval"
 #   or printf.
+# hooks.emailmaxlines
+#   The maximum number of lines that should be included in the generated
+#   email body. If not specified, there is no limit.
+#   Lines beyond the limit are suppressed and counted, and a final
+#   line is added indicating the number of suppressed lines.
+# hooks.diffopts
+#   Alternate options for the git diff-tree invocation that shows changes.
+#   Default is "--stat --summary --find-copies-harder". Add -p to those
+#   options to include a unified diff of changes in addition to the usual
+#   summary output.
 #
 # Notes
 # -----
@@ -59,19 +76,10 @@
 # ---------------------------- Functions
 
 #
-# Top level email generation function.  This decides what type of update
-# this is and calls the appropriate body-generation routine after outputting
-# the common header
+# Function to prepare for email generation. This decides what type
+# of update this is and whether an email should even be generated.
 #
-# Note this function doesn't actually generate any email output, that is
-# taken care of by the functions it calls:
-#  - generate_email_header
-#  - generate_create_XXXX_email
-#  - generate_update_XXXX_email
-#  - generate_delete_XXXX_email
-#  - generate_email_footer
-#
-generate_email()
+prep_for_email()
 {
 	# --- Arguments
 	oldrev=$(git rev-parse $1)
@@ -140,13 +148,13 @@
 			short_refname=${refname##refs/remotes/}
 			echo >&2 "*** Push-update of tracking branch, $refname"
 			echo >&2 "***  - no email generated."
-			exit 0
+			return 1
 			;;
 		*)
 			# Anything else (is there anything else?)
 			echo >&2 "*** Unknown type of update to $refname ($rev_type)"
 			echo >&2 "***  - no email generated"
-			exit 1
+			return 1
 			;;
 	esac
 
@@ -162,9 +170,32 @@
 		esac
 		echo >&2 "*** $config_name is not set so no email will be sent"
 		echo >&2 "*** for $refname update $oldrev->$newrev"
-		exit 0
+		return 1
 	fi
 
+	return 0
+}
+
+#
+# Top level email generation function.  This calls the appropriate
+# body-generation routine after outputting the common header.
+#
+# Note this function doesn't actually generate any email output, that is
+# taken care of by the functions it calls:
+#  - generate_email_header
+#  - generate_create_XXXX_email
+#  - generate_update_XXXX_email
+#  - generate_delete_XXXX_email
+#  - generate_email_footer
+#
+# Note also that this function cannot 'exit' from the script; when this
+# function is running (in hook script mode), the send_mail() function
+# is already executing in another process, connected via a pipe, and
+# if this function exits without, whatever has been generated to that
+# point will be sent as an email... even if nothing has been generated.
+#
+generate_email()
+{
 	# Email parameters
 	# The email subject will contain the best description of the ref
 	# that we can build from the parameters
@@ -185,7 +216,12 @@
 		fn_name=atag
 		;;
 	esac
-	generate_${change_type}_${fn_name}_email
+
+	if [ -z "$maxlines" ]; then
+		generate_${change_type}_${fn_name}_email
+	else
+		generate_${change_type}_${fn_name}_email | limit_lines $maxlines
+	fi
 
 	generate_email_footer
 }
@@ -196,7 +232,7 @@
 	# Generate header
 	cat <<-EOF
 	To: $recipients
-	Subject: ${emailprefix}$projectdesc $refname_type, $short_refname, ${change_type}d. $describe
+	Subject: ${emailprefix}$projectdesc $refname_type $short_refname ${change_type}d. $describe
 	X-Git-Refname: $refname
 	X-Git-Reftype: $refname_type
 	X-Git-Oldrev: $oldrev
@@ -315,8 +351,8 @@
 	# "remotes/" will be ignored as well.
 
 	# List all of the revisions that were removed by this update, in a
-	# fast forward update, this list will be empty, because rev-list O
-	# ^N is empty.  For a non fast forward, O ^N is the list of removed
+	# fast-forward update, this list will be empty, because rev-list O
+	# ^N is empty.  For a non-fast-forward, O ^N is the list of removed
 	# revisions
 	fast_forward=""
 	rev=""
@@ -411,10 +447,10 @@
 	# revision because the base is effectively a random revision at this
 	# point - the user will be interested in what this revision changed
 	# - including the undoing of previous revisions in the case of
-	# non-fast forward updates.
+	# non-fast-forward updates.
 	echo ""
 	echo "Summary of changes:"
-	git diff-tree --stat --summary --find-copies-harder $oldrev..$newrev
+	git diff-tree $diffopts $oldrev..$newrev
 }
 
 #
@@ -424,7 +460,7 @@
 {
 	echo "       was  $oldrev"
 	echo ""
-	echo $LOGEND
+	echo $LOGBEGIN
 	git show -s --pretty=oneline $oldrev
 	echo $LOGEND
 }
@@ -524,7 +560,7 @@
 {
 	echo "       was  $oldrev"
 	echo ""
-	echo $LOGEND
+	echo $LOGBEGIN
 	git show -s --pretty=oneline $oldrev
 	echo $LOGEND
 }
@@ -589,7 +625,7 @@
 {
 	echo "       was  $oldrev"
 	echo ""
-	echo $LOGEND
+	echo $LOGBEGIN
 	git show -s --pretty=oneline $oldrev
 	echo $LOGEND
 }
@@ -635,6 +671,24 @@
 }
 
 
+limit_lines()
+{
+	lines=0
+	skipped=0
+	while IFS="" read -r line; do
+		lines=$((lines + 1))
+		if [ $lines -gt $1 ]; then
+			skipped=$((skipped + 1))
+		else
+			printf "%s\n" "$line"
+		fi
+	done
+	if [ $skipped -ne 0 ]; then
+		echo "... $skipped lines suppressed ..."
+	fi
+}
+
+
 send_mail()
 {
 	if [ -n "$envelopesender" ]; then
@@ -659,7 +713,7 @@
 	exit 1
 fi
 
-projectdesc=$(sed -ne '1p' "$GIT_DIR/description")
+projectdesc=$(sed -ne '1p' "$GIT_DIR/description" 2>/dev/null)
 # Check if the description is unchanged from it's default, and shorten it to
 # a more manageable length if it is
 if expr "$projectdesc" : "Unnamed repository.*$" >/dev/null
@@ -672,6 +726,9 @@
 envelopesender=$(git config hooks.envelopesender)
 emailprefix=$(git config hooks.emailprefix || echo '[SCM] ')
 custom_showrev=$(git config hooks.showrev)
+maxlines=$(git config hooks.emailmaxlines)
+diffopts=$(git config hooks.diffopts)
+: ${diffopts:="--stat --summary --find-copies-harder"}
 
 # --- Main loop
 # Allow dual mode: run from the command line just like the update hook, or
@@ -680,10 +737,11 @@
 	# Output to the terminal in command line mode - if someone wanted to
 	# resend an email; they could redirect the output to sendmail
 	# themselves
-	PAGER= generate_email $2 $3 $1
+	prep_for_email $2 $3 $1 && PAGER= generate_email
 else
 	while read oldrev newrev refname
 	do
-		generate_email $oldrev $newrev $refname | send_mail
+		prep_for_email $oldrev $newrev $refname || continue
+		generate_email $maxlines | send_mail
 	done
 fi
diff --git a/contrib/mw-to-git/Makefile b/contrib/mw-to-git/Makefile
new file mode 100644
index 0000000..3ed728b
--- /dev/null
+++ b/contrib/mw-to-git/Makefile
@@ -0,0 +1,47 @@
+#
+# Copyright (C) 2012
+#     Charles Roussel <charles.roussel@ensimag.imag.fr>
+#     Simon Cathebras <simon.cathebras@ensimag.imag.fr>
+#     Julien Khayat <julien.khayat@ensimag.imag.fr>
+#     Guillaume Sasdy <guillaume.sasdy@ensimag.imag.fr>
+#     Simon Perrat <simon.perrat@ensimag.imag.fr>
+#
+## Build git-remote-mediawiki
+
+-include ../../config.mak.autogen
+-include ../../config.mak
+
+ifndef PERL_PATH
+	PERL_PATH = /usr/bin/perl
+endif
+ifndef gitexecdir
+	gitexecdir = $(shell git --exec-path)
+endif
+
+PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
+gitexecdir_SQ = $(subst ','\'',$(gitexecdir))
+SCRIPT = git-remote-mediawiki
+
+.PHONY: install help doc test clean
+
+help:
+	@echo 'This is the help target of the Makefile. Current configuration:'
+	@echo '  gitexecdir = $(gitexecdir_SQ)'
+	@echo '  PERL_PATH = $(PERL_PATH_SQ)'
+	@echo 'Run "$(MAKE) install" to install $(SCRIPT) in gitexecdir'
+	@echo 'Run "$(MAKE) test" to run the testsuite'
+
+install:
+	sed -e '1s|#!.*/perl|#!$(PERL_PATH_SQ)|' $(SCRIPT) \
+		> '$(gitexecdir_SQ)/$(SCRIPT)'
+	chmod +x '$(gitexecdir)/$(SCRIPT)'
+
+doc:
+	@echo 'Sorry, "make doc" is not implemented yet for $(SCRIPT)'
+
+test:
+	$(MAKE) -C t/ test
+
+clean:
+	$(RM) '$(gitexecdir)/$(SCRIPT)'
+	$(MAKE) -C t/ clean
diff --git a/contrib/mw-to-git/git-remote-mediawiki b/contrib/mw-to-git/git-remote-mediawiki
new file mode 100755
index 0000000..8647c92
--- /dev/null
+++ b/contrib/mw-to-git/git-remote-mediawiki
@@ -0,0 +1,1363 @@
+#! /usr/bin/perl
+
+# Copyright (C) 2011
+#     Jérémie Nikaes <jeremie.nikaes@ensimag.imag.fr>
+#     Arnaud Lacurie <arnaud.lacurie@ensimag.imag.fr>
+#     Claire Fousse <claire.fousse@ensimag.imag.fr>
+#     David Amouyal <david.amouyal@ensimag.imag.fr>
+#     Matthieu Moy <matthieu.moy@grenoble-inp.fr>
+# License: GPL v2 or later
+
+# Gateway between Git and MediaWiki.
+#   https://github.com/Bibzball/Git-Mediawiki/wiki
+#
+# Known limitations:
+#
+# - Several strategies are provided to fetch modifications from the
+#   wiki, but no automatic heuristics is provided, the user has
+#   to understand and chose which strategy is appropriate for him.
+#
+# - Git renames could be turned into MediaWiki renames (see TODO
+#   below)
+#
+# - No way to import "one page, and all pages included in it"
+#
+# - Multiple remote MediaWikis have not been very well tested.
+
+use strict;
+use MediaWiki::API;
+use DateTime::Format::ISO8601;
+
+# By default, use UTF-8 to communicate with Git and the user
+binmode STDERR, ":utf8";
+binmode STDOUT, ":utf8";
+
+use URI::Escape;
+use IPC::Open2;
+
+use warnings;
+
+# Mediawiki filenames can contain forward slashes. This variable decides by which pattern they should be replaced
+use constant SLASH_REPLACEMENT => "%2F";
+
+# It's not always possible to delete pages (may require some
+# priviledges). Deleted pages are replaced with this content.
+use constant DELETED_CONTENT => "[[Category:Deleted]]\n";
+
+# It's not possible to create empty pages. New empty files in Git are
+# sent with this content instead.
+use constant EMPTY_CONTENT => "<!-- empty page -->\n";
+
+# used to reflect file creation or deletion in diff.
+use constant NULL_SHA1 => "0000000000000000000000000000000000000000";
+
+# Used on Git's side to reflect empty edit messages on the wiki
+use constant EMPTY_MESSAGE => '*Empty MediaWiki Message*';
+
+my $remotename = $ARGV[0];
+my $url = $ARGV[1];
+
+# Accept both space-separated and multiple keys in config file.
+# Spaces should be written as _ anyway because we'll use chomp.
+my @tracked_pages = split(/[ \n]/, run_git("config --get-all remote.". $remotename .".pages"));
+chomp(@tracked_pages);
+
+# Just like @tracked_pages, but for MediaWiki categories.
+my @tracked_categories = split(/[ \n]/, run_git("config --get-all remote.". $remotename .".categories"));
+chomp(@tracked_categories);
+
+# Import media files on pull
+my $import_media = run_git("config --get --bool remote.". $remotename .".mediaimport");
+chomp($import_media);
+$import_media = ($import_media eq "true");
+
+# Export media files on push
+my $export_media = run_git("config --get --bool remote.". $remotename .".mediaexport");
+chomp($export_media);
+$export_media = !($export_media eq "false");
+
+my $wiki_login = run_git("config --get remote.". $remotename .".mwLogin");
+# Note: mwPassword is discourraged. Use the credential system instead.
+my $wiki_passwd = run_git("config --get remote.". $remotename .".mwPassword");
+my $wiki_domain = run_git("config --get remote.". $remotename .".mwDomain");
+chomp($wiki_login);
+chomp($wiki_passwd);
+chomp($wiki_domain);
+
+# Import only last revisions (both for clone and fetch)
+my $shallow_import = run_git("config --get --bool remote.". $remotename .".shallow");
+chomp($shallow_import);
+$shallow_import = ($shallow_import eq "true");
+
+# Fetch (clone and pull) by revisions instead of by pages. This behavior
+# is more efficient when we have a wiki with lots of pages and we fetch
+# the revisions quite often so that they concern only few pages.
+# Possible values:
+# - by_rev: perform one query per new revision on the remote wiki
+# - by_page: query each tracked page for new revision
+my $fetch_strategy = run_git("config --get remote.$remotename.fetchStrategy");
+unless ($fetch_strategy) {
+	$fetch_strategy = run_git("config --get mediawiki.fetchStrategy");
+}
+chomp($fetch_strategy);
+unless ($fetch_strategy) {
+	$fetch_strategy = "by_page";
+}
+
+# Dumb push: don't update notes and mediawiki ref to reflect the last push.
+#
+# Configurable with mediawiki.dumbPush, or per-remote with
+# remote.<remotename>.dumbPush.
+#
+# This means the user will have to re-import the just-pushed
+# revisions. On the other hand, this means that the Git revisions
+# corresponding to MediaWiki revisions are all imported from the wiki,
+# regardless of whether they were initially created in Git or from the
+# web interface, hence all users will get the same history (i.e. if
+# the push from Git to MediaWiki loses some information, everybody
+# will get the history with information lost). If the import is
+# deterministic, this means everybody gets the same sha1 for each
+# MediaWiki revision.
+my $dumb_push = run_git("config --get --bool remote.$remotename.dumbPush");
+unless ($dumb_push) {
+	$dumb_push = run_git("config --get --bool mediawiki.dumbPush");
+}
+chomp($dumb_push);
+$dumb_push = ($dumb_push eq "true");
+
+my $wiki_name = $url;
+$wiki_name =~ s/[^\/]*:\/\///;
+# If URL is like http://user:password@example.com/, we clearly don't
+# want the password in $wiki_name. While we're there, also remove user
+# and '@' sign, to avoid author like MWUser@HTTPUser@host.com
+$wiki_name =~ s/^.*@//;
+
+# Commands parser
+my $entry;
+my @cmd;
+while (<STDIN>) {
+	chomp;
+	@cmd = split(/ /);
+	if (defined($cmd[0])) {
+		# Line not blank
+		if ($cmd[0] eq "capabilities") {
+			die("Too many arguments for capabilities") unless (!defined($cmd[1]));
+			mw_capabilities();
+		} elsif ($cmd[0] eq "list") {
+			die("Too many arguments for list") unless (!defined($cmd[2]));
+			mw_list($cmd[1]);
+		} elsif ($cmd[0] eq "import") {
+			die("Invalid arguments for import") unless ($cmd[1] ne "" && !defined($cmd[2]));
+			mw_import($cmd[1]);
+		} elsif ($cmd[0] eq "option") {
+			die("Too many arguments for option") unless ($cmd[1] ne "" && $cmd[2] ne "" && !defined($cmd[3]));
+			mw_option($cmd[1],$cmd[2]);
+		} elsif ($cmd[0] eq "push") {
+			mw_push($cmd[1]);
+		} else {
+			print STDERR "Unknown command. Aborting...\n";
+			last;
+		}
+	} else {
+		# blank line: we should terminate
+		last;
+	}
+
+	BEGIN { $| = 1 } # flush STDOUT, to make sure the previous
+			 # command is fully processed.
+}
+
+########################## Functions ##############################
+
+## credential API management (generic functions)
+
+sub credential_read {
+	my %credential;
+	my $reader = shift;
+	my $op = shift;
+	while (<$reader>) {
+		my ($key, $value) = /([^=]*)=(.*)/;
+		if (not defined $key) {
+			die "ERROR receiving response from git credential $op:\n$_\n";
+		}
+		$credential{$key} = $value;
+	}
+	return %credential;
+}
+
+sub credential_write {
+	my $credential = shift;
+	my $writer = shift;
+	# url overwrites other fields, so it must come first
+	print $writer "url=$credential->{url}\n" if exists $credential->{url};
+	while (my ($key, $value) = each(%$credential) ) {
+		if (length $value && $key ne 'url') {
+			print $writer "$key=$value\n";
+		}
+	}
+}
+
+sub credential_run {
+	my $op = shift;
+	my $credential = shift;
+	my $pid = open2(my $reader, my $writer, "git credential $op");
+	credential_write($credential, $writer);
+	print $writer "\n";
+	close($writer);
+
+	if ($op eq "fill") {
+		%$credential = credential_read($reader, $op);
+	} else {
+		if (<$reader>) {
+			die "ERROR while running git credential $op:\n$_";
+		}
+	}
+	close($reader);
+	waitpid($pid, 0);
+	my $child_exit_status = $? >> 8;
+	if ($child_exit_status != 0) {
+		die "'git credential $op' failed with code $child_exit_status.";
+	}
+}
+
+# MediaWiki API instance, created lazily.
+my $mediawiki;
+
+sub mw_connect_maybe {
+	if ($mediawiki) {
+		return;
+	}
+	$mediawiki = MediaWiki::API->new;
+	$mediawiki->{config}->{api_url} = "$url/api.php";
+	if ($wiki_login) {
+		my %credential = (url => $url);
+		$credential{username} = $wiki_login;
+		$credential{password} = $wiki_passwd;
+		credential_run("fill", \%credential);
+		my $request = {lgname => $credential{username},
+			       lgpassword => $credential{password},
+			       lgdomain => $wiki_domain};
+		if ($mediawiki->login($request)) {
+			credential_run("approve", \%credential);
+			print STDERR "Logged in mediawiki user \"$credential{username}\".\n";
+		} else {
+			print STDERR "Failed to log in mediawiki user \"$credential{username}\" on $url\n";
+			print STDERR "  (error " .
+				$mediawiki->{error}->{code} . ': ' .
+				$mediawiki->{error}->{details} . ")\n";
+			credential_run("reject", \%credential);
+			exit 1;
+		}
+	}
+}
+
+## Functions for listing pages on the remote wiki
+sub get_mw_tracked_pages {
+	my $pages = shift;
+	get_mw_page_list(\@tracked_pages, $pages);
+}
+
+sub get_mw_page_list {
+	my $page_list = shift;
+	my $pages = shift;
+	my @some_pages = @$page_list;
+	while (@some_pages) {
+		my $last = 50;
+		if ($#some_pages < $last) {
+			$last = $#some_pages;
+		}
+		my @slice = @some_pages[0..$last];
+		get_mw_first_pages(\@slice, $pages);
+		@some_pages = @some_pages[51..$#some_pages];
+	}
+}
+
+sub get_mw_tracked_categories {
+	my $pages = shift;
+	foreach my $category (@tracked_categories) {
+		if (index($category, ':') < 0) {
+			# Mediawiki requires the Category
+			# prefix, but let's not force the user
+			# to specify it.
+			$category = "Category:" . $category;
+		}
+		my $mw_pages = $mediawiki->list( {
+			action => 'query',
+			list => 'categorymembers',
+			cmtitle => $category,
+			cmlimit => 'max' } )
+			|| die $mediawiki->{error}->{code} . ': '
+				. $mediawiki->{error}->{details};
+		foreach my $page (@{$mw_pages}) {
+			$pages->{$page->{title}} = $page;
+		}
+	}
+}
+
+sub get_mw_all_pages {
+	my $pages = shift;
+	# No user-provided list, get the list of pages from the API.
+	my $mw_pages = $mediawiki->list({
+		action => 'query',
+		list => 'allpages',
+		aplimit => 'max'
+	});
+	if (!defined($mw_pages)) {
+		print STDERR "fatal: could not get the list of wiki pages.\n";
+		print STDERR "fatal: '$url' does not appear to be a mediawiki\n";
+		print STDERR "fatal: make sure '$url/api.php' is a valid page.\n";
+		exit 1;
+	}
+	foreach my $page (@{$mw_pages}) {
+		$pages->{$page->{title}} = $page;
+	}
+}
+
+# queries the wiki for a set of pages. Meant to be used within a loop
+# querying the wiki for slices of page list.
+sub get_mw_first_pages {
+	my $some_pages = shift;
+	my @some_pages = @{$some_pages};
+
+	my $pages = shift;
+
+	# pattern 'page1|page2|...' required by the API
+	my $titles = join('|', @some_pages);
+
+	my $mw_pages = $mediawiki->api({
+		action => 'query',
+		titles => $titles,
+	});
+	if (!defined($mw_pages)) {
+		print STDERR "fatal: could not query the list of wiki pages.\n";
+		print STDERR "fatal: '$url' does not appear to be a mediawiki\n";
+		print STDERR "fatal: make sure '$url/api.php' is a valid page.\n";
+		exit 1;
+	}
+	while (my ($id, $page) = each(%{$mw_pages->{query}->{pages}})) {
+		if ($id < 0) {
+			print STDERR "Warning: page $page->{title} not found on wiki\n";
+		} else {
+			$pages->{$page->{title}} = $page;
+		}
+	}
+}
+
+# Get the list of pages to be fetched according to configuration.
+sub get_mw_pages {
+	mw_connect_maybe();
+
+	print STDERR "Listing pages on remote wiki...\n";
+
+	my %pages; # hash on page titles to avoid duplicates
+	my $user_defined;
+	if (@tracked_pages) {
+		$user_defined = 1;
+		# The user provided a list of pages titles, but we
+		# still need to query the API to get the page IDs.
+		get_mw_tracked_pages(\%pages);
+	}
+	if (@tracked_categories) {
+		$user_defined = 1;
+		get_mw_tracked_categories(\%pages);
+	}
+	if (!$user_defined) {
+		get_mw_all_pages(\%pages);
+	}
+	if ($import_media) {
+		print STDERR "Getting media files for selected pages...\n";
+		if ($user_defined) {
+			get_linked_mediafiles(\%pages);
+		} else {
+			get_all_mediafiles(\%pages);
+		}
+	}
+	print STDERR (scalar keys %pages) . " pages found.\n";
+	return %pages;
+}
+
+# usage: $out = run_git("command args");
+#        $out = run_git("command args", "raw"); # don't interpret output as UTF-8.
+sub run_git {
+	my $args = shift;
+	my $encoding = (shift || "encoding(UTF-8)");
+	open(my $git, "-|:$encoding", "git " . $args);
+	my $res = do { local $/; <$git> };
+	close($git);
+
+	return $res;
+}
+
+
+sub get_all_mediafiles {
+	my $pages = shift;
+	# Attach list of all pages for media files from the API,
+	# they are in a different namespace, only one namespace
+	# can be queried at the same moment
+	my $mw_pages = $mediawiki->list({
+		action => 'query',
+		list => 'allpages',
+		apnamespace => get_mw_namespace_id("File"),
+		aplimit => 'max'
+	});
+	if (!defined($mw_pages)) {
+		print STDERR "fatal: could not get the list of pages for media files.\n";
+		print STDERR "fatal: '$url' does not appear to be a mediawiki\n";
+		print STDERR "fatal: make sure '$url/api.php' is a valid page.\n";
+		exit 1;
+	}
+	foreach my $page (@{$mw_pages}) {
+		$pages->{$page->{title}} = $page;
+	}
+}
+
+sub get_linked_mediafiles {
+	my $pages = shift;
+	my @titles = map $_->{title}, values(%{$pages});
+
+	# The query is split in small batches because of the MW API limit of
+	# the number of links to be returned (500 links max).
+	my $batch = 10;
+	while (@titles) {
+		if ($#titles < $batch) {
+			$batch = $#titles;
+		}
+		my @slice = @titles[0..$batch];
+
+		# pattern 'page1|page2|...' required by the API
+		my $mw_titles = join('|', @slice);
+
+		# Media files could be included or linked from
+		# a page, get all related
+		my $query = {
+			action => 'query',
+			prop => 'links|images',
+			titles => $mw_titles,
+			plnamespace => get_mw_namespace_id("File"),
+			pllimit => 'max'
+		};
+		my $result = $mediawiki->api($query);
+
+		while (my ($id, $page) = each(%{$result->{query}->{pages}})) {
+			my @media_titles;
+			if (defined($page->{links})) {
+				my @link_titles = map $_->{title}, @{$page->{links}};
+				push(@media_titles, @link_titles);
+			}
+			if (defined($page->{images})) {
+				my @image_titles = map $_->{title}, @{$page->{images}};
+				push(@media_titles, @image_titles);
+			}
+			if (@media_titles) {
+				get_mw_page_list(\@media_titles, $pages);
+			}
+		}
+
+		@titles = @titles[($batch+1)..$#titles];
+	}
+}
+
+sub get_mw_mediafile_for_page_revision {
+	# Name of the file on Wiki, with the prefix.
+	my $filename = shift;
+	my $timestamp = shift;
+	my %mediafile;
+
+	# Search if on a media file with given timestamp exists on
+	# MediaWiki. In that case download the file.
+	my $query = {
+		action => 'query',
+		prop => 'imageinfo',
+		titles => "File:" . $filename,
+		iistart => $timestamp,
+		iiend => $timestamp,
+		iiprop => 'timestamp|archivename|url',
+		iilimit => 1
+	};
+	my $result = $mediawiki->api($query);
+
+	my ($fileid, $file) = each( %{$result->{query}->{pages}} );
+	# If not defined it means there is no revision of the file for
+	# given timestamp.
+	if (defined($file->{imageinfo})) {
+		$mediafile{title} = $filename;
+
+		my $fileinfo = pop(@{$file->{imageinfo}});
+		$mediafile{timestamp} = $fileinfo->{timestamp};
+		# Mediawiki::API's download function doesn't support https URLs
+		# and can't download old versions of files.
+		print STDERR "\tDownloading file $mediafile{title}, version $mediafile{timestamp}\n";
+		$mediafile{content} = download_mw_mediafile($fileinfo->{url});
+	}
+	return %mediafile;
+}
+
+sub download_mw_mediafile {
+	my $url = shift;
+
+	my $response = $mediawiki->{ua}->get($url);
+	if ($response->code == 200) {
+		return $response->decoded_content;
+	} else {
+		print STDERR "Error downloading mediafile from :\n";
+		print STDERR "URL: $url\n";
+		print STDERR "Server response: " . $response->code . " " . $response->message . "\n";
+		exit 1;
+	}
+}
+
+sub get_last_local_revision {
+	# Get note regarding last mediawiki revision
+	my $note = run_git("notes --ref=$remotename/mediawiki show refs/mediawiki/$remotename/master 2>/dev/null");
+	my @note_info = split(/ /, $note);
+
+	my $lastrevision_number;
+	if (!(defined($note_info[0]) && $note_info[0] eq "mediawiki_revision:")) {
+		print STDERR "No previous mediawiki revision found";
+		$lastrevision_number = 0;
+	} else {
+		# Notes are formatted : mediawiki_revision: #number
+		$lastrevision_number = $note_info[1];
+		chomp($lastrevision_number);
+		print STDERR "Last local mediawiki revision found is $lastrevision_number";
+	}
+	return $lastrevision_number;
+}
+
+# Remember the timestamp corresponding to a revision id.
+my %basetimestamps;
+
+# Get the last remote revision without taking in account which pages are
+# tracked or not. This function makes a single request to the wiki thus
+# avoid a loop onto all tracked pages. This is useful for the fetch-by-rev
+# option.
+sub get_last_global_remote_rev {
+	mw_connect_maybe();
+
+	my $query = {
+		action => 'query',
+		list => 'recentchanges',
+		prop => 'revisions',
+		rclimit => '1',
+		rcdir => 'older',
+	};
+	my $result = $mediawiki->api($query);
+	return $result->{query}->{recentchanges}[0]->{revid};
+}
+
+# Get the last remote revision concerning the tracked pages and the tracked
+# categories.
+sub get_last_remote_revision {
+	mw_connect_maybe();
+
+	my %pages_hash = get_mw_pages();
+	my @pages = values(%pages_hash);
+
+	my $max_rev_num = 0;
+
+	print STDERR "Getting last revision id on tracked pages...\n";
+
+	foreach my $page (@pages) {
+		my $id = $page->{pageid};
+
+		my $query = {
+			action => 'query',
+			prop => 'revisions',
+			rvprop => 'ids|timestamp',
+			pageids => $id,
+		};
+
+		my $result = $mediawiki->api($query);
+
+		my $lastrev = pop(@{$result->{query}->{pages}->{$id}->{revisions}});
+
+		$basetimestamps{$lastrev->{revid}} = $lastrev->{timestamp};
+
+		$max_rev_num = ($lastrev->{revid} > $max_rev_num ? $lastrev->{revid} : $max_rev_num);
+	}
+
+	print STDERR "Last remote revision found is $max_rev_num.\n";
+	return $max_rev_num;
+}
+
+# Clean content before sending it to MediaWiki
+sub mediawiki_clean {
+	my $string = shift;
+	my $page_created = shift;
+	# Mediawiki does not allow blank space at the end of a page and ends with a single \n.
+	# This function right trims a string and adds a \n at the end to follow this rule
+	$string =~ s/\s+$//;
+	if ($string eq "" && $page_created) {
+		# Creating empty pages is forbidden.
+		$string = EMPTY_CONTENT;
+	}
+	return $string."\n";
+}
+
+# Filter applied on MediaWiki data before adding them to Git
+sub mediawiki_smudge {
+	my $string = shift;
+	if ($string eq EMPTY_CONTENT) {
+		$string = "";
+	}
+	# This \n is important. This is due to mediawiki's way to handle end of files.
+	return $string."\n";
+}
+
+sub mediawiki_clean_filename {
+	my $filename = shift;
+	$filename =~ s/@{[SLASH_REPLACEMENT]}/\//g;
+	# [, ], |, {, and } are forbidden by MediaWiki, even URL-encoded.
+	# Do a variant of URL-encoding, i.e. looks like URL-encoding,
+	# but with _ added to prevent MediaWiki from thinking this is
+	# an actual special character.
+	$filename =~ s/[\[\]\{\}\|]/sprintf("_%%_%x", ord($&))/ge;
+	# If we use the uri escape before
+	# we should unescape here, before anything
+
+	return $filename;
+}
+
+sub mediawiki_smudge_filename {
+	my $filename = shift;
+	$filename =~ s/\//@{[SLASH_REPLACEMENT]}/g;
+	$filename =~ s/ /_/g;
+	# Decode forbidden characters encoded in mediawiki_clean_filename
+	$filename =~ s/_%_([0-9a-fA-F][0-9a-fA-F])/sprintf("%c", hex($1))/ge;
+	return $filename;
+}
+
+sub literal_data {
+	my ($content) = @_;
+	print STDOUT "data ", bytes::length($content), "\n", $content;
+}
+
+sub literal_data_raw {
+	# Output possibly binary content.
+	my ($content) = @_;
+	# Avoid confusion between size in bytes and in characters
+	utf8::downgrade($content);
+	binmode STDOUT, ":raw";
+	print STDOUT "data ", bytes::length($content), "\n", $content;
+	binmode STDOUT, ":utf8";
+}
+
+sub mw_capabilities {
+	# Revisions are imported to the private namespace
+	# refs/mediawiki/$remotename/ by the helper and fetched into
+	# refs/remotes/$remotename later by fetch.
+	print STDOUT "refspec refs/heads/*:refs/mediawiki/$remotename/*\n";
+	print STDOUT "import\n";
+	print STDOUT "list\n";
+	print STDOUT "push\n";
+	print STDOUT "\n";
+}
+
+sub mw_list {
+	# MediaWiki do not have branches, we consider one branch arbitrarily
+	# called master, and HEAD pointing to it.
+	print STDOUT "? refs/heads/master\n";
+	print STDOUT "\@refs/heads/master HEAD\n";
+	print STDOUT "\n";
+}
+
+sub mw_option {
+	print STDERR "remote-helper command 'option $_[0]' not yet implemented\n";
+	print STDOUT "unsupported\n";
+}
+
+sub fetch_mw_revisions_for_page {
+	my $page = shift;
+	my $id = shift;
+	my $fetch_from = shift;
+	my @page_revs = ();
+	my $query = {
+		action => 'query',
+		prop => 'revisions',
+		rvprop => 'ids',
+		rvdir => 'newer',
+		rvstartid => $fetch_from,
+		rvlimit => 500,
+		pageids => $id,
+	};
+
+	my $revnum = 0;
+	# Get 500 revisions at a time due to the mediawiki api limit
+	while (1) {
+		my $result = $mediawiki->api($query);
+
+		# Parse each of those 500 revisions
+		foreach my $revision (@{$result->{query}->{pages}->{$id}->{revisions}}) {
+			my $page_rev_ids;
+			$page_rev_ids->{pageid} = $page->{pageid};
+			$page_rev_ids->{revid} = $revision->{revid};
+			push(@page_revs, $page_rev_ids);
+			$revnum++;
+		}
+		last unless $result->{'query-continue'};
+		$query->{rvstartid} = $result->{'query-continue'}->{revisions}->{rvstartid};
+	}
+	if ($shallow_import && @page_revs) {
+		print STDERR "  Found 1 revision (shallow import).\n";
+		@page_revs = sort {$b->{revid} <=> $a->{revid}} (@page_revs);
+		return $page_revs[0];
+	}
+	print STDERR "  Found ", $revnum, " revision(s).\n";
+	return @page_revs;
+}
+
+sub fetch_mw_revisions {
+	my $pages = shift; my @pages = @{$pages};
+	my $fetch_from = shift;
+
+	my @revisions = ();
+	my $n = 1;
+	foreach my $page (@pages) {
+		my $id = $page->{pageid};
+
+		print STDERR "page $n/", scalar(@pages), ": ". $page->{title} ."\n";
+		$n++;
+		my @page_revs = fetch_mw_revisions_for_page($page, $id, $fetch_from);
+		@revisions = (@page_revs, @revisions);
+	}
+
+	return ($n, @revisions);
+}
+
+sub import_file_revision {
+	my $commit = shift;
+	my %commit = %{$commit};
+	my $full_import = shift;
+	my $n = shift;
+	my $mediafile = shift;
+	my %mediafile;
+	if ($mediafile) {
+		%mediafile = %{$mediafile};
+	}
+
+	my $title = $commit{title};
+	my $comment = $commit{comment};
+	my $content = $commit{content};
+	my $author = $commit{author};
+	my $date = $commit{date};
+
+	print STDOUT "commit refs/mediawiki/$remotename/master\n";
+	print STDOUT "mark :$n\n";
+	print STDOUT "committer $author <$author\@$wiki_name> ", $date->epoch, " +0000\n";
+	literal_data($comment);
+
+	# If it's not a clone, we need to know where to start from
+	if (!$full_import && $n == 1) {
+		print STDOUT "from refs/mediawiki/$remotename/master^0\n";
+	}
+	if ($content ne DELETED_CONTENT) {
+		print STDOUT "M 644 inline $title.mw\n";
+		literal_data($content);
+		if (%mediafile) {
+			print STDOUT "M 644 inline $mediafile{title}\n";
+			literal_data_raw($mediafile{content});
+		}
+		print STDOUT "\n\n";
+	} else {
+		print STDOUT "D $title.mw\n";
+	}
+
+	# mediawiki revision number in the git note
+	if ($full_import && $n == 1) {
+		print STDOUT "reset refs/notes/$remotename/mediawiki\n";
+	}
+	print STDOUT "commit refs/notes/$remotename/mediawiki\n";
+	print STDOUT "committer $author <$author\@$wiki_name> ", $date->epoch, " +0000\n";
+	literal_data("Note added by git-mediawiki during import");
+	if (!$full_import && $n == 1) {
+		print STDOUT "from refs/notes/$remotename/mediawiki^0\n";
+	}
+	print STDOUT "N inline :$n\n";
+	literal_data("mediawiki_revision: " . $commit{mw_revision});
+	print STDOUT "\n\n";
+}
+
+# parse a sequence of
+# <cmd> <arg1>
+# <cmd> <arg2>
+# \n
+# (like batch sequence of import and sequence of push statements)
+sub get_more_refs {
+	my $cmd = shift;
+	my @refs;
+	while (1) {
+		my $line = <STDIN>;
+		if ($line =~ m/^$cmd (.*)$/) {
+			push(@refs, $1);
+		} elsif ($line eq "\n") {
+			return @refs;
+		} else {
+			die("Invalid command in a '$cmd' batch: ". $_);
+		}
+	}
+}
+
+sub mw_import {
+	# multiple import commands can follow each other.
+	my @refs = (shift, get_more_refs("import"));
+	foreach my $ref (@refs) {
+		mw_import_ref($ref);
+	}
+	print STDOUT "done\n";
+}
+
+sub mw_import_ref {
+	my $ref = shift;
+	# The remote helper will call "import HEAD" and
+	# "import refs/heads/master".
+	# Since HEAD is a symbolic ref to master (by convention,
+	# followed by the output of the command "list" that we gave),
+	# we don't need to do anything in this case.
+	if ($ref eq "HEAD") {
+		return;
+	}
+
+	mw_connect_maybe();
+
+	print STDERR "Searching revisions...\n";
+	my $last_local = get_last_local_revision();
+	my $fetch_from = $last_local + 1;
+	if ($fetch_from == 1) {
+		print STDERR ", fetching from beginning.\n";
+	} else {
+		print STDERR ", fetching from here.\n";
+	}
+
+	my $n = 0;
+	if ($fetch_strategy eq "by_rev") {
+		print STDERR "Fetching & writing export data by revs...\n";
+		$n = mw_import_ref_by_revs($fetch_from);
+	} elsif ($fetch_strategy eq "by_page") {
+		print STDERR "Fetching & writing export data by pages...\n";
+		$n = mw_import_ref_by_pages($fetch_from);
+	} else {
+		print STDERR "fatal: invalid fetch strategy \"$fetch_strategy\".\n";
+		print STDERR "Check your configuration variables remote.$remotename.fetchStrategy and mediawiki.fetchStrategy\n";
+		exit 1;
+	}
+
+	if ($fetch_from == 1 && $n == 0) {
+		print STDERR "You appear to have cloned an empty MediaWiki.\n";
+		# Something has to be done remote-helper side. If nothing is done, an error is
+		# thrown saying that HEAD is refering to unknown object 0000000000000000000
+		# and the clone fails.
+	}
+}
+
+sub mw_import_ref_by_pages {
+
+	my $fetch_from = shift;
+	my %pages_hash = get_mw_pages();
+	my @pages = values(%pages_hash);
+
+	my ($n, @revisions) = fetch_mw_revisions(\@pages, $fetch_from);
+
+	@revisions = sort {$a->{revid} <=> $b->{revid}} @revisions;
+	my @revision_ids = map $_->{revid}, @revisions;
+
+	return mw_import_revids($fetch_from, \@revision_ids, \%pages_hash);
+}
+
+sub mw_import_ref_by_revs {
+
+	my $fetch_from = shift;
+	my %pages_hash = get_mw_pages();
+
+	my $last_remote = get_last_global_remote_rev();
+	my @revision_ids = $fetch_from..$last_remote;
+	return mw_import_revids($fetch_from, \@revision_ids, \%pages_hash);
+}
+
+# Import revisions given in second argument (array of integers).
+# Only pages appearing in the third argument (hash indexed by page titles)
+# will be imported.
+sub mw_import_revids {
+	my $fetch_from = shift;
+	my $revision_ids = shift;
+	my $pages = shift;
+
+	my $n = 0;
+	my $n_actual = 0;
+	my $last_timestamp = 0; # Placeholer in case $rev->timestamp is undefined
+
+	foreach my $pagerevid (@$revision_ids) {
+	        # Count page even if we skip it, since we display
+		# $n/$total and $total includes skipped pages.
+		$n++;
+
+		# fetch the content of the pages
+		my $query = {
+			action => 'query',
+			prop => 'revisions',
+			rvprop => 'content|timestamp|comment|user|ids',
+			revids => $pagerevid,
+		};
+
+		my $result = $mediawiki->api($query);
+
+		if (!$result) {
+			die "Failed to retrieve modified page for revision $pagerevid";
+		}
+
+		if (defined($result->{query}->{badrevids}->{$pagerevid})) {
+			# The revision id does not exist on the remote wiki.
+			next;
+		}
+
+		if (!defined($result->{query}->{pages})) {
+			die "Invalid revision $pagerevid.";
+		}
+
+		my @result_pages = values(%{$result->{query}->{pages}});
+		my $result_page = $result_pages[0];
+		my $rev = $result_pages[0]->{revisions}->[0];
+
+		my $page_title = $result_page->{title};
+
+		if (!exists($pages->{$page_title})) {
+			print STDERR "$n/", scalar(@$revision_ids),
+				": Skipping revision #$rev->{revid} of $page_title\n";
+			next;
+		}
+
+		$n_actual++;
+
+		my %commit;
+		$commit{author} = $rev->{user} || 'Anonymous';
+		$commit{comment} = $rev->{comment} || EMPTY_MESSAGE;
+		$commit{title} = mediawiki_smudge_filename($page_title);
+		$commit{mw_revision} = $rev->{revid};
+		$commit{content} = mediawiki_smudge($rev->{'*'});
+
+		if (!defined($rev->{timestamp})) {
+			$last_timestamp++;
+		} else {
+			$last_timestamp = $rev->{timestamp};
+		}
+		$commit{date} = DateTime::Format::ISO8601->parse_datetime($last_timestamp);
+
+		# Differentiates classic pages and media files.
+		my ($namespace, $filename) = $page_title =~ /^([^:]*):(.*)$/;
+		my %mediafile;
+		if ($namespace) {
+			my $id = get_mw_namespace_id($namespace);
+			if ($id && $id == get_mw_namespace_id("File")) {
+				%mediafile = get_mw_mediafile_for_page_revision($filename, $rev->{timestamp});
+			}
+		}
+		# If this is a revision of the media page for new version
+		# of a file do one common commit for both file and media page.
+		# Else do commit only for that page.
+		print STDERR "$n/", scalar(@$revision_ids), ": Revision #$rev->{revid} of $commit{title}\n";
+		import_file_revision(\%commit, ($fetch_from == 1), $n_actual, \%mediafile);
+	}
+
+	return $n_actual;
+}
+
+sub error_non_fast_forward {
+	my $advice = run_git("config --bool advice.pushNonFastForward");
+	chomp($advice);
+	if ($advice ne "false") {
+		# Native git-push would show this after the summary.
+		# We can't ask it to display it cleanly, so print it
+		# ourselves before.
+		print STDERR "To prevent you from losing history, non-fast-forward updates were rejected\n";
+		print STDERR "Merge the remote changes (e.g. 'git pull') before pushing again. See the\n";
+		print STDERR "'Note about fast-forwards' section of 'git push --help' for details.\n";
+	}
+	print STDOUT "error $_[0] \"non-fast-forward\"\n";
+	return 0;
+}
+
+sub mw_upload_file {
+	my $complete_file_name = shift;
+	my $new_sha1 = shift;
+	my $extension = shift;
+	my $file_deleted = shift;
+	my $summary = shift;
+	my $newrevid;
+	my $path = "File:" . $complete_file_name;
+	my %hashFiles = get_allowed_file_extensions();
+	if (!exists($hashFiles{$extension})) {
+		print STDERR "$complete_file_name is not a permitted file on this wiki.\n";
+		print STDERR "Check the configuration of file uploads in your mediawiki.\n";
+		return $newrevid;
+	}
+	# Deleting and uploading a file requires a priviledged user
+	if ($file_deleted) {
+		mw_connect_maybe();
+		my $query = {
+			action => 'delete',
+			title => $path,
+			reason => $summary
+		};
+		if (!$mediawiki->edit($query)) {
+			print STDERR "Failed to delete file on remote wiki\n";
+			print STDERR "Check your permissions on the remote site. Error code:\n";
+			print STDERR $mediawiki->{error}->{code} . ':' . $mediawiki->{error}->{details};
+			exit 1;
+		}
+	} else {
+		# Don't let perl try to interpret file content as UTF-8 => use "raw"
+		my $content = run_git("cat-file blob $new_sha1", "raw");
+		if ($content ne "") {
+			mw_connect_maybe();
+			$mediawiki->{config}->{upload_url} =
+				"$url/index.php/Special:Upload";
+			$mediawiki->edit({
+				action => 'upload',
+				filename => $complete_file_name,
+				comment => $summary,
+				file => [undef,
+					 $complete_file_name,
+					 Content => $content],
+				ignorewarnings => 1,
+			}, {
+				skip_encoding => 1
+			} ) || die $mediawiki->{error}->{code} . ':'
+				 . $mediawiki->{error}->{details};
+			my $last_file_page = $mediawiki->get_page({title => $path});
+			$newrevid = $last_file_page->{revid};
+			print STDERR "Pushed file: $new_sha1 - $complete_file_name.\n";
+		} else {
+			print STDERR "Empty file $complete_file_name not pushed.\n";
+		}
+	}
+	return $newrevid;
+}
+
+sub mw_push_file {
+	my $diff_info = shift;
+	# $diff_info contains a string in this format:
+	# 100644 100644 <sha1_of_blob_before_commit> <sha1_of_blob_now> <status>
+	my @diff_info_split = split(/[ \t]/, $diff_info);
+
+	# Filename, including .mw extension
+	my $complete_file_name = shift;
+	# Commit message
+	my $summary = shift;
+	# MediaWiki revision number. Keep the previous one by default,
+	# in case there's no edit to perform.
+	my $oldrevid = shift;
+	my $newrevid;
+
+	if ($summary eq EMPTY_MESSAGE) {
+		$summary = '';
+	}
+
+	my $new_sha1 = $diff_info_split[3];
+	my $old_sha1 = $diff_info_split[2];
+	my $page_created = ($old_sha1 eq NULL_SHA1);
+	my $page_deleted = ($new_sha1 eq NULL_SHA1);
+	$complete_file_name = mediawiki_clean_filename($complete_file_name);
+
+	my ($title, $extension) = $complete_file_name =~ /^(.*)\.([^\.]*)$/;
+	if (!defined($extension)) {
+		$extension = "";
+	}
+	if ($extension eq "mw") {
+		my $ns = get_mw_namespace_id_for_page($complete_file_name);
+		if ($ns && $ns == get_mw_namespace_id("File") && (!$export_media)) {
+			print STDERR "Ignoring media file related page: $complete_file_name\n";
+			return ($oldrevid, "ok");
+		}
+		my $file_content;
+		if ($page_deleted) {
+			# Deleting a page usually requires
+			# special priviledges. A common
+			# convention is to replace the page
+			# with this content instead:
+			$file_content = DELETED_CONTENT;
+		} else {
+			$file_content = run_git("cat-file blob $new_sha1");
+		}
+
+		mw_connect_maybe();
+
+		my $result = $mediawiki->edit( {
+			action => 'edit',
+			summary => $summary,
+			title => $title,
+			basetimestamp => $basetimestamps{$oldrevid},
+			text => mediawiki_clean($file_content, $page_created),
+				  }, {
+					  skip_encoding => 1 # Helps with names with accentuated characters
+				  });
+		if (!$result) {
+			if ($mediawiki->{error}->{code} == 3) {
+				# edit conflicts, considered as non-fast-forward
+				print STDERR 'Warning: Error ' .
+				    $mediawiki->{error}->{code} .
+				    ' from mediwiki: ' . $mediawiki->{error}->{details} .
+				    ".\n";
+				return ($oldrevid, "non-fast-forward");
+			} else {
+				# Other errors. Shouldn't happen => just die()
+				die 'Fatal: Error ' .
+				    $mediawiki->{error}->{code} .
+				    ' from mediwiki: ' . $mediawiki->{error}->{details};
+			}
+		}
+		$newrevid = $result->{edit}->{newrevid};
+		print STDERR "Pushed file: $new_sha1 - $title\n";
+	} elsif ($export_media) {
+		$newrevid = mw_upload_file($complete_file_name, $new_sha1,
+					   $extension, $page_deleted,
+					   $summary);
+	} else {
+		print STDERR "Ignoring media file $title\n";
+	}
+	$newrevid = ($newrevid or $oldrevid);
+	return ($newrevid, "ok");
+}
+
+sub mw_push {
+	# multiple push statements can follow each other
+	my @refsspecs = (shift, get_more_refs("push"));
+	my $pushed;
+	for my $refspec (@refsspecs) {
+		my ($force, $local, $remote) = $refspec =~ /^(\+)?([^:]*):([^:]*)$/
+		    or die("Invalid refspec for push. Expected <src>:<dst> or +<src>:<dst>");
+		if ($force) {
+			print STDERR "Warning: forced push not allowed on a MediaWiki.\n";
+		}
+		if ($local eq "") {
+			print STDERR "Cannot delete remote branch on a MediaWiki\n";
+			print STDOUT "error $remote cannot delete\n";
+			next;
+		}
+		if ($remote ne "refs/heads/master") {
+			print STDERR "Only push to the branch 'master' is supported on a MediaWiki\n";
+			print STDOUT "error $remote only master allowed\n";
+			next;
+		}
+		if (mw_push_revision($local, $remote)) {
+			$pushed = 1;
+		}
+	}
+
+	# Notify Git that the push is done
+	print STDOUT "\n";
+
+	if ($pushed && $dumb_push) {
+		print STDERR "Just pushed some revisions to MediaWiki.\n";
+		print STDERR "The pushed revisions now have to be re-imported, and your current branch\n";
+		print STDERR "needs to be updated with these re-imported commits. You can do this with\n";
+		print STDERR "\n";
+		print STDERR "  git pull --rebase\n";
+		print STDERR "\n";
+	}
+}
+
+sub mw_push_revision {
+	my $local = shift;
+	my $remote = shift; # actually, this has to be "refs/heads/master" at this point.
+	my $last_local_revid = get_last_local_revision();
+	print STDERR ".\n"; # Finish sentence started by get_last_local_revision()
+	my $last_remote_revid = get_last_remote_revision();
+	my $mw_revision = $last_remote_revid;
+
+	# Get sha1 of commit pointed by local HEAD
+	my $HEAD_sha1 = run_git("rev-parse $local 2>/dev/null"); chomp($HEAD_sha1);
+	# Get sha1 of commit pointed by remotes/$remotename/master
+	my $remoteorigin_sha1 = run_git("rev-parse refs/remotes/$remotename/master 2>/dev/null");
+	chomp($remoteorigin_sha1);
+
+	if ($last_local_revid > 0 &&
+	    $last_local_revid < $last_remote_revid) {
+		return error_non_fast_forward($remote);
+	}
+
+	if ($HEAD_sha1 eq $remoteorigin_sha1) {
+		# nothing to push
+		return 0;
+	}
+
+	# Get every commit in between HEAD and refs/remotes/origin/master,
+	# including HEAD and refs/remotes/origin/master
+	my @commit_pairs = ();
+	if ($last_local_revid > 0) {
+		my $parsed_sha1 = $remoteorigin_sha1;
+		# Find a path from last MediaWiki commit to pushed commit
+		print STDERR "Computing path from local to remote ...\n";
+		my @local_ancestry = split(/\n/, run_git("rev-list --boundary --parents $local ^$parsed_sha1"));
+		my %local_ancestry;
+		foreach my $line (@local_ancestry) {
+			if (my ($child, $parents) = $line =~ m/^-?([a-f0-9]+) ([a-f0-9 ]+)/) {
+				foreach my $parent (split(' ', $parents)) {
+					$local_ancestry{$parent} = $child;
+				}
+			} elsif (!$line =~ m/^([a-f0-9]+)/) {
+				die "Unexpected output from git rev-list: $line";
+			}
+		}
+		while ($parsed_sha1 ne $HEAD_sha1) {
+			my $child = $local_ancestry{$parsed_sha1};
+			if (!$child) {
+				printf STDERR "Cannot find a path in history from remote commit to last commit\n";
+				return error_non_fast_forward($remote);
+			}
+			push(@commit_pairs, [$parsed_sha1, $child]);
+			$parsed_sha1 = $child;
+		}
+	} else {
+		# No remote mediawiki revision. Export the whole
+		# history (linearized with --first-parent)
+		print STDERR "Warning: no common ancestor, pushing complete history\n";
+		my $history = run_git("rev-list --first-parent --children $local");
+		my @history = split('\n', $history);
+		@history = @history[1..$#history];
+		foreach my $line (reverse @history) {
+			my @commit_info_split = split(/ |\n/, $line);
+			push(@commit_pairs, \@commit_info_split);
+		}
+	}
+
+	foreach my $commit_info_split (@commit_pairs) {
+		my $sha1_child = @{$commit_info_split}[0];
+		my $sha1_commit = @{$commit_info_split}[1];
+		my $diff_infos = run_git("diff-tree -r --raw -z $sha1_child $sha1_commit");
+		# TODO: we could detect rename, and encode them with a #redirect on the wiki.
+		# TODO: for now, it's just a delete+add
+		my @diff_info_list = split(/\0/, $diff_infos);
+		# Keep the subject line of the commit message as mediawiki comment for the revision
+		my $commit_msg = run_git("log --no-walk --format=\"%s\" $sha1_commit");
+		chomp($commit_msg);
+		# Push every blob
+		while (@diff_info_list) {
+			my $status;
+			# git diff-tree -z gives an output like
+			# <metadata>\0<filename1>\0
+			# <metadata>\0<filename2>\0
+			# and we've split on \0.
+			my $info = shift(@diff_info_list);
+			my $file = shift(@diff_info_list);
+			($mw_revision, $status) = mw_push_file($info, $file, $commit_msg, $mw_revision);
+			if ($status eq "non-fast-forward") {
+				# we may already have sent part of the
+				# commit to MediaWiki, but it's too
+				# late to cancel it. Stop the push in
+				# the middle, but still give an
+				# accurate error message.
+				return error_non_fast_forward($remote);
+			}
+			if ($status ne "ok") {
+				die("Unknown error from mw_push_file()");
+			}
+		}
+		unless ($dumb_push) {
+			run_git("notes --ref=$remotename/mediawiki add -f -m \"mediawiki_revision: $mw_revision\" $sha1_commit");
+			run_git("update-ref -m \"Git-MediaWiki push\" refs/mediawiki/$remotename/master $sha1_commit $sha1_child");
+		}
+	}
+
+	print STDOUT "ok $remote\n";
+	return 1;
+}
+
+sub get_allowed_file_extensions {
+	mw_connect_maybe();
+
+	my $query = {
+		action => 'query',
+		meta => 'siteinfo',
+		siprop => 'fileextensions'
+		};
+	my $result = $mediawiki->api($query);
+	my @file_extensions= map $_->{ext},@{$result->{query}->{fileextensions}};
+	my %hashFile = map {$_ => 1}@file_extensions;
+
+	return %hashFile;
+}
+
+# In memory cache for MediaWiki namespace ids.
+my %namespace_id;
+
+# Namespaces whose id is cached in the configuration file
+# (to avoid duplicates)
+my %cached_mw_namespace_id;
+
+# Return MediaWiki id for a canonical namespace name.
+# Ex.: "File", "Project".
+sub get_mw_namespace_id {
+	mw_connect_maybe();
+	my $name = shift;
+
+	if (!exists $namespace_id{$name}) {
+		# Look at configuration file, if the record for that namespace is
+		# already cached. Namespaces are stored in form:
+		# "Name_of_namespace:Id_namespace", ex.: "File:6".
+		my @temp = split(/[\n]/, run_git("config --get-all remote."
+						. $remotename .".namespaceCache"));
+		chomp(@temp);
+		foreach my $ns (@temp) {
+			my ($n, $id) = split(/:/, $ns);
+			if ($id eq 'notANameSpace') {
+				$namespace_id{$n} = {is_namespace => 0};
+			} else {
+				$namespace_id{$n} = {is_namespace => 1, id => $id};
+			}
+			$cached_mw_namespace_id{$n} = 1;
+		}
+	}
+
+	if (!exists $namespace_id{$name}) {
+		print STDERR "Namespace $name not found in cache, querying the wiki ...\n";
+		# NS not found => get namespace id from MW and store it in
+	        # configuration file.
+	        my $query = {
+	                action => 'query',
+	                meta => 'siteinfo',
+	                siprop => 'namespaces'
+	        };
+	        my $result = $mediawiki->api($query);
+
+	        while (my ($id, $ns) = each(%{$result->{query}->{namespaces}})) {
+	                if (defined($ns->{id}) && defined($ns->{canonical})) {
+				$namespace_id{$ns->{canonical}} = {is_namespace => 1, id => $ns->{id}};
+				if ($ns->{'*'}) {
+					# alias (e.g. french Fichier: as alias for canonical File:)
+					$namespace_id{$ns->{'*'}} = {is_namespace => 1, id => $ns->{id}};
+				}
+			}
+	        }
+	}
+
+	my $ns = $namespace_id{$name};
+	my $id;
+
+	unless (defined $ns) {
+		print STDERR "No such namespace $name on MediaWiki.\n";
+		$ns = {is_namespace => 0};
+		$namespace_id{$name} = $ns;
+	}
+
+	if ($ns->{is_namespace}) {
+		$id = $ns->{id};
+	}
+
+	# Store "notANameSpace" as special value for inexisting namespaces
+	my $store_id = ($id || 'notANameSpace');
+
+	# Store explicitely requested namespaces on disk
+	if (!exists $cached_mw_namespace_id{$name}) {
+		run_git("config --add remote.". $remotename
+			.".namespaceCache \"". $name .":". $store_id ."\"");
+		$cached_mw_namespace_id{$name} = 1;
+	}
+	return $id;
+}
+
+sub get_mw_namespace_id_for_page {
+	if (my ($namespace) = $_[0] =~ /^([^:]*):/) {
+		return get_mw_namespace_id($namespace);
+	} else {
+		return;
+	}
+}
diff --git a/contrib/mw-to-git/git-remote-mediawiki.txt b/contrib/mw-to-git/git-remote-mediawiki.txt
new file mode 100644
index 0000000..4d211f5
--- /dev/null
+++ b/contrib/mw-to-git/git-remote-mediawiki.txt
@@ -0,0 +1,7 @@
+Git-Mediawiki is a project which aims the creation of a gate
+between git and mediawiki, allowing git users to push and pull
+objects from mediawiki just as one would do with a classic git
+repository thanks to remote-helpers.
+
+For more information, visit the wiki at
+https://github.com/Bibzball/Git-Mediawiki/wiki
diff --git a/contrib/mw-to-git/t/.gitignore b/contrib/mw-to-git/t/.gitignore
new file mode 100644
index 0000000..a7a40b4
--- /dev/null
+++ b/contrib/mw-to-git/t/.gitignore
@@ -0,0 +1,4 @@
+WEB/
+wiki/
+trash directory.t*/
+test-results/
diff --git a/contrib/mw-to-git/t/Makefile b/contrib/mw-to-git/t/Makefile
new file mode 100644
index 0000000..f422203
--- /dev/null
+++ b/contrib/mw-to-git/t/Makefile
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 2012
+#     Charles Roussel <charles.roussel@ensimag.imag.fr>
+#     Simon Cathebras <simon.cathebras@ensimag.imag.fr>
+#     Julien Khayat <julien.khayat@ensimag.imag.fr>
+#     Guillaume Sasdy <guillaume.sasdy@ensimag.imag.fr>
+#     Simon Perrat <simon.perrat@ensimag.imag.fr>
+#
+## Test git-remote-mediawiki
+
+all: test
+
+-include ../../../config.mak.autogen
+-include ../../../config.mak
+
+T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
+
+.PHONY: help test clean all
+
+help:
+	@echo 'Run "$(MAKE) test" to launch test scripts'
+	@echo 'Run "$(MAKE) clean" to remove trash folders'
+
+test:
+	@for t in $(T); do \
+		echo "$$t"; \
+		"./$$t" || exit 1; \
+	done
+
+clean:
+	$(RM) -r 'trash directory'.*
diff --git a/contrib/mw-to-git/t/README b/contrib/mw-to-git/t/README
new file mode 100644
index 0000000..96e9739
--- /dev/null
+++ b/contrib/mw-to-git/t/README
@@ -0,0 +1,124 @@
+Tests for Mediawiki-to-Git
+==========================
+
+Introduction
+------------
+This manual describes how to install the git-remote-mediawiki test
+environment on a machine with git installed on it.
+
+Prerequisite
+------------
+
+In order to run this test environment correctly, you will need to
+install the following packages (Debian/Ubuntu names, may need to be
+adapted for another distribution):
+
+* lighttpd
+* php5
+* php5-cgi
+* php5-cli
+* php5-curl
+* php5-sqlite
+
+Principles and Technical Choices
+--------------------------------
+
+The test environment makes it easy to install and manipulate one or
+several MediaWiki instances. To allow developers to run the testsuite
+easily, the environment does not require root priviledge (except to
+install the required packages if needed). It starts a webserver
+instance on the user's account (using lighttpd greatly helps for
+that), and does not need a separate database daemon (thanks to the use
+of sqlite).
+
+Run the test environment
+------------------------
+
+Install a new wiki
+~~~~~~~~~~~~~~~~~~
+
+Once you have all the prerequisite, you need to install a MediaWiki
+instance on your machine. If you already have one, it is still
+strongly recommended to install one with the script provided. Here's
+how to work it:
+
+a. change directory to contrib/mw-to-git/t/
+b. if needed, edit test.config to choose your installation parameters
+c. run `./install-wiki.sh install`
+d. check on your favourite web browser if your wiki is correctly
+   installed.
+
+Remove an existing wiki
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Edit the file test.config to fit the wiki you want to delete, and then
+execute the command `./install-wiki.sh delete` from the
+contrib/mw-to-git/t directory.
+
+Run the existing tests
+~~~~~~~~~~~~~~~~~~~~~~
+
+The provided tests are currently in the `contrib/mw-to-git/t` directory.
+The files are all the t936[0-9]-*.sh shell scripts.
+
+a. Run all tests:
+To do so, run "make test" from the contrib/mw-to-git/ directory.
+
+b. Run a specific test:
+To run a given test <test_name>, run ./<test_name> from the
+contrib/mw-to-git/t directory.
+
+How to create new tests
+-----------------------
+
+Available functions
+~~~~~~~~~~~~~~~~~~~
+
+The test environment of git-remote-mediawiki provides some functions
+useful to test its behaviour. for more details about the functions'
+parameters, please refer to the `test-gitmw-lib.sh` and
+`test-gitmw.pl` files.
+
+** `test_check_wiki_precond`:
+Check if the tests must be skipped or not. Please use this function
+at the beggining of each new test file.
+
+** `wiki_getpage`:
+Fetch a given page from the wiki and puts its content in the
+directory in parameter.
+
+** `wiki_delete_page`:
+Delete a given page from the wiki.
+
+** `wiki_edit_page`:
+Create or modify a given page in the wiki. You can specify several
+parameters like a summary for the page edition, or add the page to a
+given category.
+See test-gitmw.pl for more details.
+
+** `wiki_getallpage`:
+Fetch all pages from the wiki into a given directory. The directory
+is created if it does not exists.
+
+** `test_diff_directories`:
+Compare the content of two directories. The content must be the same.
+Use this function to compare the content of a git directory and a wiki
+one created by wiki_getallpage.
+
+** `test_contains_N_files`:
+Check if the given directory contains a given number of file.
+
+** `wiki_page_exists`:
+Tests if a given page exists on the wiki.
+
+** `wiki_reset`:
+Reset the wiki, i.e. flush the database. Use this function at the
+begining of each new test, except if the test re-uses the same wiki
+(and history) as the previous test.
+
+How to write a new test
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Please, follow the standards given by git. See git/t/README.
+New file should be named as t936[0-9]-*.sh.
+Be sure to reset your wiki regulary with the function `wiki_reset`.
diff --git a/contrib/mw-to-git/t/install-wiki.sh b/contrib/mw-to-git/t/install-wiki.sh
new file mode 100755
index 0000000..c6d6fa3
--- /dev/null
+++ b/contrib/mw-to-git/t/install-wiki.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+# This script installs or deletes a MediaWiki on your computer.
+# It requires a web server with PHP and SQLite running. In addition, if you
+# do not have MediaWiki sources on your computer, the option 'install'
+# downloads them for you.
+# Please set the CONFIGURATION VARIABLES in ./test-gitmw-lib.sh
+
+WIKI_TEST_DIR=$(cd "$(dirname "$0")" && pwd)
+
+if test -z "$WIKI_TEST_DIR"
+then
+	WIKI_TEST_DIR=.
+fi
+
+. "$WIKI_TEST_DIR"/test-gitmw-lib.sh
+usage () {
+	echo "Usage: "
+	echo "	./install-wiki.sh <install | delete | --help>"
+	echo "		install | -i :	Install a wiki on your computer."
+	echo "		delete | -d : Delete the wiki and all its pages and "
+	echo "			content."
+}
+
+
+# Argument: install, delete, --help | -h
+case "$1" in
+	"install" | "-i")
+		wiki_install
+		exit 0
+		;;
+	"delete" | "-d")
+		wiki_delete
+		exit 0
+		;;
+	"--help" | "-h")
+		usage
+		exit 0
+		;;
+	*)
+		echo "Invalid argument: $1"
+		usage
+		exit 1
+		;;
+esac
diff --git a/contrib/mw-to-git/t/install-wiki/.gitignore b/contrib/mw-to-git/t/install-wiki/.gitignore
new file mode 100644
index 0000000..b5a2a44
--- /dev/null
+++ b/contrib/mw-to-git/t/install-wiki/.gitignore
@@ -0,0 +1 @@
+wikidb.sqlite
diff --git a/contrib/mw-to-git/t/install-wiki/LocalSettings.php b/contrib/mw-to-git/t/install-wiki/LocalSettings.php
new file mode 100644
index 0000000..29f1251
--- /dev/null
+++ b/contrib/mw-to-git/t/install-wiki/LocalSettings.php
@@ -0,0 +1,129 @@
+<?php
+# This file was automatically generated by the MediaWiki 1.19.0
+# installer. If you make manual changes, please keep track in case you
+# need to recreate them later.
+#
+# See includes/DefaultSettings.php for all configurable settings
+# and their default values, but don't forget to make changes in _this_
+# file, not there.
+#
+# Further documentation for configuration settings may be found at:
+# http://www.mediawiki.org/wiki/Manual:Configuration_settings
+
+# Protect against web entry
+if ( !defined( 'MEDIAWIKI' ) ) {
+	exit;
+}
+
+## Uncomment this to disable output compression
+# $wgDisableOutputCompression = true;
+
+$wgSitename      = "Git-MediaWiki-Test";
+$wgMetaNamespace = "Git-MediaWiki-Test";
+
+## The URL base path to the directory containing the wiki;
+## defaults for all runtime URL paths are based off of this.
+## For more information on customizing the URLs please see:
+## http://www.mediawiki.org/wiki/Manual:Short_URL
+$wgScriptPath       = "@WG_SCRIPT_PATH@";
+$wgScriptExtension  = ".php";
+
+## The protocol and server name to use in fully-qualified URLs
+$wgServer           = "@WG_SERVER@";
+
+## The relative URL path to the skins directory
+$wgStylePath        = "$wgScriptPath/skins";
+
+## The relative URL path to the logo.  Make sure you change this from the default,
+## or else you'll overwrite your logo when you upgrade!
+$wgLogo             = "$wgStylePath/common/images/wiki.png";
+
+## UPO means: this is also a user preference option
+
+$wgEnableEmail      = true;
+$wgEnableUserEmail  = true; # UPO
+
+$wgEmergencyContact = "apache@localhost";
+$wgPasswordSender   = "apache@localhost";
+
+$wgEnotifUserTalk      = false; # UPO
+$wgEnotifWatchlist     = false; # UPO
+$wgEmailAuthentication = true;
+
+## Database settings
+$wgDBtype           = "sqlite";
+$wgDBserver         = "";
+$wgDBname           = "@WG_SQLITE_DATAFILE@";
+$wgDBuser           = "";
+$wgDBpassword       = "";
+
+# SQLite-specific settings
+$wgSQLiteDataDir    = "@WG_SQLITE_DATADIR@";
+
+
+## Shared memory settings
+$wgMainCacheType    = CACHE_NONE;
+$wgMemCachedServers = array();
+
+## To enable image uploads, make sure the 'images' directory
+## is writable, then set this to true:
+$wgEnableUploads  = true;
+$wgUseImageMagick = true;
+$wgImageMagickConvertCommand ="@CONVERT@";
+$wgFileExtensions[] = 'txt';
+
+# InstantCommons allows wiki to use images from http://commons.wikimedia.org
+$wgUseInstantCommons  = false;
+
+## If you use ImageMagick (or any other shell command) on a
+## Linux server, this will need to be set to the name of an
+## available UTF-8 locale
+$wgShellLocale = "en_US.utf8";
+
+## If you want to use image uploads under safe mode,
+## create the directories images/archive, images/thumb and
+## images/temp, and make them all writable. Then uncomment
+## this, if it's not already uncommented:
+#$wgHashedUploadDirectory = false;
+
+## Set $wgCacheDirectory to a writable directory on the web server
+## to make your wiki go slightly faster. The directory should not
+## be publically accessible from the web.
+#$wgCacheDirectory = "$IP/cache";
+
+# Site language code, should be one of the list in ./languages/Names.php
+$wgLanguageCode = "en";
+
+$wgSecretKey = "1c912bfe3519fb70f5dc523ecc698111cd43d81a11c585b3eefb28f29c2699b7";
+#$wgSecretKey = "@SECRETKEY@";
+
+
+# Site upgrade key. Must be set to a string (default provided) to turn on the
+# web installer while LocalSettings.php is in place
+$wgUpgradeKey = "ddae7dc87cd0a645";
+
+## Default skin: you can change the default skin. Use the internal symbolic
+## names, ie 'standard', 'nostalgia', 'cologneblue', 'monobook', 'vector':
+$wgDefaultSkin = "vector";
+
+## For attaching licensing metadata to pages, and displaying an
+## appropriate copyright notice / icon. GNU Free Documentation
+## License and Creative Commons licenses are supported so far.
+$wgRightsPage = ""; # Set to the title of a wiki page that describes your license/copyright
+$wgRightsUrl  = "";
+$wgRightsText = "";
+$wgRightsIcon = "";
+
+# Path to the GNU diff3 utility. Used for conflict resolution.
+$wgDiff3 = "/usr/bin/diff3";
+
+# Query string length limit for ResourceLoader. You should only set this if
+# your web server has a query string length limit (then set it to that limit),
+# or if you have suhosin.get.max_value_length set in php.ini (then set it to
+# that value)
+$wgResourceLoaderMaxQueryLength = -1;
+
+
+
+# End of automatically generated settings.
+# Add more configuration options below.
diff --git a/contrib/mw-to-git/t/install-wiki/db_install.php b/contrib/mw-to-git/t/install-wiki/db_install.php
new file mode 100644
index 0000000..0f3f4e0
--- /dev/null
+++ b/contrib/mw-to-git/t/install-wiki/db_install.php
@@ -0,0 +1,120 @@
+<?php
+/**
+ * This script generates a SQLite database for a MediaWiki version 1.19.0
+ * You must specify the login of the admin (argument 1) and its
+ * password (argument 2) and the folder where the database file
+ * is located (absolute path in argument 3).
+ * It is used by the script install-wiki.sh in order to make easy the
+ * installation of a MediaWiki.
+ *
+ * In order to generate a SQLite database file, MediaWiki ask the user
+ * to submit some forms in its web browser. This script simulates this
+ * behavior though the functions <get> and <submit>
+ *
+ */
+$argc = $_SERVER['argc'];
+$argv = $_SERVER['argv'];
+
+$login = $argv[2];
+$pass = $argv[3];
+$tmp = $argv[4];
+$port = $argv[5];
+
+$url = 'http://localhost:'.$port.'/wiki/mw-config/index.php';
+$db_dir = urlencode($tmp);
+$tmp_cookie = tempnam($tmp, "COOKIE_");
+/*
+ * Fetchs a page with cURL.
+ */
+function get($page_name = "") {
+	$curl = curl_init();
+	$page_name_add = "";
+	if ($page_name != "") {
+		$page_name_add = '?page='.$page_name;
+	}
+	$url = $GLOBALS['url'].$page_name_add;
+	$tmp_cookie = $GLOBALS['tmp_cookie'];
+	curl_setopt($curl, CURLOPT_COOKIEJAR, $tmp_cookie);
+	curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
+	curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
+	curl_setopt($curl, CURLOPT_COOKIEFILE, $tmp_cookie);
+	curl_setopt($curl, CURLOPT_HEADER, true);
+	curl_setopt($curl, CURLOPT_URL, $url);
+
+	$page = curl_exec($curl);
+	if (!$page) {
+		die("Could not get page: $url\n");
+	}
+	curl_close($curl);
+	return $page;
+}
+
+/*
+ * Submits a form with cURL.
+ */
+function submit($page_name, $option = "") {
+	$curl = curl_init();
+	$datapost = 'submit-continue=Continue+%E2%86%92';
+	if ($option != "") {
+		$datapost = $option.'&'.$datapost;
+	}
+	$url = $GLOBALS['url'].'?page='.$page_name;
+	$tmp_cookie = $GLOBALS['tmp_cookie'];
+	curl_setopt($curl, CURLOPT_URL, $url);
+	curl_setopt($curl, CURLOPT_POST, true);
+	curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
+	curl_setopt($curl, CURLOPT_POSTFIELDS, $datapost);
+	curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
+	curl_setopt($curl, CURLOPT_COOKIEJAR, $tmp_cookie);
+	curl_setopt($curl, CURLOPT_COOKIEFILE, $tmp_cookie);
+
+	$page = curl_exec($curl);
+	if (!$page) {
+		die("Could not get page: $url\n");
+	}
+	curl_close($curl);
+	return "$page";
+}
+
+/*
+ * Here starts this script: simulates the behavior of the user
+ * submitting forms to generates the database file.
+ * Note this simulation was made for the MediaWiki version 1.19.0,
+ * we can't assume it works with other versions.
+ *
+ */
+
+$page = get();
+if (!preg_match('/input type="hidden" value="([0-9]+)" name="LanguageRequestTime"/',
+		$page, $matches)) {
+	echo "Unexpected content for page downloaded:\n";
+	echo "$page";
+	die;
+};
+$timestamp = $matches[1];
+$language = "LanguageRequestTime=$timestamp&uselang=en&ContLang=en";
+$page = submit('Language', $language);
+
+submit('Welcome');
+
+$db_config = 'DBType=sqlite';
+$db_config = $db_config.'&sqlite_wgSQLiteDataDir='.$db_dir;
+$db_config = $db_config.'&sqlite_wgDBname='.$argv[1];
+submit('DBConnect', $db_config);
+
+$wiki_config = 'config_wgSitename=TEST';
+$wiki_config = $wiki_config.'&config__NamespaceType=site-name';
+$wiki_config = $wiki_config.'&config_wgMetaNamespace=MyWiki';
+$wiki_config = $wiki_config.'&config__AdminName='.$login;
+
+$wiki_config = $wiki_config.'&config__AdminPassword='.$pass;
+$wiki_config = $wiki_config.'&config__AdminPassword2='.$pass;
+
+$wiki_config = $wiki_config.'&wiki__configEmail=email%40email.org';
+$wiki_config = $wiki_config.'&config__SkipOptional=skip';
+submit('Name', $wiki_config);
+submit('Install');
+submit('Install');
+
+unlink($tmp_cookie);
+?>
diff --git a/contrib/mw-to-git/t/push-pull-tests.sh b/contrib/mw-to-git/t/push-pull-tests.sh
new file mode 100644
index 0000000..9da2dc5
--- /dev/null
+++ b/contrib/mw-to-git/t/push-pull-tests.sh
@@ -0,0 +1,144 @@
+test_push_pull () {
+
+	test_expect_success 'Git pull works after adding a new wiki page' '
+		wiki_reset &&
+
+		git clone mediawiki::'"$WIKI_URL"' mw_dir_1 &&
+		wiki_editpage Foo "page created after the git clone" false &&
+
+		(
+			cd mw_dir_1 &&
+			git pull
+		) &&
+
+		wiki_getallpage ref_page_1 &&
+		test_diff_directories mw_dir_1 ref_page_1
+	'
+
+	test_expect_success 'Git pull works after editing a wiki page' '
+		wiki_reset &&
+
+		wiki_editpage Foo "page created before the git clone" false &&
+		git clone mediawiki::'"$WIKI_URL"' mw_dir_2 &&
+		wiki_editpage Foo "new line added on the wiki" true &&
+
+		(
+			cd mw_dir_2 &&
+			git pull
+		) &&
+
+		wiki_getallpage ref_page_2 &&
+		test_diff_directories mw_dir_2 ref_page_2
+	'
+
+	test_expect_success 'git pull works on conflict handled by auto-merge' '
+		wiki_reset &&
+
+		wiki_editpage Foo "1 init
+3
+5
+	" false &&
+		git clone mediawiki::'"$WIKI_URL"' mw_dir_3 &&
+
+		wiki_editpage Foo "1 init
+2 content added on wiki after clone
+3
+5
+	" false &&
+
+		(
+			cd mw_dir_3 &&
+		echo "1 init
+3
+4 content added on git after clone
+5
+" >Foo.mw &&
+			git commit -am "conflicting change on foo" &&
+			git pull &&
+			git push
+		)
+	'
+
+	test_expect_success 'Git push works after adding a file .mw' '
+		wiki_reset &&
+		git clone mediawiki::'"$WIKI_URL"' mw_dir_4 &&
+		wiki_getallpage ref_page_4 &&
+		(
+			cd mw_dir_4 &&
+			test_path_is_missing Foo.mw &&
+			touch Foo.mw &&
+			echo "hello world" >>Foo.mw &&
+			git add Foo.mw &&
+			git commit -m "Foo" &&
+			git push
+		) &&
+		wiki_getallpage ref_page_4 &&
+		test_diff_directories mw_dir_4 ref_page_4
+	'
+
+	test_expect_success 'Git push works after editing a file .mw' '
+		wiki_reset &&
+		wiki_editpage "Foo" "page created before the git clone" false &&
+		git clone mediawiki::'"$WIKI_URL"' mw_dir_5 &&
+
+		(
+			cd mw_dir_5 &&
+			echo "new line added in the file Foo.mw" >>Foo.mw &&
+			git commit -am "edit file Foo.mw" &&
+			git push
+		) &&
+
+		wiki_getallpage ref_page_5 &&
+		test_diff_directories mw_dir_5 ref_page_5
+	'
+
+	test_expect_failure 'Git push works after deleting a file' '
+		wiki_reset &&
+		wiki_editpage Foo "wiki page added before git clone" false &&
+		git clone mediawiki::'"$WIKI_URL"' mw_dir_6 &&
+
+		(
+			cd mw_dir_6 &&
+			git rm Foo.mw &&
+			git commit -am "page Foo.mw deleted" &&
+			git push
+		) &&
+
+		test_must_fail wiki_page_exist Foo
+	'
+
+	test_expect_success 'Merge conflict expected and solving it' '
+		wiki_reset &&
+
+		git clone mediawiki::'"$WIKI_URL"' mw_dir_7 &&
+		wiki_editpage Foo "1 conflict
+3 wiki
+4" false &&
+
+		(
+			cd mw_dir_7 &&
+		echo "1 conflict
+2 git
+4" >Foo.mw &&
+			git add Foo.mw &&
+			git commit -m "conflict created" &&
+			test_must_fail git pull &&
+			"$PERL_PATH" -pi -e "s/[<=>].*//g" Foo.mw &&
+			git commit -am "merge conflict solved" &&
+			git push
+		)
+	'
+
+	test_expect_failure 'git pull works after deleting a wiki page' '
+		wiki_reset &&
+		wiki_editpage Foo "wiki page added before the git clone" false &&
+		git clone mediawiki::'"$WIKI_URL"' mw_dir_8 &&
+
+		wiki_delete_page Foo &&
+		(
+			cd mw_dir_8 &&
+			git pull &&
+			test_path_is_missing Foo.mw
+		)
+	'
+}
diff --git a/contrib/mw-to-git/t/t9360-mw-to-git-clone.sh b/contrib/mw-to-git/t/t9360-mw-to-git-clone.sh
new file mode 100755
index 0000000..811a90c
--- /dev/null
+++ b/contrib/mw-to-git/t/t9360-mw-to-git-clone.sh
@@ -0,0 +1,257 @@
+#!/bin/sh
+#
+# Copyright (C) 2012
+#     Charles Roussel <charles.roussel@ensimag.imag.fr>
+#     Simon Cathebras <simon.cathebras@ensimag.imag.fr>
+#     Julien Khayat <julien.khayat@ensimag.imag.fr>
+#     Guillaume Sasdy <guillaume.sasdy@ensimag.imag.fr>
+#     Simon Perrat <simon.perrat@ensimag.imag.fr>
+#
+# License: GPL v2 or later
+
+
+test_description='Test the Git Mediawiki remote helper: git clone'
+
+. ./test-gitmw-lib.sh
+. $TEST_DIRECTORY/test-lib.sh
+
+
+test_check_precond
+
+
+test_expect_success 'Git clone creates the expected git log with one file' '
+	wiki_reset &&
+	wiki_editpage foo "this is not important" false -c cat -s "this must be the same" &&
+	git clone mediawiki::'"$WIKI_URL"' mw_dir_1 &&
+	(
+		cd mw_dir_1 &&
+		git log --format=%s HEAD^..HEAD >log.tmp
+	) &&
+	echo "this must be the same" >msg.tmp &&
+	diff -b mw_dir_1/log.tmp msg.tmp
+'
+
+
+test_expect_success 'Git clone creates the expected git log with multiple files' '
+	wiki_reset &&
+	wiki_editpage daddy "this is not important" false -s="this must be the same" &&
+	wiki_editpage daddy "neither is this" true -s="this must also be the same" &&
+	wiki_editpage daddy "neither is this" true -s="same same same" &&
+	wiki_editpage dj "dont care" false -s="identical" &&
+	wiki_editpage dj "dont care either" true -s="identical too" &&
+	git clone mediawiki::'"$WIKI_URL"' mw_dir_2 &&
+	(
+		cd mw_dir_2 &&
+		git log --format=%s Daddy.mw  >logDaddy.tmp &&
+		git log --format=%s Dj.mw >logDj.tmp
+	) &&
+	echo "same same same" >msgDaddy.tmp &&
+	echo "this must also be the same" >>msgDaddy.tmp &&
+	echo "this must be the same" >>msgDaddy.tmp &&
+	echo "identical too" >msgDj.tmp &&
+	echo "identical" >>msgDj.tmp &&
+	diff -b mw_dir_2/logDaddy.tmp msgDaddy.tmp &&
+	diff -b mw_dir_2/logDj.tmp msgDj.tmp
+'
+
+
+test_expect_success 'Git clone creates only Main_Page.mw with an empty wiki' '
+	wiki_reset &&
+	git clone mediawiki::'"$WIKI_URL"' mw_dir_3 &&
+	test_contains_N_files mw_dir_3 1 &&
+	test_path_is_file mw_dir_3/Main_Page.mw
+'
+
+test_expect_success 'Git clone does not fetch a deleted page' '
+	wiki_reset &&
+	wiki_editpage foo "this page must be deleted before the clone" false &&
+	wiki_delete_page foo &&
+	git clone mediawiki::'"$WIKI_URL"' mw_dir_4 &&
+	test_contains_N_files mw_dir_4 1 &&
+	test_path_is_file mw_dir_4/Main_Page.mw &&
+	test_path_is_missing mw_dir_4/Foo.mw
+'
+
+test_expect_success 'Git clone works with page added' '
+	wiki_reset &&
+	wiki_editpage foo " I will be cloned" false &&
+	wiki_editpage bar "I will be cloned" false &&
+	git clone mediawiki::'"$WIKI_URL"' mw_dir_5 &&
+	wiki_getallpage ref_page_5 &&
+	test_diff_directories mw_dir_5 ref_page_5 &&
+	wiki_delete_page foo &&
+	wiki_delete_page bar
+'
+
+test_expect_success 'Git clone works with an edited page ' '
+	wiki_reset &&
+	wiki_editpage foo "this page will be edited" \
+		false -s "first edition of page foo"&&
+	wiki_editpage foo "this page has been edited and must be on the clone " true &&
+	git clone mediawiki::'"$WIKI_URL"' mw_dir_6 &&
+	test_path_is_file mw_dir_6/Foo.mw &&
+	test_path_is_file mw_dir_6/Main_Page.mw &&
+	wiki_getallpage mw_dir_6/page_ref_6 &&
+	test_diff_directories mw_dir_6 mw_dir_6/page_ref_6 &&
+	(
+		cd mw_dir_6 &&
+		git log --format=%s HEAD^ Foo.mw > ../Foo.log
+	) &&
+	echo "first edition of page foo" > FooExpect.log &&
+	diff FooExpect.log Foo.log
+'
+
+
+test_expect_success 'Git clone works with several pages and some deleted ' '
+	wiki_reset &&
+	wiki_editpage foo "this page will not be deleted" false &&
+	wiki_editpage bar "I must not be erased" false &&
+	wiki_editpage namnam "I will not be there at the end" false &&
+	wiki_editpage nyancat "nyan nyan nyan delete me" false &&
+	wiki_delete_page namnam &&
+	wiki_delete_page nyancat &&
+	git clone mediawiki::'"$WIKI_URL"' mw_dir_7 &&
+	test_path_is_file mw_dir_7/Foo.mw &&
+	test_path_is_file mw_dir_7/Bar.mw &&
+	test_path_is_missing mw_dir_7/Namnam.mw &&
+	test_path_is_missing mw_dir_7/Nyancat.mw &&
+	wiki_getallpage mw_dir_7/page_ref_7 &&
+	test_diff_directories mw_dir_7 mw_dir_7/page_ref_7
+'
+
+
+test_expect_success 'Git clone works with one specific page cloned ' '
+	wiki_reset &&
+	wiki_editpage foo "I will not be cloned" false &&
+	wiki_editpage bar "Do not clone me" false &&
+	wiki_editpage namnam "I will be cloned :)" false  -s="this log must stay" &&
+	wiki_editpage nyancat "nyan nyan nyan you cant clone me" false &&
+	git clone -c remote.origin.pages=namnam \
+		mediawiki::'"$WIKI_URL"' mw_dir_8 &&
+	test_contains_N_files mw_dir_8 1 &&
+	test_path_is_file mw_dir_8/Namnam.mw &&
+	test_path_is_missing mw_dir_8/Main_Page.mw &&
+	(
+		cd mw_dir_8 &&
+		echo "this log must stay" >msg.tmp &&
+		git log --format=%s >log.tmp &&
+		diff -b msg.tmp log.tmp
+	) &&
+	wiki_check_content mw_dir_8/Namnam.mw Namnam
+'
+
+test_expect_success 'Git clone works with multiple specific page cloned ' '
+	wiki_reset &&
+	wiki_editpage foo "I will be there" false &&
+	wiki_editpage bar "I will not disapear" false &&
+	wiki_editpage namnam "I be erased" false &&
+	wiki_editpage nyancat "nyan nyan nyan you will not erase me" false &&
+	wiki_delete_page namnam &&
+	git clone -c remote.origin.pages="foo bar nyancat namnam" \
+		mediawiki::'"$WIKI_URL"' mw_dir_9 &&
+	test_contains_N_files mw_dir_9 3 &&
+	test_path_is_missing mw_dir_9/Namnam.mw &&
+	test_path_is_file mw_dir_9/Foo.mw &&
+	test_path_is_file mw_dir_9/Nyancat.mw &&
+	test_path_is_file mw_dir_9/Bar.mw &&
+	wiki_check_content mw_dir_9/Foo.mw Foo &&
+	wiki_check_content mw_dir_9/Bar.mw Bar &&
+	wiki_check_content mw_dir_9/Nyancat.mw Nyancat
+'
+
+test_expect_success 'Mediawiki-clone of several specific pages on wiki' '
+	wiki_reset &&
+	wiki_editpage foo "foo 1" false &&
+	wiki_editpage bar "bar 1" false &&
+	wiki_editpage dummy "dummy 1" false &&
+	wiki_editpage cloned_1 "cloned_1 1" false &&
+	wiki_editpage cloned_2 "cloned_2 2" false &&
+	wiki_editpage cloned_3 "cloned_3 3" false &&
+	mkdir -p ref_page_10 &&
+	wiki_getpage cloned_1 ref_page_10 &&
+	wiki_getpage cloned_2 ref_page_10 &&
+	wiki_getpage cloned_3 ref_page_10 &&
+	git clone -c remote.origin.pages="cloned_1 cloned_2 cloned_3" \
+		mediawiki::'"$WIKI_URL"' mw_dir_10 &&
+	test_diff_directories mw_dir_10 ref_page_10
+'
+
+test_expect_success 'Git clone works with the shallow option' '
+	wiki_reset &&
+	wiki_editpage foo "1st revision, should be cloned" false &&
+	wiki_editpage bar "1st revision, should be cloned" false &&
+	wiki_editpage nyan "1st revision, should not be cloned" false &&
+	wiki_editpage nyan "2nd revision, should be cloned" false &&
+	git -c remote.origin.shallow=true clone \
+		mediawiki::'"$WIKI_URL"' mw_dir_11 &&
+	test_contains_N_files mw_dir_11 4 &&
+	test_path_is_file mw_dir_11/Nyan.mw &&
+	test_path_is_file mw_dir_11/Foo.mw &&
+	test_path_is_file mw_dir_11/Bar.mw &&
+	test_path_is_file mw_dir_11/Main_Page.mw &&
+	(
+		cd mw_dir_11 &&
+		test `git log --oneline Nyan.mw | wc -l` -eq 1 &&
+		test `git log --oneline Foo.mw | wc -l` -eq 1 &&
+		test `git log --oneline Bar.mw | wc -l` -eq 1 &&
+		test `git log --oneline Main_Page.mw | wc -l ` -eq 1
+	) &&
+	wiki_check_content mw_dir_11/Nyan.mw Nyan &&
+	wiki_check_content mw_dir_11/Foo.mw Foo &&
+	wiki_check_content mw_dir_11/Bar.mw Bar &&
+	wiki_check_content mw_dir_11/Main_Page.mw Main_Page
+'
+
+test_expect_success 'Git clone works with the shallow option with a delete page' '
+	wiki_reset &&
+	wiki_editpage foo "1st revision, will be deleted" false &&
+	wiki_editpage bar "1st revision, should be cloned" false &&
+	wiki_editpage nyan "1st revision, should not be cloned" false &&
+	wiki_editpage nyan "2nd revision, should be cloned" false &&
+	wiki_delete_page foo &&
+	git -c remote.origin.shallow=true clone \
+		mediawiki::'"$WIKI_URL"' mw_dir_12 &&
+	test_contains_N_files mw_dir_12 3 &&
+	test_path_is_file mw_dir_12/Nyan.mw &&
+	test_path_is_missing mw_dir_12/Foo.mw &&
+	test_path_is_file mw_dir_12/Bar.mw &&
+	test_path_is_file mw_dir_12/Main_Page.mw &&
+	(
+		cd mw_dir_12 &&
+		test `git log --oneline Nyan.mw | wc -l` -eq 1 &&
+		test `git log --oneline Bar.mw | wc -l` -eq 1 &&
+		test `git log --oneline Main_Page.mw | wc -l ` -eq 1
+	) &&
+	wiki_check_content mw_dir_12/Nyan.mw Nyan &&
+	wiki_check_content mw_dir_12/Bar.mw Bar &&
+	wiki_check_content mw_dir_12/Main_Page.mw Main_Page
+'
+
+test_expect_success 'Test of fetching a category' '
+	wiki_reset &&
+	wiki_editpage Foo "I will be cloned" false -c=Category &&
+	wiki_editpage Bar "Meet me on the repository" false -c=Category &&
+	wiki_editpage Dummy "I will not come" false &&
+	wiki_editpage BarWrong "I will stay online only" false -c=NotCategory &&
+	git clone -c remote.origin.categories="Category" \
+		mediawiki::'"$WIKI_URL"' mw_dir_13 &&
+	wiki_getallpage ref_page_13 Category &&
+	test_diff_directories mw_dir_13 ref_page_13
+'
+
+test_expect_success 'Test of resistance to modification of category on wiki for clone' '
+	wiki_reset &&
+	wiki_editpage Tobedeleted "this page will be deleted" false -c=Catone &&
+	wiki_editpage Tobeedited "this page will be modified" false -c=Catone &&
+	wiki_editpage Normalone "this page wont be modified and will be on git" false -c=Catone &&
+	wiki_editpage Notconsidered "this page will not appear on local" false &&
+	wiki_editpage Othercategory "this page will not appear on local" false -c=Cattwo &&
+	wiki_editpage Tobeedited "this page have been modified" true -c=Catone &&
+	wiki_delete_page Tobedeleted
+	git clone -c remote.origin.categories="Catone" \
+		mediawiki::'"$WIKI_URL"' mw_dir_14 &&
+	wiki_getallpage ref_page_14 Catone &&
+	test_diff_directories mw_dir_14 ref_page_14
+'
+
+test_done
diff --git a/contrib/mw-to-git/t/t9361-mw-to-git-push-pull.sh b/contrib/mw-to-git/t/t9361-mw-to-git-push-pull.sh
new file mode 100755
index 0000000..9ea2014
--- /dev/null
+++ b/contrib/mw-to-git/t/t9361-mw-to-git-push-pull.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+#
+# Copyright (C) 2012
+#     Charles Roussel <charles.roussel@ensimag.imag.fr>
+#     Simon Cathebras <simon.cathebras@ensimag.imag.fr>
+#     Julien Khayat <julien.khayat@ensimag.imag.fr>
+#     Guillaume Sasdy <guillaume.sasdy@ensimag.imag.fr>
+#     Simon Perrat <simon.perrat@ensimag.imag.fr>
+#
+# License: GPL v2 or later
+
+# tests for git-remote-mediawiki
+
+test_description='Test the Git Mediawiki remote helper: git push and git pull simple test cases'
+
+. ./test-gitmw-lib.sh
+. ./push-pull-tests.sh
+. $TEST_DIRECTORY/test-lib.sh
+
+test_check_precond
+
+test_push_pull
+
+test_done
diff --git a/contrib/mw-to-git/t/t9362-mw-to-git-utf8.sh b/contrib/mw-to-git/t/t9362-mw-to-git-utf8.sh
new file mode 100755
index 0000000..246d47d
--- /dev/null
+++ b/contrib/mw-to-git/t/t9362-mw-to-git-utf8.sh
@@ -0,0 +1,321 @@
+#!/bin/sh
+#
+# Copyright (C) 2012
+#     Charles Roussel <charles.roussel@ensimag.imag.fr>
+#     Simon Cathebras <simon.cathebras@ensimag.imag.fr>
+#     Julien Khayat <julien.khayat@ensimag.imag.fr>
+#     Guillaume Sasdy <guillaume.sasdy@ensimag.imag.fr>
+#     Simon Perrat <simon.perrat@ensimag.imag.fr>
+#
+# License: GPL v2 or later
+
+# tests for git-remote-mediawiki
+
+test_description='Test git-mediawiki with special characters in filenames'
+
+. ./test-gitmw-lib.sh
+. $TEST_DIRECTORY/test-lib.sh
+
+
+test_check_precond
+
+
+test_expect_success 'Git clone works for a wiki with accents in the page names' '
+	wiki_reset &&
+	wiki_editpage féé "This page must be délétéd before clone" false &&
+	wiki_editpage kèè "This page must be deleted before clone" false &&
+	wiki_editpage hàà "This page must be deleted before clone" false &&
+	wiki_editpage kîî "This page must be deleted before clone" false &&
+	wiki_editpage foo "This page must be deleted before clone" false &&
+	git clone mediawiki::'"$WIKI_URL"' mw_dir_1 &&
+	wiki_getallpage ref_page_1 &&
+	test_diff_directories mw_dir_1 ref_page_1
+'
+
+
+test_expect_success 'Git pull works with a wiki with accents in the pages names' '
+	wiki_reset &&
+	wiki_editpage kîî "this page must be cloned" false &&
+	wiki_editpage foo "this page must be cloned" false &&
+	git clone mediawiki::'"$WIKI_URL"' mw_dir_2 &&
+	wiki_editpage éàîôû "This page must be pulled" false &&
+	(
+		cd mw_dir_2 &&
+		git pull
+	) &&
+	wiki_getallpage ref_page_2 &&
+	test_diff_directories mw_dir_2 ref_page_2
+'
+
+
+test_expect_success 'Cloning a chosen page works with accents' '
+	wiki_reset &&
+	wiki_editpage kîî "this page must be cloned" false &&
+	git clone -c remote.origin.pages=kîî \
+		mediawiki::'"$WIKI_URL"' mw_dir_3 &&
+	wiki_check_content mw_dir_3/Kîî.mw Kîî &&
+	test_path_is_file mw_dir_3/Kîî.mw &&
+	rm -rf mw_dir_3
+'
+
+
+test_expect_success 'The shallow option works with accents' '
+	wiki_reset &&
+	wiki_editpage néoà "1st revision, should not be cloned" false &&
+	wiki_editpage néoà "2nd revision, should be cloned" false &&
+	git -c remote.origin.shallow=true clone \
+		mediawiki::'"$WIKI_URL"' mw_dir_4 &&
+	test_contains_N_files mw_dir_4 2 &&
+	test_path_is_file mw_dir_4/Néoà.mw &&
+	test_path_is_file mw_dir_4/Main_Page.mw &&
+	(
+		cd mw_dir_4 &&
+		test `git log --oneline Néoà.mw | wc -l` -eq 1 &&
+		test `git log --oneline Main_Page.mw | wc -l ` -eq 1
+	) &&
+	wiki_check_content mw_dir_4/Néoà.mw Néoà &&
+	wiki_check_content mw_dir_4/Main_Page.mw Main_Page
+'
+
+
+test_expect_success 'Cloning works when page name first letter has an accent' '
+	wiki_reset &&
+	wiki_editpage îî "this page must be cloned" false &&
+	git clone -c remote.origin.pages=îî \
+		mediawiki::'"$WIKI_URL"' mw_dir_5 &&
+	test_path_is_file mw_dir_5/Îî.mw &&
+	wiki_check_content mw_dir_5/Îî.mw Îî
+'
+
+
+test_expect_success 'Git push works with a wiki with accents' '
+	wiki_reset &&
+	wiki_editpage féé "lots of accents : éèàÖ" false &&
+	wiki_editpage foo "this page must be cloned" false &&
+	git clone mediawiki::'"$WIKI_URL"' mw_dir_6 &&
+	(
+		cd mw_dir_6 &&
+		echo "A wild Pîkächû appears on the wiki" >Pîkächû.mw &&
+		git add Pîkächû.mw &&
+		git commit -m "A new page appears" &&
+		git push
+	) &&
+	wiki_getallpage ref_page_6 &&
+	test_diff_directories mw_dir_6 ref_page_6
+'
+
+test_expect_success 'Git clone works with accentsand spaces' '
+	wiki_reset &&
+	wiki_editpage "é à î" "this page must be délété before the clone" false &&
+	git clone mediawiki::'"$WIKI_URL"' mw_dir_7 &&
+	wiki_getallpage ref_page_7 &&
+	test_diff_directories mw_dir_7 ref_page_7
+'
+
+test_expect_success 'character $ in page name (mw -> git)' '
+	wiki_reset &&
+	wiki_editpage file_\$_foo "expect to be called file_$_foo" false &&
+	git clone mediawiki::'"$WIKI_URL"' mw_dir_8 &&
+	test_path_is_file mw_dir_8/File_\$_foo.mw &&
+	wiki_getallpage ref_page_8 &&
+	test_diff_directories mw_dir_8 ref_page_8
+'
+
+
+
+test_expect_success 'character $ in file name (git -> mw) ' '
+	wiki_reset &&
+	git clone mediawiki::'"$WIKI_URL"' mw_dir_9 &&
+	(
+		cd mw_dir_9 &&
+		echo "this file is called File_\$_foo.mw" >File_\$_foo.mw &&
+		git add . &&
+		git commit -am "file File_\$_foo.mw" &&
+		git pull &&
+		git push
+	) &&
+	wiki_getallpage ref_page_9 &&
+	test_diff_directories mw_dir_9 ref_page_9
+'
+
+
+test_expect_failure 'capital at the begining of file names' '
+	wiki_reset &&
+	git clone mediawiki::'"$WIKI_URL"' mw_dir_10 &&
+	(
+		cd mw_dir_10 &&
+		echo "my new file foo" >foo.mw &&
+		echo "my new file Foo... Finger crossed" >Foo.mw &&
+		git add . &&
+		git commit -am "file foo.mw" &&
+		git pull &&
+		git push
+	) &&
+	wiki_getallpage ref_page_10 &&
+	test_diff_directories mw_dir_10 ref_page_10
+'
+
+
+test_expect_failure 'special character at the begining of file name from mw to git' '
+	wiki_reset &&
+	git clone mediawiki::'"$WIKI_URL"' mw_dir_11 &&
+	wiki_editpage {char_1 "expect to be renamed {char_1" false &&
+	wiki_editpage [char_2 "expect to be renamed [char_2" false &&
+	(
+		cd mw_dir_11 &&
+		git pull
+	) &&
+	test_path_is_file mw_dir_11/{char_1 &&
+	test_path_is_file mw_dir_11/[char_2
+'
+
+test_expect_success 'Pull page with title containing ":" other than namespace separator' '
+	wiki_editpage Foo:Bar content false &&
+	(
+		cd mw_dir_11 &&
+		git pull
+	) &&
+	test_path_is_file mw_dir_11/Foo:Bar.mw
+'
+
+test_expect_success 'Push page with title containing ":" other than namespace separator' '
+	(
+		cd mw_dir_11 &&
+		echo content >NotANameSpace:Page.mw &&
+		git add NotANameSpace:Page.mw &&
+		git commit -m "add page with colon" &&
+		git push
+	) &&
+	wiki_page_exist NotANameSpace:Page
+'
+
+test_expect_success 'test of correct formating for file name from mw to git' '
+	wiki_reset &&
+	git clone mediawiki::'"$WIKI_URL"' mw_dir_12 &&
+	wiki_editpage char_%_7b_1 "expect to be renamed char{_1" false &&
+	wiki_editpage char_%_5b_2 "expect to be renamed char{_2" false &&
+	(
+		cd mw_dir_12 &&
+		git pull
+	) &&
+	test_path_is_file mw_dir_12/Char\{_1.mw &&
+	test_path_is_file mw_dir_12/Char\[_2.mw &&
+	wiki_getallpage ref_page_12 &&
+	mv ref_page_12/Char_%_7b_1.mw ref_page_12/Char\{_1.mw &&
+	mv ref_page_12/Char_%_5b_2.mw ref_page_12/Char\[_2.mw &&
+	test_diff_directories mw_dir_12 ref_page_12
+'
+
+
+test_expect_failure 'test of correct formating for file name begining with special character' '
+	wiki_reset &&
+	git clone mediawiki::'"$WIKI_URL"' mw_dir_13 &&
+	(
+		cd mw_dir_13 &&
+		echo "my new file {char_1" >\{char_1.mw &&
+		echo "my new file [char_2" >\[char_2.mw &&
+		git add . &&
+		git commit -am "commiting some exotic file name..." &&
+		git push &&
+		git pull
+	) &&
+	wiki_getallpage ref_page_13 &&
+	test_path_is_file ref_page_13/{char_1.mw &&
+	test_path_is_file ref_page_13/[char_2.mw &&
+	test_diff_directories mw_dir_13 ref_page_13
+'
+
+
+test_expect_success 'test of correct formating for file name from git to mw' '
+	wiki_reset &&
+	git clone mediawiki::'"$WIKI_URL"' mw_dir_14 &&
+	(
+		cd mw_dir_14 &&
+		echo "my new file char{_1" >Char\{_1.mw &&
+		echo "my new file char[_2" >Char\[_2.mw &&
+		git add . &&
+		git commit -m "commiting some exotic file name..." &&
+		git push
+	) &&
+	wiki_getallpage ref_page_14 &&
+	mv mw_dir_14/Char\{_1.mw mw_dir_14/Char_%_7b_1.mw &&
+	mv mw_dir_14/Char\[_2.mw mw_dir_14/Char_%_5b_2.mw &&
+	test_diff_directories mw_dir_14 ref_page_14
+'
+
+
+test_expect_success 'git clone with /' '
+	wiki_reset &&
+	wiki_editpage \/fo\/o "this is not important" false -c=Deleted &&
+	git clone mediawiki::'"$WIKI_URL"' mw_dir_15 &&
+	test_path_is_file mw_dir_15/%2Ffo%2Fo.mw &&
+	wiki_check_content mw_dir_15/%2Ffo%2Fo.mw \/fo\/o
+'
+
+
+test_expect_success 'git push with /' '
+	wiki_reset &&
+	git clone mediawiki::'"$WIKI_URL"' mw_dir_16 &&
+	echo "I will be on the wiki" >mw_dir_16/%2Ffo%2Fo.mw &&
+	(
+		cd mw_dir_16 &&
+		git add %2Ffo%2Fo.mw &&
+		git commit -m " %2Ffo%2Fo added" &&
+		git push
+	) &&
+	wiki_page_exist \/fo\/o &&
+	wiki_check_content mw_dir_16/%2Ffo%2Fo.mw \/fo\/o
+
+'
+
+
+test_expect_success 'git clone with \' '
+	wiki_reset &&
+	wiki_editpage \\ko\\o "this is not important" false -c=Deleted &&
+	git clone mediawiki::'"$WIKI_URL"' mw_dir_17 &&
+	test_path_is_file mw_dir_17/\\ko\\o.mw &&
+	wiki_check_content mw_dir_17/\\ko\\o.mw \\ko\\o
+'
+
+
+test_expect_success 'git push with \' '
+	wiki_reset &&
+	git clone mediawiki::'"$WIKI_URL"' mw_dir_18 &&
+	echo "I will be on the wiki" >mw_dir_18/\\ko\\o.mw &&
+	(
+		cd mw_dir_18 &&
+		git add \\ko\\o.mw &&
+		git commit -m " \\ko\\o added" &&
+		git push
+	)&&
+	wiki_page_exist \\ko\\o &&
+	wiki_check_content mw_dir_18/\\ko\\o.mw \\ko\\o
+
+'
+
+test_expect_success 'git clone with \ in format control' '
+	wiki_reset &&
+	wiki_editpage \\no\\o "this is not important" false &&
+	git clone mediawiki::'"$WIKI_URL"' mw_dir_19 &&
+	test_path_is_file mw_dir_19/\\no\\o.mw &&
+	wiki_check_content mw_dir_19/\\no\\o.mw \\no\\o
+'
+
+
+test_expect_success 'git push with \ in format control' '
+	wiki_reset &&
+	git clone mediawiki::'"$WIKI_URL"' mw_dir_20 &&
+	echo "I will be on the wiki" >mw_dir_20/\\fo\\o.mw &&
+	(
+		cd mw_dir_20 &&
+		git add \\fo\\o.mw &&
+		git commit -m " \\fo\\o added" &&
+		git push
+	)&&
+	wiki_page_exist \\fo\\o &&
+	wiki_check_content mw_dir_20/\\fo\\o.mw \\fo\\o
+
+'
+
+
+test_done
diff --git a/contrib/mw-to-git/t/t9363-mw-to-git-export-import.sh b/contrib/mw-to-git/t/t9363-mw-to-git-export-import.sh
new file mode 100755
index 0000000..5a03739
--- /dev/null
+++ b/contrib/mw-to-git/t/t9363-mw-to-git-export-import.sh
@@ -0,0 +1,198 @@
+#!/bin/sh
+#
+# Copyright (C) 2012
+#     Charles Roussel <charles.roussel@ensimag.imag.fr>
+#     Simon Cathebras <simon.cathebras@ensimag.imag.fr>
+#     Julien Khayat <julien.khayat@ensimag.imag.fr>
+#     Guillaume Sasdy <guillaume.sasdy@ensimag.imag.fr>
+#     Simon Perrat <simon.perrat@ensimag.imag.fr>
+#
+# License: GPL v2 or later
+
+# tests for git-remote-mediawiki
+
+test_description='Test the Git Mediawiki remote helper: git push and git pull simple test cases'
+
+. ./test-gitmw-lib.sh
+. $TEST_DIRECTORY/test-lib.sh
+
+
+test_check_precond
+
+
+test_git_reimport () {
+	git -c remote.origin.dumbPush=true push &&
+	git -c remote.origin.mediaImport=true pull --rebase
+}
+
+# Don't bother with permissions, be administrator by default
+test_expect_success 'setup config' '
+	git config --global remote.origin.mwLogin WikiAdmin &&
+	git config --global remote.origin.mwPassword AdminPass &&
+	test_might_fail git config --global --unset remote.origin.mediaImport
+'
+
+test_expect_success 'git push can upload media (File:) files' '
+	wiki_reset &&
+	git clone mediawiki::'"$WIKI_URL"' mw_dir &&
+	(
+		cd mw_dir &&
+		echo "hello world" >Foo.txt &&
+		git add Foo.txt &&
+		git commit -m "add a text file" &&
+		git push &&
+		"$PERL_PATH" -e "print STDOUT \"binary content: \".chr(255);" >Foo.txt &&
+		git add Foo.txt &&
+		git commit -m "add a text file with binary content" &&
+		git push
+	)
+'
+
+test_expect_success 'git clone works on previously created wiki with media files' '
+	test_when_finished "rm -rf mw_dir mw_dir_clone" &&
+	git clone -c remote.origin.mediaimport=true \
+		mediawiki::'"$WIKI_URL"' mw_dir_clone &&
+	test_cmp mw_dir_clone/Foo.txt mw_dir/Foo.txt &&
+	(cd mw_dir_clone && git checkout HEAD^) &&
+	(cd mw_dir && git checkout HEAD^) &&
+	test_cmp mw_dir_clone/Foo.txt mw_dir/Foo.txt
+'
+
+test_expect_success 'git push & pull work with locally renamed media files' '
+	wiki_reset &&
+	git clone mediawiki::'"$WIKI_URL"' mw_dir &&
+	test_when_finished "rm -fr mw_dir" &&
+	(
+		cd mw_dir &&
+		echo "A File" >Foo.txt &&
+		git add Foo.txt &&
+		git commit -m "add a file" &&
+		git mv Foo.txt Bar.txt &&
+		git commit -m "Rename a file" &&
+		test_git_reimport &&
+		echo "A File" >expect &&
+		test_cmp expect Bar.txt &&
+		test_path_is_missing Foo.txt
+	)
+'
+
+test_expect_success 'git push can propagate local page deletion' '
+	wiki_reset &&
+	git clone mediawiki::'"$WIKI_URL"' mw_dir &&
+	test_when_finished "rm -fr mw_dir" &&
+	(
+		cd mw_dir &&
+		test_path_is_missing Foo.mw &&
+		echo "hello world" >Foo.mw &&
+		git add Foo.mw &&
+		git commit -m "Add the page Foo" &&
+		git push &&
+		rm -f Foo.mw &&
+		git commit -am "Delete the page Foo" &&
+		test_git_reimport &&
+		test_path_is_missing Foo.mw
+	)
+'
+
+test_expect_success 'git push can propagate local media file deletion' '
+	wiki_reset &&
+	git clone mediawiki::'"$WIKI_URL"' mw_dir &&
+	test_when_finished "rm -fr mw_dir" &&
+	(
+		cd mw_dir &&
+		echo "hello world" >Foo.txt &&
+		git add Foo.txt &&
+		git commit -m "Add the text file Foo" &&
+		git rm Foo.txt &&
+		git commit -m "Delete the file Foo" &&
+		test_git_reimport &&
+		test_path_is_missing Foo.txt
+	)
+'
+
+# test failure: the file is correctly uploaded, and then deleted but
+# as no page link to it, the import (which looks at page revisions)
+# doesn't notice the file deletion on the wiki. We fetch the list of
+# files from the wiki, but as the file is deleted, it doesn't appear.
+test_expect_failure 'git pull correctly imports media file deletion when no page link to it' '
+	wiki_reset &&
+	git clone mediawiki::'"$WIKI_URL"' mw_dir &&
+	test_when_finished "rm -fr mw_dir" &&
+	(
+		cd mw_dir &&
+		echo "hello world" >Foo.txt &&
+		git add Foo.txt &&
+		git commit -m "Add the text file Foo" &&
+		git push &&
+		git rm Foo.txt &&
+		git commit -m "Delete the file Foo" &&
+		test_git_reimport &&
+		test_path_is_missing Foo.txt
+	)
+'
+
+test_expect_success 'git push properly warns about insufficient permissions' '
+	wiki_reset &&
+	git clone mediawiki::'"$WIKI_URL"' mw_dir &&
+	test_when_finished "rm -fr mw_dir" &&
+	(
+		cd mw_dir &&
+		echo "A File" >foo.forbidden &&
+		git add foo.forbidden &&
+		git commit -m "add a file" &&
+		git push 2>actual &&
+		test_i18ngrep "foo.forbidden is not a permitted file" actual
+	)
+'
+
+test_expect_success 'setup a repository with media files' '
+	wiki_reset &&
+	wiki_editpage testpage "I am linking a file [[File:File.txt]]" false &&
+	echo "File content" >File.txt &&
+	wiki_upload_file File.txt &&
+	echo "Another file content" >AnotherFile.txt &&
+	wiki_upload_file AnotherFile.txt
+'
+
+test_expect_success 'git clone works with one specific page cloned and mediaimport=true' '
+	git clone -c remote.origin.pages=testpage \
+		  -c remote.origin.mediaimport=true \
+			mediawiki::'"$WIKI_URL"' mw_dir_15 &&
+	test_when_finished "rm -rf mw_dir_15" &&
+	test_contains_N_files mw_dir_15 3 &&
+	test_path_is_file mw_dir_15/Testpage.mw &&
+	test_path_is_file mw_dir_15/File:File.txt.mw &&
+	test_path_is_file mw_dir_15/File.txt &&
+	test_path_is_missing mw_dir_15/Main_Page.mw &&
+	test_path_is_missing mw_dir_15/File:AnotherFile.txt.mw &&
+	test_path_is_missing mw_dir_15/AnothetFile.txt &&
+	wiki_check_content mw_dir_15/Testpage.mw Testpage &&
+	test_cmp mw_dir_15/File.txt File.txt
+'
+
+test_expect_success 'git clone works with one specific page cloned and mediaimport=false' '
+	test_when_finished "rm -rf mw_dir_16" &&
+	git clone -c remote.origin.pages=testpage \
+			mediawiki::'"$WIKI_URL"' mw_dir_16 &&
+	test_contains_N_files mw_dir_16 1 &&
+	test_path_is_file mw_dir_16/Testpage.mw &&
+	test_path_is_missing mw_dir_16/File:File.txt.mw &&
+	test_path_is_missing mw_dir_16/File.txt &&
+	test_path_is_missing mw_dir_16/Main_Page.mw &&
+	wiki_check_content mw_dir_16/Testpage.mw Testpage
+'
+
+# should behave like mediaimport=false
+test_expect_success 'git clone works with one specific page cloned and mediaimport unset' '
+	test_when_finished "rm -fr mw_dir_17" &&
+	git clone -c remote.origin.pages=testpage \
+		mediawiki::'"$WIKI_URL"' mw_dir_17 &&
+	test_contains_N_files mw_dir_17 1 &&
+	test_path_is_file mw_dir_17/Testpage.mw &&
+	test_path_is_missing mw_dir_17/File:File.txt.mw &&
+	test_path_is_missing mw_dir_17/File.txt &&
+	test_path_is_missing mw_dir_17/Main_Page.mw &&
+	wiki_check_content mw_dir_17/Testpage.mw Testpage
+'
+
+test_done
diff --git a/contrib/mw-to-git/t/t9364-pull-by-rev.sh b/contrib/mw-to-git/t/t9364-pull-by-rev.sh
new file mode 100755
index 0000000..5c22457
--- /dev/null
+++ b/contrib/mw-to-git/t/t9364-pull-by-rev.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+test_description='Test the Git Mediawiki remote helper: git pull by revision'
+
+. ./test-gitmw-lib.sh
+. ./push-pull-tests.sh
+. $TEST_DIRECTORY/test-lib.sh
+
+test_check_precond
+
+test_expect_success 'configuration' '
+	git config --global mediawiki.fetchStrategy by_rev
+'
+
+test_push_pull
+
+test_done
diff --git a/contrib/mw-to-git/t/test-gitmw-lib.sh b/contrib/mw-to-git/t/test-gitmw-lib.sh
new file mode 100755
index 0000000..3b2cfac
--- /dev/null
+++ b/contrib/mw-to-git/t/test-gitmw-lib.sh
@@ -0,0 +1,435 @@
+# Copyright (C) 2012
+#     Charles Roussel <charles.roussel@ensimag.imag.fr>
+#     Simon Cathebras <simon.cathebras@ensimag.imag.fr>
+#     Julien Khayat <julien.khayat@ensimag.imag.fr>
+#     Guillaume Sasdy <guillaume.sasdy@ensimag.imag.fr>
+#     Simon Perrat <simon.perrat@ensimag.imag.fr>
+# License: GPL v2 or later
+
+#
+# CONFIGURATION VARIABLES
+# You might want to change these ones
+#
+
+. ./test.config
+
+WIKI_URL=http://"$SERVER_ADDR:$PORT/$WIKI_DIR_NAME"
+CURR_DIR=$(pwd)
+TEST_OUTPUT_DIRECTORY=$(pwd)
+TEST_DIRECTORY="$CURR_DIR"/../../../t
+
+export TEST_OUTPUT_DIRECTORY TEST_DIRECTORY CURR_DIR
+
+if test "$LIGHTTPD" = "false" ; then
+	PORT=80
+else
+	WIKI_DIR_INST="$CURR_DIR/$WEB_WWW"
+fi
+
+wiki_upload_file () {
+	"$CURR_DIR"/test-gitmw.pl upload_file "$@"
+}
+
+wiki_getpage () {
+	"$CURR_DIR"/test-gitmw.pl get_page "$@"
+}
+
+wiki_delete_page () {
+	"$CURR_DIR"/test-gitmw.pl delete_page "$@"
+}
+
+wiki_editpage () {
+	"$CURR_DIR"/test-gitmw.pl edit_page "$@"
+}
+
+die () {
+	die_with_status 1 "$@"
+}
+
+die_with_status () {
+	status=$1
+	shift
+	echo >&2 "$*"
+	exit "$status"
+}
+
+
+# Check the preconditions to run git-remote-mediawiki's tests
+test_check_precond () {
+	if ! test_have_prereq PERL
+	then
+		skip_all='skipping gateway git-mw tests, perl not available'
+		test_done
+	fi
+
+	if [ ! -f "$GIT_BUILD_DIR"/git-remote-mediawiki ];
+	then
+		echo "No remote mediawiki for git found. Copying it in git"
+		echo "cp $GIT_BUILD_DIR/contrib/mw-to-git/git-remote-mediawiki $GIT_BUILD_DIR/"
+		ln -s "$GIT_BUILD_DIR"/contrib/mw-to-git/git-remote-mediawiki "$GIT_BUILD_DIR"
+	fi
+
+	if [ ! -d "$WIKI_DIR_INST/$WIKI_DIR_NAME" ];
+	then
+		skip_all='skipping gateway git-mw tests, no mediawiki found'
+		test_done
+	fi
+}
+
+# test_diff_directories <dir_git> <dir_wiki>
+#
+# Compare the contents of directories <dir_git> and <dir_wiki> with diff
+# and errors if they do not match. The program will
+# not look into .git in the process.
+# Warning: the first argument MUST be the directory containing the git data
+test_diff_directories () {
+	rm -rf "$1_tmp"
+	mkdir -p "$1_tmp"
+	cp "$1"/*.mw "$1_tmp"
+	diff -r -b "$1_tmp" "$2"
+}
+
+# $1=<dir>
+# $2=<N>
+#
+# Check that <dir> contains exactly <N> files
+test_contains_N_files () {
+	if test `ls -- "$1" | wc -l` -ne "$2"; then
+		echo "directory $1 sould contain $2 files"
+		echo "it contains these files:"
+		ls "$1"
+		false
+	fi
+}
+
+
+# wiki_check_content <file_name> <page_name>
+#
+# Compares the contents of the file <file_name> and the wiki page
+# <page_name> and exits with error 1 if they do not match.
+wiki_check_content () {
+	mkdir -p wiki_tmp
+	wiki_getpage "$2" wiki_tmp
+	# replacement of forbidden character in file name
+	page_name=$(printf "%s\n" "$2" | sed -e "s/\//%2F/g")
+
+	diff -b "$1" wiki_tmp/"$page_name".mw
+	if test $? -ne 0
+	then
+		rm -rf wiki_tmp
+		error "ERROR: file $2 not found on wiki"
+	fi
+	rm -rf wiki_tmp
+}
+
+# wiki_page_exist <page_name>
+#
+# Check the existence of the page <page_name> on the wiki and exits
+# with error if it is absent from it.
+wiki_page_exist () {
+	mkdir -p wiki_tmp
+	wiki_getpage "$1" wiki_tmp
+	page_name=$(printf "%s\n" "$1" | sed "s/\//%2F/g")
+	if test -f wiki_tmp/"$page_name".mw ; then
+		rm -rf wiki_tmp
+	else
+		rm -rf wiki_tmp
+		error "test failed: file $1 not found on wiki"
+	fi
+}
+
+# wiki_getallpagename
+#
+# Fetch the name of each page on the wiki.
+wiki_getallpagename () {
+	"$CURR_DIR"/test-gitmw.pl getallpagename
+}
+
+# wiki_getallpagecategory <category>
+#
+# Fetch the name of each page belonging to <category> on the wiki.
+wiki_getallpagecategory () {
+	"$CURR_DIR"/test-gitmw.pl getallpagename "$@"
+}
+
+# wiki_getallpage <dest_dir> [<category>]
+#
+# Fetch all the pages from the wiki and place them in the directory
+# <dest_dir>.
+# If <category> is define, then wiki_getallpage fetch the pages included
+# in <category>.
+wiki_getallpage () {
+	if test -z "$2";
+	then
+		wiki_getallpagename
+	else
+		wiki_getallpagecategory "$2"
+	fi
+	mkdir -p "$1"
+	while read -r line; do
+		wiki_getpage "$line" $1;
+	done < all.txt
+}
+
+# ================= Install part =================
+
+error () {
+	echo "$@" >&2
+	exit 1
+}
+
+# config_lighttpd
+#
+# Create the configuration files and the folders necessary to start lighttpd.
+# Overwrite any existing file.
+config_lighttpd () {
+	mkdir -p $WEB
+	mkdir -p $WEB_TMP
+	mkdir -p $WEB_WWW
+	cat > $WEB/lighttpd.conf <<EOF
+	server.document-root = "$CURR_DIR/$WEB_WWW"
+	server.port = $PORT
+	server.pid-file = "$CURR_DIR/$WEB_TMP/pid"
+
+	server.modules = (
+	"mod_rewrite",
+	"mod_redirect",
+	"mod_access",
+	"mod_accesslog",
+	"mod_fastcgi"
+	)
+
+	index-file.names = ("index.php" , "index.html")
+
+	mimetype.assign		    = (
+	".pdf"		=>	"application/pdf",
+	".sig"		=>	"application/pgp-signature",
+	".spl"		=>	"application/futuresplash",
+	".class"	=>	"application/octet-stream",
+	".ps"		=>	"application/postscript",
+	".torrent"	=>	"application/x-bittorrent",
+	".dvi"		=>	"application/x-dvi",
+	".gz"		=>	"application/x-gzip",
+	".pac"		=>	"application/x-ns-proxy-autoconfig",
+	".swf"		=>	"application/x-shockwave-flash",
+	".tar.gz"	=>	"application/x-tgz",
+	".tgz"		=>	"application/x-tgz",
+	".tar"		=>	"application/x-tar",
+	".zip"		=>	"application/zip",
+	".mp3"		=>	"audio/mpeg",
+	".m3u"		=>	"audio/x-mpegurl",
+	".wma"		=>	"audio/x-ms-wma",
+	".wax"		=>	"audio/x-ms-wax",
+	".ogg"		=>	"application/ogg",
+	".wav"		=>	"audio/x-wav",
+	".gif"		=>	"image/gif",
+	".jpg"		=>	"image/jpeg",
+	".jpeg"		=>	"image/jpeg",
+	".png"		=>	"image/png",
+	".xbm"		=>	"image/x-xbitmap",
+	".xpm"		=>	"image/x-xpixmap",
+	".xwd"		=>	"image/x-xwindowdump",
+	".css"		=>	"text/css",
+	".html"		=>	"text/html",
+	".htm"		=>	"text/html",
+	".js"		=>	"text/javascript",
+	".asc"		=>	"text/plain",
+	".c"		=>	"text/plain",
+	".cpp"		=>	"text/plain",
+	".log"		=>	"text/plain",
+	".conf"		=>	"text/plain",
+	".text"		=>	"text/plain",
+	".txt"		=>	"text/plain",
+	".dtd"		=>	"text/xml",
+	".xml"		=>	"text/xml",
+	".mpeg"		=>	"video/mpeg",
+	".mpg"		=>	"video/mpeg",
+	".mov"		=>	"video/quicktime",
+	".qt"		=>	"video/quicktime",
+	".avi"		=>	"video/x-msvideo",
+	".asf"		=>	"video/x-ms-asf",
+	".asx"		=>	"video/x-ms-asf",
+	".wmv"		=>	"video/x-ms-wmv",
+	".bz2"		=>	"application/x-bzip",
+	".tbz"		=>	"application/x-bzip-compressed-tar",
+	".tar.bz2"	=>	"application/x-bzip-compressed-tar",
+	""		=>	"text/plain"
+	)
+
+	fastcgi.server = ( ".php" =>
+	("localhost" =>
+	( "socket" => "$CURR_DIR/$WEB_TMP/php.socket",
+	"bin-path" => "$PHP_DIR/php-cgi -c $CURR_DIR/$WEB/php.ini"
+
+	)
+	)
+	)
+EOF
+
+	cat > $WEB/php.ini <<EOF
+	session.save_path ='$CURR_DIR/$WEB_TMP'
+EOF
+}
+
+# start_lighttpd
+#
+# Start or restart daemon lighttpd. If restart, rewrite configuration files.
+start_lighttpd () {
+	if test -f "$WEB_TMP/pid"; then
+		echo "Instance already running. Restarting..."
+		stop_lighttpd
+	fi
+	config_lighttpd
+	"$LIGHTTPD_DIR"/lighttpd -f "$WEB"/lighttpd.conf
+
+	if test $? -ne 0 ; then
+		echo "Could not execute http deamon lighttpd"
+		exit 1
+	fi
+}
+
+# stop_lighttpd
+#
+# Kill daemon lighttpd and removes files and folders associated.
+stop_lighttpd () {
+	test -f "$WEB_TMP/pid" && kill $(cat "$WEB_TMP/pid")
+	rm -rf "$WEB"
+}
+
+# Create the SQLite database of the MediaWiki. If the database file already
+# exists, it will be deleted.
+# This script should be runned from the directory where $FILES_FOLDER is
+# located.
+create_db () {
+	rm -f "$TMP/$DB_FILE"
+
+	echo "Generating the SQLite database file. It can take some time ..."
+	# Run the php script to generate the SQLite database file
+	# with cURL calls.
+	php "$FILES_FOLDER/$DB_INSTALL_SCRIPT" $(basename "$DB_FILE" .sqlite) \
+		"$WIKI_ADMIN" "$WIKI_PASSW" "$TMP" "$PORT"
+
+	if [ ! -f "$TMP/$DB_FILE" ] ; then
+		error "Can't create database file $TMP/$DB_FILE. Try to run ./install-wiki.sh delete first."
+	fi
+
+	# Copy the generated database file into the directory the
+	# user indicated.
+	cp "$TMP/$DB_FILE" "$FILES_FOLDER" ||
+		error "Unable to copy $TMP/$DB_FILE to $FILES_FOLDER"
+}
+
+# Install a wiki in your web server directory.
+wiki_install () {
+	if test $LIGHTTPD = "true" ; then
+		start_lighttpd
+	fi
+
+	SERVER_ADDR=$SERVER_ADDR:$PORT
+	# In this part, we change directory to $TMP in order to download,
+	# unpack and copy the files of MediaWiki
+	(
+	mkdir -p "$WIKI_DIR_INST/$WIKI_DIR_NAME"
+	if [ ! -d "$WIKI_DIR_INST/$WIKI_DIR_NAME" ] ; then
+		error "Folder $WIKI_DIR_INST/$WIKI_DIR_NAME doesn't exist.
+		Please create it and launch the script again."
+	fi
+
+	# Fetch MediaWiki's archive if not already present in the TMP directory
+	cd "$TMP"
+	if [ ! -f "$MW_VERSION.tar.gz" ] ; then
+		echo "Downloading $MW_VERSION sources ..."
+		wget "http://download.wikimedia.org/mediawiki/1.19/mediawiki-1.19.0.tar.gz" ||
+			error "Unable to download "\
+			"http://download.wikimedia.org/mediawiki/1.19/"\
+			"mediawiki-1.19.0.tar.gz. "\
+			"Please fix your connection and launch the script again."
+		echo "$MW_VERSION.tar.gz downloaded in `pwd`. "\
+			"You can delete it later if you want."
+	else
+		echo "Reusing existing $MW_VERSION.tar.gz downloaded in `pwd`."
+	fi
+	archive_abs_path=$(pwd)/"$MW_VERSION.tar.gz"
+	cd "$WIKI_DIR_INST/$WIKI_DIR_NAME/" ||
+		error "can't cd to $WIKI_DIR_INST/$WIKI_DIR_NAME/"
+	tar xzf "$archive_abs_path" --strip-components=1 ||
+		error "Unable to extract WikiMedia's files from $archive_abs_path to "\
+			"$WIKI_DIR_INST/$WIKI_DIR_NAME"
+	) || exit 1
+
+	create_db
+
+	# Copy the generic LocalSettings.php in the web server's directory
+	# And modify parameters according to the ones set at the top
+	# of this script.
+	# Note that LocalSettings.php is never modified.
+	if [ ! -f "$FILES_FOLDER/LocalSettings.php" ] ; then
+		error "Can't find $FILES_FOLDER/LocalSettings.php " \
+			"in the current folder. "\
+		"Please run the script inside its folder."
+	fi
+	cp "$FILES_FOLDER/LocalSettings.php" \
+		"$FILES_FOLDER/LocalSettings-tmp.php" ||
+		error "Unable to copy $FILES_FOLDER/LocalSettings.php " \
+		"to $FILES_FOLDER/LocalSettings-tmp.php"
+
+	# Parse and set the LocalSettings file of the user according to the
+	# CONFIGURATION VARIABLES section at the beginning of this script
+	file_swap="$FILES_FOLDER/LocalSettings-swap.php"
+	sed "s,@WG_SCRIPT_PATH@,/$WIKI_DIR_NAME," \
+		"$FILES_FOLDER/LocalSettings-tmp.php" > "$file_swap"
+	mv "$file_swap" "$FILES_FOLDER/LocalSettings-tmp.php"
+	sed "s,@WG_SERVER@,http://$SERVER_ADDR," \
+		"$FILES_FOLDER/LocalSettings-tmp.php" > "$file_swap"
+	mv "$file_swap" "$FILES_FOLDER/LocalSettings-tmp.php"
+	sed "s,@WG_SQLITE_DATADIR@,$TMP," \
+		"$FILES_FOLDER/LocalSettings-tmp.php" > "$file_swap"
+	mv "$file_swap" "$FILES_FOLDER/LocalSettings-tmp.php"
+	sed "s,@WG_SQLITE_DATAFILE@,$( basename $DB_FILE .sqlite)," \
+		"$FILES_FOLDER/LocalSettings-tmp.php" > "$file_swap"
+	mv "$file_swap" "$FILES_FOLDER/LocalSettings-tmp.php"
+
+	mv "$FILES_FOLDER/LocalSettings-tmp.php" \
+		"$WIKI_DIR_INST/$WIKI_DIR_NAME/LocalSettings.php" ||
+		error "Unable to move $FILES_FOLDER/LocalSettings-tmp.php" \
+		"in $WIKI_DIR_INST/$WIKI_DIR_NAME"
+	echo "File $FILES_FOLDER/LocalSettings.php is set in" \
+		" $WIKI_DIR_INST/$WIKI_DIR_NAME"
+
+	echo "Your wiki has been installed. You can check it at
+		http://$SERVER_ADDR/$WIKI_DIR_NAME"
+}
+
+# Reset the database of the wiki and the password of the admin
+#
+# Warning: This function must be called only in a subdirectory of t/ directory
+wiki_reset () {
+	# Copy initial database of the wiki
+	if [ ! -f "../$FILES_FOLDER/$DB_FILE" ] ; then
+		error "Can't find ../$FILES_FOLDER/$DB_FILE in the current folder."
+	fi
+	cp "../$FILES_FOLDER/$DB_FILE" "$TMP" ||
+		error "Can't copy ../$FILES_FOLDER/$DB_FILE in $TMP"
+	echo "File $FILES_FOLDER/$DB_FILE is set in $TMP"
+}
+
+# Delete the wiki created in the web server's directory and all its content
+# saved in the database.
+wiki_delete () {
+	if test $LIGHTTPD = "true"; then
+		stop_lighttpd
+	else
+		# Delete the wiki's directory.
+		rm -rf "$WIKI_DIR_INST/$WIKI_DIR_NAME" ||
+			error "Wiki's directory $WIKI_DIR_INST/" \
+			"$WIKI_DIR_NAME could not be deleted"
+		# Delete the wiki's SQLite database.
+		rm -f "$TMP/$DB_FILE" ||
+			error "Database $TMP/$DB_FILE could not be deleted."
+	fi
+
+	# Delete the wiki's SQLite database
+	rm -f "$TMP/$DB_FILE" || error "Database $TMP/$DB_FILE could not be deleted."
+	rm -f "$FILES_FOLDER/$DB_FILE"
+	rm -rf "$TMP/$MW_VERSION"
+}
diff --git a/contrib/mw-to-git/t/test-gitmw.pl b/contrib/mw-to-git/t/test-gitmw.pl
new file mode 100755
index 0000000..0ff7625
--- /dev/null
+++ b/contrib/mw-to-git/t/test-gitmw.pl
@@ -0,0 +1,225 @@
+#!/usr/bin/perl -w -s
+# Copyright (C) 2012
+#     Charles Roussel <charles.roussel@ensimag.imag.fr>
+#     Simon Cathebras <simon.cathebras@ensimag.imag.fr>
+#     Julien Khayat <julien.khayat@ensimag.imag.fr>
+#     Guillaume Sasdy <guillaume.sasdy@ensimag.imag.fr>
+#     Simon Perrat <simon.perrat@ensimag.imag.fr>
+# License: GPL v2 or later
+
+# Usage:
+#       ./test-gitmw.pl <command> [argument]*
+# Execute in terminal using the name of the function to call as first
+# parameter, and the function's arguments as following parameters
+#
+# Example:
+#     ./test-gitmw.pl "get_page" foo .
+# will call <wiki_getpage> with arguments <foo> and <.>
+#
+# Available functions are:
+#     "get_page"
+#     "delete_page"
+#     "edit_page"
+#     "getallpagename"
+
+use MediaWiki::API;
+use Getopt::Long;
+use encoding 'utf8';
+use DateTime::Format::ISO8601;
+use open ':encoding(utf8)';
+use constant SLASH_REPLACEMENT => "%2F";
+
+#Parsing of the config file
+
+my $configfile = "$ENV{'CURR_DIR'}/test.config";
+my %config;
+open my $CONFIG, "<",  $configfile or die "can't open $configfile: $!";
+while (<$CONFIG>)
+{
+	chomp;
+	s/#.*//;
+	s/^\s+//;
+	s/\s+$//;
+	next unless length;
+	my ($key, $value) = split (/\s*=\s*/,$_, 2);
+	$config{$key} = $value;
+	last if ($key eq 'LIGHTTPD' and $value eq 'false');
+	last if ($key eq 'PORT');
+}
+close $CONFIG or die "can't close $configfile: $!";
+
+my $wiki_address = "http://$config{'SERVER_ADDR'}".":"."$config{'PORT'}";
+my $wiki_url = "$wiki_address/$config{'WIKI_DIR_NAME'}/api.php";
+my $wiki_admin = "$config{'WIKI_ADMIN'}";
+my $wiki_admin_pass = "$config{'WIKI_PASSW'}";
+my $mw = MediaWiki::API->new;
+$mw->{config}->{api_url} = $wiki_url;
+
+
+# wiki_login <name> <password>
+#
+# Logs the user with <name> and <password> in the global variable
+# of the mediawiki $mw
+sub wiki_login {
+	$mw->login( { lgname => "$_[0]",lgpassword => "$_[1]" } )
+	|| die "getpage: login failed";
+}
+
+# wiki_getpage <wiki_page> <dest_path>
+#
+# fetch a page <wiki_page> from the wiki referenced in the global variable
+# $mw and copies its content in directory dest_path
+sub wiki_getpage {
+	my $pagename = $_[0];
+	my $destdir = $_[1];
+
+	my $page = $mw->get_page( { title => $pagename } );
+	if (!defined($page)) {
+		die "getpage: wiki does not exist";
+	}
+
+	my $content = $page->{'*'};
+	if (!defined($content)) {
+		die "getpage: page does not exist";
+	}
+
+	$pagename=$page->{'title'};
+	# Replace spaces by underscore in the page name
+	$pagename =~ s/ /_/g;
+	$pagename =~ s/\//%2F/g;
+	open(my $file, ">$destdir/$pagename.mw");
+	print $file "$content";
+	close ($file);
+
+}
+
+# wiki_delete_page <page_name>
+#
+# delete the page with name <page_name> from the wiki referenced
+# in the global variable $mw
+sub wiki_delete_page {
+	my $pagename = $_[0];
+
+	my $exist=$mw->get_page({title => $pagename});
+
+	if (defined($exist->{'*'})){
+		$mw->edit({ action => 'delete',
+				title => $pagename})
+		|| die $mw->{error}->{code} . ": " . $mw->{error}->{details};
+	} else {
+		die "no page with such name found: $pagename\n";
+	}
+}
+
+# wiki_editpage <wiki_page> <wiki_content> <wiki_append> [-c=<category>] [-s=<summary>]
+#
+# Edit a page named <wiki_page> with content <wiki_content> on the wiki
+# referenced with the global variable $mw
+# If <wiki_append> == true : append <wiki_content> at the end of the actual
+# content of the page <wiki_page>
+# If <wik_page> doesn't exist, that page is created with the <wiki_content>
+sub wiki_editpage {
+	my $wiki_page = $_[0];
+	my $wiki_content = $_[1];
+	my $wiki_append = $_[2];
+	my $summary = "";
+	my ($summ, $cat) = ();
+	GetOptions('s=s' => \$summ, 'c=s' => \$cat);
+
+	my $append = 0;
+	if (defined($wiki_append) && $wiki_append eq 'true') {
+		$append=1;
+	}
+
+	my $previous_text ="";
+
+	if ($append) {
+		my $ref = $mw->get_page( { title => $wiki_page } );
+		$previous_text = $ref->{'*'};
+	}
+
+	my $text = $wiki_content;
+	if (defined($previous_text)) {
+		$text="$previous_text$text";
+	}
+
+	# Eventually, add this page to a category.
+	if (defined($cat)) {
+		my $category_name="[[Category:$cat]]";
+		$text="$text\n $category_name";
+	}
+	if(defined($summ)){
+		$summary=$summ;
+	}
+
+	$mw->edit( { action => 'edit', title => $wiki_page, summary => $summary, text => "$text"} );
+}
+
+# wiki_getallpagename [<category>]
+#
+# Fetch all pages of the wiki referenced by the global variable $mw
+# and print the names of each one in the file all.txt with a new line
+# ("\n") between these.
+# If the argument <category> is defined, then this function get only the pages
+# belonging to <category>.
+sub wiki_getallpagename {
+	# fetch the pages of the wiki
+	if (defined($_[0])) {
+		my $mw_pages = $mw->list ( { action => 'query',
+				list => 'categorymembers',
+				cmtitle => "Category:$_[0]",
+				cmnamespace => 0,
+				cmlimit => 500 },
+		)
+		|| die $mw->{error}->{code}.": ".$mw->{error}->{details};
+		open(my $file, ">all.txt");
+		foreach my $page (@{$mw_pages}) {
+			print $file "$page->{title}\n";
+		}
+		close ($file);
+
+	} else {
+		my $mw_pages = $mw->list({
+				action => 'query',
+				list => 'allpages',
+				aplimit => 500,
+			})
+		|| die $mw->{error}->{code}.": ".$mw->{error}->{details};
+		open(my $file, ">all.txt");
+		foreach my $page (@{$mw_pages}) {
+			print $file "$page->{title}\n";
+		}
+		close ($file);
+	}
+}
+
+sub wiki_upload_file {
+	my $file_name = $_[0];
+	my $resultat = $mw->edit ( {
+		action => 'upload',
+		filename => $file_name,
+		comment => 'upload a file',
+		file => [ $file_name ],
+		ignorewarnings=>1,
+	}, {
+		skip_encoding => 1
+	} ) || die $mw->{error}->{code} . ' : ' . $mw->{error}->{details};
+}
+
+
+
+# Main part of this script: parse the command line arguments
+# and select which function to execute
+my $fct_to_call = shift;
+
+wiki_login($wiki_admin, $wiki_admin_pass);
+
+my %functions_to_call = qw(
+	upload_file    wiki_upload_file
+	get_page       wiki_getpage
+	delete_page    wiki_delete_page
+	edit_page      wiki_editpage
+	getallpagename wiki_getallpagename
+);
+die "$0 ERROR: wrong argument" unless exists $functions_to_call{$fct_to_call};
+&{$functions_to_call{$fct_to_call}}(@ARGV);
diff --git a/contrib/mw-to-git/t/test.config b/contrib/mw-to-git/t/test.config
new file mode 100644
index 0000000..958b37b
--- /dev/null
+++ b/contrib/mw-to-git/t/test.config
@@ -0,0 +1,35 @@
+# Name of the web server's directory dedicated to the wiki is WIKI_DIR_NAME
+WIKI_DIR_NAME=wiki
+
+# Login and password of the wiki's admin
+WIKI_ADMIN=WikiAdmin
+WIKI_PASSW=AdminPass
+
+# Address of the web server
+SERVER_ADDR=localhost
+
+# SQLite database of the wiki, named DB_FILE, is located in TMP
+TMP=/tmp
+DB_FILE=wikidb.sqlite
+
+# If LIGHTTPD is not set to true, the script will use the defaut
+# web server running in WIKI_DIR_INST.
+WIKI_DIR_INST=/var/www
+
+# If LIGHTTPD is set to true, the script will use Lighttpd to run
+# the wiki.
+LIGHTTPD=true
+
+# The variables below are useful only if LIGHTTPD is set to true.
+PORT=1234
+PHP_DIR=/usr/bin
+LIGHTTPD_DIR=/usr/sbin
+WEB=WEB
+WEB_TMP=$WEB/tmp
+WEB_WWW=$WEB/www
+
+# The variables below are used by the script to install a wiki.
+# You should not modify these unless you are modifying the script itself.
+MW_VERSION=mediawiki-1.19.0
+FILES_FOLDER=install-wiki
+DB_INSTALL_SCRIPT=db_install.php
diff --git a/contrib/p4import/git-p4import.py b/contrib/p4import/git-p4import.py
index 0f3d97b..b6e534b 100644
--- a/contrib/p4import/git-p4import.py
+++ b/contrib/p4import/git-p4import.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python
 #
 # This tool is copyright (c) 2006, Sean Estabrooks.
 # It is released under the Gnu Public License, version 2.
diff --git a/contrib/persistent-https/LICENSE b/contrib/persistent-https/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/contrib/persistent-https/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/contrib/persistent-https/Makefile b/contrib/persistent-https/Makefile
new file mode 100644
index 0000000..92baa3b
--- /dev/null
+++ b/contrib/persistent-https/Makefile
@@ -0,0 +1,38 @@
+# Copyright 2012 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+BUILD_LABEL=$(shell date +"%s")
+TAR_OUT=$(shell go env GOOS)_$(shell go env GOARCH).tar.gz
+
+all: git-remote-persistent-https git-remote-persistent-https--proxy \
+	git-remote-persistent-http
+
+git-remote-persistent-https--proxy: git-remote-persistent-https
+	ln -f -s git-remote-persistent-https git-remote-persistent-https--proxy
+
+git-remote-persistent-http: git-remote-persistent-https
+	ln -f -s git-remote-persistent-https git-remote-persistent-http
+
+git-remote-persistent-https:
+	go build -o git-remote-persistent-https \
+		-ldflags "-X main._BUILD_EMBED_LABEL $(BUILD_LABEL)"
+
+clean:
+	rm -f git-remote-persistent-http* *.tar.gz
+
+tar: clean all
+	@chmod 555 git-remote-persistent-https
+	@tar -czf $(TAR_OUT) git-remote-persistent-http* README LICENSE
+	@echo
+	@echo "Created $(TAR_OUT)"
diff --git a/contrib/persistent-https/README b/contrib/persistent-https/README
new file mode 100644
index 0000000..f784dd2
--- /dev/null
+++ b/contrib/persistent-https/README
@@ -0,0 +1,62 @@
+git-remote-persistent-https
+
+The git-remote-persistent-https binary speeds up SSL operations
+by running a daemon job (git-remote-persistent-https--proxy) that
+keeps a connection open to a server.
+
+
+PRE-BUILT BINARIES
+
+Darwin amd64:
+https://commondatastorage.googleapis.com/git-remote-persistent-https/darwin_amd64.tar.gz
+
+Linux amd64:
+https://commondatastorage.googleapis.com/git-remote-persistent-https/linux_amd64.tar.gz
+
+
+INSTALLING
+
+Move all of the git-remote-persistent-http* binaries to a directory
+in PATH.
+
+
+USAGE
+
+HTTPS requests can be delegated to the proxy by using the
+"persistent-https" scheme, e.g.
+
+git clone persistent-https://kernel.googlesource.com/pub/scm/git/git
+
+Likewise, .gitconfig can be updated as follows to rewrite https urls
+to use persistent-https:
+
+[url "persistent-https"]
+	insteadof = https
+[url "persistent-http"]
+	insteadof = http
+
+
+#####################################################################
+# BUILDING FROM SOURCE
+#####################################################################
+
+LOCATION
+
+The source is available in the contrib/persistent-https directory of
+the Git source repository. The Git source repository is available at
+git://git.kernel.org/pub/scm/git/git.git/
+https://kernel.googlesource.com/pub/scm/git/git
+
+
+PREREQUISITES
+
+The code is written in Go (http://golang.org/) and the Go compiler is
+required. Currently, the compiler must be built and installed from tip
+of source, in order to include a fix in the reverse http proxy:
+http://code.google.com/p/go/source/detail?r=a615b796570a2cd8591884767a7d67ede74f6648
+
+
+BUILDING
+
+Run "make" to build the binaries. See the section on
+INSTALLING above.
diff --git a/contrib/persistent-https/client.go b/contrib/persistent-https/client.go
new file mode 100644
index 0000000..71125b5
--- /dev/null
+++ b/contrib/persistent-https/client.go
@@ -0,0 +1,189 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+	"bufio"
+	"errors"
+	"fmt"
+	"net"
+	"net/url"
+	"os"
+	"os/exec"
+	"strings"
+	"syscall"
+	"time"
+)
+
+type Client struct {
+	ProxyBin string
+	Args     []string
+
+	insecure bool
+}
+
+func (c *Client) Run() error {
+	if err := c.resolveArgs(); err != nil {
+		return fmt.Errorf("resolveArgs() got error: %v", err)
+	}
+
+	// Connect to the proxy.
+	uconn, hconn, addr, err := c.connect()
+	if err != nil {
+		return fmt.Errorf("connect() got error: %v", err)
+	}
+	// Keep the unix socket connection open for the duration of the request.
+	defer uconn.Close()
+	// Keep a connection to the HTTP server open, so no other user can
+	// bind on the same address so long as the process is running.
+	defer hconn.Close()
+
+	// Start the git-remote-http subprocess.
+	cargs := []string{"-c", fmt.Sprintf("http.proxy=%v", addr), "remote-http"}
+	cargs = append(cargs, c.Args...)
+	cmd := exec.Command("git", cargs...)
+
+	for _, v := range os.Environ() {
+		if !strings.HasPrefix(v, "GIT_PERSISTENT_HTTPS_SECURE=") {
+			cmd.Env = append(cmd.Env, v)
+		}
+	}
+	// Set the GIT_PERSISTENT_HTTPS_SECURE environment variable when
+	// the proxy is using a SSL connection.  This allows credential helpers
+	// to identify secure proxy connections, despite being passed an HTTP
+	// scheme.
+	if !c.insecure {
+		cmd.Env = append(cmd.Env, "GIT_PERSISTENT_HTTPS_SECURE=1")
+	}
+
+	cmd.Stdin = os.Stdin
+	cmd.Stdout = os.Stdout
+	cmd.Stderr = os.Stderr
+	if err := cmd.Run(); err != nil {
+		if eerr, ok := err.(*exec.ExitError); ok {
+			if stat, ok := eerr.ProcessState.Sys().(syscall.WaitStatus); ok && stat.ExitStatus() != 0 {
+				os.Exit(stat.ExitStatus())
+			}
+		}
+		return fmt.Errorf("git-remote-http subprocess got error: %v", err)
+	}
+	return nil
+}
+
+func (c *Client) connect() (uconn net.Conn, hconn net.Conn, addr string, err error) {
+	uconn, err = DefaultSocket.Dial()
+	if err != nil {
+		if e, ok := err.(*net.OpError); ok && (os.IsNotExist(e.Err) || e.Err == syscall.ECONNREFUSED) {
+			if err = c.startProxy(); err == nil {
+				uconn, err = DefaultSocket.Dial()
+			}
+		}
+		if err != nil {
+			return
+		}
+	}
+
+	if addr, err = c.readAddr(uconn); err != nil {
+		return
+	}
+
+	// Open a tcp connection to the proxy.
+	if hconn, err = net.Dial("tcp", addr); err != nil {
+		return
+	}
+
+	// Verify the address hasn't changed ownership.
+	var addr2 string
+	if addr2, err = c.readAddr(uconn); err != nil {
+		return
+	} else if addr != addr2 {
+		err = fmt.Errorf("address changed after connect. got %q, want %q", addr2, addr)
+		return
+	}
+	return
+}
+
+func (c *Client) readAddr(conn net.Conn) (string, error) {
+	conn.SetDeadline(time.Now().Add(5 * time.Second))
+	data := make([]byte, 100)
+	n, err := conn.Read(data)
+	if err != nil {
+		return "", fmt.Errorf("error reading unix socket: %v", err)
+	} else if n == 0 {
+		return "", errors.New("empty data response")
+	}
+	conn.Write([]byte{1}) // Ack
+
+	var addr string
+	if addrs := strings.Split(string(data[:n]), "\n"); len(addrs) != 2 {
+		return "", fmt.Errorf("got %q, wanted 2 addresses", data[:n])
+	} else if c.insecure {
+		addr = addrs[1]
+	} else {
+		addr = addrs[0]
+	}
+	return addr, nil
+}
+
+func (c *Client) startProxy() error {
+	cmd := exec.Command(c.ProxyBin)
+	cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
+	stdout, err := cmd.StdoutPipe()
+	if err != nil {
+		return err
+	}
+	defer stdout.Close()
+	if err := cmd.Start(); err != nil {
+		return err
+	}
+	result := make(chan error)
+	go func() {
+		bytes, _, err := bufio.NewReader(stdout).ReadLine()
+		if line := string(bytes); err == nil && line != "OK" {
+			err = fmt.Errorf("proxy returned %q, want \"OK\"", line)
+		}
+		result <- err
+	}()
+	select {
+	case err := <-result:
+		return err
+	case <-time.After(5 * time.Second):
+		return errors.New("timeout waiting for proxy to start")
+	}
+	panic("not reachable")
+}
+
+func (c *Client) resolveArgs() error {
+	if nargs := len(c.Args); nargs == 0 {
+		return errors.New("remote needed")
+	} else if nargs > 2 {
+		return fmt.Errorf("want at most 2 args, got %v", c.Args)
+	}
+
+	// Rewrite the url scheme to be http.
+	idx := len(c.Args) - 1
+	rawurl := c.Args[idx]
+	rurl, err := url.Parse(rawurl)
+	if err != nil {
+		return fmt.Errorf("invalid remote: %v", err)
+	}
+	c.insecure = rurl.Scheme == "persistent-http"
+	rurl.Scheme = "http"
+	c.Args[idx] = rurl.String()
+	if idx != 0 && c.Args[0] == rawurl {
+		c.Args[0] = c.Args[idx]
+	}
+	return nil
+}
diff --git a/contrib/persistent-https/main.go b/contrib/persistent-https/main.go
new file mode 100644
index 0000000..fd1b107
--- /dev/null
+++ b/contrib/persistent-https/main.go
@@ -0,0 +1,82 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// The git-remote-persistent-https binary speeds up SSL operations by running
+// a daemon job that keeps a connection open to a Git server. This ensures the
+// git-remote-persistent-https--proxy is running and delegating execution
+// to the git-remote-http binary with the http_proxy set to the daemon job.
+// A unix socket is used to authenticate the proxy and discover the
+// HTTP address. Note, both the client and proxy are included in the same
+// binary.
+package main
+
+import (
+	"flag"
+	"fmt"
+	"log"
+	"os"
+	"strings"
+	"time"
+)
+
+var (
+	forceProxy = flag.Bool("proxy", false, "Whether to start the binary in proxy mode")
+	proxyBin   = flag.String("proxy_bin", "git-remote-persistent-https--proxy", "Path to the proxy binary")
+	printLabel = flag.Bool("print_label", false, "Prints the build label for the binary")
+
+	// Variable that should be defined through the -X linker flag.
+	_BUILD_EMBED_LABEL string
+)
+
+const (
+	defaultMaxIdleDuration    = 24 * time.Hour
+	defaultPollUpdateInterval = 15 * time.Minute
+)
+
+func main() {
+	flag.Parse()
+	if *printLabel {
+		// Short circuit execution to print the build label
+		fmt.Println(buildLabel())
+		return
+	}
+
+	var err error
+	if *forceProxy || strings.HasSuffix(os.Args[0], "--proxy") {
+		log.SetPrefix("git-remote-persistent-https--proxy: ")
+		proxy := &Proxy{
+			BuildLabel:         buildLabel(),
+			MaxIdleDuration:    defaultMaxIdleDuration,
+			PollUpdateInterval: defaultPollUpdateInterval,
+		}
+		err = proxy.Run()
+	} else {
+		log.SetPrefix("git-remote-persistent-https: ")
+		client := &Client{
+			ProxyBin: *proxyBin,
+			Args:     flag.Args(),
+		}
+		err = client.Run()
+	}
+	if err != nil {
+		log.Fatalln(err)
+	}
+}
+
+func buildLabel() string {
+	if _BUILD_EMBED_LABEL == "" {
+		log.Println(`unlabeled build; build with "make" to label`)
+	}
+	return _BUILD_EMBED_LABEL
+}
diff --git a/contrib/persistent-https/proxy.go b/contrib/persistent-https/proxy.go
new file mode 100644
index 0000000..bb0cdba
--- /dev/null
+++ b/contrib/persistent-https/proxy.go
@@ -0,0 +1,190 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+	"fmt"
+	"log"
+	"net"
+	"net/http"
+	"net/http/httputil"
+	"os"
+	"os/exec"
+	"os/signal"
+	"sync"
+	"syscall"
+	"time"
+)
+
+type Proxy struct {
+	BuildLabel         string
+	MaxIdleDuration    time.Duration
+	PollUpdateInterval time.Duration
+
+	ul        net.Listener
+	httpAddr  string
+	httpsAddr string
+}
+
+func (p *Proxy) Run() error {
+	hl, err := net.Listen("tcp", "127.0.0.1:0")
+	if err != nil {
+		return fmt.Errorf("http listen failed: %v", err)
+	}
+	defer hl.Close()
+
+	hsl, err := net.Listen("tcp", "127.0.0.1:0")
+	if err != nil {
+		return fmt.Errorf("https listen failed: %v", err)
+	}
+	defer hsl.Close()
+
+	p.ul, err = DefaultSocket.Listen()
+	if err != nil {
+		c, derr := DefaultSocket.Dial()
+		if derr == nil {
+			c.Close()
+			fmt.Println("OK\nA proxy is already running... exiting")
+			return nil
+		} else if e, ok := derr.(*net.OpError); ok && e.Err == syscall.ECONNREFUSED {
+			// Nothing is listening on the socket, unlink it and try again.
+			syscall.Unlink(DefaultSocket.Path())
+			p.ul, err = DefaultSocket.Listen()
+		}
+		if err != nil {
+			return fmt.Errorf("unix listen failed on %v: %v", DefaultSocket.Path(), err)
+		}
+	}
+	defer p.ul.Close()
+	go p.closeOnSignal()
+	go p.closeOnUpdate()
+
+	p.httpAddr = hl.Addr().String()
+	p.httpsAddr = hsl.Addr().String()
+	fmt.Printf("OK\nListening on unix socket=%v http=%v https=%v\n",
+		p.ul.Addr(), p.httpAddr, p.httpsAddr)
+
+	result := make(chan error, 2)
+	go p.serveUnix(result)
+	go func() {
+		result <- http.Serve(hl, &httputil.ReverseProxy{
+			FlushInterval: 500 * time.Millisecond,
+			Director:      func(r *http.Request) {},
+		})
+	}()
+	go func() {
+		result <- http.Serve(hsl, &httputil.ReverseProxy{
+			FlushInterval: 500 * time.Millisecond,
+			Director: func(r *http.Request) {
+				r.URL.Scheme = "https"
+			},
+		})
+	}()
+	return <-result
+}
+
+type socketContext struct {
+	sync.WaitGroup
+	mutex sync.Mutex
+	last  time.Time
+}
+
+func (sc *socketContext) Done() {
+	sc.mutex.Lock()
+	defer sc.mutex.Unlock()
+	sc.last = time.Now()
+	sc.WaitGroup.Done()
+}
+
+func (p *Proxy) serveUnix(result chan<- error) {
+	sockCtx := &socketContext{}
+	go p.closeOnIdle(sockCtx)
+
+	var err error
+	for {
+		var uconn net.Conn
+		uconn, err = p.ul.Accept()
+		if err != nil {
+			err = fmt.Errorf("accept failed: %v", err)
+			break
+		}
+		sockCtx.Add(1)
+		go p.handleUnixConn(sockCtx, uconn)
+	}
+	sockCtx.Wait()
+	result <- err
+}
+
+func (p *Proxy) handleUnixConn(sockCtx *socketContext, uconn net.Conn) {
+	defer sockCtx.Done()
+	defer uconn.Close()
+	data := []byte(fmt.Sprintf("%v\n%v", p.httpsAddr, p.httpAddr))
+	uconn.SetDeadline(time.Now().Add(5 * time.Second))
+	for i := 0; i < 2; i++ {
+		if n, err := uconn.Write(data); err != nil {
+			log.Printf("error sending http addresses: %+v\n", err)
+			return
+		} else if n != len(data) {
+			log.Printf("sent %d data bytes, wanted %d\n", n, len(data))
+			return
+		}
+		if _, err := uconn.Read([]byte{0, 0, 0, 0}); err != nil {
+			log.Printf("error waiting for Ack: %+v\n", err)
+			return
+		}
+	}
+	// Wait without a deadline for the client to finish via EOF
+	uconn.SetDeadline(time.Time{})
+	uconn.Read([]byte{0, 0, 0, 0})
+}
+
+func (p *Proxy) closeOnIdle(sockCtx *socketContext) {
+	for d := p.MaxIdleDuration; d > 0; {
+		time.Sleep(d)
+		sockCtx.Wait()
+		sockCtx.mutex.Lock()
+		if d = sockCtx.last.Add(p.MaxIdleDuration).Sub(time.Now()); d <= 0 {
+			log.Println("graceful shutdown from idle timeout")
+			p.ul.Close()
+		}
+		sockCtx.mutex.Unlock()
+	}
+}
+
+func (p *Proxy) closeOnUpdate() {
+	for {
+		time.Sleep(p.PollUpdateInterval)
+		if out, err := exec.Command(os.Args[0], "--print_label").Output(); err != nil {
+			log.Printf("error polling for updated binary: %v\n", err)
+		} else if s := string(out[:len(out)-1]); p.BuildLabel != s {
+			log.Printf("graceful shutdown from updated binary: %q --> %q\n", p.BuildLabel, s)
+			p.ul.Close()
+			break
+		}
+	}
+}
+
+func (p *Proxy) closeOnSignal() {
+	ch := make(chan os.Signal, 10)
+	signal.Notify(ch, os.Interrupt, os.Kill, os.Signal(syscall.SIGTERM), os.Signal(syscall.SIGHUP))
+	sig := <-ch
+	p.ul.Close()
+	switch sig {
+	case os.Signal(syscall.SIGHUP):
+		log.Printf("graceful shutdown from signal: %v\n", sig)
+	default:
+		log.Fatalf("exiting from signal: %v\n", sig)
+	}
+}
diff --git a/contrib/persistent-https/socket.go b/contrib/persistent-https/socket.go
new file mode 100644
index 0000000..193b911
--- /dev/null
+++ b/contrib/persistent-https/socket.go
@@ -0,0 +1,97 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+	"fmt"
+	"log"
+	"net"
+	"os"
+	"path/filepath"
+	"syscall"
+)
+
+// A Socket is a wrapper around a Unix socket that verifies directory
+// permissions.
+type Socket struct {
+	Dir string
+}
+
+func defaultDir() string {
+	sockPath := ".git-credential-cache"
+	if home := os.Getenv("HOME"); home != "" {
+		return filepath.Join(home, sockPath)
+	}
+	log.Printf("socket: cannot find HOME path. using relative directory %q for socket", sockPath)
+	return sockPath
+}
+
+// DefaultSocket is a Socket in the $HOME/.git-credential-cache directory.
+var DefaultSocket = Socket{Dir: defaultDir()}
+
+// Listen announces the local network address of the unix socket. The
+// permissions on the socket directory are verified before attempting
+// the actual listen.
+func (s Socket) Listen() (net.Listener, error) {
+	network, addr := "unix", s.Path()
+	if err := s.mkdir(); err != nil {
+		return nil, &net.OpError{Op: "listen", Net: network, Addr: &net.UnixAddr{Name: addr, Net: network}, Err: err}
+	}
+	return net.Listen(network, addr)
+}
+
+// Dial connects to the unix socket. The permissions on the socket directory
+// are verified before attempting the actual dial.
+func (s Socket) Dial() (net.Conn, error) {
+	network, addr := "unix", s.Path()
+	if err := s.checkPermissions(); err != nil {
+		return nil, &net.OpError{Op: "dial", Net: network, Addr: &net.UnixAddr{Name: addr, Net: network}, Err: err}
+	}
+	return net.Dial(network, addr)
+}
+
+// Path returns the fully specified file name of the unix socket.
+func (s Socket) Path() string {
+	return filepath.Join(s.Dir, "persistent-https-proxy-socket")
+}
+
+func (s Socket) mkdir() error {
+	if err := s.checkPermissions(); err == nil {
+		return nil
+	} else if !os.IsNotExist(err) {
+		return err
+	}
+	if err := os.MkdirAll(s.Dir, 0700); err != nil {
+		return err
+	}
+	return s.checkPermissions()
+}
+
+func (s Socket) checkPermissions() error {
+	fi, err := os.Stat(s.Dir)
+	if err != nil {
+		return err
+	}
+	if !fi.IsDir() {
+		return fmt.Errorf("socket: got file, want directory for %q", s.Dir)
+	}
+	if fi.Mode().Perm() != 0700 {
+		return fmt.Errorf("socket: got perm %o, want 700 for %q", fi.Mode().Perm(), s.Dir)
+	}
+	if st := fi.Sys().(*syscall.Stat_t); int(st.Uid) != os.Getuid() {
+		return fmt.Errorf("socket: got uid %d, want %d for %q", st.Uid, os.Getuid(), s.Dir)
+	}
+	return nil
+}
diff --git a/contrib/rerere-train.sh b/contrib/rerere-train.sh
index 2cfe1b9..36b6fee 100755
--- a/contrib/rerere-train.sh
+++ b/contrib/rerere-train.sh
@@ -7,7 +7,7 @@
 
 SUBDIRECTORY_OK=Yes
 OPTIONS_SPEC=
-. git-sh-setup
+. $(git --exec-path)/git-sh-setup
 require_work_tree
 cd_to_toplevel
 
diff --git a/contrib/subtree/.gitignore b/contrib/subtree/.gitignore
new file mode 100644
index 0000000..7e77c9d
--- /dev/null
+++ b/contrib/subtree/.gitignore
@@ -0,0 +1,5 @@
+*~
+git-subtree.xml
+git-subtree.1
+mainline
+subproj
diff --git a/contrib/subtree/COPYING b/contrib/subtree/COPYING
new file mode 100644
index 0000000..d511905
--- /dev/null
+++ b/contrib/subtree/COPYING
@@ -0,0 +1,339 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/contrib/subtree/INSTALL b/contrib/subtree/INSTALL
new file mode 100644
index 0000000..7ab0cf4
--- /dev/null
+++ b/contrib/subtree/INSTALL
@@ -0,0 +1,28 @@
+HOW TO INSTALL git-subtree
+==========================
+
+First, build from the top source directory.
+
+Then, in contrib/subtree, run:
+
+  make
+  make install
+  make install-doc
+
+If you used configure to do the main build the git-subtree build will
+pick up those settings.  If not, you will likely have to provide a
+value for prefix:
+
+  make prefix=<some dir>
+  make prefix=<some dir> install
+  make prefix=<some dir> install-doc
+
+To run tests first copy git-subtree to the main build area so the
+newly-built git can find it:
+
+  cp git-subtree ../..
+
+Then:
+
+  make test
+
diff --git a/contrib/subtree/Makefile b/contrib/subtree/Makefile
new file mode 100644
index 0000000..05cdd5c
--- /dev/null
+++ b/contrib/subtree/Makefile
@@ -0,0 +1,52 @@
+-include ../../config.mak.autogen
+-include ../../config.mak
+
+prefix ?= /usr/local
+mandir ?= $(prefix)/share/man
+libexecdir ?= $(prefix)/libexec/git-core
+gitdir ?= $(shell git --exec-path)
+man1dir ?= $(mandir)/man1
+
+gitver ?= $(word 3,$(shell git --version))
+
+# this should be set to a 'standard' bsd-type install program
+INSTALL ?= install
+
+ASCIIDOC_CONF      = ../../Documentation/asciidoc.conf
+MANPAGE_NORMAL_XSL =  ../../Documentation/manpage-normal.xsl
+
+GIT_SUBTREE_SH := git-subtree.sh
+GIT_SUBTREE    := git-subtree
+
+GIT_SUBTREE_DOC := git-subtree.1
+GIT_SUBTREE_XML := git-subtree.xml
+GIT_SUBTREE_TXT := git-subtree.txt
+
+all: $(GIT_SUBTREE)
+
+$(GIT_SUBTREE): $(GIT_SUBTREE_SH)
+	cp $< $@ && chmod +x $@
+
+doc: $(GIT_SUBTREE_DOC)
+
+install: $(GIT_SUBTREE)
+	$(INSTALL) -m 755 $(GIT_SUBTREE) $(libexecdir)
+
+install-doc: install-man
+
+install-man: $(GIT_SUBTREE_DOC)
+	$(INSTALL) -m 644 $^ $(man1dir)
+
+$(GIT_SUBTREE_DOC): $(GIT_SUBTREE_XML)
+	xmlto -m $(MANPAGE_NORMAL_XSL)  man $^
+
+$(GIT_SUBTREE_XML): $(GIT_SUBTREE_TXT)
+	asciidoc -b docbook -d manpage -f $(ASCIIDOC_CONF) \
+		-agit_version=$(gitver) $^
+
+test:
+	$(MAKE) -C t/ test
+
+clean:
+	rm -f *~ *.xml *.html *.1
+	rm -rf subproj mainline
diff --git a/contrib/subtree/README b/contrib/subtree/README
new file mode 100644
index 0000000..c686b4a
--- /dev/null
+++ b/contrib/subtree/README
@@ -0,0 +1,8 @@
+
+Please read git-subtree.txt for documentation.
+
+Please don't contact me using github mail; it's slow, ugly, and worst of
+all, redundant. Email me instead at apenwarr@gmail.com and I'll be happy to
+help.
+
+Avery
diff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh
new file mode 100755
index 0000000..920c664
--- /dev/null
+++ b/contrib/subtree/git-subtree.sh
Binary files differ
diff --git a/contrib/subtree/git-subtree.txt b/contrib/subtree/git-subtree.txt
new file mode 100644
index 0000000..0c44fda
--- /dev/null
+++ b/contrib/subtree/git-subtree.txt
@@ -0,0 +1,366 @@
+git-subtree(1)
+==============
+
+NAME
+----
+git-subtree - Merge subtrees together and split repository into subtrees
+
+
+SYNOPSIS
+--------
+[verse]
+'git subtree' add   -P <prefix> <commit>
+'git subtree' pull  -P <prefix> <repository> <refspec...>
+'git subtree' push  -P <prefix> <repository> <refspec...>
+'git subtree' merge -P <prefix> <commit>
+'git subtree' split -P <prefix> [OPTIONS] [<commit>]
+
+
+DESCRIPTION
+-----------
+Subtrees allow subprojects to be included within a subdirectory
+of the main project, optionally including the subproject's
+entire history.
+
+For example, you could include the source code for a library
+as a subdirectory of your application.
+
+Subtrees are not to be confused with submodules, which are meant for
+the same task. Unlike submodules, subtrees do not need any special
+constructions (like .gitmodule files or gitlinks) be present in
+your repository, and do not force end-users of your
+repository to do anything special or to understand how subtrees
+work. A subtree is just a subdirectory that can be
+committed to, branched, and merged along with your project in
+any way you want.
+
+They are also not to be confused with using the subtree merge
+strategy. The main difference is that, besides merging
+the other project as a subdirectory, you can also extract the
+entire history of a subdirectory from your project and make it
+into a standalone project. Unlike the subtree merge strategy
+you can alternate back and forth between these
+two operations. If the standalone library gets updated, you can
+automatically merge the changes into your project; if you
+update the library inside your project, you can "split" the
+changes back out again and merge them back into the library
+project.
+
+For example, if a library you made for one application ends up being
+useful elsewhere, you can extract its entire history and publish
+that as its own git repository, without accidentally
+intermingling the history of your application project.
+
+[TIP]
+In order to keep your commit messages clean, we recommend that
+people split their commits between the subtrees and the main
+project as much as possible.  That is, if you make a change that
+affects both the library and the main application, commit it in
+two pieces.  That way, when you split the library commits out
+later, their descriptions will still make sense.  But if this
+isn't important to you, it's not *necessary*.  git subtree will
+simply leave out the non-library-related parts of the commit
+when it splits it out into the subproject later.
+
+
+COMMANDS
+--------
+add::
+	Create the <prefix> subtree by importing its contents
+	from the given <refspec> or <repository> and remote <refspec>.
+	A new commit is created	automatically, joining the imported
+	project's history with your own.  With '--squash', imports
+	only a single commit from the subproject, rather than its
+	entire history.
+
+merge::
+	Merge recent changes up to <commit> into the <prefix>
+	subtree.  As with normal 'git merge', this doesn't
+	remove your own local changes; it just merges those
+	changes into the latest <commit>.  With '--squash',
+	creates only one commit that contains all the changes,
+	rather than merging in the entire history.
+
+	If you use '--squash', the merge direction doesn't
+	always have to be forward; you can use this command to
+	go back in time from v2.5 to v2.4, for example.  If your
+	merge introduces a conflict, you can resolve it in the
+	usual ways.
+	
+pull::
+	Exactly like 'merge', but parallels 'git pull' in that
+	it fetches the given commit from the specified remote
+	repository.
+	
+push::
+	Does a 'split' (see above) using the <prefix> supplied
+	and then does a 'git push' to push the result to the 
+	repository and refspec. This can be used to push your
+	subtree to different branches of the remote repository.
+
+split::
+	Extract a new, synthetic project history from the
+	history of the <prefix> subtree.  The new history
+	includes only the commits (including merges) that
+	affected <prefix>, and each of those commits now has the
+	contents of <prefix> at the root of the project instead
+	of in a subdirectory.  Thus, the newly created history
+	is suitable for export as a separate git repository.
+	
+	After splitting successfully, a single commit id is
+	printed to stdout.  This corresponds to the HEAD of the
+	newly created tree, which you can manipulate however you
+	want.
+	
+	Repeated splits of exactly the same history are
+	guaranteed to be identical (ie. to produce the same
+	commit ids).  Because of this, if you add new commits
+	and then re-split, the new commits will be attached as
+	commits on top of the history you generated last time,
+	so 'git merge' and friends will work as expected.
+	
+	Note that if you use '--squash' when you merge, you
+	should usually not just '--rejoin' when you split.
+
+
+OPTIONS
+-------
+-q::
+--quiet::
+	Suppress unnecessary output messages on stderr.
+
+-d::
+--debug::
+	Produce even more unnecessary output messages on stderr.
+
+-P <prefix>::
+--prefix=<prefix>::
+	Specify the path in the repository to the subtree you
+	want to manipulate.  This option is mandatory
+	for all commands.
+
+-m <message>::
+--message=<message>::
+	This option is only valid for add, merge and pull (unsure).
+	Specify <message> as the commit message for the merge commit.
+
+
+OPTIONS FOR add, merge, push, pull
+----------------------------------
+--squash::
+	This option is only valid for add, merge, push and pull
+	commands.
+
+	Instead of merging the entire history from the subtree
+	project, produce only a single commit that contains all
+	the differences you want to merge, and then merge that
+	new commit into your project.
+	
+	Using this option helps to reduce log clutter. People
+	rarely want to see every change that happened between
+	v1.0 and v1.1 of the library they're using, since none of the
+	interim versions were ever included in their application.
+	
+	Using '--squash' also helps avoid problems when the same
+	subproject is included multiple times in the same
+	project, or is removed and then re-added.  In such a
+	case, it doesn't make sense to combine the histories
+	anyway, since it's unclear which part of the history
+	belongs to which subtree.
+	
+	Furthermore, with '--squash', you can switch back and
+	forth between different versions of a subtree, rather
+	than strictly forward.  'git subtree merge --squash'
+	always adjusts the subtree to match the exactly
+	specified commit, even if getting to that commit would
+	require undoing some changes that were added earlier.
+	
+	Whether or not you use '--squash', changes made in your
+	local repository remain intact and can be later split
+	and send upstream to the subproject.
+
+
+OPTIONS FOR split
+-----------------
+--annotate=<annotation>::
+	This option is only valid for the split command.
+
+	When generating synthetic history, add <annotation> as a
+	prefix to each commit message.  Since we're creating new
+	commits with the same commit message, but possibly
+	different content, from the original commits, this can help
+	to differentiate them and avoid confusion.
+	
+	Whenever you split, you need to use the same
+	<annotation>, or else you don't have a guarantee that
+	the new re-created history will be identical to the old
+	one.  That will prevent merging from working correctly. 
+	git subtree tries to make it work anyway, particularly
+	if you use --rejoin, but it may not always be effective.
+
+-b <branch>::
+--branch=<branch>::
+	This option is only valid for the split command.
+
+	After generating the synthetic history, create a new
+	branch called <branch> that contains the new history. 
+	This is suitable for immediate pushing upstream. 
+	<branch> must not already exist.
+
+--ignore-joins::
+	This option is only valid for the split command.
+
+	If you use '--rejoin', git subtree attempts to optimize
+	its history reconstruction to generate only the new
+	commits since the last '--rejoin'.  '--ignore-join'
+	disables this behaviour, forcing it to regenerate the
+	entire history.  In a large project, this can take a
+	long time.
+
+--onto=<onto>::
+	This option is only valid for the split command.
+
+	If your subtree was originally imported using something
+	other than git subtree, its history may not match what
+	git subtree is expecting.  In that case, you can specify
+	the commit id <onto> that corresponds to the first
+	revision of the subproject's history that was imported
+	into your project, and git subtree will attempt to build
+	its history from there.
+	
+	If you used 'git subtree add', you should never need
+	this option.
+
+--rejoin::
+	This option is only valid for the split command.
+
+	After splitting, merge the newly created synthetic
+	history back into your main project.  That way, future
+	splits can search only the part of history that has
+	been added since the most recent --rejoin.
+	
+	If your split commits end up merged into the upstream
+	subproject, and then you want to get the latest upstream
+	version, this will allow git's merge algorithm to more
+	intelligently avoid conflicts (since it knows these
+	synthetic commits are already part of the upstream
+	repository).
+	
+	Unfortunately, using this option results in 'git log'
+	showing an extra copy of every new commit that was
+	created (the original, and the synthetic one).
+	
+	If you do all your merges with '--squash', don't use
+	'--rejoin' when you split, because you don't want the
+	subproject's history to be part of your project anyway.
+
+
+EXAMPLE 1. Add command
+----------------------
+Let's assume that you have a local repository that you would like
+to add an external vendor library to. In this case we will add the
+git-subtree repository as a subdirectory of your already existing
+git-extensions repository in ~/git-extensions/:
+
+	$ git subtree add --prefix=git-subtree --squash \
+		git://github.com/apenwarr/git-subtree.git master
+
+'master' needs to be a valid remote ref and can be a different branch
+name
+
+You can omit the --squash flag, but doing so will increase the number
+of commits that are incldued in your local repository.
+
+We now have a ~/git-extensions/git-subtree directory containing code
+from the master branch of git://github.com/apenwarr/git-subtree.git
+in our git-extensions repository.
+
+EXAMPLE 2. Extract a subtree using commit, merge and pull
+---------------------------------------------------------
+Let's use the repository for the git source code as an example.
+First, get your own copy of the git.git repository:
+
+	$ git clone git://git.kernel.org/pub/scm/git/git.git test-git
+	$ cd test-git
+
+gitweb (commit 1130ef3) was merged into git as of commit
+0a8f4f0, after which it was no longer maintained separately. 
+But imagine it had been maintained separately, and we wanted to
+extract git's changes to gitweb since that time, to share with
+the upstream.  You could do this:
+
+	$ git subtree split --prefix=gitweb --annotate='(split) ' \
+        	0a8f4f0^.. --onto=1130ef3 --rejoin \
+        	--branch gitweb-latest
+        $ gitk gitweb-latest
+        $ git push git@github.com:whatever/gitweb.git gitweb-latest:master
+        
+(We use '0a8f4f0^..' because that means "all the changes from
+0a8f4f0 to the current version, including 0a8f4f0 itself.")
+
+If gitweb had originally been merged using 'git subtree add' (or
+a previous split had already been done with --rejoin specified)
+then you can do all your splits without having to remember any
+weird commit ids:
+
+	$ git subtree split --prefix=gitweb --annotate='(split) ' --rejoin \
+		--branch gitweb-latest2
+
+And you can merge changes back in from the upstream project just
+as easily:
+
+	$ git subtree pull --prefix=gitweb \
+		git@github.com:whatever/gitweb.git master
+
+Or, using '--squash', you can actually rewind to an earlier
+version of gitweb:
+
+	$ git subtree merge --prefix=gitweb --squash gitweb-latest~10
+
+Then make some changes:
+
+	$ date >gitweb/myfile
+	$ git add gitweb/myfile
+	$ git commit -m 'created myfile'
+
+And fast forward again:
+
+	$ git subtree merge --prefix=gitweb --squash gitweb-latest
+
+And notice that your change is still intact:
+	
+	$ ls -l gitweb/myfile
+
+And you can split it out and look at your changes versus
+the standard gitweb:
+
+	git log gitweb-latest..$(git subtree split --prefix=gitweb)
+
+EXAMPLE 3. Extract a subtree using branch
+-----------------------------------------
+Suppose you have a source directory with many files and
+subdirectories, and you want to extract the lib directory to its own
+git project. Here's a short way to do it:
+
+First, make the new repository wherever you want:
+
+	$ <go to the new location>
+	$ git init --bare
+
+Back in your original directory:
+
+	$ git subtree split --prefix=lib --annotate="(split)" -b split
+
+Then push the new branch onto the new empty repository:
+
+	$ git push <new-repo> split:master
+
+
+AUTHOR
+------
+Written by Avery Pennarun <apenwarr@gmail.com>
+
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/contrib/subtree/t/Makefile b/contrib/subtree/t/Makefile
new file mode 100644
index 0000000..c864810
--- /dev/null
+++ b/contrib/subtree/t/Makefile
@@ -0,0 +1,69 @@
+# Run tests
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+-include ../../../config.mak.autogen
+-include ../../../config.mak
+
+#GIT_TEST_OPTS=--verbose --debug
+SHELL_PATH ?= $(SHELL)
+PERL_PATH ?= /usr/bin/perl
+TAR ?= $(TAR)
+RM ?= rm -f
+PROVE ?= prove
+DEFAULT_TEST_TARGET ?= test
+
+# Shell quote;
+SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
+
+T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
+
+all: $(DEFAULT_TEST_TARGET)
+
+test: pre-clean $(TEST_LINT)
+	$(MAKE) aggregate-results-and-cleanup
+
+prove: pre-clean $(TEST_LINT)
+	@echo "*** prove ***"; GIT_CONFIG=.git/config $(PROVE) --exec '$(SHELL_PATH_SQ)' $(GIT_PROVE_OPTS) $(T) :: $(GIT_TEST_OPTS)
+	$(MAKE) clean
+
+$(T):
+	@echo "*** $@ ***"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
+
+pre-clean:
+	$(RM) -r test-results
+
+clean:
+	$(RM) -r 'trash directory'.* test-results
+	$(RM) -r valgrind/bin
+	$(RM) .prove
+
+test-lint: test-lint-duplicates test-lint-executable
+
+test-lint-duplicates:
+	@dups=`echo $(T) | tr ' ' '\n' | sed 's/-.*//' | sort | uniq -d` && \
+		test -z "$$dups" || { \
+		echo >&2 "duplicate test numbers:" $$dups; exit 1; }
+
+test-lint-executable:
+	@bad=`for i in $(T); do test -x "$$i" || echo $$i; done` && \
+		test -z "$$bad" || { \
+		echo >&2 "non-executable tests:" $$bad; exit 1; }
+
+aggregate-results-and-cleanup: $(T)
+	$(MAKE) aggregate-results
+	$(MAKE) clean
+
+aggregate-results:
+	for f in ../../../t/test-results/t*-*.counts; do \
+		echo "$$f"; \
+	done | '$(SHELL_PATH_SQ)' ../../../t/aggregate-results.sh
+
+valgrind:
+	$(MAKE) GIT_TEST_OPTS="$(GIT_TEST_OPTS) --valgrind"
+
+test-results:
+	mkdir -p test-results
+
+.PHONY: pre-clean $(T) aggregate-results clean valgrind
diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh
new file mode 100755
index 0000000..bc2eeb0
--- /dev/null
+++ b/contrib/subtree/t/t7900-subtree.sh
@@ -0,0 +1,508 @@
+#!/bin/sh
+#
+# Copyright (c) 2012 Avery Pennaraum
+#
+test_description='Basic porcelain support for subtrees
+
+This test verifies the basic operation of the merge, pull, add
+and split subcommands of git subtree.
+'
+
+export TEST_DIRECTORY=$(pwd)/../../../t
+
+. ../../../t/test-lib.sh
+
+create()
+{
+	echo "$1" >"$1"
+	git add "$1"
+}
+
+
+check_equal()
+{
+	test_debug 'echo'
+	test_debug "echo \"check a:\" \"{$1}\""
+	test_debug "echo \"      b:\" \"{$2}\""
+	if [ "$1" = "$2" ]; then
+		return 0
+	else
+		return 1
+	fi
+}
+
+fixnl()
+{
+	t=""
+	while read x; do
+		t="$t$x "
+	done
+	echo $t
+}
+
+multiline()
+{
+	while read x; do
+		set -- $x
+		for d in "$@"; do
+			echo "$d"
+		done
+	done
+}
+
+undo()
+{
+	git reset --hard HEAD~
+}
+
+last_commit_message()
+{
+	git log --pretty=format:%s -1
+}
+
+# 1
+test_expect_success 'init subproj' '
+        test_create_repo subproj
+'
+
+# To the subproject!
+cd subproj
+
+# 2
+test_expect_success 'add sub1' '
+        create sub1 &&
+        git commit -m "sub1" &&
+        git branch sub1 &&
+        git branch -m master subproj
+'
+
+# 3
+test_expect_success 'add sub2' '
+        create sub2 &&
+        git commit -m "sub2" &&
+        git branch sub2
+'
+
+# 4
+test_expect_success 'add sub3' '
+        create sub3 &&
+        git commit -m "sub3" &&
+        git branch sub3
+'
+
+# Back to mainline
+cd ..
+
+# 5
+test_expect_success 'add main4' '
+        create main4 &&
+        git commit -m "main4" &&
+        git branch -m master mainline &&
+        git branch subdir
+'
+
+# 6
+test_expect_success 'fetch subproj history' '
+        git fetch ./subproj sub1 &&
+        git branch sub1 FETCH_HEAD
+'
+
+# 7
+test_expect_success 'no subtree exists in main tree' '
+        test_must_fail git subtree merge --prefix=subdir sub1
+'
+
+# 8
+test_expect_success 'no pull from non-existant subtree' '
+        test_must_fail git subtree pull --prefix=subdir ./subproj sub1
+'
+
+# 9
+test_expect_success 'check if --message works for add' '
+        git subtree add --prefix=subdir --message="Added subproject" sub1 &&
+        check_equal ''"$(last_commit_message)"'' "Added subproject" &&
+        undo
+'
+
+# 10
+test_expect_success 'check if --message works as -m and --prefix as -P' '
+        git subtree add -P subdir -m "Added subproject using git subtree" sub1 &&
+        check_equal ''"$(last_commit_message)"'' "Added subproject using git subtree" &&
+        undo
+'
+
+# 11
+test_expect_success 'check if --message works with squash too' '
+        git subtree add -P subdir -m "Added subproject with squash" --squash sub1 &&
+        check_equal ''"$(last_commit_message)"'' "Added subproject with squash" &&
+        undo
+'
+
+# 12
+test_expect_success 'add subproj to mainline' '
+        git subtree add --prefix=subdir/ FETCH_HEAD &&
+        check_equal ''"$(last_commit_message)"'' "Add '"'subdir/'"' from commit '"'"'''"$(git rev-parse sub1)"'''"'"'"
+'
+
+# 13
+# this shouldn't actually do anything, since FETCH_HEAD is already a parent
+test_expect_success 'merge fetched subproj' '
+        git merge -m "merge -s -ours" -s ours FETCH_HEAD
+'
+
+# 14
+test_expect_success 'add main-sub5' '
+        create subdir/main-sub5 &&
+        git commit -m "main-sub5"
+'
+
+# 15
+test_expect_success 'add main6' '
+        create main6 &&
+        git commit -m "main6 boring"
+'
+
+# 16
+test_expect_success 'add main-sub7' '
+        create subdir/main-sub7 &&
+        git commit -m "main-sub7"
+'
+
+# 17
+test_expect_success 'fetch new subproj history' '
+        git fetch ./subproj sub2 &&
+        git branch sub2 FETCH_HEAD
+'
+
+# 18
+test_expect_success 'check if --message works for merge' '
+        git subtree merge --prefix=subdir -m "Merged changes from subproject" sub2 &&
+        check_equal ''"$(last_commit_message)"'' "Merged changes from subproject" &&
+        undo
+'
+
+# 19
+test_expect_success 'check if --message for merge works with squash too' '
+        git subtree merge --prefix subdir -m "Merged changes from subproject using squash" --squash sub2 &&
+        check_equal ''"$(last_commit_message)"'' "Merged changes from subproject using squash" &&
+        undo
+'
+
+# 20
+test_expect_success 'merge new subproj history into subdir' '
+        git subtree merge --prefix=subdir FETCH_HEAD &&
+        git branch pre-split &&
+        check_equal ''"$(last_commit_message)"'' "Merge commit '"'"'"$(git rev-parse sub2)"'"'"' into mainline"
+'
+
+# 21
+test_expect_success 'Check that prefix argument is required for split' '
+        echo "You must provide the --prefix option." > expected &&
+        test_must_fail git subtree split > actual 2>&1 &&
+        test_debug "echo -n expected: " &&
+        test_debug "cat expected" &&
+        test_debug "echo -n actual: " &&
+        test_debug "cat actual" &&
+        test_cmp expected actual &&
+        rm -f expected actual
+'
+
+# 22
+test_expect_success 'Check that the <prefix> exists for a split' '
+        echo "'"'"'non-existent-directory'"'"'" does not exist\; use "'"'"'git subtree add'"'"'" > expected &&
+        test_must_fail git subtree split --prefix=non-existent-directory > actual 2>&1 &&
+        test_debug "echo -n expected: " &&
+        test_debug "cat expected" &&
+        test_debug "echo -n actual: " &&
+        test_debug "cat actual" &&
+        test_cmp expected actual
+#        rm -f expected actual
+'
+
+# 23
+test_expect_success 'check if --message works for split+rejoin' '
+        spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
+        git branch spl1 "$spl1" &&
+        check_equal ''"$(last_commit_message)"'' "Split & rejoin" &&
+        undo
+'
+
+# 24
+test_expect_success 'check split with --branch' '
+        spl1=$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin) &&
+        undo &&
+        git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --branch splitbr1 &&
+        check_equal ''"$(git rev-parse splitbr1)"'' "$spl1"
+'
+
+# 25
+test_expect_success 'check split with --branch for an existing branch' '
+        spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
+        undo &&
+        git branch splitbr2 sub1 &&
+        git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --branch splitbr2 &&
+        check_equal ''"$(git rev-parse splitbr2)"'' "$spl1"
+'
+
+# 26
+test_expect_success 'check split with --branch for an incompatible branch' '
+        test_must_fail git subtree split --prefix subdir --onto FETCH_HEAD --branch subdir
+'
+
+
+# 27
+test_expect_success 'check split+rejoin' '
+        spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
+        undo &&
+        git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --rejoin &&
+        check_equal ''"$(last_commit_message)"'' "Split '"'"'subdir/'"'"' into commit '"'"'"$spl1"'"'"'"
+'
+
+# 28
+test_expect_success 'add main-sub8' '
+        create subdir/main-sub8 &&
+        git commit -m "main-sub8"
+'
+
+# To the subproject!
+cd ./subproj
+
+# 29
+test_expect_success 'merge split into subproj' '
+        git fetch .. spl1 &&
+        git branch spl1 FETCH_HEAD &&
+        git merge FETCH_HEAD
+'
+
+# 30
+test_expect_success 'add sub9' '
+        create sub9 &&
+        git commit -m "sub9"
+'
+
+# Back to mainline
+cd ..
+
+# 31
+test_expect_success 'split for sub8' '
+        split2=''"$(git subtree split --annotate='"'*'"' --prefix subdir/ --rejoin)"''
+        git branch split2 "$split2"
+'
+
+# 32
+test_expect_success 'add main-sub10' '
+        create subdir/main-sub10 &&
+        git commit -m "main-sub10"
+'
+
+# 33
+test_expect_success 'split for sub10' '
+        spl3=''"$(git subtree split --annotate='"'*'"' --prefix subdir --rejoin)"'' &&
+        git branch spl3 "$spl3"
+'
+
+# To the subproject!
+cd ./subproj
+
+# 34
+test_expect_success 'merge split into subproj' '
+        git fetch .. spl3 &&
+        git branch spl3 FETCH_HEAD &&
+        git merge FETCH_HEAD &&
+        git branch subproj-merge-spl3
+'
+
+chkm="main4 main6"
+chkms="main-sub10 main-sub5 main-sub7 main-sub8"
+chkms_sub=$(echo $chkms | multiline | sed 's,^,subdir/,' | fixnl)
+chks="sub1 sub2 sub3 sub9"
+chks_sub=$(echo $chks | multiline | sed 's,^,subdir/,' | fixnl)
+
+# 35
+test_expect_success 'make sure exactly the right set of files ends up in the subproj' '
+        subfiles=''"$(git ls-files | fixnl)"'' &&
+        check_equal "$subfiles" "$chkms $chks"
+'
+
+# 36
+test_expect_success 'make sure the subproj history *only* contains commits that affect the subdir' '
+        allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | fixnl)"'' &&
+        check_equal "$allchanges" "$chkms $chks"
+'
+
+# Back to mainline
+cd ..
+
+# 37
+test_expect_success 'pull from subproj' '
+        git fetch ./subproj subproj-merge-spl3 &&
+        git branch subproj-merge-spl3 FETCH_HEAD &&
+        git subtree pull --prefix=subdir ./subproj subproj-merge-spl3
+'
+
+# 38
+test_expect_success 'make sure exactly the right set of files ends up in the mainline' '
+        mainfiles=''"$(git ls-files | fixnl)"'' &&
+        check_equal "$mainfiles" "$chkm $chkms_sub $chks_sub"
+'
+
+# 39
+test_expect_success 'make sure each filename changed exactly once in the entire history' '
+        # main-sub?? and /subdir/main-sub?? both change, because those are the
+        # changes that were split into their own history.  And subdir/sub?? never
+        # change, since they were *only* changed in the subtree branch.
+        allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | fixnl)"'' &&
+        check_equal "$allchanges" ''"$(echo $chkms $chkm $chks $chkms_sub | multiline | sort | fixnl)"''
+'
+
+# 40
+test_expect_success 'make sure the --rejoin commits never make it into subproj' '
+        check_equal ''"$(git log --pretty=format:'"'%s'"' HEAD^2 | grep -i split)"'' ""
+'
+
+# 41
+test_expect_success 'make sure no "git subtree" tagged commits make it into subproj' '
+        # They are meaningless to subproj since one side of the merge refers to the mainline
+        check_equal ''"$(git log --pretty=format:'"'%s%n%b'"' HEAD^2 | grep "git-subtree.*:")"'' ""
+'
+
+# prepare second pair of repositories
+mkdir test2
+cd test2
+
+# 42
+test_expect_success 'init main' '
+        test_create_repo main
+'
+
+cd main
+
+# 43
+test_expect_success 'add main1' '
+        create main1 &&
+        git commit -m "main1"
+'
+
+cd ..
+
+# 44
+test_expect_success 'init sub' '
+        test_create_repo sub
+'
+
+cd sub
+
+# 45
+test_expect_success 'add sub2' '
+        create sub2 &&
+        git commit -m "sub2"
+'
+
+cd ../main
+
+# check if split can find proper base without --onto
+
+# 46
+test_expect_success 'add sub as subdir in main' '
+        git fetch ../sub master &&
+        git branch sub2 FETCH_HEAD &&
+        git subtree add --prefix subdir sub2
+'
+
+cd ../sub
+
+# 47
+test_expect_success 'add sub3' '
+        create sub3 &&
+        git commit -m "sub3"
+'
+
+cd ../main
+
+# 48
+test_expect_success 'merge from sub' '
+        git fetch ../sub master &&
+        git branch sub3 FETCH_HEAD &&
+        git subtree merge --prefix subdir sub3
+'
+
+# 49
+test_expect_success 'add main-sub4' '
+        create subdir/main-sub4 &&
+        git commit -m "main-sub4"
+'
+
+# 50
+test_expect_success 'split for main-sub4 without --onto' '
+        git subtree split --prefix subdir --branch mainsub4
+'
+
+# at this point, the new commit parent should be sub3 if it is not,
+# something went wrong (the "newparent" of "master~" commit should
+# have been sub3, but it was not, because its cache was not set to
+# itself)
+
+# 51
+test_expect_success 'check that the commit parent is sub3' '
+        check_equal ''"$(git log --pretty=format:%P -1 mainsub4)"'' ''"$(git rev-parse sub3)"''
+'
+
+# 52
+test_expect_success 'add main-sub5' '
+        mkdir subdir2 &&
+        create subdir2/main-sub5 &&
+        git commit -m "main-sub5"
+'
+
+# 53
+test_expect_success 'split for main-sub5 without --onto' '
+        # also test that we still can split out an entirely new subtree
+        # if the parent of the first commit in the tree is not empty,
+        # then the new subtree has accidently been attached to something
+        git subtree split --prefix subdir2 --branch mainsub5 &&
+        check_equal ''"$(git log --pretty=format:%P -1 mainsub5)"'' ""
+'
+
+# make sure no patch changes more than one file.  The original set of commits
+# changed only one file each.  A multi-file change would imply that we pruned
+# commits too aggressively.
+joincommits()
+{
+	commit=
+	all=
+	while read x y; do
+		#echo "{$x}" >&2
+		if [ -z "$x" ]; then
+			continue
+		elif [ "$x" = "commit:" ]; then
+			if [ -n "$commit" ]; then
+				echo "$commit $all"
+				all=
+			fi
+			commit="$y"
+		else
+			all="$all $y"
+		fi
+	done
+	echo "$commit $all"
+}
+
+# 54
+test_expect_success 'verify one file change per commit' '
+        x= &&
+        list=''"$(git log --pretty=format:'"'commit: %H'"' | joincommits)"'' &&
+#        test_debug "echo HERE" &&
+#        test_debug "echo ''"$list"''" &&
+        (git log --pretty=format:'"'commit: %H'"' | joincommits |
+        (       while read commit a b; do
+		        test_debug "echo Verifying commit "''"$commit"''
+		        test_debug "echo a: "''"$a"''
+		        test_debug "echo b: "''"$b"''
+		        check_equal "$b" ""
+		        x=1
+	        done
+	        check_equal "$x" 1
+        ))
+'
+
+test_done
diff --git a/contrib/subtree/todo b/contrib/subtree/todo
new file mode 100644
index 0000000..7e44b00
--- /dev/null
+++ b/contrib/subtree/todo
@@ -0,0 +1,50 @@
+
+	delete tempdir
+
+	'git subtree rejoin' option to do the same as --rejoin, eg. after a
+	  rebase
+
+	--prefix doesn't force the subtree correctly in merge/pull:
+	"-s subtree" should be given an explicit subtree option?
+		There doesn't seem to be a way to do this.  We'd have to
+		patch git-merge-subtree.  Ugh.
+		(but we could avoid this problem by generating squashes with
+		exactly the right subtree structure, rather than using
+		subtree merge...)
+
+	add a 'push' subcommand to parallel 'pull'
+	
+	add a 'log' subcommand to see what's new in a subtree?
+
+	add to-submodule and from-submodule commands
+
+	automated tests for --squash stuff
+
+	"add" command non-obviously requires a commitid; would be easier if
+		it had a "pull" sort of mode instead
+
+	"pull" and "merge" commands should fail if you've never merged
+		that --prefix before
+		
+	docs should provide an example of "add"
+	
+	note that the initial split doesn't *have* to have a commitid
+		specified... that's just an optimization
+
+	if you try to add (or maybe merge?) with an invalid commitid, you
+		get a misleading "prefix must end with /" message from
+		one of the other git tools that git-subtree calls.  Should
+		detect this situation and print the *real* problem.
+	
+	"pull --squash" should do fetch-synthesize-merge, but instead just
+		does "pull" directly, which doesn't work at all.
+
+	make a 'force-update' that does what 'add' does even if the subtree
+		already exists.  That way we can help people who imported
+		subtrees "incorrectly" (eg. by just copying in the files) in
+		the past.
+
+	guess --prefix automatically if possible based on pwd
+
+	make a 'git subtree grafts' that automatically expands --squash'd
+		commits so you can see the full history if you want it.
diff --git a/contrib/svn-fe/.gitignore b/contrib/svn-fe/.gitignore
new file mode 100644
index 0000000..02a7791
--- /dev/null
+++ b/contrib/svn-fe/.gitignore
@@ -0,0 +1,4 @@
+/*.xml
+/*.1
+/*.html
+/svn-fe
diff --git a/contrib/svn-fe/Makefile b/contrib/svn-fe/Makefile
new file mode 100644
index 0000000..360d8da
--- /dev/null
+++ b/contrib/svn-fe/Makefile
@@ -0,0 +1,63 @@
+all:: svn-fe$X
+
+CC = gcc
+RM = rm -f
+MV = mv
+
+CFLAGS = -g -O2 -Wall
+LDFLAGS =
+ALL_CFLAGS = $(CFLAGS)
+ALL_LDFLAGS = $(LDFLAGS)
+EXTLIBS =
+
+GIT_LIB = ../../libgit.a
+VCSSVN_LIB = ../../vcs-svn/lib.a
+LIBS = $(VCSSVN_LIB) $(GIT_LIB) $(EXTLIBS)
+
+QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir
+QUIET_SUBDIR1 =
+
+ifneq ($(findstring $(MAKEFLAGS),w),w)
+PRINT_DIR = --no-print-directory
+else # "make -w"
+NO_SUBDIR = :
+endif
+
+ifneq ($(findstring $(MAKEFLAGS),s),s)
+ifndef V
+	QUIET_CC      = @echo '   ' CC $@;
+	QUIET_LINK    = @echo '   ' LINK $@;
+	QUIET_SUBDIR0 = +@subdir=
+	QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \
+	                $(MAKE) $(PRINT_DIR) -C $$subdir
+endif
+endif
+
+svn-fe$X: svn-fe.o $(VCSSVN_LIB) $(GIT_LIB)
+	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ svn-fe.o \
+		$(ALL_LDFLAGS) $(LIBS)
+
+svn-fe.o: svn-fe.c ../../vcs-svn/svndump.h
+	$(QUIET_CC)$(CC) -I../../vcs-svn -o $*.o -c $(ALL_CFLAGS) $<
+
+svn-fe.html: svn-fe.txt
+	$(QUIET_SUBDIR0)../../Documentation $(QUIET_SUBDIR1) \
+		MAN_TXT=../contrib/svn-fe/svn-fe.txt \
+		../contrib/svn-fe/$@
+
+svn-fe.1: svn-fe.txt
+	$(QUIET_SUBDIR0)../../Documentation $(QUIET_SUBDIR1) \
+		MAN_TXT=../contrib/svn-fe/svn-fe.txt \
+		../contrib/svn-fe/$@
+	$(MV) ../../Documentation/svn-fe.1 .
+
+../../vcs-svn/lib.a: FORCE
+	$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) vcs-svn/lib.a
+
+../../libgit.a: FORCE
+	$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) libgit.a
+
+clean:
+	$(RM) svn-fe$X svn-fe.o svn-fe.html svn-fe.xml svn-fe.1
+
+.PHONY: all clean FORCE
diff --git a/contrib/svn-fe/svn-fe.c b/contrib/svn-fe/svn-fe.c
new file mode 100644
index 0000000..35db24f
--- /dev/null
+++ b/contrib/svn-fe/svn-fe.c
@@ -0,0 +1,17 @@
+/*
+ * This file is in the public domain.
+ * You may freely use, modify, distribute, and relicense it.
+ */
+
+#include <stdlib.h>
+#include "svndump.h"
+
+int main(int argc, char **argv)
+{
+	if (svndump_init(NULL))
+		return 1;
+	svndump_read((argc > 1) ? argv[1] : NULL);
+	svndump_deinit();
+	svndump_reset();
+	return 0;
+}
diff --git a/contrib/svn-fe/svn-fe.txt b/contrib/svn-fe/svn-fe.txt
new file mode 100644
index 0000000..1128ab2
--- /dev/null
+++ b/contrib/svn-fe/svn-fe.txt
@@ -0,0 +1,71 @@
+svn-fe(1)
+=========
+
+NAME
+----
+svn-fe - convert an SVN "dumpfile" to a fast-import stream
+
+SYNOPSIS
+--------
+[verse]
+mkfifo backchannel &&
+svnadmin dump --deltas REPO |
+	svn-fe [url] 3<backchannel |
+	git fast-import --cat-blob-fd=3 3>backchannel
+
+DESCRIPTION
+-----------
+
+Converts a Subversion dumpfile into input suitable for
+git-fast-import(1) and similar importers. REPO is a path to a
+Subversion repository mirrored on the local disk. Remote Subversion
+repositories can be mirrored on local disk using the `svnsync`
+command.
+
+Note: this tool is very young.  The details of its commandline
+interface may change in backward incompatible ways.
+
+INPUT FORMAT
+------------
+Subversion's repository dump format is documented in full in
+`notes/dump-load-format.txt` from the Subversion source tree.
+Files in this format can be generated using the 'svnadmin dump' or
+'svk admin dump' command.
+
+OUTPUT FORMAT
+-------------
+The fast-import format is documented by the git-fast-import(1)
+manual page.
+
+NOTES
+-----
+Subversion dumps do not record a separate author and committer for
+each revision, nor a separate display name and email address for
+each author.  Like git-svn(1), 'svn-fe' will use the name
+
+---------
+user <user@UUID>
+---------
+
+as committer, where 'user' is the value of the `svn:author` property
+and 'UUID' the repository's identifier.
+
+To support incremental imports, 'svn-fe' puts a `git-svn-id` line at
+the end of each commit log message if passed a URL on the command
+line.  This line has the form `git-svn-id: URL@REVNO UUID`.
+
+The resulting repository will generally require further processing
+to put each project in its own repository and to separate the history
+of each branch.  The 'git filter-branch --subdirectory-filter' command
+may be useful for this purpose.
+
+BUGS
+----
+Empty directories and unknown properties are silently discarded.
+
+The exit status does not reflect whether an error was detected.
+
+SEE ALSO
+--------
+git-svn(1), svn2git(1), svk(1), git-filter-branch(1), git-fast-import(1),
+https://svn.apache.org/repos/asf/subversion/trunk/notes/dump-load-format.txt
diff --git a/contrib/thunderbird-patch-inline/appp.sh b/contrib/thunderbird-patch-inline/appp.sh
index cc518f3..5eb4a51 100755
--- a/contrib/thunderbird-patch-inline/appp.sh
+++ b/contrib/thunderbird-patch-inline/appp.sh
@@ -1,8 +1,8 @@
-#!/bin/bash
+#!/bin/sh
 # Copyright 2008 Lukas Sandström <luksan@gmail.com>
 #
 # AppendPatch - A script to be used together with ExternalEditor
-# for Mozilla Thunderbird to properly include pathes inline i e-mails.
+# for Mozilla Thunderbird to properly include patches inline in e-mails.
 
 # ExternalEditor can be downloaded at http://globs.org/articles.php?lng=en&pg=2
 
diff --git a/contrib/workdir/git-new-workdir b/contrib/workdir/git-new-workdir
index 993cacf..75e8b25 100755
--- a/contrib/workdir/git-new-workdir
+++ b/contrib/workdir/git-new-workdir
@@ -42,7 +42,7 @@
 fi
 
 # don't link to a workdir
-if test -L "$git_dir/config"
+if test -h "$git_dir/config"
 then
 	die "\"$orig_git\" is a working directory only, please specify" \
 		"a complete repository."
@@ -54,13 +54,13 @@
 	die "destination directory '$new_workdir' already exists."
 fi
 
-# make sure the the links use full paths
+# make sure the links use full paths
 git_dir=$(cd "$git_dir"; pwd)
 
 # create the workdir
 mkdir -p "$new_workdir/.git" || die "unable to create \"$new_workdir\"!"
 
-# create the links to the original repo.  explictly exclude index, HEAD and
+# create the links to the original repo.  explicitly exclude index, HEAD and
 # logs/HEAD from the list since they are purely related to the current working
 # directory, and should not be shared.
 for x in config refs logs/refs objects info hooks packed-refs remotes rr-cache svn
diff --git a/convert.c b/convert.c
index 1816e97..6602155 100644
--- a/convert.c
+++ b/convert.c
@@ -1,6 +1,8 @@
 #include "cache.h"
 #include "attr.h"
 #include "run-command.h"
+#include "quote.h"
+#include "sigchain.h"
 
 /*
  * convert.c - convert a file when checking it out and checking it in.
@@ -8,13 +10,17 @@
  * This should use the pathname to decide on whether it wants to do some
  * more interesting conversions (automatic gzip/unzip, general format
  * conversions etc etc), but by default it just does automatic CRLF<->LF
- * translation when the "auto_crlf" option is set.
+ * translation when the "text" attribute or "auto_crlf" option is set.
  */
 
-#define CRLF_GUESS	(-1)
-#define CRLF_BINARY	0
-#define CRLF_TEXT	1
-#define CRLF_INPUT	2
+enum crlf_action {
+	CRLF_GUESS = -1,
+	CRLF_BINARY = 0,
+	CRLF_TEXT,
+	CRLF_INPUT,
+	CRLF_CRLF,
+	CRLF_AUTO
+};
 
 struct text_stat {
 	/* NUL, CR, LF and CRLF counts */
@@ -89,49 +95,121 @@
 	return 0;
 }
 
-static void check_safe_crlf(const char *path, int action,
+static enum eol output_eol(enum crlf_action crlf_action)
+{
+	switch (crlf_action) {
+	case CRLF_BINARY:
+		return EOL_UNSET;
+	case CRLF_CRLF:
+		return EOL_CRLF;
+	case CRLF_INPUT:
+		return EOL_LF;
+	case CRLF_GUESS:
+		if (!auto_crlf)
+			return EOL_UNSET;
+		/* fall through */
+	case CRLF_TEXT:
+	case CRLF_AUTO:
+		if (auto_crlf == AUTO_CRLF_TRUE)
+			return EOL_CRLF;
+		else if (auto_crlf == AUTO_CRLF_INPUT)
+			return EOL_LF;
+		else if (core_eol == EOL_UNSET)
+			return EOL_NATIVE;
+	}
+	return core_eol;
+}
+
+static void check_safe_crlf(const char *path, enum crlf_action crlf_action,
                             struct text_stat *stats, enum safe_crlf checksafe)
 {
 	if (!checksafe)
 		return;
 
-	if (action == CRLF_INPUT || auto_crlf <= 0) {
+	if (output_eol(crlf_action) == EOL_LF) {
 		/*
 		 * CRLFs would not be restored by checkout:
 		 * check if we'd remove CRLFs
 		 */
 		if (stats->crlf) {
 			if (checksafe == SAFE_CRLF_WARN)
-				warning("CRLF will be replaced by LF in %s.", path);
+				warning("CRLF will be replaced by LF in %s.\nThe file will have its original line endings in your working directory.", path);
 			else /* i.e. SAFE_CRLF_FAIL */
 				die("CRLF would be replaced by LF in %s.", path);
 		}
-	} else if (auto_crlf > 0) {
+	} else if (output_eol(crlf_action) == EOL_CRLF) {
 		/*
 		 * CRLFs would be added by checkout:
 		 * check if we have "naked" LFs
 		 */
 		if (stats->lf != stats->crlf) {
 			if (checksafe == SAFE_CRLF_WARN)
-				warning("LF will be replaced by CRLF in %s", path);
+				warning("LF will be replaced by CRLF in %s.\nThe file will have its original line endings in your working directory.", path);
 			else /* i.e. SAFE_CRLF_FAIL */
 				die("LF would be replaced by CRLF in %s", path);
 		}
 	}
 }
 
+static int has_cr_in_index(const char *path)
+{
+	int pos, len;
+	unsigned long sz;
+	enum object_type type;
+	void *data;
+	int has_cr;
+	struct index_state *istate = &the_index;
+
+	len = strlen(path);
+	pos = index_name_pos(istate, path, len);
+	if (pos < 0) {
+		/*
+		 * We might be in the middle of a merge, in which
+		 * case we would read stage #2 (ours).
+		 */
+		int i;
+		for (i = -pos - 1;
+		     (pos < 0 && i < istate->cache_nr &&
+		      !strcmp(istate->cache[i]->name, path));
+		     i++)
+			if (ce_stage(istate->cache[i]) == 2)
+				pos = i;
+	}
+	if (pos < 0)
+		return 0;
+	data = read_sha1_file(istate->cache[pos]->sha1, &type, &sz);
+	if (!data || type != OBJ_BLOB) {
+		free(data);
+		return 0;
+	}
+
+	has_cr = memchr(data, '\r', sz) != NULL;
+	free(data);
+	return has_cr;
+}
+
 static int crlf_to_git(const char *path, const char *src, size_t len,
-                       struct strbuf *buf, int action, enum safe_crlf checksafe)
+		       struct strbuf *buf,
+		       enum crlf_action crlf_action, enum safe_crlf checksafe)
 {
 	struct text_stat stats;
 	char *dst;
 
-	if ((action == CRLF_BINARY) || !auto_crlf || !len)
+	if (crlf_action == CRLF_BINARY ||
+	    (crlf_action == CRLF_GUESS && auto_crlf == AUTO_CRLF_FALSE) ||
+	    (src && !len))
 		return 0;
 
+	/*
+	 * If we are doing a dry-run and have no source buffer, there is
+	 * nothing to analyze; we must assume we would convert.
+	 */
+	if (!buf && !src)
+		return 1;
+
 	gather_stats(src, len, &stats);
 
-	if (action == CRLF_GUESS) {
+	if (crlf_action == CRLF_AUTO || crlf_action == CRLF_GUESS) {
 		/*
 		 * We're currently not going to even try to convert stuff
 		 * that has bare CR characters. Does anybody do that crazy
@@ -145,19 +223,35 @@
 		 */
 		if (is_binary(len, &stats))
 			return 0;
+
+		if (crlf_action == CRLF_GUESS) {
+			/*
+			 * If the file in the index has any CR in it, do not convert.
+			 * This is the new safer autocrlf handling.
+			 */
+			if (has_cr_in_index(path))
+				return 0;
+		}
 	}
 
-	check_safe_crlf(path, action, &stats, checksafe);
+	check_safe_crlf(path, crlf_action, &stats, checksafe);
 
 	/* Optimization: No CR? Nothing to convert, regardless. */
 	if (!stats.cr)
 		return 0;
 
+	/*
+	 * At this point all of our source analysis is done, and we are sure we
+	 * would convert. If we are in dry-run mode, we can give an answer.
+	 */
+	if (!buf)
+		return 1;
+
 	/* only grow if not in place */
 	if (strbuf_avail(buf) + buf->len < len)
 		strbuf_grow(buf, len - buf->len);
 	dst = buf->buf;
-	if (action == CRLF_GUESS) {
+	if (crlf_action == CRLF_AUTO || crlf_action == CRLF_GUESS) {
 		/*
 		 * If we guessed, we already know we rejected a file with
 		 * lone CR, and we can strip a CR without looking at what
@@ -180,16 +274,12 @@
 }
 
 static int crlf_to_worktree(const char *path, const char *src, size_t len,
-                            struct strbuf *buf, int action)
+			    struct strbuf *buf, enum crlf_action crlf_action)
 {
 	char *to_free = NULL;
 	struct text_stat stats;
 
-	if ((action == CRLF_BINARY) || (action == CRLF_INPUT) ||
-	    auto_crlf <= 0)
-		return 0;
-
-	if (!len)
+	if (!len || output_eol(crlf_action) != EOL_CRLF)
 		return 0;
 
 	gather_stats(src, len, &stats);
@@ -202,7 +292,14 @@
 	if (stats.lf == stats.crlf)
 		return 0;
 
-	if (action == CRLF_GUESS) {
+	if (crlf_action == CRLF_AUTO || crlf_action == CRLF_GUESS) {
+		if (crlf_action == CRLF_GUESS) {
+			/* If we have any CR or CRLF line endings, we do not touch it */
+			/* This is the new safer autocrlf-handling */
+			if (stats.cr > 0 || stats.crlf > 0)
+				return 0;
+		}
+
 		/* If we have any bare CR characters, we're not going to touch it */
 		if (stats.cr != stats.crlf)
 			return 0;
@@ -239,9 +336,10 @@
 	const char *src;
 	unsigned long size;
 	const char *cmd;
+	const char *path;
 };
 
-static int filter_buffer(int fd, void *data)
+static int filter_buffer(int in, int out, void *data)
 {
 	/*
 	 * Spawn cmd and feed the buffer contents through its stdin.
@@ -249,25 +347,50 @@
 	struct child_process child_process;
 	struct filter_params *params = (struct filter_params *)data;
 	int write_err, status;
-	const char *argv[] = { "sh", "-c", params->cmd, NULL };
+	const char *argv[] = { NULL, NULL };
+
+	/* apply % substitution to cmd */
+	struct strbuf cmd = STRBUF_INIT;
+	struct strbuf path = STRBUF_INIT;
+	struct strbuf_expand_dict_entry dict[] = {
+		{ "f", NULL, },
+		{ NULL, NULL, },
+	};
+
+	/* quote the path to preserve spaces, etc. */
+	sq_quote_buf(&path, params->path);
+	dict[0].value = path.buf;
+
+	/* expand all %f with the quoted path */
+	strbuf_expand(&cmd, params->cmd, strbuf_expand_dict_cb, &dict);
+	strbuf_release(&path);
+
+	argv[0] = cmd.buf;
 
 	memset(&child_process, 0, sizeof(child_process));
 	child_process.argv = argv;
+	child_process.use_shell = 1;
 	child_process.in = -1;
-	child_process.out = fd;
+	child_process.out = out;
 
 	if (start_command(&child_process))
 		return error("cannot fork to run external filter %s", params->cmd);
 
+	sigchain_push(SIGPIPE, SIG_IGN);
+
 	write_err = (write_in_full(child_process.in, params->src, params->size) < 0);
 	if (close(child_process.in))
 		write_err = 1;
 	if (write_err)
 		error("cannot feed the input to external filter %s", params->cmd);
 
+	sigchain_pop(SIGPIPE);
+
 	status = finish_command(&child_process);
 	if (status)
-		error("external filter %s failed %d", params->cmd, -status);
+		error("external filter %s failed %d", params->cmd, status);
+
+	strbuf_release(&cmd);
 	return (write_err || status);
 }
 
@@ -288,12 +411,17 @@
 	if (!cmd)
 		return 0;
 
+	if (!dst)
+		return 1;
+
 	memset(&async, 0, sizeof(async));
 	async.proc = filter_buffer;
 	async.data = &params;
+	async.out = -1;
 	params.src = src;
 	params.size = len;
 	params.cmd = cmd;
+	params.path = path;
 
 	fflush(NULL);
 	if (start_async(&async))
@@ -324,6 +452,7 @@
 	struct convert_driver *next;
 	const char *smudge;
 	const char *clean;
+	int required;
 } *user_convert, **user_convert_tail;
 
 static int read_convert_config(const char *var, const char *value, void *cb)
@@ -367,25 +496,12 @@
 	if (!strcmp("clean", ep))
 		return git_config_string(&drv->clean, var, value);
 
-	return 0;
-}
-
-static void setup_convert_check(struct git_attr_check *check)
-{
-	static struct git_attr *attr_crlf;
-	static struct git_attr *attr_ident;
-	static struct git_attr *attr_filter;
-
-	if (!attr_crlf) {
-		attr_crlf = git_attr("crlf", 4);
-		attr_ident = git_attr("ident", 5);
-		attr_filter = git_attr("filter", 6);
-		user_convert_tail = &user_convert;
-		git_config(read_convert_config, NULL);
+	if (!strcmp("required", ep)) {
+		drv->required = git_config_bool(var, value);
+		return 0;
 	}
-	check[0].attr = attr_crlf;
-	check[1].attr = attr_ident;
-	check[2].attr = attr_filter;
+
+	return 0;
 }
 
 static int count_ident(const char *cp, unsigned long size)
@@ -423,6 +539,8 @@
 				cnt++;
 				break;
 			}
+			if (ch == '\n')
+				break;
 		}
 	}
 	return cnt;
@@ -433,9 +551,12 @@
 {
 	char *dst, *dollar;
 
-	if (!ident || !count_ident(src, len))
+	if (!ident || (src && !count_ident(src, len)))
 		return 0;
 
+	if (!buf)
+		return 1;
+
 	/* only grow if not in place */
 	if (strbuf_avail(buf) + buf->len < len)
 		strbuf_grow(buf, len - buf->len);
@@ -444,7 +565,7 @@
 		dollar = memchr(src, '$', len);
 		if (!dollar)
 			break;
-		memcpy(dst, src, dollar + 1 - src);
+		memmove(dst, src, dollar + 1 - src);
 		dst += dollar + 1 - src;
 		len -= dollar + 1 - src;
 		src  = dollar + 1;
@@ -453,13 +574,18 @@
 			dollar = memchr(src + 3, '$', len - 3);
 			if (!dollar)
 				break;
+			if (memchr(src + 3, '\n', dollar - src - 3)) {
+				/* Line break before the next dollar. */
+				continue;
+			}
+
 			memcpy(dst, "Id$", 3);
 			dst += 3;
 			len -= dollar + 1 - src;
 			src  = dollar + 1;
 		}
 	}
-	memcpy(dst, src, len);
+	memmove(dst, src, len);
 	strbuf_setlen(buf, dst + len - buf->buf);
 	return 1;
 }
@@ -468,7 +594,7 @@
                              struct strbuf *buf, int ident)
 {
 	unsigned char sha1[20];
-	char *to_free = NULL, *dollar;
+	char *to_free = NULL, *dollar, *spc;
 	int cnt;
 
 	if (!ident)
@@ -504,7 +630,10 @@
 		} else if (src[2] == ':') {
 			/*
 			 * It's possible that an expanded Id has crept its way into the
-			 * repository, we cope with that by stripping the expansion out
+			 * repository, we cope with that by stripping the expansion out.
+			 * This is probably not a good idea, since it will cause changes
+			 * on checkout, which won't go away by stash, but let's keep it
+			 * for git-style ids.
 			 */
 			dollar = memchr(src + 3, '$', len - 3);
 			if (!dollar) {
@@ -512,6 +641,20 @@
 				break;
 			}
 
+			if (memchr(src + 3, '\n', dollar - src - 3)) {
+				/* Line break before the next dollar. */
+				continue;
+			}
+
+			spc = memchr(src + 4, ' ', dollar - src - 4);
+			if (spc && spc < dollar-1) {
+				/* There are spaces in unexpected places.
+				 * This is probably an id from some other
+				 * versioning system. Keep it for now.
+				 */
+				continue;
+			}
+
 			len -= dollar + 1 - src;
 			src  = dollar + 1;
 		} else {
@@ -530,7 +673,7 @@
 	return 1;
 }
 
-static int git_path_check_crlf(const char *path, struct git_attr_check *check)
+static enum crlf_action git_path_check_crlf(const char *path, struct git_attr_check *check)
 {
 	const char *value = check->value;
 
@@ -542,9 +685,24 @@
 		;
 	else if (!strcmp(value, "input"))
 		return CRLF_INPUT;
+	else if (!strcmp(value, "auto"))
+		return CRLF_AUTO;
 	return CRLF_GUESS;
 }
 
+static enum eol git_path_check_eol(const char *path, struct git_attr_check *check)
+{
+	const char *value = check->value;
+
+	if (ATTR_UNSET(value))
+		;
+	else if (!strcmp(value, "lf"))
+		return EOL_LF;
+	else if (!strcmp(value, "crlf"))
+		return EOL_CRLF;
+	return EOL_UNSET;
+}
+
 static struct convert_driver *git_path_check_convert(const char *path,
 					     struct git_attr_check *check)
 {
@@ -566,63 +724,601 @@
 	return !!ATTR_TRUE(value);
 }
 
+static enum crlf_action input_crlf_action(enum crlf_action text_attr, enum eol eol_attr)
+{
+	if (text_attr == CRLF_BINARY)
+		return CRLF_BINARY;
+	if (eol_attr == EOL_LF)
+		return CRLF_INPUT;
+	if (eol_attr == EOL_CRLF)
+		return CRLF_CRLF;
+	return text_attr;
+}
+
+struct conv_attrs {
+	struct convert_driver *drv;
+	enum crlf_action crlf_action;
+	enum eol eol_attr;
+	int ident;
+};
+
+static const char *conv_attr_name[] = {
+	"crlf", "ident", "filter", "eol", "text",
+};
+#define NUM_CONV_ATTRS ARRAY_SIZE(conv_attr_name)
+
+static void convert_attrs(struct conv_attrs *ca, const char *path)
+{
+	int i;
+	static struct git_attr_check ccheck[NUM_CONV_ATTRS];
+
+	if (!ccheck[0].attr) {
+		for (i = 0; i < NUM_CONV_ATTRS; i++)
+			ccheck[i].attr = git_attr(conv_attr_name[i]);
+		user_convert_tail = &user_convert;
+		git_config(read_convert_config, NULL);
+	}
+
+	if (!git_check_attr(path, NUM_CONV_ATTRS, ccheck)) {
+		ca->crlf_action = git_path_check_crlf(path, ccheck + 4);
+		if (ca->crlf_action == CRLF_GUESS)
+			ca->crlf_action = git_path_check_crlf(path, ccheck + 0);
+		ca->ident = git_path_check_ident(path, ccheck + 1);
+		ca->drv = git_path_check_convert(path, ccheck + 2);
+		ca->eol_attr = git_path_check_eol(path, ccheck + 3);
+	} else {
+		ca->drv = NULL;
+		ca->crlf_action = CRLF_GUESS;
+		ca->eol_attr = EOL_UNSET;
+		ca->ident = 0;
+	}
+}
+
 int convert_to_git(const char *path, const char *src, size_t len,
                    struct strbuf *dst, enum safe_crlf checksafe)
 {
-	struct git_attr_check check[3];
-	int crlf = CRLF_GUESS;
-	int ident = 0, ret = 0;
+	int ret = 0;
 	const char *filter = NULL;
+	int required = 0;
+	struct conv_attrs ca;
 
-	setup_convert_check(check);
-	if (!git_checkattr(path, ARRAY_SIZE(check), check)) {
-		struct convert_driver *drv;
-		crlf = git_path_check_crlf(path, check + 0);
-		ident = git_path_check_ident(path, check + 1);
-		drv = git_path_check_convert(path, check + 2);
-		if (drv && drv->clean)
-			filter = drv->clean;
+	convert_attrs(&ca, path);
+	if (ca.drv) {
+		filter = ca.drv->clean;
+		required = ca.drv->required;
 	}
 
 	ret |= apply_filter(path, src, len, dst, filter);
+	if (!ret && required)
+		die("%s: clean filter '%s' failed", path, ca.drv->name);
+
+	if (ret && dst) {
+		src = dst->buf;
+		len = dst->len;
+	}
+	ca.crlf_action = input_crlf_action(ca.crlf_action, ca.eol_attr);
+	ret |= crlf_to_git(path, src, len, dst, ca.crlf_action, checksafe);
+	if (ret && dst) {
+		src = dst->buf;
+		len = dst->len;
+	}
+	return ret | ident_to_git(path, src, len, dst, ca.ident);
+}
+
+static int convert_to_working_tree_internal(const char *path, const char *src,
+					    size_t len, struct strbuf *dst,
+					    int normalizing)
+{
+	int ret = 0, ret_filter = 0;
+	const char *filter = NULL;
+	int required = 0;
+	struct conv_attrs ca;
+
+	convert_attrs(&ca, path);
+	if (ca.drv) {
+		filter = ca.drv->smudge;
+		required = ca.drv->required;
+	}
+
+	ret |= ident_to_worktree(path, src, len, dst, ca.ident);
 	if (ret) {
 		src = dst->buf;
 		len = dst->len;
 	}
-	ret |= crlf_to_git(path, src, len, dst, crlf, checksafe);
-	if (ret) {
-		src = dst->buf;
-		len = dst->len;
+	/*
+	 * CRLF conversion can be skipped if normalizing, unless there
+	 * is a smudge filter.  The filter might expect CRLFs.
+	 */
+	if (filter || !normalizing) {
+		ca.crlf_action = input_crlf_action(ca.crlf_action, ca.eol_attr);
+		ret |= crlf_to_worktree(path, src, len, dst, ca.crlf_action);
+		if (ret) {
+			src = dst->buf;
+			len = dst->len;
+		}
 	}
-	return ret | ident_to_git(path, src, len, dst, ident);
+
+	ret_filter = apply_filter(path, src, len, dst, filter);
+	if (!ret_filter && required)
+		die("%s: smudge filter %s failed", path, ca.drv->name);
+
+	return ret | ret_filter;
 }
 
 int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst)
 {
-	struct git_attr_check check[3];
-	int crlf = CRLF_GUESS;
-	int ident = 0, ret = 0;
-	const char *filter = NULL;
+	return convert_to_working_tree_internal(path, src, len, dst, 0);
+}
 
-	setup_convert_check(check);
-	if (!git_checkattr(path, ARRAY_SIZE(check), check)) {
-		struct convert_driver *drv;
-		crlf = git_path_check_crlf(path, check + 0);
-		ident = git_path_check_ident(path, check + 1);
-		drv = git_path_check_convert(path, check + 2);
-		if (drv && drv->smudge)
-			filter = drv->smudge;
-	}
-
-	ret |= ident_to_worktree(path, src, len, dst, ident);
+int renormalize_buffer(const char *path, const char *src, size_t len, struct strbuf *dst)
+{
+	int ret = convert_to_working_tree_internal(path, src, len, dst, 1);
 	if (ret) {
 		src = dst->buf;
 		len = dst->len;
 	}
-	ret |= crlf_to_worktree(path, src, len, dst, crlf);
-	if (ret) {
-		src = dst->buf;
-		len = dst->len;
+	return ret | convert_to_git(path, src, len, dst, SAFE_CRLF_FALSE);
+}
+
+/*****************************************************************
+ *
+ * Streaming converison support
+ *
+ *****************************************************************/
+
+typedef int (*filter_fn)(struct stream_filter *,
+			 const char *input, size_t *isize_p,
+			 char *output, size_t *osize_p);
+typedef void (*free_fn)(struct stream_filter *);
+
+struct stream_filter_vtbl {
+	filter_fn filter;
+	free_fn free;
+};
+
+struct stream_filter {
+	struct stream_filter_vtbl *vtbl;
+};
+
+static int null_filter_fn(struct stream_filter *filter,
+			  const char *input, size_t *isize_p,
+			  char *output, size_t *osize_p)
+{
+	size_t count;
+
+	if (!input)
+		return 0; /* we do not keep any states */
+	count = *isize_p;
+	if (*osize_p < count)
+		count = *osize_p;
+	if (count) {
+		memmove(output, input, count);
+		*isize_p -= count;
+		*osize_p -= count;
 	}
-	return ret | apply_filter(path, src, len, dst, filter);
+	return 0;
+}
+
+static void null_free_fn(struct stream_filter *filter)
+{
+	; /* nothing -- null instances are shared */
+}
+
+static struct stream_filter_vtbl null_vtbl = {
+	null_filter_fn,
+	null_free_fn,
+};
+
+static struct stream_filter null_filter_singleton = {
+	&null_vtbl,
+};
+
+int is_null_stream_filter(struct stream_filter *filter)
+{
+	return filter == &null_filter_singleton;
+}
+
+
+/*
+ * LF-to-CRLF filter
+ */
+
+struct lf_to_crlf_filter {
+	struct stream_filter filter;
+	unsigned has_held:1;
+	char held;
+};
+
+static int lf_to_crlf_filter_fn(struct stream_filter *filter,
+				const char *input, size_t *isize_p,
+				char *output, size_t *osize_p)
+{
+	size_t count, o = 0;
+	struct lf_to_crlf_filter *lf_to_crlf = (struct lf_to_crlf_filter *)filter;
+
+	/*
+	 * We may be holding onto the CR to see if it is followed by a
+	 * LF, in which case we would need to go to the main loop.
+	 * Otherwise, just emit it to the output stream.
+	 */
+	if (lf_to_crlf->has_held && (lf_to_crlf->held != '\r' || !input)) {
+		output[o++] = lf_to_crlf->held;
+		lf_to_crlf->has_held = 0;
+	}
+
+	/* We are told to drain */
+	if (!input) {
+		*osize_p -= o;
+		return 0;
+	}
+
+	count = *isize_p;
+	if (count || lf_to_crlf->has_held) {
+		size_t i;
+		int was_cr = 0;
+
+		if (lf_to_crlf->has_held) {
+			was_cr = 1;
+			lf_to_crlf->has_held = 0;
+		}
+
+		for (i = 0; o < *osize_p && i < count; i++) {
+			char ch = input[i];
+
+			if (ch == '\n') {
+				output[o++] = '\r';
+			} else if (was_cr) {
+				/*
+				 * Previous round saw CR and it is not followed
+				 * by a LF; emit the CR before processing the
+				 * current character.
+				 */
+				output[o++] = '\r';
+			}
+
+			/*
+			 * We may have consumed the last output slot,
+			 * in which case we need to break out of this
+			 * loop; hold the current character before
+			 * returning.
+			 */
+			if (*osize_p <= o) {
+				lf_to_crlf->has_held = 1;
+				lf_to_crlf->held = ch;
+				continue; /* break but increment i */
+			}
+
+			if (ch == '\r') {
+				was_cr = 1;
+				continue;
+			}
+
+			was_cr = 0;
+			output[o++] = ch;
+		}
+
+		*osize_p -= o;
+		*isize_p -= i;
+
+		if (!lf_to_crlf->has_held && was_cr) {
+			lf_to_crlf->has_held = 1;
+			lf_to_crlf->held = '\r';
+		}
+	}
+	return 0;
+}
+
+static void lf_to_crlf_free_fn(struct stream_filter *filter)
+{
+	free(filter);
+}
+
+static struct stream_filter_vtbl lf_to_crlf_vtbl = {
+	lf_to_crlf_filter_fn,
+	lf_to_crlf_free_fn,
+};
+
+static struct stream_filter *lf_to_crlf_filter(void)
+{
+	struct lf_to_crlf_filter *lf_to_crlf = xcalloc(1, sizeof(*lf_to_crlf));
+
+	lf_to_crlf->filter.vtbl = &lf_to_crlf_vtbl;
+	return (struct stream_filter *)lf_to_crlf;
+}
+
+/*
+ * Cascade filter
+ */
+#define FILTER_BUFFER 1024
+struct cascade_filter {
+	struct stream_filter filter;
+	struct stream_filter *one;
+	struct stream_filter *two;
+	char buf[FILTER_BUFFER];
+	int end, ptr;
+};
+
+static int cascade_filter_fn(struct stream_filter *filter,
+			     const char *input, size_t *isize_p,
+			     char *output, size_t *osize_p)
+{
+	struct cascade_filter *cas = (struct cascade_filter *) filter;
+	size_t filled = 0;
+	size_t sz = *osize_p;
+	size_t to_feed, remaining;
+
+	/*
+	 * input -- (one) --> buf -- (two) --> output
+	 */
+	while (filled < sz) {
+		remaining = sz - filled;
+
+		/* do we already have something to feed two with? */
+		if (cas->ptr < cas->end) {
+			to_feed = cas->end - cas->ptr;
+			if (stream_filter(cas->two,
+					  cas->buf + cas->ptr, &to_feed,
+					  output + filled, &remaining))
+				return -1;
+			cas->ptr += (cas->end - cas->ptr) - to_feed;
+			filled = sz - remaining;
+			continue;
+		}
+
+		/* feed one from upstream and have it emit into our buffer */
+		to_feed = input ? *isize_p : 0;
+		if (input && !to_feed)
+			break;
+		remaining = sizeof(cas->buf);
+		if (stream_filter(cas->one,
+				  input, &to_feed,
+				  cas->buf, &remaining))
+			return -1;
+		cas->end = sizeof(cas->buf) - remaining;
+		cas->ptr = 0;
+		if (input) {
+			size_t fed = *isize_p - to_feed;
+			*isize_p -= fed;
+			input += fed;
+		}
+
+		/* do we know that we drained one completely? */
+		if (input || cas->end)
+			continue;
+
+		/* tell two to drain; we have nothing more to give it */
+		to_feed = 0;
+		remaining = sz - filled;
+		if (stream_filter(cas->two,
+				  NULL, &to_feed,
+				  output + filled, &remaining))
+			return -1;
+		if (remaining == (sz - filled))
+			break; /* completely drained two */
+		filled = sz - remaining;
+	}
+	*osize_p -= filled;
+	return 0;
+}
+
+static void cascade_free_fn(struct stream_filter *filter)
+{
+	struct cascade_filter *cas = (struct cascade_filter *)filter;
+	free_stream_filter(cas->one);
+	free_stream_filter(cas->two);
+	free(filter);
+}
+
+static struct stream_filter_vtbl cascade_vtbl = {
+	cascade_filter_fn,
+	cascade_free_fn,
+};
+
+static struct stream_filter *cascade_filter(struct stream_filter *one,
+					    struct stream_filter *two)
+{
+	struct cascade_filter *cascade;
+
+	if (!one || is_null_stream_filter(one))
+		return two;
+	if (!two || is_null_stream_filter(two))
+		return one;
+
+	cascade = xmalloc(sizeof(*cascade));
+	cascade->one = one;
+	cascade->two = two;
+	cascade->end = cascade->ptr = 0;
+	cascade->filter.vtbl = &cascade_vtbl;
+	return (struct stream_filter *)cascade;
+}
+
+/*
+ * ident filter
+ */
+#define IDENT_DRAINING (-1)
+#define IDENT_SKIPPING (-2)
+struct ident_filter {
+	struct stream_filter filter;
+	struct strbuf left;
+	int state;
+	char ident[45]; /* ": x40 $" */
+};
+
+static int is_foreign_ident(const char *str)
+{
+	int i;
+
+	if (prefixcmp(str, "$Id: "))
+		return 0;
+	for (i = 5; str[i]; i++) {
+		if (isspace(str[i]) && str[i+1] != '$')
+			return 1;
+	}
+	return 0;
+}
+
+static void ident_drain(struct ident_filter *ident, char **output_p, size_t *osize_p)
+{
+	size_t to_drain = ident->left.len;
+
+	if (*osize_p < to_drain)
+		to_drain = *osize_p;
+	if (to_drain) {
+		memcpy(*output_p, ident->left.buf, to_drain);
+		strbuf_remove(&ident->left, 0, to_drain);
+		*output_p += to_drain;
+		*osize_p -= to_drain;
+	}
+	if (!ident->left.len)
+		ident->state = 0;
+}
+
+static int ident_filter_fn(struct stream_filter *filter,
+			   const char *input, size_t *isize_p,
+			   char *output, size_t *osize_p)
+{
+	struct ident_filter *ident = (struct ident_filter *)filter;
+	static const char head[] = "$Id";
+
+	if (!input) {
+		/* drain upon eof */
+		switch (ident->state) {
+		default:
+			strbuf_add(&ident->left, head, ident->state);
+		case IDENT_SKIPPING:
+			/* fallthru */
+		case IDENT_DRAINING:
+			ident_drain(ident, &output, osize_p);
+		}
+		return 0;
+	}
+
+	while (*isize_p || (ident->state == IDENT_DRAINING)) {
+		int ch;
+
+		if (ident->state == IDENT_DRAINING) {
+			ident_drain(ident, &output, osize_p);
+			if (!*osize_p)
+				break;
+			continue;
+		}
+
+		ch = *(input++);
+		(*isize_p)--;
+
+		if (ident->state == IDENT_SKIPPING) {
+			/*
+			 * Skipping until '$' or LF, but keeping them
+			 * in case it is a foreign ident.
+			 */
+			strbuf_addch(&ident->left, ch);
+			if (ch != '\n' && ch != '$')
+				continue;
+			if (ch == '$' && !is_foreign_ident(ident->left.buf)) {
+				strbuf_setlen(&ident->left, sizeof(head) - 1);
+				strbuf_addstr(&ident->left, ident->ident);
+			}
+			ident->state = IDENT_DRAINING;
+			continue;
+		}
+
+		if (ident->state < sizeof(head) &&
+		    head[ident->state] == ch) {
+			ident->state++;
+			continue;
+		}
+
+		if (ident->state)
+			strbuf_add(&ident->left, head, ident->state);
+		if (ident->state == sizeof(head) - 1) {
+			if (ch != ':' && ch != '$') {
+				strbuf_addch(&ident->left, ch);
+				ident->state = 0;
+				continue;
+			}
+
+			if (ch == ':') {
+				strbuf_addch(&ident->left, ch);
+				ident->state = IDENT_SKIPPING;
+			} else {
+				strbuf_addstr(&ident->left, ident->ident);
+				ident->state = IDENT_DRAINING;
+			}
+			continue;
+		}
+
+		strbuf_addch(&ident->left, ch);
+		ident->state = IDENT_DRAINING;
+	}
+	return 0;
+}
+
+static void ident_free_fn(struct stream_filter *filter)
+{
+	struct ident_filter *ident = (struct ident_filter *)filter;
+	strbuf_release(&ident->left);
+	free(filter);
+}
+
+static struct stream_filter_vtbl ident_vtbl = {
+	ident_filter_fn,
+	ident_free_fn,
+};
+
+static struct stream_filter *ident_filter(const unsigned char *sha1)
+{
+	struct ident_filter *ident = xmalloc(sizeof(*ident));
+
+	sprintf(ident->ident, ": %s $", sha1_to_hex(sha1));
+	strbuf_init(&ident->left, 0);
+	ident->filter.vtbl = &ident_vtbl;
+	ident->state = 0;
+	return (struct stream_filter *)ident;
+}
+
+/*
+ * Return an appropriately constructed filter for the path, or NULL if
+ * the contents cannot be filtered without reading the whole thing
+ * in-core.
+ *
+ * Note that you would be crazy to set CRLF, smuge/clean or ident to a
+ * large binary blob you would want us not to slurp into the memory!
+ */
+struct stream_filter *get_stream_filter(const char *path, const unsigned char *sha1)
+{
+	struct conv_attrs ca;
+	enum crlf_action crlf_action;
+	struct stream_filter *filter = NULL;
+
+	convert_attrs(&ca, path);
+
+	if (ca.drv && (ca.drv->smudge || ca.drv->clean))
+		return filter;
+
+	if (ca.ident)
+		filter = ident_filter(sha1);
+
+	crlf_action = input_crlf_action(ca.crlf_action, ca.eol_attr);
+
+	if ((crlf_action == CRLF_BINARY) || (crlf_action == CRLF_INPUT) ||
+	    (crlf_action == CRLF_GUESS && auto_crlf == AUTO_CRLF_FALSE))
+		filter = cascade_filter(filter, &null_filter_singleton);
+
+	else if (output_eol(crlf_action) == EOL_CRLF &&
+		 !(crlf_action == CRLF_AUTO || crlf_action == CRLF_GUESS))
+		filter = cascade_filter(filter, lf_to_crlf_filter());
+
+	return filter;
+}
+
+void free_stream_filter(struct stream_filter *filter)
+{
+	filter->vtbl->free(filter);
+}
+
+int stream_filter(struct stream_filter *filter,
+		  const char *input, size_t *isize_p,
+		  char *output, size_t *osize_p)
+{
+	return filter->vtbl->filter(filter, input, isize_p, output, osize_p);
 }
diff --git a/convert.h b/convert.h
new file mode 100644
index 0000000..ec5fd69
--- /dev/null
+++ b/convert.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2011, Google Inc.
+ */
+#ifndef CONVERT_H
+#define CONVERT_H
+
+enum safe_crlf {
+	SAFE_CRLF_FALSE = 0,
+	SAFE_CRLF_FAIL = 1,
+	SAFE_CRLF_WARN = 2
+};
+
+extern enum safe_crlf safe_crlf;
+
+enum auto_crlf {
+	AUTO_CRLF_FALSE = 0,
+	AUTO_CRLF_TRUE = 1,
+	AUTO_CRLF_INPUT = -1
+};
+
+extern enum auto_crlf auto_crlf;
+
+enum eol {
+	EOL_UNSET,
+	EOL_CRLF,
+	EOL_LF,
+#ifdef NATIVE_CRLF
+	EOL_NATIVE = EOL_CRLF
+#else
+	EOL_NATIVE = EOL_LF
+#endif
+};
+
+extern enum eol core_eol;
+
+/* returns 1 if *dst was used */
+extern int convert_to_git(const char *path, const char *src, size_t len,
+			  struct strbuf *dst, enum safe_crlf checksafe);
+extern int convert_to_working_tree(const char *path, const char *src,
+				   size_t len, struct strbuf *dst);
+extern int renormalize_buffer(const char *path, const char *src, size_t len,
+			      struct strbuf *dst);
+static inline int would_convert_to_git(const char *path, const char *src,
+				       size_t len, enum safe_crlf checksafe)
+{
+	return convert_to_git(path, src, len, NULL, checksafe);
+}
+
+/*****************************************************************
+ *
+ * Streaming converison support
+ *
+ *****************************************************************/
+
+struct stream_filter; /* opaque */
+
+extern struct stream_filter *get_stream_filter(const char *path, const unsigned char *);
+extern void free_stream_filter(struct stream_filter *);
+extern int is_null_stream_filter(struct stream_filter *);
+
+/*
+ * Use as much input up to *isize_p and fill output up to *osize_p;
+ * update isize_p and osize_p to indicate how much buffer space was
+ * consumed and filled. Return 0 on success, non-zero on error.
+ *
+ * Some filters may need to buffer the input and look-ahead inside it
+ * to decide what to output, and they may consume more than zero bytes
+ * of input and still not produce any output. After feeding all the
+ * input, pass NULL as input and keep calling this function, to let
+ * such filters know there is no more input coming and it is time for
+ * them to produce the remaining output based on the buffered input.
+ */
+extern int stream_filter(struct stream_filter *,
+			 const char *input, size_t *isize_p,
+			 char *output, size_t *osize_p);
+
+#endif /* CONVERT_H */
diff --git a/copy.c b/copy.c
index e54d15a..a7f58fd 100644
--- a/copy.c
+++ b/copy.c
@@ -35,6 +35,19 @@
 	return 0;
 }
 
+static int copy_times(const char *dst, const char *src)
+{
+	struct stat st;
+	struct utimbuf times;
+	if (stat(src, &st) < 0)
+		return -1;
+	times.actime = st.st_atime;
+	times.modtime = st.st_mtime;
+	if (utime(dst, &times) < 0)
+		return -1;
+	return 0;
+}
+
 int copy_file(const char *dst, const char *src, int mode)
 {
 	int fdi, fdo, status;
@@ -55,3 +68,11 @@
 
 	return status;
 }
+
+int copy_file_with_time(const char *dst, const char *src, int mode)
+{
+	int status = copy_file(dst, src, mode);
+	if (!status)
+		return copy_times(dst, src);
+	return status;
+}
diff --git a/credential-cache--daemon.c b/credential-cache--daemon.c
new file mode 100644
index 0000000..390f194
--- /dev/null
+++ b/credential-cache--daemon.c
@@ -0,0 +1,269 @@
+#include "cache.h"
+#include "credential.h"
+#include "unix-socket.h"
+#include "sigchain.h"
+
+static const char *socket_path;
+
+static void cleanup_socket(void)
+{
+	if (socket_path)
+		unlink(socket_path);
+}
+
+static void cleanup_socket_on_signal(int sig)
+{
+	cleanup_socket();
+	sigchain_pop(sig);
+	raise(sig);
+}
+
+struct credential_cache_entry {
+	struct credential item;
+	unsigned long expiration;
+};
+static struct credential_cache_entry *entries;
+static int entries_nr;
+static int entries_alloc;
+
+static void cache_credential(struct credential *c, int timeout)
+{
+	struct credential_cache_entry *e;
+
+	ALLOC_GROW(entries, entries_nr + 1, entries_alloc);
+	e = &entries[entries_nr++];
+
+	/* take ownership of pointers */
+	memcpy(&e->item, c, sizeof(*c));
+	memset(c, 0, sizeof(*c));
+	e->expiration = time(NULL) + timeout;
+}
+
+static struct credential_cache_entry *lookup_credential(const struct credential *c)
+{
+	int i;
+	for (i = 0; i < entries_nr; i++) {
+		struct credential *e = &entries[i].item;
+		if (credential_match(c, e))
+			return &entries[i];
+	}
+	return NULL;
+}
+
+static void remove_credential(const struct credential *c)
+{
+	struct credential_cache_entry *e;
+
+	e = lookup_credential(c);
+	if (e)
+		e->expiration = 0;
+}
+
+static int check_expirations(void)
+{
+	static unsigned long wait_for_entry_until;
+	int i = 0;
+	unsigned long now = time(NULL);
+	unsigned long next = (unsigned long)-1;
+
+	/*
+	 * Initially give the client 30 seconds to actually contact us
+	 * and store a credential before we decide there's no point in
+	 * keeping the daemon around.
+	 */
+	if (!wait_for_entry_until)
+		wait_for_entry_until = now + 30;
+
+	while (i < entries_nr) {
+		if (entries[i].expiration <= now) {
+			entries_nr--;
+			credential_clear(&entries[i].item);
+			if (i != entries_nr)
+				memcpy(&entries[i], &entries[entries_nr], sizeof(*entries));
+			/*
+			 * Stick around 30 seconds in case a new credential
+			 * shows up (e.g., because we just removed a failed
+			 * one, and we will soon get the correct one).
+			 */
+			wait_for_entry_until = now + 30;
+		}
+		else {
+			if (entries[i].expiration < next)
+				next = entries[i].expiration;
+			i++;
+		}
+	}
+
+	if (!entries_nr) {
+		if (wait_for_entry_until <= now)
+			return 0;
+		next = wait_for_entry_until;
+	}
+
+	return next - now;
+}
+
+static int read_request(FILE *fh, struct credential *c,
+			struct strbuf *action, int *timeout) {
+	static struct strbuf item = STRBUF_INIT;
+	const char *p;
+
+	strbuf_getline(&item, fh, '\n');
+	p = skip_prefix(item.buf, "action=");
+	if (!p)
+		return error("client sent bogus action line: %s", item.buf);
+	strbuf_addstr(action, p);
+
+	strbuf_getline(&item, fh, '\n');
+	p = skip_prefix(item.buf, "timeout=");
+	if (!p)
+		return error("client sent bogus timeout line: %s", item.buf);
+	*timeout = atoi(p);
+
+	if (credential_read(c, fh) < 0)
+		return -1;
+	return 0;
+}
+
+static void serve_one_client(FILE *in, FILE *out)
+{
+	struct credential c = CREDENTIAL_INIT;
+	struct strbuf action = STRBUF_INIT;
+	int timeout = -1;
+
+	if (read_request(in, &c, &action, &timeout) < 0)
+		/* ignore error */ ;
+	else if (!strcmp(action.buf, "get")) {
+		struct credential_cache_entry *e = lookup_credential(&c);
+		if (e) {
+			fprintf(out, "username=%s\n", e->item.username);
+			fprintf(out, "password=%s\n", e->item.password);
+		}
+	}
+	else if (!strcmp(action.buf, "exit"))
+		exit(0);
+	else if (!strcmp(action.buf, "erase"))
+		remove_credential(&c);
+	else if (!strcmp(action.buf, "store")) {
+		if (timeout < 0)
+			warning("cache client didn't specify a timeout");
+		else if (!c.username || !c.password)
+			warning("cache client gave us a partial credential");
+		else {
+			remove_credential(&c);
+			cache_credential(&c, timeout);
+		}
+	}
+	else
+		warning("cache client sent unknown action: %s", action.buf);
+
+	credential_clear(&c);
+	strbuf_release(&action);
+}
+
+static int serve_cache_loop(int fd)
+{
+	struct pollfd pfd;
+	unsigned long wakeup;
+
+	wakeup = check_expirations();
+	if (!wakeup)
+		return 0;
+
+	pfd.fd = fd;
+	pfd.events = POLLIN;
+	if (poll(&pfd, 1, 1000 * wakeup) < 0) {
+		if (errno != EINTR)
+			die_errno("poll failed");
+		return 1;
+	}
+
+	if (pfd.revents & POLLIN) {
+		int client, client2;
+		FILE *in, *out;
+
+		client = accept(fd, NULL, NULL);
+		if (client < 0) {
+			warning("accept failed: %s", strerror(errno));
+			return 1;
+		}
+		client2 = dup(client);
+		if (client2 < 0) {
+			warning("dup failed: %s", strerror(errno));
+			close(client);
+			return 1;
+		}
+
+		in = xfdopen(client, "r");
+		out = xfdopen(client2, "w");
+		serve_one_client(in, out);
+		fclose(in);
+		fclose(out);
+	}
+	return 1;
+}
+
+static void serve_cache(const char *socket_path)
+{
+	int fd;
+
+	fd = unix_stream_listen(socket_path);
+	if (fd < 0)
+		die_errno("unable to bind to '%s'", socket_path);
+
+	printf("ok\n");
+	fclose(stdout);
+
+	while (serve_cache_loop(fd))
+		; /* nothing */
+
+	close(fd);
+	unlink(socket_path);
+}
+
+static const char permissions_advice[] =
+"The permissions on your socket directory are too loose; other\n"
+"users may be able to read your cached credentials. Consider running:\n"
+"\n"
+"	chmod 0700 %s";
+static void check_socket_directory(const char *path)
+{
+	struct stat st;
+	char *path_copy = xstrdup(path);
+	char *dir = dirname(path_copy);
+
+	if (!stat(dir, &st)) {
+		if (st.st_mode & 077)
+			die(permissions_advice, dir);
+		free(path_copy);
+		return;
+	}
+
+	/*
+	 * We must be sure to create the directory with the correct mode,
+	 * not just chmod it after the fact; otherwise, there is a race
+	 * condition in which somebody can chdir to it, sleep, then try to open
+	 * our protected socket.
+	 */
+	if (safe_create_leading_directories_const(dir) < 0)
+		die_errno("unable to create directories for '%s'", dir);
+	if (mkdir(dir, 0700) < 0)
+		die_errno("unable to mkdir '%s'", dir);
+	free(path_copy);
+}
+
+int main(int argc, const char **argv)
+{
+	socket_path = argv[1];
+
+	if (!socket_path)
+		die("usage: git-credential-cache--daemon <socket_path>");
+	check_socket_directory(socket_path);
+
+	atexit(cleanup_socket);
+	sigchain_push_common(cleanup_socket_on_signal);
+
+	serve_cache(socket_path);
+
+	return 0;
+}
diff --git a/credential-cache.c b/credential-cache.c
new file mode 100644
index 0000000..9a03792
--- /dev/null
+++ b/credential-cache.c
@@ -0,0 +1,123 @@
+#include "cache.h"
+#include "credential.h"
+#include "string-list.h"
+#include "parse-options.h"
+#include "unix-socket.h"
+#include "run-command.h"
+
+#define FLAG_SPAWN 0x1
+#define FLAG_RELAY 0x2
+
+static int send_request(const char *socket, const struct strbuf *out)
+{
+	int got_data = 0;
+	int fd = unix_stream_connect(socket);
+
+	if (fd < 0)
+		return -1;
+
+	if (write_in_full(fd, out->buf, out->len) < 0)
+		die_errno("unable to write to cache daemon");
+	shutdown(fd, SHUT_WR);
+
+	while (1) {
+		char in[1024];
+		int r;
+
+		r = read_in_full(fd, in, sizeof(in));
+		if (r == 0)
+			break;
+		if (r < 0)
+			die_errno("read error from cache daemon");
+		write_or_die(1, in, r);
+		got_data = 1;
+	}
+	return got_data;
+}
+
+static void spawn_daemon(const char *socket)
+{
+	struct child_process daemon;
+	const char *argv[] = { NULL, NULL, NULL };
+	char buf[128];
+	int r;
+
+	memset(&daemon, 0, sizeof(daemon));
+	argv[0] = "git-credential-cache--daemon";
+	argv[1] = socket;
+	daemon.argv = argv;
+	daemon.no_stdin = 1;
+	daemon.out = -1;
+
+	if (start_command(&daemon))
+		die_errno("unable to start cache daemon");
+	r = read_in_full(daemon.out, buf, sizeof(buf));
+	if (r < 0)
+		die_errno("unable to read result code from cache daemon");
+	if (r != 3 || memcmp(buf, "ok\n", 3))
+		die("cache daemon did not start: %.*s", r, buf);
+	close(daemon.out);
+}
+
+static void do_cache(const char *socket, const char *action, int timeout,
+		     int flags)
+{
+	struct strbuf buf = STRBUF_INIT;
+
+	strbuf_addf(&buf, "action=%s\n", action);
+	strbuf_addf(&buf, "timeout=%d\n", timeout);
+	if (flags & FLAG_RELAY) {
+		if (strbuf_read(&buf, 0, 0) < 0)
+			die_errno("unable to relay credential");
+	}
+
+	if (send_request(socket, &buf) < 0) {
+		if (errno != ENOENT && errno != ECONNREFUSED)
+			die_errno("unable to connect to cache daemon");
+		if (flags & FLAG_SPAWN) {
+			spawn_daemon(socket);
+			if (send_request(socket, &buf) < 0)
+				die_errno("unable to connect to cache daemon");
+		}
+	}
+	strbuf_release(&buf);
+}
+
+int main(int argc, const char **argv)
+{
+	char *socket_path = NULL;
+	int timeout = 900;
+	const char *op;
+	const char * const usage[] = {
+		"git credential-cache [options] <action>",
+		NULL
+	};
+	struct option options[] = {
+		OPT_INTEGER(0, "timeout", &timeout,
+			    "number of seconds to cache credentials"),
+		OPT_STRING(0, "socket", &socket_path, "path",
+			   "path of cache-daemon socket"),
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, NULL, options, usage, 0);
+	if (!argc)
+		usage_with_options(usage, options);
+	op = argv[0];
+
+	if (!socket_path)
+		socket_path = expand_user_path("~/.git-credential-cache/socket");
+	if (!socket_path)
+		die("unable to find a suitable socket path; use --socket");
+
+	if (!strcmp(op, "exit"))
+		do_cache(socket_path, op, timeout, 0);
+	else if (!strcmp(op, "get") || !strcmp(op, "erase"))
+		do_cache(socket_path, op, timeout, FLAG_RELAY);
+	else if (!strcmp(op, "store"))
+		do_cache(socket_path, op, timeout, FLAG_RELAY|FLAG_SPAWN);
+	else
+		; /* ignore unknown operation */
+
+	return 0;
+}
diff --git a/credential-store.c b/credential-store.c
new file mode 100644
index 0000000..26f7589
--- /dev/null
+++ b/credential-store.c
@@ -0,0 +1,157 @@
+#include "cache.h"
+#include "credential.h"
+#include "string-list.h"
+#include "parse-options.h"
+
+static struct lock_file credential_lock;
+
+static void parse_credential_file(const char *fn,
+				  struct credential *c,
+				  void (*match_cb)(struct credential *),
+				  void (*other_cb)(struct strbuf *))
+{
+	FILE *fh;
+	struct strbuf line = STRBUF_INIT;
+	struct credential entry = CREDENTIAL_INIT;
+
+	fh = fopen(fn, "r");
+	if (!fh) {
+		if (errno != ENOENT)
+			die_errno("unable to open %s", fn);
+		return;
+	}
+
+	while (strbuf_getline(&line, fh, '\n') != EOF) {
+		credential_from_url(&entry, line.buf);
+		if (entry.username && entry.password &&
+		    credential_match(c, &entry)) {
+			if (match_cb) {
+				match_cb(&entry);
+				break;
+			}
+		}
+		else if (other_cb)
+			other_cb(&line);
+	}
+
+	credential_clear(&entry);
+	strbuf_release(&line);
+	fclose(fh);
+}
+
+static void print_entry(struct credential *c)
+{
+	printf("username=%s\n", c->username);
+	printf("password=%s\n", c->password);
+}
+
+static void print_line(struct strbuf *buf)
+{
+	strbuf_addch(buf, '\n');
+	write_or_die(credential_lock.fd, buf->buf, buf->len);
+}
+
+static void rewrite_credential_file(const char *fn, struct credential *c,
+				    struct strbuf *extra)
+{
+	if (hold_lock_file_for_update(&credential_lock, fn, 0) < 0)
+		die_errno("unable to get credential storage lock");
+	if (extra)
+		print_line(extra);
+	parse_credential_file(fn, c, NULL, print_line);
+	if (commit_lock_file(&credential_lock) < 0)
+		die_errno("unable to commit credential store");
+}
+
+static void store_credential(const char *fn, struct credential *c)
+{
+	struct strbuf buf = STRBUF_INIT;
+
+	/*
+	 * Sanity check that what we are storing is actually sensible.
+	 * In particular, we can't make a URL without a protocol field.
+	 * Without either a host or pathname (depending on the scheme),
+	 * we have no primary key. And without a username and password,
+	 * we are not actually storing a credential.
+	 */
+	if (!c->protocol || !(c->host || c->path) ||
+	    !c->username || !c->password)
+		return;
+
+	strbuf_addf(&buf, "%s://", c->protocol);
+	strbuf_addstr_urlencode(&buf, c->username, 1);
+	strbuf_addch(&buf, ':');
+	strbuf_addstr_urlencode(&buf, c->password, 1);
+	strbuf_addch(&buf, '@');
+	if (c->host)
+		strbuf_addstr_urlencode(&buf, c->host, 1);
+	if (c->path) {
+		strbuf_addch(&buf, '/');
+		strbuf_addstr_urlencode(&buf, c->path, 0);
+	}
+
+	rewrite_credential_file(fn, c, &buf);
+	strbuf_release(&buf);
+}
+
+static void remove_credential(const char *fn, struct credential *c)
+{
+	/*
+	 * Sanity check that we actually have something to match
+	 * against. The input we get is a restrictive pattern,
+	 * so technically a blank credential means "erase everything".
+	 * But it is too easy to accidentally send this, since it is equivalent
+	 * to empty input. So explicitly disallow it, and require that the
+	 * pattern have some actual content to match.
+	 */
+	if (c->protocol || c->host || c->path || c->username)
+		rewrite_credential_file(fn, c, NULL);
+}
+
+static int lookup_credential(const char *fn, struct credential *c)
+{
+	parse_credential_file(fn, c, print_entry, NULL);
+	return c->username && c->password;
+}
+
+int main(int argc, const char **argv)
+{
+	const char * const usage[] = {
+		"git credential-store [options] <action>",
+		NULL
+	};
+	const char *op;
+	struct credential c = CREDENTIAL_INIT;
+	char *file = NULL;
+	struct option options[] = {
+		OPT_STRING(0, "file", &file, "path",
+			   "fetch and store credentials in <path>"),
+		OPT_END()
+	};
+
+	umask(077);
+
+	argc = parse_options(argc, argv, NULL, options, usage, 0);
+	if (argc != 1)
+		usage_with_options(usage, options);
+	op = argv[0];
+
+	if (!file)
+		file = expand_user_path("~/.git-credentials");
+	if (!file)
+		die("unable to set up default path; use --file");
+
+	if (credential_read(&c, stdin) < 0)
+		die("unable to read credential");
+
+	if (!strcmp(op, "get"))
+		lookup_credential(file, &c);
+	else if (!strcmp(op, "erase"))
+		remove_credential(file, &c);
+	else if (!strcmp(op, "store"))
+		store_credential(file, &c);
+	else
+		; /* Ignore unknown operation. */
+
+	return 0;
+}
diff --git a/credential.c b/credential.c
new file mode 100644
index 0000000..e54753c
--- /dev/null
+++ b/credential.c
@@ -0,0 +1,367 @@
+#include "cache.h"
+#include "credential.h"
+#include "string-list.h"
+#include "run-command.h"
+#include "url.h"
+#include "prompt.h"
+
+void credential_init(struct credential *c)
+{
+	memset(c, 0, sizeof(*c));
+	c->helpers.strdup_strings = 1;
+}
+
+void credential_clear(struct credential *c)
+{
+	free(c->protocol);
+	free(c->host);
+	free(c->path);
+	free(c->username);
+	free(c->password);
+	string_list_clear(&c->helpers, 0);
+
+	credential_init(c);
+}
+
+int credential_match(const struct credential *want,
+		     const struct credential *have)
+{
+#define CHECK(x) (!want->x || (have->x && !strcmp(want->x, have->x)))
+	return CHECK(protocol) &&
+	       CHECK(host) &&
+	       CHECK(path) &&
+	       CHECK(username);
+#undef CHECK
+}
+
+static int credential_config_callback(const char *var, const char *value,
+				      void *data)
+{
+	struct credential *c = data;
+	const char *key, *dot;
+
+	key = skip_prefix(var, "credential.");
+	if (!key)
+		return 0;
+
+	if (!value)
+		return config_error_nonbool(var);
+
+	dot = strrchr(key, '.');
+	if (dot) {
+		struct credential want = CREDENTIAL_INIT;
+		char *url = xmemdupz(key, dot - key);
+		int matched;
+
+		credential_from_url(&want, url);
+		matched = credential_match(&want, c);
+
+		credential_clear(&want);
+		free(url);
+
+		if (!matched)
+			return 0;
+		key = dot + 1;
+	}
+
+	if (!strcmp(key, "helper"))
+		string_list_append(&c->helpers, value);
+	else if (!strcmp(key, "username")) {
+		if (!c->username)
+			c->username = xstrdup(value);
+	}
+	else if (!strcmp(key, "usehttppath"))
+		c->use_http_path = git_config_bool(var, value);
+
+	return 0;
+}
+
+static int proto_is_http(const char *s)
+{
+	if (!s)
+		return 0;
+	return !strcmp(s, "https") || !strcmp(s, "http");
+}
+
+static void credential_apply_config(struct credential *c)
+{
+	if (c->configured)
+		return;
+	git_config(credential_config_callback, c);
+	c->configured = 1;
+
+	if (!c->use_http_path && proto_is_http(c->protocol)) {
+		free(c->path);
+		c->path = NULL;
+	}
+}
+
+static void credential_describe(struct credential *c, struct strbuf *out)
+{
+	if (!c->protocol)
+		return;
+	strbuf_addf(out, "%s://", c->protocol);
+	if (c->username && *c->username)
+		strbuf_addf(out, "%s@", c->username);
+	if (c->host)
+		strbuf_addstr(out, c->host);
+	if (c->path)
+		strbuf_addf(out, "/%s", c->path);
+}
+
+static char *credential_ask_one(const char *what, struct credential *c,
+				int flags)
+{
+	struct strbuf desc = STRBUF_INIT;
+	struct strbuf prompt = STRBUF_INIT;
+	char *r;
+
+	credential_describe(c, &desc);
+	if (desc.len)
+		strbuf_addf(&prompt, "%s for '%s': ", what, desc.buf);
+	else
+		strbuf_addf(&prompt, "%s: ", what);
+
+	r = git_prompt(prompt.buf, flags);
+
+	strbuf_release(&desc);
+	strbuf_release(&prompt);
+	return xstrdup(r);
+}
+
+static void credential_getpass(struct credential *c)
+{
+	if (!c->username)
+		c->username = credential_ask_one("Username", c,
+						 PROMPT_ASKPASS|PROMPT_ECHO);
+	if (!c->password)
+		c->password = credential_ask_one("Password", c,
+						 PROMPT_ASKPASS);
+}
+
+int credential_read(struct credential *c, FILE *fp)
+{
+	struct strbuf line = STRBUF_INIT;
+
+	while (strbuf_getline(&line, fp, '\n') != EOF) {
+		char *key = line.buf;
+		char *value = strchr(key, '=');
+
+		if (!line.len)
+			break;
+
+		if (!value) {
+			warning("invalid credential line: %s", key);
+			strbuf_release(&line);
+			return -1;
+		}
+		*value++ = '\0';
+
+		if (!strcmp(key, "username")) {
+			free(c->username);
+			c->username = xstrdup(value);
+		} else if (!strcmp(key, "password")) {
+			free(c->password);
+			c->password = xstrdup(value);
+		} else if (!strcmp(key, "protocol")) {
+			free(c->protocol);
+			c->protocol = xstrdup(value);
+		} else if (!strcmp(key, "host")) {
+			free(c->host);
+			c->host = xstrdup(value);
+		} else if (!strcmp(key, "path")) {
+			free(c->path);
+			c->path = xstrdup(value);
+		} else if (!strcmp(key, "url")) {
+			credential_from_url(c, value);
+		}
+		/*
+		 * Ignore other lines; we don't know what they mean, but
+		 * this future-proofs us when later versions of git do
+		 * learn new lines, and the helpers are updated to match.
+		 */
+	}
+
+	strbuf_release(&line);
+	return 0;
+}
+
+static void credential_write_item(FILE *fp, const char *key, const char *value)
+{
+	if (!value)
+		return;
+	fprintf(fp, "%s=%s\n", key, value);
+}
+
+void credential_write(const struct credential *c, FILE *fp)
+{
+	credential_write_item(fp, "protocol", c->protocol);
+	credential_write_item(fp, "host", c->host);
+	credential_write_item(fp, "path", c->path);
+	credential_write_item(fp, "username", c->username);
+	credential_write_item(fp, "password", c->password);
+}
+
+static int run_credential_helper(struct credential *c,
+				 const char *cmd,
+				 int want_output)
+{
+	struct child_process helper;
+	const char *argv[] = { NULL, NULL };
+	FILE *fp;
+
+	memset(&helper, 0, sizeof(helper));
+	argv[0] = cmd;
+	helper.argv = argv;
+	helper.use_shell = 1;
+	helper.in = -1;
+	if (want_output)
+		helper.out = -1;
+	else
+		helper.no_stdout = 1;
+
+	if (start_command(&helper) < 0)
+		return -1;
+
+	fp = xfdopen(helper.in, "w");
+	credential_write(c, fp);
+	fclose(fp);
+
+	if (want_output) {
+		int r;
+		fp = xfdopen(helper.out, "r");
+		r = credential_read(c, fp);
+		fclose(fp);
+		if (r < 0) {
+			finish_command(&helper);
+			return -1;
+		}
+	}
+
+	if (finish_command(&helper))
+		return -1;
+	return 0;
+}
+
+static int credential_do(struct credential *c, const char *helper,
+			 const char *operation)
+{
+	struct strbuf cmd = STRBUF_INIT;
+	int r;
+
+	if (helper[0] == '!')
+		strbuf_addstr(&cmd, helper + 1);
+	else if (is_absolute_path(helper))
+		strbuf_addstr(&cmd, helper);
+	else
+		strbuf_addf(&cmd, "git credential-%s", helper);
+
+	strbuf_addf(&cmd, " %s", operation);
+	r = run_credential_helper(c, cmd.buf, !strcmp(operation, "get"));
+
+	strbuf_release(&cmd);
+	return r;
+}
+
+void credential_fill(struct credential *c)
+{
+	int i;
+
+	if (c->username && c->password)
+		return;
+
+	credential_apply_config(c);
+
+	for (i = 0; i < c->helpers.nr; i++) {
+		credential_do(c, c->helpers.items[i].string, "get");
+		if (c->username && c->password)
+			return;
+	}
+
+	credential_getpass(c);
+	if (!c->username && !c->password)
+		die("unable to get password from user");
+}
+
+void credential_approve(struct credential *c)
+{
+	int i;
+
+	if (c->approved)
+		return;
+	if (!c->username || !c->password)
+		return;
+
+	credential_apply_config(c);
+
+	for (i = 0; i < c->helpers.nr; i++)
+		credential_do(c, c->helpers.items[i].string, "store");
+	c->approved = 1;
+}
+
+void credential_reject(struct credential *c)
+{
+	int i;
+
+	credential_apply_config(c);
+
+	for (i = 0; i < c->helpers.nr; i++)
+		credential_do(c, c->helpers.items[i].string, "erase");
+
+	free(c->username);
+	c->username = NULL;
+	free(c->password);
+	c->password = NULL;
+	c->approved = 0;
+}
+
+void credential_from_url(struct credential *c, const char *url)
+{
+	const char *at, *colon, *cp, *slash, *host, *proto_end;
+
+	credential_clear(c);
+
+	/*
+	 * Match one of:
+	 *   (1) proto://<host>/...
+	 *   (2) proto://<user>@<host>/...
+	 *   (3) proto://<user>:<pass>@<host>/...
+	 */
+	proto_end = strstr(url, "://");
+	if (!proto_end)
+		return;
+	cp = proto_end + 3;
+	at = strchr(cp, '@');
+	colon = strchr(cp, ':');
+	slash = strchrnul(cp, '/');
+
+	if (!at || slash <= at) {
+		/* Case (1) */
+		host = cp;
+	}
+	else if (!colon || at <= colon) {
+		/* Case (2) */
+		c->username = url_decode_mem(cp, at - cp);
+		host = at + 1;
+	} else {
+		/* Case (3) */
+		c->username = url_decode_mem(cp, colon - cp);
+		c->password = url_decode_mem(colon + 1, at - (colon + 1));
+		host = at + 1;
+	}
+
+	if (proto_end - url > 0)
+		c->protocol = xmemdupz(url, proto_end - url);
+	if (slash - host > 0)
+		c->host = url_decode_mem(host, slash - host);
+	/* Trim leading and trailing slashes from path */
+	while (*slash == '/')
+		slash++;
+	if (*slash) {
+		char *p;
+		c->path = url_decode(slash);
+		p = c->path + strlen(c->path) - 1;
+		while (p > c->path && *p == '/')
+			*p-- = '\0';
+	}
+}
diff --git a/credential.h b/credential.h
new file mode 100644
index 0000000..0c3e85e
--- /dev/null
+++ b/credential.h
@@ -0,0 +1,34 @@
+#ifndef CREDENTIAL_H
+#define CREDENTIAL_H
+
+#include "string-list.h"
+
+struct credential {
+	struct string_list helpers;
+	unsigned approved:1,
+		 configured:1,
+		 use_http_path:1;
+
+	char *username;
+	char *password;
+	char *protocol;
+	char *host;
+	char *path;
+};
+
+#define CREDENTIAL_INIT { STRING_LIST_INIT_DUP }
+
+void credential_init(struct credential *);
+void credential_clear(struct credential *);
+
+void credential_fill(struct credential *);
+void credential_approve(struct credential *);
+void credential_reject(struct credential *);
+
+int credential_read(struct credential *, FILE *);
+void credential_write(const struct credential *, FILE *);
+void credential_from_url(struct credential *, const char *url);
+int credential_match(const struct credential *have,
+		     const struct credential *want);
+
+#endif /* CREDENTIAL_H */
diff --git a/csum-file.c b/csum-file.c
index 4d50cc5..53f5375 100644
--- a/csum-file.c
+++ b/csum-file.c
@@ -11,8 +11,20 @@
 #include "progress.h"
 #include "csum-file.h"
 
-static void flush(struct sha1file *f, void * buf, unsigned int count)
+static void flush(struct sha1file *f, void *buf, unsigned int count)
 {
+	if (0 <= f->check_fd && count)  {
+		unsigned char check_buffer[8192];
+		ssize_t ret = read_in_full(f->check_fd, check_buffer, count);
+
+		if (ret < 0)
+			die_errno("%s: sha1 file read error", f->name);
+		if (ret < count)
+			die("%s: sha1 file truncated", f->name);
+		if (memcmp(buf, check_buffer, count))
+			die("sha1 file '%s' validation error", f->name);
+	}
+
 	for (;;) {
 		int ret = xwrite(f->fd, buf, count);
 		if (ret > 0) {
@@ -59,6 +71,17 @@
 		fd = 0;
 	} else
 		fd = f->fd;
+	if (0 <= f->check_fd) {
+		char discard;
+		int cnt = read_in_full(f->check_fd, &discard, 1);
+		if (cnt < 0)
+			die_errno("%s: error when reading the tail of sha1 file",
+				  f->name);
+		if (cnt)
+			die("%s: sha1 file has trailing garbage", f->name);
+		if (close(f->check_fd))
+			die_errno("%s: sha1 file error on close", f->name);
+	}
 	free(f);
 	return fd;
 }
@@ -101,10 +124,31 @@
 	return sha1fd_throughput(fd, name, NULL);
 }
 
+struct sha1file *sha1fd_check(const char *name)
+{
+	int sink, check;
+	struct sha1file *f;
+
+	sink = open("/dev/null", O_WRONLY);
+	if (sink < 0)
+		return NULL;
+	check = open(name, O_RDONLY);
+	if (check < 0) {
+		int saved_errno = errno;
+		close(sink);
+		errno = saved_errno;
+		return NULL;
+	}
+	f = sha1fd(sink, name);
+	f->check_fd = check;
+	return f;
+}
+
 struct sha1file *sha1fd_throughput(int fd, const char *name, struct progress *tp)
 {
 	struct sha1file *f = xmalloc(sizeof(*f));
 	f->fd = fd;
+	f->check_fd = -1;
 	f->offset = 0;
 	f->total = 0;
 	f->tp = tp;
@@ -114,9 +158,29 @@
 	return f;
 }
 
+void sha1file_checkpoint(struct sha1file *f, struct sha1file_checkpoint *checkpoint)
+{
+	sha1flush(f);
+	checkpoint->offset = f->total;
+	checkpoint->ctx = f->ctx;
+}
+
+int sha1file_truncate(struct sha1file *f, struct sha1file_checkpoint *checkpoint)
+{
+	off_t offset = checkpoint->offset;
+
+	if (ftruncate(f->fd, offset) ||
+	    lseek(f->fd, offset, SEEK_SET) != offset)
+		return -1;
+	f->total = offset;
+	f->ctx = checkpoint->ctx;
+	f->offset = 0; /* sha1flush() was called in checkpoint */
+	return 0;
+}
+
 void crc32_begin(struct sha1file *f)
 {
-	f->crc32 = crc32(0, Z_NULL, 0);
+	f->crc32 = crc32(0, NULL, 0);
 	f->do_crc = 1;
 }
 
diff --git a/csum-file.h b/csum-file.h
index 294add2..3b540bd 100644
--- a/csum-file.h
+++ b/csum-file.h
@@ -6,6 +6,7 @@
 /* A SHA1-protected file */
 struct sha1file {
 	int fd;
+	int check_fd;
 	unsigned int offset;
 	git_SHA_CTX ctx;
 	off_t total;
@@ -16,11 +17,21 @@
 	unsigned char buffer[8192];
 };
 
+/* Checkpoint */
+struct sha1file_checkpoint {
+	off_t offset;
+	git_SHA_CTX ctx;
+};
+
+extern void sha1file_checkpoint(struct sha1file *, struct sha1file_checkpoint *);
+extern int sha1file_truncate(struct sha1file *, struct sha1file_checkpoint *);
+
 /* sha1close flags */
 #define CSUM_CLOSE	1
 #define CSUM_FSYNC	2
 
 extern struct sha1file *sha1fd(int fd, const char *name);
+extern struct sha1file *sha1fd_check(const char *name);
 extern struct sha1file *sha1fd_throughput(int fd, const char *name, struct progress *tp);
 extern int sha1close(struct sha1file *, unsigned char *, unsigned int);
 extern int sha1write(struct sha1file *, void *, unsigned int);
diff --git a/ctype.c b/ctype.c
index 7ee64c7..9353271 100644
--- a/ctype.c
+++ b/ctype.c
@@ -3,7 +3,7 @@
  *
  * No surprises, and works with signed and unsigned chars.
  */
-#include "cache.h"
+#include "git-compat-util.h"
 
 enum {
 	S = GIT_SPACE,
@@ -11,16 +11,53 @@
 	D = GIT_DIGIT,
 	G = GIT_GLOB_SPECIAL,	/* *, ?, [, \\ */
 	R = GIT_REGEX_SPECIAL,	/* $, (, ), +, ., ^, {, | */
+	P = GIT_PATHSPEC_MAGIC  /* other non-alnum, except for ] and } */
 };
 
 unsigned char sane_ctype[256] = {
 	0, 0, 0, 0, 0, 0, 0, 0, 0, S, S, 0, 0, S, 0, 0,		/*   0.. 15 */
 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,		/*  16.. 31 */
-	S, 0, 0, 0, R, 0, 0, 0, R, R, G, R, 0, 0, R, 0,		/*  32.. 47 */
-	D, D, D, D, D, D, D, D, D, D, 0, 0, 0, 0, 0, G,		/*  48.. 63 */
-	0, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,		/*  64.. 79 */
-	A, A, A, A, A, A, A, A, A, A, A, G, G, 0, R, 0,		/*  80.. 95 */
-	0, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,		/*  96..111 */
-	A, A, A, A, A, A, A, A, A, A, A, R, R, 0, 0, 0,		/* 112..127 */
+	S, P, P, P, R, P, P, P, R, R, G, R, P, P, R, P,		/*  32.. 47 */
+	D, D, D, D, D, D, D, D, D, D, P, P, P, P, P, G,		/*  48.. 63 */
+	P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,		/*  64.. 79 */
+	A, A, A, A, A, A, A, A, A, A, A, G, G, 0, R, P,		/*  80.. 95 */
+	P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,		/*  96..111 */
+	A, A, A, A, A, A, A, A, A, A, A, R, R, 0, P, 0,		/* 112..127 */
 	/* Nothing in the 128.. range */
 };
+
+/* For case-insensitive kwset */
+const char tolower_trans_tbl[256] = {
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+	0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+	0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+	 ' ',  '!',  '"',  '#',  '$',  '%',  '&', 0x27,
+	 '(',  ')',  '*',  '+',  ',',  '-',  '.',  '/',
+	 '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',
+	 '8',  '9',  ':',  ';',  '<',  '=',  '>',  '?',
+	 '@',  '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',  '[', 0x5c,  ']',  '^',  '_',
+	 '`',  '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',  '{',  '|',  '}',  '~', 0x7f,
+	0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+	0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+	0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+	0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+	0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+	0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+	0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+	0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+	0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+	0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+	0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+	0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+	0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+	0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+	0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
+};
diff --git a/daemon.c b/daemon.c
index 1b5ada6..ab21e66 100644
--- a/daemon.c
+++ b/daemon.c
@@ -3,8 +3,7 @@
 #include "exec_cmd.h"
 #include "run-command.h"
 #include "strbuf.h"
-
-#include <syslog.h>
+#include "string-list.h"
 
 #ifndef HOST_NAME_MAX
 #define HOST_NAME_MAX 256
@@ -14,21 +13,26 @@
 #define NI_MAXSERV 32
 #endif
 
+#ifdef NO_INITGROUPS
+#define initgroups(x, y) (0) /* nothing */
+#endif
+
 static int log_syslog;
 static int verbose;
 static int reuseaddr;
+static int informative_errors;
 
 static const char daemon_usage[] =
 "git daemon [--verbose] [--syslog] [--export-all]\n"
-"           [--timeout=n] [--init-timeout=n] [--max-connections=n]\n"
-"           [--strict-paths] [--base-path=path] [--base-path-relaxed]\n"
-"           [--user-path | --user-path=path]\n"
-"           [--interpolated-path=path]\n"
-"           [--reuseaddr] [--detach] [--pid-file=file]\n"
-"           [--[enable|disable|allow-override|forbid-override]=service]\n"
-"           [--inetd | [--listen=host_or_ipaddr] [--port=n]\n"
-"                      [--user=user [--group=group]]\n"
-"           [directory...]";
+"           [--timeout=<n>] [--init-timeout=<n>] [--max-connections=<n>]\n"
+"           [--strict-paths] [--base-path=<path>] [--base-path-relaxed]\n"
+"           [--user-path | --user-path=<path>]\n"
+"           [--interpolated-path=<path>]\n"
+"           [--reuseaddr] [--pid-file=<file>]\n"
+"           [--(enable|disable|allow-override|forbid-override)=<service>]\n"
+"           [--inetd | [--listen=<host_or_ipaddr>] [--port=<n>]\n"
+"                      [--detach] [--user=<user> [--group=<group>]]\n"
+"           [<directory>...]";
 
 /* List of acceptable pathname prefixes */
 static char **ok_paths;
@@ -68,15 +72,18 @@
 		syslog(priority, "%s", buf);
 	} else {
 		/*
-		 * Since stderr is set to linebuffered mode, the
+		 * Since stderr is set to buffered mode, the
 		 * logging of different processes will not overlap
+		 * unless they overflow the (rather big) buffers.
 		 */
 		fprintf(stderr, "[%"PRIuMAX"] ", (uintmax_t)getpid());
 		vfprintf(stderr, err, params);
 		fputc('\n', stderr);
+		fflush(stderr);
 	}
 }
 
+__attribute__((format (printf, 1, 2)))
 static void logerror(const char *err, ...)
 {
 	va_list params;
@@ -85,6 +92,7 @@
 	va_end(params);
 }
 
+__attribute__((format (printf, 1, 2)))
 static void loginfo(const char *err, ...)
 {
 	va_list params;
@@ -101,63 +109,16 @@
 	exit(1);
 }
 
-static int avoid_alias(char *p)
-{
-	int sl, ndot;
-
-	/*
-	 * This resurrects the belts and suspenders paranoia check by HPA
-	 * done in <435560F7.4080006@zytor.com> thread, now enter_repo()
-	 * does not do getcwd() based path canonicalizations.
-	 *
-	 * sl becomes true immediately after seeing '/' and continues to
-	 * be true as long as dots continue after that without intervening
-	 * non-dot character.
-	 */
-	if (!p || (*p != '/' && *p != '~'))
-		return -1;
-	sl = 1; ndot = 0;
-	p++;
-
-	while (1) {
-		char ch = *p++;
-		if (sl) {
-			if (ch == '.')
-				ndot++;
-			else if (ch == '/') {
-				if (ndot < 3)
-					/* reject //, /./ and /../ */
-					return -1;
-				ndot = 0;
-			}
-			else if (ch == 0) {
-				if (0 < ndot && ndot < 3)
-					/* reject /.$ and /..$ */
-					return -1;
-				return 0;
-			}
-			else
-				sl = ndot = 0;
-		}
-		else if (ch == 0)
-			return 0;
-		else if (ch == '/') {
-			sl = 1;
-			ndot = 0;
-		}
-	}
-}
-
-static char *path_ok(char *directory)
+static const char *path_ok(char *directory)
 {
 	static char rpath[PATH_MAX];
 	static char interp_path[PATH_MAX];
-	char *path;
+	const char *path;
 	char *dir;
 
 	dir = directory;
 
-	if (avoid_alias(dir)) {
+	if (daemon_avoid_alias(dir)) {
 		logerror("'%s': aliased", dir);
 		return NULL;
 	}
@@ -186,16 +147,14 @@
 	}
 	else if (interpolated_path && saw_extended_args) {
 		struct strbuf expanded_path = STRBUF_INIT;
-		struct strbuf_expand_dict_entry dict[] = {
-			{ "H", hostname },
-			{ "CH", canon_hostname },
-			{ "IP", ip_address },
-			{ "P", tcp_port },
-			{ "D", directory },
-			{ "%", "%" },
-			{ NULL }
-		};
+		struct strbuf_expand_dict_entry dict[6];
 
+		dict[0].placeholder = "H"; dict[0].value = hostname;
+		dict[1].placeholder = "CH"; dict[1].value = canon_hostname;
+		dict[2].placeholder = "IP"; dict[2].value = ip_address;
+		dict[3].placeholder = "P"; dict[3].value = tcp_port;
+		dict[4].placeholder = "D"; dict[4].value = directory;
+		dict[5].placeholder = NULL; dict[5].value = NULL;
 		if (*dir != '/') {
 			/* Allow only absolute */
 			logerror("'%s': Non-absolute path denied (interpolated-path active)", dir);
@@ -289,6 +248,14 @@
 	return 0;
 }
 
+static int daemon_error(const char *dir, const char *msg)
+{
+	if (!informative_errors)
+		msg = "access denied or repository not exported";
+	packet_write(1, "ERR %s: %s", msg, dir);
+	return -1;
+}
+
 static int run_service(char *dir, struct daemon_service *service)
 {
 	const char *path;
@@ -299,11 +266,11 @@
 	if (!enabled && !service->overridable) {
 		logerror("'%s': service not enabled.", service->name);
 		errno = EACCES;
-		return -1;
+		return daemon_error(dir, "service not enabled");
 	}
 
 	if (!(path = path_ok(dir)))
-		return -1;
+		return daemon_error(dir, "no such repository");
 
 	/*
 	 * Security on the cheap.
@@ -319,7 +286,7 @@
 	if (!export_all_trees && access("git-daemon-export-ok", F_OK)) {
 		logerror("'%s': repository not exported.", path);
 		errno = EACCES;
-		return -1;
+		return daemon_error(dir, "repository not exported");
 	}
 
 	if (service->overridable) {
@@ -333,7 +300,7 @@
 		logerror("'%s': service not enabled for '%s'",
 			 service->name, path);
 		errno = EACCES;
-		return -1;
+		return daemon_error(dir, "service not enabled");
 	}
 
 	/*
@@ -389,7 +356,9 @@
 {
 	/* Timeout as string */
 	char timeout_buf[64];
-	const char *argv[] = { "upload-pack", "--strict", timeout_buf, ".", NULL };
+	const char *argv[] = { "upload-pack", "--strict", NULL, ".", NULL };
+
+	argv[2] = timeout_buf;
 
 	snprintf(timeout_buf, sizeof timeout_buf, "--timeout=%u", timeout);
 	return run_service_command(argv);
@@ -445,6 +414,33 @@
 	return dup;
 }
 
+static void parse_host_and_port(char *hostport, char **host,
+	char **port)
+{
+	if (*hostport == '[') {
+		char *end;
+
+		end = strchr(hostport, ']');
+		if (!end)
+			die("Invalid request ('[' without ']')");
+		*end = '\0';
+		*host = hostport + 1;
+		if (!end[1])
+			*port = NULL;
+		else if (end[1] == ':')
+			*port = end + 2;
+		else
+			die("Garbage after end of host part");
+	} else {
+		*host = hostport;
+		*port = strrchr(hostport, ':');
+		if (*port) {
+			**port = '\0';
+			++*port;
+		}
+	}
+}
+
 /*
  * Read the host as supplied by the client connection.
  */
@@ -461,11 +457,10 @@
 			vallen = strlen(val) + 1;
 			if (*val) {
 				/* Split <host>:<port> at colon. */
-				char *host = val;
-				char *port = strrchr(host, ':');
+				char *host;
+				char *port;
+				parse_host_and_port(val, &host, &port);
 				if (port) {
-					*port = 0;
-					port++;
 					free(tcp_port);
 					tcp_port = xstrdup(port);
 				}
@@ -534,37 +529,14 @@
 }
 
 
-static int execute(struct sockaddr *addr)
+static int execute(void)
 {
 	static char line[1000];
 	int pktlen, len, i;
+	char *addr = getenv("REMOTE_ADDR"), *port = getenv("REMOTE_PORT");
 
-	if (addr) {
-		char addrbuf[256] = "";
-		int port = -1;
-
-		if (addr->sa_family == AF_INET) {
-			struct sockaddr_in *sin_addr = (void *) addr;
-			inet_ntop(addr->sa_family, &sin_addr->sin_addr, addrbuf, sizeof(addrbuf));
-			port = ntohs(sin_addr->sin_port);
-#ifndef NO_IPV6
-		} else if (addr && addr->sa_family == AF_INET6) {
-			struct sockaddr_in6 *sin6_addr = (void *) addr;
-
-			char *buf = addrbuf;
-			*buf++ = '['; *buf = '\0'; /* stpcpy() is cool */
-			inet_ntop(AF_INET6, &sin6_addr->sin6_addr, buf, sizeof(addrbuf) - 1);
-			strcat(buf, "]");
-
-			port = ntohs(sin6_addr->sin6_port);
-#endif
-		}
-		loginfo("Connection from %s:%d", addrbuf, port);
-		setenv("REMOTE_ADDR", addrbuf, 1);
-	}
-	else {
-		unsetenv("REMOTE_ADDR");
-	}
+	if (addr)
+		loginfo("Connection from %s:%s", addr, port);
 
 	alarm(init_timeout ? init_timeout : timeout);
 	pktlen = packet_read_line(0, line, sizeof(line));
@@ -607,49 +579,52 @@
 	return -1;
 }
 
+static int addrcmp(const struct sockaddr_storage *s1,
+    const struct sockaddr_storage *s2)
+{
+	const struct sockaddr *sa1 = (const struct sockaddr*) s1;
+	const struct sockaddr *sa2 = (const struct sockaddr*) s2;
+
+	if (sa1->sa_family != sa2->sa_family)
+		return sa1->sa_family - sa2->sa_family;
+	if (sa1->sa_family == AF_INET)
+		return memcmp(&((struct sockaddr_in *)s1)->sin_addr,
+		    &((struct sockaddr_in *)s2)->sin_addr,
+		    sizeof(struct in_addr));
+#ifndef NO_IPV6
+	if (sa1->sa_family == AF_INET6)
+		return memcmp(&((struct sockaddr_in6 *)s1)->sin6_addr,
+		    &((struct sockaddr_in6 *)s2)->sin6_addr,
+		    sizeof(struct in6_addr));
+#endif
+	return 0;
+}
+
 static int max_connections = 32;
 
 static unsigned int live_children;
 
 static struct child {
 	struct child *next;
-	pid_t pid;
+	struct child_process cld;
 	struct sockaddr_storage address;
 } *firstborn;
 
-static void add_child(pid_t pid, struct sockaddr *addr, int addrlen)
+static void add_child(struct child_process *cld, struct sockaddr *addr, socklen_t addrlen)
 {
 	struct child *newborn, **cradle;
 
-	/*
-	 * This must be xcalloc() -- we'll compare the whole sockaddr_storage
-	 * but individual address may be shorter.
-	 */
 	newborn = xcalloc(1, sizeof(*newborn));
 	live_children++;
-	newborn->pid = pid;
+	memcpy(&newborn->cld, cld, sizeof(*cld));
 	memcpy(&newborn->address, addr, addrlen);
 	for (cradle = &firstborn; *cradle; cradle = &(*cradle)->next)
-		if (!memcmp(&(*cradle)->address, &newborn->address,
-			    sizeof(newborn->address)))
+		if (!addrcmp(&(*cradle)->address, &newborn->address))
 			break;
 	newborn->next = *cradle;
 	*cradle = newborn;
 }
 
-static void remove_child(pid_t pid)
-{
-	struct child **cradle, *blanket;
-
-	for (cradle = &firstborn; (blanket = *cradle); cradle = &blanket->next)
-		if (blanket->pid == pid) {
-			*cradle = blanket->next;
-			live_children--;
-			free(blanket);
-			break;
-		}
-}
-
 /*
  * This gets called if the number of connections grows
  * past "max_connections".
@@ -664,9 +639,8 @@
 		return;
 
 	for (; (next = blanket->next); blanket = next)
-		if (!memcmp(&blanket->address, &next->address,
-			    sizeof(next->address))) {
-			kill(blanket->pid, SIGTERM);
+		if (!addrcmp(&blanket->address, &next->address)) {
+			kill(blanket->cld.pid, SIGTERM);
 			break;
 		}
 }
@@ -676,18 +650,28 @@
 	int status;
 	pid_t pid;
 
-	while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
-		const char *dead = "";
-		remove_child(pid);
-		if (!WIFEXITED(status) || (WEXITSTATUS(status) > 0))
-			dead = " (with error)";
-		loginfo("[%"PRIuMAX"] Disconnected%s", (uintmax_t)pid, dead);
-	}
+	struct child **cradle, *blanket;
+	for (cradle = &firstborn; (blanket = *cradle);)
+		if ((pid = waitpid(blanket->cld.pid, &status, WNOHANG)) > 1) {
+			const char *dead = "";
+			if (status)
+				dead = " (with error)";
+			loginfo("[%"PRIuMAX"] Disconnected%s", (uintmax_t)pid, dead);
+
+			/* remove the child */
+			*cradle = blanket->next;
+			live_children--;
+			free(blanket);
+		} else
+			cradle = &blanket->next;
 }
 
-static void handle(int incoming, struct sockaddr *addr, int addrlen)
+static char **cld_argv;
+static void handle(int incoming, struct sockaddr *addr, socklen_t addrlen)
 {
-	pid_t pid;
+	struct child_process cld = { NULL };
+	char addrbuf[300] = "REMOTE_ADDR=", portbuf[300];
+	char *env[] = { addrbuf, portbuf, NULL };
 
 	if (max_connections && live_children >= max_connections) {
 		kill_some_child();
@@ -700,22 +684,37 @@
 		}
 	}
 
-	if ((pid = fork())) {
-		close(incoming);
-		if (pid < 0) {
-			logerror("Couldn't fork %s", strerror(errno));
-			return;
-		}
+	if (addr->sa_family == AF_INET) {
+		struct sockaddr_in *sin_addr = (void *) addr;
+		inet_ntop(addr->sa_family, &sin_addr->sin_addr, addrbuf + 12,
+		    sizeof(addrbuf) - 12);
+		snprintf(portbuf, sizeof(portbuf), "REMOTE_PORT=%d",
+		    ntohs(sin_addr->sin_port));
+#ifndef NO_IPV6
+	} else if (addr && addr->sa_family == AF_INET6) {
+		struct sockaddr_in6 *sin6_addr = (void *) addr;
 
-		add_child(pid, addr, addrlen);
-		return;
+		char *buf = addrbuf + 12;
+		*buf++ = '['; *buf = '\0'; /* stpcpy() is cool */
+		inet_ntop(AF_INET6, &sin6_addr->sin6_addr, buf,
+		    sizeof(addrbuf) - 13);
+		strcat(buf, "]");
+
+		snprintf(portbuf, sizeof(portbuf), "REMOTE_PORT=%d",
+		    ntohs(sin6_addr->sin6_port));
+#endif
 	}
 
-	dup2(incoming, 0);
-	dup2(incoming, 1);
-	close(incoming);
+	cld.env = (const char **)env;
+	cld.argv = (const char **)cld_argv;
+	cld.in = incoming;
+	cld.out = dup(incoming);
 
-	exit(execute(addr));
+	if (start_command(&cld))
+		logerror("unable to fork");
+	else
+		add_child(&cld, addr, addrlen);
+	close(incoming);
 }
 
 static void child_handler(int signo)
@@ -738,11 +737,40 @@
 			  &on, sizeof(on));
 }
 
+struct socketlist {
+	int *list;
+	size_t nr;
+	size_t alloc;
+};
+
+static const char *ip2str(int family, struct sockaddr *sin, socklen_t len)
+{
+#ifdef NO_IPV6
+	static char ip[INET_ADDRSTRLEN];
+#else
+	static char ip[INET6_ADDRSTRLEN];
+#endif
+
+	switch (family) {
+#ifndef NO_IPV6
+	case AF_INET6:
+		inet_ntop(family, &((struct sockaddr_in6*)sin)->sin6_addr, ip, len);
+		break;
+#endif
+	case AF_INET:
+		inet_ntop(family, &((struct sockaddr_in*)sin)->sin_addr, ip, len);
+		break;
+	default:
+		strcpy(ip, "<unknown>");
+	}
+	return ip;
+}
+
 #ifndef NO_IPV6
 
-static int socksetup(char *listen_addr, int listen_port, int **socklist_p)
+static int setup_named_sock(char *listen_addr, int listen_port, struct socketlist *socklist)
 {
-	int socknum = 0, *socklist = NULL;
+	int socknum = 0;
 	int maxfd = -1;
 	char pbuf[NI_MAXSERV];
 	struct addrinfo hints, *ai0, *ai;
@@ -757,8 +785,10 @@
 	hints.ai_flags = AI_PASSIVE;
 
 	gai = getaddrinfo(listen_addr, pbuf, &hints, &ai0);
-	if (gai)
-		die("getaddrinfo() failed: %s", gai_strerror(gai));
+	if (gai) {
+		logerror("getaddrinfo() for %s failed: %s", listen_addr, gai_strerror(gai));
+		return 0;
+	}
 
 	for (ai = ai0; ai; ai = ai->ai_next) {
 		int sockfd;
@@ -782,15 +812,22 @@
 #endif
 
 		if (set_reuse_addr(sockfd)) {
+			logerror("Could not set SO_REUSEADDR: %s", strerror(errno));
 			close(sockfd);
 			continue;
 		}
 
 		if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {
+			logerror("Could not bind to %s: %s",
+				 ip2str(ai->ai_family, ai->ai_addr, ai->ai_addrlen),
+				 strerror(errno));
 			close(sockfd);
 			continue;	/* not fatal */
 		}
 		if (listen(sockfd, 5) < 0) {
+			logerror("Could not listen to %s: %s",
+				 ip2str(ai->ai_family, ai->ai_addr, ai->ai_addrlen),
+				 strerror(errno));
 			close(sockfd);
 			continue;	/* not fatal */
 		}
@@ -799,8 +836,9 @@
 		if (flags >= 0)
 			fcntl(sockfd, F_SETFD, flags | FD_CLOEXEC);
 
-		socklist = xrealloc(socklist, sizeof(int) * (socknum + 1));
-		socklist[socknum++] = sockfd;
+		ALLOC_GROW(socklist->list, socklist->nr + 1, socklist->alloc);
+		socklist->list[socklist->nr++] = sockfd;
+		socknum++;
 
 		if (maxfd < sockfd)
 			maxfd = sockfd;
@@ -808,13 +846,12 @@
 
 	freeaddrinfo(ai0);
 
-	*socklist_p = socklist;
 	return socknum;
 }
 
 #else /* NO_IPV6 */
 
-static int socksetup(char *listen_addr, int listen_port, int **socklist_p)
+static int setup_named_sock(char *listen_addr, int listen_port, struct socketlist *socklist)
 {
 	struct sockaddr_in sin;
 	int sockfd;
@@ -837,16 +874,23 @@
 		return 0;
 
 	if (set_reuse_addr(sockfd)) {
+		logerror("Could not set SO_REUSEADDR: %s", strerror(errno));
 		close(sockfd);
 		return 0;
 	}
 
 	if ( bind(sockfd, (struct sockaddr *)&sin, sizeof sin) < 0 ) {
+		logerror("Could not listen to %s: %s",
+			 ip2str(AF_INET, (struct sockaddr *)&sin, sizeof(sin)),
+			 strerror(errno));
 		close(sockfd);
 		return 0;
 	}
 
 	if (listen(sockfd, 5) < 0) {
+		logerror("Could not listen to %s: %s",
+			 ip2str(AF_INET, (struct sockaddr *)&sin, sizeof(sin)),
+			 strerror(errno));
 		close(sockfd);
 		return 0;
 	}
@@ -855,22 +899,39 @@
 	if (flags >= 0)
 		fcntl(sockfd, F_SETFD, flags | FD_CLOEXEC);
 
-	*socklist_p = xmalloc(sizeof(int));
-	**socklist_p = sockfd;
+	ALLOC_GROW(socklist->list, socklist->nr + 1, socklist->alloc);
+	socklist->list[socklist->nr++] = sockfd;
 	return 1;
 }
 
 #endif
 
-static int service_loop(int socknum, int *socklist)
+static void socksetup(struct string_list *listen_addr, int listen_port, struct socketlist *socklist)
+{
+	if (!listen_addr->nr)
+		setup_named_sock(NULL, listen_port, socklist);
+	else {
+		int i, socknum;
+		for (i = 0; i < listen_addr->nr; i++) {
+			socknum = setup_named_sock(listen_addr->items[i].string,
+						   listen_port, socklist);
+
+			if (socknum == 0)
+				logerror("unable to allocate any listen sockets for host %s on port %u",
+					 listen_addr->items[i].string, listen_port);
+		}
+	}
+}
+
+static int service_loop(struct socketlist *socklist)
 {
 	struct pollfd *pfd;
 	int i;
 
-	pfd = xcalloc(socknum, sizeof(struct pollfd));
+	pfd = xcalloc(socklist->nr, sizeof(struct pollfd));
 
-	for (i = 0; i < socknum; i++) {
-		pfd[i].fd = socklist[i];
+	for (i = 0; i < socklist->nr; i++) {
+		pfd[i].fd = socklist->list[i];
 		pfd[i].events = POLLIN;
 	}
 
@@ -881,7 +942,7 @@
 
 		check_dead_children();
 
-		if (poll(pfd, socknum, -1) < 0) {
+		if (poll(pfd, socklist->nr, -1) < 0) {
 			if (errno != EINTR) {
 				logerror("Poll failed, resuming: %s",
 				      strerror(errno));
@@ -890,11 +951,17 @@
 			continue;
 		}
 
-		for (i = 0; i < socknum; i++) {
+		for (i = 0; i < socklist->nr; i++) {
 			if (pfd[i].revents & POLLIN) {
-				struct sockaddr_storage ss;
-				unsigned int sslen = sizeof(ss);
-				int incoming = accept(pfd[i].fd, (struct sockaddr *)&ss, &sslen);
+				union {
+					struct sockaddr sa;
+					struct sockaddr_in sai;
+#ifndef NO_IPV6
+					struct sockaddr_in6 sai6;
+#endif
+				} ss;
+				socklen_t sslen = sizeof(ss);
+				int incoming = accept(pfd[i].fd, &ss.sa, &sslen);
 				if (incoming < 0) {
 					switch (errno) {
 					case EAGAIN:
@@ -905,7 +972,7 @@
 						die_errno("accept returned");
 					}
 				}
-				handle(incoming, (struct sockaddr *)&ss, sslen);
+				handle(incoming, &ss.sa, sslen);
 			}
 		}
 	}
@@ -923,6 +990,62 @@
 		close(fd);
 }
 
+#ifdef NO_POSIX_GOODIES
+
+struct credentials;
+
+static void drop_privileges(struct credentials *cred)
+{
+	/* nothing */
+}
+
+static void daemonize(void)
+{
+	die("--detach not supported on this platform");
+}
+
+static struct credentials *prepare_credentials(const char *user_name,
+    const char *group_name)
+{
+	die("--user not supported on this platform");
+}
+
+#else
+
+struct credentials {
+	struct passwd *pass;
+	gid_t gid;
+};
+
+static void drop_privileges(struct credentials *cred)
+{
+	if (cred && (initgroups(cred->pass->pw_name, cred->gid) ||
+	    setgid (cred->gid) || setuid(cred->pass->pw_uid)))
+		die("cannot drop privileges");
+}
+
+static struct credentials *prepare_credentials(const char *user_name,
+    const char *group_name)
+{
+	static struct credentials c;
+
+	c.pass = getpwnam(user_name);
+	if (!c.pass)
+		die("user not found - %s", user_name);
+
+	if (!group_name)
+		c.gid = c.pass->pw_gid;
+	else {
+		struct group *group = getgrnam(group_name);
+		if (!group)
+			die("group not found - %s", group_name);
+
+		c.gid = group->gr_gid;
+	}
+
+	return &c;
+}
+
 static void daemonize(void)
 {
 	switch (fork()) {
@@ -940,6 +1063,7 @@
 	close(2);
 	sanitize_stdfds();
 }
+#endif
 
 static void store_pid(const char *path)
 {
@@ -950,42 +1074,42 @@
 		die_errno("failed to write pid file '%s'", path);
 }
 
-static int serve(char *listen_addr, int listen_port, struct passwd *pass, gid_t gid)
+static int serve(struct string_list *listen_addr, int listen_port,
+    struct credentials *cred)
 {
-	int socknum, *socklist;
+	struct socketlist socklist = { NULL, 0, 0 };
 
-	socknum = socksetup(listen_addr, listen_port, &socklist);
-	if (socknum == 0)
-		die("unable to allocate any listen sockets on host %s port %u",
-		    listen_addr, listen_port);
+	socksetup(listen_addr, listen_port, &socklist);
+	if (socklist.nr == 0)
+		die("unable to allocate any listen sockets on port %u",
+		    listen_port);
 
-	if (pass && gid &&
-	    (initgroups(pass->pw_name, gid) || setgid (gid) ||
-	     setuid(pass->pw_uid)))
-		die("cannot drop privileges");
+	drop_privileges(cred);
 
-	return service_loop(socknum, socklist);
+	loginfo("Ready to rumble");
+
+	return service_loop(&socklist);
 }
 
 int main(int argc, char **argv)
 {
 	int listen_port = 0;
-	char *listen_addr = NULL;
-	int inetd_mode = 0;
+	struct string_list listen_addr = STRING_LIST_INIT_NODUP;
+	int serve_mode = 0, inetd_mode = 0;
 	const char *pid_file = NULL, *user_name = NULL, *group_name = NULL;
 	int detach = 0;
-	struct passwd *pass = NULL;
-	struct group *group;
-	gid_t gid = 0;
+	struct credentials *cred = NULL;
 	int i;
 
+	git_setup_gettext();
+
 	git_extract_argv0_path(argv[0]);
 
 	for (i = 1; i < argc; i++) {
 		char *arg = argv[i];
 
 		if (!prefixcmp(arg, "--listen=")) {
-			listen_addr = xstrdup_tolower(arg + 9);
+			string_list_append(&listen_addr, xstrdup_tolower(arg + 9));
 			continue;
 		}
 		if (!prefixcmp(arg, "--port=")) {
@@ -997,6 +1121,10 @@
 				continue;
 			}
 		}
+		if (!strcmp(arg, "--serve")) {
+			serve_mode = 1;
+			continue;
+		}
 		if (!strcmp(arg, "--inetd")) {
 			inetd_mode = 1;
 			log_syslog = 1;
@@ -1089,6 +1217,14 @@
 			make_service_overridable(arg + 18, 0);
 			continue;
 		}
+		if (!prefixcmp(arg, "--informative-errors")) {
+			informative_errors = 1;
+			continue;
+		}
+		if (!prefixcmp(arg, "--no-informative-errors")) {
+			informative_errors = 0;
+			continue;
+		}
 		if (!strcmp(arg, "--")) {
 			ok_paths = &argv[i+1];
 			break;
@@ -1105,12 +1241,12 @@
 		set_die_routine(daemon_die);
 	} else
 		/* avoid splitting a message in the middle */
-		setvbuf(stderr, NULL, _IOLBF, 0);
+		setvbuf(stderr, NULL, _IOFBF, 4096);
 
-	if (inetd_mode && (group_name || user_name))
-		die("--user and --group are incompatible with --inetd");
+	if (inetd_mode && (detach || group_name || user_name))
+		die("--detach, --user and --group are incompatible with --inetd");
 
-	if (inetd_mode && (listen_port || listen_addr))
+	if (inetd_mode && (listen_port || (listen_addr.nr > 0)))
 		die("--listen= and --port= are incompatible with --inetd");
 	else if (listen_port == 0)
 		listen_port = DEFAULT_GIT_PORT;
@@ -1118,21 +1254,8 @@
 	if (group_name && !user_name)
 		die("--group supplied without --user");
 
-	if (user_name) {
-		pass = getpwnam(user_name);
-		if (!pass)
-			die("user not found - %s", user_name);
-
-		if (!group_name)
-			gid = pass->pw_gid;
-		else {
-			group = getgrnam(group_name);
-			if (!group)
-				die("group not found - %s", group_name);
-
-			gid = group->gr_gid;
-		}
-	}
+	if (user_name)
+		cred = prepare_credentials(user_name, group_name);
 
 	if (strict_paths && (!ok_paths || !*ok_paths))
 		die("option --strict-paths requires a whitelist");
@@ -1142,28 +1265,28 @@
 		    base_path);
 
 	if (inetd_mode) {
-		struct sockaddr_storage ss;
-		struct sockaddr *peer = (struct sockaddr *)&ss;
-		socklen_t slen = sizeof(ss);
-
 		if (!freopen("/dev/null", "w", stderr))
 			die_errno("failed to redirect stderr to /dev/null");
-
-		if (getpeername(0, peer, &slen))
-			peer = NULL;
-
-		return execute(peer);
 	}
 
-	if (detach) {
+	if (inetd_mode || serve_mode)
+		return execute();
+
+	if (detach)
 		daemonize();
-		loginfo("Ready to rumble");
-	}
 	else
 		sanitize_stdfds();
 
 	if (pid_file)
 		store_pid(pid_file);
 
-	return serve(listen_addr, listen_port, pass, gid);
+	/* prepare argv for serving-processes */
+	cld_argv = xmalloc(sizeof (char *) * (argc + 2));
+	cld_argv[0] = argv[0];	/* git-daemon */
+	cld_argv[1] = "--serve";
+	for (i = 1; i < argc; ++i)
+		cld_argv[i+1] = argv[i];
+	cld_argv[argc+1] = NULL;
+
+	return serve(&listen_addr, listen_port, cred);
 }
diff --git a/date.c b/date.c
index 409a17d..57331ed 100644
--- a/date.c
+++ b/date.c
@@ -9,7 +9,7 @@
 /*
  * This is like mktime, but without normalization of tm_wday and tm_yday.
  */
-time_t tm_to_time_t(const struct tm *tm)
+static time_t tm_to_time_t(const struct tm *tm)
 {
 	static const int mdays[] = {
 	    0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
@@ -24,6 +24,8 @@
 		return -1;
 	if (month < 2 || (year + 2) % 4)
 		day--;
+	if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_sec < 0)
+		return -1;
 	return (year * 365 + (year + 1) / 4 + mdays[month] + day) * 24*60*60UL +
 		tm->tm_hour * 60*60 + tm->tm_min * 60 + tm->tm_sec;
 }
@@ -84,74 +86,98 @@
 	return offset * eastwest;
 }
 
+void show_date_relative(unsigned long time, int tz,
+			       const struct timeval *now,
+			       struct strbuf *timebuf)
+{
+	unsigned long diff;
+	if (now->tv_sec < time) {
+		strbuf_addstr(timebuf, _("in the future"));
+		return;
+	}
+	diff = now->tv_sec - time;
+	if (diff < 90) {
+		strbuf_addf(timebuf,
+			 Q_("%lu second ago", "%lu seconds ago", diff), diff);
+		return;
+	}
+	/* Turn it into minutes */
+	diff = (diff + 30) / 60;
+	if (diff < 90) {
+		strbuf_addf(timebuf,
+			 Q_("%lu minute ago", "%lu minutes ago", diff), diff);
+		return;
+	}
+	/* Turn it into hours */
+	diff = (diff + 30) / 60;
+	if (diff < 36) {
+		strbuf_addf(timebuf,
+			 Q_("%lu hour ago", "%lu hours ago", diff), diff);
+		return;
+	}
+	/* We deal with number of days from here on */
+	diff = (diff + 12) / 24;
+	if (diff < 14) {
+		strbuf_addf(timebuf,
+			 Q_("%lu day ago", "%lu days ago", diff), diff);
+		return;
+	}
+	/* Say weeks for the past 10 weeks or so */
+	if (diff < 70) {
+		strbuf_addf(timebuf,
+			 Q_("%lu week ago", "%lu weeks ago", (diff + 3) / 7),
+			 (diff + 3) / 7);
+		return;
+	}
+	/* Say months for the past 12 months or so */
+	if (diff < 365) {
+		strbuf_addf(timebuf,
+			 Q_("%lu month ago", "%lu months ago", (diff + 15) / 30),
+			 (diff + 15) / 30);
+		return;
+	}
+	/* Give years and months for 5 years or so */
+	if (diff < 1825) {
+		unsigned long totalmonths = (diff * 12 * 2 + 365) / (365 * 2);
+		unsigned long years = totalmonths / 12;
+		unsigned long months = totalmonths % 12;
+		if (months) {
+			struct strbuf sb = STRBUF_INIT;
+			strbuf_addf(&sb, Q_("%lu year", "%lu years", years), years);
+			/* TRANSLATORS: "%s" is "<n> years" */
+			strbuf_addf(timebuf,
+				 Q_("%s, %lu month ago", "%s, %lu months ago", months),
+				 sb.buf, months);
+			strbuf_release(&sb);
+		} else
+			strbuf_addf(timebuf,
+				 Q_("%lu year ago", "%lu years ago", years), years);
+		return;
+	}
+	/* Otherwise, just years. Centuries is probably overkill. */
+	strbuf_addf(timebuf,
+		 Q_("%lu year ago", "%lu years ago", (diff + 183) / 365),
+		 (diff + 183) / 365);
+}
+
 const char *show_date(unsigned long time, int tz, enum date_mode mode)
 {
 	struct tm *tm;
-	static char timebuf[200];
+	static struct strbuf timebuf = STRBUF_INIT;
 
 	if (mode == DATE_RAW) {
-		snprintf(timebuf, sizeof(timebuf), "%lu %+05d", time, tz);
-		return timebuf;
+		strbuf_reset(&timebuf);
+		strbuf_addf(&timebuf, "%lu %+05d", time, tz);
+		return timebuf.buf;
 	}
 
 	if (mode == DATE_RELATIVE) {
-		unsigned long diff;
 		struct timeval now;
+
+		strbuf_reset(&timebuf);
 		gettimeofday(&now, NULL);
-		if (now.tv_sec < time)
-			return "in the future";
-		diff = now.tv_sec - time;
-		if (diff < 90) {
-			snprintf(timebuf, sizeof(timebuf), "%lu seconds ago", diff);
-			return timebuf;
-		}
-		/* Turn it into minutes */
-		diff = (diff + 30) / 60;
-		if (diff < 90) {
-			snprintf(timebuf, sizeof(timebuf), "%lu minutes ago", diff);
-			return timebuf;
-		}
-		/* Turn it into hours */
-		diff = (diff + 30) / 60;
-		if (diff < 36) {
-			snprintf(timebuf, sizeof(timebuf), "%lu hours ago", diff);
-			return timebuf;
-		}
-		/* We deal with number of days from here on */
-		diff = (diff + 12) / 24;
-		if (diff < 14) {
-			snprintf(timebuf, sizeof(timebuf), "%lu days ago", diff);
-			return timebuf;
-		}
-		/* Say weeks for the past 10 weeks or so */
-		if (diff < 70) {
-			snprintf(timebuf, sizeof(timebuf), "%lu weeks ago", (diff + 3) / 7);
-			return timebuf;
-		}
-		/* Say months for the past 12 months or so */
-		if (diff < 360) {
-			snprintf(timebuf, sizeof(timebuf), "%lu months ago", (diff + 15) / 30);
-			return timebuf;
-		}
-		/* Give years and months for 5 years or so */
-		if (diff < 1825) {
-			unsigned long years = (diff + 183) / 365;
-			unsigned long months = (diff % 365 + 15) / 30;
-			int n;
-			n = snprintf(timebuf, sizeof(timebuf), "%lu year%s",
-					years, (years > 1 ? "s" : ""));
-			if (months)
-				snprintf(timebuf + n, sizeof(timebuf) - n,
-					", %lu month%s ago",
-					months, (months > 1 ? "s" : ""));
-			else
-				snprintf(timebuf + n, sizeof(timebuf) - n,
-					" ago");
-			return timebuf;
-		}
-		/* Otherwise, just years. Centuries is probably overkill. */
-		snprintf(timebuf, sizeof(timebuf), "%lu years ago", (diff + 183) / 365);
-		return timebuf;
+		show_date_relative(time, tz, &now, &timebuf);
+		return timebuf.buf;
 	}
 
 	if (mode == DATE_LOCAL)
@@ -160,23 +186,25 @@
 	tm = time_to_tm(time, tz);
 	if (!tm)
 		return NULL;
+
+	strbuf_reset(&timebuf);
 	if (mode == DATE_SHORT)
-		sprintf(timebuf, "%04d-%02d-%02d", tm->tm_year + 1900,
+		strbuf_addf(&timebuf, "%04d-%02d-%02d", tm->tm_year + 1900,
 				tm->tm_mon + 1, tm->tm_mday);
 	else if (mode == DATE_ISO8601)
-		sprintf(timebuf, "%04d-%02d-%02d %02d:%02d:%02d %+05d",
+		strbuf_addf(&timebuf, "%04d-%02d-%02d %02d:%02d:%02d %+05d",
 				tm->tm_year + 1900,
 				tm->tm_mon + 1,
 				tm->tm_mday,
 				tm->tm_hour, tm->tm_min, tm->tm_sec,
 				tz);
 	else if (mode == DATE_RFC2822)
-		sprintf(timebuf, "%.3s, %d %.3s %d %02d:%02d:%02d %+05d",
+		strbuf_addf(&timebuf, "%.3s, %d %.3s %d %02d:%02d:%02d %+05d",
 			weekday_names[tm->tm_wday], tm->tm_mday,
 			month_names[tm->tm_mon], tm->tm_year + 1900,
 			tm->tm_hour, tm->tm_min, tm->tm_sec, tz);
 	else
-		sprintf(timebuf, "%.3s %.3s %d %02d:%02d:%02d %d%c%+05d",
+		strbuf_addf(&timebuf, "%.3s %.3s %d %02d:%02d:%02d %d%c%+05d",
 				weekday_names[tm->tm_wday],
 				month_names[tm->tm_mon],
 				tm->tm_mday,
@@ -184,7 +212,7 @@
 				tm->tm_year + 1900,
 				(mode == DATE_LOCAL) ? 0 : ' ',
 				tz);
-	return timebuf;
+	return timebuf.buf;
 }
 
 /*
@@ -219,6 +247,7 @@
 
 	{ "GMT",    0, 0, },	/* Greenwich Mean */
 	{ "UTC",    0, 0, },	/* Universal (Coordinated) */
+	{ "Z",      0, 0, },    /* Zulu, alias for UTC */
 
 	{ "WET",    0, 0, },	/* Western European */
 	{ "BST",    0, 1, },	/* British Summer */
@@ -295,7 +324,7 @@
 
 	for (i = 0; i < ARRAY_SIZE(timezone_names); i++) {
 		int match = match_string(date, timezone_names[i].name);
-		if (match >= 3) {
+		if (match >= 3 || match == strlen(timezone_names[i].name)) {
 			int off = timezone_names[i].offset;
 
 			/* This is bogus, but we like summer */
@@ -425,13 +454,19 @@
 	return end - date;
 }
 
-/* Have we filled in any part of the time/date yet? */
+/*
+ * Have we filled in any part of the time/date yet?
+ * We just do a binary 'and' to see if the sign bit
+ * is set in all the values.
+ */
 static inline int nodate(struct tm *tm)
 {
-	return tm->tm_year < 0 &&
-		tm->tm_mon < 0 &&
-		tm->tm_mday < 0 &&
-		!(tm->tm_hour | tm->tm_min | tm->tm_sec);
+	return (tm->tm_year &
+		tm->tm_mon &
+		tm->tm_mday &
+		tm->tm_hour &
+		tm->tm_min &
+		tm->tm_sec) < 0;
 }
 
 /*
@@ -525,11 +560,8 @@
 		}
 	}
 
-	if (num > 0 && num < 32) {
-		tm->tm_mday = num;
-	} else if (num > 0 && num < 13) {
+	if (num > 0 && num < 13 && tm->tm_mon < 0)
 		tm->tm_mon = num-1;
-	}
 
 	return n;
 }
@@ -537,23 +569,35 @@
 static int match_tz(const char *date, int *offp)
 {
 	char *end;
-	int offset = strtoul(date+1, &end, 10);
-	int min, hour;
-	int n = end - date - 1;
+	int hour = strtoul(date + 1, &end, 10);
+	int n = end - (date + 1);
+	int min = 0;
 
-	min = offset % 100;
-	hour = offset / 100;
+	if (n == 4) {
+		/* hhmm */
+		min = hour % 100;
+		hour = hour / 100;
+	} else if (n != 2) {
+		min = 99; /* random crap */
+	} else if (*end == ':') {
+		/* hh:mm? */
+		min = strtoul(end + 1, &end, 10);
+		if (end - (date + 1) != 5)
+			min = 99; /* random crap */
+	} /* otherwise we parsed "hh" */
 
 	/*
-	 * Don't accept any random crap.. At least 3 digits, and
-	 * a valid minute. We might want to check that the minutes
-	 * are divisible by 30 or something too.
+	 * Don't accept any random crap. Even though some places have
+	 * offset larger than 12 hours (e.g. Pacific/Kiritimati is at
+	 * UTC+14), there is something wrong if hour part is much
+	 * larger than that. We might also want to check that the
+	 * minutes are divisible by 15 or something too. (Offset of
+	 * Kathmandu, Nepal is UTC+5:45)
 	 */
-	if (min < 60 && n > 2) {
-		offset = hour*60+min;
+	if (min < 60 && hour < 24) {
+		int offset = hour * 60 + min;
 		if (*date == '-')
 			offset = -offset;
-
 		*offp = offset;
 	}
 	return end - date;
@@ -570,22 +614,61 @@
 	return snprintf(buf, len, "%lu %c%02d%02d", date, sign, offset / 60, offset % 60);
 }
 
+/*
+ * Parse a string like "0 +0000" as ancient timestamp near epoch, but
+ * only when it appears not as part of any other string.
+ */
+static int match_object_header_date(const char *date, unsigned long *timestamp, int *offset)
+{
+	char *end;
+	unsigned long stamp;
+	int ofs;
+
+	if (*date < '0' || '9' < *date)
+		return -1;
+	stamp = strtoul(date, &end, 10);
+	if (*end != ' ' || stamp == ULONG_MAX || (end[1] != '+' && end[1] != '-'))
+		return -1;
+	date = end + 2;
+	ofs = strtol(date, &end, 10);
+	if ((*end != '\0' && (*end != '\n')) || end != date + 4)
+		return -1;
+	ofs = (ofs / 100) * 60 + (ofs % 100);
+	if (date[-1] == '-')
+		ofs = -ofs;
+	*timestamp = stamp;
+	*offset = ofs;
+	return 0;
+}
+
 /* Gr. strptime is crap for this; it doesn't have a way to require RFC2822
    (i.e. English) day/month names, and it doesn't work correctly with %z. */
-int parse_date(const char *date, char *result, int maxlen)
+int parse_date_basic(const char *date, unsigned long *timestamp, int *offset)
 {
 	struct tm tm;
-	int offset, tm_gmt;
-	time_t then;
+	int tm_gmt;
+	unsigned long dummy_timestamp;
+	int dummy_offset;
+
+	if (!timestamp)
+		timestamp = &dummy_timestamp;
+	if (!offset)
+		offset = &dummy_offset;
 
 	memset(&tm, 0, sizeof(tm));
 	tm.tm_year = -1;
 	tm.tm_mon = -1;
 	tm.tm_mday = -1;
 	tm.tm_isdst = -1;
-	offset = -1;
+	tm.tm_hour = -1;
+	tm.tm_min = -1;
+	tm.tm_sec = -1;
+	*offset = -1;
 	tm_gmt = 0;
 
+	if (*date == '@' &&
+	    !match_object_header_date(date + 1, timestamp, offset))
+		return 0; /* success */
 	for (;;) {
 		int match = 0;
 		unsigned char c = *date;
@@ -595,11 +678,11 @@
 			break;
 
 		if (isalpha(c))
-			match = match_alpha(date, &tm, &offset);
+			match = match_alpha(date, &tm, offset);
 		else if (isdigit(c))
-			match = match_digit(date, &tm, &offset, &tm_gmt);
+			match = match_digit(date, &tm, offset, &tm_gmt);
 		else if ((c == '-' || c == '+') && isdigit(date[1]))
-			match = match_tz(date, &offset);
+			match = match_tz(date, offset);
 
 		if (!match) {
 			/* BAD CRAP */
@@ -610,16 +693,25 @@
 	}
 
 	/* mktime uses local timezone */
-	then = tm_to_time_t(&tm);
-	if (offset == -1)
-		offset = (then - mktime(&tm)) / 60;
+	*timestamp = tm_to_time_t(&tm);
+	if (*offset == -1)
+		*offset = ((time_t)*timestamp - mktime(&tm)) / 60;
 
-	if (then == -1)
+	if (*timestamp == -1)
 		return -1;
 
 	if (!tm_gmt)
-		then -= offset * 60;
-	return date_string(then, offset, result, maxlen);
+		*timestamp -= *offset * 60;
+	return 0; /* success */
+}
+
+int parse_date(const char *date, char *result, int maxlen)
+{
+	unsigned long timestamp;
+	int offset;
+	if (parse_date_basic(date, &timestamp, &offset))
+		return -1;
+	return date_string(timestamp, offset, result, maxlen);
 }
 
 enum date_mode parse_date_format(const char *format)
@@ -657,42 +749,64 @@
 	date_string(now, offset, buf, bufsize);
 }
 
-static void update_tm(struct tm *tm, unsigned long sec)
+/*
+ * Relative time update (eg "2 days ago").  If we haven't set the time
+ * yet, we need to set it from current time.
+ */
+static unsigned long update_tm(struct tm *tm, struct tm *now, unsigned long sec)
 {
-	time_t n = mktime(tm) - sec;
+	time_t n;
+
+	if (tm->tm_mday < 0)
+		tm->tm_mday = now->tm_mday;
+	if (tm->tm_mon < 0)
+		tm->tm_mon = now->tm_mon;
+	if (tm->tm_year < 0) {
+		tm->tm_year = now->tm_year;
+		if (tm->tm_mon > now->tm_mon)
+			tm->tm_year--;
+	}
+
+	n = mktime(tm) - sec;
 	localtime_r(&n, tm);
+	return n;
 }
 
-static void date_yesterday(struct tm *tm, int *num)
+static void date_now(struct tm *tm, struct tm *now, int *num)
 {
-	update_tm(tm, 24*60*60);
+	update_tm(tm, now, 0);
 }
 
-static void date_time(struct tm *tm, int hour)
+static void date_yesterday(struct tm *tm, struct tm *now, int *num)
+{
+	update_tm(tm, now, 24*60*60);
+}
+
+static void date_time(struct tm *tm, struct tm *now, int hour)
 {
 	if (tm->tm_hour < hour)
-		date_yesterday(tm, NULL);
+		date_yesterday(tm, now, NULL);
 	tm->tm_hour = hour;
 	tm->tm_min = 0;
 	tm->tm_sec = 0;
 }
 
-static void date_midnight(struct tm *tm, int *num)
+static void date_midnight(struct tm *tm, struct tm *now, int *num)
 {
-	date_time(tm, 0);
+	date_time(tm, now, 0);
 }
 
-static void date_noon(struct tm *tm, int *num)
+static void date_noon(struct tm *tm, struct tm *now, int *num)
 {
-	date_time(tm, 12);
+	date_time(tm, now, 12);
 }
 
-static void date_tea(struct tm *tm, int *num)
+static void date_tea(struct tm *tm, struct tm *now, int *num)
 {
-	date_time(tm, 17);
+	date_time(tm, now, 17);
 }
 
-static void date_pm(struct tm *tm, int *num)
+static void date_pm(struct tm *tm, struct tm *now, int *num)
 {
 	int hour, n = *num;
 	*num = 0;
@@ -706,7 +820,7 @@
 	tm->tm_hour = (hour % 12) + 12;
 }
 
-static void date_am(struct tm *tm, int *num)
+static void date_am(struct tm *tm, struct tm *now, int *num)
 {
 	int hour, n = *num;
 	*num = 0;
@@ -720,7 +834,7 @@
 	tm->tm_hour = (hour % 12);
 }
 
-static void date_never(struct tm *tm, int *num)
+static void date_never(struct tm *tm, struct tm *now, int *num)
 {
 	time_t n = 0;
 	localtime_r(&n, tm);
@@ -728,7 +842,7 @@
 
 static const struct special {
 	const char *name;
-	void (*fn)(struct tm *, int *);
+	void (*fn)(struct tm *, struct tm *, int *);
 } special[] = {
 	{ "yesterday", date_yesterday },
 	{ "noon", date_noon },
@@ -737,6 +851,7 @@
 	{ "PM", date_pm },
 	{ "AM", date_am },
 	{ "never", date_never },
+	{ "now", date_now },
 	{ NULL }
 };
 
@@ -757,7 +872,7 @@
 	{ NULL }
 };
 
-static const char *approxidate_alpha(const char *date, struct tm *tm, int *num)
+static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm *now, int *num, int *touched)
 {
 	const struct typelen *tl;
 	const struct special *s;
@@ -771,6 +886,7 @@
 		int match = match_string(date, month_names[i]);
 		if (match >= 3) {
 			tm->tm_mon = i;
+			*touched = 1;
 			return end;
 		}
 	}
@@ -778,7 +894,8 @@
 	for (s = special; s->name; s++) {
 		int len = strlen(s->name);
 		if (match_string(date, s->name) == len) {
-			s->fn(tm, num);
+			s->fn(tm, now, num);
+			*touched = 1;
 			return end;
 		}
 	}
@@ -788,11 +905,14 @@
 			int len = strlen(number_name[i]);
 			if (match_string(date, number_name[i]) == len) {
 				*num = i;
+				*touched = 1;
 				return end;
 			}
 		}
-		if (match_string(date, "last") == 4)
+		if (match_string(date, "last") == 4) {
 			*num = 1;
+			*touched = 1;
+		}
 		return end;
 	}
 
@@ -800,8 +920,9 @@
 	while (tl->type) {
 		int len = strlen(tl->type);
 		if (match_string(date, tl->type) >= len-1) {
-			update_tm(tm, tl->length * *num);
+			update_tm(tm, now, tl->length * *num);
 			*num = 0;
+			*touched = 1;
 			return end;
 		}
 		tl++;
@@ -818,25 +939,31 @@
 				n++;
 			diff += 7*n;
 
-			update_tm(tm, diff * 24 * 60 * 60);
+			update_tm(tm, now, diff * 24 * 60 * 60);
+			*touched = 1;
 			return end;
 		}
 	}
 
 	if (match_string(date, "months") >= 5) {
-		int n = tm->tm_mon - *num;
+		int n;
+		update_tm(tm, now, 0); /* fill in date fields if needed */
+		n = tm->tm_mon - *num;
 		*num = 0;
 		while (n < 0) {
 			n += 12;
 			tm->tm_year--;
 		}
 		tm->tm_mon = n;
+		*touched = 1;
 		return end;
 	}
 
 	if (match_string(date, "years") >= 4) {
+		update_tm(tm, now, 0); /* fill in date fields if needed */
 		tm->tm_year -= *num;
 		*num = 0;
+		*touched = 1;
 		return end;
 	}
 
@@ -866,36 +993,95 @@
 	return end;
 }
 
-unsigned long approxidate(const char *date)
+/*
+ * Do we have a pending number at the end, or when
+ * we see a new one? Let's assume it's a month day,
+ * as in "Dec 6, 1992"
+ */
+static void pending_number(struct tm *tm, int *num)
+{
+	int number = *num;
+
+	if (number) {
+		*num = 0;
+		if (tm->tm_mday < 0 && number < 32)
+			tm->tm_mday = number;
+		else if (tm->tm_mon < 0 && number < 13)
+			tm->tm_mon = number-1;
+		else if (tm->tm_year < 0) {
+			if (number > 1969 && number < 2100)
+				tm->tm_year = number - 1900;
+			else if (number > 69 && number < 100)
+				tm->tm_year = number;
+			else if (number < 38)
+				tm->tm_year = 100 + number;
+			/* We screw up for number = 00 ? */
+		}
+	}
+}
+
+static unsigned long approxidate_str(const char *date,
+				     const struct timeval *tv,
+				     int *error_ret)
 {
 	int number = 0;
+	int touched = 0;
 	struct tm tm, now;
-	struct timeval tv;
 	time_t time_sec;
-	char buffer[50];
 
-	if (parse_date(date, buffer, sizeof(buffer)) > 0)
-		return strtoul(buffer, NULL, 10);
-
-	gettimeofday(&tv, NULL);
-	time_sec = tv.tv_sec;
+	time_sec = tv->tv_sec;
 	localtime_r(&time_sec, &tm);
 	now = tm;
+
+	tm.tm_year = -1;
+	tm.tm_mon = -1;
+	tm.tm_mday = -1;
+
 	for (;;) {
 		unsigned char c = *date;
 		if (!c)
 			break;
 		date++;
 		if (isdigit(c)) {
+			pending_number(&tm, &number);
 			date = approxidate_digit(date-1, &tm, &number);
+			touched = 1;
 			continue;
 		}
 		if (isalpha(c))
-			date = approxidate_alpha(date-1, &tm, &number);
+			date = approxidate_alpha(date-1, &tm, &now, &number, &touched);
 	}
-	if (number > 0 && number < 32)
-		tm.tm_mday = number;
-	if (tm.tm_mon > now.tm_mon && tm.tm_year == now.tm_year)
-		tm.tm_year--;
-	return mktime(&tm);
+	pending_number(&tm, &number);
+	if (!touched)
+		*error_ret = 1;
+	return update_tm(&tm, &now, 0);
+}
+
+unsigned long approxidate_relative(const char *date, const struct timeval *tv)
+{
+	unsigned long timestamp;
+	int offset;
+	int errors = 0;
+
+	if (!parse_date_basic(date, &timestamp, &offset))
+		return timestamp;
+	return approxidate_str(date, tv, &errors);
+}
+
+unsigned long approxidate_careful(const char *date, int *error_ret)
+{
+	struct timeval tv;
+	unsigned long timestamp;
+	int offset;
+	int dummy = 0;
+	if (!error_ret)
+		error_ret = &dummy;
+
+	if (!parse_date_basic(date, &timestamp, &offset)) {
+		*error_ret = 0;
+		return timestamp;
+	}
+
+	gettimeofday(&tv, NULL);
+	return approxidate_str(date, &tv, error_ret);
 }
diff --git a/diff-delta.c b/diff-delta.c
index a4e28df..93385e1 100644
--- a/diff-delta.c
+++ b/diff-delta.c
@@ -4,7 +4,7 @@
  * This code was greatly inspired by parts of LibXDiff from Davide Libenzi
  * http://www.xmailserver.org/xdiff-lib.html
  *
- * Rewritten for GIT by Nicolas Pitre <nico@cam.org>, (C) 2005-2007
+ * Rewritten for GIT by Nicolas Pitre <nico@fluxnic.net>, (C) 2005-2007
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -146,7 +146,14 @@
 	/* Determine index hash size.  Note that indexing skips the
 	   first byte to allow for optimizing the Rabin's polynomial
 	   initialization in create_delta(). */
-	entries = (bufsize - 1)  / RABIN_WINDOW;
+	entries = (bufsize - 1) / RABIN_WINDOW;
+	if (bufsize >= 0xffffffffUL) {
+		/*
+		 * Current delta format can't encode offsets into
+		 * reference buffer with more than 32 bits.
+		 */
+		entries = 0xfffffffeU / RABIN_WINDOW;
+	}
 	hsize = entries / 4;
 	for (i = 4; (1u << i) < hsize && i < 31; i++);
 	hsize = 1 << i;
diff --git a/diff-lib.c b/diff-lib.c
index ad2a4cd..fc0dff3 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -10,6 +10,7 @@
 #include "cache-tree.h"
 #include "unpack-trees.h"
 #include "refs.h"
+#include "submodule.h"
 
 /*
  * diff-files
@@ -54,6 +55,33 @@
 	return 0;
 }
 
+/*
+ * Has a file changed or has a submodule new commits or a dirty work tree?
+ *
+ * Return 1 when changes are detected, 0 otherwise. If the DIRTY_SUBMODULES
+ * option is set, the caller does not only want to know if a submodule is
+ * modified at all but wants to know all the conditions that are met (new
+ * commits, untracked content and/or modified content).
+ */
+static int match_stat_with_submodule(struct diff_options *diffopt,
+				      struct cache_entry *ce, struct stat *st,
+				      unsigned ce_option, unsigned *dirty_submodule)
+{
+	int changed = ce_match_stat(ce, st, ce_option);
+	if (S_ISGITLINK(ce->ce_mode)) {
+		unsigned orig_flags = diffopt->flags;
+		if (!DIFF_OPT_TST(diffopt, OVERRIDE_SUBMODULE_CONFIG))
+			set_diffopt_flags_from_submodule_config(diffopt, ce->name);
+		if (DIFF_OPT_TST(diffopt, IGNORE_SUBMODULES))
+			changed = 0;
+		else if (!DIFF_OPT_TST(diffopt, IGNORE_DIRTY_SUBMODULES)
+		    && (!changed || DIFF_OPT_TST(diffopt, DIRTY_SUBMODULES)))
+			*dirty_submodule = is_submodule_modified(ce->name, DIFF_OPT_TST(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES));
+		diffopt->flags = orig_flags;
+	}
+	return changed;
+}
+
 int run_diff_files(struct rev_info *revs, unsigned int option)
 {
 	int entries, i;
@@ -72,16 +100,18 @@
 		unsigned int oldmode, newmode;
 		struct cache_entry *ce = active_cache[i];
 		int changed;
+		unsigned dirty_submodule = 0;
 
-		if (DIFF_OPT_TST(&revs->diffopt, QUIET) &&
-			DIFF_OPT_TST(&revs->diffopt, HAS_CHANGES))
+		if (diff_can_quit_early(&revs->diffopt))
 			break;
 
-		if (!ce_path_match(ce, revs->prune_data))
+		if (!ce_path_match(ce, &revs->prune_data))
 			continue;
 
 		if (ce_stage(ce)) {
 			struct combine_diff_path *dpath;
+			struct diff_filepair *pair;
+			unsigned int wt_mode = 0;
 			int num_compare_stages = 0;
 			size_t path_len;
 
@@ -100,7 +130,7 @@
 
 			changed = check_removed(ce, &st);
 			if (!changed)
-				dpath->mode = ce_mode_from_stat(ce, st.st_mode);
+				wt_mode = ce_mode_from_stat(ce, st.st_mode);
 			else {
 				if (changed < 0) {
 					perror(ce->name);
@@ -108,7 +138,9 @@
 				}
 				if (silent_on_removed)
 					continue;
+				wt_mode = 0;
 			}
+			dpath->mode = wt_mode;
 
 			while (i < entries) {
 				struct cache_entry *nce = active_cache[i];
@@ -154,15 +186,18 @@
 			 * Show the diff for the 'ce' if we found the one
 			 * from the desired stage.
 			 */
-			diff_unmerge(&revs->diffopt, ce->name, 0, null_sha1);
+			pair = diff_unmerge(&revs->diffopt, ce->name);
+			if (wt_mode)
+				pair->two->mode = wt_mode;
 			if (ce_stage(ce) != diff_unmerged_stage)
 				continue;
 		}
 
-		if (ce_uptodate(ce))
+		if (ce_uptodate(ce) || ce_skip_worktree(ce))
 			continue;
 
-		changed = check_removed(ce, &st);
+		/* If CE_VALID is set, don't look at workdir for file removal */
+		changed = (ce->ce_flags & CE_VALID) ? 0 : check_removed(ce, &st);
 		if (changed) {
 			if (changed < 0) {
 				perror(ce->name);
@@ -171,11 +206,12 @@
 			if (silent_on_removed)
 				continue;
 			diff_addremove(&revs->diffopt, '-', ce->ce_mode,
-				       ce->sha1, ce->name);
+				       ce->sha1, ce->name, 0);
 			continue;
 		}
-		changed = ce_match_stat(ce, &st, ce_option);
-		if (!changed) {
+		changed = match_stat_with_submodule(&revs->diffopt, ce, &st,
+						    ce_option, &dirty_submodule);
+		if (!changed && !dirty_submodule) {
 			ce_mark_uptodate(ce);
 			if (!DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER))
 				continue;
@@ -184,7 +220,7 @@
 		newmode = ce_mode_from_stat(ce, st.st_mode);
 		diff_change(&revs->diffopt, oldmode, newmode,
 			    ce->sha1, (changed ? null_sha1 : ce->sha1),
-			    ce->name);
+			    ce->name, 0, dirty_submodule);
 
 	}
 	diffcore_std(&revs->diffopt);
@@ -200,16 +236,18 @@
 static void diff_index_show_file(struct rev_info *revs,
 				 const char *prefix,
 				 struct cache_entry *ce,
-				 const unsigned char *sha1, unsigned int mode)
+				 const unsigned char *sha1, unsigned int mode,
+				 unsigned dirty_submodule)
 {
 	diff_addremove(&revs->diffopt, prefix[0], mode,
-		       sha1, ce->name);
+		       sha1, ce->name, dirty_submodule);
 }
 
 static int get_stat_data(struct cache_entry *ce,
 			 const unsigned char **sha1p,
 			 unsigned int *modep,
-			 int cached, int match_missing)
+			 int cached, int match_missing,
+			 unsigned *dirty_submodule, struct diff_options *diffopt)
 {
 	const unsigned char *sha1 = ce->sha1;
 	unsigned int mode = ce->ce_mode;
@@ -228,7 +266,8 @@
 			}
 			return -1;
 		}
-		changed = ce_match_stat(ce, &st, 0);
+		changed = match_stat_with_submodule(diffopt, ce, &st,
+						    0, dirty_submodule);
 		if (changed) {
 			mode = ce_mode_from_stat(ce, st.st_mode);
 			sha1 = null_sha1;
@@ -246,15 +285,17 @@
 {
 	const unsigned char *sha1;
 	unsigned int mode;
+	unsigned dirty_submodule = 0;
 
 	/*
 	 * New file in the index: it might actually be different in
-	 * the working copy.
+	 * the working tree.
 	 */
-	if (get_stat_data(new, &sha1, &mode, cached, match_missing) < 0)
+	if (get_stat_data(new, &sha1, &mode, cached, match_missing,
+	    &dirty_submodule, &revs->diffopt) < 0)
 		return;
 
-	diff_index_show_file(revs, "+", new, sha1, mode);
+	diff_index_show_file(revs, "+", new, sha1, mode, dirty_submodule);
 }
 
 static int show_modified(struct rev_info *revs,
@@ -265,11 +306,13 @@
 {
 	unsigned int mode, oldmode;
 	const unsigned char *sha1;
+	unsigned dirty_submodule = 0;
 
-	if (get_stat_data(new, &sha1, &mode, cached, match_missing) < 0) {
+	if (get_stat_data(new, &sha1, &mode, cached, match_missing,
+			  &dirty_submodule, &revs->diffopt) < 0) {
 		if (report_missing)
 			diff_index_show_file(revs, "-", old,
-					     old->sha1, old->ce_mode);
+					     old->sha1, old->ce_mode, 0);
 		return -1;
 	}
 
@@ -299,32 +342,16 @@
 	}
 
 	oldmode = old->ce_mode;
-	if (mode == oldmode && !hashcmp(sha1, old->sha1) &&
+	if (mode == oldmode && !hashcmp(sha1, old->sha1) && !dirty_submodule &&
 	    !DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER))
 		return 0;
 
 	diff_change(&revs->diffopt, oldmode, mode,
-		    old->sha1, sha1, old->name);
+		    old->sha1, sha1, old->name, 0, dirty_submodule);
 	return 0;
 }
 
 /*
- * This turns all merge entries into "stage 3". That guarantees that
- * when we read in the new tree (into "stage 1"), we won't lose sight
- * of the fact that we had unmerged entries.
- */
-static void mark_merge_entries(void)
-{
-	int i;
-	for (i = 0; i < active_nr; i++) {
-		struct cache_entry *ce = active_cache[i];
-		if (!ce_stage(ce))
-			continue;
-		ce->ce_flags |= CE_STAGEMASK;
-	}
-}
-
-/*
  * This gets a mix of an existing index and a tree, one pathname entry
  * at a time. The index entry may be a single stage-0 one, but it could
  * also be multiple unmerged entries (in which case idx_pos/idx_nr will
@@ -337,6 +364,9 @@
 	struct rev_info *revs = o->unpack_data;
 	int match_missing, cached;
 
+	/* if the entry is not checked out, don't examine work tree */
+	cached = o->index_only ||
+		(idx && ((idx->ce_flags & CE_VALID) || ce_skip_worktree(idx)));
 	/*
 	 * Backward compatibility wart - "diff-index -m" does
 	 * not mean "do not ignore merges", but "match_missing".
@@ -344,12 +374,13 @@
 	 * But with the revision flag parsing, that's found in
 	 * "!revs->ignore_merges".
 	 */
-	cached = o->index_only;
 	match_missing = !revs->ignore_merges;
 
 	if (cached && idx && ce_stage(idx)) {
+		struct diff_filepair *pair;
+		pair = diff_unmerge(&revs->diffopt, idx->name);
 		if (tree)
-			diff_unmerge(&revs->diffopt, idx->name, idx->ce_mode, idx->sha1);
+			fill_filespec(pair->one, tree->sha1, tree->ce_mode);
 		return;
 	}
 
@@ -365,7 +396,7 @@
 	 * Something removed from the tree?
 	 */
 	if (!idx) {
-		diff_index_show_file(revs, "-", tree, tree->sha1, tree->ce_mode);
+		diff_index_show_file(revs, "-", tree, tree->sha1, tree->ce_mode, 0);
 		return;
 	}
 
@@ -373,21 +404,6 @@
 	show_modified(revs, tree, idx, 1, cached, match_missing);
 }
 
-static inline void skip_same_name(struct cache_entry *ce, struct unpack_trees_options *o)
-{
-	int len = ce_namelen(ce);
-	const struct index_state *index = o->src_index;
-
-	while (o->pos < index->cache_nr) {
-		struct cache_entry *next = index->cache[o->pos];
-		if (len != ce_namelen(next))
-			break;
-		if (memcmp(ce->name, next->name, len))
-			break;
-		o->pos++;
-	}
-}
-
 /*
  * The unpack_trees() interface is designed for merging, so
  * the different source entries are designed primarily for
@@ -397,7 +413,7 @@
  * For diffing, the index is more important, and we only have a
  * single tree.
  *
- * We're supposed to return how many index entries we want to skip.
+ * We're supposed to advance o->pos to skip what we have already processed.
  *
  * This wrapper makes it all more readable, and takes care of all
  * the fairly complex unpack_trees() semantic requirements, including
@@ -409,9 +425,6 @@
 	struct cache_entry *tree = src[1];
 	struct rev_info *revs = o->unpack_data;
 
-	if (idx && ce_stage(idx))
-		skip_same_name(idx, o);
-
 	/*
 	 * Unpack-trees generates a DF/conflict entry if
 	 * there was a directory in the index and a tree
@@ -421,28 +434,30 @@
 	if (tree == o->df_conflict_entry)
 		tree = NULL;
 
-	if (ce_path_match(idx ? idx : tree, revs->prune_data))
+	if (ce_path_match(idx ? idx : tree, &revs->prune_data)) {
 		do_oneway_diff(o, idx, tree);
+		if (diff_can_quit_early(&revs->diffopt)) {
+			o->exiting_early = 1;
+			return -1;
+		}
+	}
 
 	return 0;
 }
 
-int run_diff_index(struct rev_info *revs, int cached)
+static int diff_cache(struct rev_info *revs,
+		      const unsigned char *tree_sha1,
+		      const char *tree_name,
+		      int cached)
 {
-	struct object *ent;
 	struct tree *tree;
-	const char *tree_name;
-	struct unpack_trees_options opts;
 	struct tree_desc t;
+	struct unpack_trees_options opts;
 
-	mark_merge_entries();
-
-	ent = revs->pending.objects[0].item;
-	tree_name = revs->pending.objects[0].name;
-	tree = parse_tree_indirect(ent->sha1);
+	tree = parse_tree_indirect(tree_sha1);
 	if (!tree)
-		return error("bad tree object %s", tree_name);
-
+		return error("bad tree object %s",
+			     tree_name ? tree_name : sha1_to_hex(tree_sha1));
 	memset(&opts, 0, sizeof(opts));
 	opts.head_idx = 1;
 	opts.index_only = cached;
@@ -453,12 +468,24 @@
 	opts.unpack_data = revs;
 	opts.src_index = &the_index;
 	opts.dst_index = NULL;
+	opts.pathspec = &revs->diffopt.pathspec;
+	opts.pathspec->recursive = 1;
+	opts.pathspec->max_depth = -1;
 
 	init_tree_desc(&t, tree->buffer, tree->size);
-	if (unpack_trees(1, &t, &opts))
+	return unpack_trees(1, &t, &opts);
+}
+
+int run_diff_index(struct rev_info *revs, int cached)
+{
+	struct object_array_entry *ent;
+
+	ent = revs->pending.objects;
+	if (diff_cache(revs, ent->item->sha1, ent->name, cached))
 		exit(128);
 
 	diff_set_mnemonic_prefix(&revs->diffopt, "c/", cached ? "i/" : "w/");
+	diffcore_fix_diff_index(&revs->diffopt);
 	diffcore_std(&revs->diffopt);
 	diff_flush(&revs->diffopt);
 	return 0;
@@ -466,53 +493,13 @@
 
 int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
 {
-	struct tree *tree;
 	struct rev_info revs;
-	int i;
-	struct cache_entry **dst;
-	struct cache_entry *last = NULL;
-	struct unpack_trees_options opts;
-	struct tree_desc t;
-
-	/*
-	 * This is used by git-blame to run diff-cache internally;
-	 * it potentially needs to repeatedly run this, so we will
-	 * start by removing the higher order entries the last round
-	 * left behind.
-	 */
-	dst = active_cache;
-	for (i = 0; i < active_nr; i++) {
-		struct cache_entry *ce = active_cache[i];
-		if (ce_stage(ce)) {
-			if (last && !strcmp(ce->name, last->name))
-				continue;
-			cache_tree_invalidate_path(active_cache_tree,
-						   ce->name);
-			last = ce;
-			ce->ce_flags |= CE_REMOVE;
-		}
-		*dst++ = ce;
-	}
-	active_nr = dst - active_cache;
 
 	init_revisions(&revs, NULL);
-	revs.prune_data = opt->paths;
-	tree = parse_tree_indirect(tree_sha1);
-	if (!tree)
-		die("bad tree object %s", sha1_to_hex(tree_sha1));
+	init_pathspec(&revs.prune_data, opt->pathspec.raw);
+	revs.diffopt = *opt;
 
-	memset(&opts, 0, sizeof(opts));
-	opts.head_idx = 1;
-	opts.index_only = 1;
-	opts.diff_index_cached = !DIFF_OPT_TST(opt, FIND_COPIES_HARDER);
-	opts.merge = 1;
-	opts.fn = oneway_diff;
-	opts.unpack_data = &revs;
-	opts.src_index = &the_index;
-	opts.dst_index = &the_index;
-
-	init_tree_desc(&t, tree->buffer, tree->size);
-	if (unpack_trees(1, &t, &opts))
+	if (diff_cache(&revs, tree_sha1, NULL, 1))
 		exit(128);
 	return 0;
 }
@@ -520,10 +507,13 @@
 int index_differs_from(const char *def, int diff_flags)
 {
 	struct rev_info rev;
+	struct setup_revision_opt opt;
 
 	init_revisions(&rev, NULL);
-	setup_revisions(0, NULL, &rev, def);
-	DIFF_OPT_SET(&rev.diffopt, QUIET);
+	memset(&opt, 0, sizeof(opt));
+	opt.def = def;
+	setup_revisions(0, NULL, &rev, &opt);
+	DIFF_OPT_SET(&rev.diffopt, QUICK);
 	DIFF_OPT_SET(&rev.diffopt, EXIT_WITH_STATUS);
 	rev.diffopt.flags |= diff_flags;
 	run_diff_index(&rev, 1);
diff --git a/diff-no-index.c b/diff-no-index.c
index 4ebc1db..7d805a0 100644
--- a/diff-no-index.c
+++ b/diff-no-index.c
@@ -26,12 +26,19 @@
 
 	while ((e = readdir(dir)))
 		if (strcmp(".", e->d_name) && strcmp("..", e->d_name))
-			string_list_insert(e->d_name, list);
+			string_list_insert(list, e->d_name);
 
 	closedir(dir);
 	return 0;
 }
 
+/*
+ * This should be "(standard input)" or something, but it will
+ * probably expose many more breakages in the way no-index code
+ * is bolted onto the diff callchain.
+ */
+static const char file_from_standard_input[] = "-";
+
 static int get_mode(const char *path, int *mode)
 {
 	struct stat st;
@@ -42,7 +49,7 @@
 	else if (!strcasecmp(path, "nul"))
 		*mode = 0;
 #endif
-	else if (!strcmp(path, "-"))
+	else if (path == file_from_standard_input)
 		*mode = create_ce_mode(0666);
 	else if (lstat(path, &st))
 		return error("Could not access '%s'", path);
@@ -51,8 +58,38 @@
 	return 0;
 }
 
+static int populate_from_stdin(struct diff_filespec *s)
+{
+	struct strbuf buf = STRBUF_INIT;
+	size_t size = 0;
+
+	if (strbuf_read(&buf, 0, 0) < 0)
+		return error("error while reading from stdin %s",
+				     strerror(errno));
+
+	s->should_munmap = 0;
+	s->data = strbuf_detach(&buf, &size);
+	s->size = size;
+	s->should_free = 1;
+	s->is_stdin = 1;
+	return 0;
+}
+
+static struct diff_filespec *noindex_filespec(const char *name, int mode)
+{
+	struct diff_filespec *s;
+
+	if (!name)
+		name = "/dev/null";
+	s = alloc_filespec(name);
+	fill_filespec(s, null_sha1, mode);
+	if (name == file_from_standard_input)
+		populate_from_stdin(s);
+	return s;
+}
+
 static int queue_diff(struct diff_options *o,
-		const char *name1, const char *name2)
+		      const char *name1, const char *name2)
 {
 	int mode1 = 0, mode2 = 0;
 
@@ -63,9 +100,12 @@
 		return error("file/directory conflict: %s, %s", name1, name2);
 
 	if (S_ISDIR(mode1) || S_ISDIR(mode2)) {
-		char buffer1[PATH_MAX], buffer2[PATH_MAX];
-		struct string_list p1 = {NULL, 0, 0, 1}, p2 = {NULL, 0, 0, 1};
-		int len1 = 0, len2 = 0, i1, i2, ret = 0;
+		struct strbuf buffer1 = STRBUF_INIT;
+		struct strbuf buffer2 = STRBUF_INIT;
+		struct string_list p1 = STRING_LIST_INIT_DUP;
+		struct string_list p2 = STRING_LIST_INIT_DUP;
+		int i1, i2, ret = 0;
+		size_t len1 = 0, len2 = 0;
 
 		if (name1 && read_directory(name1, &p1))
 			return -1;
@@ -75,53 +115,53 @@
 		}
 
 		if (name1) {
-			len1 = strlen(name1);
-			if (len1 > 0 && name1[len1 - 1] == '/')
-				len1--;
-			memcpy(buffer1, name1, len1);
-			buffer1[len1++] = '/';
+			strbuf_addstr(&buffer1, name1);
+			if (buffer1.len && buffer1.buf[buffer1.len - 1] != '/')
+				strbuf_addch(&buffer1, '/');
+			len1 = buffer1.len;
 		}
 
 		if (name2) {
-			len2 = strlen(name2);
-			if (len2 > 0 && name2[len2 - 1] == '/')
-				len2--;
-			memcpy(buffer2, name2, len2);
-			buffer2[len2++] = '/';
+			strbuf_addstr(&buffer2, name2);
+			if (buffer2.len && buffer2.buf[buffer2.len - 1] != '/')
+				strbuf_addch(&buffer2, '/');
+			len2 = buffer2.len;
 		}
 
 		for (i1 = i2 = 0; !ret && (i1 < p1.nr || i2 < p2.nr); ) {
 			const char *n1, *n2;
 			int comp;
 
+			strbuf_setlen(&buffer1, len1);
+			strbuf_setlen(&buffer2, len2);
+
 			if (i1 == p1.nr)
 				comp = 1;
 			else if (i2 == p2.nr)
 				comp = -1;
 			else
-				comp = strcmp(p1.items[i1].string,
-					p2.items[i2].string);
+				comp = strcmp(p1.items[i1].string, p2.items[i2].string);
 
 			if (comp > 0)
 				n1 = NULL;
 			else {
-				n1 = buffer1;
-				strncpy(buffer1 + len1, p1.items[i1++].string,
-						PATH_MAX - len1);
+				strbuf_addstr(&buffer1, p1.items[i1++].string);
+				n1 = buffer1.buf;
 			}
 
 			if (comp < 0)
 				n2 = NULL;
 			else {
-				n2 = buffer2;
-				strncpy(buffer2 + len2, p2.items[i2++].string,
-						PATH_MAX - len2);
+				strbuf_addstr(&buffer2, p2.items[i2++].string);
+				n2 = buffer2.buf;
 			}
 
 			ret = queue_diff(o, n1, n2);
 		}
 		string_list_clear(&p1, 0);
 		string_list_clear(&p2, 0);
+		strbuf_release(&buffer1);
+		strbuf_release(&buffer2);
 
 		return ret;
 	} else {
@@ -134,46 +174,21 @@
 			tmp_c = name1; name1 = name2; name2 = tmp_c;
 		}
 
-		if (!name1)
-			name1 = "/dev/null";
-		if (!name2)
-			name2 = "/dev/null";
-		d1 = alloc_filespec(name1);
-		d2 = alloc_filespec(name2);
-		fill_filespec(d1, null_sha1, mode1);
-		fill_filespec(d2, null_sha1, mode2);
-
+		d1 = noindex_filespec(name1, mode1);
+		d2 = noindex_filespec(name2, mode2);
 		diff_queue(&diff_queued_diff, d1, d2);
 		return 0;
 	}
 }
 
-static int path_outside_repo(const char *path)
-{
-	/*
-	 * We have already done setup_git_directory_gently() so we
-	 * know we are inside a git work tree already.
-	 */
-	const char *work_tree;
-	size_t len;
-
-	if (!is_absolute_path(path))
-		return 0;
-	work_tree = get_git_work_tree();
-	len = strlen(work_tree);
-	if (strncmp(path, work_tree, len) ||
-	    (path[len] != '\0' && path[len] != '/'))
-		return 1;
-	return 0;
-}
-
 void diff_no_index(struct rev_info *revs,
 		   int argc, const char **argv,
 		   int nongit, const char *prefix)
 {
-	int i;
+	int i, prefixlen;
 	int no_index = 0;
 	unsigned options = 0;
+	const char *paths[2];
 
 	/* Were we asked to do --no-index explicitly? */
 	for (i = 1; i < argc; i++) {
@@ -196,13 +211,13 @@
 		 * a colourful "diff" replacement.
 		 */
 		if ((argc != i + 2) ||
-		    (!path_outside_repo(argv[i]) &&
-		     !path_outside_repo(argv[i+1])))
+		    (path_inside_repo(prefix, argv[i]) &&
+		     path_inside_repo(prefix, argv[i+1])))
 			return;
 	}
 	if (argc != i + 2)
-		die("git diff %s takes two paths",
-		    no_index ? "--no-index" : "[--no-index]");
+		usagef("git diff %s <path> <path>",
+		       no_index ? "--no-index" : "[--no-index]");
 
 	diff_setup(&revs->diffopt);
 	for (i = 1; i < argc - 2; ) {
@@ -223,45 +238,33 @@
 		}
 	}
 
-	/*
-	 * If the user asked for our exit code then don't start a
-	 * pager or we would end up reporting its exit code instead.
-	 */
-	if (!DIFF_OPT_TST(&revs->diffopt, EXIT_WITH_STATUS))
-		setup_pager();
-
-	if (prefix) {
-		int len = strlen(prefix);
-
-		revs->diffopt.paths = xcalloc(2, sizeof(char *));
-		for (i = 0; i < 2; i++) {
-			const char *p = argv[argc - 2 + i];
+	prefixlen = prefix ? strlen(prefix) : 0;
+	for (i = 0; i < 2; i++) {
+		const char *p = argv[argc - 2 + i];
+		if (!strcmp(p, "-"))
 			/*
-			 * stdin should be spelled as '-'; if you have
-			 * path that is '-', spell it as ./-.
+			 * stdin should be spelled as "-"; if you have
+			 * path that is "-", spell it as "./-".
 			 */
-			p = (strcmp(p, "-")
-			     ? xstrdup(prefix_filename(prefix, len, p))
-			     : p);
-			revs->diffopt.paths[i] = p;
-		}
+			p = file_from_standard_input;
+		else if (prefixlen)
+			p = xstrdup(prefix_filename(prefix, prefixlen, p));
+		paths[i] = p;
 	}
-	else
-		revs->diffopt.paths = argv + argc - 2;
-	revs->diffopt.nr_paths = 2;
 	revs->diffopt.skip_stat_unmatch = 1;
 	if (!revs->diffopt.output_format)
 		revs->diffopt.output_format = DIFF_FORMAT_PATCH;
 
-	DIFF_OPT_SET(&revs->diffopt, EXIT_WITH_STATUS);
 	DIFF_OPT_SET(&revs->diffopt, NO_INDEX);
 
 	revs->max_count = -2;
 	if (diff_setup_done(&revs->diffopt) < 0)
 		die("diff_setup_done failed");
 
-	if (queue_diff(&revs->diffopt, revs->diffopt.paths[0],
-		       revs->diffopt.paths[1]))
+	setup_diff_pager(&revs->diffopt);
+	DIFF_OPT_SET(&revs->diffopt, EXIT_WITH_STATUS);
+
+	if (queue_diff(&revs->diffopt, paths[0], paths[1]))
 		exit(1);
 	diff_set_mnemonic_prefix(&revs->diffopt, "1/", "2/");
 	diffcore_std(&revs->diffopt);
@@ -271,5 +274,5 @@
 	 * The return code for --no-index imitates diff(1):
 	 * 0 = no changes, 1 = changes, else error
 	 */
-	exit(revs->diffopt.found_changes);
+	exit(diff_result_code(&revs->diffopt, 0));
 }
diff --git a/diff.c b/diff.c
index cd35e0c..62cbe14 100644
--- a/diff.c
+++ b/diff.c
@@ -13,6 +13,8 @@
 #include "utf8.h"
 #include "userdiff.h"
 #include "sigchain.h"
+#include "submodule.h"
+#include "ll-merge.h"
 
 #ifdef NO_FAST_WORKING_DIRECTORY
 #define FAST_WORKING_DIRECTORY 0
@@ -21,13 +23,17 @@
 #endif
 
 static int diff_detect_rename_default;
-static int diff_rename_limit_default = 200;
+static int diff_rename_limit_default = 400;
 static int diff_suppress_blank_empty;
 int diff_use_color_default = -1;
 static const char *diff_word_regex_cfg;
 static const char *external_diff_cmd_cfg;
 int diff_auto_refresh_index = 1;
 static int diff_mnemonic_prefix;
+static int diff_no_prefix;
+static int diff_stat_graph_width;
+static int diff_dirstat_permille_default = 30;
+static struct diff_options default_diff_options;
 
 static char diff_colors[][COLOR_MAXLEN] = {
 	GIT_COLOR_RESET,
@@ -38,11 +44,9 @@
 	GIT_COLOR_GREEN,	/* NEW */
 	GIT_COLOR_YELLOW,	/* COMMIT */
 	GIT_COLOR_BG_RED,	/* WHITESPACE */
+	GIT_COLOR_NORMAL,	/* FUNCINFO */
 };
 
-static void diff_filespec_load_driver(struct diff_filespec *one);
-static char *run_textconv(const char *, struct diff_filespec *, size_t *);
-
 static int parse_diff_color_slot(const char *var, int ofs)
 {
 	if (!strcasecmp(var+ofs, "plain"))
@@ -59,7 +63,61 @@
 		return DIFF_COMMIT;
 	if (!strcasecmp(var+ofs, "whitespace"))
 		return DIFF_WHITESPACE;
-	die("bad config variable '%s'", var);
+	if (!strcasecmp(var+ofs, "func"))
+		return DIFF_FUNCINFO;
+	return -1;
+}
+
+static int parse_dirstat_params(struct diff_options *options, const char *params,
+				struct strbuf *errmsg)
+{
+	const char *p = params;
+	int p_len, ret = 0;
+
+	while (*p) {
+		p_len = strchrnul(p, ',') - p;
+		if (!memcmp(p, "changes", p_len)) {
+			DIFF_OPT_CLR(options, DIRSTAT_BY_LINE);
+			DIFF_OPT_CLR(options, DIRSTAT_BY_FILE);
+		} else if (!memcmp(p, "lines", p_len)) {
+			DIFF_OPT_SET(options, DIRSTAT_BY_LINE);
+			DIFF_OPT_CLR(options, DIRSTAT_BY_FILE);
+		} else if (!memcmp(p, "files", p_len)) {
+			DIFF_OPT_CLR(options, DIRSTAT_BY_LINE);
+			DIFF_OPT_SET(options, DIRSTAT_BY_FILE);
+		} else if (!memcmp(p, "noncumulative", p_len)) {
+			DIFF_OPT_CLR(options, DIRSTAT_CUMULATIVE);
+		} else if (!memcmp(p, "cumulative", p_len)) {
+			DIFF_OPT_SET(options, DIRSTAT_CUMULATIVE);
+		} else if (isdigit(*p)) {
+			char *end;
+			int permille = strtoul(p, &end, 10) * 10;
+			if (*end == '.' && isdigit(*++end)) {
+				/* only use first digit */
+				permille += *end - '0';
+				/* .. and ignore any further digits */
+				while (isdigit(*++end))
+					; /* nothing */
+			}
+			if (end - p == p_len)
+				options->dirstat_permille = permille;
+			else {
+				strbuf_addf(errmsg, _("  Failed to parse dirstat cut-off percentage '%.*s'\n"),
+					    p_len, p);
+				ret++;
+			}
+		} else {
+			strbuf_addf(errmsg, _("  Unknown dirstat parameter '%.*s'\n"),
+				    p_len, p);
+			ret++;
+		}
+
+		p += p_len;
+
+		if (*p)
+			p++; /* more parameters, swallow separator */
+	}
+	return ret;
 }
 
 static int git_config_rename(const char *var, const char *value)
@@ -80,7 +138,7 @@
 int git_diff_ui_config(const char *var, const char *value, void *cb)
 {
 	if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
-		diff_use_color_default = git_config_colorbool(var, value, -1);
+		diff_use_color_default = git_config_colorbool(var, value);
 		return 0;
 	}
 	if (!strcmp(var, "diff.renames")) {
@@ -95,11 +153,25 @@
 		diff_mnemonic_prefix = git_config_bool(var, value);
 		return 0;
 	}
+	if (!strcmp(var, "diff.noprefix")) {
+		diff_no_prefix = git_config_bool(var, value);
+		return 0;
+	}
+	if (!strcmp(var, "diff.statgraphwidth")) {
+		diff_stat_graph_width = git_config_int(var, value);
+		return 0;
+	}
 	if (!strcmp(var, "diff.external"))
 		return git_config_string(&external_diff_cmd_cfg, var, value);
 	if (!strcmp(var, "diff.wordregex"))
 		return git_config_string(&diff_word_regex_cfg, var, value);
 
+	if (!strcmp(var, "diff.ignoresubmodules"))
+		handle_ignore_submodules_arg(&default_diff_options, value);
+
+	if (git_color_config(var, value, cb) < 0)
+		return -1;
+
 	return git_diff_basic_config(var, value, cb);
 }
 
@@ -110,14 +182,13 @@
 		return 0;
 	}
 
-	switch (userdiff_config(var, value)) {
-		case 0: break;
-		case -1: return -1;
-		default: return 0;
-	}
+	if (userdiff_config(var, value) < 0)
+		return -1;
 
 	if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) {
 		int slot = parse_diff_color_slot(var, 11);
+		if (slot < 0)
+			return 0;
 		if (!value)
 			return config_error_nonbool(var);
 		color_parse(value, var, diff_colors[slot]);
@@ -132,7 +203,21 @@
 		return 0;
 	}
 
-	return git_color_default_config(var, value, cb);
+	if (!strcmp(var, "diff.dirstat")) {
+		struct strbuf errmsg = STRBUF_INIT;
+		default_diff_options.dirstat_permille = diff_dirstat_permille_default;
+		if (parse_dirstat_params(&default_diff_options, value, &errmsg))
+			warning(_("Found errors in 'diff.dirstat' config variable:\n%s"),
+				errmsg.buf);
+		strbuf_release(&errmsg);
+		diff_dirstat_permille_default = default_diff_options.dirstat_permille;
+		return 0;
+	}
+
+	if (!prefixcmp(var, "submodule."))
+		return parse_submodule_config_option(var, value);
+
+	return git_default_config(var, value, cb);
 }
 
 static char *quote_two(const char *one, const char *two)
@@ -174,6 +259,252 @@
 	char tmp_path[PATH_MAX];
 } diff_temp[2];
 
+typedef unsigned long (*sane_truncate_fn)(char *line, unsigned long len);
+
+struct emit_callback {
+	int color_diff;
+	unsigned ws_rule;
+	int blank_at_eof_in_preimage;
+	int blank_at_eof_in_postimage;
+	int lno_in_preimage;
+	int lno_in_postimage;
+	sane_truncate_fn truncate;
+	const char **label_path;
+	struct diff_words_data *diff_words;
+	struct diff_options *opt;
+	int *found_changesp;
+	struct strbuf *header;
+};
+
+static int count_lines(const char *data, int size)
+{
+	int count, ch, completely_empty = 1, nl_just_seen = 0;
+	count = 0;
+	while (0 < size--) {
+		ch = *data++;
+		if (ch == '\n') {
+			count++;
+			nl_just_seen = 1;
+			completely_empty = 0;
+		}
+		else {
+			nl_just_seen = 0;
+			completely_empty = 0;
+		}
+	}
+	if (completely_empty)
+		return 0;
+	if (!nl_just_seen)
+		count++; /* no trailing newline */
+	return count;
+}
+
+static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
+{
+	if (!DIFF_FILE_VALID(one)) {
+		mf->ptr = (char *)""; /* does not matter */
+		mf->size = 0;
+		return 0;
+	}
+	else if (diff_populate_filespec(one, 0))
+		return -1;
+
+	mf->ptr = one->data;
+	mf->size = one->size;
+	return 0;
+}
+
+/* like fill_mmfile, but only for size, so we can avoid retrieving blob */
+static unsigned long diff_filespec_size(struct diff_filespec *one)
+{
+	if (!DIFF_FILE_VALID(one))
+		return 0;
+	diff_populate_filespec(one, 1);
+	return one->size;
+}
+
+static int count_trailing_blank(mmfile_t *mf, unsigned ws_rule)
+{
+	char *ptr = mf->ptr;
+	long size = mf->size;
+	int cnt = 0;
+
+	if (!size)
+		return cnt;
+	ptr += size - 1; /* pointing at the very end */
+	if (*ptr != '\n')
+		; /* incomplete line */
+	else
+		ptr--; /* skip the last LF */
+	while (mf->ptr < ptr) {
+		char *prev_eol;
+		for (prev_eol = ptr; mf->ptr <= prev_eol; prev_eol--)
+			if (*prev_eol == '\n')
+				break;
+		if (!ws_blank_line(prev_eol + 1, ptr - prev_eol, ws_rule))
+			break;
+		cnt++;
+		ptr = prev_eol - 1;
+	}
+	return cnt;
+}
+
+static void check_blank_at_eof(mmfile_t *mf1, mmfile_t *mf2,
+			       struct emit_callback *ecbdata)
+{
+	int l1, l2, at;
+	unsigned ws_rule = ecbdata->ws_rule;
+	l1 = count_trailing_blank(mf1, ws_rule);
+	l2 = count_trailing_blank(mf2, ws_rule);
+	if (l2 <= l1) {
+		ecbdata->blank_at_eof_in_preimage = 0;
+		ecbdata->blank_at_eof_in_postimage = 0;
+		return;
+	}
+	at = count_lines(mf1->ptr, mf1->size);
+	ecbdata->blank_at_eof_in_preimage = (at - l1) + 1;
+
+	at = count_lines(mf2->ptr, mf2->size);
+	ecbdata->blank_at_eof_in_postimage = (at - l2) + 1;
+}
+
+static void emit_line_0(struct diff_options *o, const char *set, const char *reset,
+			int first, const char *line, int len)
+{
+	int has_trailing_newline, has_trailing_carriage_return;
+	int nofirst;
+	FILE *file = o->file;
+
+	if (o->output_prefix) {
+		struct strbuf *msg = NULL;
+		msg = o->output_prefix(o, o->output_prefix_data);
+		assert(msg);
+		fwrite(msg->buf, msg->len, 1, file);
+	}
+
+	if (len == 0) {
+		has_trailing_newline = (first == '\n');
+		has_trailing_carriage_return = (!has_trailing_newline &&
+						(first == '\r'));
+		nofirst = has_trailing_newline || has_trailing_carriage_return;
+	} else {
+		has_trailing_newline = (len > 0 && line[len-1] == '\n');
+		if (has_trailing_newline)
+			len--;
+		has_trailing_carriage_return = (len > 0 && line[len-1] == '\r');
+		if (has_trailing_carriage_return)
+			len--;
+		nofirst = 0;
+	}
+
+	if (len || !nofirst) {
+		fputs(set, file);
+		if (!nofirst)
+			fputc(first, file);
+		fwrite(line, len, 1, file);
+		fputs(reset, file);
+	}
+	if (has_trailing_carriage_return)
+		fputc('\r', file);
+	if (has_trailing_newline)
+		fputc('\n', file);
+}
+
+static void emit_line(struct diff_options *o, const char *set, const char *reset,
+		      const char *line, int len)
+{
+	emit_line_0(o, set, reset, line[0], line+1, len-1);
+}
+
+static int new_blank_line_at_eof(struct emit_callback *ecbdata, const char *line, int len)
+{
+	if (!((ecbdata->ws_rule & WS_BLANK_AT_EOF) &&
+	      ecbdata->blank_at_eof_in_preimage &&
+	      ecbdata->blank_at_eof_in_postimage &&
+	      ecbdata->blank_at_eof_in_preimage <= ecbdata->lno_in_preimage &&
+	      ecbdata->blank_at_eof_in_postimage <= ecbdata->lno_in_postimage))
+		return 0;
+	return ws_blank_line(line, len, ecbdata->ws_rule);
+}
+
+static void emit_add_line(const char *reset,
+			  struct emit_callback *ecbdata,
+			  const char *line, int len)
+{
+	const char *ws = diff_get_color(ecbdata->color_diff, DIFF_WHITESPACE);
+	const char *set = diff_get_color(ecbdata->color_diff, DIFF_FILE_NEW);
+
+	if (!*ws)
+		emit_line_0(ecbdata->opt, set, reset, '+', line, len);
+	else if (new_blank_line_at_eof(ecbdata, line, len))
+		/* Blank line at EOF - paint '+' as well */
+		emit_line_0(ecbdata->opt, ws, reset, '+', line, len);
+	else {
+		/* Emit just the prefix, then the rest. */
+		emit_line_0(ecbdata->opt, set, reset, '+', "", 0);
+		ws_check_emit(line, len, ecbdata->ws_rule,
+			      ecbdata->opt->file, set, reset, ws);
+	}
+}
+
+static void emit_hunk_header(struct emit_callback *ecbdata,
+			     const char *line, int len)
+{
+	const char *plain = diff_get_color(ecbdata->color_diff, DIFF_PLAIN);
+	const char *frag = diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO);
+	const char *func = diff_get_color(ecbdata->color_diff, DIFF_FUNCINFO);
+	const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
+	static const char atat[2] = { '@', '@' };
+	const char *cp, *ep;
+	struct strbuf msgbuf = STRBUF_INIT;
+	int org_len = len;
+	int i = 1;
+
+	/*
+	 * As a hunk header must begin with "@@ -<old>, +<new> @@",
+	 * it always is at least 10 bytes long.
+	 */
+	if (len < 10 ||
+	    memcmp(line, atat, 2) ||
+	    !(ep = memmem(line + 2, len - 2, atat, 2))) {
+		emit_line(ecbdata->opt, plain, reset, line, len);
+		return;
+	}
+	ep += 2; /* skip over @@ */
+
+	/* The hunk header in fraginfo color */
+	strbuf_add(&msgbuf, frag, strlen(frag));
+	strbuf_add(&msgbuf, line, ep - line);
+	strbuf_add(&msgbuf, reset, strlen(reset));
+
+	/*
+	 * trailing "\r\n"
+	 */
+	for ( ; i < 3; i++)
+		if (line[len - i] == '\r' || line[len - i] == '\n')
+			len--;
+
+	/* blank before the func header */
+	for (cp = ep; ep - line < len; ep++)
+		if (*ep != ' ' && *ep != '\t')
+			break;
+	if (ep != cp) {
+		strbuf_add(&msgbuf, plain, strlen(plain));
+		strbuf_add(&msgbuf, cp, ep - cp);
+		strbuf_add(&msgbuf, reset, strlen(reset));
+	}
+
+	if (ep < line + len) {
+		strbuf_add(&msgbuf, func, strlen(func));
+		strbuf_add(&msgbuf, ep, line + len - ep);
+		strbuf_add(&msgbuf, reset, strlen(reset));
+	}
+
+	strbuf_add(&msgbuf, line + len, org_len - len);
+	emit_line(ecbdata->opt, "", "", msgbuf.buf, msgbuf.len);
+	strbuf_release(&msgbuf);
+}
+
 static struct diff_tempfile *claim_diff_tempfile(void) {
 	int i;
 	for (i = 0; i < ARRAY_SIZE(diff_temp); i++)
@@ -201,29 +532,6 @@
 	raise(signo);
 }
 
-static int count_lines(const char *data, int size)
-{
-	int count, ch, completely_empty = 1, nl_just_seen = 0;
-	count = 0;
-	while (0 < size--) {
-		ch = *data++;
-		if (ch == '\n') {
-			count++;
-			nl_just_seen = 1;
-			completely_empty = 0;
-		}
-		else {
-			nl_just_seen = 0;
-			completely_empty = 0;
-		}
-	}
-	if (completely_empty)
-		return 0;
-	if (!nl_just_seen)
-		count++; /* no trailing newline */
-	return count;
-}
-
 static void print_line_count(FILE *file, int count)
 {
 	switch (count) {
@@ -239,48 +547,63 @@
 	}
 }
 
-static void copy_file_with_prefix(FILE *file,
-				  int prefix, const char *data, int size,
-				  const char *set, const char *reset)
+static void emit_rewrite_lines(struct emit_callback *ecb,
+			       int prefix, const char *data, int size)
 {
-	int ch, nl_just_seen = 1;
-	while (0 < size--) {
-		ch = *data++;
-		if (nl_just_seen) {
-			fputs(set, file);
-			putc(prefix, file);
+	const char *endp = NULL;
+	static const char *nneof = " No newline at end of file\n";
+	const char *old = diff_get_color(ecb->color_diff, DIFF_FILE_OLD);
+	const char *reset = diff_get_color(ecb->color_diff, DIFF_RESET);
+
+	while (0 < size) {
+		int len;
+
+		endp = memchr(data, '\n', size);
+		len = endp ? (endp - data + 1) : size;
+		if (prefix != '+') {
+			ecb->lno_in_preimage++;
+			emit_line_0(ecb->opt, old, reset, '-',
+				    data, len);
+		} else {
+			ecb->lno_in_postimage++;
+			emit_add_line(reset, ecb, data, len);
 		}
-		if (ch == '\n') {
-			nl_just_seen = 1;
-			fputs(reset, file);
-		} else
-			nl_just_seen = 0;
-		putc(ch, file);
+		size -= len;
+		data += len;
 	}
-	if (!nl_just_seen)
-		fprintf(file, "%s\n\\ No newline at end of file\n", reset);
+	if (!endp) {
+		const char *plain = diff_get_color(ecb->color_diff,
+						   DIFF_PLAIN);
+		emit_line_0(ecb->opt, plain, reset, '\\',
+			    nneof, strlen(nneof));
+	}
 }
 
 static void emit_rewrite_diff(const char *name_a,
 			      const char *name_b,
 			      struct diff_filespec *one,
 			      struct diff_filespec *two,
-			      const char *textconv_one,
-			      const char *textconv_two,
+			      struct userdiff_driver *textconv_one,
+			      struct userdiff_driver *textconv_two,
 			      struct diff_options *o)
 {
 	int lc_a, lc_b;
-	int color_diff = DIFF_OPT_TST(o, COLOR_DIFF);
 	const char *name_a_tab, *name_b_tab;
-	const char *metainfo = diff_get_color(color_diff, DIFF_METAINFO);
-	const char *fraginfo = diff_get_color(color_diff, DIFF_FRAGINFO);
-	const char *old = diff_get_color(color_diff, DIFF_FILE_OLD);
-	const char *new = diff_get_color(color_diff, DIFF_FILE_NEW);
-	const char *reset = diff_get_color(color_diff, DIFF_RESET);
+	const char *metainfo = diff_get_color(o->use_color, DIFF_METAINFO);
+	const char *fraginfo = diff_get_color(o->use_color, DIFF_FRAGINFO);
+	const char *reset = diff_get_color(o->use_color, DIFF_RESET);
 	static struct strbuf a_name = STRBUF_INIT, b_name = STRBUF_INIT;
 	const char *a_prefix, *b_prefix;
-	const char *data_one, *data_two;
+	char *data_one, *data_two;
 	size_t size_one, size_two;
+	struct emit_callback ecbdata;
+	char *line_prefix = "";
+	struct strbuf *msgbuf;
+
+	if (o && o->output_prefix) {
+		msgbuf = o->output_prefix(o, o->output_prefix_data);
+		line_prefix = msgbuf->buf;
+	}
 
 	if (diff_mnemonic_prefix && DIFF_OPT_TST(o, REVERSE_DIFF)) {
 		a_prefix = o->b_prefix;
@@ -300,56 +623,47 @@
 	quote_two_c_style(&a_name, a_prefix, name_a, 0);
 	quote_two_c_style(&b_name, b_prefix, name_b, 0);
 
-	diff_populate_filespec(one, 0);
-	diff_populate_filespec(two, 0);
-	if (textconv_one) {
-		data_one = run_textconv(textconv_one, one, &size_one);
-		if (!data_one)
-			die("unable to read files to diff");
+	size_one = fill_textconv(textconv_one, one, &data_one);
+	size_two = fill_textconv(textconv_two, two, &data_two);
+
+	memset(&ecbdata, 0, sizeof(ecbdata));
+	ecbdata.color_diff = want_color(o->use_color);
+	ecbdata.found_changesp = &o->found_changes;
+	ecbdata.ws_rule = whitespace_rule(name_b ? name_b : name_a);
+	ecbdata.opt = o;
+	if (ecbdata.ws_rule & WS_BLANK_AT_EOF) {
+		mmfile_t mf1, mf2;
+		mf1.ptr = (char *)data_one;
+		mf2.ptr = (char *)data_two;
+		mf1.size = size_one;
+		mf2.size = size_two;
+		check_blank_at_eof(&mf1, &mf2, &ecbdata);
 	}
-	else {
-		data_one = one->data;
-		size_one = one->size;
-	}
-	if (textconv_two) {
-		data_two = run_textconv(textconv_two, two, &size_two);
-		if (!data_two)
-			die("unable to read files to diff");
-	}
-	else {
-		data_two = two->data;
-		size_two = two->size;
-	}
+	ecbdata.lno_in_preimage = 1;
+	ecbdata.lno_in_postimage = 1;
 
 	lc_a = count_lines(data_one, size_one);
 	lc_b = count_lines(data_two, size_two);
 	fprintf(o->file,
-		"%s--- %s%s%s\n%s+++ %s%s%s\n%s@@ -",
-		metainfo, a_name.buf, name_a_tab, reset,
-		metainfo, b_name.buf, name_b_tab, reset, fraginfo);
-	print_line_count(o->file, lc_a);
+		"%s%s--- %s%s%s\n%s%s+++ %s%s%s\n%s%s@@ -",
+		line_prefix, metainfo, a_name.buf, name_a_tab, reset,
+		line_prefix, metainfo, b_name.buf, name_b_tab, reset,
+		line_prefix, fraginfo);
+	if (!o->irreversible_delete)
+		print_line_count(o->file, lc_a);
+	else
+		fprintf(o->file, "?,?");
 	fprintf(o->file, " +");
 	print_line_count(o->file, lc_b);
 	fprintf(o->file, " @@%s\n", reset);
-	if (lc_a)
-		copy_file_with_prefix(o->file, '-', data_one, size_one, old, reset);
+	if (lc_a && !o->irreversible_delete)
+		emit_rewrite_lines(&ecbdata, '-', data_one, size_one);
 	if (lc_b)
-		copy_file_with_prefix(o->file, '+', data_two, size_two, new, reset);
-}
-
-static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
-{
-	if (!DIFF_FILE_VALID(one)) {
-		mf->ptr = (char *)""; /* does not matter */
-		mf->size = 0;
-		return 0;
-	}
-	else if (diff_populate_filespec(one, 0))
-		return -1;
-
-	mf->ptr = one->data;
-	mf->size = one->size;
-	return 0;
+		emit_rewrite_lines(&ecbdata, '+', data_two, size_two);
+	if (textconv_one)
+		free((char *)data_one);
+	if (textconv_two)
+		free((char *)data_two);
 }
 
 struct diff_words_buffer {
@@ -372,23 +686,132 @@
 	buffer->text.ptr[buffer->text.size] = '\0';
 }
 
+struct diff_words_style_elem {
+	const char *prefix;
+	const char *suffix;
+	const char *color; /* NULL; filled in by the setup code if
+			    * color is enabled */
+};
+
+struct diff_words_style {
+	enum diff_words_type type;
+	struct diff_words_style_elem new, old, ctx;
+	const char *newline;
+};
+
+static struct diff_words_style diff_words_styles[] = {
+	{ DIFF_WORDS_PORCELAIN, {"+", "\n"}, {"-", "\n"}, {" ", "\n"}, "~\n" },
+	{ DIFF_WORDS_PLAIN, {"{+", "+}"}, {"[-", "-]"}, {"", ""}, "\n" },
+	{ DIFF_WORDS_COLOR, {"", ""}, {"", ""}, {"", ""}, "\n" }
+};
+
 struct diff_words_data {
 	struct diff_words_buffer minus, plus;
 	const char *current_plus;
-	FILE *file;
+	int last_minus;
+	struct diff_options *opt;
 	regex_t *word_regex;
+	enum diff_words_type type;
+	struct diff_words_style *style;
 };
 
+static int fn_out_diff_words_write_helper(FILE *fp,
+					  struct diff_words_style_elem *st_el,
+					  const char *newline,
+					  size_t count, const char *buf,
+					  const char *line_prefix)
+{
+	int print = 0;
+
+	while (count) {
+		char *p = memchr(buf, '\n', count);
+		if (print)
+			fputs(line_prefix, fp);
+		if (p != buf) {
+			if (st_el->color && fputs(st_el->color, fp) < 0)
+				return -1;
+			if (fputs(st_el->prefix, fp) < 0 ||
+			    fwrite(buf, p ? p - buf : count, 1, fp) != 1 ||
+			    fputs(st_el->suffix, fp) < 0)
+				return -1;
+			if (st_el->color && *st_el->color
+			    && fputs(GIT_COLOR_RESET, fp) < 0)
+				return -1;
+		}
+		if (!p)
+			return 0;
+		if (fputs(newline, fp) < 0)
+			return -1;
+		count -= p + 1 - buf;
+		buf = p + 1;
+		print = 1;
+	}
+	return 0;
+}
+
+/*
+ * '--color-words' algorithm can be described as:
+ *
+ *   1. collect a the minus/plus lines of a diff hunk, divided into
+ *      minus-lines and plus-lines;
+ *
+ *   2. break both minus-lines and plus-lines into words and
+ *      place them into two mmfile_t with one word for each line;
+ *
+ *   3. use xdiff to run diff on the two mmfile_t to get the words level diff;
+ *
+ * And for the common parts of the both file, we output the plus side text.
+ * diff_words->current_plus is used to trace the current position of the plus file
+ * which printed. diff_words->last_minus is used to trace the last minus word
+ * printed.
+ *
+ * For '--graph' to work with '--color-words', we need to output the graph prefix
+ * on each line of color words output. Generally, there are two conditions on
+ * which we should output the prefix.
+ *
+ *   1. diff_words->last_minus == 0 &&
+ *      diff_words->current_plus == diff_words->plus.text.ptr
+ *
+ *      that is: the plus text must start as a new line, and if there is no minus
+ *      word printed, a graph prefix must be printed.
+ *
+ *   2. diff_words->current_plus > diff_words->plus.text.ptr &&
+ *      *(diff_words->current_plus - 1) == '\n'
+ *
+ *      that is: a graph prefix must be printed following a '\n'
+ */
+static int color_words_output_graph_prefix(struct diff_words_data *diff_words)
+{
+	if ((diff_words->last_minus == 0 &&
+		diff_words->current_plus == diff_words->plus.text.ptr) ||
+		(diff_words->current_plus > diff_words->plus.text.ptr &&
+		*(diff_words->current_plus - 1) == '\n')) {
+		return 1;
+	} else {
+		return 0;
+	}
+}
+
 static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)
 {
 	struct diff_words_data *diff_words = priv;
+	struct diff_words_style *style = diff_words->style;
 	int minus_first, minus_len, plus_first, plus_len;
 	const char *minus_begin, *minus_end, *plus_begin, *plus_end;
+	struct diff_options *opt = diff_words->opt;
+	struct strbuf *msgbuf;
+	char *line_prefix = "";
 
 	if (line[0] != '@' || parse_hunk_header(line, len,
 			&minus_first, &minus_len, &plus_first, &plus_len))
 		return;
 
+	assert(opt);
+	if (opt->output_prefix) {
+		msgbuf = opt->output_prefix(opt, opt->output_prefix_data);
+		line_prefix = msgbuf->buf;
+	}
+
 	/* POSIX requires that first be decremented by one if len == 0... */
 	if (minus_len) {
 		minus_begin = diff_words->minus.orig[minus_first].begin;
@@ -404,20 +827,32 @@
 	} else
 		plus_begin = plus_end = diff_words->plus.orig[plus_first].end;
 
-	if (diff_words->current_plus != plus_begin)
-		fwrite(diff_words->current_plus,
-				plus_begin - diff_words->current_plus, 1,
-				diff_words->file);
-	if (minus_begin != minus_end)
-		color_fwrite_lines(diff_words->file,
-				diff_get_color(1, DIFF_FILE_OLD),
-				minus_end - minus_begin, minus_begin);
-	if (plus_begin != plus_end)
-		color_fwrite_lines(diff_words->file,
-				diff_get_color(1, DIFF_FILE_NEW),
-				plus_end - plus_begin, plus_begin);
+	if (color_words_output_graph_prefix(diff_words)) {
+		fputs(line_prefix, diff_words->opt->file);
+	}
+	if (diff_words->current_plus != plus_begin) {
+		fn_out_diff_words_write_helper(diff_words->opt->file,
+				&style->ctx, style->newline,
+				plus_begin - diff_words->current_plus,
+				diff_words->current_plus, line_prefix);
+		if (*(plus_begin - 1) == '\n')
+			fputs(line_prefix, diff_words->opt->file);
+	}
+	if (minus_begin != minus_end) {
+		fn_out_diff_words_write_helper(diff_words->opt->file,
+				&style->old, style->newline,
+				minus_end - minus_begin, minus_begin,
+				line_prefix);
+	}
+	if (plus_begin != plus_end) {
+		fn_out_diff_words_write_helper(diff_words->opt->file,
+				&style->new, style->newline,
+				plus_end - plus_begin, plus_begin,
+				line_prefix);
+	}
 
 	diff_words->current_plus = plus_end;
+	diff_words->last_minus = minus_first;
 }
 
 /* This function starts looking at *begin, and returns 0 iff a word was found. */
@@ -495,65 +930,141 @@
 {
 	xpparam_t xpp;
 	xdemitconf_t xecfg;
-	xdemitcb_t ecb;
 	mmfile_t minus, plus;
+	struct diff_words_style *style = diff_words->style;
+
+	struct diff_options *opt = diff_words->opt;
+	struct strbuf *msgbuf;
+	char *line_prefix = "";
+
+	assert(opt);
+	if (opt->output_prefix) {
+		msgbuf = opt->output_prefix(opt, opt->output_prefix_data);
+		line_prefix = msgbuf->buf;
+	}
 
 	/* special case: only removal */
 	if (!diff_words->plus.text.size) {
-		color_fwrite_lines(diff_words->file,
-			diff_get_color(1, DIFF_FILE_OLD),
-			diff_words->minus.text.size, diff_words->minus.text.ptr);
+		fputs(line_prefix, diff_words->opt->file);
+		fn_out_diff_words_write_helper(diff_words->opt->file,
+			&style->old, style->newline,
+			diff_words->minus.text.size,
+			diff_words->minus.text.ptr, line_prefix);
 		diff_words->minus.text.size = 0;
 		return;
 	}
 
 	diff_words->current_plus = diff_words->plus.text.ptr;
+	diff_words->last_minus = 0;
 
 	memset(&xpp, 0, sizeof(xpp));
 	memset(&xecfg, 0, sizeof(xecfg));
 	diff_words_fill(&diff_words->minus, &minus, diff_words->word_regex);
 	diff_words_fill(&diff_words->plus, &plus, diff_words->word_regex);
-	xpp.flags = XDF_NEED_MINIMAL;
+	xpp.flags = 0;
 	/* as only the hunk header will be parsed, we need a 0-context */
 	xecfg.ctxlen = 0;
 	xdi_diff_outf(&minus, &plus, fn_out_diff_words_aux, diff_words,
-		      &xpp, &xecfg, &ecb);
+		      &xpp, &xecfg);
 	free(minus.ptr);
 	free(plus.ptr);
 	if (diff_words->current_plus != diff_words->plus.text.ptr +
-			diff_words->plus.text.size)
-		fwrite(diff_words->current_plus,
+			diff_words->plus.text.size) {
+		if (color_words_output_graph_prefix(diff_words))
+			fputs(line_prefix, diff_words->opt->file);
+		fn_out_diff_words_write_helper(diff_words->opt->file,
+			&style->ctx, style->newline,
 			diff_words->plus.text.ptr + diff_words->plus.text.size
-			- diff_words->current_plus, 1,
-			diff_words->file);
+			- diff_words->current_plus, diff_words->current_plus,
+			line_prefix);
+	}
 	diff_words->minus.text.size = diff_words->plus.text.size = 0;
 }
 
-typedef unsigned long (*sane_truncate_fn)(char *line, unsigned long len);
+/* In "color-words" mode, show word-diff of words accumulated in the buffer */
+static void diff_words_flush(struct emit_callback *ecbdata)
+{
+	if (ecbdata->diff_words->minus.text.size ||
+	    ecbdata->diff_words->plus.text.size)
+		diff_words_show(ecbdata->diff_words);
+}
 
-struct emit_callback {
-	int nparents, color_diff;
-	unsigned ws_rule;
-	sane_truncate_fn truncate;
-	const char **label_path;
-	struct diff_words_data *diff_words;
-	int *found_changesp;
-	FILE *file;
-};
+static void diff_filespec_load_driver(struct diff_filespec *one)
+{
+	/* Use already-loaded driver */
+	if (one->driver)
+		return;
+
+	if (S_ISREG(one->mode))
+		one->driver = userdiff_find_by_path(one->path);
+
+	/* Fallback to default settings */
+	if (!one->driver)
+		one->driver = userdiff_find_by_name("default");
+}
+
+static const char *userdiff_word_regex(struct diff_filespec *one)
+{
+	diff_filespec_load_driver(one);
+	return one->driver->word_regex;
+}
+
+static void init_diff_words_data(struct emit_callback *ecbdata,
+				 struct diff_options *orig_opts,
+				 struct diff_filespec *one,
+				 struct diff_filespec *two)
+{
+	int i;
+	struct diff_options *o = xmalloc(sizeof(struct diff_options));
+	memcpy(o, orig_opts, sizeof(struct diff_options));
+
+	ecbdata->diff_words =
+		xcalloc(1, sizeof(struct diff_words_data));
+	ecbdata->diff_words->type = o->word_diff;
+	ecbdata->diff_words->opt = o;
+	if (!o->word_regex)
+		o->word_regex = userdiff_word_regex(one);
+	if (!o->word_regex)
+		o->word_regex = userdiff_word_regex(two);
+	if (!o->word_regex)
+		o->word_regex = diff_word_regex_cfg;
+	if (o->word_regex) {
+		ecbdata->diff_words->word_regex = (regex_t *)
+			xmalloc(sizeof(regex_t));
+		if (regcomp(ecbdata->diff_words->word_regex,
+			    o->word_regex,
+			    REG_EXTENDED | REG_NEWLINE))
+			die ("Invalid regular expression: %s",
+			     o->word_regex);
+	}
+	for (i = 0; i < ARRAY_SIZE(diff_words_styles); i++) {
+		if (o->word_diff == diff_words_styles[i].type) {
+			ecbdata->diff_words->style =
+				&diff_words_styles[i];
+			break;
+		}
+	}
+	if (want_color(o->use_color)) {
+		struct diff_words_style *st = ecbdata->diff_words->style;
+		st->old.color = diff_get_color_opt(o, DIFF_FILE_OLD);
+		st->new.color = diff_get_color_opt(o, DIFF_FILE_NEW);
+		st->ctx.color = diff_get_color_opt(o, DIFF_PLAIN);
+	}
+}
 
 static void free_diff_words_data(struct emit_callback *ecbdata)
 {
 	if (ecbdata->diff_words) {
-		/* flush buffers */
-		if (ecbdata->diff_words->minus.text.size ||
-				ecbdata->diff_words->plus.text.size)
-			diff_words_show(ecbdata->diff_words);
-
+		diff_words_flush(ecbdata);
+		free (ecbdata->diff_words->opt);
 		free (ecbdata->diff_words->minus.text.ptr);
 		free (ecbdata->diff_words->minus.orig);
 		free (ecbdata->diff_words->plus.text.ptr);
 		free (ecbdata->diff_words->plus.orig);
-		free(ecbdata->diff_words->word_regex);
+		if (ecbdata->diff_words->word_regex) {
+			regfree(ecbdata->diff_words->word_regex);
+			free(ecbdata->diff_words->word_regex);
+		}
 		free(ecbdata->diff_words);
 		ecbdata->diff_words = NULL;
 	}
@@ -561,47 +1072,11 @@
 
 const char *diff_get_color(int diff_use_color, enum color_diff ix)
 {
-	if (diff_use_color)
+	if (want_color(diff_use_color))
 		return diff_colors[ix];
 	return "";
 }
 
-static void emit_line(FILE *file, const char *set, const char *reset, const char *line, int len)
-{
-	int has_trailing_newline, has_trailing_carriage_return;
-
-	has_trailing_newline = (len > 0 && line[len-1] == '\n');
-	if (has_trailing_newline)
-		len--;
-	has_trailing_carriage_return = (len > 0 && line[len-1] == '\r');
-	if (has_trailing_carriage_return)
-		len--;
-
-	fputs(set, file);
-	fwrite(line, len, 1, file);
-	fputs(reset, file);
-	if (has_trailing_carriage_return)
-		fputc('\r', file);
-	if (has_trailing_newline)
-		fputc('\n', file);
-}
-
-static void emit_add_line(const char *reset, struct emit_callback *ecbdata, const char *line, int len)
-{
-	const char *ws = diff_get_color(ecbdata->color_diff, DIFF_WHITESPACE);
-	const char *set = diff_get_color(ecbdata->color_diff, DIFF_FILE_NEW);
-
-	if (!*ws)
-		emit_line(ecbdata->file, set, reset, line, len);
-	else {
-		/* Emit just the prefix, then the rest. */
-		emit_line(ecbdata->file, set, reset, line, ecbdata->nparents);
-		ws_check_emit(line + ecbdata->nparents,
-			      len - ecbdata->nparents, ecbdata->ws_rule,
-			      ecbdata->file, set, reset, ws);
-	}
-}
-
 static unsigned long sane_truncate_line(struct emit_callback *ecb, char *line, unsigned long len)
 {
 	const char *cp;
@@ -620,15 +1095,41 @@
 	return allot - l;
 }
 
+static void find_lno(const char *line, struct emit_callback *ecbdata)
+{
+	const char *p;
+	ecbdata->lno_in_preimage = 0;
+	ecbdata->lno_in_postimage = 0;
+	p = strchr(line, '-');
+	if (!p)
+		return; /* cannot happen */
+	ecbdata->lno_in_preimage = strtol(p + 1, NULL, 10);
+	p = strchr(p, '+');
+	if (!p)
+		return; /* cannot happen */
+	ecbdata->lno_in_postimage = strtol(p + 1, NULL, 10);
+}
+
 static void fn_out_consume(void *priv, char *line, unsigned long len)
 {
-	int i;
-	int color;
 	struct emit_callback *ecbdata = priv;
 	const char *meta = diff_get_color(ecbdata->color_diff, DIFF_METAINFO);
 	const char *plain = diff_get_color(ecbdata->color_diff, DIFF_PLAIN);
 	const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
+	struct diff_options *o = ecbdata->opt;
+	char *line_prefix = "";
+	struct strbuf *msgbuf;
 
+	if (o && o->output_prefix) {
+		msgbuf = o->output_prefix(o, o->output_prefix_data);
+		line_prefix = msgbuf->buf;
+	}
+
+	if (ecbdata->header) {
+		fprintf(ecbdata->opt->file, "%s", ecbdata->header->buf);
+		strbuf_reset(ecbdata->header);
+		ecbdata->header = NULL;
+	}
 	*(ecbdata->found_changesp) = 1;
 
 	if (ecbdata->label_path[0]) {
@@ -637,10 +1138,10 @@
 		name_a_tab = strchr(ecbdata->label_path[0], ' ') ? "\t" : "";
 		name_b_tab = strchr(ecbdata->label_path[1], ' ') ? "\t" : "";
 
-		fprintf(ecbdata->file, "%s--- %s%s%s\n",
-			meta, ecbdata->label_path[0], reset, name_a_tab);
-		fprintf(ecbdata->file, "%s+++ %s%s%s\n",
-			meta, ecbdata->label_path[1], reset, name_b_tab);
+		fprintf(ecbdata->opt->file, "%s%s--- %s%s%s\n",
+			line_prefix, meta, ecbdata->label_path[0], reset, name_a_tab);
+		fprintf(ecbdata->opt->file, "%s%s+++ %s%s%s\n",
+			line_prefix, meta, ecbdata->label_path[1], reset, name_b_tab);
 		ecbdata->label_path[0] = ecbdata->label_path[1] = NULL;
 	}
 
@@ -650,31 +1151,25 @@
 		len = 1;
 	}
 
-	/* This is not really necessary for now because
-	 * this codepath only deals with two-way diffs.
-	 */
-	for (i = 0; i < len && line[i] == '@'; i++)
-		;
-	if (2 <= i && i < len && line[i] == ' ') {
-		ecbdata->nparents = i - 1;
+	if (line[0] == '@') {
+		if (ecbdata->diff_words)
+			diff_words_flush(ecbdata);
 		len = sane_truncate_line(ecbdata, line, len);
-		emit_line(ecbdata->file,
-			  diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO),
-			  reset, line, len);
+		find_lno(line, ecbdata);
+		emit_hunk_header(ecbdata, line, len);
 		if (line[len-1] != '\n')
-			putc('\n', ecbdata->file);
+			putc('\n', ecbdata->opt->file);
 		return;
 	}
 
-	if (len < ecbdata->nparents) {
-		emit_line(ecbdata->file, reset, reset, line, len);
+	if (len < 1) {
+		emit_line(ecbdata->opt, reset, reset, line, len);
+		if (ecbdata->diff_words
+		    && ecbdata->diff_words->type == DIFF_WORDS_PORCELAIN)
+			fputs("~\n", ecbdata->opt->file);
 		return;
 	}
 
-	color = DIFF_PLAIN;
-	if (ecbdata->diff_words && ecbdata->nparents != 1)
-		/* fall back to normal diff */
-		free_diff_words_data(ecbdata);
 	if (ecbdata->diff_words) {
 		if (line[0] == '-') {
 			diff_words_append(line, len,
@@ -684,29 +1179,47 @@
 			diff_words_append(line, len,
 					  &ecbdata->diff_words->plus);
 			return;
+		} else if (!prefixcmp(line, "\\ ")) {
+			/*
+			 * Eat the "no newline at eof" marker as if we
+			 * saw a "+" or "-" line with nothing on it,
+			 * and return without diff_words_flush() to
+			 * defer processing. If this is the end of
+			 * preimage, more "+" lines may come after it.
+			 */
+			return;
 		}
-		if (ecbdata->diff_words->minus.text.size ||
-		    ecbdata->diff_words->plus.text.size)
-			diff_words_show(ecbdata->diff_words);
-		line++;
-		len--;
-		emit_line(ecbdata->file, plain, reset, line, len);
+		diff_words_flush(ecbdata);
+		if (ecbdata->diff_words->type == DIFF_WORDS_PORCELAIN) {
+			emit_line(ecbdata->opt, plain, reset, line, len);
+			fputs("~\n", ecbdata->opt->file);
+		} else {
+			/*
+			 * Skip the prefix character, if any.  With
+			 * diff_suppress_blank_empty, there may be
+			 * none.
+			 */
+			if (line[0] != '\n') {
+			      line++;
+			      len--;
+			}
+			emit_line(ecbdata->opt, plain, reset, line, len);
+		}
 		return;
 	}
-	for (i = 0; i < ecbdata->nparents && len; i++) {
-		if (line[i] == '-')
-			color = DIFF_FILE_OLD;
-		else if (line[i] == '+')
-			color = DIFF_FILE_NEW;
-	}
 
-	if (color != DIFF_FILE_NEW) {
-		emit_line(ecbdata->file,
-			  diff_get_color(ecbdata->color_diff, color),
-			  reset, line, len);
-		return;
+	if (line[0] != '+') {
+		const char *color =
+			diff_get_color(ecbdata->color_diff,
+				       line[0] == '-' ? DIFF_FILE_OLD : DIFF_PLAIN);
+		ecbdata->lno_in_preimage++;
+		if (line[0] == ' ')
+			ecbdata->lno_in_postimage++;
+		emit_line(ecbdata->opt, color, reset, line, len);
+	} else {
+		ecbdata->lno_in_postimage++;
+		emit_add_line(reset, ecbdata, line + 1, len - 1);
 	}
-	emit_add_line(reset, ecbdata, line, len);
 }
 
 static char *pprint_rename(const char *a, const char *b)
@@ -786,7 +1299,7 @@
 		unsigned is_unmerged:1;
 		unsigned is_binary:1;
 		unsigned is_renamed:1;
-		unsigned int added, deleted;
+		uintmax_t added, deleted;
 	} **files;
 };
 
@@ -829,13 +1342,15 @@
 
 static int scale_linear(int it, int width, int max_change)
 {
+	if (!it)
+		return 0;
 	/*
-	 * make sure that at least one '-' is printed if there were deletions,
-	 * and likewise for '+'.
+	 * make sure that at least one '-' or '+' is printed if
+	 * there is any change to this path. The easiest way is to
+	 * scale linearly as if the alloted width is one column shorter
+	 * than it is, and then add 1 to the result.
 	 */
-	if (max_change < 2)
-		return it;
-	return ((it - 1) * (width - 1) + max_change - 1) / (max_change - 1);
+	return 1 + (it * (width - 1) / max_change);
 }
 
 static void show_name(FILE *file,
@@ -875,70 +1390,212 @@
 	file->print_name = pname;
 }
 
+int print_stat_summary(FILE *fp, int files, int insertions, int deletions)
+{
+	struct strbuf sb = STRBUF_INIT;
+	int ret;
+
+	if (!files) {
+		assert(insertions == 0 && deletions == 0);
+		return fputs(_(" 0 files changed\n"), fp);
+	}
+
+	strbuf_addf(&sb,
+		    Q_(" %d file changed", " %d files changed", files),
+		    files);
+
+	/*
+	 * For binary diff, the caller may want to print "x files
+	 * changed" with insertions == 0 && deletions == 0.
+	 *
+	 * Not omitting "0 insertions(+), 0 deletions(-)" in this case
+	 * is probably less confusing (i.e skip over "2 files changed
+	 * but nothing about added/removed lines? Is this a bug in Git?").
+	 */
+	if (insertions || deletions == 0) {
+		/*
+		 * TRANSLATORS: "+" in (+) is a line addition marker;
+		 * do not translate it.
+		 */
+		strbuf_addf(&sb,
+			    Q_(", %d insertion(+)", ", %d insertions(+)",
+			       insertions),
+			    insertions);
+	}
+
+	if (deletions || insertions == 0) {
+		/*
+		 * TRANSLATORS: "-" in (-) is a line removal marker;
+		 * do not translate it.
+		 */
+		strbuf_addf(&sb,
+			    Q_(", %d deletion(-)", ", %d deletions(-)",
+			       deletions),
+			    deletions);
+	}
+	strbuf_addch(&sb, '\n');
+	ret = fputs(sb.buf, fp);
+	strbuf_release(&sb);
+	return ret;
+}
+
 static void show_stats(struct diffstat_t *data, struct diff_options *options)
 {
 	int i, len, add, del, adds = 0, dels = 0;
-	int max_change = 0, max_len = 0;
-	int total_files = data->nr;
-	int width, name_width;
-	const char *reset, *set, *add_c, *del_c;
+	uintmax_t max_change = 0, max_len = 0;
+	int total_files = data->nr, count;
+	int width, name_width, graph_width, number_width = 0, bin_width = 0;
+	const char *reset, *add_c, *del_c;
+	const char *line_prefix = "";
+	int extra_shown = 0;
+	struct strbuf *msg = NULL;
 
 	if (data->nr == 0)
 		return;
 
-	width = options->stat_width ? options->stat_width : 80;
-	name_width = options->stat_name_width ? options->stat_name_width : 50;
+	if (options->output_prefix) {
+		msg = options->output_prefix(options, options->output_prefix_data);
+		line_prefix = msg->buf;
+	}
 
-	/* Sanity: give at least 5 columns to the graph,
-	 * but leave at least 10 columns for the name.
-	 */
-	if (width < 25)
-		width = 25;
-	if (name_width < 10)
-		name_width = 10;
-	else if (width < name_width + 15)
-		name_width = width - 15;
+	count = options->stat_count ? options->stat_count : data->nr;
 
-	/* Find the longest filename and max number of changes */
 	reset = diff_get_color_opt(options, DIFF_RESET);
-	set   = diff_get_color_opt(options, DIFF_PLAIN);
 	add_c = diff_get_color_opt(options, DIFF_FILE_NEW);
 	del_c = diff_get_color_opt(options, DIFF_FILE_OLD);
 
-	for (i = 0; i < data->nr; i++) {
+	/*
+	 * Find the longest filename and max number of changes
+	 */
+	for (i = 0; (i < count) && (i < data->nr); i++) {
 		struct diffstat_file *file = data->files[i];
-		int change = file->added + file->deleted;
+		uintmax_t change = file->added + file->deleted;
+		if (!data->files[i]->is_renamed &&
+			 (change == 0)) {
+			count++; /* not shown == room for one more */
+			continue;
+		}
 		fill_print_name(file);
 		len = strlen(file->print_name);
 		if (max_len < len)
 			max_len = len;
 
-		if (file->is_binary || file->is_unmerged)
+		if (file->is_unmerged) {
+			/* "Unmerged" is 8 characters */
+			bin_width = bin_width < 8 ? 8 : bin_width;
 			continue;
+		}
+		if (file->is_binary) {
+			/* "Bin XXX -> YYY bytes" */
+			int w = 14 + decimal_width(file->added)
+				+ decimal_width(file->deleted);
+			bin_width = bin_width < w ? w : bin_width;
+			/* Display change counts aligned with "Bin" */
+			number_width = 3;
+			continue;
+		}
+
 		if (max_change < change)
 			max_change = change;
 	}
+	count = i; /* min(count, data->nr) */
 
-	/* Compute the width of the graph part;
-	 * 10 is for one blank at the beginning of the line plus
-	 * " | count " between the name and the graph.
+	/*
+	 * We have width = stat_width or term_columns() columns total.
+	 * We want a maximum of min(max_len, stat_name_width) for the name part.
+	 * We want a maximum of min(max_change, stat_graph_width) for the +- part.
+	 * We also need 1 for " " and 4 + decimal_width(max_change)
+	 * for " | NNNN " and one the empty column at the end, altogether
+	 * 6 + decimal_width(max_change).
 	 *
-	 * From here on, name_width is the width of the name area,
-	 * and width is the width of the graph area.
+	 * If there's not enough space, we will use the smaller of
+	 * stat_name_width (if set) and 5/8*width for the filename,
+	 * and the rest for constant elements + graph part, but no more
+	 * than stat_graph_width for the graph part.
+	 * (5/8 gives 50 for filename and 30 for the constant parts + graph
+	 * for the standard terminal size).
+	 *
+	 * In other words: stat_width limits the maximum width, and
+	 * stat_name_width fixes the maximum width of the filename,
+	 * and is also used to divide available columns if there
+	 * aren't enough.
+	 *
+	 * Binary files are displayed with "Bin XXX -> YYY bytes"
+	 * instead of the change count and graph. This part is treated
+	 * similarly to the graph part, except that it is not
+	 * "scaled". If total width is too small to accomodate the
+	 * guaranteed minimum width of the filename part and the
+	 * separators and this message, this message will "overflow"
+	 * making the line longer than the maximum width.
 	 */
-	name_width = (name_width < max_len) ? name_width : max_len;
-	if (width < (name_width + 10) + max_change)
-		width = width - (name_width + 10);
-	else
-		width = max_change;
 
-	for (i = 0; i < data->nr; i++) {
+	if (options->stat_width == -1)
+		width = term_columns() - options->output_prefix_length;
+	else
+		width = options->stat_width ? options->stat_width : 80;
+	number_width = decimal_width(max_change) > number_width ?
+		decimal_width(max_change) : number_width;
+
+	if (options->stat_graph_width == -1)
+		options->stat_graph_width = diff_stat_graph_width;
+
+	/*
+	 * Guarantee 3/8*16==6 for the graph part
+	 * and 5/8*16==10 for the filename part
+	 */
+	if (width < 16 + 6 + number_width)
+		width = 16 + 6 + number_width;
+
+	/*
+	 * First assign sizes that are wanted, ignoring available width.
+	 * strlen("Bin XXX -> YYY bytes") == bin_width, and the part
+	 * starting from "XXX" should fit in graph_width.
+	 */
+	graph_width = max_change + 4 > bin_width ? max_change : bin_width - 4;
+	if (options->stat_graph_width &&
+	    options->stat_graph_width < graph_width)
+		graph_width = options->stat_graph_width;
+
+	name_width = (options->stat_name_width > 0 &&
+		      options->stat_name_width < max_len) ?
+		options->stat_name_width : max_len;
+
+	/*
+	 * Adjust adjustable widths not to exceed maximum width
+	 */
+	if (name_width + number_width + 6 + graph_width > width) {
+		if (graph_width > width * 3/8 - number_width - 6) {
+			graph_width = width * 3/8 - number_width - 6;
+			if (graph_width < 6)
+				graph_width = 6;
+		}
+
+		if (options->stat_graph_width &&
+		    graph_width > options->stat_graph_width)
+			graph_width = options->stat_graph_width;
+		if (name_width > width - number_width - 6 - graph_width)
+			name_width = width - number_width - 6 - graph_width;
+		else
+			graph_width = width - number_width - 6 - name_width;
+	}
+
+	/*
+	 * From here name_width is the width of the name area,
+	 * and graph_width is the width of the graph area.
+	 * max_change is used to scale graph properly.
+	 */
+	for (i = 0; i < count; i++) {
 		const char *prefix = "";
 		char *name = data->files[i]->print_name;
-		int added = data->files[i]->added;
-		int deleted = data->files[i]->deleted;
+		uintmax_t added = data->files[i]->added;
+		uintmax_t deleted = data->files[i]->deleted;
 		int name_len;
 
+		if (!data->files[i]->is_renamed &&
+			 (added + deleted == 0)) {
+			total_files--;
+			continue;
+		}
 		/*
 		 * "scale" the filename
 		 */
@@ -955,23 +1612,26 @@
 		}
 
 		if (data->files[i]->is_binary) {
+			fprintf(options->file, "%s", line_prefix);
 			show_name(options->file, prefix, name, len);
-			fprintf(options->file, "  Bin ");
-			fprintf(options->file, "%s%d%s", del_c, deleted, reset);
+			fprintf(options->file, " %*s", number_width, "Bin");
+			if (!added && !deleted) {
+				putc('\n', options->file);
+				continue;
+			}
+			fprintf(options->file, " %s%"PRIuMAX"%s",
+				del_c, deleted, reset);
 			fprintf(options->file, " -> ");
-			fprintf(options->file, "%s%d%s", add_c, added, reset);
+			fprintf(options->file, "%s%"PRIuMAX"%s",
+				add_c, added, reset);
 			fprintf(options->file, " bytes");
 			fprintf(options->file, "\n");
 			continue;
 		}
 		else if (data->files[i]->is_unmerged) {
+			fprintf(options->file, "%s", line_prefix);
 			show_name(options->file, prefix, name, len);
-			fprintf(options->file, "  Unmerged\n");
-			continue;
-		}
-		else if (!data->files[i]->is_renamed &&
-			 (added + deleted == 0)) {
-			total_files--;
+			fprintf(options->file, " Unmerged\n");
 			continue;
 		}
 
@@ -983,23 +1643,49 @@
 		adds += add;
 		dels += del;
 
-		if (width <= max_change) {
-			add = scale_linear(add, width, max_change);
-			del = scale_linear(del, width, max_change);
+		if (graph_width <= max_change) {
+			int total = add + del;
+
+			total = scale_linear(add + del, graph_width, max_change);
+			if (total < 2 && add && del)
+				/* width >= 2 due to the sanity check */
+				total = 2;
+			if (add < del) {
+				add = scale_linear(add, graph_width, max_change);
+				del = total - add;
+			} else {
+				del = scale_linear(del, graph_width, max_change);
+				add = total - del;
+			}
 		}
+		fprintf(options->file, "%s", line_prefix);
 		show_name(options->file, prefix, name, len);
-		fprintf(options->file, "%5d%s", added + deleted,
-				added + deleted ? " " : "");
+		fprintf(options->file, " %*"PRIuMAX"%s",
+			number_width, added + deleted,
+			added + deleted ? " " : "");
 		show_graph(options->file, '+', add, add_c, reset);
 		show_graph(options->file, '-', del, del_c, reset);
 		fprintf(options->file, "\n");
 	}
-	fprintf(options->file,
-	       " %d files changed, %d insertions(+), %d deletions(-)\n",
-	       total_files, adds, dels);
+	for (i = count; i < data->nr; i++) {
+		uintmax_t added = data->files[i]->added;
+		uintmax_t deleted = data->files[i]->deleted;
+		if (!data->files[i]->is_renamed &&
+			 (added + deleted == 0)) {
+			total_files--;
+			continue;
+		}
+		adds += added;
+		dels += deleted;
+		if (!extra_shown)
+			fprintf(options->file, "%s ...\n", line_prefix);
+		extra_shown = 1;
+	}
+	fprintf(options->file, "%s", line_prefix);
+	print_stat_summary(options->file, total_files, adds, dels);
 }
 
-static void show_shortstats(struct diffstat_t* data, struct diff_options *options)
+static void show_shortstats(struct diffstat_t *data, struct diff_options *options)
 {
 	int i, adds = 0, dels = 0, total_files = data->nr;
 
@@ -1007,21 +1693,25 @@
 		return;
 
 	for (i = 0; i < data->nr; i++) {
-		if (!data->files[i]->is_binary &&
-		    !data->files[i]->is_unmerged) {
-			int added = data->files[i]->added;
-			int deleted= data->files[i]->deleted;
-			if (!data->files[i]->is_renamed &&
-			    (added + deleted == 0)) {
-				total_files--;
-			} else {
-				adds += added;
-				dels += deleted;
-			}
+		int added = data->files[i]->added;
+		int deleted= data->files[i]->deleted;
+
+		if (data->files[i]->is_unmerged)
+			continue;
+		if (!data->files[i]->is_renamed && (added + deleted == 0)) {
+			total_files--;
+		} else if (!data->files[i]->is_binary) { /* don't count bytes */
+			adds += added;
+			dels += deleted;
 		}
 	}
-	fprintf(options->file, " %d files changed, %d insertions(+), %d deletions(-)\n",
-	       total_files, adds, dels);
+	if (options->output_prefix) {
+		struct strbuf *msg = NULL;
+		msg = options->output_prefix(options,
+				options->output_prefix_data);
+		fprintf(options->file, "%s", msg->buf);
+	}
+	print_stat_summary(options->file, total_files, adds, dels);
 }
 
 static void show_numstat(struct diffstat_t *data, struct diff_options *options)
@@ -1034,11 +1724,19 @@
 	for (i = 0; i < data->nr; i++) {
 		struct diffstat_file *file = data->files[i];
 
+		if (options->output_prefix) {
+			struct strbuf *msg = NULL;
+			msg = options->output_prefix(options,
+					options->output_prefix_data);
+			fprintf(options->file, "%s", msg->buf);
+		}
+
 		if (file->is_binary)
 			fprintf(options->file, "-\t-\t");
 		else
 			fprintf(options->file,
-				"%d\t%d\t", file->added, file->deleted);
+				"%"PRIuMAX"\t%"PRIuMAX"\t",
+				file->added, file->deleted);
 		if (options->line_termination) {
 			fill_print_name(file);
 			if (!file->is_renamed)
@@ -1065,13 +1763,21 @@
 
 struct dirstat_dir {
 	struct dirstat_file *files;
-	int alloc, nr, percent, cumulative;
+	int alloc, nr, permille, cumulative;
 };
 
-static long gather_dirstat(FILE *file, struct dirstat_dir *dir, unsigned long changed, const char *base, int baselen)
+static long gather_dirstat(struct diff_options *opt, struct dirstat_dir *dir,
+		unsigned long changed, const char *base, int baselen)
 {
 	unsigned long this_dir = 0;
 	unsigned int sources = 0;
+	const char *line_prefix = "";
+	struct strbuf *msg = NULL;
+
+	if (opt->output_prefix) {
+		msg = opt->output_prefix(opt, opt->output_prefix_data);
+		line_prefix = msg->buf;
+	}
 
 	while (dir->nr) {
 		struct dirstat_file *f = dir->files;
@@ -1086,7 +1792,7 @@
 		slash = strchr(f->name + baselen, '/');
 		if (slash) {
 			int newbaselen = slash + 1 - f->name;
-			this = gather_dirstat(file, dir, changed, f->name, newbaselen);
+			this = gather_dirstat(opt, dir, changed, f->name, newbaselen);
 			sources++;
 		} else {
 			this = f->changed;
@@ -1104,11 +1810,11 @@
 	 *    under this directory (sources == 1).
 	 */
 	if (baselen && sources != 1) {
-		int permille = this_dir * 1000 / changed;
-		if (permille) {
-			int percent = permille / 10;
-			if (percent >= dir->percent) {
-				fprintf(file, "%4d.%01d%% %.*s\n", percent, permille % 10, baselen, base);
+		if (this_dir) {
+			int permille = this_dir * 1000 / changed;
+			if (permille >= dir->permille) {
+				fprintf(opt->file, "%s%4d.%01d%% %.*s\n", line_prefix,
+					permille / 10, permille % 10, baselen, base);
 				if (!dir->cumulative)
 					return 0;
 			}
@@ -1134,7 +1840,7 @@
 	dir.files = NULL;
 	dir.alloc = 0;
 	dir.nr = 0;
-	dir.percent = options->dirstat_percent;
+	dir.permille = options->dirstat_permille;
 	dir.cumulative = DIFF_OPT_TST(options, DIRSTAT_CUMULATIVE);
 
 	changed = 0;
@@ -1142,8 +1848,36 @@
 		struct diff_filepair *p = q->queue[i];
 		const char *name;
 		unsigned long copied, added, damage;
+		int content_changed;
 
-		name = p->one->path ? p->one->path : p->two->path;
+		name = p->two->path ? p->two->path : p->one->path;
+
+		if (p->one->sha1_valid && p->two->sha1_valid)
+			content_changed = hashcmp(p->one->sha1, p->two->sha1);
+		else
+			content_changed = 1;
+
+		if (!content_changed) {
+			/*
+			 * The SHA1 has not changed, so pre-/post-content is
+			 * identical. We can therefore skip looking at the
+			 * file contents altogether.
+			 */
+			damage = 0;
+			goto found_damage;
+		}
+
+		if (DIFF_OPT_TST(options, DIRSTAT_BY_FILE)) {
+			/*
+			 * In --dirstat-by-file mode, we don't really need to
+			 * look at the actual file contents at all.
+			 * The fact that the SHA1 changed is enough for us to
+			 * add this file to the list of results
+			 * (with each file contributing equal damage).
+			 */
+			damage = 1;
+			goto found_damage;
+		}
 
 		if (DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two)) {
 			diff_populate_filespec(p->one, 0);
@@ -1167,14 +1901,18 @@
 		/*
 		 * Original minus copied is the removed material,
 		 * added is the new material.  They are both damages
-		 * made to the preimage. In --dirstat-by-file mode, count
-		 * damaged files, not damaged lines. This is done by
-		 * counting only a single damaged line per file.
+		 * made to the preimage.
+		 * If the resulting damage is zero, we know that
+		 * diffcore_count_changes() considers the two entries to
+		 * be identical, but since content_changed is true, we
+		 * know that there must have been _some_ kind of change,
+		 * so we force all entries to have damage > 0.
 		 */
 		damage = (p->one->size - copied) + added;
-		if (DIFF_OPT_TST(options, DIRSTAT_BY_FILE) && damage > 0)
+		if (!damage)
 			damage = 1;
 
+found_damage:
 		ALLOC_GROW(dir.files, dir.nr + 1, dir.alloc);
 		dir.files[dir.nr].name = name;
 		dir.files[dir.nr].changed = damage;
@@ -1188,7 +1926,51 @@
 
 	/* Show all directories with more than x% of the changes */
 	qsort(dir.files, dir.nr, sizeof(dir.files[0]), dirstat_compare);
-	gather_dirstat(options->file, &dir, changed, "", 0);
+	gather_dirstat(options, &dir, changed, "", 0);
+}
+
+static void show_dirstat_by_line(struct diffstat_t *data, struct diff_options *options)
+{
+	int i;
+	unsigned long changed;
+	struct dirstat_dir dir;
+
+	if (data->nr == 0)
+		return;
+
+	dir.files = NULL;
+	dir.alloc = 0;
+	dir.nr = 0;
+	dir.permille = options->dirstat_permille;
+	dir.cumulative = DIFF_OPT_TST(options, DIRSTAT_CUMULATIVE);
+
+	changed = 0;
+	for (i = 0; i < data->nr; i++) {
+		struct diffstat_file *file = data->files[i];
+		unsigned long damage = file->added + file->deleted;
+		if (file->is_binary)
+			/*
+			 * binary files counts bytes, not lines. Must find some
+			 * way to normalize binary bytes vs. textual lines.
+			 * The following heuristic assumes that there are 64
+			 * bytes per "line".
+			 * This is stupid and ugly, but very cheap...
+			 */
+			damage = (damage + 63) / 64;
+		ALLOC_GROW(dir.files, dir.nr + 1, dir.alloc);
+		dir.files[dir.nr].name = file->name;
+		dir.files[dir.nr].changed = damage;
+		changed += damage;
+		dir.nr++;
+	}
+
+	/* This can happen even with many files, if everything was renames */
+	if (!changed)
+		return;
+
+	/* Show all directories with more than x% of the changes */
+	qsort(dir.files, dir.nr, sizeof(dir.files[0]), dirstat_compare);
+	gather_dirstat(options, &dir, changed, "", 0);
 }
 
 static void free_diffstat_info(struct diffstat_t *diffstat)
@@ -1208,84 +1990,81 @@
 struct checkdiff_t {
 	const char *filename;
 	int lineno;
+	int conflict_marker_size;
 	struct diff_options *o;
 	unsigned ws_rule;
 	unsigned status;
-	int trailing_blanks_start;
 };
 
-static int is_conflict_marker(const char *line, unsigned long len)
+static int is_conflict_marker(const char *line, int marker_size, unsigned long len)
 {
 	char firstchar;
 	int cnt;
 
-	if (len < 8)
+	if (len < marker_size + 1)
 		return 0;
 	firstchar = line[0];
 	switch (firstchar) {
-	case '=': case '>': case '<':
+	case '=': case '>': case '<': case '|':
 		break;
 	default:
 		return 0;
 	}
-	for (cnt = 1; cnt < 7; cnt++)
+	for (cnt = 1; cnt < marker_size; cnt++)
 		if (line[cnt] != firstchar)
 			return 0;
-	/* line[0] thru line[6] are same as firstchar */
-	if (firstchar == '=') {
-		/* divider between ours and theirs? */
-		if (len != 8 || line[7] != '\n')
-			return 0;
-	} else if (len < 8 || !isspace(line[7])) {
-		/* not divider before ours nor after theirs */
+	/* line[1] thru line[marker_size-1] are same as firstchar */
+	if (len < marker_size + 1 || !isspace(line[marker_size]))
 		return 0;
-	}
 	return 1;
 }
 
 static void checkdiff_consume(void *priv, char *line, unsigned long len)
 {
 	struct checkdiff_t *data = priv;
-	int color_diff = DIFF_OPT_TST(data->o, COLOR_DIFF);
-	const char *ws = diff_get_color(color_diff, DIFF_WHITESPACE);
-	const char *reset = diff_get_color(color_diff, DIFF_RESET);
-	const char *set = diff_get_color(color_diff, DIFF_FILE_NEW);
+	int marker_size = data->conflict_marker_size;
+	const char *ws = diff_get_color(data->o->use_color, DIFF_WHITESPACE);
+	const char *reset = diff_get_color(data->o->use_color, DIFF_RESET);
+	const char *set = diff_get_color(data->o->use_color, DIFF_FILE_NEW);
 	char *err;
+	char *line_prefix = "";
+	struct strbuf *msgbuf;
+
+	assert(data->o);
+	if (data->o->output_prefix) {
+		msgbuf = data->o->output_prefix(data->o,
+			data->o->output_prefix_data);
+		line_prefix = msgbuf->buf;
+	}
 
 	if (line[0] == '+') {
 		unsigned bad;
 		data->lineno++;
-		if (!ws_blank_line(line + 1, len - 1, data->ws_rule))
-			data->trailing_blanks_start = 0;
-		else if (!data->trailing_blanks_start)
-			data->trailing_blanks_start = data->lineno;
-		if (is_conflict_marker(line + 1, len - 1)) {
+		if (is_conflict_marker(line + 1, marker_size, len - 1)) {
 			data->status |= 1;
 			fprintf(data->o->file,
-				"%s:%d: leftover conflict marker\n",
-				data->filename, data->lineno);
+				"%s%s:%d: leftover conflict marker\n",
+				line_prefix, data->filename, data->lineno);
 		}
 		bad = ws_check(line + 1, len - 1, data->ws_rule);
 		if (!bad)
 			return;
 		data->status |= bad;
 		err = whitespace_error_string(bad);
-		fprintf(data->o->file, "%s:%d: %s.\n",
-			data->filename, data->lineno, err);
+		fprintf(data->o->file, "%s%s:%d: %s.\n",
+			line_prefix, data->filename, data->lineno, err);
 		free(err);
-		emit_line(data->o->file, set, reset, line, 1);
+		emit_line(data->o, set, reset, line, 1);
 		ws_check_emit(line + 1, len - 1, data->ws_rule,
 			      data->o->file, set, reset, ws);
 	} else if (line[0] == ' ') {
 		data->lineno++;
-		data->trailing_blanks_start = 0;
 	} else if (line[0] == '@') {
 		char *plus = strchr(line, '+');
 		if (plus)
 			data->lineno = strtol(plus, NULL, 10) - 1;
 		else
 			die("invalid diff");
-		data->trailing_blanks_start = 0;
 	}
 }
 
@@ -1295,25 +2074,25 @@
 {
 	int bound;
 	unsigned char *deflated;
-	z_stream stream;
+	git_zstream stream;
 
 	memset(&stream, 0, sizeof(stream));
-	deflateInit(&stream, zlib_compression_level);
-	bound = deflateBound(&stream, size);
+	git_deflate_init(&stream, zlib_compression_level);
+	bound = git_deflate_bound(&stream, size);
 	deflated = xmalloc(bound);
 	stream.next_out = deflated;
 	stream.avail_out = bound;
 
 	stream.next_in = (unsigned char *)data;
 	stream.avail_in = size;
-	while (deflate(&stream, Z_FINISH) == Z_OK)
+	while (git_deflate(&stream, Z_FINISH) == Z_OK)
 		; /* nothing */
-	deflateEnd(&stream);
+	git_deflate_end(&stream);
 	*result_size = stream.total_out;
 	return deflated;
 }
 
-static void emit_binary_diff_body(FILE *file, mmfile_t *one, mmfile_t *two)
+static void emit_binary_diff_body(FILE *file, mmfile_t *one, mmfile_t *two, char *prefix)
 {
 	void *cp;
 	void *delta;
@@ -1342,13 +2121,13 @@
 	}
 
 	if (delta && delta_size < deflate_size) {
-		fprintf(file, "delta %lu\n", orig_size);
+		fprintf(file, "%sdelta %lu\n", prefix, orig_size);
 		free(deflated);
 		data = delta;
 		data_size = delta_size;
 	}
 	else {
-		fprintf(file, "literal %lu\n", two->size);
+		fprintf(file, "%sliteral %lu\n", prefix, two->size);
 		free(delta);
 		data = deflated;
 		data_size = deflate_size;
@@ -1366,26 +2145,19 @@
 			line[0] = bytes - 26 + 'a' - 1;
 		encode_85(line + 1, cp, bytes);
 		cp = (char *) cp + bytes;
+		fprintf(file, "%s", prefix);
 		fputs(line, file);
 		fputc('\n', file);
 	}
-	fprintf(file, "\n");
+	fprintf(file, "%s\n", prefix);
 	free(data);
 }
 
-static void emit_binary_diff(FILE *file, mmfile_t *one, mmfile_t *two)
+static void emit_binary_diff(FILE *file, mmfile_t *one, mmfile_t *two, char *prefix)
 {
-	fprintf(file, "GIT binary patch\n");
-	emit_binary_diff_body(file, one, two);
-	emit_binary_diff_body(file, two, one);
-}
-
-static void diff_filespec_load_driver(struct diff_filespec *one)
-{
-	if (!one->driver)
-		one->driver = userdiff_find_by_path(one->path);
-	if (!one->driver)
-		one->driver = userdiff_find_by_name("default");
+	fprintf(file, "%sGIT binary patch\n", prefix);
+	emit_binary_diff_body(file, one, two, prefix);
+	emit_binary_diff_body(file, two, one, prefix);
 }
 
 int diff_filespec_is_binary(struct diff_filespec *one)
@@ -1413,12 +2185,6 @@
 	return one->driver->funcname.pattern ? &one->driver->funcname : NULL;
 }
 
-static const char *userdiff_word_regex(struct diff_filespec *one)
-{
-	diff_filespec_load_driver(one);
-	return one->driver->word_regex;
-}
-
 void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const char *b)
 {
 	if (!options->a_prefix)
@@ -1427,14 +2193,13 @@
 		options->b_prefix = b;
 }
 
-static const char *get_textconv(struct diff_filespec *one)
+struct userdiff_driver *get_textconv(struct diff_filespec *one)
 {
 	if (!DIFF_FILE_VALID(one))
 		return NULL;
-	if (!S_ISREG(one->mode))
-		return NULL;
+
 	diff_filespec_load_driver(one);
-	return one->driver->textconv;
+	return userdiff_get_textconv(one->driver);
 }
 
 static void builtin_diff(const char *name_a,
@@ -1442,6 +2207,7 @@
 			 struct diff_filespec *one,
 			 struct diff_filespec *two,
 			 const char *xfrm_msg,
+			 int must_show_header,
 			 struct diff_options *o,
 			 int complete_rewrite)
 {
@@ -1451,7 +2217,27 @@
 	const char *set = diff_get_color_opt(o, DIFF_METAINFO);
 	const char *reset = diff_get_color_opt(o, DIFF_RESET);
 	const char *a_prefix, *b_prefix;
-	const char *textconv_one = NULL, *textconv_two = NULL;
+	struct userdiff_driver *textconv_one = NULL;
+	struct userdiff_driver *textconv_two = NULL;
+	struct strbuf header = STRBUF_INIT;
+	struct strbuf *msgbuf;
+	char *line_prefix = "";
+
+	if (o->output_prefix) {
+		msgbuf = o->output_prefix(o, o->output_prefix_data);
+		line_prefix = msgbuf->buf;
+	}
+
+	if (DIFF_OPT_TST(o, SUBMODULE_LOG) &&
+			(!one->mode || S_ISGITLINK(one->mode)) &&
+			(!two->mode || S_ISGITLINK(two->mode))) {
+		const char *del = diff_get_color_opt(o, DIFF_FILE_OLD);
+		const char *add = diff_get_color_opt(o, DIFF_FILE_NEW);
+		show_submodule_summary(o->file, one ? one->path : two->path,
+				one->sha1, two->sha1, two->dirty_submodule,
+				del, add, reset);
+		return;
+	}
 
 	if (DIFF_OPT_TST(o, ALLOW_TEXTCONV)) {
 		textconv_one = get_textconv(one);
@@ -1475,25 +2261,29 @@
 	b_two = quote_two(b_prefix, name_b + (*name_b == '/'));
 	lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null";
 	lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null";
-	fprintf(o->file, "%sdiff --git %s %s%s\n", set, a_one, b_two, reset);
+	strbuf_addf(&header, "%s%sdiff --git %s %s%s\n", line_prefix, set, a_one, b_two, reset);
 	if (lbl[0][0] == '/') {
 		/* /dev/null */
-		fprintf(o->file, "%snew file mode %06o%s\n", set, two->mode, reset);
-		if (xfrm_msg && xfrm_msg[0])
-			fprintf(o->file, "%s%s%s\n", set, xfrm_msg, reset);
+		strbuf_addf(&header, "%s%snew file mode %06o%s\n", line_prefix, set, two->mode, reset);
+		if (xfrm_msg)
+			strbuf_addstr(&header, xfrm_msg);
+		must_show_header = 1;
 	}
 	else if (lbl[1][0] == '/') {
-		fprintf(o->file, "%sdeleted file mode %06o%s\n", set, one->mode, reset);
-		if (xfrm_msg && xfrm_msg[0])
-			fprintf(o->file, "%s%s%s\n", set, xfrm_msg, reset);
+		strbuf_addf(&header, "%s%sdeleted file mode %06o%s\n", line_prefix, set, one->mode, reset);
+		if (xfrm_msg)
+			strbuf_addstr(&header, xfrm_msg);
+		must_show_header = 1;
 	}
 	else {
 		if (one->mode != two->mode) {
-			fprintf(o->file, "%sold mode %06o%s\n", set, one->mode, reset);
-			fprintf(o->file, "%snew mode %06o%s\n", set, two->mode, reset);
+			strbuf_addf(&header, "%s%sold mode %06o%s\n", line_prefix, set, one->mode, reset);
+			strbuf_addf(&header, "%s%snew mode %06o%s\n", line_prefix, set, two->mode, reset);
+			must_show_header = 1;
 		}
-		if (xfrm_msg && xfrm_msg[0])
-			fprintf(o->file, "%s%s%s\n", set, xfrm_msg, reset);
+		if (xfrm_msg)
+			strbuf_addstr(&header, xfrm_msg);
+
 		/*
 		 * we do not run diff between different kind
 		 * of objects.
@@ -1503,6 +2293,8 @@
 		if (complete_rewrite &&
 		    (textconv_one || !diff_filespec_is_binary(one)) &&
 		    (textconv_two || !diff_filespec_is_binary(two))) {
+			fprintf(o->file, "%s", header.buf);
+			strbuf_reset(&header);
 			emit_rewrite_diff(name_a, name_b, one, two,
 						textconv_one, textconv_two, o);
 			o->found_changes = 1;
@@ -1510,46 +2302,45 @@
 		}
 	}
 
-	if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
-		die("unable to read files to diff");
-
-	if (!DIFF_OPT_TST(o, TEXT) &&
-	    ( (diff_filespec_is_binary(one) && !textconv_one) ||
-	      (diff_filespec_is_binary(two) && !textconv_two) )) {
+	if (o->irreversible_delete && lbl[1][0] == '/') {
+		fprintf(o->file, "%s", header.buf);
+		strbuf_reset(&header);
+		goto free_ab_and_return;
+	} else if (!DIFF_OPT_TST(o, TEXT) &&
+	    ( (!textconv_one && diff_filespec_is_binary(one)) ||
+	      (!textconv_two && diff_filespec_is_binary(two)) )) {
+		if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
+			die("unable to read files to diff");
 		/* Quite common confusing case */
 		if (mf1.size == mf2.size &&
-		    !memcmp(mf1.ptr, mf2.ptr, mf1.size))
+		    !memcmp(mf1.ptr, mf2.ptr, mf1.size)) {
+			if (must_show_header)
+				fprintf(o->file, "%s", header.buf);
 			goto free_ab_and_return;
+		}
+		fprintf(o->file, "%s", header.buf);
+		strbuf_reset(&header);
 		if (DIFF_OPT_TST(o, BINARY))
-			emit_binary_diff(o->file, &mf1, &mf2);
+			emit_binary_diff(o->file, &mf1, &mf2, line_prefix);
 		else
-			fprintf(o->file, "Binary files %s and %s differ\n",
-				lbl[0], lbl[1]);
+			fprintf(o->file, "%sBinary files %s and %s differ\n",
+				line_prefix, lbl[0], lbl[1]);
 		o->found_changes = 1;
-	}
-	else {
+	} else {
 		/* Crazy xdl interfaces.. */
 		const char *diffopts = getenv("GIT_DIFF_OPTS");
 		xpparam_t xpp;
 		xdemitconf_t xecfg;
-		xdemitcb_t ecb;
 		struct emit_callback ecbdata;
 		const struct userdiff_funcname *pe;
 
-		if (textconv_one) {
-			size_t size;
-			mf1.ptr = run_textconv(textconv_one, one, &size);
-			if (!mf1.ptr)
-				die("unable to read files to diff");
-			mf1.size = size;
+		if (must_show_header) {
+			fprintf(o->file, "%s", header.buf);
+			strbuf_reset(&header);
 		}
-		if (textconv_two) {
-			size_t size;
-			mf2.ptr = run_textconv(textconv_two, two, &size);
-			if (!mf2.ptr)
-				die("unable to read files to diff");
-			mf2.size = size;
-		}
+
+		mf1.size = fill_textconv(textconv_one, one, &mf1.ptr);
+		mf2.size = fill_textconv(textconv_two, two, &mf2.ptr);
 
 		pe = diff_funcname_pattern(one);
 		if (!pe)
@@ -1559,14 +2350,19 @@
 		memset(&xecfg, 0, sizeof(xecfg));
 		memset(&ecbdata, 0, sizeof(ecbdata));
 		ecbdata.label_path = lbl;
-		ecbdata.color_diff = DIFF_OPT_TST(o, COLOR_DIFF);
+		ecbdata.color_diff = want_color(o->use_color);
 		ecbdata.found_changesp = &o->found_changes;
 		ecbdata.ws_rule = whitespace_rule(name_b ? name_b : name_a);
-		ecbdata.file = o->file;
-		xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
+		if (ecbdata.ws_rule & WS_BLANK_AT_EOF)
+			check_blank_at_eof(&mf1, &mf2, &ecbdata);
+		ecbdata.opt = o;
+		ecbdata.header = header.len ? &header : NULL;
+		xpp.flags = o->xdl_opts;
 		xecfg.ctxlen = o->context;
 		xecfg.interhunkctxlen = o->interhunkcontext;
 		xecfg.flags = XDL_EMIT_FUNCNAMES;
+		if (DIFF_OPT_TST(o, FUNCCONTEXT))
+			xecfg.flags |= XDL_EMIT_FUNCCONTEXT;
 		if (pe)
 			xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);
 		if (!diffopts)
@@ -1575,29 +2371,11 @@
 			xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10);
 		else if (!prefixcmp(diffopts, "-u"))
 			xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10);
-		if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) {
-			ecbdata.diff_words =
-				xcalloc(1, sizeof(struct diff_words_data));
-			ecbdata.diff_words->file = o->file;
-			if (!o->word_regex)
-				o->word_regex = userdiff_word_regex(one);
-			if (!o->word_regex)
-				o->word_regex = userdiff_word_regex(two);
-			if (!o->word_regex)
-				o->word_regex = diff_word_regex_cfg;
-			if (o->word_regex) {
-				ecbdata.diff_words->word_regex = (regex_t *)
-					xmalloc(sizeof(regex_t));
-				if (regcomp(ecbdata.diff_words->word_regex,
-						o->word_regex,
-						REG_EXTENDED | REG_NEWLINE))
-					die ("Invalid regular expression: %s",
-							o->word_regex);
-			}
-		}
+		if (o->word_diff)
+			init_diff_words_data(&ecbdata, o, one, two);
 		xdi_diff_outf(&mf1, &mf2, fn_out_consume, &ecbdata,
-			      &xpp, &xecfg, &ecb);
-		if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS))
+			      &xpp, &xecfg);
+		if (o->word_diff)
 			free_diff_words_data(&ecbdata);
 		if (textconv_one)
 			free(mf1.ptr);
@@ -1607,6 +2385,7 @@
 	}
 
  free_ab_and_return:
+	strbuf_release(&header);
 	diff_free_filespec_data(one);
 	diff_free_filespec_data(two);
 	free(a_one);
@@ -1623,6 +2402,7 @@
 {
 	mmfile_t mf1, mf2;
 	struct diffstat_file *data;
+	int same_contents;
 
 	data = diffstat_add(diffstat, name_a, name_b);
 
@@ -1630,34 +2410,44 @@
 		data->is_unmerged = 1;
 		return;
 	}
-	if (complete_rewrite) {
+
+	same_contents = !hashcmp(one->sha1, two->sha1);
+
+	if (diff_filespec_is_binary(one) || diff_filespec_is_binary(two)) {
+		data->is_binary = 1;
+		if (same_contents) {
+			data->added = 0;
+			data->deleted = 0;
+		} else {
+			data->added = diff_filespec_size(two);
+			data->deleted = diff_filespec_size(one);
+		}
+	}
+
+	else if (complete_rewrite) {
 		diff_populate_filespec(one, 0);
 		diff_populate_filespec(two, 0);
 		data->deleted = count_lines(one->data, one->size);
 		data->added = count_lines(two->data, two->size);
-		goto free_and_return;
 	}
-	if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
-		die("unable to read files to diff");
 
-	if (diff_filespec_is_binary(one) || diff_filespec_is_binary(two)) {
-		data->is_binary = 1;
-		data->added = mf2.size;
-		data->deleted = mf1.size;
-	} else {
+	else if (!same_contents) {
 		/* Crazy xdl interfaces.. */
 		xpparam_t xpp;
 		xdemitconf_t xecfg;
-		xdemitcb_t ecb;
+
+		if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
+			die("unable to read files to diff");
 
 		memset(&xpp, 0, sizeof(xpp));
 		memset(&xecfg, 0, sizeof(xecfg));
-		xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
+		xpp.flags = o->xdl_opts;
+		xecfg.ctxlen = o->context;
+		xecfg.interhunkctxlen = o->interhunkcontext;
 		xdi_diff_outf(&mf1, &mf2, diffstat_consume, diffstat,
-			      &xpp, &xecfg, &ecb);
+			      &xpp, &xecfg);
 	}
 
- free_and_return:
 	diff_free_filespec_data(one);
 	diff_free_filespec_data(two);
 }
@@ -1679,6 +2469,7 @@
 	data.lineno = 0;
 	data.o = o;
 	data.ws_rule = whitespace_rule(attr_path);
+	data.conflict_marker_size = ll_merge_marker_size(attr_path);
 
 	if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
 		die("unable to read files to diff");
@@ -1695,20 +2486,30 @@
 		/* Crazy xdl interfaces.. */
 		xpparam_t xpp;
 		xdemitconf_t xecfg;
-		xdemitcb_t ecb;
 
 		memset(&xpp, 0, sizeof(xpp));
 		memset(&xecfg, 0, sizeof(xecfg));
 		xecfg.ctxlen = 1; /* at least one context line */
-		xpp.flags = XDF_NEED_MINIMAL;
+		xpp.flags = 0;
 		xdi_diff_outf(&mf1, &mf2, checkdiff_consume, &data,
-			      &xpp, &xecfg, &ecb);
+			      &xpp, &xecfg);
 
-		if ((data.ws_rule & WS_TRAILING_SPACE) &&
-		    data.trailing_blanks_start) {
-			fprintf(o->file, "%s:%d: ends with blank lines.\n",
-				data.filename, data.trailing_blanks_start);
-			data.status = 1; /* report errors */
+		if (data.ws_rule & WS_BLANK_AT_EOF) {
+			struct emit_callback ecbdata;
+			int blank_at_eof;
+
+			ecbdata.ws_rule = data.ws_rule;
+			check_blank_at_eof(&mf1, &mf2, &ecbdata);
+			blank_at_eof = ecbdata.blank_at_eof_in_postimage;
+
+			if (blank_at_eof) {
+				static char *err;
+				if (!err)
+					err = whitespace_error_string(WS_BLANK_AT_EOF);
+				fprintf(o->file, "%s:%d: %s.\n",
+					data.filename, blank_at_eof, err);
+				data.status = 1; /* report errors */
+			}
 		}
 	}
  free_and_return:
@@ -1805,7 +2606,7 @@
 	 * If ce is marked as "assume unchanged", there is no
 	 * guarantee that work tree matches what we are looking for.
 	 */
-	if (ce->ce_flags & CE_VALID)
+	if ((ce->ce_flags & CE_VALID) || ce_skip_worktree(ce))
 		return 0;
 
 	/*
@@ -1818,28 +2619,17 @@
 	return 0;
 }
 
-static int populate_from_stdin(struct diff_filespec *s)
-{
-	struct strbuf buf = STRBUF_INIT;
-	size_t size = 0;
-
-	if (strbuf_read(&buf, 0, 0) < 0)
-		return error("error while reading from stdin %s",
-				     strerror(errno));
-
-	s->should_munmap = 0;
-	s->data = strbuf_detach(&buf, &size);
-	s->size = size;
-	s->should_free = 1;
-	return 0;
-}
-
 static int diff_populate_gitlink(struct diff_filespec *s, int size_only)
 {
 	int len;
-	char *data = xmalloc(100);
+	char *data = xmalloc(100), *dirty = "";
+
+	/* Are we looking at the work tree? */
+	if (s->dirty_submodule)
+		dirty = "-dirty";
+
 	len = snprintf(data, 100,
-		"Subproject commit %s\n", sha1_to_hex(s->sha1));
+		       "Subproject commit %s%s\n", sha1_to_hex(s->sha1), dirty);
 	s->data = data;
 	s->size = len;
 	s->should_free = 1;
@@ -1878,9 +2668,6 @@
 		struct stat st;
 		int fd;
 
-		if (!strcmp(s->path, "-"))
-			return populate_from_stdin(s);
-
 		if (lstat(s->path, &st) < 0) {
 			if (errno == ENOENT) {
 			err_empty:
@@ -1927,10 +2714,14 @@
 	}
 	else {
 		enum object_type type;
-		if (size_only)
+		if (size_only) {
 			type = sha1_object_info(s->sha1, &s->size);
-		else {
+			if (type < 0)
+				die("unable to read %s", sha1_to_hex(s->sha1));
+		} else {
 			s->data = read_sha1_file(s->sha1, &type, &s->size);
+			if (!s->data)
+				die("unable to read %s", sha1_to_hex(s->sha1));
 			s->should_free = 1;
 		}
 	}
@@ -2102,7 +2893,7 @@
 	}
 	*arg = NULL;
 	fflush(NULL);
-	retval = run_command_v_opt(spawn_arg, 0);
+	retval = run_command_v_opt(spawn_arg, RUN_USING_SHELL);
 	remove_tempfile();
 	if (retval) {
 		fprintf(stderr, "external diff died, stopping at %s.\n", name);
@@ -2121,36 +2912,53 @@
 			  struct diff_filespec *one,
 			  struct diff_filespec *two,
 			  struct diff_options *o,
-			  struct diff_filepair *p)
+			  struct diff_filepair *p,
+			  int *must_show_header,
+			  int use_color)
 {
+	const char *set = diff_get_color(use_color, DIFF_METAINFO);
+	const char *reset = diff_get_color(use_color, DIFF_RESET);
+	struct strbuf *msgbuf;
+	char *line_prefix = "";
+
+	*must_show_header = 1;
+	if (o->output_prefix) {
+		msgbuf = o->output_prefix(o, o->output_prefix_data);
+		line_prefix = msgbuf->buf;
+	}
 	strbuf_init(msg, PATH_MAX * 2 + 300);
 	switch (p->status) {
 	case DIFF_STATUS_COPIED:
-		strbuf_addf(msg, "similarity index %d%%", similarity_index(p));
-		strbuf_addstr(msg, "\ncopy from ");
+		strbuf_addf(msg, "%s%ssimilarity index %d%%",
+			    line_prefix, set, similarity_index(p));
+		strbuf_addf(msg, "%s\n%s%scopy from ",
+			    reset,  line_prefix, set);
 		quote_c_style(name, msg, NULL, 0);
-		strbuf_addstr(msg, "\ncopy to ");
+		strbuf_addf(msg, "%s\n%s%scopy to ", reset, line_prefix, set);
 		quote_c_style(other, msg, NULL, 0);
-		strbuf_addch(msg, '\n');
+		strbuf_addf(msg, "%s\n", reset);
 		break;
 	case DIFF_STATUS_RENAMED:
-		strbuf_addf(msg, "similarity index %d%%", similarity_index(p));
-		strbuf_addstr(msg, "\nrename from ");
+		strbuf_addf(msg, "%s%ssimilarity index %d%%",
+			    line_prefix, set, similarity_index(p));
+		strbuf_addf(msg, "%s\n%s%srename from ",
+			    reset, line_prefix, set);
 		quote_c_style(name, msg, NULL, 0);
-		strbuf_addstr(msg, "\nrename to ");
+		strbuf_addf(msg, "%s\n%s%srename to ",
+			    reset, line_prefix, set);
 		quote_c_style(other, msg, NULL, 0);
-		strbuf_addch(msg, '\n');
+		strbuf_addf(msg, "%s\n", reset);
 		break;
 	case DIFF_STATUS_MODIFIED:
 		if (p->score) {
-			strbuf_addf(msg, "dissimilarity index %d%%\n",
-				    similarity_index(p));
+			strbuf_addf(msg, "%s%sdissimilarity index %d%%%s\n",
+				    line_prefix,
+				    set, similarity_index(p), reset);
 			break;
 		}
 		/* fallthru */
 	default:
-		/* nothing */
-		;
+		*must_show_header = 0;
 	}
 	if (one && two && hashcmp(one->sha1, two->sha1)) {
 		int abbrev = DIFF_OPT_TST(o, FULL_INDEX) ? 40 : DEFAULT_ABBREV;
@@ -2161,15 +2969,13 @@
 			    (!fill_mmfile(&mf, two) && diff_filespec_is_binary(two)))
 				abbrev = 40;
 		}
-		strbuf_addf(msg, "index %.*s..%.*s",
-			    abbrev, sha1_to_hex(one->sha1),
-			    abbrev, sha1_to_hex(two->sha1));
+		strbuf_addf(msg, "%s%sindex %s..", line_prefix, set,
+			    find_unique_abbrev(one->sha1, abbrev));
+		strbuf_addstr(msg, find_unique_abbrev(two->sha1, abbrev));
 		if (one->mode == two->mode)
 			strbuf_addf(msg, " %06o", one->mode);
-		strbuf_addch(msg, '\n');
+		strbuf_addf(msg, "%s\n", reset);
 	}
-	if (msg->len)
-		strbuf_setlen(msg, msg->len - 1);
 }
 
 static void run_diff_cmd(const char *pgm,
@@ -2184,20 +2990,26 @@
 {
 	const char *xfrm_msg = NULL;
 	int complete_rewrite = (p->status == DIFF_STATUS_MODIFIED) && p->score;
+	int must_show_header = 0;
 
-	if (msg) {
-		fill_metainfo(msg, name, other, one, two, o, p);
-		xfrm_msg = msg->len ? msg->buf : NULL;
-	}
 
-	if (!DIFF_OPT_TST(o, ALLOW_EXTERNAL))
-		pgm = NULL;
-	else {
+	if (DIFF_OPT_TST(o, ALLOW_EXTERNAL)) {
 		struct userdiff_driver *drv = userdiff_find_by_path(attr_path);
 		if (drv && drv->external)
 			pgm = drv->external;
 	}
 
+	if (msg) {
+		/*
+		 * don't use colors when the header is intended for an
+		 * external diff driver
+		 */
+		fill_metainfo(msg, name, other, one, two, o, p,
+			      &must_show_header,
+			      want_color(o->use_color) && !pgm);
+		xfrm_msg = msg->len ? msg->buf : NULL;
+	}
+
 	if (pgm) {
 		run_external_diff(pgm, name, other, one, two, xfrm_msg,
 				  complete_rewrite);
@@ -2205,7 +3017,8 @@
 	}
 	if (one && two)
 		builtin_diff(name, other ? other : name,
-			     one, two, xfrm_msg, o, complete_rewrite);
+			     one, two, xfrm_msg, must_show_header,
+			     o, complete_rewrite);
 	else
 		fprintf(o->file, "* Unmerged path %s\n", name);
 }
@@ -2215,7 +3028,7 @@
 	if (DIFF_FILE_VALID(one)) {
 		if (!one->sha1_valid) {
 			struct stat st;
-			if (!strcmp(one->path, "-")) {
+			if (one->is_stdin) {
 				hashcpy(one->sha1, null_sha1);
 				return;
 			}
@@ -2232,10 +3045,16 @@
 static void strip_prefix(int prefix_length, const char **namep, const char **otherp)
 {
 	/* Strip the prefix but do not molest /dev/null and absolute paths */
-	if (*namep && **namep != '/')
+	if (*namep && **namep != '/') {
 		*namep += prefix_length;
-	if (*otherp && **otherp != '/')
+		if (**namep == '/')
+			++*namep;
+	}
+	if (*otherp && **otherp != '/') {
 		*otherp += prefix_length;
+		if (**otherp == '/')
+			++*otherp;
+	}
 }
 
 static void run_diff(struct diff_filepair *p, struct diff_options *o)
@@ -2254,6 +3073,9 @@
 	if (o->prefix_length)
 		strip_prefix(o->prefix_length, &name, &other);
 
+	if (!DIFF_OPT_TST(o, ALLOW_EXTERNAL))
+		pgm = NULL;
+
 	if (DIFF_PAIR_UNMERGED(p)) {
 		run_diff_cmd(pgm, name, NULL, attr_path,
 			     NULL, NULL, NULL, o, p);
@@ -2341,23 +3163,25 @@
 
 void diff_setup(struct diff_options *options)
 {
-	memset(options, 0, sizeof(*options));
+	memcpy(options, &default_diff_options, sizeof(*options));
 
 	options->file = stdout;
 
 	options->line_termination = '\n';
 	options->break_opt = -1;
 	options->rename_limit = -1;
-	options->dirstat_percent = 3;
+	options->dirstat_permille = diff_dirstat_permille_default;
 	options->context = 3;
+	DIFF_OPT_SET(options, RENAME_EMPTY);
 
 	options->change = diff_change;
 	options->add_remove = diff_addremove;
-	if (diff_use_color_default > 0)
-		DIFF_OPT_SET(options, COLOR_DIFF);
+	options->use_color = diff_use_color_default;
 	options->detect_rename = diff_detect_rename_default;
 
-	if (!diff_mnemonic_prefix) {
+	if (diff_no_prefix) {
+		options->a_prefix = options->b_prefix = "";
+	} else if (!diff_mnemonic_prefix) {
 		options->a_prefix = "a/";
 		options->b_prefix = "b/";
 	}
@@ -2378,6 +3202,20 @@
 	if (count > 1)
 		die("--name-only, --name-status, --check and -s are mutually exclusive");
 
+	/*
+	 * Most of the time we can say "there are changes"
+	 * only by checking if there are changed paths, but
+	 * --ignore-whitespace* options force us to look
+	 * inside contents.
+	 */
+
+	if (DIFF_XDL_TST(options, IGNORE_WHITESPACE) ||
+	    DIFF_XDL_TST(options, IGNORE_WHITESPACE_CHANGE) ||
+	    DIFF_XDL_TST(options, IGNORE_WHITESPACE_AT_EOL))
+		DIFF_OPT_SET(options, DIFF_FROM_CONTENTS);
+	else
+		DIFF_OPT_CLR(options, DIFF_FROM_CONTENTS);
+
 	if (DIFF_OPT_TST(options, FIND_COPIES_HARDER))
 		options->detect_rename = DIFF_DETECT_COPY;
 
@@ -2417,6 +3255,12 @@
 	 */
 	if (options->pickaxe)
 		DIFF_OPT_SET(options, RECURSIVE);
+	/*
+	 * When patches are generated, submodules diffed against the work tree
+	 * must be checked for dirtiness too so it can be shown in the output
+	 */
+	if (options->output_format & DIFF_FORMAT_PATCH)
+		DIFF_OPT_SET(options, DIRTY_SUBMODULES);
 
 	if (options->detect_rename && options->rename_limit < 0)
 		options->rename_limit = diff_rename_limit_default;
@@ -2438,7 +3282,7 @@
 	 * to have found.  It does not make sense not to return with
 	 * exit code in such a case either.
 	 */
-	if (DIFF_OPT_TST(options, QUIET)) {
+	if (DIFF_OPT_TST(options, QUICK)) {
 		options->output_format = DIFF_FORMAT_NO_OUTPUT;
 		DIFF_OPT_SET(options, EXIT_WITH_STATUS);
 	}
@@ -2495,12 +3339,144 @@
 
 static int diff_scoreopt_parse(const char *opt);
 
+static inline int short_opt(char opt, const char **argv,
+			    const char **optarg)
+{
+	const char *arg = argv[0];
+	if (arg[0] != '-' || arg[1] != opt)
+		return 0;
+	if (arg[2] != '\0') {
+		*optarg = arg + 2;
+		return 1;
+	}
+	if (!argv[1])
+		die("Option '%c' requires a value", opt);
+	*optarg = argv[1];
+	return 2;
+}
+
+int parse_long_opt(const char *opt, const char **argv,
+		   const char **optarg)
+{
+	const char *arg = argv[0];
+	if (arg[0] != '-' || arg[1] != '-')
+		return 0;
+	arg += strlen("--");
+	if (prefixcmp(arg, opt))
+		return 0;
+	arg += strlen(opt);
+	if (*arg == '=') { /* sticked form: --option=value */
+		*optarg = arg + 1;
+		return 1;
+	}
+	if (*arg != '\0')
+		return 0;
+	/* separate form: --option value */
+	if (!argv[1])
+		die("Option '--%s' requires a value", opt);
+	*optarg = argv[1];
+	return 2;
+}
+
+static int stat_opt(struct diff_options *options, const char **av)
+{
+	const char *arg = av[0];
+	char *end;
+	int width = options->stat_width;
+	int name_width = options->stat_name_width;
+	int graph_width = options->stat_graph_width;
+	int count = options->stat_count;
+	int argcount = 1;
+
+	arg += strlen("--stat");
+	end = (char *)arg;
+
+	switch (*arg) {
+	case '-':
+		if (!prefixcmp(arg, "-width")) {
+			arg += strlen("-width");
+			if (*arg == '=')
+				width = strtoul(arg + 1, &end, 10);
+			else if (!*arg && !av[1])
+				die("Option '--stat-width' requires a value");
+			else if (!*arg) {
+				width = strtoul(av[1], &end, 10);
+				argcount = 2;
+			}
+		} else if (!prefixcmp(arg, "-name-width")) {
+			arg += strlen("-name-width");
+			if (*arg == '=')
+				name_width = strtoul(arg + 1, &end, 10);
+			else if (!*arg && !av[1])
+				die("Option '--stat-name-width' requires a value");
+			else if (!*arg) {
+				name_width = strtoul(av[1], &end, 10);
+				argcount = 2;
+			}
+		} else if (!prefixcmp(arg, "-graph-width")) {
+			arg += strlen("-graph-width");
+			if (*arg == '=')
+				graph_width = strtoul(arg + 1, &end, 10);
+			else if (!*arg && !av[1])
+				die("Option '--stat-graph-width' requires a value");
+			else if (!*arg) {
+				graph_width = strtoul(av[1], &end, 10);
+				argcount = 2;
+			}
+		} else if (!prefixcmp(arg, "-count")) {
+			arg += strlen("-count");
+			if (*arg == '=')
+				count = strtoul(arg + 1, &end, 10);
+			else if (!*arg && !av[1])
+				die("Option '--stat-count' requires a value");
+			else if (!*arg) {
+				count = strtoul(av[1], &end, 10);
+				argcount = 2;
+			}
+		}
+		break;
+	case '=':
+		width = strtoul(arg+1, &end, 10);
+		if (*end == ',')
+			name_width = strtoul(end+1, &end, 10);
+		if (*end == ',')
+			count = strtoul(end+1, &end, 10);
+	}
+
+	/* Important! This checks all the error cases! */
+	if (*end)
+		return 0;
+	options->output_format |= DIFF_FORMAT_DIFFSTAT;
+	options->stat_name_width = name_width;
+	options->stat_graph_width = graph_width;
+	options->stat_width = width;
+	options->stat_count = count;
+	return argcount;
+}
+
+static int parse_dirstat_opt(struct diff_options *options, const char *params)
+{
+	struct strbuf errmsg = STRBUF_INIT;
+	if (parse_dirstat_params(options, params, &errmsg))
+		die(_("Failed to parse --dirstat/-X option parameter:\n%s"),
+		    errmsg.buf);
+	strbuf_release(&errmsg);
+	/*
+	 * The caller knows a dirstat-related option is given from the command
+	 * line; allow it to say "return this_function();"
+	 */
+	options->output_format |= DIFF_FORMAT_DIRSTAT;
+	return 1;
+}
+
 int diff_opt_parse(struct diff_options *options, const char **av, int ac)
 {
 	const char *arg = av[0];
+	const char *optarg;
+	int argcount;
 
 	/* Output format options */
-	if (!strcmp(arg, "-p") || !strcmp(arg, "-u"))
+	if (!strcmp(arg, "-p") || !strcmp(arg, "-u") || !strcmp(arg, "--patch"))
 		options->output_format |= DIFF_FORMAT_PATCH;
 	else if (opt_arg(arg, 'U', "unified", &options->context))
 		options->output_format |= DIFF_FORMAT_PATCH;
@@ -2512,15 +3488,19 @@
 		options->output_format |= DIFF_FORMAT_NUMSTAT;
 	else if (!strcmp(arg, "--shortstat"))
 		options->output_format |= DIFF_FORMAT_SHORTSTAT;
-	else if (opt_arg(arg, 'X', "dirstat", &options->dirstat_percent))
-		options->output_format |= DIFF_FORMAT_DIRSTAT;
-	else if (!strcmp(arg, "--cumulative")) {
-		options->output_format |= DIFF_FORMAT_DIRSTAT;
-		DIFF_OPT_SET(options, DIRSTAT_CUMULATIVE);
-	} else if (opt_arg(arg, 0, "dirstat-by-file",
-			   &options->dirstat_percent)) {
-		options->output_format |= DIFF_FORMAT_DIRSTAT;
-		DIFF_OPT_SET(options, DIRSTAT_BY_FILE);
+	else if (!strcmp(arg, "-X") || !strcmp(arg, "--dirstat"))
+		return parse_dirstat_opt(options, "");
+	else if (!prefixcmp(arg, "-X"))
+		return parse_dirstat_opt(options, arg + 2);
+	else if (!prefixcmp(arg, "--dirstat="))
+		return parse_dirstat_opt(options, arg + 10);
+	else if (!strcmp(arg, "--cumulative"))
+		return parse_dirstat_opt(options, "cumulative");
+	else if (!strcmp(arg, "--dirstat-by-file"))
+		return parse_dirstat_opt(options, "files");
+	else if (!prefixcmp(arg, "--dirstat-by-file=")) {
+		parse_dirstat_opt(options, "files");
+		return parse_dirstat_opt(options, arg + 18);
 	}
 	else if (!strcmp(arg, "--check"))
 		options->output_format |= DIFF_FORMAT_CHECKDIFF;
@@ -2534,53 +3514,39 @@
 		options->output_format |= DIFF_FORMAT_NAME_STATUS;
 	else if (!strcmp(arg, "-s"))
 		options->output_format |= DIFF_FORMAT_NO_OUTPUT;
-	else if (!prefixcmp(arg, "--stat")) {
-		char *end;
-		int width = options->stat_width;
-		int name_width = options->stat_name_width;
-		arg += 6;
-		end = (char *)arg;
-
-		switch (*arg) {
-		case '-':
-			if (!prefixcmp(arg, "-width="))
-				width = strtoul(arg + 7, &end, 10);
-			else if (!prefixcmp(arg, "-name-width="))
-				name_width = strtoul(arg + 12, &end, 10);
-			break;
-		case '=':
-			width = strtoul(arg+1, &end, 10);
-			if (*end == ',')
-				name_width = strtoul(end+1, &end, 10);
-		}
-
-		/* Important! This checks all the error cases! */
-		if (*end)
-			return 0;
-		options->output_format |= DIFF_FORMAT_DIFFSTAT;
-		options->stat_name_width = name_width;
-		options->stat_width = width;
-	}
+	else if (!prefixcmp(arg, "--stat"))
+		/* --stat, --stat-width, --stat-name-width, or --stat-count */
+		return stat_opt(options, av);
 
 	/* renames options */
-	else if (!prefixcmp(arg, "-B")) {
+	else if (!prefixcmp(arg, "-B") || !prefixcmp(arg, "--break-rewrites=") ||
+		 !strcmp(arg, "--break-rewrites")) {
 		if ((options->break_opt = diff_scoreopt_parse(arg)) == -1)
-			return -1;
+			return error("invalid argument to -B: %s", arg+2);
 	}
-	else if (!prefixcmp(arg, "-M")) {
+	else if (!prefixcmp(arg, "-M") || !prefixcmp(arg, "--find-renames=") ||
+		 !strcmp(arg, "--find-renames")) {
 		if ((options->rename_score = diff_scoreopt_parse(arg)) == -1)
-			return -1;
+			return error("invalid argument to -M: %s", arg+2);
 		options->detect_rename = DIFF_DETECT_RENAME;
 	}
-	else if (!prefixcmp(arg, "-C")) {
+	else if (!strcmp(arg, "-D") || !strcmp(arg, "--irreversible-delete")) {
+		options->irreversible_delete = 1;
+	}
+	else if (!prefixcmp(arg, "-C") || !prefixcmp(arg, "--find-copies=") ||
+		 !strcmp(arg, "--find-copies")) {
 		if (options->detect_rename == DIFF_DETECT_COPY)
 			DIFF_OPT_SET(options, FIND_COPIES_HARDER);
 		if ((options->rename_score = diff_scoreopt_parse(arg)) == -1)
-			return -1;
+			return error("invalid argument to -C: %s", arg+2);
 		options->detect_rename = DIFF_DETECT_COPY;
 	}
 	else if (!strcmp(arg, "--no-renames"))
 		options->detect_rename = 0;
+	else if (!strcmp(arg, "--rename-empty"))
+		DIFF_OPT_SET(options, RENAME_EMPTY);
+	else if (!strcmp(arg, "--no-rename-empty"))
+		DIFF_OPT_CLR(options, RENAME_EMPTY);
 	else if (!strcmp(arg, "--relative"))
 		DIFF_OPT_SET(options, RELATIVE_NAME);
 	else if (!prefixcmp(arg, "--relative=")) {
@@ -2589,6 +3555,10 @@
 	}
 
 	/* xdiff options */
+	else if (!strcmp(arg, "--minimal"))
+		DIFF_XDL_SET(options, NEED_MINIMAL);
+	else if (!strcmp(arg, "--no-minimal"))
+		DIFF_XDL_CLR(options, NEED_MINIMAL);
 	else if (!strcmp(arg, "-w") || !strcmp(arg, "--ignore-all-space"))
 		DIFF_XDL_SET(options, IGNORE_WHITESPACE);
 	else if (!strcmp(arg, "-b") || !strcmp(arg, "--ignore-space-change"))
@@ -2596,7 +3566,9 @@
 	else if (!strcmp(arg, "--ignore-space-at-eol"))
 		DIFF_XDL_SET(options, IGNORE_WHITESPACE_AT_EOL);
 	else if (!strcmp(arg, "--patience"))
-		DIFF_XDL_SET(options, PATIENCE_DIFF);
+		options->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF);
+	else if (!strcmp(arg, "--histogram"))
+		options->xdl_opts = DIFF_WITH_ALG(options, HISTOGRAM_DIFF);
 
 	/* flags options */
 	else if (!strcmp(arg, "--binary")) {
@@ -2614,22 +3586,53 @@
 	else if (!strcmp(arg, "--follow"))
 		DIFF_OPT_SET(options, FOLLOW_RENAMES);
 	else if (!strcmp(arg, "--color"))
-		DIFF_OPT_SET(options, COLOR_DIFF);
+		options->use_color = 1;
+	else if (!prefixcmp(arg, "--color=")) {
+		int value = git_config_colorbool(NULL, arg+8);
+		if (value < 0)
+			return error("option `color' expects \"always\", \"auto\", or \"never\"");
+		options->use_color = value;
+	}
 	else if (!strcmp(arg, "--no-color"))
-		DIFF_OPT_CLR(options, COLOR_DIFF);
+		options->use_color = 0;
 	else if (!strcmp(arg, "--color-words")) {
-		DIFF_OPT_SET(options, COLOR_DIFF);
-		DIFF_OPT_SET(options, COLOR_DIFF_WORDS);
+		options->use_color = 1;
+		options->word_diff = DIFF_WORDS_COLOR;
 	}
 	else if (!prefixcmp(arg, "--color-words=")) {
-		DIFF_OPT_SET(options, COLOR_DIFF);
-		DIFF_OPT_SET(options, COLOR_DIFF_WORDS);
+		options->use_color = 1;
+		options->word_diff = DIFF_WORDS_COLOR;
 		options->word_regex = arg + 14;
 	}
+	else if (!strcmp(arg, "--word-diff")) {
+		if (options->word_diff == DIFF_WORDS_NONE)
+			options->word_diff = DIFF_WORDS_PLAIN;
+	}
+	else if (!prefixcmp(arg, "--word-diff=")) {
+		const char *type = arg + 12;
+		if (!strcmp(type, "plain"))
+			options->word_diff = DIFF_WORDS_PLAIN;
+		else if (!strcmp(type, "color")) {
+			options->use_color = 1;
+			options->word_diff = DIFF_WORDS_COLOR;
+		}
+		else if (!strcmp(type, "porcelain"))
+			options->word_diff = DIFF_WORDS_PORCELAIN;
+		else if (!strcmp(type, "none"))
+			options->word_diff = DIFF_WORDS_NONE;
+		else
+			die("bad --word-diff argument: %s", type);
+	}
+	else if ((argcount = parse_long_opt("word-diff-regex", av, &optarg))) {
+		if (options->word_diff == DIFF_WORDS_NONE)
+			options->word_diff = DIFF_WORDS_PLAIN;
+		options->word_regex = optarg;
+		return argcount;
+	}
 	else if (!strcmp(arg, "--exit-code"))
 		DIFF_OPT_SET(options, EXIT_WITH_STATUS);
 	else if (!strcmp(arg, "--quiet"))
-		DIFF_OPT_SET(options, QUIET);
+		DIFF_OPT_SET(options, QUICK);
 	else if (!strcmp(arg, "--ext-diff"))
 		DIFF_OPT_SET(options, ALLOW_EXTERNAL);
 	else if (!strcmp(arg, "--no-ext-diff"))
@@ -2638,24 +3641,47 @@
 		DIFF_OPT_SET(options, ALLOW_TEXTCONV);
 	else if (!strcmp(arg, "--no-textconv"))
 		DIFF_OPT_CLR(options, ALLOW_TEXTCONV);
-	else if (!strcmp(arg, "--ignore-submodules"))
-		DIFF_OPT_SET(options, IGNORE_SUBMODULES);
+	else if (!strcmp(arg, "--ignore-submodules")) {
+		DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG);
+		handle_ignore_submodules_arg(options, "all");
+	} else if (!prefixcmp(arg, "--ignore-submodules=")) {
+		DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG);
+		handle_ignore_submodules_arg(options, arg + 20);
+	} else if (!strcmp(arg, "--submodule"))
+		DIFF_OPT_SET(options, SUBMODULE_LOG);
+	else if (!prefixcmp(arg, "--submodule=")) {
+		if (!strcmp(arg + 12, "log"))
+			DIFF_OPT_SET(options, SUBMODULE_LOG);
+	}
 
 	/* misc options */
 	else if (!strcmp(arg, "-z"))
 		options->line_termination = 0;
-	else if (!prefixcmp(arg, "-l"))
-		options->rename_limit = strtoul(arg+2, NULL, 10);
-	else if (!prefixcmp(arg, "-S"))
-		options->pickaxe = arg + 2;
+	else if ((argcount = short_opt('l', av, &optarg))) {
+		options->rename_limit = strtoul(optarg, NULL, 10);
+		return argcount;
+	}
+	else if ((argcount = short_opt('S', av, &optarg))) {
+		options->pickaxe = optarg;
+		options->pickaxe_opts |= DIFF_PICKAXE_KIND_S;
+		return argcount;
+	} else if ((argcount = short_opt('G', av, &optarg))) {
+		options->pickaxe = optarg;
+		options->pickaxe_opts |= DIFF_PICKAXE_KIND_G;
+		return argcount;
+	}
 	else if (!strcmp(arg, "--pickaxe-all"))
-		options->pickaxe_opts = DIFF_PICKAXE_ALL;
+		options->pickaxe_opts |= DIFF_PICKAXE_ALL;
 	else if (!strcmp(arg, "--pickaxe-regex"))
-		options->pickaxe_opts = DIFF_PICKAXE_REGEX;
-	else if (!prefixcmp(arg, "-O"))
-		options->orderfile = arg + 2;
-	else if (!prefixcmp(arg, "--diff-filter="))
-		options->filter = arg + 14;
+		options->pickaxe_opts |= DIFF_PICKAXE_REGEX;
+	else if ((argcount = short_opt('O', av, &optarg))) {
+		options->orderfile = optarg;
+		return argcount;
+	}
+	else if ((argcount = parse_long_opt("diff-filter", av, &optarg))) {
+		options->filter = optarg;
+		return argcount;
+	}
 	else if (!strcmp(arg, "--abbrev"))
 		options->abbrev = DEFAULT_ABBREV;
 	else if (!prefixcmp(arg, "--abbrev=")) {
@@ -2665,24 +3691,37 @@
 		else if (40 < options->abbrev)
 			options->abbrev = 40;
 	}
-	else if (!prefixcmp(arg, "--src-prefix="))
-		options->a_prefix = arg + 13;
-	else if (!prefixcmp(arg, "--dst-prefix="))
-		options->b_prefix = arg + 13;
+	else if ((argcount = parse_long_opt("src-prefix", av, &optarg))) {
+		options->a_prefix = optarg;
+		return argcount;
+	}
+	else if ((argcount = parse_long_opt("dst-prefix", av, &optarg))) {
+		options->b_prefix = optarg;
+		return argcount;
+	}
 	else if (!strcmp(arg, "--no-prefix"))
 		options->a_prefix = options->b_prefix = "";
 	else if (opt_arg(arg, '\0', "inter-hunk-context",
 			 &options->interhunkcontext))
 		;
-	else if (!prefixcmp(arg, "--output=")) {
-		options->file = fopen(arg + strlen("--output="), "w");
+	else if (!strcmp(arg, "-W"))
+		DIFF_OPT_SET(options, FUNCCONTEXT);
+	else if (!strcmp(arg, "--function-context"))
+		DIFF_OPT_SET(options, FUNCCONTEXT);
+	else if (!strcmp(arg, "--no-function-context"))
+		DIFF_OPT_CLR(options, FUNCCONTEXT);
+	else if ((argcount = parse_long_opt("output", av, &optarg))) {
+		options->file = fopen(optarg, "w");
+		if (!options->file)
+			die_errno("Could not open '%s'", optarg);
 		options->close_file = 1;
+		return argcount;
 	} else
 		return 0;
 	return 1;
 }
 
-static int parse_num(const char **cp_p)
+int parse_rename_score(const char **cp_p)
 {
 	unsigned long num, scale;
 	int ch, dot;
@@ -2691,7 +3730,7 @@
 	num = 0;
 	scale = 1;
 	dot = 0;
-	for(;;) {
+	for (;;) {
 		ch = *cp;
 		if ( !dot && ch == '.' ) {
 			scale = 1;
@@ -2725,10 +3764,26 @@
 	if (*opt++ != '-')
 		return -1;
 	cmd = *opt++;
+	if (cmd == '-') {
+		/* convert the long-form arguments into short-form versions */
+		if (!prefixcmp(opt, "break-rewrites")) {
+			opt += strlen("break-rewrites");
+			if (*opt == 0 || *opt++ == '=')
+				cmd = 'B';
+		} else if (!prefixcmp(opt, "find-copies")) {
+			opt += strlen("find-copies");
+			if (*opt == 0 || *opt++ == '=')
+				cmd = 'C';
+		} else if (!prefixcmp(opt, "find-renames")) {
+			opt += strlen("find-renames");
+			if (*opt == 0 || *opt++ == '=')
+				cmd = 'M';
+		}
+	}
 	if (cmd != 'M' && cmd != 'C' && cmd != 'B')
 		return -1; /* that is not a -M, -C nor -B option */
 
-	opt1 = parse_num(&opt);
+	opt1 = parse_rename_score(&opt);
 	if (cmd != 'B')
 		opt2 = 0;
 	else {
@@ -2738,7 +3793,7 @@
 			return -1; /* we expect -B80/99 or -B80 */
 		else {
 			opt++;
-			opt2 = parse_num(&opt);
+			opt2 = parse_rename_score(&opt);
 		}
 	}
 	if (*opt != 0)
@@ -2804,6 +3859,11 @@
 {
 	int line_termination = opt->line_termination;
 	int inter_name_termination = line_termination ? '\t' : '\0';
+	if (opt->output_prefix) {
+		struct strbuf *msg = NULL;
+		msg = opt->output_prefix(opt, opt->output_prefix_data);
+		fprintf(opt->file, "%s", msg->buf);
+	}
 
 	if (!(opt->output_format & DIFF_FORMAT_NAME_STATUS)) {
 		fprintf(opt->file, ":%06o %06o %s ", p->one->mode, p->two->mode,
@@ -2858,7 +3918,8 @@
 	 * dealing with a change.
 	 */
 	if (one->sha1_valid && two->sha1_valid &&
-	    !hashcmp(one->sha1, two->sha1))
+	    !hashcmp(one->sha1, two->sha1) &&
+	    !one->dirty_submodule && !two->dirty_submodule)
 		return 1; /* no change */
 	if (!one->sha1_valid && !two->sha1_valid)
 		return 1; /* both look at the same file on the filesystem. */
@@ -2885,7 +3946,7 @@
 
 	if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||
 	    (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))
-		return; /* no tree diffs in patch format */
+		return; /* no useful stat for tree diffs */
 
 	run_diffstat(p, o, diffstat);
 }
@@ -2898,7 +3959,7 @@
 
 	if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||
 	    (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))
-		return; /* no tree diffs in patch format */
+		return; /* nothing to check in tree diffs */
 
 	run_checkdiff(p, o);
 }
@@ -2993,6 +4054,8 @@
 		}
 		else if (hashcmp(p->one->sha1, p->two->sha1) ||
 			 p->one->mode != p->two->mode ||
+			 p->one->dirty_submodule ||
+			 p->two->dirty_submodule ||
 			 is_null_sha1(p->one->sha1))
 			p->status = DIFF_STATUS_MODIFIED;
 		else {
@@ -3046,48 +4109,62 @@
 }
 
 
-static void show_mode_change(FILE *file, struct diff_filepair *p, int show_name)
+static void show_mode_change(FILE *file, struct diff_filepair *p, int show_name,
+		const char *line_prefix)
 {
 	if (p->one->mode && p->two->mode && p->one->mode != p->two->mode) {
-		fprintf(file, " mode change %06o => %06o%c", p->one->mode, p->two->mode,
-			show_name ? ' ' : '\n');
+		fprintf(file, "%s mode change %06o => %06o%c", line_prefix, p->one->mode,
+			p->two->mode, show_name ? ' ' : '\n');
 		if (show_name) {
 			write_name_quoted(p->two->path, file, '\n');
 		}
 	}
 }
 
-static void show_rename_copy(FILE *file, const char *renamecopy, struct diff_filepair *p)
+static void show_rename_copy(FILE *file, const char *renamecopy, struct diff_filepair *p,
+			const char *line_prefix)
 {
 	char *names = pprint_rename(p->one->path, p->two->path);
 
 	fprintf(file, " %s %s (%d%%)\n", renamecopy, names, similarity_index(p));
 	free(names);
-	show_mode_change(file, p, 0);
+	show_mode_change(file, p, 0, line_prefix);
 }
 
-static void diff_summary(FILE *file, struct diff_filepair *p)
+static void diff_summary(struct diff_options *opt, struct diff_filepair *p)
 {
+	FILE *file = opt->file;
+	char *line_prefix = "";
+
+	if (opt->output_prefix) {
+		struct strbuf *buf = opt->output_prefix(opt, opt->output_prefix_data);
+		line_prefix = buf->buf;
+	}
+
 	switch(p->status) {
 	case DIFF_STATUS_DELETED:
+		fputs(line_prefix, file);
 		show_file_mode_name(file, "delete", p->one);
 		break;
 	case DIFF_STATUS_ADDED:
+		fputs(line_prefix, file);
 		show_file_mode_name(file, "create", p->two);
 		break;
 	case DIFF_STATUS_COPIED:
-		show_rename_copy(file, "copy", p);
+		fputs(line_prefix, file);
+		show_rename_copy(file, "copy", p, line_prefix);
 		break;
 	case DIFF_STATUS_RENAMED:
-		show_rename_copy(file, "rename", p);
+		fputs(line_prefix, file);
+		show_rename_copy(file, "rename", p, line_prefix);
 		break;
 	default:
 		if (p->score) {
-			fputs(" rewrite ", file);
+			fprintf(file, "%s rewrite ", line_prefix);
 			write_name_quoted(p->two->path, file, ' ');
 			fprintf(file, "(%d%%)\n", similarity_index(p));
 		}
-		show_mode_change(file, p, !p->score);
+		show_mode_change(file, p, !p->score, line_prefix);
 		break;
 	}
 }
@@ -3141,7 +4218,6 @@
 	for (i = 0; i < q->nr; i++) {
 		xpparam_t xpp;
 		xdemitconf_t xecfg;
-		xdemitcb_t ecb;
 		mmfile_t mf1, mf2;
 		struct diff_filepair *p = q->queue[i];
 		int len1, len2;
@@ -3199,11 +4275,18 @@
 					len2, p->two->path);
 		git_SHA1_Update(&ctx, buffer, len1);
 
-		xpp.flags = XDF_NEED_MINIMAL;
+		if (diff_filespec_is_binary(p->one) ||
+		    diff_filespec_is_binary(p->two)) {
+			git_SHA1_Update(&ctx, sha1_to_hex(p->one->sha1), 40);
+			git_SHA1_Update(&ctx, sha1_to_hex(p->two->sha1), 40);
+			continue;
+		}
+
+		xpp.flags = 0;
 		xecfg.ctxlen = 3;
-		xecfg.flags = XDL_EMIT_FUNCNAMES;
+		xecfg.flags = 0;
 		xdi_diff_outf(&mf1, &mf2, patch_id_consume, &data,
-			      &xpp, &xecfg, &ecb);
+			      &xpp, &xecfg);
 	}
 
 	git_SHA1_Final(sha1, &ctx);
@@ -3220,8 +4303,7 @@
 		diff_free_filepair(q->queue[i]);
 
 	free(q->queue);
-	q->queue = NULL;
-	q->nr = q->alloc = 0;
+	DIFF_QUEUE_CLEAR(q);
 
 	return result;
 }
@@ -3251,11 +4333,34 @@
 	return 1;
 }
 
+static const char rename_limit_warning[] =
+"inexact rename detection was skipped due to too many files.";
+
+static const char degrade_cc_to_c_warning[] =
+"only found copies from modified paths due to too many files.";
+
+static const char rename_limit_advice[] =
+"you may want to set your %s variable to at least "
+"%d and retry the command.";
+
+void diff_warn_rename_limit(const char *varname, int needed, int degraded_cc)
+{
+	if (degraded_cc)
+		warning(degrade_cc_to_c_warning);
+	else if (needed)
+		warning(rename_limit_warning);
+	else
+		return;
+	if (0 < needed && needed < 32767)
+		warning(rename_limit_advice, varname, needed);
+}
+
 void diff_flush(struct diff_options *options)
 {
 	struct diff_queue_struct *q = &diff_queued_diff;
 	int i, output_format = options->output_format;
 	int separator = 0;
+	int dirstat_by_line = 0;
 
 	/*
 	 * Order: raw, stat, summary, patch
@@ -3276,7 +4381,11 @@
 		separator++;
 	}
 
-	if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT)) {
+	if (output_format & DIFF_FORMAT_DIRSTAT && DIFF_OPT_TST(options, DIRSTAT_BY_LINE))
+		dirstat_by_line = 1;
+
+	if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT) ||
+	    dirstat_by_line) {
 		struct diffstat_t diffstat;
 
 		memset(&diffstat, 0, sizeof(struct diffstat_t));
@@ -3291,20 +4400,52 @@
 			show_stats(&diffstat, options);
 		if (output_format & DIFF_FORMAT_SHORTSTAT)
 			show_shortstats(&diffstat, options);
+		if (output_format & DIFF_FORMAT_DIRSTAT)
+			show_dirstat_by_line(&diffstat, options);
 		free_diffstat_info(&diffstat);
 		separator++;
 	}
-	if (output_format & DIFF_FORMAT_DIRSTAT)
+	if ((output_format & DIFF_FORMAT_DIRSTAT) && !dirstat_by_line)
 		show_dirstat(options);
 
 	if (output_format & DIFF_FORMAT_SUMMARY && !is_summary_empty(q)) {
-		for (i = 0; i < q->nr; i++)
-			diff_summary(options->file, q->queue[i]);
+		for (i = 0; i < q->nr; i++) {
+			diff_summary(options, q->queue[i]);
+		}
 		separator++;
 	}
 
+	if (output_format & DIFF_FORMAT_NO_OUTPUT &&
+	    DIFF_OPT_TST(options, EXIT_WITH_STATUS) &&
+	    DIFF_OPT_TST(options, DIFF_FROM_CONTENTS)) {
+		/*
+		 * run diff_flush_patch for the exit status. setting
+		 * options->file to /dev/null should be safe, becaue we
+		 * aren't supposed to produce any output anyway.
+		 */
+		if (options->close_file)
+			fclose(options->file);
+		options->file = fopen("/dev/null", "w");
+		if (!options->file)
+			die_errno("Could not open /dev/null");
+		options->close_file = 1;
+		for (i = 0; i < q->nr; i++) {
+			struct diff_filepair *p = q->queue[i];
+			if (check_pair_status(p))
+				diff_flush_patch(p, options);
+			if (options->found_changes)
+				break;
+		}
+	}
+
 	if (output_format & DIFF_FORMAT_PATCH) {
 		if (separator) {
+			if (options->output_prefix) {
+				struct strbuf *msg = NULL;
+				msg = options->output_prefix(options,
+					options->output_prefix_data);
+				fwrite(msg->buf, msg->len, 1, stdout);
+			}
 			putc(options->line_termination, options->file);
 			if (options->stat_sep) {
 				/* attach patch instead of inline */
@@ -3326,10 +4467,21 @@
 		diff_free_filepair(q->queue[i]);
 free_queue:
 	free(q->queue);
-	q->queue = NULL;
-	q->nr = q->alloc = 0;
+	DIFF_QUEUE_CLEAR(q);
 	if (options->close_file)
 		fclose(options->file);
+
+	/*
+	 * Report the content-level differences with HAS_CHANGES;
+	 * diff_addremove/diff_change does not set the bit when
+	 * DIFF_FROM_CONTENTS is in effect (e.g. with -w).
+	 */
+	if (DIFF_OPT_TST(options, DIFF_FROM_CONTENTS)) {
+		if (options->found_changes)
+			DIFF_OPT_SET(options, HAS_CHANGES);
+		else
+			DIFF_OPT_CLR(options, HAS_CHANGES);
+	}
 }
 
 static void diffcore_apply_filter(const char *filter)
@@ -3337,8 +4489,7 @@
 	int i;
 	struct diff_queue_struct *q = &diff_queued_diff;
 	struct diff_queue_struct outq;
-	outq.queue = NULL;
-	outq.nr = outq.alloc = 0;
+	DIFF_QUEUE_CLEAR(&outq);
 
 	if (!filter)
 		return;
@@ -3406,14 +4557,13 @@
 	int i;
 	struct diff_queue_struct *q = &diff_queued_diff;
 	struct diff_queue_struct outq;
-	outq.queue = NULL;
-	outq.nr = outq.alloc = 0;
+	DIFF_QUEUE_CLEAR(&outq);
 
 	for (i = 0; i < q->nr; i++) {
 		struct diff_filepair *p = q->queue[i];
 
 		/*
-		 * 1. Entries that come from stat info dirtyness
+		 * 1. Entries that come from stat info dirtiness
 		 *    always have both sides (iow, not create/delete),
 		 *    one side of the object name is unknown, with
 		 *    the same mode and size.  Keep the ones that
@@ -3449,32 +4599,60 @@
 	*q = outq;
 }
 
+static int diffnamecmp(const void *a_, const void *b_)
+{
+	const struct diff_filepair *a = *((const struct diff_filepair **)a_);
+	const struct diff_filepair *b = *((const struct diff_filepair **)b_);
+	const char *name_a, *name_b;
+
+	name_a = a->one ? a->one->path : a->two->path;
+	name_b = b->one ? b->one->path : b->two->path;
+	return strcmp(name_a, name_b);
+}
+
+void diffcore_fix_diff_index(struct diff_options *options)
+{
+	struct diff_queue_struct *q = &diff_queued_diff;
+	qsort(q->queue, q->nr, sizeof(q->queue[0]), diffnamecmp);
+}
+
 void diffcore_std(struct diff_options *options)
 {
 	if (options->skip_stat_unmatch)
 		diffcore_skip_stat_unmatch(options);
-	if (options->break_opt != -1)
-		diffcore_break(options->break_opt);
-	if (options->detect_rename)
-		diffcore_rename(options);
-	if (options->break_opt != -1)
-		diffcore_merge_broken();
+	if (!options->found_follow) {
+		/* See try_to_follow_renames() in tree-diff.c */
+		if (options->break_opt != -1)
+			diffcore_break(options->break_opt);
+		if (options->detect_rename)
+			diffcore_rename(options);
+		if (options->break_opt != -1)
+			diffcore_merge_broken();
+	}
 	if (options->pickaxe)
-		diffcore_pickaxe(options->pickaxe, options->pickaxe_opts);
+		diffcore_pickaxe(options);
 	if (options->orderfile)
 		diffcore_order(options->orderfile);
-	diff_resolve_rename_copy();
+	if (!options->found_follow)
+		/* See try_to_follow_renames() in tree-diff.c */
+		diff_resolve_rename_copy();
 	diffcore_apply_filter(options->filter);
 
-	if (diff_queued_diff.nr)
+	if (diff_queued_diff.nr && !DIFF_OPT_TST(options, DIFF_FROM_CONTENTS))
 		DIFF_OPT_SET(options, HAS_CHANGES);
 	else
 		DIFF_OPT_CLR(options, HAS_CHANGES);
+
+	options->found_follow = 0;
 }
 
 int diff_result_code(struct diff_options *opt, int status)
 {
 	int result = 0;
+
+	diff_warn_rename_limit("diff.renamelimit",
+			       opt->needed_rename_limit,
+			       opt->degraded_cc_to_c);
 	if (!DIFF_OPT_TST(opt, EXIT_WITH_STATUS) &&
 	    !(opt->output_format & DIFF_FORMAT_CHECKDIFF))
 		return status;
@@ -3487,14 +4665,39 @@
 	return result;
 }
 
+int diff_can_quit_early(struct diff_options *opt)
+{
+	return (DIFF_OPT_TST(opt, QUICK) &&
+		!opt->filter &&
+		DIFF_OPT_TST(opt, HAS_CHANGES));
+}
+
+/*
+ * Shall changes to this submodule be ignored?
+ *
+ * Submodule changes can be configured to be ignored separately for each path,
+ * but that configuration can be overridden from the command line.
+ */
+static int is_submodule_ignored(const char *path, struct diff_options *options)
+{
+	int ignored = 0;
+	unsigned orig_flags = options->flags;
+	if (!DIFF_OPT_TST(options, OVERRIDE_SUBMODULE_CONFIG))
+		set_diffopt_flags_from_submodule_config(options, path);
+	if (DIFF_OPT_TST(options, IGNORE_SUBMODULES))
+		ignored = 1;
+	options->flags = orig_flags;
+	return ignored;
+}
+
 void diff_addremove(struct diff_options *options,
 		    int addremove, unsigned mode,
 		    const unsigned char *sha1,
-		    const char *concatpath)
+		    const char *concatpath, unsigned dirty_submodule)
 {
 	struct diff_filespec *one, *two;
 
-	if (DIFF_OPT_TST(options, IGNORE_SUBMODULES) && S_ISGITLINK(mode))
+	if (S_ISGITLINK(mode) && is_submodule_ignored(concatpath, options))
 		return;
 
 	/* This may look odd, but it is a preparation for
@@ -3522,23 +4725,27 @@
 
 	if (addremove != '+')
 		fill_filespec(one, sha1, mode);
-	if (addremove != '-')
+	if (addremove != '-') {
 		fill_filespec(two, sha1, mode);
+		two->dirty_submodule = dirty_submodule;
+	}
 
 	diff_queue(&diff_queued_diff, one, two);
-	DIFF_OPT_SET(options, HAS_CHANGES);
+	if (!DIFF_OPT_TST(options, DIFF_FROM_CONTENTS))
+		DIFF_OPT_SET(options, HAS_CHANGES);
 }
 
 void diff_change(struct diff_options *options,
 		 unsigned old_mode, unsigned new_mode,
 		 const unsigned char *old_sha1,
 		 const unsigned char *new_sha1,
-		 const char *concatpath)
+		 const char *concatpath,
+		 unsigned old_dirty_submodule, unsigned new_dirty_submodule)
 {
 	struct diff_filespec *one, *two;
 
-	if (DIFF_OPT_TST(options, IGNORE_SUBMODULES) && S_ISGITLINK(old_mode)
-			&& S_ISGITLINK(new_mode))
+	if (S_ISGITLINK(old_mode) && S_ISGITLINK(new_mode) &&
+	    is_submodule_ignored(concatpath, options))
 		return;
 
 	if (DIFF_OPT_TST(options, REVERSE_DIFF)) {
@@ -3546,6 +4753,8 @@
 		const unsigned char *tmp_c;
 		tmp = old_mode; old_mode = new_mode; new_mode = tmp;
 		tmp_c = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_c;
+		tmp = old_dirty_submodule; old_dirty_submodule = new_dirty_submodule;
+			new_dirty_submodule = tmp;
 	}
 
 	if (options->prefix &&
@@ -3556,25 +4765,28 @@
 	two = alloc_filespec(concatpath);
 	fill_filespec(one, old_sha1, old_mode);
 	fill_filespec(two, new_sha1, new_mode);
+	one->dirty_submodule = old_dirty_submodule;
+	two->dirty_submodule = new_dirty_submodule;
 
 	diff_queue(&diff_queued_diff, one, two);
-	DIFF_OPT_SET(options, HAS_CHANGES);
+	if (!DIFF_OPT_TST(options, DIFF_FROM_CONTENTS))
+		DIFF_OPT_SET(options, HAS_CHANGES);
 }
 
-void diff_unmerge(struct diff_options *options,
-		  const char *path,
-		  unsigned mode, const unsigned char *sha1)
+struct diff_filepair *diff_unmerge(struct diff_options *options, const char *path)
 {
+	struct diff_filepair *pair;
 	struct diff_filespec *one, *two;
 
 	if (options->prefix &&
 	    strncmp(path, options->prefix, options->prefix_length))
-		return;
+		return NULL;
 
 	one = alloc_filespec(path);
 	two = alloc_filespec(path);
-	fill_filespec(one, sha1, mode);
-	diff_queue(&diff_queued_diff, one, two)->is_unmerged = 1;
+	pair = diff_queue(&diff_queued_diff, one, two);
+	pair->is_unmerged = 1;
+	return pair;
 }
 
 static char *run_textconv(const char *pgm, struct diff_filespec *spec,
@@ -3585,6 +4797,7 @@
 	const char **arg = argv;
 	struct child_process child;
 	struct strbuf buf = STRBUF_INIT;
+	int err = 0;
 
 	temp = prepare_temp_file(spec->path, spec);
 	*arg++ = pgm;
@@ -3592,17 +4805,68 @@
 	*arg = NULL;
 
 	memset(&child, 0, sizeof(child));
+	child.use_shell = 1;
 	child.argv = argv;
 	child.out = -1;
-	if (start_command(&child) != 0 ||
-	    strbuf_read(&buf, child.out, 0) < 0 ||
-	    finish_command(&child) != 0) {
+	if (start_command(&child)) {
+		remove_tempfile();
+		return NULL;
+	}
+
+	if (strbuf_read(&buf, child.out, 0) < 0)
+		err = error("error reading from textconv command '%s'", pgm);
+	close(child.out);
+
+	if (finish_command(&child) || err) {
 		strbuf_release(&buf);
 		remove_tempfile();
-		error("error running textconv command '%s'", pgm);
 		return NULL;
 	}
 	remove_tempfile();
 
 	return strbuf_detach(&buf, outsize);
 }
+
+size_t fill_textconv(struct userdiff_driver *driver,
+		     struct diff_filespec *df,
+		     char **outbuf)
+{
+	size_t size;
+
+	if (!driver || !driver->textconv) {
+		if (!DIFF_FILE_VALID(df)) {
+			*outbuf = "";
+			return 0;
+		}
+		if (diff_populate_filespec(df, 0))
+			die("unable to read files to diff");
+		*outbuf = df->data;
+		return df->size;
+	}
+
+	if (driver->textconv_cache && df->sha1_valid) {
+		*outbuf = notes_cache_get(driver->textconv_cache, df->sha1,
+					  &size);
+		if (*outbuf)
+			return size;
+	}
+
+	*outbuf = run_textconv(driver->textconv, df, &size);
+	if (!*outbuf)
+		die("unable to read files to diff");
+
+	if (driver->textconv_cache && df->sha1_valid) {
+		/* ignore errors, as we might be in a readonly repository */
+		notes_cache_put(driver->textconv_cache, df->sha1, *outbuf,
+				size);
+		/*
+		 * we could save up changes and flush them all at the end,
+		 * but we would need an extra call after all diffing is done.
+		 * Since generating a cache entry is the slow path anyway,
+		 * this extra overhead probably isn't a big deal.
+		 */
+		notes_cache_write(driver->textconv_cache);
+	}
+
+	return size;
+}
diff --git a/diff.h b/diff.h
index 6616877..e027650 100644
--- a/diff.h
+++ b/diff.h
@@ -9,21 +9,29 @@
 struct rev_info;
 struct diff_options;
 struct diff_queue_struct;
+struct strbuf;
+struct diff_filespec;
+struct userdiff_driver;
+struct sha1_array;
+struct commit;
 
 typedef void (*change_fn_t)(struct diff_options *options,
 		 unsigned old_mode, unsigned new_mode,
 		 const unsigned char *old_sha1,
 		 const unsigned char *new_sha1,
-		 const char *fullpath);
+		 const char *fullpath,
+		 unsigned old_dirty_submodule, unsigned new_dirty_submodule);
 
 typedef void (*add_remove_fn_t)(struct diff_options *options,
 		    int addremove, unsigned mode,
 		    const unsigned char *sha1,
-		    const char *fullpath);
+		    const char *fullpath, unsigned dirty_submodule);
 
 typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
 		struct diff_options *options, void *data);
 
+typedef struct strbuf *(*diff_prefix_fn_t)(struct diff_options *opt, void *data);
+
 #define DIFF_FORMAT_RAW		0x0001
 #define DIFF_FORMAT_DIFFSTAT	0x0002
 #define DIFF_FORMAT_NUMSTAT	0x0004
@@ -52,10 +60,10 @@
 #define DIFF_OPT_SILENT_ON_REMOVE    (1 <<  5)
 #define DIFF_OPT_FIND_COPIES_HARDER  (1 <<  6)
 #define DIFF_OPT_FOLLOW_RENAMES      (1 <<  7)
-#define DIFF_OPT_COLOR_DIFF          (1 <<  8)
-#define DIFF_OPT_COLOR_DIFF_WORDS    (1 <<  9)
+#define DIFF_OPT_RENAME_EMPTY        (1 <<  8)
+/* (1 <<  9) unused */
 #define DIFF_OPT_HAS_CHANGES         (1 << 10)
-#define DIFF_OPT_QUIET               (1 << 11)
+#define DIFF_OPT_QUICK               (1 << 11)
 #define DIFF_OPT_NO_INDEX            (1 << 12)
 #define DIFF_OPT_ALLOW_EXTERNAL      (1 << 13)
 #define DIFF_OPT_EXIT_WITH_STATUS    (1 << 14)
@@ -66,6 +74,16 @@
 #define DIFF_OPT_DIRSTAT_CUMULATIVE  (1 << 19)
 #define DIFF_OPT_DIRSTAT_BY_FILE     (1 << 20)
 #define DIFF_OPT_ALLOW_TEXTCONV      (1 << 21)
+#define DIFF_OPT_DIFF_FROM_CONTENTS  (1 << 22)
+#define DIFF_OPT_SUBMODULE_LOG       (1 << 23)
+#define DIFF_OPT_DIRTY_SUBMODULES    (1 << 24)
+#define DIFF_OPT_IGNORE_UNTRACKED_IN_SUBMODULES (1 << 25)
+#define DIFF_OPT_IGNORE_DIRTY_SUBMODULES (1 << 26)
+#define DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG (1 << 27)
+#define DIFF_OPT_DIRSTAT_BY_LINE     (1 << 28)
+#define DIFF_OPT_FUNCCONTEXT         (1 << 29)
+#define DIFF_OPT_PICKAXE_IGNORE_CASE (1 << 30)
+
 #define DIFF_OPT_TST(opts, flag)    ((opts)->flags & DIFF_OPT_##flag)
 #define DIFF_OPT_SET(opts, flag)    ((opts)->flags |= DIFF_OPT_##flag)
 #define DIFF_OPT_CLR(opts, flag)    ((opts)->flags &= ~DIFF_OPT_##flag)
@@ -73,6 +91,15 @@
 #define DIFF_XDL_SET(opts, flag)    ((opts)->xdl_opts |= XDF_##flag)
 #define DIFF_XDL_CLR(opts, flag)    ((opts)->xdl_opts &= ~XDF_##flag)
 
+#define DIFF_WITH_ALG(opts, flag)   (((opts)->xdl_opts & ~XDF_DIFF_ALGORITHM_MASK) | XDF_##flag)
+
+enum diff_words_type {
+	DIFF_WORDS_NONE = 0,
+	DIFF_WORDS_PORCELAIN,
+	DIFF_WORDS_PLAIN,
+	DIFF_WORDS_COLOR
+};
+
 struct diff_options {
 	const char *filter;
 	const char *orderfile;
@@ -80,18 +107,22 @@
 	const char *single_follow;
 	const char *a_prefix, *b_prefix;
 	unsigned flags;
+	int use_color;
 	int context;
 	int interhunkcontext;
 	int break_opt;
 	int detect_rename;
+	int irreversible_delete;
 	int skip_stat_unmatch;
 	int line_termination;
 	int output_format;
 	int pickaxe_opts;
 	int rename_score;
 	int rename_limit;
-	int warn_on_too_large_rename;
-	int dirstat_percent;
+	int needed_rename_limit;
+	int degraded_cc_to_c;
+	int show_rename_progress;
+	int dirstat_permille;
 	int setup;
 	int abbrev;
 	const char *prefix;
@@ -101,21 +132,28 @@
 
 	int stat_width;
 	int stat_name_width;
+	int stat_graph_width;
+	int stat_count;
 	const char *word_regex;
+	enum diff_words_type word_diff;
 
 	/* this is set by diffcore for DIFF_FORMAT_PATCH */
 	int found_changes;
 
+	/* to support internal diff recursion by --follow hack*/
+	int found_follow;
+
 	FILE *file;
 	int close_file;
 
-	int nr_paths;
-	const char **paths;
-	int *pathlens;
+	struct pathspec pathspec;
 	change_fn_t change;
 	add_remove_fn_t add_remove;
 	diff_format_fn_t format_callback;
 	void *format_callback_data;
+	diff_prefix_fn_t output_prefix;
+	int output_prefix_length;
+	void *output_prefix_data;
 };
 
 enum color_diff {
@@ -127,10 +165,11 @@
 	DIFF_FILE_NEW = 5,
 	DIFF_COMMIT = 6,
 	DIFF_WHITESPACE = 7,
+	DIFF_FUNCINFO = 8
 };
 const char *diff_get_color(int diff_use_color, enum color_diff ix);
 #define diff_get_color_opt(o, ix) \
-	diff_get_color(DIFF_OPT_TST((o), COLOR_DIFF), ix)
+	diff_get_color((o)->use_color, ix)
 
 
 extern const char mime_boundary_leader[];
@@ -163,33 +202,40 @@
 extern void show_combined_diff(struct combine_diff_path *elem, int num_parent,
 			      int dense, struct rev_info *);
 
-extern void diff_tree_combined(const unsigned char *sha1, const unsigned char parent[][20], int num_parent, int dense, struct rev_info *rev);
+extern void diff_tree_combined(const unsigned char *sha1, const struct sha1_array *parents, int dense, struct rev_info *rev);
 
-extern void diff_tree_combined_merge(const unsigned char *sha1, int, struct rev_info *);
+extern void diff_tree_combined_merge(const struct commit *commit, int dense, struct rev_info *rev);
 
 void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const char *b);
 
+extern int diff_can_quit_early(struct diff_options *);
+
 extern void diff_addremove(struct diff_options *,
 			   int addremove,
 			   unsigned mode,
 			   const unsigned char *sha1,
-			   const char *fullpath);
+			   const char *fullpath, unsigned dirty_submodule);
 
 extern void diff_change(struct diff_options *,
 			unsigned mode1, unsigned mode2,
 			const unsigned char *sha1,
 			const unsigned char *sha2,
-			const char *fullpath);
+			const char *fullpath,
+			unsigned dirty_submodule1, unsigned dirty_submodule2);
 
-extern void diff_unmerge(struct diff_options *,
-			 const char *path,
-			 unsigned mode,
-			 const unsigned char *sha1);
+extern struct diff_filepair *diff_unmerge(struct diff_options *, const char *path);
 
 #define DIFF_SETUP_REVERSE      	1
 #define DIFF_SETUP_USE_CACHE		2
 #define DIFF_SETUP_USE_SIZE_CACHE	4
 
+/*
+ * Poor man's alternative to parse-option, to allow both sticked form
+ * (--option=value) and separate form (--option value).
+ */
+extern int parse_long_opt(const char *opt, const char **argv,
+			 const char **optarg);
+
 extern int git_diff_basic_config(const char *var, const char *value, void *cb);
 extern int git_diff_ui_config(const char *var, const char *value, void *cb);
 extern int diff_use_color_default;
@@ -203,7 +249,11 @@
 #define DIFF_PICKAXE_ALL	1
 #define DIFF_PICKAXE_REGEX	2
 
+#define DIFF_PICKAXE_KIND_S	4 /* traditional plumbing counter */
+#define DIFF_PICKAXE_KIND_G	8 /* grep in the patch */
+
 extern void diffcore_std(struct diff_options *);
+extern void diffcore_fix_diff_index(struct diff_options *);
 
 #define COMMON_DIFF_OPTIONS_HELP \
 "\ncommon diff options:\n" \
@@ -235,6 +285,7 @@
 
 extern int diff_queue_is_empty(void);
 extern void diff_flush(struct diff_options*);
+extern void diff_warn_rename_limit(const char *varname, int needed, int degraded_cc);
 
 /* diff-raw status letters */
 #define DIFF_STATUS_ADDED		'A'
@@ -270,4 +321,15 @@
 
 extern int index_differs_from(const char *def, int diff_flags);
 
+extern size_t fill_textconv(struct userdiff_driver *driver,
+			    struct diff_filespec *df,
+			    char **outbuf);
+
+extern struct userdiff_driver *get_textconv(struct diff_filespec *one);
+
+extern int parse_rename_score(const char **cp_p);
+
+extern int print_stat_summary(FILE *fp, int files,
+			      int insertions, int deletions);
+
 #endif /* DIFF_H */
diff --git a/diffcore-break.c b/diffcore-break.c
index d7097bb..44f8678 100644
--- a/diffcore-break.c
+++ b/diffcore-break.c
@@ -69,7 +69,7 @@
 		return 0; /* we do not break too small filepair */
 
 	if (diffcore_count_changes(src, dst,
-				   NULL, NULL,
+				   &src->cnt_data, &dst->cnt_data,
 				   0,
 				   &src_copied, &literal_added))
 		return 0;
@@ -162,8 +162,7 @@
 	if (!merge_score)
 		merge_score = DEFAULT_MERGE_SCORE;
 
-	outq.nr = outq.alloc = 0;
-	outq.queue = NULL;
+	DIFF_QUEUE_CLEAR(&outq);
 
 	for (i = 0; i < q->nr; i++) {
 		struct diff_filepair *p = q->queue[i];
@@ -204,12 +203,16 @@
 				dp->score = score;
 				dp->broken_pair = 1;
 
+				diff_free_filespec_blob(p->one);
+				diff_free_filespec_blob(p->two);
 				free(p); /* not diff_free_filepair(), we are
 					  * reusing one and two here.
 					  */
 				continue;
 			}
 		}
+		diff_free_filespec_data(p->one);
+		diff_free_filespec_data(p->two);
 		diff_q(&outq, p);
 	}
 	free(q->queue);
@@ -252,8 +255,7 @@
 	struct diff_queue_struct outq;
 	int i, j;
 
-	outq.nr = outq.alloc = 0;
-	outq.queue = NULL;
+	DIFF_QUEUE_CLEAR(&outq);
 
 	for (i = 0; i < q->nr; i++) {
 		struct diff_filepair *p = q->queue[i];
diff --git a/diffcore-delta.c b/diffcore-delta.c
index e670f85..7cf431d2 100644
--- a/diffcore-delta.c
+++ b/diffcore-delta.c
@@ -201,10 +201,15 @@
 		while (d->cnt) {
 			if (d->hashval >= s->hashval)
 				break;
+			la += d->cnt;
 			d++;
 		}
 		src_cnt = s->cnt;
-		dst_cnt = d->hashval == s->hashval ? d->cnt : 0;
+		dst_cnt = 0;
+		if (d->cnt && d->hashval == s->hashval) {
+			dst_cnt = d->cnt;
+			d++;
+		}
 		if (src_cnt < dst_cnt) {
 			la += dst_cnt - src_cnt;
 			sc += src_cnt;
@@ -213,6 +218,10 @@
 			sc += dst_cnt;
 		s++;
 	}
+	while (d->cnt) {
+		la += d->cnt;
+		d++;
+	}
 
 	if (!src_count_p)
 		free(src_count);
diff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c
index d0ef839..ed23eb4 100644
--- a/diffcore-pickaxe.c
+++ b/diffcore-pickaxe.c
@@ -1,20 +1,171 @@
 /*
  * Copyright (C) 2005 Junio C Hamano
+ * Copyright (C) 2010 Google Inc.
  */
 #include "cache.h"
 #include "diff.h"
 #include "diffcore.h"
+#include "xdiff-interface.h"
+#include "kwset.h"
 
-static unsigned int contains(struct diff_filespec *one,
-			     const char *needle, unsigned long len,
-			     regex_t *regexp)
+typedef int (*pickaxe_fn)(struct diff_filepair *p, struct diff_options *o, regex_t *regexp, kwset_t kws);
+
+static void pickaxe(struct diff_queue_struct *q, struct diff_options *o,
+		    regex_t *regexp, kwset_t kws, pickaxe_fn fn)
+{
+	int i;
+	struct diff_queue_struct outq;
+
+	DIFF_QUEUE_CLEAR(&outq);
+
+	if (o->pickaxe_opts & DIFF_PICKAXE_ALL) {
+		/* Showing the whole changeset if needle exists */
+		for (i = 0; i < q->nr; i++) {
+			struct diff_filepair *p = q->queue[i];
+			if (fn(p, o, regexp, kws))
+				return; /* do not munge the queue */
+		}
+
+		/*
+		 * Otherwise we will clear the whole queue by copying
+		 * the empty outq at the end of this function, but
+		 * first clear the current entries in the queue.
+		 */
+		for (i = 0; i < q->nr; i++)
+			diff_free_filepair(q->queue[i]);
+	} else {
+		/* Showing only the filepairs that has the needle */
+		for (i = 0; i < q->nr; i++) {
+			struct diff_filepair *p = q->queue[i];
+			if (fn(p, o, regexp, kws))
+				diff_q(&outq, p);
+			else
+				diff_free_filepair(p);
+		}
+	}
+
+	free(q->queue);
+	*q = outq;
+}
+
+struct diffgrep_cb {
+	regex_t *regexp;
+	int hit;
+};
+
+static void diffgrep_consume(void *priv, char *line, unsigned long len)
+{
+	struct diffgrep_cb *data = priv;
+	regmatch_t regmatch;
+	int hold;
+
+	if (line[0] != '+' && line[0] != '-')
+		return;
+	if (data->hit)
+		/*
+		 * NEEDSWORK: we should have a way to terminate the
+		 * caller early.
+		 */
+		return;
+	/* Yuck -- line ought to be "const char *"! */
+	hold = line[len];
+	line[len] = '\0';
+	data->hit = !regexec(data->regexp, line + 1, 1, &regmatch, 0);
+	line[len] = hold;
+}
+
+static void fill_one(struct diff_filespec *one,
+		     mmfile_t *mf, struct userdiff_driver **textconv)
+{
+	if (DIFF_FILE_VALID(one)) {
+		*textconv = get_textconv(one);
+		mf->size = fill_textconv(*textconv, one, &mf->ptr);
+	} else {
+		memset(mf, 0, sizeof(*mf));
+	}
+}
+
+static int diff_grep(struct diff_filepair *p, struct diff_options *o,
+		     regex_t *regexp, kwset_t kws)
+{
+	regmatch_t regmatch;
+	struct userdiff_driver *textconv_one = NULL;
+	struct userdiff_driver *textconv_two = NULL;
+	mmfile_t mf1, mf2;
+	int hit;
+
+	if (diff_unmodified_pair(p))
+		return 0;
+
+	fill_one(p->one, &mf1, &textconv_one);
+	fill_one(p->two, &mf2, &textconv_two);
+
+	if (!mf1.ptr) {
+		if (!mf2.ptr)
+			return 0; /* ignore unmerged */
+		/* created "two" -- does it have what we are looking for? */
+		hit = !regexec(regexp, p->two->data, 1, &regmatch, 0);
+	} else if (!mf2.ptr) {
+		/* removed "one" -- did it have what we are looking for? */
+		hit = !regexec(regexp, p->one->data, 1, &regmatch, 0);
+	} else {
+		/*
+		 * We have both sides; need to run textual diff and see if
+		 * the pattern appears on added/deleted lines.
+		 */
+		struct diffgrep_cb ecbdata;
+		xpparam_t xpp;
+		xdemitconf_t xecfg;
+
+		memset(&xpp, 0, sizeof(xpp));
+		memset(&xecfg, 0, sizeof(xecfg));
+		ecbdata.regexp = regexp;
+		ecbdata.hit = 0;
+		xecfg.ctxlen = o->context;
+		xecfg.interhunkctxlen = o->interhunkcontext;
+		xdi_diff_outf(&mf1, &mf2, diffgrep_consume, &ecbdata,
+			      &xpp, &xecfg);
+		hit = ecbdata.hit;
+	}
+	if (textconv_one)
+		free(mf1.ptr);
+	if (textconv_two)
+		free(mf2.ptr);
+	return hit;
+}
+
+static void diffcore_pickaxe_grep(struct diff_options *o)
+{
+	int err;
+	regex_t regex;
+	int cflags = REG_EXTENDED | REG_NEWLINE;
+
+	if (DIFF_OPT_TST(o, PICKAXE_IGNORE_CASE))
+		cflags |= REG_ICASE;
+
+	err = regcomp(&regex, o->pickaxe, cflags);
+	if (err) {
+		char errbuf[1024];
+		regerror(err, &regex, errbuf, 1024);
+		regfree(&regex);
+		die("invalid log-grep regex: %s", errbuf);
+	}
+
+	pickaxe(&diff_queued_diff, o, &regex, NULL, diff_grep);
+
+	regfree(&regex);
+	return;
+}
+
+static unsigned int contains(struct diff_filespec *one, struct diff_options *o,
+			     regex_t *regexp, kwset_t kws)
 {
 	unsigned int cnt;
 	unsigned long sz;
 	const char *data;
-	if (diff_populate_filespec(one, 0))
+	if (!o->pickaxe[0])
 		return 0;
-	if (!len)
+	if (diff_populate_filespec(one, 0))
 		return 0;
 
 	sz = one->size;
@@ -36,11 +187,15 @@
 
 	} else { /* Classic exact string match */
 		while (sz) {
-			const char *found = memmem(data, sz, needle, len);
-			if (!found)
+			struct kwsmatch kwsm;
+			size_t offset = kwsexec(kws, data, sz, &kwsm);
+			const char *found;
+			if (offset == -1)
 				break;
-			sz -= found - data + len;
-			data = found + len;
+			else
+				found = data + offset;
+			sz -= found - data + kwsm.size[0];
+			data = found + kwsm.size[0];
 			cnt++;
 		}
 	}
@@ -48,15 +203,31 @@
 	return cnt;
 }
 
-void diffcore_pickaxe(const char *needle, int opts)
+static int has_changes(struct diff_filepair *p, struct diff_options *o,
+		       regex_t *regexp, kwset_t kws)
 {
-	struct diff_queue_struct *q = &diff_queued_diff;
+	if (!DIFF_FILE_VALID(p->one)) {
+		if (!DIFF_FILE_VALID(p->two))
+			return 0; /* ignore unmerged */
+		/* created */
+		return contains(p->two, o, regexp, kws) != 0;
+	}
+	if (!DIFF_FILE_VALID(p->two))
+		return contains(p->one, o, regexp, kws) != 0;
+	if (!diff_unmodified_pair(p)) {
+		return contains(p->one, o, regexp, kws) !=
+		       contains(p->two, o, regexp, kws);
+	}
+	return 0;
+}
+
+static void diffcore_pickaxe_count(struct diff_options *o)
+{
+	const char *needle = o->pickaxe;
+	int opts = o->pickaxe_opts;
 	unsigned long len = strlen(needle);
-	int i, has_changes;
 	regex_t regex, *regexp = NULL;
-	struct diff_queue_struct outq;
-	outq.queue = NULL;
-	outq.nr = outq.alloc = 0;
+	kwset_t kws = NULL;
 
 	if (opts & DIFF_PICKAXE_REGEX) {
 		int err;
@@ -69,71 +240,27 @@
 			die("invalid pickaxe regex: %s", errbuf);
 		}
 		regexp = &regex;
+	} else {
+		kws = kwsalloc(DIFF_OPT_TST(o, PICKAXE_IGNORE_CASE)
+			       ? tolower_trans_tbl : NULL);
+		kwsincr(kws, needle, len);
+		kwsprep(kws);
 	}
 
-	if (opts & DIFF_PICKAXE_ALL) {
-		/* Showing the whole changeset if needle exists */
-		for (i = has_changes = 0; !has_changes && i < q->nr; i++) {
-			struct diff_filepair *p = q->queue[i];
-			if (!DIFF_FILE_VALID(p->one)) {
-				if (!DIFF_FILE_VALID(p->two))
-					continue; /* ignore unmerged */
-				/* created */
-				if (contains(p->two, needle, len, regexp))
-					has_changes++;
-			}
-			else if (!DIFF_FILE_VALID(p->two)) {
-				if (contains(p->one, needle, len, regexp))
-					has_changes++;
-			}
-			else if (!diff_unmodified_pair(p) &&
-				 contains(p->one, needle, len, regexp) !=
-				 contains(p->two, needle, len, regexp))
-				has_changes++;
-		}
-		if (has_changes)
-			return; /* not munge the queue */
+	pickaxe(&diff_queued_diff, o, regexp, kws, has_changes);
 
-		/* otherwise we will clear the whole queue
-		 * by copying the empty outq at the end of this
-		 * function, but first clear the current entries
-		 * in the queue.
-		 */
-		for (i = 0; i < q->nr; i++)
-			diff_free_filepair(q->queue[i]);
-	}
-	else
-		/* Showing only the filepairs that has the needle */
-		for (i = 0; i < q->nr; i++) {
-			struct diff_filepair *p = q->queue[i];
-			has_changes = 0;
-			if (!DIFF_FILE_VALID(p->one)) {
-				if (!DIFF_FILE_VALID(p->two))
-					; /* ignore unmerged */
-				/* created */
-				else if (contains(p->two, needle, len, regexp))
-					has_changes = 1;
-			}
-			else if (!DIFF_FILE_VALID(p->two)) {
-				if (contains(p->one, needle, len, regexp))
-					has_changes = 1;
-			}
-			else if (!diff_unmodified_pair(p) &&
-				 contains(p->one, needle, len, regexp) !=
-				 contains(p->two, needle, len, regexp))
-				has_changes = 1;
-
-			if (has_changes)
-				diff_q(&outq, p);
-			else
-				diff_free_filepair(p);
-		}
-
-	if (opts & DIFF_PICKAXE_REGEX) {
+	if (opts & DIFF_PICKAXE_REGEX)
 		regfree(&regex);
-	}
-
-	free(q->queue);
-	*q = outq;
+	else
+		kwsfree(kws);
 	return;
 }
+
+void diffcore_pickaxe(struct diff_options *o)
+{
+	/* Might want to warn when both S and G are on; I don't care... */
+	if (o->pickaxe_opts & DIFF_PICKAXE_KIND_G)
+		diffcore_pickaxe_grep(o);
+	else
+		diffcore_pickaxe_count(o);
+}
diff --git a/diffcore-rename.c b/diffcore-rename.c
index 63ac998..216a7a4 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -5,6 +5,7 @@
 #include "diff.h"
 #include "diffcore.h"
 #include "hash.h"
+#include "progress.h"
 
 /* Table of rename/copy destinations */
 
@@ -54,22 +55,23 @@
 
 /* Table of rename/copy src files */
 static struct diff_rename_src {
-	struct diff_filespec *one;
+	struct diff_filepair *p;
 	unsigned short score; /* to remember the break score */
 } *rename_src;
 static int rename_src_nr, rename_src_alloc;
 
-static struct diff_rename_src *register_rename_src(struct diff_filespec *one,
-						   unsigned short score)
+static struct diff_rename_src *register_rename_src(struct diff_filepair *p)
 {
 	int first, last;
+	struct diff_filespec *one = p->one;
+	unsigned short score = p->score;
 
 	first = 0;
 	last = rename_src_nr;
 	while (last > first) {
 		int next = (last + first) >> 1;
 		struct diff_rename_src *src = &(rename_src[next]);
-		int cmp = strcmp(one->path, src->one->path);
+		int cmp = strcmp(one->path, src->p->one->path);
 		if (!cmp)
 			return src;
 		if (cmp < 0) {
@@ -89,7 +91,7 @@
 	if (first < rename_src_nr)
 		memmove(rename_src + first + 1, rename_src + first,
 			(rename_src_nr - first - 1) * sizeof(*rename_src));
-	rename_src[first].one = one;
+	rename_src[first].p = p;
 	rename_src[first].score = score;
 	return &(rename_src[first]);
 }
@@ -170,7 +172,7 @@
 	 * and the final score computation below would not have a
 	 * divide-by-zero issue.
 	 */
-	if (base_size * (MAX_SCORE-minimum_score) < delta_size * MAX_SCORE)
+	if (max_size * (MAX_SCORE-minimum_score) < delta_size * MAX_SCORE)
 		return 0;
 
 	if (!src->cnt_data && diff_populate_filespec(src, 0))
@@ -204,7 +206,7 @@
 	if (rename_dst[dst_index].pair)
 		die("internal error: dst already matched.");
 
-	src = rename_src[src_index].one;
+	src = rename_src[src_index].p->one;
 	src->rename_used++;
 	src->count++;
 
@@ -247,7 +249,8 @@
 };
 
 static int find_identical_files(struct file_similarity *src,
-				struct file_similarity *dst)
+				struct file_similarity *dst,
+				struct diff_options *options)
 {
 	int renames = 0;
 
@@ -277,6 +280,8 @@
 			}
 			/* Give higher scores to sources that haven't been used already */
 			score = !source->rename_used;
+			if (source->rename_used && options->detect_rename != DIFF_DETECT_COPY)
+				continue;
 			score += basename_same(source, target);
 			if (score > best_score) {
 				best = p;
@@ -306,11 +311,12 @@
 	}
 }
 
-static int find_same_files(void *ptr)
+static int find_same_files(void *ptr, void *data)
 {
 	int ret;
 	struct file_similarity *p = ptr;
 	struct file_similarity *src = NULL, *dst = NULL;
+	struct diff_options *options = data;
 
 	/* Split the hash list up into sources and destinations */
 	do {
@@ -329,7 +335,7 @@
 	 * If we have both sources *and* destinations, see if
 	 * we can match them up
 	 */
-	ret = (src && dst) ? find_identical_files(src, dst) : 0;
+	ret = (src && dst) ? find_identical_files(src, dst, options) : 0;
 
 	/* Free the hashes and return the number of renames found */
 	free_similarity_list(src);
@@ -377,20 +383,20 @@
  * and then during the second round we try to match
  * cache-dirty entries as well.
  */
-static int find_exact_renames(void)
+static int find_exact_renames(struct diff_options *options)
 {
 	int i;
 	struct hash_table file_table;
 
 	init_hash(&file_table);
 	for (i = 0; i < rename_src_nr; i++)
-		insert_file_table(&file_table, -1, i, rename_src[i].one);
+		insert_file_table(&file_table, -1, i, rename_src[i].p->one);
 
 	for (i = 0; i < rename_dst_nr; i++)
 		insert_file_table(&file_table, 1, i, rename_dst[i].two);
 
 	/* Find the renames */
-	i = for_each_hash(&file_table, find_same_files);
+	i = for_each_hash(&file_table, find_same_files, options);
 
 	/* .. and free the hash data structure */
 	free_hash(&file_table);
@@ -414,75 +420,20 @@
 		m[worst] = *o;
 }
 
-void diffcore_rename(struct diff_options *options)
+/*
+ * Returns:
+ * 0 if we are under the limit;
+ * 1 if we need to disable inexact rename detection;
+ * 2 if we would be under the limit if we were given -C instead of -C -C.
+ */
+static int too_many_rename_candidates(int num_create,
+				      struct diff_options *options)
 {
-	int detect_rename = options->detect_rename;
-	int minimum_score = options->rename_score;
 	int rename_limit = options->rename_limit;
-	struct diff_queue_struct *q = &diff_queued_diff;
-	struct diff_queue_struct outq;
-	struct diff_score *mx;
-	int i, j, rename_count;
-	int num_create, num_src, dst_cnt;
+	int num_src = rename_src_nr;
+	int i;
 
-	if (!minimum_score)
-		minimum_score = DEFAULT_RENAME_SCORE;
-
-	for (i = 0; i < q->nr; i++) {
-		struct diff_filepair *p = q->queue[i];
-		if (!DIFF_FILE_VALID(p->one)) {
-			if (!DIFF_FILE_VALID(p->two))
-				continue; /* unmerged */
-			else if (options->single_follow &&
-				 strcmp(options->single_follow, p->two->path))
-				continue; /* not interested */
-			else
-				locate_rename_dst(p->two, 1);
-		}
-		else if (!DIFF_FILE_VALID(p->two)) {
-			/*
-			 * If the source is a broken "delete", and
-			 * they did not really want to get broken,
-			 * that means the source actually stays.
-			 * So we increment the "rename_used" score
-			 * by one, to indicate ourselves as a user
-			 */
-			if (p->broken_pair && !p->score)
-				p->one->rename_used++;
-			register_rename_src(p->one, p->score);
-		}
-		else if (detect_rename == DIFF_DETECT_COPY) {
-			/*
-			 * Increment the "rename_used" score by
-			 * one, to indicate ourselves as a user.
-			 */
-			p->one->rename_used++;
-			register_rename_src(p->one, p->score);
-		}
-	}
-	if (rename_dst_nr == 0 || rename_src_nr == 0)
-		goto cleanup; /* nothing to do */
-
-	/*
-	 * We really want to cull the candidates list early
-	 * with cheap tests in order to avoid doing deltas.
-	 */
-	rename_count = find_exact_renames();
-
-	/* Did we only want exact renames? */
-	if (minimum_score == MAX_SCORE)
-		goto cleanup;
-
-	/*
-	 * Calculate how many renames are left (but all the source
-	 * files still remain as options for rename/copies!)
-	 */
-	num_create = (rename_dst_nr - rename_count);
-	num_src = rename_src_nr;
-
-	/* All done? */
-	if (!num_create)
-		goto cleanup;
+	options->needed_rename_limit = 0;
 
 	/*
 	 * This basically does a test for the rename matrix not
@@ -495,11 +446,140 @@
 	 */
 	if (rename_limit <= 0 || rename_limit > 32767)
 		rename_limit = 32767;
-	if ((num_create > rename_limit && num_src > rename_limit) ||
-	    (num_create * num_src > rename_limit * rename_limit)) {
-		if (options->warn_on_too_large_rename)
-			warning("too many files (created: %d deleted: %d), skipping inexact rename detection", num_create, num_src);
+	if ((num_create <= rename_limit || num_src <= rename_limit) &&
+	    (num_create * num_src <= rename_limit * rename_limit))
+		return 0;
+
+	options->needed_rename_limit =
+		num_src > num_create ? num_src : num_create;
+
+	/* Are we running under -C -C? */
+	if (!DIFF_OPT_TST(options, FIND_COPIES_HARDER))
+		return 1;
+
+	/* Would we bust the limit if we were running under -C? */
+	for (num_src = i = 0; i < rename_src_nr; i++) {
+		if (diff_unmodified_pair(rename_src[i].p))
+			continue;
+		num_src++;
+	}
+	if ((num_create <= rename_limit || num_src <= rename_limit) &&
+	    (num_create * num_src <= rename_limit * rename_limit))
+		return 2;
+	return 1;
+}
+
+static int find_renames(struct diff_score *mx, int dst_cnt, int minimum_score, int copies)
+{
+	int count = 0, i;
+
+	for (i = 0; i < dst_cnt * NUM_CANDIDATE_PER_DST; i++) {
+		struct diff_rename_dst *dst;
+
+		if ((mx[i].dst < 0) ||
+		    (mx[i].score < minimum_score))
+			break; /* there is no more usable pair. */
+		dst = &rename_dst[mx[i].dst];
+		if (dst->pair)
+			continue; /* already done, either exact or fuzzy. */
+		if (!copies && rename_src[mx[i].src].p->one->rename_used)
+			continue;
+		record_rename_pair(mx[i].dst, mx[i].src, mx[i].score);
+		count++;
+	}
+	return count;
+}
+
+void diffcore_rename(struct diff_options *options)
+{
+	int detect_rename = options->detect_rename;
+	int minimum_score = options->rename_score;
+	struct diff_queue_struct *q = &diff_queued_diff;
+	struct diff_queue_struct outq;
+	struct diff_score *mx;
+	int i, j, rename_count, skip_unmodified = 0;
+	int num_create, dst_cnt;
+	struct progress *progress = NULL;
+
+	if (!minimum_score)
+		minimum_score = DEFAULT_RENAME_SCORE;
+
+	for (i = 0; i < q->nr; i++) {
+		struct diff_filepair *p = q->queue[i];
+		if (!DIFF_FILE_VALID(p->one)) {
+			if (!DIFF_FILE_VALID(p->two))
+				continue; /* unmerged */
+			else if (options->single_follow &&
+				 strcmp(options->single_follow, p->two->path))
+				continue; /* not interested */
+			else if (!DIFF_OPT_TST(options, RENAME_EMPTY) &&
+				 is_empty_blob_sha1(p->two->sha1))
+				continue;
+			else
+				locate_rename_dst(p->two, 1);
+		}
+		else if (!DIFF_OPT_TST(options, RENAME_EMPTY) &&
+			 is_empty_blob_sha1(p->one->sha1))
+			continue;
+		else if (!DIFF_PAIR_UNMERGED(p) && !DIFF_FILE_VALID(p->two)) {
+			/*
+			 * If the source is a broken "delete", and
+			 * they did not really want to get broken,
+			 * that means the source actually stays.
+			 * So we increment the "rename_used" score
+			 * by one, to indicate ourselves as a user
+			 */
+			if (p->broken_pair && !p->score)
+				p->one->rename_used++;
+			register_rename_src(p);
+		}
+		else if (detect_rename == DIFF_DETECT_COPY) {
+			/*
+			 * Increment the "rename_used" score by
+			 * one, to indicate ourselves as a user.
+			 */
+			p->one->rename_used++;
+			register_rename_src(p);
+		}
+	}
+	if (rename_dst_nr == 0 || rename_src_nr == 0)
+		goto cleanup; /* nothing to do */
+
+	/*
+	 * We really want to cull the candidates list early
+	 * with cheap tests in order to avoid doing deltas.
+	 */
+	rename_count = find_exact_renames(options);
+
+	/* Did we only want exact renames? */
+	if (minimum_score == MAX_SCORE)
 		goto cleanup;
+
+	/*
+	 * Calculate how many renames are left (but all the source
+	 * files still remain as options for rename/copies!)
+	 */
+	num_create = (rename_dst_nr - rename_count);
+
+	/* All done? */
+	if (!num_create)
+		goto cleanup;
+
+	switch (too_many_rename_candidates(num_create, options)) {
+	case 1:
+		goto cleanup;
+	case 2:
+		options->degraded_cc_to_c = 1;
+		skip_unmodified = 1;
+		break;
+	default:
+		break;
+	}
+
+	if (options->show_rename_progress) {
+		progress = start_progress_delay(
+				"Performing inexact rename detection",
+				rename_dst_nr * rename_src_nr, 50, 1);
 	}
 
 	mx = xcalloc(num_create * NUM_CANDIDATE_PER_DST, sizeof(*mx));
@@ -515,64 +595,52 @@
 			m[j].dst = -1;
 
 		for (j = 0; j < rename_src_nr; j++) {
-			struct diff_filespec *one = rename_src[j].one;
+			struct diff_filespec *one = rename_src[j].p->one;
 			struct diff_score this_src;
+
+			if (skip_unmodified &&
+			    diff_unmodified_pair(rename_src[j].p))
+				continue;
+
 			this_src.score = estimate_similarity(one, two,
 							     minimum_score);
 			this_src.name_score = basename_same(one, two);
 			this_src.dst = i;
 			this_src.src = j;
 			record_if_better(m, &this_src);
+			/*
+			 * Once we run estimate_similarity,
+			 * We do not need the text anymore.
+			 */
 			diff_free_filespec_blob(one);
+			diff_free_filespec_blob(two);
 		}
-		/* We do not need the text anymore */
-		diff_free_filespec_blob(two);
 		dst_cnt++;
+		display_progress(progress, (i+1)*rename_src_nr);
 	}
+	stop_progress(&progress);
 
 	/* cost matrix sorted by most to least similar pair */
 	qsort(mx, dst_cnt * NUM_CANDIDATE_PER_DST, sizeof(*mx), score_compare);
 
-	for (i = 0; i < dst_cnt * NUM_CANDIDATE_PER_DST; i++) {
-		struct diff_rename_dst *dst;
-
-		if ((mx[i].dst < 0) ||
-		    (mx[i].score < minimum_score))
-			break; /* there is no more usable pair. */
-		dst = &rename_dst[mx[i].dst];
-		if (dst->pair)
-			continue; /* already done, either exact or fuzzy. */
-		if (rename_src[mx[i].src].one->rename_used)
-			continue;
-		record_rename_pair(mx[i].dst, mx[i].src, mx[i].score);
-		rename_count++;
-	}
-
-	for (i = 0; i < dst_cnt * NUM_CANDIDATE_PER_DST; i++) {
-		struct diff_rename_dst *dst;
-
-		if ((mx[i].dst < 0) ||
-		    (mx[i].score < minimum_score))
-			break; /* there is no more usable pair. */
-		dst = &rename_dst[mx[i].dst];
-		if (dst->pair)
-			continue; /* already done, either exact or fuzzy. */
-		record_rename_pair(mx[i].dst, mx[i].src, mx[i].score);
-		rename_count++;
-	}
+	rename_count += find_renames(mx, dst_cnt, minimum_score, 0);
+	if (detect_rename == DIFF_DETECT_COPY)
+		rename_count += find_renames(mx, dst_cnt, minimum_score, 1);
 	free(mx);
 
  cleanup:
 	/* At this point, we have found some renames and copies and they
 	 * are recorded in rename_dst.  The original list is still in *q.
 	 */
-	outq.queue = NULL;
-	outq.nr = outq.alloc = 0;
+	DIFF_QUEUE_CLEAR(&outq);
 	for (i = 0; i < q->nr; i++) {
 		struct diff_filepair *p = q->queue[i];
 		struct diff_filepair *pair_to_free = NULL;
 
-		if (!DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two)) {
+		if (DIFF_PAIR_UNMERGED(p)) {
+			diff_q(&outq, p);
+		}
+		else if (!DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two)) {
 			/*
 			 * Creation
 			 *
diff --git a/diffcore.h b/diffcore.h
index 5b63458..be0739c 100644
--- a/diffcore.h
+++ b/diffcore.h
@@ -18,7 +18,7 @@
 #define MAX_SCORE 60000.0
 #define DEFAULT_RENAME_SCORE 30000 /* rename/copy similarity minimum (50%) */
 #define DEFAULT_BREAK_SCORE  30000 /* minimum for break to happen (50%) */
-#define DEFAULT_MERGE_SCORE  36000 /* maximum for break-merge to happen 60%) */
+#define DEFAULT_MERGE_SCORE  36000 /* maximum for break-merge to happen (60%) */
 
 #define MINIMUM_BREAK_SIZE     400 /* do not break a file smaller than this */
 
@@ -42,7 +42,11 @@
 #define DIFF_FILE_VALID(spec) (((spec)->mode) != 0)
 	unsigned should_free : 1; /* data should be free()'ed */
 	unsigned should_munmap : 1; /* data should be munmap()'ed */
-
+	unsigned dirty_submodule : 2;  /* For submodules: its work tree is dirty */
+	unsigned is_stdin : 1;
+#define DIRTY_SUBMODULE_UNTRACKED 1
+#define DIRTY_SUBMODULE_MODIFIED  2
+	unsigned has_more_entries : 1; /* only appear in combined diff */
 	struct userdiff_driver *driver;
 	/* data should be considered "binary"; -1 means "don't know yet" */
 	int is_binary;
@@ -89,6 +93,11 @@
 	int alloc;
 	int nr;
 };
+#define DIFF_QUEUE_CLEAR(q) \
+	do { \
+		(q)->queue = NULL; \
+		(q)->nr = (q)->alloc = 0; \
+	} while (0)
 
 extern struct diff_queue_struct diff_queued_diff;
 extern struct diff_filepair *diff_queue(struct diff_queue_struct *,
@@ -99,7 +108,7 @@
 extern void diffcore_break(int);
 extern void diffcore_rename(struct diff_options *);
 extern void diffcore_merge_broken(void);
-extern void diffcore_pickaxe(const char *needle, int opts);
+extern void diffcore_pickaxe(struct diff_options *);
 extern void diffcore_order(const char *orderfile);
 
 #define DIFF_DEBUG 0
@@ -108,9 +117,9 @@
 void diff_debug_filepair(const struct diff_filepair *, int);
 void diff_debug_queue(const char *, struct diff_queue_struct *);
 #else
-#define diff_debug_filespec(a,b,c) do {} while(0)
-#define diff_debug_filepair(a,b) do {} while(0)
-#define diff_debug_queue(a,b) do {} while(0)
+#define diff_debug_filespec(a,b,c) do { /* nothing */ } while (0)
+#define diff_debug_filepair(a,b) do { /* nothing */ } while (0)
+#define diff_debug_queue(a,b) do { /* nothing */ } while (0)
 #endif
 
 extern int diffcore_count_changes(struct diff_filespec *src,
diff --git a/dir.c b/dir.c
index e05b850..a772c6d 100644
--- a/dir.c
+++ b/dir.c
@@ -18,59 +18,90 @@
 	int check_only, const struct path_simplify *simplify);
 static int get_dtype(struct dirent *de, const char *path, int len);
 
-static int common_prefix(const char **pathspec)
+/* helper string functions with support for the ignore_case flag */
+int strcmp_icase(const char *a, const char *b)
 {
-	const char *path, *slash, *next;
-	int prefix;
+	return ignore_case ? strcasecmp(a, b) : strcmp(a, b);
+}
+
+int strncmp_icase(const char *a, const char *b, size_t count)
+{
+	return ignore_case ? strncasecmp(a, b, count) : strncmp(a, b, count);
+}
+
+int fnmatch_icase(const char *pattern, const char *string, int flags)
+{
+	return fnmatch(pattern, string, flags | (ignore_case ? FNM_CASEFOLD : 0));
+}
+
+static size_t common_prefix_len(const char **pathspec)
+{
+	const char *n, *first;
+	size_t max = 0;
 
 	if (!pathspec)
-		return 0;
+		return max;
 
-	path = *pathspec;
-	slash = strrchr(path, '/');
-	if (!slash)
-		return 0;
-
-	prefix = slash - path + 1;
-	while ((next = *++pathspec) != NULL) {
-		int len = strlen(next);
-		if (len >= prefix && !memcmp(path, next, prefix))
-			continue;
-		len = prefix - 1;
-		for (;;) {
-			if (!len)
-				return 0;
-			if (next[--len] != '/')
-				continue;
-			if (memcmp(path, next, len+1))
-				continue;
-			prefix = len + 1;
-			break;
+	first = *pathspec;
+	while ((n = *pathspec++)) {
+		size_t i, len = 0;
+		for (i = 0; first == n || i < max; i++) {
+			char c = n[i];
+			if (!c || c != first[i] || is_glob_special(c))
+				break;
+			if (c == '/')
+				len = i + 1;
+		}
+		if (first == n || len < max) {
+			max = len;
+			if (!max)
+				break;
 		}
 	}
-	return prefix;
+	return max;
+}
+
+/*
+ * Returns a copy of the longest leading path common among all
+ * pathspecs.
+ */
+char *common_prefix(const char **pathspec)
+{
+	unsigned long len = common_prefix_len(pathspec);
+
+	return len ? xmemdupz(*pathspec, len) : NULL;
 }
 
 int fill_directory(struct dir_struct *dir, const char **pathspec)
 {
-	const char *path;
-	int len;
+	size_t len;
 
 	/*
 	 * Calculate common prefix for the pathspec, and
 	 * use that to optimize the directory walk
 	 */
-	len = common_prefix(pathspec);
-	path = "";
-
-	if (len)
-		path = xmemdupz(*pathspec, len);
+	len = common_prefix_len(pathspec);
 
 	/* Read the directory and prune it */
-	read_directory(dir, path, len, pathspec);
+	read_directory(dir, pathspec ? *pathspec : "", len, pathspec);
 	return len;
 }
 
+int within_depth(const char *name, int namelen,
+			int depth, int max_depth)
+{
+	const char *cp = name, *cpe = name + namelen;
+
+	while (cp < cpe) {
+		if (*cp++ != '/')
+			continue;
+		depth++;
+		if (depth > max_depth)
+			return 0;
+	}
+	return 1;
+}
+
 /*
  * Does 'match' match the given name?
  * A match is found if
@@ -91,16 +122,30 @@
 	if (!*match)
 		return MATCHED_RECURSIVELY;
 
-	for (;;) {
-		unsigned char c1 = *match;
-		unsigned char c2 = *name;
-		if (c1 == '\0' || is_glob_special(c1))
-			break;
-		if (c1 != c2)
-			return 0;
-		match++;
-		name++;
-		namelen--;
+	if (ignore_case) {
+		for (;;) {
+			unsigned char c1 = tolower(*match);
+			unsigned char c2 = tolower(*name);
+			if (c1 == '\0' || is_glob_special(c1))
+				break;
+			if (c1 != c2)
+				return 0;
+			match++;
+			name++;
+			namelen--;
+		}
+	} else {
+		for (;;) {
+			unsigned char c1 = *match;
+			unsigned char c2 = *name;
+			if (c1 == '\0' || is_glob_special(c1))
+				break;
+			if (c1 != c2)
+				return 0;
+			match++;
+			name++;
+			namelen--;
+		}
 	}
 
 
@@ -109,8 +154,8 @@
 	 * we need to match by fnmatch
 	 */
 	matchlen = strlen(match);
-	if (strncmp(match, name, matchlen))
-		return !fnmatch(match, name, 0) ? MATCHED_FNMATCH : 0;
+	if (strncmp_icase(match, name, matchlen))
+		return !fnmatch_icase(match, name, 0) ? MATCHED_FNMATCH : 0;
 
 	if (namelen == matchlen)
 		return MATCHED_EXACTLY;
@@ -154,9 +199,113 @@
 	return retval;
 }
 
+/*
+ * Does 'match' match the given name?
+ * A match is found if
+ *
+ * (1) the 'match' string is leading directory of 'name', or
+ * (2) the 'match' string is a wildcard and matches 'name', or
+ * (3) the 'match' string is exactly the same as 'name'.
+ *
+ * and the return value tells which case it was.
+ *
+ * It returns 0 when there is no match.
+ */
+static int match_pathspec_item(const struct pathspec_item *item, int prefix,
+			       const char *name, int namelen)
+{
+	/* name/namelen has prefix cut off by caller */
+	const char *match = item->match + prefix;
+	int matchlen = item->len - prefix;
+
+	/* If the match was just the prefix, we matched */
+	if (!*match)
+		return MATCHED_RECURSIVELY;
+
+	if (matchlen <= namelen && !strncmp(match, name, matchlen)) {
+		if (matchlen == namelen)
+			return MATCHED_EXACTLY;
+
+		if (match[matchlen-1] == '/' || name[matchlen] == '/')
+			return MATCHED_RECURSIVELY;
+	}
+
+	if (item->use_wildcard && !fnmatch(match, name, 0))
+		return MATCHED_FNMATCH;
+
+	return 0;
+}
+
+/*
+ * Given a name and a list of pathspecs, see if the name matches
+ * any of the pathspecs.  The caller is also interested in seeing
+ * all pathspec matches some names it calls this function with
+ * (otherwise the user could have mistyped the unmatched pathspec),
+ * and a mark is left in seen[] array for pathspec element that
+ * actually matched anything.
+ */
+int match_pathspec_depth(const struct pathspec *ps,
+			 const char *name, int namelen,
+			 int prefix, char *seen)
+{
+	int i, retval = 0;
+
+	if (!ps->nr) {
+		if (!ps->recursive || ps->max_depth == -1)
+			return MATCHED_RECURSIVELY;
+
+		if (within_depth(name, namelen, 0, ps->max_depth))
+			return MATCHED_EXACTLY;
+		else
+			return 0;
+	}
+
+	name += prefix;
+	namelen -= prefix;
+
+	for (i = ps->nr - 1; i >= 0; i--) {
+		int how;
+		if (seen && seen[i] == MATCHED_EXACTLY)
+			continue;
+		how = match_pathspec_item(ps->items+i, prefix, name, namelen);
+		if (ps->recursive && ps->max_depth != -1 &&
+		    how && how != MATCHED_FNMATCH) {
+			int len = ps->items[i].len;
+			if (name[len] == '/')
+				len++;
+			if (within_depth(name+len, namelen-len, 0, ps->max_depth))
+				how = MATCHED_EXACTLY;
+			else
+				how = 0;
+		}
+		if (how) {
+			if (retval < how)
+				retval = how;
+			if (seen && seen[i] < how)
+				seen[i] = how;
+		}
+	}
+	return retval;
+}
+
+/*
+ * Return the length of the "simple" part of a path match limiter.
+ */
+static int simple_length(const char *match)
+{
+	int len = -1;
+
+	for (;;) {
+		unsigned char c = *match++;
+		len++;
+		if (c == '\0' || is_glob_special(c))
+			return len;
+	}
+}
+
 static int no_wildcard(const char *string)
 {
-	return string[strcspn(string, "*?[{\\")] == '\0';
+	return string[simple_length(string)] == '\0';
 }
 
 void add_exclude(const char *string, const char *base,
@@ -192,44 +341,94 @@
 	x->flags = flags;
 	if (!strchr(string, '/'))
 		x->flags |= EXC_FLAG_NODIR;
-	if (no_wildcard(string))
-		x->flags |= EXC_FLAG_NOWILDCARD;
+	x->nowildcardlen = simple_length(string);
 	if (*string == '*' && no_wildcard(string+1))
 		x->flags |= EXC_FLAG_ENDSWITH;
 	ALLOC_GROW(which->excludes, which->nr + 1, which->alloc);
 	which->excludes[which->nr++] = x;
 }
 
-static int add_excludes_from_file_1(const char *fname,
-				    const char *base,
-				    int baselen,
-				    char **buf_p,
-				    struct exclude_list *which)
+static void *read_skip_worktree_file_from_index(const char *path, size_t *size)
+{
+	int pos, len;
+	unsigned long sz;
+	enum object_type type;
+	void *data;
+	struct index_state *istate = &the_index;
+
+	len = strlen(path);
+	pos = index_name_pos(istate, path, len);
+	if (pos < 0)
+		return NULL;
+	if (!ce_skip_worktree(istate->cache[pos]))
+		return NULL;
+	data = read_sha1_file(istate->cache[pos]->sha1, &type, &sz);
+	if (!data || type != OBJ_BLOB) {
+		free(data);
+		return NULL;
+	}
+	*size = xsize_t(sz);
+	return data;
+}
+
+void free_excludes(struct exclude_list *el)
+{
+	int i;
+
+	for (i = 0; i < el->nr; i++)
+		free(el->excludes[i]);
+	free(el->excludes);
+
+	el->nr = 0;
+	el->excludes = NULL;
+}
+
+int add_excludes_from_file_to_list(const char *fname,
+				   const char *base,
+				   int baselen,
+				   char **buf_p,
+				   struct exclude_list *which,
+				   int check_index)
 {
 	struct stat st;
 	int fd, i;
-	size_t size;
+	size_t size = 0;
 	char *buf, *entry;
 
 	fd = open(fname, O_RDONLY);
-	if (fd < 0 || fstat(fd, &st) < 0)
-		goto err;
-	size = xsize_t(st.st_size);
-	if (size == 0) {
+	if (fd < 0 || fstat(fd, &st) < 0) {
+		if (0 <= fd)
+			close(fd);
+		if (!check_index ||
+		    (buf = read_skip_worktree_file_from_index(fname, &size)) == NULL)
+			return -1;
+		if (size == 0) {
+			free(buf);
+			return 0;
+		}
+		if (buf[size-1] != '\n') {
+			buf = xrealloc(buf, size+1);
+			buf[size++] = '\n';
+		}
+	}
+	else {
+		size = xsize_t(st.st_size);
+		if (size == 0) {
+			close(fd);
+			return 0;
+		}
+		buf = xmalloc(size+1);
+		if (read_in_full(fd, buf, size) != size) {
+			free(buf);
+			close(fd);
+			return -1;
+		}
+		buf[size++] = '\n';
 		close(fd);
-		return 0;
 	}
-	buf = xmalloc(size+1);
-	if (read_in_full(fd, buf, size) != size)
-	{
-		free(buf);
-		goto err;
-	}
-	close(fd);
 
 	if (buf_p)
 		*buf_p = buf;
-	buf[size++] = '\n';
 	entry = buf;
 	for (i = 0; i < size; i++) {
 		if (buf[i] == '\n') {
@@ -241,17 +440,12 @@
 		}
 	}
 	return 0;
-
- err:
-	if (0 <= fd)
-		close(fd);
-	return -1;
 }
 
 void add_excludes_from_file(struct dir_struct *dir, const char *fname)
 {
-	if (add_excludes_from_file_1(fname, "", 0, NULL,
-				     &dir->exclude_list[EXC_FILE]) < 0)
+	if (add_excludes_from_file_to_list(fname, "", 0, NULL,
+					   &dir->exclude_list[EXC_FILE], 0) < 0)
 		die("cannot use %s as an exclude file", fname);
 }
 
@@ -300,9 +494,9 @@
 		memcpy(dir->basebuf + current, base + current,
 		       stk->baselen - current);
 		strcpy(dir->basebuf + stk->baselen, dir->exclude_per_dir);
-		add_excludes_from_file_1(dir->basebuf,
-					 dir->basebuf, stk->baselen,
-					 &stk->filebuf, el);
+		add_excludes_from_file_to_list(dir->basebuf,
+					       dir->basebuf, stk->baselen,
+					       &stk->filebuf, el, 1);
 		dir->exclude_stack = stk;
 		current = stk->baselen;
 	}
@@ -312,68 +506,80 @@
 /* Scan the list and let the last match determine the fate.
  * Return 1 for exclude, 0 for include and -1 for undecided.
  */
-static int excluded_1(const char *pathname,
-		      int pathlen, const char *basename, int *dtype,
-		      struct exclude_list *el)
+int excluded_from_list(const char *pathname,
+		       int pathlen, const char *basename, int *dtype,
+		       struct exclude_list *el)
 {
 	int i;
 
-	if (el->nr) {
-		for (i = el->nr - 1; 0 <= i; i--) {
-			struct exclude *x = el->excludes[i];
-			const char *exclude = x->pattern;
-			int to_exclude = x->to_exclude;
+	if (!el->nr)
+		return -1;	/* undefined */
 
-			if (x->flags & EXC_FLAG_MUSTBEDIR) {
-				if (*dtype == DT_UNKNOWN)
-					*dtype = get_dtype(NULL, pathname, pathlen);
-				if (*dtype != DT_DIR)
-					continue;
-			}
+	for (i = el->nr - 1; 0 <= i; i--) {
+		struct exclude *x = el->excludes[i];
+		const char *name, *exclude = x->pattern;
+		int to_exclude = x->to_exclude;
+		int namelen, prefix = x->nowildcardlen;
 
-			if (x->flags & EXC_FLAG_NODIR) {
-				/* match basename */
-				if (x->flags & EXC_FLAG_NOWILDCARD) {
-					if (!strcmp(exclude, basename))
-						return to_exclude;
-				} else if (x->flags & EXC_FLAG_ENDSWITH) {
-					if (x->patternlen - 1 <= pathlen &&
-					    !strcmp(exclude + 1, pathname + pathlen - x->patternlen + 1))
-						return to_exclude;
-				} else {
-					if (fnmatch(exclude, basename, 0) == 0)
-						return to_exclude;
-				}
-			}
-			else {
-				/* match with FNM_PATHNAME:
-				 * exclude has base (baselen long) implicitly
-				 * in front of it.
-				 */
-				int baselen = x->baselen;
-				if (*exclude == '/')
-					exclude++;
-
-				if (pathlen < baselen ||
-				    (baselen && pathname[baselen-1] != '/') ||
-				    strncmp(pathname, x->base, baselen))
-				    continue;
-
-				if (x->flags & EXC_FLAG_NOWILDCARD) {
-					if (!strcmp(exclude, pathname + baselen))
-						return to_exclude;
-				} else {
-					if (fnmatch(exclude, pathname+baselen,
-						    FNM_PATHNAME) == 0)
-					    return to_exclude;
-				}
-			}
+		if (x->flags & EXC_FLAG_MUSTBEDIR) {
+			if (*dtype == DT_UNKNOWN)
+				*dtype = get_dtype(NULL, pathname, pathlen);
+			if (*dtype != DT_DIR)
+				continue;
 		}
+
+		if (x->flags & EXC_FLAG_NODIR) {
+			/* match basename */
+			if (prefix == x->patternlen) {
+				if (!strcmp_icase(exclude, basename))
+					return to_exclude;
+			} else if (x->flags & EXC_FLAG_ENDSWITH) {
+				if (x->patternlen - 1 <= pathlen &&
+				    !strcmp_icase(exclude + 1, pathname + pathlen - x->patternlen + 1))
+					return to_exclude;
+			} else {
+				if (fnmatch_icase(exclude, basename, 0) == 0)
+					return to_exclude;
+			}
+			continue;
+		}
+
+		/* match with FNM_PATHNAME:
+		 * exclude has base (baselen long) implicitly in front of it.
+		 */
+		if (*exclude == '/') {
+			exclude++;
+			prefix--;
+		}
+
+		if (pathlen < x->baselen ||
+		    (x->baselen && pathname[x->baselen-1] != '/') ||
+		    strncmp_icase(pathname, x->base, x->baselen))
+			continue;
+
+		namelen = x->baselen ? pathlen - x->baselen : pathlen;
+		name = pathname + pathlen  - namelen;
+
+		/* if the non-wildcard part is longer than the
+		   remaining pathname, surely it cannot match */
+		if (prefix > namelen)
+			continue;
+
+		if (prefix) {
+			if (strncmp_icase(exclude, name, prefix))
+				continue;
+			exclude += prefix;
+			name    += prefix;
+			namelen -= prefix;
+		}
+
+		if (!namelen || !fnmatch_icase(exclude, name, FNM_PATHNAME))
+			return to_exclude;
 	}
 	return -1; /* undecided */
 }
 
-int excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)
+static int excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)
 {
 	int pathlen = strlen(pathname);
 	int st;
@@ -382,8 +588,8 @@
 
 	prep_exclude(dir, pathname, basename-pathname);
 	for (st = EXC_CMDL; st <= EXC_FILE; st++) {
-		switch (excluded_1(pathname, pathlen, basename,
-				   dtype_p, &dir->exclude_list[st])) {
+		switch (excluded_from_list(pathname, pathlen, basename,
+					   dtype_p, &dir->exclude_list[st])) {
 		case 0:
 			return 0;
 		case 1:
@@ -393,6 +599,64 @@
 	return 0;
 }
 
+void path_exclude_check_init(struct path_exclude_check *check,
+			     struct dir_struct *dir)
+{
+	check->dir = dir;
+	strbuf_init(&check->path, 256);
+}
+
+void path_exclude_check_clear(struct path_exclude_check *check)
+{
+	strbuf_release(&check->path);
+}
+
+/*
+ * Is this name excluded?  This is for a caller like show_files() that
+ * do not honor directory hierarchy and iterate through paths that are
+ * possibly in an ignored directory.
+ *
+ * A path to a directory known to be excluded is left in check->path to
+ * optimize for repeated checks for files in the same excluded directory.
+ */
+int path_excluded(struct path_exclude_check *check,
+		  const char *name, int namelen, int *dtype)
+{
+	int i;
+	struct strbuf *path = &check->path;
+
+	/*
+	 * we allow the caller to pass namelen as an optimization; it
+	 * must match the length of the name, as we eventually call
+	 * excluded() on the whole name string.
+	 */
+	if (namelen < 0)
+		namelen = strlen(name);
+
+	if (path->len &&
+	    path->len <= namelen &&
+	    !memcmp(name, path->buf, path->len) &&
+	    (!name[path->len] || name[path->len] == '/'))
+		return 1;
+
+	strbuf_setlen(path, 0);
+	for (i = 0; name[i]; i++) {
+		int ch = name[i];
+
+		if (ch == '/') {
+			int dt = DT_DIR;
+			if (excluded(check->dir, path->buf, &dt))
+				return 1;
+		}
+		strbuf_addch(path, ch);
+	}
+
+	/* An entry in the index; cannot be a directory with subentries */
+	strbuf_setlen(path, 0);
+
+	return excluded(check->dir, name, dtype);
+}
+
 static struct dir_entry *dir_entry_new(const char *pathname, int len)
 {
 	struct dir_entry *ent;
@@ -413,7 +677,7 @@
 	return dir->entries[dir->nr++] = dir_entry_new(pathname, len);
 }
 
-static struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname, int len)
+struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname, int len)
 {
 	if (!cache_name_is_other(pathname, len))
 		return NULL;
@@ -425,10 +689,43 @@
 enum exist_status {
 	index_nonexistent = 0,
 	index_directory,
-	index_gitdir,
+	index_gitdir
 };
 
 /*
+ * Do not use the alphabetically stored index to look up
+ * the directory name; instead, use the case insensitive
+ * name hash.
+ */
+static enum exist_status directory_exists_in_index_icase(const char *dirname, int len)
+{
+	struct cache_entry *ce = index_name_exists(&the_index, dirname, len + 1, ignore_case);
+	unsigned char endchar;
+
+	if (!ce)
+		return index_nonexistent;
+	endchar = ce->name[len];
+
+	/*
+	 * The cache_entry structure returned will contain this dirname
+	 * and possibly additional path components.
+	 */
+	if (endchar == '/')
+		return index_directory;
+
+	/*
+	 * If there are no additional path components, then this cache_entry
+	 * represents a submodule.  Submodules, despite being directories,
+	 * are stored in the cache without a closing slash.
+	 */
+	if (!endchar && S_ISGITLINK(ce->ce_mode))
+		return index_gitdir;
+
+	/* This should never be hit, but it exists just in case. */
+	return index_nonexistent;
+}
+
+/*
  * The index sorts alphabetically by entry name, which
  * means that a gitlink sorts as '\0' at the end, while
  * a directory (which is defined not as an entry, but as
@@ -437,7 +734,12 @@
  */
 static enum exist_status directory_exists_in_index(const char *dirname, int len)
 {
-	int pos = cache_name_pos(dirname, len);
+	int pos;
+
+	if (ignore_case)
+		return directory_exists_in_index_icase(dirname, len);
+
+	pos = cache_name_pos(dirname, len);
 	if (pos < 0)
 		pos = -pos-1;
 	while (pos < active_nr) {
@@ -493,7 +795,7 @@
 enum directory_treatment {
 	show_directory,
 	ignore_directory,
-	recurse_into_directory,
+	recurse_into_directory
 };
 
 static enum directory_treatment treat_directory(struct dir_struct *dir,
@@ -554,13 +856,29 @@
 	return 0;
 }
 
-static int in_pathspec(const char *path, int len, const struct path_simplify *simplify)
+/*
+ * This function tells us whether an excluded path matches a
+ * list of "interesting" pathspecs. That is, whether a path matched
+ * by any of the pathspecs could possibly be ignored by excluding
+ * the specified path. This can happen if:
+ *
+ *   1. the path is mentioned explicitly in the pathspec
+ *
+ *   2. the path is a directory prefix of some element in the
+ *      pathspec
+ */
+static int exclude_matches_pathspec(const char *path, int len,
+		const struct path_simplify *simplify)
 {
 	if (simplify) {
 		for (; simplify->path; simplify++) {
 			if (len == simplify->len
 			    && !memcmp(path, simplify->path, len))
 				return 1;
+			if (len < simplify->len
+			    && simplify->path[len] == '/'
+			    && !memcmp(path, simplify->path, len))
+				return 1;
 		}
 	}
 	return 0;
@@ -625,6 +943,86 @@
 	return dtype;
 }
 
+enum path_treatment {
+	path_ignored,
+	path_handled,
+	path_recurse
+};
+
+static enum path_treatment treat_one_path(struct dir_struct *dir,
+					  struct strbuf *path,
+					  const struct path_simplify *simplify,
+					  int dtype, struct dirent *de)
+{
+	int exclude = excluded(dir, path->buf, &dtype);
+	if (exclude && (dir->flags & DIR_COLLECT_IGNORED)
+	    && exclude_matches_pathspec(path->buf, path->len, simplify))
+		dir_add_ignored(dir, path->buf, path->len);
+
+	/*
+	 * Excluded? If we don't explicitly want to show
+	 * ignored files, ignore it
+	 */
+	if (exclude && !(dir->flags & DIR_SHOW_IGNORED))
+		return path_ignored;
+
+	if (dtype == DT_UNKNOWN)
+		dtype = get_dtype(de, path->buf, path->len);
+
+	/*
+	 * Do we want to see just the ignored files?
+	 * We still need to recurse into directories,
+	 * even if we don't ignore them, since the
+	 * directory may contain files that we do..
+	 */
+	if (!exclude && (dir->flags & DIR_SHOW_IGNORED)) {
+		if (dtype != DT_DIR)
+			return path_ignored;
+	}
+
+	switch (dtype) {
+	default:
+		return path_ignored;
+	case DT_DIR:
+		strbuf_addch(path, '/');
+		switch (treat_directory(dir, path->buf, path->len, simplify)) {
+		case show_directory:
+			if (exclude != !!(dir->flags
+					  & DIR_SHOW_IGNORED))
+				return path_ignored;
+			break;
+		case recurse_into_directory:
+			return path_recurse;
+		case ignore_directory:
+			return path_ignored;
+		}
+		break;
+	case DT_REG:
+	case DT_LNK:
+		break;
+	}
+	return path_handled;
+}
+
+static enum path_treatment treat_path(struct dir_struct *dir,
+				      struct dirent *de,
+				      struct strbuf *path,
+				      int baselen,
+				      const struct path_simplify *simplify)
+{
+	int dtype;
+
+	if (is_dot_or_dotdot(de->d_name) || !strcmp(de->d_name, ".git"))
+		return path_ignored;
+	strbuf_setlen(path, baselen);
+	strbuf_addstr(path, de->d_name);
+	if (simplify_away(path->buf, path->len, simplify))
+		return path_ignored;
+
+	dtype = DTYPE(de);
+	return treat_one_path(dir, path, simplify, dtype, de);
+}
+
 /*
  * Read a directory tree. We currently ignore anything but
  * directories, regular files and symlinks. That's because git
@@ -634,92 +1032,42 @@
  * Also, we ignore the name ".git" (even if it is not a directory).
  * That likely will not change.
  */
-static int read_directory_recursive(struct dir_struct *dir, const char *base, int baselen, int check_only, const struct path_simplify *simplify)
+static int read_directory_recursive(struct dir_struct *dir,
+				    const char *base, int baselen,
+				    int check_only,
+				    const struct path_simplify *simplify)
 {
-	DIR *fdir = opendir(*base ? base : ".");
+	DIR *fdir;
 	int contents = 0;
+	struct dirent *de;
+	struct strbuf path = STRBUF_INIT;
 
-	if (fdir) {
-		struct dirent *de;
-		char path[PATH_MAX + 1];
-		memcpy(path, base, baselen);
+	strbuf_add(&path, base, baselen);
 
-		while ((de = readdir(fdir)) != NULL) {
-			int len, dtype;
-			int exclude;
+	fdir = opendir(path.len ? path.buf : ".");
+	if (!fdir)
+		goto out;
 
-			if (is_dot_or_dotdot(de->d_name) ||
-			     !strcmp(de->d_name, ".git"))
-				continue;
-			len = strlen(de->d_name);
-			/* Ignore overly long pathnames! */
-			if (len + baselen + 8 > sizeof(path))
-				continue;
-			memcpy(path + baselen, de->d_name, len+1);
-			len = baselen + len;
-			if (simplify_away(path, len, simplify))
-				continue;
-
-			dtype = DTYPE(de);
-			exclude = excluded(dir, path, &dtype);
-			if (exclude && (dir->flags & DIR_COLLECT_IGNORED)
-			    && in_pathspec(path, len, simplify))
-				dir_add_ignored(dir, path,len);
-
-			/*
-			 * Excluded? If we don't explicitly want to show
-			 * ignored files, ignore it
-			 */
-			if (exclude && !(dir->flags & DIR_SHOW_IGNORED))
-				continue;
-
-			if (dtype == DT_UNKNOWN)
-				dtype = get_dtype(de, path, len);
-
-			/*
-			 * Do we want to see just the ignored files?
-			 * We still need to recurse into directories,
-			 * even if we don't ignore them, since the
-			 * directory may contain files that we do..
-			 */
-			if (!exclude && (dir->flags & DIR_SHOW_IGNORED)) {
-				if (dtype != DT_DIR)
-					continue;
-			}
-
-			switch (dtype) {
-			default:
-				continue;
-			case DT_DIR:
-				memcpy(path + len, "/", 2);
-				len++;
-				switch (treat_directory(dir, path, len, simplify)) {
-				case show_directory:
-					if (exclude != !!(dir->flags
-							& DIR_SHOW_IGNORED))
-						continue;
-					break;
-				case recurse_into_directory:
-					contents += read_directory_recursive(dir,
-						path, len, 0, simplify);
-					continue;
-				case ignore_directory:
-					continue;
-				}
-				break;
-			case DT_REG:
-			case DT_LNK:
-				break;
-			}
-			contents++;
-			if (check_only)
-				goto exit_early;
-			else
-				dir_add_name(dir, path, len);
+	while ((de = readdir(fdir)) != NULL) {
+		switch (treat_path(dir, de, &path, baselen, simplify)) {
+		case path_recurse:
+			contents += read_directory_recursive(dir, path.buf,
+							     path.len, 0,
+							     simplify);
+			continue;
+		case path_ignored:
+			continue;
+		case path_handled:
+			break;
 		}
-exit_early:
-		closedir(fdir);
+		contents++;
+		if (check_only)
+			break;
+		dir_add_name(dir, path.buf, path.len);
 	}
+	closedir(fdir);
+ out:
+	strbuf_release(&path);
 
 	return contents;
 }
@@ -733,21 +1081,6 @@
 				  e2->name, e2->len);
 }
 
-/*
- * Return the length of the "simple" part of a path match limiter.
- */
-static int simple_length(const char *match)
-{
-	int len = -1;
-
-	for (;;) {
-		unsigned char c = *match++;
-		len++;
-		if (c == '\0' || is_glob_special(c))
-			return len;
-	}
-}
-
 static struct path_simplify *create_simplify(const char **pathspec)
 {
 	int nr, alloc = 0;
@@ -778,6 +1111,44 @@
 	free(simplify);
 }
 
+static int treat_leading_path(struct dir_struct *dir,
+			      const char *path, int len,
+			      const struct path_simplify *simplify)
+{
+	struct strbuf sb = STRBUF_INIT;
+	int baselen, rc = 0;
+	const char *cp;
+
+	while (len && path[len - 1] == '/')
+		len--;
+	if (!len)
+		return 1;
+	baselen = 0;
+	while (1) {
+		cp = path + baselen + !!baselen;
+		cp = memchr(cp, '/', path + len - cp);
+		if (!cp)
+			baselen = len;
+		else
+			baselen = cp - path;
+		strbuf_setlen(&sb, 0);
+		strbuf_add(&sb, path, baselen);
+		if (!is_directory(sb.buf))
+			break;
+		if (simplify_away(sb.buf, sb.len, simplify))
+			break;
+		if (treat_one_path(dir, &sb, simplify,
+				   DT_DIR, NULL) == path_ignored)
+			break; /* do not recurse into it */
+		if (len <= baselen) {
+			rc = 1;
+			break; /* finished checking */
+		}
+	}
+	strbuf_release(&sb);
+	return rc;
+}
+
 int read_directory(struct dir_struct *dir, const char *path, int len, const char **pathspec)
 {
 	struct path_simplify *simplify;
@@ -786,7 +1157,8 @@
 		return dir->nr;
 
 	simplify = create_simplify(pathspec);
-	read_directory_recursive(dir, path, len, 0, simplify);
+	if (!len || treat_leading_path(dir, path, len, simplify))
+		read_directory_recursive(dir, path, len, 0, simplify);
 	free_simplify(simplify);
 	qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
 	qsort(dir->ignored, dir->ignored_nr, sizeof(struct dir_entry *), cmp_name);
@@ -800,46 +1172,45 @@
 }
 
 /*
- * get_relative_cwd() gets the prefix of the current working directory
- * relative to 'dir'.  If we are not inside 'dir', it returns NULL.
- *
- * As a convenience, it also returns NULL if 'dir' is already NULL.  The
- * reason for this behaviour is that it is natural for functions returning
- * directory names to return NULL to say "this directory does not exist"
- * or "this directory is invalid".  These cases are usually handled the
- * same as if the cwd is not inside 'dir' at all, so get_relative_cwd()
- * returns NULL for both of them.
- *
- * Most notably, get_relative_cwd(buffer, size, get_git_work_tree())
- * unifies the handling of "outside work tree" with "no work tree at all".
+ * Given two normalized paths (a trailing slash is ok), if subdir is
+ * outside dir, return -1.  Otherwise return the offset in subdir that
+ * can be used as relative path to dir.
  */
-char *get_relative_cwd(char *buffer, int size, const char *dir)
+int dir_inside_of(const char *subdir, const char *dir)
 {
-	char *cwd = buffer;
+	int offset = 0;
 
-	if (!dir)
-		return NULL;
-	if (!getcwd(buffer, size))
-		die_errno("can't find the current directory");
+	assert(dir && subdir && *dir && *subdir);
 
-	if (!is_absolute_path(dir))
-		dir = make_absolute_path(dir);
-
-	while (*dir && *dir == *cwd) {
+	while (*dir && *subdir && *dir == *subdir) {
 		dir++;
-		cwd++;
+		subdir++;
+		offset++;
 	}
-	if (*dir)
-		return NULL;
-	if (*cwd == '/')
-		return cwd + 1;
-	return cwd;
+
+	/* hel[p]/me vs hel[l]/yeah */
+	if (*dir && *subdir)
+		return -1;
+
+	if (!*subdir)
+		return !*dir ? offset : -1; /* same dir */
+
+	/* foo/[b]ar vs foo/[] */
+	if (is_dir_sep(dir[-1]))
+		return is_dir_sep(subdir[-1]) ? offset : -1;
+
+	/* foo[/]bar vs foo[] */
+	return is_dir_sep(*subdir) ? offset + 1 : -1;
 }
 
 int is_inside_dir(const char *dir)
 {
-	char buffer[PATH_MAX];
-	return get_relative_cwd(buffer, sizeof(buffer), dir) != NULL;
+	char cwd[PATH_MAX];
+	if (!dir)
+		return 0;
+	if (!getcwd(cwd, sizeof(cwd)))
+		die_errno("can't find the current directory");
+	return dir_inside_of(cwd, dir) >= 0;
 }
 
 int is_empty_dir(const char *path)
@@ -861,14 +1232,32 @@
 	return ret;
 }
 
-int remove_dir_recursively(struct strbuf *path, int only_empty)
+static int remove_dir_recurse(struct strbuf *path, int flag, int *kept_up)
 {
-	DIR *dir = opendir(path->buf);
+	DIR *dir;
 	struct dirent *e;
-	int ret = 0, original_len = path->len, len;
+	int ret = 0, original_len = path->len, len, kept_down = 0;
+	int only_empty = (flag & REMOVE_DIR_EMPTY_ONLY);
+	int keep_toplevel = (flag & REMOVE_DIR_KEEP_TOPLEVEL);
+	unsigned char submodule_head[20];
 
-	if (!dir)
-		return -1;
+	if ((flag & REMOVE_DIR_KEEP_NESTED_GIT) &&
+	    !resolve_gitlink_ref(path->buf, "HEAD", submodule_head)) {
+		/* Do not descend and nuke a nested git work tree. */
+		if (kept_up)
+			*kept_up = 1;
+		return 0;
+	}
+
+	flag &= ~REMOVE_DIR_KEEP_TOPLEVEL;
+	dir = opendir(path->buf);
+	if (!dir) {
+		/* an empty dir could be removed even if it is unreadble */
+		if (!keep_toplevel)
+			return rmdir(path->buf);
+		else
+			return -1;
+	}
 	if (path->buf[original_len - 1] != '/')
 		strbuf_addch(path, '/');
 
@@ -883,7 +1272,7 @@
 		if (lstat(path->buf, &st))
 			; /* fall thru */
 		else if (S_ISDIR(st.st_mode)) {
-			if (!remove_dir_recursively(path, only_empty))
+			if (!remove_dir_recurse(path, flag, &kept_down))
 				continue; /* happy */
 		} else if (!only_empty && !unlink(path->buf))
 			continue; /* happy, too */
@@ -895,20 +1284,36 @@
 	closedir(dir);
 
 	strbuf_setlen(path, original_len);
-	if (!ret)
+	if (!ret && !keep_toplevel && !kept_down)
 		ret = rmdir(path->buf);
+	else if (kept_up)
+		/*
+		 * report the uplevel that it is not an error that we
+		 * did not rmdir() our directory.
+		 */
+		*kept_up = !ret;
 	return ret;
 }
 
+int remove_dir_recursively(struct strbuf *path, int flag)
+{
+	return remove_dir_recurse(path, flag, NULL);
+}
+
 void setup_standard_excludes(struct dir_struct *dir)
 {
 	const char *path;
+	char *xdg_path;
 
 	dir->exclude_per_dir = ".gitignore";
 	path = git_path("info/exclude");
+	if (!excludes_file) {
+		home_config_paths(NULL, &xdg_path, "ignore");
+		excludes_file = xdg_path;
+	}
 	if (!access(path, R_OK))
 		add_excludes_from_file(dir, path);
-	if (excludes_file && !access(excludes_file, R_OK))
+	if (!access(excludes_file, R_OK))
 		add_excludes_from_file(dir, excludes_file);
 }
 
@@ -925,9 +1330,56 @@
 		slash = dirs + (slash - name);
 		do {
 			*slash = '\0';
-		} while (rmdir(dirs) && (slash = strrchr(dirs, '/')));
+		} while (rmdir(dirs) == 0 && (slash = strrchr(dirs, '/')));
 		free(dirs);
 	}
 	return 0;
 }
 
+static int pathspec_item_cmp(const void *a_, const void *b_)
+{
+	struct pathspec_item *a, *b;
+
+	a = (struct pathspec_item *)a_;
+	b = (struct pathspec_item *)b_;
+	return strcmp(a->match, b->match);
+}
+
+int init_pathspec(struct pathspec *pathspec, const char **paths)
+{
+	const char **p = paths;
+	int i;
+
+	memset(pathspec, 0, sizeof(*pathspec));
+	if (!p)
+		return 0;
+	while (*p)
+		p++;
+	pathspec->raw = paths;
+	pathspec->nr = p - paths;
+	if (!pathspec->nr)
+		return 0;
+
+	pathspec->items = xmalloc(sizeof(struct pathspec_item)*pathspec->nr);
+	for (i = 0; i < pathspec->nr; i++) {
+		struct pathspec_item *item = pathspec->items+i;
+		const char *path = paths[i];
+
+		item->match = path;
+		item->len = strlen(path);
+		item->use_wildcard = !no_wildcard(path);
+		if (item->use_wildcard)
+			pathspec->has_wildcard = 1;
+	}
+
+	qsort(pathspec->items, pathspec->nr,
+	      sizeof(struct pathspec_item), pathspec_item_cmp);
+
+	return 0;
+}
+
+void free_pathspec(struct pathspec *pathspec)
+{
+	free(pathspec->items);
+	pathspec->items = NULL;
+}
diff --git a/dir.h b/dir.h
index a631446..893465a 100644
--- a/dir.h
+++ b/dir.h
@@ -1,13 +1,14 @@
 #ifndef DIR_H
 #define DIR_H
 
+#include "strbuf.h"
+
 struct dir_entry {
 	unsigned int len;
 	char name[FLEX_ARRAY]; /* more */
 };
 
 #define EXC_FLAG_NODIR 1
-#define EXC_FLAG_NOWILDCARD 2
 #define EXC_FLAG_ENDSWITH 4
 #define EXC_FLAG_MUSTBEDIR 8
 
@@ -17,6 +18,7 @@
 	struct exclude {
 		const char *pattern;
 		int patternlen;
+		int nowildcardlen;
 		const char *base;
 		int baselen;
 		int to_exclude;
@@ -64,19 +66,44 @@
 #define MATCHED_RECURSIVELY 1
 #define MATCHED_FNMATCH 2
 #define MATCHED_EXACTLY 3
+extern char *common_prefix(const char **pathspec);
 extern int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen);
+extern int match_pathspec_depth(const struct pathspec *pathspec,
+				const char *name, int namelen,
+				int prefix, char *seen);
+extern int within_depth(const char *name, int namelen, int depth, int max_depth);
 
 extern int fill_directory(struct dir_struct *dir, const char **pathspec);
 extern int read_directory(struct dir_struct *, const char *path, int len, const char **pathspec);
 
-extern int excluded(struct dir_struct *, const char *, int *);
+extern int excluded_from_list(const char *pathname, int pathlen, const char *basename,
+			      int *dtype, struct exclude_list *el);
+struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname, int len);
+
+/*
+ * The excluded() API is meant for callers that check each level of leading
+ * directory hierarchies with excluded() to avoid recursing into excluded
+ * directories.  Callers that do not do so should use this API instead.
+ */
+struct path_exclude_check {
+	struct dir_struct *dir;
+	struct strbuf path;
+};
+extern void path_exclude_check_init(struct path_exclude_check *, struct dir_struct *);
+extern void path_exclude_check_clear(struct path_exclude_check *);
+extern int path_excluded(struct path_exclude_check *, const char *, int namelen, int *dtype);
+
+
+extern int add_excludes_from_file_to_list(const char *fname, const char *base, int baselen,
+					  char **buf_p, struct exclude_list *which, int check_index);
 extern void add_excludes_from_file(struct dir_struct *, const char *fname);
 extern void add_exclude(const char *string, const char *base,
 			int baselen, struct exclude_list *which);
+extern void free_excludes(struct exclude_list *el);
 extern int file_exists(const char *);
 
-extern char *get_relative_cwd(char *buffer, int size, const char *dir);
 extern int is_inside_dir(const char *dir);
+extern int dir_inside_of(const char *subdir, const char *dir);
 
 static inline int is_dot_or_dotdot(const char *name)
 {
@@ -88,9 +115,17 @@
 extern int is_empty_dir(const char *dir);
 
 extern void setup_standard_excludes(struct dir_struct *dir);
-extern int remove_dir_recursively(struct strbuf *path, int only_empty);
+
+#define REMOVE_DIR_EMPTY_ONLY 01
+#define REMOVE_DIR_KEEP_NESTED_GIT 02
+#define REMOVE_DIR_KEEP_TOPLEVEL 04
+extern int remove_dir_recursively(struct strbuf *path, int flag);
 
 /* tries to remove the path with empty directories along it, ignores ENOENT */
 extern int remove_path(const char *path);
 
+extern int strcmp_icase(const char *a, const char *b);
+extern int strncmp_icase(const char *a, const char *b, size_t count);
+extern int fnmatch_icase(const char *pattern, const char *string, int flags);
+
 #endif
diff --git a/editor.c b/editor.c
index 4d469d0..d834003 100644
--- a/editor.c
+++ b/editor.c
@@ -2,46 +2,43 @@
 #include "strbuf.h"
 #include "run-command.h"
 
-int launch_editor(const char *path, struct strbuf *buffer, const char *const *env)
-{
-	const char *editor, *terminal;
+#ifndef DEFAULT_EDITOR
+#define DEFAULT_EDITOR "vi"
+#endif
 
-	editor = getenv("GIT_EDITOR");
+const char *git_editor(void)
+{
+	const char *editor = getenv("GIT_EDITOR");
+	const char *terminal = getenv("TERM");
+	int terminal_is_dumb = !terminal || !strcmp(terminal, "dumb");
+
 	if (!editor && editor_program)
 		editor = editor_program;
-	if (!editor)
+	if (!editor && !terminal_is_dumb)
 		editor = getenv("VISUAL");
 	if (!editor)
 		editor = getenv("EDITOR");
 
-	terminal = getenv("TERM");
-	if (!editor && (!terminal || !strcmp(terminal, "dumb")))
-		return error("Terminal is dumb but no VISUAL nor EDITOR defined.");
+	if (!editor && terminal_is_dumb)
+		return NULL;
 
 	if (!editor)
-		editor = "vi";
+		editor = DEFAULT_EDITOR;
+
+	return editor;
+}
+
+int launch_editor(const char *path, struct strbuf *buffer, const char *const *env)
+{
+	const char *editor = git_editor();
+
+	if (!editor)
+		return error("Terminal is dumb, but EDITOR unset");
 
 	if (strcmp(editor, ":")) {
-		size_t len = strlen(editor);
-		int i = 0;
-		int failed;
-		const char *args[6];
-		struct strbuf arg0 = STRBUF_INIT;
+		const char *args[] = { editor, path, NULL };
 
-		if (strcspn(editor, "$ \t'") != len) {
-			/* there are specials */
-			strbuf_addf(&arg0, "%s \"$@\"", editor);
-			args[i++] = "sh";
-			args[i++] = "-c";
-			args[i++] = arg0.buf;
-		}
-		args[i++] = editor;
-		args[i++] = path;
-		args[i] = NULL;
-
-		failed = run_command_v_opt_cd_env(args, 0, NULL, env);
-		strbuf_release(&arg0);
-		if (failed)
+		if (run_command_v_opt_cd_env(args, RUN_USING_SHELL, NULL, env))
 			return error("There was a problem with the editor '%s'.",
 					editor);
 	}
diff --git a/entry.c b/entry.c
index d3e86c7..17a6bcc 100644
--- a/entry.c
+++ b/entry.c
@@ -1,6 +1,7 @@
 #include "cache.h"
 #include "blob.h"
 #include "dir.h"
+#include "streaming.h"
 
 static void create_directories(const char *path, int path_len,
 			       const struct checkout *state)
@@ -91,6 +92,48 @@
 	return NULL;
 }
 
+static int open_output_fd(char *path, struct cache_entry *ce, int to_tempfile)
+{
+	int symlink = (ce->ce_mode & S_IFMT) != S_IFREG;
+	if (to_tempfile) {
+		strcpy(path, symlink
+		       ? ".merge_link_XXXXXX" : ".merge_file_XXXXXX");
+		return mkstemp(path);
+	} else {
+		return create_file(path, !symlink ? ce->ce_mode : 0666);
+	}
+}
+
+static int fstat_output(int fd, const struct checkout *state, struct stat *st)
+{
+	/* use fstat() only when path == ce->name */
+	if (fstat_is_reliable() &&
+	    state->refresh_cache && !state->base_dir_len) {
+		fstat(fd, st);
+		return 1;
+	}
+	return 0;
+}
+
+static int streaming_write_entry(struct cache_entry *ce, char *path,
+				 struct stream_filter *filter,
+				 const struct checkout *state, int to_tempfile,
+				 int *fstat_done, struct stat *statbuf)
+{
+	int result = -1;
+	int fd;
+
+	fd = open_output_fd(path, ce, to_tempfile);
+	if (0 <= fd) {
+		result = stream_blob_to_fd(fd, ce->sha1, filter, 1);
+		*fstat_done = fstat_output(fd, state, statbuf);
+		result = close(fd);
+	}
+	if (result && 0 <= fd)
+		unlink(path);
+	return result;
+}
+
 static int write_entry(struct cache_entry *ce, char *path, const struct checkout *state, int to_tempfile)
 {
 	unsigned int ce_mode_s_ifmt = ce->ce_mode & S_IFMT;
@@ -101,19 +144,28 @@
 	size_t wrote, newsize = 0;
 	struct stat st;
 
+	if (ce_mode_s_ifmt == S_IFREG) {
+		struct stream_filter *filter = get_stream_filter(path, ce->sha1);
+		if (filter &&
+		    !streaming_write_entry(ce, path, filter,
+					   state, to_tempfile,
+					   &fstat_done, &st))
+			goto finish;
+	}
+
 	switch (ce_mode_s_ifmt) {
 	case S_IFREG:
 	case S_IFLNK:
 		new = read_blob_entry(ce, &size);
 		if (!new)
-			return error("git checkout-index: unable to read sha1 file of %s (%s)",
+			return error("unable to read sha1 file of %s (%s)",
 				path, sha1_to_hex(ce->sha1));
 
 		if (ce_mode_s_ifmt == S_IFLNK && has_symlinks && !to_tempfile) {
 			ret = symlink(new, path);
 			free(new);
 			if (ret)
-				return error("git checkout-index: unable to create symlink %s (%s)",
+				return error("unable to create symlink %s (%s)",
 					     path, strerror(errno));
 			break;
 		}
@@ -128,45 +180,32 @@
 			size = newsize;
 		}
 
-		if (to_tempfile) {
-			if (ce_mode_s_ifmt == S_IFREG)
-				strcpy(path, ".merge_file_XXXXXX");
-			else
-				strcpy(path, ".merge_link_XXXXXX");
-			fd = mkstemp(path);
-		} else if (ce_mode_s_ifmt == S_IFREG) {
-			fd = create_file(path, ce->ce_mode);
-		} else {
-			fd = create_file(path, 0666);
-		}
+		fd = open_output_fd(path, ce, to_tempfile);
 		if (fd < 0) {
 			free(new);
-			return error("git checkout-index: unable to create file %s (%s)",
+			return error("unable to create file %s (%s)",
 				path, strerror(errno));
 		}
 
 		wrote = write_in_full(fd, new, size);
-		/* use fstat() only when path == ce->name */
-		if (fstat_is_reliable() &&
-		    state->refresh_cache && !to_tempfile && !state->base_dir_len) {
-			fstat(fd, &st);
-			fstat_done = 1;
-		}
+		if (!to_tempfile)
+			fstat_done = fstat_output(fd, state, &st);
 		close(fd);
 		free(new);
 		if (wrote != size)
-			return error("git checkout-index: unable to write file %s", path);
+			return error("unable to write file %s", path);
 		break;
 	case S_IFGITLINK:
 		if (to_tempfile)
-			return error("git checkout-index: cannot create temporary subproject %s", path);
+			return error("cannot create temporary subproject %s", path);
 		if (mkdir(path, 0777) < 0)
-			return error("git checkout-index: cannot create subproject directory %s", path);
+			return error("cannot create subproject directory %s", path);
 		break;
 	default:
-		return error("git checkout-index: unknown file mode for %s", path);
+		return error("unknown file mode for %s in index", path);
 	}
 
+finish:
 	if (state->refresh_cache) {
 		if (!fstat_done)
 			lstat(ce->name, &st);
@@ -175,6 +214,23 @@
 	return 0;
 }
 
+/*
+ * This is like 'lstat()', except it refuses to follow symlinks
+ * in the path, after skipping "skiplen".
+ */
+static int check_path(const char *path, int len, struct stat *st, int skiplen)
+{
+	const char *slash = path + len;
+
+	while (path < slash && *slash != '/')
+		slash--;
+	if (!has_dirs_only_path(path, slash - path, skiplen)) {
+		errno = ENOENT;
+		return -1;
+	}
+	return lstat(path, st);
+}
+
 int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath)
 {
 	static char path[PATH_MAX + 1];
@@ -188,13 +244,13 @@
 	strcpy(path + len, ce->name);
 	len += ce_namelen(ce);
 
-	if (!lstat(path, &st)) {
-		unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID);
+	if (!check_path(path, len, &st, state->base_dir_len)) {
+		unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);
 		if (!changed)
 			return 0;
 		if (!state->force) {
 			if (!state->quiet)
-				fprintf(stderr, "git-checkout-index: %s already exists\n", path);
+				fprintf(stderr, "%s already exists, no checkout\n", path);
 			return -1;
 		}
 
diff --git a/environment.c b/environment.c
index 8f5eaa7..85edd7f 100644
--- a/environment.c
+++ b/environment.c
@@ -8,13 +8,13 @@
  * are.
  */
 #include "cache.h"
+#include "refs.h"
+#include "fmt-merge-msg.h"
 
-char git_default_email[MAX_GITNAME];
-char git_default_name[MAX_GITNAME];
-int user_ident_explicitly_given;
 int trust_executable_bit = 1;
 int trust_ctime = 1;
 int has_symlinks = 1;
+int minimum_abbrev = 4, default_abbrev = 7;
 int ignore_case;
 int assume_unchanged;
 int prefer_symlink_refs;
@@ -26,6 +26,8 @@
 const char *git_log_output_encoding;
 int shared_repository = PERM_UMASK;
 const char *apply_default_whitespace;
+const char *apply_default_ignorewhitespace;
+const char *git_attributes_file;
 int zlib_compression_level = Z_BEST_SPEED;
 int core_compression_level;
 int core_compression_seen;
@@ -33,21 +35,32 @@
 size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE;
 size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT;
 size_t delta_base_cache_limit = 16 * 1024 * 1024;
+unsigned long big_file_threshold = 512 * 1024 * 1024;
+const char *log_pack_access;
 const char *pager_program;
 int pager_use_color = 1;
 const char *editor_program;
+const char *askpass_program;
 const char *excludes_file;
-int auto_crlf = 0;	/* 1: both ways, -1: only when adding git objects */
+enum auto_crlf auto_crlf = AUTO_CRLF_FALSE;
+int read_replace_refs = 1; /* NEEDSWORK: rename to use_replace_refs */
+enum eol core_eol = EOL_UNSET;
 enum safe_crlf safe_crlf = SAFE_CRLF_WARN;
 unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
 enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
 enum rebase_setup_type autorebase = AUTOREBASE_NEVER;
-enum push_default_type push_default = PUSH_DEFAULT_MATCHING;
+enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;
 #ifndef OBJECT_CREATION_MODE
 #define OBJECT_CREATION_MODE OBJECT_CREATION_USES_HARDLINKS
 #endif
 enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE;
+char *notes_ref_name;
 int grafts_replace_parents = 1;
+int core_apply_sparse_checkout;
+int merge_log_config = -1;
+int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
+struct startup_info *startup_info;
+unsigned long pack_size_limit_cfg;
 
 /* Parallel index stat data preload? */
 int core_preload_index = 0;
@@ -56,14 +69,59 @@
 char *git_work_tree_cfg;
 static char *work_tree;
 
+static const char *namespace;
+static size_t namespace_len;
+
 static const char *git_dir;
-static char *git_object_dir, *git_index_file, *git_refs_dir, *git_graft_file;
+static char *git_object_dir, *git_index_file, *git_graft_file;
+
+/*
+ * Repository-local GIT_* environment variables
+ * Remember to update local_repo_env_size in cache.h when
+ * the size of the list changes
+ */
+const char * const local_repo_env[LOCAL_REPO_ENV_SIZE + 1] = {
+	ALTERNATE_DB_ENVIRONMENT,
+	CONFIG_ENVIRONMENT,
+	CONFIG_DATA_ENVIRONMENT,
+	DB_ENVIRONMENT,
+	GIT_DIR_ENVIRONMENT,
+	GIT_WORK_TREE_ENVIRONMENT,
+	GRAFT_ENVIRONMENT,
+	INDEX_ENVIRONMENT,
+	NO_REPLACE_OBJECTS_ENVIRONMENT,
+	NULL
+};
+
+static char *expand_namespace(const char *raw_namespace)
+{
+	struct strbuf buf = STRBUF_INIT;
+	struct strbuf **components, **c;
+
+	if (!raw_namespace || !*raw_namespace)
+		return xstrdup("");
+
+	strbuf_addstr(&buf, raw_namespace);
+	components = strbuf_split(&buf, '/');
+	strbuf_reset(&buf);
+	for (c = components; *c; c++)
+		if (strcmp((*c)->buf, "/") != 0)
+			strbuf_addf(&buf, "refs/namespaces/%s", (*c)->buf);
+	strbuf_list_free(components);
+	if (check_refname_format(buf.buf, 0))
+		die("bad git namespace path \"%s\"", raw_namespace);
+	strbuf_addch(&buf, '/');
+	return strbuf_detach(&buf, NULL);
+}
 
 static void setup_git_env(void)
 {
 	git_dir = getenv(GIT_DIR_ENVIRONMENT);
-	if (!git_dir)
-		git_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);
+	git_dir = git_dir ? xstrdup(git_dir) : NULL;
+	if (!git_dir) {
+		git_dir = read_gitfile(DEFAULT_GIT_DIR_ENVIRONMENT);
+		git_dir = git_dir ? xstrdup(git_dir) : NULL;
+	}
 	if (!git_dir)
 		git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
 	git_object_dir = getenv(DB_ENVIRONMENT);
@@ -71,8 +129,6 @@
 		git_object_dir = xmalloc(strlen(git_dir) + 9);
 		sprintf(git_object_dir, "%s/objects", git_dir);
 	}
-	git_refs_dir = xmalloc(strlen(git_dir) + 6);
-	sprintf(git_refs_dir, "%s/refs", git_dir);
 	git_index_file = getenv(INDEX_ENVIRONMENT);
 	if (!git_index_file) {
 		git_index_file = xmalloc(strlen(git_dir) + 7);
@@ -81,6 +137,10 @@
 	git_graft_file = getenv(GRAFT_ENVIRONMENT);
 	if (!git_graft_file)
 		git_graft_file = git_pathdup("info/grafts");
+	if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))
+		read_replace_refs = 0;
+	namespace = expand_namespace(getenv(GIT_NAMESPACE_ENVIRONMENT));
+	namespace_len = strlen(namespace);
 }
 
 int is_bare_repository(void)
@@ -101,6 +161,20 @@
 	return git_dir;
 }
 
+const char *get_git_namespace(void)
+{
+	if (!namespace)
+		setup_git_env();
+	return namespace;
+}
+
+const char *strip_namespace(const char *namespaced_ref)
+{
+	if (prefixcmp(namespaced_ref, get_git_namespace()) != 0)
+		return NULL;
+	return namespaced_ref + namespace_len;
+}
+
 static int git_work_tree_initialized;
 
 /*
@@ -110,30 +184,20 @@
  */
 void set_git_work_tree(const char *new_work_tree)
 {
-	if (is_bare_repository_cfg >= 0)
-		die("cannot set work tree after initialization");
+	if (git_work_tree_initialized) {
+		new_work_tree = real_path(new_work_tree);
+		if (strcmp(new_work_tree, work_tree))
+			die("internal error: work tree has already been set\n"
+			    "Current worktree: %s\nNew worktree: %s",
+			    work_tree, new_work_tree);
+		return;
+	}
 	git_work_tree_initialized = 1;
-	free(work_tree);
-	work_tree = xstrdup(make_absolute_path(new_work_tree));
-	is_bare_repository_cfg = 0;
+	work_tree = xstrdup(real_path(new_work_tree));
 }
 
 const char *get_git_work_tree(void)
 {
-	if (!git_work_tree_initialized) {
-		work_tree = getenv(GIT_WORK_TREE_ENVIRONMENT);
-		/* core.bare = true overrides implicit and config work tree */
-		if (!work_tree && is_bare_repository_cfg < 1) {
-			work_tree = git_work_tree_cfg;
-			/* make_absolute_path also normalizes the path */
-			if (work_tree && !is_absolute_path(work_tree))
-				work_tree = xstrdup(make_absolute_path(git_path("%s", work_tree)));
-		} else if (work_tree)
-			work_tree = xstrdup(make_absolute_path(work_tree));
-		git_work_tree_initialized = 1;
-		if (work_tree)
-			is_bare_repository_cfg = 0;
-	}
 	return work_tree;
 }
 
@@ -144,6 +208,43 @@
 	return git_object_dir;
 }
 
+int odb_mkstemp(char *template, size_t limit, const char *pattern)
+{
+	int fd;
+	/*
+	 * we let the umask do its job, don't try to be more
+	 * restrictive except to remove write permission.
+	 */
+	int mode = 0444;
+	snprintf(template, limit, "%s/%s",
+		 get_object_directory(), pattern);
+	fd = git_mkstemp_mode(template, mode);
+	if (0 <= fd)
+		return fd;
+
+	/* slow path */
+	/* some mkstemp implementations erase template on failure */
+	snprintf(template, limit, "%s/%s",
+		 get_object_directory(), pattern);
+	safe_create_leading_directories(template);
+	return xmkstemp_mode(template, mode);
+}
+
+int odb_pack_keep(char *name, size_t namesz, unsigned char *sha1)
+{
+	int fd;
+
+	snprintf(name, namesz, "%s/pack/pack-%s.keep",
+		 get_object_directory(), sha1_to_hex(sha1));
+	fd = open(name, O_RDWR|O_CREAT|O_EXCL, 0600);
+	if (0 <= fd)
+		return fd;
+
+	/* slow path */
+	safe_create_leading_directories(name);
+	return open(name, O_RDWR|O_CREAT|O_EXCL, 0600);
+}
+
 char *get_index_file(void)
 {
 	if (!git_index_file)
@@ -165,3 +266,14 @@
 	setup_git_env();
 	return 0;
 }
+
+const char *get_log_output_encoding(void)
+{
+	return git_log_output_encoding ? git_log_output_encoding
+		: get_commit_output_encoding();
+}
+
+const char *get_commit_output_encoding(void)
+{
+	return git_commit_encoding ? git_commit_encoding : "UTF-8";
+}
diff --git a/exec_cmd.c b/exec_cmd.c
index 408e4e5..125fa6f 100644
--- a/exec_cmd.c
+++ b/exec_cmd.c
@@ -3,7 +3,6 @@
 #include "quote.h"
 #define MAX_ARGS	32
 
-extern char **environ;
 static const char *argv_exec_path;
 static const char *argv0_path;
 
@@ -28,7 +27,7 @@
 	    !(prefix = strip_path_suffix(argv0_path, BINDIR)) &&
 	    !(prefix = strip_path_suffix(argv0_path, "git"))) {
 		prefix = PREFIX;
-		fprintf(stderr, "RUNTIME_PREFIX requested, "
+		trace_printf("RUNTIME_PREFIX requested, "
 				"but prefix computation failed.  "
 				"Using static fallback '%s'.\n", prefix);
 	}
@@ -90,7 +89,7 @@
 		if (is_absolute_path(path))
 			strbuf_addstr(out, path);
 		else
-			strbuf_addstr(out, make_nonrelative_path(path));
+			strbuf_addstr(out, absolute_path(path));
 
 		strbuf_addch(out, PATH_SEP);
 	}
@@ -107,7 +106,7 @@
 	if (old_path)
 		strbuf_addstr(&new_path, old_path);
 	else
-		strbuf_addstr(&new_path, "/usr/local/bin:/usr/bin:/bin");
+		strbuf_addstr(&new_path, _PATH_DEFPATH);
 
 	setenv("PATH", new_path.buf, 1);
 
@@ -135,7 +134,7 @@
 	trace_argv_printf(nargv, "trace: exec:");
 
 	/* execvp() can only ever return if it fails */
-	execvp("git", (char **)nargv);
+	sane_execvp("git", (char **)nargv);
 
 	trace_printf("trace: exec failed: %s\n", strerror(errno));
 
diff --git a/fast-import.c b/fast-import.c
index 7ef9865..eed97c8 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -19,15 +19,17 @@
 
   new_commit ::= 'commit' sp ref_str lf
     mark?
-    ('author' sp name sp '<' email '>' sp when lf)?
-    'committer' sp name sp '<' email '>' sp when lf
+    ('author' (sp name)? sp '<' email '>' sp when lf)?
+    'committer' (sp name)? sp '<' email '>' sp when lf
     commit_msg
-    ('from' sp (ref_str | hexsha1 | sha1exp_str | idnum) lf)?
-    ('merge' sp (ref_str | hexsha1 | sha1exp_str | idnum) lf)*
-    file_change*
+    ('from' sp committish lf)?
+    ('merge' sp committish lf)*
+    (file_change | ls)*
     lf?;
   commit_msg ::= data;
 
+  ls ::= 'ls' sp '"' quoted(path) '"' lf;
+
   file_change ::= file_clr
     | file_del
     | file_rnm
@@ -41,15 +43,18 @@
   file_obm ::= 'M' sp mode sp (hexsha1 | idnum) sp path_str lf;
   file_inm ::= 'M' sp mode sp 'inline' sp path_str lf
     data;
+  note_obm ::= 'N' sp (hexsha1 | idnum) sp committish lf;
+  note_inm ::= 'N' sp 'inline' sp committish lf
+    data;
 
   new_tag ::= 'tag' sp tag_str lf
-    'from' sp (ref_str | hexsha1 | sha1exp_str | idnum) lf
-    ('tagger' sp name sp '<' email '>' sp when lf)?
+    'from' sp committish lf
+    ('tagger' (sp name)? sp '<' email '>' sp when lf)?
     tag_msg;
   tag_msg ::= data;
 
   reset_branch ::= 'reset' sp ref_str lf
-    ('from' sp (ref_str | hexsha1 | sha1exp_str | idnum) lf)?
+    ('from' sp committish lf)?
     lf?;
 
   checkpoint ::= 'checkpoint' lf
@@ -88,6 +93,7 @@
      # stream formatting is: \, " and LF.  Otherwise these values
      # are UTF8.
      #
+  committish  ::= (ref_str | hexsha1 | sha1exp_str | idnum);
   ref_str     ::= ref;
   sha1exp_str ::= sha1exp;
   tag_str     ::= tag;
@@ -128,14 +134,19 @@
   ts    ::= # time since the epoch in seconds, ascii base10 notation;
   tz    ::= # GIT style timezone;
 
-     # note: comments may appear anywhere in the input, except
-     # within a data command.  Any form of the data command
-     # always escapes the related input from comment processing.
+     # note: comments, ls and cat requests may appear anywhere
+     # in the input, except within a data command.  Any form
+     # of the data command always escapes the related input
+     # from comment processing.
      #
      # In case it is not clear, the '#' that starts the comment
      # must be the first character on that line (an lf
      # preceded it).
      #
+
+  cat_blob ::= 'cat-blob' sp (hexsha1 | idnum) lf;
+  ls_tree  ::= 'ls' sp (hexsha1 | idnum) sp path_str lf;
+
   comment ::= '#' not_lf* lf;
   not_lf  ::= # Any byte that is not ASCII newline (LF);
 */
@@ -152,32 +163,34 @@
 #include "csum-file.h"
 #include "quote.h"
 #include "exec_cmd.h"
+#include "dir.h"
 
 #define PACK_ID_BITS 16
 #define MAX_PACK_ID ((1<<PACK_ID_BITS)-1)
 #define DEPTH_BITS 13
 #define MAX_DEPTH ((1<<DEPTH_BITS)-1)
 
-struct object_entry
-{
+/*
+ * We abuse the setuid bit on directories to mean "do not delta".
+ */
+#define NO_DELTA S_ISUID
+
+struct object_entry {
+	struct pack_idx_entry idx;
 	struct object_entry *next;
-	uint32_t offset;
 	uint32_t type : TYPE_BITS,
 		pack_id : PACK_ID_BITS,
 		depth : DEPTH_BITS;
-	unsigned char sha1[20];
 };
 
-struct object_entry_pool
-{
+struct object_entry_pool {
 	struct object_entry_pool *next_pool;
 	struct object_entry *next_free;
 	struct object_entry *end;
 	struct object_entry entries[FLEX_ARRAY]; /* more */
 };
 
-struct mark_set
-{
+struct mark_set {
 	union {
 		struct object_entry *marked[1024];
 		struct mark_set *sets[1024];
@@ -185,77 +198,68 @@
 	unsigned int shift;
 };
 
-struct last_object
-{
+struct last_object {
 	struct strbuf data;
-	uint32_t offset;
+	off_t offset;
 	unsigned int depth;
 	unsigned no_swap : 1;
 };
 
-struct mem_pool
-{
+struct mem_pool {
 	struct mem_pool *next_pool;
 	char *next_free;
 	char *end;
 	uintmax_t space[FLEX_ARRAY]; /* more */
 };
 
-struct atom_str
-{
+struct atom_str {
 	struct atom_str *next_atom;
 	unsigned short str_len;
 	char str_dat[FLEX_ARRAY]; /* more */
 };
 
 struct tree_content;
-struct tree_entry
-{
+struct tree_entry {
 	struct tree_content *tree;
 	struct atom_str *name;
-	struct tree_entry_ms
-	{
+	struct tree_entry_ms {
 		uint16_t mode;
 		unsigned char sha1[20];
 	} versions[2];
 };
 
-struct tree_content
-{
+struct tree_content {
 	unsigned int entry_capacity; /* must match avail_tree_content */
 	unsigned int entry_count;
 	unsigned int delta_depth;
 	struct tree_entry *entries[FLEX_ARRAY]; /* more */
 };
 
-struct avail_tree_content
-{
+struct avail_tree_content {
 	unsigned int entry_capacity; /* must match tree_content */
 	struct avail_tree_content *next_avail;
 };
 
-struct branch
-{
+struct branch {
 	struct branch *table_next_branch;
 	struct branch *active_next_branch;
 	const char *name;
 	struct tree_entry branch_tree;
 	uintmax_t last_commit;
+	uintmax_t num_notes;
 	unsigned active : 1;
 	unsigned pack_id : PACK_ID_BITS;
 	unsigned char sha1[20];
 };
 
-struct tag
-{
+struct tag {
 	struct tag *next_tag;
 	const char *name;
 	unsigned int pack_id;
 	unsigned char sha1[20];
 };
 
-struct hash_list
-{
+struct hash_list {
 	struct hash_list *next;
 	unsigned char sha1[20];
 };
@@ -263,11 +267,10 @@
 typedef enum {
 	WHENSPEC_RAW = 1,
 	WHENSPEC_RFC2822,
-	WHENSPEC_NOW,
+	WHENSPEC_NOW
 } whenspec_type;
 
-struct recent_command
-{
+struct recent_command {
 	struct recent_command *prev;
 	struct recent_command *next;
 	char *buf;
@@ -275,7 +278,7 @@
 
 /* Configured limits on output */
 static unsigned long max_depth = 10;
-static off_t max_packsize = (1LL << 32) - 1;
+static off_t max_packsize;
 static int force_update;
 static int pack_compression_level = Z_DEFAULT_COMPRESSION;
 static int pack_compression_seen;
@@ -286,11 +289,15 @@
 static uintmax_t object_count_by_type[1 << TYPE_BITS];
 static uintmax_t duplicate_count_by_type[1 << TYPE_BITS];
 static uintmax_t delta_count_by_type[1 << TYPE_BITS];
+static uintmax_t delta_count_attempts_by_type[1 << TYPE_BITS];
 static unsigned long object_count;
 static unsigned long branch_count;
 static unsigned long branch_load_count;
 static int failure;
 static FILE *pack_edges;
+static unsigned int show_stats = 1;
+static int global_argc;
+static const char **global_argv;
 
 /* Memory pools */
 static size_t mem_pool_alloc = 2*1024*1024 - sizeof(struct mem_pool);
@@ -303,17 +310,23 @@
 static struct atom_str **atom_table;
 
 /* The .pack file being generated */
+static struct pack_idx_option pack_idx_opts;
 static unsigned int pack_id;
+static struct sha1file *pack_file;
 static struct packed_git *pack_data;
 static struct packed_git **all_packs;
-static unsigned long pack_size;
+static off_t pack_size;
 
 /* Table of objects we've written. */
 static unsigned int object_entry_alloc = 5000;
 static struct object_entry_pool *blocks;
 static struct object_entry *object_table[1 << 16];
 static struct mark_set *marks;
-static const char *mark_file;
+static const char *export_marks_file;
+static const char *import_marks_file;
+static int import_marks_file_from_stream;
+static int import_marks_file_ignore_missing;
+static int relative_marks_paths;
 
 /* Our last blob */
 static struct last_object last_blob = { STRBUF_INIT, 0, 0, 0 };
@@ -347,6 +360,18 @@
 static unsigned int cmd_save = 100;
 static uintmax_t next_mark;
 static struct strbuf new_data = STRBUF_INIT;
+static int seen_data_command;
+static int require_explicit_termination;
+
+/* Signal handling */
+static volatile sig_atomic_t checkpoint_requested;
+
+/* Where to write output of cat-blob commands */
+static int cat_blob_fd = STDOUT_FILENO;
+
+static void parse_argv(void);
+static void parse_cat_blob(void);
+static void parse_ls(struct branch *b);
 
 static void write_branch_report(FILE *rpt, struct branch *b)
 {
@@ -450,8 +475,8 @@
 	fputc('\n', rpt);
 	fputs("Marks\n", rpt);
 	fputs("-----\n", rpt);
-	if (mark_file)
-		fprintf(rpt, "  exported to %s\n", mark_file);
+	if (export_marks_file)
+		fprintf(rpt, "  exported to %s\n", export_marks_file);
 	else
 		dump_marks_helper(rpt, 0, marks);
 
@@ -485,6 +510,32 @@
 	exit(128);
 }
 
+#ifndef SIGUSR1	/* Windows, for example */
+
+static void set_checkpoint_signal(void)
+{
+}
+
+#else
+
+static void checkpoint_signal(int signo)
+{
+	checkpoint_requested = 1;
+}
+
+static void set_checkpoint_signal(void)
+{
+	struct sigaction sa;
+
+	memset(&sa, 0, sizeof(sa));
+	sa.sa_handler = checkpoint_signal;
+	sigemptyset(&sa.sa_mask);
+	sa.sa_flags = SA_RESTART;
+	sigaction(SIGUSR1, &sa, NULL);
+}
+
+#endif
+
 static void alloc_objects(unsigned int cnt)
 {
 	struct object_entry_pool *b;
@@ -506,7 +557,7 @@
 		alloc_objects(object_entry_alloc);
 
 	e = blocks->next_free++;
-	hashcpy(e->sha1, sha1);
+	hashcpy(e->idx.sha1, sha1);
 	return e;
 }
 
@@ -515,7 +566,7 @@
 	unsigned int h = sha1[0] << 8 | sha1[1];
 	struct object_entry *e;
 	for (e = object_table[h]; e; e = e->next)
-		if (!hashcmp(sha1, e->sha1))
+		if (!hashcmp(sha1, e->idx.sha1))
 			return e;
 	return NULL;
 }
@@ -524,22 +575,17 @@
 {
 	unsigned int h = sha1[0] << 8 | sha1[1];
 	struct object_entry *e = object_table[h];
-	struct object_entry *p = NULL;
 
 	while (e) {
-		if (!hashcmp(sha1, e->sha1))
+		if (!hashcmp(sha1, e->idx.sha1))
 			return e;
-		p = e;
 		e = e->next;
 	}
 
 	e = new_object(sha1);
-	e->next = NULL;
-	e->offset = 0;
-	if (p)
-		p->next = e;
-	else
-		object_table[h] = e;
+	e->next = object_table[h];
+	e->idx.offset = 0;
+	object_table[h] = e;
 	return e;
 }
 
@@ -676,19 +722,15 @@
 
 	if (b)
 		die("Invalid attempt to create duplicate branch: %s", name);
-	switch (check_ref_format(name)) {
-	case 0: break; /* its valid */
-	case CHECK_REF_FORMAT_ONELEVEL:
-		break; /* valid, but too few '/', allow anyway */
-	default:
+	if (check_refname_format(name, REFNAME_ALLOW_ONELEVEL))
 		die("Branch name doesn't conform to GIT standards: %s", name);
-	}
 
 	b = pool_calloc(1, sizeof(struct branch));
 	b->name = pool_strdup(name);
 	b->table_next_branch = branch_table[hc];
 	b->branch_tree.versions[0].mode = S_IFDIR;
 	b->branch_tree.versions[1].mode = S_IFDIR;
+	b->num_notes = 0;
 	b->active = 0;
 	b->pack_id = MAX_PACK_ID;
 	branch_table[hc] = b;
@@ -813,21 +855,23 @@
 
 static void start_packfile(void)
 {
-	static char tmpfile[PATH_MAX];
+	static char tmp_file[PATH_MAX];
 	struct packed_git *p;
 	struct pack_header hdr;
 	int pack_fd;
 
-	pack_fd = odb_mkstemp(tmpfile, sizeof(tmpfile),
+	pack_fd = odb_mkstemp(tmp_file, sizeof(tmp_file),
 			      "pack/tmp_pack_XXXXXX");
-	p = xcalloc(1, sizeof(*p) + strlen(tmpfile) + 2);
-	strcpy(p->pack_name, tmpfile);
+	p = xcalloc(1, sizeof(*p) + strlen(tmp_file) + 2);
+	strcpy(p->pack_name, tmp_file);
 	p->pack_fd = pack_fd;
+	p->do_not_close = 1;
+	pack_file = sha1fd(pack_fd, p->pack_name);
 
 	hdr.hdr_signature = htonl(PACK_SIGNATURE);
 	hdr.hdr_version = htonl(2);
 	hdr.hdr_entries = 0;
-	write_or_die(p->pack_fd, &hdr, sizeof(hdr));
+	sha1write(pack_file, &hdr, sizeof(hdr));
 
 	pack_data = p;
 	pack_size = sizeof(hdr);
@@ -837,67 +881,30 @@
 	all_packs[pack_id] = p;
 }
 
-static int oecmp (const void *a_, const void *b_)
+static const char *create_index(void)
 {
-	struct object_entry *a = *((struct object_entry**)a_);
-	struct object_entry *b = *((struct object_entry**)b_);
-	return hashcmp(a->sha1, b->sha1);
-}
-
-static char *create_index(void)
-{
-	static char tmpfile[PATH_MAX];
-	git_SHA_CTX ctx;
-	struct sha1file *f;
-	struct object_entry **idx, **c, **last, *e;
+	const char *tmpfile;
+	struct pack_idx_entry **idx, **c, **last;
+	struct object_entry *e;
 	struct object_entry_pool *o;
-	uint32_t array[256];
-	int i, idx_fd;
 
-	/* Build the sorted table of object IDs. */
-	idx = xmalloc(object_count * sizeof(struct object_entry*));
+	/* Build the table of object IDs. */
+	idx = xmalloc(object_count * sizeof(*idx));
 	c = idx;
 	for (o = blocks; o; o = o->next_pool)
 		for (e = o->next_free; e-- != o->entries;)
 			if (pack_id == e->pack_id)
-				*c++ = e;
+				*c++ = &e->idx;
 	last = idx + object_count;
 	if (c != last)
 		die("internal consistency error creating the index");
-	qsort(idx, object_count, sizeof(struct object_entry*), oecmp);
 
-	/* Generate the fan-out array. */
-	c = idx;
-	for (i = 0; i < 256; i++) {
-		struct object_entry **next = c;
-		while (next < last) {
-			if ((*next)->sha1[0] != i)
-				break;
-			next++;
-		}
-		array[i] = htonl(next - idx);
-		c = next;
-	}
-
-	idx_fd = odb_mkstemp(tmpfile, sizeof(tmpfile),
-			     "pack/tmp_idx_XXXXXX");
-	f = sha1fd(idx_fd, tmpfile);
-	sha1write(f, array, 256 * sizeof(int));
-	git_SHA1_Init(&ctx);
-	for (c = idx; c != last; c++) {
-		uint32_t offset = htonl((*c)->offset);
-		sha1write(f, &offset, 4);
-		sha1write(f, (*c)->sha1, sizeof((*c)->sha1));
-		git_SHA1_Update(&ctx, (*c)->sha1, 20);
-	}
-	sha1write(f, pack_data->sha1, sizeof(pack_data->sha1));
-	sha1close(f, NULL, CSUM_FSYNC);
+	tmpfile = write_idx_file(NULL, idx, object_count, &pack_idx_opts, pack_data->sha1);
 	free(idx);
-	git_SHA1_Final(pack_data->sha1, &ctx);
 	return tmpfile;
 }
 
-static char *keep_pack(char *curr_index_name)
+static char *keep_pack(const char *curr_index_name)
 {
 	static char name[PATH_MAX];
 	static const char *keep_msg = "fast-import";
@@ -919,6 +926,7 @@
 		 get_object_directory(), sha1_to_hex(pack_data->sha1));
 	if (move_temp_to_file(curr_index_name, name))
 		die("cannot store index file");
+	free((void *)curr_index_name);
 	return name;
 }
 
@@ -941,15 +949,17 @@
 
 	clear_delta_base_cache();
 	if (object_count) {
+		unsigned char cur_pack_sha1[20];
 		char *idx_name;
 		int i;
 		struct branch *b;
 		struct tag *t;
 
 		close_pack_windows(pack_data);
+		sha1close(pack_file, cur_pack_sha1, 0);
 		fixup_pack_header_footer(pack_data->pack_fd, pack_data->sha1,
 				    pack_data->pack_name, object_count,
-				    NULL, 0);
+				    cur_pack_sha1, pack_size);
 		close(pack_data->pack_fd);
 		idx_name = keep_pack(create_index());
 
@@ -997,29 +1007,6 @@
 	start_packfile();
 }
 
-static size_t encode_header(
-	enum object_type type,
-	size_t size,
-	unsigned char *hdr)
-{
-	int n = 1;
-	unsigned char c;
-
-	if (type < OBJ_COMMIT || type > OBJ_REF_DELTA)
-		die("bad type %d", type);
-
-	c = (type << 4) | (size & 15);
-	size >>= 4;
-	while (size) {
-		*hdr++ = c | 0x80;
-		c = size & 0x7f;
-		size >>= 7;
-		n++;
-	}
-	*hdr = c;
-	return n;
-}
-
 static int store_object(
 	enum object_type type,
 	struct strbuf *dat,
@@ -1033,7 +1020,7 @@
 	unsigned char sha1[20];
 	unsigned long hdrlen, deltalen;
 	git_SHA_CTX c;
-	z_stream s;
+	git_zstream s;
 
 	hdrlen = sprintf((char *)hdr,"%s %lu", typename(type),
 		(unsigned long)dat->len) + 1;
@@ -1047,30 +1034,27 @@
 	e = insert_object(sha1);
 	if (mark)
 		insert_mark(mark, e);
-	if (e->offset) {
+	if (e->idx.offset) {
 		duplicate_count_by_type[type]++;
 		return 1;
 	} else if (find_sha1_pack(sha1, packed_git)) {
 		e->type = type;
 		e->pack_id = MAX_PACK_ID;
-		e->offset = 1; /* just not zero! */
+		e->idx.offset = 1; /* just not zero! */
 		duplicate_count_by_type[type]++;
 		return 1;
 	}
 
-	if (last && last->data.buf && last->depth < max_depth) {
+	if (last && last->data.buf && last->depth < max_depth && dat->len > 20) {
+		delta_count_attempts_by_type[type]++;
 		delta = diff_delta(last->data.buf, last->data.len,
 			dat->buf, dat->len,
-			&deltalen, 0);
-		if (delta && deltalen >= dat->len) {
-			free(delta);
-			delta = NULL;
-		}
+			&deltalen, dat->len - 20);
 	} else
 		delta = NULL;
 
 	memset(&s, 0, sizeof(s));
-	deflateInit(&s, pack_compression_level);
+	git_deflate_init(&s, pack_compression_level);
 	if (delta) {
 		s.next_in = delta;
 		s.avail_in = deltalen;
@@ -1078,14 +1062,14 @@
 		s.next_in = (void *)dat->buf;
 		s.avail_in = dat->len;
 	}
-	s.avail_out = deflateBound(&s, s.avail_in);
+	s.avail_out = git_deflate_bound(&s, s.avail_in);
 	s.next_out = out = xmalloc(s.avail_out);
-	while (deflate(&s, Z_FINISH) == Z_OK)
-		/* nothing */;
-	deflateEnd(&s);
+	while (git_deflate(&s, Z_FINISH) == Z_OK)
+		; /* nothing */
+	git_deflate_end(&s);
 
 	/* Determine if we should auto-checkpoint. */
-	if ((pack_size + 60 + s.total_out) > max_packsize
+	if ((max_packsize && (pack_size + 60 + s.total_out) > max_packsize)
 		|| (pack_size + 60 + s.total_out) < pack_size) {
 
 		/* This new object needs to *not* have the current pack_id. */
@@ -1098,49 +1082,53 @@
 			delta = NULL;
 
 			memset(&s, 0, sizeof(s));
-			deflateInit(&s, pack_compression_level);
+			git_deflate_init(&s, pack_compression_level);
 			s.next_in = (void *)dat->buf;
 			s.avail_in = dat->len;
-			s.avail_out = deflateBound(&s, s.avail_in);
+			s.avail_out = git_deflate_bound(&s, s.avail_in);
 			s.next_out = out = xrealloc(out, s.avail_out);
-			while (deflate(&s, Z_FINISH) == Z_OK)
-				/* nothing */;
-			deflateEnd(&s);
+			while (git_deflate(&s, Z_FINISH) == Z_OK)
+				; /* nothing */
+			git_deflate_end(&s);
 		}
 	}
 
 	e->type = type;
 	e->pack_id = pack_id;
-	e->offset = pack_size;
+	e->idx.offset = pack_size;
 	object_count++;
 	object_count_by_type[type]++;
 
+	crc32_begin(pack_file);
+
 	if (delta) {
-		unsigned long ofs = e->offset - last->offset;
+		off_t ofs = e->idx.offset - last->offset;
 		unsigned pos = sizeof(hdr) - 1;
 
 		delta_count_by_type[type]++;
 		e->depth = last->depth + 1;
 
-		hdrlen = encode_header(OBJ_OFS_DELTA, deltalen, hdr);
-		write_or_die(pack_data->pack_fd, hdr, hdrlen);
+		hdrlen = encode_in_pack_object_header(OBJ_OFS_DELTA, deltalen, hdr);
+		sha1write(pack_file, hdr, hdrlen);
 		pack_size += hdrlen;
 
 		hdr[pos] = ofs & 127;
 		while (ofs >>= 7)
 			hdr[--pos] = 128 | (--ofs & 127);
-		write_or_die(pack_data->pack_fd, hdr + pos, sizeof(hdr) - pos);
+		sha1write(pack_file, hdr + pos, sizeof(hdr) - pos);
 		pack_size += sizeof(hdr) - pos;
 	} else {
 		e->depth = 0;
-		hdrlen = encode_header(type, dat->len, hdr);
-		write_or_die(pack_data->pack_fd, hdr, hdrlen);
+		hdrlen = encode_in_pack_object_header(type, dat->len, hdr);
+		sha1write(pack_file, hdr, hdrlen);
 		pack_size += hdrlen;
 	}
 
-	write_or_die(pack_data->pack_fd, out, s.total_out);
+	sha1write(pack_file, out, s.total_out);
 	pack_size += s.total_out;
 
+	e->idx.crc32 = crc32_end(pack_file);
+
 	free(out);
 	free(delta);
 	if (last) {
@@ -1149,12 +1137,128 @@
 		} else {
 			strbuf_swap(&last->data, dat);
 		}
-		last->offset = e->offset;
+		last->offset = e->idx.offset;
 		last->depth = e->depth;
 	}
 	return 0;
 }
 
+static void truncate_pack(struct sha1file_checkpoint *checkpoint)
+{
+	if (sha1file_truncate(pack_file, checkpoint))
+		die_errno("cannot truncate pack to skip duplicate");
+	pack_size = checkpoint->offset;
+}
+
+static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark)
+{
+	size_t in_sz = 64 * 1024, out_sz = 64 * 1024;
+	unsigned char *in_buf = xmalloc(in_sz);
+	unsigned char *out_buf = xmalloc(out_sz);
+	struct object_entry *e;
+	unsigned char sha1[20];
+	unsigned long hdrlen;
+	off_t offset;
+	git_SHA_CTX c;
+	git_zstream s;
+	struct sha1file_checkpoint checkpoint;
+	int status = Z_OK;
+
+	/* Determine if we should auto-checkpoint. */
+	if ((max_packsize && (pack_size + 60 + len) > max_packsize)
+		|| (pack_size + 60 + len) < pack_size)
+		cycle_packfile();
+
+	sha1file_checkpoint(pack_file, &checkpoint);
+	offset = checkpoint.offset;
+
+	hdrlen = snprintf((char *)out_buf, out_sz, "blob %" PRIuMAX, len) + 1;
+	if (out_sz <= hdrlen)
+		die("impossibly large object header");
+
+	git_SHA1_Init(&c);
+	git_SHA1_Update(&c, out_buf, hdrlen);
+
+	crc32_begin(pack_file);
+
+	memset(&s, 0, sizeof(s));
+	git_deflate_init(&s, pack_compression_level);
+
+	hdrlen = encode_in_pack_object_header(OBJ_BLOB, len, out_buf);
+	if (out_sz <= hdrlen)
+		die("impossibly large object header");
+
+	s.next_out = out_buf + hdrlen;
+	s.avail_out = out_sz - hdrlen;
+
+	while (status != Z_STREAM_END) {
+		if (0 < len && !s.avail_in) {
+			size_t cnt = in_sz < len ? in_sz : (size_t)len;
+			size_t n = fread(in_buf, 1, cnt, stdin);
+			if (!n && feof(stdin))
+				die("EOF in data (%" PRIuMAX " bytes remaining)", len);
+
+			git_SHA1_Update(&c, in_buf, n);
+			s.next_in = in_buf;
+			s.avail_in = n;
+			len -= n;
+		}
+
+		status = git_deflate(&s, len ? 0 : Z_FINISH);
+
+		if (!s.avail_out || status == Z_STREAM_END) {
+			size_t n = s.next_out - out_buf;
+			sha1write(pack_file, out_buf, n);
+			pack_size += n;
+			s.next_out = out_buf;
+			s.avail_out = out_sz;
+		}
+
+		switch (status) {
+		case Z_OK:
+		case Z_BUF_ERROR:
+		case Z_STREAM_END:
+			continue;
+		default:
+			die("unexpected deflate failure: %d", status);
+		}
+	}
+	git_deflate_end(&s);
+	git_SHA1_Final(sha1, &c);
+
+	if (sha1out)
+		hashcpy(sha1out, sha1);
+
+	e = insert_object(sha1);
+
+	if (mark)
+		insert_mark(mark, e);
+
+	if (e->idx.offset) {
+		duplicate_count_by_type[OBJ_BLOB]++;
+		truncate_pack(&checkpoint);
+
+	} else if (find_sha1_pack(sha1, packed_git)) {
+		e->type = OBJ_BLOB;
+		e->pack_id = MAX_PACK_ID;
+		e->idx.offset = 1; /* just not zero! */
+		duplicate_count_by_type[OBJ_BLOB]++;
+		truncate_pack(&checkpoint);
+
+	} else {
+		e->depth = 0;
+		e->type = OBJ_BLOB;
+		e->pack_id = pack_id;
+		e->idx.offset = offset;
+		e->idx.crc32 = crc32_end(pack_file);
+		object_count++;
+		object_count_by_type[OBJ_BLOB]++;
+	}
+
+	free(in_buf);
+	free(out_buf);
+}
+
 /* All calls must be guarded by find_object() or find_mark() to
  * ensure the 'struct object_entry' passed was written by this
  * process instance.  We unpack the entry by the offset, avoiding
@@ -1189,6 +1293,7 @@
 		 * the newly written data.
 		 */
 		close_pack_windows(p);
+		sha1flush(pack_file);
 
 		/* We have to offer 20 bytes additional on the end of
 		 * the packfile as the core unpacker code assumes the
@@ -1198,7 +1303,7 @@
 		 */
 		p->pack_size = pack_size + 20;
 	}
-	return unpack_entry(p, oe->offset, &type, sizep);
+	return unpack_entry(p, oe->idx.offset, &type, sizep);
 }
 
 static const char *get_mode(const char *str, uint16_t *modep)
@@ -1304,8 +1409,9 @@
 		struct tree_entry *e = t->entries[i];
 		if (!e->versions[v].mode)
 			continue;
-		strbuf_addf(b, "%o %s%c", (unsigned int)e->versions[v].mode,
-					e->name->str_dat, '\0');
+		strbuf_addf(b, "%o %s%c",
+			(unsigned int)(e->versions[v].mode & ~NO_DELTA),
+			e->name->str_dat, '\0');
 		strbuf_add(b, e->versions[v].sha1, 20);
 	}
 }
@@ -1315,7 +1421,7 @@
 	struct tree_content *t = root->tree;
 	unsigned int i, j, del;
 	struct last_object lo = { STRBUF_INIT, 0, 0, /* no_swap */ 1 };
-	struct object_entry *le;
+	struct object_entry *le = NULL;
 
 	if (!is_null_sha1(root->versions[1].sha1))
 		return;
@@ -1325,11 +1431,12 @@
 			store_tree(t->entries[i]);
 	}
 
-	le = find_object(root->versions[0].sha1);
+	if (!(root->versions[0].mode & NO_DELTA))
+		le = find_object(root->versions[0].sha1);
 	if (S_ISDIR(root->versions[0].mode) && le && le->pack_id == pack_id) {
 		mktree(t, 0, &old_tree);
 		lo.data = old_tree;
-		lo.offset = le->offset;
+		lo.offset = le->idx.offset;
 		lo.depth = t->delta_depth;
 	}
 
@@ -1351,6 +1458,21 @@
 	t->entry_count -= del;
 }
 
+static void tree_content_replace(
+	struct tree_entry *root,
+	const unsigned char *sha1,
+	const uint16_t mode,
+	struct tree_content *newtree)
+{
+	if (!S_ISDIR(mode))
+		die("Root cannot be a non-directory");
+	hashclr(root->versions[0].sha1);
+	hashcpy(root->versions[1].sha1, sha1);
+	if (root->tree)
+		release_tree_content_recursive(root->tree);
+	root->tree = newtree;
+}
+
 static int tree_content_set(
 	struct tree_entry *root,
 	const char *p,
@@ -1358,7 +1480,7 @@
 	const uint16_t mode,
 	struct tree_content *subtree)
 {
-	struct tree_content *t = root->tree;
+	struct tree_content *t;
 	const char *slash1;
 	unsigned int i, n;
 	struct tree_entry *e;
@@ -1373,9 +1495,12 @@
 	if (!slash1 && !S_ISDIR(mode) && subtree)
 		die("Non-directories cannot have subtrees");
 
+	if (!root->tree)
+		load_tree(root);
+	t = root->tree;
 	for (i = 0; i < t->entry_count; i++) {
 		e = t->entries[i];
-		if (e->name->str_len == n && !strncmp(p, e->name->str_dat, n)) {
+		if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
 			if (!slash1) {
 				if (!S_ISDIR(mode)
 						&& e->versions[1].mode == mode
@@ -1386,6 +1511,23 @@
 				if (e->tree)
 					release_tree_content_recursive(e->tree);
 				e->tree = subtree;
+
+				/*
+				 * We need to leave e->versions[0].sha1 alone
+				 * to avoid modifying the preimage tree used
+				 * when writing out the parent directory.
+				 * But after replacing the subdir with a
+				 * completely different one, it's not a good
+				 * delta base any more, and besides, we've
+				 * thrown away the tree entries needed to
+				 * make a delta against it.
+				 *
+				 * So let's just explicitly disable deltas
+				 * for the subtree.
+				 */
+				if (S_ISDIR(e->versions[0].mode))
+					e->versions[0].mode |= NO_DELTA;
+
 				hashclr(root->versions[1].sha1);
 				return 1;
 			}
@@ -1428,7 +1570,7 @@
 	const char *p,
 	struct tree_entry *backup_leaf)
 {
-	struct tree_content *t = root->tree;
+	struct tree_content *t;
 	const char *slash1;
 	unsigned int i, n;
 	struct tree_entry *e;
@@ -1439,9 +1581,20 @@
 	else
 		n = strlen(p);
 
+	if (!root->tree)
+		load_tree(root);
+	t = root->tree;
 	for (i = 0; i < t->entry_count; i++) {
 		e = t->entries[i];
-		if (e->name->str_len == n && !strncmp(p, e->name->str_dat, n)) {
+		if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
+			if (slash1 && !S_ISDIR(e->versions[1].mode))
+				/*
+				 * If p names a file in some subdirectory, and a
+				 * file or symlink matching the name of the
+				 * parent directory of p exists, then p cannot
+				 * exist and need not be deleted.
+				 */
+				return 1;
 			if (!slash1 || !S_ISDIR(e->versions[1].mode))
 				goto del_entry;
 			if (!e->tree)
@@ -1478,7 +1631,7 @@
 	const char *p,
 	struct tree_entry *leaf)
 {
-	struct tree_content *t = root->tree;
+	struct tree_content *t;
 	const char *slash1;
 	unsigned int i, n;
 	struct tree_entry *e;
@@ -1488,10 +1641,15 @@
 		n = slash1 - p;
 	else
 		n = strlen(p);
+	if (!n)
+		die("Empty path component found in input");
 
+	if (!root->tree)
+		load_tree(root);
+	t = root->tree;
 	for (i = 0; i < t->entry_count; i++) {
 		e = t->entries[i];
-		if (e->name->str_len == n && !strncmp(p, e->name->str_dat, n)) {
+		if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
 			if (!slash1) {
 				memcpy(leaf, e, sizeof(*leaf));
 				if (e->tree && is_null_sha1(e->versions[1].sha1))
@@ -1580,14 +1738,14 @@
 	if (m->shift) {
 		for (k = 0; k < 1024; k++) {
 			if (m->data.sets[k])
-				dump_marks_helper(f, (base + k) << m->shift,
+				dump_marks_helper(f, base + (k << m->shift),
 					m->data.sets[k]);
 		}
 	} else {
 		for (k = 0; k < 1024; k++) {
 			if (m->data.marked[k])
 				fprintf(f, ":%" PRIuMAX " %s\n", base + k,
-					sha1_to_hex(m->data.marked[k]->sha1));
+					sha1_to_hex(m->data.marked[k]->idx.sha1));
 		}
 	}
 }
@@ -1598,13 +1756,13 @@
 	int mark_fd;
 	FILE *f;
 
-	if (!mark_file)
+	if (!export_marks_file)
 		return;
 
-	mark_fd = hold_lock_file_for_update(&mark_lock, mark_file, 0);
+	mark_fd = hold_lock_file_for_update(&mark_lock, export_marks_file, 0);
 	if (mark_fd < 0) {
 		failure |= error("Unable to write marks file %s: %s",
-			mark_file, strerror(errno));
+			export_marks_file, strerror(errno));
 		return;
 	}
 
@@ -1613,7 +1771,7 @@
 		int saved_errno = errno;
 		rollback_lock_file(&mark_lock);
 		failure |= error("Unable to write marks file %s: %s",
-			mark_file, strerror(saved_errno));
+			export_marks_file, strerror(saved_errno));
 		return;
 	}
 
@@ -1629,7 +1787,7 @@
 		int saved_errno = errno;
 		rollback_lock_file(&mark_lock);
 		failure |= error("Unable to write marks file %s: %s",
-			mark_file, strerror(saved_errno));
+			export_marks_file, strerror(saved_errno));
 		return;
 	}
 
@@ -1637,11 +1795,51 @@
 		int saved_errno = errno;
 		rollback_lock_file(&mark_lock);
 		failure |= error("Unable to commit marks file %s: %s",
-			mark_file, strerror(saved_errno));
+			export_marks_file, strerror(saved_errno));
 		return;
 	}
 }
 
+static void read_marks(void)
+{
+	char line[512];
+	FILE *f = fopen(import_marks_file, "r");
+	if (f)
+		;
+	else if (import_marks_file_ignore_missing && errno == ENOENT)
+		return; /* Marks file does not exist */
+	else
+		die_errno("cannot read '%s'", import_marks_file);
+	while (fgets(line, sizeof(line), f)) {
+		uintmax_t mark;
+		char *end;
+		unsigned char sha1[20];
+		struct object_entry *e;
+
+		end = strchr(line, '\n');
+		if (line[0] != ':' || !end)
+			die("corrupt mark line: %s", line);
+		*end = 0;
+		mark = strtoumax(line + 1, &end, 10);
+		if (!mark || end == line + 1
+			|| *end != ' ' || get_sha1(end + 1, sha1))
+			die("corrupt mark line: %s", line);
+		e = find_object(sha1);
+		if (!e) {
+			enum object_type type = sha1_object_info(sha1, NULL);
+			if (type < 0)
+				die("object not found: %s", sha1_to_hex(sha1));
+			e = insert_object(sha1);
+			e->type = type;
+			e->pack_id = MAX_PACK_ID;
+			e->idx.offset = 1; /* just not zero! */
+		}
+		insert_mark(mark, e);
+	}
+	fclose(f);
+}
+
+
 static int read_next_command(void)
 {
 	static int stdin_eof = 0;
@@ -1651,7 +1849,7 @@
 		return EOF;
 	}
 
-	do {
+	for (;;) {
 		if (unread_command_buf) {
 			unread_command_buf = 0;
 		} else {
@@ -1662,6 +1860,12 @@
 			if (stdin_eof)
 				return EOF;
 
+			if (!seen_data_command
+				&& prefixcmp(command_buf.buf, "feature ")
+				&& prefixcmp(command_buf.buf, "option ")) {
+				parse_argv();
+			}
+
 			rc = rc_free;
 			if (rc)
 				rc_free = rc->next;
@@ -1678,9 +1882,14 @@
 			rc->prev->next = rc;
 			cmd_tail = rc;
 		}
-	} while (command_buf.buf[0] == '#');
-
-	return 0;
+		if (!prefixcmp(command_buf.buf, "cat-blob ")) {
+			parse_cat_blob();
+			continue;
+		}
+		if (command_buf.buf[0] == '#')
+			continue;
+		return 0;
+	}
 }
 
 static void skip_optional_lf(void)
@@ -1700,7 +1909,7 @@
 		next_mark = 0;
 }
 
-static void parse_data(struct strbuf *sb)
+static int parse_data(struct strbuf *sb, uintmax_t limit, uintmax_t *len_res)
 {
 	strbuf_reset(sb);
 
@@ -1724,9 +1933,15 @@
 		free(term);
 	}
 	else {
-		size_t n = 0, length;
+		uintmax_t len = strtoumax(command_buf.buf + 5, NULL, 10);
+		size_t n = 0, length = (size_t)len;
 
-		length = strtoul(command_buf.buf + 5, NULL, 10);
+		if (limit && limit < len) {
+			*len_res = len;
+			return 0;
+		}
+		if (length < len)
+			die("data is too large to use in this context");
 
 		while (n < length) {
 			size_t s = strbuf_fread(sb, length - n, stdin);
@@ -1738,16 +1953,19 @@
 	}
 
 	skip_optional_lf();
+	return 1;
 }
 
 static int validate_raw_date(const char *src, char *result, int maxlen)
 {
 	const char *orig_src = src;
 	char *endp;
+	unsigned long num;
 
 	errno = 0;
 
-	strtoul(src, &endp, 10);
+	num = strtoul(src, &endp, 10);
+	/* NEEDSWORK: perhaps check for reasonable values? */
 	if (errno || endp == src || *endp != ' ')
 		return -1;
 
@@ -1755,8 +1973,9 @@
 	if (*src != '-' && *src != '+')
 		return -1;
 
-	strtoul(src + 1, &endp, 10);
-	if (errno || endp == src || *endp || (endp - orig_src) >= maxlen)
+	num = strtoul(src + 1, &endp, 10);
+	if (errno || endp == src + 1 || *endp || (endp - orig_src) >= maxlen ||
+	    1400 < num)
 		return -1;
 
 	strcpy(result, orig_src);
@@ -1765,32 +1984,41 @@
 
 static char *parse_ident(const char *buf)
 {
-	const char *gt;
+	const char *ltgt;
 	size_t name_len;
 	char *ident;
 
-	gt = strrchr(buf, '>');
-	if (!gt)
+	/* ensure there is a space delimiter even if there is no name */
+	if (*buf == '<')
+		--buf;
+
+	ltgt = buf + strcspn(buf, "<>");
+	if (*ltgt != '<')
+		die("Missing < in ident string: %s", buf);
+	if (ltgt != buf && ltgt[-1] != ' ')
+		die("Missing space before < in ident string: %s", buf);
+	ltgt = ltgt + 1 + strcspn(ltgt + 1, "<>");
+	if (*ltgt != '>')
 		die("Missing > in ident string: %s", buf);
-	gt++;
-	if (*gt != ' ')
+	ltgt++;
+	if (*ltgt != ' ')
 		die("Missing space after > in ident string: %s", buf);
-	gt++;
-	name_len = gt - buf;
+	ltgt++;
+	name_len = ltgt - buf;
 	ident = xmalloc(name_len + 24);
 	strncpy(ident, buf, name_len);
 
 	switch (whenspec) {
 	case WHENSPEC_RAW:
-		if (validate_raw_date(gt, ident + name_len, 24) < 0)
-			die("Invalid raw date \"%s\" in ident: %s", gt, buf);
+		if (validate_raw_date(ltgt, ident + name_len, 24) < 0)
+			die("Invalid raw date \"%s\" in ident: %s", ltgt, buf);
 		break;
 	case WHENSPEC_RFC2822:
-		if (parse_date(gt, ident + name_len, 24) < 0)
-			die("Invalid rfc2822 date \"%s\" in ident: %s", gt, buf);
+		if (parse_date(ltgt, ident + name_len, 24) < 0)
+			die("Invalid rfc2822 date \"%s\" in ident: %s", ltgt, buf);
 		break;
 	case WHENSPEC_NOW:
-		if (strcmp("now", gt))
+		if (strcmp("now", ltgt))
 			die("Date in ident must be 'now': %s", buf);
 		datestamp(ident + name_len, 24);
 		break;
@@ -1799,14 +2027,32 @@
 	return ident;
 }
 
-static void parse_new_blob(void)
+static void parse_and_store_blob(
+	struct last_object *last,
+	unsigned char *sha1out,
+	uintmax_t mark)
 {
 	static struct strbuf buf = STRBUF_INIT;
+	uintmax_t len;
 
+	if (parse_data(&buf, big_file_threshold, &len))
+		store_object(OBJ_BLOB, &buf, last, sha1out, mark);
+	else {
+		if (last) {
+			strbuf_release(&last->data);
+			last->offset = 0;
+			last->depth = 0;
+		}
+		stream_blob(len, sha1out, mark);
+		skip_optional_lf();
+	}
+}
+
+static void parse_new_blob(void)
+{
 	read_next_command();
 	parse_mark();
-	parse_data(&buf);
-	store_object(OBJ_BLOB, &buf, &last_blob, NULL, next_mark);
+	parse_and_store_blob(&last_blob, NULL, next_mark);
 }
 
 static void unload_one_branch(void)
@@ -1853,6 +2099,167 @@
 	}
 }
 
+static unsigned char convert_num_notes_to_fanout(uintmax_t num_notes)
+{
+	unsigned char fanout = 0;
+	while ((num_notes >>= 8))
+		fanout++;
+	return fanout;
+}
+
+static void construct_path_with_fanout(const char *hex_sha1,
+		unsigned char fanout, char *path)
+{
+	unsigned int i = 0, j = 0;
+	if (fanout >= 20)
+		die("Too large fanout (%u)", fanout);
+	while (fanout) {
+		path[i++] = hex_sha1[j++];
+		path[i++] = hex_sha1[j++];
+		path[i++] = '/';
+		fanout--;
+	}
+	memcpy(path + i, hex_sha1 + j, 40 - j);
+	path[i + 40 - j] = '\0';
+}
+
+static uintmax_t do_change_note_fanout(
+		struct tree_entry *orig_root, struct tree_entry *root,
+		char *hex_sha1, unsigned int hex_sha1_len,
+		char *fullpath, unsigned int fullpath_len,
+		unsigned char fanout)
+{
+	struct tree_content *t = root->tree;
+	struct tree_entry *e, leaf;
+	unsigned int i, tmp_hex_sha1_len, tmp_fullpath_len;
+	uintmax_t num_notes = 0;
+	unsigned char sha1[20];
+	char realpath[60];
+
+	for (i = 0; t && i < t->entry_count; i++) {
+		e = t->entries[i];
+		tmp_hex_sha1_len = hex_sha1_len + e->name->str_len;
+		tmp_fullpath_len = fullpath_len;
+
+		/*
+		 * We're interested in EITHER existing note entries (entries
+		 * with exactly 40 hex chars in path, not including directory
+		 * separators), OR directory entries that may contain note
+		 * entries (with < 40 hex chars in path).
+		 * Also, each path component in a note entry must be a multiple
+		 * of 2 chars.
+		 */
+		if (!e->versions[1].mode ||
+		    tmp_hex_sha1_len > 40 ||
+		    e->name->str_len % 2)
+			continue;
+
+		/* This _may_ be a note entry, or a subdir containing notes */
+		memcpy(hex_sha1 + hex_sha1_len, e->name->str_dat,
+		       e->name->str_len);
+		if (tmp_fullpath_len)
+			fullpath[tmp_fullpath_len++] = '/';
+		memcpy(fullpath + tmp_fullpath_len, e->name->str_dat,
+		       e->name->str_len);
+		tmp_fullpath_len += e->name->str_len;
+		fullpath[tmp_fullpath_len] = '\0';
+
+		if (tmp_hex_sha1_len == 40 && !get_sha1_hex(hex_sha1, sha1)) {
+			/* This is a note entry */
+			if (fanout == 0xff) {
+				/* Counting mode, no rename */
+				num_notes++;
+				continue;
+			}
+			construct_path_with_fanout(hex_sha1, fanout, realpath);
+			if (!strcmp(fullpath, realpath)) {
+				/* Note entry is in correct location */
+				num_notes++;
+				continue;
+			}
+
+			/* Rename fullpath to realpath */
+			if (!tree_content_remove(orig_root, fullpath, &leaf))
+				die("Failed to remove path %s", fullpath);
+			tree_content_set(orig_root, realpath,
+				leaf.versions[1].sha1,
+				leaf.versions[1].mode,
+				leaf.tree);
+		} else if (S_ISDIR(e->versions[1].mode)) {
+			/* This is a subdir that may contain note entries */
+			if (!e->tree)
+				load_tree(e);
+			num_notes += do_change_note_fanout(orig_root, e,
+				hex_sha1, tmp_hex_sha1_len,
+				fullpath, tmp_fullpath_len, fanout);
+		}
+
+		/* The above may have reallocated the current tree_content */
+		t = root->tree;
+	}
+	return num_notes;
+}
+
+static uintmax_t change_note_fanout(struct tree_entry *root,
+		unsigned char fanout)
+{
+	char hex_sha1[40], path[60];
+	return do_change_note_fanout(root, root, hex_sha1, 0, path, 0, fanout);
+}
+
+/*
+ * Given a pointer into a string, parse a mark reference:
+ *
+ *   idnum ::= ':' bigint;
+ *
+ * Return the first character after the value in *endptr.
+ *
+ * Complain if the following character is not what is expected,
+ * either a space or end of the string.
+ */
+static uintmax_t parse_mark_ref(const char *p, char **endptr)
+{
+	uintmax_t mark;
+
+	assert(*p == ':');
+	p++;
+	mark = strtoumax(p, endptr, 10);
+	if (*endptr == p)
+		die("No value after ':' in mark: %s", command_buf.buf);
+	return mark;
+}
+
+/*
+ * Parse the mark reference, and complain if this is not the end of
+ * the string.
+ */
+static uintmax_t parse_mark_ref_eol(const char *p)
+{
+	char *end;
+	uintmax_t mark;
+
+	mark = parse_mark_ref(p, &end);
+	if (*end != '\0')
+		die("Garbage after mark: %s", command_buf.buf);
+	return mark;
+}
+
+/*
+ * Parse the mark reference, demanding a trailing space.  Return a
+ * pointer to the space.
+ */
+static uintmax_t parse_mark_ref_space(const char **p)
+{
+	uintmax_t mark;
+	char *end;
+
+	mark = parse_mark_ref(*p, &end);
+	if (*end != ' ')
+		die("Missing space after mark: %s", command_buf.buf);
+	*p = end;
+	return mark;
+}
+
 static void file_change_m(struct branch *b)
 {
 	const char *p = command_buf.buf + 2;
@@ -1872,6 +2279,7 @@
 	case S_IFREG | 0644:
 	case S_IFREG | 0755:
 	case S_IFLNK:
+	case S_IFDIR:
 	case S_IFGITLINK:
 		/* ok */
 		break;
@@ -1880,21 +2288,21 @@
 	}
 
 	if (*p == ':') {
-		char *x;
-		oe = find_mark(strtoumax(p + 1, &x, 10));
-		hashcpy(sha1, oe->sha1);
-		p = x;
-	} else if (!prefixcmp(p, "inline")) {
+		oe = find_mark(parse_mark_ref_space(&p));
+		hashcpy(sha1, oe->idx.sha1);
+	} else if (!prefixcmp(p, "inline ")) {
 		inline_data = 1;
-		p += 6;
+		p += strlen("inline");  /* advance to space */
 	} else {
 		if (get_sha1_hex(p, sha1))
-			die("Invalid SHA1: %s", command_buf.buf);
+			die("Invalid dataref: %s", command_buf.buf);
 		oe = find_object(sha1);
 		p += 40;
+		if (*p != ' ')
+			die("Missing space after SHA1: %s", command_buf.buf);
 	}
-	if (*p++ != ' ')
-		die("Missing space after SHA1: %s", command_buf.buf);
+	assert(*p == ' ');
+	p++;  /* skip space */
 
 	strbuf_reset(&uq);
 	if (!unquote_c_style(&uq, p, &endp)) {
@@ -1903,6 +2311,12 @@
 		p = uq.buf;
 	}
 
+	/* Git does not track empty, non-toplevel directories. */
+	if (S_ISDIR(mode) && !memcmp(sha1, EMPTY_TREE_SHA1_BIN, 20) && *p) {
+		tree_content_remove(&b->branch_tree, p, NULL);
+		return;
+	}
+
 	if (S_ISGITLINK(mode)) {
 		if (inline_data)
 			die("Git links cannot be specified 'inline': %s",
@@ -1917,28 +2331,34 @@
 		 * another repository.
 		 */
 	} else if (inline_data) {
-		static struct strbuf buf = STRBUF_INIT;
-
+		if (S_ISDIR(mode))
+			die("Directories cannot be specified 'inline': %s",
+				command_buf.buf);
 		if (p != uq.buf) {
 			strbuf_addstr(&uq, p);
 			p = uq.buf;
 		}
 		read_next_command();
-		parse_data(&buf);
-		store_object(OBJ_BLOB, &buf, &last_blob, sha1, 0);
-	} else if (oe) {
-		if (oe->type != OBJ_BLOB)
-			die("Not a blob (actually a %s): %s",
-				typename(oe->type), command_buf.buf);
+		parse_and_store_blob(&last_blob, sha1, 0);
 	} else {
-		enum object_type type = sha1_object_info(sha1, NULL);
+		enum object_type expected = S_ISDIR(mode) ?
+						OBJ_TREE: OBJ_BLOB;
+		enum object_type type = oe ? oe->type :
+					sha1_object_info(sha1, NULL);
 		if (type < 0)
-			die("Blob not found: %s", command_buf.buf);
-		if (type != OBJ_BLOB)
-			die("Not a blob (actually a %s): %s",
-			    typename(type), command_buf.buf);
+			die("%s not found: %s",
+					S_ISDIR(mode) ?  "Tree" : "Blob",
+					command_buf.buf);
+		if (type != expected)
+			die("Not a %s (actually a %s): %s",
+				typename(expected), typename(type),
+				command_buf.buf);
 	}
 
+	if (!*p) {
+		tree_content_replace(&b->branch_tree, sha1, mode, NULL);
+		return;
+	}
 	tree_content_set(&b->branch_tree, p, sha1, mode, NULL);
 }
 
@@ -1997,18 +2417,127 @@
 		tree_content_get(&b->branch_tree, s, &leaf);
 	if (!leaf.versions[1].mode)
 		die("Path %s not in branch", s);
+	if (!*d) {	/* C "path/to/subdir" "" */
+		tree_content_replace(&b->branch_tree,
+			leaf.versions[1].sha1,
+			leaf.versions[1].mode,
+			leaf.tree);
+		return;
+	}
 	tree_content_set(&b->branch_tree, d,
 		leaf.versions[1].sha1,
 		leaf.versions[1].mode,
 		leaf.tree);
 }
 
+static void note_change_n(struct branch *b, unsigned char *old_fanout)
+{
+	const char *p = command_buf.buf + 2;
+	static struct strbuf uq = STRBUF_INIT;
+	struct object_entry *oe = oe;
+	struct branch *s;
+	unsigned char sha1[20], commit_sha1[20];
+	char path[60];
+	uint16_t inline_data = 0;
+	unsigned char new_fanout;
+
+	/*
+	 * When loading a branch, we don't traverse its tree to count the real
+	 * number of notes (too expensive to do this for all non-note refs).
+	 * This means that recently loaded notes refs might incorrectly have
+	 * b->num_notes == 0, and consequently, old_fanout might be wrong.
+	 *
+	 * Fix this by traversing the tree and counting the number of notes
+	 * when b->num_notes == 0. If the notes tree is truly empty, the
+	 * calculation should not take long.
+	 */
+	if (b->num_notes == 0 && *old_fanout == 0) {
+		/* Invoke change_note_fanout() in "counting mode". */
+		b->num_notes = change_note_fanout(&b->branch_tree, 0xff);
+		*old_fanout = convert_num_notes_to_fanout(b->num_notes);
+	}
+
+	/* Now parse the notemodify command. */
+	/* <dataref> or 'inline' */
+	if (*p == ':') {
+		oe = find_mark(parse_mark_ref_space(&p));
+		hashcpy(sha1, oe->idx.sha1);
+	} else if (!prefixcmp(p, "inline ")) {
+		inline_data = 1;
+		p += strlen("inline");  /* advance to space */
+	} else {
+		if (get_sha1_hex(p, sha1))
+			die("Invalid dataref: %s", command_buf.buf);
+		oe = find_object(sha1);
+		p += 40;
+		if (*p != ' ')
+			die("Missing space after SHA1: %s", command_buf.buf);
+	}
+	assert(*p == ' ');
+	p++;  /* skip space */
+
+	/* <committish> */
+	s = lookup_branch(p);
+	if (s) {
+		if (is_null_sha1(s->sha1))
+			die("Can't add a note on empty branch.");
+		hashcpy(commit_sha1, s->sha1);
+	} else if (*p == ':') {
+		uintmax_t commit_mark = parse_mark_ref_eol(p);
+		struct object_entry *commit_oe = find_mark(commit_mark);
+		if (commit_oe->type != OBJ_COMMIT)
+			die("Mark :%" PRIuMAX " not a commit", commit_mark);
+		hashcpy(commit_sha1, commit_oe->idx.sha1);
+	} else if (!get_sha1(p, commit_sha1)) {
+		unsigned long size;
+		char *buf = read_object_with_reference(commit_sha1,
+			commit_type, &size, commit_sha1);
+		if (!buf || size < 46)
+			die("Not a valid commit: %s", p);
+		free(buf);
+	} else
+		die("Invalid ref name or SHA1 expression: %s", p);
+
+	if (inline_data) {
+		if (p != uq.buf) {
+			strbuf_addstr(&uq, p);
+			p = uq.buf;
+		}
+		read_next_command();
+		parse_and_store_blob(&last_blob, sha1, 0);
+	} else if (oe) {
+		if (oe->type != OBJ_BLOB)
+			die("Not a blob (actually a %s): %s",
+				typename(oe->type), command_buf.buf);
+	} else if (!is_null_sha1(sha1)) {
+		enum object_type type = sha1_object_info(sha1, NULL);
+		if (type < 0)
+			die("Blob not found: %s", command_buf.buf);
+		if (type != OBJ_BLOB)
+			die("Not a blob (actually a %s): %s",
+			    typename(type), command_buf.buf);
+	}
+
+	construct_path_with_fanout(sha1_to_hex(commit_sha1), *old_fanout, path);
+	if (tree_content_remove(&b->branch_tree, path, NULL))
+		b->num_notes--;
+
+	if (is_null_sha1(sha1))
+		return; /* nothing to insert */
+
+	b->num_notes++;
+	new_fanout = convert_num_notes_to_fanout(b->num_notes);
+	construct_path_with_fanout(sha1_to_hex(commit_sha1), new_fanout, path);
+	tree_content_set(&b->branch_tree, path, sha1, S_IFREG | 0644, NULL);
+}
+
 static void file_change_deleteall(struct branch *b)
 {
 	release_tree_content_recursive(b->branch_tree.tree);
 	hashclr(b->branch_tree.versions[0].sha1);
 	hashclr(b->branch_tree.versions[1].sha1);
 	load_tree(&b->branch_tree);
+	b->num_notes = 0;
 }
 
 static void parse_from_commit(struct branch *b, char *buf, unsigned long size)
@@ -2061,11 +2590,11 @@
 		hashcpy(b->branch_tree.versions[0].sha1, t);
 		hashcpy(b->branch_tree.versions[1].sha1, t);
 	} else if (*from == ':') {
-		uintmax_t idnum = strtoumax(from + 1, NULL, 10);
+		uintmax_t idnum = parse_mark_ref_eol(from);
 		struct object_entry *oe = find_mark(idnum);
 		if (oe->type != OBJ_COMMIT)
 			die("Mark :%" PRIuMAX " not a commit", idnum);
-		hashcpy(b->sha1, oe->sha1);
+		hashcpy(b->sha1, oe->idx.sha1);
 		if (oe->pack_id != MAX_PACK_ID) {
 			unsigned long size;
 			char *buf = gfi_unpack_entry(oe, &size);
@@ -2096,11 +2625,11 @@
 		if (s)
 			hashcpy(n->sha1, s->sha1);
 		else if (*from == ':') {
-			uintmax_t idnum = strtoumax(from + 1, NULL, 10);
+			uintmax_t idnum = parse_mark_ref_eol(from);
 			struct object_entry *oe = find_mark(idnum);
 			if (oe->type != OBJ_COMMIT)
 				die("Mark :%" PRIuMAX " not a commit", idnum);
-			hashcpy(n->sha1, oe->sha1);
+			hashcpy(n->sha1, oe->idx.sha1);
 		} else if (!get_sha1(from, n->sha1)) {
 			unsigned long size;
 			char *buf = read_object_with_reference(n->sha1,
@@ -2132,6 +2661,7 @@
 	char *committer = NULL;
 	struct hash_list *merge_list = NULL;
 	unsigned int merge_count;
+	unsigned char prev_fanout, new_fanout;
 
 	/* Obtain the branch name from the rest of our command */
 	sp = strchr(command_buf.buf, ' ') + 1;
@@ -2151,7 +2681,7 @@
 	}
 	if (!committer)
 		die("Expected committer but didn't get one");
-	parse_data(&msg);
+	parse_data(&msg, 0, NULL);
 	read_next_command();
 	parse_from(b);
 	merge_list = parse_merge(&merge_count);
@@ -2162,6 +2692,8 @@
 		load_branch(b);
 	}
 
+	prev_fanout = convert_num_notes_to_fanout(b->num_notes);
+
 	/* file_change* */
 	while (command_buf.len > 0) {
 		if (!prefixcmp(command_buf.buf, "M "))
@@ -2172,8 +2704,12 @@
 			file_change_cr(b, 1);
 		else if (!prefixcmp(command_buf.buf, "C "))
 			file_change_cr(b, 0);
+		else if (!prefixcmp(command_buf.buf, "N "))
+			note_change_n(b, &prev_fanout);
 		else if (!strcmp("deleteall", command_buf.buf))
 			file_change_deleteall(b);
+		else if (!prefixcmp(command_buf.buf, "ls "))
+			parse_ls(b);
 		else {
 			unread_command_buf = 1;
 			break;
@@ -2182,6 +2718,10 @@
 			break;
 	}
 
+	new_fanout = convert_num_notes_to_fanout(b->num_notes);
+	if (new_fanout != prev_fanout)
+		b->num_notes = change_note_fanout(&b->branch_tree, new_fanout);
+
 	/* build the tree and the commit */
 	store_tree(&b->branch_tree);
 	hashcpy(b->branch_tree.versions[0].sha1,
@@ -2222,11 +2762,12 @@
 	struct tag *t;
 	uintmax_t from_mark = 0;
 	unsigned char sha1[20];
+	enum object_type type;
 
 	/* Obtain the new tag name from the rest of our command */
 	sp = strchr(command_buf.buf, ' ') + 1;
 	t = pool_alloc(sizeof(struct tag));
-	t->next_tag = NULL;
+	memset(t, 0, sizeof(struct tag));
 	t->name = pool_strdup(sp);
 	if (last_tag)
 		last_tag->next_tag = t;
@@ -2241,23 +2782,24 @@
 	from = strchr(command_buf.buf, ' ') + 1;
 	s = lookup_branch(from);
 	if (s) {
+		if (is_null_sha1(s->sha1))
+			die("Can't tag an empty branch.");
 		hashcpy(sha1, s->sha1);
+		type = OBJ_COMMIT;
 	} else if (*from == ':') {
 		struct object_entry *oe;
-		from_mark = strtoumax(from + 1, NULL, 10);
+		from_mark = parse_mark_ref_eol(from);
 		oe = find_mark(from_mark);
-		if (oe->type != OBJ_COMMIT)
-			die("Mark :%" PRIuMAX " not a commit", from_mark);
-		hashcpy(sha1, oe->sha1);
+		type = oe->type;
+		hashcpy(sha1, oe->idx.sha1);
 	} else if (!get_sha1(from, sha1)) {
-		unsigned long size;
-		char *buf;
-
-		buf = read_object_with_reference(sha1,
-			commit_type, &size, sha1);
-		if (!buf || size < 46)
-			die("Not a valid commit: %s", from);
-		free(buf);
+		struct object_entry *oe = find_object(sha1);
+		if (!oe) {
+			type = sha1_object_info(sha1, NULL);
+			if (type < 0)
+				die("Not a valid object: %s", from);
+		} else
+			type = oe->type;
 	} else
 		die("Invalid ref name or SHA1 expression: %s", from);
 	read_next_command();
@@ -2270,7 +2812,7 @@
 		tagger = NULL;
 
 	/* tag payload/message */
-	parse_data(&msg);
+	parse_data(&msg, 0, NULL);
 
 	/* build the tag object */
 	strbuf_reset(&new_data);
@@ -2279,7 +2821,7 @@
 		    "object %s\n"
 		    "type %s\n"
 		    "tag %s\n",
-		    sha1_to_hex(sha1), commit_type, t->name);
+		    sha1_to_hex(sha1), typename(type), t->name);
 	if (tagger)
 		strbuf_addf(&new_data,
 			    "tagger %s\n", tagger);
@@ -2318,14 +2860,240 @@
 		unread_command_buf = 1;
 }
 
-static void parse_checkpoint(void)
+static void cat_blob_write(const char *buf, unsigned long size)
 {
+	if (write_in_full(cat_blob_fd, buf, size) != size)
+		die_errno("Write to frontend failed");
+}
+
+static void cat_blob(struct object_entry *oe, unsigned char sha1[20])
+{
+	struct strbuf line = STRBUF_INIT;
+	unsigned long size;
+	enum object_type type = 0;
+	char *buf;
+
+	if (!oe || oe->pack_id == MAX_PACK_ID) {
+		buf = read_sha1_file(sha1, &type, &size);
+	} else {
+		type = oe->type;
+		buf = gfi_unpack_entry(oe, &size);
+	}
+
+	/*
+	 * Output based on batch_one_object() from cat-file.c.
+	 */
+	if (type <= 0) {
+		strbuf_reset(&line);
+		strbuf_addf(&line, "%s missing\n", sha1_to_hex(sha1));
+		cat_blob_write(line.buf, line.len);
+		strbuf_release(&line);
+		free(buf);
+		return;
+	}
+	if (!buf)
+		die("Can't read object %s", sha1_to_hex(sha1));
+	if (type != OBJ_BLOB)
+		die("Object %s is a %s but a blob was expected.",
+		    sha1_to_hex(sha1), typename(type));
+	strbuf_reset(&line);
+	strbuf_addf(&line, "%s %s %lu\n", sha1_to_hex(sha1),
+						typename(type), size);
+	cat_blob_write(line.buf, line.len);
+	strbuf_release(&line);
+	cat_blob_write(buf, size);
+	cat_blob_write("\n", 1);
+	if (oe && oe->pack_id == pack_id) {
+		last_blob.offset = oe->idx.offset;
+		strbuf_attach(&last_blob.data, buf, size, size);
+		last_blob.depth = oe->depth;
+	} else
+		free(buf);
+}
+
+static void parse_cat_blob(void)
+{
+	const char *p;
+	struct object_entry *oe = oe;
+	unsigned char sha1[20];
+
+	/* cat-blob SP <object> LF */
+	p = command_buf.buf + strlen("cat-blob ");
+	if (*p == ':') {
+		oe = find_mark(parse_mark_ref_eol(p));
+		if (!oe)
+			die("Unknown mark: %s", command_buf.buf);
+		hashcpy(sha1, oe->idx.sha1);
+	} else {
+		if (get_sha1_hex(p, sha1))
+			die("Invalid dataref: %s", command_buf.buf);
+		if (p[40])
+			die("Garbage after SHA1: %s", command_buf.buf);
+		oe = find_object(sha1);
+	}
+
+	cat_blob(oe, sha1);
+}
+
+static struct object_entry *dereference(struct object_entry *oe,
+					unsigned char sha1[20])
+{
+	unsigned long size;
+	char *buf = NULL;
+	if (!oe) {
+		enum object_type type = sha1_object_info(sha1, NULL);
+		if (type < 0)
+			die("object not found: %s", sha1_to_hex(sha1));
+		/* cache it! */
+		oe = insert_object(sha1);
+		oe->type = type;
+		oe->pack_id = MAX_PACK_ID;
+		oe->idx.offset = 1;
+	}
+	switch (oe->type) {
+	case OBJ_TREE:	/* easy case. */
+		return oe;
+	case OBJ_COMMIT:
+	case OBJ_TAG:
+		break;
+	default:
+		die("Not a treeish: %s", command_buf.buf);
+	}
+
+	if (oe->pack_id != MAX_PACK_ID) {	/* in a pack being written */
+		buf = gfi_unpack_entry(oe, &size);
+	} else {
+		enum object_type unused;
+		buf = read_sha1_file(sha1, &unused, &size);
+	}
+	if (!buf)
+		die("Can't load object %s", sha1_to_hex(sha1));
+
+	/* Peel one layer. */
+	switch (oe->type) {
+	case OBJ_TAG:
+		if (size < 40 + strlen("object ") ||
+		    get_sha1_hex(buf + strlen("object "), sha1))
+			die("Invalid SHA1 in tag: %s", command_buf.buf);
+		break;
+	case OBJ_COMMIT:
+		if (size < 40 + strlen("tree ") ||
+		    get_sha1_hex(buf + strlen("tree "), sha1))
+			die("Invalid SHA1 in commit: %s", command_buf.buf);
+	}
+
+	free(buf);
+	return find_object(sha1);
+}
+
+static struct object_entry *parse_treeish_dataref(const char **p)
+{
+	unsigned char sha1[20];
+	struct object_entry *e;
+
+	if (**p == ':') {	/* <mark> */
+		e = find_mark(parse_mark_ref_space(p));
+		if (!e)
+			die("Unknown mark: %s", command_buf.buf);
+		hashcpy(sha1, e->idx.sha1);
+	} else {	/* <sha1> */
+		if (get_sha1_hex(*p, sha1))
+			die("Invalid dataref: %s", command_buf.buf);
+		e = find_object(sha1);
+		*p += 40;
+	}
+
+	while (!e || e->type != OBJ_TREE)
+		e = dereference(e, sha1);
+	return e;
+}
+
+static void print_ls(int mode, const unsigned char *sha1, const char *path)
+{
+	static struct strbuf line = STRBUF_INIT;
+
+	/* See show_tree(). */
+	const char *type =
+		S_ISGITLINK(mode) ? commit_type :
+		S_ISDIR(mode) ? tree_type :
+		blob_type;
+
+	if (!mode) {
+		/* missing SP path LF */
+		strbuf_reset(&line);
+		strbuf_addstr(&line, "missing ");
+		quote_c_style(path, &line, NULL, 0);
+		strbuf_addch(&line, '\n');
+	} else {
+		/* mode SP type SP object_name TAB path LF */
+		strbuf_reset(&line);
+		strbuf_addf(&line, "%06o %s %s\t",
+				mode & ~NO_DELTA, type, sha1_to_hex(sha1));
+		quote_c_style(path, &line, NULL, 0);
+		strbuf_addch(&line, '\n');
+	}
+	cat_blob_write(line.buf, line.len);
+}
+
+static void parse_ls(struct branch *b)
+{
+	const char *p;
+	struct tree_entry *root = NULL;
+	struct tree_entry leaf = {NULL};
+
+	/* ls SP (<treeish> SP)? <path> */
+	p = command_buf.buf + strlen("ls ");
+	if (*p == '"') {
+		if (!b)
+			die("Not in a commit: %s", command_buf.buf);
+		root = &b->branch_tree;
+	} else {
+		struct object_entry *e = parse_treeish_dataref(&p);
+		root = new_tree_entry();
+		hashcpy(root->versions[1].sha1, e->idx.sha1);
+		load_tree(root);
+		if (*p++ != ' ')
+			die("Missing space after tree-ish: %s", command_buf.buf);
+	}
+	if (*p == '"') {
+		static struct strbuf uq = STRBUF_INIT;
+		const char *endp;
+		strbuf_reset(&uq);
+		if (unquote_c_style(&uq, p, &endp))
+			die("Invalid path: %s", command_buf.buf);
+		if (*endp)
+			die("Garbage after path in: %s", command_buf.buf);
+		p = uq.buf;
+	}
+	tree_content_get(root, p, &leaf);
+	/*
+	 * A directory in preparation would have a sha1 of zero
+	 * until it is saved.  Save, for simplicity.
+	 */
+	if (S_ISDIR(leaf.versions[1].mode))
+		store_tree(&leaf);
+
+	print_ls(leaf.versions[1].mode, leaf.versions[1].sha1, p);
+	if (leaf.tree)
+		release_tree_content_recursive(leaf.tree);
+	if (!b || root != &b->branch_tree)
+		release_tree_entry(root);
+}
+
+static void checkpoint(void)
+{
+	checkpoint_requested = 0;
 	if (object_count) {
 		cycle_packfile();
 		dump_branches();
 		dump_tags();
 		dump_marks();
 	}
+}
+
+static void parse_checkpoint(void)
+{
+	checkpoint_requested = 1;
 	skip_optional_lf();
 }
 
@@ -2337,39 +3105,180 @@
 	skip_optional_lf();
 }
 
-static void import_marks(const char *input_file)
+static char* make_fast_import_path(const char *path)
 {
-	char line[512];
-	FILE *f = fopen(input_file, "r");
-	if (!f)
-		die_errno("cannot read '%s'", input_file);
-	while (fgets(line, sizeof(line), f)) {
-		uintmax_t mark;
-		char *end;
-		unsigned char sha1[20];
-		struct object_entry *e;
+	struct strbuf abs_path = STRBUF_INIT;
 
-		end = strchr(line, '\n');
-		if (line[0] != ':' || !end)
-			die("corrupt mark line: %s", line);
-		*end = 0;
-		mark = strtoumax(line + 1, &end, 10);
-		if (!mark || end == line + 1
-			|| *end != ' ' || get_sha1(end + 1, sha1))
-			die("corrupt mark line: %s", line);
-		e = find_object(sha1);
-		if (!e) {
-			enum object_type type = sha1_object_info(sha1, NULL);
-			if (type < 0)
-				die("object not found: %s", sha1_to_hex(sha1));
-			e = insert_object(sha1);
-			e->type = type;
-			e->pack_id = MAX_PACK_ID;
-			e->offset = 1; /* just not zero! */
-		}
-		insert_mark(mark, e);
+	if (!relative_marks_paths || is_absolute_path(path))
+		return xstrdup(path);
+	strbuf_addf(&abs_path, "%s/info/fast-import/%s", get_git_dir(), path);
+	return strbuf_detach(&abs_path, NULL);
+}
+
+static void option_import_marks(const char *marks,
+					int from_stream, int ignore_missing)
+{
+	if (import_marks_file) {
+		if (from_stream)
+			die("Only one import-marks command allowed per stream");
+
+		/* read previous mark file */
+		if(!import_marks_file_from_stream)
+			read_marks();
 	}
-	fclose(f);
+
+	import_marks_file = make_fast_import_path(marks);
+	safe_create_leading_directories_const(import_marks_file);
+	import_marks_file_from_stream = from_stream;
+	import_marks_file_ignore_missing = ignore_missing;
+}
+
+static void option_date_format(const char *fmt)
+{
+	if (!strcmp(fmt, "raw"))
+		whenspec = WHENSPEC_RAW;
+	else if (!strcmp(fmt, "rfc2822"))
+		whenspec = WHENSPEC_RFC2822;
+	else if (!strcmp(fmt, "now"))
+		whenspec = WHENSPEC_NOW;
+	else
+		die("unknown --date-format argument %s", fmt);
+}
+
+static unsigned long ulong_arg(const char *option, const char *arg)
+{
+	char *endptr;
+	unsigned long rv = strtoul(arg, &endptr, 0);
+	if (strchr(arg, '-') || endptr == arg || *endptr)
+		die("%s: argument must be a non-negative integer", option);
+	return rv;
+}
+
+static void option_depth(const char *depth)
+{
+	max_depth = ulong_arg("--depth", depth);
+	if (max_depth > MAX_DEPTH)
+		die("--depth cannot exceed %u", MAX_DEPTH);
+}
+
+static void option_active_branches(const char *branches)
+{
+	max_active_branches = ulong_arg("--active-branches", branches);
+}
+
+static void option_export_marks(const char *marks)
+{
+	export_marks_file = make_fast_import_path(marks);
+	safe_create_leading_directories_const(export_marks_file);
+}
+
+static void option_cat_blob_fd(const char *fd)
+{
+	unsigned long n = ulong_arg("--cat-blob-fd", fd);
+	if (n > (unsigned long) INT_MAX)
+		die("--cat-blob-fd cannot exceed %d", INT_MAX);
+	cat_blob_fd = (int) n;
+}
+
+static void option_export_pack_edges(const char *edges)
+{
+	if (pack_edges)
+		fclose(pack_edges);
+	pack_edges = fopen(edges, "a");
+	if (!pack_edges)
+		die_errno("Cannot open '%s'", edges);
+}
+
+static int parse_one_option(const char *option)
+{
+	if (!prefixcmp(option, "max-pack-size=")) {
+		unsigned long v;
+		if (!git_parse_ulong(option + 14, &v))
+			return 0;
+		if (v < 8192) {
+			warning("max-pack-size is now in bytes, assuming --max-pack-size=%lum", v);
+			v *= 1024 * 1024;
+		} else if (v < 1024 * 1024) {
+			warning("minimum max-pack-size is 1 MiB");
+			v = 1024 * 1024;
+		}
+		max_packsize = v;
+	} else if (!prefixcmp(option, "big-file-threshold=")) {
+		unsigned long v;
+		if (!git_parse_ulong(option + 19, &v))
+			return 0;
+		big_file_threshold = v;
+	} else if (!prefixcmp(option, "depth=")) {
+		option_depth(option + 6);
+	} else if (!prefixcmp(option, "active-branches=")) {
+		option_active_branches(option + 16);
+	} else if (!prefixcmp(option, "export-pack-edges=")) {
+		option_export_pack_edges(option + 18);
+	} else if (!prefixcmp(option, "quiet")) {
+		show_stats = 0;
+	} else if (!prefixcmp(option, "stats")) {
+		show_stats = 1;
+	} else {
+		return 0;
+	}
+
+	return 1;
+}
+
+static int parse_one_feature(const char *feature, int from_stream)
+{
+	if (!prefixcmp(feature, "date-format=")) {
+		option_date_format(feature + 12);
+	} else if (!prefixcmp(feature, "import-marks=")) {
+		option_import_marks(feature + 13, from_stream, 0);
+	} else if (!prefixcmp(feature, "import-marks-if-exists=")) {
+		option_import_marks(feature + strlen("import-marks-if-exists="),
+					from_stream, 1);
+	} else if (!prefixcmp(feature, "export-marks=")) {
+		option_export_marks(feature + 13);
+	} else if (!strcmp(feature, "cat-blob")) {
+		; /* Don't die - this feature is supported */
+	} else if (!strcmp(feature, "relative-marks")) {
+		relative_marks_paths = 1;
+	} else if (!strcmp(feature, "no-relative-marks")) {
+		relative_marks_paths = 0;
+	} else if (!strcmp(feature, "done")) {
+		require_explicit_termination = 1;
+	} else if (!strcmp(feature, "force")) {
+		force_update = 1;
+	} else if (!strcmp(feature, "notes") || !strcmp(feature, "ls")) {
+		; /* do nothing; we have the feature */
+	} else {
+		return 0;
+	}
+
+	return 1;
+}
+
+static void parse_feature(void)
+{
+	char *feature = command_buf.buf + 8;
+
+	if (seen_data_command)
+		die("Got feature command '%s' after data command", feature);
+
+	if (parse_one_feature(feature, 1))
+		return;
+
+	die("This version of fast-import does not support feature %s.", feature);
+}
+
+static void parse_option(void)
+{
+	char *option = command_buf.buf + 11;
+
+	if (seen_data_command)
+		die("Got option command '%s' after data command", option);
+
+	if (parse_one_option(option))
+		return;
+
+	die("This version of fast-import does not support option: %s", option);
 }
 
 static int git_pack_config(const char *k, const char *v, void *cb)
@@ -2390,19 +3299,67 @@
 		pack_compression_seen = 1;
 		return 0;
 	}
+	if (!strcmp(k, "pack.indexversion")) {
+		pack_idx_opts.version = git_config_int(k, v);
+		if (pack_idx_opts.version > 2)
+			die("bad pack.indexversion=%"PRIu32,
+			    pack_idx_opts.version);
+		return 0;
+	}
+	if (!strcmp(k, "pack.packsizelimit")) {
+		max_packsize = git_config_ulong(k, v);
+		return 0;
+	}
 	return git_default_config(k, v, cb);
 }
 
 static const char fast_import_usage[] =
-"git fast-import [--date-format=f] [--max-pack-size=n] [--depth=n] [--active-branches=n] [--export-marks=marks.file]";
+"git fast-import [--date-format=<f>] [--max-pack-size=<n>] [--big-file-threshold=<n>] [--depth=<n>] [--active-branches=<n>] [--export-marks=<marks.file>]";
+
+static void parse_argv(void)
+{
+	unsigned int i;
+
+	for (i = 1; i < global_argc; i++) {
+		const char *a = global_argv[i];
+
+		if (*a != '-' || !strcmp(a, "--"))
+			break;
+
+		if (parse_one_option(a + 2))
+			continue;
+
+		if (parse_one_feature(a + 2, 0))
+			continue;
+
+		if (!prefixcmp(a + 2, "cat-blob-fd=")) {
+			option_cat_blob_fd(a + 2 + strlen("cat-blob-fd="));
+			continue;
+		}
+
+		die("unknown option %s", a);
+	}
+	if (i != global_argc)
+		usage(fast_import_usage);
+
+	seen_data_command = 1;
+	if (import_marks_file)
+		read_marks();
+}
 
 int main(int argc, const char **argv)
 {
-	unsigned int i, show_stats = 1;
+	unsigned int i;
 
 	git_extract_argv0_path(argv[0]);
 
+	git_setup_gettext();
+
+	if (argc == 2 && !strcmp(argv[1], "-h"))
+		usage(fast_import_usage);
+
 	setup_git_directory();
+	reset_pack_idx_option(&pack_idx_opts);
 	git_config(git_pack_config, NULL);
 	if (!pack_compression_seen && core_compression_seen)
 		pack_compression_level = core_compression_level;
@@ -2414,52 +3371,8 @@
 	avail_tree_table = xcalloc(avail_tree_table_sz, sizeof(struct avail_tree_content*));
 	marks = pool_calloc(1, sizeof(struct mark_set));
 
-	for (i = 1; i < argc; i++) {
-		const char *a = argv[i];
-
-		if (*a != '-' || !strcmp(a, "--"))
-			break;
-		else if (!prefixcmp(a, "--date-format=")) {
-			const char *fmt = a + 14;
-			if (!strcmp(fmt, "raw"))
-				whenspec = WHENSPEC_RAW;
-			else if (!strcmp(fmt, "rfc2822"))
-				whenspec = WHENSPEC_RFC2822;
-			else if (!strcmp(fmt, "now"))
-				whenspec = WHENSPEC_NOW;
-			else
-				die("unknown --date-format argument %s", fmt);
-		}
-		else if (!prefixcmp(a, "--max-pack-size="))
-			max_packsize = strtoumax(a + 16, NULL, 0) * 1024 * 1024;
-		else if (!prefixcmp(a, "--depth=")) {
-			max_depth = strtoul(a + 8, NULL, 0);
-			if (max_depth > MAX_DEPTH)
-				die("--depth cannot exceed %u", MAX_DEPTH);
-		}
-		else if (!prefixcmp(a, "--active-branches="))
-			max_active_branches = strtoul(a + 18, NULL, 0);
-		else if (!prefixcmp(a, "--import-marks="))
-			import_marks(a + 15);
-		else if (!prefixcmp(a, "--export-marks="))
-			mark_file = a + 15;
-		else if (!prefixcmp(a, "--export-pack-edges=")) {
-			if (pack_edges)
-				fclose(pack_edges);
-			pack_edges = fopen(a + 20, "a");
-			if (!pack_edges)
-				die_errno("Cannot open '%s'", a + 20);
-		} else if (!strcmp(a, "--force"))
-			force_update = 1;
-		else if (!strcmp(a, "--quiet"))
-			show_stats = 0;
-		else if (!strcmp(a, "--stats"))
-			show_stats = 1;
-		else
-			die("unknown option %s", a);
-	}
-	if (i != argc)
-		usage(fast_import_usage);
+	global_argc = argc;
+	global_argv = argv;
 
 	rc_free = pool_alloc(cmd_save * sizeof(*rc_free));
 	for (i = 0; i < (cmd_save - 1); i++)
@@ -2469,9 +3382,12 @@
 	prepare_packed_git();
 	start_packfile();
 	set_die_routine(die_nicely);
+	set_checkpoint_signal();
 	while (read_next_command() != EOF) {
 		if (!strcmp("blob", command_buf.buf))
 			parse_new_blob();
+		else if (!prefixcmp(command_buf.buf, "ls "))
+			parse_ls(NULL);
 		else if (!prefixcmp(command_buf.buf, "commit "))
 			parse_new_commit();
 		else if (!prefixcmp(command_buf.buf, "tag "))
@@ -2480,11 +3396,30 @@
 			parse_reset_branch();
 		else if (!strcmp("checkpoint", command_buf.buf))
 			parse_checkpoint();
+		else if (!strcmp("done", command_buf.buf))
+			break;
 		else if (!prefixcmp(command_buf.buf, "progress "))
 			parse_progress();
+		else if (!prefixcmp(command_buf.buf, "feature "))
+			parse_feature();
+		else if (!prefixcmp(command_buf.buf, "option git "))
+			parse_option();
+		else if (!prefixcmp(command_buf.buf, "option "))
+			/* ignore non-git options*/;
 		else
 			die("Unsupported command: %s", command_buf.buf);
+
+		if (checkpoint_requested)
+			checkpoint();
 	}
+
+	/* argv hasn't been parsed yet, do so */
+	if (!seen_data_command)
+		parse_argv();
+
+	if (require_explicit_termination && feof(stdin))
+		die("stream ends early");
+
 	end_packfile();
 
 	dump_branches();
@@ -2506,10 +3441,10 @@
 		fprintf(stderr, "---------------------------------------------------------------------\n");
 		fprintf(stderr, "Alloc'd objects: %10" PRIuMAX "\n", alloc_count);
 		fprintf(stderr, "Total objects:   %10" PRIuMAX " (%10" PRIuMAX " duplicates                  )\n", total_count, duplicate_count);
-		fprintf(stderr, "      blobs  :   %10" PRIuMAX " (%10" PRIuMAX " duplicates %10" PRIuMAX " deltas)\n", object_count_by_type[OBJ_BLOB], duplicate_count_by_type[OBJ_BLOB], delta_count_by_type[OBJ_BLOB]);
-		fprintf(stderr, "      trees  :   %10" PRIuMAX " (%10" PRIuMAX " duplicates %10" PRIuMAX " deltas)\n", object_count_by_type[OBJ_TREE], duplicate_count_by_type[OBJ_TREE], delta_count_by_type[OBJ_TREE]);
-		fprintf(stderr, "      commits:   %10" PRIuMAX " (%10" PRIuMAX " duplicates %10" PRIuMAX " deltas)\n", object_count_by_type[OBJ_COMMIT], duplicate_count_by_type[OBJ_COMMIT], delta_count_by_type[OBJ_COMMIT]);
-		fprintf(stderr, "      tags   :   %10" PRIuMAX " (%10" PRIuMAX " duplicates %10" PRIuMAX " deltas)\n", object_count_by_type[OBJ_TAG], duplicate_count_by_type[OBJ_TAG], delta_count_by_type[OBJ_TAG]);
+		fprintf(stderr, "      blobs  :   %10" PRIuMAX " (%10" PRIuMAX " duplicates %10" PRIuMAX " deltas of %10" PRIuMAX" attempts)\n", object_count_by_type[OBJ_BLOB], duplicate_count_by_type[OBJ_BLOB], delta_count_by_type[OBJ_BLOB], delta_count_attempts_by_type[OBJ_BLOB]);
+		fprintf(stderr, "      trees  :   %10" PRIuMAX " (%10" PRIuMAX " duplicates %10" PRIuMAX " deltas of %10" PRIuMAX" attempts)\n", object_count_by_type[OBJ_TREE], duplicate_count_by_type[OBJ_TREE], delta_count_by_type[OBJ_TREE], delta_count_attempts_by_type[OBJ_TREE]);
+		fprintf(stderr, "      commits:   %10" PRIuMAX " (%10" PRIuMAX " duplicates %10" PRIuMAX " deltas of %10" PRIuMAX" attempts)\n", object_count_by_type[OBJ_COMMIT], duplicate_count_by_type[OBJ_COMMIT], delta_count_by_type[OBJ_COMMIT], delta_count_attempts_by_type[OBJ_COMMIT]);
+		fprintf(stderr, "      tags   :   %10" PRIuMAX " (%10" PRIuMAX " duplicates %10" PRIuMAX " deltas of %10" PRIuMAX" attempts)\n", object_count_by_type[OBJ_TAG], duplicate_count_by_type[OBJ_TAG], delta_count_by_type[OBJ_TAG], delta_count_attempts_by_type[OBJ_TAG]);
 		fprintf(stderr, "Total branches:  %10lu (%10lu loads     )\n", branch_count, branch_load_count);
 		fprintf(stderr, "      marks:     %10" PRIuMAX " (%10" PRIuMAX " unique    )\n", (((uintmax_t)1) << marks->shift) * 1024, marks_set_count);
 		fprintf(stderr, "      atoms:     %10u\n", atom_cnt);
diff --git a/fetch-pack.h b/fetch-pack.h
index 8bd9c32..7c2069c 100644
--- a/fetch-pack.h
+++ b/fetch-pack.h
@@ -1,8 +1,7 @@
 #ifndef FETCH_PACK_H
 #define FETCH_PACK_H
 
-struct fetch_pack_args
-{
+struct fetch_pack_args {
 	const char *uploadpack;
 	int unpacklimit;
 	int depth;
@@ -11,9 +10,11 @@
 		lock_pack:1,
 		use_thin_pack:1,
 		fetch_all:1,
+		stdin_refs:1,
 		verbose:1,
 		no_progress:1,
-		include_tag:1;
+		include_tag:1,
+		stateless_rpc:1;
 };
 
 struct ref *fetch_pack(struct fetch_pack_args *args,
diff --git a/fmt-merge-msg.h b/fmt-merge-msg.h
new file mode 100644
index 0000000..b28d3a6
--- /dev/null
+++ b/fmt-merge-msg.h
@@ -0,0 +1,7 @@
+#ifndef FMT_MERGE_MSG_H
+#define FMT_MERGE_MSG_H
+
+extern int merge_log_config;
+extern int fmt_merge_msg_config(const char *key, const char *value, void *cb);
+
+#endif /* FMT_MERGE_MSG_H */
diff --git a/fsck.c b/fsck.c
index 89278c1..4c63b2c 100644
--- a/fsck.c
+++ b/fsck.c
@@ -27,7 +27,7 @@
 		else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode))
 			result = walk(&lookup_blob(entry.sha1)->object, OBJ_BLOB, data);
 		else {
-			result = error("in tree %s: entry %s has bad mode %.6o\n",
+			result = error("in tree %s: entry %s has bad mode %.6o",
 					sha1_to_hex(tree->object.sha1), entry.path, entry.mode);
 		}
 		if (result < 0)
@@ -222,12 +222,49 @@
 	return retval;
 }
 
+static int fsck_ident(char **ident, struct object *obj, fsck_error error_func)
+{
+	if (**ident == '<')
+		return error_func(obj, FSCK_ERROR, "invalid author/committer line - missing space before email");
+	*ident += strcspn(*ident, "<>\n");
+	if (**ident == '>')
+		return error_func(obj, FSCK_ERROR, "invalid author/committer line - bad name");
+	if (**ident != '<')
+		return error_func(obj, FSCK_ERROR, "invalid author/committer line - missing email");
+	if ((*ident)[-1] != ' ')
+		return error_func(obj, FSCK_ERROR, "invalid author/committer line - missing space before email");
+	(*ident)++;
+	*ident += strcspn(*ident, "<>\n");
+	if (**ident != '>')
+		return error_func(obj, FSCK_ERROR, "invalid author/committer line - bad email");
+	(*ident)++;
+	if (**ident != ' ')
+		return error_func(obj, FSCK_ERROR, "invalid author/committer line - missing space before date");
+	(*ident)++;
+	if (**ident == '0' && (*ident)[1] != ' ')
+		return error_func(obj, FSCK_ERROR, "invalid author/committer line - zero-padded date");
+	*ident += strspn(*ident, "0123456789");
+	if (**ident != ' ')
+		return error_func(obj, FSCK_ERROR, "invalid author/committer line - bad date");
+	(*ident)++;
+	if ((**ident != '+' && **ident != '-') ||
+	    !isdigit((*ident)[1]) ||
+	    !isdigit((*ident)[2]) ||
+	    !isdigit((*ident)[3]) ||
+	    !isdigit((*ident)[4]) ||
+	    ((*ident)[5] != '\n'))
+		return error_func(obj, FSCK_ERROR, "invalid author/committer line - bad time zone");
+	(*ident) += 6;
+	return 0;
+}
+
 static int fsck_commit(struct commit *commit, fsck_error error_func)
 {
 	char *buffer = commit->buffer;
 	unsigned char tree_sha1[20], sha1[20];
 	struct commit_graft *graft;
 	int parents = 0;
+	int err;
 
 	if (commit->date == ULONG_MAX)
 		return error_func(&commit->object, FSCK_ERROR, "invalid author/committer line");
@@ -266,6 +303,16 @@
 	}
 	if (memcmp(buffer, "author ", 7))
 		return error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'author' line");
+	buffer += 7;
+	err = fsck_ident(&buffer, &commit->object, error_func);
+	if (err)
+		return err;
+	if (memcmp(buffer, "committer ", strlen("committer ")))
+		return error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'committer' line");
+	buffer += strlen("committer ");
+	err = fsck_ident(&buffer, &commit->object, error_func);
+	if (err)
+		return err;
 	if (!commit->tree)
 		return error_func(&commit->object, FSCK_ERROR, "could not load commit's tree %s", sha1_to_hex(tree_sha1));
 
@@ -302,26 +349,14 @@
 int fsck_error_function(struct object *obj, int type, const char *fmt, ...)
 {
 	va_list ap;
-	int len;
 	struct strbuf sb = STRBUF_INIT;
 
-	strbuf_addf(&sb, "object %s:", obj->sha1?sha1_to_hex(obj->sha1):"(null)");
+	strbuf_addf(&sb, "object %s:", sha1_to_hex(obj->sha1));
 
 	va_start(ap, fmt);
-	len = vsnprintf(sb.buf + sb.len, strbuf_avail(&sb), fmt, ap);
+	strbuf_vaddf(&sb, fmt, ap);
 	va_end(ap);
 
-	if (len < 0)
-		len = 0;
-	if (len >= strbuf_avail(&sb)) {
-		strbuf_grow(&sb, len + 2);
-		va_start(ap, fmt);
-		len = vsnprintf(sb.buf + sb.len, strbuf_avail(&sb), fmt, ap);
-		va_end(ap);
-		if (len >= strbuf_avail(&sb))
-			die("this should not happen, your snprintf is broken");
-	}
-
 	error("%s", sb.buf);
 	strbuf_release(&sb);
 	return 1;
diff --git a/fsck.h b/fsck.h
index 008456b..1e4f527 100644
--- a/fsck.h
+++ b/fsck.h
@@ -17,6 +17,7 @@
 /* callback for fsck_object, type is FSCK_ERROR or FSCK_WARN */
 typedef int (*fsck_error)(struct object *obj, int type, const char *err, ...);
 
+__attribute__((format (printf, 3, 4)))
 int fsck_error_function(struct object *obj, int type, const char *fmt, ...);
 
 /* descend in all linked child objects
diff --git a/generate-cmdlist.sh b/generate-cmdlist.sh
index 75c68d9..9a4c9b9 100755
--- a/generate-cmdlist.sh
+++ b/generate-cmdlist.sh
@@ -1,8 +1,7 @@
 #!/bin/sh
 
 echo "/* Automatically generated by $0 */
-struct cmdname_help
-{
+struct cmdname_help {
     char name[16];
     char help[80];
 };
@@ -16,8 +15,8 @@
      sed -n '
      /^NAME/,/git-'"$cmd"'/H
      ${
-            x
-            s/.*git-'"$cmd"' - \(.*\)/  {"'"$cmd"'", "\1"},/
+	    x
+	    s/.*git-'"$cmd"' - \(.*\)/  {"'"$cmd"'", N_("\1")},/
 	    p
      }' "Documentation/git-$cmd.txt"
 done
diff --git a/gettext.c b/gettext.c
new file mode 100644
index 0000000..f75bca7
--- /dev/null
+++ b/gettext.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2010 Ævar Arnfjörð Bjarmason
+ */
+
+#include "git-compat-util.h"
+#include "gettext.h"
+
+#ifndef NO_GETTEXT
+#	include <locale.h>
+#	include <libintl.h>
+#	ifdef HAVE_LIBCHARSET_H
+#		include <libcharset.h>
+#	else
+#		include <langinfo.h>
+#		define locale_charset() nl_langinfo(CODESET)
+#	endif
+#endif
+
+#ifdef GETTEXT_POISON
+int use_gettext_poison(void)
+{
+	static int poison_requested = -1;
+	if (poison_requested == -1)
+		poison_requested = getenv("GIT_GETTEXT_POISON") ? 1 : 0;
+	return poison_requested;
+}
+#endif
+
+#ifndef NO_GETTEXT
+static void init_gettext_charset(const char *domain)
+{
+	const char *charset;
+
+	/*
+	   This trick arranges for messages to be emitted in the user's
+	   requested encoding, but avoids setting LC_CTYPE from the
+	   environment for the whole program.
+
+	   This primarily done to avoid a bug in vsnprintf in the GNU C
+	   Library [1]. which triggered a "your vsnprintf is broken" error
+	   on Git's own repository when inspecting v0.99.6~1 under a UTF-8
+	   locale.
+
+	   That commit contains a ISO-8859-1 encoded author name, which
+	   the locale aware vsnprintf(3) won't interpolate in the format
+	   argument, due to mismatch between the data encoding and the
+	   locale.
+
+	   Even if it wasn't for that bug we wouldn't want to use LC_CTYPE at
+	   this point, because it'd require auditing all the code that uses C
+	   functions whose semantics are modified by LC_CTYPE.
+
+	   But only setting LC_MESSAGES as we do creates a problem, since
+	   we declare the encoding of our PO files[2] the gettext
+	   implementation will try to recode it to the user's locale, but
+	   without LC_CTYPE it'll emit something like this on 'git init'
+	   under the Icelandic locale:
+
+	       Bj? til t?ma Git lind ? /hlagh/.git/
+
+	   Gettext knows about the encoding of our PO file, but we haven't
+	   told it about the user's encoding, so all the non-US-ASCII
+	   characters get encoded to question marks.
+
+	   But we're in luck! We can set LC_CTYPE from the environment
+	   only while we call nl_langinfo and
+	   bind_textdomain_codeset. That suffices to tell gettext what
+	   encoding it should emit in, so it'll now say:
+
+	       Bjó til tóma Git lind í /hlagh/.git/
+
+	   And the equivalent ISO-8859-1 string will be emitted under a
+	   ISO-8859-1 locale.
+
+	   With this change way we get the advantages of setting LC_CTYPE
+	   (talk to the user in his language/encoding), without the major
+	   drawbacks (changed semantics for C functions we rely on).
+
+	   However foreign functions using other message catalogs that
+	   aren't using our neat trick will still have a problem, e.g. if
+	   we have to call perror(3):
+
+	   #include <stdio.h>
+	   #include <locale.h>
+	   #include <errno.h>
+
+	   int main(void)
+	   {
+		   setlocale(LC_MESSAGES, "");
+		   setlocale(LC_CTYPE, "C");
+		   errno = ENODEV;
+		   perror("test");
+		   return 0;
+	   }
+
+	   Running that will give you a message with question marks:
+
+	   $ LANGUAGE= LANG=de_DE.utf8 ./test
+	   test: Kein passendes Ger?t gefunden
+
+	   In the long term we should probably see about getting that
+	   vsnprintf bug in glibc fixed, and audit our code so it won't
+	   fall apart under a non-C locale.
+
+	   Then we could simply set LC_CTYPE from the environment, which would
+	   make things like the external perror(3) messages work.
+
+	   See t/t0203-gettext-setlocale-sanity.sh's "gettext.c" tests for
+	   regression tests.
+
+	   1. http://sourceware.org/bugzilla/show_bug.cgi?id=6530
+	   2. E.g. "Content-Type: text/plain; charset=UTF-8\n" in po/is.po
+	*/
+	setlocale(LC_CTYPE, "");
+	charset = locale_charset();
+	bind_textdomain_codeset(domain, charset);
+	setlocale(LC_CTYPE, "C");
+}
+
+void git_setup_gettext(void)
+{
+	const char *podir = getenv("GIT_TEXTDOMAINDIR");
+
+	if (!podir)
+		podir = GIT_LOCALE_PATH;
+	bindtextdomain("git", podir);
+	setlocale(LC_MESSAGES, "");
+	init_gettext_charset("git");
+	textdomain("git");
+}
+#endif
diff --git a/gettext.h b/gettext.h
new file mode 100644
index 0000000..57ba8bb
--- /dev/null
+++ b/gettext.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2010-2011 Ævar Arnfjörð Bjarmason
+ *
+ * This is a skeleton no-op implementation of gettext for Git.
+ * You can replace it with something that uses libintl.h and wraps
+ * gettext() to try out the translations.
+ */
+
+#ifndef GETTEXT_H
+#define GETTEXT_H
+
+#if defined(_) || defined(Q_)
+#error "namespace conflict: '_' or 'Q_' is pre-defined?"
+#endif
+
+#ifndef NO_GETTEXT
+#	include <libintl.h>
+#else
+#	ifdef gettext
+#		undef gettext
+#	endif
+#	define gettext(s) (s)
+#	ifdef ngettext
+#		undef ngettext
+#	endif
+#	define ngettext(s, p, n) ((n == 1) ? (s) : (p))
+#endif
+
+#define FORMAT_PRESERVING(n) __attribute__((format_arg(n)))
+
+#ifndef NO_GETTEXT
+extern void git_setup_gettext(void);
+#else
+static inline void git_setup_gettext(void)
+{
+}
+#endif
+
+#ifdef GETTEXT_POISON
+extern int use_gettext_poison(void);
+#else
+#define use_gettext_poison() 0
+#endif
+
+static inline FORMAT_PRESERVING(1) const char *_(const char *msgid)
+{
+	return use_gettext_poison() ? "# GETTEXT POISON #" : gettext(msgid);
+}
+
+static inline FORMAT_PRESERVING(1) FORMAT_PRESERVING(2)
+const char *Q_(const char *msgid, const char *plu, unsigned long n)
+{
+	if (use_gettext_poison())
+		return "# GETTEXT POISON #";
+	return ngettext(msgid, plu, n);
+}
+
+/* Mark msgid for translation but do not translate it. */
+#define N_(msgid) msgid
+
+#endif
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index df9f231..710764a 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -1,6 +1,8 @@
-#!/usr/bin/perl -w
+#!/usr/bin/perl
 
+use 5.008;
 use strict;
+use warnings;
 use Git;
 
 binmode(STDOUT, ":raw");
@@ -43,6 +45,9 @@
 my $normal_color = $repo->get_color("", "reset");
 
 my $use_readkey = 0;
+my $use_termcap = 0;
+my %term_escapes;
+
 sub ReadMode;
 sub ReadKey;
 if ($repo->config_bool("interactive.singlekey")) {
@@ -51,6 +56,14 @@
 		Term::ReadKey->import;
 		$use_readkey = 1;
 	};
+	eval {
+		require Term::Cap;
+		my $termcap = Term::Cap->Tgetent;
+		foreach (values %$termcap) {
+			$term_escapes{$_} = 1 if /^\e/;
+		}
+		$use_termcap = 1;
+	};
 }
 
 sub colored {
@@ -72,6 +85,86 @@
 
 # command line options
 my $patch_mode;
+my $patch_mode_revision;
+
+sub apply_patch;
+sub apply_patch_for_checkout_commit;
+sub apply_patch_for_stash;
+
+my %patch_modes = (
+	'stage' => {
+		DIFF => 'diff-files -p',
+		APPLY => sub { apply_patch 'apply --cached', @_; },
+		APPLY_CHECK => 'apply --cached',
+		VERB => 'Stage',
+		TARGET => '',
+		PARTICIPLE => 'staging',
+		FILTER => 'file-only',
+		IS_REVERSE => 0,
+	},
+	'stash' => {
+		DIFF => 'diff-index -p HEAD',
+		APPLY => sub { apply_patch 'apply --cached', @_; },
+		APPLY_CHECK => 'apply --cached',
+		VERB => 'Stash',
+		TARGET => '',
+		PARTICIPLE => 'stashing',
+		FILTER => undef,
+		IS_REVERSE => 0,
+	},
+	'reset_head' => {
+		DIFF => 'diff-index -p --cached',
+		APPLY => sub { apply_patch 'apply -R --cached', @_; },
+		APPLY_CHECK => 'apply -R --cached',
+		VERB => 'Unstage',
+		TARGET => '',
+		PARTICIPLE => 'unstaging',
+		FILTER => 'index-only',
+		IS_REVERSE => 1,
+	},
+	'reset_nothead' => {
+		DIFF => 'diff-index -R -p --cached',
+		APPLY => sub { apply_patch 'apply --cached', @_; },
+		APPLY_CHECK => 'apply --cached',
+		VERB => 'Apply',
+		TARGET => ' to index',
+		PARTICIPLE => 'applying',
+		FILTER => 'index-only',
+		IS_REVERSE => 0,
+	},
+	'checkout_index' => {
+		DIFF => 'diff-files -p',
+		APPLY => sub { apply_patch 'apply -R', @_; },
+		APPLY_CHECK => 'apply -R',
+		VERB => 'Discard',
+		TARGET => ' from worktree',
+		PARTICIPLE => 'discarding',
+		FILTER => 'file-only',
+		IS_REVERSE => 1,
+	},
+	'checkout_head' => {
+		DIFF => 'diff-index -p',
+		APPLY => sub { apply_patch_for_checkout_commit '-R', @_ },
+		APPLY_CHECK => 'apply -R',
+		VERB => 'Discard',
+		TARGET => ' from index and worktree',
+		PARTICIPLE => 'discarding',
+		FILTER => undef,
+		IS_REVERSE => 1,
+	},
+	'checkout_nothead' => {
+		DIFF => 'diff-index -R -p',
+		APPLY => sub { apply_patch_for_checkout_commit '', @_ },
+		APPLY_CHECK => 'apply',
+		VERB => 'Apply',
+		TARGET => ' to index and worktree',
+		PARTICIPLE => 'applying',
+		FILTER => undef,
+		IS_REVERSE => 0,
+	},
+);
+
+my %patch_mode_flavour = %{$patch_modes{stage}};
 
 sub run_cmd_pipe {
 	if ($^O eq 'MSWin32' || $^O eq 'msys') {
@@ -175,6 +268,7 @@
 # FILE:		is file different from index?
 # INDEX_ADDDEL:	is it add/delete between HEAD and index?
 # FILE_ADDDEL:	is it add/delete between index and file?
+# UNMERGED:	is the path unmerged
 
 sub list_modified {
 	my ($only) = @_;
@@ -186,11 +280,18 @@
 		@tracked = map {
 			chomp $_;
 			unquote_path($_);
-		} run_cmd_pipe(qw(git ls-files --exclude-standard --), @ARGV);
+		} run_cmd_pipe(qw(git ls-files --), @ARGV);
 		return if (!@tracked);
 	}
 
-	my $reference = is_initial_commit() ? get_empty_tree() : 'HEAD';
+	my $reference;
+	if (defined $patch_mode_revision and $patch_mode_revision ne 'HEAD') {
+		$reference = $patch_mode_revision;
+	} elsif (is_initial_commit()) {
+		$reference = get_empty_tree();
+	} else {
+		$reference = 'HEAD';
+	}
 	for (run_cmd_pipe(qw(git diff-index --cached
 			     --numstat --summary), $reference,
 			     '--', @tracked)) {
@@ -218,16 +319,10 @@
 		}
 	}
 
-	for (run_cmd_pipe(qw(git diff-files --numstat --summary --), @tracked)) {
+	for (run_cmd_pipe(qw(git diff-files --numstat --summary --raw --), @tracked)) {
 		if (($add, $del, $file) =
 		    /^([-\d]+)	([-\d]+)	(.*)/) {
 			$file = unquote_path($file);
-			if (!exists $data{$file}) {
-				$data{$file} = +{
-					INDEX => 'unchanged',
-					BINARY => 0,
-				};
-			}
 			my ($change, $bin);
 			if ($add eq '-' && $del eq '-') {
 				$change = 'binary';
@@ -246,6 +341,18 @@
 			$file = unquote_path($file);
 			$data{$file}{FILE_ADDDEL} = $adddel;
 		}
+		elsif (/^:[0-7]+ [0-7]+ [0-9a-f]+ [0-9a-f]+ (.)	(.*)$/) {
+			$file = unquote_path($2);
+			if (!exists $data{$file}) {
+				$data{$file} = +{
+					INDEX => 'unchanged',
+					BINARY => 0,
+				};
+			}
+			if ($1 eq 'U') {
+				$data{$file}{UNMERGED} = 1;
+			}
+		}
 	}
 
 	for (sort keys %data) {
@@ -613,12 +720,24 @@
 	print "\n";
 }
 
+sub run_git_apply {
+	my $cmd = shift;
+	my $fh;
+	open $fh, '| git ' . $cmd . " --recount --allow-overlap";
+	print $fh @_;
+	return close $fh;
+}
+
 sub parse_diff {
 	my ($path) = @_;
-	my @diff = run_cmd_pipe(qw(git diff-files -p --), $path);
+	my @diff_cmd = split(" ", $patch_mode_flavour{DIFF});
+	if (defined $patch_mode_revision) {
+		push @diff_cmd, $patch_mode_revision;
+	}
+	my @diff = run_cmd_pipe("git", @diff_cmd, "--", $path);
 	my @colored = ();
 	if ($diff_use_color) {
-		@colored = run_cmd_pipe(qw(git diff-files -p --color --), $path);
+		@colored = run_cmd_pipe("git", @diff_cmd, qw(--color --), $path);
 	}
 	my (@hunk) = { TEXT => [], DISPLAY => [], TYPE => 'header' };
 
@@ -639,14 +758,17 @@
 
 	my $head = { TEXT => [], DISPLAY => [], TYPE => 'header' };
 	my $mode = { TEXT => [], DISPLAY => [], TYPE => 'mode' };
+	my $deletion = { TEXT => [], DISPLAY => [], TYPE => 'deletion' };
 
 	for (my $i = 0; $i < @{$src->{TEXT}}; $i++) {
-		my $dest = $src->{TEXT}->[$i] =~ /^(old|new) mode (\d+)$/ ?
-			$mode : $head;
+		my $dest =
+		   $src->{TEXT}->[$i] =~ /^(old|new) mode (\d+)$/ ? $mode :
+		   $src->{TEXT}->[$i] =~ /^deleted file/ ? $deletion :
+		   $head;
 		push @{$dest->{TEXT}}, $src->{TEXT}->[$i];
 		push @{$dest->{DISPLAY}}, $src->{DISPLAY}->[$i];
 	}
-	return ($head, $mode);
+	return ($head, $mode, $deletion);
 }
 
 sub hunk_splittable {
@@ -841,6 +963,10 @@
 	my ($last_o_ctx, $last_was_dirty);
 
 	for (grep { $_->{USE} } @in) {
+		if ($_->{TYPE} ne 'hunk') {
+			push @out, $_;
+			next;
+		}
 		my $text = $_->{TEXT};
 		my ($o_ofs) = parse_hunk_header($text->[0]);
 		if (defined $last_o_ctx &&
@@ -858,6 +984,28 @@
 	return @out;
 }
 
+sub reassemble_patch {
+	my $head = shift;
+	my @patch;
+
+	# Include everything in the header except the beginning of the diff.
+	push @patch, (grep { !/^[-+]{3}/ } @$head);
+
+	# Then include any headers from the hunk lines, which must
+	# come before any actual hunk.
+	while (@_ && $_[0] !~ /^@/) {
+		push @patch, shift;
+	}
+
+	# Then begin the diff.
+	push @patch, grep { /^[-+]{3}/ } @$head;
+
+	# And then the actual hunks.
+	push @patch, @_;
+
+	return @patch;
+}
+
 sub color_diff {
 	return map {
 		colored((/^@/  ? $fraginfo_color :
@@ -877,21 +1025,23 @@
 		or die "failed to open hunk edit file for writing: " . $!;
 	print $fh "# Manual hunk edit mode -- see bottom for a quick guide\n";
 	print $fh @$oldtext;
+	my $participle = $patch_mode_flavour{PARTICIPLE};
+	my $is_reverse = $patch_mode_flavour{IS_REVERSE};
+	my ($remove_plus, $remove_minus) = $is_reverse ? ('-', '+') : ('+', '-');
 	print $fh <<EOF;
 # ---
-# To remove '-' lines, make them ' ' lines (context).
-# To remove '+' lines, delete them.
+# To remove '$remove_minus' lines, make them ' ' lines (context).
+# To remove '$remove_plus' lines, delete them.
 # Lines starting with # will be removed.
 #
 # If the patch applies cleanly, the edited hunk will immediately be
-# marked for staging. If it does not apply cleanly, you will be given
+# marked for $participle. If it does not apply cleanly, you will be given
 # an opportunity to edit again. If all lines of the hunk are removed,
 # then the edit is aborted and the hunk is left unchanged.
 EOF
 	close $fh;
 
-	my $editor = $ENV{GIT_EDITOR} || $repo->config("core.editor")
-		|| $ENV{VISUAL} || $ENV{EDITOR} || "vi";
+	chomp(my $editor = run_cmd_pipe(qw(git var GIT_EDITOR)));
 	system('sh', '-c', $editor.' "$@"', $editor, $hunkfile);
 
 	if ($? != 0) {
@@ -917,12 +1067,8 @@
 }
 
 sub diff_applies {
-	my $fh;
-	open $fh, '| git apply --recount --cached --check';
-	for my $h (@_) {
-		print $fh @{$h->{TEXT}};
-	}
-	return close $fh;
+	return run_git_apply($patch_mode_flavour{APPLY_CHECK} . ' --check',
+			     map { @{$_->{TEXT}} } @_);
 }
 
 sub _restore_terminal_and_die {
@@ -938,6 +1084,14 @@
 		ReadMode 'cbreak';
 		my $key = ReadKey 0;
 		ReadMode 'restore';
+		if ($use_termcap and $key eq "\e") {
+			while (!defined $term_escapes{$key}) {
+				my $next = ReadKey 0.5;
+				last if (!defined $next);
+				$key .= $next;
+			}
+			$key =~ s/\e/^[/;
+		}
 		print "$key" if defined $key;
 		print "\n";
 		return $key;
@@ -988,12 +1142,14 @@
 }
 
 sub help_patch_cmd {
-	print colored $help_color, <<\EOF ;
-y - stage this hunk
-n - do not stage this hunk
-q - quit, do not stage this hunk nor any of the remaining ones
-a - stage this and all the remaining hunks in the file
-d - do not stage this hunk nor any of the remaining hunks in the file
+	my $verb = lc $patch_mode_flavour{VERB};
+	my $target = $patch_mode_flavour{TARGET};
+	print colored $help_color, <<EOF ;
+y - $verb this hunk$target
+n - do not $verb this hunk$target
+q - quit; do not $verb this hunk nor any of the remaining ones
+a - $verb this hunk and all later hunks in the file
+d - do not $verb this hunk nor any of the later hunks in the file
 g - select a hunk to go to
 / - search for a hunk matching the given regex
 j - leave this hunk undecided, see next undecided hunk
@@ -1006,8 +1162,44 @@
 EOF
 }
 
+sub apply_patch {
+	my $cmd = shift;
+	my $ret = run_git_apply $cmd, @_;
+	if (!$ret) {
+		print STDERR @_;
+	}
+	return $ret;
+}
+
+sub apply_patch_for_checkout_commit {
+	my $reverse = shift;
+	my $applies_index = run_git_apply 'apply '.$reverse.' --cached --check', @_;
+	my $applies_worktree = run_git_apply 'apply '.$reverse.' --check', @_;
+
+	if ($applies_worktree && $applies_index) {
+		run_git_apply 'apply '.$reverse.' --cached', @_;
+		run_git_apply 'apply '.$reverse, @_;
+		return 1;
+	} elsif (!$applies_index) {
+		print colored $error_color, "The selected hunks do not apply to the index!\n";
+		if (prompt_yesno "Apply them to the worktree anyway? ") {
+			return run_git_apply 'apply '.$reverse, @_;
+		} else {
+			print colored $error_color, "Nothing was applied.\n";
+			return 0;
+		}
+	} else {
+		print STDERR @_;
+		return 0;
+	}
+}
+
 sub patch_update_cmd {
-	my @all_mods = list_modified('file-only');
+	my @all_mods = list_modified($patch_mode_flavour{FILTER});
+	error_msg "ignoring unmerged: $_->{VALUE}\n"
+		for grep { $_->{UNMERGED} } @all_mods;
+	@all_mods = grep { !$_->{UNMERGED} } @all_mods;
+
 	my @mods = grep { !($_->{BINARY}) } @all_mods;
 	my @them;
 
@@ -1078,7 +1270,7 @@
 	my ($ix, $num);
 	my $path = shift;
 	my ($head, @hunk) = parse_diff($path);
-	($head, my $mode) = parse_diff_header($head);
+	($head, my $mode, my $deletion) = parse_diff_header($head);
 	for (@{$head->{DISPLAY}}) {
 		print;
 	}
@@ -1086,6 +1278,13 @@
 	if (@{$mode->{TEXT}}) {
 		unshift @hunk, $mode;
 	}
+	if (@{$deletion->{TEXT}}) {
+		foreach my $hunk (@hunk) {
+			push @{$deletion->{TEXT}}, @{$hunk->{TEXT}};
+			push @{$deletion->{DISPLAY}}, @{$hunk->{DISPLAY}};
+		}
+		@hunk = ($deletion);
+	}
 
 	$num = scalar @hunk;
 	$ix = 0;
@@ -1138,8 +1337,11 @@
 		for (@{$hunk[$ix]{DISPLAY}}) {
 			print;
 		}
-		print colored $prompt_color, 'Stage ',
-		  ($hunk[$ix]{TYPE} eq 'mode' ? 'mode change' : 'this hunk'),
+		print colored $prompt_color, $patch_mode_flavour{VERB},
+		  ($hunk[$ix]{TYPE} eq 'mode' ? ' mode change' :
+		   $hunk[$ix]{TYPE} eq 'deletion' ? ' deletion' :
+		   ' this hunk'),
+		  $patch_mode_flavour{TARGET},
 		  " [y,n,q,a,d,/$other,?]? ";
 		my $line = prompt_single_character;
 		if ($line) {
@@ -1193,14 +1395,13 @@
 				next;
 			}
 			elsif ($line =~ /^q/i) {
-				while ($ix < $num) {
-					if (!defined $hunk[$ix]{USE}) {
-						$hunk[$ix]{USE} = 0;
+				for ($i = 0; $i < $num; $i++) {
+					if (!defined $hunk[$i]{USE}) {
+						$hunk[$i]{USE} = 0;
 					}
-					$ix++;
 				}
 				$quit = 1;
-				next;
+				last;
 			}
 			elsif ($line =~ m|^/(.*)|) {
 				my $regex = $1;
@@ -1312,17 +1513,9 @@
 	}
 
 	if (@result) {
-		my $fh;
-
-		open $fh, '| git apply --cached --recount';
-		for (@{$head->{TEXT}}, @result) {
-			print $fh $_;
-		}
-		if (!close $fh) {
-			for (@{$head->{TEXT}}, @result) {
-				print STDERR $_;
-			}
-		}
+		my @patch = reassemble_patch($head->{TEXT}, @result);
+		my $apply_routine = $patch_mode_flavour{APPLY};
+		&$apply_routine(@patch);
 		refresh();
 	}
 
@@ -1363,11 +1556,41 @@
 sub process_args {
 	return unless @ARGV;
 	my $arg = shift @ARGV;
-	if ($arg eq "--patch") {
-		$patch_mode = 1;
-		$arg = shift @ARGV or die "missing --";
+	if ($arg =~ /--patch(?:=(.*))?/) {
+		if (defined $1) {
+			if ($1 eq 'reset') {
+				$patch_mode = 'reset_head';
+				$patch_mode_revision = 'HEAD';
+				$arg = shift @ARGV or die "missing --";
+				if ($arg ne '--') {
+					$patch_mode_revision = $arg;
+					$patch_mode = ($arg eq 'HEAD' ?
+						       'reset_head' : 'reset_nothead');
+					$arg = shift @ARGV or die "missing --";
+				}
+			} elsif ($1 eq 'checkout') {
+				$arg = shift @ARGV or die "missing --";
+				if ($arg eq '--') {
+					$patch_mode = 'checkout_index';
+				} else {
+					$patch_mode_revision = $arg;
+					$patch_mode = ($arg eq 'HEAD' ?
+						       'checkout_head' : 'checkout_nothead');
+					$arg = shift @ARGV or die "missing --";
+				}
+			} elsif ($1 eq 'stage' or $1 eq 'stash') {
+				$patch_mode = $1;
+				$arg = shift @ARGV or die "missing --";
+			} else {
+				die "unknown --patch mode: $1";
+			}
+		} else {
+			$patch_mode = 'stage';
+			$arg = shift @ARGV or die "missing --";
+		}
 		die "invalid argument $arg, expecting --"
 		    unless $arg eq "--";
+		%patch_mode_flavour = %{$patch_modes{$patch_mode}};
 	}
 	elsif ($arg ne "--") {
 		die "invalid argument $arg, expecting --";
diff --git a/git-am.sh b/git-am.sh
index d64d997..c02e62d 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -5,7 +5,7 @@
 SUBDIRECTORY_OK=Yes
 OPTIONS_KEEPDASHDASH=
 OPTIONS_SPEC="\
-git am [options] [<mbox>|<Maildir>...]
+git am [options] [(<mbox>|<Maildir>)...]
 git am [options] (--resolved | --skip | --abort)
 --
 i,interactive   run interactively
@@ -15,28 +15,39 @@
 s,signoff       add a Signed-off-by line to the commit message
 u,utf8          recode into utf8 (default)
 k,keep          pass -k flag to git-mailinfo
+keep-non-patch  pass -b flag to git-mailinfo
+keep-cr         pass --keep-cr flag to git-mailsplit for mbox format
+no-keep-cr      do not pass --keep-cr flag to git-mailsplit independent of am.keepcr
+c,scissors      strip everything before a scissors line
 whitespace=     pass it through git-apply
+ignore-space-change pass it through git-apply
+ignore-whitespace pass it through git-apply
 directory=      pass it through git-apply
+exclude=        pass it through git-apply
+include=        pass it through git-apply
 C=              pass it through git-apply
 p=              pass it through git-apply
 patch-format=   format the patch(es) are in
 reject          pass it through git-apply
 resolvemsg=     override error message when patch failure occurs
-r,resolved      to be used after a patch failure
+continue        continue applying patches after resolving a conflict
+r,resolved      synonyms for --continue
 skip            skip the current patch
 abort           restore the original branch and abort the patching operation.
 committer-date-is-author-date    lie about committer date
 ignore-date     use current timestamp for author date
+rerere-autoupdate update the index with reused conflict resolution if possible
 rebasing*       (internal use for git-rebase)"
 
 . git-sh-setup
+. git-sh-i18n
 prefix=$(git rev-parse --show-prefix)
 set_reflog_action am
 require_work_tree
 cd_to_toplevel
 
 git var GIT_COMMITTER_IDENT >/dev/null ||
-	die "You need to set your committer info first"
+	die "$(gettext "You need to set your committer info first")"
 
 if git rev-parse --verify -q HEAD >/dev/null
 then
@@ -45,32 +56,55 @@
 	HAS_HEAD=
 fi
 
+cmdline="git am"
+if test '' != "$interactive"
+then
+	cmdline="$cmdline -i"
+fi
+if test '' != "$threeway"
+then
+	cmdline="$cmdline -3"
+fi
+
 sq () {
 	git rev-parse --sq-quote "$@"
 }
 
 stop_here () {
     echo "$1" >"$dotest/next"
+    git rev-parse --verify -q HEAD >"$dotest/abort-safety"
     exit 1
 }
 
+safe_to_abort () {
+	if test -f "$dotest/dirtyindex"
+	then
+		return 1
+	fi
+
+	if ! test -s "$dotest/abort-safety"
+	then
+		return 0
+	fi
+
+	abort_safety=$(cat "$dotest/abort-safety")
+	if test "z$(git rev-parse --verify -q HEAD)" = "z$abort_safety"
+	then
+		return 0
+	fi
+		gettextln "You seem to have moved HEAD since the last 'am' failure.
+Not rewinding to ORIG_HEAD" >&2
+	return 1
+}
+
 stop_here_user_resolve () {
     if [ -n "$resolvemsg" ]; then
 	    printf '%s\n' "$resolvemsg"
 	    stop_here $1
     fi
-    cmdline="git am"
-    if test '' != "$interactive"
-    then
-        cmdline="$cmdline -i"
-    fi
-    if test '' != "$threeway"
-    then
-        cmdline="$cmdline -3"
-    fi
-    echo "When you have resolved this problem run \"$cmdline --resolved\"."
-    echo "If you would prefer to skip this patch, instead run \"$cmdline --skip\"."
-    echo "To restore the original branch and stop patching run \"$cmdline --abort\"."
+    eval_gettextln "When you have resolved this problem run \"\$cmdline --resolved\".
+If you would prefer to skip this patch, instead run \"\$cmdline --skip\".
+To restore the original branch and stop patching run \"\$cmdline --abort\"."
 
     stop_here $1
 }
@@ -84,7 +118,7 @@
 
 cannot_fallback () {
 	echo "$1"
-	echo "Cannot fall back to three-way merge."
+	gettextln "Cannot fall back to three-way merge."
 	exit 1
 }
 
@@ -95,21 +129,30 @@
     mkdir "$dotest/patch-merge-tmp-dir"
 
     # First see if the patch records the index info that we can use.
-    git apply --build-fake-ancestor "$dotest/patch-merge-tmp-index" \
-	"$dotest/patch" &&
+    cmd="git apply $git_apply_opt --build-fake-ancestor" &&
+    cmd="$cmd "'"$dotest/patch-merge-tmp-index" "$dotest/patch"' &&
+    eval "$cmd" &&
     GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
     git write-tree >"$dotest/patch-merge-base+" ||
-    cannot_fallback "Repository lacks necessary blobs to fall back on 3-way merge."
+    cannot_fallback "$(gettext "Repository lacks necessary blobs to fall back on 3-way merge.")"
 
     say Using index info to reconstruct a base tree...
-    if GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
-	git apply --cached <"$dotest/patch"
+
+    cmd='GIT_INDEX_FILE="$dotest/patch-merge-tmp-index"'
+
+    if test -z "$GIT_QUIET"
+    then
+	eval "$cmd git diff-index --cached --diff-filter=AM --name-status HEAD"
+    fi
+
+    cmd="$cmd git apply --cached $git_apply_opt"' <"$dotest/patch"'
+    if eval "$cmd"
     then
 	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."
+	cannot_fallback "$(gettext "Did you hand edit your patch?
+It does not apply to blobs recorded in its index.")"
     fi
 
     test -f "$dotest/patch-merge-index" &&
@@ -117,7 +160,7 @@
     orig_tree=$(cat "$dotest/patch-merge-base") &&
     rm -fr "$dotest"/patch-merge-* || exit 1
 
-    say Falling back to patching base and 3-way merge...
+    say "$(gettext "Falling back to patching base and 3-way merge...")"
 
     # This is not so wrong.  Depending on which base we picked,
     # orig_tree may be wildly different from ours, but his_tree
@@ -129,10 +172,10 @@
     export GITHEAD_$his_tree
     if test -n "$GIT_QUIET"
     then
-	    export GIT_MERGE_VERBOSITY=0
+	    GIT_MERGE_VERBOSITY=0 && export GIT_MERGE_VERBOSITY
     fi
     git-merge-recursive $orig_tree -- HEAD $his_tree || {
-	    git rerere
+	    git rerere $allow_rerere_autoupdate
 	    echo Failed to merge in the changes.
 	    exit 1
     }
@@ -162,10 +205,15 @@
 		return 0
 	fi
 
-	# otherwise, check the first few lines of the first patch to try
-	# to detect its format
+	# otherwise, check the first few non-blank lines of the first
+	# patch to try to detect its format
 	{
-		read l1
+		# Start from first line containing non-whitespace
+		l1=
+		while test -z "$l1"
+		do
+			read l1 || break
+		done
 		read l2
 		read l3
 		case "$l1" in
@@ -191,19 +239,40 @@
 			esac
 			;;
 		esac
+		if test -z "$patch_format" &&
+			test -n "$l1" &&
+			test -n "$l2" &&
+			test -n "$l3"
+		then
+			# This begins with three non-empty lines.  Is this a
+			# piece of e-mail a-la RFC2822?  Grab all the headers,
+			# discarding the indented remainder of folded lines,
+			# and see if it looks like that they all begin with the
+			# header field names...
+			tr -d '\015' <"$1" |
+			sed -n -e '/^$/q' -e '/^[ 	]/d' -e p |
+			sane_egrep -v '^[!-9;-~]+:' >/dev/null ||
+			patch_format=mbox
+		fi
 	} < "$1" || clean_abort
 }
 
 split_patches () {
 	case "$patch_format" in
 	mbox)
-		git mailsplit -d"$prec" -o"$dotest" -b -- "$@" > "$dotest/last" ||
+		if test t = "$keepcr"
+		then
+		    keep_cr=--keep-cr
+		else
+		    keep_cr=
+		fi
+		git mailsplit -d"$prec" -o"$dotest" -b $keep_cr -- "$@" > "$dotest/last" ||
 		clean_abort
 		;;
 	stgit-series)
 		if test $# -ne 1
 		then
-			clean_abort "Only one StGIT patch series can be applied at once"
+			clean_abort "$(gettext "Only one StGIT patch series can be applied at once")"
 		fi
 		series_dir=`dirname "$1"`
 		series_file="$1"
@@ -237,7 +306,7 @@
 			perl -ne 'BEGIN { $subject = 0 }
 				if ($subject > 1) { print ; }
 				elsif (/^\s+$/) { next ; }
-				elsif (/^Author:/) { print s/Author/From/ ; }
+				elsif (/^Author:/) { s/Author/From/ ; print ;}
 				elsif (/^(From|Date)/) { print ; }
 				elsif ($subject) {
 					$subject = 2 ;
@@ -253,19 +322,64 @@
 		this=
 		msgnum=
 		;;
+	hg)
+		this=0
+		for hg in "$@"
+		do
+			this=$(( $this + 1 ))
+			msgnum=$(printf "%0${prec}d" $this)
+			# hg stores changeset metadata in #-commented lines preceding
+			# the commit message and diff(s). The only metadata we care about
+			# are the User and Date (Node ID and Parent are hashes which are
+			# only relevant to the hg repository and thus not useful to us)
+			# Since we cannot guarantee that the commit message is in
+			# git-friendly format, we put no Subject: line and just consume
+			# all of the message as the body
+			perl -M'POSIX qw(strftime)' -ne 'BEGIN { $subject = 0 }
+				if ($subject) { print ; }
+				elsif (/^\# User /) { s/\# User/From:/ ; print ; }
+				elsif (/^\# Date /) {
+					my ($hashsign, $str, $time, $tz) = split ;
+					$tz = sprintf "%+05d", (0-$tz)/36;
+					print "Date: " .
+					      strftime("%a, %d %b %Y %H:%M:%S ",
+						       localtime($time))
+					      . "$tz\n";
+				} elsif (/^\# /) { next ; }
+				else {
+					print "\n", $_ ;
+					$subject = 1;
+				}
+			' <"$hg" >"$dotest/$msgnum" || clean_abort
+		done
+		echo "$this" >"$dotest/last"
+		this=
+		msgnum=
+		;;
 	*)
-		clean_abort "Patch format $patch_format is not supported."
+		if test -n "$patch_format"
+		then
+			clean_abort "$(eval_gettext "Patch format \$patch_format is not supported.")"
+		else
+			clean_abort "$(gettext "Patch format detection failed.")"
+		fi
 		;;
 	esac
 }
 
 prec=4
 dotest="$GIT_DIR/rebase-apply"
-sign= utf8=t keep= skip= interactive= resolved= rebasing= abort=
-resolvemsg= resume=
+sign= utf8=t keep= keepcr= skip= interactive= resolved= rebasing= abort=
+resolvemsg= resume= scissors= no_inbody_headers=
 git_apply_opt=
 committer_date_is_author_date=
 ignore_date=
+allow_rerere_autoupdate=
+
+if test "$(git config --bool --get am.keepcr)" = true
+then
+    keepcr=t
+fi
 
 while test $# != 0
 do
@@ -273,7 +387,9 @@
 	-i|--interactive)
 		interactive=t ;;
 	-b|--binary)
-		: ;;
+		echo >&2 "The $1 option has been a no-op for long time, and"
+		echo >&2 "it will be removed. Please do not use it anymore."
+		;;
 	-3|--3way)
 		threeway=t ;;
 	-s|--signoff)
@@ -284,33 +400,45 @@
 		utf8= ;;
 	-k|--keep)
 		keep=t ;;
-	-r|--resolved)
+	--keep-non-patch)
+		keep=b ;;
+	-c|--scissors)
+		scissors=t ;;
+	--no-scissors)
+		scissors=f ;;
+	-r|--resolved|--continue)
 		resolved=t ;;
 	--skip)
 		skip=t ;;
 	--abort)
 		abort=t ;;
 	--rebasing)
-		rebasing=t threeway=t keep=t ;;
+		rebasing=t threeway=t ;;
 	-d|--dotest)
-		die "-d option is no longer supported.  Do not use."
+		die "$(gettext "-d option is no longer supported.  Do not use.")"
 		;;
 	--resolvemsg)
 		shift; resolvemsg=$1 ;;
-	--whitespace|--directory)
+	--whitespace|--directory|--exclude|--include)
 		git_apply_opt="$git_apply_opt $(sq "$1=$2")"; shift ;;
 	-C|-p)
 		git_apply_opt="$git_apply_opt $(sq "$1$2")"; shift ;;
 	--patch-format)
 		shift ; patch_format="$1" ;;
-	--reject)
+	--reject|--ignore-whitespace|--ignore-space-change)
 		git_apply_opt="$git_apply_opt $1" ;;
 	--committer-date-is-author-date)
 		committer_date_is_author_date=t ;;
 	--ignore-date)
 		ignore_date=t ;;
+	--rerere-autoupdate|--no-rerere-autoupdate)
+		allow_rerere_autoupdate="$1" ;;
 	-q|--quiet)
 		GIT_QUIET=t ;;
+	--keep-cr)
+		keepcr=t ;;
+	--no-keep-cr)
+		keepcr=f ;;
 	--)
 		shift; break ;;
 	*)
@@ -350,12 +478,12 @@
 		false
 		;;
 	esac ||
-	die "previous rebase directory $dotest still exists but mbox given."
+	die "$(eval_gettext "previous rebase directory \$dotest still exists but mbox given.")"
 	resume=yes
 
 	case "$skip,$abort" in
 	t,t)
-		die "Please make up your mind. --skip or --abort?"
+		die "$(gettext "Please make up your mind. --skip or --abort?")"
 		;;
 	t,)
 		git rerere clear
@@ -370,10 +498,11 @@
 			exec git rebase --abort
 		fi
 		git rerere clear
-		test -f "$dotest/dirtyindex" || {
+		if safe_to_abort
+		then
 			git read-tree --reset -u HEAD ORIG_HEAD
 			git reset ORIG_HEAD
-		}
+		fi
 		rm -fr "$dotest"
 		exit ;;
 	esac
@@ -381,7 +510,7 @@
 else
 	# Make sure we are not given --skip, --resolved, nor --abort
 	test "$skip$resolved$abort" = "" ||
-		die "Resolve operation not in progress, we are not resuming."
+		die "$(gettext "Resolve operation not in progress, we are not resuming.")"
 
 	# Start afresh.
 	mkdir -p "$dotest" || exit
@@ -395,12 +524,12 @@
 				set x
 				first=
 			}
-			case "$arg" in
-			/*)
-				set "$@" "$arg" ;;
-			*)
-				set "$@" "$prefix$arg" ;;
-			esac
+			if is_absolute_path "$arg"
+			then
+				set "$@" "$arg"
+			else
+				set "$@" "$prefix$arg"
+			fi
 		done
 		shift
 	fi
@@ -409,14 +538,15 @@
 
 	split_patches "$@"
 
-	# -s, -u, -k, --whitespace, -3, -C, -q and -p flags are kept
-	# for the resuming session after a patch failure.
-	# -i can and must be given when resuming.
+	# -i can and must be given when resuming; everything
+	# else is kept
 	echo " $git_apply_opt" >"$dotest/apply-opt"
 	echo "$threeway" >"$dotest/threeway"
 	echo "$sign" >"$dotest/sign"
 	echo "$utf8" >"$dotest/utf8"
 	echo "$keep" >"$dotest/keep"
+	echo "$scissors" >"$dotest/scissors"
+	echo "$no_inbody_headers" >"$dotest/no_inbody_headers"
 	echo "$GIT_QUIET" >"$dotest/quiet"
 	echo 1 >"$dotest/next"
 	if test -n "$rebasing"
@@ -433,6 +563,8 @@
 	fi
 fi
 
+git update-index -q --refresh
+
 case "$resolved" in
 '')
 	case "$HAS_HEAD" in
@@ -444,19 +576,40 @@
 	if test "$files"
 	then
 		test -n "$HAS_HEAD" && : >"$dotest/dirtyindex"
-		die "Dirty index: cannot apply patches (dirty: $files)"
+		die "$(eval_gettext "Dirty index: cannot apply patches (dirty: \$files)")"
 	fi
 esac
 
+# Now, decide what command line options we will give to the git
+# commands we invoke, based on the result of parsing command line
+# options and previous invocation state stored in $dotest/ files.
+
 if test "$(cat "$dotest/utf8")" = t
 then
 	utf8=-u
 else
 	utf8=-n
 fi
-if test "$(cat "$dotest/keep")" = t
+keep=$(cat "$dotest/keep")
+case "$keep" in
+t)
+	keep=-k ;;
+b)
+	keep=-b ;;
+*)
+	keep= ;;
+esac
+case "$(cat "$dotest/scissors")" in
+t)
+	scissors=--scissors ;;
+f)
+	scissors=--no-scissors ;;
+esac
+if test "$(cat "$dotest/no_inbody_headers")" = t
 then
-	keep=-k
+	no_inbody_headers=--no-inbody-headers
+else
+	no_inbody_headers=
 fi
 if test "$(cat "$dotest/quiet")" = t
 then
@@ -485,13 +638,6 @@
 	resume=
 fi
 
-if test "$this" -gt "$last"
-then
-	say Nothing to do.
-	rm -fr "$dotest"
-	exit
-fi
-
 while test "$this" -le "$last"
 do
 	msgnum=`printf "%0${prec}d" $this`
@@ -512,43 +658,56 @@
 	# by the user, or the user can tell us to do so by --resolved flag.
 	case "$resume" in
 	'')
-		git mailinfo $keep $utf8 "$dotest/msg" "$dotest/patch" \
-			<"$dotest/$msgnum" >"$dotest/info" ||
-			stop_here $this
-
-		# skip pine's internal folder data
-		grep '^Author: Mail System Internal Data$' \
-			<"$dotest"/info >/dev/null &&
-			go_next && continue
-
-		test -s "$dotest/patch" || {
-			echo "Patch is empty.  Was it split wrong?"
-			stop_here $this
-		}
-		if test -f "$dotest/rebasing" &&
+		if test -f "$dotest/rebasing"
+		then
 			commit=$(sed -e 's/^From \([0-9a-f]*\) .*/\1/' \
 				-e q "$dotest/$msgnum") &&
-			test "$(git cat-file -t "$commit")" = commit
-		then
+			test "$(git cat-file -t "$commit")" = commit ||
+				stop_here $this
 			git cat-file commit "$commit" |
 			sed -e '1,/^$/d' >"$dotest/msg-clean"
+			echo "$commit" >"$dotest/original-commit"
+			get_author_ident_from_commit "$commit" >"$dotest/author-script"
+			git diff-tree --root --binary "$commit" >"$dotest/patch"
 		else
-			SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' "$dotest/info")"
-			case "$keep_subject" in -k)  SUBJECT="[PATCH] $SUBJECT" ;; esac
+			git mailinfo $keep $no_inbody_headers $scissors $utf8 "$dotest/msg" "$dotest/patch" \
+				<"$dotest/$msgnum" >"$dotest/info" ||
+				stop_here $this
 
-			(printf '%s\n\n' "$SUBJECT"; cat "$dotest/msg") |
-				git stripspace > "$dotest/msg-clean"
+			# skip pine's internal folder data
+			sane_grep '^Author: Mail System Internal Data$' \
+				<"$dotest"/info >/dev/null &&
+				go_next && continue
+
+			test -s "$dotest/patch" || {
+				eval_gettextln "Patch is empty.  Was it split wrong?
+If you would prefer to skip this patch, instead run \"\$cmdline --skip\".
+To restore the original branch and stop patching run \"\$cmdline --abort\"."
+				stop_here $this
+			}
+			rm -f "$dotest/original-commit" "$dotest/author-script"
+			{
+				sed -n '/^Subject/ s/Subject: //p' "$dotest/info"
+				echo
+				cat "$dotest/msg"
+			} |
+			git stripspace > "$dotest/msg-clean"
 		fi
 		;;
 	esac
 
-	GIT_AUTHOR_NAME="$(sed -n '/^Author/ s/Author: //p' "$dotest/info")"
-	GIT_AUTHOR_EMAIL="$(sed -n '/^Email/ s/Email: //p' "$dotest/info")"
-	GIT_AUTHOR_DATE="$(sed -n '/^Date/ s/Date: //p' "$dotest/info")"
+	if test -f "$dotest/author-script"
+	then
+		eval $(cat "$dotest/author-script")
+	else
+		GIT_AUTHOR_NAME="$(sed -n '/^Author/ s/Author: //p' "$dotest/info")"
+		GIT_AUTHOR_EMAIL="$(sed -n '/^Email/ s/Email: //p' "$dotest/info")"
+		GIT_AUTHOR_DATE="$(sed -n '/^Date/ s/Date: //p' "$dotest/info")"
+	fi
 
 	if test -z "$GIT_AUTHOR_EMAIL"
 	then
-		echo "Patch does not have a valid e-mail address."
+		gettextln "Patch does not have a valid e-mail address."
 		stop_here $this
 	fi
 
@@ -595,15 +754,18 @@
 	if test "$interactive" = t
 	then
 	    test -t 0 ||
-	    die "cannot be interactive without stdin connected to a terminal."
+	    die "$(gettext "cannot be interactive without stdin connected to a terminal.")"
 	    action=again
 	    while test "$action" = again
 	    do
-		echo "Commit Body is:"
+		gettextln "Commit Body is:"
 		echo "--------------------------"
 		cat "$dotest/final-commit"
 		echo "--------------------------"
-		printf "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
+		# TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
+		# in your translation. The program will only accept English
+		# input at this point.
+		gettext "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
 		read reply
 		case "$reply" in
 		[yY]*) action=yes ;;
@@ -612,14 +774,20 @@
 		[eE]*) git_editor "$dotest/final-commit"
 		       action=again ;;
 		[vV]*) action=again
-		       LESS=-S ${PAGER:-less} "$dotest/patch" ;;
+		       git_pager "$dotest/patch" ;;
 		*)     action=again ;;
 		esac
 	    done
 	else
 	    action=yes
 	fi
-	FIRSTLINE=$(sed 1q "$dotest/final-commit")
+
+	if test -f "$dotest/final-commit"
+	then
+		FIRSTLINE=$(sed 1q "$dotest/final-commit")
+	else
+		FIRSTLINE=""
+	fi
 
 	if test $action = skip
 	then
@@ -633,7 +801,7 @@
 		stop_here $this
 	fi
 
-	say "Applying: $FIRSTLINE"
+	say "$(eval_gettext "Applying: \$FIRSTLINE")"
 
 	case "$resolved" in
 	'')
@@ -654,14 +822,16 @@
 		# working tree.
 		resolved=
 		git diff-index --quiet --cached HEAD -- && {
-			echo "No changes - did you forget to use 'git add'?"
+			gettextln "No changes - did you forget to use 'git add'?
+If there is nothing left to stage, chances are that something else
+already introduced the same changes; you might want to skip this patch."
 			stop_here_user_resolve $this
 		}
 		unmerged=$(git ls-files -u)
 		if test -n "$unmerged"
 		then
-			echo "You still have unmerged paths in your index"
-			echo "did you forget to use 'git add'?"
+			gettextln "You still have unmerged paths in your index
+did you forget to use 'git add'?"
 			stop_here_user_resolve $this
 		fi
 		apply_status=0
@@ -669,14 +839,14 @@
 		;;
 	esac
 
-	if test $apply_status = 1 && test "$threeway" = t
+	if test $apply_status != 0 && test "$threeway" = t
 	then
 		if (fall_back_3way)
 		then
 		    # Applying the patch to an earlier tree and merging the
 		    # result may have produced the same tree as ours.
 		    git diff-index --quiet --cached HEAD -- && {
-			say No changes -- Patch already applied.
+			say "$(gettext "No changes -- Patch already applied.")"
 			go_next
 			continue
 		    }
@@ -686,7 +856,12 @@
 	fi
 	if test $apply_status != 0
 	then
-		printf 'Patch failed at %s %s\n' "$msgnum" "$FIRSTLINE"
+		eval_gettextln 'Patch failed at $msgnum $FIRSTLINE'
+		if test "$(git config --bool advice.amworkdir)" != false
+		then
+			eval_gettextln "The copy of the patch that failed is found in:
+   $dotest/patch"
+		fi
 		stop_here_user_resolve $this
 	fi
 
@@ -702,7 +877,7 @@
 			GIT_AUTHOR_DATE=
 		fi
 		parent=$(git rev-parse --verify -q HEAD) ||
-		say >&2 "applying to an empty history"
+		say >&2 "$(gettext "applying to an empty history")"
 
 		if test -n "$committer_date_is_author_date"
 		then
@@ -714,6 +889,10 @@
 	git update-ref -m "$GIT_REFLOG_ACTION: $FIRSTLINE" HEAD $commit $parent ||
 	stop_here $this
 
+	if test -f "$dotest/original-commit"; then
+		echo "$(cat "$dotest/original-commit") $commit" >> "$dotest/rewritten"
+	fi
+
 	if test -x "$GIT_DIR"/hooks/post-applypatch
 	then
 		"$GIT_DIR"/hooks/post-applypatch
@@ -722,6 +901,12 @@
 	go_next
 done
 
-git gc --auto
+if test -s "$dotest"/rewritten; then
+    git notes copy --for-rewrite=rebase < "$dotest"/rewritten
+    if test -x "$GIT_DIR"/hooks/post-rewrite; then
+	"$GIT_DIR"/hooks/post-rewrite rebase < "$dotest"/rewritten
+    fi
+fi
 
 rm -fr "$dotest"
+git gc --auto
diff --git a/git-archimport.perl b/git-archimport.perl
index 98f3ede..bc32f18 100755
--- a/git-archimport.perl
+++ b/git-archimport.perl
@@ -1,4 +1,4 @@
-#!/usr/bin/perl -w
+#!/usr/bin/perl
 #
 # This tool is copyright (c) 2005, Martin Langhoff.
 # It is released under the Gnu Public License, version 2.
@@ -54,6 +54,7 @@
 
 =cut
 
+use 5.008;
 use strict;
 use warnings;
 use Getopt::Std;
diff --git a/git-bisect.sh b/git-bisect.sh
index 8969553..99efbe8 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -2,43 +2,56 @@
 
 USAGE='[help|start|bad|good|skip|next|reset|visualize|replay|log|run]'
 LONG_USAGE='git bisect help
-        print this long help message.
-git bisect start [<bad> [<good>...]] [--] [<pathspec>...]
-        reset bisect state and start bisection.
+	print this long help message.
+git bisect start [--no-checkout] [<bad> [<good>...]] [--] [<pathspec>...]
+	reset bisect state and start bisection.
 git bisect bad [<rev>]
-        mark <rev> a known-bad revision.
+	mark <rev> a known-bad revision.
 git bisect good [<rev>...]
-        mark <rev>... known-good revisions.
+	mark <rev>... known-good revisions.
 git bisect skip [(<rev>|<range>)...]
-        mark <rev>... untestable revisions.
+	mark <rev>... untestable revisions.
 git bisect next
-        find next bisection to test and check it out.
-git bisect reset [<branch>]
-        finish bisection search and go back to branch.
+	find next bisection to test and check it out.
+git bisect reset [<commit>]
+	finish bisection search and go back to commit.
 git bisect visualize
-        show bisect status in gitk.
+	show bisect status in gitk.
 git bisect replay <logfile>
-        replay bisection log.
+	replay bisection log.
 git bisect log
-        show bisect log.
+	show bisect log.
 git bisect run <cmd>...
-        use <cmd>... to automatically bisect.
+	use <cmd>... to automatically bisect.
 
 Please use "git help bisect" to get the full man page.'
 
 OPTIONS_SPEC=
 . git-sh-setup
-require_work_tree
+. git-sh-i18n
 
 _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
 _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
 
+bisect_head()
+{
+	if test -f "$GIT_DIR/BISECT_HEAD"
+	then
+		echo BISECT_HEAD
+	else
+		echo HEAD
+	fi
+}
+
 bisect_autostart() {
 	test -s "$GIT_DIR/BISECT_START" || {
-		echo >&2 'You need to start by "git bisect start"'
+		gettextln "You need to start by \"git bisect start\"" >&2
 		if test -t 0
 		then
-			echo >&2 -n 'Do you want me to do it for you [Y/n]? '
+			# TRANSLATORS: Make sure to include [Y] and [n] in your
+			# translation. The program will only accept English input
+			# at this point.
+			gettext "Do you want me to do it for you [Y/n]? " >&2
 			read yesno
 			case "$yesno" in
 			[Nn]*)
@@ -53,11 +66,55 @@
 
 bisect_start() {
 	#
+	# Check for one bad and then some good revisions.
+	#
+	has_double_dash=0
+	for arg; do
+		case "$arg" in --) has_double_dash=1; break ;; esac
+	done
+	orig_args=$(git rev-parse --sq-quote "$@")
+	bad_seen=0
+	eval=''
+	if test "z$(git rev-parse --is-bare-repository)" != zfalse
+	then
+		mode=--no-checkout
+	else
+		mode=''
+	fi
+	while [ $# -gt 0 ]; do
+		arg="$1"
+		case "$arg" in
+		--)
+			shift
+			break
+		;;
+		--no-checkout)
+			mode=--no-checkout
+			shift ;;
+		--*)
+			die "$(eval_gettext "unrecognised option: '\$arg'")" ;;
+		*)
+			rev=$(git rev-parse -q --verify "$arg^{commit}") || {
+				test $has_double_dash -eq 1 &&
+				die "$(eval_gettext "'\$arg' does not appear to be a valid revision")"
+				break
+			}
+			case $bad_seen in
+			0) state='bad' ; bad_seen=1 ;;
+			*) state='good' ;;
+			esac
+			eval="$eval bisect_write '$state' '$rev' 'nolog' &&"
+			shift
+			;;
+		esac
+	done
+
+	#
 	# Verify HEAD.
 	#
 	head=$(GIT_DIR="$GIT_DIR" git symbolic-ref -q HEAD) ||
 	head=$(GIT_DIR="$GIT_DIR" git rev-parse --verify HEAD) ||
-	die "Bad HEAD - I need a HEAD"
+	die "$(gettext "Bad HEAD - I need a HEAD")"
 
 	#
 	# Check if we are bisecting.
@@ -67,7 +124,11 @@
 	then
 		# Reset to the rev from where we started.
 		start_head=$(cat "$GIT_DIR/BISECT_START")
-		git checkout "$start_head" -- || exit
+		if test "z$mode" != "z--no-checkout"
+		then
+			git checkout "$start_head" -- ||
+			die "$(eval_gettext "Checking out '\$start_head' failed. Try 'git bisect reset <validbranch>'.")"
+		fi
 	else
 		# Get rev from where we start.
 		case "$head" in
@@ -76,11 +137,11 @@
 			# cogito usage, and cogito users should understand
 			# it relates to cg-seek.
 			[ -s "$GIT_DIR/head-name" ] &&
-				die "won't bisect on seeked tree"
+				die "$(gettext "won't bisect on seeked tree")"
 			start_head="${head#refs/heads/}"
 			;;
 		*)
-			die "Bad HEAD - strange symbolic ref"
+			die "$(gettext "Bad HEAD - strange symbolic ref")"
 			;;
 		esac
 	fi
@@ -91,39 +152,6 @@
 	bisect_clean_state || exit
 
 	#
-	# Check for one bad and then some good revisions.
-	#
-	has_double_dash=0
-	for arg; do
-	    case "$arg" in --) has_double_dash=1; break ;; esac
-	done
-	orig_args=$(git rev-parse --sq-quote "$@")
-	bad_seen=0
-	eval=''
-	while [ $# -gt 0 ]; do
-	    arg="$1"
-	    case "$arg" in
-	    --)
-		shift
-		break
-		;;
-	    *)
-		rev=$(git rev-parse -q --verify "$arg^{commit}") || {
-		    test $has_double_dash -eq 1 &&
-		        die "'$arg' does not appear to be a valid revision"
-		    break
-		}
-		case $bad_seen in
-		0) state='bad' ; bad_seen=1 ;;
-		*) state='good' ;;
-		esac
-		eval="$eval bisect_write '$state' '$rev' 'nolog'; "
-		shift
-		;;
-	    esac
-	done
-
-	#
 	# Change state.
 	# In case of mistaken revs or checkout error, or signals received,
 	# "bisect_auto_next" below may exit or misbehave.
@@ -136,9 +164,12 @@
 	#
 	# Write new start state.
 	#
-	echo "$start_head" >"$GIT_DIR/BISECT_START" &&
+	echo "$start_head" >"$GIT_DIR/BISECT_START" && {
+		test "z$mode" != "z--no-checkout" ||
+		git update-ref --no-deref BISECT_HEAD "$start_head"
+	} &&
 	git rev-parse --sq-quote "$@" >"$GIT_DIR/BISECT_NAMES" &&
-	eval "$eval" &&
+	eval "$eval true" &&
 	echo "git bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" || exit
 	#
 	# Check if we can proceed to the next bisect state.
@@ -155,7 +186,7 @@
 	case "$state" in
 		bad)		tag="$state" ;;
 		good|skip)	tag="$state"-"$rev" ;;
-		*)		die "Bad bisect_write argument: $state" ;;
+		*)		die "$(eval_gettext "Bad bisect_write argument: \$state")" ;;
 	esac
 	git update-ref "refs/bisect/$tag" "$rev" || exit
 	echo "# $state: $(git show-branch $rev)" >>"$GIT_DIR/BISECT_LOG"
@@ -169,7 +200,8 @@
 
 check_expected_revs() {
 	for _rev in "$@"; do
-		if ! is_expected_rev "$_rev"; then
+		if ! is_expected_rev "$_rev"
+		then
 			rm -f "$GIT_DIR/BISECT_ANCESTORS_OK"
 			rm -f "$GIT_DIR/BISECT_EXPECTED_REV"
 			return
@@ -178,18 +210,18 @@
 }
 
 bisect_skip() {
-        all=''
+	all=''
 	for arg in "$@"
 	do
-	    case "$arg" in
-            *..*)
-                revs=$(git rev-list "$arg") || die "Bad rev input: $arg" ;;
-            *)
-                revs=$(git rev-parse --sq-quote "$arg") ;;
-	    esac
-            all="$all $revs"
-        done
-        eval bisect_state 'skip' $all
+		case "$arg" in
+		*..*)
+			revs=$(git rev-list "$arg") || die "$(eval_gettext "Bad rev input: \$arg")" ;;
+		*)
+			revs=$(git rev-parse --sq-quote "$arg") ;;
+		esac
+		all="$all $revs"
+	done
+	eval bisect_state 'skip' $all
 }
 
 bisect_state() {
@@ -197,10 +229,10 @@
 	state=$1
 	case "$#,$state" in
 	0,*)
-		die "Please call 'bisect_state' with at least one argument." ;;
+		die "$(gettext "Please call 'bisect_state' with at least one argument.")" ;;
 	1,bad|1,good|1,skip)
-		rev=$(git rev-parse --verify HEAD) ||
-			die "Bad rev input: HEAD"
+		rev=$(git rev-parse --verify $(bisect_head)) ||
+			die "$(gettext "Bad rev input: $(bisect_head)")"
 		bisect_write "$state" "$rev"
 		check_expected_revs "$rev" ;;
 	2,bad|*,good|*,skip)
@@ -209,13 +241,13 @@
 		for rev in "$@"
 		do
 			sha=$(git rev-parse --verify "$rev^{commit}") ||
-				die "Bad rev input: $rev"
+				die "$(eval_gettext "Bad rev input: \$rev")"
 			eval="$eval bisect_write '$state' '$sha'; "
 		done
 		eval "$eval"
 		check_expected_revs "$@" ;;
 	*,bad)
-		die "'git bisect bad' can take only one argument." ;;
+		die "$(gettext "'git bisect bad' can take only one argument.")" ;;
 	*)
 		usage ;;
 	esac
@@ -238,25 +270,29 @@
 	t,,good)
 		# have bad but not good.  we could bisect although
 		# this is less optimum.
-		echo >&2 'Warning: bisecting only with a bad commit.'
+		gettextln "Warning: bisecting only with a bad commit." >&2
 		if test -t 0
 		then
-			printf >&2 'Are you sure [Y/n]? '
+			# TRANSLATORS: Make sure to include [Y] and [n] in your
+			# translation. The program will only accept English input
+			# at this point.
+			gettext "Are you sure [Y/n]? " >&2
 			read yesno
 			case "$yesno" in [Nn]*) exit 1 ;; esac
 		fi
 		: bisect without good...
 		;;
 	*)
-		THEN=''
-		test -s "$GIT_DIR/BISECT_START" || {
-			echo >&2 'You need to start by "git bisect start".'
-			THEN='then '
-		}
-		echo >&2 'You '$THEN'need to give me at least one good' \
-			'and one bad revisions.'
-		echo >&2 '(You can use "git bisect bad" and' \
-			'"git bisect good" for that.)'
+
+		if test -s "$GIT_DIR/BISECT_START"
+		then
+			gettextln "You need to give me at least one good and one bad revisions.
+(You can use \"git bisect bad\" and \"git bisect good\" for that.)" >&2
+		else
+			gettextln "You need to start by \"git bisect start\".
+You then need to give me at least one good and one bad revisions.
+(You can use \"git bisect bad\" and \"git bisect good\" for that.)" >&2
+		fi
 		exit 1 ;;
 	esac
 }
@@ -271,10 +307,10 @@
 	bisect_next_check good
 
 	# Perform all bisection computation, display and checkout
-	git bisect--helper --next-all
+	git bisect--helper --next-all $(test -f "$GIT_DIR/BISECT_HEAD" && echo --no-checkout)
 	res=$?
 
-        # Check if we should exit because bisection is finished
+	# Check if we should exit because bisection is finished
 	test $res -eq 10 && exit 0
 
 	# Check for an error in the bisection process
@@ -288,10 +324,13 @@
 
 	if test $# = 0
 	then
-		case "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" in
-		'')	set git log ;;
-		set*)	set gitk ;;
-		esac
+		if test -n "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" &&
+			type gitk >/dev/null 2>&1
+		then
+			set gitk
+		else
+			set git log
+		fi
 	else
 		case "$1" in
 		git*|tig) ;;
@@ -300,24 +339,31 @@
 		esac
 	fi
 
-	not=$(git for-each-ref --format='%(refname)' "refs/bisect/good-*")
-	eval '"$@"' refs/bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES")
+	eval '"$@"' --bisect -- $(cat "$GIT_DIR/BISECT_NAMES")
 }
 
 bisect_reset() {
 	test -s "$GIT_DIR/BISECT_START" || {
-		echo "We are not bisecting."
+		gettextln "We are not bisecting."
 		return
 	}
 	case "$#" in
 	0) branch=$(cat "$GIT_DIR/BISECT_START") ;;
-	1) git show-ref --verify --quiet -- "refs/heads/$1" ||
-	       die "$1 does not seem to be a valid branch"
-	   branch="$1" ;;
+	1) git rev-parse --quiet --verify "$1^{commit}" > /dev/null || {
+			invalid="$1"
+			die "$(eval_gettext "'\$invalid' is not a valid commit")"
+		}
+		branch="$1" ;;
 	*)
-	    usage ;;
+		usage ;;
 	esac
-	git checkout "$branch" -- && bisect_clean_state
+
+	if ! test -f "$GIT_DIR/BISECT_HEAD" && ! git checkout "$branch" --
+	then
+		die "$(eval_gettext "Could not check out original HEAD '\$branch'.
+Try 'git bisect reset <commit>'.")"
+	fi
+	bisect_clean_state
 }
 
 bisect_clean_state() {
@@ -334,17 +380,21 @@
 	rm -f "$GIT_DIR/BISECT_RUN" &&
 	# Cleanup head-name if it got left by an old version of git-bisect
 	rm -f "$GIT_DIR/head-name" &&
-
+	git update-ref -d --no-deref BISECT_HEAD &&
+	# clean up BISECT_START last
 	rm -f "$GIT_DIR/BISECT_START"
 }
 
 bisect_replay () {
-	test -r "$1" || die "cannot read $1 for replaying"
+	file="$1"
+	test "$#" -eq 1 || die "$(gettext "No logfile given")"
+	test -r "$file" || die "$(eval_gettext "cannot read \$file for replaying")"
 	bisect_reset
 	while read git bisect command rev
 	do
 		test "$git $bisect" = "git bisect" -o "$git" = "git-bisect" || continue
-		if test "$git" = "git-bisect"; then
+		if test "$git" = "git-bisect"
+		then
 			rev="$command"
 			command="$bisect"
 		fi
@@ -355,94 +405,105 @@
 		good|bad|skip)
 			bisect_write "$command" "$rev" ;;
 		*)
-			die "?? what are you talking about?" ;;
+			die "$(gettext "?? what are you talking about?")" ;;
 		esac
-	done <"$1"
+	done <"$file"
 	bisect_auto_next
 }
 
 bisect_run () {
-    bisect_next_check fail
+	bisect_next_check fail
 
-    while true
-    do
-      echo "running $@"
-      "$@"
-      res=$?
+	while true
+	do
+		command="$@"
+		eval_gettextln "running \$command"
+		"$@"
+		res=$?
 
-      # Check for really bad run error.
-      if [ $res -lt 0 -o $res -ge 128 ]; then
-	  echo >&2 "bisect run failed:"
-	  echo >&2 "exit code $res from '$@' is < 0 or >= 128"
-	  exit $res
-      fi
+		# Check for really bad run error.
+		if [ $res -lt 0 -o $res -ge 128 ]
+		then
+			eval_gettextln "bisect run failed:
+exit code \$res from '\$command' is < 0 or >= 128" >&2
+			exit $res
+		fi
 
-      # Find current state depending on run success or failure.
-      # A special exit code of 125 means cannot test.
-      if [ $res -eq 125 ]; then
-	  state='skip'
-      elif [ $res -gt 0 ]; then
-	  state='bad'
-      else
-	  state='good'
-      fi
+		# Find current state depending on run success or failure.
+		# A special exit code of 125 means cannot test.
+		if [ $res -eq 125 ]
+		then
+			state='skip'
+		elif [ $res -gt 0 ]
+		then
+			state='bad'
+		else
+			state='good'
+		fi
 
-      # We have to use a subshell because "bisect_state" can exit.
-      ( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
-      res=$?
+		# We have to use a subshell because "bisect_state" can exit.
+		( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
+		res=$?
 
-      cat "$GIT_DIR/BISECT_RUN"
+		cat "$GIT_DIR/BISECT_RUN"
 
-      if grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
-		> /dev/null; then
-	  echo >&2 "bisect run cannot continue any more"
-	  exit $res
-      fi
+		if sane_grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
+			> /dev/null
+		then
+			gettextln "bisect run cannot continue any more" >&2
+			exit $res
+		fi
 
-      if [ $res -ne 0 ]; then
-	  echo >&2 "bisect run failed:"
-	  echo >&2 "'bisect_state $state' exited with error code $res"
-	  exit $res
-      fi
+		if [ $res -ne 0 ]
+		then
+			eval_gettextln "bisect run failed:
+'bisect_state \$state' exited with error code \$res" >&2
+			exit $res
+		fi
 
-      if grep "is first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
-	  echo "bisect run success"
-	  exit 0;
-      fi
+		if sane_grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null
+		then
+			gettextln "bisect run success"
+			exit 0;
+		fi
 
-    done
+	done
 }
 
+bisect_log () {
+	test -s "$GIT_DIR/BISECT_LOG" || die "$(gettext "We are not bisecting.")"
+	cat "$GIT_DIR/BISECT_LOG"
+}
 
 case "$#" in
 0)
-    usage ;;
+	usage ;;
 *)
-    cmd="$1"
-    shift
-    case "$cmd" in
-    help)
-        git bisect -h ;;
-    start)
-        bisect_start "$@" ;;
-    bad|good)
-        bisect_state "$cmd" "$@" ;;
-    skip)
-        bisect_skip "$@" ;;
-    next)
-        # Not sure we want "next" at the UI level anymore.
-        bisect_next "$@" ;;
-    visualize|view)
-	bisect_visualize "$@" ;;
-    reset)
-        bisect_reset "$@" ;;
-    replay)
-	bisect_replay "$@" ;;
-    log)
-	cat "$GIT_DIR/BISECT_LOG" ;;
-    run)
-        bisect_run "$@" ;;
-    *)
-        usage ;;
-    esac
+	cmd="$1"
+	shift
+	case "$cmd" in
+	help)
+		git bisect -h ;;
+	start)
+		bisect_start "$@" ;;
+	bad|good)
+		bisect_state "$cmd" "$@" ;;
+	skip)
+		bisect_skip "$@" ;;
+	next)
+		# Not sure we want "next" at the UI level anymore.
+		bisect_next "$@" ;;
+	visualize|view)
+		bisect_visualize "$@" ;;
+	reset)
+		bisect_reset "$@" ;;
+	replay)
+		bisect_replay "$@" ;;
+	log)
+		bisect_log ;;
+	run)
+		bisect_run "$@" ;;
+	*)
+		usage ;;
+	esac
 esac
diff --git a/git-compat-util.h b/git-compat-util.h
index 000859e..35b095e 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -28,6 +28,24 @@
 #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
 #define bitsizeof(x)  (CHAR_BIT * sizeof(x))
 
+#define maximum_signed_value_of_type(a) \
+    (INTMAX_MAX >> (bitsizeof(intmax_t) - bitsizeof(a)))
+
+#define maximum_unsigned_value_of_type(a) \
+    (UINTMAX_MAX >> (bitsizeof(uintmax_t) - bitsizeof(a)))
+
+/*
+ * Signed integer overflow is undefined in C, so here's a helper macro
+ * to detect if the sum of two integers will overflow.
+ *
+ * Requires: a >= 0, typeof(a) equals typeof(b)
+ */
+#define signed_add_overflows(a, b) \
+    ((b) > maximum_signed_value_of_type(a) - (a))
+
+#define unsigned_add_overflows(a, b) \
+    ((b) > maximum_unsigned_value_of_type(a) - (a))
+
 #ifdef __GNUC__
 #define TYPEOF(x) (__typeof__(x))
 #else
@@ -55,18 +73,23 @@
 # else
 # define _XOPEN_SOURCE 500
 # endif
-#elif !defined(__APPLE__) && !defined(__FreeBSD__)  && !defined(__USLC__) && !defined(_M_UNIX) && !defined(sgi)
+#elif !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__USLC__) && \
+      !defined(_M_UNIX) && !defined(__sgi) && !defined(__DragonFly__)
 #define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */
-#ifndef __sun__
 #define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */
 #endif
-#endif
 #define _ALL_SOURCE 1
 #define _GNU_SOURCE 1
 #define _BSD_SOURCE 1
 #define _NETBSD_SOURCE 1
 #define _SGI_SOURCE 1
 
+#ifdef WIN32 /* Both MinGW and MSVC */
+#define WIN32_LEAN_AND_MEAN  /* stops windows.h including winsock.h */
+#include <winsock2.h>
+#include <windows.h>
+#endif
+
 #include <unistd.h>
 #include <stdio.h>
 #include <sys/stat.h>
@@ -87,11 +110,23 @@
 #include <assert.h>
 #include <regex.h>
 #include <utime.h>
-#ifndef __MINGW32__
-#include <sys/wait.h>
+#include <syslog.h>
+#ifndef NO_SYS_POLL_H
 #include <sys/poll.h>
+#else
+#include <poll.h>
+#endif
+#if defined(__MINGW32__)
+/* pull in Windows compatibility stuff */
+#include "compat/mingw.h"
+#elif defined(_MSC_VER)
+#include "compat/msvc.h"
+#else
+#include <sys/wait.h>
+#include <sys/resource.h>
 #include <sys/socket.h>
 #include <sys/ioctl.h>
+#include <termios.h>
 #ifndef NO_SYS_SELECT_H
 #include <sys/select.h>
 #endif
@@ -100,7 +135,12 @@
 #include <arpa/inet.h>
 #include <netdb.h>
 #include <pwd.h>
+#include <sys/un.h>
+#ifndef NO_INTTYPES_H
 #include <inttypes.h>
+#else
+#include <stdint.h>
+#endif
 #if defined(__CYGWIN__)
 #undef _XOPEN_SOURCE
 #include <grp.h>
@@ -111,10 +151,16 @@
 #include <grp.h>
 #define _ALL_SOURCE 1
 #endif
-#else 	/* __MINGW32__ */
-/* pull in Windows compatibility stuff */
-#include "compat/mingw.h"
-#endif	/* __MINGW32__ */
+#endif
+
+/* used on Mac OS X */
+#ifdef PRECOMPOSE_UNICODE
+#include "compat/precompose_utf8.h"
+#else
+#define precompose_str(in,i_nfd2nfc)
+#define precompose_argv(c,v)
+#define probe_utf8_pathname_composition(a,b)
+#endif
 
 #ifndef NO_LIBGEN_H
 #include <libgen.h>
@@ -151,10 +197,21 @@
 #define PRIx32 "x"
 #endif
 
+#ifndef PRIo32
+#define PRIo32 "o"
+#endif
+
 #ifndef PATH_SEP
 #define PATH_SEP ':'
 #endif
 
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+#ifndef _PATH_DEFPATH
+#define _PATH_DEFPATH "/usr/local/bin:/usr/bin:/bin"
+#endif
+
 #ifndef STRIP_EXTENSION
 #define STRIP_EXTENSION ""
 #endif
@@ -167,10 +224,22 @@
 #define is_dir_sep(c) ((c) == '/')
 #endif
 
-#ifdef __GNUC__
+#ifndef find_last_dir_sep
+#define find_last_dir_sep(path) strrchr(path, '/')
+#endif
+
+#if defined(__HP_cc) && (__HP_cc >= 61000)
+#define NORETURN __attribute__((noreturn))
+#define NORETURN_PTR
+#elif defined(__GNUC__) && !defined(NO_NORETURN)
 #define NORETURN __attribute__((__noreturn__))
+#define NORETURN_PTR __attribute__((__noreturn__))
+#elif defined(_MSC_VER)
+#define NORETURN __declspec(noreturn)
+#define NORETURN_PTR
 #else
 #define NORETURN
+#define NORETURN_PTR
 #ifndef __attribute__
 #define __attribute__(x)
 #endif
@@ -179,16 +248,20 @@
 #include "compat/bswap.h"
 
 /* General helper functions */
-extern void usage(const char *err) NORETURN;
-extern void die(const char *err, ...) NORETURN __attribute__((format (printf, 1, 2)));
-extern void die_errno(const char *err, ...) NORETURN __attribute__((format (printf, 1, 2)));
+extern void vreportf(const char *prefix, const char *err, va_list params);
+extern void vwritef(int fd, const char *prefix, const char *err, va_list params);
+extern NORETURN void usage(const char *err);
+extern NORETURN void usagef(const char *err, ...) __attribute__((format (printf, 1, 2)));
+extern NORETURN void die(const char *err, ...) __attribute__((format (printf, 1, 2)));
+extern NORETURN void die_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
 extern int error(const char *err, ...) __attribute__((format (printf, 1, 2)));
 extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)));
 
-extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN);
+extern void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list params));
+extern void set_error_routine(void (*routine)(const char *err, va_list params));
 
 extern int prefixcmp(const char *str, const char *prefix);
-extern time_t tm_to_time_t(const struct tm *tm);
+extern int suffixcmp(const char *str, const char *suffix);
 
 static inline const char *skip_prefix(const char *str, const char *prefix)
 {
@@ -202,7 +275,6 @@
 #define PROT_READ 1
 #define PROT_WRITE 2
 #define MAP_PRIVATE 1
-#define MAP_FAILED ((void*)-1)
 #endif
 
 #define mmap git_mmap
@@ -231,6 +303,10 @@
 
 #endif /* NO_MMAP */
 
+#ifndef MAP_FAILED
+#define MAP_FAILED ((void *)-1)
+#endif
+
 #ifdef NO_ST_BLOCKS_IN_STRUCT_STAT
 #define on_disk_bytes(st) ((st).st_size)
 #else
@@ -284,6 +360,13 @@
 #ifdef NO_STRTOUMAX
 #define strtoumax gitstrtoumax
 extern uintmax_t gitstrtoumax(const char *, char **, int);
+#define strtoimax gitstrtoimax
+extern intmax_t gitstrtoimax(const char *, char **, int);
+#endif
+
+#ifdef NO_STRTOK_R
+#define strtok_r gitstrtok_r
+extern char *gitstrtok_r(char *s, const char *delim, char **save_ptr);
 #endif
 
 #ifdef NO_HSTRERROR
@@ -317,6 +400,7 @@
 #ifdef __GLIBC_PREREQ
 #if __GLIBC_PREREQ(2, 1)
 #define HAVE_STRCHRNUL
+#define HAVE_MEMPCPY
 #endif
 #endif
 
@@ -330,10 +414,30 @@
 }
 #endif
 
+#ifndef HAVE_MEMPCPY
+#define mempcpy gitmempcpy
+static inline void *gitmempcpy(void *dest, const void *src, size_t n)
+{
+	return (char *)memcpy(dest, src, n) + n;
+}
+#endif
+
+#ifdef NO_INET_PTON
+int inet_pton(int af, const char *src, void *dst);
+#endif
+
+#ifdef NO_INET_NTOP
+const char *inet_ntop(int af, const void *src, char *dst, size_t size);
+#endif
+
 extern void release_pack_memory(size_t, int);
 
+typedef void (*try_to_free_t)(size_t);
+extern try_to_free_t set_try_to_free_routine(try_to_free_t);
+
 extern char *xstrdup(const char *str);
 extern void *xmalloc(size_t size);
+extern void *xmallocz(size_t size);
 extern void *xmemdupz(const void *data, size_t len);
 extern char *xstrndup(const char *str, size_t len);
 extern void *xrealloc(void *ptr, size_t size);
@@ -344,11 +448,14 @@
 extern int xdup(int fd);
 extern FILE *xfdopen(int fd, const char *mode);
 extern int xmkstemp(char *template);
+extern int xmkstemp_mode(char *template, int mode);
 extern int odb_mkstemp(char *template, size_t limit, const char *pattern);
 extern int odb_pack_keep(char *name, size_t namesz, unsigned char *sha1);
 
 static inline size_t xsize_t(off_t len)
 {
+	if (len > (size_t) len)
+		die("Cannot handle files this big");
 	return (size_t)len;
 }
 
@@ -359,12 +466,17 @@
 	return len > extlen && !memcmp(filename + len - extlen, ext, extlen);
 }
 
+/* in ctype.c, for kwset users */
+extern const char tolower_trans_tbl[256];
+
 /* Sane ctype - no locale, and works with signed chars */
 #undef isascii
 #undef isspace
 #undef isdigit
 #undef isalpha
 #undef isalnum
+#undef islower
+#undef isupper
 #undef tolower
 #undef toupper
 extern unsigned char sane_ctype[256];
@@ -373,16 +485,20 @@
 #define GIT_ALPHA 0x04
 #define GIT_GLOB_SPECIAL 0x08
 #define GIT_REGEX_SPECIAL 0x10
+#define GIT_PATHSPEC_MAGIC 0x20
 #define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0)
 #define isascii(x) (((x) & ~0x7f) == 0)
 #define isspace(x) sane_istest(x,GIT_SPACE)
 #define isdigit(x) sane_istest(x,GIT_DIGIT)
 #define isalpha(x) sane_istest(x,GIT_ALPHA)
 #define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT)
+#define islower(x) sane_iscase(x, 1)
+#define isupper(x) sane_iscase(x, 0)
 #define is_glob_special(x) sane_istest(x,GIT_GLOB_SPECIAL)
 #define is_regex_special(x) sane_istest(x,GIT_GLOB_SPECIAL | GIT_REGEX_SPECIAL)
 #define tolower(x) sane_case((unsigned char)(x), 0x20)
 #define toupper(x) sane_case((unsigned char)(x), 0)
+#define is_pathspec_magic(x) sane_istest(x,GIT_PATHSPEC_MAGIC)
 
 static inline int sane_case(int x, int high)
 {
@@ -391,6 +507,17 @@
 	return x;
 }
 
+static inline int sane_iscase(int x, int is_lower)
+{
+	if (!sane_istest(x, GIT_ALPHA))
+		return 0;
+
+	if (is_lower)
+		return (x & 0x20) != 0;
+	else
+		return (x & 0x20) == 0;
+}
+
 static inline int strtoul_ui(char const *s, int base, unsigned int *result)
 {
 	unsigned long ul;
@@ -449,10 +576,35 @@
 #define fstat_is_reliable() 1
 #endif
 
+#ifndef va_copy
+/*
+ * Since an obvious implementation of va_list would be to make it a
+ * pointer into the stack frame, a simple assignment will work on
+ * many systems.  But let's try to be more portable.
+ */
+#ifdef __va_copy
+#define va_copy(dst, src) __va_copy(dst, src)
+#else
+#define va_copy(dst, src) ((dst) = (src))
+#endif
+#endif
+
 /*
  * Preserves errno, prints a message, but gives no warning for ENOENT.
  * Always returns the return value of unlink(2).
  */
 int unlink_or_warn(const char *path);
+/*
+ * Likewise for rmdir(2).
+ */
+int rmdir_or_warn(const char *path);
+/*
+ * Calls the correct function out of {unlink,rmdir}_or_warn based on
+ * the supplied file mode.
+ */
+int remove_or_warn(unsigned int mode, const char *path);
+
+/* Get the passwd entry for the UID of the current process. */
+struct passwd *xgetpwuid_self(void);
 
 #endif
diff --git a/git-cvsexportcommit.perl b/git-cvsexportcommit.perl
index 59b6722..e6bf252 100755
--- a/git-cvsexportcommit.perl
+++ b/git-cvsexportcommit.perl
@@ -1,6 +1,8 @@
-#!/usr/bin/perl -w
+#!/usr/bin/perl
 
+use 5.008;
 use strict;
+use warnings;
 use Getopt::Std;
 use File::Temp qw(tempdir);
 use Data::Dumper;
@@ -28,6 +30,13 @@
 		chomp($gd);
 		$ENV{GIT_DIR} = $gd;
 	}
+
+	# On MSYS, convert a Windows-style path to an MSYS-style path
+	# so that rel2abs() below works correctly.
+	if ($^O eq 'msys') {
+		$ENV{GIT_DIR} =~ s#^([[:alpha:]]):/#/$1/#;
+	}
+
 	# Make sure GIT_DIR is absolute
 	$ENV{GIT_DIR} = File::Spec->rel2abs($ENV{GIT_DIR});
 }
diff --git a/git-cvsimport.perl b/git-cvsimport.perl
index e439202..8d41610 100755
--- a/git-cvsimport.perl
+++ b/git-cvsimport.perl
@@ -1,4 +1,4 @@
-#!/usr/bin/perl -w
+#!/usr/bin/perl
 
 # This tool is copyright (c) 2005, Matthias Urlichs.
 # It is released under the Gnu Public License, version 2.
@@ -13,6 +13,7 @@
 # The head revision is on branch "origin" by default.
 # You can change that with the '-o' option.
 
+use 5.008;
 use strict;
 use warnings;
 use Getopt::Long;
@@ -29,7 +30,7 @@
 $SIG{'PIPE'}="IGNORE";
 $ENV{'TZ'}="UTC";
 
-our ($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,@opt_M,$opt_A,$opt_S,$opt_L, $opt_a, $opt_r);
+our ($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,@opt_M,$opt_A,$opt_S,$opt_L, $opt_a, $opt_r, $opt_R);
 my (%conv_author_name, %conv_author_email);
 
 sub usage(;$) {
@@ -40,7 +41,7 @@
        [-o branch-for-HEAD] [-h] [-v] [-d CVSROOT] [-A author-conv-file]
        [-p opts-for-cvsps] [-P file] [-C GIT_repository] [-z fuzz] [-i] [-k]
        [-u] [-s subst] [-a] [-m] [-M regex] [-S regex] [-L commitlimit]
-       [-r remote] [CVS_module]
+       [-r remote] [-R] [CVS_module]
 END
 	exit(1);
 }
@@ -89,28 +90,45 @@
 }
 
 # convert getopts specs for use by git config
+my %longmap = (
+	'A:' => 'authors-file',
+	'M:' => 'merge-regex',
+	'P:' => undef,
+	'R' => 'track-revisions',
+	'S:' => 'ignore-paths',
+);
+
 sub read_repo_config {
-    # Split the string between characters, unless there is a ':'
-    # So "abc:de" becomes ["a", "b", "c:", "d", "e"]
+	# Split the string between characters, unless there is a ':'
+	# So "abc:de" becomes ["a", "b", "c:", "d", "e"]
 	my @opts = split(/ *(?!:)/, shift);
 	foreach my $o (@opts) {
 		my $key = $o;
 		$key =~ s/://g;
 		my $arg = 'git config';
 		$arg .= ' --bool' if ($o !~ /:$/);
+		my $ckey = $key;
 
-        chomp(my $tmp = `$arg --get cvsimport.$key`);
+		if (exists $longmap{$o}) {
+			# An uppercase option like -R cannot be
+			# expressed in the configuration, as the
+			# variable names are downcased.
+			$ckey = $longmap{$o};
+			next if (! defined $ckey);
+			$ckey =~ s/-//g;
+		}
+		chomp(my $tmp = `$arg --get cvsimport.$ckey`);
 		if ($tmp && !($arg =~ /--bool/ && $tmp eq 'false')) {
-            no strict 'refs';
-            my $opt_name = "opt_" . $key;
-            if (!$$opt_name) {
-                $$opt_name = $tmp;
-            }
+			no strict 'refs';
+			my $opt_name = "opt_" . $key;
+			if (!$$opt_name) {
+				$$opt_name = $tmp;
+			}
 		}
 	}
 }
 
-my $opts = "haivmkuo:d:p:r:C:z:s:M:P:A:S:L:";
+my $opts = "haivmkuo:d:p:r:C:z:s:M:P:A:S:L:R";
 read_repo_config($opts);
 Getopt::Long::Configure( 'no_ignore_case', 'bundling' );
 
@@ -209,6 +227,31 @@
 	return $self;
 }
 
+sub find_password_entry {
+	my ($cvspass, @cvsroot) = @_;
+	my ($file, $delim) = @$cvspass;
+	my $pass;
+	local ($_);
+
+	if (open(my $fh, $file)) {
+		# :pserver:cvs@mea.tmt.tele.fi:/cvsroot/zmailer Ah<Z
+		CVSPASSFILE:
+		while (<$fh>) {
+			chomp;
+			s/^\/\d+\s+//;
+			my ($w, $p) = split($delim,$_,2);
+			for my $cvsroot (@cvsroot) {
+				if ($w eq $cvsroot) {
+					$pass = $p;
+					last CVSPASSFILE;
+				}
+			}
+		}
+		close($fh);
+	}
+	return $pass;
+}
+
 sub conn {
 	my $self = shift;
 	my $repo = $self->{'fullrep'};
@@ -238,21 +281,27 @@
 		}
 		my $rr = ":pserver:$user\@$serv:$port$repo";
 
-		unless ($pass) {
-			open(H,$ENV{'HOME'}."/.cvspass") and do {
-				# :pserver:cvs@mea.tmt.tele.fi:/cvsroot/zmailer Ah<Z
-				while (<H>) {
-					chomp;
-					s/^\/\d+\s+//;
-					my ($w,$p) = split(/\s/,$_,2);
-					if ($w eq $rr or $w eq $rr2) {
-						$pass = $p;
-						last;
-					}
+		if ($pass) {
+			$pass = $self->_scramble($pass);
+		} else {
+			my @cvspass = ([$ENV{'HOME'}."/.cvspass", qr/\s/],
+				       [$ENV{'HOME'}."/.cvs/cvspass", qr/=/]);
+			my @loc = ();
+			foreach my $cvspass (@cvspass) {
+				my $p = find_password_entry($cvspass, $rr, $rr2);
+				if ($p) {
+					push @loc, $cvspass->[0];
+					$pass = $p;
 				}
-			};
+			}
+
+			if (1 < @loc) {
+				die("Multiple cvs password files have ".
+				    "entries for CVSROOT $opt_d: @loc");
+			} elsif (!$pass) {
+				$pass = "A";
+			}
 		}
-		$pass="A" unless $pass;
 
 		my ($s, $rep);
 		if ($proxyhost) {
@@ -346,7 +395,9 @@
 	$self->{'socketo'}->write("valid-requests\n");
 	$self->{'socketo'}->flush();
 
-	chomp(my $rep=$self->readline());
+	my $rep=$self->readline();
+	die "Failed to read from server" unless defined $rep;
+	chomp($rep);
 	if ($rep !~ s/^Valid-requests\s*//) {
 		$rep="<unknown>" unless $rep;
 		die "Expected Valid-requests from server, but got: $rep\n";
@@ -484,6 +535,42 @@
 	return $res;
 }
 
+sub _scramble {
+	my ($self, $pass) = @_;
+	my $scrambled = "A";
+
+	return $scrambled unless $pass;
+
+	my $pass_len = length($pass);
+	my @pass_arr = split("", $pass);
+	my $i;
+
+	# from cvs/src/scramble.c
+	my @shifts = (
+		  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
+		 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+		114,120, 53, 79, 96,109, 72,108, 70, 64, 76, 67,116, 74, 68, 87,
+		111, 52, 75,119, 49, 34, 82, 81, 95, 65,112, 86,118,110,122,105,
+		 41, 57, 83, 43, 46,102, 40, 89, 38,103, 45, 50, 42,123, 91, 35,
+		125, 55, 54, 66,124,126, 59, 47, 92, 71,115, 78, 88,107,106, 56,
+		 36,121,117,104,101,100, 69, 73, 99, 63, 94, 93, 39, 37, 61, 48,
+		 58,113, 32, 90, 44, 98, 60, 51, 33, 97, 62, 77, 84, 80, 85,223,
+		225,216,187,166,229,189,222,188,141,249,148,200,184,136,248,190,
+		199,170,181,204,138,232,218,183,255,234,220,247,213,203,226,193,
+		174,172,228,252,217,201,131,230,197,211,145,238,161,179,160,212,
+		207,221,254,173,202,146,224,151,140,196,205,130,135,133,143,246,
+		192,159,244,239,185,168,215,144,139,165,180,157,147,186,214,176,
+		227,231,219,169,175,156,206,198,129,164,150,210,154,177,134,127,
+		182,128,158,208,162,132,167,209,149,241,153,251,237,236,171,195,
+		243,233,253,240,194,250,191,155,142,137,245,235,163,242,178,152
+	);
+
+	for ($i = 0; $i < $pass_len; $i++) {
+		$scrambled .= pack("C", $shifts[ord($pass_arr[$i])]);
+	}
+
+	return $scrambled;
+}
 
 package main;
 
@@ -541,10 +628,21 @@
 	return $r;
 }
 
+my $user_filename_prepend = '';
+sub munge_user_filename {
+	my $name = shift;
+	return File::Spec->file_name_is_absolute($name) ?
+		$name :
+		$user_filename_prepend . $name;
+}
+
 -d $git_tree
 	or mkdir($git_tree,0777)
 	or die "Could not create $git_tree: $!";
-chdir($git_tree);
+if ($git_tree ne '.') {
+	$user_filename_prepend = getwd() . '/';
+	chdir($git_tree);
+}
 
 my $last_branch = "";
 my $orig_branch = "";
@@ -560,16 +658,16 @@
 my %index; # holds filenames of one index per branch
 
 unless (-d $git_dir) {
-	system("git-init");
+	system(qw(git init));
 	die "Cannot init the GIT db at $git_tree: $?\n" if $?;
-	system("git-read-tree");
+	system(qw(git read-tree --empty));
 	die "Cannot init an empty tree: $?\n" if $?;
 
 	$last_branch = $opt_o;
 	$orig_branch = "";
 } else {
-	open(F, "git-symbolic-ref HEAD |") or
-		die "Cannot run git-symbolic-ref: $!\n";
+	open(F, "-|", qw(git symbolic-ref HEAD)) or
+		die "Cannot run git symbolic-ref: $!\n";
 	chomp ($last_branch = <F>);
 	$last_branch = basename($last_branch);
 	close(F);
@@ -578,12 +676,12 @@
 		$last_branch = "master";
 	}
 	$orig_branch = $last_branch;
-	$tip_at_start = `git-rev-parse --verify HEAD`;
+	$tip_at_start = `git rev-parse --verify HEAD`;
 
 	# Get the last import timestamps
 	my $fmt = '($ref, $author) = (%(refname), %(author));';
-	open(H, "git-for-each-ref --perl --format='$fmt' $remote |") or
-		die "Cannot run git-for-each-ref: $!\n";
+	my @cmd = ('git', 'for-each-ref', '--perl', "--format=$fmt", $remote);
+	open(H, "-|", @cmd) or die "Cannot run git for-each-ref: $!\n";
 	while (defined(my $entry = <H>)) {
 		my ($ref, $author);
 		eval($entry) || die "cannot eval refs list: $@";
@@ -606,10 +704,15 @@
 -f "$git_dir/cvs-authors" and
   read_author_info("$git_dir/cvs-authors");
 if ($opt_A) {
-	read_author_info($opt_A);
+	read_author_info(munge_user_filename($opt_A));
 	write_author_info("$git_dir/cvs-authors");
 }
 
+# open .git/cvs-revisions, if requested
+open my $revision_map, '>>', "$git_dir/cvs-revisions"
+    or die "Can't open $git_dir/cvs-revisions for appending: $!\n"
+	if defined $opt_R;
+
 
 #
 # run cvsps into a file unless we are getting
@@ -638,10 +741,10 @@
 	    print $cvspsfh $_;
 	}
 	close CVSPS;
-	$? == 0 or die "git-cvsimport: fatal: cvsps reported error\n";
+	$? == 0 or die "git cvsimport: fatal: cvsps reported error\n";
 	close $cvspsfh;
 } else {
-	$cvspsfile = $opt_P;
+	$cvspsfile = munge_user_filename($opt_P);
 }
 
 open(CVS, "<$cvspsfile") or die $!;
@@ -667,33 +770,33 @@
 sub update_index (\@\@) {
 	my $old = shift;
 	my $new = shift;
-	open(my $fh, '|-', qw(git-update-index -z --index-info))
-		or die "unable to open git-update-index: $!";
+	open(my $fh, '|-', qw(git update-index -z --index-info))
+		or die "unable to open git update-index: $!";
 	print $fh
 		(map { "0 0000000000000000000000000000000000000000\t$_\0" }
 			@$old),
 		(map { '100' . sprintf('%o', $_->[0]) . " $_->[1]\t$_->[2]\0" }
 			@$new)
-		or die "unable to write to git-update-index: $!";
+		or die "unable to write to git update-index: $!";
 	close $fh
-		or die "unable to write to git-update-index: $!";
-	$? and die "git-update-index reported error: $?";
+		or die "unable to write to git update-index: $!";
+	$? and die "git update-index reported error: $?";
 }
 
 sub write_tree () {
-	open(my $fh, '-|', qw(git-write-tree))
-		or die "unable to open git-write-tree: $!";
+	open(my $fh, '-|', qw(git write-tree))
+		or die "unable to open git write-tree: $!";
 	chomp(my $tree = <$fh>);
 	is_sha1($tree)
 		or die "Cannot get tree id ($tree): $!";
 	close($fh)
-		or die "Error running git-write-tree: $?\n";
+		or die "Error running git write-tree: $?\n";
 	print "Tree ID $tree\n" if $opt_v;
 	return $tree;
 }
 
 my ($patchset,$date,$author_name,$author_email,$branch,$ancestor,$tag,$logmsg);
-my (@old,@new,@skipped,%ignorebranch);
+my (@old,@new,@skipped,%ignorebranch,@commit_revisions);
 
 # commits that cvsps cannot place anywhere...
 $ignorebranch{'#CVSPS_NO_BRANCH'} = 1;
@@ -702,7 +805,7 @@
 	if ($branch eq $opt_o && !$index{branch} &&
 		!get_headref("$remote/$branch")) {
 	    # looks like an initial commit
-	    # use the index primed by git-init
+	    # use the index primed by git init
 	    $ENV{GIT_INDEX_FILE} = "$git_dir/index";
 	    $index{$branch} = "$git_dir/index";
 	} else {
@@ -712,9 +815,9 @@
 		$index{$branch} = tmpnam();
 		$ENV{GIT_INDEX_FILE} = $index{$branch};
 		if ($ancestor) {
-		    system("git-read-tree", "$remote/$ancestor");
+		    system("git", "read-tree", "$remote/$ancestor");
 		} else {
-		    system("git-read-tree", "$remote/$branch");
+		    system("git", "read-tree", "$remote/$branch");
 		}
 		die "read-tree failed: $?\n" if $?;
 	    }
@@ -749,7 +852,7 @@
 	$ENV{GIT_COMMITTER_EMAIL} = $author_email;
 	$ENV{GIT_COMMITTER_DATE} = $commit_date;
 	my $pid = open2(my $commit_read, my $commit_write,
-		'git-commit-tree', $tree, @commit_args);
+		'git', 'commit-tree', $tree, @commit_args);
 
 	# compatibility with git2cvs
 	substr($logmsg,32767) = "" if length($logmsg) > 32767;
@@ -762,7 +865,7 @@
 	}
 
 	print($commit_write "$logmsg\n") && close($commit_write)
-		or die "Error writing to git-commit-tree: $!\n";
+		or die "Error writing to git commit-tree: $!\n";
 
 	print "Committed patch $patchset ($branch $commit_date)\n" if $opt_v;
 	chomp(my $cid = <$commit_read>);
@@ -771,11 +874,16 @@
 	close($commit_read);
 
 	waitpid($pid,0);
-	die "Error running git-commit-tree: $?\n" if $?;
+	die "Error running git commit-tree: $?\n" if $?;
 
-	system('git-update-ref', "$remote/$branch", $cid) == 0
+	system('git' , 'update-ref', "$remote/$branch", $cid) == 0
 		or die "Cannot write branch $branch for update: $!\n";
 
+	if ($revision_map) {
+		print $revision_map "@$_ $cid\n" for @commit_revisions;
+	}
+	@commit_revisions = ();
+
 	if ($tag) {
 	        my ($xtag) = $tag;
 		$xtag =~ s/\s+\*\*.*$//; # Remove stuff like ** INVALID ** and ** FUNKY **
@@ -783,7 +891,7 @@
 		$xtag =~ s/[\/]/$opt_s/g;
 		$xtag =~ s/\[//g;
 
-		system('git-tag', '-f', $xtag, $cid) == 0
+		system('git' , 'tag', '-f', $xtag, $cid) == 0
 			or die "Cannot create tag $xtag: $!\n";
 
 		print "Created tag '$xtag' on '$branch'\n" if $opt_v;
@@ -910,6 +1018,7 @@
 		    push(@skipped, $fn);
 		    next;
 		}
+		push @commit_revisions, [$fn, $rev];
 		print "Fetching $fn   v $rev\n" if $opt_v;
 		my ($tmpname, $size) = $cvs->file($fn,$rev);
 		if ($size == -1) {
@@ -920,7 +1029,7 @@
 			my $pid = open(my $F, '-|');
 			die $! unless defined $pid;
 			if (!$pid) {
-			    exec("git-hash-object", "-w", $tmpname)
+			    exec("git", "hash-object", "-w", $tmpname)
 				or die "Cannot create object: $!\n";
 			}
 			my $sha = <$F>;
@@ -932,7 +1041,9 @@
 		unlink($tmpname);
 	} elsif ($state == 9 and /^\s+(.+?):\d+(?:\.\d+)+->(\d+(?:\.\d+)+)\(DEAD\)\s*$/) {
 		my $fn = $1;
+		my $rev = $2;
 		$fn =~ s#^/+##;
+		push @commit_revisions, [$fn, $rev];
 		push(@old,$fn);
 		print "Delete $fn\n" if $opt_v;
 	} elsif ($state == 9 and /^\s*$/) {
@@ -944,7 +1055,7 @@
 		}
 		commit();
 		if (($commitcount & 1023) == 0) {
-			system("git repack -a -d");
+			system(qw(git repack -a -d));
 		}
 		$state = 1;
 	} elsif ($state == 11 and /^-+$/) {
@@ -964,11 +1075,11 @@
 # The heuristic of repacking every 1024 commits can leave a
 # lot of unpacked data.  If there is more than 1MB worth of
 # not-packed objects, repack once more.
-my $line = `git-count-objects`;
+my $line = `git count-objects`;
 if ($line =~ /^(\d+) objects, (\d+) kilobytes$/) {
   my ($n_objects, $kb) = ($1, $2);
   1024 < $kb
-    and system("git repack -a -d");
+    and system(qw(git repack -a -d));
 }
 
 foreach my $git_index (values %index) {
@@ -989,28 +1100,28 @@
 	if ($opt_i) {
 		exit 0;
 	}
-	my $tip_at_end = `git-rev-parse --verify HEAD`;
+	my $tip_at_end = `git rev-parse --verify HEAD`;
 	if ($tip_at_start ne $tip_at_end) {
 		for ($tip_at_start, $tip_at_end) { chomp; }
 		print "Fetched into the current branch.\n" if $opt_v;
-		system(qw(git-read-tree -u -m),
+		system(qw(git read-tree -u -m),
 		       $tip_at_start, $tip_at_end);
 		die "Fast-forward update failed: $?\n" if $?;
 	}
 	else {
-		system(qw(git-merge cvsimport HEAD), "$remote/$opt_o");
+		system(qw(git merge cvsimport HEAD), "$remote/$opt_o");
 		die "Could not merge $opt_o into the current branch.\n" if $?;
 	}
 } else {
 	$orig_branch = "master";
 	print "DONE; creating $orig_branch branch\n" if $opt_v;
-	system("git-update-ref", "refs/heads/master", "$remote/$opt_o")
+	system("git", "update-ref", "refs/heads/master", "$remote/$opt_o")
 		unless defined get_headref('refs/heads/master');
-	system("git-symbolic-ref", "$remote/HEAD", "$remote/$opt_o")
+	system("git", "symbolic-ref", "$remote/HEAD", "$remote/$opt_o")
 		if ($opt_r && $opt_o ne 'HEAD');
-	system('git-update-ref', 'HEAD', "$orig_branch");
+	system('git', 'update-ref', 'HEAD', "$orig_branch");
 	unless ($opt_i) {
-		system('git checkout -f');
+		system(qw(git checkout -f));
 		die "checkout failed: $?\n" if $?;
 	}
 }
diff --git a/git-cvsserver.perl b/git-cvsserver.perl
index ab6cea3..b8eddab 100755
--- a/git-cvsserver.perl
+++ b/git-cvsserver.perl
@@ -8,13 +8,14 @@
 #### Copyright The Open University UK - 2006.
 ####
 #### Authors: Martyn Smith    <martyn@catalyst.net.nz>
-####          Martin Langhoff <martin@catalyst.net.nz>
+####          Martin Langhoff <martin@laptop.org>
 ####
 ####
 #### Released under the GNU Public License, version 2.
 ####
 ####
 
+use 5.008;
 use strict;
 use warnings;
 use bytes;
@@ -104,16 +105,18 @@
 my $usage =
     "Usage: git cvsserver [options] [pserver|server] [<directory> ...]\n".
     "    --base-path <path>  : Prepend to requested CVSROOT\n".
+    "                          Can be read from GIT_CVSSERVER_BASE_PATH\n".
     "    --strict-paths      : Don't allow recursing into subdirectories\n".
     "    --export-all        : Don't check for gitcvs.enabled in config\n".
     "    --version, -V       : Print version information and exit\n".
-    "    --help, -h, -H      : Print usage information and exit\n".
+    "    -h, -H              : Print usage information and exit\n".
     "\n".
     "<directory> ... is a list of allowed directories. If no directories\n".
     "are given, all are allowed. This is an additional restriction, gitcvs\n".
-    "access still needs to be enabled by the gitcvs.enabled config option.\n";
+    "access still needs to be enabled by the gitcvs.enabled config option.\n".
+    "Alternately, one directory may be specified in GIT_CVSSERVER_ROOT.\n";
 
-my @opts = ( 'help|h|H', 'version|V',
+my @opts = ( 'h|H', 'version|V',
 	     'base-path=s', 'strict-paths', 'export-all' );
 GetOptions( $state, @opts )
     or die $usage;
@@ -148,6 +151,24 @@
     die "--export-all can only be used together with an explicit whitelist\n";
 }
 
+# Environment handling for running under git-shell
+if (exists $ENV{GIT_CVSSERVER_BASE_PATH}) {
+    if ($state->{'base-path'}) {
+	die "Cannot specify base path both ways.\n";
+    }
+    my $base_path = $ENV{GIT_CVSSERVER_BASE_PATH};
+    $state->{'base-path'} = $base_path;
+    $log->debug("Picked up base path '$base_path' from environment.\n");
+}
+if (exists $ENV{GIT_CVSSERVER_ROOT}) {
+    if (@{$state->{allowed_roots}}) {
+	die "Cannot specify roots both ways: @ARGV\n";
+    }
+    my $allowed_root = $ENV{GIT_CVSSERVER_ROOT};
+    $state->{allowed_roots} = [ $allowed_root ];
+    $log->debug("Picked up allowed root '$allowed_root' from environment.\n");
+}
+
 # if we are called with a pserver argument,
 # deal with the authentication cat before entering the
 # main loop
@@ -163,12 +184,58 @@
        exit 1;
     }
     $line = <STDIN>; chomp $line;
-    unless ($line eq 'anonymous') {
-       print "E Only anonymous user allowed via pserver\n";
-       print "I HATE YOU\n";
-       exit 1;
+    my $user = $line;
+    $line = <STDIN>; chomp $line;
+    my $password = $line;
+
+    if ($user eq 'anonymous') {
+        # "A" will be 1 byte, use length instead in case the
+        # encryption method ever changes (yeah, right!)
+        if (length($password) > 1 ) {
+            print "E Don't supply a password for the `anonymous' user\n";
+            print "I HATE YOU\n";
+            exit 1;
+        }
+
+        # Fall through to LOVE
+    } else {
+        # Trying to authenticate a user
+        if (not exists $cfg->{gitcvs}->{authdb}) {
+            print "E the repo config file needs a [gitcvs] section with an 'authdb' parameter set to the filename of the authentication database\n";
+            print "I HATE YOU\n";
+            exit 1;
+        }
+
+        my $authdb = $cfg->{gitcvs}->{authdb};
+
+        unless (-e $authdb) {
+            print "E The authentication database specified in [gitcvs.authdb] does not exist\n";
+            print "I HATE YOU\n";
+            exit 1;
+        }
+
+        my $auth_ok;
+        open my $passwd, "<", $authdb or die $!;
+        while (<$passwd>) {
+            if (m{^\Q$user\E:(.*)}) {
+                if (crypt($user, descramble($password)) eq $1) {
+                    $auth_ok = 1;
+                }
+            };
+        }
+        close $passwd;
+
+        unless ($auth_ok) {
+            print "I HATE YOU\n";
+            exit 1;
+        }
+
+        # Fall through to LOVE
     }
-    $line = <STDIN>; chomp $line;    # validate the password?
+
+    # For checking whether the user is anonymous on commit
+    $state->{user} = $user;
+
     $line = <STDIN>; chomp $line;
     unless ($line eq "END $request REQUEST") {
        die "E Do not understand $line -- expecting END $request REQUEST\n";
@@ -285,7 +352,7 @@
        return 0;
     }
 
-    my @gitvars = `git-config -l`;
+    my @gitvars = `git config -l`;
     if ($?) {
        print "E problems executing git-config on the server -- this is not a git repository or the PATH is not set correctly.\n";
         print "E \n";
@@ -388,7 +455,7 @@
     $state->{localdir} = $data;
     $state->{repository} = $repository;
     $state->{path} = $repository;
-    $state->{path} =~ s/^$state->{CVSROOT}\///;
+    $state->{path} =~ s/^\Q$state->{CVSROOT}\E\///;
     $state->{module} = $1 if ($state->{path} =~ s/^(.*?)(\/|$)//);
     $state->{path} .= "/" if ( $state->{path} =~ /\S/ );
 
@@ -702,7 +769,7 @@
     # Save the file data in $state
     $state->{entries}{$state->{directory}.$data}{modified_filename} = $filename;
     $state->{entries}{$state->{directory}.$data}{modified_mode} = $mode;
-    $state->{entries}{$state->{directory}.$data}{modified_hash} = `git-hash-object $filename`;
+    $state->{entries}{$state->{directory}.$data}{modified_hash} = `git hash-object $filename`;
     $state->{entries}{$state->{directory}.$data}{modified_hash} =~ s/\s.*$//s;
 
     #$log->debug("req_Modified : file=$data mode=$mode size=$size");
@@ -981,6 +1048,8 @@
 
     #$log->debug("update state : " . Dumper($state));
 
+    my $last_dirname = "///";
+
     # foreach file specified on the command line ...
     foreach my $filename ( @{$state->{args}} )
     {
@@ -988,6 +1057,20 @@
 
         $log->debug("Processing file $filename");
 
+        unless ( $state->{globaloptions}{-Q} || $state->{globaloptions}{-q} )
+        {
+            my $cur_dirname = dirname($filename);
+            if ( $cur_dirname ne $last_dirname )
+            {
+                $last_dirname = $cur_dirname;
+                if ( $cur_dirname eq "" )
+                {
+                    $cur_dirname = ".";
+                }
+                print "E cvs update: Updating $cur_dirname\n";
+            }
+        }
+
         # if we have a -C we should pretend we never saw modified stuff
         if ( exists ( $state->{opt}{C} ) )
         {
@@ -1235,9 +1318,9 @@
 
     $log->info("req_ci : " . ( defined($data) ? $data : "[NULL]" ));
 
-    if ( $state->{method} eq 'pserver')
+    if ( $state->{method} eq 'pserver' and $state->{user} eq 'anonymous' )
     {
-        print "error 1 pserver access cannot commit\n";
+        print "error 1 anonymous user cannot commit via pserver\n";
         cleanupWorkTree();
         exit;
     }
@@ -1289,7 +1372,7 @@
 
 	# do a checkout of the file if it is part of this tree
         if ($wrev) {
-            system('git-checkout-index', '-f', '-u', $filename);
+            system('git', 'checkout-index', '-f', '-u', $filename);
             unless ($? == 0) {
                 die "Error running git-checkout-index -f -u $filename : $!";
             }
@@ -1331,15 +1414,15 @@
         {
             $log->info("Removing file '$filename'");
             unlink($filename);
-            system("git-update-index", "--remove", $filename);
+            system("git", "update-index", "--remove", $filename);
         }
         elsif ( $addflag )
         {
             $log->info("Adding file '$filename'");
-            system("git-update-index", "--add", $filename);
+            system("git", "update-index", "--add", $filename);
         } else {
             $log->info("Updating file '$filename'");
-            system("git-update-index", $filename);
+            system("git", "update-index", $filename);
         }
     }
 
@@ -1351,7 +1434,7 @@
         return;
     }
 
-    my $treehash = `git-write-tree`;
+    my $treehash = `git write-tree`;
     chomp $treehash;
 
     $log->debug("Treehash : $treehash, Parenthash : $parenthash");
@@ -1368,7 +1451,7 @@
     }
     close $msg_fh;
 
-    my $commithash = `git-commit-tree $treehash -p $parenthash < $msg_filename`;
+    my $commithash = `git commit-tree $treehash -p $parenthash < $msg_filename`;
     chomp($commithash);
     $log->info("Commit hash : $commithash");
 
@@ -1821,7 +1904,7 @@
 	# TODO: if we got a revision from the client, use that instead
 	# to look up the commithash in sqlite (still good to default to
 	# the current head as we do now)
-	system("git-read-tree", $lastseenin);
+	system("git", "read-tree", $lastseenin);
 	unless ($? == 0)
 	{
 	    print "E error running git-read-tree $lastseenin $ENV{GIT_INDEX_FILE} $!\n";
@@ -1830,7 +1913,7 @@
 	$log->info("Created index '$ENV{GIT_INDEX_FILE}' with commit $lastseenin - exit status $?");
 
         # do a checkout of the file
-        system('git-checkout-index', '-f', '-u', $filename);
+        system('git', 'checkout-index', '-f', '-u', $filename);
         unless ($? == 0) {
             print "E error running git-checkout-index -f -u $filename : $!\n";
             return;
@@ -1861,7 +1944,7 @@
         close ANNOTATEHINTS
             or (print "E failed to write $a_hints: $!\n"), return;
 
-        my @cmd = (qw(git-annotate -l -S), $a_hints, $filename);
+        my @cmd = (qw(git annotate -l -S), $a_hints, $filename);
         if (!open(ANNOTATE, "-|", @cmd)) {
             print "E error invoking ". join(' ',@cmd) .": $!\n";
             return;
@@ -2078,17 +2161,17 @@
 
     die "Need filehash" unless ( defined ( $filehash ) and $filehash =~ /^[a-zA-Z0-9]{40}$/ );
 
-    my $type = `git-cat-file -t $filehash`;
+    my $type = `git cat-file -t $filehash`;
     chomp $type;
 
     die ( "Invalid type '$type' (expected 'blob')" ) unless ( defined ( $type ) and $type eq "blob" );
 
-    my $size = `git-cat-file -s $filehash`;
+    my $size = `git cat-file -s $filehash`;
     chomp $size;
 
     $log->debug("transmitfile($filehash) size=$size, type=$type");
 
-    if ( open my $fh, '-|', "git-cat-file", "blob", $filehash )
+    if ( open my $fh, '-|', "git", "cat-file", "blob", $filehash )
     {
         if ( defined ( $options->{targetfile} ) )
         {
@@ -2333,15 +2416,20 @@
     if ( defined ( $cfg->{gitcvs}{usecrlfattr} ) and
          $cfg->{gitcvs}{usecrlfattr} =~ /\s*(1|true|yes)\s*$/i )
     {
-        my ($val) = check_attr( "crlf", $path );
-        if ( $val eq "set" )
+        my ($val) = check_attr( "text", $path );
+        if ( $val eq "unspecified" )
         {
-            return "";
+            $val = check_attr( "crlf", $path );
         }
-        elsif ( $val eq "unset" )
+        if ( $val eq "unset" )
         {
             return "-kb"
         }
+        elsif ( check_attr( "eol", $path ) ne "unspecified" ||
+                $val eq "set" || $val eq "input" )
+        {
+            return "";
+        }
         else
         {
             $log->info("Unrecognized check_attr crlf $path : $val");
@@ -2550,13 +2638,50 @@
     $author;
 }
 
+
+sub descramble
+{
+    # This table is from src/scramble.c in the CVS source
+    my @SHIFTS = (
+        0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
+        16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+        114,120, 53, 79, 96,109, 72,108, 70, 64, 76, 67,116, 74, 68, 87,
+        111, 52, 75,119, 49, 34, 82, 81, 95, 65,112, 86,118,110,122,105,
+        41, 57, 83, 43, 46,102, 40, 89, 38,103, 45, 50, 42,123, 91, 35,
+        125, 55, 54, 66,124,126, 59, 47, 92, 71,115, 78, 88,107,106, 56,
+        36,121,117,104,101,100, 69, 73, 99, 63, 94, 93, 39, 37, 61, 48,
+        58,113, 32, 90, 44, 98, 60, 51, 33, 97, 62, 77, 84, 80, 85,223,
+        225,216,187,166,229,189,222,188,141,249,148,200,184,136,248,190,
+        199,170,181,204,138,232,218,183,255,234,220,247,213,203,226,193,
+        174,172,228,252,217,201,131,230,197,211,145,238,161,179,160,212,
+        207,221,254,173,202,146,224,151,140,196,205,130,135,133,143,246,
+        192,159,244,239,185,168,215,144,139,165,180,157,147,186,214,176,
+        227,231,219,169,175,156,206,198,129,164,150,210,154,177,134,127,
+        182,128,158,208,162,132,167,209,149,241,153,251,237,236,171,195,
+        243,233,253,240,194,250,191,155,142,137,245,235,163,242,178,152
+    );
+    my ($str) = @_;
+
+    # This should never happen, the same password format (A) has been
+    # used by CVS since the beginning of time
+    {
+        my $fmt = substr($str, 0, 1);
+        die "invalid password format `$fmt'" unless $fmt eq 'A';
+    }
+
+    my @str = unpack "C*", substr($str, 1);
+    my $ret = join '', map { chr $SHIFTS[$_] } @str;
+    return $ret;
+}
+
+
 package GITCVS::log;
 
 ####
 #### Copyright The Open University UK - 2006.
 ####
 #### Authors: Martyn Smith    <martyn@catalyst.net.nz>
-####          Martin Langhoff <martin@catalyst.net.nz>
+####          Martin Langhoff <martin@laptop.org>
 ####
 ####
 
@@ -2723,7 +2848,7 @@
 #### Copyright The Open University UK - 2006.
 ####
 #### Authors: Martyn Smith    <martyn@catalyst.net.nz>
-####          Martin Langhoff <martin@catalyst.net.nz>
+####          Martin Langhoff <martin@laptop.org>
 ####
 ####
 
@@ -2935,7 +3060,7 @@
         push @git_log_params, $self->{module};
     }
     # git-rev-list is the backend / plumbing version of git-log
-    open(GITLOG, '-|', 'git-rev-list', @git_log_params) or die "Cannot call git-rev-list: $!";
+    open(GITLOG, '-|', 'git', 'rev-list', @git_log_params) or die "Cannot call git-rev-list: $!";
 
     my @commits;
 
@@ -3021,7 +3146,7 @@
                         next;
                     }
 		    my $base = eval {
-			    safe_pipe_capture('git-merge-base',
+			    safe_pipe_capture('git', 'merge-base',
 						 $lastpicked, $parent);
 		    };
 		    # The two branches may not be related at all,
@@ -3033,7 +3158,7 @@
                     if ($base) {
                         my @merged;
                         # print "want to log between  $base $parent \n";
-                        open(GITLOG, '-|', 'git-log', '--pretty=medium', "$base..$parent")
+                        open(GITLOG, '-|', 'git', 'log', '--pretty=medium', "$base..$parent")
 			  or die "Cannot call git-log: $!";
                         my $mergedhash;
                         while (<GITLOG>) {
@@ -3075,7 +3200,7 @@
 
         if ( defined ( $lastpicked ) )
         {
-            my $filepipe = open(FILELIST, '-|', 'git-diff-tree', '-z', '-r', $lastpicked, $commit->{hash}) or die("Cannot call git-diff-tree : $!");
+            my $filepipe = open(FILELIST, '-|', 'git', 'diff-tree', '-z', '-r', $lastpicked, $commit->{hash}) or die("Cannot call git-diff-tree : $!");
 	    local ($/) = "\0";
             while ( <FILELIST> )
             {
@@ -3149,7 +3274,7 @@
             # this is used to detect files removed from the repo
             my $seen_files = {};
 
-            my $filepipe = open(FILELIST, '-|', 'git-ls-tree', '-z', '-r', $commit->{hash}) or die("Cannot call git-ls-tree : $!");
+            my $filepipe = open(FILELIST, '-|', 'git', 'ls-tree', '-z', '-r', $commit->{hash}) or die("Cannot call git-ls-tree : $!");
 	    local $/ = "\0";
             while ( <FILELIST> )
             {
@@ -3451,7 +3576,7 @@
         return $message;
     }
 
-    my @lines = safe_pipe_capture("git-cat-file", "commit", $commithash);
+    my @lines = safe_pipe_capture("git", "cat-file", "commit", $commithash);
     shift @lines while ( $lines[0] =~ /\S/ );
     $message = join("",@lines);
     $message .= " " if ( $message =~ /\n$/ );
diff --git a/git-difftool--helper.sh b/git-difftool--helper.sh
index 57e8e32..3d0fe0c 100755
--- a/git-difftool--helper.sh
+++ b/git-difftool--helper.sh
@@ -3,24 +3,29 @@
 # This script is typically launched by using the 'git difftool'
 # convenience command.
 #
-# Copyright (c) 2009 David Aguilar
+# Copyright (c) 2009, 2010 David Aguilar
 
-# Load common functions from git-mergetool--lib
 TOOL_MODE=diff
 . git-mergetool--lib
 
 # difftool.prompt controls the default prompt/no-prompt behavior
 # and is overridden with $GIT_DIFFTOOL*_PROMPT.
 should_prompt () {
-	prompt=$(git config --bool difftool.prompt || echo true)
-	if test "$prompt" = true; then
+	prompt_merge=$(git config --bool mergetool.prompt || echo true)
+	prompt=$(git config --bool difftool.prompt || echo $prompt_merge)
+	if test "$prompt" = true
+	then
 		test -z "$GIT_DIFFTOOL_NO_PROMPT"
 	else
 		test -n "$GIT_DIFFTOOL_PROMPT"
 	fi
 }
 
-# Sets up shell variables and runs a merge tool
+# Indicates that --extcmd=... was specified
+use_ext_cmd () {
+	test -n "$GIT_DIFFTOOL_EXTCMD"
+}
+
 launch_merge_tool () {
 	# Merged is the filename as it appears in the work tree
 	# Local is the contents of a/filename
@@ -33,27 +38,51 @@
 
 	# $LOCAL and $REMOTE are temporary files so prompt
 	# the user with the real $MERGED name before launching $merge_tool.
-	if should_prompt; then
+	if should_prompt
+	then
 		printf "\nViewing: '$MERGED'\n"
-		printf "Hit return to launch '%s': " "$merge_tool"
-		read ans
+		if use_ext_cmd
+		then
+			printf "Launch '%s' [Y/n]: " \
+				"$GIT_DIFFTOOL_EXTCMD"
+		else
+			printf "Launch '%s' [Y/n]: " "$merge_tool"
+		fi
+		if read ans && test "$ans" = n
+		then
+			return
+		fi
 	fi
 
-	# Run the appropriate merge tool command
-	run_merge_tool "$merge_tool"
+	if use_ext_cmd
+	then
+		export BASE
+		eval $GIT_DIFFTOOL_EXTCMD '"$LOCAL"' '"$REMOTE"'
+	else
+		run_merge_tool "$merge_tool"
+	fi
 }
 
-# Allow GIT_DIFF_TOOL and GIT_MERGE_TOOL to provide default values
-test -n "$GIT_MERGE_TOOL" && merge_tool="$GIT_MERGE_TOOL"
-test -n "$GIT_DIFF_TOOL" && merge_tool="$GIT_DIFF_TOOL"
-
-if test -z "$merge_tool"; then
-	merge_tool="$(get_merge_tool)" || exit
+if ! use_ext_cmd
+then
+	if test -n "$GIT_DIFF_TOOL"
+	then
+		merge_tool="$GIT_DIFF_TOOL"
+	else
+		merge_tool="$(get_merge_tool)" || exit
+	fi
 fi
 
-# Launch the merge tool on each path provided by 'git diff'
-while test $# -gt 6
-do
-	launch_merge_tool "$1" "$2" "$5"
-	shift 7
-done
+if test -n "$GIT_DIFFTOOL_DIRDIFF"
+then
+	LOCAL="$1"
+	REMOTE="$2"
+	run_merge_tool "$merge_tool" false
+else
+	# Launch the merge tool on each path provided by 'git diff'
+	while test $# -gt 6
+	do
+		launch_merge_tool "$1" "$2" "$5"
+		shift 7
+	done
+fi
diff --git a/git-difftool.perl b/git-difftool.perl
index ba5e60a..c079854 100755
--- a/git-difftool.perl
+++ b/git-difftool.perl
@@ -1,92 +1,364 @@
-#!/usr/bin/env perl
-# Copyright (c) 2009 David Aguilar
+#!/usr/bin/perl
+# Copyright (c) 2009, 2010 David Aguilar
+# Copyright (c) 2012 Tim Henigan
 #
 # This is a wrapper around the GIT_EXTERNAL_DIFF-compatible
 # git-difftool--helper script.
 #
 # This script exports GIT_EXTERNAL_DIFF and GIT_PAGER for use by git.
-# GIT_DIFFTOOL_NO_PROMPT, GIT_DIFFTOOL_PROMPT, and GIT_DIFF_TOOL
-# are exported for use by git-difftool--helper.
+# The GIT_DIFF* variables are exported for use by git-difftool--helper.
 #
 # Any arguments that are unknown to this script are forwarded to 'git diff'.
 
+use 5.008;
 use strict;
 use warnings;
-use Cwd qw(abs_path);
 use File::Basename qw(dirname);
+use File::Copy;
+use File::Compare;
+use File::Find;
+use File::stat;
+use File::Path qw(mkpath);
+use File::Temp qw(tempdir);
+use Getopt::Long qw(:config pass_through);
+use Git;
 
-my $DIR = abs_path(dirname($0));
-
+my @tools;
+my @working_tree;
+my $rc;
+my $repo = Git->repository();
+my $repo_path = $repo->repo_path();
 
 sub usage
 {
+	my $exitcode = shift;
 	print << 'USAGE';
-usage: git difftool [--tool=<tool>] [-y|--no-prompt] ["git diff" options]
+usage: git difftool [-t|--tool=<tool>] [--tool-help]
+                    [-x|--extcmd=<cmd>]
+                    [-g|--gui] [--no-gui]
+                    [--prompt] [-y|--no-prompt]
+                    [-d|--dir-diff]
+                    ['git diff' options]
 USAGE
-	exit 1;
+	exit($exitcode);
 }
 
-sub setup_environment
+sub find_worktree
 {
-	$ENV{PATH} = "$DIR:$ENV{PATH}";
+	# Git->repository->wc_path() does not honor changes to the working
+	# tree location made by $ENV{GIT_WORK_TREE} or the 'core.worktree'
+	# config variable.
+	my $worktree;
+	my $env_worktree = $ENV{GIT_WORK_TREE};
+	my $core_worktree = Git::config('core.worktree');
+
+	if (defined($env_worktree) and (length($env_worktree) > 0)) {
+		$worktree = $env_worktree;
+	} elsif (defined($core_worktree) and (length($core_worktree) > 0)) {
+		$worktree = $core_worktree;
+	} else {
+		$worktree = $repo->wc_path();
+	}
+
+	return $worktree;
+}
+
+my $workdir = find_worktree();
+
+sub filter_tool_scripts
+{
+	if (-d $_) {
+		if ($_ ne ".") {
+			# Ignore files in subdirectories
+			$File::Find::prune = 1;
+		}
+	} else {
+		if ((-f $_) && ($_ ne "defaults")) {
+			push(@tools, $_);
+		}
+	}
+}
+
+sub print_tool_help
+{
+	my ($cmd, @found, @notfound);
+	my $gitpath = Git::exec_path();
+
+	find(\&filter_tool_scripts, "$gitpath/mergetools");
+
+	foreach my $tool (@tools) {
+		$cmd  = "TOOL_MODE=diff";
+		$cmd .= ' && . "$(git --exec-path)/git-mergetool--lib"';
+		$cmd .= " && get_merge_tool_path $tool >/dev/null 2>&1";
+		$cmd .= " && can_diff >/dev/null 2>&1";
+		if (system('sh', '-c', $cmd) == 0) {
+			push(@found, $tool);
+		} else {
+			push(@notfound, $tool);
+		}
+	}
+
+	print "'git difftool --tool=<tool>' may be set to one of the following:\n";
+	print "\t$_\n" for (sort(@found));
+
+	print "\nThe following tools are valid, but not currently available:\n";
+	print "\t$_\n" for (sort(@notfound));
+
+	print "\nNOTE: Some of the tools listed above only work in a windowed\n";
+	print "environment. If run in a terminal-only session, they will fail.\n";
+
+	exit(0);
+}
+
+sub setup_dir_diff
+{
+	# Run the diff; exit immediately if no diff found
+	# 'Repository' and 'WorkingCopy' must be explicitly set to insure that
+	# if $GIT_DIR and $GIT_WORK_TREE are set in ENV, they are actually used
+	# by Git->repository->command*.
+	my $diffrepo = Git->repository(Repository => $repo_path, WorkingCopy => $workdir);
+	my $diffrtn = $diffrepo->command_oneline('diff', '--raw', '--no-abbrev', '-z', @ARGV);
+	exit(0) if (length($diffrtn) == 0);
+
+	# Setup temp directories
+	my $tmpdir = tempdir('git-diffall.XXXXX', CLEANUP => 1, TMPDIR => 1);
+	my $ldir = "$tmpdir/left";
+	my $rdir = "$tmpdir/right";
+	mkpath($ldir) or die $!;
+	mkpath($rdir) or die $!;
+
+	# Build index info for left and right sides of the diff
+	my $submodule_mode = '160000';
+	my $symlink_mode = '120000';
+	my $null_mode = '0' x 6;
+	my $null_sha1 = '0' x 40;
+	my $lindex = '';
+	my $rindex = '';
+	my %submodule;
+	my %symlink;
+	my @rawdiff = split('\0', $diffrtn);
+
+	my $i = 0;
+	while ($i < $#rawdiff) {
+		if ($rawdiff[$i] =~ /^::/) {
+			print "Combined diff formats ('-c' and '--cc') are not supported in directory diff mode.\n";
+			exit(1);
+		}
+
+		my ($lmode, $rmode, $lsha1, $rsha1, $status) = split(' ', substr($rawdiff[$i], 1));
+		my $src_path = $rawdiff[$i + 1];
+		my $dst_path;
+
+		if ($status =~ /^[CR]/) {
+			$dst_path = $rawdiff[$i + 2];
+			$i += 3;
+		} else {
+			$dst_path = $src_path;
+			$i += 2;
+		}
+
+		if (($lmode eq $submodule_mode) or ($rmode eq $submodule_mode)) {
+			$submodule{$src_path}{left} = $lsha1;
+			if ($lsha1 ne $rsha1) {
+				$submodule{$dst_path}{right} = $rsha1;
+			} else {
+				$submodule{$dst_path}{right} = "$rsha1-dirty";
+			}
+			next;
+		}
+
+		if ($lmode eq $symlink_mode) {
+			$symlink{$src_path}{left} = $diffrepo->command_oneline('show', "$lsha1");
+		}
+
+		if ($rmode eq $symlink_mode) {
+			$symlink{$dst_path}{right} = $diffrepo->command_oneline('show', "$rsha1");
+		}
+
+		if (($lmode ne $null_mode) and ($status !~ /^C/)) {
+			$lindex .= "$lmode $lsha1\t$src_path\0";
+		}
+
+		if ($rmode ne $null_mode) {
+			if ($rsha1 ne $null_sha1) {
+				$rindex .= "$rmode $rsha1\t$dst_path\0";
+			} else {
+				push(@working_tree, $dst_path);
+			}
+		}
+	}
+
+	# If $GIT_DIR is not set prior to calling 'git update-index' and
+	# 'git checkout-index', then those commands will fail if difftool
+	# is called from a directory other than the repo root.
+	my $must_unset_git_dir = 0;
+	if (not defined($ENV{GIT_DIR})) {
+		$must_unset_git_dir = 1;
+		$ENV{GIT_DIR} = $repo_path;
+	}
+
+	# Populate the left and right directories based on each index file
+	my ($inpipe, $ctx);
+	$ENV{GIT_INDEX_FILE} = "$tmpdir/lindex";
+	($inpipe, $ctx) = $repo->command_input_pipe(qw/update-index -z --index-info/);
+	print($inpipe $lindex);
+	$repo->command_close_pipe($inpipe, $ctx);
+	$rc = system('git', 'checkout-index', '--all', "--prefix=$ldir/");
+	exit($rc | ($rc >> 8)) if ($rc != 0);
+
+	$ENV{GIT_INDEX_FILE} = "$tmpdir/rindex";
+	($inpipe, $ctx) = $repo->command_input_pipe(qw/update-index -z --index-info/);
+	print($inpipe $rindex);
+	$repo->command_close_pipe($inpipe, $ctx);
+	$rc = system('git', 'checkout-index', '--all', "--prefix=$rdir/");
+	exit($rc | ($rc >> 8)) if ($rc != 0);
+
+	# If $GIT_DIR was explicitly set just for the update/checkout
+	# commands, then it should be unset before continuing.
+	delete($ENV{GIT_DIR}) if ($must_unset_git_dir);
+	delete($ENV{GIT_INDEX_FILE});
+
+	# Changes in the working tree need special treatment since they are
+	# not part of the index
+	for my $file (@working_tree) {
+		my $dir = dirname($file);
+		unless (-d "$rdir/$dir") {
+			mkpath("$rdir/$dir") or die $!;
+		}
+		copy("$workdir/$file", "$rdir/$file") or die $!;
+		chmod(stat("$workdir/$file")->mode, "$rdir/$file") or die $!;
+	}
+
+	# Changes to submodules require special treatment. This loop writes a
+	# temporary file to both the left and right directories to show the
+	# change in the recorded SHA1 for the submodule.
+	for my $path (keys %submodule) {
+		if (defined($submodule{$path}{left})) {
+			write_to_file("$ldir/$path", "Subproject commit $submodule{$path}{left}");
+		}
+		if (defined($submodule{$path}{right})) {
+			write_to_file("$rdir/$path", "Subproject commit $submodule{$path}{right}");
+		}
+	}
+
+	# Symbolic links require special treatment. The standard "git diff"
+	# shows only the link itself, not the contents of the link target.
+	# This loop replicates that behavior.
+	for my $path (keys %symlink) {
+		if (defined($symlink{$path}{left})) {
+			write_to_file("$ldir/$path", $symlink{$path}{left});
+		}
+		if (defined($symlink{$path}{right})) {
+			write_to_file("$rdir/$path", $symlink{$path}{right});
+		}
+	}
+
+	return ($ldir, $rdir);
+}
+
+sub write_to_file
+{
+	my $path = shift;
+	my $value = shift;
+
+	# Make sure the path to the file exists
+	my $dir = dirname($path);
+	unless (-d "$dir") {
+		mkpath("$dir") or die $!;
+	}
+
+	# If the file already exists in that location, delete it.  This
+	# is required in the case of symbolic links.
+	unlink("$path");
+
+	open(my $fh, '>', "$path") or die $!;
+	print($fh $value);
+	close($fh);
+}
+
+# parse command-line options. all unrecognized options and arguments
+# are passed through to the 'git diff' command.
+my ($difftool_cmd, $dirdiff, $extcmd, $gui, $help, $prompt, $tool_help);
+GetOptions('g|gui!' => \$gui,
+	'd|dir-diff' => \$dirdiff,
+	'h' => \$help,
+	'prompt!' => \$prompt,
+	'y' => sub { $prompt = 0; },
+	't|tool:s' => \$difftool_cmd,
+	'tool-help' => \$tool_help,
+	'x|extcmd:s' => \$extcmd);
+
+if (defined($help)) {
+	usage(0);
+}
+if (defined($tool_help)) {
+	print_tool_help();
+}
+if (defined($difftool_cmd)) {
+	if (length($difftool_cmd) > 0) {
+		$ENV{GIT_DIFF_TOOL} = $difftool_cmd;
+	} else {
+		print "No <tool> given for --tool=<tool>\n";
+		usage(1);
+	}
+}
+if (defined($extcmd)) {
+	if (length($extcmd) > 0) {
+		$ENV{GIT_DIFFTOOL_EXTCMD} = $extcmd;
+	} else {
+		print "No <cmd> given for --extcmd=<cmd>\n";
+		usage(1);
+	}
+}
+if ($gui) {
+	my $guitool = '';
+	$guitool = Git::config('diff.guitool');
+	if (length($guitool) > 0) {
+		$ENV{GIT_DIFF_TOOL} = $guitool;
+	}
+}
+
+# In directory diff mode, 'git-difftool--helper' is called once
+# to compare the a/b directories.  In file diff mode, 'git diff'
+# will invoke a separate instance of 'git-difftool--helper' for
+# each file that changed.
+if (defined($dirdiff)) {
+	my ($a, $b) = setup_dir_diff();
+	if (defined($extcmd)) {
+		$rc = system($extcmd, $a, $b);
+	} else {
+		$ENV{GIT_DIFFTOOL_DIRDIFF} = 'true';
+		$rc = system('git', 'difftool--helper', $a, $b);
+	}
+
+	exit($rc | ($rc >> 8)) if ($rc != 0);
+
+	# If the diff including working copy files and those
+	# files were modified during the diff, then the changes
+	# should be copied back to the working tree
+	for my $file (@working_tree) {
+		if (-e "$b/$file" && compare("$b/$file", "$workdir/$file")) {
+			copy("$b/$file", "$workdir/$file") or die $!;
+			chmod(stat("$b/$file")->mode, "$workdir/$file") or die $!;
+		}
+	}
+} else {
+	if (defined($prompt)) {
+		if ($prompt) {
+			$ENV{GIT_DIFFTOOL_PROMPT} = 'true';
+		} else {
+			$ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true';
+		}
+	}
+
 	$ENV{GIT_PAGER} = '';
 	$ENV{GIT_EXTERNAL_DIFF} = 'git-difftool--helper';
+
+	# ActiveState Perl for Win32 does not implement POSIX semantics of
+	# exec* system call. It just spawns the given executable and finishes
+	# the starting program, exiting with code 0.
+	# system will at least catch the errors returned by git diff,
+	# allowing the caller of git difftool better handling of failures.
+	my $rc = system('git', 'diff', @ARGV);
+	exit($rc | ($rc >> 8));
 }
-
-sub exe
-{
-	my $exe = shift;
-	if ($^O eq 'MSWin32' || $^O eq 'msys') {
-		return "$exe.exe";
-	}
-	return $exe;
-}
-
-sub generate_command
-{
-	my @command = (exe('git'), 'diff');
-	my $skip_next = 0;
-	my $idx = -1;
-	for my $arg (@ARGV) {
-		$idx++;
-		if ($skip_next) {
-			$skip_next = 0;
-			next;
-		}
-		if ($arg eq '-t' || $arg eq '--tool') {
-			usage() if $#ARGV <= $idx;
-			$ENV{GIT_DIFF_TOOL} = $ARGV[$idx + 1];
-			$skip_next = 1;
-			next;
-		}
-		if ($arg =~ /^--tool=/) {
-			$ENV{GIT_DIFF_TOOL} = substr($arg, 7);
-			next;
-		}
-		if ($arg eq '-y' || $arg eq '--no-prompt') {
-			$ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true';
-			delete $ENV{GIT_DIFFTOOL_PROMPT};
-			next;
-		}
-		if ($arg eq '--prompt') {
-			$ENV{GIT_DIFFTOOL_PROMPT} = 'true';
-			delete $ENV{GIT_DIFFTOOL_NO_PROMPT};
-			next;
-		}
-		if ($arg eq '-h' || $arg eq '--help') {
-			usage();
-		}
-		push @command, $arg;
-	}
-	return @command
-}
-
-setup_environment();
-
-# ActiveState Perl for Win32 does not implement POSIX semantics of
-# exec* system call. It just spawns the given executable and finishes
-# the starting program, exiting with code 0.
-# system will at least catch the errors returned by git diff,
-# allowing the caller of git difftool better handling of failures.
-my $rc = system(generate_command());
-exit($rc | ($rc >> 8));
diff --git a/git-filter-branch.sh b/git-filter-branch.sh
index 37e044d..178e453 100755
--- a/git-filter-branch.sh
+++ b/git-filter-branch.sh
@@ -12,7 +12,7 @@
 
 functions=$(cat << \EOF
 warn () {
-        echo "$*" >&2
+	echo "$*" >&2
 }
 
 map()
@@ -84,7 +84,7 @@
 			s/.*/GIT_'$uid'_EMAIL='\''&'\''; export GIT_'$uid'_EMAIL/p
 
 			g
-			s/^'$lid' [^<]* <[^>]*> \(.*\)$/\1/
+			s/^'$lid' [^<]* <[^>]*> \(.*\)$/@\1/
 			s/'\''/'\''\'\'\''/g
 			s/.*/GIT_'$uid'_DATE='\''&'\''; export GIT_'$uid'_DATE/p
 
@@ -97,20 +97,18 @@
 	echo "case \"\$GIT_${uid}_NAME\" in \"\") GIT_${uid}_NAME=\"\${GIT_${uid}_EMAIL%%@*}\" && export GIT_${uid}_NAME;; esac"
 }
 
-USAGE="[--env-filter <command>] [--tree-filter <command>] \
-[--index-filter <command>] [--parent-filter <command>] \
-[--msg-filter <command>] [--commit-filter <command>] \
-[--tag-name-filter <command>] [--subdirectory-filter <directory>] \
-[--original <namespace>] [-d <directory>] [-f | --force] \
-[<rev-list options>...]"
+USAGE="[--env-filter <command>] [--tree-filter <command>]
+	[--index-filter <command>] [--parent-filter <command>]
+	[--msg-filter <command>] [--commit-filter <command>]
+	[--tag-name-filter <command>] [--subdirectory-filter <directory>]
+	[--original <namespace>] [-d <directory>] [-f | --force]
+	[<rev-list options>...]"
 
 OPTIONS_SPEC=
 . git-sh-setup
 
 if [ "$(is_bare_repository)" = false ]; then
-	git diff-files --ignore-submodules --quiet &&
-	git diff-index --cached --quiet HEAD -- ||
-	die "Cannot rewrite branch(es) with a dirty working directory."
+	require_clean_work_tree 'rewrite branches'
 fi
 
 tempdir=.git-rewrite
@@ -125,6 +123,7 @@
 orig_namespace=refs/original/
 force=
 prune_empty=
+remap_to_ancestor=
 while :
 do
 	case "$1" in
@@ -137,6 +136,12 @@
 		force=t
 		continue
 		;;
+	--remap-to-ancestor)
+		# deprecated ($remap_to_ancestor is set now automatically)
+		shift
+		remap_to_ancestor=t
+		continue
+		;;
 	--prune-empty)
 		shift
 		prune_empty=t
@@ -182,6 +187,7 @@
 		;;
 	--subdirectory-filter)
 		filter_subdir="$OPTARG"
+		remap_to_ancestor=t
 		;;
 	--original)
 		orig_namespace=$(expr "$OPTARG/" : '\(.*[^/]\)/*$')/
@@ -200,7 +206,7 @@
 ,*)
 	;;
 *)
-	die "Cannot set --prune-empty and --filter-commit at the same time"
+	die "Cannot set --prune-empty and --commit-filter at the same time"
 esac
 
 case "$force" in
@@ -252,20 +258,35 @@
 
 GIT_INDEX_FILE="$(pwd)/../index"
 export GIT_INDEX_FILE
-git read-tree || die "Could not seed the index"
 
 # map old->new commit ids for rewriting parents
 mkdir ../map || die "Could not create map/ directory"
 
+# we need "--" only if there are no path arguments in $@
+nonrevs=$(git rev-parse --no-revs "$@") || exit
+if test -z "$nonrevs"
+then
+	dashdash=--
+else
+	dashdash=
+	remap_to_ancestor=t
+fi
+
+rev_args=$(git rev-parse --revs-only "$@")
+
 case "$filter_subdir" in
 "")
-	git rev-list --reverse --topo-order --default HEAD \
-		--parents --simplify-merges "$@"
+	eval set -- "$(git rev-parse --sq --no-revs "$@")"
 	;;
 *)
-	git rev-list --reverse --topo-order --default HEAD \
-		--parents --simplify-merges "$@" -- "$filter_subdir"
-esac > ../revs || die "Could not get the commits"
+	eval set -- "$(git rev-parse --sq --no-revs "$@" $dashdash \
+		"$filter_subdir")"
+	;;
+esac
+
+git rev-list --reverse --topo-order --default HEAD \
+	--parents --simplify-merges $rev_args "$@" > ../revs ||
+	die "Could not get the commits"
 commits=$(wc -l <../revs | tr -d " ")
 
 test $commits -eq 0 && die "Found nothing to rewrite"
@@ -316,7 +337,7 @@
 			die "tree filter failed: $filter_tree"
 
 		(
-			git diff-index -r --name-only $commit &&
+			git diff-index -r --name-only --ignore-submodules $commit &&
 			git ls-files --others
 		) > "$tempdir"/tree-state || exit
 		git update-index --add --replace --remove --stdin \
@@ -340,24 +361,24 @@
 	sed -e '1,/^$/d' <../commit | \
 		eval "$filter_msg" > ../message ||
 			die "msg filter failed: $filter_msg"
-	@SHELL_PATH@ -c "$filter_commit" "git commit-tree" \
+	workdir=$workdir @SHELL_PATH@ -c "$filter_commit" "git commit-tree" \
 		$(git write-tree) $parentstr < ../message > ../map/$commit ||
 			die "could not write rewritten commit"
 done <../revs
 
-# In case of a subdirectory filter, it is possible that a specified head
-# is not in the set of rewritten commits, because it was pruned by the
-# revision walker.  Fix it by mapping these heads to the unique nearest
-# ancestor that survived the pruning.
+# If we are filtering for paths, as in the case of a subdirectory
+# filter, it is possible that a specified head is not in the set of
+# rewritten commits, because it was pruned by the revision walker.
+# Ancestor remapping fixes this by mapping these heads to the unique
+# nearest ancestor that survived the pruning.
 
-if test "$filter_subdir"
+if test "$remap_to_ancestor" = t
 then
 	while read ref
 	do
 		sha1=$(git rev-parse "$ref"^0)
 		test -f "$workdir"/../map/$sha1 && continue
-		ancestor=$(git rev-list --simplify-merges -1 \
-				$ref -- "$filter_subdir")
+		ancestor=$(git rev-list --simplify-merges -1 "$ref" "$@")
 		test "$ancestor" && echo $(map $ancestor) >> "$workdir"/../map/$sha1
 	done < "$tempdir"/heads
 fi
@@ -447,17 +468,17 @@
 						"$new_sha1" "$new_ref"
 				git cat-file tag "$ref" |
 				sed -n \
-				    -e "1,/^$/{
+				    -e '1,/^$/{
 					  /^object /d
 					  /^type /d
 					  /^tag /d
-					}" \
+					}' \
 				    -e '/^-----BEGIN PGP SIGNATURE-----/q' \
 				    -e 'p' ) |
 				git mktag) ||
 				die "Could not create new tag object for $ref"
 			if git cat-file tag "$ref" | \
-			   grep '^-----BEGIN PGP SIGNATURE-----' >/dev/null 2>&1
+			   sane_grep '^-----BEGIN PGP SIGNATURE-----' >/dev/null 2>&1
 			then
 				warn "gpg signature stripped from tag object $sha1t"
 			fi
diff --git a/git-gui/.gitattributes b/git-gui/.gitattributes
index f96112d..33d07c0 100644
--- a/git-gui/.gitattributes
+++ b/git-gui/.gitattributes
@@ -1,3 +1,4 @@
+*           whitespace=indent-with-non-tab,trailing-space,space-before-tab,tabwidth=4
 *           encoding=US-ASCII
 git-gui.sh  encoding=UTF-8
 /po/*.po    encoding=UTF-8
diff --git a/git-gui/GIT-VERSION-GEN b/git-gui/GIT-VERSION-GEN
index b3f937e..6570943 100755
--- a/git-gui/GIT-VERSION-GEN
+++ b/git-gui/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=0.12.GITGUI
+DEF_VER=0.16.GITGUI
 
 LF='
 '
diff --git a/git-gui/Makefile b/git-gui/Makefile
index b3580e9..e22ba5c 100644
--- a/git-gui/Makefile
+++ b/git-gui/Makefile
@@ -7,7 +7,7 @@
 # TCL_PATH must be vaild for this to work.
 #
 
-GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
+GIT-VERSION-FILE: FORCE
 	@$(SHELL_PATH) ./GIT-VERSION-GEN
 -include GIT-VERSION-FILE
 
@@ -215,6 +215,7 @@
 $(GITGUI_MAIN): git-gui.sh GIT-VERSION-FILE GIT-GUI-VARS
 	$(QUIET_GEN)rm -f $@ $@+ && \
 	sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
+		-e 's|@@SHELL_PATH@@|$(SHELL_PATH_SQ)|' \
 		-e '1,30s|^ argv0=$$0| argv0=$(GITGUI_SCRIPT)|' \
 		-e '1,30s|^ exec wish | exec '\''$(TCLTK_PATH_SED)'\'' |' \
 		-e 's/@@GITGUI_VERSION@@/$(GITGUI_VERSION)/g' \
@@ -270,7 +271,7 @@
 	GITGUI_MACOSXAPP=$(GITGUI_MACOSXAPP) \
 #end TRACK_VARS
 
-GIT-GUI-VARS: .FORCE-GIT-GUI-VARS
+GIT-GUI-VARS: FORCE
 	@VARS='$(TRACK_VARS)'; \
 	if test x"$$VARS" != x"`cat $@ 2>/dev/null`" ; then \
 		echo 1>&2 "    * new locations or Tcl/Tk interpreter"; \
@@ -340,5 +341,4 @@
 endif
 
 .PHONY: all install uninstall dist-version clean
-.PHONY: .FORCE-GIT-VERSION-FILE
-.PHONY: .FORCE-GIT-GUI-VARS
+.PHONY: FORCE
diff --git a/git-gui/git-gui--askpass b/git-gui/git-gui--askpass
index 12e117e..4277f30 100755
--- a/git-gui/git-gui--askpass
+++ b/git-gui/git-gui--askpass
@@ -5,6 +5,8 @@
 # This is a trivial implementation of an SSH_ASKPASS handler.
 # Git-gui uses this script if none are already configured.
 
+package require Tk
+
 set answer {}
 set yesno  0
 set rc     255
@@ -30,16 +32,20 @@
 
 frame .b
 button .b.ok     -text OK     -command finish
-button .b.cancel -text Cancel -command {destroy .}
+button .b.cancel -text Cancel -command cancel
 
 pack .b.ok -side left -expand 1
 pack .b.cancel -side right -expand 1
 pack .b -side bottom -fill x -padx 10 -pady 10
 
 bind . <Visibility> {focus -force .e}
-bind . <Key-Return> finish
-bind . <Key-Escape> {destroy .}
-bind . <Destroy>    {exit $rc}
+bind . <Key-Return> [list .b.ok invoke]
+bind . <Key-Escape> [list .b.cancel invoke]
+bind . <Destroy>    {set rc $rc}
+
+proc cancel {} {
+	set ::rc 255
+}
 
 proc finish {} {
 	if {$::yesno} {
@@ -50,10 +56,11 @@
 		}
 	}
 
-	set ::rc 0
 	puts $::answer
-	destroy .
+	set ::rc 0
 }
 
 wm title . "OpenSSH"
 tk::PlaceWindow .
+vwait rc
+exit $rc
diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh
index 14b92ba..ba4e5c1 100755
--- a/git-gui/git-gui.sh
+++ b/git-gui/git-gui.sh
@@ -10,8 +10,8 @@
  exec wish "$argv0" -- "$@"
 
 set appvers {@@GITGUI_VERSION@@}
-set copyright [encoding convertfrom utf-8 {
-Copyright © 2006, 2007 Shawn Pearce, et. al.
+set copyright [string map [list (c) \u00a9] {
+Copyright (c) 2006-2010 Shawn Pearce, et. al.
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -38,7 +38,7 @@
 	tk_messageBox \
 		-icon error \
 		-type ok \
-		-title [mc "git-gui: fatal error"] \
+		-title "git-gui: fatal error" \
 		-message $err
 	exit 1
 }
@@ -83,6 +83,7 @@
 		puts stderr "source    $name"
 		uplevel 1 real__source $name
 	}
+	if {[tk windowingsystem] eq "win32"} { console show }
 }
 
 ######################################################################
@@ -92,6 +93,25 @@
 
 package require msgcat
 
+# Check for Windows 7 MUI language pack (missed by msgcat < 1.4.4)
+if {[tk windowingsystem] eq "win32"
+	&& [package vcompare [package provide msgcat] 1.4.4] < 0
+} then {
+	proc _mc_update_locale {} {
+		set key {HKEY_CURRENT_USER\Control Panel\Desktop}
+		if {![catch {
+			package require registry
+			set uilocale [registry get $key "PreferredUILanguages"]
+			msgcat::ConvertLocale [string map {- _} [lindex $uilocale 0]]
+		} uilocale]} {
+			if {[string length $uilocale] > 0} {
+				msgcat::mclocale $uilocale
+			}
+		}
+	}
+	_mc_update_locale
+}
+
 proc _mc_trim {fmt} {
 	set cmk [string first @@ $fmt]
 	if {$cmk > 0} {
@@ -121,11 +141,14 @@
 
 set _appname {Git Gui}
 set _gitdir {}
+set _gitworktree {}
+set _isbare {}
 set _gitexec {}
 set _githtmldir {}
 set _reponame {}
 set _iscygwin {}
 set _search_path {}
+set _shellpath {@@SHELL_PATH@@}
 
 set _trace [lsearch -exact $argv --trace]
 if {$_trace >= 0} {
@@ -135,6 +158,22 @@
 	set _trace 0
 }
 
+# variable for the last merged branch (useful for a default when deleting
+# branches).
+set _last_merged_branch {}
+
+proc shellpath {} {
+	global _shellpath env
+	if {[string match @@* $_shellpath]} {
+		if {[info exists env(SHELL)]} {
+			return $env(SHELL)
+		} else {
+			return /bin/sh
+		}
+	}
+	return $_shellpath
+}
+
 proc appname {} {
 	global _appname
 	return $_appname
@@ -260,7 +299,22 @@
 	global repo_config
 	if {[catch {set v $repo_config($name)}]} {
 		return 0
-	} elseif {$v eq {true} || $v eq {1} || $v eq {yes}} {
+	}
+	set v [string tolower $v]
+	if {$v eq {} || $v eq {true} || $v eq {1} || $v eq {yes} || $v eq {on}} {
+		return 1
+	} else {
+		return 0
+	}
+}
+
+proc is_config_false {name} {
+	global repo_config
+	if {[catch {set v $repo_config($name)}]} {
+		return 0
+	}
+	set v [string tolower $v]
+	if {$v eq {false} || $v eq {0} || $v eq {no} || $v eq {off}} {
 		return 1
 	} else {
 		return 0
@@ -276,6 +330,32 @@
 	}
 }
 
+proc is_bare {} {
+	global _isbare
+	global _gitdir
+	global _gitworktree
+
+	if {$_isbare eq {}} {
+		if {[catch {
+			set _bare [git rev-parse --is-bare-repository]
+			switch  -- $_bare {
+			true { set _isbare 1 }
+			false { set _isbare 0}
+			default { throw }
+			}
+		}]} {
+			if {[is_config_true core.bare]
+				|| ($_gitworktree eq {}
+					&& [lindex [file split $_gitdir] end] ne {.git})} {
+				set _isbare 1
+			} else {
+				set _isbare 0
+			}
+		}
+	}
+	return $_isbare
+}
+
 ######################################################################
 ##
 ## handy utils
@@ -295,6 +375,8 @@
 	puts stderr $d
 }
 
+#'"  fix poor old emacs font-lock mode
+
 proc _git_cmd {name} {
 	global _git_cmd_path
 
@@ -382,12 +464,46 @@
 	return {}
 }
 
+# Test a file for a hashbang to identify executable scripts on Windows.
+proc is_shellscript {filename} {
+	if {![file exists $filename]} {return 0}
+	set f [open $filename r]
+	fconfigure $f -encoding binary
+	set magic [read $f 2]
+	close $f
+	return [expr {$magic eq "#!"}]
+}
+
+# Run a command connected via pipes on stdout.
+# This is for use with textconv filters and uses sh -c "..." to allow it to
+# contain a command with arguments. On windows we must check for shell
+# scripts specifically otherwise just call the filter command.
+proc open_cmd_pipe {cmd path} {
+	global env
+	if {![file executable [shellpath]]} {
+		set exe [auto_execok [lindex $cmd 0]]
+		if {[is_shellscript [lindex $exe 0]]} {
+			set run [linsert [auto_execok sh] end -c "$cmd \"\$0\"" $path]
+		} else {
+			set run [concat $exe [lrange $cmd 1 end] $path]
+		}
+	} else {
+		set run [list [shellpath] -c "$cmd \"\$0\"" $path]
+	}
+	return [open |$run r]
+}
+
 proc _lappend_nice {cmd_var} {
 	global _nice
 	upvar $cmd_var cmd
 
 	if {![info exists _nice]} {
 		set _nice [_which nice]
+		if {[catch {exec $_nice git version}]} {
+			set _nice {}
+		} elseif {[is_Windows] && [file dirname $_nice] ne [file dirname $::_git]} {
+			set _nice {}
+		}
 	}
 	if {$_nice ne {}} {
 		lappend cmd $_nice
@@ -606,6 +722,7 @@
 	return $text
 }
 
+wm withdraw .
 set root_exists 0
 bind . <Visibility> {
 	bind . <Visibility> {}
@@ -615,6 +732,7 @@
 if {[is_Windows]} {
 	wm iconbitmap . -default $oguilib/git-gui.ico
 	set ::tk::AlwaysShowSelection 1
+	bind . <Control-F2> {console show}
 
 	# Spoof an X11 display for SSH
 	if {![info exists env(DISPLAY)]} {
@@ -640,7 +758,10 @@
 		gitlogo put gray26  -to  5 15 11 16
 		gitlogo redither
 
-		wm iconphoto . -default gitlogo
+		image create photo gitlogo32 -width 32 -height 32
+		gitlogo32 copy gitlogo -zoom 2 2
+
+		wm iconphoto . -default gitlogo gitlogo32
 	}
 }
 
@@ -649,12 +770,17 @@
 ## config defaults
 
 set cursor_ptr arrow
-font create font_diff -family Courier -size 10
 font create font_ui
-catch {
-	label .dummy
-	eval font configure font_ui [font actual [.dummy cget -font]]
-	destroy .dummy
+if {[lsearch -exact [font names] TkDefaultFont] != -1} {
+	eval [linsert [font actual TkDefaultFont] 0 font configure font_ui]
+	eval [linsert [font actual TkFixedFont] 0 font create font_diff]
+} else {
+	font create font_diff -family Courier -size 10
+	catch {
+		label .dummy
+		eval font configure font_ui [font actual [.dummy cget -font]]
+		destroy .dummy
+	}
 }
 
 font create font_uiitalic
@@ -669,6 +795,9 @@
 }
 if {![is_MacOSX]} {
 	option add *Menu.font font_ui
+	option add *Entry.borderWidth 1 startupFile
+	option add *Entry.relief sunken startupFile
+	option add *RadioButton.anchor w startupFile
 }
 unset class
 
@@ -721,6 +850,18 @@
 		font configure ${font}bold -weight bold
 		font configure ${font}italic -slant italic
 	}
+
+	global use_ttk NS
+	set use_ttk 0
+	set NS {}
+	if {$repo_config(gui.usettk)} {
+		set use_ttk [package vsatisfies [package provide Tk] 8.5]
+		if {$use_ttk} {
+			set NS ttk
+			bind [winfo class .] <<ThemeChanged>> [list InitTheme]
+			pave_toplevel .
+		}
+	}
 }
 
 set default_config(branch.autosetupmerge) true
@@ -734,21 +875,28 @@
 
 set default_config(gui.encoding) [encoding system]
 set default_config(gui.matchtrackingbranch) false
+set default_config(gui.textconv) true
 set default_config(gui.pruneduringfetch) false
 set default_config(gui.trustmtime) false
 set default_config(gui.fastcopyblame) false
 set default_config(gui.copyblamethreshold) 40
 set default_config(gui.blamehistoryctx) 7
 set default_config(gui.diffcontext) 5
+set default_config(gui.diffopts) {}
 set default_config(gui.commitmsgwidth) 75
 set default_config(gui.newbranchtemplate) {}
 set default_config(gui.spellingdictionary) {}
 set default_config(gui.fontui) [font configure font_ui]
 set default_config(gui.fontdiff) [font configure font_diff]
+# TODO: this option should be added to the git-config documentation
+set default_config(gui.maxfilesdisplayed) 5000
+set default_config(gui.usettk) 1
+set default_config(gui.warndetachedcommit) 1
 set font_descs {
 	{fontui   font_ui   {mc "Main Font"}}
 	{fontdiff font_diff {mc "Diff/Console Font"}}
 }
+set default_config(gui.stageuntracked) ask
 
 ######################################################################
 ##
@@ -792,12 +940,19 @@
 	exit 1
 }
 
+proc get_trimmed_version {s} {
+    set r {}
+    foreach x [split $s -._] {
+        if {[string is integer -strict $x]} {
+            lappend r $x
+        } else {
+            break
+        }
+    }
+    return [join $r .]
+}
 set _real_git_version $_git_version
-regsub -- {[\-\.]dirty$} $_git_version {} _git_version
-regsub {\.[0-9]+\.g[0-9a-f]+$} $_git_version {} _git_version
-regsub {\.[a-zA-Z]+\.?[0-9]+$} $_git_version {} _git_version
-regsub {\.GIT$} $_git_version {} _git_version
-regsub {\.[a-zA-Z]+\.?[0-9]+$} $_git_version {} _git_version
+set _git_version [get_trimmed_version $_git_version]
 
 if {![regexp {^[1-9]+(\.[0-9]+)+$} $_git_version]} {
 	catch {wm withdraw .}
@@ -943,6 +1098,10 @@
 				} else {
 					set arr($name) $value
 				}
+			} elseif {[regexp {^([^\n]+)$} $line line name]} {
+				# no value given, but interpreting them as
+				# boolean will be handled as true
+				set arr($name) {}
 			}
 		}
 	}
@@ -958,6 +1117,10 @@
 					} else {
 						set arr($name) $value
 					}
+				} elseif {[regexp {^([^=]+)$} $line line name]} {
+					# no value given, but interpreting them as
+					# boolean will be handled as true
+					set arr($name) {}
 				}
 			}
 			close $fd_rc
@@ -1072,6 +1235,8 @@
 		set _prefix {}
 		}]
 	&& [catch {
+		# beware that from the .git dir this sets _gitdir to .
+		# and _prefix to the empty string
 		set _gitdir [git rev-parse --git-dir]
 		set _prefix [git rev-parse --show-prefix]
 	} err]} {
@@ -1080,6 +1245,14 @@
 	choose_repository::pick
 	set picked 1
 }
+
+# we expand the _gitdir when it's just a single dot (i.e. when we're being
+# run from the .git dir itself) lest the routines to find the worktree
+# get confused
+if {$_gitdir eq "."} {
+	set _gitdir [pwd]
+}
+
 if {![file isdirectory $_gitdir] && [is_Cygwin]} {
 	catch {set _gitdir [exec cygpath --windows $_gitdir]}
 }
@@ -1088,25 +1261,53 @@
 	error_popup [strcat [mc "Git directory not found:"] "\n\n$_gitdir"]
 	exit 1
 }
+# _gitdir exists, so try loading the config
+load_config 0
+apply_config
+
+# v1.7.0 introduced --show-toplevel to return the canonical work-tree
+if {[package vsatisfies $_git_version 1.7.0]} {
+	set _gitworktree [git rev-parse --show-toplevel]
+} else {
+	# try to set work tree from environment, core.worktree or use
+	# cdup to obtain a relative path to the top of the worktree. If
+	# run from the top, the ./ prefix ensures normalize expands pwd.
+	if {[catch { set _gitworktree $env(GIT_WORK_TREE) }]} {
+		set _gitworktree [get_config core.worktree]
+		if {$_gitworktree eq ""} {
+			set _gitworktree [file normalize ./[git rev-parse --show-cdup]]
+		}
+	}
+}
+
 if {$_prefix ne {}} {
-	regsub -all {[^/]+/} $_prefix ../ cdup
+	if {$_gitworktree eq {}} {
+		regsub -all {[^/]+/} $_prefix ../ cdup
+	} else {
+		set cdup $_gitworktree
+	}
 	if {[catch {cd $cdup} err]} {
 		catch {wm withdraw .}
 		error_popup [strcat [mc "Cannot move to top of working directory:"] "\n\n$err"]
 		exit 1
 	}
+	set _gitworktree [pwd]
 	unset cdup
 } elseif {![is_enabled bare]} {
-	if {[lindex [file split $_gitdir] end] ne {.git}} {
+	if {[is_bare]} {
 		catch {wm withdraw .}
-		error_popup [strcat [mc "Cannot use funny .git directory:"] "\n\n$_gitdir"]
+		error_popup [strcat [mc "Cannot use bare repository:"] "\n\n$_gitdir"]
 		exit 1
 	}
-	if {[catch {cd [file dirname $_gitdir]} err]} {
+	if {$_gitworktree eq {}} {
+		set _gitworktree [file dirname $_gitdir]
+	}
+	if {[catch {cd $_gitworktree} err]} {
 		catch {wm withdraw .}
-		error_popup [strcat [mc "No working directory"] " [file dirname $_gitdir]:\n\n$err"]
+		error_popup [strcat [mc "No working directory"] " $_gitworktree:\n\n$err"]
 		exit 1
 	}
+	set _gitworktree [pwd]
 }
 set _reponame [file split [file normalize $_gitdir]]
 if {[lindex $_reponame end] eq {.git}} {
@@ -1115,6 +1316,9 @@
 	set _reponame [lindex $_reponame end]
 }
 
+set env(GIT_DIR) $_gitdir
+set env(GIT_WORK_TREE) $_gitworktree
+
 ######################################################################
 ##
 ## global init
@@ -1132,6 +1336,7 @@
 set is_detached 0
 set current_diff_path {}
 set is_3way_diff 0
+set is_submodule_diff 0
 set is_conflict_diff 0
 set selected_commit_type new
 set diff_empty_count 0
@@ -1313,13 +1518,17 @@
 		close $fd
 	}
 
-	set ls_others [list --exclude-per-directory=.gitignore]
-	if {[have_info_exclude]} {
-		lappend ls_others "--exclude-from=[gitdir info exclude]"
-	}
-	set user_exclude [get_config core.excludesfile]
-	if {$user_exclude ne {} && [file readable $user_exclude]} {
-		lappend ls_others "--exclude-from=$user_exclude"
+	if {[package vsatisfies $::_git_version 1.6.3]} {
+		set ls_others [list --exclude-standard]
+	} else {
+		set ls_others [list --exclude-per-directory=.gitignore]
+		if {[have_info_exclude]} {
+			lappend ls_others "--exclude-from=[gitdir info exclude]"
+		}
+		set user_exclude [get_config core.excludesfile]
+		if {$user_exclude ne {} && [file readable $user_exclude]} {
+			lappend ls_others "--exclude-from=[file normalize $user_exclude]"
+		}
 	}
 
 	set buf_rdi {}
@@ -1364,7 +1573,7 @@
 
 	# prepare-commit-msg requires PREPARE_COMMIT_MSG exist.  From git-gui
 	# it will be .git/MERGE_MSG (merge), .git/SQUASH_MSG (squash), or an
-	# empty file but existant file.
+	# empty file but existent file.
 
 	set fd_pcm [open [gitdir PREPARE_COMMIT_MSG] a]
 
@@ -1610,6 +1819,9 @@
 	} elseif {$s0 ne {_} && [string index $state 0] eq {_}
 		&& $head_info eq {}} {
 		set head_info $index_info
+	} elseif {$s0 eq {_} && [string index $state 0] ne {_}} {
+		set index_info $head_info
+		set head_info {}
 	}
 
 	set file_states($path) [list $s0$s1 $icon \
@@ -1698,10 +1910,12 @@
 	$w insert end "[escape_path $path]\n"
 }
 
+set files_warning 0
 proc display_all_files {} {
 	global ui_index ui_workdir
 	global file_states file_lists
 	global last_clicked
+	global files_warning
 
 	$ui_index conf -state normal
 	$ui_workdir conf -state normal
@@ -1713,7 +1927,18 @@
 	set file_lists($ui_index) [list]
 	set file_lists($ui_workdir) [list]
 
-	foreach path [lsort [array names file_states]] {
+	set to_display [lsort [array names file_states]]
+	set display_limit [get_config gui.maxfilesdisplayed]
+	if {[llength $to_display] > $display_limit} {
+		if {!$files_warning} {
+			# do not repeatedly warn:
+			set files_warning 1
+			info_popup [mc "Displaying only %s of %s files." \
+				$display_limit [llength $to_display]]
+		}
+		set to_display [lrange $to_display 0 [expr {$display_limit-1}]]
+	}
+	foreach path $to_display {
 		set s $file_states($path)
 		set m [lindex $s 0]
 		set icon_name [lindex $s 1]
@@ -1779,15 +2004,6 @@
    0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
 } -maskdata $filemask
 
-image create bitmap file_parttick -background white -foreground "#005050" -data {
-#define parttick_width 14
-#define parttick_height 15
-static unsigned char parttick_bits[] = {
-   0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
-   0x7a, 0x14, 0x02, 0x16, 0x02, 0x13, 0x8a, 0x11, 0xda, 0x10, 0x72, 0x10,
-   0x22, 0x10, 0x02, 0x10, 0xfe, 0x1f};
-} -maskdata $filemask
-
 image create bitmap file_question -background white -foreground black -data {
 #define file_question_width 14
 #define file_question_height 15
@@ -1816,8 +2032,8 @@
 } -maskdata $filemask
 
 image create bitmap file_statechange -background white -foreground green -data {
-#define file_merge_width 14
-#define file_merge_height 15
+#define file_statechange_width 14
+#define file_statechange_height 15
 static unsigned char file_statechange_bits[] = {
    0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x62, 0x10,
    0x62, 0x10, 0xba, 0x11, 0xba, 0x11, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10,
@@ -1828,7 +2044,7 @@
 set ui_workdir .vpane.files.workdir.list
 
 set all_icons(_$ui_index)   file_plain
-set all_icons(A$ui_index)   file_fulltick
+set all_icons(A$ui_index)   file_plain
 set all_icons(M$ui_index)   file_fulltick
 set all_icons(D$ui_index)   file_removed
 set all_icons(U$ui_index)   file_merge
@@ -1851,7 +2067,11 @@
 		{MD {mc "Staged for commit, missing"}}
 
 		{_T {mc "File type changed, not staged"}}
+		{MT {mc "File type changed, old type staged for commit"}}
+		{AT {mc "File type changed, old type staged for commit"}}
 		{T_ {mc "File type changed, staged"}}
+		{TM {mc "File type change staged, modification not staged"}}
+		{TD {mc "File type change staged, file missing"}}
 
 		{_O {mc "Untracked, not staged"}}
 		{A_ {mc "Staged for commit"}}
@@ -1904,7 +2124,10 @@
 
 set starting_gitk_msg [mc "Starting gitk... please wait..."]
 
-proc do_gitk {revs} {
+proc do_gitk {revs {is_submodule false}} {
+	global current_diff_path file_states current_diff_side ui_index
+	global _gitdir _gitworktree
+
 	# -- Always start gitk through whatever we were loaded with.  This
 	#    lets us bypass using shell process on Windows systems.
 	#
@@ -1915,23 +2138,78 @@
 	} else {
 		global env
 
-		if {[info exists env(GIT_DIR)]} {
-			set old_GIT_DIR $env(GIT_DIR)
+		set pwd [pwd]
+
+		if {!$is_submodule} {
+			if {![is_bare]} {
+				cd $_gitworktree
+			}
 		} else {
-			set old_GIT_DIR {}
+			cd $current_diff_path
+			if {$revs eq {--}} {
+				set s $file_states($current_diff_path)
+				set old_sha1 {}
+				set new_sha1 {}
+				switch -glob -- [lindex $s 0] {
+				M_ { set old_sha1 [lindex [lindex $s 2] 1] }
+				_M { set old_sha1 [lindex [lindex $s 3] 1] }
+				MM {
+					if {$current_diff_side eq $ui_index} {
+						set old_sha1 [lindex [lindex $s 2] 1]
+						set new_sha1 [lindex [lindex $s 3] 1]
+					} else {
+						set old_sha1 [lindex [lindex $s 3] 1]
+					}
+				}
+				}
+				set revs $old_sha1...$new_sha1
+			}
+			# GIT_DIR and GIT_WORK_TREE for the submodule are not the ones
+			# we've been using for the main repository, so unset them.
+			# TODO we could make life easier (start up faster?) for gitk
+			# by setting these to the appropriate values to allow gitk
+			# to skip the heuristics to find their proper value
+			unset env(GIT_DIR)
+			unset env(GIT_WORK_TREE)
 		}
+		eval exec $cmd $revs "--" "--" &
+
+		set env(GIT_DIR) $_gitdir
+		set env(GIT_WORK_TREE) $_gitworktree
+		cd $pwd
+
+		ui_status $::starting_gitk_msg
+		after 10000 {
+			ui_ready $starting_gitk_msg
+		}
+	}
+}
+
+proc do_git_gui {} {
+	global current_diff_path
+
+	# -- Always start git gui through whatever we were loaded with.  This
+	#    lets us bypass using shell process on Windows systems.
+	#
+	set exe [list [_which git]]
+	if {$exe eq {}} {
+		error_popup [mc "Couldn't find git gui in PATH"]
+	} else {
+		global env
+		global _gitdir _gitworktree
+
+		# see note in do_gitk about unsetting these vars when
+		# running tools in a submodule
+		unset env(GIT_DIR)
+		unset env(GIT_WORK_TREE)
 
 		set pwd [pwd]
-		cd [file dirname [gitdir]]
-		set env(GIT_DIR) [file tail [gitdir]]
+		cd $current_diff_path
 
-		eval exec $cmd $revs &
+		eval exec $exe gui &
 
-		if {$old_GIT_DIR eq {}} {
-			unset env(GIT_DIR)
-		} else {
-			set env(GIT_DIR) $old_GIT_DIR
-		}
+		set env(GIT_DIR) $_gitdir
+		set env(GIT_WORK_TREE) $_gitworktree
 		cd $pwd
 
 		ui_status $::starting_gitk_msg
@@ -1942,6 +2220,7 @@
 }
 
 proc do_explore {} {
+	global _gitworktree
 	set explorer {}
 	if {[is_Cygwin] || [is_Windows]} {
 		set explorer "explorer.exe"
@@ -1951,7 +2230,7 @@
 		# freedesktop.org-conforming system is our best shot
 		set explorer "xdg-open"
 	}
-	eval exec $explorer [list [file nativename [file dirname [gitdir]]]] &
+	eval exec $explorer [list [file nativename $_gitworktree]] &
 }
 
 set is_quitting 0
@@ -1967,7 +2246,7 @@
 	global ui_comm is_quitting repo_config commit_type
 	global GITGUI_BCK_exists GITGUI_BCK_i
 	global ui_comm_spell
-	global ret_code
+	global ret_code use_ttk
 
 	if {$is_quitting} return
 	set is_quitting 1
@@ -2010,10 +2289,28 @@
 
 		# -- Stash our current window geometry into this repository.
 		#
+		set cfg_wmstate [wm state .]
+		if {[catch {set rc_wmstate $repo_config(gui.wmstate)}]} {
+			set rc_wmstate {}
+		}
+		if {$cfg_wmstate ne $rc_wmstate} {
+			catch {git config gui.wmstate $cfg_wmstate}
+		}
+		if {$cfg_wmstate eq {zoomed}} {
+			# on Windows wm geometry will lie about window
+			# position (but not size) when window is zoomed
+			# restore the window before querying wm geometry
+			wm state . normal
+		}
 		set cfg_geometry [list]
 		lappend cfg_geometry [wm geometry .]
-		lappend cfg_geometry [lindex [.vpane sash coord 0] 0]
-		lappend cfg_geometry [lindex [.vpane.files sash coord 0] 1]
+		if {$use_ttk} {
+			lappend cfg_geometry [.vpane sashpos 0]
+			lappend cfg_geometry [.vpane.files sashpos 0]
+		} else {
+			lappend cfg_geometry [lindex [.vpane sash coord 0] 0]
+			lappend cfg_geometry [lindex [.vpane.files sash coord 0] 1]
+		}
 		if {[catch {set rc_geometry $repo_config(gui.geometry)}]} {
 			set rc_geometry {}
 		}
@@ -2023,6 +2320,11 @@
 	}
 
 	set ret_code $rc
+
+	# Briefly enable send again, working around Tk bug
+	# http://sourceforge.net/tracker/?func=detail&atid=112997&aid=1821174&group_id=12997
+	tk appname [appname]
+
 	destroy .
 }
 
@@ -2218,6 +2520,7 @@
 				[concat $after [list ui_ready]]
 		}
 	} else {
+		set selected_paths($path) 1
 		show_diff $path $w $lno
 	}
 }
@@ -2297,8 +2600,6 @@
 ##
 ## ui construction
 
-load_config 0
-apply_config
 set ui_comm {}
 
 # -- Menu Bar
@@ -2330,10 +2631,12 @@
 #
 menu .mbar.repository
 
-.mbar.repository add command \
-	-label [mc "Explore Working Copy"] \
-	-command {do_explore}
-.mbar.repository add separator
+if {![is_bare]} {
+	.mbar.repository add command \
+		-label [mc "Explore Working Copy"] \
+		-command {do_explore}
+	.mbar.repository add separator
+}
 
 .mbar.repository add command \
 	-label [mc "Browse Current Branch's Files"] \
@@ -2509,12 +2812,14 @@
 		[list .mbar.commit entryconf [.mbar.commit index last] -state]
 
 	.mbar.commit add command -label [mc "Unstage From Commit"] \
-		-command do_unstage_selection
+		-command do_unstage_selection \
+		-accelerator $M1T-U
 	lappend disable_on_lock \
 		[list .mbar.commit entryconf [.mbar.commit index last] -state]
 
 	.mbar.commit add command -label [mc "Revert Changes"] \
-		-command do_revert_selection
+		-command do_revert_selection \
+		-accelerator $M1T-J
 	lappend disable_on_lock \
 		[list .mbar.commit entryconf [.mbar.commit index last] -state]
 
@@ -2652,7 +2957,14 @@
 
 set subcommand_args {}
 proc usage {} {
-	puts stderr "usage: $::argv0 $::subcommand $::subcommand_args"
+	set s "usage: $::argv0 $::subcommand $::subcommand_args"
+	if {[tk windowingsystem] eq "win32"} {
+		wm withdraw .
+		tk_messageBox -icon info -message $s \
+			-title [mc "Usage"]
+	} else {
+		puts stderr $s
+	}
 	exit 1
 }
 
@@ -2722,13 +3034,18 @@
 			if {[catch {
 					set head [git rev-parse --verify $head]
 				} err]} {
-				puts stderr $err
+				if {[tk windowingsystem] eq "win32"} {
+					tk_messageBox -icon error -title [mc Error] -message $err
+				} else {
+					puts stderr $err
+				}
 				exit 1
 			}
 		}
 		set current_branch $head
 	}
 
+	wm deiconify .
 	switch -- $subcommand {
 	browser {
 		if {$jump_spec ne {}} usage
@@ -2744,7 +3061,12 @@
 	}
 	blame   {
 		if {$head eq {} && ![file exists $path]} {
-			puts stderr [mc "fatal: cannot stat path %s: No such file or directory" $path]
+			catch {wm withdraw .}
+			tk_messageBox \
+				-icon error \
+				-type ok \
+				-title [mc "git-gui: fatal error"] \
+				-message [mc "fatal: cannot stat path %s: No such file or directory" $path]
 			exit 1
 		}
 		blame::new $head $path $jump_spec
@@ -2755,32 +3077,32 @@
 citool -
 gui {
 	if {[llength $argv] != 0} {
-		puts -nonewline stderr "usage: $argv0"
-		if {$subcommand ne {gui}
-			&& [file tail $argv0] ne "git-$subcommand"} {
-			puts -nonewline stderr " $subcommand"
-		}
-		puts stderr {}
-		exit 1
+		usage
 	}
 	# fall through to setup UI for commits
 }
 default {
-	puts stderr "usage: $argv0 \[{blame|browser|citool}\]"
+	set err "usage: $argv0 \[{blame|browser|citool}\]"
+	if {[tk windowingsystem] eq "win32"} {
+		wm withdraw .
+		tk_messageBox -icon error -message $err \
+			-title [mc "Usage"]
+	} else {
+		puts stderr $err
+	}
 	exit 1
 }
 }
 
 # -- Branch Control
 #
-frame .branch \
-	-borderwidth 1 \
-	-relief sunken
-label .branch.l1 \
+${NS}::frame .branch
+if {!$use_ttk} {.branch configure -borderwidth 1 -relief sunken}
+${NS}::label .branch.l1 \
 	-text [mc "Current Branch:"] \
 	-anchor w \
 	-justify left
-label .branch.cb \
+${NS}::label .branch.cb \
 	-textvariable current_branch \
 	-anchor w \
 	-justify left
@@ -2790,15 +3112,20 @@
 
 # -- Main Window Layout
 #
-panedwindow .vpane -orient horizontal
-panedwindow .vpane.files -orient vertical
-.vpane add .vpane.files -sticky nsew -height 100 -width 200
+${NS}::panedwindow .vpane -orient horizontal
+${NS}::panedwindow .vpane.files -orient vertical
+if {$use_ttk} {
+	.vpane add .vpane.files
+} else {
+	.vpane add .vpane.files -sticky nsew -height 100 -width 200
+}
 pack .vpane -anchor n -side top -fill both -expand 1
 
 # -- Index File List
 #
-frame .vpane.files.index -height 100 -width 200
-label .vpane.files.index.title -text [mc "Staged Changes (Will Commit)"] \
+${NS}::frame .vpane.files.index -height 100 -width 200
+tlabel .vpane.files.index.title \
+	-text [mc "Staged Changes (Will Commit)"] \
 	-background lightgreen -foreground black
 text $ui_index -background white -foreground black \
 	-borderwidth 0 \
@@ -2808,8 +3135,8 @@
 	-xscrollcommand {.vpane.files.index.sx set} \
 	-yscrollcommand {.vpane.files.index.sy set} \
 	-state disabled
-scrollbar .vpane.files.index.sx -orient h -command [list $ui_index xview]
-scrollbar .vpane.files.index.sy -orient v -command [list $ui_index yview]
+${NS}::scrollbar .vpane.files.index.sx -orient h -command [list $ui_index xview]
+${NS}::scrollbar .vpane.files.index.sy -orient v -command [list $ui_index yview]
 pack .vpane.files.index.title -side top -fill x
 pack .vpane.files.index.sx -side bottom -fill x
 pack .vpane.files.index.sy -side right -fill y
@@ -2817,8 +3144,8 @@
 
 # -- Working Directory File List
 #
-frame .vpane.files.workdir -height 100 -width 200
-label .vpane.files.workdir.title -text [mc "Unstaged Changes"] \
+${NS}::frame .vpane.files.workdir -height 100 -width 200
+tlabel .vpane.files.workdir.title -text [mc "Unstaged Changes"] \
 	-background lightsalmon -foreground black
 text $ui_workdir -background white -foreground black \
 	-borderwidth 0 \
@@ -2828,15 +3155,19 @@
 	-xscrollcommand {.vpane.files.workdir.sx set} \
 	-yscrollcommand {.vpane.files.workdir.sy set} \
 	-state disabled
-scrollbar .vpane.files.workdir.sx -orient h -command [list $ui_workdir xview]
-scrollbar .vpane.files.workdir.sy -orient v -command [list $ui_workdir yview]
+${NS}::scrollbar .vpane.files.workdir.sx -orient h -command [list $ui_workdir xview]
+${NS}::scrollbar .vpane.files.workdir.sy -orient v -command [list $ui_workdir yview]
 pack .vpane.files.workdir.title -side top -fill x
 pack .vpane.files.workdir.sx -side bottom -fill x
 pack .vpane.files.workdir.sy -side right -fill y
 pack $ui_workdir -side left -fill both -expand 1
 
-.vpane.files add .vpane.files.workdir -sticky nsew
-.vpane.files add .vpane.files.index -sticky nsew
+.vpane.files add .vpane.files.workdir
+.vpane.files add .vpane.files.index
+if {!$use_ttk} {
+	.vpane.files paneconfigure .vpane.files.workdir -sticky news
+	.vpane.files paneconfigure .vpane.files.index -sticky news
+}
 
 foreach i [list $ui_index $ui_workdir] {
 	rmsel_tag $i
@@ -2846,68 +3177,69 @@
 
 # -- Diff and Commit Area
 #
-frame .vpane.lower -height 300 -width 400
-frame .vpane.lower.commarea
-frame .vpane.lower.diff -relief sunken -borderwidth 1
+${NS}::frame .vpane.lower -height 300 -width 400
+${NS}::frame .vpane.lower.commarea
+${NS}::frame .vpane.lower.diff -relief sunken -borderwidth 1
 pack .vpane.lower.diff -fill both -expand 1
 pack .vpane.lower.commarea -side bottom -fill x
-.vpane add .vpane.lower -sticky nsew
+.vpane add .vpane.lower
+if {!$use_ttk} {.vpane paneconfigure .vpane.lower -sticky nsew}
 
 # -- Commit Area Buttons
 #
-frame .vpane.lower.commarea.buttons
-label .vpane.lower.commarea.buttons.l -text {} \
+${NS}::frame .vpane.lower.commarea.buttons
+${NS}::label .vpane.lower.commarea.buttons.l -text {} \
 	-anchor w \
 	-justify left
 pack .vpane.lower.commarea.buttons.l -side top -fill x
 pack .vpane.lower.commarea.buttons -side left -fill y
 
-button .vpane.lower.commarea.buttons.rescan -text [mc Rescan] \
+${NS}::button .vpane.lower.commarea.buttons.rescan -text [mc Rescan] \
 	-command ui_do_rescan
 pack .vpane.lower.commarea.buttons.rescan -side top -fill x
 lappend disable_on_lock \
 	{.vpane.lower.commarea.buttons.rescan conf -state}
 
-button .vpane.lower.commarea.buttons.incall -text [mc "Stage Changed"] \
+${NS}::button .vpane.lower.commarea.buttons.incall -text [mc "Stage Changed"] \
 	-command do_add_all
 pack .vpane.lower.commarea.buttons.incall -side top -fill x
 lappend disable_on_lock \
 	{.vpane.lower.commarea.buttons.incall conf -state}
 
 if {![is_enabled nocommitmsg]} {
-	button .vpane.lower.commarea.buttons.signoff -text [mc "Sign Off"] \
+	${NS}::button .vpane.lower.commarea.buttons.signoff -text [mc "Sign Off"] \
 		-command do_signoff
 	pack .vpane.lower.commarea.buttons.signoff -side top -fill x
 }
 
-button .vpane.lower.commarea.buttons.commit -text [commit_btn_caption] \
+${NS}::button .vpane.lower.commarea.buttons.commit -text [commit_btn_caption] \
 	-command do_commit
 pack .vpane.lower.commarea.buttons.commit -side top -fill x
 lappend disable_on_lock \
 	{.vpane.lower.commarea.buttons.commit conf -state}
 
 if {![is_enabled nocommit]} {
-	button .vpane.lower.commarea.buttons.push -text [mc Push] \
+	${NS}::button .vpane.lower.commarea.buttons.push -text [mc Push] \
 		-command do_push_anywhere
 	pack .vpane.lower.commarea.buttons.push -side top -fill x
 }
 
 # -- Commit Message Buffer
 #
-frame .vpane.lower.commarea.buffer
-frame .vpane.lower.commarea.buffer.header
+${NS}::frame .vpane.lower.commarea.buffer
+${NS}::frame .vpane.lower.commarea.buffer.header
 set ui_comm .vpane.lower.commarea.buffer.t
 set ui_coml .vpane.lower.commarea.buffer.header.l
 
 if {![is_enabled nocommit]} {
-	radiobutton .vpane.lower.commarea.buffer.header.new \
+	${NS}::radiobutton .vpane.lower.commarea.buffer.header.new \
 		-text [mc "New Commit"] \
 		-command do_select_commit_type \
 		-variable selected_commit_type \
 		-value new
 	lappend disable_on_lock \
 		[list .vpane.lower.commarea.buffer.header.new conf -state]
-	radiobutton .vpane.lower.commarea.buffer.header.amend \
+	${NS}::radiobutton .vpane.lower.commarea.buffer.header.amend \
 		-text [mc "Amend Last Commit"] \
 		-command do_select_commit_type \
 		-variable selected_commit_type \
@@ -2916,7 +3248,7 @@
 		[list .vpane.lower.commarea.buffer.header.amend conf -state]
 }
 
-label $ui_coml \
+${NS}::label $ui_coml \
 	-anchor w \
 	-justify left
 proc trace_commit_type {varname args} {
@@ -2948,7 +3280,7 @@
 	-width $repo_config(gui.commitmsgwidth) -height 9 -wrap none \
 	-font font_diff \
 	-yscrollcommand {.vpane.lower.commarea.buffer.sby set}
-scrollbar .vpane.lower.commarea.buffer.sby \
+${NS}::scrollbar .vpane.lower.commarea.buffer.sby \
 	-command [list $ui_comm yview]
 pack .vpane.lower.commarea.buffer.header -side top -fill x
 pack .vpane.lower.commarea.buffer.sby -side right -fill y
@@ -3014,19 +3346,19 @@
 }
 trace add variable current_diff_path write trace_current_diff_path
 
-frame .vpane.lower.diff.header -background gold
-label .vpane.lower.diff.header.status \
+gold_frame .vpane.lower.diff.header
+tlabel .vpane.lower.diff.header.status \
 	-background gold \
 	-foreground black \
 	-width $max_status_desc \
 	-anchor w \
 	-justify left
-label .vpane.lower.diff.header.file \
+tlabel .vpane.lower.diff.header.file \
 	-background gold \
 	-foreground black \
 	-anchor w \
 	-justify left
-label .vpane.lower.diff.header.path \
+tlabel .vpane.lower.diff.header.path \
 	-background gold \
 	-foreground black \
 	-anchor w \
@@ -3050,18 +3382,19 @@
 
 # -- Diff Body
 #
-frame .vpane.lower.diff.body
+${NS}::frame .vpane.lower.diff.body
 set ui_diff .vpane.lower.diff.body.t
 text $ui_diff -background white -foreground black \
 	-borderwidth 0 \
-	-width 80 -height 15 -wrap none \
+	-width 80 -height 5 -wrap none \
 	-font font_diff \
 	-xscrollcommand {.vpane.lower.diff.body.sbx set} \
 	-yscrollcommand {.vpane.lower.diff.body.sby set} \
 	-state disabled
-scrollbar .vpane.lower.diff.body.sbx -orient horizontal \
+catch {$ui_diff configure -tabstyle wordprocessor}
+${NS}::scrollbar .vpane.lower.diff.body.sbx -orient horizontal \
 	-command [list $ui_diff xview]
-scrollbar .vpane.lower.diff.body.sby -orient vertical \
+${NS}::scrollbar .vpane.lower.diff.body.sby -orient vertical \
 	-command [list $ui_diff yview]
 pack .vpane.lower.diff.body.sbx -side bottom -fill x
 pack .vpane.lower.diff.body.sby -side right -fill y
@@ -3069,8 +3402,19 @@
 pack .vpane.lower.diff.header -side top -fill x
 pack .vpane.lower.diff.body -side bottom -fill both -expand 1
 
+foreach {n c} {0 black 1 red4 2 green4 3 yellow4 4 blue4 5 magenta4 6 cyan4 7 grey60} {
+	$ui_diff tag configure clr4$n -background $c
+	$ui_diff tag configure clri4$n -foreground $c
+	$ui_diff tag configure clr3$n -foreground $c
+	$ui_diff tag configure clri3$n -background $c
+}
+$ui_diff tag configure clr1 -font font_diffbold
+$ui_diff tag configure clr4 -underline 1
+
+$ui_diff tag conf d_info -foreground blue -font font_diffbold
+
 $ui_diff tag conf d_cr -elide true
-$ui_diff tag conf d_@ -foreground blue -font font_diffbold
+$ui_diff tag conf d_@ -font font_diffbold
 $ui_diff tag conf d_+ -foreground {#00a000}
 $ui_diff tag conf d_- -foreground red
 
@@ -3089,13 +3433,13 @@
 	-foreground red \
 	-background ivory1
 
-$ui_diff tag conf d<<<<<<< \
+$ui_diff tag conf d< \
 	-foreground orange \
 	-font font_diffbold
-$ui_diff tag conf d======= \
+$ui_diff tag conf d= \
 	-foreground orange \
 	-font font_diffbold
-$ui_diff tag conf d>>>>>>> \
+$ui_diff tag conf d> \
 	-foreground orange \
 	-font font_diffbold
 
@@ -3106,15 +3450,6 @@
 
 proc create_common_diff_popup {ctxm} {
 	$ctxm add command \
-		-label [mc "Show Less Context"] \
-		-command show_less_context
-	lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
-	$ctxm add command \
-		-label [mc "Show More Context"] \
-		-command show_more_context
-	lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
-	$ctxm add separator
-	$ctxm add command \
 		-label [mc Refresh] \
 		-command reshow_diff
 	lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
@@ -3165,10 +3500,19 @@
 lappend diff_actions [list $ctxm entryconf $ui_diff_applyhunk -state]
 $ctxm add command \
 	-label [mc "Apply/Reverse Line"] \
-	-command {apply_line $cursorX $cursorY; do_rescan}
+	-command {apply_range_or_line $cursorX $cursorY; do_rescan}
 set ui_diff_applyline [$ctxm index last]
 lappend diff_actions [list $ctxm entryconf $ui_diff_applyline -state]
 $ctxm add separator
+$ctxm add command \
+	-label [mc "Show Less Context"] \
+	-command show_less_context
+lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
+$ctxm add command \
+	-label [mc "Show More Context"] \
+	-command show_more_context
+lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
+$ctxm add separator
 create_common_diff_popup $ctxm
 
 set ctxmmg .vpane.lower.diff.body.ctxmmg
@@ -3191,9 +3535,53 @@
 	-command {merge_resolve_one 1}
 lappend diff_actions [list $ctxmmg entryconf [$ctxmmg index last] -state]
 $ctxmmg add separator
+$ctxmmg add command \
+	-label [mc "Show Less Context"] \
+	-command show_less_context
+lappend diff_actions [list $ctxmmg entryconf [$ctxmmg index last] -state]
+$ctxmmg add command \
+	-label [mc "Show More Context"] \
+	-command show_more_context
+lappend diff_actions [list $ctxmmg entryconf [$ctxmmg index last] -state]
+$ctxmmg add separator
 create_common_diff_popup $ctxmmg
 
-proc popup_diff_menu {ctxm ctxmmg x y X Y} {
+set ctxmsm .vpane.lower.diff.body.ctxmsm
+menu $ctxmsm -tearoff 0
+$ctxmsm add command \
+	-label [mc "Visualize These Changes In The Submodule"] \
+	-command {do_gitk -- true}
+lappend diff_actions [list $ctxmsm entryconf [$ctxmsm index last] -state]
+$ctxmsm add command \
+	-label [mc "Visualize Current Branch History In The Submodule"] \
+	-command {do_gitk {} true}
+lappend diff_actions [list $ctxmsm entryconf [$ctxmsm index last] -state]
+$ctxmsm add command \
+	-label [mc "Visualize All Branch History In The Submodule"] \
+	-command {do_gitk --all true}
+lappend diff_actions [list $ctxmsm entryconf [$ctxmsm index last] -state]
+$ctxmsm add separator
+$ctxmsm add command \
+	-label [mc "Start git gui In The Submodule"] \
+	-command {do_git_gui}
+lappend diff_actions [list $ctxmsm entryconf [$ctxmsm index last] -state]
+$ctxmsm add separator
+create_common_diff_popup $ctxmsm
+
+proc has_textconv {path} {
+	if {[is_config_false gui.textconv]} {
+		return 0
+	}
+	set filter [gitattr $path diff set]
+	set textconv [get_config [join [list diff $filter textconv] .]]
+	if {$filter ne {set} && $textconv ne {}} {
+		return 1
+	} else {
+		return 0
+	}
+}
+
+proc popup_diff_menu {ctxm ctxmmg ctxmsm x y X Y} {
 	global current_diff_path file_states
 	set ::cursorX $x
 	set ::cursorY $y
@@ -3204,20 +3592,32 @@
 	}
 	if {[string first {U} $state] >= 0} {
 		tk_popup $ctxmmg $X $Y
+	} elseif {$::is_submodule_diff} {
+		tk_popup $ctxmsm $X $Y
 	} else {
+		set has_range [expr {[$::ui_diff tag nextrange sel 0.0] != {}}]
 		if {$::ui_index eq $::current_diff_side} {
 			set l [mc "Unstage Hunk From Commit"]
-			set t [mc "Unstage Line From Commit"]
+			if {$has_range} {
+				set t [mc "Unstage Lines From Commit"]
+			} else {
+				set t [mc "Unstage Line From Commit"]
+			}
 		} else {
 			set l [mc "Stage Hunk For Commit"]
-			set t [mc "Stage Line For Commit"]
+			if {$has_range} {
+				set t [mc "Stage Lines For Commit"]
+			} else {
+				set t [mc "Stage Line For Commit"]
+			}
 		}
 		if {$::is_3way_diff
 			|| $current_diff_path eq {}
 			|| {__} eq $state
 			|| {_O} eq $state
-			|| {_T} eq $state
-			|| {T_} eq $state} {
+			|| [string match {?T} $state]
+			|| [string match {T?} $state]
+			|| [has_textconv $current_diff_path]} {
 			set s disabled
 		} else {
 			set s normal
@@ -3227,7 +3627,7 @@
 		tk_popup $ctxm $X $Y
 	}
 }
-bind_button3 $ui_diff [list popup_diff_menu $ctxm $ctxmmg %x %y %X %Y]
+bind_button3 $ui_diff [list popup_diff_menu $ctxm $ctxmmg $ctxmsm %x %y %X %Y]
 
 # -- Status Bar
 #
@@ -3237,16 +3637,44 @@
 
 # -- Load geometry
 #
-catch {
-set gm $repo_config(gui.geometry)
-wm geometry . [lindex $gm 0]
-.vpane sash place 0 \
-	[lindex $gm 1] \
-	[lindex [.vpane sash coord 0] 1]
-.vpane.files sash place 0 \
-	[lindex [.vpane.files sash coord 0] 0] \
-	[lindex $gm 2]
-unset gm
+proc on_ttk_pane_mapped {w pane pos} {
+	bind $w <Map> {}
+	after 0 [list after idle [list $w sashpos $pane $pos]]
+}
+proc on_tk_pane_mapped {w pane x y} {
+	bind $w <Map> {}
+	after 0 [list after idle [list $w sash place $pane $x $y]]
+}
+proc on_application_mapped {} {
+	global repo_config use_ttk
+	bind . <Map> {}
+	set gm $repo_config(gui.geometry)
+	if {$use_ttk} {
+		bind .vpane <Map> \
+		    [list on_ttk_pane_mapped %W 0 [lindex $gm 1]]
+		bind .vpane.files <Map> \
+		    [list on_ttk_pane_mapped %W 0 [lindex $gm 2]]
+	} else {
+		bind .vpane <Map> \
+		    [list on_tk_pane_mapped %W 0 \
+			 [lindex $gm 1] \
+			 [lindex [.vpane sash coord 0] 1]]
+		bind .vpane.files <Map> \
+		    [list on_tk_pane_mapped %W 0 \
+			 [lindex [.vpane.files sash coord 0] 0] \
+			 [lindex $gm 2]]
+	}
+	wm geometry . [lindex $gm 0]
+}
+if {[info exists repo_config(gui.geometry)]} {
+	bind . <Map> [list on_application_mapped]
+	wm geometry . [lindex $repo_config(gui.geometry) 0]
+}
+
+# -- Load window state
+#
+if {[info exists repo_config(gui.wmstate)]} {
+	catch {wm state . $repo_config(gui.wmstate)}
 }
 
 # -- Key Bindings
@@ -3254,6 +3682,10 @@
 bind $ui_comm <$M1B-Key-Return> {do_commit;break}
 bind $ui_comm <$M1B-Key-t> {do_add_selection;break}
 bind $ui_comm <$M1B-Key-T> {do_add_selection;break}
+bind $ui_comm <$M1B-Key-u> {do_unstage_selection;break}
+bind $ui_comm <$M1B-Key-U> {do_unstage_selection;break}
+bind $ui_comm <$M1B-Key-j> {do_revert_selection;break}
+bind $ui_comm <$M1B-Key-J> {do_revert_selection;break}
 bind $ui_comm <$M1B-Key-i> {do_add_all;break}
 bind $ui_comm <$M1B-Key-I> {do_add_all;break}
 bind $ui_comm <$M1B-Key-x> {tk_textCut %W;break}
@@ -3310,6 +3742,8 @@
 bind .   <$M1B-Key-S> do_signoff
 bind .   <$M1B-Key-t> do_add_selection
 bind .   <$M1B-Key-T> do_add_selection
+bind .   <$M1B-Key-j> do_revert_selection
+bind .   <$M1B-Key-J> do_revert_selection
 bind .   <$M1B-Key-i> do_add_all
 bind .   <$M1B-Key-I> do_add_all
 bind .   <$M1B-Key-minus> {show_less_context;break}
@@ -3328,7 +3762,7 @@
 set file_lists($ui_index) [list]
 set file_lists($ui_workdir) [list]
 
-wm title . "[appname] ([reponame]) [file normalize [file dirname [gitdir]]]"
+wm title . "[appname] ([reponame]) [file normalize $_gitworktree]"
 focus -force $ui_comm
 
 # -- Warn the user about environmental problems.  Cygwin's Tcl
@@ -3492,7 +3926,7 @@
 		$ui_comm configure -state disabled -background gray
 	}
 }
-if {[is_enabled multicommit]} {
+if {[is_enabled multicommit] && ![is_config_false gui.gcwarning]} {
 	after 1000 hint_gc
 }
 if {[is_enabled retcode]} {
@@ -3501,3 +3935,9 @@
 if {$picked && [is_config_true gui.autoexplore]} {
 	do_explore
 }
+
+# Local variables:
+# mode: tcl
+# indent-tabs-mode: t
+# tab-width: 4
+# End:
diff --git a/git-gui/lib/about.tcl b/git-gui/lib/about.tcl
index 241ab89..cfa50fc 100644
--- a/git-gui/lib/about.tcl
+++ b/git-gui/lib/about.tcl
@@ -4,31 +4,26 @@
 proc do_about {} {
 	global appvers copyright oguilib
 	global tcl_patchLevel tk_patchLevel
-	global ui_comm_spell
+	global ui_comm_spell NS use_ttk
 
 	set w .about_dialog
-	toplevel $w
+	Dialog $w
 	wm geometry $w "+[winfo rootx .]+[winfo rooty .]"
 
 	pack [git_logo $w.git_logo] -side left -fill y -padx 10 -pady 10
-	label $w.header -text [mc "About %s" [appname]] \
-		-font font_uibold
+	${NS}::label $w.header -text [mc "About %s" [appname]] \
+		-font font_uibold -anchor center
 	pack $w.header -side top -fill x
 
-	frame $w.buttons
-	button $w.buttons.close -text {Close} \
+	${NS}::frame $w.buttons
+	${NS}::button $w.buttons.close -text {Close} \
 		-default active \
 		-command [list destroy $w]
 	pack $w.buttons.close -side right
 	pack $w.buttons -side bottom -fill x -pady 10 -padx 10
 
-	label $w.desc \
-		-text "[mc "git-gui - a graphical user interface for Git."]\n$copyright" \
-		-padx 5 -pady 5 \
-		-justify left \
-		-anchor w \
-		-borderwidth 1 \
-		-relief solid
+	paddedlabel $w.desc \
+		-text "[mc "git-gui - a graphical user interface for Git."]\n$copyright"
 	pack $w.desc -side top -fill x -padx 5 -pady 5
 
 	set v {}
@@ -52,22 +47,10 @@
 	append d "git exec dir: [gitexec]\n"
 	append d "git-gui lib: $oguilib"
 
-	label $w.vers \
-		-text $v \
-		-padx 5 -pady 5 \
-		-justify left \
-		-anchor w \
-		-borderwidth 1 \
-		-relief solid
+	paddedlabel $w.vers -text $v
 	pack $w.vers -side top -fill x -padx 5 -pady 5
 
-	label $w.dirs \
-		-text $d \
-		-padx 5 -pady 5 \
-		-justify left \
-		-anchor w \
-		-borderwidth 1 \
-		-relief solid
+	paddedlabel $w.dirs -text $d
 	pack $w.dirs -side top -fill x -padx 5 -pady 5
 
 	menu $w.ctxm -tearoff 0
diff --git a/git-gui/lib/blame.tcl b/git-gui/lib/blame.tcl
index 1f3b08f..324f774 100644
--- a/git-gui/lib/blame.tcl
+++ b/git-gui/lib/blame.tcl
@@ -22,6 +22,7 @@
 field w_file     ; # text column: actual file data
 field w_cviewer  ; # pane showing commit message
 field finder     ; # find mini-dialog frame
+field gotoline   ; # line goto mini-dialog frame
 field status     ; # status mega-widget instance
 field old_height ; # last known height of $w.file_pane
 
@@ -61,7 +62,7 @@
 field tooltip_commit    {} ; # Commit(s) in tooltip
 
 constructor new {i_commit i_path i_jump} {
-	global cursor_ptr M1B M1T have_tk85
+	global cursor_ptr M1B M1T have_tk85 use_ttk NS
 	variable active_color
 	variable group_colors
 
@@ -73,15 +74,15 @@
 
 	set font_w [font measure font_diff "0"]
 
-	frame $w.header -background gold
-	label $w.header.commit_l \
+	gold_frame $w.header
+	tlabel $w.header.commit_l \
 		-text [mc "Commit:"] \
 		-background gold \
 		-foreground black \
 		-anchor w \
 		-justify left
 	set w_back $w.header.commit_b
-	label $w_back \
+	tlabel $w_back \
 		-image ::blame::img_back_arrow \
 		-borderwidth 0 \
 		-relief flat \
@@ -94,20 +95,20 @@
 			[cb _history_menu]
 		}
 		"
-	label $w.header.commit \
+	tlabel $w.header.commit \
 		-textvariable @commit \
 		-background gold \
 		-foreground black \
 		-anchor w \
 		-justify left
-	label $w.header.path_l \
+	tlabel $w.header.path_l \
 		-text [mc "File:"] \
 		-background gold \
 		-foreground black \
 		-anchor w \
 		-justify left
 	set w_path $w.header.path
-	label $w_path \
+	tlabel $w_path \
 		-background gold \
 		-foreground black \
 		-anchor w \
@@ -209,16 +210,17 @@
 
 	set w_columns [list $w_amov $w_asim $w_line $w_file]
 
-	scrollbar $w.file_pane.out.sbx \
+	${NS}::scrollbar $w.file_pane.out.sbx \
 		-orient h \
 		-command [list $w_file xview]
-	scrollbar $w.file_pane.out.sby \
+	${NS}::scrollbar $w.file_pane.out.sby \
 		-orient v \
 		-command [list scrollbar2many $w_columns yview]
 	eval grid $w_columns $w.file_pane.out.sby -sticky nsew
 	grid conf \
 		$w.file_pane.out.sbx \
-		-column [expr {[llength $w_columns] - 1}] \
+		-column 0 \
+		-columnspan [expr {[llength $w_columns] + 1}] \
 		-sticky we
 	grid columnconfigure \
 		$w.file_pane.out \
@@ -228,7 +230,14 @@
 
 	set finder [::searchbar::new \
 		$w.file_pane.out.ff $w_file \
-		-column [expr {[llength $w_columns] - 1}] \
+		-column 0 \
+		-columnspan [expr {[llength $w_columns] + 1}] \
+		]
+
+	set gotoline [::linebar::new \
+		$w.file_pane.out.lf $w_file \
+		-column 0 \
+		-columnspan [expr {[llength $w_columns] + 1}] \
 		]
 
 	set w_cviewer $w.file_pane.cm.t
@@ -254,10 +263,10 @@
 		-background $active_color \
 		-font font_ui
 	$w_cviewer tag raise sel
-	scrollbar $w.file_pane.cm.sbx \
+	${NS}::scrollbar $w.file_pane.cm.sbx \
 		-orient h \
 		-command [list $w_cviewer xview]
-	scrollbar $w.file_pane.cm.sby \
+	${NS}::scrollbar $w.file_pane.cm.sby \
 		-orient v \
 		-command [list $w_cviewer yview]
 	pack $w.file_pane.cm.sby -side right -fill y
@@ -274,7 +283,11 @@
 	$w.ctxm add command \
 		-label [mc "Find Text..."] \
 		-accelerator F7 \
-		-command [list searchbar::show $finder]
+		-command [cb _show_finder]
+	$w.ctxm add command \
+		-label [mc "Goto Line..."] \
+		-accelerator "Ctrl-G" \
+		-command [cb _show_linebar]
 	menu $w.ctxm.enc
 	build_encoding_menu $w.ctxm.enc [cb _setencoding]
 	$w.ctxm add cascade \
@@ -341,10 +354,13 @@
 	bind $w_cviewer <Tab>       "[list focus $w_file];break"
 	bind $w_cviewer <Button-1>   [list focus $w_cviewer]
 	bind $w_file    <Visibility> [cb _focus_search $w_file]
-	bind $top       <F7>         [list searchbar::show $finder]
+	bind $top       <F7>         [cb _show_finder]
+	bind $top       <Key-slash>  [cb _show_finder]
+	bind $top    <Control-Key-s> [cb _show_finder]
 	bind $top       <Escape>     [list searchbar::hide $finder]
 	bind $top       <F3>         [list searchbar::find_next $finder]
 	bind $top       <Shift-F3>   [list searchbar::find_prev $finder]
+	bind $top    <Control-Key-g> [cb _show_linebar]
 	catch { bind $top <Shift-Key-XF86_Switch_VT_3> [list searchbar::find_prev $finder] }
 
 	grid configure $w.header -sticky ew
@@ -449,11 +465,28 @@
 
 	$status show [mc "Reading %s..." "$commit:[escape_path $path]"]
 	$w_path conf -text [escape_path $path]
+
+	set do_textconv 0
+	if {![is_config_false gui.textconv] && [git-version >= 1.7.2]} {
+		set filter [gitattr $path diff set]
+		set textconv [get_config [join [list diff $filter textconv] .]]
+		if {$filter ne {set} && $textconv ne {}} {
+			set do_textconv 1
+		}
+	}
 	if {$commit eq {}} {
-		set fd [open $path r]
+		if {$do_textconv ne 0} {
+			set fd [open_cmd_pipe $textconv $path]
+		} else {
+			set fd [open $path r]
+		}
 		fconfigure $fd -eofchar {}
 	} else {
-		set fd [git_read cat-file blob "$commit:$path"]
+		if {$do_textconv ne 0} {
+			set fd [git_read cat-file --textconv "$commit:$path"]
+		} else {
+			set fd [git_read cat-file blob "$commit:$path"]
+		}
 	}
 	fconfigure $fd \
 		-blocking 0 \
@@ -535,7 +568,11 @@
 	foreach i $w_columns {$i conf -state disabled}
 
 	if {[eof $fd]} {
-		close $fd
+		fconfigure $fd -blocking 1; # enable error reporting on close
+		if {[catch {close $fd} err]} {
+			tk_messageBox -icon error -title [mc Error] \
+				-message $err
+		}
 
 		# If we don't force Tk to update the widgets *right now*
 		# none of our jump commands will cause a change in the UI.
@@ -1025,7 +1062,7 @@
 		set radius [get_config gui.blamehistoryctx]
 		set cmdline [list --select-commit=$cmit]
 
-                if {$radius > 0} {
+		if {$radius > 0} {
 			set author_time {}
 			set committer_time {}
 
@@ -1133,7 +1170,7 @@
 	}
 
 	if {[eof $fd]} {
-		close $fd;
+		close $fd
 		set current_fd {}
 
 		_load_new_commit $this  \
@@ -1164,6 +1201,7 @@
 		_hide_tooltip $this
 
 		set tooltip_wm [toplevel $cur_w.tooltip -borderwidth 1]
+		catch {wm attributes $tooltip_wm -type tooltip}
 		wm overrideredirect $tooltip_wm 1
 		wm transient $tooltip_wm [winfo toplevel $cur_w]
 		set tooltip_t $tooltip_wm.label
@@ -1245,6 +1283,18 @@
 
 	$tooltip_t conf -state disabled
 	_position_tooltip $this
+
+	# On MacOS raising a window causes it to acquire focus.
+	# Tk 8.5 on MacOS seems to properly support wm transient,
+	# so we can safely counter the effect there.
+	if {$::have_tk85 && [is_MacOSX]} {
+		update
+		if {$w eq {}} {
+			raise .
+		} else {
+			raise $w
+		}
+	}
 }
 
 method _position_tooltip {} {
@@ -1262,13 +1312,15 @@
 	set pos_y [expr {[winfo pointery .] + 10}]
 
 	set g "${req_w}x${req_h}"
-	if {$pos_x >= 0} {append g +}
+	if {[tk windowingsystem] eq "win32" || $pos_x >= 0} {append g +}
 	append g $pos_x
-	if {$pos_y >= 0} {append g +}
+	if {[tk windowingsystem] eq "win32" || $pos_y >= 0} {append g +}
 	append g $pos_y
 
 	wm geometry $tooltip_wm $g
-	raise $tooltip_wm
+	if {![is_MacOSX]} {
+		raise $tooltip_wm
+	}
 }
 
 method _hide_tooltip {} {
@@ -1298,4 +1350,14 @@
 	set old_height $new_height
 }
 
+method _show_finder {} {
+	linebar::hide $gotoline
+	searchbar::show $finder
+}
+
+method _show_linebar {} {
+	searchbar::hide $finder
+	linebar::show $gotoline
+}
+
 }
diff --git a/git-gui/lib/branch_checkout.tcl b/git-gui/lib/branch_checkout.tcl
index 6603703..2e459a8 100644
--- a/git-gui/lib/branch_checkout.tcl
+++ b/git-gui/lib/branch_checkout.tcl
@@ -10,21 +10,24 @@
 field opt_detach    0; # force a detached head case?
 
 constructor dialog {} {
-	make_toplevel top w
+	global use_ttk NS
+	make_dialog top w
+	wm withdraw $w
 	wm title $top [append "[appname] ([reponame]): " [mc "Checkout Branch"]]
 	if {$top ne {.}} {
 		wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
 	}
 
-	label $w.header -text [mc "Checkout Branch"] -font font_uibold
+	${NS}::label $w.header -text [mc "Checkout Branch"] \
+		-font font_uibold -anchor center
 	pack $w.header -side top -fill x
 
-	frame $w.buttons
-	button $w.buttons.create -text [mc Checkout] \
+	${NS}::frame $w.buttons
+	${NS}::button $w.buttons.create -text [mc Checkout] \
 		-default active \
 		-command [cb _checkout]
 	pack $w.buttons.create -side right
-	button $w.buttons.cancel -text [mc Cancel] \
+	${NS}::button $w.buttons.cancel -text [mc Cancel] \
 		-command [list destroy $w]
 	pack $w.buttons.cancel -side right -padx 5
 	pack $w.buttons -side bottom -fill x -pady 10 -padx 10
@@ -33,14 +36,14 @@
 	$w_rev bind_listbox <Double-Button-1> [cb _checkout]
 	pack $w.rev -anchor nw -fill both -expand 1 -pady 5 -padx 5
 
-	labelframe $w.options -text [mc Options]
+	${NS}::labelframe $w.options -text [mc Options]
 
-	checkbutton $w.options.fetch \
+	${NS}::checkbutton $w.options.fetch \
 		-text [mc "Fetch Tracking Branch"] \
 		-variable @opt_fetch
 	pack $w.options.fetch -anchor nw
 
-	checkbutton $w.options.detach \
+	${NS}::checkbutton $w.options.detach \
 		-text [mc "Detach From Local Branch"] \
 		-variable @opt_detach
 	pack $w.options.detach -anchor nw
@@ -50,6 +53,7 @@
 	bind $w <Visibility> [cb _visible]
 	bind $w <Key-Escape> [list destroy $w]
 	bind $w <Key-Return> [cb _checkout]\;break
+	wm deiconify $w
 	tkwait window $w
 }
 
diff --git a/git-gui/lib/branch_create.tcl b/git-gui/lib/branch_create.tcl
index 3817771..4bb9077 100644
--- a/git-gui/lib/branch_create.tcl
+++ b/git-gui/lib/branch_create.tcl
@@ -16,48 +16,48 @@
 field reset_ok      0; # did the user agree to reset?
 
 constructor dialog {} {
-	global repo_config
+	global repo_config use_ttk NS
 
-	make_toplevel top w
+	make_dialog top w
+	wm withdraw $w
 	wm title $top [append "[appname] ([reponame]): " [mc "Create Branch"]]
 	if {$top ne {.}} {
 		wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
 	}
 
-	label $w.header -text [mc "Create New Branch"] -font font_uibold
+	${NS}::label $w.header -text [mc "Create New Branch"] \
+		-font font_uibold -anchor center
 	pack $w.header -side top -fill x
 
-	frame $w.buttons
-	button $w.buttons.create -text [mc Create] \
+	${NS}::frame $w.buttons
+	${NS}::button $w.buttons.create -text [mc Create] \
 		-default active \
 		-command [cb _create]
 	pack $w.buttons.create -side right
-	button $w.buttons.cancel -text [mc Cancel] \
+	${NS}::button $w.buttons.cancel -text [mc Cancel] \
 		-command [list destroy $w]
 	pack $w.buttons.cancel -side right -padx 5
 	pack $w.buttons -side bottom -fill x -pady 10 -padx 10
 
-	labelframe $w.desc -text [mc "Branch Name"]
-	radiobutton $w.desc.name_r \
-		-anchor w \
+	${NS}::labelframe $w.desc -text [mc "Branch Name"]
+	${NS}::radiobutton $w.desc.name_r \
 		-text [mc "Name:"] \
 		-value user \
 		-variable @name_type
+	if {!$use_ttk} {$w.desc.name_r configure -anchor w}
 	set w_name $w.desc.name_t
-	entry $w_name \
-		-borderwidth 1 \
-		-relief sunken \
+	${NS}::entry $w_name \
 		-width 40 \
 		-textvariable @name \
 		-validate key \
 		-validatecommand [cb _validate %d %S]
 	grid $w.desc.name_r $w_name -sticky we -padx {0 5}
 
-	radiobutton $w.desc.match_r \
-		-anchor w \
+	${NS}::radiobutton $w.desc.match_r \
 		-text [mc "Match Tracking Branch Name"] \
 		-value match \
 		-variable @name_type
+	if {!$use_ttk} {$w.desc.match_r configure -anchor w}
 	grid $w.desc.match_r -sticky we -padx {0 5} -columnspan 2
 
 	grid columnconfigure $w.desc 1 -weight 1
@@ -66,34 +66,34 @@
 	set w_rev [::choose_rev::new $w.rev [mc "Starting Revision"]]
 	pack $w.rev -anchor nw -fill both -expand 1 -pady 5 -padx 5
 
-	labelframe $w.options -text [mc Options]
+	${NS}::labelframe $w.options -text [mc Options]
 
-	frame $w.options.merge
-	label $w.options.merge.l -text [mc "Update Existing Branch:"]
+	${NS}::frame $w.options.merge
+	${NS}::label $w.options.merge.l -text [mc "Update Existing Branch:"]
 	pack $w.options.merge.l -side left
-	radiobutton $w.options.merge.no \
+	${NS}::radiobutton $w.options.merge.no \
 		-text [mc No] \
 		-value none \
 		-variable @opt_merge
 	pack $w.options.merge.no -side left
-	radiobutton $w.options.merge.ff \
+	${NS}::radiobutton $w.options.merge.ff \
 		-text [mc "Fast Forward Only"] \
 		-value ff \
 		-variable @opt_merge
 	pack $w.options.merge.ff -side left
-	radiobutton $w.options.merge.reset \
+	${NS}::radiobutton $w.options.merge.reset \
 		-text [mc Reset] \
 		-value reset \
 		-variable @opt_merge
 	pack $w.options.merge.reset -side left
 	pack $w.options.merge -anchor nw
 
-	checkbutton $w.options.fetch \
+	${NS}::checkbutton $w.options.fetch \
 		-text [mc "Fetch Tracking Branch"] \
 		-variable @opt_fetch
 	pack $w.options.fetch -anchor nw
 
-	checkbutton $w.options.checkout \
+	${NS}::checkbutton $w.options.checkout \
 		-text [mc "Checkout After Creation"] \
 		-variable @opt_checkout
 	pack $w.options.checkout -anchor nw
@@ -109,6 +109,7 @@
 	bind $w <Visibility> [cb _visible]
 	bind $w <Key-Escape> [list destroy $w]
 	bind $w <Key-Return> [cb _create]\;break
+	wm deiconify $w
 	tkwait window $w
 }
 
diff --git a/git-gui/lib/branch_delete.tcl b/git-gui/lib/branch_delete.tcl
index 20d5e42..867938e 100644
--- a/git-gui/lib/branch_delete.tcl
+++ b/git-gui/lib/branch_delete.tcl
@@ -9,41 +9,40 @@
 field w_delete        ; # delete button
 
 constructor dialog {} {
-	global current_branch
+	global current_branch use_ttk NS
 
-	make_toplevel top w
+	make_dialog top w
+	wm withdraw $w
 	wm title $top [append "[appname] ([reponame]): " [mc "Delete Branch"]]
 	if {$top ne {.}} {
 		wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
 	}
 
-	label $w.header -text [mc "Delete Local Branch"] -font font_uibold
+	${NS}::label $w.header -text [mc "Delete Local Branch"] \
+		-font font_uibold -anchor center
 	pack $w.header -side top -fill x
 
-	frame $w.buttons
+	${NS}::frame $w.buttons
 	set w_delete $w.buttons.delete
-	button $w_delete \
+	${NS}::button $w_delete \
 		-text [mc Delete] \
 		-default active \
 		-state disabled \
 		-command [cb _delete]
 	pack $w_delete -side right
-	button $w.buttons.cancel \
+	${NS}::button $w.buttons.cancel \
 		-text [mc Cancel] \
 		-command [list destroy $w]
 	pack $w.buttons.cancel -side right -padx 5
 	pack $w.buttons -side bottom -fill x -pady 10 -padx 10
 
-	labelframe $w.list -text [mc "Local Branches"]
+	${NS}::labelframe $w.list -text [mc "Local Branches"]
 	set w_heads $w.list.l
-	listbox $w_heads \
+	slistbox $w_heads \
 		-height 10 \
 		-width 70 \
 		-selectmode extended \
-		-exportselection false \
-		-yscrollcommand [list $w.list.sby set]
-	scrollbar $w.list.sby -command [list $w.list.l yview]
-	pack $w.list.sby -side right -fill y
+		-exportselection false
 	pack $w.list.l -side left -fill both -expand 1
 	pack $w.list -fill both -expand 1 -pady 5 -padx 5
 
@@ -67,6 +66,7 @@
 	"
 	bind $w <Key-Escape> [list destroy $w]
 	bind $w <Key-Return> [cb _delete]\;break
+	wm deiconify $w
 	tkwait window $w
 }
 
diff --git a/git-gui/lib/branch_rename.tcl b/git-gui/lib/branch_rename.tcl
index 1665388..6e510ec 100644
--- a/git-gui/lib/branch_rename.tcl
+++ b/git-gui/lib/branch_rename.tcl
@@ -8,9 +8,10 @@
 field newname
 
 constructor dialog {} {
-	global current_branch
+	global current_branch use_ttk NS
 
-	make_toplevel top w
+	make_dialog top w
+	wm withdraw $w
 	wm title $top [append "[appname] ([reponame]): " [mc "Rename Branch"]]
 	if {$top ne {.}} {
 		wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
@@ -19,27 +20,31 @@
 	set oldname $current_branch
 	set newname [get_config gui.newbranchtemplate]
 
-	label $w.header -text [mc "Rename Branch"] -font font_uibold
+	${NS}::label $w.header -text [mc "Rename Branch"]\
+		-font font_uibold -anchor center
 	pack $w.header -side top -fill x
 
-	frame $w.buttons
-	button $w.buttons.rename -text [mc Rename] \
+	${NS}::frame $w.buttons
+	${NS}::button $w.buttons.rename -text [mc Rename] \
 		-default active \
 		-command [cb _rename]
 	pack $w.buttons.rename -side right
-	button $w.buttons.cancel -text [mc Cancel] \
+	${NS}::button $w.buttons.cancel -text [mc Cancel] \
 		-command [list destroy $w]
 	pack $w.buttons.cancel -side right -padx 5
 	pack $w.buttons -side bottom -fill x -pady 10 -padx 10
 
-	frame $w.rename
-	label $w.rename.oldname_l -text [mc "Branch:"]
-	eval tk_optionMenu $w.rename.oldname_m @oldname [load_all_heads]
+	${NS}::frame $w.rename
+	${NS}::label $w.rename.oldname_l -text [mc "Branch:"]
+	if {$use_ttk} {
+		ttk::combobox $w.rename.oldname_m -textvariable @oldname \
+			-values [load_all_heads] -state readonly
+	} else {
+		eval tk_optionMenu $w.rename.oldname_m @oldname [load_all_heads]
+	}
 
-	label $w.rename.newname_l -text [mc "New Name:"]
-	entry $w.rename.newname_t \
-		-borderwidth 1 \
-		-relief sunken \
+	${NS}::label $w.rename.newname_l -text [mc "New Name:"]
+	${NS}::entry $w.rename.newname_t \
 		-width 40 \
 		-textvariable @newname \
 		-validate key \
@@ -48,7 +53,7 @@
 			return 1
 		}
 
-	grid $w.rename.oldname_l $w.rename.oldname_m -sticky w  -padx {0 5}
+	grid $w.rename.oldname_l $w.rename.oldname_m -sticky we -padx {0 5}
 	grid $w.rename.newname_l $w.rename.newname_t -sticky we -padx {0 5}
 	grid columnconfigure $w.rename 1 -weight 1
 	pack $w.rename -anchor nw -fill x -pady 5 -padx 5
@@ -60,6 +65,7 @@
 		$w.rename.newname_t icursor end
 		focus $w.rename.newname_t
 	"
+	wm deiconify $w
 	tkwait window $w
 }
 
diff --git a/git-gui/lib/browser.tcl b/git-gui/lib/browser.tcl
index 0410cc6..0328338 100644
--- a/git-gui/lib/browser.tcl
+++ b/git-gui/lib/browser.tcl
@@ -21,23 +21,29 @@
 field ls_buf     {}; # Buffered record output from ls-tree
 
 constructor new {commit {path {}}} {
-	global cursor_ptr M1B
-	make_toplevel top w
+	global cursor_ptr M1B use_ttk NS
+	make_dialog top w
+	wm withdraw $top
 	wm title $top [append "[appname] ([reponame]): " [mc "File Browser"]]
 
-	set browser_commit $commit
-	set browser_path $browser_commit:$path
+	if {$path ne {}} {
+		if {[string index $path end] ne {/}} {
+			append path /
+		}
+	}
 
-	label $w.path \
+	set browser_commit $commit
+	set browser_path "$browser_commit:[escape_path $path]"
+
+	${NS}::label $w.path \
 		-textvariable @browser_path \
 		-anchor w \
 		-justify left \
-		-borderwidth 1 \
-		-relief sunken \
 		-font font_uibold
+	if {!$use_ttk} { $w.path configure -borderwidth 1 -relief sunken}
 	pack $w.path -anchor w -side top -fill x
 
-	frame $w.list
+	${NS}::frame $w.list
 	set w_list $w.list.l
 	text $w_list -background white -foreground black \
 		-borderwidth 0 \
@@ -49,19 +55,18 @@
 		-xscrollcommand [list $w.list.sbx set] \
 		-yscrollcommand [list $w.list.sby set]
 	rmsel_tag $w_list
-	scrollbar $w.list.sbx -orient h -command [list $w_list xview]
-	scrollbar $w.list.sby -orient v -command [list $w_list yview]
+	${NS}::scrollbar $w.list.sbx -orient h -command [list $w_list xview]
+	${NS}::scrollbar $w.list.sby -orient v -command [list $w_list yview]
 	pack $w.list.sbx -side bottom -fill x
 	pack $w.list.sby -side right -fill y
 	pack $w_list -side left -fill both -expand 1
 	pack $w.list -side top -fill both -expand 1
 
-	label $w.status \
+	${NS}::label $w.status \
 		-textvariable @browser_status \
 		-anchor w \
-		-justify left \
-		-borderwidth 1 \
-		-relief sunken
+		-justify left
+	if {!$use_ttk} { $w.status configure -borderwidth 1 -relief sunken}
 	pack $w.status -anchor w -side bottom -fill x
 
 	bind $w_list <Button-1>        "[cb _click 0 @%x,%y];break"
@@ -78,6 +83,7 @@
 	bind $w_list <Right>           break
 
 	bind $w_list <Visibility> [list focus $w_list]
+	wm deiconify $top
 	set w $w_list
 	if {$path ne {}} {
 		_ls $this $browser_commit:$path $path
@@ -121,7 +127,7 @@
 		if {$browser_stack eq {}} {
 			regsub {:.*$} $browser_path {:} browser_path
 		} else {
-			regsub {/[^/]+$} $browser_path {} browser_path
+			regsub {/[^/]+/$} $browser_path {/} browser_path
 		}
 		set browser_status [mc "Loading %s..." $browser_path]
 		_ls $this [lindex $parent 0] [lindex $parent 1]
@@ -263,23 +269,27 @@
 field w_rev          ; # mega-widget to pick the initial revision
 
 constructor dialog {} {
-	make_toplevel top w
+	global use_ttk NS
+	make_dialog top w
+	wm withdraw $top
 	wm title $top [append "[appname] ([reponame]): " [mc "Browse Branch Files"]]
 	if {$top ne {.}} {
 		wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
+		wm transient $top .
 	}
 
-	label $w.header \
+	${NS}::label $w.header \
 		-text [mc "Browse Branch Files"] \
-		-font font_uibold
+		-font font_uibold \
+		-anchor center
 	pack $w.header -side top -fill x
 
-	frame $w.buttons
-	button $w.buttons.browse -text [mc Browse] \
+	${NS}::frame $w.buttons
+	${NS}::button $w.buttons.browse -text [mc Browse] \
 		-default active \
 		-command [cb _open]
 	pack $w.buttons.browse -side right
-	button $w.buttons.cancel -text [mc Cancel] \
+	${NS}::button $w.buttons.cancel -text [mc Cancel] \
 		-command [list destroy $w]
 	pack $w.buttons.cancel -side right -padx 5
 	pack $w.buttons -side bottom -fill x -pady 10 -padx 10
@@ -291,6 +301,7 @@
 	bind $w <Visibility> [cb _visible]
 	bind $w <Key-Escape> [list destroy $w]
 	bind $w <Key-Return> [cb _open]\;break
+	wm deiconify $top
 	tkwait window $w
 }
 
diff --git a/git-gui/lib/choose_font.tcl b/git-gui/lib/choose_font.tcl
index 56443b0..ebe50bd 100644
--- a/git-gui/lib/choose_font.tcl
+++ b/git-gui/lib/choose_font.tcl
@@ -17,6 +17,7 @@
 
 constructor pick {path title a_family a_size} {
 	variable all_families
+	global use_ttk NS
 
 	set v_family $a_family
 	set v_size $a_size
@@ -27,29 +28,30 @@
 	set f_family $pv_family
 	set f_size $pv_size
 
-	make_toplevel top w
+	make_dialog top w
+	wm withdraw $top
 	wm title $top "[appname] ([reponame]): $title"
 	wm geometry $top "+[winfo rootx $path]+[winfo rooty $path]"
 
-	label $w.header -text $title -font font_uibold
+	${NS}::label $w.header -text $title -font font_uibold -anchor center
 	pack $w.header -side top -fill x
 
-	frame $w.buttons
-	button $w.buttons.select \
+	${NS}::frame $w.buttons
+	${NS}::button $w.buttons.select \
 		-text [mc Select] \
 		-default active \
 		-command [cb _select]
-	button $w.buttons.cancel \
+	${NS}::button $w.buttons.cancel \
 		-text [mc Cancel] \
 		-command [list destroy $w]
 	pack $w.buttons.select -side right
 	pack $w.buttons.cancel -side right -padx 5
 	pack $w.buttons -side bottom -fill x -pady 10 -padx 10
 
-	frame $w.inner
+	${NS}::frame $w.inner
 
-	frame $w.inner.family
-	label $w.inner.family.l \
+	${NS}::frame $w.inner.family
+	${NS}::label $w.inner.family.l \
 		-text [mc "Font Family"] \
 		-anchor w
 	set w_family $w.inner.family.v
@@ -64,16 +66,16 @@
 		-height 10 \
 		-yscrollcommand [list $w.inner.family.sby set]
 	rmsel_tag $w_family
-	scrollbar $w.inner.family.sby -command [list $w_family yview]
+	${NS}::scrollbar $w.inner.family.sby -command [list $w_family yview]
 	pack $w.inner.family.l -side top -fill x
 	pack $w.inner.family.sby -side right -fill y
 	pack $w_family -fill both -expand 1
 
-	frame $w.inner.size
-	label $w.inner.size.l \
+	${NS}::frame $w.inner.size
+	${NS}::label $w.inner.size.l \
 		-text [mc "Font Size"] \
 		-anchor w
-	spinbox $w.inner.size.v \
+	tspinbox $w.inner.size.v \
 		-textvariable @f_size \
 		-from 2 -to 80 -increment 1 \
 		-width 3
@@ -86,8 +88,8 @@
 	grid columnconfigure $w.inner 0 -weight 1
 	pack $w.inner -fill both -expand 1 -padx 5 -pady 5
 
-	frame $w.example
-	label $w.example.l \
+	${NS}::frame $w.example
+	${NS}::label $w.example.l \
 		-text [mc "Font Example"] \
 		-anchor w
 	set w_example $w.example.t
@@ -129,6 +131,7 @@
 		grab $w
 		focus $w
 	"
+	wm deiconify $w
 	tkwait window $w
 }
 
diff --git a/git-gui/lib/choose_repository.tcl b/git-gui/lib/choose_repository.tcl
index 633cc57..657f7d5 100644
--- a/git-gui/lib/choose_repository.tcl
+++ b/git-gui/lib/choose_repository.tcl
@@ -22,9 +22,9 @@
 field sorted_recent       ; # recent repositories (sorted)
 
 constructor pick {} {
-	global M1T M1B
+	global M1T M1B use_ttk NS
 
-	make_toplevel top w
+	make_dialog top w
 	wm title $top [mc "Git Gui"]
 
 	if {$top eq {.}} {
@@ -71,11 +71,11 @@
 
 	set w_body $w.body
 	set opts $w_body.options
-	frame $w_body
+	${NS}::frame $w_body
 	text $opts \
 		-cursor $::cursor_ptr \
 		-relief flat \
-		-background [$w_body cget -background] \
+		-background [get_bg_color $w_body] \
 		-wrap none \
 		-spacing1 5 \
 		-width 50 \
@@ -100,12 +100,17 @@
 	$opts insert end [mc "Clone Existing Repository"] link_clone
 	$opts insert end "\n"
 	if {$m_repo ne {}} {
+		if {[tk windowingsystem] eq "win32"} {
+			set key L
+		} else {
+			set key C
+		}
 		$m_repo add command \
 			-command [cb _next clone] \
-			-accelerator $M1T-C \
+			-accelerator $M1T-$key \
 			-label [mc "Clone..."]
-		bind $top <$M1B-c> [cb _next clone]
-		bind $top <$M1B-C> [cb _next clone]
+		bind $top <$M1B-[string tolower $key]> [cb _next clone]
+		bind $top <$M1B-[string toupper $key]> [cb _next clone]
 	}
 
 	$opts tag conf link_open -foreground blue -underline 1
@@ -132,15 +137,15 @@
 				-label [mc "Recent Repositories"]
 		}
 
-		label $w_body.space
-		label $w_body.recentlabel \
+		${NS}::label $w_body.space
+		${NS}::label $w_body.recentlabel \
 			-anchor w \
 			-text [mc "Open Recent Repository:"]
 		set w_recentlist $w_body.recentlist
 		text $w_recentlist \
 			-cursor $::cursor_ptr \
 			-relief flat \
-			-background [$w_body.recentlabel cget -background] \
+			-background [get_bg_color $w_body.recentlabel] \
 			-wrap none \
 			-width 50 \
 			-height 10
@@ -176,10 +181,10 @@
 	}
 	pack $w_body -fill x -padx 10 -pady 10
 
-	frame $w.buttons
+	${NS}::frame $w.buttons
 	set w_next $w.buttons.next
 	set w_quit $w.buttons.quit
-	button $w_quit \
+	${NS}::button $w_quit \
 		-text [mc "Quit"] \
 		-command exit
 	pack $w_quit -side right -padx 5
@@ -203,19 +208,12 @@
 	wm deiconify $top
 	tkwait variable @done
 
+	grab release $top
 	if {$top eq {.}} {
 		eval destroy [winfo children $top]
 	}
 }
 
-proc _home {} {
-	if {[catch {set h $::env(HOME)}]
-		|| ![file isdirectory $h]} {
-		set h .
-	}
-	return $h
-}
-
 method _center {} {
 	set nx [winfo reqwidth $top]
 	set ny [winfo reqheight $top]
@@ -235,6 +233,8 @@
 	foreach p [get_config gui.recentrepo] {
 		if {[_is_git [file join $p .git]]} {
 			lappend recent $p
+		} else {
+			_unset_recentrepo $p
 		}
 	}
 	return [lsort $recent]
@@ -243,6 +243,7 @@
 proc _unset_recentrepo {p} {
 	regsub -all -- {([()\[\]{}\.^$+*?\\])} $p {\\\1} p
 	git config --global --unset gui.recentrepo "^$p\$"
+	load_config 1
 }
 
 proc _append_recentrepos {path} {
@@ -261,6 +262,7 @@
 
 	lappend recent $path
 	git config --global --add gui.recentrepo $path
+	load_config 1
 
 	while {[llength $recent] > 10} {
 		_unset_recentrepo [lindex $recent 0]
@@ -280,9 +282,10 @@
 }
 
 method _next {action} {
+	global NS
 	destroy $w_body
 	if {![winfo exists $w_next]} {
-		button $w_next -default active
+		${NS}::button $w_next -default active
 		pack $w_next -side right -padx 5 -before $w_quit
 	}
 	_do_$action $this
@@ -371,26 +374,25 @@
 ## Create New Repository
 
 method _do_new {} {
+	global use_ttk NS
 	$w_next conf \
 		-state disabled \
 		-command [cb _do_new2] \
 		-text [mc "Create"]
 
-	frame $w_body
-	label $w_body.h \
-		-font font_uibold \
+	${NS}::frame $w_body
+	${NS}::label $w_body.h \
+		-font font_uibold -anchor center \
 		-text [mc "Create New Repository"]
 	pack $w_body.h -side top -fill x -pady 10
 	pack $w_body -fill x -padx 10
 
-	frame $w_body.where
-	label $w_body.where.l -text [mc "Directory:"]
-	entry $w_body.where.t \
+	${NS}::frame $w_body.where
+	${NS}::label $w_body.where.l -text [mc "Directory:"]
+	${NS}::entry $w_body.where.t \
 		-textvariable @local_path \
-		-borderwidth 1 \
-		-relief sunken \
 		-width 50
-	button $w_body.where.b \
+	${NS}::button $w_body.where.b \
 		-text [mc "Browse"] \
 		-command [cb _new_local_path]
 	set w_localpath $w_body.where.t
@@ -410,7 +412,7 @@
 	if {$local_path ne {}} {
 		set p [file dirname $local_path]
 	} else {
-		set p [_home]
+		set p [pwd]
 	}
 
 	set p [tk_chooseDirectory \
@@ -456,63 +458,57 @@
 ## Clone Existing Repository
 
 method _do_clone {} {
+	global use_ttk NS
 	$w_next conf \
 		-state disabled \
 		-command [cb _do_clone2] \
 		-text [mc "Clone"]
 
-	frame $w_body
-	label $w_body.h \
-		-font font_uibold \
+	${NS}::frame $w_body
+	${NS}::label $w_body.h \
+		-font font_uibold -anchor center \
 		-text [mc "Clone Existing Repository"]
 	pack $w_body.h -side top -fill x -pady 10
 	pack $w_body -fill x -padx 10
 
 	set args $w_body.args
-	frame $w_body.args
+	${NS}::frame $w_body.args
 	pack $args -fill both
 
-	label $args.origin_l -text [mc "Source Location:"]
-	entry $args.origin_t \
+	${NS}::label $args.origin_l -text [mc "Source Location:"]
+	${NS}::entry $args.origin_t \
 		-textvariable @origin_url \
-		-borderwidth 1 \
-		-relief sunken \
 		-width 50
-	button $args.origin_b \
+	${NS}::button $args.origin_b \
 		-text [mc "Browse"] \
 		-command [cb _open_origin]
 	grid $args.origin_l $args.origin_t $args.origin_b -sticky ew
 
-	label $args.where_l -text [mc "Target Directory:"]
-	entry $args.where_t \
+	${NS}::label $args.where_l -text [mc "Target Directory:"]
+	${NS}::entry $args.where_t \
 		-textvariable @local_path \
-		-borderwidth 1 \
-		-relief sunken \
 		-width 50
-	button $args.where_b \
+	${NS}::button $args.where_b \
 		-text [mc "Browse"] \
 		-command [cb _new_local_path]
 	grid $args.where_l $args.where_t $args.where_b -sticky ew
 	set w_localpath $args.where_t
 
-	label $args.type_l -text [mc "Clone Type:"]
-	frame $args.type_f
+	${NS}::label $args.type_l -text [mc "Clone Type:"]
+	${NS}::frame $args.type_f
 	set w_types [list]
-	lappend w_types [radiobutton $args.type_f.hardlink \
+	lappend w_types [${NS}::radiobutton $args.type_f.hardlink \
 		-state disabled \
-		-anchor w \
 		-text [mc "Standard (Fast, Semi-Redundant, Hardlinks)"] \
 		-variable @clone_type \
 		-value hardlink]
-	lappend w_types [radiobutton $args.type_f.full \
+	lappend w_types [${NS}::radiobutton $args.type_f.full \
 		-state disabled \
-		-anchor w \
 		-text [mc "Full Copy (Slower, Redundant Backup)"] \
 		-variable @clone_type \
 		-value full]
-	lappend w_types [radiobutton $args.type_f.shared \
+	lappend w_types [${NS}::radiobutton $args.type_f.shared \
 		-state disabled \
-		-anchor w \
 		-text [mc "Shared (Fastest, Not Recommended, No Backup)"] \
 		-variable @clone_type \
 		-value shared]
@@ -537,7 +533,7 @@
 	if {$origin_url ne {} && [file isdirectory $origin_url]} {
 		set p $origin_url
 	} else {
-		set p [_home]
+		set p [pwd]
 	}
 
 	set p [tk_chooseDirectory \
@@ -1001,26 +997,25 @@
 ## Open Existing Repository
 
 method _do_open {} {
+	global NS
 	$w_next conf \
 		-state disabled \
 		-command [cb _do_open2] \
 		-text [mc "Open"]
 
-	frame $w_body
-	label $w_body.h \
-		-font font_uibold \
+	${NS}::frame $w_body
+	${NS}::label $w_body.h \
+		-font font_uibold -anchor center \
 		-text [mc "Open Existing Repository"]
 	pack $w_body.h -side top -fill x -pady 10
 	pack $w_body -fill x -padx 10
 
-	frame $w_body.where
-	label $w_body.where.l -text [mc "Repository:"]
-	entry $w_body.where.t \
+	${NS}::frame $w_body.where
+	${NS}::label $w_body.where.l -text [mc "Repository:"]
+	${NS}::entry $w_body.where.t \
 		-textvariable @local_path \
-		-borderwidth 1 \
-		-relief sunken \
 		-width 50
-	button $w_body.where.b \
+	${NS}::button $w_body.where.b \
 		-text [mc "Browse"] \
 		-command [cb _open_local_path]
 
@@ -1039,7 +1034,7 @@
 	if {$local_path ne {}} {
 		set p $local_path
 	} else {
-		set p [_home]
+		set p [pwd]
 	}
 
 	set p [tk_chooseDirectory \
diff --git a/git-gui/lib/choose_rev.tcl b/git-gui/lib/choose_rev.tcl
index c8821c1..6dae793 100644
--- a/git-gui/lib/choose_rev.tcl
+++ b/git-gui/lib/choose_rev.tcl
@@ -10,7 +10,7 @@
 field w_filter        ; # filter entry for $w_list
 
 field c_expr        {}; # current revision expression
-field filter          ; # current filter string
+field filter        ""; # current filter string
 field revtype     head; # type of revision chosen
 field cur_specs [list]; # list of specs for $revtype
 field spec_head       ; # list of all head specs
@@ -32,7 +32,7 @@
 }
 
 constructor _new {path unmerged_only title} {
-	global current_branch is_detached
+	global current_branch is_detached use_ttk NS
 
 	if {![info exists ::all_remotes]} {
 		load_all_remotes
@@ -41,65 +41,65 @@
 	set w $path
 
 	if {$title ne {}} {
-		labelframe $w -text $title
+		${NS}::labelframe $w -text $title
 	} else {
-		frame $w
+		${NS}::frame $w
 	}
 	bind $w <Destroy> [cb _delete %W]
 
 	if {$is_detached} {
-		radiobutton $w.detachedhead_r \
-			-anchor w \
+		${NS}::radiobutton $w.detachedhead_r \
 			-text [mc "This Detached Checkout"] \
 			-value HEAD \
 			-variable @revtype
+		if {!$use_ttk} {$w.detachedhead_r configure -anchor w}
 		grid $w.detachedhead_r -sticky we -padx {0 5} -columnspan 2
 	}
 
-	radiobutton $w.expr_r \
+	${NS}::radiobutton $w.expr_r \
 		-text [mc "Revision Expression:"] \
 		-value expr \
 		-variable @revtype
-	entry $w.expr_t \
-		-borderwidth 1 \
-		-relief sunken \
+	${NS}::entry $w.expr_t \
 		-width 50 \
 		-textvariable @c_expr \
 		-validate key \
 		-validatecommand [cb _validate %d %S]
 	grid $w.expr_r $w.expr_t -sticky we -padx {0 5}
 
-	frame $w.types
-	radiobutton $w.types.head_r \
+	${NS}::frame $w.types
+	${NS}::radiobutton $w.types.head_r \
 		-text [mc "Local Branch"] \
 		-value head \
 		-variable @revtype
 	pack $w.types.head_r -side left
-	radiobutton $w.types.trck_r \
+	${NS}::radiobutton $w.types.trck_r \
 		-text [mc "Tracking Branch"] \
 		-value trck \
 		-variable @revtype
 	pack $w.types.trck_r -side left
-	radiobutton $w.types.tag_r \
+	${NS}::radiobutton $w.types.tag_r \
 		-text [mc "Tag"] \
 		-value tag \
 		-variable @revtype
 	pack $w.types.tag_r -side left
 	set w_filter $w.types.filter
-	entry $w_filter \
-		-borderwidth 1 \
-		-relief sunken \
+	${NS}::entry $w_filter \
 		-width 12 \
 		-textvariable @filter \
 		-validate key \
 		-validatecommand [cb _filter %P]
 	pack $w_filter -side right
-	pack [label $w.types.filter_icon \
+	pack [${NS}::label $w.types.filter_icon \
 		-image ::choose_rev::img_find \
 		] -side right
 	grid $w.types -sticky we -padx {0 5} -columnspan 2
 
-	frame $w.list
+	if {$use_ttk} {
+		ttk::frame $w.list -style SListbox.TFrame -padding 2
+	} else {
+		frame $w.list
+	}
 	set w_list $w.list.l
 	listbox $w_list \
 		-font font_diff \
@@ -109,6 +109,9 @@
 		-exportselection false \
 		-xscrollcommand [cb _sb_set $w.list.sbx h] \
 		-yscrollcommand [cb _sb_set $w.list.sby v]
+	if {$use_ttk} {
+		$w_list configure -relief flat -highlightthickness 0 -borderwidth 0
+	}
 	pack $w_list -fill both -expand 1
 	grid $w.list -sticky nswe -padx {20 5} -columnspan 2
 	bind $w_list <Any-Motion>  [cb _show_tooltip @%x,%y]
@@ -235,11 +238,12 @@
 }
 
 method none {text} {
+	global NS use_ttk
 	if {![winfo exists $w.none_r]} {
-		radiobutton $w.none_r \
-			-anchor w \
+		${NS}::radiobutton $w.none_r \
 			-value none \
 			-variable @revtype
+		if {!$use_ttk} {$w.none_r configure -anchor w}
 		grid $w.none_r -sticky we -padx {0 5} -columnspan 2
 	}
 	$w.none_r configure -text $text
@@ -425,6 +429,7 @@
 }
 
 method _sb_set {sb orient first last} {
+	global NS
 	set old_focus [focus -lastfor $w]
 
 	if {$first == 0 && $last == 1} {
@@ -440,10 +445,10 @@
 
 	if {![winfo exists $sb]} {
 		if {$orient eq {h}} {
-			scrollbar $sb -orient h -command [list $w_list xview]
+			${NS}::scrollbar $sb -orient h -command [list $w_list xview]
 			pack $sb -fill x -side bottom -before $w_list
 		} else {
-			scrollbar $sb -orient v -command [list $w_list yview]
+			${NS}::scrollbar $sb -orient v -command [list $w_list yview]
 			pack $sb -fill y -side right -before $w_list
 		}
 		if {$old_focus ne {}} {
@@ -492,6 +497,7 @@
 
 	if {$tooltip_wm eq {}} {
 		set tooltip_wm [toplevel $w_list.tooltip -borderwidth 1]
+		catch {wm attributes $tooltip_wm -type tooltip}
 		wm overrideredirect $tooltip_wm 1
 		wm transient $tooltip_wm [winfo toplevel $w_list]
 		set tooltip_t $tooltip_wm.label
@@ -605,9 +611,9 @@
 	set pos_y [expr {[winfo pointery .] + 10}]
 
 	set g "${req_w}x${req_h}"
-	if {$pos_x >= 0} {append g +}
+	if {[tk windowingsystem] eq "win32" || $pos_x >= 0} {append g +}
 	append g $pos_x
-	if {$pos_y >= 0} {append g +}
+	if {[tk windowingsystem] eq "win32" || $pos_y >= 0} {append g +}
 	append g $pos_y
 
 	wm geometry $tooltip_wm $g
diff --git a/git-gui/lib/class.tcl b/git-gui/lib/class.tcl
index dc21411..f08506f 100644
--- a/git-gui/lib/class.tcl
+++ b/git-gui/lib/class.tcl
@@ -134,6 +134,14 @@
 	if {[namespace exists $t]} {namespace delete $t}
 }
 
+proc make_dialog {t w args} {
+	upvar $t top $w pfx this this
+	global use_ttk
+	uplevel [linsert $args 0 make_toplevel $t $w]
+	catch {wm attributes $top -type dialog}
+	pave_toplevel $pfx
+}
+
 proc make_toplevel {t w args} {
 	upvar $t top $w pfx this this
 
diff --git a/git-gui/lib/commit.tcl b/git-gui/lib/commit.tcl
index 7f459cd..0d81432 100644
--- a/git-gui/lib/commit.tcl
+++ b/git-gui/lib/commit.tcl
@@ -161,11 +161,12 @@
 	#
 	set files_ready 0
 	foreach path [array names file_states] {
-		switch -glob -- [lindex $file_states($path) 0] {
+		set s $file_states($path)
+		switch -glob -- [lindex $s 0] {
 		_? {continue}
 		A? -
 		D? -
-		T_ -
+		T? -
 		M? {set files_ready 1}
 		_U -
 		U? {
@@ -259,8 +260,25 @@
 }
 
 proc commit_commitmsg {curHEAD msg_p} {
+	global is_detached repo_config
 	global pch_error
 
+	if {$is_detached
+	    && ![file exists [gitdir rebase-merge head-name]]
+	    && 	[is_config_true gui.warndetachedcommit]} {
+		set msg [mc "You are about to commit on a detached head.\
+This is a potentially dangerous thing to do because if you switch\
+to another branch you will loose your changes and it can be difficult\
+to retrieve them later from the reflog. You should probably cancel this\
+commit and create a new branch to continue.\n\
+\n\
+Do you really want to proceed with your Commit?"]
+		if {[ask_popup $msg] ne yes} {
+			unlock_index
+			return
+		}
+	}
+
 	# -- Run the commit-msg hook.
 	#
 	set fd_ph [githook_read commit-msg $msg_p]
@@ -452,7 +470,11 @@
 		}
 		AM -
 		AD -
+		AT -
+		TM -
+		TD -
 		MM -
+		MT -
 		MD {
 			set file_states($path) [list \
 				_[string index $m 1] \
diff --git a/git-gui/lib/console.tcl b/git-gui/lib/console.tcl
index c112464..1f3248f 100644
--- a/git-gui/lib/console.tcl
+++ b/git-gui/lib/console.tcl
@@ -27,20 +27,20 @@
 }
 
 method _init {} {
-	global M1B
+	global M1B use_ttk NS
 
 	if {$is_toplevel} {
-		make_toplevel top w -autodelete 0
+		make_dialog top w -autodelete 0
 		wm title $top "[appname] ([reponame]): $t_short"
 	} else {
-		frame $w
+		${NS}::frame $w
 	}
 
 	set console_cr 1.0
 	set w_t $w.m.t
 
-	frame $w.m
-	label $w.m.l1 \
+	${NS}::frame $w.m
+	${NS}::label $w.m.l1 \
 		-textvariable @t_long  \
 		-anchor w \
 		-justify left \
@@ -78,7 +78,7 @@
 		"
 
 	if {$is_toplevel} {
-		button $w.ok -text [mc "Close"] \
+		${NS}::button $w.ok -text [mc "Close"] \
 			-state disabled \
 			-command [list destroy $w]
 		pack $w.ok -side bottom -anchor e -pady 10 -padx 10
@@ -206,13 +206,14 @@
 }
 
 method _sb_set {sb orient first last} {
+	global NS
 	if {![winfo exists $sb]} {
 		if {$first == $last || ($first == 0 && $last == 1)} return
 		if {$orient eq {h}} {
-			scrollbar $sb -orient h -command [list $w_t xview]
+			${NS}::scrollbar $sb -orient h -command [list $w_t xview]
 			pack $sb -fill x -side bottom -before $w_t
 		} else {
-			scrollbar $sb -orient v -command [list $w_t yview]
+			${NS}::scrollbar $sb -orient v -command [list $w_t yview]
 			pack $sb -fill y -side right -before $w_t
 		}
 	}
diff --git a/git-gui/lib/database.tcl b/git-gui/lib/database.tcl
index a18ac8b..1f187ed 100644
--- a/git-gui/lib/database.tcl
+++ b/git-gui/lib/database.tcl
@@ -2,6 +2,7 @@
 # Copyright (C) 2006, 2007 Shawn Pearce
 
 proc do_stats {} {
+	global use_ttk NS
 	set fd [git_read count-objects -v]
 	while {[gets $fd line] > 0} {
 		if {[regexp {^([^:]+): (\d+)$} $line _ name value]} {
@@ -21,24 +22,22 @@
 	}
 
 	set w .stats_view
-	toplevel $w
+	Dialog $w
+	wm withdraw $w
 	wm geometry $w "+[winfo rootx .]+[winfo rooty .]"
 
-	label $w.header -text [mc "Database Statistics"]
-	pack $w.header -side top -fill x
-
-	frame $w.buttons -border 1
-	button $w.buttons.close -text [mc Close] \
+	${NS}::frame $w.buttons
+	${NS}::button $w.buttons.close -text [mc Close] \
 		-default active \
 		-command [list destroy $w]
-	button $w.buttons.gc -text [mc "Compress Database"] \
+	${NS}::button $w.buttons.gc -text [mc "Compress Database"] \
 		-default normal \
 		-command "destroy $w;do_gc"
 	pack $w.buttons.close -side right
 	pack $w.buttons.gc -side left
 	pack $w.buttons -side bottom -fill x -pady 10 -padx 10
 
-	frame $w.stat -borderwidth 1 -relief solid
+	${NS}::labelframe $w.stat -text [mc "Database Statistics"]
 	foreach s {
 		{count           {mc "Number of loose objects"}}
 		{size            {mc "Disk space used by loose objects"} { KiB}}
@@ -55,8 +54,8 @@
 			set value "$value[lindex $s 2]"
 		}
 
-		label $w.stat.l_$name -text "$label:" -anchor w
-		label $w.stat.v_$name -text $value -anchor w
+		${NS}::label $w.stat.l_$name -text "$label:" -anchor w
+		${NS}::label $w.stat.v_$name -text $value -anchor w
 		grid $w.stat.l_$name $w.stat.v_$name -sticky we -padx {0 5}
 	}
 	pack $w.stat -pady 10 -padx 10
@@ -65,6 +64,7 @@
 	bind $w <Key-Escape> [list destroy $w]
 	bind $w <Key-Return> [list destroy $w]
 	wm title $w [append "[appname] ([reponame]): " [mc "Database Statistics"]]
+	wm deiconify $w
 	tkwait window $w
 }
 
@@ -89,27 +89,26 @@
 }
 
 proc hint_gc {} {
-	set object_limit 8
+	set ndirs 1
+	set limit 8
 	if {[is_Windows]} {
-		set object_limit 1
+		set ndirs 4
+		set limit 1
 	}
 
-	set objects_current [llength [glob \
-		-directory [gitdir objects 42] \
+	set count [llength [glob \
 		-nocomplain \
-		-tails \
 		-- \
-		*]]
+		[gitdir objects 4\[0-[expr {$ndirs-1}]\]/*]]]
 
-	if {$objects_current >= $object_limit} {
-		set objects_current [expr {$objects_current * 250}]
-		set object_limit    [expr {$object_limit    * 250}]
+	if {$count >= $limit * $ndirs} {
+		set objects_current [expr {$count * 256/$ndirs}]
 		if {[ask_popup \
 			[mc "This repository currently has approximately %i loose objects.
 
-To maintain optimal performance it is strongly recommended that you compress the database when more than %i loose objects exist.
+To maintain optimal performance it is strongly recommended that you compress the database.
 
-Compress the database now?" $objects_current $object_limit]] eq yes} {
+Compress the database now?" $objects_current]] eq yes} {
 			do_gc
 		}
 	}
diff --git a/git-gui/lib/diff.tcl b/git-gui/lib/diff.tcl
index 925b3f5..ec44055 100644
--- a/git-gui/lib/diff.tcl
+++ b/git-gui/lib/diff.tcl
@@ -55,7 +55,7 @@
 
 	set path $current_diff_path
 	set s $file_states($path)
-	if {[lindex $s 0] ne {_M}} return
+	if {[lindex $s 0] ne {_M} || [has_textconv $path]} return
 
 	# Prevent infinite rescan loops
 	incr diff_empty_count
@@ -122,22 +122,22 @@
 	if {$merge_stages(2) eq {}} {
 		set is_conflict_diff 1
 		lappend current_diff_queue \
-			[list [mc "LOCAL: deleted\nREMOTE:\n"] d======= \
+			[list [mc "LOCAL: deleted\nREMOTE:\n"] d= \
 			    [list ":1:$current_diff_path" ":3:$current_diff_path"]]
 	} elseif {$merge_stages(3) eq {}} {
 		set is_conflict_diff 1
 		lappend current_diff_queue \
-			[list [mc "REMOTE: deleted\nLOCAL:\n"] d======= \
+			[list [mc "REMOTE: deleted\nLOCAL:\n"] d= \
 			    [list ":1:$current_diff_path" ":2:$current_diff_path"]]
 	} elseif {[lindex $merge_stages(1) 0] eq {120000}
 		|| [lindex $merge_stages(2) 0] eq {120000}
 		|| [lindex $merge_stages(3) 0] eq {120000}} {
 		set is_conflict_diff 1
 		lappend current_diff_queue \
-			[list [mc "LOCAL:\n"] d======= \
+			[list [mc "LOCAL:\n"] d= \
 			    [list ":1:$current_diff_path" ":2:$current_diff_path"]]
 		lappend current_diff_queue \
-			[list [mc "REMOTE:\n"] d======= \
+			[list [mc "REMOTE:\n"] d= \
 			    [list ":1:$current_diff_path" ":3:$current_diff_path"]]
 	} else {
 		start_show_diff $cont_info
@@ -208,32 +208,32 @@
 			$ui_diff insert end [append \
 				"* " \
 				[mc "Git Repository (subproject)"] \
-				"\n"] d_@
+				"\n"] d_info
 		} elseif {![catch {set type [exec file $path]}]} {
 			set n [string length $path]
 			if {[string equal -length $n $path $type]} {
 				set type [string range $type $n end]
 				regsub {^:?\s*} $type {} type
 			}
-			$ui_diff insert end "* $type\n" d_@
+			$ui_diff insert end "* $type\n" d_info
 		}
 		if {[string first "\0" $content] != -1} {
 			$ui_diff insert end \
 				[mc "* Binary file (not showing content)."] \
-				d_@
+				d_info
 		} else {
 			if {$sz > $max_sz} {
 				$ui_diff insert end [mc \
 "* Untracked file is %d bytes.
 * Showing only first %d bytes.
-" $sz $max_sz] d_@
+" $sz $max_sz] d_info
 			}
 			$ui_diff insert end $content
 			if {$sz > $max_sz} {
 				$ui_diff insert end [mc "
 * Untracked file clipped here by %s.
 * To see the entire file, use an external editor.
-" [appname]] d_@
+" [appname]] d_info
 			}
 		}
 		$ui_diff conf -state disabled
@@ -253,9 +253,22 @@
 	}
 }
 
+proc get_conflict_marker_size {path} {
+	set size 7
+	catch {
+		set fd_rc [eval [list git_read check-attr "conflict-marker-size" -- $path]]
+		set ret [gets $fd_rc line]
+		close $fd_rc
+		if {$ret > 0} {
+			regexp {.*: conflict-marker-size: (\d+)$} $line line size
+		}
+	}
+	return $size
+}
+
 proc start_show_diff {cont_info {add_opts {}}} {
 	global file_states file_lists
-	global is_3way_diff diff_active repo_config
+	global is_3way_diff is_submodule_diff diff_active repo_config
 	global ui_diff ui_index ui_workdir
 	global current_diff_path current_diff_side current_diff_header
 
@@ -265,8 +278,10 @@
 	set s $file_states($path)
 	set m [lindex $s 0]
 	set is_3way_diff 0
+	set is_submodule_diff 0
 	set diff_active 1
 	set current_diff_header {}
+	set conflict_size [get_conflict_marker_size $path]
 
 	set cmd [list]
 	if {$w eq $ui_index} {
@@ -279,9 +294,22 @@
 			lappend cmd diff-files
 		}
 	}
+	if {![is_config_false gui.textconv] && [git-version >= 1.6.1]} {
+		lappend cmd --textconv
+	}
+
+	if {[string match {160000 *} [lindex $s 2]]
+	 || [string match {160000 *} [lindex $s 3]]} {
+		set is_submodule_diff 1
+
+		if {[git-version >= "1.6.6"]} {
+			lappend cmd --submodule
+		}
+	}
 
 	lappend cmd -p
-	lappend cmd --no-color
+	lappend cmd --color
+	set cmd [concat $cmd $repo_config(gui.diffopts)]
 	if {$repo_config(gui.diffcontext) >= 1} {
 		lappend cmd "-U$repo_config(gui.diffcontext)"
 	}
@@ -295,6 +323,14 @@
 		lappend cmd $path
 	}
 
+	if {$is_submodule_diff && [git-version < "1.6.6"]} {
+		if {$w eq $ui_index} {
+			set cmd [list submodule summary --cached -- $path]
+		} else {
+			set cmd [list submodule summary --files -- $path]
+		}
+	}
+
 	if {[catch {set fd [eval git_read --nice $cmd]} err]} {
 		set diff_active 0
 		unlock_index
@@ -308,48 +344,89 @@
 		-blocking 0 \
 		-encoding [get_path_encoding $path] \
 		-translation lf
-	fileevent $fd readable [list read_diff $fd $cont_info]
+	fileevent $fd readable [list read_diff $fd $conflict_size $cont_info]
 }
 
-proc read_diff {fd cont_info} {
-	global ui_diff diff_active
+proc parse_color_line {line} {
+	set start 0
+	set result ""
+	set markup [list]
+	set regexp {\033\[((?:\d+;)*\d+)?m}
+	set need_reset 0
+	while {[regexp -indices -start $start $regexp $line match code]} {
+		foreach {begin end} $match break
+		append result [string range $line $start [expr {$begin - 1}]]
+		set pos [string length $result]
+		set col [eval [linsert $code 0 string range $line]]
+		set start [incr end]
+		if {$col eq "0" || $col eq ""} {
+			if {!$need_reset} continue
+			set need_reset 0
+		} else {
+			set need_reset 1
+		}
+		lappend markup $pos $col
+	}
+	append result [string range $line $start end]
+	if {[llength $markup] < 4} {set markup {}}
+	return [list $result $markup]
+}
+
+proc read_diff {fd conflict_size cont_info} {
+	global ui_diff diff_active is_submodule_diff
 	global is_3way_diff is_conflict_diff current_diff_header
 	global current_diff_queue
 	global diff_empty_count
 
 	$ui_diff conf -state normal
 	while {[gets $fd line] >= 0} {
-		# -- Cleanup uninteresting diff header lines.
+		foreach {line markup} [parse_color_line $line] break
+		set line [string map {\033 ^} $line]
+
+		set tags {}
+
+		# -- Check for start of diff header.
+		if {   [string match {diff --git *}      $line]
+		    || [string match {diff --cc *}       $line]
+		    || [string match {diff --combined *} $line]} {
+			set ::current_diff_inheader 1
+		}
+
+		# -- Check for end of diff header (any hunk line will do this).
 		#
-		if {$::current_diff_inheader} {
-			if {   [string match {diff --git *}      $line]
-			    || [string match {diff --cc *}       $line]
-			    || [string match {diff --combined *} $line]
-			    || [string match {--- *}             $line]
-			    || [string match {+++ *}             $line]} {
-				append current_diff_header $line "\n"
-				continue
-			}
-		}
-		if {[string match {index *} $line]} continue
-		if {$line eq {deleted file mode 120000}} {
-			set line "deleted symlink"
-		}
-		set ::current_diff_inheader 0
+		if {[regexp {^@@+ } $line]} {set ::current_diff_inheader 0}
 
 		# -- Automatically detect if this is a 3 way diff.
 		#
 		if {[string match {@@@ *} $line]} {set is_3way_diff 1}
 
-		if {[string match {mode *} $line]
-			|| [string match {new file *} $line]
-			|| [regexp {^(old|new) mode *} $line]
-			|| [string match {deleted file *} $line]
-			|| [string match {deleted symlink} $line]
-			|| [string match {Binary files * and * differ} $line]
-			|| $line eq {\ No newline at end of file}
-			|| [regexp {^\* Unmerged path } $line]} {
-			set tags {}
+		if {$::current_diff_inheader} {
+
+			# -- These two lines stop a diff header and shouldn't be in there
+			if {   [string match {Binary files * and * differ} $line]
+			    || [regexp {^\* Unmerged path }                $line]} {
+				set ::current_diff_inheader 0
+			} else {
+				append current_diff_header $line "\n"
+			}
+
+			# -- Cleanup uninteresting diff header lines.
+			#
+			if {   [string match {diff --git *}      $line]
+			    || [string match {diff --cc *}       $line]
+			    || [string match {diff --combined *} $line]
+			    || [string match {--- *}             $line]
+			    || [string match {+++ *}             $line]
+			    || [string match {index *}           $line]} {
+				continue
+			}
+
+			# -- Name it symlink, not 120000
+			#    Note, that the original line is in $current_diff_header
+			regsub {^(deleted|new) file mode 120000} $line {\1 symlink} line
+
+		} elseif {   $line eq {\ No newline at end of file}} {
+			# -- Handle some special lines
 		} elseif {$is_3way_diff} {
 			set op [string range $line 0 1]
 			switch -- $op {
@@ -361,7 +438,9 @@
 			{- } {set tags d_-s}
 			{--} {set tags d_--}
 			{++} {
-				if {[regexp {^\+\+([<>]{7} |={7})} $line _g op]} {
+				set regexp [string map [list %conflict_size $conflict_size]\
+								{^\+\+([<>=]){%conflict_size}(?: |$)}]
+				if {[regexp $regexp $line _g op]} {
 					set is_conflict_diff 1
 					set line [string replace $line 0 1 {  }]
 					set tags d$op
@@ -374,6 +453,25 @@
 				set tags {}
 			}
 			}
+		} elseif {$is_submodule_diff} {
+			if {$line == ""} continue
+			if {[regexp {^Submodule } $line]} {
+				set tags d_info
+			} elseif {[regexp {^\* } $line]} {
+				set line [string replace $line 0 1 {Submodule }]
+				set tags d_info
+			} else {
+				set op [string range $line 0 2]
+				switch -- $op {
+				{  <} {set tags d_-}
+				{  >} {set tags d_+}
+				{  W} {set tags {}}
+				default {
+					puts "error: Unhandled submodule diff marker: {$op}"
+					set tags {}
+				}
+				}
+			}
 		} else {
 			set op [string index $line 0]
 			switch -- $op {
@@ -381,7 +479,9 @@
 			{@} {set tags d_@}
 			{-} {set tags d_-}
 			{+} {
-				if {[regexp {^\+([<>]{7} |={7})} $line _g op]} {
+				set regexp [string map [list %conflict_size $conflict_size]\
+								{^\+([<>=]){%conflict_size}(?: |$)}]
+				if {[regexp $regexp $line _g op]} {
 					set is_conflict_diff 1
 					set tags d$op
 				} else {
@@ -394,11 +494,23 @@
 			}
 			}
 		}
+		set mark [$ui_diff index "end - 1 line linestart"]
 		$ui_diff insert end $line $tags
 		if {[string index $line end] eq "\r"} {
 			$ui_diff tag add d_cr {end - 2c}
 		}
 		$ui_diff insert end "\n" $tags
+
+		foreach {posbegin colbegin posend colend} $markup {
+			set prefix clr
+			foreach style [lsort -integer [split $colbegin ";"]] {
+				if {$style eq "7"} {append prefix i; continue}
+				if {$style != 4 && ($style < 30 || $style > 47)} {continue}
+				set a "$mark linestart + $posbegin chars"
+				set b "$mark linestart + $posend chars"
+				catch {$ui_diff tag add $prefix$style $a $b}
+			}
+		}
 	}
 	$ui_diff conf -state disabled
 
@@ -505,10 +617,23 @@
 	}
 }
 
-proc apply_line {x y} {
+proc apply_range_or_line {x y} {
 	global current_diff_path current_diff_header current_diff_side
 	global ui_diff ui_index file_states
 
+	set selected [$ui_diff tag nextrange sel 0.0]
+
+	if {$selected == {}} {
+		set first [$ui_diff index "@$x,$y"]
+		set last $first
+	} else {
+		set first [lindex $selected 0]
+		set last [lindex $selected 1]
+	}
+
+	set first_l [$ui_diff index "$first linestart"]
+	set last_l [$ui_diff index "$last lineend"]
+
 	if {$current_diff_path eq {} || $current_diff_header eq {}} return
 	if {![lock_index apply_hunk]} return
 
@@ -531,119 +656,147 @@
 		}
 	}
 
-	set the_l [$ui_diff index @$x,$y]
+	set wholepatch {}
 
-	# operate only on change lines
-	set c1 [$ui_diff get "$the_l linestart"]
-	if {$c1 ne {+} && $c1 ne {-}} {
-		unlock_index
-		return
-	}
-	set sign $c1
-
-	set i_l [$ui_diff search -backwards -regexp ^@@ $the_l 0.0]
-	if {$i_l eq {}} {
-		unlock_index
-		return
-	}
-	# $i_l is now at the beginning of a line
-
-	# pick start line number from hunk header
-	set hh [$ui_diff get $i_l "$i_l + 1 lines"]
-	set hh [lindex [split $hh ,] 0]
-	set hln [lindex [split $hh -] 1]
-
-	# There is a special situation to take care of. Consider this hunk:
-	#
-	#    @@ -10,4 +10,4 @@
-	#     context before
-	#    -old 1
-	#    -old 2
-	#    +new 1
-	#    +new 2
-	#     context after
-	#
-	# We used to keep the context lines in the order they appear in the
-	# hunk. But then it is not possible to correctly stage only
-	# "-old 1" and "+new 1" - it would result in this staged text:
-	#
-	#    context before
-	#    old 2
-	#    new 1
-	#    context after
-	#
-	# (By symmetry it is not possible to *un*stage "old 2" and "new 2".)
-	#
-	# We resolve the problem by introducing an asymmetry, namely, when
-	# a "+" line is *staged*, it is moved in front of the context lines
-	# that are generated from the "-" lines that are immediately before
-	# the "+" block. That is, we construct this patch:
-	#
-	#    @@ -10,4 +10,5 @@
-	#     context before
-	#    +new 1
-	#     old 1
-	#     old 2
-	#     context after
-	#
-	# But we do *not* treat "-" lines that are *un*staged in a special
-	# way.
-	#
-	# With this asymmetry it is possible to stage the change
-	# "old 1" -> "new 1" directly, and to stage the change
-	# "old 2" -> "new 2" by first staging the entire hunk and
-	# then unstaging the change "old 1" -> "new 1".
-
-	# This is non-empty if and only if we are _staging_ changes;
-	# then it accumulates the consecutive "-" lines (after converting
-	# them to context lines) in order to be moved after the "+" change
-	# line.
-	set pre_context {}
-
-	set n 0
-	set i_l [$ui_diff index "$i_l + 1 lines"]
-	set patch {}
-	while {[$ui_diff compare $i_l < "end - 1 chars"] &&
-	       [$ui_diff get $i_l "$i_l + 2 chars"] ne {@@}} {
-		set next_l [$ui_diff index "$i_l + 1 lines"]
-		set c1 [$ui_diff get $i_l]
-		if {[$ui_diff compare $i_l <= $the_l] &&
-		    [$ui_diff compare $the_l < $next_l]} {
-			# the line to stage/unstage
-			set ln [$ui_diff get $i_l $next_l]
-			if {$c1 eq {-}} {
-				set n [expr $n+1]
-				set patch "$patch$pre_context$ln"
-			} else {
-				set patch "$patch$ln$pre_context"
-			}
-			set pre_context {}
-		} elseif {$c1 ne {-} && $c1 ne {+}} {
-			# context line
-			set ln [$ui_diff get $i_l $next_l]
-			set patch "$patch$pre_context$ln"
-			set n [expr $n+1]
-			set pre_context {}
-		} elseif {$c1 eq $to_context} {
-			# turn change line into context line
-			set ln [$ui_diff get "$i_l + 1 chars" $next_l]
-			if {$c1 eq {-}} {
-				set pre_context "$pre_context $ln"
-			} else {
-				set patch "$patch $ln"
-			}
-			set n [expr $n+1]
+	while {$first_l < $last_l} {
+		set i_l [$ui_diff search -backwards -regexp ^@@ $first_l 0.0]
+		if {$i_l eq {}} {
+			# If there's not a @@ above, then the selected range
+			# must have come before the first_l @@
+			set i_l [$ui_diff search -regexp ^@@ $first_l $last_l]
 		}
-		set i_l $next_l
+		if {$i_l eq {}} {
+			unlock_index
+			return
+		}
+		# $i_l is now at the beginning of a line
+
+		# pick start line number from hunk header
+		set hh [$ui_diff get $i_l "$i_l + 1 lines"]
+		set hh [lindex [split $hh ,] 0]
+		set hln [lindex [split $hh -] 1]
+
+		# There is a special situation to take care of. Consider this
+		# hunk:
+		#
+		#    @@ -10,4 +10,4 @@
+		#     context before
+		#    -old 1
+		#    -old 2
+		#    +new 1
+		#    +new 2
+		#     context after
+		#
+		# We used to keep the context lines in the order they appear in
+		# the hunk. But then it is not possible to correctly stage only
+		# "-old 1" and "+new 1" - it would result in this staged text:
+		#
+		#    context before
+		#    old 2
+		#    new 1
+		#    context after
+		#
+		# (By symmetry it is not possible to *un*stage "old 2" and "new
+		# 2".)
+		#
+		# We resolve the problem by introducing an asymmetry, namely,
+		# when a "+" line is *staged*, it is moved in front of the
+		# context lines that are generated from the "-" lines that are
+		# immediately before the "+" block. That is, we construct this
+		# patch:
+		#
+		#    @@ -10,4 +10,5 @@
+		#     context before
+		#    +new 1
+		#     old 1
+		#     old 2
+		#     context after
+		#
+		# But we do *not* treat "-" lines that are *un*staged in a
+		# special way.
+		#
+		# With this asymmetry it is possible to stage the change "old
+		# 1" -> "new 1" directly, and to stage the change "old 2" ->
+		# "new 2" by first staging the entire hunk and then unstaging
+		# the change "old 1" -> "new 1".
+		#
+		# Applying multiple lines adds complexity to the special
+		# situation.  The pre_context must be moved after the entire
+		# first block of consecutive staged "+" lines, so that
+		# staging both additions gives the following patch:
+		#
+		#    @@ -10,4 +10,6 @@
+		#     context before
+		#    +new 1
+		#    +new 2
+		#     old 1
+		#     old 2
+		#     context after
+
+		# This is non-empty if and only if we are _staging_ changes;
+		# then it accumulates the consecutive "-" lines (after
+		# converting them to context lines) in order to be moved after
+		# "+" change lines.
+		set pre_context {}
+
+		set n 0
+		set m 0
+		set i_l [$ui_diff index "$i_l + 1 lines"]
+		set patch {}
+		while {[$ui_diff compare $i_l < "end - 1 chars"] &&
+		       [$ui_diff get $i_l "$i_l + 2 chars"] ne {@@}} {
+			set next_l [$ui_diff index "$i_l + 1 lines"]
+			set c1 [$ui_diff get $i_l]
+			if {[$ui_diff compare $first_l <= $i_l] &&
+			    [$ui_diff compare $i_l < $last_l] &&
+			    ($c1 eq {-} || $c1 eq {+})} {
+				# a line to stage/unstage
+				set ln [$ui_diff get $i_l $next_l]
+				if {$c1 eq {-}} {
+					set n [expr $n+1]
+					set patch "$patch$pre_context$ln"
+					set pre_context {}
+				} else {
+					set m [expr $m+1]
+					set patch "$patch$ln"
+				}
+			} elseif {$c1 ne {-} && $c1 ne {+}} {
+				# context line
+				set ln [$ui_diff get $i_l $next_l]
+				set patch "$patch$pre_context$ln"
+				set n [expr $n+1]
+				set m [expr $m+1]
+				set pre_context {}
+			} elseif {$c1 eq $to_context} {
+				# turn change line into context line
+				set ln [$ui_diff get "$i_l + 1 chars" $next_l]
+				if {$c1 eq {-}} {
+					set pre_context "$pre_context $ln"
+				} else {
+					set patch "$patch $ln"
+				}
+				set n [expr $n+1]
+				set m [expr $m+1]
+			} else {
+				# a change in the opposite direction of
+				# to_context which is outside the range of
+				# lines to apply.
+				set patch "$patch$pre_context"
+				set pre_context {}
+			}
+			set i_l $next_l
+		}
+		set patch "$patch$pre_context"
+		set wholepatch "$wholepatch@@ -$hln,$n +$hln,$m @@\n$patch"
+		set first_l [$ui_diff index "$next_l + 1 lines"]
 	}
-	set patch "@@ -$hln,$n +$hln,[eval expr $n $sign 1] @@\n$patch"
 
 	if {[catch {
 		set enc [get_path_encoding $current_diff_path]
 		set p [eval git_write $apply_cmd]
 		fconfigure $p -translation binary -encoding $enc
 		puts -nonewline $p $current_diff_header
-		puts -nonewline $p $patch
+		puts -nonewline $p $wholepatch
 		close $p} err]} {
 		error_popup [append $failed_msg "\n\n$err"]
 	}
diff --git a/git-gui/lib/error.tcl b/git-gui/lib/error.tcl
index 7565015..c0fa69a 100644
--- a/git-gui/lib/error.tcl
+++ b/git-gui/lib/error.tcl
@@ -71,11 +71,13 @@
 }
 
 proc hook_failed_popup {hook msg {is_fatal 1}} {
+	global use_ttk NS
 	set w .hookfail
-	toplevel $w
+	Dialog $w
+	wm withdraw $w
 
-	frame $w.m
-	label $w.m.l1 -text "$hook hook failed:" \
+	${NS}::frame $w.m
+	${NS}::label $w.m.l1 -text "$hook hook failed:" \
 		-anchor w \
 		-justify left \
 		-font font_uibold
@@ -87,10 +89,10 @@
 		-width 80 -height 10 \
 		-font font_diff \
 		-yscrollcommand [list $w.m.sby set]
-	scrollbar $w.m.sby -command [list $w.m.t yview]
+	${NS}::scrollbar $w.m.sby -command [list $w.m.t yview]
 	pack $w.m.l1 -side top -fill x
 	if {$is_fatal} {
-		label $w.m.l2 \
+		${NS}::label $w.m.l2 \
 			-text [mc "You must correct the above errors before committing."] \
 			-anchor w \
 			-justify left \
@@ -104,7 +106,7 @@
 	$w.m.t insert 1.0 $msg
 	$w.m.t conf -state disabled
 
-	button $w.ok -text OK \
+	${NS}::button $w.ok -text OK \
 		-width 15 \
 		-command "destroy $w"
 	pack $w.ok -side bottom -anchor e -pady 10 -padx 10
@@ -112,5 +114,6 @@
 	bind $w <Visibility> "grab $w; focus $w"
 	bind $w <Key-Return> "destroy $w"
 	wm title $w [strcat "[appname] ([reponame]): " [mc "error"]]
+	wm deiconify $w
 	tkwait window $w
 }
diff --git a/git-gui/lib/index.tcl b/git-gui/lib/index.tcl
index d33896a..8efbbdd 100644
--- a/git-gui/lib/index.tcl
+++ b/git-gui/lib/index.tcl
@@ -8,36 +8,41 @@
 }
 
 proc _close_updateindex {fd after} {
+	global use_ttk NS
 	fconfigure $fd -blocking 1
 	if {[catch {close $fd} err]} {
 		set w .indexfried
-		toplevel $w
+		Dialog $w
+		wm withdraw $w
 		wm title $w [strcat "[appname] ([reponame]): " [mc "Index Error"]]
 		wm geometry $w "+[winfo rootx .]+[winfo rooty .]"
-		pack [label $w.msg \
-			-justify left \
-			-anchor w \
-			-text [strcat \
-				[mc "Updating the Git index failed.  A rescan will be automatically started to resynchronize git-gui."] \
-				"\n\n$err"] \
-			] -anchor w
+		set s [mc "Updating the Git index failed.  A rescan will be automatically started to resynchronize git-gui."]
+		text $w.msg -yscrollcommand [list $w.vs set] \
+			-width [string length $s] -relief flat \
+			-borderwidth 0 -highlightthickness 0 \
+			-background [get_bg_color $w]
+		$w.msg tag configure bold -font font_uibold -justify center
+		${NS}::scrollbar $w.vs -command [list $w.msg yview]
+		$w.msg insert end $s bold \n\n$err {}
+		$w.msg configure -state disabled
 
-		frame $w.buttons
-		button $w.buttons.continue \
+		${NS}::button $w.continue \
 			-text [mc "Continue"] \
 			-command [list destroy $w]
-		pack $w.buttons.continue -side right -padx 5
-		button $w.buttons.unlock \
+		${NS}::button $w.unlock \
 			-text [mc "Unlock Index"] \
 			-command "destroy $w; _delete_indexlock"
-		pack $w.buttons.unlock -side right
-		pack $w.buttons -side bottom -fill x -pady 10 -padx 10
+		grid $w.msg - $w.vs -sticky news
+		grid $w.unlock $w.continue - -sticky se -padx 2 -pady 2
+		grid columnconfigure $w 0 -weight 1
+		grid rowconfigure $w 0 -weight 1
 
 		wm protocol $w WM_DELETE_WINDOW update
-		bind $w.buttons.continue <Visibility> "
+		bind $w.continue <Visibility> "
 			grab $w
-			focus $w.buttons.continue
+			focus %W
 		"
+		wm deiconify $w
 		tkwait window $w
 
 		$::main_status stop
@@ -98,8 +103,11 @@
 		set s $file_states($path)
 		switch -glob -- [lindex $s 0] {
 		A? {set new _O}
-		M? {set new _M}
+		MT -
+		TM -
 		T_ {set new _T}
+		M? {set new _M}
+		TD -
 		D_ {set new _D}
 		D? {set new _?}
 		?? {continue}
@@ -162,7 +170,10 @@
 		AD {set new __}
 		?D {set new D_}
 		_O -
+		AT -
 		AM {set new A_}
+		TM -
+		MT -
 		_T {set new T_}
 		_U -
 		U? {
@@ -256,7 +267,7 @@
 		switch -glob -- [lindex $file_states($path) 0] {
 		A? -
 		M? -
-		T_ -
+		T? -
 		D? {
 			lappend pathList $path
 			if {$path eq $current_diff_path} {
@@ -345,12 +356,33 @@
 	global file_states
 
 	set paths [list]
+	set untracked_paths [list]
 	foreach path [array names file_states] {
 		switch -glob -- [lindex $file_states($path) 0] {
 		U? {continue}
 		?M -
 		?T -
 		?D {lappend paths $path}
+		?O {lappend untracked_paths $path}
+		}
+	}
+	if {[llength $untracked_paths]} {
+		set reply 0
+		switch -- [get_config gui.stageuntracked] {
+		no {
+			set reply 0
+		}
+		yes {
+			set reply 1
+		}
+		ask -
+		default {
+			set reply [ask_popup [mc "Stage %d untracked files?" \
+									  [llength $untracked_paths]]]
+		}
+		}
+		if {$reply} {
+			set paths [concat $paths $untracked_paths]
 		}
 	}
 	add_helper {Adding all changed files} $paths
diff --git a/git-gui/lib/line.tcl b/git-gui/lib/line.tcl
new file mode 100644
index 0000000..a026de9
--- /dev/null
+++ b/git-gui/lib/line.tcl
@@ -0,0 +1,81 @@
+# goto line number
+# based on code from gitk, Copyright (C) Paul Mackerras
+
+class linebar {
+
+field w
+field ctext
+
+field linenum   {}
+
+constructor new {i_w i_text args} {
+	global use_ttk NS
+	set w      $i_w
+	set ctext  $i_text
+
+	${NS}::frame  $w
+	${NS}::label  $w.l       -text [mc "Goto Line:"]
+	tentry  $w.ent \
+		-textvariable ${__this}::linenum \
+		-background lightgreen \
+		-validate key \
+		-validatecommand [cb _validate %P]
+	${NS}::button $w.bn      -text [mc Go] -command [cb _goto]
+
+	pack   $w.l   -side left
+	pack   $w.bn  -side right
+	pack   $w.ent -side left -expand 1 -fill x
+
+	eval grid conf $w -sticky we $args
+	grid remove $w
+
+	trace add variable linenum write [cb _goto_cb]
+	bind $w.ent <Return> [cb _goto]
+	bind $w.ent <Escape> [cb hide]
+
+	bind $w <Destroy> [list delete_this $this]
+	return $this
+}
+
+method show {} {
+	if {![visible $this]} {
+		grid $w
+	}
+	focus -force $w.ent
+}
+
+method hide {} {
+	if {[visible $this]} {
+		$w.ent delete 0 end
+		focus $ctext
+		grid remove $w
+	}
+}
+
+method visible {} {
+	return [winfo ismapped $w]
+}
+
+method editor {} {
+	return $w.ent
+}
+
+method _validate {P} {
+	# only accept numbers as input
+	string is integer $P
+}
+
+method _goto_cb {name ix op} {
+	after idle [cb _goto 1]
+}
+
+method _goto {{nohide {0}}} {
+	if {$linenum ne {}} {
+		$ctext see $linenum.0
+		if {!$nohide} {
+			hide $this
+		}
+	}
+}
+
+}
diff --git a/git-gui/lib/merge.tcl b/git-gui/lib/merge.tcl
index 283e491..460d32f 100644
--- a/git-gui/lib/merge.tcl
+++ b/git-gui/lib/merge.tcl
@@ -83,6 +83,7 @@
 
 method _start {} {
 	global HEAD current_branch remote_url
+	global _last_merged_branch
 
 	set name [_rev $this]
 	if {$name eq {}} {
@@ -109,6 +110,7 @@
 	regsub ^refs/heads/ $branch {} branch
 	puts $fh "$cmit\t\tbranch '$branch' of $remote"
 	close $fh
+	set _last_merged_branch $branch
 
 	set cmd [list git]
 	lappend cmd merge
@@ -139,14 +141,14 @@
 
 constructor dialog {} {
 	global current_branch
-	global M1B
+	global M1B use_ttk NS
 
 	if {![_can_merge $this]} {
 		delete_this
 		return
 	}
 
-	make_toplevel top w
+	make_dialog top w
 	wm title $top [append "[appname] ([reponame]): " [mc "Merge"]]
 	if {$top ne {.}} {
 		wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
@@ -154,21 +156,21 @@
 
 	set _start [cb _start]
 
-	label $w.header \
+	${NS}::label $w.header \
 		-text [mc "Merge Into %s" $current_branch] \
 		-font font_uibold
 	pack $w.header -side top -fill x
 
-	frame $w.buttons
-	button $w.buttons.visualize \
+	${NS}::frame $w.buttons
+	${NS}::button $w.buttons.visualize \
 		-text [mc Visualize] \
 		-command [cb _visualize]
 	pack $w.buttons.visualize -side left
-	button $w.buttons.merge \
+	${NS}::button $w.buttons.merge \
 		-text [mc Merge] \
 		-command $_start
 	pack $w.buttons.merge -side right
-	button $w.buttons.cancel \
+	${NS}::button $w.buttons.cancel \
 		-text [mc "Cancel"] \
 		-command [cb _cancel]
 	pack $w.buttons.cancel -side right -padx 5
diff --git a/git-gui/lib/mergetool.tcl b/git-gui/lib/mergetool.tcl
index 3fe90e6..3c8e73b 100644
--- a/git-gui/lib/mergetool.tcl
+++ b/git-gui/lib/mergetool.tcl
@@ -175,48 +175,23 @@
 
 	# Build the command line
 	switch -- $tool {
-	kdiff3 {
+	araxis {
 		if {$base_stage ne {}} {
-			set cmdline [list "$merge_tool_path" --auto --L1 "$MERGED (Base)" \
-				--L2 "$MERGED (Local)" --L3 "$MERGED (Remote)" -o "$MERGED" "$BASE" "$LOCAL" "$REMOTE"]
+			set cmdline [list "$merge_tool_path" -wait -merge -3 -a1 \
+				-title1:"'$MERGED (Base)'" -title2:"'$MERGED (Local)'" \
+				-title3:"'$MERGED (Remote)'" \
+				"$BASE" "$LOCAL" "$REMOTE" "$MERGED"]
 		} else {
-			set cmdline [list "$merge_tool_path" --auto --L1 "$MERGED (Local)" \
-				--L2 "$MERGED (Remote)" -o "$MERGED" "$LOCAL" "$REMOTE"]
+			set cmdline [list "$merge_tool_path" -wait -2 \
+				 -title1:"'$MERGED (Local)'" -title2:"'$MERGED (Remote)'" \
+				 "$LOCAL" "$REMOTE" "$MERGED"]
 		}
 	}
-	tkdiff {
+	bc3 {
 		if {$base_stage ne {}} {
-			set cmdline [list "$merge_tool_path" -a "$BASE" -o "$MERGED" "$LOCAL" "$REMOTE"]
+			set cmdline [list "$merge_tool_path" "$LOCAL" "$REMOTE" "$BASE" -mergeoutput="$MERGED"]
 		} else {
-			set cmdline [list "$merge_tool_path" -o "$MERGED" "$LOCAL" "$REMOTE"]
-		}
-	}
-	meld {
-		set cmdline [list "$merge_tool_path" "$LOCAL" "$MERGED" "$REMOTE"]
-	}
-	gvimdiff {
-		set cmdline [list "$merge_tool_path" -f "$LOCAL" "$MERGED" "$REMOTE"]
-	}
-	xxdiff {
-		if {$base_stage ne {}} {
-			set cmdline [list "$merge_tool_path" -X --show-merged-pane \
-					    -R {Accel.SaveAsMerged: "Ctrl-S"} \
-					    -R {Accel.Search: "Ctrl+F"} \
-					    -R {Accel.SearchForward: "Ctrl-G"} \
-					    --merged-file "$MERGED" "$LOCAL" "$BASE" "$REMOTE"]
-		} else {
-			set cmdline [list "$merge_tool_path" -X --show-merged-pane \
-					    -R {Accel.SaveAsMerged: "Ctrl-S"} \
-					    -R {Accel.Search: "Ctrl+F"} \
-					    -R {Accel.SearchForward: "Ctrl-G"} \
-					    --merged-file "$MERGED" "$LOCAL" "$REMOTE"]
-		}
-	}
-	opendiff {
-		if {$base_stage ne {}} {
-			set cmdline [list "$merge_tool_path" "$LOCAL" "$REMOTE" -ancestor "$BASE" -merge "$MERGED"]
-		} else {
-			set cmdline [list "$merge_tool_path" "$LOCAL" "$REMOTE" -merge "$MERGED"]
+			set cmdline [list "$merge_tool_path" "$LOCAL" "$REMOTE" -mergeoutput="$MERGED"]
 		}
 	}
 	ecmerge {
@@ -235,6 +210,42 @@
 					"$LOCAL" "$REMOTE" "$basename"]
 		}
 	}
+	gvimdiff {
+		set cmdline [list "$merge_tool_path" -f "$LOCAL" "$MERGED" "$REMOTE"]
+	}
+	kdiff3 {
+		if {$base_stage ne {}} {
+			set cmdline [list "$merge_tool_path" --auto --L1 "$MERGED (Base)" \
+				--L2 "$MERGED (Local)" --L3 "$MERGED (Remote)" -o "$MERGED" "$BASE" "$LOCAL" "$REMOTE"]
+		} else {
+			set cmdline [list "$merge_tool_path" --auto --L1 "$MERGED (Local)" \
+				--L2 "$MERGED (Remote)" -o "$MERGED" "$LOCAL" "$REMOTE"]
+		}
+	}
+	meld {
+		set cmdline [list "$merge_tool_path" "$LOCAL" "$MERGED" "$REMOTE"]
+	}
+	opendiff {
+		if {$base_stage ne {}} {
+			set cmdline [list "$merge_tool_path" "$LOCAL" "$REMOTE" -ancestor "$BASE" -merge "$MERGED"]
+		} else {
+			set cmdline [list "$merge_tool_path" "$LOCAL" "$REMOTE" -merge "$MERGED"]
+		}
+	}
+	p4merge {
+		set cmdline [list "$merge_tool_path" "$BASE" "$REMOTE" "$LOCAL" "$MERGED"]
+	}
+	tkdiff {
+		if {$base_stage ne {}} {
+			set cmdline [list "$merge_tool_path" -a "$BASE" -o "$MERGED" "$LOCAL" "$REMOTE"]
+		} else {
+			set cmdline [list "$merge_tool_path" -o "$MERGED" "$LOCAL" "$REMOTE"]
+		}
+	}
+	vimdiff {
+		error_popup [mc "Not a GUI merge tool: '%s'" $tool]
+		return
+	}
 	winmerge {
 		if {$base_stage ne {}} {
 			# This tool does not support 3-way merges.
@@ -245,25 +256,21 @@
 				-dl "Theirs File" -dr "Mine File" "$REMOTE" "$LOCAL" "$MERGED"]
 		}
 	}
-	araxis {
+	xxdiff {
 		if {$base_stage ne {}} {
-			set cmdline [list "$merge_tool_path" -wait -merge -3 -a1 \
-				-title1:"'$MERGED (Base)'" -title2:"'$MERGED (Local)'" \
-				-title3:"'$MERGED (Remote)'" \
-				"$BASE" "$LOCAL" "$REMOTE" "$MERGED"]
+			set cmdline [list "$merge_tool_path" -X --show-merged-pane \
+					    -R {Accel.SaveAsMerged: "Ctrl-S"} \
+					    -R {Accel.Search: "Ctrl+F"} \
+					    -R {Accel.SearchForward: "Ctrl-G"} \
+					    --merged-file "$MERGED" "$LOCAL" "$BASE" "$REMOTE"]
 		} else {
-			set cmdline [list "$merge_tool_path" -wait -2 \
-				 -title1:"'$MERGED (Local)'" -title2:"'$MERGED (Remote)'" \
-				 "$LOCAL" "$REMOTE" "$MERGED"]
+			set cmdline [list "$merge_tool_path" -X --show-merged-pane \
+					    -R {Accel.SaveAsMerged: "Ctrl-S"} \
+					    -R {Accel.Search: "Ctrl+F"} \
+					    -R {Accel.SearchForward: "Ctrl-G"} \
+					    --merged-file "$MERGED" "$LOCAL" "$REMOTE"]
 		}
 	}
-	p4merge {
-		set cmdline [list "$merge_tool_path" "$BASE" "$REMOTE" "$LOCAL" "$MERGED"]
-	}
-	vimdiff {
-		error_popup [mc "Not a GUI merge tool: '%s'" $tool]
-		return
-	}
 	default {
 		error_popup [mc "Unsupported merge tool '%s'" $tool]
 		return
diff --git a/git-gui/lib/option.tcl b/git-gui/lib/option.tcl
index 1d55b49..0cf1da1 100644
--- a/git-gui/lib/option.tcl
+++ b/git-gui/lib/option.tcl
@@ -91,7 +91,7 @@
 proc do_options {} {
 	global repo_config global_config font_descs
 	global repo_config_new global_config_new
-	global ui_comm_spell
+	global ui_comm_spell use_ttk NS
 
 	array unset repo_config_new
 	array unset global_config_new
@@ -110,26 +110,28 @@
 	}
 
 	set w .options_editor
-	toplevel $w
+	Dialog $w
+	wm withdraw $w
+	wm transient $w [winfo parent $w]
 	wm geometry $w "+[winfo rootx .]+[winfo rooty .]"
 
-	frame $w.buttons
-	button $w.buttons.restore -text [mc "Restore Defaults"] \
+	${NS}::frame $w.buttons
+	${NS}::button $w.buttons.restore -text [mc "Restore Defaults"] \
 		-default normal \
 		-command do_restore_defaults
 	pack $w.buttons.restore -side left
-	button $w.buttons.save -text [mc Save] \
+	${NS}::button $w.buttons.save -text [mc Save] \
 		-default active \
 		-command [list do_save_config $w]
 	pack $w.buttons.save -side right
-	button $w.buttons.cancel -text [mc "Cancel"] \
+	${NS}::button $w.buttons.cancel -text [mc "Cancel"] \
 		-default normal \
 		-command [list destroy $w]
 	pack $w.buttons.cancel -side right -padx 5
 	pack $w.buttons -side bottom -fill x -pady 10 -padx 10
 
-	labelframe $w.repo -text [mc "%s Repository" [reponame]]
-	labelframe $w.global -text [mc "Global (All Repositories)"]
+	${NS}::labelframe $w.repo -text [mc "%s Repository" [reponame]]
+	${NS}::labelframe $w.global -text [mc "Global (All Repositories)"]
 	pack $w.repo -side left -fill both -expand 1 -pady 5 -padx 5
 	pack $w.global -side right -fill both -expand 1 -pady 5 -padx 5
 
@@ -146,13 +148,17 @@
 		{b gui.trustmtime  {mc "Trust File Modification Timestamps"}}
 		{b gui.pruneduringfetch {mc "Prune Tracking Branches During Fetch"}}
 		{b gui.matchtrackingbranch {mc "Match Tracking Branches"}}
+		{b gui.textconv {mc "Use Textconv For Diffs and Blames"}}
 		{b gui.fastcopyblame {mc "Blame Copy Only On Changed Files"}}
 		{i-20..200 gui.copyblamethreshold {mc "Minimum Letters To Blame Copy On"}}
 		{i-0..300 gui.blamehistoryctx {mc "Blame History Context Radius (days)"}}
 		{i-1..99 gui.diffcontext {mc "Number of Diff Context Lines"}}
+		{t gui.diffopts {mc "Additional Diff Parameters"}}
 		{i-0..99 gui.commitmsgwidth {mc "Commit Message Text Width"}}
 		{t gui.newbranchtemplate {mc "New Branch Name Template"}}
 		{c gui.encoding {mc "Default File Contents Encoding"}}
+		{b gui.warndetachedcommit {mc "Warn before committing to a detached head"}}
+		{s gui.stageuntracked {mc "Staging of untracked files"} {list "yes" "no" "ask"}}
 		} {
 		set type [lindex $option 0]
 		set name [lindex $option 1]
@@ -161,7 +167,7 @@
 		foreach f {repo global} {
 			switch -glob -- $type {
 			b {
-				checkbutton $w.$f.$optid -text $text \
+				${NS}::checkbutton $w.$f.$optid -text $text \
 					-variable ${f}_config_new($name) \
 					-onvalue true \
 					-offvalue false
@@ -169,10 +175,10 @@
 			}
 			i-* {
 				regexp -- {-(\d+)\.\.(\d+)$} $type _junk min max
-				frame $w.$f.$optid
-				label $w.$f.$optid.l -text "$text:"
+				${NS}::frame $w.$f.$optid
+				${NS}::label $w.$f.$optid.l -text "$text:"
 				pack $w.$f.$optid.l -side left -anchor w -fill x
-				spinbox $w.$f.$optid.v \
+				tspinbox $w.$f.$optid.v \
 					-textvariable ${f}_config_new($name) \
 					-from $min \
 					-to $max \
@@ -184,11 +190,9 @@
 			}
 			c -
 			t {
-				frame $w.$f.$optid
-				label $w.$f.$optid.l -text "$text:"
-				entry $w.$f.$optid.v \
-					-borderwidth 1 \
-					-relief sunken \
+				${NS}::frame $w.$f.$optid
+				${NS}::label $w.$f.$optid.l -text "$text:"
+				${NS}::entry $w.$f.$optid.v \
 					-width 20 \
 					-textvariable ${f}_config_new($name)
 				pack $w.$f.$optid.l -side left -anchor w
@@ -199,7 +203,7 @@
 					menu $w.$f.$optid.m
 					build_encoding_menu $w.$f.$optid.m \
 						[list set ${f}_config_new($name)] 1
-					button $w.$f.$optid.b \
+					${NS}::button $w.$f.$optid.b \
 						-text [mc "Change"] \
 						-command [list popup_btn_menu \
 							$w.$f.$optid.m $w.$f.$optid.b]
@@ -207,6 +211,23 @@
 				}
 				pack $w.$f.$optid -side top -anchor w -fill x
 			}
+			s {
+				set opts [eval [lindex $option 3]]
+				${NS}::frame $w.$f.$optid
+				${NS}::label $w.$f.$optid.l -text "$text:"
+				if {$use_ttk} {
+					ttk::combobox $w.$f.$optid.v \
+						-textvariable ${f}_config_new($name) \
+						-values $opts -state readonly
+				} else {
+					eval tk_optionMenu $w.$f.$optid.v \
+						${f}_config_new($name) \
+						$opts
+				}
+				pack $w.$f.$optid.l -side left -anchor w -fill x
+				pack $w.$f.$optid.v -side right -anchor e -padx 5
+				pack $w.$f.$optid -side top -anchor w -fill x
+			}
 			}
 		}
 	}
@@ -226,11 +247,17 @@
 			set ${f}_config_new(gui.spellingdictionary) $value
 		}
 
-		frame $w.$f.$optid
-		label $w.$f.$optid.l -text [mc "Spelling Dictionary:"]
-		eval tk_optionMenu $w.$f.$optid.v \
-			${f}_config_new(gui.spellingdictionary) \
-			$all_dicts
+		${NS}::frame $w.$f.$optid
+		${NS}::label $w.$f.$optid.l -text [mc "Spelling Dictionary:"]
+		if {$use_ttk} {
+			ttk::combobox $w.$f.$optid.v \
+				-textvariable ${f}_config_new(gui.spellingdictionary) \
+				-values $all_dicts -state readonly
+		} else {
+			eval tk_optionMenu $w.$f.$optid.v \
+				${f}_config_new(gui.spellingdictionary) \
+				$all_dicts
+		}
 		pack $w.$f.$optid.l -side left -anchor w -fill x
 		pack $w.$f.$optid.v -side right -anchor e -padx 5
 		pack $w.$f.$optid -side top -anchor w -fill x
@@ -248,20 +275,20 @@
 		set global_config_new(gui.$font^^size) \
 			[font configure $font -size]
 
-		frame $w.global.$name
-		label $w.global.$name.l -text "$text:"
-		button $w.global.$name.b \
+		${NS}::frame $w.global.$name
+		${NS}::label $w.global.$name.l -text "$text:"
+		${NS}::button $w.global.$name.b \
 			-text [mc "Change Font"] \
 			-command [list \
-				choose_font::pick \
+				tchoosefont \
 				$w \
 				[mc "Choose %s" $text] \
 				global_config_new(gui.$font^^family) \
 				global_config_new(gui.$font^^size) \
 				]
-		label $w.global.$name.f -textvariable global_config_new(gui.$font^^family)
-		label $w.global.$name.s -textvariable global_config_new(gui.$font^^size)
-		label $w.global.$name.pt -text [mc "pt."]
+		${NS}::label $w.global.$name.f -textvariable global_config_new(gui.$font^^family)
+		${NS}::label $w.global.$name.s -textvariable global_config_new(gui.$font^^size)
+		${NS}::label $w.global.$name.pt -text [mc "pt."]
 		pack $w.global.$name.l -side left -anchor w
 		pack $w.global.$name.b -side right -anchor e
 		pack $w.global.$name.pt -side right -anchor w
@@ -280,6 +307,7 @@
 		set t [mc "Options"]
 	}
 	wm title $w "[appname] ([reponame]): $t"
+	wm deiconify $w
 	tkwait window $w
 }
 
diff --git a/git-gui/lib/remote.tcl b/git-gui/lib/remote.tcl
index b92b429..5e4e7f4 100644
--- a/git-gui/lib/remote.tcl
+++ b/git-gui/lib/remote.tcl
@@ -157,22 +157,7 @@
 	}
 
 	if {$enable} {
-		if {![winfo exists $fetch_m]} {
-			menu $remove_m
-			$remote_m insert 0 cascade \
-				-label [mc "Remove Remote"] \
-				-menu $remove_m
-
-			menu $prune_m
-			$remote_m insert 0 cascade \
-				-label [mc "Prune from"] \
-				-menu $prune_m
-
-			menu $fetch_m
-			$remote_m insert 0 cascade \
-				-label [mc "Fetch from"] \
-				-menu $fetch_m
-		}
+		make_sure_remote_submenues_exist $remote_m
 
 		$fetch_m add command \
 			-label $r \
@@ -222,6 +207,70 @@
 	}
 }
 
+proc make_sure_remote_submenues_exist {remote_m} {
+	set fetch_m $remote_m.fetch
+	set prune_m $remote_m.prune
+	set remove_m $remote_m.remove
+
+	if {![winfo exists $fetch_m]} {
+		menu $remove_m
+		$remote_m insert 0 cascade \
+			-label [mc "Remove Remote"] \
+			-menu $remove_m
+
+		menu $prune_m
+		$remote_m insert 0 cascade \
+			-label [mc "Prune from"] \
+			-menu $prune_m
+
+		menu $fetch_m
+		$remote_m insert 0 cascade \
+			-label [mc "Fetch from"] \
+			-menu $fetch_m
+	}
+}
+
+proc update_all_remotes_menu_entry {} {
+	global all_remotes
+
+	if {[git-version < 1.6.6]} { return }
+
+	set have_remote 0
+	foreach r $all_remotes {
+		incr have_remote
+	}
+
+	set remote_m .mbar.remote
+	set fetch_m $remote_m.fetch
+	set prune_m $remote_m.prune
+	if {$have_remote > 1} {
+		make_sure_remote_submenues_exist $remote_m
+		if {[$fetch_m entrycget end -label] ne "All"} {
+
+			$fetch_m insert end separator
+			$fetch_m insert end command \
+				-label "All" \
+				-command fetch_from_all
+
+			$prune_m insert end separator
+			$prune_m insert end command \
+				-label "All" \
+				-command prune_from_all
+		}
+	} else {
+		if {[winfo exists $fetch_m]} {
+			if {[$fetch_m entrycget end -label] eq "All"} {
+
+				delete_from_menu $fetch_m end
+				delete_from_menu $fetch_m end
+
+				delete_from_menu $prune_m end
+				delete_from_menu $prune_m end
+			}
+		}
+	}
+}
+
 proc populate_remotes_menu {} {
 	global all_remotes
 
@@ -229,6 +278,8 @@
 		add_fetch_entry $r
 		add_push_entry $r
 	}
+
+	update_all_remotes_menu_entry
 }
 
 proc add_single_remote {name location} {
@@ -244,6 +295,8 @@
 
 	add_fetch_entry $name
 	add_push_entry $name
+
+	update_all_remotes_menu_entry
 }
 
 proc delete_from_menu {menu name} {
@@ -264,8 +317,8 @@
 		unset repo_config(remote.$name.push)
 	}
 
-	set i [lsearch -exact all_remotes $name]
-	lreplace all_remotes $i $i
+	set i [lsearch -exact $all_remotes $name]
+	set all_remotes [lreplace $all_remotes $i $i]
 
 	set remote_m .mbar.remote
 	delete_from_menu $remote_m.fetch $name
@@ -273,4 +326,6 @@
 	delete_from_menu $remote_m.remove $name
 	# Not all remotes are in the push menu
 	catch { delete_from_menu $remote_m.push $name }
+
+	update_all_remotes_menu_entry
 }
diff --git a/git-gui/lib/remote_add.tcl b/git-gui/lib/remote_add.tcl
index fb29422..50029d0 100644
--- a/git-gui/lib/remote_add.tcl
+++ b/git-gui/lib/remote_add.tcl
@@ -13,45 +13,43 @@
 field opt_action fetch; # action to do after registering the remote locally
 
 constructor dialog {} {
-	global repo_config
+	global repo_config use_ttk NS
 
-	make_toplevel top w
+	make_dialog top w
+	wm withdraw $top
 	wm title $top [append "[appname] ([reponame]): " [mc "Add Remote"]]
 	if {$top ne {.}} {
 		wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
 	}
 
-	label $w.header -text [mc "Add New Remote"] -font font_uibold
+	${NS}::label $w.header -text [mc "Add New Remote"] \
+		-font font_uibold -anchor center
 	pack $w.header -side top -fill x
 
-	frame $w.buttons
-	button $w.buttons.create -text [mc Add] \
+	${NS}::frame $w.buttons
+	${NS}::button $w.buttons.create -text [mc Add] \
 		-default active \
 		-command [cb _add]
 	pack $w.buttons.create -side right
-	button $w.buttons.cancel -text [mc Cancel] \
+	${NS}::button $w.buttons.cancel -text [mc Cancel] \
 		-command [list destroy $w]
 	pack $w.buttons.cancel -side right -padx 5
 	pack $w.buttons -side bottom -fill x -pady 10 -padx 10
 
-	labelframe $w.desc -text [mc "Remote Details"]
+	${NS}::labelframe $w.desc -text [mc "Remote Details"]
 
-	label $w.desc.name_l -text [mc "Name:"]
+	${NS}::label $w.desc.name_l -text [mc "Name:"]
 	set w_name $w.desc.name_t
-	entry $w_name \
-		-borderwidth 1 \
-		-relief sunken \
+	${NS}::entry $w_name \
 		-width 40 \
 		-textvariable @name \
 		-validate key \
 		-validatecommand [cb _validate_name %d %S]
 	grid $w.desc.name_l $w_name -sticky we -padx {0 5}
 
-	label $w.desc.loc_l -text [mc "Location:"]
+	${NS}::label $w.desc.loc_l -text [mc "Location:"]
 	set w_loc $w.desc.loc_t
-	entry $w_loc \
-		-borderwidth 1 \
-		-relief sunken \
+	${NS}::entry $w_loc \
 		-width 40 \
 		-textvariable @location
 	grid $w.desc.loc_l $w_loc -sticky we -padx {0 5}
@@ -59,21 +57,21 @@
 	grid columnconfigure $w.desc 1 -weight 1
 	pack $w.desc -anchor nw -fill x -pady 5 -padx 5
 
-	labelframe $w.action -text [mc "Further Action"]
+	${NS}::labelframe $w.action -text [mc "Further Action"]
 
-	radiobutton $w.action.fetch \
+	${NS}::radiobutton $w.action.fetch \
 		-text [mc "Fetch Immediately"] \
 		-value fetch \
 		-variable @opt_action
 	pack $w.action.fetch -anchor nw
 
-	radiobutton $w.action.push \
+	${NS}::radiobutton $w.action.push \
 		-text [mc "Initialize Remote Repository and Push"] \
 		-value push \
 		-variable @opt_action
 	pack $w.action.push -anchor nw
 
-	radiobutton $w.action.none \
+	${NS}::radiobutton $w.action.none \
 		-text [mc "Do Nothing Else Now"] \
 		-value none \
 		-variable @opt_action
@@ -85,6 +83,7 @@
 	bind $w <Visibility> [cb _visible]
 	bind $w <Key-Escape> [list destroy $w]
 	bind $w <Key-Return> [cb _add]\;break
+	wm deiconify $top
 	tkwait window $w
 }
 
diff --git a/git-gui/lib/remote_branch_delete.tcl b/git-gui/lib/remote_branch_delete.tcl
index 4e02fc0..fcc06d0 100644
--- a/git-gui/lib/remote_branch_delete.tcl
+++ b/git-gui/lib/remote_branch_delete.tcl
@@ -23,34 +23,40 @@
 field cached
 
 constructor dialog {} {
-	global all_remotes M1B
+	global all_remotes M1B use_ttk NS
 
-	make_toplevel top w
+	make_dialog top w
 	wm title $top [append "[appname] ([reponame]): " [mc "Delete Branch Remotely"]]
 	if {$top ne {.}} {
 		wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
 	}
 
-	label $w.header -text [mc "Delete Branch Remotely"] -font font_uibold
+	${NS}::label $w.header -text [mc "Delete Branch Remotely"] \
+		-font font_uibold -anchor center
 	pack $w.header -side top -fill x
 
-	frame $w.buttons
-	button $w.buttons.delete -text [mc Delete] \
+	${NS}::frame $w.buttons
+	${NS}::button $w.buttons.delete -text [mc Delete] \
 		-default active \
 		-command [cb _delete]
 	pack $w.buttons.delete -side right
-	button $w.buttons.cancel -text [mc "Cancel"] \
+	${NS}::button $w.buttons.cancel -text [mc "Cancel"] \
 		-command [list destroy $w]
 	pack $w.buttons.cancel -side right -padx 5
 	pack $w.buttons -side bottom -fill x -pady 10 -padx 10
 
-	labelframe $w.dest -text [mc "From Repository"]
+	${NS}::labelframe $w.dest -text [mc "From Repository"]
 	if {$all_remotes ne {}} {
-		radiobutton $w.dest.remote_r \
+		${NS}::radiobutton $w.dest.remote_r \
 			-text [mc "Remote:"] \
 			-value remote \
 			-variable @urltype
-		eval tk_optionMenu $w.dest.remote_m @remote $all_remotes
+		if {$use_ttk} {
+			ttk::combobox $w.dest.remote_m -textvariable @remote \
+				-values $all_remotes -state readonly
+		} else {
+			eval tk_optionMenu $w.dest.remote_m @remote $all_remotes
+		}
 		grid $w.dest.remote_r $w.dest.remote_m -sticky w
 		if {[lsearch -sorted -exact $all_remotes origin] != -1} {
 			set remote origin
@@ -62,13 +68,11 @@
 	} else {
 		set urltype url
 	}
-	radiobutton $w.dest.url_r \
+	${NS}::radiobutton $w.dest.url_r \
 		-text [mc "Arbitrary Location:"] \
 		-value url \
 		-variable @urltype
-	entry $w.dest.url_t \
-		-borderwidth 1 \
-		-relief sunken \
+	${NS}::entry $w.dest.url_t \
 		-width 50 \
 		-textvariable @url \
 		-validate key \
@@ -81,33 +85,30 @@
 	grid columnconfigure $w.dest 1 -weight 1
 	pack $w.dest -anchor nw -fill x -pady 5 -padx 5
 
-	labelframe $w.heads -text [mc "Branches"]
-	listbox $w.heads.l \
+	${NS}::labelframe $w.heads -text [mc "Branches"]
+	slistbox $w.heads.l \
 		-height 10 \
 		-width 70 \
 		-listvariable @head_list \
-		-selectmode extended \
-		-yscrollcommand [list $w.heads.sby set]
-	scrollbar $w.heads.sby -command [list $w.heads.l yview]
+		-selectmode extended
 
-	frame $w.heads.footer
-	label $w.heads.footer.status \
+	${NS}::frame $w.heads.footer
+	${NS}::label $w.heads.footer.status \
 		-textvariable @status \
 		-anchor w \
 		-justify left
-	button $w.heads.footer.rescan \
+	${NS}::button $w.heads.footer.rescan \
 		-text [mc "Rescan"] \
 		-command [cb _rescan]
 	pack $w.heads.footer.status -side left -fill x
 	pack $w.heads.footer.rescan -side right
 
 	pack $w.heads.footer -side bottom -fill x
-	pack $w.heads.sby -side right -fill y
 	pack $w.heads.l -side left -fill both -expand 1
 	pack $w.heads -fill both -expand 1 -pady 5 -padx 5
 
-	labelframe $w.validate -text [mc "Delete Only If"]
-	radiobutton $w.validate.head_r \
+	${NS}::labelframe $w.validate -text [mc "Delete Only If"]
+	${NS}::radiobutton $w.validate.head_r \
 		-text [mc "Merged Into:"] \
 		-value head \
 		-variable @checktype
@@ -115,7 +116,7 @@
 	trace add variable @head_list write [cb _write_head_list]
 	trace add variable @check_head write [cb _write_check_head]
 	grid $w.validate.head_r $w.validate.head_m -sticky w
-	radiobutton $w.validate.always_r \
+	${NS}::radiobutton $w.validate.always_r \
 		-text [mc "Always (Do not perform merge checks)"] \
 		-value always \
 		-variable @checktype
@@ -208,13 +209,15 @@
 		return
 	}
 
-	if {[tk_messageBox \
-		-icon warning \
-		-type yesno \
-		-title [wm title $w] \
-		-parent $w \
-		-message [mc "Recovering deleted branches is difficult.\n\nDelete the selected branches?"]] ne yes} {
-		return
+	if {$checktype ne {head}} {
+		if {[tk_messageBox \
+			-icon warning \
+			-type yesno \
+			-title [wm title $w] \
+			-parent $w \
+			-message [mc "Recovering deleted branches is difficult.\n\nDelete the selected branches?"]] ne yes} {
+			return
+		}
 	}
 
 	destroy $w
@@ -248,6 +251,8 @@
 method _write_check_head {args} { set checktype head }
 
 method _write_head_list {args} {
+	global current_branch _last_merged_branch
+
 	$head_m delete 0 end
 	foreach abr $head_list {
 		$head_m insert end radiobutton \
@@ -256,7 +261,18 @@
 			-variable @check_head
 	}
 	if {[lsearch -exact -sorted $head_list $check_head] < 0} {
-		set check_head {}
+		if {[lsearch -exact -sorted $head_list $current_branch] < 0} {
+			set check_head {}
+		} else {
+			set check_head $current_branch
+		}
+	}
+	set lmb [lsearch -exact -sorted $head_list $_last_merged_branch]
+	if {$lmb >= 0} {
+		$w.heads.l conf -state normal
+		$w.heads.l select set $lmb
+		$w.heads.l yview $lmb
+		$w.heads.l conf -state disabled
 	}
 }
 
diff --git a/git-gui/lib/search.tcl b/git-gui/lib/search.tcl
index b371e9a..ef1e555 100644
--- a/git-gui/lib/search.tcl
+++ b/git-gui/lib/search.tcl
@@ -7,25 +7,55 @@
 field ctext
 
 field searchstring   {}
-field casesensitive  1
+field regexpsearch
+field default_regexpsearch
+field casesensitive
+field default_casesensitive
+field smartcase
 field searchdirn     -forwards
 
+field history
+field history_index
+
 field smarktop
 field smarkbot
 
 constructor new {i_w i_text args} {
+	global use_ttk NS
 	set w      $i_w
 	set ctext  $i_text
 
-	frame  $w
-	label  $w.l       -text [mc Find:]
-	entry  $w.ent -textvariable ${__this}::searchstring -background lightgreen
-	button $w.bn      -text [mc Next] -command [cb find_next]
-	button $w.bp      -text [mc Prev] -command [cb find_prev]
-	checkbutton $w.cs -text [mc Case-Sensitive] \
+	set default_regexpsearch [is_config_true gui.search.regexp]
+	switch -- [get_config gui.search.case] {
+	no {
+		set default_casesensitive 0
+		set smartcase 0
+	}
+	smart {
+		set default_casesensitive 0
+		set smartcase 1
+	}
+	yes -
+	default {
+		set default_casesensitive 1
+		set smartcase 0
+	}
+	}
+
+	set history [list]
+
+	${NS}::frame  $w
+	${NS}::label  $w.l       -text [mc Find:]
+	tentry  $w.ent -textvariable ${__this}::searchstring -background lightgreen
+	${NS}::button $w.bn      -text [mc Next] -command [cb find_next]
+	${NS}::button $w.bp      -text [mc Prev] -command [cb find_prev]
+	${NS}::checkbutton $w.re -text [mc RegExp] \
+		-variable ${__this}::regexpsearch -command [cb _incrsearch]
+	${NS}::checkbutton $w.cs -text [mc Case] \
 		-variable ${__this}::casesensitive -command [cb _incrsearch]
 	pack   $w.l   -side left
 	pack   $w.cs  -side right
+	pack   $w.re  -side right
 	pack   $w.bp  -side right
 	pack   $w.bn  -side right
 	pack   $w.ent -side left -expand 1 -fill x
@@ -34,6 +64,10 @@
 	grid remove $w
 
 	trace add variable searchstring write [cb _incrsearch_cb]
+	bind $w.ent <Return> [cb find_next]
+	bind $w.ent <Shift-Return> [cb find_prev]
+	bind $w.ent <Key-Up>   [cb _prev_search]
+	bind $w.ent <Key-Down> [cb _next_search]
 	
 	bind $w <Destroy> [list delete_this $this]
 	return $this
@@ -42,6 +76,10 @@
 method show {} {
 	if {![visible $this]} {
 		grid $w
+		$w.ent delete 0 end
+		set regexpsearch  $default_regexpsearch
+		set casesensitive $default_casesensitive
+		set history_index [llength $history]
 	}
 	focus -force $w.ent
 }
@@ -50,6 +88,7 @@
 	if {[visible $this]} {
 		focus $ctext
 		grid remove $w
+		_save_search $this
 	}
 }
 
@@ -95,6 +134,9 @@
 		upvar $mlenvar mlen
 		lappend cmd -count mlen
 	}
+	if {$regexpsearch} {
+		lappend cmd -regexp
+	}
 	if {!$casesensitive} {
 		lappend cmd -nocase
 	}
@@ -102,14 +144,16 @@
 		set dir $searchdirn
 	}
 	lappend cmd $dir -- $searchstring
-	if {$endbound ne {}} {
-		set here [eval $cmd [list $start] [list $endbound]]
-	} else {
-		set here [eval $cmd [list $start]]
-		if {$here eq {}} {
-			set here [eval $cmd [_get_wrap_anchor $this $dir]]
+	if {[catch {
+		if {$endbound ne {}} {
+			set here [eval $cmd [list $start] [list $endbound]]
+		} else {
+			set here [eval $cmd [list $start]]
+			if {$here eq {}} {
+				set here [eval $cmd [_get_wrap_anchor $this $dir]]
+			}
 		}
-	}
+	} err]} { set here {} }
 	return $here
 }
 
@@ -123,19 +167,76 @@
 		$ctext mark set anchor [_get_new_anchor $this]
 	}
 	if {$searchstring ne {}} {
+		if {$smartcase && [regexp {[[:upper:]]} $searchstring]} {
+			set casesensitive 1
+		}
 		set here [_do_search $this anchor mlen]
 		if {$here ne {}} {
 			$ctext see $here
 			$ctext tag remove sel 1.0 end
 			$ctext tag add sel $here "$here + $mlen c"
-			$w.ent configure -background lightgreen
+			#$w.ent configure -background lightgreen
+			$w.ent state !pressed
 			_set_marks $this 1
 		} else {
-			$w.ent configure -background lightpink
+			#$w.ent configure -background lightpink
+			$w.ent state pressed
 		}
+	} elseif {$smartcase} {
+		# clearing the field resets the smart case detection
+		set casesensitive 0
 	}
 }
 
+method _save_search {} {
+	if {$searchstring eq {}} {
+		return
+	}
+	if {[llength $history] > 0} {
+		foreach {s_regexp s_case s_expr} [lindex $history end] break
+	} else {
+		set s_regexp $regexpsearch
+		set s_case   $casesensitive
+		set s_expr   ""
+	}
+	if {$searchstring eq $s_expr} {
+		# update modes
+		set history [lreplace $history end end \
+				[list $regexpsearch $casesensitive $searchstring]]
+	} else {
+		lappend history [list $regexpsearch $casesensitive $searchstring]
+	}
+	set history_index [llength $history]
+}
+
+method _prev_search {} {
+	if {$history_index > 0} {
+		incr history_index -1
+		foreach {s_regexp s_case s_expr} [lindex $history $history_index] break
+		$w.ent delete 0 end
+		$w.ent insert 0 $s_expr
+		set regexpsearch $s_regexp
+		set casesensitive $s_case
+	}
+}
+
+method _next_search {} {
+	if {$history_index < [llength $history]} {
+		incr history_index
+	}
+	if {$history_index < [llength $history]} {
+		foreach {s_regexp s_case s_expr} [lindex $history $history_index] break
+	} else {
+		set s_regexp $default_regexpsearch
+		set s_case   $default_casesensitive
+		set s_expr   ""
+	}
+	$w.ent delete 0 end
+	$w.ent insert 0 $s_expr
+	set regexpsearch $s_regexp
+	set casesensitive $s_case
+}
+
 method find_prev {} {
 	find_next $this -backwards
 }
@@ -146,6 +247,7 @@
 	set searchdirn $dir
 	$ctext mark unset anchor
 	if {$searchstring ne {}} {
+		_save_search $this
 		set start [_get_new_anchor $this]
 		if {$dir eq "-forwards"} {
 			set start "$start + 1c"
@@ -195,4 +297,4 @@
 	}
 }
 
-}
\ No newline at end of file
+}
diff --git a/git-gui/lib/shortcut.tcl b/git-gui/lib/shortcut.tcl
index 2f20eb3..78878ef 100644
--- a/git-gui/lib/shortcut.tcl
+++ b/git-gui/lib/shortcut.tcl
@@ -2,6 +2,7 @@
 # Copyright (C) 2006, 2007 Shawn Pearce
 
 proc do_windows_shortcut {} {
+	global _gitworktree
 	set fn [tk_getSaveFile \
 		-parent . \
 		-title [append "[appname] ([reponame]): " [mc "Create Desktop Icon"]] \
@@ -15,7 +16,7 @@
 					[info nameofexecutable] \
 					[file normalize $::argv0] \
 					] \
-					[file dirname [file normalize [gitdir]]]
+					[file normalize $_gitworktree]
 			} err]} {
 			error_popup [strcat [mc "Cannot write shortcut:"] "\n\n$err"]
 		}
@@ -23,7 +24,7 @@
 }
 
 proc do_cygwin_shortcut {} {
-	global argv0
+	global argv0 _gitworktree
 
 	if {[catch {
 		set desktop [exec cygpath \
@@ -56,7 +57,7 @@
 					$sh -c \
 					"CHERE_INVOKING=1 source /etc/profile;[sq $me] &" \
 					] \
-					[file dirname [file normalize [gitdir]]]
+					[file normalize $_gitworktree]
 			} err]} {
 			error_popup [strcat [mc "Cannot write shortcut:"] "\n\n$err"]
 		}
diff --git a/git-gui/lib/sshkey.tcl b/git-gui/lib/sshkey.tcl
index 82a1a80..aa6457b 100644
--- a/git-gui/lib/sshkey.tcl
+++ b/git-gui/lib/sshkey.tcl
@@ -15,7 +15,7 @@
 }
 
 proc do_ssh_key {} {
-	global sshkey_title have_tk85 sshkey_fd
+	global sshkey_title have_tk85 sshkey_fd use_ttk NS
 
 	set w .sshkey_dialog
 	if {[winfo exists $w]} {
@@ -23,7 +23,7 @@
 		return
 	}
 
-	toplevel $w
+	Dialog $w
 	wm transient $w .
 
 	set finfo [find_ssh_key]
@@ -35,9 +35,9 @@
 		set gen_state   disabled
 	}
 
-	frame $w.header -relief flat
-	label $w.header.lbl -textvariable sshkey_title -anchor w
-	button $w.header.gen -text [mc "Generate Key"] \
+	${NS}::frame $w.header
+	${NS}::label $w.header.lbl -textvariable sshkey_title -anchor w
+	${NS}::button $w.header.gen -text [mc "Generate Key"] \
 		-command [list make_ssh_key $w] -state $gen_state
 	pack $w.header.lbl -side left -expand 1 -fill x
 	pack $w.header.gen -side right
@@ -46,14 +46,16 @@
 	text $w.contents -width 60 -height 10 -wrap char -relief sunken
 	pack $w.contents -fill both -expand 1
 	if {$have_tk85} {
-		$w.contents configure -inactiveselectbackground darkblue
+		set clr darkblue
+		if {$use_ttk} { set clr [ttk::style lookup . -selectbackground] }
+		$w.contents configure -inactiveselectbackground $clr
 	}
 
-	frame $w.buttons
-	button $w.buttons.close -text [mc Close] \
+	${NS}::frame $w.buttons
+	${NS}::button $w.buttons.close -text [mc Close] \
 		-default active -command [list destroy $w]
 	pack $w.buttons.close -side right
-	button $w.buttons.copy -text [mc "Copy To Clipboard"] \
+	${NS}::button $w.buttons.copy -text [mc "Copy To Clipboard"] \
 		-command [list tk_textCopy $w.contents]
 	pack $w.buttons.copy -side left
 	pack $w.buttons -side bottom -fill x -pady 5 -padx 5
@@ -115,7 +117,7 @@
 	} else {
 		set finfo [find_ssh_key]
 		if {$finfo eq {}} {
-			set sshkey_title [mc "Generation succeded, but no keys found."]
+			set sshkey_title [mc "Generation succeeded, but no keys found."]
 			$w.contents insert end $sshkey_output
 		} else {
 			set sshkey_title [mc "Your key is in: %s" [lindex $finfo 0]]
diff --git a/git-gui/lib/status_bar.tcl b/git-gui/lib/status_bar.tcl
index 51d4177..95cb449 100644
--- a/git-gui/lib/status_bar.tcl
+++ b/git-gui/lib/status_bar.tcl
@@ -13,14 +13,16 @@
 field meter   {}; # current core git progress meter (if active)
 
 constructor new {path} {
+	global use_ttk NS
 	set w $path
 	set w_l $w.l
 	set w_c $w.c
 
-	frame $w \
-		-borderwidth 1 \
-		-relief sunken
-	label $w_l \
+	${NS}::frame $w
+	if {!$use_ttk} {
+		$w configure -borderwidth 1 -relief sunken
+	}
+	${NS}::label $w_l \
 		-textvariable @status \
 		-anchor w \
 		-justify left
@@ -37,12 +39,13 @@
 }
 
 constructor two_line {path} {
+	global NS
 	set w $path
 	set w_l $w.l
 	set w_c $w.c
 
-	frame $w
-	label $w_l \
+	${NS}::frame $w
+	${NS}::label $w_l \
 		-textvariable @status \
 		-anchor w \
 		-justify left
diff --git a/git-gui/lib/themed.tcl b/git-gui/lib/themed.tcl
new file mode 100644
index 0000000..8b88d36
--- /dev/null
+++ b/git-gui/lib/themed.tcl
@@ -0,0 +1,265 @@
+# Functions for supporting the use of themed Tk widgets in git-gui.
+# Copyright (C) 2009 Pat Thoyts <patthoyts@users.sourceforge.net>
+
+proc InitTheme {} {
+	# Create a color label style (bg can be overridden by widget option)
+	ttk::style layout Color.TLabel {
+		Color.Label.border -sticky news -children {
+			Color.label.fill -sticky news -children {
+				Color.Label.padding -sticky news -children {
+					Color.Label.label -sticky news}}}}
+	eval [linsert [ttk::style configure TLabel] 0 \
+			  ttk::style configure Color.TLabel]
+	ttk::style configure Color.TLabel \
+		-borderwidth 0 -relief flat -padding 2
+	ttk::style map Color.TLabel -background {{} gold}
+	# We also need a padded label.
+	ttk::style configure Padded.TLabel \
+		-padding {5 5} -borderwidth 1 -relief solid
+	# We need a gold frame.
+	ttk::style layout Gold.TFrame {
+		Gold.Frame.border -sticky nswe -children {
+			Gold.Frame.fill -sticky nswe}}
+	ttk::style configure Gold.TFrame -background gold -relief flat
+	# listboxes should have a theme border so embed in ttk::frame
+	ttk::style layout SListbox.TFrame {
+		SListbox.Frame.Entry.field -sticky news -border true -children {
+			SListbox.Frame.padding -sticky news
+		}
+	}
+
+	# Handle either current Tk or older versions of 8.5
+	if {[catch {set theme [ttk::style theme use]}]} {
+		set theme  $::ttk::currentTheme
+	}
+
+	if {[lsearch -exact {default alt classic clam} $theme] != -1} {
+		# Simple override of standard ttk::entry to change the field
+		# packground according to a state flag. We should use 'user1'
+		# but not all versions of 8.5 support that so make use of 'pressed'
+		# which is not normally in use for entry widgets.
+		ttk::style layout Edged.Entry [ttk::style layout TEntry]
+		ttk::style map Edged.Entry {*}[ttk::style map TEntry]
+		ttk::style configure Edged.Entry {*}[ttk::style configure TEntry] \
+			-fieldbackground lightgreen
+		ttk::style map Edged.Entry -fieldbackground {
+			{pressed !disabled} lightpink
+		}
+	} else {
+		# For fancier themes, in particular the Windows ones, the field
+		# element may not support changing the background color. So instead
+		# override the fill using the default fill element. If we overrode
+		# the vista theme field element we would loose the themed border
+		# of the widget.
+		catch {
+			ttk::style element create color.fill from default
+		}
+
+		ttk::style layout Edged.Entry {
+			Edged.Entry.field -sticky nswe -border 0 -children {
+				Edged.Entry.border -sticky nswe -border 1 -children {
+					Edged.Entry.padding -sticky nswe -children {
+						Edged.Entry.color.fill -sticky nswe -children {
+							Edged.Entry.textarea -sticky nswe
+						}
+					}
+				}
+			}
+		}
+
+		ttk::style configure Edged.Entry {*}[ttk::style configure TEntry] \
+			-background lightgreen -padding 0 -borderwidth 0
+		ttk::style map Edged.Entry {*}[ttk::style map TEntry] \
+			-background {{pressed !disabled} lightpink}
+	}
+
+	if {[lsearch [bind . <<ThemeChanged>>] InitTheme] == -1} {
+		bind . <<ThemeChanged>> +[namespace code [list InitTheme]]
+	}
+}
+
+proc gold_frame {w args} {
+	global use_ttk
+	if {$use_ttk} {
+		eval [linsert $args 0 ttk::frame $w -style Gold.TFrame]
+	} else {
+		eval [linsert $args 0 frame $w -background gold]
+	}
+}
+
+proc tlabel {w args} {
+	global use_ttk
+	if {$use_ttk} {
+		set cmd [list ttk::label $w -style Color.TLabel]
+		foreach {k v} $args {
+			switch -glob -- $k {
+				-activebackground {}
+				default { lappend cmd $k $v }
+			}
+		}
+		eval $cmd
+	} else {
+		eval [linsert $args 0 label $w]
+	}
+}
+
+# The padded label gets used in the about class.
+proc paddedlabel {w args} {
+	global use_ttk
+	if {$use_ttk} {
+		eval [linsert $args 0 ttk::label $w -style Padded.TLabel]
+	} else {
+		eval [linsert $args 0 label $w \
+				  -padx 5 -pady 5 \
+				  -justify left \
+				  -anchor w \
+				  -borderwidth 1 \
+				  -relief solid]
+	}
+}
+
+# Create a toplevel for use as a dialog.
+# If available, sets the EWMH dialog hint and if ttk is enabled
+# place a themed frame over the surface.
+proc Dialog {w args} {
+	eval [linsert $args 0 toplevel $w -class Dialog]
+	catch {wm attributes $w -type dialog}	
+	pave_toplevel $w
+	return $w
+}
+
+# Tk toplevels are not themed - so pave it over with a themed frame to get
+# the base color correct per theme.
+proc pave_toplevel {w} {
+	global use_ttk
+	if {$use_ttk && ![winfo exists $w.!paving]} {
+		set paving [ttk::frame $w.!paving]
+		place $paving -x 0 -y 0 -relwidth 1 -relheight 1
+		lower $paving
+	}
+}
+
+# Create a scrolled listbox with appropriate border for the current theme.
+# On many themes the border for a scrolled listbox needs to go around the
+# listbox and the scrollbar.
+proc slistbox {w args} {
+	global use_ttk NS
+	if {$use_ttk} {
+		set f [ttk::frame $w -style SListbox.TFrame -padding 2]
+	} else {
+		set f [frame $w -relief flat]
+	}
+    if {[catch {
+		if {$use_ttk} {
+			eval [linsert $args 0 listbox $f.list -relief flat \
+					  -highlightthickness 0 -borderwidth 0]
+		} else {
+			eval [linsert $args 0 listbox $f.list]
+		}
+        ${NS}::scrollbar $f.vs -command [list $f.list yview]
+        $f.list configure -yscrollcommand [list $f.vs set]
+        grid $f.list $f.vs -sticky news
+        grid rowconfigure $f 0 -weight 1
+        grid columnconfigure $f 0 -weight 1
+		bind $f.list <<ListboxSelect>> \
+			[list event generate $w <<ListboxSelect>>]
+        interp hide {} $w
+        interp alias {} $w {} $f.list
+    } err]} {
+        destroy $f
+        return -code error $err
+    }
+    return $w
+}
+
+# fetch the background color from a widget.
+proc get_bg_color {w} {
+	global use_ttk
+	if {$use_ttk} {
+		set bg [ttk::style lookup [winfo class $w] -background]
+	} else {
+		set bg [$w cget -background]
+	}
+	return $bg
+}
+
+# ttk::spinbox didn't get added until 8.6
+proc tspinbox {w args} {
+	global use_ttk
+	if {$use_ttk && [llength [info commands ttk::spinbox]] > 0} {
+		eval [linsert $args 0 ttk::spinbox $w]
+	} else {
+		eval [linsert $args 0 spinbox $w]
+	}
+}
+
+proc tentry {w args} {
+	global use_ttk
+	if {$use_ttk} {
+		InitTheme
+		ttk::entry $w -style Edged.Entry
+	} else {
+		entry $w
+	}
+
+	rename $w _$w
+	interp alias {} $w {} tentry_widgetproc $w
+	eval [linsert $args 0 tentry_widgetproc $w configure]
+	return $w
+}
+proc tentry_widgetproc {w cmd args} {
+	global use_ttk
+	switch -- $cmd {
+		state {
+			if {$use_ttk} {
+				return [uplevel 1 [list _$w $cmd] $args]
+			} else {
+				if {[lsearch -exact $args pressed] != -1} {
+					_$w configure -background lightpink
+				} else {
+					_$w configure -background lightgreen
+				}
+			}
+		}
+		configure {
+			if {$use_ttk} {
+				if {[set n [lsearch -exact $args -background]] != -1} {
+					set args [lreplace $args $n [incr n]]
+					if {[llength $args] == 0} {return}
+				}
+			}
+			return [uplevel 1 [list _$w $cmd] $args]
+		}
+		default { return [uplevel 1 [list _$w $cmd] $args] }
+	}
+}
+
+# Tk 8.6 provides a standard font selection dialog. This uses the native
+# dialogs on Windows and MacOSX or a standard Tk dialog on X11.
+proc tchoosefont {w title familyvar sizevar} {
+	if {[package vsatisfies [package provide Tk] 8.6]} {
+		upvar #0 $familyvar family
+		upvar #0 $sizevar size
+		tk fontchooser configure -parent $w -title $title \
+			-font [list $family $size] \
+			-command [list on_choosefont $familyvar $sizevar]
+		tk fontchooser show
+	} else {
+		choose_font::pick $w $title $familyvar $sizevar
+	}
+}
+
+# Called when the Tk 8.6 fontchooser selects a font.
+proc on_choosefont {familyvar sizevar font} {
+	upvar #0 $familyvar family
+	upvar #0 $sizevar size
+	set font [font actual $font]
+	set family [dict get $font -family]
+	set size [dict get $font -size]
+}
+
+# Local variables:
+# mode: tcl
+# indent-tabs-mode: t
+# tab-width: 4
+# End:
diff --git a/git-gui/lib/tools.tcl b/git-gui/lib/tools.tcl
index 95e6e55..6ec9411 100644
--- a/git-gui/lib/tools.tcl
+++ b/git-gui/lib/tools.tcl
@@ -87,8 +87,14 @@
 			return
 		}
 	} elseif {[is_config_true "guitool.$fullname.confirm"]} {
-		if {[ask_popup [mc "Are you sure you want to run %s?" $fullname]] ne {yes}} {
-			return
+		if {[is_config_true "guitool.$fullname.needsfile"]} {
+			if {[ask_popup [mc "Are you sure you want to run %1\$s on file \"%2\$s\"?" $fullname $current_diff_path]] ne {yes}} {
+				return
+			}
+		} else {
+			if {[ask_popup [mc "Are you sure you want to run %s?" $fullname]] ne {yes}} {
+				return
+			}
 		}
 	}
 
diff --git a/git-gui/lib/tools_dlg.tcl b/git-gui/lib/tools_dlg.tcl
index 5f7f08e..7eeda9d 100644
--- a/git-gui/lib/tools_dlg.tcl
+++ b/git-gui/lib/tools_dlg.tcl
@@ -16,53 +16,50 @@
 field ask_args      0; # ask for additional args
 
 constructor dialog {} {
-	global repo_config
+	global repo_config use_ttk NS
 
-	make_toplevel top w
+	make_dialog top w
 	wm title $top [append "[appname] ([reponame]): " [mc "Add Tool"]]
 	if {$top ne {.}} {
 		wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
 		wm transient $top .
 	}
 
-	label $w.header -text [mc "Add New Tool Command"] -font font_uibold
+	${NS}::label $w.header -text [mc "Add New Tool Command"] \
+		-font font_uibold -anchor center
 	pack $w.header -side top -fill x
 
-	frame $w.buttons
-	checkbutton $w.buttons.global \
+	${NS}::frame $w.buttons
+	${NS}::checkbutton $w.buttons.global \
 		-text [mc "Add globally"] \
 		-variable @add_global
 	pack $w.buttons.global -side left -padx 5
-	button $w.buttons.create -text [mc Add] \
+	${NS}::button $w.buttons.create -text [mc Add] \
 		-default active \
 		-command [cb _add]
 	pack $w.buttons.create -side right
-	button $w.buttons.cancel -text [mc Cancel] \
+	${NS}::button $w.buttons.cancel -text [mc Cancel] \
 		-command [list destroy $w]
 	pack $w.buttons.cancel -side right -padx 5
 	pack $w.buttons -side bottom -fill x -pady 10 -padx 10
 
-	labelframe $w.desc -text [mc "Tool Details"]
+	${NS}::labelframe $w.desc -text [mc "Tool Details"]
 
-	label $w.desc.name_cmnt -anchor w\
+	${NS}::label $w.desc.name_cmnt -anchor w\
 		-text [mc "Use '/' separators to create a submenu tree:"]
 	grid x $w.desc.name_cmnt -sticky we -padx {0 5} -pady {0 2}
-	label $w.desc.name_l -text [mc "Name:"]
+	${NS}::label $w.desc.name_l -text [mc "Name:"]
 	set w_name $w.desc.name_t
-	entry $w_name \
-		-borderwidth 1 \
-		-relief sunken \
+	${NS}::entry $w_name \
 		-width 40 \
 		-textvariable @name \
 		-validate key \
 		-validatecommand [cb _validate_name %d %S]
 	grid $w.desc.name_l $w_name -sticky we -padx {0 5}
 
-	label $w.desc.cmd_l -text [mc "Command:"]
+	${NS}::label $w.desc.cmd_l -text [mc "Command:"]
 	set w_cmd $w.desc.cmd_t
-	entry $w_cmd \
-		-borderwidth 1 \
-		-relief sunken \
+	${NS}::entry $w_cmd \
 		-width 40 \
 		-textvariable @command
 	grid $w.desc.cmd_l $w_cmd -sticky we -padx {0 5} -pady {0 3}
@@ -70,30 +67,30 @@
 	grid columnconfigure $w.desc 1 -weight 1
 	pack $w.desc -anchor nw -fill x -pady 5 -padx 5
 
-	checkbutton $w.confirm \
+	${NS}::checkbutton $w.confirm \
 		-text [mc "Show a dialog before running"] \
 		-variable @confirm -command [cb _check_enable_dlg]
 
-	labelframe $w.dlg -labelwidget $w.confirm
+	${NS}::labelframe $w.dlg -labelwidget $w.confirm
 
-	checkbutton $w.dlg.askbranch \
+	${NS}::checkbutton $w.dlg.askbranch \
 		-text [mc "Ask the user to select a revision (sets \$REVISION)"] \
 		-variable @ask_branch -state disabled
 	pack $w.dlg.askbranch -anchor w -padx 15
 
-	checkbutton $w.dlg.askargs \
+	${NS}::checkbutton $w.dlg.askargs \
 		-text [mc "Ask the user for additional arguments (sets \$ARGS)"] \
 		-variable @ask_args -state disabled
 	pack $w.dlg.askargs -anchor w -padx 15
 
 	pack $w.dlg -anchor nw -fill x -pady {0 8} -padx 5
 
-	checkbutton $w.noconsole \
+	${NS}::checkbutton $w.noconsole \
 		-text [mc "Don't show the command output window"] \
 		-variable @no_console
 	pack $w.noconsole -anchor w -padx 5
 
-	checkbutton $w.needsfile \
+	${NS}::checkbutton $w.needsfile \
 		-text [mc "Run only if a diff is selected (\$FILENAME not empty)"] \
 		-variable @needs_file
 	pack $w.needsfile -anchor w -padx 5
@@ -182,40 +179,38 @@
 field w_names        ; # name list
 
 constructor dialog {} {
-	global repo_config global_config system_config
+	global repo_config global_config system_config use_ttk NS
 
 	load_config 1
 
-	make_toplevel top w
+	make_dialog top w
 	wm title $top [append "[appname] ([reponame]): " [mc "Remove Tool"]]
 	if {$top ne {.}} {
 		wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
 		wm transient $top .
 	}
 
-	label $w.header -text [mc "Remove Tool Commands"] -font font_uibold
+	${NS}::label $w.header -text [mc "Remove Tool Commands"] \
+		-font font_uibold -anchor center
 	pack $w.header -side top -fill x
 
-	frame $w.buttons
-	button $w.buttons.create -text [mc Remove] \
+	${NS}::frame $w.buttons
+	${NS}::button $w.buttons.create -text [mc Remove] \
 		-default active \
 		-command [cb _remove]
 	pack $w.buttons.create -side right
-	button $w.buttons.cancel -text [mc Cancel] \
+	${NS}::button $w.buttons.cancel -text [mc Cancel] \
 		-command [list destroy $w]
 	pack $w.buttons.cancel -side right -padx 5
 	pack $w.buttons -side bottom -fill x -pady 10 -padx 10
 
-	frame $w.list
+	${NS}::frame $w.list
 	set w_names $w.list.l
-	listbox $w_names \
+	slistbox $w_names \
 		-height 10 \
 		-width 30 \
 		-selectmode extended \
-		-exportselection false \
-		-yscrollcommand [list $w.list.sby set]
-	scrollbar $w.list.sby -command [list $w.list.l yview]
-	pack $w.list.sby -side right -fill y
+		-exportselection false
 	pack $w.list.l -side left -fill both -expand 1
 	pack $w.list -fill both -expand 1 -pady 5 -padx 5
 
@@ -232,7 +227,7 @@
 	}
 
 	if {$local_cnt > 0} {
-		label $w.colorlbl -foreground blue \
+		${NS}::label $w.colorlbl -foreground blue \
 			-text [mc "(Blue denotes repository-local tools)"]
 		pack $w.colorlbl -fill x -pady 5 -padx 5
 	}
@@ -277,14 +272,14 @@
 field argstr       {}; # arguments
 
 constructor dialog {fullname} {
-	global M1B
+	global M1B use_ttk NS
 
 	set title [get_config "guitool.$fullname.title"]
 	if {$title eq {}} {
 		regsub {/} $fullname { / } title
 	}
 
-	make_toplevel top w -autodelete 0
+	make_dialog top w -autodelete 0
 	wm title $top [append "[appname] ([reponame]): " $title]
 	if {$top ne {.}} {
 		wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
@@ -297,7 +292,7 @@
 		set prompt [mc "Run Command: %s" $command]
 	}
 
-	label $w.header -text $prompt -font font_uibold
+	${NS}::label $w.header -text $prompt -font font_uibold -anchor center
 	pack $w.header -side top -fill x
 
 	set argprompt [get_config "guitool.$fullname.argprompt"]
@@ -311,12 +306,10 @@
 			set argprompt [mc "Arguments"]
 		}
 
-		labelframe $w.arg -text $argprompt
+		${NS}::labelframe $w.arg -text $argprompt
 
 		set w_args $w.arg.txt
-		entry $w_args \
-			-borderwidth 1 \
-			-relief sunken \
+		${NS}::entry $w_args \
 			-width 40 \
 			-textvariable @argstr
 		pack $w_args -padx 5 -pady 5 -fill both
@@ -337,18 +330,18 @@
 		pack $w.rev -anchor nw -fill both -expand 1 -pady 5 -padx 5
 	}
 
-	frame $w.buttons
+	${NS}::frame $w.buttons
 	if {$is_ask_revs} {
-		button $w.buttons.visualize \
+		${NS}::button $w.buttons.visualize \
 			-text [mc Visualize] \
 			-command [cb _visualize]
 		pack $w.buttons.visualize -side left
 	}
-	button $w.buttons.ok \
+	${NS}::button $w.buttons.ok \
 		-text [mc OK] \
 		-command [cb _start]
 	pack $w.buttons.ok -side right
-	button $w.buttons.cancel \
+	${NS}::button $w.buttons.cancel \
 		-text [mc "Cancel"] \
 		-command [cb _cancel]
 	pack $w.buttons.cancel -side right -padx 5
diff --git a/git-gui/lib/transport.tcl b/git-gui/lib/transport.tcl
index b18d9c7..e5d211e 100644
--- a/git-gui/lib/transport.tcl
+++ b/git-gui/lib/transport.tcl
@@ -20,6 +20,35 @@
 	console::exec $w [list git remote prune $remote]
 }
 
+proc fetch_from_all {} {
+	set w [console::new \
+		[mc "fetch all remotes"] \
+		[mc "Fetching new changes from all remotes"]]
+
+	set cmd [list git fetch --all]
+	if {[is_config_true gui.pruneduringfetch]} {
+		lappend cmd --prune
+	}
+
+	console::exec $w $cmd
+}
+
+proc prune_from_all {} {
+	global all_remotes
+
+	set w [console::new \
+		[mc "remote prune all remotes"] \
+		[mc "Pruning tracking branches deleted from all remotes"]]
+
+	set cmd [list git remote prune]
+
+	foreach r $all_remotes {
+		lappend cmd $r
+	}
+
+	console::exec $w $cmd
+}
+
 proc push_to {remote} {
 	set w [console::new \
 		[mc "push %s" $remote] \
@@ -91,50 +120,59 @@
 proc do_push_anywhere {} {
 	global all_remotes current_branch
 	global push_urltype push_remote push_url push_thin push_tags
-	global push_force
+	global push_force use_ttk NS
 
 	set w .push_setup
 	toplevel $w
+	catch {wm attributes $w -type dialog}
+	wm withdraw $w
 	wm geometry $w "+[winfo rootx .]+[winfo rooty .]"
+	pave_toplevel $w
 
-	label $w.header -text [mc "Push Branches"] -font font_uibold
+	${NS}::label $w.header -text [mc "Push Branches"] \
+		-font font_uibold -anchor center
 	pack $w.header -side top -fill x
 
-	frame $w.buttons
-	button $w.buttons.create -text [mc Push] \
+	${NS}::frame $w.buttons
+	${NS}::button $w.buttons.create -text [mc Push] \
 		-default active \
 		-command [list start_push_anywhere_action $w]
 	pack $w.buttons.create -side right
-	button $w.buttons.cancel -text [mc "Cancel"] \
+	${NS}::button $w.buttons.cancel -text [mc "Cancel"] \
 		-default normal \
 		-command [list destroy $w]
 	pack $w.buttons.cancel -side right -padx 5
 	pack $w.buttons -side bottom -fill x -pady 10 -padx 10
 
-	labelframe $w.source -text [mc "Source Branches"]
-	listbox $w.source.l \
+	${NS}::labelframe $w.source -text [mc "Source Branches"]
+	slistbox $w.source.l \
 		-height 10 \
 		-width 70 \
-		-selectmode extended \
-		-yscrollcommand [list $w.source.sby set]
+		-selectmode extended
 	foreach h [load_all_heads] {
 		$w.source.l insert end $h
 		if {$h eq $current_branch} {
 			$w.source.l select set end
+			$w.source.l yview end
 		}
 	}
-	scrollbar $w.source.sby -command [list $w.source.l yview]
-	pack $w.source.sby -side right -fill y
 	pack $w.source.l -side left -fill both -expand 1
 	pack $w.source -fill both -expand 1 -pady 5 -padx 5
 
-	labelframe $w.dest -text [mc "Destination Repository"]
+	${NS}::labelframe $w.dest -text [mc "Destination Repository"]
 	if {$all_remotes ne {}} {
-		radiobutton $w.dest.remote_r \
+		${NS}::radiobutton $w.dest.remote_r \
 			-text [mc "Remote:"] \
 			-value remote \
 			-variable push_urltype
-		eval tk_optionMenu $w.dest.remote_m push_remote $all_remotes
+		if {$use_ttk} {
+			ttk::combobox $w.dest.remote_m -state readonly \
+				-exportselection false \
+				-textvariable push_remote \
+				-values $all_remotes
+		} else {
+			eval tk_optionMenu $w.dest.remote_m push_remote $all_remotes
+		}
 		grid $w.dest.remote_r $w.dest.remote_m -sticky w
 		if {[lsearch -sorted -exact $all_remotes origin] != -1} {
 			set push_remote origin
@@ -145,13 +183,11 @@
 	} else {
 		set push_urltype url
 	}
-	radiobutton $w.dest.url_r \
+	${NS}::radiobutton $w.dest.url_r \
 		-text [mc "Arbitrary Location:"] \
 		-value url \
 		-variable push_urltype
-	entry $w.dest.url_t \
-		-borderwidth 1 \
-		-relief sunken \
+	${NS}::entry $w.dest.url_t \
 		-width 50 \
 		-textvariable push_url \
 		-validate key \
@@ -166,16 +202,16 @@
 	grid columnconfigure $w.dest 1 -weight 1
 	pack $w.dest -anchor nw -fill x -pady 5 -padx 5
 
-	labelframe $w.options -text [mc "Transfer Options"]
-	checkbutton $w.options.force \
+	${NS}::labelframe $w.options -text [mc "Transfer Options"]
+	${NS}::checkbutton $w.options.force \
 		-text [mc "Force overwrite existing branch (may discard changes)"] \
 		-variable push_force
 	grid $w.options.force -columnspan 2 -sticky w
-	checkbutton $w.options.thin \
+	${NS}::checkbutton $w.options.thin \
 		-text [mc "Use thin pack (for slow network connections)"] \
 		-variable push_thin
 	grid $w.options.thin -columnspan 2 -sticky w
-	checkbutton $w.options.tags \
+	${NS}::checkbutton $w.options.tags \
 		-text [mc "Include tags"] \
 		-variable push_tags
 	grid $w.options.tags -columnspan 2 -sticky w
@@ -191,5 +227,6 @@
 	bind $w <Key-Escape> "destroy $w"
 	bind $w <Key-Return> [list start_push_anywhere_action $w]
 	wm title $w [append "[appname] ([reponame]): " [mc "Push"]]
+	wm deiconify $w
 	tkwait window $w
 }
diff --git a/git-gui/lib/win32.tcl b/git-gui/lib/win32.tcl
index d7f93d0..db91ab8 100644
--- a/git-gui/lib/win32.tcl
+++ b/git-gui/lib/win32.tcl
@@ -18,9 +18,9 @@
 	eval [list exec wscript.exe \
 		/E:jscript \
 		/nologo \
-		[file join $oguilib win32_shortcut.js] \
+		[file nativename [file join $oguilib win32_shortcut.js]] \
 		$lnk_path \
-		[file join $oguilib git-gui.ico] \
+		[file nativename [file join $oguilib git-gui.ico]] \
 		$lnk_dir \
 		$lnk_exec] $lnk_args
 }
diff --git a/git-gui/po/README b/git-gui/po/README
index 595bbf5..0f5837d 100644
--- a/git-gui/po/README
+++ b/git-gui/po/README
@@ -18,28 +18,23 @@
 poedit, GTranslator --- any of them would work well).  Please install
 them.
 
-You would then need to clone the git-gui internationalization project
-repository, so that you can work on it:
+You would then need to clone the git-gui project repository and create
+a feature branch to begin working:
 
-	$ git clone mob@repo.or.cz:/srv/git/git-gui/git-gui-i18n.git/
-	$ cd git-gui-i18n
-	$ git checkout --track -b mob origin/mob
-	$ git config remote.origin.push mob
+	$ git clone git://repo.or.cz/git-gui.git
+	$ cd git-gui.git
+	$ git checkout -b my-translation
 
-The "git checkout" command creates a 'mob' branch from upstream's
-corresponding branch and makes it your current branch.  You will be
-working on this branch.
-
-The "git config" command records in your repository configuration file
-that you would push "mob" branch to the upstream when you say "git
-push".
+The "git checkout" command creates a new branch to keep your work
+isolated and to make it simple to post your patch series when
+completed.  You will be working on this branch.
 
 
 2. Starting a new language.
 
-In the git-gui-i18n directory is a po/ subdirectory.  It has a
-handful files whose names end with ".po".  Is there a file that has
-messages in your language?
+In the git-gui directory is a po/ subdirectory.  It has a handful of
+files whose names end with ".po".  Is there a file that has messages
+in your language?
 
 If you do not know what your language should be named, you need to find
 it.  This currently follows ISO 639-1 two letter codes:
@@ -149,15 +144,18 @@
 	$ make
 	$ LANG=af ./git-gui.sh
 
-When you are satisfied with your translation, commit your changes, and
-push it back to the 'mob' branch:
+When you are satisfied with your translation, commit your changes then submit
+your patch series to the maintainer and the Git mailing list:
 
 	$ edit po/af.po
 	... be sure to update Last-Translator: and
 	... PO-Revision-Date: lines.
 	$ git add po/af.po
-	$ git commit -m 'Started Afrikaans translation.'
-	$ git push
+	$ git commit -s -m 'git-gui: added Afrikaans translation.'
+	$ git send-email --to 'git@vger.kernel.org' \
+	   --cc 'Pat Thoyts <patthoyts@users.sourceforge.net>' \
+	   --subject 'git-gui: Afrikaans translation' \
+	   master..
 
 
 3. Updating your translation.
@@ -169,6 +167,7 @@
 
 In any case, make sure you are up-to-date before starting your work:
 
+	$ git checkout master
 	$ git pull
 
 In the former case, you will edit po/af.po (again, replace "af" with
diff --git a/git-gui/po/de.po b/git-gui/po/de.po
index 51abb50..44c5ddc 100644
--- a/git-gui/po/de.po
+++ b/git-gui/po/de.po
@@ -7,41 +7,41 @@
 msgstr ""
 "Project-Id-Version: git-gui\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-12-06 20:51+0100\n"
-"PO-Revision-Date: 2008-12-06 21:22+0100\n"
+"POT-Creation-Date: 2010-01-26 22:22+0100\n"
+"PO-Revision-Date: 2010-01-26 22:25+0100\n"
 "Last-Translator: Christian Stimming <stimming@tuhh.de>\n"
 "Language-Team: German\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: git-gui.sh:41 git-gui.sh:634 git-gui.sh:648 git-gui.sh:661 git-gui.sh:744
-#: git-gui.sh:763
+#: git-gui.sh:41 git-gui.sh:793 git-gui.sh:807 git-gui.sh:820 git-gui.sh:903
+#: git-gui.sh:922
 msgid "git-gui: fatal error"
 msgstr "git-gui: Programmfehler"
 
-#: git-gui.sh:593
+#: git-gui.sh:743
 #, tcl-format
 msgid "Invalid font specified in %s:"
 msgstr "Ungültige Zeichensatz-Angabe in %s:"
 
-#: git-gui.sh:620
+#: git-gui.sh:779
 msgid "Main Font"
 msgstr "Programmschriftart"
 
-#: git-gui.sh:621
+#: git-gui.sh:780
 msgid "Diff/Console Font"
 msgstr "Vergleich-Schriftart"
 
-#: git-gui.sh:635
+#: git-gui.sh:794
 msgid "Cannot find git in PATH."
 msgstr "Git kann im PATH nicht gefunden werden."
 
-#: git-gui.sh:662
+#: git-gui.sh:821
 msgid "Cannot parse Git version string:"
 msgstr "Git Versionsangabe kann nicht erkannt werden:"
 
-#: git-gui.sh:680
+#: git-gui.sh:839
 #, tcl-format
 msgid ""
 "Git version cannot be determined.\n"
@@ -60,447 +60,479 @@
 "\n"
 "Soll angenommen werden, »%s« sei Version 1.5.0?\n"
 
-#: git-gui.sh:918
+#: git-gui.sh:1128
 msgid "Git directory not found:"
 msgstr "Git-Verzeichnis nicht gefunden:"
 
-#: git-gui.sh:925
+#: git-gui.sh:1146
 msgid "Cannot move to top of working directory:"
 msgstr ""
 "Es konnte nicht in das oberste Verzeichnis der Arbeitskopie gewechselt "
 "werden:"
 
-#: git-gui.sh:932
-msgid "Cannot use funny .git directory:"
-msgstr "Unerwartete Struktur des .git Verzeichnis:"
+#: git-gui.sh:1154
+msgid "Cannot use bare repository:"
+msgstr "Leeres Projektarchiv kann nicht benutzt werden:"
 
-#: git-gui.sh:937
+#: git-gui.sh:1162
 msgid "No working directory"
 msgstr "Kein Arbeitsverzeichnis"
 
-#: git-gui.sh:1084 lib/checkout_op.tcl:283
+#: git-gui.sh:1334 lib/checkout_op.tcl:306
 msgid "Refreshing file status..."
 msgstr "Dateistatus aktualisieren..."
 
-#: git-gui.sh:1149
+#: git-gui.sh:1390
 msgid "Scanning for modified files ..."
 msgstr "Nach geänderten Dateien suchen..."
 
-#: git-gui.sh:1367
+#: git-gui.sh:1454
 msgid "Calling prepare-commit-msg hook..."
 msgstr "Aufrufen der Eintragen-Vorbereiten-Kontrolle..."
 
-#: git-gui.sh:1384
+#: git-gui.sh:1471
 msgid "Commit declined by prepare-commit-msg hook."
-msgstr "Eintragen abgelehnt durch Eintragen-Vorbereiten-Kontrolle (»prepare-commit hook«)."
+msgstr ""
+"Eintragen abgelehnt durch Eintragen-Vorbereiten-Kontrolle (»prepare-commit "
+"hook«)."
 
-#: git-gui.sh:1542 lib/browser.tcl:246
+#: git-gui.sh:1629 lib/browser.tcl:246
 msgid "Ready."
 msgstr "Bereit."
 
-#: git-gui.sh:1590
+#: git-gui.sh:1787
+#, tcl-format
+msgid "Displaying only %s of %s files."
+msgstr "Nur %s von %s Dateien werden angezeigt."
+
+#: git-gui.sh:1913
 msgid "Unmodified"
 msgstr "Unverändert"
 
-#: git-gui.sh:1592
+#: git-gui.sh:1915
 msgid "Modified, not staged"
 msgstr "Verändert, nicht bereitgestellt"
 
-#: git-gui.sh:1593 git-gui.sh:1598
+#: git-gui.sh:1916 git-gui.sh:1924
 msgid "Staged for commit"
 msgstr "Bereitgestellt zum Eintragen"
 
-#: git-gui.sh:1594 git-gui.sh:1599
+#: git-gui.sh:1917 git-gui.sh:1925
 msgid "Portions staged for commit"
 msgstr "Teilweise bereitgestellt zum Eintragen"
 
-#: git-gui.sh:1595 git-gui.sh:1600
+#: git-gui.sh:1918 git-gui.sh:1926
 msgid "Staged for commit, missing"
 msgstr "Bereitgestellt zum Eintragen, fehlend"
 
-#: git-gui.sh:1658
+#: git-gui.sh:1920
 msgid "File type changed, not staged"
 msgstr "Dateityp geändert, nicht bereitgestellt"
 
-#: git-gui.sh:1659
+#: git-gui.sh:1921
 msgid "File type changed, staged"
 msgstr "Dateityp geändert, bereitgestellt"
 
-#: git-gui.sh:1661
+#: git-gui.sh:1923
 msgid "Untracked, not staged"
 msgstr "Nicht unter Versionskontrolle, nicht bereitgestellt"
 
-#: git-gui.sh:1602
+#: git-gui.sh:1928
 msgid "Missing"
 msgstr "Fehlend"
 
-#: git-gui.sh:1603
+#: git-gui.sh:1929
 msgid "Staged for removal"
 msgstr "Bereitgestellt zum Löschen"
 
-#: git-gui.sh:1604
+#: git-gui.sh:1930
 msgid "Staged for removal, still present"
 msgstr "Bereitgestellt zum Löschen, trotzdem vorhanden"
 
-#: git-gui.sh:1606 git-gui.sh:1607 git-gui.sh:1608 git-gui.sh:1609
+#: git-gui.sh:1932 git-gui.sh:1933 git-gui.sh:1934 git-gui.sh:1935
+#: git-gui.sh:1936 git-gui.sh:1937
 msgid "Requires merge resolution"
 msgstr "Konfliktauflösung nötig"
 
-#: git-gui.sh:1644
+#: git-gui.sh:1972
 msgid "Starting gitk... please wait..."
 msgstr "Gitk wird gestartet... bitte warten."
 
-#: git-gui.sh:1698
+#: git-gui.sh:1984
 msgid "Couldn't find gitk in PATH"
 msgstr "Gitk kann im PATH nicht gefunden werden."
 
-#: git-gui.sh:1948 lib/choose_repository.tcl:36
+#: git-gui.sh:2043
+msgid "Couldn't find git gui in PATH"
+msgstr "»Git gui« kann im PATH nicht gefunden werden."
+
+#: git-gui.sh:2455 lib/choose_repository.tcl:36
 msgid "Repository"
 msgstr "Projektarchiv"
 
-#: git-gui.sh:1861
+#: git-gui.sh:2456
 msgid "Edit"
 msgstr "Bearbeiten"
 
-#: git-gui.sh:1863 lib/choose_rev.tcl:561
+#: git-gui.sh:2458 lib/choose_rev.tcl:561
 msgid "Branch"
 msgstr "Zweig"
 
-#: git-gui.sh:1866 lib/choose_rev.tcl:548
+#: git-gui.sh:2461 lib/choose_rev.tcl:548
 msgid "Commit@@noun"
 msgstr "Version"
 
-#: git-gui.sh:1869 lib/merge.tcl:120 lib/merge.tcl:149 lib/merge.tcl:167
+#: git-gui.sh:2464 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
 msgid "Merge"
 msgstr "Zusammenführen"
 
-#: git-gui.sh:1870 lib/choose_rev.tcl:557
+#: git-gui.sh:2465 lib/choose_rev.tcl:557
 msgid "Remote"
 msgstr "Andere Archive"
 
-#: git-gui.sh:2293
+#: git-gui.sh:2468
 msgid "Tools"
 msgstr "Werkzeuge"
 
-#: git-gui.sh:2302
+#: git-gui.sh:2477
 msgid "Explore Working Copy"
 msgstr "Arbeitskopie im Dateimanager"
 
-#: git-gui.sh:2247
+#: git-gui.sh:2483
 msgid "Browse Current Branch's Files"
 msgstr "Aktuellen Zweig durchblättern"
 
-#: git-gui.sh:1883
+#: git-gui.sh:2487
 msgid "Browse Branch Files..."
 msgstr "Einen Zweig durchblättern..."
 
-#: git-gui.sh:1888
+#: git-gui.sh:2492
 msgid "Visualize Current Branch's History"
 msgstr "Aktuellen Zweig darstellen"
 
-#: git-gui.sh:1892
+#: git-gui.sh:2496
 msgid "Visualize All Branch History"
 msgstr "Alle Zweige darstellen"
 
-#: git-gui.sh:1899
+#: git-gui.sh:2503
 #, tcl-format
 msgid "Browse %s's Files"
 msgstr "Zweig »%s« durchblättern"
 
-#: git-gui.sh:1901
+#: git-gui.sh:2505
 #, tcl-format
 msgid "Visualize %s's History"
 msgstr "Historie von »%s« darstellen"
 
-#: git-gui.sh:1906 lib/database.tcl:27 lib/database.tcl:67
+#: git-gui.sh:2510 lib/database.tcl:27 lib/database.tcl:67
 msgid "Database Statistics"
 msgstr "Datenbankstatistik"
 
-#: git-gui.sh:1909 lib/database.tcl:34
+#: git-gui.sh:2513 lib/database.tcl:34
 msgid "Compress Database"
 msgstr "Datenbank komprimieren"
 
-#: git-gui.sh:1912
+#: git-gui.sh:2516
 msgid "Verify Database"
 msgstr "Datenbank überprüfen"
 
-#: git-gui.sh:1919 git-gui.sh:1923 git-gui.sh:1927 lib/shortcut.tcl:7
-#: lib/shortcut.tcl:39 lib/shortcut.tcl:71
+#: git-gui.sh:2523 git-gui.sh:2527 git-gui.sh:2531 lib/shortcut.tcl:8
+#: lib/shortcut.tcl:40 lib/shortcut.tcl:72
 msgid "Create Desktop Icon"
 msgstr "Desktop-Icon erstellen"
 
-#: git-gui.sh:1932 lib/choose_repository.tcl:177 lib/choose_repository.tcl:185
+#: git-gui.sh:2539 lib/choose_repository.tcl:183 lib/choose_repository.tcl:191
 msgid "Quit"
 msgstr "Beenden"
 
-#: git-gui.sh:1939
+#: git-gui.sh:2547
 msgid "Undo"
 msgstr "Rückgängig"
 
-#: git-gui.sh:1942
+#: git-gui.sh:2550
 msgid "Redo"
 msgstr "Wiederholen"
 
-#: git-gui.sh:1946 git-gui.sh:2443
+#: git-gui.sh:2554 git-gui.sh:3109
 msgid "Cut"
 msgstr "Ausschneiden"
 
-#: git-gui.sh:1949 git-gui.sh:2446 git-gui.sh:2520 git-gui.sh:2614
+#: git-gui.sh:2557 git-gui.sh:3112 git-gui.sh:3186 git-gui.sh:3259
 #: lib/console.tcl:69
 msgid "Copy"
 msgstr "Kopieren"
 
-#: git-gui.sh:1952 git-gui.sh:2449
+#: git-gui.sh:2560 git-gui.sh:3115
 msgid "Paste"
 msgstr "Einfügen"
 
-#: git-gui.sh:1955 git-gui.sh:2452 lib/branch_delete.tcl:26
+#: git-gui.sh:2563 git-gui.sh:3118 lib/branch_delete.tcl:26
 #: lib/remote_branch_delete.tcl:38
 msgid "Delete"
 msgstr "Löschen"
 
-#: git-gui.sh:1959 git-gui.sh:2456 git-gui.sh:2618 lib/console.tcl:71
+#: git-gui.sh:2567 git-gui.sh:3122 git-gui.sh:3263 lib/console.tcl:71
 msgid "Select All"
 msgstr "Alle auswählen"
 
-#: git-gui.sh:1968
+#: git-gui.sh:2576
 msgid "Create..."
 msgstr "Erstellen..."
 
-#: git-gui.sh:1974
+#: git-gui.sh:2582
 msgid "Checkout..."
 msgstr "Umstellen..."
 
-#: git-gui.sh:1980
+#: git-gui.sh:2588
 msgid "Rename..."
 msgstr "Umbenennen..."
 
-#: git-gui.sh:1985 git-gui.sh:2085
+#: git-gui.sh:2593
 msgid "Delete..."
 msgstr "Löschen..."
 
-#: git-gui.sh:1990
+#: git-gui.sh:2598
 msgid "Reset..."
 msgstr "Zurücksetzen..."
 
-#: git-gui.sh:2372
+#: git-gui.sh:2608
 msgid "Done"
 msgstr "Fertig"
 
-#: git-gui.sh:2374
+#: git-gui.sh:2610
 msgid "Commit@@verb"
 msgstr "Eintragen"
 
-#: git-gui.sh:2383 git-gui.sh:2786
+#: git-gui.sh:2619 git-gui.sh:3050
 msgid "New Commit"
 msgstr "Neue Version"
 
-#: git-gui.sh:2010 git-gui.sh:2396
+#: git-gui.sh:2627 git-gui.sh:3057
 msgid "Amend Last Commit"
 msgstr "Letzte nachbessern"
 
-#: git-gui.sh:2019 git-gui.sh:2356 lib/remote_branch_delete.tcl:99
+#: git-gui.sh:2637 git-gui.sh:3011 lib/remote_branch_delete.tcl:99
 msgid "Rescan"
 msgstr "Neu laden"
 
-#: git-gui.sh:2025
+#: git-gui.sh:2643
 msgid "Stage To Commit"
 msgstr "Zum Eintragen bereitstellen"
 
-#: git-gui.sh:2031
+#: git-gui.sh:2649
 msgid "Stage Changed Files To Commit"
 msgstr "Geänderte Dateien bereitstellen"
 
-#: git-gui.sh:2037
+#: git-gui.sh:2655
 msgid "Unstage From Commit"
 msgstr "Aus der Bereitstellung herausnehmen"
 
-#: git-gui.sh:2042 lib/index.tcl:395
+#: git-gui.sh:2661 lib/index.tcl:412
 msgid "Revert Changes"
 msgstr "Änderungen verwerfen"
 
-#: git-gui.sh:2141 git-gui.sh:2702
+#: git-gui.sh:2669 git-gui.sh:3310 git-gui.sh:3341
 msgid "Show Less Context"
 msgstr "Weniger Zeilen anzeigen"
 
-#: git-gui.sh:2145 git-gui.sh:2706
+#: git-gui.sh:2673 git-gui.sh:3314 git-gui.sh:3345
 msgid "Show More Context"
 msgstr "Mehr Zeilen anzeigen"
 
-#: git-gui.sh:2151 git-gui.sh:2470 git-gui.sh:2569
+#: git-gui.sh:2680 git-gui.sh:3024 git-gui.sh:3133
 msgid "Sign Off"
 msgstr "Abzeichnen"
 
-#: git-gui.sh:2458
+#: git-gui.sh:2696
 msgid "Local Merge..."
 msgstr "Lokales Zusammenführen..."
 
-#: git-gui.sh:2069
+#: git-gui.sh:2701
 msgid "Abort Merge..."
 msgstr "Zusammenführen abbrechen..."
 
-#: git-gui.sh:2475
+#: git-gui.sh:2713 git-gui.sh:2741
 msgid "Add..."
 msgstr "Hinzufügen..."
 
-#: git-gui.sh:2479
+#: git-gui.sh:2717
 msgid "Push..."
 msgstr "Versenden..."
 
-#: git-gui.sh:2483
+#: git-gui.sh:2721
 msgid "Delete Branch..."
 msgstr "Zweig löschen..."
 
-#: git-gui.sh:2493 git-gui.sh:2515 lib/about.tcl:14
-#: lib/choose_repository.tcl:44 lib/choose_repository.tcl:50
+#: git-gui.sh:2731 git-gui.sh:3292
+msgid "Options..."
+msgstr "Optionen..."
+
+#: git-gui.sh:2742
+msgid "Remove..."
+msgstr "Entfernen..."
+
+#: git-gui.sh:2751 lib/choose_repository.tcl:50
+msgid "Help"
+msgstr "Hilfe"
+
+#: git-gui.sh:2755 git-gui.sh:2759 lib/about.tcl:14
+#: lib/choose_repository.tcl:44 lib/choose_repository.tcl:53
 #, tcl-format
 msgid "About %s"
 msgstr "Über %s"
 
-#: git-gui.sh:2099
-msgid "Preferences..."
-msgstr "Einstellungen..."
-
-#: git-gui.sh:2107 git-gui.sh:2639
-msgid "Options..."
-msgstr "Optionen..."
-
-#: git-gui.sh:2576
-msgid "Remove..."
-msgstr "Entfernen..."
-
-#: git-gui.sh:2585 lib/choose_repository.tcl:50
-msgid "Help"
-msgstr "Hilfe"
-
-#: git-gui.sh:2154
+#: git-gui.sh:2783
 msgid "Online Documentation"
 msgstr "Online-Dokumentation"
 
-#: git-gui.sh:2614 lib/choose_repository.tcl:47 lib/choose_repository.tcl:56
+#: git-gui.sh:2786 lib/choose_repository.tcl:47 lib/choose_repository.tcl:56
 msgid "Show SSH Key"
 msgstr "SSH-Schlüssel anzeigen"
 
-#: git-gui.sh:2707
+#: git-gui.sh:2893
 #, tcl-format
 msgid "fatal: cannot stat path %s: No such file or directory"
 msgstr ""
 "Fehler: Verzeichnis »%s« kann nicht gelesen werden: Datei oder Verzeichnis "
 "nicht gefunden"
 
-#: git-gui.sh:2271
+#: git-gui.sh:2926
 msgid "Current Branch:"
 msgstr "Aktueller Zweig:"
 
-#: git-gui.sh:2292
+#: git-gui.sh:2947
 msgid "Staged Changes (Will Commit)"
 msgstr "Bereitstellung (zum Eintragen)"
 
-#: git-gui.sh:2312
+#: git-gui.sh:2967
 msgid "Unstaged Changes"
 msgstr "Nicht bereitgestellte Änderungen"
 
-#: git-gui.sh:2362
+#: git-gui.sh:3017
 msgid "Stage Changed"
 msgstr "Alles bereitstellen"
 
-#: git-gui.sh:2378 lib/transport.tcl:93 lib/transport.tcl:182
+#: git-gui.sh:3036 lib/transport.tcl:104 lib/transport.tcl:193
 msgid "Push"
 msgstr "Versenden"
 
-#: git-gui.sh:2408
+#: git-gui.sh:3071
 msgid "Initial Commit Message:"
 msgstr "Erste Versionsbeschreibung:"
 
-#: git-gui.sh:2409
+#: git-gui.sh:3072
 msgid "Amended Commit Message:"
 msgstr "Nachgebesserte Beschreibung:"
 
-#: git-gui.sh:2410
+#: git-gui.sh:3073
 msgid "Amended Initial Commit Message:"
 msgstr "Nachgebesserte erste Beschreibung:"
 
-#: git-gui.sh:2411
+#: git-gui.sh:3074
 msgid "Amended Merge Commit Message:"
 msgstr "Nachgebesserte Zusammenführungs-Beschreibung:"
 
-#: git-gui.sh:2412
+#: git-gui.sh:3075
 msgid "Merge Commit Message:"
 msgstr "Zusammenführungs-Beschreibung:"
 
-#: git-gui.sh:2413
+#: git-gui.sh:3076
 msgid "Commit Message:"
 msgstr "Versionsbeschreibung:"
 
-#: git-gui.sh:2459 git-gui.sh:2622 lib/console.tcl:73
+#: git-gui.sh:3125 git-gui.sh:3267 lib/console.tcl:73
 msgid "Copy All"
 msgstr "Alle kopieren"
 
-#: git-gui.sh:2483 lib/blame.tcl:107
+#: git-gui.sh:3149 lib/blame.tcl:104
 msgid "File:"
 msgstr "Datei:"
 
-#: git-gui.sh:2834
+#: git-gui.sh:3255
 msgid "Refresh"
 msgstr "Aktualisieren"
 
-#: git-gui.sh:2631
+#: git-gui.sh:3276
 msgid "Decrease Font Size"
 msgstr "Schriftgröße verkleinern"
 
-#: git-gui.sh:2635
+#: git-gui.sh:3280
 msgid "Increase Font Size"
 msgstr "Schriftgröße vergrößern"
 
-#: git-gui.sh:3033 lib/blame.tcl:281
+#: git-gui.sh:3288 lib/blame.tcl:281
 msgid "Encoding"
 msgstr "Zeichenkodierung"
 
-#: git-gui.sh:3044
+#: git-gui.sh:3299
 msgid "Apply/Reverse Hunk"
 msgstr "Kontext anwenden/umkehren"
 
-#: git-gui.sh:2875
+#: git-gui.sh:3304
 msgid "Apply/Reverse Line"
 msgstr "Zeile anwenden/umkehren"
 
-#: git-gui.sh:2885
+#: git-gui.sh:3323
 msgid "Run Merge Tool"
 msgstr "Zusammenführungswerkzeug"
 
-#: git-gui.sh:2890
+#: git-gui.sh:3328
 msgid "Use Remote Version"
 msgstr "Entfernte Version benutzen"
 
-#: git-gui.sh:2894
+#: git-gui.sh:3332
 msgid "Use Local Version"
 msgstr "Lokale Version benutzen"
 
-#: git-gui.sh:2898
+#: git-gui.sh:3336
 msgid "Revert To Base"
 msgstr "Ursprüngliche Version benutzen"
 
-#: git-gui.sh:3091
+#: git-gui.sh:3354
+msgid "Visualize These Changes In The Submodule"
+msgstr "Diese Änderungen im Untermodul darstellen"
+
+#: git-gui.sh:3358
+msgid "Visualize Current Branch History In The Submodule"
+msgstr "Aktuellen Zweig im Untermodul darstellen"
+
+#: git-gui.sh:3362
+msgid "Visualize All Branch History In The Submodule"
+msgstr "Alle Zweige im Untermodul darstellen"
+
+#: git-gui.sh:3367
+msgid "Start git gui In The Submodule"
+msgstr "Git gui im Untermodul starten"
+
+#: git-gui.sh:3389
 msgid "Unstage Hunk From Commit"
 msgstr "Kontext aus Bereitstellung herausnehmen"
 
-#: git-gui.sh:2748
+#: git-gui.sh:3391
+msgid "Unstage Lines From Commit"
+msgstr "Zeilen aus der Bereitstellung herausnehmen"
+
+#: git-gui.sh:3393
 msgid "Unstage Line From Commit"
 msgstr "Zeile aus der Bereitstellung herausnehmen"
 
-#: git-gui.sh:2750
+#: git-gui.sh:3396
 msgid "Stage Hunk For Commit"
 msgstr "Kontext zur Bereitstellung hinzufügen"
 
-#: git-gui.sh:2751
+#: git-gui.sh:3398
+msgid "Stage Lines For Commit"
+msgstr "Zeilen zur Bereitstellung hinzufügen"
+
+#: git-gui.sh:3400
 msgid "Stage Line For Commit"
 msgstr "Zeile zur Bereitstellung hinzufügen"
 
-#: git-gui.sh:2771
+#: git-gui.sh:3424
 msgid "Initializing..."
 msgstr "Initialisieren..."
 
-#: git-gui.sh:2762
+#: git-gui.sh:3541
 #, tcl-format
 msgid ""
 "Possible environment issues exist.\n"
@@ -516,7 +548,7 @@
 "von %s an Git weitergegeben werden:\n"
 "\n"
 
-#: git-gui.sh:2792
+#: git-gui.sh:3570
 msgid ""
 "\n"
 "This is due to a known issue with the\n"
@@ -526,7 +558,7 @@
 "Dies ist ein bekanntes Problem der Tcl-Version, die\n"
 "in Cygwin mitgeliefert wird."
 
-#: git-gui.sh:2797
+#: git-gui.sh:3575
 #, tcl-format
 msgid ""
 "\n"
@@ -546,15 +578,15 @@
 msgid "git-gui - a graphical user interface for Git."
 msgstr "git-gui - eine grafische Oberfläche für Git."
 
-#: lib/blame.tcl:77
+#: lib/blame.tcl:72
 msgid "File Viewer"
 msgstr "Datei-Browser"
 
-#: lib/blame.tcl:81
+#: lib/blame.tcl:78
 msgid "Commit:"
 msgstr "Version:"
 
-#: lib/blame.tcl:264
+#: lib/blame.tcl:271
 msgid "Copy Commit"
 msgstr "Version kopieren"
 
@@ -566,88 +598,88 @@
 msgid "Do Full Copy Detection"
 msgstr "Volle Kopie-Erkennung"
 
-#: lib/blame.tcl:263
+#: lib/blame.tcl:288
 msgid "Show History Context"
 msgstr "Historien-Kontext anzeigen"
 
-#: lib/blame.tcl:266
+#: lib/blame.tcl:291
 msgid "Blame Parent Commit"
 msgstr "Elternversion annotieren"
 
-#: lib/blame.tcl:394
+#: lib/blame.tcl:450
 #, tcl-format
 msgid "Reading %s..."
 msgstr "%s lesen..."
 
-#: lib/blame.tcl:488
+#: lib/blame.tcl:557
 msgid "Loading copy/move tracking annotations..."
 msgstr "Annotierungen für Kopieren/Verschieben werden geladen..."
 
-#: lib/blame.tcl:508
+#: lib/blame.tcl:577
 msgid "lines annotated"
 msgstr "Zeilen annotiert"
 
-#: lib/blame.tcl:689
+#: lib/blame.tcl:769
 msgid "Loading original location annotations..."
 msgstr "Annotierungen für ursprünglichen Ort werden geladen..."
 
-#: lib/blame.tcl:692
+#: lib/blame.tcl:772
 msgid "Annotation complete."
 msgstr "Annotierung vollständig."
 
-#: lib/blame.tcl:737
+#: lib/blame.tcl:802
 msgid "Busy"
 msgstr "Verarbeitung läuft"
 
-#: lib/blame.tcl:738
+#: lib/blame.tcl:803
 msgid "Annotation process is already running."
 msgstr "Annotierung läuft bereits."
 
-#: lib/blame.tcl:777
+#: lib/blame.tcl:842
 msgid "Running thorough copy detection..."
 msgstr "Intensive Kopie-Erkennung läuft..."
 
-#: lib/blame.tcl:827
+#: lib/blame.tcl:910
 msgid "Loading annotation..."
 msgstr "Annotierung laden..."
 
-#: lib/blame.tcl:802
+#: lib/blame.tcl:963
 msgid "Author:"
 msgstr "Autor:"
 
-#: lib/blame.tcl:806
+#: lib/blame.tcl:967
 msgid "Committer:"
 msgstr "Eintragender:"
 
-#: lib/blame.tcl:811
+#: lib/blame.tcl:972
 msgid "Original File:"
 msgstr "Ursprüngliche Datei:"
 
-#: lib/blame.tcl:1021
+#: lib/blame.tcl:1020
 msgid "Cannot find HEAD commit:"
 msgstr "Zweigspitze (»HEAD«) kann nicht gefunden werden:"
 
-#: lib/blame.tcl:1076
+#: lib/blame.tcl:1075
 msgid "Cannot find parent commit:"
 msgstr "Elternversion kann nicht gefunden werden:"
 
-#: lib/blame.tcl:1001
+#: lib/blame.tcl:1090
 msgid "Unable to display parent"
 msgstr "Elternversion kann nicht angezeigt werden"
 
-#: lib/blame.tcl:1002 lib/diff.tcl:191
+#: lib/blame.tcl:1091 lib/diff.tcl:320
 msgid "Error loading diff:"
 msgstr "Fehler beim Laden des Vergleichs:"
 
-#: lib/blame.tcl:1142
+#: lib/blame.tcl:1231
 msgid "Originally By:"
 msgstr "Ursprünglich von:"
 
-#: lib/blame.tcl:931
+#: lib/blame.tcl:1237
 msgid "In File:"
 msgstr "In Datei:"
 
-#: lib/blame.tcl:936
+#: lib/blame.tcl:1242
 msgid "Copied Or Moved Here By:"
 msgstr "Kopiert oder verschoben durch:"
 
@@ -661,16 +693,18 @@
 
 #: lib/branch_checkout.tcl:27 lib/branch_create.tcl:35
 #: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:282
-#: lib/checkout_op.tcl:522 lib/choose_font.tcl:43 lib/merge.tcl:171
-#: lib/option.tcl:103 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97
+#: lib/checkout_op.tcl:579 lib/choose_font.tcl:43 lib/merge.tcl:172
+#: lib/option.tcl:125 lib/remote_add.tcl:32 lib/remote_branch_delete.tcl:42
+#: lib/tools_dlg.tcl:40 lib/tools_dlg.tcl:204 lib/tools_dlg.tcl:352
+#: lib/transport.tcl:108
 msgid "Cancel"
 msgstr "Abbrechen"
 
-#: lib/branch_checkout.tcl:32 lib/browser.tcl:287
+#: lib/branch_checkout.tcl:32 lib/browser.tcl:287 lib/tools_dlg.tcl:328
 msgid "Revision"
 msgstr "Version"
 
-#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:242
+#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:280
 msgid "Options"
 msgstr "Optionen"
 
@@ -690,7 +724,7 @@
 msgid "Create New Branch"
 msgstr "Neuen Zweig erstellen"
 
-#: lib/branch_create.tcl:31 lib/choose_repository.tcl:371
+#: lib/branch_create.tcl:31 lib/choose_repository.tcl:381
 msgid "Create"
 msgstr "Erstellen"
 
@@ -698,7 +732,7 @@
 msgid "Branch Name"
 msgstr "Zweigname"
 
-#: lib/branch_create.tcl:43
+#: lib/branch_create.tcl:43 lib/remote_add.tcl:39 lib/tools_dlg.tcl:50
 msgid "Name:"
 msgstr "Name:"
 
@@ -722,7 +756,7 @@
 msgid "Fast Forward Only"
 msgstr "Nur Schnellzusammenführung"
 
-#: lib/branch_create.tcl:85 lib/checkout_op.tcl:514
+#: lib/branch_create.tcl:85 lib/checkout_op.tcl:571
 msgid "Reset"
 msgstr "Zurücksetzen"
 
@@ -764,15 +798,26 @@
 msgid "Delete Only If Merged Into"
 msgstr "Nur löschen, wenn zusammengeführt nach"
 
-#: lib/branch_delete.tcl:54
-msgid "Always (Do not perform merge test.)"
-msgstr "Immer (ohne Zusammenführungstest)"
+#: lib/branch_delete.tcl:54 lib/remote_branch_delete.tcl:119
+msgid "Always (Do not perform merge checks)"
+msgstr "Immer (Keine Zusammenführungsprüfung)"
 
 #: lib/branch_delete.tcl:103
 #, tcl-format
 msgid "The following branches are not completely merged into %s:"
 msgstr "Folgende Zweige sind noch nicht mit »%s« zusammengeführt:"
 
+#: lib/branch_delete.tcl:115 lib/remote_branch_delete.tcl:217
+msgid ""
+"Recovering deleted branches is difficult.\n"
+"\n"
+"Delete the selected branches?"
+msgstr ""
+"Das Wiederherstellen von gelöschten Zweigen ist nur mit größerem Aufwand "
+"möglich.\n"
+"\n"
+"Sollen die ausgewählten Zweige gelöscht werden?"
+
 #: lib/branch_delete.tcl:141
 #, tcl-format
 msgid ""
@@ -802,7 +847,7 @@
 msgid "Please select a branch to rename."
 msgstr "Bitte wählen Sie einen Zweig zum umbenennen."
 
-#: lib/branch_rename.tcl:96 lib/checkout_op.tcl:179
+#: lib/branch_rename.tcl:96 lib/checkout_op.tcl:202
 #, tcl-format
 msgid "Branch '%s' already exists."
 msgstr "Zweig »%s« existiert bereits."
@@ -833,37 +878,38 @@
 msgid "Browse Branch Files"
 msgstr "Dateien des Zweigs durchblättern"
 
-#: lib/browser.tcl:278 lib/choose_repository.tcl:387
-#: lib/choose_repository.tcl:474 lib/choose_repository.tcl:484
-#: lib/choose_repository.tcl:987
+#: lib/browser.tcl:278 lib/choose_repository.tcl:398
+#: lib/choose_repository.tcl:486 lib/choose_repository.tcl:497
+#: lib/choose_repository.tcl:1028
 msgid "Browse"
 msgstr "Blättern"
 
-#: lib/checkout_op.tcl:79
+#: lib/checkout_op.tcl:85
 #, tcl-format
 msgid "Fetching %s from %s"
 msgstr "Änderungen »%s« von »%s« anfordern"
 
-#: lib/checkout_op.tcl:127
+#: lib/checkout_op.tcl:133
 #, tcl-format
 msgid "fatal: Cannot resolve %s"
 msgstr "Fehler: »%s« kann nicht als Zweig oder Version erkannt werden"
 
-#: lib/checkout_op.tcl:140 lib/console.tcl:81 lib/database.tcl:31
+#: lib/checkout_op.tcl:146 lib/console.tcl:81 lib/database.tcl:31
+#: lib/sshkey.tcl:53
 msgid "Close"
 msgstr "Schließen"
 
-#: lib/checkout_op.tcl:169
+#: lib/checkout_op.tcl:175
 #, tcl-format
 msgid "Branch '%s' does not exist."
 msgstr "Zweig »%s« existiert nicht."
 
-#: lib/checkout_op.tcl:193
+#: lib/checkout_op.tcl:194
 #, tcl-format
 msgid "Failed to configure simplified git-pull for '%s'."
 msgstr "Fehler beim Einrichten der vereinfachten git-pull für »%s«."
 
-#: lib/checkout_op.tcl:228
+#: lib/checkout_op.tcl:229
 #, tcl-format
 msgid ""
 "Branch '%s' already exists.\n"
@@ -876,21 +922,21 @@
 "Zweig kann nicht mit »%s« schnellzusammengeführt werden. Reguläres "
 "Zusammenführen ist notwendig."
 
-#: lib/checkout_op.tcl:220
+#: lib/checkout_op.tcl:243
 #, tcl-format
 msgid "Merge strategy '%s' not supported."
 msgstr "Zusammenführungsmethode »%s« nicht unterstützt."
 
-#: lib/checkout_op.tcl:239
+#: lib/checkout_op.tcl:262
 #, tcl-format
 msgid "Failed to update '%s'."
 msgstr "Aktualisieren von »%s« fehlgeschlagen."
 
-#: lib/checkout_op.tcl:251
+#: lib/checkout_op.tcl:274
 msgid "Staging area (index) is already locked."
 msgstr "Bereitstellung (»index«) ist zur Bearbeitung gesperrt (»locked«)."
 
-#: lib/checkout_op.tcl:266
+#: lib/checkout_op.tcl:289
 msgid ""
 "Last scanned state does not match repository state.\n"
 "\n"
@@ -906,32 +952,32 @@
 "\n"
 "Es wird gleich neu geladen.\n"
 
-#: lib/checkout_op.tcl:322
+#: lib/checkout_op.tcl:345
 #, tcl-format
 msgid "Updating working directory to '%s'..."
 msgstr "Arbeitskopie umstellen auf »%s«..."
 
-#: lib/checkout_op.tcl:323
+#: lib/checkout_op.tcl:346
 msgid "files checked out"
 msgstr "Dateien aktualisiert"
 
-#: lib/checkout_op.tcl:353
+#: lib/checkout_op.tcl:376
 #, tcl-format
 msgid "Aborted checkout of '%s' (file level merging is required)."
 msgstr ""
 "Auf Zweig »%s« umstellen abgebrochen (Zusammenführen der Dateien ist "
 "notwendig)."
 
-#: lib/checkout_op.tcl:354
+#: lib/checkout_op.tcl:377
 msgid "File level merge required."
 msgstr "Zusammenführen der Dateien ist notwendig."
 
-#: lib/checkout_op.tcl:358
+#: lib/checkout_op.tcl:381
 #, tcl-format
 msgid "Staying on branch '%s'."
 msgstr "Es wird auf Zweig »%s« verblieben."
 
-#: lib/checkout_op.tcl:429
+#: lib/checkout_op.tcl:452
 msgid ""
 "You are no longer on a local branch.\n"
 "\n"
@@ -943,32 +989,32 @@
 "Wenn Sie auf einem Zweig arbeiten möchten, erstellen Sie bitte jetzt einen "
 "Zweig mit der Auswahl »Abgetrennte Arbeitskopie-Version«."
 
-#: lib/checkout_op.tcl:446 lib/checkout_op.tcl:450
+#: lib/checkout_op.tcl:503 lib/checkout_op.tcl:507
 #, tcl-format
 msgid "Checked out '%s'."
 msgstr "Umgestellt auf »%s«."
 
-#: lib/checkout_op.tcl:478
+#: lib/checkout_op.tcl:535
 #, tcl-format
 msgid "Resetting '%s' to '%s' will lose the following commits:"
 msgstr "Zurücksetzen von »%s« nach »%s« wird folgende Versionen verwerfen:"
 
-#: lib/checkout_op.tcl:500
+#: lib/checkout_op.tcl:557
 msgid "Recovering lost commits may not be easy."
 msgstr ""
 "Verworfene Versionen können nur mit größerem Aufwand wiederhergestellt "
 "werden."
 
-#: lib/checkout_op.tcl:505
+#: lib/checkout_op.tcl:562
 #, tcl-format
 msgid "Reset '%s'?"
 msgstr "»%s« zurücksetzen?"
 
-#: lib/checkout_op.tcl:510 lib/merge.tcl:163
+#: lib/checkout_op.tcl:567 lib/merge.tcl:164 lib/tools_dlg.tcl:343
 msgid "Visualize"
 msgstr "Darstellen"
 
-#: lib/checkout_op.tcl:578
+#: lib/checkout_op.tcl:635
 #, tcl-format
 msgid ""
 "Failed to set current branch.\n"
@@ -1014,231 +1060,231 @@
 msgid "Git Gui"
 msgstr "Git Gui"
 
-#: lib/choose_repository.tcl:81 lib/choose_repository.tcl:376
+#: lib/choose_repository.tcl:87 lib/choose_repository.tcl:386
 msgid "Create New Repository"
 msgstr "Neues Projektarchiv"
 
-#: lib/choose_repository.tcl:87
+#: lib/choose_repository.tcl:93
 msgid "New..."
 msgstr "Neu..."
 
-#: lib/choose_repository.tcl:94 lib/choose_repository.tcl:460
+#: lib/choose_repository.tcl:100 lib/choose_repository.tcl:471
 msgid "Clone Existing Repository"
 msgstr "Projektarchiv klonen"
 
-#: lib/choose_repository.tcl:100
+#: lib/choose_repository.tcl:106
 msgid "Clone..."
 msgstr "Klonen..."
 
-#: lib/choose_repository.tcl:107 lib/choose_repository.tcl:976
+#: lib/choose_repository.tcl:113 lib/choose_repository.tcl:1016
 msgid "Open Existing Repository"
 msgstr "Projektarchiv öffnen"
 
-#: lib/choose_repository.tcl:113
+#: lib/choose_repository.tcl:119
 msgid "Open..."
 msgstr "Öffnen..."
 
-#: lib/choose_repository.tcl:126
+#: lib/choose_repository.tcl:132
 msgid "Recent Repositories"
 msgstr "Zuletzt benutzte Projektarchive"
 
-#: lib/choose_repository.tcl:132
+#: lib/choose_repository.tcl:138
 msgid "Open Recent Repository:"
 msgstr "Zuletzt benutztes Projektarchiv öffnen:"
 
-#: lib/choose_repository.tcl:296 lib/choose_repository.tcl:303
-#: lib/choose_repository.tcl:310
+#: lib/choose_repository.tcl:306 lib/choose_repository.tcl:313
+#: lib/choose_repository.tcl:320
 #, tcl-format
 msgid "Failed to create repository %s:"
 msgstr "Projektarchiv »%s« konnte nicht erstellt werden:"
 
-#: lib/choose_repository.tcl:387
+#: lib/choose_repository.tcl:391
 msgid "Directory:"
 msgstr "Verzeichnis:"
 
-#: lib/choose_repository.tcl:412 lib/choose_repository.tcl:537
-#: lib/choose_repository.tcl:1011
+#: lib/choose_repository.tcl:423 lib/choose_repository.tcl:550
+#: lib/choose_repository.tcl:1052
 msgid "Git Repository"
 msgstr "Git Projektarchiv"
 
-#: lib/choose_repository.tcl:442
+#: lib/choose_repository.tcl:448
 #, tcl-format
 msgid "Directory %s already exists."
 msgstr "Verzeichnis »%s« existiert bereits."
 
-#: lib/choose_repository.tcl:446
+#: lib/choose_repository.tcl:452
 #, tcl-format
 msgid "File %s already exists."
 msgstr "Datei »%s« existiert bereits."
 
-#: lib/choose_repository.tcl:455
+#: lib/choose_repository.tcl:466
 msgid "Clone"
 msgstr "Klonen"
 
-#: lib/choose_repository.tcl:473
+#: lib/choose_repository.tcl:479
 msgid "Source Location:"
 msgstr "Herkunft:"
 
-#: lib/choose_repository.tcl:484
+#: lib/choose_repository.tcl:490
 msgid "Target Directory:"
 msgstr "Zielverzeichnis:"
 
-#: lib/choose_repository.tcl:490
+#: lib/choose_repository.tcl:502
 msgid "Clone Type:"
 msgstr "Art des Klonens:"
 
-#: lib/choose_repository.tcl:495
+#: lib/choose_repository.tcl:508
 msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
 msgstr "Standard (schnell, teilweise redundant, Hardlinks)"
 
-#: lib/choose_repository.tcl:501
+#: lib/choose_repository.tcl:514
 msgid "Full Copy (Slower, Redundant Backup)"
 msgstr "Alles kopieren (langsamer, volle Redundanz)"
 
-#: lib/choose_repository.tcl:507
+#: lib/choose_repository.tcl:520
 msgid "Shared (Fastest, Not Recommended, No Backup)"
 msgstr "Verknüpft (schnell, nicht empfohlen, kein Backup)"
 
-#: lib/choose_repository.tcl:543 lib/choose_repository.tcl:590
-#: lib/choose_repository.tcl:736 lib/choose_repository.tcl:806
-#: lib/choose_repository.tcl:1017 lib/choose_repository.tcl:1025
+#: lib/choose_repository.tcl:556 lib/choose_repository.tcl:603
+#: lib/choose_repository.tcl:749 lib/choose_repository.tcl:819
+#: lib/choose_repository.tcl:1058 lib/choose_repository.tcl:1066
 #, tcl-format
 msgid "Not a Git repository: %s"
 msgstr "Kein Git-Projektarchiv in »%s« gefunden."
 
-#: lib/choose_repository.tcl:579
+#: lib/choose_repository.tcl:592
 msgid "Standard only available for local repository."
 msgstr "Standard ist nur für lokale Projektarchive verfügbar."
 
-#: lib/choose_repository.tcl:583
+#: lib/choose_repository.tcl:596
 msgid "Shared only available for local repository."
 msgstr "Verknüpft ist nur für lokale Projektarchive verfügbar."
 
-#: lib/choose_repository.tcl:604
+#: lib/choose_repository.tcl:617
 #, tcl-format
 msgid "Location %s already exists."
 msgstr "Projektarchiv »%s« existiert bereits."
 
-#: lib/choose_repository.tcl:615
+#: lib/choose_repository.tcl:628
 msgid "Failed to configure origin"
 msgstr "Der Ursprungsort konnte nicht eingerichtet werden"
 
-#: lib/choose_repository.tcl:627
+#: lib/choose_repository.tcl:640
 msgid "Counting objects"
 msgstr "Objekte werden gezählt"
 
-#: lib/choose_repository.tcl:628
+#: lib/choose_repository.tcl:641
 msgid "buckets"
 msgstr "Buckets"
 
-#: lib/choose_repository.tcl:652
+#: lib/choose_repository.tcl:665
 #, tcl-format
 msgid "Unable to copy objects/info/alternates: %s"
 msgstr "Kopien von Objekten/Info/Alternates konnten nicht erstellt werden: %s"
 
-#: lib/choose_repository.tcl:688
+#: lib/choose_repository.tcl:701
 #, tcl-format
 msgid "Nothing to clone from %s."
 msgstr "Von »%s« konnte nichts geklont werden."
 
-#: lib/choose_repository.tcl:690 lib/choose_repository.tcl:904
-#: lib/choose_repository.tcl:916
+#: lib/choose_repository.tcl:703 lib/choose_repository.tcl:917
+#: lib/choose_repository.tcl:929
 msgid "The 'master' branch has not been initialized."
 msgstr "Der »master«-Zweig wurde noch nicht initialisiert."
 
-#: lib/choose_repository.tcl:703
+#: lib/choose_repository.tcl:716
 msgid "Hardlinks are unavailable.  Falling back to copying."
 msgstr "Hardlinks nicht verfügbar. Stattdessen wird kopiert."
 
-#: lib/choose_repository.tcl:715
+#: lib/choose_repository.tcl:728
 #, tcl-format
 msgid "Cloning from %s"
 msgstr "Kopieren von »%s«"
 
-#: lib/choose_repository.tcl:746
+#: lib/choose_repository.tcl:759
 msgid "Copying objects"
 msgstr "Objektdatenbank kopieren"
 
-#: lib/choose_repository.tcl:747
+#: lib/choose_repository.tcl:760
 msgid "KiB"
 msgstr "KB"
 
-#: lib/choose_repository.tcl:771
+#: lib/choose_repository.tcl:784
 #, tcl-format
 msgid "Unable to copy object: %s"
 msgstr "Objekt kann nicht kopiert werden: %s"
 
-#: lib/choose_repository.tcl:781
+#: lib/choose_repository.tcl:794
 msgid "Linking objects"
 msgstr "Objekte verlinken"
 
-#: lib/choose_repository.tcl:782
+#: lib/choose_repository.tcl:795
 msgid "objects"
 msgstr "Objekte"
 
-#: lib/choose_repository.tcl:790
+#: lib/choose_repository.tcl:803
 #, tcl-format
 msgid "Unable to hardlink object: %s"
 msgstr "Für Objekt konnte kein Hardlink erstellt werden: %s"
 
-#: lib/choose_repository.tcl:845
+#: lib/choose_repository.tcl:858
 msgid "Cannot fetch branches and objects.  See console output for details."
 msgstr ""
 "Zweige und Objekte konnten nicht angefordert werden.  Kontrollieren Sie die "
 "Ausgaben auf der Konsole für weitere Angaben."
 
-#: lib/choose_repository.tcl:856
+#: lib/choose_repository.tcl:869
 msgid "Cannot fetch tags.  See console output for details."
 msgstr ""
 "Markierungen konnten nicht angefordert werden.  Kontrollieren Sie die "
 "Ausgaben auf der Konsole für weitere Angaben."
 
-#: lib/choose_repository.tcl:880
+#: lib/choose_repository.tcl:893
 msgid "Cannot determine HEAD.  See console output for details."
 msgstr ""
 "Die Zweigspitze (HEAD) konnte nicht gefunden werden.  Kontrollieren Sie die "
 "Ausgaben auf der Konsole für weitere Angaben."
 
-#: lib/choose_repository.tcl:889
+#: lib/choose_repository.tcl:902
 #, tcl-format
 msgid "Unable to cleanup %s"
 msgstr "Verzeichnis »%s« kann nicht aufgeräumt werden."
 
-#: lib/choose_repository.tcl:895
+#: lib/choose_repository.tcl:908
 msgid "Clone failed."
 msgstr "Klonen fehlgeschlagen."
 
-#: lib/choose_repository.tcl:902
+#: lib/choose_repository.tcl:915
 msgid "No default branch obtained."
 msgstr "Kein voreingestellter Zweig gefunden."
 
-#: lib/choose_repository.tcl:913
+#: lib/choose_repository.tcl:926
 #, tcl-format
 msgid "Cannot resolve %s as a commit."
 msgstr "»%s« wurde nicht als Version gefunden."
 
-#: lib/choose_repository.tcl:925
+#: lib/choose_repository.tcl:938
 msgid "Creating working directory"
 msgstr "Arbeitskopie erstellen"
 
-#: lib/choose_repository.tcl:926 lib/index.tcl:65 lib/index.tcl:127
-#: lib/index.tcl:193
+#: lib/choose_repository.tcl:939 lib/index.tcl:67 lib/index.tcl:130
+#: lib/index.tcl:198
 msgid "files"
 msgstr "Dateien"
 
-#: lib/choose_repository.tcl:955
+#: lib/choose_repository.tcl:968
 msgid "Initial file checkout failed."
 msgstr "Erstellen der Arbeitskopie fehlgeschlagen."
 
-#: lib/choose_repository.tcl:971
+#: lib/choose_repository.tcl:1011
 msgid "Open"
 msgstr "Öffnen"
 
-#: lib/choose_repository.tcl:981
+#: lib/choose_repository.tcl:1021
 msgid "Repository:"
 msgstr "Projektarchiv:"
 
-#: lib/choose_repository.tcl:1031
+#: lib/choose_repository.tcl:1072
 #, tcl-format
 msgid "Failed to open repository %s:"
 msgstr "Projektarchiv »%s« konnte nicht geöffnet werden."
@@ -1311,19 +1357,24 @@
 "unfertige Zusammenführung existiert. Dazu müssen Sie die Zusammenführung "
 "beenden oder abbrechen.\n"
 
-#: lib/commit.tcl:49
+#: lib/commit.tcl:48
 msgid "Error loading commit data for amend:"
 msgstr "Fehler beim Laden der Versionsdaten für Nachbessern:"
 
-#: lib/commit.tcl:76
+#: lib/commit.tcl:75
 msgid "Unable to obtain your identity:"
 msgstr "Benutzername konnte nicht bestimmt werden:"
 
-#: lib/commit.tcl:81
+#: lib/commit.tcl:80
 msgid "Invalid GIT_COMMITTER_IDENT:"
 msgstr "Ungültiger Wert von GIT_COMMITTER_INDENT:"
 
-#: lib/commit.tcl:133
+#: lib/commit.tcl:129
+#, tcl-format
+msgid "warning: Tcl does not support encoding '%s'."
+msgstr "Warning: Tcl/Tk unterstützt die Zeichencodierung »%s« nicht."
+
+#: lib/commit.tcl:149
 msgid ""
 "Last scanned state does not match repository state.\n"
 "\n"
@@ -1339,7 +1390,7 @@
 "\n"
 "Es wird gleich neu geladen.\n"
 
-#: lib/commit.tcl:154
+#: lib/commit.tcl:172
 #, tcl-format
 msgid ""
 "Unmerged files cannot be committed.\n"
@@ -1352,7 +1403,7 @@
 "Die Datei »%s« hat noch nicht aufgelöste Zusammenführungs-Konflikte. Sie "
 "müssen diese Konflikte auflösen, bevor Sie eintragen können.\n"
 
-#: lib/commit.tcl:162
+#: lib/commit.tcl:180
 #, tcl-format
 msgid ""
 "Unknown file state %s detected.\n"
@@ -1363,7 +1414,7 @@
 "\n"
 "Datei »%s« kann nicht eingetragen werden.\n"
 
-#: lib/commit.tcl:170
+#: lib/commit.tcl:188
 msgid ""
 "No changes to commit.\n"
 "\n"
@@ -1373,7 +1424,7 @@
 "\n"
 "Sie müssen mindestens eine Datei bereitstellen, bevor Sie eintragen können.\n"
 
-#: lib/commit.tcl:183
+#: lib/commit.tcl:203
 msgid ""
 "Please supply a commit message.\n"
 "\n"
@@ -1393,47 +1444,42 @@
 "\n"
 "- Rest: Eine ausführliche Beschreibung, warum diese Änderung hilfreich ist.\n"
 
-#: lib/commit.tcl:207
-#, tcl-format
-msgid "warning: Tcl does not support encoding '%s'."
-msgstr "Warning: Tcl/Tk unterstützt die Zeichencodierung »%s« nicht."
-
-#: lib/commit.tcl:221
+#: lib/commit.tcl:234
 msgid "Calling pre-commit hook..."
 msgstr "Aufrufen der Vor-Eintragen-Kontrolle..."
 
-#: lib/commit.tcl:236
+#: lib/commit.tcl:249
 msgid "Commit declined by pre-commit hook."
 msgstr "Eintragen abgelehnt durch Vor-Eintragen-Kontrolle (»pre-commit hook«)."
 
-#: lib/commit.tcl:259
+#: lib/commit.tcl:272
 msgid "Calling commit-msg hook..."
 msgstr "Aufrufen der Versionsbeschreibungs-Kontrolle..."
 
-#: lib/commit.tcl:274
+#: lib/commit.tcl:287
 msgid "Commit declined by commit-msg hook."
 msgstr ""
 "Eintragen abgelehnt durch Versionsbeschreibungs-Kontrolle (»commit-message "
 "hook«)."
 
-#: lib/commit.tcl:287
+#: lib/commit.tcl:300
 msgid "Committing changes..."
 msgstr "Änderungen eintragen..."
 
-#: lib/commit.tcl:303
+#: lib/commit.tcl:316
 msgid "write-tree failed:"
 msgstr "write-tree fehlgeschlagen:"
 
-#: lib/commit.tcl:304 lib/commit.tcl:348 lib/commit.tcl:368
+#: lib/commit.tcl:317 lib/commit.tcl:361 lib/commit.tcl:382
 msgid "Commit failed."
 msgstr "Eintragen fehlgeschlagen."
 
-#: lib/commit.tcl:321
+#: lib/commit.tcl:334
 #, tcl-format
 msgid "Commit %s appears to be corrupt"
 msgstr "Version »%s« scheint beschädigt zu sein"
 
-#: lib/commit.tcl:326
+#: lib/commit.tcl:339
 msgid ""
 "No changes to commit.\n"
 "\n"
@@ -1448,19 +1494,19 @@
 "\n"
 "Das Arbeitsverzeichnis wird daher jetzt neu geladen.\n"
 
-#: lib/commit.tcl:333
+#: lib/commit.tcl:346
 msgid "No changes to commit."
 msgstr "Keine Änderungen, die eingetragen werden können."
 
-#: lib/commit.tcl:347
+#: lib/commit.tcl:360
 msgid "commit-tree failed:"
 msgstr "commit-tree fehlgeschlagen:"
 
-#: lib/commit.tcl:367
+#: lib/commit.tcl:381
 msgid "update-ref failed:"
 msgstr "update-ref fehlgeschlagen:"
 
-#: lib/commit.tcl:454
+#: lib/commit.tcl:469
 #, tcl-format
 msgid "Created commit %s: %s"
 msgstr "Version %s übertragen: %s"
@@ -1513,21 +1559,19 @@
 msgid "Verifying the object database with fsck-objects"
 msgstr "Die Objektdatenbank durch »fsck-objects« überprüfen lassen"
 
-#: lib/database.tcl:108
+#: lib/database.tcl:107
 #, tcl-format
 msgid ""
 "This repository currently has approximately %i loose objects.\n"
 "\n"
 "To maintain optimal performance it is strongly recommended that you compress "
-"the database when more than %i loose objects exist.\n"
+"the database.\n"
 "\n"
 "Compress the database now?"
 msgstr ""
 "Dieses Projektarchiv enthält ungefähr %i nicht verknüpfte Objekte.\n"
 "\n"
-"Für eine optimale Performance wird empfohlen, die Datenbank des "
-"Projektarchivs zu komprimieren, sobald mehr als %i nicht verknüpfte Objekte "
-"vorliegen.\n"
+"Für eine optimale Performance wird empfohlen, die Datenbank des Projektarchivs zu komprimieren.\n"
 "\n"
 "Soll die Datenbank jetzt komprimiert werden?"
 
@@ -1536,7 +1580,7 @@
 msgid "Invalid date from Git: %s"
 msgstr "Ungültiges Datum von Git: %s"
 
-#: lib/diff.tcl:42
+#: lib/diff.tcl:64
 #, tcl-format
 msgid ""
 "No differences detected.\n"
@@ -1551,19 +1595,19 @@
 msgstr ""
 "Keine Änderungen feststellbar.\n"
 "\n"
-"»%s« enthält keine Änderungen. Zwar wurde das Änderungsdatum dieser Datei "
-"von einem anderen Programm modifiziert, aber der Inhalt der Datei ist "
+"»%s« enthält keine Änderungen. Zwar wurde das Änderungsdatum dieser Datei von "
+"einem anderen Programm modifiziert, aber der Inhalt der Datei ist "
 "unverändert.\n"
 "\n"
 "Das Arbeitsverzeichnis wird jetzt neu geladen, um diese Änderung bei allen "
 "Dateien zu prüfen."
 
-#: lib/diff.tcl:81
+#: lib/diff.tcl:104
 #, tcl-format
 msgid "Loading diff of %s..."
 msgstr "Vergleich von »%s« laden..."
 
-#: lib/diff.tcl:120
+#: lib/diff.tcl:125
 msgid ""
 "LOCAL: deleted\n"
 "REMOTE:\n"
@@ -1571,7 +1615,7 @@
 "LOKAL: gelöscht\n"
 "ANDERES:\n"
 
-#: lib/diff.tcl:125
+#: lib/diff.tcl:130
 msgid ""
 "REMOTE: deleted\n"
 "LOCAL:\n"
@@ -1579,32 +1623,32 @@
 "ANDERES: gelöscht\n"
 "LOKAL:\n"
 
-#: lib/diff.tcl:132
+#: lib/diff.tcl:137
 msgid "LOCAL:\n"
 msgstr "LOKAL:\n"
 
-#: lib/diff.tcl:135
+#: lib/diff.tcl:140
 msgid "REMOTE:\n"
 msgstr "ANDERES:\n"
 
-#: lib/diff.tcl:197 lib/diff.tcl:296
+#: lib/diff.tcl:202 lib/diff.tcl:319
 #, tcl-format
 msgid "Unable to display %s"
 msgstr "Datei »%s« kann nicht angezeigt werden"
 
-#: lib/diff.tcl:115
+#: lib/diff.tcl:203
 msgid "Error loading file:"
 msgstr "Fehler beim Laden der Datei:"
 
-#: lib/diff.tcl:122
+#: lib/diff.tcl:210
 msgid "Git Repository (subproject)"
 msgstr "Git-Projektarchiv (Unterprojekt)"
 
-#: lib/diff.tcl:134
+#: lib/diff.tcl:222
 msgid "* Binary file (not showing content)."
 msgstr "* Binärdatei (Inhalt wird nicht angezeigt)"
 
-#: lib/diff.tcl:222
+#: lib/diff.tcl:227
 #, tcl-format
 msgid ""
 "* Untracked file is %d bytes.\n"
@@ -1613,7 +1657,7 @@
 "* Datei nicht unter Versionskontrolle, Dateigröße %d Bytes.\n"
 "* Nur erste %d Bytes werden angezeigt.\n"
 
-#: lib/diff.tcl:228
+#: lib/diff.tcl:233
 #, tcl-format
 msgid ""
 "\n"
@@ -1624,20 +1668,20 @@
 "* Datei nicht unter Versionskontrolle, hier abgeschnitten durch %s.\n"
 "* Zum Ansehen der vollständigen Datei externen Editor benutzen.\n"
 
-#: lib/diff.tcl:436
+#: lib/diff.tcl:482
 msgid "Failed to unstage selected hunk."
 msgstr ""
 "Fehler beim Herausnehmen des gewählten Kontexts aus der Bereitstellung."
 
-#: lib/diff.tcl:310
+#: lib/diff.tcl:489
 msgid "Failed to stage selected hunk."
 msgstr "Fehler beim Bereitstellen des gewählten Kontexts."
 
-#: lib/diff.tcl:386
+#: lib/diff.tcl:568
 msgid "Failed to unstage selected line."
 msgstr "Fehler beim Herausnehmen der gewählten Zeile aus der Bereitstellung."
 
-#: lib/diff.tcl:394
+#: lib/diff.tcl:576
 msgid "Failed to stage selected line."
 msgstr "Fehler beim Bereitstellen der gewählten Zeile."
 
@@ -1675,7 +1719,7 @@
 msgid "Index Error"
 msgstr "Fehler in Bereitstellung"
 
-#: lib/index.tcl:21
+#: lib/index.tcl:17
 msgid ""
 "Updating the Git index failed.  A rescan will be automatically started to "
 "resynchronize git-gui."
@@ -1684,7 +1728,7 @@
 "Git-Aktualisierung wird jetzt gestartet, um git-gui wieder mit git zu "
 "synchronisieren."
 
-#: lib/index.tcl:27
+#: lib/index.tcl:28
 msgid "Continue"
 msgstr "Fortsetzen"
 
@@ -1692,44 +1736,44 @@
 msgid "Unlock Index"
 msgstr "Bereitstellung freigeben"
 
-#: lib/index.tcl:282
+#: lib/index.tcl:289
 #, tcl-format
 msgid "Unstaging %s from commit"
 msgstr "Datei »%s« aus der Bereitstellung herausnehmen"
 
-#: lib/index.tcl:313
+#: lib/index.tcl:328
 msgid "Ready to commit."
 msgstr "Bereit zum Eintragen."
 
-#: lib/index.tcl:326
+#: lib/index.tcl:341
 #, tcl-format
 msgid "Adding %s"
 msgstr "»%s« hinzufügen..."
 
-#: lib/index.tcl:381
+#: lib/index.tcl:398
 #, tcl-format
 msgid "Revert changes in file %s?"
 msgstr "Änderungen in Datei »%s« verwerfen?"
 
-#: lib/index.tcl:383
+#: lib/index.tcl:400
 #, tcl-format
 msgid "Revert changes in these %i files?"
 msgstr "Änderungen in den gewählten %i Dateien verwerfen?"
 
-#: lib/index.tcl:391
+#: lib/index.tcl:408
 msgid "Any unstaged changes will be permanently lost by the revert."
 msgstr ""
 "Alle nicht bereitgestellten Änderungen werden beim Verwerfen verloren gehen."
 
-#: lib/index.tcl:394
+#: lib/index.tcl:411
 msgid "Do Nothing"
 msgstr "Nichts tun"
 
-#: lib/index.tcl:419
+#: lib/index.tcl:429
 msgid "Reverting selected files"
 msgstr "Änderungen in gewählten Dateien verwerfen"
 
-#: lib/index.tcl:423
+#: lib/index.tcl:433
 #, tcl-format
 msgid "Reverting %s"
 msgstr "Änderungen in %s verwerfen"
@@ -1761,7 +1805,7 @@
 "\n"
 "Es wird gleich neu geladen.\n"
 
-#: lib/merge.tcl:44
+#: lib/merge.tcl:45
 #, tcl-format
 msgid ""
 "You are in the middle of a conflicted merge.\n"
@@ -1778,7 +1822,7 @@
 "bereitstellen und eintragen, um die Zusammenführung abzuschließen. Erst "
 "danach kann eine neue Zusammenführung begonnen werden.\n"
 
-#: lib/merge.tcl:54
+#: lib/merge.tcl:55
 #, tcl-format
 msgid ""
 "You are in the middle of a change.\n"
@@ -1795,34 +1839,34 @@
 "Reihenfolge können Sie mögliche Konflikte beim Zusammenführen wesentlich "
 "einfacher beheben oder abbrechen.\n"
 
-#: lib/merge.tcl:106
+#: lib/merge.tcl:107
 #, tcl-format
 msgid "%s of %s"
 msgstr "%s von %s"
 
-#: lib/merge.tcl:119
+#: lib/merge.tcl:120
 #, tcl-format
 msgid "Merging %s and %s..."
 msgstr "Zusammenführen von %s und %s..."
 
-#: lib/merge.tcl:130
+#: lib/merge.tcl:131
 msgid "Merge completed successfully."
 msgstr "Zusammenführen erfolgreich abgeschlossen."
 
-#: lib/merge.tcl:132
+#: lib/merge.tcl:133
 msgid "Merge failed.  Conflict resolution is required."
 msgstr "Zusammenführen fehlgeschlagen. Konfliktauflösung ist notwendig."
 
-#: lib/merge.tcl:157
+#: lib/merge.tcl:158
 #, tcl-format
 msgid "Merge Into %s"
 msgstr "Zusammenführen in »%s«"
 
-#: lib/merge.tcl:176
+#: lib/merge.tcl:177
 msgid "Revision To Merge"
 msgstr "Zusammenzuführende Version"
 
-#: lib/merge.tcl:211
+#: lib/merge.tcl:212
 msgid ""
 "Cannot abort while amending.\n"
 "\n"
@@ -1832,7 +1876,7 @@
 "\n"
 "Sie müssen die Nachbesserung der Version abschließen.\n"
 
-#: lib/merge.tcl:221
+#: lib/merge.tcl:222
 msgid ""
 "Abort merge?\n"
 "\n"
@@ -1847,7 +1891,7 @@
 "\n"
 "Zusammenführen jetzt abbrechen?"
 
-#: lib/merge.tcl:227
+#: lib/merge.tcl:228
 msgid ""
 "Reset changes?\n"
 "\n"
@@ -1862,35 +1906,35 @@
 "\n"
 "Änderungen jetzt zurücksetzen?"
 
-#: lib/merge.tcl:238
+#: lib/merge.tcl:239
 msgid "Aborting"
 msgstr "Abbruch"
 
-#: lib/merge.tcl:238
+#: lib/merge.tcl:239
 msgid "files reset"
 msgstr "Dateien zurückgesetzt"
 
-#: lib/merge.tcl:265
+#: lib/merge.tcl:267
 msgid "Abort failed."
 msgstr "Abbruch fehlgeschlagen."
 
-#: lib/merge.tcl:267
+#: lib/merge.tcl:269
 msgid "Abort completed.  Ready."
 msgstr "Abbruch durchgeführt. Bereit."
 
-#: lib/mergetool.tcl:14
+#: lib/mergetool.tcl:8
 msgid "Force resolution to the base version?"
 msgstr "Konflikt durch Basisversion ersetzen?"
 
-#: lib/mergetool.tcl:15
+#: lib/mergetool.tcl:9
 msgid "Force resolution to this branch?"
 msgstr "Konflikt durch diesen Zweig ersetzen?"
 
-#: lib/mergetool.tcl:16
+#: lib/mergetool.tcl:10
 msgid "Force resolution to the other branch?"
 msgstr "Konflikt durch anderen Zweig ersetzen?"
 
-#: lib/mergetool.tcl:20
+#: lib/mergetool.tcl:14
 #, tcl-format
 msgid ""
 "Note that the diff shows only conflicting changes.\n"
@@ -1916,31 +1960,31 @@
 msgid "Adding resolution for %s"
 msgstr "Auflösung hinzugefügt für %s"
 
-#: lib/mergetool.tcl:119
+#: lib/mergetool.tcl:141
 msgid "Cannot resolve deletion or link conflicts using a tool"
 msgstr ""
 "Konflikte durch gelöschte Dateien oder symbolische Links können nicht durch "
 "das Zusamenführungswerkzeug gelöst werden."
 
-#: lib/mergetool.tcl:124
+#: lib/mergetool.tcl:146
 msgid "Conflict file does not exist"
 msgstr "Konflikt-Datei existiert nicht"
 
-#: lib/mergetool.tcl:236
+#: lib/mergetool.tcl:264
 #, tcl-format
 msgid "Not a GUI merge tool: '%s'"
 msgstr "Kein GUI Zusammenführungswerkzeug: »%s«"
 
-#: lib/mergetool.tcl:240
+#: lib/mergetool.tcl:268
 #, tcl-format
 msgid "Unsupported merge tool '%s'"
 msgstr "Unbekanntes Zusammenführungswerkzeug: »%s«"
 
-#: lib/mergetool.tcl:275
+#: lib/mergetool.tcl:303
 msgid "Merge tool is already running, terminate it?"
 msgstr "Zusammenführungswerkzeug läuft bereits. Soll es abgebrochen werden?"
 
-#: lib/mergetool.tcl:295
+#: lib/mergetool.tcl:323
 #, tcl-format
 msgid ""
 "Error retrieving versions:\n"
@@ -1949,7 +1993,7 @@
 "Fehler beim Abrufen der Dateiversionen:\n"
 "%s"
 
-#: lib/mergetool.tcl:315
+#: lib/mergetool.tcl:343
 #, tcl-format
 msgid ""
 "Could not start the merge tool:\n"
@@ -1960,11 +2004,11 @@
 "\n"
 "%s"
 
-#: lib/mergetool.tcl:319
+#: lib/mergetool.tcl:347
 msgid "Running merge tool..."
 msgstr "Zusammenführungswerkzeug starten..."
 
-#: lib/mergetool.tcl:347 lib/mergetool.tcl:363
+#: lib/mergetool.tcl:375 lib/mergetool.tcl:383
 msgid "Merge tool failed."
 msgstr "Zusammenführungswerkzeug fehlgeschlagen."
 
@@ -1982,76 +2026,76 @@
 msgid "Restore Defaults"
 msgstr "Voreinstellungen wiederherstellen"
 
-#: lib/option.tcl:99
+#: lib/option.tcl:121
 msgid "Save"
 msgstr "Speichern"
 
-#: lib/option.tcl:109
+#: lib/option.tcl:131
 #, tcl-format
 msgid "%s Repository"
 msgstr "Projektarchiv %s"
 
-#: lib/option.tcl:110
+#: lib/option.tcl:132
 msgid "Global (All Repositories)"
 msgstr "Global (Alle Projektarchive)"
 
-#: lib/option.tcl:116
+#: lib/option.tcl:138
 msgid "User Name"
 msgstr "Benutzername"
 
-#: lib/option.tcl:117
+#: lib/option.tcl:139
 msgid "Email Address"
 msgstr "E-Mail-Adresse"
 
-#: lib/option.tcl:119
+#: lib/option.tcl:141
 msgid "Summarize Merge Commits"
 msgstr "Zusammenführungs-Versionen zusammenfassen"
 
-#: lib/option.tcl:120
+#: lib/option.tcl:142
 msgid "Merge Verbosity"
 msgstr "Ausführlichkeit der Zusammenführen-Meldungen"
 
-#: lib/option.tcl:121
+#: lib/option.tcl:143
 msgid "Show Diffstat After Merge"
 msgstr "Vergleichsstatistik nach Zusammenführen anzeigen"
 
-#: lib/option.tcl:122
+#: lib/option.tcl:144
 msgid "Use Merge Tool"
 msgstr "Zusammenführungswerkzeug"
 
-#: lib/option.tcl:124
+#: lib/option.tcl:146
 msgid "Trust File Modification Timestamps"
 msgstr "Auf Dateiänderungsdatum verlassen"
 
-#: lib/option.tcl:124
+#: lib/option.tcl:147
 msgid "Prune Tracking Branches During Fetch"
 msgstr "Übernahmezweige aufräumen während Anforderung"
 
-#: lib/option.tcl:125
+#: lib/option.tcl:148
 msgid "Match Tracking Branches"
 msgstr "Passend zu Übernahmezweig"
 
-#: lib/option.tcl:126
+#: lib/option.tcl:149
 msgid "Blame Copy Only On Changed Files"
 msgstr "Kopie-Annotieren nur bei geänderten Dateien"
 
-#: lib/option.tcl:127
+#: lib/option.tcl:150
 msgid "Minimum Letters To Blame Copy On"
 msgstr "Mindestzahl Zeichen für Kopie-Annotieren"
 
-#: lib/option.tcl:128
+#: lib/option.tcl:151
 msgid "Blame History Context Radius (days)"
 msgstr "Anzahl Tage für Historien-Kontext"
 
-#: lib/option.tcl:129
+#: lib/option.tcl:152
 msgid "Number of Diff Context Lines"
 msgstr "Anzahl der Kontextzeilen beim Vergleich"
 
-#: lib/option.tcl:127
+#: lib/option.tcl:153
 msgid "Commit Message Text Width"
 msgstr "Textbreite der Versionsbeschreibung"
 
-#: lib/option.tcl:128
+#: lib/option.tcl:154
 msgid "New Branch Name Template"
 msgstr "Namensvorschlag für neue Zweige"
 
@@ -2067,24 +2111,24 @@
 msgid "Spelling Dictionary:"
 msgstr "Wörterbuch Rechtschreibprüfung:"
 
-#: lib/option.tcl:216
+#: lib/option.tcl:254
 msgid "Change Font"
 msgstr "Schriftart ändern"
 
-#: lib/option.tcl:220
+#: lib/option.tcl:258
 #, tcl-format
 msgid "Choose %s"
 msgstr "%s wählen"
 
-#: lib/option.tcl:226
+#: lib/option.tcl:264
 msgid "pt."
 msgstr "pt."
 
-#: lib/option.tcl:240
+#: lib/option.tcl:278
 msgid "Preferences"
 msgstr "Einstellungen"
 
-#: lib/option.tcl:275
+#: lib/option.tcl:314
 msgid "Failed to completely save options:"
 msgstr "Optionen konnten nicht gespeichert werden:"
 
@@ -2096,7 +2140,7 @@
 msgid "Add New Remote"
 msgstr "Neues anderes Archiv hinzufügen"
 
-#: lib/remote_add.tcl:28
+#: lib/remote_add.tcl:28 lib/tools_dlg.tcl:36
 msgid "Add"
 msgstr "Hinzufügen"
 
@@ -2151,8 +2195,7 @@
 #: lib/remote_add.tcl:157
 #, tcl-format
 msgid "Do not know how to initialize repository at location '%s'."
-msgstr ""
-"Initialisieren eines anderen Archivs an Adresse »%s« ist nicht möglich."
+msgstr "Initialisieren eines anderen Archivs an Adresse »%s« ist nicht möglich."
 
 #: lib/remote_add.tcl:163 lib/transport.tcl:25 lib/transport.tcl:63
 #: lib/transport.tcl:81
@@ -2173,11 +2216,11 @@
 msgid "From Repository"
 msgstr "In Projektarchiv"
 
-#: lib/remote_branch_delete.tcl:50 lib/transport.tcl:123
+#: lib/remote_branch_delete.tcl:50 lib/transport.tcl:134
 msgid "Remote:"
 msgstr "Anderes Archiv:"
 
-#: lib/remote_branch_delete.tcl:66 lib/transport.tcl:138
+#: lib/remote_branch_delete.tcl:66 lib/transport.tcl:149
 msgid "Arbitrary Location:"
 msgstr "Adresse:"
 
@@ -2193,10 +2236,6 @@
 msgid "Merged Into:"
 msgstr "Zusammengeführt mit:"
 
-#: lib/remote_branch_delete.tcl:119
-msgid "Always (Do not perform merge checks)"
-msgstr "Immer (Keine Zusammenführungsprüfung)"
-
 #: lib/remote_branch_delete.tcl:152
 msgid "A branch is required for 'Merged Into'."
 msgstr "Für »Zusammenführen mit« muss ein Zweig angegeben werden."
@@ -2226,27 +2265,16 @@
 msgid "Please select one or more branches to delete."
 msgstr "Bitte wählen Sie mindestens einen Zweig, der gelöscht werden soll."
 
-#: lib/remote_branch_delete.tcl:216
-msgid ""
-"Recovering deleted branches is difficult.\n"
-"\n"
-"Delete the selected branches?"
-msgstr ""
-"Das Wiederherstellen von gelöschten Zweigen ist nur mit größerem Aufwand "
-"möglich.\n"
-"\n"
-"Sollen die ausgewählten Zweige gelöscht werden?"
-
 #: lib/remote_branch_delete.tcl:226
 #, tcl-format
 msgid "Deleting branches from %s"
 msgstr "Zweige auf »%s« werden gelöscht"
 
-#: lib/remote_branch_delete.tcl:286
+#: lib/remote_branch_delete.tcl:292
 msgid "No repository selected."
 msgstr "Kein Projektarchiv ausgewählt."
 
-#: lib/remote_branch_delete.tcl:291
+#: lib/remote_branch_delete.tcl:297
 #, tcl-format
 msgid "Scanning %s..."
 msgstr "»%s« laden..."
@@ -2259,11 +2287,11 @@
 msgid "Prune from"
 msgstr "Aufräumen von"
 
-#: lib/remote.tcl:170
+#: lib/remote.tcl:173
 msgid "Fetch from"
 msgstr "Anfordern von"
 
-#: lib/remote.tcl:213
+#: lib/remote.tcl:215
 msgid "Push to"
 msgstr "Versenden nach"
 
@@ -2271,11 +2299,11 @@
 msgid "Find:"
 msgstr "Suchen:"
 
-#: lib/search.tcl:22
+#: lib/search.tcl:23
 msgid "Next"
 msgstr "Nächster"
 
-#: lib/search.tcl:23
+#: lib/search.tcl:24
 msgid "Prev"
 msgstr "Voriger"
 
@@ -2283,11 +2311,11 @@
 msgid "Case-Sensitive"
 msgstr "Groß-/Kleinschreibung unterscheiden"
 
-#: lib/shortcut.tcl:20 lib/shortcut.tcl:61
+#: lib/shortcut.tcl:21 lib/shortcut.tcl:62
 msgid "Cannot write shortcut:"
 msgstr "Fehler beim Schreiben der Verknüpfung:"
 
-#: lib/shortcut.tcl:136
+#: lib/shortcut.tcl:137
 msgid "Cannot write icon:"
 msgstr "Fehler beim Erstellen des Icons:"
 
@@ -2316,15 +2344,15 @@
 msgid "Unrecognized spell checker"
 msgstr "Unbekanntes Rechtschreibprüfungsprogramm"
 
-#: lib/spellcheck.tcl:180
+#: lib/spellcheck.tcl:186
 msgid "No Suggestions"
 msgstr "Keine Vorschläge"
 
-#: lib/spellcheck.tcl:381
+#: lib/spellcheck.tcl:388
 msgid "Unexpected EOF from spell checker"
 msgstr "Unerwartetes EOF vom Rechtschreibprüfungsprogramm"
 
-#: lib/spellcheck.tcl:385
+#: lib/spellcheck.tcl:392
 msgid "Spell Checker Failed"
 msgstr "Rechtschreibprüfung fehlgeschlagen"
 
@@ -2534,31 +2562,31 @@
 msgid "Pushing %s %s to %s"
 msgstr "%s %s nach %s versenden"
 
-#: lib/transport.tcl:89
+#: lib/transport.tcl:100
 msgid "Push Branches"
 msgstr "Zweige versenden"
 
-#: lib/transport.tcl:103
+#: lib/transport.tcl:114
 msgid "Source Branches"
 msgstr "Lokale Zweige"
 
-#: lib/transport.tcl:120
+#: lib/transport.tcl:131
 msgid "Destination Repository"
 msgstr "Ziel-Projektarchiv"
 
-#: lib/transport.tcl:158
+#: lib/transport.tcl:169
 msgid "Transfer Options"
 msgstr "Netzwerk-Einstellungen"
 
-#: lib/transport.tcl:160
+#: lib/transport.tcl:171
 msgid "Force overwrite existing branch (may discard changes)"
 msgstr ""
 "Überschreiben von existierenden Zweigen erzwingen (könnte Änderungen löschen)"
 
-#: lib/transport.tcl:164
+#: lib/transport.tcl:175
 msgid "Use thin pack (for slow network connections)"
 msgstr "Kompaktes Datenformat benutzen (für langsame Netzverbindungen)"
 
-#: lib/transport.tcl:168
+#: lib/transport.tcl:179
 msgid "Include tags"
 msgstr "Mit Markierungen übertragen"
diff --git a/git-gui/po/el.po b/git-gui/po/el.po
new file mode 100644
index 0000000..3634ba4
--- /dev/null
+++ b/git-gui/po/el.po
@@ -0,0 +1,2005 @@
+# Translation of git-gui to Greek
+# Copyright (C) 2009 Jimmy Angelakos
+# This file is distributed under the same license as the git-gui package.
+# Jimmy Angelakos <vyruss@hellug.gr>, 2009.
+msgid ""
+msgstr ""
+"Project-Id-Version: el\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2008-03-14 07:18+0100\n"
+"PO-Revision-Date: 2009-06-23 21:33+0300\n"
+"Last-Translator: Jimmy Angelakos <vyruss@hellug.gr>\n"
+"Language-Team: Greek <i18n@lists.hellug.gr>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Lokalize 0.3\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: git-gui.sh:41 git-gui.sh:634 git-gui.sh:648 git-gui.sh:661 git-gui.sh:744
+#: git-gui.sh:763
+msgid "git-gui: fatal error"
+msgstr "git-gui: κρίσιμο σφάλμα"
+
+#: git-gui.sh:593
+#, tcl-format
+msgid "Invalid font specified in %s:"
+msgstr "Μη έγκυρη γραμματοσειρά στο %s:"
+
+#: git-gui.sh:620
+msgid "Main Font"
+msgstr "Κύρια Γραμματοσειρά"
+
+#: git-gui.sh:621
+msgid "Diff/Console Font"
+msgstr "Γραμματοσειρά Διαφοράς/Κονσόλας"
+
+#: git-gui.sh:635
+msgid "Cannot find git in PATH."
+msgstr "Δε βρέθηκε το git στο PATH."
+
+#: git-gui.sh:662
+msgid "Cannot parse Git version string:"
+msgstr "Αδύνατη η ανάγνωση στοιχειοσειράς έκδοσης Git:"
+
+#: git-gui.sh:680
+#, tcl-format
+msgid ""
+"Git version cannot be determined.\n"
+"\n"
+"%s claims it is version '%s'.\n"
+"\n"
+"%s requires at least Git 1.5.0 or later.\n"
+"\n"
+"Assume '%s' is version 1.5.0?\n"
+msgstr ""
+"Δε μπορεί να ανιχνευτεί η έκδοση του Git. \n"
+"\n"
+"Το %s υποστηρίζει πως είναι η έκδοση '%s'.\n"
+"\n"
+"Το %s  απαιτεί τουλάχιστον Git 1.5.0 ή πιό πρόσφατη.\n"
+"\n"
+"Να υποτεθεί πως το '%s' είναι η έκδοση 1.5.0;\n"
+
+#: git-gui.sh:918
+msgid "Git directory not found:"
+msgstr "Δε βρέθηκε φάκελος Git:"
+
+#: git-gui.sh:925
+msgid "Cannot move to top of working directory:"
+msgstr "Δεν είναι δυνατή η μετακίνηση στην κορυφή του φακέλου εργασίας:"
+
+#: git-gui.sh:932
+msgid "Cannot use funny .git directory:"
+msgstr "Δεν είναι δυνατή η χρήση περίεργου φακέλου .git:"
+
+#: git-gui.sh:937
+msgid "No working directory"
+msgstr "Δεν υπάρχει φάκελος εργασίας"
+
+#: git-gui.sh:1084 lib/checkout_op.tcl:283
+msgid "Refreshing file status..."
+msgstr "Ανανέωση κατάστασης αρχείου..."
+
+#: git-gui.sh:1149
+msgid "Scanning for modified files ..."
+msgstr "Ανίχνευση για τροποποιημένα αρχεία..."
+
+#: git-gui.sh:1324 lib/browser.tcl:246
+msgid "Ready."
+msgstr "Έτοιμο."
+
+#: git-gui.sh:1590
+msgid "Unmodified"
+msgstr "Μη τροποποιημένο"
+
+#: git-gui.sh:1592
+msgid "Modified, not staged"
+msgstr "Τροποποιημένο, μη σταδιοποιημένο"
+
+#: git-gui.sh:1593 git-gui.sh:1598
+msgid "Staged for commit"
+msgstr "Σταδιοποιημένο προς υποβολή"
+
+#: git-gui.sh:1594 git-gui.sh:1599
+msgid "Portions staged for commit"
+msgstr "Μέρη σταδιοποιημένα προς υποβολή"
+
+#: git-gui.sh:1595 git-gui.sh:1600
+msgid "Staged for commit, missing"
+msgstr "Σταδιοποιημένο προς υποβολή, λείπει"
+
+#: git-gui.sh:1597
+msgid "Untracked, not staged"
+msgstr "Μη παρακολουθούμενο, μη σταδιοποιημένο"
+
+#: git-gui.sh:1602
+msgid "Missing"
+msgstr "Λείπει"
+
+#: git-gui.sh:1603
+msgid "Staged for removal"
+msgstr "Σταδιοποιημένο προς αφαίρεση"
+
+#: git-gui.sh:1604
+msgid "Staged for removal, still present"
+msgstr "Σταδιοποιημένο προς αφαίρεση, ακόμα παρόν"
+
+#: git-gui.sh:1606 git-gui.sh:1607 git-gui.sh:1608 git-gui.sh:1609
+msgid "Requires merge resolution"
+msgstr "Απαιτεί επίλυση συγχώνευσης"
+
+#: git-gui.sh:1644
+msgid "Starting gitk... please wait..."
+msgstr "Γίνεται εκκίνηση του gitk... παρακαλώ περιμένετε..."
+
+#: git-gui.sh:1653
+#, tcl-format
+msgid ""
+"Unable to start gitk:\n"
+"\n"
+"%s does not exist"
+msgstr ""
+"Αδυναμία εκκίνησης του gitk:\n"
+"\n"
+"Το %s δεν υπάρχει"
+
+#: git-gui.sh:1860 lib/choose_repository.tcl:36
+msgid "Repository"
+msgstr "Αποθετήριο"
+
+#: git-gui.sh:1861
+msgid "Edit"
+msgstr "Επεξεργασία"
+
+#: git-gui.sh:1863 lib/choose_rev.tcl:561
+msgid "Branch"
+msgstr "Κλάδος"
+
+#: git-gui.sh:1866 lib/choose_rev.tcl:548
+msgid "Commit@@noun"
+msgstr "Υποβολή@@noun"
+
+#: git-gui.sh:1869 lib/merge.tcl:120 lib/merge.tcl:149 lib/merge.tcl:167
+msgid "Merge"
+msgstr "Συγχώνευση"
+
+#: git-gui.sh:1870 lib/choose_rev.tcl:557
+msgid "Remote"
+msgstr "Απομακρυσμένο"
+
+#: git-gui.sh:1879
+msgid "Browse Current Branch's Files"
+msgstr "Περιήγηση Αρχείων Τρέχοντα Κλάδου"
+
+#: git-gui.sh:1883
+msgid "Browse Branch Files..."
+msgstr "Περιήγηση Αρχείων Κλάδου..."
+
+#: git-gui.sh:1888
+msgid "Visualize Current Branch's History"
+msgstr "Απεικόνιση Ιστορικού Τρέχοντα Κλάδου"
+
+#: git-gui.sh:1892
+msgid "Visualize All Branch History"
+msgstr "Απεικόνιση Ιστορικού Όλων των Κλάδων"
+
+#: git-gui.sh:1899
+#, tcl-format
+msgid "Browse %s's Files"
+msgstr "Περιήγηση Αρχείων του %s"
+
+#: git-gui.sh:1901
+#, tcl-format
+msgid "Visualize %s's History"
+msgstr "Απεικόνιση Ιστορικού του %s"
+
+#: git-gui.sh:1906 lib/database.tcl:27 lib/database.tcl:67
+msgid "Database Statistics"
+msgstr "Στατιστικά Βάσης Δεδομένων"
+
+#: git-gui.sh:1909 lib/database.tcl:34
+msgid "Compress Database"
+msgstr "Συμπίεση Βάσης Δεδομένων"
+
+#: git-gui.sh:1912
+msgid "Verify Database"
+msgstr "Επαλήθευση Βάσης Δεδομένων"
+
+#: git-gui.sh:1919 git-gui.sh:1923 git-gui.sh:1927 lib/shortcut.tcl:7
+#: lib/shortcut.tcl:39 lib/shortcut.tcl:71
+msgid "Create Desktop Icon"
+msgstr "Δημιουργία Εικονιδίου Επιφάνειας Εργασίας"
+
+#: git-gui.sh:1932 lib/choose_repository.tcl:177 lib/choose_repository.tcl:185
+msgid "Quit"
+msgstr "Έξοδος"
+
+#: git-gui.sh:1939
+msgid "Undo"
+msgstr "Αναίρεση"
+
+#: git-gui.sh:1942
+msgid "Redo"
+msgstr "Ξανά"
+
+#: git-gui.sh:1946 git-gui.sh:2443
+msgid "Cut"
+msgstr "Αποκοπή"
+
+#: git-gui.sh:1949 git-gui.sh:2446 git-gui.sh:2520 git-gui.sh:2614
+#: lib/console.tcl:69
+msgid "Copy"
+msgstr "Αντιγραφή"
+
+#: git-gui.sh:1952 git-gui.sh:2449
+msgid "Paste"
+msgstr "Επικόλληση"
+
+#: git-gui.sh:1955 git-gui.sh:2452 lib/branch_delete.tcl:26
+#: lib/remote_branch_delete.tcl:38
+msgid "Delete"
+msgstr "Διαγραφή"
+
+#: git-gui.sh:1959 git-gui.sh:2456 git-gui.sh:2618 lib/console.tcl:71
+msgid "Select All"
+msgstr "Επιλογή Όλων"
+
+#: git-gui.sh:1968
+msgid "Create..."
+msgstr "Δημιουργία..."
+
+#: git-gui.sh:1974
+msgid "Checkout..."
+msgstr "Εξαγωγή..."
+
+#: git-gui.sh:1980
+msgid "Rename..."
+msgstr "Μετονομασία..."
+
+#: git-gui.sh:1985 git-gui.sh:2085
+msgid "Delete..."
+msgstr "Διαγραφή..."
+
+#: git-gui.sh:1990
+msgid "Reset..."
+msgstr "Επαναφορά..."
+
+#: git-gui.sh:2002 git-gui.sh:2389
+msgid "New Commit"
+msgstr "Νέα Υποβολή"
+
+#: git-gui.sh:2010 git-gui.sh:2396
+msgid "Amend Last Commit"
+msgstr "Διόρθωση Τελευταίας Υποβολής"
+
+#: git-gui.sh:2019 git-gui.sh:2356 lib/remote_branch_delete.tcl:99
+msgid "Rescan"
+msgstr "Επανανίχνευση"
+
+#: git-gui.sh:2025
+msgid "Stage To Commit"
+msgstr "Σταδιοποίηση Προς Υποβολή"
+
+#: git-gui.sh:2031
+msgid "Stage Changed Files To Commit"
+msgstr "Σταδιοποίηση Αλλαγμένων Αρχείων Προς Υποβολή"
+
+#: git-gui.sh:2037
+msgid "Unstage From Commit"
+msgstr "Αποσταδιοποίηση Από Υποβολή"
+
+#: git-gui.sh:2042 lib/index.tcl:395
+msgid "Revert Changes"
+msgstr "Αναίρεση Αλλαγών"
+
+#: git-gui.sh:2049 git-gui.sh:2368 git-gui.sh:2467
+msgid "Sign Off"
+msgstr "Αποσύνδεση"
+
+#: git-gui.sh:2053 git-gui.sh:2372
+msgid "Commit@@verb"
+msgstr "Υποβολή@@verb"
+
+#: git-gui.sh:2064
+msgid "Local Merge..."
+msgstr "Τοπική Συγχώνευση..."
+
+#: git-gui.sh:2069
+msgid "Abort Merge..."
+msgstr "Ακύρωση Συγχώνευσης..."
+
+#: git-gui.sh:2081
+msgid "Push..."
+msgstr "Ώθηση..."
+
+#: git-gui.sh:2092 lib/choose_repository.tcl:41
+#, fuzzy
+msgid "Apple"
+msgstr ""
+
+#: git-gui.sh:2095 git-gui.sh:2117 lib/about.tcl:14
+#: lib/choose_repository.tcl:44 lib/choose_repository.tcl:50
+#, tcl-format
+msgid "About %s"
+msgstr "Περί %s"
+
+#: git-gui.sh:2099
+msgid "Preferences..."
+msgstr "Προτιμήσεις..."
+
+#: git-gui.sh:2107 git-gui.sh:2639
+msgid "Options..."
+msgstr "Επιλογές..."
+
+#: git-gui.sh:2113 lib/choose_repository.tcl:47
+msgid "Help"
+msgstr "Βοήθεια"
+
+#: git-gui.sh:2154
+msgid "Online Documentation"
+msgstr "Διαδικτυακή Τεκμηρίωση"
+
+#: git-gui.sh:2238
+#, tcl-format
+msgid "fatal: cannot stat path %s: No such file or directory"
+msgstr "κρίσιμο: δε βρέθηκε η διαδρομή: %s: Δεν υπάρχει το αρχείο ή ο φάκελος"
+
+#: git-gui.sh:2271
+msgid "Current Branch:"
+msgstr "Τρέχων Κλάδος:"
+
+#: git-gui.sh:2292
+msgid "Staged Changes (Will Commit)"
+msgstr "Σταδιοποιημένες Αλλαγές (Θα Υποβληθούν)"
+
+#: git-gui.sh:2312
+msgid "Unstaged Changes"
+msgstr "Μη Σταδιοποιημένες Αλλαγές"
+
+#: git-gui.sh:2362
+msgid "Stage Changed"
+msgstr "Σταδιοποίηση Αλλαγών"
+
+#: git-gui.sh:2378 lib/transport.tcl:93 lib/transport.tcl:182
+msgid "Push"
+msgstr "Ώθηση"
+
+#: git-gui.sh:2408
+msgid "Initial Commit Message:"
+msgstr "Αρχικό Μήνυμα Υποβολής:"
+
+#: git-gui.sh:2409
+msgid "Amended Commit Message:"
+msgstr "Διορθωμένο Μήνυμα Υποβολής:"
+
+#: git-gui.sh:2410
+msgid "Amended Initial Commit Message:"
+msgstr "Διορθωμένο Αρχικό Μήνυμα Υποβολής:"
+
+#: git-gui.sh:2411
+msgid "Amended Merge Commit Message:"
+msgstr "Διορθωμένο Μήνυμα Υποβολής Συγχώνευσης:"
+
+#: git-gui.sh:2412
+msgid "Merge Commit Message:"
+msgstr "Μήνυμα Υποβολής Συγχώνευσης:"
+
+#: git-gui.sh:2413
+msgid "Commit Message:"
+msgstr "Μήνυμα Υποβολής:"
+
+#: git-gui.sh:2459 git-gui.sh:2622 lib/console.tcl:73
+msgid "Copy All"
+msgstr "Αντιγραφή Όλων"
+
+#: git-gui.sh:2483 lib/blame.tcl:107
+msgid "File:"
+msgstr "Αρχείο:"
+
+#: git-gui.sh:2589
+msgid "Apply/Reverse Hunk"
+msgstr "Εφαρμογή/Αντιστροφή Κομματιού"
+
+#: git-gui.sh:2595
+msgid "Show Less Context"
+msgstr "Προβολή Στενότερου Πλαισίου"
+
+#: git-gui.sh:2602
+msgid "Show More Context"
+msgstr "Προβολή Ευρύτερου Πλαισίου"
+
+#: git-gui.sh:2610
+msgid "Refresh"
+msgstr "Ανανέωση"
+
+#: git-gui.sh:2631
+msgid "Decrease Font Size"
+msgstr "Μείωση Μεγέθους Γραμματοσειράς"
+
+#: git-gui.sh:2635
+msgid "Increase Font Size"
+msgstr "Αύξηση Μεγέθους Γραμματοσειράς"
+
+#: git-gui.sh:2646
+msgid "Unstage Hunk From Commit"
+msgstr "Αποσταδιοποίηση Κομματιού Από Υποβολή"
+
+#: git-gui.sh:2648
+msgid "Stage Hunk For Commit"
+msgstr "Σταδιοποίηση Κομματιού Προς Υποβολή"
+
+#: git-gui.sh:2667
+msgid "Initializing..."
+msgstr "Γίνεται αρχικοποίηση..."
+
+#: git-gui.sh:2762
+#, tcl-format
+msgid ""
+"Possible environment issues exist.\n"
+"\n"
+"The following environment variables are probably\n"
+"going to be ignored by any Git subprocess run\n"
+"by %s:\n"
+"\n"
+msgstr ""
+"Υπάρχουν πιθανά θέματα με το περιβάλλον.\n"
+"\n"
+"Οι εξής μεταβλητές περιβάλλοντος μάλλον θα\n"
+"αγνοηθούν από πιθανή εκτέλεση υποδιεργασίας Git\n"
+"από το %s:\n"
+"\n"
+
+#: git-gui.sh:2792
+msgid ""
+"\n"
+"This is due to a known issue with the\n"
+"Tcl binary distributed by Cygwin."
+msgstr ""
+"\n"
+"Αυτό οφείλεται σε ένα γνωστό θέμα με το\n"
+"εκτελέσιμο Tcl που διανέμεται με το Cygwin."
+
+#: git-gui.sh:2797
+#, tcl-format
+msgid ""
+"\n"
+"\n"
+"A good replacement for %s\n"
+"is placing values for the user.name and\n"
+"user.email settings into your personal\n"
+"~/.gitconfig file.\n"
+msgstr ""
+"\n"
+"\n"
+"Ένα καλό υποκατάστατο για το %s\n"
+"είναι η τοποθέτηση τιμών για τις ρυθμίσεις\n"
+"user.name και user.email στο προσωπικό σας\n"
+"αρχείο ~/.gitconfig .\n"
+
+#: lib/about.tcl:26
+msgid "git-gui - a graphical user interface for Git."
+msgstr "git-gui - ένα γραφικό περιβάλλον για το Git."
+
+#: lib/blame.tcl:77
+msgid "File Viewer"
+msgstr "Εφαρμογή Προβολής Αρχείου"
+
+#: lib/blame.tcl:81
+msgid "Commit:"
+msgstr "Υποβολή:"
+
+#: lib/blame.tcl:264
+msgid "Copy Commit"
+msgstr "Αντιγραφή Υποβολής"
+
+#: lib/blame.tcl:384
+#, tcl-format
+msgid "Reading %s..."
+msgstr "Ανάγνωση %s..."
+
+#: lib/blame.tcl:488
+msgid "Loading copy/move tracking annotations..."
+msgstr "Γίνεται φόρτωση σχολίων παρακολούθησης αντιγραφής/μετακίνησης..."
+
+#: lib/blame.tcl:508
+msgid "lines annotated"
+msgstr "γραμμές σχολιασμένες"
+
+#: lib/blame.tcl:689
+msgid "Loading original location annotations..."
+msgstr "Γίνεται φόρτωση σχολίων αρχικής τοποθεσίας..."
+
+#: lib/blame.tcl:692
+msgid "Annotation complete."
+msgstr "Έγινε ολοκλήρωση του σχολιασμού."
+
+#: lib/blame.tcl:746
+msgid "Loading annotation..."
+msgstr "Φόρτωση σχολίου..."
+
+#: lib/blame.tcl:802
+msgid "Author:"
+msgstr "Δημιουργός:"
+
+#: lib/blame.tcl:806
+msgid "Committer:"
+msgstr "Υποβολέας:"
+
+#: lib/blame.tcl:811
+msgid "Original File:"
+msgstr "Αρχικό Αρχείο:"
+
+#: lib/blame.tcl:925
+msgid "Originally By:"
+msgstr "Αρχικά Από:"
+
+#: lib/blame.tcl:931
+msgid "In File:"
+msgstr "Στο Αρχείο:"
+
+#: lib/blame.tcl:936
+msgid "Copied Or Moved Here By:"
+msgstr "Αντιγράφηκε ή Μετακινήθηκε Εδώ Από:"
+
+#: lib/branch_checkout.tcl:14 lib/branch_checkout.tcl:19
+msgid "Checkout Branch"
+msgstr "Εξαγωγή Κλάδου"
+
+#: lib/branch_checkout.tcl:23
+msgid "Checkout"
+msgstr "Εξαγωγή"
+
+#: lib/branch_checkout.tcl:27 lib/branch_create.tcl:35
+#: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:282
+#: lib/checkout_op.tcl:522 lib/choose_font.tcl:43 lib/merge.tcl:171
+#: lib/option.tcl:103 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97
+msgid "Cancel"
+msgstr "Ακύρωση"
+
+#: lib/branch_checkout.tcl:32 lib/browser.tcl:287
+msgid "Revision"
+msgstr "Αναθεώρηση"
+
+#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:242
+msgid "Options"
+msgstr "Επιλογές"
+
+#: lib/branch_checkout.tcl:39 lib/branch_create.tcl:92
+msgid "Fetch Tracking Branch"
+msgstr "Ανάκτηση Κλάδου Παρακολούθησης"
+
+#: lib/branch_checkout.tcl:44
+msgid "Detach From Local Branch"
+msgstr "Αποκόλληση Από Τοπικό Κλάδο"
+
+#: lib/branch_create.tcl:22
+msgid "Create Branch"
+msgstr "Δημιουργία Κλάδου"
+
+#: lib/branch_create.tcl:27
+msgid "Create New Branch"
+msgstr "Δημιουργία Νέου Κλάδου"
+
+#: lib/branch_create.tcl:31 lib/choose_repository.tcl:371
+msgid "Create"
+msgstr "Δημιουργία"
+
+#: lib/branch_create.tcl:40
+msgid "Branch Name"
+msgstr "Όνομα Κλάδου"
+
+#: lib/branch_create.tcl:43
+msgid "Name:"
+msgstr "Όνομα:"
+
+#: lib/branch_create.tcl:58
+msgid "Match Tracking Branch Name"
+msgstr "Συμφωνία Ονόματος Κλάδου Παρακολούθησης"
+
+#: lib/branch_create.tcl:66
+msgid "Starting Revision"
+msgstr "Αρχική Αναθεώρηση"
+
+#: lib/branch_create.tcl:72
+msgid "Update Existing Branch:"
+msgstr "Ενημέρωση Υπάρχοντα Κλάδου:"
+
+#: lib/branch_create.tcl:75
+#, fuzzy
+msgid "No"
+msgstr "Όχι"
+
+#: lib/branch_create.tcl:80
+msgid "Fast Forward Only"
+msgstr "Συγχώνευση Επιτάχυνσης Μόνο"
+
+#: lib/branch_create.tcl:85 lib/checkout_op.tcl:514
+msgid "Reset"
+msgstr "Επαναφορά"
+
+#: lib/branch_create.tcl:97
+msgid "Checkout After Creation"
+msgstr "Εξαγωγή Μετά τη Δημιουργία"
+
+#: lib/branch_create.tcl:131
+msgid "Please select a tracking branch."
+msgstr "Παρακαλώ επιλέξτε έναν κλάδο παρακολούθησης."
+
+#: lib/branch_create.tcl:140
+#, tcl-format
+msgid "Tracking branch %s is not a branch in the remote repository."
+msgstr ""
+"Ο κλάδος παρακολούθησης %s δεν είναι κλάδος που βρίσκεται στο απομακρυσμένο "
+"αποθετήριο."
+
+#: lib/branch_create.tcl:153 lib/branch_rename.tcl:86
+msgid "Please supply a branch name."
+msgstr "Παρακαλώ δώστε ένα όνομα κλάδου."
+
+#: lib/branch_create.tcl:164 lib/branch_rename.tcl:106
+#, tcl-format
+msgid "'%s' is not an acceptable branch name."
+msgstr "'%s' δεν είναι αποδεκτό όνομα κλάδου."
+
+#: lib/branch_delete.tcl:15
+msgid "Delete Branch"
+msgstr "Διαγραφή Κλάδου"
+
+#: lib/branch_delete.tcl:20
+msgid "Delete Local Branch"
+msgstr "Διαγραφή Τοπικού Κλάδου"
+
+#: lib/branch_delete.tcl:37
+msgid "Local Branches"
+msgstr "Τοπικοί Κλάδοι"
+
+#: lib/branch_delete.tcl:52
+msgid "Delete Only If Merged Into"
+msgstr "Διαγραφή Μόνο Εάν Είναι Συγχωνευμένο Με"
+
+#: lib/branch_delete.tcl:54
+msgid "Always (Do not perform merge test.)"
+msgstr "Πάντα (Μη διενεργηθεί δοκιμή συγχώνευσης.)"
+
+#: lib/branch_delete.tcl:103
+#, tcl-format
+msgid "The following branches are not completely merged into %s:"
+msgstr "Οι εξής κλάδοι δεν είναι πλήρως συγχωνευμένοι με το %s:"
+
+#: lib/branch_delete.tcl:115
+msgid ""
+"Recovering deleted branches is difficult. \n"
+"\n"
+" Delete the selected branches?"
+msgstr ""
+"Η ανάκτηση διεγραμμένων κλάδων είναι δύσκολη.\n"
+"\n"
+"Διαγραφή των επιλεγμένων κλάδων;"
+
+#: lib/branch_delete.tcl:141
+#, tcl-format
+msgid ""
+"Failed to delete branches:\n"
+"%s"
+msgstr ""
+"Αποτυχία διαγραφής κλάδων:\n"
+"%s"
+
+#: lib/branch_rename.tcl:14 lib/branch_rename.tcl:22
+msgid "Rename Branch"
+msgstr "Μετονομασία Κλάδου"
+
+#: lib/branch_rename.tcl:26
+msgid "Rename"
+msgstr "Μετονομασία"
+
+#: lib/branch_rename.tcl:36
+msgid "Branch:"
+msgstr "Κλάδος:"
+
+#: lib/branch_rename.tcl:39
+msgid "New Name:"
+msgstr "Νέο Όνομα:"
+
+#: lib/branch_rename.tcl:75
+msgid "Please select a branch to rename."
+msgstr "Παρακαλώ επιλέξτε κλάδο προς μετονομασία:"
+
+#: lib/branch_rename.tcl:96 lib/checkout_op.tcl:179
+#, tcl-format
+msgid "Branch '%s' already exists."
+msgstr "Ο Κλάδος '%s' υπάρχει ήδη."
+
+#: lib/branch_rename.tcl:117
+#, tcl-format
+msgid "Failed to rename '%s'."
+msgstr "Αποτυχία μετονομασίας '%s'."
+
+#: lib/browser.tcl:17
+msgid "Starting..."
+msgstr "Γίνεται Εκκίνηση..."
+
+#: lib/browser.tcl:26
+msgid "File Browser"
+msgstr "Περιηγητής Αρχείων"
+
+#: lib/browser.tcl:126 lib/browser.tcl:143
+#, tcl-format
+msgid "Loading %s..."
+msgstr "Γίνεται φόρτωση %s..."
+
+#: lib/browser.tcl:187
+#, fuzzy
+msgid "[Up To Parent]"
+msgstr "[Πάνω Προς Γονέα]"
+
+#: lib/browser.tcl:267 lib/browser.tcl:273
+msgid "Browse Branch Files"
+msgstr "Περιήγηση Αρχείων Κλάδου"
+
+#: lib/browser.tcl:278 lib/choose_repository.tcl:387
+#: lib/choose_repository.tcl:474 lib/choose_repository.tcl:484
+#: lib/choose_repository.tcl:987
+msgid "Browse"
+msgstr "Περιήγηση"
+
+#: lib/checkout_op.tcl:79
+#, tcl-format
+msgid "Fetching %s from %s"
+msgstr "Ανάκτηση %s από το %s"
+
+#: lib/checkout_op.tcl:127
+#, tcl-format
+msgid "fatal: Cannot resolve %s"
+msgstr "κρίσιμο: Δε μπόρεσε να επιλυθεί το %s"
+
+#: lib/checkout_op.tcl:140 lib/console.tcl:81 lib/database.tcl:31
+msgid "Close"
+msgstr "Κλείσιμο"
+
+#: lib/checkout_op.tcl:169
+#, tcl-format
+msgid "Branch '%s' does not exist."
+msgstr "Ο Κλάδος '%s' δεν υπάρχει."
+
+#: lib/checkout_op.tcl:206
+#, tcl-format
+msgid ""
+"Branch '%s' already exists.\n"
+"\n"
+"It cannot fast-forward to %s.\n"
+"A merge is required."
+msgstr ""
+"Ο Κλάδος '%s' υπάρχει ήδη.\n"
+"\n"
+"Δε γίνεται συγχώνευση επιτάχυνσής του στο %s.\n"
+"Απαιτείται συγχώνευση."
+
+#: lib/checkout_op.tcl:220
+#, tcl-format
+msgid "Merge strategy '%s' not supported."
+msgstr "Η στρατηγική Συγχώνευσης %s δεν υποστηρίζεται."
+
+#: lib/checkout_op.tcl:239
+#, tcl-format
+msgid "Failed to update '%s'."
+msgstr "Αποτυχία ενημέρωσης '%s'."
+
+#: lib/checkout_op.tcl:251
+msgid "Staging area (index) is already locked."
+msgstr "Η περιοχή σταδιοποίησης (το ευρετήριο) είναι ήδη κλειδωμένη."
+
+#: lib/checkout_op.tcl:266
+msgid ""
+"Last scanned state does not match repository state.\n"
+"\n"
+"Another Git program has modified this repository since the last scan.  A "
+"rescan must be performed before the current branch can be changed.\n"
+"\n"
+"The rescan will be automatically started now.\n"
+msgstr ""
+"Η τελευταία κατάσταση που ανιχνεύθηκε δε συμφωνεί με την κατάσταση του "
+"αποθετηρίου.\n"
+"\n"
+"Κάποιο άλλο πρόγραμμα Git τροποποίησε το αποθετήριο από την τελευταία "
+"ανίχνευση. Πρέπει να γίνει επανανίχνευση πριν να αλλαχθεί ο τρέχων κλάδος.\n"
+"\n"
+"Η επανανίχνευση θα ξεκινήσει αυτόματα τώρα.\n"
+
+#: lib/checkout_op.tcl:322
+#, tcl-format
+msgid "Updating working directory to '%s'..."
+msgstr "Ενημέρωση φακέλου εργασίας σε '%s'..."
+
+#: lib/checkout_op.tcl:323
+msgid "files checked out"
+msgstr "αρχεία έχουν εξαχθεί"
+
+#: lib/checkout_op.tcl:353
+#, tcl-format
+msgid "Aborted checkout of '%s' (file level merging is required)."
+msgstr ""
+"Έγινε ακύρωση εξαγωγής του '%s' (απαιτείται συγχώνευση επιπέδου αρχείου)."
+
+#: lib/checkout_op.tcl:354
+msgid "File level merge required."
+msgstr "Απαιτείται συγχώνευση επιπέδου αρχείου."
+
+#: lib/checkout_op.tcl:358
+#, tcl-format
+msgid "Staying on branch '%s'."
+msgstr "Παραμονή στον κλάδο '%s'."
+
+#: lib/checkout_op.tcl:429
+msgid ""
+"You are no longer on a local branch.\n"
+"\n"
+"If you wanted to be on a branch, create one now starting from 'This Detached "
+"Checkout'."
+msgstr ""
+"Δε βρίσκεστε πια σε τοπικό κλάδο.\n"
+"\n"
+"Αν θέλατε να βρίσκεστε σε κλάδο, δημιουργήστε έναν τώρα αρχίζοντας από 'This "
+"Detached Checkout'."
+
+#: lib/checkout_op.tcl:446 lib/checkout_op.tcl:450
+#, tcl-format
+msgid "Checked out '%s'."
+msgstr "Έγινε εξαγωγή του '%s'."
+
+#: lib/checkout_op.tcl:478
+#, tcl-format
+msgid "Resetting '%s' to '%s' will lose the following commits:"
+msgstr "Η επαναφορά '%s' στο '%s' θα χάσει τις εξής υποβολές:"
+
+#: lib/checkout_op.tcl:500
+msgid "Recovering lost commits may not be easy."
+msgstr "Η ανάκτηση χαμένων υποβολών μπορεί να είναι δύσκολη."
+
+#: lib/checkout_op.tcl:505
+#, tcl-format
+msgid "Reset '%s'?"
+msgstr "Επαναφορά '%s';"
+
+#: lib/checkout_op.tcl:510 lib/merge.tcl:163
+msgid "Visualize"
+msgstr "Απεικόνιση"
+
+#: lib/checkout_op.tcl:578
+#, tcl-format
+msgid ""
+"Failed to set current branch.\n"
+"\n"
+"This working directory is only partially switched.  We successfully updated "
+"your files, but failed to update an internal Git file.\n"
+"\n"
+"This should not have occurred.  %s will now close and give up."
+msgstr ""
+"Αποτυχία ορισμού τρέχοντος κλάδου.\n"
+"\n"
+"Αυτός ο φάκελος εργασίας είναι μόνο εν μέρει επιλεγμένος. 'Εγινε επιτυχής "
+"ενημέρωση των αρχείων σας, αλλά απέτυχε η ενημέρωση ενός εσωτερικού αρχείου "
+"του Git.\n"
+"\n"
+"Αυτό δε θα έπρεπε να συμβεί. Το %s θα κλείσει και θα εγκαταλείψει τώρα."
+
+#: lib/choose_font.tcl:39
+msgid "Select"
+msgstr "Επιλογή"
+
+#: lib/choose_font.tcl:53
+msgid "Font Family"
+msgstr "Οικογένεια Γραμματοσειράς"
+
+#: lib/choose_font.tcl:74
+msgid "Font Size"
+msgstr "Μέγεθος Γραμματοσειράς"
+
+#: lib/choose_font.tcl:91
+msgid "Font Example"
+msgstr "Παράδειγμα Γραμματοσειράς"
+
+#: lib/choose_font.tcl:103
+msgid ""
+"This is example text.\n"
+"If you like this text, it can be your font."
+msgstr ""
+"Αυτό είναι ένα παράδειγμα κειμένου.\n"
+"Αν σας αρέσει αυτό το κείμενο, μπορεί να γίνει δικό σας."
+
+#: lib/choose_repository.tcl:28
+msgid "Git Gui"
+msgstr "Γραφικό Περιβάλλον Git"
+
+#: lib/choose_repository.tcl:81 lib/choose_repository.tcl:376
+msgid "Create New Repository"
+msgstr "Δημιουργία Νέου Αποθετηρίου"
+
+#: lib/choose_repository.tcl:87
+msgid "New..."
+msgstr "Νέο..."
+
+#: lib/choose_repository.tcl:94 lib/choose_repository.tcl:460
+msgid "Clone Existing Repository"
+msgstr "Κλωνοποίηση Υπάρχοντος Αποθετηρίου"
+
+#: lib/choose_repository.tcl:100
+msgid "Clone..."
+msgstr "Κλωνοποίηση..."
+
+#: lib/choose_repository.tcl:107 lib/choose_repository.tcl:976
+msgid "Open Existing Repository"
+msgstr "Άνοιγμα Υπάρχοντος Αποθετηρίου"
+
+#: lib/choose_repository.tcl:113
+msgid "Open..."
+msgstr "Άνοιγμα..."
+
+#: lib/choose_repository.tcl:126
+msgid "Recent Repositories"
+msgstr "Πρόσφατα Αποθετήρια"
+
+#: lib/choose_repository.tcl:132
+msgid "Open Recent Repository:"
+msgstr "Άνοιγμα Πρόσφατου Αποθετηρίου:"
+
+#: lib/choose_repository.tcl:296 lib/choose_repository.tcl:303
+#: lib/choose_repository.tcl:310
+#, tcl-format
+msgid "Failed to create repository %s:"
+msgstr "Αποτυχία δημιουργίας αποθετηρίου %s:"
+
+#: lib/choose_repository.tcl:381 lib/choose_repository.tcl:478
+msgid "Directory:"
+msgstr "Φάκελος:"
+
+#: lib/choose_repository.tcl:412 lib/choose_repository.tcl:537
+#: lib/choose_repository.tcl:1011
+msgid "Git Repository"
+msgstr "Αποθετήριο Git"
+
+#: lib/choose_repository.tcl:437
+#, tcl-format
+msgid "Directory %s already exists."
+msgstr "Ο Φάκελος '%s' υπάρχει ήδη."
+
+#: lib/choose_repository.tcl:441
+#, tcl-format
+msgid "File %s already exists."
+msgstr "Το αρχείο %s υπάρχει ήδη."
+
+#: lib/choose_repository.tcl:455
+msgid "Clone"
+msgstr "Κλώνος"
+
+#: lib/choose_repository.tcl:468
+#, fuzzy
+msgid "URL:"
+msgstr ""
+
+#: lib/choose_repository.tcl:489
+msgid "Clone Type:"
+msgstr "Τύπος Κλώνου:"
+
+#: lib/choose_repository.tcl:495
+msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
+msgstr "Τυπικό (Ταχύ, Ημι-Πλεονάζον, Hardlinks)"
+
+#: lib/choose_repository.tcl:501
+msgid "Full Copy (Slower, Redundant Backup)"
+msgstr "Πλήρες Αντίγραφο (Πιο αργό, Πλεονάζον Αντίγραφο Ασφαλείας)"
+
+#: lib/choose_repository.tcl:507
+msgid "Shared (Fastest, Not Recommended, No Backup)"
+msgstr "Κοινή Χρήση (Ταχύτατο, Δε Συνιστάται, Κανένα Αντίγραφο Ασφαλείας)"
+
+#: lib/choose_repository.tcl:543 lib/choose_repository.tcl:590
+#: lib/choose_repository.tcl:736 lib/choose_repository.tcl:806
+#: lib/choose_repository.tcl:1017 lib/choose_repository.tcl:1025
+#, tcl-format
+msgid "Not a Git repository: %s"
+msgstr "Δεν είναι αποθετήριο Git: %s"
+
+#: lib/choose_repository.tcl:579
+msgid "Standard only available for local repository."
+msgstr "\"Τυπικό\" διαθέσιμο μόνο για τοπικό αποθετήριο."
+
+#: lib/choose_repository.tcl:583
+msgid "Shared only available for local repository."
+msgstr "\"Κοινή Χρήση\" διαθέσιμη μόνο για τοπικό αποθετήριο."
+
+#: lib/choose_repository.tcl:604
+#, tcl-format
+msgid "Location %s already exists."
+msgstr "Η Τοποθεσία %s υπάρχει ήδη."
+
+#: lib/choose_repository.tcl:615
+msgid "Failed to configure origin"
+msgstr "Αποτυχία ρύθμισης πηγής"
+
+#: lib/choose_repository.tcl:627
+msgid "Counting objects"
+msgstr "Γίνεται καταμέτρηση αντικειμένων"
+
+#: lib/choose_repository.tcl:628
+#, fuzzy
+msgid "buckets"
+msgstr ""
+
+#: lib/choose_repository.tcl:652
+#, tcl-format
+msgid "Unable to copy objects/info/alternates: %s"
+msgstr "Αδυναμία αντιγραφής αντικειμένων/πληροφοριών/ενναλακτικών: %s"
+
+#: lib/choose_repository.tcl:688
+#, tcl-format
+msgid "Nothing to clone from %s."
+msgstr "Τίποτα προς κλωνοποίηση από το %s."
+
+#: lib/choose_repository.tcl:690 lib/choose_repository.tcl:904
+#: lib/choose_repository.tcl:916
+msgid "The 'master' branch has not been initialized."
+msgstr "Ο κλάδος 'master' δεν έχει αρχικοποιηθεί."
+
+#: lib/choose_repository.tcl:703
+msgid "Hardlinks are unavailable.  Falling back to copying."
+msgstr "Hardlinks μη διαθέσιμα. Μετάπτωση σε αντιγραφή."
+
+#: lib/choose_repository.tcl:715
+#, tcl-format
+msgid "Cloning from %s"
+msgstr "Γίνεται κλωνοποίηση από το %s"
+
+#: lib/choose_repository.tcl:746
+msgid "Copying objects"
+msgstr "Γίνεται αντιγραφή αντικειμένων"
+
+#: lib/choose_repository.tcl:747
+#, fuzzy
+msgid "KiB"
+msgstr ""
+
+#: lib/choose_repository.tcl:771
+#, tcl-format
+msgid "Unable to copy object: %s"
+msgstr "Αδυναμία αντιγραφής αντικειμένου: %s"
+
+#: lib/choose_repository.tcl:781
+msgid "Linking objects"
+msgstr "Γίνεται σύνδεση αντικειμένων"
+
+#: lib/choose_repository.tcl:782
+msgid "objects"
+msgstr "αντικείμενα"
+
+#: lib/choose_repository.tcl:790
+#, tcl-format
+msgid "Unable to hardlink object: %s"
+msgstr "Αδυναμία hardlink αντικειμένου: %s"
+
+#: lib/choose_repository.tcl:845
+msgid "Cannot fetch branches and objects.  See console output for details."
+msgstr ""
+"Δε μπόρεσε να γίνει ανάκτηση κλάδων και αντικειμένων. Δείτε την έξοδο "
+"κονσόλας για λεπτομέρειες."
+
+#: lib/choose_repository.tcl:856
+msgid "Cannot fetch tags.  See console output for details."
+msgstr ""
+"Δε μπόρεσε να γίνει ανάκτηση ετικετών. Δείτε την έξοδο κονσόλας για "
+"λεπτομέρειες."
+
+#: lib/choose_repository.tcl:880
+msgid "Cannot determine HEAD.  See console output for details."
+msgstr ""
+"Δε μπόρεσε να γίνει καθορισμός του HEAD (παρακλαδιού). Δείτε την έξοδο "
+"κονσόλας για "
+"λεπτομέρειες."
+
+#: lib/choose_repository.tcl:889
+#, tcl-format
+msgid "Unable to cleanup %s"
+msgstr "Αδυναμία εκκαθάρισης %s"
+
+#: lib/choose_repository.tcl:895
+msgid "Clone failed."
+msgstr "Αποτυχία κλωνοποίησης."
+
+#: lib/choose_repository.tcl:902
+msgid "No default branch obtained."
+msgstr "Δεν έγινε ανάκτηση προεπιλεγμένου κλάδου."
+
+#: lib/choose_repository.tcl:913
+#, tcl-format
+msgid "Cannot resolve %s as a commit."
+msgstr "Δε μπόρεσε να επιλυθεί το %s ως υποβολή."
+
+#: lib/choose_repository.tcl:925
+msgid "Creating working directory"
+msgstr "Δημιουργία φακέλου εργασίας"
+
+#: lib/choose_repository.tcl:926 lib/index.tcl:65 lib/index.tcl:127
+#: lib/index.tcl:193
+msgid "files"
+msgstr "αρχεία"
+
+#: lib/choose_repository.tcl:955
+msgid "Initial file checkout failed."
+msgstr "Η αρχική εξαγωγή αρχείου απέτυχε."
+
+#: lib/choose_repository.tcl:971
+msgid "Open"
+msgstr "Άνοιγμα"
+
+#: lib/choose_repository.tcl:981
+msgid "Repository:"
+msgstr "Αποθετήριο:"
+
+#: lib/choose_repository.tcl:1031
+#, tcl-format
+msgid "Failed to open repository %s:"
+msgstr "Αποτυχία ανοίγματος αποθετηρίου %s:"
+
+#: lib/choose_rev.tcl:53
+#, fuzzy
+msgid "This Detached Checkout"
+msgstr "Αποσυνδεδεμένη Εξαγωγή"
+
+#: lib/choose_rev.tcl:60
+msgid "Revision Expression:"
+msgstr "Έκφραση Αναθεώρησης:"
+
+#: lib/choose_rev.tcl:74
+msgid "Local Branch"
+msgstr "Τοπικός Κλάδος"
+
+#: lib/choose_rev.tcl:79
+msgid "Tracking Branch"
+msgstr "Κλάδος Παρακολούθησης"
+
+#: lib/choose_rev.tcl:84 lib/choose_rev.tcl:538
+msgid "Tag"
+msgstr "Ετικέτα"
+
+#: lib/choose_rev.tcl:317
+#, tcl-format
+msgid "Invalid revision: %s"
+msgstr "Μη έγκυρη αναθεώρηση: %s"
+
+#: lib/choose_rev.tcl:338
+msgid "No revision selected."
+msgstr "Δεν έχει επιλεγεί αναθεώρηση."
+
+#: lib/choose_rev.tcl:346
+msgid "Revision expression is empty."
+msgstr "Η έκφραση αναθεώρησης είναι κενή."
+
+#: lib/choose_rev.tcl:531
+msgid "Updated"
+msgstr "Ενημερωμένο"
+
+#: lib/choose_rev.tcl:559
+#, fuzzy
+msgid "URL"
+msgstr ""
+
+#: lib/commit.tcl:9
+msgid ""
+"There is nothing to amend.\n"
+"\n"
+"You are about to create the initial commit.  There is no commit before this "
+"to amend.\n"
+msgstr ""
+"Δεν υπάρχει κάτι προς διόρθωση.\n"
+"\n"
+"Πρόκειται να δημιουργήσετε την αρχική υποβολή. Δεν υπάρχει υποβολή πριν από "
+"αυτή για να διορθώσετε.\n"
+
+#: lib/commit.tcl:18
+msgid ""
+"Cannot amend while merging.\n"
+"\n"
+"You are currently in the middle of a merge that has not been fully "
+"completed.  You cannot amend the prior commit unless you first abort the "
+"current merge activity.\n"
+msgstr ""
+"Δε γίνεται διόρθωση καθώς συγχωνεύετε.\n"
+"\n"
+"Βρίσκεστε στο μέσο μιας συγχώνευσης που δεν έχει ολοκληρωθεί. Δε μπορείτε να "
+"διορθώσετε την προηγούμενη υποβολή εκτός εάν ακυρώσετε την τρέχουσα ενέργεια "
+"συγχώνευσης.\n"
+
+#: lib/commit.tcl:49
+msgid "Error loading commit data for amend:"
+msgstr "Σφάλμα φόρτωσης δεδομένων υποβολής προς διόρθωση:"
+
+#: lib/commit.tcl:76
+msgid "Unable to obtain your identity:"
+msgstr "Αδυναμία ανάκτησης της ταυτότητάς σας:"
+
+#: lib/commit.tcl:81
+msgid "Invalid GIT_COMMITTER_IDENT:"
+msgstr "Μη έγκυρο GIT_COMMITTER_IDENT:"
+
+#: lib/commit.tcl:133
+msgid ""
+"Last scanned state does not match repository state.\n"
+"\n"
+"Another Git program has modified this repository since the last scan.  A "
+"rescan must be performed before another commit can be created.\n"
+"\n"
+"The rescan will be automatically started now.\n"
+msgstr ""
+"Η τελευταία κατάσταση που ανιχνεύθηκε δε συμφωνεί με την κατάσταση του "
+"αποθετηρίου.\n"
+"\n"
+"Κάποιο άλλο πρόγραμμα Git τροποποίησε το αποθετήριο από την τελευταία "
+"ανίχνευση. Πρέπει να γίνει επανανίχνευση πριν τη δημιουργία νέας υποβολής.\n"
+"\n"
+"Η επανανίχνευση θα ξεκινήσει αυτόματα τώρα.\n"
+
+#: lib/commit.tcl:154
+#, tcl-format
+msgid ""
+"Unmerged files cannot be committed.\n"
+"\n"
+"File %s has merge conflicts.  You must resolve them and stage the file "
+"before committing.\n"
+msgstr ""
+"Τα μη συγχωνευμένα αρχεία δε μπορούν να υποβληθούν.\n"
+"\n"
+"Το αρχείο %s έχει συγκρούσεις συγχώνευσης. Πρέπει να τις επιλύσετε και να "
+"σταδιοποιήσετε το αρχείο πριν την υποβολή.\n"
+
+#: lib/commit.tcl:162
+#, tcl-format
+msgid ""
+"Unknown file state %s detected.\n"
+"\n"
+"File %s cannot be committed by this program.\n"
+msgstr ""
+"Άγνωστη κατάσταση αρχείου %s ανιχνεύθηκε.\n"
+"\n"
+"Το αρχείο %s δε μπορεί να υποβληθεί από αυτό το πρόγραμμα.\n"
+
+#: lib/commit.tcl:170
+msgid ""
+"No changes to commit.\n"
+"\n"
+"You must stage at least 1 file before you can commit.\n"
+msgstr ""
+"Δεν υπάρχουν αλλαγές προς υποβολή.\n"
+"\n "
+"Πρέπει να σταδιοποιήσετε τουλάχιστον 1 αρχείο πριν να κάνετε υποβολή.\n"
+
+#: lib/commit.tcl:183
+msgid ""
+"Please supply a commit message.\n"
+"\n"
+"A good commit message has the following format:\n"
+"\n"
+"- First line: Describe in one sentence what you did.\n"
+"- Second line: Blank\n"
+"- Remaining lines: Describe why this change is good.\n"
+msgstr ""
+"Παρακαλώ δώστε ένα μήνυμα υποβολής.\n"
+"\n"
+"Ένα σωστό μήνυμα υποβολής έχει την εξής μορφή:\n"
+"\n"
+"- Πρώτη γραμμή: Περιγραφή σε μία πρόταση του τι κάνατε.\n"
+"- Δεύτερη γραμμή: Κενή\n"
+"- Υπόλοιπες γραμμές: Περιγραφή του γιατί αυτή η αλλαγή είναι σωστή.\n"
+
+#: lib/commit.tcl:207
+#, tcl-format
+msgid "warning: Tcl does not support encoding '%s'."
+msgstr "προειδοποίηση: H Tcl δεν υποστηρίζει την κωδικοποίηση '%s'."
+
+#: lib/commit.tcl:221
+msgid "Calling pre-commit hook..."
+msgstr "Γίνεται κλήση του pre-commit hook..."
+
+#: lib/commit.tcl:236
+msgid "Commit declined by pre-commit hook."
+msgstr "Η υποβολή απορρίφθηκε από το pre-commit hook."
+
+#: lib/commit.tcl:259
+msgid "Calling commit-msg hook..."
+msgstr "Γίνεται κλήση του commit-msg hook..."
+
+#: lib/commit.tcl:274
+msgid "Commit declined by commit-msg hook."
+msgstr "Η υποβολή απορρίφθηκε από το commit-msg hook."
+
+#: lib/commit.tcl:287
+msgid "Committing changes..."
+msgstr "Γίνεται υποβολή των αλλαγών..."
+
+#: lib/commit.tcl:303
+msgid "write-tree failed:"
+msgstr "το write-tree απέτυχε:"
+
+#: lib/commit.tcl:304 lib/commit.tcl:348 lib/commit.tcl:368
+msgid "Commit failed."
+msgstr "Η υποβολή απέτυχε."
+
+#: lib/commit.tcl:321
+#, tcl-format
+msgid "Commit %s appears to be corrupt"
+msgstr "Η υποβολή %s δείχνει κατεστραμμένη"
+
+#: lib/commit.tcl:326
+msgid ""
+"No changes to commit.\n"
+"\n"
+"No files were modified by this commit and it was not a merge commit.\n"
+"\n"
+"A rescan will be automatically started now.\n"
+msgstr ""
+"Δεν υπάρχουν αλλαγές προς υποβολή.\n"
+"\n"
+"Δεν τροποποιήθηκαν αρχεία από αυτή την υποβολή και δεν ήταν υποβολή "
+"συγχώνευσης.\n"
+"\n"
+"Θα ξεκινήσει αυτόματα επανανίχνευση τώρα.\n"
+
+#: lib/commit.tcl:333
+msgid "No changes to commit."
+msgstr "Δεν υπάρχουν αλλαγές προς υποβολή."
+
+#: lib/commit.tcl:347
+msgid "commit-tree failed:"
+msgstr "το commit-tree απέτυχε:"
+
+#: lib/commit.tcl:367
+msgid "update-ref failed:"
+msgstr "το update-ref απέτυχε:"
+
+#: lib/commit.tcl:454
+#, tcl-format
+msgid "Created commit %s: %s"
+msgstr "Δημιουργήθηκε υποβολή %s: %s"
+
+#: lib/console.tcl:59
+msgid "Working... please wait..."
+msgstr "Γίνεται εργασία... Παρακαλώ περιμένετε..."
+
+#: lib/console.tcl:186
+msgid "Success"
+msgstr "Επιτυχία"
+
+#: lib/console.tcl:200
+msgid "Error: Command Failed"
+msgstr "Σφάλμα: Η Εντολή Απέτυχε"
+
+#: lib/database.tcl:43
+msgid "Number of loose objects"
+msgstr "Αριθμός ελεύθερων αντικειμένων"
+
+#: lib/database.tcl:44
+msgid "Disk space used by loose objects"
+msgstr "Χώρος κατειλλημένος από ελεύθερα αντικείμενα"
+
+#: lib/database.tcl:45
+msgid "Number of packed objects"
+msgstr "Αριθμός πακεταρισμένων αντικειμένων"
+
+#: lib/database.tcl:46
+msgid "Number of packs"
+msgstr "Αριθμός πακέτων"
+
+#: lib/database.tcl:47
+msgid "Disk space used by packed objects"
+msgstr "Χώρος κατειλλημένος από πακεταρισμένα αντικείμενα"
+
+#: lib/database.tcl:48
+msgid "Packed objects waiting for pruning"
+msgstr "Πακεταρισμένα αντικείμενα έτοιμα για κλάδεμα"
+
+#: lib/database.tcl:49
+msgid "Garbage files"
+msgstr "Άχρηστα αρχεία"
+
+#: lib/database.tcl:72
+msgid "Compressing the object database"
+msgstr "Γίνεται συμπίεση της βάσης δεδομένων αντικειμένων"
+
+#: lib/database.tcl:83
+msgid "Verifying the object database with fsck-objects"
+msgstr ""
+"Γίνεται επαλήθευση της βάσης δεδομένων αντικειμένων με αντικείμενα fsck"
+
+#: lib/database.tcl:108
+#, tcl-format
+msgid ""
+"This repository currently has approximately %i loose objects.\n"
+"\n"
+"To maintain optimal performance it is strongly recommended that you compress "
+"the database when more than %i loose objects exist.\n"
+"\n"
+"Compress the database now?"
+msgstr ""
+"Αυτό το αποθετήριο έχει αυτή τη στιγμή περίπου %i ελεύθερα αντικείμενα.\n"
+"\n"
+"Για τη διατήρηση βέλτιστων επιδόσεων συνιστάται να συμπιέσετε τη βάση "
+"δεδομένων όταν υπάρχουν περισσότερα από %i ελεύθερα αντικείμενα.\n"
+"\n"
+"Συμπίεση της βάσης δεδομένων τώρα;"
+
+#: lib/date.tcl:25
+#, tcl-format
+msgid "Invalid date from Git: %s"
+msgstr "Μη έγκυρη ημερομηνία από το Git: %s"
+
+#: lib/diff.tcl:42
+#, tcl-format
+msgid ""
+"No differences detected.\n"
+"\n"
+"%s has no changes.\n"
+"\n"
+"The modification date of this file was updated by another application, but "
+"the content within the file was not changed.\n"
+"\n"
+"A rescan will be automatically started to find other files which may have "
+"the same state."
+msgstr ""
+"Δεν ανιχνεύθηκαν διαφορές.\n"
+"\n"
+"Το %s δεν έχει αλλαγές."
+"\n"
+"Η ημερομηνία τροποποίησης αυτού του αρχείου ενημερώθηκε από άλλη εφαρμογή, "
+"αλλά το περιεχόμενο του αρχείου δεν άλλαξε.\n"
+"\n"
+"Θα ξεκινήσει αυτόματα επανανίχνευση για να βρεθούν άλλα αρχεία που μπορεί να "
+"βρίσκονται σε ίδια κατάσταση."
+
+#: lib/diff.tcl:81
+#, tcl-format
+msgid "Loading diff of %s..."
+msgstr "Γίνεται φόρτωση διαφοράς του %s..."
+
+#: lib/diff.tcl:114 lib/diff.tcl:184
+#, tcl-format
+msgid "Unable to display %s"
+msgstr "Δεν είναι δυνατή η προβολή του %s"
+
+#: lib/diff.tcl:115
+msgid "Error loading file:"
+msgstr "Σφάλμα φόρτωσης αρχείου:"
+
+#: lib/diff.tcl:122
+msgid "Git Repository (subproject)"
+msgstr "Αποθετήριο Git (θυγατρικό έργο)"
+
+#: lib/diff.tcl:134
+msgid "* Binary file (not showing content)."
+msgstr "* Δυαδικό αρχείο (μη εμφάνιση περιεχομένου)."
+
+#: lib/diff.tcl:185
+msgid "Error loading diff:"
+msgstr "Σφάλμα φόρτωσης διαφοράς:"
+
+#: lib/diff.tcl:303
+msgid "Failed to unstage selected hunk."
+msgstr "Αποτυχία αποσταδιοποίησης επιλεγμένου κομματιού."
+
+#: lib/diff.tcl:310
+msgid "Failed to stage selected hunk."
+msgstr "Αποτυχία σταδιοποίησης επιλεγμένου κομματιού."
+
+#: lib/error.tcl:20 lib/error.tcl:114
+msgid "error"
+msgstr "σφάλμα"
+
+#: lib/error.tcl:36
+msgid "warning"
+msgstr "προειδοποίηση"
+
+#: lib/error.tcl:94
+msgid "You must correct the above errors before committing."
+msgstr "Πρέπει να διορθώσετε τα παραπάνω λάθη πριν την υποβολή."
+
+#: lib/index.tcl:6
+msgid "Unable to unlock the index."
+msgstr "Αδυναμία ξεκλειδώματος του ευρετηρίου."
+
+#: lib/index.tcl:15
+msgid "Index Error"
+msgstr "Σφάλμα Ευρετηρίου"
+
+#: lib/index.tcl:21
+msgid ""
+"Updating the Git index failed.  A rescan will be automatically started to "
+"resynchronize git-gui."
+msgstr ""
+"Η ενημέρωση του ευρετηρίου Git απέτυχε. Θα ξεκινήσει αυτόματα επανανίχνευση "
+"για επανασυγχρονισμό του git-gui."
+
+#: lib/index.tcl:27
+msgid "Continue"
+msgstr "Συνέχεια"
+
+#: lib/index.tcl:31
+msgid "Unlock Index"
+msgstr "Ξεκλείδωμα Ευρετηρίου"
+
+#: lib/index.tcl:282
+#, tcl-format
+msgid "Unstaging %s from commit"
+msgstr "Αποσταδιοποίηση %s από υποβολή"
+
+#: lib/index.tcl:313
+msgid "Ready to commit."
+msgstr "Έτοιμο προς υποβολή."
+
+#: lib/index.tcl:326
+#, tcl-format
+msgid "Adding %s"
+msgstr "Προσθήκη %s"
+
+#: lib/index.tcl:381
+#, tcl-format
+msgid "Revert changes in file %s?"
+msgstr "Αναίρεση αλλαγών στο αρχείο %s;"
+
+#: lib/index.tcl:383
+#, tcl-format
+msgid "Revert changes in these %i files?"
+msgstr "Αναίρεση αλλαγών σε αυτά τα %i αρχεία;"
+
+#: lib/index.tcl:391
+msgid "Any unstaged changes will be permanently lost by the revert."
+msgstr ""
+"Όλες οι μη σταδιοποιημένες αλλαγές θα χαθούν οριστικά από την αναίρεση."
+
+#: lib/index.tcl:394
+msgid "Do Nothing"
+msgstr "Καμία Ενέργεια"
+
+#: lib/merge.tcl:13
+msgid ""
+"Cannot merge while amending.\n"
+"\n"
+"You must finish amending this commit before starting any type of merge.\n"
+msgstr ""
+"Δε γίνεται συγχώνευση καθώς διορθώνετε.\n"
+"\n"
+"Πρέπει να τελειώσετε τη διόρθωση αυτής της υποβολής πριν να ξεκινήσετε "
+"οποιασδήποτε μορφής συγχώνευση.\n"
+
+#: lib/merge.tcl:27
+msgid ""
+"Last scanned state does not match repository state.\n"
+"\n"
+"Another Git program has modified this repository since the last scan.  A "
+"rescan must be performed before a merge can be performed.\n"
+"\n"
+"The rescan will be automatically started now.\n"
+msgstr ""
+"Η τελευταία κατάσταση που ανιχνεύθηκε δε συμφωνεί με την κατάσταση του "
+"αποθετηρίου.\n"
+"\n"
+"Κάποιο άλλο πρόγραμμα Git τροποποίησε το αποθετήριο από την τελευταία "
+"ανίχνευση. Πρέπει να γίνει επανανίχνευση πριν τη διενέργεια συγχώνευσης.\n"
+"\n"
+"Η επανανίχνευση θα ξεκινήσει αυτόματα τώρα.\n"
+
+#: lib/merge.tcl:44
+#, tcl-format
+msgid ""
+"You are in the middle of a conflicted merge.\n"
+"\n"
+"File %s has merge conflicts.\n"
+"\n"
+"You must resolve them, stage the file, and commit to complete the current "
+"merge.  Only then can you begin another merge.\n"
+msgstr ""
+"Βρίσκεστε στο μέσο μιας συγκρουόμενης συγχώνευσης.\n"
+"\n"
+"Το αρχείο %s έχει συγκρούσεις συγχώνευσης.\n"
+"\n"
+"Πρέπει να τις επιλύσετε, να σταδιοποιήσετε το αρχείο, και να κάνετε υποβολή "
+"για να ολοκληρώσετε την τρέχουσα συγχώνευση. Μόνο τότε μπορείτε να "
+"ξεκινήσετε άλλη συγχώνευση.\n"
+
+#: lib/merge.tcl:54
+#, tcl-format
+msgid ""
+"You are in the middle of a change.\n"
+"\n"
+"File %s is modified.\n"
+"\n"
+"You should complete the current commit before starting a merge.  Doing so "
+"will help you abort a failed merge, should the need arise.\n"
+msgstr ""
+"Βρίσκεστε στο μέσο μιας αλλαγής.\n"
+"\n"
+"Το αρχείο %s έχει τροποποιηθεί.\n"
+"\n"
+"Πρέπει να ολοκληρώσετε την τρέχουσα συγχώνευση πριν να ξεκινήσετε συγχώνευση."
+" Αυτό βοηθά στην ακύρωση αποτυχημένης συγχώνευσης, εάν χρειαστεί.\n"
+
+#: lib/merge.tcl:106
+#, tcl-format
+msgid "%s of %s"
+msgstr "%s από %s"
+
+#: lib/merge.tcl:119
+#, tcl-format
+msgid "Merging %s and %s..."
+msgstr "Γίνεται συγχώνευση του %s με το %s..."
+
+#: lib/merge.tcl:130
+msgid "Merge completed successfully."
+msgstr "Η συγχώνευση ολοκληρώθηκε επιτυχώς."
+
+#: lib/merge.tcl:132
+msgid "Merge failed.  Conflict resolution is required."
+msgstr "Η συγχώνευση απέτυχε. Απαιτείται επίλυση συγκρούσεων."
+
+#: lib/merge.tcl:157
+#, tcl-format
+msgid "Merge Into %s"
+msgstr "Συγχώνευση με %s"
+
+#: lib/merge.tcl:176
+msgid "Revision To Merge"
+msgstr "Αναθεώρηση Προς Συγχώνευση"
+
+#: lib/merge.tcl:211
+msgid ""
+"Cannot abort while amending.\n"
+"\n"
+"You must finish amending this commit.\n"
+msgstr ""
+"Δε γίνεται ακύρωση καθώς διορθώνετε.\n"
+"\n"
+"Πρέπει να τελειώσετε τη διόρθωση αυτής της υποβολής.\n"
+
+#: lib/merge.tcl:221
+msgid ""
+"Abort merge?\n"
+"\n"
+"Aborting the current merge will cause *ALL* uncommitted changes to be lost.\n"
+"\n"
+"Continue with aborting the current merge?"
+msgstr ""
+"Ακύρωση συγχώνευσης;\n"
+"\n"
+"Η ακύρωση της τρέχουσας συγχώνευσης θα προκαλέσει απώλεια *ΟΛΩΝ* των μη "
+"υποβεβλημένων αλλαγών.\n"
+"\n"
+"Να προχωρήσει η ακύρωση της τρέχουσας συγχώνευσης;"
+
+#: lib/merge.tcl:227
+msgid ""
+"Reset changes?\n"
+"\n"
+"Resetting the changes will cause *ALL* uncommitted changes to be lost.\n"
+"\n"
+"Continue with resetting the current changes?"
+msgstr ""
+"Επαναφορά αλλαγών;\n"
+"\n"
+"Η επαναφορά των αλλαγών θα προκαλέσει απώλεια *ΟΛΩΝ* των μη υποβεβλημένων "
+"αλλαγών.\n"
+"\n"
+"Να συνεχίσει η επαναφορά των τρέχουσων αλλαγών;"
+
+#: lib/merge.tcl:238
+msgid "Aborting"
+msgstr "Γίνεται ακύρωση"
+
+#: lib/merge.tcl:238
+msgid "files reset"
+msgstr "αρχεία που επαναφέρθηκαν"
+
+#: lib/merge.tcl:265
+msgid "Abort failed."
+msgstr "Η ακύρωση απέτυχε."
+
+#: lib/merge.tcl:267
+msgid "Abort completed.  Ready."
+msgstr "Η ακύρωση απέτυχε. Έτοιμο."
+
+#: lib/option.tcl:95
+msgid "Restore Defaults"
+msgstr "Επαναφορά Προεπιλογών"
+
+#: lib/option.tcl:99
+msgid "Save"
+msgstr "Αποθήκευση"
+
+#: lib/option.tcl:109
+#, tcl-format
+msgid "%s Repository"
+msgstr "%s Αποθετήριο"
+
+#: lib/option.tcl:110
+msgid "Global (All Repositories)"
+msgstr "Ολικό (Όλα τα Αποθετήρια)"
+
+#: lib/option.tcl:116
+msgid "User Name"
+msgstr "Όνομα Χρήστη"
+
+#: lib/option.tcl:117
+msgid "Email Address"
+msgstr "Διεύθυνση Email"
+
+#: lib/option.tcl:119
+msgid "Summarize Merge Commits"
+msgstr "Περίληψη Υποβολών Συγχώνευσης"
+
+#: lib/option.tcl:120
+msgid "Merge Verbosity"
+msgstr "Λεπτομέρεια Συγχώνευσης"
+
+#: lib/option.tcl:121
+msgid "Show Diffstat After Merge"
+msgstr "Προβολή Στατιστικών Διαφοράς Μετά από Συγχώνευση"
+
+#: lib/option.tcl:123
+msgid "Trust File Modification Timestamps"
+msgstr "Εμπιστοσύνη Ημερομηνιών Μετατροπής Αρχείων"
+
+#: lib/option.tcl:124
+msgid "Prune Tracking Branches During Fetch"
+msgstr "Κλάδεμα Κλάδων Παρακολούθησης Κατά Την Ανάκτηση"
+
+#: lib/option.tcl:125
+msgid "Match Tracking Branches"
+msgstr "Συμφωνία Κλάδων Παρακολούθησης"
+
+#: lib/option.tcl:126
+msgid "Number of Diff Context Lines"
+msgstr "Αριθμός Γραμμών Εννοιολογικού Πλαισίου Διαφοράς"
+
+#: lib/option.tcl:127
+msgid "Commit Message Text Width"
+msgstr "Πλάτος Κειμένου Μηνύματος Υποβολής"
+
+#: lib/option.tcl:128
+msgid "New Branch Name Template"
+msgstr "Νέο Πρότυπο Ονόματος Κλάδου"
+
+#: lib/option.tcl:192
+msgid "Spelling Dictionary:"
+msgstr "Λεξικό Ορθογραφίας:"
+
+#: lib/option.tcl:216
+msgid "Change Font"
+msgstr "Αλλαγή Γραμματοσειράς"
+
+#: lib/option.tcl:220
+#, tcl-format
+msgid "Choose %s"
+msgstr "Επιλογή %s"
+
+#: lib/option.tcl:226
+#, fuzzy
+msgid "pt."
+msgstr ""
+
+#: lib/option.tcl:240
+msgid "Preferences"
+msgstr "Προτιμήσεις"
+
+#: lib/option.tcl:275
+msgid "Failed to completely save options:"
+msgstr "Αποτυχία πλήρους αποθήκευσης επιλογών:"
+
+#: lib/remote_branch_delete.tcl:29 lib/remote_branch_delete.tcl:34
+msgid "Delete Remote Branch"
+msgstr "Διαγραφή Απομακρυσμένου Κλάδου"
+
+#: lib/remote_branch_delete.tcl:47
+msgid "From Repository"
+msgstr "Από Αποθετήριο"
+
+#: lib/remote_branch_delete.tcl:50 lib/transport.tcl:123
+msgid "Remote:"
+msgstr "Απομακρυσμένο:"
+
+#: lib/remote_branch_delete.tcl:66 lib/transport.tcl:138
+#, fuzzy
+msgid "Arbitrary URL:"
+msgstr "Αυθαίρετο URL:"
+
+#: lib/remote_branch_delete.tcl:84
+msgid "Branches"
+msgstr "Κλάδοι"
+
+#: lib/remote_branch_delete.tcl:109
+msgid "Delete Only If"
+msgstr "Διαγραφή Μόνο Εάν"
+
+#: lib/remote_branch_delete.tcl:111
+msgid "Merged Into:"
+msgstr "Συγχωνευμένο Με:"
+
+#: lib/remote_branch_delete.tcl:119
+msgid "Always (Do not perform merge checks)"
+msgstr "Πάντα (Μη διενεργηθούν έλεγχοι συγχώνευσης)"
+
+#: lib/remote_branch_delete.tcl:152
+msgid "A branch is required for 'Merged Into'."
+msgstr "Απαιτείται ένας κλάδος για 'Συγχωνευμένο Με'."
+
+#: lib/remote_branch_delete.tcl:184
+#, tcl-format
+msgid ""
+"The following branches are not completely merged into %s:\n"
+"\n"
+" - %s"
+msgstr ""
+"Οι εξής κλάδοι δεν είναι πλήρως συγχωνευμένοι με το %s:\n"
+"\n"
+" - %s"
+
+#: lib/remote_branch_delete.tcl:189
+#, tcl-format
+msgid ""
+"One or more of the merge tests failed because you have not fetched the "
+"necessary commits.  Try fetching from %s first."
+msgstr ""
+"Μία ή περισσότερες από τις δοκιμές συγχώνευσης απέτυχαν επειδή δεν έχετε "
+"φέρει τις αναγκαίες υποβολές. Δοκιμάστε ανάκτηση από το %s πρώτα."
+
+#: lib/remote_branch_delete.tcl:207
+msgid "Please select one or more branches to delete."
+msgstr "Παρακαλώ επιλέξτε έναν ή περισσότερους κλάδους προς διαγραφή."
+
+#: lib/remote_branch_delete.tcl:216
+msgid ""
+"Recovering deleted branches is difficult.\n"
+"\n"
+"Delete the selected branches?"
+msgstr ""
+"Η ανάκτηση διεγραμμένων κλάδων είναι δύσκολη.\n"
+"\n"
+"Διαγραφή των επιλεγμένων κλάδων;"
+
+#: lib/remote_branch_delete.tcl:226
+#, tcl-format
+msgid "Deleting branches from %s"
+msgstr "Γίνεται διαγραφή κλάδων από %s"
+
+#: lib/remote_branch_delete.tcl:286
+msgid "No repository selected."
+msgstr "Δεν έχει επιλεγεί αποθετήριο."
+
+#: lib/remote_branch_delete.tcl:291
+#, tcl-format
+msgid "Scanning %s..."
+msgstr "Ανίχνευση %s..."
+
+#: lib/remote.tcl:165
+msgid "Prune from"
+msgstr "Κλάδεμα από"
+
+#: lib/remote.tcl:170
+msgid "Fetch from"
+msgstr "Ανάκτηση από"
+
+#: lib/remote.tcl:213
+msgid "Push to"
+msgstr "Ώθηση σε"
+
+#: lib/shortcut.tcl:20 lib/shortcut.tcl:61
+msgid "Cannot write shortcut:"
+msgstr "Δε μπόρεσε να αποθηκευτεί η συντόμευση:"
+
+#: lib/shortcut.tcl:136
+msgid "Cannot write icon:"
+msgstr "Δε μπόρεσε να αποθηκευτεί το εικονίδιο:"
+
+#: lib/spellcheck.tcl:57
+msgid "Unsupported spell checker"
+msgstr "Mη υποστηριζόμενος ελεγκτής ορθογραφίας"
+
+#: lib/spellcheck.tcl:65
+msgid "Spell checking is unavailable"
+msgstr "Έλεγχος ορθογραφίας μη διαθέσιμος"
+
+#: lib/spellcheck.tcl:68
+msgid "Invalid spell checking configuration"
+msgstr "Μη έγκυρη ρύθμιση ελέγχου ορθογραφίας"
+
+#: lib/spellcheck.tcl:70
+#, tcl-format
+msgid "Reverting dictionary to %s."
+msgstr "Γίνεται επαναφορά του λεξικού σε %s."
+
+#: lib/spellcheck.tcl:73
+msgid "Spell checker silently failed on startup"
+msgstr "Ο ελεγκτής ορθογραφίας απέτυχε σιωπηλά κατά την εκκίνηση"
+
+#: lib/spellcheck.tcl:80
+msgid "Unrecognized spell checker"
+msgstr "Μη αναγνωρίσιμος ελεγκτής ορθογραφίας"
+
+#: lib/spellcheck.tcl:180
+msgid "No Suggestions"
+msgstr "Καμία Πρόταση"
+
+#: lib/spellcheck.tcl:381
+msgid "Unexpected EOF from spell checker"
+msgstr "Μη αναμενόμενο τέλος αρχείου από τον ελεγκτή ορθογραφίας"
+
+#: lib/spellcheck.tcl:385
+msgid "Spell Checker Failed"
+msgstr "Αποτυχία Ελεγκτή Ορθογραφίας"
+
+#: lib/status_bar.tcl:83
+#, tcl-format
+msgid "%s ... %*i of %*i %s (%3i%%)"
+msgstr "%s ... %*i από %*i %s (%3i%%)"
+
+#: lib/transport.tcl:6
+#, tcl-format
+msgid "fetch %s"
+msgstr "ανάκτηση %s"
+
+#: lib/transport.tcl:7
+#, tcl-format
+msgid "Fetching new changes from %s"
+msgstr "Ανάκτηση νέων αλλαγών από το %s"
+
+#: lib/transport.tcl:18
+#, tcl-format
+msgid "remote prune %s"
+msgstr "απομακρυσμένο κλάδεμα %s"
+
+#: lib/transport.tcl:19
+#, tcl-format
+msgid "Pruning tracking branches deleted from %s"
+msgstr "Γίνεται κλάδεμα κλάδων παρακολούθησης που διεγράφησαν από το %s"
+
+#: lib/transport.tcl:25 lib/transport.tcl:71
+#, tcl-format
+msgid "push %s"
+msgstr "ώθηση %s"
+
+#: lib/transport.tcl:26
+#, tcl-format
+msgid "Pushing changes to %s"
+msgstr "Γίνεται ώθηση αλλαγών στο %s"
+
+#: lib/transport.tcl:72
+#, tcl-format
+msgid "Pushing %s %s to %s"
+msgstr "Γίνεται ώθηση %s %s στο %s"
+
+#: lib/transport.tcl:89
+msgid "Push Branches"
+msgstr "Ώθηση Κλάδων"
+
+#: lib/transport.tcl:103
+msgid "Source Branches"
+msgstr "Πηγαίοι Κλάδοι"
+
+#: lib/transport.tcl:120
+msgid "Destination Repository"
+msgstr "Αποθετήριο Προορισμού"
+
+#: lib/transport.tcl:158
+msgid "Transfer Options"
+msgstr "Επιλογές Μεταφοράς"
+
+#: lib/transport.tcl:160
+msgid "Force overwrite existing branch (may discard changes)"
+msgstr ""
+"Εξαναγκασμός επεγγραφής υπάρχοντος κλάδου (μπορεί να απορρίψει αλλαγές)"
+
+#: lib/transport.tcl:164
+msgid "Use thin pack (for slow network connections)"
+msgstr "Χρήση ισχνού πακέτου (για αργές συνδέσεις δικτύου)"
+
+#: lib/transport.tcl:168
+msgid "Include tags"
+msgstr "Συμπερίληψη ετικετών"
+
+
diff --git a/git-gui/po/fr.po b/git-gui/po/fr.po
index a944ace..8170696 100644
--- a/git-gui/po/fr.po
+++ b/git-gui/po/fr.po
@@ -9,8 +9,8 @@
 msgstr ""
 "Project-Id-Version: fr\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-11-16 13:56-0800\n"
-"PO-Revision-Date: 2008-11-20 10:20+0100\n"
+"POT-Creation-Date: 2010-01-26 15:47-0800\n"
+"PO-Revision-Date: 2010-02-02 12:59+0100\n"
 "Last-Translator: Christian Couder <chriscool@tuxfamily.org>\n"
 "Language-Team: French\n"
 "MIME-Version: 1.0\n"
@@ -19,33 +19,33 @@
 "X-Generator: KBabel 1.11.4\n"
 "Plural-Forms:  nplurals=2; plural=(n > 1);\n"
 
-#: git-gui.sh:41 git-gui.sh:737 git-gui.sh:751 git-gui.sh:764 git-gui.sh:847
-#: git-gui.sh:866
+#: git-gui.sh:41 git-gui.sh:793 git-gui.sh:807 git-gui.sh:820 git-gui.sh:903
+#: git-gui.sh:922
 msgid "git-gui: fatal error"
 msgstr "git-gui: erreur fatale"
 
-#: git-gui.sh:689
+#: git-gui.sh:743
 #, tcl-format
 msgid "Invalid font specified in %s:"
 msgstr "Police invalide spécifiée dans %s :"
 
-#: git-gui.sh:723
+#: git-gui.sh:779
 msgid "Main Font"
 msgstr "Police principale"
 
-#: git-gui.sh:724
+#: git-gui.sh:780
 msgid "Diff/Console Font"
 msgstr "Police diff/console"
 
-#: git-gui.sh:738
+#: git-gui.sh:794
 msgid "Cannot find git in PATH."
 msgstr "Impossible de trouver git dans PATH."
 
-#: git-gui.sh:765
+#: git-gui.sh:821
 msgid "Cannot parse Git version string:"
 msgstr "Impossible de parser la version de Git :"
 
-#: git-gui.sh:783
+#: git-gui.sh:839
 #, tcl-format
 msgid ""
 "Git version cannot be determined.\n"
@@ -64,446 +64,475 @@
 "\n"
 "Peut-on considérer que '%s' est en version 1.5.0 ?\n"
 
-#: git-gui.sh:1062
+#: git-gui.sh:1128
 msgid "Git directory not found:"
 msgstr "Impossible de trouver le répertoire git :"
 
-#: git-gui.sh:1069
+#: git-gui.sh:1146
 msgid "Cannot move to top of working directory:"
 msgstr "Impossible d'aller à la racine du répertoire de travail :"
 
-#: git-gui.sh:1076
-msgid "Cannot use funny .git directory:"
-msgstr "Impossible d'utiliser le répertoire .git:"
+#: git-gui.sh:1154
+msgid "Cannot use bare repository:"
+msgstr "Impossible d'utiliser un dépôt nu (bare) :"
 
-#: git-gui.sh:1081
+#: git-gui.sh:1162
 msgid "No working directory"
 msgstr "Aucun répertoire de travail"
 
-#: git-gui.sh:1247 lib/checkout_op.tcl:305
+#: git-gui.sh:1334 lib/checkout_op.tcl:306
 msgid "Refreshing file status..."
 msgstr "Rafraîchissement du statut des fichiers..."
 
-#: git-gui.sh:1303
+#: git-gui.sh:1390
 msgid "Scanning for modified files ..."
 msgstr "Recherche de fichiers modifiés..."
 
-#: git-gui.sh:1367
+#: git-gui.sh:1454
 msgid "Calling prepare-commit-msg hook..."
 msgstr "Lancement de l'action de préparation du message de commit..."
 
-#: git-gui.sh:1384
+#: git-gui.sh:1471
 msgid "Commit declined by prepare-commit-msg hook."
 msgstr "Commit refusé par l'action de préparation du message de commit."
 
-#: git-gui.sh:1542 lib/browser.tcl:246
+#: git-gui.sh:1629 lib/browser.tcl:246
 msgid "Ready."
 msgstr "Prêt."
 
-#: git-gui.sh:1819
+#: git-gui.sh:1787
+#, tcl-format
+msgid "Displaying only %s of %s files."
+msgstr "Affiche seulement %s fichiers sur %s."
+
+#: git-gui.sh:1913
 msgid "Unmodified"
 msgstr "Non modifié"
 
-#: git-gui.sh:1821
+#: git-gui.sh:1915
 msgid "Modified, not staged"
 msgstr "Modifié, pas indexé"
 
-#: git-gui.sh:1822 git-gui.sh:1830
+#: git-gui.sh:1916 git-gui.sh:1924
 msgid "Staged for commit"
 msgstr "Indexé"
 
-#: git-gui.sh:1823 git-gui.sh:1831
+#: git-gui.sh:1917 git-gui.sh:1925
 msgid "Portions staged for commit"
 msgstr "Portions indexées"
 
-#: git-gui.sh:1824 git-gui.sh:1832
+#: git-gui.sh:1918 git-gui.sh:1926
 msgid "Staged for commit, missing"
 msgstr "Indexés, manquant"
 
-#: git-gui.sh:1826
+#: git-gui.sh:1920
 msgid "File type changed, not staged"
 msgstr "Le type de fichier a changé, non indexé"
 
-#: git-gui.sh:1827
+#: git-gui.sh:1921
 msgid "File type changed, staged"
 msgstr "Le type de fichier a changé, indexé"
 
-#: git-gui.sh:1829
+#: git-gui.sh:1923
 msgid "Untracked, not staged"
 msgstr "Non versionné, non indexé"
 
-#: git-gui.sh:1834
+#: git-gui.sh:1928
 msgid "Missing"
 msgstr "Manquant"
 
-#: git-gui.sh:1835
+#: git-gui.sh:1929
 msgid "Staged for removal"
 msgstr "Indexé pour suppression"
 
-#: git-gui.sh:1836
+#: git-gui.sh:1930
 msgid "Staged for removal, still present"
 msgstr "Indexé pour suppression, toujours présent"
 
-#: git-gui.sh:1838 git-gui.sh:1839 git-gui.sh:1840 git-gui.sh:1841
-#: git-gui.sh:1842 git-gui.sh:1843
+#: git-gui.sh:1932 git-gui.sh:1933 git-gui.sh:1934 git-gui.sh:1935
+#: git-gui.sh:1936 git-gui.sh:1937
 msgid "Requires merge resolution"
 msgstr "Nécessite la résolution d'une fusion"
 
-#: git-gui.sh:1878
+#: git-gui.sh:1972
 msgid "Starting gitk... please wait..."
 msgstr "Lancement de gitk... un instant..."
 
-#: git-gui.sh:1887
+#: git-gui.sh:1984
 msgid "Couldn't find gitk in PATH"
 msgstr "Impossible de trouver gitk dans PATH."
 
-#: git-gui.sh:2280 lib/choose_repository.tcl:36
+#: git-gui.sh:2043
+msgid "Couldn't find git gui in PATH"
+msgstr "Impossible de trouver git gui dans PATH"
+
+#: git-gui.sh:2455 lib/choose_repository.tcl:36
 msgid "Repository"
 msgstr "Dépôt"
 
-#: git-gui.sh:2281
+#: git-gui.sh:2456
 msgid "Edit"
 msgstr "Édition"
 
-#: git-gui.sh:2283 lib/choose_rev.tcl:561
+#: git-gui.sh:2458 lib/choose_rev.tcl:561
 msgid "Branch"
 msgstr "Branche"
 
-#: git-gui.sh:2286 lib/choose_rev.tcl:548
+#: git-gui.sh:2461 lib/choose_rev.tcl:548
 msgid "Commit@@noun"
 msgstr "Commit"
 
-#: git-gui.sh:2289 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
+#: git-gui.sh:2464 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
 msgid "Merge"
 msgstr "Fusionner"
 
-#: git-gui.sh:2290 lib/choose_rev.tcl:557
+#: git-gui.sh:2465 lib/choose_rev.tcl:557
 msgid "Remote"
 msgstr "Dépôt distant"
 
-#: git-gui.sh:2293
+#: git-gui.sh:2468
 msgid "Tools"
 msgstr "Outils"
 
-#: git-gui.sh:2302
+#: git-gui.sh:2477
 msgid "Explore Working Copy"
 msgstr "Explorer la copie de travail"
 
-#: git-gui.sh:2307
+#: git-gui.sh:2483
 msgid "Browse Current Branch's Files"
 msgstr "Naviguer dans la branche courante"
 
-#: git-gui.sh:2311
+#: git-gui.sh:2487
 msgid "Browse Branch Files..."
 msgstr "Naviguer dans la branche..."
 
-#: git-gui.sh:2316
+#: git-gui.sh:2492
 msgid "Visualize Current Branch's History"
 msgstr "Visualiser l'historique de la branche courante"
 
-#: git-gui.sh:2320
+#: git-gui.sh:2496
 msgid "Visualize All Branch History"
 msgstr "Voir l'historique de toutes les branches"
 
-#: git-gui.sh:2327
+#: git-gui.sh:2503
 #, tcl-format
 msgid "Browse %s's Files"
 msgstr "Parcourir l'arborescence de %s"
 
-#: git-gui.sh:2329
+#: git-gui.sh:2505
 #, tcl-format
 msgid "Visualize %s's History"
 msgstr "Voir l'historique de la branche : %s"
 
-#: git-gui.sh:2334 lib/database.tcl:27 lib/database.tcl:67
+#: git-gui.sh:2510 lib/database.tcl:27 lib/database.tcl:67
 msgid "Database Statistics"
 msgstr "Statistiques du dépôt"
 
-#: git-gui.sh:2337 lib/database.tcl:34
+#: git-gui.sh:2513 lib/database.tcl:34
 msgid "Compress Database"
 msgstr "Comprimer le dépôt"
 
-#: git-gui.sh:2340
+#: git-gui.sh:2516
 msgid "Verify Database"
 msgstr "Vérifier le dépôt"
 
-#: git-gui.sh:2347 git-gui.sh:2351 git-gui.sh:2355 lib/shortcut.tcl:7
-#: lib/shortcut.tcl:39 lib/shortcut.tcl:71
+#: git-gui.sh:2523 git-gui.sh:2527 git-gui.sh:2531 lib/shortcut.tcl:8
+#: lib/shortcut.tcl:40 lib/shortcut.tcl:72
 msgid "Create Desktop Icon"
 msgstr "Créer une icône sur le bureau"
 
-#: git-gui.sh:2363 lib/choose_repository.tcl:183 lib/choose_repository.tcl:191
+#: git-gui.sh:2539 lib/choose_repository.tcl:183 lib/choose_repository.tcl:191
 msgid "Quit"
 msgstr "Quitter"
 
-#: git-gui.sh:2371
+#: git-gui.sh:2547
 msgid "Undo"
 msgstr "Défaire"
 
-#: git-gui.sh:2374
+#: git-gui.sh:2550
 msgid "Redo"
 msgstr "Refaire"
 
-#: git-gui.sh:2378 git-gui.sh:2923
+#: git-gui.sh:2554 git-gui.sh:3109
 msgid "Cut"
 msgstr "Couper"
 
-#: git-gui.sh:2381 git-gui.sh:2926 git-gui.sh:3000 git-gui.sh:3082
+#: git-gui.sh:2557 git-gui.sh:3112 git-gui.sh:3186 git-gui.sh:3259
 #: lib/console.tcl:69
 msgid "Copy"
 msgstr "Copier"
 
-#: git-gui.sh:2384 git-gui.sh:2929
+#: git-gui.sh:2560 git-gui.sh:3115
 msgid "Paste"
 msgstr "Coller"
 
-#: git-gui.sh:2387 git-gui.sh:2932 lib/branch_delete.tcl:26
+#: git-gui.sh:2563 git-gui.sh:3118 lib/branch_delete.tcl:26
 #: lib/remote_branch_delete.tcl:38
 msgid "Delete"
 msgstr "Supprimer"
 
-#: git-gui.sh:2391 git-gui.sh:2936 git-gui.sh:3086 lib/console.tcl:71
+#: git-gui.sh:2567 git-gui.sh:3122 git-gui.sh:3263 lib/console.tcl:71
 msgid "Select All"
 msgstr "Tout sélectionner"
 
-#: git-gui.sh:2400
+#: git-gui.sh:2576
 msgid "Create..."
 msgstr "Créer..."
 
-#: git-gui.sh:2406
+#: git-gui.sh:2582
 msgid "Checkout..."
 msgstr "Charger (checkout)..."
 
-#: git-gui.sh:2412
+#: git-gui.sh:2588
 msgid "Rename..."
 msgstr "Renommer..."
 
-#: git-gui.sh:2417
+#: git-gui.sh:2593
 msgid "Delete..."
 msgstr "Supprimer..."
 
-#: git-gui.sh:2422
+#: git-gui.sh:2598
 msgid "Reset..."
 msgstr "Réinitialiser..."
 
-#: git-gui.sh:2432
+#: git-gui.sh:2608
 msgid "Done"
 msgstr "Effectué"
 
-#: git-gui.sh:2434
+#: git-gui.sh:2610
 msgid "Commit@@verb"
 msgstr "Commiter@@verb"
 
-#: git-gui.sh:2443 git-gui.sh:2864
+#: git-gui.sh:2619 git-gui.sh:3050
 msgid "New Commit"
 msgstr "Nouveau commit"
 
-#: git-gui.sh:2451 git-gui.sh:2871
+#: git-gui.sh:2627 git-gui.sh:3057
 msgid "Amend Last Commit"
 msgstr "Corriger dernier commit"
 
-#: git-gui.sh:2461 git-gui.sh:2825 lib/remote_branch_delete.tcl:99
+#: git-gui.sh:2637 git-gui.sh:3011 lib/remote_branch_delete.tcl:99
 msgid "Rescan"
 msgstr "Recharger modifs."
 
-#: git-gui.sh:2467
+#: git-gui.sh:2643
 msgid "Stage To Commit"
 msgstr "Indexer"
 
-#: git-gui.sh:2473
+#: git-gui.sh:2649
 msgid "Stage Changed Files To Commit"
 msgstr "Indexer toutes modifications"
 
-#: git-gui.sh:2479
+#: git-gui.sh:2655
 msgid "Unstage From Commit"
 msgstr "Désindexer"
 
-#: git-gui.sh:2484 lib/index.tcl:410
+#: git-gui.sh:2661 lib/index.tcl:412
 msgid "Revert Changes"
 msgstr "Annuler les modifications"
 
-#: git-gui.sh:2491 git-gui.sh:3069
+#: git-gui.sh:2669 git-gui.sh:3310 git-gui.sh:3341
 msgid "Show Less Context"
 msgstr "Montrer moins de contexte"
 
-#: git-gui.sh:2495 git-gui.sh:3073
+#: git-gui.sh:2673 git-gui.sh:3314 git-gui.sh:3345
 msgid "Show More Context"
 msgstr "Montrer plus de contexte"
 
-#: git-gui.sh:2502 git-gui.sh:2838 git-gui.sh:2947
+#: git-gui.sh:2680 git-gui.sh:3024 git-gui.sh:3133
 msgid "Sign Off"
 msgstr "Signer"
 
-#: git-gui.sh:2518
+#: git-gui.sh:2696
 msgid "Local Merge..."
 msgstr "Fusion locale..."
 
-#: git-gui.sh:2523
+#: git-gui.sh:2701
 msgid "Abort Merge..."
 msgstr "Abandonner fusion..."
 
-#: git-gui.sh:2535 git-gui.sh:2575
+#: git-gui.sh:2713 git-gui.sh:2741
 msgid "Add..."
 msgstr "Ajouter..."
 
-#: git-gui.sh:2539
+#: git-gui.sh:2717
 msgid "Push..."
 msgstr "Pousser..."
 
-#: git-gui.sh:2543
+#: git-gui.sh:2721
 msgid "Delete Branch..."
 msgstr "Supprimer branche..."
 
-#: git-gui.sh:2553 git-gui.sh:2589 lib/about.tcl:14
+#: git-gui.sh:2731 git-gui.sh:3292
+msgid "Options..."
+msgstr "Options..."
+
+#: git-gui.sh:2742
+msgid "Remove..."
+msgstr "Supprimer..."
+
+#: git-gui.sh:2751 lib/choose_repository.tcl:50
+msgid "Help"
+msgstr "Aide"
+
+#: git-gui.sh:2755 git-gui.sh:2759 lib/about.tcl:14
 #: lib/choose_repository.tcl:44 lib/choose_repository.tcl:53
 #, tcl-format
 msgid "About %s"
 msgstr "À propos de %s"
 
-#: git-gui.sh:2557
-msgid "Preferences..."
-msgstr "Préférences..."
-
-#: git-gui.sh:2565 git-gui.sh:3115
-msgid "Options..."
-msgstr "Options..."
-
-#: git-gui.sh:2576
-msgid "Remove..."
-msgstr "Supprimer..."
-
-#: git-gui.sh:2585 lib/choose_repository.tcl:50
-msgid "Help"
-msgstr "Aide"
-
-#: git-gui.sh:2611
+#: git-gui.sh:2783
 msgid "Online Documentation"
 msgstr "Documentation en ligne"
 
-#: git-gui.sh:2614 lib/choose_repository.tcl:47 lib/choose_repository.tcl:56
+#: git-gui.sh:2786 lib/choose_repository.tcl:47 lib/choose_repository.tcl:56
 msgid "Show SSH Key"
 msgstr "Montrer la clé SSH"
 
-#: git-gui.sh:2707
+#: git-gui.sh:2893
 #, tcl-format
 msgid "fatal: cannot stat path %s: No such file or directory"
 msgstr ""
 "erreur fatale : pas d'infos sur le chemin %s : Fichier ou répertoire "
 "inexistant"
 
-#: git-gui.sh:2740
+#: git-gui.sh:2926
 msgid "Current Branch:"
 msgstr "Branche courante :"
 
-#: git-gui.sh:2761
+#: git-gui.sh:2947
 msgid "Staged Changes (Will Commit)"
 msgstr "Modifs. indexées (pour commit)"
 
-#: git-gui.sh:2781
+#: git-gui.sh:2967
 msgid "Unstaged Changes"
 msgstr "Modifs. non indexées"
 
-#: git-gui.sh:2831
+#: git-gui.sh:3017
 msgid "Stage Changed"
 msgstr "Indexer modifs."
 
-#: git-gui.sh:2850 lib/transport.tcl:93 lib/transport.tcl:182
+#: git-gui.sh:3036 lib/transport.tcl:104 lib/transport.tcl:193
 msgid "Push"
 msgstr "Pousser"
 
-#: git-gui.sh:2885
+#: git-gui.sh:3071
 msgid "Initial Commit Message:"
 msgstr "Message de commit initial :"
 
-#: git-gui.sh:2886
+#: git-gui.sh:3072
 msgid "Amended Commit Message:"
 msgstr "Message de commit corrigé :"
 
-#: git-gui.sh:2887
+#: git-gui.sh:3073
 msgid "Amended Initial Commit Message:"
 msgstr "Message de commit initial corrigé :"
 
-#: git-gui.sh:2888
+#: git-gui.sh:3074
 msgid "Amended Merge Commit Message:"
 msgstr "Message de commit de fusion corrigé :"
 
-#: git-gui.sh:2889
+#: git-gui.sh:3075
 msgid "Merge Commit Message:"
 msgstr "Message de commit de fusion :"
 
-#: git-gui.sh:2890
+#: git-gui.sh:3076
 msgid "Commit Message:"
 msgstr "Message de commit :"
 
-#: git-gui.sh:2939 git-gui.sh:3090 lib/console.tcl:73
+#: git-gui.sh:3125 git-gui.sh:3267 lib/console.tcl:73
 msgid "Copy All"
 msgstr "Copier tout"
 
-#: git-gui.sh:2963 lib/blame.tcl:104
+#: git-gui.sh:3149 lib/blame.tcl:104
 msgid "File:"
 msgstr "Fichier :"
 
-#: git-gui.sh:3078
+#: git-gui.sh:3255
 msgid "Refresh"
 msgstr "Rafraîchir"
 
-#: git-gui.sh:3099
+#: git-gui.sh:3276
 msgid "Decrease Font Size"
 msgstr "Diminuer la police"
 
-#: git-gui.sh:3103
+#: git-gui.sh:3280
 msgid "Increase Font Size"
 msgstr "Agrandir la police"
 
-#: git-gui.sh:3111 lib/blame.tcl:281
+#: git-gui.sh:3288 lib/blame.tcl:281
 msgid "Encoding"
 msgstr "Codage des caractères"
 
-#: git-gui.sh:3122
+#: git-gui.sh:3299
 msgid "Apply/Reverse Hunk"
 msgstr "Appliquer/Inverser section"
 
-#: git-gui.sh:3127
+#: git-gui.sh:3304
 msgid "Apply/Reverse Line"
 msgstr "Appliquer/Inverser la ligne"
 
-#: git-gui.sh:3137
+#: git-gui.sh:3323
 msgid "Run Merge Tool"
 msgstr "Lancer l'outil de fusion"
 
-#: git-gui.sh:3142
+#: git-gui.sh:3328
 msgid "Use Remote Version"
 msgstr "Utiliser la version distante"
 
-#: git-gui.sh:3146
+#: git-gui.sh:3332
 msgid "Use Local Version"
 msgstr "Utiliser la version locale"
 
-#: git-gui.sh:3150
+#: git-gui.sh:3336
 msgid "Revert To Base"
 msgstr "Revenir à la version de base"
 
-#: git-gui.sh:3169
+#: git-gui.sh:3354
+msgid "Visualize These Changes In The Submodule"
+msgstr "Voir les changments dans le sous-module"
+
+#: git-gui.sh:3358
+msgid "Visualize Current Branch History In The Submodule"
+msgstr "Voir l'historique de la branche courante du sous-module"
+
+#: git-gui.sh:3362
+msgid "Visualize All Branch History In The Submodule"
+msgstr "Voir l'historique de toutes les branches du sous-module"
+
+#: git-gui.sh:3367
+msgid "Start git gui In The Submodule"
+msgstr "Démarrer git gui dans le sous-module"
+
+#: git-gui.sh:3389
 msgid "Unstage Hunk From Commit"
 msgstr "Désindexer la section"
 
-#: git-gui.sh:3170
+#: git-gui.sh:3391
+msgid "Unstage Lines From Commit"
+msgstr "Désindexer la ligne du commit"
+
+#: git-gui.sh:3393
 msgid "Unstage Line From Commit"
 msgstr "Désindexer la ligne"
 
-#: git-gui.sh:3172
+#: git-gui.sh:3396
 msgid "Stage Hunk For Commit"
 msgstr "Indexer la section"
 
-#: git-gui.sh:3173
+#: git-gui.sh:3398
+msgid "Stage Lines For Commit"
+msgstr "Indexer les lignes"
+
+#: git-gui.sh:3400
 msgid "Stage Line For Commit"
 msgstr "Indexer la ligne"
 
-#: git-gui.sh:3196
+#: git-gui.sh:3424
 msgid "Initializing..."
 msgstr "Initialisation..."
 
-#: git-gui.sh:3301
+#: git-gui.sh:3541
 #, tcl-format
 msgid ""
 "Possible environment issues exist.\n"
@@ -520,7 +549,7 @@
 "sous-processus de Git lancés par %s\n"
 "\n"
 
-#: git-gui.sh:3331
+#: git-gui.sh:3570
 msgid ""
 "\n"
 "This is due to a known issue with the\n"
@@ -530,7 +559,7 @@
 "Ceci est dû à un problème connu avec\n"
 "le binaire Tcl distribué par Cygwin."
 
-#: git-gui.sh:3336
+#: git-gui.sh:3575
 #, tcl-format
 msgid ""
 "\n"
@@ -616,43 +645,43 @@
 msgid "Loading annotation..."
 msgstr "Chargement des annotations..."
 
-#: lib/blame.tcl:964
+#: lib/blame.tcl:963
 msgid "Author:"
 msgstr "Auteur :"
 
-#: lib/blame.tcl:968
+#: lib/blame.tcl:967
 msgid "Committer:"
 msgstr "Commiteur :"
 
-#: lib/blame.tcl:973
+#: lib/blame.tcl:972
 msgid "Original File:"
 msgstr "Fichier original :"
 
-#: lib/blame.tcl:1021
+#: lib/blame.tcl:1020
 msgid "Cannot find HEAD commit:"
 msgstr "Impossible de trouver le commit HEAD :"
 
-#: lib/blame.tcl:1076
+#: lib/blame.tcl:1075
 msgid "Cannot find parent commit:"
 msgstr "Impossible de trouver le commit parent :"
 
-#: lib/blame.tcl:1091
+#: lib/blame.tcl:1090
 msgid "Unable to display parent"
 msgstr "Impossible d'afficher le parent"
 
-#: lib/blame.tcl:1092 lib/diff.tcl:297
+#: lib/blame.tcl:1091 lib/diff.tcl:320
 msgid "Error loading diff:"
 msgstr "Erreur lors du chargement des différences :"
 
-#: lib/blame.tcl:1232
+#: lib/blame.tcl:1231
 msgid "Originally By:"
 msgstr "À l'origine par :"
 
-#: lib/blame.tcl:1238
+#: lib/blame.tcl:1237
 msgid "In File:"
 msgstr "Dans le fichier :"
 
-#: lib/blame.tcl:1243
+#: lib/blame.tcl:1242
 msgid "Copied Or Moved Here By:"
 msgstr "Copié ou déplacé ici par :"
 
@@ -666,10 +695,10 @@
 
 #: lib/branch_checkout.tcl:27 lib/branch_create.tcl:35
 #: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:282
-#: lib/checkout_op.tcl:544 lib/choose_font.tcl:43 lib/merge.tcl:172
+#: lib/checkout_op.tcl:579 lib/choose_font.tcl:43 lib/merge.tcl:172
 #: lib/option.tcl:125 lib/remote_add.tcl:32 lib/remote_branch_delete.tcl:42
 #: lib/tools_dlg.tcl:40 lib/tools_dlg.tcl:204 lib/tools_dlg.tcl:352
-#: lib/transport.tcl:97
+#: lib/transport.tcl:108
 msgid "Cancel"
 msgstr "Annuler"
 
@@ -697,7 +726,7 @@
 msgid "Create New Branch"
 msgstr "Créer une nouvelle branche"
 
-#: lib/branch_create.tcl:31 lib/choose_repository.tcl:377
+#: lib/branch_create.tcl:31 lib/choose_repository.tcl:381
 msgid "Create"
 msgstr "Créer"
 
@@ -729,7 +758,7 @@
 msgid "Fast Forward Only"
 msgstr "Mise à jour rectiligne seulement (fast-forward)"
 
-#: lib/branch_create.tcl:85 lib/checkout_op.tcl:536
+#: lib/branch_create.tcl:85 lib/checkout_op.tcl:571
 msgid "Reset"
 msgstr "Réinitialiser"
 
@@ -771,15 +800,25 @@
 msgid "Delete Only If Merged Into"
 msgstr "Supprimer seulement si fusionnée dans :"
 
-#: lib/branch_delete.tcl:54
-msgid "Always (Do not perform merge test.)"
-msgstr "Toujours (Ne pas faire de test de fusion.)"
+#: lib/branch_delete.tcl:54 lib/remote_branch_delete.tcl:119
+msgid "Always (Do not perform merge checks)"
+msgstr "Toujours (ne pas vérifier les fusions)"
 
 #: lib/branch_delete.tcl:103
 #, tcl-format
 msgid "The following branches are not completely merged into %s:"
 msgstr "Les branches suivantes ne sont pas complètement fusionnées dans %s :"
 
+#: lib/branch_delete.tcl:115 lib/remote_branch_delete.tcl:217
+msgid ""
+"Recovering deleted branches is difficult.\n"
+"\n"
+"Delete the selected branches?"
+msgstr ""
+"Il est difficile de récupérer des branches supprimées.\n"
+"\n"
+"Supprimer les branches sélectionnées ?"
+
 #: lib/branch_delete.tcl:141
 #, tcl-format
 msgid ""
@@ -809,7 +848,7 @@
 msgid "Please select a branch to rename."
 msgstr "Merci de sélectionner une branche à renommer."
 
-#: lib/branch_rename.tcl:96 lib/checkout_op.tcl:201
+#: lib/branch_rename.tcl:96 lib/checkout_op.tcl:202
 #, tcl-format
 msgid "Branch '%s' already exists."
 msgstr "La branche '%s' existe déjà."
@@ -840,38 +879,38 @@
 msgid "Browse Branch Files"
 msgstr "Naviguer dans les fichiers de le branche"
 
-#: lib/browser.tcl:278 lib/choose_repository.tcl:394
-#: lib/choose_repository.tcl:480 lib/choose_repository.tcl:491
-#: lib/choose_repository.tcl:995
+#: lib/browser.tcl:278 lib/choose_repository.tcl:398
+#: lib/choose_repository.tcl:486 lib/choose_repository.tcl:497
+#: lib/choose_repository.tcl:1028
 msgid "Browse"
 msgstr "Naviguer"
 
-#: lib/checkout_op.tcl:84
+#: lib/checkout_op.tcl:85
 #, tcl-format
 msgid "Fetching %s from %s"
 msgstr "Récupération de %s à partir de %s"
 
-#: lib/checkout_op.tcl:132
+#: lib/checkout_op.tcl:133
 #, tcl-format
 msgid "fatal: Cannot resolve %s"
 msgstr "erreur fatale : Impossible de résoudre %s"
 
-#: lib/checkout_op.tcl:145 lib/console.tcl:81 lib/database.tcl:31
+#: lib/checkout_op.tcl:146 lib/console.tcl:81 lib/database.tcl:31
 #: lib/sshkey.tcl:53
 msgid "Close"
 msgstr "Fermer"
 
-#: lib/checkout_op.tcl:174
+#: lib/checkout_op.tcl:175
 #, tcl-format
 msgid "Branch '%s' does not exist."
 msgstr "La branche '%s' n'existe pas."
 
-#: lib/checkout_op.tcl:193
+#: lib/checkout_op.tcl:194
 #, tcl-format
 msgid "Failed to configure simplified git-pull for '%s'."
 msgstr "Échec de la configuration simplifiée de git-pull pour '%s'."
 
-#: lib/checkout_op.tcl:228
+#: lib/checkout_op.tcl:229
 #, tcl-format
 msgid ""
 "Branch '%s' already exists.\n"
@@ -884,21 +923,21 @@
 "Impossible de faire une avance rapide (fast forward) vers %s.\n"
 "Une fusion est nécessaire."
 
-#: lib/checkout_op.tcl:242
+#: lib/checkout_op.tcl:243
 #, tcl-format
 msgid "Merge strategy '%s' not supported."
 msgstr "La stratégie de fusion '%s' n'est pas supportée."
 
-#: lib/checkout_op.tcl:261
+#: lib/checkout_op.tcl:262
 #, tcl-format
 msgid "Failed to update '%s'."
 msgstr "La mise à jour de '%s' a échoué."
 
-#: lib/checkout_op.tcl:273
+#: lib/checkout_op.tcl:274
 msgid "Staging area (index) is already locked."
 msgstr "L'index (staging area) est déjà verrouillé."
 
-#: lib/checkout_op.tcl:288
+#: lib/checkout_op.tcl:289
 msgid ""
 "Last scanned state does not match repository state.\n"
 "\n"
@@ -916,30 +955,31 @@
 "\n"
 "Cela va être fait tout de suite automatiquement.\n"
 
-#: lib/checkout_op.tcl:344
+#: lib/checkout_op.tcl:345
 #, tcl-format
 msgid "Updating working directory to '%s'..."
 msgstr "Mise à jour du répertoire courant avec '%s'..."
 
-#: lib/checkout_op.tcl:345
+#: lib/checkout_op.tcl:346
 msgid "files checked out"
 msgstr "fichiers chargés"
 
-#: lib/checkout_op.tcl:375
+#: lib/checkout_op.tcl:376
 #, tcl-format
 msgid "Aborted checkout of '%s' (file level merging is required)."
-msgstr "Chargement de '%s' abandonné (il est nécessaire de fusionner des fichiers)."
+msgstr ""
+"Chargement de '%s' abandonné (il est nécessaire de fusionner des fichiers)."
 
-#: lib/checkout_op.tcl:376
+#: lib/checkout_op.tcl:377
 msgid "File level merge required."
 msgstr "Il est nécessaire de fusionner des fichiers."
 
-#: lib/checkout_op.tcl:380
+#: lib/checkout_op.tcl:381
 #, tcl-format
 msgid "Staying on branch '%s'."
 msgstr "Le répertoire de travail reste sur la branche '%s'."
 
-#: lib/checkout_op.tcl:451
+#: lib/checkout_op.tcl:452
 msgid ""
 "You are no longer on a local branch.\n"
 "\n"
@@ -951,30 +991,30 @@
 "Si vous vouliez être sur une branche, créez-en une maintenant en partant de "
 "'Cet emprunt détaché'."
 
-#: lib/checkout_op.tcl:468 lib/checkout_op.tcl:472
+#: lib/checkout_op.tcl:503 lib/checkout_op.tcl:507
 #, tcl-format
 msgid "Checked out '%s'."
 msgstr "'%s' chargé."
 
-#: lib/checkout_op.tcl:500
+#: lib/checkout_op.tcl:535
 #, tcl-format
 msgid "Resetting '%s' to '%s' will lose the following commits:"
 msgstr "Réinitialiser '%s' à '%s' va faire perdre les commits suivants :"
 
-#: lib/checkout_op.tcl:522
+#: lib/checkout_op.tcl:557
 msgid "Recovering lost commits may not be easy."
 msgstr "Récupérer les commits perdus ne sera peut être pas facile."
 
-#: lib/checkout_op.tcl:527
+#: lib/checkout_op.tcl:562
 #, tcl-format
 msgid "Reset '%s'?"
 msgstr "Réinitialiser '%s' ?"
 
-#: lib/checkout_op.tcl:532 lib/merge.tcl:164 lib/tools_dlg.tcl:343
+#: lib/checkout_op.tcl:567 lib/merge.tcl:164 lib/tools_dlg.tcl:343
 msgid "Visualize"
 msgstr "Visualiser"
 
-#: lib/checkout_op.tcl:600
+#: lib/checkout_op.tcl:635
 #, tcl-format
 msgid ""
 "Failed to set current branch.\n"
@@ -1020,7 +1060,7 @@
 msgid "Git Gui"
 msgstr "Git Gui"
 
-#: lib/choose_repository.tcl:87 lib/choose_repository.tcl:382
+#: lib/choose_repository.tcl:87 lib/choose_repository.tcl:386
 msgid "Create New Repository"
 msgstr "Créer nouveau dépôt"
 
@@ -1028,7 +1068,7 @@
 msgid "New..."
 msgstr "Nouveau..."
 
-#: lib/choose_repository.tcl:100 lib/choose_repository.tcl:465
+#: lib/choose_repository.tcl:100 lib/choose_repository.tcl:471
 msgid "Clone Existing Repository"
 msgstr "Cloner un dépôt existant"
 
@@ -1036,7 +1076,7 @@
 msgid "Clone..."
 msgstr "Cloner..."
 
-#: lib/choose_repository.tcl:113 lib/choose_repository.tcl:983
+#: lib/choose_repository.tcl:113 lib/choose_repository.tcl:1016
 msgid "Open Existing Repository"
 msgstr "Ouvrir un dépôt existant"
 
@@ -1052,197 +1092,199 @@
 msgid "Open Recent Repository:"
 msgstr "Ouvrir un dépôt récent :"
 
-#: lib/choose_repository.tcl:302 lib/choose_repository.tcl:309
-#: lib/choose_repository.tcl:316
+#: lib/choose_repository.tcl:306 lib/choose_repository.tcl:313
+#: lib/choose_repository.tcl:320
 #, tcl-format
 msgid "Failed to create repository %s:"
 msgstr "La création du dépôt %s a échoué :"
 
-#: lib/choose_repository.tcl:387
+#: lib/choose_repository.tcl:391
 msgid "Directory:"
 msgstr "Répertoire :"
 
-#: lib/choose_repository.tcl:417 lib/choose_repository.tcl:544
-#: lib/choose_repository.tcl:1017
+#: lib/choose_repository.tcl:423 lib/choose_repository.tcl:550
+#: lib/choose_repository.tcl:1052
 msgid "Git Repository"
 msgstr "Dépôt Git"
 
-#: lib/choose_repository.tcl:442
+#: lib/choose_repository.tcl:448
 #, tcl-format
 msgid "Directory %s already exists."
 msgstr "Le répertoire %s existe déjà."
 
-#: lib/choose_repository.tcl:446
+#: lib/choose_repository.tcl:452
 #, tcl-format
 msgid "File %s already exists."
 msgstr "Le fichier %s existe déjà."
 
-#: lib/choose_repository.tcl:460
+#: lib/choose_repository.tcl:466
 msgid "Clone"
 msgstr "Cloner"
 
-#: lib/choose_repository.tcl:473
+#: lib/choose_repository.tcl:479
 msgid "Source Location:"
 msgstr "Emplacement source :"
 
-#: lib/choose_repository.tcl:484
+#: lib/choose_repository.tcl:490
 msgid "Target Directory:"
 msgstr "Répertoire cible :"
 
-#: lib/choose_repository.tcl:496
+#: lib/choose_repository.tcl:502
 msgid "Clone Type:"
 msgstr "Type de clonage :"
 
-#: lib/choose_repository.tcl:502
+#: lib/choose_repository.tcl:508
 msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
 msgstr "Standard (rapide, semi-redondant, liens durs)"
 
-#: lib/choose_repository.tcl:508
+#: lib/choose_repository.tcl:514
 msgid "Full Copy (Slower, Redundant Backup)"
 msgstr "Copy complète (plus lent, sauvegarde redondante)"
 
-#: lib/choose_repository.tcl:514
+#: lib/choose_repository.tcl:520
 msgid "Shared (Fastest, Not Recommended, No Backup)"
 msgstr "Partagé (le plus rapide, non recommandé, pas de sauvegarde)"
 
-#: lib/choose_repository.tcl:550 lib/choose_repository.tcl:597
-#: lib/choose_repository.tcl:743 lib/choose_repository.tcl:813
-#: lib/choose_repository.tcl:1023 lib/choose_repository.tcl:1031
+#: lib/choose_repository.tcl:556 lib/choose_repository.tcl:603
+#: lib/choose_repository.tcl:749 lib/choose_repository.tcl:819
+#: lib/choose_repository.tcl:1058 lib/choose_repository.tcl:1066
 #, tcl-format
 msgid "Not a Git repository: %s"
 msgstr "'%s' n'est pas un dépôt Git."
 
-#: lib/choose_repository.tcl:586
+#: lib/choose_repository.tcl:592
 msgid "Standard only available for local repository."
 msgstr "Standard n'est disponible que pour un dépôt local."
 
-#: lib/choose_repository.tcl:590
+#: lib/choose_repository.tcl:596
 msgid "Shared only available for local repository."
 msgstr "Partagé n'est disponible que pour un dépôt local."
 
-#: lib/choose_repository.tcl:611
+#: lib/choose_repository.tcl:617
 #, tcl-format
 msgid "Location %s already exists."
 msgstr "L'emplacement %s existe déjà."
 
-#: lib/choose_repository.tcl:622
+#: lib/choose_repository.tcl:628
 msgid "Failed to configure origin"
 msgstr "La configuration de l'origine a échoué."
 
-#: lib/choose_repository.tcl:634
+#: lib/choose_repository.tcl:640
 msgid "Counting objects"
 msgstr "Décompte des objets"
 
-#: lib/choose_repository.tcl:635
+#: lib/choose_repository.tcl:641
 msgid "buckets"
 msgstr "paniers"
 
-#: lib/choose_repository.tcl:659
+#: lib/choose_repository.tcl:665
 #, tcl-format
 msgid "Unable to copy objects/info/alternates: %s"
 msgstr "Impossible de copier 'objects/info/alternates' : %s"
 
-#: lib/choose_repository.tcl:695
+#: lib/choose_repository.tcl:701
 #, tcl-format
 msgid "Nothing to clone from %s."
 msgstr "Il n'y a rien à cloner depuis %s."
 
-#: lib/choose_repository.tcl:697 lib/choose_repository.tcl:911
-#: lib/choose_repository.tcl:923
+#: lib/choose_repository.tcl:703 lib/choose_repository.tcl:917
+#: lib/choose_repository.tcl:929
 msgid "The 'master' branch has not been initialized."
 msgstr "La branche 'master' n'a pas été initialisée."
 
-#: lib/choose_repository.tcl:710
+#: lib/choose_repository.tcl:716
 msgid "Hardlinks are unavailable.  Falling back to copying."
-msgstr "Les liens durs ne sont pas supportés. Une copie sera effectuée à la place."
+msgstr ""
+"Les liens durs ne sont pas supportés. Une copie sera effectuée à la place."
 
-#: lib/choose_repository.tcl:722
+#: lib/choose_repository.tcl:728
 #, tcl-format
 msgid "Cloning from %s"
 msgstr "Clonage depuis %s"
 
-#: lib/choose_repository.tcl:753
+#: lib/choose_repository.tcl:759
 msgid "Copying objects"
 msgstr "Copie des objets"
 
-#: lib/choose_repository.tcl:754
+#: lib/choose_repository.tcl:760
 msgid "KiB"
 msgstr "KiB"
 
-#: lib/choose_repository.tcl:778
+#: lib/choose_repository.tcl:784
 #, tcl-format
 msgid "Unable to copy object: %s"
 msgstr "Impossible de copier l'objet : %s"
 
-#: lib/choose_repository.tcl:788
+#: lib/choose_repository.tcl:794
 msgid "Linking objects"
 msgstr "Liaison des objets"
 
-#: lib/choose_repository.tcl:789
+#: lib/choose_repository.tcl:795
 msgid "objects"
 msgstr "objets"
 
-#: lib/choose_repository.tcl:797
+#: lib/choose_repository.tcl:803
 #, tcl-format
 msgid "Unable to hardlink object: %s"
 msgstr "Impossible créer un lien dur pour l'objet : %s"
 
-#: lib/choose_repository.tcl:852
+#: lib/choose_repository.tcl:858
 msgid "Cannot fetch branches and objects.  See console output for details."
 msgstr ""
 "Impossible de récupérer les branches et objets. Voir la sortie console pour "
 "plus de détails."
 
-#: lib/choose_repository.tcl:863
+#: lib/choose_repository.tcl:869
 msgid "Cannot fetch tags.  See console output for details."
 msgstr ""
 "Impossible de récupérer les marques (tags). Voir la sortie console pour plus "
 "de détails."
 
-#: lib/choose_repository.tcl:887
+#: lib/choose_repository.tcl:893
 msgid "Cannot determine HEAD.  See console output for details."
-msgstr "Impossible de déterminer HEAD. Voir la sortie console pour plus de détails."
+msgstr ""
+"Impossible de déterminer HEAD. Voir la sortie console pour plus de détails."
 
-#: lib/choose_repository.tcl:896
+#: lib/choose_repository.tcl:902
 #, tcl-format
 msgid "Unable to cleanup %s"
 msgstr "Impossible de nettoyer %s"
 
-#: lib/choose_repository.tcl:902
+#: lib/choose_repository.tcl:908
 msgid "Clone failed."
 msgstr "Le clonage a échoué."
 
-#: lib/choose_repository.tcl:909
+#: lib/choose_repository.tcl:915
 msgid "No default branch obtained."
 msgstr "Aucune branche par défaut n'a été obtenue."
 
-#: lib/choose_repository.tcl:920
+#: lib/choose_repository.tcl:926
 #, tcl-format
 msgid "Cannot resolve %s as a commit."
 msgstr "Impossible de résoudre %s comme commit."
 
-#: lib/choose_repository.tcl:932
+#: lib/choose_repository.tcl:938
 msgid "Creating working directory"
 msgstr "Création du répertoire de travail"
 
-#: lib/choose_repository.tcl:933 lib/index.tcl:65 lib/index.tcl:128
-#: lib/index.tcl:196
+#: lib/choose_repository.tcl:939 lib/index.tcl:67 lib/index.tcl:130
+#: lib/index.tcl:198
 msgid "files"
 msgstr "fichiers"
 
-#: lib/choose_repository.tcl:962
+#: lib/choose_repository.tcl:968
 msgid "Initial file checkout failed."
 msgstr "Le chargement initial du fichier a échoué."
 
-#: lib/choose_repository.tcl:978
+#: lib/choose_repository.tcl:1011
 msgid "Open"
 msgstr "Ouvrir"
 
-#: lib/choose_repository.tcl:988
+#: lib/choose_repository.tcl:1021
 msgid "Repository:"
 msgstr "Dépôt :"
 
-#: lib/choose_repository.tcl:1037
+#: lib/choose_repository.tcl:1072
 #, tcl-format
 msgid "Failed to open repository %s:"
 msgstr "Impossible d'ouvrir le dépôt %s :"
@@ -1314,19 +1356,24 @@
 "terminée. Vous ne pouvez pas corriger le commit précédent sauf si vous "
 "abandonnez la fusion courante.\n"
 
-#: lib/commit.tcl:49
+#: lib/commit.tcl:48
 msgid "Error loading commit data for amend:"
 msgstr "Erreur lors du chargement des données de commit pour correction :"
 
-#: lib/commit.tcl:76
+#: lib/commit.tcl:75
 msgid "Unable to obtain your identity:"
 msgstr "Impossible d'obtenir votre identité :"
 
-#: lib/commit.tcl:81
+#: lib/commit.tcl:80
 msgid "Invalid GIT_COMMITTER_IDENT:"
 msgstr "GIT_COMMITTER_IDENT invalide :"
 
-#: lib/commit.tcl:133
+#: lib/commit.tcl:129
+#, tcl-format
+msgid "warning: Tcl does not support encoding '%s'."
+msgstr "attention : Tcl ne supporte pas le codage '%s'."
+
+#: lib/commit.tcl:149
 msgid ""
 "Last scanned state does not match repository state.\n"
 "\n"
@@ -1344,7 +1391,7 @@
 "\n"
 "Cela va être fait tout de suite automatiquement.\n"
 
-#: lib/commit.tcl:156
+#: lib/commit.tcl:172
 #, tcl-format
 msgid ""
 "Unmerged files cannot be committed.\n"
@@ -1357,7 +1404,7 @@
 "Le fichier %s a des conflicts de fusion. Vous devez les résoudre et pré-"
 "commiter le fichier avant de pouvoir commiter.\n"
 
-#: lib/commit.tcl:164
+#: lib/commit.tcl:180
 #, tcl-format
 msgid ""
 "Unknown file state %s detected.\n"
@@ -1368,7 +1415,7 @@
 "\n"
 "Le fichier %s ne peut pas être commité par ce programme.\n"
 
-#: lib/commit.tcl:172
+#: lib/commit.tcl:188
 msgid ""
 "No changes to commit.\n"
 "\n"
@@ -1378,7 +1425,7 @@
 "\n"
 "Vous devez indexer au moins 1 fichier avant de pouvoir commiter.\n"
 
-#: lib/commit.tcl:187
+#: lib/commit.tcl:203
 msgid ""
 "Please supply a commit message.\n"
 "\n"
@@ -1396,45 +1443,40 @@
 "- Deuxième ligne : rien.\n"
 "- Lignes suivantes : Décrire pourquoi ces modifications sont bonnes.\n"
 
-#: lib/commit.tcl:211
-#, tcl-format
-msgid "warning: Tcl does not support encoding '%s'."
-msgstr "attention : Tcl ne supporte pas le codage '%s'."
-
-#: lib/commit.tcl:227
+#: lib/commit.tcl:234
 msgid "Calling pre-commit hook..."
 msgstr "Lancement de l'action d'avant-commit..."
 
-#: lib/commit.tcl:242
+#: lib/commit.tcl:249
 msgid "Commit declined by pre-commit hook."
 msgstr "Commit refusé par l'action d'avant-commit."
 
-#: lib/commit.tcl:265
+#: lib/commit.tcl:272
 msgid "Calling commit-msg hook..."
 msgstr "Lancement de l'action \"message de commit\"..."
 
-#: lib/commit.tcl:280
+#: lib/commit.tcl:287
 msgid "Commit declined by commit-msg hook."
 msgstr "Commit refusé par l'action \"message de commit\"."
 
-#: lib/commit.tcl:293
+#: lib/commit.tcl:300
 msgid "Committing changes..."
 msgstr "Commit des modifications..."
 
-#: lib/commit.tcl:309
+#: lib/commit.tcl:316
 msgid "write-tree failed:"
 msgstr "write-tree a échoué :"
 
-#: lib/commit.tcl:310 lib/commit.tcl:354 lib/commit.tcl:374
+#: lib/commit.tcl:317 lib/commit.tcl:361 lib/commit.tcl:382
 msgid "Commit failed."
 msgstr "Le commit a échoué."
 
-#: lib/commit.tcl:327
+#: lib/commit.tcl:334
 #, tcl-format
 msgid "Commit %s appears to be corrupt"
 msgstr "Le commit %s semble être corrompu"
 
-#: lib/commit.tcl:332
+#: lib/commit.tcl:339
 msgid ""
 "No changes to commit.\n"
 "\n"
@@ -1449,19 +1491,19 @@
 "\n"
 "Une resynchronisation va être lancée tout de suite automatiquement.\n"
 
-#: lib/commit.tcl:339
+#: lib/commit.tcl:346
 msgid "No changes to commit."
 msgstr "Pas de modifications à commiter."
 
-#: lib/commit.tcl:353
+#: lib/commit.tcl:360
 msgid "commit-tree failed:"
 msgstr "commit-tree a échoué :"
 
-#: lib/commit.tcl:373
+#: lib/commit.tcl:381
 msgid "update-ref failed:"
 msgstr "update-ref a échoué :"
 
-#: lib/commit.tcl:461
+#: lib/commit.tcl:469
 #, tcl-format
 msgid "Created commit %s: %s"
 msgstr "Commit %s créé : %s"
@@ -1514,13 +1556,13 @@
 msgid "Verifying the object database with fsck-objects"
 msgstr "Vérification de la base des objets avec fsck-objects"
 
-#: lib/database.tcl:108
+#: lib/database.tcl:107
 #, tcl-format
 msgid ""
 "This repository currently has approximately %i loose objects.\n"
 "\n"
 "To maintain optimal performance it is strongly recommended that you compress "
-"the database when more than %i loose objects exist.\n"
+"the database.\n"
 "\n"
 "Compress the database now?"
 msgstr ""
@@ -1528,8 +1570,7 @@
 "particulier.\n"
 "\n"
 "Pour conserver une performance optimale, il est fortement recommandé de "
-"comprimer la base quand plus de %i objets ayant leur fichier particulier "
-"existent.\n"
+"comprimer la base de donnée.\n"
 "\n"
 "Comprimer la base maintenant ?"
 
@@ -1538,7 +1579,7 @@
 msgid "Invalid date from Git: %s"
 msgstr "Date invalide de Git : %s"
 
-#: lib/diff.tcl:59
+#: lib/diff.tcl:64
 #, tcl-format
 msgid ""
 "No differences detected.\n"
@@ -1561,12 +1602,12 @@
 "Une resynchronisation va être lancée automatiquement pour trouver d'autres "
 "fichiers qui pourraient se trouver dans le même état."
 
-#: lib/diff.tcl:99
+#: lib/diff.tcl:104
 #, tcl-format
 msgid "Loading diff of %s..."
 msgstr "Chargement des différences de %s..."
 
-#: lib/diff.tcl:120
+#: lib/diff.tcl:125
 msgid ""
 "LOCAL: deleted\n"
 "REMOTE:\n"
@@ -1574,7 +1615,7 @@
 "LOCAL : supprimé\n"
 "DISTANT :\n"
 
-#: lib/diff.tcl:125
+#: lib/diff.tcl:130
 msgid ""
 "REMOTE: deleted\n"
 "LOCAL:\n"
@@ -1582,32 +1623,32 @@
 "DISTANT : supprimé\n"
 "LOCAL :\n"
 
-#: lib/diff.tcl:132
+#: lib/diff.tcl:137
 msgid "LOCAL:\n"
 msgstr "LOCAL :\n"
 
-#: lib/diff.tcl:135
+#: lib/diff.tcl:140
 msgid "REMOTE:\n"
 msgstr "DISTANT :\n"
 
-#: lib/diff.tcl:197 lib/diff.tcl:296
+#: lib/diff.tcl:202 lib/diff.tcl:319
 #, tcl-format
 msgid "Unable to display %s"
 msgstr "Impossible d'afficher %s"
 
-#: lib/diff.tcl:198
+#: lib/diff.tcl:203
 msgid "Error loading file:"
 msgstr "Erreur lors du chargement du fichier :"
 
-#: lib/diff.tcl:205
+#: lib/diff.tcl:210
 msgid "Git Repository (subproject)"
 msgstr "Dépôt Git (sous projet)"
 
-#: lib/diff.tcl:217
+#: lib/diff.tcl:222
 msgid "* Binary file (not showing content)."
 msgstr "* Fichier binaire (pas d'apperçu du contenu)."
 
-#: lib/diff.tcl:222
+#: lib/diff.tcl:227
 #, tcl-format
 msgid ""
 "* Untracked file is %d bytes.\n"
@@ -1616,7 +1657,7 @@
 "* Le fichier non suivi fait %d octets.\n"
 "* Seuls les %d premiers octets sont montrés.\n"
 
-#: lib/diff.tcl:228
+#: lib/diff.tcl:233
 #, tcl-format
 msgid ""
 "\n"
@@ -1627,19 +1668,19 @@
 "* Fichier suivi raccourcis ici de %s.\n"
 "* Pour voir le fichier entier, utilisez un éditeur externe.\n"
 
-#: lib/diff.tcl:436
+#: lib/diff.tcl:482
 msgid "Failed to unstage selected hunk."
 msgstr "Échec lors de la désindexation de la section sélectionnée."
 
-#: lib/diff.tcl:443
+#: lib/diff.tcl:489
 msgid "Failed to stage selected hunk."
 msgstr "Échec lors de l'indexation de la section."
 
-#: lib/diff.tcl:509
+#: lib/diff.tcl:568
 msgid "Failed to unstage selected line."
 msgstr "Échec lors de la désindexation de la ligne sélectionnée."
 
-#: lib/diff.tcl:517
+#: lib/diff.tcl:576
 msgid "Failed to stage selected line."
 msgstr "Échec lors de l'indexation de la ligne."
 
@@ -1676,7 +1717,7 @@
 msgid "Index Error"
 msgstr "Erreur de l'index"
 
-#: lib/index.tcl:21
+#: lib/index.tcl:17
 msgid ""
 "Updating the Git index failed.  A rescan will be automatically started to "
 "resynchronize git-gui."
@@ -1684,7 +1725,7 @@
 "Échec de la mise à jour de l'index. Une resynchronisation va être lancée "
 "automatiquement."
 
-#: lib/index.tcl:27
+#: lib/index.tcl:28
 msgid "Continue"
 msgstr "Continuer"
 
@@ -1692,45 +1733,45 @@
 msgid "Unlock Index"
 msgstr "Déverrouiller l'index"
 
-#: lib/index.tcl:287
+#: lib/index.tcl:289
 #, tcl-format
 msgid "Unstaging %s from commit"
 msgstr "Désindexation de : %s"
 
-#: lib/index.tcl:326
+#: lib/index.tcl:328
 msgid "Ready to commit."
 msgstr "Prêt à être commité."
 
-#: lib/index.tcl:339
+#: lib/index.tcl:341
 #, tcl-format
 msgid "Adding %s"
 msgstr "Ajout de %s"
 
-#: lib/index.tcl:396
+#: lib/index.tcl:398
 #, tcl-format
 msgid "Revert changes in file %s?"
 msgstr "Annuler les modifications dans le fichier %s ? "
 
-#: lib/index.tcl:398
+#: lib/index.tcl:400
 #, tcl-format
 msgid "Revert changes in these %i files?"
 msgstr "Annuler les modifications dans ces %i fichiers ?"
 
-#: lib/index.tcl:406
+#: lib/index.tcl:408
 msgid "Any unstaged changes will be permanently lost by the revert."
 msgstr ""
 "Toutes les modifications non-indexées seront définitivement perdues par "
 "l'annulation."
 
-#: lib/index.tcl:409
+#: lib/index.tcl:411
 msgid "Do Nothing"
 msgstr "Ne rien faire"
 
-#: lib/index.tcl:427
+#: lib/index.tcl:429
 msgid "Reverting selected files"
 msgstr "Annuler modifications dans fichiers selectionnés"
 
-#: lib/index.tcl:431
+#: lib/index.tcl:433
 #, tcl-format
 msgid "Reverting %s"
 msgstr "Annulation des modifications dans %s"
@@ -1913,7 +1954,8 @@
 #: lib/mergetool.tcl:45
 #, tcl-format
 msgid "File %s seems to have unresolved conflicts, still stage?"
-msgstr "Le fichier %s semble avoir des conflits non résolus, indexer quand même ?"
+msgstr ""
+"Le fichier %s semble avoir des conflits non résolus, indexer quand même ?"
 
 #: lib/mergetool.tcl:60
 #, tcl-format
@@ -1922,7 +1964,9 @@
 
 #: lib/mergetool.tcl:141
 msgid "Cannot resolve deletion or link conflicts using a tool"
-msgstr "Impossible de résoudre la suppression ou de relier des conflits en utilisant un outil"
+msgstr ""
+"Impossible de résoudre la suppression ou de relier des conflits en utilisant "
+"un outil"
 
 #: lib/mergetool.tcl:146
 msgid "Conflict file does not exist"
@@ -2171,7 +2215,8 @@
 msgid "Do not know how to initialize repository at location '%s'."
 msgstr "Pas de méthode connue pour initialiser le dépôt à l'emplacement '%s'."
 
-#: lib/remote_add.tcl:163 lib/transport.tcl:25 lib/transport.tcl:71
+#: lib/remote_add.tcl:163 lib/transport.tcl:25 lib/transport.tcl:63
+#: lib/transport.tcl:81
 #, tcl-format
 msgid "push %s"
 msgstr "pousser %s"
@@ -2189,11 +2234,11 @@
 msgid "From Repository"
 msgstr "Dépôt source"
 
-#: lib/remote_branch_delete.tcl:50 lib/transport.tcl:123
+#: lib/remote_branch_delete.tcl:50 lib/transport.tcl:134
 msgid "Remote:"
 msgstr "Branche distante :"
 
-#: lib/remote_branch_delete.tcl:66 lib/transport.tcl:138
+#: lib/remote_branch_delete.tcl:66 lib/transport.tcl:149
 msgid "Arbitrary Location:"
 msgstr "Emplacement arbitraire :"
 
@@ -2209,10 +2254,6 @@
 msgid "Merged Into:"
 msgstr "Fusionné dans :"
 
-#: lib/remote_branch_delete.tcl:119
-msgid "Always (Do not perform merge checks)"
-msgstr "Toujours (ne pas vérifier les fusions)"
-
 #: lib/remote_branch_delete.tcl:152
 msgid "A branch is required for 'Merged Into'."
 msgstr "Une branche est nécessaire pour 'Fusionné dans'."
@@ -2235,32 +2276,23 @@
 "necessary commits.  Try fetching from %s first."
 msgstr ""
 "Un ou plusieurs des tests de fusion ont échoué parce que vous n'avez pas "
-"récupéré les commits nécessaires. Essayez de récupérer à partir de %s d'abord."
+"récupéré les commits nécessaires. Essayez de récupérer à partir de %s "
+"d'abord."
 
 #: lib/remote_branch_delete.tcl:207
 msgid "Please select one or more branches to delete."
 msgstr "Merci de sélectionner une ou plusieurs branches à supprimer."
 
-#: lib/remote_branch_delete.tcl:216
-msgid ""
-"Recovering deleted branches is difficult.\n"
-"\n"
-"Delete the selected branches?"
-msgstr ""
-"Il est difficile de récupérer des branches supprimées.\n"
-"\n"
-"Supprimer les branches sélectionnées ?"
-
 #: lib/remote_branch_delete.tcl:226
 #, tcl-format
 msgid "Deleting branches from %s"
 msgstr "Suppression des branches de %s"
 
-#: lib/remote_branch_delete.tcl:286
+#: lib/remote_branch_delete.tcl:292
 msgid "No repository selected."
 msgstr "Aucun dépôt n'est sélectionné."
 
-#: lib/remote_branch_delete.tcl:291
+#: lib/remote_branch_delete.tcl:297
 #, tcl-format
 msgid "Scanning %s..."
 msgstr "Synchronisation de %s..."
@@ -2281,11 +2313,11 @@
 msgid "Case-Sensitive"
 msgstr "Sensible à la casse"
 
-#: lib/shortcut.tcl:20 lib/shortcut.tcl:61
+#: lib/shortcut.tcl:21 lib/shortcut.tcl:62
 msgid "Cannot write shortcut:"
 msgstr "Impossible d'écrire le raccourci :"
 
-#: lib/shortcut.tcl:136
+#: lib/shortcut.tcl:137
 msgid "Cannot write icon:"
 msgstr "Impossible d'écrire l'icône :"
 
@@ -2440,7 +2472,8 @@
 
 #: lib/tools_dlg.tcl:80
 msgid "Ask the user to select a revision (sets $REVISION)"
-msgstr "Demander à l'utilisateur de sélectionner une révision (change $REVISION)"
+msgstr ""
+"Demander à l'utilisateur de sélectionner une révision (change $REVISION)"
 
 #: lib/tools_dlg.tcl:85
 msgid "Ask the user for additional arguments (sets $ARGS)"
@@ -2521,38 +2554,51 @@
 msgid "Pushing changes to %s"
 msgstr "Les modifications sont poussées vers %s"
 
-#: lib/transport.tcl:72
+#: lib/transport.tcl:64
+#, tcl-format
+msgid "Mirroring to %s"
+msgstr "Dupliquer dans %s"
+
+#: lib/transport.tcl:82
 #, tcl-format
 msgid "Pushing %s %s to %s"
 msgstr "Pousse %s %s vers %s"
 
-#: lib/transport.tcl:89
+#: lib/transport.tcl:100
 msgid "Push Branches"
 msgstr "Pousser branches"
 
-#: lib/transport.tcl:103
+#: lib/transport.tcl:114
 msgid "Source Branches"
 msgstr "Branches source"
 
-#: lib/transport.tcl:120
+#: lib/transport.tcl:131
 msgid "Destination Repository"
 msgstr "Dépôt de destination"
 
-#: lib/transport.tcl:158
+#: lib/transport.tcl:169
 msgid "Transfer Options"
 msgstr "Options de transfert"
 
-#: lib/transport.tcl:160
+#: lib/transport.tcl:171
 msgid "Force overwrite existing branch (may discard changes)"
 msgstr ""
 "Forcer l'écrasement d'une branche existante (peut supprimer des "
 "modifications)"
 
-#: lib/transport.tcl:164
+#: lib/transport.tcl:175
 msgid "Use thin pack (for slow network connections)"
 msgstr "Utiliser des petits paquets (pour les connexions lentes)"
 
-#: lib/transport.tcl:168
+#: lib/transport.tcl:179
 msgid "Include tags"
 msgstr "Inclure les marques (tags)"
 
+#~ msgid "Cannot use funny .git directory:"
+#~ msgstr "Impossible d'utiliser le répertoire .git:"
+
+#~ msgid "Preferences..."
+#~ msgstr "Préférences..."
+
+#~ msgid "Always (Do not perform merge test.)"
+#~ msgstr "Toujours (Ne pas faire de test de fusion.)"
diff --git a/git-gui/po/git-gui.pot b/git-gui/po/git-gui.pot
index 53b7d36..1ae2aaa 100644
--- a/git-gui/po/git-gui.pot
+++ b/git-gui/po/git-gui.pot
@@ -8,7 +8,7 @@
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-12-08 08:31-0800\n"
+"POT-Creation-Date: 2010-01-26 15:47-0800\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -16,33 +16,33 @@
 "Content-Type: text/plain; charset=CHARSET\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: git-gui.sh:41 git-gui.sh:737 git-gui.sh:751 git-gui.sh:764 git-gui.sh:847
-#: git-gui.sh:866
+#: git-gui.sh:41 git-gui.sh:793 git-gui.sh:807 git-gui.sh:820 git-gui.sh:903
+#: git-gui.sh:922
 msgid "git-gui: fatal error"
 msgstr ""
 
-#: git-gui.sh:689
+#: git-gui.sh:743
 #, tcl-format
 msgid "Invalid font specified in %s:"
 msgstr ""
 
-#: git-gui.sh:723
+#: git-gui.sh:779
 msgid "Main Font"
 msgstr ""
 
-#: git-gui.sh:724
+#: git-gui.sh:780
 msgid "Diff/Console Font"
 msgstr ""
 
-#: git-gui.sh:738
+#: git-gui.sh:794
 msgid "Cannot find git in PATH."
 msgstr ""
 
-#: git-gui.sh:765
+#: git-gui.sh:821
 msgid "Cannot parse Git version string:"
 msgstr ""
 
-#: git-gui.sh:783
+#: git-gui.sh:839
 #, tcl-format
 msgid ""
 "Git version cannot be determined.\n"
@@ -54,444 +54,473 @@
 "Assume '%s' is version 1.5.0?\n"
 msgstr ""
 
-#: git-gui.sh:1062
+#: git-gui.sh:1128
 msgid "Git directory not found:"
 msgstr ""
 
-#: git-gui.sh:1069
+#: git-gui.sh:1146
 msgid "Cannot move to top of working directory:"
 msgstr ""
 
-#: git-gui.sh:1076
-msgid "Cannot use funny .git directory:"
+#: git-gui.sh:1154
+msgid "Cannot use bare repository:"
 msgstr ""
 
-#: git-gui.sh:1081
+#: git-gui.sh:1162
 msgid "No working directory"
 msgstr ""
 
-#: git-gui.sh:1247 lib/checkout_op.tcl:305
+#: git-gui.sh:1334 lib/checkout_op.tcl:306
 msgid "Refreshing file status..."
 msgstr ""
 
-#: git-gui.sh:1303
+#: git-gui.sh:1390
 msgid "Scanning for modified files ..."
 msgstr ""
 
-#: git-gui.sh:1367
+#: git-gui.sh:1454
 msgid "Calling prepare-commit-msg hook..."
 msgstr ""
 
-#: git-gui.sh:1384
+#: git-gui.sh:1471
 msgid "Commit declined by prepare-commit-msg hook."
 msgstr ""
 
-#: git-gui.sh:1542 lib/browser.tcl:246
+#: git-gui.sh:1629 lib/browser.tcl:246
 msgid "Ready."
 msgstr ""
 
-#: git-gui.sh:1819
+#: git-gui.sh:1787
+#, tcl-format
+msgid "Displaying only %s of %s files."
+msgstr ""
+
+#: git-gui.sh:1913
 msgid "Unmodified"
 msgstr ""
 
-#: git-gui.sh:1821
+#: git-gui.sh:1915
 msgid "Modified, not staged"
 msgstr ""
 
-#: git-gui.sh:1822 git-gui.sh:1830
+#: git-gui.sh:1916 git-gui.sh:1924
 msgid "Staged for commit"
 msgstr ""
 
-#: git-gui.sh:1823 git-gui.sh:1831
+#: git-gui.sh:1917 git-gui.sh:1925
 msgid "Portions staged for commit"
 msgstr ""
 
-#: git-gui.sh:1824 git-gui.sh:1832
+#: git-gui.sh:1918 git-gui.sh:1926
 msgid "Staged for commit, missing"
 msgstr ""
 
-#: git-gui.sh:1826
+#: git-gui.sh:1920
 msgid "File type changed, not staged"
 msgstr ""
 
-#: git-gui.sh:1827
+#: git-gui.sh:1921
 msgid "File type changed, staged"
 msgstr ""
 
-#: git-gui.sh:1829
+#: git-gui.sh:1923
 msgid "Untracked, not staged"
 msgstr ""
 
-#: git-gui.sh:1834
+#: git-gui.sh:1928
 msgid "Missing"
 msgstr ""
 
-#: git-gui.sh:1835
+#: git-gui.sh:1929
 msgid "Staged for removal"
 msgstr ""
 
-#: git-gui.sh:1836
+#: git-gui.sh:1930
 msgid "Staged for removal, still present"
 msgstr ""
 
-#: git-gui.sh:1838 git-gui.sh:1839 git-gui.sh:1840 git-gui.sh:1841
-#: git-gui.sh:1842 git-gui.sh:1843
+#: git-gui.sh:1932 git-gui.sh:1933 git-gui.sh:1934 git-gui.sh:1935
+#: git-gui.sh:1936 git-gui.sh:1937
 msgid "Requires merge resolution"
 msgstr ""
 
-#: git-gui.sh:1878
+#: git-gui.sh:1972
 msgid "Starting gitk... please wait..."
 msgstr ""
 
-#: git-gui.sh:1887
+#: git-gui.sh:1984
 msgid "Couldn't find gitk in PATH"
 msgstr ""
 
-#: git-gui.sh:2280 lib/choose_repository.tcl:36
+#: git-gui.sh:2043
+msgid "Couldn't find git gui in PATH"
+msgstr ""
+
+#: git-gui.sh:2455 lib/choose_repository.tcl:36
 msgid "Repository"
 msgstr ""
 
-#: git-gui.sh:2281
+#: git-gui.sh:2456
 msgid "Edit"
 msgstr ""
 
-#: git-gui.sh:2283 lib/choose_rev.tcl:561
+#: git-gui.sh:2458 lib/choose_rev.tcl:561
 msgid "Branch"
 msgstr ""
 
-#: git-gui.sh:2286 lib/choose_rev.tcl:548
+#: git-gui.sh:2461 lib/choose_rev.tcl:548
 msgid "Commit@@noun"
 msgstr ""
 
-#: git-gui.sh:2289 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
+#: git-gui.sh:2464 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
 msgid "Merge"
 msgstr ""
 
-#: git-gui.sh:2290 lib/choose_rev.tcl:557
+#: git-gui.sh:2465 lib/choose_rev.tcl:557
 msgid "Remote"
 msgstr ""
 
-#: git-gui.sh:2293
+#: git-gui.sh:2468
 msgid "Tools"
 msgstr ""
 
-#: git-gui.sh:2302
+#: git-gui.sh:2477
 msgid "Explore Working Copy"
 msgstr ""
 
-#: git-gui.sh:2307
+#: git-gui.sh:2483
 msgid "Browse Current Branch's Files"
 msgstr ""
 
-#: git-gui.sh:2311
+#: git-gui.sh:2487
 msgid "Browse Branch Files..."
 msgstr ""
 
-#: git-gui.sh:2316
+#: git-gui.sh:2492
 msgid "Visualize Current Branch's History"
 msgstr ""
 
-#: git-gui.sh:2320
+#: git-gui.sh:2496
 msgid "Visualize All Branch History"
 msgstr ""
 
-#: git-gui.sh:2327
+#: git-gui.sh:2503
 #, tcl-format
 msgid "Browse %s's Files"
 msgstr ""
 
-#: git-gui.sh:2329
+#: git-gui.sh:2505
 #, tcl-format
 msgid "Visualize %s's History"
 msgstr ""
 
-#: git-gui.sh:2334 lib/database.tcl:27 lib/database.tcl:67
+#: git-gui.sh:2510 lib/database.tcl:27 lib/database.tcl:67
 msgid "Database Statistics"
 msgstr ""
 
-#: git-gui.sh:2337 lib/database.tcl:34
+#: git-gui.sh:2513 lib/database.tcl:34
 msgid "Compress Database"
 msgstr ""
 
-#: git-gui.sh:2340
+#: git-gui.sh:2516
 msgid "Verify Database"
 msgstr ""
 
-#: git-gui.sh:2347 git-gui.sh:2351 git-gui.sh:2355 lib/shortcut.tcl:7
-#: lib/shortcut.tcl:39 lib/shortcut.tcl:71
+#: git-gui.sh:2523 git-gui.sh:2527 git-gui.sh:2531 lib/shortcut.tcl:8
+#: lib/shortcut.tcl:40 lib/shortcut.tcl:72
 msgid "Create Desktop Icon"
 msgstr ""
 
-#: git-gui.sh:2363 lib/choose_repository.tcl:183 lib/choose_repository.tcl:191
+#: git-gui.sh:2539 lib/choose_repository.tcl:183 lib/choose_repository.tcl:191
 msgid "Quit"
 msgstr ""
 
-#: git-gui.sh:2371
+#: git-gui.sh:2547
 msgid "Undo"
 msgstr ""
 
-#: git-gui.sh:2374
+#: git-gui.sh:2550
 msgid "Redo"
 msgstr ""
 
-#: git-gui.sh:2378 git-gui.sh:2937
+#: git-gui.sh:2554 git-gui.sh:3109
 msgid "Cut"
 msgstr ""
 
-#: git-gui.sh:2381 git-gui.sh:2940 git-gui.sh:3014 git-gui.sh:3096
+#: git-gui.sh:2557 git-gui.sh:3112 git-gui.sh:3186 git-gui.sh:3259
 #: lib/console.tcl:69
 msgid "Copy"
 msgstr ""
 
-#: git-gui.sh:2384 git-gui.sh:2943
+#: git-gui.sh:2560 git-gui.sh:3115
 msgid "Paste"
 msgstr ""
 
-#: git-gui.sh:2387 git-gui.sh:2946 lib/branch_delete.tcl:26
+#: git-gui.sh:2563 git-gui.sh:3118 lib/branch_delete.tcl:26
 #: lib/remote_branch_delete.tcl:38
 msgid "Delete"
 msgstr ""
 
-#: git-gui.sh:2391 git-gui.sh:2950 git-gui.sh:3100 lib/console.tcl:71
+#: git-gui.sh:2567 git-gui.sh:3122 git-gui.sh:3263 lib/console.tcl:71
 msgid "Select All"
 msgstr ""
 
-#: git-gui.sh:2400
+#: git-gui.sh:2576
 msgid "Create..."
 msgstr ""
 
-#: git-gui.sh:2406
+#: git-gui.sh:2582
 msgid "Checkout..."
 msgstr ""
 
-#: git-gui.sh:2412
+#: git-gui.sh:2588
 msgid "Rename..."
 msgstr ""
 
-#: git-gui.sh:2417
+#: git-gui.sh:2593
 msgid "Delete..."
 msgstr ""
 
-#: git-gui.sh:2422
+#: git-gui.sh:2598
 msgid "Reset..."
 msgstr ""
 
-#: git-gui.sh:2432
+#: git-gui.sh:2608
 msgid "Done"
 msgstr ""
 
-#: git-gui.sh:2434
+#: git-gui.sh:2610
 msgid "Commit@@verb"
 msgstr ""
 
-#: git-gui.sh:2443 git-gui.sh:2878
+#: git-gui.sh:2619 git-gui.sh:3050
 msgid "New Commit"
 msgstr ""
 
-#: git-gui.sh:2451 git-gui.sh:2885
+#: git-gui.sh:2627 git-gui.sh:3057
 msgid "Amend Last Commit"
 msgstr ""
 
-#: git-gui.sh:2461 git-gui.sh:2839 lib/remote_branch_delete.tcl:99
+#: git-gui.sh:2637 git-gui.sh:3011 lib/remote_branch_delete.tcl:99
 msgid "Rescan"
 msgstr ""
 
-#: git-gui.sh:2467
+#: git-gui.sh:2643
 msgid "Stage To Commit"
 msgstr ""
 
-#: git-gui.sh:2473
+#: git-gui.sh:2649
 msgid "Stage Changed Files To Commit"
 msgstr ""
 
-#: git-gui.sh:2479
+#: git-gui.sh:2655
 msgid "Unstage From Commit"
 msgstr ""
 
-#: git-gui.sh:2484 lib/index.tcl:410
+#: git-gui.sh:2661 lib/index.tcl:412
 msgid "Revert Changes"
 msgstr ""
 
-#: git-gui.sh:2491 git-gui.sh:3083
+#: git-gui.sh:2669 git-gui.sh:3310 git-gui.sh:3341
 msgid "Show Less Context"
 msgstr ""
 
-#: git-gui.sh:2495 git-gui.sh:3087
+#: git-gui.sh:2673 git-gui.sh:3314 git-gui.sh:3345
 msgid "Show More Context"
 msgstr ""
 
-#: git-gui.sh:2502 git-gui.sh:2852 git-gui.sh:2961
+#: git-gui.sh:2680 git-gui.sh:3024 git-gui.sh:3133
 msgid "Sign Off"
 msgstr ""
 
-#: git-gui.sh:2518
+#: git-gui.sh:2696
 msgid "Local Merge..."
 msgstr ""
 
-#: git-gui.sh:2523
+#: git-gui.sh:2701
 msgid "Abort Merge..."
 msgstr ""
 
-#: git-gui.sh:2535 git-gui.sh:2575
+#: git-gui.sh:2713 git-gui.sh:2741
 msgid "Add..."
 msgstr ""
 
-#: git-gui.sh:2539
+#: git-gui.sh:2717
 msgid "Push..."
 msgstr ""
 
-#: git-gui.sh:2543
+#: git-gui.sh:2721
 msgid "Delete Branch..."
 msgstr ""
 
-#: git-gui.sh:2553 git-gui.sh:2589 lib/about.tcl:14
+#: git-gui.sh:2731 git-gui.sh:3292
+msgid "Options..."
+msgstr ""
+
+#: git-gui.sh:2742
+msgid "Remove..."
+msgstr ""
+
+#: git-gui.sh:2751 lib/choose_repository.tcl:50
+msgid "Help"
+msgstr ""
+
+#: git-gui.sh:2755 git-gui.sh:2759 lib/about.tcl:14
 #: lib/choose_repository.tcl:44 lib/choose_repository.tcl:53
 #, tcl-format
 msgid "About %s"
 msgstr ""
 
-#: git-gui.sh:2557
-msgid "Preferences..."
-msgstr ""
-
-#: git-gui.sh:2565 git-gui.sh:3129
-msgid "Options..."
-msgstr ""
-
-#: git-gui.sh:2576
-msgid "Remove..."
-msgstr ""
-
-#: git-gui.sh:2585 lib/choose_repository.tcl:50
-msgid "Help"
-msgstr ""
-
-#: git-gui.sh:2611
+#: git-gui.sh:2783
 msgid "Online Documentation"
 msgstr ""
 
-#: git-gui.sh:2614 lib/choose_repository.tcl:47 lib/choose_repository.tcl:56
+#: git-gui.sh:2786 lib/choose_repository.tcl:47 lib/choose_repository.tcl:56
 msgid "Show SSH Key"
 msgstr ""
 
-#: git-gui.sh:2721
+#: git-gui.sh:2893
 #, tcl-format
 msgid "fatal: cannot stat path %s: No such file or directory"
 msgstr ""
 
-#: git-gui.sh:2754
+#: git-gui.sh:2926
 msgid "Current Branch:"
 msgstr ""
 
-#: git-gui.sh:2775
+#: git-gui.sh:2947
 msgid "Staged Changes (Will Commit)"
 msgstr ""
 
-#: git-gui.sh:2795
+#: git-gui.sh:2967
 msgid "Unstaged Changes"
 msgstr ""
 
-#: git-gui.sh:2845
+#: git-gui.sh:3017
 msgid "Stage Changed"
 msgstr ""
 
-#: git-gui.sh:2864 lib/transport.tcl:104 lib/transport.tcl:193
+#: git-gui.sh:3036 lib/transport.tcl:104 lib/transport.tcl:193
 msgid "Push"
 msgstr ""
 
-#: git-gui.sh:2899
+#: git-gui.sh:3071
 msgid "Initial Commit Message:"
 msgstr ""
 
-#: git-gui.sh:2900
+#: git-gui.sh:3072
 msgid "Amended Commit Message:"
 msgstr ""
 
-#: git-gui.sh:2901
+#: git-gui.sh:3073
 msgid "Amended Initial Commit Message:"
 msgstr ""
 
-#: git-gui.sh:2902
+#: git-gui.sh:3074
 msgid "Amended Merge Commit Message:"
 msgstr ""
 
-#: git-gui.sh:2903
+#: git-gui.sh:3075
 msgid "Merge Commit Message:"
 msgstr ""
 
-#: git-gui.sh:2904
+#: git-gui.sh:3076
 msgid "Commit Message:"
 msgstr ""
 
-#: git-gui.sh:2953 git-gui.sh:3104 lib/console.tcl:73
+#: git-gui.sh:3125 git-gui.sh:3267 lib/console.tcl:73
 msgid "Copy All"
 msgstr ""
 
-#: git-gui.sh:2977 lib/blame.tcl:104
+#: git-gui.sh:3149 lib/blame.tcl:104
 msgid "File:"
 msgstr ""
 
-#: git-gui.sh:3092
+#: git-gui.sh:3255
 msgid "Refresh"
 msgstr ""
 
-#: git-gui.sh:3113
+#: git-gui.sh:3276
 msgid "Decrease Font Size"
 msgstr ""
 
-#: git-gui.sh:3117
+#: git-gui.sh:3280
 msgid "Increase Font Size"
 msgstr ""
 
-#: git-gui.sh:3125 lib/blame.tcl:281
+#: git-gui.sh:3288 lib/blame.tcl:281
 msgid "Encoding"
 msgstr ""
 
-#: git-gui.sh:3136
+#: git-gui.sh:3299
 msgid "Apply/Reverse Hunk"
 msgstr ""
 
-#: git-gui.sh:3141
+#: git-gui.sh:3304
 msgid "Apply/Reverse Line"
 msgstr ""
 
-#: git-gui.sh:3151
+#: git-gui.sh:3323
 msgid "Run Merge Tool"
 msgstr ""
 
-#: git-gui.sh:3156
+#: git-gui.sh:3328
 msgid "Use Remote Version"
 msgstr ""
 
-#: git-gui.sh:3160
+#: git-gui.sh:3332
 msgid "Use Local Version"
 msgstr ""
 
-#: git-gui.sh:3164
+#: git-gui.sh:3336
 msgid "Revert To Base"
 msgstr ""
 
-#: git-gui.sh:3183
+#: git-gui.sh:3354
+msgid "Visualize These Changes In The Submodule"
+msgstr ""
+
+#: git-gui.sh:3358
+msgid "Visualize Current Branch History In The Submodule"
+msgstr ""
+
+#: git-gui.sh:3362
+msgid "Visualize All Branch History In The Submodule"
+msgstr ""
+
+#: git-gui.sh:3367
+msgid "Start git gui In The Submodule"
+msgstr ""
+
+#: git-gui.sh:3389
 msgid "Unstage Hunk From Commit"
 msgstr ""
 
-#: git-gui.sh:3184
+#: git-gui.sh:3391
+msgid "Unstage Lines From Commit"
+msgstr ""
+
+#: git-gui.sh:3393
 msgid "Unstage Line From Commit"
 msgstr ""
 
-#: git-gui.sh:3186
+#: git-gui.sh:3396
 msgid "Stage Hunk For Commit"
 msgstr ""
 
-#: git-gui.sh:3187
+#: git-gui.sh:3398
+msgid "Stage Lines For Commit"
+msgstr ""
+
+#: git-gui.sh:3400
 msgid "Stage Line For Commit"
 msgstr ""
 
-#: git-gui.sh:3210
+#: git-gui.sh:3424
 msgid "Initializing..."
 msgstr ""
 
-#: git-gui.sh:3315
+#: git-gui.sh:3541
 #, tcl-format
 msgid ""
 "Possible environment issues exist.\n"
@@ -502,14 +531,14 @@
 "\n"
 msgstr ""
 
-#: git-gui.sh:3345
+#: git-gui.sh:3570
 msgid ""
 "\n"
 "This is due to a known issue with the\n"
 "Tcl binary distributed by Cygwin."
 msgstr ""
 
-#: git-gui.sh:3350
+#: git-gui.sh:3575
 #, tcl-format
 msgid ""
 "\n"
@@ -613,7 +642,7 @@
 msgid "Unable to display parent"
 msgstr ""
 
-#: lib/blame.tcl:1091 lib/diff.tcl:297
+#: lib/blame.tcl:1091 lib/diff.tcl:320
 msgid "Error loading diff:"
 msgstr ""
 
@@ -639,7 +668,7 @@
 
 #: lib/branch_checkout.tcl:27 lib/branch_create.tcl:35
 #: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:282
-#: lib/checkout_op.tcl:544 lib/choose_font.tcl:43 lib/merge.tcl:172
+#: lib/checkout_op.tcl:579 lib/choose_font.tcl:43 lib/merge.tcl:172
 #: lib/option.tcl:125 lib/remote_add.tcl:32 lib/remote_branch_delete.tcl:42
 #: lib/tools_dlg.tcl:40 lib/tools_dlg.tcl:204 lib/tools_dlg.tcl:352
 #: lib/transport.tcl:108
@@ -670,7 +699,7 @@
 msgid "Create New Branch"
 msgstr ""
 
-#: lib/branch_create.tcl:31 lib/choose_repository.tcl:377
+#: lib/branch_create.tcl:31 lib/choose_repository.tcl:381
 msgid "Create"
 msgstr ""
 
@@ -702,7 +731,7 @@
 msgid "Fast Forward Only"
 msgstr ""
 
-#: lib/branch_create.tcl:85 lib/checkout_op.tcl:536
+#: lib/branch_create.tcl:85 lib/checkout_op.tcl:571
 msgid "Reset"
 msgstr ""
 
@@ -744,8 +773,8 @@
 msgid "Delete Only If Merged Into"
 msgstr ""
 
-#: lib/branch_delete.tcl:54
-msgid "Always (Do not perform merge test.)"
+#: lib/branch_delete.tcl:54 lib/remote_branch_delete.tcl:119
+msgid "Always (Do not perform merge checks)"
 msgstr ""
 
 #: lib/branch_delete.tcl:103
@@ -753,6 +782,13 @@
 msgid "The following branches are not completely merged into %s:"
 msgstr ""
 
+#: lib/branch_delete.tcl:115 lib/remote_branch_delete.tcl:217
+msgid ""
+"Recovering deleted branches is difficult.\n"
+"\n"
+"Delete the selected branches?"
+msgstr ""
+
 #: lib/branch_delete.tcl:141
 #, tcl-format
 msgid ""
@@ -780,7 +816,7 @@
 msgid "Please select a branch to rename."
 msgstr ""
 
-#: lib/branch_rename.tcl:96 lib/checkout_op.tcl:201
+#: lib/branch_rename.tcl:96 lib/checkout_op.tcl:202
 #, tcl-format
 msgid "Branch '%s' already exists."
 msgstr ""
@@ -811,38 +847,38 @@
 msgid "Browse Branch Files"
 msgstr ""
 
-#: lib/browser.tcl:278 lib/choose_repository.tcl:394
-#: lib/choose_repository.tcl:480 lib/choose_repository.tcl:491
-#: lib/choose_repository.tcl:995
+#: lib/browser.tcl:278 lib/choose_repository.tcl:398
+#: lib/choose_repository.tcl:486 lib/choose_repository.tcl:497
+#: lib/choose_repository.tcl:1028
 msgid "Browse"
 msgstr ""
 
-#: lib/checkout_op.tcl:84
+#: lib/checkout_op.tcl:85
 #, tcl-format
 msgid "Fetching %s from %s"
 msgstr ""
 
-#: lib/checkout_op.tcl:132
+#: lib/checkout_op.tcl:133
 #, tcl-format
 msgid "fatal: Cannot resolve %s"
 msgstr ""
 
-#: lib/checkout_op.tcl:145 lib/console.tcl:81 lib/database.tcl:31
+#: lib/checkout_op.tcl:146 lib/console.tcl:81 lib/database.tcl:31
 #: lib/sshkey.tcl:53
 msgid "Close"
 msgstr ""
 
-#: lib/checkout_op.tcl:174
+#: lib/checkout_op.tcl:175
 #, tcl-format
 msgid "Branch '%s' does not exist."
 msgstr ""
 
-#: lib/checkout_op.tcl:193
+#: lib/checkout_op.tcl:194
 #, tcl-format
 msgid "Failed to configure simplified git-pull for '%s'."
 msgstr ""
 
-#: lib/checkout_op.tcl:228
+#: lib/checkout_op.tcl:229
 #, tcl-format
 msgid ""
 "Branch '%s' already exists.\n"
@@ -851,21 +887,21 @@
 "A merge is required."
 msgstr ""
 
-#: lib/checkout_op.tcl:242
+#: lib/checkout_op.tcl:243
 #, tcl-format
 msgid "Merge strategy '%s' not supported."
 msgstr ""
 
-#: lib/checkout_op.tcl:261
+#: lib/checkout_op.tcl:262
 #, tcl-format
 msgid "Failed to update '%s'."
 msgstr ""
 
-#: lib/checkout_op.tcl:273
+#: lib/checkout_op.tcl:274
 msgid "Staging area (index) is already locked."
 msgstr ""
 
-#: lib/checkout_op.tcl:288
+#: lib/checkout_op.tcl:289
 msgid ""
 "Last scanned state does not match repository state.\n"
 "\n"
@@ -875,30 +911,30 @@
 "The rescan will be automatically started now.\n"
 msgstr ""
 
-#: lib/checkout_op.tcl:344
+#: lib/checkout_op.tcl:345
 #, tcl-format
 msgid "Updating working directory to '%s'..."
 msgstr ""
 
-#: lib/checkout_op.tcl:345
+#: lib/checkout_op.tcl:346
 msgid "files checked out"
 msgstr ""
 
-#: lib/checkout_op.tcl:375
+#: lib/checkout_op.tcl:376
 #, tcl-format
 msgid "Aborted checkout of '%s' (file level merging is required)."
 msgstr ""
 
-#: lib/checkout_op.tcl:376
+#: lib/checkout_op.tcl:377
 msgid "File level merge required."
 msgstr ""
 
-#: lib/checkout_op.tcl:380
+#: lib/checkout_op.tcl:381
 #, tcl-format
 msgid "Staying on branch '%s'."
 msgstr ""
 
-#: lib/checkout_op.tcl:451
+#: lib/checkout_op.tcl:452
 msgid ""
 "You are no longer on a local branch.\n"
 "\n"
@@ -906,30 +942,30 @@
 "Checkout'."
 msgstr ""
 
-#: lib/checkout_op.tcl:468 lib/checkout_op.tcl:472
+#: lib/checkout_op.tcl:503 lib/checkout_op.tcl:507
 #, tcl-format
 msgid "Checked out '%s'."
 msgstr ""
 
-#: lib/checkout_op.tcl:500
+#: lib/checkout_op.tcl:535
 #, tcl-format
 msgid "Resetting '%s' to '%s' will lose the following commits:"
 msgstr ""
 
-#: lib/checkout_op.tcl:522
+#: lib/checkout_op.tcl:557
 msgid "Recovering lost commits may not be easy."
 msgstr ""
 
-#: lib/checkout_op.tcl:527
+#: lib/checkout_op.tcl:562
 #, tcl-format
 msgid "Reset '%s'?"
 msgstr ""
 
-#: lib/checkout_op.tcl:532 lib/merge.tcl:164 lib/tools_dlg.tcl:343
+#: lib/checkout_op.tcl:567 lib/merge.tcl:164 lib/tools_dlg.tcl:343
 msgid "Visualize"
 msgstr ""
 
-#: lib/checkout_op.tcl:600
+#: lib/checkout_op.tcl:635
 #, tcl-format
 msgid ""
 "Failed to set current branch.\n"
@@ -966,7 +1002,7 @@
 msgid "Git Gui"
 msgstr ""
 
-#: lib/choose_repository.tcl:87 lib/choose_repository.tcl:382
+#: lib/choose_repository.tcl:87 lib/choose_repository.tcl:386
 msgid "Create New Repository"
 msgstr ""
 
@@ -974,7 +1010,7 @@
 msgid "New..."
 msgstr ""
 
-#: lib/choose_repository.tcl:100 lib/choose_repository.tcl:465
+#: lib/choose_repository.tcl:100 lib/choose_repository.tcl:471
 msgid "Clone Existing Repository"
 msgstr ""
 
@@ -982,7 +1018,7 @@
 msgid "Clone..."
 msgstr ""
 
-#: lib/choose_repository.tcl:113 lib/choose_repository.tcl:983
+#: lib/choose_repository.tcl:113 lib/choose_repository.tcl:1016
 msgid "Open Existing Repository"
 msgstr ""
 
@@ -998,193 +1034,193 @@
 msgid "Open Recent Repository:"
 msgstr ""
 
-#: lib/choose_repository.tcl:302 lib/choose_repository.tcl:309
-#: lib/choose_repository.tcl:316
+#: lib/choose_repository.tcl:306 lib/choose_repository.tcl:313
+#: lib/choose_repository.tcl:320
 #, tcl-format
 msgid "Failed to create repository %s:"
 msgstr ""
 
-#: lib/choose_repository.tcl:387
+#: lib/choose_repository.tcl:391
 msgid "Directory:"
 msgstr ""
 
-#: lib/choose_repository.tcl:417 lib/choose_repository.tcl:544
-#: lib/choose_repository.tcl:1017
+#: lib/choose_repository.tcl:423 lib/choose_repository.tcl:550
+#: lib/choose_repository.tcl:1052
 msgid "Git Repository"
 msgstr ""
 
-#: lib/choose_repository.tcl:442
+#: lib/choose_repository.tcl:448
 #, tcl-format
 msgid "Directory %s already exists."
 msgstr ""
 
-#: lib/choose_repository.tcl:446
+#: lib/choose_repository.tcl:452
 #, tcl-format
 msgid "File %s already exists."
 msgstr ""
 
-#: lib/choose_repository.tcl:460
+#: lib/choose_repository.tcl:466
 msgid "Clone"
 msgstr ""
 
-#: lib/choose_repository.tcl:473
+#: lib/choose_repository.tcl:479
 msgid "Source Location:"
 msgstr ""
 
-#: lib/choose_repository.tcl:484
+#: lib/choose_repository.tcl:490
 msgid "Target Directory:"
 msgstr ""
 
-#: lib/choose_repository.tcl:496
+#: lib/choose_repository.tcl:502
 msgid "Clone Type:"
 msgstr ""
 
-#: lib/choose_repository.tcl:502
+#: lib/choose_repository.tcl:508
 msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
 msgstr ""
 
-#: lib/choose_repository.tcl:508
+#: lib/choose_repository.tcl:514
 msgid "Full Copy (Slower, Redundant Backup)"
 msgstr ""
 
-#: lib/choose_repository.tcl:514
+#: lib/choose_repository.tcl:520
 msgid "Shared (Fastest, Not Recommended, No Backup)"
 msgstr ""
 
-#: lib/choose_repository.tcl:550 lib/choose_repository.tcl:597
-#: lib/choose_repository.tcl:743 lib/choose_repository.tcl:813
-#: lib/choose_repository.tcl:1023 lib/choose_repository.tcl:1031
+#: lib/choose_repository.tcl:556 lib/choose_repository.tcl:603
+#: lib/choose_repository.tcl:749 lib/choose_repository.tcl:819
+#: lib/choose_repository.tcl:1058 lib/choose_repository.tcl:1066
 #, tcl-format
 msgid "Not a Git repository: %s"
 msgstr ""
 
-#: lib/choose_repository.tcl:586
+#: lib/choose_repository.tcl:592
 msgid "Standard only available for local repository."
 msgstr ""
 
-#: lib/choose_repository.tcl:590
+#: lib/choose_repository.tcl:596
 msgid "Shared only available for local repository."
 msgstr ""
 
-#: lib/choose_repository.tcl:611
+#: lib/choose_repository.tcl:617
 #, tcl-format
 msgid "Location %s already exists."
 msgstr ""
 
-#: lib/choose_repository.tcl:622
+#: lib/choose_repository.tcl:628
 msgid "Failed to configure origin"
 msgstr ""
 
-#: lib/choose_repository.tcl:634
+#: lib/choose_repository.tcl:640
 msgid "Counting objects"
 msgstr ""
 
-#: lib/choose_repository.tcl:635
+#: lib/choose_repository.tcl:641
 msgid "buckets"
 msgstr ""
 
-#: lib/choose_repository.tcl:659
+#: lib/choose_repository.tcl:665
 #, tcl-format
 msgid "Unable to copy objects/info/alternates: %s"
 msgstr ""
 
-#: lib/choose_repository.tcl:695
+#: lib/choose_repository.tcl:701
 #, tcl-format
 msgid "Nothing to clone from %s."
 msgstr ""
 
-#: lib/choose_repository.tcl:697 lib/choose_repository.tcl:911
-#: lib/choose_repository.tcl:923
+#: lib/choose_repository.tcl:703 lib/choose_repository.tcl:917
+#: lib/choose_repository.tcl:929
 msgid "The 'master' branch has not been initialized."
 msgstr ""
 
-#: lib/choose_repository.tcl:710
+#: lib/choose_repository.tcl:716
 msgid "Hardlinks are unavailable.  Falling back to copying."
 msgstr ""
 
-#: lib/choose_repository.tcl:722
+#: lib/choose_repository.tcl:728
 #, tcl-format
 msgid "Cloning from %s"
 msgstr ""
 
-#: lib/choose_repository.tcl:753
+#: lib/choose_repository.tcl:759
 msgid "Copying objects"
 msgstr ""
 
-#: lib/choose_repository.tcl:754
+#: lib/choose_repository.tcl:760
 msgid "KiB"
 msgstr ""
 
-#: lib/choose_repository.tcl:778
+#: lib/choose_repository.tcl:784
 #, tcl-format
 msgid "Unable to copy object: %s"
 msgstr ""
 
-#: lib/choose_repository.tcl:788
+#: lib/choose_repository.tcl:794
 msgid "Linking objects"
 msgstr ""
 
-#: lib/choose_repository.tcl:789
+#: lib/choose_repository.tcl:795
 msgid "objects"
 msgstr ""
 
-#: lib/choose_repository.tcl:797
+#: lib/choose_repository.tcl:803
 #, tcl-format
 msgid "Unable to hardlink object: %s"
 msgstr ""
 
-#: lib/choose_repository.tcl:852
+#: lib/choose_repository.tcl:858
 msgid "Cannot fetch branches and objects.  See console output for details."
 msgstr ""
 
-#: lib/choose_repository.tcl:863
+#: lib/choose_repository.tcl:869
 msgid "Cannot fetch tags.  See console output for details."
 msgstr ""
 
-#: lib/choose_repository.tcl:887
+#: lib/choose_repository.tcl:893
 msgid "Cannot determine HEAD.  See console output for details."
 msgstr ""
 
-#: lib/choose_repository.tcl:896
+#: lib/choose_repository.tcl:902
 #, tcl-format
 msgid "Unable to cleanup %s"
 msgstr ""
 
-#: lib/choose_repository.tcl:902
+#: lib/choose_repository.tcl:908
 msgid "Clone failed."
 msgstr ""
 
-#: lib/choose_repository.tcl:909
+#: lib/choose_repository.tcl:915
 msgid "No default branch obtained."
 msgstr ""
 
-#: lib/choose_repository.tcl:920
+#: lib/choose_repository.tcl:926
 #, tcl-format
 msgid "Cannot resolve %s as a commit."
 msgstr ""
 
-#: lib/choose_repository.tcl:932
+#: lib/choose_repository.tcl:938
 msgid "Creating working directory"
 msgstr ""
 
-#: lib/choose_repository.tcl:933 lib/index.tcl:65 lib/index.tcl:128
-#: lib/index.tcl:196
+#: lib/choose_repository.tcl:939 lib/index.tcl:67 lib/index.tcl:130
+#: lib/index.tcl:198
 msgid "files"
 msgstr ""
 
-#: lib/choose_repository.tcl:962
+#: lib/choose_repository.tcl:968
 msgid "Initial file checkout failed."
 msgstr ""
 
-#: lib/choose_repository.tcl:978
+#: lib/choose_repository.tcl:1011
 msgid "Open"
 msgstr ""
 
-#: lib/choose_repository.tcl:988
+#: lib/choose_repository.tcl:1021
 msgid "Repository:"
 msgstr ""
 
-#: lib/choose_repository.tcl:1037
+#: lib/choose_repository.tcl:1072
 #, tcl-format
 msgid "Failed to open repository %s:"
 msgstr ""
@@ -1259,7 +1295,12 @@
 msgid "Invalid GIT_COMMITTER_IDENT:"
 msgstr ""
 
-#: lib/commit.tcl:132
+#: lib/commit.tcl:129
+#, tcl-format
+msgid "warning: Tcl does not support encoding '%s'."
+msgstr ""
+
+#: lib/commit.tcl:149
 msgid ""
 "Last scanned state does not match repository state.\n"
 "\n"
@@ -1269,7 +1310,7 @@
 "The rescan will be automatically started now.\n"
 msgstr ""
 
-#: lib/commit.tcl:155
+#: lib/commit.tcl:172
 #, tcl-format
 msgid ""
 "Unmerged files cannot be committed.\n"
@@ -1278,7 +1319,7 @@
 "before committing.\n"
 msgstr ""
 
-#: lib/commit.tcl:163
+#: lib/commit.tcl:180
 #, tcl-format
 msgid ""
 "Unknown file state %s detected.\n"
@@ -1286,14 +1327,14 @@
 "File %s cannot be committed by this program.\n"
 msgstr ""
 
-#: lib/commit.tcl:171
+#: lib/commit.tcl:188
 msgid ""
 "No changes to commit.\n"
 "\n"
 "You must stage at least 1 file before you can commit.\n"
 msgstr ""
 
-#: lib/commit.tcl:186
+#: lib/commit.tcl:203
 msgid ""
 "Please supply a commit message.\n"
 "\n"
@@ -1304,45 +1345,40 @@
 "- Remaining lines: Describe why this change is good.\n"
 msgstr ""
 
-#: lib/commit.tcl:210
-#, tcl-format
-msgid "warning: Tcl does not support encoding '%s'."
-msgstr ""
-
-#: lib/commit.tcl:226
+#: lib/commit.tcl:234
 msgid "Calling pre-commit hook..."
 msgstr ""
 
-#: lib/commit.tcl:241
+#: lib/commit.tcl:249
 msgid "Commit declined by pre-commit hook."
 msgstr ""
 
-#: lib/commit.tcl:264
+#: lib/commit.tcl:272
 msgid "Calling commit-msg hook..."
 msgstr ""
 
-#: lib/commit.tcl:279
+#: lib/commit.tcl:287
 msgid "Commit declined by commit-msg hook."
 msgstr ""
 
-#: lib/commit.tcl:292
+#: lib/commit.tcl:300
 msgid "Committing changes..."
 msgstr ""
 
-#: lib/commit.tcl:308
+#: lib/commit.tcl:316
 msgid "write-tree failed:"
 msgstr ""
 
-#: lib/commit.tcl:309 lib/commit.tcl:353 lib/commit.tcl:373
+#: lib/commit.tcl:317 lib/commit.tcl:361 lib/commit.tcl:382
 msgid "Commit failed."
 msgstr ""
 
-#: lib/commit.tcl:326
+#: lib/commit.tcl:334
 #, tcl-format
 msgid "Commit %s appears to be corrupt"
 msgstr ""
 
-#: lib/commit.tcl:331
+#: lib/commit.tcl:339
 msgid ""
 "No changes to commit.\n"
 "\n"
@@ -1351,19 +1387,19 @@
 "A rescan will be automatically started now.\n"
 msgstr ""
 
-#: lib/commit.tcl:338
+#: lib/commit.tcl:346
 msgid "No changes to commit."
 msgstr ""
 
-#: lib/commit.tcl:352
+#: lib/commit.tcl:360
 msgid "commit-tree failed:"
 msgstr ""
 
-#: lib/commit.tcl:372
+#: lib/commit.tcl:381
 msgid "update-ref failed:"
 msgstr ""
 
-#: lib/commit.tcl:460
+#: lib/commit.tcl:469
 #, tcl-format
 msgid "Created commit %s: %s"
 msgstr ""
@@ -1416,13 +1452,13 @@
 msgid "Verifying the object database with fsck-objects"
 msgstr ""
 
-#: lib/database.tcl:108
+#: lib/database.tcl:107
 #, tcl-format
 msgid ""
 "This repository currently has approximately %i loose objects.\n"
 "\n"
 "To maintain optimal performance it is strongly recommended that you compress "
-"the database when more than %i loose objects exist.\n"
+"the database.\n"
 "\n"
 "Compress the database now?"
 msgstr ""
@@ -1432,7 +1468,7 @@
 msgid "Invalid date from Git: %s"
 msgstr ""
 
-#: lib/diff.tcl:59
+#: lib/diff.tcl:64
 #, tcl-format
 msgid ""
 "No differences detected.\n"
@@ -1446,56 +1482,56 @@
 "the same state."
 msgstr ""
 
-#: lib/diff.tcl:99
+#: lib/diff.tcl:104
 #, tcl-format
 msgid "Loading diff of %s..."
 msgstr ""
 
-#: lib/diff.tcl:120
+#: lib/diff.tcl:125
 msgid ""
 "LOCAL: deleted\n"
 "REMOTE:\n"
 msgstr ""
 
-#: lib/diff.tcl:125
+#: lib/diff.tcl:130
 msgid ""
 "REMOTE: deleted\n"
 "LOCAL:\n"
 msgstr ""
 
-#: lib/diff.tcl:132
+#: lib/diff.tcl:137
 msgid "LOCAL:\n"
 msgstr ""
 
-#: lib/diff.tcl:135
+#: lib/diff.tcl:140
 msgid "REMOTE:\n"
 msgstr ""
 
-#: lib/diff.tcl:197 lib/diff.tcl:296
+#: lib/diff.tcl:202 lib/diff.tcl:319
 #, tcl-format
 msgid "Unable to display %s"
 msgstr ""
 
-#: lib/diff.tcl:198
+#: lib/diff.tcl:203
 msgid "Error loading file:"
 msgstr ""
 
-#: lib/diff.tcl:205
+#: lib/diff.tcl:210
 msgid "Git Repository (subproject)"
 msgstr ""
 
-#: lib/diff.tcl:217
+#: lib/diff.tcl:222
 msgid "* Binary file (not showing content)."
 msgstr ""
 
-#: lib/diff.tcl:222
+#: lib/diff.tcl:227
 #, tcl-format
 msgid ""
 "* Untracked file is %d bytes.\n"
 "* Showing only first %d bytes.\n"
 msgstr ""
 
-#: lib/diff.tcl:228
+#: lib/diff.tcl:233
 #, tcl-format
 msgid ""
 "\n"
@@ -1503,19 +1539,19 @@
 "* To see the entire file, use an external editor.\n"
 msgstr ""
 
-#: lib/diff.tcl:436
+#: lib/diff.tcl:482
 msgid "Failed to unstage selected hunk."
 msgstr ""
 
-#: lib/diff.tcl:443
+#: lib/diff.tcl:489
 msgid "Failed to stage selected hunk."
 msgstr ""
 
-#: lib/diff.tcl:509
+#: lib/diff.tcl:568
 msgid "Failed to unstage selected line."
 msgstr ""
 
-#: lib/diff.tcl:517
+#: lib/diff.tcl:576
 msgid "Failed to stage selected line."
 msgstr ""
 
@@ -1552,13 +1588,13 @@
 msgid "Index Error"
 msgstr ""
 
-#: lib/index.tcl:21
+#: lib/index.tcl:17
 msgid ""
 "Updating the Git index failed.  A rescan will be automatically started to "
 "resynchronize git-gui."
 msgstr ""
 
-#: lib/index.tcl:27
+#: lib/index.tcl:28
 msgid "Continue"
 msgstr ""
 
@@ -1566,43 +1602,43 @@
 msgid "Unlock Index"
 msgstr ""
 
-#: lib/index.tcl:287
+#: lib/index.tcl:289
 #, tcl-format
 msgid "Unstaging %s from commit"
 msgstr ""
 
-#: lib/index.tcl:326
+#: lib/index.tcl:328
 msgid "Ready to commit."
 msgstr ""
 
-#: lib/index.tcl:339
+#: lib/index.tcl:341
 #, tcl-format
 msgid "Adding %s"
 msgstr ""
 
-#: lib/index.tcl:396
+#: lib/index.tcl:398
 #, tcl-format
 msgid "Revert changes in file %s?"
 msgstr ""
 
-#: lib/index.tcl:398
+#: lib/index.tcl:400
 #, tcl-format
 msgid "Revert changes in these %i files?"
 msgstr ""
 
-#: lib/index.tcl:406
+#: lib/index.tcl:408
 msgid "Any unstaged changes will be permanently lost by the revert."
 msgstr ""
 
-#: lib/index.tcl:409
+#: lib/index.tcl:411
 msgid "Do Nothing"
 msgstr ""
 
-#: lib/index.tcl:427
+#: lib/index.tcl:429
 msgid "Reverting selected files"
 msgstr ""
 
-#: lib/index.tcl:431
+#: lib/index.tcl:433
 #, tcl-format
 msgid "Reverting %s"
 msgstr ""
@@ -2031,10 +2067,6 @@
 msgid "Merged Into:"
 msgstr ""
 
-#: lib/remote_branch_delete.tcl:119
-msgid "Always (Do not perform merge checks)"
-msgstr ""
-
 #: lib/remote_branch_delete.tcl:152
 msgid "A branch is required for 'Merged Into'."
 msgstr ""
@@ -2058,23 +2090,16 @@
 msgid "Please select one or more branches to delete."
 msgstr ""
 
-#: lib/remote_branch_delete.tcl:216
-msgid ""
-"Recovering deleted branches is difficult.\n"
-"\n"
-"Delete the selected branches?"
-msgstr ""
-
 #: lib/remote_branch_delete.tcl:226
 #, tcl-format
 msgid "Deleting branches from %s"
 msgstr ""
 
-#: lib/remote_branch_delete.tcl:286
+#: lib/remote_branch_delete.tcl:292
 msgid "No repository selected."
 msgstr ""
 
-#: lib/remote_branch_delete.tcl:291
+#: lib/remote_branch_delete.tcl:297
 #, tcl-format
 msgid "Scanning %s..."
 msgstr ""
@@ -2095,11 +2120,11 @@
 msgid "Case-Sensitive"
 msgstr ""
 
-#: lib/shortcut.tcl:20 lib/shortcut.tcl:61
+#: lib/shortcut.tcl:21 lib/shortcut.tcl:62
 msgid "Cannot write shortcut:"
 msgstr ""
 
-#: lib/shortcut.tcl:136
+#: lib/shortcut.tcl:137
 msgid "Cannot write icon:"
 msgstr ""
 
diff --git a/git-gui/po/glossary/el.po b/git-gui/po/glossary/el.po
new file mode 100644
index 0000000..1d3cc81
--- /dev/null
+++ b/git-gui/po/glossary/el.po
@@ -0,0 +1,171 @@
+# Translation of git-gui glossary to Greek
+# Copyright (C) 2009 Jimmy Angelakos
+# This file is distributed under the same license as the git-gui package.
+# Jimmy Angelakos <vyruss@hellug.gr>, 2009.
+msgid ""
+msgstr ""
+"Project-Id-Version: git-gui-glossary\n"
+"POT-Creation-Date: 2008-01-07 21:20+0100\n"
+"PO-Revision-Date: 2009-06-23 20:41+0300\n"
+"Last-Translator: Jimmy Angelakos <vyruss@hellug.gr>\n"
+"Language-Team: Greek <i18n@lists.hellug.gr>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Lokalize 0.3\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#. "English Definition (Dear translator: This file will never be visible to the user! It should only serve as a tool for you, the translator. Nothing more.)"
+msgid "English Term (Dear translator: This file will never be visible to the user!)"
+msgstr ""
+
+#. ""
+msgid "amend"
+msgstr "διόρθωση"
+
+#. ""
+msgid "annotate"
+msgstr "σχολιασμός"
+
+#. "A 'branch' is an active line of development."
+msgid "branch [noun]"
+msgstr "κλάδος [αντικείμενο]"
+
+#. ""
+msgid "branch [verb]"
+msgstr "διακλάδωση [ενέργεια]"
+
+#. ""
+msgid "checkout [noun]"
+msgstr "εξαγωγή [αντικείμενο]"
+
+#. "The action of updating the working tree to a revision which was stored in the object database."
+msgid "checkout [verb]"
+msgstr "εξαγωγή [ενέργεια]"
+
+#. ""
+msgid "clone [verb]"
+msgstr "κλωνοποίηση [ενέργεια]"
+
+#. "A single point in the git history."
+msgid "commit [noun]"
+msgstr "υποβολή [αντικείμενο] "
+
+#. "The action of storing a new snapshot of the project's state in the git history."
+msgid "commit [verb]"
+msgstr "υποβολή [ενέργεια]"
+
+#. ""
+msgid "diff [noun]"
+msgstr "διαφορά [αντικείμενο] "
+
+#. ""
+msgid "diff [verb]"
+msgstr "διαφορά [ενέργεια]"
+
+#. "A fast-forward is a special type of merge where you have a revision and you are merging another branch's changes that happen to be a descendant of what you have."
+msgid "fast forward merge"
+msgstr "συγχώνευση επιτάχυνσης"
+
+#. "Fetching a branch means to get the branch's head from a remote repository, to find out which objects are missing from the local object database, and to get them, too."
+msgid "fetch"
+msgstr "ανάκτηση"
+
+#. "One context of consecutive lines in a whole patch, which consists of many such hunks"
+msgid "hunk"
+msgstr "κομμάτι"
+
+#. "A collection of files. The index is a stored version of your working tree."
+msgid "index (in git-gui: staging area)"
+msgstr "ευρετήριο (στο git-gui: περιοχή σταδιοποίησης)"
+
+#. "A successful merge results in the creation of a new commit representing the result of the merge."
+msgid "merge [noun]"
+msgstr "συγχώνευση [αντικείμενο]"
+
+#. "To bring the contents of another branch into the current branch."
+msgid "merge [verb]"
+msgstr "συγχώνευση [ενέργεια]"
+
+#. ""
+msgid "message"
+msgstr "μήνυμα"
+
+#. "Deletes all stale tracking branches under <name>. These stale branches have already been removed from the remote repository referenced by <name>, but are still locally available in 'remotes/<name>'."
+msgid "prune"
+msgstr "κλάδεμα"
+
+#. "Pulling a branch means to fetch it and merge it."
+msgid "pull"
+msgstr "λήψη"
+
+#. "Pushing a branch means to get the branch's head ref from a remote repository, and ... (well, can someone please explain it for mere mortals?)"
+msgid "push"
+msgstr "ώθηση"
+
+#. ""
+msgid "redo"
+msgstr "ξανά"
+
+#. "An other repository ('remote'). One might have a set of remotes whose branches one tracks."
+msgid "remote"
+msgstr "απομακρυσμένο"
+
+#. "A collection of refs (?) together with an object database containing all objects which are reachable from the refs... (oops, you've lost me here. Again, please an explanation for mere mortals?)"
+msgid "repository"
+msgstr "αποθετήριο"
+
+#. ""
+msgid "reset"
+msgstr "επαναφορά"
+
+#. ""
+msgid "revert"
+msgstr "αναίρεση"
+
+#. "A particular state of files and directories which was stored in the object database."
+msgid "revision"
+msgstr "αναθεώρηση"
+
+#. ""
+#, fuzzy
+msgid "sign off"
+msgstr "αποσύνδεση"
+
+#. ""
+msgid "staging area"
+msgstr "περιοχή σταδιοποίησης"
+
+#. ""
+msgid "status"
+msgstr "κατάσταση"
+
+#. "A ref pointing to a tag or commit object"
+msgid "tag [noun]"
+msgstr "ετικέτα [αντικείμενο]"
+
+#. ""
+msgid "tag [verb]"
+msgstr "ετικέτα [ενέργεια]"
+
+#. "A regular git branch that is used to follow changes from another repository."
+msgid "tracking branch"
+msgstr "κλάδος παρακολούθησης"
+
+#. ""
+msgid "undo"
+msgstr "αναίρεση"
+
+#. ""
+msgid "update"
+msgstr "ενημέρωση"
+
+#. ""
+msgid "verify"
+msgstr "επαλήθευση"
+
+#. "The tree of actual checked out files."
+msgid "working copy, working tree"
+msgstr "αντίγραφο εργασίας"
+
+
diff --git a/git-gui/po/glossary/pt_br.po b/git-gui/po/glossary/pt_br.po
new file mode 100644
index 0000000..eb039b2
--- /dev/null
+++ b/git-gui/po/glossary/pt_br.po
@@ -0,0 +1,169 @@
+# Translation of git-gui to Brazilian Portuguese
+# Copyright (C) 2007 Shawn Pearce, et al.
+# This file is distributed under the same license as the git-gui package.
+#
+# Alexandre Erwin Ittner <alexandre@ittner.com.br>, 2010.
+msgid ""
+msgstr ""
+"Project-Id-Version: git-gui\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-01-26 15:47-0800\n"
+"PO-Revision-Date: 2010-09-18 11:09-0300\n"
+"Last-Translator: Alexandre Erwin Ittner <alexandre@ittner.com.br>\n"
+"Language-Team: Brazilian Portuguese <>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#. "English Definition (Dear translator: This file will never be visible to the user! It should only serve as a tool for you, the translator. Nothing more.)"
+msgid ""
+"English Term (Dear translator: This file will never be visible to the user!)"
+msgstr ""
+
+#. ""
+msgid "amend"
+msgstr "corrigir"
+
+#. ""
+msgid "annotate"
+msgstr "anotar"
+
+#. "A 'branch' is an active line of development."
+msgid "branch [noun]"
+msgstr "ramo"
+
+#. ""
+msgid "branch [verb]"
+msgstr "ramificar"
+
+#. ""
+msgid "checkout [noun]"
+msgstr "checkout"
+
+#. "The action of updating the working tree to a revision which was stored in the object database."
+msgid "checkout [verb]"
+msgstr "efetuar checkout"
+
+#. ""
+msgid "clone [verb]"
+msgstr "clonar"
+
+#. "A single point in the git history."
+msgid "commit [noun]"
+msgstr "revisão"
+
+#. "The action of storing a new snapshot of the project's state in the git history."
+msgid "commit [verb]"
+msgstr "salvar revisão"
+
+#. ""
+msgid "diff [noun]"
+msgstr "diff"
+
+#. ""
+msgid "diff [verb]"
+msgstr "comparar"
+
+#. "A fast-forward is a special type of merge where you have a revision and you are merging another branch's changes that happen to be a descendant of what you have."
+msgid "fast forward merge"
+msgstr "mesclagem rápida"
+
+#. "Fetching a branch means to get the branch's head from a remote repository, to find out which objects are missing from the local object database, and to get them, too."
+msgid "fetch"
+msgstr "receber"
+
+#. "One context of consecutive lines in a whole patch, which consists of many such hunks"
+msgid "hunk"
+msgstr "trecho"
+
+#. "A collection of files. The index is a stored version of your working tree."
+msgid "index (in git-gui: staging area)"
+msgstr "índice"
+
+#. "A successful merge results in the creation of a new commit representing the result of the merge."
+msgid "merge [noun]"
+msgstr "mesclagem"
+
+#. "To bring the contents of another branch into the current branch."
+msgid "merge [verb]"
+msgstr "mesclar"
+
+#. ""
+msgid "message"
+msgstr "descrição da revisão"
+
+#. "Deletes all stale tracking branches under <name>. These stale branches have already been removed from the remote repository referenced by <name>, but are still locally available in 'remotes/<name>'."
+msgid "prune"
+msgstr "limpar"
+
+#. "Pulling a branch means to fetch it and merge it."
+msgid "pull"
+msgstr "receber e mesclar"
+
+#. "Pushing a branch means to get the branch's head ref from a remote repository, and ... (well, can someone please explain it for mere mortals?)"
+msgid "push"
+msgstr "enviar"
+
+#. ""
+msgid "redo"
+msgstr "refazer"
+
+#. "An other repository ('remote'). One might have a set of remotes whose branches one tracks."
+msgid "remote"
+msgstr "repositório remoto"
+
+#. "A collection of refs (?) together with an object database containing all objects which are reachable from the refs... (oops, you've lost me here. Again, please an explanation for mere mortals?)"
+msgid "repository"
+msgstr "repositório"
+
+#. ""
+msgid "reset"
+msgstr "descartar, redefinir"
+
+#. ""
+msgid "revert"
+msgstr "reverter"
+
+#. "A particular state of files and directories which was stored in the object database."
+msgid "revision"
+msgstr "revisão"
+
+#. ""
+msgid "sign off"
+msgstr "assinar embaixo"
+
+#. ""
+msgid "staging area"
+msgstr "???"
+
+#. ""
+msgid "status"
+msgstr "status"
+
+#. "A ref pointing to a tag or commit object"
+msgid "tag [noun]"
+msgstr "etiqueta"
+
+#. ""
+msgid "tag [verb]"
+msgstr "marcar etiqueta"
+
+#. "A regular git branch that is used to follow changes from another repository."
+msgid "tracking branch"
+msgstr "ramo de rastreamento"
+
+#. ""
+msgid "undo"
+msgstr "desfazer"
+
+#. ""
+msgid "update"
+msgstr "atualizar"
+
+#. ""
+msgid "verify"
+msgstr "verificar"
+
+#. "The tree of actual checked out files."
+msgid "working copy, working tree"
+msgstr "cópia de trabalho, árvore de trabalho"
diff --git a/git-gui/po/it.po b/git-gui/po/it.po
index 762632c..aa15a20 100644
--- a/git-gui/po/it.po
+++ b/git-gui/po/it.po
@@ -9,41 +9,41 @@
 msgstr ""
 "Project-Id-Version: git-gui\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-12-08 08:31-0800\n"
-"PO-Revision-Date: 2008-12-09 13:04+0100\n"
+"POT-Creation-Date: 2010-01-26 15:47-0800\n"
+"PO-Revision-Date: 2010-01-28 10:04+0100\n"
 "Last-Translator: Michele Ballabio <barra_cuda@katamail.com>\n"
 "Language-Team: Italian <tp@lists.linux.it>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: git-gui.sh:41 git-gui.sh:737 git-gui.sh:751 git-gui.sh:764 git-gui.sh:847
-#: git-gui.sh:866
+#: git-gui.sh:41 git-gui.sh:793 git-gui.sh:807 git-gui.sh:820 git-gui.sh:903
+#: git-gui.sh:922
 msgid "git-gui: fatal error"
 msgstr "git-gui: errore grave"
 
-#: git-gui.sh:689
+#: git-gui.sh:743
 #, tcl-format
 msgid "Invalid font specified in %s:"
 msgstr "Caratteri non validi specificati in %s:"
 
-#: git-gui.sh:723
+#: git-gui.sh:779
 msgid "Main Font"
 msgstr "Caratteri principali"
 
-#: git-gui.sh:724
+#: git-gui.sh:780
 msgid "Diff/Console Font"
 msgstr "Caratteri per confronti e terminale"
 
-#: git-gui.sh:738
+#: git-gui.sh:794
 msgid "Cannot find git in PATH."
 msgstr "Impossibile trovare git nel PATH"
 
-#: git-gui.sh:765
+#: git-gui.sh:821
 msgid "Cannot parse Git version string:"
 msgstr "Impossibile determinare la versione di Git:"
 
-#: git-gui.sh:783
+#: git-gui.sh:839
 #, tcl-format
 msgid ""
 "Git version cannot be determined.\n"
@@ -62,446 +62,475 @@
 "\n"
 "Assumere che '%s' sia alla versione 1.5.0?\n"
 
-#: git-gui.sh:1062
+#: git-gui.sh:1128
 msgid "Git directory not found:"
 msgstr "Non trovo la directory di git: "
 
-#: git-gui.sh:1069
+#: git-gui.sh:1146
 msgid "Cannot move to top of working directory:"
 msgstr "Impossibile spostarsi sulla directory principale del progetto:"
 
-#: git-gui.sh:1076
-msgid "Cannot use funny .git directory:"
-msgstr "Impossibile usare una .git directory strana:"
+#: git-gui.sh:1154
+msgid "Cannot use bare repository:"
+msgstr "Impossibile usare un archivio senza directory di lavoro:"
 
-#: git-gui.sh:1081
+#: git-gui.sh:1162
 msgid "No working directory"
 msgstr "Nessuna directory di lavoro"
 
-#: git-gui.sh:1247 lib/checkout_op.tcl:305
+#: git-gui.sh:1334 lib/checkout_op.tcl:306
 msgid "Refreshing file status..."
 msgstr "Controllo dello stato dei file in corso..."
 
-#: git-gui.sh:1303
+#: git-gui.sh:1390
 msgid "Scanning for modified files ..."
 msgstr "Ricerca di file modificati in corso..."
 
-#: git-gui.sh:1367
+#: git-gui.sh:1454
 msgid "Calling prepare-commit-msg hook..."
 msgstr "Avvio prepare-commit-msg hook..."
 
-#: git-gui.sh:1384
+#: git-gui.sh:1471
 msgid "Commit declined by prepare-commit-msg hook."
 msgstr "Revisione rifiutata dal prepare-commit-msg hook."
 
-#: git-gui.sh:1542 lib/browser.tcl:246
+#: git-gui.sh:1629 lib/browser.tcl:246
 msgid "Ready."
 msgstr "Pronto."
 
-#: git-gui.sh:1819
+#: git-gui.sh:1787
+#, tcl-format
+msgid "Displaying only %s of %s files."
+msgstr "Saranno mostrati solo %s file su %s."
+
+#: git-gui.sh:1913
 msgid "Unmodified"
 msgstr "Non modificato"
 
-#: git-gui.sh:1821
+#: git-gui.sh:1915
 msgid "Modified, not staged"
 msgstr "Modificato, non preparato per una nuova revisione"
 
-#: git-gui.sh:1822 git-gui.sh:1830
+#: git-gui.sh:1916 git-gui.sh:1924
 msgid "Staged for commit"
 msgstr "Preparato per una nuova revisione"
 
-#: git-gui.sh:1823 git-gui.sh:1831
+#: git-gui.sh:1917 git-gui.sh:1925
 msgid "Portions staged for commit"
 msgstr "Parti preparate per una nuova revisione"
 
-#: git-gui.sh:1824 git-gui.sh:1832
+#: git-gui.sh:1918 git-gui.sh:1926
 msgid "Staged for commit, missing"
 msgstr "Preparato per una nuova revisione, mancante"
 
-#: git-gui.sh:1826
+#: git-gui.sh:1920
 msgid "File type changed, not staged"
 msgstr "Tipo di file modificato, non preparato per una nuova revisione"
 
-#: git-gui.sh:1827
+#: git-gui.sh:1921
 msgid "File type changed, staged"
 msgstr "Tipo di file modificato, preparato per una nuova revisione"
 
-#: git-gui.sh:1829
+#: git-gui.sh:1923
 msgid "Untracked, not staged"
 msgstr "Non tracciato, non preparato per una nuova revisione"
 
-#: git-gui.sh:1834
+#: git-gui.sh:1928
 msgid "Missing"
 msgstr "Mancante"
 
-#: git-gui.sh:1835
+#: git-gui.sh:1929
 msgid "Staged for removal"
 msgstr "Preparato per la rimozione"
 
-#: git-gui.sh:1836
+#: git-gui.sh:1930
 msgid "Staged for removal, still present"
 msgstr "Preparato alla rimozione, ancora presente"
 
-#: git-gui.sh:1838 git-gui.sh:1839 git-gui.sh:1840 git-gui.sh:1841
-#: git-gui.sh:1842 git-gui.sh:1843
+#: git-gui.sh:1932 git-gui.sh:1933 git-gui.sh:1934 git-gui.sh:1935
+#: git-gui.sh:1936 git-gui.sh:1937
 msgid "Requires merge resolution"
 msgstr "Richiede risoluzione dei conflitti"
 
-#: git-gui.sh:1878
+#: git-gui.sh:1972
 msgid "Starting gitk... please wait..."
 msgstr "Avvio di gitk... attendere..."
 
-#: git-gui.sh:1887
+#: git-gui.sh:1984
 msgid "Couldn't find gitk in PATH"
 msgstr "Impossibile trovare gitk nel PATH"
 
-#: git-gui.sh:2280 lib/choose_repository.tcl:36
+#: git-gui.sh:2043
+msgid "Couldn't find git gui in PATH"
+msgstr "Impossibile trovare git gui nel PATH"
+
+#: git-gui.sh:2455 lib/choose_repository.tcl:36
 msgid "Repository"
 msgstr "Archivio"
 
-#: git-gui.sh:2281
+#: git-gui.sh:2456
 msgid "Edit"
 msgstr "Modifica"
 
-#: git-gui.sh:2283 lib/choose_rev.tcl:561
+#: git-gui.sh:2458 lib/choose_rev.tcl:561
 msgid "Branch"
 msgstr "Ramo"
 
-#: git-gui.sh:2286 lib/choose_rev.tcl:548
+#: git-gui.sh:2461 lib/choose_rev.tcl:548
 msgid "Commit@@noun"
 msgstr "Revisione"
 
-#: git-gui.sh:2289 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
+#: git-gui.sh:2464 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
 msgid "Merge"
 msgstr "Fusione (Merge)"
 
-#: git-gui.sh:2290 lib/choose_rev.tcl:557
+#: git-gui.sh:2465 lib/choose_rev.tcl:557
 msgid "Remote"
 msgstr "Remoto"
 
-#: git-gui.sh:2293
+#: git-gui.sh:2468
 msgid "Tools"
-msgstr "Strumenti"
+msgstr "Accessori"
 
-#: git-gui.sh:2302
+#: git-gui.sh:2477
 msgid "Explore Working Copy"
 msgstr "Esplora copia di lavoro"
 
-#: git-gui.sh:2307
+#: git-gui.sh:2483
 msgid "Browse Current Branch's Files"
 msgstr "Esplora i file del ramo attuale"
 
-#: git-gui.sh:2311
+#: git-gui.sh:2487
 msgid "Browse Branch Files..."
 msgstr "Esplora i file del ramo..."
 
-#: git-gui.sh:2316
+#: git-gui.sh:2492
 msgid "Visualize Current Branch's History"
 msgstr "Visualizza la cronologia del ramo attuale"
 
-#: git-gui.sh:2320
+#: git-gui.sh:2496
 msgid "Visualize All Branch History"
 msgstr "Visualizza la cronologia di tutti i rami"
 
-#: git-gui.sh:2327
+#: git-gui.sh:2503
 #, tcl-format
 msgid "Browse %s's Files"
 msgstr "Esplora i file di %s"
 
-#: git-gui.sh:2329
+#: git-gui.sh:2505
 #, tcl-format
 msgid "Visualize %s's History"
 msgstr "Visualizza la cronologia di %s"
 
-#: git-gui.sh:2334 lib/database.tcl:27 lib/database.tcl:67
+#: git-gui.sh:2510 lib/database.tcl:27 lib/database.tcl:67
 msgid "Database Statistics"
 msgstr "Statistiche dell'archivio"
 
-#: git-gui.sh:2337 lib/database.tcl:34
+#: git-gui.sh:2513 lib/database.tcl:34
 msgid "Compress Database"
 msgstr "Comprimi l'archivio"
 
-#: git-gui.sh:2340
+#: git-gui.sh:2516
 msgid "Verify Database"
 msgstr "Verifica l'archivio"
 
-#: git-gui.sh:2347 git-gui.sh:2351 git-gui.sh:2355 lib/shortcut.tcl:7
-#: lib/shortcut.tcl:39 lib/shortcut.tcl:71
+#: git-gui.sh:2523 git-gui.sh:2527 git-gui.sh:2531 lib/shortcut.tcl:8
+#: lib/shortcut.tcl:40 lib/shortcut.tcl:72
 msgid "Create Desktop Icon"
 msgstr "Crea icona desktop"
 
-#: git-gui.sh:2363 lib/choose_repository.tcl:183 lib/choose_repository.tcl:191
+#: git-gui.sh:2539 lib/choose_repository.tcl:183 lib/choose_repository.tcl:191
 msgid "Quit"
 msgstr "Esci"
 
-#: git-gui.sh:2371
+#: git-gui.sh:2547
 msgid "Undo"
 msgstr "Annulla"
 
-#: git-gui.sh:2374
+#: git-gui.sh:2550
 msgid "Redo"
 msgstr "Ripeti"
 
-#: git-gui.sh:2378 git-gui.sh:2937
+#: git-gui.sh:2554 git-gui.sh:3109
 msgid "Cut"
 msgstr "Taglia"
 
-#: git-gui.sh:2381 git-gui.sh:2940 git-gui.sh:3014 git-gui.sh:3096
+#: git-gui.sh:2557 git-gui.sh:3112 git-gui.sh:3186 git-gui.sh:3259
 #: lib/console.tcl:69
 msgid "Copy"
 msgstr "Copia"
 
-#: git-gui.sh:2384 git-gui.sh:2943
+#: git-gui.sh:2560 git-gui.sh:3115
 msgid "Paste"
 msgstr "Incolla"
 
-#: git-gui.sh:2387 git-gui.sh:2946 lib/branch_delete.tcl:26
+#: git-gui.sh:2563 git-gui.sh:3118 lib/branch_delete.tcl:26
 #: lib/remote_branch_delete.tcl:38
 msgid "Delete"
 msgstr "Elimina"
 
-#: git-gui.sh:2391 git-gui.sh:2950 git-gui.sh:3100 lib/console.tcl:71
+#: git-gui.sh:2567 git-gui.sh:3122 git-gui.sh:3263 lib/console.tcl:71
 msgid "Select All"
 msgstr "Seleziona tutto"
 
-#: git-gui.sh:2400
+#: git-gui.sh:2576
 msgid "Create..."
 msgstr "Crea..."
 
-#: git-gui.sh:2406
+#: git-gui.sh:2582
 msgid "Checkout..."
 msgstr "Attiva..."
 
-#: git-gui.sh:2412
+#: git-gui.sh:2588
 msgid "Rename..."
 msgstr "Rinomina"
 
-#: git-gui.sh:2417
+#: git-gui.sh:2593
 msgid "Delete..."
 msgstr "Elimina..."
 
-#: git-gui.sh:2422
+#: git-gui.sh:2598
 msgid "Reset..."
 msgstr "Ripristina..."
 
-#: git-gui.sh:2432
+#: git-gui.sh:2608
 msgid "Done"
 msgstr "Fatto"
 
-#: git-gui.sh:2434
+#: git-gui.sh:2610
 msgid "Commit@@verb"
 msgstr "Nuova revisione"
 
-#: git-gui.sh:2443 git-gui.sh:2878
+#: git-gui.sh:2619 git-gui.sh:3050
 msgid "New Commit"
 msgstr "Nuova revisione"
 
-#: git-gui.sh:2451 git-gui.sh:2885
+#: git-gui.sh:2627 git-gui.sh:3057
 msgid "Amend Last Commit"
 msgstr "Correggi l'ultima revisione"
 
-#: git-gui.sh:2461 git-gui.sh:2839 lib/remote_branch_delete.tcl:99
+#: git-gui.sh:2637 git-gui.sh:3011 lib/remote_branch_delete.tcl:99
 msgid "Rescan"
 msgstr "Analizza nuovamente"
 
-#: git-gui.sh:2467
+#: git-gui.sh:2643
 msgid "Stage To Commit"
 msgstr "Prepara per una nuova revisione"
 
-#: git-gui.sh:2473
+#: git-gui.sh:2649
 msgid "Stage Changed Files To Commit"
 msgstr "Prepara i file modificati per una nuova revisione"
 
-#: git-gui.sh:2479
+#: git-gui.sh:2655
 msgid "Unstage From Commit"
 msgstr "Annulla preparazione"
 
-#: git-gui.sh:2484 lib/index.tcl:410
+#: git-gui.sh:2661 lib/index.tcl:412
 msgid "Revert Changes"
 msgstr "Annulla modifiche"
 
-#: git-gui.sh:2491 git-gui.sh:3083
+#: git-gui.sh:2669 git-gui.sh:3310 git-gui.sh:3341
 msgid "Show Less Context"
 msgstr "Mostra meno contesto"
 
-#: git-gui.sh:2495 git-gui.sh:3087
+#: git-gui.sh:2673 git-gui.sh:3314 git-gui.sh:3345
 msgid "Show More Context"
 msgstr "Mostra più contesto"
 
-#: git-gui.sh:2502 git-gui.sh:2852 git-gui.sh:2961
+#: git-gui.sh:2680 git-gui.sh:3024 git-gui.sh:3133
 msgid "Sign Off"
 msgstr "Sign Off"
 
-#: git-gui.sh:2518
+#: git-gui.sh:2696
 msgid "Local Merge..."
 msgstr "Fusione locale..."
 
-#: git-gui.sh:2523
+#: git-gui.sh:2701
 msgid "Abort Merge..."
 msgstr "Interrompi fusione..."
 
-#: git-gui.sh:2535 git-gui.sh:2575
+#: git-gui.sh:2713 git-gui.sh:2741
 msgid "Add..."
 msgstr "Aggiungi..."
 
-#: git-gui.sh:2539
+#: git-gui.sh:2717
 msgid "Push..."
 msgstr "Propaga..."
 
-#: git-gui.sh:2543
+#: git-gui.sh:2721
 msgid "Delete Branch..."
 msgstr "Elimina ramo..."
 
-#: git-gui.sh:2553 git-gui.sh:2589 lib/about.tcl:14
+#: git-gui.sh:2731 git-gui.sh:3292
+msgid "Options..."
+msgstr "Opzioni..."
+
+#: git-gui.sh:2742
+msgid "Remove..."
+msgstr "Rimuovi..."
+
+#: git-gui.sh:2751 lib/choose_repository.tcl:50
+msgid "Help"
+msgstr "Aiuto"
+
+#: git-gui.sh:2755 git-gui.sh:2759 lib/about.tcl:14
 #: lib/choose_repository.tcl:44 lib/choose_repository.tcl:53
 #, tcl-format
 msgid "About %s"
 msgstr "Informazioni su %s"
 
-#: git-gui.sh:2557
-msgid "Preferences..."
-msgstr "Preferenze..."
-
-#: git-gui.sh:2565 git-gui.sh:3129
-msgid "Options..."
-msgstr "Opzioni..."
-
-#: git-gui.sh:2576
-msgid "Remove..."
-msgstr "Rimuovi..."
-
-#: git-gui.sh:2585 lib/choose_repository.tcl:50
-msgid "Help"
-msgstr "Aiuto"
-
-#: git-gui.sh:2611
+#: git-gui.sh:2783
 msgid "Online Documentation"
 msgstr "Documentazione sul web"
 
-#: git-gui.sh:2614 lib/choose_repository.tcl:47 lib/choose_repository.tcl:56
+#: git-gui.sh:2786 lib/choose_repository.tcl:47 lib/choose_repository.tcl:56
 msgid "Show SSH Key"
 msgstr "Mostra chave SSH"
 
-#: git-gui.sh:2721
+#: git-gui.sh:2893
 #, tcl-format
 msgid "fatal: cannot stat path %s: No such file or directory"
 msgstr ""
 "errore grave: impossibile effettuare lo stat del path %s: file o directory "
 "non trovata"
 
-#: git-gui.sh:2754
+#: git-gui.sh:2926
 msgid "Current Branch:"
 msgstr "Ramo attuale:"
 
-#: git-gui.sh:2775
+#: git-gui.sh:2947
 msgid "Staged Changes (Will Commit)"
 msgstr "Modifiche preparate (saranno nella nuova revisione)"
 
-#: git-gui.sh:2795
+#: git-gui.sh:2967
 msgid "Unstaged Changes"
 msgstr "Modifiche non preparate"
 
-#: git-gui.sh:2845
+#: git-gui.sh:3017
 msgid "Stage Changed"
 msgstr "Prepara modificati"
 
-#: git-gui.sh:2864 lib/transport.tcl:104 lib/transport.tcl:193
+#: git-gui.sh:3036 lib/transport.tcl:104 lib/transport.tcl:193
 msgid "Push"
 msgstr "Propaga (Push)"
 
-#: git-gui.sh:2899
+#: git-gui.sh:3071
 msgid "Initial Commit Message:"
 msgstr "Messaggio di revisione iniziale:"
 
-#: git-gui.sh:2900
+#: git-gui.sh:3072
 msgid "Amended Commit Message:"
 msgstr "Messaggio di revisione corretto:"
 
-#: git-gui.sh:2901
+#: git-gui.sh:3073
 msgid "Amended Initial Commit Message:"
 msgstr "Messaggio iniziale di revisione corretto:"
 
-#: git-gui.sh:2902
+#: git-gui.sh:3074
 msgid "Amended Merge Commit Message:"
 msgstr "Messaggio di fusione corretto:"
 
-#: git-gui.sh:2903
+#: git-gui.sh:3075
 msgid "Merge Commit Message:"
 msgstr "Messaggio di fusione:"
 
-#: git-gui.sh:2904
+#: git-gui.sh:3076
 msgid "Commit Message:"
 msgstr "Messaggio di revisione:"
 
-#: git-gui.sh:2953 git-gui.sh:3104 lib/console.tcl:73
+#: git-gui.sh:3125 git-gui.sh:3267 lib/console.tcl:73
 msgid "Copy All"
 msgstr "Copia tutto"
 
-#: git-gui.sh:2977 lib/blame.tcl:104
+#: git-gui.sh:3149 lib/blame.tcl:104
 msgid "File:"
 msgstr "File:"
 
-#: git-gui.sh:3092
+#: git-gui.sh:3255
 msgid "Refresh"
 msgstr "Rinfresca"
 
-#: git-gui.sh:3113
+#: git-gui.sh:3276
 msgid "Decrease Font Size"
 msgstr "Diminuisci dimensione caratteri"
 
-#: git-gui.sh:3117
+#: git-gui.sh:3280
 msgid "Increase Font Size"
 msgstr "Aumenta dimensione caratteri"
 
-#: git-gui.sh:3125 lib/blame.tcl:281
+#: git-gui.sh:3288 lib/blame.tcl:281
 msgid "Encoding"
 msgstr "Codifica"
 
-#: git-gui.sh:3136
+#: git-gui.sh:3299
 msgid "Apply/Reverse Hunk"
 msgstr "Applica/Inverti sezione"
 
-#: git-gui.sh:3141
+#: git-gui.sh:3304
 msgid "Apply/Reverse Line"
 msgstr "Applica/Inverti riga"
 
-#: git-gui.sh:3151
+#: git-gui.sh:3323
 msgid "Run Merge Tool"
 msgstr "Avvia programma esterno per la risoluzione dei conflitti"
 
-#: git-gui.sh:3156
+#: git-gui.sh:3328
 msgid "Use Remote Version"
 msgstr "Usa versione remota"
 
-#: git-gui.sh:3160
+#: git-gui.sh:3332
 msgid "Use Local Version"
 msgstr "Usa versione locale"
 
-#: git-gui.sh:3164
+#: git-gui.sh:3336
 msgid "Revert To Base"
 msgstr "Ritorna alla revisione comune"
 
-#: git-gui.sh:3183
+#: git-gui.sh:3354
+msgid "Visualize These Changes In The Submodule"
+msgstr "Visualizza queste modifiche nel sottomodulo"
+
+#: git-gui.sh:3358
+msgid "Visualize Current Branch History In The Submodule"
+msgstr "Visualizza la cronologia del ramo attuale nel sottomodulo"
+
+#: git-gui.sh:3362
+msgid "Visualize All Branch History In The Submodule"
+msgstr "Visualizza la cronologia di tutti i rami nel sottomodulo"
+
+#: git-gui.sh:3367
+msgid "Start git gui In The Submodule"
+msgstr "Avvia git gui nel sottomodulo"
+
+#: git-gui.sh:3389
 msgid "Unstage Hunk From Commit"
 msgstr "Annulla preparazione della sezione per una nuova revisione"
 
-#: git-gui.sh:3184
+#: git-gui.sh:3391
+msgid "Unstage Lines From Commit"
+msgstr "Annulla preparazione delle linee per una nuova revisione"
+
+#: git-gui.sh:3393
 msgid "Unstage Line From Commit"
 msgstr "Annulla preparazione della linea per una nuova revisione"
 
-#: git-gui.sh:3186
+#: git-gui.sh:3396
 msgid "Stage Hunk For Commit"
 msgstr "Prepara sezione per una nuova revisione"
 
-#: git-gui.sh:3187
+#: git-gui.sh:3398
+msgid "Stage Lines For Commit"
+msgstr "Prepara linee per una nuova revisione"
+
+#: git-gui.sh:3400
 msgid "Stage Line For Commit"
 msgstr "Prepara linea per una nuova revisione"
 
-#: git-gui.sh:3210
+#: git-gui.sh:3424
 msgid "Initializing..."
 msgstr "Inizializzazione..."
 
-#: git-gui.sh:3315
+#: git-gui.sh:3541
 #, tcl-format
 msgid ""
 "Possible environment issues exist.\n"
@@ -518,7 +547,7 @@
 "da %s:\n"
 "\n"
 
-#: git-gui.sh:3345
+#: git-gui.sh:3570
 msgid ""
 "\n"
 "This is due to a known issue with the\n"
@@ -528,7 +557,7 @@
 "Ciò è dovuto a un problema conosciuto\n"
 "causato dall'eseguibile Tcl distribuito da Cygwin."
 
-#: git-gui.sh:3350
+#: git-gui.sh:3575
 #, tcl-format
 msgid ""
 "\n"
@@ -637,7 +666,7 @@
 msgid "Unable to display parent"
 msgstr "Impossibile visualizzare la revisione precedente"
 
-#: lib/blame.tcl:1091 lib/diff.tcl:297
+#: lib/blame.tcl:1091 lib/diff.tcl:320
 msgid "Error loading diff:"
 msgstr "Errore nel caricamento delle differenze:"
 
@@ -663,7 +692,7 @@
 
 #: lib/branch_checkout.tcl:27 lib/branch_create.tcl:35
 #: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:282
-#: lib/checkout_op.tcl:544 lib/choose_font.tcl:43 lib/merge.tcl:172
+#: lib/checkout_op.tcl:579 lib/choose_font.tcl:43 lib/merge.tcl:172
 #: lib/option.tcl:125 lib/remote_add.tcl:32 lib/remote_branch_delete.tcl:42
 #: lib/tools_dlg.tcl:40 lib/tools_dlg.tcl:204 lib/tools_dlg.tcl:352
 #: lib/transport.tcl:108
@@ -694,7 +723,7 @@
 msgid "Create New Branch"
 msgstr "Crea nuovo ramo"
 
-#: lib/branch_create.tcl:31 lib/choose_repository.tcl:377
+#: lib/branch_create.tcl:31 lib/choose_repository.tcl:381
 msgid "Create"
 msgstr "Crea"
 
@@ -726,7 +755,7 @@
 msgid "Fast Forward Only"
 msgstr "Solo fast forward"
 
-#: lib/branch_create.tcl:85 lib/checkout_op.tcl:536
+#: lib/branch_create.tcl:85 lib/checkout_op.tcl:571
 msgid "Reset"
 msgstr "Ripristina"
 
@@ -769,15 +798,25 @@
 msgid "Delete Only If Merged Into"
 msgstr "Cancella solo se fuso con un altro ramo"
 
-#: lib/branch_delete.tcl:54
-msgid "Always (Do not perform merge test.)"
-msgstr "Sempre (Non effettuare verifiche di fusione)."
+#: lib/branch_delete.tcl:54 lib/remote_branch_delete.tcl:119
+msgid "Always (Do not perform merge checks)"
+msgstr "Sempre (non verificare le fusioni)"
 
 #: lib/branch_delete.tcl:103
 #, tcl-format
 msgid "The following branches are not completely merged into %s:"
 msgstr "I rami seguenti non sono stati fusi completamente in %s:"
 
+#: lib/branch_delete.tcl:115 lib/remote_branch_delete.tcl:217
+msgid ""
+"Recovering deleted branches is difficult.\n"
+"\n"
+"Delete the selected branches?"
+msgstr ""
+"Ripristinare rami cancellati è difficile.\n"
+"\n"
+"Cancellare i rami selezionati?"
+
 #: lib/branch_delete.tcl:141
 #, tcl-format
 msgid ""
@@ -807,7 +846,7 @@
 msgid "Please select a branch to rename."
 msgstr "Scegliere un ramo da rinominare."
 
-#: lib/branch_rename.tcl:96 lib/checkout_op.tcl:201
+#: lib/branch_rename.tcl:96 lib/checkout_op.tcl:202
 #, tcl-format
 msgid "Branch '%s' already exists."
 msgstr "Il ramo '%s' esiste già."
@@ -838,38 +877,38 @@
 msgid "Browse Branch Files"
 msgstr "Esplora i file del ramo"
 
-#: lib/browser.tcl:278 lib/choose_repository.tcl:394
-#: lib/choose_repository.tcl:480 lib/choose_repository.tcl:491
-#: lib/choose_repository.tcl:995
+#: lib/browser.tcl:278 lib/choose_repository.tcl:398
+#: lib/choose_repository.tcl:486 lib/choose_repository.tcl:497
+#: lib/choose_repository.tcl:1028
 msgid "Browse"
 msgstr "Esplora"
 
-#: lib/checkout_op.tcl:84
+#: lib/checkout_op.tcl:85
 #, tcl-format
 msgid "Fetching %s from %s"
 msgstr "Recupero %s da %s"
 
-#: lib/checkout_op.tcl:132
+#: lib/checkout_op.tcl:133
 #, tcl-format
 msgid "fatal: Cannot resolve %s"
 msgstr "errore grave: impossibile risolvere %s"
 
-#: lib/checkout_op.tcl:145 lib/console.tcl:81 lib/database.tcl:31
+#: lib/checkout_op.tcl:146 lib/console.tcl:81 lib/database.tcl:31
 #: lib/sshkey.tcl:53
 msgid "Close"
 msgstr "Chiudi"
 
-#: lib/checkout_op.tcl:174
+#: lib/checkout_op.tcl:175
 #, tcl-format
 msgid "Branch '%s' does not exist."
 msgstr "Il ramo '%s' non esiste."
 
-#: lib/checkout_op.tcl:193
+#: lib/checkout_op.tcl:194
 #, tcl-format
 msgid "Failed to configure simplified git-pull for '%s'."
 msgstr "Impossibile configurare git-pull semplificato per '%s'."
 
-#: lib/checkout_op.tcl:228
+#: lib/checkout_op.tcl:229
 #, tcl-format
 msgid ""
 "Branch '%s' already exists.\n"
@@ -882,22 +921,22 @@
 "Non può effettuare un 'fast-forward' a %s.\n"
 "E' necessaria una fusione."
 
-#: lib/checkout_op.tcl:242
+#: lib/checkout_op.tcl:243
 #, tcl-format
 msgid "Merge strategy '%s' not supported."
 msgstr "La strategia di fusione '%s' non è supportata."
 
-#: lib/checkout_op.tcl:261
+#: lib/checkout_op.tcl:262
 #, tcl-format
 msgid "Failed to update '%s'."
 msgstr "Impossibile aggiornare '%s'."
 
-#: lib/checkout_op.tcl:273
+#: lib/checkout_op.tcl:274
 msgid "Staging area (index) is already locked."
 msgstr ""
 "L'area di preparazione per una nuova revisione (indice) è già bloccata."
 
-#: lib/checkout_op.tcl:288
+#: lib/checkout_op.tcl:289
 msgid ""
 "Last scanned state does not match repository state.\n"
 "\n"
@@ -914,30 +953,30 @@
 "\n"
 "La nuova analisi comincerà ora.\n"
 
-#: lib/checkout_op.tcl:344
+#: lib/checkout_op.tcl:345
 #, tcl-format
 msgid "Updating working directory to '%s'..."
 msgstr "Aggiornamento della directory di lavoro a '%s' in corso..."
 
-#: lib/checkout_op.tcl:345
+#: lib/checkout_op.tcl:346
 msgid "files checked out"
 msgstr "file presenti nella directory di lavoro"
 
-#: lib/checkout_op.tcl:375
+#: lib/checkout_op.tcl:376
 #, tcl-format
 msgid "Aborted checkout of '%s' (file level merging is required)."
 msgstr "Attivazione di '%s' fallita (richiesta una fusione a livello file)."
 
-#: lib/checkout_op.tcl:376
+#: lib/checkout_op.tcl:377
 msgid "File level merge required."
 msgstr "E' richiesta una fusione a livello file."
 
-#: lib/checkout_op.tcl:380
+#: lib/checkout_op.tcl:381
 #, tcl-format
 msgid "Staying on branch '%s'."
 msgstr "Si rimarrà sul ramo '%s'."
 
-#: lib/checkout_op.tcl:451
+#: lib/checkout_op.tcl:452
 msgid ""
 "You are no longer on a local branch.\n"
 "\n"
@@ -949,31 +988,31 @@
 "Se si vuole rimanere su un ramo, crearne uno ora a partire da 'Questa "
 "revisione attiva staccata'."
 
-#: lib/checkout_op.tcl:468 lib/checkout_op.tcl:472
+#: lib/checkout_op.tcl:503 lib/checkout_op.tcl:507
 #, tcl-format
 msgid "Checked out '%s'."
 msgstr "Attivazione di '%s' completata."
 
-#: lib/checkout_op.tcl:500
+#: lib/checkout_op.tcl:535
 #, tcl-format
 msgid "Resetting '%s' to '%s' will lose the following commits:"
 msgstr ""
 "Ripristinare '%s' a '%s' comporterà la perdita delle seguenti revisioni:"
 
-#: lib/checkout_op.tcl:522
+#: lib/checkout_op.tcl:557
 msgid "Recovering lost commits may not be easy."
 msgstr "Ricomporre le revisioni perdute potrebbe non essere semplice."
 
-#: lib/checkout_op.tcl:527
+#: lib/checkout_op.tcl:562
 #, tcl-format
 msgid "Reset '%s'?"
 msgstr "Ripristinare '%s'?"
 
-#: lib/checkout_op.tcl:532 lib/merge.tcl:164 lib/tools_dlg.tcl:343
+#: lib/checkout_op.tcl:567 lib/merge.tcl:164 lib/tools_dlg.tcl:343
 msgid "Visualize"
 msgstr "Visualizza"
 
-#: lib/checkout_op.tcl:600
+#: lib/checkout_op.tcl:635
 #, tcl-format
 msgid ""
 "Failed to set current branch.\n"
@@ -1019,7 +1058,7 @@
 msgid "Git Gui"
 msgstr "Git Gui"
 
-#: lib/choose_repository.tcl:87 lib/choose_repository.tcl:382
+#: lib/choose_repository.tcl:87 lib/choose_repository.tcl:386
 msgid "Create New Repository"
 msgstr "Crea nuovo archivio"
 
@@ -1027,7 +1066,7 @@
 msgid "New..."
 msgstr "Nuovo..."
 
-#: lib/choose_repository.tcl:100 lib/choose_repository.tcl:465
+#: lib/choose_repository.tcl:100 lib/choose_repository.tcl:471
 msgid "Clone Existing Repository"
 msgstr "Clona archivio esistente"
 
@@ -1035,7 +1074,7 @@
 msgid "Clone..."
 msgstr "Clona..."
 
-#: lib/choose_repository.tcl:113 lib/choose_repository.tcl:983
+#: lib/choose_repository.tcl:113 lib/choose_repository.tcl:1016
 msgid "Open Existing Repository"
 msgstr "Apri archivio esistente"
 
@@ -1051,198 +1090,198 @@
 msgid "Open Recent Repository:"
 msgstr "Apri archivio recente:"
 
-#: lib/choose_repository.tcl:302 lib/choose_repository.tcl:309
-#: lib/choose_repository.tcl:316
+#: lib/choose_repository.tcl:306 lib/choose_repository.tcl:313
+#: lib/choose_repository.tcl:320
 #, tcl-format
 msgid "Failed to create repository %s:"
 msgstr "Impossibile creare l'archivio %s:"
 
-#: lib/choose_repository.tcl:387
+#: lib/choose_repository.tcl:391
 msgid "Directory:"
 msgstr "Directory:"
 
-#: lib/choose_repository.tcl:417 lib/choose_repository.tcl:544
-#: lib/choose_repository.tcl:1017
+#: lib/choose_repository.tcl:423 lib/choose_repository.tcl:550
+#: lib/choose_repository.tcl:1052
 msgid "Git Repository"
 msgstr "Archivio Git"
 
-#: lib/choose_repository.tcl:442
+#: lib/choose_repository.tcl:448
 #, tcl-format
 msgid "Directory %s already exists."
 msgstr "La directory %s esiste già."
 
-#: lib/choose_repository.tcl:446
+#: lib/choose_repository.tcl:452
 #, tcl-format
 msgid "File %s already exists."
 msgstr "Il file %s esiste già."
 
-#: lib/choose_repository.tcl:460
+#: lib/choose_repository.tcl:466
 msgid "Clone"
 msgstr "Clona"
 
-#: lib/choose_repository.tcl:473
+#: lib/choose_repository.tcl:479
 msgid "Source Location:"
 msgstr "Posizione sorgente:"
 
-#: lib/choose_repository.tcl:484
+#: lib/choose_repository.tcl:490
 msgid "Target Directory:"
 msgstr "Directory di destinazione:"
 
-#: lib/choose_repository.tcl:496
+#: lib/choose_repository.tcl:502
 msgid "Clone Type:"
 msgstr "Tipo di clone:"
 
-#: lib/choose_repository.tcl:502
+#: lib/choose_repository.tcl:508
 msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
 msgstr "Standard (veloce, semi-ridondante, con hardlink)"
 
-#: lib/choose_repository.tcl:508
+#: lib/choose_repository.tcl:514
 msgid "Full Copy (Slower, Redundant Backup)"
 msgstr "Copia completa (più lento, backup ridondante)"
 
-#: lib/choose_repository.tcl:514
+#: lib/choose_repository.tcl:520
 msgid "Shared (Fastest, Not Recommended, No Backup)"
 msgstr "Shared (il più veloce, non raccomandato, nessun backup)"
 
-#: lib/choose_repository.tcl:550 lib/choose_repository.tcl:597
-#: lib/choose_repository.tcl:743 lib/choose_repository.tcl:813
-#: lib/choose_repository.tcl:1023 lib/choose_repository.tcl:1031
+#: lib/choose_repository.tcl:556 lib/choose_repository.tcl:603
+#: lib/choose_repository.tcl:749 lib/choose_repository.tcl:819
+#: lib/choose_repository.tcl:1058 lib/choose_repository.tcl:1066
 #, tcl-format
 msgid "Not a Git repository: %s"
 msgstr "%s non è un archivio Git."
 
-#: lib/choose_repository.tcl:586
+#: lib/choose_repository.tcl:592
 msgid "Standard only available for local repository."
 msgstr "Standard è disponibile solo per archivi locali."
 
-#: lib/choose_repository.tcl:590
+#: lib/choose_repository.tcl:596
 msgid "Shared only available for local repository."
 msgstr "Shared è disponibile solo per archivi locali."
 
-#: lib/choose_repository.tcl:611
+#: lib/choose_repository.tcl:617
 #, tcl-format
 msgid "Location %s already exists."
 msgstr "Il file/directory %s esiste già."
 
-#: lib/choose_repository.tcl:622
+#: lib/choose_repository.tcl:628
 msgid "Failed to configure origin"
 msgstr "Impossibile configurare origin"
 
-#: lib/choose_repository.tcl:634
+#: lib/choose_repository.tcl:640
 msgid "Counting objects"
 msgstr "Calcolo oggetti"
 
-#: lib/choose_repository.tcl:635
+#: lib/choose_repository.tcl:641
 msgid "buckets"
 msgstr ""
 
-#: lib/choose_repository.tcl:659
+#: lib/choose_repository.tcl:665
 #, tcl-format
 msgid "Unable to copy objects/info/alternates: %s"
 msgstr "Impossibile copiare oggetti/info/alternate: %s"
 
-#: lib/choose_repository.tcl:695
+#: lib/choose_repository.tcl:701
 #, tcl-format
 msgid "Nothing to clone from %s."
 msgstr "Niente da clonare da %s."
 
-#: lib/choose_repository.tcl:697 lib/choose_repository.tcl:911
-#: lib/choose_repository.tcl:923
+#: lib/choose_repository.tcl:703 lib/choose_repository.tcl:917
+#: lib/choose_repository.tcl:929
 msgid "The 'master' branch has not been initialized."
 msgstr "Il ramo 'master' non è stato inizializzato."
 
-#: lib/choose_repository.tcl:710
+#: lib/choose_repository.tcl:716
 msgid "Hardlinks are unavailable.  Falling back to copying."
 msgstr "Impossibile utilizzare gli hardlink. Si ricorrerà alla copia."
 
-#: lib/choose_repository.tcl:722
+#: lib/choose_repository.tcl:728
 #, tcl-format
 msgid "Cloning from %s"
 msgstr "Clonazione da %s"
 
-#: lib/choose_repository.tcl:753
+#: lib/choose_repository.tcl:759
 msgid "Copying objects"
 msgstr "Copia degli oggetti"
 
-#: lib/choose_repository.tcl:754
+#: lib/choose_repository.tcl:760
 msgid "KiB"
 msgstr "KiB"
 
-#: lib/choose_repository.tcl:778
+#: lib/choose_repository.tcl:784
 #, tcl-format
 msgid "Unable to copy object: %s"
 msgstr "Impossibile copiare oggetto: %s"
 
-#: lib/choose_repository.tcl:788
+#: lib/choose_repository.tcl:794
 msgid "Linking objects"
 msgstr "Collegamento oggetti"
 
-#: lib/choose_repository.tcl:789
+#: lib/choose_repository.tcl:795
 msgid "objects"
 msgstr "oggetti"
 
-#: lib/choose_repository.tcl:797
+#: lib/choose_repository.tcl:803
 #, tcl-format
 msgid "Unable to hardlink object: %s"
 msgstr "Hardlink impossibile sull'oggetto: %s"
 
-#: lib/choose_repository.tcl:852
+#: lib/choose_repository.tcl:858
 msgid "Cannot fetch branches and objects.  See console output for details."
 msgstr ""
 "Impossibile recuperare rami e oggetti. Controllare i dettagli forniti dalla "
 "console."
 
-#: lib/choose_repository.tcl:863
+#: lib/choose_repository.tcl:869
 msgid "Cannot fetch tags.  See console output for details."
 msgstr ""
 "Impossibile recuperare le etichette. Controllare i dettagli forniti dalla "
 "console."
 
-#: lib/choose_repository.tcl:887
+#: lib/choose_repository.tcl:893
 msgid "Cannot determine HEAD.  See console output for details."
 msgstr ""
 "Impossibile determinare HEAD. Controllare i dettagli forniti dalla console."
 
-#: lib/choose_repository.tcl:896
+#: lib/choose_repository.tcl:902
 #, tcl-format
 msgid "Unable to cleanup %s"
 msgstr "Impossibile ripulire %s"
 
-#: lib/choose_repository.tcl:902
+#: lib/choose_repository.tcl:908
 msgid "Clone failed."
 msgstr "Clonazione non riuscita."
 
-#: lib/choose_repository.tcl:909
+#: lib/choose_repository.tcl:915
 msgid "No default branch obtained."
 msgstr "Non è stato trovato un ramo predefinito."
 
-#: lib/choose_repository.tcl:920
+#: lib/choose_repository.tcl:926
 #, tcl-format
 msgid "Cannot resolve %s as a commit."
 msgstr "Impossibile risolvere %s come una revisione."
 
-#: lib/choose_repository.tcl:932
+#: lib/choose_repository.tcl:938
 msgid "Creating working directory"
 msgstr "Creazione directory di lavoro"
 
-#: lib/choose_repository.tcl:933 lib/index.tcl:65 lib/index.tcl:128
-#: lib/index.tcl:196
+#: lib/choose_repository.tcl:939 lib/index.tcl:67 lib/index.tcl:130
+#: lib/index.tcl:198
 msgid "files"
 msgstr "file"
 
-#: lib/choose_repository.tcl:962
+#: lib/choose_repository.tcl:968
 msgid "Initial file checkout failed."
 msgstr "Attivazione iniziale non riuscita."
 
-#: lib/choose_repository.tcl:978
+#: lib/choose_repository.tcl:1011
 msgid "Open"
 msgstr "Apri"
 
-#: lib/choose_repository.tcl:988
+#: lib/choose_repository.tcl:1021
 msgid "Repository:"
 msgstr "Archivio:"
 
-#: lib/choose_repository.tcl:1037
+#: lib/choose_repository.tcl:1072
 #, tcl-format
 msgid "Failed to open repository %s:"
 msgstr "Impossibile accedere all'archivio %s:"
@@ -1326,7 +1365,12 @@
 msgid "Invalid GIT_COMMITTER_IDENT:"
 msgstr "GIT_COMMITTER_IDENT non valida:"
 
-#: lib/commit.tcl:132
+#: lib/commit.tcl:129
+#, tcl-format
+msgid "warning: Tcl does not support encoding '%s'."
+msgstr "attenzione: Tcl non supporta la codifica '%s'."
+
+#: lib/commit.tcl:149
 msgid ""
 "Last scanned state does not match repository state.\n"
 "\n"
@@ -1343,7 +1387,7 @@
 "\n"
 "La nuova analisi comincerà ora.\n"
 
-#: lib/commit.tcl:155
+#: lib/commit.tcl:172
 #, tcl-format
 msgid ""
 "Unmerged files cannot be committed.\n"
@@ -1356,7 +1400,7 @@
 "Il file %s presenta dei conflitti. Devi risolverli e preparare il file per "
 "creare una nuova revisione prima di effettuare questa azione.\n"
 
-#: lib/commit.tcl:163
+#: lib/commit.tcl:180
 #, tcl-format
 msgid ""
 "Unknown file state %s detected.\n"
@@ -1367,7 +1411,7 @@
 "\n"
 "Questo programma non può creare una revisione contenente il file %s.\n"
 
-#: lib/commit.tcl:171
+#: lib/commit.tcl:188
 msgid ""
 "No changes to commit.\n"
 "\n"
@@ -1378,7 +1422,7 @@
 "Devi preparare per una nuova revisione almeno 1 file prima di effettuare "
 "questa operazione.\n"
 
-#: lib/commit.tcl:186
+#: lib/commit.tcl:203
 msgid ""
 "Please supply a commit message.\n"
 "\n"
@@ -1396,45 +1440,40 @@
 "- Seconda linea: vuota.\n"
 "- Terza linea: spiega a cosa serve la tua modifica.\n"
 
-#: lib/commit.tcl:210
-#, tcl-format
-msgid "warning: Tcl does not support encoding '%s'."
-msgstr "attenzione: Tcl non supporta la codifica '%s'."
-
-#: lib/commit.tcl:226
+#: lib/commit.tcl:234
 msgid "Calling pre-commit hook..."
 msgstr "Avvio pre-commit hook..."
 
-#: lib/commit.tcl:241
+#: lib/commit.tcl:249
 msgid "Commit declined by pre-commit hook."
 msgstr "Revisione rifiutata dal pre-commit hook."
 
-#: lib/commit.tcl:264
+#: lib/commit.tcl:272
 msgid "Calling commit-msg hook..."
 msgstr "Avvio commit-msg hook..."
 
-#: lib/commit.tcl:279
+#: lib/commit.tcl:287
 msgid "Commit declined by commit-msg hook."
 msgstr "Revisione rifiutata dal commit-msg hook."
 
-#: lib/commit.tcl:292
+#: lib/commit.tcl:300
 msgid "Committing changes..."
 msgstr "Archiviazione modifiche..."
 
-#: lib/commit.tcl:308
+#: lib/commit.tcl:316
 msgid "write-tree failed:"
 msgstr "write-tree non riuscito:"
 
-#: lib/commit.tcl:309 lib/commit.tcl:353 lib/commit.tcl:373
+#: lib/commit.tcl:317 lib/commit.tcl:361 lib/commit.tcl:382
 msgid "Commit failed."
 msgstr "Impossibile creare una nuova revisione."
 
-#: lib/commit.tcl:326
+#: lib/commit.tcl:334
 #, tcl-format
 msgid "Commit %s appears to be corrupt"
 msgstr "La revisione %s sembra essere danneggiata"
 
-#: lib/commit.tcl:331
+#: lib/commit.tcl:339
 msgid ""
 "No changes to commit.\n"
 "\n"
@@ -1448,19 +1487,19 @@
 "\n"
 "Si procederà subito ad una nuova analisi.\n"
 
-#: lib/commit.tcl:338
+#: lib/commit.tcl:346
 msgid "No changes to commit."
 msgstr "Nessuna modifica per la nuova revisione."
 
-#: lib/commit.tcl:352
+#: lib/commit.tcl:360
 msgid "commit-tree failed:"
 msgstr "commit-tree non riuscito:"
 
-#: lib/commit.tcl:372
+#: lib/commit.tcl:381
 msgid "update-ref failed:"
 msgstr "update-ref non riuscito:"
 
-#: lib/commit.tcl:460
+#: lib/commit.tcl:469
 #, tcl-format
 msgid "Created commit %s: %s"
 msgstr "Creata revisione %s: %s"
@@ -1513,20 +1552,19 @@
 msgid "Verifying the object database with fsck-objects"
 msgstr "Verifica dell'archivio con fsck-objects in corso"
 
-#: lib/database.tcl:108
+#: lib/database.tcl:107
 #, tcl-format
 msgid ""
 "This repository currently has approximately %i loose objects.\n"
 "\n"
 "To maintain optimal performance it is strongly recommended that you compress "
-"the database when more than %i loose objects exist.\n"
+"the database.\n"
 "\n"
 "Compress the database now?"
 msgstr ""
 "Questo archivio attualmente ha circa %i oggetti slegati.\n"
 "\n"
-"Per mantenere buone prestazioni si raccomanda di comprimere l'archivio "
-"quando sono presenti più di %i oggetti slegati.\n"
+"Per mantenere buone prestazioni si raccomanda di comprimere l'archivio.\n"
 "\n"
 "Comprimere l'archivio ora?"
 
@@ -1535,7 +1573,7 @@
 msgid "Invalid date from Git: %s"
 msgstr "Git ha restituito una data non valida: %s"
 
-#: lib/diff.tcl:59
+#: lib/diff.tcl:64
 #, tcl-format
 msgid ""
 "No differences detected.\n"
@@ -1558,12 +1596,12 @@
 "Si procederà automaticamente ad una nuova analisi per trovare altri file che "
 "potrebbero avere lo stesso stato."
 
-#: lib/diff.tcl:99
+#: lib/diff.tcl:104
 #, tcl-format
 msgid "Loading diff of %s..."
 msgstr "Caricamento delle differenze di %s..."
 
-#: lib/diff.tcl:120
+#: lib/diff.tcl:125
 msgid ""
 "LOCAL: deleted\n"
 "REMOTE:\n"
@@ -1571,7 +1609,7 @@
 "LOCALE: cancellato\n"
 "REMOTO:\n"
 
-#: lib/diff.tcl:125
+#: lib/diff.tcl:130
 msgid ""
 "REMOTE: deleted\n"
 "LOCAL:\n"
@@ -1579,32 +1617,32 @@
 "REMOTO: cancellato\n"
 "LOCALE:\n"
 
-#: lib/diff.tcl:132
+#: lib/diff.tcl:137
 msgid "LOCAL:\n"
 msgstr "LOCALE:\n"
 
-#: lib/diff.tcl:135
+#: lib/diff.tcl:140
 msgid "REMOTE:\n"
 msgstr "REMOTO:\n"
 
-#: lib/diff.tcl:197 lib/diff.tcl:296
+#: lib/diff.tcl:202 lib/diff.tcl:319
 #, tcl-format
 msgid "Unable to display %s"
 msgstr "Impossibile visualizzare %s"
 
-#: lib/diff.tcl:198
+#: lib/diff.tcl:203
 msgid "Error loading file:"
 msgstr "Errore nel caricamento del file:"
 
-#: lib/diff.tcl:205
+#: lib/diff.tcl:210
 msgid "Git Repository (subproject)"
 msgstr "Archivio Git (sottoprogetto)"
 
-#: lib/diff.tcl:217
+#: lib/diff.tcl:222
 msgid "* Binary file (not showing content)."
 msgstr "* File binario (il contenuto non sarà mostrato)."
 
-#: lib/diff.tcl:222
+#: lib/diff.tcl:227
 #, tcl-format
 msgid ""
 "* Untracked file is %d bytes.\n"
@@ -1613,7 +1651,7 @@
 "* Il file non tracciato è di %d byte.\n"
 "* Saranno visualizzati solo i primi %d byte.\n"
 
-#: lib/diff.tcl:228
+#: lib/diff.tcl:233
 #, tcl-format
 msgid ""
 "\n"
@@ -1624,19 +1662,19 @@
 "* %s non visualizza completamente questo file non tracciato.\n"
 "* Per visualizzare il file completo, usare un programma esterno.\n"
 
-#: lib/diff.tcl:436
+#: lib/diff.tcl:482
 msgid "Failed to unstage selected hunk."
 msgstr "Impossibile rimuovere la sezione scelta dalla nuova revisione."
 
-#: lib/diff.tcl:443
+#: lib/diff.tcl:489
 msgid "Failed to stage selected hunk."
 msgstr "Impossibile preparare la sezione scelta per una nuova revisione."
 
-#: lib/diff.tcl:509
+#: lib/diff.tcl:568
 msgid "Failed to unstage selected line."
 msgstr "Impossibile rimuovere la riga scelta dalla nuova revisione."
 
-#: lib/diff.tcl:517
+#: lib/diff.tcl:576
 msgid "Failed to stage selected line."
 msgstr "Impossibile preparare la riga scelta per una nuova revisione."
 
@@ -1674,7 +1712,7 @@
 msgid "Index Error"
 msgstr "Errore nell'indice"
 
-#: lib/index.tcl:21
+#: lib/index.tcl:17
 msgid ""
 "Updating the Git index failed.  A rescan will be automatically started to "
 "resynchronize git-gui."
@@ -1682,7 +1720,7 @@
 "Impossibile aggiornare l'indice. Ora sarà avviata una nuova analisi che "
 "aggiornerà git-gui."
 
-#: lib/index.tcl:27
+#: lib/index.tcl:28
 msgid "Continue"
 msgstr "Continua"
 
@@ -1690,45 +1728,45 @@
 msgid "Unlock Index"
 msgstr "Sblocca l'accesso all'indice"
 
-#: lib/index.tcl:287
+#: lib/index.tcl:289
 #, tcl-format
 msgid "Unstaging %s from commit"
 msgstr "%s non farà parte della prossima revisione"
 
-#: lib/index.tcl:326
+#: lib/index.tcl:328
 msgid "Ready to commit."
 msgstr "Pronto per creare una nuova revisione."
 
-#: lib/index.tcl:339
+#: lib/index.tcl:341
 #, tcl-format
 msgid "Adding %s"
 msgstr "Aggiunta di %s in corso"
 
-#: lib/index.tcl:396
+#: lib/index.tcl:398
 #, tcl-format
 msgid "Revert changes in file %s?"
 msgstr "Annullare le modifiche nel file %s?"
 
-#: lib/index.tcl:398
+#: lib/index.tcl:400
 #, tcl-format
 msgid "Revert changes in these %i files?"
 msgstr "Annullare le modifiche in questi %i file?"
 
-#: lib/index.tcl:406
+#: lib/index.tcl:408
 msgid "Any unstaged changes will be permanently lost by the revert."
 msgstr ""
 "Tutte le modifiche non preparate per una nuova revisione saranno perse per "
 "sempre."
 
-#: lib/index.tcl:409
+#: lib/index.tcl:411
 msgid "Do Nothing"
 msgstr "Non fare niente"
 
-#: lib/index.tcl:427
+#: lib/index.tcl:429
 msgid "Reverting selected files"
 msgstr "Annullo le modifiche nei file selezionati"
 
-#: lib/index.tcl:431
+#: lib/index.tcl:433
 #, tcl-format
 msgid "Reverting %s"
 msgstr "Annullo le modifiche in %s"
@@ -2215,10 +2253,6 @@
 msgid "Merged Into:"
 msgstr "Fuso in:"
 
-#: lib/remote_branch_delete.tcl:119
-msgid "Always (Do not perform merge checks)"
-msgstr "Sempre (non verificare le fusioni)"
-
 #: lib/remote_branch_delete.tcl:152
 msgid "A branch is required for 'Merged Into'."
 msgstr "Si richiede un ramo per 'Fuso in'."
@@ -2247,26 +2281,16 @@
 msgid "Please select one or more branches to delete."
 msgstr "Scegliere uno o più rami da cancellare."
 
-#: lib/remote_branch_delete.tcl:216
-msgid ""
-"Recovering deleted branches is difficult.\n"
-"\n"
-"Delete the selected branches?"
-msgstr ""
-"Ricomporre rami cancellati è difficile.\n"
-"\n"
-"Cancellare i rami selezionati?"
-
 #: lib/remote_branch_delete.tcl:226
 #, tcl-format
 msgid "Deleting branches from %s"
 msgstr "Cancellazione rami da %s"
 
-#: lib/remote_branch_delete.tcl:286
+#: lib/remote_branch_delete.tcl:292
 msgid "No repository selected."
 msgstr "Nessun archivio selezionato."
 
-#: lib/remote_branch_delete.tcl:291
+#: lib/remote_branch_delete.tcl:297
 #, tcl-format
 msgid "Scanning %s..."
 msgstr "Analisi in corso %s..."
@@ -2287,11 +2311,11 @@
 msgid "Case-Sensitive"
 msgstr "Distingui maiuscole"
 
-#: lib/shortcut.tcl:20 lib/shortcut.tcl:61
+#: lib/shortcut.tcl:21 lib/shortcut.tcl:62
 msgid "Cannot write shortcut:"
 msgstr "Impossibile scrivere shortcut:"
 
-#: lib/shortcut.tcl:136
+#: lib/shortcut.tcl:137
 msgid "Cannot write icon:"
 msgstr "Impossibile scrivere icona:"
 
@@ -2399,7 +2423,7 @@
 #: lib/tools.tcl:110
 #, tcl-format
 msgid "Tool: %s"
-msgstr "Strumento: %s"
+msgstr "Accessorio: %s"
 
 #: lib/tools.tcl:111
 #, tcl-format
@@ -2418,7 +2442,7 @@
 
 #: lib/tools_dlg.tcl:22
 msgid "Add Tool"
-msgstr "Aggiungi strumento"
+msgstr "Aggiungi accessorio"
 
 #: lib/tools_dlg.tcl:28
 msgid "Add New Tool Command"
@@ -2430,7 +2454,7 @@
 
 #: lib/tools_dlg.tcl:45
 msgid "Tool Details"
-msgstr "Dettagli sullo strumento"
+msgstr "Dettagli sull'accessorio"
 
 #: lib/tools_dlg.tcl:48
 msgid "Use '/' separators to create a submenu tree:"
@@ -2462,12 +2486,12 @@
 
 #: lib/tools_dlg.tcl:121
 msgid "Please supply a name for the tool."
-msgstr "Bisogna dare un nome allo strumento."
+msgstr "Bisogna dare un nome all'accessorio."
 
 #: lib/tools_dlg.tcl:129
 #, tcl-format
 msgid "Tool '%s' already exists."
-msgstr "Lo strumento '%s' esiste già."
+msgstr "L'accessorio '%s' esiste già."
 
 #: lib/tools_dlg.tcl:151
 #, tcl-format
@@ -2475,17 +2499,17 @@
 "Could not add tool:\n"
 "%s"
 msgstr ""
-"Impossibile aggiungere lo strumento:\n"
+"Impossibile aggiungere l'accessorio:\n"
 "\n"
 "%s"
 
 #: lib/tools_dlg.tcl:190
 msgid "Remove Tool"
-msgstr "Rimuovi strumento"
+msgstr "Rimuovi accessorio"
 
 #: lib/tools_dlg.tcl:196
 msgid "Remove Tool Commands"
-msgstr "Rimuovi i comandi dello strumento"
+msgstr "Rimuovi i comandi accessori"
 
 #: lib/tools_dlg.tcl:200
 msgid "Remove"
@@ -2493,7 +2517,7 @@
 
 #: lib/tools_dlg.tcl:236
 msgid "(Blue denotes repository-local tools)"
-msgstr "(Il colore blu indica strumenti per l'archivio locale)"
+msgstr "(Il colore blu indica accessori per l'archivio locale)"
 
 #: lib/tools_dlg.tcl:297
 #, tcl-format
diff --git a/git-gui/po/ja.po b/git-gui/po/ja.po
index 63c4695..1501798 100644
--- a/git-gui/po/ja.po
+++ b/git-gui/po/ja.po
@@ -3,46 +3,45 @@
 # This file is distributed under the same license as the git-gui package.
 # しらいし ななこ <nanako3@bluebottle.com>, 2007.
 #
-#, fuzzy
 msgid ""
 msgstr ""
 "Project-Id-Version: git-gui\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-12-08 08:31-0800\n"
-"PO-Revision-Date: 2008-12-09 06:27+0900\n"
+"POT-Creation-Date: 2010-01-26 15:47-0800\n"
+"PO-Revision-Date: 2010-02-02 19:03+0900\n"
 "Last-Translator: しらいし ななこ <nanako3@lavabit.com>\n"
 "Language-Team: Japanese\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: git-gui.sh:41 git-gui.sh:737 git-gui.sh:751 git-gui.sh:764 git-gui.sh:847
-#: git-gui.sh:866
+#: git-gui.sh:41 git-gui.sh:793 git-gui.sh:807 git-gui.sh:820 git-gui.sh:903
+#: git-gui.sh:922
 msgid "git-gui: fatal error"
 msgstr "git-gui: 致命的なエラー"
 
-#: git-gui.sh:689
+#: git-gui.sh:743
 #, tcl-format
 msgid "Invalid font specified in %s:"
 msgstr "%s に無効なフォントが指定されています:"
 
-#: git-gui.sh:723
+#: git-gui.sh:779
 msgid "Main Font"
 msgstr "主フォント"
 
-#: git-gui.sh:724
+#: git-gui.sh:780
 msgid "Diff/Console Font"
 msgstr "diff/コンソール・フォント"
 
-#: git-gui.sh:738
+#: git-gui.sh:794
 msgid "Cannot find git in PATH."
 msgstr "PATH 中に git が見つかりません"
 
-#: git-gui.sh:765
+#: git-gui.sh:821
 msgid "Cannot parse Git version string:"
 msgstr "Git バージョン名が理解できません:"
 
-#: git-gui.sh:783
+#: git-gui.sh:839
 #, tcl-format
 msgid ""
 "Git version cannot be determined.\n"
@@ -61,446 +60,475 @@
 "\n"
 "'%s' はバージョン 1.5.0 と思って良いですか?\n"
 
-#: git-gui.sh:1062
+#: git-gui.sh:1128
 msgid "Git directory not found:"
 msgstr "Git ディレクトリが見つかりません:"
 
-#: git-gui.sh:1069
+#: git-gui.sh:1146
 msgid "Cannot move to top of working directory:"
 msgstr "作業ディレクトリの最上位に移動できません"
 
-#: git-gui.sh:1076
-msgid "Cannot use funny .git directory:"
-msgstr "変な .git ディレクトリは使えません"
+#: git-gui.sh:1154
+msgid "Cannot use bare repository:"
+msgstr "裸のリポジトリは使えません:"
 
-#: git-gui.sh:1081
+#: git-gui.sh:1162
 msgid "No working directory"
 msgstr "作業ディレクトリがありません"
 
-#: git-gui.sh:1247 lib/checkout_op.tcl:305
+#: git-gui.sh:1334 lib/checkout_op.tcl:306
 msgid "Refreshing file status..."
 msgstr "ファイル状態を更新しています…"
 
-#: git-gui.sh:1303
+#: git-gui.sh:1390
 msgid "Scanning for modified files ..."
 msgstr "変更されたファイルをスキャンしています…"
 
-#: git-gui.sh:1367
+#: git-gui.sh:1454
 msgid "Calling prepare-commit-msg hook..."
 msgstr "prepare-commit-msg フックを実行中・・・"
 
-#: git-gui.sh:1384
+#: git-gui.sh:1471
 msgid "Commit declined by prepare-commit-msg hook."
 msgstr "prepare-commit-msg フックがコミットを拒否しました"
 
-#: git-gui.sh:1542 lib/browser.tcl:246
+#: git-gui.sh:1629 lib/browser.tcl:246
 msgid "Ready."
 msgstr "準備完了"
 
-#: git-gui.sh:1819
+#: git-gui.sh:1787
+#, tcl-format
+msgid "Displaying only %s of %s files."
+msgstr "全体で%s個の内の%sファイルだけ表示しています"
+
+#: git-gui.sh:1913
 msgid "Unmodified"
 msgstr "変更無し"
 
-#: git-gui.sh:1821
+#: git-gui.sh:1915
 msgid "Modified, not staged"
 msgstr "変更あり、コミット未予定"
 
-#: git-gui.sh:1822 git-gui.sh:1830
+#: git-gui.sh:1916 git-gui.sh:1924
 msgid "Staged for commit"
 msgstr "コミット予定済"
 
-#: git-gui.sh:1823 git-gui.sh:1831
+#: git-gui.sh:1917 git-gui.sh:1925
 msgid "Portions staged for commit"
 msgstr "部分的にコミット予定済"
 
-#: git-gui.sh:1824 git-gui.sh:1832
+#: git-gui.sh:1918 git-gui.sh:1926
 msgid "Staged for commit, missing"
 msgstr "コミット予定済、ファイル無し"
 
-#: git-gui.sh:1826
+#: git-gui.sh:1920
 msgid "File type changed, not staged"
 msgstr "ファイル型変更、コミット未予定"
 
-#: git-gui.sh:1827
+#: git-gui.sh:1921
 msgid "File type changed, staged"
 msgstr "ファイル型変更、コミット予定済"
 
-#: git-gui.sh:1829
+#: git-gui.sh:1923
 msgid "Untracked, not staged"
 msgstr "管理外、コミット未予定"
 
-#: git-gui.sh:1834
+#: git-gui.sh:1928
 msgid "Missing"
 msgstr "ファイル無し"
 
-#: git-gui.sh:1835
+#: git-gui.sh:1929
 msgid "Staged for removal"
 msgstr "削除予定済"
 
-#: git-gui.sh:1836
+#: git-gui.sh:1930
 msgid "Staged for removal, still present"
 msgstr "削除予定済、ファイル未削除"
 
-#: git-gui.sh:1838 git-gui.sh:1839 git-gui.sh:1840 git-gui.sh:1841
-#: git-gui.sh:1842 git-gui.sh:1843
+#: git-gui.sh:1932 git-gui.sh:1933 git-gui.sh:1934 git-gui.sh:1935
+#: git-gui.sh:1936 git-gui.sh:1937
 msgid "Requires merge resolution"
 msgstr "要マージ解決"
 
-#: git-gui.sh:1878
+#: git-gui.sh:1972
 msgid "Starting gitk... please wait..."
 msgstr "gitk を起動中…お待ち下さい…"
 
-#: git-gui.sh:1887
+#: git-gui.sh:1984
 msgid "Couldn't find gitk in PATH"
 msgstr "PATH 中に gitk が見つかりません"
 
-#: git-gui.sh:2280 lib/choose_repository.tcl:36
+#: git-gui.sh:2043
+msgid "Couldn't find git gui in PATH"
+msgstr "PATH 中に git gui が見つかりません"
+
+#: git-gui.sh:2455 lib/choose_repository.tcl:36
 msgid "Repository"
 msgstr "リポジトリ"
 
-#: git-gui.sh:2281
+#: git-gui.sh:2456
 msgid "Edit"
 msgstr "編集"
 
-#: git-gui.sh:2283 lib/choose_rev.tcl:561
+#: git-gui.sh:2458 lib/choose_rev.tcl:561
 msgid "Branch"
 msgstr "ブランチ"
 
-#: git-gui.sh:2286 lib/choose_rev.tcl:548
+#: git-gui.sh:2461 lib/choose_rev.tcl:548
 msgid "Commit@@noun"
 msgstr "コミット"
 
-#: git-gui.sh:2289 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
+#: git-gui.sh:2464 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
 msgid "Merge"
 msgstr "マージ"
 
-#: git-gui.sh:2290 lib/choose_rev.tcl:557
+#: git-gui.sh:2465 lib/choose_rev.tcl:557
 msgid "Remote"
 msgstr "リモート"
 
-#: git-gui.sh:2293
+#: git-gui.sh:2468
 msgid "Tools"
 msgstr "ツール"
 
-#: git-gui.sh:2302
+#: git-gui.sh:2477
 msgid "Explore Working Copy"
 msgstr "ワーキングコピーをブラウズ"
 
-#: git-gui.sh:2307
+#: git-gui.sh:2483
 msgid "Browse Current Branch's Files"
 msgstr "現在のブランチのファイルを見る"
 
-#: git-gui.sh:2311
+#: git-gui.sh:2487
 msgid "Browse Branch Files..."
 msgstr "ブランチのファイルを見る…"
 
-#: git-gui.sh:2316
+#: git-gui.sh:2492
 msgid "Visualize Current Branch's History"
 msgstr "現在のブランチの履歴を見る"
 
-#: git-gui.sh:2320
+#: git-gui.sh:2496
 msgid "Visualize All Branch History"
 msgstr "全てのブランチの履歴を見る"
 
-#: git-gui.sh:2327
+#: git-gui.sh:2503
 #, tcl-format
 msgid "Browse %s's Files"
 msgstr "ブランチ %s のファイルを見る"
 
-#: git-gui.sh:2329
+#: git-gui.sh:2505
 #, tcl-format
 msgid "Visualize %s's History"
 msgstr "ブランチ %s の履歴を見る"
 
-#: git-gui.sh:2334 lib/database.tcl:27 lib/database.tcl:67
+#: git-gui.sh:2510 lib/database.tcl:27 lib/database.tcl:67
 msgid "Database Statistics"
 msgstr "データベース統計"
 
-#: git-gui.sh:2337 lib/database.tcl:34
+#: git-gui.sh:2513 lib/database.tcl:34
 msgid "Compress Database"
 msgstr "データベース圧縮"
 
-#: git-gui.sh:2340
+#: git-gui.sh:2516
 msgid "Verify Database"
 msgstr "データベース検証"
 
-#: git-gui.sh:2347 git-gui.sh:2351 git-gui.sh:2355 lib/shortcut.tcl:7
-#: lib/shortcut.tcl:39 lib/shortcut.tcl:71
+#: git-gui.sh:2523 git-gui.sh:2527 git-gui.sh:2531 lib/shortcut.tcl:8
+#: lib/shortcut.tcl:40 lib/shortcut.tcl:72
 msgid "Create Desktop Icon"
 msgstr "デスクトップ・アイコンを作る"
 
-#: git-gui.sh:2363 lib/choose_repository.tcl:183 lib/choose_repository.tcl:191
+#: git-gui.sh:2539 lib/choose_repository.tcl:183 lib/choose_repository.tcl:191
 msgid "Quit"
 msgstr "終了"
 
-#: git-gui.sh:2371
+#: git-gui.sh:2547
 msgid "Undo"
 msgstr "元に戻す"
 
-#: git-gui.sh:2374
+#: git-gui.sh:2550
 msgid "Redo"
 msgstr "やり直し"
 
-#: git-gui.sh:2378 git-gui.sh:2923
+#: git-gui.sh:2554 git-gui.sh:3109
 msgid "Cut"
 msgstr "切り取り"
 
-#: git-gui.sh:2381 git-gui.sh:2926 git-gui.sh:3000 git-gui.sh:3082
+#: git-gui.sh:2557 git-gui.sh:3112 git-gui.sh:3186 git-gui.sh:3259
 #: lib/console.tcl:69
 msgid "Copy"
 msgstr "コピー"
 
-#: git-gui.sh:2384 git-gui.sh:2929
+#: git-gui.sh:2560 git-gui.sh:3115
 msgid "Paste"
 msgstr "貼り付け"
 
-#: git-gui.sh:2387 git-gui.sh:2932 lib/branch_delete.tcl:26
+#: git-gui.sh:2563 git-gui.sh:3118 lib/branch_delete.tcl:26
 #: lib/remote_branch_delete.tcl:38
 msgid "Delete"
 msgstr "削除"
 
-#: git-gui.sh:2391 git-gui.sh:2936 git-gui.sh:3086 lib/console.tcl:71
+#: git-gui.sh:2567 git-gui.sh:3122 git-gui.sh:3263 lib/console.tcl:71
 msgid "Select All"
 msgstr "全て選択"
 
-#: git-gui.sh:2400
+#: git-gui.sh:2576
 msgid "Create..."
 msgstr "作成…"
 
-#: git-gui.sh:2406
+#: git-gui.sh:2582
 msgid "Checkout..."
 msgstr "チェックアウト"
 
-#: git-gui.sh:2412
+#: git-gui.sh:2588
 msgid "Rename..."
 msgstr "名前変更…"
 
-#: git-gui.sh:2417
+#: git-gui.sh:2593
 msgid "Delete..."
 msgstr "削除…"
 
-#: git-gui.sh:2422
+#: git-gui.sh:2598
 msgid "Reset..."
 msgstr "リセット…"
 
-#: git-gui.sh:2432
+#: git-gui.sh:2608
 msgid "Done"
 msgstr "完了"
 
-#: git-gui.sh:2434
+#: git-gui.sh:2610
 msgid "Commit@@verb"
 msgstr "コミット"
 
-#: git-gui.sh:2443 git-gui.sh:2864
+#: git-gui.sh:2619 git-gui.sh:3050
 msgid "New Commit"
 msgstr "新規コミット"
 
-#: git-gui.sh:2451 git-gui.sh:2871
+#: git-gui.sh:2627 git-gui.sh:3057
 msgid "Amend Last Commit"
 msgstr "最新コミットを訂正"
 
-#: git-gui.sh:2461 git-gui.sh:2825 lib/remote_branch_delete.tcl:99
+#: git-gui.sh:2637 git-gui.sh:3011 lib/remote_branch_delete.tcl:99
 msgid "Rescan"
 msgstr "再スキャン"
 
-#: git-gui.sh:2467
+#: git-gui.sh:2643
 msgid "Stage To Commit"
 msgstr "コミット予定する"
 
-#: git-gui.sh:2473
+#: git-gui.sh:2649
 msgid "Stage Changed Files To Commit"
 msgstr "変更されたファイルをコミット予定"
 
-#: git-gui.sh:2479
+#: git-gui.sh:2655
 msgid "Unstage From Commit"
 msgstr "コミットから降ろす"
 
-#: git-gui.sh:2484 lib/index.tcl:410
+#: git-gui.sh:2661 lib/index.tcl:412
 msgid "Revert Changes"
 msgstr "変更を元に戻す"
 
-#: git-gui.sh:2491 git-gui.sh:3069
+#: git-gui.sh:2669 git-gui.sh:3310 git-gui.sh:3341
 msgid "Show Less Context"
 msgstr "文脈を少なく"
 
-#: git-gui.sh:2495 git-gui.sh:3073
+#: git-gui.sh:2673 git-gui.sh:3314 git-gui.sh:3345
 msgid "Show More Context"
 msgstr "文脈を多く"
 
-#: git-gui.sh:2502 git-gui.sh:2838 git-gui.sh:2947
+#: git-gui.sh:2680 git-gui.sh:3024 git-gui.sh:3133
 msgid "Sign Off"
 msgstr "署名"
 
-#: git-gui.sh:2518
+#: git-gui.sh:2696
 msgid "Local Merge..."
 msgstr "ローカル・マージ…"
 
-#: git-gui.sh:2523
+#: git-gui.sh:2701
 msgid "Abort Merge..."
 msgstr "マージ中止…"
 
-#: git-gui.sh:2535 git-gui.sh:2575
+#: git-gui.sh:2713 git-gui.sh:2741
 msgid "Add..."
 msgstr "追加"
 
-#: git-gui.sh:2539
+#: git-gui.sh:2717
 msgid "Push..."
 msgstr "プッシュ…"
 
-#: git-gui.sh:2543
+#: git-gui.sh:2721
 msgid "Delete Branch..."
 msgstr "ブランチ削除..."
 
-#: git-gui.sh:2553 git-gui.sh:2589 lib/about.tcl:14
+#: git-gui.sh:2731 git-gui.sh:3292
+msgid "Options..."
+msgstr "オプション…"
+
+#: git-gui.sh:2742
+msgid "Remove..."
+msgstr "削除..."
+
+#: git-gui.sh:2751 lib/choose_repository.tcl:50
+msgid "Help"
+msgstr "ヘルプ"
+
+#: git-gui.sh:2755 git-gui.sh:2759 lib/about.tcl:14
 #: lib/choose_repository.tcl:44 lib/choose_repository.tcl:53
 #, tcl-format
 msgid "About %s"
 msgstr "%s について"
 
-#: git-gui.sh:2557
-msgid "Preferences..."
-msgstr "設定…"
-
-#: git-gui.sh:2565 git-gui.sh:3115
-msgid "Options..."
-msgstr "オプション…"
-
-#: git-gui.sh:2576
-msgid "Remove..."
-msgstr "削除..."
-
-#: git-gui.sh:2585 lib/choose_repository.tcl:50
-msgid "Help"
-msgstr "ヘルプ"
-
-#: git-gui.sh:2611
+#: git-gui.sh:2783
 msgid "Online Documentation"
 msgstr "オンライン・ドキュメント"
 
-#: git-gui.sh:2614 lib/choose_repository.tcl:47 lib/choose_repository.tcl:56
+#: git-gui.sh:2786 lib/choose_repository.tcl:47 lib/choose_repository.tcl:56
 msgid "Show SSH Key"
 msgstr "SSH キーを表示"
 
-#: git-gui.sh:2707
+#: git-gui.sh:2893
 #, tcl-format
 msgid "fatal: cannot stat path %s: No such file or directory"
 msgstr ""
 "致命的: パス %s が stat できません。そのようなファイルやディレクトリはありま"
 "せん"
 
-#: git-gui.sh:2740
+#: git-gui.sh:2926
 msgid "Current Branch:"
 msgstr "現在のブランチ"
 
-#: git-gui.sh:2761
+#: git-gui.sh:2947
 msgid "Staged Changes (Will Commit)"
 msgstr "ステージングされた(コミット予定済の)変更"
 
-#: git-gui.sh:2781
+#: git-gui.sh:2967
 msgid "Unstaged Changes"
 msgstr "コミット予定に入っていない変更"
 
-#: git-gui.sh:2831
+#: git-gui.sh:3017
 msgid "Stage Changed"
 msgstr "変更をコミット予定に入れる"
 
-#: git-gui.sh:2850 lib/transport.tcl:93 lib/transport.tcl:182
+#: git-gui.sh:3036 lib/transport.tcl:104 lib/transport.tcl:193
 msgid "Push"
 msgstr "プッシュ"
 
-#: git-gui.sh:2885
+#: git-gui.sh:3071
 msgid "Initial Commit Message:"
 msgstr "最初のコミットメッセージ:"
 
-#: git-gui.sh:2886
+#: git-gui.sh:3072
 msgid "Amended Commit Message:"
 msgstr "訂正したコミットメッセージ:"
 
-#: git-gui.sh:2887
+#: git-gui.sh:3073
 msgid "Amended Initial Commit Message:"
 msgstr "訂正した最初のコミットメッセージ:"
 
-#: git-gui.sh:2888
+#: git-gui.sh:3074
 msgid "Amended Merge Commit Message:"
 msgstr "訂正したマージコミットメッセージ:"
 
-#: git-gui.sh:2889
+#: git-gui.sh:3075
 msgid "Merge Commit Message:"
 msgstr "マージコミットメッセージ:"
 
-#: git-gui.sh:2890
+#: git-gui.sh:3076
 msgid "Commit Message:"
 msgstr "コミットメッセージ:"
 
-#: git-gui.sh:2939 git-gui.sh:3090 lib/console.tcl:73
+#: git-gui.sh:3125 git-gui.sh:3267 lib/console.tcl:73
 msgid "Copy All"
 msgstr "全てコピー"
 
-#: git-gui.sh:2963 lib/blame.tcl:104
+#: git-gui.sh:3149 lib/blame.tcl:104
 msgid "File:"
 msgstr "ファイル:"
 
-#: git-gui.sh:3078
+#: git-gui.sh:3255
 msgid "Refresh"
 msgstr "再読み込み"
 
-#: git-gui.sh:3099
+#: git-gui.sh:3276
 msgid "Decrease Font Size"
 msgstr "フォントを小さく"
 
-#: git-gui.sh:3103
+#: git-gui.sh:3280
 msgid "Increase Font Size"
 msgstr "フォントを大きく"
 
-#: git-gui.sh:3111 lib/blame.tcl:281
+#: git-gui.sh:3288 lib/blame.tcl:281
 msgid "Encoding"
 msgstr "エンコーディング"
 
-#: git-gui.sh:3122
+#: git-gui.sh:3299
 msgid "Apply/Reverse Hunk"
 msgstr "パッチを適用/取り消す"
 
-#: git-gui.sh:3127
+#: git-gui.sh:3304
 msgid "Apply/Reverse Line"
 msgstr "パッチ行を適用/取り消す"
 
-#: git-gui.sh:3137
+#: git-gui.sh:3323
 msgid "Run Merge Tool"
 msgstr "マージツールを起動"
 
-#: git-gui.sh:3142
+#: git-gui.sh:3328
 msgid "Use Remote Version"
 msgstr "リモートの方を採用"
 
-#: git-gui.sh:3146
+#: git-gui.sh:3332
 msgid "Use Local Version"
 msgstr "ローカルの方を採用"
 
-#: git-gui.sh:3150
+#: git-gui.sh:3336
 msgid "Revert To Base"
 msgstr "ベース版を採用"
 
-#: git-gui.sh:3169
+#: git-gui.sh:3354
+msgid "Visualize These Changes In The Submodule"
+msgstr "サブモジュール内のこれらの変更を見る"
+
+#: git-gui.sh:3358
+msgid "Visualize Current Branch History In The Submodule"
+msgstr "サブモジュール内で現在のブランチの履歴を見る"
+
+#: git-gui.sh:3362
+msgid "Visualize All Branch History In The Submodule"
+msgstr "サブモジュール内で全てのブランチの履歴を見る"
+
+#: git-gui.sh:3367
+msgid "Start git gui In The Submodule"
+msgstr "サブモジュール内でgit guiを起動する"
+
+#: git-gui.sh:3389
 msgid "Unstage Hunk From Commit"
 msgstr "パッチをコミット予定から外す"
 
-#: git-gui.sh:3170
+#: git-gui.sh:3391
+msgid "Unstage Lines From Commit"
+msgstr "コミット予定から行を外す"
+
+#: git-gui.sh:3393
 msgid "Unstage Line From Commit"
 msgstr "コミット予定から行を外す"
 
-#: git-gui.sh:3172
+#: git-gui.sh:3396
 msgid "Stage Hunk For Commit"
 msgstr "パッチをコミット予定に加える"
 
-#: git-gui.sh:3173
+#: git-gui.sh:3398
+msgid "Stage Lines For Commit"
+msgstr "パッチ行をコミット予定に加える"
+
+#: git-gui.sh:3400
 msgid "Stage Line For Commit"
 msgstr "パッチ行をコミット予定に加える"
 
-#: git-gui.sh:3196
+#: git-gui.sh:3424
 msgid "Initializing..."
 msgstr "初期化しています…"
 
-#: git-gui.sh:3301
+#: git-gui.sh:3541
 #, tcl-format
 msgid ""
 "Possible environment issues exist.\n"
@@ -515,7 +543,7 @@
 "以下の環境変数は %s が起動する Git サブプロセスによって無視されるでしょう:\n"
 "\n"
 
-#: git-gui.sh:3331
+#: git-gui.sh:3570
 msgid ""
 "\n"
 "This is due to a known issue with the\n"
@@ -525,7 +553,7 @@
 "これは Cygwin で配布されている Tcl バイナリに\n"
 "関しての既知の問題によります"
 
-#: git-gui.sh:3336
+#: git-gui.sh:3575
 #, tcl-format
 msgid ""
 "\n"
@@ -609,43 +637,43 @@
 msgid "Loading annotation..."
 msgstr "注釈を読み込んでいます…"
 
-#: lib/blame.tcl:964
+#: lib/blame.tcl:963
 msgid "Author:"
 msgstr "作者:"
 
-#: lib/blame.tcl:968
+#: lib/blame.tcl:967
 msgid "Committer:"
 msgstr "コミット者:"
 
-#: lib/blame.tcl:973
+#: lib/blame.tcl:972
 msgid "Original File:"
 msgstr "元ファイル"
 
-#: lib/blame.tcl:1021
+#: lib/blame.tcl:1020
 msgid "Cannot find HEAD commit:"
 msgstr "HEAD コミットが見つかりません"
 
-#: lib/blame.tcl:1076
+#: lib/blame.tcl:1075
 msgid "Cannot find parent commit:"
 msgstr "親コミットが見つかりません:"
 
-#: lib/blame.tcl:1091
+#: lib/blame.tcl:1090
 msgid "Unable to display parent"
 msgstr "親を表示できません"
 
-#: lib/blame.tcl:1092 lib/diff.tcl:297
+#: lib/blame.tcl:1091 lib/diff.tcl:320
 msgid "Error loading diff:"
 msgstr "diff を読む際のエラーです:"
 
-#: lib/blame.tcl:1232
+#: lib/blame.tcl:1231
 msgid "Originally By:"
 msgstr "原作者:"
 
-#: lib/blame.tcl:1238
+#: lib/blame.tcl:1237
 msgid "In File:"
 msgstr "ファイル:"
 
-#: lib/blame.tcl:1243
+#: lib/blame.tcl:1242
 msgid "Copied Or Moved Here By:"
 msgstr "複写・移動者:"
 
@@ -659,10 +687,10 @@
 
 #: lib/branch_checkout.tcl:27 lib/branch_create.tcl:35
 #: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:282
-#: lib/checkout_op.tcl:544 lib/choose_font.tcl:43 lib/merge.tcl:172
+#: lib/checkout_op.tcl:579 lib/choose_font.tcl:43 lib/merge.tcl:172
 #: lib/option.tcl:125 lib/remote_add.tcl:32 lib/remote_branch_delete.tcl:42
 #: lib/tools_dlg.tcl:40 lib/tools_dlg.tcl:204 lib/tools_dlg.tcl:352
-#: lib/transport.tcl:97
+#: lib/transport.tcl:108
 msgid "Cancel"
 msgstr "中止"
 
@@ -690,7 +718,7 @@
 msgid "Create New Branch"
 msgstr "ブランチを新規作成"
 
-#: lib/branch_create.tcl:31 lib/choose_repository.tcl:377
+#: lib/branch_create.tcl:31 lib/choose_repository.tcl:381
 msgid "Create"
 msgstr "作成"
 
@@ -722,7 +750,7 @@
 msgid "Fast Forward Only"
 msgstr "早送りのみ"
 
-#: lib/branch_create.tcl:85 lib/checkout_op.tcl:536
+#: lib/branch_create.tcl:85 lib/checkout_op.tcl:571
 msgid "Reset"
 msgstr "リセット"
 
@@ -764,15 +792,25 @@
 msgid "Delete Only If Merged Into"
 msgstr "マージ済みの時のみ削除"
 
-#: lib/branch_delete.tcl:54
-msgid "Always (Do not perform merge test.)"
-msgstr "無条件(マージテストしない)"
+#: lib/branch_delete.tcl:54 lib/remote_branch_delete.tcl:119
+msgid "Always (Do not perform merge checks)"
+msgstr "無条件(マージ検査をしない)"
 
 #: lib/branch_delete.tcl:103
 #, tcl-format
 msgid "The following branches are not completely merged into %s:"
 msgstr "以下のブランチは %s に完全にマージされていません:"
 
+#: lib/branch_delete.tcl:115 lib/remote_branch_delete.tcl:217
+msgid ""
+"Recovering deleted branches is difficult.\n"
+"\n"
+"Delete the selected branches?"
+msgstr ""
+"削除したブランチを回復するのは困難です。\n"
+"\n"
+"選択したブランチを削除して良いですか?"
+
 #: lib/branch_delete.tcl:141
 #, tcl-format
 msgid ""
@@ -802,7 +840,7 @@
 msgid "Please select a branch to rename."
 msgstr "名前を変更するブランチを選んで下さい。"
 
-#: lib/branch_rename.tcl:96 lib/checkout_op.tcl:201
+#: lib/branch_rename.tcl:96 lib/checkout_op.tcl:202
 #, tcl-format
 msgid "Branch '%s' already exists."
 msgstr "'%s'というブランチは既に存在します。"
@@ -833,38 +871,38 @@
 msgid "Browse Branch Files"
 msgstr "現在のブランチのファイルを見る"
 
-#: lib/browser.tcl:278 lib/choose_repository.tcl:394
-#: lib/choose_repository.tcl:480 lib/choose_repository.tcl:491
-#: lib/choose_repository.tcl:995
+#: lib/browser.tcl:278 lib/choose_repository.tcl:398
+#: lib/choose_repository.tcl:486 lib/choose_repository.tcl:497
+#: lib/choose_repository.tcl:1028
 msgid "Browse"
 msgstr "ブラウズ"
 
-#: lib/checkout_op.tcl:84
+#: lib/checkout_op.tcl:85
 #, tcl-format
 msgid "Fetching %s from %s"
 msgstr "%s から %s をフェッチしています"
 
-#: lib/checkout_op.tcl:132
+#: lib/checkout_op.tcl:133
 #, tcl-format
 msgid "fatal: Cannot resolve %s"
 msgstr "致命的エラー: %s を解決できません"
 
-#: lib/checkout_op.tcl:145 lib/console.tcl:81 lib/database.tcl:31
+#: lib/checkout_op.tcl:146 lib/console.tcl:81 lib/database.tcl:31
 #: lib/sshkey.tcl:53
 msgid "Close"
 msgstr "閉じる"
 
-#: lib/checkout_op.tcl:174
+#: lib/checkout_op.tcl:175
 #, tcl-format
 msgid "Branch '%s' does not exist."
 msgstr "ブランチ'%s'は存在しません。"
 
-#: lib/checkout_op.tcl:193
+#: lib/checkout_op.tcl:194
 #, tcl-format
 msgid "Failed to configure simplified git-pull for '%s'."
 msgstr "'%s' に簡易 git-pull を設定できませんでした"
 
-#: lib/checkout_op.tcl:228
+#: lib/checkout_op.tcl:229
 #, tcl-format
 msgid ""
 "Branch '%s' already exists.\n"
@@ -877,21 +915,21 @@
 "%s に早送りできません。\n"
 "マージが必要です。"
 
-#: lib/checkout_op.tcl:242
+#: lib/checkout_op.tcl:243
 #, tcl-format
 msgid "Merge strategy '%s' not supported."
 msgstr "'%s' マージ戦略はサポートされていません。"
 
-#: lib/checkout_op.tcl:261
+#: lib/checkout_op.tcl:262
 #, tcl-format
 msgid "Failed to update '%s'."
 msgstr "'%s' の更新に失敗しました。"
 
-#: lib/checkout_op.tcl:273
+#: lib/checkout_op.tcl:274
 msgid "Staging area (index) is already locked."
 msgstr "インデックスは既にロックされています。"
 
-#: lib/checkout_op.tcl:288
+#: lib/checkout_op.tcl:289
 msgid ""
 "Last scanned state does not match repository state.\n"
 "\n"
@@ -907,30 +945,30 @@
 "\n"
 "自動的に再スキャンを開始します。\n"
 
-#: lib/checkout_op.tcl:344
+#: lib/checkout_op.tcl:345
 #, tcl-format
 msgid "Updating working directory to '%s'..."
 msgstr "作業ディレクトリを '%s' に更新しています…"
 
-#: lib/checkout_op.tcl:345
+#: lib/checkout_op.tcl:346
 msgid "files checked out"
 msgstr "チェックアウトされたファイル"
 
-#: lib/checkout_op.tcl:375
+#: lib/checkout_op.tcl:376
 #, tcl-format
 msgid "Aborted checkout of '%s' (file level merging is required)."
 msgstr "'%s' のチェックアウトを中止しました(ファイル毎のマージが必要です)。"
 
-#: lib/checkout_op.tcl:376
+#: lib/checkout_op.tcl:377
 msgid "File level merge required."
 msgstr "ファイル毎のマージが必要です。"
 
-#: lib/checkout_op.tcl:380
+#: lib/checkout_op.tcl:381
 #, tcl-format
 msgid "Staying on branch '%s'."
 msgstr "ブランチ '%s' に滞まります。"
 
-#: lib/checkout_op.tcl:451
+#: lib/checkout_op.tcl:452
 msgid ""
 "You are no longer on a local branch.\n"
 "\n"
@@ -942,30 +980,30 @@
 "ブランチ上に滞まりたいときは、この「分離されたチェックアウト」から新規ブラン"
 "チを開始してください。"
 
-#: lib/checkout_op.tcl:468 lib/checkout_op.tcl:472
+#: lib/checkout_op.tcl:503 lib/checkout_op.tcl:507
 #, tcl-format
 msgid "Checked out '%s'."
 msgstr "'%s' をチェックアウトしました"
 
-#: lib/checkout_op.tcl:500
+#: lib/checkout_op.tcl:535
 #, tcl-format
 msgid "Resetting '%s' to '%s' will lose the following commits:"
 msgstr "'%s' を '%s' にリセットすると、以下のコミットが失なわれます:"
 
-#: lib/checkout_op.tcl:522
+#: lib/checkout_op.tcl:557
 msgid "Recovering lost commits may not be easy."
 msgstr "失なわれたコミットを回復するのは簡単ではありません。"
 
-#: lib/checkout_op.tcl:527
+#: lib/checkout_op.tcl:562
 #, tcl-format
 msgid "Reset '%s'?"
 msgstr "'%s' をリセットしますか?"
 
-#: lib/checkout_op.tcl:532 lib/merge.tcl:164 lib/tools_dlg.tcl:343
+#: lib/checkout_op.tcl:567 lib/merge.tcl:164 lib/tools_dlg.tcl:343
 msgid "Visualize"
 msgstr "可視化"
 
-#: lib/checkout_op.tcl:600
+#: lib/checkout_op.tcl:635
 #, tcl-format
 msgid ""
 "Failed to set current branch.\n"
@@ -1009,7 +1047,7 @@
 msgid "Git Gui"
 msgstr "Git GUI"
 
-#: lib/choose_repository.tcl:87 lib/choose_repository.tcl:382
+#: lib/choose_repository.tcl:87 lib/choose_repository.tcl:386
 msgid "Create New Repository"
 msgstr "新しいリポジトリを作る"
 
@@ -1017,7 +1055,7 @@
 msgid "New..."
 msgstr "新規…"
 
-#: lib/choose_repository.tcl:100 lib/choose_repository.tcl:465
+#: lib/choose_repository.tcl:100 lib/choose_repository.tcl:471
 msgid "Clone Existing Repository"
 msgstr "既存リポジトリを複製する"
 
@@ -1025,7 +1063,7 @@
 msgid "Clone..."
 msgstr "複製…"
 
-#: lib/choose_repository.tcl:113 lib/choose_repository.tcl:983
+#: lib/choose_repository.tcl:113 lib/choose_repository.tcl:1016
 msgid "Open Existing Repository"
 msgstr "既存リポジトリを開く"
 
@@ -1041,193 +1079,193 @@
 msgid "Open Recent Repository:"
 msgstr "最近使ったリポジトリを開く"
 
-#: lib/choose_repository.tcl:302 lib/choose_repository.tcl:309
-#: lib/choose_repository.tcl:316
+#: lib/choose_repository.tcl:306 lib/choose_repository.tcl:313
+#: lib/choose_repository.tcl:320
 #, tcl-format
 msgid "Failed to create repository %s:"
 msgstr "リポジトリ %s を作製できません:"
 
-#: lib/choose_repository.tcl:387
+#: lib/choose_repository.tcl:391
 msgid "Directory:"
 msgstr "ディレクトリ:"
 
-#: lib/choose_repository.tcl:417 lib/choose_repository.tcl:544
-#: lib/choose_repository.tcl:1017
+#: lib/choose_repository.tcl:423 lib/choose_repository.tcl:550
+#: lib/choose_repository.tcl:1052
 msgid "Git Repository"
 msgstr "GIT リポジトリ"
 
-#: lib/choose_repository.tcl:442
+#: lib/choose_repository.tcl:448
 #, tcl-format
 msgid "Directory %s already exists."
 msgstr "ディレクトリ '%s' は既に存在します。"
 
-#: lib/choose_repository.tcl:446
+#: lib/choose_repository.tcl:452
 #, tcl-format
 msgid "File %s already exists."
 msgstr "ファイル '%s' は既に存在します。"
 
-#: lib/choose_repository.tcl:460
+#: lib/choose_repository.tcl:466
 msgid "Clone"
 msgstr "複製"
 
-#: lib/choose_repository.tcl:473
+#: lib/choose_repository.tcl:479
 msgid "Source Location:"
 msgstr "ソースの位置"
 
-#: lib/choose_repository.tcl:484
+#: lib/choose_repository.tcl:490
 msgid "Target Directory:"
 msgstr "先ディレクトリ:"
 
-#: lib/choose_repository.tcl:496
+#: lib/choose_repository.tcl:502
 msgid "Clone Type:"
 msgstr "複製方式:"
 
-#: lib/choose_repository.tcl:502
+#: lib/choose_repository.tcl:508
 msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
 msgstr "標準(高速・中冗長度・ハードリンク)"
 
-#: lib/choose_repository.tcl:508
+#: lib/choose_repository.tcl:514
 msgid "Full Copy (Slower, Redundant Backup)"
 msgstr "全複写(低速・冗長バックアップ)"
 
-#: lib/choose_repository.tcl:514
+#: lib/choose_repository.tcl:520
 msgid "Shared (Fastest, Not Recommended, No Backup)"
 msgstr "共有(最高速・非推奨・バックアップ無し)"
 
-#: lib/choose_repository.tcl:550 lib/choose_repository.tcl:597
-#: lib/choose_repository.tcl:743 lib/choose_repository.tcl:813
-#: lib/choose_repository.tcl:1023 lib/choose_repository.tcl:1031
+#: lib/choose_repository.tcl:556 lib/choose_repository.tcl:603
+#: lib/choose_repository.tcl:749 lib/choose_repository.tcl:819
+#: lib/choose_repository.tcl:1058 lib/choose_repository.tcl:1066
 #, tcl-format
 msgid "Not a Git repository: %s"
 msgstr "Git リポジトリではありません: %s"
 
-#: lib/choose_repository.tcl:586
+#: lib/choose_repository.tcl:592
 msgid "Standard only available for local repository."
 msgstr "標準方式は同一計算機上のリポジトリにのみ使えます。"
 
-#: lib/choose_repository.tcl:590
+#: lib/choose_repository.tcl:596
 msgid "Shared only available for local repository."
 msgstr "共有方式は同一計算機上のリポジトリにのみ使えます。"
 
-#: lib/choose_repository.tcl:611
+#: lib/choose_repository.tcl:617
 #, tcl-format
 msgid "Location %s already exists."
 msgstr "'%s' は既に存在します。"
 
-#: lib/choose_repository.tcl:622
+#: lib/choose_repository.tcl:628
 msgid "Failed to configure origin"
 msgstr "origin を設定できませんでした"
 
-#: lib/choose_repository.tcl:634
+#: lib/choose_repository.tcl:640
 msgid "Counting objects"
 msgstr "オブジェクトを数えています"
 
-#: lib/choose_repository.tcl:635
+#: lib/choose_repository.tcl:641
 msgid "buckets"
 msgstr "バケツ"
 
-#: lib/choose_repository.tcl:659
+#: lib/choose_repository.tcl:665
 #, tcl-format
 msgid "Unable to copy objects/info/alternates: %s"
 msgstr "objects/info/alternates を複写できません: %s"
 
-#: lib/choose_repository.tcl:695
+#: lib/choose_repository.tcl:701
 #, tcl-format
 msgid "Nothing to clone from %s."
 msgstr "%s から複製する内容はありません"
 
-#: lib/choose_repository.tcl:697 lib/choose_repository.tcl:911
-#: lib/choose_repository.tcl:923
+#: lib/choose_repository.tcl:703 lib/choose_repository.tcl:917
+#: lib/choose_repository.tcl:929
 msgid "The 'master' branch has not been initialized."
 msgstr "'master' ブランチが初期化されていません"
 
-#: lib/choose_repository.tcl:710
+#: lib/choose_repository.tcl:716
 msgid "Hardlinks are unavailable.  Falling back to copying."
 msgstr "ハードリンクが作れないので、コピーします"
 
-#: lib/choose_repository.tcl:722
+#: lib/choose_repository.tcl:728
 #, tcl-format
 msgid "Cloning from %s"
 msgstr "%s から複製しています"
 
-#: lib/choose_repository.tcl:753
+#: lib/choose_repository.tcl:759
 msgid "Copying objects"
 msgstr "オブジェクトを複写しています"
 
-#: lib/choose_repository.tcl:754
+#: lib/choose_repository.tcl:760
 msgid "KiB"
 msgstr "KiB"
 
-#: lib/choose_repository.tcl:778
+#: lib/choose_repository.tcl:784
 #, tcl-format
 msgid "Unable to copy object: %s"
 msgstr "オブジェクトを複写できません: %s"
 
-#: lib/choose_repository.tcl:788
+#: lib/choose_repository.tcl:794
 msgid "Linking objects"
 msgstr "オブジェクトを連結しています"
 
-#: lib/choose_repository.tcl:789
+#: lib/choose_repository.tcl:795
 msgid "objects"
 msgstr "オブジェクト"
 
-#: lib/choose_repository.tcl:797
+#: lib/choose_repository.tcl:803
 #, tcl-format
 msgid "Unable to hardlink object: %s"
 msgstr "オブジェクトをハードリンクできません: %s"
 
-#: lib/choose_repository.tcl:852
+#: lib/choose_repository.tcl:858
 msgid "Cannot fetch branches and objects.  See console output for details."
 msgstr "ブランチやオブジェクトを取得できません。コンソール出力を見て下さい"
 
-#: lib/choose_repository.tcl:863
+#: lib/choose_repository.tcl:869
 msgid "Cannot fetch tags.  See console output for details."
 msgstr "タグを取得できません。コンソール出力を見て下さい"
 
-#: lib/choose_repository.tcl:887
+#: lib/choose_repository.tcl:893
 msgid "Cannot determine HEAD.  See console output for details."
 msgstr "HEAD を確定できません。コンソール出力を見て下さい"
 
-#: lib/choose_repository.tcl:896
+#: lib/choose_repository.tcl:902
 #, tcl-format
 msgid "Unable to cleanup %s"
 msgstr "%s を掃除できません"
 
-#: lib/choose_repository.tcl:902
+#: lib/choose_repository.tcl:908
 msgid "Clone failed."
 msgstr "複写に失敗しました。"
 
-#: lib/choose_repository.tcl:909
+#: lib/choose_repository.tcl:915
 msgid "No default branch obtained."
 msgstr "デフォールト・ブランチが取得されませんでした"
 
-#: lib/choose_repository.tcl:920
+#: lib/choose_repository.tcl:926
 #, tcl-format
 msgid "Cannot resolve %s as a commit."
 msgstr "%s をコミットとして解釈できません"
 
-#: lib/choose_repository.tcl:932
+#: lib/choose_repository.tcl:938
 msgid "Creating working directory"
 msgstr "作業ディレクトリを作成しています"
 
-#: lib/choose_repository.tcl:933 lib/index.tcl:65 lib/index.tcl:128
-#: lib/index.tcl:196
+#: lib/choose_repository.tcl:939 lib/index.tcl:67 lib/index.tcl:130
+#: lib/index.tcl:198
 msgid "files"
 msgstr "ファイル"
 
-#: lib/choose_repository.tcl:962
+#: lib/choose_repository.tcl:968
 msgid "Initial file checkout failed."
 msgstr "初期チェックアウトに失敗しました"
 
-#: lib/choose_repository.tcl:978
+#: lib/choose_repository.tcl:1011
 msgid "Open"
 msgstr "開く"
 
-#: lib/choose_repository.tcl:988
+#: lib/choose_repository.tcl:1021
 msgid "Repository:"
 msgstr "リポジトリ:"
 
-#: lib/choose_repository.tcl:1037
+#: lib/choose_repository.tcl:1072
 #, tcl-format
 msgid "Failed to open repository %s:"
 msgstr "リポジトリ %s を開けません:"
@@ -1298,19 +1336,24 @@
 "現在はまだマージの途中です。先にこのマージを中止しないと、前のコミットの訂正"
 "はできません\n"
 
-#: lib/commit.tcl:49
+#: lib/commit.tcl:48
 msgid "Error loading commit data for amend:"
 msgstr "訂正するコミットのデータを読めません:"
 
-#: lib/commit.tcl:76
+#: lib/commit.tcl:75
 msgid "Unable to obtain your identity:"
 msgstr "ユーザの正体を確認できません:"
 
-#: lib/commit.tcl:81
+#: lib/commit.tcl:80
 msgid "Invalid GIT_COMMITTER_IDENT:"
 msgstr "GIT_COMMITTER_IDENT が無効です:"
 
-#: lib/commit.tcl:133
+#: lib/commit.tcl:129
+#, tcl-format
+msgid "warning: Tcl does not support encoding '%s'."
+msgstr "警告: Tcl はエンコーディング '%s' をサポートしていません"
+
+#: lib/commit.tcl:149
 msgid ""
 "Last scanned state does not match repository state.\n"
 "\n"
@@ -1326,7 +1369,7 @@
 "\n"
 "自動的に再スキャンを開始します。\n"
 
-#: lib/commit.tcl:156
+#: lib/commit.tcl:172
 #, tcl-format
 msgid ""
 "Unmerged files cannot be committed.\n"
@@ -1339,7 +1382,7 @@
 "ファイル %s にはマージ衝突が残っています。まず解決してコミット予定に加える必"
 "要があります。\n"
 
-#: lib/commit.tcl:164
+#: lib/commit.tcl:180
 #, tcl-format
 msgid ""
 "Unknown file state %s detected.\n"
@@ -1350,7 +1393,7 @@
 "\n"
 "ファイル %s は本プログラムではコミットできません。\n"
 
-#: lib/commit.tcl:172
+#: lib/commit.tcl:188
 msgid ""
 "No changes to commit.\n"
 "\n"
@@ -1360,7 +1403,7 @@
 "\n"
 "最低一つの変更をコミット予定に加えてからコミットして下さい。\n"
 
-#: lib/commit.tcl:187
+#: lib/commit.tcl:203
 msgid ""
 "Please supply a commit message.\n"
 "\n"
@@ -1378,45 +1421,40 @@
 "- 第2行: 空白\n"
 "- 残りの行: なぜ、この変更が良い変更か、の説明。\n"
 
-#: lib/commit.tcl:211
-#, tcl-format
-msgid "warning: Tcl does not support encoding '%s'."
-msgstr "警告: Tcl はエンコーディング '%s' をサポートしていません"
-
-#: lib/commit.tcl:227
+#: lib/commit.tcl:234
 msgid "Calling pre-commit hook..."
 msgstr "コミット前フックを実行中・・・"
 
-#: lib/commit.tcl:242
+#: lib/commit.tcl:249
 msgid "Commit declined by pre-commit hook."
 msgstr "コミット前フックがコミットを拒否しました"
 
-#: lib/commit.tcl:265
+#: lib/commit.tcl:272
 msgid "Calling commit-msg hook..."
 msgstr "コミット・メッセージ・フックを実行中・・・"
 
-#: lib/commit.tcl:280
+#: lib/commit.tcl:287
 msgid "Commit declined by commit-msg hook."
 msgstr "コミット・メッセージ・フックがコミットを拒否しました"
 
-#: lib/commit.tcl:293
+#: lib/commit.tcl:300
 msgid "Committing changes..."
 msgstr "変更点をコミット中・・・"
 
-#: lib/commit.tcl:309
+#: lib/commit.tcl:316
 msgid "write-tree failed:"
 msgstr "write-tree が失敗しました:"
 
-#: lib/commit.tcl:310 lib/commit.tcl:354 lib/commit.tcl:374
+#: lib/commit.tcl:317 lib/commit.tcl:361 lib/commit.tcl:382
 msgid "Commit failed."
 msgstr "コミットに失敗しました。"
 
-#: lib/commit.tcl:327
+#: lib/commit.tcl:334
 #, tcl-format
 msgid "Commit %s appears to be corrupt"
 msgstr "コミット %s は壊れています"
 
-#: lib/commit.tcl:332
+#: lib/commit.tcl:339
 msgid ""
 "No changes to commit.\n"
 "\n"
@@ -1430,19 +1468,19 @@
 "\n"
 "自動的に再スキャンを開始します。\n"
 
-#: lib/commit.tcl:339
+#: lib/commit.tcl:346
 msgid "No changes to commit."
 msgstr "コミットする変更がありません。"
 
-#: lib/commit.tcl:353
+#: lib/commit.tcl:360
 msgid "commit-tree failed:"
 msgstr "commit-tree が失敗しました:"
 
-#: lib/commit.tcl:373
+#: lib/commit.tcl:381
 msgid "update-ref failed:"
 msgstr "update-ref が失敗しました:"
 
-#: lib/commit.tcl:461
+#: lib/commit.tcl:469
 #, tcl-format
 msgid "Created commit %s: %s"
 msgstr "コミット %s を作成しました: %s"
@@ -1495,20 +1533,19 @@
 msgid "Verifying the object database with fsck-objects"
 msgstr "fsck-objects でオブジェクト・データベースを検証しています"
 
-#: lib/database.tcl:108
+#: lib/database.tcl:107
 #, tcl-format
 msgid ""
 "This repository currently has approximately %i loose objects.\n"
 "\n"
 "To maintain optimal performance it is strongly recommended that you compress "
-"the database when more than %i loose objects exist.\n"
+"the database.\n"
 "\n"
 "Compress the database now?"
 msgstr ""
 "このリポジトリにはおおよそ %i 個の個別オブジェクトがあります\n"
 "\n"
-"最適な性能を保つために、%i 個以上の個別オブジェクトを作る毎にデータベースを圧"
-"縮することを推奨します\n"
+"最適な性能を保つために、データベースを圧縮することを推奨します\n"
 "\n"
 "データベースを圧縮しますか?"
 
@@ -1517,7 +1554,7 @@
 msgid "Invalid date from Git: %s"
 msgstr "Git から出た無効な日付: %s"
 
-#: lib/diff.tcl:59
+#: lib/diff.tcl:64
 #, tcl-format
 msgid ""
 "No differences detected.\n"
@@ -1539,12 +1576,12 @@
 "\n"
 "同様な状態のファイルを探すために、自動的に再スキャンを開始します。"
 
-#: lib/diff.tcl:99
+#: lib/diff.tcl:104
 #, tcl-format
 msgid "Loading diff of %s..."
 msgstr "%s の変更点をロード中…"
 
-#: lib/diff.tcl:120
+#: lib/diff.tcl:125
 msgid ""
 "LOCAL: deleted\n"
 "REMOTE:\n"
@@ -1552,7 +1589,7 @@
 "LOCAL: 削除\n"
 "Remote:\n"
 
-#: lib/diff.tcl:125
+#: lib/diff.tcl:130
 msgid ""
 "REMOTE: deleted\n"
 "LOCAL:\n"
@@ -1560,32 +1597,32 @@
 "REMOTE: 削除\n"
 "LOCAL:\n"
 
-#: lib/diff.tcl:132
+#: lib/diff.tcl:137
 msgid "LOCAL:\n"
 msgstr "LOCAL:\n"
 
-#: lib/diff.tcl:135
+#: lib/diff.tcl:140
 msgid "REMOTE:\n"
 msgstr "REMOTE\n"
 
-#: lib/diff.tcl:197 lib/diff.tcl:296
+#: lib/diff.tcl:202 lib/diff.tcl:319
 #, tcl-format
 msgid "Unable to display %s"
 msgstr "%s を表示できません"
 
-#: lib/diff.tcl:198
+#: lib/diff.tcl:203
 msgid "Error loading file:"
 msgstr "ファイルを読む際のエラーです:"
 
-#: lib/diff.tcl:205
+#: lib/diff.tcl:210
 msgid "Git Repository (subproject)"
 msgstr "Git リポジトリ(サブプロジェクト)"
 
-#: lib/diff.tcl:217
+#: lib/diff.tcl:222
 msgid "* Binary file (not showing content)."
 msgstr "* バイナリファイル(内容は表示しません)"
 
-#: lib/diff.tcl:222
+#: lib/diff.tcl:227
 #, tcl-format
 msgid ""
 "* Untracked file is %d bytes.\n"
@@ -1594,7 +1631,7 @@
 "* 管理外のファイルの大きさは %d バイトです。\n"
 "* 最初の %d バイトだけ表示しています。\n"
 
-#: lib/diff.tcl:228
+#: lib/diff.tcl:233
 #, tcl-format
 msgid ""
 "\n"
@@ -1606,19 +1643,19 @@
 "* %s は管理外のファイルをここで切りおとしました。\n"
 "* 全体を見るには外部エディタを使ってください。\n"
 
-#: lib/diff.tcl:436
+#: lib/diff.tcl:482
 msgid "Failed to unstage selected hunk."
 msgstr "選択されたパッチをコミット予定から外せません。"
 
-#: lib/diff.tcl:443
+#: lib/diff.tcl:489
 msgid "Failed to stage selected hunk."
 msgstr "選択されたパッチをコミット予定に加えられません。"
 
-#: lib/diff.tcl:509
+#: lib/diff.tcl:568
 msgid "Failed to unstage selected line."
 msgstr "選択されたパッチ行をコミット予定から外せません。"
 
-#: lib/diff.tcl:517
+#: lib/diff.tcl:576
 msgid "Failed to stage selected line."
 msgstr "選択されたパッチ行をコミット予定に加えられません。"
 
@@ -1655,7 +1692,7 @@
 msgid "Index Error"
 msgstr "索引エラー"
 
-#: lib/index.tcl:21
+#: lib/index.tcl:17
 msgid ""
 "Updating the Git index failed.  A rescan will be automatically started to "
 "resynchronize git-gui."
@@ -1663,7 +1700,7 @@
 "GIT インデックスの更新が失敗しました。git-gui と同期をとるために再スキャンし"
 "ます。"
 
-#: lib/index.tcl:27
+#: lib/index.tcl:28
 msgid "Continue"
 msgstr "続行"
 
@@ -1671,43 +1708,43 @@
 msgid "Unlock Index"
 msgstr "インデックスのロック解除"
 
-#: lib/index.tcl:287
+#: lib/index.tcl:289
 #, tcl-format
 msgid "Unstaging %s from commit"
 msgstr "コミットから '%s' を降ろす"
 
-#: lib/index.tcl:326
+#: lib/index.tcl:328
 msgid "Ready to commit."
 msgstr "コミット準備完了"
 
-#: lib/index.tcl:339
+#: lib/index.tcl:341
 #, tcl-format
 msgid "Adding %s"
 msgstr "コミットに %s を加えています"
 
-#: lib/index.tcl:396
+#: lib/index.tcl:398
 #, tcl-format
 msgid "Revert changes in file %s?"
 msgstr "ファイル %s にした変更を元に戻しますか?"
 
-#: lib/index.tcl:398
+#: lib/index.tcl:400
 #, tcl-format
 msgid "Revert changes in these %i files?"
 msgstr "これら %i 個のファイルにした変更を元に戻しますか?"
 
-#: lib/index.tcl:406
+#: lib/index.tcl:408
 msgid "Any unstaged changes will be permanently lost by the revert."
 msgstr "変更を元に戻すとコミット予定していない変更は全て失われます。"
 
-#: lib/index.tcl:409
+#: lib/index.tcl:411
 msgid "Do Nothing"
 msgstr "何もしない"
 
-#: lib/index.tcl:427
+#: lib/index.tcl:429
 msgid "Reverting selected files"
 msgstr "選択されたファイルにした変更を元に戻します"
 
-#: lib/index.tcl:431
+#: lib/index.tcl:433
 #, tcl-format
 msgid "Reverting %s"
 msgstr "%s にした変更を元に戻します"
@@ -1883,7 +1920,8 @@
 #: lib/mergetool.tcl:45
 #, tcl-format
 msgid "File %s seems to have unresolved conflicts, still stage?"
-msgstr "ファイル %s には解決していない競合部分がまだあるようですが、いいですか?"
+msgstr ""
+"ファイル %s には解決していない競合部分がまだあるようですが、いいですか?"
 
 #: lib/mergetool.tcl:60
 #, tcl-format
@@ -2141,7 +2179,8 @@
 msgid "Do not know how to initialize repository at location '%s'."
 msgstr "リポジトリ '%s' を初期化できません。"
 
-#: lib/remote_add.tcl:163 lib/transport.tcl:25 lib/transport.tcl:71
+#: lib/remote_add.tcl:163 lib/transport.tcl:25 lib/transport.tcl:63
+#: lib/transport.tcl:81
 #, tcl-format
 msgid "push %s"
 msgstr "%s をプッシュ"
@@ -2159,11 +2198,11 @@
 msgid "From Repository"
 msgstr "元のリポジトリ"
 
-#: lib/remote_branch_delete.tcl:50 lib/transport.tcl:123
+#: lib/remote_branch_delete.tcl:50 lib/transport.tcl:134
 msgid "Remote:"
 msgstr "リモート:"
 
-#: lib/remote_branch_delete.tcl:66 lib/transport.tcl:138
+#: lib/remote_branch_delete.tcl:66 lib/transport.tcl:149
 msgid "Arbitrary Location:"
 msgstr "任意の位置:"
 
@@ -2179,10 +2218,6 @@
 msgid "Merged Into:"
 msgstr "マージ先:"
 
-#: lib/remote_branch_delete.tcl:119
-msgid "Always (Do not perform merge checks)"
-msgstr "無条件(マージ検査をしない)"
-
 #: lib/remote_branch_delete.tcl:152
 msgid "A branch is required for 'Merged Into'."
 msgstr "'マージ先' にはブランチが必要です。"
@@ -2211,26 +2246,16 @@
 msgid "Please select one or more branches to delete."
 msgstr "削除するブランチを選択して下さい。"
 
-#: lib/remote_branch_delete.tcl:216
-msgid ""
-"Recovering deleted branches is difficult.\n"
-"\n"
-"Delete the selected branches?"
-msgstr ""
-"削除したブランチを回復するのは困難です。\n"
-"\n"
-"選択したブランチを削除して良いですか?"
-
 #: lib/remote_branch_delete.tcl:226
 #, tcl-format
 msgid "Deleting branches from %s"
 msgstr "%s からブランチを削除しています。"
 
-#: lib/remote_branch_delete.tcl:286
+#: lib/remote_branch_delete.tcl:292
 msgid "No repository selected."
 msgstr "リポジトリが選択されていません。"
 
-#: lib/remote_branch_delete.tcl:291
+#: lib/remote_branch_delete.tcl:297
 #, tcl-format
 msgid "Scanning %s..."
 msgstr "%s をスキャンしています…"
@@ -2251,11 +2276,11 @@
 msgid "Case-Sensitive"
 msgstr "大文字小文字を区別"
 
-#: lib/shortcut.tcl:20 lib/shortcut.tcl:61
+#: lib/shortcut.tcl:21 lib/shortcut.tcl:62
 msgid "Cannot write shortcut:"
 msgstr "ショートカットが書けません:"
 
-#: lib/shortcut.tcl:136
+#: lib/shortcut.tcl:137
 msgid "Cannot write icon:"
 msgstr "アイコンが書けません:"
 
@@ -2501,30 +2526,30 @@
 msgid "Pushing %s %s to %s"
 msgstr "%3$s へ %1$s %2$s をプッシュしています"
 
-#: lib/transport.tcl:89
+#: lib/transport.tcl:100
 msgid "Push Branches"
 msgstr "ブランチをプッシュ"
 
-#: lib/transport.tcl:103
+#: lib/transport.tcl:114
 msgid "Source Branches"
 msgstr "元のブランチ"
 
-#: lib/transport.tcl:120
+#: lib/transport.tcl:131
 msgid "Destination Repository"
 msgstr "送り先リポジトリ"
 
-#: lib/transport.tcl:158
+#: lib/transport.tcl:169
 msgid "Transfer Options"
 msgstr "通信オプション"
 
-#: lib/transport.tcl:160
+#: lib/transport.tcl:171
 msgid "Force overwrite existing branch (may discard changes)"
 msgstr "既存ブランチを上書き(変更を破棄する可能性があります)"
 
-#: lib/transport.tcl:164
+#: lib/transport.tcl:175
 msgid "Use thin pack (for slow network connections)"
 msgstr "Thin Pack を使う(遅いネットワーク接続)"
 
-#: lib/transport.tcl:168
+#: lib/transport.tcl:179
 msgid "Include tags"
 msgstr "タグを含める"
diff --git a/git-gui/po/pt_br.po b/git-gui/po/pt_br.po
new file mode 100644
index 0000000..b175b97
--- /dev/null
+++ b/git-gui/po/pt_br.po
@@ -0,0 +1,2568 @@
+# Translation of git-gui to Brazilian Portuguese
+# Copyright (C) 2007 Shawn Pearce, et al.
+# This file is distributed under the same license as the git-gui package.
+#
+# Alexandre Erwin Ittner <alexandre@ittner.com.br>, 2010.
+msgid ""
+msgstr ""
+"Project-Id-Version: git-gui\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-01-26 15:47-0800\n"
+"PO-Revision-Date: 2010-09-18 11:09-0300\n"
+"Last-Translator: Alexandre Erwin Ittner <alexandre@ittner.com.br>\n"
+"Language-Team: Brazilian Portuguese <>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: git-gui.sh:41 git-gui.sh:793 git-gui.sh:807 git-gui.sh:820 git-gui.sh:903
+#: git-gui.sh:922
+msgid "git-gui: fatal error"
+msgstr "git-gui: erro fatal"
+
+#: git-gui.sh:743
+#, tcl-format
+msgid "Invalid font specified in %s:"
+msgstr "Fonte inválida indicada em %s:"
+
+#: git-gui.sh:779
+msgid "Main Font"
+msgstr "Fonte principal"
+
+#: git-gui.sh:780
+msgid "Diff/Console Font"
+msgstr "Fonte para o diff/console"
+
+#: git-gui.sh:794
+msgid "Cannot find git in PATH."
+msgstr "Impossível encontrar o git no \"PATH\""
+
+#: git-gui.sh:821
+msgid "Cannot parse Git version string:"
+msgstr "Impossível interpretar a versão do git:"
+
+#: git-gui.sh:839
+#, tcl-format
+msgid ""
+"Git version cannot be determined.\n"
+"\n"
+"%s claims it is version '%s'.\n"
+"\n"
+"%s requires at least Git 1.5.0 or later.\n"
+"\n"
+"Assume '%s' is version 1.5.0?\n"
+msgstr ""
+"Não foi possível determinar a versão do git:\n"
+"\n"
+"%s afirmar que sua versão é \"%s\".\n"
+"\n"
+"%s exige o Git 1.5.0 ou posterior.\n"
+"\n"
+"Assumir que '%s' é a versão 1.5.0?\n"
+
+#: git-gui.sh:1128
+msgid "Git directory not found:"
+msgstr "Diretório do Git não encontrado:"
+
+#: git-gui.sh:1146
+msgid "Cannot move to top of working directory:"
+msgstr "Impossível mover para o início do diretório de trabalho:"
+
+#: git-gui.sh:1154
+msgid "Cannot use bare repository:"
+msgstr "Impossível usar repositório puro:"
+
+#: git-gui.sh:1162
+msgid "No working directory"
+msgstr "Sem diretório de trabalho"
+
+#: git-gui.sh:1334 lib/checkout_op.tcl:306
+msgid "Refreshing file status..."
+msgstr "Atualizando estado dos arquivos..."
+
+#: git-gui.sh:1390
+msgid "Scanning for modified files ..."
+msgstr "Procurando por arquivos modificados ..."
+
+#: git-gui.sh:1454
+msgid "Calling prepare-commit-msg hook..."
+msgstr "Executando hook \"prepare-commit-msg\"..."
+
+#: git-gui.sh:1471
+msgid "Commit declined by prepare-commit-msg hook."
+msgstr "O script \"prepare-commit-msg\" negou a criação de uma nova revisão"
+
+#: git-gui.sh:1629 lib/browser.tcl:246
+msgid "Ready."
+msgstr "Pronto."
+
+#: git-gui.sh:1787
+#, tcl-format
+msgid "Displaying only %s of %s files."
+msgstr "Exibindo apenas %s de %s arquivos."
+
+#: git-gui.sh:1913
+msgid "Unmodified"
+msgstr "Não modificado"
+
+#: git-gui.sh:1915
+msgid "Modified, not staged"
+msgstr "Modificado, não marcado"
+
+#: git-gui.sh:1916 git-gui.sh:1924
+msgid "Staged for commit"
+msgstr "Marcado para uma nova revisão"
+
+#: git-gui.sh:1917 git-gui.sh:1925
+msgid "Portions staged for commit"
+msgstr "Trechos marcados para revisão"
+
+#: git-gui.sh:1918 git-gui.sh:1926
+msgid "Staged for commit, missing"
+msgstr "Marcado para revisão, faltando"
+
+#: git-gui.sh:1920
+msgid "File type changed, not staged"
+msgstr "Tipo do arquivo modificado, não marcado"
+
+#: git-gui.sh:1921
+msgid "File type changed, staged"
+msgstr "Tipo do arquivo modificado, marcado"
+
+#: git-gui.sh:1923
+msgid "Untracked, not staged"
+msgstr "Não monitorado, não marcado"
+
+#: git-gui.sh:1928
+msgid "Missing"
+msgstr "Faltando"
+
+#: git-gui.sh:1929
+msgid "Staged for removal"
+msgstr "Marcado para remoção"
+
+#: git-gui.sh:1930
+msgid "Staged for removal, still present"
+msgstr "Marcado para remoção, ainda presente"
+
+#: git-gui.sh:1932 git-gui.sh:1933 git-gui.sh:1934 git-gui.sh:1935
+#: git-gui.sh:1936 git-gui.sh:1937
+msgid "Requires merge resolution"
+msgstr "Requer resolução de conflitos"
+
+#: git-gui.sh:1972
+msgid "Starting gitk... please wait..."
+msgstr "Iniciando gitk... Aguarde..."
+
+#: git-gui.sh:1984
+msgid "Couldn't find gitk in PATH"
+msgstr "Impossível encontrar o gitk no PATH"
+
+#: git-gui.sh:2043
+msgid "Couldn't find git gui in PATH"
+msgstr "Impossível encontrar o \"git gui\" no PATH"
+
+#: git-gui.sh:2455 lib/choose_repository.tcl:36
+msgid "Repository"
+msgstr "Repositório"
+
+#: git-gui.sh:2456
+msgid "Edit"
+msgstr "Editar"
+
+#: git-gui.sh:2458 lib/choose_rev.tcl:561
+msgid "Branch"
+msgstr "Ramo"
+
+#: git-gui.sh:2461 lib/choose_rev.tcl:548
+msgid "Commit@@noun"
+msgstr "Revisão"
+
+#: git-gui.sh:2464 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
+msgid "Merge"
+msgstr "Mesclar"
+
+#: git-gui.sh:2465 lib/choose_rev.tcl:557
+msgid "Remote"
+msgstr "Remoto"
+
+#: git-gui.sh:2468
+msgid "Tools"
+msgstr "Ferramentas"
+
+#: git-gui.sh:2477
+msgid "Explore Working Copy"
+msgstr "Explorar cópia de trabalho"
+
+#: git-gui.sh:2483
+msgid "Browse Current Branch's Files"
+msgstr "Explorar arquivos do ramo atual"
+
+#: git-gui.sh:2487
+msgid "Browse Branch Files..."
+msgstr "Explorar arquivos do ramo..."
+
+#: git-gui.sh:2492
+msgid "Visualize Current Branch's History"
+msgstr "Visualizar histórico do ramo atual"
+
+#: git-gui.sh:2496
+msgid "Visualize All Branch History"
+msgstr "Visualizar histórico de todos os ramos"
+
+#: git-gui.sh:2503
+#, tcl-format
+msgid "Browse %s's Files"
+msgstr "Explorar arquivos de %s"
+
+#: git-gui.sh:2505
+#, tcl-format
+msgid "Visualize %s's History"
+msgstr "Visualizar histórico de %s"
+
+#: git-gui.sh:2510 lib/database.tcl:27 lib/database.tcl:67
+msgid "Database Statistics"
+msgstr "Estatísticas do banco de dados"
+
+#: git-gui.sh:2513 lib/database.tcl:34
+msgid "Compress Database"
+msgstr "Compactar banco de dados"
+
+#: git-gui.sh:2516
+msgid "Verify Database"
+msgstr "Verificar banco de dados"
+
+#: git-gui.sh:2523 git-gui.sh:2527 git-gui.sh:2531 lib/shortcut.tcl:8
+#: lib/shortcut.tcl:40 lib/shortcut.tcl:72
+msgid "Create Desktop Icon"
+msgstr "Criar ícone na área de trabalho"
+
+#: git-gui.sh:2539 lib/choose_repository.tcl:183 lib/choose_repository.tcl:191
+msgid "Quit"
+msgstr "Sair"
+
+#: git-gui.sh:2547
+msgid "Undo"
+msgstr "Desfazer"
+
+#: git-gui.sh:2550
+msgid "Redo"
+msgstr "Refazer"
+
+#: git-gui.sh:2554 git-gui.sh:3109
+msgid "Cut"
+msgstr "Recortar"
+
+#: git-gui.sh:2557 git-gui.sh:3112 git-gui.sh:3186 git-gui.sh:3259
+#: lib/console.tcl:69
+msgid "Copy"
+msgstr "Copiar"
+
+#: git-gui.sh:2560 git-gui.sh:3115
+msgid "Paste"
+msgstr "Colar"
+
+#: git-gui.sh:2563 git-gui.sh:3118 lib/branch_delete.tcl:26
+#: lib/remote_branch_delete.tcl:38
+msgid "Delete"
+msgstr "Apagar"
+
+#: git-gui.sh:2567 git-gui.sh:3122 git-gui.sh:3263 lib/console.tcl:71
+msgid "Select All"
+msgstr "Selecionar tudo"
+
+#: git-gui.sh:2576
+msgid "Create..."
+msgstr "Criar..."
+
+#: git-gui.sh:2582
+msgid "Checkout..."
+msgstr "Checkout..."
+
+#: git-gui.sh:2588
+msgid "Rename..."
+msgstr "Renomear..."
+
+#: git-gui.sh:2593
+msgid "Delete..."
+msgstr "Apagar..."
+
+#: git-gui.sh:2598
+msgid "Reset..."
+msgstr "Redefinir..."
+
+#: git-gui.sh:2608
+msgid "Done"
+msgstr "Pronto"
+
+#: git-gui.sh:2610
+msgid "Commit@@verb"
+msgstr "Salvar revisão"
+
+#: git-gui.sh:2619 git-gui.sh:3050
+msgid "New Commit"
+msgstr "Nova revisão"
+
+#: git-gui.sh:2627 git-gui.sh:3057
+msgid "Amend Last Commit"
+msgstr "Corrigir última revisão"
+
+#: git-gui.sh:2637 git-gui.sh:3011 lib/remote_branch_delete.tcl:99
+msgid "Rescan"
+msgstr "Atualizar"
+
+#: git-gui.sh:2643
+msgid "Stage To Commit"
+msgstr "Marcar para revisão"
+
+#: git-gui.sh:2649
+msgid "Stage Changed Files To Commit"
+msgstr "Marcar arquivos modificados"
+
+#: git-gui.sh:2655
+msgid "Unstage From Commit"
+msgstr "Desmarcar"
+
+#: git-gui.sh:2661 lib/index.tcl:412
+msgid "Revert Changes"
+msgstr "Reverter mudanças"
+
+#: git-gui.sh:2669 git-gui.sh:3310 git-gui.sh:3341
+msgid "Show Less Context"
+msgstr "Mostrar menos contexto"
+
+#: git-gui.sh:2673 git-gui.sh:3314 git-gui.sh:3345
+msgid "Show More Context"
+msgstr "Mostrar mais contexto"
+
+#: git-gui.sh:2680 git-gui.sh:3024 git-gui.sh:3133
+msgid "Sign Off"
+msgstr "Assinar embaixo"
+
+#: git-gui.sh:2696
+msgid "Local Merge..."
+msgstr "Mesclar localmente..."
+
+#: git-gui.sh:2701
+msgid "Abort Merge..."
+msgstr "Abortar mesclagem..."
+
+#: git-gui.sh:2713 git-gui.sh:2741
+msgid "Add..."
+msgstr "Adicionar..."
+
+#: git-gui.sh:2717
+msgid "Push..."
+msgstr "Enviar..."
+
+#: git-gui.sh:2721
+msgid "Delete Branch..."
+msgstr "Apagar ramo..."
+
+#: git-gui.sh:2731 git-gui.sh:3292
+msgid "Options..."
+msgstr "Opções..."
+
+#: git-gui.sh:2742
+msgid "Remove..."
+msgstr "Remover..."
+
+#: git-gui.sh:2751 lib/choose_repository.tcl:50
+msgid "Help"
+msgstr "Ajuda"
+
+#: git-gui.sh:2755 git-gui.sh:2759 lib/about.tcl:14
+#: lib/choose_repository.tcl:44 lib/choose_repository.tcl:53
+#, tcl-format
+msgid "About %s"
+msgstr "Sobre o %s"
+
+#: git-gui.sh:2783
+msgid "Online Documentation"
+msgstr "Ajuda online"
+
+#: git-gui.sh:2786 lib/choose_repository.tcl:47 lib/choose_repository.tcl:56
+msgid "Show SSH Key"
+msgstr "Mostrar chave SSH"
+
+#: git-gui.sh:2893
+#, tcl-format
+msgid "fatal: cannot stat path %s: No such file or directory"
+msgstr ""
+"erro fatal: impossível executar \"stat\" em  %s: Arquivo ou diretório não "
+"encontrado"
+
+#: git-gui.sh:2926
+msgid "Current Branch:"
+msgstr "Ramo atual:"
+
+#: git-gui.sh:2947
+msgid "Staged Changes (Will Commit)"
+msgstr "Mudanças marcadas"
+
+#: git-gui.sh:2967
+msgid "Unstaged Changes"
+msgstr "Mudanças não marcadas"
+
+#: git-gui.sh:3017
+msgid "Stage Changed"
+msgstr "Marcar alterados"
+
+#: git-gui.sh:3036 lib/transport.tcl:104 lib/transport.tcl:193
+msgid "Push"
+msgstr "Enviar"
+
+#: git-gui.sh:3071
+msgid "Initial Commit Message:"
+msgstr "Descrição da revisão inicial:"
+
+#: git-gui.sh:3072
+msgid "Amended Commit Message:"
+msgstr "Descrição da revisão corrigida:"
+
+#: git-gui.sh:3073
+msgid "Amended Initial Commit Message:"
+msgstr "Descrição da revisão inicial corrigida:"
+
+#: git-gui.sh:3074
+msgid "Amended Merge Commit Message:"
+msgstr "Descrição da revisão de mescla corrigida:"
+
+#: git-gui.sh:3075
+msgid "Merge Commit Message:"
+msgstr "Descrição da revisão de mescla:"
+
+#: git-gui.sh:3076
+msgid "Commit Message:"
+msgstr "Descrição da revisão:"
+
+#: git-gui.sh:3125 git-gui.sh:3267 lib/console.tcl:73
+msgid "Copy All"
+msgstr "Copiar todos"
+
+#: git-gui.sh:3149 lib/blame.tcl:104
+msgid "File:"
+msgstr "Arquivo:"
+
+#: git-gui.sh:3255
+msgid "Refresh"
+msgstr "Atualizar"
+
+#: git-gui.sh:3276
+msgid "Decrease Font Size"
+msgstr "Reduzir tamanho da fonte"
+
+#: git-gui.sh:3280
+msgid "Increase Font Size"
+msgstr "Aumentar tamanho da fonte"
+
+#: git-gui.sh:3288 lib/blame.tcl:281
+msgid "Encoding"
+msgstr "Codificação"
+
+#: git-gui.sh:3299
+msgid "Apply/Reverse Hunk"
+msgstr "Aplicar/reverter trecho"
+
+#: git-gui.sh:3304
+msgid "Apply/Reverse Line"
+msgstr "Aplicar/reverter linha"
+
+#: git-gui.sh:3323
+msgid "Run Merge Tool"
+msgstr "Executar ferramenta de mescla"
+
+#: git-gui.sh:3328
+msgid "Use Remote Version"
+msgstr "Usar versão remota"
+
+#: git-gui.sh:3332
+msgid "Use Local Version"
+msgstr "Usar versão local"
+
+#: git-gui.sh:3336
+msgid "Revert To Base"
+msgstr "Reverter para a versão-base"
+
+#: git-gui.sh:3354
+msgid "Visualize These Changes In The Submodule"
+msgstr "Visualizar estas mudanças no sub-módulo"
+
+#: git-gui.sh:3358
+msgid "Visualize Current Branch History In The Submodule"
+msgstr "Visualizar histórico do ramo atual no sub-módulo"
+
+#: git-gui.sh:3362
+msgid "Visualize All Branch History In The Submodule"
+msgstr "Visualizar histórico de todos os camos no sub-módulo"
+
+#: git-gui.sh:3367
+msgid "Start git gui In The Submodule"
+msgstr "Iniciar \"git gui\" no sub-módulo"
+
+#: git-gui.sh:3389
+msgid "Unstage Hunk From Commit"
+msgstr "Desmarcar trecho para revisão"
+
+#: git-gui.sh:3391
+msgid "Unstage Lines From Commit"
+msgstr "Desmarcar linhas para revisão"
+
+#: git-gui.sh:3393
+msgid "Unstage Line From Commit"
+msgstr "Desmarcar linha para revisão"
+
+#: git-gui.sh:3396
+msgid "Stage Hunk For Commit"
+msgstr "Marcar trecho para revisão"
+
+#: git-gui.sh:3398
+msgid "Stage Lines For Commit"
+msgstr "Marcar linhas para revisão"
+
+#: git-gui.sh:3400
+msgid "Stage Line For Commit"
+msgstr "Marcar linha para revisão"
+
+#: git-gui.sh:3424
+msgid "Initializing..."
+msgstr "Iniciando..."
+
+#: git-gui.sh:3541
+#, tcl-format
+msgid ""
+"Possible environment issues exist.\n"
+"\n"
+"The following environment variables are probably\n"
+"going to be ignored by any Git subprocess run\n"
+"by %s:\n"
+"\n"
+msgstr ""
+"Possíveis problemas com as variáveis de ambiente.\n"
+"\n"
+"As seguintes variáveis de ambiente provavelmente serão\n"
+"ignoradas por qualquer sub-processo do Git executado por\n"
+"%s:\n"
+
+#: git-gui.sh:3570
+msgid ""
+"\n"
+"This is due to a known issue with the\n"
+"Tcl binary distributed by Cygwin."
+msgstr ""
+"\n"
+"Isto se deve a um problema conhecido com os binários da Tcl \n"
+"distribuídos com o Cygwin"
+
+#: git-gui.sh:3575
+#, tcl-format
+msgid ""
+"\n"
+"\n"
+"A good replacement for %s\n"
+"is placing values for the user.name and\n"
+"user.email settings into your personal\n"
+"~/.gitconfig file.\n"
+msgstr ""
+"\n"
+"\n"
+"Uma boa alternativa para %s\n"
+"é colocar os valores para o nome de usuário e e-mail\n"
+"no seu arquivo \"~/.gitconfig\"\n"
+
+#: lib/about.tcl:26
+msgid "git-gui - a graphical user interface for Git."
+msgstr "git-gui - uma interface gráfica para o Git"
+
+#: lib/blame.tcl:72
+msgid "File Viewer"
+msgstr "Visualizador de arquivos"
+
+#: lib/blame.tcl:78
+msgid "Commit:"
+msgstr "Revisão:"
+
+#: lib/blame.tcl:271
+msgid "Copy Commit"
+msgstr "Copiar revisão"
+
+#: lib/blame.tcl:275
+msgid "Find Text..."
+msgstr "Procurar texto..."
+
+#: lib/blame.tcl:284
+msgid "Do Full Copy Detection"
+msgstr "Executar detecção completa de cópias"
+
+#: lib/blame.tcl:288
+msgid "Show History Context"
+msgstr "Mostrar contexto do histórico"
+
+#: lib/blame.tcl:291
+msgid "Blame Parent Commit"
+msgstr "Anotar revisão anterior"
+
+#: lib/blame.tcl:450
+#, tcl-format
+msgid "Reading %s..."
+msgstr "Lendo %s..."
+
+#: lib/blame.tcl:557
+msgid "Loading copy/move tracking annotations..."
+msgstr "Carregando anotações de cópia/movimentação..."
+
+#: lib/blame.tcl:577
+msgid "lines annotated"
+msgstr "linhas anotadas"
+
+#: lib/blame.tcl:769
+msgid "Loading original location annotations..."
+msgstr "Carregando anotações originais..."
+
+#: lib/blame.tcl:772
+msgid "Annotation complete."
+msgstr "Anotação completa."
+
+#: lib/blame.tcl:802
+msgid "Busy"
+msgstr "Ocupado"
+
+#: lib/blame.tcl:803
+msgid "Annotation process is already running."
+msgstr "O processo de anotação já está em execução"
+
+#: lib/blame.tcl:842
+msgid "Running thorough copy detection..."
+msgstr "Executando detecção de cópia..."
+
+#: lib/blame.tcl:910
+msgid "Loading annotation..."
+msgstr "Carregando anotações..."
+
+#: lib/blame.tcl:963
+msgid "Author:"
+msgstr "Autor:"
+
+#: lib/blame.tcl:967
+msgid "Committer:"
+msgstr "Revisor:"
+
+#: lib/blame.tcl:972
+msgid "Original File:"
+msgstr "Arquivo original:"
+
+#: lib/blame.tcl:1020
+msgid "Cannot find HEAD commit:"
+msgstr "Impossível encontrar revisão HEAD:"
+
+#: lib/blame.tcl:1075
+msgid "Cannot find parent commit:"
+msgstr "Impossível encontrar revisão anterior:"
+
+#: lib/blame.tcl:1090
+msgid "Unable to display parent"
+msgstr "Impossível exibir revisão anterior"
+
+#: lib/blame.tcl:1091 lib/diff.tcl:320
+msgid "Error loading diff:"
+msgstr "Erro ao carregar as diferenças:"
+
+#: lib/blame.tcl:1231
+msgid "Originally By:"
+msgstr "Originalmente por:"
+
+#: lib/blame.tcl:1237
+msgid "In File:"
+msgstr "No arquivo:"
+
+#: lib/blame.tcl:1242
+msgid "Copied Or Moved Here By:"
+msgstr "Copiado ou movido para cá por:"
+
+#: lib/branch_checkout.tcl:14 lib/branch_checkout.tcl:19
+msgid "Checkout Branch"
+msgstr "Efetuar checkout do ramo"
+
+#: lib/branch_checkout.tcl:23
+msgid "Checkout"
+msgstr "Checkout"
+
+#: lib/branch_checkout.tcl:27 lib/branch_create.tcl:35
+#: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:282
+#: lib/checkout_op.tcl:579 lib/choose_font.tcl:43 lib/merge.tcl:172
+#: lib/option.tcl:125 lib/remote_add.tcl:32 lib/remote_branch_delete.tcl:42
+#: lib/tools_dlg.tcl:40 lib/tools_dlg.tcl:204 lib/tools_dlg.tcl:352
+#: lib/transport.tcl:108
+msgid "Cancel"
+msgstr "Cancelar"
+
+#: lib/branch_checkout.tcl:32 lib/browser.tcl:287 lib/tools_dlg.tcl:328
+msgid "Revision"
+msgstr "Revisão"
+
+#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:280
+msgid "Options"
+msgstr "Opções"
+
+#: lib/branch_checkout.tcl:39 lib/branch_create.tcl:92
+msgid "Fetch Tracking Branch"
+msgstr "Obter ramo de rastreamento"
+
+#: lib/branch_checkout.tcl:44
+msgid "Detach From Local Branch"
+msgstr "Separar do ramo local"
+
+#: lib/branch_create.tcl:22
+msgid "Create Branch"
+msgstr "Criar ramo"
+
+#: lib/branch_create.tcl:27
+msgid "Create New Branch"
+msgstr "Criar novo ramo"
+
+#: lib/branch_create.tcl:31 lib/choose_repository.tcl:381
+msgid "Create"
+msgstr "Criar"
+
+#: lib/branch_create.tcl:40
+msgid "Branch Name"
+msgstr "Nome do ramo"
+
+#: lib/branch_create.tcl:43 lib/remote_add.tcl:39 lib/tools_dlg.tcl:50
+msgid "Name:"
+msgstr "Nome:"
+
+#: lib/branch_create.tcl:58
+msgid "Match Tracking Branch Name"
+msgstr "Coincidir nome do ramo de rastreamento"
+
+#: lib/branch_create.tcl:66
+msgid "Starting Revision"
+msgstr "Revisão inicial"
+
+#: lib/branch_create.tcl:72
+msgid "Update Existing Branch:"
+msgstr "Atualizar ramo existente:"
+
+#: lib/branch_create.tcl:75
+msgid "No"
+msgstr "Não"
+
+#: lib/branch_create.tcl:80
+msgid "Fast Forward Only"
+msgstr "Somente se for um avanço rápido"
+
+#: lib/branch_create.tcl:85 lib/checkout_op.tcl:571
+msgid "Reset"
+msgstr "Redefinir"
+
+#: lib/branch_create.tcl:97
+msgid "Checkout After Creation"
+msgstr "Efetuar checkout após a criação"
+
+#: lib/branch_create.tcl:131
+msgid "Please select a tracking branch."
+msgstr "Selecione um ramo de rastreamento."
+
+#: lib/branch_create.tcl:140
+#, tcl-format
+msgid "Tracking branch %s is not a branch in the remote repository."
+msgstr "O ramo de rastreamento %s não é um ramo do repositório remoto."
+
+#: lib/branch_create.tcl:153 lib/branch_rename.tcl:86
+msgid "Please supply a branch name."
+msgstr "Indique um nome para o ramo."
+
+#: lib/branch_create.tcl:164 lib/branch_rename.tcl:106
+#, tcl-format
+msgid "'%s' is not an acceptable branch name."
+msgstr "\"%s\" não é um nome de ramo válido"
+
+#: lib/branch_delete.tcl:15
+msgid "Delete Branch"
+msgstr "Apagar ramo"
+
+#: lib/branch_delete.tcl:20
+msgid "Delete Local Branch"
+msgstr "Apagar ramo local"
+
+#: lib/branch_delete.tcl:37
+msgid "Local Branches"
+msgstr "Ramos locais"
+
+#: lib/branch_delete.tcl:52
+msgid "Delete Only If Merged Into"
+msgstr "Apagar somente se mesclado em"
+
+#: lib/branch_delete.tcl:54 lib/remote_branch_delete.tcl:119
+msgid "Always (Do not perform merge checks)"
+msgstr "Forçar exclusão (não verificar se o ramo foi mesclado)"
+
+#: lib/branch_delete.tcl:103
+#, tcl-format
+msgid "The following branches are not completely merged into %s:"
+msgstr "Os ramos seguintes não foram completamente mesclados em %s:"
+
+#: lib/branch_delete.tcl:115 lib/remote_branch_delete.tcl:217
+msgid ""
+"Recovering deleted branches is difficult.\n"
+"\n"
+"Delete the selected branches?"
+msgstr ""
+"Recuperar ramos apagados é difícil.\n"
+"\n"
+"Apagar os ramos selecionados?"
+
+#: lib/branch_delete.tcl:141
+#, tcl-format
+msgid ""
+"Failed to delete branches:\n"
+"%s"
+msgstr ""
+"Erro ao apagar ramos:\n"
+"%s"
+
+#: lib/branch_rename.tcl:14 lib/branch_rename.tcl:22
+msgid "Rename Branch"
+msgstr "Renomear ramo"
+
+#: lib/branch_rename.tcl:26
+msgid "Rename"
+msgstr "Renomear"
+
+#: lib/branch_rename.tcl:36
+msgid "Branch:"
+msgstr "Ramo:"
+
+#: lib/branch_rename.tcl:39
+msgid "New Name:"
+msgstr "Novo nome:"
+
+#: lib/branch_rename.tcl:75
+msgid "Please select a branch to rename."
+msgstr "Selecione um ramo para renomear."
+
+#: lib/branch_rename.tcl:96 lib/checkout_op.tcl:202
+#, tcl-format
+msgid "Branch '%s' already exists."
+msgstr "O ramo \"%s\" já existe."
+
+#: lib/branch_rename.tcl:117
+#, tcl-format
+msgid "Failed to rename '%s'."
+msgstr "Erro ao renomear \"%s\"."
+
+#: lib/browser.tcl:17
+msgid "Starting..."
+msgstr "Inciando..."
+
+#: lib/browser.tcl:26
+msgid "File Browser"
+msgstr "Navegador de arquivos"
+
+#: lib/browser.tcl:126 lib/browser.tcl:143
+#, tcl-format
+msgid "Loading %s..."
+msgstr "Carregando %s..."
+
+#: lib/browser.tcl:187
+msgid "[Up To Parent]"
+msgstr "[Subir]"
+
+#: lib/browser.tcl:267 lib/browser.tcl:273
+msgid "Browse Branch Files"
+msgstr "Explorar arquivos do ramo"
+
+#: lib/browser.tcl:278 lib/choose_repository.tcl:398
+#: lib/choose_repository.tcl:486 lib/choose_repository.tcl:497
+#: lib/choose_repository.tcl:1028
+msgid "Browse"
+msgstr "Explorar"
+
+#: lib/checkout_op.tcl:85
+#, tcl-format
+msgid "Fetching %s from %s"
+msgstr "Obtendo %s de %s"
+
+#: lib/checkout_op.tcl:133
+#, tcl-format
+msgid "fatal: Cannot resolve %s"
+msgstr "Erro fatal: impossível resolver %s"
+
+#: lib/checkout_op.tcl:146 lib/console.tcl:81 lib/database.tcl:31
+#: lib/sshkey.tcl:53
+msgid "Close"
+msgstr "Fechar"
+
+#: lib/checkout_op.tcl:175
+#, tcl-format
+msgid "Branch '%s' does not exist."
+msgstr "O ramo \"%s\" não existe."
+
+#: lib/checkout_op.tcl:194
+#, tcl-format
+msgid "Failed to configure simplified git-pull for '%s'."
+msgstr "Erro ao configurar git-pull simplificado para \"%s\"."
+
+#: lib/checkout_op.tcl:229
+#, tcl-format
+msgid ""
+"Branch '%s' already exists.\n"
+"\n"
+"It cannot fast-forward to %s.\n"
+"A merge is required."
+msgstr ""
+"O ramo \"%s\" já existe.\n"
+"\n"
+"Não é possível avançá-lo para %s.\n"
+"É preciso mesclar."
+
+#: lib/checkout_op.tcl:243
+#, tcl-format
+msgid "Merge strategy '%s' not supported."
+msgstr "Estratégia de mesclagem \"%s\" não suportada."
+
+#: lib/checkout_op.tcl:262
+#, tcl-format
+msgid "Failed to update '%s'."
+msgstr "Erro ao atualizar \"%s\"."
+
+#: lib/checkout_op.tcl:274
+msgid "Staging area (index) is already locked."
+msgstr "A área de marcação (staging area, index) já está bloqueada."
+
+#: lib/checkout_op.tcl:289
+msgid ""
+"Last scanned state does not match repository state.\n"
+"\n"
+"Another Git program has modified this repository since the last scan.  A "
+"rescan must be performed before the current branch can be changed.\n"
+"\n"
+"The rescan will be automatically started now.\n"
+msgstr ""
+"O último estado lido não confere com o estado atual.\n"
+"\n"
+"Outro programa do Git modificou o repositório desde a última leitura. Uma "
+"atualização deve ser executada antes de alterar o ramo atual.\n"
+"\n"
+"A atualização começará automaticamente agora.\n"
+
+#: lib/checkout_op.tcl:345
+#, tcl-format
+msgid "Updating working directory to '%s'..."
+msgstr "Atualizando diretório de trabalho para \"%s\"..."
+
+#: lib/checkout_op.tcl:346
+msgid "files checked out"
+msgstr "arquivos retirados"
+
+#: lib/checkout_op.tcl:376
+#, tcl-format
+msgid "Aborted checkout of '%s' (file level merging is required)."
+msgstr "Checkout de \"%s\" abortado (é preciso mesclar alguns arquivos)"
+
+#: lib/checkout_op.tcl:377
+msgid "File level merge required."
+msgstr "Mesclagem de arquivos necessária."
+
+#: lib/checkout_op.tcl:381
+#, tcl-format
+msgid "Staying on branch '%s'."
+msgstr "Permanecendo no ramo \"%s\"."
+
+#: lib/checkout_op.tcl:452
+msgid ""
+"You are no longer on a local branch.\n"
+"\n"
+"If you wanted to be on a branch, create one now starting from 'This Detached "
+"Checkout'."
+msgstr ""
+"Você não está mais em um ramo local\n"
+"\n"
+"Se você deseja um ramo, crie um agora a partir deste checkout."
+
+#: lib/checkout_op.tcl:503 lib/checkout_op.tcl:507
+#, tcl-format
+msgid "Checked out '%s'."
+msgstr "Checkout de \"%s\" concluído."
+
+#: lib/checkout_op.tcl:535
+#, tcl-format
+msgid "Resetting '%s' to '%s' will lose the following commits:"
+msgstr "Redefinir \"%s\" para \"%s\" provocará a perda das seguintes revisões:"
+
+#: lib/checkout_op.tcl:557
+msgid "Recovering lost commits may not be easy."
+msgstr "Recuperar revisões perdidas pode não ser fácil."
+
+#: lib/checkout_op.tcl:562
+#, tcl-format
+msgid "Reset '%s'?"
+msgstr "Redefinir \"%s\"?"
+
+#: lib/checkout_op.tcl:567 lib/merge.tcl:164 lib/tools_dlg.tcl:343
+msgid "Visualize"
+msgstr "Visualizar"
+
+#: lib/checkout_op.tcl:635
+#, tcl-format
+msgid ""
+"Failed to set current branch.\n"
+"\n"
+"This working directory is only partially switched.  We successfully updated "
+"your files, but failed to update an internal Git file.\n"
+"\n"
+"This should not have occurred.  %s will now close and give up."
+msgstr ""
+"Erro ao definir o ramo atual.\n"
+"\n"
+"Este diretório de trabalho está incompleto. Foi possível atualizar seus "
+"arquivos, mas houve uma falha ao atualizar os arquivos internos do Git.\n"
+"\n"
+"Isto não deveria ter acontecido, %s terminará agora."
+
+#: lib/choose_font.tcl:39
+msgid "Select"
+msgstr "Selecionar"
+
+#: lib/choose_font.tcl:53
+msgid "Font Family"
+msgstr "Tipo da fonte"
+
+#: lib/choose_font.tcl:74
+msgid "Font Size"
+msgstr "Tamanho da fonte"
+
+#: lib/choose_font.tcl:91
+msgid "Font Example"
+msgstr "Exemplo"
+
+#: lib/choose_font.tcl:103
+msgid ""
+"This is example text.\n"
+"If you like this text, it can be your font."
+msgstr ""
+"Este é um texto de exemplo.\n"
+"Se você gostar deste texto, esta pode ser sua fonte."
+
+#: lib/choose_repository.tcl:28
+msgid "Git Gui"
+msgstr "Git Gui"
+
+#: lib/choose_repository.tcl:87 lib/choose_repository.tcl:386
+msgid "Create New Repository"
+msgstr "Criar novo repositório"
+
+#: lib/choose_repository.tcl:93
+msgid "New..."
+msgstr "Novo..."
+
+#: lib/choose_repository.tcl:100 lib/choose_repository.tcl:471
+msgid "Clone Existing Repository"
+msgstr "Clonar repositório existente"
+
+#: lib/choose_repository.tcl:106
+msgid "Clone..."
+msgstr "Clonar..."
+
+#: lib/choose_repository.tcl:113 lib/choose_repository.tcl:1016
+msgid "Open Existing Repository"
+msgstr "Abrir repositório existente"
+
+#: lib/choose_repository.tcl:119
+msgid "Open..."
+msgstr "Abrir..."
+
+#: lib/choose_repository.tcl:132
+msgid "Recent Repositories"
+msgstr "Repositórios recentes"
+
+#: lib/choose_repository.tcl:138
+msgid "Open Recent Repository:"
+msgstr "Abrir repositório recente:"
+
+#: lib/choose_repository.tcl:306 lib/choose_repository.tcl:313
+#: lib/choose_repository.tcl:320
+#, tcl-format
+msgid "Failed to create repository %s:"
+msgstr "Erro ao criar repositório %s:"
+
+#: lib/choose_repository.tcl:391
+msgid "Directory:"
+msgstr "Diretório:"
+
+#: lib/choose_repository.tcl:423 lib/choose_repository.tcl:550
+#: lib/choose_repository.tcl:1052
+msgid "Git Repository"
+msgstr "Repositório Git"
+
+#: lib/choose_repository.tcl:448
+#, tcl-format
+msgid "Directory %s already exists."
+msgstr "O diretório %s já existe."
+
+#: lib/choose_repository.tcl:452
+#, tcl-format
+msgid "File %s already exists."
+msgstr "O arquivo %s já existe."
+
+#: lib/choose_repository.tcl:466
+msgid "Clone"
+msgstr "Clonar"
+
+#: lib/choose_repository.tcl:479
+msgid "Source Location:"
+msgstr "Origem:"
+
+#: lib/choose_repository.tcl:490
+msgid "Target Directory:"
+msgstr "Diretório de destino:"
+
+#: lib/choose_repository.tcl:502
+msgid "Clone Type:"
+msgstr "Tipo de clonagem:"
+
+#: lib/choose_repository.tcl:508
+msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
+msgstr "Padrão (rápida, semi-redundante, com hardlinks)"
+
+#: lib/choose_repository.tcl:514
+msgid "Full Copy (Slower, Redundant Backup)"
+msgstr "Cópia completa (mais lenta, backup redundante)"
+
+#: lib/choose_repository.tcl:520
+msgid "Shared (Fastest, Not Recommended, No Backup)"
+msgstr "Compartilhada (A mais rápida, não recomendada, sem backup)"
+
+#: lib/choose_repository.tcl:556 lib/choose_repository.tcl:603
+#: lib/choose_repository.tcl:749 lib/choose_repository.tcl:819
+#: lib/choose_repository.tcl:1058 lib/choose_repository.tcl:1066
+#, tcl-format
+msgid "Not a Git repository: %s"
+msgstr "Este não é um repositório do Git: %s"
+
+#: lib/choose_repository.tcl:592
+msgid "Standard only available for local repository."
+msgstr "Clonagens padrões só são possíveis em repositórios locais."
+
+#: lib/choose_repository.tcl:596
+msgid "Shared only available for local repository."
+msgstr "Clonagens parciais só são possíveis em repositórios locais."
+
+#: lib/choose_repository.tcl:617
+#, tcl-format
+msgid "Location %s already exists."
+msgstr "O local %s já existe."
+
+#: lib/choose_repository.tcl:628
+msgid "Failed to configure origin"
+msgstr "Erro ao configurar origem"
+
+#: lib/choose_repository.tcl:640
+msgid "Counting objects"
+msgstr "Contando objetos"
+
+#: lib/choose_repository.tcl:641
+msgid "buckets"
+msgstr "buckets"
+
+#: lib/choose_repository.tcl:665
+#, tcl-format
+msgid "Unable to copy objects/info/alternates: %s"
+msgstr "Erro ao copiar objetos ou informações adicionais: %s"
+
+#: lib/choose_repository.tcl:701
+#, tcl-format
+msgid "Nothing to clone from %s."
+msgstr "Não há nada para clonar em %s."
+
+#: lib/choose_repository.tcl:703 lib/choose_repository.tcl:917
+#: lib/choose_repository.tcl:929
+msgid "The 'master' branch has not been initialized."
+msgstr "O ramo \"master\" não foi inicializado."
+
+#: lib/choose_repository.tcl:716
+msgid "Hardlinks are unavailable.  Falling back to copying."
+msgstr "Não foi possível criar hardlinks, usando cópias convencionais."
+
+#: lib/choose_repository.tcl:728
+#, tcl-format
+msgid "Cloning from %s"
+msgstr "Clonando de %s"
+
+#: lib/choose_repository.tcl:759
+msgid "Copying objects"
+msgstr "Copiando objetos"
+
+#: lib/choose_repository.tcl:760
+msgid "KiB"
+msgstr "KiB"
+
+#: lib/choose_repository.tcl:784
+#, tcl-format
+msgid "Unable to copy object: %s"
+msgstr "Não foi possível copiar o objeto: %s"
+
+#: lib/choose_repository.tcl:794
+msgid "Linking objects"
+msgstr "Ligando objetos"
+
+#: lib/choose_repository.tcl:795
+msgid "objects"
+msgstr "objetos"
+
+#: lib/choose_repository.tcl:803
+#, tcl-format
+msgid "Unable to hardlink object: %s"
+msgstr "Não foi possível ligar o objeto: %s"
+
+#: lib/choose_repository.tcl:858
+msgid "Cannot fetch branches and objects.  See console output for details."
+msgstr ""
+"Não foi possível receber ramos ou objetos. Veja a saída do console para "
+"detalhes."
+
+#: lib/choose_repository.tcl:869
+msgid "Cannot fetch tags.  See console output for details."
+msgstr ""
+"Não foi possível receber as etiquetas. Veja a saída do console para detalhes."
+
+#: lib/choose_repository.tcl:893
+msgid "Cannot determine HEAD.  See console output for details."
+msgstr ""
+"Não foi possível determinar a etiqueta HEAD. Veja a saída do console para "
+"detalhes."
+
+#: lib/choose_repository.tcl:902
+#, tcl-format
+msgid "Unable to cleanup %s"
+msgstr "Não foi possível limpar %s"
+
+#: lib/choose_repository.tcl:908
+msgid "Clone failed."
+msgstr "A clonagem falhou."
+
+#: lib/choose_repository.tcl:915
+msgid "No default branch obtained."
+msgstr "O ramo padrão não foi recebido."
+
+#: lib/choose_repository.tcl:926
+#, tcl-format
+msgid "Cannot resolve %s as a commit."
+msgstr "Não foi possível resolver %s como uma revisão."
+
+#: lib/choose_repository.tcl:938
+msgid "Creating working directory"
+msgstr "Criando diretório de trabalho."
+
+#: lib/choose_repository.tcl:939 lib/index.tcl:67 lib/index.tcl:130
+#: lib/index.tcl:198
+msgid "files"
+msgstr "arquivos"
+
+#: lib/choose_repository.tcl:968
+msgid "Initial file checkout failed."
+msgstr "Erro ao efetuar checkout inicial."
+
+#: lib/choose_repository.tcl:1011
+msgid "Open"
+msgstr "Abrir"
+
+#: lib/choose_repository.tcl:1021
+msgid "Repository:"
+msgstr "Repositório:"
+
+#: lib/choose_repository.tcl:1072
+#, tcl-format
+msgid "Failed to open repository %s:"
+msgstr "Erro ao abrir o repositório %s:"
+
+#: lib/choose_rev.tcl:53
+msgid "This Detached Checkout"
+msgstr "Este checkout"
+
+#: lib/choose_rev.tcl:60
+msgid "Revision Expression:"
+msgstr "Expressão de revisão:"
+
+#: lib/choose_rev.tcl:74
+msgid "Local Branch"
+msgstr "Ramo local"
+
+#: lib/choose_rev.tcl:79
+msgid "Tracking Branch"
+msgstr "Ramo de rastreamento"
+
+#: lib/choose_rev.tcl:84 lib/choose_rev.tcl:538
+msgid "Tag"
+msgstr "Etiqueta"
+
+#: lib/choose_rev.tcl:317
+#, tcl-format
+msgid "Invalid revision: %s"
+msgstr "Revisão inválida: %s"
+
+#: lib/choose_rev.tcl:338
+msgid "No revision selected."
+msgstr "Nenhuma revisão selecionada."
+
+#: lib/choose_rev.tcl:346
+msgid "Revision expression is empty."
+msgstr "A expressão de revisão está vazia."
+
+#: lib/choose_rev.tcl:531
+msgid "Updated"
+msgstr "Atualizado"
+
+#: lib/choose_rev.tcl:559
+msgid "URL"
+msgstr "URL"
+
+#: lib/commit.tcl:9
+msgid ""
+"There is nothing to amend.\n"
+"\n"
+"You are about to create the initial commit.  There is no commit before this "
+"to amend.\n"
+msgstr ""
+"Não há nada para corrigir.\n"
+"\n"
+"Você está prestes a criar uma revisão inicial. Não há revisão anterior para "
+"corrigir.\n"
+
+#: lib/commit.tcl:18
+msgid ""
+"Cannot amend while merging.\n"
+"\n"
+"You are currently in the middle of a merge that has not been fully "
+"completed.  You cannot amend the prior commit unless you first abort the "
+"current merge activity.\n"
+msgstr ""
+"Não é possível corrigir durante uma mesclagem.\n"
+"\n"
+"Você está em meio a uma operação de mesclagem que não foi completada. Não é "
+"possível corrigir a revisão anterior a menos que você aborte a mescla atual "
+"antes.\n"
+
+#: lib/commit.tcl:48
+msgid "Error loading commit data for amend:"
+msgstr "Erro ao carregar dados da revisão para corrigir:"
+
+#: lib/commit.tcl:75
+msgid "Unable to obtain your identity:"
+msgstr "Não foi possível obter a sua identidade:"
+
+#: lib/commit.tcl:80
+msgid "Invalid GIT_COMMITTER_IDENT:"
+msgstr "Variável \"GIT_COMMITTER_IDENT\" inválida:"
+
+#: lib/commit.tcl:129
+#, tcl-format
+msgid "warning: Tcl does not support encoding '%s'."
+msgstr "aviso: O Tcl não suporta a codificação \"%s\"."
+
+#: lib/commit.tcl:149
+msgid ""
+"Last scanned state does not match repository state.\n"
+"\n"
+"Another Git program has modified this repository since the last scan.  A "
+"rescan must be performed before another commit can be created.\n"
+"\n"
+"The rescan will be automatically started now.\n"
+msgstr ""
+"O último estado lido não confere com o estado atual.\n"
+"\n"
+"Outro programa do Git modificou o repositório desde a última leitura. Uma "
+"atualização deve ser executada antes de criar outra revisão.\n"
+"\n"
+"A atualização começará automaticamente agora.\n"
+
+#: lib/commit.tcl:172
+#, tcl-format
+msgid ""
+"Unmerged files cannot be committed.\n"
+"\n"
+"File %s has merge conflicts.  You must resolve them and stage the file "
+"before committing.\n"
+msgstr ""
+"Não é possível salvar revisões para arquivos não mesclados.\n"
+"\n"
+"O arquivo %s possui conflitos de mesclagem. Você deve resolvê-los e marcar o "
+"arquivo antes de salvar a revisão.\n"
+
+#: lib/commit.tcl:180
+#, tcl-format
+msgid ""
+"Unknown file state %s detected.\n"
+"\n"
+"File %s cannot be committed by this program.\n"
+msgstr ""
+"Estado desconhecido detectado para o arquivo %s.\n"
+"\n"
+"Este programa não pode salvar uma revisão para o arquivo %s.\n"
+
+#: lib/commit.tcl:188
+msgid ""
+"No changes to commit.\n"
+"\n"
+"You must stage at least 1 file before you can commit.\n"
+msgstr ""
+"Não há mudanças para salvar.\n"
+"\n"
+"Você deve marcar ao menos um arquivo antes de salvar a revisão.\n"
+
+#: lib/commit.tcl:203
+msgid ""
+"Please supply a commit message.\n"
+"\n"
+"A good commit message has the following format:\n"
+"\n"
+"- First line: Describe in one sentence what you did.\n"
+"- Second line: Blank\n"
+"- Remaining lines: Describe why this change is good.\n"
+msgstr ""
+"Por favor, indique uma descrição para a revisão.\n"
+"\n"
+"Uma boa descrição tem o seguinte formato:\n"
+"\n"
+"- Primeira linha: descreve, em uma única frase, o que você fez.\n"
+"- Segunda linha: em branco.\n"
+"- Demais linhas: Descreve detalhadamente a revisão.\n"
+
+#: lib/commit.tcl:234
+msgid "Calling pre-commit hook..."
+msgstr "Executando script \"pre-commit\"..."
+
+#: lib/commit.tcl:249
+msgid "Commit declined by pre-commit hook."
+msgstr "A revisão foi bloqueada pelo script \"pre-commit\"."
+
+#: lib/commit.tcl:272
+msgid "Calling commit-msg hook..."
+msgstr "Executando script \"commit-msg\"..."
+
+#: lib/commit.tcl:287
+msgid "Commit declined by commit-msg hook."
+msgstr "Revisão bloqueada pelo script \"commit-msg\"."
+
+#: lib/commit.tcl:300
+msgid "Committing changes..."
+msgstr "Salvando revisão..."
+
+#: lib/commit.tcl:316
+msgid "write-tree failed:"
+msgstr "write-tree falhou:"
+
+#: lib/commit.tcl:317 lib/commit.tcl:361 lib/commit.tcl:382
+msgid "Commit failed."
+msgstr "A revisão falhou."
+
+#: lib/commit.tcl:334
+#, tcl-format
+msgid "Commit %s appears to be corrupt"
+msgstr "A revisão %s parece estar corrompida."
+
+#: lib/commit.tcl:339
+msgid ""
+"No changes to commit.\n"
+"\n"
+"No files were modified by this commit and it was not a merge commit.\n"
+"\n"
+"A rescan will be automatically started now.\n"
+msgstr ""
+"Não há alterações para salvar.\n"
+"\n"
+"Nenhum arquivo foi modificado e esta não é uma revisão de mesclagem.\n"
+"\n"
+"Uma atualização será executada automaticamente agora.\n"
+
+#: lib/commit.tcl:346
+msgid "No changes to commit."
+msgstr "Não há alterações para salvar."
+
+#: lib/commit.tcl:360
+msgid "commit-tree failed:"
+msgstr "commit-tree falhou:"
+
+#: lib/commit.tcl:381
+msgid "update-ref failed:"
+msgstr "update-ref falhou:"
+
+#: lib/commit.tcl:469
+#, tcl-format
+msgid "Created commit %s: %s"
+msgstr "Revisão %s criada: %s"
+
+#: lib/console.tcl:59
+msgid "Working... please wait..."
+msgstr "Trabalhando... aguarde..."
+
+#: lib/console.tcl:186
+msgid "Success"
+msgstr "Sucesso"
+
+#: lib/console.tcl:200
+msgid "Error: Command Failed"
+msgstr "Erro: o comando falhou"
+
+#: lib/database.tcl:43
+msgid "Number of loose objects"
+msgstr "Número de objetos soltos"
+
+#: lib/database.tcl:44
+msgid "Disk space used by loose objects"
+msgstr "Espaço ocupado pelos objetos soltos"
+
+#: lib/database.tcl:45
+msgid "Number of packed objects"
+msgstr "Número de objetos compactados"
+
+#: lib/database.tcl:46
+msgid "Number of packs"
+msgstr "Número de pacotes"
+
+#: lib/database.tcl:47
+msgid "Disk space used by packed objects"
+msgstr "Espaço ocupado pelos objetos compactados"
+
+#: lib/database.tcl:48
+msgid "Packed objects waiting for pruning"
+msgstr "Objetos compactados aguardando eliminação"
+
+#: lib/database.tcl:49
+msgid "Garbage files"
+msgstr "Arquivos de lixo"
+
+#: lib/database.tcl:72
+msgid "Compressing the object database"
+msgstr "Compactando banco de dados de objetos"
+
+#: lib/database.tcl:83
+msgid "Verifying the object database with fsck-objects"
+msgstr "Verificando banco de dados de objetos com fsck-objects"
+
+#: lib/database.tcl:107
+#, tcl-format
+msgid ""
+"This repository currently has approximately %i loose objects.\n"
+"\n"
+"To maintain optimal performance it is strongly recommended that you compress "
+"the database.\n"
+"\n"
+"Compress the database now?"
+msgstr ""
+"Este repositório possui aproximadamente %i objetos soltos.\n"
+"\n"
+"Para manter o desempenho ótimo é altamente recomendado que você compacte o "
+"banco de dados.\n"
+"\n"
+"Compactar o banco de dados agora?"
+
+#: lib/date.tcl:25
+#, tcl-format
+msgid "Invalid date from Git: %s"
+msgstr "Data inválida recebida do Git: %s"
+
+#: lib/diff.tcl:64
+#, tcl-format
+msgid ""
+"No differences detected.\n"
+"\n"
+"%s has no changes.\n"
+"\n"
+"The modification date of this file was updated by another application, but "
+"the content within the file was not changed.\n"
+"\n"
+"A rescan will be automatically started to find other files which may have "
+"the same state."
+msgstr ""
+"Nenhuma diferença foi detectada.\n"
+"\n"
+"%s não possui mudanças.\n"
+"\n"
+"A data de modificação deste arquivo foi atualizada por outro aplicativo, mas "
+"o conteúdo do arquivo não foi alterado.\n"
+"\n"
+"Uma atualização ser executada para encontrar outros arquivos que possam ter "
+"o mesmo estado."
+
+#: lib/diff.tcl:104
+#, tcl-format
+msgid "Loading diff of %s..."
+msgstr "Carregando diferenças de %s..."
+
+#: lib/diff.tcl:125
+msgid ""
+"LOCAL: deleted\n"
+"REMOTE:\n"
+msgstr ""
+"Local: apagado\n"
+"Remoto:\n"
+
+#: lib/diff.tcl:130
+msgid ""
+"REMOTE: deleted\n"
+"LOCAL:\n"
+msgstr ""
+"Remoto: apagado\n"
+"Local:\n"
+
+#: lib/diff.tcl:137
+msgid "LOCAL:\n"
+msgstr "Local:\n"
+
+#: lib/diff.tcl:140
+msgid "REMOTE:\n"
+msgstr "Remoto:\n"
+
+#: lib/diff.tcl:202 lib/diff.tcl:319
+#, tcl-format
+msgid "Unable to display %s"
+msgstr "Impossível exibir %s"
+
+#: lib/diff.tcl:203
+msgid "Error loading file:"
+msgstr "Erro ao carregar o arquivo:"
+
+#: lib/diff.tcl:210
+msgid "Git Repository (subproject)"
+msgstr "Repositório Git (sub-projeto)"
+
+#: lib/diff.tcl:222
+msgid "* Binary file (not showing content)."
+msgstr "* Arquivo binário (conteúdo não exibido)."
+
+#: lib/diff.tcl:227
+#, tcl-format
+msgid ""
+"* Untracked file is %d bytes.\n"
+"* Showing only first %d bytes.\n"
+msgstr ""
+"* O arquivo não rastreado possui %d bytes.\n"
+"* Exibindo apenas os primeiros %d bytes.\n"
+
+#: lib/diff.tcl:233
+#, tcl-format
+msgid ""
+"\n"
+"* Untracked file clipped here by %s.\n"
+"* To see the entire file, use an external editor.\n"
+msgstr ""
+"\n"
+"* O arquivo não rastreado foi cortado aqui por %s.\n"
+"* Para ver o arquivo completo, use um editor externo.\n"
+
+#: lib/diff.tcl:482
+msgid "Failed to unstage selected hunk."
+msgstr "Erro ao desmarcar o trecho selecionado."
+
+#: lib/diff.tcl:489
+msgid "Failed to stage selected hunk."
+msgstr "Erro ao marcar o trecho selecionado."
+
+#: lib/diff.tcl:568
+msgid "Failed to unstage selected line."
+msgstr "Erro ao desmarcar a linha selecionada."
+
+#: lib/diff.tcl:576
+msgid "Failed to stage selected line."
+msgstr "Erro ao marcar a linha selecionada."
+
+#: lib/encoding.tcl:443
+msgid "Default"
+msgstr "Padrão"
+
+#: lib/encoding.tcl:448
+#, tcl-format
+msgid "System (%s)"
+msgstr "Sistema (%s)"
+
+#: lib/encoding.tcl:459 lib/encoding.tcl:465
+msgid "Other"
+msgstr "Outro"
+
+#: lib/error.tcl:20 lib/error.tcl:114
+msgid "error"
+msgstr "Erro"
+
+#: lib/error.tcl:36
+msgid "warning"
+msgstr "aviso"
+
+#: lib/error.tcl:94
+msgid "You must correct the above errors before committing."
+msgstr "Você precisa corrigir os erros acima antes de salvar a revisão."
+
+#: lib/index.tcl:6
+msgid "Unable to unlock the index."
+msgstr "Impossível desbloquear o índice."
+
+#: lib/index.tcl:15
+msgid "Index Error"
+msgstr "Erro no índice"
+
+#: lib/index.tcl:17
+msgid ""
+"Updating the Git index failed.  A rescan will be automatically started to "
+"resynchronize git-gui."
+msgstr ""
+"A atualização do índice do Git falhou. Uma atualização será executada "
+"automaticamente para ressincronizar o Git GUI"
+
+#: lib/index.tcl:28
+msgid "Continue"
+msgstr "Continuar"
+
+#: lib/index.tcl:31
+msgid "Unlock Index"
+msgstr "Desbloquear índice"
+
+#: lib/index.tcl:289
+#, tcl-format
+msgid "Unstaging %s from commit"
+msgstr "Desmarcando %s para revisão"
+
+#: lib/index.tcl:328
+msgid "Ready to commit."
+msgstr "Pronto para salvar a revisão."
+
+#: lib/index.tcl:341
+#, tcl-format
+msgid "Adding %s"
+msgstr "Adicionando %s"
+
+#: lib/index.tcl:398
+#, tcl-format
+msgid "Revert changes in file %s?"
+msgstr "Reverter as alterações no arquivo %s?"
+
+#: lib/index.tcl:400
+#, tcl-format
+msgid "Revert changes in these %i files?"
+msgstr "Reverter as alterações nestes %i arquivos?"
+
+#: lib/index.tcl:408
+msgid "Any unstaged changes will be permanently lost by the revert."
+msgstr ""
+"Qualquer alteração não marcada será permanentemente perdida na reversão."
+
+#: lib/index.tcl:411
+msgid "Do Nothing"
+msgstr "Não fazer nada"
+
+#: lib/index.tcl:429
+msgid "Reverting selected files"
+msgstr "Revertendo os arquivos selecionados"
+
+#: lib/index.tcl:433
+#, tcl-format
+msgid "Reverting %s"
+msgstr "Revertendo %s"
+
+#: lib/merge.tcl:13
+msgid ""
+"Cannot merge while amending.\n"
+"\n"
+"You must finish amending this commit before starting any type of merge.\n"
+msgstr ""
+"Não é possível mesclar durante uma correção.\n"
+"\n"
+"Você deve concluir a correção antes de começar qualquer mesclagem.\n"
+
+#: lib/merge.tcl:27
+msgid ""
+"Last scanned state does not match repository state.\n"
+"\n"
+"Another Git program has modified this repository since the last scan.  A "
+"rescan must be performed before a merge can be performed.\n"
+"\n"
+"The rescan will be automatically started now.\n"
+msgstr ""
+"O último estado lido não confere com o estado atual.\n"
+"\n"
+"Outro programa do Git modificou o repositório desde a última leitura. Uma "
+"atualização deve ser executada antes de efetuar uma mesclagem.\n"
+"\n"
+"A atualização começará automaticamente agora.\n"
+
+#: lib/merge.tcl:45
+#, tcl-format
+msgid ""
+"You are in the middle of a conflicted merge.\n"
+"\n"
+"File %s has merge conflicts.\n"
+"\n"
+"You must resolve them, stage the file, and commit to complete the current "
+"merge.  Only then can you begin another merge.\n"
+msgstr ""
+"Há uma mesclagem com conflitos em progresso.\n"
+"\n"
+"O arquivo %s possui conflitos de mesclagem.\n"
+"\n"
+"Você deve resolvê-los, marcar o arquivo e salvar a revisão para completar a "
+"mesclagem atual. Só então você poderá começar outra.\n"
+
+#: lib/merge.tcl:55
+#, tcl-format
+msgid ""
+"You are in the middle of a change.\n"
+"\n"
+"File %s is modified.\n"
+"\n"
+"You should complete the current commit before starting a merge.  Doing so "
+"will help you abort a failed merge, should the need arise.\n"
+msgstr ""
+"Você está em meio a uma mudança.\n"
+"\n"
+"O arquivo %s foi modificado.\n"
+"\n"
+"Você deve completar e salvar a revisão atual antes de começar uma mesclagem. "
+"Ao fazê-lo, você poderá abortar a mesclagem caso haja algum erro.\n"
+
+#: lib/merge.tcl:107
+#, tcl-format
+msgid "%s of %s"
+msgstr "%s de %s"
+
+#: lib/merge.tcl:120
+#, tcl-format
+msgid "Merging %s and %s..."
+msgstr "Mesclando %s e %s..."
+
+#: lib/merge.tcl:131
+msgid "Merge completed successfully."
+msgstr "Mesclagem completada com sucesso."
+
+#: lib/merge.tcl:133
+msgid "Merge failed.  Conflict resolution is required."
+msgstr "A mesclagem falhou. É necessário resolver conflitos."
+
+#: lib/merge.tcl:158
+#, tcl-format
+msgid "Merge Into %s"
+msgstr "Mesclar em %s"
+
+#: lib/merge.tcl:177
+msgid "Revision To Merge"
+msgstr "Revisão para mesclar"
+
+#: lib/merge.tcl:212
+msgid ""
+"Cannot abort while amending.\n"
+"\n"
+"You must finish amending this commit.\n"
+msgstr ""
+"Não é possível abortar durante uma correção.\n"
+"\n"
+"Você precisa finalizar a correção desta revisão.\n"
+
+#: lib/merge.tcl:222
+msgid ""
+"Abort merge?\n"
+"\n"
+"Aborting the current merge will cause *ALL* uncommitted changes to be lost.\n"
+"\n"
+"Continue with aborting the current merge?"
+msgstr ""
+"Abortar mesclagem?\n"
+"\n"
+"Abortar a mesclagem atual implicará na perda de *TODAS* as mudanças não "
+"salvas.\n"
+"\n"
+"Abortar a mesclagem atual?"
+
+#: lib/merge.tcl:228
+msgid ""
+"Reset changes?\n"
+"\n"
+"Resetting the changes will cause *ALL* uncommitted changes to be lost.\n"
+"\n"
+"Continue with resetting the current changes?"
+msgstr ""
+"Descartar as mudanças?\n"
+"\n"
+"Ao fazê-lo, *TODAS* as alterações não salvas serão perdidas.\n"
+"\n"
+"Continuar e descartar as mudanças atuais?"
+
+#: lib/merge.tcl:239
+msgid "Aborting"
+msgstr "Abortando"
+
+#: lib/merge.tcl:239
+msgid "files reset"
+msgstr "arquivos redefindos"
+
+#: lib/merge.tcl:267
+msgid "Abort failed."
+msgstr "A tentativa de abortar a operação falhou"
+
+#: lib/merge.tcl:269
+msgid "Abort completed.  Ready."
+msgstr "Operação abortada com sucesso. Pronto."
+
+#: lib/mergetool.tcl:8
+msgid "Force resolution to the base version?"
+msgstr "Forçar a resolução para a versão base?"
+
+#: lib/mergetool.tcl:9
+msgid "Force resolution to this branch?"
+msgstr "Forçar resolução para este ramo?"
+
+#: lib/mergetool.tcl:10
+msgid "Force resolution to the other branch?"
+msgstr "Forçar resolução para o outro ramo?"
+
+#: lib/mergetool.tcl:14
+#, tcl-format
+msgid ""
+"Note that the diff shows only conflicting changes.\n"
+"\n"
+"%s will be overwritten.\n"
+"\n"
+"This operation can be undone only by restarting the merge."
+msgstr ""
+"Note que o diff mostra apenas as mudanças conflitantes.\n"
+"\n"
+"%s será sobrescrito.\n"
+"\n"
+"Caso necessário, será preciso reiniciar a mesclagem para desfazer esta "
+"operação."
+
+#: lib/mergetool.tcl:45
+#, tcl-format
+msgid "File %s seems to have unresolved conflicts, still stage?"
+msgstr "O arquivo %s parece ter conflitos não resolvidos. Marcar mesmo assim?"
+
+#: lib/mergetool.tcl:60
+#, tcl-format
+msgid "Adding resolution for %s"
+msgstr "Adicionando resolução para %s"
+
+#: lib/mergetool.tcl:141
+msgid "Cannot resolve deletion or link conflicts using a tool"
+msgstr ""
+"Impossível resolver conflitos envolvendo exclusão ou links de arquivos com "
+"esta ferramenta."
+
+#: lib/mergetool.tcl:146
+msgid "Conflict file does not exist"
+msgstr "O arquivo conflitante não existe"
+
+#: lib/mergetool.tcl:264
+#, tcl-format
+msgid "Not a GUI merge tool: '%s'"
+msgstr "Não é uma ferramenta de mesclagem gráfica: \"%s\""
+
+#: lib/mergetool.tcl:268
+#, tcl-format
+msgid "Unsupported merge tool '%s'"
+msgstr "Ferramenta de mesclagem não suportada \"%s\""
+
+#: lib/mergetool.tcl:303
+msgid "Merge tool is already running, terminate it?"
+msgstr "A ferramenta de mesclagem já está em execução. Finalizar?"
+
+#: lib/mergetool.tcl:323
+#, tcl-format
+msgid ""
+"Error retrieving versions:\n"
+"%s"
+msgstr ""
+"Erro ao obter as versões:\n"
+"%s"
+
+#: lib/mergetool.tcl:343
+#, tcl-format
+msgid ""
+"Could not start the merge tool:\n"
+"\n"
+"%s"
+msgstr ""
+"Não foi possível iniciar a ferramenta de mesclagem:\n"
+"\n"
+"%s"
+
+#: lib/mergetool.tcl:347
+msgid "Running merge tool..."
+msgstr "Executando ferramenta de mesclagem..."
+
+#: lib/mergetool.tcl:375 lib/mergetool.tcl:383
+msgid "Merge tool failed."
+msgstr "Ferramenta de mesclagem falhou."
+
+#: lib/option.tcl:11
+#, tcl-format
+msgid "Invalid global encoding '%s'"
+msgstr "Codificação global inválida \"%s\""
+
+#: lib/option.tcl:19
+#, tcl-format
+msgid "Invalid repo encoding '%s'"
+msgstr "Codificação do repositório inválida \"%s\""
+
+#: lib/option.tcl:117
+msgid "Restore Defaults"
+msgstr "Restaurar padrões"
+
+#: lib/option.tcl:121
+msgid "Save"
+msgstr "Salvar"
+
+#: lib/option.tcl:131
+#, tcl-format
+msgid "%s Repository"
+msgstr "Repositório %s"
+
+#: lib/option.tcl:132
+msgid "Global (All Repositories)"
+msgstr "Global (todos os repositórios)"
+
+#: lib/option.tcl:138
+msgid "User Name"
+msgstr "Nome do usuário"
+
+#: lib/option.tcl:139
+msgid "Email Address"
+msgstr "Endereço de e-mail"
+
+#: lib/option.tcl:141
+msgid "Summarize Merge Commits"
+msgstr "Exibir sumário das revisões de mesclagem"
+
+#: lib/option.tcl:142
+msgid "Merge Verbosity"
+msgstr "Nível de detalhamento da mesclagem"
+
+#: lib/option.tcl:143
+msgid "Show Diffstat After Merge"
+msgstr "Exibir estatísticas após mesclagens"
+
+#: lib/option.tcl:144
+msgid "Use Merge Tool"
+msgstr "Usar ferramenta de mesclagem"
+
+#: lib/option.tcl:146
+msgid "Trust File Modification Timestamps"
+msgstr "Confiar nas datas de modificação dos arquivos"
+
+#: lib/option.tcl:147
+msgid "Prune Tracking Branches During Fetch"
+msgstr "Eliminar ramos de rastreamento ao receber"
+
+#: lib/option.tcl:148
+msgid "Match Tracking Branches"
+msgstr "Coincidir ramos de rastreamento"
+
+#: lib/option.tcl:149
+msgid "Blame Copy Only On Changed Files"
+msgstr "Detectar cópias somente em arquivos modificados"
+
+#: lib/option.tcl:150
+msgid "Minimum Letters To Blame Copy On"
+msgstr "Número mínimo de letras para detectar cópias"
+
+#: lib/option.tcl:151
+msgid "Blame History Context Radius (days)"
+msgstr "Extensão do contexto de detecção (em dias)"
+
+#: lib/option.tcl:152
+msgid "Number of Diff Context Lines"
+msgstr "Número de linhas para o diff contextual"
+
+#: lib/option.tcl:153
+msgid "Commit Message Text Width"
+msgstr "Largura do texto da descrição da revisão"
+
+#: lib/option.tcl:154
+msgid "New Branch Name Template"
+msgstr "Modelo de nome para novos ramos"
+
+#: lib/option.tcl:155
+msgid "Default File Contents Encoding"
+msgstr "Codificação padrão dos arquivos"
+
+#: lib/option.tcl:203
+msgid "Change"
+msgstr "Alterar"
+
+#: lib/option.tcl:230
+msgid "Spelling Dictionary:"
+msgstr "Dicionário para o verificador ortográfico:"
+
+#: lib/option.tcl:254
+msgid "Change Font"
+msgstr "Mudar fonte"
+
+#: lib/option.tcl:258
+#, tcl-format
+msgid "Choose %s"
+msgstr "Escolher %s"
+
+#: lib/option.tcl:264
+msgid "pt."
+msgstr "pt."
+
+#: lib/option.tcl:278
+msgid "Preferences"
+msgstr "Preferências"
+
+#: lib/option.tcl:314
+msgid "Failed to completely save options:"
+msgstr "Houve um erro ao salvar as opções:"
+
+#: lib/remote.tcl:163
+msgid "Remove Remote"
+msgstr "Excluir"
+
+#: lib/remote.tcl:168
+msgid "Prune from"
+msgstr "Limpar de"
+
+#: lib/remote.tcl:173
+msgid "Fetch from"
+msgstr "Receber de"
+
+#: lib/remote.tcl:215
+msgid "Push to"
+msgstr "Enviar para"
+
+#: lib/remote_add.tcl:19
+msgid "Add Remote"
+msgstr "Adicionar repositório remoto"
+
+#: lib/remote_add.tcl:24
+msgid "Add New Remote"
+msgstr "Adicionar novo repositório remoto"
+
+#: lib/remote_add.tcl:28 lib/tools_dlg.tcl:36
+msgid "Add"
+msgstr "Adicionar"
+
+#: lib/remote_add.tcl:37
+msgid "Remote Details"
+msgstr "Detalhes do repositório remoto"
+
+#: lib/remote_add.tcl:50
+msgid "Location:"
+msgstr "Local:"
+
+#: lib/remote_add.tcl:62
+msgid "Further Action"
+msgstr "Ações adicionais"
+
+#: lib/remote_add.tcl:65
+msgid "Fetch Immediately"
+msgstr "Receber imediatamente"
+
+#: lib/remote_add.tcl:71
+msgid "Initialize Remote Repository and Push"
+msgstr "Inicializar repositório remoto e enviar"
+
+#: lib/remote_add.tcl:77
+msgid "Do Nothing Else Now"
+msgstr "Não fazer nada agora"
+
+#: lib/remote_add.tcl:101
+msgid "Please supply a remote name."
+msgstr "Por favor, indique um nome para o repositório remoto."
+
+#: lib/remote_add.tcl:114
+#, tcl-format
+msgid "'%s' is not an acceptable remote name."
+msgstr "\"%s\" não é um nome válido para um repositório remoto."
+
+#: lib/remote_add.tcl:125
+#, tcl-format
+msgid "Failed to add remote '%s' of location '%s'."
+msgstr "Erro ao adicionar repositório remoto \"%s\" do local \"%s\":"
+
+#: lib/remote_add.tcl:133 lib/transport.tcl:6
+#, tcl-format
+msgid "fetch %s"
+msgstr "receber %s"
+
+#: lib/remote_add.tcl:134
+#, tcl-format
+msgid "Fetching the %s"
+msgstr "Recebendo o %s"
+
+#: lib/remote_add.tcl:157
+#, tcl-format
+msgid "Do not know how to initialize repository at location '%s'."
+msgstr "Não sabe como inicializar o repositório remoto em \"%s\"."
+
+#: lib/remote_add.tcl:163 lib/transport.tcl:25 lib/transport.tcl:63
+#: lib/transport.tcl:81
+#, tcl-format
+msgid "push %s"
+msgstr "enviar %s"
+
+#: lib/remote_add.tcl:164
+#, tcl-format
+msgid "Setting up the %s (at %s)"
+msgstr "Configurando %s (em %s)"
+
+#: lib/remote_branch_delete.tcl:29 lib/remote_branch_delete.tcl:34
+msgid "Delete Branch Remotely"
+msgstr "Apagar ramo remoto"
+
+#: lib/remote_branch_delete.tcl:47
+msgid "From Repository"
+msgstr "Do repositório"
+
+#: lib/remote_branch_delete.tcl:50 lib/transport.tcl:134
+msgid "Remote:"
+msgstr "Remoto:"
+
+#: lib/remote_branch_delete.tcl:66 lib/transport.tcl:149
+msgid "Arbitrary Location:"
+msgstr "Outro local:"
+
+#: lib/remote_branch_delete.tcl:84
+msgid "Branches"
+msgstr "Ramos"
+
+#: lib/remote_branch_delete.tcl:109
+msgid "Delete Only If"
+msgstr "Apagar somente se"
+
+#: lib/remote_branch_delete.tcl:111
+msgid "Merged Into:"
+msgstr "Mesclado em:"
+
+#: lib/remote_branch_delete.tcl:152
+msgid "A branch is required for 'Merged Into'."
+msgstr "É preciso indicar um ramo para \"Mesclado em\"."
+
+#: lib/remote_branch_delete.tcl:184
+#, tcl-format
+msgid ""
+"The following branches are not completely merged into %s:\n"
+"\n"
+" - %s"
+msgstr ""
+"Os seguintes ramos não estão inteiramente mesclados em %s:\n"
+"\n"
+" - %s"
+
+#: lib/remote_branch_delete.tcl:189
+#, tcl-format
+msgid ""
+"One or more of the merge tests failed because you have not fetched the "
+"necessary commits.  Try fetching from %s first."
+msgstr ""
+"Um ou mais testes de mesclagem falharam porque você não possui as revisões "
+"necessárias. Tente receber revisões de %s primeiro."
+
+#: lib/remote_branch_delete.tcl:207
+msgid "Please select one or more branches to delete."
+msgstr "Por favor selecione um ou mais ramos para apagar."
+
+#: lib/remote_branch_delete.tcl:226
+#, tcl-format
+msgid "Deleting branches from %s"
+msgstr "Apagar ramos de %s"
+
+#: lib/remote_branch_delete.tcl:292
+msgid "No repository selected."
+msgstr "Nenhum repositório foi selecionado."
+
+#: lib/remote_branch_delete.tcl:297
+#, tcl-format
+msgid "Scanning %s..."
+msgstr "Atualizando %s..."
+
+#: lib/search.tcl:21
+msgid "Find:"
+msgstr "Encontrar:"
+
+#: lib/search.tcl:23
+msgid "Next"
+msgstr "Próximo"
+
+#: lib/search.tcl:24
+msgid "Prev"
+msgstr "Anterior"
+
+#: lib/search.tcl:25
+msgid "Case-Sensitive"
+msgstr "Sensível a maiúsculas/minúsculas"
+
+#: lib/shortcut.tcl:21 lib/shortcut.tcl:62
+msgid "Cannot write shortcut:"
+msgstr "Não foi possível gravar o atalho:"
+
+#: lib/shortcut.tcl:137
+msgid "Cannot write icon:"
+msgstr "Não foi possível gravar o ícone:"
+
+#: lib/spellcheck.tcl:57
+msgid "Unsupported spell checker"
+msgstr "Verificador ortográfico não suportado"
+
+#: lib/spellcheck.tcl:65
+msgid "Spell checking is unavailable"
+msgstr "Verificação ortográfica indisponível"
+
+#: lib/spellcheck.tcl:68
+msgid "Invalid spell checking configuration"
+msgstr "Configuração do verificador ortográfico inválida"
+
+#: lib/spellcheck.tcl:70
+#, tcl-format
+msgid "Reverting dictionary to %s."
+msgstr "Revertendo dicionário para %s."
+
+#: lib/spellcheck.tcl:73
+msgid "Spell checker silently failed on startup"
+msgstr "O verificador ortográfico falhou sem relatar nenhum erro"
+
+#: lib/spellcheck.tcl:80
+msgid "Unrecognized spell checker"
+msgstr "Verificador ortográfico não reconhecido"
+
+#: lib/spellcheck.tcl:186
+msgid "No Suggestions"
+msgstr "Sem sugestões"
+
+#: lib/spellcheck.tcl:388
+msgid "Unexpected EOF from spell checker"
+msgstr "Final de arquivo inesperado recebido do verificador ortográfico"
+
+#: lib/spellcheck.tcl:392
+msgid "Spell Checker Failed"
+msgstr "A verificação ortográfica falhou"
+
+#: lib/sshkey.tcl:31
+msgid "No keys found."
+msgstr "Nenhuma chave encontrada"
+
+#: lib/sshkey.tcl:34
+#, tcl-format
+msgid "Found a public key in: %s"
+msgstr "Chave pública encontrada em: %s"
+
+#: lib/sshkey.tcl:40
+msgid "Generate Key"
+msgstr "Gerar chave"
+
+#: lib/sshkey.tcl:56
+msgid "Copy To Clipboard"
+msgstr "Copiar para a área de transferência"
+
+#: lib/sshkey.tcl:70
+msgid "Your OpenSSH Public Key"
+msgstr "Sua chave pública OpenSSH"
+
+#: lib/sshkey.tcl:78
+msgid "Generating..."
+msgstr "Gerando..."
+
+#: lib/sshkey.tcl:84
+#, tcl-format
+msgid ""
+"Could not start ssh-keygen:\n"
+"\n"
+"%s"
+msgstr ""
+"Impossível iniciar ssh-keygen:\n"
+"\n"
+"%s"
+
+#: lib/sshkey.tcl:111
+msgid "Generation failed."
+msgstr "A geração da chave falhou."
+
+#: lib/sshkey.tcl:118
+msgid "Generation succeded, but no keys found."
+msgstr "A geração da chave foi bem-sucedida, mas nenhuma chave foi encontrada."
+
+#: lib/sshkey.tcl:121
+#, tcl-format
+msgid "Your key is in: %s"
+msgstr "Sua chave em: %s"
+
+#: lib/status_bar.tcl:83
+#, tcl-format
+msgid "%s ... %*i of %*i %s (%3i%%)"
+msgstr "%s ... %*i de %*i %s (%3i%%)"
+
+#: lib/tools.tcl:75
+#, tcl-format
+msgid "Running %s requires a selected file."
+msgstr "É preciso selecionar um arquivo para executar %s."
+
+#: lib/tools.tcl:90
+#, tcl-format
+msgid "Are you sure you want to run %s?"
+msgstr "Você tem certeza que deseja executar %s?"
+
+#: lib/tools.tcl:110
+#, tcl-format
+msgid "Tool: %s"
+msgstr "Ferramenta: %s"
+
+#: lib/tools.tcl:111
+#, tcl-format
+msgid "Running: %s"
+msgstr "Executando: %s"
+
+#: lib/tools.tcl:149
+#, tcl-format
+msgid "Tool completed successfully: %s"
+msgstr "Execução completada com sucesso: %s"
+
+#: lib/tools.tcl:151
+#, tcl-format
+msgid "Tool failed: %s"
+msgstr "Ferramenta falhou: %s"
+
+#: lib/tools_dlg.tcl:22
+msgid "Add Tool"
+msgstr "Adicionar ferramenta"
+
+#: lib/tools_dlg.tcl:28
+msgid "Add New Tool Command"
+msgstr "Adicionar novo comando de ferramenta"
+
+#: lib/tools_dlg.tcl:33
+msgid "Add globally"
+msgstr "Adicionar globalmente"
+
+#: lib/tools_dlg.tcl:45
+msgid "Tool Details"
+msgstr "Detalhes da ferramenta"
+
+#: lib/tools_dlg.tcl:48
+msgid "Use '/' separators to create a submenu tree:"
+msgstr "Use o separador \"/\" para criar uma árvore de sub-menus:"
+
+#: lib/tools_dlg.tcl:61
+msgid "Command:"
+msgstr "Comando:"
+
+#: lib/tools_dlg.tcl:74
+msgid "Show a dialog before running"
+msgstr "Exibir uma caixa de diálogo antes de executar"
+
+#: lib/tools_dlg.tcl:80
+msgid "Ask the user to select a revision (sets $REVISION)"
+msgstr "Solicitar a seleção de uma revisão (a variável $REVISION)"
+
+#: lib/tools_dlg.tcl:85
+msgid "Ask the user for additional arguments (sets $ARGS)"
+msgstr "Solicitar argumentos adicionais (define a variável $ARGS)"
+
+#: lib/tools_dlg.tcl:92
+msgid "Don't show the command output window"
+msgstr "Não exibir a janela de saída do comando"
+
+#: lib/tools_dlg.tcl:97
+msgid "Run only if a diff is selected ($FILENAME not empty)"
+msgstr "Executar apenas se houver um diff selecionado ($FILENAME não-vazio)"
+
+#: lib/tools_dlg.tcl:121
+msgid "Please supply a name for the tool."
+msgstr "Por favor, indique um nome para a ferramenta."
+
+#: lib/tools_dlg.tcl:129
+#, tcl-format
+msgid "Tool '%s' already exists."
+msgstr "A ferramenta \"%s\" já existe."
+
+#: lib/tools_dlg.tcl:151
+#, tcl-format
+msgid ""
+"Could not add tool:\n"
+"%s"
+msgstr ""
+"Não foi possível adicionar a ferramenta:\n"
+"%s"
+
+#: lib/tools_dlg.tcl:190
+msgid "Remove Tool"
+msgstr "Excluir ferramenta"
+
+#: lib/tools_dlg.tcl:196
+msgid "Remove Tool Commands"
+msgstr "Excluir comando de ferramenta"
+
+#: lib/tools_dlg.tcl:200
+msgid "Remove"
+msgstr "Excluir"
+
+#: lib/tools_dlg.tcl:236
+msgid "(Blue denotes repository-local tools)"
+msgstr "(Azul indica ferramentas do repositório local)"
+
+#: lib/tools_dlg.tcl:297
+#, tcl-format
+msgid "Run Command: %s"
+msgstr "Executar comando: %s"
+
+#: lib/tools_dlg.tcl:311
+msgid "Arguments"
+msgstr "Argumentos"
+
+#: lib/tools_dlg.tcl:348
+msgid "OK"
+msgstr "OK"
+
+#: lib/transport.tcl:7
+#, tcl-format
+msgid "Fetching new changes from %s"
+msgstr "Recebendo novas mudanças de %s"
+
+#: lib/transport.tcl:18
+#, tcl-format
+msgid "remote prune %s"
+msgstr "Limpar %s"
+
+#: lib/transport.tcl:19
+#, tcl-format
+msgid "Pruning tracking branches deleted from %s"
+msgstr "Limpando ramos excluídos de %s"
+
+#: lib/transport.tcl:26
+#, tcl-format
+msgid "Pushing changes to %s"
+msgstr "Enviando mudanças para %s"
+
+#: lib/transport.tcl:64
+#, tcl-format
+msgid "Mirroring to %s"
+msgstr "Duplicando para %s"
+
+#: lib/transport.tcl:82
+#, tcl-format
+msgid "Pushing %s %s to %s"
+msgstr "Enviando %s %s para %s"
+
+#: lib/transport.tcl:100
+msgid "Push Branches"
+msgstr "Enviar ramos"
+
+#: lib/transport.tcl:114
+msgid "Source Branches"
+msgstr "Ramos de origem"
+
+#: lib/transport.tcl:131
+msgid "Destination Repository"
+msgstr "Repositório de destino"
+
+#: lib/transport.tcl:169
+msgid "Transfer Options"
+msgstr "Opções de transferência"
+
+#: lib/transport.tcl:171
+msgid "Force overwrite existing branch (may discard changes)"
+msgstr "Sobrescrever ramos existentes (pode descartar mudanças)"
+
+#: lib/transport.tcl:175
+msgid "Use thin pack (for slow network connections)"
+msgstr "Usar compactação minimalista (para redes lentas)"
+
+#: lib/transport.tcl:179
+msgid "Include tags"
+msgstr "Incluir etiquetas"
diff --git a/git-gui/po/ru.po b/git-gui/po/ru.po
index 0ffc4a4..30f4b77 100644
--- a/git-gui/po/ru.po
+++ b/git-gui/po/ru.po
@@ -7,7 +7,7 @@
 msgstr ""
 "Project-Id-Version: git-gui\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-12-08 08:31-0800\n"
+"POT-Creation-Date: 2010-01-26 15:47-0800\n"
 "PO-Revision-Date: 2007-10-22 22:30-0200\n"
 "Last-Translator: Alex Riesen <raa.lkml@gmail.com>\n"
 "Language-Team: Russian Translation <git@vger.kernel.org>\n"
@@ -15,33 +15,33 @@
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: git-gui.sh:41 git-gui.sh:737 git-gui.sh:751 git-gui.sh:764 git-gui.sh:847
-#: git-gui.sh:866
+#: git-gui.sh:41 git-gui.sh:793 git-gui.sh:807 git-gui.sh:820 git-gui.sh:903
+#: git-gui.sh:922
 msgid "git-gui: fatal error"
 msgstr "git-gui: критическая ошибка"
 
-#: git-gui.sh:689
+#: git-gui.sh:743
 #, tcl-format
 msgid "Invalid font specified in %s:"
 msgstr "В %s установлен неверный шрифт:"
 
-#: git-gui.sh:723
+#: git-gui.sh:779
 msgid "Main Font"
 msgstr "Шрифт интерфейса"
 
-#: git-gui.sh:724
+#: git-gui.sh:780
 msgid "Diff/Console Font"
 msgstr "Шрифт консоли и изменений (diff)"
 
-#: git-gui.sh:738
+#: git-gui.sh:794
 msgid "Cannot find git in PATH."
 msgstr "git не найден в PATH."
 
-#: git-gui.sh:765
+#: git-gui.sh:821
 msgid "Cannot parse Git version string:"
 msgstr "Невозможно распознать строку версии Git: "
 
-#: git-gui.sh:783
+#: git-gui.sh:839
 #, tcl-format
 msgid ""
 "Git version cannot be determined.\n"
@@ -60,444 +60,474 @@
 "\n"
 "Принять '%s' как версию 1.5.0?\n"
 
-#: git-gui.sh:1062
+#: git-gui.sh:1128
 msgid "Git directory not found:"
 msgstr "Каталог Git не найден:"
 
-#: git-gui.sh:1069
+#: git-gui.sh:1146
 msgid "Cannot move to top of working directory:"
 msgstr "Невозможно перейти к корню рабочего каталога репозитория: "
 
-#: git-gui.sh:1076
-msgid "Cannot use funny .git directory:"
-msgstr "Каталог .git испорчен: "
+#: git-gui.sh:1154
+msgid "Cannot use bare repository:"
+msgstr "Невозможно использование репозитория без рабочего каталога:"
 
-#: git-gui.sh:1081
+#: git-gui.sh:1162
 msgid "No working directory"
 msgstr "Отсутствует рабочий каталог"
 
-#: git-gui.sh:1247 lib/checkout_op.tcl:305
+#: git-gui.sh:1334 lib/checkout_op.tcl:306
 msgid "Refreshing file status..."
 msgstr "Обновление информации о состоянии файлов..."
 
-#: git-gui.sh:1303
+#: git-gui.sh:1390
 msgid "Scanning for modified files ..."
 msgstr "Поиск измененных файлов..."
 
-#: git-gui.sh:1367
+#: git-gui.sh:1454
 msgid "Calling prepare-commit-msg hook..."
 msgstr "Вызов программы поддержки репозитория prepare-commit-msg..."
 
-#: git-gui.sh:1384
+#: git-gui.sh:1471
 msgid "Commit declined by prepare-commit-msg hook."
-msgstr "Сохранение прервано программой поддержки репозитория prepare-commit-msg"
+msgstr ""
+"Сохранение прервано программой поддержки репозитория prepare-commit-msg"
 
-#: git-gui.sh:1542 lib/browser.tcl:246
+#: git-gui.sh:1629 lib/browser.tcl:246
 msgid "Ready."
 msgstr "Готово."
 
-#: git-gui.sh:1819
+#: git-gui.sh:1787
+#, tcl-format
+msgid "Displaying only %s of %s files."
+msgstr "Показано %s из %s файлов."
+
+#: git-gui.sh:1913
 msgid "Unmodified"
 msgstr "Не изменено"
 
-#: git-gui.sh:1821
+#: git-gui.sh:1915
 msgid "Modified, not staged"
 msgstr "Изменено, не подготовлено"
 
-#: git-gui.sh:1822 git-gui.sh:1830
+#: git-gui.sh:1916 git-gui.sh:1924
 msgid "Staged for commit"
 msgstr "Подготовлено для сохранения"
 
-#: git-gui.sh:1823 git-gui.sh:1831
+#: git-gui.sh:1917 git-gui.sh:1925
 msgid "Portions staged for commit"
 msgstr "Части, подготовленные для сохранения"
 
-#: git-gui.sh:1824 git-gui.sh:1832
+#: git-gui.sh:1918 git-gui.sh:1926
 msgid "Staged for commit, missing"
 msgstr "Подготовлено для сохранения, отсутствует"
 
-#: git-gui.sh:1826
+#: git-gui.sh:1920
 msgid "File type changed, not staged"
 msgstr "Тип файла изменён, не подготовлено"
 
-#: git-gui.sh:1827
+#: git-gui.sh:1921
 msgid "File type changed, staged"
 msgstr "Тип файла изменён, подготовлено"
 
-#: git-gui.sh:1829
+#: git-gui.sh:1923
 msgid "Untracked, not staged"
 msgstr "Не отслеживается, не подготовлено"
 
-#: git-gui.sh:1834
+#: git-gui.sh:1928
 msgid "Missing"
 msgstr "Отсутствует"
 
-#: git-gui.sh:1835
+#: git-gui.sh:1929
 msgid "Staged for removal"
 msgstr "Подготовлено для удаления"
 
-#: git-gui.sh:1836
+#: git-gui.sh:1930
 msgid "Staged for removal, still present"
 msgstr "Подготовлено для удаления, еще не удалено"
 
-#: git-gui.sh:1838 git-gui.sh:1839 git-gui.sh:1840 git-gui.sh:1841
-#: git-gui.sh:1842 git-gui.sh:1843
+#: git-gui.sh:1932 git-gui.sh:1933 git-gui.sh:1934 git-gui.sh:1935
+#: git-gui.sh:1936 git-gui.sh:1937
 msgid "Requires merge resolution"
 msgstr "Требуется разрешение конфликта при слиянии"
 
-#: git-gui.sh:1878
+#: git-gui.sh:1972
 msgid "Starting gitk... please wait..."
 msgstr "Запускается gitk... Подождите, пожалуйста..."
 
-#: git-gui.sh:1887
+#: git-gui.sh:1984
 msgid "Couldn't find gitk in PATH"
 msgstr "gitk не найден в PATH."
 
-#: git-gui.sh:2280 lib/choose_repository.tcl:36
+#: git-gui.sh:2043
+msgid "Couldn't find git gui in PATH"
+msgstr "git gui не найден в PATH."
+
+#: git-gui.sh:2455 lib/choose_repository.tcl:36
 msgid "Repository"
 msgstr "Репозиторий"
 
-#: git-gui.sh:2281
+#: git-gui.sh:2456
 msgid "Edit"
 msgstr "Редактировать"
 
-#: git-gui.sh:2283 lib/choose_rev.tcl:561
+#: git-gui.sh:2458 lib/choose_rev.tcl:561
 msgid "Branch"
 msgstr "Ветвь"
 
-#: git-gui.sh:2286 lib/choose_rev.tcl:548
+#: git-gui.sh:2461 lib/choose_rev.tcl:548
 msgid "Commit@@noun"
 msgstr "Состояние"
 
-#: git-gui.sh:2289 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
+#: git-gui.sh:2464 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
 msgid "Merge"
 msgstr "Слияние"
 
-#: git-gui.sh:2290 lib/choose_rev.tcl:557
+#: git-gui.sh:2465 lib/choose_rev.tcl:557
 msgid "Remote"
 msgstr "Внешние репозитории"
 
-#: git-gui.sh:2293
+#: git-gui.sh:2468
 msgid "Tools"
 msgstr "Вспомогательные операции"
 
-#: git-gui.sh:2302
+#: git-gui.sh:2477
 msgid "Explore Working Copy"
 msgstr "Просмотр рабочего каталога"
 
-#: git-gui.sh:2307
+#: git-gui.sh:2483
 msgid "Browse Current Branch's Files"
 msgstr "Просмотреть файлы текущей ветви"
 
-#: git-gui.sh:2311
+#: git-gui.sh:2487
 msgid "Browse Branch Files..."
 msgstr "Показать файлы ветви..."
 
-#: git-gui.sh:2316
+#: git-gui.sh:2492
 msgid "Visualize Current Branch's History"
 msgstr "Показать историю текущей ветви"
 
-#: git-gui.sh:2320
+#: git-gui.sh:2496
 msgid "Visualize All Branch History"
 msgstr "Показать историю всех ветвей"
 
-#: git-gui.sh:2327
+#: git-gui.sh:2503
 #, tcl-format
 msgid "Browse %s's Files"
 msgstr "Показать файлы ветви %s"
 
-#: git-gui.sh:2329
+#: git-gui.sh:2505
 #, tcl-format
 msgid "Visualize %s's History"
 msgstr "Показать историю ветви %s"
 
-#: git-gui.sh:2334 lib/database.tcl:27 lib/database.tcl:67
+#: git-gui.sh:2510 lib/database.tcl:27 lib/database.tcl:67
 msgid "Database Statistics"
 msgstr "Статистика базы данных"
 
-#: git-gui.sh:2337 lib/database.tcl:34
+#: git-gui.sh:2513 lib/database.tcl:34
 msgid "Compress Database"
 msgstr "Сжать базу данных"
 
-#: git-gui.sh:2340
+#: git-gui.sh:2516
 msgid "Verify Database"
 msgstr "Проверить базу данных"
 
-#: git-gui.sh:2347 git-gui.sh:2351 git-gui.sh:2355 lib/shortcut.tcl:7
-#: lib/shortcut.tcl:39 lib/shortcut.tcl:71
+#: git-gui.sh:2523 git-gui.sh:2527 git-gui.sh:2531 lib/shortcut.tcl:8
+#: lib/shortcut.tcl:40 lib/shortcut.tcl:72
 msgid "Create Desktop Icon"
 msgstr "Создать ярлык на рабочем столе"
 
-#: git-gui.sh:2363 lib/choose_repository.tcl:183 lib/choose_repository.tcl:191
+#: git-gui.sh:2539 lib/choose_repository.tcl:183 lib/choose_repository.tcl:191
 msgid "Quit"
 msgstr "Выход"
 
-#: git-gui.sh:2371
+#: git-gui.sh:2547
 msgid "Undo"
 msgstr "Отменить"
 
-#: git-gui.sh:2374
+#: git-gui.sh:2550
 msgid "Redo"
 msgstr "Повторить"
 
-#: git-gui.sh:2378 git-gui.sh:2937
+#: git-gui.sh:2554 git-gui.sh:3109
 msgid "Cut"
 msgstr "Вырезать"
 
-#: git-gui.sh:2381 git-gui.sh:2940 git-gui.sh:3014 git-gui.sh:3096
+#: git-gui.sh:2557 git-gui.sh:3112 git-gui.sh:3186 git-gui.sh:3259
 #: lib/console.tcl:69
 msgid "Copy"
 msgstr "Копировать"
 
-#: git-gui.sh:2384 git-gui.sh:2943
+#: git-gui.sh:2560 git-gui.sh:3115
 msgid "Paste"
 msgstr "Вставить"
 
-#: git-gui.sh:2387 git-gui.sh:2946 lib/branch_delete.tcl:26
+#: git-gui.sh:2563 git-gui.sh:3118 lib/branch_delete.tcl:26
 #: lib/remote_branch_delete.tcl:38
 msgid "Delete"
 msgstr "Удалить"
 
-#: git-gui.sh:2391 git-gui.sh:2950 git-gui.sh:3100 lib/console.tcl:71
+#: git-gui.sh:2567 git-gui.sh:3122 git-gui.sh:3263 lib/console.tcl:71
 msgid "Select All"
 msgstr "Выделить все"
 
-#: git-gui.sh:2400
+#: git-gui.sh:2576
 msgid "Create..."
 msgstr "Создать..."
 
-#: git-gui.sh:2406
+#: git-gui.sh:2582
 msgid "Checkout..."
 msgstr "Перейти..."
 
-#: git-gui.sh:2412
+#: git-gui.sh:2588
 msgid "Rename..."
 msgstr "Переименовать..."
 
-#: git-gui.sh:2417
+#: git-gui.sh:2593
 msgid "Delete..."
 msgstr "Удалить..."
 
-#: git-gui.sh:2422
+#: git-gui.sh:2598
 msgid "Reset..."
 msgstr "Сбросить..."
 
-#: git-gui.sh:2432
+#: git-gui.sh:2608
 msgid "Done"
 msgstr "Завершено"
 
-#: git-gui.sh:2434
+#: git-gui.sh:2610
 msgid "Commit@@verb"
 msgstr "Сохранить"
 
-#: git-gui.sh:2443 git-gui.sh:2878
+#: git-gui.sh:2619 git-gui.sh:3050
 msgid "New Commit"
 msgstr "Новое состояние"
 
-#: git-gui.sh:2451 git-gui.sh:2885
+#: git-gui.sh:2627 git-gui.sh:3057
 msgid "Amend Last Commit"
 msgstr "Исправить последнее состояние"
 
-#: git-gui.sh:2461 git-gui.sh:2839 lib/remote_branch_delete.tcl:99
+#: git-gui.sh:2637 git-gui.sh:3011 lib/remote_branch_delete.tcl:99
 msgid "Rescan"
 msgstr "Перечитать"
 
-#: git-gui.sh:2467
+#: git-gui.sh:2643
 msgid "Stage To Commit"
 msgstr "Подготовить для сохранения"
 
-#: git-gui.sh:2473
+#: git-gui.sh:2649
 msgid "Stage Changed Files To Commit"
 msgstr "Подготовить измененные файлы для сохранения"
 
-#: git-gui.sh:2479
+#: git-gui.sh:2655
 msgid "Unstage From Commit"
 msgstr "Убрать из подготовленного"
 
-#: git-gui.sh:2484 lib/index.tcl:410
+#: git-gui.sh:2661 lib/index.tcl:412
 msgid "Revert Changes"
 msgstr "Отменить изменения"
 
-#: git-gui.sh:2491 git-gui.sh:3083
+#: git-gui.sh:2669 git-gui.sh:3310 git-gui.sh:3341
 msgid "Show Less Context"
 msgstr "Меньше контекста"
 
-#: git-gui.sh:2495 git-gui.sh:3087
+#: git-gui.sh:2673 git-gui.sh:3314 git-gui.sh:3345
 msgid "Show More Context"
 msgstr "Больше контекста"
 
-#: git-gui.sh:2502 git-gui.sh:2852 git-gui.sh:2961
+#: git-gui.sh:2680 git-gui.sh:3024 git-gui.sh:3133
 msgid "Sign Off"
 msgstr "Вставить Signed-off-by"
 
-#: git-gui.sh:2518
+#: git-gui.sh:2696
 msgid "Local Merge..."
 msgstr "Локальное слияние..."
 
-#: git-gui.sh:2523
+#: git-gui.sh:2701
 msgid "Abort Merge..."
 msgstr "Прервать слияние..."
 
-#: git-gui.sh:2535 git-gui.sh:2575
+#: git-gui.sh:2713 git-gui.sh:2741
 msgid "Add..."
 msgstr "Добавить..."
 
-#: git-gui.sh:2539
+#: git-gui.sh:2717
 msgid "Push..."
 msgstr "Отправить..."
 
-#: git-gui.sh:2543
+#: git-gui.sh:2721
 msgid "Delete Branch..."
 msgstr "Удалить ветвь..."
 
-#: git-gui.sh:2553 git-gui.sh:2589 lib/about.tcl:14
+#: git-gui.sh:2731 git-gui.sh:3292
+msgid "Options..."
+msgstr "Настройки..."
+
+#: git-gui.sh:2742
+msgid "Remove..."
+msgstr "Удалить..."
+
+#: git-gui.sh:2751 lib/choose_repository.tcl:50
+msgid "Help"
+msgstr "Помощь"
+
+#: git-gui.sh:2755 git-gui.sh:2759 lib/about.tcl:14
 #: lib/choose_repository.tcl:44 lib/choose_repository.tcl:53
 #, tcl-format
 msgid "About %s"
 msgstr "О %s"
 
-#: git-gui.sh:2557
-msgid "Preferences..."
-msgstr "Настройки..."
-
-#: git-gui.sh:2565 git-gui.sh:3129
-msgid "Options..."
-msgstr "Настройки..."
-
-#: git-gui.sh:2576
-msgid "Remove..."
-msgstr "Удалить..."
-
-#: git-gui.sh:2585 lib/choose_repository.tcl:50
-msgid "Help"
-msgstr "Помощь"
-
-#: git-gui.sh:2611
+#: git-gui.sh:2783
 msgid "Online Documentation"
 msgstr "Документация в интернете"
 
-#: git-gui.sh:2614 lib/choose_repository.tcl:47 lib/choose_repository.tcl:56
+#: git-gui.sh:2786 lib/choose_repository.tcl:47 lib/choose_repository.tcl:56
 msgid "Show SSH Key"
 msgstr "Показать ключ SSH"
 
-#: git-gui.sh:2721
+#: git-gui.sh:2893
 #, tcl-format
 msgid "fatal: cannot stat path %s: No such file or directory"
 msgstr "критическая ошибка: %s: нет такого файла или каталога"
 
-#: git-gui.sh:2754
+#: git-gui.sh:2926
 msgid "Current Branch:"
 msgstr "Текущая ветвь:"
 
-#: git-gui.sh:2775
+#: git-gui.sh:2947
 msgid "Staged Changes (Will Commit)"
 msgstr "Подготовлено (будет сохранено)"
 
-#: git-gui.sh:2795
+#: git-gui.sh:2967
 msgid "Unstaged Changes"
 msgstr "Изменено (не будет сохранено)"
 
-#: git-gui.sh:2845
+#: git-gui.sh:3017
 msgid "Stage Changed"
 msgstr "Подготовить все"
 
-#: git-gui.sh:2864 lib/transport.tcl:104 lib/transport.tcl:193
+#: git-gui.sh:3036 lib/transport.tcl:104 lib/transport.tcl:193
 msgid "Push"
 msgstr "Отправить"
 
-#: git-gui.sh:2899
+#: git-gui.sh:3071
 msgid "Initial Commit Message:"
 msgstr "Комментарий к первому состоянию:"
 
-#: git-gui.sh:2900
+#: git-gui.sh:3072
 msgid "Amended Commit Message:"
 msgstr "Комментарий к исправленному состоянию:"
 
-#: git-gui.sh:2901
+#: git-gui.sh:3073
 msgid "Amended Initial Commit Message:"
 msgstr "Комментарий к исправленному первоначальному состоянию:"
 
-#: git-gui.sh:2902
+#: git-gui.sh:3074
 msgid "Amended Merge Commit Message:"
 msgstr "Комментарий к исправленному слиянию:"
 
-#: git-gui.sh:2903
+#: git-gui.sh:3075
 msgid "Merge Commit Message:"
 msgstr "Комментарий к слиянию:"
 
-#: git-gui.sh:2904
+#: git-gui.sh:3076
 msgid "Commit Message:"
 msgstr "Комментарий к состоянию:"
 
-#: git-gui.sh:2953 git-gui.sh:3104 lib/console.tcl:73
+#: git-gui.sh:3125 git-gui.sh:3267 lib/console.tcl:73
 msgid "Copy All"
 msgstr "Копировать все"
 
-#: git-gui.sh:2977 lib/blame.tcl:104
+#: git-gui.sh:3149 lib/blame.tcl:104
 msgid "File:"
 msgstr "Файл:"
 
-#: git-gui.sh:3092
+#: git-gui.sh:3255
 msgid "Refresh"
 msgstr "Обновить"
 
-#: git-gui.sh:3113
+#: git-gui.sh:3276
 msgid "Decrease Font Size"
 msgstr "Уменьшить размер шрифта"
 
-#: git-gui.sh:3117
+#: git-gui.sh:3280
 msgid "Increase Font Size"
 msgstr "Увеличить размер шрифта"
 
-#: git-gui.sh:3125 lib/blame.tcl:281
+#: git-gui.sh:3288 lib/blame.tcl:281
 msgid "Encoding"
 msgstr "Кодировка"
 
-#: git-gui.sh:3136
+#: git-gui.sh:3299
 msgid "Apply/Reverse Hunk"
 msgstr "Применить/Убрать изменение"
 
-#: git-gui.sh:3141
+#: git-gui.sh:3304
 msgid "Apply/Reverse Line"
 msgstr "Применить/Убрать строку"
 
-#: git-gui.sh:3151
+#: git-gui.sh:3323
 msgid "Run Merge Tool"
 msgstr "Запустить программу слияния"
 
-#: git-gui.sh:3156
+#: git-gui.sh:3328
 msgid "Use Remote Version"
 msgstr "Взять внешнюю версию"
 
-#: git-gui.sh:3160
+#: git-gui.sh:3332
 msgid "Use Local Version"
 msgstr "Взять локальную версию"
 
-#: git-gui.sh:3164
+#: git-gui.sh:3336
 msgid "Revert To Base"
 msgstr "Отменить изменения"
 
-#: git-gui.sh:3183
+#: git-gui.sh:3354
+msgid "Visualize These Changes In The Submodule"
+msgstr ""
+
+#: git-gui.sh:3358
+msgid "Visualize Current Branch History In The Submodule"
+msgstr "Показать историю текущей ветви подмодуля"
+
+#: git-gui.sh:3362
+msgid "Visualize All Branch History In The Submodule"
+msgstr "Показать историю всех ветвей подмодуля"
+
+#: git-gui.sh:3367
+msgid "Start git gui In The Submodule"
+msgstr ""
+
+#: git-gui.sh:3389
 msgid "Unstage Hunk From Commit"
 msgstr "Не сохранять часть"
 
-#: git-gui.sh:3184
+#: git-gui.sh:3391
+msgid "Unstage Lines From Commit"
+msgstr "Убрать строки из подготовленного"
+
+#: git-gui.sh:3393
 msgid "Unstage Line From Commit"
 msgstr "Убрать строку из подготовленного"
 
-#: git-gui.sh:3186
+#: git-gui.sh:3396
 msgid "Stage Hunk For Commit"
 msgstr "Подготовить часть для сохранения"
 
-#: git-gui.sh:3187
+#: git-gui.sh:3398
+msgid "Stage Lines For Commit"
+msgstr "Подготовить строки для сохранения"
+
+#: git-gui.sh:3400
 msgid "Stage Line For Commit"
 msgstr "Подготовить строку для сохранения"
 
-#: git-gui.sh:3210
+#: git-gui.sh:3424
 msgid "Initializing..."
 msgstr "Инициализация..."
 
-#: git-gui.sh:3315
+#: git-gui.sh:3541
 #, tcl-format
 msgid ""
 "Possible environment issues exist.\n"
@@ -514,7 +544,7 @@
 "запущенными из %s\n"
 "\n"
 
-#: git-gui.sh:3345
+#: git-gui.sh:3570
 msgid ""
 "\n"
 "This is due to a known issue with the\n"
@@ -524,7 +554,7 @@
 "Это известная проблема с Tcl,\n"
 "распространяемым Cygwin."
 
-#: git-gui.sh:3350
+#: git-gui.sh:3575
 #, tcl-format
 msgid ""
 "\n"
@@ -634,7 +664,7 @@
 msgid "Unable to display parent"
 msgstr "Не могу показать предка"
 
-#: lib/blame.tcl:1091 lib/diff.tcl:297
+#: lib/blame.tcl:1091 lib/diff.tcl:320
 msgid "Error loading diff:"
 msgstr "Ошибка загрузки изменений:"
 
@@ -660,7 +690,7 @@
 
 #: lib/branch_checkout.tcl:27 lib/branch_create.tcl:35
 #: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:282
-#: lib/checkout_op.tcl:544 lib/choose_font.tcl:43 lib/merge.tcl:172
+#: lib/checkout_op.tcl:579 lib/choose_font.tcl:43 lib/merge.tcl:172
 #: lib/option.tcl:125 lib/remote_add.tcl:32 lib/remote_branch_delete.tcl:42
 #: lib/tools_dlg.tcl:40 lib/tools_dlg.tcl:204 lib/tools_dlg.tcl:352
 #: lib/transport.tcl:108
@@ -691,7 +721,7 @@
 msgid "Create New Branch"
 msgstr "Создать новую ветвь"
 
-#: lib/branch_create.tcl:31 lib/choose_repository.tcl:377
+#: lib/branch_create.tcl:31 lib/choose_repository.tcl:381
 msgid "Create"
 msgstr "Создать"
 
@@ -723,7 +753,7 @@
 msgid "Fast Forward Only"
 msgstr "Только Fast Forward"
 
-#: lib/branch_create.tcl:85 lib/checkout_op.tcl:536
+#: lib/branch_create.tcl:85 lib/checkout_op.tcl:571
 msgid "Reset"
 msgstr "Сброс"
 
@@ -765,8 +795,8 @@
 msgid "Delete Only If Merged Into"
 msgstr "Удалить только в случае, если было слияние с"
 
-#: lib/branch_delete.tcl:54
-msgid "Always (Do not perform merge test.)"
+#: lib/branch_delete.tcl:54 lib/remote_branch_delete.tcl:119
+msgid "Always (Do not perform merge checks)"
 msgstr "Всегда (не выполнять проверку на слияние)"
 
 #: lib/branch_delete.tcl:103
@@ -774,6 +804,16 @@
 msgid "The following branches are not completely merged into %s:"
 msgstr "Ветви, которые не полностью сливаются с %s:"
 
+#: lib/branch_delete.tcl:115 lib/remote_branch_delete.tcl:217
+msgid ""
+"Recovering deleted branches is difficult.\n"
+"\n"
+"Delete the selected branches?"
+msgstr ""
+"Восстановить удаленные ветви сложно.\n"
+"\n"
+"Продолжить?"
+
 #: lib/branch_delete.tcl:141
 #, tcl-format
 msgid ""
@@ -803,7 +843,7 @@
 msgid "Please select a branch to rename."
 msgstr "Укажите ветвь для переименования."
 
-#: lib/branch_rename.tcl:96 lib/checkout_op.tcl:201
+#: lib/branch_rename.tcl:96 lib/checkout_op.tcl:202
 #, tcl-format
 msgid "Branch '%s' already exists."
 msgstr "Ветвь '%s' уже существует."
@@ -834,38 +874,38 @@
 msgid "Browse Branch Files"
 msgstr "Показать файлы ветви"
 
-#: lib/browser.tcl:278 lib/choose_repository.tcl:394
-#: lib/choose_repository.tcl:480 lib/choose_repository.tcl:491
-#: lib/choose_repository.tcl:995
+#: lib/browser.tcl:278 lib/choose_repository.tcl:398
+#: lib/choose_repository.tcl:486 lib/choose_repository.tcl:497
+#: lib/choose_repository.tcl:1028
 msgid "Browse"
 msgstr "Показать"
 
-#: lib/checkout_op.tcl:84
+#: lib/checkout_op.tcl:85
 #, tcl-format
 msgid "Fetching %s from %s"
 msgstr "Получение %s из %s "
 
-#: lib/checkout_op.tcl:132
+#: lib/checkout_op.tcl:133
 #, tcl-format
 msgid "fatal: Cannot resolve %s"
 msgstr "критическая ошибка: невозможно разрешить %s"
 
-#: lib/checkout_op.tcl:145 lib/console.tcl:81 lib/database.tcl:31
+#: lib/checkout_op.tcl:146 lib/console.tcl:81 lib/database.tcl:31
 #: lib/sshkey.tcl:53
 msgid "Close"
 msgstr "Закрыть"
 
-#: lib/checkout_op.tcl:174
+#: lib/checkout_op.tcl:175
 #, tcl-format
 msgid "Branch '%s' does not exist."
 msgstr "Ветвь '%s' не существует "
 
-#: lib/checkout_op.tcl:193
+#: lib/checkout_op.tcl:194
 #, tcl-format
 msgid "Failed to configure simplified git-pull for '%s'."
 msgstr "Ошибка создания упрощённой конфигурации git pull для '%s'."
 
-#: lib/checkout_op.tcl:228
+#: lib/checkout_op.tcl:229
 #, tcl-format
 msgid ""
 "Branch '%s' already exists.\n"
@@ -878,21 +918,21 @@
 "Она не может быть прокручена(fast-forward) к %s.\n"
 "Требуется слияние."
 
-#: lib/checkout_op.tcl:242
+#: lib/checkout_op.tcl:243
 #, tcl-format
 msgid "Merge strategy '%s' not supported."
 msgstr "Неизвестная стратегия слияния: '%s'."
 
-#: lib/checkout_op.tcl:261
+#: lib/checkout_op.tcl:262
 #, tcl-format
 msgid "Failed to update '%s'."
 msgstr "Не удалось обновить '%s'."
 
-#: lib/checkout_op.tcl:273
+#: lib/checkout_op.tcl:274
 msgid "Staging area (index) is already locked."
 msgstr "Рабочая область заблокирована другим процессом."
 
-#: lib/checkout_op.tcl:288
+#: lib/checkout_op.tcl:289
 msgid ""
 "Last scanned state does not match repository state.\n"
 "\n"
@@ -908,30 +948,30 @@
 "\n"
 "Это будет сделано сейчас автоматически.\n"
 
-#: lib/checkout_op.tcl:344
+#: lib/checkout_op.tcl:345
 #, tcl-format
 msgid "Updating working directory to '%s'..."
 msgstr "Обновление рабочего каталога из '%s'..."
 
-#: lib/checkout_op.tcl:345
+#: lib/checkout_op.tcl:346
 msgid "files checked out"
 msgstr "файлы извлечены"
 
-#: lib/checkout_op.tcl:375
+#: lib/checkout_op.tcl:376
 #, tcl-format
 msgid "Aborted checkout of '%s' (file level merging is required)."
 msgstr "Прерван переход на '%s' (требуется слияние содержания файлов)"
 
-#: lib/checkout_op.tcl:376
+#: lib/checkout_op.tcl:377
 msgid "File level merge required."
 msgstr "Требуется слияние содержания файлов."
 
-#: lib/checkout_op.tcl:380
+#: lib/checkout_op.tcl:381
 #, tcl-format
 msgid "Staying on branch '%s'."
 msgstr "Ветвь '%s' остается текущей."
 
-#: lib/checkout_op.tcl:451
+#: lib/checkout_op.tcl:452
 msgid ""
 "You are no longer on a local branch.\n"
 "\n"
@@ -943,30 +983,30 @@
 "Если вы хотите снова вернуться к какой-нибудь ветви, создайте ее сейчас, "
 "начиная с 'Текущего отсоединенного состояния'."
 
-#: lib/checkout_op.tcl:468 lib/checkout_op.tcl:472
+#: lib/checkout_op.tcl:503 lib/checkout_op.tcl:507
 #, tcl-format
 msgid "Checked out '%s'."
 msgstr "Ветвь '%s' сделана текущей."
 
-#: lib/checkout_op.tcl:500
+#: lib/checkout_op.tcl:535
 #, tcl-format
 msgid "Resetting '%s' to '%s' will lose the following commits:"
 msgstr "Сброс '%s' в '%s' приведет к потере следующих сохраненных состояний: "
 
-#: lib/checkout_op.tcl:522
+#: lib/checkout_op.tcl:557
 msgid "Recovering lost commits may not be easy."
 msgstr "Восстановить потерянные сохраненные состояния будет сложно."
 
-#: lib/checkout_op.tcl:527
+#: lib/checkout_op.tcl:562
 #, tcl-format
 msgid "Reset '%s'?"
 msgstr "Сбросить '%s'?"
 
-#: lib/checkout_op.tcl:532 lib/merge.tcl:164 lib/tools_dlg.tcl:343
+#: lib/checkout_op.tcl:567 lib/merge.tcl:164 lib/tools_dlg.tcl:343
 msgid "Visualize"
 msgstr "Наглядно"
 
-#: lib/checkout_op.tcl:600
+#: lib/checkout_op.tcl:635
 #, tcl-format
 msgid ""
 "Failed to set current branch.\n"
@@ -1011,7 +1051,7 @@
 msgid "Git Gui"
 msgstr "Git Gui"
 
-#: lib/choose_repository.tcl:87 lib/choose_repository.tcl:382
+#: lib/choose_repository.tcl:87 lib/choose_repository.tcl:386
 msgid "Create New Repository"
 msgstr "Создать новый репозиторий"
 
@@ -1019,7 +1059,7 @@
 msgid "New..."
 msgstr "Новый..."
 
-#: lib/choose_repository.tcl:100 lib/choose_repository.tcl:465
+#: lib/choose_repository.tcl:100 lib/choose_repository.tcl:471
 msgid "Clone Existing Repository"
 msgstr "Склонировать существующий репозиторий"
 
@@ -1027,7 +1067,7 @@
 msgid "Clone..."
 msgstr "Склонировать..."
 
-#: lib/choose_repository.tcl:113 lib/choose_repository.tcl:983
+#: lib/choose_repository.tcl:113 lib/choose_repository.tcl:1016
 msgid "Open Existing Repository"
 msgstr "Выбрать существующий репозиторий"
 
@@ -1043,194 +1083,194 @@
 msgid "Open Recent Repository:"
 msgstr "Открыть последний репозиторий"
 
-#: lib/choose_repository.tcl:302 lib/choose_repository.tcl:309
-#: lib/choose_repository.tcl:316
+#: lib/choose_repository.tcl:306 lib/choose_repository.tcl:313
+#: lib/choose_repository.tcl:320
 #, tcl-format
 msgid "Failed to create repository %s:"
 msgstr "Не удалось создать репозиторий %s:"
 
-#: lib/choose_repository.tcl:387
+#: lib/choose_repository.tcl:391
 msgid "Directory:"
 msgstr "Каталог:"
 
-#: lib/choose_repository.tcl:417 lib/choose_repository.tcl:544
-#: lib/choose_repository.tcl:1017
+#: lib/choose_repository.tcl:423 lib/choose_repository.tcl:550
+#: lib/choose_repository.tcl:1052
 msgid "Git Repository"
 msgstr "Репозиторий"
 
-#: lib/choose_repository.tcl:442
+#: lib/choose_repository.tcl:448
 #, tcl-format
 msgid "Directory %s already exists."
 msgstr "Каталог '%s' уже существует."
 
-#: lib/choose_repository.tcl:446
+#: lib/choose_repository.tcl:452
 #, tcl-format
 msgid "File %s already exists."
 msgstr "Файл '%s' уже существует."
 
-#: lib/choose_repository.tcl:460
+#: lib/choose_repository.tcl:466
 msgid "Clone"
 msgstr "Склонировать"
 
-#: lib/choose_repository.tcl:473
+#: lib/choose_repository.tcl:479
 msgid "Source Location:"
 msgstr "Исходное положение:"
 
-#: lib/choose_repository.tcl:484
+#: lib/choose_repository.tcl:490
 msgid "Target Directory:"
 msgstr "Каталог назначения:"
 
-#: lib/choose_repository.tcl:496
+#: lib/choose_repository.tcl:502
 msgid "Clone Type:"
 msgstr "Тип клона:"
 
-#: lib/choose_repository.tcl:502
+#: lib/choose_repository.tcl:508
 msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
 msgstr "Стандартный (Быстрый, полуизбыточный, \"жесткие\" ссылки)"
 
-#: lib/choose_repository.tcl:508
+#: lib/choose_repository.tcl:514
 msgid "Full Copy (Slower, Redundant Backup)"
 msgstr "Полная копия (Медленный, создает резервную копию)"
 
-#: lib/choose_repository.tcl:514
+#: lib/choose_repository.tcl:520
 msgid "Shared (Fastest, Not Recommended, No Backup)"
 msgstr "Общий (Самый быстрый, не рекомендуется, без резервной копии)"
 
-#: lib/choose_repository.tcl:550 lib/choose_repository.tcl:597
-#: lib/choose_repository.tcl:743 lib/choose_repository.tcl:813
-#: lib/choose_repository.tcl:1023 lib/choose_repository.tcl:1031
+#: lib/choose_repository.tcl:556 lib/choose_repository.tcl:603
+#: lib/choose_repository.tcl:749 lib/choose_repository.tcl:819
+#: lib/choose_repository.tcl:1058 lib/choose_repository.tcl:1066
 #, tcl-format
 msgid "Not a Git repository: %s"
 msgstr "Каталог не является репозиторием: %s"
 
-#: lib/choose_repository.tcl:586
+#: lib/choose_repository.tcl:592
 msgid "Standard only available for local repository."
 msgstr "Стандартный клон возможен только для локального репозитория."
 
-#: lib/choose_repository.tcl:590
+#: lib/choose_repository.tcl:596
 msgid "Shared only available for local repository."
 msgstr "Общий клон возможен только для локального репозитория."
 
-#: lib/choose_repository.tcl:611
+#: lib/choose_repository.tcl:617
 #, tcl-format
 msgid "Location %s already exists."
 msgstr "Путь '%s' уже существует."
 
-#: lib/choose_repository.tcl:622
+#: lib/choose_repository.tcl:628
 msgid "Failed to configure origin"
 msgstr "Не могу сконфигурировать исходный репозиторий."
 
-#: lib/choose_repository.tcl:634
+#: lib/choose_repository.tcl:640
 msgid "Counting objects"
 msgstr "Считаю объекты"
 
-#: lib/choose_repository.tcl:635
+#: lib/choose_repository.tcl:641
 msgid "buckets"
 msgstr ""
 
-#: lib/choose_repository.tcl:659
+#: lib/choose_repository.tcl:665
 #, tcl-format
 msgid "Unable to copy objects/info/alternates: %s"
 msgstr "Не могу скопировать objects/info/alternates: %s"
 
-#: lib/choose_repository.tcl:695
+#: lib/choose_repository.tcl:701
 #, tcl-format
 msgid "Nothing to clone from %s."
 msgstr "Нечего клонировать с %s."
 
-#: lib/choose_repository.tcl:697 lib/choose_repository.tcl:911
-#: lib/choose_repository.tcl:923
+#: lib/choose_repository.tcl:703 lib/choose_repository.tcl:917
+#: lib/choose_repository.tcl:929
 msgid "The 'master' branch has not been initialized."
 msgstr "Не инициализирована ветвь 'master'."
 
-#: lib/choose_repository.tcl:710
+#: lib/choose_repository.tcl:716
 msgid "Hardlinks are unavailable.  Falling back to copying."
 msgstr "\"Жесткие ссылки\" недоступны. Будет использовано копирование."
 
-#: lib/choose_repository.tcl:722
+#: lib/choose_repository.tcl:728
 #, tcl-format
 msgid "Cloning from %s"
 msgstr "Клонирование %s"
 
-#: lib/choose_repository.tcl:753
+#: lib/choose_repository.tcl:759
 msgid "Copying objects"
 msgstr "Копирование objects"
 
-#: lib/choose_repository.tcl:754
+#: lib/choose_repository.tcl:760
 msgid "KiB"
 msgstr "КБ"
 
-#: lib/choose_repository.tcl:778
+#: lib/choose_repository.tcl:784
 #, tcl-format
 msgid "Unable to copy object: %s"
 msgstr "Не могу скопировать объект: %s"
 
-#: lib/choose_repository.tcl:788
+#: lib/choose_repository.tcl:794
 msgid "Linking objects"
 msgstr "Создание ссылок на objects"
 
-#: lib/choose_repository.tcl:789
+#: lib/choose_repository.tcl:795
 msgid "objects"
 msgstr "объекты"
 
-#: lib/choose_repository.tcl:797
+#: lib/choose_repository.tcl:803
 #, tcl-format
 msgid "Unable to hardlink object: %s"
 msgstr "Не могу \"жестко связать\" объект: %s"
 
-#: lib/choose_repository.tcl:852
+#: lib/choose_repository.tcl:858
 msgid "Cannot fetch branches and objects.  See console output for details."
 msgstr ""
 "Не могу получить ветви и объекты. Дополнительная информация на консоли."
 
-#: lib/choose_repository.tcl:863
+#: lib/choose_repository.tcl:869
 msgid "Cannot fetch tags.  See console output for details."
 msgstr "Не могу получить метки. Дополнительная информация на консоли."
 
-#: lib/choose_repository.tcl:887
+#: lib/choose_repository.tcl:893
 msgid "Cannot determine HEAD.  See console output for details."
 msgstr "Не могу определить HEAD. Дополнительная информация на консоли."
 
-#: lib/choose_repository.tcl:896
+#: lib/choose_repository.tcl:902
 #, tcl-format
 msgid "Unable to cleanup %s"
 msgstr "Не могу очистить %s"
 
-#: lib/choose_repository.tcl:902
+#: lib/choose_repository.tcl:908
 msgid "Clone failed."
 msgstr "Клонирование не удалось."
 
-#: lib/choose_repository.tcl:909
+#: lib/choose_repository.tcl:915
 msgid "No default branch obtained."
 msgstr "Не было получено ветви по умолчанию."
 
-#: lib/choose_repository.tcl:920
+#: lib/choose_repository.tcl:926
 #, tcl-format
 msgid "Cannot resolve %s as a commit."
 msgstr "Не могу распознать %s как состояние."
 
-#: lib/choose_repository.tcl:932
+#: lib/choose_repository.tcl:938
 msgid "Creating working directory"
 msgstr "Создаю рабочий каталог"
 
-#: lib/choose_repository.tcl:933 lib/index.tcl:65 lib/index.tcl:128
-#: lib/index.tcl:196
+#: lib/choose_repository.tcl:939 lib/index.tcl:67 lib/index.tcl:130
+#: lib/index.tcl:198
 msgid "files"
 msgstr "файлов"
 
-#: lib/choose_repository.tcl:962
+#: lib/choose_repository.tcl:968
 msgid "Initial file checkout failed."
 msgstr "Не удалось получить начальное состояние файлов репозитория."
 
-#: lib/choose_repository.tcl:978
+#: lib/choose_repository.tcl:1011
 msgid "Open"
 msgstr "Открыть"
 
-#: lib/choose_repository.tcl:988
+#: lib/choose_repository.tcl:1021
 msgid "Repository:"
 msgstr "Репозиторий:"
 
-#: lib/choose_repository.tcl:1037
+#: lib/choose_repository.tcl:1072
 #, tcl-format
 msgid "Failed to open repository %s:"
 msgstr "Не удалось открыть репозиторий %s:"
@@ -1297,8 +1337,8 @@
 msgstr ""
 "Невозможно исправить состояние во время операции слияния.\n"
 "\n"
-"Текущее слияние не завершено. Невозможно исправить предыдущее "
-"сохраненное состояние, не прерывая эту операцию.\n"
+"Текущее слияние не завершено. Невозможно исправить предыдущее сохраненное "
+"состояние, не прерывая эту операцию.\n"
 
 #: lib/commit.tcl:48
 msgid "Error loading commit data for amend:"
@@ -1312,7 +1352,12 @@
 msgid "Invalid GIT_COMMITTER_IDENT:"
 msgstr "Неверный GIT_COMMITTER_IDENT:"
 
-#: lib/commit.tcl:132
+#: lib/commit.tcl:129
+#, tcl-format
+msgid "warning: Tcl does not support encoding '%s'."
+msgstr "предупреждение: Tcl не поддерживает кодировку '%s'."
+
+#: lib/commit.tcl:149
 msgid ""
 "Last scanned state does not match repository state.\n"
 "\n"
@@ -1328,7 +1373,7 @@
 "\n"
 "Это будет сделано сейчас автоматически.\n"
 
-#: lib/commit.tcl:155
+#: lib/commit.tcl:172
 #, tcl-format
 msgid ""
 "Unmerged files cannot be committed.\n"
@@ -1336,12 +1381,12 @@
 "File %s has merge conflicts.  You must resolve them and stage the file "
 "before committing.\n"
 msgstr ""
-"Нельзя сохранить файлы с незавершённой операцей слияния.\n"
+"Нельзя сохранить файлы с незавершённой операцией слияния.\n"
 "\n"
 "Для файла %s возник конфликт слияния. Разрешите конфликт и добавьте к "
 "подготовленным файлам перед сохранением.\n"
 
-#: lib/commit.tcl:163
+#: lib/commit.tcl:180
 #, tcl-format
 msgid ""
 "Unknown file state %s detected.\n"
@@ -1352,7 +1397,7 @@
 "\n"
 "Файл %s не может быть сохранен данной программой.\n"
 
-#: lib/commit.tcl:171
+#: lib/commit.tcl:188
 msgid ""
 "No changes to commit.\n"
 "\n"
@@ -1362,7 +1407,7 @@
 "\n"
 "Подготовьте хотя бы один файл до создания сохраненного состояния.\n"
 
-#: lib/commit.tcl:186
+#: lib/commit.tcl:203
 msgid ""
 "Please supply a commit message.\n"
 "\n"
@@ -1380,45 +1425,40 @@
 "- вторая строка пустая\n"
 "- оставшиеся строки: опишите, что дают ваши изменения.\n"
 
-#: lib/commit.tcl:210
-#, tcl-format
-msgid "warning: Tcl does not support encoding '%s'."
-msgstr "предупреждение: Tcl не поддерживает кодировку '%s'."
-
-#: lib/commit.tcl:226
+#: lib/commit.tcl:234
 msgid "Calling pre-commit hook..."
 msgstr "Вызов программы поддержки репозитория pre-commit..."
 
-#: lib/commit.tcl:241
+#: lib/commit.tcl:249
 msgid "Commit declined by pre-commit hook."
 msgstr "Сохранение прервано программой поддержки репозитория pre-commit"
 
-#: lib/commit.tcl:264
+#: lib/commit.tcl:272
 msgid "Calling commit-msg hook..."
 msgstr "Вызов программы поддержки репозитория commit-msg..."
 
-#: lib/commit.tcl:279
+#: lib/commit.tcl:287
 msgid "Commit declined by commit-msg hook."
 msgstr "Сохранение прервано программой поддержки репозитория commit-msg"
 
-#: lib/commit.tcl:292
+#: lib/commit.tcl:300
 msgid "Committing changes..."
 msgstr "Сохранение изменений..."
 
-#: lib/commit.tcl:308
+#: lib/commit.tcl:316
 msgid "write-tree failed:"
 msgstr "Программа write-tree завершилась с ошибкой:"
 
-#: lib/commit.tcl:309 lib/commit.tcl:353 lib/commit.tcl:373
+#: lib/commit.tcl:317 lib/commit.tcl:361 lib/commit.tcl:382
 msgid "Commit failed."
 msgstr "Сохранить состояние не удалось."
 
-#: lib/commit.tcl:326
+#: lib/commit.tcl:334
 #, tcl-format
 msgid "Commit %s appears to be corrupt"
 msgstr "Состояние %s выглядит поврежденным"
 
-#: lib/commit.tcl:331
+#: lib/commit.tcl:339
 msgid ""
 "No changes to commit.\n"
 "\n"
@@ -1432,19 +1472,19 @@
 "\n"
 "Сейчас автоматически запустится перечитывание репозитория.\n"
 
-#: lib/commit.tcl:338
+#: lib/commit.tcl:346
 msgid "No changes to commit."
-msgstr "Отуствуют измения для сохранения."
+msgstr "Отсутствуют изменения для сохранения."
 
-#: lib/commit.tcl:352
+#: lib/commit.tcl:360
 msgid "commit-tree failed:"
 msgstr "Программа commit-tree завершилась с ошибкой:"
 
-#: lib/commit.tcl:372
+#: lib/commit.tcl:381
 msgid "update-ref failed:"
 msgstr "Программа update-ref завершилась с ошибкой:"
 
-#: lib/commit.tcl:460
+#: lib/commit.tcl:469
 #, tcl-format
 msgid "Created commit %s: %s"
 msgstr "Создано состояние %s: %s "
@@ -1497,20 +1537,19 @@
 msgid "Verifying the object database with fsck-objects"
 msgstr "Проверка базы объектов при помощи fsck"
 
-#: lib/database.tcl:108
+#: lib/database.tcl:107
 #, tcl-format
 msgid ""
 "This repository currently has approximately %i loose objects.\n"
 "\n"
 "To maintain optimal performance it is strongly recommended that you compress "
-"the database when more than %i loose objects exist.\n"
+"the database.\n"
 "\n"
 "Compress the database now?"
 msgstr ""
 "Этот репозиторий сейчас содержит примерно %i свободных объектов\n"
 "\n"
-"Для лучшей производительности рекомендуется сжать базу данных, когда есть "
-"более %i несвязанных объектов.\n"
+"Для лучшей производительности рекомендуется сжать базу данных.\n"
 "\n"
 "Сжать базу данных сейчас?"
 
@@ -1519,7 +1558,7 @@
 msgid "Invalid date from Git: %s"
 msgstr "Неправильная дата в репозитории: %s"
 
-#: lib/diff.tcl:59
+#: lib/diff.tcl:64
 #, tcl-format
 msgid ""
 "No differences detected.\n"
@@ -1534,19 +1573,19 @@
 msgstr ""
 "Изменений не обнаружено.\n"
 "\n"
-"в %s отутствуют изменения.\n"
+"в %s отсутствуют изменения.\n"
 "\n"
 "Дата изменения файла была обновлена другой программой, но содержимое файла "
 "осталось прежним.\n"
 "\n"
 "Сейчас будет запущено перечитывание репозитория, чтобы найти подобные файлы."
 
-#: lib/diff.tcl:99
+#: lib/diff.tcl:104
 #, tcl-format
 msgid "Loading diff of %s..."
 msgstr "Загрузка изменений в %s..."
 
-#: lib/diff.tcl:120
+#: lib/diff.tcl:125
 msgid ""
 "LOCAL: deleted\n"
 "REMOTE:\n"
@@ -1554,7 +1593,7 @@
 "ЛОКАЛЬНО: удалён\n"
 "ВНЕШНИЙ:\n"
 
-#: lib/diff.tcl:125
+#: lib/diff.tcl:130
 msgid ""
 "REMOTE: deleted\n"
 "LOCAL:\n"
@@ -1562,41 +1601,41 @@
 "ВНЕШНИЙ: удалён\n"
 "ЛОКАЛЬНО:\n"
 
-#: lib/diff.tcl:132
+#: lib/diff.tcl:137
 msgid "LOCAL:\n"
 msgstr "ЛОКАЛЬНО:\n"
 
-#: lib/diff.tcl:135
+#: lib/diff.tcl:140
 msgid "REMOTE:\n"
 msgstr "ВНЕШНИЙ:\n"
 
-#: lib/diff.tcl:197 lib/diff.tcl:296
+#: lib/diff.tcl:202 lib/diff.tcl:319
 #, tcl-format
 msgid "Unable to display %s"
 msgstr "Не могу показать %s"
 
-#: lib/diff.tcl:198
+#: lib/diff.tcl:203
 msgid "Error loading file:"
 msgstr "Ошибка загрузки файла:"
 
-#: lib/diff.tcl:205
+#: lib/diff.tcl:210
 msgid "Git Repository (subproject)"
 msgstr "Репозиторий Git (подпроект)"
 
-#: lib/diff.tcl:217
+#: lib/diff.tcl:222
 msgid "* Binary file (not showing content)."
 msgstr "* Двоичный файл (содержимое не показано)"
 
-#: lib/diff.tcl:222
+#: lib/diff.tcl:227
 #, tcl-format
 msgid ""
 "* Untracked file is %d bytes.\n"
 "* Showing only first %d bytes.\n"
 msgstr ""
-"* Размер неподготовленого файла %d байт.\n"
+"* Размер неподготовленного файла %d байт.\n"
 "* Показано первых %d байт.\n"
 
-#: lib/diff.tcl:228
+#: lib/diff.tcl:233
 #, tcl-format
 msgid ""
 "\n"
@@ -1604,22 +1643,22 @@
 "* To see the entire file, use an external editor.\n"
 msgstr ""
 "\n"
-"* Неподготовленый файл обрезан: %s.\n"
+"* Неподготовленный файл обрезан: %s.\n"
 "* Чтобы увидеть весь файл, используйте программу-редактор.\n"
 
-#: lib/diff.tcl:436
+#: lib/diff.tcl:482
 msgid "Failed to unstage selected hunk."
 msgstr "Не удалось исключить выбранную часть."
 
-#: lib/diff.tcl:443
+#: lib/diff.tcl:489
 msgid "Failed to stage selected hunk."
 msgstr "Не удалось подготовить к сохранению выбранную часть."
 
-#: lib/diff.tcl:509
+#: lib/diff.tcl:568
 msgid "Failed to unstage selected line."
 msgstr "Не удалось исключить выбранную строку."
 
-#: lib/diff.tcl:517
+#: lib/diff.tcl:576
 msgid "Failed to stage selected line."
 msgstr "Не удалось подготовить к сохранению выбранную строку."
 
@@ -1656,15 +1695,15 @@
 msgid "Index Error"
 msgstr "Ошибка в индексе"
 
-#: lib/index.tcl:21
+#: lib/index.tcl:17
 msgid ""
 "Updating the Git index failed.  A rescan will be automatically started to "
 "resynchronize git-gui."
 msgstr ""
-"Не удалось обновить индекс Git. Состояние репозитория будетперечитано "
+"Не удалось обновить индекс Git. Состояние репозитория будет перечитано "
 "автоматически."
 
-#: lib/index.tcl:27
+#: lib/index.tcl:28
 msgid "Continue"
 msgstr "Продолжить"
 
@@ -1672,45 +1711,45 @@
 msgid "Unlock Index"
 msgstr "Разблокировать индекс"
 
-#: lib/index.tcl:287
+#: lib/index.tcl:289
 #, tcl-format
 msgid "Unstaging %s from commit"
 msgstr "Удаление %s из подготовленного"
 
-#: lib/index.tcl:326
+#: lib/index.tcl:328
 msgid "Ready to commit."
 msgstr "Подготовлено для сохранения"
 
-#: lib/index.tcl:339
+#: lib/index.tcl:341
 #, tcl-format
 msgid "Adding %s"
 msgstr "Добавление %s..."
 
-#: lib/index.tcl:396
+#: lib/index.tcl:398
 #, tcl-format
 msgid "Revert changes in file %s?"
 msgstr "Отменить изменения в файле %s?"
 
-#: lib/index.tcl:398
+#: lib/index.tcl:400
 #, tcl-format
 msgid "Revert changes in these %i files?"
 msgstr "Отменить изменения в %i файле(-ах)?"
 
-#: lib/index.tcl:406
+#: lib/index.tcl:408
 msgid "Any unstaged changes will be permanently lost by the revert."
 msgstr ""
 "Любые изменения, не подготовленные к сохранению, будут потеряны при данной "
 "операции."
 
-#: lib/index.tcl:409
+#: lib/index.tcl:411
 msgid "Do Nothing"
 msgstr "Ничего не делать"
 
-#: lib/index.tcl:427
+#: lib/index.tcl:429
 msgid "Reverting selected files"
-msgstr "Удаление изменений в выбраных файлах"
+msgstr "Удаление изменений в выбранных файлах"
 
-#: lib/index.tcl:431
+#: lib/index.tcl:433
 #, tcl-format
 msgid "Reverting %s"
 msgstr "Отмена изменений в %s"
@@ -1723,8 +1762,7 @@
 msgstr ""
 "Невозможно выполнить слияние во время исправления.\n"
 "\n"
-"Завершите исправление данного состояния перед выполнением операции "
-"слияния.\n"
+"Завершите исправление данного состояния перед выполнением операции слияния.\n"
 
 #: lib/merge.tcl:27
 msgid ""
@@ -1773,7 +1811,7 @@
 "\n"
 "Файл %s изменен.\n"
 "\n"
-"Подготовьте и сохраните измения перед началом слияния. В случае "
+"Подготовьте и сохраните изменения перед началом слияния. В случае "
 "необходимости это позволит прервать операцию слияния.\n"
 
 #: lib/merge.tcl:107
@@ -1888,8 +1926,8 @@
 #, tcl-format
 msgid "File %s seems to have unresolved conflicts, still stage?"
 msgstr ""
-"Файл %s кажется содержит необработаные конфликты. "
-"Продолжить подготовку к сохранению?"
+"Файл %s, похоже, содержит необработанные конфликты. Продолжить подготовку к "
+"сохранению?"
 
 #: lib/mergetool.tcl:60
 #, tcl-format
@@ -2147,7 +2185,7 @@
 #: lib/remote_add.tcl:157
 #, tcl-format
 msgid "Do not know how to initialize repository at location '%s'."
-msgstr "Невозможно инициалировать репозиторий в '%s'."
+msgstr "Невозможно инициализировать репозиторий в '%s'."
 
 #: lib/remote_add.tcl:163 lib/transport.tcl:25 lib/transport.tcl:63
 #: lib/transport.tcl:81
@@ -2174,7 +2212,7 @@
 
 #: lib/remote_branch_delete.tcl:66 lib/transport.tcl:149
 msgid "Arbitrary Location:"
-msgstr "Указаное положение:"
+msgstr "Указанное положение:"
 
 #: lib/remote_branch_delete.tcl:84
 msgid "Branches"
@@ -2188,10 +2226,6 @@
 msgid "Merged Into:"
 msgstr "Слияние с:"
 
-#: lib/remote_branch_delete.tcl:119
-msgid "Always (Do not perform merge checks)"
-msgstr "Всегда (не выполнять проверку на слияние)"
-
 #: lib/remote_branch_delete.tcl:152
 msgid "A branch is required for 'Merged Into'."
 msgstr "Для опции 'Слияние с' требуется указать ветвь."
@@ -2213,33 +2247,23 @@
 "One or more of the merge tests failed because you have not fetched the "
 "necessary commits.  Try fetching from %s first."
 msgstr ""
-"Некоторые тесты на слияние не прошли, потому что Вы не "
-"получили необходимые состояния. Попытайтесь получить их из %s."
+"Некоторые тесты на слияние не прошли, потому что Вы не получили необходимые "
+"состояния. Попытайтесь получить их из %s."
 
 #: lib/remote_branch_delete.tcl:207
 msgid "Please select one or more branches to delete."
 msgstr "Укажите одну или несколько ветвей для удаления."
 
-#: lib/remote_branch_delete.tcl:216
-msgid ""
-"Recovering deleted branches is difficult.\n"
-"\n"
-"Delete the selected branches?"
-msgstr ""
-"Восстановить удаленные ветви сложно.\n"
-"\n"
-"Продолжить?"
-
 #: lib/remote_branch_delete.tcl:226
 #, tcl-format
 msgid "Deleting branches from %s"
 msgstr "Удаление ветвей из %s"
 
-#: lib/remote_branch_delete.tcl:286
+#: lib/remote_branch_delete.tcl:292
 msgid "No repository selected."
 msgstr "Не указан репозиторий."
 
-#: lib/remote_branch_delete.tcl:291
+#: lib/remote_branch_delete.tcl:297
 #, tcl-format
 msgid "Scanning %s..."
 msgstr "Перечитывание %s... "
@@ -2260,11 +2284,11 @@
 msgid "Case-Sensitive"
 msgstr "Игн. большие/маленькие"
 
-#: lib/shortcut.tcl:20 lib/shortcut.tcl:61
+#: lib/shortcut.tcl:21 lib/shortcut.tcl:62
 msgid "Cannot write shortcut:"
 msgstr "Невозможно записать ссылку:"
 
-#: lib/shortcut.tcl:136
+#: lib/shortcut.tcl:137
 msgid "Cannot write icon:"
 msgstr "Невозможно записать значок:"
 
@@ -2287,11 +2311,11 @@
 
 #: lib/spellcheck.tcl:73
 msgid "Spell checker silently failed on startup"
-msgstr "Программа проверки правописания не смогла запустится"
+msgstr "Программа проверки правописания не смогла запуститься"
 
 #: lib/spellcheck.tcl:80
 msgid "Unrecognized spell checker"
-msgstr "Нераспознаная программа проверки правописания"
+msgstr "Нераспознанная программа проверки правописания"
 
 #: lib/spellcheck.tcl:186
 msgid "No Suggestions"
@@ -2381,8 +2405,8 @@
 
 #: lib/tools.tcl:149
 #, tcl-format
-msgid "Tool completed succesfully: %s"
-msgstr "Программа %s успешно завершилась."
+msgid "Tool completed successfully: %s"
+msgstr "Программа %s завершилась успешно."
 
 #: lib/tools.tcl:151
 #, tcl-format
@@ -2407,7 +2431,7 @@
 
 #: lib/tools_dlg.tcl:48
 msgid "Use '/' separators to create a submenu tree:"
-msgstr "Испольуйте '/' для создания подменю"
+msgstr "Используйте '/' для создания подменю"
 
 #: lib/tools_dlg.tcl:61
 msgid "Command:"
@@ -2538,4 +2562,3 @@
 #: lib/transport.tcl:179
 msgid "Include tags"
 msgstr "Передать метки"
-
diff --git a/git-gui/po/sv.po b/git-gui/po/sv.po
index c1535f9..24cc4e3 100644
--- a/git-gui/po/sv.po
+++ b/git-gui/po/sv.po
@@ -8,41 +8,41 @@
 msgstr ""
 "Project-Id-Version: sv\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-12-08 08:31-0800\n"
-"PO-Revision-Date: 2008-12-10 09:49+0100\n"
+"POT-Creation-Date: 2010-09-12 21:11+0100\n"
+"PO-Revision-Date: 2010-09-12 21:12+0100\n"
 "Last-Translator: Peter Krefting <peter@softwolves.pp.se>\n"
 "Language-Team: Swedish <tp-sv@listor.tp-sv.se>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
+"Content-Transfer-Encoding: 8bit"
 
-#: git-gui.sh:41 git-gui.sh:737 git-gui.sh:751 git-gui.sh:764 git-gui.sh:847
-#: git-gui.sh:866
-msgid "git-gui: fatal error"
-msgstr "git-gui: ödesdigert fel"
-
-#: git-gui.sh:689
+#: git-gui.sh:781
 #, tcl-format
 msgid "Invalid font specified in %s:"
 msgstr "Ogiltigt teckensnitt angivet i %s:"
 
-#: git-gui.sh:723
+#: git-gui.sh:831
 msgid "Main Font"
 msgstr "Huvudteckensnitt"
 
-#: git-gui.sh:724
+#: git-gui.sh:832
 msgid "Diff/Console Font"
 msgstr "Diff/konsolteckensnitt"
 
-#: git-gui.sh:738
+#: git-gui.sh:845 git-gui.sh:859 git-gui.sh:872 git-gui.sh:955 git-gui.sh:974
+#: git-gui.sh:2964
+msgid "git-gui: fatal error"
+msgstr "git-gui: ödesdigert fel"
+
+#: git-gui.sh:846
 msgid "Cannot find git in PATH."
 msgstr "Hittar inte git i PATH."
 
-#: git-gui.sh:765
+#: git-gui.sh:873
 msgid "Cannot parse Git version string:"
 msgstr "Kan inte tolka versionssträng från Git:"
 
-#: git-gui.sh:783
+#: git-gui.sh:891
 #, tcl-format
 msgid ""
 "Git version cannot be determined.\n"
@@ -61,449 +61,478 @@
 "\n"
 "Anta att \"%s\" är version 1.5.0?\n"
 
-#: git-gui.sh:1062
+#: git-gui.sh:1180
 msgid "Git directory not found:"
 msgstr "Git-katalogen hittades inte:"
 
-#: git-gui.sh:1069
+#: git-gui.sh:1201
 msgid "Cannot move to top of working directory:"
 msgstr "Kan inte gå till början på arbetskatalogen:"
 
-#: git-gui.sh:1076
-msgid "Cannot use funny .git directory:"
-msgstr "Kan inte använda underlig .git-katalog:"
+#: git-gui.sh:1209
+msgid "Cannot use bare repository:"
+msgstr "Kan inte använda naket arkiv:"
 
-#: git-gui.sh:1081
+#: git-gui.sh:1217
 msgid "No working directory"
 msgstr "Ingen arbetskatalog"
 
-#: git-gui.sh:1247 lib/checkout_op.tcl:305
+#: git-gui.sh:1389 lib/checkout_op.tcl:306
 msgid "Refreshing file status..."
 msgstr "Uppdaterar filstatus..."
 
-#: git-gui.sh:1303
+#: git-gui.sh:1445
 msgid "Scanning for modified files ..."
 msgstr "Söker efter ändrade filer..."
 
-#: git-gui.sh:1367
+#: git-gui.sh:1509
 msgid "Calling prepare-commit-msg hook..."
 msgstr ""
 "Anropar kroken för förberedelse av incheckningsmeddelande (prepare-commit-"
 "msg)..."
 
-#: git-gui.sh:1384
+#: git-gui.sh:1526
 msgid "Commit declined by prepare-commit-msg hook."
 msgstr ""
 "Incheckningen avvisades av kroken för förberedelse av incheckningsmeddelande "
 "(prepare-commit-msg)."
 
-#: git-gui.sh:1542 lib/browser.tcl:246
+#: git-gui.sh:1684 lib/browser.tcl:246
 msgid "Ready."
 msgstr "Klar."
 
-#: git-gui.sh:1819
+#: git-gui.sh:1842
+#, tcl-format
+msgid "Displaying only %s of %s files."
+msgstr "Visar endast %s av %s filer."
+
+#: git-gui.sh:1968
 msgid "Unmodified"
 msgstr "Oförändrade"
 
-#: git-gui.sh:1821
+#: git-gui.sh:1970
 msgid "Modified, not staged"
 msgstr "Förändrade, ej köade"
 
-#: git-gui.sh:1822 git-gui.sh:1830
+#: git-gui.sh:1971 git-gui.sh:1979
 msgid "Staged for commit"
 msgstr "Köade för incheckning"
 
-#: git-gui.sh:1823 git-gui.sh:1831
+#: git-gui.sh:1972 git-gui.sh:1980
 msgid "Portions staged for commit"
 msgstr "Delar köade för incheckning"
 
-#: git-gui.sh:1824 git-gui.sh:1832
+#: git-gui.sh:1973 git-gui.sh:1981
 msgid "Staged for commit, missing"
 msgstr "Köade för incheckning, saknade"
 
-#: git-gui.sh:1826
+#: git-gui.sh:1975
 msgid "File type changed, not staged"
 msgstr "Filtyp ändrad, ej köade"
 
-#: git-gui.sh:1827
+#: git-gui.sh:1976
 msgid "File type changed, staged"
 msgstr "Filtyp ändrad, köade"
 
-#: git-gui.sh:1829
+#: git-gui.sh:1978
 msgid "Untracked, not staged"
 msgstr "Ej spårade, ej köade"
 
-#: git-gui.sh:1834
+#: git-gui.sh:1983
 msgid "Missing"
 msgstr "Saknade"
 
-#: git-gui.sh:1835
+#: git-gui.sh:1984
 msgid "Staged for removal"
 msgstr "Köade för borttagning"
 
-#: git-gui.sh:1836
+#: git-gui.sh:1985
 msgid "Staged for removal, still present"
 msgstr "Köade för borttagning, fortfarande närvarande"
 
-#: git-gui.sh:1838 git-gui.sh:1839 git-gui.sh:1840 git-gui.sh:1841
-#: git-gui.sh:1842 git-gui.sh:1843
+#: git-gui.sh:1987 git-gui.sh:1988 git-gui.sh:1989 git-gui.sh:1990
+#: git-gui.sh:1991 git-gui.sh:1992
 msgid "Requires merge resolution"
 msgstr "Kräver konflikthantering efter sammanslagning"
 
-#: git-gui.sh:1878
+#: git-gui.sh:2027
 msgid "Starting gitk... please wait..."
 msgstr "Startar gitk... vänta..."
 
-#: git-gui.sh:1887
+#: git-gui.sh:2039
 msgid "Couldn't find gitk in PATH"
-msgstr "Hittar inte gitk i PATH."
+msgstr "Hittade inte gitk i PATH."
 
-#: git-gui.sh:2280 lib/choose_repository.tcl:36
+#: git-gui.sh:2098
+msgid "Couldn't find git gui in PATH"
+msgstr "Hittade inte git gui i PATH."
+
+#: git-gui.sh:2515 lib/choose_repository.tcl:36
 msgid "Repository"
 msgstr "Arkiv"
 
-#: git-gui.sh:2281
+#: git-gui.sh:2516
 msgid "Edit"
 msgstr "Redigera"
 
-#: git-gui.sh:2283 lib/choose_rev.tcl:561
+#: git-gui.sh:2518 lib/choose_rev.tcl:566
 msgid "Branch"
 msgstr "Gren"
 
-#: git-gui.sh:2286 lib/choose_rev.tcl:548
+#: git-gui.sh:2521 lib/choose_rev.tcl:553
 msgid "Commit@@noun"
 msgstr "Incheckning"
 
-#: git-gui.sh:2289 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
+#: git-gui.sh:2524 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
 msgid "Merge"
 msgstr "Slå ihop"
 
-#: git-gui.sh:2290 lib/choose_rev.tcl:557
+#: git-gui.sh:2525 lib/choose_rev.tcl:562
 msgid "Remote"
 msgstr "Fjärrarkiv"
 
-#: git-gui.sh:2293
+#: git-gui.sh:2528
 msgid "Tools"
 msgstr "Verktyg"
 
-#: git-gui.sh:2302
+#: git-gui.sh:2537
 msgid "Explore Working Copy"
 msgstr "Utforska arbetskopia"
 
-#: git-gui.sh:2307
+#: git-gui.sh:2543
 msgid "Browse Current Branch's Files"
 msgstr "Bläddra i grenens filer"
 
-#: git-gui.sh:2311
+#: git-gui.sh:2547
 msgid "Browse Branch Files..."
 msgstr "Bläddra filer på gren..."
 
-#: git-gui.sh:2316
+#: git-gui.sh:2552
 msgid "Visualize Current Branch's History"
 msgstr "Visualisera grenens historik"
 
-#: git-gui.sh:2320
+#: git-gui.sh:2556
 msgid "Visualize All Branch History"
 msgstr "Visualisera alla grenars historik"
 
-#: git-gui.sh:2327
+#: git-gui.sh:2563
 #, tcl-format
 msgid "Browse %s's Files"
 msgstr "Bläddra i filer för %s"
 
-#: git-gui.sh:2329
+#: git-gui.sh:2565
 #, tcl-format
 msgid "Visualize %s's History"
 msgstr "Visualisera historik för %s"
 
-#: git-gui.sh:2334 lib/database.tcl:27 lib/database.tcl:67
+#: git-gui.sh:2570 lib/database.tcl:40 lib/database.tcl:66
 msgid "Database Statistics"
 msgstr "Databasstatistik"
 
-#: git-gui.sh:2337 lib/database.tcl:34
+#: git-gui.sh:2573 lib/database.tcl:33
 msgid "Compress Database"
 msgstr "Komprimera databas"
 
-#: git-gui.sh:2340
+#: git-gui.sh:2576
 msgid "Verify Database"
 msgstr "Verifiera databas"
 
-#: git-gui.sh:2347 git-gui.sh:2351 git-gui.sh:2355 lib/shortcut.tcl:7
-#: lib/shortcut.tcl:39 lib/shortcut.tcl:71
+#: git-gui.sh:2583 git-gui.sh:2587 git-gui.sh:2591 lib/shortcut.tcl:8
+#: lib/shortcut.tcl:40 lib/shortcut.tcl:72
 msgid "Create Desktop Icon"
 msgstr "Skapa skrivbordsikon"
 
-#: git-gui.sh:2363 lib/choose_repository.tcl:183 lib/choose_repository.tcl:191
+#: git-gui.sh:2599 lib/choose_repository.tcl:188 lib/choose_repository.tcl:196
 msgid "Quit"
 msgstr "Avsluta"
 
-#: git-gui.sh:2371
+#: git-gui.sh:2607
 msgid "Undo"
 msgstr "Ångra"
 
-#: git-gui.sh:2374
+#: git-gui.sh:2610
 msgid "Redo"
 msgstr "Gör om"
 
-#: git-gui.sh:2378 git-gui.sh:2937
+#: git-gui.sh:2614 git-gui.sh:3190
 msgid "Cut"
 msgstr "Klipp ut"
 
-#: git-gui.sh:2381 git-gui.sh:2940 git-gui.sh:3014 git-gui.sh:3096
+#: git-gui.sh:2617 git-gui.sh:3193 git-gui.sh:3267 git-gui.sh:3340
 #: lib/console.tcl:69
 msgid "Copy"
 msgstr "Kopiera"
 
-#: git-gui.sh:2384 git-gui.sh:2943
+#: git-gui.sh:2620 git-gui.sh:3196
 msgid "Paste"
 msgstr "Klistra in"
 
-#: git-gui.sh:2387 git-gui.sh:2946 lib/branch_delete.tcl:26
-#: lib/remote_branch_delete.tcl:38
+#: git-gui.sh:2623 git-gui.sh:3199 lib/branch_delete.tcl:28
+#: lib/remote_branch_delete.tcl:39
 msgid "Delete"
 msgstr "Ta bort"
 
-#: git-gui.sh:2391 git-gui.sh:2950 git-gui.sh:3100 lib/console.tcl:71
+#: git-gui.sh:2627 git-gui.sh:3203 git-gui.sh:3344 lib/console.tcl:71
 msgid "Select All"
 msgstr "Markera alla"
 
-#: git-gui.sh:2400
+#: git-gui.sh:2636
 msgid "Create..."
 msgstr "Skapa..."
 
-#: git-gui.sh:2406
+#: git-gui.sh:2642
 msgid "Checkout..."
 msgstr "Checka ut..."
 
-#: git-gui.sh:2412
+#: git-gui.sh:2648
 msgid "Rename..."
 msgstr "Byt namn..."
 
-#: git-gui.sh:2417
+#: git-gui.sh:2653
 msgid "Delete..."
 msgstr "Ta bort..."
 
-#: git-gui.sh:2422
+#: git-gui.sh:2658
 msgid "Reset..."
 msgstr "Återställ..."
 
-#: git-gui.sh:2432
+#: git-gui.sh:2668
 msgid "Done"
 msgstr "Färdig"
 
-#: git-gui.sh:2434
+#: git-gui.sh:2670
 msgid "Commit@@verb"
 msgstr "Checka in"
 
-#: git-gui.sh:2443 git-gui.sh:2878
+#: git-gui.sh:2679 git-gui.sh:3131
 msgid "New Commit"
 msgstr "Ny incheckning"
 
-#: git-gui.sh:2451 git-gui.sh:2885
+#: git-gui.sh:2687 git-gui.sh:3138
 msgid "Amend Last Commit"
 msgstr "Lägg till föregående incheckning"
 
-#: git-gui.sh:2461 git-gui.sh:2839 lib/remote_branch_delete.tcl:99
+#: git-gui.sh:2697 git-gui.sh:3092 lib/remote_branch_delete.tcl:101
 msgid "Rescan"
 msgstr "Sök på nytt"
 
-#: git-gui.sh:2467
+#: git-gui.sh:2703
 msgid "Stage To Commit"
 msgstr "Köa för incheckning"
 
-#: git-gui.sh:2473
+#: git-gui.sh:2709
 msgid "Stage Changed Files To Commit"
 msgstr "Köa ändrade filer för incheckning"
 
-#: git-gui.sh:2479
+#: git-gui.sh:2715
 msgid "Unstage From Commit"
 msgstr "Ta bort från incheckningskö"
 
-#: git-gui.sh:2484 lib/index.tcl:410
+#: git-gui.sh:2721 lib/index.tcl:415
 msgid "Revert Changes"
 msgstr "Återställ ändringar"
 
-#: git-gui.sh:2491 git-gui.sh:3083
+#: git-gui.sh:2729 git-gui.sh:3391 git-gui.sh:3422
 msgid "Show Less Context"
 msgstr "Visa mindre sammanhang"
 
-#: git-gui.sh:2495 git-gui.sh:3087
+#: git-gui.sh:2733 git-gui.sh:3395 git-gui.sh:3426
 msgid "Show More Context"
 msgstr "Visa mer sammanhang"
 
-#: git-gui.sh:2502 git-gui.sh:2852 git-gui.sh:2961
+#: git-gui.sh:2740 git-gui.sh:3105 git-gui.sh:3214
 msgid "Sign Off"
 msgstr "Skriv under"
 
-#: git-gui.sh:2518
+#: git-gui.sh:2756
 msgid "Local Merge..."
 msgstr "Lokal sammanslagning..."
 
-#: git-gui.sh:2523
+#: git-gui.sh:2761
 msgid "Abort Merge..."
 msgstr "Avbryt sammanslagning..."
 
-#: git-gui.sh:2535 git-gui.sh:2575
+#: git-gui.sh:2773 git-gui.sh:2801
 msgid "Add..."
 msgstr "Lägg till..."
 
-#: git-gui.sh:2539
+#: git-gui.sh:2777
 msgid "Push..."
 msgstr "Sänd..."
 
-#: git-gui.sh:2543
+#: git-gui.sh:2781
 msgid "Delete Branch..."
 msgstr "Ta bort gren..."
 
-#: git-gui.sh:2553 git-gui.sh:2589 lib/about.tcl:14
+#: git-gui.sh:2791 git-gui.sh:3373
+msgid "Options..."
+msgstr "Alternativ..."
+
+#: git-gui.sh:2802
+msgid "Remove..."
+msgstr "Ta bort..."
+
+#: git-gui.sh:2811 lib/choose_repository.tcl:50
+msgid "Help"
+msgstr "Hjälp"
+
+#: git-gui.sh:2815 git-gui.sh:2819 lib/about.tcl:14
 #: lib/choose_repository.tcl:44 lib/choose_repository.tcl:53
 #, tcl-format
 msgid "About %s"
 msgstr "Om %s"
 
-#: git-gui.sh:2557
-msgid "Preferences..."
-msgstr "Inställningar..."
-
-#: git-gui.sh:2565 git-gui.sh:3129
-msgid "Options..."
-msgstr "Alternativ..."
-
-#: git-gui.sh:2576
-msgid "Remove..."
-msgstr "Ta bort..."
-
-#: git-gui.sh:2585 lib/choose_repository.tcl:50
-msgid "Help"
-msgstr "Hjälp"
-
-#: git-gui.sh:2611
+#: git-gui.sh:2843
 msgid "Online Documentation"
 msgstr "Webbdokumentation"
 
-#: git-gui.sh:2614 lib/choose_repository.tcl:47 lib/choose_repository.tcl:56
+#: git-gui.sh:2846 lib/choose_repository.tcl:47 lib/choose_repository.tcl:56
 msgid "Show SSH Key"
 msgstr "Visa SSH-nyckel"
 
-#: git-gui.sh:2721
+#: git-gui.sh:2965
 #, tcl-format
 msgid "fatal: cannot stat path %s: No such file or directory"
 msgstr ""
 "ödesdigert: kunde inte ta status på sökvägen %s: Fil eller katalog saknas"
 
-#: git-gui.sh:2754
+#: git-gui.sh:2997
 msgid "Current Branch:"
 msgstr "Aktuell gren:"
 
-#: git-gui.sh:2775
+#: git-gui.sh:3023
 msgid "Staged Changes (Will Commit)"
 msgstr "Köade ändringar (kommer att checkas in)"
 
-#: git-gui.sh:2795
+#: git-gui.sh:3043
 msgid "Unstaged Changes"
 msgstr "Oköade ändringar"
 
-#: git-gui.sh:2845
+#: git-gui.sh:3098
 msgid "Stage Changed"
 msgstr "Köa ändrade"
 
-#: git-gui.sh:2864 lib/transport.tcl:104 lib/transport.tcl:193
+#: git-gui.sh:3117 lib/transport.tcl:107 lib/transport.tcl:196
 msgid "Push"
 msgstr "Sänd"
 
-#: git-gui.sh:2899
+#: git-gui.sh:3152
 msgid "Initial Commit Message:"
 msgstr "Inledande incheckningsmeddelande:"
 
-#: git-gui.sh:2900
+#: git-gui.sh:3153
 msgid "Amended Commit Message:"
 msgstr "Utökat incheckningsmeddelande:"
 
-#: git-gui.sh:2901
+#: git-gui.sh:3154
 msgid "Amended Initial Commit Message:"
 msgstr "Utökat inledande incheckningsmeddelande:"
 
-#: git-gui.sh:2902
+#: git-gui.sh:3155
 msgid "Amended Merge Commit Message:"
 msgstr "Utökat incheckningsmeddelande för sammanslagning:"
 
-#: git-gui.sh:2903
+#: git-gui.sh:3156
 msgid "Merge Commit Message:"
 msgstr "Incheckningsmeddelande för sammanslagning:"
 
-#: git-gui.sh:2904
+#: git-gui.sh:3157
 msgid "Commit Message:"
 msgstr "Incheckningsmeddelande:"
 
-#: git-gui.sh:2953 git-gui.sh:3104 lib/console.tcl:73
+#: git-gui.sh:3206 git-gui.sh:3348 lib/console.tcl:73
 msgid "Copy All"
 msgstr "Kopiera alla"
 
-#: git-gui.sh:2977 lib/blame.tcl:104
+#: git-gui.sh:3230 lib/blame.tcl:104
 msgid "File:"
 msgstr "Fil:"
 
-#: git-gui.sh:3092
+#: git-gui.sh:3336
 msgid "Refresh"
 msgstr "Uppdatera"
 
-#: git-gui.sh:3113
+#: git-gui.sh:3357
 msgid "Decrease Font Size"
 msgstr "Minska teckensnittsstorlek"
 
-#: git-gui.sh:3117
+#: git-gui.sh:3361
 msgid "Increase Font Size"
 msgstr "Öka teckensnittsstorlek"
 
-#: git-gui.sh:3125 lib/blame.tcl:281
+#: git-gui.sh:3369 lib/blame.tcl:281
 msgid "Encoding"
 msgstr "Teckenkodning"
 
-#: git-gui.sh:3136
+#: git-gui.sh:3380
 msgid "Apply/Reverse Hunk"
 msgstr "Använd/återställ del"
 
-#: git-gui.sh:3141
+#: git-gui.sh:3385
 msgid "Apply/Reverse Line"
 msgstr "Använd/återställ rad"
 
-#: git-gui.sh:3151
+#: git-gui.sh:3404
 msgid "Run Merge Tool"
 msgstr "Starta verktyg för sammanslagning"
 
-#: git-gui.sh:3156
+#: git-gui.sh:3409
 msgid "Use Remote Version"
 msgstr "Använd versionen från fjärrarkivet"
 
-#: git-gui.sh:3160
+#: git-gui.sh:3413
 msgid "Use Local Version"
 msgstr "Använd lokala versionen"
 
-#: git-gui.sh:3164
+#: git-gui.sh:3417
 msgid "Revert To Base"
 msgstr "Återställ till basversionen"
 
-#: git-gui.sh:3183
+#: git-gui.sh:3435
+msgid "Visualize These Changes In The Submodule"
+msgstr "Visualisera ändringarna i undermodulen"
+
+#: git-gui.sh:3439
+msgid "Visualize Current Branch History In The Submodule"
+msgstr "Visualisera grenens historik i undermodulen"
+
+#: git-gui.sh:3443
+msgid "Visualize All Branch History In The Submodule"
+msgstr "Visualisera alla grenars historik i undermodulen"
+
+#: git-gui.sh:3448
+msgid "Start git gui In The Submodule"
+msgstr "Starta git gui i undermodulen"
+
+#: git-gui.sh:3483
 msgid "Unstage Hunk From Commit"
 msgstr "Ta bort del ur incheckningskö"
 
-#: git-gui.sh:3184
+#: git-gui.sh:3485
+msgid "Unstage Lines From Commit"
+msgstr "Ta bort rader ur incheckningskö"
+
+#: git-gui.sh:3487
 msgid "Unstage Line From Commit"
 msgstr "Ta bort rad ur incheckningskö"
 
-#: git-gui.sh:3186
+#: git-gui.sh:3490
 msgid "Stage Hunk For Commit"
 msgstr "Ställ del i incheckningskö"
 
-#: git-gui.sh:3187
+#: git-gui.sh:3492
+msgid "Stage Lines For Commit"
+msgstr "Ställ rader i incheckningskö"
+
+#: git-gui.sh:3494
 msgid "Stage Line For Commit"
 msgstr "Ställ rad i incheckningskö"
 
-#: git-gui.sh:3210
+#: git-gui.sh:3519
 msgid "Initializing..."
 msgstr "Initierar..."
 
-#: git-gui.sh:3315
+#: git-gui.sh:3658
 #, tcl-format
 msgid ""
 "Possible environment issues exist.\n"
@@ -520,7 +549,7 @@
 "av %s:\n"
 "\n"
 
-#: git-gui.sh:3345
+#: git-gui.sh:3687
 msgid ""
 "\n"
 "This is due to a known issue with the\n"
@@ -530,7 +559,7 @@
 "Detta beror på ett känt problem med\n"
 "Tcl-binären som följer med Cygwin."
 
-#: git-gui.sh:3350
+#: git-gui.sh:3692
 #, tcl-format
 msgid ""
 "\n"
@@ -584,132 +613,132 @@
 msgid "Reading %s..."
 msgstr "Läser %s..."
 
-#: lib/blame.tcl:557
+#: lib/blame.tcl:581
 msgid "Loading copy/move tracking annotations..."
 msgstr "Läser annoteringar för kopiering/flyttning..."
 
-#: lib/blame.tcl:577
+#: lib/blame.tcl:601
 msgid "lines annotated"
 msgstr "rader annoterade"
 
-#: lib/blame.tcl:769
+#: lib/blame.tcl:793
 msgid "Loading original location annotations..."
 msgstr "Läser in annotering av originalplacering..."
 
-#: lib/blame.tcl:772
+#: lib/blame.tcl:796
 msgid "Annotation complete."
 msgstr "Annotering fullbordad."
 
-#: lib/blame.tcl:802
+#: lib/blame.tcl:826
 msgid "Busy"
 msgstr "Upptagen"
 
-#: lib/blame.tcl:803
+#: lib/blame.tcl:827
 msgid "Annotation process is already running."
 msgstr "Annoteringsprocess körs redan."
 
-#: lib/blame.tcl:842
+#: lib/blame.tcl:866
 msgid "Running thorough copy detection..."
 msgstr "Kör grundlig kopieringsigenkänning..."
 
-#: lib/blame.tcl:910
+#: lib/blame.tcl:934
 msgid "Loading annotation..."
 msgstr "Läser in annotering..."
 
-#: lib/blame.tcl:963
+#: lib/blame.tcl:987
 msgid "Author:"
 msgstr "Författare:"
 
-#: lib/blame.tcl:967
+#: lib/blame.tcl:991
 msgid "Committer:"
 msgstr "Incheckare:"
 
-#: lib/blame.tcl:972
+#: lib/blame.tcl:996
 msgid "Original File:"
 msgstr "Ursprunglig fil:"
 
-#: lib/blame.tcl:1020
+#: lib/blame.tcl:1044
 msgid "Cannot find HEAD commit:"
 msgstr "Hittar inte incheckning för HEAD:"
 
-#: lib/blame.tcl:1075
+#: lib/blame.tcl:1099
 msgid "Cannot find parent commit:"
 msgstr "Hittar inte föräldraincheckning:"
 
-#: lib/blame.tcl:1090
+#: lib/blame.tcl:1114
 msgid "Unable to display parent"
 msgstr "Kan inte visa förälder"
 
-#: lib/blame.tcl:1091 lib/diff.tcl:297
+#: lib/blame.tcl:1115 lib/diff.tcl:323
 msgid "Error loading diff:"
 msgstr "Fel vid inläsning av differens:"
 
-#: lib/blame.tcl:1231
+#: lib/blame.tcl:1255
 msgid "Originally By:"
 msgstr "Ursprungligen av:"
 
-#: lib/blame.tcl:1237
+#: lib/blame.tcl:1261
 msgid "In File:"
 msgstr "I filen:"
 
-#: lib/blame.tcl:1242
+#: lib/blame.tcl:1266
 msgid "Copied Or Moved Here By:"
 msgstr "Kopierad eller flyttad hit av:"
 
-#: lib/branch_checkout.tcl:14 lib/branch_checkout.tcl:19
+#: lib/branch_checkout.tcl:16 lib/branch_checkout.tcl:21
 msgid "Checkout Branch"
 msgstr "Checka ut gren"
 
-#: lib/branch_checkout.tcl:23
+#: lib/branch_checkout.tcl:26
 msgid "Checkout"
 msgstr "Checka ut"
 
-#: lib/branch_checkout.tcl:27 lib/branch_create.tcl:35
-#: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:282
-#: lib/checkout_op.tcl:544 lib/choose_font.tcl:43 lib/merge.tcl:172
-#: lib/option.tcl:125 lib/remote_add.tcl:32 lib/remote_branch_delete.tcl:42
-#: lib/tools_dlg.tcl:40 lib/tools_dlg.tcl:204 lib/tools_dlg.tcl:352
-#: lib/transport.tcl:108
+#: lib/branch_checkout.tcl:30 lib/branch_create.tcl:37
+#: lib/branch_delete.tcl:34 lib/branch_rename.tcl:32 lib/browser.tcl:286
+#: lib/checkout_op.tcl:579 lib/choose_font.tcl:45 lib/merge.tcl:172
+#: lib/option.tcl:127 lib/remote_add.tcl:34 lib/remote_branch_delete.tcl:43
+#: lib/tools_dlg.tcl:41 lib/tools_dlg.tcl:202 lib/tools_dlg.tcl:345
+#: lib/transport.tcl:111
 msgid "Cancel"
 msgstr "Avbryt"
 
-#: lib/branch_checkout.tcl:32 lib/browser.tcl:287 lib/tools_dlg.tcl:328
+#: lib/branch_checkout.tcl:35 lib/browser.tcl:291 lib/tools_dlg.tcl:321
 msgid "Revision"
 msgstr "Revision"
 
-#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:280
+#: lib/branch_checkout.tcl:39 lib/branch_create.tcl:69 lib/option.tcl:287
 msgid "Options"
 msgstr "Alternativ"
 
-#: lib/branch_checkout.tcl:39 lib/branch_create.tcl:92
+#: lib/branch_checkout.tcl:42 lib/branch_create.tcl:92
 msgid "Fetch Tracking Branch"
 msgstr "Hämta spårande gren"
 
-#: lib/branch_checkout.tcl:44
+#: lib/branch_checkout.tcl:47
 msgid "Detach From Local Branch"
 msgstr "Koppla bort från lokal gren"
 
-#: lib/branch_create.tcl:22
+#: lib/branch_create.tcl:23
 msgid "Create Branch"
 msgstr "Skapa gren"
 
-#: lib/branch_create.tcl:27
+#: lib/branch_create.tcl:28
 msgid "Create New Branch"
 msgstr "Skapa ny gren"
 
-#: lib/branch_create.tcl:31 lib/choose_repository.tcl:377
+#: lib/branch_create.tcl:33 lib/choose_repository.tcl:389
 msgid "Create"
 msgstr "Skapa"
 
-#: lib/branch_create.tcl:40
+#: lib/branch_create.tcl:42
 msgid "Branch Name"
 msgstr "Namn på gren"
 
-#: lib/branch_create.tcl:43 lib/remote_add.tcl:39 lib/tools_dlg.tcl:50
+#: lib/branch_create.tcl:44 lib/remote_add.tcl:41 lib/tools_dlg.tcl:51
 msgid "Name:"
 msgstr "Namn:"
 
-#: lib/branch_create.tcl:58
+#: lib/branch_create.tcl:57
 msgid "Match Tracking Branch Name"
 msgstr "Använd namn på spårad gren"
 
@@ -729,7 +758,7 @@
 msgid "Fast Forward Only"
 msgstr "Endast snabbspolning"
 
-#: lib/branch_create.tcl:85 lib/checkout_op.tcl:536
+#: lib/branch_create.tcl:85 lib/checkout_op.tcl:571
 msgid "Reset"
 msgstr "Återställ"
 
@@ -737,49 +766,59 @@
 msgid "Checkout After Creation"
 msgstr "Checka ut när skapad"
 
-#: lib/branch_create.tcl:131
+#: lib/branch_create.tcl:132
 msgid "Please select a tracking branch."
 msgstr "Välj en gren att spåra."
 
-#: lib/branch_create.tcl:140
+#: lib/branch_create.tcl:141
 #, tcl-format
 msgid "Tracking branch %s is not a branch in the remote repository."
 msgstr "Den spårade grenen %s är inte en gren i fjärrarkivet."
 
-#: lib/branch_create.tcl:153 lib/branch_rename.tcl:86
+#: lib/branch_create.tcl:154 lib/branch_rename.tcl:92
 msgid "Please supply a branch name."
 msgstr "Ange ett namn för grenen."
 
-#: lib/branch_create.tcl:164 lib/branch_rename.tcl:106
+#: lib/branch_create.tcl:165 lib/branch_rename.tcl:112
 #, tcl-format
 msgid "'%s' is not an acceptable branch name."
 msgstr "\"%s\" kan inte användas som namn på grenen."
 
-#: lib/branch_delete.tcl:15
+#: lib/branch_delete.tcl:16
 msgid "Delete Branch"
 msgstr "Ta bort gren"
 
-#: lib/branch_delete.tcl:20
+#: lib/branch_delete.tcl:21
 msgid "Delete Local Branch"
 msgstr "Ta bort lokal gren"
 
-#: lib/branch_delete.tcl:37
+#: lib/branch_delete.tcl:39
 msgid "Local Branches"
 msgstr "Lokala grenar"
 
-#: lib/branch_delete.tcl:52
+#: lib/branch_delete.tcl:51
 msgid "Delete Only If Merged Into"
 msgstr "Ta bara bort om sammanslagen med"
 
-#: lib/branch_delete.tcl:54
-msgid "Always (Do not perform merge test.)"
-msgstr "Alltid (utför inte sammanslagningstest)."
+#: lib/branch_delete.tcl:53 lib/remote_branch_delete.tcl:120
+msgid "Always (Do not perform merge checks)"
+msgstr "Alltid (utför inte sammanslagningstest)"
 
 #: lib/branch_delete.tcl:103
 #, tcl-format
 msgid "The following branches are not completely merged into %s:"
 msgstr "Följande grenar är inte till fullo sammanslagna med %s:"
 
+#: lib/branch_delete.tcl:115 lib/remote_branch_delete.tcl:218
+msgid ""
+"Recovering deleted branches is difficult.\n"
+"\n"
+"Delete the selected branches?"
+msgstr ""
+"Det kan vara svårt att återställa borttagna grenar.\n"
+"\n"
+"Ta bort de valda grenarna?"
+
 #: lib/branch_delete.tcl:141
 #, tcl-format
 msgid ""
@@ -789,32 +828,32 @@
 "Kunde inte ta bort grenar:\n"
 "%s"
 
-#: lib/branch_rename.tcl:14 lib/branch_rename.tcl:22
+#: lib/branch_rename.tcl:15 lib/branch_rename.tcl:23
 msgid "Rename Branch"
 msgstr "Byt namn på gren"
 
-#: lib/branch_rename.tcl:26
+#: lib/branch_rename.tcl:28
 msgid "Rename"
 msgstr "Byt namn"
 
-#: lib/branch_rename.tcl:36
+#: lib/branch_rename.tcl:38
 msgid "Branch:"
 msgstr "Gren:"
 
-#: lib/branch_rename.tcl:39
+#: lib/branch_rename.tcl:46
 msgid "New Name:"
 msgstr "Nytt namn:"
 
-#: lib/branch_rename.tcl:75
+#: lib/branch_rename.tcl:81
 msgid "Please select a branch to rename."
 msgstr "Välj en gren att byta namn på."
 
-#: lib/branch_rename.tcl:96 lib/checkout_op.tcl:201
+#: lib/branch_rename.tcl:102 lib/checkout_op.tcl:202
 #, tcl-format
 msgid "Branch '%s' already exists."
 msgstr "Grenen \"%s\" finns redan."
 
-#: lib/branch_rename.tcl:117
+#: lib/branch_rename.tcl:123
 #, tcl-format
 msgid "Failed to rename '%s'."
 msgstr "Kunde inte byta namn på \"%s\"."
@@ -823,7 +862,7 @@
 msgid "Starting..."
 msgstr "Startar..."
 
-#: lib/browser.tcl:26
+#: lib/browser.tcl:27
 msgid "File Browser"
 msgstr "Filbläddrare"
 
@@ -836,42 +875,42 @@
 msgid "[Up To Parent]"
 msgstr "[Upp till förälder]"
 
-#: lib/browser.tcl:267 lib/browser.tcl:273
+#: lib/browser.tcl:269 lib/browser.tcl:276
 msgid "Browse Branch Files"
 msgstr "Bläddra filer på grenen"
 
-#: lib/browser.tcl:278 lib/choose_repository.tcl:394
-#: lib/choose_repository.tcl:480 lib/choose_repository.tcl:491
-#: lib/choose_repository.tcl:995
+#: lib/browser.tcl:282 lib/choose_repository.tcl:404
+#: lib/choose_repository.tcl:491 lib/choose_repository.tcl:500
+#: lib/choose_repository.tcl:1027
 msgid "Browse"
 msgstr "Bläddra"
 
-#: lib/checkout_op.tcl:84
+#: lib/checkout_op.tcl:85
 #, tcl-format
 msgid "Fetching %s from %s"
 msgstr "Hämtar %s från %s"
 
-#: lib/checkout_op.tcl:132
+#: lib/checkout_op.tcl:133
 #, tcl-format
 msgid "fatal: Cannot resolve %s"
 msgstr "ödesdigert: Kunde inte slå upp %s"
 
-#: lib/checkout_op.tcl:145 lib/console.tcl:81 lib/database.tcl:31
-#: lib/sshkey.tcl:53
+#: lib/checkout_op.tcl:146 lib/console.tcl:81 lib/database.tcl:30
+#: lib/sshkey.tcl:55
 msgid "Close"
 msgstr "Stäng"
 
-#: lib/checkout_op.tcl:174
+#: lib/checkout_op.tcl:175
 #, tcl-format
 msgid "Branch '%s' does not exist."
 msgstr "Grenen \"%s\" finns inte."
 
-#: lib/checkout_op.tcl:193
+#: lib/checkout_op.tcl:194
 #, tcl-format
 msgid "Failed to configure simplified git-pull for '%s'."
 msgstr "Kunde inte konfigurera förenklad git-pull för '%s'."
 
-#: lib/checkout_op.tcl:228
+#: lib/checkout_op.tcl:229
 #, tcl-format
 msgid ""
 "Branch '%s' already exists.\n"
@@ -884,21 +923,21 @@
 "Den kan inte snabbspolas till %s.\n"
 "En sammanslagning krävs."
 
-#: lib/checkout_op.tcl:242
+#: lib/checkout_op.tcl:243
 #, tcl-format
 msgid "Merge strategy '%s' not supported."
 msgstr "Sammanslagningsstrategin \"%s\" stöds inte."
 
-#: lib/checkout_op.tcl:261
+#: lib/checkout_op.tcl:262
 #, tcl-format
 msgid "Failed to update '%s'."
 msgstr "Misslyckades med att uppdatera \"%s\"."
 
-#: lib/checkout_op.tcl:273
+#: lib/checkout_op.tcl:274
 msgid "Staging area (index) is already locked."
 msgstr "Köområdet (index) är redan låst."
 
-#: lib/checkout_op.tcl:288
+#: lib/checkout_op.tcl:289
 msgid ""
 "Last scanned state does not match repository state.\n"
 "\n"
@@ -914,30 +953,30 @@
 "\n"
 "Sökningen kommer att startas automatiskt nu.\n"
 
-#: lib/checkout_op.tcl:344
+#: lib/checkout_op.tcl:345
 #, tcl-format
 msgid "Updating working directory to '%s'..."
 msgstr "Uppdaterar arbetskatalogen till \"%s\"..."
 
-#: lib/checkout_op.tcl:345
+#: lib/checkout_op.tcl:346
 msgid "files checked out"
 msgstr "filer utcheckade"
 
-#: lib/checkout_op.tcl:375
+#: lib/checkout_op.tcl:376
 #, tcl-format
 msgid "Aborted checkout of '%s' (file level merging is required)."
 msgstr "Avbryter utcheckning av \"%s\" (sammanslagning på filnivå krävs)."
 
-#: lib/checkout_op.tcl:376
+#: lib/checkout_op.tcl:377
 msgid "File level merge required."
 msgstr "Sammanslagning på filnivå krävs."
 
-#: lib/checkout_op.tcl:380
+#: lib/checkout_op.tcl:381
 #, tcl-format
 msgid "Staying on branch '%s'."
 msgstr "Stannar på grenen \"%s\"."
 
-#: lib/checkout_op.tcl:451
+#: lib/checkout_op.tcl:452
 msgid ""
 "You are no longer on a local branch.\n"
 "\n"
@@ -949,31 +988,31 @@
 "Om du ville vara på en gren skapar du en nu, baserad på \"Denna frånkopplade "
 "utcheckning\"."
 
-#: lib/checkout_op.tcl:468 lib/checkout_op.tcl:472
+#: lib/checkout_op.tcl:503 lib/checkout_op.tcl:507
 #, tcl-format
 msgid "Checked out '%s'."
 msgstr "Checkade ut \"%s\"."
 
-#: lib/checkout_op.tcl:500
+#: lib/checkout_op.tcl:535
 #, tcl-format
 msgid "Resetting '%s' to '%s' will lose the following commits:"
 msgstr ""
 "Om du återställer \"%s\" till \"%s\" går följande incheckningar förlorade:"
 
-#: lib/checkout_op.tcl:522
+#: lib/checkout_op.tcl:557
 msgid "Recovering lost commits may not be easy."
 msgstr "Det kanske inte är så enkelt att återskapa förlorade incheckningar."
 
-#: lib/checkout_op.tcl:527
+#: lib/checkout_op.tcl:562
 #, tcl-format
 msgid "Reset '%s'?"
 msgstr "Återställa \"%s\"?"
 
-#: lib/checkout_op.tcl:532 lib/merge.tcl:164 lib/tools_dlg.tcl:343
+#: lib/checkout_op.tcl:567 lib/merge.tcl:164 lib/tools_dlg.tcl:336
 msgid "Visualize"
 msgstr "Visualisera"
 
-#: lib/checkout_op.tcl:600
+#: lib/checkout_op.tcl:635
 #, tcl-format
 msgid ""
 "Failed to set current branch.\n"
@@ -990,23 +1029,23 @@
 "\n"
 "Detta skulle inte ha hänt. %s kommer nu stängas och ge upp."
 
-#: lib/choose_font.tcl:39
+#: lib/choose_font.tcl:41
 msgid "Select"
 msgstr "Välj"
 
-#: lib/choose_font.tcl:53
+#: lib/choose_font.tcl:55
 msgid "Font Family"
 msgstr "Teckensnittsfamilj"
 
-#: lib/choose_font.tcl:74
+#: lib/choose_font.tcl:76
 msgid "Font Size"
 msgstr "Storlek"
 
-#: lib/choose_font.tcl:91
+#: lib/choose_font.tcl:93
 msgid "Font Example"
 msgstr "Exempel"
 
-#: lib/choose_font.tcl:103
+#: lib/choose_font.tcl:105
 msgid ""
 "This is example text.\n"
 "If you like this text, it can be your font."
@@ -1018,7 +1057,7 @@
 msgid "Git Gui"
 msgstr "Git Gui"
 
-#: lib/choose_repository.tcl:87 lib/choose_repository.tcl:382
+#: lib/choose_repository.tcl:87 lib/choose_repository.tcl:394
 msgid "Create New Repository"
 msgstr "Skapa nytt arkiv"
 
@@ -1026,222 +1065,222 @@
 msgid "New..."
 msgstr "Nytt..."
 
-#: lib/choose_repository.tcl:100 lib/choose_repository.tcl:465
+#: lib/choose_repository.tcl:100 lib/choose_repository.tcl:478
 msgid "Clone Existing Repository"
 msgstr "Klona befintligt arkiv"
 
-#: lib/choose_repository.tcl:106
+#: lib/choose_repository.tcl:111
 msgid "Clone..."
 msgstr "Klona..."
 
-#: lib/choose_repository.tcl:113 lib/choose_repository.tcl:983
+#: lib/choose_repository.tcl:118 lib/choose_repository.tcl:1017
 msgid "Open Existing Repository"
 msgstr "Öppna befintligt arkiv"
 
-#: lib/choose_repository.tcl:119
+#: lib/choose_repository.tcl:124
 msgid "Open..."
 msgstr "Öppna..."
 
-#: lib/choose_repository.tcl:132
+#: lib/choose_repository.tcl:137
 msgid "Recent Repositories"
 msgstr "Senaste arkiven"
 
-#: lib/choose_repository.tcl:138
+#: lib/choose_repository.tcl:143
 msgid "Open Recent Repository:"
 msgstr "Öppna tidigare arkiv:"
 
-#: lib/choose_repository.tcl:302 lib/choose_repository.tcl:309
-#: lib/choose_repository.tcl:316
+#: lib/choose_repository.tcl:313 lib/choose_repository.tcl:320
+#: lib/choose_repository.tcl:327
 #, tcl-format
 msgid "Failed to create repository %s:"
 msgstr "Kunde inte skapa arkivet %s:"
 
-#: lib/choose_repository.tcl:387
+#: lib/choose_repository.tcl:399
 msgid "Directory:"
 msgstr "Katalog:"
 
-#: lib/choose_repository.tcl:417 lib/choose_repository.tcl:544
-#: lib/choose_repository.tcl:1017
+#: lib/choose_repository.tcl:429 lib/choose_repository.tcl:550
+#: lib/choose_repository.tcl:1051
 msgid "Git Repository"
 msgstr "Gitarkiv"
 
-#: lib/choose_repository.tcl:442
+#: lib/choose_repository.tcl:454
 #, tcl-format
 msgid "Directory %s already exists."
 msgstr "Katalogen %s finns redan."
 
-#: lib/choose_repository.tcl:446
+#: lib/choose_repository.tcl:458
 #, tcl-format
 msgid "File %s already exists."
 msgstr "Filen %s finns redan."
 
-#: lib/choose_repository.tcl:460
+#: lib/choose_repository.tcl:473
 msgid "Clone"
 msgstr "Klona"
 
-#: lib/choose_repository.tcl:473
+#: lib/choose_repository.tcl:486
 msgid "Source Location:"
 msgstr "Plats för källkod:"
 
-#: lib/choose_repository.tcl:484
+#: lib/choose_repository.tcl:495
 msgid "Target Directory:"
 msgstr "Målkatalog:"
 
-#: lib/choose_repository.tcl:496
+#: lib/choose_repository.tcl:505
 msgid "Clone Type:"
 msgstr "Typ av klon:"
 
-#: lib/choose_repository.tcl:502
+#: lib/choose_repository.tcl:510
 msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
 msgstr "Standard (snabb, semiredundant, hårda länkar)"
 
-#: lib/choose_repository.tcl:508
+#: lib/choose_repository.tcl:515
 msgid "Full Copy (Slower, Redundant Backup)"
 msgstr "Full kopia (långsammare, redundant säkerhetskopia)"
 
-#: lib/choose_repository.tcl:514
+#: lib/choose_repository.tcl:520
 msgid "Shared (Fastest, Not Recommended, No Backup)"
 msgstr "Delad (snabbast, rekommenderas ej, ingen säkerhetskopia)"
 
-#: lib/choose_repository.tcl:550 lib/choose_repository.tcl:597
-#: lib/choose_repository.tcl:743 lib/choose_repository.tcl:813
-#: lib/choose_repository.tcl:1023 lib/choose_repository.tcl:1031
+#: lib/choose_repository.tcl:556 lib/choose_repository.tcl:603
+#: lib/choose_repository.tcl:749 lib/choose_repository.tcl:819
+#: lib/choose_repository.tcl:1057 lib/choose_repository.tcl:1065
 #, tcl-format
 msgid "Not a Git repository: %s"
 msgstr "Inte ett Gitarkiv: %s"
 
-#: lib/choose_repository.tcl:586
+#: lib/choose_repository.tcl:592
 msgid "Standard only available for local repository."
 msgstr "Standard är endast tillgängligt för lokala arkiv."
 
-#: lib/choose_repository.tcl:590
+#: lib/choose_repository.tcl:596
 msgid "Shared only available for local repository."
 msgstr "Delat är endast tillgängligt för lokala arkiv."
 
-#: lib/choose_repository.tcl:611
+#: lib/choose_repository.tcl:617
 #, tcl-format
 msgid "Location %s already exists."
 msgstr "Platsen %s finns redan."
 
-#: lib/choose_repository.tcl:622
+#: lib/choose_repository.tcl:628
 msgid "Failed to configure origin"
 msgstr "Kunde inte konfigurera ursprung"
 
-#: lib/choose_repository.tcl:634
+#: lib/choose_repository.tcl:640
 msgid "Counting objects"
 msgstr "Räknar objekt"
 
-#: lib/choose_repository.tcl:635
+#: lib/choose_repository.tcl:641
 msgid "buckets"
 msgstr "hinkar"
 
-#: lib/choose_repository.tcl:659
+#: lib/choose_repository.tcl:665
 #, tcl-format
 msgid "Unable to copy objects/info/alternates: %s"
 msgstr "Kunde inte kopiera objekt/info/alternativ: %s"
 
-#: lib/choose_repository.tcl:695
+#: lib/choose_repository.tcl:701
 #, tcl-format
 msgid "Nothing to clone from %s."
 msgstr "Ingenting att klona från %s."
 
-#: lib/choose_repository.tcl:697 lib/choose_repository.tcl:911
-#: lib/choose_repository.tcl:923
+#: lib/choose_repository.tcl:703 lib/choose_repository.tcl:917
+#: lib/choose_repository.tcl:929
 msgid "The 'master' branch has not been initialized."
 msgstr "Grenen \"master\" har inte initierats."
 
-#: lib/choose_repository.tcl:710
+#: lib/choose_repository.tcl:716
 msgid "Hardlinks are unavailable.  Falling back to copying."
 msgstr "Hårda länkar är inte tillgängliga. Faller tillbaka på kopiering."
 
-#: lib/choose_repository.tcl:722
+#: lib/choose_repository.tcl:728
 #, tcl-format
 msgid "Cloning from %s"
 msgstr "Klonar från %s"
 
-#: lib/choose_repository.tcl:753
+#: lib/choose_repository.tcl:759
 msgid "Copying objects"
 msgstr "Kopierar objekt"
 
-#: lib/choose_repository.tcl:754
+#: lib/choose_repository.tcl:760
 msgid "KiB"
 msgstr "KiB"
 
-#: lib/choose_repository.tcl:778
+#: lib/choose_repository.tcl:784
 #, tcl-format
 msgid "Unable to copy object: %s"
 msgstr "Kunde inte kopiera objekt: %s"
 
-#: lib/choose_repository.tcl:788
+#: lib/choose_repository.tcl:794
 msgid "Linking objects"
 msgstr "Länkar objekt"
 
-#: lib/choose_repository.tcl:789
+#: lib/choose_repository.tcl:795
 msgid "objects"
 msgstr "objekt"
 
-#: lib/choose_repository.tcl:797
+#: lib/choose_repository.tcl:803
 #, tcl-format
 msgid "Unable to hardlink object: %s"
 msgstr "Kunde inte hårdlänka objekt: %s"
 
-#: lib/choose_repository.tcl:852
+#: lib/choose_repository.tcl:858
 msgid "Cannot fetch branches and objects.  See console output for details."
 msgstr "Kunde inte hämta grenar och objekt. Se konsolutdata för detaljer."
 
-#: lib/choose_repository.tcl:863
+#: lib/choose_repository.tcl:869
 msgid "Cannot fetch tags.  See console output for details."
 msgstr "Kunde inte hämta taggar. Se konsolutdata för detaljer."
 
-#: lib/choose_repository.tcl:887
+#: lib/choose_repository.tcl:893
 msgid "Cannot determine HEAD.  See console output for details."
 msgstr "Kunde inte avgöra HEAD. Se konsolutdata för detaljer."
 
-#: lib/choose_repository.tcl:896
+#: lib/choose_repository.tcl:902
 #, tcl-format
 msgid "Unable to cleanup %s"
 msgstr "Kunde inte städa upp %s"
 
-#: lib/choose_repository.tcl:902
+#: lib/choose_repository.tcl:908
 msgid "Clone failed."
 msgstr "Kloning misslyckades."
 
-#: lib/choose_repository.tcl:909
+#: lib/choose_repository.tcl:915
 msgid "No default branch obtained."
 msgstr "Hämtade ingen standardgren."
 
-#: lib/choose_repository.tcl:920
+#: lib/choose_repository.tcl:926
 #, tcl-format
 msgid "Cannot resolve %s as a commit."
 msgstr "Kunde inte slå upp %s till någon incheckning."
 
-#: lib/choose_repository.tcl:932
+#: lib/choose_repository.tcl:938
 msgid "Creating working directory"
 msgstr "Skapar arbetskatalog"
 
-#: lib/choose_repository.tcl:933 lib/index.tcl:65 lib/index.tcl:128
-#: lib/index.tcl:196
+#: lib/choose_repository.tcl:939 lib/index.tcl:70 lib/index.tcl:133
+#: lib/index.tcl:201
 msgid "files"
 msgstr "filer"
 
-#: lib/choose_repository.tcl:962
+#: lib/choose_repository.tcl:968
 msgid "Initial file checkout failed."
 msgstr "Inledande filutcheckning misslyckades."
 
-#: lib/choose_repository.tcl:978
+#: lib/choose_repository.tcl:1012
 msgid "Open"
 msgstr "Öppna"
 
-#: lib/choose_repository.tcl:988
+#: lib/choose_repository.tcl:1022
 msgid "Repository:"
 msgstr "Arkiv:"
 
-#: lib/choose_repository.tcl:1037
+#: lib/choose_repository.tcl:1071
 #, tcl-format
 msgid "Failed to open repository %s:"
 msgstr "Kunde inte öppna arkivet %s:"
 
-#: lib/choose_rev.tcl:53
+#: lib/choose_rev.tcl:52
 msgid "This Detached Checkout"
 msgstr "Denna frånkopplade utcheckning"
 
@@ -1249,36 +1288,36 @@
 msgid "Revision Expression:"
 msgstr "Revisionsuttryck:"
 
-#: lib/choose_rev.tcl:74
+#: lib/choose_rev.tcl:72
 msgid "Local Branch"
 msgstr "Lokal gren"
 
-#: lib/choose_rev.tcl:79
+#: lib/choose_rev.tcl:77
 msgid "Tracking Branch"
 msgstr "Spårande gren"
 
-#: lib/choose_rev.tcl:84 lib/choose_rev.tcl:538
+#: lib/choose_rev.tcl:82 lib/choose_rev.tcl:543
 msgid "Tag"
 msgstr "Tagg"
 
-#: lib/choose_rev.tcl:317
+#: lib/choose_rev.tcl:321
 #, tcl-format
 msgid "Invalid revision: %s"
 msgstr "Ogiltig revision: %s"
 
-#: lib/choose_rev.tcl:338
+#: lib/choose_rev.tcl:342
 msgid "No revision selected."
 msgstr "Ingen revision vald."
 
-#: lib/choose_rev.tcl:346
+#: lib/choose_rev.tcl:350
 msgid "Revision expression is empty."
 msgstr "Revisionsuttrycket är tomt."
 
-#: lib/choose_rev.tcl:531
+#: lib/choose_rev.tcl:536
 msgid "Updated"
 msgstr "Uppdaterad"
 
-#: lib/choose_rev.tcl:559
+#: lib/choose_rev.tcl:564
 msgid "URL"
 msgstr "Webbadress"
 
@@ -1320,7 +1359,12 @@
 msgid "Invalid GIT_COMMITTER_IDENT:"
 msgstr "Felaktig GIT_COMMITTER_IDENT:"
 
-#: lib/commit.tcl:132
+#: lib/commit.tcl:129
+#, tcl-format
+msgid "warning: Tcl does not support encoding '%s'."
+msgstr "varning: Tcl stöder inte teckenkodningen \"%s\"."
+
+#: lib/commit.tcl:149
 msgid ""
 "Last scanned state does not match repository state.\n"
 "\n"
@@ -1336,7 +1380,7 @@
 "\n"
 "Sökningen kommer att startas automatiskt nu.\n"
 
-#: lib/commit.tcl:155
+#: lib/commit.tcl:172
 #, tcl-format
 msgid ""
 "Unmerged files cannot be committed.\n"
@@ -1349,7 +1393,7 @@
 "Filen %s har sammanslagningskonflikter. Du måste lösa dem och köa filen "
 "innan du checkar in den.\n"
 
-#: lib/commit.tcl:163
+#: lib/commit.tcl:180
 #, tcl-format
 msgid ""
 "Unknown file state %s detected.\n"
@@ -1360,7 +1404,7 @@
 "\n"
 "Filen %s kan inte checkas in av programmet.\n"
 
-#: lib/commit.tcl:171
+#: lib/commit.tcl:188
 msgid ""
 "No changes to commit.\n"
 "\n"
@@ -1370,7 +1414,7 @@
 "\n"
 "Du måste köa åtminstone en fil innan du kan checka in.\n"
 
-#: lib/commit.tcl:186
+#: lib/commit.tcl:203
 msgid ""
 "Please supply a commit message.\n"
 "\n"
@@ -1388,45 +1432,40 @@
 "- Andra raden: Tom\n"
 "- Följande rader: Beskriv varför det här är en bra ändring.\n"
 
-#: lib/commit.tcl:210
-#, tcl-format
-msgid "warning: Tcl does not support encoding '%s'."
-msgstr "varning: Tcl stöder inte teckenkodningen \"%s\"."
-
-#: lib/commit.tcl:226
+#: lib/commit.tcl:234
 msgid "Calling pre-commit hook..."
 msgstr "Anropar kroken före incheckning (pre-commit)..."
 
-#: lib/commit.tcl:241
+#: lib/commit.tcl:249
 msgid "Commit declined by pre-commit hook."
 msgstr "Incheckningen avvisades av kroken före incheckning (pre-commit)."
 
-#: lib/commit.tcl:264
+#: lib/commit.tcl:272
 msgid "Calling commit-msg hook..."
 msgstr "Anropar kroken för incheckningsmeddelande (commit-msg)..."
 
-#: lib/commit.tcl:279
+#: lib/commit.tcl:287
 msgid "Commit declined by commit-msg hook."
 msgstr "Incheckning avvisad av kroken för incheckningsmeddelande (commit-msg)."
 
-#: lib/commit.tcl:292
+#: lib/commit.tcl:300
 msgid "Committing changes..."
 msgstr "Checkar in ändringar..."
 
-#: lib/commit.tcl:308
+#: lib/commit.tcl:316
 msgid "write-tree failed:"
 msgstr "write-tree misslyckades:"
 
-#: lib/commit.tcl:309 lib/commit.tcl:353 lib/commit.tcl:373
+#: lib/commit.tcl:317 lib/commit.tcl:361 lib/commit.tcl:382
 msgid "Commit failed."
 msgstr "Incheckningen misslyckades."
 
-#: lib/commit.tcl:326
+#: lib/commit.tcl:334
 #, tcl-format
 msgid "Commit %s appears to be corrupt"
 msgstr "Incheckningen %s verkar vara trasig"
 
-#: lib/commit.tcl:331
+#: lib/commit.tcl:339
 msgid ""
 "No changes to commit.\n"
 "\n"
@@ -1440,19 +1479,19 @@
 "\n"
 "En sökning kommer att startas automatiskt nu.\n"
 
-#: lib/commit.tcl:338
+#: lib/commit.tcl:346
 msgid "No changes to commit."
 msgstr "Inga ändringar att checka in."
 
-#: lib/commit.tcl:352
+#: lib/commit.tcl:360
 msgid "commit-tree failed:"
 msgstr "commit-tree misslyckades:"
 
-#: lib/commit.tcl:372
+#: lib/commit.tcl:381
 msgid "update-ref failed:"
 msgstr "update-ref misslyckades:"
 
-#: lib/commit.tcl:460
+#: lib/commit.tcl:469
 #, tcl-format
 msgid "Created commit %s: %s"
 msgstr "Skapade incheckningen %s: %s"
@@ -1469,31 +1508,31 @@
 msgid "Error: Command Failed"
 msgstr "Fel: Kommando misslyckades"
 
-#: lib/database.tcl:43
+#: lib/database.tcl:42
 msgid "Number of loose objects"
 msgstr "Antal lösa objekt"
 
-#: lib/database.tcl:44
+#: lib/database.tcl:43
 msgid "Disk space used by loose objects"
 msgstr "Diskutrymme använt av lösa objekt"
 
-#: lib/database.tcl:45
+#: lib/database.tcl:44
 msgid "Number of packed objects"
 msgstr "Antal packade objekt"
 
-#: lib/database.tcl:46
+#: lib/database.tcl:45
 msgid "Number of packs"
 msgstr "Antal paket"
 
-#: lib/database.tcl:47
+#: lib/database.tcl:46
 msgid "Disk space used by packed objects"
 msgstr "Diskutrymme använt av packade objekt"
 
-#: lib/database.tcl:48
+#: lib/database.tcl:47
 msgid "Packed objects waiting for pruning"
 msgstr "Packade objekt som väntar på städning"
 
-#: lib/database.tcl:49
+#: lib/database.tcl:48
 msgid "Garbage files"
 msgstr "Skräpfiler"
 
@@ -1505,20 +1544,20 @@
 msgid "Verifying the object database with fsck-objects"
 msgstr "Verifierar objektdatabasen med fsck-objects"
 
-#: lib/database.tcl:108
+#: lib/database.tcl:107
 #, tcl-format
 msgid ""
 "This repository currently has approximately %i loose objects.\n"
 "\n"
 "To maintain optimal performance it is strongly recommended that you compress "
-"the database when more than %i loose objects exist.\n"
+"the database.\n"
 "\n"
 "Compress the database now?"
 msgstr ""
 "Arkivet har för närvarande omkring %i lösa objekt.\n"
 "\n"
 "För att bibehålla optimal prestanda rekommenderas det å det bestämdaste att "
-"du komprimerar databasen när den innehåller mer än %i lösa objekt.\n"
+"du komprimerar databasen.\n"
 "\n"
 "Komprimera databasen nu?"
 
@@ -1527,7 +1566,7 @@
 msgid "Invalid date from Git: %s"
 msgstr "Ogiltigt datum från Git: %s"
 
-#: lib/diff.tcl:59
+#: lib/diff.tcl:64
 #, tcl-format
 msgid ""
 "No differences detected.\n"
@@ -1550,12 +1589,12 @@
 "En sökning kommer automatiskt att startas för att hitta andra filer som kan "
 "vara i samma tillstånd."
 
-#: lib/diff.tcl:99
+#: lib/diff.tcl:104
 #, tcl-format
 msgid "Loading diff of %s..."
 msgstr "Läser differens för %s..."
 
-#: lib/diff.tcl:120
+#: lib/diff.tcl:125
 msgid ""
 "LOCAL: deleted\n"
 "REMOTE:\n"
@@ -1563,7 +1602,7 @@
 "LOKAL: borttagen\n"
 "FJÄRR:\n"
 
-#: lib/diff.tcl:125
+#: lib/diff.tcl:130
 msgid ""
 "REMOTE: deleted\n"
 "LOCAL:\n"
@@ -1571,32 +1610,32 @@
 "FJÄRR: borttagen\n"
 "LOKAL:\n"
 
-#: lib/diff.tcl:132
+#: lib/diff.tcl:137
 msgid "LOCAL:\n"
 msgstr "LOKAL:\n"
 
-#: lib/diff.tcl:135
+#: lib/diff.tcl:140
 msgid "REMOTE:\n"
 msgstr "FJÄRR:\n"
 
-#: lib/diff.tcl:197 lib/diff.tcl:296
+#: lib/diff.tcl:202 lib/diff.tcl:322
 #, tcl-format
 msgid "Unable to display %s"
 msgstr "Kan inte visa %s"
 
-#: lib/diff.tcl:198
+#: lib/diff.tcl:203
 msgid "Error loading file:"
 msgstr "Fel vid läsning av fil:"
 
-#: lib/diff.tcl:205
+#: lib/diff.tcl:210
 msgid "Git Repository (subproject)"
 msgstr "Gitarkiv (underprojekt)"
 
-#: lib/diff.tcl:217
+#: lib/diff.tcl:222
 msgid "* Binary file (not showing content)."
 msgstr "* Binärfil (visar inte innehållet)."
 
-#: lib/diff.tcl:222
+#: lib/diff.tcl:227
 #, tcl-format
 msgid ""
 "* Untracked file is %d bytes.\n"
@@ -1605,7 +1644,7 @@
 "* Den ospårade filen är %d byte.\n"
 "* Visar endast inledande %d byte.\n"
 
-#: lib/diff.tcl:228
+#: lib/diff.tcl:233
 #, tcl-format
 msgid ""
 "\n"
@@ -1616,19 +1655,19 @@
 "* Den ospårade filen klipptes här av %s.\n"
 "* För att se hela filen, använd ett externt redigeringsprogram.\n"
 
-#: lib/diff.tcl:436
+#: lib/diff.tcl:485
 msgid "Failed to unstage selected hunk."
 msgstr "Kunde inte ta bort den valda delen från kön."
 
-#: lib/diff.tcl:443
+#: lib/diff.tcl:492
 msgid "Failed to stage selected hunk."
 msgstr "Kunde inte lägga till den valda delen till kön."
 
-#: lib/diff.tcl:509
+#: lib/diff.tcl:571
 msgid "Failed to unstage selected line."
 msgstr "Kunde inte ta bort den valda raden från kön."
 
-#: lib/diff.tcl:517
+#: lib/diff.tcl:579
 msgid "Failed to stage selected line."
 msgstr "Kunde inte lägga till den valda raden till kön."
 
@@ -1645,7 +1684,7 @@
 msgid "Other"
 msgstr "Annan"
 
-#: lib/error.tcl:20 lib/error.tcl:114
+#: lib/error.tcl:20 lib/error.tcl:116
 msgid "error"
 msgstr "fel"
 
@@ -1653,7 +1692,7 @@
 msgid "warning"
 msgstr "varning"
 
-#: lib/error.tcl:94
+#: lib/error.tcl:96
 msgid "You must correct the above errors before committing."
 msgstr "Du måste rätta till felen ovan innan du checkar in."
 
@@ -1661,11 +1700,11 @@
 msgid "Unable to unlock the index."
 msgstr "Kunde inte låsa upp indexet."
 
-#: lib/index.tcl:15
+#: lib/index.tcl:17
 msgid "Index Error"
 msgstr "Indexfel"
 
-#: lib/index.tcl:21
+#: lib/index.tcl:19
 msgid ""
 "Updating the Git index failed.  A rescan will be automatically started to "
 "resynchronize git-gui."
@@ -1673,52 +1712,52 @@
 "Misslyckades med att uppdatera Gitindexet. En omsökning kommer att startas "
 "automatiskt för att synkronisera om git-gui."
 
-#: lib/index.tcl:27
+#: lib/index.tcl:30
 msgid "Continue"
-msgstr "Forstätt"
+msgstr "Fortsätt"
 
-#: lib/index.tcl:31
+#: lib/index.tcl:33
 msgid "Unlock Index"
 msgstr "Lås upp index"
 
-#: lib/index.tcl:287
+#: lib/index.tcl:292
 #, tcl-format
 msgid "Unstaging %s from commit"
 msgstr "Tar bort %s för incheckningskön"
 
-#: lib/index.tcl:326
+#: lib/index.tcl:331
 msgid "Ready to commit."
 msgstr "Redo att checka in."
 
-#: lib/index.tcl:339
+#: lib/index.tcl:344
 #, tcl-format
 msgid "Adding %s"
 msgstr "Lägger till %s"
 
-#: lib/index.tcl:396
+#: lib/index.tcl:401
 #, tcl-format
 msgid "Revert changes in file %s?"
 msgstr "Återställ ändringarna i filen %s?"
 
-#: lib/index.tcl:398
+#: lib/index.tcl:403
 #, tcl-format
 msgid "Revert changes in these %i files?"
 msgstr "Återställ ändringarna i dessa %i filer?"
 
-#: lib/index.tcl:406
+#: lib/index.tcl:411
 msgid "Any unstaged changes will be permanently lost by the revert."
 msgstr ""
 "Alla oköade ändringar kommer permanent gå förlorade vid återställningen."
 
-#: lib/index.tcl:409
+#: lib/index.tcl:414
 msgid "Do Nothing"
 msgstr "Gör ingenting"
 
-#: lib/index.tcl:427
+#: lib/index.tcl:432
 msgid "Reverting selected files"
 msgstr "Återställer valda filer"
 
-#: lib/index.tcl:431
+#: lib/index.tcl:436
 #, tcl-format
 msgid "Reverting %s"
 msgstr "Återställer %s"
@@ -1965,116 +2004,266 @@
 msgid "Invalid repo encoding '%s'"
 msgstr "Arkivets teckenkodning \"%s\" är ogiltig"
 
-#: lib/option.tcl:117
+#: lib/option.tcl:119
 msgid "Restore Defaults"
 msgstr "Återställ standardvärden"
 
-#: lib/option.tcl:121
+#: lib/option.tcl:123
 msgid "Save"
 msgstr "Spara"
 
-#: lib/option.tcl:131
+#: lib/option.tcl:133
 #, tcl-format
 msgid "%s Repository"
 msgstr "Arkivet %s"
 
-#: lib/option.tcl:132
+#: lib/option.tcl:134
 msgid "Global (All Repositories)"
 msgstr "Globalt (alla arkiv)"
 
-#: lib/option.tcl:138
+#: lib/option.tcl:140
 msgid "User Name"
 msgstr "Användarnamn"
 
-#: lib/option.tcl:139
+#: lib/option.tcl:141
 msgid "Email Address"
 msgstr "E-postadress"
 
-#: lib/option.tcl:141
+#: lib/option.tcl:143
 msgid "Summarize Merge Commits"
 msgstr "Summera sammanslagningsincheckningar"
 
-#: lib/option.tcl:142
+#: lib/option.tcl:144
 msgid "Merge Verbosity"
 msgstr "Pratsamhet för sammanslagningar"
 
-#: lib/option.tcl:143
+#: lib/option.tcl:145
 msgid "Show Diffstat After Merge"
 msgstr "Visa diffstatistik efter sammanslagning"
 
-#: lib/option.tcl:144
+#: lib/option.tcl:146
 msgid "Use Merge Tool"
 msgstr "Använd verktyg för sammanslagning"
 
-#: lib/option.tcl:146
+#: lib/option.tcl:148
 msgid "Trust File Modification Timestamps"
 msgstr "Lita på filändringstidsstämplar"
 
-#: lib/option.tcl:147
+#: lib/option.tcl:149
 msgid "Prune Tracking Branches During Fetch"
 msgstr "Städa spårade grenar vid hämtning"
 
-#: lib/option.tcl:148
+#: lib/option.tcl:150
 msgid "Match Tracking Branches"
 msgstr "Matcha spårade grenar"
 
-#: lib/option.tcl:149
+#: lib/option.tcl:151
+msgid "Use Textconv For Diffs and Blames"
+msgstr "Använd Textconv för diff och klandring"
+
+#: lib/option.tcl:152
 msgid "Blame Copy Only On Changed Files"
 msgstr "Klandra kopiering bara i ändrade filer"
 
-#: lib/option.tcl:150
+#: lib/option.tcl:153
 msgid "Minimum Letters To Blame Copy On"
 msgstr "Minsta antal tecken att klandra kopiering för"
 
-#: lib/option.tcl:151
+#: lib/option.tcl:154
 msgid "Blame History Context Radius (days)"
 msgstr "Historikradie för klandring (dagar)"
 
-#: lib/option.tcl:152
+#: lib/option.tcl:155
 msgid "Number of Diff Context Lines"
 msgstr "Antal rader sammanhang i differenser"
 
-#: lib/option.tcl:153
+#: lib/option.tcl:156
 msgid "Commit Message Text Width"
 msgstr "Textbredd för incheckningsmeddelande"
 
-#: lib/option.tcl:154
+#: lib/option.tcl:157
 msgid "New Branch Name Template"
 msgstr "Mall för namn på nya grenar"
 
-#: lib/option.tcl:155
+#: lib/option.tcl:158
 msgid "Default File Contents Encoding"
 msgstr "Standardteckenkodning för filinnehåll"
 
-#: lib/option.tcl:203
+#: lib/option.tcl:204
 msgid "Change"
 msgstr "Ändra"
 
-#: lib/option.tcl:230
+#: lib/option.tcl:231
 msgid "Spelling Dictionary:"
 msgstr "Stavningsordlista:"
 
-#: lib/option.tcl:254
+#: lib/option.tcl:261
 msgid "Change Font"
 msgstr "Byt teckensnitt"
 
-#: lib/option.tcl:258
+#: lib/option.tcl:265
 #, tcl-format
 msgid "Choose %s"
 msgstr "Välj %s"
 
-#: lib/option.tcl:264
+#: lib/option.tcl:271
 msgid "pt."
 msgstr "p."
 
-#: lib/option.tcl:278
+#: lib/option.tcl:285
 msgid "Preferences"
 msgstr "Inställningar"
 
-#: lib/option.tcl:314
+#: lib/option.tcl:322
 msgid "Failed to completely save options:"
 msgstr "Misslyckades med att helt spara alternativ:"
 
+#: lib/remote_add.tcl:20
+msgid "Add Remote"
+msgstr "Lägg till fjärrarkiv"
+
+#: lib/remote_add.tcl:25
+msgid "Add New Remote"
+msgstr "Lägg till nytt fjärrarkiv"
+
+#: lib/remote_add.tcl:30 lib/tools_dlg.tcl:37
+msgid "Add"
+msgstr "Lägg till"
+
+#: lib/remote_add.tcl:39
+msgid "Remote Details"
+msgstr "Detaljer för fjärrarkiv"
+
+#: lib/remote_add.tcl:50
+msgid "Location:"
+msgstr "Plats:"
+
+#: lib/remote_add.tcl:60
+msgid "Further Action"
+msgstr "Ytterligare åtgärd"
+
+#: lib/remote_add.tcl:63
+msgid "Fetch Immediately"
+msgstr "Hämta omedelbart"
+
+#: lib/remote_add.tcl:69
+msgid "Initialize Remote Repository and Push"
+msgstr "Initiera fjärrarkiv och sänd till"
+
+#: lib/remote_add.tcl:75
+msgid "Do Nothing Else Now"
+msgstr "Gör ingent mer nu"
+
+#: lib/remote_add.tcl:100
+msgid "Please supply a remote name."
+msgstr "Ange ett namn för fjärrarkivet."
+
+#: lib/remote_add.tcl:113
+#, tcl-format
+msgid "'%s' is not an acceptable remote name."
+msgstr "\"%s\" kan inte användas som namn på fjärrarkivet."
+
+#: lib/remote_add.tcl:124
+#, tcl-format
+msgid "Failed to add remote '%s' of location '%s'."
+msgstr "Kunde inte lägga till fjärrarkivet \"%s\" på platsen \"%s\"."
+
+#: lib/remote_add.tcl:132 lib/transport.tcl:6
+#, tcl-format
+msgid "fetch %s"
+msgstr "hämta %s"
+
+#: lib/remote_add.tcl:133
+#, tcl-format
+msgid "Fetching the %s"
+msgstr "Hämtar %s"
+
+#: lib/remote_add.tcl:156
+#, tcl-format
+msgid "Do not know how to initialize repository at location '%s'."
+msgstr "Vet inte hur arkivet på platsen \"%s\" skall initieras."
+
+#: lib/remote_add.tcl:162 lib/transport.tcl:25 lib/transport.tcl:63
+#: lib/transport.tcl:81
+#, tcl-format
+msgid "push %s"
+msgstr "sänd %s"
+
+#: lib/remote_add.tcl:163
+#, tcl-format
+msgid "Setting up the %s (at %s)"
+msgstr "Konfigurerar %s (på %s)"
+
+#: lib/remote_branch_delete.tcl:29 lib/remote_branch_delete.tcl:34
+msgid "Delete Branch Remotely"
+msgstr "Ta bort gren från fjärrarkiv"
+
+#: lib/remote_branch_delete.tcl:48
+msgid "From Repository"
+msgstr "Från arkiv"
+
+#: lib/remote_branch_delete.tcl:51 lib/transport.tcl:134
+msgid "Remote:"
+msgstr "Fjärrarkiv:"
+
+#: lib/remote_branch_delete.tcl:72 lib/transport.tcl:154
+msgid "Arbitrary Location:"
+msgstr "Godtycklig plats:"
+
+#: lib/remote_branch_delete.tcl:88
+msgid "Branches"
+msgstr "Grenar"
+
+#: lib/remote_branch_delete.tcl:110
+msgid "Delete Only If"
+msgstr "Ta endast bort om"
+
+#: lib/remote_branch_delete.tcl:112
+msgid "Merged Into:"
+msgstr "Sammanslagen i:"
+
+#: lib/remote_branch_delete.tcl:153
+msgid "A branch is required for 'Merged Into'."
+msgstr "En gren krävs för \"Sammanslagen i\"."
+
+#: lib/remote_branch_delete.tcl:185
+#, tcl-format
+msgid ""
+"The following branches are not completely merged into %s:\n"
+"\n"
+" - %s"
+msgstr ""
+"Följande grenar har inte helt slagits samman i %s:\n"
+"\n"
+" - %s"
+
+#: lib/remote_branch_delete.tcl:190
+#, tcl-format
+msgid ""
+"One or more of the merge tests failed because you have not fetched the "
+"necessary commits.  Try fetching from %s first."
+msgstr ""
+"En eller flera av sammanslagningstesterna misslyckades eftersom du inte har "
+"hämtat de nödvändiga incheckningarna. Försök hämta från %s först."
+
+#: lib/remote_branch_delete.tcl:208
+msgid "Please select one or more branches to delete."
+msgstr "Välj en eller flera grenar att ta bort."
+
+#: lib/remote_branch_delete.tcl:227
+#, tcl-format
+msgid "Deleting branches from %s"
+msgstr "Tar bort grenar från %s"
+
+#: lib/remote_branch_delete.tcl:293
+msgid "No repository selected."
+msgstr "Inget arkiv markerat."
+
+#: lib/remote_branch_delete.tcl:298
+#, tcl-format
+msgid "Scanning %s..."
+msgstr "Söker %s..."
+
 #: lib/remote.tcl:163
 msgid "Remove Remote"
 msgstr "Ta bort fjärrarkiv"
@@ -2091,187 +2280,27 @@
 msgid "Push to"
 msgstr "Sänd till"
 
-#: lib/remote_add.tcl:19
-msgid "Add Remote"
-msgstr "Lägg till fjärrarkiv"
-
-#: lib/remote_add.tcl:24
-msgid "Add New Remote"
-msgstr "Lägg till nytt fjärrarkiv"
-
-#: lib/remote_add.tcl:28 lib/tools_dlg.tcl:36
-msgid "Add"
-msgstr "Lägg till"
-
-#: lib/remote_add.tcl:37
-msgid "Remote Details"
-msgstr "Detaljer för fjärrarkiv"
-
-#: lib/remote_add.tcl:50
-msgid "Location:"
-msgstr "Plats:"
-
-#: lib/remote_add.tcl:62
-msgid "Further Action"
-msgstr "Ytterligare åtgärd"
-
-#: lib/remote_add.tcl:65
-msgid "Fetch Immediately"
-msgstr "Hämta omedelbart"
-
-#: lib/remote_add.tcl:71
-msgid "Initialize Remote Repository and Push"
-msgstr "Initiera fjärrarkiv och sänd till"
-
-#: lib/remote_add.tcl:77
-msgid "Do Nothing Else Now"
-msgstr "Gör ingent mer nu"
-
-#: lib/remote_add.tcl:101
-msgid "Please supply a remote name."
-msgstr "Ange ett namn för fjärrarkivet."
-
-#: lib/remote_add.tcl:114
-#, tcl-format
-msgid "'%s' is not an acceptable remote name."
-msgstr "\"%s\" kan inte användas som namn på fjärrarkivet."
-
-#: lib/remote_add.tcl:125
-#, tcl-format
-msgid "Failed to add remote '%s' of location '%s'."
-msgstr "Kunde inte lägga till fjärrarkivet \"%s\" på platsen \"%s\"."
-
-#: lib/remote_add.tcl:133 lib/transport.tcl:6
-#, tcl-format
-msgid "fetch %s"
-msgstr "hämta %s"
-
-#: lib/remote_add.tcl:134
-#, tcl-format
-msgid "Fetching the %s"
-msgstr "Hämtar %s"
-
-#: lib/remote_add.tcl:157
-#, tcl-format
-msgid "Do not know how to initialize repository at location '%s'."
-msgstr "Vet inte hur arkivet på platsen \"%s\" skall initieras."
-
-#: lib/remote_add.tcl:163 lib/transport.tcl:25 lib/transport.tcl:63
-#: lib/transport.tcl:81
-#, tcl-format
-msgid "push %s"
-msgstr "sänd %s"
-
-#: lib/remote_add.tcl:164
-#, tcl-format
-msgid "Setting up the %s (at %s)"
-msgstr "Konfigurerar %s (på %s)"
-
-#: lib/remote_branch_delete.tcl:29 lib/remote_branch_delete.tcl:34
-msgid "Delete Branch Remotely"
-msgstr "Ta bort gren från fjärrarkiv"
-
-#: lib/remote_branch_delete.tcl:47
-msgid "From Repository"
-msgstr "Från arkiv"
-
-#: lib/remote_branch_delete.tcl:50 lib/transport.tcl:134
-msgid "Remote:"
-msgstr "Fjärrarkiv:"
-
-#: lib/remote_branch_delete.tcl:66 lib/transport.tcl:149
-msgid "Arbitrary Location:"
-msgstr "Godtycklig plats:"
-
-#: lib/remote_branch_delete.tcl:84
-msgid "Branches"
-msgstr "Grenar"
-
-#: lib/remote_branch_delete.tcl:109
-msgid "Delete Only If"
-msgstr "Ta endast bort om"
-
-#: lib/remote_branch_delete.tcl:111
-msgid "Merged Into:"
-msgstr "Sammanslagen i:"
-
-#: lib/remote_branch_delete.tcl:119
-msgid "Always (Do not perform merge checks)"
-msgstr "Alltid (utför inte sammanslagningstest)"
-
-#: lib/remote_branch_delete.tcl:152
-msgid "A branch is required for 'Merged Into'."
-msgstr "En gren krävs för \"Sammanslagen i\"."
-
-#: lib/remote_branch_delete.tcl:184
-#, tcl-format
-msgid ""
-"The following branches are not completely merged into %s:\n"
-"\n"
-" - %s"
-msgstr ""
-"Följande grenar har inte helt slagits samman i %s:\n"
-"\n"
-" - %s"
-
-#: lib/remote_branch_delete.tcl:189
-#, tcl-format
-msgid ""
-"One or more of the merge tests failed because you have not fetched the "
-"necessary commits.  Try fetching from %s first."
-msgstr ""
-"En eller flera av sammanslagningstesterna misslyckades eftersom du inte har "
-"hämtat de nödvändiga incheckningarna. Försök hämta från %s först."
-
-#: lib/remote_branch_delete.tcl:207
-msgid "Please select one or more branches to delete."
-msgstr "Välj en eller flera grenar att ta bort."
-
-#: lib/remote_branch_delete.tcl:216
-msgid ""
-"Recovering deleted branches is difficult.\n"
-"\n"
-"Delete the selected branches?"
-msgstr ""
-"Det kan vara svårt att återställa borttagna grenar.\n"
-"\n"
-"Ta bort de valda grenarna?"
-
-#: lib/remote_branch_delete.tcl:226
-#, tcl-format
-msgid "Deleting branches from %s"
-msgstr "Tar bort grenar från %s"
-
-#: lib/remote_branch_delete.tcl:286
-msgid "No repository selected."
-msgstr "Inget arkiv markerat."
-
-#: lib/remote_branch_delete.tcl:291
-#, tcl-format
-msgid "Scanning %s..."
-msgstr "Söker %s..."
-
-#: lib/search.tcl:21
+#: lib/search.tcl:22
 msgid "Find:"
 msgstr "Sök:"
 
-#: lib/search.tcl:23
+#: lib/search.tcl:24
 msgid "Next"
 msgstr "Nästa"
 
-#: lib/search.tcl:24
+#: lib/search.tcl:25
 msgid "Prev"
 msgstr "Föreg"
 
-#: lib/search.tcl:25
+#: lib/search.tcl:26
 msgid "Case-Sensitive"
 msgstr "Skilj på VERSALER/gemener"
 
-#: lib/shortcut.tcl:20 lib/shortcut.tcl:61
+#: lib/shortcut.tcl:21 lib/shortcut.tcl:62
 msgid "Cannot write shortcut:"
 msgstr "Kan inte skriva genväg:"
 
-#: lib/shortcut.tcl:136
+#: lib/shortcut.tcl:137
 msgid "Cannot write icon:"
 msgstr "Kan inte skriva ikon:"
 
@@ -2325,19 +2354,19 @@
 msgid "Generate Key"
 msgstr "Skapa nyckel"
 
-#: lib/sshkey.tcl:56
+#: lib/sshkey.tcl:58
 msgid "Copy To Clipboard"
 msgstr "Kopiera till Urklipp"
 
-#: lib/sshkey.tcl:70
+#: lib/sshkey.tcl:72
 msgid "Your OpenSSH Public Key"
 msgstr "Din öppna OpenSSH-nyckel"
 
-#: lib/sshkey.tcl:78
+#: lib/sshkey.tcl:80
 msgid "Generating..."
 msgstr "Skapar..."
 
-#: lib/sshkey.tcl:84
+#: lib/sshkey.tcl:86
 #, tcl-format
 msgid ""
 "Could not start ssh-keygen:\n"
@@ -2348,24 +2377,115 @@
 "\n"
 "%s"
 
-#: lib/sshkey.tcl:111
+#: lib/sshkey.tcl:113
 msgid "Generation failed."
 msgstr "Misslyckades med att skapa."
 
-#: lib/sshkey.tcl:118
+#: lib/sshkey.tcl:120
 msgid "Generation succeded, but no keys found."
 msgstr "Lyckades skapa nyckeln, men hittar inte någon nyckel."
 
-#: lib/sshkey.tcl:121
+#: lib/sshkey.tcl:123
 #, tcl-format
 msgid "Your key is in: %s"
 msgstr "Din nyckel finns i: %s"
 
-#: lib/status_bar.tcl:83
+#: lib/status_bar.tcl:86
 #, tcl-format
 msgid "%s ... %*i of %*i %s (%3i%%)"
 msgstr "%s... %*i av %*i %s (%3i%%)"
 
+#: lib/tools_dlg.tcl:22
+msgid "Add Tool"
+msgstr "Lägg till verktyg"
+
+#: lib/tools_dlg.tcl:28
+msgid "Add New Tool Command"
+msgstr "Lägg till nytt verktygskommando"
+
+#: lib/tools_dlg.tcl:34
+msgid "Add globally"
+msgstr "Lägg till globalt"
+
+#: lib/tools_dlg.tcl:46
+msgid "Tool Details"
+msgstr "Detaljer för verktyg"
+
+#: lib/tools_dlg.tcl:49
+msgid "Use '/' separators to create a submenu tree:"
+msgstr "Använd \"/\"-avdelare för att skapa ett undermenyträd:"
+
+#: lib/tools_dlg.tcl:60
+msgid "Command:"
+msgstr "Kommando:"
+
+#: lib/tools_dlg.tcl:71
+msgid "Show a dialog before running"
+msgstr "Visa dialog innan programmet startas"
+
+#: lib/tools_dlg.tcl:77
+msgid "Ask the user to select a revision (sets $REVISION)"
+msgstr "Be användaren välja en version (sätter $REVISION)"
+
+#: lib/tools_dlg.tcl:82
+msgid "Ask the user for additional arguments (sets $ARGS)"
+msgstr "Be användaren om ytterligare parametrar (sätter $ARGS)"
+
+#: lib/tools_dlg.tcl:89
+msgid "Don't show the command output window"
+msgstr "Visa inte kommandots utdatafönster"
+
+#: lib/tools_dlg.tcl:94
+msgid "Run only if a diff is selected ($FILENAME not empty)"
+msgstr "Kör endast om en diff har markerats ($FILENAME är inte tomt)"
+
+#: lib/tools_dlg.tcl:118
+msgid "Please supply a name for the tool."
+msgstr "Ange ett namn för verktyget."
+
+#: lib/tools_dlg.tcl:126
+#, tcl-format
+msgid "Tool '%s' already exists."
+msgstr "Verktyget \"%s\" finns redan."
+
+#: lib/tools_dlg.tcl:148
+#, tcl-format
+msgid ""
+"Could not add tool:\n"
+"%s"
+msgstr ""
+"Kunde inte lägga till verktyget:\n"
+"%s"
+
+#: lib/tools_dlg.tcl:187
+msgid "Remove Tool"
+msgstr "Ta bort verktyg"
+
+#: lib/tools_dlg.tcl:193
+msgid "Remove Tool Commands"
+msgstr "Ta bort verktygskommandon"
+
+#: lib/tools_dlg.tcl:198
+msgid "Remove"
+msgstr "Ta bort"
+
+#: lib/tools_dlg.tcl:231
+msgid "(Blue denotes repository-local tools)"
+msgstr "(Blått anger verktyg lokala för arkivet)"
+
+#: lib/tools_dlg.tcl:292
+#, tcl-format
+msgid "Run Command: %s"
+msgstr "Kör kommandot: %s"
+
+#: lib/tools_dlg.tcl:306
+msgid "Arguments"
+msgstr "Argument"
+
+#: lib/tools_dlg.tcl:341
+msgid "OK"
+msgstr "OK"
+
 #: lib/tools.tcl:75
 #, tcl-format
 msgid "Running %s requires a selected file."
@@ -2396,97 +2516,6 @@
 msgid "Tool failed: %s"
 msgstr "Verktyget misslyckades: %s"
 
-#: lib/tools_dlg.tcl:22
-msgid "Add Tool"
-msgstr "Lägg till verktyg"
-
-#: lib/tools_dlg.tcl:28
-msgid "Add New Tool Command"
-msgstr "Lägg till nytt verktygskommando"
-
-#: lib/tools_dlg.tcl:33
-msgid "Add globally"
-msgstr "Lägg till globalt"
-
-#: lib/tools_dlg.tcl:45
-msgid "Tool Details"
-msgstr "Detaljer för verktyg"
-
-#: lib/tools_dlg.tcl:48
-msgid "Use '/' separators to create a submenu tree:"
-msgstr "Använd \"/\"-avdelare för att skapa ett undermenyträd:"
-
-#: lib/tools_dlg.tcl:61
-msgid "Command:"
-msgstr "Kommando:"
-
-#: lib/tools_dlg.tcl:74
-msgid "Show a dialog before running"
-msgstr "Visa dialog innan programmet startas"
-
-#: lib/tools_dlg.tcl:80
-msgid "Ask the user to select a revision (sets $REVISION)"
-msgstr "Be användaren välja en version (sätter $REVISION)"
-
-#: lib/tools_dlg.tcl:85
-msgid "Ask the user for additional arguments (sets $ARGS)"
-msgstr "Be användaren om ytterligare parametrar (sätter $ARGS)"
-
-#: lib/tools_dlg.tcl:92
-msgid "Don't show the command output window"
-msgstr "Visa inte kommandots utdatafönster"
-
-#: lib/tools_dlg.tcl:97
-msgid "Run only if a diff is selected ($FILENAME not empty)"
-msgstr "Kör endast om en diff har markerats ($FILENAME är inte tomt)"
-
-#: lib/tools_dlg.tcl:121
-msgid "Please supply a name for the tool."
-msgstr "Ange ett namn för verktyget."
-
-#: lib/tools_dlg.tcl:129
-#, tcl-format
-msgid "Tool '%s' already exists."
-msgstr "Verktyget \"%s\" finns redan."
-
-#: lib/tools_dlg.tcl:151
-#, tcl-format
-msgid ""
-"Could not add tool:\n"
-"%s"
-msgstr ""
-"Kunde inte lägga till verktyget:\n"
-"%s"
-
-#: lib/tools_dlg.tcl:190
-msgid "Remove Tool"
-msgstr "Ta bort verktyg"
-
-#: lib/tools_dlg.tcl:196
-msgid "Remove Tool Commands"
-msgstr "Ta bort verktygskommandon"
-
-#: lib/tools_dlg.tcl:200
-msgid "Remove"
-msgstr "Ta bort"
-
-#: lib/tools_dlg.tcl:236
-msgid "(Blue denotes repository-local tools)"
-msgstr "(Blått anger verktyg lokala för arkivet)"
-
-#: lib/tools_dlg.tcl:297
-#, tcl-format
-msgid "Run Command: %s"
-msgstr "Kör kommandot: %s"
-
-#: lib/tools_dlg.tcl:311
-msgid "Arguments"
-msgstr "Argument"
-
-#: lib/tools_dlg.tcl:348
-msgid "OK"
-msgstr "OK"
-
 #: lib/transport.tcl:7
 #, tcl-format
 msgid "Fetching new changes from %s"
@@ -2517,11 +2546,11 @@
 msgid "Pushing %s %s to %s"
 msgstr "Sänder %s %s till %s"
 
-#: lib/transport.tcl:100
+#: lib/transport.tcl:102
 msgid "Push Branches"
 msgstr "Sänd grenar"
 
-#: lib/transport.tcl:114
+#: lib/transport.tcl:117
 msgid "Source Branches"
 msgstr "Källgrenar"
 
@@ -2529,22 +2558,31 @@
 msgid "Destination Repository"
 msgstr "Destinationsarkiv"
 
-#: lib/transport.tcl:169
+#: lib/transport.tcl:172
 msgid "Transfer Options"
 msgstr "Överföringsalternativ"
 
-#: lib/transport.tcl:171
+#: lib/transport.tcl:174
 msgid "Force overwrite existing branch (may discard changes)"
 msgstr "Tvinga överskrivning av befintlig gren (kan kasta bort ändringar)"
 
-#: lib/transport.tcl:175
+#: lib/transport.tcl:178
 msgid "Use thin pack (for slow network connections)"
 msgstr "Använd tunt paket (för långsamma nätverksanslutningar)"
 
-#: lib/transport.tcl:179
+#: lib/transport.tcl:182
 msgid "Include tags"
 msgstr "Ta med taggar"
 
+#~ msgid "Cannot use funny .git directory:"
+#~ msgstr "Kan inte använda underlig .git-katalog:"
+
+#~ msgid "Preferences..."
+#~ msgstr "Inställningar..."
+
+#~ msgid "Always (Do not perform merge test.)"
+#~ msgstr "Alltid (utför inte sammanslagningstest)."
+
 #~ msgid "URL:"
 #~ msgstr "Webbadress:"
 
diff --git a/git-gui/windows/git-gui.sh b/git-gui/windows/git-gui.sh
index 66bbb2f..b1845c5 100644
--- a/git-gui/windows/git-gui.sh
+++ b/git-gui/windows/git-gui.sh
@@ -13,10 +13,11 @@
 	incr argc -2
 }
 
-set bindir [file dirname \
+set basedir [file dirname \
             [file dirname \
              [file dirname [info script]]]]
-set bindir [file join $bindir bin]
+set bindir [file join $basedir bin]
+set bindir "$bindir;[file join $basedir mingw bin]"
 regsub -all ";" $bindir "\\;" bindir
 set env(PATH) "$bindir;$env(PATH)"
 unset bindir
diff --git a/git-instaweb.sh b/git-instaweb.sh
index 32f6496..01a1b05 100755
--- a/git-instaweb.sh
+++ b/git-instaweb.sh
@@ -24,8 +24,10 @@
 fqgitdir="$GIT_DIR"
 local="$(git config --bool --get instaweb.local)"
 httpd="$(git config --get instaweb.httpd)"
+root="$(git config --get instaweb.gitwebdir)"
 port=$(git config --get instaweb.port)
 module_path="$(git config --get instaweb.modulepath)"
+action="browse"
 
 conf="$GIT_DIR/gitweb/httpd.conf"
 
@@ -34,18 +36,35 @@
 # if installed, it doesn't need further configuration (module_path)
 test -z "$httpd" && httpd='lighttpd -f'
 
+# Default is @@GITWEBDIR@@
+test -z "$root" && root='@@GITWEBDIR@@'
+
 # any untaken local port will do...
 test -z "$port" && port=1234
 
 resolve_full_httpd () {
 	case "$httpd" in
-	*apache2*|*lighttpd*)
+	*apache2*|*lighttpd*|*httpd*)
+		# yes, *httpd* covers *lighttpd* above, but it is there for clarity
 		# ensure that the apache2/lighttpd command ends with "-f"
-		if ! echo "$httpd" | grep -- '-f *$' >/dev/null 2>&1
+		if ! echo "$httpd" | sane_grep -- '-f *$' >/dev/null 2>&1
 		then
 			httpd="$httpd -f"
 		fi
 		;;
+	*plackup*)
+		# server is started by running via generated gitweb.psgi in $fqgitdir/gitweb
+		full_httpd="$fqgitdir/gitweb/gitweb.psgi"
+		httpd_only="${httpd%% *}" # cut on first space
+		return
+		;;
+	*webrick*)
+		# server is started by running via generated webrick.rb in
+		# $fqgitdir/gitweb
+		full_httpd="$fqgitdir/gitweb/webrick.rb"
+		httpd_only="${httpd%% *}" # cut on first space
+		return
+		;;
 	esac
 
 	httpd_only="$(echo $httpd | cut -f1 -d' ')"
@@ -57,7 +76,7 @@
 		# these days and those are not in most users $PATHs
 		# in addition, we may have generated a server script
 		# in $fqgitdir/gitweb.
-		for i in /usr/local/sbin /usr/sbin "$fqgitdir/gitweb"
+		for i in /usr/local/sbin /usr/sbin "$root" "$fqgitdir/gitweb"
 		do
 			if test -x "$i/$httpd_only"
 			then
@@ -73,36 +92,76 @@
 }
 
 start_httpd () {
+	if test -f "$fqgitdir/pid"; then
+		say "Instance already running. Restarting..."
+		stop_httpd
+	fi
+
 	# here $httpd should have a meaningful value
 	resolve_full_httpd
+	mkdir -p "$fqgitdir/gitweb/$httpd_only"
+	conf="$fqgitdir/gitweb/$httpd_only.conf"
+
+	# generate correct config file if it doesn't exist
+	test -f "$conf" || configure_httpd
+	test -f "$fqgitdir/gitweb/gitweb_config.perl" || gitweb_conf
 
 	# don't quote $full_httpd, there can be arguments to it (-f)
-	$full_httpd "$fqgitdir/gitweb/httpd.conf"
-	if test $? != 0; then
-		echo "Could not execute http daemon $httpd."
-		exit 1
-	fi
+	case "$httpd" in
+	*mongoose*|*plackup*)
+		#These servers don't have a daemon mode so we'll have to fork it
+		$full_httpd "$conf" &
+		#Save the pid before doing anything else (we'll print it later)
+		pid=$!
+
+		if test $? != 0; then
+			echo "Could not execute http daemon $httpd."
+			exit 1
+		fi
+
+		cat > "$fqgitdir/pid" <<EOF
+$pid
+EOF
+		;;
+	*)
+		$full_httpd "$conf"
+		if test $? != 0; then
+			echo "Could not execute http daemon $httpd."
+			exit 1
+		fi
+		;;
+	esac
 }
 
 stop_httpd () {
 	test -f "$fqgitdir/pid" && kill $(cat "$fqgitdir/pid")
+	rm -f "$fqgitdir/pid"
+}
+
+httpd_is_ready () {
+	"$PERL" -MIO::Socket::INET -e "
+local \$| = 1; # turn on autoflush
+exit if (IO::Socket::INET->new('127.0.0.1:$port'));
+print 'Waiting for \'$httpd\' to start ..';
+do {
+	print '.';
+	sleep(1);
+} until (IO::Socket::INET->new('127.0.0.1:$port'));
+print qq! (done)\n!;
+"
 }
 
 while test $# != 0
 do
 	case "$1" in
 	--stop|stop)
-		stop_httpd
-		exit 0
+		action="stop"
 		;;
 	--start|start)
-		start_httpd
-		exit 0
+		action="start"
 		;;
 	--restart|restart)
-		stop_httpd
-		start_httpd
-		exit 0
+		action="restart"
 		;;
 	-l|--local)
 		local=true
@@ -135,60 +194,73 @@
 mkdir -p "$GIT_DIR/gitweb/tmp"
 GIT_EXEC_PATH="$(git --exec-path)"
 GIT_DIR="$fqgitdir"
-export GIT_EXEC_PATH GIT_DIR
-
+GITWEB_CONFIG="$fqgitdir/gitweb/gitweb_config.perl"
+export GIT_EXEC_PATH GIT_DIR GITWEB_CONFIG
 
 webrick_conf () {
+	# webrick seems to have no way of passing arbitrary environment
+	# variables to the underlying CGI executable, so we wrap the
+	# actual gitweb.cgi using a shell script to force it
+  wrapper="$fqgitdir/gitweb/$httpd/wrapper.sh"
+	cat > "$wrapper" <<EOF
+#!/bin/sh
+# we use this shell script wrapper around the real gitweb.cgi since
+# there appears to be no other way to pass arbitrary environment variables
+# into the CGI process
+GIT_EXEC_PATH=$GIT_EXEC_PATH GIT_DIR=$GIT_DIR GITWEB_CONFIG=$GITWEB_CONFIG
+export GIT_EXEC_PATH GIT_DIR GITWEB_CONFIG
+exec $root/gitweb.cgi
+EOF
+	chmod +x "$wrapper"
+
+	# This assumes _ruby_ is in the user's $PATH. that's _one_
+	# portable way to run ruby, which could be installed anywhere, really.
 	# generate a standalone server script in $fqgitdir/gitweb.
 	cat >"$fqgitdir/gitweb/$httpd.rb" <<EOF
+#!/usr/bin/env ruby
 require 'webrick'
-require 'yaml'
-options = YAML::load_file(ARGV[0])
-options[:StartCallback] = proc do
-  File.open(options[:PidFile],"w") do |f|
-    f.puts Process.pid
-  end
-end
-options[:ServerType] = WEBrick::Daemon
+require 'logger'
+options = {
+  :Port => $port,
+  :DocumentRoot => "$root",
+  :Logger => Logger.new('$fqgitdir/gitweb/error.log'),
+  :AccessLog => [
+    [ Logger.new('$fqgitdir/gitweb/access.log'),
+      WEBrick::AccessLog::COMBINED_LOG_FORMAT ]
+  ],
+  :DirectoryIndex => ["gitweb.cgi"],
+  :CGIInterpreter => "$wrapper",
+  :StartCallback => lambda do
+    File.open("$fqgitdir/pid", "w") { |f| f.puts Process.pid }
+  end,
+  :ServerType => WEBrick::Daemon,
+}
+options[:BindAddress] = '127.0.0.1' if "$local" == "true"
 server = WEBrick::HTTPServer.new(options)
 ['INT', 'TERM'].each do |signal|
   trap(signal) {server.shutdown}
 end
 server.start
 EOF
-	# generate a shell script to invoke the above ruby script,
-	# which assumes _ruby_ is in the user's $PATH. that's _one_
-	# portable way to run ruby, which could be installed anywhere,
-	# really.
-	cat >"$fqgitdir/gitweb/$httpd" <<EOF
-#!/bin/sh
-exec ruby "$fqgitdir/gitweb/$httpd.rb" \$*
-EOF
-	chmod +x "$fqgitdir/gitweb/$httpd"
-
-	cat >"$conf" <<EOF
-:Port: $port
-:DocumentRoot: "$fqgitdir/gitweb"
-:DirectoryIndex: ["gitweb.cgi"]
-:PidFile: "$fqgitdir/pid"
-EOF
-	test "$local" = true && echo ':BindAddress: "127.0.0.1"' >> "$conf"
+	chmod +x "$fqgitdir/gitweb/$httpd.rb"
+	# configuration is embedded in server script file, webrick.rb
+	rm -f "$conf"
 }
 
 lighttpd_conf () {
 	cat > "$conf" <<EOF
-server.document-root = "$fqgitdir/gitweb"
+server.document-root = "$root"
 server.port = $port
 server.modules = ( "mod_setenv", "mod_cgi" )
 server.indexfiles = ( "gitweb.cgi" )
 server.pid-file = "$fqgitdir/pid"
-server.errorlog = "$fqgitdir/gitweb/error.log"
+server.errorlog = "$fqgitdir/gitweb/$httpd_only/error.log"
 
 # to enable, add "mod_access", "mod_accesslog" to server.modules
 # variable above and uncomment this
-#accesslog.filename = "$fqgitdir/gitweb/access.log"
+#accesslog.filename = "$fqgitdir/gitweb/$httpd_only/access.log"
 
-setenv.add-environment = ( "PATH" => "/usr/local/bin:/usr/bin:/bin" )
+setenv.add-environment = ( "PATH" => env.PATH, "GITWEB_CONFIG" => env.GITWEB_CONFIG )
 
 cgi.assign = ( ".cgi" => "" )
 
@@ -252,21 +324,30 @@
 }
 
 apache2_conf () {
-	test -z "$module_path" && module_path=/usr/lib/apache2/modules
-	mkdir -p "$GIT_DIR/gitweb/logs"
+	if test -z "$module_path"
+	then
+		test -d "/usr/lib/httpd/modules" &&
+			module_path="/usr/lib/httpd/modules"
+		test -d "/usr/lib/apache2/modules" &&
+			module_path="/usr/lib/apache2/modules"
+	fi
 	bind=
 	test x"$local" = xtrue && bind='127.0.0.1:'
 	echo 'text/css css' > "$fqgitdir/mime.types"
 	cat > "$conf" <<EOF
 ServerName "git-instaweb"
-ServerRoot "$fqgitdir/gitweb"
-DocumentRoot "$fqgitdir/gitweb"
+ServerRoot "$root"
+DocumentRoot "$root"
+ErrorLog "$fqgitdir/gitweb/$httpd_only/error.log"
+CustomLog "$fqgitdir/gitweb/$httpd_only/access.log" combined
 PidFile "$fqgitdir/pid"
 Listen $bind$port
 EOF
 
-	for mod in mime dir; do
-		if test -e $module_path/mod_${mod}.so; then
+	for mod in mime dir env log_config
+	do
+		if test -e $module_path/mod_${mod}.so
+		then
 			echo "LoadModule ${mod}_module " \
 			     "$module_path/mod_${mod}.so" >> "$conf"
 		fi
@@ -278,14 +359,15 @@
 
 	# 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
+	if test -f "$module_path/mod_perl.so" &&
+	   sane_grep 'MOD_PERL' "$root/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
+PerlPassEnv GIT_EXEC_PATH
+PerlPassEnv GITWEB_CONFIG
 <Location /gitweb.cgi>
 	SetHandler perl-script
 	PerlResponseHandler ModPerl::Registry
@@ -296,10 +378,27 @@
 	else
 		# plain-old CGI
 		resolve_full_httpd
-		list_mods=$(echo "$full_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"
+		list_mods=$(echo "$full_httpd" | sed 's/-f$/-l/')
+		$list_mods | sane_grep 'mod_cgi\.c' >/dev/null 2>&1 || \
+		if test -f "$module_path/mod_cgi.so"
+		then
+			echo "LoadModule cgi_module $module_path/mod_cgi.so" >> "$conf"
+		else
+			$list_mods | grep 'mod_cgid\.c' >/dev/null 2>&1 || \
+			if test -f "$module_path/mod_cgid.so"
+			then
+				echo "LoadModule cgid_module $module_path/mod_cgid.so" \
+					>> "$conf"
+			else
+				echo "You have no CGI support!"
+				exit 2
+			fi
+			echo "ScriptSock logs/gitweb.sock" >> "$conf"
+		fi
 		cat >> "$conf" <<EOF
+PassEnv GIT_DIR
+PassEnv GIT_EXEC_PATH
+PassEnv GITWEB_CONFIG
 AddHandler cgi-script .cgi
 <Location /gitweb.cgi>
 	Options +ExecCGI
@@ -308,53 +407,242 @@
 	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'";#;'
+mongoose_conf() {
+	cat > "$conf" <<EOF
+# Mongoose web server configuration file.
+# Lines starting with '#' and empty lines are ignored.
+# For detailed description of every option, visit
+# http://code.google.com/p/mongoose/wiki/MongooseManual
 
-gitweb_cgi () {
-	cat > "$1.tmp" <<\EOFGITWEB
-@@GITWEB_CGI@@
-EOFGITWEB
-	# Use the configured full path to perl to match the generated
-	# scripts' 'hashpling' line
-	"$PERL" -p -e "$script" "$1.tmp"  > "$1"
-	chmod +x "$1"
-	rm -f "$1.tmp"
+root		$root
+ports		$port
+index_files	gitweb.cgi
+#ssl_cert	$fqgitdir/gitweb/ssl_cert.pem
+error_log	$fqgitdir/gitweb/$httpd_only/error.log
+access_log	$fqgitdir/gitweb/$httpd_only/access.log
+
+#cgi setup
+cgi_env		PATH=$PATH,GIT_DIR=$GIT_DIR,GIT_EXEC_PATH=$GIT_EXEC_PATH,GITWEB_CONFIG=$GITWEB_CONFIG
+cgi_interp	$PERL
+cgi_ext		cgi,pl
+
+# mimetype mapping
+mime_types	.gz=application/x-gzip,.tar.gz=application/x-tgz,.tgz=application/x-tgz,.tar=application/x-tar,.zip=application/zip,.gif=image/gif,.jpg=image/jpeg,.jpeg=image/jpeg,.png=image/png,.css=text/css,.html=text/html,.htm=text/html,.js=text/javascript,.c=text/plain,.cpp=text/plain,.log=text/plain,.conf=text/plain,.text=text/plain,.txt=text/plain,.dtd=text/xml,.bz2=application/x-bzip,.tbz=application/x-bzip-compressed-tar,.tar.bz2=application/x-bzip-compressed-tar
+EOF
 }
 
-gitweb_css () {
-	cat > "$1" <<\EOFGITWEB
-@@GITWEB_CSS@@
-EOFGITWEB
+plackup_conf () {
+	# generate a standalone 'plackup' server script in $fqgitdir/gitweb
+	# with embedded configuration; it does not use "$conf" file
+	cat > "$fqgitdir/gitweb/gitweb.psgi" <<EOF
+#!$PERL
+
+# gitweb - simple web interface to track changes in git repositories
+#          PSGI wrapper and server starter (see http://plackperl.org)
+
+use strict;
+
+use IO::Handle;
+use Plack::MIME;
+use Plack::Builder;
+use Plack::App::WrapCGI;
+use CGI::Emulate::PSGI 0.07; # minimum version required to work with gitweb
+
+# mimetype mapping (from lighttpd_conf)
+Plack::MIME->add_type(
+	".pdf"          =>      "application/pdf",
+	".sig"          =>      "application/pgp-signature",
+	".spl"          =>      "application/futuresplash",
+	".class"        =>      "application/octet-stream",
+	".ps"           =>      "application/postscript",
+	".torrent"      =>      "application/x-bittorrent",
+	".dvi"          =>      "application/x-dvi",
+	".gz"           =>      "application/x-gzip",
+	".pac"          =>      "application/x-ns-proxy-autoconfig",
+	".swf"          =>      "application/x-shockwave-flash",
+	".tar.gz"       =>      "application/x-tgz",
+	".tgz"          =>      "application/x-tgz",
+	".tar"          =>      "application/x-tar",
+	".zip"          =>      "application/zip",
+	".mp3"          =>      "audio/mpeg",
+	".m3u"          =>      "audio/x-mpegurl",
+	".wma"          =>      "audio/x-ms-wma",
+	".wax"          =>      "audio/x-ms-wax",
+	".ogg"          =>      "application/ogg",
+	".wav"          =>      "audio/x-wav",
+	".gif"          =>      "image/gif",
+	".jpg"          =>      "image/jpeg",
+	".jpeg"         =>      "image/jpeg",
+	".png"          =>      "image/png",
+	".xbm"          =>      "image/x-xbitmap",
+	".xpm"          =>      "image/x-xpixmap",
+	".xwd"          =>      "image/x-xwindowdump",
+	".css"          =>      "text/css",
+	".html"         =>      "text/html",
+	".htm"          =>      "text/html",
+	".js"           =>      "text/javascript",
+	".asc"          =>      "text/plain",
+	".c"            =>      "text/plain",
+	".cpp"          =>      "text/plain",
+	".log"          =>      "text/plain",
+	".conf"         =>      "text/plain",
+	".text"         =>      "text/plain",
+	".txt"          =>      "text/plain",
+	".dtd"          =>      "text/xml",
+	".xml"          =>      "text/xml",
+	".mpeg"         =>      "video/mpeg",
+	".mpg"          =>      "video/mpeg",
+	".mov"          =>      "video/quicktime",
+	".qt"           =>      "video/quicktime",
+	".avi"          =>      "video/x-msvideo",
+	".asf"          =>      "video/x-ms-asf",
+	".asx"          =>      "video/x-ms-asf",
+	".wmv"          =>      "video/x-ms-wmv",
+	".bz2"          =>      "application/x-bzip",
+	".tbz"          =>      "application/x-bzip-compressed-tar",
+	".tar.bz2"      =>      "application/x-bzip-compressed-tar",
+	""              =>      "text/plain"
+);
+
+my \$app = builder {
+	# to be able to override \$SIG{__WARN__} to log build time warnings
+	use CGI::Carp; # it sets \$SIG{__WARN__} itself
+
+	my \$logdir = "$fqgitdir/gitweb/$httpd_only";
+	open my \$access_log_fh, '>>', "\$logdir/access.log"
+		or die "Couldn't open access log '\$logdir/access.log': \$!";
+	open my \$error_log_fh,  '>>', "\$logdir/error.log"
+		or die "Couldn't open error log '\$logdir/error.log': \$!";
+
+	\$access_log_fh->autoflush(1);
+	\$error_log_fh->autoflush(1);
+
+	# redirect build time warnings to error.log
+	\$SIG{'__WARN__'} = sub {
+		my \$msg = shift;
+		# timestamp warning like in CGI::Carp::warn
+		my \$stamp = CGI::Carp::stamp();
+		\$msg =~ s/^/\$stamp/gm;
+		print \$error_log_fh \$msg;
+	};
+
+	# write errors to error.log, access to access.log
+	enable 'AccessLog',
+		format => "combined",
+		logger => sub { print \$access_log_fh @_; };
+	enable sub {
+		my \$app = shift;
+		sub {
+			my \$env = shift;
+			\$env->{'psgi.errors'} = \$error_log_fh;
+			\$app->(\$env);
+		}
+	};
+	# gitweb currently doesn't work with $SIG{CHLD} set to 'IGNORE',
+	# because it uses 'close $fd or die...' on piped filehandle $fh
+	# (which causes the parent process to wait for child to finish).
+	enable_if { \$SIG{'CHLD'} eq 'IGNORE' } sub {
+		my \$app = shift;
+		sub {
+			my \$env = shift;
+			local \$SIG{'CHLD'} = 'DEFAULT';
+			local \$SIG{'CLD'}  = 'DEFAULT';
+			\$app->(\$env);
+		}
+	};
+	# serve static files, i.e. stylesheet, images, script
+	enable 'Static',
+		path => sub { m!\.(js|css|png)\$! && s!^/gitweb/!! },
+		root => "$root/",
+		encoding => 'utf-8'; # encoding for 'text/plain' files
+	# convert CGI application to PSGI app
+	Plack::App::WrapCGI->new(script => "$root/gitweb.cgi")->to_app;
+};
+
+# make it runnable as standalone app,
+# like it would be run via 'plackup' utility
+if (caller) {
+	return \$app;
+} else {
+	require Plack::Runner;
+
+	my \$runner = Plack::Runner->new();
+	\$runner->parse_options(qw(--env deployment --port $port),
+				"$local" ? qw(--host 127.0.0.1) : ());
+	\$runner->run(\$app);
+}
+__END__
+EOF
+
+	chmod a+x "$fqgitdir/gitweb/gitweb.psgi"
+	# configuration is embedded in server script file, gitweb.psgi
+	rm -f "$conf"
 }
 
-gitweb_cgi "$GIT_DIR/gitweb/gitweb.cgi"
-gitweb_css "$GIT_DIR/gitweb/gitweb.css"
+gitweb_conf() {
+	cat > "$fqgitdir/gitweb/gitweb_config.perl" <<EOF
+#!/usr/bin/perl
+our \$projectroot = "$(dirname "$fqgitdir")";
+our \$git_temp = "$fqgitdir/gitweb/tmp";
+our \$projects_list = \$projectroot;
 
-case "$httpd" in
-*lighttpd*)
-	lighttpd_conf
+\$feature{'remote_heads'}{'default'} = [1];
+EOF
+}
+
+configure_httpd() {
+	case "$httpd" in
+	*lighttpd*)
+		lighttpd_conf
+		;;
+	*apache2*|*httpd*)
+		apache2_conf
+		;;
+	webrick)
+		webrick_conf
+		;;
+	*mongoose*)
+		mongoose_conf
+		;;
+	*plackup*)
+		plackup_conf
+		;;
+	*)
+		echo "Unknown httpd specified: $httpd"
+		exit 1
+		;;
+	esac
+}
+
+case "$action" in
+stop)
+	stop_httpd
+	exit 0
 	;;
-*apache2*)
-	apache2_conf
+start)
+	start_httpd
+	exit 0
 	;;
-webrick)
-	webrick_conf
-	;;
-*)
-	echo "Unknown httpd specified: $httpd"
-	exit 1
+restart)
+	stop_httpd
+	start_httpd
+	exit 0
 	;;
 esac
 
+gitweb_conf
+
+resolve_full_httpd
+mkdir -p "$fqgitdir/gitweb/$httpd_only"
+conf="$fqgitdir/gitweb/$httpd_only.conf"
+
+configure_httpd
+
 start_httpd
 url=http://127.0.0.1:$port
 
 if test -n "$browser"; then
-	git web--browse -b "$browser" $url || echo $url
+	httpd_is_ready && git web--browse -b "$browser" $url || echo $url
 else
-	git web--browse -c "instaweb.browser" $url || echo $url
+	httpd_is_ready && git web--browse -c "instaweb.browser" $url || echo $url
 fi
diff --git a/git-merge-octopus.sh b/git-merge-octopus.sh
index 1dadbb4..8643f74 100755
--- a/git-merge-octopus.sh
+++ b/git-merge-octopus.sh
@@ -44,9 +44,8 @@
 # MRC is the current "merge reference commit"
 # MRT is the current "merge result tree"
 
-MRC=$head MSG= PARENT="-p $head"
+MRC=$(git rev-parse --verify -q $head)
 MRT=$(git write-tree)
-CNT=1 ;# counting our head
 NON_FF_MERGE=0
 OCTOPUS_FAILURE=0
 for SHA1 in $remotes
@@ -61,19 +60,22 @@
 		exit 2
 	esac
 
+	eval pretty_name=\${GITHEAD_$SHA1:-$SHA1}
+	if test "$SHA1" = "$pretty_name"
+	then
+		SHA1_UP="$(echo "$SHA1" | tr a-z A-Z)"
+		eval pretty_name=\${GITHEAD_$SHA1_UP:-$pretty_name}
+	fi
 	common=$(git merge-base --all $SHA1 $MRC) ||
-		die "Unable to find common commit with $SHA1"
+		die "Unable to find common commit with $pretty_name"
 
 	case "$LF$common$LF" in
 	*"$LF$SHA1$LF"*)
-		echo "Already up-to-date with $SHA1"
+		echo "Already up-to-date with $pretty_name"
 		continue
 		;;
 	esac
 
-	CNT=`expr $CNT + 1`
-	PARENT="$PARENT -p $SHA1"
-
 	if test "$common,$NON_FF_MERGE" = "$MRC,0"
 	then
 		# The first head being merged was a fast-forward.
@@ -81,7 +83,7 @@
 		# tree as the intermediate result of the merge.
 		# We still need to count this as part of the parent set.
 
-		echo "Fast forwarding to: $SHA1"
+		echo "Fast-forwarding to: $pretty_name"
 		git read-tree -u -m $head $SHA1 || exit
 		MRC=$SHA1 MRT=$(git write-tree)
 		continue
@@ -89,7 +91,7 @@
 
 	NON_FF_MERGE=1
 
-	echo "Trying simple merge with $SHA1"
+	echo "Trying simple merge with $pretty_name"
 	git read-tree -u -m --aggressive  $common $MRT $SHA1 || exit 2
 	next=$(git write-tree 2>/dev/null)
 	if test $? -ne 0
diff --git a/git-merge-one-file.sh b/git-merge-one-file.sh
index 9c2c1b7..f612cb8 100755
--- a/git-merge-one-file.sh
+++ b/git-merge-one-file.sh
@@ -16,6 +16,23 @@
 # been handled already by git read-tree, but that one doesn't
 # do any merges that might change the tree layout.
 
+USAGE='<orig blob> <our blob> <their blob> <path>'
+USAGE="$USAGE <orig mode> <our mode> <their mode>"
+LONG_USAGE="Usage: git merge-one-file $USAGE
+
+Blob ids and modes should be empty for missing files."
+
+SUBDIRECTORY_OK=Yes
+. git-sh-setup
+cd_to_toplevel
+require_work_tree
+
+if ! test "$#" -eq 7
+then
+	echo "$LONG_USAGE"
+	exit 1
+fi
+
 case "${1:-.}${2:-.}${3:-.}" in
 #
 # Deleted in both or deleted in one and unchanged in the other
@@ -95,12 +112,12 @@
 		# remove lines that are unique to ours.
 		orig=`git-unpack-file $2`
 		sz0=`wc -c <"$orig"`
-		diff -u -La/$orig -Lb/$orig $orig $src2 | git apply --no-add
+		@@DIFF@@ -u -La/$orig -Lb/$orig $orig $src2 | git apply --no-add
 		sz1=`wc -c <"$orig"`
 
 		# If we do not have enough common material, it is not
 		# worth trying two-file merge using common subsections.
-		expr "$sz0" \< "$sz1" \* 2 >/dev/null || : >$orig
+		expr $sz0 \< $sz1 \* 2 >/dev/null || : >$orig
 		;;
 	*)
 		echo "Auto-merging $4"
@@ -120,7 +137,7 @@
 
 	# Create the working tree file, using "our tree" version from the
 	# index, and then store the result of the merge.
-	git checkout-index -f --stage=2 -- "$4" && cat "$src1" >"$4"
+	git checkout-index -f --stage=2 -- "$4" && cat "$src1" >"$4" || exit 1
 	rm -f -- "$orig" "$src1" "$src2"
 
 	if [ "$6" != "$7" ]; then
diff --git a/git-mergetool--lib.sh b/git-mergetool--lib.sh
index bfb01f7..ed630b2 100644
--- a/git-mergetool--lib.sh
+++ b/git-mergetool--lib.sh
@@ -1,3 +1,4 @@
+#!/bin/sh
 # git-mergetool--lib is a library for common merge tool functions
 diff_mode() {
 	test "$TOOL_MODE" = diff
@@ -8,33 +9,19 @@
 }
 
 translate_merge_tool_path () {
-	case "$1" in
-	vimdiff)
-		echo vim
-		;;
-	gvimdiff)
-		echo gvim
-		;;
-	emerge)
-		echo emacs
-		;;
-	araxis)
-		echo compare
-		;;
-	*)
-		echo "$1"
-		;;
-	esac
+	echo "$1"
 }
 
 check_unchanged () {
-	if test "$MERGED" -nt "$BACKUP"; then
+	if test "$MERGED" -nt "$BACKUP"
+	then
 		status=0
 	else
-		while true; do
+		while true
+		do
 			echo "$MERGED seems unchanged."
 			printf "Was the merge successful? [y/n] "
-			read answer < /dev/tty
+			read answer || return 1
 			case "$answer" in
 			y*|Y*) status=0; break ;;
 			n*|N*) status=1; break ;;
@@ -43,304 +30,120 @@
 	fi
 }
 
+valid_tool_config () {
+	if test -n "$(get_merge_tool_cmd "$1")"
+	then
+		return 0
+	else
+		return 1
+	fi
+}
+
 valid_tool () {
+	setup_tool "$1" || valid_tool_config "$1"
+}
+
+setup_tool () {
 	case "$1" in
-	kdiff3 | tkdiff | xxdiff | meld | opendiff | \
-	emerge | vimdiff | gvimdiff | ecmerge | diffuse | araxis)
-		;; # happy
-	tortoisemerge)
-		if ! merge_mode; then
-			return 1
-		fi
-		;;
-	kompare)
-		if ! diff_mode; then
-			return 1
-		fi
+	vim*|gvim*)
+		tool=vim
 		;;
 	*)
-		if test -z "$(get_merge_tool_cmd "$1")"; then
-			return 1
-		fi
+		tool="$1"
 		;;
 	esac
+	mergetools="$(git --exec-path)/mergetools"
+
+	# Load the default definitions
+	. "$mergetools/defaults"
+	if ! test -f "$mergetools/$tool"
+	then
+		return 1
+	fi
+
+	# Load the redefined functions
+	. "$mergetools/$tool"
+
+	if merge_mode && ! can_merge
+	then
+		echo "error: '$tool' can not be used to resolve merges" >&2
+		exit 1
+	elif diff_mode && ! can_diff
+	then
+		echo "error: '$tool' can only be used to resolve merges" >&2
+		exit 1
+	fi
+	return 0
 }
 
 get_merge_tool_cmd () {
 	# Prints the custom command for a merge tool
-	if test -n "$1"; then
-		merge_tool="$1"
-	else
-		merge_tool="$(get_merge_tool)"
-	fi
-	if diff_mode; then
+	merge_tool="$1"
+	if diff_mode
+	then
 		echo "$(git config difftool.$merge_tool.cmd ||
-		        git config mergetool.$merge_tool.cmd)"
+			git config mergetool.$merge_tool.cmd)"
 	else
 		echo "$(git config mergetool.$merge_tool.cmd)"
 	fi
 }
 
+# Entry point for running tools
 run_merge_tool () {
+	# If GIT_PREFIX is empty then we cannot use it in tools
+	# that expect to be able to chdir() to its value.
+	GIT_PREFIX=${GIT_PREFIX:-.}
+	export GIT_PREFIX
+
 	merge_tool_path="$(get_merge_tool_path "$1")" || exit
 	base_present="$2"
 	status=0
 
-	case "$1" in
-	kdiff3)
-		if merge_mode; then
-			if $base_present; then
-				("$merge_tool_path" --auto \
-					--L1 "$MERGED (Base)" \
-					--L2 "$MERGED (Local)" \
-					--L3 "$MERGED (Remote)" \
-					-o "$MERGED" \
-					"$BASE" "$LOCAL" "$REMOTE" \
-				> /dev/null 2>&1)
-			else
-				("$merge_tool_path" --auto \
-					--L1 "$MERGED (Local)" \
-					--L2 "$MERGED (Remote)" \
-					-o "$MERGED" \
-					"$LOCAL" "$REMOTE" \
-				> /dev/null 2>&1)
-			fi
-			status=$?
-		else
-			("$merge_tool_path" --auto \
-				--L1 "$MERGED (A)" \
-				--L2 "$MERGED (B)" "$LOCAL" "$REMOTE" \
-			> /dev/null 2>&1)
-		fi
-		;;
-	kompare)
-		"$merge_tool_path" "$LOCAL" "$REMOTE"
-		;;
-	tkdiff)
-		if merge_mode; then
-			if $base_present; then
-				"$merge_tool_path" -a "$BASE" \
-					-o "$MERGED" "$LOCAL" "$REMOTE"
-			else
-				"$merge_tool_path" \
-					-o "$MERGED" "$LOCAL" "$REMOTE"
-			fi
-			status=$?
-		else
-			"$merge_tool_path" "$LOCAL" "$REMOTE"
-		fi
-		;;
-	meld)
-		if merge_mode; then
-			touch "$BACKUP"
-			"$merge_tool_path" "$LOCAL" "$MERGED" "$REMOTE"
-			check_unchanged
-		else
-			"$merge_tool_path" "$LOCAL" "$REMOTE"
-		fi
-		;;
-	diffuse)
-		if merge_mode; then
-			touch "$BACKUP"
-			if $base_present; then
-				"$merge_tool_path" \
-					"$LOCAL" "$MERGED" "$REMOTE" \
-					"$BASE" | cat
-			else
-				"$merge_tool_path" \
-					"$LOCAL" "$MERGED" "$REMOTE" | cat
-			fi
-			check_unchanged
-		else
-			"$merge_tool_path" "$LOCAL" "$REMOTE" | cat
-		fi
-		;;
-	vimdiff)
-		if merge_mode; then
-			touch "$BACKUP"
-			"$merge_tool_path" -d -c "wincmd l" \
-				"$LOCAL" "$MERGED" "$REMOTE"
-			check_unchanged
-		else
-			"$merge_tool_path" -d -c "wincmd l" \
-				"$LOCAL" "$REMOTE"
-		fi
-		;;
-	gvimdiff)
-		if merge_mode; then
-			touch "$BACKUP"
-			"$merge_tool_path" -d -c "wincmd l" -f \
-				"$LOCAL" "$MERGED" "$REMOTE"
-			check_unchanged
-		else
-			"$merge_tool_path" -d -c "wincmd l" -f \
-				"$LOCAL" "$REMOTE"
-		fi
-		;;
-	xxdiff)
-		if merge_mode; then
-			touch "$BACKUP"
-			if $base_present; then
-				"$merge_tool_path" -X --show-merged-pane \
-					-R 'Accel.SaveAsMerged: "Ctrl-S"' \
-					-R 'Accel.Search: "Ctrl+F"' \
-					-R 'Accel.SearchForward: "Ctrl-G"' \
-					--merged-file "$MERGED" \
-					"$LOCAL" "$BASE" "$REMOTE"
-			else
-				"$merge_tool_path" -X $extra \
-					-R 'Accel.SaveAsMerged: "Ctrl-S"' \
-					-R 'Accel.Search: "Ctrl+F"' \
-					-R 'Accel.SearchForward: "Ctrl-G"' \
-					--merged-file "$MERGED" \
-					"$LOCAL" "$REMOTE"
-			fi
-			check_unchanged
-		else
-			"$merge_tool_path" \
-				-R 'Accel.Search: "Ctrl+F"' \
-				-R 'Accel.SearchForward: "Ctrl-G"' \
-				"$LOCAL" "$REMOTE"
-		fi
-		;;
-	opendiff)
-		if merge_mode; then
-			touch "$BACKUP"
-			if $base_present; then
-				"$merge_tool_path" "$LOCAL" "$REMOTE" \
-					-ancestor "$BASE" \
-					-merge "$MERGED" | cat
-			else
-				"$merge_tool_path" "$LOCAL" "$REMOTE" \
-					-merge "$MERGED" | cat
-			fi
-			check_unchanged
-		else
-			"$merge_tool_path" "$LOCAL" "$REMOTE" | cat
-		fi
-		;;
-	ecmerge)
-		if merge_mode; then
-			touch "$BACKUP"
-			if $base_present; then
-				"$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" \
-					--default --mode=merge3 --to="$MERGED"
-			else
-				"$merge_tool_path" "$LOCAL" "$REMOTE" \
-					--default --mode=merge2 --to="$MERGED"
-			fi
-			check_unchanged
-		else
-			"$merge_tool_path" --default --mode=diff2 \
-				"$LOCAL" "$REMOTE"
-		fi
-		;;
-	emerge)
-		if merge_mode; then
-			if $base_present; then
-				"$merge_tool_path" \
-					-f emerge-files-with-ancestor-command \
-					"$LOCAL" "$REMOTE" "$BASE" \
-					"$(basename "$MERGED")"
-			else
-				"$merge_tool_path" \
-					-f emerge-files-command \
-					"$LOCAL" "$REMOTE" \
-					"$(basename "$MERGED")"
-			fi
-			status=$?
-		else
-			"$merge_tool_path" -f emerge-files-command \
-				"$LOCAL" "$REMOTE"
-		fi
-		;;
-	tortoisemerge)
-		if $base_present; then
-			touch "$BACKUP"
-			"$merge_tool_path" \
-				-base:"$BASE" -mine:"$LOCAL" \
-				-theirs:"$REMOTE" -merged:"$MERGED"
-			check_unchanged
-		else
-			echo "TortoiseMerge cannot be used without a base" 1>&2
-			status=1
-		fi
-		;;
-	araxis)
-		if merge_mode; then
-			touch "$BACKUP"
-			if $base_present; then
-				"$merge_tool_path" -wait -merge -3 -a1 \
-					"$BASE" "$LOCAL" "$REMOTE" "$MERGED" \
-					>/dev/null 2>&1
-			else
-				"$merge_tool_path" -wait -2 \
-					"$LOCAL" "$REMOTE" "$MERGED" \
-					>/dev/null 2>&1
-			fi
-			check_unchanged
-		else
-			"$merge_tool_path" -wait -2 "$LOCAL" "$REMOTE" \
-				>/dev/null 2>&1
-		fi
-		;;
-	*)
-		merge_tool_cmd="$(get_merge_tool_cmd "$1")"
-		if test -z "$merge_tool_cmd"; then
-			if merge_mode; then
-				status=1
-			fi
-			break
-		fi
-		if merge_mode; then
-			trust_exit_code="$(git config --bool \
-				mergetool."$1".trustExitCode || echo false)"
-			if test "$trust_exit_code" = "false"; then
-				touch "$BACKUP"
-				( eval $merge_tool_cmd )
-				check_unchanged
-			else
-				( eval $merge_tool_cmd )
-				status=$?
-			fi
-		else
-			( eval $merge_tool_cmd )
-		fi
-		;;
-	esac
+	# Bring tool-specific functions into scope
+	setup_tool "$1"
+
+	if merge_mode
+	then
+		merge_cmd "$1"
+	else
+		diff_cmd "$1"
+	fi
 	return $status
 }
 
 guess_merge_tool () {
-	if merge_mode; then
+	if merge_mode
+	then
 		tools="tortoisemerge"
 	else
 		tools="kompare"
 	fi
-	if test -n "$DISPLAY"; then
-		if test -n "$GNOME_DESKTOP_SESSION_ID" ; then
+	if test -n "$DISPLAY"
+	then
+		if test -n "$GNOME_DESKTOP_SESSION_ID"
+		then
 			tools="meld opendiff kdiff3 tkdiff xxdiff $tools"
 		else
 			tools="opendiff kdiff3 tkdiff xxdiff meld $tools"
 		fi
-		tools="$tools gvimdiff diffuse ecmerge araxis"
+		tools="$tools gvimdiff diffuse ecmerge p4merge araxis bc3"
 	fi
-	if echo "${VISUAL:-$EDITOR}" | grep emacs > /dev/null 2>&1; then
-		# $EDITOR is emacs so add emerge as a candidate
-		tools="$tools emerge vimdiff"
-	elif echo "${VISUAL:-$EDITOR}" | grep vim > /dev/null 2>&1; then
-		# $EDITOR is vim so add vimdiff as a candidate
+	case "${VISUAL:-$EDITOR}" in
+	*vim*)
 		tools="$tools vimdiff emerge"
-	else
+		;;
+	*)
 		tools="$tools emerge vimdiff"
-	fi
+		;;
+	esac
 	echo >&2 "merge tool candidates: $tools"
 
 	# Loop over each candidate and stop when a valid merge tool is found.
 	for i in $tools
 	do
 		merge_tool_path="$(translate_merge_tool_path "$i")"
-		if type "$merge_tool_path" > /dev/null 2>&1; then
+		if type "$merge_tool_path" >/dev/null 2>&1
+		then
 			echo "$i"
 			return 0
 		fi
@@ -353,12 +156,14 @@
 get_configured_merge_tool () {
 	# Diff mode first tries diff.tool and falls back to merge.tool.
 	# Merge mode only checks merge.tool
-	if diff_mode; then
+	if diff_mode
+	then
 		merge_tool=$(git config diff.tool || git config merge.tool)
 	else
 		merge_tool=$(git config merge.tool)
 	fi
-	if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then
+	if test -n "$merge_tool" && ! valid_tool "$merge_tool"
+	then
 		echo >&2 "git config option $TOOL_MODE.tool set to unknown tool: $merge_tool"
 		echo >&2 "Resetting to default..."
 		return 1
@@ -368,28 +173,28 @@
 
 get_merge_tool_path () {
 	# A merge tool has been set, so verify that it's valid.
-	if test -n "$1"; then
-		merge_tool="$1"
-	else
-		merge_tool="$(get_merge_tool)"
-	fi
-	if ! valid_tool "$merge_tool"; then
+	merge_tool="$1"
+	if ! valid_tool "$merge_tool"
+	then
 		echo >&2 "Unknown merge tool $merge_tool"
 		exit 1
 	fi
-	if diff_mode; then
+	if diff_mode
+	then
 		merge_tool_path=$(git config difftool."$merge_tool".path ||
-		                  git config mergetool."$merge_tool".path)
+				  git config mergetool."$merge_tool".path)
 	else
 		merge_tool_path=$(git config mergetool."$merge_tool".path)
 	fi
-	if test -z "$merge_tool_path"; then
+	if test -z "$merge_tool_path"
+	then
 		merge_tool_path="$(translate_merge_tool_path "$merge_tool")"
 	fi
 	if test -z "$(get_merge_tool_cmd "$merge_tool")" &&
-	! type "$merge_tool_path" > /dev/null 2>&1; then
+		! type "$merge_tool_path" >/dev/null 2>&1
+	then
 		echo >&2 "The $TOOL_MODE tool $merge_tool is not available as"\
-		         "'$merge_tool_path'"
+			 "'$merge_tool_path'"
 		exit 1
 	fi
 	echo "$merge_tool_path"
@@ -397,9 +202,10 @@
 
 get_merge_tool () {
 	# Check if a merge tool has been configured
-	merge_tool=$(get_configured_merge_tool)
+	merge_tool="$(get_configured_merge_tool)"
 	# Try to guess an appropriate merge tool if no tool has been set.
-	if test -z "$merge_tool"; then
+	if test -z "$merge_tool"
+	then
 		merge_tool="$(guess_merge_tool)" || exit
 	fi
 	echo "$merge_tool"
diff --git a/git-mergetool.sh b/git-mergetool.sh
index b52a741..a9f23f7 100755
--- a/git-mergetool.sh
+++ b/git-mergetool.sh
@@ -21,6 +21,10 @@
     test "$1" = 120000
 }
 
+is_submodule () {
+    test "$1" = 160000
+}
+
 local_present () {
     test -n "$local_mode"
 }
@@ -35,7 +39,8 @@
 
 cleanup_temp_files () {
     if test "$1" = --save-backup ; then
-	mv -- "$BACKUP" "$MERGED.orig"
+	rm -rf -- "$MERGED.orig"
+	test -e "$BACKUP" && mv -- "$BACKUP" "$MERGED.orig"
 	rm -f -- "$LOCAL" "$REMOTE" "$BASE"
     else
 	rm -f -- "$LOCAL" "$REMOTE" "$BASE" "$BACKUP"
@@ -52,11 +57,13 @@
 	echo "deleted"
     elif is_symlink "$mode" ; then
 	echo "a symbolic link -> '$(cat "$file")'"
+    elif is_submodule "$mode" ; then
+	echo "submodule commit $file"
     else
 	if base_present; then
-	    echo "modified"
+	    echo "modified file"
 	else
-	    echo "created"
+	    echo "created file"
 	fi
     fi
 }
@@ -65,7 +72,7 @@
 resolve_symlink_merge () {
     while true; do
 	printf "Use (l)ocal or (r)emote, or (a)bort? "
-	read ans
+	read ans || return 1
 	case "$ans" in
 	    [lL]*)
 		git checkout-index -f --stage=2 -- "$MERGED"
@@ -93,7 +100,7 @@
 	else
 	    printf "Use (c)reated or (d)eleted file, or (a)bort? "
 	fi
-	read ans
+	read ans || return 1
 	case "$ans" in
 	    [mMcC]*)
 		git add -- "$MERGED"
@@ -112,11 +119,76 @@
 	done
 }
 
+resolve_submodule_merge () {
+    while true; do
+	printf "Use (l)ocal or (r)emote, or (a)bort? "
+	read ans || return 1
+	case "$ans" in
+	    [lL]*)
+		if ! local_present; then
+		    if test -n "$(git ls-tree HEAD -- "$MERGED")"; then
+			# Local isn't present, but it's a subdirectory
+			git ls-tree --full-name -r HEAD -- "$MERGED" | git update-index --index-info || exit $?
+		    else
+			test -e "$MERGED" && mv -- "$MERGED" "$BACKUP"
+			git update-index --force-remove "$MERGED"
+			cleanup_temp_files --save-backup
+		    fi
+		elif is_submodule "$local_mode"; then
+		    stage_submodule "$MERGED" "$local_sha1"
+		else
+		    git checkout-index -f --stage=2 -- "$MERGED"
+		    git add -- "$MERGED"
+		fi
+		return 0
+		;;
+	    [rR]*)
+		if ! remote_present; then
+		    if test -n "$(git ls-tree MERGE_HEAD -- "$MERGED")"; then
+			# Remote isn't present, but it's a subdirectory
+			git ls-tree --full-name -r MERGE_HEAD -- "$MERGED" | git update-index --index-info || exit $?
+		    else
+			test -e "$MERGED" && mv -- "$MERGED" "$BACKUP"
+			git update-index --force-remove "$MERGED"
+		    fi
+		elif is_submodule "$remote_mode"; then
+		    ! is_submodule "$local_mode" && test -e "$MERGED" && mv -- "$MERGED" "$BACKUP"
+		    stage_submodule "$MERGED" "$remote_sha1"
+		else
+		    test -e "$MERGED" && mv -- "$MERGED" "$BACKUP"
+		    git checkout-index -f --stage=3 -- "$MERGED"
+		    git add -- "$MERGED"
+		fi
+		cleanup_temp_files --save-backup
+		return 0
+		;;
+	    [aA]*)
+		return 1
+		;;
+	    esac
+	done
+}
+
+stage_submodule () {
+    path="$1"
+    submodule_sha1="$2"
+    mkdir -p "$path" || die "fatal: unable to create directory for module at $path"
+    # Find $path relative to work tree
+    work_tree_root=$(cd_to_toplevel && pwd)
+    work_rel_path=$(cd "$path" && GIT_WORK_TREE="${work_tree_root}" git rev-parse --show-prefix)
+    test -n "$work_rel_path" || die "fatal: unable to get path of module $path relative to work tree"
+    git update-index --add --replace --cacheinfo 160000 "$submodule_sha1" "${work_rel_path%/}" || die
+}
+
 checkout_staged_file () {
-    tmpfile=$(expr "$(git checkout-index --temp --stage="$1" "$2")" : '\([^	]*\)	')
+    tmpfile=$(expr \
+	    "$(git checkout-index --temp --stage="$1" "$2" 2>/dev/null)" \
+	    : '\([^	]*\)	')
 
     if test $? -eq 0 -a -n "$tmpfile" ; then
 	mv -- "$(git rev-parse --show-cdup)$tmpfile" "$3"
+    else
+	>"$3"
     fi
 }
 
@@ -139,16 +211,26 @@
     REMOTE="./$MERGED.REMOTE.$ext"
     BASE="./$MERGED.BASE.$ext"
 
-    mv -- "$MERGED" "$BACKUP"
-    cp -- "$BACKUP" "$MERGED"
-
     base_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==1) print $1;}')
     local_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $1;}')
     remote_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $1;}')
 
-    base_present   && checkout_staged_file 1 "$MERGED" "$BASE"
-    local_present  && checkout_staged_file 2 "$MERGED" "$LOCAL"
-    remote_present && checkout_staged_file 3 "$MERGED" "$REMOTE"
+    if is_submodule "$local_mode" || is_submodule "$remote_mode"; then
+	echo "Submodule merge conflict for '$MERGED':"
+	local_sha1=$(git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $2;}')
+	remote_sha1=$(git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $2;}')
+	describe_file "$local_mode" "local" "$local_sha1"
+	describe_file "$remote_mode" "remote" "$remote_sha1"
+	resolve_submodule_merge
+	return
+    fi
+
+    mv -- "$MERGED" "$BACKUP"
+    cp -- "$BACKUP" "$MERGED"
+
+    checkout_staged_file 1 "$MERGED" "$BASE"
+    checkout_staged_file 2 "$MERGED" "$LOCAL"
+    checkout_staged_file 3 "$MERGED" "$REMOTE"
 
     if test -z "$local_mode" -o -z "$remote_mode"; then
 	echo "Deleted merge conflict for '$MERGED':"
@@ -171,7 +253,7 @@
     describe_file "$remote_mode" "remote" "$REMOTE"
     if "$prompt" = true; then
 	printf "Hit return to start merge resolution tool (%s): " "$merge_tool"
-	read ans
+	read ans || return 1
     fi
 
     if base_present; then
@@ -242,7 +324,7 @@
 prompt_after_failed_merge() {
     while true; do
 	printf "Continue merging other unresolved paths (y/n) ? "
-	read ans
+	read ans || return 1
 	case "$ans" in
 
 	    [yY]*)
@@ -264,42 +346,42 @@
 
 last_status=0
 rollup_status=0
+files=
 
 if test $# -eq 0 ; then
-    files=$(git ls-files -u | sed -e 's/^[^	]*	//' | sort -u)
-    if test -z "$files" ; then
-	echo "No files need merging"
-	exit 0
+    cd_to_toplevel
+
+    if test -e "$GIT_DIR/MERGE_RR"
+    then
+	files=$(git rerere remaining)
+    else
+	files=$(git ls-files -u | sed -e 's/^[^	]*	//' | sort -u)
     fi
-    echo Merging the files: "$files"
-    git ls-files -u |
-    sed -e 's/^[^	]*	//' |
-    sort -u |
-    while IFS= read i
-    do
-	if test $last_status -ne 0; then
-	    prompt_after_failed_merge < /dev/tty || exit 1
-	fi
-	printf "\n"
-	merge_file "$i" < /dev/tty > /dev/tty
-	last_status=$?
-	if test $last_status -ne 0; then
-	    rollup_status=1
-	fi
-    done
 else
-    while test $# -gt 0; do
-	if test $last_status -ne 0; then
-	    prompt_after_failed_merge || exit 1
-	fi
-	printf "\n"
-	merge_file "$1"
-	last_status=$?
-	if test $last_status -ne 0; then
-	    rollup_status=1
-	fi
-	shift
-    done
+    files=$(git ls-files -u -- "$@" | sed -e 's/^[^	]*	//' | sort -u)
 fi
 
+if test -z "$files" ; then
+    echo "No files need merging"
+    exit 0
+fi
+
+printf "Merging:\n"
+printf "$files\n"
+
+IFS='
+'
+for i in $files
+do
+    if test $last_status -ne 0; then
+	prompt_after_failed_merge || exit 1
+    fi
+    printf "\n"
+    merge_file "$i"
+    last_status=$?
+    if test $last_status -ne 0; then
+	rollup_status=1
+    fi
+done
+
 exit $rollup_status
diff --git a/git-p4.py b/git-p4.py
new file mode 100755
index 0000000..e67d37d
--- /dev/null
+++ b/git-p4.py
@@ -0,0 +1,3027 @@
+#!/usr/bin/env python
+#
+# git-p4.py -- A tool for bidirectional operation between a Perforce depot and git.
+#
+# Author: Simon Hausmann <simon@lst.de>
+# Copyright: 2007 Simon Hausmann <simon@lst.de>
+#            2007 Trolltech ASA
+# License: MIT <http://www.opensource.org/licenses/mit-license.php>
+#
+
+import optparse, sys, os, marshal, subprocess, shelve
+import tempfile, getopt, os.path, time, platform
+import re, shutil
+
+verbose = False
+
+# Only labels/tags matching this will be imported/exported
+defaultLabelRegexp = r'[a-zA-Z0-9_\-.]+$'
+
+def p4_build_cmd(cmd):
+    """Build a suitable p4 command line.
+
+    This consolidates building and returning a p4 command line into one
+    location. It means that hooking into the environment, or other configuration
+    can be done more easily.
+    """
+    real_cmd = ["p4"]
+
+    user = gitConfig("git-p4.user")
+    if len(user) > 0:
+        real_cmd += ["-u",user]
+
+    password = gitConfig("git-p4.password")
+    if len(password) > 0:
+        real_cmd += ["-P", password]
+
+    port = gitConfig("git-p4.port")
+    if len(port) > 0:
+        real_cmd += ["-p", port]
+
+    host = gitConfig("git-p4.host")
+    if len(host) > 0:
+        real_cmd += ["-H", host]
+
+    client = gitConfig("git-p4.client")
+    if len(client) > 0:
+        real_cmd += ["-c", client]
+
+
+    if isinstance(cmd,basestring):
+        real_cmd = ' '.join(real_cmd) + ' ' + cmd
+    else:
+        real_cmd += cmd
+    return real_cmd
+
+def chdir(dir):
+    # P4 uses the PWD environment variable rather than getcwd(). Since we're
+    # not using the shell, we have to set it ourselves.  This path could
+    # be relative, so go there first, then figure out where we ended up.
+    os.chdir(dir)
+    os.environ['PWD'] = os.getcwd()
+
+def die(msg):
+    if verbose:
+        raise Exception(msg)
+    else:
+        sys.stderr.write(msg + "\n")
+        sys.exit(1)
+
+def write_pipe(c, stdin):
+    if verbose:
+        sys.stderr.write('Writing pipe: %s\n' % str(c))
+
+    expand = isinstance(c,basestring)
+    p = subprocess.Popen(c, stdin=subprocess.PIPE, shell=expand)
+    pipe = p.stdin
+    val = pipe.write(stdin)
+    pipe.close()
+    if p.wait():
+        die('Command failed: %s' % str(c))
+
+    return val
+
+def p4_write_pipe(c, stdin):
+    real_cmd = p4_build_cmd(c)
+    return write_pipe(real_cmd, stdin)
+
+def read_pipe(c, ignore_error=False):
+    if verbose:
+        sys.stderr.write('Reading pipe: %s\n' % str(c))
+
+    expand = isinstance(c,basestring)
+    p = subprocess.Popen(c, stdout=subprocess.PIPE, shell=expand)
+    pipe = p.stdout
+    val = pipe.read()
+    if p.wait() and not ignore_error:
+        die('Command failed: %s' % str(c))
+
+    return val
+
+def p4_read_pipe(c, ignore_error=False):
+    real_cmd = p4_build_cmd(c)
+    return read_pipe(real_cmd, ignore_error)
+
+def read_pipe_lines(c):
+    if verbose:
+        sys.stderr.write('Reading pipe: %s\n' % str(c))
+
+    expand = isinstance(c, basestring)
+    p = subprocess.Popen(c, stdout=subprocess.PIPE, shell=expand)
+    pipe = p.stdout
+    val = pipe.readlines()
+    if pipe.close() or p.wait():
+        die('Command failed: %s' % str(c))
+
+    return val
+
+def p4_read_pipe_lines(c):
+    """Specifically invoke p4 on the command supplied. """
+    real_cmd = p4_build_cmd(c)
+    return read_pipe_lines(real_cmd)
+
+def p4_has_command(cmd):
+    """Ask p4 for help on this command.  If it returns an error, the
+       command does not exist in this version of p4."""
+    real_cmd = p4_build_cmd(["help", cmd])
+    p = subprocess.Popen(real_cmd, stdout=subprocess.PIPE,
+                                   stderr=subprocess.PIPE)
+    p.communicate()
+    return p.returncode == 0
+
+def system(cmd):
+    expand = isinstance(cmd,basestring)
+    if verbose:
+        sys.stderr.write("executing %s\n" % str(cmd))
+    subprocess.check_call(cmd, shell=expand)
+
+def p4_system(cmd):
+    """Specifically invoke p4 as the system command. """
+    real_cmd = p4_build_cmd(cmd)
+    expand = isinstance(real_cmd, basestring)
+    subprocess.check_call(real_cmd, shell=expand)
+
+def p4_integrate(src, dest):
+    p4_system(["integrate", "-Dt", wildcard_encode(src), wildcard_encode(dest)])
+
+def p4_sync(f, *options):
+    p4_system(["sync"] + list(options) + [wildcard_encode(f)])
+
+def p4_add(f):
+    # forcibly add file names with wildcards
+    if wildcard_present(f):
+        p4_system(["add", "-f", f])
+    else:
+        p4_system(["add", f])
+
+def p4_delete(f):
+    p4_system(["delete", wildcard_encode(f)])
+
+def p4_edit(f):
+    p4_system(["edit", wildcard_encode(f)])
+
+def p4_revert(f):
+    p4_system(["revert", wildcard_encode(f)])
+
+def p4_reopen(type, f):
+    p4_system(["reopen", "-t", type, wildcard_encode(f)])
+
+def p4_move(src, dest):
+    p4_system(["move", "-k", wildcard_encode(src), wildcard_encode(dest)])
+
+#
+# Canonicalize the p4 type and return a tuple of the
+# base type, plus any modifiers.  See "p4 help filetypes"
+# for a list and explanation.
+#
+def split_p4_type(p4type):
+
+    p4_filetypes_historical = {
+        "ctempobj": "binary+Sw",
+        "ctext": "text+C",
+        "cxtext": "text+Cx",
+        "ktext": "text+k",
+        "kxtext": "text+kx",
+        "ltext": "text+F",
+        "tempobj": "binary+FSw",
+        "ubinary": "binary+F",
+        "uresource": "resource+F",
+        "uxbinary": "binary+Fx",
+        "xbinary": "binary+x",
+        "xltext": "text+Fx",
+        "xtempobj": "binary+Swx",
+        "xtext": "text+x",
+        "xunicode": "unicode+x",
+        "xutf16": "utf16+x",
+    }
+    if p4type in p4_filetypes_historical:
+        p4type = p4_filetypes_historical[p4type]
+    mods = ""
+    s = p4type.split("+")
+    base = s[0]
+    mods = ""
+    if len(s) > 1:
+        mods = s[1]
+    return (base, mods)
+
+#
+# return the raw p4 type of a file (text, text+ko, etc)
+#
+def p4_type(file):
+    results = p4CmdList(["fstat", "-T", "headType", file])
+    return results[0]['headType']
+
+#
+# Given a type base and modifier, return a regexp matching
+# the keywords that can be expanded in the file
+#
+def p4_keywords_regexp_for_type(base, type_mods):
+    if base in ("text", "unicode", "binary"):
+        kwords = None
+        if "ko" in type_mods:
+            kwords = 'Id|Header'
+        elif "k" in type_mods:
+            kwords = 'Id|Header|Author|Date|DateTime|Change|File|Revision'
+        else:
+            return None
+        pattern = r"""
+            \$              # Starts with a dollar, followed by...
+            (%s)            # one of the keywords, followed by...
+            (:[^$]+)?       # possibly an old expansion, followed by...
+            \$              # another dollar
+            """ % kwords
+        return pattern
+    else:
+        return None
+
+#
+# Given a file, return a regexp matching the possible
+# RCS keywords that will be expanded, or None for files
+# with kw expansion turned off.
+#
+def p4_keywords_regexp_for_file(file):
+    if not os.path.exists(file):
+        return None
+    else:
+        (type_base, type_mods) = split_p4_type(p4_type(file))
+        return p4_keywords_regexp_for_type(type_base, type_mods)
+
+def setP4ExecBit(file, mode):
+    # Reopens an already open file and changes the execute bit to match
+    # the execute bit setting in the passed in mode.
+
+    p4Type = "+x"
+
+    if not isModeExec(mode):
+        p4Type = getP4OpenedType(file)
+        p4Type = re.sub('^([cku]?)x(.*)', '\\1\\2', p4Type)
+        p4Type = re.sub('(.*?\+.*?)x(.*?)', '\\1\\2', p4Type)
+        if p4Type[-1] == "+":
+            p4Type = p4Type[0:-1]
+
+    p4_reopen(p4Type, file)
+
+def getP4OpenedType(file):
+    # Returns the perforce file type for the given file.
+
+    result = p4_read_pipe(["opened", wildcard_encode(file)])
+    match = re.match(".*\((.+)\)\r?$", result)
+    if match:
+        return match.group(1)
+    else:
+        die("Could not determine file type for %s (result: '%s')" % (file, result))
+
+# Return the set of all p4 labels
+def getP4Labels(depotPaths):
+    labels = set()
+    if isinstance(depotPaths,basestring):
+        depotPaths = [depotPaths]
+
+    for l in p4CmdList(["labels"] + ["%s..." % p for p in depotPaths]):
+        label = l['label']
+        labels.add(label)
+
+    return labels
+
+# Return the set of all git tags
+def getGitTags():
+    gitTags = set()
+    for line in read_pipe_lines(["git", "tag"]):
+        tag = line.strip()
+        gitTags.add(tag)
+    return gitTags
+
+def diffTreePattern():
+    # This is a simple generator for the diff tree regex pattern. This could be
+    # a class variable if this and parseDiffTreeEntry were a part of a class.
+    pattern = re.compile(':(\d+) (\d+) (\w+) (\w+) ([A-Z])(\d+)?\t(.*?)((\t(.*))|$)')
+    while True:
+        yield pattern
+
+def parseDiffTreeEntry(entry):
+    """Parses a single diff tree entry into its component elements.
+
+    See git-diff-tree(1) manpage for details about the format of the diff
+    output. This method returns a dictionary with the following elements:
+
+    src_mode - The mode of the source file
+    dst_mode - The mode of the destination file
+    src_sha1 - The sha1 for the source file
+    dst_sha1 - The sha1 fr the destination file
+    status - The one letter status of the diff (i.e. 'A', 'M', 'D', etc)
+    status_score - The score for the status (applicable for 'C' and 'R'
+                   statuses). This is None if there is no score.
+    src - The path for the source file.
+    dst - The path for the destination file. This is only present for
+          copy or renames. If it is not present, this is None.
+
+    If the pattern is not matched, None is returned."""
+
+    match = diffTreePattern().next().match(entry)
+    if match:
+        return {
+            'src_mode': match.group(1),
+            'dst_mode': match.group(2),
+            'src_sha1': match.group(3),
+            'dst_sha1': match.group(4),
+            'status': match.group(5),
+            'status_score': match.group(6),
+            'src': match.group(7),
+            'dst': match.group(10)
+        }
+    return None
+
+def isModeExec(mode):
+    # Returns True if the given git mode represents an executable file,
+    # otherwise False.
+    return mode[-3:] == "755"
+
+def isModeExecChanged(src_mode, dst_mode):
+    return isModeExec(src_mode) != isModeExec(dst_mode)
+
+def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None):
+
+    if isinstance(cmd,basestring):
+        cmd = "-G " + cmd
+        expand = True
+    else:
+        cmd = ["-G"] + cmd
+        expand = False
+
+    cmd = p4_build_cmd(cmd)
+    if verbose:
+        sys.stderr.write("Opening pipe: %s\n" % str(cmd))
+
+    # Use a temporary file to avoid deadlocks without
+    # subprocess.communicate(), which would put another copy
+    # of stdout into memory.
+    stdin_file = None
+    if stdin is not None:
+        stdin_file = tempfile.TemporaryFile(prefix='p4-stdin', mode=stdin_mode)
+        if isinstance(stdin,basestring):
+            stdin_file.write(stdin)
+        else:
+            for i in stdin:
+                stdin_file.write(i + '\n')
+        stdin_file.flush()
+        stdin_file.seek(0)
+
+    p4 = subprocess.Popen(cmd,
+                          shell=expand,
+                          stdin=stdin_file,
+                          stdout=subprocess.PIPE)
+
+    result = []
+    try:
+        while True:
+            entry = marshal.load(p4.stdout)
+            if cb is not None:
+                cb(entry)
+            else:
+                result.append(entry)
+    except EOFError:
+        pass
+    exitCode = p4.wait()
+    if exitCode != 0:
+        entry = {}
+        entry["p4ExitCode"] = exitCode
+        result.append(entry)
+
+    return result
+
+def p4Cmd(cmd):
+    list = p4CmdList(cmd)
+    result = {}
+    for entry in list:
+        result.update(entry)
+    return result;
+
+def p4Where(depotPath):
+    if not depotPath.endswith("/"):
+        depotPath += "/"
+    depotPath = depotPath + "..."
+    outputList = p4CmdList(["where", depotPath])
+    output = None
+    for entry in outputList:
+        if "depotFile" in entry:
+            if entry["depotFile"] == depotPath:
+                output = entry
+                break
+        elif "data" in entry:
+            data = entry.get("data")
+            space = data.find(" ")
+            if data[:space] == depotPath:
+                output = entry
+                break
+    if output == None:
+        return ""
+    if output["code"] == "error":
+        return ""
+    clientPath = ""
+    if "path" in output:
+        clientPath = output.get("path")
+    elif "data" in output:
+        data = output.get("data")
+        lastSpace = data.rfind(" ")
+        clientPath = data[lastSpace + 1:]
+
+    if clientPath.endswith("..."):
+        clientPath = clientPath[:-3]
+    return clientPath
+
+def currentGitBranch():
+    return read_pipe("git name-rev HEAD").split(" ")[1].strip()
+
+def isValidGitDir(path):
+    if (os.path.exists(path + "/HEAD")
+        and os.path.exists(path + "/refs") and os.path.exists(path + "/objects")):
+        return True;
+    return False
+
+def parseRevision(ref):
+    return read_pipe("git rev-parse %s" % ref).strip()
+
+def branchExists(ref):
+    rev = read_pipe(["git", "rev-parse", "-q", "--verify", ref],
+                     ignore_error=True)
+    return len(rev) > 0
+
+def extractLogMessageFromGitCommit(commit):
+    logMessage = ""
+
+    ## fixme: title is first line of commit, not 1st paragraph.
+    foundTitle = False
+    for log in read_pipe_lines("git cat-file commit %s" % commit):
+       if not foundTitle:
+           if len(log) == 1:
+               foundTitle = True
+           continue
+
+       logMessage += log
+    return logMessage
+
+def extractSettingsGitLog(log):
+    values = {}
+    for line in log.split("\n"):
+        line = line.strip()
+        m = re.search (r"^ *\[git-p4: (.*)\]$", line)
+        if not m:
+            continue
+
+        assignments = m.group(1).split (':')
+        for a in assignments:
+            vals = a.split ('=')
+            key = vals[0].strip()
+            val = ('='.join (vals[1:])).strip()
+            if val.endswith ('\"') and val.startswith('"'):
+                val = val[1:-1]
+
+            values[key] = val
+
+    paths = values.get("depot-paths")
+    if not paths:
+        paths = values.get("depot-path")
+    if paths:
+        values['depot-paths'] = paths.split(',')
+    return values
+
+def gitBranchExists(branch):
+    proc = subprocess.Popen(["git", "rev-parse", branch],
+                            stderr=subprocess.PIPE, stdout=subprocess.PIPE);
+    return proc.wait() == 0;
+
+_gitConfig = {}
+def gitConfig(key, args = None): # set args to "--bool", for instance
+    if not _gitConfig.has_key(key):
+        argsFilter = ""
+        if args != None:
+            argsFilter = "%s " % args
+        cmd = "git config %s%s" % (argsFilter, key)
+        _gitConfig[key] = read_pipe(cmd, ignore_error=True).strip()
+    return _gitConfig[key]
+
+def gitConfigList(key):
+    if not _gitConfig.has_key(key):
+        _gitConfig[key] = read_pipe("git config --get-all %s" % key, ignore_error=True).strip().split(os.linesep)
+    return _gitConfig[key]
+
+def p4BranchesInGit(branchesAreInRemotes = True):
+    branches = {}
+
+    cmdline = "git rev-parse --symbolic "
+    if branchesAreInRemotes:
+        cmdline += " --remotes"
+    else:
+        cmdline += " --branches"
+
+    for line in read_pipe_lines(cmdline):
+        line = line.strip()
+
+        ## only import to p4/
+        if not line.startswith('p4/') or line == "p4/HEAD":
+            continue
+        branch = line
+
+        # strip off p4
+        branch = re.sub ("^p4/", "", line)
+
+        branches[branch] = parseRevision(line)
+    return branches
+
+def findUpstreamBranchPoint(head = "HEAD"):
+    branches = p4BranchesInGit()
+    # map from depot-path to branch name
+    branchByDepotPath = {}
+    for branch in branches.keys():
+        tip = branches[branch]
+        log = extractLogMessageFromGitCommit(tip)
+        settings = extractSettingsGitLog(log)
+        if settings.has_key("depot-paths"):
+            paths = ",".join(settings["depot-paths"])
+            branchByDepotPath[paths] = "remotes/p4/" + branch
+
+    settings = None
+    parent = 0
+    while parent < 65535:
+        commit = head + "~%s" % parent
+        log = extractLogMessageFromGitCommit(commit)
+        settings = extractSettingsGitLog(log)
+        if settings.has_key("depot-paths"):
+            paths = ",".join(settings["depot-paths"])
+            if branchByDepotPath.has_key(paths):
+                return [branchByDepotPath[paths], settings]
+
+        parent = parent + 1
+
+    return ["", settings]
+
+def createOrUpdateBranchesFromOrigin(localRefPrefix = "refs/remotes/p4/", silent=True):
+    if not silent:
+        print ("Creating/updating branch(es) in %s based on origin branch(es)"
+               % localRefPrefix)
+
+    originPrefix = "origin/p4/"
+
+    for line in read_pipe_lines("git rev-parse --symbolic --remotes"):
+        line = line.strip()
+        if (not line.startswith(originPrefix)) or line.endswith("HEAD"):
+            continue
+
+        headName = line[len(originPrefix):]
+        remoteHead = localRefPrefix + headName
+        originHead = line
+
+        original = extractSettingsGitLog(extractLogMessageFromGitCommit(originHead))
+        if (not original.has_key('depot-paths')
+            or not original.has_key('change')):
+            continue
+
+        update = False
+        if not gitBranchExists(remoteHead):
+            if verbose:
+                print "creating %s" % remoteHead
+            update = True
+        else:
+            settings = extractSettingsGitLog(extractLogMessageFromGitCommit(remoteHead))
+            if settings.has_key('change') > 0:
+                if settings['depot-paths'] == original['depot-paths']:
+                    originP4Change = int(original['change'])
+                    p4Change = int(settings['change'])
+                    if originP4Change > p4Change:
+                        print ("%s (%s) is newer than %s (%s). "
+                               "Updating p4 branch from origin."
+                               % (originHead, originP4Change,
+                                  remoteHead, p4Change))
+                        update = True
+                else:
+                    print ("Ignoring: %s was imported from %s while "
+                           "%s was imported from %s"
+                           % (originHead, ','.join(original['depot-paths']),
+                              remoteHead, ','.join(settings['depot-paths'])))
+
+        if update:
+            system("git update-ref %s %s" % (remoteHead, originHead))
+
+def originP4BranchesExist():
+        return gitBranchExists("origin") or gitBranchExists("origin/p4") or gitBranchExists("origin/p4/master")
+
+def p4ChangesForPaths(depotPaths, changeRange):
+    assert depotPaths
+    cmd = ['changes']
+    for p in depotPaths:
+        cmd += ["%s...%s" % (p, changeRange)]
+    output = p4_read_pipe_lines(cmd)
+
+    changes = {}
+    for line in output:
+        changeNum = int(line.split(" ")[1])
+        changes[changeNum] = True
+
+    changelist = changes.keys()
+    changelist.sort()
+    return changelist
+
+def p4PathStartsWith(path, prefix):
+    # This method tries to remedy a potential mixed-case issue:
+    #
+    # If UserA adds  //depot/DirA/file1
+    # and UserB adds //depot/dira/file2
+    #
+    # we may or may not have a problem. If you have core.ignorecase=true,
+    # we treat DirA and dira as the same directory
+    ignorecase = gitConfig("core.ignorecase", "--bool") == "true"
+    if ignorecase:
+        return path.lower().startswith(prefix.lower())
+    return path.startswith(prefix)
+
+def getClientSpec():
+    """Look at the p4 client spec, create a View() object that contains
+       all the mappings, and return it."""
+
+    specList = p4CmdList("client -o")
+    if len(specList) != 1:
+        die('Output from "client -o" is %d lines, expecting 1' %
+            len(specList))
+
+    # dictionary of all client parameters
+    entry = specList[0]
+
+    # just the keys that start with "View"
+    view_keys = [ k for k in entry.keys() if k.startswith("View") ]
+
+    # hold this new View
+    view = View()
+
+    # append the lines, in order, to the view
+    for view_num in range(len(view_keys)):
+        k = "View%d" % view_num
+        if k not in view_keys:
+            die("Expected view key %s missing" % k)
+        view.append(entry[k])
+
+    return view
+
+def getClientRoot():
+    """Grab the client directory."""
+
+    output = p4CmdList("client -o")
+    if len(output) != 1:
+        die('Output from "client -o" is %d lines, expecting 1' % len(output))
+
+    entry = output[0]
+    if "Root" not in entry:
+        die('Client has no "Root"')
+
+    return entry["Root"]
+
+#
+# P4 wildcards are not allowed in filenames.  P4 complains
+# if you simply add them, but you can force it with "-f", in
+# which case it translates them into %xx encoding internally.
+#
+def wildcard_decode(path):
+    # Search for and fix just these four characters.  Do % last so
+    # that fixing it does not inadvertently create new %-escapes.
+    # Cannot have * in a filename in windows; untested as to
+    # what p4 would do in such a case.
+    if not platform.system() == "Windows":
+        path = path.replace("%2A", "*")
+    path = path.replace("%23", "#") \
+               .replace("%40", "@") \
+               .replace("%25", "%")
+    return path
+
+def wildcard_encode(path):
+    # do % first to avoid double-encoding the %s introduced here
+    path = path.replace("%", "%25") \
+               .replace("*", "%2A") \
+               .replace("#", "%23") \
+               .replace("@", "%40")
+    return path
+
+def wildcard_present(path):
+    return path.translate(None, "*#@%") != path
+
+class Command:
+    def __init__(self):
+        self.usage = "usage: %prog [options]"
+        self.needsGit = True
+        self.verbose = False
+
+class P4UserMap:
+    def __init__(self):
+        self.userMapFromPerforceServer = False
+        self.myP4UserId = None
+
+    def p4UserId(self):
+        if self.myP4UserId:
+            return self.myP4UserId
+
+        results = p4CmdList("user -o")
+        for r in results:
+            if r.has_key('User'):
+                self.myP4UserId = r['User']
+                return r['User']
+        die("Could not find your p4 user id")
+
+    def p4UserIsMe(self, p4User):
+        # return True if the given p4 user is actually me
+        me = self.p4UserId()
+        if not p4User or p4User != me:
+            return False
+        else:
+            return True
+
+    def getUserCacheFilename(self):
+        home = os.environ.get("HOME", os.environ.get("USERPROFILE"))
+        return home + "/.gitp4-usercache.txt"
+
+    def getUserMapFromPerforceServer(self):
+        if self.userMapFromPerforceServer:
+            return
+        self.users = {}
+        self.emails = {}
+
+        for output in p4CmdList("users"):
+            if not output.has_key("User"):
+                continue
+            self.users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">"
+            self.emails[output["Email"]] = output["User"]
+
+
+        s = ''
+        for (key, val) in self.users.items():
+            s += "%s\t%s\n" % (key.expandtabs(1), val.expandtabs(1))
+
+        open(self.getUserCacheFilename(), "wb").write(s)
+        self.userMapFromPerforceServer = True
+
+    def loadUserMapFromCache(self):
+        self.users = {}
+        self.userMapFromPerforceServer = False
+        try:
+            cache = open(self.getUserCacheFilename(), "rb")
+            lines = cache.readlines()
+            cache.close()
+            for line in lines:
+                entry = line.strip().split("\t")
+                self.users[entry[0]] = entry[1]
+        except IOError:
+            self.getUserMapFromPerforceServer()
+
+class P4Debug(Command):
+    def __init__(self):
+        Command.__init__(self)
+        self.options = []
+        self.description = "A tool to debug the output of p4 -G."
+        self.needsGit = False
+
+    def run(self, args):
+        j = 0
+        for output in p4CmdList(args):
+            print 'Element: %d' % j
+            j += 1
+            print output
+        return True
+
+class P4RollBack(Command):
+    def __init__(self):
+        Command.__init__(self)
+        self.options = [
+            optparse.make_option("--local", dest="rollbackLocalBranches", action="store_true")
+        ]
+        self.description = "A tool to debug the multi-branch import. Don't use :)"
+        self.rollbackLocalBranches = False
+
+    def run(self, args):
+        if len(args) != 1:
+            return False
+        maxChange = int(args[0])
+
+        if "p4ExitCode" in p4Cmd("changes -m 1"):
+            die("Problems executing p4");
+
+        if self.rollbackLocalBranches:
+            refPrefix = "refs/heads/"
+            lines = read_pipe_lines("git rev-parse --symbolic --branches")
+        else:
+            refPrefix = "refs/remotes/"
+            lines = read_pipe_lines("git rev-parse --symbolic --remotes")
+
+        for line in lines:
+            if self.rollbackLocalBranches or (line.startswith("p4/") and line != "p4/HEAD\n"):
+                line = line.strip()
+                ref = refPrefix + line
+                log = extractLogMessageFromGitCommit(ref)
+                settings = extractSettingsGitLog(log)
+
+                depotPaths = settings['depot-paths']
+                change = settings['change']
+
+                changed = False
+
+                if len(p4Cmd("changes -m 1 "  + ' '.join (['%s...@%s' % (p, maxChange)
+                                                           for p in depotPaths]))) == 0:
+                    print "Branch %s did not exist at change %s, deleting." % (ref, maxChange)
+                    system("git update-ref -d %s `git rev-parse %s`" % (ref, ref))
+                    continue
+
+                while change and int(change) > maxChange:
+                    changed = True
+                    if self.verbose:
+                        print "%s is at %s ; rewinding towards %s" % (ref, change, maxChange)
+                    system("git update-ref %s \"%s^\"" % (ref, ref))
+                    log = extractLogMessageFromGitCommit(ref)
+                    settings =  extractSettingsGitLog(log)
+
+
+                    depotPaths = settings['depot-paths']
+                    change = settings['change']
+
+                if changed:
+                    print "%s rewound to %s" % (ref, change)
+
+        return True
+
+class P4Submit(Command, P4UserMap):
+    def __init__(self):
+        Command.__init__(self)
+        P4UserMap.__init__(self)
+        self.options = [
+                optparse.make_option("--origin", dest="origin"),
+                optparse.make_option("-M", dest="detectRenames", action="store_true"),
+                # preserve the user, requires relevant p4 permissions
+                optparse.make_option("--preserve-user", dest="preserveUser", action="store_true"),
+                optparse.make_option("--export-labels", dest="exportLabels", action="store_true"),
+        ]
+        self.description = "Submit changes from git to the perforce depot."
+        self.usage += " [name of git branch to submit into perforce depot]"
+        self.origin = ""
+        self.detectRenames = False
+        self.preserveUser = gitConfig("git-p4.preserveUser").lower() == "true"
+        self.isWindows = (platform.system() == "Windows")
+        self.exportLabels = False
+        self.p4HasMoveCommand = p4_has_command("move")
+
+    def check(self):
+        if len(p4CmdList("opened ...")) > 0:
+            die("You have files opened with perforce! Close them before starting the sync.")
+
+    def separate_jobs_from_description(self, message):
+        """Extract and return a possible Jobs field in the commit
+           message.  It goes into a separate section in the p4 change
+           specification.
+
+           A jobs line starts with "Jobs:" and looks like a new field
+           in a form.  Values are white-space separated on the same
+           line or on following lines that start with a tab.
+
+           This does not parse and extract the full git commit message
+           like a p4 form.  It just sees the Jobs: line as a marker
+           to pass everything from then on directly into the p4 form,
+           but outside the description section.
+
+           Return a tuple (stripped log message, jobs string)."""
+
+        m = re.search(r'^Jobs:', message, re.MULTILINE)
+        if m is None:
+            return (message, None)
+
+        jobtext = message[m.start():]
+        stripped_message = message[:m.start()].rstrip()
+        return (stripped_message, jobtext)
+
+    def prepareLogMessage(self, template, message, jobs):
+        """Edits the template returned from "p4 change -o" to insert
+           the message in the Description field, and the jobs text in
+           the Jobs field."""
+        result = ""
+
+        inDescriptionSection = False
+
+        for line in template.split("\n"):
+            if line.startswith("#"):
+                result += line + "\n"
+                continue
+
+            if inDescriptionSection:
+                if line.startswith("Files:") or line.startswith("Jobs:"):
+                    inDescriptionSection = False
+                    # insert Jobs section
+                    if jobs:
+                        result += jobs + "\n"
+                else:
+                    continue
+            else:
+                if line.startswith("Description:"):
+                    inDescriptionSection = True
+                    line += "\n"
+                    for messageLine in message.split("\n"):
+                        line += "\t" + messageLine + "\n"
+
+            result += line + "\n"
+
+        return result
+
+    def patchRCSKeywords(self, file, pattern):
+        # Attempt to zap the RCS keywords in a p4 controlled file matching the given pattern
+        (handle, outFileName) = tempfile.mkstemp(dir='.')
+        try:
+            outFile = os.fdopen(handle, "w+")
+            inFile = open(file, "r")
+            regexp = re.compile(pattern, re.VERBOSE)
+            for line in inFile.readlines():
+                line = regexp.sub(r'$\1$', line)
+                outFile.write(line)
+            inFile.close()
+            outFile.close()
+            # Forcibly overwrite the original file
+            os.unlink(file)
+            shutil.move(outFileName, file)
+        except:
+            # cleanup our temporary file
+            os.unlink(outFileName)
+            print "Failed to strip RCS keywords in %s" % file
+            raise
+
+        print "Patched up RCS keywords in %s" % file
+
+    def p4UserForCommit(self,id):
+        # Return the tuple (perforce user,git email) for a given git commit id
+        self.getUserMapFromPerforceServer()
+        gitEmail = read_pipe("git log --max-count=1 --format='%%ae' %s" % id)
+        gitEmail = gitEmail.strip()
+        if not self.emails.has_key(gitEmail):
+            return (None,gitEmail)
+        else:
+            return (self.emails[gitEmail],gitEmail)
+
+    def checkValidP4Users(self,commits):
+        # check if any git authors cannot be mapped to p4 users
+        for id in commits:
+            (user,email) = self.p4UserForCommit(id)
+            if not user:
+                msg = "Cannot find p4 user for email %s in commit %s." % (email, id)
+                if gitConfig('git-p4.allowMissingP4Users').lower() == "true":
+                    print "%s" % msg
+                else:
+                    die("Error: %s\nSet git-p4.allowMissingP4Users to true to allow this." % msg)
+
+    def lastP4Changelist(self):
+        # Get back the last changelist number submitted in this client spec. This
+        # then gets used to patch up the username in the change. If the same
+        # client spec is being used by multiple processes then this might go
+        # wrong.
+        results = p4CmdList("client -o")        # find the current client
+        client = None
+        for r in results:
+            if r.has_key('Client'):
+                client = r['Client']
+                break
+        if not client:
+            die("could not get client spec")
+        results = p4CmdList(["changes", "-c", client, "-m", "1"])
+        for r in results:
+            if r.has_key('change'):
+                return r['change']
+        die("Could not get changelist number for last submit - cannot patch up user details")
+
+    def modifyChangelistUser(self, changelist, newUser):
+        # fixup the user field of a changelist after it has been submitted.
+        changes = p4CmdList("change -o %s" % changelist)
+        if len(changes) != 1:
+            die("Bad output from p4 change modifying %s to user %s" %
+                (changelist, newUser))
+
+        c = changes[0]
+        if c['User'] == newUser: return   # nothing to do
+        c['User'] = newUser
+        input = marshal.dumps(c)
+
+        result = p4CmdList("change -f -i", stdin=input)
+        for r in result:
+            if r.has_key('code'):
+                if r['code'] == 'error':
+                    die("Could not modify user field of changelist %s to %s:%s" % (changelist, newUser, r['data']))
+            if r.has_key('data'):
+                print("Updated user field for changelist %s to %s" % (changelist, newUser))
+                return
+        die("Could not modify user field of changelist %s to %s" % (changelist, newUser))
+
+    def canChangeChangelists(self):
+        # check to see if we have p4 admin or super-user permissions, either of
+        # which are required to modify changelists.
+        results = p4CmdList(["protects", self.depotPath])
+        for r in results:
+            if r.has_key('perm'):
+                if r['perm'] == 'admin':
+                    return 1
+                if r['perm'] == 'super':
+                    return 1
+        return 0
+
+    def prepareSubmitTemplate(self):
+        """Run "p4 change -o" to grab a change specification template.
+           This does not use "p4 -G", as it is nice to keep the submission
+           template in original order, since a human might edit it.
+
+           Remove lines in the Files section that show changes to files
+           outside the depot path we're committing into."""
+
+        template = ""
+        inFilesSection = False
+        for line in p4_read_pipe_lines(['change', '-o']):
+            if line.endswith("\r\n"):
+                line = line[:-2] + "\n"
+            if inFilesSection:
+                if line.startswith("\t"):
+                    # path starts and ends with a tab
+                    path = line[1:]
+                    lastTab = path.rfind("\t")
+                    if lastTab != -1:
+                        path = path[:lastTab]
+                        if not p4PathStartsWith(path, self.depotPath):
+                            continue
+                else:
+                    inFilesSection = False
+            else:
+                if line.startswith("Files:"):
+                    inFilesSection = True
+
+            template += line
+
+        return template
+
+    def edit_template(self, template_file):
+        """Invoke the editor to let the user change the submission
+           message.  Return true if okay to continue with the submit."""
+
+        # if configured to skip the editing part, just submit
+        if gitConfig("git-p4.skipSubmitEdit") == "true":
+            return True
+
+        # look at the modification time, to check later if the user saved
+        # the file
+        mtime = os.stat(template_file).st_mtime
+
+        # invoke the editor
+        if os.environ.has_key("P4EDITOR") and (os.environ.get("P4EDITOR") != ""):
+            editor = os.environ.get("P4EDITOR")
+        else:
+            editor = read_pipe("git var GIT_EDITOR").strip()
+        system(editor + " " + template_file)
+
+        # If the file was not saved, prompt to see if this patch should
+        # be skipped.  But skip this verification step if configured so.
+        if gitConfig("git-p4.skipSubmitEditCheck") == "true":
+            return True
+
+        # modification time updated means user saved the file
+        if os.stat(template_file).st_mtime > mtime:
+            return True
+
+        while True:
+            response = raw_input("Submit template unchanged. Submit anyway? [y]es, [n]o (skip this patch) ")
+            if response == 'y':
+                return True
+            if response == 'n':
+                return False
+
+    def applyCommit(self, id):
+        print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id))
+
+        (p4User, gitEmail) = self.p4UserForCommit(id)
+
+        diff = read_pipe_lines("git diff-tree -r %s \"%s^\" \"%s\"" % (self.diffOpts, id, id))
+        filesToAdd = set()
+        filesToDelete = set()
+        editedFiles = set()
+        pureRenameCopy = set()
+        filesToChangeExecBit = {}
+
+        for line in diff:
+            diff = parseDiffTreeEntry(line)
+            modifier = diff['status']
+            path = diff['src']
+            if modifier == "M":
+                p4_edit(path)
+                if isModeExecChanged(diff['src_mode'], diff['dst_mode']):
+                    filesToChangeExecBit[path] = diff['dst_mode']
+                editedFiles.add(path)
+            elif modifier == "A":
+                filesToAdd.add(path)
+                filesToChangeExecBit[path] = diff['dst_mode']
+                if path in filesToDelete:
+                    filesToDelete.remove(path)
+            elif modifier == "D":
+                filesToDelete.add(path)
+                if path in filesToAdd:
+                    filesToAdd.remove(path)
+            elif modifier == "C":
+                src, dest = diff['src'], diff['dst']
+                p4_integrate(src, dest)
+                pureRenameCopy.add(dest)
+                if diff['src_sha1'] != diff['dst_sha1']:
+                    p4_edit(dest)
+                    pureRenameCopy.discard(dest)
+                if isModeExecChanged(diff['src_mode'], diff['dst_mode']):
+                    p4_edit(dest)
+                    pureRenameCopy.discard(dest)
+                    filesToChangeExecBit[dest] = diff['dst_mode']
+                os.unlink(dest)
+                editedFiles.add(dest)
+            elif modifier == "R":
+                src, dest = diff['src'], diff['dst']
+                if self.p4HasMoveCommand:
+                    p4_edit(src)        # src must be open before move
+                    p4_move(src, dest)  # opens for (move/delete, move/add)
+                else:
+                    p4_integrate(src, dest)
+                    if diff['src_sha1'] != diff['dst_sha1']:
+                        p4_edit(dest)
+                    else:
+                        pureRenameCopy.add(dest)
+                if isModeExecChanged(diff['src_mode'], diff['dst_mode']):
+                    if not self.p4HasMoveCommand:
+                        p4_edit(dest)   # with move: already open, writable
+                    filesToChangeExecBit[dest] = diff['dst_mode']
+                if not self.p4HasMoveCommand:
+                    os.unlink(dest)
+                    filesToDelete.add(src)
+                editedFiles.add(dest)
+            else:
+                die("unknown modifier %s for %s" % (modifier, path))
+
+        diffcmd = "git format-patch -k --stdout \"%s^\"..\"%s\"" % (id, id)
+        patchcmd = diffcmd + " | git apply "
+        tryPatchCmd = patchcmd + "--check -"
+        applyPatchCmd = patchcmd + "--check --apply -"
+        patch_succeeded = True
+
+        if os.system(tryPatchCmd) != 0:
+            fixed_rcs_keywords = False
+            patch_succeeded = False
+            print "Unfortunately applying the change failed!"
+
+            # Patch failed, maybe it's just RCS keyword woes. Look through
+            # the patch to see if that's possible.
+            if gitConfig("git-p4.attemptRCSCleanup","--bool") == "true":
+                file = None
+                pattern = None
+                kwfiles = {}
+                for file in editedFiles | filesToDelete:
+                    # did this file's delta contain RCS keywords?
+                    pattern = p4_keywords_regexp_for_file(file)
+
+                    if pattern:
+                        # this file is a possibility...look for RCS keywords.
+                        regexp = re.compile(pattern, re.VERBOSE)
+                        for line in read_pipe_lines(["git", "diff", "%s^..%s" % (id, id), file]):
+                            if regexp.search(line):
+                                if verbose:
+                                    print "got keyword match on %s in %s in %s" % (pattern, line, file)
+                                kwfiles[file] = pattern
+                                break
+
+                for file in kwfiles:
+                    if verbose:
+                        print "zapping %s with %s" % (line,pattern)
+                    self.patchRCSKeywords(file, kwfiles[file])
+                    fixed_rcs_keywords = True
+
+            if fixed_rcs_keywords:
+                print "Retrying the patch with RCS keywords cleaned up"
+                if os.system(tryPatchCmd) == 0:
+                    patch_succeeded = True
+
+        if not patch_succeeded:
+            print "What do you want to do?"
+            response = "x"
+            while response != "s" and response != "a" and response != "w":
+                response = raw_input("[s]kip this patch / [a]pply the patch forcibly "
+                                     "and with .rej files / [w]rite the patch to a file (patch.txt) ")
+            if response == "s":
+                print "Skipping! Good luck with the next patches..."
+                for f in editedFiles:
+                    p4_revert(f)
+                for f in filesToAdd:
+                    os.remove(f)
+                return
+            elif response == "a":
+                os.system(applyPatchCmd)
+                if len(filesToAdd) > 0:
+                    print "You may also want to call p4 add on the following files:"
+                    print " ".join(filesToAdd)
+                if len(filesToDelete):
+                    print "The following files should be scheduled for deletion with p4 delete:"
+                    print " ".join(filesToDelete)
+                die("Please resolve and submit the conflict manually and "
+                    + "continue afterwards with git p4 submit --continue")
+            elif response == "w":
+                system(diffcmd + " > patch.txt")
+                print "Patch saved to patch.txt in %s !" % self.clientPath
+                die("Please resolve and submit the conflict manually and "
+                    "continue afterwards with git p4 submit --continue")
+
+        system(applyPatchCmd)
+
+        for f in filesToAdd:
+            p4_add(f)
+        for f in filesToDelete:
+            p4_revert(f)
+            p4_delete(f)
+
+        # Set/clear executable bits
+        for f in filesToChangeExecBit.keys():
+            mode = filesToChangeExecBit[f]
+            setP4ExecBit(f, mode)
+
+        logMessage = extractLogMessageFromGitCommit(id)
+        logMessage = logMessage.strip()
+        (logMessage, jobs) = self.separate_jobs_from_description(logMessage)
+
+        template = self.prepareSubmitTemplate()
+        submitTemplate = self.prepareLogMessage(template, logMessage, jobs)
+
+        if self.preserveUser:
+           submitTemplate = submitTemplate + ("\n######## Actual user %s, modified after commit\n" % p4User)
+
+        if os.environ.has_key("P4DIFF"):
+            del(os.environ["P4DIFF"])
+        diff = ""
+        for editedFile in editedFiles:
+            diff += p4_read_pipe(['diff', '-du',
+                                  wildcard_encode(editedFile)])
+
+        newdiff = ""
+        for newFile in filesToAdd:
+            newdiff += "==== new file ====\n"
+            newdiff += "--- /dev/null\n"
+            newdiff += "+++ %s\n" % newFile
+            f = open(newFile, "r")
+            for line in f.readlines():
+                newdiff += "+" + line
+            f.close()
+
+        if self.checkAuthorship and not self.p4UserIsMe(p4User):
+            submitTemplate += "######## git author %s does not match your p4 account.\n" % gitEmail
+            submitTemplate += "######## Use option --preserve-user to modify authorship.\n"
+            submitTemplate += "######## Variable git-p4.skipUserNameCheck hides this message.\n"
+
+        separatorLine = "######## everything below this line is just the diff #######\n"
+
+        (handle, fileName) = tempfile.mkstemp()
+        tmpFile = os.fdopen(handle, "w+")
+        if self.isWindows:
+            submitTemplate = submitTemplate.replace("\n", "\r\n")
+            separatorLine = separatorLine.replace("\n", "\r\n")
+            newdiff = newdiff.replace("\n", "\r\n")
+        tmpFile.write(submitTemplate + separatorLine + diff + newdiff)
+        tmpFile.close()
+
+        if self.edit_template(fileName):
+            # read the edited message and submit
+            tmpFile = open(fileName, "rb")
+            message = tmpFile.read()
+            tmpFile.close()
+            submitTemplate = message[:message.index(separatorLine)]
+            if self.isWindows:
+                submitTemplate = submitTemplate.replace("\r\n", "\n")
+            p4_write_pipe(['submit', '-i'], submitTemplate)
+
+            if self.preserveUser:
+                if p4User:
+                    # Get last changelist number. Cannot easily get it from
+                    # the submit command output as the output is
+                    # unmarshalled.
+                    changelist = self.lastP4Changelist()
+                    self.modifyChangelistUser(changelist, p4User)
+
+            # The rename/copy happened by applying a patch that created a
+            # new file.  This leaves it writable, which confuses p4.
+            for f in pureRenameCopy:
+                p4_sync(f, "-f")
+
+        else:
+            # skip this patch
+            print "Submission cancelled, undoing p4 changes."
+            for f in editedFiles:
+                p4_revert(f)
+            for f in filesToAdd:
+                p4_revert(f)
+                os.remove(f)
+
+        os.remove(fileName)
+
+    # Export git tags as p4 labels. Create a p4 label and then tag
+    # with that.
+    def exportGitTags(self, gitTags):
+        validLabelRegexp = gitConfig("git-p4.labelExportRegexp")
+        if len(validLabelRegexp) == 0:
+            validLabelRegexp = defaultLabelRegexp
+        m = re.compile(validLabelRegexp)
+
+        for name in gitTags:
+
+            if not m.match(name):
+                if verbose:
+                    print "tag %s does not match regexp %s" % (name, validLabelRegexp)
+                continue
+
+            # Get the p4 commit this corresponds to
+            logMessage = extractLogMessageFromGitCommit(name)
+            values = extractSettingsGitLog(logMessage)
+
+            if not values.has_key('change'):
+                # a tag pointing to something not sent to p4; ignore
+                if verbose:
+                    print "git tag %s does not give a p4 commit" % name
+                continue
+            else:
+                changelist = values['change']
+
+            # Get the tag details.
+            inHeader = True
+            isAnnotated = False
+            body = []
+            for l in read_pipe_lines(["git", "cat-file", "-p", name]):
+                l = l.strip()
+                if inHeader:
+                    if re.match(r'tag\s+', l):
+                        isAnnotated = True
+                    elif re.match(r'\s*$', l):
+                        inHeader = False
+                        continue
+                else:
+                    body.append(l)
+
+            if not isAnnotated:
+                body = ["lightweight tag imported by git p4\n"]
+
+            # Create the label - use the same view as the client spec we are using
+            clientSpec = getClientSpec()
+
+            labelTemplate  = "Label: %s\n" % name
+            labelTemplate += "Description:\n"
+            for b in body:
+                labelTemplate += "\t" + b + "\n"
+            labelTemplate += "View:\n"
+            for mapping in clientSpec.mappings:
+                labelTemplate += "\t%s\n" % mapping.depot_side.path
+
+            p4_write_pipe(["label", "-i"], labelTemplate)
+
+            # Use the label
+            p4_system(["tag", "-l", name] +
+                      ["%s@%s" % (mapping.depot_side.path, changelist) for mapping in clientSpec.mappings])
+
+            if verbose:
+                print "created p4 label for tag %s" % name
+
+    def run(self, args):
+        if len(args) == 0:
+            self.master = currentGitBranch()
+            if len(self.master) == 0 or not gitBranchExists("refs/heads/%s" % self.master):
+                die("Detecting current git branch failed!")
+        elif len(args) == 1:
+            self.master = args[0]
+            if not branchExists(self.master):
+                die("Branch %s does not exist" % self.master)
+        else:
+            return False
+
+        allowSubmit = gitConfig("git-p4.allowSubmit")
+        if len(allowSubmit) > 0 and not self.master in allowSubmit.split(","):
+            die("%s is not in git-p4.allowSubmit" % self.master)
+
+        [upstream, settings] = findUpstreamBranchPoint()
+        self.depotPath = settings['depot-paths'][0]
+        if len(self.origin) == 0:
+            self.origin = upstream
+
+        if self.preserveUser:
+            if not self.canChangeChangelists():
+                die("Cannot preserve user names without p4 super-user or admin permissions")
+
+        if self.verbose:
+            print "Origin branch is " + self.origin
+
+        if len(self.depotPath) == 0:
+            print "Internal error: cannot locate perforce depot path from existing branches"
+            sys.exit(128)
+
+        self.useClientSpec = False
+        if gitConfig("git-p4.useclientspec", "--bool") == "true":
+            self.useClientSpec = True
+        if self.useClientSpec:
+            self.clientSpecDirs = getClientSpec()
+
+        if self.useClientSpec:
+            # all files are relative to the client spec
+            self.clientPath = getClientRoot()
+        else:
+            self.clientPath = p4Where(self.depotPath)
+
+        if self.clientPath == "":
+            die("Error: Cannot locate perforce checkout of %s in client view" % self.depotPath)
+
+        print "Perforce checkout for depot path %s located at %s" % (self.depotPath, self.clientPath)
+        self.oldWorkingDirectory = os.getcwd()
+
+        # ensure the clientPath exists
+        new_client_dir = False
+        if not os.path.exists(self.clientPath):
+            new_client_dir = True
+            os.makedirs(self.clientPath)
+
+        chdir(self.clientPath)
+        print "Synchronizing p4 checkout..."
+        if new_client_dir:
+            # old one was destroyed, and maybe nobody told p4
+            p4_sync("...", "-f")
+        else:
+            p4_sync("...")
+        self.check()
+
+        commits = []
+        for line in read_pipe_lines("git rev-list --no-merges %s..%s" % (self.origin, self.master)):
+            commits.append(line.strip())
+        commits.reverse()
+
+        if self.preserveUser or (gitConfig("git-p4.skipUserNameCheck") == "true"):
+            self.checkAuthorship = False
+        else:
+            self.checkAuthorship = True
+
+        if self.preserveUser:
+            self.checkValidP4Users(commits)
+
+        #
+        # Build up a set of options to be passed to diff when
+        # submitting each commit to p4.
+        #
+        if self.detectRenames:
+            # command-line -M arg
+            self.diffOpts = "-M"
+        else:
+            # If not explicitly set check the config variable
+            detectRenames = gitConfig("git-p4.detectRenames")
+
+            if detectRenames.lower() == "false" or detectRenames == "":
+                self.diffOpts = ""
+            elif detectRenames.lower() == "true":
+                self.diffOpts = "-M"
+            else:
+                self.diffOpts = "-M%s" % detectRenames
+
+        # no command-line arg for -C or --find-copies-harder, just
+        # config variables
+        detectCopies = gitConfig("git-p4.detectCopies")
+        if detectCopies.lower() == "false" or detectCopies == "":
+            pass
+        elif detectCopies.lower() == "true":
+            self.diffOpts += " -C"
+        else:
+            self.diffOpts += " -C%s" % detectCopies
+
+        if gitConfig("git-p4.detectCopiesHarder", "--bool") == "true":
+            self.diffOpts += " --find-copies-harder"
+
+        while len(commits) > 0:
+            commit = commits[0]
+            commits = commits[1:]
+            self.applyCommit(commit)
+
+        if len(commits) == 0:
+            print "All changes applied!"
+            chdir(self.oldWorkingDirectory)
+
+            sync = P4Sync()
+            sync.run([])
+
+            rebase = P4Rebase()
+            rebase.rebase()
+
+        if gitConfig("git-p4.exportLabels", "--bool") == "true":
+            self.exportLabels = True
+
+        if self.exportLabels:
+            p4Labels = getP4Labels(self.depotPath)
+            gitTags = getGitTags()
+
+            missingGitTags = gitTags - p4Labels
+            self.exportGitTags(missingGitTags)
+
+        return True
+
+class View(object):
+    """Represent a p4 view ("p4 help views"), and map files in a
+       repo according to the view."""
+
+    class Path(object):
+        """A depot or client path, possibly containing wildcards.
+           The only one supported is ... at the end, currently.
+           Initialize with the full path, with //depot or //client."""
+
+        def __init__(self, path, is_depot):
+            self.path = path
+            self.is_depot = is_depot
+            self.find_wildcards()
+            # remember the prefix bit, useful for relative mappings
+            m = re.match("(//[^/]+/)", self.path)
+            if not m:
+                die("Path %s does not start with //prefix/" % self.path)
+            prefix = m.group(1)
+            if not self.is_depot:
+                # strip //client/ on client paths
+                self.path = self.path[len(prefix):]
+
+        def find_wildcards(self):
+            """Make sure wildcards are valid, and set up internal
+               variables."""
+
+            self.ends_triple_dot = False
+            # There are three wildcards allowed in p4 views
+            # (see "p4 help views").  This code knows how to
+            # handle "..." (only at the end), but cannot deal with
+            # "%%n" or "*".  Only check the depot_side, as p4 should
+            # validate that the client_side matches too.
+            if re.search(r'%%[1-9]', self.path):
+                die("Can't handle %%n wildcards in view: %s" % self.path)
+            if self.path.find("*") >= 0:
+                die("Can't handle * wildcards in view: %s" % self.path)
+            triple_dot_index = self.path.find("...")
+            if triple_dot_index >= 0:
+                if triple_dot_index != len(self.path) - 3:
+                    die("Can handle only single ... wildcard, at end: %s" %
+                        self.path)
+                self.ends_triple_dot = True
+
+        def ensure_compatible(self, other_path):
+            """Make sure the wildcards agree."""
+            if self.ends_triple_dot != other_path.ends_triple_dot:
+                 die("Both paths must end with ... if either does;\n" +
+                     "paths: %s %s" % (self.path, other_path.path))
+
+        def match_wildcards(self, test_path):
+            """See if this test_path matches us, and fill in the value
+               of the wildcards if so.  Returns a tuple of
+               (True|False, wildcards[]).  For now, only the ... at end
+               is supported, so at most one wildcard."""
+            if self.ends_triple_dot:
+                dotless = self.path[:-3]
+                if test_path.startswith(dotless):
+                    wildcard = test_path[len(dotless):]
+                    return (True, [ wildcard ])
+            else:
+                if test_path == self.path:
+                    return (True, [])
+            return (False, [])
+
+        def match(self, test_path):
+            """Just return if it matches; don't bother with the wildcards."""
+            b, _ = self.match_wildcards(test_path)
+            return b
+
+        def fill_in_wildcards(self, wildcards):
+            """Return the relative path, with the wildcards filled in
+               if there are any."""
+            if self.ends_triple_dot:
+                return self.path[:-3] + wildcards[0]
+            else:
+                return self.path
+
+    class Mapping(object):
+        def __init__(self, depot_side, client_side, overlay, exclude):
+            # depot_side is without the trailing /... if it had one
+            self.depot_side = View.Path(depot_side, is_depot=True)
+            self.client_side = View.Path(client_side, is_depot=False)
+            self.overlay = overlay  # started with "+"
+            self.exclude = exclude  # started with "-"
+            assert not (self.overlay and self.exclude)
+            self.depot_side.ensure_compatible(self.client_side)
+
+        def __str__(self):
+            c = " "
+            if self.overlay:
+                c = "+"
+            if self.exclude:
+                c = "-"
+            return "View.Mapping: %s%s -> %s" % \
+                   (c, self.depot_side.path, self.client_side.path)
+
+        def map_depot_to_client(self, depot_path):
+            """Calculate the client path if using this mapping on the
+               given depot path; does not consider the effect of other
+               mappings in a view.  Even excluded mappings are returned."""
+            matches, wildcards = self.depot_side.match_wildcards(depot_path)
+            if not matches:
+                return ""
+            client_path = self.client_side.fill_in_wildcards(wildcards)
+            return client_path
+
+    #
+    # View methods
+    #
+    def __init__(self):
+        self.mappings = []
+
+    def append(self, view_line):
+        """Parse a view line, splitting it into depot and client
+           sides.  Append to self.mappings, preserving order."""
+
+        # Split the view line into exactly two words.  P4 enforces
+        # structure on these lines that simplifies this quite a bit.
+        #
+        # Either or both words may be double-quoted.
+        # Single quotes do not matter.
+        # Double-quote marks cannot occur inside the words.
+        # A + or - prefix is also inside the quotes.
+        # There are no quotes unless they contain a space.
+        # The line is already white-space stripped.
+        # The two words are separated by a single space.
+        #
+        if view_line[0] == '"':
+            # First word is double quoted.  Find its end.
+            close_quote_index = view_line.find('"', 1)
+            if close_quote_index <= 0:
+                die("No first-word closing quote found: %s" % view_line)
+            depot_side = view_line[1:close_quote_index]
+            # skip closing quote and space
+            rhs_index = close_quote_index + 1 + 1
+        else:
+            space_index = view_line.find(" ")
+            if space_index <= 0:
+                die("No word-splitting space found: %s" % view_line)
+            depot_side = view_line[0:space_index]
+            rhs_index = space_index + 1
+
+        if view_line[rhs_index] == '"':
+            # Second word is double quoted.  Make sure there is a
+            # double quote at the end too.
+            if not view_line.endswith('"'):
+                die("View line with rhs quote should end with one: %s" %
+                    view_line)
+            # skip the quotes
+            client_side = view_line[rhs_index+1:-1]
+        else:
+            client_side = view_line[rhs_index:]
+
+        # prefix + means overlay on previous mapping
+        overlay = False
+        if depot_side.startswith("+"):
+            overlay = True
+            depot_side = depot_side[1:]
+
+        # prefix - means exclude this path
+        exclude = False
+        if depot_side.startswith("-"):
+            exclude = True
+            depot_side = depot_side[1:]
+
+        m = View.Mapping(depot_side, client_side, overlay, exclude)
+        self.mappings.append(m)
+
+    def map_in_client(self, depot_path):
+        """Return the relative location in the client where this
+           depot file should live.  Returns "" if the file should
+           not be mapped in the client."""
+
+        paths_filled = []
+        client_path = ""
+
+        # look at later entries first
+        for m in self.mappings[::-1]:
+
+            # see where will this path end up in the client
+            p = m.map_depot_to_client(depot_path)
+
+            if p == "":
+                # Depot path does not belong in client.  Must remember
+                # this, as previous items should not cause files to
+                # exist in this path either.  Remember that the list is
+                # being walked from the end, which has higher precedence.
+                # Overlap mappings do not exclude previous mappings.
+                if not m.overlay:
+                    paths_filled.append(m.client_side)
+
+            else:
+                # This mapping matched; no need to search any further.
+                # But, the mapping could be rejected if the client path
+                # has already been claimed by an earlier mapping (i.e.
+                # one later in the list, which we are walking backwards).
+                already_mapped_in_client = False
+                for f in paths_filled:
+                    # this is View.Path.match
+                    if f.match(p):
+                        already_mapped_in_client = True
+                        break
+                if not already_mapped_in_client:
+                    # Include this file, unless it is from a line that
+                    # explicitly said to exclude it.
+                    if not m.exclude:
+                        client_path = p
+
+                # a match, even if rejected, always stops the search
+                break
+
+        return client_path
+
+class P4Sync(Command, P4UserMap):
+    delete_actions = ( "delete", "move/delete", "purge" )
+
+    def __init__(self):
+        Command.__init__(self)
+        P4UserMap.__init__(self)
+        self.options = [
+                optparse.make_option("--branch", dest="branch"),
+                optparse.make_option("--detect-branches", dest="detectBranches", action="store_true"),
+                optparse.make_option("--changesfile", dest="changesFile"),
+                optparse.make_option("--silent", dest="silent", action="store_true"),
+                optparse.make_option("--detect-labels", dest="detectLabels", action="store_true"),
+                optparse.make_option("--import-labels", dest="importLabels", action="store_true"),
+                optparse.make_option("--import-local", dest="importIntoRemotes", action="store_false",
+                                     help="Import into refs/heads/ , not refs/remotes"),
+                optparse.make_option("--max-changes", dest="maxChanges"),
+                optparse.make_option("--keep-path", dest="keepRepoPath", action='store_true',
+                                     help="Keep entire BRANCH/DIR/SUBDIR prefix during import"),
+                optparse.make_option("--use-client-spec", dest="useClientSpec", action='store_true',
+                                     help="Only sync files that are included in the Perforce Client Spec")
+        ]
+        self.description = """Imports from Perforce into a git repository.\n
+    example:
+    //depot/my/project/ -- to import the current head
+    //depot/my/project/@all -- to import everything
+    //depot/my/project/@1,6 -- to import only from revision 1 to 6
+
+    (a ... is not needed in the path p4 specification, it's added implicitly)"""
+
+        self.usage += " //depot/path[@revRange]"
+        self.silent = False
+        self.createdBranches = set()
+        self.committedChanges = set()
+        self.branch = ""
+        self.detectBranches = False
+        self.detectLabels = False
+        self.importLabels = False
+        self.changesFile = ""
+        self.syncWithOrigin = True
+        self.importIntoRemotes = True
+        self.maxChanges = ""
+        self.isWindows = (platform.system() == "Windows")
+        self.keepRepoPath = False
+        self.depotPaths = None
+        self.p4BranchesInGit = []
+        self.cloneExclude = []
+        self.useClientSpec = False
+        self.useClientSpec_from_options = False
+        self.clientSpecDirs = None
+        self.tempBranches = []
+        self.tempBranchLocation = "git-p4-tmp"
+
+        if gitConfig("git-p4.syncFromOrigin") == "false":
+            self.syncWithOrigin = False
+
+    # Force a checkpoint in fast-import and wait for it to finish
+    def checkpoint(self):
+        self.gitStream.write("checkpoint\n\n")
+        self.gitStream.write("progress checkpoint\n\n")
+        out = self.gitOutput.readline()
+        if self.verbose:
+            print "checkpoint finished: " + out
+
+    def extractFilesFromCommit(self, commit):
+        self.cloneExclude = [re.sub(r"\.\.\.$", "", path)
+                             for path in self.cloneExclude]
+        files = []
+        fnum = 0
+        while commit.has_key("depotFile%s" % fnum):
+            path =  commit["depotFile%s" % fnum]
+
+            if [p for p in self.cloneExclude
+                if p4PathStartsWith(path, p)]:
+                found = False
+            else:
+                found = [p for p in self.depotPaths
+                         if p4PathStartsWith(path, p)]
+            if not found:
+                fnum = fnum + 1
+                continue
+
+            file = {}
+            file["path"] = path
+            file["rev"] = commit["rev%s" % fnum]
+            file["action"] = commit["action%s" % fnum]
+            file["type"] = commit["type%s" % fnum]
+            files.append(file)
+            fnum = fnum + 1
+        return files
+
+    def stripRepoPath(self, path, prefixes):
+        if self.useClientSpec:
+            return self.clientSpecDirs.map_in_client(path)
+
+        if self.keepRepoPath:
+            prefixes = [re.sub("^(//[^/]+/).*", r'\1', prefixes[0])]
+
+        for p in prefixes:
+            if p4PathStartsWith(path, p):
+                path = path[len(p):]
+
+        return path
+
+    def splitFilesIntoBranches(self, commit):
+        branches = {}
+        fnum = 0
+        while commit.has_key("depotFile%s" % fnum):
+            path =  commit["depotFile%s" % fnum]
+            found = [p for p in self.depotPaths
+                     if p4PathStartsWith(path, p)]
+            if not found:
+                fnum = fnum + 1
+                continue
+
+            file = {}
+            file["path"] = path
+            file["rev"] = commit["rev%s" % fnum]
+            file["action"] = commit["action%s" % fnum]
+            file["type"] = commit["type%s" % fnum]
+            fnum = fnum + 1
+
+            relPath = self.stripRepoPath(path, self.depotPaths)
+            relPath = wildcard_decode(relPath)
+
+            for branch in self.knownBranches.keys():
+
+                # add a trailing slash so that a commit into qt/4.2foo doesn't end up in qt/4.2
+                if relPath.startswith(branch + "/"):
+                    if branch not in branches:
+                        branches[branch] = []
+                    branches[branch].append(file)
+                    break
+
+        return branches
+
+    # output one file from the P4 stream
+    # - helper for streamP4Files
+
+    def streamOneP4File(self, file, contents):
+        relPath = self.stripRepoPath(file['depotFile'], self.branchPrefixes)
+        relPath = wildcard_decode(relPath)
+        if verbose:
+            sys.stderr.write("%s\n" % relPath)
+
+        (type_base, type_mods) = split_p4_type(file["type"])
+
+        git_mode = "100644"
+        if "x" in type_mods:
+            git_mode = "100755"
+        if type_base == "symlink":
+            git_mode = "120000"
+            # p4 print on a symlink contains "target\n"; remove the newline
+            data = ''.join(contents)
+            contents = [data[:-1]]
+
+        if type_base == "utf16":
+            # p4 delivers different text in the python output to -G
+            # than it does when using "print -o", or normal p4 client
+            # operations.  utf16 is converted to ascii or utf8, perhaps.
+            # But ascii text saved as -t utf16 is completely mangled.
+            # Invoke print -o to get the real contents.
+            text = p4_read_pipe(['print', '-q', '-o', '-', file['depotFile']])
+            contents = [ text ]
+
+        if type_base == "apple":
+            # Apple filetype files will be streamed as a concatenation of
+            # its appledouble header and the contents.  This is useless
+            # on both macs and non-macs.  If using "print -q -o xx", it
+            # will create "xx" with the data, and "%xx" with the header.
+            # This is also not very useful.
+            #
+            # Ideally, someday, this script can learn how to generate
+            # appledouble files directly and import those to git, but
+            # non-mac machines can never find a use for apple filetype.
+            print "\nIgnoring apple filetype file %s" % file['depotFile']
+            return
+
+        # Perhaps windows wants unicode, utf16 newlines translated too;
+        # but this is not doing it.
+        if self.isWindows and type_base == "text":
+            mangled = []
+            for data in contents:
+                data = data.replace("\r\n", "\n")
+                mangled.append(data)
+            contents = mangled
+
+        # Note that we do not try to de-mangle keywords on utf16 files,
+        # even though in theory somebody may want that.
+        pattern = p4_keywords_regexp_for_type(type_base, type_mods)
+        if pattern:
+            regexp = re.compile(pattern, re.VERBOSE)
+            text = ''.join(contents)
+            text = regexp.sub(r'$\1$', text)
+            contents = [ text ]
+
+        self.gitStream.write("M %s inline %s\n" % (git_mode, relPath))
+
+        # total length...
+        length = 0
+        for d in contents:
+            length = length + len(d)
+
+        self.gitStream.write("data %d\n" % length)
+        for d in contents:
+            self.gitStream.write(d)
+        self.gitStream.write("\n")
+
+    def streamOneP4Deletion(self, file):
+        relPath = self.stripRepoPath(file['path'], self.branchPrefixes)
+        relPath = wildcard_decode(relPath)
+        if verbose:
+            sys.stderr.write("delete %s\n" % relPath)
+        self.gitStream.write("D %s\n" % relPath)
+
+    # handle another chunk of streaming data
+    def streamP4FilesCb(self, marshalled):
+
+        if marshalled.has_key('depotFile') and self.stream_have_file_info:
+            # start of a new file - output the old one first
+            self.streamOneP4File(self.stream_file, self.stream_contents)
+            self.stream_file = {}
+            self.stream_contents = []
+            self.stream_have_file_info = False
+
+        # pick up the new file information... for the
+        # 'data' field we need to append to our array
+        for k in marshalled.keys():
+            if k == 'data':
+                self.stream_contents.append(marshalled['data'])
+            else:
+                self.stream_file[k] = marshalled[k]
+
+        self.stream_have_file_info = True
+
+    # Stream directly from "p4 files" into "git fast-import"
+    def streamP4Files(self, files):
+        filesForCommit = []
+        filesToRead = []
+        filesToDelete = []
+
+        for f in files:
+            # if using a client spec, only add the files that have
+            # a path in the client
+            if self.clientSpecDirs:
+                if self.clientSpecDirs.map_in_client(f['path']) == "":
+                    continue
+
+            filesForCommit.append(f)
+            if f['action'] in self.delete_actions:
+                filesToDelete.append(f)
+            else:
+                filesToRead.append(f)
+
+        # deleted files...
+        for f in filesToDelete:
+            self.streamOneP4Deletion(f)
+
+        if len(filesToRead) > 0:
+            self.stream_file = {}
+            self.stream_contents = []
+            self.stream_have_file_info = False
+
+            # curry self argument
+            def streamP4FilesCbSelf(entry):
+                self.streamP4FilesCb(entry)
+
+            fileArgs = ['%s#%s' % (f['path'], f['rev']) for f in filesToRead]
+
+            p4CmdList(["-x", "-", "print"],
+                      stdin=fileArgs,
+                      cb=streamP4FilesCbSelf)
+
+            # do the last chunk
+            if self.stream_file.has_key('depotFile'):
+                self.streamOneP4File(self.stream_file, self.stream_contents)
+
+    def make_email(self, userid):
+        if userid in self.users:
+            return self.users[userid]
+        else:
+            return "%s <a@b>" % userid
+
+    # Stream a p4 tag
+    def streamTag(self, gitStream, labelName, labelDetails, commit, epoch):
+        if verbose:
+            print "writing tag %s for commit %s" % (labelName, commit)
+        gitStream.write("tag %s\n" % labelName)
+        gitStream.write("from %s\n" % commit)
+
+        if labelDetails.has_key('Owner'):
+            owner = labelDetails["Owner"]
+        else:
+            owner = None
+
+        # Try to use the owner of the p4 label, or failing that,
+        # the current p4 user id.
+        if owner:
+            email = self.make_email(owner)
+        else:
+            email = self.make_email(self.p4UserId())
+        tagger = "%s %s %s" % (email, epoch, self.tz)
+
+        gitStream.write("tagger %s\n" % tagger)
+
+        print "labelDetails=",labelDetails
+        if labelDetails.has_key('Description'):
+            description = labelDetails['Description']
+        else:
+            description = 'Label from git p4'
+
+        gitStream.write("data %d\n" % len(description))
+        gitStream.write(description)
+        gitStream.write("\n")
+
+    def commit(self, details, files, branch, branchPrefixes, parent = ""):
+        epoch = details["time"]
+        author = details["user"]
+        self.branchPrefixes = branchPrefixes
+
+        if self.verbose:
+            print "commit into %s" % branch
+
+        # start with reading files; if that fails, we should not
+        # create a commit.
+        new_files = []
+        for f in files:
+            if [p for p in branchPrefixes if p4PathStartsWith(f['path'], p)]:
+                new_files.append (f)
+            else:
+                sys.stderr.write("Ignoring file outside of prefix: %s\n" % f['path'])
+
+        self.gitStream.write("commit %s\n" % branch)
+#        gitStream.write("mark :%s\n" % details["change"])
+        self.committedChanges.add(int(details["change"]))
+        committer = ""
+        if author not in self.users:
+            self.getUserMapFromPerforceServer()
+        committer = "%s %s %s" % (self.make_email(author), epoch, self.tz)
+
+        self.gitStream.write("committer %s\n" % committer)
+
+        self.gitStream.write("data <<EOT\n")
+        self.gitStream.write(details["desc"])
+        self.gitStream.write("\n[git-p4: depot-paths = \"%s\": change = %s"
+                             % (','.join (branchPrefixes), details["change"]))
+        if len(details['options']) > 0:
+            self.gitStream.write(": options = %s" % details['options'])
+        self.gitStream.write("]\nEOT\n\n")
+
+        if len(parent) > 0:
+            if self.verbose:
+                print "parent %s" % parent
+            self.gitStream.write("from %s\n" % parent)
+
+        self.streamP4Files(new_files)
+        self.gitStream.write("\n")
+
+        change = int(details["change"])
+
+        if self.labels.has_key(change):
+            label = self.labels[change]
+            labelDetails = label[0]
+            labelRevisions = label[1]
+            if self.verbose:
+                print "Change %s is labelled %s" % (change, labelDetails)
+
+            files = p4CmdList(["files"] + ["%s...@%s" % (p, change)
+                                                    for p in branchPrefixes])
+
+            if len(files) == len(labelRevisions):
+
+                cleanedFiles = {}
+                for info in files:
+                    if info["action"] in self.delete_actions:
+                        continue
+                    cleanedFiles[info["depotFile"]] = info["rev"]
+
+                if cleanedFiles == labelRevisions:
+                    self.streamTag(self.gitStream, 'tag_%s' % labelDetails['label'], labelDetails, branch, epoch)
+
+                else:
+                    if not self.silent:
+                        print ("Tag %s does not match with change %s: files do not match."
+                               % (labelDetails["label"], change))
+
+            else:
+                if not self.silent:
+                    print ("Tag %s does not match with change %s: file count is different."
+                           % (labelDetails["label"], change))
+
+    # Build a dictionary of changelists and labels, for "detect-labels" option.
+    def getLabels(self):
+        self.labels = {}
+
+        l = p4CmdList(["labels"] + ["%s..." % p for p in self.depotPaths])
+        if len(l) > 0 and not self.silent:
+            print "Finding files belonging to labels in %s" % `self.depotPaths`
+
+        for output in l:
+            label = output["label"]
+            revisions = {}
+            newestChange = 0
+            if self.verbose:
+                print "Querying files for label %s" % label
+            for file in p4CmdList(["files"] +
+                                      ["%s...@%s" % (p, label)
+                                          for p in self.depotPaths]):
+                revisions[file["depotFile"]] = file["rev"]
+                change = int(file["change"])
+                if change > newestChange:
+                    newestChange = change
+
+            self.labels[newestChange] = [output, revisions]
+
+        if self.verbose:
+            print "Label changes: %s" % self.labels.keys()
+
+    # Import p4 labels as git tags. A direct mapping does not
+    # exist, so assume that if all the files are at the same revision
+    # then we can use that, or it's something more complicated we should
+    # just ignore.
+    def importP4Labels(self, stream, p4Labels):
+        if verbose:
+            print "import p4 labels: " + ' '.join(p4Labels)
+
+        ignoredP4Labels = gitConfigList("git-p4.ignoredP4Labels")
+        validLabelRegexp = gitConfig("git-p4.labelImportRegexp")
+        if len(validLabelRegexp) == 0:
+            validLabelRegexp = defaultLabelRegexp
+        m = re.compile(validLabelRegexp)
+
+        for name in p4Labels:
+            commitFound = False
+
+            if not m.match(name):
+                if verbose:
+                    print "label %s does not match regexp %s" % (name,validLabelRegexp)
+                continue
+
+            if name in ignoredP4Labels:
+                continue
+
+            labelDetails = p4CmdList(['label', "-o", name])[0]
+
+            # get the most recent changelist for each file in this label
+            change = p4Cmd(["changes", "-m", "1"] + ["%s...@%s" % (p, name)
+                                for p in self.depotPaths])
+
+            if change.has_key('change'):
+                # find the corresponding git commit; take the oldest commit
+                changelist = int(change['change'])
+                gitCommit = read_pipe(["git", "rev-list", "--max-count=1",
+                     "--reverse", ":/\[git-p4:.*change = %d\]" % changelist])
+                if len(gitCommit) == 0:
+                    print "could not find git commit for changelist %d" % changelist
+                else:
+                    gitCommit = gitCommit.strip()
+                    commitFound = True
+                    # Convert from p4 time format
+                    try:
+                        tmwhen = time.strptime(labelDetails['Update'], "%Y/%m/%d %H:%M:%S")
+                    except ValueError:
+                        print "Could not convert label time %s" % labelDetail['Update']
+                        tmwhen = 1
+
+                    when = int(time.mktime(tmwhen))
+                    self.streamTag(stream, name, labelDetails, gitCommit, when)
+                    if verbose:
+                        print "p4 label %s mapped to git commit %s" % (name, gitCommit)
+            else:
+                if verbose:
+                    print "Label %s has no changelists - possibly deleted?" % name
+
+            if not commitFound:
+                # We can't import this label; don't try again as it will get very
+                # expensive repeatedly fetching all the files for labels that will
+                # never be imported. If the label is moved in the future, the
+                # ignore will need to be removed manually.
+                system(["git", "config", "--add", "git-p4.ignoredP4Labels", name])
+
+    def guessProjectName(self):
+        for p in self.depotPaths:
+            if p.endswith("/"):
+                p = p[:-1]
+            p = p[p.strip().rfind("/") + 1:]
+            if not p.endswith("/"):
+               p += "/"
+            return p
+
+    def getBranchMapping(self):
+        lostAndFoundBranches = set()
+
+        user = gitConfig("git-p4.branchUser")
+        if len(user) > 0:
+            command = "branches -u %s" % user
+        else:
+            command = "branches"
+
+        for info in p4CmdList(command):
+            details = p4Cmd(["branch", "-o", info["branch"]])
+            viewIdx = 0
+            while details.has_key("View%s" % viewIdx):
+                paths = details["View%s" % viewIdx].split(" ")
+                viewIdx = viewIdx + 1
+                # require standard //depot/foo/... //depot/bar/... mapping
+                if len(paths) != 2 or not paths[0].endswith("/...") or not paths[1].endswith("/..."):
+                    continue
+                source = paths[0]
+                destination = paths[1]
+                ## HACK
+                if p4PathStartsWith(source, self.depotPaths[0]) and p4PathStartsWith(destination, self.depotPaths[0]):
+                    source = source[len(self.depotPaths[0]):-4]
+                    destination = destination[len(self.depotPaths[0]):-4]
+
+                    if destination in self.knownBranches:
+                        if not self.silent:
+                            print "p4 branch %s defines a mapping from %s to %s" % (info["branch"], source, destination)
+                            print "but there exists another mapping from %s to %s already!" % (self.knownBranches[destination], destination)
+                        continue
+
+                    self.knownBranches[destination] = source
+
+                    lostAndFoundBranches.discard(destination)
+
+                    if source not in self.knownBranches:
+                        lostAndFoundBranches.add(source)
+
+        # Perforce does not strictly require branches to be defined, so we also
+        # check git config for a branch list.
+        #
+        # Example of branch definition in git config file:
+        # [git-p4]
+        #   branchList=main:branchA
+        #   branchList=main:branchB
+        #   branchList=branchA:branchC
+        configBranches = gitConfigList("git-p4.branchList")
+        for branch in configBranches:
+            if branch:
+                (source, destination) = branch.split(":")
+                self.knownBranches[destination] = source
+
+                lostAndFoundBranches.discard(destination)
+
+                if source not in self.knownBranches:
+                    lostAndFoundBranches.add(source)
+
+
+        for branch in lostAndFoundBranches:
+            self.knownBranches[branch] = branch
+
+    def getBranchMappingFromGitBranches(self):
+        branches = p4BranchesInGit(self.importIntoRemotes)
+        for branch in branches.keys():
+            if branch == "master":
+                branch = "main"
+            else:
+                branch = branch[len(self.projectName):]
+            self.knownBranches[branch] = branch
+
+    def listExistingP4GitBranches(self):
+        # branches holds mapping from name to commit
+        branches = p4BranchesInGit(self.importIntoRemotes)
+        self.p4BranchesInGit = branches.keys()
+        for branch in branches.keys():
+            self.initialParents[self.refPrefix + branch] = branches[branch]
+
+    def updateOptionDict(self, d):
+        option_keys = {}
+        if self.keepRepoPath:
+            option_keys['keepRepoPath'] = 1
+
+        d["options"] = ' '.join(sorted(option_keys.keys()))
+
+    def readOptions(self, d):
+        self.keepRepoPath = (d.has_key('options')
+                             and ('keepRepoPath' in d['options']))
+
+    def gitRefForBranch(self, branch):
+        if branch == "main":
+            return self.refPrefix + "master"
+
+        if len(branch) <= 0:
+            return branch
+
+        return self.refPrefix + self.projectName + branch
+
+    def gitCommitByP4Change(self, ref, change):
+        if self.verbose:
+            print "looking in ref " + ref + " for change %s using bisect..." % change
+
+        earliestCommit = ""
+        latestCommit = parseRevision(ref)
+
+        while True:
+            if self.verbose:
+                print "trying: earliest %s latest %s" % (earliestCommit, latestCommit)
+            next = read_pipe("git rev-list --bisect %s %s" % (latestCommit, earliestCommit)).strip()
+            if len(next) == 0:
+                if self.verbose:
+                    print "argh"
+                return ""
+            log = extractLogMessageFromGitCommit(next)
+            settings = extractSettingsGitLog(log)
+            currentChange = int(settings['change'])
+            if self.verbose:
+                print "current change %s" % currentChange
+
+            if currentChange == change:
+                if self.verbose:
+                    print "found %s" % next
+                return next
+
+            if currentChange < change:
+                earliestCommit = "^%s" % next
+            else:
+                latestCommit = "%s" % next
+
+        return ""
+
+    def importNewBranch(self, branch, maxChange):
+        # make fast-import flush all changes to disk and update the refs using the checkpoint
+        # command so that we can try to find the branch parent in the git history
+        self.gitStream.write("checkpoint\n\n");
+        self.gitStream.flush();
+        branchPrefix = self.depotPaths[0] + branch + "/"
+        range = "@1,%s" % maxChange
+        #print "prefix" + branchPrefix
+        changes = p4ChangesForPaths([branchPrefix], range)
+        if len(changes) <= 0:
+            return False
+        firstChange = changes[0]
+        #print "first change in branch: %s" % firstChange
+        sourceBranch = self.knownBranches[branch]
+        sourceDepotPath = self.depotPaths[0] + sourceBranch
+        sourceRef = self.gitRefForBranch(sourceBranch)
+        #print "source " + sourceBranch
+
+        branchParentChange = int(p4Cmd(["changes", "-m", "1", "%s...@1,%s" % (sourceDepotPath, firstChange)])["change"])
+        #print "branch parent: %s" % branchParentChange
+        gitParent = self.gitCommitByP4Change(sourceRef, branchParentChange)
+        if len(gitParent) > 0:
+            self.initialParents[self.gitRefForBranch(branch)] = gitParent
+            #print "parent git commit: %s" % gitParent
+
+        self.importChanges(changes)
+        return True
+
+    def searchParent(self, parent, branch, target):
+        parentFound = False
+        for blob in read_pipe_lines(["git", "rev-list", "--reverse", "--no-merges", parent]):
+            blob = blob.strip()
+            if len(read_pipe(["git", "diff-tree", blob, target])) == 0:
+                parentFound = True
+                if self.verbose:
+                    print "Found parent of %s in commit %s" % (branch, blob)
+                break
+        if parentFound:
+            return blob
+        else:
+            return None
+
+    def importChanges(self, changes):
+        cnt = 1
+        for change in changes:
+            description = p4Cmd(["describe", str(change)])
+            self.updateOptionDict(description)
+
+            if not self.silent:
+                sys.stdout.write("\rImporting revision %s (%s%%)" % (change, cnt * 100 / len(changes)))
+                sys.stdout.flush()
+            cnt = cnt + 1
+
+            try:
+                if self.detectBranches:
+                    branches = self.splitFilesIntoBranches(description)
+                    for branch in branches.keys():
+                        ## HACK  --hwn
+                        branchPrefix = self.depotPaths[0] + branch + "/"
+
+                        parent = ""
+
+                        filesForCommit = branches[branch]
+
+                        if self.verbose:
+                            print "branch is %s" % branch
+
+                        self.updatedBranches.add(branch)
+
+                        if branch not in self.createdBranches:
+                            self.createdBranches.add(branch)
+                            parent = self.knownBranches[branch]
+                            if parent == branch:
+                                parent = ""
+                            else:
+                                fullBranch = self.projectName + branch
+                                if fullBranch not in self.p4BranchesInGit:
+                                    if not self.silent:
+                                        print("\n    Importing new branch %s" % fullBranch);
+                                    if self.importNewBranch(branch, change - 1):
+                                        parent = ""
+                                        self.p4BranchesInGit.append(fullBranch)
+                                    if not self.silent:
+                                        print("\n    Resuming with change %s" % change);
+
+                                if self.verbose:
+                                    print "parent determined through known branches: %s" % parent
+
+                        branch = self.gitRefForBranch(branch)
+                        parent = self.gitRefForBranch(parent)
+
+                        if self.verbose:
+                            print "looking for initial parent for %s; current parent is %s" % (branch, parent)
+
+                        if len(parent) == 0 and branch in self.initialParents:
+                            parent = self.initialParents[branch]
+                            del self.initialParents[branch]
+
+                        blob = None
+                        if len(parent) > 0:
+                            tempBranch = os.path.join(self.tempBranchLocation, "%d" % (change))
+                            if self.verbose:
+                                print "Creating temporary branch: " + tempBranch
+                            self.commit(description, filesForCommit, tempBranch, [branchPrefix])
+                            self.tempBranches.append(tempBranch)
+                            self.checkpoint()
+                            blob = self.searchParent(parent, branch, tempBranch)
+                        if blob:
+                            self.commit(description, filesForCommit, branch, [branchPrefix], blob)
+                        else:
+                            if self.verbose:
+                                print "Parent of %s not found. Committing into head of %s" % (branch, parent)
+                            self.commit(description, filesForCommit, branch, [branchPrefix], parent)
+                else:
+                    files = self.extractFilesFromCommit(description)
+                    self.commit(description, files, self.branch, self.depotPaths,
+                                self.initialParent)
+                    self.initialParent = ""
+            except IOError:
+                print self.gitError.read()
+                sys.exit(1)
+
+    def importHeadRevision(self, revision):
+        print "Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), revision, self.branch)
+
+        details = {}
+        details["user"] = "git perforce import user"
+        details["desc"] = ("Initial import of %s from the state at revision %s\n"
+                           % (' '.join(self.depotPaths), revision))
+        details["change"] = revision
+        newestRevision = 0
+
+        fileCnt = 0
+        fileArgs = ["%s...%s" % (p,revision) for p in self.depotPaths]
+
+        for info in p4CmdList(["files"] + fileArgs):
+
+            if 'code' in info and info['code'] == 'error':
+                sys.stderr.write("p4 returned an error: %s\n"
+                                 % info['data'])
+                if info['data'].find("must refer to client") >= 0:
+                    sys.stderr.write("This particular p4 error is misleading.\n")
+                    sys.stderr.write("Perhaps the depot path was misspelled.\n");
+                    sys.stderr.write("Depot path:  %s\n" % " ".join(self.depotPaths))
+                sys.exit(1)
+            if 'p4ExitCode' in info:
+                sys.stderr.write("p4 exitcode: %s\n" % info['p4ExitCode'])
+                sys.exit(1)
+
+
+            change = int(info["change"])
+            if change > newestRevision:
+                newestRevision = change
+
+            if info["action"] in self.delete_actions:
+                # don't increase the file cnt, otherwise details["depotFile123"] will have gaps!
+                #fileCnt = fileCnt + 1
+                continue
+
+            for prop in ["depotFile", "rev", "action", "type" ]:
+                details["%s%s" % (prop, fileCnt)] = info[prop]
+
+            fileCnt = fileCnt + 1
+
+        details["change"] = newestRevision
+
+        # Use time from top-most change so that all git p4 clones of
+        # the same p4 repo have the same commit SHA1s.
+        res = p4CmdList("describe -s %d" % newestRevision)
+        newestTime = None
+        for r in res:
+            if r.has_key('time'):
+                newestTime = int(r['time'])
+        if newestTime is None:
+            die("\"describe -s\" on newest change %d did not give a time")
+        details["time"] = newestTime
+
+        self.updateOptionDict(details)
+        try:
+            self.commit(details, self.extractFilesFromCommit(details), self.branch, self.depotPaths)
+        except IOError:
+            print "IO error with git fast-import. Is your git version recent enough?"
+            print self.gitError.read()
+
+
+    def run(self, args):
+        self.depotPaths = []
+        self.changeRange = ""
+        self.initialParent = ""
+        self.previousDepotPaths = []
+
+        # map from branch depot path to parent branch
+        self.knownBranches = {}
+        self.initialParents = {}
+        self.hasOrigin = originP4BranchesExist()
+        if not self.syncWithOrigin:
+            self.hasOrigin = False
+
+        if self.importIntoRemotes:
+            self.refPrefix = "refs/remotes/p4/"
+        else:
+            self.refPrefix = "refs/heads/p4/"
+
+        if self.syncWithOrigin and self.hasOrigin:
+            if not self.silent:
+                print "Syncing with origin first by calling git fetch origin"
+            system("git fetch origin")
+
+        if len(self.branch) == 0:
+            self.branch = self.refPrefix + "master"
+            if gitBranchExists("refs/heads/p4") and self.importIntoRemotes:
+                system("git update-ref %s refs/heads/p4" % self.branch)
+                system("git branch -D p4");
+            # create it /after/ importing, when master exists
+            if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes and gitBranchExists(self.branch):
+                system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch))
+
+        # accept either the command-line option, or the configuration variable
+        if self.useClientSpec:
+            # will use this after clone to set the variable
+            self.useClientSpec_from_options = True
+        else:
+            if gitConfig("git-p4.useclientspec", "--bool") == "true":
+                self.useClientSpec = True
+        if self.useClientSpec:
+            self.clientSpecDirs = getClientSpec()
+
+        # TODO: should always look at previous commits,
+        # merge with previous imports, if possible.
+        if args == []:
+            if self.hasOrigin:
+                createOrUpdateBranchesFromOrigin(self.refPrefix, self.silent)
+            self.listExistingP4GitBranches()
+
+            if len(self.p4BranchesInGit) > 1:
+                if not self.silent:
+                    print "Importing from/into multiple branches"
+                self.detectBranches = True
+
+            if self.verbose:
+                print "branches: %s" % self.p4BranchesInGit
+
+            p4Change = 0
+            for branch in self.p4BranchesInGit:
+                logMsg =  extractLogMessageFromGitCommit(self.refPrefix + branch)
+
+                settings = extractSettingsGitLog(logMsg)
+
+                self.readOptions(settings)
+                if (settings.has_key('depot-paths')
+                    and settings.has_key ('change')):
+                    change = int(settings['change']) + 1
+                    p4Change = max(p4Change, change)
+
+                    depotPaths = sorted(settings['depot-paths'])
+                    if self.previousDepotPaths == []:
+                        self.previousDepotPaths = depotPaths
+                    else:
+                        paths = []
+                        for (prev, cur) in zip(self.previousDepotPaths, depotPaths):
+                            prev_list = prev.split("/")
+                            cur_list = cur.split("/")
+                            for i in range(0, min(len(cur_list), len(prev_list))):
+                                if cur_list[i] <> prev_list[i]:
+                                    i = i - 1
+                                    break
+
+                            paths.append ("/".join(cur_list[:i + 1]))
+
+                        self.previousDepotPaths = paths
+
+            if p4Change > 0:
+                self.depotPaths = sorted(self.previousDepotPaths)
+                self.changeRange = "@%s,#head" % p4Change
+                if not self.detectBranches:
+                    self.initialParent = parseRevision(self.branch)
+                if not self.silent and not self.detectBranches:
+                    print "Performing incremental import into %s git branch" % self.branch
+
+        if not self.branch.startswith("refs/"):
+            self.branch = "refs/heads/" + self.branch
+
+        if len(args) == 0 and self.depotPaths:
+            if not self.silent:
+                print "Depot paths: %s" % ' '.join(self.depotPaths)
+        else:
+            if self.depotPaths and self.depotPaths != args:
+                print ("previous import used depot path %s and now %s was specified. "
+                       "This doesn't work!" % (' '.join (self.depotPaths),
+                                               ' '.join (args)))
+                sys.exit(1)
+
+            self.depotPaths = sorted(args)
+
+        revision = ""
+        self.users = {}
+
+        # Make sure no revision specifiers are used when --changesfile
+        # is specified.
+        bad_changesfile = False
+        if len(self.changesFile) > 0:
+            for p in self.depotPaths:
+                if p.find("@") >= 0 or p.find("#") >= 0:
+                    bad_changesfile = True
+                    break
+        if bad_changesfile:
+            die("Option --changesfile is incompatible with revision specifiers")
+
+        newPaths = []
+        for p in self.depotPaths:
+            if p.find("@") != -1:
+                atIdx = p.index("@")
+                self.changeRange = p[atIdx:]
+                if self.changeRange == "@all":
+                    self.changeRange = ""
+                elif ',' not in self.changeRange:
+                    revision = self.changeRange
+                    self.changeRange = ""
+                p = p[:atIdx]
+            elif p.find("#") != -1:
+                hashIdx = p.index("#")
+                revision = p[hashIdx:]
+                p = p[:hashIdx]
+            elif self.previousDepotPaths == []:
+                # pay attention to changesfile, if given, else import
+                # the entire p4 tree at the head revision
+                if len(self.changesFile) == 0:
+                    revision = "#head"
+
+            p = re.sub ("\.\.\.$", "", p)
+            if not p.endswith("/"):
+                p += "/"
+
+            newPaths.append(p)
+
+        self.depotPaths = newPaths
+
+        self.loadUserMapFromCache()
+        self.labels = {}
+        if self.detectLabels:
+            self.getLabels();
+
+        if self.detectBranches:
+            ## FIXME - what's a P4 projectName ?
+            self.projectName = self.guessProjectName()
+
+            if self.hasOrigin:
+                self.getBranchMappingFromGitBranches()
+            else:
+                self.getBranchMapping()
+            if self.verbose:
+                print "p4-git branches: %s" % self.p4BranchesInGit
+                print "initial parents: %s" % self.initialParents
+            for b in self.p4BranchesInGit:
+                if b != "master":
+
+                    ## FIXME
+                    b = b[len(self.projectName):]
+                self.createdBranches.add(b)
+
+        self.tz = "%+03d%02d" % (- time.timezone / 3600, ((- time.timezone % 3600) / 60))
+
+        importProcess = subprocess.Popen(["git", "fast-import"],
+                                         stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+                                         stderr=subprocess.PIPE);
+        self.gitOutput = importProcess.stdout
+        self.gitStream = importProcess.stdin
+        self.gitError = importProcess.stderr
+
+        if revision:
+            self.importHeadRevision(revision)
+        else:
+            changes = []
+
+            if len(self.changesFile) > 0:
+                output = open(self.changesFile).readlines()
+                changeSet = set()
+                for line in output:
+                    changeSet.add(int(line))
+
+                for change in changeSet:
+                    changes.append(change)
+
+                changes.sort()
+            else:
+                # catch "git p4 sync" with no new branches, in a repo that
+                # does not have any existing p4 branches
+                if len(args) == 0 and not self.p4BranchesInGit:
+                    die("No remote p4 branches.  Perhaps you never did \"git p4 clone\" in here.");
+                if self.verbose:
+                    print "Getting p4 changes for %s...%s" % (', '.join(self.depotPaths),
+                                                              self.changeRange)
+                changes = p4ChangesForPaths(self.depotPaths, self.changeRange)
+
+                if len(self.maxChanges) > 0:
+                    changes = changes[:min(int(self.maxChanges), len(changes))]
+
+            if len(changes) == 0:
+                if not self.silent:
+                    print "No changes to import!"
+            else:
+                if not self.silent and not self.detectBranches:
+                    print "Import destination: %s" % self.branch
+
+                self.updatedBranches = set()
+
+                self.importChanges(changes)
+
+                if not self.silent:
+                    print ""
+                    if len(self.updatedBranches) > 0:
+                        sys.stdout.write("Updated branches: ")
+                        for b in self.updatedBranches:
+                            sys.stdout.write("%s " % b)
+                        sys.stdout.write("\n")
+
+        if gitConfig("git-p4.importLabels", "--bool") == "true":
+            self.importLabels = True
+
+        if self.importLabels:
+            p4Labels = getP4Labels(self.depotPaths)
+            gitTags = getGitTags()
+
+            missingP4Labels = p4Labels - gitTags
+            self.importP4Labels(self.gitStream, missingP4Labels)
+
+        self.gitStream.close()
+        if importProcess.wait() != 0:
+            die("fast-import failed: %s" % self.gitError.read())
+        self.gitOutput.close()
+        self.gitError.close()
+
+        # Cleanup temporary branches created during import
+        if self.tempBranches != []:
+            for branch in self.tempBranches:
+                read_pipe("git update-ref -d %s" % branch)
+            os.rmdir(os.path.join(os.environ.get("GIT_DIR", ".git"), self.tempBranchLocation))
+
+        return True
+
+class P4Rebase(Command):
+    def __init__(self):
+        Command.__init__(self)
+        self.options = [
+                optparse.make_option("--import-labels", dest="importLabels", action="store_true"),
+        ]
+        self.importLabels = False
+        self.description = ("Fetches the latest revision from perforce and "
+                            + "rebases the current work (branch) against it")
+
+    def run(self, args):
+        sync = P4Sync()
+        sync.importLabels = self.importLabels
+        sync.run([])
+
+        return self.rebase()
+
+    def rebase(self):
+        if os.system("git update-index --refresh") != 0:
+            die("Some files in your working directory are modified and different than what is in your index. You can use git update-index <filename> to bring the index up-to-date or stash away all your changes with git stash.");
+        if len(read_pipe("git diff-index HEAD --")) > 0:
+            die("You have uncommited changes. Please commit them before rebasing or stash them away with git stash.");
+
+        [upstream, settings] = findUpstreamBranchPoint()
+        if len(upstream) == 0:
+            die("Cannot find upstream branchpoint for rebase")
+
+        # the branchpoint may be p4/foo~3, so strip off the parent
+        upstream = re.sub("~[0-9]+$", "", upstream)
+
+        print "Rebasing the current branch onto %s" % upstream
+        oldHead = read_pipe("git rev-parse HEAD").strip()
+        system("git rebase %s" % upstream)
+        system("git diff-tree --stat --summary -M %s HEAD" % oldHead)
+        return True
+
+class P4Clone(P4Sync):
+    def __init__(self):
+        P4Sync.__init__(self)
+        self.description = "Creates a new git repository and imports from Perforce into it"
+        self.usage = "usage: %prog [options] //depot/path[@revRange]"
+        self.options += [
+            optparse.make_option("--destination", dest="cloneDestination",
+                                 action='store', default=None,
+                                 help="where to leave result of the clone"),
+            optparse.make_option("-/", dest="cloneExclude",
+                                 action="append", type="string",
+                                 help="exclude depot path"),
+            optparse.make_option("--bare", dest="cloneBare",
+                                 action="store_true", default=False),
+        ]
+        self.cloneDestination = None
+        self.needsGit = False
+        self.cloneBare = False
+
+    # This is required for the "append" cloneExclude action
+    def ensure_value(self, attr, value):
+        if not hasattr(self, attr) or getattr(self, attr) is None:
+            setattr(self, attr, value)
+        return getattr(self, attr)
+
+    def defaultDestination(self, args):
+        ## TODO: use common prefix of args?
+        depotPath = args[0]
+        depotDir = re.sub("(@[^@]*)$", "", depotPath)
+        depotDir = re.sub("(#[^#]*)$", "", depotDir)
+        depotDir = re.sub(r"\.\.\.$", "", depotDir)
+        depotDir = re.sub(r"/$", "", depotDir)
+        return os.path.split(depotDir)[1]
+
+    def run(self, args):
+        if len(args) < 1:
+            return False
+
+        if self.keepRepoPath and not self.cloneDestination:
+            sys.stderr.write("Must specify destination for --keep-path\n")
+            sys.exit(1)
+
+        depotPaths = args
+
+        if not self.cloneDestination and len(depotPaths) > 1:
+            self.cloneDestination = depotPaths[-1]
+            depotPaths = depotPaths[:-1]
+
+        self.cloneExclude = ["/"+p for p in self.cloneExclude]
+        for p in depotPaths:
+            if not p.startswith("//"):
+                return False
+
+        if not self.cloneDestination:
+            self.cloneDestination = self.defaultDestination(args)
+
+        print "Importing from %s into %s" % (', '.join(depotPaths), self.cloneDestination)
+
+        if not os.path.exists(self.cloneDestination):
+            os.makedirs(self.cloneDestination)
+        chdir(self.cloneDestination)
+
+        init_cmd = [ "git", "init" ]
+        if self.cloneBare:
+            init_cmd.append("--bare")
+        subprocess.check_call(init_cmd)
+
+        if not P4Sync.run(self, depotPaths):
+            return False
+        if self.branch != "master":
+            if self.importIntoRemotes:
+                masterbranch = "refs/remotes/p4/master"
+            else:
+                masterbranch = "refs/heads/p4/master"
+            if gitBranchExists(masterbranch):
+                system("git branch master %s" % masterbranch)
+                if not self.cloneBare:
+                    system("git checkout -f")
+            else:
+                print "Could not detect main branch. No checkout/master branch created."
+
+        # auto-set this variable if invoked with --use-client-spec
+        if self.useClientSpec_from_options:
+            system("git config --bool git-p4.useclientspec true")
+
+        return True
+
+class P4Branches(Command):
+    def __init__(self):
+        Command.__init__(self)
+        self.options = [ ]
+        self.description = ("Shows the git branches that hold imports and their "
+                            + "corresponding perforce depot paths")
+        self.verbose = False
+
+    def run(self, args):
+        if originP4BranchesExist():
+            createOrUpdateBranchesFromOrigin()
+
+        cmdline = "git rev-parse --symbolic "
+        cmdline += " --remotes"
+
+        for line in read_pipe_lines(cmdline):
+            line = line.strip()
+
+            if not line.startswith('p4/') or line == "p4/HEAD":
+                continue
+            branch = line
+
+            log = extractLogMessageFromGitCommit("refs/remotes/%s" % branch)
+            settings = extractSettingsGitLog(log)
+
+            print "%s <= %s (%s)" % (branch, ",".join(settings["depot-paths"]), settings["change"])
+        return True
+
+class HelpFormatter(optparse.IndentedHelpFormatter):
+    def __init__(self):
+        optparse.IndentedHelpFormatter.__init__(self)
+
+    def format_description(self, description):
+        if description:
+            return description + "\n"
+        else:
+            return ""
+
+def printUsage(commands):
+    print "usage: %s <command> [options]" % sys.argv[0]
+    print ""
+    print "valid commands: %s" % ", ".join(commands)
+    print ""
+    print "Try %s <command> --help for command specific help." % sys.argv[0]
+    print ""
+
+commands = {
+    "debug" : P4Debug,
+    "submit" : P4Submit,
+    "commit" : P4Submit,
+    "sync" : P4Sync,
+    "rebase" : P4Rebase,
+    "clone" : P4Clone,
+    "rollback" : P4RollBack,
+    "branches" : P4Branches
+}
+
+
+def main():
+    if len(sys.argv[1:]) == 0:
+        printUsage(commands.keys())
+        sys.exit(2)
+
+    cmd = ""
+    cmdName = sys.argv[1]
+    try:
+        klass = commands[cmdName]
+        cmd = klass()
+    except KeyError:
+        print "unknown command %s" % cmdName
+        print ""
+        printUsage(commands.keys())
+        sys.exit(2)
+
+    options = cmd.options
+    cmd.gitdir = os.environ.get("GIT_DIR", None)
+
+    args = sys.argv[2:]
+
+    options.append(optparse.make_option("--verbose", dest="verbose", action="store_true"))
+    if cmd.needsGit:
+        options.append(optparse.make_option("--git-dir", dest="gitdir"))
+
+    parser = optparse.OptionParser(cmd.usage.replace("%prog", "%prog " + cmdName),
+                                   options,
+                                   description = cmd.description,
+                                   formatter = HelpFormatter())
+
+    (cmd, args) = parser.parse_args(sys.argv[2:], cmd);
+    global verbose
+    verbose = cmd.verbose
+    if cmd.needsGit:
+        if cmd.gitdir == None:
+            cmd.gitdir = os.path.abspath(".git")
+            if not isValidGitDir(cmd.gitdir):
+                cmd.gitdir = read_pipe("git rev-parse --git-dir").strip()
+                if os.path.exists(cmd.gitdir):
+                    cdup = read_pipe("git rev-parse --show-cdup").strip()
+                    if len(cdup) > 0:
+                        chdir(cdup);
+
+        if not isValidGitDir(cmd.gitdir):
+            if isValidGitDir(cmd.gitdir + "/.git"):
+                cmd.gitdir += "/.git"
+            else:
+                die("fatal: cannot locate git repository at %s" % cmd.gitdir)
+
+        os.environ["GIT_DIR"] = cmd.gitdir
+
+    if not cmd.run(args):
+        parser.print_help()
+        sys.exit(2)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/git-parse-remote.sh b/git-parse-remote.sh
old mode 100755
new mode 100644
index 5f47b18..484b2e6
--- a/git-parse-remote.sh
+++ b/git-parse-remote.sh
@@ -4,58 +4,9 @@
 # this would fail in that case and would issue an error message.
 GIT_DIR=$(git rev-parse -q --git-dir) || :;
 
-get_data_source () {
-	case "$1" in
-	*/*)
-		echo ''
-		;;
-	.)
-		echo self
-		;;
-	*)
-		if test "$(git config --get "remote.$1.url")"
-		then
-			echo config
-		elif test -f "$GIT_DIR/remotes/$1"
-		then
-			echo remotes
-		elif test -f "$GIT_DIR/branches/$1"
-		then
-			echo branches
-		else
-			echo ''
-		fi ;;
-	esac
-}
-
-get_remote_url () {
-	data_source=$(get_data_source "$1")
-	case "$data_source" in
-	'')
-		echo "$1"
-		;;
-	self)
-		echo "$1"
-		;;
-	config)
-		git config --get "remote.$1.url"
-		;;
-	remotes)
-		sed -ne '/^URL: */{
-			s///p
-			q
-		}' "$GIT_DIR/remotes/$1"
-		;;
-	branches)
-		sed -e 's/#.*//' "$GIT_DIR/branches/$1"
-		;;
-	*)
-		die "internal error: get-remote-url $1" ;;
-	esac
-}
-
 get_default_remote () {
-	curr_branch=$(git symbolic-ref -q HEAD | sed -e 's|^refs/heads/||')
+	curr_branch=$(git symbolic-ref -q HEAD)
+	curr_branch="${curr_branch#refs/heads/}"
 	origin=$(git config --get "branch.$curr_branch.remote")
 	echo ${origin:-origin}
 }
@@ -66,7 +17,7 @@
 	    origin="$1"
 	    default=$(get_default_remote)
 	    test -z "$origin" && origin=$default
-	    curr_branch=$(git symbolic-ref -q HEAD)
+	    curr_branch=$(git symbolic-ref -q HEAD) &&
 	    [ "$origin" = "$default" ] &&
 	    echo $(git for-each-ref --format='%(upstream)' $curr_branch)
 	    ;;
@@ -89,7 +40,48 @@
 	    refs/heads/*) remote=${remote#refs/heads/} ;;
 	    refs/* | tags/* | remotes/* ) remote=
 	    esac
-
-	    [ -n "$remote" ] && echo "refs/remotes/$repo/$remote"
+	    [ -n "$remote" ] && case "$repo" in
+		.)
+		    echo "refs/heads/$remote"
+		    ;;
+		*)
+		    echo "refs/remotes/$repo/$remote"
+		    ;;
+	    esac
 	esac
 }
+
+error_on_missing_default_upstream () {
+	cmd="$1"
+	op_type="$2"
+	op_prep="$3"
+	example="$4"
+	branch_name=$(git symbolic-ref -q HEAD)
+	# If there's only one remote, use that in the suggestion
+	remote="<remote>"
+	if test $(git remote | wc -l) = 1
+	then
+		remote=$(git remote)
+	fi
+
+	if test -z "$branch_name"
+	then
+		echo "You are not currently on a branch. Please specify which
+branch you want to $op_type $op_prep. See git-${cmd}(1) for details.
+
+    $example
+"
+	else
+		echo "There is no tracking information for the current branch.
+Please specify which branch you want to $op_type $op_prep.
+See git-${cmd}(1) for details
+
+    $example
+
+If you wish to set tracking information for this branch you can do so with:
+
+    git branch --set-upstream ${branch_name#refs/heads/} $remote/<branch>
+"
+	fi
+	exit 1
+}
diff --git a/git-pull.sh b/git-pull.sh
index 0f24182..2a10047 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -9,17 +9,46 @@
 SUBDIRECTORY_OK=Yes
 OPTIONS_SPEC=
 . git-sh-setup
-set_reflog_action "pull $*"
-require_work_tree
+. git-sh-i18n
+set_reflog_action "pull${1+ $*}"
+require_work_tree_exists
 cd_to_toplevel
 
-test -z "$(git ls-files -u)" ||
-	die "You are in the middle of a conflicted merge."
 
-strategy_args= diffstat= no_commit= squash= no_ff= log_arg= verbosity=
+die_conflict () {
+    git diff-index --cached --name-status -r --ignore-submodules HEAD --
+    if [ $(git config --bool --get advice.resolveConflict || echo true) = "true" ]; then
+	die "$(gettext "Pull is not possible because you have unmerged files.
+Please, fix them up in the work tree, and then use 'git add/rm <file>'
+as appropriate to mark resolution, or use 'git commit -a'.")"
+    else
+	die "$(gettext "Pull is not possible because you have unmerged files.")"
+    fi
+}
+
+die_merge () {
+    if [ $(git config --bool --get advice.resolveConflict || echo true) = "true" ]; then
+	die "$(gettext "You have not concluded your merge (MERGE_HEAD exists).
+Please, commit your changes before you can merge.")"
+    else
+	die "$(gettext "You have not concluded your merge (MERGE_HEAD exists).")"
+    fi
+}
+
+test -z "$(git ls-files -u)" || die_conflict
+test -f "$GIT_DIR/MERGE_HEAD" && die_merge
+
+strategy_args= diffstat= no_commit= squash= no_ff= ff_only=
+log_arg= verbosity= progress= recurse_submodules=
+merge_args= edit=
 curr_branch=$(git symbolic-ref -q HEAD)
-curr_branch_short=$(echo "$curr_branch" | sed "s|refs/heads/||")
+curr_branch_short="${curr_branch#refs/heads/}"
 rebase=$(git config --bool branch.$curr_branch_short.rebase)
+if test -z "$rebase"
+then
+	rebase=$(git config --bool pull.rebase)
+fi
+dry_run=
 while :
 do
 	case "$1" in
@@ -27,6 +56,10 @@
 		verbosity="$verbosity -q" ;;
 	-v|--verbose)
 		verbosity="$verbosity -v" ;;
+	--progress)
+		progress=--progress ;;
+	--no-progress)
+		progress=--no-progress ;;
 	-n|--no-stat|--no-summary)
 		diffstat=--no-stat ;;
 	--stat|--summary)
@@ -37,6 +70,10 @@
 		no_commit=--no-commit ;;
 	--c|--co|--com|--comm|--commi|--commit)
 		no_commit=--commit ;;
+	-e|--edit)
+		edit=--edit ;;
+	--no-edit)
+		edit=--no-edit ;;
 	--sq|--squ|--squa|--squas|--squash)
 		squash=--squash ;;
 	--no-sq|--no-squ|--no-squa|--no-squas|--no-squash)
@@ -45,6 +82,8 @@
 		no_ff=--ff ;;
 	--no-ff)
 		no_ff=--no-ff ;;
+	--ff-only)
+		ff_only=--ff-only ;;
 	-s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\
 		--strateg=*|--strategy=*|\
 	-s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)
@@ -59,13 +98,37 @@
 		esac
 		strategy_args="${strategy_args}-s $strategy "
 		;;
+	-X*)
+		case "$#,$1" in
+		1,-X)
+			usage ;;
+		*,-X)
+			xx="-X $(git rev-parse --sq-quote "$2")"
+			shift ;;
+		*,*)
+			xx=$(git rev-parse --sq-quote "$1") ;;
+		esac
+		merge_args="$merge_args$xx "
+		;;
 	-r|--r|--re|--reb|--reba|--rebas|--rebase)
 		rebase=true
 		;;
 	--no-r|--no-re|--no-reb|--no-reba|--no-rebas|--no-rebase)
 		rebase=false
 		;;
-	-h|--h|--he|--hel|--help)
+	--recurse-submodules)
+		recurse_submodules=--recurse-submodules
+		;;
+	--recurse-submodules=*)
+		recurse_submodules="$1"
+		;;
+	--no-recurse-submodules)
+		recurse_submodules=--no-recurse-submodules
+		;;
+	--d|--dr|--dry|--dry-|--dry-r|--dry-ru|--dry-run)
+		dry_run=--dry-run
+		;;
+	-h|--help-all)
 		usage
 		;;
 	*)
@@ -88,42 +151,54 @@
 		esac
 	done
 
-	curr_branch=${curr_branch#refs/heads/}
-
-	if [ -z "$curr_branch" ]; then
-		echo "You are not currently on a branch, so I cannot use any"
-		echo "'branch.<branchname>.merge' in your configuration file."
-		echo "Please specify which branch you want to merge on the command"
-		echo "line and try again (e.g. 'git pull <repository> <refspec>')."
-		echo "See git-pull(1) for details."
+	if test true = "$rebase"
+	then
+		op_type=rebase
+		op_prep=against
 	else
-		echo "You asked me to pull without telling me which branch you"
-		echo "want to merge with, and 'branch.${curr_branch}.merge' in"
-		echo "your configuration file does not tell me either.	Please"
-		echo "specify which branch you want to merge on the command line and"
-		echo "try again (e.g. 'git pull <repository> <refspec>')."
-		echo "See git-pull(1) for details."
-		echo
-		echo "If you often merge with the same branch, you may want to"
-		echo "configure the following variables in your configuration"
-		echo "file:"
-		echo
-		echo "    branch.${curr_branch}.remote = <nickname>"
-		echo "    branch.${curr_branch}.merge = <remote-ref>"
-		echo "    remote.<nickname>.url = <url>"
-		echo "    remote.<nickname>.fetch = <refspec>"
-		echo
-		echo "See git-config(1) for details."
+		op_type=merge
+		op_prep=with
+	fi
+
+	curr_branch=${curr_branch#refs/heads/}
+	upstream=$(git config "branch.$curr_branch.merge")
+	remote=$(git config "branch.$curr_branch.remote")
+
+	if [ $# -gt 1 ]; then
+		if [ "$rebase" = true ]; then
+			printf "There is no candidate for rebasing against "
+		else
+			printf "There are no candidates for merging "
+		fi
+		echo "among the refs that you just fetched."
+		echo "Generally this means that you provided a wildcard refspec which had no"
+		echo "matches on the remote end."
+	elif [ $# -gt 0 ] && [ "$1" != "$remote" ]; then
+		echo "You asked to pull from the remote '$1', but did not specify"
+		echo "a branch. Because this is not the default configured remote"
+		echo "for your current branch, you must specify a branch on the command line."
+	elif [ -z "$curr_branch" -o -z "$upstream" ]; then
+		. git-parse-remote
+		error_on_missing_default_upstream "pull" $op_type $op_prep \
+			"git pull <remote> <branch>"
+	else
+		echo "Your configuration specifies to $op_type $op_prep the ref '${upstream#refs/heads/}'"
+		echo "from the remote, but no such ref was fetched."
 	fi
 	exit 1
 }
 
 test true = "$rebase" && {
-	git update-index --ignore-submodules --refresh &&
-	git diff-files --ignore-submodules --quiet &&
-	git diff-index --ignore-submodules --cached --quiet HEAD -- ||
-	die "refusing to pull with rebase: your working tree is not up-to-date"
-
+	if ! git rev-parse -q --verify HEAD >/dev/null
+	then
+		# On an unborn branch
+		if test -f "$GIT_DIR/index"
+		then
+			die "$(gettext "updating an unborn branch with changes added to the index")"
+		fi
+	else
+		require_clean_work_tree "pull with rebase" "Please commit or stash them."
+	fi
 	oldremoteref= &&
 	. git-parse-remote &&
 	remoteref="$(get_remote_merge_branch "$@" 2>/dev/null)" &&
@@ -138,7 +213,8 @@
 	done
 }
 orig_head=$(git rev-parse -q --verify HEAD)
-git fetch $verbosity --update-head-ok "$@" || exit 1
+git fetch $verbosity $progress $dry_run $recurse_submodules --update-head-ok "$@" || exit 1
+test -z "$dry_run" || exit 0
 
 curr_head=$(git rev-parse -q --verify HEAD)
 if test -n "$orig_head" && test "$curr_head" != "$orig_head"
@@ -149,17 +225,17 @@
 	# $orig_head commit, but we are merging into $curr_head.
 	# First update the working tree to match $curr_head.
 
-	echo >&2 "Warning: fetch updated the current branch head."
-	echo >&2 "Warning: fast forwarding your working tree from"
-	echo >&2 "Warning: commit $orig_head."
+	eval_gettextln "Warning: fetch updated the current branch head.
+Warning: fast-forwarding your working tree from
+Warning: commit \$orig_head." >&2
 	git update-index -q --refresh
 	git read-tree -u -m "$orig_head" "$curr_head" ||
-		die 'Cannot fast-forward your working tree.
+		die "$(eval_gettext "Cannot fast-forward your working tree.
 After making sure that you saved anything precious from
-$ git diff '$orig_head'
+$ git diff \$orig_head
 output, run
 $ git reset --hard
-to recover.'
+to recover.")"
 
 fi
 
@@ -169,23 +245,16 @@
 
 case "$merge_head" in
 '')
-	case $? in
-	0) error_on_no_merge_candidates "$@";;
-	1) echo >&2 "You are not currently on a branch; you must explicitly"
-	   echo >&2 "specify which branch you wish to merge:"
-	   echo >&2 "  git pull <remote> <branch>"
-	   exit 1;;
-	*) exit $?;;
-	esac
+	error_on_no_merge_candidates "$@"
 	;;
 ?*' '?*)
 	if test -z "$orig_head"
 	then
-		die "Cannot merge multiple branches into empty head"
+		die "$(gettext "Cannot merge multiple branches into empty head")"
 	fi
 	if test true = "$rebase"
 	then
-		die "Cannot rebase onto multiple branches"
+		die "$(gettext "Cannot rebase onto multiple branches")"
 	fi
 	;;
 esac
@@ -193,13 +262,29 @@
 if test -z "$orig_head"
 then
 	git update-ref -m "initial pull" HEAD $merge_head "$curr_head" &&
-	git read-tree --reset -u HEAD || exit 1
+	git read-tree -m -u HEAD || exit 1
 	exit
 fi
 
+if test true = "$rebase"
+then
+	o=$(git show-branch --merge-base $curr_branch $merge_head $oldremoteref)
+	if test "$oldremoteref" = "$o"
+	then
+		unset oldremoteref
+	fi
+fi
+
 merge_name=$(git fmt-merge-msg $log_arg <"$GIT_DIR/FETCH_HEAD") || exit
-test true = "$rebase" &&
-	exec git-rebase $diffstat $strategy_args --onto $merge_head \
-	${oldremoteref:-$merge_head}
-exec git-merge $diffstat $no_commit $squash $no_ff $log_arg $strategy_args \
-	"$merge_name" HEAD $merge_head $verbosity
+case "$rebase" in
+true)
+	eval="git-rebase $diffstat $strategy_args $merge_args"
+	eval="$eval --onto $merge_head ${oldremoteref:-$merge_head}"
+	;;
+*)
+	eval="git-merge $diffstat $no_commit $edit $squash $no_ff $ff_only"
+	eval="$eval  $log_arg $strategy_args $merge_args $verbosity $progress"
+	eval="$eval \"\$merge_name\" HEAD $merge_head"
+	;;
+esac
+eval "exec $eval"
diff --git a/git-rebase--am.sh b/git-rebase--am.sh
new file mode 100644
index 0000000..392ebc9
--- /dev/null
+++ b/git-rebase--am.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Junio C Hamano.
+#
+
+case "$action" in
+continue)
+	git am --resolved --resolvemsg="$resolvemsg" &&
+	move_to_original_branch
+	exit
+	;;
+skip)
+	git am --skip --resolvemsg="$resolvemsg" &&
+	move_to_original_branch
+	exit
+	;;
+esac
+
+test -n "$rebase_root" && root_flag=--root
+
+if test -n "$keep_empty"
+then
+	# we have to do this the hard way.  git format-patch completely squashes
+	# empty commits and even if it didn't the format doesn't really lend
+	# itself well to recording empty patches.  fortunately, cherry-pick
+	# makes this easy
+	git cherry-pick --allow-empty "$revisions"
+else
+	git format-patch -k --stdout --full-index --ignore-if-in-upstream \
+		--src-prefix=a/ --dst-prefix=b/ \
+		--no-renames $root_flag "$revisions" |
+	git am $git_am_opt --rebasing --resolvemsg="$resolvemsg"
+fi && move_to_original_branch
+
+ret=$?
+test 0 != $ret -a -d "$state_dir" && write_basic_state
+exit $ret
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
old mode 100755
new mode 100644
index 23ded48..bef7bc0
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -9,91 +9,90 @@
 #
 # The original idea comes from Eric W. Biederman, in
 # http://article.gmane.org/gmane.comp.version-control.git/22407
+#
+# The file containing rebase commands, comments, and empty lines.
+# This file is created by "git rebase -i" then edited by the user.  As
+# the lines are processed, they are removed from the front of this
+# file and written to the tail of $done.
+todo="$state_dir"/git-rebase-todo
 
-OPTIONS_KEEPDASHDASH=
-OPTIONS_SPEC="\
-git-rebase [-i] [options] [--] <upstream> [<branch>]
-git-rebase [-i] (--continue | --abort | --skip)
---
- Available options are
-v,verbose          display a diffstat of what changed upstream
-onto=              rebase onto given branch instead of upstream
-p,preserve-merges  try to recreate merges instead of ignoring them
-s,strategy=        use the given merge strategy
-m,merge            always used (no-op)
-i,interactive      always used (no-op)
- Actions:
-continue           continue rebasing process
-abort              abort rebasing process and restore original branch
-skip               skip current patch and continue rebasing process
-no-verify          override pre-rebase hook from stopping the operation
-root               rebase all reachable commmits up to the root(s)
-"
+# The rebase command lines that have already been processed.  A line
+# is moved here when it is first handled, before any associated user
+# actions.
+done="$state_dir"/done
 
-. git-sh-setup
-require_work_tree
+# The commit message that is planned to be used for any changes that
+# need to be committed following a user interaction.
+msg="$state_dir"/message
 
-DOTEST="$GIT_DIR/rebase-merge"
-TODO="$DOTEST"/git-rebase-todo
-DONE="$DOTEST"/done
-MSG="$DOTEST"/message
-SQUASH_MSG="$DOTEST"/message-squash
-REWRITTEN="$DOTEST"/rewritten
-DROPPED="$DOTEST"/dropped
-PRESERVE_MERGES=
-STRATEGY=
-ONTO=
-VERBOSE=
-OK_TO_SKIP_PRE_REBASE=
-REBASE_ROOT=
+# The file into which is accumulated the suggested commit message for
+# squash/fixup commands.  When the first of a series of squash/fixups
+# is seen, the file is created and the commit message from the
+# previous commit and from the first squash/fixup commit are written
+# to it.  The commit message for each subsequent squash/fixup commit
+# is appended to the file as it is processed.
+#
+# The first line of the file is of the form
+#     # This is a combination of $count commits.
+# where $count is the number of commits whose messages have been
+# written to the file so far (including the initial "pick" commit).
+# Each time that a commit message is processed, this line is read and
+# updated.  It is deleted just before the combined commit is made.
+squash_msg="$state_dir"/message-squash
 
-GIT_CHERRY_PICK_HELP="  After resolving the conflicts,
-mark the corrected paths with 'git add <paths>', and
-run 'git rebase --continue'"
+# If the current series of squash/fixups has not yet included a squash
+# command, then this file exists and holds the commit message of the
+# original "pick" commit.  (If the series ends without a "squash"
+# command, then this can be used as the commit message of the combined
+# commit without opening the editor.)
+fixup_msg="$state_dir"/message-fixup
+
+# $rewritten is the name of a directory containing files for each
+# commit that is reachable by at least one merge base of $head and
+# $upstream. They are not necessarily rewritten, but their children
+# might be.  This ensures that commits on merged, but otherwise
+# unrelated side branches are left alone. (Think "X" in the man page's
+# example.)
+rewritten="$state_dir"/rewritten
+
+dropped="$state_dir"/dropped
+
+# A script to set the GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and
+# GIT_AUTHOR_DATE that will be used for the commit that is currently
+# being rebased.
+author_script="$state_dir"/author-script
+
+# When an "edit" rebase command is being processed, the SHA1 of the
+# commit to be edited is recorded in this file.  When "git rebase
+# --continue" is executed, if there are any staged changes then they
+# will be amended to the HEAD commit, but only provided the HEAD
+# commit is still the commit to be edited.  When any other rebase
+# command is processed, this file is deleted.
+amend="$state_dir"/amend
+
+# For the post-rewrite hook, we make a list of rewritten commits and
+# their new sha1s.  The rewritten-pending list keeps the sha1s of
+# commits that have been processed, but not committed yet,
+# e.g. because they are waiting for a 'squash' command.
+rewritten_list="$state_dir"/rewritten-list
+rewritten_pending="$state_dir"/rewritten-pending
+
+GIT_CHERRY_PICK_HELP="$resolvemsg"
 export GIT_CHERRY_PICK_HELP
 
 warn () {
-	echo "$*" >&2
+	printf '%s\n' "$*" >&2
 }
 
-output () {
-	case "$VERBOSE" in
-	'')
-		output=$("$@" 2>&1 )
-		status=$?
-		test $status != 0 && printf "%s\n" "$output"
-		return $status
-		;;
-	*)
-		"$@"
-		;;
-	esac
+# Output the commit message for the specified commit.
+commit_message () {
+	git cat-file commit "$1" | sed "1,/^$/d"
 }
 
-run_pre_rebase_hook () {
-	if test -z "$OK_TO_SKIP_PRE_REBASE" &&
-	   test -x "$GIT_DIR/hooks/pre-rebase"
-	then
-		"$GIT_DIR/hooks/pre-rebase" ${1+"$@"} || {
-			echo >&2 "The pre-rebase hook refused to rebase."
-			exit 1
-		}
-	fi
-}
-
-require_clean_work_tree () {
-	# test if working tree is dirty
-	git rev-parse --verify HEAD > /dev/null &&
-	git update-index --ignore-submodules --refresh &&
-	git diff-files --quiet --ignore-submodules &&
-	git diff-index --cached --quiet HEAD --ignore-submodules -- ||
-	die "Working tree is dirty"
-}
-
-ORIG_REFLOG_ACTION="$GIT_REFLOG_ACTION"
+orig_reflog_action="$GIT_REFLOG_ACTION"
 
 comment_for_reflog () {
-	case "$ORIG_REFLOG_ACTION" in
+	case "$orig_reflog_action" in
 	''|rebase*)
 		GIT_REFLOG_ACTION="rebase -i ($1)"
 		export GIT_REFLOG_ACTION
@@ -103,16 +102,16 @@
 
 last_count=
 mark_action_done () {
-	sed -e 1q < "$TODO" >> "$DONE"
-	sed -e 1d < "$TODO" >> "$TODO".new
-	mv -f "$TODO".new "$TODO"
-	count=$(grep -c '^[^#]' < "$DONE")
-	total=$(($count+$(grep -c '^[^#]' < "$TODO")))
-	if test "$last_count" != "$count"
+	sed -e 1q < "$todo" >> "$done"
+	sed -e 1d < "$todo" >> "$todo".new
+	mv -f "$todo".new "$todo"
+	new_count=$(sane_grep -c '^[^#]' < "$done")
+	total=$(($new_count+$(sane_grep -c '^[^#]' < "$todo")))
+	if test "$last_count" != "$new_count"
 	then
-		last_count=$count
-		printf "Rebasing (%d/%d)\r" $count $total
-		test -z "$VERBOSE" || echo
+		last_count=$new_count
+		printf "Rebasing (%d/%d)\r" $new_count $total
+		test -z "$verbose" || echo
 	fi
 }
 
@@ -128,50 +127,89 @@
 	*)
 		echo "Root commit"
 		;;
-	esac > "$DOTEST"/patch
-	test -f "$DOTEST"/message ||
-		git cat-file commit "$1" | sed "1,/^$/d" > "$DOTEST"/message
-	test -f "$DOTEST"/author-script ||
-		get_author_ident_from_commit "$1" > "$DOTEST"/author-script
+	esac > "$state_dir"/patch
+	test -f "$msg" ||
+		commit_message "$1" > "$msg"
+	test -f "$author_script" ||
+		get_author_ident_from_commit "$1" > "$author_script"
 }
 
 die_with_patch () {
+	echo "$1" > "$state_dir"/stopped-sha
 	make_patch "$1"
 	git rerere
 	die "$2"
 }
 
+exit_with_patch () {
+	echo "$1" > "$state_dir"/stopped-sha
+	make_patch $1
+	git rev-parse --verify HEAD > "$amend"
+	warn "You can amend the commit now, with"
+	warn
+	warn "	git commit --amend"
+	warn
+	warn "Once you are satisfied with your changes, run"
+	warn
+	warn "	git rebase --continue"
+	warn
+	exit $2
+}
+
 die_abort () {
-	rm -rf "$DOTEST"
+	rm -rf "$state_dir"
 	die "$1"
 }
 
 has_action () {
-	grep '^[^#]' "$1" >/dev/null
+	sane_grep '^[^#]' "$1" >/dev/null
+}
+
+is_empty_commit() {
+	tree=$(git rev-parse -q --verify "$1"^{tree} 2>/dev/null ||
+		die "$1: not a commit that can be picked")
+	ptree=$(git rev-parse -q --verify "$1"^^{tree} 2>/dev/null ||
+		ptree=4b825dc642cb6eb9a060e54bf8d69288fbee4904)
+	test "$tree" = "$ptree"
+}
+
+# Run command with GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and
+# GIT_AUTHOR_DATE exported from the current environment.
+do_with_author () {
+	(
+		export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
+		"$@"
+	)
+}
+
+git_sequence_editor () {
+	if test -z "$GIT_SEQUENCE_EDITOR"
+	then
+		GIT_SEQUENCE_EDITOR="$(git config sequence.editor)"
+		if [ -z "$GIT_SEQUENCE_EDITOR" ]
+		then
+			GIT_SEQUENCE_EDITOR="$(git var GIT_EDITOR)" || return $?
+		fi
+	fi
+
+	eval "$GIT_SEQUENCE_EDITOR" '"$@"'
 }
 
 pick_one () {
-	no_ff=
-	case "$1" in -n) sha1=$2; no_ff=t ;; *) sha1=$1 ;; esac
+	ff=--ff
+
+	case "$1" in -n) sha1=$2; ff= ;; *) sha1=$1 ;; esac
+	case "$force_rebase" in '') ;; ?*) ff= ;; esac
 	output git rev-parse --verify $sha1 || die "Invalid commit name: $sha1"
-	test -d "$REWRITTEN" &&
-		pick_one_preserving_merges "$@" && return
-	if test ! -z "$REBASE_ROOT"
+
+	if is_empty_commit "$sha1"
 	then
-		output git cherry-pick "$@"
-		return
+		empty_args="--allow-empty"
 	fi
-	parent_sha1=$(git rev-parse --verify $sha1^) ||
-		die "Could not get the parent of $sha1"
-	current_sha1=$(git rev-parse --verify HEAD)
-	if test "$no_ff$current_sha1" = "$parent_sha1"; then
-		output git reset --hard $sha1
-		test "a$1" = a-n && output git reset --soft $current_sha1
-		sha1=$(git rev-parse --short $sha1)
-		output warn Fast forward to $sha1
-	else
-		output git cherry-pick "$@"
-	fi
+
+	test -d "$rewritten" &&
+		pick_one_preserving_merges "$@" && return
+	output git cherry-pick $empty_args $ff "$@"
 }
 
 pick_one_preserving_merges () {
@@ -187,20 +225,20 @@
 	esac
 	sha1=$(git rev-parse $sha1)
 
-	if test -f "$DOTEST"/current-commit
+	if test -f "$state_dir"/current-commit
 	then
 		if test "$fast_forward" = t
 		then
-			cat "$DOTEST"/current-commit | while read current_commit
+			while read current_commit
 			do
-				git rev-parse HEAD > "$REWRITTEN"/$current_commit
-			done
-			rm "$DOTEST"/current-commit ||
+				git rev-parse HEAD > "$rewritten"/$current_commit
+			done <"$state_dir"/current-commit
+			rm "$state_dir"/current-commit ||
 			die "Cannot write current commit's replacement sha1"
 		fi
 	fi
 
-	echo $sha1 >> "$DOTEST"/current-commit
+	echo $sha1 >> "$state_dir"/current-commit
 
 	# rewrite parents; if none were rewritten, we can fast-forward.
 	new_parents=
@@ -214,9 +252,9 @@
 		p=$(expr "$pend" : ' \([^ ]*\)')
 		pend="${pend# $p}"
 
-		if test -f "$REWRITTEN"/$p
+		if test -f "$rewritten"/$p
 		then
-			new_p=$(cat "$REWRITTEN"/$p)
+			new_p=$(cat "$rewritten"/$p)
 
 			# If the todo reordered commits, and our parent is marked for
 			# rewriting, but hasn't been gotten to yet, assume the user meant to
@@ -235,10 +273,10 @@
 				;;
 			esac
 		else
-			if test -f "$DROPPED"/$p
+			if test -f "$dropped"/$p
 			then
 				fast_forward=f
-				replacement="$(cat "$DROPPED"/$p)"
+				replacement="$(cat "$dropped"/$p)"
 				test -z "$replacement" && replacement=root
 				pend=" $replacement$pend"
 			else
@@ -248,9 +286,9 @@
 	done
 	case $fast_forward in
 	t)
-		output warn "Fast forward to $sha1"
+		output warn "Fast-forward to $sha1"
 		output git reset --hard $sha1 ||
-			die "Cannot fast forward to $sha1"
+			die "Cannot fast-forward to $sha1"
 		;;
 	f)
 		first_parent=$(expr "$new_parents" : ' \([^ ]*\)')
@@ -267,20 +305,19 @@
 			test "a$1" = a-n && die "Refusing to squash a merge: $sha1"
 
 			# redo merge
-			author_script=$(get_author_ident_from_commit $sha1)
-			eval "$author_script"
-			msg="$(git cat-file commit $sha1 | sed -e '1,/^$/d')"
+			author_script_content=$(get_author_ident_from_commit $sha1)
+			eval "$author_script_content"
+			msg_content="$(commit_message $sha1)"
 			# No point in merging the first parent, that's HEAD
 			new_parents=${new_parents# $first_parent}
-			if ! GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \
-				GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \
-				GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \
-				output git merge $STRATEGY -m "$msg" \
-					$new_parents
+			if ! do_with_author output \
+				git merge --no-ff ${strategy:+-s $strategy} -m \
+					"$msg_content" $new_parents
 			then
-				printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG
+				printf "%s\n" "$msg_content" > "$GIT_DIR"/MERGE_MSG
 				die_with_patch $sha1 "Error redoing merge $sha1"
 			fi
+			echo "$sha1 $(git rev-parse HEAD^0)" >> "$rewritten_list"
 			;;
 		*)
 			output git cherry-pick "$@" ||
@@ -300,35 +337,110 @@
 	esac
 }
 
-make_squash_message () {
-	if test -f "$SQUASH_MSG"; then
-		COUNT=$(($(sed -n "s/^# This is [^0-9]*\([1-9][0-9]*\).*/\1/p" \
-			< "$SQUASH_MSG" | sed -ne '$p')+1))
-		echo "# This is a combination of $COUNT commits."
-		sed -e 1d -e '2,/^./{
-			/^$/d
-		}' <"$SQUASH_MSG"
+update_squash_messages () {
+	if test -f "$squash_msg"; then
+		mv "$squash_msg" "$squash_msg".bak || exit
+		count=$(($(sed -n \
+			-e "1s/^# This is a combination of \(.*\) commits\./\1/p" \
+			-e "q" < "$squash_msg".bak)+1))
+		{
+			echo "# This is a combination of $count commits."
+			sed -e 1d -e '2,/^./{
+				/^$/d
+			}' <"$squash_msg".bak
+		} >"$squash_msg"
 	else
-		COUNT=2
-		echo "# This is a combination of two commits."
-		echo "# The first commit's message is:"
-		echo
-		git cat-file commit HEAD | sed -e '1,/^$/d'
+		commit_message HEAD > "$fixup_msg" || die "Cannot write $fixup_msg"
+		count=2
+		{
+			echo "# This is a combination of 2 commits."
+			echo "# The first commit's message is:"
+			echo
+			cat "$fixup_msg"
+		} >"$squash_msg"
 	fi
-	echo
-	echo "# This is the $(nth_string $COUNT) commit message:"
-	echo
-	git cat-file commit $1 | sed -e '1,/^$/d'
+	case $1 in
+	squash)
+		rm -f "$fixup_msg"
+		echo
+		echo "# This is the $(nth_string $count) commit message:"
+		echo
+		commit_message $2
+		;;
+	fixup)
+		echo
+		echo "# The $(nth_string $count) commit message will be skipped:"
+		echo
+		commit_message $2 | sed -e 's/^/#	/'
+		;;
+	esac >>"$squash_msg"
 }
 
 peek_next_command () {
-	sed -n "1s/ .*$//p" < "$TODO"
+	sed -n -e "/^#/d" -e '/^$/d' -e "s/ .*//p" -e "q" < "$todo"
+}
+
+# A squash/fixup has failed.  Prepare the long version of the squash
+# commit message, then die_with_patch.  This code path requires the
+# user to edit the combined commit message for all commits that have
+# been squashed/fixedup so far.  So also erase the old squash
+# messages, effectively causing the combined commit to be used as the
+# new basis for any further squash/fixups.  Args: sha1 rest
+die_failed_squash() {
+	mv "$squash_msg" "$msg" || exit
+	rm -f "$fixup_msg"
+	cp "$msg" "$GIT_DIR"/MERGE_MSG || exit
+	warn
+	warn "Could not apply $1... $2"
+	die_with_patch $1 ""
+}
+
+flush_rewritten_pending() {
+	test -s "$rewritten_pending" || return
+	newsha1="$(git rev-parse HEAD^0)"
+	sed "s/$/ $newsha1/" < "$rewritten_pending" >> "$rewritten_list"
+	rm -f "$rewritten_pending"
+}
+
+record_in_rewritten() {
+	oldsha1="$(git rev-parse $1)"
+	echo "$oldsha1" >> "$rewritten_pending"
+
+	case "$(peek_next_command)" in
+	squash|s|fixup|f)
+		;;
+	*)
+		flush_rewritten_pending
+		;;
+	esac
+}
+
+do_pick () {
+	if test "$(git rev-parse HEAD)" = "$squash_onto"
+	then
+		# Set the correct commit message and author info on the
+		# sentinel root before cherry-picking the original changes
+		# without committing (-n).  Finally, update the sentinel again
+		# to include these changes.  If the cherry-pick results in a
+		# conflict, this means our behaviour is similar to a standard
+		# failed cherry-pick during rebase, with a dirty index to
+		# resolve before manually running git commit --amend then git
+		# rebase --continue.
+		git commit --allow-empty --allow-empty-message --amend \
+			   --no-post-rewrite -n -q -C $1 &&
+			pick_one -n $1 &&
+			git commit --allow-empty --allow-empty-message \
+				   --amend --no-post-rewrite -n -q -C $1 ||
+			die_with_patch $1 "Could not apply $1... $2"
+	else
+		pick_one $1 ||
+			die_with_patch $1 "Could not apply $1... $2"
+	fi
 }
 
 do_next () {
-	rm -f "$DOTEST"/message "$DOTEST"/author-script \
-		"$DOTEST"/amend || exit
-	read command sha1 rest < "$TODO"
+	rm -f "$msg" "$author_script" "$amend" || exit
+	read -r command sha1 rest < "$todo"
 	case "$command" in
 	'#'*|''|noop)
 		mark_action_done
@@ -337,100 +449,150 @@
 		comment_for_reflog pick
 
 		mark_action_done
-		pick_one $sha1 ||
-			die_with_patch $sha1 "Could not apply $sha1... $rest"
+		do_pick $sha1 "$rest"
+		record_in_rewritten $sha1
+		;;
+	reword|r)
+		comment_for_reflog reword
+
+		mark_action_done
+		do_pick $sha1 "$rest"
+		git commit --amend --no-post-rewrite || {
+			warn "Could not amend commit after successfully picking $sha1... $rest"
+			warn "This is most likely due to an empty commit message, or the pre-commit hook"
+			warn "failed. If the pre-commit hook failed, you may need to resolve the issue before"
+			warn "you are able to reword the commit."
+			exit_with_patch $sha1 1
+		}
+		record_in_rewritten $sha1
 		;;
 	edit|e)
 		comment_for_reflog edit
 
 		mark_action_done
-		pick_one $sha1 ||
-			die_with_patch $sha1 "Could not apply $sha1... $rest"
-		make_patch $sha1
-		git rev-parse --verify HEAD > "$DOTEST"/amend
+		do_pick $sha1 "$rest"
 		warn "Stopped at $sha1... $rest"
-		warn "You can amend the commit now, with"
-		warn
-		warn "	git commit --amend"
-		warn
-		warn "Once you are satisfied with your changes, run"
-		warn
-		warn "	git rebase --continue"
-		warn
-		exit 0
+		exit_with_patch $sha1 0
 		;;
-	squash|s)
-		comment_for_reflog squash
-
-		test -f "$DONE" && has_action "$DONE" ||
-			die "Cannot 'squash' without a previous commit"
-
-		mark_action_done
-		make_squash_message $sha1 > "$MSG"
-		failed=f
-		author_script=$(get_author_ident_from_commit HEAD)
-		output git reset --soft HEAD^
-		pick_one -n $sha1 || failed=t
-		case "$(peek_next_command)" in
+	squash|s|fixup|f)
+		case "$command" in
 		squash|s)
-			USE_OUTPUT=output
-			MSG_OPT=-F
-			EDIT_OR_FILE="$MSG"
-			cp "$MSG" "$SQUASH_MSG"
+			squash_style=squash
 			;;
-		*)
-			USE_OUTPUT=
-			MSG_OPT=
-			EDIT_OR_FILE=-e
-			rm -f "$SQUASH_MSG" || exit
-			cp "$MSG" "$GIT_DIR"/SQUASH_MSG
-			rm -f "$GIT_DIR"/MERGE_MSG || exit
+		fixup|f)
+			squash_style=fixup
 			;;
 		esac
-		echo "$author_script" > "$DOTEST"/author-script
-		if test $failed = f
+		comment_for_reflog $squash_style
+
+		test -f "$done" && has_action "$done" ||
+			die "Cannot '$squash_style' without a previous commit"
+
+		mark_action_done
+		update_squash_messages $squash_style $sha1
+		author_script_content=$(get_author_ident_from_commit HEAD)
+		echo "$author_script_content" > "$author_script"
+		eval "$author_script_content"
+		output git reset --soft HEAD^
+		pick_one -n $sha1 || die_failed_squash $sha1 "$rest"
+		case "$(peek_next_command)" in
+		squash|s|fixup|f)
+			# This is an intermediate commit; its message will only be
+			# used in case of trouble.  So use the long version:
+			do_with_author output git commit --no-verify -F "$squash_msg" ||
+				die_failed_squash $sha1 "$rest"
+			;;
+		*)
+			# This is the final command of this squash/fixup group
+			if test -f "$fixup_msg"
+			then
+				do_with_author git commit --no-verify -F "$fixup_msg" ||
+					die_failed_squash $sha1 "$rest"
+			else
+				cp "$squash_msg" "$GIT_DIR"/SQUASH_MSG || exit
+				rm -f "$GIT_DIR"/MERGE_MSG
+				do_with_author git commit --no-verify -e ||
+					die_failed_squash $sha1 "$rest"
+			fi
+			rm -f "$squash_msg" "$fixup_msg"
+			;;
+		esac
+		record_in_rewritten $sha1
+		;;
+	x|"exec")
+		read -r command rest < "$todo"
+		mark_action_done
+		printf 'Executing: %s\n' "$rest"
+		# "exec" command doesn't take a sha1 in the todo-list.
+		# => can't just use $sha1 here.
+		git rev-parse --verify HEAD > "$state_dir"/stopped-sha
+		${SHELL:-@SHELL_PATH@} -c "$rest" # Actual execution
+		status=$?
+		# Run in subshell because require_clean_work_tree can die.
+		dirty=f
+		(require_clean_work_tree "rebase" 2>/dev/null) || dirty=t
+		if test "$status" -ne 0
 		then
-			# This is like --amend, but with a different message
-			eval "$author_script"
-			GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \
-			GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \
-			GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \
-			$USE_OUTPUT git commit --no-verify \
-				$MSG_OPT "$EDIT_OR_FILE" || failed=t
-		fi
-		if test $failed = t
-		then
-			cp "$MSG" "$GIT_DIR"/MERGE_MSG
+			warn "Execution failed: $rest"
+			test "$dirty" = f ||
+			warn "and made changes to the index and/or the working tree"
+
+			warn "You can fix the problem, and then run"
 			warn
-			warn "Could not apply $sha1... $rest"
-			die_with_patch $sha1 ""
+			warn "	git rebase --continue"
+			warn
+			exit "$status"
+		elif test "$dirty" = t
+		then
+			warn "Execution succeeded: $rest"
+			warn "but left changes to the index and/or the working tree"
+			warn "Commit or stash your changes, and then run"
+			warn
+			warn "	git rebase --continue"
+			warn
+			exit 1
 		fi
 		;;
 	*)
 		warn "Unknown command: $command $sha1 $rest"
-		die_with_patch $sha1 "Please fix this in the file $TODO."
+		if git rev-parse --verify -q "$sha1" >/dev/null
+		then
+			die_with_patch $sha1 "Please fix this in the file $todo."
+		else
+			die "Please fix this in the file $todo."
+		fi
 		;;
 	esac
-	test -s "$TODO" && return
+	test -s "$todo" && return
 
 	comment_for_reflog finish &&
-	HEADNAME=$(cat "$DOTEST"/head-name) &&
-	OLDHEAD=$(cat "$DOTEST"/head) &&
-	SHORTONTO=$(git rev-parse --short $(cat "$DOTEST"/onto)) &&
-	NEWHEAD=$(git rev-parse HEAD) &&
-	case $HEADNAME in
+	shortonto=$(git rev-parse --short $onto) &&
+	newhead=$(git rev-parse HEAD) &&
+	case $head_name in
 	refs/*)
-		message="$GIT_REFLOG_ACTION: $HEADNAME onto $SHORTONTO" &&
-		git update-ref -m "$message" $HEADNAME $NEWHEAD $OLDHEAD &&
-		git symbolic-ref HEAD $HEADNAME
+		message="$GIT_REFLOG_ACTION: $head_name onto $shortonto" &&
+		git update-ref -m "$message" $head_name $newhead $orig_head &&
+		git symbolic-ref \
+		  -m "$GIT_REFLOG_ACTION: returning to $head_name" \
+		  HEAD $head_name
 		;;
 	esac && {
-		test ! -f "$DOTEST"/verbose ||
-			git diff-tree --stat $(cat "$DOTEST"/head)..HEAD
+		test ! -f "$state_dir"/verbose ||
+			git diff-tree --stat $orig_head..HEAD
 	} &&
-	rm -rf "$DOTEST" &&
+	{
+		test -s "$rewritten_list" &&
+		git notes copy --for-rewrite=rebase < "$rewritten_list" ||
+		true # we don't care if this copying failed
+	} &&
+	if test -x "$GIT_DIR"/hooks/post-rewrite &&
+		test -s "$rewritten_list"; then
+		"$GIT_DIR"/hooks/post-rewrite rebase < "$rewritten_list"
+		true # we don't care if this hook failed
+	fi &&
+	rm -rf "$state_dir" &&
 	git gc --auto &&
-	warn "Successfully rebased and updated $HEADNAME."
+	warn "Successfully rebased and updated $head_name."
 
 	exit
 }
@@ -445,336 +607,334 @@
 # skip picking commits whose parents are unchanged
 skip_unnecessary_picks () {
 	fd=3
-	while read command sha1 rest
+	while read -r command rest
 	do
 		# fd=3 means we skip the command
-		case "$fd,$command,$(git rev-parse --verify --quiet $sha1^)" in
-		3,pick,"$ONTO"*|3,p,"$ONTO"*)
-			# pick a commit whose parent is current $ONTO -> skip
-			ONTO=$sha1
+		case "$fd,$command" in
+		3,pick|3,p)
+			# pick a commit whose parent is current $onto -> skip
+			sha1=${rest%% *}
+			case "$(git rev-parse --verify --quiet "$sha1"^)" in
+			"$onto"*)
+				onto=$sha1
+				;;
+			*)
+				fd=1
+				;;
+			esac
 			;;
-		3,#*|3,,*)
+		3,#*|3,)
 			# copy comments
 			;;
 		*)
 			fd=1
 			;;
 		esac
-		echo "$command${sha1:+ }$sha1${rest:+ }$rest" >&$fd
-	done <"$TODO" >"$TODO.new" 3>>"$DONE" &&
-	mv -f "$TODO".new "$TODO" ||
+		printf '%s\n' "$command${rest:+ }$rest" >&$fd
+	done <"$todo" >"$todo.new" 3>>"$done" &&
+	mv -f "$todo".new "$todo" &&
+	case "$(peek_next_command)" in
+	squash|s|fixup|f)
+		record_in_rewritten "$onto"
+		;;
+	esac ||
 	die "Could not skip unnecessary pick commands"
 }
 
-# check if no other options are set
-is_standalone () {
-	test $# -eq 2 -a "$2" = '--' &&
-	test -z "$ONTO" &&
-	test -z "$PRESERVE_MERGES" &&
-	test -z "$STRATEGY" &&
-	test -z "$VERBOSE"
+# Rearrange the todo list that has both "pick sha1 msg" and
+# "pick sha1 fixup!/squash! msg" appears in it so that the latter
+# comes immediately after the former, and change "pick" to
+# "fixup"/"squash".
+rearrange_squash () {
+	# extract fixup!/squash! lines and resolve any referenced sha1's
+	while read -r pick sha1 message
+	do
+		case "$message" in
+		"squash! "*|"fixup! "*)
+			action="${message%%!*}"
+			rest="${message#*! }"
+			echo "$sha1 $action $rest"
+			# if it's a single word, try to resolve to a full sha1 and
+			# emit a second copy. This allows us to match on both message
+			# and on sha1 prefix
+			if test "${rest#* }" = "$rest"; then
+				fullsha="$(git rev-parse -q --verify "$rest" 2>/dev/null)"
+				if test -n "$fullsha"; then
+					# prefix the action to uniquely identify this line as
+					# intended for full sha1 match
+					echo "$sha1 +$action $fullsha"
+				fi
+			fi
+		esac
+	done >"$1.sq" <"$1"
+	test -s "$1.sq" || return
+
+	used=
+	while read -r pick sha1 message
+	do
+		case " $used" in
+		*" $sha1 "*) continue ;;
+		esac
+		printf '%s\n' "$pick $sha1 $message"
+		used="$used$sha1 "
+		while read -r squash action msg_content
+		do
+			case " $used" in
+			*" $squash "*) continue ;;
+			esac
+			emit=0
+			case "$action" in
+			+*)
+				action="${action#+}"
+				# full sha1 prefix test
+				case "$msg_content" in "$sha1"*) emit=1;; esac ;;
+			*)
+				# message prefix test
+				case "$message" in "$msg_content"*) emit=1;; esac ;;
+			esac
+			if test $emit = 1; then
+				printf '%s\n' "$action $squash $action! $msg_content"
+				used="$used$squash "
+			fi
+		done <"$1.sq"
+	done >"$1.rearranged" <"$1"
+	cat "$1.rearranged" >"$1"
+	rm -f "$1.sq" "$1.rearranged"
 }
 
-get_saved_options () {
-	test -d "$REWRITTEN" && PRESERVE_MERGES=t
-	test -f "$DOTEST"/strategy && STRATEGY="$(cat "$DOTEST"/strategy)"
-	test -f "$DOTEST"/verbose && VERBOSE=t
-	test -f "$DOTEST"/rebase-root && REBASE_ROOT=t
+# Add commands after a pick or after a squash/fixup serie
+# in the todo list.
+add_exec_commands () {
+	{
+		first=t
+		while read -r insn rest
+		do
+			case $insn in
+			pick)
+				test -n "$first" ||
+				printf "%s" "$cmd"
+				;;
+			esac
+			printf "%s %s\n" "$insn" "$rest"
+			first=
+		done
+		printf "%s" "$cmd"
+	} <"$1" >"$1.new" &&
+	mv "$1.new" "$1"
 }
 
-while test $# != 0
-do
-	case "$1" in
-	--no-verify)
-		OK_TO_SKIP_PRE_REBASE=yes
-		;;
-	--verify)
-		;;
-	--continue)
-		is_standalone "$@" || usage
-		get_saved_options
-		comment_for_reflog continue
-
-		test -d "$DOTEST" || die "No interactive rebase running"
-
-		# Sanity check
-		git rev-parse --verify HEAD >/dev/null ||
-			die "Cannot read HEAD"
-		git update-index --ignore-submodules --refresh &&
-			git diff-files --quiet --ignore-submodules ||
-			die "Working tree is dirty"
-
-		# do we have anything to commit?
-		if git diff-index --cached --quiet --ignore-submodules HEAD --
+case "$action" in
+continue)
+	# do we have anything to commit?
+	if git diff-index --cached --quiet HEAD --
+	then
+		: Nothing to commit -- skip this
+	else
+		if ! test -f "$author_script"
 		then
-			: Nothing to commit -- skip this
-		else
-			. "$DOTEST"/author-script ||
-				die "Cannot find the author identity"
-			amend=
-			if test -f "$DOTEST"/amend
-			then
-				amend=$(git rev-parse --verify HEAD)
-				test "$amend" = $(cat "$DOTEST"/amend) ||
-				die "\
+			die "You have staged changes in your working tree. If these changes are meant to be
+squashed into the previous commit, run:
+
+  git commit --amend
+
+If they are meant to go into a new commit, run:
+
+  git commit
+
+In both case, once you're done, continue with:
+
+  git rebase --continue
+"
+		fi
+		. "$author_script" ||
+			die "Error trying to find the author identity to amend commit"
+		current_head=
+		if test -f "$amend"
+		then
+			current_head=$(git rev-parse --verify HEAD)
+			test "$current_head" = $(cat "$amend") ||
+			die "\
 You have uncommitted changes in your working tree. Please, commit them
 first and then run 'git rebase --continue' again."
-				git reset --soft HEAD^ ||
-				die "Cannot rewind the HEAD"
-			fi
-			export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE &&
-			git commit --no-verify -F "$DOTEST"/message -e || {
-				test -n "$amend" && git reset --soft $amend
-				die "Could not commit staged changes."
-			}
+			git reset --soft HEAD^ ||
+			die "Cannot rewind the HEAD"
 		fi
+		do_with_author git commit --no-verify -F "$msg" -e || {
+			test -n "$current_head" && git reset --soft $current_head
+			die "Could not commit staged changes."
+		}
+	fi
 
-		require_clean_work_tree
-		do_rest
-		;;
-	--abort)
-		is_standalone "$@" || usage
-		get_saved_options
-		comment_for_reflog abort
+	record_in_rewritten "$(cat "$state_dir"/stopped-sha)"
 
-		git rerere clear
-		test -d "$DOTEST" || die "No interactive rebase running"
+	require_clean_work_tree "rebase"
+	do_rest
+	;;
+skip)
+	git rerere clear
 
-		HEADNAME=$(cat "$DOTEST"/head-name)
-		HEAD=$(cat "$DOTEST"/head)
-		case $HEADNAME in
-		refs/*)
-			git symbolic-ref HEAD $HEADNAME
-			;;
-		esac &&
-		output git reset --hard $HEAD &&
-		rm -rf "$DOTEST"
-		exit
-		;;
-	--skip)
-		is_standalone "$@" || usage
-		get_saved_options
-		comment_for_reflog skip
+	do_rest
+	;;
+esac
 
-		git rerere clear
-		test -d "$DOTEST" || die "No interactive rebase running"
+git var GIT_COMMITTER_IDENT >/dev/null ||
+	die "You need to set your committer info first"
 
-		output git reset --hard && do_rest
-		;;
-	-s)
-		case "$#,$1" in
-		*,*=*)
-			STRATEGY="-s "$(expr "z$1" : 'z-[^=]*=\(.*\)') ;;
-		1,*)
-			usage ;;
-		*)
-			STRATEGY="-s $2"
-			shift ;;
-		esac
-		;;
-	-m)
-		# we use merge anyway
-		;;
-	-v)
-		VERBOSE=t
-		;;
-	-p)
-		PRESERVE_MERGES=t
-		;;
-	-i)
-		# yeah, we know
-		;;
-	--root)
-		REBASE_ROOT=t
-		;;
-	--onto)
-		shift
-		ONTO=$(git rev-parse --verify "$1") ||
-			die "Does not point to a valid commit: $1"
-		;;
-	--)
-		shift
-		test -z "$REBASE_ROOT" -a $# -ge 1 -a $# -le 2 ||
-		test ! -z "$REBASE_ROOT" -a $# -le 1 || usage
-		test -d "$DOTEST" &&
-			die "Interactive rebase already started"
+comment_for_reflog start
 
-		git var GIT_COMMITTER_IDENT >/dev/null ||
-			die "You need to set your committer info first"
+if test ! -z "$switch_to"
+then
+	output git checkout "$switch_to" -- ||
+		die "Could not checkout $switch_to"
+fi
 
-		if test -z "$REBASE_ROOT"
-		then
-			UPSTREAM_ARG="$1"
-			UPSTREAM=$(git rev-parse --verify "$1") || die "Invalid base"
-			test -z "$ONTO" && ONTO=$UPSTREAM
-			shift
-		else
-			UPSTREAM=
-			UPSTREAM_ARG=--root
-			test -z "$ONTO" &&
-				die "You must specify --onto when using --root"
-		fi
-		run_pre_rebase_hook "$UPSTREAM_ARG" "$@"
+orig_head=$(git rev-parse --verify HEAD) || die "No HEAD?"
+mkdir "$state_dir" || die "Could not create temporary $state_dir"
 
-		comment_for_reflog start
-
-		require_clean_work_tree
-
-		if test ! -z "$1"
-		then
-			output git show-ref --verify --quiet "refs/heads/$1" ||
-				die "Invalid branchname: $1"
-			output git checkout "$1" ||
-				die "Could not checkout $1"
-		fi
-
-		HEAD=$(git rev-parse --verify HEAD) || die "No HEAD?"
-		mkdir "$DOTEST" || die "Could not create temporary $DOTEST"
-
-		: > "$DOTEST"/interactive || die "Could not mark as interactive"
-		git symbolic-ref HEAD > "$DOTEST"/head-name 2> /dev/null ||
-			echo "detached HEAD" > "$DOTEST"/head-name
-
-		echo $HEAD > "$DOTEST"/head
-		case "$REBASE_ROOT" in
-		'')
-			rm -f "$DOTEST"/rebase-root ;;
-		*)
-			: >"$DOTEST"/rebase-root ;;
-		esac
-		echo $ONTO > "$DOTEST"/onto
-		test -z "$STRATEGY" || echo "$STRATEGY" > "$DOTEST"/strategy
-		test t = "$VERBOSE" && : > "$DOTEST"/verbose
-		if test t = "$PRESERVE_MERGES"
-		then
-			# $REWRITTEN contains files for each commit that is
-			# reachable by at least one merge base of $HEAD and
-			# $UPSTREAM. They are not necessarily rewritten, but
-			# their children might be.
-			# This ensures that commits on merged, but otherwise
-			# unrelated side branches are left alone. (Think "X"
-			# in the man page's example.)
-			if test -z "$REBASE_ROOT"
-			then
-				mkdir "$REWRITTEN" &&
-				for c in $(git merge-base --all $HEAD $UPSTREAM)
-				do
-					echo $ONTO > "$REWRITTEN"/$c ||
-						die "Could not init rewritten commits"
-				done
-			else
-				mkdir "$REWRITTEN" &&
-				echo $ONTO > "$REWRITTEN"/root ||
-					die "Could not init rewritten commits"
-			fi
-			# No cherry-pick because our first pass is to determine
-			# parents to rewrite and skipping dropped commits would
-			# prematurely end our probe
-			MERGES_OPTION=
-			first_after_upstream="$(git rev-list --reverse --first-parent $UPSTREAM..$HEAD | head -n 1)"
-		else
-			MERGES_OPTION="--no-merges --cherry-pick"
-		fi
-
-		SHORTHEAD=$(git rev-parse --short $HEAD)
-		SHORTONTO=$(git rev-parse --short $ONTO)
-		if test -z "$REBASE_ROOT"
-			# this is now equivalent to ! -z "$UPSTREAM"
-		then
-			SHORTUPSTREAM=$(git rev-parse --short $UPSTREAM)
-			REVISIONS=$UPSTREAM...$HEAD
-			SHORTREVISIONS=$SHORTUPSTREAM..$SHORTHEAD
-		else
-			REVISIONS=$ONTO...$HEAD
-			SHORTREVISIONS=$SHORTHEAD
-		fi
-		git rev-list $MERGES_OPTION --pretty=oneline --abbrev-commit \
-			--abbrev=7 --reverse --left-right --topo-order \
-			$REVISIONS | \
-			sed -n "s/^>//p" | while read shortsha1 rest
+: > "$state_dir"/interactive || die "Could not mark as interactive"
+write_basic_state
+if test t = "$preserve_merges"
+then
+	if test -z "$rebase_root"
+	then
+		mkdir "$rewritten" &&
+		for c in $(git merge-base --all $orig_head $upstream)
 		do
-			if test t != "$PRESERVE_MERGES"
-			then
-				echo "pick $shortsha1 $rest" >> "$TODO"
-			else
-				sha1=$(git rev-parse $shortsha1)
-				if test -z "$REBASE_ROOT"
+			echo $onto > "$rewritten"/$c ||
+				die "Could not init rewritten commits"
+		done
+	else
+		mkdir "$rewritten" &&
+		echo $onto > "$rewritten"/root ||
+			die "Could not init rewritten commits"
+	fi
+	# No cherry-pick because our first pass is to determine
+	# parents to rewrite and skipping dropped commits would
+	# prematurely end our probe
+	merges_option=
+else
+	merges_option="--no-merges --cherry-pick"
+fi
+
+shorthead=$(git rev-parse --short $orig_head)
+shortonto=$(git rev-parse --short $onto)
+if test -z "$rebase_root"
+	# this is now equivalent to ! -z "$upstream"
+then
+	shortupstream=$(git rev-parse --short $upstream)
+	revisions=$upstream...$orig_head
+	shortrevisions=$shortupstream..$shorthead
+else
+	revisions=$onto...$orig_head
+	shortrevisions=$shorthead
+fi
+git rev-list $merges_option --pretty=oneline --abbrev-commit \
+	--abbrev=7 --reverse --left-right --topo-order \
+	$revisions | \
+	sed -n "s/^>//p" |
+while read -r shortsha1 rest
+do
+
+	if test -z "$keep_empty" && is_empty_commit $shortsha1
+	then
+		comment_out="# "
+	else
+		comment_out=
+	fi
+
+	if test t != "$preserve_merges"
+	then
+		printf '%s\n' "${comment_out}pick $shortsha1 $rest" >>"$todo"
+	else
+		sha1=$(git rev-parse $shortsha1)
+		if test -z "$rebase_root"
+		then
+			preserve=t
+			for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-)
+			do
+				if test -f "$rewritten"/$p
 				then
-					preserve=t
-					for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-)
-					do
-						if test -f "$REWRITTEN"/$p -a \( $p != $ONTO -o $sha1 = $first_after_upstream \)
-						then
-							preserve=f
-						fi
-					done
-				else
 					preserve=f
 				fi
-				if test f = "$preserve"
-				then
-					touch "$REWRITTEN"/$sha1
-					echo "pick $shortsha1 $rest" >> "$TODO"
-				fi
-			fi
-		done
-
-		# Watch for commits that been dropped by --cherry-pick
-		if test t = "$PRESERVE_MERGES"
-		then
-			mkdir "$DROPPED"
-			# Save all non-cherry-picked changes
-			git rev-list $REVISIONS --left-right --cherry-pick | \
-				sed -n "s/^>//p" > "$DOTEST"/not-cherry-picks
-			# Now all commits and note which ones are missing in
-			# not-cherry-picks and hence being dropped
-			git rev-list $REVISIONS |
-			while read rev
-			do
-				if test -f "$REWRITTEN"/$rev -a "$(grep "$rev" "$DOTEST"/not-cherry-picks)" = ""
-				then
-					# Use -f2 because if rev-list is telling us this commit is
-					# not worthwhile, we don't want to track its multiple heads,
-					# just the history of its first-parent for others that will
-					# be rebasing on top of it
-					git rev-list --parents -1 $rev | cut -d' ' -s -f2 > "$DROPPED"/$rev
-					short=$(git rev-list -1 --abbrev-commit --abbrev=7 $rev)
-					grep -v "^[a-z][a-z]* $short" <"$TODO" > "${TODO}2" ; mv "${TODO}2" "$TODO"
-					rm "$REWRITTEN"/$rev
-				fi
 			done
+		else
+			preserve=f
 		fi
+		if test f = "$preserve"
+		then
+			touch "$rewritten"/$sha1
+			printf '%s\n' "${comment_out}pick $shortsha1 $rest" >>"$todo"
+		fi
+	fi
+done
 
-		test -s "$TODO" || echo noop >> "$TODO"
-		cat >> "$TODO" << EOF
+# Watch for commits that been dropped by --cherry-pick
+if test t = "$preserve_merges"
+then
+	mkdir "$dropped"
+	# Save all non-cherry-picked changes
+	git rev-list $revisions --left-right --cherry-pick | \
+		sed -n "s/^>//p" > "$state_dir"/not-cherry-picks
+	# Now all commits and note which ones are missing in
+	# not-cherry-picks and hence being dropped
+	git rev-list $revisions |
+	while read rev
+	do
+		if test -f "$rewritten"/$rev -a "$(sane_grep "$rev" "$state_dir"/not-cherry-picks)" = ""
+		then
+			# Use -f2 because if rev-list is telling us this commit is
+			# not worthwhile, we don't want to track its multiple heads,
+			# just the history of its first-parent for others that will
+			# be rebasing on top of it
+			git rev-list --parents -1 $rev | cut -d' ' -s -f2 > "$dropped"/$rev
+			short=$(git rev-list -1 --abbrev-commit --abbrev=7 $rev)
+			sane_grep -v "^[a-z][a-z]* $short" <"$todo" > "${todo}2" ; mv "${todo}2" "$todo"
+			rm "$rewritten"/$rev
+		fi
+	done
+fi
 
-# Rebase $SHORTREVISIONS onto $SHORTONTO
+test -s "$todo" || echo noop >> "$todo"
+test -n "$autosquash" && rearrange_squash "$todo"
+test -n "$cmd" && add_exec_commands "$todo"
+
+cat >> "$todo" << EOF
+
+# Rebase $shortrevisions onto $shortonto
 #
 # Commands:
 #  p, pick = use commit
+#  r, reword = use commit, but edit the commit message
 #  e, edit = use commit, but stop for amending
 #  s, squash = use commit, but meld into previous commit
+#  f, fixup = like "squash", but discard this commit's log message
+#  x, exec = run command (the rest of the line) using shell
+#
+# These lines can be re-ordered; they are executed from top to bottom.
 #
 # If you remove a line here THAT COMMIT WILL BE LOST.
 # However, if you remove everything, the rebase will be aborted.
 #
 EOF
 
-		has_action "$TODO" ||
-			die_abort "Nothing to do"
+if test -z "$keep_empty"
+then
+	echo "# Note that empty commits are commented out" >>"$todo"
+fi
 
-		cp "$TODO" "$TODO".backup
-		git_editor "$TODO" ||
-			die "Could not execute editor"
 
-		has_action "$TODO" ||
-			die_abort "Nothing to do"
+has_action "$todo" ||
+	die_abort "Nothing to do"
 
-		test -d "$REWRITTEN" || skip_unnecessary_picks
+cp "$todo" "$todo".backup
+git_sequence_editor "$todo" ||
+	die_abort "Could not execute editor"
 
-		git update-ref ORIG_HEAD $HEAD
-		output git checkout $ONTO && do_rest
-		;;
-	esac
-	shift
-done
+has_action "$todo" ||
+	die_abort "Nothing to do"
+
+test -d "$rewritten" || test -n "$force_rebase" || skip_unnecessary_picks
+
+output git checkout $onto || die_abort "could not detach HEAD"
+git update-ref ORIG_HEAD $orig_head
+do_rest
diff --git a/git-rebase--merge.sh b/git-rebase--merge.sh
new file mode 100644
index 0000000..b10f2cf
--- /dev/null
+++ b/git-rebase--merge.sh
@@ -0,0 +1,152 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Junio C Hamano.
+#
+
+prec=4
+
+read_state () {
+	onto_name=$(cat "$state_dir"/onto_name) &&
+	end=$(cat "$state_dir"/end) &&
+	msgnum=$(cat "$state_dir"/msgnum)
+}
+
+continue_merge () {
+	test -d "$state_dir" || die "$state_dir directory does not exist"
+
+	unmerged=$(git ls-files -u)
+	if test -n "$unmerged"
+	then
+		echo "You still have unmerged paths in your index"
+		echo "did you forget to use git add?"
+		die "$resolvemsg"
+	fi
+
+	cmt=`cat "$state_dir/current"`
+	if ! git diff-index --quiet --ignore-submodules HEAD --
+	then
+		if ! git commit --no-verify -C "$cmt"
+		then
+			echo "Commit failed, please do not call \"git commit\""
+			echo "directly, but instead do one of the following: "
+			die "$resolvemsg"
+		fi
+		if test -z "$GIT_QUIET"
+		then
+			printf "Committed: %0${prec}d " $msgnum
+		fi
+		echo "$cmt $(git rev-parse HEAD^0)" >> "$state_dir/rewritten"
+	else
+		if test -z "$GIT_QUIET"
+		then
+			printf "Already applied: %0${prec}d " $msgnum
+		fi
+	fi
+	test -z "$GIT_QUIET" &&
+	GIT_PAGER='' git log --format=%s -1 "$cmt"
+
+	# onto the next patch:
+	msgnum=$(($msgnum + 1))
+	echo "$msgnum" >"$state_dir/msgnum"
+}
+
+call_merge () {
+	cmt="$(cat "$state_dir/cmt.$1")"
+	echo "$cmt" > "$state_dir/current"
+	hd=$(git rev-parse --verify HEAD)
+	cmt_name=$(git symbolic-ref HEAD 2> /dev/null || echo HEAD)
+	msgnum=$(cat "$state_dir/msgnum")
+	eval GITHEAD_$cmt='"${cmt_name##refs/heads/}~$(($end - $msgnum))"'
+	eval GITHEAD_$hd='$onto_name'
+	export GITHEAD_$cmt GITHEAD_$hd
+	if test -n "$GIT_QUIET"
+	then
+		GIT_MERGE_VERBOSITY=1 && export GIT_MERGE_VERBOSITY
+	fi
+	test -z "$strategy" && strategy=recursive
+	eval 'git-merge-$strategy' $strategy_opts '"$cmt^" -- "$hd" "$cmt"'
+	rv=$?
+	case "$rv" in
+	0)
+		unset GITHEAD_$cmt GITHEAD_$hd
+		return
+		;;
+	1)
+		git rerere $allow_rerere_autoupdate
+		die "$resolvemsg"
+		;;
+	2)
+		echo "Strategy: $strategy failed, try another" 1>&2
+		die "$resolvemsg"
+		;;
+	*)
+		die "Unknown exit code ($rv) from command:" \
+			"git-merge-$strategy $cmt^ -- HEAD $cmt"
+		;;
+	esac
+}
+
+finish_rb_merge () {
+	move_to_original_branch
+	if test -s "$state_dir"/rewritten
+	then
+		git notes copy --for-rewrite=rebase <"$state_dir"/rewritten
+		if test -x "$GIT_DIR"/hooks/post-rewrite
+		then
+			"$GIT_DIR"/hooks/post-rewrite rebase <"$state_dir"/rewritten
+		fi
+	fi
+	rm -r "$state_dir"
+	say All done.
+}
+
+case "$action" in
+continue)
+	read_state
+	continue_merge
+	while test "$msgnum" -le "$end"
+	do
+		call_merge "$msgnum"
+		continue_merge
+	done
+	finish_rb_merge
+	exit
+	;;
+skip)
+	read_state
+	git rerere clear
+	msgnum=$(($msgnum + 1))
+	while test "$msgnum" -le "$end"
+	do
+		call_merge "$msgnum"
+		continue_merge
+	done
+	finish_rb_merge
+	exit
+	;;
+esac
+
+mkdir -p "$state_dir"
+echo "$onto_name" > "$state_dir/onto_name"
+write_basic_state
+
+msgnum=0
+for cmt in `git rev-list --reverse --no-merges "$revisions"`
+do
+	msgnum=$(($msgnum + 1))
+	echo "$cmt" > "$state_dir/cmt.$msgnum"
+done
+
+echo 1 >"$state_dir/msgnum"
+echo $msgnum >"$state_dir/end"
+
+end=$msgnum
+msgnum=1
+
+while test "$msgnum" -le "$end"
+do
+	call_merge "$msgnum"
+	continue_merge
+done
+
+finish_rb_merge
diff --git a/git-rebase.sh b/git-rebase.sh
index 18bc694..1cd0633 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -3,7 +3,8 @@
 # Copyright (c) 2005 Junio C Hamano.
 #
 
-USAGE='[--interactive | -i] [-v] [--force-rebase | -f] [--onto <newbase>] [<upstream>|--root] [<branch>] [--quiet | -q]'
+USAGE='[--interactive | -i] [--exec | -x <cmd>] [-v] [--force-rebase | -f]
+       [--no-ff] [--onto <newbase>] [<upstream>|--root] [<branch>] [--quiet | -q]'
 LONG_USAGE='git-rebase replaces <branch> with a new branch of the
 same name.  When the --onto option is provided the new branch starts
 out with a HEAD equal to <newbase>, otherwise it is equal to <upstream>
@@ -13,7 +14,7 @@
 It is possible that a merge failure will prevent this process from being
 completely automatic.  You will have to resolve any such merge failure
 and run git rebase --continue.  Another option is to bypass the commit
-that caused the merge failure with git rebase --skip.  To restore the
+that caused the merge failure with git rebase --skip.  To check out the
 original <branch> and remove the .git/rebase-apply working files, use the
 command git rebase --abort instead.
 
@@ -22,163 +23,160 @@
 
 Example:       git-rebase master~1 topic
 
-        A---B---C topic                   A'\''--B'\''--C'\'' topic
+	A---B---C topic                   A'\''--B'\''--C'\'' topic
        /                   -->           /
   D---E---F---G master          D---E---F---G master
 '
 
 SUBDIRECTORY_OK=Yes
-OPTIONS_SPEC=
+OPTIONS_KEEPDASHDASH=
+OPTIONS_SPEC="\
+git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] [<upstream>] [<branch>]
+git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] --root [<branch>]
+git-rebase [-i] --continue | --abort | --skip
+--
+ Available options are
+v,verbose!         display a diffstat of what changed upstream
+q,quiet!           be quiet. implies --no-stat
+onto=!             rebase onto given branch instead of upstream
+p,preserve-merges! try to recreate merges instead of ignoring them
+s,strategy=!       use the given merge strategy
+no-ff!             cherry-pick all commits, even if unchanged
+m,merge!           use merging strategies to rebase
+i,interactive!     let the user edit the list of commits to rebase
+x,exec=!           add exec lines after each commit of the editable list
+k,keep-empty	   preserve empty commits during rebase
+f,force-rebase!    force rebase even if branch is up to date
+X,strategy-option=! pass the argument through to the merge strategy
+stat!              display a diffstat of what changed upstream
+n,no-stat!         do not show diffstat of what changed upstream
+verify             allow pre-rebase hook to run
+rerere-autoupdate  allow rerere to update index with resolved conflicts
+root!              rebase all reachable commits up to the root(s)
+autosquash         move commits that begin with squash!/fixup! under -i
+committer-date-is-author-date! passed to 'git am'
+ignore-date!       passed to 'git am'
+whitespace=!       passed to 'git apply'
+ignore-whitespace! passed to 'git apply'
+C=!                passed to 'git apply'
+ Actions:
+continue!          continue
+abort!             abort and check out the original branch
+skip!              skip current patch and continue
+"
 . git-sh-setup
 set_reflog_action rebase
-require_work_tree
+require_work_tree_exists
 cd_to_toplevel
 
-OK_TO_SKIP_PRE_REBASE=
-RESOLVEMSG="
+LF='
+'
+ok_to_skip_pre_rebase=
+resolvemsg="
 When you have resolved this problem run \"git rebase --continue\".
 If you would prefer to skip this patch, instead run \"git rebase --skip\".
-To restore the original branch and stop rebasing run \"git rebase --abort\".
+To check out the original branch and stop rebasing run \"git rebase --abort\".
 "
-unset newbase
-strategy=recursive
+unset onto
+cmd=
+strategy=
+strategy_opts=
 do_merge=
-dotest="$GIT_DIR"/rebase-merge
-prec=4
+merge_dir="$GIT_DIR"/rebase-merge
+apply_dir="$GIT_DIR"/rebase-apply
 verbose=
-diffstat=$(git config --bool rebase.stat)
+diffstat=
+test "$(git config --bool rebase.stat)" = true && diffstat=t
 git_am_opt=
 rebase_root=
 force_rebase=
+allow_rerere_autoupdate=
+# Non-empty if a rebase was in progress when 'git rebase' was invoked
+in_progress=
+# One of {am, merge, interactive}
+type=
+# One of {"$GIT_DIR"/rebase-apply, "$GIT_DIR"/rebase-merge}
+state_dir=
+# One of {'', continue, skip, abort}, as parsed from command line
+action=
+preserve_merges=
+autosquash=
+keep_empty=
+test "$(git config --bool rebase.autosquash)" = "true" && autosquash=t
 
-continue_merge () {
-	test -n "$prev_head" || die "prev_head must be defined"
-	test -d "$dotest" || die "$dotest directory does not exist"
-
-	unmerged=$(git ls-files -u)
-	if test -n "$unmerged"
+read_basic_state () {
+	head_name=$(cat "$state_dir"/head-name) &&
+	onto=$(cat "$state_dir"/onto) &&
+	# We always write to orig-head, but interactive rebase used to write to
+	# head. Fall back to reading from head to cover for the case that the
+	# user upgraded git with an ongoing interactive rebase.
+	if test -f "$state_dir"/orig-head
 	then
-		echo "You still have unmerged paths in your index"
-		echo "did you forget to use git add?"
-		die "$RESOLVEMSG"
-	fi
-
-	cmt=`cat "$dotest/current"`
-	if ! git diff-index --quiet --ignore-submodules HEAD --
-	then
-		if ! git commit --no-verify -C "$cmt"
-		then
-			echo "Commit failed, please do not call \"git commit\""
-			echo "directly, but instead do one of the following: "
-			die "$RESOLVEMSG"
-		fi
-		if test -z "$GIT_QUIET"
-		then
-			printf "Committed: %0${prec}d " $msgnum
-		fi
+		orig_head=$(cat "$state_dir"/orig-head)
 	else
-		if test -z "$GIT_QUIET"
-		then
-			printf "Already applied: %0${prec}d " $msgnum
-		fi
-	fi
-	if test -z "$GIT_QUIET"
-	then
-		git rev-list --pretty=oneline -1 "$cmt" | sed -e 's/^[^ ]* //'
-	fi
-
-	prev_head=`git rev-parse HEAD^0`
-	# save the resulting commit so we can read-tree on it later
-	echo "$prev_head" > "$dotest/prev_head"
-
-	# onto the next patch:
-	msgnum=$(($msgnum + 1))
-	echo "$msgnum" >"$dotest/msgnum"
+		orig_head=$(cat "$state_dir"/head)
+	fi &&
+	GIT_QUIET=$(cat "$state_dir"/quiet) &&
+	test -f "$state_dir"/verbose && verbose=t
+	test -f "$state_dir"/strategy && strategy="$(cat "$state_dir"/strategy)"
+	test -f "$state_dir"/strategy_opts &&
+		strategy_opts="$(cat "$state_dir"/strategy_opts)"
+	test -f "$state_dir"/allow_rerere_autoupdate &&
+		allow_rerere_autoupdate="$(cat "$state_dir"/allow_rerere_autoupdate)"
 }
 
-call_merge () {
-	cmt="$(cat "$dotest/cmt.$1")"
-	echo "$cmt" > "$dotest/current"
-	hd=$(git rev-parse --verify HEAD)
-	cmt_name=$(git symbolic-ref HEAD 2> /dev/null || echo HEAD)
-	msgnum=$(cat "$dotest/msgnum")
-	end=$(cat "$dotest/end")
-	eval GITHEAD_$cmt='"${cmt_name##refs/heads/}~$(($end - $msgnum))"'
-	eval GITHEAD_$hd='$(cat "$dotest/onto_name")'
-	export GITHEAD_$cmt GITHEAD_$hd
-	if test -n "$GIT_QUIET"
-	then
-		export GIT_MERGE_VERBOSITY=1
-	fi
-	git-merge-$strategy "$cmt^" -- "$hd" "$cmt"
-	rv=$?
-	case "$rv" in
-	0)
-		unset GITHEAD_$cmt GITHEAD_$hd
-		return
-		;;
-	1)
-		git rerere
-		die "$RESOLVEMSG"
-		;;
-	2)
-		echo "Strategy: $rv $strategy failed, try another" 1>&2
-		die "$RESOLVEMSG"
+write_basic_state () {
+	echo "$head_name" > "$state_dir"/head-name &&
+	echo "$onto" > "$state_dir"/onto &&
+	echo "$orig_head" > "$state_dir"/orig-head &&
+	echo "$GIT_QUIET" > "$state_dir"/quiet &&
+	test t = "$verbose" && : > "$state_dir"/verbose
+	test -n "$strategy" && echo "$strategy" > "$state_dir"/strategy
+	test -n "$strategy_opts" && echo "$strategy_opts" > \
+		"$state_dir"/strategy_opts
+	test -n "$allow_rerere_autoupdate" && echo "$allow_rerere_autoupdate" > \
+		"$state_dir"/allow_rerere_autoupdate
+}
+
+output () {
+	case "$verbose" in
+	'')
+		output=$("$@" 2>&1 )
+		status=$?
+		test $status != 0 && printf "%s\n" "$output"
+		return $status
 		;;
 	*)
-		die "Unknown exit code ($rv) from command:" \
-			"git-merge-$strategy $cmt^ -- HEAD $cmt"
+		"$@"
 		;;
 	esac
 }
 
 move_to_original_branch () {
-	test -z "$head_name" &&
-		head_name="$(cat "$dotest"/head-name)" &&
-		onto="$(cat "$dotest"/onto)" &&
-		orig_head="$(cat "$dotest"/orig-head)"
 	case "$head_name" in
 	refs/*)
 		message="rebase finished: $head_name onto $onto"
 		git update-ref -m "$message" \
 			$head_name $(git rev-parse HEAD) $orig_head &&
-		git symbolic-ref HEAD $head_name ||
+		git symbolic-ref \
+			-m "rebase finished: returning to $head_name" \
+			HEAD $head_name ||
 		die "Could not move back to $head_name"
 		;;
 	esac
 }
 
-finish_rb_merge () {
-	move_to_original_branch
-	rm -r "$dotest"
-	say All done.
-}
-
-is_interactive () {
-	while test $# != 0
-	do
-		case "$1" in
-			-i|--interactive)
-				interactive_rebase=explicit
-				break
-			;;
-			-p|--preserve-merges)
-				interactive_rebase=implied
-			;;
-		esac
-		shift
-	done
-
+run_specific_rebase () {
 	if [ "$interactive_rebase" = implied ]; then
 		GIT_EDITOR=:
 		export GIT_EDITOR
+		autosquash=
 	fi
-
-	test -n "$interactive_rebase" || test -f "$dotest"/interactive
+	. git-rebase--$type
 }
 
 run_pre_rebase_hook () {
-	if test -z "$OK_TO_SKIP_PRE_REBASE" &&
+	if test -z "$ok_to_skip_pre_rebase" &&
 	   test -x "$GIT_DIR/hooks/pre-rebase"
 	then
 		"$GIT_DIR/hooks/pre-rebase" ${1+"$@"} ||
@@ -186,170 +184,128 @@
 	fi
 }
 
-test -f "$GIT_DIR"/rebase-apply/applying &&
+test -f "$apply_dir"/applying &&
 	die 'It looks like git-am is in progress. Cannot rebase.'
 
-is_interactive "$@" && exec git-rebase--interactive "$@"
-
-if test $# -eq 0
+if test -d "$apply_dir"
 then
-	test -d "$dotest" -o -d "$GIT_DIR"/rebase-apply || usage
-	test -d "$dotest" -o -f "$GIT_DIR"/rebase-apply/rebasing &&
-		die 'A rebase is in progress, try --continue, --skip or --abort.'
-	die "No arguments given and $GIT_DIR/rebase-apply already exists."
+	type=am
+	state_dir="$apply_dir"
+elif test -d "$merge_dir"
+then
+	if test -f "$merge_dir"/interactive
+	then
+		type=interactive
+		interactive_rebase=explicit
+	else
+		type=merge
+	fi
+	state_dir="$merge_dir"
 fi
+test -n "$type" && in_progress=t
 
+total_argc=$#
 while test $# != 0
 do
 	case "$1" in
 	--no-verify)
-		OK_TO_SKIP_PRE_REBASE=yes
+		ok_to_skip_pre_rebase=yes
 		;;
-	--continue)
-		test -d "$dotest" -o -d "$GIT_DIR"/rebase-apply ||
-			die "No rebase in progress?"
-
-		git diff-files --quiet --ignore-submodules || {
-			echo "You must edit all merge conflicts and then"
-			echo "mark them as resolved using git add"
-			exit 1
-		}
-		if test -d "$dotest"
-		then
-			prev_head=$(cat "$dotest/prev_head")
-			end=$(cat "$dotest/end")
-			msgnum=$(cat "$dotest/msgnum")
-			onto=$(cat "$dotest/onto")
-			GIT_QUIET=$(cat "$dotest/quiet")
-			continue_merge
-			while test "$msgnum" -le "$end"
-			do
-				call_merge "$msgnum"
-				continue_merge
-			done
-			finish_rb_merge
-			exit
-		fi
-		head_name=$(cat "$GIT_DIR"/rebase-apply/head-name) &&
-		onto=$(cat "$GIT_DIR"/rebase-apply/onto) &&
-		orig_head=$(cat "$GIT_DIR"/rebase-apply/orig-head) &&
-		GIT_QUIET=$(cat "$GIT_DIR"/rebase-apply/quiet)
-		git am --resolved --3way --resolvemsg="$RESOLVEMSG" &&
-		move_to_original_branch
-		exit
+	--verify)
+		ok_to_skip_pre_rebase=
 		;;
-	--skip)
-		test -d "$dotest" -o -d "$GIT_DIR"/rebase-apply ||
-			die "No rebase in progress?"
-
-		git reset --hard HEAD || exit $?
-		if test -d "$dotest"
-		then
-			git rerere clear
-			prev_head=$(cat "$dotest/prev_head")
-			end=$(cat "$dotest/end")
-			msgnum=$(cat "$dotest/msgnum")
-			msgnum=$(($msgnum + 1))
-			onto=$(cat "$dotest/onto")
-			GIT_QUIET=$(cat "$dotest/quiet")
-			while test "$msgnum" -le "$end"
-			do
-				call_merge "$msgnum"
-				continue_merge
-			done
-			finish_rb_merge
-			exit
-		fi
-		head_name=$(cat "$GIT_DIR"/rebase-apply/head-name) &&
-		onto=$(cat "$GIT_DIR"/rebase-apply/onto) &&
-		orig_head=$(cat "$GIT_DIR"/rebase-apply/orig-head) &&
-		GIT_QUIET=$(cat "$GIT_DIR"/rebase-apply/quiet)
-		git am -3 --skip --resolvemsg="$RESOLVEMSG" &&
-		move_to_original_branch
-		exit
-		;;
-	--abort)
-		test -d "$dotest" -o -d "$GIT_DIR"/rebase-apply ||
-			die "No rebase in progress?"
-
-		git rerere clear
-		if test -d "$dotest"
-		then
-			GIT_QUIET=$(cat "$dotest/quiet")
-			move_to_original_branch
-		else
-			dotest="$GIT_DIR"/rebase-apply
-			GIT_QUIET=$(cat "$dotest/quiet")
-			move_to_original_branch
-		fi
-		git reset --hard $(cat "$dotest/orig-head")
-		rm -r "$dotest"
-		exit
+	--continue|--skip|--abort)
+		test $total_argc -eq 2 || usage
+		action=${1##--}
 		;;
 	--onto)
 		test 2 -le "$#" || usage
-		newbase="$2"
+		onto="$2"
 		shift
 		;;
-	-M|-m|--m|--me|--mer|--merg|--merge)
+	-x)
+		test 2 -le "$#" || usage
+		cmd="${cmd}exec $2${LF}"
+		shift
+		;;
+	-i)
+		interactive_rebase=explicit
+		;;
+	-k)
+		keep_empty=yes
+		;;
+	-p)
+		preserve_merges=t
+		test -z "$interactive_rebase" && interactive_rebase=implied
+		;;
+	--autosquash)
+		autosquash=t
+		;;
+	--no-autosquash)
+		autosquash=
+		;;
+	-M|-m)
 		do_merge=t
 		;;
-	-s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\
-		--strateg=*|--strategy=*|\
-	-s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)
-		case "$#,$1" in
-		*,*=*)
-			strategy=`expr "z$1" : 'z-[^=]*=\(.*\)'` ;;
-		1,*)
-			usage ;;
-		*)
-			strategy="$2"
-			shift ;;
-		esac
+	-X)
+		shift
+		strategy_opts="$strategy_opts $(git rev-parse --sq-quote "--$1")"
+		do_merge=t
+		test -z "$strategy" && strategy=recursive
+		;;
+	-s)
+		shift
+		strategy="$1"
 		do_merge=t
 		;;
-	-n|--no-stat)
+	-n)
 		diffstat=
 		;;
 	--stat)
 		diffstat=t
 		;;
-	-v|--verbose)
+	-v)
 		verbose=t
 		diffstat=t
 		GIT_QUIET=
 		;;
-	-q|--quiet)
+	-q)
 		GIT_QUIET=t
 		git_am_opt="$git_am_opt -q"
 		verbose=
 		diffstat=
 		;;
-	--whitespace=*)
-		git_am_opt="$git_am_opt $1"
+	--whitespace)
+		shift
+		git_am_opt="$git_am_opt --whitespace=$1"
 		case "$1" in
-		--whitespace=fix|--whitespace=strip)
+		fix|strip)
 			force_rebase=t
 			;;
 		esac
 		;;
+	--ignore-whitespace)
+		git_am_opt="$git_am_opt $1"
+		;;
 	--committer-date-is-author-date|--ignore-date)
 		git_am_opt="$git_am_opt $1"
 		force_rebase=t
 		;;
-	-C*)
-		git_am_opt="$git_am_opt $1"
+	-C)
+		shift
+		git_am_opt="$git_am_opt -C$1"
 		;;
 	--root)
 		rebase_root=t
 		;;
-	-f|--f|--fo|--for|--forc|force|--force-r|--force-re|--force-reb|--force-reba|--force-rebas|--force-rebase)
+	-f|--no-ff)
 		force_rebase=t
 		;;
-	-*)
-		usage
+	--rerere-autoupdate|--no-rerere-autoupdate)
+		allow_rerere_autoupdate="$1"
 		;;
-	*)
+	--)
+		shift
 		break
 		;;
 	esac
@@ -357,65 +313,145 @@
 done
 test $# -gt 2 && usage
 
-# Make sure we do not have $GIT_DIR/rebase-apply
-if test -z "$do_merge"
+if test -n "$cmd" &&
+   test "$interactive_rebase" != explicit
 then
-	if mkdir "$GIT_DIR"/rebase-apply 2>/dev/null
+	die "--exec option must be used with --interactive option"
+fi
+
+if test -n "$action"
+then
+	test -z "$in_progress" && die "No rebase in progress?"
+	# Only interactive rebase uses detailed reflog messages
+	if test "$type" = interactive && test "$GIT_REFLOG_ACTION" = rebase
 	then
-		rmdir "$GIT_DIR"/rebase-apply
-	else
-		echo >&2 '
-It seems that I cannot create a rebase-apply directory, and
-I wonder if you are in the middle of patch application or another
-rebase.  If that is not the case, please
-	rm -fr '"$GIT_DIR"'/rebase-apply
-and run me again.  I am stopping in case you still have something
-valuable there.'
-		exit 1
-	fi
-else
-	if test -d "$dotest"
-	then
-		die "previous rebase directory $dotest still exists." \
-			'Try git rebase (--continue | --abort | --skip)'
+		GIT_REFLOG_ACTION="rebase -i ($action)"
+		export GIT_REFLOG_ACTION
 	fi
 fi
 
-# The tree must be really really clean.
-if ! git update-index --ignore-submodules --refresh; then
-	die "cannot rebase: you have unstaged changes"
-fi
-diff=$(git diff-index --cached --name-status -r --ignore-submodules HEAD --)
-case "$diff" in
-?*)	echo >&2 "cannot rebase: your index contains uncommitted changes"
-	echo >&2 "$diff"
-	exit 1
+case "$action" in
+continue)
+	# Sanity check
+	git rev-parse --verify HEAD >/dev/null ||
+		die "Cannot read HEAD"
+	git update-index --ignore-submodules --refresh &&
+	git diff-files --quiet --ignore-submodules || {
+		echo "You must edit all merge conflicts and then"
+		echo "mark them as resolved using git add"
+		exit 1
+	}
+	read_basic_state
+	run_specific_rebase
+	;;
+skip)
+	output git reset --hard HEAD || exit $?
+	read_basic_state
+	run_specific_rebase
+	;;
+abort)
+	git rerere clear
+	read_basic_state
+	case "$head_name" in
+	refs/*)
+		git symbolic-ref -m "rebase: aborting" HEAD $head_name ||
+		die "Could not move back to $head_name"
+		;;
+	esac
+	output git reset --hard $orig_head
+	rm -r "$state_dir"
+	exit
 	;;
 esac
 
+# Make sure no rebase is in progress
+if test -n "$in_progress"
+then
+	die '
+It seems that there is already a '"${state_dir##*/}"' directory, and
+I wonder if you are in the middle of another rebase.  If that is the
+case, please try
+	git rebase (--continue | --abort | --skip)
+If that is not the case, please
+	rm -fr '"$state_dir"'
+and run me again.  I am stopping in case you still have something
+valuable there.'
+fi
+
+if test -n "$rebase_root" && test -z "$onto"
+then
+	test -z "$interactive_rebase" && interactive_rebase=implied
+fi
+
+if test -n "$interactive_rebase"
+then
+	type=interactive
+	state_dir="$merge_dir"
+elif test -n "$do_merge"
+then
+	type=merge
+	state_dir="$merge_dir"
+else
+	type=am
+	state_dir="$apply_dir"
+fi
+
 if test -z "$rebase_root"
 then
-	# The upstream head must be given.  Make sure it is valid.
-	upstream_name="$1"
-	shift
+	case "$#" in
+	0)
+		if ! upstream_name=$(git rev-parse --symbolic-full-name \
+			--verify -q @{upstream} 2>/dev/null)
+		then
+			. git-parse-remote
+			error_on_missing_default_upstream "rebase" "rebase" \
+				"against" "git rebase <branch>"
+		fi
+		;;
+	*)	upstream_name="$1"
+		shift
+		;;
+	esac
 	upstream=`git rev-parse --verify "${upstream_name}^0"` ||
 	die "invalid upstream $upstream_name"
-	unset root_flag
 	upstream_arg="$upstream_name"
 else
-	test -z "$newbase" && die "--root must be used with --onto"
+	if test -z "$onto"
+	then
+		empty_tree=`git hash-object -t tree /dev/null`
+		onto=`git commit-tree $empty_tree </dev/null`
+		squash_onto="$onto"
+	fi
 	unset upstream_name
 	unset upstream
-	root_flag="--root"
-	upstream_arg="$root_flag"
+	test $# -gt 1 && usage
+	upstream_arg=--root
 fi
 
 # Make sure the branch to rebase onto is valid.
-onto_name=${newbase-"$upstream_name"}
-onto=$(git rev-parse --verify "${onto_name}^0") || exit
-
-# If a hook exists, give it a chance to interrupt
-run_pre_rebase_hook "$upstream_arg" "$@"
+onto_name=${onto-"$upstream_name"}
+case "$onto_name" in
+*...*)
+	if	left=${onto_name%...*} right=${onto_name#*...} &&
+		onto=$(git merge-base --all ${left:-HEAD} ${right:-HEAD})
+	then
+		case "$onto" in
+		?*"$LF"?*)
+			die "$onto_name: there are more than one merge bases"
+			;;
+		'')
+			die "$onto_name: there is no merge base"
+			;;
+		esac
+	else
+		die "$onto_name: there is no merge base"
+	fi
+	;;
+*)
+	onto=$(git rev-parse --verify "${onto_name}^0") ||
+	die "Does not point to a valid commit: $onto_name"
+	;;
+esac
 
 # If the branch to rebase is given, that is the branch we will rebase
 # $branch_name -- branch being rebased, or HEAD (already detached)
@@ -429,17 +465,17 @@
 	switch_to="$1"
 
 	if git show-ref --verify --quiet -- "refs/heads/$1" &&
-	   branch=$(git rev-parse -q --verify "refs/heads/$1")
+	   orig_head=$(git rev-parse -q --verify "refs/heads/$1")
 	then
 		head_name="refs/heads/$1"
-	elif branch=$(git rev-parse -q --verify "$1")
+	elif orig_head=$(git rev-parse -q --verify "$1")
 	then
 		head_name="detached HEAD"
 	else
-		usage
+		die "fatal: no such branch: $1"
 	fi
 	;;
-*)
+0)
 	# Do not need to switch branches, we are already on it.
 	if branch_name=`git symbolic-ref -q HEAD`
 	then
@@ -449,25 +485,31 @@
 		head_name="detached HEAD"
 		branch_name=HEAD ;# detached
 	fi
-	branch=$(git rev-parse --verify "${branch_name}^0") || exit
+	orig_head=$(git rev-parse --verify "${branch_name}^0") || exit
+	;;
+*)
+	die "BUG: unexpected number of arguments left to parse"
 	;;
 esac
-orig_head=$branch
 
-# Now we are rebasing commits $upstream..$branch (or with --root,
-# everything leading up to $branch) on top of $onto
+require_clean_work_tree "rebase" "Please commit or stash them."
+
+# Now we are rebasing commits $upstream..$orig_head (or with --root,
+# everything leading up to $orig_head) on top of $onto
 
 # Check if we are already based on $onto with linear history,
-# but this should be done only when upstream and onto are the same.
-mb=$(git merge-base "$onto" "$branch")
-if test "$upstream" = "$onto" && test "$mb" = "$onto" &&
+# but this should be done only when upstream and onto are the same
+# and if this is not an interactive rebase.
+mb=$(git merge-base "$onto" "$orig_head")
+if test "$type" != interactive && test "$upstream" = "$onto" &&
+	test "$mb" = "$onto" &&
 	# linear history?
-	! (git rev-list --parents "$onto".."$branch" | grep " .* ") > /dev/null
+	! (git rev-list --parents "$onto".."$orig_head" | sane_grep " .* ") > /dev/null
 then
 	if test -z "$force_rebase"
 	then
 		# Lazily switch to the target branch if needed...
-		test -z "$switch_to" || git checkout "$switch_to"
+		test -z "$switch_to" || git checkout "$switch_to" --
 		say "Current branch $branch_name is up to date."
 		exit 0
 	else
@@ -475,10 +517,8 @@
 	fi
 fi
 
-# Detach HEAD and reset the tree
-say "First, rewinding head to replay your work on top of it..."
-git checkout -q "$onto^0" || die "could not detach HEAD"
-git update-ref ORIG_HEAD $branch
+# If a hook exists, give it a chance to interrupt
+run_pre_rebase_hook "$upstream_arg" "$@"
 
 if test -n "$diffstat"
 then
@@ -490,9 +530,16 @@
 	GIT_PAGER='' git diff --stat --summary "$mb" "$onto"
 fi
 
+test "$type" = interactive && run_specific_rebase
+
+# Detach HEAD and reset the tree
+say "First, rewinding head to replay your work on top of it..."
+git checkout -q "$onto^0" || die "could not detach HEAD"
+git update-ref ORIG_HEAD $orig_head
+
 # If the $onto is a proper descendant of the tip of the branch, then
-# we just fast forwarded.
-if test "$mb" = "$branch"
+# we just fast-forwarded.
+if test "$mb" = "$orig_head"
 then
 	say "Fast-forwarded $branch_name to $onto_name."
 	move_to_original_branch
@@ -506,50 +553,4 @@
 	revisions="$upstream..$orig_head"
 fi
 
-if test -z "$do_merge"
-then
-	git format-patch -k --stdout --full-index --ignore-if-in-upstream \
-		$root_flag "$revisions" |
-	git am $git_am_opt --rebasing --resolvemsg="$RESOLVEMSG" &&
-	move_to_original_branch
-	ret=$?
-	test 0 != $ret -a -d "$GIT_DIR"/rebase-apply &&
-		echo $head_name > "$GIT_DIR"/rebase-apply/head-name &&
-		echo $onto > "$GIT_DIR"/rebase-apply/onto &&
-		echo $orig_head > "$GIT_DIR"/rebase-apply/orig-head &&
-		echo "$GIT_QUIET" > "$GIT_DIR"/rebase-apply/quiet
-	exit $ret
-fi
-
-# start doing a rebase with git-merge
-# this is rename-aware if the recursive (default) strategy is used
-
-mkdir -p "$dotest"
-echo "$onto" > "$dotest/onto"
-echo "$onto_name" > "$dotest/onto_name"
-prev_head=$orig_head
-echo "$prev_head" > "$dotest/prev_head"
-echo "$orig_head" > "$dotest/orig-head"
-echo "$head_name" > "$dotest/head-name"
-echo "$GIT_QUIET" > "$dotest/quiet"
-
-msgnum=0
-for cmt in `git rev-list --reverse --no-merges "$revisions"`
-do
-	msgnum=$(($msgnum + 1))
-	echo "$cmt" > "$dotest/cmt.$msgnum"
-done
-
-echo 1 >"$dotest/msgnum"
-echo $msgnum >"$dotest/end"
-
-end=$msgnum
-msgnum=1
-
-while test "$msgnum" -le "$end"
-do
-	call_merge "$msgnum"
-	continue_merge
-done
-
-finish_rb_merge
+run_specific_rebase
diff --git a/git-relink.perl b/git-relink.perl
index 937c69a..f29285c 100755
--- a/git-relink.perl
+++ b/git-relink.perl
@@ -1,4 +1,4 @@
-#!/usr/bin/env perl
+#!/usr/bin/perl
 # Copyright 2005, Ryan Anderson <ryan@michonline.com>
 # Distribution permitted under the GPL v2, as distributed
 # by the Free Software Foundation.
@@ -6,7 +6,7 @@
 #
 # Scan two git object-trees, and hardlink any common objects between them.
 
-use 5.006;
+use 5.008;
 use strict;
 use warnings;
 use Getopt::Long;
@@ -163,7 +163,7 @@
 
 
 sub usage() {
-	print("Usage: git relink [--safe] <dir> [<dir> ...] <master_dir> \n");
+	print("Usage: git relink [--safe] <dir>... <master_dir> \n");
 	print("All directories should contain a .git/objects/ subdirectory.\n");
 	print("Options\n");
 	print("\t--safe\t" .
diff --git a/git-remote-testgit.py b/git-remote-testgit.py
new file mode 100644
index 0000000..5f3ebd2
--- /dev/null
+++ b/git-remote-testgit.py
@@ -0,0 +1,272 @@
+#!/usr/bin/env python
+
+# This command is a simple remote-helper, that is used both as a
+# testcase for the remote-helper functionality, and as an example to
+# show remote-helper authors one possible implementation.
+#
+# This is a Git <-> Git importer/exporter, that simply uses git
+# fast-import and git fast-export to consume and produce fast-import
+# streams.
+#
+# To understand better the way things work, one can activate debug
+# traces by setting (to any value) the environment variables
+# GIT_TRANSPORT_HELPER_DEBUG and GIT_DEBUG_TESTGIT, to see messages
+# from the transport-helper side, or from this example remote-helper.
+
+# hashlib is only available in python >= 2.5
+try:
+    import hashlib
+    _digest = hashlib.sha1
+except ImportError:
+    import sha
+    _digest = sha.new
+import sys
+import os
+import time
+sys.path.insert(0, os.getenv("GITPYTHONLIB","."))
+
+from git_remote_helpers.util import die, debug, warn
+from git_remote_helpers.git.repo import GitRepo
+from git_remote_helpers.git.exporter import GitExporter
+from git_remote_helpers.git.importer import GitImporter
+from git_remote_helpers.git.non_local import NonLocalGit
+
+def get_repo(alias, url):
+    """Returns a git repository object initialized for usage.
+    """
+
+    repo = GitRepo(url)
+    repo.get_revs()
+    repo.get_head()
+
+    hasher = _digest()
+    hasher.update(repo.path)
+    repo.hash = hasher.hexdigest()
+
+    repo.get_base_path = lambda base: os.path.join(
+        base, 'info', 'fast-import', repo.hash)
+
+    prefix = 'refs/testgit/%s/' % alias
+    debug("prefix: '%s'", prefix)
+
+    repo.gitdir = os.environ["GIT_DIR"]
+    repo.alias = alias
+    repo.prefix = prefix
+
+    repo.exporter = GitExporter(repo)
+    repo.importer = GitImporter(repo)
+    repo.non_local = NonLocalGit(repo)
+
+    return repo
+
+
+def local_repo(repo, path):
+    """Returns a git repository object initalized for usage.
+    """
+
+    local = GitRepo(path)
+
+    local.non_local = None
+    local.gitdir = repo.gitdir
+    local.alias = repo.alias
+    local.prefix = repo.prefix
+    local.hash = repo.hash
+    local.get_base_path = repo.get_base_path
+    local.exporter = GitExporter(local)
+    local.importer = GitImporter(local)
+
+    return local
+
+
+def do_capabilities(repo, args):
+    """Prints the supported capabilities.
+    """
+
+    print "import"
+    print "export"
+    print "refspec refs/heads/*:%s*" % repo.prefix
+
+    dirname = repo.get_base_path(repo.gitdir)
+
+    if not os.path.exists(dirname):
+        os.makedirs(dirname)
+
+    path = os.path.join(dirname, 'testgit.marks')
+
+    print "*export-marks %s" % path
+    if os.path.exists(path):
+        print "*import-marks %s" % path
+
+    print # end capabilities
+
+
+def do_list(repo, args):
+    """Lists all known references.
+
+    Bug: This will always set the remote head to master for non-local
+    repositories, since we have no way of determining what the remote
+    head is at clone time.
+    """
+
+    for ref in repo.revs:
+        debug("? refs/heads/%s", ref)
+        print "? refs/heads/%s" % ref
+
+    if repo.head:
+        debug("@refs/heads/%s HEAD" % repo.head)
+        print "@refs/heads/%s HEAD" % repo.head
+    else:
+        debug("@refs/heads/master HEAD")
+        print "@refs/heads/master HEAD"
+
+    print # end list
+
+
+def update_local_repo(repo):
+    """Updates (or clones) a local repo.
+    """
+
+    if repo.local:
+        return repo
+
+    path = repo.non_local.clone(repo.gitdir)
+    repo.non_local.update(repo.gitdir)
+    repo = local_repo(repo, path)
+    return repo
+
+
+def do_import(repo, args):
+    """Exports a fast-import stream from testgit for git to import.
+    """
+
+    if len(args) != 1:
+        die("Import needs exactly one ref")
+
+    if not repo.gitdir:
+        die("Need gitdir to import")
+
+    ref = args[0]
+    refs = [ref]
+
+    while True:
+        line = sys.stdin.readline()
+        if line == '\n':
+            break
+        if not line.startswith('import '):
+            die("Expected import line.")
+
+        # strip of leading 'import '
+        ref = line[7:].strip()
+        refs.append(ref)
+
+    repo = update_local_repo(repo)
+    repo.exporter.export_repo(repo.gitdir, refs)
+
+    print "done"
+
+
+def do_export(repo, args):
+    """Imports a fast-import stream from git to testgit.
+    """
+
+    if not repo.gitdir:
+        die("Need gitdir to export")
+
+    update_local_repo(repo)
+    changed = repo.importer.do_import(repo.gitdir)
+
+    if not repo.local:
+        repo.non_local.push(repo.gitdir)
+
+    for ref in changed:
+        print "ok %s" % ref
+    print
+
+
+COMMANDS = {
+    'capabilities': do_capabilities,
+    'list': do_list,
+    'import': do_import,
+    'export': do_export,
+}
+
+
+def sanitize(value):
+    """Cleans up the url.
+    """
+
+    if value.startswith('testgit::'):
+        value = value[9:]
+
+    return value
+
+
+def read_one_line(repo):
+    """Reads and processes one command.
+    """
+
+    sleepy = os.environ.get("GIT_REMOTE_TESTGIT_SLEEPY")
+    if sleepy:
+        debug("Sleeping %d sec before readline" % int(sleepy))
+        time.sleep(int(sleepy))
+
+    line = sys.stdin.readline()
+
+    cmdline = line
+
+    if not cmdline:
+        warn("Unexpected EOF")
+        return False
+
+    cmdline = cmdline.strip().split()
+    if not cmdline:
+        # Blank line means we're about to quit
+        return False
+
+    cmd = cmdline.pop(0)
+    debug("Got command '%s' with args '%s'", cmd, ' '.join(cmdline))
+
+    if cmd not in COMMANDS:
+        die("Unknown command, %s", cmd)
+
+    func = COMMANDS[cmd]
+    func(repo, cmdline)
+    sys.stdout.flush()
+
+    return True
+
+
+def main(args):
+    """Starts a new remote helper for the specified repository.
+    """
+
+    if len(args) != 3:
+        die("Expecting exactly three arguments.")
+        sys.exit(1)
+
+    if os.getenv("GIT_DEBUG_TESTGIT"):
+        import git_remote_helpers.util
+        git_remote_helpers.util.DEBUG = True
+
+    alias = sanitize(args[1])
+    url = sanitize(args[2])
+
+    if not alias.isalnum():
+        warn("non-alnum alias '%s'", alias)
+        alias = "tmp"
+
+    args[1] = alias
+    args[2] = url
+
+    repo = get_repo(alias, url)
+
+    debug("Got arguments %s", args[1:])
+
+    more = True
+
+    sys.stdin = os.fdopen(sys.stdin.fileno(), 'r', 0)
+    while (more):
+        more = read_one_line(repo)
+
+if __name__ == '__main__':
+    sys.exit(main(sys.argv))
diff --git a/git-repack.sh b/git-repack.sh
index 1eb3bca..7579331 100755
--- a/git-repack.sh
+++ b/git-repack.sh
@@ -10,10 +10,12 @@
 a               pack everything in a single pack
 A               same as -a, and turn unreachable objects loose
 d               remove redundant packs, and run git-prune-packed
-f               pass --no-reuse-object to git-pack-objects
+f               pass --no-reuse-delta to git-pack-objects
+F               pass --no-reuse-object to git-pack-objects
 n               do not run git-update-server-info
 q,quiet         be quiet
 l               pass --local to git-pack-objects
+unpack-unreachable=  with -A, do not loosen objects older than this
  Packing constraints
 window=         size of the window used for delta compression
 window-memory=  same as the above, but limit memory size instead of entries count
@@ -32,9 +34,12 @@
 	-a)	all_into_one=t ;;
 	-A)	all_into_one=t
 		unpack_unreachable=--unpack-unreachable ;;
+	--unpack-unreachable)
+		unpack_unreachable="--unpack-unreachable=$2"; shift ;;
 	-d)	remove_redundant=t ;;
 	-q)	GIT_QUIET=t ;;
-	-f)	no_reuse=--no-reuse-object ;;
+	-f)	no_reuse=--no-reuse-delta ;;
+	-F)	no_reuse=--no-reuse-object ;;
 	-l)	local=--local ;;
 	--max-pack-size|--window|--window-memory|--depth)
 		extra="$extra $1=$2"; shift ;;
@@ -50,7 +55,7 @@
 esac
 
 PACKDIR="$GIT_OBJECT_DIRECTORY/pack"
-PACKTMP="$GIT_OBJECT_DIRECTORY/.tmp-$$-pack"
+PACKTMP="$PACKDIR/.tmp-$$-pack"
 rm -f "$PACKTMP"-*
 trap 'rm -f "$PACKTMP"-*' 0 1 2 3 15
 
@@ -74,12 +79,19 @@
 		if test -n "$existing" -a -n "$unpack_unreachable" -a \
 			-n "$remove_redundant"
 		then
-			args="$args $unpack_unreachable"
+			# This may have arbitrary user arguments, so we
+			# have to protect it against whitespace splitting
+			# when it gets run as "pack-objects $args" later.
+			# Fortunately, we know it's an approxidate, so we
+			# can just use dots instead.
+			args="$args $(echo "$unpack_unreachable" | tr ' ' .)"
 		fi
 	fi
 	;;
 esac
 
+mkdir -p "$PACKDIR" || exit
+
 args="$args $local ${GIT_QUIET:+-q} $no_reuse$extra"
 names=$(git pack-objects --keep-true-parents --honor-pack-keep --non-empty --all --reflog $args </dev/null "$PACKTMP") ||
 	exit 1
@@ -88,7 +100,6 @@
 fi
 
 # Ok we have prepared all new packfiles.
-mkdir -p "$PACKDIR" || exit
 
 # First see if there are packs of the same name and if so
 # if we can move them out of the way (this can happen if we
diff --git a/git-request-pull.sh b/git-request-pull.sh
index 630cedd..d566015 100755
--- a/git-request-pull.sh
+++ b/git-request-pull.sh
@@ -1,4 +1,4 @@
-#!/bin/sh -e
+#!/bin/sh
 # Copyright 2005, Ryan Anderson <ryan@michonline.com>
 #
 # This file is licensed under the GPL v2, or a later version
@@ -8,13 +8,13 @@
 LONG_USAGE='Summarizes the changes between two commits to the standard output,
 and includes the given URL in the generated summary.'
 SUBDIRECTORY_OK='Yes'
+OPTIONS_KEEPDASHDASH=
 OPTIONS_SPEC='git request-pull [options] start url [end]
 --
 p    show patch text as well
 '
 
 . git-sh-setup
-. git-parse-remote
 
 GIT_PAGER=
 export GIT_PAGER
@@ -35,44 +35,118 @@
 	shift
 done
 
-base=$1
-url=$2
-head=${3-HEAD}
+base=$1 url=$2 head=${3-HEAD} status=0 branch_name=
 
-[ "$base" ] || usage
-[ "$url" ] || usage
-
-baserev=`git rev-parse --verify "$base"^0` &&
-headrev=`git rev-parse --verify "$head"^0` || exit
-
-merge_base=`git merge-base $baserev $headrev` ||
-die "fatal: No commits in common between $base and $head"
-
-branch=$(git ls-remote "$url" \
-	| sed -n -e "/^$headrev	refs.heads./{
-		s/^.*	refs.heads.//
-		p
-		q
-	}")
-url=$(get_remote_url "$url")
-if [ -z "$branch" ]; then
-	echo "warn: No branch of $url is at:" >&2
-	git log --max-count=1 --pretty='tformat:warn:   %h: %s' $headrev >&2
-	echo "warn: Are you sure you pushed $head there?" >&2
-	echo >&2
-	echo >&2
-	branch=..BRANCH.NOT.VERIFIED..
-	status=1
+headref=$(git symbolic-ref -q "$head")
+if git show-ref -q --verify "$headref"
+then
+	branch_name=${headref#refs/heads/}
+	if test "z$branch_name" = "z$headref" ||
+		! git config "branch.$branch_name.description" >/dev/null
+	then
+		branch_name=
+	fi
 fi
 
-echo "The following changes since commit $baserev:"
-git shortlog --max-count=1 $baserev | sed -e 's/^\(.\)/  \1/'
+tag_name=$(git describe --exact "$head^0" 2>/dev/null)
 
-echo "are available in the git repository at:"
-echo
-echo "  $url $branch"
-echo
+test -n "$base" && test -n "$url" || usage
+baserev=$(git rev-parse --verify "$base"^0) &&
+headrev=$(git rev-parse --verify "$head"^0) || exit
 
-git shortlog ^$baserev $headrev
-git diff -M --stat --summary $patch $merge_base..$headrev
+merge_base=$(git merge-base $baserev $headrev) ||
+die "fatal: No commits in common between $base and $head"
+
+# $head is the token given from the command line, and $tag_name, if
+# exists, is the tag we are going to show the commit information for.
+# If that tag exists at the remote and it points at the commit, use it.
+# Otherwise, if a branch with the same name as $head exists at the remote
+# and their values match, use that instead.
+#
+# Otherwise find a random ref that matches $headrev.
+find_matching_ref='
+	sub abbr {
+		my $ref = shift;
+		if ($ref =~ s|^refs/heads/|| || $ref =~ s|^refs/tags/|tags/|) {
+			return $ref;
+		} else {
+			return $ref;
+		}
+	}
+
+	my ($tagged, $branch, $found);
+	while (<STDIN>) {
+		my ($sha1, $ref, $deref) = /^(\S+)\s+(\S+?)(\^\{\})?$/;
+		next unless ($sha1 eq $ARGV[1]);
+		$found = abbr($ref);
+		if ($deref && $ref eq "tags/$ARGV[2]") {
+			$tagged = $found;
+			last;
+		}
+		if ($ref =~ m|/\Q$ARGV[0]\E$|) {
+			$exact = $found;
+		}
+	}
+	if ($tagged) {
+		print "$tagged\n";
+	} elsif ($exact) {
+		print "$exact\n";
+	} elsif ($found) {
+		print "$found\n";
+	}
+'
+
+ref=$(git ls-remote "$url" | perl -e "$find_matching_ref" "$head" "$headrev" "$tag_name")
+
+url=$(git ls-remote --get-url "$url")
+
+git show -s --format='The following changes since commit %H:
+
+  %s (%ci)
+
+are available in the git repository at:
+' $merge_base &&
+echo "  $url${ref+ $ref}" &&
+git show -s --format='
+for you to fetch changes up to %H:
+
+  %s (%ci)
+
+----------------------------------------------------------------' $headrev &&
+
+if test -n "$branch_name"
+then
+	echo "(from the branch description for $branch_name local branch)"
+	echo
+	git config "branch.$branch_name.description"
+fi &&
+
+if test -n "$tag_name"
+then
+	if test -z "$ref" || test "$ref" != "tags/$tag_name"
+	then
+		echo >&2 "warn: You locally have $tag_name but it does not (yet)"
+		echo >&2 "warn: appear to be at $url"
+		echo >&2 "warn: Do you want to push it there, perhaps?"
+	fi
+	git cat-file tag "$tag_name" |
+	sed -n -e '1,/^$/d' -e '/^-----BEGIN PGP /q' -e p
+	echo
+fi &&
+
+if test -n "$branch_name" || test -n "$tag_name"
+then
+	echo "----------------------------------------------------------------"
+fi &&
+
+git shortlog ^$baserev $headrev &&
+git diff -M --stat --summary $patch $merge_base..$headrev || status=1
+
+if test -z "$ref"
+then
+	echo "warn: No branch of $url is at:" >&2
+	git show -s --format='warn:   %h: %s' $headrev >&2
+	echo "warn: Are you sure you pushed '$head' there?" >&2
+	status=1
+fi
 exit $status
diff --git a/git-send-email.perl b/git-send-email.perl
index 0700d80..ef30c55 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -1,4 +1,4 @@
-#!/usr/bin/perl -w
+#!/usr/bin/perl
 #
 # Copyright 2002,2005 Greg Kroah-Hartman <greg@kroah.com>
 # Copyright 2005 Ryan Anderson <ryan@michonline.com>
@@ -16,6 +16,7 @@
 #    and second line is the subject of the message.
 #
 
+use 5.008;
 use strict;
 use warnings;
 use Term::ReadLine;
@@ -24,6 +25,7 @@
 use Data::Dumper;
 use Term::ANSIColor;
 use File::Temp qw/ tempdir tempfile /;
+use File::Spec::Functions qw(catfile);
 use Error qw(:try);
 use Git;
 
@@ -47,31 +49,36 @@
 
   Composing:
     --from                  <str>  * Email From:
-    --to                    <str>  * Email To:
-    --cc                    <str>  * Email Cc:
-    --bcc                   <str>  * Email Bcc:
+    --[no-]to               <str>  * Email To:
+    --[no-]cc               <str>  * Email Cc:
+    --[no-]bcc              <str>  * Email Bcc:
     --subject               <str>  * Email "Subject:"
     --in-reply-to           <str>  * Email "In-Reply-To:"
     --annotate                     * Review each patch that will be sent in an editor.
     --compose                      * Open an editor for introduction.
+    --8bit-encoding         <str>  * Encoding to assume 8bit mails if undeclared
 
   Sending:
     --envelope-sender       <str>  * Email envelope sender.
     --smtp-server       <str:int>  * Outgoing SMTP server to use. The port
                                      is optional. Default 'localhost'.
+    --smtp-server-option    <str>  * Outgoing SMTP server option to use.
     --smtp-server-port      <int>  * Outgoing SMTP server port.
     --smtp-user             <str>  * Username for SMTP-AUTH.
     --smtp-pass             <str>  * Password for SMTP-AUTH; not necessary.
     --smtp-encryption       <str>  * tls or ssl; anything else disables.
     --smtp-ssl                     * Deprecated. Use '--smtp-encryption ssl'.
+    --smtp-domain           <str>  * The domain name sent to HELO/EHLO handshake
+    --smtp-debug            <0|1>  * Disable, enable Net::SMTP debug.
 
   Automating:
     --identity              <str>  * Use the sendemail.<id> options.
+    --to-cmd                <str>  * Email To: via `<str> \$patch_path`
     --cc-cmd                <str>  * Email Cc: via `<str> \$patch_path`
     --suppress-cc           <str>  * author, self, sob, cc, cccmd, body, bodycc, all.
     --[no-]signed-off-by-cc        * Send to Signed-off-by: addresses. Default on.
     --[no-]suppress-from           * Send to self. Default off.
-    --[no-]chain-reply-to          * Chain In-Reply-To: fields. Default on.
+    --[no-]chain-reply-to          * Chain In-Reply-To: fields. Default off.
     --[no-]thread                  * Use In-Reply-To: field. Default on.
 
   Administering:
@@ -82,6 +89,7 @@
     --[no-]validate                * Perform patch sanity checks. Default on.
     --[no-]format-patch            * understand any non optional arguments as
                                      `git format-patch` ones.
+    --force                        * Send even if safety checks would prevent it.
 
 EOT
 	exit(1);
@@ -131,11 +139,8 @@
 my $smtp;
 my $auth;
 
-sub unique_email_list(@);
-sub cleanup_compose_files();
-
 # Variables we fill in automatically, or via prompting:
-my (@to,@cc,@initial_cc,@bcclist,@xh,
+my (@to,$no_to,@initial_to,@cc,$no_cc,@initial_cc,@bcclist,$no_bcc,@xh,
 	$initial_reply_to,$initial_subject,@files,
 	$author,$sender,$smtp_authpass,$annotate,$compose,$time);
 
@@ -159,11 +164,16 @@
 my ($quiet, $dry_run) = (0, 0);
 my $format_patch;
 my $compose_filename;
+my $force = 0;
 
 # Handle interactive edition of files.
 my $multiedit;
-my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
+my $editor;
+
 sub do_edit {
+	if (!defined($editor)) {
+		$editor = Git::command_oneline('var', 'GIT_EDITOR');
+	}
 	if (defined($multiedit) && !$multiedit) {
 		map {
 			system('sh', '-c', $editor.' "$@"', $editor, $_);
@@ -180,39 +190,66 @@
 }
 
 # Variables with corresponding config settings
-my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc, $cc_cmd);
-my ($smtp_server, $smtp_server_port, $smtp_authuser, $smtp_encryption);
-my ($identity, $aliasfiletype, @alias_files, @smtp_host_parts);
+my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc);
+my ($to_cmd, $cc_cmd);
+my ($smtp_server, $smtp_server_port, @smtp_server_options);
+my ($smtp_authuser, $smtp_encryption);
+my ($identity, $aliasfiletype, @alias_files, $smtp_domain);
 my ($validate, $confirm);
 my (@suppress_cc);
+my ($auto_8bit_encoding);
+
+my ($debug_net_smtp) = 0;		# Net::SMTP, see send_message()
+
+my $not_set_by_user = "true but not set by the user";
 
 my %config_bool_settings = (
     "thread" => [\$thread, 1],
-    "chainreplyto" => [\$chain_reply_to, 1],
+    "chainreplyto" => [\$chain_reply_to, $not_set_by_user],
     "suppressfrom" => [\$suppress_from, undef],
     "signedoffbycc" => [\$signed_off_by_cc, undef],
     "signedoffcc" => [\$signed_off_by_cc, undef],      # Deprecated
     "validate" => [\$validate, 1],
+    "multiedit" => [\$multiedit, undef]
 );
 
 my %config_settings = (
     "smtpserver" => \$smtp_server,
     "smtpserverport" => \$smtp_server_port,
+    "smtpserveroption" => \@smtp_server_options,
     "smtpuser" => \$smtp_authuser,
     "smtppass" => \$smtp_authpass,
-    "to" => \@to,
+    "smtpdomain" => \$smtp_domain,
+    "to" => \@initial_to,
+    "tocmd" => \$to_cmd,
     "cc" => \@initial_cc,
     "cccmd" => \$cc_cmd,
     "aliasfiletype" => \$aliasfiletype,
     "bcc" => \@bcclist,
-    "aliasesfile" => \@alias_files,
     "suppresscc" => \@suppress_cc,
     "envelopesender" => \$envelope_sender,
-    "multiedit" => \$multiedit,
     "confirm"   => \$confirm,
     "from" => \$sender,
+    "assume8bitencoding" => \$auto_8bit_encoding,
 );
 
+my %config_path_settings = (
+    "aliasesfile" => \@alias_files,
+);
+
+# Help users prepare for 1.7.0
+sub chain_reply_to {
+	if (defined $chain_reply_to &&
+	    $chain_reply_to eq $not_set_by_user) {
+		print STDERR
+		    "In git 1.7.0, the default has changed to --no-chain-reply-to\n" .
+		    "Set sendemail.chainreplyto configuration variable to true if\n" .
+		    "you want to keep --chain-reply-to as your default.\n";
+		$chain_reply_to = 0;
+	}
+	return $chain_reply_to;
+}
+
 # Handle Uncouth Termination
 sub signal_handler {
 
@@ -241,19 +278,28 @@
 # Begin by accumulating all the variables (defined above), that we will end up
 # needing, first, from the command line:
 
-my $rc = GetOptions("sender|from=s" => \$sender,
+my $help;
+my $rc = GetOptions("h" => \$help,
+		    "sender|from=s" => \$sender,
                     "in-reply-to=s" => \$initial_reply_to,
 		    "subject=s" => \$initial_subject,
-		    "to=s" => \@to,
+		    "to=s" => \@initial_to,
+		    "to-cmd=s" => \$to_cmd,
+		    "no-to" => \$no_to,
 		    "cc=s" => \@initial_cc,
+		    "no-cc" => \$no_cc,
 		    "bcc=s" => \@bcclist,
+		    "no-bcc" => \$no_bcc,
 		    "chain-reply-to!" => \$chain_reply_to,
 		    "smtp-server=s" => \$smtp_server,
+		    "smtp-server-option=s" => \@smtp_server_options,
 		    "smtp-server-port=s" => \$smtp_server_port,
 		    "smtp-user=s" => \$smtp_authuser,
 		    "smtp-pass:s" => \$smtp_authpass,
 		    "smtp-ssl" => sub { $smtp_encryption = 'ssl' },
 		    "smtp-encryption=s" => \$smtp_encryption,
+		    "smtp-debug:i" => \$debug_net_smtp,
+		    "smtp-domain:s" => \$smtp_domain,
 		    "identity=s" => \$identity,
 		    "annotate" => \$annotate,
 		    "compose" => \$compose,
@@ -268,8 +314,11 @@
 		    "thread!" => \$thread,
 		    "validate!" => \$validate,
 		    "format-patch!" => \$format_patch,
+		    "8bit-encoding=s" => \$auto_8bit_encoding,
+		    "force" => \$force,
 	 );
 
+usage() if $help;
 unless ($rc) {
     usage();
 }
@@ -287,8 +336,24 @@
 		$$target = Git::config_bool(@repo, "$prefix.$setting") unless (defined $$target);
 	}
 
+	foreach my $setting (keys %config_path_settings) {
+		my $target = $config_path_settings{$setting};
+		if (ref($target) eq "ARRAY") {
+			unless (@$target) {
+				my @values = Git::config_path(@repo, "$prefix.$setting");
+				@$target = @values if (@values && defined $values[0]);
+			}
+		}
+		else {
+			$$target = Git::config_path(@repo, "$prefix.$setting") unless (defined $$target);
+		}
+	}
+
 	foreach my $setting (keys %config_settings) {
 		my $target = $config_settings{$setting};
+		next if $setting eq "to" and defined $no_to;
+		next if $setting eq "cc" and defined $no_cc;
+		next if $setting eq "bcc" and defined $no_bcc;
 		if (ref($target) eq "ARRAY") {
 			unless (@$target) {
 				my @values = Git::config(@repo, "$prefix.$setting");
@@ -328,7 +393,7 @@
 if (@suppress_cc) {
 	foreach my $entry (@suppress_cc) {
 		die "Unknown --suppress-cc field: '$entry'\n"
-			unless $entry =~ /^(all|cccmd|cc|author|self|sob|body|bodycc)$/;
+			unless $entry =~ /^(?:all|cccmd|cc|author|self|sob|body|bodycc)$/;
 		$suppress_cc{$entry} = 1;
 	}
 }
@@ -373,7 +438,7 @@
 
 # Verify the user input
 
-foreach my $entry (@to) {
+foreach my $entry (@initial_to) {
 	die "Comma in --to entry: $entry'\n" unless $entry !~ m/,/;
 }
 
@@ -401,7 +466,7 @@
 my %parse_alias = (
 	# multiline formats can be supported in the future
 	mutt => sub { my $fh = shift; while (<$fh>) {
-		if (/^\s*alias\s+(\S+)\s+(.*)$/) {
+		if (/^\s*alias\s+(?:-group\s+\S+\s+)*(\S+)\s+(.*)$/) {
 			my ($alias, $addr) = ($1, $2);
 			$addr =~ s/#.*$//; # mutt allows # comments
 			 # commas delimit multiple addresses
@@ -472,12 +537,12 @@
 		push @rev_list_opts, "--", @ARGV;
 		@ARGV = ();
 	} elsif (-d $f and !check_file_rev_conflict($f)) {
-		opendir(DH,$f)
+		opendir my $dh, $f
 			or die "Failed to opendir $f: $!";
 
-		push @files, grep { -f $_ } map { +$f . "/" . $_ }
-				sort readdir(DH);
-		closedir(DH);
+		push @files, grep { -f $_ } map { catfile($f, $_) }
+				sort readdir $dh;
+		closedir $dh;
 	} elsif ((-f $f or -p $f) and !check_file_rev_conflict($f)) {
 		push @files, $f;
 	} else {
@@ -509,7 +574,7 @@
 	usage();
 }
 
-sub get_patch_subject($) {
+sub get_patch_subject {
 	my $fn = shift;
 	open (my $fh, '<', $fn);
 	while (my $line = <$fh>) {
@@ -527,7 +592,7 @@
 	$compose_filename = ($repo ?
 		tempfile(".gitsendemail.msg.XXXXXX", DIR => $repo->repo_path()) :
 		tempfile(".gitsendemail.msg.XXXXXX", DIR => "."))[1];
-	open(C,">",$compose_filename)
+	open my $c, ">", $compose_filename
 		or die "Failed to open for writing $compose_filename: $!";
 
 
@@ -535,7 +600,7 @@
 	my $tpl_subject = $initial_subject || '';
 	my $tpl_reply_to = $initial_reply_to || '';
 
-	print C <<EOT;
+	print $c <<EOT;
 From $tpl_sender # This line is ignored.
 GIT: Lines beginning in "GIT:" will be removed.
 GIT: Consider including an overall diffstat or table of contents
@@ -548,9 +613,9 @@
 
 EOT
 	for my $f (@files) {
-		print C get_patch_subject($f);
+		print $c get_patch_subject($f);
 	}
-	close(C);
+	close $c;
 
 	if ($annotate) {
 		do_edit($compose_filename, @files);
@@ -558,23 +623,23 @@
 		do_edit($compose_filename);
 	}
 
-	open(C2,">",$compose_filename . ".final")
+	open my $c2, ">", $compose_filename . ".final"
 		or die "Failed to open $compose_filename.final : " . $!;
 
-	open(C,"<",$compose_filename)
+	open $c, "<", $compose_filename
 		or die "Failed to open $compose_filename : " . $!;
 
 	my $need_8bit_cte = file_has_nonascii($compose_filename);
 	my $in_body = 0;
 	my $summary_empty = 1;
-	while(<C>) {
+	while(<$c>) {
 		next if m/^GIT:/;
 		if ($in_body) {
 			$summary_empty = 0 unless (/^\n$/);
 		} elsif (/^\n$/) {
 			$in_body = 1;
 			if ($need_8bit_cte) {
-				print C2 "MIME-Version: 1.0\n",
+				print $c2 "MIME-Version: 1.0\n",
 					 "Content-Type: text/plain; ",
 					   "charset=UTF-8\n",
 					 "Content-Transfer-Encoding: 8bit\n";
@@ -599,10 +664,10 @@
 			print "To/Cc/Bcc fields are not interpreted yet, they have been ignored\n";
 			next;
 		}
-		print C2 $_;
+		print $c2 $_;
 	}
-	close(C);
-	close(C2);
+	close $c;
+	close $c2;
 
 	if ($summary_empty) {
 		print "Summary email is empty, skipping it\n";
@@ -637,6 +702,45 @@
 	return undef;
 }
 
+my %broken_encoding;
+
+sub file_declares_8bit_cte {
+	my $fn = shift;
+	open (my $fh, '<', $fn);
+	while (my $line = <$fh>) {
+		last if ($line =~ /^$/);
+		return 1 if ($line =~ /^Content-Transfer-Encoding: .*8bit.*$/);
+	}
+	close $fh;
+	return 0;
+}
+
+foreach my $f (@files) {
+	next unless (body_or_subject_has_nonascii($f)
+		     && !file_declares_8bit_cte($f));
+	$broken_encoding{$f} = 1;
+}
+
+if (!defined $auto_8bit_encoding && scalar %broken_encoding) {
+	print "The following files are 8bit, but do not declare " .
+		"a Content-Transfer-Encoding.\n";
+	foreach my $f (sort keys %broken_encoding) {
+		print "    $f\n";
+	}
+	$auto_8bit_encoding = ask("Which 8bit encoding should I declare [UTF-8]? ",
+				  default => "UTF-8");
+}
+
+if (!$force) {
+	for my $f (@files) {
+		if (get_patch_subject($f) =~ /\Q*** SUBJECT HERE ***\E/) {
+			die "Refusing to send because the patch\n\t$f\n"
+				. "has the template subject '*** SUBJECT HERE ***'. "
+				. "Pass --force if you really want to send.\n";
+		}
+	}
+}
+
 my $prompting = 0;
 if (!defined $sender) {
 	$sender = $repoauthor || $repocommitter || '';
@@ -646,9 +750,9 @@
 	$prompting++;
 }
 
-if (!@to) {
+if (!@initial_to && !defined $to_cmd) {
 	my $to = ask("Who should the emails be sent to? ");
-	push @to, parse_address_line($to) if defined $to; # sanitized/validated later
+	push @initial_to, parse_address_line($to) if defined $to; # sanitized/validated later
 	$prompting++;
 }
 
@@ -666,8 +770,8 @@
 	return $aliases{$alias} ? expand_aliases(@{$aliases{$alias}}) : $alias;
 }
 
-@to = expand_aliases(@to);
-@to = (map { sanitize_address($_) } @to);
+@initial_to = expand_aliases(@initial_to);
+@initial_to = (map { sanitize_address($_) } @initial_to);
 @initial_cc = expand_aliases(@initial_cc);
 @bcclist = expand_aliases(@bcclist);
 
@@ -701,8 +805,8 @@
 
 sub extract_valid_address {
 	my $address = shift;
-	my $local_part_regexp = '[^<>"\s@]+';
-	my $domain_regexp = '[^.<>"\s@]+(?:\.[^.<>"\s@]+)+';
+	my $local_part_regexp = qr/[^<>"\s@]+/;
+	my $domain_regexp = qr/[^.<>"\s@]+(?:\.[^.<>"\s@]+)+/;
 
 	# check for a local address:
 	return $address if ($address =~ /^($local_part_regexp)$/);
@@ -728,8 +832,7 @@
 # We'll setup a template for the message id, using the "from" address:
 
 my ($message_id_stamp, $message_id_serial);
-sub make_message_id
-{
+sub make_message_id {
 	my $uniq;
 	if (!defined $message_id_stamp) {
 		$message_id_stamp = sprintf("%s-%s", time, $$);
@@ -744,7 +847,7 @@
 		last if (defined $du_part and $du_part ne '');
 	}
 	if (not defined $du_part or $du_part eq '') {
-		use Sys::Hostname qw();
+		require Sys::Hostname;
 		$du_part = 'user@' . Sys::Hostname::hostname();
 	}
 	my $message_id_template = "<%s-git-send-email-%s>";
@@ -777,20 +880,19 @@
 
 sub is_rfc2047_quoted {
 	my $s = shift;
-	my $token = '[^][()<>@,;:"\/?.= \000-\037\177-\377]+';
-	my $encoded_text = '[!->@-~]+';
+	my $token = qr/[^][()<>@,;:"\/?.= \000-\037\177-\377]+/;
+	my $encoded_text = qr/[!->@-~]+/;
 	length($s) <= 75 &&
 	$s =~ m/^(?:"[[:ascii:]]*"|=\?$token\?$token\?$encoded_text\?=)$/o;
 }
 
 # use the simplest quoting being able to handle the recipient
-sub sanitize_address
-{
+sub sanitize_address {
 	my ($recipient) = @_;
 	my ($recipient_name, $recipient_addr) = ($recipient =~ /^(.*?)\s*(<.*)/);
 
 	if (not $recipient_name) {
-		return "$recipient";
+		return $recipient;
 	}
 
 	# if recipient_name is already quoted, do nothing
@@ -807,22 +909,77 @@
 	# double quotes are needed if specials or CTLs are included
 	elsif ($recipient_name =~ /[][()<>@,;:\\".\000-\037\177]/) {
 		$recipient_name =~ s/(["\\\r])/\\$1/g;
-		$recipient_name = "\"$recipient_name\"";
+		$recipient_name = qq["$recipient_name"];
 	}
 
 	return "$recipient_name $recipient_addr";
 
 }
 
+# Returns the local Fully Qualified Domain Name (FQDN) if available.
+#
+# Tightly configured MTAa require that a caller sends a real DNS
+# domain name that corresponds the IP address in the HELO/EHLO
+# handshake. This is used to verify the connection and prevent
+# spammers from trying to hide their identity. If the DNS and IP don't
+# match, the receiveing MTA may deny the connection.
+#
+# Here is a deny example of Net::SMTP with the default "localhost.localdomain"
+#
+# Net::SMTP=GLOB(0x267ec28)>>> EHLO localhost.localdomain
+# Net::SMTP=GLOB(0x267ec28)<<< 550 EHLO argument does not match calling host
+#
+# This maildomain*() code is based on ideas in Perl library Test::Reporter
+# /usr/share/perl5/Test/Reporter/Mail/Util.pm ==> sub _maildomain ()
+
+sub valid_fqdn {
+	my $domain = shift;
+	return defined $domain && !($^O eq 'darwin' && $domain =~ /\.local$/) && $domain =~ /\./;
+}
+
+sub maildomain_net {
+	my $maildomain;
+
+	if (eval { require Net::Domain; 1 }) {
+		my $domain = Net::Domain::domainname();
+		$maildomain = $domain if valid_fqdn($domain);
+	}
+
+	return $maildomain;
+}
+
+sub maildomain_mta {
+	my $maildomain;
+
+	if (eval { require Net::SMTP; 1 }) {
+		for my $host (qw(mailhost localhost)) {
+			my $smtp = Net::SMTP->new($host);
+			if (defined $smtp) {
+				my $domain = $smtp->domain;
+				$smtp->quit;
+
+				$maildomain = $domain if valid_fqdn($domain);
+
+				last if $maildomain;
+			}
+		}
+	}
+
+	return $maildomain;
+}
+
+sub maildomain {
+	return maildomain_net() || maildomain_mta() || 'localhost.localdomain';
+}
+
 # Returns 1 if the message was sent, and 0 otherwise.
 # In actuality, the whole program dies when there
 # is an error sending a message.
 
-sub send_message
-{
+sub send_message {
 	my @recipients = unique_email_list(@to);
 	@cc = (grep { my $cc = extract_valid_address($_);
-		      not grep { $cc eq $_ } @recipients
+		      not grep { $cc eq $_ || $_ =~ /<\Q${cc}\E>$/ } @recipients
 		    }
 	       map { sanitize_address($_) }
 	       @cc);
@@ -835,7 +992,7 @@
 	    $gitversion = Git::version();
 	}
 
-	my $cc = join(", ", unique_email_list(@cc));
+	my $cc = join(",\n\t", unique_email_list(@cc));
 	my $ccline = "";
 	if ($cc ne '') {
 		$ccline = "\nCc: $cc";
@@ -861,7 +1018,9 @@
 
 	my @sendmail_parameters = ('-i', @recipients);
 	my $raw_from = $sanitized_sender;
-	$raw_from = $envelope_sender if (defined $envelope_sender);
+	if (defined $envelope_sender && $envelope_sender ne "auto") {
+		$raw_from = $envelope_sender;
+	}
 	$raw_from = extract_valid_address($raw_from);
 	unshift (@sendmail_parameters,
 			'-f', $raw_from) if(defined $envelope_sender);
@@ -895,6 +1054,8 @@
 		}
 	}
 
+	unshift (@sendmail_parameters, @smtp_server_options);
+
 	if ($dry_run) {
 		# We don't want to send the email.
 	} elsif ($smtp_server =~ m#^/#) {
@@ -904,7 +1065,7 @@
 			exec($smtp_server, @sendmail_parameters) or die $!;
 		}
 		print $sm "$header\n$message";
-		close $sm or die $?;
+		close $sm or die $!;
 	} else {
 
 		if (!defined $smtp_server) {
@@ -914,14 +1075,20 @@
 		if ($smtp_encryption eq 'ssl') {
 			$smtp_server_port ||= 465; # ssmtp
 			require Net::SMTP::SSL;
-			$smtp ||= Net::SMTP::SSL->new($smtp_server, Port => $smtp_server_port);
+			$smtp_domain ||= maildomain();
+			$smtp ||= Net::SMTP::SSL->new($smtp_server,
+						      Hello => $smtp_domain,
+						      Port => $smtp_server_port);
 		}
 		else {
 			require Net::SMTP;
+			$smtp_domain ||= maildomain();
 			$smtp ||= Net::SMTP->new((defined $smtp_server_port)
 						 ? "$smtp_server:$smtp_server_port"
-						 : $smtp_server);
-			if ($smtp_encryption eq 'tls') {
+						 : $smtp_server,
+						 Hello => $smtp_domain,
+						 Debug => $debug_net_smtp);
+			if ($smtp_encryption eq 'tls' && $smtp) {
 				require Net::SMTP::SSL;
 				$smtp->command('STARTTLS');
 				$smtp->response();
@@ -931,7 +1098,7 @@
 					$smtp_encryption = '';
 					# Send EHLO again to receive fresh
 					# supported commands
-					$smtp->hello();
+					$smtp->hello($smtp_domain);
 				} else {
 					die "Server does not support STARTTLS! ".$smtp->message;
 				}
@@ -939,10 +1106,20 @@
 		}
 
 		if (!$smtp) {
-			die "Unable to initialize SMTP properly.  Is there something wrong with your config?";
+			die "Unable to initialize SMTP properly. Check config and use --smtp-debug. ",
+			    "VALUES: server=$smtp_server ",
+			    "encryption=$smtp_encryption ",
+			    "hello=$smtp_domain",
+			    defined $smtp_server_port ? " port=$smtp_server_port" : "";
 		}
 
 		if (defined $smtp_authuser) {
+			# Workaround AUTH PLAIN/LOGIN interaction defect
+			# with Authen::SASL::Cyrus
+			eval {
+				require Authen::SASL;
+				Authen::SASL->import(qw(Perl));
+			};
 
 			if (!defined $smtp_authpass) {
 
@@ -976,7 +1153,9 @@
 		if ($smtp_server !~ m#^/#) {
 			print "Server: $smtp_server\n";
 			print "MAIL FROM:<$raw_from>\n";
-			print "RCPT TO:".join(',',(map { "<$_>" } @recipients))."\n";
+			foreach my $entry (@recipients) {
+			    print "RCPT TO:<$entry>\n";
+			}
 		} else {
 			print "Sendmail: $smtp_server ".join(' ',@sendmail_parameters)."\n";
 		}
@@ -998,12 +1177,13 @@
 $message_num = 0;
 
 foreach my $t (@files) {
-	open(F,"<",$t) or die "can't open file $t";
+	open my $fh, "<", $t or die "can't open file $t";
 
 	my $author = undef;
 	my $author_encoding;
 	my $has_content_type;
 	my $body_encoding;
+	@to = ();
 	@cc = ();
 	@xh = ();
 	my $input_format = undef;
@@ -1011,7 +1191,7 @@
 	$message = "";
 	$message_num++;
 	# First unfold multiline header fields
-	while(<F>) {
+	while(<$fh>) {
 		last if /^\s*$/;
 		if (/^\s+\S/ and @header) {
 			chomp($header[$#header]);
@@ -1044,6 +1224,13 @@
 					$1, $_) unless $quiet;
 				push @cc, $1;
 			}
+			elsif (/^To:\s+(.*)$/) {
+				foreach my $addr (parse_address_line($1)) {
+					printf("(mbox) Adding to: %s from line '%s'\n",
+						$addr, $_) unless $quiet;
+					push @to, sanitize_address($addr);
+				}
+			}
 			elsif (/^Cc:\s+(.*)$/) {
 				foreach my $addr (parse_address_line($1)) {
 					if (unquote_rfc2047($addr) eq $sender) {
@@ -1087,7 +1274,7 @@
 		}
 	}
 	# Now parse the message body
-	while(<F>) {
+	while(<$fh>) {
 		$message .=  $_;
 		if (/^(Signed-off-by|Cc): (.*)$/i) {
 			chomp;
@@ -1104,22 +1291,23 @@
 				$c, $_) unless $quiet;
 		}
 	}
-	close F;
+	close $fh;
 
-	if (defined $cc_cmd && !$suppress_cc{'cccmd'}) {
-		open(F, "$cc_cmd \Q$t\E |")
-			or die "(cc-cmd) Could not execute '$cc_cmd'";
-		while(<F>) {
-			my $c = $_;
-			$c =~ s/^\s*//g;
-			$c =~ s/\n$//g;
-			next if ($c eq $sender and $suppress_from);
-			push @cc, $c;
-			printf("(cc-cmd) Adding cc: %s from: '%s'\n",
-				$c, $cc_cmd) unless $quiet;
-		}
-		close F
-			or die "(cc-cmd) failed to close pipe to '$cc_cmd'";
+	push @to, recipients_cmd("to-cmd", "to", $to_cmd, $t)
+		if defined $to_cmd;
+	push @cc, recipients_cmd("cc-cmd", "cc", $cc_cmd, $t)
+		if defined $cc_cmd && !$suppress_cc{'cccmd'};
+
+	if ($broken_encoding{$t} && !$has_content_type) {
+		$has_content_type = 1;
+		push @xh, "MIME-Version: 1.0",
+			"Content-Type: text/plain; charset=$auto_8bit_encoding",
+			"Content-Transfer-Encoding: 8bit";
+		$body_encoding = $auto_8bit_encoding;
+	}
+
+	if ($broken_encoding{$t} && !is_rfc2047_quoted($subject)) {
+		$subject = quote_rfc2047($subject, $auto_8bit_encoding);
 	}
 
 	if (defined $author and $author ne $sender) {
@@ -1134,6 +1322,7 @@
 				}
 			}
 			else {
+				$has_content_type = 1;
 				push @xh,
 				  'MIME-Version: 1.0',
 				  "Content-Type: text/plain; charset=$author_encoding",
@@ -1148,13 +1337,15 @@
 		($confirm =~ /^(?:auto|compose)$/ && $compose && $message_num == 1));
 	$needs_confirm = "inform" if ($needs_confirm && $confirm_unconfigured && @cc);
 
+	@to = (@initial_to, @to);
 	@cc = (@initial_cc, @cc);
 
 	my $message_was_sent = send_message();
 
 	# set up for the next message
 	if ($thread && $message_was_sent &&
-		($chain_reply_to || !defined $reply_to || length($reply_to) == 0)) {
+		(chain_reply_to() || !defined $reply_to || length($reply_to) == 0 ||
+		$message_num == 1)) {
 		$reply_to = $message_id;
 		if (length $references > 0) {
 			$references .= "\n $message_id";
@@ -1165,15 +1356,38 @@
 	$message_id = undef;
 }
 
+# Execute a command (e.g. $to_cmd) to get a list of email addresses
+# and return a results array
+sub recipients_cmd {
+	my ($prefix, $what, $cmd, $file) = @_;
+
+	my $sanitized_sender = sanitize_address($sender);
+	my @addresses = ();
+	open my $fh, "$cmd \Q$file\E |"
+	    or die "($prefix) Could not execute '$cmd'";
+	while (my $address = <$fh>) {
+		$address =~ s/^\s*//g;
+		$address =~ s/\s*$//g;
+		$address = sanitize_address($address);
+		next if ($address eq $sanitized_sender and $suppress_from);
+		push @addresses, $address;
+		printf("($prefix) Adding %s: %s from: '%s'\n",
+		       $what, $address, $cmd) unless $quiet;
+		}
+	close $fh
+	    or die "($prefix) failed to close pipe to '$cmd'";
+	return @addresses;
+}
+
 cleanup_compose_files();
 
-sub cleanup_compose_files() {
+sub cleanup_compose_files {
 	unlink($compose_filename, $compose_filename . ".final") if $compose;
 }
 
 $smtp->quit if $smtp;
 
-sub unique_email_list(@) {
+sub unique_email_list {
 	my %seen;
 	my @emails;
 
@@ -1211,3 +1425,17 @@
 	}
 	return 0;
 }
+
+sub body_or_subject_has_nonascii {
+	my $fn = shift;
+	open(my $fh, '<', $fn)
+		or die "unable to open $fn: $!\n";
+	while (my $line = <$fh>) {
+		last if $line =~ /^$/;
+		return 1 if $line =~ /^Subject.*[^[:ascii:]]/;
+	}
+	while (my $line = <$fh>) {
+		return 1 if $line =~ /[^[:ascii:]]/;
+	}
+	return 0;
+}
diff --git a/git-sh-i18n.sh b/git-sh-i18n.sh
new file mode 100644
index 0000000..6a27f68
--- /dev/null
+++ b/git-sh-i18n.sh
@@ -0,0 +1,92 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Ævar Arnfjörð Bjarmason
+#
+# This is Git's interface to gettext.sh. See po/README for usage
+# instructions.
+
+# Export the TEXTDOMAIN* data that we need for Git
+TEXTDOMAIN=git
+export TEXTDOMAIN
+if test -z "$GIT_TEXTDOMAINDIR"
+then
+	TEXTDOMAINDIR="@@LOCALEDIR@@"
+else
+	TEXTDOMAINDIR="$GIT_TEXTDOMAINDIR"
+fi
+export TEXTDOMAINDIR
+
+# First decide what scheme to use...
+GIT_INTERNAL_GETTEXT_SH_SCHEME=fallthrough
+if test -n "@@USE_GETTEXT_SCHEME@@"
+then
+	GIT_INTERNAL_GETTEXT_SH_SCHEME="@@USE_GETTEXT_SCHEME@@"
+elif test -n "$GIT_INTERNAL_GETTEXT_TEST_FALLBACKS"
+then
+	: no probing necessary
+elif test -n "$GIT_GETTEXT_POISON"
+then
+	GIT_INTERNAL_GETTEXT_SH_SCHEME=poison
+elif type gettext.sh >/dev/null 2>&1
+then
+	# GNU libintl's gettext.sh
+	GIT_INTERNAL_GETTEXT_SH_SCHEME=gnu
+elif test "$(gettext -h 2>&1)" = "-h"
+then
+	# gettext binary exists but no gettext.sh. likely to be a gettext
+	# binary on a Solaris or something that is not GNU libintl and
+	# lack eval_gettext.
+	GIT_INTERNAL_GETTEXT_SH_SCHEME=gettext_without_eval_gettext
+fi
+export GIT_INTERNAL_GETTEXT_SH_SCHEME
+
+# ... and then follow that decision.
+case "$GIT_INTERNAL_GETTEXT_SH_SCHEME" in
+gnu)
+	# Use libintl's gettext.sh, or fall back to English if we can't.
+	. gettext.sh
+	;;
+gettext_without_eval_gettext)
+	# Solaris has a gettext(1) but no eval_gettext(1)
+	eval_gettext () {
+		gettext "$1" | (
+			export PATH $(git sh-i18n--envsubst --variables "$1");
+			git sh-i18n--envsubst "$1"
+		)
+	}
+	;;
+poison)
+	# Emit garbage so that tests that incorrectly rely on translatable
+	# strings will fail.
+	gettext () {
+		printf "%s" "# GETTEXT POISON #"
+	}
+
+	eval_gettext () {
+		printf "%s" "# GETTEXT POISON #"
+	}
+	;;
+*)
+	gettext () {
+		printf "%s" "$1"
+	}
+
+	eval_gettext () {
+		printf "%s" "$1" | (
+			export PATH $(git sh-i18n--envsubst --variables "$1");
+			git sh-i18n--envsubst "$1"
+		)
+	}
+	;;
+esac
+
+# Git-specific wrapper functions
+gettextln () {
+	gettext "$1"
+	echo
+}
+
+eval_gettextln () {
+	eval_gettext "$1"
+	echo
+}
diff --git a/git-sh-setup.sh b/git-sh-setup.sh
old mode 100755
new mode 100644
index c41c2f7..770a86e
--- a/git-sh-setup.sh
+++ b/git-sh-setup.sh
@@ -39,9 +39,15 @@
 
 # @@BROKEN_PATH_FIX@@
 
-die() {
-	echo >&2 "$@"
-	exit 1
+die () {
+	die_with_status 1 "$@"
+}
+
+die_with_status () {
+	status=$1
+	shift
+	echo >&2 "$*"
+	exit "$status"
 }
 
 GIT_QUIET=
@@ -84,7 +90,7 @@
 	fi
 
 	case "$1" in
-		-h|--h|--he|--hel|--help)
+		-h)
 		echo "$LONG_USAGE"
 		exit
 	esac
@@ -99,19 +105,33 @@
 }
 
 git_editor() {
-	: "${GIT_EDITOR:=$(git config core.editor)}"
-	: "${GIT_EDITOR:=${VISUAL:-${EDITOR}}}"
-	case "$GIT_EDITOR,$TERM" in
-	,dumb)
-		echo >&2 "No editor specified in GIT_EDITOR, core.editor, VISUAL,"
-		echo >&2 "or EDITOR. Tried to fall back to vi but terminal is dumb."
-		echo >&2 "Please set one of these variables to an appropriate"
-		echo >&2 "editor or run $0 with options that will not cause an"
-		echo >&2 "editor to be invoked (e.g., -m or -F for git-commit)."
-		exit 1
-		;;
-	esac
-	eval "${GIT_EDITOR:=vi}" '"$@"'
+	if test -z "${GIT_EDITOR:+set}"
+	then
+		GIT_EDITOR="$(git var GIT_EDITOR)" || return $?
+	fi
+
+	eval "$GIT_EDITOR" '"$@"'
+}
+
+git_pager() {
+	if test -t 1
+	then
+		GIT_PAGER=$(git var GIT_PAGER)
+	else
+		GIT_PAGER=cat
+	fi
+	: ${LESS=-FRSX}
+	export LESS
+
+	eval "$GIT_PAGER" '"$@"'
+}
+
+sane_grep () {
+	GREP_OPTIONS= LC_ALL=C grep "$@"
+}
+
+sane_egrep () {
+	GREP_OPTIONS= LC_ALL=C egrep "$@"
 }
 
 is_bare_repository () {
@@ -119,44 +139,68 @@
 }
 
 cd_to_toplevel () {
-	cdup=$(git rev-parse --show-cdup)
-	if test ! -z "$cdup"
+	cdup=$(git rev-parse --show-toplevel) &&
+	cd "$cdup" || {
+		echo >&2 "Cannot chdir to $cdup, the toplevel of the working tree"
+		exit 1
+	}
+}
+
+require_work_tree_exists () {
+	if test "z$(git rev-parse --is-bare-repository)" != zfalse
 	then
-		# The "-P" option says to follow "physical" directory
-		# structure instead of following symbolic links.  When cdup is
-		# "../", this means following the ".." entry in the current
-		# directory instead textually removing a symlink path element
-		# from the PWD shell variable.  The "-P" behavior is more
-		# consistent with the C-style chdir used by most of Git.
-		cd -P "$cdup" || {
-			echo >&2 "Cannot chdir to $cdup, the toplevel of the working tree"
-			exit 1
-		}
+		die "fatal: $0 cannot be used without a working tree."
 	fi
 }
 
 require_work_tree () {
-	test $(git rev-parse --is-inside-work-tree) = true ||
+	test "$(git rev-parse --is-inside-work-tree 2>/dev/null)" = true ||
 	die "fatal: $0 cannot be used without a working tree."
 }
 
+require_clean_work_tree () {
+	git rev-parse --verify HEAD >/dev/null || exit 1
+	git update-index -q --ignore-submodules --refresh
+	err=0
+
+	if ! git diff-files --quiet --ignore-submodules
+	then
+		echo >&2 "Cannot $1: You have unstaged changes."
+		err=1
+	fi
+
+	if ! git diff-index --cached --quiet --ignore-submodules HEAD --
+	then
+		if [ $err = 0 ]
+		then
+		    echo >&2 "Cannot $1: Your index contains uncommitted changes."
+		else
+		    echo >&2 "Additionally, your index contains uncommitted changes."
+		fi
+		err=1
+	fi
+
+	if [ $err = 1 ]
+	then
+		test -n "$2" && echo >&2 "$2"
+		exit 1
+	fi
+}
+
 get_author_ident_from_commit () {
 	pick_author_script='
 	/^author /{
 		s/'\''/'\''\\'\'\''/g
 		h
 		s/^author \([^<]*\) <[^>]*> .*$/\1/
-		s/'\''/'\''\'\'\''/g
 		s/.*/GIT_AUTHOR_NAME='\''&'\''/p
 
 		g
 		s/^author [^<]* <\([^>]*\)> .*$/\1/
-		s/'\''/'\''\'\'\''/g
 		s/.*/GIT_AUTHOR_EMAIL='\''&'\''/p
 
 		g
-		s/^author [^<]* <[^>]*> \(.*\)$/\1/
-		s/'\''/'\''\'\'\''/g
+		s/^author [^<]* <[^>]*> \(.*\)$/@\1/
 		s/.*/GIT_AUTHOR_DATE='\''&'\''/p
 
 		q
@@ -167,6 +211,46 @@
 	LANG=C LC_ALL=C sed -ne "$pick_author_script"
 }
 
+# Clear repo-local GIT_* environment variables. Useful when switching to
+# another repository (e.g. when entering a submodule). See also the env
+# list in git_connect()
+clear_local_git_env() {
+	unset $(git rev-parse --local-env-vars)
+}
+
+
+# Platform specific tweaks to work around some commands
+case $(uname -s) in
+*MINGW*)
+	# Windows has its own (incompatible) sort and find
+	sort () {
+		/usr/bin/sort "$@"
+	}
+	find () {
+		/usr/bin/find "$@"
+	}
+	# git sees Windows-style pwd
+	pwd () {
+		builtin pwd -W
+	}
+	is_absolute_path () {
+		case "$1" in
+		[/\\]* | [A-Za-z]:*)
+			return 0 ;;
+		esac
+		return 1
+	}
+	;;
+*)
+	is_absolute_path () {
+		case "$1" in
+		/*)
+			return 0 ;;
+		esac
+		return 1
+	}
+esac
+
 # Make sure we are in a valid repository of a vintage we understand,
 # if we require to be in a git repository.
 if test -z "$NONGIT_OK"
@@ -186,16 +270,3 @@
 	}
 	: ${GIT_OBJECT_DIRECTORY="$GIT_DIR/objects"}
 fi
-
-# Fix some commands on Windows
-case $(uname -s) in
-*MINGW*)
-	# Windows has its own (incompatible) sort and find
-	sort () {
-		/usr/bin/sort "$@"
-	}
-	find () {
-		/usr/bin/find "$@"
-	}
-	;;
-esac
diff --git a/git-stash.sh b/git-stash.sh
index 03e589f..4e2c7f8 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -7,29 +7,48 @@
    or: $dashless drop [-q|--quiet] [<stash>]
    or: $dashless ( pop | apply ) [--index] [-q|--quiet] [<stash>]
    or: $dashless branch <branchname> [<stash>]
-   or: $dashless [save [--keep-index] [-q|--quiet] [<message>]]
+   or: $dashless [save [--patch] [-k|--[no-]keep-index] [-q|--quiet]
+		       [-u|--include-untracked] [-a|--all] [<message>]]
    or: $dashless clear"
 
 SUBDIRECTORY_OK=Yes
 OPTIONS_SPEC=
+START_DIR=`pwd`
 . git-sh-setup
+. git-sh-i18n
 require_work_tree
 cd_to_toplevel
 
 TMP="$GIT_DIR/.git-stash.$$"
-trap 'rm -f "$TMP-*"' 0
+TMPindex=${GIT_INDEX_FILE-"$GIT_DIR/index"}.stash.$$
+trap 'rm -f "$TMP-"* "$TMPindex"' 0
 
 ref_stash=refs/stash
 
+if git config --get-colorbool color.interactive; then
+       help_color="$(git config --get-color color.interactive.help 'red bold')"
+       reset_color="$(git config --get-color '' reset)"
+else
+       help_color=
+       reset_color=
+fi
+
 no_changes () {
 	git diff-index --quiet --cached HEAD --ignore-submodules -- &&
-	git diff-files --quiet --ignore-submodules
+	git diff-files --quiet --ignore-submodules &&
+	(test -z "$untracked" || test -z "$(untracked_files)")
+}
+
+untracked_files () {
+	excl_opt=--exclude-standard
+	test "$untracked" = "all" && excl_opt=
+	git ls-files -o -z $excl_opt
 }
 
 clear_stash () {
 	if test $# != 0
 	then
-		die "git stash clear with parameters is unimplemented"
+		die "$(gettext "git stash clear with parameters is unimplemented")"
 	fi
 	if current=$(git rev-parse --verify $ref_stash 2>/dev/null)
 	then
@@ -39,6 +58,7 @@
 
 create_stash () {
 	stash_msg="$1"
+	untracked="$2"
 
 	git update-index -q --refresh
 	if no_changes
@@ -49,9 +69,9 @@
 	# state of the base commit
 	if b_commit=$(git rev-parse --verify HEAD)
 	then
-		head=$(git log --no-color --abbrev-commit --pretty=oneline -n 1 HEAD --)
+		head=$(git rev-list --oneline -n 1 HEAD --)
 	else
-		die "You do not have the initial commit yet"
+		die "$(gettext "You do not have the initial commit yet")"
 	fi
 
 	if branch=$(git symbolic-ref -q HEAD)
@@ -66,20 +86,63 @@
 	i_tree=$(git write-tree) &&
 	i_commit=$(printf 'index on %s\n' "$msg" |
 		git commit-tree $i_tree -p $b_commit) ||
-		die "Cannot save the current index state"
+		die "$(gettext "Cannot save the current index state")"
 
-	# state of the working tree
-	w_tree=$( (
+	if test -n "$untracked"
+	then
+		# Untracked files are stored by themselves in a parentless commit, for
+		# ease of unpacking later.
+		u_commit=$(
+			untracked_files | (
+				export GIT_INDEX_FILE="$TMPindex"
+				rm -f "$TMPindex" &&
+				git update-index -z --add --remove --stdin &&
+				u_tree=$(git write-tree) &&
+				printf 'untracked files on %s\n' "$msg" | git commit-tree $u_tree  &&
+				rm -f "$TMPindex"
+		) ) || die "Cannot save the untracked files"
+
+		untracked_commit_option="-p $u_commit";
+	else
+		untracked_commit_option=
+	fi
+
+	if test -z "$patch_mode"
+	then
+
+		# state of the working tree
+		w_tree=$( (
+			git read-tree --index-output="$TMPindex" -m $i_tree &&
+			GIT_INDEX_FILE="$TMPindex" &&
+			export GIT_INDEX_FILE &&
+			git diff --name-only -z HEAD -- >"$TMP-stagenames" &&
+			git update-index -z --add --remove --stdin <"$TMP-stagenames" &&
+			git write-tree &&
+			rm -f "$TMPindex"
+		) ) ||
+			die "$(gettext "Cannot save the current worktree state")"
+
+	else
+
 		rm -f "$TMP-index" &&
-		cp -p ${GIT_INDEX_FILE-"$GIT_DIR/index"} "$TMP-index" &&
-		GIT_INDEX_FILE="$TMP-index" &&
-		export GIT_INDEX_FILE &&
-		git read-tree -m $i_tree &&
-		git add -u &&
-		git write-tree &&
-		rm -f "$TMP-index"
-	) ) ||
-		die "Cannot save the current worktree state"
+		GIT_INDEX_FILE="$TMP-index" git read-tree HEAD &&
+
+		# find out what the user wants
+		GIT_INDEX_FILE="$TMP-index" \
+			git add--interactive --patch=stash -- &&
+
+		# state of the working tree
+		w_tree=$(GIT_INDEX_FILE="$TMP-index" git write-tree) ||
+		die "$(gettext "Cannot save the current worktree state")"
+
+		git diff-tree -p HEAD $w_tree -- >"$TMP-patch" &&
+		test -s "$TMP-patch" ||
+		die "$(gettext "No changes selected")"
+
+		rm -f "$TMP-index" ||
+		die "$(gettext "Cannot remove temporary index (can't happen)")"
+
+	fi
 
 	# create the stash
 	if test -z "$stash_msg"
@@ -89,21 +152,57 @@
 		stash_msg=$(printf 'On %s: %s' "$branch" "$stash_msg")
 	fi
 	w_commit=$(printf '%s\n' "$stash_msg" |
-		git commit-tree $w_tree -p $b_commit -p $i_commit) ||
-		die "Cannot record working tree state"
+	git commit-tree $w_tree -p $b_commit -p $i_commit $untracked_commit_option) ||
+	die "$(gettext "Cannot record working tree state")"
 }
 
 save_stash () {
 	keep_index=
+	patch_mode=
+	untracked=
 	while test $# != 0
 	do
 		case "$1" in
-		--keep-index)
+		-k|--keep-index)
 			keep_index=t
 			;;
+		--no-keep-index)
+			keep_index=n
+			;;
+		-p|--patch)
+			patch_mode=t
+			# only default to keep if we don't already have an override
+			test -z "$keep_index" && keep_index=t
+			;;
 		-q|--quiet)
 			GIT_QUIET=t
 			;;
+		-u|--include-untracked)
+			untracked=untracked
+			;;
+		-a|--all)
+			untracked=all
+			;;
+		--)
+			shift
+			break
+			;;
+		-*)
+			option="$1"
+			# TRANSLATORS: $option is an invalid option, like
+			# `--blah-blah'. The 7 spaces at the beginning of the
+			# second line correspond to "error: ". So you should line
+			# up the second line with however many characters the
+			# translation of "error: " takes in your language. E.g. in
+			# English this is:
+			#
+			#    $ git stash save --blah-blah 2>&1 | head -n 2
+			#    error: unknown option for 'stash save': --blah-blah
+			#           To provide a message, use git stash save -- '--blah-blah'
+			eval_gettextln "error: unknown option for 'stash save': \$option
+       To provide a message, use git stash save -- '\$option'"
+			usage
+			;;
 		*)
 			break
 			;;
@@ -111,31 +210,52 @@
 		shift
 	done
 
+	if test -n "$patch_mode" && test -n "$untracked"
+	then
+	    die "Can't use --patch and --include-untracked or --all at the same time"
+	fi
+
 	stash_msg="$*"
 
 	git update-index -q --refresh
 	if no_changes
 	then
-		say 'No local changes to save'
+		say "$(gettext "No local changes to save")"
 		exit 0
 	fi
 	test -f "$GIT_DIR/logs/$ref_stash" ||
-		clear_stash || die "Cannot initialize stash"
+		clear_stash || die "$(gettext "Cannot initialize stash")"
 
-	create_stash "$stash_msg"
+	create_stash "$stash_msg" $untracked
 
 	# Make sure the reflog for stash is kept.
 	: >>"$GIT_DIR/logs/$ref_stash"
 
 	git update-ref -m "$stash_msg" $ref_stash $w_commit ||
-		die "Cannot save the current status"
+		die "$(gettext "Cannot save the current status")"
 	say Saved working directory and index state "$stash_msg"
 
-	git reset --hard ${GIT_QUIET:+-q}
-
-	if test -n "$keep_index" && test -n $i_tree
+	if test -z "$patch_mode"
 	then
-		git read-tree --reset -u $i_tree
+		git reset --hard ${GIT_QUIET:+-q}
+		test "$untracked" = "all" && CLEAN_X_OPTION=-x || CLEAN_X_OPTION=
+		if test -n "$untracked"
+		then
+			git clean --force --quiet -d $CLEAN_X_OPTION
+		fi
+
+		if test "$keep_index" = "t" && test -n $i_tree
+		then
+			git read-tree --reset -u $i_tree
+		fi
+	else
+		git apply -R < "$TMP-patch" ||
+		die "$(gettext "Cannot remove worktree changes")"
+
+		if test "$keep_index" != "t"
+		then
+			git reset
+		fi
 	fi
 }
 
@@ -145,69 +265,176 @@
 
 list_stash () {
 	have_stash || return 0
-	git log --no-color --pretty=oneline -g "$@" $ref_stash -- |
-	sed -n -e 's/^[.0-9a-f]* refs\///p'
+	git log --format="%gd: %gs" -g "$@" $ref_stash --
 }
 
 show_stash () {
-	flags=$(git rev-parse --no-revs --flags "$@")
-	if test -z "$flags"
-	then
-		flags=--stat
-	fi
+	assert_stash_like "$@"
 
-	w_commit=$(git rev-parse --verify --default $ref_stash "$@") &&
-	b_commit=$(git rev-parse --verify "$w_commit^") &&
-	git diff $flags $b_commit $w_commit
+	git diff ${FLAGS:---stat} $b_commit $w_commit
+}
+
+#
+# Parses the remaining options looking for flags and
+# at most one revision defaulting to ${ref_stash}@{0}
+# if none found.
+#
+# Derives related tree and commit objects from the
+# revision, if one is found.
+#
+# stash records the work tree, and is a merge between the
+# base commit (first parent) and the index tree (second parent).
+#
+#   REV is set to the symbolic version of the specified stash-like commit
+#   IS_STASH_LIKE is non-blank if ${REV} looks like a stash
+#   IS_STASH_REF is non-blank if the ${REV} looks like a stash ref
+#   s is set to the SHA1 of the stash commit
+#   w_commit is set to the commit containing the working tree
+#   b_commit is set to the base commit
+#   i_commit is set to the commit containing the index tree
+#   u_commit is set to the commit containing the untracked files tree
+#   w_tree is set to the working tree
+#   b_tree is set to the base tree
+#   i_tree is set to the index tree
+#   u_tree is set to the untracked files tree
+#
+#   GIT_QUIET is set to t if -q is specified
+#   INDEX_OPTION is set to --index if --index is specified.
+#   FLAGS is set to the remaining flags
+#
+# dies if:
+#   * too many revisions specified
+#   * no revision is specified and there is no stash stack
+#   * a revision is specified which cannot be resolve to a SHA1
+#   * a non-existent stash reference is specified
+#
+
+parse_flags_and_rev()
+{
+	test "$PARSE_CACHE" = "$*" && return 0 # optimisation
+	PARSE_CACHE="$*"
+
+	IS_STASH_LIKE=
+	IS_STASH_REF=
+	INDEX_OPTION=
+	s=
+	w_commit=
+	b_commit=
+	i_commit=
+	u_commit=
+	w_tree=
+	b_tree=
+	i_tree=
+	u_tree=
+
+	REV=$(git rev-parse --no-flags --symbolic "$@") || exit 1
+
+	FLAGS=
+	for opt
+	do
+		case "$opt" in
+			-q|--quiet)
+				GIT_QUIET=-t
+			;;
+			--index)
+				INDEX_OPTION=--index
+			;;
+			-*)
+				FLAGS="${FLAGS}${FLAGS:+ }$opt"
+			;;
+		esac
+	done
+
+	set -- $REV
+
+	case $# in
+		0)
+			have_stash || die "$(gettext "No stash found.")"
+			set -- ${ref_stash}@{0}
+		;;
+		1)
+			:
+		;;
+		*)
+			die "$(eval_gettext "Too many revisions specified: \$REV")"
+		;;
+	esac
+
+	REV=$(git rev-parse --quiet --symbolic --verify $1 2>/dev/null) || {
+		reference="$1"
+		die "$(eval_gettext "\$reference is not valid reference")"
+	}
+
+	i_commit=$(git rev-parse --quiet --verify $REV^2 2>/dev/null) &&
+	set -- $(git rev-parse $REV $REV^1 $REV: $REV^1: $REV^2: 2>/dev/null) &&
+	s=$1 &&
+	w_commit=$1 &&
+	b_commit=$2 &&
+	w_tree=$3 &&
+	b_tree=$4 &&
+	i_tree=$5 &&
+	IS_STASH_LIKE=t &&
+	test "$ref_stash" = "$(git rev-parse --symbolic-full-name "${REV%@*}")" &&
+	IS_STASH_REF=t
+
+	u_commit=$(git rev-parse --quiet --verify $REV^3 2>/dev/null) &&
+	u_tree=$(git rev-parse $REV^3: 2>/dev/null)
+}
+
+is_stash_like()
+{
+	parse_flags_and_rev "$@"
+	test -n "$IS_STASH_LIKE"
+}
+
+assert_stash_like() {
+	is_stash_like "$@" || {
+		args="$*"
+		die "$(eval_gettext "'\$args' is not a stash-like commit")"
+	}
+}
+
+is_stash_ref() {
+	is_stash_like "$@" && test -n "$IS_STASH_REF"
+}
+
+assert_stash_ref() {
+	is_stash_ref "$@" || {
+		args="$*"
+		die "$(eval_gettext "'\$args' is not a stash reference")"
+	}
 }
 
 apply_stash () {
-	git update-index -q --refresh &&
-	git diff-files --quiet --ignore-submodules ||
-		die 'Cannot apply to a dirty working tree, please stage your changes'
 
-	unstash_index=
+	assert_stash_like "$@"
 
-	while test $# != 0
-	do
-		case "$1" in
-		--index)
-			unstash_index=t
-			;;
-		-q|--quiet)
-			GIT_QUIET=t
-			;;
-		*)
-			break
-			;;
-		esac
-		shift
-	done
+	git update-index -q --refresh || die "$(gettext "unable to refresh index")"
 
 	# current index state
 	c_tree=$(git write-tree) ||
-		die 'Cannot apply a stash in the middle of a merge'
-
-	# stash records the work tree, and is a merge between the
-	# base commit (first parent) and the index tree (second parent).
-	s=$(git rev-parse --verify --default $ref_stash "$@") &&
-	w_tree=$(git rev-parse --verify "$s:") &&
-	b_tree=$(git rev-parse --verify "$s^1:") &&
-	i_tree=$(git rev-parse --verify "$s^2:") ||
-		die "$*: no valid stashed state found"
+		die "$(gettext "Cannot apply a stash in the middle of a merge")"
 
 	unstashed_index_tree=
-	if test -n "$unstash_index" && test "$b_tree" != "$i_tree" &&
+	if test -n "$INDEX_OPTION" && test "$b_tree" != "$i_tree" &&
 			test "$c_tree" != "$i_tree"
 	then
 		git diff-tree --binary $s^2^..$s^2 | git apply --cached
 		test $? -ne 0 &&
-			die 'Conflicts in index. Try without --index.'
+			die "$(gettext "Conflicts in index. Try without --index.")"
 		unstashed_index_tree=$(git write-tree) ||
-			die 'Could not save index tree'
+			die "$(gettext "Could not save index tree")"
 		git reset
 	fi
 
+	if test -n "$u_tree"
+	then
+		GIT_INDEX_FILE="$TMPindex" git-read-tree "$u_tree" &&
+		GIT_INDEX_FILE="$TMPindex" git checkout-index --all &&
+		rm -f "$TMPindex" ||
+		die 'Could not restore untracked files from stash'
+	fi
+
 	eval "
 		GITHEAD_$w_tree='Stashed changes' &&
 		GITHEAD_$c_tree='Updated upstream' &&
@@ -217,7 +444,7 @@
 
 	if test -n "$GIT_QUIET"
 	then
-		export GIT_MERGE_VERBOSITY=0
+		GIT_MERGE_VERBOSITY=0 && export GIT_MERGE_VERBOSITY
 	fi
 	if git merge-recursive $b_tree -- $c_tree $w_tree
 	then
@@ -230,7 +457,7 @@
 			git diff-index --cached --name-only --diff-filter=A $c_tree >"$a" &&
 			git read-tree --reset $c_tree &&
 			git update-index --add --stdin <"$a" ||
-				die "Cannot unstage modified files"
+				die "$(gettext "Cannot unstage modified files")"
 			rm -f "$a"
 		fi
 		squelch=
@@ -238,79 +465,67 @@
 		then
 			squelch='>/dev/null 2>&1'
 		fi
-		eval "git status $squelch" || :
+		(cd "$START_DIR" && eval "git status $squelch") || :
 	else
 		# Merge conflict; keep the exit status from merge-recursive
 		status=$?
-		if test -n "$unstash_index"
+		if test -n "$INDEX_OPTION"
 		then
-			echo >&2 'Index was not unstashed.'
+			gettextln "Index was not unstashed." >&2
 		fi
 		exit $status
 	fi
 }
 
+pop_stash() {
+	assert_stash_ref "$@"
+
+	apply_stash "$@" &&
+	drop_stash "$@"
+}
+
 drop_stash () {
-	have_stash || die 'No stash entries to drop'
+	assert_stash_ref "$@"
 
-	while test $# != 0
-	do
-		case "$1" in
-		-q|--quiet)
-			GIT_QUIET=t
-			;;
-		*)
-			break
-			;;
-		esac
-		shift
-	done
-
-	if test $# = 0
-	then
-		set x "$ref_stash@{0}"
-		shift
-	fi
-	# Verify supplied argument looks like a stash entry
-	s=$(git rev-parse --verify "$@") &&
-	git rev-parse --verify "$s:"   > /dev/null 2>&1 &&
-	git rev-parse --verify "$s^1:" > /dev/null 2>&1 &&
-	git rev-parse --verify "$s^2:" > /dev/null 2>&1 ||
-		die "$*: not a valid stashed state"
-
-	git reflog delete --updateref --rewrite "$@" &&
-		say "Dropped $* ($s)" || die "$*: Could not drop stash entry"
+	git reflog delete --updateref --rewrite "${REV}" &&
+		say "$(eval_gettext "Dropped \${REV} (\$s)")" ||
+		die "$(eval_gettext "\${REV}: Could not drop stash entry")"
 
 	# clear_stash if we just dropped the last stash entry
-	git rev-parse --verify "$ref_stash@{0}" > /dev/null 2>&1 || clear_stash
+	git rev-parse --verify "$ref_stash@{0}" >/dev/null 2>&1 || clear_stash
 }
 
 apply_to_branch () {
-	have_stash || die 'Nothing to apply'
-
-	test -n "$1" || die 'No branch name specified'
+	test -n "$1" || die "$(gettext "No branch name specified")"
 	branch=$1
+	shift 1
 
-	if test -z "$2"
-	then
-		set x "$ref_stash@{0}"
-	fi
-	stash=$2
+	set -- --index "$@"
+	assert_stash_like "$@"
 
-	git checkout -b $branch $stash^ &&
-	apply_stash --index $stash &&
-	drop_stash $stash
+	git checkout -b $branch $REV^ &&
+	apply_stash "$@" && {
+		test -z "$IS_STASH_REF" || drop_stash "$@"
+	}
 }
 
+PARSE_CACHE='--not-parsed'
+# The default command is "save" if nothing but options are given
+seen_non_option=
+for opt
+do
+	case "$opt" in
+	-*) ;;
+	*) seen_non_option=t; break ;;
+	esac
+done
+
+test -n "$seen_non_option" || set "save" "$@"
+
 # Main command set
 case "$1" in
 list)
 	shift
-	if test $# = 0
-	then
-		set x -n 10
-		shift
-	fi
 	list_stash "$@"
 	;;
 show)
@@ -342,23 +557,20 @@
 	;;
 pop)
 	shift
-	if apply_stash "$@"
-	then
-		test -z "$unstash_index" || shift
-		drop_stash "$@"
-	fi
+	pop_stash "$@"
 	;;
 branch)
 	shift
 	apply_to_branch "$@"
 	;;
 *)
-	if test $# -eq 0
-	then
+	case $# in
+	0)
 		save_stash &&
-		say '(To restore them type "git stash apply")'
-	else
+		say "$(gettext "(To restore them type \"git stash apply\")")"
+		;;
+	*)
 		usage
-	fi
+	esac
 	;;
 esac
diff --git a/git-submodule.sh b/git-submodule.sh
index ebed711..dba4d39 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -1,38 +1,96 @@
 #!/bin/sh
 #
-# git-submodules.sh: add, init, update or list git submodules
+# git-submodule.sh: add, init, update or list git submodules
 #
 # Copyright (c) 2007 Lars Hjemli
 
-USAGE="[--quiet] [--cached] \
-[add [-b branch] <repo> <path>]|[status|init|update [-i|--init] [-N|--no-fetch] [--rebase|--merge]|summary [-n|--summary-limit <n>] [<commit>]] \
-[--] [<path>...]|[foreach <command>]|[sync [--] [<path>...]]"
+dashless=$(basename "$0" | sed -e 's/-/ /')
+USAGE="[--quiet] add [-b branch] [-f|--force] [--reference <repository>] [--] <repository> [<path>]
+   or: $dashless [--quiet] status [--cached] [--recursive] [--] [<path>...]
+   or: $dashless [--quiet] init [--] [<path>...]
+   or: $dashless [--quiet] update [--init] [-N|--no-fetch] [-f|--force] [--rebase] [--reference <repository>] [--merge] [--recursive] [--] [<path>...]
+   or: $dashless [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]
+   or: $dashless [--quiet] foreach [--recursive] <command>
+   or: $dashless [--quiet] sync [--] [<path>...]"
 OPTIONS_SPEC=
 . git-sh-setup
+. git-sh-i18n
 . git-parse-remote
 require_work_tree
 
 command=
 branch=
+force=
 reference=
 cached=
+recursive=
+init=
+files=
 nofetch=
 update=
+prefix=
 
-# Resolve relative url by appending to parent's url
+# The function takes at most 2 arguments. The first argument is the
+# URL that navigates to the submodule origin repo. When relative, this URL
+# is relative to the superproject origin URL repo. The second up_path
+# argument, if specified, is the relative path that navigates
+# from the submodule working tree to the superproject working tree.
+#
+# The output of the function is the origin URL of the submodule.
+#
+# The output will either be an absolute URL or filesystem path (if the
+# superproject origin URL is an absolute URL or filesystem path,
+# respectively) or a relative file system path (if the superproject
+# origin URL is a relative file system path).
+#
+# When the output is a relative file system path, the path is either
+# relative to the submodule working tree, if up_path is specified, or to
+# the superproject working tree otherwise.
 resolve_relative_url ()
 {
 	remote=$(get_default_remote)
 	remoteurl=$(git config "remote.$remote.url") ||
-		die "remote ($remote) does not have a url defined in .git/config"
+		remoteurl=$(pwd) # the repository is its own authoritative upstream
 	url="$1"
 	remoteurl=${remoteurl%/}
+	sep=/
+	up_path="$2"
+
+	case "$remoteurl" in
+	*:*|/*)
+		is_relative=
+		;;
+	./*|../*)
+		is_relative=t
+		;;
+	*)
+		is_relative=t
+		remoteurl="./$remoteurl"
+		;;
+	esac
+
 	while test -n "$url"
 	do
 		case "$url" in
 		../*)
 			url="${url#../}"
-			remoteurl="${remoteurl%/*}"
+			case "$remoteurl" in
+			*/*)
+				remoteurl="${remoteurl%/*}"
+				;;
+			*:*)
+				remoteurl="${remoteurl%:*}"
+				sep=:
+				;;
+			*)
+				if test -z "$is_relative" || test "." = "$remoteurl"
+				then
+					die "$(eval_gettext "cannot strip one component off url '\$remoteurl'")"
+				else
+					remoteurl=.
+				fi
+				;;
+			esac
 			;;
 		./*)
 			url="${url#./}"
@@ -41,7 +99,8 @@
 			break;;
 		esac
 	done
-	echo "$remoteurl/${url%/}"
+	remoteurl="$remoteurl$sep${url%/}"
+	echo "${is_relative:+${up_path}}${remoteurl#./}"
 }
 
 #
@@ -50,7 +109,24 @@
 #
 module_list()
 {
-	git ls-files --error-unmatch --stage -- "$@" | grep '^160000 '
+	git ls-files --error-unmatch --stage -- "$@" |
+	perl -e '
+	my %unmerged = ();
+	my ($null_sha1) = ("0" x 40);
+	while (<STDIN>) {
+		chomp;
+		my ($mode, $sha1, $stage, $path) =
+			/^([0-7]+) ([0-9a-f]{40}) ([0-3])\t(.*)$/;
+		next unless $mode eq "160000";
+		if ($stage ne "0") {
+			if (!$unmerged{$path}++) {
+				print "$mode $null_sha1 U\t$path\n";
+			}
+			next;
+		}
+		print "$_\n";
+	}
+	'
 }
 
 #
@@ -61,12 +137,13 @@
 module_name()
 {
 	# Do we have "submodule.<something>.path = $1" defined in .gitmodules file?
+	sm_path="$1"
 	re=$(printf '%s\n' "$1" | sed -e 's/[].[^$\\*]/\\&/g')
 	name=$( git config -f .gitmodules --get-regexp '^submodule\..*\.path$' |
 		sed -n -e 's|^submodule\.\(.*\)\.path '"$re"'$|\1|p' )
-       test -z "$name" &&
-       die "No submodule mapping found in .gitmodules for path '$path'"
-       echo "$name"
+	test -z "$name" &&
+	die "$(eval_gettext "No submodule mapping found in .gitmodules for path '\$sm_path'")"
+	echo "$name"
 }
 
 #
@@ -79,31 +156,62 @@
 #
 module_clone()
 {
-	path=$1
+	sm_path=$1
 	url=$2
 	reference="$3"
-
-	# If there already is a directory at the submodule path,
-	# expect it to be empty (since that is the default checkout
-	# action) and try to remove it.
-	# Note: if $path is a symlink to a directory the test will
-	# succeed but the rmdir will fail. We might want to fix this.
-	if test -d "$path"
+	quiet=
+	if test -n "$GIT_QUIET"
 	then
-		rmdir "$path" 2>/dev/null ||
-		die "Directory '$path' exist, but is neither empty nor a git repository"
+		quiet=-q
 	fi
 
-	test -e "$path" &&
-	die "A file already exist at path '$path'"
+	gitdir=
+	gitdir_base=
+	name=$(module_name "$sm_path" 2>/dev/null)
+	test -n "$name" || name="$sm_path"
+	base_name=$(dirname "$name")
 
-	if test -n "$reference"
+	gitdir=$(git rev-parse --git-dir)
+	gitdir_base="$gitdir/modules/$base_name"
+	gitdir="$gitdir/modules/$name"
+
+	if test -d "$gitdir"
 	then
-		git-clone "$reference" -n "$url" "$path"
+		mkdir -p "$sm_path"
+		rm -f "$gitdir/index"
 	else
-		git-clone -n "$url" "$path"
-	fi ||
-	die "Clone of '$url' into submodule path '$path' failed"
+		mkdir -p "$gitdir_base"
+		git clone $quiet -n ${reference:+"$reference"} \
+			--separate-git-dir "$gitdir" "$url" "$sm_path" ||
+		die "$(eval_gettext "Clone of '\$url' into submodule path '\$sm_path' failed")"
+	fi
+
+	# We already are at the root of the work tree but cd_to_toplevel will
+	# resolve any symlinks that might be present in $PWD
+	a=$(cd_to_toplevel && cd "$gitdir" && pwd)/
+	b=$(cd_to_toplevel && cd "$sm_path" && pwd)/
+	# normalize Windows-style absolute paths to POSIX-style absolute paths
+	case $a in [a-zA-Z]:/*) a=/${a%%:*}${a#*:} ;; esac
+	case $b in [a-zA-Z]:/*) b=/${b%%:*}${b#*:} ;; esac
+	# Remove all common leading directories after a sanity check
+	if test "${a#$b}" != "$a" || test "${b#$a}" != "$b"; then
+		die "$(eval_gettext "Gitdir '\$a' is part of the submodule path '\$b' or vice versa")"
+	fi
+	while test "${a%%/*}" = "${b%%/*}"
+	do
+		a=${a#*/}
+		b=${b#*/}
+	done
+	# Now chop off the trailing '/'s that were added in the beginning
+	a=${a%/}
+	b=${b%/}
+
+	# Turn each leading "*/" component into "../"
+	rel=$(echo $b | sed -e 's|[^/][^/]*|..|g')
+	echo "gitdir: $rel/$a" >"$sm_path/.git"
+
+	rel=$(echo $a | sed -e 's|[^/][^/]*|..|g')
+	(clear_local_git_env; cd "$sm_path" && GIT_WORK_TREE=. git config core.worktree "$rel/$b")
 }
 
 #
@@ -124,6 +232,9 @@
 			branch=$2
 			shift
 			;;
+		-f | --force)
+			force=$1
+			;;
 		-q|--quiet)
 			GIT_QUIET=1
 			;;
@@ -151,9 +262,14 @@
 	done
 
 	repo=$1
-	path=$2
+	sm_path=$2
 
-	if test -z "$repo" -o -z "$path"; then
+	if test -z "$sm_path"; then
+		sm_path=$(echo "$repo" |
+			sed -e 's|/$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g')
+	fi
+
+	if test -z "$repo" -o -z "$sm_path"; then
 		usage
 	fi
 
@@ -168,13 +284,13 @@
 		realrepo=$repo
 		;;
 	*)
-		die "repo URL: '$repo' must be absolute or begin with ./|../"
+		die "$(eval_gettext "repo URL: '\$repo' must be absolute or begin with ./|../")"
 	;;
 	esac
 
 	# normalize path:
 	# multiple //; leading ./; /./; /../; trailing /
-	path=$(printf '%s/\n' "$path" |
+	sm_path=$(printf '%s/\n' "$sm_path" |
 		sed -e '
 			s|//*|/|g
 			s|^\(\./\)*||
@@ -184,49 +300,49 @@
 			tstart
 			s|/*$||
 		')
-	git ls-files --error-unmatch "$path" > /dev/null 2>&1 &&
-	die "'$path' already exists in the index"
+	git ls-files --error-unmatch "$sm_path" > /dev/null 2>&1 &&
+	die "$(eval_gettext "'\$sm_path' already exists in the index")"
+
+	if test -z "$force" && ! git add --dry-run --ignore-missing "$sm_path" > /dev/null 2>&1
+	then
+		eval_gettextln "The following path is ignored by one of your .gitignore files:
+\$sm_path
+Use -f if you really want to add it." >&2
+		exit 1
+	fi
 
 	# perhaps the path exists and is already a git repo, else clone it
-	if test -e "$path"
+	if test -e "$sm_path"
 	then
-		if test -d "$path"/.git -o -f "$path"/.git
+		if test -d "$sm_path"/.git -o -f "$sm_path"/.git
 		then
-			echo "Adding existing repo at '$path' to the index"
+			eval_gettextln "Adding existing repo at '\$sm_path' to the index"
 		else
-			die "'$path' already exists and is not a valid git repo"
+			die "$(eval_gettext "'\$sm_path' already exists and is not a valid git repo")"
 		fi
 
-		case "$repo" in
-		./*|../*)
-			url=$(resolve_relative_url "$repo") || exit
-		    ;;
-		*)
-			url="$repo"
-			;;
-		esac
-		git config submodule."$path".url "$url"
 	else
 
-		module_clone "$path" "$realrepo" "$reference" || exit
+		module_clone "$sm_path" "$realrepo" "$reference" || exit
 		(
-			unset GIT_DIR
-			cd "$path" &&
+			clear_local_git_env
+			cd "$sm_path" &&
 			# ash fails to wordsplit ${branch:+-b "$branch"...}
 			case "$branch" in
 			'') git checkout -f -q ;;
-			?*) git checkout -f -q -b "$branch" "origin/$branch" ;;
+			?*) git checkout -f -q -B "$branch" "origin/$branch" ;;
 			esac
-		) || die "Unable to checkout submodule '$path'"
+		) || die "$(eval_gettext "Unable to checkout submodule '\$sm_path'")"
 	fi
+	git config submodule."$sm_path".url "$realrepo"
 
-	git add "$path" ||
-	die "Failed to add submodule '$path'"
+	git add $force "$sm_path" ||
+	die "$(eval_gettext "Failed to add submodule '\$sm_path'")"
 
-	git config -f .gitmodules submodule."$path".path "$path" &&
-	git config -f .gitmodules submodule."$path".url "$repo" &&
-	git add .gitmodules ||
-	die "Failed to register submodule '$path'"
+	git config -f .gitmodules submodule."$sm_path".path "$sm_path" &&
+	git config -f .gitmodules submodule."$sm_path".url "$repo" &&
+	git add --force .gitmodules ||
+	die "$(eval_gettext "Failed to register submodule '\$sm_path'")"
 }
 
 #
@@ -237,14 +353,52 @@
 #
 cmd_foreach()
 {
-	module_list |
-	while read mode sha1 stage path
+	# parse $args after "submodule ... foreach".
+	while test $# -ne 0
 	do
-		if test -e "$path"/.git
+		case "$1" in
+		-q|--quiet)
+			GIT_QUIET=1
+			;;
+		--recursive)
+			recursive=1
+			;;
+		-*)
+			usage
+			;;
+		*)
+			break
+			;;
+		esac
+		shift
+	done
+
+	toplevel=$(pwd)
+
+	# dup stdin so that it can be restored when running the external
+	# command in the subshell (and a recursive call to this function)
+	exec 3<&0
+
+	module_list |
+	while read mode sha1 stage sm_path
+	do
+		if test -e "$sm_path"/.git
 		then
-			say "Entering '$path'"
-			(cd "$path" && eval "$@") ||
-			die "Stopping at '$path'; script returned non-zero status."
+			say "$(eval_gettext "Entering '\$prefix\$sm_path'")"
+			name=$(module_name "$sm_path")
+			(
+				prefix="$prefix$sm_path/"
+				clear_local_git_env
+				# we make $path available to scripts ...
+				path=$sm_path
+				cd "$sm_path" &&
+				eval "$@" &&
+				if test -n "$recursive"
+				then
+					cmd_foreach "--recursive" "$@"
+				fi
+			) <&3 3<&- ||
+			die "$(eval_gettext "Stopping at '\$sm_path'; script returned non-zero status.")"
 		fi
 	done
 }
@@ -278,33 +432,35 @@
 	done
 
 	module_list "$@" |
-	while read mode sha1 stage path
+	while read mode sha1 stage sm_path
 	do
-		# Skip already registered paths
-		name=$(module_name "$path") || exit
-		url=$(git config submodule."$name".url)
-		test -z "$url" || continue
+		name=$(module_name "$sm_path") || exit
 
-		url=$(git config -f .gitmodules submodule."$name".url)
-		test -z "$url" &&
-		die "No url found for submodule path '$path' in .gitmodules"
+		# Copy url setting when it is not set yet
+		if test -z "$(git config "submodule.$name.url")"
+		then
+			url=$(git config -f .gitmodules submodule."$name".url)
+			test -z "$url" &&
+			die "$(eval_gettext "No url found for submodule path '\$sm_path' in .gitmodules")"
 
-		# Possibly a url relative to parent
-		case "$url" in
-		./*|../*)
-			url=$(resolve_relative_url "$url") || exit
-			;;
-		esac
+			# Possibly a url relative to parent
+			case "$url" in
+			./*|../*)
+				url=$(resolve_relative_url "$url") || exit
+				;;
+			esac
+			git config submodule."$name".url "$url" ||
+			die "$(eval_gettext "Failed to register url for submodule path '\$sm_path'")"
 
-		git config submodule."$name".url "$url" ||
-		die "Failed to register url for submodule path '$path'"
+			say "$(eval_gettext "Submodule '\$name' (\$url) registered for path '\$sm_path'")"
+		fi
 
+		# Copy "update" setting when it is not set yet
 		upd="$(git config -f .gitmodules submodule."$name".update)"
 		test -z "$upd" ||
+		test -n "$(git config submodule."$name".update)" ||
 		git config submodule."$name".update "$upd" ||
-		die "Failed to register update mode for submodule path '$path'"
-
-		say "Submodule '$name' ($url) registered for path '$path'"
+		die "$(eval_gettext "Failed to register update mode for submodule path '\$sm_path'")"
 	done
 }
 
@@ -316,38 +472,43 @@
 cmd_update()
 {
 	# parse $args after "submodule ... update".
+	orig_flags=
 	while test $# -ne 0
 	do
 		case "$1" in
 		-q|--quiet)
-			shift
 			GIT_QUIET=1
 			;;
 		-i|--init)
 			init=1
-			shift
 			;;
 		-N|--no-fetch)
-			shift
 			nofetch=1
 			;;
+		-f|--force)
+			force=$1
+			;;
 		-r|--rebase)
-			shift
 			update="rebase"
 			;;
 		--reference)
 			case "$2" in '') usage ;; esac
 			reference="--reference=$2"
-			shift 2
+			orig_flags="$orig_flags $(git rev-parse --sq-quote "$1")"
+			shift
 			;;
 		--reference=*)
 			reference="$1"
-			shift
 			;;
 		-m|--merge)
-			shift
 			update="merge"
 			;;
+		--recursive)
+			recursive=1
+			;;
+		--checkout)
+			update="checkout"
+			;;
 		--)
 			shift
 			break
@@ -359,6 +520,8 @@
 			break
 			;;
 		esac
+		orig_flags="$orig_flags $(git rev-parse --sq-quote "$1")"
+		shift
 	done
 
 	if test -n "$init"
@@ -366,80 +529,149 @@
 		cmd_init "--" "$@" || return
 	fi
 
-	module_list "$@" |
-	while read mode sha1 stage path
+	cloned_modules=
+	module_list "$@" | {
+	err=
+	while read mode sha1 stage sm_path
 	do
-		name=$(module_name "$path") || exit
+		if test "$stage" = U
+		then
+			echo >&2 "Skipping unmerged submodule $sm_path"
+			continue
+		fi
+		name=$(module_name "$sm_path") || exit
 		url=$(git config submodule."$name".url)
-		update_module=$(git config submodule."$name".update)
+		if ! test -z "$update"
+		then
+			update_module=$update
+		else
+			update_module=$(git config submodule."$name".update)
+		fi
+
+		if test "$update_module" = "none"
+		then
+			echo "Skipping submodule '$sm_path'"
+			continue
+		fi
+
 		if test -z "$url"
 		then
 			# Only mention uninitialized submodules when its
 			# path have been specified
 			test "$#" != "0" &&
-			say "Submodule path '$path' not initialized" &&
-			say "Maybe you want to use 'update --init'?"
+			say "$(eval_gettext "Submodule path '\$sm_path' not initialized
+Maybe you want to use 'update --init'?")"
 			continue
 		fi
 
-		if ! test -d "$path"/.git -o -f "$path"/.git
+		if ! test -d "$sm_path"/.git -o -f "$sm_path"/.git
 		then
-			module_clone "$path" "$url" "$reference"|| exit
+			module_clone "$sm_path" "$url" "$reference"|| exit
+			cloned_modules="$cloned_modules;$name"
 			subsha1=
 		else
-			subsha1=$(unset GIT_DIR; cd "$path" &&
+			subsha1=$(clear_local_git_env; cd "$sm_path" &&
 				git rev-parse --verify HEAD) ||
-			die "Unable to find current revision in submodule path '$path'"
-		fi
-
-		if ! test -z "$update"
-		then
-			update_module=$update
+			die "$(eval_gettext "Unable to find current revision in submodule path '\$sm_path'")"
 		fi
 
 		if test "$subsha1" != "$sha1"
 		then
-			force=
-			if test -z "$subsha1"
+			subforce=$force
+			# If we don't already have a -f flag and the submodule has never been checked out
+			if test -z "$subsha1" -a -z "$force"
 			then
-				force="-f"
+				subforce="-f"
 			fi
 
 			if test -z "$nofetch"
 			then
-				(unset GIT_DIR; cd "$path" &&
-					git-fetch) ||
-				die "Unable to fetch in submodule path '$path'"
+				# Run fetch only if $sha1 isn't present or it
+				# is not reachable from a ref.
+				(clear_local_git_env; cd "$sm_path" &&
+					( (rev=$(git rev-list -n 1 $sha1 --not --all 2>/dev/null) &&
+					 test -z "$rev") || git-fetch)) ||
+				die "$(eval_gettext "Unable to fetch in submodule path '\$sm_path'")"
 			fi
 
+			# Is this something we just cloned?
+			case ";$cloned_modules;" in
+			*";$name;"*)
+				# then there is no local change to integrate
+				update_module= ;;
+			esac
+
+			must_die_on_failure=
 			case "$update_module" in
 			rebase)
 				command="git rebase"
-				action="rebase"
-				msg="rebased onto"
+				die_msg="$(eval_gettext "Unable to rebase '\$sha1' in submodule path '\$sm_path'")"
+				say_msg="$(eval_gettext "Submodule path '\$sm_path': rebased into '\$sha1'")"
+				must_die_on_failure=yes
 				;;
 			merge)
 				command="git merge"
-				action="merge"
-				msg="merged in"
+				die_msg="$(eval_gettext "Unable to merge '\$sha1' in submodule path '\$sm_path'")"
+				say_msg="$(eval_gettext "Submodule path '\$sm_path': merged in '\$sha1'")"
+				must_die_on_failure=yes
 				;;
 			*)
-				command="git checkout $force -q"
-				action="checkout"
-				msg="checked out"
+				command="git checkout $subforce -q"
+				die_msg="$(eval_gettext "Unable to checkout '\$sha1' in submodule path '\$sm_path'")"
+				say_msg="$(eval_gettext "Submodule path '\$sm_path': checked out '\$sha1'")"
 				;;
 			esac
 
-			(unset GIT_DIR; cd "$path" && $command "$sha1") ||
-			die "Unable to $action '$sha1' in submodule path '$path'"
-			say "Submodule path '$path': $msg '$sha1'"
+			if (clear_local_git_env; cd "$sm_path" && $command "$sha1")
+			then
+				say "$say_msg"
+			elif test -n "$must_die_on_failure"
+			then
+				die_with_status 2 "$die_msg"
+			else
+				err="${err};$die_msg"
+				continue
+			fi
+		fi
+
+		if test -n "$recursive"
+		then
+			(clear_local_git_env; cd "$sm_path" && eval cmd_update "$orig_flags")
+			res=$?
+			if test $res -gt 0
+			then
+				die_msg="$(eval_gettext "Failed to recurse into submodule path '\$sm_path'")"
+				if test $res -eq 1
+				then
+					err="${err};$die_msg"
+					continue
+				else
+					die_with_status $res "$die_msg"
+				fi
+			fi
 		fi
 	done
+
+	if test -n "$err"
+	then
+		OIFS=$IFS
+		IFS=';'
+		for e in $err
+		do
+			if test -n "$e"
+			then
+				echo >&2 "$e"
+			fi
+		done
+		IFS=$OIFS
+		exit 1
+	fi
+	}
 }
 
 set_name_rev () {
 	revname=$( (
-		unset GIT_DIR
+		clear_local_git_env
 		cd "$1" && {
 			git describe "$2" 2>/dev/null ||
 			git describe --tags "$2" 2>/dev/null ||
@@ -460,6 +692,7 @@
 cmd_summary() {
 	summary_limit=-1
 	for_status=
+	diff_cmd=diff-index
 
 	# parse $args after "submodule ... summary".
 	while test $# -ne 0
@@ -468,6 +701,9 @@
 		--cached)
 			cached="$1"
 			;;
+		--files)
+			files="$1"
+			;;
 		--for-status)
 			for_status="$1"
 			;;
@@ -496,18 +732,31 @@
 
 	test $summary_limit = 0 && return
 
-	if rev=$(git rev-parse -q --verify "$1^0")
+	if rev=$(git rev-parse -q --verify --default HEAD ${1+"$1"})
 	then
 		head=$rev
-		shift
+		test $# = 0 || shift
+	elif test -z "$1" -o "$1" = "HEAD"
+	then
+		# before the first commit: compare with an empty tree
+		head=$(git hash-object -w -t tree --stdin </dev/null)
+		test -z "$1" || shift
 	else
-		head=HEAD
+		head="HEAD"
+	fi
+
+	if [ -n "$files" ]
+	then
+		test -n "$cached" &&
+		die "$(gettext -- "--cached cannot be used with --files")"
+		diff_cmd=diff-files
+		head=
 	fi
 
 	cd_to_toplevel
 	# Get modified modules cared by user
-	modules=$(git diff-index $cached --raw $head -- "$@" |
-		egrep '^:([0-7]* )?160000' |
+	modules=$(git $diff_cmd $cached --ignore-submodules=dirty --raw $head -- "$@" |
+		sane_egrep '^:([0-7]* )?160000' |
 		while read mod_src mod_dst sha1_src sha1_dst status name
 		do
 			# Always show modules deleted or type-changed (blob<->module)
@@ -520,8 +769,8 @@
 
 	test -z "$modules" && return
 
-	git diff-index $cached --raw $head -- $modules |
-	egrep '^:([0-7]* )?160000' |
+	git $diff_cmd $cached --ignore-submodules=dirty --raw $head -- $modules |
+	sane_egrep '^:([0-7]* )?160000' |
 	cut -c2- |
 	while read mod_src mod_dst sha1_src sha1_dst status name
 	do
@@ -539,7 +788,7 @@
 				;; # removed
 			*)
 				# unexpected type
-				echo >&2 "unexpected mode $mod_dst"
+				eval_gettextln "unexpected mode \$mod_dst" >&2
 				continue ;;
 			esac
 		fi
@@ -557,13 +806,13 @@
 		total_commits=
 		case "$missing_src,$missing_dst" in
 		t,)
-			errmsg="  Warn: $name doesn't contain commit $sha1_src"
+			errmsg="$(eval_gettext "  Warn: \$name doesn't contain commit \$sha1_src")"
 			;;
 		,t)
-			errmsg="  Warn: $name doesn't contain commit $sha1_dst"
+			errmsg="$(eval_gettext "  Warn: \$name doesn't contain commit \$sha1_dst")"
 			;;
 		t,t)
-			errmsg="  Warn: $name doesn't contain commits $sha1_src and $sha1_dst"
+			errmsg="$(eval_gettext "  Warn: \$name doesn't contain commits \$sha1_src and \$sha1_dst")"
 			;;
 		*)
 			errmsg=
@@ -578,7 +827,7 @@
 				range=$sha1_dst
 			fi
 			GIT_DIR="$name/.git" \
-			git log --pretty=oneline --first-parent $range | wc -l
+			git rev-list --first-parent $range -- | wc -l
 			)
 			total_commits=" ($(($total_commits + 0)))"
 			;;
@@ -588,11 +837,13 @@
 		sha1_abbr_dst=$(echo $sha1_dst | cut -c1-7)
 		if test $status = T
 		then
+			blob="$(gettext "blob")"
+			submodule="$(gettext "submodule")"
 			if test $mod_dst = 160000
 			then
-				echo "* $name $sha1_abbr_src(blob)->$sha1_abbr_dst(submodule)$total_commits:"
+				echo "* $name $sha1_abbr_src($blob)->$sha1_abbr_dst($submodule)$total_commits:"
 			else
-				echo "* $name $sha1_abbr_src(submodule)->$sha1_abbr_dst(blob)$total_commits:"
+				echo "* $name $sha1_abbr_src($submodule)->$sha1_abbr_dst($blob)$total_commits:"
 			fi
 		else
 			echo "* $name $sha1_abbr_src...$sha1_abbr_dst$total_commits:"
@@ -623,7 +874,11 @@
 		echo
 	done |
 	if test -n "$for_status"; then
-		echo "# Modified submodules:"
+		if [ -n "$files" ]; then
+			gettextln "# Submodules changed but not updated:"
+		else
+			gettextln "# Submodule changes to be committed:"
+		fi
 		echo "#"
 		sed -e 's|^|# |' -e 's|^# $|#|'
 	else
@@ -643,6 +898,7 @@
 cmd_status()
 {
 	# parse $args after "submodule ... status".
+	orig_flags=
 	while test $# -ne 0
 	do
 		case "$1" in
@@ -652,6 +908,9 @@
 		--cached)
 			cached=1
 			;;
+		--recursive)
+			recursive=1
+			;;
 		--)
 			shift
 			break
@@ -663,30 +922,48 @@
 			break
 			;;
 		esac
+		orig_flags="$orig_flags $(git rev-parse --sq-quote "$1")"
 		shift
 	done
 
 	module_list "$@" |
-	while read mode sha1 stage path
+	while read mode sha1 stage sm_path
 	do
-		name=$(module_name "$path") || exit
+		name=$(module_name "$sm_path") || exit
 		url=$(git config submodule."$name".url)
-		if test -z "$url" || ! test -d "$path"/.git -o -f "$path"/.git
+		displaypath="$prefix$sm_path"
+		if test "$stage" = U
 		then
-			say "-$sha1 $path"
+			say "U$sha1 $displaypath"
+			continue
+		fi
+		if test -z "$url" || ! test -d "$sm_path"/.git -o -f "$sm_path"/.git
+		then
+			say "-$sha1 $displaypath"
 			continue;
 		fi
-		set_name_rev "$path" "$sha1"
-		if git diff-files --quiet -- "$path"
+		set_name_rev "$sm_path" "$sha1"
+		if git diff-files --ignore-submodules=dirty --quiet -- "$sm_path"
 		then
-			say " $sha1 $path$revname"
+			say " $sha1 $displaypath$revname"
 		else
 			if test -z "$cached"
 			then
-				sha1=$(unset GIT_DIR; cd "$path" && git rev-parse --verify HEAD)
-				set_name_rev "$path" "$sha1"
+				sha1=$(clear_local_git_env; cd "$sm_path" && git rev-parse --verify HEAD)
+				set_name_rev "$sm_path" "$sha1"
 			fi
-			say "+$sha1 $path$revname"
+			say "+$sha1 $displaypath$revname"
+		fi
+
+		if test -n "$recursive"
+		then
+			(
+				prefix="$displaypath/"
+				clear_local_git_env
+				cd "$sm_path" &&
+				eval cmd_status "$orig_args"
+			) ||
+			die "$(eval_gettext "Failed to recurse into submodule path '\$sm_path'")"
 		fi
 	done
 }
@@ -718,27 +995,44 @@
 	done
 	cd_to_toplevel
 	module_list "$@" |
-	while read mode sha1 stage path
+	while read mode sha1 stage sm_path
 	do
-		name=$(module_name "$path")
+		name=$(module_name "$sm_path")
 		url=$(git config -f .gitmodules --get submodule."$name".url)
 
 		# Possibly a url relative to parent
 		case "$url" in
 		./*|../*)
-			url=$(resolve_relative_url "$url") || exit
+			# rewrite foo/bar as ../.. to find path from
+			# submodule work tree to superproject work tree
+			up_path="$(echo "$sm_path" | sed "s/[^/][^/]*/../g")" &&
+			# guarantee a trailing /
+			up_path=${up_path%/}/ &&
+			# path from submodule work tree to submodule origin repo
+			sub_origin_url=$(resolve_relative_url "$url" "$up_path") &&
+			# path from superproject work tree to submodule origin repo
+			super_config_url=$(resolve_relative_url "$url") || exit
+			;;
+		*)
+			sub_origin_url="$url"
+			super_config_url="$url"
 			;;
 		esac
 
-		if test -e "$path"/.git
+		if git config "submodule.$name.url" >/dev/null 2>/dev/null
 		then
-		(
-			unset GIT_DIR
-			cd "$path"
-			remote=$(get_default_remote)
-			say "Synchronizing submodule url for '$name'"
-			git config remote."$remote".url "$url"
-		)
+			say "$(eval_gettext "Synchronizing submodule url for '\$name'")"
+			git config submodule."$name".url "$super_config_url"
+
+			if test -e "$sm_path"/.git
+			then
+			(
+				clear_local_git_env
+				cd "$sm_path"
+				remote=$(get_default_remote)
+				git config remote."$remote".url "$sub_origin_url"
+			)
+			fi
 		fi
 	done
 }
diff --git a/git-svn.perl b/git-svn.perl
index d075810..6673d21 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -1,6 +1,7 @@
-#!/usr/bin/env perl
+#!/usr/bin/perl
 # Copyright (C) 2006, Eric Wong <normalperson@yhbt.net>
 # License: GPL v2 or later
+use 5.008;
 use warnings;
 use strict;
 use vars qw/	$AUTHOR $VERSION
@@ -21,21 +22,51 @@
 $Git::SVN::Ra::_log_window_size = 100;
 $Git::SVN::_minimize_url = 'unset';
 
+if (! exists $ENV{SVN_SSH} && exists $ENV{GIT_SSH}) {
+	$ENV{SVN_SSH} = $ENV{GIT_SSH};
+}
+
+if (exists $ENV{SVN_SSH} && $^O eq 'msys') {
+	$ENV{SVN_SSH} =~ s/\\/\\\\/g;
+	$ENV{SVN_SSH} =~ s/(.*)/"$1"/;
+}
+
 $Git::SVN::Log::TZ = $ENV{TZ};
 $ENV{TZ} = 'UTC';
 $| = 1; # unbuffer STDOUT
 
 sub fatal (@) { print STDERR "@_\n"; exit 1 }
-require SVN::Core; # use()-ing this causes segfaults for me... *shrug*
-require SVN::Ra;
-require SVN::Delta;
-if ($SVN::Core::VERSION lt '1.1.0') {
-	fatal "Need SVN::Core 1.1.0 or better (got $SVN::Core::VERSION)";
+
+# All SVN commands do it.  Otherwise we may die on SIGPIPE when the remote
+# repository decides to close the connection which we expect to be kept alive.
+$SIG{PIPE} = 'IGNORE';
+
+# Given a dot separated version number, "subtract" it from
+# the SVN::Core::VERSION; non-negaitive return means the SVN::Core
+# is at least at the version the caller asked for.
+sub compare_svn_version {
+	my (@ours) = split(/\./, $SVN::Core::VERSION);
+	my (@theirs) = split(/\./, $_[0]);
+	my ($i, $diff);
+
+	for ($i = 0; $i < @ours && $i < @theirs; $i++) {
+		$diff = $ours[$i] - $theirs[$i];
+		return $diff if ($diff);
+	}
+	return 1 if ($i < @ours);
+	return -1 if ($i < @theirs);
+	return 0;
+}
+
+sub _req_svn {
+	require SVN::Core; # use()-ing this causes segfaults for me... *shrug*
+	require SVN::Ra;
+	require SVN::Delta;
+	if (::compare_svn_version('1.1.0') < 0) {
+		fatal "Need SVN::Core 1.1.0 or better (got $SVN::Core::VERSION)";
+	}
 }
 my $can_compress = eval { require Compress::Zlib; 1};
-push @Git::SVN::Ra::ISA, 'SVN::Ra';
-push @SVN::Git::Editor::ISA, 'SVN::Delta::Editor';
-push @SVN::Git::Fetcher::ISA, 'SVN::Delta::Editor';
 use Carp qw/croak/;
 use Digest::MD5;
 use IO::File qw//;
@@ -46,6 +77,11 @@
 use Getopt::Long qw/:config gnu_getopt no_ignore_case auto_abbrev/;
 use IPC::Open3;
 use Git;
+use Git::SVN::Editor qw//;
+use Git::SVN::Fetcher qw//;
+use Git::SVN::Ra qw//;
+use Git::SVN::Prompt qw//;
+use Memoize;  # core since 5.8.0, Jul 2002
 
 BEGIN {
 	# import functions from Git into our packages, en masse
@@ -53,12 +89,13 @@
 	foreach (qw/command command_oneline command_noisy command_output_pipe
 	            command_input_pipe command_close_pipe
 	            command_bidi_pipe command_close_bidi_pipe/) {
-		for my $package ( qw(SVN::Git::Editor SVN::Git::Fetcher
-			Git::SVN::Migration Git::SVN::Log Git::SVN),
+		for my $package ( qw(Git::SVN::Migration Git::SVN::Log Git::SVN),
 			__PACKAGE__) {
 			*{"${package}::$_"} = \&{"Git::$_"};
 		}
 	}
+	Memoize::memoize 'Git::config';
+	Memoize::memoize 'Git::config_bool';
 }
 
 my ($SVN);
@@ -69,15 +106,17 @@
 	$_message, $_file, $_branch_dest,
 	$_template, $_shared,
 	$_version, $_fetch_all, $_no_rebase, $_fetch_parent,
-	$_merge, $_strategy, $_dry_run, $_local,
+	$_merge, $_strategy, $_preserve_merges, $_dry_run, $_local,
 	$_prefix, $_no_checkout, $_url, $_verbose,
-	$_git_format, $_commit_url, $_tag);
+	$_git_format, $_commit_url, $_tag, $_merge_info, $_interactive);
 $Git::SVN::_follow_parent = 1;
+$Git::SVN::Fetcher::_placeholder_filename = ".gitignore";
 $_q ||= 0;
 my %remote_opts = ( 'username=s' => \$Git::SVN::Prompt::_username,
                     'config-dir=s' => \$Git::SVN::Ra::config_dir,
                     'no-auth-cache' => \$Git::SVN::Prompt::_no_auth_cache,
-                    'ignore-paths=s' => \$SVN::Git::Fetcher::_ignore_regex );
+                    'ignore-paths=s' => \$Git::SVN::Fetcher::_ignore_regex,
+                    'ignore-refs=s' => \$Git::SVN::Ra::_ignore_refs_regex );
 my %fc_opts = ( 'follow-parent|follow!' => \$Git::SVN::_follow_parent,
 		'authors-file|A=s' => \$_authors,
 		'authors-prog=s' => \$_authors_prog,
@@ -106,12 +145,13 @@
 		  'use-svm-props' => sub { $icv{useSvmProps} = 1 },
 		  'use-svnsync-props' => sub { $icv{useSvnsyncProps} = 1 },
 		  'rewrite-root=s' => sub { $icv{rewriteRoot} = $_[1] },
+		  'rewrite-uuid=s' => sub { $icv{rewriteUUID} = $_[1] },
                   %remote_opts );
 my %cmt_opts = ( 'edit|e' => \$_edit,
-		'rmdir' => \$SVN::Git::Editor::_rmdir,
-		'find-copies-harder' => \$SVN::Git::Editor::_find_copies_harder,
-		'l=i' => \$SVN::Git::Editor::_rename_limit,
-		'copy-similarity|C=i'=> \$SVN::Git::Editor::_cp_similarity
+		'rmdir' => \$Git::SVN::Editor::_rmdir,
+		'find-copies-harder' => \$Git::SVN::Editor::_find_copies_harder,
+		'l=i' => \$Git::SVN::Editor::_rename_limit,
+		'copy-similarity|C=i'=> \$Git::SVN::Editor::_cp_similarity
 );
 
 my %cmd = (
@@ -122,6 +162,10 @@
 			   %fc_opts } ],
 	clone => [ \&cmd_clone, "Initialize and fetch revisions",
 			{ 'revision|r=s' => \$_revision,
+			  'preserve-empty-dirs' =>
+				\$Git::SVN::Fetcher::_preserve_empty_dirs,
+			  'placeholder-filename=s' =>
+				\$Git::SVN::Fetcher::_placeholder_filename,
 			   %fc_opts, %init_opts } ],
 	init => [ \&cmd_init, "Initialize a repo for tracking" .
 			  " (requires URL argument)",
@@ -140,18 +184,24 @@
 			  'commit-url=s' => \$_commit_url,
 			  'revision|r=i' => \$_revision,
 			  'no-rebase' => \$_no_rebase,
+			  'mergeinfo=s' => \$_merge_info,
+			  'interactive|i' => \$_interactive,
 			%cmt_opts, %fc_opts } ],
 	branch => [ \&cmd_branch,
 	            'Create a branch in the SVN repository',
 	            { 'message|m=s' => \$_message,
 	              'destination|d=s' => \$_branch_dest,
 	              'dry-run|n' => \$_dry_run,
-		      'tag|t' => \$_tag } ],
+	              'tag|t' => \$_tag,
+	              'username=s' => \$Git::SVN::Prompt::_username,
+	              'commit-url=s' => \$_commit_url } ],
 	tag => [ sub { $_tag = 1; cmd_branch(@_) },
 	         'Create a tag in the SVN repository',
 	         { 'message|m=s' => \$_message,
 	           'destination|d=s' => \$_branch_dest,
-	           'dry-run|n' => \$_dry_run } ],
+	           'dry-run|n' => \$_dry_run,
+	           'username=s' => \$Git::SVN::Prompt::_username,
+	           'commit-url=s' => \$_commit_url } ],
 	'set-tree' => [ \&cmd_set_tree,
 	                "Set an SVN repository to a git tree-ish",
 			{ 'stdin' => \$_stdin, %cmt_opts, %fc_opts, } ],
@@ -159,6 +209,9 @@
 			     'Create a .gitignore per svn:ignore',
 			     { 'revision|r=i' => \$_revision
 			     } ],
+	'mkdirs' => [ \&cmd_mkdirs ,
+	              "recreate empty directories after a checkout",
+	              { 'revision|r=i' => \$_revision } ],
         'propget' => [ \&cmd_propget,
 		       'Print the value of a property on a file or directory',
 		       { 'revision|r=i' => \$_revision } ],
@@ -202,6 +255,7 @@
 			  'local|l' => \$_local,
 			  'fetch-all|all' => \$_fetch_all,
 			  'dry-run|n' => \$_dry_run,
+			  'preserve-merges|p' => \$_preserve_merges,
 			  %fc_opts } ],
 	'commit-diff' => [ \&cmd_commit_diff,
 	                   'Commit a diff between two trees',
@@ -226,6 +280,27 @@
 		{} ],
 );
 
+use Term::ReadLine;
+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;
+
+my $term = eval {
+	$ENV{"GIT_SVN_NOTTY"}
+		? new Term::ReadLine 'git-svn', \*STDIN, \*STDOUT
+		: new Term::ReadLine 'git-svn';
+};
+if ($@) {
+	$term = new FakeTerm "$@: going non-interactive";
+}
+
 my $cmd;
 for (my $i = 0; $i < @ARGV; $i++) {
 	if (defined $cmd{$ARGV[$i]}) {
@@ -265,11 +340,11 @@
 
 my %opts = %{$cmd{$cmd}->[2]} if (defined $cmd);
 
-read_repo_config(\%opts);
+read_git_config(\%opts);
 if ($cmd && ($cmd eq 'log' || $cmd eq 'blame')) {
 	Getopt::Long::Configure('pass_through');
 }
-my $rv = GetOptions(%opts, 'help|H|h' => \$_help, 'version|V' => \$_version,
+my $rv = GetOptions(%opts, 'h|H' => \$_help, 'version|V' => \$_version,
                     'minimize-connections' => \$Git::SVN::Migration::_minimize,
                     'id|i=s' => \$Git::SVN::default_ref_id,
                     'svn-remote|remote|R=s' => sub {
@@ -292,9 +367,9 @@
 eval {
 	Git::SVN::verify_remotes_sanity();
 	$cmd{$cmd}->[0]->(@ARGV);
+	post_fetch_checkout();
 };
 fatal $@ if $@;
-post_fetch_checkout();
 exit 0;
 
 ####################### primary functions ######################
@@ -331,10 +406,41 @@
 }
 
 sub version {
+	::_req_svn();
 	print "git-svn version $VERSION (svn $SVN::Core::VERSION)\n";
 	exit 0;
 }
 
+sub ask {
+	my ($prompt, %arg) = @_;
+	my $valid_re = $arg{valid_re};
+	my $default = $arg{default};
+	my $resp;
+	my $i = 0;
+
+	if ( !( defined($term->IN)
+            && defined( fileno($term->IN) )
+            && defined( $term->OUT )
+            && defined( fileno($term->OUT) ) ) ){
+		return defined($default) ? $default : undef;
+	}
+
+	while ($i++ < 10) {
+		$resp = $term->readline($prompt);
+		if (!defined $resp) { # EOF
+			print "\n";
+			return defined $default ? $default : undef;
+		}
+		if ($resp eq '' and defined $default) {
+			return $default;
+		}
+		if (!defined $valid_re or $resp =~ /$valid_re/) {
+			return $resp;
+		}
+	}
+	return undef;
+}
+
 sub do_git_init_db {
 	unless (-d $ENV{GIT_DIR}) {
 		my @init_db = ('init');
@@ -349,7 +455,6 @@
 		command_noisy(@init_db);
 		$_repository = Git->repository(Repository => ".git");
 	}
-	command_noisy('config', 'core.autocrlf', 'false');
 	my $set;
 	my $pfx = "svn-remote.$Git::SVN::default_repo_id";
 	foreach my $i (keys %icv) {
@@ -358,9 +463,18 @@
 		command_noisy('config', "$pfx.$i", $icv{$i});
 		$set = $i;
 	}
-	my $ignore_regex = \$SVN::Git::Fetcher::_ignore_regex;
-	command_noisy('config', "$pfx.ignore-paths", $$ignore_regex)
-		if defined $$ignore_regex;
+	my $ignore_paths_regex = \$Git::SVN::Fetcher::_ignore_regex;
+	command_noisy('config', "$pfx.ignore-paths", $$ignore_paths_regex)
+		if defined $$ignore_paths_regex;
+	my $ignore_refs_regex = \$Git::SVN::Ra::_ignore_refs_regex;
+	command_noisy('config', "$pfx.ignore-refs", $$ignore_refs_regex)
+		if defined $$ignore_refs_regex;
+
+	if (defined $Git::SVN::Fetcher::_preserve_empty_dirs) {
+		my $fname = \$Git::SVN::Fetcher::_placeholder_filename;
+		command_noisy('config', "$pfx.preserve-empty-dirs", 'true');
+		command_noisy('config', "$pfx.placeholder-filename", $$fname);
+	}
 }
 
 sub init_subdir {
@@ -380,9 +494,11 @@
 		$path = $url;
 	}
 	$path = basename($url) if !defined $path || !length $path;
+	my $authors_absolute = $_authors ? File::Spec->rel2abs($_authors) : "";
 	cmd_init($url, $path);
+	command_oneline('config', 'svn.authorsfile', $authors_absolute)
+	    if $_authors;
 	Git::SVN::fetch_all($Git::SVN::default_repo_id);
-	command_oneline('config', 'svn.authorsfile', $_authors) if $_authors;
 }
 
 sub cmd_init {
@@ -416,6 +532,7 @@
 	if (@_ > 1) {
 		die "Usage: $0 fetch [--all] [--parent] [svn-remote]\n";
 	}
+	$Git::SVN::no_reuse_existing = undef;
 	if ($_fetch_parent) {
 		my ($url, $rev, $uuid, $gs) = working_head_info('HEAD');
 		unless ($gs) {
@@ -469,8 +586,198 @@
 	unlink $gs->{index};
 }
 
+sub split_merge_info_range {
+	my ($range) = @_;
+	if ($range =~ /(\d+)-(\d+)/) {
+		return (int($1), int($2));
+	} else {
+		return (int($range), int($range));
+	}
+}
+
+sub combine_ranges {
+	my ($in) = @_;
+
+	my @fnums = ();
+	my @arr = split(/,/, $in);
+	for my $element (@arr) {
+		my ($start, $end) = split_merge_info_range($element);
+		push @fnums, $start;
+	}
+
+	my @sorted = @arr [ sort {
+		$fnums[$a] <=> $fnums[$b]
+	} 0..$#arr ];
+
+	my @return = ();
+	my $last = -1;
+	my $first = -1;
+	for my $element (@sorted) {
+		my ($start, $end) = split_merge_info_range($element);
+
+		if ($last == -1) {
+			$first = $start;
+			$last = $end;
+			next;
+		}
+		if ($start <= $last+1) {
+			if ($end > $last) {
+				$last = $end;
+			}
+			next;
+		}
+		if ($first == $last) {
+			push @return, "$first";
+		} else {
+			push @return, "$first-$last";
+		}
+		$first = $start;
+		$last = $end;
+	}
+
+	if ($first != -1) {
+		if ($first == $last) {
+			push @return, "$first";
+		} else {
+			push @return, "$first-$last";
+		}
+	}
+
+	return join(',', @return);
+}
+
+sub merge_revs_into_hash {
+	my ($hash, $minfo) = @_;
+	my @lines = split(' ', $minfo);
+
+	for my $line (@lines) {
+		my ($branchpath, $revs) = split(/:/, $line);
+
+		if (exists($hash->{$branchpath})) {
+			# Merge the two revision sets
+			my $combined = "$hash->{$branchpath},$revs";
+			$hash->{$branchpath} = combine_ranges($combined);
+		} else {
+			# Just do range combining for consolidation
+			$hash->{$branchpath} = combine_ranges($revs);
+		}
+	}
+}
+
+sub merge_merge_info {
+	my ($mergeinfo_one, $mergeinfo_two) = @_;
+	my %result_hash = ();
+
+	merge_revs_into_hash(\%result_hash, $mergeinfo_one);
+	merge_revs_into_hash(\%result_hash, $mergeinfo_two);
+
+	my $result = '';
+	# Sort below is for consistency's sake
+	for my $branchname (sort keys(%result_hash)) {
+		my $revlist = $result_hash{$branchname};
+		$result .= "$branchname:$revlist\n"
+	}
+	return $result;
+}
+
+sub populate_merge_info {
+	my ($d, $gs, $uuid, $linear_refs, $rewritten_parent) = @_;
+
+	my %parentshash;
+	read_commit_parents(\%parentshash, $d);
+	my @parents = @{$parentshash{$d}};
+	if ($#parents > 0) {
+		# Merge commit
+		my $all_parents_ok = 1;
+		my $aggregate_mergeinfo = '';
+		my $rooturl = $gs->repos_root;
+
+		if (defined($rewritten_parent)) {
+			# Replace first parent with newly-rewritten version
+			shift @parents;
+			unshift @parents, $rewritten_parent;
+		}
+
+		foreach my $parent (@parents) {
+			my ($branchurl, $svnrev, $paruuid) =
+				cmt_metadata($parent);
+
+			unless (defined($svnrev)) {
+				# Should have been caught be preflight check
+				fatal "merge commit $d has ancestor $parent, but that change "
+                     ."does not have git-svn metadata!";
+			}
+			unless ($branchurl =~ /^\Q$rooturl\E(.*)/) {
+				fatal "commit $parent git-svn metadata changed mid-run!";
+			}
+			my $branchpath = $1;
+
+			my $ra = Git::SVN::Ra->new($branchurl);
+			my (undef, undef, $props) =
+				$ra->get_dir(canonicalize_path("."), $svnrev);
+			my $par_mergeinfo = $props->{'svn:mergeinfo'};
+			unless (defined $par_mergeinfo) {
+				$par_mergeinfo = '';
+			}
+			# Merge previous mergeinfo values
+			$aggregate_mergeinfo =
+				merge_merge_info($aggregate_mergeinfo,
+								 $par_mergeinfo, 0);
+
+			next if $parent eq $parents[0]; # Skip first parent
+			# Add new changes being placed in tree by merge
+			my @cmd = (qw/rev-list --reverse/,
+					   $parent, qw/--not/);
+			foreach my $par (@parents) {
+				unless ($par eq $parent) {
+					push @cmd, $par;
+				}
+			}
+			my @revsin = ();
+			my ($revlist, $ctx) = command_output_pipe(@cmd);
+			while (<$revlist>) {
+				my $irev = $_;
+				chomp $irev;
+				my (undef, $csvnrev, undef) =
+					cmt_metadata($irev);
+				unless (defined $csvnrev) {
+					# A child is missing SVN annotations...
+					# this might be OK, or might not be.
+					warn "W:child $irev is merged into revision "
+						 ."$d but does not have git-svn metadata. "
+						 ."This means git-svn cannot determine the "
+						 ."svn revision numbers to place into the "
+						 ."svn:mergeinfo property. You must ensure "
+						 ."a branch is entirely committed to "
+						 ."SVN before merging it in order for "
+						 ."svn:mergeinfo population to function "
+						 ."properly";
+				}
+				push @revsin, $csvnrev;
+			}
+			command_close_pipe($revlist, $ctx);
+
+			last unless $all_parents_ok;
+
+			# We now have a list of all SVN revnos which are
+			# merged by this particular parent. Integrate them.
+			next if $#revsin == -1;
+			my $newmergeinfo = "$branchpath:" . join(',', @revsin);
+			$aggregate_mergeinfo =
+				merge_merge_info($aggregate_mergeinfo,
+								 $newmergeinfo, 1);
+		}
+		if ($all_parents_ok and $aggregate_mergeinfo) {
+			return $aggregate_mergeinfo;
+		}
+	}
+
+	return undef;
+}
+
 sub cmd_dcommit {
 	my $head = shift;
+	command_noisy(qw/update-index --refresh/);
 	git_cmd_try { command_oneline(qw/diff-index --quiet HEAD/) }
 		'Cannot dcommit with a dirty index.  Commit your changes first, '
 		. "or stash them with `git stash'.\n";
@@ -502,7 +809,7 @@
 		$url = eval { command_oneline('config', '--get',
 			      "svn-remote.$gs->{repo_id}.commiturl") };
 		if (!$url) {
-			$url = $gs->full_url
+			$url = $gs->full_pushurl
 		}
 	}
 
@@ -517,8 +824,88 @@
 		     "If these changes depend on each other, re-running ",
 		     "without --no-rebase may be required."
 	}
+
+	if (defined $_interactive){
+		my $ask_default = "y";
+		foreach my $d (@$linear_refs){
+			my ($fh, $ctx) = command_output_pipe(qw(show --summary), "$d");
+			while (<$fh>){
+				print $_;
+			}
+			command_close_pipe($fh, $ctx);
+			$_ = ask("Commit this patch to SVN? ([y]es (default)|[n]o|[q]uit|[a]ll): ",
+			         valid_re => qr/^(?:yes|y|no|n|quit|q|all|a)/i,
+			         default => $ask_default);
+			die "Commit this patch reply required" unless defined $_;
+			if (/^[nq]/i) {
+				exit(0);
+			} elsif (/^a/i) {
+				last;
+			}
+		}
+	}
+
 	my $expect_url = $url;
+
+	my $push_merge_info = eval {
+		command_oneline(qw/config --get svn.pushmergeinfo/)
+		};
+	if (not defined($push_merge_info)
+			or $push_merge_info eq "false"
+			or $push_merge_info eq "no"
+			or $push_merge_info eq "never") {
+		$push_merge_info = 0;
+	}
+
+	unless (defined($_merge_info) || ! $push_merge_info) {
+		# Preflight check of changes to ensure no issues with mergeinfo
+		# This includes check for uncommitted-to-SVN parents
+		# (other than the first parent, which we will handle),
+		# information from different SVN repos, and paths
+		# which are not underneath this repository root.
+		my $rooturl = $gs->repos_root;
+		foreach my $d (@$linear_refs) {
+			my %parentshash;
+			read_commit_parents(\%parentshash, $d);
+			my @realparents = @{$parentshash{$d}};
+			if ($#realparents > 0) {
+				# Merge commit
+				shift @realparents; # Remove/ignore first parent
+				foreach my $parent (@realparents) {
+					my ($branchurl, $svnrev, $paruuid) = cmt_metadata($parent);
+					unless (defined $paruuid) {
+						# A parent is missing SVN annotations...
+						# abort the whole operation.
+						fatal "$parent is merged into revision $d, "
+							 ."but does not have git-svn metadata. "
+							 ."Either dcommit the branch or use a "
+							 ."local cherry-pick, FF merge, or rebase "
+							 ."instead of an explicit merge commit.";
+					}
+
+					unless ($paruuid eq $uuid) {
+						# Parent has SVN metadata from different repository
+						fatal "merge parent $parent for change $d has "
+							 ."git-svn uuid $paruuid, while current change "
+							 ."has uuid $uuid!";
+					}
+
+					unless ($branchurl =~ /^\Q$rooturl\E(.*)/) {
+						# This branch is very strange indeed.
+						fatal "merge parent $parent for $d is on branch "
+							 ."$branchurl, which is not under the "
+							 ."git-svn root $rooturl!";
+					}
+				}
+			}
+		}
+	}
+
+	my $rewritten_parent;
 	Git::SVN::remove_username($expect_url);
+	if (defined($_merge_info)) {
+		$_merge_info =~ tr{ }{\n};
+	}
 	while (1) {
 		my $d = shift @$linear_refs or last;
 		unless (defined $last_rev) {
@@ -532,6 +919,14 @@
 			print "diff-tree $d~1 $d\n";
 		} else {
 			my $cmt_rev;
+
+			unless (defined($_merge_info) || ! $push_merge_info) {
+				$_merge_info = populate_merge_info($d, $gs,
+				                             $uuid,
+				                             $linear_refs,
+				                             $rewritten_parent);
+			}
+
 			my %ed_opts = ( r => $last_rev,
 			                log => get_commit_entry($d)->{log},
 			                ra => Git::SVN::Ra->new($url),
@@ -544,8 +939,9 @@
 			                       print "Committed r$_[0]\n";
 			                       $cmt_rev = $_[0];
 			                },
+					mergeinfo => $_merge_info,
 			                svn_path => '');
-			if (!SVN::Git::Editor->new(\%ed_opts)->apply_diff) {
+			if (!Git::SVN::Editor->new(\%ed_opts)->apply_diff) {
 				print "No changes\n$d~1 == $d\n";
 			} elsif ($parents->{$d} && @{$parents->{$d}}) {
 				$gs->{inject_parents_dcommit}->{$cmt_rev} =
@@ -573,6 +969,9 @@
 				@finish = qw/reset --mixed/;
 			}
 			command_noisy(@finish, $gs->refname);
+
+			$rewritten_parent = command_oneline(qw/rev-parse HEAD/);
+
 			if (@diff) {
 				@refs = ();
 				my ($url_, $rev_, $uuid_, $gs_) =
@@ -594,8 +993,15 @@
 					  "\nBefore dcommitting";
 				}
 				if ($url_ ne $expect_url) {
-					fatal "URL mismatch after rebase: ",
-					      "$url_ != $expect_url";
+					if ($url_ eq $gs->metadata_url) {
+						print
+						  "Accepting rewritten URL:",
+						  " $url_\n";
+					} else {
+						fatal
+						  "URL mismatch after rebase:",
+						  " $url_ != $expect_url";
+					}
 				}
 				if ($uuid_ ne $uuid) {
 					fatal "uuid mismatch after rebase: ",
@@ -641,7 +1047,8 @@
 	}
 	$head ||= 'HEAD';
 
-	my ($src, $rev, undef, $gs) = working_head_info($head);
+	my (undef, $rev, undef, $gs) = working_head_info($head);
+	my $src = $gs->full_pushurl;
 
 	my $remote = Git::SVN::read_all_remotes()->{$gs->{repo_id}};
 	my $allglobs = $remote->{ $_tag ? 'tags' : 'branches' };
@@ -658,8 +1065,7 @@
 		            " with the --destination argument.\n";
 		}
 		foreach my $g (@{$allglobs}) {
-			# SVN::Git::Editor could probably be moved to Git.pm..
-			my $re = SVN::Git::Editor::glob2pat($g->{path}->{left});
+			my $re = Git::SVN::Editor::glob2pat($g->{path}->{left});
 			if ($_branch_dest =~ /$re/) {
 				$glob = $g;
 				last;
@@ -685,7 +1091,23 @@
 		}
 	}
 	my ($lft, $rgt) = @{ $glob->{path} }{qw/left right/};
-	my $dst = join '/', $remote->{url}, $lft, $branch_name, ($rgt || ());
+	my $url;
+	if (defined $_commit_url) {
+		$url = $_commit_url;
+	} else {
+		$url = eval { command_oneline('config', '--get',
+			"svn-remote.$gs->{repo_id}.commiturl") };
+		if (!$url) {
+			$url = $remote->{pushurl} || $remote->{url};
+		}
+	}
+	my $dst = join '/', $url, $lft, $branch_name, ($rgt || ());
+
+	if ($dst =~ /^https:/ && $src =~ /^http:/) {
+		$src=~s/^http:/https:/;
+	}
+
+	::_req_svn();
 
 	my $ctx = SVN::Client->new(
 		auth    => Git::SVN::Ra::_auth_providers(),
@@ -730,6 +1152,15 @@
 	print "$result\n" if $result;
 }
 
+sub auto_create_empty_directories {
+	my ($gs) = @_;
+	my $var = eval { command_oneline('config', '--get', '--bool',
+					 "svn-remote.$gs->{repo_id}.automkdirs") };
+	# By default, create empty directories by consulting the unhandled log,
+	# but allow setting it to 'false' to skip it.
+	return !($var && $var eq 'false');
+}
+
 sub cmd_rebase {
 	command_noisy(qw/update-index --refresh/);
 	my ($url, $rev, $uuid, $gs) = working_head_info('HEAD');
@@ -753,6 +1184,9 @@
 		$_fetch_all ? $gs->fetch_all : $gs->fetch;
 	}
 	command_noisy(rebase_cmd(), $gs->refname);
+	if (auto_create_empty_directories($gs)) {
+		$gs->mkemptydirs;
+	}
 }
 
 sub cmd_show_ignore {
@@ -764,6 +1198,7 @@
 		print STDOUT "\n# $path\n";
 		my $s = $props->{'svn:ignore'} or return;
 		$s =~ s/[\r\n]+/\n/g;
+		$s =~ s/^\n+//;
 		chomp $s;
 		$s =~ s#^#$path#gm;
 		print STDOUT "$s\n";
@@ -801,6 +1236,7 @@
 		open(GITIGNORE, '>', $ignore)
 		  or fatal("Failed to open `$ignore' for writing: $!");
 		$s =~ s/[\r\n]+/\n/g;
+		$s =~ s/^\n+//;
 		chomp $s;
 		# Prefix all patterns so that the ignore doesn't apply
 		# to sub-directories.
@@ -812,6 +1248,12 @@
 	});
 }
 
+sub cmd_mkdirs {
+	my ($url, $rev, $uuid, $gs) = working_head_info('HEAD');
+	$gs ||= Git::SVN->new;
+	$gs->mkemptydirs($_revision);
+}
+
 sub canonicalize_path {
 	my ($path) = @_;
 	my $dot_slash_added = 0;
@@ -907,7 +1349,8 @@
 	}
 	do_git_init_db();
 	if (defined $_trunk) {
-		my $trunk_ref = $_prefix . 'trunk';
+		$_trunk =~ s#^/+##;
+		my $trunk_ref = 'refs/remotes/' . $_prefix . 'trunk';
 		# try both old-style and new-style lookups:
 		my $gs_trunk = eval { Git::SVN->new($trunk_ref) };
 		unless ($gs_trunk) {
@@ -928,6 +1371,7 @@
 }
 
 sub cmd_multi_fetch {
+	$Git::SVN::no_reuse_existing = undef;
 	my $remotes = Git::SVN::read_all_remotes();
 	foreach my $repo_id (sort keys %$remotes) {
 		if ($remotes->{$repo_id}->{url}) {
@@ -979,7 +1423,7 @@
 	                tree_b => $tb,
 	                editor_cb => sub { print "Committed r$_[0]\n" },
 	                svn_path => $svn_path );
-	if (!SVN::Git::Editor->new(\%ed_opts)->apply_diff) {
+	if (!Git::SVN::Editor->new(\%ed_opts)->apply_diff) {
 		print "No changes\n$ta == $tb\n";
 	}
 }
@@ -1045,8 +1489,9 @@
 	if ($@) {
 		$result .= "Repository Root: (offline)\n";
 	}
+	::_req_svn();
 	$result .= "Repository UUID: $uuid\n" unless $diff_status eq "A" &&
-		($SVN::Core::VERSION le '1.5.4' || $file_type ne "dir");
+		(::compare_svn_version('1.5.4') <= 0 || $file_type ne "dir");
 	$result .= "Revision: " . ($diff_status eq "A" ? 0 : $rev) . "\n";
 
 	$result .= "Node Kind: " .
@@ -1127,6 +1572,7 @@
 		    "history\n";
 	}
 	my ($r, $c) = $gs->find_rev_before($target, not $_fetch_parent);
+	die "Cannot find SVN revision $target\n" unless defined($c);
 	$gs->rev_map_set($r, $c, 'reset', $uuid);
 	print "r$r = $c ($gs->{ref_id})\n";
 }
@@ -1146,17 +1592,28 @@
 	push @cmd, '-v' if $_verbose;
 	push @cmd, qw/--merge/ if $_merge;
 	push @cmd, "--strategy=$_strategy" if $_strategy;
+	push @cmd, "--preserve-merges" if $_preserve_merges;
 	@cmd;
 }
 
 sub post_fetch_checkout {
 	return if $_no_checkout;
+	return if verify_ref('HEAD^0');
 	my $gs = $Git::SVN::_head or return;
-	return if verify_ref('refs/heads/master^0');
 
-	my $valid_head = verify_ref('HEAD^0');
-	command_noisy(qw(update-ref refs/heads/master), $gs->refname);
-	return if ($valid_head || !verify_ref('HEAD^0'));
+	# look for "trunk" ref if it exists
+	my $remote = Git::SVN::read_all_remotes()->{$gs->{repo_id}};
+	my $fetch = $remote->{fetch};
+	if ($fetch) {
+		foreach my $p (keys %$fetch) {
+			basename($fetch->{$p}) eq 'trunk' or next;
+			$gs = Git::SVN->new($fetch->{$p}, $gs->{repo_id}, $p);
+			last;
+		}
+	}
+
+	command_noisy(qw(update-ref HEAD), $gs->refname);
+	return unless verify_ref('HEAD^0');
 
 	return if $ENV{GIT_DIR} !~ m#^(?:.*/)?\.git$#;
 	my $index = $ENV{GIT_INDEX_FILE} || "$ENV{GIT_DIR}/index";
@@ -1167,6 +1624,9 @@
 	command_noisy(qw/read-tree -m -u -v HEAD HEAD/);
 	print STDERR "Checked out HEAD:\n  ",
 	             $gs->full_url, " r", $gs->last_rev, "\n";
+	if (auto_create_empty_directories($gs)) {
+		$gs->mkemptydirs($gs->last_rev);
+	}
 }
 
 sub complete_svn_url {
@@ -1209,6 +1669,7 @@
 	}
 	command_oneline('config', $k, $gs->{url}) unless $orig_url;
 	my $remote_path = "$gs->{path}/$repo_path";
+	$remote_path =~ s{%([0-9A-F]{2})}{chr hex($1)}ieg;
 	$remote_path =~ s#/+#/#g;
 	$remote_path =~ s#^/##g;
 	$remote_path .= "/*" if $remote_path !~ /\*/;
@@ -1291,9 +1752,8 @@
 	close $log_fh or croak $!;
 
 	if ($_edit || ($type eq 'tree')) {
-		my $editor = $ENV{VISUAL} || $ENV{EDITOR} || 'vi';
-		# TODO: strip out spaces, comments, like git-commit.sh
-		system($editor, $commit_editmsg);
+		chomp(my $editor = command_oneline(qw(var GIT_EDITOR)));
+		system('sh', '-c', $editor.' "$@"', $editor, $commit_editmsg);
 	}
 	rename $commit_editmsg, $commit_msg or croak $!;
 	{
@@ -1360,8 +1820,7 @@
 }
 
 # convert GetOpt::Long specs for use by git-config
-sub read_repo_config {
-	return unless -d $ENV{GIT_DIR};
+sub read_git_config {
 	my $opts = shift;
 	my @config_only;
 	foreach my $o (keys %$opts) {
@@ -1441,7 +1900,7 @@
 
 sub working_head_info {
 	my ($head, $refs) = @_;
-	my @args = ('log', '--no-color', '--first-parent', '--pretty=medium');
+	my @args = qw/rev-list --first-parent --pretty=medium/;
 	my ($fh, $ctx) = command_output_pipe(@args, $head);
 	my $hash;
 	my %max;
@@ -1591,6 +2050,14 @@
 use File::Path qw/mkpath/;
 use File::Copy qw/copy/;
 use IPC::Open3;
+use Time::Local;
+use Memoize;  # core since 5.8.0, Jul 2002
+use Memoize::Storable;
+use POSIX qw(:signal_h);
+my $can_use_yaml;
+BEGIN {
+	$can_use_yaml = eval { require Git::SVN::Memoize::YAML; 1};
+}
 
 my ($_gc_nr, $_gc_period);
 
@@ -1641,23 +2108,23 @@
 	return unless defined $glob_spec;
 	my $ref = $glob_spec->{ref};
 	my $path = $glob_spec->{path};
-	foreach (command(qw#for-each-ref --format=%(refname) refs/remotes#)) {
-		next unless m#^refs/remotes/$ref->{regex}$#;
+	foreach (command(qw#for-each-ref --format=%(refname) refs/#)) {
+		next unless m#^$ref->{regex}$#;
 		my $p = $1;
 		my $pathname = desanitize_refname($path->full_path($p));
 		my $refname = desanitize_refname($ref->full_path($p));
 		if (my $existing = $fetch->{$pathname}) {
 			if ($existing ne $refname) {
 				die "Refspec conflict:\n",
-				    "existing: refs/remotes/$existing\n",
-				    " globbed: refs/remotes/$refname\n";
+				    "existing: $existing\n",
+				    " globbed: $refname\n";
 			}
-			my $u = (::cmt_metadata("refs/remotes/$refname"))[0];
+			my $u = (::cmt_metadata("$refname"))[0];
 			$u =~ s!^\Q$url\E(/|$)!! or die
-			  "refs/remotes/$refname: '$url' not found in '$u'\n";
+			  "$refname: '$url' not found in '$u'\n";
 			if ($pathname ne $u) {
 				warn "W: Refspec glob conflict ",
-				     "(ref: refs/remotes/$refname):\n",
+				     "(ref: $refname):\n",
 				     "expected path: $pathname\n",
 				     "    real path: $u\n",
 				     "Continuing ahead with $u\n";
@@ -1698,7 +2165,11 @@
 	my $ra = Git::SVN::Ra->new($url);
 	my $uuid = $ra->get_uuid;
 	my $head = $ra->get_latest_revnum;
-	$ra->get_log("", $head, 0, 1, 0, 1, sub { $head = $_[1] });
+
+	# ignore errors, $head revision may not even exist anymore
+	eval { $ra->get_log("", $head, 0, 1, 0, 1, sub { $head = $_[1] }) };
+	warn "W: $@\n" if $@;
+
 	my $base = defined $fetch ? $head : 0;
 
 	# read the max revs for wildcard expansion (branches/*, tags/*)
@@ -1735,33 +2206,41 @@
 	my $use_svm_props = eval { command_oneline(qw/config --bool
 	    svn.useSvmProps/) };
 	$use_svm_props = $use_svm_props eq 'true' if $use_svm_props;
+	my $svn_refspec = qr{\s*(.*?)\s*:\s*(.+?)\s*};
 	foreach (grep { s/^svn-remote\.// } command(qw/config -l/)) {
-		if (m!^(.+)\.fetch=\s*(.*)\s*:\s*(.+)\s*$!) {
-			my ($remote, $local_ref, $_remote_ref) = ($1, $2, $3);
-			die("svn-remote.$remote: remote ref '$_remote_ref' "
-			    . "must start with 'refs/remotes/'\n")
-				unless $_remote_ref =~ m{^refs/remotes/(.+)};
-			my $remote_ref = $1;
-			$local_ref =~ s{^/}{};
+		if (m!^(.+)\.fetch=$svn_refspec$!) {
+			my ($remote, $local_ref, $remote_ref) = ($1, $2, $3);
+			die("svn-remote.$remote: remote ref '$remote_ref' "
+			    . "must start with 'refs/'\n")
+				unless $remote_ref =~ m{^refs/};
+			$local_ref = uri_decode($local_ref);
 			$r->{$remote}->{fetch}->{$local_ref} = $remote_ref;
 			$r->{$remote}->{svm} = {} if $use_svm_props;
 		} elsif (m!^(.+)\.usesvmprops=\s*(.*)\s*$!) {
 			$r->{$1}->{svm} = {};
 		} elsif (m!^(.+)\.url=\s*(.*)\s*$!) {
 			$r->{$1}->{url} = $2;
-		} elsif (m!^(.+)\.(branches|tags)=
-		           (.*):refs/remotes/(.+)\s*$/!x) {
-			my ($p, $g) = ($3, $4);
+		} elsif (m!^(.+)\.pushurl=\s*(.*)\s*$!) {
+			$r->{$1}->{pushurl} = $2;
+		} elsif (m!^(.+)\.ignore-refs=\s*(.*)\s*$!) {
+			$r->{$1}->{ignore_refs_regex} = $2;
+		} elsif (m!^(.+)\.(branches|tags)=$svn_refspec$!) {
+			my ($remote, $t, $local_ref, $remote_ref) =
+			                                     ($1, $2, $3, $4);
+			die("svn-remote.$remote: remote ref '$remote_ref' ($t) "
+			    . "must start with 'refs/'\n")
+				unless $remote_ref =~ m{^refs/};
+			$local_ref = uri_decode($local_ref);
 			my $rs = {
-			    t => $2,
-			    remote => $1,
-			    path => Git::SVN::GlobSpec->new($p),
-			    ref => Git::SVN::GlobSpec->new($g) };
+			    t => $t,
+			    remote => $remote,
+			    path => Git::SVN::GlobSpec->new($local_ref, 1),
+			    ref => Git::SVN::GlobSpec->new($remote_ref, 0) };
 			if (length($rs->{ref}->{right}) != 0) {
 				die "The '*' glob character must be the last ",
-				    "character of '$g'\n";
+				    "character of '$remote_ref'\n";
 			}
-			push @{ $r->{$1}->{$2} }, $rs;
+			push @{ $r->{$remote}->{$t} }, $rs;
 		}
 	}
 
@@ -1781,6 +2260,16 @@
 		}
 	} keys %$r;
 
+	foreach my $remote (keys %$r) {
+		foreach ( grep { defined $_ }
+			  map { $r->{$remote}->{$_} } qw(branches tags) ) {
+			foreach my $rs ( @$_ ) {
+				$rs->{ignore_refs_regex} =
+				    $r->{$remote}->{ignore_refs_regex};
+			}
+		}
+	}
+
 	$r;
 }
 
@@ -1869,14 +2358,15 @@
 		}
 	}
 	my ($xrepo_id, $xpath) = find_ref($self->refname);
-	if (defined $xpath) {
+	if (!$no_write && defined $xpath) {
 		die "svn-remote.$xrepo_id.fetch already set to track ",
-		    "$xpath:refs/remotes/", $self->refname, "\n";
+		    "$xpath:", $self->refname, "\n";
 	}
 	unless ($no_write) {
 		command_noisy('config',
 			      "svn-remote.$self->{repo_id}.url", $url);
 		$self->{path} =~ s{^/}{};
+		$self->{path} =~ s{%([0-9A-F]{2})}{chr hex($1)}ieg;
 		command_noisy('config', '--add',
 			      "svn-remote.$self->{repo_id}.fetch",
 			      "$self->{path}:".$self->refname);
@@ -1946,7 +2436,7 @@
 	my ($ref_id) = @_;
 	foreach (command(qw/config -l/)) {
 		next unless m!^svn-remote\.(.+)\.fetch=
-		              \s*(.*)\s*:\s*refs/remotes/(.+)\s*$!x;
+		              \s*(.*?)\s*:\s*(.+?)\s*$!x;
 		my ($repo_id, $path, $ref) = ($1, $2, $3);
 		if ($ref eq $ref_id) {
 			$path = '' if ($path =~ m#^\./?#);
@@ -1963,27 +2453,32 @@
 		if (!defined $repo_id) {
 			die "Could not find a \"svn-remote.*.fetch\" key ",
 			    "in the repository configuration matching: ",
-			    "refs/remotes/$ref_id\n";
+			    "$ref_id\n";
 		}
 	}
 	my $self = _new($class, $repo_id, $ref_id, $path);
 	if (!defined $self->{path} || !length $self->{path}) {
 		my $fetch = command_oneline('config', '--get',
 		                            "svn-remote.$repo_id.fetch",
-		                            ":refs/remotes/$ref_id\$") or
+		                            ":$ref_id\$") or
 		     die "Failed to read \"svn-remote.$repo_id.fetch\" ",
-		         "\":refs/remotes/$ref_id\$\" in config\n";
+		         "\":$ref_id\$\" in config\n";
 		($self->{path}, undef) = split(/\s*:\s*/, $fetch);
 	}
+	$self->{path} =~ s{/+}{/}g;
+	$self->{path} =~ s{\A/}{};
+	$self->{path} =~ s{/\z}{};
 	$self->{url} = command_oneline('config', '--get',
 	                               "svn-remote.$repo_id.url") or
                   die "Failed to read \"svn-remote.$repo_id.url\" in config\n";
+	$self->{pushurl} = eval { command_oneline('config', '--get',
+	                          "svn-remote.$repo_id.pushurl") };
 	$self->rebuild;
 	$self;
 }
 
 sub refname {
-	my ($refname) = "refs/remotes/$_[0]->{ref_id}" ;
+	my ($refname) = $_[0]->{ref_id} ;
 
 	# It cannot end with a slash /, we'll throw up on this because
 	# SVN can't have directories with a slash in their name, either:
@@ -2008,6 +2503,14 @@
 	# .. becomes %2E%2E
 	$refname =~ s{\.\.}{%2E%2E}g;
 
+	# trailing dots and .lock are not allowed
+	# .$ becomes %2E and .lock becomes %2Elock
+	$refname =~ s{\.(?=$|lock$)}{%2E};
+
+	# the sequence @{ is used to access the reflog
+	# @{ becomes %40{
+	$refname =~ s{\@\{}{%40\{}g;
+
 	return $refname;
 }
 
@@ -2137,6 +2640,10 @@
 		die "Can't have both 'useSvnsyncProps' and 'rewriteRoot' ",
 		    "options set!\n";
 	}
+	if ($self->rewrite_uuid) {
+		die "Can't have both 'useSvnsyncProps' and 'rewriteUUID' ",
+		    "options set!\n";
+	}
 
 	my $svnsync;
 	# see if we have it in our config, first:
@@ -2400,12 +2907,6 @@
 		next if $seen{$p};
 		$seen{$p} = 1;
 		push @ret, $p;
-		# MAXPARENT is defined to 16 in commit-tree.c:
-		last if @ret >= 16;
-	}
-	if (@tmp) {
-		die "r$log_entry->{revision}: No room for parents:\n\t",
-		    join("\n\t", @tmp), "\n";
 	}
 	@ret;
 }
@@ -2424,6 +2925,20 @@
 	$self->{-rewrite_root} = $rwr;
 }
 
+sub rewrite_uuid {
+	my ($self) = @_;
+	return $self->{-rewrite_uuid} if exists $self->{-rewrite_uuid};
+	my $k = "svn-remote.$self->{repo_id}.rewriteUUID";
+	my $rwid = eval { command_oneline(qw/config --get/, $k) };
+	if ($rwid) {
+		$rwid =~ s#/+$##;
+		if ($rwid !~ m#^[a-f0-9]{8}-(?:[a-f0-9]{4}-){3}[a-f0-9]{12}$#) {
+			die "$rwid is not a valid UUID (key: $k)\n";
+		}
+	}
+	$self->{-rewrite_uuid} = $rwid;
+}
+
 sub metadata_url {
 	my ($self) = @_;
 	($self->rewrite_root || $self->{url}) .
@@ -2435,6 +2950,15 @@
 	$self->{url} . (length $self->{path} ? '/' . $self->{path} : '');
 }
 
+sub full_pushurl {
+	my ($self) = @_;
+	if ($self->{pushurl}) {
+		return $self->{pushurl} . (length $self->{path} ? '/' .
+		       $self->{path} : '');
+	} else {
+		return $self->full_url;
+	}
+}
 
 sub set_commit_header_env {
 	my ($log_entry) = @_;
@@ -2600,7 +3124,8 @@
 	my $url = $self->ra->{url};
 	my $new_url = $url . $branch_from;
 	print STDERR  "Found possible branch point: ",
-	              "$new_url => ", $self->full_url, ", $r\n";
+	              "$new_url => ", $self->full_url, ", $r\n"
+	              unless $::_q > 1;
 	$branch_from =~ s#^/##;
 	my $gs = $self->other_gs($new_url, $url,
 		                 $branch_from, $r, $self->{ref_id});
@@ -2621,17 +3146,19 @@
 		($r0, $parent) = $gs->find_rev_before($r, 1);
 	}
 	if (defined $r0 && defined $parent) {
-		print STDERR "Found branch parent: ($self->{ref_id}) $parent\n";
+		print STDERR "Found branch parent: ($self->{ref_id}) $parent\n"
+		             unless $::_q > 1;
 		my $ed;
 		if ($self->ra->can_do_switch) {
 			$self->assert_index_clean($parent);
-			print STDERR "Following parent with do_switch\n";
+			print STDERR "Following parent with do_switch\n"
+			             unless $::_q > 1;
 			# do_switch works with svn/trunk >= r22312, but that
 			# is not included with SVN 1.4.3 (the latest version
 			# at the moment), so we can't rely on it
 			$self->{last_rev} = $r0;
 			$self->{last_commit} = $parent;
-			$ed = SVN::Git::Fetcher->new($self, $gs->{path});
+			$ed = Git::SVN::Fetcher->new($self, $gs->{path});
 			$gs->ra->gs_do_switch($r0, $rev, $gs,
 					      $self->full_url, $ed)
 			  or die "SVN connection failed somewhere...\n";
@@ -2640,18 +3167,20 @@
 			print STDERR "Trees match:\n",
 			             "  $new_url\@$r0\n",
 			             "  ${\$self->full_url}\@$rev\n",
-				     "Following parent with no changes\n";
+			             "Following parent with no changes\n"
+			             unless $::_q > 1;
 			$self->tmp_index_do(sub {
 			    command_noisy('read-tree', $parent);
 			});
 			$self->{last_commit} = $parent;
 		} else {
-			print STDERR "Following parent with do_update\n";
-			$ed = SVN::Git::Fetcher->new($self);
+			print STDERR "Following parent with do_update\n"
+			             unless $::_q > 1;
+			$ed = Git::SVN::Fetcher->new($self);
 			$self->ra->gs_do_update($rev, $rev, $self, $ed)
 			  or die "SVN connection failed somewhere...\n";
 		}
-		print STDERR "Successfully followed parent\n";
+		print STDERR "Successfully followed parent\n" unless $::_q > 1;
 		return $self->make_log_entry($rev, [$parent], $ed);
 	}
 	return undef;
@@ -2670,7 +3199,7 @@
 			push @{$log_entry->{parents}}, $lc;
 			return $log_entry;
 		}
-		$ed = SVN::Git::Fetcher->new($self);
+		$ed = Git::SVN::Fetcher->new($self);
 		$last_rev = $self->{last_rev};
 		$ed->{c} = $lc;
 		@parents = ($lc);
@@ -2679,7 +3208,7 @@
 		if (my $log_entry = $self->find_parent_branch($paths, $rev)) {
 			return $log_entry;
 		}
-		$ed = SVN::Git::Fetcher->new($self);
+		$ed = Git::SVN::Fetcher->new($self);
 	}
 	unless ($self->ra->gs_do_update($last_rev, $rev, $self, $ed)) {
 		die "SVN connection failed somewhere...\n";
@@ -2687,6 +3216,62 @@
 	$self->make_log_entry($rev, \@parents, $ed);
 }
 
+sub mkemptydirs {
+	my ($self, $r) = @_;
+
+	sub scan {
+		my ($r, $empty_dirs, $line) = @_;
+		if (defined $r && $line =~ /^r(\d+)$/) {
+			return 0 if $1 > $r;
+		} elsif ($line =~ /^  \+empty_dir: (.+)$/) {
+			$empty_dirs->{$1} = 1;
+		} elsif ($line =~ /^  \-empty_dir: (.+)$/) {
+			my @d = grep {m[^\Q$1\E(/|$)]} (keys %$empty_dirs);
+			delete @$empty_dirs{@d};
+		}
+		1; # continue
+	};
+
+	my %empty_dirs = ();
+	my $gz_file = "$self->{dir}/unhandled.log.gz";
+	if (-f $gz_file) {
+		if (!$can_compress) {
+			warn "Compress::Zlib could not be found; ",
+			     "empty directories in $gz_file will not be read\n";
+		} else {
+			my $gz = Compress::Zlib::gzopen($gz_file, "rb") or
+				die "Unable to open $gz_file: $!\n";
+			my $line;
+			while ($gz->gzreadline($line) > 0) {
+				scan($r, \%empty_dirs, $line) or last;
+			}
+			$gz->gzclose;
+		}
+	}
+
+	if (open my $fh, '<', "$self->{dir}/unhandled.log") {
+		binmode $fh or croak "binmode: $!";
+		while (<$fh>) {
+			scan($r, \%empty_dirs, $_) or last;
+		}
+		close $fh;
+	}
+
+	my $strip = qr/\A\Q$self->{path}\E(?:\/|$)/;
+	foreach my $d (sort keys %empty_dirs) {
+		$d = uri_decode($d);
+		$d =~ s/$strip//;
+		next unless length($d);
+		next if -d $d;
+		if (-e $d) {
+			warn "$d exists but is not a directory\n";
+		} else {
+			print "creating empty directory: $d\n";
+			mkpath([$d]);
+		}
+	}
+}
+
 sub get_untracked {
 	my ($self, $ed) = @_;
 	my @out;
@@ -2729,6 +3314,14 @@
 	\@out;
 }
 
+sub get_tz {
+	# some systmes don't handle or mishandle %z, so be creative.
+	my $t = shift || time;
+	my $gm = timelocal(gmtime($t));
+	my $sign = qw( + + - )[ $t <=> $gm ];
+	return sprintf("%s%02d%02d", $sign, (gmtime(abs($t - $gm)))[2,1]);
+}
+
 # parse_svn_date(DATE)
 # --------------------
 # Given a date (in UTC) from Subversion, return a string in the format
@@ -2761,8 +3354,7 @@
 			delete $ENV{TZ};
 		}
 
-		my $our_TZ =
-		    POSIX::strftime('%Z', $S, $M, $H, $d, $m - 1, $Y - 1900);
+		my $our_TZ = get_tz();
 
 		# This converts $epoch_in_UTC into our local timezone.
 		my ($sec, $min, $hour, $mday, $mon, $year,
@@ -2792,24 +3384,36 @@
 	my $gs = Git::SVN->find_by_url($new_url, $url, $branch_from);
 	unless ($gs) {
 		my $ref_id = $old_ref_id;
-		$ref_id =~ s/\@\d+$//;
+		$ref_id =~ s/\@\d+-*$//;
 		$ref_id .= "\@$r";
 		# just grow a tail if we're not unique enough :x
 		$ref_id .= '-' while find_ref($ref_id);
-		print STDERR "Initializing parent: $ref_id\n";
 		my ($u, $p, $repo_id) = ($new_url, '', $ref_id);
 		if ($u =~ s#^\Q$url\E(/|$)##) {
 			$p = $u;
 			$u = $url;
 			$repo_id = $self->{repo_id};
 		}
-		$gs = Git::SVN->init($u, $p, $repo_id, $ref_id, 1);
+		while (1) {
+			# It is possible to tag two different subdirectories at
+			# the same revision.  If the url for an existing ref
+			# does not match, we must either find a ref with a
+			# matching url or create a new ref by growing a tail.
+			$gs = Git::SVN->init($u, $p, $repo_id, $ref_id, 1);
+			my (undef, $max_commit) = $gs->rev_map_max(1);
+			last if (!$max_commit);
+			my ($url) = ::cmt_metadata($max_commit);
+			last if ($url eq $gs->metadata_url);
+			$ref_id .= '-';
+		}
+		print STDERR "Initializing parent: $ref_id\n" unless $::_q > 1;
 	}
 	$gs
 }
 
 sub call_authors_prog {
 	my ($orig_author) = @_;
+	$orig_author = command_oneline('rev-parse', '--sq-quote', $orig_author);
 	my $author = `$::_authors_prog $orig_author`;
 	if ($? != 0) {
 		die "$::_authors_prog failed with exit code $?\n"
@@ -2839,14 +3443,365 @@
 	$author;
 }
 
+sub find_extra_svk_parents {
+	my ($self, $ed, $tickets, $parents) = @_;
+	# aha!  svk:merge property changed...
+	my @tickets = split "\n", $tickets;
+	my @known_parents;
+	for my $ticket ( @tickets ) {
+		my ($uuid, $path, $rev) = split /:/, $ticket;
+		if ( $uuid eq $self->ra_uuid ) {
+			my $url = $self->{url};
+			my $repos_root = $url;
+			my $branch_from = $path;
+			$branch_from =~ s{^/}{};
+			my $gs = $self->other_gs($repos_root."/".$branch_from,
+			                         $url,
+			                         $branch_from,
+			                         $rev,
+			                         $self->{ref_id});
+			if ( my $commit = $gs->rev_map_get($rev, $uuid) ) {
+				# wahey!  we found it, but it might be
+				# an old one (!)
+				push @known_parents, [ $rev, $commit ];
+			}
+		}
+	}
+	# Ordering matters; highest-numbered commit merge tickets
+	# first, as they may account for later merge ticket additions
+	# or changes.
+	@known_parents = map {$_->[1]} sort {$b->[0] <=> $a->[0]} @known_parents;
+	for my $parent ( @known_parents ) {
+		my @cmd = ('rev-list', $parent, map { "^$_" } @$parents );
+		my ($msg_fh, $ctx) = command_output_pipe(@cmd);
+		my $new;
+		while ( <$msg_fh> ) {
+			$new=1;last;
+		}
+		command_close_pipe($msg_fh, $ctx);
+		if ( $new ) {
+			print STDERR
+			    "Found merge parent (svk:merge ticket): $parent\n";
+			push @$parents, $parent;
+		}
+	}
+}
+
+sub lookup_svn_merge {
+	my $uuid = shift;
+	my $url = shift;
+	my $merge = shift;
+
+	my ($source, $revs) = split ":", $merge;
+	my $path = $source;
+	$path =~ s{^/}{};
+	my $gs = Git::SVN->find_by_url($url.$source, $url, $path);
+	if ( !$gs ) {
+		warn "Couldn't find revmap for $url$source\n";
+		return;
+	}
+	my @ranges = split ",", $revs;
+	my ($tip, $tip_commit);
+	my @merged_commit_ranges;
+	# find the tip
+	for my $range ( @ranges ) {
+		my ($bottom, $top) = split "-", $range;
+		$top ||= $bottom;
+		my $bottom_commit = $gs->find_rev_after( $bottom, 1, $top );
+		my $top_commit = $gs->find_rev_before( $top, 1, $bottom );
+
+		unless ($top_commit and $bottom_commit) {
+			warn "W:unknown path/rev in svn:mergeinfo "
+				."dirprop: $source:$range\n";
+			next;
+		}
+
+		if (scalar(command('rev-parse', "$bottom_commit^@"))) {
+			push @merged_commit_ranges,
+			     "$bottom_commit^..$top_commit";
+		} else {
+			push @merged_commit_ranges, "$top_commit";
+		}
+
+		if ( !defined $tip or $top > $tip ) {
+			$tip = $top;
+			$tip_commit = $top_commit;
+		}
+	}
+	return ($tip_commit, @merged_commit_ranges);
+}
+
+sub _rev_list {
+	my ($msg_fh, $ctx) = command_output_pipe(
+		"rev-list", @_,
+	       );
+	my @rv;
+	while ( <$msg_fh> ) {
+		chomp;
+		push @rv, $_;
+	}
+	command_close_pipe($msg_fh, $ctx);
+	@rv;
+}
+
+sub check_cherry_pick {
+	my $base = shift;
+	my $tip = shift;
+	my $parents = shift;
+	my @ranges = @_;
+	my %commits = map { $_ => 1 }
+		_rev_list("--no-merges", $tip, "--not", $base, @$parents, "--");
+	for my $range ( @ranges ) {
+		delete @commits{_rev_list($range, "--")};
+	}
+	for my $commit (keys %commits) {
+		if (has_no_changes($commit)) {
+			delete $commits{$commit};
+		}
+	}
+	return (keys %commits);
+}
+
+sub has_no_changes {
+	my $commit = shift;
+
+	my @revs = split / /, command_oneline(
+		qw(rev-list --parents -1 -m), $commit);
+
+	# Commits with no parents, e.g. the start of a partial branch,
+	# have changes by definition.
+	return 1 if (@revs < 2);
+
+	# Commits with multiple parents, e.g a merge, have no changes
+	# by definition.
+	return 0 if (@revs > 2);
+
+	return (command_oneline("rev-parse", "$commit^{tree}") eq
+		command_oneline("rev-parse", "$commit~1^{tree}"));
+}
+
+sub tie_for_persistent_memoization {
+	my $hash = shift;
+	my $path = shift;
+
+	if ($can_use_yaml) {
+		tie %$hash => 'Git::SVN::Memoize::YAML', "$path.yaml";
+	} else {
+		tie %$hash => 'Memoize::Storable', "$path.db", 'nstore';
+	}
+}
+
+# The GIT_DIR environment variable is not always set until after the command
+# line arguments are processed, so we can't memoize in a BEGIN block.
+{
+	my $memoized = 0;
+
+	sub memoize_svn_mergeinfo_functions {
+		return if $memoized;
+		$memoized = 1;
+
+		my $cache_path = "$ENV{GIT_DIR}/svn/.caches/";
+		mkpath([$cache_path]) unless -d $cache_path;
+
+		my %lookup_svn_merge_cache;
+		my %check_cherry_pick_cache;
+		my %has_no_changes_cache;
+
+		tie_for_persistent_memoization(\%lookup_svn_merge_cache,
+		    "$cache_path/lookup_svn_merge");
+		memoize 'lookup_svn_merge',
+			SCALAR_CACHE => 'FAULT',
+			LIST_CACHE => ['HASH' => \%lookup_svn_merge_cache],
+		;
+
+		tie_for_persistent_memoization(\%check_cherry_pick_cache,
+		    "$cache_path/check_cherry_pick");
+		memoize 'check_cherry_pick',
+			SCALAR_CACHE => 'FAULT',
+			LIST_CACHE => ['HASH' => \%check_cherry_pick_cache],
+		;
+
+		tie_for_persistent_memoization(\%has_no_changes_cache,
+		    "$cache_path/has_no_changes");
+		memoize 'has_no_changes',
+			SCALAR_CACHE => ['HASH' => \%has_no_changes_cache],
+			LIST_CACHE => 'FAULT',
+		;
+	}
+
+	sub unmemoize_svn_mergeinfo_functions {
+		return if not $memoized;
+		$memoized = 0;
+
+		Memoize::unmemoize 'lookup_svn_merge';
+		Memoize::unmemoize 'check_cherry_pick';
+		Memoize::unmemoize 'has_no_changes';
+	}
+
+	Memoize::memoize 'Git::SVN::repos_root';
+}
+
+END {
+	# Force cache writeout explicitly instead of waiting for
+	# global destruction to avoid segfault in Storable:
+	# http://rt.cpan.org/Public/Bug/Display.html?id=36087
+	unmemoize_svn_mergeinfo_functions();
+}
+
+sub parents_exclude {
+	my $parents = shift;
+	my @commits = @_;
+	return unless @commits;
+
+	my @excluded;
+	my $excluded;
+	do {
+		my @cmd = ('rev-list', "-1", @commits, "--not", @$parents );
+		$excluded = command_oneline(@cmd);
+		if ( $excluded ) {
+			my @new;
+			my $found;
+			for my $commit ( @commits ) {
+				if ( $commit eq $excluded ) {
+					push @excluded, $commit;
+					$found++;
+					last;
+				}
+				else {
+					push @new, $commit;
+				}
+			}
+			die "saw commit '$excluded' in rev-list output, "
+				."but we didn't ask for that commit (wanted: @commits --not @$parents)"
+					unless $found;
+			@commits = @new;
+		}
+	}
+		while ($excluded and @commits);
+
+	return @excluded;
+}
+
+
+# note: this function should only be called if the various dirprops
+# have actually changed
+sub find_extra_svn_parents {
+	my ($self, $ed, $mergeinfo, $parents) = @_;
+	# aha!  svk:merge property changed...
+
+	memoize_svn_mergeinfo_functions();
+
+	# We first search for merged tips which are not in our
+	# history.  Then, we figure out which git revisions are in
+	# that tip, but not this revision.  If all of those revisions
+	# are now marked as merge, we can add the tip as a parent.
+	my @merges = split "\n", $mergeinfo;
+	my @merge_tips;
+	my $url = $self->{url};
+	my $uuid = $self->ra_uuid;
+	my %ranges;
+	for my $merge ( @merges ) {
+		my ($tip_commit, @ranges) =
+			lookup_svn_merge( $uuid, $url, $merge );
+		unless (!$tip_commit or
+				grep { $_ eq $tip_commit } @$parents ) {
+			push @merge_tips, $tip_commit;
+			$ranges{$tip_commit} = \@ranges;
+		} else {
+			push @merge_tips, undef;
+		}
+	}
+
+	my %excluded = map { $_ => 1 }
+		parents_exclude($parents, grep { defined } @merge_tips);
+
+	# check merge tips for new parents
+	my @new_parents;
+	for my $merge_tip ( @merge_tips ) {
+		my $spec = shift @merges;
+		next unless $merge_tip and $excluded{$merge_tip};
+
+		my $ranges = $ranges{$merge_tip};
+
+		# check out 'new' tips
+		my $merge_base;
+		eval {
+			$merge_base = command_oneline(
+				"merge-base",
+				@$parents, $merge_tip,
+			);
+		};
+		if ($@) {
+			die "An error occurred during merge-base"
+				unless $@->isa("Git::Error::Command");
+
+			warn "W: Cannot find common ancestor between ".
+			     "@$parents and $merge_tip. Ignoring merge info.\n";
+			next;
+		}
+
+		# double check that there are no missing non-merge commits
+		my (@incomplete) = check_cherry_pick(
+			$merge_base, $merge_tip,
+			$parents,
+			@$ranges,
+		       );
+
+		if ( @incomplete ) {
+			warn "W:svn cherry-pick ignored ($spec) - missing "
+				.@incomplete." commit(s) (eg $incomplete[0])\n";
+		} else {
+			warn
+				"Found merge parent (svn:mergeinfo prop): ",
+					$merge_tip, "\n";
+			push @new_parents, $merge_tip;
+		}
+	}
+
+	# cater for merges which merge commits from multiple branches
+	if ( @new_parents > 1 ) {
+		for ( my $i = 0; $i <= $#new_parents; $i++ ) {
+			for ( my $j = 0; $j <= $#new_parents; $j++ ) {
+				next if $i == $j;
+				next unless $new_parents[$i];
+				next unless $new_parents[$j];
+				my $revs = command_oneline(
+					"rev-list", "-1",
+					"$new_parents[$i]..$new_parents[$j]",
+				       );
+				if ( !$revs ) {
+					undef($new_parents[$j]);
+				}
+			}
+		}
+	}
+	push @$parents, grep { defined } @new_parents;
+}
+
 sub make_log_entry {
 	my ($self, $rev, $parents, $ed) = @_;
 	my $untracked = $self->get_untracked($ed);
 
+	my @parents = @$parents;
+	my $ps = $ed->{path_strip} || "";
+	for my $path ( grep { m/$ps/ } %{$ed->{dir_prop}} ) {
+		my $props = $ed->{dir_prop}{$path};
+		if ( $props->{"svk:merge"} ) {
+			$self->find_extra_svk_parents
+				($ed, $props->{"svk:merge"}, \@parents);
+		}
+		if ( $props->{"svn:mergeinfo"} ) {
+			$self->find_extra_svn_parents
+				($ed,
+				 $props->{"svn:mergeinfo"},
+				 \@parents);
+		}
+	}
+
 	open my $un, '>>', "$self->{dir}/unhandled.log" or croak $!;
 	print $un "r$rev\n" or croak $!;
 	print $un $_, "\n" foreach @$untracked;
-	my %log_entry = ( parents => $parents || [], revision => $rev,
+	my %log_entry = ( parents => \@parents, revision => $rev,
 	                  log => '');
 
 	my $headrev;
@@ -2900,6 +3855,10 @@
 			die "Can't have both 'useSvmProps' and 'rewriteRoot' ",
 			    "options set!\n";
 		}
+		if ($self->rewrite_uuid) {
+			die "Can't have both 'useSvmProps' and 'rewriteUUID' ",
+			    "options set!\n";
+		}
 		my ($uuid, $r) = $headrev =~ m{^([a-f\d\-]{30,}):(\d+)$}i;
 		# we don't want "SVM: initializing mirror for junk" ...
 		return undef if $r == 0;
@@ -2930,10 +3889,10 @@
 	} else {
 		my $url = $self->metadata_url;
 		remove_username($url);
-		$log_entry{metadata} = "$url\@$rev " .
-		                       $self->ra->get_uuid;
-		$email ||= "$author\@" . $self->ra->get_uuid;
-		$commit_email ||= "$author\@" . $self->ra->get_uuid;
+		my $uuid = $self->rewrite_uuid || $self->ra->get_uuid;
+		$log_entry{metadata} = "$url\@$rev " . $uuid;
+		$email ||= "$author\@" . $uuid;
+		$commit_email ||= "$author\@" . $uuid;
 	}
 	$log_entry{name} = $name;
 	$log_entry{email} = $email;
@@ -2969,7 +3928,7 @@
 	                editor_cb => sub {
 			       $self->set_tree_cb($log_entry, $tree, @_) },
 	                svn_path => $self->{path} );
-	if (!SVN::Git::Editor->new(\%ed_opts)->apply_diff) {
+	if (!Git::SVN::Editor->new(\%ed_opts)->apply_diff) {
 		print "No changes\nr$self->{last_rev} = $tree\n";
 	}
 }
@@ -3010,12 +3969,12 @@
 	my ($base_rev, $head) = ($partial ? $self->rev_map_max_norebuild(1) :
 		(undef, undef));
 	my ($log, $ctx) =
-	    command_output_pipe(qw/rev-list --pretty=raw --no-color --reverse/,
+	    command_output_pipe(qw/rev-list --pretty=raw --reverse/,
 				($head ? "$head.." : "") . $self->refname,
 				'--');
 	my $metadata_url = $self->metadata_url;
 	remove_username($metadata_url);
-	my $svn_uuid = $self->ra_uuid;
+	my $svn_uuid = $self->rewrite_uuid || $self->ra_uuid;
 	my $c;
 	while (<$log>) {
 		if ( m{^commit ($::sha1)$} ) {
@@ -3138,14 +4097,18 @@
 
 sub rev_map_set {
 	my ($self, $rev, $commit, $update_ref, $uuid) = @_;
+	defined $commit or die "missing arg3\n";
 	length $commit == 40 or die "arg3 must be a full SHA1 hexsum\n";
 	my $db = $self->map_path($uuid);
 	my $db_lock = "$db.lock";
-	my $sig;
+	my $sigmask;
 	$update_ref ||= 0;
 	if ($update_ref) {
-		$SIG{INT} = $SIG{HUP} = $SIG{TERM} = $SIG{ALRM} = $SIG{PIPE} =
-		            $SIG{USR1} = $SIG{USR2} = sub { $sig = $_[0] };
+		$sigmask = POSIX::SigSet->new();
+		my $signew = POSIX::SigSet->new(SIGINT, SIGHUP, SIGTERM,
+			SIGALRM, SIGUSR1, SIGUSR2);
+		sigprocmask(SIG_BLOCK, $signew, $sigmask) or
+			croak "Can't block signals: $!";
 	}
 	mkfile($db);
 
@@ -3184,9 +4147,8 @@
 	                            "$db_lock => $db ($!)\n";
 	delete $LOCKFILES{$db_lock};
 	if ($update_ref) {
-		$SIG{INT} = $SIG{HUP} = $SIG{TERM} = $SIG{ALRM} = $SIG{PIPE} =
-		            $SIG{USR1} = $SIG{USR2} = 'DEFAULT';
-		kill $sig, $$ if defined $sig;
+		sigprocmask(SIG_SETMASK, $sigmask) or
+			croak "Can't restore signal mask: $!";
 	}
 }
 
@@ -3262,7 +4224,7 @@
 		my $i = int(($l/24 + $u/24) / 2) * 24;
 		sysseek($fh, $i, SEEK_SET) or croak "seek: $!";
 		sysread($fh, my $buf, 24) == 24 or croak "read: $!";
-		my ($r, $c) = unpack('NH40', $buf);
+		my ($r, $c) = unpack(rev_map_fmt, $buf);
 
 		if ($r < $rev) {
 			$l = $i + 24;
@@ -3317,12 +4279,24 @@
 		$repo_id = $Git::SVN::default_repo_id;
 	}
 	unless (defined $ref_id && length $ref_id) {
-		$_[2] = $ref_id = $Git::SVN::default_ref_id;
+		$_prefix = '' unless defined($_prefix);
+		$_[2] = $ref_id =
+		             "refs/remotes/$_prefix$Git::SVN::default_ref_id";
 	}
 	$_[1] = $repo_id;
 	my $dir = "$ENV{GIT_DIR}/svn/$ref_id";
+
+	# Older repos imported by us used $GIT_DIR/svn/foo instead of
+	# $GIT_DIR/svn/refs/remotes/foo when tracking refs/remotes/foo
+	if ($ref_id =~ m{^refs/remotes/(.*)}) {
+		my $old_dir = "$ENV{GIT_DIR}/svn/$1";
+		if (-d $old_dir && ! -d $dir) {
+			$dir = $old_dir;
+		}
+	}
+
 	$_[3] = $path = '' unless (defined $path);
-	mkpath(["$ENV{GIT_DIR}/svn"]);
+	mkpath([$dir]);
 	bless {
 		ref_id => $ref_id, dir => $dir, index => "$dir/index",
 	        path => $path, config => "$ENV{GIT_DIR}/svn/config",
@@ -3360,1542 +4334,20 @@
 	$f
 }
 
+sub uri_decode {
+	my ($f) = @_;
+	$f =~ s#%([0-9a-fA-F]{2})#chr(hex($1))#eg;
+	$f
+}
+
 sub remove_username {
 	$_[0] =~ s{^([^:]*://)[^@]+@}{$1};
 }
 
-package Git::SVN::Prompt;
-use strict;
-use warnings;
-require SVN::Core;
-use vars qw/$_no_auth_cache $_username/;
-
-sub simple {
-	my ($cred, $realm, $default_username, $may_save, $pool) = @_;
-	$may_save = undef if $_no_auth_cache;
-	$default_username = $_username if defined $_username;
-	if (defined $default_username && length $default_username) {
-		if (defined $realm && length $realm) {
-			print STDERR "Authentication realm: $realm\n";
-			STDERR->flush;
-		}
-		$cred->username($default_username);
-	} else {
-		username($cred, $realm, $may_save, $pool);
-	}
-	$cred->password(_read_password("Password for '" .
-	                               $cred->username . "': ", $realm));
-	$cred->may_save($may_save);
-	$SVN::_Core::SVN_NO_ERROR;
-}
-
-sub ssl_server_trust {
-	my ($cred, $realm, $failures, $cert_info, $may_save, $pool) = @_;
-	$may_save = undef if $_no_auth_cache;
-	print STDERR "Error validating server certificate for '$realm':\n";
-	{
-		no warnings 'once';
-		# All variables SVN::Auth::SSL::* are used only once,
-		# so we're shutting up Perl warnings about this.
-		if ($failures & $SVN::Auth::SSL::UNKNOWNCA) {
-			print STDERR " - The certificate is not issued ",
-			    "by a trusted authority. Use the\n",
-			    "   fingerprint to validate ",
-			    "the certificate manually!\n";
-		}
-		if ($failures & $SVN::Auth::SSL::CNMISMATCH) {
-			print STDERR " - The certificate hostname ",
-			    "does not match.\n";
-		}
-		if ($failures & $SVN::Auth::SSL::NOTYETVALID) {
-			print STDERR " - The certificate is not yet valid.\n";
-		}
-		if ($failures & $SVN::Auth::SSL::EXPIRED) {
-			print STDERR " - The certificate has expired.\n";
-		}
-		if ($failures & $SVN::Auth::SSL::OTHER) {
-			print STDERR " - The certificate has ",
-			    "an unknown error.\n";
-		}
-	} # no warnings 'once'
-	printf STDERR
-	        "Certificate information:\n".
-	        " - Hostname: %s\n".
-	        " - Valid: from %s until %s\n".
-	        " - Issuer: %s\n".
-	        " - Fingerprint: %s\n",
-	        map $cert_info->$_, qw(hostname valid_from valid_until
-	                               issuer_dname fingerprint);
-	my $choice;
-prompt:
-	print STDERR $may_save ?
-	      "(R)eject, accept (t)emporarily or accept (p)ermanently? " :
-	      "(R)eject or accept (t)emporarily? ";
-	STDERR->flush;
-	$choice = lc(substr(<STDIN> || 'R', 0, 1));
-	if ($choice =~ /^t$/i) {
-		$cred->may_save(undef);
-	} elsif ($choice =~ /^r$/i) {
-		return -1;
-	} elsif ($may_save && $choice =~ /^p$/i) {
-		$cred->may_save($may_save);
-	} else {
-		goto prompt;
-	}
-	$cred->accepted_failures($failures);
-	$SVN::_Core::SVN_NO_ERROR;
-}
-
-sub ssl_client_cert {
-	my ($cred, $realm, $may_save, $pool) = @_;
-	$may_save = undef if $_no_auth_cache;
-	print STDERR "Client certificate filename: ";
-	STDERR->flush;
-	chomp(my $filename = <STDIN>);
-	$cred->cert_file($filename);
-	$cred->may_save($may_save);
-	$SVN::_Core::SVN_NO_ERROR;
-}
-
-sub ssl_client_cert_pw {
-	my ($cred, $realm, $may_save, $pool) = @_;
-	$may_save = undef if $_no_auth_cache;
-	$cred->password(_read_password("Password: ", $realm));
-	$cred->may_save($may_save);
-	$SVN::_Core::SVN_NO_ERROR;
-}
-
-sub username {
-	my ($cred, $realm, $may_save, $pool) = @_;
-	$may_save = undef if $_no_auth_cache;
-	if (defined $realm && length $realm) {
-		print STDERR "Authentication realm: $realm\n";
-	}
-	my $username;
-	if (defined $_username) {
-		$username = $_username;
-	} else {
-		print STDERR "Username: ";
-		STDERR->flush;
-		chomp($username = <STDIN>);
-	}
-	$cred->username($username);
-	$cred->may_save($may_save);
-	$SVN::_Core::SVN_NO_ERROR;
-}
-
-sub _read_password {
-	my ($prompt, $realm) = @_;
-	print STDERR $prompt;
-	STDERR->flush;
-	require Term::ReadKey;
-	Term::ReadKey::ReadMode('noecho');
-	my $password = '';
-	while (defined(my $key = Term::ReadKey::ReadKey(0))) {
-		last if $key =~ /[\012\015]/; # \n\r
-		$password .= $key;
-	}
-	Term::ReadKey::ReadMode('restore');
-	print STDERR "\n";
-	STDERR->flush;
-	$password;
-}
-
-package SVN::Git::Fetcher;
-use vars qw/@ISA/;
-use strict;
-use warnings;
-use Carp qw/croak/;
-use File::Temp qw/tempfile/;
-use IO::File qw//;
-use vars qw/$_ignore_regex/;
-
-# file baton members: path, mode_a, mode_b, pool, fh, blob, base
-sub new {
-	my ($class, $git_svn, $switch_path) = @_;
-	my $self = SVN::Delta::Editor->new;
-	bless $self, $class;
-	if (exists $git_svn->{last_commit}) {
-		$self->{c} = $git_svn->{last_commit};
-		$self->{empty_symlinks} =
-		                  _mark_empty_symlinks($git_svn, $switch_path);
-	}
-	$self->{ignore_regex} = eval { command_oneline('config', '--get',
-			     "svn-remote.$git_svn->{repo_id}.ignore-paths") };
-	$self->{empty} = {};
-	$self->{dir_prop} = {};
-	$self->{file_prop} = {};
-	$self->{absent_dir} = {};
-	$self->{absent_file} = {};
-	$self->{gii} = $git_svn->tmp_index_do(sub { Git::IndexInfo->new });
-	$self;
-}
-
-# this uses the Ra object, so it must be called before do_{switch,update},
-# not inside them (when the Git::SVN::Fetcher object is passed) to
-# do_{switch,update}
-sub _mark_empty_symlinks {
-	my ($git_svn, $switch_path) = @_;
-	my $bool = Git::config_bool('svn.brokenSymlinkWorkaround');
-	return {} if (!defined($bool)) || (defined($bool) && ! $bool);
-
-	my %ret;
-	my ($rev, $cmt) = $git_svn->last_rev_commit;
-	return {} unless ($rev && $cmt);
-
-	# allow the warning to be printed for each revision we fetch to
-	# ensure the user sees it.  The user can also disable the workaround
-	# on the repository even while git svn is running and the next
-	# revision fetched will skip this expensive function.
-	my $printed_warning;
-	chomp(my $empty_blob = `git hash-object -t blob --stdin < /dev/null`);
-	my ($ls, $ctx) = command_output_pipe(qw/ls-tree -r -z/, $cmt);
-	local $/ = "\0";
-	my $pfx = defined($switch_path) ? $switch_path : $git_svn->{path};
-	$pfx .= '/' if length($pfx);
-	while (<$ls>) {
-		chomp;
-		s/\A100644 blob $empty_blob\t//o or next;
-		unless ($printed_warning) {
-			print STDERR "Scanning for empty symlinks, ",
-			             "this may take a while if you have ",
-				     "many empty files\n",
-				     "You may disable this with `",
-				     "git config svn.brokenSymlinkWorkaround ",
-				     "false'.\n",
-				     "This may be done in a different ",
-				     "terminal without restarting ",
-				     "git svn\n";
-			$printed_warning = 1;
-		}
-		my $path = $_;
-		my (undef, $props) =
-		               $git_svn->ra->get_file($pfx.$path, $rev, undef);
-		if ($props->{'svn:special'}) {
-			$ret{$path} = 1;
-		}
-	}
-	command_close_pipe($ls, $ctx);
-	\%ret;
-}
-
-# returns true if a given path is inside a ".git" directory
-sub in_dot_git {
-	$_[0] =~ m{(?:^|/)\.git(?:/|$)};
-}
-
-# return value: 0 -- don't ignore, 1 -- ignore
-sub is_path_ignored {
-	my ($self, $path) = @_;
-	return 1 if in_dot_git($path);
-	return 1 if defined($self->{ignore_regex}) &&
-	            $path =~ m!$self->{ignore_regex}!;
-	return 0 unless defined($_ignore_regex);
-	return 1 if $path =~ m!$_ignore_regex!o;
-	return 0;
-}
-
-sub set_path_strip {
-	my ($self, $path) = @_;
-	$self->{path_strip} = qr/^\Q$path\E(\/|$)/ if length $path;
-}
-
-sub open_root {
-	{ path => '' };
-}
-
-sub open_directory {
-	my ($self, $path, $pb, $rev) = @_;
-	{ path => $path };
-}
-
-sub git_path {
-	my ($self, $path) = @_;
-	if ($self->{path_strip}) {
-		$path =~ s!$self->{path_strip}!! or
-		  die "Failed to strip path '$path' ($self->{path_strip})\n";
-	}
-	$path;
-}
-
-sub delete_entry {
-	my ($self, $path, $rev, $pb) = @_;
-	return undef if $self->is_path_ignored($path);
-
-	my $gpath = $self->git_path($path);
-	return undef if ($gpath eq '');
-
-	# remove entire directories.
-	my ($tree) = (command('ls-tree', '-z', $self->{c}, "./$gpath")
-	                 =~ /\A040000 tree ([a-f\d]{40})\t\Q$gpath\E\0/);
-	if ($tree) {
-		my ($ls, $ctx) = command_output_pipe(qw/ls-tree
-		                                     -r --name-only -z/,
-				                     $tree);
-		local $/ = "\0";
-		while (<$ls>) {
-			chomp;
-			my $rmpath = "$gpath/$_";
-			$self->{gii}->remove($rmpath);
-			print "\tD\t$rmpath\n" unless $::_q;
-		}
-		print "\tD\t$gpath/\n" unless $::_q;
-		command_close_pipe($ls, $ctx);
-		$self->{empty}->{$path} = 0
-	} else {
-		$self->{gii}->remove($gpath);
-		print "\tD\t$gpath\n" unless $::_q;
-	}
-	undef;
-}
-
-sub open_file {
-	my ($self, $path, $pb, $rev) = @_;
-	my ($mode, $blob);
-
-	goto out if $self->is_path_ignored($path);
-
-	my $gpath = $self->git_path($path);
-	($mode, $blob) = (command('ls-tree', '-z', $self->{c}, "./$gpath")
-	                     =~ /\A(\d{6}) blob ([a-f\d]{40})\t\Q$gpath\E\0/);
-	unless (defined $mode && defined $blob) {
-		die "$path was not found in commit $self->{c} (r$rev)\n";
-	}
-	if ($mode eq '100644' && $self->{empty_symlinks}->{$path}) {
-		$mode = '120000';
-	}
-out:
-	{ path => $path, mode_a => $mode, mode_b => $mode, blob => $blob,
-	  pool => SVN::Pool->new, action => 'M' };
-}
-
-sub add_file {
-	my ($self, $path, $pb, $cp_path, $cp_rev) = @_;
-	my $mode;
-
-	if (!$self->is_path_ignored($path)) {
-		my ($dir, $file) = ($path =~ m#^(.*?)/?([^/]+)$#);
-		delete $self->{empty}->{$dir};
-		$mode = '100644';
-	}
-	{ path => $path, mode_a => $mode, mode_b => $mode,
-	  pool => SVN::Pool->new, action => 'A' };
-}
-
-sub add_directory {
-	my ($self, $path, $cp_path, $cp_rev) = @_;
-	goto out if $self->is_path_ignored($path);
-	my $gpath = $self->git_path($path);
-	if ($gpath eq '') {
-		my ($ls, $ctx) = command_output_pipe(qw/ls-tree
-		                                     -r --name-only -z/,
-				                     $self->{c});
-		local $/ = "\0";
-		while (<$ls>) {
-			chomp;
-			$self->{gii}->remove($_);
-			print "\tD\t$_\n" unless $::_q;
-		}
-		command_close_pipe($ls, $ctx);
-		$self->{empty}->{$path} = 0;
-	}
-	my ($dir, $file) = ($path =~ m#^(.*?)/?([^/]+)$#);
-	delete $self->{empty}->{$dir};
-	$self->{empty}->{$path} = 1;
-out:
-	{ path => $path };
-}
-
-sub change_dir_prop {
-	my ($self, $db, $prop, $value) = @_;
-	return undef if $self->is_path_ignored($db->{path});
-	$self->{dir_prop}->{$db->{path}} ||= {};
-	$self->{dir_prop}->{$db->{path}}->{$prop} = $value;
-	undef;
-}
-
-sub absent_directory {
-	my ($self, $path, $pb) = @_;
-	return undef if $self->is_path_ignored($path);
-	$self->{absent_dir}->{$pb->{path}} ||= [];
-	push @{$self->{absent_dir}->{$pb->{path}}}, $path;
-	undef;
-}
-
-sub absent_file {
-	my ($self, $path, $pb) = @_;
-	return undef if $self->is_path_ignored($path);
-	$self->{absent_file}->{$pb->{path}} ||= [];
-	push @{$self->{absent_file}->{$pb->{path}}}, $path;
-	undef;
-}
-
-sub change_file_prop {
-	my ($self, $fb, $prop, $value) = @_;
-	return undef if $self->is_path_ignored($fb->{path});
-	if ($prop eq 'svn:executable') {
-		if ($fb->{mode_b} != 120000) {
-			$fb->{mode_b} = defined $value ? 100755 : 100644;
-		}
-	} elsif ($prop eq 'svn:special') {
-		$fb->{mode_b} = defined $value ? 120000 : 100644;
-	} else {
-		$self->{file_prop}->{$fb->{path}} ||= {};
-		$self->{file_prop}->{$fb->{path}}->{$prop} = $value;
-	}
-	undef;
-}
-
-sub apply_textdelta {
-	my ($self, $fb, $exp) = @_;
-	return undef if $self->is_path_ignored($fb->{path});
-	my $fh = $::_repository->temp_acquire('svn_delta');
-	# $fh gets auto-closed() by SVN::TxDelta::apply(),
-	# (but $base does not,) so dup() it for reading in close_file
-	open my $dup, '<&', $fh or croak $!;
-	my $base = $::_repository->temp_acquire('git_blob');
-
-	if ($fb->{blob}) {
-		my ($base_is_link, $size);
-
-		if ($fb->{mode_a} eq '120000' &&
-		    ! $self->{empty_symlinks}->{$fb->{path}}) {
-			print $base 'link ' or die "print $!\n";
-			$base_is_link = 1;
-		}
-	retry:
-		$size = $::_repository->cat_blob($fb->{blob}, $base);
-		die "Failed to read object $fb->{blob}" if ($size < 0);
-
-		if (defined $exp) {
-			seek $base, 0, 0 or croak $!;
-			my $got = ::md5sum($base);
-			if ($got ne $exp) {
-				my $err = "Checksum mismatch: ".
-				       "$fb->{path} $fb->{blob}\n" .
-				       "expected: $exp\n" .
-				       "     got: $got\n";
-				if ($base_is_link) {
-					warn $err,
-					     "Retrying... (possibly ",
-					     "a bad symlink from SVN)\n";
-					$::_repository->temp_reset($base);
-					$base_is_link = 0;
-					goto retry;
-				}
-				die $err;
-			}
-		}
-	}
-	seek $base, 0, 0 or croak $!;
-	$fb->{fh} = $fh;
-	$fb->{base} = $base;
-	[ SVN::TxDelta::apply($base, $dup, undef, $fb->{path}, $fb->{pool}) ];
-}
-
-sub close_file {
-	my ($self, $fb, $exp) = @_;
-	return undef if $self->is_path_ignored($fb->{path});
-
-	my $hash;
-	my $path = $self->git_path($fb->{path});
-	if (my $fh = $fb->{fh}) {
-		if (defined $exp) {
-			seek($fh, 0, 0) or croak $!;
-			my $got = ::md5sum($fh);
-			if ($got ne $exp) {
-				die "Checksum mismatch: $path\n",
-				    "expected: $exp\n    got: $got\n";
-			}
-		}
-		if ($fb->{mode_b} == 120000) {
-			sysseek($fh, 0, 0) or croak $!;
-			my $rd = sysread($fh, my $buf, 5);
-
-			if (!defined $rd) {
-				croak "sysread: $!\n";
-			} elsif ($rd == 0) {
-				warn "$path has mode 120000",
-				     " but it points to nothing\n",
-				     "converting to an empty file with mode",
-				     " 100644\n";
-				$fb->{mode_b} = '100644';
-			} elsif ($buf ne 'link ') {
-				warn "$path has mode 120000",
-				     " but is not a link\n";
-			} else {
-				my $tmp_fh = $::_repository->temp_acquire(
-					'svn_hash');
-				my $res;
-				while ($res = sysread($fh, my $str, 1024)) {
-					my $out = syswrite($tmp_fh, $str, $res);
-					defined($out) && $out == $res
-						or croak("write ",
-							Git::temp_path($tmp_fh),
-							": $!\n");
-				}
-				defined $res or croak $!;
-
-				($fh, $tmp_fh) = ($tmp_fh, $fh);
-				Git::temp_release($tmp_fh, 1);
-			}
-		}
-
-		$hash = $::_repository->hash_and_insert_object(
-				Git::temp_path($fh));
-		$hash =~ /^[a-f\d]{40}$/ or die "not a sha1: $hash\n";
-
-		Git::temp_release($fb->{base}, 1);
-		Git::temp_release($fh, 1);
-	} else {
-		$hash = $fb->{blob} or die "no blob information\n";
-	}
-	$fb->{pool}->clear;
-	$self->{gii}->update($fb->{mode_b}, $hash, $path) or croak $!;
-	print "\t$fb->{action}\t$path\n" if $fb->{action} && ! $::_q;
-	undef;
-}
-
-sub abort_edit {
-	my $self = shift;
-	$self->{nr} = $self->{gii}->{nr};
-	delete $self->{gii};
-	$self->SUPER::abort_edit(@_);
-}
-
-sub close_edit {
-	my $self = shift;
-	$self->{git_commit_ok} = 1;
-	$self->{nr} = $self->{gii}->{nr};
-	delete $self->{gii};
-	$self->SUPER::close_edit(@_);
-}
-
-package SVN::Git::Editor;
-use vars qw/@ISA $_rmdir $_cp_similarity $_find_copies_harder $_rename_limit/;
-use strict;
-use warnings;
-use Carp qw/croak/;
-use IO::File;
-
-sub new {
-	my ($class, $opts) = @_;
-	foreach (qw/svn_path r ra tree_a tree_b log editor_cb/) {
-		die "$_ required!\n" unless (defined $opts->{$_});
-	}
-
-	my $pool = SVN::Pool->new;
-	my $mods = generate_diff($opts->{tree_a}, $opts->{tree_b});
-	my $types = check_diff_paths($opts->{ra}, $opts->{svn_path},
-	                             $opts->{r}, $mods);
-
-	# $opts->{ra} functions should not be used after this:
-	my @ce  = $opts->{ra}->get_commit_editor($opts->{log},
-	                                        $opts->{editor_cb}, $pool);
-	my $self = SVN::Delta::Editor->new(@ce, $pool);
-	bless $self, $class;
-	foreach (qw/svn_path r tree_a tree_b/) {
-		$self->{$_} = $opts->{$_};
-	}
-	$self->{url} = $opts->{ra}->{url};
-	$self->{mods} = $mods;
-	$self->{types} = $types;
-	$self->{pool} = $pool;
-	$self->{bat} = { '' => $self->open_root($self->{r}, $self->{pool}) };
-	$self->{rm} = { };
-	$self->{path_prefix} = length $self->{svn_path} ?
-	                       "$self->{svn_path}/" : '';
-	$self->{config} = $opts->{config};
-	return $self;
-}
-
-sub generate_diff {
-	my ($tree_a, $tree_b) = @_;
-	my @diff_tree = qw(diff-tree -z -r);
-	if ($_cp_similarity) {
-		push @diff_tree, "-C$_cp_similarity";
-	} else {
-		push @diff_tree, '-C';
-	}
-	push @diff_tree, '--find-copies-harder' if $_find_copies_harder;
-	push @diff_tree, "-l$_rename_limit" if defined $_rename_limit;
-	push @diff_tree, $tree_a, $tree_b;
-	my ($diff_fh, $ctx) = command_output_pipe(@diff_tree);
-	local $/ = "\0";
-	my $state = 'meta';
-	my @mods;
-	while (<$diff_fh>) {
-		chomp $_; # this gets rid of the trailing "\0"
-		if ($state eq 'meta' && /^:(\d{6})\s(\d{6})\s
-					($::sha1)\s($::sha1)\s
-					([MTCRAD])\d*$/xo) {
-			push @mods, {	mode_a => $1, mode_b => $2,
-					sha1_a => $3, sha1_b => $4,
-					chg => $5 };
-			if ($5 =~ /^(?:C|R)$/) {
-				$state = 'file_a';
-			} else {
-				$state = 'file_b';
-			}
-		} elsif ($state eq 'file_a') {
-			my $x = $mods[$#mods] or croak "Empty array\n";
-			if ($x->{chg} !~ /^(?:C|R)$/) {
-				croak "Error parsing $_, $x->{chg}\n";
-			}
-			$x->{file_a} = $_;
-			$state = 'file_b';
-		} elsif ($state eq 'file_b') {
-			my $x = $mods[$#mods] or croak "Empty array\n";
-			if (exists $x->{file_a} && $x->{chg} !~ /^(?:C|R)$/) {
-				croak "Error parsing $_, $x->{chg}\n";
-			}
-			if (!exists $x->{file_a} && $x->{chg} =~ /^(?:C|R)$/) {
-				croak "Error parsing $_, $x->{chg}\n";
-			}
-			$x->{file_b} = $_;
-			$state = 'meta';
-		} else {
-			croak "Error parsing $_\n";
-		}
-	}
-	command_close_pipe($diff_fh, $ctx);
-	\@mods;
-}
-
-sub check_diff_paths {
-	my ($ra, $pfx, $rev, $mods) = @_;
-	my %types;
-	$pfx .= '/' if length $pfx;
-
-	sub type_diff_paths {
-		my ($ra, $types, $path, $rev) = @_;
-		my @p = split m#/+#, $path;
-		my $c = shift @p;
-		unless (defined $types->{$c}) {
-			$types->{$c} = $ra->check_path($c, $rev);
-		}
-		while (@p) {
-			$c .= '/' . shift @p;
-			next if defined $types->{$c};
-			$types->{$c} = $ra->check_path($c, $rev);
-		}
-	}
-
-	foreach my $m (@$mods) {
-		foreach my $f (qw/file_a file_b/) {
-			next unless defined $m->{$f};
-			my ($dir) = ($m->{$f} =~ m#^(.*?)/?(?:[^/]+)$#);
-			if (length $pfx.$dir && ! defined $types{$dir}) {
-				type_diff_paths($ra, \%types, $pfx.$dir, $rev);
-			}
-		}
-	}
-	\%types;
-}
-
-sub split_path {
-	return ($_[0] =~ m#^(.*?)/?([^/]+)$#);
-}
-
-sub repo_path {
-	my ($self, $path) = @_;
-	$self->{path_prefix}.(defined $path ? $path : '');
-}
-
-sub url_path {
-	my ($self, $path) = @_;
-	if ($self->{url} =~ m#^https?://#) {
-		$path =~ s!([^~a-zA-Z0-9_./-])!uc sprintf("%%%02x",ord($1))!eg;
-	}
-	$self->{url} . '/' . $self->repo_path($path);
-}
-
-sub rmdirs {
-	my ($self) = @_;
-	my $rm = $self->{rm};
-	delete $rm->{''}; # we never delete the url we're tracking
-	return unless %$rm;
-
-	foreach (keys %$rm) {
-		my @d = split m#/#, $_;
-		my $c = shift @d;
-		$rm->{$c} = 1;
-		while (@d) {
-			$c .= '/' . shift @d;
-			$rm->{$c} = 1;
-		}
-	}
-	delete $rm->{$self->{svn_path}};
-	delete $rm->{''}; # we never delete the url we're tracking
-	return unless %$rm;
-
-	my ($fh, $ctx) = command_output_pipe(qw/ls-tree --name-only -r -z/,
-	                                     $self->{tree_b});
-	local $/ = "\0";
-	while (<$fh>) {
-		chomp;
-		my @dn = split m#/#, $_;
-		while (pop @dn) {
-			delete $rm->{join '/', @dn};
-		}
-		unless (%$rm) {
-			close $fh;
-			return;
-		}
-	}
-	command_close_pipe($fh, $ctx);
-
-	my ($r, $p, $bat) = ($self->{r}, $self->{pool}, $self->{bat});
-	foreach my $d (sort { $b =~ tr#/#/# <=> $a =~ tr#/#/# } keys %$rm) {
-		$self->close_directory($bat->{$d}, $p);
-		my ($dn) = ($d =~ m#^(.*?)/?(?:[^/]+)$#);
-		print "\tD+\t$d/\n" unless $::_q;
-		$self->SUPER::delete_entry($d, $r, $bat->{$dn}, $p);
-		delete $bat->{$d};
-	}
-}
-
-sub open_or_add_dir {
-	my ($self, $full_path, $baton) = @_;
-	my $t = $self->{types}->{$full_path};
-	if (!defined $t) {
-		die "$full_path not known in r$self->{r} or we have a bug!\n";
-	}
-	{
-		no warnings 'once';
-		# SVN::Node::none and SVN::Node::file are used only once,
-		# so we're shutting up Perl's warnings about them.
-		if ($t == $SVN::Node::none) {
-			return $self->add_directory($full_path, $baton,
-			    undef, -1, $self->{pool});
-		} elsif ($t == $SVN::Node::dir) {
-			return $self->open_directory($full_path, $baton,
-			    $self->{r}, $self->{pool});
-		} # no warnings 'once'
-		print STDERR "$full_path already exists in repository at ",
-		    "r$self->{r} and it is not a directory (",
-		    ($t == $SVN::Node::file ? 'file' : 'unknown'),"/$t)\n";
-	} # no warnings 'once'
-	exit 1;
-}
-
-sub ensure_path {
-	my ($self, $path) = @_;
-	my $bat = $self->{bat};
-	my $repo_path = $self->repo_path($path);
-	return $bat->{''} unless (length $repo_path);
-	my @p = split m#/+#, $repo_path;
-	my $c = shift @p;
-	$bat->{$c} ||= $self->open_or_add_dir($c, $bat->{''});
-	while (@p) {
-		my $c0 = $c;
-		$c .= '/' . shift @p;
-		$bat->{$c} ||= $self->open_or_add_dir($c, $bat->{$c0});
-	}
-	return $bat->{$c};
-}
-
-# Subroutine to convert a globbing pattern to a regular expression.
-# From perl cookbook.
-sub glob2pat {
-	my $globstr = shift;
-	my %patmap = ('*' => '.*', '?' => '.', '[' => '[', ']' => ']');
-	$globstr =~ s{(.)} { $patmap{$1} || "\Q$1" }ge;
-	return '^' . $globstr . '$';
-}
-
-sub check_autoprop {
-	my ($self, $pattern, $properties, $file, $fbat) = @_;
-	# Convert the globbing pattern to a regular expression.
-	my $regex = glob2pat($pattern);
-	# Check if the pattern matches the file name.
-	if($file =~ m/($regex)/) {
-		# Parse the list of properties to set.
-		my @props = split(/;/, $properties);
-		foreach my $prop (@props) {
-			# Parse 'name=value' syntax and set the property.
-			if ($prop =~ /([^=]+)=(.*)/) {
-				my ($n,$v) = ($1,$2);
-				for ($n, $v) {
-					s/^\s+//; s/\s+$//;
-				}
-				$self->change_file_prop($fbat, $n, $v);
-			}
-		}
-	}
-}
-
-sub apply_autoprops {
-	my ($self, $file, $fbat) = @_;
-	my $conf_t = ${$self->{config}}{'config'};
-	no warnings 'once';
-	# Check [miscellany]/enable-auto-props in svn configuration.
-	if (SVN::_Core::svn_config_get_bool(
-		$conf_t,
-		$SVN::_Core::SVN_CONFIG_SECTION_MISCELLANY,
-		$SVN::_Core::SVN_CONFIG_OPTION_ENABLE_AUTO_PROPS,
-		0)) {
-		# Auto-props are enabled.  Enumerate them to look for matches.
-		my $callback = sub {
-			$self->check_autoprop($_[0], $_[1], $file, $fbat);
-		};
-		SVN::_Core::svn_config_enumerate(
-			$conf_t,
-			$SVN::_Core::SVN_CONFIG_SECTION_AUTO_PROPS,
-			$callback);
-	}
-}
-
-sub A {
-	my ($self, $m) = @_;
-	my ($dir, $file) = split_path($m->{file_b});
-	my $pbat = $self->ensure_path($dir);
-	my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat,
-					undef, -1);
-	print "\tA\t$m->{file_b}\n" unless $::_q;
-	$self->apply_autoprops($file, $fbat);
-	$self->chg_file($fbat, $m);
-	$self->close_file($fbat,undef,$self->{pool});
-}
-
-sub C {
-	my ($self, $m) = @_;
-	my ($dir, $file) = split_path($m->{file_b});
-	my $pbat = $self->ensure_path($dir);
-	my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat,
-				$self->url_path($m->{file_a}), $self->{r});
-	print "\tC\t$m->{file_a} => $m->{file_b}\n" unless $::_q;
-	$self->chg_file($fbat, $m);
-	$self->close_file($fbat,undef,$self->{pool});
-}
-
-sub delete_entry {
-	my ($self, $path, $pbat) = @_;
-	my $rpath = $self->repo_path($path);
-	my ($dir, $file) = split_path($rpath);
-	$self->{rm}->{$dir} = 1;
-	$self->SUPER::delete_entry($rpath, $self->{r}, $pbat, $self->{pool});
-}
-
-sub R {
-	my ($self, $m) = @_;
-	my ($dir, $file) = split_path($m->{file_b});
-	my $pbat = $self->ensure_path($dir);
-	my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat,
-				$self->url_path($m->{file_a}), $self->{r});
-	print "\tR\t$m->{file_a} => $m->{file_b}\n" unless $::_q;
-	$self->apply_autoprops($file, $fbat);
-	$self->chg_file($fbat, $m);
-	$self->close_file($fbat,undef,$self->{pool});
-
-	($dir, $file) = split_path($m->{file_a});
-	$pbat = $self->ensure_path($dir);
-	$self->delete_entry($m->{file_a}, $pbat);
-}
-
-sub M {
-	my ($self, $m) = @_;
-	my ($dir, $file) = split_path($m->{file_b});
-	my $pbat = $self->ensure_path($dir);
-	my $fbat = $self->open_file($self->repo_path($m->{file_b}),
-				$pbat,$self->{r},$self->{pool});
-	print "\t$m->{chg}\t$m->{file_b}\n" unless $::_q;
-	$self->chg_file($fbat, $m);
-	$self->close_file($fbat,undef,$self->{pool});
-}
-
-sub T { shift->M(@_) }
-
-sub change_file_prop {
-	my ($self, $fbat, $pname, $pval) = @_;
-	$self->SUPER::change_file_prop($fbat, $pname, $pval, $self->{pool});
-}
-
-sub _chg_file_get_blob ($$$$) {
-	my ($self, $fbat, $m, $which) = @_;
-	my $fh = $::_repository->temp_acquire("git_blob_$which");
-	if ($m->{"mode_$which"} =~ /^120/) {
-		print $fh 'link ' or croak $!;
-		$self->change_file_prop($fbat,'svn:special','*');
-	} elsif ($m->{mode_a} =~ /^120/ && $m->{"mode_$which"} !~ /^120/) {
-		$self->change_file_prop($fbat,'svn:special',undef);
-	}
-	my $blob = $m->{"sha1_$which"};
-	return ($fh,) if ($blob =~ /^0{40}$/);
-	my $size = $::_repository->cat_blob($blob, $fh);
-	croak "Failed to read object $blob" if ($size < 0);
-	$fh->flush == 0 or croak $!;
-	seek $fh, 0, 0 or croak $!;
-
-	my $exp = ::md5sum($fh);
-	seek $fh, 0, 0 or croak $!;
-	return ($fh, $exp);
-}
-
-sub chg_file {
-	my ($self, $fbat, $m) = @_;
-	if ($m->{mode_b} =~ /755$/ && $m->{mode_a} !~ /755$/) {
-		$self->change_file_prop($fbat,'svn:executable','*');
-	} elsif ($m->{mode_b} !~ /755$/ && $m->{mode_a} =~ /755$/) {
-		$self->change_file_prop($fbat,'svn:executable',undef);
-	}
-	my ($fh_a, $exp_a) = _chg_file_get_blob $self, $fbat, $m, 'a';
-	my ($fh_b, $exp_b) = _chg_file_get_blob $self, $fbat, $m, 'b';
-	my $pool = SVN::Pool->new;
-	my $atd = $self->apply_textdelta($fbat, $exp_a, $pool);
-	if (-s $fh_a) {
-		my $txstream = SVN::TxDelta::new ($fh_a, $fh_b, $pool);
-		my $res = SVN::TxDelta::send_txstream($txstream, @$atd, $pool);
-		if (defined $res) {
-			die "Unexpected result from send_txstream: $res\n",
-			    "(SVN::Core::VERSION: $SVN::Core::VERSION)\n";
-		}
-	} else {
-		my $got = SVN::TxDelta::send_stream($fh_b, @$atd, $pool);
-		die "Checksum mismatch\nexpected: $exp_b\ngot: $got\n"
-		    if ($got ne $exp_b);
-	}
-	Git::temp_release($fh_b, 1);
-	Git::temp_release($fh_a, 1);
-	$pool->clear;
-}
-
-sub D {
-	my ($self, $m) = @_;
-	my ($dir, $file) = split_path($m->{file_b});
-	my $pbat = $self->ensure_path($dir);
-	print "\tD\t$m->{file_b}\n" unless $::_q;
-	$self->delete_entry($m->{file_b}, $pbat);
-}
-
-sub close_edit {
-	my ($self) = @_;
-	my ($p,$bat) = ($self->{pool}, $self->{bat});
-	foreach (sort { $b =~ tr#/#/# <=> $a =~ tr#/#/# } keys %$bat) {
-		next if $_ eq '';
-		$self->close_directory($bat->{$_}, $p);
-	}
-	$self->close_directory($bat->{''}, $p);
-	$self->SUPER::close_edit($p);
-	$p->clear;
-}
-
-sub abort_edit {
-	my ($self) = @_;
-	$self->SUPER::abort_edit($self->{pool});
-}
-
-sub DESTROY {
-	my $self = shift;
-	$self->SUPER::DESTROY(@_);
-	$self->{pool}->clear;
-}
-
-# this drives the editor
-sub apply_diff {
-	my ($self) = @_;
-	my $mods = $self->{mods};
-	my %o = ( D => 1, R => 0, C => -1, A => 3, M => 3, T => 3 );
-	foreach my $m (sort { $o{$a->{chg}} <=> $o{$b->{chg}} } @$mods) {
-		my $f = $m->{chg};
-		if (defined $o{$f}) {
-			$self->$f($m);
-		} else {
-			fatal("Invalid change type: $f");
-		}
-	}
-	$self->rmdirs if $_rmdir;
-	if (@$mods == 0) {
-		$self->abort_edit;
-	} else {
-		$self->close_edit;
-	}
-	return scalar @$mods;
-}
-
-package Git::SVN::Ra;
-use vars qw/@ISA $config_dir $_log_window_size/;
-use strict;
-use warnings;
-my ($ra_invalid, $can_do_switch, %ignored_err, $RA);
-
-BEGIN {
-	# enforce temporary pool usage for some simple functions
-	no strict 'refs';
-	for my $f (qw/rev_proplist get_latest_revnum get_uuid get_repos_root
-	              get_file/) {
-		my $SUPER = "SUPER::$f";
-		*$f = sub {
-			my $self = shift;
-			my $pool = SVN::Pool->new;
-			my @ret = $self->$SUPER(@_,$pool);
-			$pool->clear;
-			wantarray ? @ret : $ret[0];
-		};
-	}
-}
-
-sub _auth_providers () {
-	[
-	  SVN::Client::get_simple_provider(),
-	  SVN::Client::get_ssl_server_trust_file_provider(),
-	  SVN::Client::get_simple_prompt_provider(
-	    \&Git::SVN::Prompt::simple, 2),
-	  SVN::Client::get_ssl_client_cert_file_provider(),
-	  SVN::Client::get_ssl_client_cert_prompt_provider(
-	    \&Git::SVN::Prompt::ssl_client_cert, 2),
-	  SVN::Client::get_ssl_client_cert_pw_file_provider(),
-	  SVN::Client::get_ssl_client_cert_pw_prompt_provider(
-	    \&Git::SVN::Prompt::ssl_client_cert_pw, 2),
-	  SVN::Client::get_username_provider(),
-	  SVN::Client::get_ssl_server_trust_prompt_provider(
-	    \&Git::SVN::Prompt::ssl_server_trust),
-	  SVN::Client::get_username_prompt_provider(
-	    \&Git::SVN::Prompt::username, 2)
-	]
-}
-
-sub escape_uri_only {
-	my ($uri) = @_;
-	my @tmp;
-	foreach (split m{/}, $uri) {
-		s/([^~\w.%+-]|%(?![a-fA-F0-9]{2}))/sprintf("%%%02X",ord($1))/eg;
-		push @tmp, $_;
-	}
-	join('/', @tmp);
-}
-
-sub escape_url {
-	my ($url) = @_;
-	if ($url =~ m#^(https?)://([^/]+)(.*)$#) {
-		my ($scheme, $domain, $uri) = ($1, $2, escape_uri_only($3));
-		$url = "$scheme://$domain$uri";
-	}
-	$url;
-}
-
-sub new {
-	my ($class, $url) = @_;
-	$url =~ s!/+$!!;
-	return $RA if ($RA && $RA->{url} eq $url);
-
-	SVN::_Core::svn_config_ensure($config_dir, undef);
-	my ($baton, $callbacks) = SVN::Core::auth_open_helper(_auth_providers);
-	my $config = SVN::Core::config_get_config($config_dir);
-	$RA = undef;
-	my $dont_store_passwords = 1;
-	my $conf_t = ${$config}{'config'};
-	{
-		no warnings 'once';
-		# The usage of $SVN::_Core::SVN_CONFIG_* variables
-		# produces warnings that variables are used only once.
-		# I had not found the better way to shut them up, so
-		# the warnings of type 'once' are disabled in this block.
-		if (SVN::_Core::svn_config_get_bool($conf_t,
-		    $SVN::_Core::SVN_CONFIG_SECTION_AUTH,
-		    $SVN::_Core::SVN_CONFIG_OPTION_STORE_PASSWORDS,
-		    1) == 0) {
-			SVN::_Core::svn_auth_set_parameter($baton,
-			    $SVN::_Core::SVN_AUTH_PARAM_DONT_STORE_PASSWORDS,
-			    bless (\$dont_store_passwords, "_p_void"));
-		}
-		if (SVN::_Core::svn_config_get_bool($conf_t,
-		    $SVN::_Core::SVN_CONFIG_SECTION_AUTH,
-		    $SVN::_Core::SVN_CONFIG_OPTION_STORE_AUTH_CREDS,
-		    1) == 0) {
-			$Git::SVN::Prompt::_no_auth_cache = 1;
-		}
-	} # no warnings 'once'
-	my $self = SVN::Ra->new(url => escape_url($url), auth => $baton,
-	                      config => $config,
-			      pool => SVN::Pool->new,
-	                      auth_provider_callbacks => $callbacks);
-	$self->{url} = $url;
-	$self->{svn_path} = $url;
-	$self->{repos_root} = $self->get_repos_root;
-	$self->{svn_path} =~ s#^\Q$self->{repos_root}\E(/|$)##;
-	$self->{cache} = { check_path => { r => 0, data => {} },
-	                   get_dir => { r => 0, data => {} } };
-	$RA = bless $self, $class;
-}
-
-sub check_path {
-	my ($self, $path, $r) = @_;
-	my $cache = $self->{cache}->{check_path};
-	if ($r == $cache->{r} && exists $cache->{data}->{$path}) {
-		return $cache->{data}->{$path};
-	}
-	my $pool = SVN::Pool->new;
-	my $t = $self->SUPER::check_path($path, $r, $pool);
-	$pool->clear;
-	if ($r != $cache->{r}) {
-		%{$cache->{data}} = ();
-		$cache->{r} = $r;
-	}
-	$cache->{data}->{$path} = $t;
-}
-
-sub get_dir {
-	my ($self, $dir, $r) = @_;
-	my $cache = $self->{cache}->{get_dir};
-	if ($r == $cache->{r}) {
-		if (my $x = $cache->{data}->{$dir}) {
-			return wantarray ? @$x : $x->[0];
-		}
-	}
-	my $pool = SVN::Pool->new;
-	my ($d, undef, $props) = $self->SUPER::get_dir($dir, $r, $pool);
-	my %dirents = map { $_ => { kind => $d->{$_}->kind } } keys %$d;
-	$pool->clear;
-	if ($r != $cache->{r}) {
-		%{$cache->{data}} = ();
-		$cache->{r} = $r;
-	}
-	$cache->{data}->{$dir} = [ \%dirents, $r, $props ];
-	wantarray ? (\%dirents, $r, $props) : \%dirents;
-}
-
-sub DESTROY {
-	# do not call the real DESTROY since we store ourselves in $RA
-}
-
-# get_log(paths, start, end, limit,
-#         discover_changed_paths, strict_node_history, receiver)
-sub get_log {
-	my ($self, @args) = @_;
-	my $pool = SVN::Pool->new;
-
-	# svn_log_changed_path_t objects passed to get_log are likely to be
-	# overwritten even if only the refs are copied to an external variable,
-	# so we should dup the structures in their entirety.  Using an
-	# externally passed pool (instead of our temporary and quickly cleared
-	# pool in Git::SVN::Ra) does not help matters at all...
-	my $receiver = pop @args;
-	my $prefix = "/".$self->{svn_path};
-	$prefix =~ s#/+($)##;
-	my $prefix_regex = qr#^\Q$prefix\E#;
-	push(@args, sub {
-		my ($paths) = $_[0];
-		return &$receiver(@_) unless $paths;
-		$_[0] = ();
-		foreach my $p (keys %$paths) {
-			my $i = $paths->{$p};
-			# Make path relative to our url, not repos_root
-			$p =~ s/$prefix_regex//;
-			my %s = map { $_ => $i->$_; }
-				qw/copyfrom_path copyfrom_rev action/;
-			if ($s{'copyfrom_path'}) {
-				$s{'copyfrom_path'} =~ s/$prefix_regex//;
-			}
-			$_[0]{$p} = \%s;
-		}
-		&$receiver(@_);
-	});
-
-
-	# the limit parameter was not supported in SVN 1.1.x, so we
-	# drop it.  Therefore, the receiver callback passed to it
-	# is made aware of this limitation by being wrapped if
-	# the limit passed to is being wrapped.
-	if ($SVN::Core::VERSION le '1.2.0') {
-		my $limit = splice(@args, 3, 1);
-		if ($limit > 0) {
-			my $receiver = pop @args;
-			push(@args, sub { &$receiver(@_) if (--$limit >= 0) });
-		}
-	}
-	my $ret = $self->SUPER::get_log(@args, $pool);
-	$pool->clear;
-	$ret;
-}
-
-sub trees_match {
-	my ($self, $url1, $rev1, $url2, $rev2) = @_;
-	my $ctx = SVN::Client->new(auth => _auth_providers);
-	my $out = IO::File->new_tmpfile;
-
-	# older SVN (1.1.x) doesn't take $pool as the last parameter for
-	# $ctx->diff(), so we'll create a default one
-	my $pool = SVN::Pool->new_default_sub;
-
-	$ra_invalid = 1; # this will open a new SVN::Ra connection to $url1
-	$ctx->diff([], $url1, $rev1, $url2, $rev2, 1, 1, 0, $out, $out);
-	$out->flush;
-	my $ret = (($out->stat)[7] == 0);
-	close $out or croak $!;
-
-	$ret;
-}
-
-sub get_commit_editor {
-	my ($self, $log, $cb, $pool) = @_;
-	my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef, 0) : ();
-	$self->SUPER::get_commit_editor($log, $cb, @lock, $pool);
-}
-
-sub gs_do_update {
-	my ($self, $rev_a, $rev_b, $gs, $editor) = @_;
-	my $new = ($rev_a == $rev_b);
-	my $path = $gs->{path};
-
-	if ($new && -e $gs->{index}) {
-		unlink $gs->{index} or die
-		  "Couldn't unlink index: $gs->{index}: $!\n";
-	}
-	my $pool = SVN::Pool->new;
-	$editor->set_path_strip($path);
-	my (@pc) = split m#/#, $path;
-	my $reporter = $self->do_update($rev_b, (@pc ? shift @pc : ''),
-	                                1, $editor, $pool);
-	my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef) : ();
-
-	# Since we can't rely on svn_ra_reparent being available, we'll
-	# just have to do some magic with set_path to make it so
-	# we only want a partial path.
-	my $sp = '';
-	my $final = join('/', @pc);
-	while (@pc) {
-		$reporter->set_path($sp, $rev_b, 0, @lock, $pool);
-		$sp .= '/' if length $sp;
-		$sp .= shift @pc;
-	}
-	die "BUG: '$sp' != '$final'\n" if ($sp ne $final);
-
-	$reporter->set_path($sp, $rev_a, $new, @lock, $pool);
-
-	$reporter->finish_report($pool);
-	$pool->clear;
-	$editor->{git_commit_ok};
-}
-
-# this requires SVN 1.4.3 or later (do_switch didn't work before 1.4.3, and
-# svn_ra_reparent didn't work before 1.4)
-sub gs_do_switch {
-	my ($self, $rev_a, $rev_b, $gs, $url_b, $editor) = @_;
-	my $path = $gs->{path};
-	my $pool = SVN::Pool->new;
-
-	my $full_url = $self->{url};
-	my $old_url = $full_url;
-	$full_url .= '/' . $path if length $path;
-	my ($ra, $reparented);
-
-	if ($old_url =~ m#^svn(\+ssh)?://# ||
-	    ($full_url =~ m#^https?://# &&
-	     escape_url($full_url) ne $full_url)) {
-		$_[0] = undef;
-		$self = undef;
-		$RA = undef;
-		$ra = Git::SVN::Ra->new($full_url);
-		$ra_invalid = 1;
-	} elsif ($old_url ne $full_url) {
-		SVN::_Ra::svn_ra_reparent($self->{session}, $full_url, $pool);
-		$self->{url} = $full_url;
-		$reparented = 1;
-	}
-
-	$ra ||= $self;
-	$url_b = escape_url($url_b);
-	my $reporter = $ra->do_switch($rev_b, '', 1, $url_b, $editor, $pool);
-	my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef) : ();
-	$reporter->set_path('', $rev_a, 0, @lock, $pool);
-	$reporter->finish_report($pool);
-
-	if ($reparented) {
-		SVN::_Ra::svn_ra_reparent($self->{session}, $old_url, $pool);
-		$self->{url} = $old_url;
-	}
-
-	$pool->clear;
-	$editor->{git_commit_ok};
-}
-
-sub longest_common_path {
-	my ($gsv, $globs) = @_;
-	my %common;
-	my $common_max = scalar @$gsv;
-
-	foreach my $gs (@$gsv) {
-		my @tmp = split m#/#, $gs->{path};
-		my $p = '';
-		foreach (@tmp) {
-			$p .= length($p) ? "/$_" : $_;
-			$common{$p} ||= 0;
-			$common{$p}++;
-		}
-	}
-	$globs ||= [];
-	$common_max += scalar @$globs;
-	foreach my $glob (@$globs) {
-		my @tmp = split m#/#, $glob->{path}->{left};
-		my $p = '';
-		foreach (@tmp) {
-			$p .= length($p) ? "/$_" : $_;
-			$common{$p} ||= 0;
-			$common{$p}++;
-		}
-	}
-
-	my $longest_path = '';
-	foreach (sort {length $b <=> length $a} keys %common) {
-		if ($common{$_} == $common_max) {
-			$longest_path = $_;
-			last;
-		}
-	}
-	$longest_path;
-}
-
-sub gs_fetch_loop_common {
-	my ($self, $base, $head, $gsv, $globs) = @_;
-	return if ($base > $head);
-	my $inc = $_log_window_size;
-	my ($min, $max) = ($base, $head < $base + $inc ? $head : $base + $inc);
-	my $longest_path = longest_common_path($gsv, $globs);
-	my $ra_url = $self->{url};
-	my $find_trailing_edge;
-	while (1) {
-		my %revs;
-		my $err;
-		my $err_handler = $SVN::Error::handler;
-		$SVN::Error::handler = sub {
-			($err) = @_;
-			skip_unknown_revs($err);
-		};
-		sub _cb {
-			my ($paths, $r, $author, $date, $log) = @_;
-			[ $paths,
-			  { author => $author, date => $date, log => $log } ];
-		}
-		$self->get_log([$longest_path], $min, $max, 0, 1, 1,
-		               sub { $revs{$_[1]} = _cb(@_) });
-		if ($err) {
-			print "Checked through r$max\r";
-		} else {
-			$find_trailing_edge = 1;
-		}
-		if ($err and $find_trailing_edge) {
-			print STDERR "Path '$longest_path' ",
-				     "was probably deleted:\n",
-				     $err->expanded_message,
-				     "\nWill attempt to follow ",
-				     "revisions r$min .. r$max ",
-				     "committed before the deletion\n";
-			my $hi = $max;
-			while (--$hi >= $min) {
-				my $ok;
-				$self->get_log([$longest_path], $min, $hi,
-				               0, 1, 1, sub {
-				               $ok = $_[1];
-				               $revs{$_[1]} = _cb(@_) });
-				if ($ok) {
-					print STDERR "r$min .. r$ok OK\n";
-					last;
-				}
-			}
-			$find_trailing_edge = 0;
-		}
-		$SVN::Error::handler = $err_handler;
-
-		my %exists = map { $_->{path} => $_ } @$gsv;
-		foreach my $r (sort {$a <=> $b} keys %revs) {
-			my ($paths, $logged) = @{$revs{$r}};
-
-			foreach my $gs ($self->match_globs(\%exists, $paths,
-			                                   $globs, $r)) {
-				if ($gs->rev_map_max >= $r) {
-					next;
-				}
-				next unless $gs->match_paths($paths, $r);
-				$gs->{logged_rev_props} = $logged;
-				if (my $last_commit = $gs->last_commit) {
-					$gs->assert_index_clean($last_commit);
-				}
-				my $log_entry = $gs->do_fetch($paths, $r);
-				if ($log_entry) {
-					$gs->do_git_commit($log_entry);
-				}
-				$INDEX_FILES{$gs->{index}} = 1;
-			}
-			foreach my $g (@$globs) {
-				my $k = "svn-remote.$g->{remote}." .
-				        "$g->{t}-maxRev";
-				Git::SVN::tmp_config($k, $r);
-			}
-			if ($ra_invalid) {
-				$_[0] = undef;
-				$self = undef;
-				$RA = undef;
-				$self = Git::SVN::Ra->new($ra_url);
-				$ra_invalid = undef;
-			}
-		}
-		# pre-fill the .rev_db since it'll eventually get filled in
-		# with '0' x40 if something new gets committed
-		foreach my $gs (@$gsv) {
-			next if $gs->rev_map_max >= $max;
-			next if defined $gs->rev_map_get($max);
-			$gs->rev_map_set($max, 0 x40);
-		}
-		foreach my $g (@$globs) {
-			my $k = "svn-remote.$g->{remote}.$g->{t}-maxRev";
-			Git::SVN::tmp_config($k, $max);
-		}
-		last if $max >= $head;
-		$min = $max + 1;
-		$max += $inc;
-		$max = $head if ($max > $head);
-	}
-	Git::SVN::gc();
-}
-
-sub get_dir_globbed {
-	my ($self, $left, $depth, $r) = @_;
-
-	my @x = eval { $self->get_dir($left, $r) };
-	return unless scalar @x == 3;
-	my $dirents = $x[0];
-	my @finalents;
-	foreach my $de (keys %$dirents) {
-		next if $dirents->{$de}->{kind} != $SVN::Node::dir;
-		if ($depth > 1) {
-			my @args = ("$left/$de", $depth - 1, $r);
-			foreach my $dir ($self->get_dir_globbed(@args)) {
-				push @finalents, "$de/$dir";
-			}
-		} else {
-			push @finalents, $de;
-		}
-	}
-	@finalents;
-}
-
-sub match_globs {
-	my ($self, $exists, $paths, $globs, $r) = @_;
-
-	sub get_dir_check {
-		my ($self, $exists, $g, $r) = @_;
-
-		my @dirs = $self->get_dir_globbed($g->{path}->{left},
-		                                  $g->{path}->{depth},
-		                                  $r);
-
-		foreach my $de (@dirs) {
-			my $p = $g->{path}->full_path($de);
-			next if $exists->{$p};
-			next if (length $g->{path}->{right} &&
-				 ($self->check_path($p, $r) !=
-				  $SVN::Node::dir));
-			$exists->{$p} = Git::SVN->init($self->{url}, $p, undef,
-					 $g->{ref}->full_path($de), 1);
-		}
-	}
-	foreach my $g (@$globs) {
-		if (my $path = $paths->{"/$g->{path}->{left}"}) {
-			if ($path->{action} =~ /^[AR]$/) {
-				get_dir_check($self, $exists, $g, $r);
-			}
-		}
-		foreach (keys %$paths) {
-			if (/$g->{path}->{left_regex}/ &&
-			    !/$g->{path}->{regex}/) {
-				next if $paths->{$_}->{action} !~ /^[AR]$/;
-				get_dir_check($self, $exists, $g, $r);
-			}
-			next unless /$g->{path}->{regex}/;
-			my $p = $1;
-			my $pathname = $g->{path}->full_path($p);
-			next if $exists->{$pathname};
-			next if ($self->check_path($pathname, $r) !=
-			         $SVN::Node::dir);
-			$exists->{$pathname} = Git::SVN->init(
-			                      $self->{url}, $pathname, undef,
-			                      $g->{ref}->full_path($p), 1);
-		}
-		my $c = '';
-		foreach (split m#/#, $g->{path}->{left}) {
-			$c .= "/$_";
-			next unless ($paths->{$c} &&
-			             ($paths->{$c}->{action} =~ /^[AR]$/));
-			get_dir_check($self, $exists, $g, $r);
-		}
-	}
-	values %$exists;
-}
-
-sub minimize_url {
-	my ($self) = @_;
-	return $self->{url} if ($self->{url} eq $self->{repos_root});
-	my $url = $self->{repos_root};
-	my @components = split(m!/!, $self->{svn_path});
-	my $c = '';
-	do {
-		$url .= "/$c" if length $c;
-		eval {
-			my $ra = (ref $self)->new($url);
-			my $latest = $ra->get_latest_revnum;
-			$ra->get_log("", $latest, 0, 1, 0, 1, sub {});
-		};
-	} while ($@ && ($c = shift @components));
-	$url;
-}
-
-sub can_do_switch {
-	my $self = shift;
-	unless (defined $can_do_switch) {
-		my $pool = SVN::Pool->new;
-		my $rep = eval {
-			$self->do_switch(1, '', 0, $self->{url},
-			                 SVN::Delta::Editor->new, $pool);
-		};
-		if ($@) {
-			$can_do_switch = 0;
-		} else {
-			$rep->abort_report($pool);
-			$can_do_switch = 1;
-		}
-		$pool->clear;
-	}
-	$can_do_switch;
-}
-
-sub skip_unknown_revs {
-	my ($err) = @_;
-	my $errno = $err->apr_err();
-	# Maybe the branch we're tracking didn't
-	# exist when the repo started, so it's
-	# not an error if it doesn't, just continue
-	#
-	# Wonderfully consistent library, eh?
-	# 160013 - svn:// and file://
-	# 175002 - http(s)://
-	# 175007 - http(s):// (this repo required authorization, too...)
-	#   More codes may be discovered later...
-	if ($errno == 175007 || $errno == 175002 || $errno == 160013) {
-		my $err_key = $err->expanded_message;
-		# revision numbers change every time, filter them out
-		$err_key =~ s/\d+/\0/g;
-		$err_key = "$errno\0$err_key";
-		unless ($ignored_err{$err_key}) {
-			warn "W: Ignoring error from SVN, path probably ",
-			     "does not exist: ($errno): ",
-			     $err->expanded_message,"\n";
-			warn "W: Do not be alarmed at the above message ",
-			     "git-svn is just searching aggressively for ",
-			     "old history.\n",
-			     "This may take a while on large repositories\n";
-			$ignored_err{$err_key} = 1;
-		}
-		return;
-	}
-	die "Error from SVN, ($errno): ", $err->expanded_message,"\n";
-}
-
 package Git::SVN::Log;
 use strict;
 use warnings;
 use POSIX qw/strftime/;
-use Time::Local;
 use constant commit_log_separator => ('-' x 72) . "\n";
 use vars qw/$TZ $limit $color $pager $non_recursive $verbose $oneline
             %rusers $show_commit $incremental/;
@@ -4978,17 +4430,20 @@
 
 # adapted from pager.c
 sub config_pager {
-	$pager ||= $ENV{GIT_PAGER} || $ENV{PAGER};
-	if (!defined $pager) {
-		$pager = 'less';
-	} elsif (length $pager == 0 || $pager eq 'cat') {
+	if (! -t *STDOUT) {
+		$ENV{GIT_PAGER_IN_USE} = 'false';
+		$pager = undef;
+		return;
+	}
+	chomp($pager = command_oneline(qw(var GIT_PAGER)));
+	if ($pager eq 'cat') {
 		$pager = undef;
 	}
 	$ENV{GIT_PAGER_IN_USE} = defined($pager);
 }
 
 sub run_pager {
-	return unless -t *STDOUT && defined $pager;
+	return unless defined $pager;
 	pipe my ($rfd, $wfd) or return;
 	defined(my $pid = fork) or ::fatal "Can't fork: $!";
 	if (!$pid) {
@@ -5002,11 +4457,8 @@
 }
 
 sub format_svn_date {
-	# some systmes don't handle or mishandle %z, so be creative.
 	my $t = shift || time;
-	my $gm = timelocal(gmtime($t));
-	my $sign = qw( + + - )[ $t <=> $gm ];
-	my $gmoff = sprintf("%s%02d%02d", $sign, (gmtime(abs($t - $gm)))[2,1]);
+	my $gmoff = Git::SVN::get_tz($t);
 	return strftime("%Y-%m-%d %H:%M:%S $gmoff (%a, %d %b %Y)", localtime($t));
 }
 
@@ -5166,7 +4618,7 @@
 	my (@k, $c, $d, $stat);
 	my $esc_color = qr/(?:\033\[(?:(?:\d+;)*\d*)?m)*/;
 	while (<$log>) {
-		if (/^${esc_color}commit -?($::sha1_short)/o) {
+		if (/^${esc_color}commit (?:- )?($::sha1_short)/o) {
 			my $cmt = $1;
 			if ($c && cmt_showable($c) && $c->{r} != $r_last) {
 				$r_last = $c->{r};
@@ -5495,7 +4947,7 @@
 			my $pfx = "svn-remote.$x->{old_repo_id}";
 
 			my $old_fetch = quotemeta("$x->{old_path}:".
-			                          "refs/remotes/$x->{ref_id}");
+			                          "$x->{ref_id}");
 			command_noisy(qw/config --unset/,
 			              "$pfx.fetch", '^'. $old_fetch . '$');
 			delete $r->{$x->{old_repo_id}}->
@@ -5561,29 +5013,48 @@
 use warnings;
 
 sub new {
-	my ($class, $glob) = @_;
+	my ($class, $glob, $pattern_ok) = @_;
 	my $re = $glob;
 	$re =~ s!/+$!!g; # no need for trailing slashes
-	$re =~ m!^([^*]*)(\*(?:/\*)*)([^*]*)$!;
-	my $temp = $re;
-	my ($left, $right) = ($1, $3);
-	$re = $2;
-	my $depth = $re =~ tr/*/*/;
-	if ($depth != $temp =~ tr/*/*/) {
-		die "Only one set of wildcard directories " .
-			"(e.g. '*' or '*/*/*') is supported: '$glob'\n";
+	my (@left, @right, @patterns);
+	my $state = "left";
+	my $die_msg = "Only one set of wildcard directories " .
+				"(e.g. '*' or '*/*/*') is supported: '$glob'\n";
+	for my $part (split(m|/|, $glob)) {
+		if ($part =~ /\*/ && $part ne "*") {
+			die "Invalid pattern in '$glob': $part\n";
+		} elsif ($pattern_ok && $part =~ /[{}]/ &&
+			 $part !~ /^\{[^{}]+\}/) {
+			die "Invalid pattern in '$glob': $part\n";
+		}
+		if ($part eq "*") {
+			die $die_msg if $state eq "right";
+			$state = "pattern";
+			push(@patterns, "[^/]*");
+		} elsif ($pattern_ok && $part =~ /^\{(.*)\}$/) {
+			die $die_msg if $state eq "right";
+			$state = "pattern";
+			my $p = quotemeta($1);
+			$p =~ s/\\,/|/g;
+			push(@patterns, "(?:$p)");
+		} else {
+			if ($state eq "left") {
+				push(@left, $part);
+			} else {
+				push(@right, $part);
+				$state = "right";
+			}
+		}
 	}
+	my $depth = @patterns;
 	if ($depth == 0) {
-		die "One '*' is needed for glob: '$glob'\n";
+		die "One '*' is needed in glob: '$glob'\n";
 	}
-	$re =~ s!\*!\[^/\]*!g;
-	$re = quotemeta($left) . "($re)" . quotemeta($right);
-	if (length $left && !($left =~ s!/+$!!g)) {
-		die "Missing trailing '/' on left side of: '$glob' ($left)\n";
-	}
-	if (length $right && !($right =~ s!^/+!!g)) {
-		die "Missing leading '/' on right side of: '$glob' ($right)\n";
-	}
+	my $left = join('/', @left);
+	my $right = join('/', @right);
+	$re = join('/', @patterns);
+	$re = join('\/',
+		   grep(length, quotemeta($left), "($re)", quotemeta($right)));
 	my $left_re = qr/^\/\Q$left\E(\/|$)/;
 	bless { left => $left, right => $right, left_regex => $left_re,
 	        regex => qr/$re/, glob => $glob, depth => $depth }, $class;
diff --git a/git-web--browse.sh b/git-web--browse.sh
index 4f5c740..1e82726 100755
--- a/git-web--browse.sh
+++ b/git-web--browse.sh
@@ -31,148 +31,161 @@
 
 valid_tool() {
 	case "$1" in
-		firefox | iceweasel | konqueror | w3m | links | lynx | dillo | open | start)
-			;; # happy
-		*)
-			valid_custom_tool "$1" || return 1
-			;;
+	firefox | iceweasel | seamonkey | iceape | \
+	chrome | google-chrome | chromium | chromium-browser |\
+	konqueror | opera | w3m | elinks | links | lynx | dillo | open | start)
+		;; # happy
+	*)
+		valid_custom_tool "$1" || return 1
+		;;
 	esac
 }
 
 init_browser_path() {
 	browser_path=$(git config "browser.$1.path")
-	test -z "$browser_path" && browser_path="$1"
+	if test -z "$browser_path" &&
+	   test "$1" = chromium &&
+	   type chromium-browser >/dev/null 2>&1
+	then
+		browser_path=chromium-browser
+	fi
+	: ${browser_path:="$1"}
 }
 
 while test $# != 0
 do
-    case "$1" in
+	case "$1" in
 	-b|--browser*|-t|--tool*)
-	    case "$#,$1" in
+		case "$#,$1" in
 		*,*=*)
-		    browser=`expr "z$1" : 'z-[^=]*=\(.*\)'`
-		    ;;
+			browser=`expr "z$1" : 'z-[^=]*=\(.*\)'`
+			;;
 		1,*)
-		    usage ;;
+			usage ;;
 		*)
-		    browser="$2"
-		    shift ;;
-	    esac
-	    ;;
+			browser="$2"
+			shift ;;
+		esac
+		;;
 	-c|--config*)
-	    case "$#,$1" in
+		case "$#,$1" in
 		*,*=*)
-		    conf=`expr "z$1" : 'z-[^=]*=\(.*\)'`
-		    ;;
+			conf=`expr "z$1" : 'z-[^=]*=\(.*\)'`
+			;;
 		1,*)
-		    usage ;;
+			usage ;;
 		*)
-		    conf="$2"
-		    shift ;;
-	    esac
-	    ;;
+			conf="$2"
+			shift ;;
+		esac
+		;;
 	--)
-	    break
-	    ;;
+		break
+		;;
 	-*)
-	    usage
-	    ;;
+		usage
+		;;
 	*)
-	    break
-	    ;;
-    esac
-    shift
+		break
+		;;
+	esac
+	shift
 done
 
 test $# = 0 && usage
 
 if test -z "$browser"
 then
-    for opt in "$conf" "web.browser"
-    do
-	test -z "$opt" && continue
-	browser="`git config $opt`"
-	test -z "$browser" || break
-    done
-    if test -n "$browser" && ! valid_tool "$browser"; then
-	echo >&2 "git config option $opt set to unknown browser: $browser"
-	echo >&2 "Resetting to default..."
-	unset browser
-    fi
+	for opt in "$conf" "web.browser"
+	do
+		test -z "$opt" && continue
+		browser="`git config $opt`"
+		test -z "$browser" || break
+	done
+	if test -n "$browser" && ! valid_tool "$browser"; then
+		echo >&2 "git config option $opt set to unknown browser: $browser"
+		echo >&2 "Resetting to default..."
+		unset browser
+	fi
 fi
 
 if test -z "$browser" ; then
-    if test -n "$DISPLAY"; then
-	browser_candidates="firefox iceweasel konqueror w3m links lynx dillo"
-	if test "$KDE_FULL_SESSION" = "true"; then
-	    browser_candidates="konqueror $browser_candidates"
+	if test -n "$DISPLAY"; then
+		browser_candidates="firefox iceweasel google-chrome chrome chromium chromium-browser konqueror opera seamonkey iceape w3m elinks links lynx dillo"
+		if test "$KDE_FULL_SESSION" = "true"; then
+			browser_candidates="konqueror $browser_candidates"
+		fi
+	else
+		browser_candidates="w3m elinks links lynx"
 	fi
-    else
-	browser_candidates="w3m links lynx"
-    fi
-    # SECURITYSESSIONID indicates an OS X GUI login session
-    if test -n "$SECURITYSESSIONID"; then
-	browser_candidates="open $browser_candidates"
-    fi
-    # /bin/start indicates MinGW
-    if test -x /bin/start; then
-	browser_candidates="start $browser_candidates"
-    fi
+	# SECURITYSESSIONID indicates an OS X GUI login session
+	if test -n "$SECURITYSESSIONID" \
+		-o "$TERM_PROGRAM" = "Apple_Terminal" ; then
+		browser_candidates="open $browser_candidates"
+	fi
+	# /bin/start indicates MinGW
+	if test -x /bin/start; then
+		browser_candidates="start $browser_candidates"
+	fi
 
-    for i in $browser_candidates; do
-	init_browser_path $i
-	if type "$browser_path" > /dev/null 2>&1; then
-	    browser=$i
-	    break
-	fi
-    done
-    test -z "$browser" && die "No known browser available."
+	for i in $browser_candidates; do
+		init_browser_path $i
+		if type "$browser_path" > /dev/null 2>&1; then
+			browser=$i
+			break
+		fi
+	done
+	test -z "$browser" && die "No known browser available."
 else
-    valid_tool "$browser" || die "Unknown browser '$browser'."
+	valid_tool "$browser" || die "Unknown browser '$browser'."
 
-    init_browser_path "$browser"
+	init_browser_path "$browser"
 
-    if test -z "$browser_cmd" && ! type "$browser_path" > /dev/null 2>&1; then
-	die "The browser $browser is not available as '$browser_path'."
-    fi
+	if test -z "$browser_cmd" && ! type "$browser_path" > /dev/null 2>&1; then
+		die "The browser $browser is not available as '$browser_path'."
+	fi
 fi
 
 case "$browser" in
-    firefox|iceweasel)
+firefox|iceweasel|seamonkey|iceape)
 	# Check version because firefox < 2.0 does not support "-new-tab".
 	vers=$(expr "$($browser_path -version)" : '.* \([0-9][0-9]*\)\..*')
 	NEWTAB='-new-tab'
 	test "$vers" -lt 2 && NEWTAB=''
 	"$browser_path" $NEWTAB "$@" &
 	;;
-    konqueror)
+google-chrome|chrome|chromium|chromium-browser)
+	# No need to specify newTab. It's default in chromium
+	"$browser_path" "$@" &
+	;;
+konqueror)
 	case "$(basename "$browser_path")" in
-	    konqueror)
+	konqueror)
 		# It's simpler to use kfmclient to open a new tab in konqueror.
 		browser_path="$(echo "$browser_path" | sed -e 's/konqueror$/kfmclient/')"
 		type "$browser_path" > /dev/null 2>&1 || die "No '$browser_path' found."
-		eval "$browser_path" newTab "$@"
+		"$browser_path" newTab "$@" &
 		;;
-	    kfmclient)
-		eval "$browser_path" newTab "$@"
+	kfmclient)
+		"$browser_path" newTab "$@" &
 		;;
-	    *)
+	*)
 		"$browser_path" "$@" &
 		;;
 	esac
 	;;
-    w3m|links|lynx|open)
-	eval "$browser_path" "$@"
+w3m|elinks|links|lynx|open)
+	"$browser_path" "$@"
 	;;
-    start)
-        exec "$browser_path" '"web-browse"' "$@"
-        ;;
-    dillo)
+start)
+	exec "$browser_path" '"web-browse"' "$@"
+	;;
+opera|dillo)
 	"$browser_path" "$@" &
 	;;
-    *)
+*)
 	if test -n "$browser_cmd"; then
-	    ( eval $browser_cmd "$@" )
+		( eval "$browser_cmd \"\$@\"" )
 	fi
 	;;
 esac
diff --git a/git.c b/git.c
index 807d875..8788b32 100644
--- a/git.c
+++ b/git.c
@@ -1,26 +1,40 @@
 #include "builtin.h"
-#include "exec_cmd.h"
 #include "cache.h"
+#include "exec_cmd.h"
+#include "help.h"
 #include "quote.h"
 #include "run-command.h"
 
 const char git_usage_string[] =
-	"git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path] [-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]";
+	"git [--version] [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n"
+	"           [-p|--paginate|--no-pager] [--no-replace-objects] [--bare]\n"
+	"           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
+	"           [-c name=value] [--help]\n"
+	"           <command> [<args>]";
 
 const char git_more_info_string[] =
-	"See 'git help COMMAND' for more information on a specific command.";
+	N_("See 'git help <command>' for more information on a specific command.");
 
+static struct startup_info git_startup_info;
 static int use_pager = -1;
 struct pager_config {
 	const char *cmd;
-	int val;
+	int want;
+	char *value;
 };
 
 static int pager_command_config(const char *var, const char *value, void *data)
 {
 	struct pager_config *c = data;
-	if (!prefixcmp(var, "pager.") && !strcmp(var + 6, c->cmd))
-		c->val = git_config_bool(var, value);
+	if (!prefixcmp(var, "pager.") && !strcmp(var + 6, c->cmd)) {
+		int b = git_config_maybe_bool(var, value);
+		if (b >= 0)
+			c->want = b;
+		else {
+			c->want = 1;
+			c->value = xstrdup(value);
+		}
+	}
 	return 0;
 }
 
@@ -29,9 +43,12 @@
 {
 	struct pager_config c;
 	c.cmd = cmd;
-	c.val = -1;
+	c.want = -1;
+	c.value = NULL;
 	git_config(pager_command_config, &c);
-	return c.val;
+	if (c.value)
+		pager_program = c.value;
+	return c.want;
 }
 
 static void commit_pager_choice(void) {
@@ -49,7 +66,7 @@
 
 static int handle_options(const char ***argv, int *argc, int *envchanged)
 {
-	int handled = 0;
+	const char **orig_argv = *argv;
 
 	while (*argc > 0) {
 		const char *cmd = (*argv)[0];
@@ -78,12 +95,23 @@
 		} else if (!strcmp(cmd, "--html-path")) {
 			puts(system_path(GIT_HTML_PATH));
 			exit(0);
+		} else if (!strcmp(cmd, "--man-path")) {
+			puts(system_path(GIT_MAN_PATH));
+			exit(0);
+		} else if (!strcmp(cmd, "--info-path")) {
+			puts(system_path(GIT_INFO_PATH));
+			exit(0);
 		} else if (!strcmp(cmd, "-p") || !strcmp(cmd, "--paginate")) {
 			use_pager = 1;
 		} else if (!strcmp(cmd, "--no-pager")) {
 			use_pager = 0;
 			if (envchanged)
 				*envchanged = 1;
+		} else if (!strcmp(cmd, "--no-replace-objects")) {
+			read_replace_refs = 0;
+			setenv(NO_REPLACE_OBJECTS_ENVIRONMENT, "1", 1);
+			if (envchanged)
+				*envchanged = 1;
 		} else if (!strcmp(cmd, "--git-dir")) {
 			if (*argc < 2) {
 				fprintf(stderr, "No directory given for --git-dir.\n" );
@@ -94,11 +122,24 @@
 				*envchanged = 1;
 			(*argv)++;
 			(*argc)--;
-			handled++;
 		} else if (!prefixcmp(cmd, "--git-dir=")) {
 			setenv(GIT_DIR_ENVIRONMENT, cmd + 10, 1);
 			if (envchanged)
 				*envchanged = 1;
+		} else if (!strcmp(cmd, "--namespace")) {
+			if (*argc < 2) {
+				fprintf(stderr, "No namespace given for --namespace.\n" );
+				usage(git_usage_string);
+			}
+			setenv(GIT_NAMESPACE_ENVIRONMENT, (*argv)[1], 1);
+			if (envchanged)
+				*envchanged = 1;
+			(*argv)++;
+			(*argc)--;
+		} else if (!prefixcmp(cmd, "--namespace=")) {
+			setenv(GIT_NAMESPACE_ENVIRONMENT, cmd + 12, 1);
+			if (envchanged)
+				*envchanged = 1;
 		} else if (!strcmp(cmd, "--work-tree")) {
 			if (*argc < 2) {
 				fprintf(stderr, "No directory given for --work-tree.\n" );
@@ -119,6 +160,14 @@
 			setenv(GIT_DIR_ENVIRONMENT, getcwd(git_dir, sizeof(git_dir)), 0);
 			if (envchanged)
 				*envchanged = 1;
+		} else if (!strcmp(cmd, "-c")) {
+			if (*argc < 2) {
+				fprintf(stderr, "-c expects a configuration string\n" );
+				usage(git_usage_string);
+			}
+			git_config_push_parameter((*argv)[1]);
+			(*argv)++;
+			(*argc)--;
 		} else {
 			fprintf(stderr, "Unknown option: %s\n", cmd);
 			usage(git_usage_string);
@@ -126,9 +175,8 @@
 
 		(*argv)++;
 		(*argc)--;
-		handled++;
 	}
-	return handled;
+	return (*argv) - orig_argv;
 }
 
 static int handle_alias(int *argcp, const char ***argv)
@@ -147,27 +195,29 @@
 	alias_string = alias_lookup(alias_command);
 	if (alias_string) {
 		if (alias_string[0] == '!') {
-			if (*argcp > 1) {
-				struct strbuf buf;
+			const char **alias_argv;
+			int argc = *argcp, i;
 
-				strbuf_init(&buf, PATH_MAX);
-				strbuf_addstr(&buf, alias_string);
-				sq_quote_argv(&buf, (*argv) + 1, PATH_MAX);
-				free(alias_string);
-				alias_string = buf.buf;
-			}
-			trace_printf("trace: alias to shell cmd: %s => %s\n",
-				     alias_command, alias_string + 1);
-			ret = system(alias_string + 1);
-			if (ret >= 0 && WIFEXITED(ret) &&
-			    WEXITSTATUS(ret) != 127)
-				exit(WEXITSTATUS(ret));
-			die("Failed to run '%s' when expanding alias '%s'",
-			    alias_string + 1, alias_command);
+			commit_pager_choice();
+
+			/* build alias_argv */
+			alias_argv = xmalloc(sizeof(*alias_argv) * (argc + 1));
+			alias_argv[0] = alias_string + 1;
+			for (i = 1; i < argc; ++i)
+				alias_argv[i] = (*argv)[i];
+			alias_argv[argc] = NULL;
+
+			ret = run_command_v_opt(alias_argv, RUN_USING_SHELL);
+			if (ret >= 0)   /* normal exit */
+				exit(ret);
+
+			die_errno("While expanding alias '%s': '%s'",
+			    alias_command, alias_string + 1);
 		}
 		count = split_cmdline(alias_string, &new_argv);
 		if (count < 0)
-			die("Bad alias.%s string", alias_command);
+			die("Bad alias.%s string: %s", alias_command,
+			    split_cmdline_strerror(count));
 		option_count = handle_options(&new_argv, &count, &envchanged);
 		if (envchanged)
 			die("alias '%s' changes environment variables\n"
@@ -206,15 +256,14 @@
 	return ret;
 }
 
-const char git_version_string[] = GIT_VERSION;
-
-#define RUN_SETUP	(1<<0)
-#define USE_PAGER	(1<<1)
+#define RUN_SETUP		(1<<0)
+#define RUN_SETUP_GENTLY	(1<<1)
+#define USE_PAGER		(1<<2)
 /*
  * require working tree to be present -- anything uses this needs
  * RUN_SETUP for reading from the configuration file.
  */
-#define NEED_WORK_TREE	(1<<2)
+#define NEED_WORK_TREE		(1<<3)
 
 struct cmd_struct {
 	const char *cmd;
@@ -224,21 +273,32 @@
 
 static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
 {
-	int status;
+	int status, help;
 	struct stat st;
 	const char *prefix;
 
 	prefix = NULL;
-	if (p->option & RUN_SETUP)
-		prefix = setup_git_directory();
+	help = argc == 2 && !strcmp(argv[1], "-h");
+	if (!help) {
+		if (p->option & RUN_SETUP)
+			prefix = setup_git_directory();
+		if (p->option & RUN_SETUP_GENTLY) {
+			int nongit_ok;
+			prefix = setup_git_directory_gently(&nongit_ok);
+		}
 
-	if (use_pager == -1 && p->option & RUN_SETUP)
-		use_pager = check_pager_config(p->cmd);
-	if (use_pager == -1 && p->option & USE_PAGER)
-		use_pager = 1;
+		if (use_pager == -1 && p->option & (RUN_SETUP | RUN_SETUP_GENTLY))
+			use_pager = check_pager_config(p->cmd);
+		if (use_pager == -1 && p->option & USE_PAGER)
+			use_pager = 1;
+
+		if ((p->option & (RUN_SETUP | RUN_SETUP_GENTLY)) &&
+		    startup_info->have_repository) /* get_git_dir() may set up repo, avoid that */
+			trace_repo_setup(prefix);
+	}
 	commit_pager_choice();
 
-	if (p->option & NEED_WORK_TREE)
+	if (!help && p->option & NEED_WORK_TREE)
 		setup_work_tree();
 
 	trace_argv_printf(argv, "trace: built-in: git");
@@ -269,28 +329,29 @@
 	const char *cmd = argv[0];
 	static struct cmd_struct commands[] = {
 		{ "add", cmd_add, RUN_SETUP | NEED_WORK_TREE },
-		{ "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
 		{ "annotate", cmd_annotate, RUN_SETUP },
-		{ "apply", cmd_apply },
+		{ "apply", cmd_apply, RUN_SETUP_GENTLY },
 		{ "archive", cmd_archive },
-		{ "bisect--helper", cmd_bisect__helper, RUN_SETUP | NEED_WORK_TREE },
+		{ "bisect--helper", cmd_bisect__helper, RUN_SETUP },
 		{ "blame", cmd_blame, RUN_SETUP },
 		{ "branch", cmd_branch, RUN_SETUP },
-		{ "bundle", cmd_bundle },
+		{ "bundle", cmd_bundle, RUN_SETUP_GENTLY },
 		{ "cat-file", cmd_cat_file, RUN_SETUP },
+		{ "check-attr", cmd_check_attr, RUN_SETUP },
+		{ "check-ref-format", cmd_check_ref_format },
 		{ "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
 		{ "checkout-index", cmd_checkout_index,
 			RUN_SETUP | NEED_WORK_TREE},
-		{ "check-ref-format", cmd_check_ref_format },
-		{ "check-attr", cmd_check_attr, RUN_SETUP },
 		{ "cherry", cmd_cherry, RUN_SETUP },
 		{ "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
-		{ "clone", cmd_clone },
 		{ "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE },
+		{ "clone", cmd_clone },
+		{ "column", cmd_column, RUN_SETUP_GENTLY },
 		{ "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
 		{ "commit-tree", cmd_commit_tree, RUN_SETUP },
-		{ "config", cmd_config },
+		{ "config", cmd_config, RUN_SETUP_GENTLY },
 		{ "count-objects", cmd_count_objects, RUN_SETUP },
+		{ "credential", cmd_credential, RUN_SETUP_GENTLY },
 		{ "describe", cmd_describe, RUN_SETUP },
 		{ "diff", cmd_diff },
 		{ "diff-files", cmd_diff_files, RUN_SETUP | NEED_WORK_TREE },
@@ -299,7 +360,6 @@
 		{ "fast-export", cmd_fast_export, RUN_SETUP },
 		{ "fetch", cmd_fetch, RUN_SETUP },
 		{ "fetch-pack", cmd_fetch_pack, RUN_SETUP },
-		{ "fetch--tool", cmd_fetch__tool, RUN_SETUP },
 		{ "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP },
 		{ "for-each-ref", cmd_for_each_ref, RUN_SETUP },
 		{ "format-patch", cmd_format_patch, RUN_SETUP },
@@ -307,30 +367,38 @@
 		{ "fsck-objects", cmd_fsck, RUN_SETUP },
 		{ "gc", cmd_gc, RUN_SETUP },
 		{ "get-tar-commit-id", cmd_get_tar_commit_id },
-		{ "grep", cmd_grep, RUN_SETUP | USE_PAGER },
+		{ "grep", cmd_grep, RUN_SETUP_GENTLY },
+		{ "hash-object", cmd_hash_object },
 		{ "help", cmd_help },
-#ifndef NO_CURL
-		{ "http-fetch", cmd_http_fetch, RUN_SETUP },
-#endif
+		{ "index-pack", cmd_index_pack, RUN_SETUP_GENTLY },
 		{ "init", cmd_init_db },
 		{ "init-db", cmd_init_db },
-		{ "log", cmd_log, RUN_SETUP | USE_PAGER },
+		{ "log", cmd_log, RUN_SETUP },
 		{ "ls-files", cmd_ls_files, RUN_SETUP },
+		{ "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
 		{ "ls-tree", cmd_ls_tree, RUN_SETUP },
-		{ "ls-remote", cmd_ls_remote },
 		{ "mailinfo", cmd_mailinfo },
 		{ "mailsplit", cmd_mailsplit },
 		{ "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
 		{ "merge-base", cmd_merge_base, RUN_SETUP },
-		{ "merge-file", cmd_merge_file },
+		{ "merge-file", cmd_merge_file, RUN_SETUP_GENTLY },
+		{ "merge-index", cmd_merge_index, RUN_SETUP },
 		{ "merge-ours", cmd_merge_ours, RUN_SETUP },
 		{ "merge-recursive", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
+		{ "merge-recursive-ours", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
+		{ "merge-recursive-theirs", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
 		{ "merge-subtree", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
+		{ "merge-tree", cmd_merge_tree, RUN_SETUP },
+		{ "mktag", cmd_mktag, RUN_SETUP },
 		{ "mktree", cmd_mktree, RUN_SETUP },
 		{ "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
 		{ "name-rev", cmd_name_rev, RUN_SETUP },
+		{ "notes", cmd_notes, RUN_SETUP },
 		{ "pack-objects", cmd_pack_objects, RUN_SETUP },
-		{ "peek-remote", cmd_ls_remote },
+		{ "pack-redundant", cmd_pack_redundant, RUN_SETUP },
+		{ "pack-refs", cmd_pack_refs, RUN_SETUP },
+		{ "patch-id", cmd_patch_id },
+		{ "peek-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
 		{ "pickaxe", cmd_blame, RUN_SETUP },
 		{ "prune", cmd_prune, RUN_SETUP },
 		{ "prune-packed", cmd_prune_packed, RUN_SETUP },
@@ -339,7 +407,10 @@
 		{ "receive-pack", cmd_receive_pack },
 		{ "reflog", cmd_reflog, RUN_SETUP },
 		{ "remote", cmd_remote, RUN_SETUP },
-		{ "repo-config", cmd_config },
+		{ "remote-ext", cmd_remote_ext },
+		{ "remote-fd", cmd_remote_fd },
+		{ "replace", cmd_replace, RUN_SETUP },
+		{ "repo-config", cmd_repo_config, RUN_SETUP_GENTLY },
 		{ "rerere", cmd_rerere, RUN_SETUP },
 		{ "reset", cmd_reset, RUN_SETUP },
 		{ "rev-list", cmd_rev_list, RUN_SETUP },
@@ -347,25 +418,29 @@
 		{ "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
 		{ "rm", cmd_rm, RUN_SETUP },
 		{ "send-pack", cmd_send_pack, RUN_SETUP },
-		{ "shortlog", cmd_shortlog, USE_PAGER },
+		{ "shortlog", cmd_shortlog, RUN_SETUP_GENTLY | USE_PAGER },
+		{ "show", cmd_show, RUN_SETUP },
 		{ "show-branch", cmd_show_branch, RUN_SETUP },
-		{ "show", cmd_show, RUN_SETUP | USE_PAGER },
+		{ "show-ref", cmd_show_ref, RUN_SETUP },
+		{ "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
 		{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
 		{ "stripspace", cmd_stripspace },
 		{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
 		{ "tag", cmd_tag, RUN_SETUP },
 		{ "tar-tree", cmd_tar_tree },
+		{ "unpack-file", cmd_unpack_file, RUN_SETUP },
 		{ "unpack-objects", cmd_unpack_objects, RUN_SETUP },
 		{ "update-index", cmd_update_index, RUN_SETUP },
 		{ "update-ref", cmd_update_ref, RUN_SETUP },
+		{ "update-server-info", cmd_update_server_info, RUN_SETUP },
 		{ "upload-archive", cmd_upload_archive },
+		{ "upload-archive--writer", cmd_upload_archive_writer },
+		{ "var", cmd_var, RUN_SETUP_GENTLY },
+		{ "verify-pack", cmd_verify_pack },
 		{ "verify-tag", cmd_verify_tag, RUN_SETUP },
 		{ "version", cmd_version },
-		{ "whatchanged", cmd_whatchanged, RUN_SETUP | USE_PAGER },
+		{ "whatchanged", cmd_whatchanged, RUN_SETUP },
 		{ "write-tree", cmd_write_tree, RUN_SETUP },
-		{ "verify-pack", cmd_verify_pack },
-		{ "show-ref", cmd_show_ref, RUN_SETUP },
-		{ "pack-refs", cmd_pack_refs, RUN_SETUP },
 	};
 	int i;
 	static const char ext[] = STRIP_EXTENSION;
@@ -399,6 +474,10 @@
 	const char *tmp;
 	int status;
 
+	if (use_pager == -1)
+		use_pager = check_pager_config(argv[0]);
+	commit_pager_choice();
+
 	strbuf_addf(&cmd, "git-%s", argv[0]);
 
 	/*
@@ -416,13 +495,9 @@
 	 * if we fail because the command is not found, it is
 	 * OK to return. Otherwise, we just pass along the status code.
 	 */
-	status = run_command_v_opt(argv, 0);
-	if (status != -ERR_RUN_COMMAND_EXEC) {
-		if (IS_RUN_COMMAND_ERR(status))
-			die("unable to run '%s'", argv[0]);
-		exit(-status);
-	}
-	errno = ENOENT; /* as if we called execvp */
+	status = run_command_v_opt(argv, RUN_SILENT_EXEC_FAILURE | RUN_CLEAN_ON_EXIT);
+	if (status >= 0 || errno != ENOENT)
+		exit(status);
 
 	argv[0] = tmp;
 
@@ -457,10 +532,14 @@
 {
 	const char *cmd;
 
+	startup_info = &git_startup_info;
+
 	cmd = git_extract_argv0_path(argv[0]);
 	if (!cmd)
 		cmd = "git-help";
 
+	git_setup_gettext();
+
 	/*
 	 * "git-xxxx" is the same as "git xxxx", but we obviously:
 	 *
@@ -482,12 +561,12 @@
 	argv++;
 	argc--;
 	handle_options(&argv, &argc, NULL);
-	commit_pager_choice();
 	if (argc > 0) {
 		if (!prefixcmp(argv[0], "--"))
 			argv[0] += 2;
 	} else {
 		/* The user didn't specify a command; give them help */
+		commit_pager_choice();
 		printf("usage: %s\n\n", git_usage_string);
 		list_common_cmds_help();
 		printf("\n%s\n", git_more_info_string);
@@ -511,7 +590,7 @@
 			break;
 		if (was_alias) {
 			fprintf(stderr, "Expansion of alias '%s' failed; "
-				"'%s' is not a git-command\n",
+				"'%s' is not a git command\n",
 				cmd, argv[0]);
 			exit(1);
 		}
diff --git a/git.spec.in b/git.spec.in
index 4be0834..d61d537 100644
--- a/git.spec.in
+++ b/git.spec.in
@@ -35,6 +35,7 @@
 Requires:	git-arch = %{version}-%{release}
 Requires:	git-email = %{version}-%{release}
 Requires:	gitk = %{version}-%{release}
+Requires:	gitweb = %{version}-%{release}
 Requires:	git-gui = %{version}-%{release}
 Obsoletes:	git <= 1.5.4.2
 
@@ -87,17 +88,26 @@
 %description -n gitk
 Git revision tree visualiser ('gitk')
 
+%package -n gitweb
+Summary:	Git web interface
+Group:          Development/Tools
+Requires:       git = %{version}-%{release}
+%description -n gitweb
+Browsing git repository on the web
+
 %package -n perl-Git
 Summary:        Perl interface to Git
 Group:          Development/Libraries
 Requires:       git = %{version}-%{release}
 Requires:       perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version))
 BuildRequires:  perl(Error)
+BuildRequires:  perl(ExtUtils::MakeMaker)
 
 %description -n perl-Git
 Perl interface to Git
 
 %define path_settings ETC_GITCONFIG=/etc/gitconfig prefix=%{_prefix} mandir=%{_mandir} htmldir=%{_docdir}/%{name}-%{version}
+%{!?python_sitelib: %global python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")}
 
 %prep
 %setup -q
@@ -112,6 +122,7 @@
 make %{_smp_mflags} CFLAGS="$RPM_OPT_FLAGS" DESTDIR=$RPM_BUILD_ROOT \
      %{path_settings} \
      INSTALLDIRS=vendor install %{!?_without_docs: install-doc}
+test ! -d $RPM_BUILD_ROOT%{python_sitelib} || rm -fr $RPM_BUILD_ROOT%{python_sitelib}
 find $RPM_BUILD_ROOT -type f -name .packlist -exec rm -f {} ';'
 find $RPM_BUILD_ROOT -type f -name '*.bs' -empty -exec rm -f {} ';'
 find $RPM_BUILD_ROOT -type f -name perllocal.pod -exec rm -f {} ';'
@@ -124,6 +135,10 @@
 %else
 rm -rf $RPM_BUILD_ROOT%{_mandir}
 %endif
+rm -rf $RPM_BUILD_ROOT%{_datadir}/locale
+
+mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/bash_completion.d
+install -m 644 -T contrib/completion/git-completion.bash $RPM_BUILD_ROOT%{_sysconfdir}/bash_completion.d/git
 
 %clean
 rm -rf $RPM_BUILD_ROOT
@@ -134,6 +149,7 @@
 %doc README COPYING Documentation/*.txt
 %{!?_without_docs: %doc Documentation/*.html Documentation/howto}
 %{!?_without_docs: %doc Documentation/technical}
+%{_sysconfdir}/bash_completion.d
 
 %files svn
 %defattr(-,root,root)
@@ -183,6 +199,14 @@
 %{!?_without_docs: %{_mandir}/man1/*gitk*.1*}
 %{!?_without_docs: %doc Documentation/*gitk*.html }
 
+%files -n gitweb
+%defattr(-,root,root)
+%doc gitweb/README gitweb/INSTALL Documentation/*gitweb*.txt
+%{_datadir}/gitweb
+%{!?_without_docs: %{_mandir}/man1/*gitweb*.1*}
+%{!?_without_docs: %{_mandir}/man5/*gitweb*.5*}
+%{!?_without_docs: %doc Documentation/*gitweb*.html }
+
 %files -n perl-Git -f perl-files
 %defattr(-,root,root)
 
@@ -190,6 +214,21 @@
 # No files for you!
 
 %changelog
+* Sun Sep 18 2011 Jakub Narebski <jnareb@gmail.com>
+- Add gitweb manpages to 'gitweb' subpackage
+
+* Wed Jun 30 2010 Junio C Hamano <gitster@pobox.com>
+- Add 'gitweb' subpackage.
+
+* Fri Mar 26 2010 Ian Ward Comfort <icomfort@stanford.edu>
+- Ship bash completion support from contrib/ in the core package.
+
+* Sun Jan 31 2010 Junio C Hamano <gitster@pobox.com>
+- Do not use %define inside %{!?...} construct.
+
+* Sat Jan 30 2010 Junio C Hamano <gitster@pobox.com>
+- We don't ship Python bits until a real foreign scm interface comes.
+
 * Mon Feb 04 2009 David J. Mellor <dmellor@whistlingcat.com>
 - fixed broken git help -w after renaming the git-core package to git.
 
@@ -233,7 +272,7 @@
 * Tue Mar 27 2007 Eygene Ryabinkin <rea-git@codelabs.ru>
 - Added the git-p4 package: Perforce import stuff.
 
-* Mon Feb 13 2007 Nicolas Pitre <nico@cam.org>
+* Mon Feb 13 2007 Nicolas Pitre <nico@fluxnic.net>
 - Update core package description (Git isn't as stupid as it used to be)
 
 * Mon Feb 12 2007 Junio C Hamano <junkio@cox.net>
diff --git a/git_remote_helpers/.gitignore b/git_remote_helpers/.gitignore
new file mode 100644
index 0000000..2247d5f
--- /dev/null
+++ b/git_remote_helpers/.gitignore
@@ -0,0 +1,2 @@
+/build
+/dist
diff --git a/git_remote_helpers/Makefile b/git_remote_helpers/Makefile
new file mode 100644
index 0000000..74b05dc
--- /dev/null
+++ b/git_remote_helpers/Makefile
@@ -0,0 +1,39 @@
+#
+# Makefile for the git_remote_helpers python support modules
+#
+pysetupfile:=setup.py
+
+# Shell quote (do not use $(call) to accommodate ancient setups);
+DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
+
+ifndef PYTHON_PATH
+	ifeq ($(uname_S),FreeBSD)
+		PYTHON_PATH = /usr/local/bin/python
+	else
+		PYTHON_PATH = /usr/bin/python
+	endif
+endif
+ifndef prefix
+	prefix = $(HOME)
+endif
+ifndef V
+	QUIET = @
+	QUIETSETUP = --quiet
+endif
+
+PYLIBDIR=$(shell $(PYTHON_PATH) -c \
+	 "import sys; \
+	 print 'lib/python%i.%i/site-packages' % sys.version_info[:2]")
+
+all: $(pysetupfile)
+	$(QUIET)$(PYTHON_PATH) $(pysetupfile) $(QUIETSETUP) build
+
+install: $(pysetupfile)
+	$(PYTHON_PATH) $(pysetupfile) install --prefix $(DESTDIR_SQ)$(prefix)
+
+instlibdir: $(pysetupfile)
+	@echo "$(DESTDIR_SQ)$(prefix)/$(PYLIBDIR)"
+
+clean:
+	$(QUIET)$(PYTHON_PATH) $(pysetupfile) $(QUIETSETUP) clean -a
+	$(RM) *.pyo *.pyc
diff --git a/git_remote_helpers/__init__.py b/git_remote_helpers/__init__.py
new file mode 100644
index 0000000..00f69cb
--- /dev/null
+++ b/git_remote_helpers/__init__.py
@@ -0,0 +1,16 @@
+#!/usr/bin/env python
+
+"""Support library package for git remote helpers.
+
+Git remote helpers are helper commands that interfaces with a non-git
+repository to provide automatic import of non-git history into a Git
+repository.
+
+This package provides the support library needed by these helpers..
+The following modules are included:
+
+- git.git - Interaction with Git repositories
+
+- util - General utility functionality use by the other modules in
+         this package, and also used directly by the helpers.
+"""
diff --git a/git_remote_helpers/git/__init__.py b/git_remote_helpers/git/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/git_remote_helpers/git/__init__.py
diff --git a/git_remote_helpers/git/exporter.py b/git_remote_helpers/git/exporter.py
new file mode 100644
index 0000000..9ee5f96
--- /dev/null
+++ b/git_remote_helpers/git/exporter.py
@@ -0,0 +1,58 @@
+import os
+import subprocess
+import sys
+
+from git_remote_helpers.util import check_call
+
+
+class GitExporter(object):
+    """An exporter for testgit repositories.
+
+    The exporter simply delegates to git fast-export.
+    """
+
+    def __init__(self, repo):
+        """Creates a new exporter for the specified repo.
+        """
+
+        self.repo = repo
+
+    def export_repo(self, base, refs=None):
+        """Exports a fast-export stream for the given directory.
+
+        Simply delegates to git fast-epxort and pipes it through sed
+        to make the refs show up under the prefix rather than the
+        default refs/heads. This is to demonstrate how the export
+        data can be stored under it's own ref (using the refspec
+        capability).
+
+        If None, refs defaults to ["HEAD"].
+        """
+
+        if not refs:
+            refs = ["HEAD"]
+
+        dirname = self.repo.get_base_path(base)
+        path = os.path.abspath(os.path.join(dirname, 'testgit.marks'))
+
+        if not os.path.exists(dirname):
+            os.makedirs(dirname)
+
+        print "feature relative-marks"
+        if os.path.exists(os.path.join(dirname, 'git.marks')):
+            print "feature import-marks=%s/git.marks" % self.repo.hash
+        print "feature export-marks=%s/git.marks" % self.repo.hash
+        sys.stdout.flush()
+
+        args = ["git", "--git-dir=" + self.repo.gitpath, "fast-export", "--export-marks=" + path]
+
+        if os.path.exists(path):
+            args.append("--import-marks=" + path)
+
+        args.extend(refs)
+
+        p1 = subprocess.Popen(args, stdout=subprocess.PIPE)
+
+        args = ["sed", "s_refs/heads/_" + self.repo.prefix + "_g"]
+
+        check_call(args, stdin=p1.stdout)
diff --git a/git_remote_helpers/git/git.py b/git_remote_helpers/git/git.py
new file mode 100644
index 0000000..007a1bf
--- /dev/null
+++ b/git_remote_helpers/git/git.py
@@ -0,0 +1,678 @@
+#!/usr/bin/env python
+
+"""Functionality for interacting with Git repositories.
+
+This module provides classes for interfacing with a Git repository.
+"""
+
+import os
+import re
+import time
+from binascii import hexlify
+from cStringIO import StringIO
+import unittest
+
+from git_remote_helpers.util import debug, error, die, start_command, run_command
+
+
+def get_git_dir ():
+    """Return the path to the GIT_DIR for this repo."""
+    args = ("git", "rev-parse", "--git-dir")
+    exit_code, output, errors = run_command(args)
+    if exit_code:
+        die("Failed to retrieve git dir")
+    assert not errors
+    return output.strip()
+
+
+def parse_git_config ():
+    """Return a dict containing the parsed version of 'git config -l'."""
+    exit_code, output, errors = run_command(("git", "config", "-z", "-l"))
+    if exit_code:
+        die("Failed to retrieve git configuration")
+    assert not errors
+    return dict([e.split('\n', 1) for e in output.split("\0") if e])
+
+
+def git_config_bool (value):
+    """Convert the given git config string value to True or False.
+
+    Raise ValueError if the given string was not recognized as a
+    boolean value.
+
+    """
+    norm_value = str(value).strip().lower()
+    if norm_value in ("true", "1", "yes", "on", ""):
+        return True
+    if norm_value in ("false", "0", "no", "off", "none"):
+        return False
+    raise ValueError("Failed to parse '%s' into a boolean value" % (value))
+
+
+def valid_git_ref (ref_name):
+    """Return True iff the given ref name is a valid git ref name."""
+    # The following is a reimplementation of the git check-ref-format
+    # command.  The rules were derived from the git check-ref-format(1)
+    # manual page.  This code should be replaced by a call to
+    # check_refname_format() in the git library, when such is available.
+    if ref_name.endswith('/') or \
+       ref_name.startswith('.') or \
+       ref_name.count('/.') or \
+       ref_name.count('..') or \
+       ref_name.endswith('.lock'):
+        return False
+    for c in ref_name:
+        if ord(c) < 0x20 or ord(c) == 0x7f or c in " ~^:?*[":
+            return False
+    return True
+
+
+class GitObjectFetcher(object):
+
+    """Provide parsed access to 'git cat-file --batch'.
+
+    This provides a read-only interface to the Git object database.
+
+    """
+
+    def __init__ (self):
+        """Initiate a 'git cat-file --batch' session."""
+        self.queue = []  # List of object names to be submitted
+        self.in_transit = None  # Object name currently in transit
+
+        # 'git cat-file --batch' produces binary output which is likely
+        # to be corrupted by the default "rU"-mode pipe opened by
+        # start_command.  (Mode == "rU" does universal new-line
+        # conversion, which mangles carriage returns.) Therefore, we
+        # open an explicitly binary-safe pipe for transferring the
+        # output from 'git cat-file --batch'.
+        pipe_r_fd, pipe_w_fd = os.pipe()
+        pipe_r = os.fdopen(pipe_r_fd, "rb")
+        pipe_w = os.fdopen(pipe_w_fd, "wb")
+        self.proc = start_command(("git", "cat-file", "--batch"),
+                                  stdout = pipe_w)
+        self.f = pipe_r
+
+    def __del__ (self):
+        """Verify completed communication with 'git cat-file --batch'."""
+        assert not self.queue
+        assert self.in_transit is None
+        self.proc.stdin.close()
+        assert self.proc.wait() == 0  # Zero exit code
+        assert self.f.read() == ""  # No remaining output
+
+    def _submit_next_object (self):
+        """Submit queue items to the 'git cat-file --batch' process.
+
+        If there are items in the queue, and there is currently no item
+        currently in 'transit', then pop the first item off the queue,
+        and submit it.
+
+        """
+        if self.queue and self.in_transit is None:
+            self.in_transit = self.queue.pop(0)
+            print >> self.proc.stdin, self.in_transit[0]
+
+    def push (self, obj, callback):
+        """Push the given object name onto the queue.
+
+        The given callback function will at some point in the future
+        be called exactly once with the following arguments:
+        - self - this GitObjectFetcher instance
+        - obj  - the object name provided to push()
+        - sha1 - the SHA1 of the object, if 'None' obj is missing
+        - t    - the type of the object (tag/commit/tree/blob)
+        - size - the size of the object in bytes
+        - data - the object contents
+
+        """
+        self.queue.append((obj, callback))
+        self._submit_next_object()  # (Re)start queue processing
+
+    def process_next_entry (self):
+        """Read the next entry off the queue and invoke callback."""
+        obj, cb = self.in_transit
+        self.in_transit = None
+        header = self.f.readline()
+        if header == "%s missing\n" % (obj):
+            cb(self, obj, None, None, None, None)
+            return
+        sha1, t, size = header.split(" ")
+        assert len(sha1) == 40
+        assert t in ("tag", "commit", "tree", "blob")
+        assert size.endswith("\n")
+        size = int(size.strip())
+        data = self.f.read(size)
+        assert self.f.read(1) == "\n"
+        cb(self, obj, sha1, t, size, data)
+        self._submit_next_object()
+
+    def process (self):
+        """Process the current queue until empty."""
+        while self.in_transit is not None:
+            self.process_next_entry()
+
+    # High-level convenience methods:
+
+    def get_sha1 (self, objspec):
+        """Return the SHA1 of the object specified by 'objspec'.
+
+        Return None if 'objspec' does not specify an existing object.
+
+        """
+        class _ObjHandler(object):
+            """Helper class for getting the returned SHA1."""
+            def __init__ (self, parser):
+                self.parser = parser
+                self.sha1 = None
+
+            def __call__ (self, parser, obj, sha1, t, size, data):
+                # FIXME: Many unused arguments. Could this be cheaper?
+                assert parser == self.parser
+                self.sha1 = sha1
+
+        handler = _ObjHandler(self)
+        self.push(objspec, handler)
+        self.process()
+        return handler.sha1
+
+    def open_obj (self, objspec):
+        """Return a file object wrapping the contents of a named object.
+
+        The caller is responsible for calling .close() on the returned
+        file object.
+
+        Raise KeyError if 'objspec' does not exist in the repo.
+
+        """
+        class _ObjHandler(object):
+            """Helper class for parsing the returned git object."""
+            def __init__ (self, parser):
+                """Set up helper."""
+                self.parser = parser
+                self.contents = StringIO()
+                self.err = None
+
+            def __call__ (self, parser, obj, sha1, t, size, data):
+                """Git object callback (see GitObjectFetcher documentation)."""
+                assert parser == self.parser
+                if not sha1:  # Missing object
+                    self.err = "Missing object '%s'" % obj
+                else:
+                    assert size == len(data)
+                    self.contents.write(data)
+
+        handler = _ObjHandler(self)
+        self.push(objspec, handler)
+        self.process()
+        if handler.err:
+            raise KeyError(handler.err)
+        handler.contents.seek(0)
+        return handler.contents
+
+    def walk_tree (self, tree_objspec, callback, prefix = ""):
+        """Recursively walk the given Git tree object.
+
+        Recursively walk all subtrees of the given tree object, and
+        invoke the given callback passing three arguments:
+        (path, mode, data) with the path, permission bits, and contents
+        of all the blobs found in the entire tree structure.
+
+        """
+        class _ObjHandler(object):
+            """Helper class for walking a git tree structure."""
+            def __init__ (self, parser, cb, path, mode = None):
+                """Set up helper."""
+                self.parser = parser
+                self.cb = cb
+                self.path = path
+                self.mode = mode
+                self.err = None
+
+            def parse_tree (self, treedata):
+                """Parse tree object data, yield tree entries.
+
+                Each tree entry is a 3-tuple (mode, sha1, path)
+
+                self.path is prepended to all paths yielded
+                from this method.
+
+                """
+                while treedata:
+                    mode = int(treedata[:6], 10)
+                    # Turn 100xxx into xxx
+                    if mode > 100000:
+                        mode -= 100000
+                    assert treedata[6] == " "
+                    i = treedata.find("\0", 7)
+                    assert i > 0
+                    path = treedata[7:i]
+                    sha1 = hexlify(treedata[i + 1: i + 21])
+                    yield (mode, sha1, self.path + path)
+                    treedata = treedata[i + 21:]
+
+            def __call__ (self, parser, obj, sha1, t, size, data):
+                """Git object callback (see GitObjectFetcher documentation)."""
+                assert parser == self.parser
+                if not sha1:  # Missing object
+                    self.err = "Missing object '%s'" % (obj)
+                    return
+                assert size == len(data)
+                if t == "tree":
+                    if self.path:
+                        self.path += "/"
+                    # Recurse into all blobs and subtrees
+                    for m, s, p in self.parse_tree(data):
+                        parser.push(s,
+                                    self.__class__(self.parser, self.cb, p, m))
+                elif t == "blob":
+                    self.cb(self.path, self.mode, data)
+                else:
+                    raise ValueError("Unknown object type '%s'" % (t))
+
+        self.push(tree_objspec, _ObjHandler(self, callback, prefix))
+        self.process()
+
+
+class GitRefMap(object):
+
+    """Map Git ref names to the Git object names they currently point to.
+
+    Behaves like a dictionary of Git ref names -> Git object names.
+
+    """
+
+    def __init__ (self, obj_fetcher):
+        """Create a new Git ref -> object map."""
+        self.obj_fetcher = obj_fetcher
+        self._cache = {}  # dict: refname -> objname
+
+    def _load (self, ref):
+        """Retrieve the object currently bound to the given ref.
+
+        The name of the object pointed to by the given ref is stored
+        into this mapping, and also returned.
+
+        """
+        if ref not in self._cache:
+            self._cache[ref] = self.obj_fetcher.get_sha1(ref)
+        return self._cache[ref]
+
+    def __contains__ (self, refname):
+        """Return True if the given refname is present in this cache."""
+        return bool(self._load(refname))
+
+    def __getitem__ (self, refname):
+        """Return the git object name pointed to by the given refname."""
+        commit = self._load(refname)
+        if commit is None:
+            raise KeyError("Unknown ref '%s'" % (refname))
+        return commit
+
+    def get (self, refname, default = None):
+        """Return the git object name pointed to by the given refname."""
+        commit = self._load(refname)
+        if commit is None:
+            return default
+        return commit
+
+
+class GitFICommit(object):
+
+    """Encapsulate the data in a Git fast-import commit command."""
+
+    SHA1RE = re.compile(r'^[0-9a-f]{40}$')
+
+    @classmethod
+    def parse_mode (cls, mode):
+        """Verify the given git file mode, and return it as a string."""
+        assert mode in (644, 755, 100644, 100755, 120000)
+        return "%i" % (mode)
+
+    @classmethod
+    def parse_objname (cls, objname):
+        """Return the given object name (or mark number) as a string."""
+        if isinstance(objname, int):  # Object name is a mark number
+            assert objname > 0
+            return ":%i" % (objname)
+
+        # No existence check is done, only checks for valid format
+        assert cls.SHA1RE.match(objname)  # Object name is valid SHA1
+        return objname
+
+    @classmethod
+    def quote_path (cls, path):
+        """Return a quoted version of the given path."""
+        path = path.replace("\\", "\\\\")
+        path = path.replace("\n", "\\n")
+        path = path.replace('"', '\\"')
+        return '"%s"' % (path)
+
+    @classmethod
+    def parse_path (cls, path):
+        """Verify that the given path is valid, and quote it, if needed."""
+        assert not isinstance(path, int)  # Cannot be a mark number
+
+        # These checks verify the rules on the fast-import man page
+        assert not path.count("//")
+        assert not path.endswith("/")
+        assert not path.startswith("/")
+        assert not path.count("/./")
+        assert not path.count("/../")
+        assert not path.endswith("/.")
+        assert not path.endswith("/..")
+        assert not path.startswith("./")
+        assert not path.startswith("../")
+
+        if path.count('"') + path.count('\n') + path.count('\\'):
+            return cls.quote_path(path)
+        return path
+
+    def __init__ (self, name, email, timestamp, timezone, message):
+        """Create a new Git fast-import commit, with the given metadata."""
+        self.name = name
+        self.email = email
+        self.timestamp = timestamp
+        self.timezone = timezone
+        self.message = message
+        self.pathops = []  # List of path operations in this commit
+
+    def modify (self, mode, blobname, path):
+        """Add a file modification to this Git fast-import commit."""
+        self.pathops.append(("M",
+                             self.parse_mode(mode),
+                             self.parse_objname(blobname),
+                             self.parse_path(path)))
+
+    def delete (self, path):
+        """Add a file deletion to this Git fast-import commit."""
+        self.pathops.append(("D", self.parse_path(path)))
+
+    def copy (self, path, newpath):
+        """Add a file copy to this Git fast-import commit."""
+        self.pathops.append(("C",
+                             self.parse_path(path),
+                             self.parse_path(newpath)))
+
+    def rename (self, path, newpath):
+        """Add a file rename to this Git fast-import commit."""
+        self.pathops.append(("R",
+                             self.parse_path(path),
+                             self.parse_path(newpath)))
+
+    def note (self, blobname, commit):
+        """Add a note object to this Git fast-import commit."""
+        self.pathops.append(("N",
+                             self.parse_objname(blobname),
+                             self.parse_objname(commit)))
+
+    def deleteall (self):
+        """Delete all files in this Git fast-import commit."""
+        self.pathops.append("deleteall")
+
+
+class TestGitFICommit(unittest.TestCase):
+
+    """GitFICommit selftests."""
+
+    def test_basic (self):
+        """GitFICommit basic selftests."""
+
+        def expect_fail (method, data):
+            """Verify that the method(data) raises an AssertionError."""
+            try:
+                method(data)
+            except AssertionError:
+                return
+            raise AssertionError("Failed test for invalid data '%s(%s)'" %
+                                 (method.__name__, repr(data)))
+
+    def test_parse_mode (self):
+        """GitFICommit.parse_mode() selftests."""
+        self.assertEqual(GitFICommit.parse_mode(644), "644")
+        self.assertEqual(GitFICommit.parse_mode(755), "755")
+        self.assertEqual(GitFICommit.parse_mode(100644), "100644")
+        self.assertEqual(GitFICommit.parse_mode(100755), "100755")
+        self.assertEqual(GitFICommit.parse_mode(120000), "120000")
+        self.assertRaises(AssertionError, GitFICommit.parse_mode, 0)
+        self.assertRaises(AssertionError, GitFICommit.parse_mode, 123)
+        self.assertRaises(AssertionError, GitFICommit.parse_mode, 600)
+        self.assertRaises(AssertionError, GitFICommit.parse_mode, "644")
+        self.assertRaises(AssertionError, GitFICommit.parse_mode, "abc")
+
+    def test_parse_objname (self):
+        """GitFICommit.parse_objname() selftests."""
+        self.assertEqual(GitFICommit.parse_objname(1), ":1")
+        self.assertRaises(AssertionError, GitFICommit.parse_objname, 0)
+        self.assertRaises(AssertionError, GitFICommit.parse_objname, -1)
+        self.assertEqual(GitFICommit.parse_objname("0123456789" * 4),
+                         "0123456789" * 4)
+        self.assertEqual(GitFICommit.parse_objname("2468abcdef" * 4),
+                         "2468abcdef" * 4)
+        self.assertRaises(AssertionError, GitFICommit.parse_objname,
+                          "abcdefghij" * 4)
+
+    def test_parse_path (self):
+        """GitFICommit.parse_path() selftests."""
+        self.assertEqual(GitFICommit.parse_path("foo/bar"), "foo/bar")
+        self.assertEqual(GitFICommit.parse_path("path/with\n and \" in it"),
+                         '"path/with\\n and \\" in it"')
+        self.assertRaises(AssertionError, GitFICommit.parse_path, 1)
+        self.assertRaises(AssertionError, GitFICommit.parse_path, 0)
+        self.assertRaises(AssertionError, GitFICommit.parse_path, -1)
+        self.assertRaises(AssertionError, GitFICommit.parse_path, "foo//bar")
+        self.assertRaises(AssertionError, GitFICommit.parse_path, "foo/bar/")
+        self.assertRaises(AssertionError, GitFICommit.parse_path, "/foo/bar")
+        self.assertRaises(AssertionError, GitFICommit.parse_path, "foo/./bar")
+        self.assertRaises(AssertionError, GitFICommit.parse_path, "foo/../bar")
+        self.assertRaises(AssertionError, GitFICommit.parse_path, "foo/bar/.")
+        self.assertRaises(AssertionError, GitFICommit.parse_path, "foo/bar/..")
+        self.assertRaises(AssertionError, GitFICommit.parse_path, "./foo/bar")
+        self.assertRaises(AssertionError, GitFICommit.parse_path, "../foo/bar")
+
+
+class GitFastImport(object):
+
+    """Encapsulate communication with git fast-import."""
+
+    def __init__ (self, f, obj_fetcher, last_mark = 0):
+        """Set up self to communicate with a fast-import process through f."""
+        self.f = f  # File object where fast-import stream is written
+        self.obj_fetcher = obj_fetcher  # GitObjectFetcher instance
+        self.next_mark = last_mark + 1  # Next mark number
+        self.refs = set()  # Keep track of the refnames we've seen
+
+    def comment (self, s):
+        """Write the given comment in the fast-import stream."""
+        assert "\n" not in s, "Malformed comment: '%s'" % (s)
+        self.f.write("# %s\n" % (s))
+
+    def commit (self, ref, commitdata):
+        """Make a commit on the given ref, with the given GitFICommit.
+
+        Return the mark number identifying this commit.
+
+        """
+        self.f.write("""\
+commit %(ref)s
+mark :%(mark)i
+committer %(name)s <%(email)s> %(timestamp)i %(timezone)s
+data %(msgLength)i
+%(msg)s
+""" % {
+    'ref': ref,
+    'mark': self.next_mark,
+    'name': commitdata.name,
+    'email': commitdata.email,
+    'timestamp': commitdata.timestamp,
+    'timezone': commitdata.timezone,
+    'msgLength': len(commitdata.message),
+    'msg': commitdata.message,
+})
+
+        if ref not in self.refs:
+            self.refs.add(ref)
+            parent = ref + "^0"
+            if self.obj_fetcher.get_sha1(parent):
+                self.f.write("from %s\n" % (parent))
+
+        for op in commitdata.pathops:
+            self.f.write(" ".join(op))
+            self.f.write("\n")
+        self.f.write("\n")
+        retval = self.next_mark
+        self.next_mark += 1
+        return retval
+
+    def blob (self, data):
+        """Import the given blob.
+
+        Return the mark number identifying this blob.
+
+        """
+        self.f.write("blob\nmark :%i\ndata %i\n%s\n" %
+                     (self.next_mark, len(data), data))
+        retval = self.next_mark
+        self.next_mark += 1
+        return retval
+
+    def reset (self, ref, objname):
+        """Reset the given ref to point at the given Git object."""
+        self.f.write("reset %s\nfrom %s\n\n" %
+                     (ref, GitFICommit.parse_objname(objname)))
+        if ref not in self.refs:
+            self.refs.add(ref)
+
+
+class GitNotes(object):
+
+    """Encapsulate access to Git notes.
+
+    Simulates a dictionary of object name (SHA1) -> Git note mappings.
+
+    """
+
+    def __init__ (self, notes_ref, obj_fetcher):
+        """Create a new Git notes interface, bound to the given notes ref."""
+        self.notes_ref = notes_ref
+        self.obj_fetcher = obj_fetcher  # Used to get objects from repo
+        self.imports = []  # list: (objname, note data blob name) tuples
+
+    def __del__ (self):
+        """Verify that self.commit_notes() was called before destruction."""
+        if self.imports:
+            error("Missing call to self.commit_notes().")
+            error("%i notes are not committed!", len(self.imports))
+
+    def _load (self, objname):
+        """Return the note data associated with the given git object.
+
+        The note data is returned in string form. If no note is found
+        for the given object, None is returned.
+
+        """
+        try:
+            f = self.obj_fetcher.open_obj("%s:%s" % (self.notes_ref, objname))
+            ret = f.read()
+            f.close()
+        except KeyError:
+            ret = None
+        return ret
+
+    def __getitem__ (self, objname):
+        """Return the note contents associated with the given object.
+
+        Raise KeyError if given object has no associated note.
+
+        """
+        blobdata = self._load(objname)
+        if blobdata is None:
+            raise KeyError("Object '%s' has no note" % (objname))
+        return blobdata
+
+    def get (self, objname, default = None):
+        """Return the note contents associated with the given object.
+
+        Return given default if given object has no associated note.
+
+        """
+        blobdata = self._load(objname)
+        if blobdata is None:
+            return default
+        return blobdata
+
+    def import_note (self, objname, data, gfi):
+        """Tell git fast-import to store data as a note for objname.
+
+        This method uses the given GitFastImport object to create a
+        blob containing the given note data.  Also an entry mapping the
+        given object name to the created blob is stored until
+        commit_notes() is called.
+
+        Note that this method only works if it is later followed by a
+        call to self.commit_notes() (which produces the note commit
+        that refers to the blob produced here).
+
+        """
+        if not data.endswith("\n"):
+            data += "\n"
+        gfi.comment("Importing note for object %s" % (objname))
+        mark = gfi.blob(data)
+        self.imports.append((objname, mark))
+
+    def commit_notes (self, gfi, author, message):
+        """Produce a git fast-import note commit for the imported notes.
+
+        This method uses the given GitFastImport object to create a
+        commit on the notes ref, introducing the notes previously
+        submitted to import_note().
+
+        """
+        if not self.imports:
+            return
+        commitdata = GitFICommit(author[0], author[1],
+                                 time.time(), "0000", message)
+        for objname, blobname in self.imports:
+            assert isinstance(objname, int) and objname > 0
+            assert isinstance(blobname, int) and blobname > 0
+            commitdata.note(blobname, objname)
+        gfi.commit(self.notes_ref, commitdata)
+        self.imports = []
+
+
+class GitCachedNotes(GitNotes):
+
+    """Encapsulate access to Git notes (cached version).
+
+    Only use this class if no caching is done at a higher level.
+
+    Simulates a dictionary of object name (SHA1) -> Git note mappings.
+
+    """
+
+    def __init__ (self, notes_ref, obj_fetcher):
+        """Set up a caching wrapper around GitNotes."""
+        GitNotes.__init__(self, notes_ref, obj_fetcher)
+        self._cache = {}  # Cache: object name -> note data
+
+    def __del__ (self):
+        """Verify that GitNotes' destructor is called."""
+        GitNotes.__del__(self)
+
+    def _load (self, objname):
+        """Extend GitNotes._load() with a local objname -> note cache."""
+        if objname not in self._cache:
+            self._cache[objname] = GitNotes._load(self, objname)
+        return self._cache[objname]
+
+    def import_note (self, objname, data, gfi):
+        """Extend GitNotes.import_note() with a local objname -> note cache."""
+        if not data.endswith("\n"):
+            data += "\n"
+        assert objname not in self._cache
+        self._cache[objname] = data
+        GitNotes.import_note(self, objname, data, gfi)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/git_remote_helpers/git/importer.py b/git_remote_helpers/git/importer.py
new file mode 100644
index 0000000..5c6b595
--- /dev/null
+++ b/git_remote_helpers/git/importer.py
@@ -0,0 +1,66 @@
+import os
+import subprocess
+
+from git_remote_helpers.util import check_call, check_output
+
+
+class GitImporter(object):
+    """An importer for testgit repositories.
+
+    This importer simply delegates to git fast-import.
+    """
+
+    def __init__(self, repo):
+        """Creates a new importer for the specified repo.
+        """
+
+        self.repo = repo
+
+    def get_refs(self, gitdir):
+        """Returns a dictionary with refs.
+        """
+        args = ["git", "--git-dir=" + gitdir, "for-each-ref", "refs/heads"]
+        lines = check_output(args).strip().split('\n')
+        refs = {}
+        for line in lines:
+            value, name = line.split(' ')
+            name = name.strip('commit\t')
+            refs[name] = value
+        return refs
+
+    def do_import(self, base):
+        """Imports a fast-import stream to the given directory.
+
+        Simply delegates to git fast-import.
+        """
+
+        dirname = self.repo.get_base_path(base)
+        if self.repo.local:
+            gitdir = self.repo.gitpath
+        else:
+            gitdir = os.path.abspath(os.path.join(dirname, '.git'))
+        path = os.path.abspath(os.path.join(dirname, 'git.marks'))
+
+        if not os.path.exists(dirname):
+            os.makedirs(dirname)
+
+        refs_before = self.get_refs(gitdir)
+
+        args = ["git", "--git-dir=" + gitdir, "fast-import", "--quiet", "--export-marks=" + path]
+
+        if os.path.exists(path):
+            args.append("--import-marks=" + path)
+
+        check_call(args)
+
+        refs_after = self.get_refs(gitdir)
+
+        changed = {}
+
+        for name, value in refs_after.iteritems():
+            if refs_before.get(name) == value:
+                continue
+
+            changed[name] = value
+
+        return changed
diff --git a/git_remote_helpers/git/non_local.py b/git_remote_helpers/git/non_local.py
new file mode 100644
index 0000000..e700250
--- /dev/null
+++ b/git_remote_helpers/git/non_local.py
@@ -0,0 +1,61 @@
+import os
+import subprocess
+
+from git_remote_helpers.util import check_call, die, warn
+
+
+class NonLocalGit(object):
+    """Handler to interact with non-local repos.
+    """
+
+    def __init__(self, repo):
+        """Creates a new non-local handler for the specified repo.
+        """
+
+        self.repo = repo
+
+    def clone(self, base):
+        """Clones the non-local repo to base.
+
+        Does nothing if a clone already exists.
+        """
+
+        path = os.path.join(self.repo.get_base_path(base), '.git')
+
+        # already cloned
+        if os.path.exists(path):
+            return path
+
+        os.makedirs(path)
+        args = ["git", "clone", "--bare", "--quiet", self.repo.gitpath, path]
+
+        check_call(args)
+
+        return path
+
+    def update(self, base):
+        """Updates checkout of the non-local repo in base.
+        """
+
+        path = os.path.join(self.repo.get_base_path(base), '.git')
+
+        if not os.path.exists(path):
+            die("could not find repo at %s", path)
+
+        args = ["git", "--git-dir=" + path, "fetch", "--quiet", self.repo.gitpath]
+        check_call(args)
+
+        args = ["git", "--git-dir=" + path, "update-ref", "refs/heads/master", "FETCH_HEAD"]
+        child = check_call(args)
+
+    def push(self, base):
+        """Pushes from the non-local repo to base.
+        """
+
+        path = os.path.join(self.repo.get_base_path(base), '.git')
+
+        if not os.path.exists(path):
+            die("could not find repo at %s", path)
+
+        args = ["git", "--git-dir=" + path, "push", "--quiet", self.repo.gitpath, "--all"]
+        child = check_call(args)
diff --git a/git_remote_helpers/git/repo.py b/git_remote_helpers/git/repo.py
new file mode 100644
index 0000000..acbf8d7
--- /dev/null
+++ b/git_remote_helpers/git/repo.py
@@ -0,0 +1,76 @@
+import os
+import subprocess
+
+from git_remote_helpers.util import check_call
+
+
+def sanitize(rev, sep='\t'):
+    """Converts a for-each-ref line to a name/value pair.
+    """
+
+    splitrev = rev.split(sep)
+    branchval = splitrev[0]
+    branchname = splitrev[1].strip()
+    if branchname.startswith("refs/heads/"):
+        branchname = branchname[11:]
+
+    return branchname, branchval
+
+def is_remote(url):
+    """Checks whether the specified value is a remote url.
+    """
+
+    prefixes = ["http", "file", "git"]
+
+    for prefix in prefixes:
+        if url.startswith(prefix):
+            return True
+    return False
+
+class GitRepo(object):
+    """Repo object representing a repo.
+    """
+
+    def __init__(self, path):
+        """Initializes a new repo at the given path.
+        """
+
+        self.path = path
+        self.head = None
+        self.revmap = {}
+        self.local = not is_remote(self.path)
+
+        if(self.path.endswith('.git')):
+            self.gitpath = self.path
+        else:
+            self.gitpath = os.path.join(self.path, '.git')
+
+        if self.local and not os.path.exists(self.gitpath):
+            os.makedirs(self.gitpath)
+
+    def get_revs(self):
+        """Fetches all revs from the remote.
+        """
+
+        args = ["git", "ls-remote", self.gitpath]
+        path = ".cached_revs"
+        ofile = open(path, "w")
+
+        check_call(args, stdout=ofile)
+        output = open(path).readlines()
+        self.revmap = dict(sanitize(i) for i in output)
+        if "HEAD" in self.revmap:
+            del self.revmap["HEAD"]
+        self.revs = self.revmap.keys()
+        ofile.close()
+
+    def get_head(self):
+        """Determines the head of a local repo.
+        """
+
+        if not self.local:
+            return
+
+        path = os.path.join(self.gitpath, "HEAD")
+        head = open(path).readline()
+        self.head, _ = sanitize(head, ' ')
diff --git a/git_remote_helpers/setup.cfg b/git_remote_helpers/setup.cfg
new file mode 100644
index 0000000..4bff887
--- /dev/null
+++ b/git_remote_helpers/setup.cfg
@@ -0,0 +1,3 @@
+[build]
+build_purelib = build/lib
+build_platlib = build/lib
diff --git a/git_remote_helpers/setup.py b/git_remote_helpers/setup.py
new file mode 100644
index 0000000..4d434b6
--- /dev/null
+++ b/git_remote_helpers/setup.py
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+
+"""Distutils build/install script for the git_remote_helpers package."""
+
+from distutils.core import setup
+
+setup(
+    name = 'git_remote_helpers',
+    version = '0.1.0',
+    description = 'Git remote helper program for non-git repositories',
+    license = 'GPLv2',
+    author = 'The Git Community',
+    author_email = 'git@vger.kernel.org',
+    url = 'http://www.git-scm.com/',
+    package_dir = {'git_remote_helpers': ''},
+    packages = ['git_remote_helpers', 'git_remote_helpers.git'],
+)
diff --git a/git_remote_helpers/util.py b/git_remote_helpers/util.py
new file mode 100644
index 0000000..fbbb01b
--- /dev/null
+++ b/git_remote_helpers/util.py
@@ -0,0 +1,275 @@
+#!/usr/bin/env python
+
+"""Misc. useful functionality used by the rest of this package.
+
+This module provides common functionality used by the other modules in
+this package.
+
+"""
+
+import sys
+import os
+import subprocess
+
+try:
+    from subprocess import CalledProcessError
+except ImportError:
+    # from python2.7:subprocess.py
+    # Exception classes used by this module.
+    class CalledProcessError(Exception):
+        """This exception is raised when a process run by check_call() returns
+        a non-zero exit status.  The exit status will be stored in the
+        returncode attribute."""
+        def __init__(self, returncode, cmd):
+            self.returncode = returncode
+            self.cmd = cmd
+        def __str__(self):
+            return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode)
+
+
+# Whether or not to show debug messages
+DEBUG = False
+
+def notify(msg, *args):
+    """Print a message to stderr."""
+    print >> sys.stderr, msg % args
+
+def debug (msg, *args):
+    """Print a debug message to stderr when DEBUG is enabled."""
+    if DEBUG:
+        print >> sys.stderr, msg % args
+
+def error (msg, *args):
+    """Print an error message to stderr."""
+    print >> sys.stderr, "ERROR:", msg % args
+
+def warn(msg, *args):
+    """Print a warning message to stderr."""
+    print >> sys.stderr, "warning:", msg % args
+
+def die (msg, *args):
+    """Print as error message to stderr and exit the program."""
+    error(msg, *args)
+    sys.exit(1)
+
+
+class ProgressIndicator(object):
+
+    """Simple progress indicator.
+
+    Displayed as a spinning character by default, but can be customized
+    by passing custom messages that overrides the spinning character.
+
+    """
+
+    States = ("|", "/", "-", "\\")
+
+    def __init__ (self, prefix = "", f = sys.stdout):
+        """Create a new ProgressIndicator, bound to the given file object."""
+        self.n = 0  # Simple progress counter
+        self.f = f  # Progress is written to this file object
+        self.prev_len = 0  # Length of previous msg (to be overwritten)
+        self.prefix = prefix  # Prefix prepended to each progress message
+        self.prefix_lens = [] # Stack of prefix string lengths
+
+    def pushprefix (self, prefix):
+        """Append the given prefix onto the prefix stack."""
+        self.prefix_lens.append(len(self.prefix))
+        self.prefix += prefix
+
+    def popprefix (self):
+        """Remove the last prefix from the prefix stack."""
+        prev_len = self.prefix_lens.pop()
+        self.prefix = self.prefix[:prev_len]
+
+    def __call__ (self, msg = None, lf = False):
+        """Indicate progress, possibly with a custom message."""
+        if msg is None:
+            msg = self.States[self.n % len(self.States)]
+        msg = self.prefix + msg
+        print >> self.f, "\r%-*s" % (self.prev_len, msg),
+        self.prev_len = len(msg.expandtabs())
+        if lf:
+            print >> self.f
+            self.prev_len = 0
+        self.n += 1
+
+    def finish (self, msg = "done", noprefix = False):
+        """Finalize progress indication with the given message."""
+        if noprefix:
+            self.prefix = ""
+        self(msg, True)
+
+
+def start_command (args, cwd = None, shell = False, add_env = None,
+                   stdin = subprocess.PIPE, stdout = subprocess.PIPE,
+                   stderr = subprocess.PIPE):
+    """Start the given command, and return a subprocess object.
+
+    This provides a simpler interface to the subprocess module.
+
+    """
+    env = None
+    if add_env is not None:
+        env = os.environ.copy()
+        env.update(add_env)
+    return subprocess.Popen(args, bufsize = 1, stdin = stdin, stdout = stdout,
+                            stderr = stderr, cwd = cwd, shell = shell,
+                            env = env, universal_newlines = True)
+
+
+def run_command (args, cwd = None, shell = False, add_env = None,
+                 flag_error = True):
+    """Run the given command to completion, and return its results.
+
+    This provides a simpler interface to the subprocess module.
+
+    The results are formatted as a 3-tuple: (exit_code, output, errors)
+
+    If flag_error is enabled, Error messages will be produced if the
+    subprocess terminated with a non-zero exit code and/or stderr
+    output.
+
+    The other arguments are passed on to start_command().
+
+    """
+    process = start_command(args, cwd, shell, add_env)
+    (output, errors) = process.communicate()
+    exit_code = process.returncode
+    if flag_error and errors:
+        error("'%s' returned errors:\n---\n%s---", " ".join(args), errors)
+    if flag_error and exit_code:
+        error("'%s' returned exit code %i", " ".join(args), exit_code)
+    return (exit_code, output, errors)
+
+
+# from python2.7:subprocess.py
+def call(*popenargs, **kwargs):
+    """Run command with arguments.  Wait for command to complete, then
+    return the returncode attribute.
+
+    The arguments are the same as for the Popen constructor.  Example:
+
+    retcode = call(["ls", "-l"])
+    """
+    return subprocess.Popen(*popenargs, **kwargs).wait()
+
+
+# from python2.7:subprocess.py
+def check_call(*popenargs, **kwargs):
+    """Run command with arguments.  Wait for command to complete.  If
+    the exit code was zero then return, otherwise raise
+    CalledProcessError.  The CalledProcessError object will have the
+    return code in the returncode attribute.
+
+    The arguments are the same as for the Popen constructor.  Example:
+
+    check_call(["ls", "-l"])
+    """
+    retcode = call(*popenargs, **kwargs)
+    if retcode:
+        cmd = kwargs.get("args")
+        if cmd is None:
+            cmd = popenargs[0]
+        raise CalledProcessError(retcode, cmd)
+    return 0
+
+
+# from python2.7:subprocess.py
+def check_output(*popenargs, **kwargs):
+    r"""Run command with arguments and return its output as a byte string.
+
+    If the exit code was non-zero it raises a CalledProcessError.  The
+    CalledProcessError object will have the return code in the returncode
+    attribute and output in the output attribute.
+
+    The arguments are the same as for the Popen constructor.  Example:
+
+    >>> check_output(["ls", "-l", "/dev/null"])
+    'crw-rw-rw- 1 root root 1, 3 Oct 18  2007 /dev/null\n'
+
+    The stdout argument is not allowed as it is used internally.
+    To capture standard error in the result, use stderr=STDOUT.
+
+    >>> check_output(["/bin/sh", "-c",
+    ...               "ls -l non_existent_file ; exit 0"],
+    ...              stderr=STDOUT)
+    'ls: non_existent_file: No such file or directory\n'
+    """
+    if 'stdout' in kwargs:
+        raise ValueError('stdout argument not allowed, it will be overridden.')
+    process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs)
+    output, unused_err = process.communicate()
+    retcode = process.poll()
+    if retcode:
+        cmd = kwargs.get("args")
+        if cmd is None:
+            cmd = popenargs[0]
+        raise subprocess.CalledProcessError(retcode, cmd)
+    return output
+
+
+def file_reader_method (missing_ok = False):
+    """Decorator for simplifying reading of files.
+
+    If missing_ok is True, a failure to open a file for reading will
+    not raise the usual IOError, but instead the wrapped method will be
+    called with f == None.  The method must in this case properly
+    handle f == None.
+
+    """
+    def _wrap (method):
+        """Teach given method to handle both filenames and file objects.
+
+        The given method must take a file object as its second argument
+        (the first argument being 'self', of course).  This decorator
+        will take a filename given as the second argument and promote
+        it to a file object.
+
+        """
+        def _wrapped_method (self, filename, *args, **kwargs):
+            if isinstance(filename, file):
+                f = filename
+            else:
+                try:
+                    f = open(filename, 'r')
+                except IOError:
+                    if missing_ok:
+                        f = None
+                    else:
+                        raise
+            try:
+                return method(self, f, *args, **kwargs)
+            finally:
+                if not isinstance(filename, file) and f:
+                    f.close()
+        return _wrapped_method
+    return _wrap
+
+
+def file_writer_method (method):
+    """Decorator for simplifying writing of files.
+
+    Enables the given method to handle both filenames and file objects.
+
+    The given method must take a file object as its second argument
+    (the first argument being 'self', of course).  This decorator will
+    take a filename given as the second argument and promote it to a
+    file object.
+
+    """
+    def _new_method (self, filename, *args, **kwargs):
+        if isinstance(filename, file):
+            f = filename
+        else:
+            # Make sure the containing directory exists
+            parent_dir = os.path.dirname(filename)
+            if not os.path.isdir(parent_dir):
+                os.makedirs(parent_dir)
+            f = open(filename, 'w')
+        try:
+            return method(self, f, *args, **kwargs)
+        finally:
+            if not isinstance(filename, file):
+                f.close()
+    return _new_method
diff --git a/gitk-git/gitk b/gitk-git/gitk
old mode 100644
new mode 100755
index 4604c83..22270ce
--- a/gitk-git/gitk
+++ b/gitk-git/gitk
@@ -2,18 +2,45 @@
 # Tcl ignores the next line -*- tcl -*- \
 exec wish "$0" -- "$@"
 
-# Copyright © 2005-2008 Paul Mackerras.  All rights reserved.
+# Copyright © 2005-2011 Paul Mackerras.  All rights reserved.
 # This program is free software; it may be used, copied, modified
 # and distributed under the terms of the GNU General Public Licence,
 # either version 2, or (at your option) any later version.
 
-proc gitdir {} {
-    global env
-    if {[info exists env(GIT_DIR)]} {
-	return $env(GIT_DIR)
-    } else {
-	return [exec git rev-parse --git-dir]
+package require Tk
+
+proc hasworktree {} {
+    return [expr {[exec git rev-parse --is-bare-repository] == "false" &&
+		  [exec git rev-parse --is-inside-git-dir] == "false"}]
+}
+
+proc reponame {} {
+    global gitdir
+    set n [file normalize $gitdir]
+    if {[string match "*/.git" $n]} {
+	set n [string range $n 0 end-5]
     }
+    return [file tail $n]
+}
+
+proc gitworktree {} {
+    variable _gitworktree
+    if {[info exists _gitworktree]} {
+	return $_gitworktree
+    }
+    # v1.7.0 introduced --show-toplevel to return the canonical work-tree
+    if {[catch {set _gitworktree [exec git rev-parse --show-toplevel]}]} {
+        # try to set work tree from environment, core.worktree or use
+        # cdup to obtain a relative path to the top of the worktree. If
+        # run from the top, the ./ prefix ensures normalize expands pwd.
+        if {[catch { set _gitworktree $env(GIT_WORK_TREE) }]} {
+	    catch {set _gitworktree [exec git config --get core.worktree]}
+	    if {$_gitworktree eq ""} {
+		set _gitworktree [file normalize ./[exec git rev-parse --show-cdup]]
+	    }
+        }
+    }
+    return $_gitworktree
 }
 
 # A simple scheduler for compute-intensive stuff.
@@ -128,7 +155,8 @@
 }
 
 proc parseviewargs {n arglist} {
-    global vdatemode vmergeonly vflags vdflags vrevs vfiltered vorigargs
+    global vdatemode vmergeonly vflags vdflags vrevs vfiltered vorigargs env
+    global worddiff git_version
 
     set vdatemode($n) 0
     set vmergeonly($n) 0
@@ -166,7 +194,7 @@
 		lappend diffargs $arg
 	    }
 	    "--raw" - "--patch-with-raw" - "--patch-with-stat" -
-	    "--name-only" - "--name-status" - "--color" - "--color-words" -
+	    "--name-only" - "--name-status" - "--color" -
 	    "--log-size" - "--pretty=*" - "--decorate" - "--abbrev-commit" -
 	    "--cc" - "-z" - "--header" - "--parents" - "--boundary" -
 	    "--no-color" - "-g" - "--walk-reflogs" - "--no-walk" -
@@ -175,6 +203,18 @@
 		# These cause our parsing of git log's output to fail, or else
 		# they're options we want to set ourselves, so ignore them.
 	    }
+	    "--color-words*" - "--word-diff=color" {
+		# These trigger a word diff in the console interface,
+		# so help the user by enabling our own support
+		if {[package vcompare $git_version "1.7.2"] >= 0} {
+		    set worddiff [mc "Color words"]
+		}
+	    }
+	    "--word-diff*" {
+		if {[package vcompare $git_version "1.7.2"] >= 0} {
+		    set worddiff [mc "Markup words"]
+		}
+	    }
 	    "--stat=*" - "--numstat" - "--shortstat" - "--summary" -
 	    "--check" - "--exit-code" - "--quiet" - "--topo-order" -
 	    "--full-history" - "--dense" - "--sparse" -
@@ -208,6 +248,9 @@
 		# git rev-parse doesn't understand --merge
 		lappend revargs --gitk-symmetric-diff-marker MERGE_HEAD...HEAD
 	    }
+	    "--no-replace-objects" {
+		set env(GIT_NO_REPLACE_OBJECTS) "1"
+	    }
 	    "-*" {
 		# Other flag arguments including -<n>
 		if {[string is digit -strict [string range $arg 1 end]]} {
@@ -265,7 +308,7 @@
 		}
 		lappend badrev $line
 	    }
-	}		    
+	}
 	error_popup "[mc "Error parsing revisions:"] $err"
 	return {}
     }
@@ -288,7 +331,7 @@
 	    if {$sdm != 2} {
 		lappend ret $id
 	    } else {
-		lset ret end [lindex $ret end]...$id
+		lset ret end $id...[lindex $ret end]
 	    }
 	    lappend pos $id
 	}
@@ -308,6 +351,7 @@
     global viewactive viewinstances vmergeonly
     global mainheadid viewmainheadid viewmainheadid_orig
     global vcanopt vflags vrevs vorigargs
+    global show_notes
 
     set startmsecs [clock clicks -milliseconds]
     set commitidx($view) 0
@@ -356,8 +400,8 @@
     }
 
     if {[catch {
-	set fd [open [concat | git log --no-color -z --pretty=raw --parents \
-			 --boundary $args "--" $files] r]
+	set fd [open [concat | git log --no-color -z --pretty=raw $show_notes \
+			--parents --boundary $args "--" $files] r]
     } err]} {
 	error_popup "[mc "Error executing git log:"] $err"
 	return 0
@@ -449,10 +493,11 @@
     global viewactive viewcomplete tclencoding
     global startmsecs showneartags showlocalchanges
     global mainheadid viewmainheadid viewmainheadid_orig pending_select
-    global isworktree
+    global hasworktree
     global varcid vposids vnegids vflags vrevs
+    global show_notes
 
-    set isworktree [expr {[exec git rev-parse --is-inside-work-tree] == "true"}]
+    set hasworktree [hasworktree]
     rereadrefs
     set view $curview
     if {$mainheadid ne $viewmainheadid_orig($view)} {
@@ -503,8 +548,8 @@
 	set args $vorigargs($view)
     }
     if {[catch {
-	set fd [open [concat | git log --no-color -z --pretty=raw --parents \
-			  --boundary $args "--" $vfilelimit($view)] r]
+	set fd [open [concat | git log --no-color -z --pretty=raw $show_notes \
+			--parents --boundary $args "--" $vfilelimit($view)] r]
     } err]} {
 	error_popup "[mc "Error executing git log:"] $err"
 	return
@@ -596,12 +641,16 @@
 
 proc resetvarcs {view} {
     global varcid varccommits parents children vseedcount ordertok
+    global vshortids
 
     foreach vid [array names varcid $view,*] {
 	unset varcid($vid)
 	unset children($vid)
 	unset parents($vid)
     }
+    foreach vid [array names vshortids $view,*] {
+	unset vshortids($vid)
+    }
     # some commits might have children but haven't been seen yet
     foreach vid [array names children $view,*] {
 	unset children($vid)
@@ -639,7 +688,7 @@
 	if {![info exists commitinfo($id)]} {
 	    parsecommit $id $commitdata($id) 1
 	}
-	set cdate [lindex $commitinfo($id) 4]
+	set cdate [lindex [lindex $commitinfo($id) 4] 0]
 	if {![string is integer -strict $cdate]} {
 	    set cdate 0
 	}
@@ -888,7 +937,7 @@
 proc insertrow {id p v} {
     global cmitlisted children parents varcid varctok vtokmod
     global varccommits ordertok commitidx numcommits curview
-    global targetid targetrow
+    global targetid targetrow vshortids
 
     readcommit $id
     set vid $v,$id
@@ -897,6 +946,7 @@
     set parents($vid) [list $p]
     set a [newvarc $v $id]
     set varcid($vid) $a
+    lappend vshortids($v,[string range $id 0 3]) $id
     if {[string compare [lindex $varctok($v) $a] $vtokmod($v)] < 0} {
 	modify_arc $v $a
     }
@@ -987,6 +1037,18 @@
     drawvisible
 }
 
+proc real_children {vp} {
+    global children nullid nullid2
+
+    set kids {}
+    foreach id $children($vp) {
+	if {$id ne $nullid && $id ne $nullid2} {
+	    lappend kids $id
+	}
+    }
+    return $kids
+}
+
 proc first_real_child {vp} {
     global children nullid nullid2
 
@@ -1340,7 +1402,7 @@
     global commitidx commitdata vdatemode
     global parents children curview hlview
     global idpending ordertok
-    global varccommits varcid varctok vtokmod vfilelimit
+    global varccommits varcid varctok vtokmod vfilelimit vshortids
 
     set stuff [read $fd 500000]
     # git log doesn't terminate the last commit with a null...
@@ -1440,6 +1502,8 @@
 	set id [lindex $ids 0]
 	set vid $view,$id
 
+	lappend vshortids($view,[string range $id 0 3]) $id
+
 	if {!$listed && $updating && ![info exists varcid($vid)] &&
 	    $vfilelimit($view) ne {}} {
 	    # git log doesn't rewrite parents for unlisted commits
@@ -1589,7 +1653,7 @@
 }
 
 proc parsecommit {id contents listed} {
-    global commitinfo cdate
+    global commitinfo
 
     set inhdr 1
     set comment {}
@@ -1609,10 +1673,10 @@
 	set line [split $line " "]
 	set tag [lindex $line 0]
 	if {$tag == "author"} {
-	    set audate [lindex $line end-1]
+	    set audate [lrange $line end-1 end]
 	    set auname [join [lrange $line 1 end-2] " "]
 	} elseif {$tag == "committer"} {
-	    set comdate [lindex $line end-1]
+	    set comdate [lrange $line end-1 end]
 	    set comname [join [lrange $line 1 end-2] " "]
 	}
     }
@@ -1639,11 +1703,9 @@
 	}
 	set comment $newcomment
     }
-    if {$comdate != {}} {
-	set cdate($id) $comdate
-    }
+    set hasnote [string first "\nNotes:\n" $contents]
     set commitinfo($id) [list $headline $auname $audate \
-			     $comname $comdate $comment]
+			     $comname $comdate $comment $hasnote]
 }
 
 proc getcommit {id} {
@@ -1664,11 +1726,26 @@
 # and are present in the current view.
 # This is fairly slow...
 proc longid {prefix} {
-    global varcid curview
+    global varcid curview vshortids
 
     set ids {}
-    foreach match [array names varcid "$curview,$prefix*"] {
-	lappend ids [lindex [split $match ","] 1]
+    if {[string length $prefix] >= 4} {
+	set vshortid $curview,[string range $prefix 0 3]
+	if {[info exists vshortids($vshortid)]} {
+	    foreach id $vshortids($vshortid) {
+		if {[string match "$prefix*" $id]} {
+		    if {[lsearch -exact $ids $id] < 0} {
+			lappend ids $id
+			if {[llength $ids] >= 2} break
+		    }
+		}
+	    }
+	}
+    } else {
+	foreach match [array names varcid "$curview,$prefix*"] {
+	    lappend ids [lindex [split $match ","] 1]
+	    if {[llength $ids] >= 2} break
+	}
     }
     return $ids
 }
@@ -1677,6 +1754,7 @@
     global tagids idtags headids idheads tagobjid
     global otherrefids idotherrefs mainhead mainheadid
     global selecthead selectheadid
+    global hideremotes
 
     foreach v {tagids idtags headids idheads otherrefids idotherrefs} {
 	catch {unset $v}
@@ -1689,7 +1767,7 @@
 	if {![string match "refs/*" $ref]} continue
 	set name [string range $ref 5 end]
 	if {[string match "remotes/*" $name]} {
-	    if {![string match "*/HEAD" $name]} {
+	    if {![string match "*/HEAD" $name] && !$hideremotes} {
 		set headids($name) $id
 		lappend idheads($id) $name
 	    }
@@ -1768,6 +1846,15 @@
     unset headids($name)
 }
 
+proc ttk_toplevel {w args} {
+    global use_ttk
+    eval [linsert $args 0 ::toplevel $w]
+    if {$use_ttk} {
+        place [ttk::frame $w._toplevel_background] -x 0 -y 0 -relwidth 1 -relheight 1
+    }
+    return $w
+}
+
 proc make_transient {window origin} {
     global have_tk85
 
@@ -1786,10 +1873,13 @@
     }
 }
 
-proc show_error {w top msg} {
+proc show_error {w top msg {mc mc}} {
+    global NS
+    if {![info exists NS]} {set NS ""}
+    if {[wm state $top] eq "withdrawn"} { wm deiconify $top }
     message $w.m -text $msg -justify center -aspect 400
     pack $w.m -side top -fill x -padx 20 -pady 20
-    button $w.ok -text [mc OK] -command "destroy $top"
+    ${NS}::button $w.ok -default active -text [$mc OK] -command "destroy $top"
     pack $w.ok -side bottom -fill x
     bind $top <Visibility> "grab $top; focus $top"
     bind $top <Key-Return> "destroy $top"
@@ -1799,45 +1889,59 @@
 }
 
 proc error_popup {msg {owner .}} {
-    set w .error
-    toplevel $w
-    make_transient $w $owner
-    show_error $w $w $msg
+    if {[tk windowingsystem] eq "win32"} {
+        tk_messageBox -icon error -type ok -title [wm title .] \
+            -parent $owner -message $msg
+    } else {
+        set w .error
+        ttk_toplevel $w
+        make_transient $w $owner
+        show_error $w $w $msg
+    }
 }
 
 proc confirm_popup {msg {owner .}} {
-    global confirm_ok
+    global confirm_ok NS
     set confirm_ok 0
     set w .confirm
-    toplevel $w
+    ttk_toplevel $w
     make_transient $w $owner
     message $w.m -text $msg -justify center -aspect 400
     pack $w.m -side top -fill x -padx 20 -pady 20
-    button $w.ok -text [mc OK] -command "set confirm_ok 1; destroy $w"
+    ${NS}::button $w.ok -text [mc OK] -command "set confirm_ok 1; destroy $w"
     pack $w.ok -side left -fill x
-    button $w.cancel -text [mc Cancel] -command "destroy $w"
+    ${NS}::button $w.cancel -text [mc Cancel] -command "destroy $w"
     pack $w.cancel -side right -fill x
     bind $w <Visibility> "grab $w; focus $w"
     bind $w <Key-Return> "set confirm_ok 1; destroy $w"
     bind $w <Key-space>  "set confirm_ok 1; destroy $w"
     bind $w <Key-Escape> "destroy $w"
+    tk::PlaceWindow $w widget $owner
     tkwait window $w
     return $confirm_ok
 }
 
 proc setoptions {} {
-    option add *Panedwindow.showHandle 1 startupFile
-    option add *Panedwindow.sashRelief raised startupFile
+    if {[tk windowingsystem] ne "win32"} {
+        option add *Panedwindow.showHandle 1 startupFile
+        option add *Panedwindow.sashRelief raised startupFile
+        if {[tk windowingsystem] ne "aqua"} {
+            option add *Menu.font uifont startupFile
+        }
+    } else {
+        option add *Menu.TearOff 0 startupFile
+    }
     option add *Button.font uifont startupFile
     option add *Checkbutton.font uifont startupFile
     option add *Radiobutton.font uifont startupFile
-    if {[tk windowingsystem] ne "aqua"} {
-	option add *Menu.font uifont startupFile
-    }
     option add *Menubutton.font uifont startupFile
     option add *Label.font uifont startupFile
     option add *Message.font uifont startupFile
-    option add *Entry.font uifont startupFile
+    option add *Entry.font textfont startupFile
+    option add *Text.font textfont startupFile
+    option add *Labelframe.font uifont startupFile
+    option add *Spinbox.font textfont startupFile
+    option add *Listbox.font mainfont startupFile
 }
 
 # Make a menu and submenus.
@@ -1894,6 +1998,22 @@
     return [string map {&& & & {}} [mc $str]]
 }
 
+proc makedroplist {w varname args} {
+    global use_ttk
+    if {$use_ttk} {
+        set width 0
+        foreach label $args {
+            set cx [string length $label]
+            if {$cx > $width} {set width $cx}
+        }
+	set gm [ttk::combobox $w -width $width -state readonly\
+		    -textvariable $varname -values $args]
+    } else {
+	set gm [eval [linsert $args 0 tk_optionMenu $w $varname]]
+    }
+    return $gm
+}
+
 proc makewindow {} {
     global canv canv2 canv3 linespc charspc ctext cflist cscroll
     global tabstop
@@ -1909,7 +2029,9 @@
     global headctxmenu progresscanv progressitem progresscoords statusw
     global fprogitem fprogcoord lastprogupdate progupdatepending
     global rprogitem rprogcoord rownumsel numcommits
-    global have_tk85
+    global have_tk85 use_ttk NS
+    global git_version
+    global worddiff
 
     # The "mc" arguments here are purely so that xgettext
     # sees the following string as needing to be translated
@@ -1961,8 +2083,13 @@
     makemenu .bar $bar
     . configure -menu .bar
 
+    if {$use_ttk} {
+        # cover the non-themed toplevel with a themed frame.
+        place [ttk::frame ._main_background] -x 0 -y 0 -relwidth 1 -relheight 1
+    }
+
     # the gui has upper and lower half, parts of a paned window.
-    panedwindow .ctop -orient vertical
+    ${NS}::panedwindow .ctop -orient vertical
 
     # possibly use assumed geometry
     if {![info exists geometry(pwsash0)]} {
@@ -1970,14 +2097,17 @@
         set geometry(topwidth) [expr {80 * $charspc}]
         set geometry(botheight) [expr {15 * $linespc}]
         set geometry(botwidth) [expr {50 * $charspc}]
-        set geometry(pwsash0) "[expr {40 * $charspc}] 2"
-        set geometry(pwsash1) "[expr {60 * $charspc}] 2"
+        set geometry(pwsash0) [list [expr {40 * $charspc}] 2]
+        set geometry(pwsash1) [list [expr {60 * $charspc}] 2]
     }
 
     # the upper half will have a paned window, a scroll bar to the right, and some stuff below
-    frame .tf -height $geometry(topheight) -width $geometry(topwidth)
-    frame .tf.histframe
-    panedwindow .tf.histframe.pwclist -orient horizontal -sashpad 0 -handlesize 4
+    ${NS}::frame .tf -height $geometry(topheight) -width $geometry(topwidth)
+    ${NS}::frame .tf.histframe
+    ${NS}::panedwindow .tf.histframe.pwclist -orient horizontal
+    if {!$use_ttk} {
+	.tf.histframe.pwclist configure -sashpad 0 -handlesize 4
+    }
 
     # create three canvases
     set cscroll .tf.histframe.csb
@@ -1997,28 +2127,37 @@
 	-selectbackground $selectbgcolor \
 	-background $bgcolor -bd 0 -yscrollincr $linespc
     .tf.histframe.pwclist add $canv3
-    eval .tf.histframe.pwclist sash place 0 $geometry(pwsash0)
-    eval .tf.histframe.pwclist sash place 1 $geometry(pwsash1)
+    if {$use_ttk} {
+	bind .tf.histframe.pwclist <Map> {
+	    bind %W <Map> {}
+	    .tf.histframe.pwclist sashpos 1 [lindex $::geometry(pwsash1) 0]
+	    .tf.histframe.pwclist sashpos 0 [lindex $::geometry(pwsash0) 0]
+	}
+    } else {
+	eval .tf.histframe.pwclist sash place 0 $geometry(pwsash0)
+	eval .tf.histframe.pwclist sash place 1 $geometry(pwsash1)
+    }
 
     # a scroll bar to rule them
-    scrollbar $cscroll -command {allcanvs yview} -highlightthickness 0
+    ${NS}::scrollbar $cscroll -command {allcanvs yview}
+    if {!$use_ttk} {$cscroll configure -highlightthickness 0}
     pack $cscroll -side right -fill y
     bind .tf.histframe.pwclist <Configure> {resizeclistpanes %W %w}
     lappend bglist $canv $canv2 $canv3
     pack .tf.histframe.pwclist -fill both -expand 1 -side left
 
     # we have two button bars at bottom of top frame. Bar 1
-    frame .tf.bar
-    frame .tf.lbar -height 15
+    ${NS}::frame .tf.bar
+    ${NS}::frame .tf.lbar -height 15
 
     set sha1entry .tf.bar.sha1
     set entries $sha1entry
     set sha1but .tf.bar.sha1label
-    button $sha1but -text [mc "SHA1 ID: "] -state disabled -relief flat \
+    button $sha1but -text "[mc "SHA1 ID:"] " -state disabled -relief flat \
 	-command gotocommit -width 8
     $sha1but conf -disabledforeground [$sha1but cget -foreground]
     pack .tf.bar.sha1label -side left
-    entry $sha1entry -width 40 -font textfont -textvariable sha1string
+    ${NS}::entry $sha1entry -width 40 -font textfont -textvariable sha1string
     trace add variable sha1string write sha1change
     pack $sha1entry -side left -pady 2
 
@@ -2038,36 +2177,43 @@
 	0x00, 0x38, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x00, 0x38, 0x00, 0x1c,
 	0x00, 0x0e, 0x00, 0x07, 0x80, 0x03, 0xc0, 0x01};
     }
-    button .tf.bar.leftbut -image bm-left -command goback \
+    ${NS}::button .tf.bar.leftbut -image bm-left -command goback \
 	-state disabled -width 26
     pack .tf.bar.leftbut -side left -fill y
-    button .tf.bar.rightbut -image bm-right -command goforw \
+    ${NS}::button .tf.bar.rightbut -image bm-right -command goforw \
 	-state disabled -width 26
     pack .tf.bar.rightbut -side left -fill y
 
-    label .tf.bar.rowlabel -text [mc "Row"]
+    ${NS}::label .tf.bar.rowlabel -text [mc "Row"]
     set rownumsel {}
-    label .tf.bar.rownum -width 7 -font textfont -textvariable rownumsel \
+    ${NS}::label .tf.bar.rownum -width 7 -textvariable rownumsel \
 	-relief sunken -anchor e
-    label .tf.bar.rowlabel2 -text "/"
-    label .tf.bar.numcommits -width 7 -font textfont -textvariable numcommits \
+    ${NS}::label .tf.bar.rowlabel2 -text "/"
+    ${NS}::label .tf.bar.numcommits -width 7 -textvariable numcommits \
 	-relief sunken -anchor e
     pack .tf.bar.rowlabel .tf.bar.rownum .tf.bar.rowlabel2 .tf.bar.numcommits \
 	-side left
+    if {!$use_ttk} {
+        foreach w {rownum numcommits} {.tf.bar.$w configure -font textfont}
+    }
     global selectedline
     trace add variable selectedline write selectedline_change
 
     # Status label and progress bar
     set statusw .tf.bar.status
-    label $statusw -width 15 -relief sunken
+    ${NS}::label $statusw -width 15 -relief sunken
     pack $statusw -side left -padx 5
-    set h [expr {[font metrics uifont -linespace] + 2}]
-    set progresscanv .tf.bar.progress
-    canvas $progresscanv -relief sunken -height $h -borderwidth 2
-    set progressitem [$progresscanv create rect -1 0 0 $h -fill green]
-    set fprogitem [$progresscanv create rect -1 0 0 $h -fill yellow]
-    set rprogitem [$progresscanv create rect -1 0 0 $h -fill red]
-    pack $progresscanv -side right -expand 1 -fill x
+    if {$use_ttk} {
+	set progresscanv [ttk::progressbar .tf.bar.progress]
+    } else {
+	set h [expr {[font metrics uifont -linespace] + 2}]
+	set progresscanv .tf.bar.progress
+	canvas $progresscanv -relief sunken -height $h -borderwidth 2
+	set progressitem [$progresscanv create rect -1 0 0 $h -fill green]
+	set fprogitem [$progresscanv create rect -1 0 0 $h -fill yellow]
+	set rprogitem [$progresscanv create rect -1 0 0 $h -fill red]
+    }
+    pack $progresscanv -side right -expand 1 -fill x -padx {0 2}
     set progresscoords {0 0}
     set fprogcoord 0
     set rprogcoord 0
@@ -2076,14 +2222,14 @@
     set progupdatepending 0
 
     # build up the bottom bar of upper window
-    label .tf.lbar.flabel -text "[mc "Find"] "
-    button .tf.lbar.fnext -text [mc "next"] -command {dofind 1 1}
-    button .tf.lbar.fprev -text [mc "prev"] -command {dofind -1 1}
-    label .tf.lbar.flab2 -text " [mc "commit"] "
+    ${NS}::label .tf.lbar.flabel -text "[mc "Find"] "
+    ${NS}::button .tf.lbar.fnext -text [mc "next"] -command {dofind 1 1}
+    ${NS}::button .tf.lbar.fprev -text [mc "prev"] -command {dofind -1 1}
+    ${NS}::label .tf.lbar.flab2 -text " [mc "commit"] "
     pack .tf.lbar.flabel .tf.lbar.fnext .tf.lbar.fprev .tf.lbar.flab2 \
 	-side left -fill y
     set gdttype [mc "containing:"]
-    set gm [tk_optionMenu .tf.lbar.gdttype gdttype \
+    set gm [makedroplist .tf.lbar.gdttype gdttype \
 		[mc "containing:"] \
 		[mc "touching paths:"] \
 		[mc "adding/removing string:"]]
@@ -2093,14 +2239,14 @@
     set findstring {}
     set fstring .tf.lbar.findstring
     lappend entries $fstring
-    entry $fstring -width 30 -font textfont -textvariable findstring
+    ${NS}::entry $fstring -width 30 -textvariable findstring
     trace add variable findstring write find_change
     set findtype [mc "Exact"]
-    set findtypemenu [tk_optionMenu .tf.lbar.findtype \
-		      findtype [mc "Exact"] [mc "IgnCase"] [mc "Regexp"]]
+    set findtypemenu [makedroplist .tf.lbar.findtype \
+			  findtype [mc "Exact"] [mc "IgnCase"] [mc "Regexp"]]
     trace add variable findtype write findcom_change
     set findloc [mc "All fields"]
-    tk_optionMenu .tf.lbar.findloc findloc [mc "All fields"] [mc "Headline"] \
+    makedroplist .tf.lbar.findloc findloc [mc "All fields"] [mc "Headline"] \
 	[mc "Comments"] [mc "Author"] [mc "Committer"]
     trace add variable findloc write find_change
     pack .tf.lbar.findloc -side right
@@ -2112,40 +2258,43 @@
     pack .tf.bar -in .tf -side bottom -fill x
     pack .tf.histframe -fill both -side top -expand 1
     .ctop add .tf
-    .ctop paneconfigure .tf -height $geometry(topheight)
-    .ctop paneconfigure .tf -width $geometry(topwidth)
+    if {!$use_ttk} {
+	.ctop paneconfigure .tf -height $geometry(topheight)
+	.ctop paneconfigure .tf -width $geometry(topwidth)
+    }
 
     # now build up the bottom
-    panedwindow .pwbottom -orient horizontal
+    ${NS}::panedwindow .pwbottom -orient horizontal
 
     # lower left, a text box over search bar, scroll bar to the right
     # if we know window height, then that will set the lower text height, otherwise
     # we set lower text height which will drive window height
     if {[info exists geometry(main)]} {
-        frame .bleft -width $geometry(botwidth)
+	${NS}::frame .bleft -width $geometry(botwidth)
     } else {
-        frame .bleft -width $geometry(botwidth) -height $geometry(botheight)
+	${NS}::frame .bleft -width $geometry(botwidth) -height $geometry(botheight)
     }
-    frame .bleft.top
-    frame .bleft.mid
-    frame .bleft.bottom
+    ${NS}::frame .bleft.top
+    ${NS}::frame .bleft.mid
+    ${NS}::frame .bleft.bottom
 
-    button .bleft.top.search -text [mc "Search"] -command dosearch
+    ${NS}::button .bleft.top.search -text [mc "Search"] -command dosearch
     pack .bleft.top.search -side left -padx 5
     set sstring .bleft.top.sstring
-    entry $sstring -width 20 -font textfont -textvariable searchstring
+    set searchstring ""
+    ${NS}::entry $sstring -width 20 -textvariable searchstring
     lappend entries $sstring
     trace add variable searchstring write incrsearch
     pack $sstring -side left -expand 1 -fill x
-    radiobutton .bleft.mid.diff -text [mc "Diff"] \
+    ${NS}::radiobutton .bleft.mid.diff -text [mc "Diff"] \
 	-command changediffdisp -variable diffelide -value {0 0}
-    radiobutton .bleft.mid.old -text [mc "Old version"] \
+    ${NS}::radiobutton .bleft.mid.old -text [mc "Old version"] \
 	-command changediffdisp -variable diffelide -value {0 1}
-    radiobutton .bleft.mid.new -text [mc "New version"] \
+    ${NS}::radiobutton .bleft.mid.new -text [mc "New version"] \
 	-command changediffdisp -variable diffelide -value {1 0}
-    label .bleft.mid.labeldiffcontext -text "      [mc "Lines of context"]: "
+    ${NS}::label .bleft.mid.labeldiffcontext -text "      [mc "Lines of context"]: "
     pack .bleft.mid.diff .bleft.mid.old .bleft.mid.new -side left
-    spinbox .bleft.mid.diffcontext -width 5 -font textfont \
+    spinbox .bleft.mid.diffcontext -width 5 \
 	-from 0 -increment 1 -to 10000000 \
 	-validate all -validatecommand "diffcontextvalidate %P" \
 	-textvariable diffcontextstring
@@ -2153,9 +2302,18 @@
     trace add variable diffcontextstring write diffcontextchange
     lappend entries .bleft.mid.diffcontext
     pack .bleft.mid.labeldiffcontext .bleft.mid.diffcontext -side left
-    checkbutton .bleft.mid.ignspace -text [mc "Ignore space change"] \
+    ${NS}::checkbutton .bleft.mid.ignspace -text [mc "Ignore space change"] \
 	-command changeignorespace -variable ignorespace
     pack .bleft.mid.ignspace -side left -padx 5
+
+    set worddiff [mc "Line diff"]
+    if {[package vcompare $git_version "1.7.2"] >= 0} {
+	makedroplist .bleft.mid.worddiff worddiff [mc "Line diff"] \
+	    [mc "Markup words"] [mc "Color words"]
+	trace add variable worddiff write changeworddiff
+	pack .bleft.mid.worddiff -side left -padx 5
+    }
+
     set ctext .bleft.bottom.ctext
     text $ctext -background $bgcolor -foreground $fgcolor \
 	-state disabled -font textfont \
@@ -2164,9 +2322,8 @@
     if {$have_tk85} {
 	$ctext conf -tabstyle wordprocessor
     }
-    scrollbar .bleft.bottom.sb -command "$ctext yview"
-    scrollbar .bleft.bottom.sbhorizontal -command "$ctext xview" -orient h \
-	-width 10
+    ${NS}::scrollbar .bleft.bottom.sb -command "$ctext yview"
+    ${NS}::scrollbar .bleft.bottom.sbhorizontal -command "$ctext xview" -orient h
     pack .bleft.top -side top -fill x
     pack .bleft.mid -side top -fill x
     grid $ctext .bleft.bottom.sb -sticky nsew
@@ -2206,14 +2363,16 @@
     $ctext tag conf found -back yellow
 
     .pwbottom add .bleft
-    .pwbottom paneconfigure .bleft -width $geometry(botwidth)
+    if {!$use_ttk} {
+	.pwbottom paneconfigure .bleft -width $geometry(botwidth)
+    }
 
     # lower right
-    frame .bright
-    frame .bright.mode
-    radiobutton .bright.mode.patch -text [mc "Patch"] \
+    ${NS}::frame .bright
+    ${NS}::frame .bright.mode
+    ${NS}::radiobutton .bright.mode.patch -text [mc "Patch"] \
 	-command reselectline -variable cmitmode -value "patch"
-    radiobutton .bright.mode.tree -text [mc "Tree"] \
+    ${NS}::radiobutton .bright.mode.tree -text [mc "Tree"] \
 	-command reselectline -variable cmitmode -value "tree"
     grid .bright.mode.patch .bright.mode.tree -sticky ew
     pack .bright.mode -side top -fill x
@@ -2229,7 +2388,7 @@
 	-spacing1 1 -spacing3 1
     lappend bglist $cflist
     lappend fglist $cflist
-    scrollbar .bright.sb -command "$cflist yview"
+    ${NS}::scrollbar .bright.sb -command "$cflist yview"
     pack .bright.sb -side right -fill y
     pack $cflist -side left -fill both -expand 1
     $cflist tag configure highlight \
@@ -2264,6 +2423,17 @@
         set ::BM "2"
     }
 
+    if {$use_ttk} {
+        bind .ctop <Map> {
+            bind %W <Map> {}
+            %W sashpos 0 $::geometry(topheight)
+        }
+        bind .pwbottom <Map> {
+            bind %W <Map> {}
+            %W sashpos 0 $::geometry(botwidth)
+        }
+    }
+
     bind .pwbottom <Configure> {resizecdetpanes %W %w}
     pack .ctop -fill both -expand 1
     bindall <1> {selcanvline %W %x %y}
@@ -2287,6 +2457,8 @@
     }
     bindall <$::BM> "canvscan mark %W %x %y"
     bindall <B$::BM-Motion> "canvscan dragto %W %x %y"
+    bind all <$M1B-Key-w> {destroy [winfo toplevel %W]}
+    bind . <$M1B-Key-w> doquit
     bindkey <Home> selfirstline
     bindkey <End> sellastline
     bind . <Key-Up> "selnextline -1"
@@ -2310,9 +2482,9 @@
     bindkey n "selnextline 1"
     bindkey z "goback"
     bindkey x "goforw"
-    bindkey i "selnextline -1"
-    bindkey k "selnextline 1"
-    bindkey j "goback"
+    bindkey k "selnextline -1"
+    bindkey j "selnextline 1"
+    bindkey h "goback"
     bindkey l "goforw"
     bindkey b prevfile
     bindkey d "$ctext yview scroll 18 units"
@@ -2350,6 +2522,7 @@
     global ctxbut
     bind $cflist $ctxbut {pop_flist_menu %W %X %Y %x %y}
     bind $ctext $ctxbut {pop_diff_menu %W %X %Y %x %y}
+    bind $ctext <Button-1> {focus %W}
 
     set maincursor [. cget -cursor]
     set textcursor [$ctext cget -cursor]
@@ -2369,6 +2542,8 @@
 	{mc "Return to mark" command gotomark}
 	{mc "Find descendant of this and mark" command find_common_desc}
 	{mc "Compare with marked commit" command compare_commits}
+	{mc "Diff this -> marked commit" command {diffvsmark 0}}
+	{mc "Diff marked commit -> this" command {diffvsmark 1}}
     }
     $rowctxmenu configure -tearoff 0
 
@@ -2377,6 +2552,8 @@
 	{mc "Diff this -> selected" command {diffvssel 0}}
 	{mc "Diff selected -> this" command {diffvssel 1}}
 	{mc "Make patch" command mkpatch}
+	{mc "Diff this -> marked commit" command {diffvsmark 0}}
+	{mc "Diff marked commit -> this" command {diffvsmark 1}}
     }
     $fakerowmenu configure -tearoff 0
 
@@ -2483,7 +2660,12 @@
 proc adjustprogress {} {
     global progresscanv progressitem progresscoords
     global fprogitem fprogcoord lastprogupdate progupdatepending
-    global rprogitem rprogcoord
+    global rprogitem rprogcoord use_ttk
+
+    if {$use_ttk} {
+	$progresscanv configure -value [expr {int($fprogcoord * 100)}]
+	return
+    }
 
     set w [expr {[winfo width $progresscanv] - 4}]
     set x0 [expr {$w * [lindex $progresscoords 0]}]
@@ -2518,12 +2700,14 @@
     global maxwidth showneartags showlocalchanges
     global viewname viewfiles viewargs viewargscmd viewperm nextviewnum
     global cmitmode wrapcomment datetimeformat limitdiffs
-    global colors bgcolor fgcolor diffcolors diffcontext selectbgcolor
-    global autoselect extdifftool perfile_attrs markbgcolor
+    global colors uicolor bgcolor fgcolor diffcolors diffcontext selectbgcolor
+    global autoselect autosellen extdifftool perfile_attrs markbgcolor use_ttk
+    global hideremotes want_ttk
 
     if {$stuffsaved} return
     if {![winfo viewable .]} return
     catch {
+	if {[file exists ~/.gitk-new]} {file delete -force ~/.gitk-new}
 	set f [open "~/.gitk-new" w]
 	if {$::tcl_platform(platform) eq {windows}} {
 	    file attributes "~/.gitk-new" -hidden true
@@ -2538,10 +2722,14 @@
 	puts $f [list set cmitmode $cmitmode]
 	puts $f [list set wrapcomment $wrapcomment]
 	puts $f [list set autoselect $autoselect]
+	puts $f [list set autosellen $autosellen]
 	puts $f [list set showneartags $showneartags]
+	puts $f [list set hideremotes $hideremotes]
 	puts $f [list set showlocalchanges $showlocalchanges]
 	puts $f [list set datetimeformat $datetimeformat]
 	puts $f [list set limitdiffs $limitdiffs]
+	puts $f [list set uicolor $uicolor]
+	puts $f [list set want_ttk $want_ttk]
 	puts $f [list set bgcolor $bgcolor]
 	puts $f [list set fgcolor $fgcolor]
 	puts $f [list set colors $colors]
@@ -2556,8 +2744,13 @@
 	puts $f "set geometry(state) [wm state .]"
 	puts $f "set geometry(topwidth) [winfo width .tf]"
 	puts $f "set geometry(topheight) [winfo height .tf]"
-        puts $f "set geometry(pwsash0) \"[.tf.histframe.pwclist sash coord 0]\""
-        puts $f "set geometry(pwsash1) \"[.tf.histframe.pwclist sash coord 1]\""
+	if {$use_ttk} {
+	    puts $f "set geometry(pwsash0) \"[.tf.histframe.pwclist sashpos 0] 1\""
+	    puts $f "set geometry(pwsash1) \"[.tf.histframe.pwclist sashpos 1] 1\""
+	} else {
+	    puts $f "set geometry(pwsash0) \"[.tf.histframe.pwclist sash coord 0]\""
+	    puts $f "set geometry(pwsash1) \"[.tf.histframe.pwclist sash coord 1]\""
+	}
 	puts $f "set geometry(botwidth) [winfo width .bleft]"
 	puts $f "set geometry(botheight) [winfo height .bleft]"
 
@@ -2575,10 +2768,15 @@
 }
 
 proc resizeclistpanes {win w} {
-    global oldwidth
+    global oldwidth use_ttk
     if {[info exists oldwidth($win)]} {
-	set s0 [$win sash coord 0]
-	set s1 [$win sash coord 1]
+	if {$use_ttk} {
+	    set s0 [$win sashpos 0]
+	    set s1 [$win sashpos 1]
+	} else {
+	    set s0 [$win sash coord 0]
+	    set s1 [$win sash coord 1]
+	}
 	if {$w < 60} {
 	    set sash0 [expr {int($w/2 - 2)}]
 	    set sash1 [expr {int($w*5/6 - 2)}]
@@ -2599,16 +2797,25 @@
 		}
 	    }
 	}
-	$win sash place 0 $sash0 [lindex $s0 1]
-	$win sash place 1 $sash1 [lindex $s1 1]
+	if {$use_ttk} {
+	    $win sashpos 0 $sash0
+	    $win sashpos 1 $sash1
+	} else {
+	    $win sash place 0 $sash0 [lindex $s0 1]
+	    $win sash place 1 $sash1 [lindex $s1 1]
+	}
     }
     set oldwidth($win) $w
 }
 
 proc resizecdetpanes {win w} {
-    global oldwidth
+    global oldwidth use_ttk
     if {[info exists oldwidth($win)]} {
-	set s0 [$win sash coord 0]
+	if {$use_ttk} {
+	    set s0 [$win sashpos 0]
+	} else {
+	    set s0 [$win sash coord 0]
+	}
 	if {$w < 60} {
 	    set sash0 [expr {int($w*3/4 - 2)}]
 	} else {
@@ -2621,7 +2828,11 @@
 		set sash0 [expr {$w - 15}]
 	    }
 	}
-	$win sash place 0 $sash0 [lindex $s0 1]
+	if {$use_ttk} {
+	    $win sashpos 0 $sash0
+	} else {
+	    $win sash place 0 $sash0 [lindex $s0 1]
+	}
     }
     set oldwidth($win) $w
 }
@@ -2641,31 +2852,33 @@
 }
 
 proc about {} {
-    global uifont
+    global uifont NS
     set w .about
     if {[winfo exists $w]} {
 	raise $w
 	return
     }
-    toplevel $w
+    ttk_toplevel $w
     wm title $w [mc "About gitk"]
     make_transient $w .
     message $w.m -text [mc "
 Gitk - a commit viewer for git
 
-Copyright © 2005-2008 Paul Mackerras
+Copyright \u00a9 2005-2011 Paul Mackerras
 
 Use and redistribute under the terms of the GNU General Public License"] \
 	    -justify center -aspect 400 -border 2 -bg white -relief groove
     pack $w.m -side top -fill x -padx 2 -pady 2
-    button $w.ok -text [mc "Close"] -command "destroy $w" -default active
+    ${NS}::button $w.ok -text [mc "Close"] -command "destroy $w" -default active
     pack $w.ok -side bottom
     bind $w <Visibility> "focus $w.ok"
     bind $w <Key-Escape> "destroy $w"
     bind $w <Key-Return> "destroy $w"
+    tk::PlaceWindow $w widget .
 }
 
 proc keys {} {
+    global NS
     set w .keys
     if {[winfo exists $w]} {
 	raise $w
@@ -2676,18 +2889,19 @@
     } else {
 	set M1T Ctrl
     }
-    toplevel $w
+    ttk_toplevel $w
     wm title $w [mc "Gitk key bindings"]
     make_transient $w .
     message $w.m -text "
 [mc "Gitk key bindings:"]
 
 [mc "<%s-Q>		Quit" $M1T]
+[mc "<%s-W>		Close window" $M1T]
 [mc "<Home>		Move to first commit"]
 [mc "<End>		Move to last commit"]
-[mc "<Up>, p, i	Move up one commit"]
-[mc "<Down>, n, k	Move down one commit"]
-[mc "<Left>, z, j	Go back in history list"]
+[mc "<Up>, p, k	Move up one commit"]
+[mc "<Down>, n, j	Move down one commit"]
+[mc "<Left>, z, h	Go back in history list"]
 [mc "<Right>, x, l	Go forward in history list"]
 [mc "<PageUp>	Move up one page in commit list"]
 [mc "<PageDown>	Move down one page in commit list"]
@@ -2720,7 +2934,7 @@
 " \
 	    -justify left -bg white -border 2 -relief groove
     pack $w.m -side top -fill both -padx 2 -pady 2
-    button $w.ok -text [mc "Close"] -command "destroy $w" -default active
+    ${NS}::button $w.ok -text [mc "Close"] -command "destroy $w" -default active
     bind $w <Key-Escape> [list destroy $w]
     pack $w.ok -side bottom
     bind $w <Visibility> "focus $w.ok"
@@ -3164,6 +3378,27 @@
     set gdttype [mc "touching paths:"]
 }
 
+proc gitknewtmpdir {} {
+    global diffnum gitktmpdir gitdir
+
+    if {![info exists gitktmpdir]} {
+	set gitktmpdir [file join $gitdir [format ".gitk-tmp.%s" [pid]]]
+	if {[catch {file mkdir $gitktmpdir} err]} {
+	    error_popup "[mc "Error creating temporary directory %s:" $gitktmpdir] $err"
+	    unset gitktmpdir
+	    return {}
+	}
+	set diffnum 0
+    }
+    incr diffnum
+    set diffdir [file join $gitktmpdir $diffnum]
+    if {[catch {file mkdir $diffdir} err]} {
+	error_popup "[mc "Error creating temporary directory %s:" $diffdir] $err"
+	return {}
+    }
+    return $diffdir
+}
+
 proc save_file_from_commit {filename output what} {
     global nullfile
 
@@ -3179,10 +3414,10 @@
 
 proc external_diff_get_one_file {diffid filename diffdir} {
     global nullid nullid2 nullfile
-    global gitdir
+    global worktree
 
     if {$diffid == $nullid} {
-        set difffile [file join [file dirname $gitdir] $filename]
+        set difffile [file join $worktree $filename]
 	if {[file exists $difffile]} {
 	    return $difffile
 	}
@@ -3198,11 +3433,10 @@
 }
 
 proc external_diff {} {
-    global gitktmpdir nullid nullid2
+    global nullid nullid2
     global flist_menu_file
     global diffids
-    global diffnum
-    global gitdir extdifftool
+    global extdifftool
 
     if {[llength $diffids] == 1} {
         # no reference commit given
@@ -3224,22 +3458,8 @@
     }
 
     # make sure that several diffs wont collide
-    if {![info exists gitktmpdir]} {
-	set gitktmpdir [file join [file dirname $gitdir] \
-			    [format ".gitk-tmp.%s" [pid]]]
-	if {[catch {file mkdir $gitktmpdir} err]} {
-	    error_popup "[mc "Error creating temporary directory %s:" $gitktmpdir] $err"
-	    unset gitktmpdir
-	    return
-	}
-	set diffnum 0
-    }
-    incr diffnum
-    set diffdir [file join $gitktmpdir $diffnum]
-    if {[catch {file mkdir $diffdir} err]} {
-	error_popup "[mc "Error creating temporary directory %s:" $diffdir] $err"
-	return
-    }
+    set diffdir [gitknewtmpdir]
+    if {$diffdir eq {}} return
 
     # gather files to diff
     set difffromfile [external_diff_get_one_file $diffidfrom $flist_menu_file $diffdir]
@@ -3366,6 +3586,9 @@
 
 # Turn an absolute path into one relative to the current directory
 proc make_relative {f} {
+    if {[file pathtype $f] eq "relative"} {
+	return $f
+    }
     set elts [file split $f]
     set here [file split [pwd]]
     set ei 0
@@ -3384,7 +3607,7 @@
 }
 
 proc external_blame {parent_idx {line {}}} {
-    global flist_menu_file gitdir
+    global flist_menu_file cdup
     global nullid nullid2
     global parentlist selectedline currentid
 
@@ -3403,7 +3626,7 @@
     if {$line ne {} && $line > 1} {
 	lappend cmdline "--line=$line"
     }
-    set f [file join [file dirname $gitdir] $flist_menu_file]
+    set f [file join $cdup $flist_menu_file]
     # Unfortunately it seems git gui blame doesn't like
     # being given an absolute path...
     set f [make_relative $f]
@@ -3416,7 +3639,7 @@
 proc show_line_source {} {
     global cmitmode currentid parents curview blamestuff blameinst
     global diff_menu_line diff_menu_filebase flist_menu_file
-    global nullid nullid2 gitdir
+    global nullid nullid2 gitdir cdup
 
     set from_index {}
     if {$cmitmode eq "tree"} {
@@ -3469,7 +3692,7 @@
     } else {
 	lappend blameargs $id
     }
-    lappend blameargs -- [file join [file dirname $gitdir] $flist_menu_file]
+    lappend blameargs -- [file join $cdup $flist_menu_file]
     if {[catch {
 	set f [open $blameargs r]
     } err]} {
@@ -3664,10 +3887,10 @@
 	raise $top
 	return
     }
+    decode_view_opts $nextviewnum $revtreeargs
     set newviewname($nextviewnum) "[mc "View"] $nextviewnum"
     set newviewopts($nextviewnum,perm) 0
     set newviewopts($nextviewnum,cmd)  $viewargscmd($curview)
-    decode_view_opts $nextviewnum $revtreeargs
     vieweditor $top $nextviewnum [mc "Gitk view definition"]
 }
 
@@ -3704,6 +3927,7 @@
     {cmd       t50= +  {}               {mc "Command to generate more commits to include:"}}
     }
 
+# Convert $newviewopts($n, ...) into args for git log.
 proc encode_view_opts {n} {
     global known_view_options newviewopts
 
@@ -3737,6 +3961,7 @@
     return [concat $rargs [shellsplit $newviewopts($n,args)]]
 }
 
+# Fill $newviewopts($n, ...) based on args for git log.
 proc decode_view_opts {n view_args} {
     global known_view_options newviewopts
 
@@ -3819,25 +4044,25 @@
 	raise $top
 	return
     }
+    decode_view_opts $curview $viewargs($curview)
     set newviewname($curview)      $viewname($curview)
     set newviewopts($curview,perm) $viewperm($curview)
     set newviewopts($curview,cmd)  $viewargscmd($curview)
-    decode_view_opts $curview $viewargs($curview)
     vieweditor $top $curview "[mc "Gitk: edit view"] $viewname($curview)"
 }
 
 proc vieweditor {top n title} {
     global newviewname newviewopts viewfiles bgcolor
-    global known_view_options
+    global known_view_options NS
 
-    toplevel $top
+    ttk_toplevel $top
     wm title $top [concat $title [mc "-- criteria for selecting revisions"]]
     make_transient $top .
 
     # View name
-    frame $top.nfr
-    label $top.nl -text [mc "View Name:"]
-    entry $top.name -width 20 -textvariable newviewname($n)
+    ${NS}::frame $top.nfr
+    ${NS}::label $top.nl -text [mc "View Name"]
+    ${NS}::entry $top.name -width 20 -textvariable newviewname($n)
     pack $top.nfr -in $top -fill x -pady 5 -padx 3
     pack $top.nl -in $top.nfr -side left -padx {0 5}
     pack $top.name -in $top.nfr -side left -padx {0 25}
@@ -3856,13 +4081,13 @@
 	if {$flags eq "+" || $flags eq "*"} {
 	    set cframe $top.fr$cnt
 	    incr cnt
-	    frame $cframe
+	    ${NS}::frame $cframe
 	    pack $cframe -in $top -fill x -pady 3 -padx 3
 	    set cexpand [expr {$flags eq "*"}]
         } elseif {$flags eq ".." || $flags eq "*."} {
 	    set cframe $top.fr$cnt
 	    incr cnt
-	    frame $cframe
+	    ${NS}::frame $cframe
 	    pack $cframe -in $top -fill x -pady 3 -padx [list 15 3]
 	    set cexpand [expr {$flags eq "*."}]
 	} else {
@@ -3870,33 +4095,33 @@
 	}
 
 	if {$type eq "l"} {
-            label $cframe.l_$id -text $title
+            ${NS}::label $cframe.l_$id -text $title
             pack $cframe.l_$id -in $cframe -side left -pady [list 3 0] -anchor w
 	} elseif {$type eq "b"} {
-	    checkbutton $cframe.c_$id -text $title -variable newviewopts($n,$id)
+	    ${NS}::checkbutton $cframe.c_$id -text $title -variable newviewopts($n,$id)
 	    pack $cframe.c_$id -in $cframe -side left \
 		-padx [list $lxpad 0] -expand $cexpand -anchor w
 	} elseif {[regexp {^r(\d+)$} $type type sz]} {
 	    regexp {^(.*_)} $id uselessvar button_id
-	    radiobutton $cframe.c_$id -text $title -variable newviewopts($n,$button_id) -value $sz
+	    ${NS}::radiobutton $cframe.c_$id -text $title -variable newviewopts($n,$button_id) -value $sz
 	    pack $cframe.c_$id -in $cframe -side left \
 		-padx [list $lxpad 0] -expand $cexpand -anchor w
 	} elseif {[regexp {^t(\d+)$} $type type sz]} {
-	    message $cframe.l_$id -aspect 1500 -text $title
-	    entry $cframe.e_$id -width $sz -background $bgcolor \
+	    ${NS}::label $cframe.l_$id -text $title
+	    ${NS}::entry $cframe.e_$id -width $sz -background $bgcolor \
 		-textvariable newviewopts($n,$id)
 	    pack $cframe.l_$id -in $cframe -side left -padx [list $lxpad 0]
 	    pack $cframe.e_$id -in $cframe -side left -expand 1 -fill x
 	} elseif {[regexp {^t(\d+)=$} $type type sz]} {
-	    message $cframe.l_$id -aspect 1500 -text $title
-	    entry $cframe.e_$id -width $sz -background $bgcolor \
+	    ${NS}::label $cframe.l_$id -text $title
+	    ${NS}::entry $cframe.e_$id -width $sz -background $bgcolor \
 		-textvariable newviewopts($n,$id)
 	    pack $cframe.l_$id -in $cframe -side top -pady [list 3 0] -anchor w
 	    pack $cframe.e_$id -in $cframe -side top -fill x
 	} elseif {$type eq "path"} {
-	    message $top.l -aspect 1500 -text $title
+	    ${NS}::label $top.l -text $title
 	    pack $top.l -in $top -side top -pady [list 3 0] -anchor w -padx 3
-	    text $top.t -width 40 -height 5 -background $bgcolor -font uifont
+	    text $top.t -width 40 -height 5 -background $bgcolor
 	    if {[info exists viewfiles($n)]} {
 		foreach f $viewfiles($n) {
 		    $top.t insert end $f
@@ -3909,10 +4134,10 @@
 	}
     }
 
-    frame $top.buts
-    button $top.buts.ok -text [mc "OK"] -command [list newviewok $top $n]
-    button $top.buts.apply -text [mc "Apply (F5)"] -command [list newviewok $top $n 1]
-    button $top.buts.can -text [mc "Cancel"] -command [list destroy $top]
+    ${NS}::frame $top.buts
+    ${NS}::button $top.buts.ok -text [mc "OK"] -command [list newviewok $top $n]
+    ${NS}::button $top.buts.apply -text [mc "Apply (F5)"] -command [list newviewok $top $n 1]
+    ${NS}::button $top.buts.can -text [mc "Cancel"] -command [list destroy $top]
     bind $top <Control-Return> [list newviewok $top $n]
     bind $top <F5> [list newviewok $top $n 1]
     bind $top <Escape> [list destroy $top]
@@ -4352,12 +4577,22 @@
 
 proc do_file_hl {serial} {
     global highlight_files filehighlight highlight_paths gdttype fhl_list
+    global cdup findtype
 
     if {$gdttype eq [mc "touching paths:"]} {
+	# If "exact" match then convert backslashes to forward slashes.
+	# Most useful to support Windows-flavoured file paths.
+	if {$findtype eq [mc "Exact"]} {
+	    set highlight_files [string map {"\\" "/"} $highlight_files]
+	}
 	if {[catch {set paths [shellsplit $highlight_files]}]} return
 	set highlight_paths [makepatterns $paths]
 	highlight_filelist
-	set gdtargs [concat -- $paths]
+	set relative_paths {}
+	foreach path $paths {
+	    lappend relative_paths [file join $cdup $path]
+	}
+	set gdtargs [concat -- $relative_paths]
     } elseif {$gdttype eq [mc "adding/removing string:"]} {
 	set gdtargs [list "-S$highlight_files"]
     } else {
@@ -4450,8 +4685,9 @@
     }
     set info $commitinfo($id)
     set isbold 0
-    set fldtypes [list [mc Headline] [mc Author] [mc Date] [mc Committer] [mc CDate] [mc Comments]]
+    set fldtypes [list [mc Headline] [mc Author] "" [mc Committer] "" [mc Comments]]
     foreach f $info ty $fldtypes {
+	if {$ty eq ""} continue
 	if {($findloc eq [mc "All fields"] || $findloc eq $ty) &&
 	    [doesmatch $f]} {
 	    if {$ty eq [mc "Author"]} {
@@ -4854,9 +5090,9 @@
 # spawn off a process to do git diff-index --cached HEAD
 proc dodiffindex {} {
     global lserial showlocalchanges vfilelimit curview
-    global isworktree
+    global hasworktree
 
-    if {!$showlocalchanges || !$isworktree} return
+    if {!$showlocalchanges || !$hasworktree} return
     incr lserial
     set cmd "|git diff-index --cached HEAD"
     if {$vfilelimit($curview) ne {}} {
@@ -5722,6 +5958,9 @@
 	|| [info exists idotherrefs($id)]} {
 	set xt [drawtags $id $x $xt $y]
     }
+    if {[lindex $commitinfo($id) 6] > 0} {
+	set xt [drawnotesign $xt $y]
+    }
     set headline [lindex $commitinfo($id) 0]
     set name [lindex $commitinfo($id) 1]
     set date [lindex $commitinfo($id) 2]
@@ -6124,6 +6363,7 @@
 	       -width $lthickness -fill black -tags tag.$id]
     $canv lower $t
     foreach tag $marks x $xvals wid $wvals {
+	set tag_quoted [string map {% %%} $tag]
 	set xl [expr {$x + $delta}]
 	set xr [expr {$x + $delta + $wid + $lthickness}]
 	set font mainfont
@@ -6132,7 +6372,7 @@
 	    set t [$canv create polygon $x [expr {$yt + $delta}] $xl $yt \
 		       $xr $yt $xr $yb $xl $yb $x [expr {$yb - $delta}] \
 		       -width 1 -outline black -fill yellow -tags tag.$id]
-	    $canv bind $t <1> [list showtag $tag 1]
+	    $canv bind $t <1> [list showtag $tag_quoted 1]
 	    set rowtextx([rowofcommit $id]) [expr {$xr + $linespc}]
 	} else {
 	    # draw a head or other ref
@@ -6159,14 +6399,25 @@
 	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]
+	    $canv bind $t <1> [list showtag $tag_quoted 1]
 	} elseif {$nheads >= 0} {
-	    $canv bind $t $ctxbut [list headmenu %X %Y $id $tag]
+	    $canv bind $t $ctxbut [list headmenu %X %Y $id $tag_quoted]
 	}
     }
     return $xt
 }
 
+proc drawnotesign {xt y} {
+    global linespc canv fgcolor
+
+    set orad [expr {$linespc / 3}]
+    set t [$canv create rectangle [expr {$xt - $orad}] [expr {$y - $orad}] \
+	       [expr {$xt + $orad - 1}] [expr {$y + $orad - 1}] \
+	       -fill yellow -outline $fgcolor -width 1 -tags circle]
+    set xt [expr {$xt + $orad * 3}]
+    return $xt
+}
+
 proc xcoord {i level ln} {
     global canvx0 xspc1 xspc2
 
@@ -6297,7 +6548,7 @@
     if {![info exists find_dirn]} {
 	return 0
     }
-    set fldtypes [list [mc "Headline"] [mc "Author"] [mc "Date"] [mc "Committer"] [mc "CDate"] [mc "Comments"]]
+    set fldtypes [list [mc "Headline"] [mc "Author"] "" [mc "Committer"] "" [mc "Comments"]]
     set l $findcurline
     set moretodo 0
     if {$find_dirn > 0} {
@@ -6358,6 +6609,7 @@
 	    }
 	    set info $commitinfo($id)
 	    foreach f $info ty $fldtypes {
+		if {$ty eq ""} continue
 		if {($findloc eq [mc "All fields"] || $findloc eq $ty) &&
 		    [doesmatch $f]} {
 		    set found 1
@@ -6510,7 +6762,7 @@
 
     set start [$ctext index "end - 1c"]
     $ctext insert end $text $tags
-    set links [regexp -indices -all -inline {\m[0-9a-f]{6,40}\M} $text]
+    set links [regexp -indices -all -inline {(?:\m|-g)[0-9a-f]{6,40}\M} $text]
     foreach l $links {
 	set s [lindex $l 0]
 	set e [lindex $l 1]
@@ -6526,6 +6778,10 @@
 proc setlink {id lk} {
     global curview ctext pendinglinks
 
+    if {[string range $id 0 1] eq "-g"} {
+      set id [string range $id 2 end]
+    }
+
     set known 0
     if {[string length $id] < 40} {
 	set matches [longid $id]
@@ -6720,7 +6976,7 @@
     global mergemax numcommits pending_select
     global cmitmode showneartags allcommits
     global targetrow targetid lastscrollrows
-    global autoselect jump_to_here
+    global autoselect autosellen jump_to_here
 
     catch {unset pending_select}
     $canv delete hover
@@ -6776,14 +7032,13 @@
     make_secsel $id
 
     if {$isnew} {
-	addtohistory [list selbyid $id]
+	addtohistory [list selbyid $id 0] savecmitpos
     }
 
     $sha1entry delete 0 end
     $sha1entry insert 0 $id
     if {$autoselect} {
-	$sha1entry selection from 0
-	$sha1entry selection to end
+	$sha1entry selection range 0 $autosellen
     }
     rhighlight_sel $id
 
@@ -6928,10 +7183,12 @@
     }
 }
 
-proc addtohistory {cmd} {
+proc addtohistory {cmd {saveproc {}}} {
     global history historyindex curview
 
-    set elt [list $curview $cmd]
+    unset_posvars
+    save_position
+    set elt [list $curview $cmd $saveproc {}]
     if {$historyindex > 0
 	&& [lindex $history [expr {$historyindex - 1}]] == $elt} {
 	return
@@ -6951,14 +7208,45 @@
     .tf.bar.rightbut conf -state disabled
 }
 
+# save the scrolling position of the diff display pane
+proc save_position {} {
+    global historyindex history
+
+    if {$historyindex < 1} return
+    set hi [expr {$historyindex - 1}]
+    set fn [lindex $history $hi 2]
+    if {$fn ne {}} {
+	lset history $hi 3 [eval $fn]
+    }
+}
+
+proc unset_posvars {} {
+    global last_posvars
+
+    if {[info exists last_posvars]} {
+	foreach {var val} $last_posvars {
+	    global $var
+	    catch {unset $var}
+	}
+	unset last_posvars
+    }
+}
+
 proc godo {elt} {
-    global curview
+    global curview last_posvars
 
     set view [lindex $elt 0]
     set cmd [lindex $elt 1]
+    set pv [lindex $elt 3]
     if {$curview != $view} {
 	showview $view
     }
+    unset_posvars
+    foreach {var val} $pv {
+	global $var
+	set $var $val
+    }
+    set last_posvars $pv
     eval $cmd
 }
 
@@ -6967,6 +7255,7 @@
     focus .
 
     if {$historyindex > 1} {
+	save_position
 	incr historyindex -1
 	godo [lindex $history [expr {$historyindex - 1}]]
 	.tf.bar.rightbut conf -state normal
@@ -6981,6 +7270,7 @@
     focus .
 
     if {$historyindex < [llength $history]} {
+	save_position
 	set cmd [lindex $history $historyindex]
 	incr historyindex
 	godo $cmd
@@ -7118,6 +7408,7 @@
 			    [lindex [split $commentend .] 0]}]
 	    mark_ctext_line $lnum
 	}
+	$ctext config -state disabled
 	return 0
     }
     $ctext config -state disabled
@@ -7163,19 +7454,15 @@
     }
 }
 
+# If the filename (name) is under any of the passed filter paths
+# then return true to include the file in the listing.
 proc path_filter {filter name} {
+    set worktree [gitworktree]
     foreach p $filter {
-	set l [string length $p]
-	if {[string index $p end] eq "/"} {
-	    if {[string compare -length $l $p $name] == 0} {
-		return 1
-	    }
-	} else {
-	    if {[string compare -length $l $p $name] == 0 &&
-		([string length $name] == $l ||
-		 [string index $name $l] eq "/")} {
-		return 1
-	    }
+	set fq_p [file normalize $p]
+	set fq_n [file normalize [file join $worktree $name]]
+	if {[string match [file normalize $fq_p]* $fq_n]} {
+	    return 1
 	}
     }
     return 0
@@ -7189,7 +7476,7 @@
 }
 
 proc diffcmd {ids flags} {
-    global nullid nullid2
+    global log_showroot nullid nullid2
 
     set i [lsearch -exact $ids $nullid]
     set j [lsearch -exact $ids $nullid2]
@@ -7213,7 +7500,7 @@
 	set cmd [concat | git diff-index --cached $flags]
 	if {[llength $ids] > 1} {
 	    # comparing index with specific revision
-	    if {$i == 0} {
+	    if {$j == 0} {
 		lappend cmd -R [lindex $ids 1]
 	    } else {
 		lappend cmd [lindex $ids 0]
@@ -7223,6 +7510,9 @@
 	    lappend cmd HEAD
 	}
     } else {
+	if {$log_showroot} {
+	    lappend flags --root
+	}
 	set cmd [concat | git diff-tree -r $flags $ids]
     }
     return $cmd
@@ -7319,23 +7609,35 @@
     reselectline
 }
 
+proc changeworddiff {name ix op} {
+    reselectline
+}
+
 proc getblobdiffs {ids} {
     global blobdifffd diffids env
     global diffinhdr treediffs
     global diffcontext
     global ignorespace
+    global worddiff
     global limitdiffs vfilelimit curview
     global diffencoding targetline diffnparents
-    global git_version
+    global git_version currdiffsubmod
 
     set textconv {}
     if {[package vcompare $git_version "1.6.1"] >= 0} {
 	set textconv "--textconv"
     }
-    set cmd [diffcmd $ids "-p $textconv -C --cc --no-commit-id -U$diffcontext"]
+    set submodule {}
+    if {[package vcompare $git_version "1.6.6"] >= 0} {
+	set submodule "--submodule"
+    }
+    set cmd [diffcmd $ids "-p $textconv $submodule  -C --cc --no-commit-id -U$diffcontext"]
     if {$ignorespace} {
 	append cmd " -w"
     }
+    if {$worddiff ne [mc "Line diff"]} {
+	append cmd " --word-diff=porcelain"
+    }
     if {$limitdiffs && $vfilelimit($curview) ne {}} {
 	set cmd [concat $cmd -- $vfilelimit($curview)]
     }
@@ -7349,9 +7651,38 @@
     set diffencoding [get_path_encoding {}]
     fconfigure $bdf -blocking 0 -encoding binary -eofchar {}
     set blobdifffd($ids) $bdf
+    set currdiffsubmod ""
     filerun $bdf [list getblobdiffline $bdf $diffids]
 }
 
+proc savecmitpos {} {
+    global ctext cmitmode
+
+    if {$cmitmode eq "tree"} {
+	return {}
+    }
+    return [list target_scrollpos [$ctext index @0,0]]
+}
+
+proc savectextpos {} {
+    global ctext
+
+    return [list target_scrollpos [$ctext index @0,0]]
+}
+
+proc maybe_scroll_ctext {ateof} {
+    global ctext target_scrollpos
+
+    if {![info exists target_scrollpos]} return
+    if {!$ateof} {
+	set nlines [expr {[winfo height $ctext]
+			  / [font metrics textfont -linespace]}]
+	if {[$ctext compare "$target_scrollpos + $nlines lines" <= end]} return
+    }
+    $ctext yview $target_scrollpos
+    unset target_scrollpos
+}
+
 proc setinlist {var i val} {
     global $var
 
@@ -7391,13 +7722,14 @@
     global diffnexthead diffnextnote difffilestart
     global ctext_file_names ctext_file_lines
     global diffinhdr treediffs mergemax diffnparents
-    global diffencoding jump_to_here targetline diffline
+    global diffencoding jump_to_here targetline diffline currdiffsubmod
+    global worddiff
 
     set nr 0
     $ctext conf -state normal
     while {[incr nr] <= 1000 && [gets $bdf line] >= 0} {
 	if {$ids != $diffids || $bdf != $blobdifffd($ids)} {
-	    close $bdf
+	    catch {close $bdf}
 	    return 0
 	}
 	if {![string compare -length 5 "diff " $line]} {
@@ -7470,6 +7802,34 @@
 	    set diffnparents [expr {[string length $ats] - 1}]
 	    set diffinhdr 0
 
+	} elseif {![string compare -length 10 "Submodule " $line]} {
+	    # start of a new submodule
+	    if {[regexp -indices "\[0-9a-f\]+\\.\\." $line nameend]} {
+		set fname [string range $line 10 [expr [lindex $nameend 0] - 2]]
+	    } else {
+		set fname [string range $line 10 [expr [string first "contains " $line] - 2]]
+	    }
+	    if {$currdiffsubmod != $fname} {
+		$ctext insert end "\n";     # Add newline after commit message
+	    }
+	    set curdiffstart [$ctext index "end - 1c"]
+	    lappend ctext_file_names ""
+	    if {$currdiffsubmod != $fname} {
+		lappend ctext_file_lines $fname
+		makediffhdr $fname $ids
+		set currdiffsubmod $fname
+		$ctext insert end "\n$line\n" filesep
+	    } else {
+		$ctext insert end "$line\n" filesep
+	    }
+	} elseif {![string compare -length 3 "  >" $line]} {
+	    set $currdiffsubmod ""
+	    set line [encoding convertfrom $diffencoding $line]
+	    $ctext insert end "$line\n" dresult
+	} elseif {![string compare -length 3 "  <" $line]} {
+	    set $currdiffsubmod ""
+	    set line [encoding convertfrom $diffencoding $line]
+	    $ctext insert end "$line\n" d0
 	} elseif {$diffinhdr} {
 	    if {![string compare -length 12 "rename from " $line]} {
 		set fname [string range $line [expr 6 + [string first " from " $line] ] end]
@@ -7503,15 +7863,28 @@
 	    # parse the prefix - one ' ', '-' or '+' for each parent
 	    set prefix [string range $line 0 [expr {$diffnparents - 1}]]
 	    set tag [expr {$diffnparents > 1? "m": "d"}]
+	    set dowords [expr {$worddiff ne [mc "Line diff"] && $diffnparents == 1}]
+	    set words_pre_markup ""
+	    set words_post_markup ""
 	    if {[string trim $prefix " -+"] eq {}} {
 		# prefix only has " ", "-" and "+" in it: normal diff line
 		set num [string first "-" $prefix]
+		if {$dowords} {
+		    set line [string range $line 1 end]
+		}
 		if {$num >= 0} {
 		    # removed line, first parent with line is $num
 		    if {$num >= $mergemax} {
 			set num "max"
 		    }
-		    $ctext insert end "$line\n" $tag$num
+		    if {$dowords && $worddiff eq [mc "Markup words"]} {
+			$ctext insert end "\[-$line-\]" $tag$num
+		    } else {
+			$ctext insert end "$line" $tag$num
+		    }
+		    if {!$dowords} {
+			$ctext insert end "\n" $tag$num
+		    }
 		} else {
 		    set tags {}
 		    if {[string first "+" $prefix] >= 0} {
@@ -7526,6 +7899,8 @@
 				lappend tags m$num
 			    }
 			}
+			set words_pre_markup "{+"
+			set words_post_markup "+}"
 		    }
 		    if {$targetline ne {}} {
 			if {$diffline == $targetline} {
@@ -7535,8 +7910,17 @@
 			    incr diffline
 			}
 		    }
-		    $ctext insert end "$line\n" $tags
+		    if {$dowords && $worddiff eq [mc "Markup words"]} {
+			$ctext insert end "$words_pre_markup$line$words_post_markup" $tags
+		    } else {
+			$ctext insert end "$line" $tags
+		    }
+		    if {!$dowords} {
+			$ctext insert end "\n" $tags
+		    }
 		}
+	    } elseif {$dowords && $prefix eq "~"} {
+		$ctext insert end "\n" {}
 	    } else {
 		# "\ No newline at end of file",
 		# or something else we don't recognize
@@ -7547,9 +7931,10 @@
     if {[info exists seehere]} {
 	mark_ctext_line [lindex [split $seehere .] 0]
     }
+    maybe_scroll_ctext [eof $bdf]
     $ctext conf -state disabled
     if {[eof $bdf]} {
-	close $bdf
+	catch {close $bdf}
 	return 0
     }
     return [expr {$nr >= 1000? 2: 1}]
@@ -7906,6 +8291,11 @@
 		}
 		set id [lindex $matches 0]
 	    }
+	} else {
+	    if {[catch {set id [exec git rev-parse --verify $sha1string]}]} {
+		error_popup [mc "Revision %s is not known" $sha1string]
+		return
+	    }
 	}
     }
     if {[commitinview $id $curview]} {
@@ -7915,7 +8305,7 @@
     if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} {
 	set msg [mc "SHA1 id %s is not known" $sha1string]
     } else {
-	set msg [mc "Tag/Head %s is not known" $sha1string]
+	set msg [mc "Revision %s is not in the current view" $sha1string]
     }
     error_popup $msg
 }
@@ -8041,7 +8431,7 @@
     }
 
     if {$isnew} {
-	addtohistory [list lineclick $x $y $id 0]
+	addtohistory [list lineclick $x $y $id 0] savectextpos
     }
     # fill the details pane with info about this line
     $ctext conf -state normal
@@ -8072,6 +8462,7 @@
 	    $ctext insert end "\n\t[mc "Date"]:\t$date\n"
 	}
     }
+    maybe_scroll_ctext 1
     $ctext conf -state disabled
     init_flist {}
 }
@@ -8085,10 +8476,10 @@
     }
 }
 
-proc selbyid {id} {
+proc selbyid {id {isnew 1}} {
     global curview
     if {[commitinview $id $curview]} {
-	selectline [rowofcommit $id] 1
+	selectline [rowofcommit $id] $isnew
     }
 }
 
@@ -8111,6 +8502,11 @@
     } else {
 	set state normal
     }
+    if {[info exists markedid] && $markedid ne $id} {
+	set mstate normal
+    } else {
+	set mstate disabled
+    }
     if {$id ne $nullid && $id ne $nullid2} {
 	set menu $rowctxmenu
 	if {$mainhead ne {}} {
@@ -8118,21 +8514,17 @@
 	} else {
 	    $menu entryconfigure 7 -label [mc "Detached head: can't reset" $mainhead] -state disabled
 	}
-	if {[info exists markedid] && $markedid ne $id} {
-	    $menu entryconfigure 9 -state normal
-	    $menu entryconfigure 10 -state normal
-	    $menu entryconfigure 11 -state normal
-	} else {
-	    $menu entryconfigure 9 -state disabled
-	    $menu entryconfigure 10 -state disabled
-	    $menu entryconfigure 11 -state disabled
-	}
+	$menu entryconfigure 9 -state $mstate
+	$menu entryconfigure 10 -state $mstate
+	$menu entryconfigure 11 -state $mstate
     } else {
 	set menu $fakerowmenu
     }
     $menu entryconfigure [mca "Diff this -> selected"] -state $state
     $menu entryconfigure [mca "Diff selected -> this"] -state $state
     $menu entryconfigure [mca "Make patch"] -state $state
+    $menu entryconfigure [mca "Diff this -> marked commit"] -state $mstate
+    $menu entryconfigure [mca "Diff marked commit -> this"] -state $mstate
     tk_popup $menu $x $y
 }
 
@@ -8265,33 +8657,62 @@
 		appendshortlink $a [mc "Commit "] "  $heada\n"
 		appendshortlink $b [mc " differs from\n       "] \
 		    "  $headb\n"
-		$ctext insert end [mc "- stopping\n"]
-		break
+		$ctext insert end [mc "Diff of commits:\n\n"]
+		$ctext conf -state disabled
+		update
+		diffcommits $a $b
+		return
 	    }
 	}
 	if {$skipa} {
-	    if {[llength $children($curview,$a)] != 1} {
+	    set kids [real_children $curview,$a]
+	    if {[llength $kids] != 1} {
 		$ctext insert end "\n"
 		appendshortlink $a [mc "Commit "] \
-		    [mc " has %s children - stopping\n" \
-			 [llength $children($curview,$a)]]
+		    [mc " has %s children - stopping\n" [llength $kids]]
 		break
 	    }
-	    set a [lindex $children($curview,$a) 0]
+	    set a [lindex $kids 0]
 	}
 	if {$skipb} {
-	    if {[llength $children($curview,$b)] != 1} {
+	    set kids [real_children $curview,$b]
+	    if {[llength $kids] != 1} {
 		appendshortlink $b [mc "Commit "] \
-		    [mc " has %s children - stopping\n" \
-			 [llength $children($curview,$b)]]
+		    [mc " has %s children - stopping\n" [llength $kids]]
 		break
 	    }
-	    set b [lindex $children($curview,$b) 0]
+	    set b [lindex $kids 0]
 	}
     }
     $ctext conf -state disabled
 }
 
+proc diffcommits {a b} {
+    global diffcontext diffids blobdifffd diffinhdr currdiffsubmod
+
+    set tmpdir [gitknewtmpdir]
+    set fna [file join $tmpdir "commit-[string range $a 0 7]"]
+    set fnb [file join $tmpdir "commit-[string range $b 0 7]"]
+    if {[catch {
+	exec git diff-tree -p --pretty $a >$fna
+	exec git diff-tree -p --pretty $b >$fnb
+    } err]} {
+	error_popup [mc "Error writing commit to file: %s" $err]
+	return
+    }
+    if {[catch {
+	set fd [open "| diff -U$diffcontext $fna $fnb" r]
+    } err]} {
+	error_popup [mc "Error diffing commits: %s" $err]
+	return
+    }
+    set diffids [list commits $a $b]
+    set blobdifffd($diffids) $fd
+    set diffinhdr 0
+    set currdiffsubmod ""
+    filerun $fd [list getblobdiffline $fd $diffids]
+}
+
 proc diffvssel {dirn} {
     global rowmenuid selectedline
 
@@ -8303,7 +8724,22 @@
 	set oldid $rowmenuid
 	set newid [commitonrow $selectedline]
     }
-    addtohistory [list doseldiff $oldid $newid]
+    addtohistory [list doseldiff $oldid $newid] savectextpos
+    doseldiff $oldid $newid
+}
+
+proc diffvsmark {dirn} {
+    global rowmenuid markedid
+
+    if {![info exists markedid]} return
+    if {$dirn} {
+	set oldid $markedid
+	set newid $rowmenuid
+    } else {
+	set oldid $rowmenuid
+	set newid $markedid
+    }
+    addtohistory [list doseldiff $oldid $newid] savectextpos
     doseldiff $oldid $newid
 }
 
@@ -8331,7 +8767,7 @@
 }
 
 proc mkpatch {} {
-    global rowmenuid currentid commitinfo patchtop patchnum
+    global rowmenuid currentid commitinfo patchtop patchnum NS
 
     if {![info exists currentid]} return
     set oldid $currentid
@@ -8341,38 +8777,38 @@
     set top .patch
     set patchtop $top
     catch {destroy $top}
-    toplevel $top
+    ttk_toplevel $top
     make_transient $top .
-    label $top.title -text [mc "Generate patch"]
+    ${NS}::label $top.title -text [mc "Generate patch"]
     grid $top.title - -pady 10
-    label $top.from -text [mc "From:"]
-    entry $top.fromsha1 -width 40 -relief flat
+    ${NS}::label $top.from -text [mc "From:"]
+    ${NS}::entry $top.fromsha1 -width 40
     $top.fromsha1 insert 0 $oldid
     $top.fromsha1 conf -state readonly
     grid $top.from $top.fromsha1 -sticky w
-    entry $top.fromhead -width 60 -relief flat
+    ${NS}::entry $top.fromhead -width 60
     $top.fromhead insert 0 $oldhead
     $top.fromhead conf -state readonly
     grid x $top.fromhead -sticky w
-    label $top.to -text [mc "To:"]
-    entry $top.tosha1 -width 40 -relief flat
+    ${NS}::label $top.to -text [mc "To:"]
+    ${NS}::entry $top.tosha1 -width 40
     $top.tosha1 insert 0 $newid
     $top.tosha1 conf -state readonly
     grid $top.to $top.tosha1 -sticky w
-    entry $top.tohead -width 60 -relief flat
+    ${NS}::entry $top.tohead -width 60
     $top.tohead insert 0 $newhead
     $top.tohead conf -state readonly
     grid x $top.tohead -sticky w
-    button $top.rev -text [mc "Reverse"] -command mkpatchrev -padx 5
-    grid $top.rev x -pady 10
-    label $top.flab -text [mc "Output file:"]
-    entry $top.fname -width 60
+    ${NS}::button $top.rev -text [mc "Reverse"] -command mkpatchrev
+    grid $top.rev x -pady 10 -padx 5
+    ${NS}::label $top.flab -text [mc "Output file:"]
+    ${NS}::entry $top.fname -width 60
     $top.fname insert 0 [file normalize "patch$patchnum.patch"]
     incr patchnum
     grid $top.flab $top.fname -sticky w
-    frame $top.buts
-    button $top.buts.gen -text [mc "Generate"] -command mkpatchgo
-    button $top.buts.can -text [mc "Cancel"] -command mkpatchcan
+    ${NS}::frame $top.buts
+    ${NS}::button $top.buts.gen -text [mc "Generate"] -command mkpatchgo
+    ${NS}::button $top.buts.can -text [mc "Cancel"] -command mkpatchcan
     bind $top <Key-Return> mkpatchgo
     bind $top <Key-Escape> mkpatchcan
     grid $top.buts.gen $top.buts.can
@@ -8423,30 +8859,35 @@
 }
 
 proc mktag {} {
-    global rowmenuid mktagtop commitinfo
+    global rowmenuid mktagtop commitinfo NS
 
     set top .maketag
     set mktagtop $top
     catch {destroy $top}
-    toplevel $top
+    ttk_toplevel $top
     make_transient $top .
-    label $top.title -text [mc "Create tag"]
+    ${NS}::label $top.title -text [mc "Create tag"]
     grid $top.title - -pady 10
-    label $top.id -text [mc "ID:"]
-    entry $top.sha1 -width 40 -relief flat
+    ${NS}::label $top.id -text [mc "ID:"]
+    ${NS}::entry $top.sha1 -width 40
     $top.sha1 insert 0 $rowmenuid
     $top.sha1 conf -state readonly
     grid $top.id $top.sha1 -sticky w
-    entry $top.head -width 60 -relief flat
+    ${NS}::entry $top.head -width 60
     $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
     $top.head conf -state readonly
     grid x $top.head -sticky w
-    label $top.tlab -text [mc "Tag name:"]
-    entry $top.tag -width 60
+    ${NS}::label $top.tlab -text [mc "Tag name:"]
+    ${NS}::entry $top.tag -width 60
     grid $top.tlab $top.tag -sticky w
-    frame $top.buts
-    button $top.buts.gen -text [mc "Create"] -command mktaggo
-    button $top.buts.can -text [mc "Cancel"] -command mktagcan
+    ${NS}::label $top.op -text [mc "Tag message is optional"]
+    grid $top.op -columnspan 2 -sticky we
+    ${NS}::label $top.mlab -text [mc "Tag message:"]
+    ${NS}::entry $top.msg -width 60
+    grid $top.mlab $top.msg -sticky w
+    ${NS}::frame $top.buts
+    ${NS}::button $top.buts.gen -text [mc "Create"] -command mktaggo
+    ${NS}::button $top.buts.can -text [mc "Cancel"] -command mktagcan
     bind $top <Key-Return> mktaggo
     bind $top <Key-Escape> mktagcan
     grid $top.buts.gen $top.buts.can
@@ -8461,6 +8902,7 @@
 
     set id [$mktagtop.sha1 get]
     set tag [$mktagtop.tag get]
+    set msg [$mktagtop.msg get]
     if {$tag == {}} {
 	error_popup [mc "No tag name specified"] $mktagtop
 	return 0
@@ -8470,7 +8912,11 @@
 	return 0
     }
     if {[catch {
-	exec git tag $tag $id
+	if {$msg != {}} {
+	    exec git tag -a -m $msg $tag $id
+	} else {
+	    exec git tag $tag $id
+	}
     } err]} {
 	error_popup "[mc "Error creating tag:"] $err" $mktagtop
 	return 0
@@ -8529,34 +8975,34 @@
 }
 
 proc writecommit {} {
-    global rowmenuid wrcomtop commitinfo wrcomcmd
+    global rowmenuid wrcomtop commitinfo wrcomcmd NS
 
     set top .writecommit
     set wrcomtop $top
     catch {destroy $top}
-    toplevel $top
+    ttk_toplevel $top
     make_transient $top .
-    label $top.title -text [mc "Write commit to file"]
+    ${NS}::label $top.title -text [mc "Write commit to file"]
     grid $top.title - -pady 10
-    label $top.id -text [mc "ID:"]
-    entry $top.sha1 -width 40 -relief flat
+    ${NS}::label $top.id -text [mc "ID:"]
+    ${NS}::entry $top.sha1 -width 40
     $top.sha1 insert 0 $rowmenuid
     $top.sha1 conf -state readonly
     grid $top.id $top.sha1 -sticky w
-    entry $top.head -width 60 -relief flat
+    ${NS}::entry $top.head -width 60
     $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
     $top.head conf -state readonly
     grid x $top.head -sticky w
-    label $top.clab -text [mc "Command:"]
-    entry $top.cmd -width 60 -textvariable wrcomcmd
+    ${NS}::label $top.clab -text [mc "Command:"]
+    ${NS}::entry $top.cmd -width 60 -textvariable wrcomcmd
     grid $top.clab $top.cmd -sticky w -pady 10
-    label $top.flab -text [mc "Output file:"]
-    entry $top.fname -width 60
+    ${NS}::label $top.flab -text [mc "Output file:"]
+    ${NS}::entry $top.fname -width 60
     $top.fname insert 0 [file normalize "commit-[string range $rowmenuid 0 6]"]
     grid $top.flab $top.fname -sticky w
-    frame $top.buts
-    button $top.buts.gen -text [mc "Write"] -command wrcomgo
-    button $top.buts.can -text [mc "Cancel"] -command wrcomcan
+    ${NS}::frame $top.buts
+    ${NS}::button $top.buts.gen -text [mc "Write"] -command wrcomgo
+    ${NS}::button $top.buts.can -text [mc "Cancel"] -command wrcomcan
     bind $top <Key-Return> wrcomgo
     bind $top <Key-Escape> wrcomcan
     grid $top.buts.gen $top.buts.can
@@ -8587,25 +9033,25 @@
 }
 
 proc mkbranch {} {
-    global rowmenuid mkbrtop
+    global rowmenuid mkbrtop NS
 
     set top .makebranch
     catch {destroy $top}
-    toplevel $top
+    ttk_toplevel $top
     make_transient $top .
-    label $top.title -text [mc "Create new branch"]
+    ${NS}::label $top.title -text [mc "Create new branch"]
     grid $top.title - -pady 10
-    label $top.id -text [mc "ID:"]
-    entry $top.sha1 -width 40 -relief flat
+    ${NS}::label $top.id -text [mc "ID:"]
+    ${NS}::entry $top.sha1 -width 40
     $top.sha1 insert 0 $rowmenuid
     $top.sha1 conf -state readonly
     grid $top.id $top.sha1 -sticky w
-    label $top.nlab -text [mc "Name:"]
-    entry $top.name -width 40
+    ${NS}::label $top.nlab -text [mc "Name:"]
+    ${NS}::entry $top.name -width 40
     grid $top.nlab $top.name -sticky w
-    frame $top.buts
-    button $top.buts.go -text [mc "Create"] -command [list mkbrgo $top]
-    button $top.buts.can -text [mc "Cancel"] -command "catch {destroy $top}"
+    ${NS}::frame $top.buts
+    ${NS}::button $top.buts.go -text [mc "Create"] -command [list mkbrgo $top]
+    ${NS}::button $top.buts.can -text [mc "Cancel"] -command "catch {destroy $top}"
     bind $top <Key-Return> [list mkbrgo $top]
     bind $top <Key-Escape> "catch {destroy $top}"
     grid $top.buts.go $top.buts.can
@@ -8690,6 +9136,7 @@
 proc cherrypick {} {
     global rowmenuid curview
     global mainhead mainheadid
+    global gitdir
 
     set oldhead [exec git rev-parse HEAD]
     set dheads [descheads $rowmenuid]
@@ -8712,13 +9159,13 @@
 			to file '%s'.\nPlease commit, reset or stash\
 			your changes and try again." $fname]
 	} elseif {[regexp -line \
-		       {^(CONFLICT \(.*\):|Automatic cherry-pick failed)} \
+		       {^(CONFLICT \(.*\):|Automatic cherry-pick failed|error: could not apply)} \
 		       $err]} {
 	    if {[confirm_popup [mc "Cherry-pick failed because of merge\
 			conflict.\nDo you wish to run git citool to\
 			resolve it?"]]} {
 		# Force citool to read MERGE_MSG
-		file delete [file join [gitdir] "GITGUI_MSG"]
+		file delete [file join $gitdir "GITGUI_MSG"]
 		exec_citool {} $rowmenuid
 	    }
 	} else {
@@ -8750,34 +9197,31 @@
 }
 
 proc resethead {} {
-    global mainhead rowmenuid confirm_ok resettype
+    global mainhead rowmenuid confirm_ok resettype NS
 
     set confirm_ok 0
     set w ".confirmreset"
-    toplevel $w
+    ttk_toplevel $w
     make_transient $w .
     wm title $w [mc "Confirm reset"]
-    message $w.m -text \
-	[mc "Reset branch %s to %s?" $mainhead [string range $rowmenuid 0 7]] \
-	-justify center -aspect 1000
+    ${NS}::label $w.m -text \
+	[mc "Reset branch %s to %s?" $mainhead [string range $rowmenuid 0 7]]
     pack $w.m -side top -fill x -padx 20 -pady 20
-    frame $w.f -relief sunken -border 2
-    message $w.f.rt -text [mc "Reset type:"] -aspect 1000
-    grid $w.f.rt -sticky w
+    ${NS}::labelframe $w.f -text [mc "Reset type:"]
     set resettype mixed
-    radiobutton $w.f.soft -value soft -variable resettype -justify left \
+    ${NS}::radiobutton $w.f.soft -value soft -variable resettype \
 	-text [mc "Soft: Leave working tree and index untouched"]
     grid $w.f.soft -sticky w
-    radiobutton $w.f.mixed -value mixed -variable resettype -justify left \
+    ${NS}::radiobutton $w.f.mixed -value mixed -variable resettype \
 	-text [mc "Mixed: Leave working tree untouched, reset index"]
     grid $w.f.mixed -sticky w
-    radiobutton $w.f.hard -value hard -variable resettype -justify left \
+    ${NS}::radiobutton $w.f.hard -value hard -variable resettype \
 	-text [mc "Hard: Reset working tree and index\n(discard ALL local changes)"]
     grid $w.f.hard -sticky w
-    pack $w.f -side top -fill x
-    button $w.ok -text [mc OK] -command "set confirm_ok 1; destroy $w"
+    pack $w.f -side top -fill x -padx 4
+    ${NS}::button $w.ok -text [mc OK] -command "set confirm_ok 1; destroy $w"
     pack $w.ok -side left -fill x -padx 20 -pady 20
-    button $w.cancel -text [mc Cancel] -command "destroy $w"
+    ${NS}::button $w.cancel -text [mc Cancel] -command "destroy $w"
     bind $w <Key-Escape> [list destroy $w]
     pack $w.cancel -side right -fill x -padx 20 -pady 20
     bind $w <Visibility> "grab $w; focus $w"
@@ -8833,6 +9277,9 @@
     set headmenuid $id
     set headmenuhead $head
     set state normal
+    if {[string match "remotes/*" $head]} {
+	set state disabled
+    }
     if {$head eq $mainhead} {
 	set state disabled
     }
@@ -8925,7 +9372,7 @@
 
 # Display a list of tags and heads
 proc showrefs {} {
-    global showrefstop bgcolor fgcolor selectbgcolor
+    global showrefstop bgcolor fgcolor selectbgcolor NS
     global bglist fglist reflistfilter reflist maincursor
 
     set top .showrefs
@@ -8935,7 +9382,7 @@
 	refill_reflist
 	return
     }
-    toplevel $top
+    ttk_toplevel $top
     wm title $top [mc "Tags and heads: %s" [file tail [pwd]]]
     make_transient $top .
     text $top.list -background $bgcolor -foreground $fgcolor \
@@ -8946,19 +9393,19 @@
     $top.list tag configure highlight -background $selectbgcolor
     lappend bglist $top.list
     lappend fglist $top.list
-    scrollbar $top.ysb -command "$top.list yview" -orient vertical
-    scrollbar $top.xsb -command "$top.list xview" -orient horizontal
+    ${NS}::scrollbar $top.ysb -command "$top.list yview" -orient vertical
+    ${NS}::scrollbar $top.xsb -command "$top.list xview" -orient horizontal
     grid $top.list $top.ysb -sticky nsew
     grid $top.xsb x -sticky ew
-    frame $top.f
-    label $top.f.l -text "[mc "Filter"]: "
-    entry $top.f.e -width 20 -textvariable reflistfilter
+    ${NS}::frame $top.f
+    ${NS}::label $top.f.l -text "[mc "Filter"]: "
+    ${NS}::entry $top.f.e -width 20 -textvariable reflistfilter
     set reflistfilter "*"
     trace add variable reflistfilter write reflistfilter_change
     pack $top.f.e -side right -fill x -expand 1
     pack $top.f.l -side left
     grid $top.f - -sticky ew -pady 2
-    button $top.close -command [list destroy $top] -text [mc "Close"]
+    ${NS}::button $top.close -command [list destroy $top] -text [mc "Close"]
     bind $top <Key-Escape> [list destroy $top]
     grid $top.close -
     grid columnconfigure $top 0 -weight 1
@@ -9084,6 +9531,7 @@
 proc getallcommits {} {
     global allcommits nextarc seeds allccache allcwait cachedarcs allcupdate
     global idheads idtags idotherrefs allparents tagobjid
+    global gitdir
 
     if {![info exists allcommits]} {
 	set nextarc 0
@@ -9091,7 +9539,7 @@
 	set seeds {}
 	set allcwait 0
 	set cachedarcs 0
-	set allccache [file join [gitdir] "gitk.cache"]
+	set allccache [file join $gitdir "gitk.cache"]
 	if {![catch {
 	    set f [open $allccache r]
 	    set allcwait 1
@@ -9156,7 +9604,7 @@
     global allparents allchildren idtags idheads nextarc
     global arcnos arcids arctags arcout arcend arcstart archeads growing
     global seeds allcommits cachedarcs allcupdate
-    
+
     set nid 0
     while {[incr nid] <= 1000 && [gets $fd line] >= 0} {
 	set id [lindex $line 0]
@@ -10218,7 +10666,7 @@
     global ctext tagcontents tagids linknum tagobjid
 
     if {$isnew} {
-	addtohistory [list showtag $tag 0]
+	addtohistory [list showtag $tag 0] savectextpos
     }
     $ctext conf -state normal
     clear_ctext
@@ -10226,7 +10674,7 @@
     set linknum 0
     if {![info exists tagcontents($tag)]} {
 	catch {
-	    set tagcontents($tag) [exec git cat-file tag $tagobjid($tag)]
+           set tagcontents($tag) [exec git cat-file tag $tag]
 	}
     }
     if {[info exists tagcontents($tag)]} {
@@ -10235,6 +10683,7 @@
 	set text "[mc "Tag"]: $tag\n[mc "Id"]:  $tagids($tag)"
     }
     appendwithlinks $text {}
+    maybe_scroll_ctext 1
     $ctext conf -state disabled
     init_flist {}
 }
@@ -10253,19 +10702,19 @@
 }
 
 proc mkfontdisp {font top which} {
-    global fontattr fontpref $font
+    global fontattr fontpref $font NS use_ttk
 
     set fontpref($font) [set $font]
-    button $top.${font}but -text $which -font optionfont \
+    ${NS}::button $top.${font}but -text $which \
 	-command [list choosefont $font $which]
-    label $top.$font -relief flat -font $font \
+    ${NS}::label $top.$font -relief flat -font $font \
 	-text $fontattr($font,family) -justify left
     grid x $top.${font}but $top.$font -sticky w
 }
 
 proc choosefont {font which} {
     global fontparam fontlist fonttop fontattr
-    global prefstop
+    global prefstop NS
 
     set fontparam(which) $which
     set fontparam(font) $font
@@ -10278,21 +10727,21 @@
     if {![winfo exists $top]} {
 	font create sample
 	eval font config sample [font actual $font]
-	toplevel $top
+	ttk_toplevel $top
 	make_transient $top $prefstop
 	wm title $top [mc "Gitk font chooser"]
-	label $top.l -textvariable fontparam(which)
+	${NS}::label $top.l -textvariable fontparam(which)
 	pack $top.l -side top
 	set fontlist [lsort [font families]]
-	frame $top.f
+	${NS}::frame $top.f
 	listbox $top.f.fam -listvariable fontlist \
 	    -yscrollcommand [list $top.f.sb set]
 	bind $top.f.fam <<ListboxSelect>> selfontfam
-	scrollbar $top.f.sb -command [list $top.f.fam yview]
+	${NS}::scrollbar $top.f.sb -command [list $top.f.fam yview]
 	pack $top.f.sb -side right -fill y
 	pack $top.f.fam -side left -fill both -expand 1
 	pack $top.f -side top -fill both -expand 1
-	frame $top.g
+	${NS}::frame $top.g
 	spinbox $top.g.size -from 4 -to 40 -width 4 \
 	    -textvariable fontparam(size) \
 	    -validatecommand {string is integer -strict %s}
@@ -10310,9 +10759,9 @@
 	    -fill black -tags text
 	bind $top.c <Configure> [list centertext $top.c]
 	pack $top.c -side top -fill x
-	frame $top.buts
-	button $top.buts.ok -text [mc "OK"] -command fontok -default active
-	button $top.buts.can -text [mc "Cancel"] -command fontcan -default normal
+	${NS}::frame $top.buts
+	${NS}::button $top.buts.ok -text [mc "OK"] -command fontok -default active
+	${NS}::button $top.buts.can -text [mc "Cancel"] -command fontcan -default normal
 	bind $top <Key-Return> fontok
 	bind $top <Key-Escape> fontcan
 	grid $top.buts.ok $top.buts.can
@@ -10346,9 +10795,9 @@
     if {$fontparam(slant) eq "italic"} {
 	lappend fontpref($f) "italic"
     }
-    set w $prefstop.$f
+    set w $prefstop.notebook.fonts.$f
     $w conf -text $fontparam(family) -font $fontpref($f)
-	
+
     fontcan
 }
 
@@ -10363,6 +10812,28 @@
     }
 }
 
+if {[package vsatisfies [package provide Tk] 8.6]} {
+    # In Tk 8.6 we have a native font chooser dialog. Overwrite the above
+    # function to make use of it.
+    proc choosefont {font which} {
+	tk fontchooser configure -title $which -font $font \
+	    -command [list on_choosefont $font $which]
+	tk fontchooser show
+    }
+    proc on_choosefont {font which newfont} {
+	global fontparam
+	puts stderr "$font $newfont"
+	array set f [font actual $newfont]
+	set fontparam(which) $which
+	set fontparam(font) $font
+	set fontparam(family) $f(-family)
+	set fontparam(size) $f(-size)
+	set fontparam(weight) $f(-weight)
+	set fontparam(slant) $f(-slant)
+	fontok
+    }
+}
+
 proc selfontfam {} {
     global fonttop fontparam
 
@@ -10378,11 +10849,145 @@
     font config sample -$sub $fontparam($sub)
 }
 
+# Create a property sheet tab page
+proc create_prefs_page {w} {
+    global NS
+    set parent [join [lrange [split $w .] 0 end-1] .]
+    if {[winfo class $parent] eq "TNotebook"} {
+	${NS}::frame $w
+    } else {
+	${NS}::labelframe $w
+    }
+}
+
+proc prefspage_general {notebook} {
+    global NS maxwidth maxgraphpct showneartags showlocalchanges
+    global tabstop limitdiffs autoselect autosellen extdifftool perfile_attrs
+    global hideremotes want_ttk have_ttk
+
+    set page [create_prefs_page $notebook.general]
+
+    ${NS}::label $page.ldisp -text [mc "Commit list display options"]
+    grid $page.ldisp - -sticky w -pady 10
+    ${NS}::label $page.spacer -text " "
+    ${NS}::label $page.maxwidthl -text [mc "Maximum graph width (lines)"]
+    spinbox $page.maxwidth -from 0 -to 100 -width 4 -textvariable maxwidth
+    grid $page.spacer $page.maxwidthl $page.maxwidth -sticky w
+    ${NS}::label $page.maxpctl -text [mc "Maximum graph width (% of pane)"]
+    spinbox $page.maxpct -from 1 -to 100 -width 4 -textvariable maxgraphpct
+    grid x $page.maxpctl $page.maxpct -sticky w
+    ${NS}::checkbutton $page.showlocal -text [mc "Show local changes"] \
+	-variable showlocalchanges
+    grid x $page.showlocal -sticky w
+    ${NS}::checkbutton $page.autoselect -text [mc "Auto-select SHA1 (length)"] \
+	-variable autoselect
+    spinbox $page.autosellen -from 1 -to 40 -width 4 -textvariable autosellen
+    grid x $page.autoselect $page.autosellen -sticky w
+    ${NS}::checkbutton $page.hideremotes -text [mc "Hide remote refs"] \
+	-variable hideremotes
+    grid x $page.hideremotes -sticky w
+
+    ${NS}::label $page.ddisp -text [mc "Diff display options"]
+    grid $page.ddisp - -sticky w -pady 10
+    ${NS}::label $page.tabstopl -text [mc "Tab spacing"]
+    spinbox $page.tabstop -from 1 -to 20 -width 4 -textvariable tabstop
+    grid x $page.tabstopl $page.tabstop -sticky w
+    ${NS}::checkbutton $page.ntag -text [mc "Display nearby tags"] \
+	-variable showneartags
+    grid x $page.ntag -sticky w
+    ${NS}::checkbutton $page.ldiff -text [mc "Limit diffs to listed paths"] \
+	-variable limitdiffs
+    grid x $page.ldiff -sticky w
+    ${NS}::checkbutton $page.lattr -text [mc "Support per-file encodings"] \
+	-variable perfile_attrs
+    grid x $page.lattr -sticky w
+
+    ${NS}::entry $page.extdifft -textvariable extdifftool
+    ${NS}::frame $page.extdifff
+    ${NS}::label $page.extdifff.l -text [mc "External diff tool" ]
+    ${NS}::button $page.extdifff.b -text [mc "Choose..."] -command choose_extdiff
+    pack $page.extdifff.l $page.extdifff.b -side left
+    pack configure $page.extdifff.l -padx 10
+    grid x $page.extdifff $page.extdifft -sticky ew
+
+    ${NS}::label $page.lgen -text [mc "General options"]
+    grid $page.lgen - -sticky w -pady 10
+    ${NS}::checkbutton $page.want_ttk -variable want_ttk \
+	-text [mc "Use themed widgets"]
+    if {$have_ttk} {
+	${NS}::label $page.ttk_note -text [mc "(change requires restart)"]
+    } else {
+	${NS}::label $page.ttk_note -text [mc "(currently unavailable)"]
+    }
+    grid x $page.want_ttk $page.ttk_note -sticky w
+    return $page
+}
+
+proc prefspage_colors {notebook} {
+    global NS uicolor bgcolor fgcolor ctext diffcolors selectbgcolor markbgcolor
+
+    set page [create_prefs_page $notebook.colors]
+
+    ${NS}::label $page.cdisp -text [mc "Colors: press to choose"]
+    grid $page.cdisp - -sticky w -pady 10
+    label $page.ui -padx 40 -relief sunk -background $uicolor
+    ${NS}::button $page.uibut -text [mc "Interface"] \
+       -command [list choosecolor uicolor {} $page.ui [mc "interface"] setui]
+    grid x $page.uibut $page.ui -sticky w
+    label $page.bg -padx 40 -relief sunk -background $bgcolor
+    ${NS}::button $page.bgbut -text [mc "Background"] \
+	-command [list choosecolor bgcolor {} $page.bg [mc "background"] setbg]
+    grid x $page.bgbut $page.bg -sticky w
+    label $page.fg -padx 40 -relief sunk -background $fgcolor
+    ${NS}::button $page.fgbut -text [mc "Foreground"] \
+	-command [list choosecolor fgcolor {} $page.fg [mc "foreground"] setfg]
+    grid x $page.fgbut $page.fg -sticky w
+    label $page.diffold -padx 40 -relief sunk -background [lindex $diffcolors 0]
+    ${NS}::button $page.diffoldbut -text [mc "Diff: old lines"] \
+	-command [list choosecolor diffcolors 0 $page.diffold [mc "diff old lines"] \
+		      [list $ctext tag conf d0 -foreground]]
+    grid x $page.diffoldbut $page.diffold -sticky w
+    label $page.diffnew -padx 40 -relief sunk -background [lindex $diffcolors 1]
+    ${NS}::button $page.diffnewbut -text [mc "Diff: new lines"] \
+	-command [list choosecolor diffcolors 1 $page.diffnew [mc "diff new lines"] \
+		      [list $ctext tag conf dresult -foreground]]
+    grid x $page.diffnewbut $page.diffnew -sticky w
+    label $page.hunksep -padx 40 -relief sunk -background [lindex $diffcolors 2]
+    ${NS}::button $page.hunksepbut -text [mc "Diff: hunk header"] \
+	-command [list choosecolor diffcolors 2 $page.hunksep \
+		      [mc "diff hunk header"] \
+		      [list $ctext tag conf hunksep -foreground]]
+    grid x $page.hunksepbut $page.hunksep -sticky w
+    label $page.markbgsep -padx 40 -relief sunk -background $markbgcolor
+    ${NS}::button $page.markbgbut -text [mc "Marked line bg"] \
+	-command [list choosecolor markbgcolor {} $page.markbgsep \
+		      [mc "marked line background"] \
+		      [list $ctext tag conf omark -background]]
+    grid x $page.markbgbut $page.markbgsep -sticky w
+    label $page.selbgsep -padx 40 -relief sunk -background $selectbgcolor
+    ${NS}::button $page.selbgbut -text [mc "Select bg"] \
+	-command [list choosecolor selectbgcolor {} $page.selbgsep [mc "background"] setselbg]
+    grid x $page.selbgbut $page.selbgsep -sticky w
+    return $page
+}
+
+proc prefspage_fonts {notebook} {
+    global NS
+    set page [create_prefs_page $notebook.fonts]
+    ${NS}::label $page.cfont -text [mc "Fonts: press to choose"]
+    grid $page.cfont - -sticky w -pady 10
+    mkfontdisp mainfont $page [mc "Main font"]
+    mkfontdisp textfont $page [mc "Diff display font"]
+    mkfontdisp uifont $page [mc "User interface font"]
+    return $page
+}
+
 proc doprefs {} {
-    global maxwidth maxgraphpct
+    global maxwidth maxgraphpct use_ttk NS
     global oldprefs prefstop showneartags showlocalchanges
-    global bgcolor fgcolor ctext diffcolors selectbgcolor markbgcolor
-    global tabstop limitdiffs autoselect extdifftool perfile_attrs
+    global uicolor bgcolor fgcolor ctext diffcolors selectbgcolor markbgcolor
+    global tabstop limitdiffs autoselect autosellen extdifftool perfile_attrs
+    global hideremotes want_ttk have_ttk
 
     set top .gitkprefs
     set prefstop $top
@@ -10391,107 +10996,56 @@
 	return
     }
     foreach v {maxwidth maxgraphpct showneartags showlocalchanges \
-		   limitdiffs tabstop perfile_attrs} {
+		   limitdiffs tabstop perfile_attrs hideremotes want_ttk} {
 	set oldprefs($v) [set $v]
     }
-    toplevel $top
+    ttk_toplevel $top
     wm title $top [mc "Gitk preferences"]
     make_transient $top .
-    label $top.ldisp -text [mc "Commit list display options"]
-    grid $top.ldisp - -sticky w -pady 10
-    label $top.spacer -text " "
-    label $top.maxwidthl -text [mc "Maximum graph width (lines)"] \
-	-font optionfont
-    spinbox $top.maxwidth -from 0 -to 100 -width 4 -textvariable maxwidth
-    grid $top.spacer $top.maxwidthl $top.maxwidth -sticky w
-    label $top.maxpctl -text [mc "Maximum graph width (% of pane)"] \
-	-font optionfont
-    spinbox $top.maxpct -from 1 -to 100 -width 4 -textvariable maxgraphpct
-    grid x $top.maxpctl $top.maxpct -sticky w
-    checkbutton $top.showlocal -text [mc "Show local changes"] \
-	-font optionfont -variable showlocalchanges
-    grid x $top.showlocal -sticky w
-    checkbutton $top.autoselect -text [mc "Auto-select SHA1"] \
-	-font optionfont -variable autoselect
-    grid x $top.autoselect -sticky w
 
-    label $top.ddisp -text [mc "Diff display options"]
-    grid $top.ddisp - -sticky w -pady 10
-    label $top.tabstopl -text [mc "Tab spacing"] -font optionfont
-    spinbox $top.tabstop -from 1 -to 20 -width 4 -textvariable tabstop
-    grid x $top.tabstopl $top.tabstop -sticky w
-    checkbutton $top.ntag -text [mc "Display nearby tags"] \
-	-font optionfont -variable showneartags
-    grid x $top.ntag -sticky w
-    checkbutton $top.ldiff -text [mc "Limit diffs to listed paths"] \
-	-font optionfont -variable limitdiffs
-    grid x $top.ldiff -sticky w
-    checkbutton $top.lattr -text [mc "Support per-file encodings"] \
-	-font optionfont -variable perfile_attrs
-    grid x $top.lattr -sticky w
+    if {[set use_notebook [expr {$use_ttk && [info command ::ttk::notebook] ne ""}]]} {
+	set notebook [ttk::notebook $top.notebook]
+    } else {
+	set notebook [${NS}::frame $top.notebook -borderwidth 0 -relief flat]
+    }
 
-    entry $top.extdifft -textvariable extdifftool
-    frame $top.extdifff
-    label $top.extdifff.l -text [mc "External diff tool" ] -font optionfont \
-	-padx 10
-    button $top.extdifff.b -text [mc "Choose..."] -font optionfont \
-	-command choose_extdiff
-    pack $top.extdifff.l $top.extdifff.b -side left
-    grid x $top.extdifff $top.extdifft -sticky w
+    lappend pages [prefspage_general $notebook] [mc "General"]
+    lappend pages [prefspage_colors $notebook] [mc "Colors"]
+    lappend pages [prefspage_fonts $notebook] [mc "Fonts"]
+    set col 0
+    foreach {page title} $pages {
+	if {$use_notebook} {
+	    $notebook add $page -text $title
+	} else {
+	    set btn [${NS}::button $notebook.b_[string map {. X} $page] \
+			 -text $title -command [list raise $page]]
+	    $page configure -text $title
+	    grid $btn -row 0 -column [incr col] -sticky w
+	    grid $page -row 1 -column 0 -sticky news -columnspan 100
+	}
+    }
 
-    label $top.cdisp -text [mc "Colors: press to choose"]
-    grid $top.cdisp - -sticky w -pady 10
-    label $top.bg -padx 40 -relief sunk -background $bgcolor
-    button $top.bgbut -text [mc "Background"] -font optionfont \
-	-command [list choosecolor bgcolor {} $top.bg [mc "background"] setbg]
-    grid x $top.bgbut $top.bg -sticky w
-    label $top.fg -padx 40 -relief sunk -background $fgcolor
-    button $top.fgbut -text [mc "Foreground"] -font optionfont \
-	-command [list choosecolor fgcolor {} $top.fg [mc "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 [mc "Diff: old lines"] -font optionfont \
-	-command [list choosecolor diffcolors 0 $top.diffold [mc "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 [mc "Diff: new lines"] -font optionfont \
-	-command [list choosecolor diffcolors 1 $top.diffnew [mc "diff new lines"] \
-		      [list $ctext tag conf dresult -foreground]]
-    grid x $top.diffnewbut $top.diffnew -sticky w
-    label $top.hunksep -padx 40 -relief sunk -background [lindex $diffcolors 2]
-    button $top.hunksepbut -text [mc "Diff: hunk header"] -font optionfont \
-	-command [list choosecolor diffcolors 2 $top.hunksep \
-		      [mc "diff hunk header"] \
-		      [list $ctext tag conf hunksep -foreground]]
-    grid x $top.hunksepbut $top.hunksep -sticky w
-    label $top.markbgsep -padx 40 -relief sunk -background $markbgcolor
-    button $top.markbgbut -text [mc "Marked line bg"] -font optionfont \
-	-command [list choosecolor markbgcolor {} $top.markbgsep \
-		      [mc "marked line background"] \
-		      [list $ctext tag conf omark -background]]
-    grid x $top.markbgbut $top.markbgsep -sticky w
-    label $top.selbgsep -padx 40 -relief sunk -background $selectbgcolor
-    button $top.selbgbut -text [mc "Select bg"] -font optionfont \
-	-command [list choosecolor selectbgcolor {} $top.selbgsep [mc "background"] setselbg]
-    grid x $top.selbgbut $top.selbgsep -sticky w
+    if {!$use_notebook} {
+	grid columnconfigure $notebook 0 -weight 1
+	grid rowconfigure $notebook 1 -weight 1
+	raise [lindex $pages 0]
+    }
 
-    label $top.cfont -text [mc "Fonts: press to choose"]
-    grid $top.cfont - -sticky w -pady 10
-    mkfontdisp mainfont $top [mc "Main font"]
-    mkfontdisp textfont $top [mc "Diff display font"]
-    mkfontdisp uifont $top [mc "User interface font"]
+    grid $notebook -sticky news -padx 2 -pady 2
+    grid rowconfigure $top 0 -weight 1
+    grid columnconfigure $top 0 -weight 1
 
-    frame $top.buts
-    button $top.buts.ok -text [mc "OK"] -command prefsok -default active
-    button $top.buts.can -text [mc "Cancel"] -command prefscan -default normal
+    ${NS}::frame $top.buts
+    ${NS}::button $top.buts.ok -text [mc "OK"] -command prefsok -default active
+    ${NS}::button $top.buts.can -text [mc "Cancel"] -command prefscan -default normal
     bind $top <Key-Return> prefsok
     bind $top <Key-Escape> prefscan
     grid $top.buts.ok $top.buts.can
     grid columnconfigure $top.buts 0 -weight 1 -uniform a
     grid columnconfigure $top.buts 1 -weight 1 -uniform a
     grid $top.buts - - -pady 10 -sticky ew
-    bind $top <Visibility> "focus $top.buts.ok"
+    grid columnconfigure $top 2 -weight 1
+    bind $top <Visibility> [list focus $top.buts.ok]
 }
 
 proc choose_extdiff {} {
@@ -10524,6 +11078,21 @@
     allcanvs itemconf secsel -fill $c
 }
 
+# This sets the background color and the color scheme for the whole UI.
+# For some reason, tk_setPalette chooses a nasty dark red for selectColor
+# if we don't specify one ourselves, which makes the checkbuttons and
+# radiobuttons look bad.  This chooses white for selectColor if the
+# background color is light, or black if it is dark.
+proc setui {c} {
+    if {[tk windowingsystem] eq "win32"} { return }
+    set bg [winfo rgb . $c]
+    set selc black
+    if {[lindex $bg 0] + 1.5 * [lindex $bg 1] + 0.5 * [lindex $bg 2] > 100000} {
+	set selc white
+    }
+    tk_setPalette background $c selectColor $selc
+}
+
 proc setbg {c} {
     global bglist
 
@@ -10547,7 +11116,7 @@
     global oldprefs prefstop
 
     foreach v {maxwidth maxgraphpct showneartags showlocalchanges \
-		   limitdiffs tabstop perfile_attrs} {
+		   limitdiffs tabstop perfile_attrs hideremotes want_ttk} {
 	global $v
 	set $v $oldprefs($v)
     }
@@ -10561,6 +11130,7 @@
     global oldprefs prefstop showneartags showlocalchanges
     global fontpref mainfont textfont uifont
     global limitdiffs treediffs perfile_attrs
+    global hideremotes
 
     catch {destroy $prefstop}
     unset prefstop
@@ -10606,12 +11176,15 @@
 	  $limitdiffs != $oldprefs(limitdiffs)} {
 	reselectline
     }
+    if {$hideremotes != $oldprefs(hideremotes)} {
+	rereadrefs
+    }
 }
 
 proc formatdate {d} {
     global datetimeformat
     if {$d ne {}} {
-	set d [clock format $d -format $datetimeformat]
+	set d [clock format [lindex $d 0] -format $datetimeformat]
     }
     return $d
 }
@@ -10901,7 +11474,7 @@
     } else {
 	set r "unspecified"
 	if {![catch {set line [exec git check-attr $attr -- $path]}]} {
-	    regexp "(.*): encoding: (.*)" $line m f r
+	    regexp "(.*): $attr: (.*)" $line m f r
 	}
 	set path_attr_cache($attr,$path) $r
     }
@@ -10929,7 +11502,7 @@
 	set newlist [lrange $newlist $lim end]
 	if {![catch {set rlist [eval exec git check-attr $attr -- $head]}]} {
 	    foreach row [split $rlist "\n"] {
-		if {[regexp "(.*): encoding: (.*)" $row m path value]} {
+		if {[regexp "(.*): $attr: (.*)" $row m path value]} {
 		    if {[string index $path 0] eq "\""} {
 			set path [encoding convertfrom [lindex $path 0]]
 		    }
@@ -10954,8 +11527,8 @@
 
 # First check that Tcl/Tk is recent enough
 if {[catch {package require Tk 8.4} err]} {
-    show_error {} . [mc "Sorry, gitk cannot run with this version of Tcl/Tk.\n\
-		     Gitk requires at least Tcl/Tk 8.4."]
+    show_error {} . "Sorry, gitk cannot run with this version of Tcl/Tk.\n\
+		     Gitk requires at least Tcl/Tk 8.4." list
     exit 1
 }
 
@@ -10990,10 +11563,20 @@
     }
 }
 
+set log_showroot true
+catch {
+    set log_showroot [exec git config --bool --get log.showroot]
+}
+
 if {[tk windowingsystem] eq "aqua"} {
     set mainfont {{Lucida Grande} 9}
     set textfont {Monaco 9}
     set uifont {{Lucida Grande} 9 bold}
+} elseif {![catch {::tk::pkgconfig get fontsystem} xft] && $xft eq "xft"} {
+    # fontconfig!
+    set mainfont {sans 9}
+    set textfont {monospace 9}
+    set uifont {sans 9 bold}
 } else {
     set mainfont {Helvetica 9}
     set textfont {Courier 9}
@@ -11011,13 +11594,16 @@
 set cmitmode "patch"
 set wrapcomment "none"
 set showneartags 1
+set hideremotes 0
 set maxrefs 20
 set maxlinelen 200
 set showlocalchanges 1
 set limitdiffs 1
 set datetimeformat "%Y-%m-%d %H:%M:%S"
 set autoselect 1
+set autosellen 40
 set perfile_attrs 0
+set want_ttk 1
 
 if {[tk windowingsystem] eq "aqua"} {
     set extdifftool "opendiff"
@@ -11026,12 +11612,21 @@
 }
 
 set colors {green red blue magenta darkgrey brown orange}
-set bgcolor white
-set fgcolor black
+if {[tk windowingsystem] eq "win32"} {
+    set uicolor SystemButtonFace
+    set bgcolor SystemWindow
+    set fgcolor SystemButtonText
+    set selectbgcolor SystemHighlight
+} else {
+    set uicolor grey85
+    set bgcolor white
+    set fgcolor black
+    set selectbgcolor gray85
+}
 set diffcolors {red "#00a000" blue}
 set diffcontext 3
 set ignorespace 0
-set selectbgcolor gray85
+set worddiff ""
 set markbgcolor "#e0e0ff"
 
 set circlecolors {white blue gray blue blue}
@@ -11064,8 +11659,6 @@
 
 catch {source ~/.gitk}
 
-font create optionfont -family sans-serif -size -12
-
 parsefont mainfont $mainfont
 eval font create mainfont [fontflags mainfont]
 eval font create mainfontbold [fontflags mainfont 1]
@@ -11077,17 +11670,15 @@
 parsefont uifont $uifont
 eval font create uifont [fontflags uifont]
 
+setui $uicolor
+
 setoptions
 
 # check that we can find a .git directory somewhere...
-if {[catch {set gitdir [gitdir]}]} {
+if {[catch {set gitdir [exec git rev-parse --git-dir]}]} {
     show_error {} . [mc "Cannot find a git repository here."]
     exit 1
 }
-if {![file isdirectory $gitdir]} {
-    show_error {} . [mc "Cannot find the git directory \"%s\"." $gitdir]
-    exit 1
-}
 
 set selecthead {}
 set selectheadid {}
@@ -11154,7 +11745,20 @@
 set nullfile "/dev/null"
 
 set have_tk85 [expr {[package vcompare $tk_version "8.5"] >= 0}]
-set git_version [join [lrange [split [lindex [exec git version] end] .] 0 2] .]
+if {![info exists have_ttk]} {
+    set have_ttk [llength [info commands ::ttk::style]]
+}
+set use_ttk [expr {$have_ttk && $want_ttk}]
+set NS [expr {$use_ttk ? "ttk" : ""}]
+
+regexp {^git version ([\d.]*\d)} [exec git version] _ git_version
+
+set show_notes {}
+if {[package vcompare $git_version "1.6.6.2"] >= 0} {
+    set show_notes "--show-notes"
+}
+
+set appname "gitk"
 
 set runq {}
 set history {}
@@ -11192,7 +11796,12 @@
 set stuffsaved 0
 set patchnum 0
 set lserial 0
-set isworktree [expr {[exec git rev-parse --is-inside-work-tree] == "true"}]
+set hasworktree [hasworktree]
+set cdup {}
+if {[expr {[exec git rev-parse --is-inside-work-tree] == "true"}]} {
+    set cdup [exec git rev-parse --show-cdup]
+}
+set worktree [exec git rev-parse --show-toplevel]
 setcoords
 makewindow
 catch {
@@ -11220,7 +11829,7 @@
 }
 # wait for the window to become visible
 tkwait visibility .
-wm title . "[file tail $argv0]: [file tail [pwd]]"
+wm title . "$appname: [reponame]"
 update
 readrefs
 
@@ -11258,3 +11867,9 @@
 }
 
 getcommits {}
+
+# Local variables:
+# mode: tcl
+# indent-tabs-mode: t
+# tab-width: 8
+# End:
diff --git a/gitk-git/po/de.po b/gitk-git/po/de.po
index 53ef0d6..bd194a3 100644
--- a/gitk-git/po/de.po
+++ b/gitk-git/po/de.po
@@ -8,33 +8,33 @@
 msgstr ""
 "Project-Id-Version: git-gui\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-05-12 21:55+0200\n"
-"PO-Revision-Date: 2009-05-12 22:18+0200\n"
+"POT-Creation-Date: 2010-01-27 20:21+0100\n"
+"PO-Revision-Date: 2010-01-27 20:27+0100\n"
 "Last-Translator: Christian Stimming <stimming@tuhh.de>\n"
 "Language-Team: German\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: gitk:113
+#: gitk:115
 msgid "Couldn't get list of unmerged files:"
 msgstr "Liste der nicht zusammengeführten Dateien nicht gefunden:"
 
-#: gitk:268
+#: gitk:274
 msgid "Error parsing revisions:"
 msgstr "Fehler beim Laden der Versionen:"
 
-#: gitk:323
+#: gitk:329
 msgid "Error executing --argscmd command:"
 msgstr "Fehler beim Ausführen des --argscmd-Kommandos:"
 
-#: gitk:336
+#: gitk:342
 msgid "No files selected: --merge specified but no files are unmerged."
 msgstr ""
 "Keine Dateien ausgewählt: Es wurde --merge angegeben, aber es existieren "
 "keine nicht zusammengeführten Dateien."
 
-#: gitk:339
+#: gitk:345
 msgid ""
 "No files selected: --merge specified but no unmerged files are within file "
 "limit."
@@ -42,719 +42,801 @@
 "Keine Dateien ausgewählt: Es wurde --merge angegeben, aber es sind keine "
 "nicht zusammengeführten Dateien in der Dateiauswahl."
 
-#: gitk:361 gitk:508
+#: gitk:367 gitk:514
 msgid "Error executing git log:"
 msgstr "Fehler beim Ausführen von »git log«:"
 
-#: gitk:379 gitk:524
+#: gitk:385 gitk:530
 msgid "Reading"
 msgstr "Lesen"
 
-#: gitk:439 gitk:4061
+#: gitk:445 gitk:4261
 msgid "Reading commits..."
 msgstr "Versionen werden gelesen ..."
 
-#: gitk:442 gitk:1560 gitk:4064
+#: gitk:448 gitk:1578 gitk:4264
 msgid "No commits selected"
 msgstr "Keine Versionen ausgewählt"
 
-#: gitk:1436
+#: gitk:1454
 msgid "Can't parse git log output:"
 msgstr "Ausgabe von »git log« kann nicht erkannt werden:"
 
-#: gitk:1656
+#: gitk:1674
 msgid "No commit information available"
 msgstr "Keine Versionsinformation verfügbar"
 
-#: gitk:1791 gitk:1815 gitk:3854 gitk:8714 gitk:10250 gitk:10422
+#: gitk:1816
+msgid "mc"
+msgstr "mc"
+
+#: gitk:1851 gitk:4054 gitk:9044 gitk:10585 gitk:10804
 msgid "OK"
 msgstr "Ok"
 
-#: gitk:1817 gitk:3856 gitk:8311 gitk:8385 gitk:8495 gitk:8544 gitk:8716
-#: gitk:10251 gitk:10423
+#: gitk:1853 gitk:4056 gitk:8634 gitk:8713 gitk:8828 gitk:8877 gitk:9046
+#: gitk:10586 gitk:10805
 msgid "Cancel"
 msgstr "Abbrechen"
 
-#: gitk:1917
+#: gitk:1975
 msgid "Update"
 msgstr "Aktualisieren"
 
-#: gitk:1918
+#: gitk:1976
 msgid "Reload"
 msgstr "Neu laden"
 
-#: gitk:1919
+#: gitk:1977
 msgid "Reread references"
 msgstr "Zweige neu laden"
 
-#: gitk:1920
+#: gitk:1978
 msgid "List references"
 msgstr "Zweige/Markierungen auflisten"
 
-#: gitk:1922
+#: gitk:1980
 msgid "Start git gui"
 msgstr "»git gui« starten"
 
-#: gitk:1924
+#: gitk:1982
 msgid "Quit"
 msgstr "Beenden"
 
-#: gitk:1916
+#: gitk:1974
 msgid "File"
 msgstr "Datei"
 
-#: gitk:1928
+#: gitk:1986
 msgid "Preferences"
 msgstr "Einstellungen"
 
-#: gitk:1927
+#: gitk:1985
 msgid "Edit"
 msgstr "Bearbeiten"
 
-#: gitk:1932
+#: gitk:1990
 msgid "New view..."
 msgstr "Neue Ansicht ..."
 
-#: gitk:1933
+#: gitk:1991
 msgid "Edit view..."
 msgstr "Ansicht bearbeiten ..."
 
-#: gitk:1934
+#: gitk:1992
 msgid "Delete view"
 msgstr "Ansicht entfernen"
 
-#: gitk:1936
+#: gitk:1994
 msgid "All files"
 msgstr "Alle Dateien"
 
-#: gitk:1931 gitk:3666
+#: gitk:1989 gitk:3808
 msgid "View"
 msgstr "Ansicht"
 
-#: gitk:1941 gitk:1951 gitk:2650
+#: gitk:1999 gitk:2009 gitk:2780
 msgid "About gitk"
 msgstr "Über gitk"
 
-#: gitk:1942 gitk:1956
+#: gitk:2000 gitk:2014
 msgid "Key bindings"
 msgstr "Tastenkürzel"
 
-#: gitk:1940 gitk:1955
+#: gitk:1998 gitk:2013
 msgid "Help"
 msgstr "Hilfe"
 
-#: gitk:2016
-msgid "SHA1 ID: "
-msgstr "SHA1:"
+#: gitk:2091 gitk:8110
+msgid "SHA1 ID:"
+msgstr "SHA1 ID:"
 
-#: gitk:2047
+#: gitk:2122
 msgid "Row"
 msgstr "Zeile"
 
-#: gitk:2078
+#: gitk:2160
 msgid "Find"
 msgstr "Suche"
 
-#: gitk:2079
+#: gitk:2161
 msgid "next"
 msgstr "nächste"
 
-#: gitk:2080
+#: gitk:2162
 msgid "prev"
 msgstr "vorige"
 
-#: gitk:2081
+#: gitk:2163
 msgid "commit"
 msgstr "Version nach"
 
-#: gitk:2084 gitk:2086 gitk:4222 gitk:4245 gitk:4269 gitk:6210 gitk:6282
-#: gitk:6366
+#: gitk:2166 gitk:2168 gitk:4422 gitk:4445 gitk:4469 gitk:6410 gitk:6482
+#: gitk:6566
 msgid "containing:"
 msgstr "Beschreibung:"
 
-#: gitk:2087 gitk:3158 gitk:3163 gitk:4297
+#: gitk:2169 gitk:3290 gitk:3295 gitk:4497
 msgid "touching paths:"
 msgstr "Dateien:"
 
-#: gitk:2088 gitk:4302
+#: gitk:2170 gitk:4502
 msgid "adding/removing string:"
 msgstr "Änderungen:"
 
-#: gitk:2097 gitk:2099
+#: gitk:2179 gitk:2181
 msgid "Exact"
 msgstr "Exakt"
 
-#: gitk:2099 gitk:4377 gitk:6178
+#: gitk:2181 gitk:4577 gitk:6378
 msgid "IgnCase"
 msgstr "Kein Groß/Klein"
 
-#: gitk:2099 gitk:4271 gitk:4375 gitk:6174
+#: gitk:2181 gitk:4471 gitk:4575 gitk:6374
 msgid "Regexp"
 msgstr "Regexp"
 
-#: gitk:2101 gitk:2102 gitk:4396 gitk:4426 gitk:4433 gitk:6302 gitk:6370
+#: gitk:2183 gitk:2184 gitk:4596 gitk:4626 gitk:4633 gitk:6502 gitk:6570
 msgid "All fields"
 msgstr "Alle Felder"
 
-#: gitk:2102 gitk:4394 gitk:4426 gitk:6241
+#: gitk:2184 gitk:4594 gitk:4626 gitk:6441
 msgid "Headline"
 msgstr "Überschrift"
 
-#: gitk:2103 gitk:4394 gitk:6241 gitk:6370 gitk:6804
+#: gitk:2185 gitk:4594 gitk:6441 gitk:6570 gitk:7003
 msgid "Comments"
 msgstr "Beschreibung"
 
-#: gitk:2103 gitk:4394 gitk:4398 gitk:4433 gitk:6241 gitk:6739 gitk:7991
-#: gitk:8006
+#: gitk:2185 gitk:4594 gitk:4598 gitk:4633 gitk:6441 gitk:6938 gitk:8285
+#: gitk:8300
 msgid "Author"
 msgstr "Autor"
 
-#: gitk:2103 gitk:4394 gitk:6241 gitk:6741
+#: gitk:2185 gitk:4594 gitk:6441 gitk:6940
 msgid "Committer"
 msgstr "Eintragender"
 
-#: gitk:2132
+#: gitk:2216
 msgid "Search"
 msgstr "Suchen"
 
-#: gitk:2139
+#: gitk:2224
 msgid "Diff"
 msgstr "Vergleich"
 
-#: gitk:2141
+#: gitk:2226
 msgid "Old version"
 msgstr "Alte Version"
 
-#: gitk:2143
+#: gitk:2228
 msgid "New version"
 msgstr "Neue Version"
 
-#: gitk:2145
+#: gitk:2230
 msgid "Lines of context"
 msgstr "Kontextzeilen"
 
-#: gitk:2155
+#: gitk:2240
 msgid "Ignore space change"
 msgstr "Leerzeichenänderungen ignorieren"
 
-#: gitk:2213
+#: gitk:2299
 msgid "Patch"
 msgstr "Patch"
 
-#: gitk:2215
+#: gitk:2301
 msgid "Tree"
 msgstr "Baum"
 
-#: gitk:2359 gitk:2376
+#: gitk:2456 gitk:2473
 msgid "Diff this -> selected"
 msgstr "Vergleich: diese -> gewählte"
 
-#: gitk:2360 gitk:2377
+#: gitk:2457 gitk:2474
 msgid "Diff selected -> this"
 msgstr "Vergleich: gewählte -> diese"
 
-#: gitk:2361 gitk:2378
+#: gitk:2458 gitk:2475
 msgid "Make patch"
 msgstr "Patch erstellen"
 
-#: gitk:2362 gitk:8369
+#: gitk:2459 gitk:8692
 msgid "Create tag"
 msgstr "Markierung erstellen"
 
-#: gitk:2363 gitk:8475
+#: gitk:2460 gitk:8808
 msgid "Write commit to file"
 msgstr "Version in Datei schreiben"
 
-#: gitk:2364 gitk:8532
+#: gitk:2461 gitk:8865
 msgid "Create new branch"
 msgstr "Neuen Zweig erstellen"
 
-#: gitk:2365
+#: gitk:2462
 msgid "Cherry-pick this commit"
 msgstr "Diese Version pflücken"
 
-#: gitk:2366
+#: gitk:2463
 msgid "Reset HEAD branch to here"
 msgstr "HEAD-Zweig auf diese Version zurücksetzen"
 
-#: gitk:2367
+#: gitk:2464
 msgid "Mark this commit"
 msgstr "Lesezeichen setzen"
 
-#: gitk:2368
+#: gitk:2465
 msgid "Return to mark"
 msgstr "Zum Lesezeichen"
 
-#: gitk:2369
+#: gitk:2466
 msgid "Find descendant of this and mark"
 msgstr "Abkömmling von Lesezeichen und dieser Version finden"
 
-#: gitk:2370
+#: gitk:2467
 msgid "Compare with marked commit"
 msgstr "Mit Lesezeichen vergleichen"
 
-#: gitk:2384
+#: gitk:2481
 msgid "Check out this branch"
 msgstr "Auf diesen Zweig umstellen"
 
-#: gitk:2385
+#: gitk:2482
 msgid "Remove this branch"
 msgstr "Zweig löschen"
 
-#: gitk:2392
+#: gitk:2489
 msgid "Highlight this too"
 msgstr "Diesen auch hervorheben"
 
-#: gitk:2393
+#: gitk:2490
 msgid "Highlight this only"
 msgstr "Nur diesen hervorheben"
 
-#: gitk:2394
+#: gitk:2491
 msgid "External diff"
 msgstr "Externes Diff-Programm"
 
-#: gitk:2395
+#: gitk:2492
 msgid "Blame parent commit"
 msgstr "Annotieren der Elternversion"
 
-#: gitk:2402
+#: gitk:2499
 msgid "Show origin of this line"
 msgstr "Herkunft dieser Zeile anzeigen"
 
-#: gitk:2403
+#: gitk:2500
 msgid "Run git gui blame on this line"
 msgstr "Diese Zeile annotieren (»git gui blame«)"
 
-#: gitk:2652
+#: gitk:2782
 msgid ""
 "\n"
 "Gitk - a commit viewer for git\n"
 "\n"
-"Copyright © 2005-2008 Paul Mackerras\n"
+"Copyright \\u00a9 2005-2010 Paul Mackerras\n"
 "\n"
 "Use and redistribute under the terms of the GNU General Public License"
 msgstr ""
 "\n"
 "Gitk - eine Visualisierung der Git-Historie\n"
 "\n"
-"Copyright © 2005-2008 Paul Mackerras\n"
+"Copyright \\u00a9 2005-2010 Paul Mackerras\n"
 "\n"
-"Benutzung und Weiterverbreitung gemäß den Bedingungen der GNU General Public "
-"License"
+"Benutzung und Weiterverbreitung gemäß den Bedingungen der GNU General Public License"
 
-#: gitk:2660 gitk:2722 gitk:8897
+#: gitk:2790 gitk:2854 gitk:9230
 msgid "Close"
 msgstr "Schließen"
 
-#: gitk:2679
+#: gitk:2811
 msgid "Gitk key bindings"
 msgstr "Gitk-Tastaturbelegung"
 
-#: gitk:2682
+#: gitk:2814
 msgid "Gitk key bindings:"
 msgstr "Gitk-Tastaturbelegung:"
 
-#: gitk:2684
+#: gitk:2816
 #, tcl-format
 msgid "<%s-Q>\t\tQuit"
 msgstr "<%s-Q>\t\tBeenden"
 
-#: gitk:2685
+#: gitk:2817
 msgid "<Home>\t\tMove to first commit"
 msgstr "<Pos1>\t\tZur neuesten Version springen"
 
-#: gitk:2686
+#: gitk:2818
 msgid "<End>\t\tMove to last commit"
 msgstr "<Ende>\t\tZur ältesten Version springen"
 
-#: gitk:2687
+#: gitk:2819
 msgid "<Up>, p, i\tMove up one commit"
 msgstr "<Hoch>, p, i\tNächste neuere Version"
 
-#: gitk:2688
+#: gitk:2820
 msgid "<Down>, n, k\tMove down one commit"
 msgstr "<Runter>, n, k\tNächste ältere Version"
 
-#: gitk:2689
+#: gitk:2821
 msgid "<Left>, z, j\tGo back in history list"
 msgstr "<Links>, z, j\tEine Version zurückgehen"
 
-#: gitk:2690
+#: gitk:2822
 msgid "<Right>, x, l\tGo forward in history list"
 msgstr "<Rechts>, x, l\tEine Version weitergehen"
 
-#: gitk:2691
+#: gitk:2823
 msgid "<PageUp>\tMove up one page in commit list"
 msgstr "<BildHoch>\tEine Seite nach oben blättern"
 
-#: gitk:2692
+#: gitk:2824
 msgid "<PageDown>\tMove down one page in commit list"
 msgstr "<BildRunter>\tEine Seite nach unten blättern"
 
-#: gitk:2693
+#: gitk:2825
 #, tcl-format
 msgid "<%s-Home>\tScroll to top of commit list"
 msgstr "<%s-Pos1>\tZum oberen Ende der Versionsliste blättern"
 
-#: gitk:2694
+#: gitk:2826
 #, tcl-format
 msgid "<%s-End>\tScroll to bottom of commit list"
 msgstr "<%s-Ende>\tZum unteren Ende der Versionsliste blättern"
 
-#: gitk:2695
+#: gitk:2827
 #, tcl-format
 msgid "<%s-Up>\tScroll commit list up one line"
 msgstr "<%s-Hoch>\tVersionsliste eine Zeile nach oben blättern"
 
-#: gitk:2696
+#: gitk:2828
 #, tcl-format
 msgid "<%s-Down>\tScroll commit list down one line"
 msgstr "<%s-Runter>\tVersionsliste eine Zeile nach unten blättern"
 
-#: gitk:2697
+#: gitk:2829
 #, tcl-format
 msgid "<%s-PageUp>\tScroll commit list up one page"
 msgstr "<%s-BildHoch>\tVersionsliste eine Seite nach oben blättern"
 
-#: gitk:2698
+#: gitk:2830
 #, tcl-format
 msgid "<%s-PageDown>\tScroll commit list down one page"
 msgstr "<%s-BildRunter>\tVersionsliste eine Seite nach unten blättern"
 
-#: gitk:2699
+#: gitk:2831
 msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
 msgstr "<Umschalt-Hoch>\tRückwärts suchen (nach oben; neuere Versionen)"
 
-#: gitk:2700
+#: gitk:2832
 msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
 msgstr "<Umschalt-Runter> Suchen (nach unten; ältere Versionen)"
 
-#: gitk:2701
+#: gitk:2833
 msgid "<Delete>, b\tScroll diff view up one page"
 msgstr "<Entf>, b\t\tVergleich eine Seite nach oben blättern"
 
-#: gitk:2702
+#: gitk:2834
 msgid "<Backspace>\tScroll diff view up one page"
 msgstr "<Löschtaste>\tVergleich eine Seite nach oben blättern"
 
-#: gitk:2703
+#: gitk:2835
 msgid "<Space>\t\tScroll diff view down one page"
 msgstr "<Leertaste>\tVergleich eine Seite nach unten blättern"
 
-#: gitk:2704
+#: gitk:2836
 msgid "u\t\tScroll diff view up 18 lines"
 msgstr "u\t\tVergleich um 18 Zeilen nach oben blättern"
 
-#: gitk:2705
+#: gitk:2837
 msgid "d\t\tScroll diff view down 18 lines"
 msgstr "d\t\tVergleich um 18 Zeilen nach unten blättern"
 
-#: gitk:2706
+#: gitk:2838
 #, tcl-format
 msgid "<%s-F>\t\tFind"
 msgstr "<%s-F>\t\tSuchen"
 
-#: gitk:2707
+#: gitk:2839
 #, tcl-format
 msgid "<%s-G>\t\tMove to next find hit"
 msgstr "<%s-G>\t\tWeitersuchen"
 
-#: gitk:2708
+#: gitk:2840
 msgid "<Return>\tMove to next find hit"
 msgstr "<Eingabetaste>\tWeitersuchen"
 
-#: gitk:2709
+#: gitk:2841
 msgid "/\t\tFocus the search box"
 msgstr "/\t\tTastaturfokus ins Suchfeld"
 
-#: gitk:2710
+#: gitk:2842
 msgid "?\t\tMove to previous find hit"
 msgstr "?\t\tRückwärts weitersuchen"
 
-#: gitk:2711
+#: gitk:2843
 msgid "f\t\tScroll diff view to next file"
 msgstr "f\t\tVergleich zur nächsten Datei blättern"
 
-#: gitk:2712
+#: gitk:2844
 #, tcl-format
 msgid "<%s-S>\t\tSearch for next hit in diff view"
 msgstr "<%s-S>\t\tWeitersuchen im Vergleich"
 
-#: gitk:2713
+#: gitk:2845
 #, tcl-format
 msgid "<%s-R>\t\tSearch for previous hit in diff view"
 msgstr "<%s-R>\t\tRückwärts weitersuchen im Vergleich"
 
-#: gitk:2714
+#: gitk:2846
 #, tcl-format
 msgid "<%s-KP+>\tIncrease font size"
 msgstr "<%s-Nummerblock-Plus>\tSchrift vergrößern"
 
-#: gitk:2715
+#: gitk:2847
 #, tcl-format
 msgid "<%s-plus>\tIncrease font size"
 msgstr "<%s-Plus>\tSchrift vergrößern"
 
-#: gitk:2716
+#: gitk:2848
 #, tcl-format
 msgid "<%s-KP->\tDecrease font size"
 msgstr "<%s-Nummernblock-Minus> Schrift verkleinern"
 
-#: gitk:2717
+#: gitk:2849
 #, tcl-format
 msgid "<%s-minus>\tDecrease font size"
 msgstr "<%s-Minus>\tSchrift verkleinern"
 
-#: gitk:2718
+#: gitk:2850
 msgid "<F5>\t\tUpdate"
 msgstr "<F5>\t\tAktualisieren"
 
-#: gitk:3173
-#, tcl-format
-msgid "Error getting \"%s\" from %s:"
-msgstr "Fehler beim Holen von »%s« von »%s«:"
-
-#: gitk:3230 gitk:3239
+#: gitk:3305 gitk:3314
 #, tcl-format
 msgid "Error creating temporary directory %s:"
 msgstr "Fehler beim Erzeugen des temporären Verzeichnisses »%s«:"
 
-#: gitk:3251
+#: gitk:3327
+#, tcl-format
+msgid "Error getting \"%s\" from %s:"
+msgstr "Fehler beim Holen von »%s« von »%s«:"
+
+#: gitk:3390
 msgid "command failed:"
 msgstr "Kommando fehlgeschlagen:"
 
-#: gitk:3397
+#: gitk:3539
 msgid "No such commit"
 msgstr "Version nicht gefunden"
 
-#: gitk:3411
+#: gitk:3553
 msgid "git gui blame: command failed:"
 msgstr "git gui blame: Kommando fehlgeschlagen:"
 
-#: gitk:3442
+#: gitk:3584
 #, tcl-format
 msgid "Couldn't read merge head: %s"
 msgstr "Zusammenführungs-Spitze konnte nicht gelesen werden: %s"
 
-#: gitk:3450
+#: gitk:3592
 #, tcl-format
 msgid "Error reading index: %s"
 msgstr "Fehler beim Lesen der Bereitstellung (»index«): %s"
 
-#: gitk:3475
+#: gitk:3617
 #, tcl-format
 msgid "Couldn't start git blame: %s"
 msgstr "»git blame« konnte nicht gestartet werden: %s"
 
-#: gitk:3478 gitk:6209
+#: gitk:3620 gitk:6409
 msgid "Searching"
 msgstr "Suchen"
 
-#: gitk:3510
+#: gitk:3652
 #, tcl-format
 msgid "Error running git blame: %s"
 msgstr "Fehler beim Ausführen von »git blame«: %s"
 
-#: gitk:3538
+#: gitk:3680
 #, tcl-format
 msgid "That line comes from commit %s,  which is not in this view"
 msgstr ""
 "Diese Zeile stammt aus Version %s, die nicht in dieser Ansicht gezeigt wird"
 
-#: gitk:3552
+#: gitk:3694
 msgid "External diff viewer failed:"
 msgstr "Externes Diff-Programm fehlgeschlagen:"
 
-#: gitk:3670
+#: gitk:3812
 msgid "Gitk view definition"
 msgstr "Gitk-Ansichten"
 
-#: gitk:3674
+#: gitk:3816
 msgid "Remember this view"
 msgstr "Diese Ansicht speichern"
 
-#: gitk:3675
-msgid "Commits to include (arguments to git log):"
-msgstr "Versionen anzeigen (Argumente von git log):"
+#: gitk:3817
+msgid "References (space separated list):"
+msgstr "Zweige/Markierungen (durch Leerzeichen getrennte Liste):"
 
-#: gitk:3676
-msgid "Use all refs"
-msgstr "Alle Zweige verwenden"
+#: gitk:3818
+msgid "Branches & tags:"
+msgstr "Zweige/Markierungen:"
 
-#: gitk:3677
+#: gitk:3819
+msgid "All refs"
+msgstr "Alle Markierungen und Zweige"
+
+#: gitk:3820
+msgid "All (local) branches"
+msgstr "Alle (lokalen) Zweige"
+
+#: gitk:3821
+msgid "All tags"
+msgstr "Alle Markierungen"
+
+#: gitk:3822
+msgid "All remote-tracking branches"
+msgstr "Alle Übernahmezweige"
+
+#: gitk:3823
+msgid "Commit Info (regular expressions):"
+msgstr "Versionsinformationen (reguläre Ausdrücke):"
+
+#: gitk:3824
+msgid "Author:"
+msgstr "Autor:"
+
+#: gitk:3825
+msgid "Committer:"
+msgstr "Eintragender:"
+
+#: gitk:3826
+msgid "Commit Message:"
+msgstr "Versionsbeschreibung:"
+
+#: gitk:3827
+msgid "Matches all Commit Info criteria"
+msgstr "Alle Versionsinformationen-Kriterien erfüllen"
+
+#: gitk:3828
+msgid "Changes to Files:"
+msgstr "Dateien:"
+
+#: gitk:3829
+msgid "Fixed String"
+msgstr "Zeichenkette"
+
+#: gitk:3830
+msgid "Regular Expression"
+msgstr "Regulärer Ausdruck"
+
+#: gitk:3831
+msgid "Search string:"
+msgstr "Suchausdruck:"
+
+#: gitk:3832
+msgid ""
+"Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 "
+"15:27:38\"):"
+msgstr "Datum (»2 weeks ago«, »2009-03-17 15:27:38«, »March 17, 2009 15:27:38«)"
+
+#: gitk:3833
+msgid "Since:"
+msgstr "Von:"
+
+#: gitk:3834
+msgid "Until:"
+msgstr "Bis:"
+
+#: gitk:3835
+msgid "Limit and/or skip a number of revisions (positive integer):"
+msgstr "Versionsanzahl begrenzen oder einige überspringen (ganzzahliger Wert):"
+
+#: gitk:3836
+msgid "Number to show:"
+msgstr "Anzeigen:"
+
+#: gitk:3837
+msgid "Number to skip:"
+msgstr "Überspringen:"
+
+#: gitk:3838
+msgid "Miscellaneous options:"
+msgstr "Sonstiges:"
+
+#: gitk:3839
 msgid "Strictly sort by date"
 msgstr "Streng nach Datum sortieren"
 
-#: gitk:3678
+#: gitk:3840
 msgid "Mark branch sides"
 msgstr "Zweig-Seiten markieren"
 
-#: gitk:3679
-msgid "Since date:"
-msgstr "Von Datum:"
-
-#: gitk:3680
-msgid "Until date:"
-msgstr "Bis Datum:"
-
-#: gitk:3681
-msgid "Max count:"
-msgstr "Max. Anzahl:"
-
-#: gitk:3682
-msgid "Skip:"
-msgstr "Überspringen:"
-
-#: gitk:3683
+#: gitk:3841
 msgid "Limit to first parent"
 msgstr "Auf erste Elternversion beschränken"
 
-#: gitk:3684
-msgid "Command to generate more commits to include:"
-msgstr "Versionsliste durch folgendes Kommando erzeugen lassen:"
+#: gitk:3842
+msgid "Simple history"
+msgstr "Einfache Historie"
 
-#: gitk:3780
-msgid "Gitk: edit view"
-msgstr "Gitk: Ansicht bearbeiten"
+#: gitk:3843
+msgid "Additional arguments to git log:"
+msgstr "Zusätzliche Argumente für »git log«:"
 
-#: gitk:3793
-msgid "Name"
-msgstr "Name"
-
-#: gitk:3841
+#: gitk:3844
 msgid "Enter files and directories to include, one per line:"
 msgstr "Folgende Dateien und Verzeichnisse anzeigen (eine pro Zeile):"
 
-#: gitk:3855
+#: gitk:3845
+msgid "Command to generate more commits to include:"
+msgstr "Versionsliste durch folgendes Kommando erzeugen lassen:"
+
+#: gitk:3967
+msgid "Gitk: edit view"
+msgstr "Gitk: Ansicht bearbeiten"
+
+#: gitk:3975
+msgid "-- criteria for selecting revisions"
+msgstr "-- Auswahl der angezeigten Versionen"
+
+#: gitk:3980
+msgid "View Name"
+msgstr "Ansichtsname"
+
+#: gitk:4055
 msgid "Apply (F5)"
 msgstr "Anwenden (F5)"
 
-#: gitk:3893
+#: gitk:4093
 msgid "Error in commit selection arguments:"
 msgstr "Fehler in den ausgewählten Versionen:"
 
-#: gitk:3946 gitk:3998 gitk:4446 gitk:4460 gitk:5721 gitk:11114 gitk:11115
+#: gitk:4146 gitk:4198 gitk:4646 gitk:4660 gitk:5921 gitk:11534 gitk:11535
 msgid "None"
 msgstr "Keine"
 
-#: gitk:4394 gitk:6241 gitk:7993 gitk:8008
+#: gitk:4594 gitk:6441 gitk:8287 gitk:8302
 msgid "Date"
 msgstr "Datum"
 
-#: gitk:4394 gitk:6241
+#: gitk:4594 gitk:6441
 msgid "CDate"
 msgstr "Eintragedatum"
 
-#: gitk:4543 gitk:4548
+#: gitk:4743 gitk:4748
 msgid "Descendant"
 msgstr "Abkömmling"
 
-#: gitk:4544
+#: gitk:4744
 msgid "Not descendant"
 msgstr "Kein Abkömmling"
 
-#: gitk:4551 gitk:4556
+#: gitk:4751 gitk:4756
 msgid "Ancestor"
 msgstr "Vorgänger"
 
-#: gitk:4552
+#: gitk:4752
 msgid "Not ancestor"
 msgstr "Kein Vorgänger"
 
-#: gitk:4842
+#: gitk:5042
 msgid "Local changes checked in to index but not committed"
 msgstr "Lokale Änderungen bereitgestellt, aber nicht eingetragen"
 
-#: gitk:4878
+#: gitk:5078
 msgid "Local uncommitted changes, not checked in to index"
 msgstr "Lokale Änderungen, nicht bereitgestellt"
 
-#: gitk:6559
+#: gitk:6759
 msgid "many"
 msgstr "viele"
 
-#: gitk:6743
+#: gitk:6942
 msgid "Tags:"
 msgstr "Markierungen:"
 
-#: gitk:6760 gitk:6766 gitk:7986
+#: gitk:6959 gitk:6965 gitk:8280
 msgid "Parent"
 msgstr "Eltern"
 
-#: gitk:6771
+#: gitk:6970
 msgid "Child"
 msgstr "Kind"
 
-#: gitk:6780
+#: gitk:6979
 msgid "Branch"
 msgstr "Zweig"
 
-#: gitk:6783
+#: gitk:6982
 msgid "Follows"
 msgstr "Folgt auf"
 
-#: gitk:6786
+#: gitk:6985
 msgid "Precedes"
 msgstr "Vorgänger von"
 
-#: gitk:7279
+#: gitk:7522
 #, tcl-format
 msgid "Error getting diffs: %s"
 msgstr "Fehler beim Laden des Vergleichs: %s"
 
-#: gitk:7819
+#: gitk:8108
 msgid "Goto:"
 msgstr "Gehe zu:"
 
-#: gitk:7821
-msgid "SHA1 ID:"
-msgstr "SHA1-Hashwert:"
-
-#: gitk:7840
+#: gitk:8129
 #, tcl-format
 msgid "Short SHA1 id %s is ambiguous"
 msgstr "Kurzer SHA1-Hashwert »%s« ist mehrdeutig"
 
-#: gitk:7852
+#: gitk:8136
+#, tcl-format
+msgid "Revision %s is not known"
+msgstr "Version »%s« ist unbekannt"
+
+#: gitk:8146
 #, tcl-format
 msgid "SHA1 id %s is not known"
 msgstr "SHA1-Hashwert »%s« ist unbekannt"
 
-#: gitk:7854
+#: gitk:8148
 #, tcl-format
-msgid "Tag/Head %s is not known"
-msgstr "Markierung/Zweig »%s« ist unbekannt"
+msgid "Revision %s is not in the current view"
+msgstr "Version »%s« wird in der aktuellen Ansicht nicht angezeigt"
 
-#: gitk:7996
+#: gitk:8290
 msgid "Children"
 msgstr "Kinder"
 
-#: gitk:8053
+#: gitk:8348
 #, tcl-format
 msgid "Reset %s branch to here"
 msgstr "Zweig »%s« hierher zurücksetzen"
 
-#: gitk:8055
+#: gitk:8350
 msgid "Detached head: can't reset"
 msgstr "Zweigspitze ist abgetrennt: Zurücksetzen nicht möglich"
 
-#: gitk:8164 gitk:8170
+#: gitk:8459 gitk:8465
 msgid "Skipping merge commit "
 msgstr "Überspringe Zusammenführungs-Version "
 
-#: gitk:8179 gitk:8184
+#: gitk:8474 gitk:8479
 msgid "Error getting patch ID for "
 msgstr "Fehler beim Holen der Patch-ID für "
 
-#: gitk:8180 gitk:8185
+#: gitk:8475 gitk:8480
 msgid " - stopping\n"
 msgstr " - Abbruch.\n"
 
-#: gitk:8190 gitk:8193 gitk:8201 gitk:8211 gitk:8220
+#: gitk:8485 gitk:8488 gitk:8496 gitk:8510 gitk:8519
 msgid "Commit "
 msgstr "Version "
 
-#: gitk:8194
+#: gitk:8489
 msgid ""
 " is the same patch as\n"
 "       "
@@ -762,7 +844,7 @@
 " ist das gleiche Patch wie\n"
 "       "
 
-#: gitk:8202
+#: gitk:8497
 msgid ""
 " differs from\n"
 "       "
@@ -770,117 +852,138 @@
 " ist unterschiedlich von\n"
 "       "
 
-#: gitk:8204
-msgid "- stopping\n"
-msgstr "- Abbruch.\n"
+#: gitk:8499
+msgid ""
+"Diff of commits:\n"
+"\n"
+msgstr ""
+"Vergleich der Versionen:\n"
+"\n"
 
-#: gitk:8212 gitk:8221
+#: gitk:8511 gitk:8520
 #, tcl-format
 msgid " has %s children - stopping\n"
 msgstr " hat %s Kinder. Abbruch\n"
 
-#: gitk:8252
+#: gitk:8539
+#, tcl-format
+msgid "Error writing commit to file: %s"
+msgstr "Fehler beim Schreiben der Version in Datei: %s"
+
+#: gitk:8545
+#, tcl-format
+msgid "Error diffing commits: %s"
+msgstr "Fehler beim Vergleichen der Versionen: %s"
+
+#: gitk:8575
 msgid "Top"
 msgstr "Oben"
 
-#: gitk:8253
+#: gitk:8576
 msgid "From"
 msgstr "Von"
 
-#: gitk:8258
+#: gitk:8581
 msgid "To"
 msgstr "bis"
 
-#: gitk:8282
+#: gitk:8605
 msgid "Generate patch"
 msgstr "Patch erstellen"
 
-#: gitk:8284
+#: gitk:8607
 msgid "From:"
 msgstr "Von:"
 
-#: gitk:8293
+#: gitk:8616
 msgid "To:"
 msgstr "bis:"
 
-#: gitk:8302
+#: gitk:8625
 msgid "Reverse"
 msgstr "Umgekehrt"
 
-#: gitk:8304 gitk:8489
+#: gitk:8627 gitk:8822
 msgid "Output file:"
 msgstr "Ausgabedatei:"
 
-#: gitk:8310
+#: gitk:8633
 msgid "Generate"
 msgstr "Erzeugen"
 
-#: gitk:8348
+#: gitk:8671
 msgid "Error creating patch:"
 msgstr "Fehler beim Erzeugen des Patches:"
 
-#: gitk:8371 gitk:8477 gitk:8534
+#: gitk:8694 gitk:8810 gitk:8867
 msgid "ID:"
 msgstr "ID:"
 
-#: gitk:8380
+#: gitk:8703
 msgid "Tag name:"
 msgstr "Markierungsname:"
 
-#: gitk:8384 gitk:8543
+#: gitk:8706
+msgid "Tag message is optional"
+msgstr "Eine Markierungsbeschreibung ist optional"
+
+#: gitk:8708
+msgid "Tag message:"
+msgstr "Markierungsbeschreibung:"
+
+#: gitk:8712 gitk:8876
 msgid "Create"
 msgstr "Erstellen"
 
-#: gitk:8401
+#: gitk:8730
 msgid "No tag name specified"
 msgstr "Kein Markierungsname angegeben"
 
-#: gitk:8405
+#: gitk:8734
 #, tcl-format
 msgid "Tag \"%s\" already exists"
 msgstr "Markierung »%s« existiert bereits."
 
-#: gitk:8411
+#: gitk:8744
 msgid "Error creating tag:"
 msgstr "Fehler beim Erstellen der Markierung:"
 
-#: gitk:8486
+#: gitk:8819
 msgid "Command:"
 msgstr "Kommando:"
 
-#: gitk:8494
+#: gitk:8827
 msgid "Write"
 msgstr "Schreiben"
 
-#: gitk:8512
+#: gitk:8845
 msgid "Error writing commit:"
 msgstr "Fehler beim Schreiben der Version:"
 
-#: gitk:8539
+#: gitk:8872
 msgid "Name:"
 msgstr "Name:"
 
-#: gitk:8562
+#: gitk:8895
 msgid "Please specify a name for the new branch"
 msgstr "Bitte geben Sie einen Namen für den neuen Zweig an."
 
-#: gitk:8567
+#: gitk:8900
 #, tcl-format
 msgid "Branch '%s' already exists. Overwrite?"
 msgstr "Zweig »%s« existiert bereits. Soll er überschrieben werden?"
 
-#: gitk:8633
+#: gitk:8966
 #, tcl-format
 msgid "Commit %s is already included in branch %s -- really re-apply it?"
 msgstr ""
-"Version »%s« ist bereits im Zweig »%s« enthalten -- trotzdem erneut "
-"eintragen?"
+"Version »%s« ist bereits im Zweig »%s« enthalten -- trotzdem erneut eintragen?"
 
-#: gitk:8638
+#: gitk:8971
 msgid "Cherry-picking"
 msgstr "Version pflücken"
 
-#: gitk:8647
+#: gitk:8980
 #, tcl-format
 msgid ""
 "Cherry-pick failed because of local changes to file '%s'.\n"
@@ -890,7 +993,7 @@
 "vorliegen. Bitte diese Änderungen eintragen, zurücksetzen oder\n"
 "zwischenspeichern (»git stash«) und dann erneut versuchen."
 
-#: gitk:8653
+#: gitk:8986
 msgid ""
 "Cherry-pick failed because of merge conflict.\n"
 "Do you wish to run git citool to resolve it?"
@@ -899,34 +1002,34 @@
 "ist. Soll das Zusammenführungs-Werkzeug (»git citool«) aufgerufen\n"
 "werden, um diesen Konflikt aufzulösen?"
 
-#: gitk:8669
+#: gitk:9002
 msgid "No changes committed"
 msgstr "Keine Änderungen eingetragen"
 
-#: gitk:8695
+#: gitk:9028
 msgid "Confirm reset"
 msgstr "Zurücksetzen bestätigen"
 
-#: gitk:8697
+#: gitk:9030
 #, tcl-format
 msgid "Reset branch %s to %s?"
 msgstr "Zweig »%s« auf »%s« zurücksetzen?"
 
-#: gitk:8701
+#: gitk:9032
 msgid "Reset type:"
 msgstr "Art des Zurücksetzens:"
 
-#: gitk:8705
+#: gitk:9035
 msgid "Soft: Leave working tree and index untouched"
 msgstr "Harmlos: Arbeitskopie und Bereitstellung unverändert"
 
-#: gitk:8708
+#: gitk:9038
 msgid "Mixed: Leave working tree untouched, reset index"
 msgstr ""
 "Gemischt: Arbeitskopie unverändert,\n"
 "Bereitstellung zurückgesetzt"
 
-#: gitk:8711
+#: gitk:9041
 msgid ""
 "Hard: Reset working tree and index\n"
 "(discard ALL local changes)"
@@ -934,21 +1037,21 @@
 "Hart: Arbeitskopie und Bereitstellung\n"
 "(Alle lokalen Änderungen werden gelöscht)"
 
-#: gitk:8728
+#: gitk:9058
 msgid "Resetting"
 msgstr "Zurücksetzen"
 
-#: gitk:8785
+#: gitk:9118
 msgid "Checking out"
 msgstr "Umstellen"
 
-#: gitk:8838
+#: gitk:9171
 msgid "Cannot delete the currently checked-out branch"
 msgstr ""
 "Der Zweig, auf den die Arbeitskopie momentan umgestellt ist, kann nicht "
 "gelöscht werden."
 
-#: gitk:8844
+#: gitk:9177
 #, tcl-format
 msgid ""
 "The commits on branch %s aren't on any other branch.\n"
@@ -957,16 +1060,16 @@
 "Die Versionen auf Zweig »%s« existieren auf keinem anderen Zweig.\n"
 "Zweig »%s« trotzdem löschen?"
 
-#: gitk:8875
+#: gitk:9208
 #, tcl-format
 msgid "Tags and heads: %s"
 msgstr "Markierungen und Zweige: %s"
 
-#: gitk:8890
+#: gitk:9223
 msgid "Filter"
 msgstr "Filtern"
 
-#: gitk:9185
+#: gitk:9518
 msgid ""
 "Error reading commit topology information; branch and preceding/following "
 "tag information will be incomplete."
@@ -974,182 +1077,202 @@
 "Fehler beim Lesen der Strukturinformationen; Zweige und Informationen zu "
 "Vorgänger/Nachfolger werden unvollständig sein."
 
-#: gitk:10171
+#: gitk:10504
 msgid "Tag"
 msgstr "Markierung"
 
-#: gitk:10171
+#: gitk:10504
 msgid "Id"
 msgstr "Id"
 
-#: gitk:10219
+#: gitk:10554
 msgid "Gitk font chooser"
 msgstr "Gitk-Schriften wählen"
 
-#: gitk:10236
+#: gitk:10571
 msgid "B"
 msgstr "F"
 
-#: gitk:10239
+#: gitk:10574
 msgid "I"
 msgstr "K"
 
-#: gitk:10334
+#: gitk:10692
 msgid "Gitk preferences"
 msgstr "Gitk-Einstellungen"
 
-#: gitk:10336
+#: gitk:10694
 msgid "Commit list display options"
 msgstr "Anzeige der Versionsliste"
 
-#: gitk:10339
+#: gitk:10697
 msgid "Maximum graph width (lines)"
 msgstr "Maximale Graphenbreite (Zeilen)"
 
-#: gitk:10343
+#: gitk:10700
 #, tcl-format
 msgid "Maximum graph width (% of pane)"
 msgstr "Maximale Graphenbreite (% des Fensters)"
 
-#: gitk:10347
+#: gitk:10703
 msgid "Show local changes"
 msgstr "Lokale Änderungen anzeigen"
 
-#: gitk:10350
+#: gitk:10706
 msgid "Auto-select SHA1"
 msgstr "SHA1-Hashwert automatisch auswählen"
 
-#: gitk:10354
+#: gitk:10709
+msgid "Hide remote refs"
+msgstr "Entfernte Zweige/Markierungen ausblenden"
+
+#: gitk:10713
 msgid "Diff display options"
 msgstr "Anzeige des Vergleichs"
 
-#: gitk:10356
+#: gitk:10715
 msgid "Tab spacing"
 msgstr "Tabulatorbreite"
 
-#: gitk:10359
+#: gitk:10718
 msgid "Display nearby tags"
 msgstr "Naheliegende Markierungen anzeigen"
 
-#: gitk:10362
+#: gitk:10721
 msgid "Limit diffs to listed paths"
 msgstr "Vergleich nur für angezeigte Pfade"
 
-#: gitk:10365
+#: gitk:10724
 msgid "Support per-file encodings"
 msgstr "Zeichenkodierung pro Datei ermitteln"
 
-#: gitk:10371 gitk:10436
+#: gitk:10730 gitk:10819
 msgid "External diff tool"
 msgstr "Externes Diff-Programm"
 
-#: gitk:10373
+#: gitk:10731
 msgid "Choose..."
 msgstr "Wählen ..."
 
-#: gitk:10378
+#: gitk:10736
+msgid "General options"
+msgstr "Allgemeine Optionen"
+
+#: gitk:10739
+msgid "Use themed widgets"
+msgstr "Aussehen der Benutzeroberfläche durch Thema bestimmen"
+
+#: gitk:10741
+msgid "(change requires restart)"
+msgstr "(Änderungen werden erst nach Neustart wirksam)"
+
+#: gitk:10743
+msgid "(currently unavailable)"
+msgstr "(Momentan nicht verfügbar)"
+
+#: gitk:10747
 msgid "Colors: press to choose"
 msgstr "Farben: Klicken zum Wählen"
 
-#: gitk:10381
+#: gitk:10750
+msgid "Interface"
+msgstr "Benutzeroberfläche"
+
+#: gitk:10751
+msgid "interface"
+msgstr "Benutzeroberfläche"
+
+#: gitk:10754
 msgid "Background"
 msgstr "Hintergrund"
 
-#: gitk:10382 gitk:10412
+#: gitk:10755 gitk:10785
 msgid "background"
 msgstr "Hintergrund"
 
-#: gitk:10385
+#: gitk:10758
 msgid "Foreground"
 msgstr "Vordergrund"
 
-#: gitk:10386
+#: gitk:10759
 msgid "foreground"
 msgstr "Vordergrund"
 
-#: gitk:10389
+#: gitk:10762
 msgid "Diff: old lines"
 msgstr "Vergleich: Alte Zeilen"
 
-#: gitk:10390
+#: gitk:10763
 msgid "diff old lines"
 msgstr "Vergleich - Alte Zeilen"
 
-#: gitk:10394
+#: gitk:10767
 msgid "Diff: new lines"
 msgstr "Vergleich: Neue Zeilen"
 
-#: gitk:10395
+#: gitk:10768
 msgid "diff new lines"
 msgstr "Vergleich - Neue Zeilen"
 
-#: gitk:10399
+#: gitk:10772
 msgid "Diff: hunk header"
 msgstr "Vergleich: Änderungstitel"
 
-#: gitk:10401
+#: gitk:10774
 msgid "diff hunk header"
 msgstr "Vergleich - Änderungstitel"
 
-#: gitk:10405
+#: gitk:10778
 msgid "Marked line bg"
 msgstr "Hintergrund für markierte Zeile"
 
-#: gitk:10407
+#: gitk:10780
 msgid "marked line background"
 msgstr "Hintergrund für markierte Zeile"
 
-#: gitk:10411
+#: gitk:10784
 msgid "Select bg"
 msgstr "Hintergrundfarbe auswählen"
 
-#: gitk:10415
+#: gitk:10788
 msgid "Fonts: press to choose"
 msgstr "Schriftart: Klicken zum Wählen"
 
-#: gitk:10417
+#: gitk:10790
 msgid "Main font"
 msgstr "Programmschriftart"
 
-#: gitk:10418
+#: gitk:10791
 msgid "Diff display font"
 msgstr "Schriftart für Vergleich"
 
-#: gitk:10419
+#: gitk:10792
 msgid "User interface font"
 msgstr "Beschriftungen"
 
-#: gitk:10446
+#: gitk:10829
 #, tcl-format
 msgid "Gitk: choose color for %s"
 msgstr "Gitk: Farbe wählen für %s"
 
-#: gitk:10893
-msgid ""
-"Sorry, gitk cannot run with this version of Tcl/Tk.\n"
-" Gitk requires at least Tcl/Tk 8.4."
-msgstr ""
-"Gitk läuft nicht mit dieser Version von Tcl/Tk.\n"
-"Gitk benötigt mindestens Tcl/Tk 8.4."
-
-#: gitk:11020
+#: gitk:11433
 msgid "Cannot find a git repository here."
 msgstr "Kein Git-Projektarchiv gefunden."
 
-#: gitk:11024
+#: gitk:11437
 #, tcl-format
 msgid "Cannot find the git directory \"%s\"."
 msgstr "Git-Verzeichnis »%s« wurde nicht gefunden."
 
-#: gitk:11071
+#: gitk:11484
 #, tcl-format
 msgid "Ambiguous argument '%s': both revision and filename"
 msgstr "Mehrdeutige Angabe »%s«: Sowohl Version als auch Dateiname existiert."
 
-#: gitk:11083
+#: gitk:11496
 msgid "Bad arguments to gitk:"
 msgstr "Falsche Kommandozeilen-Parameter für gitk:"
 
-#: gitk:11167
+#: gitk:11587
 msgid "Command line"
 msgstr "Kommandozeile"
diff --git a/gitk-git/po/es.po b/gitk-git/po/es.po
index 0e19b5e..0471dd0 100644
--- a/gitk-git/po/es.po
+++ b/gitk-git/po/es.po
@@ -281,14 +281,14 @@
 "\n"
 "Gitk - a commit viewer for git\n"
 "\n"
-"Copyright © 2005-2008 Paul Mackerras\n"
+"Copyright \\u00a9 2005-2010 Paul Mackerras\n"
 "\n"
 "Use and redistribute under the terms of the GNU General Public License"
 msgstr ""
 "\n"
 "Gitk - un visualizador de revisiones para git\n"
 "\n"
-"Copyright © 2005-2008 Paul Mackerras\n"
+"Copyright \\u00a9 2005-2010 Paul Mackerras\n"
 "\n"
 "Uso y redistribución permitidos según los términos de la Licencia Pública "
 "General de GNU (GNU GPL)"
diff --git a/gitk-git/po/fr.po b/gitk-git/po/fr.po
new file mode 100644
index 0000000..5370ddc
--- /dev/null
+++ b/gitk-git/po/fr.po
@@ -0,0 +1,1254 @@
+# French translation of the gitk package
+# Copyright (C) 2005-2008 Paul Mackerras.  All rights reserved.
+# This file is distributed under the same license as the gitk package.
+# Translators:
+# Emmanuel Trillaud <etrillaud@gmail.com>
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gitk\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2009-10-05 15:16+0200\n"
+"PO-Revision-Date: 2009-11-19 22:13+0100\n"
+"Last-Translator: Emmanuel Trillaud <etrillaud@gmail.com>\n"
+"Language-Team: git@vger.kernel.org\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: French\n"
+"X-Poedit-Country: FRANCE\n"
+
+#: gitk:113
+msgid "Couldn't get list of unmerged files:"
+msgstr "Impossible de récupérer la liste des fichiers non fusionnés :"
+
+#: gitk:269
+msgid "Error parsing revisions:"
+msgstr "Erreur lors du parcours des révisions :"
+
+#: gitk:324
+msgid "Error executing --argscmd command:"
+msgstr "Erreur à l'exécution de la commande --argscmd :"
+
+#: gitk:337
+msgid "No files selected: --merge specified but no files are unmerged."
+msgstr ""
+"Aucun fichier sélectionné : --merge précisé, mais tous les fichiers sont "
+"fusionnés."
+
+# FIXME : améliorer la traduction de 'file limite'
+#: gitk:340
+#, fuzzy
+msgid ""
+"No files selected: --merge specified but no unmerged files are within file "
+"limit."
+msgstr ""
+"Aucun fichier sélectionné : --merge précisé mais aucun fichier non fusionné "
+"n'est dans la limite des fichiers."
+
+#: gitk:362 gitk:509
+msgid "Error executing git log:"
+msgstr "Erreur à l'exécution de git log :"
+
+#: gitk:380 gitk:525
+msgid "Reading"
+msgstr "Lecture en cours"
+
+#: gitk:440 gitk:4123
+msgid "Reading commits..."
+msgstr "Lecture des commits..."
+
+#: gitk:443 gitk:1561 gitk:4126
+msgid "No commits selected"
+msgstr "Aucun commit sélectionné"
+
+#: gitk:1437
+msgid "Can't parse git log output:"
+msgstr "Impossible de lire la sortie de git log :"
+
+#: gitk:1657
+msgid "No commit information available"
+msgstr "Aucune information disponible sur le commit"
+
+#: gitk:1793 gitk:1817 gitk:3916 gitk:8786 gitk:10322 gitk:10498
+msgid "OK"
+msgstr "OK"
+
+#: gitk:1819 gitk:3918 gitk:8383 gitk:8457 gitk:8567 gitk:8616 gitk:8788
+#: gitk:10323 gitk:10499
+msgid "Cancel"
+msgstr "Annuler"
+
+#: gitk:1919
+msgid "Update"
+msgstr "Mise à jour"
+
+#: gitk:1920
+msgid "Reload"
+msgstr "Recharger"
+
+#: gitk:1921
+msgid "Reread references"
+msgstr "Relire les références"
+
+#: gitk:1922
+msgid "List references"
+msgstr "Lister les références"
+
+#: gitk:1924
+msgid "Start git gui"
+msgstr "Démarrer git gui"
+
+#: gitk:1926
+msgid "Quit"
+msgstr "Quitter"
+
+#: gitk:1918
+msgid "File"
+msgstr "Fichier"
+
+#: gitk:1930
+msgid "Preferences"
+msgstr "Préférences"
+
+#: gitk:1929
+msgid "Edit"
+msgstr "Éditer"
+
+#: gitk:1934
+msgid "New view..."
+msgstr "Nouvelle vue..."
+
+#: gitk:1935
+msgid "Edit view..."
+msgstr "Éditer la vue..."
+
+#: gitk:1936
+msgid "Delete view"
+msgstr "Supprimer la vue"
+
+#: gitk:1938
+msgid "All files"
+msgstr "Tous les fichiers"
+
+#: gitk:1933 gitk:3670
+msgid "View"
+msgstr "Vue"
+
+#: gitk:1943 gitk:1953 gitk:2654
+msgid "About gitk"
+msgstr "À propos de gitk"
+
+#: gitk:1944 gitk:1958
+msgid "Key bindings"
+msgstr "Raccourcis clavier"
+
+#: gitk:1942 gitk:1957
+msgid "Help"
+msgstr "Aide"
+
+#: gitk:2018
+msgid "SHA1 ID: "
+msgstr "ID SHA1 :"
+
+#: gitk:2049
+msgid "Row"
+msgstr "Colonne"
+
+#: gitk:2080
+msgid "Find"
+msgstr "Recherche"
+
+#: gitk:2081
+msgid "next"
+msgstr "suivant"
+
+#: gitk:2082
+msgid "prev"
+msgstr "précédent"
+
+#: gitk:2083
+msgid "commit"
+msgstr "commit"
+
+#: gitk:2086 gitk:2088 gitk:4284 gitk:4307 gitk:4331 gitk:6272 gitk:6344
+#: gitk:6428
+msgid "containing:"
+msgstr "contient :"
+
+#: gitk:2089 gitk:3162 gitk:3167 gitk:4359
+msgid "touching paths:"
+msgstr "chemins modifiés :"
+
+#: gitk:2090 gitk:4364
+msgid "adding/removing string:"
+msgstr "ajoute/supprime la chaîne :"
+
+#: gitk:2099 gitk:2101
+msgid "Exact"
+msgstr "Exact"
+
+#: gitk:2101 gitk:4439 gitk:6240
+msgid "IgnCase"
+msgstr "Ignorer la casse"
+
+#: gitk:2101 gitk:4333 gitk:4437 gitk:6236
+msgid "Regexp"
+msgstr "Expression régulière"
+
+#: gitk:2103 gitk:2104 gitk:4458 gitk:4488 gitk:4495 gitk:6364 gitk:6432
+msgid "All fields"
+msgstr "Tous les champs"
+
+#: gitk:2104 gitk:4456 gitk:4488 gitk:6303
+msgid "Headline"
+msgstr "Surligner"
+
+#: gitk:2105 gitk:4456 gitk:6303 gitk:6432 gitk:6866
+msgid "Comments"
+msgstr "Commentaires"
+
+#: gitk:2105 gitk:4456 gitk:4460 gitk:4495 gitk:6303 gitk:6801 gitk:8063
+#: gitk:8078
+msgid "Author"
+msgstr "Auteur"
+
+#: gitk:2105 gitk:4456 gitk:6303 gitk:6803
+msgid "Committer"
+msgstr "Auteur du commit"
+
+#: gitk:2134
+msgid "Search"
+msgstr "Rechercher"
+
+#: gitk:2141
+msgid "Diff"
+msgstr "Diff"
+
+#: gitk:2143
+msgid "Old version"
+msgstr "Ancienne version"
+
+#: gitk:2145
+msgid "New version"
+msgstr "Nouvelle version"
+
+#: gitk:2147
+msgid "Lines of context"
+msgstr "Lignes de contexte"
+
+#: gitk:2157
+msgid "Ignore space change"
+msgstr "Ignorer les modifications d'espace"
+
+#: gitk:2215
+msgid "Patch"
+msgstr "Patch"
+
+#: gitk:2217
+msgid "Tree"
+msgstr "Arbre"
+
+#: gitk:2361 gitk:2378
+msgid "Diff this -> selected"
+msgstr "Diff entre ceci et la sélection"
+
+#: gitk:2362 gitk:2379
+msgid "Diff selected -> this"
+msgstr "Diff entre sélection et ceci"
+
+#: gitk:2363 gitk:2380
+msgid "Make patch"
+msgstr "Créer patch"
+
+#: gitk:2364 gitk:8441
+msgid "Create tag"
+msgstr "Créer tag"
+
+#: gitk:2365 gitk:8547
+msgid "Write commit to file"
+msgstr "Écrire le commit dans un fichier"
+
+#: gitk:2366 gitk:8604
+msgid "Create new branch"
+msgstr "Créer une nouvelle branche"
+
+#: gitk:2367
+msgid "Cherry-pick this commit"
+msgstr "Cueillir (cherry-pick) ce commit"
+
+#: gitk:2368
+msgid "Reset HEAD branch to here"
+msgstr "Réinitialiser la branche HEAD vers cet état"
+
+#: gitk:2369
+msgid "Mark this commit"
+msgstr "Marquer ce commit"
+
+#: gitk:2370
+msgid "Return to mark"
+msgstr "Retourner à la marque"
+
+#: gitk:2371
+msgid "Find descendant of this and mark"
+msgstr "Chercher le descendant de ceci et le marquer"
+
+#: gitk:2372
+msgid "Compare with marked commit"
+msgstr "Comparer avec le commit marqué"
+
+#: gitk:2386
+msgid "Check out this branch"
+msgstr "Récupérer cette branche"
+
+#: gitk:2387
+msgid "Remove this branch"
+msgstr "Supprimer cette branche"
+
+#: gitk:2394
+msgid "Highlight this too"
+msgstr "Surligner également ceci"
+
+#: gitk:2395
+msgid "Highlight this only"
+msgstr "Surligner seulement ceci"
+
+#: gitk:2396
+msgid "External diff"
+msgstr "Diff externe"
+
+#: gitk:2397
+msgid "Blame parent commit"
+msgstr "Blâmer le commit parent"
+
+#: gitk:2404
+msgid "Show origin of this line"
+msgstr "Montrer l'origine de cette ligne"
+
+#: gitk:2405
+msgid "Run git gui blame on this line"
+msgstr "Exécuter git gui blame sur cette ligne"
+
+#: gitk:2656
+msgid ""
+"\n"
+"Gitk - a commit viewer for git\n"
+"\n"
+"Copyright \\u00a9 2005-2010 Paul Mackerras\n"
+"\n"
+"Use and redistribute under the terms of the GNU General Public License"
+msgstr ""
+"\n"
+"Gitk - visualisateur de commit pour git\n"
+"\n"
+"Copyright \\u00a9 2005-2010 Paul Mackerras\n"
+"\n"
+"Utilisation et redistribution soumises aux termes de la GNU General Public "
+"License"
+
+#: gitk:2664 gitk:2726 gitk:8969
+msgid "Close"
+msgstr "Fermer"
+
+#: gitk:2683
+msgid "Gitk key bindings"
+msgstr "Raccourcis clavier de Gitk"
+
+#: gitk:2686
+msgid "Gitk key bindings:"
+msgstr "Raccourcis clavier de Gitk :"
+
+#: gitk:2688
+#, tcl-format
+msgid "<%s-Q>\t\tQuit"
+msgstr "<%s-Q>\t\tQuitter"
+
+#: gitk:2689
+msgid "<Home>\t\tMove to first commit"
+msgstr "<Début>\t\tAller au premier commit"
+
+#: gitk:2690
+msgid "<End>\t\tMove to last commit"
+msgstr "<Fin>\t\tAller au dernier commit"
+
+#: gitk:2691
+msgid "<Up>, p, i\tMove up one commit"
+msgstr "<Haut>, p, i\t Aller au commit suivant"
+
+#: gitk:2692
+msgid "<Down>, n, k\tMove down one commit"
+msgstr "<Bas>, n, k\t Aller au commit précédent"
+
+#: gitk:2693
+msgid "<Left>, z, j\tGo back in history list"
+msgstr "<Gauche>, z, j\tReculer dans l'historique"
+
+#: gitk:2694
+msgid "<Right>, x, l\tGo forward in history list"
+msgstr "<Droite>, x, l\tAvancer dans l'historique"
+
+#: gitk:2695
+msgid "<PageUp>\tMove up one page in commit list"
+msgstr "<PageUp>\tMonter d'une page dans la liste des commits"
+
+#: gitk:2696
+msgid "<PageDown>\tMove down one page in commit list"
+msgstr "<PageDown>\tDescendre d'une page dans la liste des commits"
+
+#: gitk:2697
+#, tcl-format
+msgid "<%s-Home>\tScroll to top of commit list"
+msgstr "<%s-Début>\tAller en haut de la liste des commits"
+
+#: gitk:2698
+#, tcl-format
+msgid "<%s-End>\tScroll to bottom of commit list"
+msgstr "<%s-End>\tAller en bas de la liste des commits"
+
+#: gitk:2699
+#, tcl-format
+msgid "<%s-Up>\tScroll commit list up one line"
+msgstr "<%s-Up>\tMonter d'une ligne dans la liste des commits"
+
+#: gitk:2700
+#, tcl-format
+msgid "<%s-Down>\tScroll commit list down one line"
+msgstr "<%s-Down>\tDescendre d'une ligne dans la liste des commits"
+
+#: gitk:2701
+#, tcl-format
+msgid "<%s-PageUp>\tScroll commit list up one page"
+msgstr "<%s-PageUp>\tMonter d'une page dans la liste des commits"
+
+#: gitk:2702
+#, tcl-format
+msgid "<%s-PageDown>\tScroll commit list down one page"
+msgstr "<%s-PageDown>\tDescendre d'une page dans la liste des commits"
+
+#: gitk:2703
+msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
+msgstr ""
+"<Shift-Up>\tRecherche en arrière (vers l'avant, commits les plus anciens)"
+
+#: gitk:2704
+msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
+msgstr ""
+"<Shift-Down>\tRecherche en avant (vers l'arrière, commit les plus récents)"
+
+#: gitk:2705
+msgid "<Delete>, b\tScroll diff view up one page"
+msgstr "<Supprimer>, b\tMonter d'une page dans la vue des diff"
+
+#: gitk:2706
+msgid "<Backspace>\tScroll diff view up one page"
+msgstr "<Backspace>\tMonter d'une page dans la vue des diff"
+
+#: gitk:2707
+msgid "<Space>\t\tScroll diff view down one page"
+msgstr "<Espace>\t\tDescendre d'une page dans la vue des diff"
+
+#: gitk:2708
+msgid "u\t\tScroll diff view up 18 lines"
+msgstr "u\t\tMonter de 18 lignes dans la vue des diff"
+
+#: gitk:2709
+msgid "d\t\tScroll diff view down 18 lines"
+msgstr "d\t\tDescendre de 18 lignes dans la vue des diff"
+
+#: gitk:2710
+#, tcl-format
+msgid "<%s-F>\t\tFind"
+msgstr "<%s-F>\t\tRechercher"
+
+#: gitk:2711
+#, tcl-format
+msgid "<%s-G>\t\tMove to next find hit"
+msgstr "<%s-G>\t\tAller au résultat de recherche suivant"
+
+#: gitk:2712
+msgid "<Return>\tMove to next find hit"
+msgstr "<Return>\t\tAller au résultat de recherche suivant"
+
+#: gitk:2713
+msgid "/\t\tFocus the search box"
+msgstr "/\t\tFocus sur la zone de recherche"
+
+#: gitk:2714
+msgid "?\t\tMove to previous find hit"
+msgstr "?\t\tAller au résultat de recherche précédent"
+
+#: gitk:2715
+msgid "f\t\tScroll diff view to next file"
+msgstr "f\t\tAller au prochain fichier dans la vue des diff"
+
+#: gitk:2716
+#, tcl-format
+msgid "<%s-S>\t\tSearch for next hit in diff view"
+msgstr "<%s-S>\t\tAller au résultat suivant dans la vue des diff"
+
+#: gitk:2717
+#, tcl-format
+msgid "<%s-R>\t\tSearch for previous hit in diff view"
+msgstr "<%s-R>\t\tAller au résultat précédent dans la vue des diff"
+
+#: gitk:2718
+#, tcl-format
+msgid "<%s-KP+>\tIncrease font size"
+msgstr "<%s-KP+>\tAugmenter la taille de la police"
+
+#: gitk:2719
+#, tcl-format
+msgid "<%s-plus>\tIncrease font size"
+msgstr "<%s-plus>\tAugmenter la taille de la police"
+
+#: gitk:2720
+#, tcl-format
+msgid "<%s-KP->\tDecrease font size"
+msgstr "<%s-KP->\tDiminuer la taille de la police"
+
+#: gitk:2721
+#, tcl-format
+msgid "<%s-minus>\tDecrease font size"
+msgstr "<%s-minus>\tDiminuer la taille de la police"
+
+#: gitk:2722
+msgid "<F5>\t\tUpdate"
+msgstr "<F5>\t\tMise à jour"
+
+#: gitk:3177
+#, tcl-format
+msgid "Error getting \"%s\" from %s:"
+msgstr "Erreur en obtenant \"%s\" de %s:"
+
+#: gitk:3234 gitk:3243
+#, tcl-format
+msgid "Error creating temporary directory %s:"
+msgstr "Erreur lors de la création du répertoire temporaire %s :"
+
+#: gitk:3255
+msgid "command failed:"
+msgstr "échec de la commande :"
+
+#: gitk:3401
+msgid "No such commit"
+msgstr "Commit inexistant"
+
+#: gitk:3415
+msgid "git gui blame: command failed:"
+msgstr "git gui blame : échec de la commande :"
+
+#: gitk:3446
+#, tcl-format
+msgid "Couldn't read merge head: %s"
+msgstr "Impossible de lire le head de la fusion : %s"
+
+#: gitk:3454
+#, tcl-format
+msgid "Error reading index: %s"
+msgstr "Erreur à la lecture de l'index : %s"
+
+#: gitk:3479
+#, tcl-format
+msgid "Couldn't start git blame: %s"
+msgstr "Impossible de démarrer git blame : %s"
+
+#: gitk:3482 gitk:6271
+msgid "Searching"
+msgstr "Recherche en cours"
+
+#: gitk:3514
+#, tcl-format
+msgid "Error running git blame: %s"
+msgstr "Erreur à l'exécution de git blame : %s"
+
+#: gitk:3542
+#, tcl-format
+msgid "That line comes from commit %s,  which is not in this view"
+msgstr "Cette ligne est issue du commit %s, qui n'est pas dans cette vue"
+
+#: gitk:3556
+msgid "External diff viewer failed:"
+msgstr "Échec de l'outil externe de visualisation des diff"
+
+#: gitk:3674
+msgid "Gitk view definition"
+msgstr "Définition des vues de Gitk"
+
+#: gitk:3678
+msgid "Remember this view"
+msgstr "Se souvenir de cette vue"
+
+#: gitk:3679
+msgid "References (space separated list):"
+msgstr "Références (liste d'éléments séparés par des espaces) :"
+
+#: gitk:3680
+msgid "Branches & tags:"
+msgstr "Branches & tags :"
+
+#: gitk:3681
+msgid "All refs"
+msgstr "Toutes les références"
+
+#: gitk:3682
+msgid "All (local) branches"
+msgstr "Toutes les branches (locales)"
+
+#: gitk:3683
+msgid "All tags"
+msgstr "Tous les tags"
+
+#: gitk:3684
+msgid "All remote-tracking branches"
+msgstr "Toutes les branches de suivi à distance"
+
+#: gitk:3685
+msgid "Commit Info (regular expressions):"
+msgstr "Info sur les commits (expressions régulières) :"
+
+#: gitk:3686
+msgid "Author:"
+msgstr "Auteur :"
+
+#: gitk:3687
+msgid "Committer:"
+msgstr "Commiteur :"
+
+#: gitk:3688
+msgid "Commit Message:"
+msgstr "Message de commit :"
+
+#: gitk:3689
+msgid "Matches all Commit Info criteria"
+msgstr "Correspond à tous les critères d'Info sur les commits"
+
+#: gitk:3690
+msgid "Changes to Files:"
+msgstr "Changements des fichiers :"
+
+#: gitk:3691
+msgid "Fixed String"
+msgstr "Chaîne Figée"
+
+#: gitk:3692
+msgid "Regular Expression"
+msgstr "Expression Régulière"
+
+#: gitk:3693
+msgid "Search string:"
+msgstr "Recherche de la chaîne :"
+
+#: gitk:3694
+msgid ""
+"Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 "
+"15:27:38\"):"
+msgstr ""
+"Dates des commits (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, "
+"2009 15:27:38\") :"
+
+#: gitk:3695
+msgid "Since:"
+msgstr "De :"
+
+#: gitk:3696
+msgid "Until:"
+msgstr "Jusqu'au :"
+
+#: gitk:3697
+msgid "Limit and/or skip a number of revisions (positive integer):"
+msgstr "Limiter et/ou sauter un certain nombre (entier positif) de révisions :"
+
+#: gitk:3698
+msgid "Number to show:"
+msgstr "Nombre à afficher :"
+
+#: gitk:3699
+msgid "Number to skip:"
+msgstr "Nombre à sauter :"
+
+#: gitk:3700
+msgid "Miscellaneous options:"
+msgstr "Options diverses :"
+
+#: gitk:3701
+msgid "Strictly sort by date"
+msgstr "Trier par date"
+
+# FIXME : traduction de "branch sides"
+#: gitk:3702
+#, fuzzy
+msgid "Mark branch sides"
+msgstr "Marquer les extrémités des branches"
+
+#: gitk:3703
+msgid "Limit to first parent"
+msgstr "Limiter au premier ancêtre"
+
+#: gitk:3704
+msgid "Simple history"
+msgstr "Historique simple"
+
+#: gitk:3705
+msgid "Additional arguments to git log:"
+msgstr "Arguments supplémentaires de git log :"
+
+#: gitk:3706
+msgid "Enter files and directories to include, one per line:"
+msgstr "Saisir les fichiers et répertoires à inclure, un par ligne :"
+
+#: gitk:3707
+msgid "Command to generate more commits to include:"
+msgstr "Commande pour générer plus de commits à inclure :"
+
+#: gitk:3829
+msgid "Gitk: edit view"
+msgstr "Gitk : éditer la vue"
+
+#: gitk:3837
+msgid "-- criteria for selecting revisions"
+msgstr "-- critère pour la sélection des révisions"
+
+#: gitk:3842
+msgid "View Name:"
+msgstr "Nom de la vue :"
+
+#: gitk:3917
+msgid "Apply (F5)"
+msgstr "Appliquer (F5)"
+
+#: gitk:3955
+msgid "Error in commit selection arguments:"
+msgstr "Erreur dans les arguments de sélection des commits :"
+
+#: gitk:4008 gitk:4060 gitk:4508 gitk:4522 gitk:5783 gitk:11196 gitk:11197
+msgid "None"
+msgstr "Aucun"
+
+#: gitk:4456 gitk:6303 gitk:8065 gitk:8080
+msgid "Date"
+msgstr "Date"
+
+#: gitk:4456 gitk:6303
+msgid "CDate"
+msgstr "CDate"
+
+#: gitk:4605 gitk:4610
+msgid "Descendant"
+msgstr "Descendant"
+
+#: gitk:4606
+msgid "Not descendant"
+msgstr "Pas un descendant"
+
+#: gitk:4613 gitk:4618
+msgid "Ancestor"
+msgstr "Ancêtre"
+
+#: gitk:4614
+msgid "Not ancestor"
+msgstr "Pas un ancêtre"
+
+#: gitk:4904
+msgid "Local changes checked in to index but not committed"
+msgstr "Modifications locales enregistrées dans l'index mais non commitées"
+
+#: gitk:4940
+msgid "Local uncommitted changes, not checked in to index"
+msgstr "Modifications locales non enregistrées dans l'index et non commitées"
+
+#: gitk:6621
+msgid "many"
+msgstr "nombreux"
+
+#: gitk:6805
+msgid "Tags:"
+msgstr "Tags :"
+
+#: gitk:6822 gitk:6828 gitk:8058
+msgid "Parent"
+msgstr "Parent"
+
+#: gitk:6833
+msgid "Child"
+msgstr "Enfant"
+
+#: gitk:6842
+msgid "Branch"
+msgstr "Branche"
+
+#: gitk:6845
+msgid "Follows"
+msgstr "Suit"
+
+#: gitk:6848
+msgid "Precedes"
+msgstr "Précède"
+
+#: gitk:7346
+#, tcl-format
+msgid "Error getting diffs: %s"
+msgstr "Erreur lors de la récupération des diff : %s"
+
+#: gitk:7886
+msgid "Goto:"
+msgstr "Aller à :"
+
+#: gitk:7888
+msgid "SHA1 ID:"
+msgstr "Id SHA1 :"
+
+#: gitk:7907
+#, tcl-format
+msgid "Short SHA1 id %s is ambiguous"
+msgstr "Id SHA1 court %s est ambigu"
+
+#: gitk:7914
+#, tcl-format
+msgid "Revision %s is not known"
+msgstr "Id SHA1 %s est inconnu"
+
+#: gitk:7924
+#, tcl-format
+msgid "SHA1 id %s is not known"
+msgstr "Id SHA1 %s est inconnu"
+
+#: gitk:7926
+#, tcl-format
+msgid "Revision %s is not in the current view"
+msgstr "La révision %s n'est pas dans la vue courante"
+
+#: gitk:8068
+msgid "Children"
+msgstr "Enfants"
+
+#: gitk:8125
+#, tcl-format
+msgid "Reset %s branch to here"
+msgstr "Réinitialiser la branche %s vers cet état"
+
+#: gitk:8127
+msgid "Detached head: can't reset"
+msgstr "Head détaché : impossible de réinitialiser"
+
+#: gitk:8236 gitk:8242
+msgid "Skipping merge commit "
+msgstr "Éviter le commit de la fusion "
+
+#: gitk:8251 gitk:8256
+msgid "Error getting patch ID for "
+msgstr "Erreur à l'obtention de l'ID du patch pour "
+
+#: gitk:8252 gitk:8257
+msgid " - stopping\n"
+msgstr " - arrêt en cours\n"
+
+#: gitk:8262 gitk:8265 gitk:8273 gitk:8283 gitk:8292
+msgid "Commit "
+msgstr "Commit "
+
+#: gitk:8266
+msgid ""
+" is the same patch as\n"
+"       "
+msgstr ""
+"est le même patch que \n"
+"       "
+
+#: gitk:8274
+msgid ""
+" differs from\n"
+"       "
+msgstr ""
+" diffère de\n"
+"       "
+
+#: gitk:8276
+msgid "- stopping\n"
+msgstr "- arrêt en cours\n"
+
+#: gitk:8284 gitk:8293
+#, tcl-format
+msgid " has %s children - stopping\n"
+msgstr "a %s enfants - arrêt en cours\n"
+
+#: gitk:8324
+msgid "Top"
+msgstr "Haut"
+
+#: gitk:8325
+msgid "From"
+msgstr "De"
+
+#: gitk:8330
+msgid "To"
+msgstr "À"
+
+#: gitk:8354
+msgid "Generate patch"
+msgstr "Générer le patch"
+
+#: gitk:8356
+msgid "From:"
+msgstr "De :"
+
+#: gitk:8365
+msgid "To:"
+msgstr "À :"
+
+#: gitk:8374
+msgid "Reverse"
+msgstr "Inverser"
+
+#: gitk:8376 gitk:8561
+msgid "Output file:"
+msgstr "Fichier de sortie :"
+
+#: gitk:8382
+msgid "Generate"
+msgstr "Générer"
+
+#: gitk:8420
+msgid "Error creating patch:"
+msgstr "Erreur à la création du patch :"
+
+#: gitk:8443 gitk:8549 gitk:8606
+msgid "ID:"
+msgstr "ID :"
+
+#: gitk:8452
+msgid "Tag name:"
+msgstr "Nom du Tag :"
+
+#: gitk:8456 gitk:8615
+msgid "Create"
+msgstr "Créer"
+
+#: gitk:8473
+msgid "No tag name specified"
+msgstr "Aucun nom de tag spécifié"
+
+#: gitk:8477
+#, tcl-format
+msgid "Tag \"%s\" already exists"
+msgstr "Le tag \"%s\" existe déjà"
+
+#: gitk:8483
+msgid "Error creating tag:"
+msgstr "Erreur à la création du tag :"
+
+#: gitk:8558
+msgid "Command:"
+msgstr "Commande :"
+
+#: gitk:8566
+msgid "Write"
+msgstr "Écrire"
+
+#: gitk:8584
+msgid "Error writing commit:"
+msgstr "Erreur à l'ecriture du commit :"
+
+#: gitk:8611
+msgid "Name:"
+msgstr "Nom :"
+
+#: gitk:8634
+msgid "Please specify a name for the new branch"
+msgstr "Veuillez spécifier un nom pour la nouvelle branche"
+
+#: gitk:8639
+#, tcl-format
+msgid "Branch '%s' already exists. Overwrite?"
+msgstr "La branche '%s' existe déjà. Écraser?"
+
+#: gitk:8705
+#, tcl-format
+msgid "Commit %s is already included in branch %s -- really re-apply it?"
+msgstr ""
+"Le Commit %s est déjà inclus dans la branche %s -- le ré-appliquer malgré "
+"tout?"
+
+#: gitk:8710
+msgid "Cherry-picking"
+msgstr "Cueillir (Cherry-picking)"
+
+#: gitk:8719
+#, tcl-format
+msgid ""
+"Cherry-pick failed because of local changes to file '%s'.\n"
+"Please commit, reset or stash your changes and try again."
+msgstr ""
+"La cueillette (cherry-pick) a échouée à cause de modifications locales du "
+"fichier '%s'.\n"
+"Veuillez commiter, réinitialiser ou stasher vos changements et essayer de "
+"nouveau."
+
+#: gitk:8725
+msgid ""
+"Cherry-pick failed because of merge conflict.\n"
+"Do you wish to run git citool to resolve it?"
+msgstr ""
+"La cueillette (cherry-pick) a échouée à cause d'un conflit lors d'une "
+"fusion.\n"
+"Souhaitez-vous exécuter git citool pour le résoudre ?"
+
+#: gitk:8741
+msgid "No changes committed"
+msgstr "Aucun changement commité"
+
+#: gitk:8767
+msgid "Confirm reset"
+msgstr "Confirmer la réinitialisation"
+
+#: gitk:8769
+#, tcl-format
+msgid "Reset branch %s to %s?"
+msgstr "Réinitialiser la branche %s à %s?"
+
+#: gitk:8773
+msgid "Reset type:"
+msgstr "Type de réinitialisation :"
+
+#: gitk:8777
+msgid "Soft: Leave working tree and index untouched"
+msgstr "Douce : Laisse le répertoire de travail et l'index intacts"
+
+#: gitk:8780
+msgid "Mixed: Leave working tree untouched, reset index"
+msgstr ""
+"Hybride : Laisse le répertoire de travail dans son état courant, "
+"réinitialise l'index"
+
+#: gitk:8783
+msgid ""
+"Hard: Reset working tree and index\n"
+"(discard ALL local changes)"
+msgstr ""
+"Dure : Réinitialise le répertoire de travail et l'index\n"
+"(abandonne TOUS les changements locaux)"
+
+#: gitk:8800
+msgid "Resetting"
+msgstr "Réinitialisation"
+
+# Fixme: Récupération est-il vraiment une mauvaise traduction?
+#: gitk:8857
+#, fuzzy
+msgid "Checking out"
+msgstr "Récupération"
+
+#: gitk:8910
+msgid "Cannot delete the currently checked-out branch"
+msgstr "Impossible de supprimer la branche en cours"
+
+#: gitk:8916
+#, tcl-format
+msgid ""
+"The commits on branch %s aren't on any other branch.\n"
+"Really delete branch %s?"
+msgstr ""
+"Les commits de la branche %s ne sont dans aucune autre branche.\n"
+"Voulez-vous vraiment supprimer cette branche %s ?"
+
+#: gitk:8947
+#, tcl-format
+msgid "Tags and heads: %s"
+msgstr "Tags et heads : %s"
+
+#: gitk:8962
+msgid "Filter"
+msgstr "Filtrer"
+
+#: gitk:9257
+msgid ""
+"Error reading commit topology information; branch and preceding/following "
+"tag information will be incomplete."
+msgstr ""
+"Erreur à la lecture des informations sur la topologie des commits, les "
+"informations sur les branches et les tags précédents/suivants seront "
+"incomplètes."
+
+#: gitk:10243
+msgid "Tag"
+msgstr "Tag"
+
+#: gitk:10243
+msgid "Id"
+msgstr "Id"
+
+#: gitk:10291
+msgid "Gitk font chooser"
+msgstr "Sélecteur de police de Gitk"
+
+#: gitk:10308
+msgid "B"
+msgstr "B"
+
+#: gitk:10311
+msgid "I"
+msgstr "I"
+
+#: gitk:10407
+msgid "Gitk preferences"
+msgstr "Préférences de Gitk"
+
+#: gitk:10409
+msgid "Commit list display options"
+msgstr "Options d'affichage de la liste des commits"
+
+#: gitk:10412
+msgid "Maximum graph width (lines)"
+msgstr "Longueur maximum du graphe (lignes)"
+
+# FIXME : Traduction standard de "pane"?
+#: gitk:10416
+#, fuzzy, tcl-format
+msgid "Maximum graph width (% of pane)"
+msgstr "Longueur maximum du graphe (% du panneau)"
+
+#: gitk:10420
+msgid "Show local changes"
+msgstr "Montrer les changements locaux"
+
+#: gitk:10423
+msgid "Auto-select SHA1"
+msgstr "Sélection auto. du SHA1"
+
+#: gitk:10427
+msgid "Diff display options"
+msgstr "Options d'affichage des diff"
+
+#: gitk:10429
+msgid "Tab spacing"
+msgstr "Taille des tabulations"
+
+#: gitk:10432
+msgid "Display nearby tags"
+msgstr "Afficher les tags les plus proches"
+
+#: gitk:10435
+msgid "Hide remote refs"
+msgstr "Cacher les refs distantes"
+
+#: gitk:10438
+msgid "Limit diffs to listed paths"
+msgstr "Limiter les différences aux chemins listés"
+
+#: gitk:10441
+msgid "Support per-file encodings"
+msgstr "Support pour un encodage des caractères par fichier"
+
+#: gitk:10447 gitk:10512
+msgid "External diff tool"
+msgstr "Outil diff externe"
+
+#: gitk:10449
+msgid "Choose..."
+msgstr "Choisir..."
+
+#: gitk:10454
+msgid "Colors: press to choose"
+msgstr "Couleurs : cliquer pour choisir"
+
+#: gitk:10457
+msgid "Background"
+msgstr "Arrière-plan"
+
+#: gitk:10458 gitk:10488
+msgid "background"
+msgstr "arrière-plan"
+
+#: gitk:10461
+msgid "Foreground"
+msgstr "Premier plan"
+
+#: gitk:10462
+msgid "foreground"
+msgstr "premier plan"
+
+#: gitk:10465
+msgid "Diff: old lines"
+msgstr "Diff : anciennes lignes"
+
+#: gitk:10466
+msgid "diff old lines"
+msgstr "diff anciennes lignes"
+
+#: gitk:10470
+msgid "Diff: new lines"
+msgstr "Diff : nouvelles lignes"
+
+#: gitk:10471
+msgid "diff new lines"
+msgstr "diff nouvelles lignes"
+
+#: gitk:10475
+msgid "Diff: hunk header"
+msgstr "Diff : entête du hunk"
+
+#: gitk:10477
+msgid "diff hunk header"
+msgstr "diff : entête du hunk"
+
+#: gitk:10481
+msgid "Marked line bg"
+msgstr "Arrière-plan de la ligne marquée"
+
+#: gitk:10483
+msgid "marked line background"
+msgstr "Arrière-plan de la ligne marquée"
+
+#: gitk:10487
+msgid "Select bg"
+msgstr "Sélectionner l'arrière-plan"
+
+#: gitk:10491
+msgid "Fonts: press to choose"
+msgstr "Polices : cliquer pour choisir"
+
+#: gitk:10493
+msgid "Main font"
+msgstr "Police principale"
+
+#: gitk:10494
+msgid "Diff display font"
+msgstr "Police d'affichage des diff"
+
+#: gitk:10495
+msgid "User interface font"
+msgstr "Police de l'interface utilisateur"
+
+#: gitk:10522
+#, tcl-format
+msgid "Gitk: choose color for %s"
+msgstr "Gitk : choisir la couleur de %s"
+
+#: gitk:10973
+msgid ""
+"Sorry, gitk cannot run with this version of Tcl/Tk.\n"
+" Gitk requires at least Tcl/Tk 8.4."
+msgstr ""
+"Désolé, gitk ne peut être exécuté avec cette version de Tcl/Tk.\n"
+" Gitk requiert Tcl/Tk version 8.4 ou supérieur."
+
+#: gitk:11101
+msgid "Cannot find a git repository here."
+msgstr "Impossible de trouver un dépôt git ici."
+
+#: gitk:11105
+#, tcl-format
+msgid "Cannot find the git directory \"%s\"."
+msgstr "Impossible de trouver le répertoire git \"%s\"."
+
+#: gitk:11152
+#, tcl-format
+msgid "Ambiguous argument '%s': both revision and filename"
+msgstr "Argument '%s' ambigu : à la fois une révision et un nom de fichier"
+
+#: gitk:11164
+msgid "Bad arguments to gitk:"
+msgstr "Arguments invalides pour gitk :"
+
+#: gitk:11249
+msgid "Command line"
+msgstr "Ligne de commande"
diff --git a/gitk-git/po/hu.po b/gitk-git/po/hu.po
new file mode 100644
index 0000000..7262b61
--- /dev/null
+++ b/gitk-git/po/hu.po
@@ -0,0 +1,1295 @@
+# Translation of gitk to Hungarian.
+# Copyright (C) 2007 Paul Mackerras.
+# This file is distributed under the same license as the gitk package.
+#
+# Laszlo Papp <laszlo.papp@arhungary.hu>, 2009.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: git-gui\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2009-12-14 13:33+0100\n"
+"PO-Revision-Date: 2009-12-14 14:04+0100\n"
+"Last-Translator: Laszlo Papp <djszapi@archlinux.us>\n"
+"Language-Team: Hungarian\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: gitk:115
+msgid "Couldn't get list of unmerged files:"
+msgstr "Nem sikerült letölteni az unmerged fájl listát:"
+
+#: gitk:271
+msgid "Error parsing revisions:"
+msgstr "Hiba történt értelmezés közben:"
+
+#: gitk:326
+msgid "Error executing --argscmd command:"
+msgstr "Hiba történt a végrehajtáskor --argscmd parancs:"
+
+#: gitk:339
+msgid "No files selected: --merge specified but no files are unmerged."
+msgstr ""
+"Nincsen fájl kiválasztva: --merge megadve, de egyetlen fájl sem unmerged."
+
+#: gitk:342
+msgid ""
+"No files selected: --merge specified but no unmerged files are within file "
+"limit."
+msgstr ""
+"Nincsen fájl kiválasztva: --merge megadva, de nincsenek unmerged fájlok a "
+"fájlon belül limit."
+
+#: gitk:364 gitk:511
+msgid "Error executing git log:"
+msgstr "Hiba történt a git log végrehajtása közben:"
+
+#: gitk:382 gitk:527
+msgid "Reading"
+msgstr "Olvasás"
+
+#: gitk:442 gitk:4258
+msgid "Reading commits..."
+msgstr "Commitok olvasása ..."
+
+#: gitk:445 gitk:1575 gitk:4261
+msgid "No commits selected"
+msgstr "Nincsen commit kiválasztva"
+
+#: gitk:1451
+msgid "Can't parse git log output:"
+msgstr "Nem lehet értelmezni a git log kimenetét:"
+
+#: gitk:1671
+msgid "No commit information available"
+msgstr "Nincsen elérhető commit információ"
+
+#: gitk:1813
+msgid "mc"
+msgstr "mc"
+
+#: gitk:1848 gitk:4051 gitk:9029 gitk:10570 gitk:10789
+msgid "OK"
+msgstr "OK"
+
+#: gitk:1850 gitk:4053 gitk:8629 gitk:8703 gitk:8813 gitk:8862 gitk:9031
+#: gitk:10571 gitk:10790
+msgid "Cancel"
+msgstr "Visszavonás"
+
+#: gitk:1972
+msgid "Update"
+msgstr "Frissités"
+
+#: gitk:1973
+msgid "Reload"
+msgstr "Újratöltés"
+
+#: gitk:1974
+msgid "Reread references"
+msgstr "Referenciák újraolvasása"
+
+#: gitk:1975
+msgid "List references"
+msgstr "Referenciák listázása"
+
+#: gitk:1977
+msgid "Start git gui"
+msgstr "Git gui indítása"
+
+#: gitk:1979
+msgid "Quit"
+msgstr "Kilépés"
+
+#: gitk:1971
+msgid "File"
+msgstr "Fájl"
+
+#: gitk:1983
+msgid "Preferences"
+msgstr "Beállítások"
+
+#: gitk:1982
+msgid "Edit"
+msgstr "Szerkesztés"
+
+#: gitk:1987
+msgid "New view..."
+msgstr "Új nézet ..."
+
+#: gitk:1988
+msgid "Edit view..."
+msgstr "Nézet szerkesztése ..."
+
+#: gitk:1989
+msgid "Delete view"
+msgstr "Nézet törlése"
+
+#: gitk:1991
+msgid "All files"
+msgstr "Minden fájl"
+
+#: gitk:1986 gitk:3805
+msgid "View"
+msgstr "Nézet"
+
+#: gitk:1996 gitk:2006 gitk:2777
+msgid "About gitk"
+msgstr "Gitk névjegy"
+
+#: gitk:1997 gitk:2011
+msgid "Key bindings"
+msgstr "Billentyűkombináció"
+
+#: gitk:1995 gitk:2010
+msgid "Help"
+msgstr "Segítség"
+
+#: gitk:2088
+msgid "SHA1 ID: "
+msgstr "SHA1 ID: "
+
+#: gitk:2119
+msgid "Row"
+msgstr "Sor"
+
+#: gitk:2157
+msgid "Find"
+msgstr "Keresés"
+
+#: gitk:2158
+msgid "next"
+msgstr "következő"
+
+#: gitk:2159
+msgid "prev"
+msgstr "előző"
+
+#: gitk:2160
+msgid "commit"
+msgstr "commit"
+
+#: gitk:2163 gitk:2165 gitk:4419 gitk:4442 gitk:4466 gitk:6407 gitk:6479
+#: gitk:6563
+msgid "containing:"
+msgstr "tartalmazás:"
+
+#: gitk:2166 gitk:3287 gitk:3292 gitk:4494
+msgid "touching paths:"
+msgstr "érintendő útvonalak:"
+
+#: gitk:2167 gitk:4499
+msgid "adding/removing string:"
+msgstr "string hozzáadása/törlése:"
+
+#: gitk:2176 gitk:2178
+msgid "Exact"
+msgstr "Pontos"
+
+#: gitk:2178 gitk:4574 gitk:6375
+msgid "IgnCase"
+msgstr "Kis/nagy betű nem számít"
+
+#: gitk:2178 gitk:4468 gitk:4572 gitk:6371
+msgid "Regexp"
+msgstr "Regexp"
+
+#: gitk:2180 gitk:2181 gitk:4593 gitk:4623 gitk:4630 gitk:6499 gitk:6567
+msgid "All fields"
+msgstr "Minden mező"
+
+#: gitk:2181 gitk:4591 gitk:4623 gitk:6438
+msgid "Headline"
+msgstr "Főcím"
+
+#: gitk:2182 gitk:4591 gitk:6438 gitk:6567 gitk:7000
+msgid "Comments"
+msgstr "Megjegyzések"
+
+#: gitk:2182 gitk:4591 gitk:4595 gitk:4630 gitk:6438 gitk:6935 gitk:8280
+#: gitk:8295
+msgid "Author"
+msgstr "Szerző"
+
+#: gitk:2182 gitk:4591 gitk:6438 gitk:6937
+msgid "Committer"
+msgstr "Commitoló"
+
+#: gitk:2213
+msgid "Search"
+msgstr "Keresés"
+
+#: gitk:2221
+msgid "Diff"
+msgstr "Diff"
+
+#: gitk:2223
+msgid "Old version"
+msgstr "Régi verzió"
+
+#: gitk:2225
+msgid "New version"
+msgstr "Új verzió"
+
+#: gitk:2227
+msgid "Lines of context"
+msgstr "Tartalmi sorok"
+
+#: gitk:2237
+msgid "Ignore space change"
+msgstr "Space váltás mellőzése"
+
+#: gitk:2296
+msgid "Patch"
+msgstr "Patch"
+
+#: gitk:2298
+msgid "Tree"
+msgstr "Tree"
+
+#: gitk:2453 gitk:2470
+msgid "Diff this -> selected"
+msgstr "Diff ezeket -> kiválasztott"
+
+#: gitk:2454 gitk:2471
+msgid "Diff selected -> this"
+msgstr "Diff kiválasztottakat -> ezt"
+
+#: gitk:2455 gitk:2472
+msgid "Make patch"
+msgstr "Patch készítése"
+
+#: gitk:2456 gitk:8687
+msgid "Create tag"
+msgstr "Tag készítése"
+
+#: gitk:2457 gitk:8793
+msgid "Write commit to file"
+msgstr "Commit fáljba írása"
+
+#: gitk:2458 gitk:8850
+msgid "Create new branch"
+msgstr "Új branch készítése"
+
+#: gitk:2459
+msgid "Cherry-pick this commit"
+msgstr "Cherry-pick erre a commitra"
+
+#: gitk:2460
+msgid "Reset HEAD branch to here"
+msgstr "HEAD branch újraindítása ide"
+
+#: gitk:2461
+msgid "Mark this commit"
+msgstr "Ezen commit megjelölése"
+
+#: gitk:2462
+msgid "Return to mark"
+msgstr "Visszatérés a megjelöléshez"
+
+#: gitk:2463
+msgid "Find descendant of this and mark"
+msgstr "Találd meg ezen utódokat és jelöld meg"
+
+#: gitk:2464
+msgid "Compare with marked commit"
+msgstr "Összehasonlítás a megjelölt commit-tal"
+
+#: gitk:2478
+msgid "Check out this branch"
+msgstr "Check out ezt a branchot"
+
+#: gitk:2479
+msgid "Remove this branch"
+msgstr "Töröld ezt a branch-ot"
+
+#: gitk:2486
+msgid "Highlight this too"
+msgstr "Emeld ki ezt is"
+
+#: gitk:2487
+msgid "Highlight this only"
+msgstr "Csak ezt emeld ki"
+
+#: gitk:2488
+msgid "External diff"
+msgstr "Külső diff"
+
+#: gitk:2489
+msgid "Blame parent commit"
+msgstr "Blame szülő kommitra"
+
+#: gitk:2496
+msgid "Show origin of this line"
+msgstr "Mutasd meg ennek a sornak az eredetét"
+
+#: gitk:2497
+msgid "Run git gui blame on this line"
+msgstr "Futtasd a git gui blame-t ezen a soron"
+
+#: gitk:2779
+msgid ""
+"\n"
+"Gitk - a commit viewer for git\n"
+"\n"
+"Copyright \\u00a9 2005-2010 Paul Mackerras\n"
+"\n"
+"Use and redistribute under the terms of the GNU General Public License"
+msgstr ""
+"\n"
+"Gitk - commit nézegető a githez\n"
+"\n"
+"Szerzői jog \\u00a9 2005-2010 Paul Mackerras\n"
+"\n"
+"Használd és terjeszd a GNU General Public License feltételei mellett"
+
+#: gitk:2787 gitk:2851 gitk:9215
+msgid "Close"
+msgstr "Bezárás"
+
+#: gitk:2808
+msgid "Gitk key bindings"
+msgstr "Gitk-billentyű hozzárendelés"
+
+#: gitk:2811
+msgid "Gitk key bindings:"
+msgstr "Gitk-billentyű hozzaárendelés:"
+
+#: gitk:2813
+#, tcl-format
+msgid "<%s-Q>\t\tQuit"
+msgstr "<%s-Q>\t\tKilépés"
+
+#: gitk:2814
+msgid "<Home>\t\tMove to first commit"
+msgstr "<Pos1>\t\tElső commithoz"
+
+#: gitk:2815
+msgid "<End>\t\tMove to last commit"
+msgstr "<Ende>\t\tUtolsó commithoz"
+
+#: gitk:2816
+msgid "<Up>, p, i\tMove up one commit"
+msgstr "<Hoch>, p, i\tEgy committal feljebb"
+
+#: gitk:2817
+msgid "<Down>, n, k\tMove down one commit"
+msgstr "<Runter>, n, k\tEgy committal lejjebb"
+
+#: gitk:2818
+msgid "<Left>, z, j\tGo back in history list"
+msgstr "<Links>, z, j\tVissza a history listába"
+
+#: gitk:2819
+msgid "<Right>, x, l\tGo forward in history list"
+msgstr "<Rechts>, x, l\tElőre a history listába"
+
+#: gitk:2820
+msgid "<PageUp>\tMove up one page in commit list"
+msgstr "<BildHoch>\tEgy lappal feljebb a commit listába"
+
+#: gitk:2821
+msgid "<PageDown>\tMove down one page in commit list"
+msgstr "<BildRunter>\tEgy lappal lejjebb a commit listába"
+
+#: gitk:2822
+#, tcl-format
+msgid "<%s-Home>\tScroll to top of commit list"
+msgstr "<%s-Pos1>\tGörgetés a commit lista tetejéhez"
+
+#: gitk:2823
+#, tcl-format
+msgid "<%s-End>\tScroll to bottom of commit list"
+msgstr "<%s-Ende>\tGörgetés a commit lista aljához"
+
+#: gitk:2824
+#, tcl-format
+msgid "<%s-Up>\tScroll commit list up one line"
+msgstr "<%s-Hoch>\tEgy sorral feljebb görgetés a commit listában"
+
+#: gitk:2825
+#, tcl-format
+msgid "<%s-Down>\tScroll commit list down one line"
+msgstr "<%s-Runter>\tEgy sorral lejjebb görgetés a commit listában"
+
+#: gitk:2826
+#, tcl-format
+msgid "<%s-PageUp>\tScroll commit list up one page"
+msgstr "<%s-BildHoch>\tEgy lappal feljebb görgetés a commit listában"
+
+#: gitk:2827
+#, tcl-format
+msgid "<%s-PageDown>\tScroll commit list down one page"
+msgstr "<%s-BildRunter>\tEgy sorral lejjebb görgetés a commit listában"
+
+#: gitk:2828
+msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
+msgstr "<Umschalt-Hoch>\tKeresés visszafele (felfele, utolsó commitok)"
+
+#: gitk:2829
+msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
+msgstr "<Umschalt-Runter>\tKeresés előre (lefelé; korábbi commitok)"
+
+#: gitk:2830
+msgid "<Delete>, b\tScroll diff view up one page"
+msgstr "<Entf>, b\t\tEgy lappal feljebb görgetés a diff nézetben"
+
+#: gitk:2831
+msgid "<Backspace>\tScroll diff view up one page"
+msgstr "<Löschtaste>\tEgy lappal feljebb görgetés a diff nézetben"
+
+#: gitk:2832
+msgid "<Space>\t\tScroll diff view down one page"
+msgstr "<Leertaste>\tEgy lappal lejjebb görgetés a diff nézetben"
+
+#: gitk:2833
+msgid "u\t\tScroll diff view up 18 lines"
+msgstr "u\t\t18 sorral felfelé görgetés diff nézetben"
+
+#: gitk:2834
+msgid "d\t\tScroll diff view down 18 lines"
+msgstr "d\t\t18 sorral lejjebb görgetés a diff nézetben"
+
+#: gitk:2835
+#, tcl-format
+msgid "<%s-F>\t\tFind"
+msgstr "<%s-F>\t\tKeresés"
+
+#: gitk:2836
+#, tcl-format
+msgid "<%s-G>\t\tMove to next find hit"
+msgstr "<%s-G>\t\tKövetkező találathoz"
+
+#: gitk:2837
+msgid "<Return>\tMove to next find hit"
+msgstr "<Eingabetaste>\tKövetkező találathoz"
+
+#: gitk:2838
+msgid "/\t\tFocus the search box"
+msgstr "/\t\tLépj a keresési mezőre"
+
+#: gitk:2839
+msgid "?\t\tMove to previous find hit"
+msgstr "?\t\tElőző találathoz"
+
+#: gitk:2840
+msgid "f\t\tScroll diff view to next file"
+msgstr "f\t\tKövetkező fájlra görgetés diff nézetben"
+
+#: gitk:2841
+#, tcl-format
+msgid "<%s-S>\t\tSearch for next hit in diff view"
+msgstr "<%s-S>\t\tKövetkező találatra keresés diff nézetben"
+
+#: gitk:2842
+#, tcl-format
+msgid "<%s-R>\t\tSearch for previous hit in diff view"
+msgstr "<%s-R>\t\tElőző találatra keresés diff nézetben"
+
+#: gitk:2843
+#, tcl-format
+msgid "<%s-KP+>\tIncrease font size"
+msgstr "<%s-Nummerblock-Plus>\tBetűméret növelése"
+
+#: gitk:2844
+#, tcl-format
+msgid "<%s-plus>\tIncrease font size"
+msgstr "<%s-Plus>\tBetűméret növelése"
+
+#: gitk:2845
+#, tcl-format
+msgid "<%s-KP->\tDecrease font size"
+msgstr "<%s-Nummernblock-Minus> Betűméret csökkentése"
+
+#: gitk:2846
+#, tcl-format
+msgid "<%s-minus>\tDecrease font size"
+msgstr "<%s-Minus>\tBetűméret csökkentése"
+
+#: gitk:2847
+msgid "<F5>\t\tUpdate"
+msgstr "<F5>\t\tFrissítés"
+
+#: gitk:3302 gitk:3311
+#, tcl-format
+msgid "Error creating temporary directory %s:"
+msgstr "Hiba történt az ideiglenes könyvtár létrehozása közben %s:"
+
+#: gitk:3324
+#, tcl-format
+msgid "Error getting \"%s\" from %s:"
+msgstr "Hiba történt \"%s\" letöltése közben %s-ről:"
+
+#: gitk:3387
+msgid "command failed:"
+msgstr "parancs hiba:"
+
+#: gitk:3536
+msgid "No such commit"
+msgstr "Nincs ilyen commit"
+
+#: gitk:3550
+msgid "git gui blame: command failed:"
+msgstr "git gui blame: parancs hiba:"
+
+#: gitk:3581
+#, tcl-format
+msgid "Couldn't read merge head: %s"
+msgstr "Nem sikerült a Merge head olvasása: %s"
+
+#: gitk:3589
+#, tcl-format
+msgid "Error reading index: %s"
+msgstr "Hiba történt az index olvasása közben: %s"
+
+#: gitk:3614
+#, tcl-format
+msgid "Couldn't start git blame: %s"
+msgstr "Nem sikerült a git blame indítása: %s"
+
+#: gitk:3617 gitk:6406
+msgid "Searching"
+msgstr "Keresés"
+
+#: gitk:3649
+#, tcl-format
+msgid "Error running git blame: %s"
+msgstr "Hiba történt a git blame futtatása közben: %s"
+
+#: gitk:3677
+#, tcl-format
+msgid "That line comes from commit %s,  which is not in this view"
+msgstr ""
+"A %s commitból származik az a sor, amelyik nem található ebben a nézetben"
+
+#: gitk:3691
+msgid "External diff viewer failed:"
+msgstr "Külső diff nézegető hiba:"
+
+#: gitk:3809
+msgid "Gitk view definition"
+msgstr "Gitk nézet meghatározása"
+
+#: gitk:3813
+msgid "Remember this view"
+msgstr "Maradj ennél a nézetnél"
+
+#: gitk:3814
+msgid "References (space separated list):"
+msgstr "Referenciák (szóközzel tagolt lista"
+
+#: gitk:3815
+msgid "Branches & tags:"
+msgstr "Branch-ek & tagek:"
+
+#: gitk:3816
+msgid "All refs"
+msgstr "Minden ref"
+
+#: gitk:3817
+msgid "All (local) branches"
+msgstr "Minden (helyi) branch"
+
+#: gitk:3818
+msgid "All tags"
+msgstr "Minden tag"
+
+#: gitk:3819
+msgid "All remote-tracking branches"
+msgstr "Minden távoli követő branch"
+
+#: gitk:3820
+msgid "Commit Info (regular expressions):"
+msgstr "Commit Infó (reguláris kifejezés):"
+
+#: gitk:3821
+msgid "Author:"
+msgstr "Szerző:"
+
+#: gitk:3822
+msgid "Committer:"
+msgstr "Commitoló:"
+
+#: gitk:3823
+msgid "Commit Message:"
+msgstr "Commit üzenet:"
+
+#: gitk:3824
+msgid "Matches all Commit Info criteria"
+msgstr "Egyezik minen Commit Infó feltétellel"
+
+#: gitk:3825
+msgid "Changes to Files:"
+msgstr "Fájl változások:"
+
+#: gitk:3826
+msgid "Fixed String"
+msgstr "Fix String"
+
+#: gitk:3827
+msgid "Regular Expression"
+msgstr "Reguláris kifejezés"
+
+#: gitk:3828
+msgid "Search string:"
+msgstr "Keresés szöveg:"
+
+#: gitk:3829
+msgid ""
+"Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 "
+"15:27:38\"):"
+msgstr ""
+"Commit Dátumok (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 "
+"15:27:38\"):"
+
+#: gitk:3830
+msgid "Since:"
+msgstr "Ettől:"
+
+#: gitk:3831
+msgid "Until:"
+msgstr "Eddig:"
+
+#: gitk:3832
+msgid "Limit and/or skip a number of revisions (positive integer):"
+msgstr "Limitálva és/vagy kihagyva egy adott számú revíziót (pozitív egész):"
+
+#: gitk:3833
+msgid "Number to show:"
+msgstr "Mutatandó szám:"
+
+#: gitk:3834
+msgid "Number to skip:"
+msgstr "Kihagyandó szám:"
+
+#: gitk:3835
+msgid "Miscellaneous options:"
+msgstr "Különféle opciók:"
+
+#: gitk:3836
+msgid "Strictly sort by date"
+msgstr "Szigorú rendezás dátum alapján"
+
+#: gitk:3837
+msgid "Mark branch sides"
+msgstr "Jelölje meg az ágakat"
+
+#: gitk:3838
+msgid "Limit to first parent"
+msgstr "Korlátozás az első szülőre"
+
+#: gitk:3839
+msgid "Simple history"
+msgstr "Egyszerű history"
+
+#: gitk:3840
+msgid "Additional arguments to git log:"
+msgstr "További argumentok a git log-hoz:"
+
+#: gitk:3841
+msgid "Enter files and directories to include, one per line:"
+msgstr "Fájlok és könyvtárak bejegyzése amiket tartalmaz, soronként:"
+
+#: gitk:3842
+msgid "Command to generate more commits to include:"
+msgstr "Parancs több tartalmazó commit generálására:"
+
+#: gitk:3964
+msgid "Gitk: edit view"
+msgstr "Gitk: szerkesztés nézet"
+
+#: gitk:3972
+msgid "-- criteria for selecting revisions"
+msgstr "-- kritériumok a revíziók kiválasztásához"
+
+#: gitk:3977
+msgid "View Name"
+msgstr "Nézet neve"
+
+#: gitk:4052
+msgid "Apply (F5)"
+msgstr "Alkalmaz (F5)"
+
+#: gitk:4090
+msgid "Error in commit selection arguments:"
+msgstr "Hiba történt a commit argumentumok kiválasztása közben:"
+
+#: gitk:4143 gitk:4195 gitk:4643 gitk:4657 gitk:5918 gitk:11519 gitk:11520
+msgid "None"
+msgstr "Keine"
+
+#: gitk:4591 gitk:6438 gitk:8282 gitk:8297
+msgid "Date"
+msgstr "Dátum"
+
+#: gitk:4591 gitk:6438
+msgid "CDate"
+msgstr "Dátum"
+
+#: gitk:4740 gitk:4745
+msgid "Descendant"
+msgstr "Leszármazott"
+
+#: gitk:4741
+msgid "Not descendant"
+msgstr "Nem leszármazott"
+
+#: gitk:4748 gitk:4753
+msgid "Ancestor"
+msgstr "Előd"
+
+#: gitk:4749
+msgid "Not ancestor"
+msgstr "Nem előd"
+
+#: gitk:5039
+msgid "Local changes checked in to index but not committed"
+msgstr ""
+"Lokális változtatások, melyek be vannak téve az indexbe, de még nincsenek "
+"commitolva"
+
+#: gitk:5075
+msgid "Local uncommitted changes, not checked in to index"
+msgstr "Lokális nem commitolt változások, nincsenek betéve az indexbe"
+
+#: gitk:6756
+msgid "many"
+msgstr "sok"
+
+#: gitk:6939
+msgid "Tags:"
+msgstr "Tagek:"
+
+#: gitk:6956 gitk:6962 gitk:8275
+msgid "Parent"
+msgstr "Eltern"
+
+#: gitk:6967
+msgid "Child"
+msgstr "Gyerek"
+
+#: gitk:6976
+msgid "Branch"
+msgstr "Ág"
+
+#: gitk:6979
+msgid "Follows"
+msgstr "Következők"
+
+#: gitk:6982
+msgid "Precedes"
+msgstr "Megelőzők"
+
+#: gitk:7519
+#, tcl-format
+msgid "Error getting diffs: %s"
+msgstr "Hiba történt a diff-ek letöltése közben: %s"
+
+#: gitk:8103
+msgid "Goto:"
+msgstr "Menj:"
+
+#: gitk:8105
+msgid "SHA1 ID:"
+msgstr "SHA1 ID:"
+
+#: gitk:8124
+#, tcl-format
+msgid "Short SHA1 id %s is ambiguous"
+msgstr "Rövid SHA1 id %s félreérthető"
+
+#: gitk:8131
+msgid "Revision %s is not known"
+msgstr "A(z) %s revízió nem ismert"
+
+#: gitk:8141
+#, tcl-format
+msgid "SHA1 id %s is not known"
+msgstr "SHA1 id %s nem ismert"
+
+#: gitk:8143
+#, tcl-format
+msgid "Revision %s is not in the current view"
+msgstr "A(z) %s revízió nincs a jelenlegi nézetben"
+
+#: gitk:8285
+msgid "Children"
+msgstr "Gyerekek"
+
+#: gitk:8343
+#, tcl-format
+msgid "Reset %s branch to here"
+msgstr "Állítsd vissza a %s branch-ot ide"
+
+#: gitk:8345
+msgid "Detached head: can't reset"
+msgstr "Elkülönített head: nem lehet visszaállítani"
+
+#: gitk:8454 gitk:8460
+msgid "Skipping merge commit "
+msgstr "Merge commit kihagyása "
+
+#: gitk:8469 gitk:8474
+msgid "Error getting patch ID for "
+msgstr "Hiba történt a patch ID megszerzése közben a következőnél "
+
+#: gitk:8470 gitk:8475
+msgid " - stopping\n"
+msgstr " - abbahagyás\n"
+
+#: gitk:8480 gitk:8483 gitk:8491 gitk:8505 gitk:8514
+msgid "Commit "
+msgstr "Commit "
+
+#: gitk:8484
+msgid ""
+" is the same patch as\n"
+"       "
+msgstr ""
+" Ugyanaz a patch mint\n"
+"       "
+
+#: gitk:8492
+msgid ""
+" differs from\n"
+"       "
+msgstr ""
+" különbözik innentől\n"
+"       "
+
+#: gitk:8494
+msgid ""
+"Diff of commits:\n"
+"\n"
+msgstr ""
+"A commitok diffje:\n"
+"\n"
+
+#: gitk:8506 gitk:8515
+#, tcl-format
+msgid " has %s children - stopping\n"
+msgstr " %s gyereke van. abbahagyás\n"
+
+#: gitk:8534
+msgid "Error writing commit to file: %s"
+msgstr "Hiba történt a commit fájlba írása közben: %s"
+
+#: gitk:8540
+msgid "Error diffing commits: %s"
+msgstr "Hiba történt a commitok diffelése közben: %s"
+
+#: gitk:8570
+msgid "Top"
+msgstr "Teteje"
+
+#: gitk:8571
+msgid "From"
+msgstr "Innen"
+
+#: gitk:8576
+msgid "To"
+msgstr "Ide"
+
+#: gitk:8600
+msgid "Generate patch"
+msgstr "Patch generálása"
+
+#: gitk:8602
+msgid "From:"
+msgstr "Innen:"
+
+#: gitk:8611
+msgid "To:"
+msgstr "Ide:"
+
+#: gitk:8620
+msgid "Reverse"
+msgstr "Visszafele"
+
+#: gitk:8622 gitk:8807
+msgid "Output file:"
+msgstr "Kimeneti fájl:"
+
+#: gitk:8628
+msgid "Generate"
+msgstr "Generálás"
+
+#: gitk:8666
+msgid "Error creating patch:"
+msgstr "Hiba törtét a patch készítése közben:"
+
+#: gitk:8689 gitk:8795 gitk:8852
+msgid "ID:"
+msgstr "ID:"
+
+#: gitk:8698
+msgid "Tag name:"
+msgstr "Tag név:"
+
+#: gitk:8702 gitk:8861
+msgid "Create"
+msgstr "Létrehozás"
+
+#: gitk:8719
+msgid "No tag name specified"
+msgstr "A tag neve nincsen megadva"
+
+#: gitk:8723
+#, tcl-format
+msgid "Tag \"%s\" already exists"
+msgstr "%s Tag már létezik"
+
+#: gitk:8729
+msgid "Error creating tag:"
+msgstr "Hiba történt a tag létrehozása közben:"
+
+#: gitk:8804
+msgid "Command:"
+msgstr "Parancs:"
+
+#: gitk:8812
+msgid "Write"
+msgstr "Írás"
+
+#: gitk:8830
+msgid "Error writing commit:"
+msgstr "Hiba történt a commit írása közben:"
+
+#: gitk:8857
+msgid "Name:"
+msgstr "Név:"
+
+#: gitk:8880
+msgid "Please specify a name for the new branch"
+msgstr "Kérem adja meg a nevét az új branchhoz"
+
+#: gitk:8885
+#, tcl-format
+msgid "Branch '%s' already exists. Overwrite?"
+msgstr "%s branch már létezik. Felülírja?"
+
+#: gitk:8951
+#, tcl-format
+msgid "Commit %s is already included in branch %s -- really re-apply it?"
+msgstr ""
+"%s commit már benne van a %s branchban -- biztos hogy újra csinálja ?"
+"eintragen?"
+
+#: gitk:8956
+msgid "Cherry-picking"
+msgstr "Cherry-picking"
+
+#: gitk:8965
+#, tcl-format
+msgid ""
+"Cherry-pick failed because of local changes to file '%s'.\n"
+"Please commit, reset or stash your changes and try again."
+msgstr ""
+"Cherry-pick hiba történt lokális váltotások miatt a '%s' fájlban.\n"
+"Kérem commitolja, indítsa újra vagy rejtse el a változtatásait és próbálja "
+"újra."
+
+#: gitk:8971
+msgid ""
+"Cherry-pick failed because of merge conflict.\n"
+"Do you wish to run git citool to resolve it?"
+msgstr ""
+"Cherry-pick hiba történt merge konfliktus miatt.\n"
+"Kívánja futtatni a git citool-t a probléma megoldásához?"
+
+#: gitk:8987
+msgid "No changes committed"
+msgstr "Nincsen változás commitolva"
+
+#: gitk:9013
+msgid "Confirm reset"
+msgstr "Újraindítás megerősítése"
+
+#: gitk:9015
+#, tcl-format
+msgid "Reset branch %s to %s?"
+msgstr "Újraindítja a %s branchot %s-ig?"
+
+#: gitk:9017
+msgid "Reset type:"
+msgstr "Újraindítás típusa:"
+
+#: gitk:9020
+msgid "Soft: Leave working tree and index untouched"
+msgstr "Soft: Hagyd a working tree-t és az indexet érintetlenül"
+
+#: gitk:9023
+msgid "Mixed: Leave working tree untouched, reset index"
+msgstr "Kevert: Hagyd a working tree-t érintetlenül, töröld az indexet"
+
+#: gitk:9026
+msgid ""
+"Hard: Reset working tree and index\n"
+"(discard ALL local changes)"
+msgstr ""
+"Hard: Indítsd újra a working tree-t és az indexet\n"
+"(MINDEN lokális változás eldobása)"
+
+#: gitk:9043
+msgid "Resetting"
+msgstr "Újraindítás"
+
+#: gitk:9103
+msgid "Checking out"
+msgstr "Kivesz"
+
+#: gitk:9156
+msgid "Cannot delete the currently checked-out branch"
+msgstr "Nem lehet a jelenleg kivett branch-ot törölni"
+
+#: gitk:9162
+#, tcl-format
+msgid ""
+"The commits on branch %s aren't on any other branch.\n"
+"Really delete branch %s?"
+msgstr ""
+"A %s branchon található commit nem található meg semelyik másik branchon.\n"
+"Tényleg törli a %s branchot?"
+
+#: gitk:9193
+#, tcl-format
+msgid "Tags and heads: %s"
+msgstr "Tagek és headek: %s"
+
+#: gitk:9208
+msgid "Filter"
+msgstr "Szűrő"
+
+#: gitk:9503
+msgid ""
+"Error reading commit topology information; branch and preceding/following "
+"tag information will be incomplete."
+msgstr ""
+"Hiba történt a commit topológiai információ olvasása közben; branch ésa "
+"megelőző/következő információ nem lesz teljes."
+
+#: gitk:10489
+msgid "Tag"
+msgstr "Tag"
+
+#: gitk:10489
+msgid "Id"
+msgstr "Id"
+
+#: gitk:10539
+msgid "Gitk font chooser"
+msgstr "Gitk-betű kiválasztó"
+
+#: gitk:10556
+msgid "B"
+msgstr "F"
+
+#: gitk:10559
+msgid "I"
+msgstr "K"
+
+#: gitk:10677
+msgid "Gitk preferences"
+msgstr "Gitk beállítások"
+
+#: gitk:10679
+msgid "Commit list display options"
+msgstr "Commit lista kijelzési opciók"
+
+#: gitk:10682
+msgid "Maximum graph width (lines)"
+msgstr "Maximális grafikon szélesség (sorok)"
+
+#: gitk:10685
+#, tcl-format
+msgid "Maximum graph width (% of pane)"
+msgstr "Maximális grafikon szélesség (táble %-je)"
+
+#: gitk:10688
+msgid "Show local changes"
+msgstr "Mutasd a lokális változtatásokat"
+
+#: gitk:10691
+msgid "Auto-select SHA1"
+msgstr "SHA1 Automatikus kiválasztása"
+
+#: gitk:10694
+msgid "Hide remote refs"
+msgstr "A távoli refek elrejtése"
+
+#: gitk:10698
+msgid "Diff display options"
+msgstr "Diff kijelző opciók"
+
+#: gitk:10700
+msgid "Tab spacing"
+msgstr "Tab sorköz"
+
+#: gitk:10703
+msgid "Display nearby tags"
+msgstr "Szomszédos tagek kijelzése"
+
+#: gitk:10706
+msgid "Limit diffs to listed paths"
+msgstr "Korlátozott diffek a kilistázott útvonalakhoz"
+
+#: gitk:10709
+msgid "Support per-file encodings"
+msgstr "Fájlonkénti kódolás támgatása"
+
+#: gitk:10715 gitk:10804
+msgid "External diff tool"
+msgstr "Külső diff alkalmazás"
+
+#: gitk:10716
+msgid "Choose..."
+msgstr "Válaszd ..."
+
+#: gitk:10721
+msgid "General options"
+msgstr "Általános opciók"
+
+#: gitk:10724
+msgid "Use themed widgets"
+msgstr "Témázott vezérlők használata"
+
+#: gitk:10726
+msgid "(change requires restart)"
+msgstr "(a változás újraindítást igényel)"
+
+#: gitk:10728
+msgid "(currently unavailable)"
+msgstr "(jelenleg nem elérhető)"
+
+#: gitk:10732
+msgid "Colors: press to choose"
+msgstr "Színek: nyomja meg a kiválasztáshoz"
+
+#: gitk:10735
+msgid "Interface"
+msgstr "Interfész"
+
+#: gitk:10736
+msgid "interface"
+msgstr "interfész"
+
+#: gitk:10739
+msgid "Background"
+msgstr "Háttér"
+
+#: gitk:10740 gitk:10770
+msgid "background"
+msgstr "háttér"
+
+#: gitk:10743
+msgid "Foreground"
+msgstr "Előtér"
+
+#: gitk:10744
+msgid "foreground"
+msgstr "előtér"
+
+#: gitk:10747
+msgid "Diff: old lines"
+msgstr "Diff: régi sorok"
+
+#: gitk:10748
+msgid "diff old lines"
+msgstr "diff régi sorok"
+
+#: gitk:10752
+msgid "Diff: new lines"
+msgstr "Diff: új sorok"
+
+#: gitk:10753
+msgid "diff new lines"
+msgstr "diff - új sorok"
+
+#: gitk:10757
+msgid "Diff: hunk header"
+msgstr "Diff: nagy headerök"
+
+#: gitk:10759
+msgid "diff hunk header"
+msgstr "diff - nagy headerök"
+
+#: gitk:10763
+msgid "Marked line bg"
+msgstr "Megjelölt sor háttér"
+
+#: gitk:10765
+msgid "marked line background"
+msgstr "megjelölt sor háttér"
+
+#: gitk:10769
+msgid "Select bg"
+msgstr "Válasszon hátteret"
+
+#: gitk:10773
+msgid "Fonts: press to choose"
+msgstr "Betű: nyomja meg a kiválasztáshoz"
+
+#: gitk:10775
+msgid "Main font"
+msgstr "Fő betű"
+
+#: gitk:10776
+msgid "Diff display font"
+msgstr "Diff kijelző betű"
+
+#: gitk:10777
+msgid "User interface font"
+msgstr "Felhasználói interfész betű"
+
+#: gitk:10814
+#, tcl-format
+msgid "Gitk: choose color for %s"
+msgstr "Gitk: válasszon színt a %s-ra"
+
+#: gitk:11418
+msgid "Cannot find a git repository here."
+msgstr "Nem találhatü git repository itt."
+
+#: gitk:11422
+#, tcl-format
+msgid "Cannot find the git directory \"%s\"."
+msgstr "Nem található a \"%s\" könyvtár."
+
+#: gitk:11469
+#, tcl-format
+msgid "Ambiguous argument '%s': both revision and filename"
+msgstr "Félreérthető argumentum '%s': revízió és fájlnév is"
+
+#: gitk:11481
+msgid "Bad arguments to gitk:"
+msgstr "Rossz gitk argumentumok:"
+
+#: gitk:11572
+msgid "Command line"
+msgstr "Parancs sor"
+
+#~ msgid "Use all refs"
+#~ msgstr "Használd az összes referenciát"
+
+#~ msgid "Max count:"
+#~ msgstr "Max. szám:"
+
+#~ msgid "Skip:"
+#~ msgstr "Kihagy:"
+
+#~ msgid "Name"
+#~ msgstr "Név"
+
+#~ msgid "Tag/Head %s is not known"
+#~ msgstr "Tag/Head %s nem ismert"
+
+#~ msgid "- stopping\n"
+#~ msgstr "- abbahagyás.\n"
+
+#~ msgid ""
+#~ "Sorry, gitk cannot run with this version of Tcl/Tk.\n"
+#~ " Gitk requires at least Tcl/Tk 8.4."
+#~ msgstr ""
+#~ "Sajnáljuk, de a gitk nem futtatható ezzel a Tcl/Tk verzióval.\n"
+#~ "Gitk futtatásához legalább Tcl/Tk 8.4 szükséges."
diff --git a/gitk-git/po/it.po b/gitk-git/po/it.po
index e89c957..a730d63 100644
--- a/gitk-git/po/it.po
+++ b/gitk-git/po/it.po
@@ -8,25 +8,33 @@
 msgstr ""
 "Project-Id-Version: gitk\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-10-18 22:03+1100\n"
-"PO-Revision-Date: 2008-03-13 17:34+0100\n"
+"POT-Creation-Date: 2010-01-28 18:40+0100\n"
+"PO-Revision-Date: 2010-01-28 18:41+0100\n"
 "Last-Translator: Michele Ballabio <barra_cuda@katamail.com>\n"
 "Language-Team: Italian\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: gitk:113
+#: gitk:115
 msgid "Couldn't get list of unmerged files:"
 msgstr "Impossibile ottenere l'elenco dei file in attesa di fusione:"
 
-#: gitk:340
+#: gitk:274
+msgid "Error parsing revisions:"
+msgstr "Errore nella lettura delle revisioni:"
+
+#: gitk:329
+msgid "Error executing --argscmd command:"
+msgstr "Errore nell'esecuzione del comando specificato con --argscmd:"
+
+#: gitk:342
 msgid "No files selected: --merge specified but no files are unmerged."
 msgstr ""
 "Nessun file selezionato: è stata specificata l'opzione --merge ma non ci "
 "sono file in attesa di fusione."
 
-#: gitk:343
+#: gitk:345
 msgid ""
 "No files selected: --merge specified but no unmerged files are within file "
 "limit."
@@ -34,688 +42,994 @@
 "Nessun file selezionato: è stata specificata l'opzione --merge ma i file "
 "specificati non sono in attesa di fusione."
 
-#: gitk:365 gitk:503
+#: gitk:367 gitk:514
 msgid "Error executing git log:"
 msgstr "Errore nell'esecuzione di git log:"
 
-#: gitk:378
+#: gitk:385 gitk:530
 msgid "Reading"
 msgstr "Lettura in corso"
 
-#: gitk:438 gitk:3462
+#: gitk:445 gitk:4261
 msgid "Reading commits..."
 msgstr "Lettura delle revisioni in corso..."
 
-#: gitk:441 gitk:1528 gitk:3465
+#: gitk:448 gitk:1578 gitk:4264
 msgid "No commits selected"
 msgstr "Nessuna revisione selezionata"
 
-#: gitk:1399
+#: gitk:1454
 msgid "Can't parse git log output:"
 msgstr "Impossibile elaborare i dati di git log:"
 
-#: gitk:1605
+#: gitk:1674
 msgid "No commit information available"
 msgstr "Nessuna informazione disponibile sulle revisioni"
 
-#: gitk:1709 gitk:1731 gitk:3259 gitk:7764 gitk:9293 gitk:9466
+#: gitk:1816
+msgid "mc"
+msgstr ""
+
+#: gitk:1851 gitk:4054 gitk:9044 gitk:10585 gitk:10804
 msgid "OK"
 msgstr "OK"
 
-#: gitk:1733 gitk:3260 gitk:7439 gitk:7510 gitk:7613 gitk:7660 gitk:7766
-#: gitk:9294 gitk:9467
+#: gitk:1853 gitk:4056 gitk:8634 gitk:8713 gitk:8828 gitk:8877 gitk:9046
+#: gitk:10586 gitk:10805
 msgid "Cancel"
 msgstr "Annulla"
 
-#: gitk:1811
+#: gitk:1975
 msgid "Update"
 msgstr "Aggiorna"
 
-#: gitk:1813
+#: gitk:1976
+msgid "Reload"
+msgstr "Ricarica"
+
+#: gitk:1977
 msgid "Reread references"
 msgstr "Rileggi riferimenti"
 
-#: gitk:1814
+#: gitk:1978
 msgid "List references"
 msgstr "Elenca riferimenti"
 
-#: gitk:1815
+#: gitk:1980
+msgid "Start git gui"
+msgstr "Avvia git gui"
+
+#: gitk:1982
 msgid "Quit"
 msgstr "Esci"
 
-#: gitk:1810
+#: gitk:1974
 msgid "File"
 msgstr "File"
 
-#: gitk:1818
+#: gitk:1986
 msgid "Preferences"
 msgstr "Preferenze"
 
-#: gitk:1817
+#: gitk:1985
 msgid "Edit"
 msgstr "Modifica"
 
-#: gitk:1821
+#: gitk:1990
 msgid "New view..."
 msgstr "Nuova vista..."
 
-#: gitk:1822
+#: gitk:1991
 msgid "Edit view..."
 msgstr "Modifica vista..."
 
-#: gitk:1823
+#: gitk:1992
 msgid "Delete view"
 msgstr "Elimina vista"
 
-#: gitk:1825
+#: gitk:1994
 msgid "All files"
 msgstr "Tutti i file"
 
-#: gitk:1820 gitk:3196
+#: gitk:1989 gitk:3808
 msgid "View"
 msgstr "Vista"
 
-#: gitk:1828 gitk:2487
+#: gitk:1999 gitk:2009 gitk:2780
 msgid "About gitk"
 msgstr "Informazioni su gitk"
 
-#: gitk:1829
+#: gitk:2000 gitk:2014
 msgid "Key bindings"
 msgstr "Scorciatoie da tastiera"
 
-#: gitk:1827
+#: gitk:1998 gitk:2013
 msgid "Help"
 msgstr "Aiuto"
 
-#: gitk:1887
-msgid "SHA1 ID: "
-msgstr "SHA1 ID: "
+#: gitk:2091 gitk:8110
+msgid "SHA1 ID:"
+msgstr "SHA1 ID:"
 
-#: gitk:1918
+#: gitk:2122
 msgid "Row"
-msgstr ""
+msgstr "Riga"
 
-#: gitk:1949
+#: gitk:2160
 msgid "Find"
 msgstr "Trova"
 
-#: gitk:1950
+#: gitk:2161
 msgid "next"
 msgstr "succ"
 
-#: gitk:1951
+#: gitk:2162
 msgid "prev"
 msgstr "prec"
 
-#: gitk:1952
+#: gitk:2163
 msgid "commit"
 msgstr "revisione"
 
-#: gitk:1955 gitk:1957 gitk:3617 gitk:3640 gitk:3664 gitk:5550 gitk:5621
+#: gitk:2166 gitk:2168 gitk:4422 gitk:4445 gitk:4469 gitk:6410 gitk:6482
+#: gitk:6566
 msgid "containing:"
 msgstr "contenente:"
 
-#: gitk:1958 gitk:2954 gitk:2959 gitk:3692
+#: gitk:2169 gitk:3290 gitk:3295 gitk:4497
 msgid "touching paths:"
 msgstr "che riguarda i percorsi:"
 
-#: gitk:1959 gitk:3697
+#: gitk:2170 gitk:4502
 msgid "adding/removing string:"
 msgstr "che aggiunge/rimuove la stringa:"
 
-#: gitk:1968 gitk:1970
+#: gitk:2179 gitk:2181
 msgid "Exact"
 msgstr "Esatto"
 
-#: gitk:1970 gitk:3773 gitk:5518
+#: gitk:2181 gitk:4577 gitk:6378
 msgid "IgnCase"
 msgstr ""
 
-#: gitk:1970 gitk:3666 gitk:3771 gitk:5514
+#: gitk:2181 gitk:4471 gitk:4575 gitk:6374
 msgid "Regexp"
 msgstr ""
 
-#: gitk:1972 gitk:1973 gitk:3792 gitk:3822 gitk:3829 gitk:5641 gitk:5708
+#: gitk:2183 gitk:2184 gitk:4596 gitk:4626 gitk:4633 gitk:6502 gitk:6570
 msgid "All fields"
 msgstr "Tutti i campi"
 
-#: gitk:1973 gitk:3790 gitk:3822 gitk:5580
+#: gitk:2184 gitk:4594 gitk:4626 gitk:6441
 msgid "Headline"
 msgstr "Titolo"
 
-#: gitk:1974 gitk:3790 gitk:5580 gitk:5708 gitk:6109
+#: gitk:2185 gitk:4594 gitk:6441 gitk:6570 gitk:7003
 msgid "Comments"
 msgstr "Commenti"
 
-#: gitk:1974 gitk:3790 gitk:3794 gitk:3829 gitk:5580 gitk:6045 gitk:7285
-#: gitk:7300
+#: gitk:2185 gitk:4594 gitk:4598 gitk:4633 gitk:6441 gitk:6938 gitk:8285
+#: gitk:8300
 msgid "Author"
 msgstr "Autore"
 
-#: gitk:1974 gitk:3790 gitk:5580 gitk:6047
+#: gitk:2185 gitk:4594 gitk:6441 gitk:6940
 msgid "Committer"
 msgstr "Revisione creata da"
 
-#: gitk:2003
+#: gitk:2216
 msgid "Search"
 msgstr "Cerca"
 
-#: gitk:2010
+#: gitk:2224
 msgid "Diff"
 msgstr ""
 
-#: gitk:2012
+#: gitk:2226
 msgid "Old version"
 msgstr "Vecchia versione"
 
-#: gitk:2014
+#: gitk:2228
 msgid "New version"
 msgstr "Nuova versione"
 
-#: gitk:2016
+#: gitk:2230
 msgid "Lines of context"
 msgstr "Linee di contesto"
 
-#: gitk:2026
+#: gitk:2240
 msgid "Ignore space change"
 msgstr "Ignora modifiche agli spazi"
 
-#: gitk:2084
+#: gitk:2299
 msgid "Patch"
 msgstr "Modifiche"
 
-#: gitk:2086
+#: gitk:2301
 msgid "Tree"
 msgstr "Directory"
 
-#: gitk:2213 gitk:2226
+#: gitk:2456 gitk:2473
 msgid "Diff this -> selected"
 msgstr "Diff questo -> selezionato"
 
-#: gitk:2214 gitk:2227
+#: gitk:2457 gitk:2474
 msgid "Diff selected -> this"
 msgstr "Diff selezionato -> questo"
 
-#: gitk:2215 gitk:2228
+#: gitk:2458 gitk:2475
 msgid "Make patch"
 msgstr "Crea patch"
 
-#: gitk:2216 gitk:7494
+#: gitk:2459 gitk:8692
 msgid "Create tag"
 msgstr "Crea etichetta"
 
-#: gitk:2217 gitk:7593
+#: gitk:2460 gitk:8808
 msgid "Write commit to file"
 msgstr "Scrivi revisione in un file"
 
-#: gitk:2218 gitk:7647
+#: gitk:2461 gitk:8865
 msgid "Create new branch"
 msgstr "Crea un nuovo ramo"
 
-#: gitk:2219
+#: gitk:2462
 msgid "Cherry-pick this commit"
 msgstr "Porta questa revisione in cima al ramo attuale"
 
-#: gitk:2220
+#: gitk:2463
 msgid "Reset HEAD branch to here"
 msgstr "Aggiorna il ramo HEAD a questa revisione"
 
-#: gitk:2234
+#: gitk:2464
+msgid "Mark this commit"
+msgstr "Segna questa revisione"
+
+#: gitk:2465
+msgid "Return to mark"
+msgstr "Torna alla revisione segnata"
+
+#: gitk:2466
+msgid "Find descendant of this and mark"
+msgstr "Trova il discendente di questa revisione e di quella segnata"
+
+#: gitk:2467
+msgid "Compare with marked commit"
+msgstr "Confronta con la revisione segnata"
+
+#: gitk:2481
 msgid "Check out this branch"
 msgstr "Attiva questo ramo"
 
-#: gitk:2235
+#: gitk:2482
 msgid "Remove this branch"
 msgstr "Elimina questo ramo"
 
-#: gitk:2242
+#: gitk:2489
 msgid "Highlight this too"
 msgstr "Evidenzia anche questo"
 
-#: gitk:2243
+#: gitk:2490
 msgid "Highlight this only"
 msgstr "Evidenzia solo questo"
 
-#: gitk:2245
-msgid "Blame parent commit"
-msgstr ""
+#: gitk:2491
+msgid "External diff"
+msgstr "Visualizza differenze in un altro programma"
 
-#: gitk:2488
+#: gitk:2492
+msgid "Blame parent commit"
+msgstr "Annota la revisione precedente"
+
+#: gitk:2499
+msgid "Show origin of this line"
+msgstr "Mostra la provenienza di questa riga"
+
+#: gitk:2500
+msgid "Run git gui blame on this line"
+msgstr "Esegui git gui blame su questa riga"
+
+#: gitk:2782
 msgid ""
 "\n"
 "Gitk - a commit viewer for git\n"
 "\n"
-"Copyright © 2005-2008 Paul Mackerras\n"
+"Copyright \\u00a9 2005-2010 Paul Mackerras\n"
 "\n"
 "Use and redistribute under the terms of the GNU General Public License"
 msgstr ""
 "\n"
 "Gitk - un visualizzatore di revisioni per git\n"
 "\n"
-"Copyright © 2005-2008 Paul Mackerras\n"
+"Copyright \\u00a9 2005-2010 Paul Mackerras\n"
 "\n"
 "Utilizzo e redistribuzione permessi sotto i termini della GNU General Public "
 "License"
 
-#: gitk:2496 gitk:2557 gitk:7943
+#: gitk:2790 gitk:2854 gitk:9230
 msgid "Close"
 msgstr "Chiudi"
 
-#: gitk:2515
+#: gitk:2811
 msgid "Gitk key bindings"
 msgstr "Scorciatoie da tastiera di Gitk"
 
-#: gitk:2517
+#: gitk:2814
 msgid "Gitk key bindings:"
 msgstr "Scorciatoie da tastiera di Gitk:"
 
-#: gitk:2519
+#: gitk:2816
 #, tcl-format
 msgid "<%s-Q>\t\tQuit"
 msgstr "<%s-Q>\t\tEsci"
 
-#: gitk:2520
+#: gitk:2817
 msgid "<Home>\t\tMove to first commit"
 msgstr "<Home>\t\tVai alla prima revisione"
 
-#: gitk:2521
+#: gitk:2818
 msgid "<End>\t\tMove to last commit"
 msgstr "<End>\t\tVai all'ultima revisione"
 
-#: gitk:2522
+#: gitk:2819
 msgid "<Up>, p, i\tMove up one commit"
-msgstr "<Up>, p, i\tVai più in alto di una revisione"
+msgstr "<Su>, p, i\tVai più in alto di una revisione"
 
-#: gitk:2523
+#: gitk:2820
 msgid "<Down>, n, k\tMove down one commit"
-msgstr "<Down>, n, k\tVai più in basso di una revisione"
+msgstr "<Giù>, n, k\tVai più in basso di una revisione"
 
-#: gitk:2524
+#: gitk:2821
 msgid "<Left>, z, j\tGo back in history list"
-msgstr "<Left>, z, j\tTorna indietro nella cronologia"
+msgstr "<Sinistra>, z, j\tTorna indietro nella cronologia"
 
-#: gitk:2525
+#: gitk:2822
 msgid "<Right>, x, l\tGo forward in history list"
-msgstr "<Right>, x, l\tVai avanti nella cronologia"
+msgstr "<Destra>, x, l\tVai avanti nella cronologia"
 
-#: gitk:2526
+#: gitk:2823
 msgid "<PageUp>\tMove up one page in commit list"
-msgstr "<PageUp>\tVai più in alto di una pagina nella lista delle revisioni"
+msgstr "<PaginaSu>\tVai più in alto di una pagina nella lista delle revisioni"
 
-#: gitk:2527
+#: gitk:2824
 msgid "<PageDown>\tMove down one page in commit list"
-msgstr "<PageDown>\tVai più in basso di una pagina nella lista delle revisioni"
+msgstr ""
+"<PaginaGiù>\tVai più in basso di una pagina nella lista delle revisioni"
 
-#: gitk:2528
+#: gitk:2825
 #, tcl-format
 msgid "<%s-Home>\tScroll to top of commit list"
 msgstr "<%s-Home>\tScorri alla cima della lista delle revisioni"
 
-#: gitk:2529
+#: gitk:2826
 #, tcl-format
 msgid "<%s-End>\tScroll to bottom of commit list"
 msgstr "<%s-End>\tScorri alla fine della lista delle revisioni"
 
-#: gitk:2530
+#: gitk:2827
 #, tcl-format
 msgid "<%s-Up>\tScroll commit list up one line"
-msgstr "<%s-Up>\tScorri la lista delle revisioni in alto di una riga"
+msgstr "<%s-Su>\tScorri la lista delle revisioni in alto di una riga"
 
-#: gitk:2531
+#: gitk:2828
 #, tcl-format
 msgid "<%s-Down>\tScroll commit list down one line"
-msgstr "<%s-Down>\tScorri la lista delle revisioni in basso di una riga"
+msgstr "<%s-Giù>\tScorri la lista delle revisioni in basso di una riga"
 
-#: gitk:2532
+#: gitk:2829
 #, tcl-format
 msgid "<%s-PageUp>\tScroll commit list up one page"
-msgstr "<%s-PageUp>\tScorri la lista delle revisioni in alto di una pagina"
+msgstr "<%s-PaginaSu>\tScorri la lista delle revisioni in alto di una pagina"
 
-#: gitk:2533
+#: gitk:2830
 #, tcl-format
 msgid "<%s-PageDown>\tScroll commit list down one page"
-msgstr "<%s-PageDown>\tScorri la lista delle revisioni in basso di una pagina"
+msgstr "<%s-PaginaGiù>\tScorri la lista delle revisioni in basso di una pagina"
 
-#: gitk:2534
+#: gitk:2831
 msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
-msgstr "<Shift-Up>\tTrova all'indietro (verso l'alto, revisioni successive)"
+msgstr "<Shift-Su>\tTrova all'indietro (verso l'alto, revisioni successive)"
 
-#: gitk:2535
+#: gitk:2832
 msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
-msgstr "<Shift-Down>\tTrova in avanti (verso il basso, revisioni precedenti)"
+msgstr "<Shift-Giù>\tTrova in avanti (verso il basso, revisioni precedenti)"
 
-#: gitk:2536
+#: gitk:2833
 msgid "<Delete>, b\tScroll diff view up one page"
 msgstr "<Delete>, b\tScorri la vista delle differenze in alto di una pagina"
 
-#: gitk:2537
+#: gitk:2834
 msgid "<Backspace>\tScroll diff view up one page"
 msgstr "<Backspace>\tScorri la vista delle differenze in alto di una pagina"
 
-#: gitk:2538
+#: gitk:2835
 msgid "<Space>\t\tScroll diff view down one page"
-msgstr "<Space>\t\tScorri la vista delle differenze in basso di una pagina"
+msgstr "<Spazio>\t\tScorri la vista delle differenze in basso di una pagina"
 
-#: gitk:2539
+#: gitk:2836
 msgid "u\t\tScroll diff view up 18 lines"
 msgstr "u\t\tScorri la vista delle differenze in alto di 18 linee"
 
-#: gitk:2540
+#: gitk:2837
 msgid "d\t\tScroll diff view down 18 lines"
 msgstr "d\t\tScorri la vista delle differenze in basso di 18 linee"
 
-#: gitk:2541
+#: gitk:2838
 #, tcl-format
 msgid "<%s-F>\t\tFind"
 msgstr "<%s-F>\t\tTrova"
 
-#: gitk:2542
+#: gitk:2839
 #, tcl-format
 msgid "<%s-G>\t\tMove to next find hit"
 msgstr "<%s-G>\t\tTrova in avanti"
 
-#: gitk:2543
+#: gitk:2840
 msgid "<Return>\tMove to next find hit"
-msgstr "<Return>\tTrova in avanti"
+msgstr "<Invio>\tTrova in avanti"
 
-#: gitk:2544
-msgid "/\t\tMove to next find hit, or redo find"
-msgstr "/\t\tTrova in avanti, o cerca di nuovo"
+#: gitk:2841
+msgid "/\t\tFocus the search box"
+msgstr "/\t\tCursore nel box di ricerca"
 
-#: gitk:2545
+#: gitk:2842
 msgid "?\t\tMove to previous find hit"
 msgstr "?\t\tTrova all'indietro"
 
-#: gitk:2546
+#: gitk:2843
 msgid "f\t\tScroll diff view to next file"
 msgstr "f\t\tScorri la vista delle differenze al file successivo"
 
-#: gitk:2547
+#: gitk:2844
 #, tcl-format
 msgid "<%s-S>\t\tSearch for next hit in diff view"
 msgstr "<%s-S>\t\tCerca in avanti nella vista delle differenze"
 
-#: gitk:2548
+#: gitk:2845
 #, tcl-format
 msgid "<%s-R>\t\tSearch for previous hit in diff view"
 msgstr "<%s-R>\t\tCerca all'indietro nella vista delle differenze"
 
-#: gitk:2549
+#: gitk:2846
 #, tcl-format
 msgid "<%s-KP+>\tIncrease font size"
-msgstr "<%s-KP+>\tAumenta grandezza carattere"
+msgstr "<%s-KP+>\tAumenta dimensione carattere"
 
-#: gitk:2550
+#: gitk:2847
 #, tcl-format
 msgid "<%s-plus>\tIncrease font size"
-msgstr "<%s-plus>\tAumenta grandezza carattere"
+msgstr "<%s-più>\tAumenta dimensione carattere"
 
-#: gitk:2551
+#: gitk:2848
 #, tcl-format
 msgid "<%s-KP->\tDecrease font size"
-msgstr "<%s-KP->\tDiminuisci grandezza carattere"
+msgstr "<%s-KP->\tDiminuisci dimensione carattere"
 
-#: gitk:2552
+#: gitk:2849
 #, tcl-format
 msgid "<%s-minus>\tDecrease font size"
-msgstr "<%s-minus>\tDiminuisci grandezza carattere"
+msgstr "<%s-meno>\tDiminuisci dimensione carattere"
 
-#: gitk:2553
+#: gitk:2850
 msgid "<F5>\t\tUpdate"
 msgstr "<F5>\t\tAggiorna"
 
-#: gitk:3200
-msgid "Gitk view definition"
-msgstr "Scelta vista Gitk"
+#: gitk:3305 gitk:3314
+#, tcl-format
+msgid "Error creating temporary directory %s:"
+msgstr "Errore durante la creazione della directory temporanea %s:"
 
-#: gitk:3225
-msgid "Name"
-msgstr "Nome"
+#: gitk:3327
+#, tcl-format
+msgid "Error getting \"%s\" from %s:"
+msgstr "Errore nella lettura di \"%s\" da %s:"
 
-#: gitk:3228
-msgid "Remember this view"
-msgstr "Ricorda questa vista"
+#: gitk:3390
+msgid "command failed:"
+msgstr "impossibile eseguire il comando:"
 
-#: gitk:3232
-msgid "Commits to include (arguments to git log):"
-msgstr "Revisioni da includere (argomenti di git log):"
+#: gitk:3539
+msgid "No such commit"
+msgstr "Revisione inesistente"
 
-#: gitk:3239
-msgid "Command to generate more commits to include:"
-msgstr "Comando che genera altre revisioni da visualizzare:"
+#: gitk:3553
+msgid "git gui blame: command failed:"
+msgstr "git gui blame: impossibile eseguire il comando:"
 
-#: gitk:3246
-msgid "Enter files and directories to include, one per line:"
-msgstr "Inserire file e directory da includere, uno per riga:"
+#: gitk:3584
+#, tcl-format
+msgid "Couldn't read merge head: %s"
+msgstr "Impossibile leggere merge head: %s"
 
-#: gitk:3293
-msgid "Error in commit selection arguments:"
-msgstr "Errore negli argomenti di selezione delle revisioni:"
+#: gitk:3592
+#, tcl-format
+msgid "Error reading index: %s"
+msgstr "Errore nella lettura dell'indice: %s"
 
-#: gitk:3347 gitk:3399 gitk:3842 gitk:3856 gitk:5060 gitk:10141 gitk:10142
-msgid "None"
-msgstr "Nessuno"
+#: gitk:3617
+#, tcl-format
+msgid "Couldn't start git blame: %s"
+msgstr "Impossibile eseguire git blame: %s"
 
-#: gitk:3790 gitk:5580 gitk:7287 gitk:7302
-msgid "Date"
-msgstr "Data"
-
-#: gitk:3790 gitk:5580
-msgid "CDate"
-msgstr ""
-
-#: gitk:3939 gitk:3944
-msgid "Descendant"
-msgstr "Discendente"
-
-#: gitk:3940
-msgid "Not descendant"
-msgstr "Non discendente"
-
-#: gitk:3947 gitk:3952
-msgid "Ancestor"
-msgstr "Ascendente"
-
-#: gitk:3948
-msgid "Not ancestor"
-msgstr "Non ascendente"
-
-#: gitk:4187
-msgid "Local changes checked in to index but not committed"
-msgstr "Modifiche locali presenti nell'indice ma non nell'archivio"
-
-#: gitk:4220
-msgid "Local uncommitted changes, not checked in to index"
-msgstr "Modifiche locali non presenti né nell'archivio né nell'indice"
-
-#: gitk:5549
+#: gitk:3620 gitk:6409
 msgid "Searching"
 msgstr "Ricerca in corso"
 
-#: gitk:6049
+#: gitk:3652
+#, tcl-format
+msgid "Error running git blame: %s"
+msgstr "Errore nell'esecuzione di git blame: %s"
+
+#: gitk:3680
+#, tcl-format
+msgid "That line comes from commit %s,  which is not in this view"
+msgstr "Quella riga proviene dalla revisione %s, non presente in questa vista"
+
+#: gitk:3694
+msgid "External diff viewer failed:"
+msgstr "Impossibile eseguire il visualizzatore di differenze:"
+
+#: gitk:3812
+msgid "Gitk view definition"
+msgstr "Scelta vista Gitk"
+
+#: gitk:3816
+msgid "Remember this view"
+msgstr "Ricorda questa vista"
+
+#: gitk:3817
+msgid "References (space separated list):"
+msgstr "Riferimenti (lista di elementi separati da spazi)"
+
+#: gitk:3818
+msgid "Branches & tags:"
+msgstr "Rami ed etichette"
+
+#: gitk:3819
+msgid "All refs"
+msgstr "Tutti i riferimenti"
+
+#: gitk:3820
+msgid "All (local) branches"
+msgstr "Tutti i rami (locali)"
+
+#: gitk:3821
+msgid "All tags"
+msgstr "Tutte le etichette"
+
+#: gitk:3822
+msgid "All remote-tracking branches"
+msgstr "Tutti i rami remoti"
+
+#: gitk:3823
+msgid "Commit Info (regular expressions):"
+msgstr "Informazioni sulla revisione (espressioni regolari):"
+
+#: gitk:3824
+msgid "Author:"
+msgstr "Autore:"
+
+#: gitk:3825
+msgid "Committer:"
+msgstr "Revisione creata da:"
+
+#: gitk:3826
+msgid "Commit Message:"
+msgstr "Messaggio di revisione:"
+
+#: gitk:3827
+msgid "Matches all Commit Info criteria"
+msgstr "Risponde a tutti i criteri di ricerca sulle revisioni"
+
+#: gitk:3828
+msgid "Changes to Files:"
+msgstr "Modifiche ai file:"
+
+#: gitk:3829
+msgid "Fixed String"
+msgstr "Stringa fissa"
+
+#: gitk:3830
+msgid "Regular Expression"
+msgstr "Espressione regolare"
+
+#: gitk:3831
+msgid "Search string:"
+msgstr "Cerca stringa:"
+
+#: gitk:3832
+msgid ""
+"Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 "
+"15:27:38\"):"
+msgstr ""
+"Date di revisione (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, "
+"2009 15:27:38\"):"
+
+#: gitk:3833
+msgid "Since:"
+msgstr "Da:"
+
+#: gitk:3834
+msgid "Until:"
+msgstr "A:"
+
+#: gitk:3835
+msgid "Limit and/or skip a number of revisions (positive integer):"
+msgstr "Limita e/o salta N revisioni (intero positivo):"
+
+#: gitk:3836
+msgid "Number to show:"
+msgstr "Numero di revisioni da mostrare:"
+
+#: gitk:3837
+msgid "Number to skip:"
+msgstr "Numero di revisioni da saltare:"
+
+#: gitk:3838
+msgid "Miscellaneous options:"
+msgstr "Altre opzioni:"
+
+#: gitk:3839
+msgid "Strictly sort by date"
+msgstr "Ordina solo per data"
+
+#: gitk:3840
+msgid "Mark branch sides"
+msgstr "Segna i lati del ramo"
+
+#: gitk:3841
+msgid "Limit to first parent"
+msgstr "Limita al primo genitore"
+
+#: gitk:3842
+msgid "Simple history"
+msgstr "Cronologia semplificata"
+
+#: gitk:3843
+msgid "Additional arguments to git log:"
+msgstr "Ulteriori argomenti da passare a git log:"
+
+#: gitk:3844
+msgid "Enter files and directories to include, one per line:"
+msgstr "Inserire file e directory da includere, uno per riga:"
+
+#: gitk:3845
+msgid "Command to generate more commits to include:"
+msgstr "Comando che genera altre revisioni da visualizzare:"
+
+#: gitk:3967
+msgid "Gitk: edit view"
+msgstr "Gitk: modifica vista"
+
+#: gitk:3975
+msgid "-- criteria for selecting revisions"
+msgstr "-- criteri per la scelta delle revisioni"
+
+#: gitk:3980
+msgid "View Name"
+msgstr "Nome vista"
+
+#: gitk:4055
+msgid "Apply (F5)"
+msgstr "Applica (F5)"
+
+#: gitk:4093
+msgid "Error in commit selection arguments:"
+msgstr "Errore negli argomenti di selezione delle revisioni:"
+
+#: gitk:4146 gitk:4198 gitk:4646 gitk:4660 gitk:5921 gitk:11534 gitk:11535
+msgid "None"
+msgstr "Nessuno"
+
+#: gitk:4594 gitk:6441 gitk:8287 gitk:8302
+msgid "Date"
+msgstr "Data"
+
+#: gitk:4594 gitk:6441
+msgid "CDate"
+msgstr ""
+
+#: gitk:4743 gitk:4748
+msgid "Descendant"
+msgstr "Discendente"
+
+#: gitk:4744
+msgid "Not descendant"
+msgstr "Non discendente"
+
+#: gitk:4751 gitk:4756
+msgid "Ancestor"
+msgstr "Ascendente"
+
+#: gitk:4752
+msgid "Not ancestor"
+msgstr "Non ascendente"
+
+#: gitk:5042
+msgid "Local changes checked in to index but not committed"
+msgstr "Modifiche locali presenti nell'indice ma non nell'archivio"
+
+#: gitk:5078
+msgid "Local uncommitted changes, not checked in to index"
+msgstr "Modifiche locali non presenti né nell'archivio né nell'indice"
+
+#: gitk:6759
+msgid "many"
+msgstr "molti"
+
+#: gitk:6942
 msgid "Tags:"
 msgstr "Etichette:"
 
-#: gitk:6066 gitk:6072 gitk:7280
+#: gitk:6959 gitk:6965 gitk:8280
 msgid "Parent"
 msgstr "Genitore"
 
-#: gitk:6077
+#: gitk:6970
 msgid "Child"
 msgstr "Figlio"
 
-#: gitk:6086
+#: gitk:6979
 msgid "Branch"
 msgstr "Ramo"
 
-#: gitk:6089
+#: gitk:6982
 msgid "Follows"
 msgstr "Segue"
 
-#: gitk:6092
+#: gitk:6985
 msgid "Precedes"
 msgstr "Precede"
 
-#: gitk:6378
-msgid "Error getting merge diffs:"
-msgstr "Errore nella lettura delle differenze di fusione:"
+#: gitk:7522
+#, tcl-format
+msgid "Error getting diffs: %s"
+msgstr "Errore nella lettura delle differenze:"
 
-#: gitk:7113
+#: gitk:8108
 msgid "Goto:"
 msgstr "Vai a:"
 
-#: gitk:7115
-msgid "SHA1 ID:"
-msgstr "SHA1 ID:"
-
-#: gitk:7134
+#: gitk:8129
 #, tcl-format
 msgid "Short SHA1 id %s is ambiguous"
 msgstr "La SHA1 id abbreviata %s è ambigua"
 
-#: gitk:7146
+#: gitk:8136
+#, tcl-format
+msgid "Revision %s is not known"
+msgstr "La revisione %s è sconosciuta"
+
+#: gitk:8146
 #, tcl-format
 msgid "SHA1 id %s is not known"
 msgstr "La SHA1 id %s è sconosciuta"
 
-#: gitk:7148
+#: gitk:8148
 #, tcl-format
-msgid "Tag/Head %s is not known"
-msgstr "L'etichetta/ramo %s è sconosciuto"
+msgid "Revision %s is not in the current view"
+msgstr "La revisione %s non è presente nella vista attuale"
 
-#: gitk:7290
+#: gitk:8290
 msgid "Children"
 msgstr "Figli"
 
-#: gitk:7347
+#: gitk:8348
 #, tcl-format
 msgid "Reset %s branch to here"
 msgstr "Aggiorna il ramo %s a questa revisione"
 
-#: gitk:7349
+#: gitk:8350
 msgid "Detached head: can't reset"
-msgstr ""
+msgstr "Nessun ramo attivo: reset impossibile"
 
-#: gitk:7381
+#: gitk:8459 gitk:8465
+msgid "Skipping merge commit "
+msgstr "Salto la revisione di fusione "
+
+#: gitk:8474 gitk:8479
+msgid "Error getting patch ID for "
+msgstr "Errore nella identificazione della patch per "
+
+#: gitk:8475 gitk:8480
+msgid " - stopping\n"
+msgstr " - fine\n"
+
+#: gitk:8485 gitk:8488 gitk:8496 gitk:8510 gitk:8519
+msgid "Commit "
+msgstr "La revisione "
+
+#: gitk:8489
+msgid ""
+" is the same patch as\n"
+"       "
+msgstr ""
+" ha le stesse differenze di\n"
+"       "
+
+#: gitk:8497
+msgid ""
+" differs from\n"
+"       "
+msgstr ""
+" è diversa da\n"
+"       "
+
+#: gitk:8499
+msgid ""
+"Diff of commits:\n"
+"\n"
+msgstr ""
+"Differenze tra le revisioni:\n"
+"\n"
+
+#: gitk:8511 gitk:8520
+#, tcl-format
+msgid " has %s children - stopping\n"
+msgstr " ha %s figli - fine\n"
+
+#: gitk:8539
+#, tcl-format
+msgid "Error writing commit to file: %s"
+msgstr "Errore nella scrittura della revisione nel file: %s"
+
+#: gitk:8545
+#, tcl-format
+msgid "Error diffing commits: %s"
+msgstr "Errore nelle differenze tra le revisioni: %s"
+
+#: gitk:8575
 msgid "Top"
 msgstr "Inizio"
 
-#: gitk:7382
+#: gitk:8576
 msgid "From"
 msgstr "Da"
 
-#: gitk:7387
+#: gitk:8581
 msgid "To"
 msgstr "A"
 
-#: gitk:7410
+#: gitk:8605
 msgid "Generate patch"
 msgstr "Genera patch"
 
-#: gitk:7412
+#: gitk:8607
 msgid "From:"
 msgstr "Da:"
 
-#: gitk:7421
+#: gitk:8616
 msgid "To:"
 msgstr "A:"
 
-#: gitk:7430
+#: gitk:8625
 msgid "Reverse"
 msgstr "Inverti"
 
-#: gitk:7432 gitk:7607
+#: gitk:8627 gitk:8822
 msgid "Output file:"
 msgstr "Scrivi sul file:"
 
-#: gitk:7438
+#: gitk:8633
 msgid "Generate"
 msgstr "Genera"
 
-#: gitk:7474
+#: gitk:8671
 msgid "Error creating patch:"
 msgstr "Errore nella creazione della patch:"
 
-#: gitk:7496 gitk:7595 gitk:7649
+#: gitk:8694 gitk:8810 gitk:8867
 msgid "ID:"
 msgstr "ID:"
 
-#: gitk:7505
+#: gitk:8703
 msgid "Tag name:"
 msgstr "Nome etichetta:"
 
-#: gitk:7509 gitk:7659
+#: gitk:8706
+msgid "Tag message is optional"
+msgstr "Il messaggio dell'etichetta è opzionale"
+
+#: gitk:8708
+msgid "Tag message:"
+msgstr "Messaggio dell'etichetta:"
+
+#: gitk:8712 gitk:8876
 msgid "Create"
 msgstr "Crea"
 
-#: gitk:7524
+#: gitk:8730
 msgid "No tag name specified"
 msgstr "Nessuna etichetta specificata"
 
-#: gitk:7528
+#: gitk:8734
 #, tcl-format
 msgid "Tag \"%s\" already exists"
 msgstr "L'etichetta \"%s\" esiste già"
 
-#: gitk:7534
+#: gitk:8744
 msgid "Error creating tag:"
 msgstr "Errore nella creazione dell'etichetta:"
 
-#: gitk:7604
+#: gitk:8819
 msgid "Command:"
 msgstr "Comando:"
 
-#: gitk:7612
+#: gitk:8827
 msgid "Write"
 msgstr "Scrivi"
 
-#: gitk:7628
+#: gitk:8845
 msgid "Error writing commit:"
 msgstr "Errore nella scrittura della revisione:"
 
-#: gitk:7654
+#: gitk:8872
 msgid "Name:"
 msgstr "Nome:"
 
-#: gitk:7674
+#: gitk:8895
 msgid "Please specify a name for the new branch"
 msgstr "Specificare un nome per il nuovo ramo"
 
-#: gitk:7703
+#: gitk:8900
+#, tcl-format
+msgid "Branch '%s' already exists. Overwrite?"
+msgstr "Il ramo '%s' esiste già. Sovrascrivere?"
+
+#: gitk:8966
 #, tcl-format
 msgid "Commit %s is already included in branch %s -- really re-apply it?"
 msgstr "La revisione %s è già inclusa nel ramo %s -- applicarla di nuovo?"
 
-#: gitk:7708
+#: gitk:8971
 msgid "Cherry-picking"
 msgstr ""
 
-#: gitk:7720
+#: gitk:8980
+#, tcl-format
+msgid ""
+"Cherry-pick failed because of local changes to file '%s'.\n"
+"Please commit, reset or stash your changes and try again."
+msgstr ""
+"Impossibile eseguire cherry-pick perché il file '%s' è stato modificato "
+"nella directory di lavoro.\n"
+"Prima di riprovare, bisogna creare una nuova revisione, annullare le "
+"modifiche o usare 'git stash'."
+
+#: gitk:8986
+msgid ""
+"Cherry-pick failed because of merge conflict.\n"
+"Do you wish to run git citool to resolve it?"
+msgstr ""
+"Impossibile eseguire cherry-pick a causa di un conflitto nella fusione.\n"
+"Vuoi avviare git citool per risolverlo?"
+
+#: gitk:9002
 msgid "No changes committed"
 msgstr "Nessuna modifica archiviata"
 
-#: gitk:7745
+#: gitk:9028
 msgid "Confirm reset"
 msgstr "Conferma git reset"
 
-#: gitk:7747
+#: gitk:9030
 #, tcl-format
 msgid "Reset branch %s to %s?"
 msgstr "Aggiornare il ramo %s a %s?"
 
-#: gitk:7751
+#: gitk:9032
 msgid "Reset type:"
 msgstr "Tipo di aggiornamento:"
 
-#: gitk:7755
+#: gitk:9035
 msgid "Soft: Leave working tree and index untouched"
 msgstr "Soft: Lascia la direcory di lavoro e l'indice come sono"
 
-#: gitk:7758
+#: gitk:9038
 msgid "Mixed: Leave working tree untouched, reset index"
 msgstr "Mixed: Lascia la directory di lavoro come è, aggiorna l'indice"
 
-#: gitk:7761
+#: gitk:9041
 msgid ""
 "Hard: Reset working tree and index\n"
 "(discard ALL local changes)"
@@ -723,19 +1037,19 @@
 "Hard: Aggiorna la directory di lavoro e l'indice\n"
 "(abbandona TUTTE le modifiche locali)"
 
-#: gitk:7777
+#: gitk:9058
 msgid "Resetting"
 msgstr "git reset in corso"
 
-#: gitk:7834
+#: gitk:9118
 msgid "Checking out"
 msgstr "Attivazione in corso"
 
-#: gitk:7885
+#: gitk:9171
 msgid "Cannot delete the currently checked-out branch"
 msgstr "Impossibile cancellare il ramo attualmente attivo"
 
-#: gitk:7891
+#: gitk:9177
 #, tcl-format
 msgid ""
 "The commits on branch %s aren't on any other branch.\n"
@@ -744,16 +1058,16 @@
 "Le revisioni nel ramo %s non sono presenti su altri rami.\n"
 "Cancellare il ramo %s?"
 
-#: gitk:7922
+#: gitk:9208
 #, tcl-format
 msgid "Tags and heads: %s"
 msgstr "Etichette e rami: %s"
 
-#: gitk:7936
+#: gitk:9223
 msgid "Filter"
 msgstr "Filtro"
 
-#: gitk:8230
+#: gitk:9518
 msgid ""
 "Error reading commit topology information; branch and preceding/following "
 "tag information will be incomplete."
@@ -761,154 +1075,202 @@
 "Errore nella lettura della topologia delle revisioni: le informazioni sul "
 "ramo e le etichette precedenti e seguenti saranno incomplete."
 
-#: gitk:9216
+#: gitk:10504
 msgid "Tag"
 msgstr "Etichetta"
 
-#: gitk:9216
+#: gitk:10504
 msgid "Id"
 msgstr "Id"
 
-#: gitk:9262
+#: gitk:10554
 msgid "Gitk font chooser"
 msgstr "Scelta caratteri gitk"
 
-#: gitk:9279
+#: gitk:10571
 msgid "B"
 msgstr "B"
 
-#: gitk:9282
+#: gitk:10574
 msgid "I"
 msgstr "I"
 
-#: gitk:9375
+#: gitk:10692
 msgid "Gitk preferences"
 msgstr "Preferenze gitk"
 
-#: gitk:9376
+#: gitk:10694
 msgid "Commit list display options"
 msgstr "Opzioni visualizzazione dell'elenco revisioni"
 
-#: gitk:9379
+#: gitk:10697
 msgid "Maximum graph width (lines)"
 msgstr "Larghezza massima del grafico (in linee)"
 
-#: gitk:9383
+#: gitk:10700
 #, tcl-format
 msgid "Maximum graph width (% of pane)"
 msgstr "Larghezza massima del grafico (% del pannello)"
 
-#: gitk:9388
+#: gitk:10703
 msgid "Show local changes"
 msgstr "Mostra modifiche locali"
 
-#: gitk:9393
+#: gitk:10706
 msgid "Auto-select SHA1"
 msgstr "Seleziona automaticamente SHA1 hash"
 
-#: gitk:9398
+#: gitk:10709
+msgid "Hide remote refs"
+msgstr "Nascondi i riferimenti remoti"
+
+#: gitk:10713
 msgid "Diff display options"
 msgstr "Opzioni di visualizzazione delle differenze"
 
-#: gitk:9400
+#: gitk:10715
 msgid "Tab spacing"
 msgstr "Spaziatura tabulazioni"
 
-#: gitk:9404
+#: gitk:10718
 msgid "Display nearby tags"
 msgstr "Mostra etichette vicine"
 
-#: gitk:9409
+#: gitk:10721
 msgid "Limit diffs to listed paths"
 msgstr "Limita le differenze ai percorsi elencati"
 
-#: gitk:9414
+#: gitk:10724
 msgid "Support per-file encodings"
-msgstr ""
+msgstr "Attiva codifica file per file"
 
-#: gitk:9421
+#: gitk:10730 gitk:10819
 msgid "External diff tool"
-msgstr ""
+msgstr "Visualizzatore di differenze"
 
-#: gitk:9423
+#: gitk:10731
 msgid "Choose..."
-msgstr ""
+msgstr "Scegli..."
 
-#: gitk:9428
+#: gitk:10736
+msgid "General options"
+msgstr "Opzioni generali"
+
+#: gitk:10739
+msgid "Use themed widgets"
+msgstr "Utilizza interfaccia a tema"
+
+#: gitk:10741
+msgid "(change requires restart)"
+msgstr "(una modifica richiede il riavvio)"
+
+#: gitk:10743
+msgid "(currently unavailable)"
+msgstr "(momentaneamente non disponibile)"
+
+#: gitk:10747
 msgid "Colors: press to choose"
 msgstr "Colori: premere per scegliere"
 
-#: gitk:9431
+#: gitk:10750
+msgid "Interface"
+msgstr "Interfaccia"
+
+#: gitk:10751
+msgid "interface"
+msgstr "interfaccia"
+
+#: gitk:10754
 msgid "Background"
 msgstr "Sfondo"
 
-#: gitk:9435
+#: gitk:10755 gitk:10785
+msgid "background"
+msgstr "sfondo"
+
+#: gitk:10758
 msgid "Foreground"
 msgstr "Primo piano"
 
-#: gitk:9439
+#: gitk:10759
+msgid "foreground"
+msgstr "primo piano"
+
+#: gitk:10762
 msgid "Diff: old lines"
 msgstr "Diff: vecchie linee"
 
-#: gitk:9444
+#: gitk:10763
+msgid "diff old lines"
+msgstr "vecchie linee"
+
+#: gitk:10767
 msgid "Diff: new lines"
 msgstr "Diff: nuove linee"
 
-#: gitk:9449
+#: gitk:10768
+msgid "diff new lines"
+msgstr "nuove linee"
+
+#: gitk:10772
 msgid "Diff: hunk header"
 msgstr "Diff: intestazione della sezione"
 
-#: gitk:9455
-msgid "Select bg"
-msgstr "Sfondo selezione"
+#: gitk:10774
+msgid "diff hunk header"
+msgstr "intestazione della sezione"
 
-#: gitk:9459
+#: gitk:10778
+msgid "Marked line bg"
+msgstr "Sfondo riga selezionata"
+
+#: gitk:10780
+msgid "marked line background"
+msgstr "sfondo riga selezionata"
+
+#: gitk:10784
+msgid "Select bg"
+msgstr "Sfondo"
+
+#: gitk:10788
 msgid "Fonts: press to choose"
 msgstr "Carattere: premere per scegliere"
 
-#: gitk:9461
+#: gitk:10790
 msgid "Main font"
 msgstr "Carattere principale"
 
-#: gitk:9462
+#: gitk:10791
 msgid "Diff display font"
 msgstr "Carattere per differenze"
 
-#: gitk:9463
+#: gitk:10792
 msgid "User interface font"
 msgstr "Carattere per interfaccia utente"
 
-#: gitk:9488
+#: gitk:10829
 #, tcl-format
 msgid "Gitk: choose color for %s"
 msgstr "Gitk: scegliere un colore per %s"
 
-#: gitk:9934
-msgid ""
-"Sorry, gitk cannot run with this version of Tcl/Tk.\n"
-" Gitk requires at least Tcl/Tk 8.4."
-msgstr ""
-"Questa versione di Tcl/Tk non può avviare gitk.\n"
-" Gitk richiede Tcl/Tk versione 8.4 o superiore."
-
-#: gitk:10047
+#: gitk:11433
 msgid "Cannot find a git repository here."
 msgstr "Archivio git non trovato."
 
-#: gitk:10051
+#: gitk:11437
 #, tcl-format
 msgid "Cannot find the git directory \"%s\"."
 msgstr "Directory git \"%s\" non trovata."
 
-#: gitk:10098
+#: gitk:11484
 #, tcl-format
 msgid "Ambiguous argument '%s': both revision and filename"
 msgstr "Argomento ambiguo: '%s' è sia revisione che nome di file"
 
-#: gitk:10110
+#: gitk:11496
 msgid "Bad arguments to gitk:"
 msgstr "Gitk: argomenti errati:"
 
-#: gitk:10170
+#: gitk:11587
 msgid "Command line"
 msgstr "Linea di comando"
diff --git a/gitk-git/po/ja.po b/gitk-git/po/ja.po
new file mode 100644
index 0000000..4f47051
--- /dev/null
+++ b/gitk-git/po/ja.po
@@ -0,0 +1,1255 @@
+# Japanese translations for gitk package.
+# Copyright (C) 2005-2009 Paul Mackerras
+# This file is distributed under the same license as the gitk package.
+#
+# Mizar <mizar.jp@gmail.com>, 2009.
+# Junio C Hamano <gitster@pobox.com>, 2009.
+msgid ""
+msgstr ""
+"Project-Id-Version: gitk\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2009-11-04 00:08+0900\n"
+"PO-Revision-Date: 2009-11-06 01:45+0900\n"
+"Last-Translator: Mizar <mizar.jp@gmail.com>\n"
+"Language-Team: Japanese\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+#: gitk:113
+msgid "Couldn't get list of unmerged files:"
+msgstr "マージされていないファイルのリストを取得できません:"
+
+#: gitk:269
+msgid "Error parsing revisions:"
+msgstr "リビジョン解析エラー:"
+
+#: gitk:324
+msgid "Error executing --argscmd command:"
+msgstr "--argscmd コマンド実行エラー:"
+
+#: gitk:337
+msgid "No files selected: --merge specified but no files are unmerged."
+msgstr ""
+"ファイル未選択: --merge が指定されましたが、マージされていないファイルはあり"
+"ません。"
+
+#: gitk:340
+msgid ""
+"No files selected: --merge specified but no unmerged files are within file "
+"limit."
+msgstr ""
+"ファイル未選択: --merge が指定されましたが、ファイル制限内にマージされていな"
+"いファイルはありません。"
+
+#: gitk:362 gitk:509
+msgid "Error executing git log:"
+msgstr "git log 実行エラー:"
+
+#: gitk:380 gitk:525
+msgid "Reading"
+msgstr "読み込み中"
+
+#: gitk:440 gitk:4132
+msgid "Reading commits..."
+msgstr "コミット読み込み中..."
+
+#: gitk:443 gitk:1561 gitk:4135
+msgid "No commits selected"
+msgstr "コミットが選択されていません"
+
+#: gitk:1437
+msgid "Can't parse git log output:"
+msgstr "git log の出力を解析できません:"
+
+#: gitk:1657
+msgid "No commit information available"
+msgstr "有効なコミットの情報がありません"
+
+#: gitk:1790
+msgid "mc"
+msgstr "mc"
+
+#: gitk:1817 gitk:3925 gitk:8842 gitk:10378 gitk:10558
+msgid "OK"
+msgstr "OK"
+
+#: gitk:1819 gitk:3927 gitk:8439 gitk:8513 gitk:8623 gitk:8672 gitk:8844
+#: gitk:10379 gitk:10559
+msgid "Cancel"
+msgstr "キャンセル"
+
+#: gitk:1919
+msgid "Update"
+msgstr "更新"
+
+#: gitk:1920
+msgid "Reload"
+msgstr "リロード"
+
+#: gitk:1921
+msgid "Reread references"
+msgstr "リファレンスを再読み込み"
+
+#: gitk:1922
+msgid "List references"
+msgstr "リファレンスリストを表示"
+
+#: gitk:1924
+msgid "Start git gui"
+msgstr "git gui の開始"
+
+#: gitk:1926
+msgid "Quit"
+msgstr "終了"
+
+#: gitk:1918
+msgid "File"
+msgstr "ファイル"
+
+#: gitk:1930
+msgid "Preferences"
+msgstr "設定"
+
+#: gitk:1929
+msgid "Edit"
+msgstr "編集"
+
+#: gitk:1934
+msgid "New view..."
+msgstr "新規ビュー..."
+
+#: gitk:1935
+msgid "Edit view..."
+msgstr "ビュー編集..."
+
+#: gitk:1936
+msgid "Delete view"
+msgstr "ビュー削除"
+
+#: gitk:1938
+msgid "All files"
+msgstr "全てのファイル"
+
+#: gitk:1933 gitk:3679
+msgid "View"
+msgstr "ビュー"
+
+#: gitk:1943 gitk:1953 gitk:2656
+msgid "About gitk"
+msgstr "gitk について"
+
+#: gitk:1944 gitk:1958
+msgid "Key bindings"
+msgstr "キーバインディング"
+
+#: gitk:1942 gitk:1957
+msgid "Help"
+msgstr "ヘルプ"
+
+#: gitk:2018
+msgid "SHA1 ID: "
+msgstr "SHA1 ID: "
+
+#: gitk:2049
+msgid "Row"
+msgstr "行"
+
+#: gitk:2080
+msgid "Find"
+msgstr "検索"
+
+#: gitk:2081
+msgid "next"
+msgstr "次"
+
+#: gitk:2082
+msgid "prev"
+msgstr "前"
+
+#: gitk:2083
+msgid "commit"
+msgstr "コミット"
+
+#: gitk:2086 gitk:2088 gitk:4293 gitk:4316 gitk:4340 gitk:6281 gitk:6353
+#: gitk:6437
+msgid "containing:"
+msgstr "含む:"
+
+#: gitk:2089 gitk:3164 gitk:3169 gitk:4368
+msgid "touching paths:"
+msgstr "パスの一部:"
+
+#: gitk:2090 gitk:4373
+msgid "adding/removing string:"
+msgstr "追加/除去する文字列:"
+
+#: gitk:2099 gitk:2101
+msgid "Exact"
+msgstr "英字の大小を区別する"
+
+#: gitk:2101 gitk:4448 gitk:6249
+msgid "IgnCase"
+msgstr "英字の大小を区別しない"
+
+#: gitk:2101 gitk:4342 gitk:4446 gitk:6245
+msgid "Regexp"
+msgstr "正規表現"
+
+#: gitk:2103 gitk:2104 gitk:4467 gitk:4497 gitk:4504 gitk:6373 gitk:6441
+msgid "All fields"
+msgstr "全ての項目"
+
+#: gitk:2104 gitk:4465 gitk:4497 gitk:6312
+msgid "Headline"
+msgstr "ヘッドライン"
+
+#: gitk:2105 gitk:4465 gitk:6312 gitk:6441 gitk:6875
+msgid "Comments"
+msgstr "コメント"
+
+#: gitk:2105 gitk:4465 gitk:4469 gitk:4504 gitk:6312 gitk:6810 gitk:8091
+#: gitk:8106
+msgid "Author"
+msgstr "作者"
+
+#: gitk:2105 gitk:4465 gitk:6312 gitk:6812
+msgid "Committer"
+msgstr "コミット者"
+
+#: gitk:2134
+msgid "Search"
+msgstr "検索"
+
+#: gitk:2141
+msgid "Diff"
+msgstr "Diff"
+
+#: gitk:2143
+msgid "Old version"
+msgstr "旧バージョン"
+
+#: gitk:2145
+msgid "New version"
+msgstr "新バージョン"
+
+#: gitk:2147
+msgid "Lines of context"
+msgstr "文脈行数"
+
+#: gitk:2157
+msgid "Ignore space change"
+msgstr "空白の違いを無視"
+
+#: gitk:2215
+msgid "Patch"
+msgstr "パッチ"
+
+#: gitk:2217
+msgid "Tree"
+msgstr "ツリー"
+
+#: gitk:2361 gitk:2378
+msgid "Diff this -> selected"
+msgstr "これと選択したコミットのdiffを見る"
+
+#: gitk:2362 gitk:2379
+msgid "Diff selected -> this"
+msgstr "選択したコミットとこれのdiffを見る"
+
+#: gitk:2363 gitk:2380
+msgid "Make patch"
+msgstr "パッチ作成"
+
+#: gitk:2364 gitk:8497
+msgid "Create tag"
+msgstr "タグ生成"
+
+#: gitk:2365 gitk:8603
+msgid "Write commit to file"
+msgstr "コミットをファイルに書き出す"
+
+#: gitk:2366 gitk:8660
+msgid "Create new branch"
+msgstr "新規ブランチ生成"
+
+#: gitk:2367
+msgid "Cherry-pick this commit"
+msgstr "このコミットをチェリーピックする"
+
+#: gitk:2368
+msgid "Reset HEAD branch to here"
+msgstr "ブランチのHEADをここにリセットする"
+
+#: gitk:2369
+msgid "Mark this commit"
+msgstr "このコミットにマークをつける"
+
+#: gitk:2370
+msgid "Return to mark"
+msgstr "マークを付けた所に戻る"
+
+#: gitk:2371
+msgid "Find descendant of this and mark"
+msgstr "これとマークをつけた所との子孫を見つける"
+
+#: gitk:2372
+msgid "Compare with marked commit"
+msgstr "マークを付けたコミットと比較する"
+
+#: gitk:2386
+msgid "Check out this branch"
+msgstr "このブランチをチェックアウトする"
+
+#: gitk:2387
+msgid "Remove this branch"
+msgstr "このブランチを除去する"
+
+#: gitk:2394
+msgid "Highlight this too"
+msgstr "これもハイライトさせる"
+
+#: gitk:2395
+msgid "Highlight this only"
+msgstr "これだけをハイライトさせる"
+
+#: gitk:2396
+msgid "External diff"
+msgstr "外部diffツール"
+
+#: gitk:2397
+msgid "Blame parent commit"
+msgstr "親コミットから blame をかける"
+
+#: gitk:2404
+msgid "Show origin of this line"
+msgstr "この行の出自を表示する"
+
+#: gitk:2405
+msgid "Run git gui blame on this line"
+msgstr "この行に git gui で blame をかける"
+
+#: gitk:2658
+msgid ""
+"\n"
+"Gitk - a commit viewer for git\n"
+"\n"
+"Copyright \\u00a9 2005-2010 Paul Mackerras\n"
+"\n"
+"Use and redistribute under the terms of the GNU General Public License"
+msgstr ""
+"\n"
+"Gitk - gitコミットビューア\n"
+"\n"
+"Copyright \\u00a9 2005-2010 Paul Mackerras\n"
+"\n"
+"使用および再配布は GNU General Public License に従ってください"
+
+#: gitk:2666 gitk:2728 gitk:9025
+msgid "Close"
+msgstr "閉じる"
+
+#: gitk:2685
+msgid "Gitk key bindings"
+msgstr "Gitk キーバインディング"
+
+#: gitk:2688
+msgid "Gitk key bindings:"
+msgstr "Gitk キーバインディング:"
+
+#: gitk:2690
+#, tcl-format
+msgid "<%s-Q>\t\tQuit"
+msgstr "<%s-Q>\t\t終了"
+
+#: gitk:2691
+msgid "<Home>\t\tMove to first commit"
+msgstr "<Home>\t\t最初のコミットに移動"
+
+#: gitk:2692
+msgid "<End>\t\tMove to last commit"
+msgstr "<End>\t\t最後のコミットに移動"
+
+#: gitk:2693
+msgid "<Up>, p, i\tMove up one commit"
+msgstr "<Up>, p, i\t一つ上のコミットに移動"
+
+#: gitk:2694
+msgid "<Down>, n, k\tMove down one commit"
+msgstr "<Down>, n, k\t一つ下のコミットに移動"
+
+#: gitk:2695
+msgid "<Left>, z, j\tGo back in history list"
+msgstr "<Left>, z, j\t履歴の前に戻る"
+
+#: gitk:2696
+msgid "<Right>, x, l\tGo forward in history list"
+msgstr "<Right>, x, l\t履歴の次へ進む"
+
+#: gitk:2697
+msgid "<PageUp>\tMove up one page in commit list"
+msgstr "<PageUp>\tコミットリストの一つ上のページに移動"
+
+#: gitk:2698
+msgid "<PageDown>\tMove down one page in commit list"
+msgstr "<PageDown>\tコミットリストの一つ下のページに移動"
+
+#: gitk:2699
+#, tcl-format
+msgid "<%s-Home>\tScroll to top of commit list"
+msgstr "<%s-Home>\tコミットリストの一番上にスクロールする"
+
+#: gitk:2700
+#, tcl-format
+msgid "<%s-End>\tScroll to bottom of commit list"
+msgstr "<%s-End>\tコミットリストの一番下にスクロールする"
+
+#: gitk:2701
+#, tcl-format
+msgid "<%s-Up>\tScroll commit list up one line"
+msgstr "<%s-Up>\tコミットリストの一つ下の行にスクロールする"
+
+#: gitk:2702
+#, tcl-format
+msgid "<%s-Down>\tScroll commit list down one line"
+msgstr "<%s-Down>\tコミットリストの一つ下の行にスクロールする"
+
+#: gitk:2703
+#, tcl-format
+msgid "<%s-PageUp>\tScroll commit list up one page"
+msgstr "<%s-PageUp>\tコミットリストの上のページにスクロールする"
+
+#: gitk:2704
+#, tcl-format
+msgid "<%s-PageDown>\tScroll commit list down one page"
+msgstr "<%s-PageDown>\tコミットリストの下のページにスクロールする"
+
+#: gitk:2705
+msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
+msgstr "<Shift-Up>\t後方を検索 (上方の・新しいコミット)"
+
+#: gitk:2706
+msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
+msgstr "<Shift-Down>\t前方を検索(下方の・古いコミット)"
+
+#: gitk:2707
+msgid "<Delete>, b\tScroll diff view up one page"
+msgstr "<Delete>, b\tdiff画面を上のページにスクロールする"
+
+#: gitk:2708
+msgid "<Backspace>\tScroll diff view up one page"
+msgstr "<Backspace>\tdiff画面を上のページにスクロールする"
+
+#: gitk:2709
+msgid "<Space>\t\tScroll diff view down one page"
+msgstr "<Space>\t\tdiff画面を下のページにスクロールする"
+
+#: gitk:2710
+msgid "u\t\tScroll diff view up 18 lines"
+msgstr "u\t\tdiff画面を上に18行スクロールする"
+
+#: gitk:2711
+msgid "d\t\tScroll diff view down 18 lines"
+msgstr "d\t\tdiff画面を下に18行スクロールする"
+
+#: gitk:2712
+#, tcl-format
+msgid "<%s-F>\t\tFind"
+msgstr "<%s-F>\t\t検索"
+
+#: gitk:2713
+#, tcl-format
+msgid "<%s-G>\t\tMove to next find hit"
+msgstr "<%s-G>\t\t次を検索して移動"
+
+#: gitk:2714
+msgid "<Return>\tMove to next find hit"
+msgstr "<Return>\t次を検索して移動"
+
+#: gitk:2715
+msgid "/\t\tFocus the search box"
+msgstr "/\t\t検索ボックスにフォーカス"
+
+#: gitk:2716
+msgid "?\t\tMove to previous find hit"
+msgstr "?\t\t前を検索して移動"
+
+#: gitk:2717
+msgid "f\t\tScroll diff view to next file"
+msgstr "f\t\t次のファイルにdiff画面をスクロールする"
+
+#: gitk:2718
+#, tcl-format
+msgid "<%s-S>\t\tSearch for next hit in diff view"
+msgstr "<%s-S>\t\tdiff画面の次を検索"
+
+#: gitk:2719
+#, tcl-format
+msgid "<%s-R>\t\tSearch for previous hit in diff view"
+msgstr "<%s-R>\t\tdiff画面の前を検索"
+
+#: gitk:2720
+#, tcl-format
+msgid "<%s-KP+>\tIncrease font size"
+msgstr "<%s-KP+>\t文字サイズを拡大"
+
+#: gitk:2721
+#, tcl-format
+msgid "<%s-plus>\tIncrease font size"
+msgstr "<%s-plus>\t文字サイズを拡大"
+
+#: gitk:2722
+#, tcl-format
+msgid "<%s-KP->\tDecrease font size"
+msgstr "<%s-KP->\t文字サイズを縮小"
+
+#: gitk:2723
+#, tcl-format
+msgid "<%s-minus>\tDecrease font size"
+msgstr "<%s-minus>\t文字サイズを縮小"
+
+#: gitk:2724
+msgid "<F5>\t\tUpdate"
+msgstr "<F5>\t\t更新"
+
+#: gitk:3179 gitk:3188
+#, tcl-format
+msgid "Error creating temporary directory %s:"
+msgstr "一時ディレクトリ %s 生成時エラー:"
+
+#: gitk:3201
+#, tcl-format
+msgid "Error getting \"%s\" from %s:"
+msgstr "\"%s\" のエラーが %s に発生:"
+
+#: gitk:3264
+msgid "command failed:"
+msgstr "コマンド失敗:"
+
+#: gitk:3410
+msgid "No such commit"
+msgstr "そのようなコミットはありません"
+
+#: gitk:3424
+msgid "git gui blame: command failed:"
+msgstr "git gui blame: コマンド失敗:"
+
+#: gitk:3455
+#, tcl-format
+msgid "Couldn't read merge head: %s"
+msgstr "マージする HEAD を読み込めません: %s"
+
+#: gitk:3463
+#, tcl-format
+msgid "Error reading index: %s"
+msgstr "インデックス読み込みエラー: %s"
+
+#: gitk:3488
+#, tcl-format
+msgid "Couldn't start git blame: %s"
+msgstr "git blame を始められません: %s"
+
+#: gitk:3491 gitk:6280
+msgid "Searching"
+msgstr "検索中"
+
+#: gitk:3523
+#, tcl-format
+msgid "Error running git blame: %s"
+msgstr "git blame 実行エラー: %s"
+
+#: gitk:3551
+#, tcl-format
+msgid "That line comes from commit %s,  which is not in this view"
+msgstr "コミット %s に由来するその行は、このビューに表示されていません"
+
+#: gitk:3565
+msgid "External diff viewer failed:"
+msgstr "外部diffビューアが失敗:"
+
+#: gitk:3683
+msgid "Gitk view definition"
+msgstr "Gitk ビュー定義"
+
+#: gitk:3687
+msgid "Remember this view"
+msgstr "このビューを記憶する"
+
+#: gitk:3688
+msgid "References (space separated list):"
+msgstr "リファレンス(スペース区切りのリスト):"
+
+#: gitk:3689
+msgid "Branches & tags:"
+msgstr "ブランチ&タグ:"
+
+#: gitk:3690
+msgid "All refs"
+msgstr "全てのリファレンス"
+
+#: gitk:3691
+msgid "All (local) branches"
+msgstr "全ての(ローカルな)ブランチ"
+
+#: gitk:3692
+msgid "All tags"
+msgstr "全てのタグ"
+
+#: gitk:3693
+msgid "All remote-tracking branches"
+msgstr "全てのリモート追跡ブランチ"
+
+#: gitk:3694
+msgid "Commit Info (regular expressions):"
+msgstr "コミット情報(正規表現):"
+
+#: gitk:3695
+msgid "Author:"
+msgstr "作者:"
+
+#: gitk:3696
+msgid "Committer:"
+msgstr "コミット者:"
+
+#: gitk:3697
+msgid "Commit Message:"
+msgstr "コミットメッセージ:"
+
+#: gitk:3698
+msgid "Matches all Commit Info criteria"
+msgstr "コミット情報の全ての条件に一致"
+
+#: gitk:3699
+msgid "Changes to Files:"
+msgstr "変更したファイル:"
+
+#: gitk:3700
+msgid "Fixed String"
+msgstr "固定文字列"
+
+#: gitk:3701
+msgid "Regular Expression"
+msgstr "正規表現"
+
+#: gitk:3702
+msgid "Search string:"
+msgstr "検索文字列:"
+
+#: gitk:3703
+msgid ""
+"Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 "
+"15:27:38\"):"
+msgstr ""
+"コミット日時 (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 "
+"15:27:38\"):"
+
+#: gitk:3704
+msgid "Since:"
+msgstr "期間の始め:"
+
+#: gitk:3705
+msgid "Until:"
+msgstr "期間の終わり:"
+
+#: gitk:3706
+msgid "Limit and/or skip a number of revisions (positive integer):"
+msgstr "制限・省略するリビジョンの数(正の整数):"
+
+#: gitk:3707
+msgid "Number to show:"
+msgstr "表示する数:"
+
+#: gitk:3708
+msgid "Number to skip:"
+msgstr "省略する数:"
+
+#: gitk:3709
+msgid "Miscellaneous options:"
+msgstr "その他のオプション:"
+
+#: gitk:3710
+msgid "Strictly sort by date"
+msgstr "厳密に日付順で並び替え"
+
+#: gitk:3711
+msgid "Mark branch sides"
+msgstr "側枝マーク"
+
+#: gitk:3712
+msgid "Limit to first parent"
+msgstr "最初の親に制限"
+
+#: gitk:3713
+msgid "Simple history"
+msgstr "簡易な履歴"
+
+#: gitk:3714
+msgid "Additional arguments to git log:"
+msgstr "git log への追加の引数:"
+
+#: gitk:3715
+msgid "Enter files and directories to include, one per line:"
+msgstr "含まれるファイル・ディレクトリを一行ごとに入力:"
+
+#: gitk:3716
+msgid "Command to generate more commits to include:"
+msgstr "コミット追加コマンド:"
+
+#: gitk:3838
+msgid "Gitk: edit view"
+msgstr "Gitk: ビュー編集"
+
+#: gitk:3846
+msgid "-- criteria for selecting revisions"
+msgstr "― リビジョンの選択条件"
+
+#: gitk:3851
+msgid "View Name:"
+msgstr "ビュー名:"
+
+#: gitk:3926
+msgid "Apply (F5)"
+msgstr "適用 (F5)"
+
+#: gitk:3964
+msgid "Error in commit selection arguments:"
+msgstr "コミット選択引数のエラー:"
+
+#: gitk:4017 gitk:4069 gitk:4517 gitk:4531 gitk:5792 gitk:11263 gitk:11264
+msgid "None"
+msgstr "無し"
+
+#: gitk:4465 gitk:6312 gitk:8093 gitk:8108
+msgid "Date"
+msgstr "日付"
+
+#: gitk:4465 gitk:6312
+msgid "CDate"
+msgstr "作成日"
+
+#: gitk:4614 gitk:4619
+msgid "Descendant"
+msgstr "子孫"
+
+#: gitk:4615
+msgid "Not descendant"
+msgstr "非子孫"
+
+#: gitk:4622 gitk:4627
+msgid "Ancestor"
+msgstr "祖先"
+
+#: gitk:4623
+msgid "Not ancestor"
+msgstr "非祖先"
+
+#: gitk:4913
+msgid "Local changes checked in to index but not committed"
+msgstr "ステージされた、コミット前のローカルな変更"
+
+#: gitk:4949
+msgid "Local uncommitted changes, not checked in to index"
+msgstr "ステージされていない、コミット前のローカルな変更"
+
+#: gitk:6630
+msgid "many"
+msgstr "多数"
+
+#: gitk:6814
+msgid "Tags:"
+msgstr "タグ:"
+
+#: gitk:6831 gitk:6837 gitk:8086
+msgid "Parent"
+msgstr "親"
+
+#: gitk:6842
+msgid "Child"
+msgstr "子"
+
+#: gitk:6851
+msgid "Branch"
+msgstr "ブランチ"
+
+#: gitk:6854
+msgid "Follows"
+msgstr "下位"
+
+#: gitk:6857
+msgid "Precedes"
+msgstr "上位"
+
+#: gitk:7359
+#, tcl-format
+msgid "Error getting diffs: %s"
+msgstr "diff取得エラー: %s"
+
+#: gitk:7914
+msgid "Goto:"
+msgstr "Goto:"
+
+#: gitk:7916
+msgid "SHA1 ID:"
+msgstr "SHA1 ID:"
+
+#: gitk:7935
+#, tcl-format
+msgid "Short SHA1 id %s is ambiguous"
+msgstr "%s を含む SHA1 ID は複数存在します"
+
+#: gitk:7942
+#, tcl-format
+msgid "Revision %s is not known"
+msgstr "リビジョン %s は不明です"
+
+#: gitk:7952
+#, tcl-format
+msgid "SHA1 id %s is not known"
+msgstr "SHA1 id %s は不明です"
+
+#: gitk:7954
+#, tcl-format
+msgid "Revision %s is not in the current view"
+msgstr "リビジョン %s は現在のビューにはありません"
+
+#: gitk:8096
+msgid "Children"
+msgstr "子"
+
+#: gitk:8153
+#, tcl-format
+msgid "Reset %s branch to here"
+msgstr "%s ブランチをここにリセットする"
+
+#: gitk:8155
+msgid "Detached head: can't reset"
+msgstr "切り離されたHEAD: リセットできません"
+
+#: gitk:8264 gitk:8270
+msgid "Skipping merge commit "
+msgstr "コミットマージをスキップ: "
+
+#: gitk:8279 gitk:8284
+msgid "Error getting patch ID for "
+msgstr "パッチ取得エラー: ID "
+
+#: gitk:8280 gitk:8285
+msgid " - stopping\n"
+msgstr " - 停止\n"
+
+#: gitk:8290 gitk:8293 gitk:8301 gitk:8314 gitk:8323
+msgid "Commit "
+msgstr "コミット "
+
+#: gitk:8294
+msgid ""
+" is the same patch as\n"
+"       "
+msgstr ""
+" は下記のパッチと同等\n"
+"       "
+
+#: gitk:8302
+msgid ""
+" differs from\n"
+"       "
+msgstr ""
+" 下記からのdiff\n"
+"       "
+
+#: gitk:8304
+msgid ""
+"Diff of commits:\n"
+"\n"
+msgstr ""
+"コミットのdiff:\n"
+"\n"
+
+#: gitk:8315 gitk:8324
+#, tcl-format
+msgid " has %s children - stopping\n"
+msgstr " には %s の子があります - 停止\n"
+
+#: gitk:8344
+#, tcl-format
+msgid "Error writing commit to file: %s"
+msgstr "ファイルへのコミット書き出しエラー: %s"
+
+#: gitk:8350
+#, tcl-format
+msgid "Error diffing commits: %s"
+msgstr "コミットのdiff実行エラー: %s"
+
+#: gitk:8380
+msgid "Top"
+msgstr "Top"
+
+#: gitk:8381
+msgid "From"
+msgstr "From"
+
+#: gitk:8386
+msgid "To"
+msgstr "To"
+
+#: gitk:8410
+msgid "Generate patch"
+msgstr "パッチ生成"
+
+#: gitk:8412
+msgid "From:"
+msgstr "From:"
+
+#: gitk:8421
+msgid "To:"
+msgstr "To:"
+
+#: gitk:8430
+msgid "Reverse"
+msgstr "逆"
+
+#: gitk:8432 gitk:8617
+msgid "Output file:"
+msgstr "出力ファイル:"
+
+#: gitk:8438
+msgid "Generate"
+msgstr "生成"
+
+#: gitk:8476
+msgid "Error creating patch:"
+msgstr "パッチ生成エラー:"
+
+#: gitk:8499 gitk:8605 gitk:8662
+msgid "ID:"
+msgstr "ID:"
+
+#: gitk:8508
+msgid "Tag name:"
+msgstr "タグ名:"
+
+#: gitk:8512 gitk:8671
+msgid "Create"
+msgstr "生成"
+
+#: gitk:8529
+msgid "No tag name specified"
+msgstr "タグの名称が指定されていません"
+
+#: gitk:8533
+#, tcl-format
+msgid "Tag \"%s\" already exists"
+msgstr "タグ \"%s\" は既に存在します"
+
+#: gitk:8539
+msgid "Error creating tag:"
+msgstr "タグ生成エラー:"
+
+#: gitk:8614
+msgid "Command:"
+msgstr "コマンド:"
+
+#: gitk:8622
+msgid "Write"
+msgstr "書き出し"
+
+#: gitk:8640
+msgid "Error writing commit:"
+msgstr "コミット書き出しエラー:"
+
+#: gitk:8667
+msgid "Name:"
+msgstr "名前:"
+
+#: gitk:8690
+msgid "Please specify a name for the new branch"
+msgstr "新しいブランチの名前を指定してください"
+
+#: gitk:8695
+#, tcl-format
+msgid "Branch '%s' already exists. Overwrite?"
+msgstr "ブランチ '%s' は既に存在します。上書きしますか?"
+
+#: gitk:8761
+#, tcl-format
+msgid "Commit %s is already included in branch %s -- really re-apply it?"
+msgstr ""
+"コミット %s は既にブランチ %s に含まれています ― 本当にこれを再適用しますか?"
+
+#: gitk:8766
+msgid "Cherry-picking"
+msgstr "チェリーピック中"
+
+#: gitk:8775
+#, tcl-format
+msgid ""
+"Cherry-pick failed because of local changes to file '%s'.\n"
+"Please commit, reset or stash your changes and try again."
+msgstr ""
+"ファイル '%s' のローカルな変更のためにチェリーピックは失敗しました。\n"
+"あなたの変更に commit, reset, stash のいずれかを行ってからやり直してくださ"
+"い。"
+
+#: gitk:8781
+msgid ""
+"Cherry-pick failed because of merge conflict.\n"
+"Do you wish to run git citool to resolve it?"
+msgstr ""
+"マージの衝突によってチェリーピックは失敗しました。\n"
+"この解決のために git citool を実行したいですか?"
+
+#: gitk:8797
+msgid "No changes committed"
+msgstr "何の変更もコミットされていません"
+
+#: gitk:8823
+msgid "Confirm reset"
+msgstr "確認を取り消す"
+
+#: gitk:8825
+#, tcl-format
+msgid "Reset branch %s to %s?"
+msgstr "ブランチ %s を %s にリセットしますか?"
+
+#: gitk:8829
+msgid "Reset type:"
+msgstr "Reset タイプ:"
+
+#: gitk:8833
+msgid "Soft: Leave working tree and index untouched"
+msgstr "Soft: 作業ツリーもインデックスもそのままにする"
+
+#: gitk:8836
+msgid "Mixed: Leave working tree untouched, reset index"
+msgstr "Mixed: 作業ツリーをそのままにして、インデックスをリセット"
+
+#: gitk:8839
+msgid ""
+"Hard: Reset working tree and index\n"
+"(discard ALL local changes)"
+msgstr ""
+"Hard: 作業ツリーやインデックスをリセット\n"
+"(「全ての」ローカルな変更を破棄)"
+
+#: gitk:8856
+msgid "Resetting"
+msgstr "リセット中"
+
+#: gitk:8913
+msgid "Checking out"
+msgstr "チェックアウト"
+
+#: gitk:8966
+msgid "Cannot delete the currently checked-out branch"
+msgstr "現在チェックアウトされているブランチを削除することはできません"
+
+#: gitk:8972
+#, tcl-format
+msgid ""
+"The commits on branch %s aren't on any other branch.\n"
+"Really delete branch %s?"
+msgstr ""
+"ブランチ %s には他のブランチに存在しないコミットがあります。\n"
+"本当にブランチ %s を削除しますか?"
+
+#: gitk:9003
+#, tcl-format
+msgid "Tags and heads: %s"
+msgstr "タグとHEAD: %s"
+
+#: gitk:9018
+msgid "Filter"
+msgstr "フィルター"
+
+#: gitk:9313
+msgid ""
+"Error reading commit topology information; branch and preceding/following "
+"tag information will be incomplete."
+msgstr ""
+"コミット構造情報読み込みエラー; ブランチ及び上位/下位のタグ情報が不完全である"
+"ようです。"
+
+#: gitk:10299
+msgid "Tag"
+msgstr "タグ"
+
+#: gitk:10299
+msgid "Id"
+msgstr "ID"
+
+#: gitk:10347
+msgid "Gitk font chooser"
+msgstr "Gitk フォント選択"
+
+#: gitk:10364
+msgid "B"
+msgstr "B"
+
+#: gitk:10367
+msgid "I"
+msgstr "I"
+
+#: gitk:10463
+msgid "Gitk preferences"
+msgstr "Gitk 設定"
+
+#: gitk:10465
+msgid "Commit list display options"
+msgstr "コミットリスト表示オプション"
+
+#: gitk:10468
+msgid "Maximum graph width (lines)"
+msgstr "最大グラフ幅(線の本数)"
+
+#: gitk:10472
+#, tcl-format
+msgid "Maximum graph width (% of pane)"
+msgstr "最大グラフ幅(ペインに対する%)"
+
+#: gitk:10476
+msgid "Show local changes"
+msgstr "ローカルな変更を表示"
+
+#: gitk:10479
+msgid "Auto-select SHA1"
+msgstr "SHA1 の自動選択"
+
+#: gitk:10483
+msgid "Diff display options"
+msgstr "diff表示オプション"
+
+#: gitk:10485
+msgid "Tab spacing"
+msgstr "タブ空白幅"
+
+#: gitk:10488
+msgid "Display nearby tags"
+msgstr "近くのタグを表示する"
+
+#: gitk:10491
+msgid "Hide remote refs"
+msgstr "リモートリファレンスを隠す"
+
+#: gitk:10494
+msgid "Limit diffs to listed paths"
+msgstr "diff をリストのパスに制限"
+
+#: gitk:10497
+msgid "Support per-file encodings"
+msgstr "ファイルごとのエンコーディングのサポート"
+
+#: gitk:10503 gitk:10572
+msgid "External diff tool"
+msgstr "外部diffツール"
+
+#: gitk:10505
+msgid "Choose..."
+msgstr "選択..."
+
+#: gitk:10510
+msgid "Colors: press to choose"
+msgstr "色: ボタンを押して選択"
+
+#: gitk:10513
+msgid "Interface"
+msgstr "インターフェイス"
+
+#: gitk:10514
+msgid "interface"
+msgstr "インターフェイス"
+
+#: gitk:10517
+msgid "Background"
+msgstr "背景"
+
+#: gitk:10518 gitk:10548
+msgid "background"
+msgstr "背景"
+
+#: gitk:10521
+msgid "Foreground"
+msgstr "前景"
+
+#: gitk:10522
+msgid "foreground"
+msgstr "前景"
+
+#: gitk:10525
+msgid "Diff: old lines"
+msgstr "Diff: 旧バージョン"
+
+#: gitk:10526
+msgid "diff old lines"
+msgstr "diff 旧バージョン"
+
+#: gitk:10530
+msgid "Diff: new lines"
+msgstr "Diff: 新バージョン"
+
+#: gitk:10531
+msgid "diff new lines"
+msgstr "diff 新バージョン"
+
+#: gitk:10535
+msgid "Diff: hunk header"
+msgstr "Diff: hunkヘッダ"
+
+#: gitk:10537
+msgid "diff hunk header"
+msgstr "diff hunkヘッダ"
+
+#: gitk:10541
+msgid "Marked line bg"
+msgstr "マーク行の背景"
+
+#: gitk:10543
+msgid "marked line background"
+msgstr "マーク行の背景"
+
+#: gitk:10547
+msgid "Select bg"
+msgstr "選択の背景"
+
+#: gitk:10551
+msgid "Fonts: press to choose"
+msgstr "フォント: ボタンを押して選択"
+
+#: gitk:10553
+msgid "Main font"
+msgstr "主フォント"
+
+#: gitk:10554
+msgid "Diff display font"
+msgstr "Diff表示用フォント"
+
+#: gitk:10555
+msgid "User interface font"
+msgstr "UI用フォント"
+
+#: gitk:10582
+#, tcl-format
+msgid "Gitk: choose color for %s"
+msgstr "Gitk: 「%s」 の色を選択"
+
+#: gitk:11168
+msgid "Cannot find a git repository here."
+msgstr "ここにはgitリポジトリがありません。"
+
+#: gitk:11172
+#, tcl-format
+msgid "Cannot find the git directory \"%s\"."
+msgstr "gitディレクトリ \"%s\" を見つけられません。"
+
+#: gitk:11219
+#, tcl-format
+msgid "Ambiguous argument '%s': both revision and filename"
+msgstr "あいまいな引数 '%s': リビジョンとファイル名の両方に解釈できます"
+
+#: gitk:11231
+msgid "Bad arguments to gitk:"
+msgstr "gitkへの不正な引数:"
+
+#: gitk:11316
+msgid "Command line"
+msgstr "コマンド行"
diff --git a/gitk-git/po/pt_br.po b/gitk-git/po/pt_br.po
new file mode 100644
index 0000000..1486e32
--- /dev/null
+++ b/gitk-git/po/pt_br.po
@@ -0,0 +1,1277 @@
+# Translation of gitk to Brazilian Portuguese.
+# Copyright (C) 2007 Paul Mackerras, et al.
+# This file is distributed under the same license as the gitk package.
+#
+# Alexandre Erwin Ittner <alexandre@ittner.com.br>, 2010.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: gitk\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-01-26 15:47-0800\n"
+"PO-Revision-Date: 2010-12-06 23:39-0200\n"
+"Last-Translator: Alexandre Erwin Ittner <alexandre@ittner.com.br>\n"
+"Language-Team: Brazilian Portuguese <>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: gitk:115
+msgid "Couldn't get list of unmerged files:"
+msgstr "Não foi possível obter a lista dos arquivos não mesclados:"
+
+#: gitk:274
+msgid "Error parsing revisions:"
+msgstr "Erro ao interpretar revisões:"
+
+#: gitk:330
+msgid "Error executing --argscmd command:"
+msgstr "Erro ao executar o comando--argscmd:"
+
+#: gitk:343
+msgid "No files selected: --merge specified but no files are unmerged."
+msgstr ""
+"Nenhum arquivo foi selecionado: --merge especificado mas não há arquivos não-"
+"mesclados."
+
+#: gitk:346
+msgid ""
+"No files selected: --merge specified but no unmerged files are within file "
+"limit."
+msgstr ""
+"Nenhum arquivo foi selecionado: --merge especificado mas não há arquivos não-"
+"mesclados dentro dos limites."
+
+#: gitk:368 gitk:516
+msgid "Error executing git log:"
+msgstr "Erro ao executar git log:"
+
+#: gitk:386 gitk:532
+msgid "Reading"
+msgstr "Lendo"
+
+#: gitk:446 gitk:4271
+msgid "Reading commits..."
+msgstr "Lendo revisões..."
+
+#: gitk:449 gitk:1580 gitk:4274
+msgid "No commits selected"
+msgstr "Nenhuma revisão foi selecionada"
+
+#: gitk:1456
+msgid "Can't parse git log output:"
+msgstr "Não foi possível interpretar a saída do \"git log\":"
+
+#: gitk:1676
+msgid "No commit information available"
+msgstr "Não há informações disponíveis sobre a revisão"
+
+#: gitk:1818
+msgid "mc"
+msgstr "mc"
+
+#: gitk:1853 gitk:4064 gitk:9067 gitk:10607 gitk:10817
+msgid "OK"
+msgstr "Ok"
+
+#: gitk:1855 gitk:4066 gitk:8657 gitk:8736 gitk:8851 gitk:8900 gitk:9069
+#: gitk:10608 gitk:10818
+msgid "Cancel"
+msgstr "Cancelar"
+
+#: gitk:1980
+msgid "Update"
+msgstr "Atualizar"
+
+#: gitk:1981
+msgid "Reload"
+msgstr "Recarregar"
+
+#: gitk:1982
+msgid "Reread references"
+msgstr "Ler as referências novamente"
+
+#: gitk:1983
+msgid "List references"
+msgstr "Listar referências"
+
+#: gitk:1985
+msgid "Start git gui"
+msgstr "Iniciar Git GUI"
+
+#: gitk:1987
+msgid "Quit"
+msgstr "Sair"
+
+#: gitk:1979
+msgid "File"
+msgstr "Arquivo"
+
+#: gitk:1991
+msgid "Preferences"
+msgstr "Preferências"
+
+#: gitk:1990
+msgid "Edit"
+msgstr "Editar"
+
+#: gitk:1995
+msgid "New view..."
+msgstr "Nova vista..."
+
+#: gitk:1996
+msgid "Edit view..."
+msgstr "Editar vista..."
+
+#: gitk:1997
+msgid "Delete view"
+msgstr "Apagar vista"
+
+#: gitk:1999
+msgid "All files"
+msgstr "Todos os arquivos"
+
+#: gitk:1994 gitk:3817
+msgid "View"
+msgstr "Exibir"
+
+#: gitk:2004 gitk:2014 gitk:2787
+msgid "About gitk"
+msgstr "Sobre o gitk"
+
+#: gitk:2005 gitk:2019
+msgid "Key bindings"
+msgstr "Atalhos de teclado"
+
+#: gitk:2003 gitk:2018
+msgid "Help"
+msgstr "Ajuda"
+
+#: gitk:2096 gitk:8132
+msgid "SHA1 ID:"
+msgstr "SHA1 ID:"
+
+#: gitk:2127
+msgid "Row"
+msgstr "Linha"
+
+#: gitk:2165
+msgid "Find"
+msgstr "Encontrar"
+
+#: gitk:2166
+msgid "next"
+msgstr "Próximo"
+
+#: gitk:2167
+msgid "prev"
+msgstr "Anterior"
+
+#: gitk:2168
+msgid "commit"
+msgstr "Revisão"
+
+#: gitk:2171 gitk:2173 gitk:4432 gitk:4455 gitk:4479 gitk:6420 gitk:6492
+#: gitk:6576
+msgid "containing:"
+msgstr "contendo:"
+
+#: gitk:2174 gitk:3298 gitk:3303 gitk:4507
+msgid "touching paths:"
+msgstr "envolvendo os caminhos:"
+
+#: gitk:2175 gitk:4512
+msgid "adding/removing string:"
+msgstr "Adicionando/removendo texto:"
+
+#: gitk:2184 gitk:2186
+msgid "Exact"
+msgstr "Exatamente"
+
+#: gitk:2186 gitk:4587 gitk:6388
+msgid "IgnCase"
+msgstr "Ignorar maiúsculas/minúsculas"
+
+#: gitk:2186 gitk:4481 gitk:4585 gitk:6384
+msgid "Regexp"
+msgstr "Expressão regular"
+
+#: gitk:2188 gitk:2189 gitk:4606 gitk:4636 gitk:4643 gitk:6512 gitk:6580
+msgid "All fields"
+msgstr "Todos os campos"
+
+#: gitk:2189 gitk:4604 gitk:4636 gitk:6451
+msgid "Headline"
+msgstr "Assunto"
+
+#: gitk:2190 gitk:4604 gitk:6451 gitk:6580 gitk:7013
+msgid "Comments"
+msgstr "Descrição da revisão"
+
+#: gitk:2190 gitk:4604 gitk:4608 gitk:4643 gitk:6451 gitk:6948 gitk:8307
+#: gitk:8322
+msgid "Author"
+msgstr "Autor"
+
+#: gitk:2190 gitk:4604 gitk:6451 gitk:6950
+msgid "Committer"
+msgstr "Revisor"
+
+#: gitk:2221
+msgid "Search"
+msgstr "Buscar"
+
+#: gitk:2229
+msgid "Diff"
+msgstr "Diferenças"
+
+#: gitk:2231
+msgid "Old version"
+msgstr "Versão antiga"
+
+#: gitk:2233
+msgid "New version"
+msgstr "Versão nova"
+
+#: gitk:2235
+msgid "Lines of context"
+msgstr "Número de linhas de contexto"
+
+#: gitk:2245
+msgid "Ignore space change"
+msgstr "Ignorar mudanças de caixa"
+
+#: gitk:2304
+msgid "Patch"
+msgstr "Diferenças"
+
+#: gitk:2306
+msgid "Tree"
+msgstr "Árvore"
+
+#: gitk:2463 gitk:2480
+msgid "Diff this -> selected"
+msgstr "Comparar esta revisão com a selecionada"
+
+#: gitk:2464 gitk:2481
+msgid "Diff selected -> this"
+msgstr "Comparar a revisão selecionada com esta"
+
+#: gitk:2465 gitk:2482
+msgid "Make patch"
+msgstr "Criar patch"
+
+#: gitk:2466 gitk:8715
+msgid "Create tag"
+msgstr "Criar etiqueta"
+
+#: gitk:2467 gitk:8831
+msgid "Write commit to file"
+msgstr "Salvar revisão para um arquivo"
+
+#: gitk:2468 gitk:8888
+msgid "Create new branch"
+msgstr "Criar novo ramo"
+
+#: gitk:2469
+msgid "Cherry-pick this commit"
+msgstr "Fazer cherry-pick desta revisão"
+
+#: gitk:2470
+msgid "Reset HEAD branch to here"
+msgstr "Redefinir HEAD para cá"
+
+#: gitk:2471
+msgid "Mark this commit"
+msgstr "Marcar esta revisão"
+
+#: gitk:2472
+msgid "Return to mark"
+msgstr "Voltar à marca"
+
+#: gitk:2473
+msgid "Find descendant of this and mark"
+msgstr "Encontrar descendente e marcar"
+
+#: gitk:2474
+msgid "Compare with marked commit"
+msgstr "Comparar com a revisão marcada"
+
+#: gitk:2488
+msgid "Check out this branch"
+msgstr "Efetuar checkout deste ramo"
+
+#: gitk:2489
+msgid "Remove this branch"
+msgstr "Excluir este ramo"
+
+#: gitk:2496
+msgid "Highlight this too"
+msgstr "Marcar este também"
+
+#: gitk:2497
+msgid "Highlight this only"
+msgstr "Marcar apenas este"
+
+#: gitk:2498
+msgid "External diff"
+msgstr "Diff externo"
+
+#: gitk:2499
+msgid "Blame parent commit"
+msgstr "Anotar revisão anterior"
+
+#: gitk:2506
+msgid "Show origin of this line"
+msgstr "Exibir origem desta linha"
+
+#: gitk:2507
+msgid "Run git gui blame on this line"
+msgstr "Executar 'git blame' nesta linha"
+
+#: gitk:2789
+msgid "\n"
+"Gitk - a commit viewer for git\n"
+"\n"
+"Copyright ©9 2005-2010 Paul Mackerras\n"
+"\n"
+"Use and redistribute under the terms of the GNU General Public License"
+msgstr "\n"
+"Gitk - um visualizador de revisões para o git \n"
+"\n"
+"Copyright ©9 2005-2010 Paul Mackerras\n"
+"\n"
+"Uso e distribuição segundo os termos da Licença Pública Geral GNU"
+
+#: gitk:2797 gitk:2862 gitk:9253
+msgid "Close"
+msgstr "Fechar"
+
+#: gitk:2818
+msgid "Gitk key bindings"
+msgstr "Atalhos de teclado"
+
+#: gitk:2821
+msgid "Gitk key bindings:"
+msgstr "Atalhos de teclado:"
+
+#: gitk:2823
+#, tcl-format
+msgid "<%s-Q>\t\tQuit"
+msgstr "<%s-Q>\t\tSair"
+
+#: gitk:2824
+#, tcl-format
+msgid "<%s-W>\t\tClose window"
+msgstr "<%s-W>\t\tFechar janela"
+
+#: gitk:2825
+msgid "<Home>\t\tMove to first commit"
+msgstr "<Home>\t\tIr para a primeira revisão"
+
+#: gitk:2826
+msgid "<End>\t\tMove to last commit"
+msgstr "<End>\t\tIr para a última revisão"
+
+#: gitk:2827
+msgid "<Up>, p, i\tMove up one commit"
+msgstr "<Up>, p, i\tIr para uma revisão acima"
+
+#: gitk:2828
+msgid "<Down>, n, k\tMove down one commit"
+msgstr "<Down>, n, k\tIr para uma revisão abaixo"
+
+#: gitk:2829
+msgid "<Left>, z, j\tGo back in history list"
+msgstr "<Left>, z, j\tVoltar no histórico"
+
+#: gitk:2830
+msgid "<Right>, x, l\tGo forward in history list"
+msgstr "<Right>, x, l\tAvançar no histórico"
+
+#: gitk:2831
+msgid "<PageUp>\tMove up one page in commit list"
+msgstr "<PageUp>\tSubir uma página na lista de revisões"
+
+#: gitk:2832
+msgid "<PageDown>\tMove down one page in commit list"
+msgstr "<PageDown>\tDescer uma página na lista de revisões"
+
+#: gitk:2833
+#, tcl-format
+msgid "<%s-Home>\tScroll to top of commit list"
+msgstr "<%s-Home>\tRolar para o início da lista de revisões"
+
+#: gitk:2834
+#, tcl-format
+msgid "<%s-End>\tScroll to bottom of commit list"
+msgstr "<%s-End>\tRolar para o final da lista de revisões"
+
+#: gitk:2835
+#, tcl-format
+msgid "<%s-Up>\tScroll commit list up one line"
+msgstr "<%s-Up>\tRolar uma linha acima na lista de revisões"
+
+#: gitk:2836
+#, tcl-format
+msgid "<%s-Down>\tScroll commit list down one line"
+msgstr "<%s-Down>\tRolar uma linha abaixo na lista de revisões"
+
+#: gitk:2837
+#, tcl-format
+msgid "<%s-PageUp>\tScroll commit list up one page"
+msgstr "<%s-PageUp>\tRolar uma página acima na lista de revisões"
+
+#: gitk:2838
+#, tcl-format
+msgid "<%s-PageDown>\tScroll commit list down one page"
+msgstr "<%s-PageDown>\tRolar uma página abaixo na lista de revisões"
+
+#: gitk:2839
+msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
+msgstr "<Shift-Up>\tProcurar próxima (revisões mas recentes)"
+
+#: gitk:2840
+msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
+msgstr "<Shift-Down>\tProcurar anterior (revisões mais antigas)"
+
+#: gitk:2841
+msgid "<Delete>, b\tScroll diff view up one page"
+msgstr "<Delete>, b\tRola alterações uma página acima"
+
+#: gitk:2842
+msgid "<Backspace>\tScroll diff view up one page"
+msgstr "<Backspace>\tRolar alterações uma página abaixo"
+
+#: gitk:2843
+msgid "<Space>\t\tScroll diff view down one page"
+msgstr "<Space>\t\tRolar alterações uma página abaixo"
+
+#: gitk:2844
+msgid "u\t\tScroll diff view up 18 lines"
+msgstr "u\t\tRolar alterações 18 linhas acima"
+
+#: gitk:2845
+msgid "d\t\tScroll diff view down 18 lines"
+msgstr "d\t\tRolar alterações 18 linhas abaixo"
+
+#: gitk:2846
+#, tcl-format
+msgid "<%s-F>\t\tFind"
+msgstr "<%s-F>\t\tProcurar"
+
+#: gitk:2847
+#, tcl-format
+msgid "<%s-G>\t\tMove to next find hit"
+msgstr "<%s-G>\t\tIr para a próxima ocorrência"
+
+#: gitk:2848
+msgid "<Return>\tMove to next find hit"
+msgstr "<Return>\tIr para a próxima ocorrência"
+
+#: gitk:2849
+msgid "/\t\tFocus the search box"
+msgstr "/\t\tPor foco na caixa de busca"
+
+#: gitk:2850
+msgid "?\t\tMove to previous find hit"
+msgstr "?\t\tIr para a ocorrência anterior"
+
+#: gitk:2851
+msgid "f\t\tScroll diff view to next file"
+msgstr "f\t\tRolar alterações para o próximo arquivo"
+
+#: gitk:2852
+#, tcl-format
+msgid "<%s-S>\t\tSearch for next hit in diff view"
+msgstr "<%s-S>\t\tProcurar a próxima ocorrência na lista de alterações"
+
+#: gitk:2853
+#, tcl-format
+msgid "<%s-R>\t\tSearch for previous hit in diff view"
+msgstr "<%s-R>\t\tProcurar ocorrência anterior na lista de alterações"
+
+#: gitk:2854
+#, tcl-format
+msgid "<%s-KP+>\tIncrease font size"
+msgstr "<%s-KP+>\tAumentar tamanho da fonte"
+
+#: gitk:2855
+#, tcl-format
+msgid "<%s-plus>\tIncrease font size"
+msgstr "<%s-plus>\tAumentar tamanho da fonte"
+
+#: gitk:2856
+#, tcl-format
+msgid "<%s-KP->\tDecrease font size"
+msgstr "<%s-KP->\tReduzir tamanho da fonte"
+
+#: gitk:2857
+#, tcl-format
+msgid "<%s-minus>\tDecrease font size"
+msgstr "<%s-minus>\tReduzir tamanho da fonte"
+
+#: gitk:2858
+msgid "<F5>\t\tUpdate"
+msgstr "<F5>\t\tAtualizar"
+
+#: gitk:3313 gitk:3322
+#, tcl-format
+msgid "Error creating temporary directory %s:"
+msgstr "Erro ao criar o diretório temporário %s:"
+
+#: gitk:3335
+#, tcl-format
+msgid "Error getting \"%s\" from %s:"
+msgstr "Erro ao ler \"%s\" de %s:"
+
+#: gitk:3398
+msgid "command failed:"
+msgstr "O comando falhou:"
+
+#: gitk:3547
+msgid "No such commit"
+msgstr "Revisão não encontrada"
+
+#: gitk:3561
+msgid "git gui blame: command failed:"
+msgstr "Comando 'git gui blame' falhou:"
+
+#: gitk:3592
+#, tcl-format
+msgid "Couldn't read merge head: %s"
+msgstr "Impossível ler merge head: %s"
+
+#: gitk:3600
+#, tcl-format
+msgid "Error reading index: %s"
+msgstr "Erro ao ler o índice: %s"
+
+#: gitk:3625
+#, tcl-format
+msgid "Couldn't start git blame: %s"
+msgstr "Não foi possível inciar o 'git blame': %s"
+
+#: gitk:3628 gitk:6419
+msgid "Searching"
+msgstr "Procurando"
+
+#: gitk:3660
+#, tcl-format
+msgid "Error running git blame: %s"
+msgstr "Erro ao executar 'git blame': %s"
+
+#: gitk:3688
+#, tcl-format
+msgid "That line comes from commit %s,  which is not in this view"
+msgstr "Esta linha vem da revisão %s, que não está nesta vista"
+
+#: gitk:3702
+msgid "External diff viewer failed:"
+msgstr "Erro do visualizador de alterações externo:"
+
+#: gitk:3820
+msgid "Gitk view definition"
+msgstr "Definir vista"
+
+#: gitk:3824
+msgid "Remember this view"
+msgstr "Lembrar esta vista"
+
+#: gitk:3825
+msgid "References (space separated list):"
+msgstr "Referências (separar a lista com um espaço):"
+
+#: gitk:3826
+msgid "Branches & tags:"
+msgstr "Ramos & etiquetas:"
+
+#: gitk:3827
+msgid "All refs"
+msgstr "Todas as referências"
+
+#: gitk:3828
+msgid "All (local) branches"
+msgstr "Todos os ramos locais"
+
+#: gitk:3829
+msgid "All tags"
+msgstr "Todas as etiquetas"
+
+#: gitk:3830
+msgid "All remote-tracking branches"
+msgstr "Todos os ramos de rastreio"
+
+#: gitk:3831
+msgid "Commit Info (regular expressions):"
+msgstr "Informações da revisão (expressões regulares):"
+
+#: gitk:3832
+msgid "Author:"
+msgstr "Autor:"
+
+#: gitk:3833
+msgid "Committer:"
+msgstr "Revisor:"
+
+#: gitk:3834
+msgid "Commit Message:"
+msgstr "Descrição da revisão:"
+
+#: gitk:3835
+msgid "Matches all Commit Info criteria"
+msgstr "Coincidir todos os critérios de informações da revisão"
+
+#: gitk:3836
+msgid "Changes to Files:"
+msgstr "Mudanças para os arquivos:"
+
+#: gitk:3837
+msgid "Fixed String"
+msgstr "Texto fixo"
+
+#: gitk:3838
+msgid "Regular Expression"
+msgstr "Expressão regular"
+
+#: gitk:3839
+msgid "Search string:"
+msgstr "Texto de busca"
+
+#: gitk:3840
+msgid ""
+"Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 "
+"15:27:38\"):"
+msgstr ""
+"Datas de revisão (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 "
+"15:27:38\"):"
+
+#: gitk:3841
+msgid "Since:"
+msgstr "Desde:"
+
+#: gitk:3842
+msgid "Until:"
+msgstr "Até:"
+
+#: gitk:3843
+msgid "Limit and/or skip a number of revisions (positive integer):"
+msgstr "Limitar e/ou ignorar um número de revisões (inteiro positivo):"
+
+#: gitk:3844
+msgid "Number to show:"
+msgstr "Número para mostrar:"
+
+#: gitk:3845
+msgid "Number to skip:"
+msgstr "Número para ignorar:"
+
+#: gitk:3846
+msgid "Miscellaneous options:"
+msgstr "Opções diversas:"
+
+#: gitk:3847
+msgid "Strictly sort by date"
+msgstr "Ordenar estritamente pela data"
+
+#: gitk:3848
+msgid "Mark branch sides"
+msgstr "Marcar os dois lados do ramo"
+
+#: gitk:3849
+msgid "Limit to first parent"
+msgstr "Limitar ao primeiro antecessor"
+
+#: gitk:3850
+msgid "Simple history"
+msgstr "Histórico simplificado"
+
+#: gitk:3851
+msgid "Additional arguments to git log:"
+msgstr "Argumentos adicionais para o 'git log':"
+
+#: gitk:3852
+msgid "Enter files and directories to include, one per line:"
+msgstr "Arquivos e diretórios para incluir, um por linha"
+
+#: gitk:3853
+msgid "Command to generate more commits to include:"
+msgstr "Comando para gerar mais revisões para incluir:"
+
+#: gitk:3977
+msgid "Gitk: edit view"
+msgstr "Gitk: editar vista"
+
+#: gitk:3985
+msgid "-- criteria for selecting revisions"
+msgstr "-- critérios para selecionar revisões"
+
+#: gitk:3990
+msgid "View Name"
+msgstr "Nome da vista"
+
+#: gitk:4065
+msgid "Apply (F5)"
+msgstr "Aplicar (F5)"
+
+#: gitk:4103
+msgid "Error in commit selection arguments:"
+msgstr "Erro nos argumentos de seleção de revisões:"
+
+#: gitk:4156 gitk:4208 gitk:4656 gitk:4670 gitk:5931 gitk:11551 gitk:11552
+msgid "None"
+msgstr "Nenhum"
+
+#: gitk:4604 gitk:6451 gitk:8309 gitk:8324
+msgid "Date"
+msgstr "Data"
+
+#: gitk:4604 gitk:6451
+msgid "CDate"
+msgstr "DataR"
+
+#: gitk:4753 gitk:4758
+msgid "Descendant"
+msgstr "Descendente de"
+
+#: gitk:4754
+msgid "Not descendant"
+msgstr "Não descendente de"
+
+#: gitk:4761 gitk:4766
+msgid "Ancestor"
+msgstr "Antecessor de"
+
+#: gitk:4762
+msgid "Not ancestor"
+msgstr "Não antecessor de"
+
+#: gitk:5052
+msgid "Local changes checked in to index but not committed"
+msgstr "Mudanças locais marcadas, porém não salvas"
+
+#: gitk:5088
+msgid "Local uncommitted changes, not checked in to index"
+msgstr "Mudanças locais não marcadas"
+
+#: gitk:6769
+msgid "many"
+msgstr "muitas"
+
+#: gitk:6952
+msgid "Tags:"
+msgstr "Etiquetas:"
+
+#: gitk:6969 gitk:6975 gitk:8302
+msgid "Parent"
+msgstr "Antecessor"
+
+#: gitk:6980
+msgid "Child"
+msgstr "Descendente"
+
+#: gitk:6989
+msgid "Branch"
+msgstr "Ramo"
+
+#: gitk:6992
+msgid "Follows"
+msgstr "Segue"
+
+#: gitk:6995
+msgid "Precedes"
+msgstr "Precede"
+
+#: gitk:7532
+#, tcl-format
+msgid "Error getting diffs: %s"
+msgstr "Erro ao obter diferenças: %s"
+
+#: gitk:8130
+msgid "Goto:"
+msgstr "Ir para:"
+
+#: gitk:8151
+#, tcl-format
+msgid "Short SHA1 id %s is ambiguous"
+msgstr "O id SHA1 %s é ambíguo"
+
+#: gitk:8158
+#, tcl-format
+msgid "Revision %s is not known"
+msgstr "Revisão %s desconhecida"
+
+#: gitk:8168
+#, tcl-format
+msgid "SHA1 id %s is not known"
+msgstr "Id SHA1 %s desconhecido"
+
+#: gitk:8170
+#, tcl-format
+msgid "Revision %s is not in the current view"
+msgstr "A revisão %s não está na vista atual"
+
+#: gitk:8312
+msgid "Children"
+msgstr "Descendentes"
+
+#: gitk:8370
+#, tcl-format
+msgid "Reset %s branch to here"
+msgstr "Redefinir ramo %s para este ponto"
+
+#: gitk:8372
+msgid "Detached head: can't reset"
+msgstr "Detached head: impossível redefinir"
+
+#: gitk:8481 gitk:8487
+msgid "Skipping merge commit "
+msgstr "Saltando revisão de mesclagem"
+
+#: gitk:8496 gitk:8501
+msgid "Error getting patch ID for "
+msgstr "Erro ao obter patch ID para"
+
+#: gitk:8497 gitk:8502
+msgid " - stopping\n"
+msgstr "- parando\n"
+
+#: gitk:8507 gitk:8510 gitk:8518 gitk:8532 gitk:8541
+msgid "Commit "
+msgstr "Revisão"
+
+#: gitk:8511
+msgid ""
+" is the same patch as\n"
+"       "
+msgstr ""
+"é o mesmo patch que\n"
+"       "
+
+#: gitk:8519
+msgid ""
+" differs from\n"
+"       "
+msgstr "difere de"
+
+#: gitk:8521
+msgid ""
+"Diff of commits:\n"
+"\n"
+msgstr ""
+"Diferença de revisões:\n"
+"\n"
+
+#: gitk:8533 gitk:8542
+#, tcl-format
+msgid " has %s children - stopping\n"
+msgstr "possui %s descendentes - parando\n"
+
+#: gitk:8561
+#, tcl-format
+msgid "Error writing commit to file: %s"
+msgstr "Erro ao salvar revisão para o arquivo: %s"
+
+#: gitk:8567
+#, tcl-format
+msgid "Error diffing commits: %s"
+msgstr "Erro ao comparar revisões: %s"
+
+#: gitk:8598
+msgid "Top"
+msgstr "Início"
+
+#: gitk:8599
+msgid "From"
+msgstr "De"
+
+#: gitk:8604
+msgid "To"
+msgstr "Para"
+
+#: gitk:8628
+msgid "Generate patch"
+msgstr "Gerar patch"
+
+#: gitk:8630
+msgid "From:"
+msgstr "De:"
+
+#: gitk:8639
+msgid "To:"
+msgstr "Para:"
+
+#: gitk:8648
+msgid "Reverse"
+msgstr "Inverter"
+
+#: gitk:8650 gitk:8845
+msgid "Output file:"
+msgstr "Arquivo de saída:"
+
+#: gitk:8656
+msgid "Generate"
+msgstr "Gerar"
+
+#: gitk:8694
+msgid "Error creating patch:"
+msgstr "Erro ao criar patch:"
+
+#: gitk:8717 gitk:8833 gitk:8890
+msgid "ID:"
+msgstr "ID:"
+
+#: gitk:8726
+msgid "Tag name:"
+msgstr "Nome da etiqueta:"
+
+#: gitk:8729
+msgid "Tag message is optional"
+msgstr "A descrição da etiqueta é opcional"
+
+#: gitk:8731
+msgid "Tag message:"
+msgstr "Descrição da etiqueta"
+
+#: gitk:8735 gitk:8899
+msgid "Create"
+msgstr "Criar"
+
+#: gitk:8753
+msgid "No tag name specified"
+msgstr "Nome da etiqueta não indicado"
+
+#: gitk:8757
+#, tcl-format
+msgid "Tag \"%s\" already exists"
+msgstr "Etiqueta \"%s\" já existe"
+
+#: gitk:8767
+msgid "Error creating tag:"
+msgstr "Erro ao criar etiqueta:"
+
+#: gitk:8842
+msgid "Command:"
+msgstr "Comando:"
+
+#: gitk:8850
+msgid "Write"
+msgstr "Exportar"
+
+#: gitk:8868
+msgid "Error writing commit:"
+msgstr "Erro ao exportar revisão"
+
+#: gitk:8895
+msgid "Name:"
+msgstr "Nome:"
+
+#: gitk:8918
+msgid "Please specify a name for the new branch"
+msgstr "Indique um nome para o novo ramo"
+
+#: gitk:8923
+#, tcl-format
+msgid "Branch '%s' already exists. Overwrite?"
+msgstr "O ramo \"%s\" já existe. Sobrescrever?"
+
+#: gitk:8989
+#, tcl-format
+msgid "Commit %s is already included in branch %s -- really re-apply it?"
+msgstr "Revisão %s já inclusa no ramo %s -- você realmente deseja reaplicá-la?"
+
+#: gitk:8994
+msgid "Cherry-picking"
+msgstr "Cherry-picking"
+
+#: gitk:9003
+#, tcl-format
+msgid ""
+"Cherry-pick failed because of local changes to file '%s'.\n"
+"Please commit, reset or stash your changes and try again."
+msgstr ""
+"O cherry-pick falhou porque o arquivo \"%s\" possui mudanças locais.\n"
+"Salve a uma revisão, redefina ou armazene (stash) suas mudanças e tente "
+"novamente."
+
+#: gitk:9009
+msgid ""
+"Cherry-pick failed because of merge conflict.\n"
+"Do you wish to run git citool to resolve it?"
+msgstr ""
+"O cherry-pick falhou porque houve um conflito na mesclagem.\n"
+"Executar o 'git citool' para resolvê-lo?"
+
+#: gitk:9025
+msgid "No changes committed"
+msgstr "Nenhuma revisão foi salva"
+
+#: gitk:9051
+msgid "Confirm reset"
+msgstr "Confirmar redefinição"
+
+#: gitk:9053
+#, tcl-format
+msgid "Reset branch %s to %s?"
+msgstr "Você realmente deseja redefinir o ramo %s para %s?"
+
+#: gitk:9055
+msgid "Reset type:"
+msgstr "Tipo de redefinição"
+
+#: gitk:9058
+msgid "Soft: Leave working tree and index untouched"
+msgstr "Soft: deixa a árvore de trabalho e o índice intocados"
+
+#: gitk:9061
+msgid "Mixed: Leave working tree untouched, reset index"
+msgstr "Misto: Deixa a árvore de trabalho intocada, redefine o índice"
+
+#: gitk:9064
+msgid ""
+"Hard: Reset working tree and index\n"
+"(discard ALL local changes)"
+msgstr ""
+"Hard: Redefine a árvore de trabalho e o índice\n"
+"(descarta TODAS as mudanças locais)"
+
+#: gitk:9081
+msgid "Resetting"
+msgstr "Redefinindo"
+
+#: gitk:9141
+msgid "Checking out"
+msgstr "Abrindo"
+
+#: gitk:9194
+msgid "Cannot delete the currently checked-out branch"
+msgstr "Impossível excluir o ramo atualmente aberto"
+
+#: gitk:9200
+#, tcl-format
+msgid ""
+"The commits on branch %s aren't on any other branch.\n"
+"Really delete branch %s?"
+msgstr ""
+"As revisões do ramo \"%s\" não existem em nenhum outro ramo.\n"
+"Você realmente deseja excluir ramo \"%s\"?"
+
+#: gitk:9231
+#, tcl-format
+msgid "Tags and heads: %s"
+msgstr "Referências: %s"
+
+#: gitk:9246
+msgid "Filter"
+msgstr "Filtro"
+
+#: gitk:9541
+msgid ""
+"Error reading commit topology information; branch and preceding/following "
+"tag information will be incomplete."
+msgstr ""
+"Erro ao ler a topologia das revisões; as informações dos ramos e etiquetas "
+"antecessoras/sucessoras estarão incompletas"
+
+#: gitk:10527
+msgid "Tag"
+msgstr "Etiqueta"
+
+#: gitk:10527
+msgid "Id"
+msgstr "Id"
+
+#: gitk:10576
+msgid "Gitk font chooser"
+msgstr "Selecionar fontes do Gitk"
+
+#: gitk:10593
+msgid "B"
+msgstr "B"
+
+#: gitk:10596
+msgid "I"
+msgstr "I"
+
+#: gitk:10714
+msgid "Gitk preferences"
+msgstr "Preferências do Gitk"
+
+#: gitk:10716
+msgid "Commit list display options"
+msgstr "Opções da lista de revisões"
+
+#: gitk:10719
+msgid "Maximum graph width (lines)"
+msgstr "Largura máxima do grafo (linhas)"
+
+#: gitk:10722
+#, tcl-format
+msgid "Maximum graph width (% of pane)"
+msgstr "Largura máxima do grafo (% do painel)"
+
+#: gitk:10725
+msgid "Show local changes"
+msgstr "Exibir mudanças locais"
+
+#: gitk:10728
+msgid "Auto-select SHA1"
+msgstr "Selecionar o SHA1 automaticamente"
+
+#: gitk:10731
+msgid "Hide remote refs"
+msgstr "Ocultar referências remotas"
+
+#: gitk:10735
+msgid "Diff display options"
+msgstr "Opções de exibição das alterações"
+
+#: gitk:10737
+msgid "Tab spacing"
+msgstr "Espaços por tabulação"
+
+#: gitk:10740
+msgid "Display nearby tags"
+msgstr "Exibir etiquetas próximas"
+
+#: gitk:10743
+msgid "Limit diffs to listed paths"
+msgstr "Limitar diferenças aos caminhos listados"
+
+#: gitk:10746
+msgid "Support per-file encodings"
+msgstr "Usar codificações distintas por arquivo"
+
+#: gitk:10752 gitk:10832
+msgid "External diff tool"
+msgstr "Ferramenta 'diff' externa"
+
+#: gitk:10753
+msgid "Choose..."
+msgstr "Selecionar..."
+
+#: gitk:10758
+msgid "General options"
+msgstr "Opções gerais"
+
+#: gitk:10761
+msgid "Use themed widgets"
+msgstr "Usar temas para as janelas"
+
+#: gitk:10763
+msgid "(change requires restart)"
+msgstr "(exige reinicialização)"
+
+#: gitk:10765
+msgid "(currently unavailable)"
+msgstr "(atualmente indisponível)"
+
+#: gitk:10769
+msgid "Colors: press to choose"
+msgstr "Cores: clique para escolher"
+
+#: gitk:10772
+msgid "Interface"
+msgstr "Interface"
+
+#: gitk:10773
+msgid "interface"
+msgstr "interface"
+
+#: gitk:10776
+msgid "Background"
+msgstr "Segundo plano"
+
+#: gitk:10777 gitk:10807
+msgid "background"
+msgstr "segundo plano"
+
+#: gitk:10780
+msgid "Foreground"
+msgstr "Primeiro plano"
+
+#: gitk:10781
+msgid "foreground"
+msgstr "primeiro plano"
+
+#: gitk:10784
+msgid "Diff: old lines"
+msgstr "Diff: linhas excluídas"
+
+#: gitk:10785
+msgid "diff old lines"
+msgstr "linhas excluídas"
+
+#: gitk:10789
+msgid "Diff: new lines"
+msgstr "Diff: linhas adicionadas"
+
+#: gitk:10790
+msgid "diff new lines"
+msgstr "linhas adicionadas"
+
+#: gitk:10794
+msgid "Diff: hunk header"
+msgstr "Diff: cabeçalho do bloco"
+
+#: gitk:10796
+msgid "diff hunk header"
+msgstr "cabeçalho do bloco"
+
+#: gitk:10800
+msgid "Marked line bg"
+msgstr "2º plano da linha marcada"
+
+#: gitk:10802
+msgid "marked line background"
+msgstr "segundo plano da linha marcada"
+
+#: gitk:10806
+msgid "Select bg"
+msgstr "2º plano da seleção"
+
+#: gitk:10810
+msgid "Fonts: press to choose"
+msgstr "Fontes: clique para escolher"
+
+#: gitk:10812
+msgid "Main font"
+msgstr "Fonte principal"
+
+#: gitk:10813
+msgid "Diff display font"
+msgstr "Fonte da lista de mudanças"
+
+#: gitk:10814
+msgid "User interface font"
+msgstr "Fonte da interface"
+
+#: gitk:10842
+#, tcl-format
+msgid "Gitk: choose color for %s"
+msgstr "Gitk: selecionar cor para %s"
+
+#: gitk:11445
+msgid "Cannot find a git repository here."
+msgstr "Não há nenhum repositório git aqui."
+
+#: gitk:11449
+#, tcl-format
+msgid "Cannot find the git directory \"%s\"."
+msgstr "Impossível encontrar o diretório git \"%s\"."
+
+#: gitk:11496
+#, tcl-format
+msgid "Ambiguous argument '%s': both revision and filename"
+msgstr ""
+"O argumento \"%s\" é ambíguo (especifica tanto uma revisão e um nome de "
+"arquivo)"
+
+#: gitk:11508
+msgid "Bad arguments to gitk:"
+msgstr "Argumentos incorretos para o gitk:"
+
+#: gitk:11604
+msgid "Command line"
+msgstr "Linha de comando"
diff --git a/gitk-git/po/ru.po b/gitk-git/po/ru.po
index 704eba8..5987303 100644
--- a/gitk-git/po/ru.po
+++ b/gitk-git/po/ru.po
@@ -24,7 +24,7 @@
 
 #: gitk:323
 msgid "Error executing --argscmd command:"
-msgstr "Ошибка выполнения команды заданой --argscmd:"
+msgstr "Ошибка выполнения команды заданной --argscmd:"
 
 #: gitk:336
 msgid "No files selected: --merge specified but no files are unmerged."
@@ -37,7 +37,7 @@
 "No files selected: --merge specified but no unmerged files are within file "
 "limit."
 msgstr ""
-"Файлы не выбраны: указан --merge, но в рамках указаного "
+"Файлы не выбраны: указан --merge, но в рамках указанного "
 "ограничения на имена файлов нет ни одного "
 "где эта операция должна быть завершена."
 
@@ -246,11 +246,11 @@
 
 #: gitk:2326 gitk:2339
 msgid "Diff this -> selected"
-msgstr "Сравнить это состояние с выделеным"
+msgstr "Сравнить это состояние с выделенным"
 
 #: gitk:2327 gitk:2340
 msgid "Diff selected -> this"
-msgstr "Сравнить выделеное с этим состоянием"
+msgstr "Сравнить выделенное с этим состоянием"
 
 #: gitk:2328 gitk:2341
 msgid "Make patch"
@@ -313,14 +313,14 @@
 "\n"
 "Gitk - a commit viewer for git\n"
 "\n"
-"Copyright © 2005-2008 Paul Mackerras\n"
+"Copyright \\u00a9 2005-2010 Paul Mackerras\n"
 "\n"
 "Use and redistribute under the terms of the GNU General Public License"
 msgstr ""
 "\n"
 "Gitk - программа просмотра истории репозиториев Git\n"
 "\n"
-"Copyright (c) 2005-2008 Paul Mackerras\n"
+"Copyright \\u00a9 2005-2010 Paul Mackerras\n"
 "\n"
 "Использование и распространение согласно условиям GNU General Public License"
 
@@ -440,11 +440,11 @@
 #: gitk:2666
 #, tcl-format
 msgid "<%s-G>\t\tMove to next find hit"
-msgstr "<%s-G>\t\tПерейти к следующему найденому состоянию"
+msgstr "<%s-G>\t\tПерейти к следующему найденному состоянию"
 
 #: gitk:2667
 msgid "<Return>\tMove to next find hit"
-msgstr "<Return>\tПерейти к следующему найденому состоянию"
+msgstr "<Return>\tПерейти к следующему найденному состоянию"
 
 #: gitk:2668
 msgid "/\t\tFocus the search box"
@@ -452,7 +452,7 @@
 
 #: gitk:2669
 msgid "?\t\tMove to previous find hit"
-msgstr "?\t\tПерейти к предыдущему найденому состоянию"
+msgstr "?\t\tПерейти к предыдущему найденному состоянию"
 
 #: gitk:2670
 msgid "f\t\tScroll diff view to next file"
@@ -466,7 +466,7 @@
 #: gitk:2672
 #, tcl-format
 msgid "<%s-R>\t\tSearch for previous hit in diff view"
-msgstr "<%s-R>\t\tПерейти к предыдущему найденому тексту в списке изменений"
+msgstr "<%s-R>\t\tПерейти к предыдущему найденному тексту в списке изменений"
 
 #: gitk:2673
 #, tcl-format
@@ -855,7 +855,7 @@
 #: gitk:8472
 msgid "Mixed: Leave working tree untouched, reset index"
 msgstr ""
-"Смешаный: оставить рабочий каталог неизменным, установить индекс"
+"Смешанный: оставить рабочий каталог неизменным, установить индекс"
 
 #: gitk:8475
 msgid ""
@@ -962,7 +962,7 @@
 
 #: gitk:10126
 msgid "Limit diffs to listed paths"
-msgstr "Ограничить показ изменений выбраными файлами"
+msgstr "Ограничить показ изменений выбранными файлами"
 
 #: gitk:10129
 msgid "Support per-file encodings"
@@ -1022,11 +1022,11 @@
 
 #: gitk:10169
 msgid "Marked line bg"
-msgstr "Фон выбраной строки"
+msgstr "Фон выбранной строки"
 
 #: gitk:10171
 msgid "marked line background"
-msgstr "фон выбраной строки"
+msgstr "фон выбранной строки"
 
 #: gitk:10175
 msgid "Select bg"
diff --git a/gitk-git/po/sv.po b/gitk-git/po/sv.po
index 947b53f..2f07a2e 100644
--- a/gitk-git/po/sv.po
+++ b/gitk-git/po/sv.po
@@ -1,32 +1,40 @@
 # Swedish translation for gitk
-# Copyright (C) 2005-2008 Paul Mackerras
+# Copyright (C) 2005-2010 Paul Mackerras
 # This file is distributed under the same license as the gitk package.
 #
-# Peter Karlsson <peter@softwolves.pp.se>, 2008.
+# Peter Krefting <peter@softwolves.pp.se>, 2008-2010.
 # Mikael Magnusson <mikachu@gmail.com>, 2008.
 msgid ""
 msgstr ""
 "Project-Id-Version: sv\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-10-18 22:03+1100\n"
-"PO-Revision-Date: 2008-08-03 19:03+0200\n"
-"Last-Translator: Mikael Magnusson <mikachu@gmail.com>\n"
-"Language-Team: Swedish <sv@li.org>\n"
+"POT-Creation-Date: 2010-09-12 21:14+0100\n"
+"PO-Revision-Date: 2010-09-12 21:16+0100\n"
+"Last-Translator: Peter Krefting <peter@softwolves.pp.se>\n"
+"Language-Team: Swedish <tp-sv@listor.tp-sv.se>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
+"Content-Transfer-Encoding: 8bit"
 
-#: gitk:113
+#: gitk:115
 msgid "Couldn't get list of unmerged files:"
-msgstr "Kunde inta hämta lista över ej sammanslagna filer:"
+msgstr "Kunde inte hämta lista över ej sammanslagna filer:"
 
-#: gitk:340
+#: gitk:274
+msgid "Error parsing revisions:"
+msgstr "Fel vid tolkning av revisioner:"
+
+#: gitk:330
+msgid "Error executing --argscmd command:"
+msgstr "Fel vid körning av --argscmd-kommando:"
+
+#: gitk:343
 msgid "No files selected: --merge specified but no files are unmerged."
 msgstr ""
 "Inga filer valdes: --merge angavs men det finns inga filer som inte har "
 "slagits samman."
 
-#: gitk:343
+#: gitk:346
 msgid ""
 "No files selected: --merge specified but no unmerged files are within file "
 "limit."
@@ -34,697 +42,998 @@
 "Inga filer valdes: --merge angavs men det finns inga filer inom "
 "filbegränsningen."
 
-#: gitk:365 gitk:503
+#: gitk:368 gitk:516
 msgid "Error executing git log:"
 msgstr "Fel vid körning av git log:"
 
-#: gitk:378
+#: gitk:386 gitk:532
 msgid "Reading"
 msgstr "Läser"
 
-#: gitk:438 gitk:3462
+#: gitk:446 gitk:4271
 msgid "Reading commits..."
 msgstr "Läser incheckningar..."
 
-#: gitk:441 gitk:1528 gitk:3465
+#: gitk:449 gitk:1580 gitk:4274
 msgid "No commits selected"
 msgstr "Inga incheckningar markerade"
 
-#: gitk:1399
+#: gitk:1456
 msgid "Can't parse git log output:"
 msgstr "Kan inte tolka utdata från git log:"
 
-#: gitk:1605
+#: gitk:1676
 msgid "No commit information available"
 msgstr "Ingen incheckningsinformation är tillgänglig"
 
-#: gitk:1709 gitk:1731 gitk:3259 gitk:7764 gitk:9293 gitk:9466
+#: gitk:1818
+msgid "mc"
+msgstr "mc"
+
+#: gitk:1853 gitk:4064 gitk:9067 gitk:10607 gitk:10817
 msgid "OK"
 msgstr "OK"
 
-#: gitk:1733 gitk:3260 gitk:7439 gitk:7510 gitk:7613 gitk:7660 gitk:7766
-#: gitk:9294 gitk:9467
+#: gitk:1855 gitk:4066 gitk:8657 gitk:8736 gitk:8851 gitk:8900 gitk:9069
+#: gitk:10608 gitk:10818
 msgid "Cancel"
 msgstr "Avbryt"
 
-#: gitk:1811
+#: gitk:1980
 msgid "Update"
 msgstr "Uppdatera"
 
-#: gitk:1812
+#: gitk:1981
 msgid "Reload"
 msgstr "Ladda om"
 
-#: gitk:1813
+#: gitk:1982
 msgid "Reread references"
 msgstr "Läs om referenser"
 
-#: gitk:1814
+#: gitk:1983
 msgid "List references"
 msgstr "Visa referenser"
 
-#: gitk:1815
+#: gitk:1985
+msgid "Start git gui"
+msgstr "Starta git gui"
+
+#: gitk:1987
 msgid "Quit"
 msgstr "Avsluta"
 
-#: gitk:1810
+#: gitk:1979
 msgid "File"
 msgstr "Arkiv"
 
-#: gitk:1818
+#: gitk:1991
 msgid "Preferences"
 msgstr "Inställningar"
 
-#: gitk:1817
+#: gitk:1990
 msgid "Edit"
 msgstr "Redigera"
 
-#: gitk:1821
+#: gitk:1995
 msgid "New view..."
 msgstr "Ny vy..."
 
-#: gitk:1822
+#: gitk:1996
 msgid "Edit view..."
 msgstr "Ändra vy..."
 
-#: gitk:1823
+#: gitk:1997
 msgid "Delete view"
 msgstr "Ta bort vy"
 
-#: gitk:1825
+#: gitk:1999
 msgid "All files"
 msgstr "Alla filer"
 
-#: gitk:1820 gitk:3196
+#: gitk:1994 gitk:3817
 msgid "View"
 msgstr "Visa"
 
-#: gitk:1828 gitk:2487
+#: gitk:2004 gitk:2014 gitk:2787
 msgid "About gitk"
 msgstr "Om gitk"
 
-#: gitk:1829
+#: gitk:2005 gitk:2019
 msgid "Key bindings"
 msgstr "Tangentbordsbindningar"
 
-#: gitk:1827
+#: gitk:2003 gitk:2018
 msgid "Help"
 msgstr "Hjälp"
 
-#: gitk:1887
-msgid "SHA1 ID: "
-msgstr "SHA1-id: "
+#: gitk:2096 gitk:8132
+msgid "SHA1 ID:"
+msgstr "SHA1-id:"
 
-#: gitk:1918
+#: gitk:2127
 msgid "Row"
 msgstr "Rad"
 
-#: gitk:1949
+#: gitk:2165
 msgid "Find"
 msgstr "Sök"
 
-#: gitk:1950
+#: gitk:2166
 msgid "next"
 msgstr "nästa"
 
-#: gitk:1951
+#: gitk:2167
 msgid "prev"
 msgstr "föreg"
 
-#: gitk:1952
+#: gitk:2168
 msgid "commit"
 msgstr "incheckning"
 
-#: gitk:1955 gitk:1957 gitk:3617 gitk:3640 gitk:3664 gitk:5550 gitk:5621
+#: gitk:2171 gitk:2173 gitk:4432 gitk:4455 gitk:4479 gitk:6420 gitk:6492
+#: gitk:6576
 msgid "containing:"
 msgstr "som innehåller:"
 
-#: gitk:1958 gitk:2954 gitk:2959 gitk:3692
+#: gitk:2174 gitk:3298 gitk:3303 gitk:4507
 msgid "touching paths:"
 msgstr "som rör sökväg:"
 
-#: gitk:1959 gitk:3697
+#: gitk:2175 gitk:4512
 msgid "adding/removing string:"
 msgstr "som lägger/till tar bort sträng:"
 
-#: gitk:1968 gitk:1970
+#: gitk:2184 gitk:2186
 msgid "Exact"
 msgstr "Exakt"
 
-#: gitk:1970 gitk:3773 gitk:5518
+#: gitk:2186 gitk:4587 gitk:6388
 msgid "IgnCase"
 msgstr "IgnVersaler"
 
-#: gitk:1970 gitk:3666 gitk:3771 gitk:5514
+#: gitk:2186 gitk:4481 gitk:4585 gitk:6384
 msgid "Regexp"
 msgstr "Reg.uttr."
 
-#: gitk:1972 gitk:1973 gitk:3792 gitk:3822 gitk:3829 gitk:5641 gitk:5708
+#: gitk:2188 gitk:2189 gitk:4606 gitk:4636 gitk:4643 gitk:6512 gitk:6580
 msgid "All fields"
 msgstr "Alla fält"
 
-#: gitk:1973 gitk:3790 gitk:3822 gitk:5580
+#: gitk:2189 gitk:4604 gitk:4636 gitk:6451
 msgid "Headline"
 msgstr "Rubrik"
 
-#: gitk:1974 gitk:3790 gitk:5580 gitk:5708 gitk:6109
+#: gitk:2190 gitk:4604 gitk:6451 gitk:6580 gitk:7013
 msgid "Comments"
 msgstr "Kommentarer"
 
-#: gitk:1974 gitk:3790 gitk:3794 gitk:3829 gitk:5580 gitk:6045 gitk:7285
-#: gitk:7300
+#: gitk:2190 gitk:4604 gitk:4608 gitk:4643 gitk:6451 gitk:6948 gitk:8307
+#: gitk:8322
 msgid "Author"
 msgstr "Författare"
 
-#: gitk:1974 gitk:3790 gitk:5580 gitk:6047
+#: gitk:2190 gitk:4604 gitk:6451 gitk:6950
 msgid "Committer"
 msgstr "Incheckare"
 
-#: gitk:2003
+#: gitk:2221
 msgid "Search"
 msgstr "Sök"
 
-#: gitk:2010
+#: gitk:2229
 msgid "Diff"
 msgstr "Diff"
 
-#: gitk:2012
+#: gitk:2231
 msgid "Old version"
 msgstr "Gammal version"
 
-#: gitk:2014
+#: gitk:2233
 msgid "New version"
 msgstr "Ny version"
 
-#: gitk:2016
+#: gitk:2235
 msgid "Lines of context"
 msgstr "Rader sammanhang"
 
-#: gitk:2026
+#: gitk:2245
 msgid "Ignore space change"
 msgstr "Ignorera ändringar i blanksteg"
 
-#: gitk:2084
+#: gitk:2304
 msgid "Patch"
 msgstr "Patch"
 
-#: gitk:2086
+#: gitk:2306
 msgid "Tree"
 msgstr "Träd"
 
-#: gitk:2213 gitk:2226
+#: gitk:2463 gitk:2480
 msgid "Diff this -> selected"
 msgstr "Diff denna -> markerad"
 
-#: gitk:2214 gitk:2227
+#: gitk:2464 gitk:2481
 msgid "Diff selected -> this"
 msgstr "Diff markerad -> denna"
 
-#: gitk:2215 gitk:2228
+#: gitk:2465 gitk:2482
 msgid "Make patch"
 msgstr "Skapa patch"
 
-#: gitk:2216 gitk:7494
+#: gitk:2466 gitk:8715
 msgid "Create tag"
 msgstr "Skapa tagg"
 
-#: gitk:2217 gitk:7593
+#: gitk:2467 gitk:8831
 msgid "Write commit to file"
 msgstr "Skriv incheckning till fil"
 
-#: gitk:2218 gitk:7647
+#: gitk:2468 gitk:8888
 msgid "Create new branch"
 msgstr "Skapa ny gren"
 
-#: gitk:2219
+#: gitk:2469
 msgid "Cherry-pick this commit"
 msgstr "Plocka denna incheckning"
 
-#: gitk:2220
+#: gitk:2470
 msgid "Reset HEAD branch to here"
 msgstr "Återställ HEAD-grenen hit"
 
-#: gitk:2234
+#: gitk:2471
+msgid "Mark this commit"
+msgstr "Markera denna incheckning"
+
+#: gitk:2472
+msgid "Return to mark"
+msgstr "Återgå till markering"
+
+#: gitk:2473
+msgid "Find descendant of this and mark"
+msgstr "Hitta efterföljare till denna och markera"
+
+#: gitk:2474
+msgid "Compare with marked commit"
+msgstr "Jämför med markerad incheckning"
+
+#: gitk:2488
 msgid "Check out this branch"
 msgstr "Checka ut denna gren"
 
-#: gitk:2235
+#: gitk:2489
 msgid "Remove this branch"
 msgstr "Ta bort denna gren"
 
-#: gitk:2242
+#: gitk:2496
 msgid "Highlight this too"
 msgstr "Markera även detta"
 
-#: gitk:2243
+#: gitk:2497
 msgid "Highlight this only"
 msgstr "Markera bara detta"
 
-#: gitk:2244
+#: gitk:2498
 msgid "External diff"
 msgstr "Extern diff"
 
-#: gitk:2245
+#: gitk:2499
 msgid "Blame parent commit"
-msgstr ""
+msgstr "Klandra föräldraincheckning"
 
-#: gitk:2488
+#: gitk:2506
+msgid "Show origin of this line"
+msgstr "Visa ursprunget för den här raden"
+
+#: gitk:2507
+msgid "Run git gui blame on this line"
+msgstr "Kör git gui blame på den här raden"
+
+#: gitk:2789
 msgid ""
 "\n"
 "Gitk - a commit viewer for git\n"
 "\n"
-"Copyright © 2005-2008 Paul Mackerras\n"
+"Copyright ©9 2005-2010 Paul Mackerras\n"
 "\n"
 "Use and redistribute under the terms of the GNU General Public License"
 msgstr ""
 "\n"
 "Gitk - en incheckningsvisare för git\n"
 "\n"
-"Copyright © 2005-2008 Paul Mackerras\n"
+"Copyright ©9 2005-2010 Paul Mackerras\n"
 "\n"
 "Använd och vidareförmedla enligt villkoren i GNU General Public License"
 
-#: gitk:2496 gitk:2557 gitk:7943
+#: gitk:2797 gitk:2862 gitk:9253
 msgid "Close"
 msgstr "Stäng"
 
-#: gitk:2515
+#: gitk:2818
 msgid "Gitk key bindings"
 msgstr "Tangentbordsbindningar för Gitk"
 
-#: gitk:2517
+#: gitk:2821
 msgid "Gitk key bindings:"
 msgstr "Tangentbordsbindningar för Gitk:"
 
-#: gitk:2519
+#: gitk:2823
 #, tcl-format
 msgid "<%s-Q>\t\tQuit"
 msgstr "<%s-Q>\t\tAvsluta"
 
-#: gitk:2520
+#: gitk:2824
+#, tcl-format
+msgid "<%s-W>\t\tClose window"
+msgstr "<%s-W>\t\tStäng fönster"
+
+#: gitk:2825
 msgid "<Home>\t\tMove to first commit"
 msgstr "<Home>\t\tGå till första incheckning"
 
-#: gitk:2521
+#: gitk:2826
 msgid "<End>\t\tMove to last commit"
 msgstr "<End>\t\tGå till sista incheckning"
 
-#: gitk:2522
+#: gitk:2827
 msgid "<Up>, p, i\tMove up one commit"
 msgstr "<Upp>, p, i\tGå en incheckning upp"
 
-#: gitk:2523
+#: gitk:2828
 msgid "<Down>, n, k\tMove down one commit"
 msgstr "<Ned>, n, k\tGå en incheckning ned"
 
-#: gitk:2524
+#: gitk:2829
 msgid "<Left>, z, j\tGo back in history list"
 msgstr "<Vänster>, z, j\tGå bakåt i historiken"
 
-#: gitk:2525
+#: gitk:2830
 msgid "<Right>, x, l\tGo forward in history list"
 msgstr "<Höger>, x, l\tGå framåt i historiken"
 
-#: gitk:2526
+#: gitk:2831
 msgid "<PageUp>\tMove up one page in commit list"
 msgstr "<PageUp>\tGå upp en sida i incheckningslistan"
 
-#: gitk:2527
+#: gitk:2832
 msgid "<PageDown>\tMove down one page in commit list"
 msgstr "<PageDown>\tGå ned en sida i incheckningslistan"
 
-#: gitk:2528
+#: gitk:2833
 #, tcl-format
 msgid "<%s-Home>\tScroll to top of commit list"
 msgstr "<%s-Home>\tRulla till början av incheckningslistan"
 
-#: gitk:2529
+#: gitk:2834
 #, tcl-format
 msgid "<%s-End>\tScroll to bottom of commit list"
 msgstr "<%s-End>\tRulla till slutet av incheckningslistan"
 
-#: gitk:2530
+#: gitk:2835
 #, tcl-format
 msgid "<%s-Up>\tScroll commit list up one line"
 msgstr "<%s-Upp>\tRulla incheckningslistan upp ett steg"
 
-#: gitk:2531
+#: gitk:2836
 #, tcl-format
 msgid "<%s-Down>\tScroll commit list down one line"
 msgstr "<%s-Ned>\tRulla incheckningslistan ned ett steg"
 
-#: gitk:2532
+#: gitk:2837
 #, tcl-format
 msgid "<%s-PageUp>\tScroll commit list up one page"
 msgstr "<%s-PageUp>\tRulla incheckningslistan upp en sida"
 
-#: gitk:2533
+#: gitk:2838
 #, tcl-format
 msgid "<%s-PageDown>\tScroll commit list down one page"
 msgstr "<%s-PageDown>\tRulla incheckningslistan ned en sida"
 
-#: gitk:2534
+#: gitk:2839
 msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
 msgstr "<Skift-Upp>\tSök bakåt (uppåt, senare incheckningar)"
 
-#: gitk:2535
+#: gitk:2840
 msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
 msgstr "<Skift-Ned>\tSök framåt (nedåt, tidigare incheckningar)"
 
-#: gitk:2536
+#: gitk:2841
 msgid "<Delete>, b\tScroll diff view up one page"
 msgstr "<Delete>, b\tRulla diffvisningen upp en sida"
 
-#: gitk:2537
+#: gitk:2842
 msgid "<Backspace>\tScroll diff view up one page"
 msgstr "<Baksteg>\tRulla diffvisningen upp en sida"
 
-#: gitk:2538
+#: gitk:2843
 msgid "<Space>\t\tScroll diff view down one page"
 msgstr "<Blanksteg>\tRulla diffvisningen ned en sida"
 
-#: gitk:2539
+#: gitk:2844
 msgid "u\t\tScroll diff view up 18 lines"
 msgstr "u\t\tRulla diffvisningen upp 18 rader"
 
-#: gitk:2540
+#: gitk:2845
 msgid "d\t\tScroll diff view down 18 lines"
 msgstr "d\t\tRulla diffvisningen ned 18 rader"
 
-#: gitk:2541
+#: gitk:2846
 #, tcl-format
 msgid "<%s-F>\t\tFind"
 msgstr "<%s-F>\t\tSök"
 
-#: gitk:2542
+#: gitk:2847
 #, tcl-format
 msgid "<%s-G>\t\tMove to next find hit"
 msgstr "<%s-G>\t\tGå till nästa sökträff"
 
-#: gitk:2543
+#: gitk:2848
 msgid "<Return>\tMove to next find hit"
 msgstr "<Return>\t\tGå till nästa sökträff"
 
-#: gitk:2544
-msgid "/\t\tMove to next find hit, or redo find"
-msgstr "/\t\tGå till nästa sökträff, eller sök på nytt"
+#: gitk:2849
+msgid "/\t\tFocus the search box"
+msgstr "/\t\tFokusera sökrutan"
 
-#: gitk:2545
+#: gitk:2850
 msgid "?\t\tMove to previous find hit"
 msgstr "?\t\tGå till föregående sökträff"
 
-#: gitk:2546
+#: gitk:2851
 msgid "f\t\tScroll diff view to next file"
 msgstr "f\t\tRulla diffvisningen till nästa fil"
 
-#: gitk:2547
+#: gitk:2852
 #, tcl-format
 msgid "<%s-S>\t\tSearch for next hit in diff view"
 msgstr "<%s-S>\t\tGå till nästa sökträff i diffvisningen"
 
-#: gitk:2548
+#: gitk:2853
 #, tcl-format
 msgid "<%s-R>\t\tSearch for previous hit in diff view"
 msgstr "<%s-R>\t\tGå till föregående sökträff i diffvisningen"
 
-#: gitk:2549
+#: gitk:2854
 #, tcl-format
 msgid "<%s-KP+>\tIncrease font size"
 msgstr "<%s-Num+>\tÖka teckenstorlek"
 
-#: gitk:2550
+#: gitk:2855
 #, tcl-format
 msgid "<%s-plus>\tIncrease font size"
 msgstr "<%s-plus>\tÖka teckenstorlek"
 
-#: gitk:2551
+#: gitk:2856
 #, tcl-format
 msgid "<%s-KP->\tDecrease font size"
 msgstr "<%s-Num->\tMinska teckenstorlek"
 
-#: gitk:2552
+#: gitk:2857
 #, tcl-format
 msgid "<%s-minus>\tDecrease font size"
 msgstr "<%s-minus>\tMinska teckenstorlek"
 
-#: gitk:2553
+#: gitk:2858
 msgid "<F5>\t\tUpdate"
 msgstr "<F5>\t\tUppdatera"
 
-#: gitk:3200
-msgid "Gitk view definition"
-msgstr "Definition av Gitk-vy"
+#: gitk:3313 gitk:3322
+#, tcl-format
+msgid "Error creating temporary directory %s:"
+msgstr "Fel vid skapande av temporär katalog %s:"
 
-#: gitk:3225
-msgid "Name"
-msgstr "Namn"
+#: gitk:3335
+#, tcl-format
+msgid "Error getting \"%s\" from %s:"
+msgstr "Fel vid hämtning av  \"%s\" från %s:"
 
-#: gitk:3228
-msgid "Remember this view"
-msgstr "Spara denna vy"
+#: gitk:3398
+msgid "command failed:"
+msgstr "kommando misslyckades:"
 
-#: gitk:3232
-msgid "Commits to include (arguments to git log):"
-msgstr "Incheckningar att ta med (argument till git log):"
+#: gitk:3547
+msgid "No such commit"
+msgstr "Incheckning saknas"
 
-#: gitk:3239
-msgid "Command to generate more commits to include:"
-msgstr "Kommando för att generera fler incheckningar att ta med:"
+#: gitk:3561
+msgid "git gui blame: command failed:"
+msgstr "git gui blame: kommando misslyckades:"
 
-#: gitk:3246
-msgid "Enter files and directories to include, one per line:"
-msgstr "Ange filer och kataloger att ta med, en per rad:"
+#: gitk:3592
+#, tcl-format
+msgid "Couldn't read merge head: %s"
+msgstr "Kunde inte läsa sammanslagningshuvud: %s"
 
-#: gitk:3293
-msgid "Error in commit selection arguments:"
-msgstr "Fel i argument för val av incheckningar:"
+#: gitk:3600
+#, tcl-format
+msgid "Error reading index: %s"
+msgstr "Fel vid läsning av index: %s"
 
-#: gitk:3347 gitk:3399 gitk:3842 gitk:3856 gitk:5060 gitk:10141 gitk:10142
-msgid "None"
-msgstr "Inget"
+#: gitk:3625
+#, tcl-format
+msgid "Couldn't start git blame: %s"
+msgstr "Kunde inte starta git blame: %s"
 
-#: gitk:3790 gitk:5580 gitk:7287 gitk:7302
-msgid "Date"
-msgstr "Datum"
-
-#: gitk:3790 gitk:5580
-msgid "CDate"
-msgstr "Skapat datum"
-
-#: gitk:3939 gitk:3944
-msgid "Descendant"
-msgstr "Avkomling"
-
-#: gitk:3940
-msgid "Not descendant"
-msgstr "Inte avkomling"
-
-#: gitk:3947 gitk:3952
-msgid "Ancestor"
-msgstr "Förfader"
-
-#: gitk:3948
-msgid "Not ancestor"
-msgstr "Inte förfader"
-
-#: gitk:4187
-msgid "Local changes checked in to index but not committed"
-msgstr "Lokala ändringar sparade i indexet men inte incheckade"
-
-#: gitk:4220
-msgid "Local uncommitted changes, not checked in to index"
-msgstr "Lokala ändringar, ej sparade i indexet"
-
-#: gitk:5549
+#: gitk:3628 gitk:6419
 msgid "Searching"
 msgstr "Söker"
 
-#: gitk:6049
+#: gitk:3660
+#, tcl-format
+msgid "Error running git blame: %s"
+msgstr "Fel vid körning av git blame: %s"
+
+#: gitk:3688
+#, tcl-format
+msgid "That line comes from commit %s,  which is not in this view"
+msgstr "Raden kommer från incheckningen %s, som inte finns i denna vy"
+
+#: gitk:3702
+msgid "External diff viewer failed:"
+msgstr "Externt diff-verktyg misslyckades:"
+
+#: gitk:3820
+msgid "Gitk view definition"
+msgstr "Definition av Gitk-vy"
+
+#: gitk:3824
+msgid "Remember this view"
+msgstr "Spara denna vy"
+
+#: gitk:3825
+msgid "References (space separated list):"
+msgstr "Referenser (blankstegsavdelad lista):"
+
+#: gitk:3826
+msgid "Branches & tags:"
+msgstr "Grenar & taggar:"
+
+#: gitk:3827
+msgid "All refs"
+msgstr "Alla referenser"
+
+#: gitk:3828
+msgid "All (local) branches"
+msgstr "Alla (lokala) grenar"
+
+#: gitk:3829
+msgid "All tags"
+msgstr "Alla taggar"
+
+#: gitk:3830
+msgid "All remote-tracking branches"
+msgstr "Alla fjärrspårande grenar"
+
+#: gitk:3831
+msgid "Commit Info (regular expressions):"
+msgstr "Incheckningsinfo (reguljära uttryck):"
+
+#: gitk:3832
+msgid "Author:"
+msgstr "Författare:"
+
+#: gitk:3833
+msgid "Committer:"
+msgstr "Incheckare:"
+
+#: gitk:3834
+msgid "Commit Message:"
+msgstr "Incheckningsmeddelande:"
+
+#: gitk:3835
+msgid "Matches all Commit Info criteria"
+msgstr "Motsvarar alla kriterier för incheckningsinfo"
+
+#: gitk:3836
+msgid "Changes to Files:"
+msgstr "Ändringar av filer:"
+
+#: gitk:3837
+msgid "Fixed String"
+msgstr "Fast sträng"
+
+#: gitk:3838
+msgid "Regular Expression"
+msgstr "Reguljärt uttryck"
+
+#: gitk:3839
+msgid "Search string:"
+msgstr "Söksträng:"
+
+#: gitk:3840
+msgid ""
+"Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 "
+"15:27:38\"):"
+msgstr ""
+"Incheckingsdatum (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 "
+"15:27:38\"):"
+
+#: gitk:3841
+msgid "Since:"
+msgstr "Från:"
+
+#: gitk:3842
+msgid "Until:"
+msgstr "Till:"
+
+#: gitk:3843
+msgid "Limit and/or skip a number of revisions (positive integer):"
+msgstr "Begränsa och/eller hoppa över ett antal revisioner (positivt heltal):"
+
+#: gitk:3844
+msgid "Number to show:"
+msgstr "Antal att visa:"
+
+#: gitk:3845
+msgid "Number to skip:"
+msgstr "Antal att hoppa över:"
+
+#: gitk:3846
+msgid "Miscellaneous options:"
+msgstr "Diverse alternativ:"
+
+#: gitk:3847
+msgid "Strictly sort by date"
+msgstr "Strikt datumsortering"
+
+#: gitk:3848
+msgid "Mark branch sides"
+msgstr "Markera sidogrenar"
+
+#: gitk:3849
+msgid "Limit to first parent"
+msgstr "Begränsa till första förälder"
+
+#: gitk:3850
+msgid "Simple history"
+msgstr "Enkel historik"
+
+#: gitk:3851
+msgid "Additional arguments to git log:"
+msgstr "Ytterligare argument till git log:"
+
+#: gitk:3852
+msgid "Enter files and directories to include, one per line:"
+msgstr "Ange filer och kataloger att ta med, en per rad:"
+
+#: gitk:3853
+msgid "Command to generate more commits to include:"
+msgstr "Kommando för att generera fler incheckningar att ta med:"
+
+#: gitk:3977
+msgid "Gitk: edit view"
+msgstr "Gitk: redigera vy"
+
+#: gitk:3985
+msgid "-- criteria for selecting revisions"
+msgstr " - kriterier för val av revisioner"
+
+#: gitk:3990
+msgid "View Name"
+msgstr "Namn på vy"
+
+#: gitk:4065
+msgid "Apply (F5)"
+msgstr "Använd (F5)"
+
+#: gitk:4103
+msgid "Error in commit selection arguments:"
+msgstr "Fel i argument för val av incheckningar:"
+
+#: gitk:4156 gitk:4208 gitk:4656 gitk:4670 gitk:5931 gitk:11551 gitk:11552
+msgid "None"
+msgstr "Inget"
+
+#: gitk:4604 gitk:6451 gitk:8309 gitk:8324
+msgid "Date"
+msgstr "Datum"
+
+#: gitk:4604 gitk:6451
+msgid "CDate"
+msgstr "Skapat datum"
+
+#: gitk:4753 gitk:4758
+msgid "Descendant"
+msgstr "Avkomling"
+
+#: gitk:4754
+msgid "Not descendant"
+msgstr "Inte avkomling"
+
+#: gitk:4761 gitk:4766
+msgid "Ancestor"
+msgstr "Förfader"
+
+#: gitk:4762
+msgid "Not ancestor"
+msgstr "Inte förfader"
+
+#: gitk:5052
+msgid "Local changes checked in to index but not committed"
+msgstr "Lokala ändringar sparade i indexet men inte incheckade"
+
+#: gitk:5088
+msgid "Local uncommitted changes, not checked in to index"
+msgstr "Lokala ändringar, ej sparade i indexet"
+
+#: gitk:6769
+msgid "many"
+msgstr "många"
+
+#: gitk:6952
 msgid "Tags:"
 msgstr "Taggar:"
 
-#: gitk:6066 gitk:6072 gitk:7280
+#: gitk:6969 gitk:6975 gitk:8302
 msgid "Parent"
 msgstr "Förälder"
 
-#: gitk:6077
+#: gitk:6980
 msgid "Child"
 msgstr "Barn"
 
-#: gitk:6086
+#: gitk:6989
 msgid "Branch"
 msgstr "Gren"
 
-#: gitk:6089
+#: gitk:6992
 msgid "Follows"
 msgstr "Följer"
 
-#: gitk:6092
+#: gitk:6995
 msgid "Precedes"
 msgstr "Föregår"
 
-#: gitk:6378
-msgid "Error getting merge diffs:"
-msgstr "Fel vid hämtning av sammanslagningsdiff:"
+#: gitk:7532
+#, tcl-format
+msgid "Error getting diffs: %s"
+msgstr "Fel vid hämtning av diff: %s"
 
-#: gitk:7113
+#: gitk:8130
 msgid "Goto:"
 msgstr "Gå till:"
 
-#: gitk:7115
-msgid "SHA1 ID:"
-msgstr "SHA1-id:"
-
-#: gitk:7134
+#: gitk:8151
 #, tcl-format
 msgid "Short SHA1 id %s is ambiguous"
 msgstr "Förkortat SHA1-id %s är tvetydigt"
 
-#: gitk:7146
+#: gitk:8158
+#, tcl-format
+msgid "Revision %s is not known"
+msgstr "Revisionen %s är inte känd"
+
+#: gitk:8168
 #, tcl-format
 msgid "SHA1 id %s is not known"
 msgstr "SHA-id:t %s är inte känt"
 
-#: gitk:7148
+#: gitk:8170
 #, tcl-format
-msgid "Tag/Head %s is not known"
-msgstr "Tagg/huvud %s är okänt"
+msgid "Revision %s is not in the current view"
+msgstr "Revisionen %s finns inte i den nuvarande vyn"
 
-#: gitk:7290
+#: gitk:8312
 msgid "Children"
 msgstr "Barn"
 
-#: gitk:7347
+#: gitk:8370
 #, tcl-format
 msgid "Reset %s branch to here"
 msgstr "Återställ grenen %s hit"
 
-#: gitk:7349
+#: gitk:8372
 msgid "Detached head: can't reset"
 msgstr "Frånkopplad head: kan inte återställa"
 
-#: gitk:7381
+#: gitk:8481 gitk:8487
+msgid "Skipping merge commit "
+msgstr "Hoppar över sammanslagningsincheckning "
+
+#: gitk:8496 gitk:8501
+msgid "Error getting patch ID for "
+msgstr "Fel vid hämtning av patch-id för "
+
+#: gitk:8497 gitk:8502
+msgid " - stopping\n"
+msgstr " - stannar\n"
+
+#: gitk:8507 gitk:8510 gitk:8518 gitk:8532 gitk:8541
+msgid "Commit "
+msgstr "Incheckning "
+
+#: gitk:8511
+msgid ""
+" is the same patch as\n"
+"       "
+msgstr ""
+" är samma patch som\n"
+"       "
+
+#: gitk:8519
+msgid ""
+" differs from\n"
+"       "
+msgstr ""
+" skiljer sig från\n"
+"       "
+
+#: gitk:8521
+msgid ""
+"Diff of commits:\n"
+"\n"
+msgstr ""
+"Skillnad mellan incheckningar:\n"
+"\n"
+
+#: gitk:8533 gitk:8542
+#, tcl-format
+msgid " has %s children - stopping\n"
+msgstr " har %s barn - stannar\n"
+
+#: gitk:8561
+#, tcl-format
+msgid "Error writing commit to file: %s"
+msgstr "Fel vid skrivning av incheckning till fil: %s"
+
+#: gitk:8567
+#, tcl-format
+msgid "Error diffing commits: %s"
+msgstr "Fel vid jämförelse av incheckningar: %s"
+
+#: gitk:8598
 msgid "Top"
 msgstr "Topp"
 
-#: gitk:7382
+#: gitk:8599
 msgid "From"
 msgstr "Från"
 
-#: gitk:7387
+#: gitk:8604
 msgid "To"
 msgstr "Till"
 
-#: gitk:7410
+#: gitk:8628
 msgid "Generate patch"
 msgstr "Generera patch"
 
-#: gitk:7412
+#: gitk:8630
 msgid "From:"
 msgstr "Från:"
 
-#: gitk:7421
+#: gitk:8639
 msgid "To:"
 msgstr "Till:"
 
-#: gitk:7430
+#: gitk:8648
 msgid "Reverse"
 msgstr "Vänd"
 
-#: gitk:7432 gitk:7607
+#: gitk:8650 gitk:8845
 msgid "Output file:"
 msgstr "Utdatafil:"
 
-#: gitk:7438
+#: gitk:8656
 msgid "Generate"
 msgstr "Generera"
 
-#: gitk:7474
+#: gitk:8694
 msgid "Error creating patch:"
 msgstr "Fel vid generering av patch:"
 
-#: gitk:7496 gitk:7595 gitk:7649
+#: gitk:8717 gitk:8833 gitk:8890
 msgid "ID:"
 msgstr "Id:"
 
-#: gitk:7505
+#: gitk:8726
 msgid "Tag name:"
 msgstr "Taggnamn:"
 
-#: gitk:7509 gitk:7659
+#: gitk:8729
+msgid "Tag message is optional"
+msgstr "Taggmeddelandet är valfritt"
+
+#: gitk:8731
+msgid "Tag message:"
+msgstr "Taggmeddelande:"
+
+#: gitk:8735 gitk:8899
 msgid "Create"
 msgstr "Skapa"
 
-#: gitk:7524
+#: gitk:8753
 msgid "No tag name specified"
 msgstr "Inget taggnamn angavs"
 
-#: gitk:7528
+#: gitk:8757
 #, tcl-format
 msgid "Tag \"%s\" already exists"
 msgstr "Taggen \"%s\" finns redan"
 
-#: gitk:7534
+#: gitk:8767
 msgid "Error creating tag:"
 msgstr "Fel vid skapande av tagg:"
 
-#: gitk:7604
+#: gitk:8842
 msgid "Command:"
 msgstr "Kommando:"
 
-#: gitk:7612
+#: gitk:8850
 msgid "Write"
 msgstr "Skriv"
 
-#: gitk:7628
+#: gitk:8868
 msgid "Error writing commit:"
 msgstr "Fel vid skrivning av incheckning:"
 
-#: gitk:7654
+#: gitk:8895
 msgid "Name:"
 msgstr "Namn:"
 
-#: gitk:7674
+#: gitk:8918
 msgid "Please specify a name for the new branch"
 msgstr "Ange ett namn för den nya grenen"
 
-#: gitk:7703
+#: gitk:8923
+#, tcl-format
+msgid "Branch '%s' already exists. Overwrite?"
+msgstr "Grenen \"%s\" finns redan. Skriva över?"
+
+#: gitk:8989
 #, tcl-format
 msgid "Commit %s is already included in branch %s -- really re-apply it?"
 msgstr ""
 "Incheckningen %s finns redan på grenen %s -- skall den verkligen appliceras "
 "på nytt?"
 
-#: gitk:7708
+#: gitk:8994
 msgid "Cherry-picking"
 msgstr "Plockar"
 
-#: gitk:7720
+#: gitk:9003
+#, tcl-format
+msgid ""
+"Cherry-pick failed because of local changes to file '%s'.\n"
+"Please commit, reset or stash your changes and try again."
+msgstr ""
+"Cherry-pick misslyckades på grund av lokala ändringar i filen \"%s\".\n"
+"Checka in, återställ eller spara undan (stash) dina ändringar och försök "
+"igen."
+
+#: gitk:9009
+msgid ""
+"Cherry-pick failed because of merge conflict.\n"
+"Do you wish to run git citool to resolve it?"
+msgstr ""
+"Cherry-pick misslyckades på grund av en sammanslagningskonflikt.\n"
+"Vill du köra git citool för att lösa den?"
+
+#: gitk:9025
 msgid "No changes committed"
 msgstr "Inga ändringar incheckade"
 
-#: gitk:7745
+#: gitk:9051
 msgid "Confirm reset"
 msgstr "Bekräfta återställning"
 
-#: gitk:7747
+#: gitk:9053
 #, tcl-format
 msgid "Reset branch %s to %s?"
 msgstr "Återställa grenen %s till %s?"
 
-#: gitk:7751
+#: gitk:9055
 msgid "Reset type:"
 msgstr "Typ av återställning:"
 
-#: gitk:7755
+#: gitk:9058
 msgid "Soft: Leave working tree and index untouched"
 msgstr "Mjuk: Rör inte utcheckning och index"
 
-#: gitk:7758
+#: gitk:9061
 msgid "Mixed: Leave working tree untouched, reset index"
 msgstr "Blandad: Rör inte utcheckning, återställ index"
 
-#: gitk:7761
+#: gitk:9064
 msgid ""
 "Hard: Reset working tree and index\n"
 "(discard ALL local changes)"
@@ -732,19 +1041,19 @@
 "Hård: Återställ utcheckning och index\n"
 "(förkastar ALLA lokala ändringar)"
 
-#: gitk:7777
+#: gitk:9081
 msgid "Resetting"
 msgstr "Återställer"
 
-#: gitk:7834
+#: gitk:9141
 msgid "Checking out"
 msgstr "Checkar ut"
 
-#: gitk:7885
+#: gitk:9194
 msgid "Cannot delete the currently checked-out branch"
 msgstr "Kan inte ta bort den just nu utcheckade grenen"
 
-#: gitk:7891
+#: gitk:9200
 #, tcl-format
 msgid ""
 "The commits on branch %s aren't on any other branch.\n"
@@ -753,16 +1062,16 @@
 "Incheckningarna på grenen %s existerar inte på någon annan gren.\n"
 "Vill du verkligen ta bort grenen %s?"
 
-#: gitk:7922
+#: gitk:9231
 #, tcl-format
 msgid "Tags and heads: %s"
 msgstr "Taggar och huvuden: %s"
 
-#: gitk:7936
+#: gitk:9246
 msgid "Filter"
 msgstr "Filter"
 
-#: gitk:8230
+#: gitk:9541
 msgid ""
 "Error reading commit topology information; branch and preceding/following "
 "tag information will be incomplete."
@@ -770,154 +1079,224 @@
 "Fel vid läsning av information om incheckningstopologi; information om "
 "grenar och föregående/senare taggar kommer inte vara komplett."
 
-#: gitk:9216
+#: gitk:10527
 msgid "Tag"
 msgstr "Tagg"
 
-#: gitk:9216
+#: gitk:10527
 msgid "Id"
 msgstr "Id"
 
-#: gitk:9262
+#: gitk:10576
 msgid "Gitk font chooser"
 msgstr "Teckensnittsväljare för Gitk"
 
-#: gitk:9279
+#: gitk:10593
 msgid "B"
 msgstr "F"
 
-#: gitk:9282
+#: gitk:10596
 msgid "I"
 msgstr "K"
 
-#: gitk:9375
+#: gitk:10714
 msgid "Gitk preferences"
 msgstr "Inställningar för Gitk"
 
-#: gitk:9376
+#: gitk:10716
 msgid "Commit list display options"
 msgstr "Alternativ för incheckningslistvy"
 
-#: gitk:9379
+#: gitk:10719
 msgid "Maximum graph width (lines)"
 msgstr "Maximal grafbredd (rader)"
 
-#: gitk:9383
+#: gitk:10722
 #, tcl-format
 msgid "Maximum graph width (% of pane)"
 msgstr "Maximal grafbredd (% av ruta)"
 
-#: gitk:9388
+#: gitk:10725
 msgid "Show local changes"
 msgstr "Visa lokala ändringar"
 
-#: gitk:9393
+#: gitk:10728
 msgid "Auto-select SHA1"
 msgstr "Välj SHA1 automatiskt"
 
-#: gitk:9398
+#: gitk:10731
+msgid "Hide remote refs"
+msgstr "Dölj fjärr-referenser"
+
+#: gitk:10735
 msgid "Diff display options"
 msgstr "Alternativ för diffvy"
 
-#: gitk:9400
+#: gitk:10737
 msgid "Tab spacing"
 msgstr "Blanksteg för tabulatortecken"
 
-#: gitk:9404
+#: gitk:10740
 msgid "Display nearby tags"
 msgstr "Visa närliggande taggar"
 
-#: gitk:9409
+#: gitk:10743
 msgid "Limit diffs to listed paths"
 msgstr "Begränsa diff till listade sökvägar"
 
-#: gitk:9414
+#: gitk:10746
 msgid "Support per-file encodings"
-msgstr ""
+msgstr "Stöd för filspecifika teckenkodningar"
 
-#: gitk:9421
+#: gitk:10752 gitk:10832
 msgid "External diff tool"
 msgstr "Externt diff-verktyg"
 
-#: gitk:9423
+#: gitk:10753
 msgid "Choose..."
 msgstr "Välj..."
 
-#: gitk:9428
+#: gitk:10758
+msgid "General options"
+msgstr "Allmänna inställningar"
+
+#: gitk:10761
+msgid "Use themed widgets"
+msgstr "Använd tema på fönsterelement"
+
+#: gitk:10763
+msgid "(change requires restart)"
+msgstr "(ändringen kräver omstart)"
+
+#: gitk:10765
+msgid "(currently unavailable)"
+msgstr "(för närvarande inte tillgängligt)"
+
+#: gitk:10769
 msgid "Colors: press to choose"
 msgstr "Färger: tryck för att välja"
 
-#: gitk:9431
+#: gitk:10772
+msgid "Interface"
+msgstr "Gränssnitt"
+
+#: gitk:10773
+msgid "interface"
+msgstr "gränssnitt"
+
+#: gitk:10776
 msgid "Background"
 msgstr "Bakgrund"
 
-#: gitk:9435
+#: gitk:10777 gitk:10807
+msgid "background"
+msgstr "bakgrund"
+
+#: gitk:10780
 msgid "Foreground"
 msgstr "Förgrund"
 
-#: gitk:9439
+#: gitk:10781
+msgid "foreground"
+msgstr "förgrund"
+
+#: gitk:10784
 msgid "Diff: old lines"
 msgstr "Diff: gamla rader"
 
-#: gitk:9444
+#: gitk:10785
+msgid "diff old lines"
+msgstr "diff gamla rader"
+
+#: gitk:10789
 msgid "Diff: new lines"
 msgstr "Diff: nya rader"
 
-#: gitk:9449
+#: gitk:10790
+msgid "diff new lines"
+msgstr "diff nya rader"
+
+#: gitk:10794
 msgid "Diff: hunk header"
 msgstr "Diff: delhuvud"
 
-#: gitk:9455
+#: gitk:10796
+msgid "diff hunk header"
+msgstr "diff delhuvud"
+
+#: gitk:10800
+msgid "Marked line bg"
+msgstr "Markerad rad bakgrund"
+
+#: gitk:10802
+msgid "marked line background"
+msgstr "markerad rad bakgrund"
+
+#: gitk:10806
 msgid "Select bg"
 msgstr "Markerad bakgrund"
 
-#: gitk:9459
+#: gitk:10810
 msgid "Fonts: press to choose"
 msgstr "Teckensnitt: tryck för att välja"
 
-#: gitk:9461
+#: gitk:10812
 msgid "Main font"
 msgstr "Huvudteckensnitt"
 
-#: gitk:9462
+#: gitk:10813
 msgid "Diff display font"
 msgstr "Teckensnitt för diffvisning"
 
-#: gitk:9463
+#: gitk:10814
 msgid "User interface font"
 msgstr "Teckensnitt för användargränssnitt"
 
-#: gitk:9488
+#: gitk:10842
 #, tcl-format
 msgid "Gitk: choose color for %s"
 msgstr "Gitk: välj färg för %s"
 
-#: gitk:9934
-msgid ""
-"Sorry, gitk cannot run with this version of Tcl/Tk.\n"
-" Gitk requires at least Tcl/Tk 8.4."
-msgstr ""
-"Gitk kan tyvärr inte köra med denna version av Tcl/Tk.\n"
-" Gitk kräver åtminstone Tcl/Tk 8.4."
-
-#: gitk:10047
+#: gitk:11445
 msgid "Cannot find a git repository here."
-msgstr "Hittar inget gitk-arkiv här."
+msgstr "Hittar inget git-arkiv här."
 
-#: gitk:10051
+#: gitk:11449
 #, tcl-format
 msgid "Cannot find the git directory \"%s\"."
 msgstr "Hittar inte git-katalogen \"%s\"."
 
-#: gitk:10098
+#: gitk:11496
 #, tcl-format
 msgid "Ambiguous argument '%s': both revision and filename"
 msgstr "Tvetydigt argument \"%s\": både revision och filnamn"
 
-#: gitk:10110
+#: gitk:11508
 msgid "Bad arguments to gitk:"
 msgstr "Felaktiga argument till gitk:"
 
-#: gitk:10170
+#: gitk:11604
 msgid "Command line"
 msgstr "Kommandorad"
+
+#~ msgid "SHA1 ID: "
+#~ msgstr "SHA1-id: "
+
+#~ msgid "- stopping\n"
+#~ msgstr "- stannar\n"
+
+#~ msgid ""
+#~ "Sorry, gitk cannot run with this version of Tcl/Tk.\n"
+#~ " Gitk requires at least Tcl/Tk 8.4."
+#~ msgstr ""
+#~ "Gitk kan tyvärr inte köra med denna version av Tcl/Tk.\n"
+#~ " Gitk kräver åtminstone Tcl/Tk 8.4."
+
+#~ msgid "Tag/Head %s is not known"
+#~ msgstr "Tagg/huvud %s är okänt"
+
+#~ msgid "/\t\tMove to next find hit, or redo find"
+#~ msgstr "/\t\tGå till nästa sökträff, eller sök på nytt"
+
+#~ msgid "Name"
+#~ msgstr "Namn"
diff --git a/gitweb/INSTALL b/gitweb/INSTALL
index 18c9ce3..6d45406 100644
--- a/gitweb/INSTALL
+++ b/gitweb/INSTALL
@@ -2,12 +2,13 @@
 =======================================
 
 First you have to generate gitweb.cgi from gitweb.perl using
-"make gitweb/gitweb.cgi", then copy appropriate files (gitweb.cgi,
-gitweb.css, git-logo.png and git-favicon.png) to their destination.
-For example if git was (or is) installed with /usr prefix, you can do
+"make gitweb", then "make install-gitweb" appropriate files
+(gitweb.cgi, gitweb.js, gitweb.css, git-logo.png and git-favicon.png)
+to their destination. For example if git was (or is) installed with
+/usr prefix and gitwebdir is /var/www/cgi-bin, you can do
 
-	$ make prefix=/usr gitweb/gitweb.cgi  ;# as yourself
-	# cp gitweb/git* /var/www/cgi-bin/    ;# as root
+	$ make prefix=/usr gitweb                            ;# as yourself
+	# make gitwebdir=/var/www/cgi-bin install-gitweb     ;# as root
 
 Alternatively you can use autoconf generated ./configure script to
 set up path to git binaries (via config.mak.autogen), so you can write
@@ -15,24 +16,38 @@
 
 	$ make configure                     ;# as yourself
 	$ ./configure --prefix=/usr          ;# as yourself
-	$ make gitweb/gitweb.cgi             ;# as yourself
-	# cp gitweb/git* /var/www/cgi-bin/   ;# as root
+	$ make gitweb                        ;# as yourself
+	# make gitwebdir=/var/www/cgi-bin \
+	       install-gitweb                ;# as root
 
 The above example assumes that your web server is configured to run
 [executable] files in /var/www/cgi-bin/ as server scripts (as CGI
 scripts).
 
 
+Requirements
+------------
+
+ - Core git tools
+ - Perl
+ - Perl modules: CGI, Encode, Fcntl, File::Find, File::Basename.
+ - web server
+
+The following optional Perl modules are required for extra features
+ - Digest::MD5 - for gravatar support
+ - CGI::Fast and FCGI - for running gitweb as FastCGI script
+ - HTML::TagCloud - for fancy tag cloud in project list view
+ - HTTP::Date or Time::ParseDate - to support If-Modified-Since for feeds
+
+
 Build time configuration
 ------------------------
 
-See also "How to configure gitweb for your local system" in README
-file for gitweb (in gitweb/README).
+See also "How to configure gitweb for your local system" section below.
 
 - There are many configuration variables which affect building of
   gitweb.cgi; see "default configuration for gitweb" section in main
-  (top dir) Makefile, and instructions for building gitweb/gitweb.cgi
-  target.
+  (top dir) Makefile, and instructions for building gitweb target.
 
   One of the most important is where to find the git wrapper binary. Gitweb
   tries to find the git wrapper at $(bindir)/git, so you have to set $bindir
@@ -62,36 +77,163 @@
   a suggestion).
 
 - You can control where gitweb tries to find its main CSS style file,
-  its favicon and logo with the GITWEB_CSS, GITWEB_FAVICON and GITWEB_LOGO
-  build configuration variables. By default gitweb tries to find them
-  in the same directory as gitweb.cgi script.
+  its JavaScript file, its favicon and logo with the GITWEB_CSS, GITWEB_JS
+  GITWEB_FAVICON and GITWEB_LOGO build configuration variables. By default
+  gitweb tries to find them in the same directory as gitweb.cgi script.
+
+- You can optionally generate minified versions of gitweb.js and gitweb.css
+  by defining the JSMIN and CSSMIN build configuration variables. By default
+  the non-minified versions will be used. NOTE: if you enable this option,
+  substitute gitweb.min.js and gitweb.min.css for all uses of gitweb.js and
+  gitweb.css in the help files.
+
+
+How to configure gitweb for your local system
+---------------------------------------------
+
+You can specify the following configuration variables when building GIT:
+
+ * GIT_BINDIR
+   Points where to find the git executable.  You should set it up to
+   the place where the git binary was installed (usually /usr/bin) if you
+   don't install git from sources together with gitweb.  [Default: $(bindir)]
+ * GITWEB_SITENAME
+   Shown in the title of all generated pages, defaults to the server name
+   (SERVER_NAME CGI environment variable) if not set. [No default]
+ * GITWEB_PROJECTROOT
+   The root directory for all projects shown by gitweb. Must be set
+   correctly for gitweb to find repositories to display.  See also
+   "Gitweb repositories" in the INSTALL file for gitweb.  [Default: /pub/git]
+ * GITWEB_PROJECT_MAXDEPTH
+   The filesystem traversing limit for getting the project list; the number
+   is taken as depth relative to the projectroot.  It is used when
+   GITWEB_LIST is a directory (or is not set; then project root is used).
+   This is meant to speed up project listing on large work trees by limiting
+   search depth.  [Default: 2007]
+ * GITWEB_LIST
+   Points to a directory to scan for projects (defaults to project root
+   if not set / if empty) or to a file with explicit listing of projects
+   (together with projects' ownership). See "Generating projects list
+   using gitweb" in INSTALL file for gitweb to find out how to generate
+   such file from scan of a directory. [No default, which means use root
+   directory for projects]
+ * GITWEB_EXPORT_OK
+   Show repository only if this file exists (in repository).  Only
+   effective if this variable evaluates to true.  [No default / Not set]
+ * GITWEB_STRICT_EXPORT
+   Only allow viewing of repositories also shown on the overview page.
+   This for example makes GITWEB_EXPORT_OK to decide if repository is
+   available and not only if it is shown.  If GITWEB_LIST points to
+   file with list of project, only those repositories listed would be
+   available for gitweb.  [No default]
+ * GITWEB_HOMETEXT
+   Points to an .html file which is included on the gitweb project
+   overview page ('projects_list' view), if it exists.  Relative to
+   gitweb.cgi script.  [Default: indextext.html]
+ * GITWEB_SITE_HTML_HEAD_STRING
+   html snippet to include in the <head> section of each page. [No default]
+ * GITWEB_SITE_HEADER
+   Filename of html text to include at top of each page.  Relative to
+   gitweb.cgi script.  [No default]
+ * GITWEB_SITE_FOOTER
+   Filename of html text to include at bottom of each page.  Relative to
+   gitweb.cgi script.  [No default]
+ * GITWEB_HOME_LINK_STR
+   String of the home link on top of all pages, leading to $home_link
+   (usually main gitweb page, which means projects list).  Used as first
+   part of gitweb view "breadcrumb trail": <home> / <project> / <view>.
+   [Default: projects]
+ * GITWEB_SITENAME
+   Name of your site or organization to appear in page titles.  Set it
+   to something descriptive for clearer bookmarks etc.  If not set
+   (if empty) gitweb uses "$SERVER_NAME Git", or "Untitled Git" if
+   SERVER_NAME CGI environment variable is not set (e.g. if running
+   gitweb as standalone script).  [No default]
+ * GITWEB_BASE_URL
+   Git base URLs used for URL to where fetch project from, i.e. full
+   URL is "$git_base_url/$project".  Shown on projects summary page.
+   Repository URL for project can be also configured per repository; this
+   takes precedence over URLs composed from base URL and a project name.
+   Note that you can setup multiple base URLs (for example one for
+   git:// protocol access, another for http:// access) from the gitweb
+   config file.  [No default]
+ * GITWEB_CSS
+   Points to the location where you put gitweb.css on your web server
+   (or to be more generic, the URI of gitweb stylesheet).  Relative to the
+   base URI of gitweb.  Note that you can setup multiple stylesheets from
+   the gitweb config file.  [Default: static/gitweb.css (or
+   static/gitweb.min.css if the CSSMIN variable is defined / CSS minifier
+   is used)]
+ * GITWEB_JS
+   Points to the location where you put gitweb.js on your web server
+   (or to be more generic URI of JavaScript code used by gitweb).
+   Relative to base URI of gitweb.  [Default: static/gitweb.js (or
+   static/gitweb.min.js if JSMIN build variable is defined / JavaScript
+   minifier is used)]
+ * CSSMIN, JSMIN
+   Invocation of a CSS minifier or a JavaScript minifier, respectively,
+   working as a filter (source on standard input, minified result on
+   standard output).  If set, it is used to generate a minified version of
+   'static/gitweb.css' or 'static/gitweb.js', respectively.  *Note* that
+   minified files would have *.min.css and *.min.js extension, which is
+   important if you also set GITWEB_CSS and/or GITWEB_JS.  [No default]
+ * GITWEB_LOGO
+   Points to the location where you put git-logo.png on your web server
+   (or to be more generic URI of logo, 72x27 size, displayed in top right
+   corner of each gitweb page, and used as logo for Atom feed).  Relative
+   to base URI of gitweb.  [Default: static/git-logo.png]
+ * GITWEB_FAVICON
+   Points to the location where you put git-favicon.png on your web server
+   (or to be more generic URI of favicon, assumed to be image/png type;
+   web browsers that support favicons (website icons) may display them
+   in the browser's URL bar and next to site name in bookmarks).  Relative
+   to base URI of gitweb.  [Default: static/git-favicon.png]
+ * GITWEB_CONFIG
+   This Perl file will be loaded using 'do' and can be used to override any
+   of the options above as well as some other options -- see the "Runtime
+   gitweb configuration" section below, and top of 'gitweb.cgi' for their
+   full list and description.  If the environment variable GITWEB_CONFIG
+   is set when gitweb.cgi is executed, then the file specified in the
+   environment variable will be loaded instead of the file specified
+   when gitweb.cgi was created.  [Default: gitweb_config.perl]
+ * GITWEB_CONFIG_SYSTEM
+   This Perl file will be loaded using 'do' as a fallback if GITWEB_CONFIG
+   does not exist.  If the environment variable GITWEB_CONFIG_SYSTEM is set
+   when gitweb.cgi is executed, then the file specified in the environment
+   variable will be loaded instead of the file specified when gitweb.cgi was
+   created.  [Default: /etc/gitweb.conf]
+ * HIGHLIGHT_BIN
+   Path to the highlight executable to use (must be the one from
+   http://www.andre-simon.de due to assumptions about parameters and output).
+   Useful if highlight is not installed on your webserver's PATH.
+   [Default: highlight]
 
 Build example
 ~~~~~~~~~~~~~
 
-- To install gitweb to /var/www/cgi-bin/gitweb/ when git wrapper
-  is installed at /usr/local/bin/git and the repositories (projects)
-  we want to display are under /home/local/scm, you can do
+- To install gitweb to /var/www/cgi-bin/gitweb/, when git wrapper
+  is installed at /usr/local/bin/git, the repositories (projects)
+  we want to display are under /home/local/scm, and you do not use
+  minifiers, you can do
 
 	make GITWEB_PROJECTROOT="/home/local/scm" \
-	     GITWEB_CSS="/gitweb/gitweb.css" \
-	     GITWEB_LOGO="/gitweb/git-logo.png" \
-	     GITWEB_FAVICON="/gitweb/git-favicon.png" \
+	     GITWEB_JS="gitweb/static/gitweb.js" \
+	     GITWEB_CSS="gitweb/static/gitweb.css" \
+	     GITWEB_LOGO="gitweb/static/git-logo.png" \
+	     GITWEB_FAVICON="gitweb/static/git-favicon.png" \
 	     bindir=/usr/local/bin \
-	     gitweb/gitweb.cgi
+	     gitweb
 
-	cp -fv ~/git/gitweb/gitweb.{cgi,css} \
-	       ~/git/gitweb/git-{favicon,logo}.png \
-	     /var/www/cgi-bin/gitweb/
+	make gitwebdir=/var/www/cgi-bin/gitweb install-gitweb
 
 
 Gitweb config file
 ------------------
 
 See also "Runtime gitweb configuration" section in README file
-for gitweb (in gitweb/README).
+for gitweb (in gitweb/README), and gitweb.conf(5) manpage.
 
-- You can configure gitweb further using the gitweb configuration file;
+- You can configure gitweb further using the per-instance gitweb configuration file;
   by default this is a file named gitweb_config.perl in the same place as
   gitweb.cgi script. You can control the default place for the config file
   using the GITWEB_CONFIG build configuration variable, and you can set it
@@ -101,6 +243,17 @@
   GITWEB_CONFIG_SYSTEM build configuration variable, and override it
   through the GITWEB_CONFIG_SYSTEM environment variable.
 
+  Note that if per-instance configuration file exists, then system-wide
+  configuration is _not used at all_.  This is quite untypical and suprising
+  behavior.  On the other hand changing current behavior would break backwards
+  compatibility and can lead to unexpected changes in gitweb behavior.
+  Therefore gitweb also looks for common system-wide configuration file,
+  normally /etc/gitweb-common.conf (set during build time using build time
+  configuration variable GITWEB_CONFIG_COMMON, set it at runtime using
+  environment variable with the same name).  Settings from per-instance or
+  system-wide configuration file override those from common system-wide
+  configuration file.
+
 - The gitweb config file is a fragment of perl code. You can set variables
   using "our $variable = value"; text from "#" character until the end
   of a line is ignored. See perlsyn(1) for details.
@@ -123,110 +276,32 @@
 	$feature{'snapshot'}{'default'} = ['zip', 'tgz'];
 	$feature{'snapshot'}{'override'} = 1;
 
+If you allow overriding for the snapshot feature, you can specify which
+snapshot formats are globally disabled. You can also add any command line
+options you want (such as setting the compression level). For instance,
+you can disable Zip compressed snapshots and set GZip to run at level 6 by
+adding the following lines to your $GITWEB_CONFIG:
+
+	$known_snapshot_formats{'zip'}{'disabled'} = 1;
+	$known_snapshot_formats{'tgz'}{'compressor'} = ['gzip','-6'];
+
 
 Gitweb repositories
 -------------------
 
-- By default all git repositories under projectroot are visible and
-  available to gitweb. The list of projects is generated by default by
-  scanning the projectroot directory for git repositories (for object
-  databases to be more exact).
+By default gitweb shows all git repositories under single common repository
+root on a local filesystem; see description of GITWEB_PROJECTROOT build-time
+configuration variable above (and also of GITWEB_LIST).
 
-  You can provide a pre-generated list of [visible] repositories,
-  together with information about their owners (the project ownership
-  defaults to the owner of the repository directory otherwise), by setting
-  the GITWEB_LIST build configuration variable (or the $projects_list
-  variable in the gitweb config file) to point to a plain file.
-
-  Each line of the projects list file should consist of the url-encoded path
-  to the project repository database (relative to projectroot), followed
-  by the url-encoded project owner on the same line (separated by a space).
-  Spaces in both project path and project owner have to be encoded as either
-  '%20' or '+'.
-
-  Other characters that have to be url-encoded, i.e. replaced by '%'
-  followed by two-digit character number in octal, are: other whitespace
-  characters (because they are field separator in a record), plus sign '+'
-  (because it can be used as replacement for spaces), and percent sign '%'
-  (which is used for encoding / escaping).
-
-  You can generate the projects list index file using the project_index
-  action (the 'TXT' link on projects list page) directly from gitweb.
-
-- By default, even if a project is not visible on projects list page, you
-  can view it nevertheless by hand-crafting a gitweb URL. You can set the
-  GITWEB_STRICT_EXPORT build configuration variable (or the $strict_export
-  variable in the gitweb config file) to only allow viewing of
-  repositories also shown on the overview page.
-
-- Alternatively, you can configure gitweb to only list and allow
-  viewing of the explicitly exported repositories, via the
-  GITWEB_EXPORT_OK build configuration variable (or the $export_ok
-  variable in gitweb config file). If it evaluates to true, gitweb
-  shows repositories only if this file exists in its object database
-  (if directory has the magic file named $export_ok).
-
-- Finally, it is possible to specify an arbitrary perl subroutine that
-  will be called for each project to determine if it can be exported.
-  The subroutine receives an absolute path to the project as its only
-  parameter.
-
-  For example, if you use mod_perl to run the script, and have dumb
-  http protocol authentication configured for your repositories, you
-  can use the following hook to allow access only if the user is
-  authorized to read the files:
-
-    $export_auth_hook = sub {
-        use Apache2::SubRequest ();
-        use Apache2::Const -compile => qw(HTTP_OK);
-        my $path = "$_[0]/HEAD";
-        my $r    = Apache2::RequestUtil->request;
-        my $sub  = $r->lookup_file($path);
-        return $sub->filename eq $path
-            && $sub->status == Apache2::Const::HTTP_OK;
-    };
-
-
-Generating projects list using gitweb
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-We assume that GITWEB_CONFIG has its default Makefile value, namely
-gitweb_config.perl. Put the following in gitweb_make_index.perl file:
-
-	$GITWEB_CONFIG = "gitweb_config.perl";
-	do $GITWEB_CONFIG if -e $GITWEB_CONFIG;
-
-	$projects_list = $projectroot;
-
-Then create the following script to get list of project in the format
-suitable for GITWEB_LIST build configuration variable (or
-$projects_list variable in gitweb config):
-
-	#!/bin/sh
-
-	export GITWEB_CONFIG="gitweb_make_index.perl"
-	export GATEWAY_INTERFACE="CGI/1.1"
-	export HTTP_ACCEPT="*/*"
-	export REQUEST_METHOD="GET"
-	export QUERY_STRING="a=project_index"
-
-	perl -- /var/www/cgi-bin/gitweb.cgi
-
-
-Requirements
-------------
-
- - Core git tools
- - Perl
- - Perl modules: CGI, Encode, Fcntl, File::Find, File::Basename.
- - web server
+More advanced usage, like limiting access or visibility of repositories and
+managing multiple roots are described on gitweb manpage.
 
 
 Example web server configuration
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-See also "Webserver configuration" section in README file for gitweb
-(in gitweb/README).
+See also "Webserver configuration" and "Advanced web server setup" sections
+in gitweb(1) manpage.
 
 
 - Apache2, gitweb installed as CGI script,
diff --git a/gitweb/Makefile b/gitweb/Makefile
new file mode 100644
index 0000000..cd194d0
--- /dev/null
+++ b/gitweb/Makefile
@@ -0,0 +1,195 @@
+# The default target of this Makefile is...
+all::
+
+# Define V=1 to have a more verbose compile.
+#
+# Define JSMIN to point to JavaScript minifier that functions as
+# a filter to have static/gitweb.js minified.
+#
+# Define CSSMIN to point to a CSS minifier in order to generate a minified
+# version of static/gitweb.css
+#
+
+prefix ?= $(HOME)
+bindir ?= $(prefix)/bin
+gitwebdir ?= /var/www/cgi-bin
+
+RM ?= rm -f
+INSTALL ?= install
+
+# default configuration for gitweb
+GITWEB_CONFIG = gitweb_config.perl
+GITWEB_CONFIG_SYSTEM = /etc/gitweb.conf
+GITWEB_CONFIG_COMMON = /etc/gitweb-common.conf
+GITWEB_HOME_LINK_STR = projects
+GITWEB_SITENAME =
+GITWEB_PROJECTROOT = /pub/git
+GITWEB_PROJECT_MAXDEPTH = 2007
+GITWEB_EXPORT_OK =
+GITWEB_STRICT_EXPORT =
+GITWEB_BASE_URL =
+GITWEB_LIST =
+GITWEB_HOMETEXT = indextext.html
+GITWEB_CSS = static/gitweb.css
+GITWEB_LOGO = static/git-logo.png
+GITWEB_FAVICON = static/git-favicon.png
+GITWEB_JS = static/gitweb.js
+GITWEB_SITE_HTML_HEAD_STRING =
+GITWEB_SITE_HEADER =
+GITWEB_SITE_FOOTER =
+HIGHLIGHT_BIN = highlight
+
+# include user config
+-include ../config.mak.autogen
+-include ../config.mak
+-include config.mak
+
+# determine version
+../GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
+	$(QUIET_SUBDIR0)../ $(QUIET_SUBDIR1) GIT-VERSION-FILE
+
+-include ../GIT-VERSION-FILE
+
+### Build rules
+
+SHELL_PATH ?= $(SHELL)
+PERL_PATH  ?= /usr/bin/perl
+
+# Shell quote;
+bindir_SQ = $(subst ','\'',$(bindir))#'
+gitwebdir_SQ = $(subst ','\'',$(gitwebdir))#'
+gitwebstaticdir_SQ = $(subst ','\'',$(gitwebdir)/static)#'
+SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))#'
+PERL_PATH_SQ  = $(subst ','\'',$(PERL_PATH))#'
+DESTDIR_SQ    = $(subst ','\'',$(DESTDIR))#'
+
+# Quiet generation (unless V=1)
+QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir
+QUIET_SUBDIR1  =
+
+ifneq ($(findstring $(MAKEFLAGS),w),w)
+PRINT_DIR = --no-print-directory
+else # "make -w"
+NO_SUBDIR = :
+endif
+
+ifneq ($(findstring $(MAKEFLAGS),s),s)
+ifndef V
+	QUIET          = @
+	QUIET_GEN      = $(QUIET)echo '   ' GEN $@;
+	QUIET_SUBDIR0  = +@subdir=
+	QUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \
+	                 $(MAKE) $(PRINT_DIR) -C $$subdir
+	export V
+	export QUIET
+	export QUIET_GEN
+	export QUIET_SUBDIR0
+	export QUIET_SUBDIR1
+endif
+endif
+
+all:: gitweb.cgi static/gitweb.js
+
+GITWEB_PROGRAMS = gitweb.cgi
+
+ifdef JSMIN
+GITWEB_FILES += static/gitweb.min.js
+GITWEB_JS = static/gitweb.min.js
+all:: static/gitweb.min.js
+static/gitweb.min.js: static/gitweb.js GITWEB-BUILD-OPTIONS
+	$(QUIET_GEN)$(JSMIN) <$< >$@
+else
+GITWEB_FILES += static/gitweb.js
+endif
+
+ifdef CSSMIN
+GITWEB_FILES += static/gitweb.min.css
+GITWEB_CSS = static/gitweb.min.css
+all:: static/gitweb.min.css
+static/gitweb.min.css: static/gitweb.css GITWEB-BUILD-OPTIONS
+	$(QUIET_GEN)$(CSSMIN) <$< >$@
+else
+GITWEB_FILES += static/gitweb.css
+endif
+
+GITWEB_FILES += static/git-logo.png static/git-favicon.png
+
+# JavaScript files that are composed (concatenated) to form gitweb.js
+#
+# js/lib/common-lib.js should be always first, then js/lib/*.js,
+# then the rest of files; js/gitweb.js should be last (if it exists)
+GITWEB_JSLIB_FILES += static/js/lib/common-lib.js
+GITWEB_JSLIB_FILES += static/js/lib/datetime.js
+GITWEB_JSLIB_FILES += static/js/lib/cookies.js
+GITWEB_JSLIB_FILES += static/js/javascript-detection.js
+GITWEB_JSLIB_FILES += static/js/adjust-timezone.js
+GITWEB_JSLIB_FILES += static/js/blame_incremental.js
+
+
+GITWEB_REPLACE = \
+	-e 's|++GIT_VERSION++|$(GIT_VERSION)|g' \
+	-e 's|++GIT_BINDIR++|$(bindir)|g' \
+	-e 's|++GITWEB_CONFIG++|$(GITWEB_CONFIG)|g' \
+	-e 's|++GITWEB_CONFIG_SYSTEM++|$(GITWEB_CONFIG_SYSTEM)|g' \
+	-e 's|++GITWEB_CONFIG_COMMON++|$(GITWEB_CONFIG_COMMON)|g' \
+	-e 's|++GITWEB_HOME_LINK_STR++|$(GITWEB_HOME_LINK_STR)|g' \
+	-e 's|++GITWEB_SITENAME++|$(GITWEB_SITENAME)|g' \
+	-e 's|++GITWEB_PROJECTROOT++|$(GITWEB_PROJECTROOT)|g' \
+	-e 's|"++GITWEB_PROJECT_MAXDEPTH++"|$(GITWEB_PROJECT_MAXDEPTH)|g' \
+	-e 's|++GITWEB_EXPORT_OK++|$(GITWEB_EXPORT_OK)|g' \
+	-e 's|++GITWEB_STRICT_EXPORT++|$(GITWEB_STRICT_EXPORT)|g' \
+	-e 's|++GITWEB_BASE_URL++|$(GITWEB_BASE_URL)|g' \
+	-e 's|++GITWEB_LIST++|$(GITWEB_LIST)|g' \
+	-e 's|++GITWEB_HOMETEXT++|$(GITWEB_HOMETEXT)|g' \
+	-e 's|++GITWEB_CSS++|$(GITWEB_CSS)|g' \
+	-e 's|++GITWEB_LOGO++|$(GITWEB_LOGO)|g' \
+	-e 's|++GITWEB_FAVICON++|$(GITWEB_FAVICON)|g' \
+	-e 's|++GITWEB_JS++|$(GITWEB_JS)|g' \
+	-e 's|++GITWEB_SITE_HTML_HEAD_STRING++|$(GITWEB_SITE_HTML_HEAD_STRING)|g' \
+	-e 's|++GITWEB_SITE_HEADER++|$(GITWEB_SITE_HEADER)|g' \
+	-e 's|++GITWEB_SITE_FOOTER++|$(GITWEB_SITE_FOOTER)|g' \
+	-e 's|++HIGHLIGHT_BIN++|$(HIGHLIGHT_BIN)|g'
+
+GITWEB-BUILD-OPTIONS: FORCE
+	@rm -f $@+
+	@echo "x" '$(PERL_PATH_SQ)' $(GITWEB_REPLACE) "$(JSMIN)|$(CSSMIN)" >$@+
+	@cmp -s $@+ $@ && rm -f $@+ || mv -f $@+ $@
+
+gitweb.cgi: gitweb.perl GITWEB-BUILD-OPTIONS
+	$(QUIET_GEN)$(RM) $@ $@+ && \
+	sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \
+		$(GITWEB_REPLACE) $< >$@+ && \
+	chmod +x $@+ && \
+	mv $@+ $@
+
+static/gitweb.js: $(GITWEB_JSLIB_FILES)
+	$(QUIET_GEN)$(RM) $@ $@+ && \
+	cat $^ >$@+ && \
+	mv $@+ $@
+
+### Testing rules
+
+test:
+	$(MAKE) -C ../t gitweb-test
+
+test-installed:
+	GITWEB_TEST_INSTALLED='$(DESTDIR_SQ)$(gitwebdir_SQ)' \
+		$(MAKE) -C ../t gitweb-test
+
+### Installation rules
+
+install: all
+	$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(gitwebdir_SQ)'
+	$(INSTALL) -m 755 $(GITWEB_PROGRAMS) '$(DESTDIR_SQ)$(gitwebdir_SQ)'
+	$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(gitwebstaticdir_SQ)'
+	$(INSTALL) -m 644 $(GITWEB_FILES) '$(DESTDIR_SQ)$(gitwebstaticdir_SQ)'
+
+### Cleaning rules
+
+clean:
+	$(RM) gitweb.cgi static/gitweb.js \
+		static/gitweb.min.js static/gitweb.min.css \
+		GITWEB-BUILD-OPTIONS
+
+.PHONY: all clean install test test-installed .FORCE-GIT-VERSION-FILE FORCE
+
diff --git a/gitweb/README b/gitweb/README
index 66c6a93..6da4778 100644
--- a/gitweb/README
+++ b/gitweb/README
@@ -7,113 +7,42 @@
 From the git version 1.4.0 gitweb is bundled with git.
 
 
-How to configure gitweb for your local system
----------------------------------------------
+Build time gitweb configuration
+-------------------------------
+There are many configuration variables which affect building gitweb (among
+others creating gitweb.cgi out of gitweb.perl by replacing placeholders such
+as `++GIT_BINDIR++` by their build-time values).
 
-See also the "Build time configuration" section in the INSTALL
-file for gitweb (in gitweb/INSTALL).
-
-You can specify the following configuration variables when building GIT:
- * GIT_BINDIR
-   Points where to find the git executable.  You should set it up to
-   the place where the git binary was installed (usually /usr/bin) if you
-   don't install git from sources together with gitweb.  [Default: $(bindir)]
- * GITWEB_SITENAME
-   Shown in the title of all generated pages, defaults to the server name
-   (SERVER_NAME CGI environment variable) if not set. [No default]
- * GITWEB_PROJECTROOT
-   The root directory for all projects shown by gitweb. Must be set
-   correctly for gitweb to find repositories to display.  See also
-   "Gitweb repositories" in the INSTALL file for gitweb.  [Default: /pub/git]
- * GITWEB_PROJECT_MAXDEPTH
-   The filesystem traversing limit for getting the project list; the number
-   is taken as depth relative to the projectroot.  It is used when
-   GITWEB_LIST is a directory (or is not set; then project root is used).
-   Is is meant to speed up project listing on large work trees by limiting
-   search depth.  [Default: 2007]
- * GITWEB_LIST
-   Points to a directory to scan for projects (defaults to project root
-   if not set / if empty) or to a file with explicit listing of projects
-   (together with projects' ownership). See "Generating projects list
-   using gitweb" in INSTALL file for gitweb to find out how to generate
-   such file from scan of a directory. [No default, which means use root
-   directory for projects]
- * GITWEB_EXPORT_OK
-   Show repository only if this file exists (in repository).  Only
-   effective if this variable evaluates to true.  [No default / Not set]
- * GITWEB_STRICT_EXPORT
-   Only allow viewing of repositories also shown on the overview page.
-   This for example makes GITWEB_EXPORT_OK to decide if repository is
-   available and not only if it is shown.  If GITWEB_LIST points to
-   file with list of project, only those repositories listed would be
-   available for gitweb.  [No default]
- * GITWEB_HOMETEXT
-   Points to an .html file which is included on the gitweb project
-   overview page ('projects_list' view), if it exists.  Relative to
-   gitweb.cgi script.  [Default: indextext.html]
- * GITWEB_SITE_HEADER
-   Filename of html text to include at top of each page.  Relative to
-   gitweb.cgi script.  [No default]
- * GITWEB_SITE_FOOTER
-   Filename of html text to include at bottom of each page.  Relative to
-   gitweb.cgi script.  [No default]
- * GITWEB_HOME_LINK_STR
-   String of the home link on top of all pages, leading to $home_link
-   (usually main gitweb page, which means projects list).  Used as first
-   part of gitweb view "breadcrumb trail": <home> / <project> / <view>.
-   [Default: projects]
- * GITWEB_SITENAME
-   Name of your site or organization to appear in page titles.  Set it
-   to something descriptive for clearer bookmarks etc.  If not set
-   (if empty) gitweb uses "$SERVER_NAME Git", or "Untitled Git" if
-   SERVER_NAME CGI environment variable is not set (e.g. if running
-   gitweb as standalone script).  [No default]
- * GITWEB_BASE_URL
-   Git base URLs used for URL to where fetch project from, i.e. full
-   URL is "$git_base_url/$project".  Shown on projects summary page.
-   Repository URL for project can be also configured per repository; this
-   takes precedence over URLs composed from base URL and a project name.
-   Note that you can setup multiple base URLs (for example one for
-   git:// protocol access, another for http:// access) from the gitweb
-   config file.  [No default]
- * GITWEB_CSS
-   Points to the location where you put gitweb.css on your web server
-   (or to be more generic, the URI of gitweb stylesheet).  Relative to the
-   base URI of gitweb.  Note that you can setup multiple stylesheets from
-   the gitweb config file.  [Default: gitweb.css]
- * GITWEB_LOGO
-   Points to the location where you put git-logo.png on your web server
-   (or to be more generic URI of logo, 72x27 size, displayed in top right
-   corner of each gitweb page, and used as logo for Atom feed).  Relative
-   to base URI of gitweb.  [Default: git-logo.png]
- * GITWEB_FAVICON
-   Points to the location where you put git-favicon.png on your web server
-   (or to be more generic URI of favicon, assumed to be image/png type;
-   web browsers that support favicons (website icons) may display them
-   in the browser's URL bar and next to site name in bookmarks).  Relative
-   to base URI of gitweb.  [Default: git-favicon.png]
- * GITWEB_CONFIG
-   This Perl file will be loaded using 'do' and can be used to override any
-   of the options above as well as some other options -- see the "Runtime
-   gitweb configuration" section below, and top of 'gitweb.cgi' for their
-   full list and description.  If the environment variable GITWEB_CONFIG
-   is set when gitweb.cgi is executed, then the file specified in the
-   environment variable will be loaded instead of the file specified
-   when gitweb.cgi was created.  [Default: gitweb_config.perl]
- * GITWEB_CONFIG_SYSTEM
-   This Perl file will be loaded using 'do' as a fallback if GITWEB_CONFIG
-   does not exist.  If the environment variable GITWEB_CONFIG_SYSTEM is set
-   when gitweb.cgi is executed, then the file specified in the environment
-   variable will be loaded instead of the file specified when gitweb.cgi was
-   created.  [Default: /etc/gitweb.conf]
+Building and installing gitweb is described in gitweb's INSTALL file
+(in 'gitweb/INSTALL').
 
 
 Runtime gitweb configuration
 ----------------------------
+Gitweb obtains configuration data from the following sources in the
+following order:
 
-You can adjust gitweb behaviour using the file specified in `GITWEB_CONFIG`
-(defaults to 'gitweb_config.perl' in the same directory as the CGI), and
-as a fallback `GITWEB_CONFIG_SYSTEM` (defaults to /etc/gitweb.conf).
+1. built-in values (some set during build stage),
+2. common system-wide configuration file (`GITWEB_CONFIG_COMMON`,
+   defaults to '/etc/gitweb-common.conf'),
+3. either per-instance configuration file (`GITWEB_CONFIG`, defaults to
+   'gitweb_config.perl' in the same directory as the installed gitweb),
+   or if it does not exists then system-wide configuration file
+   (`GITWEB_CONFIG_SYSTEM`, defaults to '/etc/gitweb.conf').
+
+Values obtained in later configuration files override values obtained earlier
+in above sequence.
+
+You can read defaults in system-wide GITWEB_CONFIG_SYSTEM from GITWEB_CONFIG
+by adding
+
+  read_config_file($GITWEB_CONFIG_SYSTEM);
+
+at very beginning of per-instance GITWEB_CONFIG file.  In this case
+settings in said per-instance file will override settings from
+system-wide configuration file.  Note that read_config_file checks
+itself that the $GITWEB_CONFIG_SYSTEM file exists.
+
 The most notable thing that is not configurable at compile time are the
 optional features, stored in the '%features' variable.
 
@@ -121,302 +50,22 @@
 in your `GITWEB_CONFIG` or per-project in `project.git/config` can be found
 as comments inside 'gitweb.cgi'.
 
-See also the "Gitweb config file" (with an example of config file), and
-the "Gitweb repositories" sections in INSTALL file for gitweb.
+See also gitweb.conf(5) manpage.
 
 
-The gitweb config file is a fragment of perl code. You can set variables
-using "our $variable = value"; text from "#" character until the end
-of a line is ignored. See perlsyn(1) man page for details.
+Web server configuration
+------------------------
+Gitweb can be run as CGI script, as legacy mod_perl application (using
+ModPerl::Registry), and as FastCGI script.  You can find some simple examples
+in "Example web server configuration" section in INSTALL file for gitweb (in
+gitweb/INSTALL).
 
-Below is the list of variables which you might want to set in gitweb config.
-See the top of 'gitweb.cgi' for the full list of variables and their
-descriptions.
-
-Gitweb config file variables
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-You can set, among others, the following variables in gitweb config files
-(with the exception of $projectroot and $projects_list this list does
-not include variables usually directly set during build):
- * $GIT
-   Core git executable to use.  By default set to "$GIT_BINDIR/git", which
-   in turn is by default set to "$(bindir)/git".  If you use git from binary
-   package, set this to "/usr/bin/git".  This can just be "git" if your
-   webserver has a sensible PATH.  If you have multiple git versions
-   installed it can be used to choose which one to use.
- * $version
-   Gitweb version, set automatically when creating gitweb.cgi from
-   gitweb.perl. You might want to modify it if you are running modified
-   gitweb.
- * $projectroot
-   Absolute filesystem path which will be prepended to project path;
-   the path to repository is $projectroot/$project.  Set to
-   $GITWEB_PROJECTROOT during installation.  This variable have to be
-   set correctly for gitweb to find repositories.
- * $projects_list
-   Source of projects list, either directory to scan, or text file
-   with list of repositories (in the "<URI-encoded repository path> SP
-   <URI-encoded repository owner>" line format; actually there can be
-   any sequence of whitespace in place of space (SP)).  Set to
-   $GITWEB_LIST during installation.  If empty, $projectroot is used
-   to scan for repositories.
- * $my_url, $my_uri
-   Full URL and absolute URL of gitweb script;
-   in earlier versions of gitweb you might have need to set those
-   variables, now there should be no need to do it.
- * $base_url
-   Base URL for relative URLs in pages generated by gitweb,
-   (e.g. $logo, $favicon, @stylesheets if they are relative URLs),
-   needed and used only for URLs with nonempty PATH_INFO via
-   <base href="$base_url>.  Usually gitweb sets its value correctly,
-   and there is no need to set this variable, e.g. to $my_uri or "/".
- * $home_link
-   Target of the home link on top of all pages (the first part of view
-   "breadcrumbs").  By default set to absolute URI of a page ($my_uri).
- * @stylesheets
-   List of URIs of stylesheets (relative to base URI of a page). You
-   might specify more than one stylesheet, for example use gitweb.css
-   as base, with site specific modifications in separate stylesheet
-   to make it easier to upgrade gitweb. You can add 'site' stylesheet
-   for example by using
-      push @stylesheets, "gitweb-site.css";
-   in the gitweb config file.
- * $logo_url, $logo_label
-   URI and label (title) of GIT logo link (or your site logo, if you choose
-   to use different logo image). By default they point to git homepage;
-   in the past they pointed to git documentation at www.kernel.org.
- * $projects_list_description_width
-   The width (in characters) of the projects list "Description" column.
-   Longer descriptions will be cut (trying to cut at word boundary);
-   full description is available as 'title' attribute (usually shown on
-   mouseover).  By default set to 25, which might be too small if you
-   use long project descriptions.
- * @git_base_url_list
-   List of git base URLs used for URL to where fetch project from, shown
-   in project summary page.  Full URL is "$git_base_url/$project".
-   You can setup multiple base URLs (for example one for  git:// protocol
-   access, and one for http:// "dumb" protocol access).  Note that per
-   repository configuration in 'cloneurl' file, or as values of gitweb.url
-   project config.
- * $default_blob_plain_mimetype
-   Default mimetype for blob_plain (raw) view, if mimetype checking
-   doesn't result in some other type; by default 'text/plain'.
- * $default_text_plain_charset
-   Default charset for text files. If not set, web server configuration
-   would be used.
- * $mimetypes_file
-   File to use for (filename extension based) guessing of MIME types before
-   trying /etc/mime.types. Path, if relative, is taken currently as
-   relative to the current git repository.
- * $fallback_encoding
-   Gitweb assumes this charset if line contains non-UTF-8 characters.
-   Fallback decoding is used without error checking, so it can be even
-   'utf-8'. Value must be valid encoding; see Encoding::Supported(3pm) man
-   page for a list.   By default 'latin1', aka. 'iso-8859-1'.
- * @diff_opts
-   Rename detection options for git-diff and git-diff-tree. By default
-   ('-M'); set it to ('-C') or ('-C', '-C') to also detect copies, or
-   set it to () if you don't want to have renames detection.
- * $prevent_xss
-   If true, some gitweb features are disabled to prevent content in
-   repositories from launching cross-site scripting (XSS) attacks.  Set this
-   to true if you don't trust the content of your repositories. The default
-   is false.
+See "Webserver configuration" and "Advanced web server setup" sections in
+gitweb(1) manpage.
 
 
-Projects list file format
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Instead of having gitweb find repositories by scanning filesystem starting
-from $projectroot (or $projects_list, if it points to directory), you can
-provide list of projects by setting $projects_list to a text file with list
-of projects (and some additional info).  This file uses the following
-format:
-
-One record (for project / repository) per line, whitespace separated fields;
-does not support (at least for now) lines continuation (newline escaping).
-Leading and trailing whitespace are ignored, any run of whitespace can be
-used as field separator (rules for Perl's "split(' ', $line)").  Keyed by
-the first field, which is project name, i.e. path to repository GIT_DIR
-relative to $projectroot.  Fields use modified URI encoding, defined in
-RFC 3986, section 2.1 (Percent-Encoding), or rather "Query string encoding"
-(see http://en.wikipedia.org/wiki/Query_string#URL_encoding), the difference
-being that SP (' ') can be encoded as '+' (and therefore '+' has to be also
-percent-encoded).  Reserved characters are: '%' (used for encoding), '+'
-(can be used to encode SPACE), all whitespace characters as defined in Perl,
-including SP, TAB and LF, (used to separate fields in a record).
-
-Currently list of fields is
- * <repository path>  - path to repository GIT_DIR, relative to $projectroot
- * <repository owner> - displayed as repository owner, preferably full name,
-                        or email, or both
-
-You can additionally use $projects_list file to limit which repositories
-are visible, and together with $strict_export to limit access to
-repositories (see "Gitweb repositories" section in gitweb/INSTALL).
-
-
-Per-repository gitweb configuration
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-You can also configure individual repositories shown in gitweb by creating
-file in the GIT_DIR of git repository, or by setting some repo configuration
-variable (in GIT_DIR/config).
-
-You can use the following files in repository:
- * README.html
-   A .html file (HTML fragment) which is included on the gitweb project
-   summary page inside <div> block element. You can use it for longer
-   description of a project, to provide links (for example to project's
-   homepage), etc. This is recognized only if XSS prevention is off
-   ($prevent_xss is false); a way to include a readme safely when XSS
-   prevention is on may be worked out in the future.
- * description (or gitweb.description)
-   Short (shortened by default to 25 characters in the projects list page)
-   single line description of a project (of a repository). Plain text file;
-   HTML will be escaped. By default set to
-     Unnamed repository; edit this file to name it for gitweb.
-   from the template during repository creation. You can use the
-   gitweb.description repo configuration variable, but the file takes
-   precedence.
- * cloneurl (or multiple-valued gitweb.url)
-   File with repository URL (used for clone and fetch), one per line.
-   Displayed in the project summary page. You can use multiple-valued
-   gitweb.url repository configuration variable for that, but the file
-   takes precedence.
- * gitweb.owner
-   You can use the gitweb.owner repository configuration variable to set
-   repository's owner. It is displayed in the project list and summary
-   page. If it's not set, filesystem directory's owner is used
-   (via GECOS field / real name field from getpwiud(3)).
- * various gitweb.* config variables (in config)
-   Read description of %feature hash for detailed list, and some
-   descriptions.
-
-
-Webserver configuration
------------------------
-
-If you want to have one URL for both gitweb and your http://
-repositories, you can configure apache like this:
-
-<VirtualHost *:80>
-    ServerName git.example.org
-    DocumentRoot /pub/git
-    SetEnv	GITWEB_CONFIG	/etc/gitweb.conf
-    RewriteEngine on
-    # make the front page an internal rewrite to the gitweb script
-    RewriteRule ^/$  /cgi-bin/gitweb.cgi
-    # make access for "dumb clients" work
-    RewriteRule ^/(.*\.git/(?!/?(HEAD|info|objects|refs)).*)?$ /cgi-bin/gitweb.cgi%{REQUEST_URI}  [L,PT]
-</VirtualHost>
-
-The above configuration expects your public repositories to live under
-/pub/git and will serve them as http://git.domain.org/dir-under-pub-git,
-both as cloneable GIT URL and as browseable gitweb interface.
-If you then start your git-daemon with --base-path=/pub/git --export-all
-then you can even use the git:// URL with exactly the same path.
-
-Setting the environment variable GITWEB_CONFIG will tell gitweb to use
-the named file (i.e. in this example /etc/gitweb.conf) as a
-configuration for gitweb.  Perl variables defined in here will
-override the defaults given at the head of the gitweb.perl (or
-gitweb.cgi).  Look at the comments in that file for information on
-which variables and what they mean.
-
-If you use the rewrite rules from the example you'll likely also need
-something like the following in your gitweb.conf (or gitweb_config.perl) file:
-
-  @stylesheets = ("/some/absolute/path/gitweb.css");
-  $my_uri = "/";
-  $home_link = "/";
-
-
-PATH_INFO usage
------------------------
-If you enable PATH_INFO usage in gitweb by putting
-
-   $feature{'pathinfo'}{'default'} = [1];
-
-in your gitweb.conf, it is possible to set up your server so that it
-consumes and produces URLs in the form
-
-http://git.example.com/project.git/shortlog/sometag
-
-by using a configuration such as the following, that assumes that
-/var/www/gitweb is the DocumentRoot of your webserver, and that it
-contains the gitweb.cgi script and complementary static files
-(stylesheet, favicon):
-
-<VirtualHost *:80>
-	ServerAlias git.example.com
-
-	DocumentRoot /var/www/gitweb
-
-	<Directory /var/www/gitweb>
-		Options ExecCGI
-		AddHandler cgi-script cgi
-
-		DirectoryIndex gitweb.cgi
-
-		RewriteEngine On
-		RewriteCond %{REQUEST_FILENAME} !-f
-		RewriteCond %{REQUEST_FILENAME} !-d
-		RewriteRule ^.* /gitweb.cgi/$0 [L,PT]
-	</Directory>
-</VirtualHost>
-
-The rewrite rule guarantees that existing static files will be properly
-served, whereas any other URL will be passed to gitweb as PATH_INFO
-parameter.
-
-Notice that in this case you don't need special settings for
-@stylesheets, $my_uri and $home_link, but you lose "dumb client" access
-to your project .git dirs. A possible workaround for the latter is the
-following: in your project root dir (e.g. /pub/git) have the projects
-named without a .git extension (e.g. /pub/git/project instead of
-/pub/git/project.git) and configure Apache as follows:
-
-<VirtualHost *:80>
-	ServerAlias git.example.com
-
-	DocumentRoot /var/www/gitweb
-
-	AliasMatch ^(/.*?)(\.git)(/.*)?$ /pub/git$1$3
-	<Directory /var/www/gitweb>
-		Options ExecCGI
-		AddHandler cgi-script cgi
-
-		DirectoryIndex gitweb.cgi
-
-		RewriteEngine On
-		RewriteCond %{REQUEST_FILENAME} !-f
-		RewriteCond %{REQUEST_FILENAME} !-d
-		RewriteRule ^.* /gitweb.cgi/$0 [L,PT]
-	</Directory>
-</VirtualHost>
-
-The additional AliasMatch makes it so that
-
-http://git.example.com/project.git
-
-will give raw access to the project's git dir (so that the project can
-be cloned), while
-
-http://git.example.com/project
-
-will provide human-friendly gitweb access.
-
-This solution is not 100% bulletproof, in the sense that if some project
-has a named ref (branch, tag) starting with 'git/', then paths such as
-
-http://git.example.com/project/command/abranch..git/abranch
-
-will fail with a 404 error.
-
-
-
+AUTHORS
+-------
 Originally written by:
   Kay Sievers <kay.sievers@vrfy.org>
 
diff --git a/gitweb/git-favicon.png b/gitweb/git-favicon.png
deleted file mode 100644
index de637c0..0000000
--- a/gitweb/git-favicon.png
+++ /dev/null
Binary files differ
diff --git a/gitweb/git-logo.png b/gitweb/git-logo.png
deleted file mode 100644
index 16ae8d5..0000000
--- a/gitweb/git-logo.png
+++ /dev/null
Binary files differ
diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css
deleted file mode 100644
index d05bc37..0000000
--- a/gitweb/gitweb.css
+++ /dev/null
@@ -1,533 +0,0 @@
-body {
-	font-family: sans-serif;
-	font-size: small;
-	border: solid #d9d8d1;
-	border-width: 1px;
-	margin: 10px;
-	background-color: #ffffff;
-	color: #000000;
-}
-
-a {
-	color: #0000cc;
-}
-
-a:hover, a:visited, a:active {
-	color: #880000;
-}
-
-span.cntrl {
-	border: dashed #aaaaaa;
-	border-width: 1px;
-	padding: 0px 2px 0px 2px;
-	margin:  0px 2px 0px 2px;
-}
-
-img.logo {
-	float: right;
-	border-width: 0px;
-}
-
-img.avatar {
-	vertical-align: middle;
-}
-
-div.page_header {
-	height: 25px;
-	padding: 8px;
-	font-size: 150%;
-	font-weight: bold;
-	background-color: #d9d8d1;
-}
-
-div.page_header a:visited, a.header {
-	color: #0000cc;
-}
-
-div.page_header a:hover {
-	color: #880000;
-}
-
-div.page_nav {
-	padding: 8px;
-}
-
-div.page_nav a:visited {
-	color: #0000cc;
-}
-
-div.page_path {
-	padding: 8px;
-	font-weight: bold;
-	border: solid #d9d8d1;
-	border-width: 0px 0px 1px;
-}
-
-div.page_footer {
-	height: 17px;
-	padding: 4px 8px;
-	background-color: #d9d8d1;
-}
-
-div.page_footer_text {
-	float: left;
-	color: #555555;
-	font-style: italic;
-}
-
-div.page_body {
-	padding: 8px;
-	font-family: monospace;
-}
-
-div.title, a.title {
-	display: block;
-	padding: 6px 8px;
-	font-weight: bold;
-	background-color: #edece6;
-	text-decoration: none;
-	color: #000000;
-}
-
-div.readme {
-	padding: 8px;
-}
-
-a.title:hover {
-	background-color: #d9d8d1;
-}
-
-div.title_text {
-	padding: 6px 0px;
-	border: solid #d9d8d1;
-	border-width: 0px 0px 1px;
-	font-family: monospace;
-}
-
-div.log_body {
-	padding: 8px 8px 8px 150px;
-}
-
-span.age {
-	position: relative;
-	float: left;
-	width: 142px;
-	font-style: italic;
-}
-
-span.signoff {
-	color: #888888;
-}
-
-div.log_link {
-	padding: 0px 8px;
-	font-size: 70%;
-	font-family: sans-serif;
-	font-style: normal;
-	position: relative;
-	float: left;
-	width: 136px;
-}
-
-div.list_head {
-	padding: 6px 8px 4px;
-	border: solid #d9d8d1;
-	border-width: 1px 0px 0px;
-	font-style: italic;
-}
-
-.author_date, .author {
-	font-style: italic;
-}
-
-div.author_date {
-	padding: 8px;
-	border: solid #d9d8d1;
-	border-width: 0px 0px 1px 0px;
-}
-
-a.list {
-	text-decoration: none;
-	color: #000000;
-}
-
-a.subject, a.name {
-	font-weight: bold;
-}
-
-table.tags a.subject {
-	font-weight: normal;
-}
-
-a.list:hover {
-	text-decoration: underline;
-	color: #880000;
-}
-
-a.text {
-	text-decoration: none;
-	color: #0000cc;
-}
-
-a.text:visited {
-	text-decoration: none;
-	color: #880000;
-}
-
-a.text:hover {
-	text-decoration: underline;
-	color: #880000;
-}
-
-table {
-	padding: 8px 4px;
-	border-spacing: 0;
-}
-
-table.diff_tree {
-	font-family: monospace;
-}
-
-table.combined.diff_tree th {
-	text-align: center;
-}
-
-table.combined.diff_tree td {
-	padding-right: 24px;
-}
-
-table.combined.diff_tree th.link,
-table.combined.diff_tree td.link {
-	padding: 0px 2px;
-}
-
-table.combined.diff_tree td.nochange a {
-	color: #6666ff;
-}
-
-table.combined.diff_tree td.nochange a:hover,
-table.combined.diff_tree td.nochange a:visited {
-	color: #d06666;
-}
-
-table.blame {
-	border-collapse: collapse;
-}
-
-table.blame td {
-	padding: 0px 5px;
-	font-size: 100%;
-	vertical-align: top;
-}
-
-th {
-	padding: 2px 5px;
-	font-size: 100%;
-	text-align: left;
-}
-
-tr.light:hover {
-	background-color: #edece6;
-}
-
-tr.dark {
-	background-color: #f6f6f0;
-}
-
-tr.dark2 {
-	background-color: #f6f6f0;
-}
-
-tr.dark:hover {
-	background-color: #edece6;
-}
-
-td {
-	padding: 2px 5px;
-	font-size: 100%;
-	vertical-align: top;
-}
-
-td.link, td.selflink {
-	padding: 2px 5px;
-	font-family: sans-serif;
-	font-size: 70%;
-}
-
-td.selflink {
-	padding-right: 0px;
-}
-
-td.sha1 {
-	font-family: monospace;
-}
-
-td.error {
-	color: red;
-	background-color: yellow;
-}
-
-td.current_head {
-	text-decoration: underline;
-}
-
-table.diff_tree span.file_status.new {
-	color: #008000;
-}
-
-table.diff_tree span.file_status.deleted {
-	color: #c00000;
-}
-
-table.diff_tree span.file_status.moved,
-table.diff_tree span.file_status.mode_chnge {
-	color: #777777;
-}
-
-table.diff_tree span.file_status.copied {
-  color: #70a070;
-}
-
-/* noage: "No commits" */
-table.project_list td.noage {
-	color: #808080;
-	font-style: italic;
-}
-
-/* age2: 60*60*24*2 <= age */
-table.project_list td.age2, table.blame td.age2 {
-	font-style: italic;
-}
-
-/* age1: 60*60*2 <= age < 60*60*24*2 */
-table.project_list td.age1 {
-	color: #009900;
-	font-style: italic;
-}
-
-table.blame td.age1 {
-	color: #009900;
-	background: transparent;
-}
-
-/* age0: age < 60*60*2 */
-table.project_list td.age0 {
-	color: #009900;
-	font-style: italic;
-	font-weight: bold;
-}
-
-table.blame td.age0 {
-	color: #009900;
-	background: transparent;
-	font-weight: bold;
-}
-
-td.pre, div.pre, div.diff {
-	font-family: monospace;
-	font-size: 12px;
-	white-space: pre;
-}
-
-td.mode {
-	font-family: monospace;
-}
-
-/* styling of diffs (patchsets): commitdiff and blobdiff views */
-div.diff.header,
-div.diff.extended_header {
-	white-space: normal;
-}
-
-div.diff.header {
-	font-weight: bold;
-
-	background-color: #edece6;
-
-	margin-top: 4px;
-	padding: 4px 0px 2px 0px;
-	border: solid #d9d8d1;
-	border-width: 1px 0px 1px 0px;
-}
-
-div.diff.header a.path {
-	text-decoration: underline;
-}
-
-div.diff.extended_header,
-div.diff.extended_header a.path,
-div.diff.extended_header a.hash {
-	color: #777777;
-}
-
-div.diff.extended_header .info {
-	color: #b0b0b0;
-}
-
-div.diff.extended_header {
-	background-color: #f6f5ee;
-	padding: 2px 0px 2px 0px;
-}
-
-div.diff a.list,
-div.diff a.path,
-div.diff a.hash {
-	text-decoration: none;
-}
-
-div.diff a.list:hover,
-div.diff a.path:hover,
-div.diff a.hash:hover {
-	text-decoration: underline;
-}
-
-div.diff.to_file a.path,
-div.diff.to_file {
-	color: #007000;
-}
-
-div.diff.add {
-	color: #008800;
-}
-
-div.diff.from_file a.path,
-div.diff.from_file {
-	color: #aa0000;
-}
-
-div.diff.rem {
-	color: #cc0000;
-}
-
-div.diff.chunk_header a,
-div.diff.chunk_header {
-	color: #990099;
-}
-
-div.diff.chunk_header {
-	border: dotted #ffe0ff;
-	border-width: 1px 0px 0px 0px;
-	margin-top: 2px;
-}
-
-div.diff.chunk_header span.chunk_info {
-	background-color: #ffeeff;
-}
-
-div.diff.chunk_header span.section {
-	color: #aa22aa;
-}
-
-div.diff.incomplete {
-	color: #cccccc;
-}
-
-div.diff.nodifferences {
-	font-weight: bold;
-	color: #600000;
-}
-
-div.index_include {
-	border: solid #d9d8d1;
-	border-width: 0px 0px 1px;
-	padding: 12px 8px;
-}
-
-div.search {
-	font-size: 100%;
-	font-weight: normal;
-	margin: 4px 8px;
-	float: right;
-	top: 56px;
-	right: 12px
-}
-
-p.projsearch {
-	text-align: center;
-}
-
-td.linenr {
-	text-align: right;
-}
-
-a.linenr {
-	color: #999999;
-	text-decoration: none
-}
-
-a.rss_logo {
-	float: right;
-	padding: 3px 0px;
-	width: 35px;
-	line-height: 10px;
-	border: 1px solid;
-	border-color: #fcc7a5 #7d3302 #3e1a01 #ff954e;
-	color: #ffffff;
-	background-color: #ff6600;
-	font-weight: bold;
-	font-family: sans-serif;
-	font-size: 70%;
-	text-align: center;
-	text-decoration: none;
-}
-
-a.rss_logo:hover {
-	background-color: #ee5500;
-}
-
-a.rss_logo.generic {
-	background-color: #ff8800;
-}
-
-a.rss_logo.generic:hover {
-	background-color: #ee7700;
-}
-
-span.refs span {
-	padding: 0px 4px;
-	font-size: 70%;
-	font-weight: normal;
-	border: 1px solid;
-	background-color: #ffaaff;
-	border-color: #ffccff #ff00ee #ff00ee #ffccff;
-}
-
-span.refs span a {
-	text-decoration: none;
-	color: inherit;
-}
-
-span.refs span a:hover {
-	text-decoration: underline;
-}
-
-span.refs span.indirect {
-	font-style: italic;
-}
-
-span.refs span.ref {
-	background-color: #aaaaff;
-	border-color: #ccccff #0033cc #0033cc #ccccff;
-}
-
-span.refs span.tag {
-	background-color: #ffffaa;
-	border-color: #ffffcc #ffee00 #ffee00 #ffffcc;
-}
-
-span.refs span.head {
-	background-color: #aaffaa;
-	border-color: #ccffcc #00cc33 #00cc33 #ccffcc;
-}
-
-span.atnight {
-	color: #cc0000;
-}
-
-span.match {
-	color: #e00000;
-}
-
-div.binary {
-	font-style: italic;
-}
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 37120a3..3d6a705 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -7,49 +7,62 @@
 #
 # This program is licensed under the GPLv2
 
+use 5.008;
 use strict;
 use warnings;
 use CGI qw(:standard :escapeHTML -nosticky);
 use CGI::Util qw(unescape);
-use CGI::Carp qw(fatalsToBrowser);
+use CGI::Carp qw(fatalsToBrowser set_message);
 use Encode;
 use Fcntl ':mode';
 use File::Find qw();
 use File::Basename qw(basename);
+use Time::HiRes qw(gettimeofday tv_interval);
 binmode STDOUT, ':utf8';
 
+our $t0 = [ gettimeofday() ];
+our $number_of_git_cmds = 0;
+
 BEGIN {
 	CGI->compile() if $ENV{'MOD_PERL'};
 }
 
-our $cgi = new CGI;
 our $version = "++GIT_VERSION++";
-our $my_url = $cgi->url();
-our $my_uri = $cgi->url(-absolute => 1);
 
-# Base URL for relative URLs in gitweb ($logo, $favicon, ...),
-# needed and used only for URLs with nonempty PATH_INFO
-our $base_url = $my_url;
+our ($my_url, $my_uri, $base_url, $path_info, $home_link);
+sub evaluate_uri {
+	our $cgi;
 
-# When the script is used as DirectoryIndex, the URL does not contain the name
-# of the script file itself, and $cgi->url() fails to strip PATH_INFO, so we
-# have to do it ourselves. We make $path_info global because it's also used
-# later on.
-#
-# Another issue with the script being the DirectoryIndex is that the resulting
-# $my_url data is not the full script URL: this is good, because we want
-# generated links to keep implying the script name if it wasn't explicitly
-# indicated in the URL we're handling, but it means that $my_url cannot be used
-# as base URL.
-# Therefore, if we needed to strip PATH_INFO, then we know that we have
-# to build the base URL ourselves:
-our $path_info = $ENV{"PATH_INFO"};
-if ($path_info) {
-	if ($my_url =~ s,\Q$path_info\E$,, &&
-	    $my_uri =~ s,\Q$path_info\E$,, &&
-	    defined $ENV{'SCRIPT_NAME'}) {
-		$base_url = $cgi->url(-base => 1) . $ENV{'SCRIPT_NAME'};
+	our $my_url = $cgi->url();
+	our $my_uri = $cgi->url(-absolute => 1);
+
+	# Base URL for relative URLs in gitweb ($logo, $favicon, ...),
+	# needed and used only for URLs with nonempty PATH_INFO
+	our $base_url = $my_url;
+
+	# When the script is used as DirectoryIndex, the URL does not contain the name
+	# of the script file itself, and $cgi->url() fails to strip PATH_INFO, so we
+	# have to do it ourselves. We make $path_info global because it's also used
+	# later on.
+	#
+	# Another issue with the script being the DirectoryIndex is that the resulting
+	# $my_url data is not the full script URL: this is good, because we want
+	# generated links to keep implying the script name if it wasn't explicitly
+	# indicated in the URL we're handling, but it means that $my_url cannot be used
+	# as base URL.
+	# Therefore, if we needed to strip PATH_INFO, then we know that we have
+	# to build the base URL ourselves:
+	our $path_info = decode_utf8($ENV{"PATH_INFO"});
+	if ($path_info) {
+		if ($my_url =~ s,\Q$path_info\E$,, &&
+		    $my_uri =~ s,\Q$path_info\E$,, &&
+		    defined $ENV{'SCRIPT_NAME'}) {
+			$base_url = $cgi->url(-base => 1) . $ENV{'SCRIPT_NAME'};
+		}
 	}
+
+	# target of the home link on top of all pages
+	our $home_link = $my_uri || "/";
 }
 
 # core git executable to use
@@ -64,9 +77,6 @@
 # the number is relative to the projectroot
 our $project_maxdepth = "++GITWEB_PROJECT_MAXDEPTH++";
 
-# target of the home link on top of all pages
-our $home_link = $my_uri || "/";
-
 # string of the home link on top of all pages
 our $home_link_str = "++GITWEB_HOME_LINK_STR++";
 
@@ -75,6 +85,8 @@
 our $site_name = "++GITWEB_SITENAME++"
                  || ($ENV{'SERVER_NAME'} || "Untitled") . " Git";
 
+# html snippet to include in the <head> section of each page
+our $site_html_head_string = "++GITWEB_SITE_HTML_HEAD_STRING++";
 # filename of html text to include at top of each page
 our $site_header = "++GITWEB_SITE_HEADER++";
 # html text to include at home page
@@ -90,6 +102,8 @@
 our $logo = "++GITWEB_LOGO++";
 # URI of GIT favicon, assumed to be image/png type
 our $favicon = "++GITWEB_FAVICON++";
+# URI of gitweb.js (JavaScript code for gitweb)
+our $javascript = "++GITWEB_JS++";
 
 # URI and label (title) of GIT logo link
 #our $logo_url = "http://www.kernel.org/pub/software/scm/git/docs/";
@@ -103,6 +117,14 @@
 # the width (in characters) of the projects list "Description" column
 our $projects_list_description_width = 25;
 
+# group projects by category on the projects list
+# (enabled if this variable evaluates to true)
+our $projects_list_group_categories = 0;
+
+# default category if none specified
+# (leave the empty string for no category)
+our $project_list_default_category = "";
+
 # default order of projects list
 # valid values are none, project, descr, owner, and age
 our $default_projects_order = "project";
@@ -111,6 +133,12 @@
 # (only effective if this variable evaluates to true)
 our $export_ok = "++GITWEB_EXPORT_OK++";
 
+# don't generate age column on the projects list page
+our $omit_age_column = 0;
+
+# don't generate information about owners of repositories
+our $omit_owner=0;
+
 # show repository only if this subroutine returns true
 # when given the path to the project, for example:
 #    sub { return -e "$_[0]/git-daemon-export-ok"; }
@@ -152,6 +180,12 @@
 # the gitweb domain.
 our $prevent_xss = 0;
 
+# Path to the highlight executable to use (must be the one from
+# http://www.andre-simon.de due to assumptions about parameters and output).
+# Useful if highlight is not installed on your webserver's PATH.
+# [Default: highlight]
+our $highlight_bin = "++HIGHLIGHT_BIN++";
+
 # information about snapshot formats that gitweb is capable of serving
 our %known_snapshot_formats = (
 	# name => {
@@ -160,14 +194,15 @@
 	# 	'suffix' => filename suffix,
 	# 	'format' => --format for git-archive,
 	# 	'compressor' => [compressor command and arguments]
-	# 	                (array reference, optional)}
+	# 	                (array reference, optional)
+	# 	'disabled' => boolean (optional)}
 	#
 	'tgz' => {
 		'display' => 'tar.gz',
 		'type' => 'application/x-gzip',
 		'suffix' => '.tar.gz',
 		'format' => 'tar',
-		'compressor' => ['gzip']},
+		'compressor' => ['gzip', '-n']},
 
 	'tbz2' => {
 		'display' => 'tar.bz2',
@@ -176,6 +211,14 @@
 		'format' => 'tar',
 		'compressor' => ['bzip2']},
 
+	'txz' => {
+		'display' => 'tar.xz',
+		'type' => 'application/x-xz',
+		'suffix' => '.tar.xz',
+		'format' => 'tar',
+		'compressor' => ['xz'],
+		'disabled' => 1},
+
 	'zip' => {
 		'display' => 'zip',
 		'type' => 'application/x-zip',
@@ -188,6 +231,7 @@
 our %known_snapshot_format_aliases = (
 	'gzip'  => 'tgz',
 	'bzip2' => 'tbz2',
+	'xz'    => 'txz',
 
 	# backward compatibility: legacy gitweb config support
 	'x-gzip' => undef, 'gz' => undef,
@@ -203,6 +247,36 @@
 	'double'  => 32
 );
 
+# Used to set the maximum load that we will still respond to gitweb queries.
+# If server load exceed this value then return "503 server busy" error.
+# If gitweb cannot determined server load, it is taken to be 0.
+# Leave it undefined (or set to 'undef') to turn off load checking.
+our $maxload = 300;
+
+# configuration for 'highlight' (http://www.andre-simon.de/)
+# match by basename
+our %highlight_basename = (
+	#'Program' => 'py',
+	#'Library' => 'py',
+	'SConstruct' => 'py', # SCons equivalent of Makefile
+	'Makefile' => 'make',
+);
+# match by extension
+our %highlight_ext = (
+	# main extensions, defining name of syntax;
+	# see files in /usr/share/highlight/langDefs/ directory
+	map { $_ => $_ }
+		qw(py c cpp rb java css php sh pl js tex bib xml awk bat ini spec tcl sql make),
+	# alternate extensions, see /etc/highlight/filetypes.conf
+	'h' => 'c',
+	map { $_ => 'sh'  } qw(bash zsh ksh),
+	map { $_ => 'cpp' } qw(cxx c++ cc),
+	map { $_ => 'php' } qw(php3 php4 php5 phps),
+	map { $_ => 'pl'  } qw(perl pm), # perhaps also 'cgi'
+	map { $_ => 'make'} qw(mak mk),
+	map { $_ => 'xml' } qw(xhtml html htm),
+);
+
 # You define site-wide feature defaults here; override them with
 # $GITWEB_CONFIG as necessary.
 our %feature = (
@@ -216,7 +290,7 @@
 	# return value of feature-sub indicates if to enable specified feature
 	#
 	# if there is no 'sub' key (no feature-sub), then feature cannot be
-	# overriden
+	# overridden
 	#
 	# use gitweb_get_feature(<feature>) to retrieve the <feature> value
 	# (an array) or gitweb_check_feature(<feature>) to check if <feature>
@@ -255,6 +329,10 @@
 	# Enable text search, which will list the commits which match author,
 	# committer or commit text to a given string.  Enabled by default.
 	# Project specific override is not supported.
+	#
+	# Note that this controls all search features, which means that if
+	# it is disabled, then 'grep' and 'pickaxe' search would also be
+	# disabled.
 	'search' => {
 		'override' => 0,
 		'default' => [1]},
@@ -262,6 +340,7 @@
 	# Enable grep search, which will list the files in currently selected
 	# tree containing the given string. Enabled by default. This can be
 	# potentially CPU-intensive, of course.
+	# Note that you need to have 'search' feature enabled too.
 
 	# To enable system wide have in $GITWEB_CONFIG
 	# $feature{'grep'}{'default'} = [1];
@@ -276,6 +355,7 @@
 	# Enable the pickaxe search, which will list the commits that modified
 	# a given string in a file. This can be practical and quite faster
 	# alternative to 'blame', but still potentially CPU-intensive.
+	# Note that you need to have 'search' feature enabled too.
 
 	# To enable system wide have in $GITWEB_CONFIG
 	# $feature{'pickaxe'}{'default'} = [1];
@@ -287,6 +367,19 @@
 		'override' => 0,
 		'default' => [1]},
 
+	# Enable showing size of blobs in a 'tree' view, in a separate
+	# column, similar to what 'ls -l' does.  This cost a bit of IO.
+
+	# To disable system wide have in $GITWEB_CONFIG
+	# $feature{'show-sizes'}{'default'} = [0];
+	# To have project specific config enable override in $GITWEB_CONFIG
+	# $feature{'show-sizes'}{'override'} = 1;
+	# and in project config gitweb.showsizes = 0|1;
+	'show-sizes' => {
+		'sub' => sub { feature_bool('showsizes', @_) },
+		'override' => 0,
+		'default' => [1]},
+
 	# Make gitweb use an alternative format of the URLs which can be
 	# more readable and natural-looking: project name is embedded
 	# directly in the path and the query string contains other
@@ -341,20 +434,23 @@
 		'override' => 0,
 		'default' => []},
 
-	# Allow gitweb scan project content tags described in ctags/
-	# of project repository, and display the popular Web 2.0-ish
-	# "tag cloud" near the project list. Note that this is something
-	# COMPLETELY different from the normal Git tags.
+	# Allow gitweb scan project content tags of project repository,
+	# and display the popular Web 2.0-ish "tag cloud" near the projects
+	# list.  Note that this is something COMPLETELY different from the
+	# normal Git tags.
 
 	# gitweb by itself can show existing tags, but it does not handle
-	# tagging itself; you need an external application for that.
-	# For an example script, check Girocco's cgi/tagproj.cgi.
+	# tagging itself; you need to do it externally, outside gitweb.
+	# The format is described in git_get_project_ctags() subroutine.
 	# You may want to install the HTML::TagCloud Perl module to get
 	# a pretty tag cloud instead of just a list of tags.
 
 	# To enable system wide have in $GITWEB_CONFIG
-	# $feature{'ctags'}{'default'} = ['path_to_tag_script'];
+	# $feature{'ctags'}{'default'} = [1];
 	# Project specific override is not supported.
+
+	# In the future whether ctags editing is enabled might depend
+	# on the value, but using 1 should always mean no editing of ctags.
 	'ctags' => {
 		'override' => 0,
 		'default' => [0]},
@@ -394,6 +490,57 @@
 		'sub' => \&feature_avatar,
 		'override' => 0,
 		'default' => ['']},
+
+	# Enable displaying how much time and how many git commands
+	# it took to generate and display page.  Disabled by default.
+	# Project specific override is not supported.
+	'timed' => {
+		'override' => 0,
+		'default' => [0]},
+
+	# Enable turning some links into links to actions which require
+	# JavaScript to run (like 'blame_incremental').  Not enabled by
+	# default.  Project specific override is currently not supported.
+	'javascript-actions' => {
+		'override' => 0,
+		'default' => [0]},
+
+	# Enable and configure ability to change common timezone for dates
+	# in gitweb output via JavaScript.  Enabled by default.
+	# Project specific override is not supported.
+	'javascript-timezone' => {
+		'override' => 0,
+		'default' => [
+			'local',     # default timezone: 'utc', 'local', or '(-|+)HHMM' format,
+			             # or undef to turn off this feature
+			'gitweb_tz', # name of cookie where to store selected timezone
+			'datetime',  # CSS class used to mark up dates for manipulation
+		]},
+
+	# Syntax highlighting support. This is based on Daniel Svensson's
+	# and Sham Chukoury's work in gitweb-xmms2.git.
+	# It requires the 'highlight' program present in $PATH,
+	# and therefore is disabled by default.
+
+	# To enable system wide have in $GITWEB_CONFIG
+	# $feature{'highlight'}{'default'} = [1];
+
+	'highlight' => {
+		'sub' => sub { feature_bool('highlight', @_) },
+		'override' => 0,
+		'default' => [0]},
+
+	# Enable displaying of remote heads in the heads list
+
+	# To enable system wide have in $GITWEB_CONFIG
+	# $feature{'remote_heads'}{'default'} = [1];
+	# To have project specific config enable override in $GITWEB_CONFIG
+	# $feature{'remote_heads'}{'override'} = 1;
+	# and in project config gitweb.remote_heads = 0|1;
+	'remote_heads' => {
+		'sub' => sub { feature_bool('remote_heads', @_) },
+		'override' => 0,
+		'default' => [0]},
 );
 
 sub gitweb_get_feature {
@@ -403,9 +550,13 @@
 		$feature{$name}{'sub'},
 		$feature{$name}{'override'},
 		@{$feature{$name}{'default'}});
-	if (!$override) { return @defaults; }
+	# project specific override is possible only if we have project
+	our $git_dir; # global variable, declared later
+	if (!$override || !defined $git_dir) {
+		return @defaults;
+	}
 	if (!defined $sub) {
-		warn "feature $name is not overrideable";
+		warn "feature $name is not overridable";
 		return @defaults;
 	}
 	return $sub->(@defaults);
@@ -494,21 +645,90 @@
 		exists $known_snapshot_format_aliases{$_} ?
 		       $known_snapshot_format_aliases{$_} : $_} @fmts;
 	@fmts = grep {
-		exists $known_snapshot_formats{$_} } @fmts;
+		exists $known_snapshot_formats{$_} &&
+		!$known_snapshot_formats{$_}{'disabled'}} @fmts;
 }
 
-our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++";
-if (-e $GITWEB_CONFIG) {
-	do $GITWEB_CONFIG;
-} else {
+# If it is set to code reference, it is code that it is to be run once per
+# request, allowing updating configurations that change with each request,
+# while running other code in config file only once.
+#
+# Otherwise, if it is false then gitweb would process config file only once;
+# if it is true then gitweb config would be run for each request.
+our $per_request_config = 1;
+
+# read and parse gitweb config file given by its parameter.
+# returns true on success, false on recoverable error, allowing
+# to chain this subroutine, using first file that exists.
+# dies on errors during parsing config file, as it is unrecoverable.
+sub read_config_file {
+	my $filename = shift;
+	return unless defined $filename;
+	# die if there are errors parsing config file
+	if (-e $filename) {
+		do $filename;
+		die $@ if $@;
+		return 1;
+	}
+	return;
+}
+
+our ($GITWEB_CONFIG, $GITWEB_CONFIG_SYSTEM, $GITWEB_CONFIG_COMMON);
+sub evaluate_gitweb_config {
+	our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++";
 	our $GITWEB_CONFIG_SYSTEM = $ENV{'GITWEB_CONFIG_SYSTEM'} || "++GITWEB_CONFIG_SYSTEM++";
-	do $GITWEB_CONFIG_SYSTEM if -e $GITWEB_CONFIG_SYSTEM;
+	our $GITWEB_CONFIG_COMMON = $ENV{'GITWEB_CONFIG_COMMON'} || "++GITWEB_CONFIG_COMMON++";
+
+	# Protect agains duplications of file names, to not read config twice.
+	# Only one of $GITWEB_CONFIG and $GITWEB_CONFIG_SYSTEM is used, so
+	# there possibility of duplication of filename there doesn't matter.
+	$GITWEB_CONFIG = ""        if ($GITWEB_CONFIG eq $GITWEB_CONFIG_COMMON);
+	$GITWEB_CONFIG_SYSTEM = "" if ($GITWEB_CONFIG_SYSTEM eq $GITWEB_CONFIG_COMMON);
+
+	# Common system-wide settings for convenience.
+	# Those settings can be ovverriden by GITWEB_CONFIG or GITWEB_CONFIG_SYSTEM.
+	read_config_file($GITWEB_CONFIG_COMMON);
+
+	# Use first config file that exists.  This means use the per-instance
+	# GITWEB_CONFIG if exists, otherwise use GITWEB_SYSTEM_CONFIG.
+	read_config_file($GITWEB_CONFIG) and return;
+	read_config_file($GITWEB_CONFIG_SYSTEM);
+}
+
+# Get loadavg of system, to compare against $maxload.
+# Currently it requires '/proc/loadavg' present to get loadavg;
+# if it is not present it returns 0, which means no load checking.
+sub get_loadavg {
+	if( -e '/proc/loadavg' ){
+		open my $fd, '<', '/proc/loadavg'
+			or return 0;
+		my @load = split(/\s+/, scalar <$fd>);
+		close $fd;
+
+		# The first three columns measure CPU and IO utilization of the last one,
+		# five, and 10 minute periods.  The fourth column shows the number of
+		# currently running processes and the total number of processes in the m/n
+		# format.  The last column displays the last process ID used.
+		return $load[0] || 0;
+	}
+	# additional checks for load average should go here for things that don't export
+	# /proc/loadavg
+
+	return 0;
 }
 
 # version of the core git binary
-our $git_version = qx("$GIT" --version) =~ m/git version (.*)$/ ? $1 : "unknown";
+our $git_version;
+sub evaluate_git_version {
+	our $git_version = qx("$GIT" --version) =~ m/git version (.*)$/ ? $1 : "unknown";
+	$number_of_git_cmds++;
+}
 
-$projects_list ||= $projectroot;
+sub check_loadavg {
+	if (defined $maxload && get_loadavg() > $maxload) {
+		die_error(503, "The load average on the server is too high");
+	}
+}
 
 # ======================================================================
 # input validation and dispatch
@@ -544,12 +764,19 @@
 	snapshot_format => "sf",
 	extra_options => "opt",
 	search_use_regexp => "sr",
+	ctag => "by_tag",
+	diff_style => "ds",
+	project_filter => "pf",
+	# this must be last entry (for manipulation from JavaScript)
+	javascript => "js"
 );
 our %cgi_param_mapping = @cgi_param_mapping;
 
 # we will also need to know the possible actions, for validation
 our %actions = (
 	"blame" => \&git_blame,
+	"blame_incremental" => \&git_blame_incremental,
+	"blame_data" => \&git_blame_data,
 	"blobdiff" => \&git_blobdiff,
 	"blobdiff_plain" => \&git_blobdiff_plain,
 	"blob" => \&git_blob,
@@ -563,6 +790,7 @@
 	"log" => \&git_log,
 	"patch" => \&git_patch,
 	"patches" => \&git_patches,
+	"remotes" => \&git_remotes,
 	"rss" => \&git_rss,
 	"atom" => \&git_atom,
 	"search" => \&git_search,
@@ -590,11 +818,15 @@
 # should be single values, but opt can be an array. We should probably
 # build an array of parameters that can be multi-valued, but since for the time
 # being it's only this one, we just single it out
-while (my ($name, $symbol) = each %cgi_param_mapping) {
-	if ($symbol eq 'opt') {
-		$input_params{$name} = [ $cgi->param($symbol) ];
-	} else {
-		$input_params{$name} = $cgi->param($symbol);
+sub evaluate_query_params {
+	our $cgi;
+
+	while (my ($name, $symbol) = each %cgi_param_mapping) {
+		if ($symbol eq 'opt') {
+			$input_params{$name} = [ map { decode_utf8($_) } $cgi->param($symbol) ];
+		} else {
+			$input_params{$name} = decode_utf8($cgi->param($symbol));
+		}
 	}
 }
 
@@ -633,10 +865,10 @@
 		'history',
 	);
 
-	# we want to catch
+	# we want to catch, among others
 	# [$hash_parent_base[:$file_parent]..]$hash_parent[:$file_name]
 	my ($parentrefname, $parentpathname, $refname, $pathname) =
-		($path_info =~ /^(?:(.+?)(?::(.+))?\.\.)?(.+?)(?::(.+))?$/);
+		($path_info =~ /^(?:(.+?)(?::(.+))?\.\.)?([^:]+?)?(?::(.+))?$/);
 
 	# first, analyze the 'current' part
 	if (defined $pathname) {
@@ -672,8 +904,15 @@
 		# hash_base instead. It should also be noted that hand-crafted
 		# links having 'history' as an action and no pathname or hash
 		# set will fail, but that happens regardless of PATH_INFO.
-		$input_params{'action'} ||= "shortlog";
-		if (grep { $_ eq $input_params{'action'} } @wants_base) {
+		if (defined $parentrefname) {
+			# if there is parent let the default be 'shortlog' action
+			# (for http://git.example.com/repo.git/A..B links); if there
+			# is no parent, dispatch will detect type of object and set
+			# action appropriately if required (if action is not set)
+			$input_params{'action'} ||= "shortlog";
+		}
+		if ($input_params{'action'} &&
+		    grep { $_ eq $input_params{'action'} } @wants_base) {
 			$input_params{'hash_base'} ||= $refname;
 		} else {
 			$input_params{'hash'} ||= $refname;
@@ -741,157 +980,316 @@
 		}
 	}
 }
-evaluate_path_info();
 
-our $action = $input_params{'action'};
-if (defined $action) {
-	if (!validate_action($action)) {
-		die_error(400, "Invalid action parameter");
+our ($action, $project, $file_name, $file_parent, $hash, $hash_parent, $hash_base,
+     $hash_parent_base, @extra_options, $page, $searchtype, $search_use_regexp,
+     $searchtext, $search_regexp, $project_filter);
+sub evaluate_and_validate_params {
+	our $action = $input_params{'action'};
+	if (defined $action) {
+		if (!validate_action($action)) {
+			die_error(400, "Invalid action parameter");
+		}
 	}
-}
 
-# parameters which are pathnames
-our $project = $input_params{'project'};
-if (defined $project) {
-	if (!validate_project($project)) {
-		undef $project;
-		die_error(404, "No such project");
+	# parameters which are pathnames
+	our $project = $input_params{'project'};
+	if (defined $project) {
+		if (!validate_project($project)) {
+			undef $project;
+			die_error(404, "No such project");
+		}
 	}
-}
 
-our $file_name = $input_params{'file_name'};
-if (defined $file_name) {
-	if (!validate_pathname($file_name)) {
-		die_error(400, "Invalid file parameter");
+	our $project_filter = $input_params{'project_filter'};
+	if (defined $project_filter) {
+		if (!validate_pathname($project_filter)) {
+			die_error(404, "Invalid project_filter parameter");
+		}
 	}
-}
 
-our $file_parent = $input_params{'file_parent'};
-if (defined $file_parent) {
-	if (!validate_pathname($file_parent)) {
-		die_error(400, "Invalid file parent parameter");
+	our $file_name = $input_params{'file_name'};
+	if (defined $file_name) {
+		if (!validate_pathname($file_name)) {
+			die_error(400, "Invalid file parameter");
+		}
 	}
-}
 
-# parameters which are refnames
-our $hash = $input_params{'hash'};
-if (defined $hash) {
-	if (!validate_refname($hash)) {
-		die_error(400, "Invalid hash parameter");
+	our $file_parent = $input_params{'file_parent'};
+	if (defined $file_parent) {
+		if (!validate_pathname($file_parent)) {
+			die_error(400, "Invalid file parent parameter");
+		}
 	}
-}
 
-our $hash_parent = $input_params{'hash_parent'};
-if (defined $hash_parent) {
-	if (!validate_refname($hash_parent)) {
-		die_error(400, "Invalid hash parent parameter");
+	# parameters which are refnames
+	our $hash = $input_params{'hash'};
+	if (defined $hash) {
+		if (!validate_refname($hash)) {
+			die_error(400, "Invalid hash parameter");
+		}
 	}
-}
 
-our $hash_base = $input_params{'hash_base'};
-if (defined $hash_base) {
-	if (!validate_refname($hash_base)) {
-		die_error(400, "Invalid hash base parameter");
+	our $hash_parent = $input_params{'hash_parent'};
+	if (defined $hash_parent) {
+		if (!validate_refname($hash_parent)) {
+			die_error(400, "Invalid hash parent parameter");
+		}
 	}
-}
 
-our @extra_options = @{$input_params{'extra_options'}};
-# @extra_options is always defined, since it can only be (currently) set from
-# CGI, and $cgi->param() returns the empty array in array context if the param
-# is not set
-foreach my $opt (@extra_options) {
-	if (not exists $allowed_options{$opt}) {
-		die_error(400, "Invalid option parameter");
+	our $hash_base = $input_params{'hash_base'};
+	if (defined $hash_base) {
+		if (!validate_refname($hash_base)) {
+			die_error(400, "Invalid hash base parameter");
+		}
 	}
-	if (not grep(/^$action$/, @{$allowed_options{$opt}})) {
-		die_error(400, "Invalid option parameter for this action");
-	}
-}
 
-our $hash_parent_base = $input_params{'hash_parent_base'};
-if (defined $hash_parent_base) {
-	if (!validate_refname($hash_parent_base)) {
-		die_error(400, "Invalid hash parent base parameter");
+	our @extra_options = @{$input_params{'extra_options'}};
+	# @extra_options is always defined, since it can only be (currently) set from
+	# CGI, and $cgi->param() returns the empty array in array context if the param
+	# is not set
+	foreach my $opt (@extra_options) {
+		if (not exists $allowed_options{$opt}) {
+			die_error(400, "Invalid option parameter");
+		}
+		if (not grep(/^$action$/, @{$allowed_options{$opt}})) {
+			die_error(400, "Invalid option parameter for this action");
+		}
 	}
-}
 
-# other parameters
-our $page = $input_params{'page'};
-if (defined $page) {
-	if ($page =~ m/[^0-9]/) {
-		die_error(400, "Invalid page parameter");
+	our $hash_parent_base = $input_params{'hash_parent_base'};
+	if (defined $hash_parent_base) {
+		if (!validate_refname($hash_parent_base)) {
+			die_error(400, "Invalid hash parent base parameter");
+		}
 	}
-}
 
-our $searchtype = $input_params{'searchtype'};
-if (defined $searchtype) {
-	if ($searchtype =~ m/[^a-z]/) {
-		die_error(400, "Invalid searchtype parameter");
+	# other parameters
+	our $page = $input_params{'page'};
+	if (defined $page) {
+		if ($page =~ m/[^0-9]/) {
+			die_error(400, "Invalid page parameter");
+		}
 	}
-}
 
-our $search_use_regexp = $input_params{'search_use_regexp'};
-
-our $searchtext = $input_params{'searchtext'};
-our $search_regexp;
-if (defined $searchtext) {
-	if (length($searchtext) < 2) {
-		die_error(403, "At least two characters are required for search parameter");
+	our $searchtype = $input_params{'searchtype'};
+	if (defined $searchtype) {
+		if ($searchtype =~ m/[^a-z]/) {
+			die_error(400, "Invalid searchtype parameter");
+		}
 	}
-	$search_regexp = $search_use_regexp ? $searchtext : quotemeta $searchtext;
+
+	our $search_use_regexp = $input_params{'search_use_regexp'};
+
+	our $searchtext = $input_params{'searchtext'};
+	our $search_regexp;
+	if (defined $searchtext) {
+		if (length($searchtext) < 2) {
+			die_error(403, "At least two characters are required for search parameter");
+		}
+		if ($search_use_regexp) {
+			$search_regexp = $searchtext;
+			if (!eval { qr/$search_regexp/; 1; }) {
+				(my $error = $@) =~ s/ at \S+ line \d+.*\n?//;
+				die_error(400, "Invalid search regexp '$search_regexp'",
+				          esc_html($error));
+			}
+		} else {
+			$search_regexp = quotemeta $searchtext;
+		}
+	}
 }
 
 # path to the current git repository
 our $git_dir;
-$git_dir = "$projectroot/$project" if $project;
-
-# list of supported snapshot formats
-our @snapshot_fmts = gitweb_get_feature('snapshot');
-@snapshot_fmts = filter_snapshot_fmts(@snapshot_fmts);
-
-# check that the avatar feature is set to a known provider name,
-# and for each provider check if the dependencies are satisfied.
-# if the provider name is invalid or the dependencies are not met,
-# reset $git_avatar to the empty string.
-our ($git_avatar) = gitweb_get_feature('avatar');
-if ($git_avatar eq 'gravatar') {
-	$git_avatar = '' unless (eval { require Digest::MD5; 1; });
-} elsif ($git_avatar eq 'picon') {
-	# no dependencies
-} else {
-	$git_avatar = '';
+sub evaluate_git_dir {
+	our $git_dir = "$projectroot/$project" if $project;
 }
 
-# dispatch
-if (!defined $action) {
-	if (defined $hash) {
-		$action = git_get_type($hash);
-	} elsif (defined $hash_base && defined $file_name) {
-		$action = git_get_type("$hash_base:$file_name");
-	} elsif (defined $project) {
-		$action = 'summary';
+our (@snapshot_fmts, $git_avatar);
+sub configure_gitweb_features {
+	# list of supported snapshot formats
+	our @snapshot_fmts = gitweb_get_feature('snapshot');
+	@snapshot_fmts = filter_snapshot_fmts(@snapshot_fmts);
+
+	# check that the avatar feature is set to a known provider name,
+	# and for each provider check if the dependencies are satisfied.
+	# if the provider name is invalid or the dependencies are not met,
+	# reset $git_avatar to the empty string.
+	our ($git_avatar) = gitweb_get_feature('avatar');
+	if ($git_avatar eq 'gravatar') {
+		$git_avatar = '' unless (eval { require Digest::MD5; 1; });
+	} elsif ($git_avatar eq 'picon') {
+		# no dependencies
 	} else {
-		$action = 'project_list';
+		$git_avatar = '';
 	}
 }
-if (!defined($actions{$action})) {
-	die_error(400, "Unknown action");
+
+# custom error handler: 'die <message>' is Internal Server Error
+sub handle_errors_html {
+	my $msg = shift; # it is already HTML escaped
+
+	# to avoid infinite loop where error occurs in die_error,
+	# change handler to default handler, disabling handle_errors_html
+	set_message("Error occured when inside die_error:\n$msg");
+
+	# you cannot jump out of die_error when called as error handler;
+	# the subroutine set via CGI::Carp::set_message is called _after_
+	# HTTP headers are already written, so it cannot write them itself
+	die_error(undef, undef, $msg, -error_handler => 1, -no_http_header => 1);
 }
-if ($action !~ m/^(?:opml|project_list|project_index)$/ &&
-    !$project) {
-	die_error(400, "Project needed");
+set_message(\&handle_errors_html);
+
+# dispatch
+sub dispatch {
+	if (!defined $action) {
+		if (defined $hash) {
+			$action = git_get_type($hash);
+			$action or die_error(404, "Object does not exist");
+		} elsif (defined $hash_base && defined $file_name) {
+			$action = git_get_type("$hash_base:$file_name");
+			$action or die_error(404, "File or directory does not exist");
+		} elsif (defined $project) {
+			$action = 'summary';
+		} else {
+			$action = 'project_list';
+		}
+	}
+	if (!defined($actions{$action})) {
+		die_error(400, "Unknown action");
+	}
+	if ($action !~ m/^(?:opml|project_list|project_index)$/ &&
+	    !$project) {
+		die_error(400, "Project needed");
+	}
+	$actions{$action}->();
 }
-$actions{$action}->();
-exit;
+
+sub reset_timer {
+	our $t0 = [ gettimeofday() ]
+		if defined $t0;
+	our $number_of_git_cmds = 0;
+}
+
+our $first_request = 1;
+sub run_request {
+	reset_timer();
+
+	evaluate_uri();
+	if ($first_request) {
+		evaluate_gitweb_config();
+		evaluate_git_version();
+	}
+	if ($per_request_config) {
+		if (ref($per_request_config) eq 'CODE') {
+			$per_request_config->();
+		} elsif (!$first_request) {
+			evaluate_gitweb_config();
+		}
+	}
+	check_loadavg();
+
+	# $projectroot and $projects_list might be set in gitweb config file
+	$projects_list ||= $projectroot;
+
+	evaluate_query_params();
+	evaluate_path_info();
+	evaluate_and_validate_params();
+	evaluate_git_dir();
+
+	configure_gitweb_features();
+
+	dispatch();
+}
+
+our $is_last_request = sub { 1 };
+our ($pre_dispatch_hook, $post_dispatch_hook, $pre_listen_hook);
+our $CGI = 'CGI';
+our $cgi;
+sub configure_as_fcgi {
+	require CGI::Fast;
+	our $CGI = 'CGI::Fast';
+
+	my $request_number = 0;
+	# let each child service 100 requests
+	our $is_last_request = sub { ++$request_number > 100 };
+}
+sub evaluate_argv {
+	my $script_name = $ENV{'SCRIPT_NAME'} || $ENV{'SCRIPT_FILENAME'} || __FILE__;
+	configure_as_fcgi()
+		if $script_name =~ /\.fcgi$/;
+
+	return unless (@ARGV);
+
+	require Getopt::Long;
+	Getopt::Long::GetOptions(
+		'fastcgi|fcgi|f' => \&configure_as_fcgi,
+		'nproc|n=i' => sub {
+			my ($arg, $val) = @_;
+			return unless eval { require FCGI::ProcManager; 1; };
+			my $proc_manager = FCGI::ProcManager->new({
+				n_processes => $val,
+			});
+			our $pre_listen_hook    = sub { $proc_manager->pm_manage()        };
+			our $pre_dispatch_hook  = sub { $proc_manager->pm_pre_dispatch()  };
+			our $post_dispatch_hook = sub { $proc_manager->pm_post_dispatch() };
+		},
+	);
+}
+
+sub run {
+	evaluate_argv();
+
+	$first_request = 1;
+	$pre_listen_hook->()
+		if $pre_listen_hook;
+
+ REQUEST:
+	while ($cgi = $CGI->new()) {
+		$pre_dispatch_hook->()
+			if $pre_dispatch_hook;
+
+		run_request();
+
+		$post_dispatch_hook->()
+			if $post_dispatch_hook;
+		$first_request = 0;
+
+		last REQUEST if ($is_last_request->());
+	}
+
+ DONE_GITWEB:
+	1;
+}
+
+run();
+
+if (defined caller) {
+	# wrapped in a subroutine processing requests,
+	# e.g. mod_perl with ModPerl::Registry, or PSGI with Plack::App::WrapCGI
+	return;
+} else {
+	# pure CGI script, serving single request
+	exit;
+}
 
 ## ======================================================================
 ## action links
 
+# possible values of extra options
+# -full => 0|1      - use absolute/full URL ($my_uri/$my_url as base)
+# -replay => 1      - start from a current view (replay with modifications)
+# -path_info => 0|1 - don't use/use path_info URL (if possible)
+# -anchor => ANCHOR - add #ANCHOR to end of URL, implies -replay if used alone
 sub href {
 	my %params = @_;
 	# default is to use -absolute url() i.e. $my_uri
 	my $href = $params{-full} ? $my_url : $my_uri;
 
+	# implicit -replay, must be first of implicit params
+	$params{-replay} = 1 if (keys %params == 1 && $params{-anchor});
+
 	$params{'project'} = $project unless exists $params{'project'};
 
 	if ($params{-replay}) {
@@ -903,7 +1301,8 @@
 	}
 
 	my $use_pathinfo = gitweb_check_feature('pathinfo');
-	if ($use_pathinfo and defined $params{'project'}) {
+	if (defined $params{'project'} &&
+	    (exists $params{-path_info} ? $params{-path_info} : $use_pathinfo)) {
 		# try to put as many parameters as possible in PATH_INFO:
 		#   - project name
 		#   - action
@@ -918,7 +1317,7 @@
 		$href =~ s,/$,,;
 
 		# Then add the project name, if present
-		$href .= "/".esc_url($params{'project'});
+		$href .= "/".esc_path_info($params{'project'});
 		delete $params{'project'};
 
 		# since we destructively absorb parameters, we keep this
@@ -928,7 +1327,8 @@
 		# Summary just uses the project path URL, any other action is
 		# added to the URL
 		if (defined $params{'action'}) {
-			$href .= "/".esc_url($params{'action'}) unless $params{'action'} eq 'summary';
+			$href .= "/".esc_path_info($params{'action'})
+				unless $params{'action'} eq 'summary';
 			delete $params{'action'};
 		}
 
@@ -938,13 +1338,13 @@
 			|| $params{'hash_parent'} || $params{'hash'});
 		if (defined $params{'hash_base'}) {
 			if (defined $params{'hash_parent_base'}) {
-				$href .= esc_url($params{'hash_parent_base'});
+				$href .= esc_path_info($params{'hash_parent_base'});
 				# skip the file_parent if it's the same as the file_name
 				if (defined $params{'file_parent'}) {
 					if (defined $params{'file_name'} && $params{'file_parent'} eq $params{'file_name'}) {
 						delete $params{'file_parent'};
 					} elsif ($params{'file_parent'} !~ /\.\./) {
-						$href .= ":/".esc_url($params{'file_parent'});
+						$href .= ":/".esc_path_info($params{'file_parent'});
 						delete $params{'file_parent'};
 					}
 				}
@@ -952,19 +1352,19 @@
 				delete $params{'hash_parent'};
 				delete $params{'hash_parent_base'};
 			} elsif (defined $params{'hash_parent'}) {
-				$href .= esc_url($params{'hash_parent'}). "..";
+				$href .= esc_path_info($params{'hash_parent'}). "..";
 				delete $params{'hash_parent'};
 			}
 
-			$href .= esc_url($params{'hash_base'});
+			$href .= esc_path_info($params{'hash_base'});
 			if (defined $params{'file_name'} && $params{'file_name'} !~ /\.\./) {
-				$href .= ":/".esc_url($params{'file_name'});
+				$href .= ":/".esc_path_info($params{'file_name'});
 				delete $params{'file_name'};
 			}
 			delete $params{'hash'};
 			delete $params{'hash_base'};
 		} elsif (defined $params{'hash'}) {
-			$href .= esc_url($params{'hash'});
+			$href .= esc_path_info($params{'hash'});
 			delete $params{'hash'};
 		}
 
@@ -997,6 +1397,13 @@
 	}
 	$href .= "?" . join(';', @result) if scalar @result;
 
+	# final transformation: trailing spaces must be escaped (URI-encoded)
+	$href =~ s/(\s+)$/CGI::escape($1)/e;
+
+	if ($params{-anchor}) {
+		$href .= "#".esc_param($params{-anchor});
+	}
+
 	return $href;
 }
 
@@ -1060,8 +1467,9 @@
 # in utf-8 thanks to "binmode STDOUT, ':utf8'" at beginning
 sub to_utf8 {
 	my $str = shift;
-	if (utf8::valid($str)) {
-		utf8::decode($str);
+	return undef unless defined $str;
+
+	if (utf8::is_utf8($str) || utf8::decode($str)) {
 		return $str;
 	} else {
 		return decode($fallback_encoding, $str, Encode::FB_DEFAULT);
@@ -1072,26 +1480,46 @@
 # correct, but quoted slashes look too horrible in bookmarks
 sub esc_param {
 	my $str = shift;
-	$str =~ s/([^A-Za-z0-9\-_.~()\/:@])/sprintf("%%%02X", ord($1))/eg;
-	$str =~ s/\+/%2B/g;
+	return undef unless defined $str;
+	$str =~ s/([^A-Za-z0-9\-_.~()\/:@ ]+)/CGI::escape($1)/eg;
 	$str =~ s/ /\+/g;
 	return $str;
 }
 
-# quote unsafe chars in whole URL, so some charactrs cannot be quoted
+# the quoting rules for path_info fragment are slightly different
+sub esc_path_info {
+	my $str = shift;
+	return undef unless defined $str;
+
+	# path_info doesn't treat '+' as space (specially), but '?' must be escaped
+	$str =~ s/([^A-Za-z0-9\-_.~();\/;:@&= +]+)/CGI::escape($1)/eg;
+
+	return $str;
+}
+
+# quote unsafe chars in whole URL, so some characters cannot be quoted
 sub esc_url {
 	my $str = shift;
-	$str =~ s/([^A-Za-z0-9\-_.~();\/;?:@&=])/sprintf("%%%02X", ord($1))/eg;
-	$str =~ s/\+/%2B/g;
+	return undef unless defined $str;
+	$str =~ s/([^A-Za-z0-9\-_.~();\/;?:@&= ]+)/CGI::escape($1)/eg;
 	$str =~ s/ /\+/g;
 	return $str;
 }
 
+# quote unsafe characters in HTML attributes
+sub esc_attr {
+
+	# for XHTML conformance escaping '"' to '&quot;' is not enough
+	return esc_html(@_);
+}
+
 # replace invalid utf8 character with SUBSTITUTION sequence
 sub esc_html {
 	my $str = shift;
 	my %opts = @_;
 
+	return undef unless defined $str;
+
 	$str = to_utf8($str);
 	$str = $cgi->escapeHTML($str);
 	if ($opts{'-nbsp'}) {
@@ -1106,6 +1534,8 @@
 	my $str = shift;
 	my %opts = @_;
 
+	return undef unless defined $str;
+
 	$str = to_utf8($str);
 	$str = $cgi->escapeHTML($str);
 	if ($opts{'-nbsp'}) {
@@ -1115,6 +1545,17 @@
 	return $str;
 }
 
+# Sanitize for use in XHTML + application/xml+xhtm (valid XML 1.0)
+sub sanitize {
+	my $str = shift;
+
+	return undef unless defined $str;
+
+	$str = to_utf8($str);
+	$str =~ s|([[:cntrl:]])|($1 =~ /[\t\n\r]/ ? $1 : quot_cec($1))|eg;
+	return $str;
+}
+
 # Make control characters "printable", using character escape codes (CEC)
 sub quot_cec {
 	my $cntrl = shift;
@@ -1248,7 +1689,6 @@
 		$str =~ m/^(.*?)($begre)$/;
 		my ($lead, $body) = ($1, $2);
 		if (length($lead) > 4) {
-			$body =~ s/^[^;]*;// if ($lead =~ m/&[^;]*$/);
 			$lead = " ...";
 		}
 		return "$lead$body";
@@ -1259,8 +1699,6 @@
 		$str =~ m/^(.*?)($begre)$/;
 		my ($mid, $right) = ($1, $2);
 		if (length($mid) > 5) {
-			$left  =~ s/&[^;]*$//;
-			$right =~ s/^[^;]*;// if ($mid =~ m/&[^;]*$/);
 			$mid = " ... ";
 		}
 		return "$left$mid$right";
@@ -1270,7 +1708,6 @@
 		my $body = $1;
 		my $tail = $2;
 		if (length($tail) > 4) {
-			$body =~ s/&[^;]*$//;
 			$tail = "... ";
 		}
 		return "$body$tail";
@@ -1284,6 +1721,7 @@
 	my ($str) = @_;
 
 	my $chopped = chop_str(@_);
+	$str = to_utf8($str);
 	if ($chopped eq $str) {
 		return esc_html($chopped);
 	} else {
@@ -1292,6 +1730,97 @@
 	}
 }
 
+# Highlight selected fragments of string, using given CSS class,
+# and escape HTML.  It is assumed that fragments do not overlap.
+# Regions are passed as list of pairs (array references).
+#
+# Example: esc_html_hl_regions("foobar", "mark", [ 0, 3 ]) returns
+# '<span class="mark">foo</span>bar'
+sub esc_html_hl_regions {
+	my ($str, $css_class, @sel) = @_;
+	my %opts = grep { ref($_) ne 'ARRAY' } @sel;
+	@sel     = grep { ref($_) eq 'ARRAY' } @sel;
+	return esc_html($str, %opts) unless @sel;
+
+	my $out = '';
+	my $pos = 0;
+
+	for my $s (@sel) {
+		my ($begin, $end) = @$s;
+
+		# Don't create empty <span> elements.
+		next if $end <= $begin;
+
+		my $escaped = esc_html(substr($str, $begin, $end - $begin),
+		                       %opts);
+
+		$out .= esc_html(substr($str, $pos, $begin - $pos), %opts)
+			if ($begin - $pos > 0);
+		$out .= $cgi->span({-class => $css_class}, $escaped);
+
+		$pos = $end;
+	}
+	$out .= esc_html(substr($str, $pos), %opts)
+		if ($pos < length($str));
+
+	return $out;
+}
+
+# return positions of beginning and end of each match
+sub matchpos_list {
+	my ($str, $regexp) = @_;
+	return unless (defined $str && defined $regexp);
+
+	my @matches;
+	while ($str =~ /$regexp/g) {
+		push @matches, [$-[0], $+[0]];
+	}
+	return @matches;
+}
+
+# highlight match (if any), and escape HTML
+sub esc_html_match_hl {
+	my ($str, $regexp) = @_;
+	return esc_html($str) unless defined $regexp;
+
+	my @matches = matchpos_list($str, $regexp);
+	return esc_html($str) unless @matches;
+
+	return esc_html_hl_regions($str, 'match', @matches);
+}
+
+
+# highlight match (if any) of shortened string, and escape HTML
+sub esc_html_match_hl_chopped {
+	my ($str, $chopped, $regexp) = @_;
+	return esc_html_match_hl($str, $regexp) unless defined $chopped;
+
+	my @matches = matchpos_list($str, $regexp);
+	return esc_html($chopped) unless @matches;
+
+	# filter matches so that we mark chopped string
+	my $tail = "... "; # see chop_str
+	unless ($chopped =~ s/\Q$tail\E$//) {
+		$tail = '';
+	}
+	my $chop_len = length($chopped);
+	my $tail_len = length($tail);
+	my @filtered;
+
+	for my $m (@matches) {
+		if ($m->[0] > $chop_len) {
+			push @filtered, [ $chop_len, $chop_len + $tail_len ] if ($tail_len > 0);
+			last;
+		} elsif ($m->[1] > $chop_len) {
+			push @filtered, [ $m->[0], $chop_len + $tail_len ];
+			last;
+		}
+		push @filtered, $m;
+	}
+
+	return esc_html_hl_regions($chopped . $tail, 'match', @filtered);
+}
+
 ## ----------------------------------------------------------------------
 ## functions returning short strings
 
@@ -1492,7 +2021,7 @@
 					hash=>$dest
 				)}, $name);
 
-			$markers .= " <span class=\"$class\" title=\"$ref\">" .
+			$markers .= " <span class=\"".esc_attr($class)."\" title=\"".esc_attr($ref)."\">" .
 				$link . "</span>";
 		}
 	}
@@ -1513,10 +2042,10 @@
 		$long =~ s/[[:cntrl:]]/?/g;
 		return $cgi->a({-href => $href, -class => "list subject",
 		                -title => to_utf8($long)},
-		       esc_html($short) . $extra);
+		       esc_html($short)) . $extra;
 	} else {
 		return $cgi->a({-href => $href, -class => "list subject"},
-		       esc_html($long)  . $extra);
+		       esc_html($long)) . $extra;
 	}
 }
 
@@ -1576,7 +2105,7 @@
 		return $pre_white .
 		       "<img width=\"$size\" " .
 		            "class=\"avatar\" " .
-		            "src=\"$url\" " .
+		            "src=\"".esc_url($url)."\" " .
 			    "alt=\"\" " .
 		       "/>" . $post_white;
 	} else {
@@ -1584,6 +2113,29 @@
 	}
 }
 
+sub format_search_author {
+	my ($author, $searchtype, $displaytext) = @_;
+	my $have_search = gitweb_check_feature('search');
+
+	if ($have_search) {
+		my $performed = "";
+		if ($searchtype eq 'author') {
+			$performed = "authored";
+		} elsif ($searchtype eq 'committer') {
+			$performed = "committed";
+		}
+
+		return $cgi->a({-href => href(action=>"search", hash=>$hash,
+				searchtext=>$author,
+				searchtype=>$searchtype), class=>"list",
+				title=>"Search for commits $performed by $author"},
+				$displaytext);
+
+	} else {
+		return $displaytext;
+	}
+}
+
 # format the author name of the given commit with the given tag
 # the author name is chopped and escaped according to the other
 # optional parameters (see chop_str).
@@ -1592,8 +2144,10 @@
 	my $co = shift;
 	my $author = chop_and_escape_str($co->{'author_name'}, @_);
 	return "<$tag class=\"author\">" .
-	       git_get_avatar($co->{'author_email'}, -pad_after => 1) .
-	       $author . "</$tag>";
+	       format_search_author($co->{'author_name'}, "author",
+		       git_get_avatar($co->{'author_email'}, -pad_after => 1) .
+		       $author) .
+	       "</$tag>";
 }
 
 # format git diff header line, i.e. "diff --(git|combined|cc) ..."
@@ -1789,93 +2343,125 @@
 	return $result;
 }
 
-# format patch (diff) line (not to be used for diff headers)
-sub format_diff_line {
-	my $line = shift;
-	my ($from, $to) = @_;
-	my $diff_class = "";
+sub diff_line_class {
+	my ($line, $from, $to) = @_;
 
-	chomp $line;
-
+	# ordinary diff
+	my $num_sign = 1;
+	# combined diff
 	if ($from && $to && ref($from->{'href'}) eq "ARRAY") {
-		# combined diff
-		my $prefix = substr($line, 0, scalar @{$from->{'href'}});
-		if ($line =~ m/^\@{3}/) {
-			$diff_class = " chunk_header";
-		} elsif ($line =~ m/^\\/) {
-			$diff_class = " incomplete";
-		} elsif ($prefix =~ tr/+/+/) {
-			$diff_class = " add";
-		} elsif ($prefix =~ tr/-/-/) {
-			$diff_class = " rem";
-		}
-	} else {
-		# assume ordinary diff
-		my $char = substr($line, 0, 1);
-		if ($char eq '+') {
-			$diff_class = " add";
-		} elsif ($char eq '-') {
-			$diff_class = " rem";
-		} elsif ($char eq '@') {
-			$diff_class = " chunk_header";
-		} elsif ($char eq "\\") {
-			$diff_class = " incomplete";
-		}
+		$num_sign = scalar @{$from->{'href'}};
 	}
-	$line = untabify($line);
-	if ($from && $to && $line =~ m/^\@{2} /) {
-		my ($from_text, $from_start, $from_lines, $to_text, $to_start, $to_lines, $section) =
-			$line =~ m/^\@{2} (-(\d+)(?:,(\d+))?) (\+(\d+)(?:,(\d+))?) \@{2}(.*)$/;
 
-		$from_lines = 0 unless defined $from_lines;
-		$to_lines   = 0 unless defined $to_lines;
+	my @diff_line_classifier = (
+		{ regexp => qr/^\@\@{$num_sign} /, class => "chunk_header"},
+		{ regexp => qr/^\\/,               class => "incomplete"  },
+		{ regexp => qr/^ {$num_sign}/,     class => "ctx" },
+		# classifier for context must come before classifier add/rem,
+		# or we would have to use more complicated regexp, for example
+		# qr/(?= {0,$m}\+)[+ ]{$num_sign}/, where $m = $num_sign - 1;
+		{ regexp => qr/^[+ ]{$num_sign}/,   class => "add" },
+		{ regexp => qr/^[- ]{$num_sign}/,   class => "rem" },
+	);
+	for my $clsfy (@diff_line_classifier) {
+		return $clsfy->{'class'}
+			if ($line =~ $clsfy->{'regexp'});
+	}
 
-		if ($from->{'href'}) {
-			$from_text = $cgi->a({-href=>"$from->{'href'}#l$from_start",
-			                     -class=>"list"}, $from_text);
-		}
-		if ($to->{'href'}) {
-			$to_text   = $cgi->a({-href=>"$to->{'href'}#l$to_start",
-			                     -class=>"list"}, $to_text);
-		}
-		$line = "<span class=\"chunk_info\">@@ $from_text $to_text @@</span>" .
-		        "<span class=\"section\">" . esc_html($section, -nbsp=>1) . "</span>";
-		return "<div class=\"diff$diff_class\">$line</div>\n";
-	} elsif ($from && $to && $line =~ m/^\@{3}/) {
-		my ($prefix, $ranges, $section) = $line =~ m/^(\@+) (.*?) \@+(.*)$/;
-		my (@from_text, @from_start, @from_nlines, $to_text, $to_start, $to_nlines);
+	# fallback
+	return "";
+}
 
-		@from_text = split(' ', $ranges);
-		for (my $i = 0; $i < @from_text; ++$i) {
-			($from_start[$i], $from_nlines[$i]) =
-				(split(',', substr($from_text[$i], 1)), 0);
-		}
+# assumes that $from and $to are defined and correctly filled,
+# and that $line holds a line of chunk header for unified diff
+sub format_unidiff_chunk_header {
+	my ($line, $from, $to) = @_;
 
-		$to_text   = pop @from_text;
-		$to_start  = pop @from_start;
-		$to_nlines = pop @from_nlines;
+	my ($from_text, $from_start, $from_lines, $to_text, $to_start, $to_lines, $section) =
+		$line =~ m/^\@{2} (-(\d+)(?:,(\d+))?) (\+(\d+)(?:,(\d+))?) \@{2}(.*)$/;
 
-		$line = "<span class=\"chunk_info\">$prefix ";
-		for (my $i = 0; $i < @from_text; ++$i) {
-			if ($from->{'href'}[$i]) {
-				$line .= $cgi->a({-href=>"$from->{'href'}[$i]#l$from_start[$i]",
-				                  -class=>"list"}, $from_text[$i]);
-			} else {
-				$line .= $from_text[$i];
-			}
-			$line .= " ";
-		}
-		if ($to->{'href'}) {
-			$line .= $cgi->a({-href=>"$to->{'href'}#l$to_start",
-			                  -class=>"list"}, $to_text);
+	$from_lines = 0 unless defined $from_lines;
+	$to_lines   = 0 unless defined $to_lines;
+
+	if ($from->{'href'}) {
+		$from_text = $cgi->a({-href=>"$from->{'href'}#l$from_start",
+		                     -class=>"list"}, $from_text);
+	}
+	if ($to->{'href'}) {
+		$to_text   = $cgi->a({-href=>"$to->{'href'}#l$to_start",
+		                     -class=>"list"}, $to_text);
+	}
+	$line = "<span class=\"chunk_info\">@@ $from_text $to_text @@</span>" .
+	        "<span class=\"section\">" . esc_html($section, -nbsp=>1) . "</span>";
+	return $line;
+}
+
+# assumes that $from and $to are defined and correctly filled,
+# and that $line holds a line of chunk header for combined diff
+sub format_cc_diff_chunk_header {
+	my ($line, $from, $to) = @_;
+
+	my ($prefix, $ranges, $section) = $line =~ m/^(\@+) (.*?) \@+(.*)$/;
+	my (@from_text, @from_start, @from_nlines, $to_text, $to_start, $to_nlines);
+
+	@from_text = split(' ', $ranges);
+	for (my $i = 0; $i < @from_text; ++$i) {
+		($from_start[$i], $from_nlines[$i]) =
+			(split(',', substr($from_text[$i], 1)), 0);
+	}
+
+	$to_text   = pop @from_text;
+	$to_start  = pop @from_start;
+	$to_nlines = pop @from_nlines;
+
+	$line = "<span class=\"chunk_info\">$prefix ";
+	for (my $i = 0; $i < @from_text; ++$i) {
+		if ($from->{'href'}[$i]) {
+			$line .= $cgi->a({-href=>"$from->{'href'}[$i]#l$from_start[$i]",
+			                  -class=>"list"}, $from_text[$i]);
 		} else {
-			$line .= $to_text;
+			$line .= $from_text[$i];
 		}
-		$line .= " $prefix</span>" .
-		         "<span class=\"section\">" . esc_html($section, -nbsp=>1) . "</span>";
-		return "<div class=\"diff$diff_class\">$line</div>\n";
+		$line .= " ";
 	}
-	return "<div class=\"diff$diff_class\">" . esc_html($line, -nbsp=>1) . "</div>\n";
+	if ($to->{'href'}) {
+		$line .= $cgi->a({-href=>"$to->{'href'}#l$to_start",
+		                  -class=>"list"}, $to_text);
+	} else {
+		$line .= $to_text;
+	}
+	$line .= " $prefix</span>" .
+	         "<span class=\"section\">" . esc_html($section, -nbsp=>1) . "</span>";
+	return $line;
+}
+
+# process patch (diff) line (not to be used for diff headers),
+# returning HTML-formatted (but not wrapped) line.
+# If the line is passed as a reference, it is treated as HTML and not
+# esc_html()'ed.
+sub format_diff_line {
+	my ($line, $diff_class, $from, $to) = @_;
+
+	if (ref($line)) {
+		$line = $$line;
+	} else {
+		chomp $line;
+		$line = untabify($line);
+
+		if ($from && $to && $line =~ m/^\@{2} /) {
+			$line = format_unidiff_chunk_header($line, $from, $to);
+		} elsif ($from && $to && $line =~ m/^\@{3}/) {
+			$line = format_cc_diff_chunk_header($line, $from, $to);
+		} else {
+			$line = esc_html($line, -nbsp=>1);
+		}
+	}
+
+	my $diff_classes = "diff";
+	$diff_classes .= " $diff_class" if ($diff_class);
+	$line = "<div class=\"$diff_classes\">$line</div>\n";
+
+	return $line;
 }
 
 # Generates undef or something like "_snapshot_" or "snapshot (_tbz2_ _zip_)",
@@ -1927,7 +2513,7 @@
 	return unless (defined $project);
 	# some views should link to OPML, or to generic project feed,
 	# or don't have specific feed yet (so they should use generic)
-	return if ($action =~ /^(?:tags|heads|forks|tag|search)$/x);
+	return if (!$action || $action =~ /^(?:tags|heads|forks|tag|search)$/x);
 
 	my $branch;
 	# branches refs uses 'refs/heads/' prefix (fullname) to differentiate
@@ -1958,6 +2544,7 @@
 
 # returns path to the core git executable and the --git-dir parameter as list
 sub git_cmd {
+	$number_of_git_cmds++;
 	return $GIT, '--git-dir='.$git_dir;
 }
 
@@ -1972,16 +2559,27 @@
 
 # get HEAD ref of given project as hash
 sub git_get_head_hash {
-	my $project = shift;
+	return git_get_full_hash(shift, 'HEAD');
+}
+
+sub git_get_full_hash {
+	return git_get_hash(@_);
+}
+
+sub git_get_short_hash {
+	return git_get_hash(@_, '--short=7');
+}
+
+sub git_get_hash {
+	my ($project, $hash, @options) = @_;
 	my $o_git_dir = $git_dir;
 	my $retval = undef;
 	$git_dir = "$projectroot/$project";
-	if (open my $fd, "-|", git_cmd(), "rev-parse", "--verify", "HEAD") {
-		my $head = <$fd>;
+	if (open my $fd, '-|', git_cmd(), 'rev-parse',
+	    '--verify', '-q', @options, $hash) {
+		$retval = <$fd>;
+		chomp $retval if defined $retval;
 		close $fd;
-		if (defined $head && $head =~ /^([0-9a-fA-F]{40})$/) {
-			$retval = $1;
-		}
 	}
 	if (defined $o_git_dir) {
 		$git_dir = $o_git_dir;
@@ -2087,8 +2685,17 @@
 sub git_get_project_config {
 	my ($key, $type) = @_;
 
+	return unless defined $git_dir;
+
 	# key sanity check
 	return unless ($key);
+	# only subsection, if exists, is case sensitive,
+	# and not lowercased by 'git config -z -l'
+	if (my ($hi, $mi, $lo) = ($key =~ /^([^.]*)\.(.*)\.([^.]*)$/)) {
+		$key = join(".", lc($hi), $mi, lc($lo));
+	} else {
+		$key = lc($key);
+	}
 	$key =~ s/^gitweb\.//;
 	return if ($key =~ m/\W/);
 
@@ -2175,37 +2782,94 @@
 ## ......................................................................
 ## git utility functions, directly accessing git repository
 
-sub git_get_project_description {
-	my $path = shift;
+# get the value of config variable either from file named as the variable
+# itself in the repository ($GIT_DIR/$name file), or from gitweb.$name
+# configuration variable in the repository config file.
+sub git_get_file_or_project_config {
+	my ($path, $name) = @_;
 
 	$git_dir = "$projectroot/$path";
-	open my $fd, '<', "$git_dir/description"
-		or return git_get_project_config('description');
-	my $descr = <$fd>;
+	open my $fd, '<', "$git_dir/$name"
+		or return git_get_project_config($name);
+	my $conf = <$fd>;
 	close $fd;
-	if (defined $descr) {
-		chomp $descr;
+	if (defined $conf) {
+		chomp $conf;
 	}
-	return $descr;
+	return $conf;
 }
 
-sub git_get_project_ctags {
+sub git_get_project_description {
 	my $path = shift;
+	return git_get_file_or_project_config($path, 'description');
+}
+
+sub git_get_project_category {
+	my $path = shift;
+	return git_get_file_or_project_config($path, 'category');
+}
+
+
+# supported formats:
+# * $GIT_DIR/ctags/<tagname> file (in 'ctags' subdirectory)
+#   - if its contents is a number, use it as tag weight,
+#   - otherwise add a tag with weight 1
+# * $GIT_DIR/ctags file, each line is a tag (with weight 1)
+#   the same value multiple times increases tag weight
+# * `gitweb.ctag' multi-valued repo config variable
+sub git_get_project_ctags {
+	my $project = shift;
 	my $ctags = {};
 
-	$git_dir = "$projectroot/$path";
-	opendir my $dh, "$git_dir/ctags"
-		or return $ctags;
-	foreach (grep { -f $_ } map { "$git_dir/ctags/$_" } readdir($dh)) {
-		open my $ct, '<', $_ or next;
-		my $val = <$ct>;
-		chomp $val;
-		close $ct;
-		my $ctag = $_; $ctag =~ s#.*/##;
-		$ctags->{$ctag} = $val;
+	$git_dir = "$projectroot/$project";
+	if (opendir my $dh, "$git_dir/ctags") {
+		my @files = grep { -f $_ } map { "$git_dir/ctags/$_" } readdir($dh);
+		foreach my $tagfile (@files) {
+			open my $ct, '<', $tagfile
+				or next;
+			my $val = <$ct>;
+			chomp $val if $val;
+			close $ct;
+
+			(my $ctag = $tagfile) =~ s#.*/##;
+			if ($val =~ /^\d+$/) {
+				$ctags->{$ctag} = $val;
+			} else {
+				$ctags->{$ctag} = 1;
+			}
+		}
+		closedir $dh;
+
+	} elsif (open my $fh, '<', "$git_dir/ctags") {
+		while (my $line = <$fh>) {
+			chomp $line;
+			$ctags->{$line}++ if $line;
+		}
+		close $fh;
+
+	} else {
+		my $taglist = config_to_multi(git_get_project_config('ctag'));
+		foreach my $tag (@$taglist) {
+			$ctags->{$tag}++;
+		}
 	}
-	closedir $dh;
-	$ctags;
+
+	return $ctags;
+}
+
+# return hash, where keys are content tags ('ctags'),
+# and values are sum of weights of given tag in every project
+sub git_gather_all_ctags {
+	my $projects = shift;
+	my $ctags = {};
+
+	foreach my $p (@$projects) {
+		foreach my $ct (keys %{$p->{'ctags'}}) {
+			$ctags->{$ct} += $p->{'ctags'}->{$ct};
+		}
+	}
+
+	return $ctags;
 }
 
 sub git_populate_project_tagcloud {
@@ -2223,33 +2887,49 @@
 	}
 
 	my $cloud;
+	my $matched = $input_params{'ctag'};
 	if (eval { require HTML::TagCloud; 1; }) {
 		$cloud = HTML::TagCloud->new;
-		foreach (sort keys %ctags_lc) {
+		foreach my $ctag (sort keys %ctags_lc) {
 			# Pad the title with spaces so that the cloud looks
 			# less crammed.
-			my $title = $ctags_lc{$_}->{topname};
+			my $title = esc_html($ctags_lc{$ctag}->{topname});
 			$title =~ s/ /&nbsp;/g;
 			$title =~ s/^/&nbsp;/g;
 			$title =~ s/$/&nbsp;/g;
-			$cloud->add($title, $home_link."?by_tag=".$_, $ctags_lc{$_}->{count});
+			if (defined $matched && $matched eq $ctag) {
+				$title = qq(<span class="match">$title</span>);
+			}
+			$cloud->add($title, href(project=>undef, ctag=>$ctag),
+			            $ctags_lc{$ctag}->{count});
 		}
 	} else {
-		$cloud = \%ctags_lc;
+		$cloud = {};
+		foreach my $ctag (keys %ctags_lc) {
+			my $title = esc_html($ctags_lc{$ctag}->{topname}, -nbsp=>1);
+			if (defined $matched && $matched eq $ctag) {
+				$title = qq(<span class="match">$title</span>);
+			}
+			$cloud->{$ctag}{count} = $ctags_lc{$ctag}->{count};
+			$cloud->{$ctag}{ctag} =
+				$cgi->a({-href=>href(project=>undef, ctag=>$ctag)}, $title);
+		}
 	}
-	$cloud;
+	return $cloud;
 }
 
 sub git_show_project_tagcloud {
 	my ($cloud, $count) = @_;
-	print STDERR ref($cloud)."..\n";
 	if (ref $cloud eq 'HTML::TagCloud') {
 		return $cloud->html_and_css($count);
 	} else {
-		my @tags = sort { $cloud->{$a}->{count} <=> $cloud->{$b}->{count} } keys %$cloud;
-		return '<p align="center">' . join (', ', map {
-			"<a href=\"$home_link?by_tag=$_\">$cloud->{$_}->{topname}</a>"
-		} splice(@tags, 0, $count)) . '</p>';
+		my @tags = sort { $cloud->{$a}->{'count'} <=> $cloud->{$b}->{'count'} } keys %$cloud;
+		return
+			'<div id="htmltagcloud"'.($project ? '' : ' align="center"').'>' .
+			join (', ', map {
+				$cloud->{$_}->{'ctag'}
+			} splice(@tags, 0, $count)) .
+			'</div>';
 	}
 }
 
@@ -2268,40 +2948,48 @@
 }
 
 sub git_get_projects_list {
-	my ($filter) = @_;
+	my $filter = shift || '';
+	my $paranoid = shift;
 	my @list;
 
-	$filter ||= '';
-	$filter =~ s/\.git$//;
-
-	my $check_forks = gitweb_check_feature('forks');
-
 	if (-d $projects_list) {
 		# search in directory
-		my $dir = $projects_list . ($filter ? "/$filter" : '');
+		my $dir = $projects_list;
 		# remove the trailing "/"
 		$dir =~ s!/+$!!;
 		my $pfxlen = length("$dir");
 		my $pfxdepth = ($dir =~ tr!/!!);
+		# when filtering, search only given subdirectory
+		if ($filter && !$paranoid) {
+			$dir .= "/$filter";
+			$dir =~ s!/+$!!;
+		}
 
 		File::Find::find({
 			follow_fast => 1, # follow symbolic links
 			follow_skip => 2, # ignore duplicates
 			dangling_symlinks => 0, # ignore dangling symlinks, silently
 			wanted => sub {
+				# global variables
+				our $project_maxdepth;
+				our $projectroot;
 				# skip project-list toplevel, if we get it.
 				return if (m!^[/.]$!);
 				# only directories can be git repositories
 				return unless (-d $_);
 				# don't traverse too deep (Find is super slow on os x)
+				# $project_maxdepth excludes depth of $projectroot
 				if (($File::Find::name =~ tr!/!!) - $pfxdepth > $project_maxdepth) {
 					$File::Find::prune = 1;
 					return;
 				}
 
-				my $subdir = substr($File::Find::name, $pfxlen + 1);
+				my $path = substr($File::Find::name, $pfxlen + 1);
+				# paranoidly only filter here
+				if ($paranoid && $filter && $path !~ m!^\Q$filter\E/!) {
+					next;
+				}
 				# we check related file in $projectroot
-				my $path = ($filter ? "$filter/" : '') . $subdir;
 				if (check_export_ok("$projectroot/$path")) {
 					push @list, { path => $path };
 					$File::Find::prune = 1;
@@ -2314,7 +3002,6 @@
 		# 'git%2Fgit.git Linus+Torvalds'
 		# 'libs%2Fklibc%2Fklibc.git H.+Peter+Anvin'
 		# 'linux%2Fhotplug%2Fudev.git Greg+Kroah-Hartman'
-		my %paths;
 		open my $fd, '<', $projects_list or return;
 	PROJECT:
 		while (my $line = <$fd>) {
@@ -2325,41 +3012,18 @@
 			if (!defined $path) {
 				next;
 			}
-			if ($filter ne '') {
-				# looking for forks;
-				my $pfx = substr($path, 0, length($filter));
-				if ($pfx ne $filter) {
-					next PROJECT;
-				}
-				my $sfx = substr($path, length($filter));
-				if ($sfx !~ /^\/.*\.git$/) {
-					next PROJECT;
-				}
-			} elsif ($check_forks) {
-			PATH:
-				foreach my $filter (keys %paths) {
-					# looking for forks;
-					my $pfx = substr($path, 0, length($filter));
-					if ($pfx ne $filter) {
-						next PATH;
-					}
-					my $sfx = substr($path, length($filter));
-					if ($sfx !~ /^\/.*\.git$/) {
-						next PATH;
-					}
-					# is a fork, don't include it in
-					# the list
-					next PROJECT;
-				}
+			# if $filter is rpovided, check if $path begins with $filter
+			if ($filter && $path !~ m!^\Q$filter\E/!) {
+				next;
 			}
 			if (check_export_ok("$projectroot/$path")) {
 				my $pr = {
-					path => $path,
-					owner => to_utf8($owner),
+					path => $path
 				};
+				if ($owner) {
+					$pr->{'owner'} = to_utf8($owner);
+				}
 				push @list, $pr;
-				(my $forks_path = $path) =~ s/\.git$//;
-				$paths{$forks_path}++;
 			}
 		}
 		close $fd;
@@ -2367,6 +3031,102 @@
 	return @list;
 }
 
+# written with help of Tree::Trie module (Perl Artistic License, GPL compatibile)
+# as side effects it sets 'forks' field to list of forks for forked projects
+sub filter_forks_from_projects_list {
+	my $projects = shift;
+
+	my %trie; # prefix tree of directories (path components)
+	# generate trie out of those directories that might contain forks
+	foreach my $pr (@$projects) {
+		my $path = $pr->{'path'};
+		$path =~ s/\.git$//;      # forks of 'repo.git' are in 'repo/' directory
+		next if ($path =~ m!/$!); # skip non-bare repositories, e.g. 'repo/.git'
+		next unless ($path);      # skip '.git' repository: tests, git-instaweb
+		next unless (-d "$projectroot/$path"); # containing directory exists
+		$pr->{'forks'} = [];      # there can be 0 or more forks of project
+
+		# add to trie
+		my @dirs = split('/', $path);
+		# walk the trie, until either runs out of components or out of trie
+		my $ref = \%trie;
+		while (scalar @dirs &&
+		       exists($ref->{$dirs[0]})) {
+			$ref = $ref->{shift @dirs};
+		}
+		# create rest of trie structure from rest of components
+		foreach my $dir (@dirs) {
+			$ref = $ref->{$dir} = {};
+		}
+		# create end marker, store $pr as a data
+		$ref->{''} = $pr if (!exists $ref->{''});
+	}
+
+	# filter out forks, by finding shortest prefix match for paths
+	my @filtered;
+ PROJECT:
+	foreach my $pr (@$projects) {
+		# trie lookup
+		my $ref = \%trie;
+	DIR:
+		foreach my $dir (split('/', $pr->{'path'})) {
+			if (exists $ref->{''}) {
+				# found [shortest] prefix, is a fork - skip it
+				push @{$ref->{''}{'forks'}}, $pr;
+				next PROJECT;
+			}
+			if (!exists $ref->{$dir}) {
+				# not in trie, cannot have prefix, not a fork
+				push @filtered, $pr;
+				next PROJECT;
+			}
+			# If the dir is there, we just walk one step down the trie.
+			$ref = $ref->{$dir};
+		}
+		# we ran out of trie
+		# (shouldn't happen: it's either no match, or end marker)
+		push @filtered, $pr;
+	}
+
+	return @filtered;
+}
+
+# note: fill_project_list_info must be run first,
+# for 'descr_long' and 'ctags' to be filled
+sub search_projects_list {
+	my ($projlist, %opts) = @_;
+	my $tagfilter  = $opts{'tagfilter'};
+	my $search_re = $opts{'search_regexp'};
+
+	return @$projlist
+		unless ($tagfilter || $search_re);
+
+	# searching projects require filling to be run before it;
+	fill_project_list_info($projlist,
+	                       $tagfilter  ? 'ctags' : (),
+	                       $search_re ? ('path', 'descr') : ());
+	my @projects;
+ PROJECT:
+	foreach my $pr (@$projlist) {
+
+		if ($tagfilter) {
+			next unless ref($pr->{'ctags'}) eq 'HASH';
+			next unless
+				grep { lc($_) eq lc($tagfilter) } keys %{$pr->{'ctags'}};
+		}
+
+		if ($search_re) {
+			next unless
+				$pr->{'path'} =~ /$search_re/ ||
+				$pr->{'descr_long'} =~ /$search_re/;
+		}
+
+		push @projects, $pr;
+	}
+
+	return @projects;
+}
+
 our $gitweb_project_owner = undef;
 sub git_get_project_list_from_file {
 
@@ -2435,6 +3195,44 @@
 	return (undef, undef);
 }
 
+# Implementation note: when a single remote is wanted, we cannot use 'git
+# remote show -n' because that command always work (assuming it's a remote URL
+# if it's not defined), and we cannot use 'git remote show' because that would
+# try to make a network roundtrip. So the only way to find if that particular
+# remote is defined is to walk the list provided by 'git remote -v' and stop if
+# and when we find what we want.
+sub git_get_remotes_list {
+	my $wanted = shift;
+	my %remotes = ();
+
+	open my $fd, '-|' , git_cmd(), 'remote', '-v';
+	return unless $fd;
+	while (my $remote = <$fd>) {
+		chomp $remote;
+		$remote =~ s!\t(.*?)\s+\((\w+)\)$!!;
+		next if $wanted and not $remote eq $wanted;
+		my ($url, $key) = ($1, $2);
+
+		$remotes{$remote} ||= { 'heads' => () };
+		$remotes{$remote}{$key} = $url;
+	}
+	close $fd or return;
+	return wantarray ? %remotes : \%remotes;
+}
+
+# Takes a hash of remotes as first parameter and fills it by adding the
+# available remote heads for each of the indicated remotes.
+sub fill_remote_heads {
+	my $remotes = shift;
+	my @heads = map { "remotes/$_" } keys %$remotes;
+	my @remoteheads = git_get_heads_list(undef, @heads);
+	foreach my $remote (keys %$remotes) {
+		$remotes->{$remote}{'heads'} = [ grep {
+			$_->{'name'} =~ s!^$remote/!!
+			} @remoteheads ];
+	}
+}
+
 sub git_get_references {
 	my $type = shift || "";
 	my %refs;
@@ -2497,8 +3295,10 @@
 	$date{'iso-8601'}  = sprintf "%04d-%02d-%02dT%02d:%02d:%02dZ",
 	                     1900+$year, 1+$mon, $mday, $hour ,$min, $sec;
 
-	$tz =~ m/^([+\-][0-9][0-9])([0-9][0-9])$/;
-	my $local = $epoch + ((int $1 + ($2/60)) * 3600);
+	my ($tz_sign, $tz_hour, $tz_min) =
+		($tz =~ m/^([-+])(\d\d)(\d\d)$/);
+	$tz_sign = ($tz_sign eq '-' ? -1 : +1);
+	my $local = $epoch + $tz_sign*((($tz_hour*60) + $tz_min)*60);
 	($sec, $min, $hour, $mday, $mon, $year, $wday, $yday) = gmtime($local);
 	$date{'hour_local'} = $hour;
 	$date{'minute_local'} = $min;
@@ -2573,7 +3373,7 @@
 		} elsif ((!defined $withparents) && ($line =~ m/^parent ([0-9a-fA-F]{40})$/)) {
 			push @parents, $1;
 		} elsif ($line =~ m/^author (.*) ([0-9]+) (.*)$/) {
-			$co{'author'} = $1;
+			$co{'author'} = to_utf8($1);
 			$co{'author_epoch'} = $2;
 			$co{'author_tz'} = $3;
 			if ($co{'author'} =~ m/^([^<]+) <([^>]*)>/) {
@@ -2583,10 +3383,9 @@
 				$co{'author_name'} = $co{'author'};
 			}
 		} elsif ($line =~ m/^committer (.*) ([0-9]+) (.*)$/) {
-			$co{'committer'} = $1;
+			$co{'committer'} = to_utf8($1);
 			$co{'committer_epoch'} = $2;
 			$co{'committer_tz'} = $3;
-			$co{'committer_name'} = $co{'committer'};
 			if ($co{'committer'} =~ m/^([^<]+) <([^>]*)>/) {
 				$co{'committer_name'}  = $1;
 				$co{'committer_email'} = $2;
@@ -2754,16 +3553,31 @@
 	my %opts = @_;
 	my %res;
 
-	#'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa	panic.c'
-	$line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/s;
+	if ($opts{'-l'}) {
+		#'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa   16717	panic.c'
+		$line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40}) +(-|[0-9]+)\t(.+)$/s;
 
-	$res{'mode'} = $1;
-	$res{'type'} = $2;
-	$res{'hash'} = $3;
-	if ($opts{'-z'}) {
-		$res{'name'} = $4;
+		$res{'mode'} = $1;
+		$res{'type'} = $2;
+		$res{'hash'} = $3;
+		$res{'size'} = $4;
+		if ($opts{'-z'}) {
+			$res{'name'} = $5;
+		} else {
+			$res{'name'} = unquote($5);
+		}
 	} else {
-		$res{'name'} = unquote($4);
+		#'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa	panic.c'
+		$line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/s;
+
+		$res{'mode'} = $1;
+		$res{'type'} = $2;
+		$res{'hash'} = $3;
+		if ($opts{'-z'}) {
+			$res{'name'} = $4;
+		} else {
+			$res{'name'} = unquote($4);
+		}
 	}
 
 	return wantarray ? %res : \%res;
@@ -2819,13 +3633,15 @@
 ## parse to array of hashes functions
 
 sub git_get_heads_list {
-	my $limit = shift;
+	my ($limit, @classes) = @_;
+	@classes = ('heads') unless @classes;
+	my @patterns = map { "refs/$_" } @classes;
 	my @headslist;
 
 	open my $fd, '-|', git_cmd(), 'for-each-ref',
 		($limit ? '--count='.($limit+1) : ()), '--sort=-committerdate',
 		'--format=%(objectname) %(refname) %(subject)%00%(committer)',
-		'refs/heads'
+		@patterns
 		or return;
 	while (my $line = <$fd>) {
 		my %ref_item;
@@ -2836,7 +3652,7 @@
 		my ($committer, $epoch, $tz) =
 			($committerinfo =~ /^(.*) ([0-9]+) (.*)$/);
 		$ref_item{'fullname'}  = $name;
-		$name =~ s!^refs/heads/!!;
+		$name =~ s!^refs/(?:head|remote)s/!!;
 
 		$ref_item{'name'}  = $name;
 		$ref_item{'id'}    = $hash;
@@ -2941,12 +3757,9 @@
 	open(my $mh, '<', $mimemap) or return undef;
 	while (<$mh>) {
 		next if m/^#/; # skip comments
-		my ($mimetype, $exts) = split(/\t+/);
-		if (defined $exts) {
-			my @exts = split(/\s+/, $exts);
-			foreach my $ext (@exts) {
-				$mimemap{$ext} = $mimetype;
-			}
+		my ($mimetype, @exts) = split(/\s+/);
+		foreach my $ext (@exts) {
+			$mimemap{$ext} = $mimetype;
 		}
 	}
 	close($mh);
@@ -3010,27 +3823,64 @@
 	return $type;
 }
 
+# guess file syntax for syntax highlighting; return undef if no highlighting
+# the name of syntax can (in the future) depend on syntax highlighter used
+sub guess_file_syntax {
+	my ($highlight, $mimetype, $file_name) = @_;
+	return undef unless ($highlight && defined $file_name);
+	my $basename = basename($file_name, '.in');
+	return $highlight_basename{$basename}
+		if exists $highlight_basename{$basename};
+
+	$basename =~ /\.([^.]*)$/;
+	my $ext = $1 or return undef;
+	return $highlight_ext{$ext}
+		if exists $highlight_ext{$ext};
+
+	return undef;
+}
+
+# run highlighter and return FD of its output,
+# or return original FD if no highlighting
+sub run_highlighter {
+	my ($fd, $highlight, $syntax) = @_;
+	return $fd unless ($highlight && defined $syntax);
+
+	close $fd;
+	open $fd, quote_command(git_cmd(), "cat-file", "blob", $hash)." | ".
+	          quote_command($highlight_bin).
+	          " --replace-tabs=8 --fragment --syntax $syntax |"
+		or die_error(500, "Couldn't open file or run syntax highlighter");
+	return $fd;
+}
+
 ## ======================================================================
 ## functions printing HTML: header, footer, error page
 
-sub git_header_html {
-	my $status = shift || "200 OK";
-	my $expires = shift;
+sub get_page_title {
+	my $title = to_utf8($site_name);
 
-	my $title = "$site_name";
-	if (defined $project) {
-		$title .= " - " . to_utf8($project);
-		if (defined $action) {
-			$title .= "/$action";
-			if (defined $file_name) {
-				$title .= " - " . esc_path($file_name);
-				if ($action eq "tree" && $file_name !~ m|/$|) {
-					$title .= "/";
-				}
-			}
+	unless (defined $project) {
+		if (defined $project_filter) {
+			$title .= " - projects in '" . esc_path($project_filter) . "'";
 		}
+		return $title;
 	}
-	my $content_type;
+	$title .= " - " . to_utf8($project);
+
+	return $title unless (defined $action);
+	$title .= "/$action"; # $action is US-ASCII (7bit ASCII)
+
+	return $title unless (defined $file_name);
+	$title .= " - " . esc_path($file_name);
+	if ($action eq "tree" && $file_name !~ m|/$|) {
+		$title .= "/";
+	}
+
+	return $title;
+}
+
+sub get_content_type_html {
 	# 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
@@ -3038,54 +3888,28 @@
 	if (defined $cgi->http('HTTP_ACCEPT') &&
 	    $cgi->http('HTTP_ACCEPT') =~ m/(,|;|\s|^)application\/xhtml\+xml(,|;|\s|$)/ &&
 	    $cgi->Accept('application/xhtml+xml') != 0) {
-		$content_type = 'application/xhtml+xml';
+		return 'application/xhtml+xml';
 	} else {
-		$content_type = 'text/html';
+		return 'text/html';
 	}
-	print $cgi->header(-type=>$content_type, -charset => 'utf-8',
-	                   -status=> $status, -expires => $expires);
-	my $mod_perl_version = $ENV{'MOD_PERL'} ? " $ENV{'MOD_PERL'}" : '';
-	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">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US">
-<!-- git web interface version $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="$content_type; charset=utf-8"/>
-<meta name="generator" content="gitweb/$version git/$git_version$mod_perl_version"/>
-<meta name="robots" content="index, nofollow"/>
-<title>$title</title>
-EOF
-	# the stylesheet, favicon etc urls won't work correctly with path_info
-	# unless we set the appropriate base URL
-	if ($ENV{'PATH_INFO'}) {
-		print "<base href=\"".esc_url($base_url)."\" />\n";
-	}
-	# print out each stylesheet that exist, providing backwards capability
-	# for those people who defined $stylesheet in a config file
-	if (defined $stylesheet) {
-		print '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'"/>'."\n";
-	} else {
-		foreach my $stylesheet (@stylesheets) {
-			next unless $stylesheet;
-			print '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'"/>'."\n";
-		}
-	}
+}
+
+sub print_feed_meta {
 	if (defined $project) {
 		my %href_params = get_feed_info();
 		if (!exists $href_params{'-title'}) {
 			$href_params{'-title'} = 'log';
 		}
 
-		foreach my $format qw(RSS Atom) {
+		foreach my $format (qw(RSS Atom)) {
 			my $type = lc($format);
 			my %link_attr = (
 				'-rel' => 'alternate',
-				'-title' => "$project - $href_params{'-title'} - $format feed",
+				'-title' => esc_attr("$project - $href_params{'-title'} - $format feed"),
 				'-type' => "application/$type+xml"
 			);
 
+			$href_params{'extra_options'} = undef;
 			$href_params{'action'} = $type;
 			$link_attr{'-href'} = href(%href_params);
 			print "<link ".
@@ -3109,71 +3933,163 @@
 	} else {
 		printf('<link rel="alternate" title="%s projects list" '.
 		       'href="%s" type="text/plain; charset=utf-8" />'."\n",
-		       $site_name, href(project=>undef, action=>"project_index"));
+		       esc_attr($site_name), href(project=>undef, action=>"project_index"));
 		printf('<link rel="alternate" title="%s projects feeds" '.
 		       'href="%s" type="text/x-opml" />'."\n",
-		       $site_name, href(project=>undef, action=>"opml"));
+		       esc_attr($site_name), href(project=>undef, action=>"opml"));
 	}
+}
+
+sub print_header_links {
+	my $status = shift;
+
+	# print out each stylesheet that exist, providing backwards capability
+	# for those people who defined $stylesheet in a config file
+	if (defined $stylesheet) {
+		print '<link rel="stylesheet" type="text/css" href="'.esc_url($stylesheet).'"/>'."\n";
+	} else {
+		foreach my $stylesheet (@stylesheets) {
+			next unless $stylesheet;
+			print '<link rel="stylesheet" type="text/css" href="'.esc_url($stylesheet).'"/>'."\n";
+		}
+	}
+	print_feed_meta()
+		if ($status eq '200 OK');
 	if (defined $favicon) {
-		print qq(<link rel="shortcut icon" href="$favicon" type="image/png" />\n);
+		print qq(<link rel="shortcut icon" href=").esc_url($favicon).qq(" type="image/png" />\n);
+	}
+}
+
+sub print_nav_breadcrumbs_path {
+	my $dirprefix = undef;
+	while (my $part = shift) {
+		$dirprefix .= "/" if defined $dirprefix;
+		$dirprefix .= $part;
+		print $cgi->a({-href => href(project => undef,
+		                             project_filter => $dirprefix,
+		                             action => "project_list")},
+			      esc_html($part)) . " / ";
+	}
+}
+
+sub print_nav_breadcrumbs {
+	my %opts = @_;
+
+	print $cgi->a({-href => esc_url($home_link)}, $home_link_str) . " / ";
+	if (defined $project) {
+		my @dirname = split '/', $project;
+		my $projectbasename = pop @dirname;
+		print_nav_breadcrumbs_path(@dirname);
+		print $cgi->a({-href => href(action=>"summary")}, esc_html($projectbasename));
+		if (defined $action) {
+			my $action_print = $action ;
+			if (defined $opts{-action_extra}) {
+				$action_print = $cgi->a({-href => href(action=>$action)},
+					$action);
+			}
+			print " / $action_print";
+		}
+		if (defined $opts{-action_extra}) {
+			print " / $opts{-action_extra}";
+		}
+		print "\n";
+	} elsif (defined $project_filter) {
+		print_nav_breadcrumbs_path(split '/', $project_filter);
+	}
+}
+
+sub print_search_form {
+	if (!defined $searchtext) {
+		$searchtext = "";
+	}
+	my $search_hash;
+	if (defined $hash_base) {
+		$search_hash = $hash_base;
+	} elsif (defined $hash) {
+		$search_hash = $hash;
+	} else {
+		$search_hash = "HEAD";
+	}
+	my $action = $my_uri;
+	my $use_pathinfo = gitweb_check_feature('pathinfo');
+	if ($use_pathinfo) {
+		$action .= "/".esc_url($project);
+	}
+	print $cgi->startform(-method => "get", -action => $action) .
+	      "<div class=\"search\">\n" .
+	      (!$use_pathinfo &&
+	      $cgi->input({-name=>"p", -value=>$project, -type=>"hidden"}) . "\n") .
+	      $cgi->input({-name=>"a", -value=>"search", -type=>"hidden"}) . "\n" .
+	      $cgi->input({-name=>"h", -value=>$search_hash, -type=>"hidden"}) . "\n" .
+	      $cgi->popup_menu(-name => 'st', -default => 'commit',
+	                       -values => ['commit', 'grep', 'author', 'committer', 'pickaxe']) .
+	      $cgi->sup($cgi->a({-href => href(action=>"search_help")}, "?")) .
+	      " search:\n",
+	      $cgi->textfield(-name => "s", -value => $searchtext, -override => 1) . "\n" .
+	      "<span title=\"Extended regular expression\">" .
+	      $cgi->checkbox(-name => 'sr', -value => 1, -label => 're',
+	                     -checked => $search_use_regexp) .
+	      "</span>" .
+	      "</div>" .
+	      $cgi->end_form() . "\n";
+}
+
+sub git_header_html {
+	my $status = shift || "200 OK";
+	my $expires = shift;
+	my %opts = @_;
+
+	my $title = get_page_title();
+	my $content_type = get_content_type_html();
+	print $cgi->header(-type=>$content_type, -charset => 'utf-8',
+	                   -status=> $status, -expires => $expires)
+		unless ($opts{'-no_http_header'});
+	my $mod_perl_version = $ENV{'MOD_PERL'} ? " $ENV{'MOD_PERL'}" : '';
+	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">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US">
+<!-- git web interface version $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="$content_type; charset=utf-8"/>
+<meta name="generator" content="gitweb/$version git/$git_version$mod_perl_version"/>
+<meta name="robots" content="index, nofollow"/>
+<title>$title</title>
+EOF
+	# the stylesheet, favicon etc urls won't work correctly with path_info
+	# unless we set the appropriate base URL
+	if ($ENV{'PATH_INFO'}) {
+		print "<base href=\"".esc_url($base_url)."\" />\n";
+	}
+	print_header_links($status);
+
+	if (defined $site_html_head_string) {
+		print to_utf8($site_html_head_string);
 	}
 
 	print "</head>\n" .
 	      "<body>\n";
 
-	if (-f $site_header) {
+	if (defined $site_header && -f $site_header) {
 		insert_file($site_header);
 	}
 
-	print "<div class=\"page_header\">\n" .
-	      $cgi->a({-href => esc_url($logo_url),
-	               -title => $logo_label},
-	              qq(<img src="$logo" width="72" height="27" alt="git" class="logo"/>));
-	print $cgi->a({-href => esc_url($home_link)}, $home_link_str) . " / ";
-	if (defined $project) {
-		print $cgi->a({-href => href(action=>"summary")}, esc_html($project));
-		if (defined $action) {
-			print " / $action";
-		}
-		print "\n";
+	print "<div class=\"page_header\">\n";
+	if (defined $logo) {
+		print $cgi->a({-href => esc_url($logo_url),
+		               -title => $logo_label},
+		              $cgi->img({-src => esc_url($logo),
+		                         -width => 72, -height => 27,
+		                         -alt => "git",
+		                         -class => "logo"}));
 	}
+	print_nav_breadcrumbs(%opts);
 	print "</div>\n";
 
 	my $have_search = gitweb_check_feature('search');
 	if (defined $project && $have_search) {
-		if (!defined $searchtext) {
-			$searchtext = "";
-		}
-		my $search_hash;
-		if (defined $hash_base) {
-			$search_hash = $hash_base;
-		} elsif (defined $hash) {
-			$search_hash = $hash;
-		} else {
-			$search_hash = "HEAD";
-		}
-		my $action = $my_uri;
-		my $use_pathinfo = gitweb_check_feature('pathinfo');
-		if ($use_pathinfo) {
-			$action .= "/".esc_url($project);
-		}
-		print $cgi->startform(-method => "get", -action => $action) .
-		      "<div class=\"search\">\n" .
-		      (!$use_pathinfo &&
-		      $cgi->input({-name=>"p", -value=>$project, -type=>"hidden"}) . "\n") .
-		      $cgi->input({-name=>"a", -value=>"search", -type=>"hidden"}) . "\n" .
-		      $cgi->input({-name=>"h", -value=>$search_hash, -type=>"hidden"}) . "\n" .
-		      $cgi->popup_menu(-name => 'st', -default => 'commit',
-		                       -values => ['commit', 'grep', 'author', 'committer', 'pickaxe']) .
-		      $cgi->sup($cgi->a({-href => href(action=>"search_help")}, "?")) .
-		      " search:\n",
-		      $cgi->textfield(-name => "s", -value => $searchtext) . "\n" .
-		      "<span title=\"Extended regular expression\">" .
-		      $cgi->checkbox(-name => 'sr', -value => 1, -label => 're',
-		                     -checked => $search_use_regexp) .
-		      "</span>" .
-		      "</div>" .
-		      $cgi->end_form() . "\n";
+		print_search_form();
 	}
 }
 
@@ -3193,7 +4109,7 @@
 		}
 		$href_params{'-title'} ||= 'log';
 
-		foreach my $format qw(RSS Atom) {
+		foreach my $format (qw(RSS Atom)) {
 			$href_params{'action'} = lc($format);
 			print $cgi->a({-href => href(%href_params),
 			              -title => "$href_params{'-title'} $format feed",
@@ -3201,22 +4117,62 @@
 		}
 
 	} else {
-		print $cgi->a({-href => href(project=>undef, action=>"opml"),
+		print $cgi->a({-href => href(project=>undef, action=>"opml",
+		                             project_filter => $project_filter),
 		              -class => $feed_class}, "OPML") . " ";
-		print $cgi->a({-href => href(project=>undef, action=>"project_index"),
+		print $cgi->a({-href => href(project=>undef, action=>"project_index",
+		                             project_filter => $project_filter),
 		              -class => $feed_class}, "TXT") . "\n";
 	}
 	print "</div>\n"; # class="page_footer"
 
-	if (-f $site_footer) {
+	if (defined $t0 && gitweb_check_feature('timed')) {
+		print "<div id=\"generating_info\">\n";
+		print 'This page took '.
+		      '<span id="generating_time" class="time_span">'.
+		      tv_interval($t0, [ gettimeofday() ]).
+		      ' seconds </span>'.
+		      ' and '.
+		      '<span id="generating_cmd">'.
+		      $number_of_git_cmds.
+		      '</span> git commands '.
+		      " to generate.\n";
+		print "</div>\n"; # class="page_footer"
+	}
+
+	if (defined $site_footer && -f $site_footer) {
 		insert_file($site_footer);
 	}
 
+	print qq!<script type="text/javascript" src="!.esc_url($javascript).qq!"></script>\n!;
+	if (defined $action &&
+	    $action eq 'blame_incremental') {
+		print qq!<script type="text/javascript">\n!.
+		      qq!startBlame("!. href(action=>"blame_data", -replay=>1) .qq!",\n!.
+		      qq!           "!. href() .qq!");\n!.
+		      qq!</script>\n!;
+	} else {
+		my ($jstimezone, $tz_cookie, $datetime_class) =
+			gitweb_get_feature('javascript-timezone');
+
+		print qq!<script type="text/javascript">\n!.
+		      qq!window.onload = function () {\n!;
+		if (gitweb_check_feature('javascript-actions')) {
+			print qq!	fixLinks();\n!;
+		}
+		if ($jstimezone && $tz_cookie && $datetime_class) {
+			print qq!	var tz_cookie = { name: '$tz_cookie', expires: 14, path: '/' };\n!. # in days
+			      qq!	onloadTZSetup('$jstimezone', tz_cookie, '$datetime_class');\n!;
+		}
+		print qq!};\n!.
+		      qq!</script>\n!;
+	}
+
 	print "</body>\n" .
 	      "</html>";
 }
 
-# die_error(<http_status_code>, <error_message>)
+# die_error(<http_status_code>, <error_message>[, <detailed_html_description>])
 # Example: die_error(404, 'Hash not found')
 # By convention, use the following status codes (as defined in RFC 2616):
 # 400: Invalid or missing CGI parameters, or
@@ -3227,24 +4183,37 @@
 # 500: The server isn't configured properly, or
 #      an internal error occurred (e.g. failed assertions caused by bugs), or
 #      an unknown error occurred (e.g. the git binary died unexpectedly).
+# 503: The server is currently unavailable (because it is overloaded,
+#      or down for maintenance).  Generally, this is a temporary state.
 sub die_error {
 	my $status = shift || 500;
-	my $error = shift || "Internal server error";
+	my $error = esc_html(shift) || "Internal Server Error";
+	my $extra = shift;
+	my %opts = @_;
 
-	my %http_responses = (400 => '400 Bad Request',
-			      403 => '403 Forbidden',
-			      404 => '404 Not Found',
-			      500 => '500 Internal Server Error');
-	git_header_html($http_responses{$status});
+	my %http_responses = (
+		400 => '400 Bad Request',
+		403 => '403 Forbidden',
+		404 => '404 Not Found',
+		500 => '500 Internal Server Error',
+		503 => '503 Service Unavailable',
+	);
+	git_header_html($http_responses{$status}, undef, %opts);
 	print <<EOF;
 <div class="page_body">
 <br /><br />
 $status - $error
 <br />
-</div>
 EOF
+	if (defined $extra) {
+		print "<hr />\n" .
+		      "$extra\n";
+	}
+	print "</div>\n";
+
 	git_footer_html();
-	exit;
+	goto DONE_GITWEB
+		unless ($opts{'-error_handler'});
 }
 
 ## ----------------------------------------------------------------------
@@ -3300,23 +4269,32 @@
 	      "</div>\n";
 }
 
+# returns a submenu for the nagivation of the refs views (tags, heads,
+# remotes) with the current view disabled and the remotes view only
+# available if the feature is enabled
+sub format_ref_views {
+	my ($current) = @_;
+	my @ref_views = qw{tags heads};
+	push @ref_views, 'remotes' if gitweb_check_feature('remote_heads');
+	return join " | ", map {
+		$_ eq $current ? $_ :
+		$cgi->a({-href => href(action=>$_)}, $_)
+	} @ref_views
+}
+
 sub format_paging_nav {
-	my ($action, $hash, $head, $page, $has_next_link) = @_;
+	my ($action, $page, $has_next_link) = @_;
 	my $paging_nav;
 
 
-	if ($hash ne $head || $page) {
-		$paging_nav .= $cgi->a({-href => href(action=>$action)}, "HEAD");
-	} else {
-		$paging_nav .= "HEAD";
-	}
-
 	if ($page > 0) {
-		$paging_nav .= " &sdot; " .
+		$paging_nav .=
+			$cgi->a({-href => href(-replay=>1, page=>undef)}, "first") .
+			" &sdot; " .
 			$cgi->a({-href => href(-replay=>1, page=>$page-1),
 			         -accesskey => "p", -title => "Alt-p"}, "prev");
 	} else {
-		$paging_nav .= " &sdot; prev";
+		$paging_nav .= "first &sdot; prev";
 	}
 
 	if ($has_next_link) {
@@ -3347,15 +4325,68 @@
 	      "\n</div>\n";
 }
 
-sub print_local_time {
-	my %date = @_;
-	if ($date{'hour_local'} < 6) {
-		printf(" (<span class=\"atnight\">%02d:%02d</span> %s)",
-			$date{'hour_local'}, $date{'minute_local'}, $date{'tz_local'});
-	} else {
-		printf(" (%02d:%02d %s)",
-			$date{'hour_local'}, $date{'minute_local'}, $date{'tz_local'});
+sub format_repo_url {
+	my ($name, $url) = @_;
+	return "<tr class=\"metadata_url\"><td>$name</td><td>$url</td></tr>\n";
+}
+
+# Group output by placing it in a DIV element and adding a header.
+# Options for start_div() can be provided by passing a hash reference as the
+# first parameter to the function.
+# Options to git_print_header_div() can be provided by passing an array
+# reference. This must follow the options to start_div if they are present.
+# The content can be a scalar, which is output as-is, a scalar reference, which
+# is output after html escaping, an IO handle passed either as *handle or
+# *handle{IO}, or a function reference. In the latter case all following
+# parameters will be taken as argument to the content function call.
+sub git_print_section {
+	my ($div_args, $header_args, $content);
+	my $arg = shift;
+	if (ref($arg) eq 'HASH') {
+		$div_args = $arg;
+		$arg = shift;
 	}
+	if (ref($arg) eq 'ARRAY') {
+		$header_args = $arg;
+		$arg = shift;
+	}
+	$content = $arg;
+
+	print $cgi->start_div($div_args);
+	git_print_header_div(@$header_args);
+
+	if (ref($content) eq 'CODE') {
+		$content->(@_);
+	} elsif (ref($content) eq 'SCALAR') {
+		print esc_html($$content);
+	} elsif (ref($content) eq 'GLOB' or ref($content) eq 'IO::Handle') {
+		print <$content>;
+	} elsif (!ref($content) && defined($content)) {
+		print $content;
+	}
+
+	print $cgi->end_div;
+}
+
+sub format_timestamp_html {
+	my $date = shift;
+	my $strtime = $date->{'rfc2822'};
+
+	my (undef, undef, $datetime_class) =
+		gitweb_get_feature('javascript-timezone');
+	if ($datetime_class) {
+		$strtime = qq!<span class="$datetime_class">$strtime</span>!;
+	}
+
+	my $localtime_format = '(%02d:%02d %s)';
+	if ($date->{'hour_local'} < 6) {
+		$localtime_format = '(<span class="atnight">%02d:%02d</span> %s)';
+	}
+	$strtime .= ' ' .
+	            sprintf($localtime_format,
+	                    $date->{'hour_local'}, $date->{'minute_local'}, $date->{'tz_local'});
+
+	return $strtime;
 }
 
 # Outputs the author name and date in long form
@@ -3363,20 +4394,20 @@
 	my $co = shift;
 	my %opts = @_;
 	my $tag = $opts{-tag} || 'div';
+	my $author = $co->{'author_name'};
 
 	my %ad = parse_date($co->{'author_epoch'}, $co->{'author_tz'});
 	print "<$tag class=\"author_date\">" .
-	      esc_html($co->{'author_name'}) .
-	      " [$ad{'rfc2822'}";
-	print_local_time(%ad) if ($opts{-localtime});
-	print "]" . git_get_avatar($co->{'author_email'}, -pad_before => 1)
-		  . "</$tag>\n";
+	      format_search_author($author, "author", esc_html($author)) .
+	      " [".format_timestamp_html(\%ad)."]".
+	      git_get_avatar($co->{'author_email'}, -pad_before => 1) .
+	      "</$tag>\n";
 }
 
 # Outputs table rows containing the full author or committer information,
-# in the format expected for 'commit' view (& similia).
+# in the format expected for 'commit' view (& similar).
 # Parameters are a commit hash reference, followed by the list of people
-# to output information for. If the list is empty it defalts to both
+# to output information for. If the list is empty it defaults to both
 # author and committer.
 sub git_print_authorship_rows {
 	my $co = shift;
@@ -3385,14 +4416,18 @@
 	@people = ('author', 'committer') unless @people;
 	foreach my $who (@people) {
 		my %wd = parse_date($co->{"${who}_epoch"}, $co->{"${who}_tz"});
-		print "<tr><td>$who</td><td>" . esc_html($co->{$who}) . "</td>" .
-		      "<td rowspan=\"2\">" .
+		print "<tr><td>$who</td><td>" .
+		      format_search_author($co->{"${who}_name"}, $who,
+		                           esc_html($co->{"${who}_name"})) . " " .
+		      format_search_author($co->{"${who}_email"}, $who,
+		                           esc_html("<" . $co->{"${who}_email"} . ">")) .
+		      "</td><td rowspan=\"2\">" .
 		      git_get_avatar($co->{"${who}_email"}, -size => 'double') .
 		      "</td></tr>\n" .
 		      "<tr>" .
-		      "<td></td><td> $wd{'rfc2822'}";
-		print_local_time(%wd);
-		print "</td>" .
+		      "<td></td><td>" .
+		      format_timestamp_html(\%wd) .
+		      "</td>" .
 		      "</tr>\n";
 	}
 }
@@ -3449,30 +4484,33 @@
 	}
 
 	# print log
-	my $signoff = 0;
-	my $empty = 0;
+	my $skip_blank_line = 0;
 	foreach my $line (@$log) {
-		if ($line =~ m/^ *(signed[ \-]off[ \-]by[ :]|acked[ \-]by[ :]|cc[ :])/i) {
-			$signoff = 1;
-			$empty = 0;
+		if ($line =~ m/^\s*([A-Z][-A-Za-z]*-[Bb]y|C[Cc]): /) {
 			if (! $opts{'-remove_signoff'}) {
 				print "<span class=\"signoff\">" . esc_html($line) . "</span><br/>\n";
-				next;
-			} else {
-				# remove signoff lines
-				next;
+				$skip_blank_line = 1;
 			}
-		} else {
-			$signoff = 0;
+			next;
+		}
+
+		if ($line =~ m,\s*([a-z]*link): (https?://\S+),i) {
+			if (! $opts{'-remove_signoff'}) {
+				print "<span class=\"signoff\">" . esc_html($1) . ": " .
+					"<a href=\"" . esc_html($2) . "\">" . esc_html($2) . "</a>" .
+					"</span><br/>\n";
+				$skip_blank_line = 1;
+			}
+			next;
 		}
 
 		# print only one empty line
 		# do not print empty line after signoff
 		if ($line eq "") {
-			next if ($empty || $signoff);
-			$empty = 1;
+			next if ($skip_blank_line);
+			$skip_blank_line = 1;
 		} else {
-			$empty = 0;
+			$skip_blank_line = 0;
 		}
 
 		print format_log_line_html($line) . "<br/>\n";
@@ -3480,7 +4518,7 @@
 
 	if ($opts{'-final_empty_line'}) {
 		# end with single empty line
-		print "<br/>\n" unless $empty;
+		print "<br/>\n" unless $skip_blank_line;
 	}
 }
 
@@ -3554,6 +4592,9 @@
 	# and link is the action links of the entry.
 
 	print "<td class=\"mode\">" . mode_str($t->{'mode'}) . "</td>\n";
+	if (exists $t->{'size'}) {
+		print "<td class=\"size\">$t->{'size'}</td>\n";
+	}
 	if ($t->{'type'} eq "blob") {
 		print "<td class=\"list\">" .
 			$cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'},
@@ -3599,12 +4640,14 @@
 	} elsif ($t->{'type'} eq "tree") {
 		print "<td class=\"list\">";
 		print $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'},
-		                             file_name=>"$basedir$t->{'name'}", %base_key)},
+		                             file_name=>"$basedir$t->{'name'}",
+		                             %base_key)},
 		              esc_path($t->{'name'}));
 		print "</td>\n";
 		print "<td class=\"link\">";
 		print $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'},
-		                             file_name=>"$basedir$t->{'name'}", %base_key)},
+		                             file_name=>"$basedir$t->{'name'}",
+		                             %base_key)},
 		              "tree");
 		if (defined $hash_base) {
 			print " | " .
@@ -3737,7 +4780,8 @@
 				# link to patch
 				$patchno++;
 				print "<td class=\"link\">" .
-				      $cgi->a({-href => "#patch$patchno"}, "patch") .
+				      $cgi->a({-href => href(-anchor=>"patch$patchno")},
+				              "patch") .
 				      " | " .
 				      "</td>\n";
 			}
@@ -3814,7 +4858,7 @@
 		}
 		if ($diff->{'from_mode'} ne ('0' x 6)) {
 			$from_mode_oct = oct $diff->{'from_mode'};
-			if (S_ISREG($to_mode_oct)) { # only for regular file
+			if (S_ISREG($from_mode_oct)) { # only for regular file
 				$from_mode_str = sprintf("%04o", $from_mode_oct & 0777); # permission bits
 			}
 			$from_file_type = file_type($diff->{'from_mode'});
@@ -3834,8 +4878,9 @@
 			if ($action eq 'commitdiff') {
 				# link to patch
 				$patchno++;
-				print $cgi->a({-href => "#patch$patchno"}, "patch");
-				print " | ";
+				print $cgi->a({-href => href(-anchor=>"patch$patchno")},
+				              "patch") .
+				      " | ";
 			}
 			print $cgi->a({-href => href(action=>"blob", hash=>$diff->{'to_id'},
 			                             hash_base=>$hash, file_name=>$diff->{'file'})},
@@ -3854,8 +4899,9 @@
 			if ($action eq 'commitdiff') {
 				# link to patch
 				$patchno++;
-				print $cgi->a({-href => "#patch$patchno"}, "patch");
-				print " | ";
+				print $cgi->a({-href => href(-anchor=>"patch$patchno")},
+				              "patch") .
+				      " | ";
 			}
 			print $cgi->a({-href => href(action=>"blob", hash=>$diff->{'from_id'},
 			                             hash_base=>$parent, file_name=>$diff->{'file'})},
@@ -3896,7 +4942,8 @@
 			if ($action eq 'commitdiff') {
 				# link to patch
 				$patchno++;
-				print $cgi->a({-href => "#patch$patchno"}, "patch") .
+				print $cgi->a({-href => href(-anchor=>"patch$patchno")},
+				              "patch") .
 				      " | ";
 			} elsif ($diff->{'to_id'} ne $diff->{'from_id'}) {
 				# "commit" view and modified file (not onlu mode changed)
@@ -3941,7 +4988,8 @@
 			if ($action eq 'commitdiff') {
 				# link to patch
 				$patchno++;
-				print $cgi->a({-href => "#patch$patchno"}, "patch") .
+				print $cgi->a({-href => href(-anchor=>"patch$patchno")},
+				              "patch") .
 				      " | ";
 			} elsif ($diff->{'to_id'} ne $diff->{'from_id'}) {
 				# "commit" view and modified file (not only pure rename or copy)
@@ -3972,8 +5020,239 @@
 	print "</table>\n";
 }
 
+# Print context lines and then rem/add lines in a side-by-side manner.
+sub print_sidebyside_diff_lines {
+	my ($ctx, $rem, $add) = @_;
+
+	# print context block before add/rem block
+	if (@$ctx) {
+		print join '',
+			'<div class="chunk_block ctx">',
+				'<div class="old">',
+				@$ctx,
+				'</div>',
+				'<div class="new">',
+				@$ctx,
+				'</div>',
+			'</div>';
+	}
+
+	if (!@$add) {
+		# pure removal
+		print join '',
+			'<div class="chunk_block rem">',
+				'<div class="old">',
+				@$rem,
+				'</div>',
+			'</div>';
+	} elsif (!@$rem) {
+		# pure addition
+		print join '',
+			'<div class="chunk_block add">',
+				'<div class="new">',
+				@$add,
+				'</div>',
+			'</div>';
+	} else {
+		print join '',
+			'<div class="chunk_block chg">',
+				'<div class="old">',
+				@$rem,
+				'</div>',
+				'<div class="new">',
+				@$add,
+				'</div>',
+			'</div>';
+	}
+}
+
+# Print context lines and then rem/add lines in inline manner.
+sub print_inline_diff_lines {
+	my ($ctx, $rem, $add) = @_;
+
+	print @$ctx, @$rem, @$add;
+}
+
+# Format removed and added line, mark changed part and HTML-format them.
+# Implementation is based on contrib/diff-highlight
+sub format_rem_add_lines_pair {
+	my ($rem, $add, $num_parents) = @_;
+
+	# We need to untabify lines before split()'ing them;
+	# otherwise offsets would be invalid.
+	chomp $rem;
+	chomp $add;
+	$rem = untabify($rem);
+	$add = untabify($add);
+
+	my @rem = split(//, $rem);
+	my @add = split(//, $add);
+	my ($esc_rem, $esc_add);
+	# Ignore leading +/- characters for each parent.
+	my ($prefix_len, $suffix_len) = ($num_parents, 0);
+	my ($prefix_has_nonspace, $suffix_has_nonspace);
+
+	my $shorter = (@rem < @add) ? @rem : @add;
+	while ($prefix_len < $shorter) {
+		last if ($rem[$prefix_len] ne $add[$prefix_len]);
+
+		$prefix_has_nonspace = 1 if ($rem[$prefix_len] !~ /\s/);
+		$prefix_len++;
+	}
+
+	while ($prefix_len + $suffix_len < $shorter) {
+		last if ($rem[-1 - $suffix_len] ne $add[-1 - $suffix_len]);
+
+		$suffix_has_nonspace = 1 if ($rem[-1 - $suffix_len] !~ /\s/);
+		$suffix_len++;
+	}
+
+	# Mark lines that are different from each other, but have some common
+	# part that isn't whitespace.  If lines are completely different, don't
+	# mark them because that would make output unreadable, especially if
+	# diff consists of multiple lines.
+	if ($prefix_has_nonspace || $suffix_has_nonspace) {
+		$esc_rem = esc_html_hl_regions($rem, 'marked',
+		        [$prefix_len, @rem - $suffix_len], -nbsp=>1);
+		$esc_add = esc_html_hl_regions($add, 'marked',
+		        [$prefix_len, @add - $suffix_len], -nbsp=>1);
+	} else {
+		$esc_rem = esc_html($rem, -nbsp=>1);
+		$esc_add = esc_html($add, -nbsp=>1);
+	}
+
+	return format_diff_line(\$esc_rem, 'rem'),
+	       format_diff_line(\$esc_add, 'add');
+}
+
+# HTML-format diff context, removed and added lines.
+sub format_ctx_rem_add_lines {
+	my ($ctx, $rem, $add, $num_parents) = @_;
+	my (@new_ctx, @new_rem, @new_add);
+	my $can_highlight = 0;
+	my $is_combined = ($num_parents > 1);
+
+	# Highlight if every removed line has a corresponding added line.
+	if (@$add > 0 && @$add == @$rem) {
+		$can_highlight = 1;
+
+		# Highlight lines in combined diff only if the chunk contains
+		# diff between the same version, e.g.
+		#
+		#    - a
+		#   -  b
+		#    + c
+		#   +  d
+		#
+		# Otherwise the highlightling would be confusing.
+		if ($is_combined) {
+			for (my $i = 0; $i < @$add; $i++) {
+				my $prefix_rem = substr($rem->[$i], 0, $num_parents);
+				my $prefix_add = substr($add->[$i], 0, $num_parents);
+
+				$prefix_rem =~ s/-/+/g;
+
+				if ($prefix_rem ne $prefix_add) {
+					$can_highlight = 0;
+					last;
+				}
+			}
+		}
+	}
+
+	if ($can_highlight) {
+		for (my $i = 0; $i < @$add; $i++) {
+			my ($line_rem, $line_add) = format_rem_add_lines_pair(
+			        $rem->[$i], $add->[$i], $num_parents);
+			push @new_rem, $line_rem;
+			push @new_add, $line_add;
+		}
+	} else {
+		@new_rem = map { format_diff_line($_, 'rem') } @$rem;
+		@new_add = map { format_diff_line($_, 'add') } @$add;
+	}
+
+	@new_ctx = map { format_diff_line($_, 'ctx') } @$ctx;
+
+	return (\@new_ctx, \@new_rem, \@new_add);
+}
+
+# Print context lines and then rem/add lines.
+sub print_diff_lines {
+	my ($ctx, $rem, $add, $diff_style, $num_parents) = @_;
+	my $is_combined = $num_parents > 1;
+
+	($ctx, $rem, $add) = format_ctx_rem_add_lines($ctx, $rem, $add,
+	        $num_parents);
+
+	if ($diff_style eq 'sidebyside' && !$is_combined) {
+		print_sidebyside_diff_lines($ctx, $rem, $add);
+	} else {
+		# default 'inline' style and unknown styles
+		print_inline_diff_lines($ctx, $rem, $add);
+	}
+}
+
+sub print_diff_chunk {
+	my ($diff_style, $num_parents, $from, $to, @chunk) = @_;
+	my (@ctx, @rem, @add);
+
+	# The class of the previous line.
+	my $prev_class = '';
+
+	return unless @chunk;
+
+	# incomplete last line might be among removed or added lines,
+	# or both, or among context lines: find which
+	for (my $i = 1; $i < @chunk; $i++) {
+		if ($chunk[$i][0] eq 'incomplete') {
+			$chunk[$i][0] = $chunk[$i-1][0];
+		}
+	}
+
+	# guardian
+	push @chunk, ["", ""];
+
+	foreach my $line_info (@chunk) {
+		my ($class, $line) = @$line_info;
+
+		# print chunk headers
+		if ($class && $class eq 'chunk_header') {
+			print format_diff_line($line, $class, $from, $to);
+			next;
+		}
+
+		## print from accumulator when have some add/rem lines or end
+		# of chunk (flush context lines), or when have add and rem
+		# lines and new block is reached (otherwise add/rem lines could
+		# be reordered)
+		if (!$class || ((@rem || @add) && $class eq 'ctx') ||
+		    (@rem && @add && $class ne $prev_class)) {
+			print_diff_lines(\@ctx, \@rem, \@add,
+		                         $diff_style, $num_parents);
+			@ctx = @rem = @add = ();
+		}
+
+		## adding lines to accumulator
+		# guardian value
+		last unless $line;
+		# rem, add or change
+		if ($class eq 'rem') {
+			push @rem, $line;
+		} elsif ($class eq 'add') {
+			push @add, $line;
+		}
+		# context line
+		if ($class eq 'ctx') {
+			push @ctx, $line;
+		}
+
+		$prev_class = $class;
+	}
+}
+
 sub git_patchset_body {
-	my ($fd, $difftree, $hash, @hash_parents) = @_;
+	my ($fd, $diff_style, $difftree, $hash, @hash_parents) = @_;
 	my ($hash_parent) = $hash_parents[0];
 
 	my $is_combined = (@hash_parents > 1);
@@ -3983,6 +5262,7 @@
 	my $diffinfo;
 	my $to_name;
 	my (%from, %to);
+	my @chunk; # for side-by-side diff
 
 	print "<div class=\"patchset\">\n";
 
@@ -4089,15 +5369,26 @@
 
 			next PATCH if ($patch_line =~ m/^diff /);
 
-			print format_diff_line($patch_line, \%from, \%to);
+			my $class = diff_line_class($patch_line, \%from, \%to);
+
+			if ($class eq 'chunk_header') {
+				print_diff_chunk($diff_style, scalar @hash_parents, \%from, \%to, @chunk);
+				@chunk = ();
+			}
+
+			push @chunk, [ $class, $patch_line ];
 		}
 
 	} continue {
+		if (@chunk) {
+			print_diff_chunk($diff_style, scalar @hash_parents, \%from, \%to, @chunk);
+			@chunk = ();
+		}
 		print "</div>\n"; # class="patch"
 	}
 
-	# for compact combined (--cc) format, with chunk and patch simpliciaction
-	# patchset might be empty, but there might be unprocessed raw lines
+	# for compact combined (--cc) format, with chunk and patch simplification
+	# the patchset might be empty, but there might be unprocessed raw lines
 	for (++$patch_idx if $patch_number > 0;
 	     $patch_idx < @$difftree;
 	     ++$patch_idx) {
@@ -4125,73 +5416,112 @@
 
 # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
 
-# fills project list info (age, description, owner, forks) for each
-# project in the list, removing invalid projects from returned list
+sub git_project_search_form {
+	my ($searchtext, $search_use_regexp) = @_;
+
+	my $limit = '';
+	if ($project_filter) {
+		$limit = " in '$project_filter/'";
+	}
+
+	print "<div class=\"projsearch\">\n";
+	print $cgi->startform(-method => 'get', -action => $my_uri) .
+	      $cgi->hidden(-name => 'a', -value => 'project_list')  . "\n";
+	print $cgi->hidden(-name => 'pf', -value => $project_filter). "\n"
+		if (defined $project_filter);
+	print $cgi->textfield(-name => 's', -value => $searchtext,
+	                      -title => "Search project by name and description$limit",
+	                      -size => 60) . "\n" .
+	      "<span title=\"Extended regular expression\">" .
+	      $cgi->checkbox(-name => 'sr', -value => 1, -label => 're',
+	                     -checked => $search_use_regexp) .
+	      "</span>\n" .
+	      $cgi->submit(-name => 'btnS', -value => 'Search') .
+	      $cgi->end_form() . "\n" .
+	      $cgi->a({-href => href(project => undef, searchtext => undef,
+	                             project_filter => $project_filter)},
+	              esc_html("List all projects$limit")) . "<br />\n";
+	print "</div>\n";
+}
+
+# entry for given @keys needs filling if at least one of keys in list
+# is not present in %$project_info
+sub project_info_needs_filling {
+	my ($project_info, @keys) = @_;
+
+	# return List::MoreUtils::any { !exists $project_info->{$_} } @keys;
+	foreach my $key (@keys) {
+		if (!exists $project_info->{$key}) {
+			return 1;
+		}
+	}
+	return;
+}
+
+# fills project list info (age, description, owner, category, forks, etc.)
+# for each project in the list, removing invalid projects from
+# returned list, or fill only specified info.
+#
+# Invalid projects are removed from the returned list if and only if you
+# ask 'age' or 'age_string' to be filled, because they are the only fields
+# that run unconditionally git command that requires repository, and
+# therefore do always check if project repository is invalid.
+#
+# USAGE:
+# * fill_project_list_info(\@project_list, 'descr_long', 'ctags')
+#   ensures that 'descr_long' and 'ctags' fields are filled
+# * @project_list = fill_project_list_info(\@project_list)
+#   ensures that all fields are filled (and invalid projects removed)
+#
 # NOTE: modifies $projlist, but does not remove entries from it
 sub fill_project_list_info {
-	my ($projlist, $check_forks) = @_;
+	my ($projlist, @wanted_keys) = @_;
 	my @projects;
+	my $filter_set = sub { return @_; };
+	if (@wanted_keys) {
+		my %wanted_keys = map { $_ => 1 } @wanted_keys;
+		$filter_set = sub { return grep { $wanted_keys{$_} } @_; };
+	}
 
 	my $show_ctags = gitweb_check_feature('ctags');
  PROJECT:
 	foreach my $pr (@$projlist) {
-		my (@activity) = git_get_last_activity($pr->{'path'});
-		unless (@activity) {
-			next PROJECT;
+		if (project_info_needs_filling($pr, $filter_set->('age', 'age_string'))) {
+			my (@activity) = git_get_last_activity($pr->{'path'});
+			unless (@activity) {
+				next PROJECT;
+			}
+			($pr->{'age'}, $pr->{'age_string'}) = @activity;
 		}
-		($pr->{'age'}, $pr->{'age_string'}) = @activity;
-		if (!defined $pr->{'descr'}) {
+		if (project_info_needs_filling($pr, $filter_set->('descr', 'descr_long'))) {
 			my $descr = git_get_project_description($pr->{'path'}) || "";
 			$descr = to_utf8($descr);
 			$pr->{'descr_long'} = $descr;
 			$pr->{'descr'} = chop_str($descr, $projects_list_description_width, 5);
 		}
-		if (!defined $pr->{'owner'}) {
+		if (project_info_needs_filling($pr, $filter_set->('owner'))) {
 			$pr->{'owner'} = git_get_project_owner("$pr->{'path'}") || "";
 		}
-		if ($check_forks) {
-			my $pname = $pr->{'path'};
-			if (($pname =~ s/\.git$//) &&
-			    ($pname !~ /\/$/) &&
-			    (-d "$projectroot/$pname")) {
-				$pr->{'forks'} = "-d $projectroot/$pname";
-			} else {
-				$pr->{'forks'} = 0;
-			}
+		if ($show_ctags &&
+		    project_info_needs_filling($pr, $filter_set->('ctags'))) {
+			$pr->{'ctags'} = git_get_project_ctags($pr->{'path'});
 		}
-		$show_ctags and $pr->{'ctags'} = git_get_project_ctags($pr->{'path'});
+		if ($projects_list_group_categories &&
+		    project_info_needs_filling($pr, $filter_set->('category'))) {
+			my $cat = git_get_project_category($pr->{'path'}) ||
+			                                   $project_list_default_category;
+			$pr->{'category'} = to_utf8($cat);
+		}
+
 		push @projects, $pr;
 	}
 
 	return @projects;
 }
 
-# print 'sort by' <th> element, generating 'sort by $name' replay link
-# if that order is not selected
-sub print_sort_th {
-	my ($name, $order, $header) = @_;
-	$header ||= ucfirst($name);
-
-	if ($order eq $name) {
-		print "<th>$header</th>\n";
-	} else {
-		print "<th>" .
-		      $cgi->a({-href => href(-replay=>1, order=>$name),
-		               -class => "header"}, $header) .
-		      "</th>\n";
-	}
-}
-
-sub git_project_list_body {
-	# actually uses global variable $project
-	my ($projlist, $order, $from, $to, $extra, $no_header) = @_;
-
-	my $check_forks = gitweb_check_feature('forks');
-	my @projects = fill_project_list_info($projlist, $check_forks);
-
-	$order ||= $default_projects_order;
-	$from = 0 unless defined $from;
-	$to = $#projects if (!defined $to || $#projects < $to);
+sub sort_projects_list {
+	my ($projlist, $order) = @_;
+	my @projects;
 
 	my %order_info = (
 		project => { key => 'path', type => 'str' },
@@ -4200,21 +5530,159 @@
 		age => { key => 'age', type => 'num' }
 	);
 	my $oi = $order_info{$order};
+	return @$projlist unless defined $oi;
 	if ($oi->{'type'} eq 'str') {
-		@projects = sort {$a->{$oi->{'key'}} cmp $b->{$oi->{'key'}}} @projects;
+		@projects = sort {$a->{$oi->{'key'}} cmp $b->{$oi->{'key'}}} @$projlist;
 	} else {
-		@projects = sort {$a->{$oi->{'key'}} <=> $b->{$oi->{'key'}}} @projects;
+		@projects = sort {$a->{$oi->{'key'}} <=> $b->{$oi->{'key'}}} @$projlist;
 	}
 
-	my $show_ctags = gitweb_check_feature('ctags');
-	if ($show_ctags) {
-		my %ctags;
-		foreach my $p (@projects) {
-			foreach my $ct (keys %{$p->{'ctags'}}) {
-				$ctags{$ct} += $p->{'ctags'}->{$ct};
-			}
+	return @projects;
+}
+
+# returns a hash of categories, containing the list of project
+# belonging to each category
+sub build_projlist_by_category {
+	my ($projlist, $from, $to) = @_;
+	my %categories;
+
+	$from = 0 unless defined $from;
+	$to = $#$projlist if (!defined $to || $#$projlist < $to);
+
+	for (my $i = $from; $i <= $to; $i++) {
+		my $pr = $projlist->[$i];
+		push @{$categories{ $pr->{'category'} }}, $pr;
+	}
+
+	return wantarray ? %categories : \%categories;
+}
+
+# print 'sort by' <th> element, generating 'sort by $name' replay link
+# if that order is not selected
+sub print_sort_th {
+	print format_sort_th(@_);
+}
+
+sub format_sort_th {
+	my ($name, $order, $header) = @_;
+	my $sort_th = "";
+	$header ||= ucfirst($name);
+
+	if ($order eq $name) {
+		$sort_th .= "<th>$header</th>\n";
+	} else {
+		$sort_th .= "<th>" .
+		            $cgi->a({-href => href(-replay=>1, order=>$name),
+		                     -class => "header"}, $header) .
+		            "</th>\n";
+	}
+
+	return $sort_th;
+}
+
+sub git_project_list_rows {
+	my ($projlist, $from, $to, $check_forks) = @_;
+
+	$from = 0 unless defined $from;
+	$to = $#$projlist if (!defined $to || $#$projlist < $to);
+
+	my $alternate = 1;
+	for (my $i = $from; $i <= $to; $i++) {
+		my $pr = $projlist->[$i];
+
+		if ($alternate) {
+			print "<tr class=\"dark\">\n";
+		} else {
+			print "<tr class=\"light\">\n";
 		}
-		my $cloud = git_populate_project_tagcloud(\%ctags);
+		$alternate ^= 1;
+
+		if ($check_forks) {
+			print "<td>";
+			if ($pr->{'forks'}) {
+				my $nforks = scalar @{$pr->{'forks'}};
+				if ($nforks > 0) {
+					print $cgi->a({-href => href(project=>$pr->{'path'}, action=>"forks"),
+					               -title => "$nforks forks"}, "+");
+				} else {
+					print $cgi->span({-title => "$nforks forks"}, "+");
+				}
+			}
+			print "</td>\n";
+		}
+		print "<td>" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary"),
+		                        -class => "list"},
+		                       esc_html_match_hl($pr->{'path'}, $search_regexp)) .
+		      "</td>\n" .
+		      "<td>" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary"),
+		                        -class => "list",
+		                        -title => $pr->{'descr_long'}},
+		                        $search_regexp
+		                        ? esc_html_match_hl_chopped($pr->{'descr_long'},
+		                                                    $pr->{'descr'}, $search_regexp)
+		                        : esc_html($pr->{'descr'})) .
+		      "</td>\n";
+		unless ($omit_owner) {
+		        print "<td><i>" . chop_and_escape_str($pr->{'owner'}, 15) . "</i></td>\n";
+		}
+		unless ($omit_age_column) {
+		        print "<td class=\"". age_class($pr->{'age'}) . "\">" .
+		            (defined $pr->{'age_string'} ? $pr->{'age_string'} : "No commits") . "</td>\n";
+		}
+		print"<td class=\"link\">" .
+		      $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary")}, "summary")   . " | " .
+		      $cgi->a({-href => href(project=>$pr->{'path'}, action=>"shortlog")}, "shortlog") . " | " .
+		      $cgi->a({-href => href(project=>$pr->{'path'}, action=>"log")}, "log") . " | " .
+		      $cgi->a({-href => href(project=>$pr->{'path'}, action=>"tree")}, "tree") .
+		      ($pr->{'forks'} ? " | " . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"forks")}, "forks") : '') .
+		      "</td>\n" .
+		      "</tr>\n";
+	}
+}
+
+sub git_project_list_body {
+	# actually uses global variable $project
+	my ($projlist, $order, $from, $to, $extra, $no_header) = @_;
+	my @projects = @$projlist;
+
+	my $check_forks = gitweb_check_feature('forks');
+	my $show_ctags  = gitweb_check_feature('ctags');
+	my $tagfilter = $show_ctags ? $input_params{'ctag'} : undef;
+	$check_forks = undef
+		if ($tagfilter || $search_regexp);
+
+	# filtering out forks before filling info allows to do less work
+	@projects = filter_forks_from_projects_list(\@projects)
+		if ($check_forks);
+	# search_projects_list pre-fills required info
+	@projects = search_projects_list(\@projects,
+	                                 'search_regexp' => $search_regexp,
+	                                 'tagfilter'  => $tagfilter)
+		if ($tagfilter || $search_regexp);
+	# fill the rest
+	my @all_fields = ('descr', 'descr_long', 'ctags', 'category');
+	push @all_fields, ('age', 'age_string') unless($omit_age_column);
+	push @all_fields, 'owner' unless($omit_owner);
+	@projects = fill_project_list_info(\@projects, @all_fields);
+
+	$order ||= $default_projects_order;
+	$from = 0 unless defined $from;
+	$to = $#projects if (!defined $to || $#projects < $to);
+
+	# short circuit
+	if ($from > $to) {
+		print "<center>\n".
+		      "<b>No such projects found</b><br />\n".
+		      "Click ".$cgi->a({-href=>href(project=>undef)},"here")." to view all projects<br />\n".
+		      "</center>\n<br />\n";
+		return;
+	}
+
+	@projects = sort_projects_list(\@projects, $order);
+
+	if ($show_ctags) {
+		my $ctags = git_gather_all_ctags(\@projects);
+		my $cloud = git_populate_project_tagcloud($ctags);
 		print git_show_project_tagcloud($cloud, 64);
 	}
 
@@ -4226,58 +5694,32 @@
 		}
 		print_sort_th('project', $order, 'Project');
 		print_sort_th('descr', $order, 'Description');
-		print_sort_th('owner', $order, 'Owner');
-		print_sort_th('age', $order, 'Last Change');
+		print_sort_th('owner', $order, 'Owner') unless $omit_owner;
+		print_sort_th('age', $order, 'Last Change') unless $omit_age_column;
 		print "<th></th>\n" . # for links
 		      "</tr>\n";
 	}
-	my $alternate = 1;
-	my $tagfilter = $cgi->param('by_tag');
-	for (my $i = $from; $i <= $to; $i++) {
-		my $pr = $projects[$i];
 
-		next if $tagfilter and $show_ctags and not grep { lc $_ eq lc $tagfilter } keys %{$pr->{'ctags'}};
-		next if $searchtext and not $pr->{'path'} =~ /$searchtext/
-			and not $pr->{'descr_long'} =~ /$searchtext/;
-		# Weed out forks or non-matching entries of search
-		if ($check_forks) {
-			my $forkbase = $project; $forkbase ||= ''; $forkbase =~ s#\.git$#/#;
-			$forkbase="^$forkbase" if $forkbase;
-			next if not $searchtext and not $tagfilter and $show_ctags
-				and $pr->{'path'} =~ m#$forkbase.*/.*#; # regexp-safe
-		}
-
-		if ($alternate) {
-			print "<tr class=\"dark\">\n";
-		} else {
-			print "<tr class=\"light\">\n";
-		}
-		$alternate ^= 1;
-		if ($check_forks) {
-			print "<td>";
-			if ($pr->{'forks'}) {
-				print "<!-- $pr->{'forks'} -->\n";
-				print $cgi->a({-href => href(project=>$pr->{'path'}, action=>"forks")}, "+");
+	if ($projects_list_group_categories) {
+		# only display categories with projects in the $from-$to window
+		@projects = sort {$a->{'category'} cmp $b->{'category'}} @projects[$from..$to];
+		my %categories = build_projlist_by_category(\@projects, $from, $to);
+		foreach my $cat (sort keys %categories) {
+			unless ($cat eq "") {
+				print "<tr>\n";
+				if ($check_forks) {
+					print "<td></td>\n";
+				}
+				print "<td class=\"category\" colspan=\"5\">".esc_html($cat)."</td>\n";
+				print "</tr>\n";
 			}
-			print "</td>\n";
+
+			git_project_list_rows($categories{$cat}, undef, undef, $check_forks);
 		}
-		print "<td>" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary"),
-		                        -class => "list"}, esc_html($pr->{'path'})) . "</td>\n" .
-		      "<td>" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary"),
-		                        -class => "list", -title => $pr->{'descr_long'}},
-		                        esc_html($pr->{'descr'})) . "</td>\n" .
-		      "<td><i>" . chop_and_escape_str($pr->{'owner'}, 15) . "</i></td>\n";
-		print "<td class=\"". age_class($pr->{'age'}) . "\">" .
-		      (defined $pr->{'age_string'} ? $pr->{'age_string'} : "No commits") . "</td>\n" .
-		      "<td class=\"link\">" .
-		      $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary")}, "summary")   . " | " .
-		      $cgi->a({-href => href(project=>$pr->{'path'}, action=>"shortlog")}, "shortlog") . " | " .
-		      $cgi->a({-href => href(project=>$pr->{'path'}, action=>"log")}, "log") . " | " .
-		      $cgi->a({-href => href(project=>$pr->{'path'}, action=>"tree")}, "tree") .
-		      ($pr->{'forks'} ? " | " . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"forks")}, "forks") : '') .
-		      "</td>\n" .
-		      "</tr>\n";
+	} else {
+		git_project_list_rows(\@projects, $from, $to, $check_forks);
 	}
+
 	if (defined $extra) {
 		print "<tr>\n";
 		if ($check_forks) {
@@ -4289,6 +5731,45 @@
 	print "</table>\n";
 }
 
+sub git_log_body {
+	# uses global variable $project
+	my ($commitlist, $from, $to, $refs, $extra) = @_;
+
+	$from = 0 unless defined $from;
+	$to = $#{$commitlist} if (!defined $to || $#{$commitlist} < $to);
+
+	for (my $i = 0; $i <= $to; $i++) {
+		my %co = %{$commitlist->[$i]};
+		next if !%co;
+		my $commit = $co{'id'};
+		my $ref = format_ref_marker($refs, $commit);
+		git_print_header_div('commit',
+		               "<span class=\"age\">$co{'age_string'}</span>" .
+		               esc_html($co{'title'}) . $ref,
+		               $commit);
+		print "<div class=\"title_text\">\n" .
+		      "<div class=\"log_link\">\n" .
+		      $cgi->a({-href => href(action=>"commit", hash=>$commit)}, "commit") .
+		      " | " .
+		      $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") .
+		      " | " .
+		      $cgi->a({-href => href(action=>"tree", hash=>$commit, hash_base=>$commit)}, "tree") .
+		      "<br/>\n" .
+		      "</div>\n";
+		      git_print_authorship(\%co, -tag => 'span');
+		      print "<br/>\n</div>\n";
+
+		print "<div class=\"log_body\">\n";
+		git_print_log($co{'comment'}, -final_empty_line=> 1);
+		print "</div>\n";
+	}
+	if ($extra) {
+		print "<div class=\"page_nav\">\n";
+		print "$extra\n";
+		print "</div>\n";
+	}
+}
+
 sub git_shortlog_body {
 	# uses global variable $project
 	my ($commitlist, $from, $to, $refs, $extra) = @_;
@@ -4335,7 +5816,8 @@
 
 sub git_history_body {
 	# Warning: assumes constant type (blob or tree) during history
-	my ($commitlist, $from, $to, $refs, $hash_base, $ftype, $extra) = @_;
+	my ($commitlist, $from, $to, $refs, $extra,
+	    $file_name, $file_hash, $ftype) = @_;
 
 	$from = 0 unless defined $from;
 	$to = $#{$commitlist} unless (defined $to && $to <= $#{$commitlist});
@@ -4369,7 +5851,7 @@
 		      $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff");
 
 		if ($ftype eq 'blob') {
-			my $blob_current = git_get_hash_by_path($hash_base, $file_name);
+			my $blob_current = $file_hash;
 			my $blob_parent  = git_get_hash_by_path($commit, $file_name);
 			if (defined $blob_current && defined $blob_parent &&
 					$blob_current ne $blob_parent) {
@@ -4457,7 +5939,7 @@
 
 sub git_heads_body {
 	# uses global variable $project
-	my ($headlist, $head, $from, $to, $extra) = @_;
+	my ($headlist, $head_at, $from, $to, $extra) = @_;
 	$from = 0 unless defined $from;
 	$to = $#{$headlist} if (!defined $to || $#{$headlist} < $to);
 
@@ -4466,7 +5948,7 @@
 	for (my $i = $from; $i <= $to; $i++) {
 		my $entry = $headlist->[$i];
 		my %ref = %$entry;
-		my $curr = $ref{'id'} eq $head;
+		my $curr = defined $head_at && $ref{'id'} eq $head_at;
 		if ($alternate) {
 			print "<tr class=\"dark\">\n";
 		} else {
@@ -4481,7 +5963,7 @@
 		      "<td class=\"link\">" .
 		      $cgi->a({-href => href(action=>"shortlog", hash=>$ref{'fullname'})}, "shortlog") . " | " .
 		      $cgi->a({-href => href(action=>"log", hash=>$ref{'fullname'})}, "log") . " | " .
-		      $cgi->a({-href => href(action=>"tree", hash=>$ref{'fullname'}, hash_base=>$ref{'name'})}, "tree") .
+		      $cgi->a({-href => href(action=>"tree", hash=>$ref{'fullname'}, hash_base=>$ref{'fullname'})}, "tree") .
 		      "</td>\n" .
 		      "</tr>";
 	}
@@ -4493,6 +5975,312 @@
 	print "</table>\n";
 }
 
+# Display a single remote block
+sub git_remote_block {
+	my ($remote, $rdata, $limit, $head) = @_;
+
+	my $heads = $rdata->{'heads'};
+	my $fetch = $rdata->{'fetch'};
+	my $push = $rdata->{'push'};
+
+	my $urls_table = "<table class=\"projects_list\">\n" ;
+
+	if (defined $fetch) {
+		if ($fetch eq $push) {
+			$urls_table .= format_repo_url("URL", $fetch);
+		} else {
+			$urls_table .= format_repo_url("Fetch URL", $fetch);
+			$urls_table .= format_repo_url("Push URL", $push) if defined $push;
+		}
+	} elsif (defined $push) {
+		$urls_table .= format_repo_url("Push URL", $push);
+	} else {
+		$urls_table .= format_repo_url("", "No remote URL");
+	}
+
+	$urls_table .= "</table>\n";
+
+	my $dots;
+	if (defined $limit && $limit < @$heads) {
+		$dots = $cgi->a({-href => href(action=>"remotes", hash=>$remote)}, "...");
+	}
+
+	print $urls_table;
+	git_heads_body($heads, $head, 0, $limit, $dots);
+}
+
+# Display a list of remote names with the respective fetch and push URLs
+sub git_remotes_list {
+	my ($remotedata, $limit) = @_;
+	print "<table class=\"heads\">\n";
+	my $alternate = 1;
+	my @remotes = sort keys %$remotedata;
+
+	my $limited = $limit && $limit < @remotes;
+
+	$#remotes = $limit - 1 if $limited;
+
+	while (my $remote = shift @remotes) {
+		my $rdata = $remotedata->{$remote};
+		my $fetch = $rdata->{'fetch'};
+		my $push = $rdata->{'push'};
+		if ($alternate) {
+			print "<tr class=\"dark\">\n";
+		} else {
+			print "<tr class=\"light\">\n";
+		}
+		$alternate ^= 1;
+		print "<td>" .
+		      $cgi->a({-href=> href(action=>'remotes', hash=>$remote),
+			       -class=> "list name"},esc_html($remote)) .
+		      "</td>";
+		print "<td class=\"link\">" .
+		      (defined $fetch ? $cgi->a({-href=> $fetch}, "fetch") : "fetch") .
+		      " | " .
+		      (defined $push ? $cgi->a({-href=> $push}, "push") : "push") .
+		      "</td>";
+
+		print "</tr>\n";
+	}
+
+	if ($limited) {
+		print "<tr>\n" .
+		      "<td colspan=\"3\">" .
+		      $cgi->a({-href => href(action=>"remotes")}, "...") .
+		      "</td>\n" . "</tr>\n";
+	}
+
+	print "</table>";
+}
+
+# Display remote heads grouped by remote, unless there are too many
+# remotes, in which case we only display the remote names
+sub git_remotes_body {
+	my ($remotedata, $limit, $head) = @_;
+	if ($limit and $limit < keys %$remotedata) {
+		git_remotes_list($remotedata, $limit);
+	} else {
+		fill_remote_heads($remotedata);
+		while (my ($remote, $rdata) = each %$remotedata) {
+			git_print_section({-class=>"remote", -id=>$remote},
+				["remotes", $remote, $remote], sub {
+					git_remote_block($remote, $rdata, $limit, $head);
+				});
+		}
+	}
+}
+
+sub git_search_message {
+	my %co = @_;
+
+	my $greptype;
+	if ($searchtype eq 'commit') {
+		$greptype = "--grep=";
+	} elsif ($searchtype eq 'author') {
+		$greptype = "--author=";
+	} elsif ($searchtype eq 'committer') {
+		$greptype = "--committer=";
+	}
+	$greptype .= $searchtext;
+	my @commitlist = parse_commits($hash, 101, (100 * $page), undef,
+	                               $greptype, '--regexp-ignore-case',
+	                               $search_use_regexp ? '--extended-regexp' : '--fixed-strings');
+
+	my $paging_nav = '';
+	if ($page > 0) {
+		$paging_nav .=
+			$cgi->a({-href => href(-replay=>1, page=>undef)},
+			        "first") .
+			" &sdot; " .
+			$cgi->a({-href => href(-replay=>1, page=>$page-1),
+			         -accesskey => "p", -title => "Alt-p"}, "prev");
+	} else {
+		$paging_nav .= "first &sdot; prev";
+	}
+	my $next_link = '';
+	if ($#commitlist >= 100) {
+		$next_link =
+			$cgi->a({-href => href(-replay=>1, page=>$page+1),
+			         -accesskey => "n", -title => "Alt-n"}, "next");
+		$paging_nav .= " &sdot; $next_link";
+	} else {
+		$paging_nav .= " &sdot; next";
+	}
+
+	git_header_html();
+
+	git_print_page_nav('','', $hash,$co{'tree'},$hash, $paging_nav);
+	git_print_header_div('commit', esc_html($co{'title'}), $hash);
+	if ($page == 0 && !@commitlist) {
+		print "<p>No match.</p>\n";
+	} else {
+		git_search_grep_body(\@commitlist, 0, 99, $next_link);
+	}
+
+	git_footer_html();
+}
+
+sub git_search_changes {
+	my %co = @_;
+
+	local $/ = "\n";
+	open my $fd, '-|', git_cmd(), '--no-pager', 'log', @diff_opts,
+		'--pretty=format:%H', '--no-abbrev', '--raw', "-S$searchtext",
+		($search_use_regexp ? '--pickaxe-regex' : ())
+			or die_error(500, "Open git-log failed");
+
+	git_header_html();
+
+	git_print_page_nav('','', $hash,$co{'tree'},$hash);
+	git_print_header_div('commit', esc_html($co{'title'}), $hash);
+
+	print "<table class=\"pickaxe search\">\n";
+	my $alternate = 1;
+	undef %co;
+	my @files;
+	while (my $line = <$fd>) {
+		chomp $line;
+		next unless $line;
+
+		my %set = parse_difftree_raw_line($line);
+		if (defined $set{'commit'}) {
+			# finish previous commit
+			if (%co) {
+				print "</td>\n" .
+				      "<td class=\"link\">" .
+				      $cgi->a({-href => href(action=>"commit", hash=>$co{'id'})},
+				              "commit") .
+				      " | " .
+				      $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'},
+				                             hash_base=>$co{'id'})},
+				              "tree") .
+				      "</td>\n" .
+				      "</tr>\n";
+			}
+
+			if ($alternate) {
+				print "<tr class=\"dark\">\n";
+			} else {
+				print "<tr class=\"light\">\n";
+			}
+			$alternate ^= 1;
+			%co = parse_commit($set{'commit'});
+			my $author = chop_and_escape_str($co{'author_name'}, 15, 5);
+			print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
+			      "<td><i>$author</i></td>\n" .
+			      "<td>" .
+			      $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}),
+			              -class => "list subject"},
+			              chop_and_escape_str($co{'title'}, 50) . "<br/>");
+		} elsif (defined $set{'to_id'}) {
+			next if ($set{'to_id'} =~ m/^0{40}$/);
+
+			print $cgi->a({-href => href(action=>"blob", hash_base=>$co{'id'},
+			                             hash=>$set{'to_id'}, file_name=>$set{'to_file'}),
+			              -class => "list"},
+			              "<span class=\"match\">" . esc_path($set{'file'}) . "</span>") .
+			      "<br/>\n";
+		}
+	}
+	close $fd;
+
+	# finish last commit (warning: repetition!)
+	if (%co) {
+		print "</td>\n" .
+		      "<td class=\"link\">" .
+		      $cgi->a({-href => href(action=>"commit", hash=>$co{'id'})},
+		              "commit") .
+		      " | " .
+		      $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'},
+		                             hash_base=>$co{'id'})},
+		              "tree") .
+		      "</td>\n" .
+		      "</tr>\n";
+	}
+
+	print "</table>\n";
+
+	git_footer_html();
+}
+
+sub git_search_files {
+	my %co = @_;
+
+	local $/ = "\n";
+	open my $fd, "-|", git_cmd(), 'grep', '-n', '-z',
+		$search_use_regexp ? ('-E', '-i') : '-F',
+		$searchtext, $co{'tree'}
+			or die_error(500, "Open git-grep failed");
+
+	git_header_html();
+
+	git_print_page_nav('','', $hash,$co{'tree'},$hash);
+	git_print_header_div('commit', esc_html($co{'title'}), $hash);
+
+	print "<table class=\"grep_search\">\n";
+	my $alternate = 1;
+	my $matches = 0;
+	my $lastfile = '';
+	my $file_href;
+	while (my $line = <$fd>) {
+		chomp $line;
+		my ($file, $lno, $ltext, $binary);
+		last if ($matches++ > 1000);
+		if ($line =~ /^Binary file (.+) matches$/) {
+			$file = $1;
+			$binary = 1;
+		} else {
+			($file, $lno, $ltext) = split(/\0/, $line, 3);
+			$file =~ s/^$co{'tree'}://;
+		}
+		if ($file ne $lastfile) {
+			$lastfile and print "</td></tr>\n";
+			if ($alternate++) {
+				print "<tr class=\"dark\">\n";
+			} else {
+				print "<tr class=\"light\">\n";
+			}
+			$file_href = href(action=>"blob", hash_base=>$co{'id'},
+			                  file_name=>$file);
+			print "<td class=\"list\">".
+				$cgi->a({-href => $file_href, -class => "list"}, esc_path($file));
+			print "</td><td>\n";
+			$lastfile = $file;
+		}
+		if ($binary) {
+			print "<div class=\"binary\">Binary file</div>\n";
+		} else {
+			$ltext = untabify($ltext);
+			if ($ltext =~ m/^(.*)($search_regexp)(.*)$/i) {
+				$ltext = esc_html($1, -nbsp=>1);
+				$ltext .= '<span class="match">';
+				$ltext .= esc_html($2, -nbsp=>1);
+				$ltext .= '</span>';
+				$ltext .= esc_html($3, -nbsp=>1);
+			} else {
+				$ltext = esc_html($ltext, -nbsp=>1);
+			}
+			print "<div class=\"pre\">" .
+				$cgi->a({-href => $file_href.'#l'.$lno,
+				        -class => "linenr"}, sprintf('%4i', $lno)) .
+				' ' .  $ltext . "</div>\n";
+		}
+	}
+	if ($lastfile) {
+		print "</td></tr>\n";
+		if ($matches > 1000) {
+			print "<div class=\"diff nodifferences\">Too many matches, listing trimmed</div>\n";
+		}
+	} else {
+		print "<div class=\"diff nodifferences\">No matches found</div>\n";
+	}
+	close $fd;
+
+	print "</table>\n";
+
+	git_footer_html();
+}
+
 sub git_search_grep_body {
 	my ($commitlist, $from, $to, $extra) = @_;
 	$from = 0 unless defined $from;
@@ -4563,22 +6351,19 @@
 		die_error(400, "Unknown order parameter");
 	}
 
-	my @list = git_get_projects_list();
+	my @list = git_get_projects_list($project_filter, $strict_export);
 	if (!@list) {
 		die_error(404, "No projects found");
 	}
 
 	git_header_html();
-	if (-f $home_text) {
+	if (defined $home_text && -f $home_text) {
 		print "<div class=\"index_include\">\n";
 		insert_file($home_text);
 		print "</div>\n";
 	}
-	print $cgi->startform(-method => "get") .
-	      "<p class=\"projsearch\">Search:\n" .
-	      $cgi->textfield(-name => "s", -value => $searchtext) . "\n" .
-	      "</p>" .
-	      $cgi->end_form() . "\n";
+
+	git_project_search_form($searchtext, $search_use_regexp);
 	git_project_list_body(\@list, $order);
 	git_footer_html();
 }
@@ -4589,7 +6374,9 @@
 		die_error(400, "Unknown order parameter");
 	}
 
-	my @list = git_get_projects_list($project);
+	my $filter = $project;
+	$filter =~ s/\.git$//;
+	my @list = git_get_projects_list($filter);
 	if (!@list) {
 		die_error(404, "No forks found");
 	}
@@ -4602,7 +6389,10 @@
 }
 
 sub git_project_index {
-	my @projects = git_get_projects_list($project);
+	my @projects = git_get_projects_list($project_filter, $strict_export);
+	if (!@projects) {
+		die_error(404, "No projects found");
+	}
 
 	print $cgi->header(
 		-type => 'text/plain',
@@ -4630,6 +6420,7 @@
 	my %co = parse_commit("HEAD");
 	my %cd = %co ? parse_date($co{'committer_epoch'}, $co{'committer_tz'}) : ();
 	my $head = $co{'id'};
+	my $remote_heads = gitweb_check_feature('remote_heads');
 
 	my $owner = git_get_project_owner($project);
 
@@ -4638,11 +6429,18 @@
 	# there are more ...
 	my @taglist  = git_get_tags_list(16);
 	my @headlist = git_get_heads_list(16);
+	my %remotedata = $remote_heads ? git_get_remotes_list() : ();
 	my @forklist;
 	my $check_forks = gitweb_check_feature('forks');
 
 	if ($check_forks) {
-		@forklist = git_get_projects_list($project);
+		# find forks of a project
+		my $filter = $project;
+		$filter =~ s/\.git$//;
+		@forklist = git_get_projects_list($filter);
+		# filter out forks of forks
+		@forklist = filter_forks_from_projects_list(\@forklist)
+			if (@forklist);
 	}
 
 	git_header_html();
@@ -4650,10 +6448,13 @@
 
 	print "<div class=\"title\">&nbsp;</div>\n";
 	print "<table class=\"projects_list\">\n" .
-	      "<tr id=\"metadata_desc\"><td>description</td><td>" . esc_html($descr) . "</td></tr>\n" .
-	      "<tr id=\"metadata_owner\"><td>owner</td><td>" . esc_html($owner) . "</td></tr>\n";
+	      "<tr id=\"metadata_desc\"><td>description</td><td>" . esc_html($descr) . "</td></tr>\n";
+        unless ($omit_owner) {
+	        print  "<tr id=\"metadata_owner\"><td>owner</td><td>" . esc_html($owner) . "</td></tr>\n";
+        }
 	if (defined $cd{'rfc2822'}) {
-		print "<tr id=\"metadata_lchange\"><td>last change</td><td>$cd{'rfc2822'}</td></tr>\n";
+		print "<tr id=\"metadata_lchange\"><td>last change</td>" .
+		      "<td>".format_timestamp_html(\%cd)."</td></tr>\n";
 	}
 
 	# use per project git URL list in $projectroot/$project/cloneurl
@@ -4663,7 +6464,7 @@
 	@url_list = map { "$_/$project" } @git_base_url_list unless @url_list;
 	foreach my $git_url (@url_list) {
 		next unless $git_url;
-		print "<tr class=\"metadata_url\"><td>$url_tag</td><td>$git_url</td></tr>\n";
+		print format_repo_url($url_tag, $git_url);
 		$url_tag = "";
 	}
 
@@ -4671,13 +6472,14 @@
 	my $show_ctags = gitweb_check_feature('ctags');
 	if ($show_ctags) {
 		my $ctags = git_get_project_ctags($project);
-		my $cloud = git_populate_project_tagcloud($ctags);
-		print "<tr id=\"metadata_ctags\"><td>Content tags:<br />";
-		print "</td>\n<td>" unless %$ctags;
-		print "<form action=\"$show_ctags\" method=\"post\"><input type=\"hidden\" name=\"p\" value=\"$project\" />Add: <input type=\"text\" name=\"t\" size=\"8\" /></form>";
-		print "</td>\n<td>" if %$ctags;
-		print git_show_project_tagcloud($cloud, 48);
-		print "</td></tr>";
+		if (%$ctags) {
+			# without ability to add tags, don't show if there are none
+			my $cloud = git_populate_project_tagcloud($ctags);
+			print "<tr id=\"metadata_ctags\">" .
+			      "<td>content tags</td>" .
+			      "<td>".git_show_project_tagcloud($cloud, 48)."</td>" .
+			      "</tr>\n";
+		}
 	}
 
 	print "</table>\n";
@@ -4715,6 +6517,11 @@
 		               $cgi->a({-href => href(action=>"heads")}, "..."));
 	}
 
+	if (%remotedata) {
+		git_print_header_div('remotes');
+		git_remotes_body(\%remotedata, 15, $head);
+	}
+
 	if (@forklist) {
 		git_print_header_div('forks');
 		git_project_list_body(\@forklist, 'age', 0, 15,
@@ -4727,15 +6534,15 @@
 }
 
 sub git_tag {
-	my $head = git_get_head_hash($project);
-	git_header_html();
-	git_print_page_nav('','', $head,undef,$head);
 	my %tag = parse_tag($hash);
 
 	if (! %tag) {
 		die_error(404, "Unknown tag object");
 	}
 
+	my $head = git_get_head_hash($project);
+	git_header_html();
+	git_print_page_nav('','', $head,undef,$head);
 	git_print_header_div('commit', esc_html($tag{'name'}), $hash);
 	print "<div class=\"title_text\">\n" .
 	      "<table class=\"object_header\">\n" .
@@ -4761,7 +6568,13 @@
 	git_footer_html();
 }
 
-sub git_blame {
+sub git_blame_common {
+	my $format = shift || 'porcelain';
+	if ($format eq 'porcelain' && $input_params{'javascript'}) {
+		$format = 'incremental';
+		$action = 'blame_incremental'; # for page title etc
+	}
+
 	# permissions
 	gitweb_check_feature('blame')
 		or die_error(403, "Blame view not allowed");
@@ -4783,111 +6596,226 @@
 		}
 	}
 
-	# run git-blame --porcelain
-	open my $fd, "-|", git_cmd(), "blame", '-p',
-		$hash_base, '--', $file_name
-		or die_error(500, "Open git-blame failed");
+	my $fd;
+	if ($format eq 'incremental') {
+		# get file contents (as base)
+		open $fd, "-|", git_cmd(), 'cat-file', 'blob', $hash
+			or die_error(500, "Open git-cat-file failed");
+	} elsif ($format eq 'data') {
+		# run git-blame --incremental
+		open $fd, "-|", git_cmd(), "blame", "--incremental",
+			$hash_base, "--", $file_name
+			or die_error(500, "Open git-blame --incremental failed");
+	} else {
+		# run git-blame --porcelain
+		open $fd, "-|", git_cmd(), "blame", '-p',
+			$hash_base, '--', $file_name
+			or die_error(500, "Open git-blame --porcelain failed");
+	}
+
+	# incremental blame data returns early
+	if ($format eq 'data') {
+		print $cgi->header(
+			-type=>"text/plain", -charset => "utf-8",
+			-status=> "200 OK");
+		local $| = 1; # output autoflush
+		while (my $line = <$fd>) {
+			print to_utf8($line);
+		}
+		close $fd
+			or print "ERROR $!\n";
+
+		print 'END';
+		if (defined $t0 && gitweb_check_feature('timed')) {
+			print ' '.
+			      tv_interval($t0, [ gettimeofday() ]).
+			      ' '.$number_of_git_cmds;
+		}
+		print "\n";
+
+		return;
+	}
 
 	# page header
 	git_header_html();
 	my $formats_nav =
 		$cgi->a({-href => href(action=>"blob", -replay=>1)},
 		        "blob") .
+		" | ";
+	if ($format eq 'incremental') {
+		$formats_nav .=
+			$cgi->a({-href => href(action=>"blame", javascript=>0, -replay=>1)},
+			        "blame") . " (non-incremental)";
+	} else {
+		$formats_nav .=
+			$cgi->a({-href => href(action=>"blame_incremental", -replay=>1)},
+			        "blame") . " (incremental)";
+	}
+	$formats_nav .=
 		" | " .
 		$cgi->a({-href => href(action=>"history", -replay=>1)},
 		        "history") .
 		" | " .
-		$cgi->a({-href => href(action=>"blame", file_name=>$file_name)},
+		$cgi->a({-href => href(action=>$action, file_name=>$file_name)},
 		        "HEAD");
 	git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav);
 	git_print_header_div('commit', esc_html($co{'title'}), $hash_base);
 	git_print_page_path($file_name, $ftype, $hash_base);
 
 	# page body
-	my @rev_color = qw(light2 dark2);
+	if ($format eq 'incremental') {
+		print "<noscript>\n<div class=\"error\"><center><b>\n".
+		      "This page requires JavaScript to run.\n Use ".
+		      $cgi->a({-href => href(action=>'blame',javascript=>0,-replay=>1)},
+		              'this page').
+		      " instead.\n".
+		      "</b></center></div>\n</noscript>\n";
+
+		print qq!<div id="progress_bar" style="width: 100%; background-color: yellow"></div>\n!;
+	}
+
+	print qq!<div class="page_body">\n!;
+	print qq!<div id="progress_info">... / ...</div>\n!
+		if ($format eq 'incremental');
+	print qq!<table id="blame_table" class="blame" width="100%">\n!.
+	      #qq!<col width="5.5em" /><col width="2.5em" /><col width="*" />\n!.
+	      qq!<thead>\n!.
+	      qq!<tr><th>Commit</th><th>Line</th><th>Data</th></tr>\n!.
+	      qq!</thead>\n!.
+	      qq!<tbody>\n!;
+
+	my @rev_color = qw(light dark);
 	my $num_colors = scalar(@rev_color);
 	my $current_color = 0;
-	my %metainfo = ();
 
-	print <<HTML;
-<div class="page_body">
-<table class="blame">
-<tr><th>Commit</th><th>Line</th><th>Data</th></tr>
-HTML
- LINE:
-	while (my $line = <$fd>) {
-		chomp $line;
-		# the header: <SHA-1> <src lineno> <dst lineno> [<lines in group>]
-		# no <lines in group> for subsequent lines in group of lines
-		my ($full_rev, $orig_lineno, $lineno, $group_size) =
-		   ($line =~ /^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/);
-		if (!exists $metainfo{$full_rev}) {
-			$metainfo{$full_rev} = {};
+	if ($format eq 'incremental') {
+		my $color_class = $rev_color[$current_color];
+
+		#contents of a file
+		my $linenr = 0;
+	LINE:
+		while (my $line = <$fd>) {
+			chomp $line;
+			$linenr++;
+
+			print qq!<tr id="l$linenr" class="$color_class">!.
+			      qq!<td class="sha1"><a href=""> </a></td>!.
+			      qq!<td class="linenr">!.
+			      qq!<a class="linenr" href="">$linenr</a></td>!;
+			print qq!<td class="pre">! . esc_html($line) . "</td>\n";
+			print qq!</tr>\n!;
 		}
-		my $meta = $metainfo{$full_rev};
-		my $data;
-		while ($data = <$fd>) {
-			chomp $data;
-			last if ($data =~ s/^\t//); # contents of line
-			if ($data =~ /^(\S+) (.*)$/) {
-				$meta->{$1} = $2;
+
+	} else { # porcelain, i.e. ordinary blame
+		my %metainfo = (); # saves information about commits
+
+		# blame data
+	LINE:
+		while (my $line = <$fd>) {
+			chomp $line;
+			# the header: <SHA-1> <src lineno> <dst lineno> [<lines in group>]
+			# no <lines in group> for subsequent lines in group of lines
+			my ($full_rev, $orig_lineno, $lineno, $group_size) =
+			   ($line =~ /^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/);
+			if (!exists $metainfo{$full_rev}) {
+				$metainfo{$full_rev} = { 'nprevious' => 0 };
 			}
-		}
-		my $short_rev = substr($full_rev, 0, 8);
-		my $author = $meta->{'author'};
-		my %date =
-			parse_date($meta->{'author-time'}, $meta->{'author-tz'});
-		my $date = $date{'iso-tz'};
-		if ($group_size) {
-			$current_color = ($current_color + 1) % $num_colors;
-		}
-		print "<tr id=\"l$lineno\" class=\"$rev_color[$current_color]\">\n";
-		if ($group_size) {
-			print "<td class=\"sha1\"";
-			print " title=\"". esc_html($author) . ", $date\"";
-			print " rowspan=\"$group_size\"" if ($group_size > 1);
-			print ">";
-			print $cgi->a({-href => href(action=>"commit",
-			                             hash=>$full_rev,
-			                             file_name=>$file_name)},
-			              esc_html($short_rev));
-			print "</td>\n";
-		}
-		my $parent_commit;
-		if (!exists $meta->{'parent'}) {
-			open (my $dd, "-|", git_cmd(), "rev-parse", "$full_rev^")
-				or die_error(500, "Open git-rev-parse failed");
-			$parent_commit = <$dd>;
-			close $dd;
-			chomp($parent_commit);
-			$meta->{'parent'} = $parent_commit;
-		} else {
-			$parent_commit = $meta->{'parent'};
-		}
-		my $blamed = href(action => 'blame',
-		                  file_name => $meta->{'filename'},
-		                  hash_base => $parent_commit);
-		print "<td class=\"linenr\">";
-		print $cgi->a({ -href => "$blamed#l$orig_lineno",
-		                -class => "linenr" },
-		              esc_html($lineno));
-		print "</td>";
-		print "<td class=\"pre\">" . esc_html($data) . "</td>\n";
-		print "</tr>\n";
+			my $meta = $metainfo{$full_rev};
+			my $data;
+			while ($data = <$fd>) {
+				chomp $data;
+				last if ($data =~ s/^\t//); # contents of line
+				if ($data =~ /^(\S+)(?: (.*))?$/) {
+					$meta->{$1} = $2 unless exists $meta->{$1};
+				}
+				if ($data =~ /^previous /) {
+					$meta->{'nprevious'}++;
+				}
+			}
+			my $short_rev = substr($full_rev, 0, 8);
+			my $author = $meta->{'author'};
+			my %date =
+				parse_date($meta->{'author-time'}, $meta->{'author-tz'});
+			my $date = $date{'iso-tz'};
+			if ($group_size) {
+				$current_color = ($current_color + 1) % $num_colors;
+			}
+			my $tr_class = $rev_color[$current_color];
+			$tr_class .= ' boundary' if (exists $meta->{'boundary'});
+			$tr_class .= ' no-previous' if ($meta->{'nprevious'} == 0);
+			$tr_class .= ' multiple-previous' if ($meta->{'nprevious'} > 1);
+			print "<tr id=\"l$lineno\" class=\"$tr_class\">\n";
+			if ($group_size) {
+				print "<td class=\"sha1\"";
+				print " title=\"". esc_html($author) . ", $date\"";
+				print " rowspan=\"$group_size\"" if ($group_size > 1);
+				print ">";
+				print $cgi->a({-href => href(action=>"commit",
+				                             hash=>$full_rev,
+				                             file_name=>$file_name)},
+				              esc_html($short_rev));
+				if ($group_size >= 2) {
+					my @author_initials = ($author =~ /\b([[:upper:]])\B/g);
+					if (@author_initials) {
+						print "<br />" .
+						      esc_html(join('', @author_initials));
+						#           or join('.', ...)
+					}
+				}
+				print "</td>\n";
+			}
+			# 'previous' <sha1 of parent commit> <filename at commit>
+			if (exists $meta->{'previous'} &&
+			    $meta->{'previous'} =~ /^([a-fA-F0-9]{40}) (.*)$/) {
+				$meta->{'parent'} = $1;
+				$meta->{'file_parent'} = unquote($2);
+			}
+			my $linenr_commit =
+				exists($meta->{'parent'}) ?
+				$meta->{'parent'} : $full_rev;
+			my $linenr_filename =
+				exists($meta->{'file_parent'}) ?
+				$meta->{'file_parent'} : unquote($meta->{'filename'});
+			my $blamed = href(action => 'blame',
+			                  file_name => $linenr_filename,
+			                  hash_base => $linenr_commit);
+			print "<td class=\"linenr\">";
+			print $cgi->a({ -href => "$blamed#l$orig_lineno",
+			                -class => "linenr" },
+			              esc_html($lineno));
+			print "</td>";
+			print "<td class=\"pre\">" . esc_html($data) . "</td>\n";
+			print "</tr>\n";
+		} # end while
+
 	}
-	print "</table>\n";
-	print "</div>";
+
+	# footer
+	print "</tbody>\n".
+	      "</table>\n"; # class="blame"
+	print "</div>\n";   # class="blame_body"
 	close $fd
 		or print "Reading blob failed\n";
 
-	# page footer
 	git_footer_html();
 }
 
+sub git_blame {
+	git_blame_common();
+}
+
+sub git_blame_incremental {
+	git_blame_common('incremental');
+}
+
+sub git_blame_data {
+	git_blame_common('data');
+}
+
 sub git_tags {
 	my $head = git_get_head_hash($project);
 	git_header_html();
-	git_print_page_nav('','', $head,undef,$head);
+	git_print_page_nav('','', $head,undef,$head,format_ref_views('tags'));
 	git_print_header_div('summary', $project);
 
 	my @tagslist = git_get_tags_list();
@@ -4900,7 +6828,7 @@
 sub git_heads {
 	my $head = git_get_head_hash($project);
 	git_header_html();
-	git_print_page_nav('','', $head,undef,$head);
+	git_print_page_nav('','', $head,undef,$head,format_ref_views('heads'));
 	git_print_header_div('summary', $project);
 
 	my @headslist = git_get_heads_list();
@@ -4910,6 +6838,39 @@
 	git_footer_html();
 }
 
+# used both for single remote view and for list of all the remotes
+sub git_remotes {
+	gitweb_check_feature('remote_heads')
+		or die_error(403, "Remote heads view is disabled");
+
+	my $head = git_get_head_hash($project);
+	my $remote = $input_params{'hash'};
+
+	my $remotedata = git_get_remotes_list($remote);
+	die_error(500, "Unable to get remote information") unless defined $remotedata;
+
+	unless (%$remotedata) {
+		die_error(404, defined $remote ?
+			"Remote $remote not found" :
+			"No remotes found");
+	}
+
+	git_header_html(undef, undef, -action_extra => $remote);
+	git_print_page_nav('', '',  $head, undef, $head,
+		format_ref_views($remote ? '' : 'remotes'));
+
+	fill_remote_heads($remotedata);
+	if (defined $remote) {
+		git_print_header_div('remotes', "$remote remote for $project");
+		git_remote_block($remote, $remotedata->{$remote}, undef, $head);
+	} else {
+		git_print_header_div('summary', "$project remotes");
+		git_remotes_body($remotedata, undef, $head);
+	}
+
+	git_footer_html();
+}
+
 sub git_blob_plain {
 	my $type = shift;
 	my $expires;
@@ -4948,7 +6909,16 @@
 	# want to be sure not to break that by serving the image as an
 	# attachment (though Firefox 3 doesn't seem to care).
 	my $sandbox = $prevent_xss &&
-		$type !~ m!^(?:text/plain|image/(?:gif|png|jpeg))$!;
+		$type !~ m!^(?:text/[a-z]+|image/(?:gif|png|jpeg))(?:[ ;]|$)!;
+
+	# serve text/* as text/plain
+	if ($prevent_xss &&
+	    ($type =~ m!^text/[a-z]+\b(.*)$! ||
+	     ($type =~ m!^[a-z]+/[a-z]\+xml\b(.*)$! && -T $fd))) {
+		my $rest = $1;
+		$rest = defined $rest ? $rest : '';
+		$type = "text/plain$rest";
+	}
 
 	print $cgi->header(
 		-type => $type,
@@ -4983,6 +6953,7 @@
 	open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash
 		or die_error(500, "Couldn't cat $file_name, $hash");
 	my $mimetype = blob_mimetype($fd, $file_name);
+	# use 'blob_plain' (aka 'raw') view for files that cannot be displayed
 	if ($mimetype !~ m!^(?:text/|image/(?:gif|png|jpeg)$)! && -B $fd) {
 		close $fd;
 		return git_blob_plain($mimetype);
@@ -4990,6 +6961,11 @@
 	# we can have blame only for text/* mimetype
 	$have_blame &&= ($mimetype =~ m!^text/!);
 
+	my $highlight = gitweb_check_feature('highlight');
+	my $syntax = guess_file_syntax($highlight, $mimetype, $file_name);
+	$fd = run_highlighter($fd, $highlight, $syntax)
+		if $syntax;
+
 	git_header_html(undef, $expires);
 	my $formats_nav = '';
 	if (defined $hash_base && (my %co = parse_commit($hash_base))) {
@@ -5020,14 +6996,14 @@
 	} else {
 		print "<div class=\"page_nav\">\n" .
 		      "<br/><br/></div>\n" .
-		      "<div class=\"title\">$hash</div>\n";
+		      "<div class=\"title\">".esc_html($hash)."</div>\n";
 	}
 	git_print_page_path($file_name, "blob", $hash_base);
 	print "<div class=\"page_body\">\n";
 	if ($mimetype =~ m!^image/!) {
-		print qq!<img type="$mimetype"!;
+		print qq!<img type="!.esc_attr($mimetype).qq!"!;
 		if ($file_name) {
-			print qq! alt="$file_name" title="$file_name"!;
+			print qq! alt="!.esc_attr($file_name).qq!" title="!.esc_attr($file_name).qq!"!;
 		}
 		print qq! src="! .
 		      href(action=>"blob_plain", hash=>$hash,
@@ -5039,8 +7015,9 @@
 			chomp $line;
 			$nr++;
 			$line = untabify($line);
-			printf "<div class=\"pre\"><a id=\"l%i\" href=\"#l%i\" class=\"linenr\">%4i</a> %s</div>\n",
-			       $nr, $nr, $nr, esc_html($line, -nbsp=>1);
+			printf qq!<div class="pre"><a id="l%i" href="%s#l%i" class="linenr">%4i</a> %s</div>\n!,
+			       $nr, esc_attr(href(-replay => 1)), $nr, $nr,
+			       $syntax ? sanitize($line) : esc_html($line, -nbsp=>1);
 		}
 	}
 	close $fd
@@ -5062,10 +7039,14 @@
 	}
 	die_error(404, "No such tree") unless defined($hash);
 
+	my $show_sizes = gitweb_check_feature('show-sizes');
+	my $have_blame = gitweb_check_feature('blame');
+
 	my @entries = ();
 	{
 		local $/ = "\0";
-		open my $fd, "-|", git_cmd(), "ls-tree", '-z', $hash
+		open my $fd, "-|", git_cmd(), "ls-tree", '-z',
+			($show_sizes ? '-l' : ()), @extra_options, $hash
 			or die_error(500, "Open git-ls-tree failed");
 		@entries = map { chomp; $_ } <$fd>;
 		close $fd
@@ -5076,7 +7057,6 @@
 	my $ref = format_ref_marker($refs, $hash_base);
 	git_header_html();
 	my $basedir = '';
-	my $have_blame = gitweb_check_feature('blame');
 	if (defined $hash_base && (my %co = parse_commit($hash_base))) {
 		my @views_nav = ();
 		if (defined $file_name) {
@@ -5092,13 +7072,14 @@
 			# FIXME: Should be available when we have no hash base as well.
 			push @views_nav, $snapshot_links;
 		}
-		git_print_page_nav('tree','', $hash_base, undef, undef, join(' | ', @views_nav));
+		git_print_page_nav('tree','', $hash_base, undef, undef,
+		                   join(' | ', @views_nav));
 		git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash_base);
 	} else {
 		undef $hash_base;
 		print "<div class=\"page_nav\">\n";
 		print "<br/><br/></div>\n";
-		print "<div class=\"title\">$hash</div>\n";
+		print "<div class=\"title\">".esc_html($hash)."</div>\n";
 	}
 	if (defined $file_name) {
 		$basedir = $file_name;
@@ -5125,8 +7106,10 @@
 		undef $up unless $up;
 		# based on git_print_tree_entry
 		print '<td class="mode">' . mode_str('040000') . "</td>\n";
+		print '<td class="size">&nbsp;</td>'."\n" if $show_sizes;
 		print '<td class="list">';
-		print $cgi->a({-href => href(action=>"tree", hash_base=>$hash_base,
+		print $cgi->a({-href => href(action=>"tree",
+		                             hash_base=>$hash_base,
 		                             file_name=>$up)},
 		              "..");
 		print "</td>\n";
@@ -5135,7 +7118,7 @@
 		print "</tr>\n";
 	}
 	foreach my $line (@entries) {
-		my %t = parse_ls_tree_line($line, -z => 1);
+		my %t = parse_ls_tree_line($line, -z => 1, -l => $show_sizes);
 
 		if ($alternate) {
 			print "<tr class=\"dark\">\n";
@@ -5153,6 +7136,65 @@
 	git_footer_html();
 }
 
+sub snapshot_name {
+	my ($project, $hash) = @_;
+
+	# path/to/project.git  -> project
+	# path/to/project/.git -> project
+	my $name = to_utf8($project);
+	$name =~ s,([^/])/*\.git$,$1,;
+	$name = basename($name);
+	# sanitize name
+	$name =~ s/[[:cntrl:]]/?/g;
+
+	my $ver = $hash;
+	if ($hash =~ /^[0-9a-fA-F]+$/) {
+		# shorten SHA-1 hash
+		my $full_hash = git_get_full_hash($project, $hash);
+		if ($full_hash =~ /^$hash/ && length($hash) > 7) {
+			$ver = git_get_short_hash($project, $hash);
+		}
+	} elsif ($hash =~ m!^refs/tags/(.*)$!) {
+		# tags don't need shortened SHA-1 hash
+		$ver = $1;
+	} else {
+		# branches and other need shortened SHA-1 hash
+		if ($hash =~ m!^refs/(?:heads|remotes)/(.*)$!) {
+			$ver = $1;
+		}
+		$ver .= '-' . git_get_short_hash($project, $hash);
+	}
+	# in case of hierarchical branch names
+	$ver =~ s!/!.!g;
+
+	# name = project-version_string
+	$name = "$name-$ver";
+
+	return wantarray ? ($name, $name) : $name;
+}
+
+sub exit_if_unmodified_since {
+	my ($latest_epoch) = @_;
+	our $cgi;
+
+	my $if_modified = $cgi->http('IF_MODIFIED_SINCE');
+	if (defined $if_modified) {
+		my $since;
+		if (eval { require HTTP::Date; 1; }) {
+			$since = HTTP::Date::str2time($if_modified);
+		} elsif (eval { require Time::ParseDate; 1; }) {
+			$since = Time::ParseDate::parsedate($if_modified, GMT => 1);
+		}
+		if (defined $since && $latest_epoch <= $since) {
+			my %latest_date = parse_date($latest_epoch);
+			print $cgi->header(
+				-last_modified => $latest_date{'rfc2822'},
+				-status => '304 Not Modified');
+			goto DONE_GITWEB;
+		}
+	}
+}
+
 sub git_snapshot {
 	my $format = $input_params{'snapshot_format'};
 	if (!@snapshot_fmts) {
@@ -5164,32 +7206,43 @@
 		die_error(400, "Invalid snapshot format parameter");
 	} elsif (!exists($known_snapshot_formats{$format})) {
 		die_error(400, "Unknown snapshot format");
+	} elsif ($known_snapshot_formats{$format}{'disabled'}) {
+		die_error(403, "Snapshot format not allowed");
 	} elsif (!grep($_ eq $format, @snapshot_fmts)) {
 		die_error(403, "Unsupported snapshot format");
 	}
 
-	if (!defined $hash) {
-		$hash = git_get_head_hash($project);
+	my $type = git_get_type("$hash^{}");
+	if (!$type) {
+		die_error(404, 'Object does not exist');
+	}  elsif ($type eq 'blob') {
+		die_error(400, 'Object is not a tree-ish');
 	}
 
-	my $name = $project;
-	$name =~ s,([^/])/*\.git$,$1,;
-	$name = basename($name);
-	my $filename = to_utf8($name);
-	$name =~ s/\047/\047\\\047\047/g;
-	my $cmd;
-	$filename .= "-$hash$known_snapshot_formats{$format}{'suffix'}";
-	$cmd = quote_command(
+	my ($name, $prefix) = snapshot_name($project, $hash);
+	my $filename = "$name$known_snapshot_formats{$format}{'suffix'}";
+
+	my %co = parse_commit($hash);
+	exit_if_unmodified_since($co{'committer_epoch'}) if %co;
+
+	my $cmd = quote_command(
 		git_cmd(), 'archive',
 		"--format=$known_snapshot_formats{$format}{'format'}",
-		"--prefix=$name/", $hash);
+		"--prefix=$prefix/", $hash);
 	if (exists $known_snapshot_formats{$format}{'compressor'}) {
 		$cmd .= ' | ' . quote_command(@{$known_snapshot_formats{$format}{'compressor'}});
 	}
 
+	$filename =~ s/(["\\])/\\$1/g;
+	my %latest_date;
+	if (%co) {
+		%latest_date = parse_date($co{'committer_epoch'}, $co{'committer_tz'});
+	}
+
 	print $cgi->header(
 		-type => $known_snapshot_formats{$format}{'type'},
-		-content_disposition => 'inline; filename="' . "$filename" . '"',
+		-content_disposition => 'inline; filename="' . $filename . '"',
+		%co ? (-last_modified => $latest_date{'rfc2822'}) : (),
 		-status => '200 OK');
 
 	open my $fd, "-|", $cmd
@@ -5200,22 +7253,57 @@
 	close $fd;
 }
 
-sub git_log {
+sub git_log_generic {
+	my ($fmt_name, $body_subr, $base, $parent, $file_name, $file_hash) = @_;
+
 	my $head = git_get_head_hash($project);
-	if (!defined $hash) {
-		$hash = $head;
+	if (!defined $base) {
+		$base = $head;
 	}
 	if (!defined $page) {
 		$page = 0;
 	}
 	my $refs = git_get_references();
 
-	my @commitlist = parse_commits($hash, 101, (100 * $page));
+	my $commit_hash = $base;
+	if (defined $parent) {
+		$commit_hash = "$parent..$base";
+	}
+	my @commitlist =
+		parse_commits($commit_hash, 101, (100 * $page),
+		              defined $file_name ? ($file_name, "--full-history") : ());
 
-	my $paging_nav = format_paging_nav('log', $hash, $head, $page, $#commitlist >= 100);
+	my $ftype;
+	if (!defined $file_hash && defined $file_name) {
+		# some commits could have deleted file in question,
+		# and not have it in tree, but one of them has to have it
+		for (my $i = 0; $i < @commitlist; $i++) {
+			$file_hash = git_get_hash_by_path($commitlist[$i]{'id'}, $file_name);
+			last if defined $file_hash;
+		}
+	}
+	if (defined $file_hash) {
+		$ftype = git_get_type($file_hash);
+	}
+	if (defined $file_name && !defined $ftype) {
+		die_error(500, "Unknown type of object");
+	}
+	my %co;
+	if (defined $file_name) {
+		%co = parse_commit($base)
+			or die_error(404, "Unknown commit object");
+	}
 
-	my ($patch_max) = gitweb_get_feature('patches');
-	if ($patch_max) {
+
+	my $paging_nav = format_paging_nav($fmt_name, $page, $#commitlist >= 100);
+	my $next_link = '';
+	if ($#commitlist >= 100) {
+		$next_link =
+			$cgi->a({-href => href(-replay=>1, page=>$page+1),
+			         -accesskey => "n", -title => "Alt-n"}, "next");
+	}
+	my $patch_max = gitweb_get_feature('patches');
+	if ($patch_max && !defined $file_name) {
 		if ($patch_max < 0 || @commitlist <= $patch_max) {
 			$paging_nav .= " &sdot; " .
 				$cgi->a({-href => href(action=>"patches", -replay=>1)},
@@ -5224,50 +7312,26 @@
 	}
 
 	git_header_html();
-	git_print_page_nav('log','', $hash,undef,undef, $paging_nav);
-
-	if (!@commitlist) {
-		my %co = parse_commit($hash);
-
-		git_print_header_div('summary', $project);
-		print "<div class=\"page_body\"> Last change $co{'age_string'}.<br/><br/></div>\n";
+	git_print_page_nav($fmt_name,'', $hash,$hash,$hash, $paging_nav);
+	if (defined $file_name) {
+		git_print_header_div('commit', esc_html($co{'title'}), $base);
+	} else {
+		git_print_header_div('summary', $project)
 	}
-	my $to = ($#commitlist >= 99) ? (99) : ($#commitlist);
-	for (my $i = 0; $i <= $to; $i++) {
-		my %co = %{$commitlist[$i]};
-		next if !%co;
-		my $commit = $co{'id'};
-		my $ref = format_ref_marker($refs, $commit);
-		my %ad = parse_date($co{'author_epoch'});
-		git_print_header_div('commit',
-		               "<span class=\"age\">$co{'age_string'}</span>" .
-		               esc_html($co{'title'}) . $ref,
-		               $commit);
-		print "<div class=\"title_text\">\n" .
-		      "<div class=\"log_link\">\n" .
-		      $cgi->a({-href => href(action=>"commit", hash=>$commit)}, "commit") .
-		      " | " .
-		      $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") .
-		      " | " .
-		      $cgi->a({-href => href(action=>"tree", hash=>$commit, hash_base=>$commit)}, "tree") .
-		      "<br/>\n" .
-		      "</div>\n";
-		      git_print_authorship(\%co, -tag => 'span');
-		      print "<br/>\n</div>\n";
+	git_print_page_path($file_name, $ftype, $hash_base)
+		if (defined $file_name);
 
-		print "<div class=\"log_body\">\n";
-		git_print_log($co{'comment'}, -final_empty_line=> 1);
-		print "</div>\n";
-	}
-	if ($#commitlist >= 100) {
-		print "<div class=\"page_nav\">\n";
-		print $cgi->a({-href => href(-replay=>1, page=>$page+1),
-			       -accesskey => "n", -title => "Alt-n"}, "next");
-		print "</div>\n";
-	}
+	$body_subr->(\@commitlist, 0, 99, $refs, $next_link,
+	             $file_name, $file_hash, $ftype);
+
 	git_footer_html();
 }
 
+sub git_log {
+	git_log_generic('log', \&git_log_body,
+	                $hash, $hash_parent);
+}
+
 sub git_commit {
 	$hash ||= $hash_base || "HEAD";
 	my %co = parse_commit($hash)
@@ -5300,7 +7364,7 @@
 			} @$parents ) .
 			')';
 	}
-	if (gitweb_check_feature('patches')) {
+	if (gitweb_check_feature('patches') && @$parents <= 1) {
 		$formats_nav .= " | " .
 			$cgi->a({-href => href(action=>"patch", -replay=>1)},
 				"patch");
@@ -5431,6 +7495,7 @@
 
 sub git_blobdiff {
 	my $format = shift || 'html';
+	my $diff_style = $input_params{'diff_style'} || 'inline';
 
 	my $fd;
 	my @difftree;
@@ -5509,13 +7574,14 @@
 		my $formats_nav =
 			$cgi->a({-href => href(action=>"blobdiff_plain", -replay=>1)},
 			        "raw");
+		$formats_nav .= diff_style_nav($diff_style);
 		git_header_html(undef, $expires);
 		if (defined $hash_base && (my %co = parse_commit($hash_base))) {
 			git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav);
 			git_print_header_div('commit', esc_html($co{'title'}), $hash_base);
 		} else {
 			print "<div class=\"page_nav\"><br/>$formats_nav<br/></div>\n";
-			print "<div class=\"title\">$hash vs $hash_parent</div>\n";
+			print "<div class=\"title\">".esc_html("$hash vs $hash_parent")."</div>\n";
 		}
 		if (defined $file_name) {
 			git_print_page_path($file_name, "blob", $hash_base);
@@ -5540,7 +7606,8 @@
 	if ($format eq 'html') {
 		print "<div class=\"page_body\">\n";
 
-		git_patchset_body($fd, [ \%diffinfo ], $hash_base, $hash_parent_base);
+		git_patchset_body($fd, $diff_style,
+		                  [ \%diffinfo ], $hash_base, $hash_parent_base);
 		close $fd;
 
 		print "</div>\n"; # class="page_body"
@@ -5565,9 +7632,31 @@
 	git_blobdiff('plain');
 }
 
+# assumes that it is added as later part of already existing navigation,
+# so it returns "| foo | bar" rather than just "foo | bar"
+sub diff_style_nav {
+	my ($diff_style, $is_combined) = @_;
+	$diff_style ||= 'inline';
+
+	return "" if ($is_combined);
+
+	my @styles = (inline => 'inline', 'sidebyside' => 'side by side');
+	my %styles = @styles;
+	@styles =
+		@styles[ map { $_ * 2 } 0..$#styles/2 ];
+
+	return join '',
+		map { " | ".$_ }
+		map {
+			$_ eq $diff_style ? $styles{$_} :
+			$cgi->a({-href => href(-replay=>1, diff_style => $_)}, $styles{$_})
+		} @styles;
+}
+
 sub git_commitdiff {
 	my %params = @_;
 	my $format = $params{-format} || 'html';
+	my $diff_style = $input_params{'diff_style'} || 'inline';
 
 	my ($patch_max) = gitweb_get_feature('patches');
 	if ($format eq 'patch') {
@@ -5588,11 +7677,12 @@
 		$formats_nav =
 			$cgi->a({-href => href(action=>"commitdiff_plain", -replay=>1)},
 			        "raw");
-		if ($patch_max) {
+		if ($patch_max && @{$co{'parents'}} <= 1) {
 			$formats_nav .= " | " .
 				$cgi->a({-href => href(action=>"patch", -replay=>1)},
 					"patch");
 		}
+		$formats_nav .= diff_style_nav($diff_style, @{$co{'parents'}} > 1);
 
 		if (defined $hash_parent &&
 		    $hash_parent ne '-c' && $hash_parent ne '--cc') {
@@ -5610,8 +7700,8 @@
 				}
 			}
 			$formats_nav .= ': ' .
-				$cgi->a({-href => href(action=>"commitdiff",
-				                       hash=>$hash_parent)},
+				$cgi->a({-href => href(-replay=>1,
+				                       hash=>$hash_parent, hash_base=>undef)},
 				        esc_html($hash_parent_short)) .
 				')';
 		} elsif (!$co{'parent'}) {
@@ -5621,28 +7711,28 @@
 			# single parent commit
 			$formats_nav .=
 				' (parent: ' .
-				$cgi->a({-href => href(action=>"commitdiff",
-				                       hash=>$co{'parent'})},
+				$cgi->a({-href => href(-replay=>1,
+				                       hash=>$co{'parent'}, hash_base=>undef)},
 				        esc_html(substr($co{'parent'}, 0, 7))) .
 				')';
 		} else {
 			# merge commit
 			if ($hash_parent eq '--cc') {
 				$formats_nav .= ' | ' .
-					$cgi->a({-href => href(action=>"commitdiff",
+					$cgi->a({-href => href(-replay=>1,
 					                       hash=>$hash, hash_parent=>'-c')},
 					        'combined');
 			} else { # $hash_parent eq '-c'
 				$formats_nav .= ' | ' .
-					$cgi->a({-href => href(action=>"commitdiff",
+					$cgi->a({-href => href(-replay=>1,
 					                       hash=>$hash, hash_parent=>'--cc')},
 					        'compact');
 			}
 			$formats_nav .=
 				' (merge: ' .
 				join(' ', map {
-					$cgi->a({-href => href(action=>"commitdiff",
-					                       hash=>$_)},
+					$cgi->a({-href => href(-replay=>1,
+					                       hash=>$_, hash_base=>undef)},
 					        esc_html(substr($_, 0, 7)));
 				} @{$co{'parents'}} ) .
 				')';
@@ -5698,8 +7788,8 @@
 			}
 			push @commit_spec, '--root', $hash;
 		}
-		open $fd, "-|", git_cmd(), "format-patch", '--encoding=utf8',
-			'--stdout', @commit_spec
+		open $fd, "-|", git_cmd(), "format-patch", @diff_opts,
+			'--encoding=utf8', '--stdout', @commit_spec
 			or die_error(500, "Open git-format-patch failed");
 	} else {
 		die_error(400, "Unknown commitdiff format");
@@ -5771,7 +7861,8 @@
 		                  $use_parents ? @{$co{'parents'}} : $hash_parent);
 		print "<br/>\n";
 
-		git_patchset_body($fd, \@difftree, $hash,
+		git_patchset_body($fd, $diff_style,
+		                  \@difftree, $hash,
 		                  $use_parents ? @{$co{'parents'}} : $hash_parent);
 		close $fd;
 		print "</div>\n"; # class="page_body"
@@ -5796,7 +7887,7 @@
 
 # format-patch-style patches
 sub git_patch {
-	git_commitdiff(-format => 'patch', -single=> 1);
+	git_commitdiff(-format => 'patch', -single => 1);
 }
 
 sub git_patches {
@@ -5804,74 +7895,29 @@
 }
 
 sub git_history {
-	if (!defined $hash_base) {
-		$hash_base = git_get_head_hash($project);
-	}
-	if (!defined $page) {
-		$page = 0;
-	}
-	my $ftype;
-	my %co = parse_commit($hash_base)
-	    or die_error(404, "Unknown commit object");
-
-	my $refs = git_get_references();
-	my $limit = sprintf("--max-count=%i", (100 * ($page+1)));
-
-	my @commitlist = parse_commits($hash_base, 101, (100 * $page),
-	                               $file_name, "--full-history")
-	    or die_error(404, "No such file or directory on given branch");
-
-	if (!defined $hash && defined $file_name) {
-		# some commits could have deleted file in question,
-		# and not have it in tree, but one of them has to have it
-		for (my $i = 0; $i <= @commitlist; $i++) {
-			$hash = git_get_hash_by_path($commitlist[$i]{'id'}, $file_name);
-			last if defined $hash;
-		}
-	}
-	if (defined $hash) {
-		$ftype = git_get_type($hash);
-	}
-	if (!defined $ftype) {
-		die_error(500, "Unknown type of object");
-	}
-
-	my $paging_nav = '';
-	if ($page > 0) {
-		$paging_nav .=
-			$cgi->a({-href => href(action=>"history", hash=>$hash, hash_base=>$hash_base,
-			                       file_name=>$file_name)},
-			        "first");
-		$paging_nav .= " &sdot; " .
-			$cgi->a({-href => href(-replay=>1, page=>$page-1),
-			         -accesskey => "p", -title => "Alt-p"}, "prev");
-	} else {
-		$paging_nav .= "first";
-		$paging_nav .= " &sdot; prev";
-	}
-	my $next_link = '';
-	if ($#commitlist >= 100) {
-		$next_link =
-			$cgi->a({-href => href(-replay=>1, page=>$page+1),
-			         -accesskey => "n", -title => "Alt-n"}, "next");
-		$paging_nav .= " &sdot; $next_link";
-	} else {
-		$paging_nav .= " &sdot; next";
-	}
-
-	git_header_html();
-	git_print_page_nav('history','', $hash_base,$co{'tree'},$hash_base, $paging_nav);
-	git_print_header_div('commit', esc_html($co{'title'}), $hash_base);
-	git_print_page_path($file_name, $ftype, $hash_base);
-
-	git_history_body(\@commitlist, 0, 99,
-	                 $refs, $hash_base, $ftype, $next_link);
-
-	git_footer_html();
+	git_log_generic('history', \&git_history_body,
+	                $hash_base, $hash_parent_base,
+	                $file_name, $hash);
 }
 
 sub git_search {
-	gitweb_check_feature('search') or die_error(403, "Search is disabled");
+	$searchtype ||= 'commit';
+
+	# check if appropriate features are enabled
+	gitweb_check_feature('search')
+		or die_error(403, "Search is disabled");
+	if ($searchtype eq 'pickaxe') {
+		# pickaxe may take all resources of your box and run for several minutes
+		# with every query - so decide by yourself how public you make this feature
+		gitweb_check_feature('pickaxe')
+			or die_error(403, "Pickaxe search is disabled");
+	}
+	if ($searchtype eq 'grep') {
+		# grep search might be potentially CPU-intensive, too
+		gitweb_check_feature('grep')
+			or die_error(403, "Grep search is disabled");
+	}
+
 	if (!defined $searchtext) {
 		die_error(400, "Text field is empty");
 	}
@@ -5886,204 +7932,17 @@
 		$page = 0;
 	}
 
-	$searchtype ||= 'commit';
-	if ($searchtype eq 'pickaxe') {
-		# pickaxe may take all resources of your box and run for several minutes
-		# with every query - so decide by yourself how public you make this feature
-		gitweb_check_feature('pickaxe')
-		    or die_error(403, "Pickaxe is disabled");
+	if ($searchtype eq 'commit' ||
+	    $searchtype eq 'author' ||
+	    $searchtype eq 'committer') {
+		git_search_message(%co);
+	} elsif ($searchtype eq 'pickaxe') {
+		git_search_changes(%co);
+	} elsif ($searchtype eq 'grep') {
+		git_search_files(%co);
+	} else {
+		die_error(400, "Unknown search type");
 	}
-	if ($searchtype eq 'grep') {
-		gitweb_check_feature('grep')
-		    or die_error(403, "Grep is disabled");
-	}
-
-	git_header_html();
-
-	if ($searchtype eq 'commit' or $searchtype eq 'author' or $searchtype eq 'committer') {
-		my $greptype;
-		if ($searchtype eq 'commit') {
-			$greptype = "--grep=";
-		} elsif ($searchtype eq 'author') {
-			$greptype = "--author=";
-		} elsif ($searchtype eq 'committer') {
-			$greptype = "--committer=";
-		}
-		$greptype .= $searchtext;
-		my @commitlist = parse_commits($hash, 101, (100 * $page), undef,
-		                               $greptype, '--regexp-ignore-case',
-		                               $search_use_regexp ? '--extended-regexp' : '--fixed-strings');
-
-		my $paging_nav = '';
-		if ($page > 0) {
-			$paging_nav .=
-				$cgi->a({-href => href(action=>"search", hash=>$hash,
-				                       searchtext=>$searchtext,
-				                       searchtype=>$searchtype)},
-				        "first");
-			$paging_nav .= " &sdot; " .
-				$cgi->a({-href => href(-replay=>1, page=>$page-1),
-				         -accesskey => "p", -title => "Alt-p"}, "prev");
-		} else {
-			$paging_nav .= "first";
-			$paging_nav .= " &sdot; prev";
-		}
-		my $next_link = '';
-		if ($#commitlist >= 100) {
-			$next_link =
-				$cgi->a({-href => href(-replay=>1, page=>$page+1),
-				         -accesskey => "n", -title => "Alt-n"}, "next");
-			$paging_nav .= " &sdot; $next_link";
-		} else {
-			$paging_nav .= " &sdot; next";
-		}
-
-		if ($#commitlist >= 100) {
-		}
-
-		git_print_page_nav('','', $hash,$co{'tree'},$hash, $paging_nav);
-		git_print_header_div('commit', esc_html($co{'title'}), $hash);
-		git_search_grep_body(\@commitlist, 0, 99, $next_link);
-	}
-
-	if ($searchtype eq 'pickaxe') {
-		git_print_page_nav('','', $hash,$co{'tree'},$hash);
-		git_print_header_div('commit', esc_html($co{'title'}), $hash);
-
-		print "<table class=\"pickaxe search\">\n";
-		my $alternate = 1;
-		local $/ = "\n";
-		open my $fd, '-|', git_cmd(), '--no-pager', 'log', @diff_opts,
-			'--pretty=format:%H', '--no-abbrev', '--raw', "-S$searchtext",
-			($search_use_regexp ? '--pickaxe-regex' : ());
-		undef %co;
-		my @files;
-		while (my $line = <$fd>) {
-			chomp $line;
-			next unless $line;
-
-			my %set = parse_difftree_raw_line($line);
-			if (defined $set{'commit'}) {
-				# finish previous commit
-				if (%co) {
-					print "</td>\n" .
-					      "<td class=\"link\">" .
-					      $cgi->a({-href => href(action=>"commit", hash=>$co{'id'})}, "commit") .
-					      " | " .
-					      $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$co{'id'})}, "tree");
-					print "</td>\n" .
-					      "</tr>\n";
-				}
-
-				if ($alternate) {
-					print "<tr class=\"dark\">\n";
-				} else {
-					print "<tr class=\"light\">\n";
-				}
-				$alternate ^= 1;
-				%co = parse_commit($set{'commit'});
-				my $author = chop_and_escape_str($co{'author_name'}, 15, 5);
-				print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
-				      "<td><i>$author</i></td>\n" .
-				      "<td>" .
-				      $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}),
-				              -class => "list subject"},
-				              chop_and_escape_str($co{'title'}, 50) . "<br/>");
-			} elsif (defined $set{'to_id'}) {
-				next if ($set{'to_id'} =~ m/^0{40}$/);
-
-				print $cgi->a({-href => href(action=>"blob", hash_base=>$co{'id'},
-				                             hash=>$set{'to_id'}, file_name=>$set{'to_file'}),
-				              -class => "list"},
-				              "<span class=\"match\">" . esc_path($set{'file'}) . "</span>") .
-				      "<br/>\n";
-			}
-		}
-		close $fd;
-
-		# finish last commit (warning: repetition!)
-		if (%co) {
-			print "</td>\n" .
-			      "<td class=\"link\">" .
-			      $cgi->a({-href => href(action=>"commit", hash=>$co{'id'})}, "commit") .
-			      " | " .
-			      $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$co{'id'})}, "tree");
-			print "</td>\n" .
-			      "</tr>\n";
-		}
-
-		print "</table>\n";
-	}
-
-	if ($searchtype eq 'grep') {
-		git_print_page_nav('','', $hash,$co{'tree'},$hash);
-		git_print_header_div('commit', esc_html($co{'title'}), $hash);
-
-		print "<table class=\"grep_search\">\n";
-		my $alternate = 1;
-		my $matches = 0;
-		local $/ = "\n";
-		open my $fd, "-|", git_cmd(), 'grep', '-n',
-			$search_use_regexp ? ('-E', '-i') : '-F',
-			$searchtext, $co{'tree'};
-		my $lastfile = '';
-		while (my $line = <$fd>) {
-			chomp $line;
-			my ($file, $lno, $ltext, $binary);
-			last if ($matches++ > 1000);
-			if ($line =~ /^Binary file (.+) matches$/) {
-				$file = $1;
-				$binary = 1;
-			} else {
-				(undef, $file, $lno, $ltext) = split(/:/, $line, 4);
-			}
-			if ($file ne $lastfile) {
-				$lastfile and print "</td></tr>\n";
-				if ($alternate++) {
-					print "<tr class=\"dark\">\n";
-				} else {
-					print "<tr class=\"light\">\n";
-				}
-				print "<td class=\"list\">".
-					$cgi->a({-href => href(action=>"blob", hash=>$co{'hash'},
-							       file_name=>"$file"),
-						-class => "list"}, esc_path($file));
-				print "</td><td>\n";
-				$lastfile = $file;
-			}
-			if ($binary) {
-				print "<div class=\"binary\">Binary file</div>\n";
-			} else {
-				$ltext = untabify($ltext);
-				if ($ltext =~ m/^(.*)($search_regexp)(.*)$/i) {
-					$ltext = esc_html($1, -nbsp=>1);
-					$ltext .= '<span class="match">';
-					$ltext .= esc_html($2, -nbsp=>1);
-					$ltext .= '</span>';
-					$ltext .= esc_html($3, -nbsp=>1);
-				} else {
-					$ltext = esc_html($ltext, -nbsp=>1);
-				}
-				print "<div class=\"pre\">" .
-					$cgi->a({-href => href(action=>"blob", hash=>$co{'hash'},
-							       file_name=>"$file").'#l'.$lno,
-						-class => "linenr"}, sprintf('%4i', $lno))
-					. ' ' .  $ltext . "</div>\n";
-			}
-		}
-		if ($lastfile) {
-			print "</td></tr>\n";
-			if ($matches > 1000) {
-				print "<div class=\"diff nodifferences\">Too many matches, listing trimmed</div>\n";
-			}
-		} else {
-			print "<div class=\"diff nodifferences\">No matches found</div>\n";
-		}
-		close $fd;
-
-		print "</table>\n";
-	}
-	git_footer_html();
 }
 
 sub git_search_help {
@@ -6131,44 +7990,8 @@
 }
 
 sub git_shortlog {
-	my $head = git_get_head_hash($project);
-	if (!defined $hash) {
-		$hash = $head;
-	}
-	if (!defined $page) {
-		$page = 0;
-	}
-	my $refs = git_get_references();
-
-	my $commit_hash = $hash;
-	if (defined $hash_parent) {
-		$commit_hash = "$hash_parent..$hash";
-	}
-	my @commitlist = parse_commits($commit_hash, 101, (100 * $page));
-
-	my $paging_nav = format_paging_nav('shortlog', $hash, $head, $page, $#commitlist >= 100);
-	my $next_link = '';
-	if ($#commitlist >= 100) {
-		$next_link =
-			$cgi->a({-href => href(-replay=>1, page=>$page+1),
-			         -accesskey => "n", -title => "Alt-n"}, "next");
-	}
-	my $patch_max = gitweb_check_feature('patches');
-	if ($patch_max) {
-		if ($patch_max < 0 || @commitlist <= $patch_max) {
-			$paging_nav .= " &sdot; " .
-				$cgi->a({-href => href(action=>"patches", -replay=>1)},
-					"patches");
-		}
-	}
-
-	git_header_html();
-	git_print_page_nav('shortlog','', $hash,$hash,$hash, $paging_nav);
-	git_print_header_div('summary', $project);
-
-	git_shortlog_body(\@commitlist, 0, 99, $refs, $next_link);
-
-	git_footer_html();
+	git_log_generic('shortlog', \&git_shortlog_body,
+	                $hash, $hash_parent);
 }
 
 ## ......................................................................
@@ -6199,33 +8022,14 @@
 	if (defined($commitlist[0])) {
 		%latest_commit = %{$commitlist[0]};
 		my $latest_epoch = $latest_commit{'committer_epoch'};
-		%latest_date   = parse_date($latest_epoch);
-		my $if_modified = $cgi->http('IF_MODIFIED_SINCE');
-		if (defined $if_modified) {
-			my $since;
-			if (eval { require HTTP::Date; 1; }) {
-				$since = HTTP::Date::str2time($if_modified);
-			} elsif (eval { require Time::ParseDate; 1; }) {
-				$since = Time::ParseDate::parsedate($if_modified, GMT => 1);
-			}
-			if (defined $since && $latest_epoch <= $since) {
-				print $cgi->header(
-					-type => $content_type,
-					-charset => 'utf-8',
-					-last_modified => $latest_date{'rfc2822'},
-					-status => '304 Not Modified');
-				return;
-			}
-		}
-		print $cgi->header(
-			-type => $content_type,
-			-charset => 'utf-8',
-			-last_modified => $latest_date{'rfc2822'});
-	} else {
-		print $cgi->header(
-			-type => $content_type,
-			-charset => 'utf-8');
+		exit_if_unmodified_since($latest_epoch);
+		%latest_date = parse_date($latest_epoch, $latest_commit{'comitter_tz'});
 	}
+	print $cgi->header(
+		-type => $content_type,
+		-charset => 'utf-8',
+		%latest_date ? (-last_modified => $latest_date{'rfc2822'}) : (),
+		-status => '200 OK');
 
 	# Optimization: skip generating the body if client asks only
 	# for Last-Modified date.
@@ -6309,7 +8113,7 @@
 		if (defined $favicon) {
 			print "<icon>" . esc_url($favicon) . "</icon>\n";
 		}
-		if (defined $logo_url) {
+		if (defined $logo) {
 			# not twice as wide as tall: 72 x 27 pixels
 			print "<logo>" . esc_url($logo) . "</logo>\n";
 		}
@@ -6330,7 +8134,7 @@
 		if (($i >= 20) && ((time - $co{'author_epoch'}) > 48*60*60)) {
 			last;
 		}
-		my %cd = parse_date($co{'author_epoch'});
+		my %cd = parse_date($co{'author_epoch'}, $co{'author_tz'});
 
 		# get list of changed files
 		open my $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
@@ -6439,18 +8243,28 @@
 }
 
 sub git_opml {
-	my @list = git_get_projects_list();
+	my @list = git_get_projects_list($project_filter, $strict_export);
+	if (!@list) {
+		die_error(404, "No projects found");
+	}
 
 	print $cgi->header(
 		-type => 'text/xml',
 		-charset => 'utf-8',
 		-content_disposition => 'inline; filename="opml.xml"');
 
+	my $title = esc_html($site_name);
+	my $filter = " within subdirectory ";
+	if (defined $project_filter) {
+		$filter .= esc_html($project_filter);
+	} else {
+		$filter = "";
+	}
 	print <<XML;
 <?xml version="1.0" encoding="utf-8"?>
 <opml version="1.0">
 <head>
-  <title>$site_name OPML Export</title>
+  <title>$title OPML Export$filter</title>
 </head>
 <body>
 <outline text="git RSS feeds">
diff --git a/gitweb/static/git-favicon.png b/gitweb/static/git-favicon.png
new file mode 100644
index 0000000..aae35a7
--- /dev/null
+++ b/gitweb/static/git-favicon.png
Binary files differ
diff --git a/gitweb/static/git-logo.png b/gitweb/static/git-logo.png
new file mode 100644
index 0000000..f4ede2e
--- /dev/null
+++ b/gitweb/static/git-logo.png
Binary files differ
diff --git a/gitweb/static/gitweb.css b/gitweb/static/gitweb.css
new file mode 100644
index 0000000..cb86d2d
--- /dev/null
+++ b/gitweb/static/gitweb.css
@@ -0,0 +1,681 @@
+body {
+	font-family: sans-serif;
+	font-size: small;
+	border: solid #d9d8d1;
+	border-width: 1px;
+	margin: 10px;
+	background-color: #ffffff;
+	color: #000000;
+}
+
+a {
+	color: #0000cc;
+}
+
+a:hover, a:visited, a:active {
+	color: #880000;
+}
+
+span.cntrl {
+	border: dashed #aaaaaa;
+	border-width: 1px;
+	padding: 0px 2px 0px 2px;
+	margin:  0px 2px 0px 2px;
+}
+
+img.logo {
+	float: right;
+	border-width: 0px;
+}
+
+img.avatar {
+	vertical-align: middle;
+}
+
+a.list img.avatar {
+	border-style: none;
+}
+
+div.page_header {
+	height: 25px;
+	padding: 8px;
+	font-size: 150%;
+	font-weight: bold;
+	background-color: #d9d8d1;
+}
+
+div.page_header a:visited, a.header {
+	color: #0000cc;
+}
+
+div.page_header a:hover {
+	color: #880000;
+}
+
+div.page_nav {
+	padding: 8px;
+}
+
+div.page_nav a:visited {
+	color: #0000cc;
+}
+
+div.page_path {
+	padding: 8px;
+	font-weight: bold;
+	border: solid #d9d8d1;
+	border-width: 0px 0px 1px;
+}
+
+div.page_footer {
+	height: 17px;
+	padding: 4px 8px;
+	background-color: #d9d8d1;
+}
+
+div.page_footer_text {
+	float: left;
+	color: #555555;
+	font-style: italic;
+}
+
+div#generating_info {
+	margin: 4px;
+	font-size: smaller;
+	text-align: center;
+	color: #505050;
+}
+
+div.page_body {
+	padding: 8px;
+	font-family: monospace;
+}
+
+div.title, a.title {
+	display: block;
+	padding: 6px 8px;
+	font-weight: bold;
+	background-color: #edece6;
+	text-decoration: none;
+	color: #000000;
+}
+
+div.readme {
+	padding: 8px;
+}
+
+a.title:hover {
+	background-color: #d9d8d1;
+}
+
+div.title_text {
+	padding: 6px 0px;
+	border: solid #d9d8d1;
+	border-width: 0px 0px 1px;
+	font-family: monospace;
+}
+
+div.log_body {
+	padding: 8px 8px 8px 150px;
+}
+
+span.age {
+	position: relative;
+	float: left;
+	width: 142px;
+	font-style: italic;
+}
+
+span.signoff {
+	color: #888888;
+}
+
+div.log_link {
+	padding: 0px 8px;
+	font-size: 70%;
+	font-family: sans-serif;
+	font-style: normal;
+	position: relative;
+	float: left;
+	width: 136px;
+}
+
+div.list_head {
+	padding: 6px 8px 4px;
+	border: solid #d9d8d1;
+	border-width: 1px 0px 0px;
+	font-style: italic;
+}
+
+.author_date, .author {
+	font-style: italic;
+}
+
+div.author_date {
+	padding: 8px;
+	border: solid #d9d8d1;
+	border-width: 0px 0px 1px 0px;
+}
+
+a.list {
+	text-decoration: none;
+	color: #000000;
+}
+
+a.subject, a.name {
+	font-weight: bold;
+}
+
+table.tags a.subject {
+	font-weight: normal;
+}
+
+a.list:hover {
+	text-decoration: underline;
+	color: #880000;
+}
+
+a.text {
+	text-decoration: none;
+	color: #0000cc;
+}
+
+a.text:visited {
+	text-decoration: none;
+	color: #880000;
+}
+
+a.text:hover {
+	text-decoration: underline;
+	color: #880000;
+}
+
+table {
+	padding: 8px 4px;
+	border-spacing: 0;
+}
+
+table.diff_tree {
+	font-family: monospace;
+}
+
+table.combined.diff_tree th {
+	text-align: center;
+}
+
+table.combined.diff_tree td {
+	padding-right: 24px;
+}
+
+table.combined.diff_tree th.link,
+table.combined.diff_tree td.link {
+	padding: 0px 2px;
+}
+
+table.combined.diff_tree td.nochange a {
+	color: #6666ff;
+}
+
+table.combined.diff_tree td.nochange a:hover,
+table.combined.diff_tree td.nochange a:visited {
+	color: #d06666;
+}
+
+table.blame {
+	border-collapse: collapse;
+}
+
+table.blame td {
+	padding: 0px 5px;
+	font-size: 100%;
+	vertical-align: top;
+}
+
+th {
+	padding: 2px 5px;
+	font-size: 100%;
+	text-align: left;
+}
+
+/* do not change row style on hover for 'blame' view */
+tr.light,
+table.blame .light:hover {
+	background-color: #ffffff;
+}
+
+tr.dark,
+table.blame .dark:hover {
+	background-color: #f6f6f0;
+}
+
+/* currently both use the same, but it can change */
+tr.light:hover,
+tr.dark:hover {
+	background-color: #edece6;
+}
+
+/* boundary commits in 'blame' view */
+/* and commits without "previous" */
+tr.boundary td.sha1,
+tr.no-previous td.linenr {
+	font-weight: bold;
+}
+
+/* for 'blame_incremental', during processing */
+tr.color1 { background-color: #f6fff6; }
+tr.color2 { background-color: #f6f6ff; }
+tr.color3 { background-color: #fff6f6; }
+
+td {
+	padding: 2px 5px;
+	font-size: 100%;
+	vertical-align: top;
+}
+
+td.link, td.selflink {
+	padding: 2px 5px;
+	font-family: sans-serif;
+	font-size: 70%;
+}
+
+td.selflink {
+	padding-right: 0px;
+}
+
+td.sha1 {
+	font-family: monospace;
+}
+
+.error {
+	color: red;
+	background-color: yellow;
+}
+
+td.current_head {
+	text-decoration: underline;
+}
+
+td.category {
+	background-color: #d9d8d1;
+	border-top: 1px solid #000000;
+	border-left: 1px solid #000000;
+	font-weight: bold;
+}
+
+table.diff_tree span.file_status.new {
+	color: #008000;
+}
+
+table.diff_tree span.file_status.deleted {
+	color: #c00000;
+}
+
+table.diff_tree span.file_status.moved,
+table.diff_tree span.file_status.mode_chnge {
+	color: #777777;
+}
+
+table.diff_tree span.file_status.copied {
+  color: #70a070;
+}
+
+/* noage: "No commits" */
+table.project_list td.noage {
+	color: #808080;
+	font-style: italic;
+}
+
+/* age2: 60*60*24*2 <= age */
+table.project_list td.age2, table.blame td.age2 {
+	font-style: italic;
+}
+
+/* age1: 60*60*2 <= age < 60*60*24*2 */
+table.project_list td.age1 {
+	color: #009900;
+	font-style: italic;
+}
+
+table.blame td.age1 {
+	color: #009900;
+	background: transparent;
+}
+
+/* age0: age < 60*60*2 */
+table.project_list td.age0 {
+	color: #009900;
+	font-style: italic;
+	font-weight: bold;
+}
+
+table.blame td.age0 {
+	color: #009900;
+	background: transparent;
+	font-weight: bold;
+}
+
+td.pre, div.pre, div.diff {
+	font-family: monospace;
+	font-size: 12px;
+	white-space: pre;
+}
+
+td.mode {
+	font-family: monospace;
+}
+
+/* progress of blame_interactive */
+div#progress_bar {
+	height: 2px;
+	margin-bottom: -2px;
+	background-color: #d8d9d0;
+}
+div#progress_info {
+	float: right;
+	text-align: right;
+}
+
+/* format of (optional) objects size in 'tree' view */
+td.size {
+	font-family: monospace;
+	text-align: right;
+}
+
+/* styling of diffs (patchsets): commitdiff and blobdiff views */
+div.diff.header,
+div.diff.extended_header {
+	white-space: normal;
+}
+
+div.diff.header {
+	font-weight: bold;
+
+	background-color: #edece6;
+
+	margin-top: 4px;
+	padding: 4px 0px 2px 0px;
+	border: solid #d9d8d1;
+	border-width: 1px 0px 1px 0px;
+}
+
+div.diff.header a.path {
+	text-decoration: underline;
+}
+
+div.diff.extended_header,
+div.diff.extended_header a.path,
+div.diff.extended_header a.hash {
+	color: #777777;
+}
+
+div.diff.extended_header .info {
+	color: #b0b0b0;
+}
+
+div.diff.extended_header {
+	background-color: #f6f5ee;
+	padding: 2px 0px 2px 0px;
+}
+
+div.diff a.list,
+div.diff a.path,
+div.diff a.hash {
+	text-decoration: none;
+}
+
+div.diff a.list:hover,
+div.diff a.path:hover,
+div.diff a.hash:hover {
+	text-decoration: underline;
+}
+
+div.diff.to_file a.path,
+div.diff.to_file {
+	color: #007000;
+}
+
+div.diff.add {
+	color: #008800;
+}
+
+div.diff.add span.marked {
+	background-color: #aaffaa;
+}
+
+div.diff.from_file a.path,
+div.diff.from_file {
+	color: #aa0000;
+}
+
+div.diff.rem {
+	color: #cc0000;
+}
+
+div.diff.rem span.marked {
+	background-color: #ffaaaa;
+}
+
+div.diff.chunk_header a,
+div.diff.chunk_header {
+	color: #990099;
+}
+
+div.diff.chunk_header {
+	border: dotted #ffe0ff;
+	border-width: 1px 0px 0px 0px;
+	margin-top: 2px;
+}
+
+div.diff.chunk_header span.chunk_info {
+	background-color: #ffeeff;
+}
+
+div.diff.chunk_header span.section {
+	color: #aa22aa;
+}
+
+div.diff.incomplete {
+	color: #cccccc;
+}
+
+div.diff.nodifferences {
+	font-weight: bold;
+	color: #600000;
+}
+
+/* side-by-side diff */
+div.chunk_block {
+	overflow: hidden;
+}
+
+div.chunk_block div.old {
+	float: left;
+	width: 50%;
+	overflow: hidden;
+}
+
+div.chunk_block div.new {
+	margin-left: 50%;
+	width: 50%;
+}
+
+div.chunk_block.rem div.old div.diff.rem {
+	background-color: #fff5f5;
+}
+div.chunk_block.add div.new div.diff.add {
+	background-color: #f8fff8;
+}
+div.chunk_block.chg div     div.diff {
+	background-color: #fffff0;
+}
+div.chunk_block.ctx div     div.diff.ctx {
+	color: #404040;
+}
+
+
+div.index_include {
+	border: solid #d9d8d1;
+	border-width: 0px 0px 1px;
+	padding: 12px 8px;
+}
+
+div.search {
+	font-size: 100%;
+	font-weight: normal;
+	margin: 4px 8px;
+	float: right;
+	top: 56px;
+	right: 12px
+}
+
+div.projsearch {
+	text-align: center;
+	margin: 20px 0px;
+}
+
+div.projsearch form {
+	margin-bottom: 2px;
+}
+
+td.linenr {
+	text-align: right;
+}
+
+a.linenr {
+	color: #999999;
+	text-decoration: none
+}
+
+a.rss_logo {
+	float: right;
+	padding: 3px 0px;
+	width: 35px;
+	line-height: 10px;
+	border: 1px solid;
+	border-color: #fcc7a5 #7d3302 #3e1a01 #ff954e;
+	color: #ffffff;
+	background-color: #ff6600;
+	font-weight: bold;
+	font-family: sans-serif;
+	font-size: 70%;
+	text-align: center;
+	text-decoration: none;
+}
+
+a.rss_logo:hover {
+	background-color: #ee5500;
+}
+
+a.rss_logo.generic {
+	background-color: #ff8800;
+}
+
+a.rss_logo.generic:hover {
+	background-color: #ee7700;
+}
+
+span.refs span {
+	padding: 0px 4px;
+	font-size: 70%;
+	font-weight: normal;
+	border: 1px solid;
+	background-color: #ffaaff;
+	border-color: #ffccff #ff00ee #ff00ee #ffccff;
+}
+
+span.refs span a {
+	text-decoration: none;
+	color: inherit;
+}
+
+span.refs span a:hover {
+	text-decoration: underline;
+}
+
+span.refs span.indirect {
+	font-style: italic;
+}
+
+span.refs span.ref {
+	background-color: #aaaaff;
+	border-color: #ccccff #0033cc #0033cc #ccccff;
+}
+
+span.refs span.tag {
+	background-color: #ffffaa;
+	border-color: #ffffcc #ffee00 #ffee00 #ffffcc;
+}
+
+span.refs span.head {
+	background-color: #aaffaa;
+	border-color: #ccffcc #00cc33 #00cc33 #ccffcc;
+}
+
+span.atnight {
+	color: #cc0000;
+}
+
+span.match {
+	color: #e00000;
+}
+
+div.binary {
+	font-style: italic;
+}
+
+div.remote {
+	margin: .5em;
+	border: 1px solid #d9d8d1;
+	display: inline-block;
+}
+
+/* JavaScript-based timezone manipulation */
+
+.popup { /* timezone selection UI */
+	position: absolute;
+	/* "top: 0; right: 0;" would be better, if not for bugs in browsers */
+	top: 0; left: 0;
+	border: 1px solid;
+	padding: 2px;
+	background-color: #f0f0f0;
+	font-style: normal;
+	color: #000000;
+	cursor: auto;
+}
+
+.close-button { /* close timezone selection UI without selecting */
+	/* float doesn't work within absolutely positioned container,
+	 * if width of container is not set explicitly */
+	/* float: right; */
+	position: absolute;
+	top: 0px; right: 0px;
+	border:  1px solid green;
+	margin:  1px 1px 1px 1px;
+	padding-bottom: 2px;
+	width:     12px;
+	height:    10px;
+	font-size:  9px;
+	font-weight: bold;
+	text-align: center;
+	background-color: #fff0f0;
+	cursor: pointer;
+}
+
+
+/* Style definition generated by highlight 2.4.5, http://www.andre-simon.de/ */
+
+/* Highlighting theme definition: */
+
+.num    { color:#2928ff; }
+.esc    { color:#ff00ff; }
+.str    { color:#ff0000; }
+.dstr   { color:#818100; }
+.slc    { color:#838183; font-style:italic; }
+.com    { color:#838183; font-style:italic; }
+.dir    { color:#008200; }
+.sym    { color:#000000; }
+.line   { color:#555555; }
+.kwa    { color:#000000; font-weight:bold; }
+.kwb    { color:#830000; }
+.kwc    { color:#000000; font-weight:bold; }
+.kwd    { color:#010181; }
diff --git a/gitweb/static/js/README b/gitweb/static/js/README
new file mode 100644
index 0000000..f8460ed
--- /dev/null
+++ b/gitweb/static/js/README
@@ -0,0 +1,20 @@
+GIT web interface (gitweb) - JavaScript
+=======================================
+
+This directory holds JavaScript code used by gitweb (GIT web interface).
+Scripts from there would be concatenated together in the order specified
+by gitweb/Makefile into gitweb/static/gitweb.js, during building of
+gitweb/gitweb.cgi (during gitweb building).  The resulting file (or its
+minification) would then be installed / deployed together with gitweb.
+
+Scripts in 'lib/' subdirectory compose generic JavaScript library,
+providing features required by gitweb but in no way limited to gitweb
+only.  In the future those scripts could be replaced by some JavaScript
+library / framework, like e.g. jQuery, YUI, Prototype, MooTools, Dojo,
+ExtJS, Script.aculo.us or SproutCore.
+
+All scripts that manipulate gitweb output should be put outside 'lib/',
+directly in this directory ('gitweb/static/js/').  Those scripts would
+have to be rewritten if gitweb moves to using some JavaScript library.
+
+See also comments in gitweb/Makefile.
diff --git a/gitweb/static/js/adjust-timezone.js b/gitweb/static/js/adjust-timezone.js
new file mode 100644
index 0000000..0c67779
--- /dev/null
+++ b/gitweb/static/js/adjust-timezone.js
@@ -0,0 +1,330 @@
+// Copyright (C) 2011, John 'Warthog9' Hawley <warthog9@eaglescrag.net>
+//               2011, Jakub Narebski <jnareb@gmail.com>
+
+/**
+ * @fileOverview Manipulate dates in gitweb output, adjusting timezone
+ * @license GPLv2 or later
+ */
+
+/**
+ * Get common timezone, add UI for changing timezones, and adjust
+ * dates to use requested common timezone.
+ *
+ * This function is called during onload event (added to window.onload).
+ *
+ * @param {String} tzDefault: default timezone, if there is no cookie
+ * @param {Object} tzCookieInfo: object literal with info about cookie to store timezone
+ * @param {String} tzCookieInfo.name: name of cookie to store timezone
+ * @param {String} tzClassName: denotes elements with date to be adjusted
+ */
+function onloadTZSetup(tzDefault, tzCookieInfo, tzClassName) {
+	var tzCookieTZ = getCookie(tzCookieInfo.name, tzCookieInfo);
+	var tz = tzDefault;
+
+	if (tzCookieTZ) {
+		// set timezone to value saved in a cookie
+		tz = tzCookieTZ;
+		// refresh cookie, so its expiration counts from last use of gitweb
+		setCookie(tzCookieInfo.name, tzCookieTZ, tzCookieInfo);
+	}
+
+	// add UI for changing timezone
+	addChangeTZ(tz, tzCookieInfo, tzClassName);
+
+	// server-side of gitweb produces datetime in UTC,
+	// so if tz is 'utc' there is no need for changes
+	var nochange = tz === 'utc';
+
+	// adjust dates to use specified common timezone
+	fixDatetimeTZ(tz, tzClassName, nochange);
+}
+
+
+/* ...................................................................... */
+/* Changing dates to use requested timezone */
+
+/**
+ * Replace RFC-2822 dates contained in SPAN elements with tzClassName
+ * CSS class with equivalent dates in given timezone.
+ *
+ * @param {String} tz: numeric timezone in '(-|+)HHMM' format, or 'utc', or 'local'
+ * @param {String} tzClassName: specifies elements to be changed
+ * @param {Boolean} nochange: markup for timezone change, but don't change it
+ */
+function fixDatetimeTZ(tz, tzClassName, nochange) {
+	// sanity check, method should be ensured by common-lib.js
+	if (!document.getElementsByClassName) {
+		return;
+	}
+
+	// translate to timezone in '(-|+)HHMM' format
+	tz = normalizeTimezoneInfo(tz);
+
+	// NOTE: result of getElementsByClassName should probably be cached
+	var classesFound = document.getElementsByClassName(tzClassName, "span");
+	for (var i = 0, len = classesFound.length; i < len; i++) {
+		var curElement = classesFound[i];
+
+		curElement.title = 'Click to change timezone';
+		if (!nochange) {
+			// we use *.firstChild.data (W3C DOM) instead of *.innerHTML
+			// as the latter doesn't always work everywhere in every browser
+			var epoch = parseRFC2822Date(curElement.firstChild.data);
+			var adjusted = formatDateRFC2882(epoch, tz);
+
+			curElement.firstChild.data = adjusted;
+		}
+	}
+}
+
+
+/* ...................................................................... */
+/* Adding triggers, generating timezone menu, displaying and hiding */
+
+/**
+ * Adds triggers for UI to change common timezone used for dates in
+ * gitweb output: it marks up and/or creates item to click to invoke
+ * timezone change UI, creates timezone UI fragment to be attached,
+ * and installs appropriate onclick trigger (via event delegation).
+ *
+ * @param {String} tzSelected: pre-selected timezone,
+ *                             'utc' or 'local' or '(-|+)HHMM'
+ * @param {Object} tzCookieInfo: object literal with info about cookie to store timezone
+ * @param {String} tzClassName: specifies elements to install trigger
+ */
+function addChangeTZ(tzSelected, tzCookieInfo, tzClassName) {
+	// make link to timezone UI discoverable
+	addCssRule('.'+tzClassName + ':hover',
+	           'text-decoration: underline; cursor: help;');
+
+	// create form for selecting timezone (to be saved in a cookie)
+	var tzSelectFragment = document.createDocumentFragment();
+	tzSelectFragment = createChangeTZForm(tzSelectFragment,
+	                                      tzSelected, tzCookieInfo, tzClassName);
+
+	// event delegation handler for timezone selection UI (clicking on entry)
+	// see http://www.nczonline.net/blog/2009/06/30/event-delegation-in-javascript/
+	// assumes that there is no existing document.onclick handler
+	document.onclick = function onclickHandler(event) {
+		//IE doesn't pass in the event object
+		event = event || window.event;
+
+		//IE uses srcElement as the target
+		var target = event.target || event.srcElement;
+
+		switch (target.className) {
+		case tzClassName:
+			// don't display timezone menu if it is already displayed
+			if (tzSelectFragment.childNodes.length > 0) {
+				displayChangeTZForm(target, tzSelectFragment);
+			}
+			break;
+		} // end switch
+	};
+}
+
+/**
+ * Create DocumentFragment with UI for changing common timezone in
+ * which dates are shown in.
+ *
+ * @param {DocumentFragment} documentFragment: where attach UI
+ * @param {String} tzSelected: default (pre-selected) timezone
+ * @param {Object} tzCookieInfo: object literal with info about cookie to store timezone
+ * @returns {DocumentFragment}
+ */
+function createChangeTZForm(documentFragment, tzSelected, tzCookieInfo, tzClassName) {
+	var div = document.createElement("div");
+	div.className = 'popup';
+
+	/* '<div class="close-button" title="(click on this box to close)">X</div>' */
+	var closeButton = document.createElement('div');
+	closeButton.className = 'close-button';
+	closeButton.title = '(click on this box to close)';
+	closeButton.appendChild(document.createTextNode('X'));
+	closeButton.onclick = closeTZFormHandler(documentFragment, tzClassName);
+	div.appendChild(closeButton);
+
+	/* 'Select timezone: <br clear="all">' */
+	div.appendChild(document.createTextNode('Select timezone: '));
+	var br = document.createElement('br');
+	br.clear = 'all';
+	div.appendChild(br);
+
+	/* '<select name="tzoffset">
+	 *    ...
+	 *    <option value="-0700">UTC-07:00</option>
+	 *    <option value="-0600">UTC-06:00</option>
+	 *    ...
+	 *  </select>' */
+	var select = document.createElement("select");
+	select.name = "tzoffset";
+	//select.style.clear = 'all';
+	select.appendChild(generateTZOptions(tzSelected));
+	select.onchange = selectTZHandler(documentFragment, tzCookieInfo, tzClassName);
+	div.appendChild(select);
+
+	documentFragment.appendChild(div);
+
+	return documentFragment;
+}
+
+
+/**
+ * Hide (remove from DOM) timezone change UI, ensuring that it is not
+ * garbage collected and that it can be re-enabled later.
+ *
+ * @param {DocumentFragment} documentFragment: contains detached UI
+ * @param {HTMLSelectElement} target: select element inside of UI
+ * @param {String} tzClassName: specifies element where UI was installed
+ * @returns {DocumentFragment} documentFragment
+ */
+function removeChangeTZForm(documentFragment, target, tzClassName) {
+	// find containing element, where we appended timezone selection UI
+	// `target' is somewhere inside timezone menu
+	var container = target.parentNode, popup = target;
+	while (container &&
+	       container.className !== tzClassName) {
+		popup = container;
+		container = container.parentNode;
+	}
+	// safety check if we found correct container,
+	// and if it isn't deleted already
+	if (!container || !popup ||
+	    container.className !== tzClassName ||
+	    popup.className     !== 'popup') {
+		return documentFragment;
+	}
+
+	// timezone selection UI was appended as last child
+	// see also displayChangeTZForm function
+	var removed = popup.parentNode.removeChild(popup);
+	if (documentFragment.firstChild !== removed) { // the only child
+		// re-append it so it would be available for next time
+		documentFragment.appendChild(removed);
+	}
+	// all of inline style was added by this script
+	// it is not really needed to remove it, but it is a good practice
+	container.removeAttribute('style');
+
+	return documentFragment;
+}
+
+
+/**
+ * Display UI for changing common timezone for dates in gitweb output.
+ * To be used from 'onclick' event handler.
+ *
+ * @param {HTMLElement} target: where to install/display UI
+ * @param {DocumentFragment} tzSelectFragment: timezone selection UI
+ */
+function displayChangeTZForm(target, tzSelectFragment) {
+	// for absolute positioning to be related to target element
+	target.style.position = 'relative';
+	target.style.display = 'inline-block';
+
+	// show/display UI for changing timezone
+	target.appendChild(tzSelectFragment);
+}
+
+
+/* ...................................................................... */
+/* List of timezones for timezone selection menu */
+
+/**
+ * Generate list of timezones for creating timezone select UI
+ *
+ * @returns {Object[]} list of e.g. { value: '+0100', descr: 'GMT+01:00' }
+ */
+function generateTZList() {
+	var timezones = [
+		{ value: "utc",   descr: "UTC/GMT"},
+		{ value: "local", descr: "Local (per browser)"}
+	];
+
+	// generate all full hour timezones (no fractional timezones)
+	for (var x = -12, idx = timezones.length; x <= +14; x++, idx++) {
+		var hours = (x >= 0 ? '+' : '-') + padLeft(x >=0 ? x : -x, 2);
+		timezones[idx] = { value: hours + '00', descr: 'UTC' + hours + ':00'};
+		if (x === 0) {
+			timezones[idx].descr = 'UTC\u00B100:00'; // 'UTC&plusmn;00:00'
+		}
+	}
+
+	return timezones;
+}
+
+/**
+ * Generate <options> elements for timezone select UI
+ *
+ * @param {String} tzSelected: default timezone
+ * @returns {DocumentFragment} list of options elements to appendChild
+ */
+function generateTZOptions(tzSelected) {
+	var elems = document.createDocumentFragment();
+	var timezones = generateTZList();
+
+	for (var i = 0, len = timezones.length; i < len; i++) {
+		var tzone = timezones[i];
+		var option = document.createElement("option");
+		if (tzone.value === tzSelected) {
+			option.defaultSelected = true;
+		}
+		option.value = tzone.value;
+		option.appendChild(document.createTextNode(tzone.descr));
+
+		elems.appendChild(option);
+	}
+
+	return elems;
+}
+
+
+/* ...................................................................... */
+/* Event handlers and/or their generators */
+
+/**
+ * Create event handler that select timezone and closes timezone select UI.
+ * To be used as $('select[name="tzselect"]').onchange handler.
+ *
+ * @param {DocumentFragment} tzSelectFragment: timezone selection UI
+ * @param {Object} tzCookieInfo: object literal with info about cookie to store timezone
+ * @param {String} tzCookieInfo.name: name of cookie to save result of selection
+ * @param {String} tzClassName: specifies element where UI was installed
+ * @returns {Function} event handler
+ */
+function selectTZHandler(tzSelectFragment, tzCookieInfo, tzClassName) {
+	//return function selectTZ(event) {
+	return function (event) {
+		event = event || window.event;
+		var target = event.target || event.srcElement;
+
+		var selected = target.options.item(target.selectedIndex);
+		removeChangeTZForm(tzSelectFragment, target, tzClassName);
+
+		if (selected) {
+			selected.defaultSelected = true;
+			setCookie(tzCookieInfo.name, selected.value, tzCookieInfo);
+			fixDatetimeTZ(selected.value, tzClassName);
+		}
+	};
+}
+
+/**
+ * Create event handler that closes timezone select UI.
+ * To be used e.g. as $('.closebutton').onclick handler.
+ *
+ * @param {DocumentFragment} tzSelectFragment: timezone selection UI
+ * @param {String} tzClassName: specifies element where UI was installed
+ * @returns {Function} event handler
+ */
+function closeTZFormHandler(tzSelectFragment, tzClassName) {
+	//return function closeTZForm(event) {
+	return function (event) {
+		event = event || window.event;
+		var target = event.target || event.srcElement;
+
+		removeChangeTZForm(tzSelectFragment, target, tzClassName);
+	};
+}
+
+/* end of adjust-timezone.js */
diff --git a/gitweb/static/js/blame_incremental.js b/gitweb/static/js/blame_incremental.js
new file mode 100644
index 0000000..db6eb50
--- /dev/null
+++ b/gitweb/static/js/blame_incremental.js
@@ -0,0 +1,692 @@
+// Copyright (C) 2007, Fredrik Kuivinen <frekui@gmail.com>
+//               2007, Petr Baudis <pasky@suse.cz>
+//          2008-2011, Jakub Narebski <jnareb@gmail.com>
+
+/**
+ * @fileOverview JavaScript side of Ajax-y 'blame_incremental' view in gitweb
+ * @license GPLv2 or later
+ */
+
+/* ============================================================ */
+/*
+ * This code uses DOM methods instead of (nonstandard) innerHTML
+ * to modify page.
+ *
+ * innerHTML is non-standard IE extension, though supported by most
+ * browsers; however Firefox up to version 1.5 didn't implement it in
+ * a strict mode (application/xml+xhtml mimetype).
+ *
+ * Also my simple benchmarks show that using elem.firstChild.data =
+ * 'content' is slightly faster than elem.innerHTML = 'content'.  It
+ * is however more fragile (text element fragment must exists), and
+ * less feature-rich (we cannot add HTML).
+ *
+ * Note that DOM 2 HTML is preferred over generic DOM 2 Core; the
+ * equivalent using DOM 2 Core is usually shown in comments.
+ */
+
+
+/* ............................................................ */
+/* utility/helper functions (and variables) */
+
+var projectUrl; // partial query + separator ('?' or ';')
+
+// 'commits' is an associative map. It maps SHA1s to Commit objects.
+var commits = {};
+
+/**
+ * constructor for Commit objects, used in 'blame'
+ * @class Represents a blamed commit
+ * @param {String} sha1: SHA-1 identifier of a commit
+ */
+function Commit(sha1) {
+	if (this instanceof Commit) {
+		this.sha1 = sha1;
+		this.nprevious = 0; /* number of 'previous', effective parents */
+	} else {
+		return new Commit(sha1);
+	}
+}
+
+/* ............................................................ */
+/* progress info, timing, error reporting */
+
+var blamedLines = 0;
+var totalLines  = '???';
+var div_progress_bar;
+var div_progress_info;
+
+/**
+ * Detects how many lines does a blamed file have,
+ * This information is used in progress info
+ *
+ * @returns {Number|String} Number of lines in file, or string '...'
+ */
+function countLines() {
+	var table =
+		document.getElementById('blame_table') ||
+		document.getElementsByTagName('table')[0];
+
+	if (table) {
+		return table.getElementsByTagName('tr').length - 1; // for header
+	} else {
+		return '...';
+	}
+}
+
+/**
+ * update progress info and length (width) of progress bar
+ *
+ * @globals div_progress_info, div_progress_bar, blamedLines, totalLines
+ */
+function updateProgressInfo() {
+	if (!div_progress_info) {
+		div_progress_info = document.getElementById('progress_info');
+	}
+	if (!div_progress_bar) {
+		div_progress_bar = document.getElementById('progress_bar');
+	}
+	if (!div_progress_info && !div_progress_bar) {
+		return;
+	}
+
+	var percentage = Math.floor(100.0*blamedLines/totalLines);
+
+	if (div_progress_info) {
+		div_progress_info.firstChild.data  = blamedLines + ' / ' + totalLines +
+			' (' + padLeftStr(percentage, 3, '\u00A0') + '%)';
+	}
+
+	if (div_progress_bar) {
+		//div_progress_bar.setAttribute('style', 'width: '+percentage+'%;');
+		div_progress_bar.style.width = percentage + '%';
+	}
+}
+
+
+var t_interval_server = '';
+var cmds_server = '';
+var t0 = new Date();
+
+/**
+ * write how much it took to generate data, and to run script
+ *
+ * @globals t0, t_interval_server, cmds_server
+ */
+function writeTimeInterval() {
+	var info_time = document.getElementById('generating_time');
+	if (!info_time || !t_interval_server) {
+		return;
+	}
+	var t1 = new Date();
+	info_time.firstChild.data += ' + (' +
+		t_interval_server + ' sec server blame_data / ' +
+		(t1.getTime() - t0.getTime())/1000 + ' sec client JavaScript)';
+
+	var info_cmds = document.getElementById('generating_cmd');
+	if (!info_time || !cmds_server) {
+		return;
+	}
+	info_cmds.firstChild.data += ' + ' + cmds_server;
+}
+
+/**
+ * show an error message alert to user within page (in progress info area)
+ * @param {String} str: plain text error message (no HTML)
+ *
+ * @globals div_progress_info
+ */
+function errorInfo(str) {
+	if (!div_progress_info) {
+		div_progress_info = document.getElementById('progress_info');
+	}
+	if (div_progress_info) {
+		div_progress_info.className = 'error';
+		div_progress_info.firstChild.data = str;
+	}
+}
+
+/* ............................................................ */
+/* coloring rows during blame_data (git blame --incremental) run */
+
+/**
+ * used to extract N from 'colorN', where N is a number,
+ * @constant
+ */
+var colorRe = /\bcolor([0-9]*)\b/;
+
+/**
+ * return N if <tr class="colorN">, otherwise return null
+ * (some browsers require CSS class names to begin with letter)
+ *
+ * @param {HTMLElement} tr: table row element to check
+ * @param {String} tr.className: 'class' attribute of tr element
+ * @returns {Number|null} N if tr.className == 'colorN', otherwise null
+ *
+ * @globals colorRe
+ */
+function getColorNo(tr) {
+	if (!tr) {
+		return null;
+	}
+	var className = tr.className;
+	if (className) {
+		var match = colorRe.exec(className);
+		if (match) {
+			return parseInt(match[1], 10);
+		}
+	}
+	return null;
+}
+
+var colorsFreq = [0, 0, 0];
+/**
+ * return one of given possible colors (currently least used one)
+ * example: chooseColorNoFrom(2, 3) returns 2 or 3
+ *
+ * @param {Number[]} arguments: one or more numbers
+ *        assumes that  1 <= arguments[i] <= colorsFreq.length
+ * @returns {Number} Least used color number from arguments
+ * @globals colorsFreq
+ */
+function chooseColorNoFrom() {
+	// choose the color which is least used
+	var colorNo = arguments[0];
+	for (var i = 1; i < arguments.length; i++) {
+		if (colorsFreq[arguments[i]-1] < colorsFreq[colorNo-1]) {
+			colorNo = arguments[i];
+		}
+	}
+	colorsFreq[colorNo-1]++;
+	return colorNo;
+}
+
+/**
+ * given two neighbor <tr> elements, find color which would be different
+ * from color of both of neighbors; used to 3-color blame table
+ *
+ * @param {HTMLElement} tr_prev
+ * @param {HTMLElement} tr_next
+ * @returns {Number} color number N such that
+ * colorN != tr_prev.className && colorN != tr_next.className
+ */
+function findColorNo(tr_prev, tr_next) {
+	var color_prev = getColorNo(tr_prev);
+	var color_next = getColorNo(tr_next);
+
+
+	// neither of neighbors has color set
+	// THEN we can use any of 3 possible colors
+	if (!color_prev && !color_next) {
+		return chooseColorNoFrom(1,2,3);
+	}
+
+	// either both neighbors have the same color,
+	// or only one of neighbors have color set
+	// THEN we can use any color except given
+	var color;
+	if (color_prev === color_next) {
+		color = color_prev; // = color_next;
+	} else if (!color_prev) {
+		color = color_next;
+	} else if (!color_next) {
+		color = color_prev;
+	}
+	if (color) {
+		return chooseColorNoFrom((color % 3) + 1, ((color+1) % 3) + 1);
+	}
+
+	// neighbors have different colors
+	// THEN there is only one color left
+	return (3 - ((color_prev + color_next) % 3));
+}
+
+/* ............................................................ */
+/* coloring rows like 'blame' after 'blame_data' finishes */
+
+/**
+ * returns true if given row element (tr) is first in commit group
+ * to be used only after 'blame_data' finishes (after processing)
+ *
+ * @param {HTMLElement} tr: table row
+ * @returns {Boolean} true if TR is first in commit group
+ */
+function isStartOfGroup(tr) {
+	return tr.firstChild.className === 'sha1';
+}
+
+/**
+ * change colors to use zebra coloring (2 colors) instead of 3 colors
+ * concatenate neighbor commit groups belonging to the same commit
+ *
+ * @globals colorRe
+ */
+function fixColorsAndGroups() {
+	var colorClasses = ['light', 'dark'];
+	var linenum = 1;
+	var tr, prev_group;
+	var colorClass = 0;
+	var table =
+		document.getElementById('blame_table') ||
+		document.getElementsByTagName('table')[0];
+
+	while ((tr = document.getElementById('l'+linenum))) {
+	// index origin is 0, which is table header; start from 1
+	//while ((tr = table.rows[linenum])) { // <- it is slower
+		if (isStartOfGroup(tr, linenum, document)) {
+			if (prev_group &&
+			    prev_group.firstChild.firstChild.href ===
+			            tr.firstChild.firstChild.href) {
+				// we have to concatenate groups
+				var prev_rows = prev_group.firstChild.rowSpan || 1;
+				var curr_rows =         tr.firstChild.rowSpan || 1;
+				prev_group.firstChild.rowSpan = prev_rows + curr_rows;
+				//tr.removeChild(tr.firstChild);
+				tr.deleteCell(0); // DOM2 HTML way
+			} else {
+				colorClass = (colorClass + 1) % 2;
+				prev_group = tr;
+			}
+		}
+		var tr_class = tr.className;
+		tr.className = tr_class.replace(colorRe, colorClasses[colorClass]);
+		linenum++;
+	}
+}
+
+
+/* ============================================================ */
+/* main part: parsing response */
+
+/**
+ * Function called for each blame entry, as soon as it finishes.
+ * It updates page via DOM manipulation, adding sha1 info, etc.
+ *
+ * @param {Commit} commit: blamed commit
+ * @param {Object} group: object representing group of lines,
+ *                        which blame the same commit (blame entry)
+ *
+ * @globals blamedLines
+ */
+function handleLine(commit, group) {
+	/*
+	   This is the structure of the HTML fragment we are working
+	   with:
+
+	   <tr id="l123" class="">
+	     <td class="sha1" title=""><a href=""> </a></td>
+	     <td class="linenr"><a class="linenr" href="">123</a></td>
+	     <td class="pre"># times (my ext3 doesn&#39;t).</td>
+	   </tr>
+	*/
+
+	var resline = group.resline;
+
+	// format date and time string only once per commit
+	if (!commit.info) {
+		/* e.g. 'Kay Sievers, 2005-08-07 21:49:46 +0200' */
+		commit.info = commit.author + ', ' +
+			formatDateISOLocal(commit.authorTime, commit.authorTimezone);
+	}
+
+	// color depends on group of lines, not only on blamed commit
+	var colorNo = findColorNo(
+		document.getElementById('l'+(resline-1)),
+		document.getElementById('l'+(resline+group.numlines))
+	);
+
+	// loop over lines in commit group
+	for (var i = 0; i < group.numlines; i++, resline++) {
+		var tr = document.getElementById('l'+resline);
+		if (!tr) {
+			break;
+		}
+		/*
+			<tr id="l123" class="">
+			  <td class="sha1" title=""><a href=""> </a></td>
+			  <td class="linenr"><a class="linenr" href="">123</a></td>
+			  <td class="pre"># times (my ext3 doesn&#39;t).</td>
+			</tr>
+		*/
+		var td_sha1  = tr.firstChild;
+		var a_sha1   = td_sha1.firstChild;
+		var a_linenr = td_sha1.nextSibling.firstChild;
+
+		/* <tr id="l123" class=""> */
+		var tr_class = '';
+		if (colorNo !== null) {
+			tr_class = 'color'+colorNo;
+		}
+		if (commit.boundary) {
+			tr_class += ' boundary';
+		}
+		if (commit.nprevious === 0) {
+			tr_class += ' no-previous';
+		} else if (commit.nprevious > 1) {
+			tr_class += ' multiple-previous';
+		}
+		tr.className = tr_class;
+
+		/* <td class="sha1" title="?" rowspan="?"><a href="?">?</a></td> */
+		if (i === 0) {
+			td_sha1.title = commit.info;
+			td_sha1.rowSpan = group.numlines;
+
+			a_sha1.href = projectUrl + 'a=commit;h=' + commit.sha1;
+			if (a_sha1.firstChild) {
+				a_sha1.firstChild.data = commit.sha1.substr(0, 8);
+			} else {
+				a_sha1.appendChild(
+					document.createTextNode(commit.sha1.substr(0, 8)));
+			}
+			if (group.numlines >= 2) {
+				var fragment = document.createDocumentFragment();
+				var br   = document.createElement("br");
+				var match = commit.author.match(/\b([A-Z])\B/g);
+				if (match) {
+					var text = document.createTextNode(
+							match.join(''));
+				}
+				if (br && text) {
+					var elem = fragment || td_sha1;
+					elem.appendChild(br);
+					elem.appendChild(text);
+					if (fragment) {
+						td_sha1.appendChild(fragment);
+					}
+				}
+			}
+		} else {
+			//tr.removeChild(td_sha1); // DOM2 Core way
+			tr.deleteCell(0); // DOM2 HTML way
+		}
+
+		/* <td class="linenr"><a class="linenr" href="?">123</a></td> */
+		var linenr_commit =
+			('previous' in commit ? commit.previous : commit.sha1);
+		var linenr_filename =
+			('file_parent' in commit ? commit.file_parent : commit.filename);
+		a_linenr.href = projectUrl + 'a=blame_incremental' +
+			';hb=' + linenr_commit +
+			';f='  + encodeURIComponent(linenr_filename) +
+			'#l' + (group.srcline + i);
+
+		blamedLines++;
+
+		//updateProgressInfo();
+	}
+}
+
+// ----------------------------------------------------------------------
+
+/**#@+
+ * @constant
+ */
+var sha1Re = /^([0-9a-f]{40}) ([0-9]+) ([0-9]+) ([0-9]+)/;
+var infoRe = /^([a-z-]+) ?(.*)/;
+var endRe  = /^END ?([^ ]*) ?(.*)/;
+/**@-*/
+
+var curCommit = new Commit();
+var curGroup  = {};
+
+/**
+ * Parse output from 'git blame --incremental [...]', received via
+ * XMLHttpRequest from server (blamedataUrl), and call handleLine
+ * (which updates page) as soon as blame entry is completed.
+ *
+ * @param {String[]} lines: new complete lines from blamedata server
+ *
+ * @globals commits, curCommit, curGroup, t_interval_server, cmds_server
+ * @globals sha1Re, infoRe, endRe
+ */
+function processBlameLines(lines) {
+	var match;
+
+	for (var i = 0, len = lines.length; i < len; i++) {
+
+		if ((match = sha1Re.exec(lines[i]))) {
+			var sha1 = match[1];
+			var srcline  = parseInt(match[2], 10);
+			var resline  = parseInt(match[3], 10);
+			var numlines = parseInt(match[4], 10);
+
+			var c = commits[sha1];
+			if (!c) {
+				c = new Commit(sha1);
+				commits[sha1] = c;
+			}
+			curCommit = c;
+
+			curGroup.srcline = srcline;
+			curGroup.resline = resline;
+			curGroup.numlines = numlines;
+
+		} else if ((match = infoRe.exec(lines[i]))) {
+			var info = match[1];
+			var data = match[2];
+			switch (info) {
+			case 'filename':
+				curCommit.filename = unquote(data);
+				// 'filename' information terminates the entry
+				handleLine(curCommit, curGroup);
+				updateProgressInfo();
+				break;
+			case 'author':
+				curCommit.author = data;
+				break;
+			case 'author-time':
+				curCommit.authorTime = parseInt(data, 10);
+				break;
+			case 'author-tz':
+				curCommit.authorTimezone = data;
+				break;
+			case 'previous':
+				curCommit.nprevious++;
+				// store only first 'previous' header
+				if (!'previous' in curCommit) {
+					var parts = data.split(' ', 2);
+					curCommit.previous    = parts[0];
+					curCommit.file_parent = unquote(parts[1]);
+				}
+				break;
+			case 'boundary':
+				curCommit.boundary = true;
+				break;
+			} // end switch
+
+		} else if ((match = endRe.exec(lines[i]))) {
+			t_interval_server = match[1];
+			cmds_server = match[2];
+
+		} else if (lines[i] !== '') {
+			// malformed line
+
+		} // end if (match)
+
+	} // end for (lines)
+}
+
+/**
+ * Process new data and return pointer to end of processed part
+ *
+ * @param {String} unprocessed: new data (from nextReadPos)
+ * @param {Number} nextReadPos: end of last processed data
+ * @return {Number} end of processed data (new value for nextReadPos)
+ */
+function processData(unprocessed, nextReadPos) {
+	var lastLineEnd = unprocessed.lastIndexOf('\n');
+	if (lastLineEnd !== -1) {
+		var lines = unprocessed.substring(0, lastLineEnd).split('\n');
+		nextReadPos += lastLineEnd + 1 /* 1 == '\n'.length */;
+
+		processBlameLines(lines);
+	} // end if
+
+	return nextReadPos;
+}
+
+/**
+ * Handle XMLHttpRequest errors
+ *
+ * @param {XMLHttpRequest} xhr: XMLHttpRequest object
+ * @param {Number} [xhr.pollTimer] ID of the timeout to clear
+ *
+ * @globals commits
+ */
+function handleError(xhr) {
+	errorInfo('Server error: ' +
+		xhr.status + ' - ' + (xhr.statusText || 'Error contacting server'));
+
+	if (typeof xhr.pollTimer === "number") {
+		clearTimeout(xhr.pollTimer);
+		delete xhr.pollTimer;
+	}
+	commits = {}; // free memory
+}
+
+/**
+ * Called after XMLHttpRequest finishes (loads)
+ *
+ * @param {XMLHttpRequest} xhr: XMLHttpRequest object
+ * @param {Number} [xhr.pollTimer] ID of the timeout to clear
+ *
+ * @globals commits
+ */
+function responseLoaded(xhr) {
+	if (typeof xhr.pollTimer === "number") {
+		clearTimeout(xhr.pollTimer);
+		delete xhr.pollTimer;
+	}
+
+	fixColorsAndGroups();
+	writeTimeInterval();
+	commits = {}; // free memory
+}
+
+/**
+ * handler for XMLHttpRequest onreadystatechange event
+ * @see startBlame
+ *
+ * @param {XMLHttpRequest} xhr: XMLHttpRequest object
+ * @param {Number} xhr.prevDataLength: previous value of xhr.responseText.length
+ * @param {Number} xhr.nextReadPos: start of unread part of xhr.responseText
+ * @param {Number} [xhr.pollTimer] ID of the timeout (to reset or cancel)
+ * @param {Boolean} fromTimer: if handler was called from timer
+ */
+function handleResponse(xhr, fromTimer) {
+
+	/*
+	 * xhr.readyState
+	 *
+	 *  Value  Constant (W3C)    Description
+	 *  -------------------------------------------------------------------
+	 *  0      UNSENT            open() has not been called yet.
+	 *  1      OPENED            send() has not been called yet.
+	 *  2      HEADERS_RECEIVED  send() has been called, and headers
+	 *                           and status are available.
+	 *  3      LOADING           Downloading; responseText holds partial data.
+	 *  4      DONE              The operation is complete.
+	 */
+
+	if (xhr.readyState !== 4 && xhr.readyState !== 3) {
+		return;
+	}
+
+	// the server returned error
+	// try ... catch block is to work around bug in IE8
+	try {
+		if (xhr.readyState === 3 && xhr.status !== 200) {
+			return;
+		}
+	} catch (e) {
+		return;
+	}
+	if (xhr.readyState === 4 && xhr.status !== 200) {
+		handleError(xhr);
+		return;
+	}
+
+	// In konqueror xhr.responseText is sometimes null here...
+	if (xhr.responseText === null) {
+		return;
+	}
+
+
+	// extract new whole (complete) lines, and process them
+	if (xhr.prevDataLength !== xhr.responseText.length) {
+		xhr.prevDataLength = xhr.responseText.length;
+		var unprocessed = xhr.responseText.substring(xhr.nextReadPos);
+		xhr.nextReadPos = processData(unprocessed, xhr.nextReadPos);
+	}
+
+	// did we finish work?
+	if (xhr.readyState === 4) {
+		responseLoaded(xhr);
+		return;
+	}
+
+	// if we get from timer, we have to restart it
+	// otherwise onreadystatechange gives us partial response, timer not needed
+	if (fromTimer) {
+		setTimeout(function () {
+			handleResponse(xhr, true);
+		}, 1000);
+
+	} else if (typeof xhr.pollTimer === "number") {
+		clearTimeout(xhr.pollTimer);
+		delete xhr.pollTimer;
+	}
+}
+
+// ============================================================
+// ------------------------------------------------------------
+
+/**
+ * Incrementally update line data in blame_incremental view in gitweb.
+ *
+ * @param {String} blamedataUrl: URL to server script generating blame data.
+ * @param {String} bUrl: partial URL to project, used to generate links.
+ *
+ * Called from 'blame_incremental' view after loading table with
+ * file contents, a base for blame view.
+ *
+ * @globals t0, projectUrl, div_progress_bar, totalLines
+*/
+function startBlame(blamedataUrl, bUrl) {
+
+	var xhr = createRequestObject();
+	if (!xhr) {
+		errorInfo('ERROR: XMLHttpRequest not supported');
+		return;
+	}
+
+	t0 = new Date();
+	projectUrl = bUrl + (bUrl.indexOf('?') === -1 ? '?' : ';');
+	if ((div_progress_bar = document.getElementById('progress_bar'))) {
+		//div_progress_bar.setAttribute('style', 'width: 100%;');
+		div_progress_bar.style.cssText = 'width: 100%;';
+	}
+	totalLines = countLines();
+	updateProgressInfo();
+
+	/* add extra properties to xhr object to help processing response */
+	xhr.prevDataLength = -1;  // used to detect if we have new data
+	xhr.nextReadPos = 0;      // where unread part of response starts
+
+	xhr.onreadystatechange = function () {
+		handleResponse(xhr, false);
+	};
+
+	xhr.open('GET', blamedataUrl);
+	xhr.setRequestHeader('Accept', 'text/plain');
+	xhr.send(null);
+
+	// not all browsers call onreadystatechange event on each server flush
+	// poll response using timer every second to handle this issue
+	xhr.pollTimer = setTimeout(function () {
+		handleResponse(xhr, true);
+	}, 1000);
+}
+
+/* end of blame_incremental.js */
diff --git a/gitweb/static/js/javascript-detection.js b/gitweb/static/js/javascript-detection.js
new file mode 100644
index 0000000..fa2596f
--- /dev/null
+++ b/gitweb/static/js/javascript-detection.js
@@ -0,0 +1,43 @@
+// Copyright (C) 2007, Fredrik Kuivinen <frekui@gmail.com>
+//               2007, Petr Baudis <pasky@suse.cz>
+//          2008-2011, Jakub Narebski <jnareb@gmail.com>
+
+/**
+ * @fileOverview Detect if JavaScript is enabled, and pass it to server-side
+ * @license GPLv2 or later
+ */
+
+
+/* ============================================================ */
+/* Manipulating links */
+
+/**
+ * used to check if link has 'js' query parameter already (at end),
+ * and other reasons to not add 'js=1' param at the end of link
+ * @constant
+ */
+var jsExceptionsRe = /[;?]js=[01](#.*)?$/;
+
+/**
+ * Add '?js=1' or ';js=1' to the end of every link in the document
+ * that doesn't have 'js' query parameter set already.
+ *
+ * Links with 'js=1' lead to JavaScript version of given action, if it
+ * exists (currently there is only 'blame_incremental' for 'blame')
+ *
+ * To be used as `window.onload` handler
+ *
+ * @globals jsExceptionsRe
+ */
+function fixLinks() {
+	var allLinks = document.getElementsByTagName("a") || document.links;
+	for (var i = 0, len = allLinks.length; i < len; i++) {
+		var link = allLinks[i];
+		if (!jsExceptionsRe.test(link)) {
+			link.href = link.href.replace(/(#|$)/,
+				(link.href.indexOf('?') === -1 ? '?' : ';') + 'js=1$1');
+		}
+	}
+}
+
+/* end of javascript-detection.js */
diff --git a/gitweb/static/js/lib/common-lib.js b/gitweb/static/js/lib/common-lib.js
new file mode 100644
index 0000000..018bbb7
--- /dev/null
+++ b/gitweb/static/js/lib/common-lib.js
@@ -0,0 +1,224 @@
+// Copyright (C) 2007, Fredrik Kuivinen <frekui@gmail.com>
+//               2007, Petr Baudis <pasky@suse.cz>
+//          2008-2011, Jakub Narebski <jnareb@gmail.com>
+
+/**
+ * @fileOverview Generic JavaScript code (helper functions)
+ * @license GPLv2 or later
+ */
+
+
+/* ============================================================ */
+/* ............................................................ */
+/* Padding */
+
+/**
+ * pad INPUT on the left with STR that is assumed to have visible
+ * width of single character (for example nonbreakable spaces),
+ * to WIDTH characters
+ *
+ * example: padLeftStr(12, 3, '\u00A0') == '\u00A012'
+ *          ('\u00A0' is nonbreakable space)
+ *
+ * @param {Number|String} input: number to pad
+ * @param {Number} width: visible width of output
+ * @param {String} str: string to prefix to string, defaults to '\u00A0'
+ * @returns {String} INPUT prefixed with STR x (WIDTH - INPUT.length)
+ */
+function padLeftStr(input, width, str) {
+	var prefix = '';
+	if (typeof str === 'undefined') {
+		ch = '\u00A0'; // using '&nbsp;' doesn't work in all browsers
+	}
+
+	width -= input.toString().length;
+	while (width > 0) {
+		prefix += str;
+		width--;
+	}
+	return prefix + input;
+}
+
+/**
+ * Pad INPUT on the left to WIDTH, using given padding character CH,
+ * for example padLeft('a', 3, '_') is '__a'
+ *             padLeft(4, 2) is '04' (same as padLeft(4, 2, '0'))
+ *
+ * @param {String} input: input value converted to string.
+ * @param {Number} width: desired length of output.
+ * @param {String} ch: single character to prefix to string, defaults to '0'.
+ *
+ * @returns {String} Modified string, at least SIZE length.
+ */
+function padLeft(input, width, ch) {
+	var s = input + "";
+	if (typeof ch === 'undefined') {
+		ch = '0';
+	}
+
+	while (s.length < width) {
+		s = ch + s;
+	}
+	return s;
+}
+
+
+/* ............................................................ */
+/* Handling browser incompatibilities */
+
+/**
+ * Create XMLHttpRequest object in cross-browser way
+ * @returns XMLHttpRequest object, or null
+ */
+function createRequestObject() {
+	try {
+		return new XMLHttpRequest();
+	} catch (e) {}
+	try {
+		return window.createRequest();
+	} catch (e) {}
+	try {
+		return new ActiveXObject("Msxml2.XMLHTTP");
+	} catch (e) {}
+	try {
+		return new ActiveXObject("Microsoft.XMLHTTP");
+	} catch (e) {}
+
+	return null;
+}
+
+
+/**
+ * Insert rule giving specified STYLE to given SELECTOR at the end of
+ * first CSS stylesheet.
+ *
+ * @param {String} selector: CSS selector, e.g. '.class'
+ * @param {String} style: rule contents, e.g. 'background-color: red;'
+ */
+function addCssRule(selector, style) {
+	var stylesheet = document.styleSheets[0];
+
+	var theRules = [];
+	if (stylesheet.cssRules) {     // W3C way
+		theRules = stylesheet.cssRules;
+	} else if (stylesheet.rules) { // IE way
+		theRules = stylesheet.rules;
+	}
+
+	if (stylesheet.insertRule) {    // W3C way
+		stylesheet.insertRule(selector + ' { ' + style + ' }', theRules.length);
+	} else if (stylesheet.addRule) { // IE way
+		stylesheet.addRule(selector, style);
+	}
+}
+
+
+/* ............................................................ */
+/* Support for legacy browsers */
+
+/**
+ * Provides getElementsByClassName method, if there is no native
+ * implementation of this method.
+ *
+ * NOTE that there are limits and differences compared to native
+ * getElementsByClassName as defined by e.g.:
+ *   https://developer.mozilla.org/en/DOM/document.getElementsByClassName
+ *   http://www.whatwg.org/specs/web-apps/current-work/multipage/dom.html#dom-getelementsbyclassname
+ *   http://www.whatwg.org/specs/web-apps/current-work/multipage/dom.html#dom-document-getelementsbyclassname
+ *
+ * Namely, this implementation supports only single class name as
+ * argument and not set of space-separated tokens representing classes,
+ * it returns Array of nodes rather than live NodeList, and has
+ * additional optional argument where you can limit search to given tags
+ * (via getElementsByTagName).
+ *
+ * Based on
+ *   http://code.google.com/p/getelementsbyclassname/
+ *   http://www.dustindiaz.com/getelementsbyclass/
+ *   http://stackoverflow.com/questions/1818865/do-we-have-getelementsbyclassname-in-javascript
+ *
+ * See also http://ejohn.org/blog/getelementsbyclassname-speed-comparison/
+ *
+ * @param {String} class: name of _single_ class to find
+ * @param {String} [taghint] limit search to given tags
+ * @returns {Node[]} array of matching elements
+ */
+if (!('getElementsByClassName' in document)) {
+	document.getElementsByClassName = function (classname, taghint) {
+		taghint = taghint || "*";
+		var elements = (taghint === "*" && document.all) ?
+		               document.all :
+		               document.getElementsByTagName(taghint);
+		var pattern = new RegExp("(^|\\s)" + classname + "(\\s|$)");
+		var matches= [];
+		for (var i = 0, j = 0, n = elements.length; i < n; i++) {
+			var el= elements[i];
+			if (el.className && pattern.test(el.className)) {
+				// matches.push(el);
+				matches[j] = el;
+				j++;
+			}
+		}
+		return matches;
+	};
+} // end if
+
+
+/* ............................................................ */
+/* unquoting/unescaping filenames */
+
+/**#@+
+ * @constant
+ */
+var escCodeRe = /\\([^0-7]|[0-7]{1,3})/g;
+var octEscRe = /^[0-7]{1,3}$/;
+var maybeQuotedRe = /^\"(.*)\"$/;
+/**#@-*/
+
+/**
+ * unquote maybe C-quoted filename (as used by git, i.e. it is
+ * in double quotes '"' if there is any escape character used)
+ * e.g. 'aa' -> 'aa', '"a\ta"' -> 'a	a'
+ *
+ * @param {String} str: git-quoted string
+ * @returns {String} Unquoted and unescaped string
+ *
+ * @globals escCodeRe, octEscRe, maybeQuotedRe
+ */
+function unquote(str) {
+	function unq(seq) {
+		var es = {
+			// character escape codes, aka escape sequences (from C)
+			// replacements are to some extent JavaScript specific
+			t: "\t",   // tab            (HT, TAB)
+			n: "\n",   // newline        (NL)
+			r: "\r",   // return         (CR)
+			f: "\f",   // form feed      (FF)
+			b: "\b",   // backspace      (BS)
+			a: "\x07", // alarm (bell)   (BEL)
+			e: "\x1B", // escape         (ESC)
+			v: "\v"    // vertical tab   (VT)
+		};
+
+		if (seq.search(octEscRe) !== -1) {
+			// octal char sequence
+			return String.fromCharCode(parseInt(seq, 8));
+		} else if (seq in es) {
+			// C escape sequence, aka character escape code
+			return es[seq];
+		}
+		// quoted ordinary character
+		return seq;
+	}
+
+	var match = str.match(maybeQuotedRe);
+	if (match) {
+		str = match[1];
+		// perhaps str = eval('"'+str+'"'); would be enough?
+		str = str.replace(escCodeRe,
+			function (substr, p1, offset, s) { return unq(p1); });
+	}
+	return str;
+}
+
+/* end of common-lib.js */
diff --git a/gitweb/static/js/lib/cookies.js b/gitweb/static/js/lib/cookies.js
new file mode 100644
index 0000000..66b9a07
--- /dev/null
+++ b/gitweb/static/js/lib/cookies.js
@@ -0,0 +1,114 @@
+/**
+ * @fileOverview Accessing cookies from JavaScript
+ * @license GPLv2 or later
+ */
+
+/*
+ * Based on subsection "Cookies in JavaScript" of "Professional
+ * JavaScript for Web Developers" by Nicholas C. Zakas and cookie
+ * plugin from jQuery (dual licensed under the MIT and GPL licenses)
+ */
+
+
+/**
+ * Create a cookie with the given name and value,
+ * and other optional parameters.
+ *
+ * @example
+ *   setCookie('foo', 'bar'); // will be deleted when browser exits
+ *   setCookie('foo', 'bar', { expires: new Date(Date.parse('Jan 1, 2012')) });
+ *   setCookie('foo', 'bar', { expires: 7 }); // 7 days = 1 week
+ *   setCookie('foo', 'bar', { expires: 14, path: '/' });
+ *
+ * @param {String} sName:    Unique name of a cookie (letters, numbers, underscores).
+ * @param {String} sValue:   The string value stored in a cookie.
+ * @param {Object} [options] An object literal containing key/value pairs
+ *                           to provide optional cookie attributes.
+ * @param {String|Number|Date} [options.expires] Either literal string to be used as cookie expires,
+ *                            or an integer specifying the expiration date from now on in days,
+ *                            or a Date object to be used as cookie expiration date.
+ *                            If a negative value is specified or a date in the past),
+ *                            the cookie will be deleted.
+ *                            If set to null or omitted, the cookie will be a session cookie
+ *                            and will not be retained when the browser exits.
+ * @param {String} [options.path] Restrict access of a cookie to particular directory
+ *                               (default: path of page that created the cookie).
+ * @param {String} [options.domain] Override what web sites are allowed to access cookie
+ *                                  (default: domain of page that created the cookie).
+ * @param {Boolean} [options.secure] If true, the secure attribute of the cookie will be set
+ *                                   and the cookie would be accessible only from secure sites
+ *                                   (cookie transmission will require secure protocol like HTTPS).
+ */
+function setCookie(sName, sValue, options) {
+	options = options || {};
+	if (sValue === null) {
+		sValue = '';
+		option.expires = 'delete';
+	}
+
+	var sCookie = sName + '=' + encodeURIComponent(sValue);
+
+	if (options.expires) {
+		var oExpires = options.expires, sDate;
+		if (oExpires === 'delete') {
+			sDate = 'Thu, 01 Jan 1970 00:00:00 GMT';
+		} else if (typeof oExpires === 'string') {
+			sDate = oExpires;
+		} else {
+			var oDate;
+			if (typeof oExpires === 'number') {
+				oDate = new Date();
+				oDate.setTime(oDate.getTime() + (oExpires * 24 * 60 * 60 * 1000)); // days to ms
+			} else {
+				oDate = oExpires;
+			}
+			sDate = oDate.toGMTString();
+		}
+		sCookie += '; expires=' + sDate;
+	}
+
+	if (options.path) {
+		sCookie += '; path=' + (options.path);
+	}
+	if (options.domain) {
+		sCookie += '; domain=' + (options.domain);
+	}
+	if (options.secure) {
+		sCookie += '; secure';
+	}
+	document.cookie = sCookie;
+}
+
+/**
+ * Get the value of a cookie with the given name.
+ *
+ * @param {String} sName: Unique name of a cookie (letters, numbers, underscores)
+ * @returns {String|null} The string value stored in a cookie
+ */
+function getCookie(sName) {
+	var sRE = '(?:; )?' + sName + '=([^;]*);?';
+	var oRE = new RegExp(sRE);
+	if (oRE.test(document.cookie)) {
+		return decodeURIComponent(RegExp['$1']);
+	} else {
+		return null;
+	}
+}
+
+/**
+ * Delete cookie with given name
+ *
+ * @param {String} sName:    Unique name of a cookie (letters, numbers, underscores)
+ * @param {Object} [options] An object literal containing key/value pairs
+ *                           to provide optional cookie attributes.
+ * @param {String} [options.path]   Must be the same as when setting a cookie
+ * @param {String} [options.domain] Must be the same as when setting a cookie
+ */
+function deleteCookie(sName, options) {
+	options = options || {};
+	options.expires = 'delete';
+
+	setCookie(sName, '', options);
+}
+
+/* end of cookies.js */
diff --git a/gitweb/static/js/lib/datetime.js b/gitweb/static/js/lib/datetime.js
new file mode 100644
index 0000000..f78c60a
--- /dev/null
+++ b/gitweb/static/js/lib/datetime.js
@@ -0,0 +1,176 @@
+// Copyright (C) 2007, Fredrik Kuivinen <frekui@gmail.com>
+//               2007, Petr Baudis <pasky@suse.cz>
+//          2008-2011, Jakub Narebski <jnareb@gmail.com>
+
+/**
+ * @fileOverview Datetime manipulation: parsing and formatting
+ * @license GPLv2 or later
+ */
+
+
+/* ............................................................ */
+/* parsing and retrieving datetime related information */
+
+/**
+ * used to extract hours and minutes from timezone info, e.g '-0900'
+ * @constant
+ */
+var tzRe = /^([+\-])([0-9][0-9])([0-9][0-9])$/;
+
+/**
+ * convert numeric timezone +/-ZZZZ to offset from UTC in seconds
+ *
+ * @param {String} timezoneInfo: numeric timezone '(+|-)HHMM'
+ * @returns {Number} offset from UTC in seconds for timezone
+ *
+ * @globals tzRe
+ */
+function timezoneOffset(timezoneInfo) {
+	var match = tzRe.exec(timezoneInfo);
+	var tz_sign = (match[1] === '-' ? -1 : +1);
+	var tz_hour = parseInt(match[2],10);
+	var tz_min  = parseInt(match[3],10);
+
+	return tz_sign*(((tz_hour*60) + tz_min)*60);
+}
+
+/**
+ * return local (browser) timezone as offset from UTC in seconds
+ *
+ * @returns {Number} offset from UTC in seconds for local timezone
+ */
+function localTimezoneOffset() {
+	// getTimezoneOffset returns the time-zone offset from UTC,
+	// in _minutes_, for the current locale
+	return ((new Date()).getTimezoneOffset() * -60);
+}
+
+/**
+ * return local (browser) timezone as numeric timezone '(+|-)HHMM'
+ *
+ * @returns {String} locat timezone as -/+ZZZZ
+ */
+function localTimezoneInfo() {
+	var tzOffsetMinutes = (new Date()).getTimezoneOffset() * -1;
+
+	return formatTimezoneInfo(0, tzOffsetMinutes);
+}
+
+
+/**
+ * Parse RFC-2822 date into a Unix timestamp (into epoch)
+ *
+ * @param {String} date: date in RFC-2822 format, e.g. 'Thu, 21 Dec 2000 16:01:07 +0200'
+ * @returns {Number} epoch i.e. seconds since '00:00:00 1970-01-01 UTC'
+ */
+function parseRFC2822Date(date) {
+	// Date.parse accepts the IETF standard (RFC 1123 Section 5.2.14 and elsewhere)
+	// date syntax, which is defined in RFC 2822 (obsoletes RFC 822)
+	// and returns number of _milli_seconds since January 1, 1970, 00:00:00 UTC
+	return Date.parse(date) / 1000;
+}
+
+
+/* ............................................................ */
+/* formatting date */
+
+/**
+ * format timezone offset as numerical timezone '(+|-)HHMM' or '(+|-)HH:MM'
+ *
+ * @param {Number} hours:    offset in hours, e.g. 2 for '+0200'
+ * @param {Number} [minutes] offset in minutes, e.g. 30 for '-4030';
+ *                           it is split into hours if not 0 <= minutes < 60,
+ *                           for example 1200 would give '+0100';
+ *                           defaults to 0
+ * @param {String} [sep] separator between hours and minutes part,
+ *                       default is '', might be ':' for W3CDTF (rfc-3339)
+ * @returns {String} timezone in '(+|-)HHMM' or '(+|-)HH:MM' format
+ */
+function formatTimezoneInfo(hours, minutes, sep) {
+	minutes = minutes || 0; // to be able to use formatTimezoneInfo(hh)
+	sep = sep || ''; // default format is +/-ZZZZ
+
+	if (minutes < 0 || minutes > 59) {
+		hours = minutes > 0 ? Math.floor(minutes / 60) : Math.ceil(minutes / 60);
+		minutes = Math.abs(minutes - 60*hours); // sign of minutes is sign of hours
+		// NOTE: this works correctly because there is no UTC-00:30 timezone
+	}
+
+	var tzSign = hours >= 0 ? '+' : '-';
+	if (hours < 0) {
+		hours = -hours; // sign is stored in tzSign
+	}
+
+	return tzSign + padLeft(hours, 2, '0') + sep + padLeft(minutes, 2, '0');
+}
+
+/**
+ * translate 'utc' and 'local' to numerical timezone
+ * @param {String} timezoneInfo: might be 'utc' or 'local' (browser)
+ */
+function normalizeTimezoneInfo(timezoneInfo) {
+	switch (timezoneInfo) {
+	case 'utc':
+		return '+0000';
+	case 'local': // 'local' is browser timezone
+		return localTimezoneInfo();
+	}
+	return timezoneInfo;
+}
+
+
+/**
+ * return date in local time formatted in iso-8601 like format
+ * 'yyyy-mm-dd HH:MM:SS +/-ZZZZ' e.g. '2005-08-07 21:49:46 +0200'
+ *
+ * @param {Number} epoch: seconds since '00:00:00 1970-01-01 UTC'
+ * @param {String} timezoneInfo: numeric timezone '(+|-)HHMM'
+ * @returns {String} date in local time in iso-8601 like format
+ */
+function formatDateISOLocal(epoch, timezoneInfo) {
+	// date corrected by timezone
+	var localDate = new Date(1000 * (epoch +
+		timezoneOffset(timezoneInfo)));
+	var localDateStr = // e.g. '2005-08-07'
+		localDate.getUTCFullYear()                 + '-' +
+		padLeft(localDate.getUTCMonth()+1, 2, '0') + '-' +
+		padLeft(localDate.getUTCDate(),    2, '0');
+	var localTimeStr = // e.g. '21:49:46'
+		padLeft(localDate.getUTCHours(),   2, '0') + ':' +
+		padLeft(localDate.getUTCMinutes(), 2, '0') + ':' +
+		padLeft(localDate.getUTCSeconds(), 2, '0');
+
+	return localDateStr + ' ' + localTimeStr + ' ' + timezoneInfo;
+}
+
+/**
+ * return date in local time formatted in rfc-2822 format
+ * e.g. 'Thu, 21 Dec 2000 16:01:07 +0200'
+ *
+ * @param {Number} epoch: seconds since '00:00:00 1970-01-01 UTC'
+ * @param {String} timezoneInfo: numeric timezone '(+|-)HHMM'
+ * @param {Boolean} [padDay] e.g. 'Sun, 07 Aug' if true, 'Sun, 7 Aug' otherwise
+ * @returns {String} date in local time in rfc-2822 format
+ */
+function formatDateRFC2882(epoch, timezoneInfo, padDay) {
+	// A short textual representation of a month, three letters
+	var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
+	// A textual representation of a day, three letters
+	var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
+	// date corrected by timezone
+	var localDate = new Date(1000 * (epoch +
+		timezoneOffset(timezoneInfo)));
+	var localDateStr = // e.g. 'Sun, 7 Aug 2005' or 'Sun, 07 Aug 2005'
+		days[localDate.getUTCDay()] + ', ' +
+		(padDay ? padLeft(localDate.getUTCDate(),2,'0') : localDate.getUTCDate()) + ' ' +
+		months[localDate.getUTCMonth()] + ' ' +
+		localDate.getUTCFullYear();
+	var localTimeStr = // e.g. '21:49:46'
+		padLeft(localDate.getUTCHours(),   2, '0') + ':' +
+		padLeft(localDate.getUTCMinutes(), 2, '0') + ':' +
+		padLeft(localDate.getUTCSeconds(), 2, '0');
+
+	return localDateStr + ' ' + localTimeStr + ' ' + timezoneInfo;
+}
+
+/* end of datetime.js */
diff --git a/gpg-interface.c b/gpg-interface.c
new file mode 100644
index 0000000..0863c61
--- /dev/null
+++ b/gpg-interface.c
@@ -0,0 +1,140 @@
+#include "cache.h"
+#include "run-command.h"
+#include "strbuf.h"
+#include "gpg-interface.h"
+#include "sigchain.h"
+
+static char *configured_signing_key;
+static const char *gpg_program = "gpg";
+
+void set_signing_key(const char *key)
+{
+	free(configured_signing_key);
+	configured_signing_key = xstrdup(key);
+}
+
+int git_gpg_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, "user.signingkey")) {
+		set_signing_key(value);
+	}
+	if (!strcmp(var, "gpg.program")) {
+		if (!value)
+			return config_error_nonbool(var);
+		gpg_program = xstrdup(value);
+	}
+	return 0;
+}
+
+const char *get_signing_key(void)
+{
+	if (configured_signing_key)
+		return configured_signing_key;
+	return git_committer_info(IDENT_STRICT|IDENT_NO_DATE);
+}
+
+/*
+ * Create a detached signature for the contents of "buffer" and append
+ * it after "signature"; "buffer" and "signature" can be the same
+ * strbuf instance, which would cause the detached signature appended
+ * at the end.
+ */
+int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *signing_key)
+{
+	struct child_process gpg;
+	const char *args[4];
+	ssize_t len;
+	size_t i, j, bottom;
+
+	memset(&gpg, 0, sizeof(gpg));
+	gpg.argv = args;
+	gpg.in = -1;
+	gpg.out = -1;
+	args[0] = gpg_program;
+	args[1] = "-bsau";
+	args[2] = signing_key;
+	args[3] = NULL;
+
+	if (start_command(&gpg))
+		return error(_("could not run gpg."));
+
+	/*
+	 * When the username signingkey is bad, program could be terminated
+	 * because gpg exits without reading and then write gets SIGPIPE.
+	 */
+	sigchain_push(SIGPIPE, SIG_IGN);
+
+	if (write_in_full(gpg.in, buffer->buf, buffer->len) != buffer->len) {
+		close(gpg.in);
+		close(gpg.out);
+		finish_command(&gpg);
+		return error(_("gpg did not accept the data"));
+	}
+	close(gpg.in);
+
+	bottom = signature->len;
+	len = strbuf_read(signature, gpg.out, 1024);
+	close(gpg.out);
+
+	sigchain_pop(SIGPIPE);
+
+	if (finish_command(&gpg) || !len || len < 0)
+		return error(_("gpg failed to sign the data"));
+
+	/* Strip CR from the line endings, in case we are on Windows. */
+	for (i = j = bottom; i < signature->len; i++)
+		if (signature->buf[i] != '\r') {
+			if (i != j)
+				signature->buf[j] = signature->buf[i];
+			j++;
+		}
+	strbuf_setlen(signature, j);
+
+	return 0;
+}
+
+/*
+ * Run "gpg" to see if the payload matches the detached signature.
+ * gpg_output, when set, receives the diagnostic output from GPG.
+ */
+int verify_signed_buffer(const char *payload, size_t payload_size,
+			 const char *signature, size_t signature_size,
+			 struct strbuf *gpg_output)
+{
+	struct child_process gpg;
+	const char *args_gpg[] = {NULL, "--verify", "FILE", "-", NULL};
+	char path[PATH_MAX];
+	int fd, ret;
+
+	args_gpg[0] = gpg_program;
+	fd = git_mkstemp(path, PATH_MAX, ".git_vtag_tmpXXXXXX");
+	if (fd < 0)
+		return error("could not create temporary file '%s': %s",
+			     path, strerror(errno));
+	if (write_in_full(fd, signature, signature_size) < 0)
+		return error("failed writing detached signature to '%s': %s",
+			     path, strerror(errno));
+	close(fd);
+
+	memset(&gpg, 0, sizeof(gpg));
+	gpg.argv = args_gpg;
+	gpg.in = -1;
+	if (gpg_output)
+		gpg.err = -1;
+	args_gpg[2] = path;
+	if (start_command(&gpg)) {
+		unlink(path);
+		return error("could not run gpg.");
+	}
+
+	write_in_full(gpg.in, payload, payload_size);
+	close(gpg.in);
+
+	if (gpg_output)
+		strbuf_read(gpg_output, gpg.err, 0);
+	ret = finish_command(&gpg);
+
+	unlink_or_warn(path);
+
+	return ret;
+}
diff --git a/gpg-interface.h b/gpg-interface.h
new file mode 100644
index 0000000..b9c3608
--- /dev/null
+++ b/gpg-interface.h
@@ -0,0 +1,10 @@
+#ifndef GPG_INTERFACE_H
+#define GPG_INTERFACE_H
+
+extern int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *signing_key);
+extern int verify_signed_buffer(const char *payload, size_t payload_size, const char *signature, size_t signature_size, struct strbuf *gpg_output);
+extern int git_gpg_config(const char *, const char *, void *);
+extern void set_signing_key(const char *);
+extern const char *get_signing_key(void);
+
+#endif
diff --git a/graph.c b/graph.c
index e466770..7e0a099 100644
--- a/graph.c
+++ b/graph.c
@@ -8,17 +8,6 @@
 /* Internal API */
 
 /*
- * Output the next line for a graph.
- * This formats the next graph line into the specified strbuf.  It is not
- * terminated with a newline.
- *
- * Returns 1 if the line includes the current commit, and 0 otherwise.
- * graph_next_line() will return 1 exactly once for each time
- * graph_update() is called.
- */
-static int graph_next_line(struct git_graph *graph, struct strbuf *sb);
-
-/*
  * Output a padding line in the graph.
  * This is similar to graph_next_line().  However, it is guaranteed to
  * never print the current commit line.  Instead, if the commit line is
@@ -70,39 +59,28 @@
 	GRAPH_COLLAPSING
 };
 
-/*
- * The list of available column colors.
- */
-static char column_colors[][COLOR_MAXLEN] = {
-	GIT_COLOR_RED,
-	GIT_COLOR_GREEN,
-	GIT_COLOR_YELLOW,
-	GIT_COLOR_BLUE,
-	GIT_COLOR_MAGENTA,
-	GIT_COLOR_CYAN,
-	GIT_COLOR_BOLD GIT_COLOR_RED,
-	GIT_COLOR_BOLD GIT_COLOR_GREEN,
-	GIT_COLOR_BOLD GIT_COLOR_YELLOW,
-	GIT_COLOR_BOLD GIT_COLOR_BLUE,
-	GIT_COLOR_BOLD GIT_COLOR_MAGENTA,
-	GIT_COLOR_BOLD GIT_COLOR_CYAN,
-};
+static const char **column_colors;
+static unsigned short column_colors_max;
 
-#define COLUMN_COLORS_MAX (ARRAY_SIZE(column_colors))
-
-static const char *column_get_color_code(const struct column *c)
+void graph_set_column_colors(const char **colors, unsigned short colors_max)
 {
-	return column_colors[c->color];
+	column_colors = colors;
+	column_colors_max = colors_max;
+}
+
+static const char *column_get_color_code(unsigned short color)
+{
+	return column_colors[color];
 }
 
 static void strbuf_write_column(struct strbuf *sb, const struct column *c,
 				char col_char)
 {
-	if (c->color < COLUMN_COLORS_MAX)
-		strbuf_addstr(sb, column_get_color_code(c));
+	if (c->color < column_colors_max)
+		strbuf_addstr(sb, column_get_color_code(c->color));
 	strbuf_addch(sb, col_char);
-	if (c->color < COLUMN_COLORS_MAX)
-		strbuf_addstr(sb, GIT_COLOR_RESET);
+	if (c->color < column_colors_max)
+		strbuf_addstr(sb, column_get_color_code(column_colors_max));
 }
 
 struct git_graph {
@@ -211,9 +189,28 @@
 	unsigned short default_column_color;
 };
 
+static struct strbuf *diff_output_prefix_callback(struct diff_options *opt, void *data)
+{
+	struct git_graph *graph = data;
+	static struct strbuf msgbuf = STRBUF_INIT;
+
+	assert(opt);
+	assert(graph);
+
+	opt->output_prefix_length = graph->width;
+	strbuf_reset(&msgbuf);
+	graph_padding_line(graph, &msgbuf);
+	return &msgbuf;
+}
+
 struct git_graph *graph_init(struct rev_info *opt)
 {
 	struct git_graph *graph = xmalloc(sizeof(struct git_graph));
+
+	if (!column_colors)
+		graph_set_column_colors(column_colors_ansi,
+					column_colors_ansi_max);
+
 	graph->commit = NULL;
 	graph->revs = opt;
 	graph->num_parents = 0;
@@ -225,7 +222,12 @@
 	graph->num_columns = 0;
 	graph->num_new_columns = 0;
 	graph->mapping_size = 0;
-	graph->default_column_color = 0;
+	/*
+	 * Start the column color at the maximum value, since we'll
+	 * always increment it for the first commit we output.
+	 * This way we start at 0 for the first commit.
+	 */
+	graph->default_column_color = column_colors_max - 1;
 
 	/*
 	 * Allocate a reasonably large default number of columns
@@ -239,6 +241,14 @@
 	graph->mapping = xmalloc(sizeof(int) * 2 * graph->column_capacity);
 	graph->new_mapping = xmalloc(sizeof(int) * 2 * graph->column_capacity);
 
+	/*
+	 * The diff output prefix callback, with this we can make
+	 * all the diff output to align with the graph lines.
+	 */
+	opt->diffopt.output_prefix = diff_output_prefix_callback;
+	opt->diffopt.output_prefix_data = graph;
+	opt->diffopt.output_prefix_length = 0;
+
 	return graph;
 }
 
@@ -286,9 +296,10 @@
 	}
 
 	/*
-	 * Uninteresting and pruned commits won't be printed
+	 * Otherwise, use get_commit_action() to see if this commit is
+	 * interesting
 	 */
-	return (commit->object.flags & (UNINTERESTING | TREESAME)) ? 0 : 1;
+	return get_commit_action(graph->revs, commit) == commit_show;
 }
 
 static struct commit_list *next_interesting_parent(struct git_graph *graph,
@@ -339,8 +350,8 @@
 
 static unsigned short graph_get_current_column_color(const struct git_graph *graph)
 {
-	if (!DIFF_OPT_TST(&graph->revs->diffopt, COLOR_DIFF))
-		return COLUMN_COLORS_MAX;
+	if (!want_color(graph->revs->diffopt.use_color))
+		return column_colors_max;
 	return graph->default_column_color;
 }
 
@@ -350,7 +361,7 @@
 static void graph_increment_column_color(struct git_graph *graph)
 {
 	graph->default_column_color = (graph->default_column_color + 1) %
-		COLUMN_COLORS_MAX;
+		column_colors_max;
 }
 
 static unsigned short graph_find_commit_color(const struct git_graph *graph,
@@ -414,7 +425,7 @@
 		max_cols++;
 
 	/*
-	 * We added a column for the the current commit as part of
+	 * We added a column for the current commit as part of
 	 * graph->num_parents.  If the current commit was already in
 	 * graph->columns, then we have double counted it.
 	 */
@@ -499,11 +510,14 @@
 			     parent;
 			     parent = next_interesting_parent(graph, parent)) {
 				/*
-				 * If this is a merge increment the current
+				 * If this is a merge, or the start of a new
+				 * childless column, increment the current
 				 * color.
 				 */
-				if (graph->num_parents > 1)
+				if (graph->num_parents > 1 ||
+				    !is_commit_in_columns) {
 					graph_increment_column_color(graph);
+				}
 				graph_insert_into_new_columns(graph,
 							      parent->item,
 							      &mapping_idx);
@@ -766,22 +780,9 @@
 	}
 
 	/*
-	 * If revs->left_right is set, print '<' for commits that
-	 * come from the left side, and '>' for commits from the right
-	 * side.
+	 * get_revision_mark() handles all other cases without assert()
 	 */
-	if (graph->revs && graph->revs->left_right) {
-		if (graph->commit->object.flags & SYMMETRIC_LEFT)
-			strbuf_addch(sb, '<');
-		else
-			strbuf_addch(sb, '>');
-		return;
-	}
-
-	/*
-	 * Print '*' in all other cases
-	 */
-	strbuf_addch(sb, '*');
+	strbuf_addstr(sb, get_revision_mark(graph->revs, graph->commit));
 }
 
 /*
@@ -1115,7 +1116,7 @@
 		graph_update_state(graph, GRAPH_PADDING);
 }
 
-static int graph_next_line(struct git_graph *graph, struct strbuf *sb)
+int graph_next_line(struct git_graph *graph, struct strbuf *sb)
 {
 	switch (graph->state) {
 	case GRAPH_PADDING:
diff --git a/graph.h b/graph.h
index bc30d68..aff960c 100644
--- a/graph.h
+++ b/graph.h
@@ -5,8 +5,24 @@
 struct git_graph;
 
 /*
+ * Set up a custom scheme for column colors.
+ *
+ * The default column color scheme inserts ANSI color escapes to colorize
+ * the graph. The various color escapes are stored in an array of strings
+ * where each entry corresponds to a color, except for the last entry,
+ * which denotes the escape for resetting the color back to the default.
+ * When generating the graph, strings from this array are inserted before
+ * and after the various column characters.
+ *
+ * This function allows you to enable a custom array of color escapes.
+ * The 'colors_max' argument is the index of the last "reset" entry.
+ *
+ * This functions must be called BEFORE graph_init() is called.
+ */
+void graph_set_column_colors(const char **colors, unsigned short colors_max);
+
+/*
  * Create a new struct git_graph.
- * The graph should be freed with graph_release() when no longer needed.
  */
 struct git_graph *graph_init(struct rev_info *opt);
 
@@ -33,6 +49,17 @@
  */
 int graph_is_commit_finished(struct git_graph const *graph);
 
+/*
+ * Output the next line for a graph.
+ * This formats the next graph line into the specified strbuf.  It is not
+ * terminated with a newline.
+ *
+ * Returns 1 if the line includes the current commit, and 0 otherwise.
+ * graph_next_line() will return 1 exactly once for each time
+ * graph_update() is called.
+ */
+int graph_next_line(struct git_graph *graph, struct strbuf *sb);
+
 
 /*
  * graph_show_*: helper functions for printing to stdout
diff --git a/grep.c b/grep.c
index 5d162da..04e3ec6 100644
--- a/grep.c
+++ b/grep.c
@@ -3,37 +3,195 @@
 #include "userdiff.h"
 #include "xdiff-interface.h"
 
-void append_header_grep_pattern(struct grep_opt *opt, enum grep_header_field field, const char *pat)
+static struct grep_pat *create_grep_pat(const char *pat, size_t patlen,
+					const char *origin, int no,
+					enum grep_pat_token t,
+					enum grep_header_field field)
 {
 	struct grep_pat *p = xcalloc(1, sizeof(*p));
-	p->pattern = pat;
-	p->origin = "header";
-	p->no = 0;
-	p->token = GREP_PATTERN_HEAD;
+	p->pattern = xmemdupz(pat, patlen);
+	p->patternlen = patlen;
+	p->origin = origin;
+	p->no = no;
+	p->token = t;
 	p->field = field;
-	*opt->pattern_tail = p;
-	opt->pattern_tail = &p->next;
+	return p;
+}
+
+static void do_append_grep_pat(struct grep_pat ***tail, struct grep_pat *p)
+{
+	**tail = p;
+	*tail = &p->next;
 	p->next = NULL;
+
+	switch (p->token) {
+	case GREP_PATTERN: /* atom */
+	case GREP_PATTERN_HEAD:
+	case GREP_PATTERN_BODY:
+		for (;;) {
+			struct grep_pat *new_pat;
+			size_t len = 0;
+			char *cp = p->pattern + p->patternlen, *nl = NULL;
+			while (++len <= p->patternlen) {
+				if (*(--cp) == '\n') {
+					nl = cp;
+					break;
+				}
+			}
+			if (!nl)
+				break;
+			new_pat = create_grep_pat(nl + 1, len - 1, p->origin,
+						  p->no, p->token, p->field);
+			new_pat->next = p->next;
+			if (!p->next)
+				*tail = &new_pat->next;
+			p->next = new_pat;
+			*nl = '\0';
+			p->patternlen -= len;
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+void append_header_grep_pattern(struct grep_opt *opt,
+				enum grep_header_field field, const char *pat)
+{
+	struct grep_pat *p = create_grep_pat(pat, strlen(pat), "header", 0,
+					     GREP_PATTERN_HEAD, field);
+	do_append_grep_pat(&opt->header_tail, p);
 }
 
 void append_grep_pattern(struct grep_opt *opt, const char *pat,
 			 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;
+	append_grep_pat(opt, pat, strlen(pat), origin, no, t);
 }
 
-static int is_fixed(const char *s)
+void append_grep_pat(struct grep_opt *opt, const char *pat, size_t patlen,
+		     const char *origin, int no, enum grep_pat_token t)
 {
-	while (*s && !is_regex_special(*s))
-		s++;
-	return !*s;
+	struct grep_pat *p = create_grep_pat(pat, patlen, origin, no, t, 0);
+	do_append_grep_pat(&opt->pattern_tail, p);
+}
+
+struct grep_opt *grep_opt_dup(const struct grep_opt *opt)
+{
+	struct grep_pat *pat;
+	struct grep_opt *ret = xmalloc(sizeof(struct grep_opt));
+	*ret = *opt;
+
+	ret->pattern_list = NULL;
+	ret->pattern_tail = &ret->pattern_list;
+
+	for(pat = opt->pattern_list; pat != NULL; pat = pat->next)
+	{
+		if(pat->token == GREP_PATTERN_HEAD)
+			append_header_grep_pattern(ret, pat->field,
+						   pat->pattern);
+		else
+			append_grep_pat(ret, pat->pattern, pat->patternlen,
+					pat->origin, pat->no, pat->token);
+	}
+
+	return ret;
+}
+
+static NORETURN void compile_regexp_failed(const struct grep_pat *p,
+		const char *error)
+{
+	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;
+
+	die("%s'%s': %s", where, p->pattern, error);
+}
+
+#ifdef USE_LIBPCRE
+static void compile_pcre_regexp(struct grep_pat *p, const struct grep_opt *opt)
+{
+	const char *error;
+	int erroffset;
+	int options = PCRE_MULTILINE;
+
+	if (opt->ignore_case)
+		options |= PCRE_CASELESS;
+
+	p->pcre_regexp = pcre_compile(p->pattern, options, &error, &erroffset,
+			NULL);
+	if (!p->pcre_regexp)
+		compile_regexp_failed(p, error);
+
+	p->pcre_extra_info = pcre_study(p->pcre_regexp, 0, &error);
+	if (!p->pcre_extra_info && error)
+		die("%s", error);
+}
+
+static int pcrematch(struct grep_pat *p, const char *line, const char *eol,
+		regmatch_t *match, int eflags)
+{
+	int ovector[30], ret, flags = 0;
+
+	if (eflags & REG_NOTBOL)
+		flags |= PCRE_NOTBOL;
+
+	ret = pcre_exec(p->pcre_regexp, p->pcre_extra_info, line, eol - line,
+			0, flags, ovector, ARRAY_SIZE(ovector));
+	if (ret < 0 && ret != PCRE_ERROR_NOMATCH)
+		die("pcre_exec failed with error code %d", ret);
+	if (ret > 0) {
+		ret = 0;
+		match->rm_so = ovector[0];
+		match->rm_eo = ovector[1];
+	}
+
+	return ret;
+}
+
+static void free_pcre_regexp(struct grep_pat *p)
+{
+	pcre_free(p->pcre_regexp);
+	pcre_free(p->pcre_extra_info);
+}
+#else /* !USE_LIBPCRE */
+static void compile_pcre_regexp(struct grep_pat *p, const struct grep_opt *opt)
+{
+	die("cannot use Perl-compatible regexes when not compiled with USE_LIBPCRE");
+}
+
+static int pcrematch(struct grep_pat *p, const char *line, const char *eol,
+		regmatch_t *match, int eflags)
+{
+	return 1;
+}
+
+static void free_pcre_regexp(struct grep_pat *p)
+{
+}
+#endif /* !USE_LIBPCRE */
+
+static int is_fixed(const char *s, size_t len)
+{
+	size_t i;
+
+	/* regcomp cannot accept patterns with NULs so we
+	 * consider any pattern containing a NUL fixed.
+	 */
+	if (memchr(s, 0, len))
+		return 1;
+
+	for (i = 0; i < len; i++) {
+		if (is_regex_special(s[i]))
+			return 0;
+	}
+
+	return 1;
 }
 
 static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
@@ -41,28 +199,34 @@
 	int err;
 
 	p->word_regexp = opt->word_regexp;
+	p->ignore_case = opt->ignore_case;
 
-	if (opt->fixed || is_fixed(p->pattern))
+	if (opt->fixed || is_fixed(p->pattern, p->patternlen))
 		p->fixed = 1;
-	if (opt->regflags & REG_ICASE)
+	else
 		p->fixed = 0;
-	if (p->fixed)
+
+	if (p->fixed) {
+		if (opt->regflags & REG_ICASE || p->ignore_case)
+			p->kws = kwsalloc(tolower_trans_tbl);
+		else
+			p->kws = kwsalloc(NULL);
+		kwsincr(p->kws, p->pattern, p->patternlen);
+		kwsprep(p->kws);
 		return;
+	}
+
+	if (opt->pcre) {
+		compile_pcre_regexp(p, opt);
+		return;
+	}
 
 	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);
+		compile_regexp_failed(p, errbuf);
 	}
 }
 
@@ -168,12 +332,73 @@
 	return compile_pattern_or(list);
 }
 
+static struct grep_expr *grep_true_expr(void)
+{
+	struct grep_expr *z = xcalloc(1, sizeof(*z));
+	z->node = GREP_NODE_TRUE;
+	return z;
+}
+
+static struct grep_expr *grep_or_expr(struct grep_expr *left, struct grep_expr *right)
+{
+	struct grep_expr *z = xcalloc(1, sizeof(*z));
+	z->node = GREP_NODE_OR;
+	z->u.binary.left = left;
+	z->u.binary.right = right;
+	return z;
+}
+
+static struct grep_expr *prep_header_patterns(struct grep_opt *opt)
+{
+	struct grep_pat *p;
+	struct grep_expr *header_expr;
+	struct grep_expr *(header_group[GREP_HEADER_FIELD_MAX]);
+	enum grep_header_field fld;
+
+	if (!opt->header_list)
+		return NULL;
+
+	for (p = opt->header_list; p; p = p->next) {
+		if (p->token != GREP_PATTERN_HEAD)
+			die("bug: a non-header pattern in grep header list.");
+		if (p->field < 0 || GREP_HEADER_FIELD_MAX <= p->field)
+			die("bug: unknown header field %d", p->field);
+		compile_regexp(p, opt);
+	}
+
+	for (fld = 0; fld < GREP_HEADER_FIELD_MAX; fld++)
+		header_group[fld] = NULL;
+
+	for (p = opt->header_list; p; p = p->next) {
+		struct grep_expr *h;
+		struct grep_pat *pp = p;
+
+		h = compile_pattern_atom(&pp);
+		if (!h || pp != p->next)
+			die("bug: malformed header expr");
+		if (!header_group[p->field]) {
+			header_group[p->field] = h;
+			continue;
+		}
+		header_group[p->field] = grep_or_expr(h, header_group[p->field]);
+	}
+
+	header_expr = NULL;
+
+	for (fld = 0; fld < GREP_HEADER_FIELD_MAX; fld++) {
+		if (!header_group[fld])
+			continue;
+		if (!header_expr)
+			header_expr = grep_true_expr();
+		header_expr = grep_or_expr(header_group[fld], header_expr);
+	}
+	return header_expr;
+}
+
 void compile_grep_patterns(struct grep_opt *opt)
 {
 	struct grep_pat *p;
-
-	if (opt->all_match)
-		opt->extended = 1;
+	struct grep_expr *header_expr = prep_header_patterns(opt);
 
 	for (p = opt->pattern_list; p; p = p->next) {
 		switch (p->token) {
@@ -188,22 +413,32 @@
 		}
 	}
 
-	if (!opt->extended)
+	if (opt->all_match || header_expr)
+		opt->extended = 1;
+	else if (!opt->extended)
 		return;
 
-	/* Then bundle them up in an expression.
-	 * A classic recursive descent parser would do.
-	 */
 	p = opt->pattern_list;
 	if (p)
 		opt->pattern_expression = compile_pattern_expr(&p);
 	if (p)
 		die("incomplete pattern expression: %s", p->pattern);
+
+	if (!header_expr)
+		return;
+
+	if (!opt->pattern_expression)
+		opt->pattern_expression = header_expr;
+	else
+		opt->pattern_expression = grep_or_expr(opt->pattern_expression,
+						       header_expr);
+	opt->all_match = 1;
 }
 
 static void free_pattern_expr(struct grep_expr *x)
 {
 	switch (x->node) {
+	case GREP_NODE_TRUE:
 	case GREP_NODE_ATOM:
 		break;
 	case GREP_NODE_NOT:
@@ -228,7 +463,13 @@
 		case GREP_PATTERN: /* atom */
 		case GREP_PATTERN_HEAD:
 		case GREP_PATTERN_BODY:
-			regfree(&p->regexp);
+			if (p->kws)
+				kwsfree(p->kws);
+			else if (p->pcre_regexp)
+				free_pcre_regexp(p);
+			else
+				regfree(&p->regexp);
+			free(p->pattern);
 			break;
 		default:
 			break;
@@ -257,25 +498,72 @@
 	return isalnum(ch) || ch == '_';
 }
 
-static void show_name(struct grep_opt *opt, const char *name)
+static void output_color(struct grep_opt *opt, const void *data, size_t size,
+			 const char *color)
 {
-	printf("%s%c", name, opt->null_following_name ? '\0' : '\n');
+	if (want_color(opt->color) && color && color[0]) {
+		opt->output(opt, color, strlen(color));
+		opt->output(opt, data, size);
+		opt->output(opt, GIT_COLOR_RESET, strlen(GIT_COLOR_RESET));
+	} else
+		opt->output(opt, data, size);
 }
 
-static int fixmatch(const char *pattern, char *line, regmatch_t *match)
+static void output_sep(struct grep_opt *opt, char sign)
 {
-	char *hit = strstr(line, pattern);
-	if (!hit) {
+	if (opt->null_following_name)
+		opt->output(opt, "\0", 1);
+	else
+		output_color(opt, &sign, 1, opt->color_sep);
+}
+
+static void show_name(struct grep_opt *opt, const char *name)
+{
+	output_color(opt, name, strlen(name), opt->color_filename);
+	opt->output(opt, opt->null_following_name ? "\0" : "\n", 1);
+}
+
+static int fixmatch(struct grep_pat *p, char *line, char *eol,
+		    regmatch_t *match)
+{
+	struct kwsmatch kwsm;
+	size_t offset = kwsexec(p->kws, line, eol - line, &kwsm);
+	if (offset == -1) {
 		match->rm_so = match->rm_eo = -1;
 		return REG_NOMATCH;
-	}
-	else {
-		match->rm_so = hit - line;
-		match->rm_eo = match->rm_so + strlen(pattern);
+	} else {
+		match->rm_so = offset;
+		match->rm_eo = match->rm_so + kwsm.size[0];
 		return 0;
 	}
 }
 
+static int regmatch(const regex_t *preg, char *line, char *eol,
+		    regmatch_t *match, int eflags)
+{
+#ifdef REG_STARTEND
+	match->rm_so = 0;
+	match->rm_eo = eol - line;
+	eflags |= REG_STARTEND;
+#endif
+	return regexec(preg, line, 1, match, eflags);
+}
+
+static int patmatch(struct grep_pat *p, char *line, char *eol,
+		    regmatch_t *match, int eflags)
+{
+	int hit;
+
+	if (p->fixed)
+		hit = !fixmatch(p, line, eol, match);
+	else if (p->pcre_regexp)
+		hit = !pcrematch(p, line, eol, match, eflags);
+	else
+		hit = !regmatch(&p->regexp, line, eol, match, eflags);
+
+	return hit;
+}
+
 static int strip_timestamp(char *bol, char **eol_p)
 {
 	char *eol = *eol_p;
@@ -325,10 +613,7 @@
 	}
 
  again:
-	if (p->fixed)
-		hit = !fixmatch(p->pattern, bol, pmatch);
-	else
-		hit = !regexec(&p->regexp, bol, 1, pmatch, eflags);
+	hit = patmatch(p, bol, eol, pmatch, eflags);
 
 	if (hit && p->word_regexp) {
 		if ((pmatch[0].rm_so < 0) ||
@@ -388,6 +673,9 @@
 	if (!x)
 		die("Not a valid grep expression");
 	switch (x->node) {
+	case GREP_NODE_TRUE:
+		h = 1;
+		break;
 	case GREP_NODE_ATOM:
 		h = match_one_pattern(x->u.atom, bol, eol, ctx, &match, 0);
 		break;
@@ -490,52 +778,114 @@
 		      const char *name, unsigned lno, char sign)
 {
 	int rest = eol - bol;
+	char *line_color = NULL;
 
-	if (opt->pre_context || opt->post_context) {
+	if (opt->file_break && opt->last_shown == 0) {
+		if (opt->show_hunk_mark)
+			opt->output(opt, "\n", 1);
+	} else if (opt->pre_context || opt->post_context || opt->funcbody) {
 		if (opt->last_shown == 0) {
-			if (opt->show_hunk_mark)
-				fputs("--\n", stdout);
-			else
-				opt->show_hunk_mark = 1;
-		} else if (lno > opt->last_shown + 1)
-			fputs("--\n", stdout);
+			if (opt->show_hunk_mark) {
+				output_color(opt, "--", 2, opt->color_sep);
+				opt->output(opt, "\n", 1);
+			}
+		} else if (lno > opt->last_shown + 1) {
+			output_color(opt, "--", 2, opt->color_sep);
+			opt->output(opt, "\n", 1);
+		}
+	}
+	if (opt->heading && opt->last_shown == 0) {
+		output_color(opt, name, strlen(name), opt->color_filename);
+		opt->output(opt, "\n", 1);
 	}
 	opt->last_shown = lno;
 
-	if (opt->null_following_name)
-		sign = '\0';
-	if (opt->pathname)
-		printf("%s%c", name, sign);
-	if (opt->linenum)
-		printf("%d%c", lno, sign);
+	if (!opt->heading && opt->pathname) {
+		output_color(opt, name, strlen(name), opt->color_filename);
+		output_sep(opt, sign);
+	}
+	if (opt->linenum) {
+		char buf[32];
+		snprintf(buf, sizeof(buf), "%d", lno);
+		output_color(opt, buf, strlen(buf), opt->color_lineno);
+		output_sep(opt, sign);
+	}
 	if (opt->color) {
 		regmatch_t match;
 		enum grep_context ctx = GREP_CONTEXT_BODY;
 		int ch = *eol;
 		int eflags = 0;
 
+		if (sign == ':')
+			line_color = opt->color_selected;
+		else if (sign == '-')
+			line_color = opt->color_context;
+		else if (sign == '=')
+			line_color = opt->color_function;
 		*eol = '\0';
 		while (next_match(opt, bol, eol, ctx, &match, eflags)) {
 			if (match.rm_so == match.rm_eo)
 				break;
-			printf("%.*s%s%.*s%s",
-			       (int)match.rm_so, bol,
-			       opt->color_match,
-			       (int)(match.rm_eo - match.rm_so), bol + match.rm_so,
-			       GIT_COLOR_RESET);
+
+			output_color(opt, bol, match.rm_so, line_color);
+			output_color(opt, bol + match.rm_so,
+				     match.rm_eo - match.rm_so,
+				     opt->color_match);
 			bol += match.rm_eo;
 			rest -= match.rm_eo;
 			eflags = REG_NOTBOL;
 		}
 		*eol = ch;
 	}
-	printf("%.*s\n", rest, bol);
+	output_color(opt, bol, rest, line_color);
+	opt->output(opt, "\n", 1);
 }
 
-static int match_funcname(struct grep_opt *opt, char *bol, char *eol)
+#ifndef NO_PTHREADS
+int grep_use_locks;
+
+/*
+ * This lock protects access to the gitattributes machinery, which is
+ * not thread-safe.
+ */
+pthread_mutex_t grep_attr_mutex;
+
+static inline void grep_attr_lock(void)
+{
+	if (grep_use_locks)
+		pthread_mutex_lock(&grep_attr_mutex);
+}
+
+static inline void grep_attr_unlock(void)
+{
+	if (grep_use_locks)
+		pthread_mutex_unlock(&grep_attr_mutex);
+}
+
+/*
+ * Same as git_attr_mutex, but protecting the thread-unsafe object db access.
+ */
+pthread_mutex_t grep_read_mutex;
+
+#else
+#define grep_attr_lock()
+#define grep_attr_unlock()
+#endif
+
+static int match_funcname(struct grep_opt *opt, struct grep_source *gs, char *bol, char *eol)
 {
 	xdemitconf_t *xecfg = opt->priv;
-	if (xecfg && xecfg->find_func) {
+	if (xecfg && !xecfg->find_func) {
+		grep_source_load_driver(gs);
+		if (gs->driver->funcname.pattern) {
+			const struct userdiff_funcname *pe = &gs->driver->funcname;
+			xdiff_set_find_func(xecfg, pe->pattern, pe->cflags);
+		} else {
+			xecfg = opt->priv = NULL;
+		}
+	}
+
+	if (xecfg) {
 		char buf[1];
 		return xecfg->find_func(bol, eol - bol, buf, 1,
 					xecfg->find_func_priv) >= 0;
@@ -548,31 +898,34 @@
 	return 0;
 }
 
-static void show_funcname_line(struct grep_opt *opt, const char *name,
-			       char *buf, char *bol, unsigned lno)
+static void show_funcname_line(struct grep_opt *opt, struct grep_source *gs,
+			       char *bol, unsigned lno)
 {
-	while (bol > buf) {
+	while (bol > gs->buf) {
 		char *eol = --bol;
 
-		while (bol > buf && bol[-1] != '\n')
+		while (bol > gs->buf && bol[-1] != '\n')
 			bol--;
 		lno--;
 
 		if (lno <= opt->last_shown)
 			break;
 
-		if (match_funcname(opt, bol, eol)) {
-			show_line(opt, bol, eol, name, lno, '=');
+		if (match_funcname(opt, gs, bol, eol)) {
+			show_line(opt, bol, eol, gs->name, lno, '=');
 			break;
 		}
 	}
 }
 
-static void show_pre_context(struct grep_opt *opt, const char *name, char *buf,
-			     char *bol, unsigned lno)
+static void show_pre_context(struct grep_opt *opt, struct grep_source *gs,
+			     char *bol, char *end, unsigned lno)
 {
 	unsigned cur = lno, from = 1, funcname_lno = 0;
-	int funcname_needed = opt->funcname;
+	int funcname_needed = !!opt->funcname;
+
+	if (opt->funcbody && !match_funcname(opt, gs, bol, end))
+		funcname_needed = 2;
 
 	if (opt->pre_context < lno)
 		from = lno - opt->pre_context;
@@ -580,13 +933,14 @@
 		from = opt->last_shown + 1;
 
 	/* Rewind. */
-	while (bol > buf && cur > from) {
+	while (bol > gs->buf &&
+	       cur > (funcname_needed == 2 ? opt->last_shown + 1 : from)) {
 		char *eol = --bol;
 
-		while (bol > buf && bol[-1] != '\n')
+		while (bol > gs->buf && bol[-1] != '\n')
 			bol--;
 		cur--;
-		if (funcname_needed && match_funcname(opt, bol, eol)) {
+		if (funcname_needed && match_funcname(opt, gs, bol, eol)) {
 			funcname_lno = cur;
 			funcname_needed = 0;
 		}
@@ -594,7 +948,7 @@
 
 	/* We need to look even further back to find a function signature. */
 	if (opt->funcname && funcname_needed)
-		show_funcname_line(opt, name, buf, bol, cur);
+		show_funcname_line(opt, gs, bol, cur);
 
 	/* Back forward. */
 	while (cur < lno) {
@@ -602,54 +956,148 @@
 
 		while (*eol != '\n')
 			eol++;
-		show_line(opt, bol, eol, name, cur, sign);
+		show_line(opt, bol, eol, gs->name, cur, sign);
 		bol = eol + 1;
 		cur++;
 	}
 }
 
-static int grep_buffer_1(struct grep_opt *opt, const char *name,
-			 char *buf, unsigned long size, int collect_hits)
+static int should_lookahead(struct grep_opt *opt)
 {
-	char *bol = buf;
-	unsigned long left = size;
+	struct grep_pat *p;
+
+	if (opt->extended)
+		return 0; /* punt for too complex stuff */
+	if (opt->invert)
+		return 0;
+	for (p = opt->pattern_list; p; p = p->next) {
+		if (p->token != GREP_PATTERN)
+			return 0; /* punt for "header only" and stuff */
+	}
+	return 1;
+}
+
+static int look_ahead(struct grep_opt *opt,
+		      unsigned long *left_p,
+		      unsigned *lno_p,
+		      char **bol_p)
+{
+	unsigned lno = *lno_p;
+	char *bol = *bol_p;
+	struct grep_pat *p;
+	char *sp, *last_bol;
+	regoff_t earliest = -1;
+
+	for (p = opt->pattern_list; p; p = p->next) {
+		int hit;
+		regmatch_t m;
+
+		hit = patmatch(p, bol, bol + *left_p, &m, 0);
+		if (!hit || m.rm_so < 0 || m.rm_eo < 0)
+			continue;
+		if (earliest < 0 || m.rm_so < earliest)
+			earliest = m.rm_so;
+	}
+
+	if (earliest < 0) {
+		*bol_p = bol + *left_p;
+		*left_p = 0;
+		return 1;
+	}
+	for (sp = bol + earliest; bol < sp && sp[-1] != '\n'; sp--)
+		; /* find the beginning of the line */
+	last_bol = sp;
+
+	for (sp = bol; sp < last_bol; sp++) {
+		if (*sp == '\n')
+			lno++;
+	}
+	*left_p -= last_bol - bol;
+	*bol_p = last_bol;
+	*lno_p = lno;
+	return 0;
+}
+
+static void std_output(struct grep_opt *opt, const void *buf, size_t size)
+{
+	fwrite(buf, size, 1, stdout);
+}
+
+static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int collect_hits)
+{
+	char *bol;
+	unsigned long left;
 	unsigned lno = 1;
 	unsigned last_hit = 0;
 	int binary_match_only = 0;
 	unsigned count = 0;
+	int try_lookahead = 0;
+	int show_function = 0;
 	enum grep_context ctx = GREP_CONTEXT_HEAD;
 	xdemitconf_t xecfg;
 
+	if (!opt->output)
+		opt->output = std_output;
+
+	if (opt->pre_context || opt->post_context || opt->file_break ||
+	    opt->funcbody) {
+		/* Show hunk marks, except for the first file. */
+		if (opt->last_shown)
+			opt->show_hunk_mark = 1;
+		/*
+		 * If we're using threads then we can't easily identify
+		 * the first file.  Always put hunk marks in that case
+		 * and skip the very first one later in work_done().
+		 */
+		if (opt->output != std_output)
+			opt->show_hunk_mark = 1;
+	}
 	opt->last_shown = 0;
 
-	if (buffer_is_binary(buf, size)) {
-		switch (opt->binary) {
-		case GREP_BINARY_DEFAULT:
+	switch (opt->binary) {
+	case GREP_BINARY_DEFAULT:
+		if (grep_source_is_binary(gs))
 			binary_match_only = 1;
-			break;
-		case GREP_BINARY_NOMATCH:
+		break;
+	case GREP_BINARY_NOMATCH:
+		if (grep_source_is_binary(gs))
 			return 0; /* Assume unmatch */
-			break;
-		default:
-			break;
-		}
+		break;
+	case GREP_BINARY_TEXT:
+		break;
+	default:
+		die("bug: unknown binary handling mode");
 	}
 
 	memset(&xecfg, 0, sizeof(xecfg));
-	if (opt->funcname && !opt->unmatch_name_only && !opt->status_only &&
-	    !opt->name_only && !binary_match_only && !collect_hits) {
-		struct userdiff_driver *drv = userdiff_find_by_path(name);
-		if (drv && drv->funcname.pattern) {
-			const struct userdiff_funcname *pe = &drv->funcname;
-			xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);
-			opt->priv = &xecfg;
-		}
-	}
+	opt->priv = &xecfg;
 
+	try_lookahead = should_lookahead(opt);
+
+	if (grep_source_load(gs) < 0)
+		return 0;
+
+	bol = gs->buf;
+	left = gs->size;
 	while (left) {
 		char *eol, ch;
 		int hit;
 
+		/*
+		 * look_ahead() skips quickly to the line that possibly
+		 * has the next hit; don't call it if we need to do
+		 * something more than just skipping the current line
+		 * in response to an unmatch for the current line.  E.g.
+		 * inside a post-context window, we will show the current
+		 * line as a context around the previous hit when it
+		 * doesn't hit.
+		 */
+		if (try_lookahead
+		    && !(last_hit
+			 && (show_function ||
+			     lno <= last_hit + opt->post_context))
+		    && look_ahead(opt, &left, &lno, &bol))
+			break;
 		eol = end_of_line(bol, &left);
 		ch = *eol;
 		*eol = 0;
@@ -678,34 +1126,40 @@
 			count++;
 			if (opt->status_only)
 				return 1;
-			if (binary_match_only) {
-				printf("Binary file %s matches\n", name);
+			if (opt->name_only) {
+				show_name(opt, gs->name);
 				return 1;
 			}
-			if (opt->name_only) {
-				show_name(opt, name);
+			if (opt->count)
+				goto next_line;
+			if (binary_match_only) {
+				opt->output(opt, "Binary file ", 12);
+				output_color(opt, gs->name, strlen(gs->name),
+					     opt->color_filename);
+				opt->output(opt, " matches\n", 9);
 				return 1;
 			}
 			/* Hit at this line.  If we haven't shown the
 			 * pre-context lines, we would need to show them.
-			 * When asked to do "count", this still show
-			 * the context which is nonsense, but the user
-			 * deserves to get that ;-).
 			 */
-			if (opt->pre_context)
-				show_pre_context(opt, name, buf, bol, lno);
+			if (opt->pre_context || opt->funcbody)
+				show_pre_context(opt, gs, bol, eol, lno);
 			else if (opt->funcname)
-				show_funcname_line(opt, name, buf, bol, lno);
-			if (!opt->count)
-				show_line(opt, bol, eol, name, lno, ':');
+				show_funcname_line(opt, gs, bol, lno);
+			show_line(opt, bol, eol, gs->name, lno, ':');
 			last_hit = lno;
+			if (opt->funcbody)
+				show_function = 1;
+			goto next_line;
 		}
-		else if (last_hit &&
-			 lno <= last_hit + opt->post_context) {
+		if (show_function && match_funcname(opt, gs, bol, eol))
+			show_function = 0;
+		if (show_function ||
+		    (last_hit && lno <= last_hit + opt->post_context)) {
 			/* If the last hit is within the post context,
 			 * we need to show this line.
 			 */
-			show_line(opt, bol, eol, name, lno, '-');
+			show_line(opt, bol, eol, gs->name, lno, '-');
 		}
 
 	next_line:
@@ -723,7 +1177,7 @@
 		return 0;
 	if (opt->unmatch_name_only) {
 		/* We did not see any hit, so we want to show this */
-		show_name(opt, name);
+		show_name(opt, gs->name);
 		return 1;
 	}
 
@@ -735,9 +1189,14 @@
 	 * which feels mostly useless but sometimes useful.  Maybe
 	 * make it another option?  For now suppress them.
 	 */
-	if (opt->count && count)
-		printf("%s%c%u\n", name,
-		       opt->null_following_name ? '\0' : ':', count);
+	if (opt->count && count) {
+		char buf[32];
+		output_color(opt, gs->name, strlen(gs->name), opt->color_filename);
+		output_sep(opt, ':');
+		snprintf(buf, sizeof(buf), "%u\n", count);
+		opt->output(opt, buf, strlen(buf));
+		return 1;
+	}
 	return !!last_hit;
 }
 
@@ -767,23 +1226,174 @@
 	}
 }
 
-int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long size)
+int grep_source(struct grep_opt *opt, struct grep_source *gs)
 {
 	/*
 	 * we do not have to do the two-pass grep when we do not check
 	 * buffer-wide "all-match".
 	 */
 	if (!opt->all_match)
-		return grep_buffer_1(opt, name, buf, size, 0);
+		return grep_source_1(opt, gs, 0);
 
 	/* Otherwise the toplevel "or" terms hit a bit differently.
 	 * We first clear hit markers from them.
 	 */
 	clr_hit_marker(opt->pattern_expression);
-	grep_buffer_1(opt, name, buf, size, 1);
+	grep_source_1(opt, gs, 1);
 
 	if (!chk_hit_marker(opt->pattern_expression))
 		return 0;
 
-	return grep_buffer_1(opt, name, buf, size, 0);
+	return grep_source_1(opt, gs, 0);
+}
+
+int grep_buffer(struct grep_opt *opt, char *buf, unsigned long size)
+{
+	struct grep_source gs;
+	int r;
+
+	grep_source_init(&gs, GREP_SOURCE_BUF, NULL, NULL);
+	gs.buf = buf;
+	gs.size = size;
+
+	r = grep_source(opt, &gs);
+
+	grep_source_clear(&gs);
+	return r;
+}
+
+void grep_source_init(struct grep_source *gs, enum grep_source_type type,
+		      const char *name, const void *identifier)
+{
+	gs->type = type;
+	gs->name = name ? xstrdup(name) : NULL;
+	gs->buf = NULL;
+	gs->size = 0;
+	gs->driver = NULL;
+
+	switch (type) {
+	case GREP_SOURCE_FILE:
+		gs->identifier = xstrdup(identifier);
+		break;
+	case GREP_SOURCE_SHA1:
+		gs->identifier = xmalloc(20);
+		memcpy(gs->identifier, identifier, 20);
+		break;
+	case GREP_SOURCE_BUF:
+		gs->identifier = NULL;
+	}
+}
+
+void grep_source_clear(struct grep_source *gs)
+{
+	free(gs->name);
+	gs->name = NULL;
+	free(gs->identifier);
+	gs->identifier = NULL;
+	grep_source_clear_data(gs);
+}
+
+void grep_source_clear_data(struct grep_source *gs)
+{
+	switch (gs->type) {
+	case GREP_SOURCE_FILE:
+	case GREP_SOURCE_SHA1:
+		free(gs->buf);
+		gs->buf = NULL;
+		gs->size = 0;
+		break;
+	case GREP_SOURCE_BUF:
+		/* leave user-provided buf intact */
+		break;
+	}
+}
+
+static int grep_source_load_sha1(struct grep_source *gs)
+{
+	enum object_type type;
+
+	grep_read_lock();
+	gs->buf = read_sha1_file(gs->identifier, &type, &gs->size);
+	grep_read_unlock();
+
+	if (!gs->buf)
+		return error(_("'%s': unable to read %s"),
+			     gs->name,
+			     sha1_to_hex(gs->identifier));
+	return 0;
+}
+
+static int grep_source_load_file(struct grep_source *gs)
+{
+	const char *filename = gs->identifier;
+	struct stat st;
+	char *data;
+	size_t size;
+	int i;
+
+	if (lstat(filename, &st) < 0) {
+	err_ret:
+		if (errno != ENOENT)
+			error(_("'%s': %s"), filename, strerror(errno));
+		return -1;
+	}
+	if (!S_ISREG(st.st_mode))
+		return -1;
+	size = xsize_t(st.st_size);
+	i = open(filename, O_RDONLY);
+	if (i < 0)
+		goto err_ret;
+	data = xmalloc(size + 1);
+	if (st.st_size != read_in_full(i, data, size)) {
+		error(_("'%s': short read %s"), filename, strerror(errno));
+		close(i);
+		free(data);
+		return -1;
+	}
+	close(i);
+	data[size] = 0;
+
+	gs->buf = data;
+	gs->size = size;
+	return 0;
+}
+
+int grep_source_load(struct grep_source *gs)
+{
+	if (gs->buf)
+		return 0;
+
+	switch (gs->type) {
+	case GREP_SOURCE_FILE:
+		return grep_source_load_file(gs);
+	case GREP_SOURCE_SHA1:
+		return grep_source_load_sha1(gs);
+	case GREP_SOURCE_BUF:
+		return gs->buf ? 0 : -1;
+	}
+	die("BUG: invalid grep_source type");
+}
+
+void grep_source_load_driver(struct grep_source *gs)
+{
+	if (gs->driver)
+		return;
+
+	grep_attr_lock();
+	gs->driver = userdiff_find_by_path(gs->name);
+	if (!gs->driver)
+		gs->driver = userdiff_find_by_name("default");
+	grep_attr_unlock();
+}
+
+int grep_source_is_binary(struct grep_source *gs)
+{
+	grep_source_load_driver(gs);
+	if (gs->driver->binary != -1)
+		return gs->driver->binary;
+
+	if (!grep_source_load(gs))
+		return buffer_is_binary(gs->buf, gs->size);
+
+	return 0;
 }
diff --git a/grep.h b/grep.h
index f00db0e..ed7de6b 100644
--- a/grep.h
+++ b/grep.h
@@ -1,6 +1,15 @@
 #ifndef GREP_H
 #define GREP_H
 #include "color.h"
+#ifdef USE_LIBPCRE
+#include <pcre.h>
+#else
+typedef int pcre;
+typedef int pcre_extra;
+#endif
+#include "kwset.h"
+#include "thread-utils.h"
+#include "userdiff.h"
 
 enum grep_pat_token {
 	GREP_PATTERN,
@@ -10,28 +19,34 @@
 	GREP_OPEN_PAREN,
 	GREP_CLOSE_PAREN,
 	GREP_NOT,
-	GREP_OR,
+	GREP_OR
 };
 
 enum grep_context {
 	GREP_CONTEXT_HEAD,
-	GREP_CONTEXT_BODY,
+	GREP_CONTEXT_BODY
 };
 
 enum grep_header_field {
 	GREP_HEADER_AUTHOR = 0,
-	GREP_HEADER_COMMITTER,
+	GREP_HEADER_COMMITTER
 };
+#define GREP_HEADER_FIELD_MAX (GREP_HEADER_COMMITTER + 1)
 
 struct grep_pat {
 	struct grep_pat *next;
 	const char *origin;
 	int no;
 	enum grep_pat_token token;
-	const char *pattern;
+	char *pattern;
+	size_t patternlen;
 	enum grep_header_field field;
 	regex_t regexp;
+	pcre *pcre_regexp;
+	pcre_extra *pcre_extra_info;
+	kwset_t kws;
 	unsigned fixed:1;
+	unsigned ignore_case:1;
 	unsigned word_regexp:1;
 };
 
@@ -39,7 +54,8 @@
 	GREP_NODE_ATOM,
 	GREP_NODE_NOT,
 	GREP_NODE_AND,
-	GREP_NODE_OR,
+	GREP_NODE_TRUE,
+	GREP_NODE_OR
 };
 
 struct grep_expr {
@@ -58,11 +74,15 @@
 struct grep_opt {
 	struct grep_pat *pattern_list;
 	struct grep_pat **pattern_tail;
+	struct grep_pat *header_list;
+	struct grep_pat **header_tail;
 	struct grep_expr *pattern_expression;
+	const char *prefix;
 	int prefix_length;
 	regex_t regexp;
 	int linenum;
 	int invert;
+	int ignore_case;
 	int status_only;
 	int name_only;
 	int unmatch_name_only;
@@ -75,25 +95,94 @@
 #define GREP_BINARY_TEXT	2
 	int binary;
 	int extended;
+	int pcre;
 	int relative;
 	int pathname;
 	int null_following_name;
 	int color;
+	int max_depth;
 	int funcname;
+	int funcbody;
+	char color_context[COLOR_MAXLEN];
+	char color_filename[COLOR_MAXLEN];
+	char color_function[COLOR_MAXLEN];
+	char color_lineno[COLOR_MAXLEN];
 	char color_match[COLOR_MAXLEN];
-	const char *color_external;
+	char color_selected[COLOR_MAXLEN];
+	char color_sep[COLOR_MAXLEN];
 	int regflags;
 	unsigned pre_context;
 	unsigned post_context;
 	unsigned last_shown;
 	int show_hunk_mark;
+	int file_break;
+	int heading;
 	void *priv;
+
+	void (*output)(struct grep_opt *opt, const void *data, size_t size);
+	void *output_priv;
 };
 
+extern void append_grep_pat(struct grep_opt *opt, const char *pat, size_t patlen, const char *origin, int no, enum grep_pat_token t);
 extern void append_grep_pattern(struct grep_opt *opt, const char *pat, const char *origin, int no, enum grep_pat_token t);
 extern void append_header_grep_pattern(struct grep_opt *, enum grep_header_field, const char *);
 extern void compile_grep_patterns(struct grep_opt *opt);
 extern void free_grep_patterns(struct grep_opt *opt);
-extern int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long size);
+extern int grep_buffer(struct grep_opt *opt, char *buf, unsigned long size);
+
+struct grep_source {
+	char *name;
+
+	enum grep_source_type {
+		GREP_SOURCE_SHA1,
+		GREP_SOURCE_FILE,
+		GREP_SOURCE_BUF,
+	} type;
+	void *identifier;
+
+	char *buf;
+	unsigned long size;
+
+	struct userdiff_driver *driver;
+};
+
+void grep_source_init(struct grep_source *gs, enum grep_source_type type,
+		      const char *name, const void *identifier);
+int grep_source_load(struct grep_source *gs);
+void grep_source_clear_data(struct grep_source *gs);
+void grep_source_clear(struct grep_source *gs);
+void grep_source_load_driver(struct grep_source *gs);
+int grep_source_is_binary(struct grep_source *gs);
+
+int grep_source(struct grep_opt *opt, struct grep_source *gs);
+
+extern struct grep_opt *grep_opt_dup(const struct grep_opt *opt);
+extern int grep_threads_ok(const struct grep_opt *opt);
+
+#ifndef NO_PTHREADS
+/*
+ * Mutex used around access to the attributes machinery if
+ * opt->use_threads.  Must be initialized/destroyed by callers!
+ */
+extern int grep_use_locks;
+extern pthread_mutex_t grep_attr_mutex;
+extern pthread_mutex_t grep_read_mutex;
+
+static inline void grep_read_lock(void)
+{
+	if (grep_use_locks)
+		pthread_mutex_lock(&grep_read_mutex);
+}
+
+static inline void grep_read_unlock(void)
+{
+	if (grep_use_locks)
+		pthread_mutex_unlock(&grep_read_mutex);
+}
+
+#else
+#define grep_read_lock()
+#define grep_read_unlock()
+#endif
 
 #endif
diff --git a/hash-object.c b/hash-object.c
deleted file mode 100644
index 9455dd0..0000000
--- a/hash-object.c
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * GIT - The information manager from hell
- *
- * Copyright (C) Linus Torvalds, 2005
- * Copyright (C) Junio C Hamano, 2005
- */
-#include "cache.h"
-#include "blob.h"
-#include "quote.h"
-#include "parse-options.h"
-#include "exec_cmd.h"
-
-static void hash_fd(int fd, const char *type, int write_object, const char *path)
-{
-	struct stat st;
-	unsigned char sha1[20];
-	if (fstat(fd, &st) < 0 ||
-	    index_fd(sha1, fd, &st, write_object, type_from_string(type), path))
-		die(write_object
-		    ? "Unable to add %s to database"
-		    : "Unable to hash %s", path);
-	printf("%s\n", sha1_to_hex(sha1));
-	maybe_flush_or_die(stdout, "hash to stdout");
-}
-
-static void hash_object(const char *path, const char *type, int write_object,
-			const char *vpath)
-{
-	int fd;
-	fd = open(path, O_RDONLY);
-	if (fd < 0)
-		die_errno("Cannot open '%s'", path);
-	hash_fd(fd, type, write_object, vpath);
-}
-
-static void hash_stdin_paths(const char *type, int write_objects)
-{
-	struct strbuf buf = STRBUF_INIT, nbuf = STRBUF_INIT;
-
-	while (strbuf_getline(&buf, stdin, '\n') != EOF) {
-		if (buf.buf[0] == '"') {
-			strbuf_reset(&nbuf);
-			if (unquote_c_style(&nbuf, buf.buf, NULL))
-				die("line is badly quoted");
-			strbuf_swap(&buf, &nbuf);
-		}
-		hash_object(buf.buf, type, write_objects, buf.buf);
-	}
-	strbuf_release(&buf);
-	strbuf_release(&nbuf);
-}
-
-static const char * const hash_object_usage[] = {
-	"git hash-object [-t <type>] [-w] [--path=<file>|--no-filters] [--stdin] [--] <file>...",
-	"git hash-object  --stdin-paths < <list-of-paths>",
-	NULL
-};
-
-static const char *type;
-static int write_object;
-static int hashstdin;
-static int stdin_paths;
-static int no_filters;
-static const char *vpath;
-
-static const struct option hash_object_options[] = {
-	OPT_STRING('t', NULL, &type, "type", "object type"),
-	OPT_BOOLEAN('w', NULL, &write_object, "write the object into the object database"),
-	OPT_BOOLEAN( 0 , "stdin", &hashstdin, "read the object from stdin"),
-	OPT_BOOLEAN( 0 , "stdin-paths", &stdin_paths, "read file names from stdin"),
-	OPT_BOOLEAN( 0 , "no-filters", &no_filters, "store file as is without filters"),
-	OPT_STRING( 0 , "path", &vpath, "file", "process file as it were from this path"),
-	OPT_END()
-};
-
-int main(int argc, const char **argv)
-{
-	int i;
-	const char *prefix = NULL;
-	int prefix_length = -1;
-	const char *errstr = NULL;
-
-	type = blob_type;
-
-	git_extract_argv0_path(argv[0]);
-
-	argc = parse_options(argc, argv, NULL, hash_object_options,
-			     hash_object_usage, 0);
-
-	if (write_object) {
-		prefix = setup_git_directory();
-		prefix_length = prefix ? strlen(prefix) : 0;
-		if (vpath && prefix)
-			vpath = prefix_filename(prefix, prefix_length, vpath);
-	}
-
-	git_config(git_default_config, NULL);
-
-	if (stdin_paths) {
-		if (hashstdin)
-			errstr = "Can't use --stdin-paths with --stdin";
-		else if (argc)
-			errstr = "Can't specify files with --stdin-paths";
-		else if (vpath)
-			errstr = "Can't use --stdin-paths with --path";
-		else if (no_filters)
-			errstr = "Can't use --stdin-paths with --no-filters";
-	}
-	else {
-		if (hashstdin > 1)
-			errstr = "Multiple --stdin arguments are not supported";
-		if (vpath && no_filters)
-			errstr = "Can't use --path with --no-filters";
-	}
-
-	if (errstr) {
-		error("%s", errstr);
-		usage_with_options(hash_object_usage, hash_object_options);
-	}
-
-	if (hashstdin)
-		hash_fd(0, type, write_object, vpath);
-
-	for (i = 0 ; i < argc; i++) {
-		const char *arg = argv[i];
-
-		if (0 <= prefix_length)
-			arg = prefix_filename(prefix, prefix_length, arg);
-		hash_object(arg, type, write_object,
-			    no_filters ? NULL : vpath ? vpath : arg);
-	}
-
-	if (stdin_paths)
-		hash_stdin_paths(type, write_object);
-
-	return 0;
-}
diff --git a/hash.c b/hash.c
index 1cd4c9d..749ecfe 100644
--- a/hash.c
+++ b/hash.c
@@ -81,7 +81,7 @@
 	return insert_hash_entry(hash, ptr, table);
 }
 
-int for_each_hash(const struct hash_table *table, int (*fn)(void *))
+int for_each_hash(const struct hash_table *table, int (*fn)(void *, void *), void *data)
 {
 	int sum = 0;
 	unsigned int i;
@@ -92,7 +92,7 @@
 		void *ptr = array->ptr;
 		array++;
 		if (ptr) {
-			int val = fn(ptr);
+			int val = fn(ptr, data);
 			if (val < 0)
 				return val;
 			sum += val;
diff --git a/hash.h b/hash.h
index 69e33a4..b875ce6 100644
--- a/hash.h
+++ b/hash.h
@@ -30,7 +30,7 @@
 
 extern void *lookup_hash(unsigned int hash, const struct hash_table *table);
 extern void **insert_hash(unsigned int hash, void *ptr, struct hash_table *table);
-extern int for_each_hash(const struct hash_table *table, int (*fn)(void *));
+extern int for_each_hash(const struct hash_table *table, int (*fn)(void *, void *), void *data);
 extern void free_hash(struct hash_table *table);
 
 static inline void init_hash(struct hash_table *table)
diff --git a/help.c b/help.c
index 6c46d8b..662349d 100644
--- a/help.c
+++ b/help.c
@@ -3,28 +3,10 @@
 #include "exec_cmd.h"
 #include "levenshtein.h"
 #include "help.h"
-
-/* most GUI terminals set COLUMNS (although some don't export it) */
-static int term_columns(void)
-{
-	char *col_string = getenv("COLUMNS");
-	int n_cols;
-
-	if (col_string && (n_cols = atoi(col_string)) > 0)
-		return n_cols;
-
-#ifdef TIOCGWINSZ
-	{
-		struct winsize ws;
-		if (!ioctl(1, TIOCGWINSZ, &ws)) {
-			if (ws.ws_col)
-				return ws.ws_col;
-		}
-	}
-#endif
-
-	return 80;
-}
+#include "common-cmds.h"
+#include "string-list.h"
+#include "column.h"
+#include "version.h"
 
 void add_cmdname(struct cmdnames *cmds, const char *name, int len)
 {
@@ -91,31 +73,25 @@
 	cmds->cnt = cj;
 }
 
-static void pretty_print_string_list(struct cmdnames *cmds, int longest)
+static void pretty_print_string_list(struct cmdnames *cmds,
+				     unsigned int colopts)
 {
-	int cols = 1, rows;
-	int space = longest + 1; /* min 1 SP between words */
-	int max_cols = term_columns() - 1; /* don't print *on* the edge */
-	int i, j;
+	struct string_list list = STRING_LIST_INIT_NODUP;
+	struct column_options copts;
+	int i;
 
-	if (space < max_cols)
-		cols = max_cols / space;
-	rows = DIV_ROUND_UP(cmds->cnt, cols);
-
-	for (i = 0; i < rows; i++) {
-		printf("  ");
-
-		for (j = 0; j < cols; j++) {
-			int n = j * rows + i;
-			int size = space;
-			if (n >= cmds->cnt)
-				break;
-			if (j == cols-1 || n + rows >= cmds->cnt)
-				size = 1;
-			printf("%-*s", size, cmds->names[n]->name);
-		}
-		putchar('\n');
-	}
+	for (i = 0; i < cmds->cnt; i++)
+		string_list_append(&list, cmds->names[i]->name);
+	/*
+	 * always enable column display, we only consult column.*
+	 * about layout strategy and stuff
+	 */
+	colopts = (colopts & ~COL_ENABLE_MASK) | COL_ENABLED;
+	memset(&copts, 0, sizeof(copts));
+	copts.indent = "  ";
+	copts.padding = 2;
+	print_columns(&list, colopts, &copts);
+	string_list_clear(&list, 0);
 }
 
 static int is_executable(const char *name)
@@ -126,8 +102,11 @@
 	    !S_ISREG(st.st_mode))
 		return 0;
 
-#ifdef __MINGW32__
-	/* cannot trust the executable bit, peek into the file instead */
+#if defined(WIN32) || defined(__CYGWIN__)
+#if defined(__CYGWIN__)
+if ((st.st_mode & S_IXUSR) == 0)
+#endif
+{	/* cannot trust the executable bit, peek into the file instead */
 	char buf[3] = { 0 };
 	int n;
 	int fd = open(name, O_RDONLY);
@@ -140,6 +119,7 @@
 				st.st_mode |= S_IXUSR;
 		close(fd);
 	}
+}
 #endif
 	return st.st_mode & S_IXUSR;
 }
@@ -220,34 +200,21 @@
 	exclude_cmds(other_cmds, main_cmds);
 }
 
-void list_commands(const char *title, struct cmdnames *main_cmds,
-		   struct cmdnames *other_cmds)
+void list_commands(unsigned int colopts,
+		   struct cmdnames *main_cmds, struct cmdnames *other_cmds)
 {
-	int i, longest = 0;
-
-	for (i = 0; i < main_cmds->cnt; i++)
-		if (longest < main_cmds->names[i]->len)
-			longest = main_cmds->names[i]->len;
-	for (i = 0; i < other_cmds->cnt; i++)
-		if (longest < other_cmds->names[i]->len)
-			longest = other_cmds->names[i]->len;
-
 	if (main_cmds->cnt) {
 		const char *exec_path = git_exec_path();
-		printf("available %s in '%s'\n", title, exec_path);
-		printf("----------------");
-		mput_char('-', strlen(title) + strlen(exec_path));
+		printf_ln(_("available git commands in '%s'"), exec_path);
 		putchar('\n');
-		pretty_print_string_list(main_cmds, longest);
+		pretty_print_string_list(main_cmds, colopts);
 		putchar('\n');
 	}
 
 	if (other_cmds->cnt) {
-		printf("%s available from elsewhere on your $PATH\n", title);
-		printf("---------------------------------------");
-		mput_char('-', strlen(title));
+		printf_ln(_("git commands available from elsewhere on your $PATH"));
 		putchar('\n');
-		pretty_print_string_list(other_cmds, longest);
+		pretty_print_string_list(other_cmds, colopts);
 		putchar('\n');
 	}
 }
@@ -296,13 +263,21 @@
 	old->names = NULL;
 }
 
+/* An empirically derived magic number */
+#define SIMILARITY_FLOOR 7
+#define SIMILAR_ENOUGH(x) ((x) < SIMILARITY_FLOOR)
+
+static const char bad_interpreter_advice[] =
+	N_("'%s' appears to be a git command, but we were not\n"
+	"able to execute it. Maybe git-%s is broken?");
+
 const char *help_unknown_cmd(const char *cmd)
 {
 	int i, n, best_similarity = 0;
 	struct cmdnames main_cmds, other_cmds;
 
 	memset(&main_cmds, 0, sizeof(main_cmds));
-	memset(&other_cmds, 0, sizeof(main_cmds));
+	memset(&other_cmds, 0, sizeof(other_cmds));
 	memset(&aliases, 0, sizeof(aliases));
 
 	git_config(git_unknown_cmd_config, NULL);
@@ -315,42 +290,82 @@
 	      sizeof(main_cmds.names), cmdname_compare);
 	uniq(&main_cmds);
 
-	/* This reuses cmdname->len for similarity index */
-	for (i = 0; i < main_cmds.cnt; ++i)
+	/* This abuses cmdname->len for levenshtein distance */
+	for (i = 0, n = 0; i < main_cmds.cnt; i++) {
+		int cmp = 0; /* avoid compiler stupidity */
+		const char *candidate = main_cmds.names[i]->name;
+
+		/*
+		 * An exact match means we have the command, but
+		 * for some reason exec'ing it gave us ENOENT; probably
+		 * it's a bad interpreter in the #! line.
+		 */
+		if (!strcmp(candidate, cmd))
+			die(_(bad_interpreter_advice), cmd, cmd);
+
+		/* Does the candidate appear in common_cmds list? */
+		while (n < ARRAY_SIZE(common_cmds) &&
+		       (cmp = strcmp(common_cmds[n].name, candidate)) < 0)
+			n++;
+		if ((n < ARRAY_SIZE(common_cmds)) && !cmp) {
+			/* Yes, this is one of the common commands */
+			n++; /* use the entry from common_cmds[] */
+			if (!prefixcmp(candidate, cmd)) {
+				/* Give prefix match a very good score */
+				main_cmds.names[i]->len = 0;
+				continue;
+			}
+		}
+
 		main_cmds.names[i]->len =
-			levenshtein(cmd, main_cmds.names[i]->name, 0, 2, 1, 4);
+			levenshtein(cmd, candidate, 0, 2, 1, 3) + 1;
+	}
 
 	qsort(main_cmds.names, main_cmds.cnt,
 	      sizeof(*main_cmds.names), levenshtein_compare);
 
 	if (!main_cmds.cnt)
-		die ("Uh oh. Your system reports no Git commands at all.");
+		die(_("Uh oh. Your system reports no Git commands at all."));
 
-	best_similarity = main_cmds.names[0]->len;
-	n = 1;
-	while (n < main_cmds.cnt && best_similarity == main_cmds.names[n]->len)
-		++n;
-	if (autocorrect && n == 1) {
+	/* skip and count prefix matches */
+	for (n = 0; n < main_cmds.cnt && !main_cmds.names[n]->len; n++)
+		; /* still counting */
+
+	if (main_cmds.cnt <= n) {
+		/* prefix matches with everything? that is too ambiguous */
+		best_similarity = SIMILARITY_FLOOR + 1;
+	} else {
+		/* count all the most similar ones */
+		for (best_similarity = main_cmds.names[n++]->len;
+		     (n < main_cmds.cnt &&
+		      best_similarity == main_cmds.names[n]->len);
+		     n++)
+			; /* still counting */
+	}
+	if (autocorrect && n == 1 && SIMILAR_ENOUGH(best_similarity)) {
 		const char *assumed = main_cmds.names[0]->name;
 		main_cmds.names[0] = NULL;
 		clean_cmdnames(&main_cmds);
-		fprintf(stderr, "WARNING: You called a Git program named '%s', "
-			"which does not exist.\n"
-			"Continuing under the assumption that you meant '%s'\n",
+		fprintf_ln(stderr,
+			   _("WARNING: You called a Git command named '%s', "
+			     "which does not exist.\n"
+			     "Continuing under the assumption that you meant '%s'"),
 			cmd, assumed);
 		if (autocorrect > 0) {
-			fprintf(stderr, "in %0.1f seconds automatically...\n",
+			fprintf_ln(stderr, _("in %0.1f seconds automatically..."),
 				(float)autocorrect/10.0);
 			poll(NULL, 0, autocorrect * 100);
 		}
 		return assumed;
 	}
 
-	fprintf(stderr, "git: '%s' is not a git-command. See 'git --help'.\n", cmd);
+	fprintf_ln(stderr, _("git: '%s' is not a git command. See 'git --help'."), cmd);
 
-	if (best_similarity < 6) {
-		fprintf(stderr, "\nDid you mean %s?\n",
-			n < 2 ? "this": "one of these");
+	if (SIMILAR_ENOUGH(best_similarity)) {
+		fprintf_ln(stderr,
+			   Q_("\nDid you mean this?",
+			      "\nDid you mean one of these?",
+			   n));
 
 		for (i = 0; i < n; i++)
 			fprintf(stderr, "\t%s\n", main_cmds.names[i]->name);
diff --git a/help.h b/help.h
index 56bc154..0ae5a12 100644
--- a/help.h
+++ b/help.h
@@ -16,14 +16,15 @@
 		putchar(c);
 }
 
-void load_command_list(const char *prefix,
-		struct cmdnames *main_cmds,
-		struct cmdnames *other_cmds);
-void add_cmdname(struct cmdnames *cmds, const char *name, int len);
+extern void list_common_cmds_help(void);
+extern const char *help_unknown_cmd(const char *cmd);
+extern void load_command_list(const char *prefix,
+			      struct cmdnames *main_cmds,
+			      struct cmdnames *other_cmds);
+extern void add_cmdname(struct cmdnames *cmds, const char *name, int len);
 /* Here we require that excludes is a sorted list. */
-void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes);
-int is_in_cmdlist(struct cmdnames *c, const char *s);
-void list_commands(const char *title, struct cmdnames *main_cmds,
-		   struct cmdnames *other_cmds);
+extern void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes);
+extern int is_in_cmdlist(struct cmdnames *cmds, const char *name);
+extern void list_commands(unsigned int colopts, struct cmdnames *main_cmds, struct cmdnames *other_cmds);
 
 #endif /* HELP_H */
diff --git a/hex.c b/hex.c
new file mode 100644
index 0000000..9ebc050
--- /dev/null
+++ b/hex.c
@@ -0,0 +1,75 @@
+#include "cache.h"
+
+const signed char hexval_table[256] = {
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 00-07 */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 08-0f */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 10-17 */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 18-1f */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 20-27 */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 28-2f */
+	  0,  1,  2,  3,  4,  5,  6,  7,		/* 30-37 */
+	  8,  9, -1, -1, -1, -1, -1, -1,		/* 38-3f */
+	 -1, 10, 11, 12, 13, 14, 15, -1,		/* 40-47 */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 48-4f */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 50-57 */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 58-5f */
+	 -1, 10, 11, 12, 13, 14, 15, -1,		/* 60-67 */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 68-67 */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 70-77 */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 78-7f */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 80-87 */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 88-8f */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 90-97 */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 98-9f */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* a0-a7 */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* a8-af */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* b0-b7 */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* b8-bf */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* c0-c7 */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* c8-cf */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* d0-d7 */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* d8-df */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* e0-e7 */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* e8-ef */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* f0-f7 */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* f8-ff */
+};
+
+int get_sha1_hex(const char *hex, unsigned char *sha1)
+{
+	int i;
+	for (i = 0; i < 20; i++) {
+		unsigned int val;
+		/*
+		 * hex[1]=='\0' is caught when val is checked below,
+		 * but if hex[0] is NUL we have to avoid reading
+		 * past the end of the string:
+		 */
+		if (!hex[0])
+			return -1;
+		val = (hexval(hex[0]) << 4) | hexval(hex[1]);
+		if (val & ~0xff)
+			return -1;
+		*sha1++ = val;
+		hex += 2;
+	}
+	return 0;
+}
+
+char *sha1_to_hex(const unsigned char *sha1)
+{
+	static int bufno;
+	static char hexbuffer[4][50];
+	static const char hex[] = "0123456789abcdef";
+	char *buffer = hexbuffer[3 & ++bufno], *buf = buffer;
+	int i;
+
+	for (i = 0; i < 20; i++) {
+		unsigned int val = *sha1++;
+		*buf++ = hex[val >> 4];
+		*buf++ = hex[val & 0xf];
+	}
+	*buf = '\0';
+
+	return buffer;
+}
diff --git a/http-backend.c b/http-backend.c
new file mode 100644
index 0000000..f50e77f
--- /dev/null
+++ b/http-backend.c
@@ -0,0 +1,600 @@
+#include "cache.h"
+#include "refs.h"
+#include "pkt-line.h"
+#include "object.h"
+#include "tag.h"
+#include "exec_cmd.h"
+#include "run-command.h"
+#include "string-list.h"
+#include "url.h"
+#include "argv-array.h"
+
+static const char content_type[] = "Content-Type";
+static const char content_length[] = "Content-Length";
+static const char last_modified[] = "Last-Modified";
+static int getanyfile = 1;
+
+static struct string_list *query_params;
+
+struct rpc_service {
+	const char *name;
+	const char *config_name;
+	signed enabled : 2;
+};
+
+static struct rpc_service rpc_service[] = {
+	{ "upload-pack", "uploadpack", 1 },
+	{ "receive-pack", "receivepack", -1 },
+};
+
+static struct string_list *get_parameters(void)
+{
+	if (!query_params) {
+		const char *query = getenv("QUERY_STRING");
+
+		query_params = xcalloc(1, sizeof(*query_params));
+		while (query && *query) {
+			char *name = url_decode_parameter_name(&query);
+			char *value = url_decode_parameter_value(&query);
+			struct string_list_item *i;
+
+			i = string_list_lookup(query_params, name);
+			if (!i)
+				i = string_list_insert(query_params, name);
+			else
+				free(i->util);
+			i->util = value;
+		}
+	}
+	return query_params;
+}
+
+static const char *get_parameter(const char *name)
+{
+	struct string_list_item *i;
+	i = string_list_lookup(get_parameters(), name);
+	return i ? i->util : NULL;
+}
+
+__attribute__((format (printf, 2, 3)))
+static void format_write(int fd, const char *fmt, ...)
+{
+	static char buffer[1024];
+
+	va_list args;
+	unsigned n;
+
+	va_start(args, fmt);
+	n = vsnprintf(buffer, sizeof(buffer), fmt, args);
+	va_end(args);
+	if (n >= sizeof(buffer))
+		die("protocol error: impossibly long line");
+
+	safe_write(fd, buffer, n);
+}
+
+static void http_status(unsigned code, const char *msg)
+{
+	format_write(1, "Status: %u %s\r\n", code, msg);
+}
+
+static void hdr_str(const char *name, const char *value)
+{
+	format_write(1, "%s: %s\r\n", name, value);
+}
+
+static void hdr_int(const char *name, uintmax_t value)
+{
+	format_write(1, "%s: %" PRIuMAX "\r\n", name, value);
+}
+
+static void hdr_date(const char *name, unsigned long when)
+{
+	const char *value = show_date(when, 0, DATE_RFC2822);
+	hdr_str(name, value);
+}
+
+static void hdr_nocache(void)
+{
+	hdr_str("Expires", "Fri, 01 Jan 1980 00:00:00 GMT");
+	hdr_str("Pragma", "no-cache");
+	hdr_str("Cache-Control", "no-cache, max-age=0, must-revalidate");
+}
+
+static void hdr_cache_forever(void)
+{
+	unsigned long now = time(NULL);
+	hdr_date("Date", now);
+	hdr_date("Expires", now + 31536000);
+	hdr_str("Cache-Control", "public, max-age=31536000");
+}
+
+static void end_headers(void)
+{
+	safe_write(1, "\r\n", 2);
+}
+
+__attribute__((format (printf, 1, 2)))
+static NORETURN void not_found(const char *err, ...)
+{
+	va_list params;
+
+	http_status(404, "Not Found");
+	hdr_nocache();
+	end_headers();
+
+	va_start(params, err);
+	if (err && *err)
+		vfprintf(stderr, err, params);
+	va_end(params);
+	exit(0);
+}
+
+__attribute__((format (printf, 1, 2)))
+static NORETURN void forbidden(const char *err, ...)
+{
+	va_list params;
+
+	http_status(403, "Forbidden");
+	hdr_nocache();
+	end_headers();
+
+	va_start(params, err);
+	if (err && *err)
+		vfprintf(stderr, err, params);
+	va_end(params);
+	exit(0);
+}
+
+static void select_getanyfile(void)
+{
+	if (!getanyfile)
+		forbidden("Unsupported service: getanyfile");
+}
+
+static void send_strbuf(const char *type, struct strbuf *buf)
+{
+	hdr_int(content_length, buf->len);
+	hdr_str(content_type, type);
+	end_headers();
+	safe_write(1, buf->buf, buf->len);
+}
+
+static void send_local_file(const char *the_type, const char *name)
+{
+	const char *p = git_path("%s", name);
+	size_t buf_alloc = 8192;
+	char *buf = xmalloc(buf_alloc);
+	int fd;
+	struct stat sb;
+
+	fd = open(p, O_RDONLY);
+	if (fd < 0)
+		not_found("Cannot open '%s': %s", p, strerror(errno));
+	if (fstat(fd, &sb) < 0)
+		die_errno("Cannot stat '%s'", p);
+
+	hdr_int(content_length, sb.st_size);
+	hdr_str(content_type, the_type);
+	hdr_date(last_modified, sb.st_mtime);
+	end_headers();
+
+	for (;;) {
+		ssize_t n = xread(fd, buf, buf_alloc);
+		if (n < 0)
+			die_errno("Cannot read '%s'", p);
+		if (!n)
+			break;
+		safe_write(1, buf, n);
+	}
+	close(fd);
+	free(buf);
+}
+
+static void get_text_file(char *name)
+{
+	select_getanyfile();
+	hdr_nocache();
+	send_local_file("text/plain", name);
+}
+
+static void get_loose_object(char *name)
+{
+	select_getanyfile();
+	hdr_cache_forever();
+	send_local_file("application/x-git-loose-object", name);
+}
+
+static void get_pack_file(char *name)
+{
+	select_getanyfile();
+	hdr_cache_forever();
+	send_local_file("application/x-git-packed-objects", name);
+}
+
+static void get_idx_file(char *name)
+{
+	select_getanyfile();
+	hdr_cache_forever();
+	send_local_file("application/x-git-packed-objects-toc", name);
+}
+
+static int http_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, "http.getanyfile")) {
+		getanyfile = git_config_bool(var, value);
+		return 0;
+	}
+
+	if (!prefixcmp(var, "http.")) {
+		int i;
+
+		for (i = 0; i < ARRAY_SIZE(rpc_service); i++) {
+			struct rpc_service *svc = &rpc_service[i];
+			if (!strcmp(var + 5, svc->config_name)) {
+				svc->enabled = git_config_bool(var, value);
+				return 0;
+			}
+		}
+	}
+
+	/* we are not interested in parsing any other configuration here */
+	return 0;
+}
+
+static struct rpc_service *select_service(const char *name)
+{
+	struct rpc_service *svc = NULL;
+	int i;
+
+	if (prefixcmp(name, "git-"))
+		forbidden("Unsupported service: '%s'", name);
+
+	for (i = 0; i < ARRAY_SIZE(rpc_service); i++) {
+		struct rpc_service *s = &rpc_service[i];
+		if (!strcmp(s->name, name + 4)) {
+			svc = s;
+			break;
+		}
+	}
+
+	if (!svc)
+		forbidden("Unsupported service: '%s'", name);
+
+	if (svc->enabled < 0) {
+		const char *user = getenv("REMOTE_USER");
+		svc->enabled = (user && *user) ? 1 : 0;
+	}
+	if (!svc->enabled)
+		forbidden("Service not enabled: '%s'", svc->name);
+	return svc;
+}
+
+static void inflate_request(const char *prog_name, int out)
+{
+	git_zstream stream;
+	unsigned char in_buf[8192];
+	unsigned char out_buf[8192];
+	unsigned long cnt = 0;
+
+	memset(&stream, 0, sizeof(stream));
+	git_inflate_init_gzip_only(&stream);
+
+	while (1) {
+		ssize_t n = xread(0, in_buf, sizeof(in_buf));
+		if (n <= 0)
+			die("request ended in the middle of the gzip stream");
+
+		stream.next_in = in_buf;
+		stream.avail_in = n;
+
+		while (0 < stream.avail_in) {
+			int ret;
+
+			stream.next_out = out_buf;
+			stream.avail_out = sizeof(out_buf);
+
+			ret = git_inflate(&stream, Z_NO_FLUSH);
+			if (ret != Z_OK && ret != Z_STREAM_END)
+				die("zlib error inflating request, result %d", ret);
+
+			n = stream.total_out - cnt;
+			if (write_in_full(out, out_buf, n) != n)
+				die("%s aborted reading request", prog_name);
+			cnt += n;
+
+			if (ret == Z_STREAM_END)
+				goto done;
+		}
+	}
+
+done:
+	git_inflate_end(&stream);
+	close(out);
+}
+
+static void run_service(const char **argv)
+{
+	const char *encoding = getenv("HTTP_CONTENT_ENCODING");
+	const char *user = getenv("REMOTE_USER");
+	const char *host = getenv("REMOTE_ADDR");
+	struct argv_array env = ARGV_ARRAY_INIT;
+	int gzipped_request = 0;
+	struct child_process cld;
+
+	if (encoding && !strcmp(encoding, "gzip"))
+		gzipped_request = 1;
+	else if (encoding && !strcmp(encoding, "x-gzip"))
+		gzipped_request = 1;
+
+	if (!user || !*user)
+		user = "anonymous";
+	if (!host || !*host)
+		host = "(none)";
+
+	if (!getenv("GIT_COMMITTER_NAME"))
+		argv_array_pushf(&env, "GIT_COMMITTER_NAME=%s", user);
+	if (!getenv("GIT_COMMITTER_EMAIL"))
+		argv_array_pushf(&env, "GIT_COMMITTER_EMAIL=%s@http.%s",
+				 user, host);
+
+	memset(&cld, 0, sizeof(cld));
+	cld.argv = argv;
+	cld.env = env.argv;
+	if (gzipped_request)
+		cld.in = -1;
+	cld.git_cmd = 1;
+	if (start_command(&cld))
+		exit(1);
+
+	close(1);
+	if (gzipped_request)
+		inflate_request(argv[0], cld.in);
+	else
+		close(0);
+
+	if (finish_command(&cld))
+		exit(1);
+	argv_array_clear(&env);
+}
+
+static int show_text_ref(const char *name, const unsigned char *sha1,
+	int flag, void *cb_data)
+{
+	struct strbuf *buf = cb_data;
+	struct object *o = parse_object(sha1);
+	if (!o)
+		return 0;
+
+	strbuf_addf(buf, "%s\t%s\n", sha1_to_hex(sha1), name);
+	if (o->type == OBJ_TAG) {
+		o = deref_tag(o, name, 0);
+		if (!o)
+			return 0;
+		strbuf_addf(buf, "%s\t%s^{}\n", sha1_to_hex(o->sha1), name);
+	}
+	return 0;
+}
+
+static void get_info_refs(char *arg)
+{
+	const char *service_name = get_parameter("service");
+	struct strbuf buf = STRBUF_INIT;
+
+	hdr_nocache();
+
+	if (service_name) {
+		const char *argv[] = {NULL /* service name */,
+			"--stateless-rpc", "--advertise-refs",
+			".", NULL};
+		struct rpc_service *svc = select_service(service_name);
+
+		strbuf_addf(&buf, "application/x-git-%s-advertisement",
+			svc->name);
+		hdr_str(content_type, buf.buf);
+		end_headers();
+
+		packet_write(1, "# service=git-%s\n", svc->name);
+		packet_flush(1);
+
+		argv[0] = svc->name;
+		run_service(argv);
+
+	} else {
+		select_getanyfile();
+		for_each_ref(show_text_ref, &buf);
+		send_strbuf("text/plain", &buf);
+	}
+	strbuf_release(&buf);
+}
+
+static void get_info_packs(char *arg)
+{
+	size_t objdirlen = strlen(get_object_directory());
+	struct strbuf buf = STRBUF_INIT;
+	struct packed_git *p;
+	size_t cnt = 0;
+
+	select_getanyfile();
+	prepare_packed_git();
+	for (p = packed_git; p; p = p->next) {
+		if (p->pack_local)
+			cnt++;
+	}
+
+	strbuf_grow(&buf, cnt * 53 + 2);
+	for (p = packed_git; p; p = p->next) {
+		if (p->pack_local)
+			strbuf_addf(&buf, "P %s\n", p->pack_name + objdirlen + 6);
+	}
+	strbuf_addch(&buf, '\n');
+
+	hdr_nocache();
+	send_strbuf("text/plain; charset=utf-8", &buf);
+	strbuf_release(&buf);
+}
+
+static void check_content_type(const char *accepted_type)
+{
+	const char *actual_type = getenv("CONTENT_TYPE");
+
+	if (!actual_type)
+		actual_type = "";
+
+	if (strcmp(actual_type, accepted_type)) {
+		http_status(415, "Unsupported Media Type");
+		hdr_nocache();
+		end_headers();
+		format_write(1,
+			"Expected POST with Content-Type '%s',"
+			" but received '%s' instead.\n",
+			accepted_type, actual_type);
+		exit(0);
+	}
+}
+
+static void service_rpc(char *service_name)
+{
+	const char *argv[] = {NULL, "--stateless-rpc", ".", NULL};
+	struct rpc_service *svc = select_service(service_name);
+	struct strbuf buf = STRBUF_INIT;
+
+	strbuf_reset(&buf);
+	strbuf_addf(&buf, "application/x-git-%s-request", svc->name);
+	check_content_type(buf.buf);
+
+	hdr_nocache();
+
+	strbuf_reset(&buf);
+	strbuf_addf(&buf, "application/x-git-%s-result", svc->name);
+	hdr_str(content_type, buf.buf);
+
+	end_headers();
+
+	argv[0] = svc->name;
+	run_service(argv);
+	strbuf_release(&buf);
+}
+
+static NORETURN void die_webcgi(const char *err, va_list params)
+{
+	static int dead;
+
+	if (!dead) {
+		dead = 1;
+		http_status(500, "Internal Server Error");
+		hdr_nocache();
+		end_headers();
+
+		vreportf("fatal: ", err, params);
+	}
+	exit(0); /* we successfully reported a failure ;-) */
+}
+
+static char* getdir(void)
+{
+	struct strbuf buf = STRBUF_INIT;
+	char *pathinfo = getenv("PATH_INFO");
+	char *root = getenv("GIT_PROJECT_ROOT");
+	char *path = getenv("PATH_TRANSLATED");
+
+	if (root && *root) {
+		if (!pathinfo || !*pathinfo)
+			die("GIT_PROJECT_ROOT is set but PATH_INFO is not");
+		if (daemon_avoid_alias(pathinfo))
+			die("'%s': aliased", pathinfo);
+		end_url_with_slash(&buf, root);
+		if (pathinfo[0] == '/')
+			pathinfo++;
+		strbuf_addstr(&buf, pathinfo);
+		return strbuf_detach(&buf, NULL);
+	} else if (path && *path) {
+		return xstrdup(path);
+	} else
+		die("No GIT_PROJECT_ROOT or PATH_TRANSLATED from server");
+	return NULL;
+}
+
+static struct service_cmd {
+	const char *method;
+	const char *pattern;
+	void (*imp)(char *);
+} services[] = {
+	{"GET", "/HEAD$", get_text_file},
+	{"GET", "/info/refs$", get_info_refs},
+	{"GET", "/objects/info/alternates$", get_text_file},
+	{"GET", "/objects/info/http-alternates$", get_text_file},
+	{"GET", "/objects/info/packs$", get_info_packs},
+	{"GET", "/objects/[0-9a-f]{2}/[0-9a-f]{38}$", get_loose_object},
+	{"GET", "/objects/pack/pack-[0-9a-f]{40}\\.pack$", get_pack_file},
+	{"GET", "/objects/pack/pack-[0-9a-f]{40}\\.idx$", get_idx_file},
+
+	{"POST", "/git-upload-pack$", service_rpc},
+	{"POST", "/git-receive-pack$", service_rpc}
+};
+
+int main(int argc, char **argv)
+{
+	char *method = getenv("REQUEST_METHOD");
+	char *dir;
+	struct service_cmd *cmd = NULL;
+	char *cmd_arg = NULL;
+	int i;
+
+	git_setup_gettext();
+
+	git_extract_argv0_path(argv[0]);
+	set_die_routine(die_webcgi);
+
+	if (!method)
+		die("No REQUEST_METHOD from server");
+	if (!strcmp(method, "HEAD"))
+		method = "GET";
+	dir = getdir();
+
+	for (i = 0; i < ARRAY_SIZE(services); i++) {
+		struct service_cmd *c = &services[i];
+		regex_t re;
+		regmatch_t out[1];
+
+		if (regcomp(&re, c->pattern, REG_EXTENDED))
+			die("Bogus regex in service table: %s", c->pattern);
+		if (!regexec(&re, dir, 1, out, 0)) {
+			size_t n;
+
+			if (strcmp(method, c->method)) {
+				const char *proto = getenv("SERVER_PROTOCOL");
+				if (proto && !strcmp(proto, "HTTP/1.1"))
+					http_status(405, "Method Not Allowed");
+				else
+					http_status(400, "Bad Request");
+				hdr_nocache();
+				end_headers();
+				return 0;
+			}
+
+			cmd = c;
+			n = out[0].rm_eo - out[0].rm_so;
+			cmd_arg = xmalloc(n);
+			memcpy(cmd_arg, dir + out[0].rm_so + 1, n-1);
+			cmd_arg[n-1] = '\0';
+			dir[out[0].rm_so] = 0;
+			break;
+		}
+		regfree(&re);
+	}
+
+	if (!cmd)
+		not_found("Request not supported: '%s'", dir);
+
+	setup_path();
+	if (!enter_repo(dir, 0))
+		not_found("Not a git repository: '%s'", dir);
+	if (!getenv("GIT_HTTP_EXPORT_ALL") &&
+	    access("git-daemon-export-ok", F_OK) )
+		not_found("Repository not exported: '%s'", dir);
+
+	git_config(http_config, NULL);
+	cmd->imp(cmd_arg);
+	return 0;
+}
diff --git a/http-fetch.c b/http-fetch.c
new file mode 100644
index 0000000..ba3ea10
--- /dev/null
+++ b/http-fetch.c
@@ -0,0 +1,98 @@
+#include "cache.h"
+#include "exec_cmd.h"
+#include "http.h"
+#include "walker.h"
+
+static const char http_fetch_usage[] = "git http-fetch "
+"[-c] [-t] [-a] [-v] [--recover] [-w ref] [--stdin] commit-id url";
+
+int main(int argc, const char **argv)
+{
+	struct walker *walker;
+	int commits_on_stdin = 0;
+	int commits;
+	const char **write_ref = NULL;
+	char **commit_id;
+	char *url = NULL;
+	int arg = 1;
+	int rc = 0;
+	int get_tree = 0;
+	int get_history = 0;
+	int get_all = 0;
+	int get_verbosely = 0;
+	int get_recover = 0;
+
+	git_setup_gettext();
+
+	git_extract_argv0_path(argv[0]);
+
+	while (arg < argc && argv[arg][0] == '-') {
+		if (argv[arg][1] == 't') {
+			get_tree = 1;
+		} else if (argv[arg][1] == 'c') {
+			get_history = 1;
+		} else if (argv[arg][1] == 'a') {
+			get_all = 1;
+			get_tree = 1;
+			get_history = 1;
+		} else if (argv[arg][1] == 'v') {
+			get_verbosely = 1;
+		} else if (argv[arg][1] == 'w') {
+			write_ref = &argv[arg + 1];
+			arg++;
+		} else if (argv[arg][1] == 'h') {
+			usage(http_fetch_usage);
+		} else if (!strcmp(argv[arg], "--recover")) {
+			get_recover = 1;
+		} else if (!strcmp(argv[arg], "--stdin")) {
+			commits_on_stdin = 1;
+		}
+		arg++;
+	}
+	if (argc != arg + 2 - commits_on_stdin)
+		usage(http_fetch_usage);
+	if (commits_on_stdin) {
+		commits = walker_targets_stdin(&commit_id, &write_ref);
+	} else {
+		commit_id = (char **) &argv[arg++];
+		commits = 1;
+	}
+
+	if (get_all == 0)
+		warning("http-fetch: use without -a is deprecated.\n"
+			"In a future release, -a will become the default.");
+
+	if (argv[arg])
+		str_end_url_with_slash(argv[arg], &url);
+
+	setup_git_directory();
+
+	git_config(git_default_config, NULL);
+
+	http_init(NULL, url, 0);
+	walker = get_http_walker(url);
+	walker->get_tree = get_tree;
+	walker->get_history = get_history;
+	walker->get_all = get_all;
+	walker->get_verbosely = get_verbosely;
+	walker->get_recover = get_recover;
+
+	rc = walker_fetch(walker, commits, commit_id, write_ref, url);
+
+	if (commits_on_stdin)
+		walker_targets_free(commits, commit_id, write_ref);
+
+	if (walker->corrupt_object_found) {
+		fprintf(stderr,
+"Some loose object were found to be corrupt, but they might be just\n"
+"a false '404 Not Found' error message sent with incorrect HTTP\n"
+"status code.  Suggest running 'git fsck'.\n");
+	}
+
+	walker_free(walker);
+	http_cleanup();
+
+	free(url);
+
+	return rc;
+}
diff --git a/http-push.c b/http-push.c
index 00e83dc..a832ca7 100644
--- a/http-push.c
+++ b/http-push.c
@@ -78,11 +78,11 @@
 static int push_all = MATCH_REFS_NONE;
 static int force_all;
 static int dry_run;
+static int helper_status;
 
 static struct object_list *objects;
 
-struct repo
-{
+struct repo {
 	char *url;
 	char *path;
 	int path_len;
@@ -104,11 +104,10 @@
 	RUN_PUT,
 	RUN_MOVE,
 	ABORTED,
-	COMPLETE,
+	COMPLETE
 };
 
-struct transfer_request
-{
+struct transfer_request {
 	struct object *obj;
 	char *url;
 	char *dest;
@@ -126,8 +125,7 @@
 
 static struct transfer_request *request_queue_head;
 
-struct xml_ctx
-{
+struct xml_ctx {
 	char *name;
 	int len;
 	char *cdata;
@@ -135,8 +133,7 @@
 	void *userData;
 };
 
-struct remote_lock
-{
+struct remote_lock {
 	char *url;
 	char *owner;
 	char *token;
@@ -155,8 +152,7 @@
 /* Flags that remote_ls passes to callback functions */
 #define IS_DIR (1u << 0)
 
-struct remote_ls_ctx
-{
+struct remote_ls_ctx {
 	char *path;
 	void (*userFunc)(struct remote_ls_ctx *ls);
 	void *userData;
@@ -173,7 +169,7 @@
 	DAV_HEADER_TIMEOUT = (1u << 2)
 };
 
-static char *xml_entities(char *s)
+static char *xml_entities(const char *s)
 {
 	struct strbuf buf = STRBUF_INIT;
 	while (*s) {
@@ -201,6 +197,34 @@
 	return strbuf_detach(&buf, NULL);
 }
 
+static void curl_setup_http_get(CURL *curl, const char *url,
+		const char *custom_req)
+{
+	curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
+	curl_easy_setopt(curl, CURLOPT_URL, url);
+	curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, custom_req);
+	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite_null);
+}
+
+static void curl_setup_http(CURL *curl, const char *url,
+		const char *custom_req, struct buffer *buffer,
+		curl_write_callback write_fn)
+{
+	curl_easy_setopt(curl, CURLOPT_PUT, 1);
+	curl_easy_setopt(curl, CURLOPT_URL, url);
+	curl_easy_setopt(curl, CURLOPT_INFILE, buffer);
+	curl_easy_setopt(curl, CURLOPT_INFILESIZE, buffer->buf.len);
+	curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
+#ifndef NO_CURL_IOCTL
+	curl_easy_setopt(curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer);
+	curl_easy_setopt(curl, CURLOPT_IOCTLDATA, &buffer);
+#endif
+	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_fn);
+	curl_easy_setopt(curl, CURLOPT_NOBODY, 0);
+	curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, custom_req);
+	curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
+}
+
 static struct curl_slist *get_dav_token_headers(struct remote_lock *lock, enum dav_header_flag options)
 {
 	struct strbuf buf = STRBUF_INIT;
@@ -276,11 +300,8 @@
 	slot = get_active_slot();
 	slot->callback_func = process_response;
 	slot->callback_data = request;
-	curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1); /* undo PUT setup */
-	curl_easy_setopt(slot->curl, CURLOPT_URL, request->url);
+	curl_setup_http_get(slot->curl, request->url, DAV_MKCOL);
 	curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, request->errorstr);
-	curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_MKCOL);
-	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
 
 	if (start_active_slot(slot)) {
 		request->slot = slot;
@@ -356,15 +377,15 @@
 	unsigned long len;
 	int hdrlen;
 	ssize_t size;
-	z_stream stream;
+	git_zstream stream;
 
 	unpacked = read_sha1_file(request->obj->sha1, &type, &len);
 	hdrlen = sprintf(hdr, "%s %lu", typename(type), len) + 1;
 
 	/* Set it up */
 	memset(&stream, 0, sizeof(stream));
-	deflateInit(&stream, zlib_compression_level);
-	size = deflateBound(&stream, len + hdrlen);
+	git_deflate_init(&stream, zlib_compression_level);
+	size = git_deflate_bound(&stream, len + hdrlen);
 	strbuf_init(&request->buffer.buf, size);
 	request->buffer.posn = 0;
 
@@ -375,15 +396,15 @@
 	/* First header.. */
 	stream.next_in = (void *)hdr;
 	stream.avail_in = hdrlen;
-	while (deflate(&stream, 0) == Z_OK)
-		/* nothing */;
+	while (git_deflate(&stream, 0) == Z_OK)
+		; /* nothing */
 
 	/* Then the data itself.. */
 	stream.next_in = unpacked;
 	stream.avail_in = len;
-	while (deflate(&stream, Z_FINISH) == Z_OK)
-		/* nothing */;
-	deflateEnd(&stream);
+	while (git_deflate(&stream, Z_FINISH) == Z_OK)
+		; /* nothing */
+	git_deflate_end(&stream);
 	free(unpacked);
 
 	request->buffer.buf.len = stream.total_out;
@@ -399,19 +420,8 @@
 	slot = get_active_slot();
 	slot->callback_func = process_response;
 	slot->callback_data = request;
-	curl_easy_setopt(slot->curl, CURLOPT_INFILE, &request->buffer);
-	curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, request->buffer.buf.len);
-	curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
-#ifndef NO_CURL_IOCTL
-	curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer);
-	curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, &request->buffer);
-#endif
-	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
-	curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PUT);
-	curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
-	curl_easy_setopt(slot->curl, CURLOPT_PUT, 1);
-	curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
-	curl_easy_setopt(slot->curl, CURLOPT_URL, request->url);
+	curl_setup_http(slot->curl, request->url, DAV_PUT,
+			&request->buffer, fwrite_null);
 
 	if (start_active_slot(slot)) {
 		request->slot = slot;
@@ -431,13 +441,10 @@
 	slot = get_active_slot();
 	slot->callback_func = process_response;
 	slot->callback_data = request;
-	curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1); /* undo PUT setup */
-	curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_MOVE);
+	curl_setup_http_get(slot->curl, request->url, DAV_MOVE);
 	dav_headers = curl_slist_append(dav_headers, request->dest);
 	dav_headers = curl_slist_append(dav_headers, "Overwrite: T");
 	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
-	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
-	curl_easy_setopt(slot->curl, CURLOPT_URL, request->url);
 
 	if (start_active_slot(slot)) {
 		request->slot = slot;
@@ -462,10 +469,7 @@
 
 	slot = get_active_slot();
 	slot->results = &results;
-	curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
-	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
-	curl_easy_setopt(slot->curl, CURLOPT_URL, lock->url);
-	curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_LOCK);
+	curl_setup_http_get(slot->curl, lock->url, DAV_LOCK);
 	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
 
 	if (start_active_slot(slot)) {
@@ -604,7 +608,7 @@
 			preq = (struct http_pack_request *)request->userData;
 
 			if (preq) {
-				if (finish_http_pack_request(preq) > 0)
+				if (finish_http_pack_request(preq) == 0)
 					fail = 0;
 				release_http_pack_request(preq);
 			}
@@ -801,7 +805,7 @@
 	}
 }
 
-static void one_remote_ref(char *refname);
+static void one_remote_ref(const char *refname);
 
 static void
 xml_start_tag(void *userData, const char *name, const char **atts)
@@ -880,10 +884,7 @@
 		ep[1] = '\0';
 		slot = get_active_slot();
 		slot->results = &results;
-		curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
-		curl_easy_setopt(slot->curl, CURLOPT_URL, url);
-		curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_MKCOL);
-		curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
+		curl_setup_http_get(slot->curl, url, DAV_MKCOL);
 		if (start_active_slot(slot)) {
 			run_active_slot(slot);
 			if (results.curl_result != CURLE_OK &&
@@ -903,7 +904,7 @@
 		ep = strchr(ep + 1, '/');
 	}
 
-	escaped = xml_entities(git_default_email);
+	escaped = xml_entities(ident_default_email());
 	strbuf_addf(&out_buffer.buf, LOCK_REQUEST, escaped);
 	free(escaped);
 
@@ -913,19 +914,9 @@
 
 	slot = get_active_slot();
 	slot->results = &results;
-	curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
-	curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.buf.len);
-	curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
-#ifndef NO_CURL_IOCTL
-	curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer);
-	curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, &out_buffer);
-#endif
-	curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer);
-	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
-	curl_easy_setopt(slot->curl, CURLOPT_URL, url);
-	curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
-	curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_LOCK);
+	curl_setup_http(slot->curl, url, DAV_LOCK, &out_buffer, fwrite_buffer);
 	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
+	curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer);
 
 	lock = xcalloc(1, sizeof(*lock));
 	lock->timeout = -1;
@@ -991,9 +982,7 @@
 
 	slot = get_active_slot();
 	slot->results = &results;
-	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
-	curl_easy_setopt(slot->curl, CURLOPT_URL, lock->url);
-	curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_UNLOCK);
+	curl_setup_http_get(slot->curl, lock->url, DAV_UNLOCK);
 	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
 
 	if (start_active_slot(slot)) {
@@ -1089,6 +1078,10 @@
 	if (tag_closed) {
 		if (!strcmp(ctx->name, DAV_PROPFIND_RESP) && ls->dentry_name) {
 			if (ls->dentry_flags & IS_DIR) {
+
+				/* ensure collection names end with slash */
+				str_end_url_with_slash(ls->dentry_name, &ls->dentry_name);
+
 				if (ls->flags & PROCESS_DIRS) {
 					ls->userFunc(ls);
 				}
@@ -1111,8 +1104,16 @@
 				}
 			}
 			if (path) {
-				path += repo->path_len;
-				ls->dentry_name = xstrdup(path);
+				const char *url = repo->url;
+				if (repo->path)
+					url = repo->path;
+				if (strncmp(path, url, repo->path_len))
+					error("Parsed path '%s' does not match url: '%s'",
+					      path, url);
+				else {
+					path += repo->path_len;
+					ls->dentry_name = xstrdup(path);
+				}
 			}
 		} else if (!strcmp(ctx->name, DAV_PROPFIND_COLLECTION)) {
 			ls->dentry_flags |= IS_DIR;
@@ -1159,19 +1160,10 @@
 
 	slot = get_active_slot();
 	slot->results = &results;
-	curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
-	curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.buf.len);
-	curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
-#ifndef NO_CURL_IOCTL
-	curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer);
-	curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, &out_buffer);
-#endif
-	curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer);
-	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
-	curl_easy_setopt(slot->curl, CURLOPT_URL, url);
-	curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
-	curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PROPFIND);
+	curl_setup_http(slot->curl, url, DAV_PROPFIND,
+			&out_buffer, fwrite_buffer);
 	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
+	curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer);
 
 	if (start_active_slot(slot)) {
 		run_active_slot(slot);
@@ -1242,19 +1234,10 @@
 
 	slot = get_active_slot();
 	slot->results = &results;
-	curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
-	curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.buf.len);
-	curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
-#ifndef NO_CURL_IOCTL
-	curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer);
-	curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, &out_buffer);
-#endif
-	curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer);
-	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
-	curl_easy_setopt(slot->curl, CURLOPT_URL, repo->url);
-	curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
-	curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PROPFIND);
+	curl_setup_http(slot->curl, repo->url, DAV_PROPFIND,
+			&out_buffer, fwrite_buffer);
 	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
+	curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer);
 
 	if (start_active_slot(slot)) {
 		run_active_slot(slot);
@@ -1428,19 +1411,9 @@
 
 	slot = get_active_slot();
 	slot->results = &results;
-	curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
-	curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.buf.len);
-	curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
-#ifndef NO_CURL_IOCTL
-	curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer);
-	curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, &out_buffer);
-#endif
-	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
-	curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PUT);
+	curl_setup_http(slot->curl, lock->url, DAV_PUT,
+			&out_buffer, fwrite_null);
 	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
-	curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
-	curl_easy_setopt(slot->curl, CURLOPT_PUT, 1);
-	curl_easy_setopt(slot->curl, CURLOPT_URL, lock->url);
 
 	if (start_active_slot(slot)) {
 		run_active_slot(slot);
@@ -1463,7 +1436,7 @@
 
 static struct ref *remote_refs;
 
-static void one_remote_ref(char *refname)
+static void one_remote_ref(const char *refname)
 {
 	struct ref *ref;
 	struct object *obj;
@@ -1564,19 +1537,9 @@
 
 		slot = get_active_slot();
 		slot->results = &results;
-		curl_easy_setopt(slot->curl, CURLOPT_INFILE, &buffer);
-		curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, buffer.buf.len);
-		curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
-#ifndef NO_CURL_IOCTL
-		curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer);
-		curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, &buffer);
-#endif
-		curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
-		curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PUT);
+		curl_setup_http(slot->curl, lock->url, DAV_PUT,
+				&buffer, fwrite_null);
 		curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
-		curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
-		curl_easy_setopt(slot->curl, CURLOPT_PUT, 1);
-		curl_easy_setopt(slot->curl, CURLOPT_URL, lock->url);
 
 		if (start_active_slot(slot)) {
 			run_active_slot(slot);
@@ -1643,16 +1606,16 @@
 	strbuf_release(&buffer);
 }
 
-static int verify_merge_base(unsigned char *head_sha1, unsigned char *branch_sha1)
+static int verify_merge_base(unsigned char *head_sha1, struct ref *remote)
 {
-	struct commit *head = lookup_commit(head_sha1);
-	struct commit *branch = lookup_commit(branch_sha1);
+	struct commit *head = lookup_commit_or_die(head_sha1, "HEAD");
+	struct commit *branch = lookup_commit_or_die(remote->old_sha1, remote->name);
 	struct commit_list *merge_bases = get_merge_bases(head, branch, 1);
 
 	return (merge_bases && !merge_bases->next && merge_bases->item == branch);
 }
 
-static int delete_remote_branch(char *pattern, int force)
+static int delete_remote_branch(const char *pattern, int force)
 {
 	struct ref *refs = remote_refs;
 	struct ref *remote_ref = NULL;
@@ -1692,7 +1655,7 @@
 		return error("Remote HEAD is not a symref");
 
 	/* Remote branch must not be the remote HEAD */
-	for (i=0; symref && i<MAXDEPTH; i++) {
+	for (i = 0; symref && i < MAXDEPTH; i++) {
 		if (!strcmp(remote_ref->name, symref))
 			return error("Remote branch %s is the current HEAD",
 				     remote_ref->name);
@@ -1717,7 +1680,7 @@
 			return error("Remote branch %s resolves to object %s\nwhich does not exist locally, perhaps you need to fetch?", remote_ref->name, sha1_to_hex(remote_ref->old_sha1));
 
 		/* Remote branch must be an ancestor of remote HEAD */
-		if (!verify_merge_base(head_sha1, remote_ref->old_sha1)) {
+		if (!verify_merge_base(head_sha1, remote_ref)) {
 			return error("The branch '%s' is not an ancestor "
 				     "of your current HEAD.\n"
 				     "If you are sure you want to delete it,"
@@ -1734,15 +1697,12 @@
 	sprintf(url, "%s%s", repo->url, remote_ref->name);
 	slot = get_active_slot();
 	slot->results = &results;
-	curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
-	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
-	curl_easy_setopt(slot->curl, CURLOPT_URL, url);
-	curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_DELETE);
+	curl_setup_http_get(slot->curl, url, DAV_DELETE);
 	if (start_active_slot(slot)) {
 		run_active_slot(slot);
 		free(url);
 		if (results.curl_result != CURLE_OK)
-			return error("DELETE request failed (%d/%ld)\n",
+			return error("DELETE request failed (%d/%ld)",
 				     results.curl_result, results.http_code);
 	} else {
 		free(url);
@@ -1787,13 +1747,11 @@
 	int i;
 	int new_refs;
 	struct ref *ref, *local_refs;
-	struct remote *remote;
-	char *rewritten_url = NULL;
+
+	git_setup_gettext();
 
 	git_extract_argv0_path(argv[0]);
 
-	setup_git_directory();
-
 	repo = xcalloc(sizeof(*repo), 1);
 
 	argv++;
@@ -1813,6 +1771,10 @@
 				dry_run = 1;
 				continue;
 			}
+			if (!strcmp(arg, "--helper-status")) {
+				helper_status = 1;
+				continue;
+			}
 			if (!strcmp(arg, "--verbose")) {
 				push_verbosely = 1;
 				http_is_verbose = 1;
@@ -1827,11 +1789,13 @@
 				force_delete = 1;
 				continue;
 			}
+			if (!strcmp(arg, "-h"))
+				usage(http_push_usage);
 		}
 		if (!repo->url) {
 			char *path = strstr(arg, "//");
-			repo->url = arg;
-			repo->path_len = strlen(arg);
+			str_end_url_with_slash(arg, &repo->url);
+			repo->path_len = strlen(repo->url);
 			if (path) {
 				repo->path = strchr(path+2, '/');
 				if (repo->path)
@@ -1854,25 +1818,11 @@
 	if (delete_branch && nr_refspec != 1)
 		die("You must specify only one branch name when deleting a remote branch");
 
+	setup_git_directory();
+
 	memset(remote_dir_exists, -1, 256);
 
-	/*
-	 * Create a minimum remote by hand to give to http_init(),
-	 * primarily to allow it to look at the URL.
-	 */
-	remote = xcalloc(sizeof(*remote), 1);
-	ALLOC_GROW(remote->url, remote->url_nr + 1, remote->url_alloc);
-	remote->url[remote->url_nr++] = repo->url;
-	http_init(remote);
-
-	if (repo->url && repo->url[strlen(repo->url)-1] != '/') {
-		rewritten_url = xmalloc(strlen(repo->url)+2);
-		strcpy(rewritten_url, repo->url);
-		strcat(rewritten_url, "/");
-		repo->path = rewritten_url + (repo->path - repo->url);
-		repo->path_len++;
-		repo->url = rewritten_url;
-	}
+	http_init(NULL, repo->url, 1);
 
 #ifdef USE_CURL_MULTI
 	is_running_queue = 0;
@@ -1911,20 +1861,25 @@
 
 	/* Remove a remote branch if -d or -D was specified */
 	if (delete_branch) {
-		if (delete_remote_branch(refspec[0], force_delete) == -1)
+		if (delete_remote_branch(refspec[0], force_delete) == -1) {
 			fprintf(stderr, "Unable to delete remote branch %s\n",
 				refspec[0]);
+			if (helper_status)
+				printf("error %s cannot remove\n", refspec[0]);
+		}
 		goto cleanup;
 	}
 
 	/* match them up */
-	if (match_refs(local_refs, &remote_refs,
-		       nr_refspec, (const char **) refspec, push_all)) {
+	if (match_push_refs(local_refs, &remote_refs,
+			    nr_refspec, (const char **) refspec, push_all)) {
 		rc = -1;
 		goto cleanup;
 	}
 	if (!remote_refs) {
 		fprintf(stderr, "No refs in common and none specified; doing nothing.\n");
+		if (helper_status)
+			printf("error null no match\n");
 		rc = 0;
 		goto cleanup;
 	}
@@ -1942,15 +1897,21 @@
 		if (is_null_sha1(ref->peer_ref->new_sha1)) {
 			if (delete_remote_branch(ref->name, 1) == -1) {
 				error("Could not remove %s", ref->name);
+				if (helper_status)
+					printf("error %s cannot remove\n", ref->name);
 				rc = -4;
 			}
+			else if (helper_status)
+				printf("ok %s\n", ref->name);
 			new_refs++;
 			continue;
 		}
 
 		if (!hashcmp(ref->old_sha1, ref->peer_ref->new_sha1)) {
-			if (push_verbosely || 1)
+			if (push_verbosely)
 				fprintf(stderr, "'%s': up-to-date\n", ref->name);
+			if (helper_status)
+				printf("ok %s up to date\n", ref->name);
 			continue;
 		}
 
@@ -1974,6 +1935,8 @@
 				      "need to pull first?",
 				      ref->name,
 				      ref->peer_ref->name);
+				if (helper_status)
+					printf("error %s non-fast forward\n", ref->name);
 				rc = -2;
 				continue;
 			}
@@ -1987,14 +1950,19 @@
 		if (strcmp(ref->name, ref->peer_ref->name))
 			fprintf(stderr, " using '%s'", ref->peer_ref->name);
 		fprintf(stderr, "\n  from %s\n  to   %s\n", old_hex, new_hex);
-		if (dry_run)
+		if (dry_run) {
+			if (helper_status)
+				printf("ok %s\n", ref->name);
 			continue;
+		}
 
 		/* Lock remote branch ref */
 		ref_lock = lock_remote(ref->name, LOCK_TIME);
 		if (ref_lock == NULL) {
 			fprintf(stderr, "Unable to lock remote branch %s\n",
 				ref->name);
+			if (helper_status)
+				printf("error %s lock error\n", ref->name);
 			rc = 1;
 			continue;
 		}
@@ -2045,6 +2013,8 @@
 
 		if (!rc)
 			fprintf(stderr, "    done\n");
+		if (helper_status)
+			printf("%s %s\n", !rc ? "ok" : "error", ref->name);
 		unlock_remote(ref_lock);
 		check_locks();
 	}
@@ -2061,7 +2031,6 @@
 	}
 
  cleanup:
-	free(rewritten_url);
 	if (info_ref_lock)
 		unlock_remote(info_ref_lock);
 	free(repo);
diff --git a/http-walker.c b/http-walker.c
index 700bc13..51a906e 100644
--- a/http-walker.c
+++ b/http-walker.c
@@ -3,8 +3,7 @@
 #include "walker.h"
 #include "http.h"
 
-struct alt_base
-{
+struct alt_base {
 	char *base;
 	int got_indices;
 	struct packed_git *packs;
@@ -15,11 +14,10 @@
 	WAITING,
 	ABORTED,
 	ACTIVE,
-	COMPLETE,
+	COMPLETE
 };
 
-struct object_request
-{
+struct object_request {
 	struct walker *walker;
 	unsigned char sha1[20];
 	struct alt_base *repo;
@@ -187,7 +185,7 @@
 	struct active_request_slot *slot = alt_req->slot;
 	struct alt_base *tail = cdata->alt;
 	const char *base = alt_req->base;
-	static const char null_byte = '\0';
+	const char null_byte = '\0';
 	char *data;
 	int i = 0;
 
@@ -220,7 +218,7 @@
 		}
 	}
 
-	fwrite_buffer(&null_byte, 1, 1, alt_req->buffer);
+	fwrite_buffer((char *)&null_byte, 1, 1, alt_req->buffer);
 	alt_req->buffer->len--;
 	data = alt_req->buffer->buf;
 
@@ -510,7 +508,7 @@
 		ret = error("File %s has bad hash", hex);
 	} else if (req->rename < 0) {
 		ret = error("unable to write sha1 filename %s",
-			    req->filename);
+			    sha1_file_name(req->sha1));
 	}
 
 	release_http_object_request(req);
@@ -543,17 +541,30 @@
 
 static void cleanup(struct walker *walker)
 {
-	http_cleanup();
+	struct walker_data *data = walker->data;
+	struct alt_base *alt, *alt_next;
+
+	if (data) {
+		alt = data->alt;
+		while (alt) {
+			alt_next = alt->next;
+
+			free(alt->base);
+			free(alt);
+
+			alt = alt_next;
+		}
+		free(data);
+		walker->data = NULL;
+	}
 }
 
-struct walker *get_http_walker(const char *url, struct remote *remote)
+struct walker *get_http_walker(const char *url)
 {
 	char *s;
 	struct walker_data *data = xmalloc(sizeof(struct walker_data));
 	struct walker *walker = xmalloc(sizeof(struct walker));
 
-	http_init(remote);
-
 	data->alt = xmalloc(sizeof(*data->alt));
 	data->alt->base = xmalloc(strlen(url) + 1);
 	strcpy(data->alt->base, url);
diff --git a/http.c b/http.c
index a2720d5..b61ac85 100644
--- a/http.c
+++ b/http.c
@@ -1,10 +1,21 @@
 #include "http.h"
 #include "pack.h"
+#include "sideband.h"
+#include "run-command.h"
+#include "url.h"
+#include "credential.h"
+#include "version.h"
 
-int data_received;
 int active_requests;
 int http_is_verbose;
+size_t http_post_buffer = 16 * LARGE_PACKET_MAX;
 
+#if LIBCURL_VERSION_NUM >= 0x070a06
+#define LIBCURL_CAN_HANDLE_AUTH_ANY
+#endif
+
+static int min_curl_sessions = 1;
+static int curl_session_count;
 #ifdef USE_CURL_MULTI
 static int max_requests = -1;
 static CURLM *curlm;
@@ -31,7 +42,10 @@
 static long curl_low_speed_time = -1;
 static int curl_ftp_no_epsv;
 static const char *curl_http_proxy;
-static char *user_name, *user_pass;
+static const char *curl_cookie_file;
+static struct credential http_auth = CREDENTIAL_INIT;
+static int http_proactive_auth;
+static const char *user_agent;
 
 #if LIBCURL_VERSION_NUM >= 0x071700
 /* Use CURLOPT_KEYPASSWD as is */
@@ -41,7 +55,7 @@
 #define CURLOPT_KEYPASSWD CURLOPT_SSLCERTPASSWD
 #endif
 
-static char *ssl_cert_password;
+static struct credential cert_auth = CREDENTIAL_INIT;
 static int ssl_cert_password_required;
 
 static struct curl_slist *pragma_header;
@@ -49,7 +63,7 @@
 
 static struct active_request_slot *active_queue_head;
 
-size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb, void *buffer_)
+size_t fread_buffer(char *ptr, size_t eltsize, size_t nmemb, void *buffer_)
 {
 	size_t size = eltsize * nmemb;
 	struct buffer *buffer = buffer_;
@@ -81,24 +95,20 @@
 }
 #endif
 
-size_t fwrite_buffer(const void *ptr, size_t eltsize, size_t nmemb, void *buffer_)
+size_t fwrite_buffer(char *ptr, size_t eltsize, size_t nmemb, void *buffer_)
 {
 	size_t size = eltsize * nmemb;
 	struct strbuf *buffer = buffer_;
 
 	strbuf_add(buffer, ptr, size);
-	data_received++;
 	return size;
 }
 
-size_t fwrite_null(const void *ptr, size_t eltsize, size_t nmemb, void *strbuf)
+size_t fwrite_null(char *ptr, size_t eltsize, size_t nmemb, void *strbuf)
 {
-	data_received++;
 	return eltsize * nmemb;
 }
 
-static void finish_active_slot(struct active_request_slot *slot);
-
 #ifdef USE_CURL_MULTI
 static void process_curl_messages(void)
 {
@@ -152,6 +162,14 @@
 			ssl_cert_password_required = 1;
 		return 0;
 	}
+	if (!strcmp("http.minsessions", var)) {
+		min_curl_sessions = git_config_int(var, value);
+#ifndef USE_CURL_MULTI
+		if (min_curl_sessions > 1)
+			min_curl_sessions = 1;
+#endif
+		return 0;
+	}
 #ifdef USE_CURL_MULTI
 	if (!strcmp("http.maxrequests", var)) {
 		max_requests = git_config_int(var, value);
@@ -174,36 +192,54 @@
 	if (!strcmp("http.proxy", var))
 		return git_config_string(&curl_http_proxy, var, value);
 
+	if (!strcmp("http.cookiefile", var))
+		return git_config_string(&curl_cookie_file, var, value);
+
+	if (!strcmp("http.postbuffer", var)) {
+		http_post_buffer = git_config_int(var, value);
+		if (http_post_buffer < LARGE_PACKET_MAX)
+			http_post_buffer = LARGE_PACKET_MAX;
+		return 0;
+	}
+
+	if (!strcmp("http.useragent", var))
+		return git_config_string(&user_agent, var, value);
+
 	/* Fall back on the default ones */
 	return git_default_config(var, value, cb);
 }
 
 static void init_curl_http_auth(CURL *result)
 {
-	if (user_name) {
-		struct strbuf up = STRBUF_INIT;
-		if (!user_pass)
-			user_pass = xstrdup(getpass("Password: "));
-		strbuf_addf(&up, "%s:%s", user_name, user_pass);
-		curl_easy_setopt(result, CURLOPT_USERPWD,
-				 strbuf_detach(&up, NULL));
+	if (!http_auth.username)
+		return;
+
+	credential_fill(&http_auth);
+
+#if LIBCURL_VERSION_NUM >= 0x071301
+	curl_easy_setopt(result, CURLOPT_USERNAME, http_auth.username);
+	curl_easy_setopt(result, CURLOPT_PASSWORD, http_auth.password);
+#else
+	{
+		static struct strbuf up = STRBUF_INIT;
+		strbuf_reset(&up);
+		strbuf_addf(&up, "%s:%s",
+			    http_auth.username, http_auth.password);
+		curl_easy_setopt(result, CURLOPT_USERPWD, up.buf);
 	}
+#endif
 }
 
 static int has_cert_password(void)
 {
-	if (ssl_cert_password != NULL)
-		return 1;
 	if (ssl_cert == NULL || ssl_cert_password_required != 1)
 		return 0;
-	/* Only prompt the user once. */
-	ssl_cert_password_required = -1;
-	ssl_cert_password = getpass("Certificate Password: ");
-	if (ssl_cert_password != NULL) {
-		ssl_cert_password = xstrdup(ssl_cert_password);
-		return 1;
-	} else
-		return 0;
+	if (!cert_auth.password) {
+		cert_auth.protocol = xstrdup("cert");
+		cert_auth.path = xstrdup(ssl_cert);
+		credential_fill(&cert_auth);
+	}
+	return 1;
 }
 
 static CURL *get_curl_handle(void)
@@ -223,13 +259,17 @@
 #if LIBCURL_VERSION_NUM >= 0x070907
 	curl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
 #endif
+#ifdef LIBCURL_CAN_HANDLE_AUTH_ANY
+	curl_easy_setopt(result, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
+#endif
 
-	init_curl_http_auth(result);
+	if (http_proactive_auth)
+		init_curl_http_auth(result);
 
 	if (ssl_cert != NULL)
 		curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert);
 	if (has_cert_password())
-		curl_easy_setopt(result, CURLOPT_KEYPASSWD, ssl_cert_password);
+		curl_easy_setopt(result, CURLOPT_KEYPASSWD, cert_auth.password);
 #if LIBCURL_VERSION_NUM >= 0x070903
 	if (ssl_key != NULL)
 		curl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key);
@@ -250,61 +290,29 @@
 	}
 
 	curl_easy_setopt(result, CURLOPT_FOLLOWLOCATION, 1);
+#if LIBCURL_VERSION_NUM >= 0x071301
+	curl_easy_setopt(result, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL);
+#elif LIBCURL_VERSION_NUM >= 0x071101
+	curl_easy_setopt(result, CURLOPT_POST301, 1);
+#endif
 
 	if (getenv("GIT_CURL_VERBOSE"))
 		curl_easy_setopt(result, CURLOPT_VERBOSE, 1);
 
-	curl_easy_setopt(result, CURLOPT_USERAGENT, GIT_USER_AGENT);
+	curl_easy_setopt(result, CURLOPT_USERAGENT,
+		user_agent ? user_agent : git_user_agent());
 
 	if (curl_ftp_no_epsv)
 		curl_easy_setopt(result, CURLOPT_FTP_USE_EPSV, 0);
 
-	if (curl_http_proxy)
+	if (curl_http_proxy) {
 		curl_easy_setopt(result, CURLOPT_PROXY, curl_http_proxy);
+		curl_easy_setopt(result, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
+	}
 
 	return result;
 }
 
-static void http_auth_init(const char *url)
-{
-	char *at, *colon, *cp, *slash;
-	int len;
-
-	cp = strstr(url, "://");
-	if (!cp)
-		return;
-
-	/*
-	 * Ok, the URL looks like "proto://something".  Which one?
-	 * "proto://<user>:<pass>@<host>/...",
-	 * "proto://<user>@<host>/...", or just
-	 * "proto://<host>/..."?
-	 */
-	cp += 3;
-	at = strchr(cp, '@');
-	colon = strchr(cp, ':');
-	slash = strchrnul(cp, '/');
-	if (!at || slash <= at)
-		return; /* No credentials */
-	if (!colon || at <= colon) {
-		/* Only username */
-		len = at - cp;
-		user_name = xmalloc(len + 1);
-		memcpy(user_name, cp, len);
-		user_name[len] = '\0';
-		user_pass = NULL;
-	} else {
-		len = colon - cp;
-		user_name = xmalloc(len + 1);
-		memcpy(user_name, cp, len);
-		user_name[len] = '\0';
-		len = at - (colon + 1);
-		user_pass = xmalloc(len + 1);
-		memcpy(user_pass, colon + 1, len);
-		user_pass[len] = '\0';
-	}
-}
-
 static void set_from_env(const char **var, const char *envname)
 {
 	const char *val = getenv(envname);
@@ -312,7 +320,7 @@
 		*var = val;
 }
 
-void http_init(struct remote *remote)
+void http_init(struct remote *remote, const char *url, int proactive_auth)
 {
 	char *low_speed_limit;
 	char *low_speed_time;
@@ -323,6 +331,8 @@
 
 	curl_global_init(CURL_GLOBAL_ALL);
 
+	http_proactive_auth = proactive_auth;
+
 	if (remote && remote->http_proxy)
 		curl_http_proxy = xstrdup(remote->http_proxy);
 
@@ -355,6 +365,8 @@
 #endif
 	set_from_env(&ssl_cainfo, "GIT_SSL_CAINFO");
 
+	set_from_env(&user_agent, "GIT_HTTP_USER_AGENT");
+
 	low_speed_limit = getenv("GIT_HTTP_LOW_SPEED_LIMIT");
 	if (low_speed_limit != NULL)
 		curl_low_speed_limit = strtol(low_speed_limit, NULL, 10);
@@ -365,6 +377,7 @@
 	if (curl_ssl_verify == -1)
 		curl_ssl_verify = 1;
 
+	curl_session_count = 0;
 #ifdef USE_CURL_MULTI
 	if (max_requests < 1)
 		max_requests = DEFAULT_MAX_REQUESTS;
@@ -373,11 +386,11 @@
 	if (getenv("GIT_CURL_FTP_NO_EPSV"))
 		curl_ftp_no_epsv = 1;
 
-	if (remote && remote->url && remote->url[0]) {
-		http_auth_init(remote->url[0]);
+	if (url) {
+		credential_from_url(&http_auth, url);
 		if (!ssl_cert_password_required &&
 		    getenv("GIT_SSL_CERT_PASSWORD_PROTECTED") &&
-		    !prefixcmp(remote->url[0], "https://"))
+		    !prefixcmp(url, "https://"))
 			ssl_cert_password_required = 1;
 	}
 
@@ -423,10 +436,10 @@
 		curl_http_proxy = NULL;
 	}
 
-	if (ssl_cert_password != NULL) {
-		memset(ssl_cert_password, 0, strlen(ssl_cert_password));
-		free(ssl_cert_password);
-		ssl_cert_password = NULL;
+	if (cert_auth.password != NULL) {
+		memset(cert_auth.password, 0, strlen(cert_auth.password));
+		free(cert_auth.password);
+		cert_auth.password = NULL;
 	}
 	ssl_cert_password_required = 0;
 }
@@ -473,22 +486,26 @@
 #else
 		slot->curl = curl_easy_duphandle(curl_default);
 #endif
+		curl_session_count++;
 	}
 
 	active_requests++;
 	slot->in_use = 1;
-	slot->local = NULL;
 	slot->results = NULL;
 	slot->finished = NULL;
 	slot->callback_data = NULL;
 	slot->callback_func = NULL;
+	curl_easy_setopt(slot->curl, CURLOPT_COOKIEFILE, curl_cookie_file);
 	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, pragma_header);
 	curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, curl_errorstr);
 	curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, NULL);
 	curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, NULL);
 	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, NULL);
+	curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, NULL);
 	curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 0);
 	curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
+	if (http_auth.password)
+		init_curl_http_auth(slot->curl);
 
 	return slot;
 }
@@ -551,9 +568,11 @@
 	}
 
 	while (slot != NULL) {
-		if (!slot->in_use && slot->curl != NULL) {
+		if (!slot->in_use && slot->curl != NULL
+			&& curl_session_count > min_curl_sessions) {
 			curl_easy_cleanup(slot->curl);
 			slot->curl = NULL;
+			curl_session_count--;
 		}
 		slot = slot->next;
 	}
@@ -577,8 +596,6 @@
 void run_active_slot(struct active_request_slot *slot)
 {
 #ifdef USE_CURL_MULTI
-	long last_pos = 0;
-	long current_pos;
 	fd_set readfds;
 	fd_set writefds;
 	fd_set excfds;
@@ -588,25 +605,33 @@
 
 	slot->finished = &finished;
 	while (!finished) {
-		data_received = 0;
 		step_active_slots();
 
-		if (!data_received && slot->local != NULL) {
-			current_pos = ftell(slot->local);
-			if (current_pos > last_pos)
-				data_received++;
-			last_pos = current_pos;
-		}
+		if (slot->in_use) {
+#if LIBCURL_VERSION_NUM >= 0x070f04
+			long curl_timeout;
+			curl_multi_timeout(curlm, &curl_timeout);
+			if (curl_timeout == 0) {
+				continue;
+			} else if (curl_timeout == -1) {
+				select_timeout.tv_sec  = 0;
+				select_timeout.tv_usec = 50000;
+			} else {
+				select_timeout.tv_sec  =  curl_timeout / 1000;
+				select_timeout.tv_usec = (curl_timeout % 1000) * 1000;
+			}
+#else
+			select_timeout.tv_sec  = 0;
+			select_timeout.tv_usec = 50000;
+#endif
 
-		if (slot->in_use && !data_received) {
-			max_fd = 0;
+			max_fd = -1;
 			FD_ZERO(&readfds);
 			FD_ZERO(&writefds);
 			FD_ZERO(&excfds);
-			select_timeout.tv_sec = 0;
-			select_timeout.tv_usec = 50000;
-			select(max_fd, &readfds, &writefds,
-			       &excfds, &select_timeout);
+			curl_multi_fdset(curlm, &readfds, &writefds, &excfds, &max_fd);
+
+			select(max_fd+1, &readfds, &writefds, &excfds, &select_timeout);
 		}
 	}
 #else
@@ -623,22 +648,23 @@
 	slot->in_use = 0;
 }
 
-void release_active_slot(struct active_request_slot *slot)
+static void release_active_slot(struct active_request_slot *slot)
 {
 	closedown_active_slot(slot);
-	if (slot->curl) {
+	if (slot->curl && curl_session_count > min_curl_sessions) {
 #ifdef USE_CURL_MULTI
 		curl_multi_remove_handle(curlm, slot->curl);
 #endif
 		curl_easy_cleanup(slot->curl);
 		slot->curl = NULL;
+		curl_session_count--;
 	}
 #ifdef USE_CURL_MULTI
 	fill_active_slots();
 #endif
 }
 
-static void finish_active_slot(struct active_request_slot *slot)
+void finish_active_slot(struct active_request_slot *slot)
 {
 	closedown_active_slot(slot);
 	curl_easy_getinfo(slot->curl, CURLINFO_HTTP_CODE, &slot->http_code);
@@ -683,21 +709,6 @@
 	return 1;
 }
 
-static inline int hex(int v)
-{
-	if (v < 10)
-		return '0' + v;
-	else
-		return 'A' + v - 10;
-}
-
-static void end_url_with_slash(struct strbuf *buf, const char *url)
-{
-	strbuf_addstr(buf, url);
-	if (buf->len && buf->buf[buf->len - 1] != '/')
-		strbuf_addstr(buf, "/");
-}
-
 static char *quote_ref_url(const char *base, const char *ref)
 {
 	struct strbuf buf = STRBUF_INIT;
@@ -719,7 +730,9 @@
 			      const char *hex,
 			      int only_two_digit_prefix)
 {
-	strbuf_addf(buf, "%s/objects/%.*s/", url, 2, hex);
+	end_url_with_slash(buf, url);
+
+	strbuf_addf(buf, "objects/%.*s/", 2, hex);
 	if (!only_two_digit_prefix)
 		strbuf_addf(buf, "%s", hex+2);
 }
@@ -763,7 +776,6 @@
 				headers = curl_slist_append(headers, buf.buf);
 				strbuf_reset(&buf);
 			}
-			slot->local = result;
 		} else
 			curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
 					 fwrite_buffer);
@@ -784,26 +796,57 @@
 			ret = HTTP_OK;
 		else if (missing_target(&results))
 			ret = HTTP_MISSING_TARGET;
-		else
+		else if (results.http_code == 401) {
+			if (http_auth.username && http_auth.password) {
+				credential_reject(&http_auth);
+				ret = HTTP_NOAUTH;
+			} else {
+				credential_fill(&http_auth);
+				init_curl_http_auth(slot->curl);
+				ret = HTTP_REAUTH;
+			}
+		} else {
+			if (!curl_errorstr[0])
+				strlcpy(curl_errorstr,
+					curl_easy_strerror(results.curl_result),
+					sizeof(curl_errorstr));
 			ret = HTTP_ERROR;
+		}
 	} else {
 		error("Unable to start HTTP request for %s", url);
 		ret = HTTP_START_FAILED;
 	}
 
-	slot->local = NULL;
 	curl_slist_free_all(headers);
 	strbuf_release(&buf);
 
+	if (ret == HTTP_OK)
+		credential_approve(&http_auth);
+
 	return ret;
 }
 
-int http_get_strbuf(const char *url, struct strbuf *result, int options)
+static int http_request_reauth(const char *url, void *result, int target,
+			       int options)
 {
-	return http_request(url, result, HTTP_REQUEST_STRBUF, options);
+	int ret = http_request(url, result, target, options);
+	if (ret != HTTP_REAUTH)
+		return ret;
+	return http_request(url, result, target, options);
 }
 
-int http_get_file(const char *url, const char *filename, int options)
+int http_get_strbuf(const char *url, struct strbuf *result, int options)
+{
+	return http_request_reauth(url, result, HTTP_REQUEST_STRBUF, options);
+}
+
+/*
+ * Downloads a URL and stores the result in the given file.
+ *
+ * If a previous interrupted download is detected (i.e. a previous temporary
+ * file is still around) the download is resumed.
+ */
+static int http_get_file(const char *url, const char *filename, int options)
 {
 	int ret;
 	struct strbuf tmpfile = STRBUF_INIT;
@@ -817,7 +860,7 @@
 		goto cleanup;
 	}
 
-	ret = http_request(url, result, HTTP_REQUEST_FILE, options);
+	ret = http_request_reauth(url, result, HTTP_REQUEST_FILE, options);
 	fclose(result);
 
 	if ((ret == HTTP_OK) && move_temp_to_file(tmpfile.buf, filename))
@@ -831,7 +874,7 @@
 {
 	/* http_request has already handled HTTP_START_FAILED. */
 	if (ret != HTTP_START_FAILED)
-		error("%s while accessing %s\n", curl_errorstr, url);
+		error("%s while accessing %s", curl_errorstr, url);
 
 	return ret;
 }
@@ -859,58 +902,67 @@
 }
 
 /* Helpers for fetching packs */
-static int fetch_pack_index(unsigned char *sha1, const char *base_url)
+static char *fetch_pack_index(unsigned char *sha1, const char *base_url)
 {
-	int ret = 0;
-	char *hex = xstrdup(sha1_to_hex(sha1));
-	char *filename;
-	char *url;
+	char *url, *tmp;
 	struct strbuf buf = STRBUF_INIT;
 
-	/* Don't use the index if the pack isn't there */
-	end_url_with_slash(&buf, base_url);
-	strbuf_addf(&buf, "objects/pack/pack-%s.pack", hex);
-	url = strbuf_detach(&buf, 0);
-
-	if (http_get_strbuf(url, NULL, 0)) {
-		ret = error("Unable to verify pack %s is available",
-			    hex);
-		goto cleanup;
-	}
-
-	if (has_pack_index(sha1)) {
-		ret = 0;
-		goto cleanup;
-	}
-
 	if (http_is_verbose)
-		fprintf(stderr, "Getting index for pack %s\n", hex);
+		fprintf(stderr, "Getting index for pack %s\n", sha1_to_hex(sha1));
 
 	end_url_with_slash(&buf, base_url);
-	strbuf_addf(&buf, "objects/pack/pack-%s.idx", hex);
+	strbuf_addf(&buf, "objects/pack/pack-%s.idx", sha1_to_hex(sha1));
 	url = strbuf_detach(&buf, NULL);
 
-	filename = sha1_pack_index_name(sha1);
-	if (http_get_file(url, filename, 0) != HTTP_OK)
-		ret = error("Unable to get pack index %s\n", url);
+	strbuf_addf(&buf, "%s.temp", sha1_pack_index_name(sha1));
+	tmp = strbuf_detach(&buf, NULL);
 
-cleanup:
-	free(hex);
+	if (http_get_file(url, tmp, 0) != HTTP_OK) {
+		error("Unable to get pack index %s", url);
+		free(tmp);
+		tmp = NULL;
+	}
+
 	free(url);
-	return ret;
+	return tmp;
 }
 
 static int fetch_and_setup_pack_index(struct packed_git **packs_head,
 	unsigned char *sha1, const char *base_url)
 {
 	struct packed_git *new_pack;
+	char *tmp_idx = NULL;
+	int ret;
 
-	if (fetch_pack_index(sha1, base_url))
+	if (has_pack_index(sha1)) {
+		new_pack = parse_pack_index(sha1, NULL);
+		if (!new_pack)
+			return -1; /* parse_pack_index() already issued error message */
+		goto add_pack;
+	}
+
+	tmp_idx = fetch_pack_index(sha1, base_url);
+	if (!tmp_idx)
 		return -1;
 
-	new_pack = parse_pack_index(sha1);
-	if (!new_pack)
+	new_pack = parse_pack_index(sha1, tmp_idx);
+	if (!new_pack) {
+		unlink(tmp_idx);
+		free(tmp_idx);
+
 		return -1; /* parse_pack_index() already issued error message */
+	}
+
+	ret = verify_pack_index(new_pack);
+	if (!ret) {
+		close_pack_index(new_pack);
+		ret = move_temp_to_file(tmp_idx, sha1_pack_index_name(sha1));
+	}
+	free(tmp_idx);
+	if (ret)
+		return -1;
+
+add_pack:
 	new_pack->next = *packs_head;
 	*packs_head = new_pack;
 	return 0;
@@ -962,7 +1014,6 @@
 	if (preq->packfile != NULL) {
 		fclose(preq->packfile);
 		preq->packfile = NULL;
-		preq->slot->local = NULL;
 	}
 	if (preq->range_header != NULL) {
 		curl_slist_free_all(preq->range_header);
@@ -974,56 +1025,76 @@
 
 int finish_http_pack_request(struct http_pack_request *preq)
 {
-	int ret;
 	struct packed_git **lst;
+	struct packed_git *p = preq->target;
+	char *tmp_idx;
+	struct child_process ip;
+	const char *ip_argv[8];
 
-	preq->target->pack_size = ftell(preq->packfile);
+	close_pack_index(p);
 
-	if (preq->packfile != NULL) {
-		fclose(preq->packfile);
-		preq->packfile = NULL;
-		preq->slot->local = NULL;
-	}
-
-	ret = move_temp_to_file(preq->tmpfile, preq->filename);
-	if (ret)
-		return ret;
+	fclose(preq->packfile);
+	preq->packfile = NULL;
 
 	lst = preq->lst;
-	while (*lst != preq->target)
+	while (*lst != p)
 		lst = &((*lst)->next);
 	*lst = (*lst)->next;
 
-	if (verify_pack(preq->target))
-		return -1;
-	install_packed_git(preq->target);
+	tmp_idx = xstrdup(preq->tmpfile);
+	strcpy(tmp_idx + strlen(tmp_idx) - strlen(".pack.temp"),
+	       ".idx.temp");
 
+	ip_argv[0] = "index-pack";
+	ip_argv[1] = "-o";
+	ip_argv[2] = tmp_idx;
+	ip_argv[3] = preq->tmpfile;
+	ip_argv[4] = NULL;
+
+	memset(&ip, 0, sizeof(ip));
+	ip.argv = ip_argv;
+	ip.git_cmd = 1;
+	ip.no_stdin = 1;
+	ip.no_stdout = 1;
+
+	if (run_command(&ip)) {
+		unlink(preq->tmpfile);
+		unlink(tmp_idx);
+		free(tmp_idx);
+		return -1;
+	}
+
+	unlink(sha1_pack_index_name(p->sha1));
+
+	if (move_temp_to_file(preq->tmpfile, sha1_pack_name(p->sha1))
+	 || move_temp_to_file(tmp_idx, sha1_pack_index_name(p->sha1))) {
+		free(tmp_idx);
+		return -1;
+	}
+
+	install_packed_git(p);
+	free(tmp_idx);
 	return 0;
 }
 
 struct http_pack_request *new_http_pack_request(
 	struct packed_git *target, const char *base_url)
 {
-	char *url;
-	char *filename;
 	long prev_posn = 0;
 	char range[RANGE_HEADER_SIZE];
 	struct strbuf buf = STRBUF_INIT;
 	struct http_pack_request *preq;
 
-	preq = xmalloc(sizeof(*preq));
+	preq = xcalloc(1, sizeof(*preq));
 	preq->target = target;
-	preq->range_header = NULL;
 
 	end_url_with_slash(&buf, base_url);
 	strbuf_addf(&buf, "objects/pack/pack-%s.pack",
 		sha1_to_hex(target->sha1));
-	url = strbuf_detach(&buf, NULL);
-	preq->url = xstrdup(url);
+	preq->url = strbuf_detach(&buf, NULL);
 
-	filename = sha1_pack_name(target->sha1);
-	snprintf(preq->filename, sizeof(preq->filename), "%s", filename);
-	snprintf(preq->tmpfile, sizeof(preq->tmpfile), "%s.temp", filename);
+	snprintf(preq->tmpfile, sizeof(preq->tmpfile), "%s.temp",
+		sha1_pack_name(target->sha1));
 	preq->packfile = fopen(preq->tmpfile, "a");
 	if (!preq->packfile) {
 		error("Unable to open local file %s for pack",
@@ -1032,10 +1103,9 @@
 	}
 
 	preq->slot = get_active_slot();
-	preq->slot->local = preq->packfile;
 	curl_easy_setopt(preq->slot->curl, CURLOPT_FILE, preq->packfile);
 	curl_easy_setopt(preq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
-	curl_easy_setopt(preq->slot->curl, CURLOPT_URL, url);
+	curl_easy_setopt(preq->slot->curl, CURLOPT_URL, preq->url);
 	curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER,
 		no_pragma_header);
 
@@ -1058,12 +1128,13 @@
 	return preq;
 
 abort:
-	free(filename);
+	free(preq->url);
+	free(preq);
 	return NULL;
 }
 
 /* Helpers for fetching objects (loose) */
-static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
+static size_t fwrite_sha1_file(char *ptr, size_t eltsize, size_t nmemb,
 			       void *data)
 {
 	unsigned char expn[4096];
@@ -1080,7 +1151,7 @@
 	} while (posn < size);
 
 	freq->stream.avail_in = size;
-	freq->stream.next_in = ptr;
+	freq->stream.next_in = (void *)ptr;
 	do {
 		freq->stream.next_out = expn;
 		freq->stream.avail_out = sizeof(expn);
@@ -1088,7 +1159,6 @@
 		git_SHA1_Update(&freq->c, expn,
 				sizeof(expn) - freq->stream.avail_out);
 	} while (freq->stream.avail_in && freq->zret == Z_OK);
-	data_received++;
 	return size;
 }
 
@@ -1098,21 +1168,19 @@
 	char *hex = sha1_to_hex(sha1);
 	char *filename;
 	char prevfile[PATH_MAX];
-	char *url;
 	int prevlocal;
-	unsigned char prev_buf[PREV_BUF_SIZE];
+	char prev_buf[PREV_BUF_SIZE];
 	ssize_t prev_read = 0;
 	long prev_posn = 0;
 	char range[RANGE_HEADER_SIZE];
 	struct curl_slist *range_header = NULL;
 	struct http_object_request *freq;
 
-	freq = xmalloc(sizeof(*freq));
+	freq = xcalloc(1, sizeof(*freq));
 	hashcpy(freq->sha1, sha1);
 	freq->localfile = -1;
 
 	filename = sha1_file_name(sha1);
-	snprintf(freq->filename, sizeof(freq->filename), "%s", filename);
 	snprintf(freq->tmpfile, sizeof(freq->tmpfile),
 		 "%s.temp", filename);
 
@@ -1141,19 +1209,16 @@
 	}
 
 	if (freq->localfile < 0) {
-		error("Couldn't create temporary file %s for %s: %s",
-		      freq->tmpfile, freq->filename, strerror(errno));
+		error("Couldn't create temporary file %s: %s",
+		      freq->tmpfile, strerror(errno));
 		goto abort;
 	}
 
-	memset(&freq->stream, 0, sizeof(freq->stream));
-
 	git_inflate_init(&freq->stream);
 
 	git_SHA1_Init(&freq->c);
 
-	url = get_remote_object_url(base_url, hex, 0);
-	freq->url = xstrdup(url);
+	freq->url = get_remote_object_url(base_url, hex, 0);
 
 	/*
 	 * If a previous temp file is present, process what was already
@@ -1189,7 +1254,11 @@
 		if (prev_posn>0) {
 			prev_posn = 0;
 			lseek(freq->localfile, 0, SEEK_SET);
-			ftruncate(freq->localfile, 0);
+			if (ftruncate(freq->localfile, 0) < 0) {
+				error("Couldn't truncate temporary file %s: %s",
+					  freq->tmpfile, strerror(errno));
+				goto abort;
+			}
 		}
 	}
 
@@ -1198,7 +1267,7 @@
 	curl_easy_setopt(freq->slot->curl, CURLOPT_FILE, freq);
 	curl_easy_setopt(freq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file);
 	curl_easy_setopt(freq->slot->curl, CURLOPT_ERRORBUFFER, freq->errorstr);
-	curl_easy_setopt(freq->slot->curl, CURLOPT_URL, url);
+	curl_easy_setopt(freq->slot->curl, CURLOPT_URL, freq->url);
 	curl_easy_setopt(freq->slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
 
 	/*
@@ -1218,9 +1287,8 @@
 
 	return freq;
 
-	free(url);
 abort:
-	free(filename);
+	free(freq->url);
 	free(freq);
 	return NULL;
 }
@@ -1244,7 +1312,7 @@
 	process_http_object_request(freq);
 
 	if (freq->http_code == 416) {
-		fprintf(stderr, "Warning: requested range invalid; we may already have all the data.\n");
+		warning("requested range invalid; we may already have all the data.");
 	} else if (freq->curl_result != CURLE_OK) {
 		if (stat(freq->tmpfile, &st) == 0)
 			if (st.st_size == 0)
@@ -1263,7 +1331,7 @@
 		return -1;
 	}
 	freq->rename =
-		move_temp_to_file(freq->tmpfile, freq->filename);
+		move_temp_to_file(freq->tmpfile, sha1_file_name(freq->sha1));
 
 	return freq->rename;
 }
@@ -1285,5 +1353,10 @@
 		free(freq->url);
 		freq->url = NULL;
 	}
-	freq->slot = NULL;
+	if (freq->slot != NULL) {
+		freq->slot->callback_func = NULL;
+		freq->slot->callback_data = NULL;
+		release_active_slot(freq->slot);
+		freq->slot = NULL;
+	}
 }
diff --git a/http.h b/http.h
index 4c4e99c..915c286 100644
--- a/http.h
+++ b/http.h
@@ -8,6 +8,7 @@
 
 #include "strbuf.h"
 #include "remote.h"
+#include "url.h"
 
 /*
  * We detect based on the cURL version if multi-transfer is
@@ -23,10 +24,10 @@
 #endif
 
 #if LIBCURL_VERSION_NUM < 0x070704
-#define curl_global_cleanup() do { /* nothing */ } while(0)
+#define curl_global_cleanup() do { /* nothing */ } while (0)
 #endif
 #if LIBCURL_VERSION_NUM < 0x070800
-#define curl_global_init(a) do { /* nothing */ } while(0)
+#define curl_global_init(a) do { /* nothing */ } while (0)
 #endif
 
 #if (LIBCURL_VERSION_NUM < 0x070c04) || (LIBCURL_VERSION_NUM == 0x071000)
@@ -41,16 +42,13 @@
 #define NO_CURL_IOCTL
 #endif
 
-struct slot_results
-{
+struct slot_results {
 	CURLcode curl_result;
 	long http_code;
 };
 
-struct active_request_slot
-{
+struct active_request_slot {
 	CURL *curl;
-	FILE *local;
 	int in_use;
 	CURLcode curl_result;
 	long http_code;
@@ -61,16 +59,15 @@
 	struct active_request_slot *next;
 };
 
-struct buffer
-{
+struct buffer {
 	struct strbuf buf;
 	size_t posn;
 };
 
 /* Curl request read/write callbacks */
-extern size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb, void *strbuf);
-extern size_t fwrite_buffer(const void *ptr, size_t eltsize, size_t nmemb, void *strbuf);
-extern size_t fwrite_null(const void *ptr, size_t eltsize, size_t nmemb, void *strbuf);
+extern size_t fread_buffer(char *ptr, size_t eltsize, size_t nmemb, void *strbuf);
+extern size_t fwrite_buffer(char *ptr, size_t eltsize, size_t nmemb, void *strbuf);
+extern size_t fwrite_null(char *ptr, size_t eltsize, size_t nmemb, void *strbuf);
 #ifndef NO_CURL_IOCTL
 extern curlioerr ioctl_buffer(CURL *handle, int cmd, void *clientp);
 #endif
@@ -79,8 +76,8 @@
 extern struct active_request_slot *get_active_slot(void);
 extern int start_active_slot(struct active_request_slot *slot);
 extern void run_active_slot(struct active_request_slot *slot);
+extern void finish_active_slot(struct active_request_slot *slot);
 extern void finish_all_active_slots(void);
-extern void release_active_slot(struct active_request_slot *slot);
 
 #ifdef USE_CURL_MULTI
 extern void fill_active_slots(void);
@@ -88,12 +85,13 @@
 extern void step_active_slots(void);
 #endif
 
-extern void http_init(struct remote *remote);
+extern void http_init(struct remote *remote, const char *url,
+		      int proactive_auth);
 extern void http_cleanup(void);
 
-extern int data_received;
 extern int active_requests;
 extern int http_is_verbose;
+extern size_t http_post_buffer;
 
 extern char curl_errorstr[CURL_ERROR_SIZE];
 
@@ -125,23 +123,17 @@
 #define HTTP_MISSING_TARGET	1
 #define HTTP_ERROR		2
 #define HTTP_START_FAILED	3
+#define HTTP_REAUTH	4
+#define HTTP_NOAUTH	5
 
 /*
- * Requests an url and stores the result in a strbuf.
+ * Requests a URL and stores the result in a strbuf.
  *
  * If the result pointer is NULL, a HTTP HEAD request is made instead of GET.
  */
 int http_get_strbuf(const char *url, struct strbuf *result, int options);
 
 /*
- * Downloads an url and stores the result in the given file.
- *
- * If a previous interrupted download is detected (i.e. a previous temporary
- * file is still around) the download is resumed.
- */
-int http_get_file(const char *url, const char *filename, int options);
-
-/*
  * Prints an error message using error() containing url and curl_errorstr,
  * and returns ret.
  */
@@ -153,13 +145,11 @@
 extern int http_get_info_packs(const char *base_url,
 	struct packed_git **packs_head);
 
-struct http_pack_request
-{
+struct http_pack_request {
 	char *url;
 	struct packed_git *target;
 	struct packed_git **lst;
 	FILE *packfile;
-	char filename[PATH_MAX];
 	char tmpfile[PATH_MAX];
 	struct curl_slist *range_header;
 	struct active_request_slot *slot;
@@ -171,10 +161,8 @@
 extern void release_http_pack_request(struct http_pack_request *preq);
 
 /* Helpers for fetching object */
-struct http_object_request
-{
+struct http_object_request {
 	char *url;
-	char filename[PATH_MAX];
 	char tmpfile[PATH_MAX];
 	int localfile;
 	CURLcode curl_result;
@@ -183,7 +171,7 @@
 	unsigned char sha1[20];
 	unsigned char real_sha1[20];
 	git_SHA_CTX c;
-	z_stream stream;
+	git_zstream stream;
 	int zret;
 	int rename;
 	struct active_request_slot *slot;
diff --git a/ident.c b/ident.c
index 99f1c85..443c075 100644
--- a/ident.c
+++ b/ident.c
@@ -7,108 +7,121 @@
  */
 #include "cache.h"
 
+static struct strbuf git_default_name = STRBUF_INIT;
+static struct strbuf git_default_email = STRBUF_INIT;
 static char git_default_date[50];
+int user_ident_explicitly_given;
 
-static void copy_gecos(const struct passwd *w, char *name, size_t sz)
+#ifdef NO_GECOS_IN_PWENT
+#define get_gecos(ignored) "&"
+#else
+#define get_gecos(struct_passwd) ((struct_passwd)->pw_gecos)
+#endif
+
+static void copy_gecos(const struct passwd *w, struct strbuf *name)
 {
-	char *src, *dst;
-	size_t len, nlen;
-
-	nlen = strlen(w->pw_name);
+	char *src;
 
 	/* Traditionally GECOS field had office phone numbers etc, separated
 	 * with commas.  Also & stands for capitalized form of the login name.
 	 */
 
-	for (len = 0, dst = name, src = w->pw_gecos; len < sz; src++) {
+	for (src = get_gecos(w); *src && *src != ','; src++) {
 		int ch = *src;
-		if (ch != '&') {
-			*dst++ = ch;
-			if (ch == 0 || ch == ',')
-				break;
-			len++;
-			continue;
-		}
-		if (len + nlen < sz) {
+		if (ch != '&')
+			strbuf_addch(name, ch);
+		else {
 			/* Sorry, Mr. McDonald... */
-			*dst++ = toupper(*w->pw_name);
-			memcpy(dst, w->pw_name + 1, nlen - 1);
-			dst += nlen - 1;
+			strbuf_addch(name, toupper(*w->pw_name));
+			strbuf_addstr(name, w->pw_name + 1);
 		}
 	}
-	if (len < sz)
-		name[len] = 0;
-	else
-		die("Your parents must have hated you!");
-
 }
 
-static void copy_email(const struct passwd *pw)
+static int add_mailname_host(struct strbuf *buf)
+{
+	FILE *mailname;
+
+	mailname = fopen("/etc/mailname", "r");
+	if (!mailname) {
+		if (errno != ENOENT)
+			warning("cannot open /etc/mailname: %s",
+				strerror(errno));
+		return -1;
+	}
+	if (strbuf_getline(buf, mailname, '\n') == EOF) {
+		if (ferror(mailname))
+			warning("cannot read /etc/mailname: %s",
+				strerror(errno));
+		fclose(mailname);
+		return -1;
+	}
+	/* success! */
+	fclose(mailname);
+	return 0;
+}
+
+static void add_domainname(struct strbuf *out)
+{
+	char buf[1024];
+	struct hostent *he;
+
+	if (gethostname(buf, sizeof(buf))) {
+		warning("cannot get host name: %s", strerror(errno));
+		strbuf_addstr(out, "(none)");
+		return;
+	}
+	if (strchr(buf, '.'))
+		strbuf_addstr(out, buf);
+	else if ((he = gethostbyname(buf)) && strchr(he->h_name, '.'))
+		strbuf_addstr(out, he->h_name);
+	else
+		strbuf_addf(out, "%s.(none)", buf);
+}
+
+static void copy_email(const struct passwd *pw, struct strbuf *email)
 {
 	/*
 	 * Make up a fake email address
 	 * (name + '@' + hostname [+ '.' + domainname])
 	 */
-	size_t len = strlen(pw->pw_name);
-	if (len > sizeof(git_default_email)/2)
-		die("Your sysadmin must hate you!");
-	memcpy(git_default_email, pw->pw_name, len);
-	git_default_email[len++] = '@';
-	gethostname(git_default_email + len, sizeof(git_default_email) - len);
-	if (!strchr(git_default_email+len, '.')) {
-		struct hostent *he = gethostbyname(git_default_email + len);
-		char *domainname;
+	strbuf_addstr(email, pw->pw_name);
+	strbuf_addch(email, '@');
 
-		len = strlen(git_default_email);
-		git_default_email[len++] = '.';
-		if (he && (domainname = strchr(he->h_name, '.')))
-			strlcpy(git_default_email + len, domainname + 1,
-				sizeof(git_default_email) - len);
-		else
-			strlcpy(git_default_email + len, "(none)",
-				sizeof(git_default_email) - len);
-	}
+	if (!add_mailname_host(email))
+		return;	/* read from "/etc/mailname" (Debian) */
+	add_domainname(email);
 }
 
-static void setup_ident(void)
+const char *ident_default_name(void)
 {
-	struct passwd *pw = NULL;
-
-	/* Get the name ("gecos") */
-	if (!git_default_name[0]) {
-		pw = getpwuid(getuid());
-		if (!pw)
-			die("You don't exist. Go away!");
-		copy_gecos(pw, git_default_name, sizeof(git_default_name));
+	if (!git_default_name.len) {
+		copy_gecos(xgetpwuid_self(), &git_default_name);
+		strbuf_trim(&git_default_name);
 	}
+	return git_default_name.buf;
+}
 
-	if (!git_default_email[0]) {
+const char *ident_default_email(void)
+{
+	if (!git_default_email.len) {
 		const char *email = getenv("EMAIL");
 
-		if (email && email[0])
-			strlcpy(git_default_email, email,
-				sizeof(git_default_email));
-		else {
-			if (!pw)
-				pw = getpwuid(getuid());
-			if (!pw)
-				die("You don't exist. Go away!");
-			copy_email(pw);
-		}
+		if (email && email[0]) {
+			strbuf_addstr(&git_default_email, email);
+			user_ident_explicitly_given |= IDENT_MAIL_GIVEN;
+		} else
+			copy_email(xgetpwuid_self(), &git_default_email);
+		strbuf_trim(&git_default_email);
 	}
-
-	/* And set the default date */
-	if (!git_default_date[0])
-		datestamp(git_default_date, sizeof(git_default_date));
+	return git_default_email.buf;
 }
 
-static int add_raw(char *buf, size_t size, int offset, const char *str)
+const char *ident_default_date(void)
 {
-	size_t len = strlen(str);
-	if (offset + len > size)
-		return size;
-	memcpy(buf + offset, str, len);
-	return offset + len;
+	if (!git_default_date[0])
+		datestamp(git_default_date, sizeof(git_default_date));
+	return git_default_date;
 }
 
 static int crud(unsigned char c)
@@ -129,7 +142,7 @@
  * Copy over a string to the destination, but avoid special
  * characters ('\n', '<' and '>') and remove crud at the end
  */
-static int copy(char *buf, size_t size, int offset, const char *src)
+static void strbuf_addstr_without_crud(struct strbuf *sb, const char *src)
 {
 	size_t i, len;
 	unsigned char c;
@@ -153,23 +166,89 @@
 	/*
 	 * Copy the rest to the buffer, but avoid the special
 	 * characters '\n' '<' and '>' that act as delimiters on
-	 * an identification line
+	 * an identification line. We can only remove crud, never add it,
+	 * so 'len' is our maximum.
 	 */
+	strbuf_grow(sb, len);
 	for (i = 0; i < len; i++) {
 		c = *src++;
 		switch (c) {
 		case '\n': case '<': case '>':
 			continue;
 		}
-		if (offset >= size)
-			return size;
-		buf[offset++] = c;
+		sb->buf[sb->len++] = c;
 	}
-	return offset;
+	sb->buf[sb->len] = '\0';
 }
 
-static const char au_env[] = "GIT_AUTHOR_NAME";
-static const char co_env[] = "GIT_COMMITTER_NAME";
+/*
+ * Reverse of fmt_ident(); given an ident line, split the fields
+ * to allow the caller to parse it.
+ * Signal a success by returning 0, but date/tz fields of the result
+ * can still be NULL if the input line only has the name/email part
+ * (e.g. reading from a reflog entry).
+ */
+int split_ident_line(struct ident_split *split, const char *line, int len)
+{
+	const char *cp;
+	size_t span;
+	int status = -1;
+
+	memset(split, 0, sizeof(*split));
+
+	split->name_begin = line;
+	for (cp = line; *cp && cp < line + len; cp++)
+		if (*cp == '<') {
+			split->mail_begin = cp + 1;
+			break;
+		}
+	if (!split->mail_begin)
+		return status;
+
+	for (cp = split->mail_begin - 2; line <= cp; cp--)
+		if (!isspace(*cp)) {
+			split->name_end = cp + 1;
+			break;
+		}
+	if (!split->name_end)
+		return status;
+
+	for (cp = split->mail_begin; cp < line + len; cp++)
+		if (*cp == '>') {
+			split->mail_end = cp;
+			break;
+		}
+	if (!split->mail_end)
+		return status;
+
+	for (cp = split->mail_end + 1; cp < line + len && isspace(*cp); cp++)
+		;
+	if (line + len <= cp)
+		goto person_only;
+	split->date_begin = cp;
+	span = strspn(cp, "0123456789");
+	if (!span)
+		goto person_only;
+	split->date_end = split->date_begin + span;
+	for (cp = split->date_end; cp < line + len && isspace(*cp); cp++)
+		;
+	if (line + len <= cp || (*cp != '+' && *cp != '-'))
+		goto person_only;
+	split->tz_begin = cp;
+	span = strspn(cp + 1, "0123456789");
+	if (!span)
+		goto person_only;
+	split->tz_end = split->tz_begin + 1 + span;
+	return 0;
+
+person_only:
+	split->date_begin = NULL;
+	split->date_end = NULL;
+	split->tz_begin = NULL;
+	split->tz_end = NULL;
+	return 0;
+}
+
 static const char *env_hint =
 "\n"
 "*** Please tell me who you are.\n"
@@ -186,59 +265,62 @@
 const char *fmt_ident(const char *name, const char *email,
 		      const char *date_str, int flag)
 {
-	static char buffer[1000];
+	static struct strbuf ident = STRBUF_INIT;
 	char date[50];
-	int i;
-	int error_on_no_name = (flag & IDENT_ERROR_ON_NO_NAME);
-	int warn_on_no_name = (flag & IDENT_WARN_ON_NO_NAME);
-	int name_addr_only = (flag & IDENT_NO_DATE);
+	int strict = (flag & IDENT_STRICT);
+	int want_date = !(flag & IDENT_NO_DATE);
+	int want_name = !(flag & IDENT_NO_NAME);
 
-	setup_ident();
-	if (!name)
-		name = git_default_name;
+	if (want_name && !name)
+		name = ident_default_name();
 	if (!email)
-		email = git_default_email;
+		email = ident_default_email();
 
-	if (!*name) {
+	if (want_name && !*name) {
 		struct passwd *pw;
 
-		if ((warn_on_no_name || error_on_no_name) &&
-		    name == git_default_name && env_hint) {
-			fprintf(stderr, env_hint, au_env, co_env);
-			env_hint = NULL; /* warn only once, for "git var -l" */
+		if (strict) {
+			if (name == git_default_name.buf)
+				fputs(env_hint, stderr);
+			die("empty ident name (for <%s>) not allowed", email);
 		}
-		if (error_on_no_name)
-			die("empty ident %s <%s> not allowed", name, email);
-		pw = getpwuid(getuid());
-		if (!pw)
-			die("You don't exist. Go away!");
-		strlcpy(git_default_name, pw->pw_name,
-			sizeof(git_default_name));
-		name = git_default_name;
+		pw = xgetpwuid_self();
+		name = pw->pw_name;
 	}
 
-	strcpy(date, git_default_date);
-	if (!name_addr_only && date_str)
-		parse_date(date_str, date, sizeof(date));
-
-	i = copy(buffer, sizeof(buffer), 0, name);
-	i = add_raw(buffer, sizeof(buffer), i, " <");
-	i = copy(buffer, sizeof(buffer), i, email);
-	if (!name_addr_only) {
-		i = add_raw(buffer, sizeof(buffer), i,  "> ");
-		i = copy(buffer, sizeof(buffer), i, date);
-	} else {
-		i = add_raw(buffer, sizeof(buffer), i, ">");
+	if (strict && email == git_default_email.buf &&
+	    strstr(email, "(none)")) {
+		fputs(env_hint, stderr);
+		die("unable to auto-detect email address (got '%s')", email);
 	}
-	if (i >= sizeof(buffer))
-		die("Impossibly long personal identifier");
-	buffer[i] = 0;
-	return buffer;
+
+	if (want_date) {
+		if (date_str && date_str[0]) {
+			if (parse_date(date_str, date, sizeof(date)) < 0)
+				die("invalid date format: %s", date_str);
+		}
+		else
+			strcpy(date, ident_default_date());
+	}
+
+	strbuf_reset(&ident);
+	if (want_name) {
+		strbuf_addstr_without_crud(&ident, name);
+		strbuf_addstr(&ident, " <");
+	}
+	strbuf_addstr_without_crud(&ident, email);
+	if (want_name)
+			strbuf_addch(&ident, '>');
+	if (want_date) {
+		strbuf_addch(&ident, ' ');
+		strbuf_addstr_without_crud(&ident, date);
+	}
+	return ident.buf;
 }
 
 const char *fmt_name(const char *name, const char *email)
 {
-	return fmt_ident(name, email, NULL, IDENT_ERROR_ON_NO_NAME | IDENT_NO_DATE);
+	return fmt_ident(name, email, NULL, IDENT_STRICT | IDENT_NO_DATE);
 }
 
 const char *git_author_info(int flag)
@@ -251,11 +333,44 @@
 
 const char *git_committer_info(int flag)
 {
-	if (getenv("GIT_COMMITTER_NAME") &&
-	    getenv("GIT_COMMITTER_EMAIL"))
-		user_ident_explicitly_given = 1;
+	if (getenv("GIT_COMMITTER_NAME"))
+		user_ident_explicitly_given |= IDENT_NAME_GIVEN;
+	if (getenv("GIT_COMMITTER_EMAIL"))
+		user_ident_explicitly_given |= IDENT_MAIL_GIVEN;
 	return fmt_ident(getenv("GIT_COMMITTER_NAME"),
 			 getenv("GIT_COMMITTER_EMAIL"),
 			 getenv("GIT_COMMITTER_DATE"),
 			 flag);
 }
+
+int user_ident_sufficiently_given(void)
+{
+#ifndef WINDOWS
+	return (user_ident_explicitly_given & IDENT_MAIL_GIVEN);
+#else
+	return (user_ident_explicitly_given == IDENT_ALL_GIVEN);
+#endif
+}
+
+int git_ident_config(const char *var, const char *value, void *data)
+{
+	if (!strcmp(var, "user.name")) {
+		if (!value)
+			return config_error_nonbool(var);
+		strbuf_reset(&git_default_name);
+		strbuf_addstr(&git_default_name, value);
+		user_ident_explicitly_given |= IDENT_NAME_GIVEN;
+		return 0;
+	}
+
+	if (!strcmp(var, "user.email")) {
+		if (!value)
+			return config_error_nonbool(var);
+		strbuf_reset(&git_default_email);
+		strbuf_addstr(&git_default_email, value);
+		user_ident_explicitly_given |= IDENT_MAIL_GIVEN;
+		return 0;
+	}
+
+	return 0;
+}
diff --git a/imap-send.c b/imap-send.c
index 3847fd1..d42e471 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -24,8 +24,13 @@
 
 #include "cache.h"
 #include "exec_cmd.h"
+#include "run-command.h"
+#include "prompt.h"
 #ifdef NO_OPENSSL
 typedef void *SSL;
+#else
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
 #endif
 
 struct store_conf {
@@ -37,28 +42,6 @@
 	unsigned trash_remote_new:1, trash_only_new:1;
 };
 
-struct string_list {
-	struct string_list *next;
-	char string[1];
-};
-
-struct channel_conf {
-	struct channel_conf *next;
-	char *name;
-	struct store_conf *master, *slave;
-	char *master_name, *slave_name;
-	char *sync_state;
-	struct string_list *patterns;
-	int mops, sops;
-	unsigned max_messages; /* for slave only */
-};
-
-struct group_conf {
-	struct group_conf *next;
-	char *name;
-	struct string_list *channels;
-};
-
 /* For message->status */
 #define M_RECENT       (1<<0) /* unsyncable flag; maildir_* depend on this being 1<<0 */
 #define M_DEAD         (1<<1) /* expunged */
@@ -66,7 +49,6 @@
 
 struct message {
 	struct message *next;
-	/* struct string_list *keywords; */
 	size_t size; /* zero implies "not fetched" */
 	int uid;
 	unsigned char flags, status;
@@ -90,9 +72,11 @@
 	char *data;
 	int len;
 	unsigned char flags;
-	unsigned int crlf:1;
 };
 
+static const char imap_send_usage[] = "git imap-send < <mbox>";
+
+#undef DRV_OK
 #define DRV_OK          0
 #define DRV_MSG_BAD     -1
 #define DRV_BOX_BAD     -2
@@ -100,13 +84,16 @@
 
 static int Verbose, Quiet;
 
+__attribute__((format (printf, 1, 2)))
 static void imap_info(const char *, ...);
+__attribute__((format (printf, 1, 2)))
 static void imap_warn(const char *, ...);
 
 static char *next_arg(char **);
 
 static void free_generic_messages(struct message *);
 
+__attribute__((format (printf, 3, 4)))
 static int nfsnprintf(char *buf, int blen, const char *fmt, ...);
 
 static int nfvasprintf(char **strp, const char *fmt, va_list ap)
@@ -123,9 +110,6 @@
 	return len;
 }
 
-static void arc4_init(void);
-static unsigned char arc4_getbyte(void);
-
 struct imap_server_conf {
 	char *name;
 	char *tunnel;
@@ -136,12 +120,25 @@
 	int use_ssl;
 	int ssl_verify;
 	int use_html;
+	char *auth_method;
+};
+
+static struct imap_server_conf server = {
+	NULL,	/* name */
+	NULL,	/* tunnel */
+	NULL,	/* host */
+	0,	/* port */
+	NULL,	/* user */
+	NULL,	/* pass */
+	0,   	/* use_ssl */
+	1,   	/* ssl_verify */
+	0,   	/* use_html */
+	NULL,	/* auth_method */
 };
 
 struct imap_store_conf {
 	struct store_conf gen;
 	struct imap_server_conf *server;
-	unsigned use_namespace:1;
 };
 
 #define NIL	(void *)0x1
@@ -154,7 +151,7 @@
 };
 
 struct imap_socket {
-	int fd;
+	int fd[2];
 	SSL *ssl;
 };
 
@@ -210,6 +207,7 @@
 	LITERALPLUS,
 	NAMESPACE,
 	STARTTLS,
+	AUTH_CRAM_MD5
 };
 
 static const char *cap_list[] = {
@@ -218,6 +216,7 @@
 	"LITERAL+",
 	"NAMESPACE",
 	"STARTTLS",
+	"AUTH=CRAM-MD5",
 };
 
 #define RESP_OK    0
@@ -273,7 +272,11 @@
 	fprintf(stderr, "SSL requested but SSL support not compiled in\n");
 	return -1;
 #else
+#if (OPENSSL_VERSION_NUMBER >= 0x10000000L)
+	const SSL_METHOD *meth;
+#else
 	SSL_METHOD *meth;
+#endif
 	SSL_CTX *ctx;
 	int ret;
 
@@ -304,8 +307,12 @@
 		ssl_socket_perror("SSL_new");
 		return -1;
 	}
-	if (!SSL_set_fd(sock->ssl, sock->fd)) {
-		ssl_socket_perror("SSL_set_fd");
+	if (!SSL_set_rfd(sock->ssl, sock->fd[0])) {
+		ssl_socket_perror("SSL_set_rfd");
+		return -1;
+	}
+	if (!SSL_set_wfd(sock->ssl, sock->fd[1])) {
+		ssl_socket_perror("SSL_set_wfd");
 		return -1;
 	}
 
@@ -327,11 +334,12 @@
 		n = SSL_read(sock->ssl, buf, len);
 	else
 #endif
-		n = xread(sock->fd, buf, len);
+		n = xread(sock->fd[0], buf, len);
 	if (n <= 0) {
 		socket_perror("read", sock, n);
-		close(sock->fd);
-		sock->fd = -1;
+		close(sock->fd[0]);
+		close(sock->fd[1]);
+		sock->fd[0] = sock->fd[1] = -1;
 	}
 	return n;
 }
@@ -344,11 +352,12 @@
 		n = SSL_write(sock->ssl, buf, len);
 	else
 #endif
-		n = write_in_full(sock->fd, buf, len);
+		n = write_in_full(sock->fd[1], buf, len);
 	if (n != len) {
 		socket_perror("write", sock, n);
-		close(sock->fd);
-		sock->fd = -1;
+		close(sock->fd[0]);
+		close(sock->fd[1]);
+		sock->fd[0] = sock->fd[1] = -1;
 	}
 	return n;
 }
@@ -361,7 +370,8 @@
 		SSL_free(sock->ssl);
 	}
 #endif
-	close(sock->fd);
+	close(sock->fd[0]);
+	close(sock->fd[1]);
 }
 
 /* simple line buffering */
@@ -489,52 +499,6 @@
 	return ret;
 }
 
-static struct {
-	unsigned char i, j, s[256];
-} rs;
-
-static void arc4_init(void)
-{
-	int i, fd;
-	unsigned char j, si, dat[128];
-
-	if ((fd = open("/dev/urandom", O_RDONLY)) < 0 && (fd = open("/dev/random", O_RDONLY)) < 0) {
-		fprintf(stderr, "Fatal: no random number source available.\n");
-		exit(3);
-	}
-	if (read_in_full(fd, dat, 128) != 128) {
-		fprintf(stderr, "Fatal: cannot read random number source.\n");
-		exit(3);
-	}
-	close(fd);
-
-	for (i = 0; i < 256; i++)
-		rs.s[i] = i;
-	for (i = j = 0; i < 256; i++) {
-		si = rs.s[i];
-		j += si + dat[i & 127];
-		rs.s[i] = rs.s[j];
-		rs.s[j] = si;
-	}
-	rs.i = rs.j = 0;
-
-	for (i = 0; i < 256; i++)
-		arc4_getbyte();
-}
-
-static unsigned char arc4_getbyte(void)
-{
-	unsigned char si, sj;
-
-	rs.i++;
-	si = rs.s[rs.i];
-	rs.j += si;
-	sj = rs.s[rs.j];
-	rs.s[rs.i] = sj;
-	rs.s[rs.j] = si;
-	return rs.s[(si + sj) & 0xff];
-}
-
 static struct imap_cmd *v_issue_imap_cmd(struct imap_store *ctx,
 					 struct imap_cmd_cb *cb,
 					 const char *fmt, va_list ap)
@@ -556,9 +520,13 @@
 	while (imap->literal_pending)
 		get_cmd_result(ctx, NULL);
 
-	bufl = nfsnprintf(buf, sizeof(buf), cmd->cb.data ? CAP(LITERALPLUS) ?
-			   "%d %s{%d+}\r\n" : "%d %s{%d}\r\n" : "%d %s\r\n",
-			   cmd->tag, cmd->cmd, cmd->cb.dlen);
+	if (!cmd->cb.data)
+		bufl = nfsnprintf(buf, sizeof(buf), "%d %s\r\n", cmd->tag, cmd->cmd);
+	else
+		bufl = nfsnprintf(buf, sizeof(buf), "%d %s{%d%s}\r\n",
+				  cmd->tag, cmd->cmd, cmd->cb.dlen,
+				  CAP(LITERALPLUS) ? "+" : "");
+
 	if (Verbose) {
 		if (imap->num_in_progress)
 			printf("(%d in progress) ", imap->num_in_progress);
@@ -596,6 +564,7 @@
 	return cmd;
 }
 
+__attribute__((format (printf, 3, 4)))
 static struct imap_cmd *issue_imap_cmd(struct imap_store *ctx,
 				       struct imap_cmd_cb *cb,
 				       const char *fmt, ...)
@@ -609,6 +578,7 @@
 	return ret;
 }
 
+__attribute__((format (printf, 3, 4)))
 static int imap_exec(struct imap_store *ctx, struct imap_cmd_cb *cb,
 		     const char *fmt, ...)
 {
@@ -624,6 +594,7 @@
 	return get_cmd_result(ctx, cmdp);
 }
 
+__attribute__((format (printf, 3, 4)))
 static int imap_exec_m(struct imap_store *ctx, struct imap_cmd_cb *cb,
 		       const char *fmt, ...)
 {
@@ -914,7 +885,7 @@
 				if (!strcmp("NO", arg)) {
 					if (cmdp->cb.create && cmd && (cmdp->cb.trycreate || !memcmp(cmd, "[TRYCREATE]", 11))) { /* SELECT, APPEND or UID COPY */
 						p = strchr(cmdp->cmd, '"');
-						if (!issue_imap_cmd(ctx, NULL, "CREATE \"%.*s\"", strchr(p + 1, '"') - p + 1, p)) {
+						if (!issue_imap_cmd(ctx, NULL, "CREATE \"%.*s\"", (int)(strchr(p + 1, '"') - p + 1), p)) {
 							resp = RESP_BAD;
 							goto normal;
 						}
@@ -960,7 +931,7 @@
 {
 	struct imap *imap = ictx->imap;
 
-	if (imap->buf.sock.fd != -1) {
+	if (imap->buf.sock.fd[0] != -1) {
 		imap_exec(ictx, NULL, "LOGOUT");
 		socket_shutdown(&imap->buf.sock);
 	}
@@ -977,45 +948,117 @@
 	free(ctx);
 }
 
+#ifndef NO_OPENSSL
+
+/*
+ * hexchar() and cram() functions are based on the code from the isync
+ * project (http://isync.sf.net/).
+ */
+static char hexchar(unsigned int b)
+{
+	return b < 10 ? '0' + b : 'a' + (b - 10);
+}
+
+#define ENCODED_SIZE(n) (4*((n+2)/3))
+static char *cram(const char *challenge_64, const char *user, const char *pass)
+{
+	int i, resp_len, encoded_len, decoded_len;
+	HMAC_CTX hmac;
+	unsigned char hash[16];
+	char hex[33];
+	char *response, *response_64, *challenge;
+
+	/*
+	 * length of challenge_64 (i.e. base-64 encoded string) is a good
+	 * enough upper bound for challenge (decoded result).
+	 */
+	encoded_len = strlen(challenge_64);
+	challenge = xmalloc(encoded_len);
+	decoded_len = EVP_DecodeBlock((unsigned char *)challenge,
+				      (unsigned char *)challenge_64, encoded_len);
+	if (decoded_len < 0)
+		die("invalid challenge %s", challenge_64);
+	HMAC_Init(&hmac, (unsigned char *)pass, strlen(pass), EVP_md5());
+	HMAC_Update(&hmac, (unsigned char *)challenge, decoded_len);
+	HMAC_Final(&hmac, hash, NULL);
+	HMAC_CTX_cleanup(&hmac);
+
+	hex[32] = 0;
+	for (i = 0; i < 16; i++) {
+		hex[2 * i] = hexchar((hash[i] >> 4) & 0xf);
+		hex[2 * i + 1] = hexchar(hash[i] & 0xf);
+	}
+
+	/* response: "<user> <digest in hex>" */
+	resp_len = strlen(user) + 1 + strlen(hex) + 1;
+	response = xmalloc(resp_len);
+	sprintf(response, "%s %s", user, hex);
+
+	response_64 = xmalloc(ENCODED_SIZE(resp_len) + 1);
+	encoded_len = EVP_EncodeBlock((unsigned char *)response_64,
+				      (unsigned char *)response, resp_len);
+	if (encoded_len < 0)
+		die("EVP_EncodeBlock error");
+	response_64[encoded_len] = '\0';
+	return (char *)response_64;
+}
+
+#else
+
+static char *cram(const char *challenge_64, const char *user, const char *pass)
+{
+	die("If you want to use CRAM-MD5 authenticate method, "
+	    "you have to build git-imap-send with OpenSSL library.");
+}
+
+#endif
+
+static int auth_cram_md5(struct imap_store *ctx, struct imap_cmd *cmd, const char *prompt)
+{
+	int ret;
+	char *response;
+
+	response = cram(prompt, server.user, server.pass);
+
+	ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
+	if (ret != strlen(response))
+		return error("IMAP error: sending response failed");
+
+	free(response);
+
+	return 0;
+}
+
 static struct store *imap_open_store(struct imap_server_conf *srvc)
 {
 	struct imap_store *ctx;
 	struct imap *imap;
 	char *arg, *rsp;
-	int s = -1, a[2], preauth;
-	pid_t pid;
+	int s = -1, preauth;
 
 	ctx = xcalloc(sizeof(*ctx), 1);
 
 	ctx->imap = imap = xcalloc(sizeof(*imap), 1);
-	imap->buf.sock.fd = -1;
+	imap->buf.sock.fd[0] = imap->buf.sock.fd[1] = -1;
 	imap->in_progress_append = &imap->in_progress;
 
 	/* open connection to IMAP server */
 
 	if (srvc->tunnel) {
+		const char *argv[] = { srvc->tunnel, NULL };
+		struct child_process tunnel = {NULL};
+
 		imap_info("Starting tunnel '%s'... ", srvc->tunnel);
 
-		if (socketpair(PF_UNIX, SOCK_STREAM, 0, a)) {
-			perror("socketpair");
-			exit(1);
-		}
+		tunnel.argv = argv;
+		tunnel.use_shell = 1;
+		tunnel.in = -1;
+		tunnel.out = -1;
+		if (start_command(&tunnel))
+			die("cannot start proxy %s", argv[0]);
 
-		pid = fork();
-		if (pid < 0)
-			_exit(127);
-		if (!pid) {
-			if (dup2(a[0], 0) == -1 || dup2(a[0], 1) == -1)
-				_exit(127);
-			close(a[0]);
-			close(a[1]);
-			execl("/bin/sh", "sh", "-c", srvc->tunnel, NULL);
-			_exit(127);
-		}
-
-		close(a[0]);
-
-		imap->buf.sock.fd = a[1];
+		imap->buf.sock.fd[0] = tunnel.out;
+		imap->buf.sock.fd[1] = tunnel.in;
 
 		imap_info("ok\n");
 	} else {
@@ -1024,7 +1067,7 @@
 		int gai;
 		char portstr[6];
 
-		snprintf(portstr, sizeof(portstr), "%hu", srvc->port);
+		snprintf(portstr, sizeof(portstr), "%d", srvc->port);
 
 		memset(&hints, 0, sizeof(hints));
 		hints.ai_socktype = SOCK_STREAM;
@@ -1092,7 +1135,8 @@
 			goto bail;
 		}
 
-		imap->buf.sock.fd = s;
+		imap->buf.sock.fd[0] = s;
+		imap->buf.sock.fd[1] = dup(s);
 
 		if (srvc->use_ssl &&
 		    ssl_socket_connect(&imap->buf.sock, 0, srvc->ssl_verify)) {
@@ -1126,13 +1170,13 @@
 	if (!preauth) {
 #ifndef NO_OPENSSL
 		if (!srvc->use_ssl && CAP(STARTTLS)) {
-			if (imap_exec(ctx, 0, "STARTTLS") != RESP_OK)
+			if (imap_exec(ctx, NULL, "STARTTLS") != RESP_OK)
 				goto bail;
 			if (ssl_socket_connect(&imap->buf.sock, 1,
 					       srvc->ssl_verify))
 				goto bail;
 			/* capabilities may have changed, so get the new capabilities */
-			if (imap_exec(ctx, 0, "CAPABILITY") != RESP_OK)
+			if (imap_exec(ctx, NULL, "CAPABILITY") != RESP_OK)
 				goto bail;
 		}
 #endif
@@ -1142,13 +1186,10 @@
 			goto bail;
 		}
 		if (!srvc->pass) {
-			char prompt[80];
-			sprintf(prompt, "Password (%s@%s): ", srvc->user, srvc->host);
-			arg = getpass(prompt);
-			if (!arg) {
-				perror("getpass");
-				exit(1);
-			}
+			struct strbuf prompt = STRBUF_INIT;
+			strbuf_addf(&prompt, "Password (%s@%s): ", srvc->user, srvc->host);
+			arg = git_getpass(prompt.buf);
+			strbuf_release(&prompt);
 			if (!*arg) {
 				fprintf(stderr, "Skipping account %s@%s, no password\n", srvc->user, srvc->host);
 				goto bail;
@@ -1163,12 +1204,37 @@
 			fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN\n", srvc->user, srvc->host);
 			goto bail;
 		}
-		if (!imap->buf.sock.ssl)
-			imap_warn("*** IMAP Warning *** Password is being "
-				  "sent in the clear\n");
-		if (imap_exec(ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass) != RESP_OK) {
-			fprintf(stderr, "IMAP error: LOGIN failed\n");
-			goto bail;
+
+		if (srvc->auth_method) {
+			struct imap_cmd_cb cb;
+
+			if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
+				if (!CAP(AUTH_CRAM_MD5)) {
+					fprintf(stderr, "You specified"
+						"CRAM-MD5 as authentication method, "
+						"but %s doesn't support it.\n", srvc->host);
+					goto bail;
+				}
+				/* CRAM-MD5 */
+
+				memset(&cb, 0, sizeof(cb));
+				cb.cont = auth_cram_md5;
+				if (imap_exec(ctx, &cb, "AUTHENTICATE CRAM-MD5") != RESP_OK) {
+					fprintf(stderr, "IMAP error: AUTHENTICATE CRAM-MD5 failed\n");
+					goto bail;
+				}
+			} else {
+				fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
+				goto bail;
+			}
+		} else {
+			if (!imap->buf.sock.ssl)
+				imap_warn("*** IMAP Warning *** Password is being "
+					  "sent in the clear\n");
+			if (imap_exec(ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass) != RESP_OK) {
+				fprintf(stderr, "IMAP error: LOGIN failed\n");
+				goto bail;
+			}
 		}
 	} /* !preauth */
 
@@ -1198,88 +1264,59 @@
 	return d;
 }
 
-#define TUIDL 8
+static void lf_to_crlf(struct msg_data *msg)
+{
+	char *new;
+	int i, j, lfnum = 0;
 
-static int imap_store_msg(struct store *gctx, struct msg_data *data, int *uid)
+	if (msg->data[0] == '\n')
+		lfnum++;
+	for (i = 1; i < msg->len; i++) {
+		if (msg->data[i - 1] != '\r' && msg->data[i] == '\n')
+			lfnum++;
+	}
+
+	new = xmalloc(msg->len + lfnum);
+	if (msg->data[0] == '\n') {
+		new[0] = '\r';
+		new[1] = '\n';
+		i = 1;
+		j = 2;
+	} else {
+		new[0] = msg->data[0];
+		i = 1;
+		j = 1;
+	}
+	for ( ; i < msg->len; i++) {
+		if (msg->data[i] != '\n') {
+			new[j++] = msg->data[i];
+			continue;
+		}
+		if (msg->data[i - 1] != '\r')
+			new[j++] = '\r';
+		/* otherwise it already had CR before */
+		new[j++] = '\n';
+	}
+	msg->len += lfnum;
+	free(msg->data);
+	msg->data = new;
+}
+
+static int imap_store_msg(struct store *gctx, struct msg_data *data)
 {
 	struct imap_store *ctx = (struct imap_store *)gctx;
 	struct imap *imap = ctx->imap;
 	struct imap_cmd_cb cb;
-	char *fmap, *buf;
 	const char *prefix, *box;
-	int ret, i, j, d, len, extra, nocr;
-	int start, sbreak = 0, ebreak = 0;
-	char flagstr[128], tuid[TUIDL * 2 + 1];
+	int ret, d;
+	char flagstr[128];
 
+	lf_to_crlf(data);
 	memset(&cb, 0, sizeof(cb));
 
-	fmap = data->data;
-	len = data->len;
-	nocr = !data->crlf;
-	extra = 0, i = 0;
-	if (!CAP(UIDPLUS) && uid) {
-	nloop:
-		start = i;
-		while (i < len)
-			if (fmap[i++] == '\n') {
-				extra += nocr;
-				if (i - 2 + nocr == start) {
-					sbreak = ebreak = i - 2 + nocr;
-					goto mktid;
-				}
-				if (!memcmp(fmap + start, "X-TUID: ", 8)) {
-					extra -= (ebreak = i) - (sbreak = start) + nocr;
-					goto mktid;
-				}
-				goto nloop;
-			}
-		/* invalid message */
-		free(fmap);
-		return DRV_MSG_BAD;
-	mktid:
-		for (j = 0; j < TUIDL; j++)
-			sprintf(tuid + j * 2, "%02x", arc4_getbyte());
-		extra += 8 + TUIDL * 2 + 2;
-	}
-	if (nocr)
-		for (; i < len; i++)
-			if (fmap[i] == '\n')
-				extra++;
-
-	cb.dlen = len + extra;
-	buf = cb.data = xmalloc(cb.dlen);
-	i = 0;
-	if (!CAP(UIDPLUS) && uid) {
-		if (nocr) {
-			for (; i < sbreak; i++)
-				if (fmap[i] == '\n') {
-					*buf++ = '\r';
-					*buf++ = '\n';
-				} else
-					*buf++ = fmap[i];
-		} else {
-			memcpy(buf, fmap, sbreak);
-			buf += sbreak;
-		}
-		memcpy(buf, "X-TUID: ", 8);
-		buf += 8;
-		memcpy(buf, tuid, TUIDL * 2);
-		buf += TUIDL * 2;
-		*buf++ = '\r';
-		*buf++ = '\n';
-		i = ebreak;
-	}
-	if (nocr) {
-		for (; i < len; i++)
-			if (fmap[i] == '\n') {
-				*buf++ = '\r';
-				*buf++ = '\n';
-			} else
-				*buf++ = fmap[i];
-	} else
-		memcpy(buf, fmap + i, len - i);
-
-	free(fmap);
+	cb.dlen = data->len;
+	cb.data = xmalloc(cb.dlen);
+	memcpy(cb.data, data->data, data->len);
 
 	d = 0;
 	if (data->flags) {
@@ -1288,26 +1325,14 @@
 	}
 	flagstr[d] = 0;
 
-	if (!uid) {
-		box = gctx->conf->trash;
-		prefix = ctx->prefix;
-		cb.create = 1;
-		if (ctx->trashnc)
-			imap->caps = imap->rcaps & ~(1 << LITERALPLUS);
-	} else {
-		box = gctx->name;
-		prefix = !strcmp(box, "INBOX") ? "" : ctx->prefix;
-		cb.create = 0;
-	}
-	cb.ctx = uid;
+	box = gctx->name;
+	prefix = !strcmp(box, "INBOX") ? "" : ctx->prefix;
+	cb.create = 0;
 	ret = imap_exec_m(ctx, &cb, "APPEND \"%s%s\" %s", prefix, box, flagstr);
 	imap->caps = imap->rcaps;
 	if (ret != DRV_OK)
 		return ret;
-	if (!uid)
-		ctx->trashnc = 0;
-	else
-		gctx->count++;
+	gctx->count++;
 
 	return DRV_OK;
 }
@@ -1384,8 +1409,14 @@
 
 	while (1) {
 		if (!prefixcmp(p, "From ")) {
+			p = strstr(p+5, "\nFrom: ");
+			if (!p) break;
+			p = strstr(p+7, "\nDate: ");
+			if (!p) break;
+			p = strstr(p+7, "\nSubject: ");
+			if (!p) break;
+			p += 10;
 			count++;
-			p += 5;
 		}
 		p = strstr(p+5, "\nFrom ");
 		if (!p)
@@ -1426,18 +1457,6 @@
 	return 1;
 }
 
-static struct imap_server_conf server = {
-	NULL,	/* name */
-	NULL,	/* tunnel */
-	NULL,	/* host */
-	0,	/* port */
-	NULL,	/* user */
-	NULL,	/* pass */
-	0,   	/* use_ssl */
-	1,   	/* ssl_verify */
-	0,   	/* use_html */
-};
-
 static char *imap_folder;
 
 static int git_imap_config(const char *key, const char *val, void *cb)
@@ -1447,11 +1466,16 @@
 	if (strncmp(key, imap_key, sizeof imap_key - 1))
 		return 0;
 
-	if (!val)
-		return config_error_nonbool(key);
-
 	key += sizeof imap_key - 1;
 
+	/* check booleans first, and barf on others */
+	if (!strcmp("sslverify", key))
+		server.ssl_verify = git_config_bool(key, val);
+	else if (!strcmp("preformattedhtml", key))
+		server.use_html = git_config_bool(key, val);
+	else if (!val)
+		return config_error_nonbool(key);
+
 	if (!strcmp("folder", key)) {
 		imap_folder = xstrdup(val);
 	} else if (!strcmp("host", key)) {
@@ -1472,10 +1496,9 @@
 		server.port = git_config_int(key, val);
 	else if (!strcmp("tunnel", key))
 		server.tunnel = xstrdup(val);
-	else if (!strcmp("sslverify", key))
-		server.ssl_verify = git_config_bool(key, val);
-	else if (!strcmp("preformattedHTML", key))
-		server.use_html = git_config_bool(key, val);
+	else if (!strcmp("authmethod", key))
+		server.auth_method = xstrdup(val);
+
 	return 0;
 }
 
@@ -1483,7 +1506,6 @@
 {
 	struct msg_data all_msgs, msg;
 	struct store *ctx = NULL;
-	int uid = 0;
 	int ofs = 0;
 	int r;
 	int total, n = 0;
@@ -1491,8 +1513,10 @@
 
 	git_extract_argv0_path(argv[0]);
 
-	/* init the random number generator */
-	arc4_init();
+	git_setup_gettext();
+
+	if (argc != 1)
+		usage(imap_send_usage);
 
 	setup_git_directory_gently(&nongit_ok);
 	git_config(git_imap_config, NULL);
@@ -1540,7 +1564,7 @@
 			break;
 		if (server.use_html)
 			wrap_in_html(&msg);
-		r = imap_store_msg(ctx, &msg, &uid);
+		r = imap_store_msg(ctx, &msg);
 		if (r != DRV_OK)
 			break;
 		n++;
diff --git a/index-pack.c b/index-pack.c
deleted file mode 100644
index 340074f..0000000
--- a/index-pack.c
+++ /dev/null
@@ -1,1044 +0,0 @@
-#include "cache.h"
-#include "delta.h"
-#include "pack.h"
-#include "csum-file.h"
-#include "blob.h"
-#include "commit.h"
-#include "tag.h"
-#include "tree.h"
-#include "progress.h"
-#include "fsck.h"
-#include "exec_cmd.h"
-
-static const char index_pack_usage[] =
-"git index-pack [-v] [-o <index-file>] [{ ---keep | --keep=<msg> }] [--strict] { <pack-file> | --stdin [--fix-thin] [<pack-file>] }";
-
-struct object_entry
-{
-	struct pack_idx_entry idx;
-	unsigned long size;
-	unsigned int hdr_size;
-	enum object_type type;
-	enum object_type real_type;
-};
-
-union delta_base {
-	unsigned char sha1[20];
-	off_t offset;
-};
-
-struct base_data {
-	struct base_data *base;
-	struct base_data *child;
-	struct object_entry *obj;
-	void *data;
-	unsigned long size;
-};
-
-/*
- * Even if sizeof(union delta_base) == 24 on 64-bit archs, we really want
- * to memcmp() only the first 20 bytes.
- */
-#define UNION_BASE_SZ	20
-
-#define FLAG_LINK (1u<<20)
-#define FLAG_CHECKED (1u<<21)
-
-struct delta_entry
-{
-	union delta_base base;
-	int obj_no;
-};
-
-static struct object_entry *objects;
-static struct delta_entry *deltas;
-static struct base_data *base_cache;
-static size_t base_cache_used;
-static int nr_objects;
-static int nr_deltas;
-static int nr_resolved_deltas;
-
-static int from_stdin;
-static int strict;
-static int verbose;
-
-static struct progress *progress;
-
-/* We always read in 4kB chunks. */
-static unsigned char input_buffer[4096];
-static unsigned int input_offset, input_len;
-static off_t consumed_bytes;
-static git_SHA_CTX input_ctx;
-static uint32_t input_crc32;
-static int input_fd, output_fd, pack_fd;
-
-static int mark_link(struct object *obj, int type, void *data)
-{
-	if (!obj)
-		return -1;
-
-	if (type != OBJ_ANY && obj->type != type)
-		die("object type mismatch at %s", sha1_to_hex(obj->sha1));
-
-	obj->flags |= FLAG_LINK;
-	return 0;
-}
-
-/* The content of each linked object must have been checked
-   or it must be already present in the object database */
-static void check_object(struct object *obj)
-{
-	if (!obj)
-		return;
-
-	if (!(obj->flags & FLAG_LINK))
-		return;
-
-	if (!(obj->flags & FLAG_CHECKED)) {
-		unsigned long size;
-		int type = sha1_object_info(obj->sha1, &size);
-		if (type != obj->type || type <= 0)
-			die("object of unexpected type");
-		obj->flags |= FLAG_CHECKED;
-		return;
-	}
-}
-
-static void check_objects(void)
-{
-	unsigned i, max;
-
-	max = get_max_object_index();
-	for (i = 0; i < max; i++)
-		check_object(get_indexed_object(i));
-}
-
-
-/* Discard current buffer used content. */
-static void flush(void)
-{
-	if (input_offset) {
-		if (output_fd >= 0)
-			write_or_die(output_fd, input_buffer, input_offset);
-		git_SHA1_Update(&input_ctx, input_buffer, input_offset);
-		memmove(input_buffer, input_buffer + input_offset, input_len);
-		input_offset = 0;
-	}
-}
-
-/*
- * Make sure at least "min" bytes are available in the buffer, and
- * return the pointer to the buffer.
- */
-static void *fill(int min)
-{
-	if (min <= input_len)
-		return input_buffer + input_offset;
-	if (min > sizeof(input_buffer))
-		die("cannot fill %d bytes", min);
-	flush();
-	do {
-		ssize_t ret = xread(input_fd, input_buffer + input_len,
-				sizeof(input_buffer) - input_len);
-		if (ret <= 0) {
-			if (!ret)
-				die("early EOF");
-			die_errno("read error on input");
-		}
-		input_len += ret;
-		if (from_stdin)
-			display_throughput(progress, consumed_bytes + input_len);
-	} while (input_len < min);
-	return input_buffer;
-}
-
-static void use(int bytes)
-{
-	if (bytes > input_len)
-		die("used more bytes than were available");
-	input_crc32 = crc32(input_crc32, input_buffer + input_offset, bytes);
-	input_len -= bytes;
-	input_offset += bytes;
-
-	/* make sure off_t is sufficiently large not to wrap */
-	if (consumed_bytes > consumed_bytes + bytes)
-		die("pack too large for current definition of off_t");
-	consumed_bytes += bytes;
-}
-
-static char *open_pack_file(char *pack_name)
-{
-	if (from_stdin) {
-		input_fd = 0;
-		if (!pack_name) {
-			static char tmpfile[PATH_MAX];
-			output_fd = odb_mkstemp(tmpfile, sizeof(tmpfile),
-						"pack/tmp_pack_XXXXXX");
-			pack_name = xstrdup(tmpfile);
-		} else
-			output_fd = open(pack_name, O_CREAT|O_EXCL|O_RDWR, 0600);
-		if (output_fd < 0)
-			die_errno("unable to create '%s'", pack_name);
-		pack_fd = output_fd;
-	} else {
-		input_fd = open(pack_name, O_RDONLY);
-		if (input_fd < 0)
-			die_errno("cannot open packfile '%s'", pack_name);
-		output_fd = -1;
-		pack_fd = input_fd;
-	}
-	git_SHA1_Init(&input_ctx);
-	return pack_name;
-}
-
-static void parse_pack_header(void)
-{
-	struct pack_header *hdr = fill(sizeof(struct pack_header));
-
-	/* Header consistency check */
-	if (hdr->hdr_signature != htonl(PACK_SIGNATURE))
-		die("pack signature mismatch");
-	if (!pack_version_ok(hdr->hdr_version))
-		die("pack version %"PRIu32" unsupported",
-			ntohl(hdr->hdr_version));
-
-	nr_objects = ntohl(hdr->hdr_entries);
-	use(sizeof(struct pack_header));
-}
-
-static void bad_object(unsigned long offset, const char *format,
-		       ...) NORETURN __attribute__((format (printf, 2, 3)));
-
-static void bad_object(unsigned long offset, const char *format, ...)
-{
-	va_list params;
-	char buf[1024];
-
-	va_start(params, format);
-	vsnprintf(buf, sizeof(buf), format, params);
-	va_end(params);
-	die("pack has bad object at offset %lu: %s", offset, buf);
-}
-
-static void free_base_data(struct base_data *c)
-{
-	if (c->data) {
-		free(c->data);
-		c->data = NULL;
-		base_cache_used -= c->size;
-	}
-}
-
-static void prune_base_data(struct base_data *retain)
-{
-	struct base_data *b;
-	for (b = base_cache;
-	     base_cache_used > delta_base_cache_limit && b;
-	     b = b->child) {
-		if (b->data && b != retain)
-			free_base_data(b);
-	}
-}
-
-static void link_base_data(struct base_data *base, struct base_data *c)
-{
-	if (base)
-		base->child = c;
-	else
-		base_cache = c;
-
-	c->base = base;
-	c->child = NULL;
-	if (c->data)
-		base_cache_used += c->size;
-	prune_base_data(c);
-}
-
-static void unlink_base_data(struct base_data *c)
-{
-	struct base_data *base = c->base;
-	if (base)
-		base->child = NULL;
-	else
-		base_cache = NULL;
-	free_base_data(c);
-}
-
-static void *unpack_entry_data(unsigned long offset, unsigned long size)
-{
-	z_stream stream;
-	void *buf = xmalloc(size);
-
-	memset(&stream, 0, sizeof(stream));
-	stream.next_out = buf;
-	stream.avail_out = size;
-	stream.next_in = fill(1);
-	stream.avail_in = input_len;
-	git_inflate_init(&stream);
-
-	for (;;) {
-		int ret = git_inflate(&stream, 0);
-		use(input_len - stream.avail_in);
-		if (stream.total_out == size && ret == Z_STREAM_END)
-			break;
-		if (ret != Z_OK)
-			bad_object(offset, "inflate returned %d", ret);
-		stream.next_in = fill(1);
-		stream.avail_in = input_len;
-	}
-	git_inflate_end(&stream);
-	return buf;
-}
-
-static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_base)
-{
-	unsigned char *p;
-	unsigned long size, c;
-	off_t base_offset;
-	unsigned shift;
-	void *data;
-
-	obj->idx.offset = consumed_bytes;
-	input_crc32 = crc32(0, Z_NULL, 0);
-
-	p = fill(1);
-	c = *p;
-	use(1);
-	obj->type = (c >> 4) & 7;
-	size = (c & 15);
-	shift = 4;
-	while (c & 0x80) {
-		p = fill(1);
-		c = *p;
-		use(1);
-		size += (c & 0x7f) << shift;
-		shift += 7;
-	}
-	obj->size = size;
-
-	switch (obj->type) {
-	case OBJ_REF_DELTA:
-		hashcpy(delta_base->sha1, fill(20));
-		use(20);
-		break;
-	case OBJ_OFS_DELTA:
-		memset(delta_base, 0, sizeof(*delta_base));
-		p = fill(1);
-		c = *p;
-		use(1);
-		base_offset = c & 127;
-		while (c & 128) {
-			base_offset += 1;
-			if (!base_offset || MSB(base_offset, 7))
-				bad_object(obj->idx.offset, "offset value overflow for delta base object");
-			p = fill(1);
-			c = *p;
-			use(1);
-			base_offset = (base_offset << 7) + (c & 127);
-		}
-		delta_base->offset = obj->idx.offset - base_offset;
-		if (delta_base->offset <= 0 || delta_base->offset >= obj->idx.offset)
-			bad_object(obj->idx.offset, "delta base offset is out of bound");
-		break;
-	case OBJ_COMMIT:
-	case OBJ_TREE:
-	case OBJ_BLOB:
-	case OBJ_TAG:
-		break;
-	default:
-		bad_object(obj->idx.offset, "unknown object type %d", obj->type);
-	}
-	obj->hdr_size = consumed_bytes - obj->idx.offset;
-
-	data = unpack_entry_data(obj->idx.offset, obj->size);
-	obj->idx.crc32 = input_crc32;
-	return data;
-}
-
-static void *get_data_from_pack(struct object_entry *obj)
-{
-	off_t from = obj[0].idx.offset + obj[0].hdr_size;
-	unsigned long len = obj[1].idx.offset - from;
-	unsigned long rdy = 0;
-	unsigned char *src, *data;
-	z_stream stream;
-	int st;
-
-	src = xmalloc(len);
-	data = src;
-	do {
-		ssize_t n = pread(pack_fd, data + rdy, len - rdy, from + rdy);
-		if (n < 0)
-			die_errno("cannot pread pack file");
-		if (!n)
-			die("premature end of pack file, %lu bytes missing",
-			    len - rdy);
-		rdy += n;
-	} while (rdy < len);
-	data = xmalloc(obj->size);
-	memset(&stream, 0, sizeof(stream));
-	stream.next_out = data;
-	stream.avail_out = obj->size;
-	stream.next_in = src;
-	stream.avail_in = len;
-	git_inflate_init(&stream);
-	while ((st = git_inflate(&stream, Z_FINISH)) == Z_OK);
-	git_inflate_end(&stream);
-	if (st != Z_STREAM_END || stream.total_out != obj->size)
-		die("serious inflate inconsistency");
-	free(src);
-	return data;
-}
-
-static int find_delta(const union delta_base *base)
-{
-	int first = 0, last = nr_deltas;
-
-        while (first < last) {
-                int next = (first + last) / 2;
-                struct delta_entry *delta = &deltas[next];
-                int cmp;
-
-                cmp = memcmp(base, &delta->base, UNION_BASE_SZ);
-                if (!cmp)
-                        return next;
-                if (cmp < 0) {
-                        last = next;
-                        continue;
-                }
-                first = next+1;
-        }
-        return -first-1;
-}
-
-static void find_delta_children(const union delta_base *base,
-				int *first_index, int *last_index)
-{
-	int first = find_delta(base);
-	int last = first;
-	int end = nr_deltas - 1;
-
-	if (first < 0) {
-		*first_index = 0;
-		*last_index = -1;
-		return;
-	}
-	while (first > 0 && !memcmp(&deltas[first - 1].base, base, UNION_BASE_SZ))
-		--first;
-	while (last < end && !memcmp(&deltas[last + 1].base, base, UNION_BASE_SZ))
-		++last;
-	*first_index = first;
-	*last_index = last;
-}
-
-static void sha1_object(const void *data, unsigned long size,
-			enum object_type type, unsigned char *sha1)
-{
-	hash_sha1_file(data, size, typename(type), sha1);
-	if (has_sha1_file(sha1)) {
-		void *has_data;
-		enum object_type has_type;
-		unsigned long has_size;
-		has_data = read_sha1_file(sha1, &has_type, &has_size);
-		if (!has_data)
-			die("cannot read existing object %s", sha1_to_hex(sha1));
-		if (size != has_size || type != has_type ||
-		    memcmp(data, has_data, size) != 0)
-			die("SHA1 COLLISION FOUND WITH %s !", sha1_to_hex(sha1));
-		free(has_data);
-	}
-	if (strict) {
-		if (type == OBJ_BLOB) {
-			struct blob *blob = lookup_blob(sha1);
-			if (blob)
-				blob->object.flags |= FLAG_CHECKED;
-			else
-				die("invalid blob object %s", sha1_to_hex(sha1));
-		} else {
-			struct object *obj;
-			int eaten;
-			void *buf = (void *) data;
-
-			/*
-			 * we do not need to free the memory here, as the
-			 * buf is deleted by the caller.
-			 */
-			obj = parse_object_buffer(sha1, type, size, buf, &eaten);
-			if (!obj)
-				die("invalid %s", typename(type));
-			if (fsck_object(obj, 1, fsck_error_function))
-				die("Error in object");
-			if (fsck_walk(obj, mark_link, NULL))
-				die("Not all child objects of %s are reachable", sha1_to_hex(obj->sha1));
-
-			if (obj->type == OBJ_TREE) {
-				struct tree *item = (struct tree *) obj;
-				item->buffer = NULL;
-			}
-			if (obj->type == OBJ_COMMIT) {
-				struct commit *commit = (struct commit *) obj;
-				commit->buffer = NULL;
-			}
-			obj->flags |= FLAG_CHECKED;
-		}
-	}
-}
-
-static void *get_base_data(struct base_data *c)
-{
-	if (!c->data) {
-		struct object_entry *obj = c->obj;
-
-		if (obj->type == OBJ_REF_DELTA || obj->type == OBJ_OFS_DELTA) {
-			void *base = get_base_data(c->base);
-			void *raw = get_data_from_pack(obj);
-			c->data = patch_delta(
-				base, c->base->size,
-				raw, obj->size,
-				&c->size);
-			free(raw);
-			if (!c->data)
-				bad_object(obj->idx.offset, "failed to apply delta");
-		} else {
-			c->data = get_data_from_pack(obj);
-			c->size = obj->size;
-		}
-
-		base_cache_used += c->size;
-		prune_base_data(c);
-	}
-	return c->data;
-}
-
-static void resolve_delta(struct object_entry *delta_obj,
-			  struct base_data *base, struct base_data *result)
-{
-	void *base_data, *delta_data;
-
-	delta_obj->real_type = base->obj->real_type;
-	delta_data = get_data_from_pack(delta_obj);
-	base_data = get_base_data(base);
-	result->obj = delta_obj;
-	result->data = patch_delta(base_data, base->size,
-				   delta_data, delta_obj->size, &result->size);
-	free(delta_data);
-	if (!result->data)
-		bad_object(delta_obj->idx.offset, "failed to apply delta");
-	sha1_object(result->data, result->size, delta_obj->real_type,
-		    delta_obj->idx.sha1);
-	nr_resolved_deltas++;
-}
-
-static void find_unresolved_deltas(struct base_data *base,
-				   struct base_data *prev_base)
-{
-	int i, ref_first, ref_last, ofs_first, ofs_last;
-
-	/*
-	 * This is a recursive function. Those brackets should help reducing
-	 * stack usage by limiting the scope of the delta_base union.
-	 */
-	{
-		union delta_base base_spec;
-
-		hashcpy(base_spec.sha1, base->obj->idx.sha1);
-		find_delta_children(&base_spec, &ref_first, &ref_last);
-
-		memset(&base_spec, 0, sizeof(base_spec));
-		base_spec.offset = base->obj->idx.offset;
-		find_delta_children(&base_spec, &ofs_first, &ofs_last);
-	}
-
-	if (ref_last == -1 && ofs_last == -1) {
-		free(base->data);
-		return;
-	}
-
-	link_base_data(prev_base, base);
-
-	for (i = ref_first; i <= ref_last; i++) {
-		struct object_entry *child = objects + deltas[i].obj_no;
-		if (child->real_type == OBJ_REF_DELTA) {
-			struct base_data result;
-			resolve_delta(child, base, &result);
-			if (i == ref_last && ofs_last == -1)
-				free_base_data(base);
-			find_unresolved_deltas(&result, base);
-		}
-	}
-
-	for (i = ofs_first; i <= ofs_last; i++) {
-		struct object_entry *child = objects + deltas[i].obj_no;
-		if (child->real_type == OBJ_OFS_DELTA) {
-			struct base_data result;
-			resolve_delta(child, base, &result);
-			if (i == ofs_last)
-				free_base_data(base);
-			find_unresolved_deltas(&result, base);
-		}
-	}
-
-	unlink_base_data(base);
-}
-
-static int compare_delta_entry(const void *a, const void *b)
-{
-	const struct delta_entry *delta_a = a;
-	const struct delta_entry *delta_b = b;
-	return memcmp(&delta_a->base, &delta_b->base, UNION_BASE_SZ);
-}
-
-/* Parse all objects and return the pack content SHA1 hash */
-static void parse_pack_objects(unsigned char *sha1)
-{
-	int i;
-	struct delta_entry *delta = deltas;
-	struct stat st;
-
-	/*
-	 * First pass:
-	 * - find locations of all objects;
-	 * - calculate SHA1 of all non-delta objects;
-	 * - remember base (SHA1 or offset) for all deltas.
-	 */
-	if (verbose)
-		progress = start_progress(
-				from_stdin ? "Receiving objects" : "Indexing objects",
-				nr_objects);
-	for (i = 0; i < nr_objects; i++) {
-		struct object_entry *obj = &objects[i];
-		void *data = unpack_raw_entry(obj, &delta->base);
-		obj->real_type = obj->type;
-		if (obj->type == OBJ_REF_DELTA || obj->type == OBJ_OFS_DELTA) {
-			nr_deltas++;
-			delta->obj_no = i;
-			delta++;
-		} else
-			sha1_object(data, obj->size, obj->type, obj->idx.sha1);
-		free(data);
-		display_progress(progress, i+1);
-	}
-	objects[i].idx.offset = consumed_bytes;
-	stop_progress(&progress);
-
-	/* Check pack integrity */
-	flush();
-	git_SHA1_Final(sha1, &input_ctx);
-	if (hashcmp(fill(20), sha1))
-		die("pack is corrupted (SHA1 mismatch)");
-	use(20);
-
-	/* If input_fd is a file, we should have reached its end now. */
-	if (fstat(input_fd, &st))
-		die_errno("cannot fstat packfile");
-	if (S_ISREG(st.st_mode) &&
-			lseek(input_fd, 0, SEEK_CUR) - input_len != st.st_size)
-		die("pack has junk at the end");
-
-	if (!nr_deltas)
-		return;
-
-	/* Sort deltas by base SHA1/offset for fast searching */
-	qsort(deltas, nr_deltas, sizeof(struct delta_entry),
-	      compare_delta_entry);
-
-	/*
-	 * Second pass:
-	 * - for all non-delta objects, look if it is used as a base for
-	 *   deltas;
-	 * - if used as a base, uncompress the object and apply all deltas,
-	 *   recursively checking if the resulting object is used as a base
-	 *   for some more deltas.
-	 */
-	if (verbose)
-		progress = start_progress("Resolving deltas", nr_deltas);
-	for (i = 0; i < nr_objects; i++) {
-		struct object_entry *obj = &objects[i];
-		struct base_data base_obj;
-
-		if (obj->type == OBJ_REF_DELTA || obj->type == OBJ_OFS_DELTA)
-			continue;
-		base_obj.obj = obj;
-		base_obj.data = NULL;
-		find_unresolved_deltas(&base_obj, NULL);
-		display_progress(progress, nr_resolved_deltas);
-	}
-}
-
-static int write_compressed(struct sha1file *f, void *in, unsigned int size)
-{
-	z_stream stream;
-	unsigned long maxsize;
-	void *out;
-
-	memset(&stream, 0, sizeof(stream));
-	deflateInit(&stream, zlib_compression_level);
-	maxsize = deflateBound(&stream, size);
-	out = xmalloc(maxsize);
-
-	/* Compress it */
-	stream.next_in = in;
-	stream.avail_in = size;
-	stream.next_out = out;
-	stream.avail_out = maxsize;
-	while (deflate(&stream, Z_FINISH) == Z_OK);
-	deflateEnd(&stream);
-
-	size = stream.total_out;
-	sha1write(f, out, size);
-	free(out);
-	return size;
-}
-
-static struct object_entry *append_obj_to_pack(struct sha1file *f,
-			       const unsigned char *sha1, void *buf,
-			       unsigned long size, enum object_type type)
-{
-	struct object_entry *obj = &objects[nr_objects++];
-	unsigned char header[10];
-	unsigned long s = size;
-	int n = 0;
-	unsigned char c = (type << 4) | (s & 15);
-	s >>= 4;
-	while (s) {
-		header[n++] = c | 0x80;
-		c = s & 0x7f;
-		s >>= 7;
-	}
-	header[n++] = c;
-	crc32_begin(f);
-	sha1write(f, header, n);
-	obj[0].size = size;
-	obj[0].hdr_size = n;
-	obj[0].type = type;
-	obj[0].real_type = type;
-	obj[1].idx.offset = obj[0].idx.offset + n;
-	obj[1].idx.offset += write_compressed(f, buf, size);
-	obj[0].idx.crc32 = crc32_end(f);
-	sha1flush(f);
-	hashcpy(obj->idx.sha1, sha1);
-	return obj;
-}
-
-static int delta_pos_compare(const void *_a, const void *_b)
-{
-	struct delta_entry *a = *(struct delta_entry **)_a;
-	struct delta_entry *b = *(struct delta_entry **)_b;
-	return a->obj_no - b->obj_no;
-}
-
-static void fix_unresolved_deltas(struct sha1file *f, int nr_unresolved)
-{
-	struct delta_entry **sorted_by_pos;
-	int i, n = 0;
-
-	/*
-	 * Since many unresolved deltas may well be themselves base objects
-	 * for more unresolved deltas, we really want to include the
-	 * smallest number of base objects that would cover as much delta
-	 * as possible by picking the
-	 * trunc deltas first, allowing for other deltas to resolve without
-	 * additional base objects.  Since most base objects are to be found
-	 * before deltas depending on them, a good heuristic is to start
-	 * resolving deltas in the same order as their position in the pack.
-	 */
-	sorted_by_pos = xmalloc(nr_unresolved * sizeof(*sorted_by_pos));
-	for (i = 0; i < nr_deltas; i++) {
-		if (objects[deltas[i].obj_no].real_type != OBJ_REF_DELTA)
-			continue;
-		sorted_by_pos[n++] = &deltas[i];
-	}
-	qsort(sorted_by_pos, n, sizeof(*sorted_by_pos), delta_pos_compare);
-
-	for (i = 0; i < n; i++) {
-		struct delta_entry *d = sorted_by_pos[i];
-		enum object_type type;
-		struct base_data base_obj;
-
-		if (objects[d->obj_no].real_type != OBJ_REF_DELTA)
-			continue;
-		base_obj.data = read_sha1_file(d->base.sha1, &type, &base_obj.size);
-		if (!base_obj.data)
-			continue;
-
-		if (check_sha1_signature(d->base.sha1, base_obj.data,
-				base_obj.size, typename(type)))
-			die("local object %s is corrupt", sha1_to_hex(d->base.sha1));
-		base_obj.obj = append_obj_to_pack(f, d->base.sha1,
-					base_obj.data, base_obj.size, type);
-		find_unresolved_deltas(&base_obj, NULL);
-		display_progress(progress, nr_resolved_deltas);
-	}
-	free(sorted_by_pos);
-}
-
-static void final(const char *final_pack_name, const char *curr_pack_name,
-		  const char *final_index_name, const char *curr_index_name,
-		  const char *keep_name, const char *keep_msg,
-		  unsigned char *sha1)
-{
-	const char *report = "pack";
-	char name[PATH_MAX];
-	int err;
-
-	if (!from_stdin) {
-		close(input_fd);
-	} else {
-		fsync_or_die(output_fd, curr_pack_name);
-		err = close(output_fd);
-		if (err)
-			die_errno("error while closing pack file");
-	}
-
-	if (keep_msg) {
-		int keep_fd, keep_msg_len = strlen(keep_msg);
-
-		if (!keep_name)
-			keep_fd = odb_pack_keep(name, sizeof(name), sha1);
-		else
-			keep_fd = open(keep_name, O_RDWR|O_CREAT|O_EXCL, 0600);
-
-		if (keep_fd < 0) {
-			if (errno != EEXIST)
-				die_errno("cannot write keep file '%s'",
-					  keep_name);
-		} else {
-			if (keep_msg_len > 0) {
-				write_or_die(keep_fd, keep_msg, keep_msg_len);
-				write_or_die(keep_fd, "\n", 1);
-			}
-			if (close(keep_fd) != 0)
-				die_errno("cannot close written keep file '%s'",
-				    keep_name);
-			report = "keep";
-		}
-	}
-
-	if (final_pack_name != curr_pack_name) {
-		if (!final_pack_name) {
-			snprintf(name, sizeof(name), "%s/pack/pack-%s.pack",
-				 get_object_directory(), sha1_to_hex(sha1));
-			final_pack_name = name;
-		}
-		if (move_temp_to_file(curr_pack_name, final_pack_name))
-			die("cannot store pack file");
-	} else if (from_stdin)
-		chmod(final_pack_name, 0444);
-
-	if (final_index_name != curr_index_name) {
-		if (!final_index_name) {
-			snprintf(name, sizeof(name), "%s/pack/pack-%s.idx",
-				 get_object_directory(), sha1_to_hex(sha1));
-			final_index_name = name;
-		}
-		if (move_temp_to_file(curr_index_name, final_index_name))
-			die("cannot store index file");
-	} else
-		chmod(final_index_name, 0444);
-
-	if (!from_stdin) {
-		printf("%s\n", sha1_to_hex(sha1));
-	} else {
-		char buf[48];
-		int len = snprintf(buf, sizeof(buf), "%s\t%s\n",
-				   report, sha1_to_hex(sha1));
-		write_or_die(1, buf, len);
-
-		/*
-		 * Let's just mimic git-unpack-objects here and write
-		 * the last part of the input buffer to stdout.
-		 */
-		while (input_len) {
-			err = xwrite(1, input_buffer + input_offset, input_len);
-			if (err <= 0)
-				break;
-			input_len -= err;
-			input_offset += err;
-		}
-	}
-}
-
-static int git_index_pack_config(const char *k, const char *v, void *cb)
-{
-	if (!strcmp(k, "pack.indexversion")) {
-		pack_idx_default_version = git_config_int(k, v);
-		if (pack_idx_default_version > 2)
-			die("bad pack.indexversion=%"PRIu32,
-				pack_idx_default_version);
-		return 0;
-	}
-	return git_default_config(k, v, cb);
-}
-
-int main(int argc, char **argv)
-{
-	int i, fix_thin_pack = 0;
-	char *curr_pack, *pack_name = NULL;
-	char *curr_index, *index_name = NULL;
-	const char *keep_name = NULL, *keep_msg = NULL;
-	char *index_name_buf = NULL, *keep_name_buf = NULL;
-	struct pack_idx_entry **idx_objects;
-	unsigned char pack_sha1[20];
-
-	git_extract_argv0_path(argv[0]);
-
-	/*
-	 * We wish to read the repository's config file if any, and
-	 * for that it is necessary to call setup_git_directory_gently().
-	 * However if the cwd was inside .git/objects/pack/ then we need
-	 * to go back there or all the pack name arguments will be wrong.
-	 * And in that case we cannot rely on any prefix returned by
-	 * setup_git_directory_gently() either.
-	 */
-	{
-		char cwd[PATH_MAX+1];
-		int nongit;
-
-		if (!getcwd(cwd, sizeof(cwd)-1))
-			die("Unable to get current working directory");
-		setup_git_directory_gently(&nongit);
-		git_config(git_index_pack_config, NULL);
-		if (chdir(cwd))
-			die("Cannot come back to cwd");
-	}
-
-	for (i = 1; i < argc; i++) {
-		char *arg = argv[i];
-
-		if (*arg == '-') {
-			if (!strcmp(arg, "--stdin")) {
-				from_stdin = 1;
-			} else if (!strcmp(arg, "--fix-thin")) {
-				fix_thin_pack = 1;
-			} else if (!strcmp(arg, "--strict")) {
-				strict = 1;
-			} else if (!strcmp(arg, "--keep")) {
-				keep_msg = "";
-			} else if (!prefixcmp(arg, "--keep=")) {
-				keep_msg = arg + 7;
-			} else if (!prefixcmp(arg, "--pack_header=")) {
-				struct pack_header *hdr;
-				char *c;
-
-				hdr = (struct pack_header *)input_buffer;
-				hdr->hdr_signature = htonl(PACK_SIGNATURE);
-				hdr->hdr_version = htonl(strtoul(arg + 14, &c, 10));
-				if (*c != ',')
-					die("bad %s", arg);
-				hdr->hdr_entries = htonl(strtoul(c + 1, &c, 10));
-				if (*c)
-					die("bad %s", arg);
-				input_len = sizeof(*hdr);
-			} else if (!strcmp(arg, "-v")) {
-				verbose = 1;
-			} else if (!strcmp(arg, "-o")) {
-				if (index_name || (i+1) >= argc)
-					usage(index_pack_usage);
-				index_name = argv[++i];
-			} else if (!prefixcmp(arg, "--index-version=")) {
-				char *c;
-				pack_idx_default_version = strtoul(arg + 16, &c, 10);
-				if (pack_idx_default_version > 2)
-					die("bad %s", arg);
-				if (*c == ',')
-					pack_idx_off32_limit = strtoul(c+1, &c, 0);
-				if (*c || pack_idx_off32_limit & 0x80000000)
-					die("bad %s", arg);
-			} else
-				usage(index_pack_usage);
-			continue;
-		}
-
-		if (pack_name)
-			usage(index_pack_usage);
-		pack_name = arg;
-	}
-
-	if (!pack_name && !from_stdin)
-		usage(index_pack_usage);
-	if (fix_thin_pack && !from_stdin)
-		die("--fix-thin cannot be used without --stdin");
-	if (!index_name && pack_name) {
-		int len = strlen(pack_name);
-		if (!has_extension(pack_name, ".pack"))
-			die("packfile name '%s' does not end with '.pack'",
-			    pack_name);
-		index_name_buf = xmalloc(len);
-		memcpy(index_name_buf, pack_name, len - 5);
-		strcpy(index_name_buf + len - 5, ".idx");
-		index_name = index_name_buf;
-	}
-	if (keep_msg && !keep_name && pack_name) {
-		int len = strlen(pack_name);
-		if (!has_extension(pack_name, ".pack"))
-			die("packfile name '%s' does not end with '.pack'",
-			    pack_name);
-		keep_name_buf = xmalloc(len);
-		memcpy(keep_name_buf, pack_name, len - 5);
-		strcpy(keep_name_buf + len - 5, ".keep");
-		keep_name = keep_name_buf;
-	}
-
-	curr_pack = open_pack_file(pack_name);
-	parse_pack_header();
-	objects = xmalloc((nr_objects + 1) * sizeof(struct object_entry));
-	deltas = xmalloc(nr_objects * sizeof(struct delta_entry));
-	parse_pack_objects(pack_sha1);
-	if (nr_deltas == nr_resolved_deltas) {
-		stop_progress(&progress);
-		/* Flush remaining pack final 20-byte SHA1. */
-		flush();
-	} else {
-		if (fix_thin_pack) {
-			struct sha1file *f;
-			unsigned char read_sha1[20], tail_sha1[20];
-			char msg[48];
-			int nr_unresolved = nr_deltas - nr_resolved_deltas;
-			int nr_objects_initial = nr_objects;
-			if (nr_unresolved <= 0)
-				die("confusion beyond insanity");
-			objects = xrealloc(objects,
-					   (nr_objects + nr_unresolved + 1)
-					   * sizeof(*objects));
-			f = sha1fd(output_fd, curr_pack);
-			fix_unresolved_deltas(f, nr_unresolved);
-			sprintf(msg, "completed with %d local objects",
-				nr_objects - nr_objects_initial);
-			stop_progress_msg(&progress, msg);
-			sha1close(f, tail_sha1, 0);
-			hashcpy(read_sha1, pack_sha1);
-			fixup_pack_header_footer(output_fd, pack_sha1,
-						 curr_pack, nr_objects,
-						 read_sha1, consumed_bytes-20);
-			if (hashcmp(read_sha1, tail_sha1) != 0)
-				die("Unexpected tail checksum for %s "
-				    "(disk corruption?)", curr_pack);
-		}
-		if (nr_deltas != nr_resolved_deltas)
-			die("pack has %d unresolved deltas",
-			    nr_deltas - nr_resolved_deltas);
-	}
-	free(deltas);
-	if (strict)
-		check_objects();
-
-	idx_objects = xmalloc((nr_objects) * sizeof(struct pack_idx_entry *));
-	for (i = 0; i < nr_objects; i++)
-		idx_objects[i] = &objects[i].idx;
-	curr_index = write_idx_file(index_name, idx_objects, nr_objects, pack_sha1);
-	free(idx_objects);
-
-	final(pack_name, curr_pack,
-		index_name, curr_index,
-		keep_name, keep_msg,
-		pack_sha1);
-	free(objects);
-	free(index_name_buf);
-	free(keep_name_buf);
-	if (pack_name == NULL)
-		free(curr_pack);
-	if (index_name == NULL)
-		free(curr_index);
-
-	return 0;
-}
diff --git a/kwset.c b/kwset.c
new file mode 100644
index 0000000..51b2ab6
--- /dev/null
+++ b/kwset.c
@@ -0,0 +1,771 @@
+/*
+ * This file has been copied from commit e7ac713d^ in the GNU grep git
+ * repository. A few small changes have been made to adapt the code to
+ * Git.
+ */
+
+/* kwset.c - search for any of a set of keywords.
+   Copyright 1989, 1998, 2000, 2005 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
+
+/* Written August 1989 by Mike Haertel.
+   The author may be reached (Email) at the address mike@ai.mit.edu,
+   or (US mail) as Mike Haertel c/o Free Software Foundation. */
+
+/* The algorithm implemented by these routines bears a startling resemblence
+   to one discovered by Beate Commentz-Walter, although it is not identical.
+   See "A String Matching Algorithm Fast on the Average," Technical Report,
+   IBM-Germany, Scientific Center Heidelberg, Tiergartenstrasse 15, D-6900
+   Heidelberg, Germany.  See also Aho, A.V., and M. Corasick, "Efficient
+   String Matching:  An Aid to Bibliographic Search," CACM June 1975,
+   Vol. 18, No. 6, which describes the failure function used below. */
+
+#include "cache.h"
+
+#include "kwset.h"
+#include "compat/obstack.h"
+
+#define NCHAR (UCHAR_MAX + 1)
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+
+#define U(c) ((unsigned char) (c))
+
+/* Balanced tree of edges and labels leaving a given trie node. */
+struct tree
+{
+  struct tree *llink;		/* Left link; MUST be first field. */
+  struct tree *rlink;		/* Right link (to larger labels). */
+  struct trie *trie;		/* Trie node pointed to by this edge. */
+  unsigned char label;		/* Label on this edge. */
+  char balance;			/* Difference in depths of subtrees. */
+};
+
+/* Node of a trie representing a set of reversed keywords. */
+struct trie
+{
+  unsigned int accepting;	/* Word index of accepted word, or zero. */
+  struct tree *links;		/* Tree of edges leaving this node. */
+  struct trie *parent;		/* Parent of this node. */
+  struct trie *next;		/* List of all trie nodes in level order. */
+  struct trie *fail;		/* Aho-Corasick failure function. */
+  int depth;			/* Depth of this node from the root. */
+  int shift;			/* Shift function for search failures. */
+  int maxshift;			/* Max shift of self and descendents. */
+};
+
+/* Structure returned opaquely to the caller, containing everything. */
+struct kwset
+{
+  struct obstack obstack;	/* Obstack for node allocation. */
+  int words;			/* Number of words in the trie. */
+  struct trie *trie;		/* The trie itself. */
+  int mind;			/* Minimum depth of an accepting node. */
+  int maxd;			/* Maximum depth of any node. */
+  unsigned char delta[NCHAR];	/* Delta table for rapid search. */
+  struct trie *next[NCHAR];	/* Table of children of the root. */
+  char *target;			/* Target string if there's only one. */
+  int mind2;			/* Used in Boyer-Moore search for one string. */
+  char const *trans;		/* Character translation table. */
+};
+
+/* Allocate and initialize a keyword set object, returning an opaque
+   pointer to it.  Return NULL if memory is not available. */
+kwset_t
+kwsalloc (char const *trans)
+{
+  struct kwset *kwset;
+
+  kwset = (struct kwset *) xmalloc(sizeof (struct kwset));
+
+  obstack_init(&kwset->obstack);
+  kwset->words = 0;
+  kwset->trie
+    = (struct trie *) obstack_alloc(&kwset->obstack, sizeof (struct trie));
+  if (!kwset->trie)
+    {
+      kwsfree((kwset_t) kwset);
+      return NULL;
+    }
+  kwset->trie->accepting = 0;
+  kwset->trie->links = NULL;
+  kwset->trie->parent = NULL;
+  kwset->trie->next = NULL;
+  kwset->trie->fail = NULL;
+  kwset->trie->depth = 0;
+  kwset->trie->shift = 0;
+  kwset->mind = INT_MAX;
+  kwset->maxd = -1;
+  kwset->target = NULL;
+  kwset->trans = trans;
+
+  return (kwset_t) kwset;
+}
+
+/* This upper bound is valid for CHAR_BIT >= 4 and
+   exact for CHAR_BIT in { 4..11, 13, 15, 17, 19 }. */
+#define DEPTH_SIZE (CHAR_BIT + CHAR_BIT/2)
+
+/* Add the given string to the contents of the keyword set.  Return NULL
+   for success, an error message otherwise. */
+const char *
+kwsincr (kwset_t kws, char const *text, size_t len)
+{
+  struct kwset *kwset;
+  register struct trie *trie;
+  register unsigned char label;
+  register struct tree *link;
+  register int depth;
+  struct tree *links[DEPTH_SIZE];
+  enum { L, R } dirs[DEPTH_SIZE];
+  struct tree *t, *r, *l, *rl, *lr;
+
+  kwset = (struct kwset *) kws;
+  trie = kwset->trie;
+  text += len;
+
+  /* Descend the trie (built of reversed keywords) character-by-character,
+     installing new nodes when necessary. */
+  while (len--)
+    {
+      label = kwset->trans ? kwset->trans[U(*--text)] : *--text;
+
+      /* Descend the tree of outgoing links for this trie node,
+	 looking for the current character and keeping track
+	 of the path followed. */
+      link = trie->links;
+      links[0] = (struct tree *) &trie->links;
+      dirs[0] = L;
+      depth = 1;
+
+      while (link && label != link->label)
+	{
+	  links[depth] = link;
+	  if (label < link->label)
+	    dirs[depth++] = L, link = link->llink;
+	  else
+	    dirs[depth++] = R, link = link->rlink;
+	}
+
+      /* The current character doesn't have an outgoing link at
+	 this trie node, so build a new trie node and install
+	 a link in the current trie node's tree. */
+      if (!link)
+	{
+	  link = (struct tree *) obstack_alloc(&kwset->obstack,
+					       sizeof (struct tree));
+	  if (!link)
+	    return "memory exhausted";
+	  link->llink = NULL;
+	  link->rlink = NULL;
+	  link->trie = (struct trie *) obstack_alloc(&kwset->obstack,
+						     sizeof (struct trie));
+	  if (!link->trie)
+	    {
+	      obstack_free(&kwset->obstack, link);
+	      return "memory exhausted";
+	    }
+	  link->trie->accepting = 0;
+	  link->trie->links = NULL;
+	  link->trie->parent = trie;
+	  link->trie->next = NULL;
+	  link->trie->fail = NULL;
+	  link->trie->depth = trie->depth + 1;
+	  link->trie->shift = 0;
+	  link->label = label;
+	  link->balance = 0;
+
+	  /* Install the new tree node in its parent. */
+	  if (dirs[--depth] == L)
+	    links[depth]->llink = link;
+	  else
+	    links[depth]->rlink = link;
+
+	  /* Back up the tree fixing the balance flags. */
+	  while (depth && !links[depth]->balance)
+	    {
+	      if (dirs[depth] == L)
+		--links[depth]->balance;
+	      else
+		++links[depth]->balance;
+	      --depth;
+	    }
+
+	  /* Rebalance the tree by pointer rotations if necessary. */
+	  if (depth && ((dirs[depth] == L && --links[depth]->balance)
+			|| (dirs[depth] == R && ++links[depth]->balance)))
+	    {
+	      switch (links[depth]->balance)
+		{
+		case (char) -2:
+		  switch (dirs[depth + 1])
+		    {
+		    case L:
+		      r = links[depth], t = r->llink, rl = t->rlink;
+		      t->rlink = r, r->llink = rl;
+		      t->balance = r->balance = 0;
+		      break;
+		    case R:
+		      r = links[depth], l = r->llink, t = l->rlink;
+		      rl = t->rlink, lr = t->llink;
+		      t->llink = l, l->rlink = lr, t->rlink = r, r->llink = rl;
+		      l->balance = t->balance != 1 ? 0 : -1;
+		      r->balance = t->balance != (char) -1 ? 0 : 1;
+		      t->balance = 0;
+		      break;
+		    default:
+		      abort ();
+		    }
+		  break;
+		case 2:
+		  switch (dirs[depth + 1])
+		    {
+		    case R:
+		      l = links[depth], t = l->rlink, lr = t->llink;
+		      t->llink = l, l->rlink = lr;
+		      t->balance = l->balance = 0;
+		      break;
+		    case L:
+		      l = links[depth], r = l->rlink, t = r->llink;
+		      lr = t->llink, rl = t->rlink;
+		      t->llink = l, l->rlink = lr, t->rlink = r, r->llink = rl;
+		      l->balance = t->balance != 1 ? 0 : -1;
+		      r->balance = t->balance != (char) -1 ? 0 : 1;
+		      t->balance = 0;
+		      break;
+		    default:
+		      abort ();
+		    }
+		  break;
+		default:
+		  abort ();
+		}
+
+	      if (dirs[depth - 1] == L)
+		links[depth - 1]->llink = t;
+	      else
+		links[depth - 1]->rlink = t;
+	    }
+	}
+
+      trie = link->trie;
+    }
+
+  /* Mark the node we finally reached as accepting, encoding the
+     index number of this word in the keyword set so far. */
+  if (!trie->accepting)
+    trie->accepting = 1 + 2 * kwset->words;
+  ++kwset->words;
+
+  /* Keep track of the longest and shortest string of the keyword set. */
+  if (trie->depth < kwset->mind)
+    kwset->mind = trie->depth;
+  if (trie->depth > kwset->maxd)
+    kwset->maxd = trie->depth;
+
+  return NULL;
+}
+
+/* Enqueue the trie nodes referenced from the given tree in the
+   given queue. */
+static void
+enqueue (struct tree *tree, struct trie **last)
+{
+  if (!tree)
+    return;
+  enqueue(tree->llink, last);
+  enqueue(tree->rlink, last);
+  (*last) = (*last)->next = tree->trie;
+}
+
+/* Compute the Aho-Corasick failure function for the trie nodes referenced
+   from the given tree, given the failure function for their parent as
+   well as a last resort failure node. */
+static void
+treefails (register struct tree const *tree, struct trie const *fail,
+	   struct trie *recourse)
+{
+  register struct tree *link;
+
+  if (!tree)
+    return;
+
+  treefails(tree->llink, fail, recourse);
+  treefails(tree->rlink, fail, recourse);
+
+  /* Find, in the chain of fails going back to the root, the first
+     node that has a descendent on the current label. */
+  while (fail)
+    {
+      link = fail->links;
+      while (link && tree->label != link->label)
+	if (tree->label < link->label)
+	  link = link->llink;
+	else
+	  link = link->rlink;
+      if (link)
+	{
+	  tree->trie->fail = link->trie;
+	  return;
+	}
+      fail = fail->fail;
+    }
+
+  tree->trie->fail = recourse;
+}
+
+/* Set delta entries for the links of the given tree such that
+   the preexisting delta value is larger than the current depth. */
+static void
+treedelta (register struct tree const *tree,
+	   register unsigned int depth,
+	   unsigned char delta[])
+{
+  if (!tree)
+    return;
+  treedelta(tree->llink, depth, delta);
+  treedelta(tree->rlink, depth, delta);
+  if (depth < delta[tree->label])
+    delta[tree->label] = depth;
+}
+
+/* Return true if A has every label in B. */
+static int
+hasevery (register struct tree const *a, register struct tree const *b)
+{
+  if (!b)
+    return 1;
+  if (!hasevery(a, b->llink))
+    return 0;
+  if (!hasevery(a, b->rlink))
+    return 0;
+  while (a && b->label != a->label)
+    if (b->label < a->label)
+      a = a->llink;
+    else
+      a = a->rlink;
+  return !!a;
+}
+
+/* Compute a vector, indexed by character code, of the trie nodes
+   referenced from the given tree. */
+static void
+treenext (struct tree const *tree, struct trie *next[])
+{
+  if (!tree)
+    return;
+  treenext(tree->llink, next);
+  treenext(tree->rlink, next);
+  next[tree->label] = tree->trie;
+}
+
+/* Compute the shift for each trie node, as well as the delta
+   table and next cache for the given keyword set. */
+const char *
+kwsprep (kwset_t kws)
+{
+  register struct kwset *kwset;
+  register int i;
+  register struct trie *curr;
+  register char const *trans;
+  unsigned char delta[NCHAR];
+
+  kwset = (struct kwset *) kws;
+
+  /* Initial values for the delta table; will be changed later.  The
+     delta entry for a given character is the smallest depth of any
+     node at which an outgoing edge is labeled by that character. */
+  memset(delta, kwset->mind < UCHAR_MAX ? kwset->mind : UCHAR_MAX, NCHAR);
+
+  /* Check if we can use the simple boyer-moore algorithm, instead
+     of the hairy commentz-walter algorithm. */
+  if (kwset->words == 1 && kwset->trans == NULL)
+    {
+      char c;
+
+      /* Looking for just one string.  Extract it from the trie. */
+      kwset->target = obstack_alloc(&kwset->obstack, kwset->mind);
+      if (!kwset->target)
+	return "memory exhausted";
+      for (i = kwset->mind - 1, curr = kwset->trie; i >= 0; --i)
+	{
+	  kwset->target[i] = curr->links->label;
+	  curr = curr->links->trie;
+	}
+      /* Build the Boyer Moore delta.  Boy that's easy compared to CW. */
+      for (i = 0; i < kwset->mind; ++i)
+	delta[U(kwset->target[i])] = kwset->mind - (i + 1);
+      /* Find the minimal delta2 shift that we might make after
+	 a backwards match has failed. */
+      c = kwset->target[kwset->mind - 1];
+      for (i = kwset->mind - 2; i >= 0; --i)
+	if (kwset->target[i] == c)
+	  break;
+      kwset->mind2 = kwset->mind - (i + 1);
+    }
+  else
+    {
+      register struct trie *fail;
+      struct trie *last, *next[NCHAR];
+
+      /* Traverse the nodes of the trie in level order, simultaneously
+	 computing the delta table, failure function, and shift function. */
+      for (curr = last = kwset->trie; curr; curr = curr->next)
+	{
+	  /* Enqueue the immediate descendents in the level order queue. */
+	  enqueue(curr->links, &last);
+
+	  curr->shift = kwset->mind;
+	  curr->maxshift = kwset->mind;
+
+	  /* Update the delta table for the descendents of this node. */
+	  treedelta(curr->links, curr->depth, delta);
+
+	  /* Compute the failure function for the decendents of this node. */
+	  treefails(curr->links, curr->fail, kwset->trie);
+
+	  /* Update the shifts at each node in the current node's chain
+	     of fails back to the root. */
+	  for (fail = curr->fail; fail; fail = fail->fail)
+	    {
+	      /* If the current node has some outgoing edge that the fail
+		 doesn't, then the shift at the fail should be no larger
+		 than the difference of their depths. */
+	      if (!hasevery(fail->links, curr->links))
+		if (curr->depth - fail->depth < fail->shift)
+		  fail->shift = curr->depth - fail->depth;
+
+	      /* If the current node is accepting then the shift at the
+		 fail and its descendents should be no larger than the
+		 difference of their depths. */
+	      if (curr->accepting && fail->maxshift > curr->depth - fail->depth)
+		fail->maxshift = curr->depth - fail->depth;
+	    }
+	}
+
+      /* Traverse the trie in level order again, fixing up all nodes whose
+	 shift exceeds their inherited maxshift. */
+      for (curr = kwset->trie->next; curr; curr = curr->next)
+	{
+	  if (curr->maxshift > curr->parent->maxshift)
+	    curr->maxshift = curr->parent->maxshift;
+	  if (curr->shift > curr->maxshift)
+	    curr->shift = curr->maxshift;
+	}
+
+      /* Create a vector, indexed by character code, of the outgoing links
+	 from the root node. */
+      for (i = 0; i < NCHAR; ++i)
+	next[i] = NULL;
+      treenext(kwset->trie->links, next);
+
+      if ((trans = kwset->trans) != NULL)
+	for (i = 0; i < NCHAR; ++i)
+	  kwset->next[i] = next[U(trans[i])];
+      else
+	memcpy(kwset->next, next, NCHAR * sizeof(struct trie *));
+    }
+
+  /* Fix things up for any translation table. */
+  if ((trans = kwset->trans) != NULL)
+    for (i = 0; i < NCHAR; ++i)
+      kwset->delta[i] = delta[U(trans[i])];
+  else
+    memcpy(kwset->delta, delta, NCHAR);
+
+  return NULL;
+}
+
+/* Fast boyer-moore search. */
+static size_t
+bmexec (kwset_t kws, char const *text, size_t size)
+{
+  struct kwset const *kwset;
+  register unsigned char const *d1;
+  register char const *ep, *sp, *tp;
+  register int d, gc, i, len, md2;
+
+  kwset = (struct kwset const *) kws;
+  len = kwset->mind;
+
+  if (len == 0)
+    return 0;
+  if (len > size)
+    return -1;
+  if (len == 1)
+    {
+      tp = memchr (text, kwset->target[0], size);
+      return tp ? tp - text : -1;
+    }
+
+  d1 = kwset->delta;
+  sp = kwset->target + len;
+  gc = U(sp[-2]);
+  md2 = kwset->mind2;
+  tp = text + len;
+
+  /* Significance of 12: 1 (initial offset) + 10 (skip loop) + 1 (md2). */
+  if (size > 12 * len)
+    /* 11 is not a bug, the initial offset happens only once. */
+    for (ep = text + size - 11 * len;;)
+      {
+	while (tp <= ep)
+	  {
+	    d = d1[U(tp[-1])], tp += d;
+	    d = d1[U(tp[-1])], tp += d;
+	    if (d == 0)
+	      goto found;
+	    d = d1[U(tp[-1])], tp += d;
+	    d = d1[U(tp[-1])], tp += d;
+	    d = d1[U(tp[-1])], tp += d;
+	    if (d == 0)
+	      goto found;
+	    d = d1[U(tp[-1])], tp += d;
+	    d = d1[U(tp[-1])], tp += d;
+	    d = d1[U(tp[-1])], tp += d;
+	    if (d == 0)
+	      goto found;
+	    d = d1[U(tp[-1])], tp += d;
+	    d = d1[U(tp[-1])], tp += d;
+	  }
+	break;
+      found:
+	if (U(tp[-2]) == gc)
+	  {
+	    for (i = 3; i <= len && U(tp[-i]) == U(sp[-i]); ++i)
+	      ;
+	    if (i > len)
+	      return tp - len - text;
+	  }
+	tp += md2;
+      }
+
+  /* Now we have only a few characters left to search.  We
+     carefully avoid ever producing an out-of-bounds pointer. */
+  ep = text + size;
+  d = d1[U(tp[-1])];
+  while (d <= ep - tp)
+    {
+      d = d1[U((tp += d)[-1])];
+      if (d != 0)
+	continue;
+      if (U(tp[-2]) == gc)
+	{
+	  for (i = 3; i <= len && U(tp[-i]) == U(sp[-i]); ++i)
+	    ;
+	  if (i > len)
+	    return tp - len - text;
+	}
+      d = md2;
+    }
+
+  return -1;
+}
+
+/* Hairy multiple string search. */
+static size_t
+cwexec (kwset_t kws, char const *text, size_t len, struct kwsmatch *kwsmatch)
+{
+  struct kwset const *kwset;
+  struct trie * const *next;
+  struct trie const *trie;
+  struct trie const *accept;
+  char const *beg, *lim, *mch, *lmch;
+  register unsigned char c;
+  register unsigned char const *delta;
+  register int d;
+  register char const *end, *qlim;
+  register struct tree const *tree;
+  register char const *trans;
+
+  accept = NULL;
+
+  /* Initialize register copies and look for easy ways out. */
+  kwset = (struct kwset *) kws;
+  if (len < kwset->mind)
+    return -1;
+  next = kwset->next;
+  delta = kwset->delta;
+  trans = kwset->trans;
+  lim = text + len;
+  end = text;
+  if ((d = kwset->mind) != 0)
+    mch = NULL;
+  else
+    {
+      mch = text, accept = kwset->trie;
+      goto match;
+    }
+
+  if (len >= 4 * kwset->mind)
+    qlim = lim - 4 * kwset->mind;
+  else
+    qlim = NULL;
+
+  while (lim - end >= d)
+    {
+      if (qlim && end <= qlim)
+	{
+	  end += d - 1;
+	  while ((d = delta[c = *end]) && end < qlim)
+	    {
+	      end += d;
+	      end += delta[U(*end)];
+	      end += delta[U(*end)];
+	    }
+	  ++end;
+	}
+      else
+	d = delta[c = (end += d)[-1]];
+      if (d)
+	continue;
+      beg = end - 1;
+      trie = next[c];
+      if (trie->accepting)
+	{
+	  mch = beg;
+	  accept = trie;
+	}
+      d = trie->shift;
+      while (beg > text)
+	{
+	  c = trans ? trans[U(*--beg)] : *--beg;
+	  tree = trie->links;
+	  while (tree && c != tree->label)
+	    if (c < tree->label)
+	      tree = tree->llink;
+	    else
+	      tree = tree->rlink;
+	  if (tree)
+	    {
+	      trie = tree->trie;
+	      if (trie->accepting)
+		{
+		  mch = beg;
+		  accept = trie;
+		}
+	    }
+	  else
+	    break;
+	  d = trie->shift;
+	}
+      if (mch)
+	goto match;
+    }
+  return -1;
+
+ match:
+  /* Given a known match, find the longest possible match anchored
+     at or before its starting point.  This is nearly a verbatim
+     copy of the preceding main search loops. */
+  if (lim - mch > kwset->maxd)
+    lim = mch + kwset->maxd;
+  lmch = NULL;
+  d = 1;
+  while (lim - end >= d)
+    {
+      if ((d = delta[c = (end += d)[-1]]) != 0)
+	continue;
+      beg = end - 1;
+      if (!(trie = next[c]))
+	{
+	  d = 1;
+	  continue;
+	}
+      if (trie->accepting && beg <= mch)
+	{
+	  lmch = beg;
+	  accept = trie;
+	}
+      d = trie->shift;
+      while (beg > text)
+	{
+	  c = trans ? trans[U(*--beg)] : *--beg;
+	  tree = trie->links;
+	  while (tree && c != tree->label)
+	    if (c < tree->label)
+	      tree = tree->llink;
+	    else
+	      tree = tree->rlink;
+	  if (tree)
+	    {
+	      trie = tree->trie;
+	      if (trie->accepting && beg <= mch)
+		{
+		  lmch = beg;
+		  accept = trie;
+		}
+	    }
+	  else
+	    break;
+	  d = trie->shift;
+	}
+      if (lmch)
+	{
+	  mch = lmch;
+	  goto match;
+	}
+      if (!d)
+	d = 1;
+    }
+
+  if (kwsmatch)
+    {
+      kwsmatch->index = accept->accepting / 2;
+      kwsmatch->offset[0] = mch - text;
+      kwsmatch->size[0] = accept->depth;
+    }
+  return mch - text;
+}
+
+/* Search through the given text for a match of any member of the
+   given keyword set.  Return a pointer to the first character of
+   the matching substring, or NULL if no match is found.  If FOUNDLEN
+   is non-NULL store in the referenced location the length of the
+   matching substring.  Similarly, if FOUNDIDX is non-NULL, store
+   in the referenced location the index number of the particular
+   keyword matched. */
+size_t
+kwsexec (kwset_t kws, char const *text, size_t size,
+	 struct kwsmatch *kwsmatch)
+{
+  struct kwset const *kwset = (struct kwset *) kws;
+  if (kwset->words == 1 && kwset->trans == NULL)
+    {
+      size_t ret = bmexec (kws, text, size);
+      if (kwsmatch != NULL && ret != (size_t) -1)
+	{
+	  kwsmatch->index = 0;
+	  kwsmatch->offset[0] = ret;
+	  kwsmatch->size[0] = kwset->mind;
+	}
+      return ret;
+    }
+  else
+    return cwexec(kws, text, size, kwsmatch);
+}
+
+/* Free the components of the given keyword set. */
+void
+kwsfree (kwset_t kws)
+{
+  struct kwset *kwset;
+
+  kwset = (struct kwset *) kws;
+  obstack_free(&kwset->obstack, NULL);
+  free(kws);
+}
diff --git a/kwset.h b/kwset.h
new file mode 100644
index 0000000..a21b2ea
--- /dev/null
+++ b/kwset.h
@@ -0,0 +1,63 @@
+/* This file has been copied from commit e7ac713d^ in the GNU grep git
+ * repository. A few small changes have been made to adapt the code to
+ * Git.
+ */
+
+/* kwset.h - header declaring the keyword set library.
+   Copyright (C) 1989, 1998, 2005 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
+
+/* Written August 1989 by Mike Haertel.
+   The author may be reached (Email) at the address mike@ai.mit.edu,
+   or (US mail) as Mike Haertel c/o Free Software Foundation. */
+
+struct kwsmatch
+{
+  int index;			/* Index number of matching keyword. */
+  size_t offset[1];		/* Offset of each submatch. */
+  size_t size[1];		/* Length of each submatch. */
+};
+
+struct kwset_t;
+typedef struct kwset_t* kwset_t;
+
+/* Return an opaque pointer to a newly allocated keyword set, or NULL
+   if enough memory cannot be obtained.  The argument if non-NULL
+   specifies a table of character translations to be applied to all
+   pattern and search text. */
+extern kwset_t kwsalloc(char const *);
+
+/* Incrementally extend the keyword set to include the given string.
+   Return NULL for success, or an error message.  Remember an index
+   number for each keyword included in the set. */
+extern const char *kwsincr(kwset_t, char const *, size_t);
+
+/* When the keyword set has been completely built, prepare it for
+   use.  Return NULL for success, or an error message. */
+extern const char *kwsprep(kwset_t);
+
+/* Search through the given buffer for a member of the keyword set.
+   Return a pointer to the leftmost longest match found, or NULL if
+   no match is found.  If foundlen is non-NULL, store the length of
+   the matching substring in the integer it points to.  Similarly,
+   if foundindex is non-NULL, store the index of the particular
+   keyword found therein. */
+extern size_t kwsexec(kwset_t, char const *, size_t, struct kwsmatch *);
+
+/* Deallocate the given keyword set and all its associated storage. */
+extern void kwsfree(kwset_t);
+
diff --git a/levenshtein.h b/levenshtein.h
index 0173abe..4105bf3 100644
--- a/levenshtein.h
+++ b/levenshtein.h
@@ -2,7 +2,7 @@
 #define LEVENSHTEIN_H
 
 int levenshtein(const char *string1, const char *string2,
-	int swap_penalty, int substition_penalty,
+	int swap_penalty, int substitution_penalty,
 	int insertion_penalty, int deletion_penalty);
 
 #endif
diff --git a/list-objects.c b/list-objects.c
index 8953548..3dd4a96 100644
--- a/list-objects.c
+++ b/list-objects.c
@@ -12,7 +12,8 @@
 			 struct blob *blob,
 			 show_object_fn show,
 			 struct name_path *path,
-			 const char *name)
+			 const char *name,
+			 void *cb_data)
 {
 	struct object *obj = &blob->object;
 
@@ -23,7 +24,7 @@
 	if (obj->flags & (UNINTERESTING | SEEN))
 		return;
 	obj->flags |= SEEN;
-	show(obj, path, name);
+	show(obj, path, name, cb_data);
 }
 
 /*
@@ -52,7 +53,8 @@
 			    const unsigned char *sha1,
 			    show_object_fn show,
 			    struct name_path *path,
-			    const char *name)
+			    const char *name,
+			    void *cb_data)
 {
 	/* Nothing to do */
 }
@@ -61,12 +63,17 @@
 			 struct tree *tree,
 			 show_object_fn show,
 			 struct name_path *path,
-			 const char *name)
+			 struct strbuf *base,
+			 const char *name,
+			 void *cb_data)
 {
 	struct object *obj = &tree->object;
 	struct tree_desc desc;
 	struct name_entry entry;
 	struct name_path me;
+	enum interesting match = revs->diffopt.pathspec.nr == 0 ?
+		all_entries_interesting: entry_not_interesting;
+	int baselen = base->len;
 
 	if (!revs->tree_objects)
 		return;
@@ -77,26 +84,45 @@
 	if (parse_tree(tree) < 0)
 		die("bad tree object %s", sha1_to_hex(obj->sha1));
 	obj->flags |= SEEN;
-	show(obj, path, name);
+	show(obj, path, name, cb_data);
 	me.up = path;
 	me.elem = name;
 	me.elem_len = strlen(name);
 
+	if (!match) {
+		strbuf_addstr(base, name);
+		if (base->len)
+			strbuf_addch(base, '/');
+	}
+
 	init_tree_desc(&desc, tree->buffer, tree->size);
 
 	while (tree_entry(&desc, &entry)) {
+		if (match != all_entries_interesting) {
+			match = tree_entry_interesting(&entry, base, 0,
+						       &revs->diffopt.pathspec);
+			if (match == all_entries_not_interesting)
+				break;
+			if (match == entry_not_interesting)
+				continue;
+		}
+
 		if (S_ISDIR(entry.mode))
 			process_tree(revs,
 				     lookup_tree(entry.sha1),
-				     show, &me, entry.path);
+				     show, &me, base, entry.path,
+				     cb_data);
 		else if (S_ISGITLINK(entry.mode))
 			process_gitlink(revs, entry.sha1,
-					show, &me, entry.path);
+					show, &me, entry.path,
+					cb_data);
 		else
 			process_blob(revs,
 				     lookup_blob(entry.sha1),
-				     show, &me, entry.path);
+				     show, &me, entry.path,
+				     cb_data);
 	}
+	strbuf_setlen(base, baselen);
 	free(tree->buffer);
 	tree->buffer = NULL;
 }
@@ -146,9 +172,16 @@
 {
 	int i;
 	struct commit *commit;
+	struct strbuf base;
 
+	strbuf_init(&base, PATH_MAX);
 	while ((commit = get_revision(revs)) != NULL) {
-		add_pending_tree(revs, commit->tree);
+		/*
+		 * an uninteresting boundary commit may not have its tree
+		 * parsed yet, but we are not going to show them anyway
+		 */
+		if (commit->tree)
+			add_pending_tree(revs, commit->tree);
 		show_commit(commit, data);
 	}
 	for (i = 0; i < revs->pending.nr; i++) {
@@ -159,17 +192,17 @@
 			continue;
 		if (obj->type == OBJ_TAG) {
 			obj->flags |= SEEN;
-			show_object(obj, NULL, name);
+			show_object(obj, NULL, name, data);
 			continue;
 		}
 		if (obj->type == OBJ_TREE) {
 			process_tree(revs, (struct tree *)obj, show_object,
-				     NULL, name);
+				     NULL, &base, name, data);
 			continue;
 		}
 		if (obj->type == OBJ_BLOB) {
 			process_blob(revs, (struct blob *)obj, show_object,
-				     NULL, name);
+				     NULL, name, data);
 			continue;
 		}
 		die("unknown pending object %s (%s)",
@@ -181,4 +214,5 @@
 		revs->pending.alloc = 0;
 		revs->pending.objects = NULL;
 	}
+	strbuf_release(&base);
 }
diff --git a/list-objects.h b/list-objects.h
index d65dbf0..3db7bb6 100644
--- a/list-objects.h
+++ b/list-objects.h
@@ -2,11 +2,10 @@
 #define LIST_OBJECTS_H
 
 typedef void (*show_commit_fn)(struct commit *, void *);
-typedef void (*show_object_fn)(struct object *, const struct name_path *, const char *);
-typedef void (*show_edge_fn)(struct commit *);
-
+typedef void (*show_object_fn)(struct object *, const struct name_path *, const char *, void *);
 void traverse_commit_list(struct rev_info *, show_commit_fn, show_object_fn, void *);
 
+typedef void (*show_edge_fn)(struct commit *);
 void mark_edges_uninteresting(struct commit_list *, struct rev_info *, show_edge_fn);
 
 #endif
diff --git a/ll-merge.c b/ll-merge.c
index 0571564..f3f7692 100644
--- a/ll-merge.c
+++ b/ll-merge.c
@@ -15,10 +15,11 @@
 typedef int (*ll_merge_fn)(const struct ll_merge_driver *,
 			   mmbuffer_t *result,
 			   const char *path,
-			   mmfile_t *orig,
+			   mmfile_t *orig, const char *orig_name,
 			   mmfile_t *src1, const char *name1,
 			   mmfile_t *src2, const char *name2,
-			   int virtual_ancestor);
+			   const struct ll_merge_options *opts,
+			   int marker_size);
 
 struct ll_merge_driver {
 	const char *name;
@@ -35,17 +36,21 @@
 static int ll_binary_merge(const struct ll_merge_driver *drv_unused,
 			   mmbuffer_t *result,
 			   const char *path_unused,
-			   mmfile_t *orig,
+			   mmfile_t *orig, const char *orig_name,
 			   mmfile_t *src1, const char *name1,
 			   mmfile_t *src2, const char *name2,
-			   int virtual_ancestor)
+			   const struct ll_merge_options *opts,
+			   int marker_size)
 {
+	mmfile_t *stolen;
+	assert(opts);
+
 	/*
 	 * The tentative merge result is "ours" for the final round,
 	 * or common ancestor for an internal merge.  Still return
 	 * "conflicted merge" status.
 	 */
-	mmfile_t *stolen = virtual_ancestor ? orig : src1;
+	stolen = opts->virtual_ancestor ? orig : src1;
 
 	result->ptr = stolen->ptr;
 	result->size = stolen->size;
@@ -56,84 +61,59 @@
 static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
 			mmbuffer_t *result,
 			const char *path,
-			mmfile_t *orig,
+			mmfile_t *orig, const char *orig_name,
 			mmfile_t *src1, const char *name1,
 			mmfile_t *src2, const char *name2,
-			int virtual_ancestor)
+			const struct ll_merge_options *opts,
+			int marker_size)
 {
-	xpparam_t xpp;
-	int style = 0;
+	xmparam_t xmp;
+	assert(opts);
 
 	if (buffer_is_binary(orig->ptr, orig->size) ||
 	    buffer_is_binary(src1->ptr, src1->size) ||
 	    buffer_is_binary(src2->ptr, src2->size)) {
-		warning("Cannot merge binary files: %s (%s vs. %s)\n",
+		warning("Cannot merge binary files: %s (%s vs. %s)",
 			path, name1, name2);
 		return ll_binary_merge(drv_unused, result,
 				       path,
-				       orig, src1, name1,
+				       orig, orig_name,
+				       src1, name1,
 				       src2, name2,
-				       virtual_ancestor);
+				       opts, marker_size);
 	}
 
-	memset(&xpp, 0, sizeof(xpp));
+	memset(&xmp, 0, sizeof(xmp));
+	xmp.level = XDL_MERGE_ZEALOUS;
+	xmp.favor = opts->variant;
+	xmp.xpp.flags = opts->xdl_opts;
 	if (git_xmerge_style >= 0)
-		style = git_xmerge_style;
-	return xdl_merge(orig,
-			 src1, name1,
-			 src2, name2,
-			 &xpp, XDL_MERGE_ZEALOUS | style,
-			 result);
+		xmp.style = git_xmerge_style;
+	if (marker_size > 0)
+		xmp.marker_size = marker_size;
+	xmp.ancestor = orig_name;
+	xmp.file1 = name1;
+	xmp.file2 = name2;
+	return xdl_merge(orig, src1, src2, &xmp, result);
 }
 
 static int ll_union_merge(const struct ll_merge_driver *drv_unused,
 			  mmbuffer_t *result,
 			  const char *path_unused,
-			  mmfile_t *orig,
+			  mmfile_t *orig, const char *orig_name,
 			  mmfile_t *src1, const char *name1,
 			  mmfile_t *src2, const char *name2,
-			  int virtual_ancestor)
+			  const struct ll_merge_options *opts,
+			  int marker_size)
 {
-	char *src, *dst;
-	long size;
-	const int marker_size = 7;
-	int status, saved_style;
-
-	/* We have to force the RCS "merge" style */
-	saved_style = git_xmerge_style;
-	git_xmerge_style = 0;
-	status = ll_xdl_merge(drv_unused, result, path_unused,
-			      orig, src1, NULL, src2, NULL,
-			      virtual_ancestor);
-	git_xmerge_style = saved_style;
-	if (status <= 0)
-		return status;
-	size = result->size;
-	src = dst = result->ptr;
-	while (size) {
-		char ch;
-		if ((marker_size < size) &&
-		    (*src == '<' || *src == '=' || *src == '>')) {
-			int i;
-			ch = *src;
-			for (i = 0; i < marker_size; i++)
-				if (src[i] != ch)
-					goto not_a_marker;
-			if (src[marker_size] != '\n')
-				goto not_a_marker;
-			src += marker_size + 1;
-			size -= marker_size + 1;
-			continue;
-		}
-	not_a_marker:
-		do {
-			ch = *src++;
-			*dst++ = ch;
-			size--;
-		} while (ch != '\n' && size);
-	}
-	result->size = dst - result->ptr;
-	return 0;
+	/* Use union favor */
+	struct ll_merge_options o;
+	assert(opts);
+	o = *opts;
+	o.variant = XDL_MERGE_FAVOR_UNION;
+	return ll_xdl_merge(drv_unused, result, path_unused,
+			    orig, NULL, src1, NULL, src2, NULL,
+			    &o, marker_size);
 }
 
 #define LL_BINARY_MERGE 0
@@ -162,22 +142,25 @@
 static int ll_ext_merge(const struct ll_merge_driver *fn,
 			mmbuffer_t *result,
 			const char *path,
-			mmfile_t *orig,
+			mmfile_t *orig, const char *orig_name,
 			mmfile_t *src1, const char *name1,
 			mmfile_t *src2, const char *name2,
-			int virtual_ancestor)
+			const struct ll_merge_options *opts,
+			int marker_size)
 {
-	char temp[3][50];
+	char temp[4][50];
 	struct strbuf cmd = STRBUF_INIT;
-	struct strbuf_expand_dict_entry dict[] = {
-		{ "O", temp[0] },
-		{ "A", temp[1] },
-		{ "B", temp[2] },
-		{ NULL }
-	};
-	const char *args[] = { "sh", "-c", NULL, NULL };
+	struct strbuf_expand_dict_entry dict[5];
+	const char *args[] = { NULL, NULL };
 	int status, fd, i;
 	struct stat st;
+	assert(opts);
+
+	dict[0].placeholder = "O"; dict[0].value = temp[0];
+	dict[1].placeholder = "A"; dict[1].value = temp[1];
+	dict[2].placeholder = "B"; dict[2].value = temp[2];
+	dict[3].placeholder = "L"; dict[3].value = temp[3];
+	dict[4].placeholder = NULL; dict[4].value = NULL;
 
 	if (fn->cmdline == NULL)
 		die("custom merge driver %s lacks command line.", fn->name);
@@ -187,15 +170,12 @@
 	create_temp(orig, temp[0]);
 	create_temp(src1, temp[1]);
 	create_temp(src2, temp[2]);
+	sprintf(temp[3], "%d", marker_size);
 
 	strbuf_expand(&cmd, fn->cmdline, strbuf_expand_dict_cb, &dict);
 
-	args[2] = cmd.buf;
-	status = run_command_v_opt(args, 0);
-	if (status < -ERR_RUN_COMMAND_FORK)
-		; /* failure in run-command */
-	else
-		status = -status;
+	args[0] = cmd.buf;
+	status = run_command_v_opt(args, RUN_USING_SHELL);
 	fd = open(temp[1], O_RDONLY);
 	if (fd < 0)
 		goto bad;
@@ -283,6 +263,7 @@
 		 *    %O - temporary file name for the merge base.
 		 *    %A - temporary file name for our version.
 		 *    %B - temporary file name for the other branches' version.
+		 *    %L - conflict marker length
 		 *
 		 * The external merge driver should write the results in the
 		 * file named by %A, and signal that it has done with zero exit
@@ -343,35 +324,73 @@
 	return &ll_merge_drv[LL_TEXT_MERGE];
 }
 
-static const char *git_path_check_merge(const char *path)
+static int git_path_check_merge(const char *path, struct git_attr_check check[2])
 {
-	static struct git_attr_check attr_merge_check;
+	if (!check[0].attr) {
+		check[0].attr = git_attr("merge");
+		check[1].attr = git_attr("conflict-marker-size");
+	}
+	return git_check_attr(path, 2, check);
+}
 
-	if (!attr_merge_check.attr)
-		attr_merge_check.attr = git_attr("merge", 5);
-
-	if (git_checkattr(path, 1, &attr_merge_check))
-		return NULL;
-	return attr_merge_check.value;
+static void normalize_file(mmfile_t *mm, const char *path)
+{
+	struct strbuf strbuf = STRBUF_INIT;
+	if (renormalize_buffer(path, mm->ptr, mm->size, &strbuf)) {
+		free(mm->ptr);
+		mm->size = strbuf.len;
+		mm->ptr = strbuf_detach(&strbuf, NULL);
+	}
 }
 
 int ll_merge(mmbuffer_t *result_buf,
 	     const char *path,
-	     mmfile_t *ancestor,
+	     mmfile_t *ancestor, const char *ancestor_label,
 	     mmfile_t *ours, const char *our_label,
 	     mmfile_t *theirs, const char *their_label,
-	     int virtual_ancestor)
+	     const struct ll_merge_options *opts)
 {
-	const char *ll_driver_name;
+	static struct git_attr_check check[2];
+	static const struct ll_merge_options default_opts;
+	const char *ll_driver_name = NULL;
+	int marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
 	const struct ll_merge_driver *driver;
 
-	ll_driver_name = git_path_check_merge(path);
-	driver = find_ll_merge_driver(ll_driver_name);
+	if (!opts)
+		opts = &default_opts;
 
-	if (virtual_ancestor && driver->recursive)
+	if (opts->renormalize) {
+		normalize_file(ancestor, path);
+		normalize_file(ours, path);
+		normalize_file(theirs, path);
+	}
+	if (!git_path_check_merge(path, check)) {
+		ll_driver_name = check[0].value;
+		if (check[1].value) {
+			marker_size = atoi(check[1].value);
+			if (marker_size <= 0)
+				marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
+		}
+	}
+	driver = find_ll_merge_driver(ll_driver_name);
+	if (opts->virtual_ancestor && driver->recursive)
 		driver = find_ll_merge_driver(driver->recursive);
-	return driver->fn(driver, result_buf, path,
-			  ancestor,
-			  ours, our_label,
-			  theirs, their_label, virtual_ancestor);
+	return driver->fn(driver, result_buf, path, ancestor, ancestor_label,
+			  ours, our_label, theirs, their_label,
+			  opts, marker_size);
+}
+
+int ll_merge_marker_size(const char *path)
+{
+	static struct git_attr_check check;
+	int marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
+
+	if (!check.attr)
+		check.attr = git_attr("conflict-marker-size");
+	if (!git_check_attr(path, 1, &check) && check.value) {
+		marker_size = atoi(check.value);
+		if (marker_size <= 0)
+			marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
+	}
+	return marker_size;
 }
diff --git a/ll-merge.h b/ll-merge.h
index 5388422..244a31f 100644
--- a/ll-merge.h
+++ b/ll-merge.h
@@ -5,11 +5,20 @@
 #ifndef LL_MERGE_H
 #define LL_MERGE_H
 
+struct ll_merge_options {
+	unsigned virtual_ancestor : 1;
+	unsigned variant : 2;	/* favor ours, favor theirs, or union merge */
+	unsigned renormalize : 1;
+	long xdl_opts;
+};
+
 int ll_merge(mmbuffer_t *result_buf,
 	     const char *path,
-	     mmfile_t *ancestor,
+	     mmfile_t *ancestor, const char *ancestor_label,
 	     mmfile_t *ours, const char *our_label,
 	     mmfile_t *theirs, const char *their_label,
-	     int virtual_ancestor);
+	     const struct ll_merge_options *opts);
+
+int ll_merge_marker_size(const char *path);
 
 #endif
diff --git a/lockfile.c b/lockfile.c
index eb931ed..c6fb77b 100644
--- a/lockfile.c
+++ b/lockfile.c
@@ -155,18 +155,33 @@
 	return lk->fd;
 }
 
-
-NORETURN void unable_to_lock_index_die(const char *path, int err)
+static char *unable_to_lock_message(const char *path, int err)
 {
+	struct strbuf buf = STRBUF_INIT;
+
 	if (err == EEXIST) {
-		die("Unable to create '%s.lock': %s.\n\n"
+		strbuf_addf(&buf, "Unable to create '%s.lock': %s.\n\n"
 		    "If no other git process is currently running, this probably means a\n"
 		    "git process crashed in this repository earlier. Make sure no other git\n"
 		    "process is running and remove the file manually to continue.",
-		    path, strerror(err));
-	} else {
-		die("Unable to create '%s.lock': %s", path, strerror(err));
-	}
+			    absolute_path(path), strerror(err));
+	} else
+		strbuf_addf(&buf, "Unable to create '%s.lock': %s",
+			    absolute_path(path), strerror(err));
+	return strbuf_detach(&buf, NULL);
+}
+
+int unable_to_lock_error(const char *path, int err)
+{
+	char *msg = unable_to_lock_message(path, err);
+	error("%s", msg);
+	free(msg);
+	return -1;
+}
+
+NORETURN void unable_to_lock_index_die(const char *path, int err)
+{
+	die("%s", unable_to_lock_message(path, err));
 }
 
 int hold_lock_file_for_update(struct lock_file *lk, const char *path, int flags)
diff --git a/log-tree.c b/log-tree.c
index 6f73c17..c894930 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -7,41 +7,153 @@
 #include "reflog-walk.h"
 #include "refs.h"
 #include "string-list.h"
+#include "color.h"
+#include "gpg-interface.h"
 
 struct decoration name_decoration = { "object names" };
 
-static void add_name_decoration(const char *prefix, const char *name, struct object *obj)
+enum decoration_type {
+	DECORATION_NONE = 0,
+	DECORATION_REF_LOCAL,
+	DECORATION_REF_REMOTE,
+	DECORATION_REF_TAG,
+	DECORATION_REF_STASH,
+	DECORATION_REF_HEAD,
+	DECORATION_GRAFTED,
+};
+
+static char decoration_colors[][COLOR_MAXLEN] = {
+	GIT_COLOR_RESET,
+	GIT_COLOR_BOLD_GREEN,	/* REF_LOCAL */
+	GIT_COLOR_BOLD_RED,	/* REF_REMOTE */
+	GIT_COLOR_BOLD_YELLOW,	/* REF_TAG */
+	GIT_COLOR_BOLD_MAGENTA,	/* REF_STASH */
+	GIT_COLOR_BOLD_CYAN,	/* REF_HEAD */
+	GIT_COLOR_BOLD_BLUE,	/* GRAFTED */
+};
+
+static const char *decorate_get_color(int decorate_use_color, enum decoration_type ix)
 {
-	int plen = strlen(prefix);
+	if (want_color(decorate_use_color))
+		return decoration_colors[ix];
+	return "";
+}
+
+static int parse_decorate_color_slot(const char *slot)
+{
+	/*
+	 * We're comparing with 'ignore-case' on
+	 * (because config.c sets them all tolower),
+	 * but let's match the letters in the literal
+	 * string values here with how they are
+	 * documented in Documentation/config.txt, for
+	 * consistency.
+	 *
+	 * We love being consistent, don't we?
+	 */
+	if (!strcasecmp(slot, "branch"))
+		return DECORATION_REF_LOCAL;
+	if (!strcasecmp(slot, "remoteBranch"))
+		return DECORATION_REF_REMOTE;
+	if (!strcasecmp(slot, "tag"))
+		return DECORATION_REF_TAG;
+	if (!strcasecmp(slot, "stash"))
+		return DECORATION_REF_STASH;
+	if (!strcasecmp(slot, "HEAD"))
+		return DECORATION_REF_HEAD;
+	return -1;
+}
+
+int parse_decorate_color_config(const char *var, const int ofs, const char *value)
+{
+	int slot = parse_decorate_color_slot(var + ofs);
+	if (slot < 0)
+		return 0;
+	if (!value)
+		return config_error_nonbool(var);
+	color_parse(value, var, decoration_colors[slot]);
+	return 0;
+}
+
+/*
+ * log-tree.c uses DIFF_OPT_TST for determining whether to use color
+ * for showing the commit sha1, use the same check for --decorate
+ */
+#define decorate_get_color_opt(o, ix) \
+	decorate_get_color((o)->use_color, ix)
+
+static void add_name_decoration(enum decoration_type type, const char *name, struct object *obj)
+{
 	int nlen = strlen(name);
-	struct name_decoration *res = xmalloc(sizeof(struct name_decoration) + plen + nlen);
-	memcpy(res->name, prefix, plen);
-	memcpy(res->name + plen, name, nlen + 1);
+	struct name_decoration *res = xmalloc(sizeof(struct name_decoration) + nlen);
+	memcpy(res->name, name, nlen + 1);
+	res->type = type;
 	res->next = add_decoration(&name_decoration, obj, res);
 }
 
 static int add_ref_decoration(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
 {
-	struct object *obj = parse_object(sha1);
+	struct object *obj;
+	enum decoration_type type = DECORATION_NONE;
+
+	if (!prefixcmp(refname, "refs/replace/")) {
+		unsigned char original_sha1[20];
+		if (!read_replace_refs)
+			return 0;
+		if (get_sha1_hex(refname + 13, original_sha1)) {
+			warning("invalid replace ref %s", refname);
+			return 0;
+		}
+		obj = parse_object(original_sha1);
+		if (obj)
+			add_name_decoration(DECORATION_GRAFTED, "replaced", obj);
+		return 0;
+	}
+
+	obj = parse_object(sha1);
 	if (!obj)
 		return 0;
-	refname = prettify_refname(refname);
-	add_name_decoration("", refname, obj);
+
+	if (!prefixcmp(refname, "refs/heads/"))
+		type = DECORATION_REF_LOCAL;
+	else if (!prefixcmp(refname, "refs/remotes/"))
+		type = DECORATION_REF_REMOTE;
+	else if (!prefixcmp(refname, "refs/tags/"))
+		type = DECORATION_REF_TAG;
+	else if (!strcmp(refname, "refs/stash"))
+		type = DECORATION_REF_STASH;
+	else if (!strcmp(refname, "HEAD"))
+		type = DECORATION_REF_HEAD;
+
+	if (!cb_data || *(int *)cb_data == DECORATE_SHORT_REFS)
+		refname = prettify_refname(refname);
+	add_name_decoration(type, refname, obj);
 	while (obj->type == OBJ_TAG) {
 		obj = ((struct tag *)obj)->tagged;
 		if (!obj)
 			break;
-		add_name_decoration("tag: ", refname, obj);
+		add_name_decoration(DECORATION_REF_TAG, refname, obj);
 	}
 	return 0;
 }
 
-void load_ref_decorations(void)
+static int add_graft_decoration(const struct commit_graft *graft, void *cb_data)
+{
+	struct commit *commit = lookup_commit(graft->sha1);
+	if (!commit)
+		return 0;
+	add_name_decoration(DECORATION_GRAFTED, "grafted", &commit->object);
+	return 0;
+}
+
+void load_ref_decorations(int flags)
 {
 	static int loaded;
 	if (!loaded) {
 		loaded = 1;
-		for_each_ref(add_ref_decoration, NULL);
+		for_each_ref(add_ref_decoration, &flags);
+		head_ref(add_ref_decoration, &flags);
+		for_each_commit_graft(add_graft_decoration, NULL);
 	}
 }
 
@@ -54,10 +166,22 @@
 	}
 }
 
+static void show_children(struct rev_info *opt, struct commit *commit, int abbrev)
+{
+	struct commit_list *p = lookup_decoration(&opt->children, &commit->object);
+	for ( ; p; p = p->next) {
+		printf(" %s", find_unique_abbrev(p->item->object.sha1, abbrev));
+	}
+}
+
 void show_decorations(struct rev_info *opt, struct commit *commit)
 {
 	const char *prefix;
 	struct name_decoration *decoration;
+	const char *color_commit =
+		diff_get_color_opt(&opt->diffopt, DIFF_COMMIT);
+	const char *color_reset =
+		decorate_get_color_opt(&opt->diffopt, DECORATION_NONE);
 
 	if (opt->show_source && commit->util)
 		printf("\t%s", (char *) commit->util);
@@ -68,7 +192,14 @@
 		return;
 	prefix = " (";
 	while (decoration) {
-		printf("%s%s", prefix, decoration->name);
+		printf("%s", prefix);
+		fputs(decorate_get_color_opt(&opt->diffopt, decoration->type),
+		      stdout);
+		if (decoration->type == DECORATION_REF_TAG)
+			fputs("tag: ", stdout);
+		printf("%s", decoration->name);
+		fputs(color_reset, stdout);
+		fputs(color_commit, stdout);
 		prefix = ", ";
 		decoration = decoration->next;
 	}
@@ -168,29 +299,22 @@
 	return result;
 }
 
-static int has_non_ascii(const char *s)
-{
-	int ch;
-	if (!s)
-		return 0;
-	while ((ch = *s++) != '\0') {
-		if (non_ascii(ch))
-			return 1;
-	}
-	return 0;
-}
-
-void get_patch_filename(struct commit *commit, int nr, const char *suffix,
-			struct strbuf *buf)
+void get_patch_filename(struct commit *commit, const char *subject, int nr,
+			const char *suffix, struct strbuf *buf)
 {
 	int suffix_len = strlen(suffix) + 1;
 	int start_len = buf->len;
 
-	strbuf_addf(buf, commit ? "%04d-" : "%d", nr);
-	if (commit) {
+	strbuf_addf(buf, commit || subject ? "%04d-" : "%d", nr);
+	if (commit || subject) {
 		int max_len = start_len + FORMAT_PATCH_NAME_MAX - suffix_len;
+		struct pretty_print_context ctx = {0};
 
-		format_commit_message(commit, "%f", buf, DATE_NORMAL);
+		if (subject)
+			strbuf_addstr(buf, subject);
+		else if (commit)
+			format_commit_message(commit, "%f", buf, &ctx);
+
 		if (max_len < buf->len)
 			strbuf_setlen(buf, max_len);
 		strbuf_addstr(buf, suffix);
@@ -210,8 +334,9 @@
 	if (opt->total > 0) {
 		static char buffer[64];
 		snprintf(buffer, sizeof(buffer),
-			 "Subject: [%s %0*d/%d] ",
+			 "Subject: [%s%s%0*d/%d] ",
 			 opt->subject_prefix,
+			 *opt->subject_prefix ? " " : "",
 			 digits_in_number(opt->total),
 			 opt->nr, opt->total);
 		subject = buffer;
@@ -262,8 +387,8 @@
 			 mime_boundary_leader, opt->mime_boundary);
 		extra_headers = subject_buffer;
 
-		get_patch_filename(opt->numbered_files ? NULL : commit, opt->nr,
-				    opt->patch_suffix, &filename);
+		get_patch_filename(opt->numbered_files ? NULL : commit, NULL,
+				   opt->nr, opt->patch_suffix, &filename);
 		snprintf(buffer, sizeof(buffer) - 1,
 			 "\n--%s%s\n"
 			 "Content-Type: text/x-patch;"
@@ -282,35 +407,150 @@
 	*extra_headers_p = extra_headers;
 }
 
+static void show_sig_lines(struct rev_info *opt, int status, const char *bol)
+{
+	const char *color, *reset, *eol;
+
+	color = diff_get_color_opt(&opt->diffopt,
+				   status ? DIFF_WHITESPACE : DIFF_FRAGINFO);
+	reset = diff_get_color_opt(&opt->diffopt, DIFF_RESET);
+	while (*bol) {
+		eol = strchrnul(bol, '\n');
+		printf("%s%.*s%s%s", color, (int)(eol - bol), bol, reset,
+		       *eol ? "\n" : "");
+		bol = (*eol) ? (eol + 1) : eol;
+	}
+}
+
+static void show_signature(struct rev_info *opt, struct commit *commit)
+{
+	struct strbuf payload = STRBUF_INIT;
+	struct strbuf signature = STRBUF_INIT;
+	struct strbuf gpg_output = STRBUF_INIT;
+	int status;
+
+	if (parse_signed_commit(commit->object.sha1, &payload, &signature) <= 0)
+		goto out;
+
+	status = verify_signed_buffer(payload.buf, payload.len,
+				      signature.buf, signature.len,
+				      &gpg_output);
+	if (status && !gpg_output.len)
+		strbuf_addstr(&gpg_output, "No signature\n");
+
+	show_sig_lines(opt, status, gpg_output.buf);
+
+ out:
+	strbuf_release(&gpg_output);
+	strbuf_release(&payload);
+	strbuf_release(&signature);
+}
+
+static int which_parent(const unsigned char *sha1, const struct commit *commit)
+{
+	int nth;
+	const struct commit_list *parent;
+
+	for (nth = 0, parent = commit->parents; parent; parent = parent->next) {
+		if (!hashcmp(parent->item->object.sha1, sha1))
+			return nth;
+		nth++;
+	}
+	return -1;
+}
+
+static int is_common_merge(const struct commit *commit)
+{
+	return (commit->parents
+		&& commit->parents->next
+		&& !commit->parents->next->next);
+}
+
+static void show_one_mergetag(struct rev_info *opt,
+			      struct commit_extra_header *extra,
+			      struct commit *commit)
+{
+	unsigned char sha1[20];
+	struct tag *tag;
+	struct strbuf verify_message;
+	int status, nth;
+	size_t payload_size, gpg_message_offset;
+
+	hash_sha1_file(extra->value, extra->len, typename(OBJ_TAG), sha1);
+	tag = lookup_tag(sha1);
+	if (!tag)
+		return; /* error message already given */
+
+	strbuf_init(&verify_message, 256);
+	if (parse_tag_buffer(tag, extra->value, extra->len))
+		strbuf_addstr(&verify_message, "malformed mergetag\n");
+	else if (is_common_merge(commit) &&
+		 !hashcmp(tag->tagged->sha1,
+			  commit->parents->next->item->object.sha1))
+		strbuf_addf(&verify_message,
+			    "merged tag '%s'\n", tag->tag);
+	else if ((nth = which_parent(tag->tagged->sha1, commit)) < 0)
+		strbuf_addf(&verify_message, "tag %s names a non-parent %s\n",
+				    tag->tag, tag->tagged->sha1);
+	else
+		strbuf_addf(&verify_message,
+			    "parent #%d, tagged '%s'\n", nth + 1, tag->tag);
+	gpg_message_offset = verify_message.len;
+
+	payload_size = parse_signature(extra->value, extra->len);
+	if ((extra->len <= payload_size) ||
+	    (verify_signed_buffer(extra->value, payload_size,
+				  extra->value + payload_size,
+				  extra->len - payload_size,
+				  &verify_message) &&
+	     verify_message.len <= gpg_message_offset)) {
+		strbuf_addstr(&verify_message, "No signature\n");
+		status = -1;
+	}
+	else if (strstr(verify_message.buf + gpg_message_offset,
+			": Good signature from "))
+		status = 0;
+	else
+		status = -1;
+
+	show_sig_lines(opt, status, verify_message.buf);
+	strbuf_release(&verify_message);
+}
+
+static void show_mergetag(struct rev_info *opt, struct commit *commit)
+{
+	struct commit_extra_header *extra, *to_free;
+
+	to_free = read_commit_extra_headers(commit, NULL);
+	for (extra = to_free; extra; extra = extra->next) {
+		if (strcmp(extra->key, "mergetag"))
+			continue; /* not a merge tag */
+		show_one_mergetag(opt, extra, commit);
+	}
+	free_commit_extra_headers(to_free);
+}
+
 void show_log(struct rev_info *opt)
 {
 	struct strbuf msgbuf = STRBUF_INIT;
 	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;
-	const char *subject = NULL, *extra_headers = opt->extra_headers;
-	int need_8bit_cte = 0;
+	const char *extra_headers = opt->extra_headers;
+	struct pretty_print_context ctx = {0};
 
 	opt->loginfo = NULL;
+	ctx.show_notes = opt->show_notes;
 	if (!opt->verbose_header) {
 		graph_show_commit(opt->graph);
 
-		if (!opt->graph) {
-			if (commit->object.flags & BOUNDARY)
-				putchar('-');
-			else if (commit->object.flags & UNINTERESTING)
-				putchar('^');
-			else if (opt->left_right) {
-				if (commit->object.flags & SYMMETRIC_LEFT)
-					putchar('<');
-				else
-					putchar('>');
-			}
-		}
+		if (!opt->graph)
+			put_revision_mark(opt, commit);
 		fputs(find_unique_abbrev(commit->object.sha1, abbrev_commit), stdout);
 		if (opt->print_parents)
 			show_parents(commit, abbrev_commit);
+		if (opt->children.name)
+			show_children(opt, commit, abbrev_commit);
 		show_decorations(opt, commit);
 		if (opt->graph && !graph_is_commit_finished(opt->graph)) {
 			putchar('\n');
@@ -357,29 +597,21 @@
 	 */
 
 	if (opt->commit_format == CMIT_FMT_EMAIL) {
-		log_write_email_headers(opt, commit, &subject, &extra_headers,
-					&need_8bit_cte);
+		log_write_email_headers(opt, commit, &ctx.subject, &extra_headers,
+					&ctx.need_8bit_cte);
 	} else if (opt->commit_format != CMIT_FMT_USERFORMAT) {
 		fputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), stdout);
 		if (opt->commit_format != CMIT_FMT_ONELINE)
 			fputs("commit ", stdout);
 
-		if (!opt->graph) {
-			if (commit->object.flags & BOUNDARY)
-				putchar('-');
-			else if (commit->object.flags & UNINTERESTING)
-				putchar('^');
-			else if (opt->left_right) {
-				if (commit->object.flags & SYMMETRIC_LEFT)
-					putchar('<');
-				else
-					putchar('>');
-			}
-		}
+		if (!opt->graph)
+			put_revision_mark(opt, commit);
 		fputs(find_unique_abbrev(commit->object.sha1, abbrev_commit),
 		      stdout);
 		if (opt->print_parents)
 			show_parents(commit, abbrev_commit);
+		if (opt->children.name)
+			show_children(opt, commit, abbrev_commit);
 		if (parent)
 			printf(" (from %s)",
 			       find_unique_abbrev(parent->object.sha1,
@@ -400,24 +632,35 @@
 			 * graph info here.
 			 */
 			show_reflog_message(opt->reflog_info,
-				    opt->commit_format == CMIT_FMT_ONELINE,
-				    opt->date_mode);
+					    opt->commit_format == CMIT_FMT_ONELINE,
+					    opt->date_mode,
+					    opt->date_mode_explicit);
 			if (opt->commit_format == CMIT_FMT_ONELINE)
 				return;
 		}
 	}
 
+	if (opt->show_signature) {
+		show_signature(opt, commit);
+		show_mergetag(opt, commit);
+	}
+
 	if (!commit->buffer)
 		return;
 
 	/*
 	 * And then the pretty-printed message itself
 	 */
-	if (need_8bit_cte >= 0)
-		need_8bit_cte = has_non_ascii(opt->add_signoff);
-	pretty_print_commit(opt->commit_format, commit, &msgbuf,
-			    abbrev, subject, extra_headers, opt->date_mode,
-			    need_8bit_cte);
+	if (ctx.need_8bit_cte >= 0)
+		ctx.need_8bit_cte = has_non_ascii(opt->add_signoff);
+	ctx.date_mode = opt->date_mode;
+	ctx.date_mode_explicit = opt->date_mode_explicit;
+	ctx.abbrev = opt->diffopt.abbrev;
+	ctx.after_subject = extra_headers;
+	ctx.preserve_subject = opt->preserve_subject;
+	ctx.reflog_info = opt->reflog_info;
+	ctx.fmt = opt->commit_format;
+	pretty_print_commit(&ctx, commit, &msgbuf);
 
 	if (opt->add_signoff)
 		append_signoff(&msgbuf, opt->add_signoff);
@@ -442,7 +685,7 @@
 	if (opt->use_terminator) {
 		if (!opt->missing_newline)
 			graph_show_padding(opt->graph);
-		putchar('\n');
+		putchar(opt->diffopt.line_termination);
 	}
 
 	strbuf_release(&msgbuf);
@@ -471,8 +714,15 @@
 		    opt->verbose_header &&
 		    opt->commit_format != CMIT_FMT_ONELINE) {
 			int pch = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_PATCH;
-			if ((pch & opt->diffopt.output_format) == pch)
+			if (opt->diffopt.output_prefix) {
+				struct strbuf *msg = NULL;
+				msg = opt->diffopt.output_prefix(&opt->diffopt,
+					opt->diffopt.output_prefix_data);
+				fwrite(msg->buf, msg->len, 1, stdout);
+			}
+			if ((pch & opt->diffopt.output_format) == pch) {
 				printf("---");
+			}
 			putchar('\n');
 		}
 	}
@@ -482,9 +732,7 @@
 
 static int do_diff_combined(struct rev_info *opt, struct commit *commit)
 {
-	unsigned const char *sha1 = commit->object.sha1;
-
-	diff_tree_combined_merge(sha1, opt->dense_combined_merges, opt);
+	diff_tree_combined_merge(commit, opt->dense_combined_merges, opt);
 	return !opt->loginfo;
 }
 
@@ -518,6 +766,16 @@
 			return 0;
 		else if (opt->combine_merges)
 			return do_diff_combined(opt, commit);
+		else if (opt->first_parent_only) {
+			/*
+			 * Generate merge log entry only for the first
+			 * parent, showing summary diff of the others
+			 * we merged _in_.
+			 */
+			diff_tree_sha1(parents->item->object.sha1, sha1, "", &opt->diffopt);
+			log_tree_diff_flush(opt);
+			return !opt->loginfo;
+		}
 
 		/* If we show individual diffs, show the parent info */
 		log->parent = parents->item;
diff --git a/log-tree.h b/log-tree.h
index 20b5caf..f5ac238 100644
--- a/log-tree.h
+++ b/log-tree.h
@@ -7,6 +7,7 @@
 	struct commit *commit, *parent;
 };
 
+int parse_decorate_color_config(const char *var, const int ofs, const char *value);
 void init_log_tree_opt(struct rev_info *);
 int log_tree_diff_flush(struct rev_info *);
 int log_tree_commit(struct rev_info *, struct commit *);
@@ -17,10 +18,10 @@
 			     const char **subject_p,
 			     const char **extra_headers_p,
 			     int *need_8bit_cte_p);
-void load_ref_decorations(void);
+void load_ref_decorations(int flags);
 
 #define FORMAT_PATCH_NAME_MAX 64
-void get_patch_filename(struct commit *commit, int nr, const char *suffix,
-			struct strbuf *buf);
+void get_patch_filename(struct commit *commit, const char *subject, int nr,
+			const char *suffix, struct strbuf *buf);
 
 #endif
diff --git a/mailmap.c b/mailmap.c
index f167c00..47aa419 100644
--- a/mailmap.c
+++ b/mailmap.c
@@ -69,9 +69,8 @@
 		index = -1 - index;
 	} else {
 		/* create mailmap entry */
-		struct string_list_item *item = string_list_insert_at_index(index, old_email, map);
-		item->util = xmalloc(sizeof(struct mailmap_entry));
-		memset(item->util, 0, sizeof(struct mailmap_entry));
+		struct string_list_item *item = string_list_insert_at_index(map, index, old_email);
+		item->util = xcalloc(1, sizeof(struct mailmap_entry));
 		((struct mailmap_entry *)item->util)->namemap.strdup_strings = 1;
 	}
 	me = (struct mailmap_entry *)map->items[index].util;
@@ -79,20 +78,22 @@
 	if (old_name == NULL) {
 		debug_mm("mailmap: adding (simple) entry for %s at index %d\n", old_email, index);
 		/* Replace current name and new email for simple entry */
-		free(me->name);
-		free(me->email);
-		if (new_name)
+		if (new_name) {
+			free(me->name);
 			me->name = xstrdup(new_name);
-		if (new_email)
+		}
+		if (new_email) {
+			free(me->email);
 			me->email = xstrdup(new_email);
+		}
 	} else {
-		struct mailmap_info *mi = xmalloc(sizeof(struct mailmap_info));
+		struct mailmap_info *mi = xcalloc(1, sizeof(struct mailmap_info));
 		debug_mm("mailmap: adding (complex) entry for %s at index %d\n", old_email, index);
 		if (new_name)
 			mi->name = xstrdup(new_name);
 		if (new_email)
 			mi->email = xstrdup(new_email);
-		string_list_insert(old_name, &me->namemap)->util = mi;
+		string_list_insert(&me->namemap, old_name)->util = mi;
 	}
 
 	debug_mm("mailmap:  '%s' <%s> -> '%s' <%s>\n",
@@ -189,38 +190,38 @@
 int map_user(struct string_list *map,
 	     char *email, int maxlen_email, char *name, int maxlen_name)
 {
-	char *p;
+	char *end_of_email;
 	struct string_list_item *item;
 	struct mailmap_entry *me;
 	char buf[1024], *mailbuf;
 	int i;
 
 	/* figure out space requirement for email */
-	p = strchr(email, '>');
-	if (!p) {
+	end_of_email = strchr(email, '>');
+	if (!end_of_email) {
 		/* email passed in might not be wrapped in <>, but end with a \0 */
-		p = memchr(email, '\0', maxlen_email);
-		if (!p)
+		end_of_email = memchr(email, '\0', maxlen_email);
+		if (!end_of_email)
 			return 0;
 	}
-	if (p - email + 1 < sizeof(buf))
+	if (end_of_email - email + 1 < sizeof(buf))
 		mailbuf = buf;
 	else
-		mailbuf = xmalloc(p - email + 1);
+		mailbuf = xmalloc(end_of_email - email + 1);
 
 	/* downcase the email address */
-	for (i = 0; i < p - email; i++)
+	for (i = 0; i < end_of_email - email; i++)
 		mailbuf[i] = tolower(email[i]);
 	mailbuf[i] = 0;
 
 	debug_mm("map_user: map '%s' <%s>\n", name, mailbuf);
-	item = string_list_lookup(mailbuf, map);
+	item = string_list_lookup(map, mailbuf);
 	if (item != NULL) {
 		me = (struct mailmap_entry *)item->util;
 		if (me->namemap.nr) {
 			/* The item has multiple items, so we'll look up on name too */
 			/* If the name is not found, we choose the simple entry      */
-			struct string_list_item *subitem = string_list_lookup(name, &me->namemap);
+			struct string_list_item *subitem = string_list_lookup(&me->namemap, name);
 			if (subitem)
 				item = subitem;
 		}
@@ -235,6 +236,8 @@
 		}
 		if (maxlen_email && mi->email)
 			strlcpy(email, mi->email, maxlen_email);
+		else
+			*end_of_email = '\0';
 		if (maxlen_name && mi->name)
 			strlcpy(name, mi->name, maxlen_name);
 		debug_mm("map_user:  to '%s' <%s>\n", name, mi->email ? mi->email : "");
@@ -243,8 +246,3 @@
 	debug_mm("map_user:  --\n");
 	return 0;
 }
-
-int map_email(struct string_list *map, const char *email, char *name, int maxlen)
-{
-	return map_user(map, (char *)email, 0, name, maxlen);
-}
diff --git a/mailmap.h b/mailmap.h
index 4b2ca3a..d5c3664 100644
--- a/mailmap.h
+++ b/mailmap.h
@@ -4,7 +4,6 @@
 int read_mailmap(struct string_list *map, char **repo_abbrev);
 void clear_mailmap(struct string_list *map);
 
-int map_email(struct string_list *mailmap, const char *email, char *name, int maxlen);
 int map_user(struct string_list *mailmap,
 	     char *email, int maxlen_email, char *name, int maxlen_name);
 
diff --git a/match-trees.c b/match-trees.c
index 0fd6df7..26f7ed1 100644
--- a/match-trees.c
+++ b/match-trees.c
@@ -185,7 +185,7 @@
  * tree object by replacing it with another tree "hash2".
  */
 static int splice_tree(const unsigned char *hash1,
-		       char *prefix,
+		       const char *prefix,
 		       const unsigned char *hash2,
 		       unsigned char *result)
 {
@@ -264,6 +264,13 @@
 	char *del_prefix;
 	int add_score, del_score;
 
+	/*
+	 * NEEDSWORK: this limits the recursion depth to hardcoded
+	 * value '2' to avoid excessive overhead.
+	 */
+	if (!depth_limit)
+		depth_limit = 2;
+
 	add_score = del_score = score_trees(hash1, hash2);
 	add_prefix = xcalloc(1, 1);
 	del_prefix = xcalloc(1, 1);
@@ -301,3 +308,63 @@
 
 	splice_tree(hash1, add_prefix, hash2, shifted);
 }
+
+/*
+ * The user says the trees will be shifted by this much.
+ * Unfortunately we cannot fundamentally tell which one to
+ * be prefixed, as recursive merge can work in either direction.
+ */
+void shift_tree_by(const unsigned char *hash1,
+		   const unsigned char *hash2,
+		   unsigned char *shifted,
+		   const char *shift_prefix)
+{
+	unsigned char sub1[20], sub2[20];
+	unsigned mode1, mode2;
+	unsigned candidate = 0;
+
+	/* Can hash2 be a tree at shift_prefix in tree hash1? */
+	if (!get_tree_entry(hash1, shift_prefix, sub1, &mode1) &&
+	    S_ISDIR(mode1))
+		candidate |= 1;
+
+	/* Can hash1 be a tree at shift_prefix in tree hash2? */
+	if (!get_tree_entry(hash2, shift_prefix, sub2, &mode2) &&
+	    S_ISDIR(mode2))
+		candidate |= 2;
+
+	if (candidate == 3) {
+		/* Both are plausible -- we need to evaluate the score */
+		int best_score = score_trees(hash1, hash2);
+		int score;
+
+		candidate = 0;
+		score = score_trees(sub1, hash2);
+		if (score > best_score) {
+			candidate = 1;
+			best_score = score;
+		}
+		score = score_trees(sub2, hash1);
+		if (score > best_score)
+			candidate = 2;
+	}
+
+	if (!candidate) {
+		/* Neither is plausible -- do not shift */
+		hashcpy(shifted, hash2);
+		return;
+	}
+
+	if (candidate == 1)
+		/*
+		 * shift tree2 down by adding shift_prefix above it
+		 * to match tree1.
+		 */
+		splice_tree(hash1, shift_prefix, hash2, shifted);
+	else
+		/*
+		 * shift tree2 up by removing shift_prefix from it
+		 * to match tree1.
+		 */
+		hashcpy(shifted, sub2);
+}
diff --git a/merge-file.c b/merge-file.c
index 3120a95..7845528 100644
--- a/merge-file.c
+++ b/merge-file.c
@@ -1,7 +1,9 @@
 #include "cache.h"
 #include "run-command.h"
 #include "xdiff-interface.h"
+#include "ll-merge.h"
 #include "blob.h"
+#include "merge-file.h"
 
 static int fill_mmfile_blob(mmfile_t *f, struct blob *obj)
 {
@@ -24,16 +26,19 @@
 	free(f->ptr);
 }
 
-static void *three_way_filemerge(mmfile_t *base, mmfile_t *our, mmfile_t *their, unsigned long *size)
+static void *three_way_filemerge(const char *path, mmfile_t *base, mmfile_t *our, mmfile_t *their, unsigned long *size)
 {
-	mmbuffer_t res;
-	xpparam_t xpp;
 	int merge_status;
+	mmbuffer_t res;
 
-	memset(&xpp, 0, sizeof(xpp));
-	merge_status = xdl_merge(base, our, ".our", their, ".their",
-		&xpp, XDL_MERGE_ZEALOUS, &res);
-
+	/*
+	 * This function is only used by cmd_merge_tree, which
+	 * does not respect the merge.conflictstyle option.
+	 * There is no need to worry about a label for the
+	 * common ancestor.
+	 */
+	merge_status = ll_merge(&res, path, base, NULL,
+				our, ".our", their, ".their", NULL);
 	if (merge_status < 0)
 		return NULL;
 
@@ -62,7 +67,7 @@
 	xdemitcb_t ecb;
 
 	memset(&xpp, 0, sizeof(xpp));
-	xpp.flags = XDF_NEED_MINIMAL;
+	xpp.flags = 0;
 	memset(&xecfg, 0, sizeof(xecfg));
 	xecfg.ctxlen = 3;
 	xecfg.flags = XDL_EMIT_COMMON;
@@ -75,7 +80,7 @@
 	return xdi_diff(f1, f2, &xpp, &xecfg, &ecb);
 }
 
-void *merge_file(struct blob *base, struct blob *our, struct blob *their, unsigned long *size)
+void *merge_file(const char *path, struct blob *base, struct blob *our, struct blob *their, unsigned long *size)
 {
 	void *res = NULL;
 	mmfile_t f1, f2, common;
@@ -108,7 +113,7 @@
 		if (generate_common_file(&common, &f1, &f2) < 0)
 			goto out_free_f2_f1;
 	}
-	res = three_way_filemerge(&common, &f1, &f2, size);
+	res = three_way_filemerge(path, &common, &f1, &f2, size);
 	free_mmfile(&common);
 out_free_f2_f1:
 	free_mmfile(&f2);
diff --git a/merge-file.h b/merge-file.h
new file mode 100644
index 0000000..9b3b83a
--- /dev/null
+++ b/merge-file.h
@@ -0,0 +1,7 @@
+#ifndef MERGE_FILE_H
+#define MERGE_FILE_H
+
+extern void *merge_file(const char *path, struct blob *base, struct blob *our,
+			struct blob *their, unsigned long *size);
+
+#endif
diff --git a/merge-index.c b/merge-index.c
deleted file mode 100644
index 19ddd03..0000000
--- a/merge-index.c
+++ /dev/null
@@ -1,114 +0,0 @@
-#include "cache.h"
-#include "run-command.h"
-#include "exec_cmd.h"
-
-static const char *pgm;
-static int one_shot, quiet;
-static int err;
-
-static int merge_entry(int pos, const char *path)
-{
-	int found;
-	const char *arguments[] = { pgm, "", "", "", path, "", "", "", NULL };
-	char hexbuf[4][60];
-	char ownbuf[4][60];
-
-	if (pos >= active_nr)
-		die("git merge-index: %s not in the cache", path);
-	found = 0;
-	do {
-		struct cache_entry *ce = active_cache[pos];
-		int stage = ce_stage(ce);
-
-		if (strcmp(ce->name, path))
-			break;
-		found++;
-		strcpy(hexbuf[stage], sha1_to_hex(ce->sha1));
-		sprintf(ownbuf[stage], "%o", ce->ce_mode);
-		arguments[stage] = hexbuf[stage];
-		arguments[stage + 4] = ownbuf[stage];
-	} while (++pos < active_nr);
-	if (!found)
-		die("git merge-index: %s not in the cache", path);
-
-	if (run_command_v_opt(arguments, 0)) {
-		if (one_shot)
-			err++;
-		else {
-			if (!quiet)
-				die("merge program failed");
-			exit(1);
-		}
-	}
-	return found;
-}
-
-static void merge_file(const char *path)
-{
-	int pos = cache_name_pos(path, strlen(path));
-
-	/*
-	 * If it already exists in the cache as stage0, it's
-	 * already merged and there is nothing to do.
-	 */
-	if (pos < 0)
-		merge_entry(-pos-1, path);
-}
-
-static void merge_all(void)
-{
-	int i;
-	for (i = 0; i < active_nr; i++) {
-		struct cache_entry *ce = active_cache[i];
-		if (!ce_stage(ce))
-			continue;
-		i += merge_entry(i, ce->name)-1;
-	}
-}
-
-int main(int argc, char **argv)
-{
-	int i, force_file = 0;
-
-	/* Without this we cannot rely on waitpid() to tell
-	 * what happened to our children.
-	 */
-	signal(SIGCHLD, SIG_DFL);
-
-	if (argc < 3)
-		usage("git merge-index [-o] [-q] <merge-program> (-a | [--] <filename>*)");
-
-	git_extract_argv0_path(argv[0]);
-
-	setup_git_directory();
-	read_cache();
-
-	i = 1;
-	if (!strcmp(argv[i], "-o")) {
-		one_shot = 1;
-		i++;
-	}
-	if (!strcmp(argv[i], "-q")) {
-		quiet = 1;
-		i++;
-	}
-	pgm = argv[i++];
-	for (; i < argc; i++) {
-		char *arg = argv[i];
-		if (!force_file && *arg == '-') {
-			if (!strcmp(arg, "--")) {
-				force_file = 1;
-				continue;
-			}
-			if (!strcmp(arg, "-a")) {
-				merge_all();
-				continue;
-			}
-			die("git merge-index: unknown option %s", arg);
-		}
-		merge_file(arg);
-	}
-	if (err && !quiet)
-		die("merge program failed");
-	return err;
-}
diff --git a/merge-recursive.c b/merge-recursive.c
index d415c41..680937c 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -3,6 +3,7 @@
  * Fredrik Kuivinen.
  * The thieves were Alex Riesen and Johannes Schindelin, in June/July 2006
  */
+#include "advice.h"
 #include "cache.h"
 #include "cache-tree.h"
 #include "commit.h"
@@ -19,31 +20,33 @@
 #include "attr.h"
 #include "merge-recursive.h"
 #include "dir.h"
+#include "submodule.h"
 
-static struct tree *shift_tree_object(struct tree *one, struct tree *two)
+static struct tree *shift_tree_object(struct tree *one, struct tree *two,
+				      const char *subtree_shift)
 {
 	unsigned char shifted[20];
 
-	/*
-	 * NEEDSWORK: this limits the recursion depth to hardcoded
-	 * value '2' to avoid excessive overhead.
-	 */
-	shift_tree(one->object.sha1, two->object.sha1, shifted, 2);
+	if (!*subtree_shift) {
+		shift_tree(one->object.sha1, two->object.sha1, shifted, 0);
+	} else {
+		shift_tree_by(one->object.sha1, two->object.sha1, shifted,
+			      subtree_shift);
+	}
 	if (!hashcmp(two->object.sha1, shifted))
 		return two;
 	return lookup_tree(shifted);
 }
 
-/*
- * A virtual commit has (const char *)commit->util set to the name.
- */
-
 static struct commit *make_virtual_commit(struct tree *tree, const char *comment)
 {
 	struct commit *commit = xcalloc(1, sizeof(struct commit));
+	struct merge_remote_desc *desc = xmalloc(sizeof(*desc));
+
+	desc->name = comment;
+	desc->obj = (struct object *)commit;
 	commit->tree = tree;
-	commit->util = (void*)comment;
-	/* avoid warnings */
+	commit->util = desc;
 	commit->object.parsed = 1;
 	return commit;
 }
@@ -59,20 +62,86 @@
 	return a && b && hashcmp(a, b) == 0;
 }
 
+enum rename_type {
+	RENAME_NORMAL = 0,
+	RENAME_DELETE,
+	RENAME_ONE_FILE_TO_ONE,
+	RENAME_ONE_FILE_TO_TWO,
+	RENAME_TWO_FILES_TO_ONE
+};
+
+struct rename_conflict_info {
+	enum rename_type rename_type;
+	struct diff_filepair *pair1;
+	struct diff_filepair *pair2;
+	const char *branch1;
+	const char *branch2;
+	struct stage_data *dst_entry1;
+	struct stage_data *dst_entry2;
+	struct diff_filespec ren1_other;
+	struct diff_filespec ren2_other;
+};
+
 /*
  * Since we want to write the index eventually, we cannot reuse the index
  * for these (temporary) data.
  */
-struct stage_data
-{
-	struct
-	{
+struct stage_data {
+	struct {
 		unsigned mode;
 		unsigned char sha[20];
 	} stages[4];
+	struct rename_conflict_info *rename_conflict_info;
 	unsigned processed:1;
 };
 
+static inline void setup_rename_conflict_info(enum rename_type rename_type,
+					      struct diff_filepair *pair1,
+					      struct diff_filepair *pair2,
+					      const char *branch1,
+					      const char *branch2,
+					      struct stage_data *dst_entry1,
+					      struct stage_data *dst_entry2,
+					      struct merge_options *o,
+					      struct stage_data *src_entry1,
+					      struct stage_data *src_entry2)
+{
+	struct rename_conflict_info *ci = xcalloc(1, sizeof(struct rename_conflict_info));
+	ci->rename_type = rename_type;
+	ci->pair1 = pair1;
+	ci->branch1 = branch1;
+	ci->branch2 = branch2;
+
+	ci->dst_entry1 = dst_entry1;
+	dst_entry1->rename_conflict_info = ci;
+	dst_entry1->processed = 0;
+
+	assert(!pair2 == !dst_entry2);
+	if (dst_entry2) {
+		ci->dst_entry2 = dst_entry2;
+		ci->pair2 = pair2;
+		dst_entry2->rename_conflict_info = ci;
+	}
+
+	if (rename_type == RENAME_TWO_FILES_TO_ONE) {
+		/*
+		 * For each rename, there could have been
+		 * modifications on the side of history where that
+		 * file was not renamed.
+		 */
+		int ostage1 = o->branch1 == branch1 ? 3 : 2;
+		int ostage2 = ostage1 ^ 1;
+
+		ci->ren1_other.path = pair1->one->path;
+		hashcpy(ci->ren1_other.sha1, src_entry1->stages[ostage1].sha);
+		ci->ren1_other.mode = src_entry1->stages[ostage1].mode;
+
+		ci->ren2_other.path = pair2->one->path;
+		hashcpy(ci->ren2_other.sha1, src_entry2->stages[ostage2].sha);
+		ci->ren2_other.mode = src_entry2->stages[ostage2].mode;
+	}
+}
+
 static int show(struct merge_options *o, int v)
 {
 	return (!o->call_depth && o->verbosity >= v) || o->verbosity >= 5;
@@ -86,9 +155,9 @@
 	}
 }
 
+__attribute__((format (printf, 3, 4)))
 static void output(struct merge_options *o, int v, const char *fmt, ...)
 {
-	int len;
 	va_list ap;
 
 	if (!show(o, v))
@@ -99,21 +168,9 @@
 	strbuf_setlen(&o->obuf, o->obuf.len + o->call_depth * 2);
 
 	va_start(ap, fmt);
-	len = vsnprintf(o->obuf.buf + o->obuf.len, strbuf_avail(&o->obuf), fmt, ap);
+	strbuf_vaddf(&o->obuf, fmt, ap);
 	va_end(ap);
 
-	if (len < 0)
-		len = 0;
-	if (len >= strbuf_avail(&o->obuf)) {
-		strbuf_grow(&o->obuf, len + 2);
-		va_start(ap, fmt);
-		len = vsnprintf(o->obuf.buf + o->obuf.len, strbuf_avail(&o->obuf), fmt, ap);
-		va_end(ap);
-		if (len >= strbuf_avail(&o->obuf)) {
-			die("this should not happen, your snprintf is broken");
-		}
-	}
-	strbuf_setlen(&o->obuf, o->obuf.len + len);
 	strbuf_add(&o->obuf, "\n", 1);
 	if (!o->buffer_output)
 		flush_output(o);
@@ -126,22 +183,16 @@
 	for (i = o->call_depth; i--;)
 		fputs("  ", stdout);
 	if (commit->util)
-		printf("virtual %s\n", (char *)commit->util);
+		printf("virtual %s\n", merge_remote_util(commit)->name);
 	else {
 		printf("%s ", find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));
 		if (parse_commit(commit) != 0)
 			printf("(bad commit)\n");
 		else {
-			const char *s;
-			int len;
-			for (s = commit->buffer; *s; s++)
-				if (*s == '\n' && s[1] == '\n') {
-					s += 2;
-					break;
-				}
-			for (len = 0; s[len] && '\n' != s[len]; len++)
-				; /* do nothing */
-			printf("%.*s\n", len, s);
+			const char *title;
+			int len = find_commit_subject(commit->buffer, &title);
+			if (len)
+				printf("%.*s\n", len, title);
 		}
 	}
 }
@@ -181,6 +232,7 @@
 	opts.fn = threeway_merge;
 	opts.src_index = &the_index;
 	opts.dst_index = &the_index;
+	setup_unpack_trees_porcelain(&opts, "merge");
 
 	init_tree_desc_from_tree(t+0, common);
 	init_tree_desc_from_tree(t+1, head);
@@ -197,13 +249,14 @@
 
 	if (unmerged_cache()) {
 		int i;
-		output(o, 0, "There are unmerged index entries:");
+		fprintf(stderr, "BUG: There are unmerged index entries:\n");
 		for (i = 0; i < active_nr; i++) {
 			struct cache_entry *ce = active_cache[i];
 			if (ce_stage(ce))
-				output(o, 0, "%d %.*s", ce_stage(ce), ce_namelen(ce), ce->name);
+				fprintf(stderr, "BUG: %d %.*s\n", ce_stage(ce),
+					(int)ce_namelen(ce), ce->name);
 		}
-		return NULL;
+		die("Bug in merge-recursive.c");
 	}
 
 	if (!active_cache_tree)
@@ -211,7 +264,7 @@
 
 	if (!cache_tree_fully_valid(active_cache_tree) &&
 	    cache_tree_update(active_cache_tree,
-			      active_cache, active_nr, 0, 0) < 0)
+			      active_cache, active_nr, 0) < 0)
 		die("error building trees");
 
 	result = lookup_tree(active_cache_tree->sha1);
@@ -232,9 +285,9 @@
 	newpath[baselen + len] = '\0';
 
 	if (S_ISDIR(mode))
-		string_list_insert(newpath, &o->current_directory_set);
+		string_list_insert(&o->current_directory_set, newpath);
 	else
-		string_list_insert(newpath, &o->current_file_set);
+		string_list_insert(&o->current_file_set, newpath);
 	free(newpath);
 
 	return (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0);
@@ -243,7 +296,9 @@
 static int get_files_dirs(struct merge_options *o, struct tree *tree)
 {
 	int n;
-	if (read_tree_recursive(tree, "", 0, 0, NULL, save_files_dirs, o))
+	struct pathspec match_all;
+	init_pathspec(&match_all, NULL);
+	if (read_tree_recursive(tree, "", 0, 0, &match_all, save_files_dirs, o))
 		return 0;
 	n = o->current_file_set.nr + o->current_directory_set.nr;
 	return n;
@@ -265,7 +320,7 @@
 			e->stages[2].sha, &e->stages[2].mode);
 	get_tree_entry(b->object.sha1, path,
 			e->stages[3].sha, &e->stages[3].mode);
-	item = string_list_insert(path, entries);
+	item = string_list_insert(entries, path);
 	item->util = e;
 	return e;
 }
@@ -288,9 +343,9 @@
 		if (!ce_stage(ce))
 			continue;
 
-		item = string_list_lookup(ce->name, unmerged);
+		item = string_list_lookup(unmerged, ce->name);
 		if (!item) {
-			item = string_list_insert(ce->name, unmerged);
+			item = string_list_insert(unmerged, ce->name);
 			item->util = xcalloc(1, sizeof(struct stage_data));
 		}
 		e = item->util;
@@ -301,8 +356,109 @@
 	return unmerged;
 }
 
-struct rename
+static int string_list_df_name_compare(const void *a, const void *b)
 {
+	const struct string_list_item *one = a;
+	const struct string_list_item *two = b;
+	int onelen = strlen(one->string);
+	int twolen = strlen(two->string);
+	/*
+	 * Here we only care that entries for D/F conflicts are
+	 * adjacent, in particular with the file of the D/F conflict
+	 * appearing before files below the corresponding directory.
+	 * The order of the rest of the list is irrelevant for us.
+	 *
+	 * To achieve this, we sort with df_name_compare and provide
+	 * the mode S_IFDIR so that D/F conflicts will sort correctly.
+	 * We use the mode S_IFDIR for everything else for simplicity,
+	 * since in other cases any changes in their order due to
+	 * sorting cause no problems for us.
+	 */
+	int cmp = df_name_compare(one->string, onelen, S_IFDIR,
+				  two->string, twolen, S_IFDIR);
+	/*
+	 * Now that 'foo' and 'foo/bar' compare equal, we have to make sure
+	 * that 'foo' comes before 'foo/bar'.
+	 */
+	if (cmp)
+		return cmp;
+	return onelen - twolen;
+}
+
+static void record_df_conflict_files(struct merge_options *o,
+				     struct string_list *entries)
+{
+	/* If there is a D/F conflict and the file for such a conflict
+	 * currently exist in the working tree, we want to allow it to be
+	 * removed to make room for the corresponding directory if needed.
+	 * The files underneath the directories of such D/F conflicts will
+	 * be processed before the corresponding file involved in the D/F
+	 * conflict.  If the D/F directory ends up being removed by the
+	 * merge, then we won't have to touch the D/F file.  If the D/F
+	 * directory needs to be written to the working copy, then the D/F
+	 * file will simply be removed (in make_room_for_path()) to make
+	 * room for the necessary paths.  Note that if both the directory
+	 * and the file need to be present, then the D/F file will be
+	 * reinstated with a new unique name at the time it is processed.
+	 */
+	struct string_list df_sorted_entries;
+	const char *last_file = NULL;
+	int last_len = 0;
+	int i;
+
+	/*
+	 * If we're merging merge-bases, we don't want to bother with
+	 * any working directory changes.
+	 */
+	if (o->call_depth)
+		return;
+
+	/* Ensure D/F conflicts are adjacent in the entries list. */
+	memset(&df_sorted_entries, 0, sizeof(struct string_list));
+	for (i = 0; i < entries->nr; i++) {
+		struct string_list_item *next = &entries->items[i];
+		string_list_append(&df_sorted_entries, next->string)->util =
+				   next->util;
+	}
+	qsort(df_sorted_entries.items, entries->nr, sizeof(*entries->items),
+	      string_list_df_name_compare);
+
+	string_list_clear(&o->df_conflict_file_set, 1);
+	for (i = 0; i < df_sorted_entries.nr; i++) {
+		const char *path = df_sorted_entries.items[i].string;
+		int len = strlen(path);
+		struct stage_data *e = df_sorted_entries.items[i].util;
+
+		/*
+		 * Check if last_file & path correspond to a D/F conflict;
+		 * i.e. whether path is last_file+'/'+<something>.
+		 * If so, record that it's okay to remove last_file to make
+		 * room for path and friends if needed.
+		 */
+		if (last_file &&
+		    len > last_len &&
+		    memcmp(path, last_file, last_len) == 0 &&
+		    path[last_len] == '/') {
+			string_list_insert(&o->df_conflict_file_set, last_file);
+		}
+
+		/*
+		 * Determine whether path could exist as a file in the
+		 * working directory as a possible D/F conflict.  This
+		 * will only occur when it exists in stage 2 as a
+		 * file.
+		 */
+		if (S_ISREG(e->stages[2].mode) || S_ISLNK(e->stages[2].mode)) {
+			last_file = path;
+			last_len = len;
+		} else {
+			last_file = NULL;
+		}
+	}
+	string_list_clear(&df_sorted_entries, 0);
+}
+
+struct rename {
 	struct diff_filepair *pair;
 	struct stage_data *src_entry;
 	struct stage_data *dst_entry;
@@ -329,16 +485,20 @@
 	renames = xcalloc(1, sizeof(struct string_list));
 	diff_setup(&opts);
 	DIFF_OPT_SET(&opts, RECURSIVE);
+	DIFF_OPT_CLR(&opts, RENAME_EMPTY);
 	opts.detect_rename = DIFF_DETECT_RENAME;
 	opts.rename_limit = o->merge_rename_limit >= 0 ? o->merge_rename_limit :
 			    o->diff_rename_limit >= 0 ? o->diff_rename_limit :
-			    500;
-	opts.warn_on_too_large_rename = 1;
+			    1000;
+	opts.rename_score = o->rename_score;
+	opts.show_rename_progress = o->show_rename_progress;
 	opts.output_format = DIFF_FORMAT_NO_OUTPUT;
 	if (diff_setup_done(&opts) < 0)
 		die("diff setup failed");
 	diff_tree_sha1(o_tree->object.sha1, tree->object.sha1, "", &opts);
 	diffcore_std(&opts);
+	if (opts.needed_rename_limit > o->needed_rename_limit)
+		o->needed_rename_limit = opts.needed_rename_limit;
 	for (i = 0; i < diff_queued_diff.nr; ++i) {
 		struct string_list_item *item;
 		struct rename *re;
@@ -350,20 +510,20 @@
 		re = xmalloc(sizeof(*re));
 		re->processed = 0;
 		re->pair = pair;
-		item = string_list_lookup(re->pair->one->path, entries);
+		item = string_list_lookup(entries, re->pair->one->path);
 		if (!item)
 			re->src_entry = insert_stage_data(re->pair->one->path,
 					o_tree, a_tree, b_tree, entries);
 		else
 			re->src_entry = item->util;
 
-		item = string_list_lookup(re->pair->two->path, entries);
+		item = string_list_lookup(entries, re->pair->two->path);
 		if (!item)
 			re->dst_entry = insert_stage_data(re->pair->two->path,
 					o_tree, a_tree, b_tree, entries);
 		else
 			re->dst_entry = item->util;
-		item = string_list_insert(pair->one->path, renames);
+		item = string_list_insert(renames, pair->one->path);
 		item->util = re;
 	}
 	opts.output_format = DIFF_FORMAT_NO_OUTPUT;
@@ -372,11 +532,21 @@
 	return renames;
 }
 
-static int update_stages(const char *path, struct diff_filespec *o,
-			 struct diff_filespec *a, struct diff_filespec *b,
-			 int clear)
+static int update_stages(const char *path, const struct diff_filespec *o,
+			 const struct diff_filespec *a,
+			 const struct diff_filespec *b)
 {
-	int options = ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE;
+
+	/*
+	 * NOTE: It is usually a bad idea to call update_stages on a path
+	 * before calling update_file on that same path, since it can
+	 * sometimes lead to spurious "refusing to lose untracked file..."
+	 * messages from update_file (via make_room_for path via
+	 * would_lose_untracked).  Instead, reverse the order of the calls
+	 * (executing update_file first and then update_stages).
+	 */
+	int clear = 1;
+	int options = ADD_CACHE_OK_TO_ADD | ADD_CACHE_SKIP_DFCHECK;
 	if (clear)
 		if (remove_file_from_cache(path))
 			return -1;
@@ -392,6 +562,20 @@
 	return 0;
 }
 
+static void update_entry(struct stage_data *entry,
+			 struct diff_filespec *o,
+			 struct diff_filespec *a,
+			 struct diff_filespec *b)
+{
+	entry->processed = 0;
+	entry->stages[1].mode = o->mode;
+	entry->stages[2].mode = a->mode;
+	entry->stages[3].mode = b->mode;
+	hashcpy(entry->stages[1].sha, o->sha1);
+	hashcpy(entry->stages[2].sha, a->sha1);
+	hashcpy(entry->stages[3].sha, b->sha1);
+}
+
 static int remove_file(struct merge_options *o, int clean,
 		       const char *path, int no_wd)
 {
@@ -403,7 +587,7 @@
 			return -1;
 	}
 	if (update_working_directory) {
-		if (remove_path(path) && errno != ENOENT)
+		if (remove_path(path))
 			return -1;
 	}
 	return 0;
@@ -426,7 +610,7 @@
 	       lstat(newpath, &st) == 0)
 		sprintf(p, "_%d", suffix++);
 
-	string_list_insert(newpath, &o->current_file_set);
+	string_list_insert(&o->current_file_set, newpath);
 	return newpath;
 }
 
@@ -447,7 +631,31 @@
 	}
 }
 
-static int would_lose_untracked(const char *path)
+static int dir_in_way(const char *path, int check_working_copy)
+{
+	int pos, pathlen = strlen(path);
+	char *dirpath = xmalloc(pathlen + 2);
+	struct stat st;
+
+	strcpy(dirpath, path);
+	dirpath[pathlen] = '/';
+	dirpath[pathlen+1] = '\0';
+
+	pos = cache_name_pos(dirpath, pathlen+1);
+
+	if (pos < 0)
+		pos = -1 - pos;
+	if (pos < active_nr &&
+	    !strncmp(dirpath, active_cache[pos]->name, pathlen+1)) {
+		free(dirpath);
+		return 1;
+	}
+
+	free(dirpath);
+	return check_working_copy && !lstat(path, &st) && S_ISDIR(st.st_mode);
+}
+
+static int was_tracked(const char *path)
 {
 	int pos = cache_name_pos(path, strlen(path));
 
@@ -464,18 +672,42 @@
 		switch (ce_stage(active_cache[pos])) {
 		case 0:
 		case 2:
-			return 0;
+			return 1;
 		}
 		pos++;
 	}
-	return file_exists(path);
+	return 0;
 }
 
-static int make_room_for_path(const char *path)
+static int would_lose_untracked(const char *path)
 {
-	int status;
+	return !was_tracked(path) && file_exists(path);
+}
+
+static int make_room_for_path(struct merge_options *o, const char *path)
+{
+	int status, i;
 	const char *msg = "failed to create path '%s'%s";
 
+	/* Unlink any D/F conflict files that are in the way */
+	for (i = 0; i < o->df_conflict_file_set.nr; i++) {
+		const char *df_path = o->df_conflict_file_set.items[i].string;
+		size_t pathlen = strlen(path);
+		size_t df_pathlen = strlen(df_path);
+		if (df_pathlen < pathlen &&
+		    path[df_pathlen] == '/' &&
+		    strncmp(path, df_path, df_pathlen) == 0) {
+			output(o, 3,
+			       "Removing %s to make room for subdirectory\n",
+			       df_path);
+			unlink(df_path);
+			unsorted_string_list_delete_item(&o->df_conflict_file_set,
+							 i, 0);
+			break;
+		}
+	}
+
+	/* Make sure leading directories are created */
 	status = safe_create_leading_directories_const(path);
 	if (status) {
 		if (status == -3) {
@@ -519,13 +751,15 @@
 		void *buf;
 		unsigned long size;
 
-		if (S_ISGITLINK(mode))
+		if (S_ISGITLINK(mode)) {
 			/*
 			 * We may later decide to recursively descend into
 			 * the submodule directory and update its index
 			 * and/or work tree, but we do not do that now.
 			 */
+			update_wd = 0;
 			goto update_index;
+		}
 
 		buf = read_sha1_file(sha, &type, &size);
 		if (!buf)
@@ -541,7 +775,7 @@
 			}
 		}
 
-		if (make_room_for_path(path) < 0) {
+		if (make_room_for_path(o, path) < 0) {
 			update_wd = 0;
 			free(buf);
 			goto update_index;
@@ -585,58 +819,65 @@
 
 /* Low level file merging, update and removal */
 
-struct merge_file_info
-{
+struct merge_file_info {
 	unsigned char sha[20];
 	unsigned mode;
 	unsigned clean:1,
 		 merge:1;
 };
 
-static void fill_mm(const unsigned char *sha1, mmfile_t *mm)
-{
-	unsigned long size;
-	enum object_type type;
-
-	if (!hashcmp(sha1, null_sha1)) {
-		mm->ptr = xstrdup("");
-		mm->size = 0;
-		return;
-	}
-
-	mm->ptr = read_sha1_file(sha1, &type, &size);
-	if (!mm->ptr || type != OBJ_BLOB)
-		die("unable to read blob object %s", sha1_to_hex(sha1));
-	mm->size = size;
-}
-
 static int merge_3way(struct merge_options *o,
 		      mmbuffer_t *result_buf,
-		      struct diff_filespec *one,
-		      struct diff_filespec *a,
-		      struct diff_filespec *b,
+		      const struct diff_filespec *one,
+		      const struct diff_filespec *a,
+		      const struct diff_filespec *b,
 		      const char *branch1,
 		      const char *branch2)
 {
 	mmfile_t orig, src1, src2;
-	char *name1, *name2;
+	struct ll_merge_options ll_opts = {0};
+	char *base_name, *name1, *name2;
 	int merge_status;
 
-	if (strcmp(a->path, b->path)) {
+	ll_opts.renormalize = o->renormalize;
+	ll_opts.xdl_opts = o->xdl_opts;
+
+	if (o->call_depth) {
+		ll_opts.virtual_ancestor = 1;
+		ll_opts.variant = 0;
+	} else {
+		switch (o->recursive_variant) {
+		case MERGE_RECURSIVE_OURS:
+			ll_opts.variant = XDL_MERGE_FAVOR_OURS;
+			break;
+		case MERGE_RECURSIVE_THEIRS:
+			ll_opts.variant = XDL_MERGE_FAVOR_THEIRS;
+			break;
+		default:
+			ll_opts.variant = 0;
+			break;
+		}
+	}
+
+	if (strcmp(a->path, b->path) ||
+	    (o->ancestor != NULL && strcmp(a->path, one->path) != 0)) {
+		base_name = o->ancestor == NULL ? NULL :
+			xstrdup(mkpath("%s:%s", o->ancestor, one->path));
 		name1 = xstrdup(mkpath("%s:%s", branch1, a->path));
 		name2 = xstrdup(mkpath("%s:%s", branch2, b->path));
 	} else {
+		base_name = o->ancestor == NULL ? NULL :
+			xstrdup(mkpath("%s", o->ancestor));
 		name1 = xstrdup(mkpath("%s", branch1));
 		name2 = xstrdup(mkpath("%s", branch2));
 	}
 
-	fill_mm(one->sha1, &orig);
-	fill_mm(a->sha1, &src1);
-	fill_mm(b->sha1, &src2);
+	read_mmblob(&orig, one->sha1);
+	read_mmblob(&src1, a->sha1);
+	read_mmblob(&src2, b->sha1);
 
-	merge_status = ll_merge(result_buf, a->path, &orig,
-				&src1, name1, &src2, name2,
-				o->call_depth);
+	merge_status = ll_merge(result_buf, a->path, &orig, base_name,
+				&src1, name1, &src2, name2, &ll_opts);
 
 	free(name1);
 	free(name2);
@@ -646,12 +887,12 @@
 	return merge_status;
 }
 
-static struct merge_file_info merge_file(struct merge_options *o,
-				         struct diff_filespec *one,
-					 struct diff_filespec *a,
-					 struct diff_filespec *b,
-					 const char *branch1,
-					 const char *branch2)
+static struct merge_file_info merge_file_1(struct merge_options *o,
+					   const struct diff_filespec *one,
+					   const struct diff_filespec *a,
+					   const struct diff_filespec *b,
+					   const char *branch1,
+					   const char *branch2)
 {
 	struct merge_file_info result;
 	result.merge = 0;
@@ -705,8 +946,10 @@
 			free(result_buf.ptr);
 			result.clean = (merge_status == 0);
 		} else if (S_ISGITLINK(a->mode)) {
-			result.clean = 0;
-			hashcpy(result.sha, a->sha1);
+			result.clean = merge_submodule(result.sha,
+						       one->path, one->sha1,
+						       a->sha1, b->sha1,
+						       !o->call_depth);
 		} else if (S_ISLNK(a->mode)) {
 			hashcpy(result.sha, a->sha1);
 
@@ -720,74 +963,303 @@
 	return result;
 }
 
-static void conflict_rename_rename(struct merge_options *o,
-				   struct rename *ren1,
-				   const char *branch1,
-				   struct rename *ren2,
-				   const char *branch2)
+static struct merge_file_info
+merge_file_special_markers(struct merge_options *o,
+			   const struct diff_filespec *one,
+			   const struct diff_filespec *a,
+			   const struct diff_filespec *b,
+			   const char *branch1,
+			   const char *filename1,
+			   const char *branch2,
+			   const char *filename2)
 {
-	char *del[2];
-	int delp = 0;
-	const char *ren1_dst = ren1->pair->two->path;
-	const char *ren2_dst = ren2->pair->two->path;
-	const char *dst_name1 = ren1_dst;
-	const char *dst_name2 = ren2_dst;
-	if (string_list_has_string(&o->current_directory_set, ren1_dst)) {
-		dst_name1 = del[delp++] = unique_path(o, ren1_dst, branch1);
-		output(o, 1, "%s is a directory in %s adding as %s instead",
-		       ren1_dst, branch2, dst_name1);
-		remove_file(o, 0, ren1_dst, 0);
+	char *side1 = NULL;
+	char *side2 = NULL;
+	struct merge_file_info mfi;
+
+	if (filename1) {
+		side1 = xmalloc(strlen(branch1) + strlen(filename1) + 2);
+		sprintf(side1, "%s:%s", branch1, filename1);
 	}
-	if (string_list_has_string(&o->current_directory_set, ren2_dst)) {
-		dst_name2 = del[delp++] = unique_path(o, ren2_dst, branch2);
-		output(o, 1, "%s is a directory in %s adding as %s instead",
-		       ren2_dst, branch1, dst_name2);
-		remove_file(o, 0, ren2_dst, 0);
+	if (filename2) {
+		side2 = xmalloc(strlen(branch2) + strlen(filename2) + 2);
+		sprintf(side2, "%s:%s", branch2, filename2);
 	}
+
+	mfi = merge_file_1(o, one, a, b,
+			   side1 ? side1 : branch1, side2 ? side2 : branch2);
+	free(side1);
+	free(side2);
+	return mfi;
+}
+
+static struct merge_file_info merge_file(struct merge_options *o,
+					 const char *path,
+					 const unsigned char *o_sha, int o_mode,
+					 const unsigned char *a_sha, int a_mode,
+					 const unsigned char *b_sha, int b_mode,
+					 const char *branch1,
+					 const char *branch2)
+{
+	struct diff_filespec one, a, b;
+
+	one.path = a.path = b.path = (char *)path;
+	hashcpy(one.sha1, o_sha);
+	one.mode = o_mode;
+	hashcpy(a.sha1, a_sha);
+	a.mode = a_mode;
+	hashcpy(b.sha1, b_sha);
+	b.mode = b_mode;
+	return merge_file_1(o, &one, &a, &b, branch1, branch2);
+}
+
+static void handle_change_delete(struct merge_options *o,
+				 const char *path,
+				 const unsigned char *o_sha, int o_mode,
+				 const unsigned char *a_sha, int a_mode,
+				 const unsigned char *b_sha, int b_mode,
+				 const char *change, const char *change_past)
+{
+	char *renamed = NULL;
+	if (dir_in_way(path, !o->call_depth)) {
+		renamed = unique_path(o, path, a_sha ? o->branch1 : o->branch2);
+	}
+
 	if (o->call_depth) {
-		remove_file_from_cache(dst_name1);
-		remove_file_from_cache(dst_name2);
 		/*
-		 * Uncomment to leave the conflicting names in the resulting tree
-		 *
-		 * update_file(o, 0, ren1->pair->two->sha1, ren1->pair->two->mode, dst_name1);
-		 * update_file(o, 0, ren2->pair->two->sha1, ren2->pair->two->mode, dst_name2);
+		 * We cannot arbitrarily accept either a_sha or b_sha as
+		 * correct; since there is no true "middle point" between
+		 * them, simply reuse the base version for virtual merge base.
 		 */
+		remove_file_from_cache(path);
+		update_file(o, 0, o_sha, o_mode, renamed ? renamed : path);
+	} else if (!a_sha) {
+		output(o, 1, "CONFLICT (%s/delete): %s deleted in %s "
+		       "and %s in %s. Version %s of %s left in tree%s%s.",
+		       change, path, o->branch1,
+		       change_past, o->branch2, o->branch2, path,
+		       NULL == renamed ? "" : " at ",
+		       NULL == renamed ? "" : renamed);
+		update_file(o, 0, b_sha, b_mode, renamed ? renamed : path);
 	} else {
-		update_stages(dst_name1, NULL, ren1->pair->two, NULL, 1);
-		update_stages(dst_name2, NULL, NULL, ren2->pair->two, 1);
+		output(o, 1, "CONFLICT (%s/delete): %s deleted in %s "
+		       "and %s in %s. Version %s of %s left in tree%s%s.",
+		       change, path, o->branch2,
+		       change_past, o->branch1, o->branch1, path,
+		       NULL == renamed ? "" : " at ",
+		       NULL == renamed ? "" : renamed);
+		if (renamed)
+			update_file(o, 0, a_sha, a_mode, renamed);
+		/*
+		 * No need to call update_file() on path when !renamed, since
+		 * that would needlessly touch path.  We could call
+		 * update_file_flags() with update_cache=0 and update_wd=0,
+		 * but that's a no-op.
+		 */
 	}
-	while (delp--)
-		free(del[delp]);
+	free(renamed);
 }
 
-static void conflict_rename_dir(struct merge_options *o,
-				struct rename *ren1,
-				const char *branch1)
+static void conflict_rename_delete(struct merge_options *o,
+				   struct diff_filepair *pair,
+				   const char *rename_branch,
+				   const char *other_branch)
 {
-	char *new_path = unique_path(o, ren1->pair->two->path, branch1);
-	output(o, 1, "Renaming %s to %s instead", ren1->pair->one->path, new_path);
-	remove_file(o, 0, ren1->pair->two->path, 0);
-	update_file(o, 0, ren1->pair->two->sha1, ren1->pair->two->mode, new_path);
-	free(new_path);
+	const struct diff_filespec *orig = pair->one;
+	const struct diff_filespec *dest = pair->two;
+	const unsigned char *a_sha = NULL;
+	const unsigned char *b_sha = NULL;
+	int a_mode = 0;
+	int b_mode = 0;
+
+	if (rename_branch == o->branch1) {
+		a_sha = dest->sha1;
+		a_mode = dest->mode;
+	} else {
+		b_sha = dest->sha1;
+		b_mode = dest->mode;
+	}
+
+	handle_change_delete(o,
+			     o->call_depth ? orig->path : dest->path,
+			     orig->sha1, orig->mode,
+			     a_sha, a_mode,
+			     b_sha, b_mode,
+			     "rename", "renamed");
+
+	if (o->call_depth) {
+		remove_file_from_cache(dest->path);
+	} else {
+		update_stages(dest->path, NULL,
+			      rename_branch == o->branch1 ? dest : NULL,
+			      rename_branch == o->branch1 ? NULL : dest);
+	}
+
 }
 
-static void conflict_rename_rename_2(struct merge_options *o,
-				     struct rename *ren1,
-				     const char *branch1,
-				     struct rename *ren2,
-				     const char *branch2)
+static struct diff_filespec *filespec_from_entry(struct diff_filespec *target,
+						 struct stage_data *entry,
+						 int stage)
 {
-	char *new_path1 = unique_path(o, ren1->pair->two->path, branch1);
-	char *new_path2 = unique_path(o, ren2->pair->two->path, branch2);
-	output(o, 1, "Renaming %s to %s and %s to %s instead",
-	       ren1->pair->one->path, new_path1,
-	       ren2->pair->one->path, new_path2);
-	remove_file(o, 0, ren1->pair->two->path, 0);
-	update_file(o, 0, ren1->pair->two->sha1, ren1->pair->two->mode, new_path1);
-	update_file(o, 0, ren2->pair->two->sha1, ren2->pair->two->mode, new_path2);
-	free(new_path2);
-	free(new_path1);
+	unsigned char *sha = entry->stages[stage].sha;
+	unsigned mode = entry->stages[stage].mode;
+	if (mode == 0 || is_null_sha1(sha))
+		return NULL;
+	hashcpy(target->sha1, sha);
+	target->mode = mode;
+	return target;
+}
+
+static void handle_file(struct merge_options *o,
+			struct diff_filespec *rename,
+			int stage,
+			struct rename_conflict_info *ci)
+{
+	char *dst_name = rename->path;
+	struct stage_data *dst_entry;
+	const char *cur_branch, *other_branch;
+	struct diff_filespec other;
+	struct diff_filespec *add;
+
+	if (stage == 2) {
+		dst_entry = ci->dst_entry1;
+		cur_branch = ci->branch1;
+		other_branch = ci->branch2;
+	} else {
+		dst_entry = ci->dst_entry2;
+		cur_branch = ci->branch2;
+		other_branch = ci->branch1;
+	}
+
+	add = filespec_from_entry(&other, dst_entry, stage ^ 1);
+	if (add) {
+		char *add_name = unique_path(o, rename->path, other_branch);
+		update_file(o, 0, add->sha1, add->mode, add_name);
+
+		remove_file(o, 0, rename->path, 0);
+		dst_name = unique_path(o, rename->path, cur_branch);
+	} else {
+		if (dir_in_way(rename->path, !o->call_depth)) {
+			dst_name = unique_path(o, rename->path, cur_branch);
+			output(o, 1, "%s is a directory in %s adding as %s instead",
+			       rename->path, other_branch, dst_name);
+		}
+	}
+	update_file(o, 0, rename->sha1, rename->mode, dst_name);
+	if (stage == 2)
+		update_stages(rename->path, NULL, rename, add);
+	else
+		update_stages(rename->path, NULL, add, rename);
+
+	if (dst_name != rename->path)
+		free(dst_name);
+}
+
+static void conflict_rename_rename_1to2(struct merge_options *o,
+					struct rename_conflict_info *ci)
+{
+	/* One file was renamed in both branches, but to different names. */
+	struct diff_filespec *one = ci->pair1->one;
+	struct diff_filespec *a = ci->pair1->two;
+	struct diff_filespec *b = ci->pair2->two;
+
+	output(o, 1, "CONFLICT (rename/rename): "
+	       "Rename \"%s\"->\"%s\" in branch \"%s\" "
+	       "rename \"%s\"->\"%s\" in \"%s\"%s",
+	       one->path, a->path, ci->branch1,
+	       one->path, b->path, ci->branch2,
+	       o->call_depth ? " (left unresolved)" : "");
+	if (o->call_depth) {
+		struct merge_file_info mfi;
+		struct diff_filespec other;
+		struct diff_filespec *add;
+		mfi = merge_file(o, one->path,
+				 one->sha1, one->mode,
+				 a->sha1, a->mode,
+				 b->sha1, b->mode,
+				 ci->branch1, ci->branch2);
+		/*
+		 * FIXME: For rename/add-source conflicts (if we could detect
+		 * such), this is wrong.  We should instead find a unique
+		 * pathname and then either rename the add-source file to that
+		 * unique path, or use that unique path instead of src here.
+		 */
+		update_file(o, 0, mfi.sha, mfi.mode, one->path);
+
+		/*
+		 * Above, we put the merged content at the merge-base's
+		 * path.  Now we usually need to delete both a->path and
+		 * b->path.  However, the rename on each side of the merge
+		 * could also be involved in a rename/add conflict.  In
+		 * such cases, we should keep the added file around,
+		 * resolving the conflict at that path in its favor.
+		 */
+		add = filespec_from_entry(&other, ci->dst_entry1, 2 ^ 1);
+		if (add)
+			update_file(o, 0, add->sha1, add->mode, a->path);
+		else
+			remove_file_from_cache(a->path);
+		add = filespec_from_entry(&other, ci->dst_entry2, 3 ^ 1);
+		if (add)
+			update_file(o, 0, add->sha1, add->mode, b->path);
+		else
+			remove_file_from_cache(b->path);
+	} else {
+		handle_file(o, a, 2, ci);
+		handle_file(o, b, 3, ci);
+	}
+}
+
+static void conflict_rename_rename_2to1(struct merge_options *o,
+					struct rename_conflict_info *ci)
+{
+	/* Two files, a & b, were renamed to the same thing, c. */
+	struct diff_filespec *a = ci->pair1->one;
+	struct diff_filespec *b = ci->pair2->one;
+	struct diff_filespec *c1 = ci->pair1->two;
+	struct diff_filespec *c2 = ci->pair2->two;
+	char *path = c1->path; /* == c2->path */
+	struct merge_file_info mfi_c1;
+	struct merge_file_info mfi_c2;
+
+	output(o, 1, "CONFLICT (rename/rename): "
+	       "Rename %s->%s in %s. "
+	       "Rename %s->%s in %s",
+	       a->path, c1->path, ci->branch1,
+	       b->path, c2->path, ci->branch2);
+
+	remove_file(o, 1, a->path, would_lose_untracked(a->path));
+	remove_file(o, 1, b->path, would_lose_untracked(b->path));
+
+	mfi_c1 = merge_file_special_markers(o, a, c1, &ci->ren1_other,
+					    o->branch1, c1->path,
+					    o->branch2, ci->ren1_other.path);
+	mfi_c2 = merge_file_special_markers(o, b, &ci->ren2_other, c2,
+					    o->branch1, ci->ren2_other.path,
+					    o->branch2, c2->path);
+
+	if (o->call_depth) {
+		/*
+		 * If mfi_c1.clean && mfi_c2.clean, then it might make
+		 * sense to do a two-way merge of those results.  But, I
+		 * think in all cases, it makes sense to have the virtual
+		 * merge base just undo the renames; they can be detected
+		 * again later for the non-recursive merge.
+		 */
+		remove_file(o, 0, path, 0);
+		update_file(o, 0, mfi_c1.sha, mfi_c1.mode, a->path);
+		update_file(o, 0, mfi_c2.sha, mfi_c2.mode, b->path);
+	} else {
+		char *new_path1 = unique_path(o, path, ci->branch1);
+		char *new_path2 = unique_path(o, path, ci->branch2);
+		output(o, 1, "Renaming %s to %s and %s to %s instead",
+		       a->path, new_path1, b->path, new_path2);
+		remove_file(o, 0, path, 0);
+		update_file(o, 0, mfi_c1.sha, mfi_c1.mode, new_path1);
+		update_file(o, 0, mfi_c2.sha, mfi_c2.mode, new_path2);
+		free(new_path2);
+		free(new_path1);
+	}
 }
 
 static int process_renames(struct merge_options *o,
@@ -795,26 +1267,27 @@
 			   struct string_list *b_renames)
 {
 	int clean_merge = 1, i, j;
-	struct string_list a_by_dst = {NULL, 0, 0, 0}, b_by_dst = {NULL, 0, 0, 0};
+	struct string_list a_by_dst = STRING_LIST_INIT_NODUP;
+	struct string_list b_by_dst = STRING_LIST_INIT_NODUP;
 	const struct rename *sre;
 
 	for (i = 0; i < a_renames->nr; i++) {
 		sre = a_renames->items[i].util;
-		string_list_insert(sre->pair->two->path, &a_by_dst)->util
-			= sre->dst_entry;
+		string_list_insert(&a_by_dst, sre->pair->two->path)->util
+			= (void *)sre;
 	}
 	for (i = 0; i < b_renames->nr; i++) {
 		sre = b_renames->items[i].util;
-		string_list_insert(sre->pair->two->path, &b_by_dst)->util
-			= sre->dst_entry;
+		string_list_insert(&b_by_dst, sre->pair->two->path)->util
+			= (void *)sre;
 	}
 
 	for (i = 0, j = 0; i < a_renames->nr || j < b_renames->nr;) {
-		char *src;
 		struct string_list *renames1, *renames2Dst;
 		struct rename *ren1 = NULL, *ren2 = NULL;
 		const char *branch1, *branch2;
 		const char *ren1_src, *ren1_dst;
+		struct string_list_item *lookup;
 
 		if (i >= a_renames->nr) {
 			ren2 = b_renames->items[j++].util;
@@ -845,132 +1318,163 @@
 			ren2 = ren1;
 			ren1 = tmp;
 		}
-		src = ren1->pair->one->path;
-
-		ren1->dst_entry->processed = 1;
-		ren1->src_entry->processed = 1;
 
 		if (ren1->processed)
 			continue;
 		ren1->processed = 1;
+		ren1->dst_entry->processed = 1;
+		/* BUG: We should only mark src_entry as processed if we
+		 * are not dealing with a rename + add-source case.
+		 */
+		ren1->src_entry->processed = 1;
 
 		ren1_src = ren1->pair->one->path;
 		ren1_dst = ren1->pair->two->path;
 
 		if (ren2) {
+			/* One file renamed on both sides */
 			const char *ren2_src = ren2->pair->one->path;
 			const char *ren2_dst = ren2->pair->two->path;
-			/* Renamed in 1 and renamed in 2 */
+			enum rename_type rename_type;
 			if (strcmp(ren1_src, ren2_src) != 0)
-				die("ren1.src != ren2.src");
+				die("ren1_src != ren2_src");
 			ren2->dst_entry->processed = 1;
 			ren2->processed = 1;
 			if (strcmp(ren1_dst, ren2_dst) != 0) {
+				rename_type = RENAME_ONE_FILE_TO_TWO;
 				clean_merge = 0;
-				output(o, 1, "CONFLICT (rename/rename): "
-				       "Rename \"%s\"->\"%s\" in branch \"%s\" "
-				       "rename \"%s\"->\"%s\" in \"%s\"%s",
-				       src, ren1_dst, branch1,
-				       src, ren2_dst, branch2,
-				       o->call_depth ? " (left unresolved)": "");
-				if (o->call_depth) {
-					remove_file_from_cache(src);
-					update_file(o, 0, ren1->pair->one->sha1,
-						    ren1->pair->one->mode, src);
-				}
-				conflict_rename_rename(o, ren1, branch1, ren2, branch2);
 			} else {
-				struct merge_file_info mfi;
+				rename_type = RENAME_ONE_FILE_TO_ONE;
+				/* BUG: We should only remove ren1_src in
+				 * the base stage (think of rename +
+				 * add-source cases).
+				 */
 				remove_file(o, 1, ren1_src, 1);
-				mfi = merge_file(o,
-						 ren1->pair->one,
-						 ren1->pair->two,
-						 ren2->pair->two,
-						 branch1,
-						 branch2);
-				if (mfi.merge || !mfi.clean)
-					output(o, 1, "Renaming %s->%s", src, ren1_dst);
-
-				if (mfi.merge)
-					output(o, 2, "Auto-merging %s", ren1_dst);
-
-				if (!mfi.clean) {
-					output(o, 1, "CONFLICT (content): merge conflict in %s",
-					       ren1_dst);
-					clean_merge = 0;
-
-					if (!o->call_depth)
-						update_stages(ren1_dst,
-							      ren1->pair->one,
-							      ren1->pair->two,
-							      ren2->pair->two,
-							      1 /* clear */);
-				}
-				update_file(o, mfi.clean, mfi.sha, mfi.mode, ren1_dst);
+				update_entry(ren1->dst_entry,
+					     ren1->pair->one,
+					     ren1->pair->two,
+					     ren2->pair->two);
 			}
+			setup_rename_conflict_info(rename_type,
+						   ren1->pair,
+						   ren2->pair,
+						   branch1,
+						   branch2,
+						   ren1->dst_entry,
+						   ren2->dst_entry,
+						   o,
+						   NULL,
+						   NULL);
+		} else if ((lookup = string_list_lookup(renames2Dst, ren1_dst))) {
+			/* Two different files renamed to the same thing */
+			char *ren2_dst;
+			ren2 = lookup->util;
+			ren2_dst = ren2->pair->two->path;
+			if (strcmp(ren1_dst, ren2_dst) != 0)
+				die("ren1_dst != ren2_dst");
+
+			clean_merge = 0;
+			ren2->processed = 1;
+			/*
+			 * BUG: We should only mark src_entry as processed
+			 * if we are not dealing with a rename + add-source
+			 * case.
+			 */
+			ren2->src_entry->processed = 1;
+
+			setup_rename_conflict_info(RENAME_TWO_FILES_TO_ONE,
+						   ren1->pair,
+						   ren2->pair,
+						   branch1,
+						   branch2,
+						   ren1->dst_entry,
+						   ren2->dst_entry,
+						   o,
+						   ren1->src_entry,
+						   ren2->src_entry);
+
 		} else {
 			/* Renamed in 1, maybe changed in 2 */
-			struct string_list_item *item;
 			/* we only use sha1 and mode of these */
 			struct diff_filespec src_other, dst_other;
-			int try_merge, stage = a_renames == renames1 ? 3: 2;
+			int try_merge;
 
-			remove_file(o, 1, ren1_src, o->call_depth || stage == 3);
+			/*
+			 * unpack_trees loads entries from common-commit
+			 * into stage 1, from head-commit into stage 2, and
+			 * from merge-commit into stage 3.  We keep track
+			 * of which side corresponds to the rename.
+			 */
+			int renamed_stage = a_renames == renames1 ? 2 : 3;
+			int other_stage =   a_renames == renames1 ? 3 : 2;
 
-			hashcpy(src_other.sha1, ren1->src_entry->stages[stage].sha);
-			src_other.mode = ren1->src_entry->stages[stage].mode;
-			hashcpy(dst_other.sha1, ren1->dst_entry->stages[stage].sha);
-			dst_other.mode = ren1->dst_entry->stages[stage].mode;
+			/* BUG: We should only remove ren1_src in the base
+			 * stage and in other_stage (think of rename +
+			 * add-source case).
+			 */
+			remove_file(o, 1, ren1_src,
+				    renamed_stage == 2 || !was_tracked(ren1_src));
 
+			hashcpy(src_other.sha1, ren1->src_entry->stages[other_stage].sha);
+			src_other.mode = ren1->src_entry->stages[other_stage].mode;
+			hashcpy(dst_other.sha1, ren1->dst_entry->stages[other_stage].sha);
+			dst_other.mode = ren1->dst_entry->stages[other_stage].mode;
 			try_merge = 0;
 
-			if (string_list_has_string(&o->current_directory_set, ren1_dst)) {
-				clean_merge = 0;
-				output(o, 1, "CONFLICT (rename/directory): Rename %s->%s in %s "
-				       " directory %s added in %s",
-				       ren1_src, ren1_dst, branch1,
-				       ren1_dst, branch2);
-				conflict_rename_dir(o, ren1, branch1);
-			} else if (sha_eq(src_other.sha1, null_sha1)) {
-				clean_merge = 0;
-				output(o, 1, "CONFLICT (rename/delete): Rename %s->%s in %s "
-				       "and deleted in %s",
-				       ren1_src, ren1_dst, branch1,
-				       branch2);
-				update_file(o, 0, ren1->pair->two->sha1, ren1->pair->two->mode, ren1_dst);
-				if (!o->call_depth)
-					update_stages(ren1_dst, NULL,
-							branch1 == o->branch1 ?
-							ren1->pair->two : NULL,
-							branch1 == o->branch1 ?
-							NULL : ren1->pair->two, 1);
+			if (sha_eq(src_other.sha1, null_sha1)) {
+				setup_rename_conflict_info(RENAME_DELETE,
+							   ren1->pair,
+							   NULL,
+							   branch1,
+							   branch2,
+							   ren1->dst_entry,
+							   NULL,
+							   o,
+							   NULL,
+							   NULL);
+			} else if ((dst_other.mode == ren1->pair->two->mode) &&
+				   sha_eq(dst_other.sha1, ren1->pair->two->sha1)) {
+				/*
+				 * Added file on the other side identical to
+				 * the file being renamed: clean merge.
+				 * Also, there is no need to overwrite the
+				 * file already in the working copy, so call
+				 * update_file_flags() instead of
+				 * update_file().
+				 */
+				update_file_flags(o,
+						  ren1->pair->two->sha1,
+						  ren1->pair->two->mode,
+						  ren1_dst,
+						  1, /* update_cache */
+						  0  /* update_wd    */);
 			} else if (!sha_eq(dst_other.sha1, null_sha1)) {
-				const char *new_path;
 				clean_merge = 0;
 				try_merge = 1;
 				output(o, 1, "CONFLICT (rename/add): Rename %s->%s in %s. "
 				       "%s added in %s",
 				       ren1_src, ren1_dst, branch1,
 				       ren1_dst, branch2);
-				new_path = unique_path(o, ren1_dst, branch2);
-				output(o, 1, "Adding as %s instead", new_path);
-				update_file(o, 0, dst_other.sha1, dst_other.mode, new_path);
-			} else if ((item = string_list_lookup(ren1_dst, renames2Dst))) {
-				ren2 = item->util;
-				clean_merge = 0;
-				ren2->processed = 1;
-				output(o, 1, "CONFLICT (rename/rename): "
-				       "Rename %s->%s in %s. "
-				       "Rename %s->%s in %s",
-				       ren1_src, ren1_dst, branch1,
-				       ren2->pair->one->path, ren2->pair->two->path, branch2);
-				conflict_rename_rename_2(o, ren1, branch1, ren2, branch2);
+				if (o->call_depth) {
+					struct merge_file_info mfi;
+					mfi = merge_file(o, ren1_dst, null_sha1, 0,
+							 ren1->pair->two->sha1, ren1->pair->two->mode,
+							 dst_other.sha1, dst_other.mode,
+							 branch1, branch2);
+					output(o, 1, "Adding merged %s", ren1_dst);
+					update_file(o, 0, mfi.sha, mfi.mode, ren1_dst);
+					try_merge = 0;
+				} else {
+					char *new_path = unique_path(o, ren1_dst, branch2);
+					output(o, 1, "Adding as %s instead", new_path);
+					update_file(o, 0, dst_other.sha1, dst_other.mode, new_path);
+					free(new_path);
+				}
 			} else
 				try_merge = 1;
 
 			if (try_merge) {
 				struct diff_filespec *one, *a, *b;
-				struct merge_file_info mfi;
 				src_other.path = (char *)ren1_src;
 
 				one = ren1->pair->one;
@@ -981,34 +1485,17 @@
 					b = ren1->pair->two;
 					a = &src_other;
 				}
-				mfi = merge_file(o, one, a, b,
-						o->branch1, o->branch2);
-
-				if (mfi.clean &&
-				    sha_eq(mfi.sha, ren1->pair->two->sha1) &&
-				    mfi.mode == ren1->pair->two->mode)
-					/*
-					 * This messaged is part of
-					 * t6022 test. If you change
-					 * it update the test too.
-					 */
-					output(o, 3, "Skipped %s (merged same as existing)", ren1_dst);
-				else {
-					if (mfi.merge || !mfi.clean)
-						output(o, 1, "Renaming %s => %s", ren1_src, ren1_dst);
-					if (mfi.merge)
-						output(o, 2, "Auto-merging %s", ren1_dst);
-					if (!mfi.clean) {
-						output(o, 1, "CONFLICT (rename/modify): Merge conflict in %s",
-						       ren1_dst);
-						clean_merge = 0;
-
-						if (!o->call_depth)
-							update_stages(ren1_dst,
-								      one, a, b, 1);
-					}
-					update_file(o, mfi.clean, mfi.sha, mfi.mode, ren1_dst);
-				}
+				update_entry(ren1->dst_entry, one, a, b);
+				setup_rename_conflict_info(RENAME_NORMAL,
+							   ren1->pair,
+							   NULL,
+							   branch1,
+							   NULL,
+							   ren1->dst_entry,
+							   NULL,
+							   o,
+							   NULL,
+							   NULL);
 			}
 		}
 	}
@@ -1023,6 +1510,171 @@
 	return (is_null_sha1(sha) || mode == 0) ? NULL: (unsigned char *)sha;
 }
 
+static int read_sha1_strbuf(const unsigned char *sha1, struct strbuf *dst)
+{
+	void *buf;
+	enum object_type type;
+	unsigned long size;
+	buf = read_sha1_file(sha1, &type, &size);
+	if (!buf)
+		return error("cannot read object %s", sha1_to_hex(sha1));
+	if (type != OBJ_BLOB) {
+		free(buf);
+		return error("object %s is not a blob", sha1_to_hex(sha1));
+	}
+	strbuf_attach(dst, buf, size, size + 1);
+	return 0;
+}
+
+static int blob_unchanged(const unsigned char *o_sha,
+			  const unsigned char *a_sha,
+			  int renormalize, const char *path)
+{
+	struct strbuf o = STRBUF_INIT;
+	struct strbuf a = STRBUF_INIT;
+	int ret = 0; /* assume changed for safety */
+
+	if (sha_eq(o_sha, a_sha))
+		return 1;
+	if (!renormalize)
+		return 0;
+
+	assert(o_sha && a_sha);
+	if (read_sha1_strbuf(o_sha, &o) || read_sha1_strbuf(a_sha, &a))
+		goto error_return;
+	/*
+	 * Note: binary | is used so that both renormalizations are
+	 * performed.  Comparison can be skipped if both files are
+	 * unchanged since their sha1s have already been compared.
+	 */
+	if (renormalize_buffer(path, o.buf, o.len, &o) |
+	    renormalize_buffer(path, a.buf, o.len, &a))
+		ret = (o.len == a.len && !memcmp(o.buf, a.buf, o.len));
+
+error_return:
+	strbuf_release(&o);
+	strbuf_release(&a);
+	return ret;
+}
+
+static void handle_modify_delete(struct merge_options *o,
+				 const char *path,
+				 unsigned char *o_sha, int o_mode,
+				 unsigned char *a_sha, int a_mode,
+				 unsigned char *b_sha, int b_mode)
+{
+	handle_change_delete(o,
+			     path,
+			     o_sha, o_mode,
+			     a_sha, a_mode,
+			     b_sha, b_mode,
+			     "modify", "modified");
+}
+
+static int merge_content(struct merge_options *o,
+			 const char *path,
+			 unsigned char *o_sha, int o_mode,
+			 unsigned char *a_sha, int a_mode,
+			 unsigned char *b_sha, int b_mode,
+			 struct rename_conflict_info *rename_conflict_info)
+{
+	const char *reason = "content";
+	const char *path1 = NULL, *path2 = NULL;
+	struct merge_file_info mfi;
+	struct diff_filespec one, a, b;
+	unsigned df_conflict_remains = 0;
+
+	if (!o_sha) {
+		reason = "add/add";
+		o_sha = (unsigned char *)null_sha1;
+	}
+	one.path = a.path = b.path = (char *)path;
+	hashcpy(one.sha1, o_sha);
+	one.mode = o_mode;
+	hashcpy(a.sha1, a_sha);
+	a.mode = a_mode;
+	hashcpy(b.sha1, b_sha);
+	b.mode = b_mode;
+
+	if (rename_conflict_info) {
+		struct diff_filepair *pair1 = rename_conflict_info->pair1;
+
+		path1 = (o->branch1 == rename_conflict_info->branch1) ?
+			pair1->two->path : pair1->one->path;
+		/* If rename_conflict_info->pair2 != NULL, we are in
+		 * RENAME_ONE_FILE_TO_ONE case.  Otherwise, we have a
+		 * normal rename.
+		 */
+		path2 = (rename_conflict_info->pair2 ||
+			 o->branch2 == rename_conflict_info->branch1) ?
+			pair1->two->path : pair1->one->path;
+
+		if (dir_in_way(path, !o->call_depth))
+			df_conflict_remains = 1;
+	}
+	mfi = merge_file_special_markers(o, &one, &a, &b,
+					 o->branch1, path1,
+					 o->branch2, path2);
+
+	if (mfi.clean && !df_conflict_remains &&
+	    sha_eq(mfi.sha, a_sha) && mfi.mode == a_mode) {
+		int path_renamed_outside_HEAD;
+		output(o, 3, "Skipped %s (merged same as existing)", path);
+		/*
+		 * The content merge resulted in the same file contents we
+		 * already had.  We can return early if those file contents
+		 * are recorded at the correct path (which may not be true
+		 * if the merge involves a rename).
+		 */
+		path_renamed_outside_HEAD = !path2 || !strcmp(path, path2);
+		if (!path_renamed_outside_HEAD) {
+			add_cacheinfo(mfi.mode, mfi.sha, path,
+				      0, (!o->call_depth), 0);
+			return mfi.clean;
+		}
+	} else
+		output(o, 2, "Auto-merging %s", path);
+
+	if (!mfi.clean) {
+		if (S_ISGITLINK(mfi.mode))
+			reason = "submodule";
+		output(o, 1, "CONFLICT (%s): Merge conflict in %s",
+				reason, path);
+		if (rename_conflict_info && !df_conflict_remains)
+			update_stages(path, &one, &a, &b);
+	}
+
+	if (df_conflict_remains) {
+		char *new_path;
+		if (o->call_depth) {
+			remove_file_from_cache(path);
+		} else {
+			if (!mfi.clean)
+				update_stages(path, &one, &a, &b);
+			else {
+				int file_from_stage2 = was_tracked(path);
+				struct diff_filespec merged;
+				hashcpy(merged.sha1, mfi.sha);
+				merged.mode = mfi.mode;
+
+				update_stages(path, NULL,
+					      file_from_stage2 ? &merged : NULL,
+					      file_from_stage2 ? NULL : &merged);
+			}
+
+		}
+		new_path = unique_path(o, path, rename_conflict_info->branch1);
+		output(o, 1, "Adding as %s instead", new_path);
+		update_file(o, 0, mfi.sha, mfi.mode, new_path);
+		free(new_path);
+		mfi.clean = 0;
+	} else {
+		update_file(o, mfi.clean, mfi.sha, mfi.mode, path);
+	}
+	return mfi.clean;
+
+}
+
 /* Per entry merge function */
 static int process_entry(struct merge_options *o,
 			 const char *path, struct stage_data *entry)
@@ -1032,6 +1684,7 @@
 	print_index_entry("\tpath: ", entry);
 	*/
 	int clean_merge = 1;
+	int normalize = o->renormalize;
 	unsigned o_mode = entry->stages[1].mode;
 	unsigned a_mode = entry->stages[2].mode;
 	unsigned b_mode = entry->stages[3].mode;
@@ -1039,11 +1692,39 @@
 	unsigned char *a_sha = stage_sha(entry->stages[2].sha, a_mode);
 	unsigned char *b_sha = stage_sha(entry->stages[3].sha, b_mode);
 
-	if (o_sha && (!a_sha || !b_sha)) {
+	entry->processed = 1;
+	if (entry->rename_conflict_info) {
+		struct rename_conflict_info *conflict_info = entry->rename_conflict_info;
+		switch (conflict_info->rename_type) {
+		case RENAME_NORMAL:
+		case RENAME_ONE_FILE_TO_ONE:
+			clean_merge = merge_content(o, path,
+						    o_sha, o_mode, a_sha, a_mode, b_sha, b_mode,
+						    conflict_info);
+			break;
+		case RENAME_DELETE:
+			clean_merge = 0;
+			conflict_rename_delete(o, conflict_info->pair1,
+					       conflict_info->branch1,
+					       conflict_info->branch2);
+			break;
+		case RENAME_ONE_FILE_TO_TWO:
+			clean_merge = 0;
+			conflict_rename_rename_1to2(o, conflict_info);
+			break;
+		case RENAME_TWO_FILES_TO_ONE:
+			clean_merge = 0;
+			conflict_rename_rename_2to1(o, conflict_info);
+			break;
+		default:
+			entry->processed = 0;
+			break;
+		}
+	} else if (o_sha && (!a_sha || !b_sha)) {
 		/* Case A: Deleted in one */
 		if ((!a_sha && !b_sha) ||
-		    (sha_eq(a_sha, o_sha) && !b_sha) ||
-		    (!a_sha && sha_eq(b_sha, o_sha))) {
+		    (!b_sha && blob_unchanged(o_sha, a_sha, normalize, path)) ||
+		    (!a_sha && blob_unchanged(o_sha, b_sha, normalize, path))) {
 			/* Deleted in both or deleted in one and
 			 * unchanged in the other */
 			if (a_sha)
@@ -1051,26 +1732,16 @@
 			/* do not touch working file if it did not exist */
 			remove_file(o, 1, path, !a_sha);
 		} else {
-			/* Deleted in one and changed in the other */
+			/* Modify/delete; deleted side may have put a directory in the way */
 			clean_merge = 0;
-			if (!a_sha) {
-				output(o, 1, "CONFLICT (delete/modify): %s deleted in %s "
-				       "and modified in %s. Version %s of %s left in tree.",
-				       path, o->branch1,
-				       o->branch2, o->branch2, path);
-				update_file(o, 0, b_sha, b_mode, path);
-			} else {
-				output(o, 1, "CONFLICT (delete/modify): %s deleted in %s "
-				       "and modified in %s. Version %s of %s left in tree.",
-				       path, o->branch2,
-				       o->branch1, o->branch1, path);
-				update_file(o, 0, a_sha, a_mode, path);
-			}
+			handle_modify_delete(o, path, o_sha, o_mode,
+					     a_sha, a_mode, b_sha, b_mode);
 		}
-
 	} else if ((!o_sha && a_sha && !b_sha) ||
 		   (!o_sha && !a_sha && b_sha)) {
 		/* Case B: Added in one. */
+		/* [nothing|directory] -> ([nothing|directory], file) */
+
 		const char *add_branch;
 		const char *other_branch;
 		unsigned mode;
@@ -1090,49 +1761,29 @@
 			sha = b_sha;
 			conf = "directory/file";
 		}
-		if (string_list_has_string(&o->current_directory_set, path)) {
-			const char *new_path = unique_path(o, path, add_branch);
+		if (dir_in_way(path, !o->call_depth)) {
+			char *new_path = unique_path(o, path, add_branch);
 			clean_merge = 0;
 			output(o, 1, "CONFLICT (%s): There is a directory with name %s in %s. "
 			       "Adding %s as %s",
 			       conf, path, other_branch, path, new_path);
-			remove_file(o, 0, path, 0);
+			if (o->call_depth)
+				remove_file_from_cache(path);
 			update_file(o, 0, sha, mode, new_path);
+			if (o->call_depth)
+				remove_file_from_cache(path);
+			free(new_path);
 		} else {
 			output(o, 2, "Adding %s", path);
-			update_file(o, 1, sha, mode, path);
+			/* do not overwrite file if already present */
+			update_file_flags(o, sha, mode, path, 1, !a_sha);
 		}
 	} else if (a_sha && b_sha) {
 		/* Case C: Added in both (check for same permissions) and */
 		/* case D: Modified in both, but differently. */
-		const char *reason = "content";
-		struct merge_file_info mfi;
-		struct diff_filespec one, a, b;
-
-		if (!o_sha) {
-			reason = "add/add";
-			o_sha = (unsigned char *)null_sha1;
-		}
-		output(o, 2, "Auto-merging %s", path);
-		one.path = a.path = b.path = (char *)path;
-		hashcpy(one.sha1, o_sha);
-		one.mode = o_mode;
-		hashcpy(a.sha1, a_sha);
-		a.mode = a_mode;
-		hashcpy(b.sha1, b_sha);
-		b.mode = b_mode;
-
-		mfi = merge_file(o, &one, &a, &b,
-				 o->branch1, o->branch2);
-
-		clean_merge = mfi.clean;
-		if (!mfi.clean) {
-			if (S_ISGITLINK(mfi.mode))
-				reason = "submodule";
-			output(o, 1, "CONFLICT (%s): Merge conflict in %s",
-					reason, path);
-		}
-		update_file(o, mfi.clean, mfi.sha, mfi.mode, path);
+		clean_merge = merge_content(o, path,
+					    o_sha, o_mode, a_sha, a_mode, b_sha, b_mode,
+					    NULL);
 	} else if (!o_sha && !a_sha && !b_sha) {
 		/*
 		 * this entry was deleted altogether. a_mode == 0 means
@@ -1153,23 +1804,27 @@
 {
 	int code, clean;
 
-	if (o->subtree_merge) {
-		merge = shift_tree_object(head, merge);
-		common = shift_tree_object(head, common);
+	if (o->subtree_shift) {
+		merge = shift_tree_object(head, merge, o->subtree_shift);
+		common = shift_tree_object(head, common, o->subtree_shift);
 	}
 
 	if (sha_eq(common->object.sha1, merge->object.sha1)) {
-		output(o, 0, "Already uptodate!");
+		output(o, 0, "Already up-to-date!");
 		*result = head;
 		return 1;
 	}
 
 	code = git_merge_trees(o->call_depth, common, head, merge);
 
-	if (code != 0)
-		die("merging of trees %s and %s failed",
-		    sha1_to_hex(head->object.sha1),
-		    sha1_to_hex(merge->object.sha1));
+	if (code != 0) {
+		if (show(o, 4) || o->call_depth)
+			die("merging of trees %s and %s failed",
+			    sha1_to_hex(head->object.sha1),
+			    sha1_to_hex(merge->object.sha1));
+		else
+			exit(128);
+	}
 
 	if (unmerged_cache()) {
 		struct string_list *entries, *re_head, *re_merge;
@@ -1180,16 +1835,23 @@
 		get_files_dirs(o, merge);
 
 		entries = get_unmerged();
+		record_df_conflict_files(o, entries);
 		re_head  = get_renames(o, head, common, head, merge, entries);
 		re_merge = get_renames(o, merge, common, head, merge, entries);
 		clean = process_renames(o, re_head, re_merge);
-		for (i = 0; i < entries->nr; i++) {
+		for (i = entries->nr-1; 0 <= i; i--) {
 			const char *path = entries->items[i].string;
 			struct stage_data *e = entries->items[i].util;
 			if (!e->processed
 				&& !process_entry(o, path, e))
 				clean = 0;
 		}
+		for (i = 0; i < entries->nr; i++) {
+			struct stage_data *e = entries->items[i].util;
+			if (!e->processed)
+				die("Unprocessed path??? %s",
+				    entries->items[i].string);
+		}
 
 		string_list_clear(re_merge, 0);
 		string_list_clear(re_head, 0);
@@ -1250,12 +1912,10 @@
 
 	merged_common_ancestors = pop_commit(&ca);
 	if (merged_common_ancestors == NULL) {
-		/* if there is no common ancestor, make an empty tree */
-		struct tree *tree = xcalloc(1, sizeof(struct tree));
+		/* if there is no common ancestor, use an empty tree */
+		struct tree *tree;
 
-		tree->object.parsed = 1;
-		tree->object.type = OBJ_TREE;
-		pretend_sha1_file(NULL, 0, OBJ_TREE, tree->object.sha1);
+		tree = lookup_tree(EMPTY_TREE_SHA1_BIN);
 		merged_common_ancestors = make_virtual_commit(tree, "ancestor");
 	}
 
@@ -1288,6 +1948,7 @@
 	if (!o->call_depth)
 		read_cache();
 
+	o->ancestor = "merged common ancestors";
 	clean = merge_trees(o, h1->tree, h2->tree, merged_common_ancestors->tree,
 			    &mrtree);
 
@@ -1297,6 +1958,9 @@
 		commit_list_insert(h2, &(*result)->parents->next);
 	}
 	flush_output(o);
+	if (show(o, 2))
+		diff_warn_rename_limit("merge.renamelimit",
+				       o->needed_rename_limit, 0);
 	return clean;
 }
 
@@ -1354,15 +2018,15 @@
 static int merge_recursive_config(const char *var, const char *value, void *cb)
 {
 	struct merge_options *o = cb;
-	if (!strcasecmp(var, "merge.verbosity")) {
+	if (!strcmp(var, "merge.verbosity")) {
 		o->verbosity = git_config_int(var, value);
 		return 0;
 	}
-	if (!strcasecmp(var, "diff.renamelimit")) {
+	if (!strcmp(var, "diff.renamelimit")) {
 		o->diff_rename_limit = git_config_int(var, value);
 		return 0;
 	}
-	if (!strcasecmp(var, "merge.renamelimit")) {
+	if (!strcmp(var, "merge.renamelimit")) {
 		o->merge_rename_limit = git_config_int(var, value);
 		return 0;
 	}
@@ -1376,6 +2040,7 @@
 	o->buffer_output = 1;
 	o->diff_rename_limit = -1;
 	o->merge_rename_limit = -1;
+	o->renormalize = 0;
 	git_config(merge_recursive_config, o);
 	if (getenv("GIT_MERGE_VERBOSITY"))
 		o->verbosity =
@@ -1387,4 +2052,42 @@
 	o->current_file_set.strdup_strings = 1;
 	memset(&o->current_directory_set, 0, sizeof(struct string_list));
 	o->current_directory_set.strdup_strings = 1;
+	memset(&o->df_conflict_file_set, 0, sizeof(struct string_list));
+	o->df_conflict_file_set.strdup_strings = 1;
+}
+
+int parse_merge_opt(struct merge_options *o, const char *s)
+{
+	if (!s || !*s)
+		return -1;
+	if (!strcmp(s, "ours"))
+		o->recursive_variant = MERGE_RECURSIVE_OURS;
+	else if (!strcmp(s, "theirs"))
+		o->recursive_variant = MERGE_RECURSIVE_THEIRS;
+	else if (!strcmp(s, "subtree"))
+		o->subtree_shift = "";
+	else if (!prefixcmp(s, "subtree="))
+		o->subtree_shift = s + strlen("subtree=");
+	else if (!strcmp(s, "patience"))
+		o->xdl_opts = DIFF_WITH_ALG(o, PATIENCE_DIFF);
+	else if (!strcmp(s, "histogram"))
+		o->xdl_opts = DIFF_WITH_ALG(o, HISTOGRAM_DIFF);
+	else if (!strcmp(s, "ignore-space-change"))
+		o->xdl_opts |= XDF_IGNORE_WHITESPACE_CHANGE;
+	else if (!strcmp(s, "ignore-all-space"))
+		o->xdl_opts |= XDF_IGNORE_WHITESPACE;
+	else if (!strcmp(s, "ignore-space-at-eol"))
+		o->xdl_opts |= XDF_IGNORE_WHITESPACE_AT_EOL;
+	else if (!strcmp(s, "renormalize"))
+		o->renormalize = 1;
+	else if (!strcmp(s, "no-renormalize"))
+		o->renormalize = 0;
+	else if (!prefixcmp(s, "rename-threshold=")) {
+		const char *score = s + strlen("rename-threshold=");
+		if ((o->rename_score = parse_rename_score(&score)) == -1 || *score != 0)
+			return -1;
+	}
+	else
+		return -1;
+	return 0;
 }
diff --git a/merge-recursive.h b/merge-recursive.h
index fd138ca..58f3435 100644
--- a/merge-recursive.h
+++ b/merge-recursive.h
@@ -4,17 +4,29 @@
 #include "string-list.h"
 
 struct merge_options {
+	const char *ancestor;
 	const char *branch1;
 	const char *branch2;
-	unsigned subtree_merge : 1;
+	enum {
+		MERGE_RECURSIVE_NORMAL = 0,
+		MERGE_RECURSIVE_OURS,
+		MERGE_RECURSIVE_THEIRS
+	} recursive_variant;
+	const char *subtree_shift;
 	unsigned buffer_output : 1;
+	unsigned renormalize : 1;
+	long xdl_opts;
 	int verbosity;
 	int diff_rename_limit;
 	int merge_rename_limit;
+	int rename_score;
+	int needed_rename_limit;
+	int show_rename_progress;
 	int call_depth;
 	struct strbuf obuf;
 	struct string_list current_file_set;
 	struct string_list current_directory_set;
+	struct string_list df_conflict_file_set;
 };
 
 /* merge_trees() but with recursive ancestor consolidation */
@@ -45,4 +57,11 @@
 void init_merge_options(struct merge_options *o);
 struct tree *write_tree_from_memory(struct merge_options *o);
 
+int parse_merge_opt(struct merge_options *out, const char *s);
+
+/* builtin/merge.c */
+int try_merge_command(const char *strategy, size_t xopts_nr,
+		const char **xopts, struct commit_list *common,
+		const char *head_arg, struct commit_list *remotes);
+
 #endif
diff --git a/merge-tree.c b/merge-tree.c
deleted file mode 100644
index f01e7c8..0000000
--- a/merge-tree.c
+++ /dev/null
@@ -1,362 +0,0 @@
-#include "cache.h"
-#include "tree-walk.h"
-#include "xdiff-interface.h"
-#include "blob.h"
-#include "exec_cmd.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)
-{
-	enum object_type type;
-	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)
-{
-	enum object_type type;
-	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;
-	memset(&xecfg, 0, sizeof(xecfg));
-	xecfg.ctxlen = 3;
-	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;
-	xdi_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)
-{
-	return	a->sha1 &&
-		b->sha1 &&
-		!hashcmp(a->sha1, b->sha1) &&
-		a->mode == b->mode;
-}
-
-static struct merge_list *create_entry(unsigned stage, unsigned mode, const unsigned char *sha1, const char *path)
-{
-	struct merge_list *res = xcalloc(1, sizeof(*res));
-
-	res->stage = stage;
-	res->path = path;
-	res->mode = mode;
-	res->blob = lookup_blob(sha1);
-	return res;
-}
-
-static char *traverse_path(const struct traverse_info *info, const struct name_entry *n)
-{
-	char *path = xmalloc(traverse_path_len(info, n) + 1);
-	return make_traverse_path(path, info, n);
-}
-
-static void resolve(const struct traverse_info *info, 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;
-
-	path = traverse_path(info, result);
-	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 struct traverse_info *info, struct name_entry n[3])
-{
-	char *newbase;
-	struct name_entry *p;
-	struct tree_desc t[3];
-	void *buf0, *buf1, *buf2;
-
-	if (!resolve_directories)
-		return 0;
-	p = n;
-	if (!p->mode) {
-		p++;
-		if (!p->mode)
-			p++;
-	}
-	if (!S_ISDIR(p->mode))
-		return 0;
-	newbase = traverse_path(info, p);
-	buf0 = fill_tree_descriptor(t+0, n[0].sha1);
-	buf1 = fill_tree_descriptor(t+1, n[1].sha1);
-	buf2 = fill_tree_descriptor(t+2, n[2].sha1);
-	merge_trees(t, newbase);
-
-	free(buf0);
-	free(buf1);
-	free(buf2);
-	free(newbase);
-	return 1;
-}
-
-
-static struct merge_list *link_entry(unsigned stage, const struct traverse_info *info, 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 = traverse_path(info, n);
-	link = create_entry(stage, n->mode, n->sha1, path);
-	link->link = entry;
-	return link;
-}
-
-static void unresolved(const struct traverse_info *info, struct name_entry n[3])
-{
-	struct merge_list *entry = NULL;
-
-	if (unresolved_directory(info, n))
-		return;
-
-	/*
-	 * 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, info, n + 2, entry);
-	entry = link_entry(2, info, n + 1, entry);
-	entry = link_entry(1, info, n + 0, entry);
-
-	add_merge_entry(entry);
-}
-
-/*
- * Merge two trees together (t[1] and t[2]), using a common base (t[0])
- * as the origin.
- *
- * This walks the (sorted) trees in lock-step, checking every possible
- * name. Note that directories automatically sort differently from other
- * files (see "base_name_compare"), so you'll never see file/directory
- * conflicts, because they won't ever compare the same.
- *
- * IOW, if a directory changes to a filename, it will automatically be
- * seen as the directory going away, and the filename being created.
- *
- * Think of this as a three-way diff.
- *
- * The output will be either:
- *  - successful merge
- *	 "0 mode sha1 filename"
- *    NOTE NOTE NOTE! FIXME! We really really need to walk the index
- *    in parallel with this too!
- *
- *  - conflict:
- *	"1 mode sha1 filename"
- *	"2 mode sha1 filename"
- *	"3 mode sha1 filename"
- *    where not all of the 1/2/3 lines may exist, of course.
- *
- * The successful merge rules are the same as for the three-way merge
- * in git-read-tree.
- */
-static int threeway_callback(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *info)
-{
-	/* Same in both? */
-	if (same_entry(entry+1, entry+2)) {
-		if (entry[0].sha1) {
-			resolve(info, NULL, entry+1);
-			return mask;
-		}
-	}
-
-	if (same_entry(entry+0, entry+1)) {
-		if (entry[2].sha1 && !S_ISDIR(entry[2].mode)) {
-			resolve(info, entry+1, entry+2);
-			return mask;
-		}
-	}
-
-	if (same_entry(entry+0, entry+2)) {
-		if (entry[1].sha1 && !S_ISDIR(entry[1].mode)) {
-			resolve(info, NULL, entry+1);
-			return mask;
-		}
-	}
-
-	unresolved(info, entry);
-	return mask;
-}
-
-static void merge_trees(struct tree_desc t[3], const char *base)
-{
-	struct traverse_info info;
-
-	setup_traverse_info(&info, base);
-	info.fn = threeway_callback;
-	traverse_trees(3, t, &info);
-}
-
-static void *get_tree_descriptor(struct tree_desc *desc, const char *rev)
-{
-	unsigned char sha1[20];
-	void *buf;
-
-	if (get_sha1(rev, sha1))
-		die("unknown rev %s", rev);
-	buf = fill_tree_descriptor(desc, sha1);
-	if (!buf)
-		die("%s is not a tree", rev);
-	return buf;
-}
-
-int main(int argc, char **argv)
-{
-	struct tree_desc t[3];
-	void *buf1, *buf2, *buf3;
-
-	if (argc != 4)
-		usage(merge_tree_usage);
-
-	git_extract_argv0_path(argv[0]);
-
-	setup_git_directory();
-
-	buf1 = get_tree_descriptor(t+0, argv[1]);
-	buf2 = get_tree_descriptor(t+1, argv[2]);
-	buf3 = get_tree_descriptor(t+2, argv[3]);
-	merge_trees(t, "");
-	free(buf1);
-	free(buf2);
-	free(buf3);
-
-	show_result();
-	return 0;
-}
diff --git a/mergesort.c b/mergesort.c
new file mode 100644
index 0000000..e5fdf2e
--- /dev/null
+++ b/mergesort.c
@@ -0,0 +1,73 @@
+#include "cache.h"
+#include "mergesort.h"
+
+struct mergesort_sublist {
+	void *ptr;
+	unsigned long len;
+};
+
+static void *get_nth_next(void *list, unsigned long n,
+			  void *(*get_next_fn)(const void *))
+{
+	while (n-- && list)
+		list = get_next_fn(list);
+	return list;
+}
+
+static void *pop_item(struct mergesort_sublist *l,
+		      void *(*get_next_fn)(const void *))
+{
+	void *p = l->ptr;
+	l->ptr = get_next_fn(l->ptr);
+	l->len = l->ptr ? (l->len - 1) : 0;
+	return p;
+}
+
+void *llist_mergesort(void *list,
+		      void *(*get_next_fn)(const void *),
+		      void (*set_next_fn)(void *, void *),
+		      int (*compare_fn)(const void *, const void *))
+{
+	unsigned long l;
+
+	if (!list)
+		return NULL;
+	for (l = 1; ; l *= 2) {
+		void *curr;
+		struct mergesort_sublist p, q;
+
+		p.ptr = list;
+		q.ptr = get_nth_next(p.ptr, l, get_next_fn);
+		if (!q.ptr)
+			break;
+		p.len = q.len = l;
+
+		if (compare_fn(p.ptr, q.ptr) > 0)
+			list = curr = pop_item(&q, get_next_fn);
+		else
+			list = curr = pop_item(&p, get_next_fn);
+
+		while (p.ptr) {
+			while (p.len || q.len) {
+				void *prev = curr;
+
+				if (!p.len)
+					curr = pop_item(&q, get_next_fn);
+				else if (!q.len)
+					curr = pop_item(&p, get_next_fn);
+				else if (compare_fn(p.ptr, q.ptr) > 0)
+					curr = pop_item(&q, get_next_fn);
+				else
+					curr = pop_item(&p, get_next_fn);
+				set_next_fn(prev, curr);
+			}
+			p.ptr = q.ptr;
+			p.len = l;
+			q.ptr = get_nth_next(p.ptr, l, get_next_fn);
+			q.len = q.ptr ? l : 0;
+
+		}
+		set_next_fn(curr, NULL);
+	}
+	return list;
+}
diff --git a/mergesort.h b/mergesort.h
new file mode 100644
index 0000000..644cff1
--- /dev/null
+++ b/mergesort.h
@@ -0,0 +1,17 @@
+#ifndef MERGESORT_H
+#define MERGESORT_H
+
+/*
+ * Sort linked list in place.
+ * - get_next_fn() returns the next element given an element of a linked list.
+ * - set_next_fn() takes two elements A and B, and makes B the "next" element
+ *   of A on the list.
+ * - compare_fn() takes two elements A and B, and returns negative, 0, positive
+ *   as the same sign as "subtracting" B from A.
+ */
+void *llist_mergesort(void *list,
+		      void *(*get_next_fn)(const void *),
+		      void (*set_next_fn)(void *, void *),
+		      int (*compare_fn)(const void *, const void *));
+
+#endif
diff --git a/mergetools/araxis b/mergetools/araxis
new file mode 100644
index 0000000..64f97c5
--- /dev/null
+++ b/mergetools/araxis
@@ -0,0 +1,20 @@
+diff_cmd () {
+	"$merge_tool_path" -wait -2 "$LOCAL" "$REMOTE" >/dev/null 2>&1
+}
+
+merge_cmd () {
+	touch "$BACKUP"
+	if $base_present
+	then
+		"$merge_tool_path" -wait -merge -3 -a1 \
+			"$BASE" "$LOCAL" "$REMOTE" "$MERGED" >/dev/null 2>&1
+	else
+		"$merge_tool_path" -wait -2 \
+			"$LOCAL" "$REMOTE" "$MERGED" >/dev/null 2>&1
+	fi
+	check_unchanged
+}
+
+translate_merge_tool_path() {
+	echo compare
+}
diff --git a/mergetools/bc3 b/mergetools/bc3
new file mode 100644
index 0000000..b6319d2
--- /dev/null
+++ b/mergetools/bc3
@@ -0,0 +1,25 @@
+diff_cmd () {
+	"$merge_tool_path" "$LOCAL" "$REMOTE"
+}
+
+merge_cmd () {
+	touch "$BACKUP"
+	if $base_present
+	then
+		"$merge_tool_path" "$LOCAL" "$REMOTE" "$BASE" \
+			-mergeoutput="$MERGED"
+	else
+		"$merge_tool_path" "$LOCAL" "$REMOTE" \
+			-mergeoutput="$MERGED"
+	fi
+	check_unchanged
+}
+
+translate_merge_tool_path() {
+	if type bcomp >/dev/null 2>/dev/null
+	then
+		echo bcomp
+	else
+		echo bcompare
+	fi
+}
diff --git a/mergetools/defaults b/mergetools/defaults
new file mode 100644
index 0000000..1d8f2a3
--- /dev/null
+++ b/mergetools/defaults
@@ -0,0 +1,46 @@
+# Redefined by builtin tools
+can_merge () {
+	return 0
+}
+
+can_diff () {
+	return 0
+}
+
+diff_cmd () {
+	merge_tool_cmd="$(get_merge_tool_cmd "$1")"
+	if test -z "$merge_tool_cmd"
+	then
+		status=1
+		break
+	fi
+	( eval $merge_tool_cmd )
+	status=$?
+	return $status
+}
+
+merge_cmd () {
+	merge_tool_cmd="$(get_merge_tool_cmd "$1")"
+	if test -z "$merge_tool_cmd"
+	then
+		status=1
+		break
+	fi
+	trust_exit_code="$(git config --bool \
+		mergetool."$1".trustExitCode || echo false)"
+	if test "$trust_exit_code" = "false"
+	then
+		touch "$BACKUP"
+		( eval $merge_tool_cmd )
+		status=$?
+		check_unchanged
+	else
+		( eval $merge_tool_cmd )
+		status=$?
+	fi
+	return $status
+}
+
+translate_merge_tool_path () {
+	echo "$1"
+}
diff --git a/mergetools/deltawalker b/mergetools/deltawalker
new file mode 100644
index 0000000..b3c71b6
--- /dev/null
+++ b/mergetools/deltawalker
@@ -0,0 +1,21 @@
+diff_cmd () {
+	"$merge_tool_path" "$LOCAL" "$REMOTE" >/dev/null 2>&1
+}
+
+merge_cmd () {
+	# Adding $(pwd)/ in front of $MERGED should not be necessary.
+	# However without it, DeltaWalker (at least v1.9.8 on Windows)
+	# crashes with a JRE exception.  The DeltaWalker user manual,
+	# shows $(pwd)/ whenever the '-merged' options is given.
+	# Adding it here seems to work around the problem.
+	if $base_present
+	then
+		"$merge_tool_path" "$LOCAL" "$REMOTE" "$BASE" -merged="$(pwd)/$MERGED"
+	else
+		"$merge_tool_path" "$LOCAL" "$REMOTE" -merged="$(pwd)/$MERGED"
+	fi >/dev/null 2>&1
+}
+
+translate_merge_tool_path() {
+	echo DeltaWalker
+}
diff --git a/mergetools/diffuse b/mergetools/diffuse
new file mode 100644
index 0000000..02e0843
--- /dev/null
+++ b/mergetools/diffuse
@@ -0,0 +1,17 @@
+diff_cmd () {
+	"$merge_tool_path" "$LOCAL" "$REMOTE" | cat
+}
+
+merge_cmd () {
+	touch "$BACKUP"
+	if $base_present
+	then
+		"$merge_tool_path" \
+			"$LOCAL" "$MERGED" "$REMOTE" \
+			"$BASE" | cat
+	else
+		"$merge_tool_path" \
+			"$LOCAL" "$MERGED" "$REMOTE" | cat
+	fi
+	check_unchanged
+}
diff --git a/mergetools/ecmerge b/mergetools/ecmerge
new file mode 100644
index 0000000..13c2e43
--- /dev/null
+++ b/mergetools/ecmerge
@@ -0,0 +1,16 @@
+diff_cmd () {
+	"$merge_tool_path" --default --mode=diff2 "$LOCAL" "$REMOTE"
+}
+
+merge_cmd () {
+	touch "$BACKUP"
+	if $base_present
+	then
+		"$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" \
+			--default --mode=merge3 --to="$MERGED"
+	else
+		"$merge_tool_path" "$LOCAL" "$REMOTE" \
+			--default --mode=merge2 --to="$MERGED"
+	fi
+	check_unchanged
+}
diff --git a/mergetools/emerge b/mergetools/emerge
new file mode 100644
index 0000000..f96d9e5
--- /dev/null
+++ b/mergetools/emerge
@@ -0,0 +1,23 @@
+diff_cmd () {
+	"$merge_tool_path" -f emerge-files-command "$LOCAL" "$REMOTE"
+}
+
+merge_cmd () {
+	if $base_present
+	then
+		"$merge_tool_path" \
+			-f emerge-files-with-ancestor-command \
+			"$LOCAL" "$REMOTE" "$BASE" \
+			"$(basename "$MERGED")"
+	else
+		"$merge_tool_path" \
+			-f emerge-files-command \
+			"$LOCAL" "$REMOTE" \
+			"$(basename "$MERGED")"
+	fi
+	status=$?
+}
+
+translate_merge_tool_path() {
+	echo emacs
+}
diff --git a/mergetools/kdiff3 b/mergetools/kdiff3
new file mode 100644
index 0000000..28fead4
--- /dev/null
+++ b/mergetools/kdiff3
@@ -0,0 +1,24 @@
+diff_cmd () {
+	"$merge_tool_path" --auto \
+		--L1 "$MERGED (A)" --L2 "$MERGED (B)" \
+		"$LOCAL" "$REMOTE" >/dev/null 2>&1
+}
+
+merge_cmd () {
+	if $base_present
+	then
+		"$merge_tool_path" --auto \
+			--L1 "$MERGED (Base)" \
+			--L2 "$MERGED (Local)" \
+			--L3 "$MERGED (Remote)" \
+			-o "$MERGED" "$BASE" "$LOCAL" "$REMOTE" \
+		>/dev/null 2>&1
+	else
+		"$merge_tool_path" --auto \
+			--L1 "$MERGED (Local)" \
+			--L2 "$MERGED (Remote)" \
+			-o "$MERGED" "$LOCAL" "$REMOTE" \
+		>/dev/null 2>&1
+	fi
+	status=$?
+}
diff --git a/mergetools/kompare b/mergetools/kompare
new file mode 100644
index 0000000..433686c
--- /dev/null
+++ b/mergetools/kompare
@@ -0,0 +1,7 @@
+can_merge () {
+	return 1
+}
+
+diff_cmd () {
+	"$merge_tool_path" "$LOCAL" "$REMOTE"
+}
diff --git a/mergetools/meld b/mergetools/meld
new file mode 100644
index 0000000..cb672a5
--- /dev/null
+++ b/mergetools/meld
@@ -0,0 +1,32 @@
+diff_cmd () {
+	"$merge_tool_path" "$LOCAL" "$REMOTE"
+}
+
+merge_cmd () {
+	if test -z "${meld_has_output_option:+set}"
+	then
+		check_meld_for_output_version
+	fi
+	touch "$BACKUP"
+	if test "$meld_has_output_option" = true
+	then
+		"$merge_tool_path" --output "$MERGED" \
+			"$LOCAL" "$BASE" "$REMOTE"
+	else
+		"$merge_tool_path" "$LOCAL" "$MERGED" "$REMOTE"
+	fi
+	check_unchanged
+}
+
+# Check whether 'meld --output <file>' is supported
+check_meld_for_output_version () {
+	meld_path="$(git config mergetool.meld.path)"
+	meld_path="${meld_path:-meld}"
+
+	if "$meld_path" --help 2>&1 | grep -e --output >/dev/null
+	then
+		meld_has_output_option=true
+	else
+		meld_has_output_option=false
+	fi
+}
diff --git a/mergetools/opendiff b/mergetools/opendiff
new file mode 100644
index 0000000..0942b2a
--- /dev/null
+++ b/mergetools/opendiff
@@ -0,0 +1,16 @@
+diff_cmd () {
+	"$merge_tool_path" "$LOCAL" "$REMOTE" | cat
+}
+
+merge_cmd () {
+	touch "$BACKUP"
+	if $base_present
+	then
+		"$merge_tool_path" "$LOCAL" "$REMOTE" \
+			-ancestor "$BASE" -merge "$MERGED" | cat
+	else
+		"$merge_tool_path" "$LOCAL" "$REMOTE" \
+			-merge "$MERGED" | cat
+	fi
+	check_unchanged
+}
diff --git a/mergetools/p4merge b/mergetools/p4merge
new file mode 100644
index 0000000..1a45c1b
--- /dev/null
+++ b/mergetools/p4merge
@@ -0,0 +1,10 @@
+diff_cmd () {
+	"$merge_tool_path" "$LOCAL" "$REMOTE"
+}
+
+merge_cmd () {
+	touch "$BACKUP"
+	$base_present || >"$BASE"
+	"$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" "$MERGED"
+	check_unchanged
+}
diff --git a/mergetools/tkdiff b/mergetools/tkdiff
new file mode 100644
index 0000000..618c438
--- /dev/null
+++ b/mergetools/tkdiff
@@ -0,0 +1,12 @@
+diff_cmd () {
+	"$merge_tool_path" "$LOCAL" "$REMOTE"
+}
+
+merge_cmd () {
+	if $base_present
+	then
+		"$merge_tool_path" -a "$BASE" -o "$MERGED" "$LOCAL" "$REMOTE"
+	else
+		"$merge_tool_path" -o "$MERGED" "$LOCAL" "$REMOTE"
+	fi
+}
diff --git a/mergetools/tortoisemerge b/mergetools/tortoisemerge
new file mode 100644
index 0000000..ed7db49
--- /dev/null
+++ b/mergetools/tortoisemerge
@@ -0,0 +1,17 @@
+can_diff () {
+	return 1
+}
+
+merge_cmd () {
+	if $base_present
+	then
+		touch "$BACKUP"
+		"$merge_tool_path" \
+			-base:"$BASE" -mine:"$LOCAL" \
+			-theirs:"$REMOTE" -merged:"$MERGED"
+		check_unchanged
+	else
+		echo "TortoiseMerge cannot be used without a base" 1>&2
+		return 1
+	fi
+}
diff --git a/mergetools/vim b/mergetools/vim
new file mode 100644
index 0000000..619594a
--- /dev/null
+++ b/mergetools/vim
@@ -0,0 +1,44 @@
+diff_cmd () {
+	case "$1" in
+	gvimdiff|vimdiff)
+		"$merge_tool_path" -R -f -d \
+			-c 'wincmd l' -c 'cd $GIT_PREFIX' "$LOCAL" "$REMOTE"
+		;;
+	gvimdiff2|vimdiff2)
+		"$merge_tool_path" -R -f -d \
+			-c 'wincmd l' -c 'cd $GIT_PREFIX' "$LOCAL" "$REMOTE"
+		;;
+	esac
+}
+
+merge_cmd () {
+	touch "$BACKUP"
+	case "$1" in
+	gvimdiff|vimdiff)
+		if $base_present
+		then
+			"$merge_tool_path" -f -d -c 'wincmd J' \
+				"$MERGED" "$LOCAL" "$BASE" "$REMOTE"
+		else
+			"$merge_tool_path" -f -d -c 'wincmd l' \
+				"$LOCAL" "$MERGED" "$REMOTE"
+		fi
+		;;
+	gvimdiff2|vimdiff2)
+		"$merge_tool_path" -f -d -c 'wincmd l' \
+			"$LOCAL" "$MERGED" "$REMOTE"
+		;;
+	esac
+	check_unchanged
+}
+
+translate_merge_tool_path() {
+	case "$1" in
+	gvimdiff|gvimdiff2)
+		echo gvim
+		;;
+	vimdiff|vimdiff2)
+		echo vim
+		;;
+	esac
+}
diff --git a/mergetools/xxdiff b/mergetools/xxdiff
new file mode 100644
index 0000000..05b4433
--- /dev/null
+++ b/mergetools/xxdiff
@@ -0,0 +1,25 @@
+diff_cmd () {
+	"$merge_tool_path" \
+		-R 'Accel.Search: "Ctrl+F"' \
+		-R 'Accel.SearchForward: "Ctrl-G"' \
+		"$LOCAL" "$REMOTE"
+}
+
+merge_cmd () {
+	touch "$BACKUP"
+	if $base_present
+	then
+		"$merge_tool_path" -X --show-merged-pane \
+			-R 'Accel.SaveAsMerged: "Ctrl-S"' \
+			-R 'Accel.Search: "Ctrl+F"' \
+			-R 'Accel.SearchForward: "Ctrl-G"' \
+			--merged-file "$MERGED" "$LOCAL" "$BASE" "$REMOTE"
+	else
+		"$merge_tool_path" -X $extra \
+			-R 'Accel.SaveAsMerged: "Ctrl-S"' \
+			-R 'Accel.Search: "Ctrl+F"' \
+			-R 'Accel.SearchForward: "Ctrl-G"' \
+			--merged-file "$MERGED" "$LOCAL" "$REMOTE"
+	fi
+	check_unchanged
+}
diff --git a/mktag.c b/mktag.c
deleted file mode 100644
index a609e3e..0000000
--- a/mktag.c
+++ /dev/null
@@ -1,182 +0,0 @@
-#include "cache.h"
-#include "tag.h"
-#include "exec_cmd.h"
-
-/*
- * 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 four lines are guaranteed to be at least 83 bytes:
- * "object <sha1>\n" is 48 bytes, "type tag\n" at 9 bytes is the
- * shortest possible type-line, "tag .\n" at 6 bytes is the shortest
- * single-character-tag line, and "tagger . <> 0 +0000\n" at 20 bytes is
- * the shortest possible tagger-line.
- */
-
-/*
- * We refuse to tag something we can't verify. Just because.
- */
-static int verify_object(unsigned char *sha1, const char *expected_type)
-{
-	int ret = -1;
-	enum object_type type;
-	unsigned long size;
-	void *buffer = read_sha1_file(sha1, &type, &size);
-
-	if (buffer) {
-		if (type == type_from_string(expected_type))
-			ret = check_sha1_signature(sha1, buffer, size, expected_type);
-		free(buffer);
-	}
-	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;
-	char type[20];
-	unsigned char sha1[20];
-	const char *object, *type_line, *tag_line, *tagger_line, *lb, *rb;
-	size_t len;
-
-	if (size < 84)
-		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 \"", 0);
-
-	if (get_sha1_hex(object + 7, sha1))
-		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 \"", 47);
-
-	/* Verify tag-line */
-	tag_line = strchr(type_line, '\n');
-	if (!tag_line)
-		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" 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" PD_FMT ": type too long", type_line+5 - buffer);
-
-	memcpy(type, type_line+5, typelen);
-	type[typelen] = 0;
-
-	/* Verify that the object matches */
-	if (verify_object(sha1, type))
-		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;
-	for (;;) {
-		unsigned char c = *tag_line++;
-		if (c == '\n')
-			break;
-		if (c > ' ')
-			continue;
-		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 ", 7))
-		return error("char" PD_FMT ": could not find \"tagger \"",
-			tagger_line - buffer);
-
-	/*
-	 * Check for correct form for name and email
-	 * i.e. " <" followed by "> " on _this_ line
-	 * No angle brackets within the name or email address fields.
-	 * No spaces within the email address field.
-	 */
-	tagger_line += 7;
-	if (!(lb = strstr(tagger_line, " <")) || !(rb = strstr(lb+2, "> ")) ||
-		strpbrk(tagger_line, "<>\n") != lb+1 ||
-		strpbrk(lb+2, "><\n ") != rb)
-		return error("char" PD_FMT ": malformed tagger field",
-			tagger_line - buffer);
-
-	/* Check for author name, at least one character, space is acceptable */
-	if (lb == tagger_line)
-		return error("char" PD_FMT ": missing tagger name",
-			tagger_line - buffer);
-
-	/* timestamp, 1 or more digits followed by space */
-	tagger_line = rb + 2;
-	if (!(len = strspn(tagger_line, "0123456789")))
-		return error("char" PD_FMT ": missing tag timestamp",
-			tagger_line - buffer);
-	tagger_line += len;
-	if (*tagger_line != ' ')
-		return error("char" PD_FMT ": malformed tag timestamp",
-			tagger_line - buffer);
-	tagger_line++;
-
-	/* timezone, 5 digits [+-]hhmm, max. 1400 */
-	if (!((tagger_line[0] == '+' || tagger_line[0] == '-') &&
-	      strspn(tagger_line+1, "0123456789") == 4 &&
-	      tagger_line[5] == '\n' && atoi(tagger_line+1) <= 1400))
-		return error("char" PD_FMT ": malformed tag timezone",
-			tagger_line - buffer);
-	tagger_line += 6;
-
-	/* Verify the blank line separating the header from the body */
-	if (*tagger_line != '\n')
-		return error("char" PD_FMT ": trailing garbage in tag header",
-			tagger_line - buffer);
-
-	/* The actual stuff afterwards we don't care about.. */
-	return 0;
-}
-
-#undef PD_FMT
-
-int main(int argc, char **argv)
-{
-	struct strbuf buf = STRBUF_INIT;
-	unsigned char result_sha1[20];
-
-	if (argc != 1)
-		usage("git mktag < signaturefile");
-
-	git_extract_argv0_path(argv[0]);
-
-	setup_git_directory();
-
-	if (strbuf_read(&buf, 0, 4096) < 0) {
-		die_errno("could not read from stdin");
-	}
-
-	/* Verify it for some basic sanity: it needs to start with
-	   "object <sha1>\ntype\ntagger " */
-	if (verify_tag(buf.buf, buf.len) < 0)
-		die("invalid tag signature file");
-
-	if (write_sha1_file(buf.buf, buf.len, tag_type, result_sha1) < 0)
-		die("unable to write tag file");
-
-	strbuf_release(&buf);
-	printf("%s\n", sha1_to_hex(result_sha1));
-	return 0;
-}
diff --git a/name-hash.c b/name-hash.c
index 0031d78..d8d25c2 100644
--- a/name-hash.c
+++ b/name-hash.c
@@ -32,6 +32,40 @@
 	return hash;
 }
 
+static void hash_index_entry_directories(struct index_state *istate, struct cache_entry *ce)
+{
+	/*
+	 * Throw each directory component in the hash for quick lookup
+	 * during a git status. Directory components are stored with their
+	 * closing slash.  Despite submodules being a directory, they never
+	 * reach this point, because they are stored without a closing slash
+	 * in the cache.
+	 *
+	 * Note that the cache_entry stored with the directory does not
+	 * represent the directory itself.  It is a pointer to an existing
+	 * filename, and its only purpose is to represent existence of the
+	 * directory in the cache.  It is very possible multiple directory
+	 * hash entries may point to the same cache_entry.
+	 */
+	unsigned int hash;
+	void **pos;
+
+	const char *ptr = ce->name;
+	while (*ptr) {
+		while (*ptr && *ptr != '/')
+			++ptr;
+		if (*ptr == '/') {
+			++ptr;
+			hash = hash_name(ce->name, ptr - ce->name);
+			pos = insert_hash(hash, ce, &istate->name_hash);
+			if (pos) {
+				ce->dir_next = *pos;
+				*pos = ce;
+			}
+		}
+	}
+}
+
 static void hash_index_entry(struct index_state *istate, struct cache_entry *ce)
 {
 	void **pos;
@@ -40,13 +74,16 @@
 	if (ce->ce_flags & CE_HASHED)
 		return;
 	ce->ce_flags |= CE_HASHED;
-	ce->next = NULL;
+	ce->next = ce->dir_next = NULL;
 	hash = hash_name(ce->name, ce_namelen(ce));
 	pos = insert_hash(hash, ce, &istate->name_hash);
 	if (pos) {
 		ce->next = *pos;
 		*pos = ce;
 	}
+
+	if (ignore_case)
+		hash_index_entry_directories(istate, ce);
 }
 
 static void lazy_init_name_hash(struct index_state *istate)
@@ -97,7 +134,21 @@
 	if (len == namelen && !cache_name_compare(name, namelen, ce->name, len))
 		return 1;
 
-	return icase && slow_same_name(name, namelen, ce->name, len);
+	if (!icase)
+		return 0;
+
+	/*
+	 * If the entry we're comparing is a filename (no trailing slash), then compare
+	 * the lengths exactly.
+	 */
+	if (name[namelen - 1] != '/')
+		return slow_same_name(name, namelen, ce->name, len);
+
+	/*
+	 * For a directory, we point to an arbitrary cache_entry filename.  Just
+	 * make sure the directory portion matches.
+	 */
+	return slow_same_name(name, namelen, ce->name, namelen < len ? namelen : len);
 }
 
 struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen, int icase)
@@ -113,7 +164,27 @@
 			if (same_name(ce, name, namelen, icase))
 				return ce;
 		}
-		ce = ce->next;
+		if (icase && name[namelen - 1] == '/')
+			ce = ce->dir_next;
+		else
+			ce = ce->next;
+	}
+
+	/*
+	 * Might be a submodule.  Despite submodules being directories,
+	 * they are stored in the name hash without a closing slash.
+	 * When ignore_case is 1, directories are stored in the name hash
+	 * with their closing slash.
+	 *
+	 * The side effect of this storage technique is we have need to
+	 * remove the slash from name and perform the lookup again without
+	 * the slash.  If a match is made, S_ISGITLINK(ce->mode) will be
+	 * true.
+	 */
+	if (icase && name[namelen - 1] == '/') {
+		ce = index_name_exists(istate, name, namelen - 1, icase);
+		if (ce && S_ISGITLINK(ce->ce_mode))
+			return ce;
 	}
 	return NULL;
 }
diff --git a/notes-cache.c b/notes-cache.c
new file mode 100644
index 0000000..eabe4a0
--- /dev/null
+++ b/notes-cache.c
@@ -0,0 +1,96 @@
+#include "cache.h"
+#include "notes-cache.h"
+#include "commit.h"
+#include "refs.h"
+
+static int notes_cache_match_validity(const char *ref, const char *validity)
+{
+	unsigned char sha1[20];
+	struct commit *commit;
+	struct pretty_print_context pretty_ctx;
+	struct strbuf msg = STRBUF_INIT;
+	int ret;
+
+	if (read_ref(ref, sha1) < 0)
+		return 0;
+
+	commit = lookup_commit_reference_gently(sha1, 1);
+	if (!commit)
+		return 0;
+
+	memset(&pretty_ctx, 0, sizeof(pretty_ctx));
+	format_commit_message(commit, "%s", &msg, &pretty_ctx);
+	strbuf_trim(&msg);
+
+	ret = !strcmp(msg.buf, validity);
+	strbuf_release(&msg);
+
+	return ret;
+}
+
+void notes_cache_init(struct notes_cache *c, const char *name,
+		     const char *validity)
+{
+	struct strbuf ref = STRBUF_INIT;
+	int flags = 0;
+
+	memset(c, 0, sizeof(*c));
+	c->validity = xstrdup(validity);
+
+	strbuf_addf(&ref, "refs/notes/%s", name);
+	if (!notes_cache_match_validity(ref.buf, validity))
+		flags = NOTES_INIT_EMPTY;
+	init_notes(&c->tree, ref.buf, combine_notes_overwrite, flags);
+	strbuf_release(&ref);
+}
+
+int notes_cache_write(struct notes_cache *c)
+{
+	unsigned char tree_sha1[20];
+	unsigned char commit_sha1[20];
+	struct strbuf msg = STRBUF_INIT;
+
+	if (!c || !c->tree.initialized || !c->tree.ref || !*c->tree.ref)
+		return -1;
+	if (!c->tree.dirty)
+		return 0;
+
+	if (write_notes_tree(&c->tree, tree_sha1))
+		return -1;
+	strbuf_attach(&msg, c->validity,
+		      strlen(c->validity), strlen(c->validity) + 1);
+	if (commit_tree(&msg, tree_sha1, NULL, commit_sha1, NULL, NULL) < 0)
+		return -1;
+	if (update_ref("update notes cache", c->tree.ref, commit_sha1, NULL,
+		       0, QUIET_ON_ERR) < 0)
+		return -1;
+
+	return 0;
+}
+
+char *notes_cache_get(struct notes_cache *c, unsigned char key_sha1[20],
+		      size_t *outsize)
+{
+	const unsigned char *value_sha1;
+	enum object_type type;
+	char *value;
+	unsigned long size;
+
+	value_sha1 = get_note(&c->tree, key_sha1);
+	if (!value_sha1)
+		return NULL;
+	value = read_sha1_file(value_sha1, &type, &size);
+
+	*outsize = size;
+	return value;
+}
+
+int notes_cache_put(struct notes_cache *c, unsigned char key_sha1[20],
+		    const char *data, size_t size)
+{
+	unsigned char value_sha1[20];
+
+	if (write_sha1_file(data, size, "blob", value_sha1) < 0)
+		return -1;
+	return add_note(&c->tree, key_sha1, value_sha1, NULL);
+}
diff --git a/notes-cache.h b/notes-cache.h
new file mode 100644
index 0000000..356f88f
--- /dev/null
+++ b/notes-cache.h
@@ -0,0 +1,20 @@
+#ifndef NOTES_CACHE_H
+#define NOTES_CACHE_H
+
+#include "notes.h"
+
+struct notes_cache {
+	struct notes_tree tree;
+	char *validity;
+};
+
+void notes_cache_init(struct notes_cache *c, const char *name,
+		     const char *validity);
+int notes_cache_write(struct notes_cache *c);
+
+char *notes_cache_get(struct notes_cache *c, unsigned char sha1[20], size_t
+		      *outsize);
+int notes_cache_put(struct notes_cache *c, unsigned char sha1[20],
+		    const char *data, size_t size);
+
+#endif /* NOTES_CACHE_H */
diff --git a/notes-merge.c b/notes-merge.c
new file mode 100644
index 0000000..29c6411
--- /dev/null
+++ b/notes-merge.c
@@ -0,0 +1,776 @@
+#include "cache.h"
+#include "commit.h"
+#include "refs.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "xdiff-interface.h"
+#include "ll-merge.h"
+#include "dir.h"
+#include "notes.h"
+#include "notes-merge.h"
+#include "strbuf.h"
+
+struct notes_merge_pair {
+	unsigned char obj[20], base[20], local[20], remote[20];
+};
+
+void init_notes_merge_options(struct notes_merge_options *o)
+{
+	memset(o, 0, sizeof(struct notes_merge_options));
+	strbuf_init(&(o->commit_msg), 0);
+	o->verbosity = NOTES_MERGE_VERBOSITY_DEFAULT;
+}
+
+static int path_to_sha1(const char *path, unsigned char *sha1)
+{
+	char hex_sha1[40];
+	int i = 0;
+	while (*path && i < 40) {
+		if (*path != '/')
+			hex_sha1[i++] = *path;
+		path++;
+	}
+	if (*path || i != 40)
+		return -1;
+	return get_sha1_hex(hex_sha1, sha1);
+}
+
+static int verify_notes_filepair(struct diff_filepair *p, unsigned char *sha1)
+{
+	switch (p->status) {
+	case DIFF_STATUS_MODIFIED:
+		assert(p->one->mode == p->two->mode);
+		assert(!is_null_sha1(p->one->sha1));
+		assert(!is_null_sha1(p->two->sha1));
+		break;
+	case DIFF_STATUS_ADDED:
+		assert(is_null_sha1(p->one->sha1));
+		break;
+	case DIFF_STATUS_DELETED:
+		assert(is_null_sha1(p->two->sha1));
+		break;
+	default:
+		return -1;
+	}
+	assert(!strcmp(p->one->path, p->two->path));
+	return path_to_sha1(p->one->path, sha1);
+}
+
+static struct notes_merge_pair *find_notes_merge_pair_pos(
+		struct notes_merge_pair *list, int len, unsigned char *obj,
+		int insert_new, int *occupied)
+{
+	/*
+	 * Both diff_tree_remote() and diff_tree_local() tend to process
+	 * merge_pairs in ascending order. Therefore, cache last returned
+	 * index, and search sequentially from there until the appropriate
+	 * position is found.
+	 *
+	 * Since inserts only happen from diff_tree_remote() (which mainly
+	 * _appends_), we don't care that inserting into the middle of the
+	 * list is expensive (using memmove()).
+	 */
+	static int last_index;
+	int i = last_index < len ? last_index : len - 1;
+	int prev_cmp = 0, cmp = -1;
+	while (i >= 0 && i < len) {
+		cmp = hashcmp(obj, list[i].obj);
+		if (!cmp) /* obj belongs @ i */
+			break;
+		else if (cmp < 0 && prev_cmp <= 0) /* obj belongs < i */
+			i--;
+		else if (cmp < 0) /* obj belongs between i-1 and i */
+			break;
+		else if (cmp > 0 && prev_cmp >= 0) /* obj belongs > i */
+			i++;
+		else /* if (cmp > 0) */ { /* obj belongs between i and i+1 */
+			i++;
+			break;
+		}
+		prev_cmp = cmp;
+	}
+	if (i < 0)
+		i = 0;
+	/* obj belongs at, or immediately preceding, index i (0 <= i <= len) */
+
+	if (!cmp)
+		*occupied = 1;
+	else {
+		*occupied = 0;
+		if (insert_new && i < len) {
+			memmove(list + i + 1, list + i,
+				(len - i) * sizeof(struct notes_merge_pair));
+			memset(list + i, 0, sizeof(struct notes_merge_pair));
+		}
+	}
+	last_index = i;
+	return list + i;
+}
+
+static unsigned char uninitialized[20] =
+	"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \
+	"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff";
+
+static struct notes_merge_pair *diff_tree_remote(struct notes_merge_options *o,
+						 const unsigned char *base,
+						 const unsigned char *remote,
+						 int *num_changes)
+{
+	struct diff_options opt;
+	struct notes_merge_pair *changes;
+	int i, len = 0;
+
+	trace_printf("\tdiff_tree_remote(base = %.7s, remote = %.7s)\n",
+	       sha1_to_hex(base), sha1_to_hex(remote));
+
+	diff_setup(&opt);
+	DIFF_OPT_SET(&opt, RECURSIVE);
+	opt.output_format = DIFF_FORMAT_NO_OUTPUT;
+	if (diff_setup_done(&opt) < 0)
+		die("diff_setup_done failed");
+	diff_tree_sha1(base, remote, "", &opt);
+	diffcore_std(&opt);
+
+	changes = xcalloc(diff_queued_diff.nr, sizeof(struct notes_merge_pair));
+
+	for (i = 0; i < diff_queued_diff.nr; i++) {
+		struct diff_filepair *p = diff_queued_diff.queue[i];
+		struct notes_merge_pair *mp;
+		int occupied;
+		unsigned char obj[20];
+
+		if (verify_notes_filepair(p, obj)) {
+			trace_printf("\t\tCannot merge entry '%s' (%c): "
+			       "%.7s -> %.7s. Skipping!\n", p->one->path,
+			       p->status, sha1_to_hex(p->one->sha1),
+			       sha1_to_hex(p->two->sha1));
+			continue;
+		}
+		mp = find_notes_merge_pair_pos(changes, len, obj, 1, &occupied);
+		if (occupied) {
+			/* We've found an addition/deletion pair */
+			assert(!hashcmp(mp->obj, obj));
+			if (is_null_sha1(p->one->sha1)) { /* addition */
+				assert(is_null_sha1(mp->remote));
+				hashcpy(mp->remote, p->two->sha1);
+			} else if (is_null_sha1(p->two->sha1)) { /* deletion */
+				assert(is_null_sha1(mp->base));
+				hashcpy(mp->base, p->one->sha1);
+			} else
+				assert(!"Invalid existing change recorded");
+		} else {
+			hashcpy(mp->obj, obj);
+			hashcpy(mp->base, p->one->sha1);
+			hashcpy(mp->local, uninitialized);
+			hashcpy(mp->remote, p->two->sha1);
+			len++;
+		}
+		trace_printf("\t\tStored remote change for %s: %.7s -> %.7s\n",
+		       sha1_to_hex(mp->obj), sha1_to_hex(mp->base),
+		       sha1_to_hex(mp->remote));
+	}
+	diff_flush(&opt);
+	diff_tree_release_paths(&opt);
+
+	*num_changes = len;
+	return changes;
+}
+
+static void diff_tree_local(struct notes_merge_options *o,
+			    struct notes_merge_pair *changes, int len,
+			    const unsigned char *base,
+			    const unsigned char *local)
+{
+	struct diff_options opt;
+	int i;
+
+	trace_printf("\tdiff_tree_local(len = %i, base = %.7s, local = %.7s)\n",
+	       len, sha1_to_hex(base), sha1_to_hex(local));
+
+	diff_setup(&opt);
+	DIFF_OPT_SET(&opt, RECURSIVE);
+	opt.output_format = DIFF_FORMAT_NO_OUTPUT;
+	if (diff_setup_done(&opt) < 0)
+		die("diff_setup_done failed");
+	diff_tree_sha1(base, local, "", &opt);
+	diffcore_std(&opt);
+
+	for (i = 0; i < diff_queued_diff.nr; i++) {
+		struct diff_filepair *p = diff_queued_diff.queue[i];
+		struct notes_merge_pair *mp;
+		int match;
+		unsigned char obj[20];
+
+		if (verify_notes_filepair(p, obj)) {
+			trace_printf("\t\tCannot merge entry '%s' (%c): "
+			       "%.7s -> %.7s. Skipping!\n", p->one->path,
+			       p->status, sha1_to_hex(p->one->sha1),
+			       sha1_to_hex(p->two->sha1));
+			continue;
+		}
+		mp = find_notes_merge_pair_pos(changes, len, obj, 0, &match);
+		if (!match) {
+			trace_printf("\t\tIgnoring local-only change for %s: "
+			       "%.7s -> %.7s\n", sha1_to_hex(obj),
+			       sha1_to_hex(p->one->sha1),
+			       sha1_to_hex(p->two->sha1));
+			continue;
+		}
+
+		assert(!hashcmp(mp->obj, obj));
+		if (is_null_sha1(p->two->sha1)) { /* deletion */
+			/*
+			 * Either this is a true deletion (1), or it is part
+			 * of an A/D pair (2), or D/A pair (3):
+			 *
+			 * (1) mp->local is uninitialized; set it to null_sha1
+			 * (2) mp->local is not uninitialized; don't touch it
+			 * (3) mp->local is uninitialized; set it to null_sha1
+			 *     (will be overwritten by following addition)
+			 */
+			if (!hashcmp(mp->local, uninitialized))
+				hashclr(mp->local);
+		} else if (is_null_sha1(p->one->sha1)) { /* addition */
+			/*
+			 * Either this is a true addition (1), or it is part
+			 * of an A/D pair (2), or D/A pair (3):
+			 *
+			 * (1) mp->local is uninitialized; set to p->two->sha1
+			 * (2) mp->local is uninitialized; set to p->two->sha1
+			 * (3) mp->local is null_sha1;     set to p->two->sha1
+			 */
+			assert(is_null_sha1(mp->local) ||
+			       !hashcmp(mp->local, uninitialized));
+			hashcpy(mp->local, p->two->sha1);
+		} else { /* modification */
+			/*
+			 * This is a true modification. p->one->sha1 shall
+			 * match mp->base, and mp->local shall be uninitialized.
+			 * Set mp->local to p->two->sha1.
+			 */
+			assert(!hashcmp(p->one->sha1, mp->base));
+			assert(!hashcmp(mp->local, uninitialized));
+			hashcpy(mp->local, p->two->sha1);
+		}
+		trace_printf("\t\tStored local change for %s: %.7s -> %.7s\n",
+		       sha1_to_hex(mp->obj), sha1_to_hex(mp->base),
+		       sha1_to_hex(mp->local));
+	}
+	diff_flush(&opt);
+	diff_tree_release_paths(&opt);
+}
+
+static void check_notes_merge_worktree(struct notes_merge_options *o)
+{
+	if (!o->has_worktree) {
+		/*
+		 * Must establish NOTES_MERGE_WORKTREE.
+		 * Abort if NOTES_MERGE_WORKTREE already exists
+		 */
+		if (file_exists(git_path(NOTES_MERGE_WORKTREE)) &&
+		    !is_empty_dir(git_path(NOTES_MERGE_WORKTREE))) {
+			if (advice_resolve_conflict)
+				die("You have not concluded your previous "
+				    "notes merge (%s exists).\nPlease, use "
+				    "'git notes merge --commit' or 'git notes "
+				    "merge --abort' to commit/abort the "
+				    "previous merge before you start a new "
+				    "notes merge.", git_path("NOTES_MERGE_*"));
+			else
+				die("You have not concluded your notes merge "
+				    "(%s exists).", git_path("NOTES_MERGE_*"));
+		}
+
+		if (safe_create_leading_directories(git_path(
+				NOTES_MERGE_WORKTREE "/.test")))
+			die_errno("unable to create directory %s",
+				  git_path(NOTES_MERGE_WORKTREE));
+		o->has_worktree = 1;
+	} else if (!file_exists(git_path(NOTES_MERGE_WORKTREE)))
+		/* NOTES_MERGE_WORKTREE should already be established */
+		die("missing '%s'. This should not happen",
+		    git_path(NOTES_MERGE_WORKTREE));
+}
+
+static void write_buf_to_worktree(const unsigned char *obj,
+				  const char *buf, unsigned long size)
+{
+	int fd;
+	char *path = git_path(NOTES_MERGE_WORKTREE "/%s", sha1_to_hex(obj));
+	if (safe_create_leading_directories(path))
+		die_errno("unable to create directory for '%s'", path);
+	if (file_exists(path))
+		die("found existing file at '%s'", path);
+
+	fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, 0666);
+	if (fd < 0)
+		die_errno("failed to open '%s'", path);
+
+	while (size > 0) {
+		long ret = write_in_full(fd, buf, size);
+		if (ret < 0) {
+			/* Ignore epipe */
+			if (errno == EPIPE)
+				break;
+			die_errno("notes-merge");
+		} else if (!ret) {
+			die("notes-merge: disk full?");
+		}
+		size -= ret;
+		buf += ret;
+	}
+
+	close(fd);
+}
+
+static void write_note_to_worktree(const unsigned char *obj,
+				   const unsigned char *note)
+{
+	enum object_type type;
+	unsigned long size;
+	void *buf = read_sha1_file(note, &type, &size);
+
+	if (!buf)
+		die("cannot read note %s for object %s",
+		    sha1_to_hex(note), sha1_to_hex(obj));
+	if (type != OBJ_BLOB)
+		die("blob expected in note %s for object %s",
+		    sha1_to_hex(note), sha1_to_hex(obj));
+	write_buf_to_worktree(obj, buf, size);
+	free(buf);
+}
+
+static int ll_merge_in_worktree(struct notes_merge_options *o,
+				struct notes_merge_pair *p)
+{
+	mmbuffer_t result_buf;
+	mmfile_t base, local, remote;
+	int status;
+
+	read_mmblob(&base, p->base);
+	read_mmblob(&local, p->local);
+	read_mmblob(&remote, p->remote);
+
+	status = ll_merge(&result_buf, sha1_to_hex(p->obj), &base, NULL,
+			  &local, o->local_ref, &remote, o->remote_ref, NULL);
+
+	free(base.ptr);
+	free(local.ptr);
+	free(remote.ptr);
+
+	if ((status < 0) || !result_buf.ptr)
+		die("Failed to execute internal merge");
+
+	write_buf_to_worktree(p->obj, result_buf.ptr, result_buf.size);
+	free(result_buf.ptr);
+
+	return status;
+}
+
+static int merge_one_change_manual(struct notes_merge_options *o,
+				   struct notes_merge_pair *p,
+				   struct notes_tree *t)
+{
+	const char *lref = o->local_ref ? o->local_ref : "local version";
+	const char *rref = o->remote_ref ? o->remote_ref : "remote version";
+
+	trace_printf("\t\t\tmerge_one_change_manual(obj = %.7s, base = %.7s, "
+	       "local = %.7s, remote = %.7s)\n",
+	       sha1_to_hex(p->obj), sha1_to_hex(p->base),
+	       sha1_to_hex(p->local), sha1_to_hex(p->remote));
+
+	/* add "Conflicts:" section to commit message first time through */
+	if (!o->has_worktree)
+		strbuf_addstr(&(o->commit_msg), "\n\nConflicts:\n");
+
+	strbuf_addf(&(o->commit_msg), "\t%s\n", sha1_to_hex(p->obj));
+
+	if (o->verbosity >= 2)
+		printf("Auto-merging notes for %s\n", sha1_to_hex(p->obj));
+	check_notes_merge_worktree(o);
+	if (is_null_sha1(p->local)) {
+		/* D/F conflict, checkout p->remote */
+		assert(!is_null_sha1(p->remote));
+		if (o->verbosity >= 1)
+			printf("CONFLICT (delete/modify): Notes for object %s "
+				"deleted in %s and modified in %s. Version from %s "
+				"left in tree.\n",
+				sha1_to_hex(p->obj), lref, rref, rref);
+		write_note_to_worktree(p->obj, p->remote);
+	} else if (is_null_sha1(p->remote)) {
+		/* D/F conflict, checkout p->local */
+		assert(!is_null_sha1(p->local));
+		if (o->verbosity >= 1)
+			printf("CONFLICT (delete/modify): Notes for object %s "
+				"deleted in %s and modified in %s. Version from %s "
+				"left in tree.\n",
+				sha1_to_hex(p->obj), rref, lref, lref);
+		write_note_to_worktree(p->obj, p->local);
+	} else {
+		/* "regular" conflict, checkout result of ll_merge() */
+		const char *reason = "content";
+		if (is_null_sha1(p->base))
+			reason = "add/add";
+		assert(!is_null_sha1(p->local));
+		assert(!is_null_sha1(p->remote));
+		if (o->verbosity >= 1)
+			printf("CONFLICT (%s): Merge conflict in notes for "
+				"object %s\n", reason, sha1_to_hex(p->obj));
+		ll_merge_in_worktree(o, p);
+	}
+
+	trace_printf("\t\t\tremoving from partial merge result\n");
+	remove_note(t, p->obj);
+
+	return 1;
+}
+
+static int merge_one_change(struct notes_merge_options *o,
+			    struct notes_merge_pair *p, struct notes_tree *t)
+{
+	/*
+	 * Return 0 if change is successfully resolved (stored in notes_tree).
+	 * Return 1 is change results in a conflict (NOT stored in notes_tree,
+	 * but instead written to NOTES_MERGE_WORKTREE with conflict markers).
+	 */
+	switch (o->strategy) {
+	case NOTES_MERGE_RESOLVE_MANUAL:
+		return merge_one_change_manual(o, p, t);
+	case NOTES_MERGE_RESOLVE_OURS:
+		if (o->verbosity >= 2)
+			printf("Using local notes for %s\n",
+						sha1_to_hex(p->obj));
+		/* nothing to do */
+		return 0;
+	case NOTES_MERGE_RESOLVE_THEIRS:
+		if (o->verbosity >= 2)
+			printf("Using remote notes for %s\n",
+						sha1_to_hex(p->obj));
+		if (add_note(t, p->obj, p->remote, combine_notes_overwrite))
+			die("BUG: combine_notes_overwrite failed");
+		return 0;
+	case NOTES_MERGE_RESOLVE_UNION:
+		if (o->verbosity >= 2)
+			printf("Concatenating local and remote notes for %s\n",
+							sha1_to_hex(p->obj));
+		if (add_note(t, p->obj, p->remote, combine_notes_concatenate))
+			die("failed to concatenate notes "
+			    "(combine_notes_concatenate)");
+		return 0;
+	case NOTES_MERGE_RESOLVE_CAT_SORT_UNIQ:
+		if (o->verbosity >= 2)
+			printf("Concatenating unique lines in local and remote "
+				"notes for %s\n", sha1_to_hex(p->obj));
+		if (add_note(t, p->obj, p->remote, combine_notes_cat_sort_uniq))
+			die("failed to concatenate notes "
+			    "(combine_notes_cat_sort_uniq)");
+		return 0;
+	}
+	die("Unknown strategy (%i).", o->strategy);
+}
+
+static int merge_changes(struct notes_merge_options *o,
+			 struct notes_merge_pair *changes, int *num_changes,
+			 struct notes_tree *t)
+{
+	int i, conflicts = 0;
+
+	trace_printf("\tmerge_changes(num_changes = %i)\n", *num_changes);
+	for (i = 0; i < *num_changes; i++) {
+		struct notes_merge_pair *p = changes + i;
+		trace_printf("\t\t%.7s: %.7s -> %.7s/%.7s\n",
+		       sha1_to_hex(p->obj), sha1_to_hex(p->base),
+		       sha1_to_hex(p->local), sha1_to_hex(p->remote));
+
+		if (!hashcmp(p->base, p->remote)) {
+			/* no remote change; nothing to do */
+			trace_printf("\t\t\tskipping (no remote change)\n");
+		} else if (!hashcmp(p->local, p->remote)) {
+			/* same change in local and remote; nothing to do */
+			trace_printf("\t\t\tskipping (local == remote)\n");
+		} else if (!hashcmp(p->local, uninitialized) ||
+			   !hashcmp(p->local, p->base)) {
+			/* no local change; adopt remote change */
+			trace_printf("\t\t\tno local change, adopted remote\n");
+			if (add_note(t, p->obj, p->remote,
+				     combine_notes_overwrite))
+				die("BUG: combine_notes_overwrite failed");
+		} else {
+			/* need file-level merge between local and remote */
+			trace_printf("\t\t\tneed content-level merge\n");
+			conflicts += merge_one_change(o, p, t);
+		}
+	}
+
+	return conflicts;
+}
+
+static int merge_from_diffs(struct notes_merge_options *o,
+			    const unsigned char *base,
+			    const unsigned char *local,
+			    const unsigned char *remote, struct notes_tree *t)
+{
+	struct notes_merge_pair *changes;
+	int num_changes, conflicts;
+
+	trace_printf("\tmerge_from_diffs(base = %.7s, local = %.7s, "
+	       "remote = %.7s)\n", sha1_to_hex(base), sha1_to_hex(local),
+	       sha1_to_hex(remote));
+
+	changes = diff_tree_remote(o, base, remote, &num_changes);
+	diff_tree_local(o, changes, num_changes, base, local);
+
+	conflicts = merge_changes(o, changes, &num_changes, t);
+	free(changes);
+
+	if (o->verbosity >= 4)
+		printf(t->dirty ?
+		       "Merge result: %i unmerged notes and a dirty notes tree\n" :
+		       "Merge result: %i unmerged notes and a clean notes tree\n",
+		       conflicts);
+
+	return conflicts ? -1 : 1;
+}
+
+void create_notes_commit(struct notes_tree *t, struct commit_list *parents,
+			 const struct strbuf *msg, unsigned char *result_sha1)
+{
+	unsigned char tree_sha1[20];
+
+	assert(t->initialized);
+
+	if (write_notes_tree(t, tree_sha1))
+		die("Failed to write notes tree to database");
+
+	if (!parents) {
+		/* Deduce parent commit from t->ref */
+		unsigned char parent_sha1[20];
+		if (!read_ref(t->ref, parent_sha1)) {
+			struct commit *parent = lookup_commit(parent_sha1);
+			if (!parent || parse_commit(parent))
+				die("Failed to find/parse commit %s", t->ref);
+			commit_list_insert(parent, &parents);
+		}
+		/* else: t->ref points to nothing, assume root/orphan commit */
+	}
+
+	if (commit_tree(msg, tree_sha1, parents, result_sha1, NULL, NULL))
+		die("Failed to commit notes tree to database");
+}
+
+int notes_merge(struct notes_merge_options *o,
+		struct notes_tree *local_tree,
+		unsigned char *result_sha1)
+{
+	unsigned char local_sha1[20], remote_sha1[20];
+	struct commit *local, *remote;
+	struct commit_list *bases = NULL;
+	const unsigned char *base_sha1, *base_tree_sha1;
+	int result = 0;
+
+	assert(o->local_ref && o->remote_ref);
+	assert(!strcmp(o->local_ref, local_tree->ref));
+	hashclr(result_sha1);
+
+	trace_printf("notes_merge(o->local_ref = %s, o->remote_ref = %s)\n",
+	       o->local_ref, o->remote_ref);
+
+	/* Dereference o->local_ref into local_sha1 */
+	if (read_ref_full(o->local_ref, local_sha1, 0, NULL))
+		die("Failed to resolve local notes ref '%s'", o->local_ref);
+	else if (!check_refname_format(o->local_ref, 0) &&
+		is_null_sha1(local_sha1))
+		local = NULL; /* local_sha1 == null_sha1 indicates unborn ref */
+	else if (!(local = lookup_commit_reference(local_sha1)))
+		die("Could not parse local commit %s (%s)",
+		    sha1_to_hex(local_sha1), o->local_ref);
+	trace_printf("\tlocal commit: %.7s\n", sha1_to_hex(local_sha1));
+
+	/* Dereference o->remote_ref into remote_sha1 */
+	if (get_sha1(o->remote_ref, remote_sha1)) {
+		/*
+		 * Failed to get remote_sha1. If o->remote_ref looks like an
+		 * unborn ref, perform the merge using an empty notes tree.
+		 */
+		if (!check_refname_format(o->remote_ref, 0)) {
+			hashclr(remote_sha1);
+			remote = NULL;
+		} else {
+			die("Failed to resolve remote notes ref '%s'",
+			    o->remote_ref);
+		}
+	} else if (!(remote = lookup_commit_reference(remote_sha1))) {
+		die("Could not parse remote commit %s (%s)",
+		    sha1_to_hex(remote_sha1), o->remote_ref);
+	}
+	trace_printf("\tremote commit: %.7s\n", sha1_to_hex(remote_sha1));
+
+	if (!local && !remote)
+		die("Cannot merge empty notes ref (%s) into empty notes ref "
+		    "(%s)", o->remote_ref, o->local_ref);
+	if (!local) {
+		/* result == remote commit */
+		hashcpy(result_sha1, remote_sha1);
+		goto found_result;
+	}
+	if (!remote) {
+		/* result == local commit */
+		hashcpy(result_sha1, local_sha1);
+		goto found_result;
+	}
+	assert(local && remote);
+
+	/* Find merge bases */
+	bases = get_merge_bases(local, remote, 1);
+	if (!bases) {
+		base_sha1 = null_sha1;
+		base_tree_sha1 = EMPTY_TREE_SHA1_BIN;
+		if (o->verbosity >= 4)
+			printf("No merge base found; doing history-less merge\n");
+	} else if (!bases->next) {
+		base_sha1 = bases->item->object.sha1;
+		base_tree_sha1 = bases->item->tree->object.sha1;
+		if (o->verbosity >= 4)
+			printf("One merge base found (%.7s)\n",
+				sha1_to_hex(base_sha1));
+	} else {
+		/* TODO: How to handle multiple merge-bases? */
+		base_sha1 = bases->item->object.sha1;
+		base_tree_sha1 = bases->item->tree->object.sha1;
+		if (o->verbosity >= 3)
+			printf("Multiple merge bases found. Using the first "
+				"(%.7s)\n", sha1_to_hex(base_sha1));
+	}
+
+	if (o->verbosity >= 4)
+		printf("Merging remote commit %.7s into local commit %.7s with "
+			"merge-base %.7s\n", sha1_to_hex(remote->object.sha1),
+			sha1_to_hex(local->object.sha1),
+			sha1_to_hex(base_sha1));
+
+	if (!hashcmp(remote->object.sha1, base_sha1)) {
+		/* Already merged; result == local commit */
+		if (o->verbosity >= 2)
+			printf("Already up-to-date!\n");
+		hashcpy(result_sha1, local->object.sha1);
+		goto found_result;
+	}
+	if (!hashcmp(local->object.sha1, base_sha1)) {
+		/* Fast-forward; result == remote commit */
+		if (o->verbosity >= 2)
+			printf("Fast-forward\n");
+		hashcpy(result_sha1, remote->object.sha1);
+		goto found_result;
+	}
+
+	result = merge_from_diffs(o, base_tree_sha1, local->tree->object.sha1,
+				  remote->tree->object.sha1, local_tree);
+
+	if (result != 0) { /* non-trivial merge (with or without conflicts) */
+		/* Commit (partial) result */
+		struct commit_list *parents = NULL;
+		commit_list_insert(remote, &parents); /* LIFO order */
+		commit_list_insert(local, &parents);
+		create_notes_commit(local_tree, parents, &o->commit_msg,
+				    result_sha1);
+	}
+
+found_result:
+	free_commit_list(bases);
+	strbuf_release(&(o->commit_msg));
+	trace_printf("notes_merge(): result = %i, result_sha1 = %.7s\n",
+	       result, sha1_to_hex(result_sha1));
+	return result;
+}
+
+int notes_merge_commit(struct notes_merge_options *o,
+		       struct notes_tree *partial_tree,
+		       struct commit *partial_commit,
+		       unsigned char *result_sha1)
+{
+	/*
+	 * Iterate through files in .git/NOTES_MERGE_WORKTREE and add all
+	 * found notes to 'partial_tree'. Write the updated notes tree to
+	 * the DB, and commit the resulting tree object while reusing the
+	 * commit message and parents from 'partial_commit'.
+	 * Finally store the new commit object SHA1 into 'result_sha1'.
+	 */
+	DIR *dir;
+	struct dirent *e;
+	struct strbuf path = STRBUF_INIT;
+	char *msg = strstr(partial_commit->buffer, "\n\n");
+	struct strbuf sb_msg = STRBUF_INIT;
+	int baselen;
+
+	strbuf_addstr(&path, git_path(NOTES_MERGE_WORKTREE));
+	if (o->verbosity >= 3)
+		printf("Committing notes in notes merge worktree at %s\n",
+			path.buf);
+
+	if (!msg || msg[2] == '\0')
+		die("partial notes commit has empty message");
+	msg += 2;
+
+	dir = opendir(path.buf);
+	if (!dir)
+		die_errno("could not open %s", path.buf);
+
+	strbuf_addch(&path, '/');
+	baselen = path.len;
+	while ((e = readdir(dir)) != NULL) {
+		struct stat st;
+		unsigned char obj_sha1[20], blob_sha1[20];
+
+		if (is_dot_or_dotdot(e->d_name))
+			continue;
+
+		if (strlen(e->d_name) != 40 || get_sha1_hex(e->d_name, obj_sha1)) {
+			if (o->verbosity >= 3)
+				printf("Skipping non-SHA1 entry '%s%s'\n",
+					path.buf, e->d_name);
+			continue;
+		}
+
+		strbuf_addstr(&path, e->d_name);
+		/* write file as blob, and add to partial_tree */
+		if (stat(path.buf, &st))
+			die_errno("Failed to stat '%s'", path.buf);
+		if (index_path(blob_sha1, path.buf, &st, HASH_WRITE_OBJECT))
+			die("Failed to write blob object from '%s'", path.buf);
+		if (add_note(partial_tree, obj_sha1, blob_sha1, NULL))
+			die("Failed to add resolved note '%s' to notes tree",
+			    path.buf);
+		if (o->verbosity >= 4)
+			printf("Added resolved note for object %s: %s\n",
+				sha1_to_hex(obj_sha1), sha1_to_hex(blob_sha1));
+		strbuf_setlen(&path, baselen);
+	}
+
+	strbuf_attach(&sb_msg, msg, strlen(msg), strlen(msg) + 1);
+	create_notes_commit(partial_tree, partial_commit->parents, &sb_msg,
+			    result_sha1);
+	if (o->verbosity >= 4)
+		printf("Finalized notes merge commit: %s\n",
+			sha1_to_hex(result_sha1));
+	strbuf_release(&path);
+	closedir(dir);
+	return 0;
+}
+
+int notes_merge_abort(struct notes_merge_options *o)
+{
+	/*
+	 * Remove all files within .git/NOTES_MERGE_WORKTREE. We do not remove
+	 * the .git/NOTES_MERGE_WORKTREE directory itself, since it might be
+	 * the current working directory of the user.
+	 */
+	struct strbuf buf = STRBUF_INIT;
+	int ret;
+
+	strbuf_addstr(&buf, git_path(NOTES_MERGE_WORKTREE));
+	if (o->verbosity >= 3)
+		printf("Removing notes merge worktree at %s/*\n", buf.buf);
+	ret = remove_dir_recursively(&buf, REMOVE_DIR_KEEP_TOPLEVEL);
+	strbuf_release(&buf);
+	return ret;
+}
diff --git a/notes-merge.h b/notes-merge.h
new file mode 100644
index 0000000..0c11b17
--- /dev/null
+++ b/notes-merge.h
@@ -0,0 +1,98 @@
+#ifndef NOTES_MERGE_H
+#define NOTES_MERGE_H
+
+#define NOTES_MERGE_WORKTREE "NOTES_MERGE_WORKTREE"
+
+enum notes_merge_verbosity {
+	NOTES_MERGE_VERBOSITY_DEFAULT = 2,
+	NOTES_MERGE_VERBOSITY_MAX = 5
+};
+
+struct notes_merge_options {
+	const char *local_ref;
+	const char *remote_ref;
+	struct strbuf commit_msg;
+	int verbosity;
+	enum {
+		NOTES_MERGE_RESOLVE_MANUAL = 0,
+		NOTES_MERGE_RESOLVE_OURS,
+		NOTES_MERGE_RESOLVE_THEIRS,
+		NOTES_MERGE_RESOLVE_UNION,
+		NOTES_MERGE_RESOLVE_CAT_SORT_UNIQ
+	} strategy;
+	unsigned has_worktree:1;
+};
+
+void init_notes_merge_options(struct notes_merge_options *o);
+
+/*
+ * Create new notes commit from the given notes tree
+ *
+ * Properties of the created commit:
+ * - tree: the result of converting t to a tree object with write_notes_tree().
+ * - parents: the given parents OR (if NULL) the commit referenced by t->ref.
+ * - author/committer: the default determined by commmit_tree().
+ * - commit message: msg
+ *
+ * The resulting commit SHA1 is stored in result_sha1.
+ */
+void create_notes_commit(struct notes_tree *t, struct commit_list *parents,
+			 const struct strbuf *msg, unsigned char *result_sha1);
+
+/*
+ * Merge notes from o->remote_ref into o->local_ref
+ *
+ * The given notes_tree 'local_tree' must be the notes_tree referenced by the
+ * o->local_ref. This is the notes_tree in which the object-level merge is
+ * performed.
+ *
+ * The commits given by the two refs are merged, producing one of the following
+ * outcomes:
+ *
+ * 1. The merge trivially results in an existing commit (e.g. fast-forward or
+ *    already-up-to-date). 'local_tree' is untouched, the SHA1 of the result
+ *    is written into 'result_sha1' and 0 is returned.
+ * 2. The merge successfully completes, producing a merge commit. local_tree
+ *    contains the updated notes tree, the SHA1 of the resulting commit is
+ *    written into 'result_sha1', and 1 is returned.
+ * 3. The merge results in conflicts. This is similar to #2 in that the
+ *    partial merge result (i.e. merge result minus the unmerged entries)
+ *    are stored in 'local_tree', and the SHA1 or the resulting commit
+ *    (to be amended when the conflicts have been resolved) is written into
+ *    'result_sha1'. The unmerged entries are written into the
+ *    .git/NOTES_MERGE_WORKTREE directory with conflict markers.
+ *    -1 is returned.
+ *
+ * Both o->local_ref and o->remote_ref must be given (non-NULL), but either ref
+ * (although not both) may refer to a non-existing notes ref, in which case
+ * that notes ref is interpreted as an empty notes tree, and the merge
+ * trivially results in what the other ref points to.
+ */
+int notes_merge(struct notes_merge_options *o,
+		struct notes_tree *local_tree,
+		unsigned char *result_sha1);
+
+/*
+ * Finalize conflict resolution from an earlier notes_merge()
+ *
+ * The given notes tree 'partial_tree' must be the notes_tree corresponding to
+ * the given 'partial_commit', the partial result commit created by a previous
+ * call to notes_merge().
+ *
+ * This function will add the (now resolved) notes in .git/NOTES_MERGE_WORKTREE
+ * to 'partial_tree', and create a final notes merge commit, the SHA1 of which
+ * will be stored in 'result_sha1'.
+ */
+int notes_merge_commit(struct notes_merge_options *o,
+		       struct notes_tree *partial_tree,
+		       struct commit *partial_commit,
+		       unsigned char *result_sha1);
+
+/*
+ * Abort conflict resolution from an earlier notes_merge()
+ *
+ * Removes the notes merge worktree in .git/NOTES_MERGE_WORKTREE.
+ */
+int notes_merge_abort(struct notes_merge_options *o);
+
+#endif
diff --git a/notes.c b/notes.c
new file mode 100644
index 0000000..93e9868
--- /dev/null
+++ b/notes.c
@@ -0,0 +1,1298 @@
+#include "cache.h"
+#include "notes.h"
+#include "blob.h"
+#include "tree.h"
+#include "utf8.h"
+#include "strbuf.h"
+#include "tree-walk.h"
+#include "string-list.h"
+#include "refs.h"
+
+/*
+ * Use a non-balancing simple 16-tree structure with struct int_node as
+ * internal nodes, and struct leaf_node as leaf nodes. Each int_node has a
+ * 16-array of pointers to its children.
+ * The bottom 2 bits of each pointer is used to identify the pointer type
+ * - ptr & 3 == 0 - NULL pointer, assert(ptr == NULL)
+ * - ptr & 3 == 1 - pointer to next internal node - cast to struct int_node *
+ * - ptr & 3 == 2 - pointer to note entry - cast to struct leaf_node *
+ * - ptr & 3 == 3 - pointer to subtree entry - cast to struct leaf_node *
+ *
+ * The root node is a statically allocated struct int_node.
+ */
+struct int_node {
+	void *a[16];
+};
+
+/*
+ * Leaf nodes come in two variants, note entries and subtree entries,
+ * distinguished by the LSb of the leaf node pointer (see above).
+ * As a note entry, the key is the SHA1 of the referenced object, and the
+ * value is the SHA1 of the note object.
+ * As a subtree entry, the key is the prefix SHA1 (w/trailing NULs) of the
+ * referenced object, using the last byte of the key to store the length of
+ * the prefix. The value is the SHA1 of the tree object containing the notes
+ * subtree.
+ */
+struct leaf_node {
+	unsigned char key_sha1[20];
+	unsigned char val_sha1[20];
+};
+
+/*
+ * A notes tree may contain entries that are not notes, and that do not follow
+ * the naming conventions of notes. There are typically none/few of these, but
+ * we still need to keep track of them. Keep a simple linked list sorted alpha-
+ * betically on the non-note path. The list is populated when parsing tree
+ * objects in load_subtree(), and the non-notes are correctly written back into
+ * the tree objects produced by write_notes_tree().
+ */
+struct non_note {
+	struct non_note *next; /* grounded (last->next == NULL) */
+	char *path;
+	unsigned int mode;
+	unsigned char sha1[20];
+};
+
+#define PTR_TYPE_NULL     0
+#define PTR_TYPE_INTERNAL 1
+#define PTR_TYPE_NOTE     2
+#define PTR_TYPE_SUBTREE  3
+
+#define GET_PTR_TYPE(ptr)       ((uintptr_t) (ptr) & 3)
+#define CLR_PTR_TYPE(ptr)       ((void *) ((uintptr_t) (ptr) & ~3))
+#define SET_PTR_TYPE(ptr, type) ((void *) ((uintptr_t) (ptr) | (type)))
+
+#define GET_NIBBLE(n, sha1) (((sha1[(n) >> 1]) >> ((~(n) & 0x01) << 2)) & 0x0f)
+
+#define SUBTREE_SHA1_PREFIXCMP(key_sha1, subtree_sha1) \
+	(memcmp(key_sha1, subtree_sha1, subtree_sha1[19]))
+
+struct notes_tree default_notes_tree;
+
+static struct string_list display_notes_refs;
+static struct notes_tree **display_notes_trees;
+
+static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,
+		struct int_node *node, unsigned int n);
+
+/*
+ * Search the tree until the appropriate location for the given key is found:
+ * 1. Start at the root node, with n = 0
+ * 2. If a[0] at the current level is a matching subtree entry, unpack that
+ *    subtree entry and remove it; restart search at the current level.
+ * 3. Use the nth nibble of the key as an index into a:
+ *    - If a[n] is an int_node, recurse from #2 into that node and increment n
+ *    - If a matching subtree entry, unpack that subtree entry (and remove it);
+ *      restart search at the current level.
+ *    - Otherwise, we have found one of the following:
+ *      - a subtree entry which does not match the key
+ *      - a note entry which may or may not match the key
+ *      - an unused leaf node (NULL)
+ *      In any case, set *tree and *n, and return pointer to the tree location.
+ */
+static void **note_tree_search(struct notes_tree *t, struct int_node **tree,
+		unsigned char *n, const unsigned char *key_sha1)
+{
+	struct leaf_node *l;
+	unsigned char i;
+	void *p = (*tree)->a[0];
+
+	if (GET_PTR_TYPE(p) == PTR_TYPE_SUBTREE) {
+		l = (struct leaf_node *) CLR_PTR_TYPE(p);
+		if (!SUBTREE_SHA1_PREFIXCMP(key_sha1, l->key_sha1)) {
+			/* unpack tree and resume search */
+			(*tree)->a[0] = NULL;
+			load_subtree(t, l, *tree, *n);
+			free(l);
+			return note_tree_search(t, tree, n, key_sha1);
+		}
+	}
+
+	i = GET_NIBBLE(*n, key_sha1);
+	p = (*tree)->a[i];
+	switch (GET_PTR_TYPE(p)) {
+	case PTR_TYPE_INTERNAL:
+		*tree = CLR_PTR_TYPE(p);
+		(*n)++;
+		return note_tree_search(t, tree, n, key_sha1);
+	case PTR_TYPE_SUBTREE:
+		l = (struct leaf_node *) CLR_PTR_TYPE(p);
+		if (!SUBTREE_SHA1_PREFIXCMP(key_sha1, l->key_sha1)) {
+			/* unpack tree and resume search */
+			(*tree)->a[i] = NULL;
+			load_subtree(t, l, *tree, *n);
+			free(l);
+			return note_tree_search(t, tree, n, key_sha1);
+		}
+		/* fall through */
+	default:
+		return &((*tree)->a[i]);
+	}
+}
+
+/*
+ * To find a leaf_node:
+ * Search to the tree location appropriate for the given key:
+ * If a note entry with matching key, return the note entry, else return NULL.
+ */
+static struct leaf_node *note_tree_find(struct notes_tree *t,
+		struct int_node *tree, unsigned char n,
+		const unsigned char *key_sha1)
+{
+	void **p = note_tree_search(t, &tree, &n, key_sha1);
+	if (GET_PTR_TYPE(*p) == PTR_TYPE_NOTE) {
+		struct leaf_node *l = (struct leaf_node *) CLR_PTR_TYPE(*p);
+		if (!hashcmp(key_sha1, l->key_sha1))
+			return l;
+	}
+	return NULL;
+}
+
+/*
+ * How to consolidate an int_node:
+ * If there are > 1 non-NULL entries, give up and return non-zero.
+ * Otherwise replace the int_node at the given index in the given parent node
+ * with the only entry (or a NULL entry if no entries) from the given tree,
+ * and return 0.
+ */
+static int note_tree_consolidate(struct int_node *tree,
+	struct int_node *parent, unsigned char index)
+{
+	unsigned int i;
+	void *p = NULL;
+
+	assert(tree && parent);
+	assert(CLR_PTR_TYPE(parent->a[index]) == tree);
+
+	for (i = 0; i < 16; i++) {
+		if (GET_PTR_TYPE(tree->a[i]) != PTR_TYPE_NULL) {
+			if (p) /* more than one entry */
+				return -2;
+			p = tree->a[i];
+		}
+	}
+
+	/* replace tree with p in parent[index] */
+	parent->a[index] = p;
+	free(tree);
+	return 0;
+}
+
+/*
+ * To remove a leaf_node:
+ * Search to the tree location appropriate for the given leaf_node's key:
+ * - If location does not hold a matching entry, abort and do nothing.
+ * - Copy the matching entry's value into the given entry.
+ * - Replace the matching leaf_node with a NULL entry (and free the leaf_node).
+ * - Consolidate int_nodes repeatedly, while walking up the tree towards root.
+ */
+static void note_tree_remove(struct notes_tree *t,
+		struct int_node *tree, unsigned char n,
+		struct leaf_node *entry)
+{
+	struct leaf_node *l;
+	struct int_node *parent_stack[20];
+	unsigned char i, j;
+	void **p = note_tree_search(t, &tree, &n, entry->key_sha1);
+
+	assert(GET_PTR_TYPE(entry) == 0); /* no type bits set */
+	if (GET_PTR_TYPE(*p) != PTR_TYPE_NOTE)
+		return; /* type mismatch, nothing to remove */
+	l = (struct leaf_node *) CLR_PTR_TYPE(*p);
+	if (hashcmp(l->key_sha1, entry->key_sha1))
+		return; /* key mismatch, nothing to remove */
+
+	/* we have found a matching entry */
+	hashcpy(entry->val_sha1, l->val_sha1);
+	free(l);
+	*p = SET_PTR_TYPE(NULL, PTR_TYPE_NULL);
+
+	/* consolidate this tree level, and parent levels, if possible */
+	if (!n)
+		return; /* cannot consolidate top level */
+	/* first, build stack of ancestors between root and current node */
+	parent_stack[0] = t->root;
+	for (i = 0; i < n; i++) {
+		j = GET_NIBBLE(i, entry->key_sha1);
+		parent_stack[i + 1] = CLR_PTR_TYPE(parent_stack[i]->a[j]);
+	}
+	assert(i == n && parent_stack[i] == tree);
+	/* next, unwind stack until note_tree_consolidate() is done */
+	while (i > 0 &&
+	       !note_tree_consolidate(parent_stack[i], parent_stack[i - 1],
+				      GET_NIBBLE(i - 1, entry->key_sha1)))
+		i--;
+}
+
+/*
+ * To insert a leaf_node:
+ * Search to the tree location appropriate for the given leaf_node's key:
+ * - If location is unused (NULL), store the tweaked pointer directly there
+ * - If location holds a note entry that matches the note-to-be-inserted, then
+ *   combine the two notes (by calling the given combine_notes function).
+ * - If location holds a note entry that matches the subtree-to-be-inserted,
+ *   then unpack the subtree-to-be-inserted into the location.
+ * - If location holds a matching subtree entry, unpack the subtree at that
+ *   location, and restart the insert operation from that level.
+ * - Else, create a new int_node, holding both the node-at-location and the
+ *   node-to-be-inserted, and store the new int_node into the location.
+ */
+static int note_tree_insert(struct notes_tree *t, struct int_node *tree,
+		unsigned char n, struct leaf_node *entry, unsigned char type,
+		combine_notes_fn combine_notes)
+{
+	struct int_node *new_node;
+	struct leaf_node *l;
+	void **p = note_tree_search(t, &tree, &n, entry->key_sha1);
+	int ret = 0;
+
+	assert(GET_PTR_TYPE(entry) == 0); /* no type bits set */
+	l = (struct leaf_node *) CLR_PTR_TYPE(*p);
+	switch (GET_PTR_TYPE(*p)) {
+	case PTR_TYPE_NULL:
+		assert(!*p);
+		if (is_null_sha1(entry->val_sha1))
+			free(entry);
+		else
+			*p = SET_PTR_TYPE(entry, type);
+		return 0;
+	case PTR_TYPE_NOTE:
+		switch (type) {
+		case PTR_TYPE_NOTE:
+			if (!hashcmp(l->key_sha1, entry->key_sha1)) {
+				/* skip concatenation if l == entry */
+				if (!hashcmp(l->val_sha1, entry->val_sha1))
+					return 0;
+
+				ret = combine_notes(l->val_sha1,
+						    entry->val_sha1);
+				if (!ret && is_null_sha1(l->val_sha1))
+					note_tree_remove(t, tree, n, entry);
+				free(entry);
+				return ret;
+			}
+			break;
+		case PTR_TYPE_SUBTREE:
+			if (!SUBTREE_SHA1_PREFIXCMP(l->key_sha1,
+						    entry->key_sha1)) {
+				/* unpack 'entry' */
+				load_subtree(t, entry, tree, n);
+				free(entry);
+				return 0;
+			}
+			break;
+		}
+		break;
+	case PTR_TYPE_SUBTREE:
+		if (!SUBTREE_SHA1_PREFIXCMP(entry->key_sha1, l->key_sha1)) {
+			/* unpack 'l' and restart insert */
+			*p = NULL;
+			load_subtree(t, l, tree, n);
+			free(l);
+			return note_tree_insert(t, tree, n, entry, type,
+						combine_notes);
+		}
+		break;
+	}
+
+	/* non-matching leaf_node */
+	assert(GET_PTR_TYPE(*p) == PTR_TYPE_NOTE ||
+	       GET_PTR_TYPE(*p) == PTR_TYPE_SUBTREE);
+	if (is_null_sha1(entry->val_sha1)) { /* skip insertion of empty note */
+		free(entry);
+		return 0;
+	}
+	new_node = (struct int_node *) xcalloc(sizeof(struct int_node), 1);
+	ret = note_tree_insert(t, new_node, n + 1, l, GET_PTR_TYPE(*p),
+			       combine_notes);
+	if (ret)
+		return ret;
+	*p = SET_PTR_TYPE(new_node, PTR_TYPE_INTERNAL);
+	return note_tree_insert(t, new_node, n + 1, entry, type, combine_notes);
+}
+
+/* Free the entire notes data contained in the given tree */
+static void note_tree_free(struct int_node *tree)
+{
+	unsigned int i;
+	for (i = 0; i < 16; i++) {
+		void *p = tree->a[i];
+		switch (GET_PTR_TYPE(p)) {
+		case PTR_TYPE_INTERNAL:
+			note_tree_free(CLR_PTR_TYPE(p));
+			/* fall through */
+		case PTR_TYPE_NOTE:
+		case PTR_TYPE_SUBTREE:
+			free(CLR_PTR_TYPE(p));
+		}
+	}
+}
+
+/*
+ * Convert a partial SHA1 hex string to the corresponding partial SHA1 value.
+ * - hex      - Partial SHA1 segment in ASCII hex format
+ * - hex_len  - Length of above segment. Must be multiple of 2 between 0 and 40
+ * - sha1     - Partial SHA1 value is written here
+ * - sha1_len - Max #bytes to store in sha1, Must be >= hex_len / 2, and < 20
+ * Returns -1 on error (invalid arguments or invalid SHA1 (not in hex format)).
+ * Otherwise, returns number of bytes written to sha1 (i.e. hex_len / 2).
+ * Pads sha1 with NULs up to sha1_len (not included in returned length).
+ */
+static int get_sha1_hex_segment(const char *hex, unsigned int hex_len,
+		unsigned char *sha1, unsigned int sha1_len)
+{
+	unsigned int i, len = hex_len >> 1;
+	if (hex_len % 2 != 0 || len > sha1_len)
+		return -1;
+	for (i = 0; i < len; i++) {
+		unsigned int val = (hexval(hex[0]) << 4) | hexval(hex[1]);
+		if (val & ~0xff)
+			return -1;
+		*sha1++ = val;
+		hex += 2;
+	}
+	for (; i < sha1_len; i++)
+		*sha1++ = 0;
+	return len;
+}
+
+static int non_note_cmp(const struct non_note *a, const struct non_note *b)
+{
+	return strcmp(a->path, b->path);
+}
+
+static void add_non_note(struct notes_tree *t, const char *path,
+		unsigned int mode, const unsigned char *sha1)
+{
+	struct non_note *p = t->prev_non_note, *n;
+	n = (struct non_note *) xmalloc(sizeof(struct non_note));
+	n->next = NULL;
+	n->path = xstrdup(path);
+	n->mode = mode;
+	hashcpy(n->sha1, sha1);
+	t->prev_non_note = n;
+
+	if (!t->first_non_note) {
+		t->first_non_note = n;
+		return;
+	}
+
+	if (non_note_cmp(p, n) < 0)
+		; /* do nothing  */
+	else if (non_note_cmp(t->first_non_note, n) <= 0)
+		p = t->first_non_note;
+	else {
+		/* n sorts before t->first_non_note */
+		n->next = t->first_non_note;
+		t->first_non_note = n;
+		return;
+	}
+
+	/* n sorts equal or after p */
+	while (p->next && non_note_cmp(p->next, n) <= 0)
+		p = p->next;
+
+	if (non_note_cmp(p, n) == 0) { /* n ~= p; overwrite p with n */
+		assert(strcmp(p->path, n->path) == 0);
+		p->mode = n->mode;
+		hashcpy(p->sha1, n->sha1);
+		free(n);
+		t->prev_non_note = p;
+		return;
+	}
+
+	/* n sorts between p and p->next */
+	n->next = p->next;
+	p->next = n;
+}
+
+static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,
+		struct int_node *node, unsigned int n)
+{
+	unsigned char object_sha1[20];
+	unsigned int prefix_len;
+	void *buf;
+	struct tree_desc desc;
+	struct name_entry entry;
+	int len, path_len;
+	unsigned char type;
+	struct leaf_node *l;
+
+	buf = fill_tree_descriptor(&desc, subtree->val_sha1);
+	if (!buf)
+		die("Could not read %s for notes-index",
+		     sha1_to_hex(subtree->val_sha1));
+
+	prefix_len = subtree->key_sha1[19];
+	assert(prefix_len * 2 >= n);
+	memcpy(object_sha1, subtree->key_sha1, prefix_len);
+	while (tree_entry(&desc, &entry)) {
+		path_len = strlen(entry.path);
+		len = get_sha1_hex_segment(entry.path, path_len,
+				object_sha1 + prefix_len, 20 - prefix_len);
+		if (len < 0)
+			goto handle_non_note; /* entry.path is not a SHA1 */
+		len += prefix_len;
+
+		/*
+		 * If object SHA1 is complete (len == 20), assume note object
+		 * If object SHA1 is incomplete (len < 20), and current
+		 * component consists of 2 hex chars, assume note subtree
+		 */
+		if (len <= 20) {
+			type = PTR_TYPE_NOTE;
+			l = (struct leaf_node *)
+				xcalloc(sizeof(struct leaf_node), 1);
+			hashcpy(l->key_sha1, object_sha1);
+			hashcpy(l->val_sha1, entry.sha1);
+			if (len < 20) {
+				if (!S_ISDIR(entry.mode) || path_len != 2)
+					goto handle_non_note; /* not subtree */
+				l->key_sha1[19] = (unsigned char) len;
+				type = PTR_TYPE_SUBTREE;
+			}
+			if (note_tree_insert(t, node, n, l, type,
+					     combine_notes_concatenate))
+				die("Failed to load %s %s into notes tree "
+				    "from %s",
+				    type == PTR_TYPE_NOTE ? "note" : "subtree",
+				    sha1_to_hex(l->key_sha1), t->ref);
+		}
+		continue;
+
+handle_non_note:
+		/*
+		 * Determine full path for this non-note entry:
+		 * The filename is already found in entry.path, but the
+		 * directory part of the path must be deduced from the subtree
+		 * containing this entry. We assume here that the overall notes
+		 * tree follows a strict byte-based progressive fanout
+		 * structure (i.e. using 2/38, 2/2/36, etc. fanouts, and not
+		 * e.g. 4/36 fanout). This means that if a non-note is found at
+		 * path "dead/beef", the following code will register it as
+		 * being found on "de/ad/beef".
+		 * On the other hand, if you use such non-obvious non-note
+		 * paths in the middle of a notes tree, you deserve what's
+		 * coming to you ;). Note that for non-notes that are not
+		 * SHA1-like at the top level, there will be no problems.
+		 *
+		 * To conclude, it is strongly advised to make sure non-notes
+		 * have at least one non-hex character in the top-level path
+		 * component.
+		 */
+		{
+			char non_note_path[PATH_MAX];
+			char *p = non_note_path;
+			const char *q = sha1_to_hex(subtree->key_sha1);
+			int i;
+			for (i = 0; i < prefix_len; i++) {
+				*p++ = *q++;
+				*p++ = *q++;
+				*p++ = '/';
+			}
+			strcpy(p, entry.path);
+			add_non_note(t, non_note_path, entry.mode, entry.sha1);
+		}
+	}
+	free(buf);
+}
+
+/*
+ * Determine optimal on-disk fanout for this part of the notes tree
+ *
+ * Given a (sub)tree and the level in the internal tree structure, determine
+ * whether or not the given existing fanout should be expanded for this
+ * (sub)tree.
+ *
+ * Values of the 'fanout' variable:
+ * - 0: No fanout (all notes are stored directly in the root notes tree)
+ * - 1: 2/38 fanout
+ * - 2: 2/2/36 fanout
+ * - 3: 2/2/2/34 fanout
+ * etc.
+ */
+static unsigned char determine_fanout(struct int_node *tree, unsigned char n,
+		unsigned char fanout)
+{
+	/*
+	 * The following is a simple heuristic that works well in practice:
+	 * For each even-numbered 16-tree level (remember that each on-disk
+	 * fanout level corresponds to _two_ 16-tree levels), peek at all 16
+	 * entries at that tree level. If all of them are either int_nodes or
+	 * subtree entries, then there are likely plenty of notes below this
+	 * level, so we return an incremented fanout.
+	 */
+	unsigned int i;
+	if ((n % 2) || (n > 2 * fanout))
+		return fanout;
+	for (i = 0; i < 16; i++) {
+		switch (GET_PTR_TYPE(tree->a[i])) {
+		case PTR_TYPE_SUBTREE:
+		case PTR_TYPE_INTERNAL:
+			continue;
+		default:
+			return fanout;
+		}
+	}
+	return fanout + 1;
+}
+
+static void construct_path_with_fanout(const unsigned char *sha1,
+		unsigned char fanout, char *path)
+{
+	unsigned int i = 0, j = 0;
+	const char *hex_sha1 = sha1_to_hex(sha1);
+	assert(fanout < 20);
+	while (fanout) {
+		path[i++] = hex_sha1[j++];
+		path[i++] = hex_sha1[j++];
+		path[i++] = '/';
+		fanout--;
+	}
+	strcpy(path + i, hex_sha1 + j);
+}
+
+static int for_each_note_helper(struct notes_tree *t, struct int_node *tree,
+		unsigned char n, unsigned char fanout, int flags,
+		each_note_fn fn, void *cb_data)
+{
+	unsigned int i;
+	void *p;
+	int ret = 0;
+	struct leaf_node *l;
+	static char path[40 + 19 + 1];  /* hex SHA1 + 19 * '/' + NUL */
+
+	fanout = determine_fanout(tree, n, fanout);
+	for (i = 0; i < 16; i++) {
+redo:
+		p = tree->a[i];
+		switch (GET_PTR_TYPE(p)) {
+		case PTR_TYPE_INTERNAL:
+			/* recurse into int_node */
+			ret = for_each_note_helper(t, CLR_PTR_TYPE(p), n + 1,
+				fanout, flags, fn, cb_data);
+			break;
+		case PTR_TYPE_SUBTREE:
+			l = (struct leaf_node *) CLR_PTR_TYPE(p);
+			/*
+			 * Subtree entries in the note tree represent parts of
+			 * the note tree that have not yet been explored. There
+			 * is a direct relationship between subtree entries at
+			 * level 'n' in the tree, and the 'fanout' variable:
+			 * Subtree entries at level 'n <= 2 * fanout' should be
+			 * preserved, since they correspond exactly to a fanout
+			 * directory in the on-disk structure. However, subtree
+			 * entries at level 'n > 2 * fanout' should NOT be
+			 * preserved, but rather consolidated into the above
+			 * notes tree level. We achieve this by unconditionally
+			 * unpacking subtree entries that exist below the
+			 * threshold level at 'n = 2 * fanout'.
+			 */
+			if (n <= 2 * fanout &&
+			    flags & FOR_EACH_NOTE_YIELD_SUBTREES) {
+				/* invoke callback with subtree */
+				unsigned int path_len =
+					l->key_sha1[19] * 2 + fanout;
+				assert(path_len < 40 + 19);
+				construct_path_with_fanout(l->key_sha1, fanout,
+							   path);
+				/* Create trailing slash, if needed */
+				if (path[path_len - 1] != '/')
+					path[path_len++] = '/';
+				path[path_len] = '\0';
+				ret = fn(l->key_sha1, l->val_sha1, path,
+					 cb_data);
+			}
+			if (n > fanout * 2 ||
+			    !(flags & FOR_EACH_NOTE_DONT_UNPACK_SUBTREES)) {
+				/* unpack subtree and resume traversal */
+				tree->a[i] = NULL;
+				load_subtree(t, l, tree, n);
+				free(l);
+				goto redo;
+			}
+			break;
+		case PTR_TYPE_NOTE:
+			l = (struct leaf_node *) CLR_PTR_TYPE(p);
+			construct_path_with_fanout(l->key_sha1, fanout, path);
+			ret = fn(l->key_sha1, l->val_sha1, path, cb_data);
+			break;
+		}
+		if (ret)
+			return ret;
+	}
+	return 0;
+}
+
+struct tree_write_stack {
+	struct tree_write_stack *next;
+	struct strbuf buf;
+	char path[2]; /* path to subtree in next, if any */
+};
+
+static inline int matches_tree_write_stack(struct tree_write_stack *tws,
+		const char *full_path)
+{
+	return  full_path[0] == tws->path[0] &&
+		full_path[1] == tws->path[1] &&
+		full_path[2] == '/';
+}
+
+static void write_tree_entry(struct strbuf *buf, unsigned int mode,
+		const char *path, unsigned int path_len, const
+		unsigned char *sha1)
+{
+	strbuf_addf(buf, "%o %.*s%c", mode, path_len, path, '\0');
+	strbuf_add(buf, sha1, 20);
+}
+
+static void tree_write_stack_init_subtree(struct tree_write_stack *tws,
+		const char *path)
+{
+	struct tree_write_stack *n;
+	assert(!tws->next);
+	assert(tws->path[0] == '\0' && tws->path[1] == '\0');
+	n = (struct tree_write_stack *)
+		xmalloc(sizeof(struct tree_write_stack));
+	n->next = NULL;
+	strbuf_init(&n->buf, 256 * (32 + 40)); /* assume 256 entries per tree */
+	n->path[0] = n->path[1] = '\0';
+	tws->next = n;
+	tws->path[0] = path[0];
+	tws->path[1] = path[1];
+}
+
+static int tree_write_stack_finish_subtree(struct tree_write_stack *tws)
+{
+	int ret;
+	struct tree_write_stack *n = tws->next;
+	unsigned char s[20];
+	if (n) {
+		ret = tree_write_stack_finish_subtree(n);
+		if (ret)
+			return ret;
+		ret = write_sha1_file(n->buf.buf, n->buf.len, tree_type, s);
+		if (ret)
+			return ret;
+		strbuf_release(&n->buf);
+		free(n);
+		tws->next = NULL;
+		write_tree_entry(&tws->buf, 040000, tws->path, 2, s);
+		tws->path[0] = tws->path[1] = '\0';
+	}
+	return 0;
+}
+
+static int write_each_note_helper(struct tree_write_stack *tws,
+		const char *path, unsigned int mode,
+		const unsigned char *sha1)
+{
+	size_t path_len = strlen(path);
+	unsigned int n = 0;
+	int ret;
+
+	/* Determine common part of tree write stack */
+	while (tws && 3 * n < path_len &&
+	       matches_tree_write_stack(tws, path + 3 * n)) {
+		n++;
+		tws = tws->next;
+	}
+
+	/* tws point to last matching tree_write_stack entry */
+	ret = tree_write_stack_finish_subtree(tws);
+	if (ret)
+		return ret;
+
+	/* Start subtrees needed to satisfy path */
+	while (3 * n + 2 < path_len && path[3 * n + 2] == '/') {
+		tree_write_stack_init_subtree(tws, path + 3 * n);
+		n++;
+		tws = tws->next;
+	}
+
+	/* There should be no more directory components in the given path */
+	assert(memchr(path + 3 * n, '/', path_len - (3 * n)) == NULL);
+
+	/* Finally add given entry to the current tree object */
+	write_tree_entry(&tws->buf, mode, path + 3 * n, path_len - (3 * n),
+			 sha1);
+
+	return 0;
+}
+
+struct write_each_note_data {
+	struct tree_write_stack *root;
+	struct non_note *next_non_note;
+};
+
+static int write_each_non_note_until(const char *note_path,
+		struct write_each_note_data *d)
+{
+	struct non_note *n = d->next_non_note;
+	int cmp = 0, ret;
+	while (n && (!note_path || (cmp = strcmp(n->path, note_path)) <= 0)) {
+		if (note_path && cmp == 0)
+			; /* do nothing, prefer note to non-note */
+		else {
+			ret = write_each_note_helper(d->root, n->path, n->mode,
+						     n->sha1);
+			if (ret)
+				return ret;
+		}
+		n = n->next;
+	}
+	d->next_non_note = n;
+	return 0;
+}
+
+static int write_each_note(const unsigned char *object_sha1,
+		const unsigned char *note_sha1, char *note_path,
+		void *cb_data)
+{
+	struct write_each_note_data *d =
+		(struct write_each_note_data *) cb_data;
+	size_t note_path_len = strlen(note_path);
+	unsigned int mode = 0100644;
+
+	if (note_path[note_path_len - 1] == '/') {
+		/* subtree entry */
+		note_path_len--;
+		note_path[note_path_len] = '\0';
+		mode = 040000;
+	}
+	assert(note_path_len <= 40 + 19);
+
+	/* Weave non-note entries into note entries */
+	return  write_each_non_note_until(note_path, d) ||
+		write_each_note_helper(d->root, note_path, mode, note_sha1);
+}
+
+struct note_delete_list {
+	struct note_delete_list *next;
+	const unsigned char *sha1;
+};
+
+static int prune_notes_helper(const unsigned char *object_sha1,
+		const unsigned char *note_sha1, char *note_path,
+		void *cb_data)
+{
+	struct note_delete_list **l = (struct note_delete_list **) cb_data;
+	struct note_delete_list *n;
+
+	if (has_sha1_file(object_sha1))
+		return 0; /* nothing to do for this note */
+
+	/* failed to find object => prune this note */
+	n = (struct note_delete_list *) xmalloc(sizeof(*n));
+	n->next = *l;
+	n->sha1 = object_sha1;
+	*l = n;
+	return 0;
+}
+
+int combine_notes_concatenate(unsigned char *cur_sha1,
+		const unsigned char *new_sha1)
+{
+	char *cur_msg = NULL, *new_msg = NULL, *buf;
+	unsigned long cur_len, new_len, buf_len;
+	enum object_type cur_type, new_type;
+	int ret;
+
+	/* read in both note blob objects */
+	if (!is_null_sha1(new_sha1))
+		new_msg = read_sha1_file(new_sha1, &new_type, &new_len);
+	if (!new_msg || !new_len || new_type != OBJ_BLOB) {
+		free(new_msg);
+		return 0;
+	}
+	if (!is_null_sha1(cur_sha1))
+		cur_msg = read_sha1_file(cur_sha1, &cur_type, &cur_len);
+	if (!cur_msg || !cur_len || cur_type != OBJ_BLOB) {
+		free(cur_msg);
+		free(new_msg);
+		hashcpy(cur_sha1, new_sha1);
+		return 0;
+	}
+
+	/* we will separate the notes by two newlines anyway */
+	if (cur_msg[cur_len - 1] == '\n')
+		cur_len--;
+
+	/* concatenate cur_msg and new_msg into buf */
+	buf_len = cur_len + 2 + new_len;
+	buf = (char *) xmalloc(buf_len);
+	memcpy(buf, cur_msg, cur_len);
+	buf[cur_len] = '\n';
+	buf[cur_len + 1] = '\n';
+	memcpy(buf + cur_len + 2, new_msg, new_len);
+	free(cur_msg);
+	free(new_msg);
+
+	/* create a new blob object from buf */
+	ret = write_sha1_file(buf, buf_len, blob_type, cur_sha1);
+	free(buf);
+	return ret;
+}
+
+int combine_notes_overwrite(unsigned char *cur_sha1,
+		const unsigned char *new_sha1)
+{
+	hashcpy(cur_sha1, new_sha1);
+	return 0;
+}
+
+int combine_notes_ignore(unsigned char *cur_sha1,
+		const unsigned char *new_sha1)
+{
+	return 0;
+}
+
+static int string_list_add_note_lines(struct string_list *sort_uniq_list,
+				      const unsigned char *sha1)
+{
+	char *data;
+	unsigned long len;
+	enum object_type t;
+	struct strbuf buf = STRBUF_INIT;
+	struct strbuf **lines = NULL;
+	int i, list_index;
+
+	if (is_null_sha1(sha1))
+		return 0;
+
+	/* read_sha1_file NUL-terminates */
+	data = read_sha1_file(sha1, &t, &len);
+	if (t != OBJ_BLOB || !data || !len) {
+		free(data);
+		return t != OBJ_BLOB || !data;
+	}
+
+	strbuf_attach(&buf, data, len, len + 1);
+	lines = strbuf_split(&buf, '\n');
+
+	for (i = 0; lines[i]; i++) {
+		if (lines[i]->buf[lines[i]->len - 1] == '\n')
+			strbuf_setlen(lines[i], lines[i]->len - 1);
+		if (!lines[i]->len)
+			continue; /* skip empty lines */
+		list_index = string_list_find_insert_index(sort_uniq_list,
+							   lines[i]->buf, 0);
+		if (list_index < 0)
+			continue; /* skip duplicate lines */
+		string_list_insert_at_index(sort_uniq_list, list_index,
+					    lines[i]->buf);
+	}
+
+	strbuf_list_free(lines);
+	strbuf_release(&buf);
+	return 0;
+}
+
+static int string_list_join_lines_helper(struct string_list_item *item,
+					 void *cb_data)
+{
+	struct strbuf *buf = cb_data;
+	strbuf_addstr(buf, item->string);
+	strbuf_addch(buf, '\n');
+	return 0;
+}
+
+int combine_notes_cat_sort_uniq(unsigned char *cur_sha1,
+		const unsigned char *new_sha1)
+{
+	struct string_list sort_uniq_list = { NULL, 0, 0, 1 };
+	struct strbuf buf = STRBUF_INIT;
+	int ret = 1;
+
+	/* read both note blob objects into unique_lines */
+	if (string_list_add_note_lines(&sort_uniq_list, cur_sha1))
+		goto out;
+	if (string_list_add_note_lines(&sort_uniq_list, new_sha1))
+		goto out;
+
+	/* create a new blob object from sort_uniq_list */
+	if (for_each_string_list(&sort_uniq_list,
+				 string_list_join_lines_helper, &buf))
+		goto out;
+
+	ret = write_sha1_file(buf.buf, buf.len, blob_type, cur_sha1);
+
+out:
+	strbuf_release(&buf);
+	string_list_clear(&sort_uniq_list, 0);
+	return ret;
+}
+
+static int string_list_add_one_ref(const char *path, const unsigned char *sha1,
+				   int flag, void *cb)
+{
+	struct string_list *refs = cb;
+	if (!unsorted_string_list_has_string(refs, path))
+		string_list_append(refs, path);
+	return 0;
+}
+
+void string_list_add_refs_by_glob(struct string_list *list, const char *glob)
+{
+	if (has_glob_specials(glob)) {
+		for_each_glob_ref(string_list_add_one_ref, glob, list);
+	} else {
+		unsigned char sha1[20];
+		if (get_sha1(glob, sha1))
+			warning("notes ref %s is invalid", glob);
+		if (!unsorted_string_list_has_string(list, glob))
+			string_list_append(list, glob);
+	}
+}
+
+void string_list_add_refs_from_colon_sep(struct string_list *list,
+					 const char *globs)
+{
+	struct strbuf globbuf = STRBUF_INIT;
+	struct strbuf **split;
+	int i;
+
+	strbuf_addstr(&globbuf, globs);
+	split = strbuf_split(&globbuf, ':');
+
+	for (i = 0; split[i]; i++) {
+		if (!split[i]->len)
+			continue;
+		if (split[i]->buf[split[i]->len-1] == ':')
+			strbuf_setlen(split[i], split[i]->len-1);
+		string_list_add_refs_by_glob(list, split[i]->buf);
+	}
+
+	strbuf_list_free(split);
+	strbuf_release(&globbuf);
+}
+
+static int notes_display_config(const char *k, const char *v, void *cb)
+{
+	int *load_refs = cb;
+
+	if (*load_refs && !strcmp(k, "notes.displayref")) {
+		if (!v)
+			config_error_nonbool(k);
+		string_list_add_refs_by_glob(&display_notes_refs, v);
+	}
+
+	return 0;
+}
+
+const char *default_notes_ref(void)
+{
+	const char *notes_ref = NULL;
+	if (!notes_ref)
+		notes_ref = getenv(GIT_NOTES_REF_ENVIRONMENT);
+	if (!notes_ref)
+		notes_ref = notes_ref_name; /* value of core.notesRef config */
+	if (!notes_ref)
+		notes_ref = GIT_NOTES_DEFAULT_REF;
+	return notes_ref;
+}
+
+void init_notes(struct notes_tree *t, const char *notes_ref,
+		combine_notes_fn combine_notes, int flags)
+{
+	unsigned char sha1[20], object_sha1[20];
+	unsigned mode;
+	struct leaf_node root_tree;
+
+	if (!t)
+		t = &default_notes_tree;
+	assert(!t->initialized);
+
+	if (!notes_ref)
+		notes_ref = default_notes_ref();
+
+	if (!combine_notes)
+		combine_notes = combine_notes_concatenate;
+
+	t->root = (struct int_node *) xcalloc(sizeof(struct int_node), 1);
+	t->first_non_note = NULL;
+	t->prev_non_note = NULL;
+	t->ref = notes_ref ? xstrdup(notes_ref) : NULL;
+	t->combine_notes = combine_notes;
+	t->initialized = 1;
+	t->dirty = 0;
+
+	if (flags & NOTES_INIT_EMPTY || !notes_ref ||
+	    read_ref(notes_ref, object_sha1))
+		return;
+	if (get_tree_entry(object_sha1, "", sha1, &mode))
+		die("Failed to read notes tree referenced by %s (%s)",
+		    notes_ref, sha1_to_hex(object_sha1));
+
+	hashclr(root_tree.key_sha1);
+	hashcpy(root_tree.val_sha1, sha1);
+	load_subtree(t, &root_tree, t->root, 0);
+}
+
+struct notes_tree **load_notes_trees(struct string_list *refs)
+{
+	struct string_list_item *item;
+	int counter = 0;
+	struct notes_tree **trees;
+	trees = xmalloc((refs->nr+1) * sizeof(struct notes_tree *));
+	for_each_string_list_item(item, refs) {
+		struct notes_tree *t = xcalloc(1, sizeof(struct notes_tree));
+		init_notes(t, item->string, combine_notes_ignore, 0);
+		trees[counter++] = t;
+	}
+	trees[counter] = NULL;
+	return trees;
+}
+
+void init_display_notes(struct display_notes_opt *opt)
+{
+	char *display_ref_env;
+	int load_config_refs = 0;
+	display_notes_refs.strdup_strings = 1;
+
+	assert(!display_notes_trees);
+
+	if (!opt || opt->use_default_notes > 0 ||
+	    (opt->use_default_notes == -1 && !opt->extra_notes_refs.nr)) {
+		string_list_append(&display_notes_refs, default_notes_ref());
+		display_ref_env = getenv(GIT_NOTES_DISPLAY_REF_ENVIRONMENT);
+		if (display_ref_env) {
+			string_list_add_refs_from_colon_sep(&display_notes_refs,
+							    display_ref_env);
+			load_config_refs = 0;
+		} else
+			load_config_refs = 1;
+	}
+
+	git_config(notes_display_config, &load_config_refs);
+
+	if (opt) {
+		struct string_list_item *item;
+		for_each_string_list_item(item, &opt->extra_notes_refs)
+			string_list_add_refs_by_glob(&display_notes_refs,
+						     item->string);
+	}
+
+	display_notes_trees = load_notes_trees(&display_notes_refs);
+	string_list_clear(&display_notes_refs, 0);
+}
+
+int add_note(struct notes_tree *t, const unsigned char *object_sha1,
+		const unsigned char *note_sha1, combine_notes_fn combine_notes)
+{
+	struct leaf_node *l;
+
+	if (!t)
+		t = &default_notes_tree;
+	assert(t->initialized);
+	t->dirty = 1;
+	if (!combine_notes)
+		combine_notes = t->combine_notes;
+	l = (struct leaf_node *) xmalloc(sizeof(struct leaf_node));
+	hashcpy(l->key_sha1, object_sha1);
+	hashcpy(l->val_sha1, note_sha1);
+	return note_tree_insert(t, t->root, 0, l, PTR_TYPE_NOTE, combine_notes);
+}
+
+int remove_note(struct notes_tree *t, const unsigned char *object_sha1)
+{
+	struct leaf_node l;
+
+	if (!t)
+		t = &default_notes_tree;
+	assert(t->initialized);
+	hashcpy(l.key_sha1, object_sha1);
+	hashclr(l.val_sha1);
+	note_tree_remove(t, t->root, 0, &l);
+	if (is_null_sha1(l.val_sha1)) /* no note was removed */
+		return 1;
+	t->dirty = 1;
+	return 0;
+}
+
+const unsigned char *get_note(struct notes_tree *t,
+		const unsigned char *object_sha1)
+{
+	struct leaf_node *found;
+
+	if (!t)
+		t = &default_notes_tree;
+	assert(t->initialized);
+	found = note_tree_find(t, t->root, 0, object_sha1);
+	return found ? found->val_sha1 : NULL;
+}
+
+int for_each_note(struct notes_tree *t, int flags, each_note_fn fn,
+		void *cb_data)
+{
+	if (!t)
+		t = &default_notes_tree;
+	assert(t->initialized);
+	return for_each_note_helper(t, t->root, 0, 0, flags, fn, cb_data);
+}
+
+int write_notes_tree(struct notes_tree *t, unsigned char *result)
+{
+	struct tree_write_stack root;
+	struct write_each_note_data cb_data;
+	int ret;
+
+	if (!t)
+		t = &default_notes_tree;
+	assert(t->initialized);
+
+	/* Prepare for traversal of current notes tree */
+	root.next = NULL; /* last forward entry in list is grounded */
+	strbuf_init(&root.buf, 256 * (32 + 40)); /* assume 256 entries */
+	root.path[0] = root.path[1] = '\0';
+	cb_data.root = &root;
+	cb_data.next_non_note = t->first_non_note;
+
+	/* Write tree objects representing current notes tree */
+	ret = for_each_note(t, FOR_EACH_NOTE_DONT_UNPACK_SUBTREES |
+				FOR_EACH_NOTE_YIELD_SUBTREES,
+			write_each_note, &cb_data) ||
+		write_each_non_note_until(NULL, &cb_data) ||
+		tree_write_stack_finish_subtree(&root) ||
+		write_sha1_file(root.buf.buf, root.buf.len, tree_type, result);
+	strbuf_release(&root.buf);
+	return ret;
+}
+
+void prune_notes(struct notes_tree *t, int flags)
+{
+	struct note_delete_list *l = NULL;
+
+	if (!t)
+		t = &default_notes_tree;
+	assert(t->initialized);
+
+	for_each_note(t, 0, prune_notes_helper, &l);
+
+	while (l) {
+		if (flags & NOTES_PRUNE_VERBOSE)
+			printf("%s\n", sha1_to_hex(l->sha1));
+		if (!(flags & NOTES_PRUNE_DRYRUN))
+			remove_note(t, l->sha1);
+		l = l->next;
+	}
+}
+
+void free_notes(struct notes_tree *t)
+{
+	if (!t)
+		t = &default_notes_tree;
+	if (t->root)
+		note_tree_free(t->root);
+	free(t->root);
+	while (t->first_non_note) {
+		t->prev_non_note = t->first_non_note->next;
+		free(t->first_non_note->path);
+		free(t->first_non_note);
+		t->first_non_note = t->prev_non_note;
+	}
+	free(t->ref);
+	memset(t, 0, sizeof(struct notes_tree));
+}
+
+void format_note(struct notes_tree *t, const unsigned char *object_sha1,
+		struct strbuf *sb, const char *output_encoding, int flags)
+{
+	static const char utf8[] = "utf-8";
+	const unsigned char *sha1;
+	char *msg, *msg_p;
+	unsigned long linelen, msglen;
+	enum object_type type;
+
+	if (!t)
+		t = &default_notes_tree;
+	if (!t->initialized)
+		init_notes(t, NULL, NULL, 0);
+
+	sha1 = get_note(t, object_sha1);
+	if (!sha1)
+		return;
+
+	if (!(msg = read_sha1_file(sha1, &type, &msglen)) || !msglen ||
+			type != OBJ_BLOB) {
+		free(msg);
+		return;
+	}
+
+	if (output_encoding && *output_encoding &&
+			strcmp(utf8, output_encoding)) {
+		char *reencoded = reencode_string(msg, output_encoding, utf8);
+		if (reencoded) {
+			free(msg);
+			msg = reencoded;
+			msglen = strlen(msg);
+		}
+	}
+
+	/* we will end the annotation by a newline anyway */
+	if (msglen && msg[msglen - 1] == '\n')
+		msglen--;
+
+	if (flags & NOTES_SHOW_HEADER) {
+		const char *ref = t->ref;
+		if (!ref || !strcmp(ref, GIT_NOTES_DEFAULT_REF)) {
+			strbuf_addstr(sb, "\nNotes:\n");
+		} else {
+			if (!prefixcmp(ref, "refs/"))
+				ref += 5;
+			if (!prefixcmp(ref, "notes/"))
+				ref += 6;
+			strbuf_addf(sb, "\nNotes (%s):\n", ref);
+		}
+	}
+
+	for (msg_p = msg; msg_p < msg + msglen; msg_p += linelen + 1) {
+		linelen = strchrnul(msg_p, '\n') - msg_p;
+
+		if (flags & NOTES_INDENT)
+			strbuf_addstr(sb, "    ");
+		strbuf_add(sb, msg_p, linelen);
+		strbuf_addch(sb, '\n');
+	}
+
+	free(msg);
+}
+
+void format_display_notes(const unsigned char *object_sha1,
+			  struct strbuf *sb, const char *output_encoding, int flags)
+{
+	int i;
+	assert(display_notes_trees);
+	for (i = 0; display_notes_trees[i]; i++)
+		format_note(display_notes_trees[i], object_sha1, sb,
+			    output_encoding, flags);
+}
+
+int copy_note(struct notes_tree *t,
+	      const unsigned char *from_obj, const unsigned char *to_obj,
+	      int force, combine_notes_fn combine_notes)
+{
+	const unsigned char *note = get_note(t, from_obj);
+	const unsigned char *existing_note = get_note(t, to_obj);
+
+	if (!force && existing_note)
+		return 1;
+
+	if (note)
+		return add_note(t, to_obj, note, combine_notes);
+	else if (existing_note)
+		return add_note(t, to_obj, null_sha1, combine_notes);
+
+	return 0;
+}
+
+void expand_notes_ref(struct strbuf *sb)
+{
+	if (!prefixcmp(sb->buf, "refs/notes/"))
+		return; /* we're happy */
+	else if (!prefixcmp(sb->buf, "notes/"))
+		strbuf_insert(sb, 0, "refs/", 5);
+	else
+		strbuf_insert(sb, 0, "refs/notes/", 11);
+}
diff --git a/notes.h b/notes.h
new file mode 100644
index 0000000..c716694
--- /dev/null
+++ b/notes.h
@@ -0,0 +1,315 @@
+#ifndef NOTES_H
+#define NOTES_H
+
+#include "string-list.h"
+
+/*
+ * Function type for combining two notes annotating the same object.
+ *
+ * When adding a new note annotating the same object as an existing note, it is
+ * up to the caller to decide how to combine the two notes. The decision is
+ * made by passing in a function of the following form. The function accepts
+ * two SHA1s -- of the existing note and the new note, respectively. The
+ * function then combines the notes in whatever way it sees fit, and writes the
+ * resulting SHA1 into the first SHA1 argument (cur_sha1). A non-zero return
+ * value indicates failure.
+ *
+ * The two given SHA1s shall both be non-NULL and different from each other.
+ * Either of them (but not both) may be == null_sha1, which indicates an
+ * empty/non-existent note. If the resulting SHA1 (cur_sha1) is == null_sha1,
+ * the note will be removed from the notes tree.
+ *
+ * The default combine_notes function (you get this when passing NULL) is
+ * combine_notes_concatenate(), which appends the contents of the new note to
+ * the contents of the existing note.
+ */
+typedef int (*combine_notes_fn)(unsigned char *cur_sha1, const unsigned char *new_sha1);
+
+/* Common notes combinators */
+int combine_notes_concatenate(unsigned char *cur_sha1, const unsigned char *new_sha1);
+int combine_notes_overwrite(unsigned char *cur_sha1, const unsigned char *new_sha1);
+int combine_notes_ignore(unsigned char *cur_sha1, const unsigned char *new_sha1);
+int combine_notes_cat_sort_uniq(unsigned char *cur_sha1, const unsigned char *new_sha1);
+
+/*
+ * Notes tree object
+ *
+ * Encapsulates the internal notes tree structure associated with a notes ref.
+ * Whenever a struct notes_tree pointer is required below, you may pass NULL in
+ * order to use the default/internal notes tree. E.g. you only need to pass a
+ * non-NULL value if you need to refer to several different notes trees
+ * simultaneously.
+ */
+extern struct notes_tree {
+	struct int_node *root;
+	struct non_note *first_non_note, *prev_non_note;
+	char *ref;
+	combine_notes_fn combine_notes;
+	int initialized;
+	int dirty;
+} default_notes_tree;
+
+/*
+ * Return the default notes ref.
+ *
+ * The default notes ref is the notes ref that is used when notes_ref == NULL
+ * is passed to init_notes().
+ *
+ * This the first of the following to be defined:
+ * 1. The '--ref' option to 'git notes', if given
+ * 2. The $GIT_NOTES_REF environment variable, if set
+ * 3. The value of the core.notesRef config variable, if set
+ * 4. GIT_NOTES_DEFAULT_REF (i.e. "refs/notes/commits")
+ */
+const char *default_notes_ref(void);
+
+/*
+ * Flags controlling behaviour of notes tree initialization
+ *
+ * Default behaviour is to initialize the notes tree from the tree object
+ * specified by the given (or default) notes ref.
+ */
+#define NOTES_INIT_EMPTY 1
+
+/*
+ * Initialize the given notes_tree with the notes tree structure at the given
+ * ref. If given ref is NULL, the value of the $GIT_NOTES_REF environment
+ * variable is used, and if that is missing, the default notes ref is used
+ * ("refs/notes/commits").
+ *
+ * If you need to re-intialize a notes_tree structure (e.g. when switching from
+ * one notes ref to another), you must first de-initialize the notes_tree
+ * structure by calling free_notes(struct notes_tree *).
+ *
+ * If you pass t == NULL, the default internal notes_tree will be initialized.
+ *
+ * The combine_notes function that is passed becomes the default combine_notes
+ * function for the given notes_tree. If NULL is passed, the default
+ * combine_notes function is combine_notes_concatenate().
+ *
+ * Precondition: The notes_tree structure is zeroed (this can be achieved with
+ * memset(t, 0, sizeof(struct notes_tree)))
+ */
+void init_notes(struct notes_tree *t, const char *notes_ref,
+		combine_notes_fn combine_notes, int flags);
+
+/*
+ * Add the given note object to the given notes_tree structure
+ *
+ * If there already exists a note for the given object_sha1, the given
+ * combine_notes function is invoked to break the tie. If not given (i.e.
+ * combine_notes == NULL), the default combine_notes function for the given
+ * notes_tree is used.
+ *
+ * Passing note_sha1 == null_sha1 indicates the addition of an
+ * empty/non-existent note. This is a (potentially expensive) no-op unless
+ * there already exists a note for the given object_sha1, AND combining that
+ * note with the empty note (using the given combine_notes function) results
+ * in a new/changed note.
+ *
+ * Returns zero on success; non-zero means combine_notes failed.
+ *
+ * IMPORTANT: The changes made by add_note() to the given notes_tree structure
+ * are not persistent until a subsequent call to write_notes_tree() returns
+ * zero.
+ */
+int add_note(struct notes_tree *t, const unsigned char *object_sha1,
+		const unsigned char *note_sha1, combine_notes_fn combine_notes);
+
+/*
+ * Remove the given note object from the given notes_tree structure
+ *
+ * IMPORTANT: The changes made by remove_note() to the given notes_tree
+ * structure are not persistent until a subsequent call to write_notes_tree()
+ * returns zero.
+ *
+ * Return 0 if a note was removed; 1 if there was no note to remove.
+ */
+int remove_note(struct notes_tree *t, const unsigned char *object_sha1);
+
+/*
+ * Get the note object SHA1 containing the note data for the given object
+ *
+ * Return NULL if the given object has no notes.
+ */
+const unsigned char *get_note(struct notes_tree *t,
+		const unsigned char *object_sha1);
+
+/*
+ * Copy a note from one object to another in the given notes_tree.
+ *
+ * Returns 1 if the to_obj already has a note and 'force' is false. Otherwise,
+ * returns non-zero if 'force' is true, but the given combine_notes function
+ * failed to combine from_obj's note with to_obj's existing note.
+ * Returns zero on success.
+ *
+ * IMPORTANT: The changes made by copy_note() to the given notes_tree structure
+ * are not persistent until a subsequent call to write_notes_tree() returns
+ * zero.
+ */
+int copy_note(struct notes_tree *t,
+	      const unsigned char *from_obj, const unsigned char *to_obj,
+	      int force, combine_notes_fn combine_notes);
+
+/*
+ * Flags controlling behaviour of for_each_note()
+ *
+ * Default behaviour of for_each_note() is to traverse every single note object
+ * in the given notes tree, unpacking subtree entries along the way.
+ * The following flags can be used to alter the default behaviour:
+ *
+ * - DONT_UNPACK_SUBTREES causes for_each_note() NOT to unpack and recurse into
+ *   subtree entries while traversing the notes tree. This causes notes within
+ *   those subtrees NOT to be passed to the callback. Use this flag if you
+ *   don't want to traverse _all_ notes, but only want to traverse the parts
+ *   of the notes tree that have already been unpacked (this includes at least
+ *   all notes that have been added/changed).
+ *
+ * - YIELD_SUBTREES causes any subtree entries that are encountered to be
+ *   passed to the callback, before recursing into them. Subtree entries are
+ *   not note objects, but represent intermediate directories in the notes
+ *   tree. When passed to the callback, subtree entries will have a trailing
+ *   slash in their path, which the callback may use to differentiate between
+ *   note entries and subtree entries. Note that already-unpacked subtree
+ *   entries are not part of the notes tree, and will therefore not be yielded.
+ *   If this flag is used together with DONT_UNPACK_SUBTREES, for_each_note()
+ *   will yield the subtree entry, but not recurse into it.
+ */
+#define FOR_EACH_NOTE_DONT_UNPACK_SUBTREES 1
+#define FOR_EACH_NOTE_YIELD_SUBTREES 2
+
+/*
+ * Invoke the specified callback function for each note in the given notes_tree
+ *
+ * If the callback returns nonzero, the note walk is aborted, and the return
+ * value from the callback is returned from for_each_note(). Hence, a zero
+ * return value from for_each_note() indicates that all notes were walked
+ * successfully.
+ *
+ * IMPORTANT: The callback function is NOT allowed to change the notes tree.
+ * In other words, the following functions can NOT be invoked (on the current
+ * notes tree) from within the callback:
+ * - add_note()
+ * - remove_note()
+ * - copy_note()
+ * - free_notes()
+ */
+typedef int each_note_fn(const unsigned char *object_sha1,
+		const unsigned char *note_sha1, char *note_path,
+		void *cb_data);
+int for_each_note(struct notes_tree *t, int flags, each_note_fn fn,
+		void *cb_data);
+
+/*
+ * Write the given notes_tree structure to the object database
+ *
+ * Creates a new tree object encapsulating the current state of the given
+ * notes_tree, and stores its SHA1 into the 'result' argument.
+ *
+ * Returns zero on success, non-zero on failure.
+ *
+ * IMPORTANT: Changes made to the given notes_tree are not persistent until
+ * this function has returned zero. Please also remember to create a
+ * corresponding commit object, and update the appropriate notes ref.
+ */
+int write_notes_tree(struct notes_tree *t, unsigned char *result);
+
+/* Flags controlling the operation of prune */
+#define NOTES_PRUNE_VERBOSE 1
+#define NOTES_PRUNE_DRYRUN 2
+/*
+ * Remove all notes annotating non-existing objects from the given notes tree
+ *
+ * All notes in the given notes_tree that are associated with objects that no
+ * longer exist in the database, are removed from the notes tree.
+ *
+ * IMPORTANT: The changes made by prune_notes() to the given notes_tree
+ * structure are not persistent until a subsequent call to write_notes_tree()
+ * returns zero.
+ */
+void prune_notes(struct notes_tree *t, int flags);
+
+/*
+ * Free (and de-initialize) the given notes_tree structure
+ *
+ * IMPORTANT: Changes made to the given notes_tree since the last, successful
+ * call to write_notes_tree() will be lost.
+ */
+void free_notes(struct notes_tree *t);
+
+/* Flags controlling how notes are formatted */
+#define NOTES_SHOW_HEADER 1
+#define NOTES_INDENT 2
+
+/*
+ * Fill the given strbuf with the notes associated with the given object.
+ *
+ * If the given notes_tree structure is not initialized, it will be auto-
+ * initialized to the default value (see documentation for init_notes() above).
+ * If the given notes_tree is NULL, the internal/default notes_tree will be
+ * used instead.
+ *
+ * 'flags' is a bitwise combination of the above formatting flags.
+ */
+void format_note(struct notes_tree *t, const unsigned char *object_sha1,
+		struct strbuf *sb, const char *output_encoding, int flags);
+
+
+struct string_list;
+
+struct display_notes_opt {
+	int use_default_notes;
+	struct string_list extra_notes_refs;
+};
+
+/*
+ * Load the notes machinery for displaying several notes trees.
+ *
+ * If 'opt' is not NULL, then it specifies additional settings for the
+ * displaying:
+ *
+ * - suppress_default_notes indicates that the notes from
+ *   core.notesRef and notes.displayRef should not be loaded.
+ *
+ * - extra_notes_refs may contain a list of globs (in the same style
+ *   as notes.displayRef) where notes should be loaded from.
+ */
+void init_display_notes(struct display_notes_opt *opt);
+
+/*
+ * Append notes for the given 'object_sha1' from all trees set up by
+ * init_display_notes() to 'sb'.  The 'flags' are a bitwise
+ * combination of
+ *
+ * - NOTES_SHOW_HEADER: add a 'Notes (refname):' header
+ *
+ * - NOTES_INDENT: indent the notes by 4 places
+ *
+ * You *must* call init_display_notes() before using this function.
+ */
+void format_display_notes(const unsigned char *object_sha1,
+			  struct strbuf *sb, const char *output_encoding, int flags);
+
+/*
+ * Load the notes tree from each ref listed in 'refs'.  The output is
+ * an array of notes_tree*, terminated by a NULL.
+ */
+struct notes_tree **load_notes_trees(struct string_list *refs);
+
+/*
+ * Add all refs that match 'glob' to the 'list'.
+ */
+void string_list_add_refs_by_glob(struct string_list *list, const char *glob);
+
+/*
+ * Add all refs from a colon-separated glob list 'globs' to the end of
+ * 'list'.  Empty components are ignored.  This helper is used to
+ * parse GIT_NOTES_DISPLAY_REF style environment variables.
+ */
+void string_list_add_refs_from_colon_sep(struct string_list *list,
+					 const char *globs);
+
+/* Expand inplace a note ref like "foo" or "notes/foo" into "refs/notes/foo" */
+void expand_notes_ref(struct strbuf *sb);
+
+#endif
diff --git a/object.c b/object.c
index a6ef439..4af3451 100644
--- a/object.c
+++ b/object.c
@@ -149,6 +149,8 @@
 		struct tree *tree = lookup_tree(sha1);
 		if (tree) {
 			obj = &tree->object;
+			if (!tree->buffer)
+				tree->object.parsed = 0;
 			if (!tree->object.parsed) {
 				if (parse_tree_buffer(tree, buffer, size))
 					return NULL;
@@ -174,7 +176,7 @@
 			obj = &tag->object;
 		}
 	} else {
-		warning("object %s has unknown type id %d\n", sha1_to_hex(sha1), type);
+		warning("object %s has unknown type id %d", sha1_to_hex(sha1), type);
 		obj = NULL;
 	}
 	if (obj && obj->type == OBJ_NONE)
@@ -188,13 +190,30 @@
 	unsigned long size;
 	enum object_type type;
 	int eaten;
-	void *buffer = read_sha1_file(sha1, &type, &size);
+	const unsigned char *repl = lookup_replace_object(sha1);
+	void *buffer;
+	struct object *obj;
 
+	obj = lookup_object(sha1);
+	if (obj && obj->parsed)
+		return obj;
+
+	if ((obj && obj->type == OBJ_BLOB) ||
+	    (!obj && has_sha1_file(sha1) &&
+	     sha1_object_info(sha1, NULL) == OBJ_BLOB)) {
+		if (check_sha1_signature(repl, NULL, 0, NULL) < 0) {
+			error("sha1 mismatch %s", sha1_to_hex(repl));
+			return NULL;
+		}
+		parse_blob_buffer(lookup_blob(sha1), NULL, 0);
+		return lookup_object(sha1);
+	}
+
+	buffer = read_sha1_file(sha1, &type, &size);
 	if (buffer) {
-		struct object *obj;
-		if (check_sha1_signature(sha1, buffer, size, typename(type)) < 0) {
+		if (check_sha1_signature(repl, buffer, size, typename(type)) < 0) {
 			free(buffer);
-			error("sha1 mismatch %s\n", sha1_to_hex(sha1));
+			error("sha1 mismatch %s", sha1_to_hex(repl));
 			return NULL;
 		}
 
@@ -210,31 +229,10 @@
 				       struct object_list **list_p)
 {
 	struct object_list *new_list = xmalloc(sizeof(struct object_list));
-        new_list->item = item;
-        new_list->next = *list_p;
-        *list_p = new_list;
-        return new_list;
-}
-
-void object_list_append(struct object *item,
-			struct object_list **list_p)
-{
-	while (*list_p) {
-		list_p = &((*list_p)->next);
-	}
-	*list_p = xmalloc(sizeof(struct object_list));
-	(*list_p)->next = NULL;
-	(*list_p)->item = item;
-}
-
-unsigned object_list_length(struct object_list *list)
-{
-	unsigned ret = 0;
-	while (list) {
-		list = list->next;
-		ret++;
-	}
-	return ret;
+	new_list->item = item;
+	new_list->next = *list_p;
+	*list_p = new_list;
+	return new_list;
 }
 
 int object_list_contains(struct object_list *list, struct object *obj)
@@ -272,10 +270,10 @@
 
 void object_array_remove_duplicates(struct object_array *array)
 {
-	int ref, src, dst;
+	unsigned int ref, src, dst;
 	struct object_array_entry *objects = array->objects;
 
-	for (ref = 0; ref < array->nr - 1; ref++) {
+	for (ref = 0; ref + 1 < array->nr; ref++) {
 		for (src = ref + 1, dst = src;
 		     src < array->nr;
 		     src++) {
@@ -288,3 +286,14 @@
 		array->nr = dst;
 	}
 }
+
+void clear_object_flags(unsigned flags)
+{
+	int i;
+
+	for (i=0; i < obj_hash_size; i++) {
+		struct object *obj = obj_hash[i];
+		if (obj)
+			obj->flags &= ~flags;
+	}
+}
diff --git a/object.h b/object.h
index 89dd0c4..6a97b6b 100644
--- a/object.h
+++ b/object.h
@@ -6,11 +6,6 @@
 	struct object_list *next;
 };
 
-struct object_refs {
-	unsigned count;
-	struct object *ref[FLEX_ARRAY]; /* more */
-};
-
 struct object_array {
 	unsigned int nr;
 	unsigned int alloc;
@@ -21,6 +16,8 @@
 	} *objects;
 };
 
+#define OBJECT_ARRAY_INIT { 0, 0, NULL }
+
 #define TYPE_BITS   3
 #define FLAG_BITS  27
 
@@ -72,11 +69,6 @@
 struct object_list *object_list_insert(struct object *item,
 				       struct object_list **list_p);
 
-void object_list_append(struct object *item,
-			struct object_list **list_p);
-
-unsigned object_list_length(struct object_list *list);
-
 int object_list_contains(struct object_list *list, struct object *obj);
 
 /* Object array handling .. */
@@ -84,4 +76,6 @@
 void add_object_array_with_mode(struct object *obj, const char *name, struct object_array *array, unsigned mode);
 void object_array_remove_duplicates(struct object_array *);
 
+void clear_object_flags(unsigned flags);
+
 #endif /* OBJECT_H */
diff --git a/pack-check.c b/pack-check.c
index 166ca70..63a595c 100644
--- a/pack-check.c
+++ b/pack-check.c
@@ -1,9 +1,9 @@
 #include "cache.h"
 #include "pack.h"
 #include "pack-revindex.h"
+#include "progress.h"
 
-struct idx_entry
-{
+struct idx_entry {
 	off_t                offset;
 	const unsigned char *sha1;
 	unsigned int nr;
@@ -24,10 +24,10 @@
 		   off_t offset, off_t len, unsigned int nr)
 {
 	const uint32_t *index_crc;
-	uint32_t data_crc = crc32(0, Z_NULL, 0);
+	uint32_t data_crc = crc32(0, NULL, 0);
 
 	do {
-		unsigned int avail;
+		unsigned long avail;
 		void *data = use_pack(p, w_curs, offset, &avail);
 		if (avail > len)
 			avail = len;
@@ -43,7 +43,10 @@
 }
 
 static int verify_packfile(struct packed_git *p,
-		struct pack_window **w_curs)
+			   struct pack_window **w_curs,
+			   verify_fn fn,
+			   struct progress *progress, uint32_t base_count)
+
 {
 	off_t index_size = p->index_size;
 	const unsigned char *index_base = p->index_data;
@@ -62,7 +65,7 @@
 
 	git_SHA1_Init(&ctx);
 	do {
-		unsigned int remaining;
+		unsigned long remaining;
 		unsigned char *in = use_pack(p, w_curs, offset, &remaining);
 		offset += remaining;
 		if (!pack_sig_ofs)
@@ -77,7 +80,7 @@
 		err = error("%s SHA1 checksum mismatch",
 			    p->pack_name);
 	if (hashcmp(index_base + index_size - 40, pack_sig))
-		err = error("%s SHA1 does not match its inddex",
+		err = error("%s SHA1 does not match its index",
 			    p->pack_name);
 	unuse_pack(w_curs);
 
@@ -114,33 +117,37 @@
 					    p->pack_name, (uintmax_t)offset);
 		}
 		data = unpack_entry(p, entries[i].offset, &type, &size);
-		if (!data) {
+		if (!data)
 			err = error("cannot unpack %s from %s at offset %"PRIuMAX"",
 				    sha1_to_hex(entries[i].sha1), p->pack_name,
 				    (uintmax_t)entries[i].offset);
-			break;
-		}
-		if (check_sha1_signature(entries[i].sha1, data, size, typename(type))) {
+		else if (check_sha1_signature(entries[i].sha1, data, size, typename(type)))
 			err = error("packed %s from %s is corrupt",
 				    sha1_to_hex(entries[i].sha1), p->pack_name);
-			free(data);
-			break;
+		else if (fn) {
+			int eaten = 0;
+			fn(entries[i].sha1, type, size, data, &eaten);
+			if (eaten)
+				data = NULL;
 		}
+		if (((base_count + i) & 1023) == 0)
+			display_progress(progress, base_count + i);
 		free(data);
+
 	}
+	display_progress(progress, base_count + i);
 	free(entries);
 
 	return err;
 }
 
-int verify_pack(struct packed_git *p)
+int verify_pack_index(struct packed_git *p)
 {
 	off_t index_size;
 	const unsigned char *index_base;
 	git_SHA_CTX ctx;
 	unsigned char sha1[20];
 	int err = 0;
-	struct pack_window *w_curs = NULL;
 
 	if (open_pack_index(p))
 		return error("packfile %s index not opened", p->pack_name);
@@ -154,9 +161,20 @@
 	if (hashcmp(sha1, index_base + index_size - 20))
 		err = error("Packfile index for %s SHA1 mismatch",
 			    p->pack_name);
+	return err;
+}
 
-	/* Verify pack file */
-	err |= verify_packfile(p, &w_curs);
+int verify_pack(struct packed_git *p, verify_fn fn,
+		struct progress *progress, uint32_t base_count)
+{
+	int err = 0;
+	struct pack_window *w_curs = NULL;
+
+	err |= verify_pack_index(p);
+	if (!p->index_data)
+		return -1;
+
+	err |= verify_packfile(p, &w_curs, fn, progress, base_count);
 	unuse_pack(&w_curs);
 
 	return err;
diff --git a/pack-redundant.c b/pack-redundant.c
deleted file mode 100644
index 48a12bc..0000000
--- a/pack-redundant.c
+++ /dev/null
@@ -1,698 +0,0 @@
-/*
-*
-* Copyright 2005, Lukas Sandstrom <lukass@etek.chalmers.se>
-*
-* This file is licensed under the GPL v2.
-*
-*/
-
-#include "cache.h"
-#include "exec_cmd.h"
-
-#define BLKSIZE 512
-
-static const char pack_redundant_usage[] =
-"git pack-redundant [ --verbose ] [ --alt-odb ] < --all | <.pack filename> ...>";
-
-static int load_all_packs, verbose, alt_odb;
-
-struct llist_item {
-	struct llist_item *next;
-	const unsigned char *sha1;
-};
-static struct llist {
-	struct llist_item *front;
-	struct llist_item *back;
-	size_t size;
-} *all_objects; /* all objects which must be present in local packfiles */
-
-static struct pack_list {
-	struct pack_list *next;
-	struct packed_git *pack;
-	struct llist *unique_objects;
-	struct llist *all_objects;
-} *local_packs = NULL, *altodb_packs = NULL;
-
-struct pll {
-	struct pll *next;
-	struct pack_list *pl;
-};
-
-static struct llist_item *free_nodes;
-
-static inline void llist_item_put(struct llist_item *item)
-{
-	item->next = free_nodes;
-	free_nodes = item;
-}
-
-static inline struct llist_item *llist_item_get(void)
-{
-	struct llist_item *new;
-	if ( free_nodes ) {
-		new = free_nodes;
-		free_nodes = free_nodes->next;
-	} else {
-		int i = 1;
-		new = xmalloc(sizeof(struct llist_item) * BLKSIZE);
-		for(;i < BLKSIZE; i++) {
-			llist_item_put(&new[i]);
-		}
-	}
-	return new;
-}
-
-static void llist_free(struct llist *list)
-{
-	while((list->back = list->front)) {
-		list->front = list->front->next;
-		llist_item_put(list->back);
-	}
-	free(list);
-}
-
-static inline void llist_init(struct llist **list)
-{
-	*list = xmalloc(sizeof(struct llist));
-	(*list)->front = (*list)->back = NULL;
-	(*list)->size = 0;
-}
-
-static struct llist * llist_copy(struct llist *list)
-{
-	struct llist *ret;
-	struct llist_item *new, *old, *prev;
-
-	llist_init(&ret);
-
-	if ((ret->size = list->size) == 0)
-		return ret;
-
-	new = ret->front = llist_item_get();
-	new->sha1 = list->front->sha1;
-
-	old = list->front->next;
-	while (old) {
-		prev = new;
-		new = llist_item_get();
-		prev->next = new;
-		new->sha1 = old->sha1;
-		old = old->next;
-	}
-	new->next = NULL;
-	ret->back = new;
-
-	return ret;
-}
-
-static inline struct llist_item *llist_insert(struct llist *list,
-					      struct llist_item *after,
-					       const unsigned char *sha1)
-{
-	struct llist_item *new = llist_item_get();
-	new->sha1 = sha1;
-	new->next = NULL;
-
-	if (after != NULL) {
-		new->next = after->next;
-		after->next = new;
-		if (after == list->back)
-			list->back = new;
-	} else {/* insert in front */
-		if (list->size == 0)
-			list->back = new;
-		else
-			new->next = list->front;
-		list->front = new;
-	}
-	list->size++;
-	return new;
-}
-
-static inline struct llist_item *llist_insert_back(struct llist *list,
-						   const unsigned char *sha1)
-{
-	return llist_insert(list, list->back, sha1);
-}
-
-static inline struct llist_item *llist_insert_sorted_unique(struct llist *list,
-			const unsigned char *sha1, struct llist_item *hint)
-{
-	struct llist_item *prev = NULL, *l;
-
-	l = (hint == NULL) ? list->front : hint;
-	while (l) {
-		int cmp = hashcmp(l->sha1, sha1);
-		if (cmp > 0) { /* we insert before this entry */
-			return llist_insert(list, prev, sha1);
-		}
-		if(!cmp) { /* already exists */
-			return l;
-		}
-		prev = l;
-		l = l->next;
-	}
-	/* insert at the end */
-	return llist_insert_back(list, sha1);
-}
-
-/* returns a pointer to an item in front of sha1 */
-static inline struct llist_item * llist_sorted_remove(struct llist *list, const unsigned char *sha1, struct llist_item *hint)
-{
-	struct llist_item *prev, *l;
-
-redo_from_start:
-	l = (hint == NULL) ? list->front : hint;
-	prev = NULL;
-	while (l) {
-		int cmp = hashcmp(l->sha1, sha1);
-		if (cmp > 0) /* not in list, since sorted */
-			return prev;
-		if(!cmp) { /* found */
-			if (prev == NULL) {
-				if (hint != NULL && hint != list->front) {
-					/* we don't know the previous element */
-					hint = NULL;
-					goto redo_from_start;
-				}
-				list->front = l->next;
-			} else
-				prev->next = l->next;
-			if (l == list->back)
-				list->back = prev;
-			llist_item_put(l);
-			list->size--;
-			return prev;
-		}
-		prev = l;
-		l = l->next;
-	}
-	return prev;
-}
-
-/* computes A\B */
-static void llist_sorted_difference_inplace(struct llist *A,
-				     struct llist *B)
-{
-	struct llist_item *hint, *b;
-
-	hint = NULL;
-	b = B->front;
-
-	while (b) {
-		hint = llist_sorted_remove(A, b->sha1, hint);
-		b = b->next;
-	}
-}
-
-static inline struct pack_list * pack_list_insert(struct pack_list **pl,
-					   struct pack_list *entry)
-{
-	struct pack_list *p = xmalloc(sizeof(struct pack_list));
-	memcpy(p, entry, sizeof(struct pack_list));
-	p->next = *pl;
-	*pl = p;
-	return p;
-}
-
-static inline size_t pack_list_size(struct pack_list *pl)
-{
-	size_t ret = 0;
-	while(pl) {
-		ret++;
-		pl = pl->next;
-	}
-	return ret;
-}
-
-static struct pack_list * pack_list_difference(const struct pack_list *A,
-					       const struct pack_list *B)
-{
-	struct pack_list *ret;
-	const struct pack_list *pl;
-
-	if (A == NULL)
-		return NULL;
-
-	pl = B;
-	while (pl != NULL) {
-		if (A->pack == pl->pack)
-			return pack_list_difference(A->next, B);
-		pl = pl->next;
-	}
-	ret = xmalloc(sizeof(struct pack_list));
-	memcpy(ret, A, sizeof(struct pack_list));
-	ret->next = pack_list_difference(A->next, B);
-	return ret;
-}
-
-static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)
-{
-	unsigned long p1_off = 0, p2_off = 0, p1_step, p2_step;
-	const unsigned char *p1_base, *p2_base;
-	struct llist_item *p1_hint = NULL, *p2_hint = NULL;
-
-	p1_base = p1->pack->index_data;
-	p2_base = p2->pack->index_data;
-	p1_base += 256 * 4 + ((p1->pack->index_version < 2) ? 4 : 8);
-	p2_base += 256 * 4 + ((p2->pack->index_version < 2) ? 4 : 8);
-	p1_step = (p1->pack->index_version < 2) ? 24 : 20;
-	p2_step = (p2->pack->index_version < 2) ? 24 : 20;
-
-	while (p1_off < p1->pack->num_objects * p1_step &&
-	       p2_off < p2->pack->num_objects * p2_step)
-	{
-		int cmp = hashcmp(p1_base + p1_off, p2_base + p2_off);
-		/* cmp ~ p1 - p2 */
-		if (cmp == 0) {
-			p1_hint = llist_sorted_remove(p1->unique_objects,
-					p1_base + p1_off, p1_hint);
-			p2_hint = llist_sorted_remove(p2->unique_objects,
-					p1_base + p1_off, p2_hint);
-			p1_off += p1_step;
-			p2_off += p2_step;
-			continue;
-		}
-		if (cmp < 0) { /* p1 has the object, p2 doesn't */
-			p1_off += p1_step;
-		} else { /* p2 has the object, p1 doesn't */
-			p2_off += p2_step;
-		}
-	}
-}
-
-static void pll_free(struct pll *l)
-{
-	struct pll *old;
-	struct pack_list *opl;
-
-	while (l) {
-		old = l;
-		while (l->pl) {
-			opl = l->pl;
-			l->pl = opl->next;
-			free(opl);
-		}
-		l = l->next;
-		free(old);
-	}
-}
-
-/* all the permutations have to be free()d at the same time,
- * since they refer to each other
- */
-static struct pll * get_permutations(struct pack_list *list, int n)
-{
-	struct pll *subset, *ret = NULL, *new_pll = NULL, *pll;
-
-	if (list == NULL || pack_list_size(list) < n || n == 0)
-		return NULL;
-
-	if (n == 1) {
-		while (list) {
-			new_pll = xmalloc(sizeof(pll));
-			new_pll->pl = NULL;
-			pack_list_insert(&new_pll->pl, list);
-			new_pll->next = ret;
-			ret = new_pll;
-			list = list->next;
-		}
-		return ret;
-	}
-
-	while (list->next) {
-		subset = get_permutations(list->next, n - 1);
-		while (subset) {
-			new_pll = xmalloc(sizeof(pll));
-			new_pll->pl = subset->pl;
-			pack_list_insert(&new_pll->pl, list);
-			new_pll->next = ret;
-			ret = new_pll;
-			subset = subset->next;
-		}
-		list = list->next;
-	}
-	return ret;
-}
-
-static int is_superset(struct pack_list *pl, struct llist *list)
-{
-	struct llist *diff;
-
-	diff = llist_copy(list);
-
-	while (pl) {
-		llist_sorted_difference_inplace(diff, pl->all_objects);
-		if (diff->size == 0) { /* we're done */
-			llist_free(diff);
-			return 1;
-		}
-		pl = pl->next;
-	}
-	llist_free(diff);
-	return 0;
-}
-
-static size_t sizeof_union(struct packed_git *p1, struct packed_git *p2)
-{
-	size_t ret = 0;
-	unsigned long p1_off = 0, p2_off = 0, p1_step, p2_step;
-	const unsigned char *p1_base, *p2_base;
-
-	p1_base = p1->index_data;
-	p2_base = p2->index_data;
-	p1_base += 256 * 4 + ((p1->index_version < 2) ? 4 : 8);
-	p2_base += 256 * 4 + ((p2->index_version < 2) ? 4 : 8);
-	p1_step = (p1->index_version < 2) ? 24 : 20;
-	p2_step = (p2->index_version < 2) ? 24 : 20;
-
-	while (p1_off < p1->num_objects * p1_step &&
-	       p2_off < p2->num_objects * p2_step)
-	{
-		int cmp = hashcmp(p1_base + p1_off, p2_base + p2_off);
-		/* cmp ~ p1 - p2 */
-		if (cmp == 0) {
-			ret++;
-			p1_off += p1_step;
-			p2_off += p2_step;
-			continue;
-		}
-		if (cmp < 0) { /* p1 has the object, p2 doesn't */
-			p1_off += p1_step;
-		} else { /* p2 has the object, p1 doesn't */
-			p2_off += p2_step;
-		}
-	}
-	return ret;
-}
-
-/* another O(n^2) function ... */
-static size_t get_pack_redundancy(struct pack_list *pl)
-{
-	struct pack_list *subset;
-	size_t ret = 0;
-
-	if (pl == NULL)
-		return 0;
-
-	while ((subset = pl->next)) {
-		while(subset) {
-			ret += sizeof_union(pl->pack, subset->pack);
-			subset = subset->next;
-		}
-		pl = pl->next;
-	}
-	return ret;
-}
-
-static inline off_t pack_set_bytecount(struct pack_list *pl)
-{
-	off_t ret = 0;
-	while (pl) {
-		ret += pl->pack->pack_size;
-		ret += pl->pack->index_size;
-		pl = pl->next;
-	}
-	return ret;
-}
-
-static void minimize(struct pack_list **min)
-{
-	struct pack_list *pl, *unique = NULL,
-		*non_unique = NULL, *min_perm = NULL;
-	struct pll *perm, *perm_all, *perm_ok = NULL, *new_perm;
-	struct llist *missing;
-	off_t min_perm_size = 0, perm_size;
-	int n;
-
-	pl = local_packs;
-	while (pl) {
-		if(pl->unique_objects->size)
-			pack_list_insert(&unique, pl);
-		else
-			pack_list_insert(&non_unique, pl);
-		pl = pl->next;
-	}
-	/* find out which objects are missing from the set of unique packs */
-	missing = llist_copy(all_objects);
-	pl = unique;
-	while (pl) {
-		llist_sorted_difference_inplace(missing, pl->all_objects);
-		pl = pl->next;
-	}
-
-	/* return if there are no objects missing from the unique set */
-	if (missing->size == 0) {
-		*min = unique;
-		return;
-	}
-
-	/* find the permutations which contain all missing objects */
-	for (n = 1; n <= pack_list_size(non_unique) && !perm_ok; n++) {
-		perm_all = perm = get_permutations(non_unique, n);
-		while (perm) {
-			if (is_superset(perm->pl, missing)) {
-				new_perm = xmalloc(sizeof(struct pll));
-				memcpy(new_perm, perm, sizeof(struct pll));
-				new_perm->next = perm_ok;
-				perm_ok = new_perm;
-			}
-			perm = perm->next;
-		}
-		if (perm_ok)
-			break;
-		pll_free(perm_all);
-	}
-	if (perm_ok == NULL)
-		die("Internal error: No complete sets found!");
-
-	/* find the permutation with the smallest size */
-	perm = perm_ok;
-	while (perm) {
-		perm_size = pack_set_bytecount(perm->pl);
-		if (!min_perm_size || min_perm_size > perm_size) {
-			min_perm_size = perm_size;
-			min_perm = perm->pl;
-		}
-		perm = perm->next;
-	}
-	*min = min_perm;
-	/* add the unique packs to the list */
-	pl = unique;
-	while(pl) {
-		pack_list_insert(min, pl);
-		pl = pl->next;
-	}
-}
-
-static void load_all_objects(void)
-{
-	struct pack_list *pl = local_packs;
-	struct llist_item *hint, *l;
-
-	llist_init(&all_objects);
-
-	while (pl) {
-		hint = NULL;
-		l = pl->all_objects->front;
-		while (l) {
-			hint = llist_insert_sorted_unique(all_objects,
-							  l->sha1, hint);
-			l = l->next;
-		}
-		pl = pl->next;
-	}
-	/* remove objects present in remote packs */
-	pl = altodb_packs;
-	while (pl) {
-		llist_sorted_difference_inplace(all_objects, pl->all_objects);
-		pl = pl->next;
-	}
-}
-
-/* this scales like O(n^2) */
-static void cmp_local_packs(void)
-{
-	struct pack_list *subset, *pl = local_packs;
-
-	while ((subset = pl)) {
-		while((subset = subset->next))
-			cmp_two_packs(pl, subset);
-		pl = pl->next;
-	}
-}
-
-static void scan_alt_odb_packs(void)
-{
-	struct pack_list *local, *alt;
-
-	alt = altodb_packs;
-	while (alt) {
-		local = local_packs;
-		while (local) {
-			llist_sorted_difference_inplace(local->unique_objects,
-							alt->all_objects);
-			local = local->next;
-		}
-		llist_sorted_difference_inplace(all_objects, alt->all_objects);
-		alt = alt->next;
-	}
-}
-
-static struct pack_list * add_pack(struct packed_git *p)
-{
-	struct pack_list l;
-	unsigned long off = 0, step;
-	const unsigned char *base;
-
-	if (!p->pack_local && !(alt_odb || verbose))
-		return NULL;
-
-	l.pack = p;
-	llist_init(&l.all_objects);
-
-	if (open_pack_index(p))
-		return NULL;
-
-	base = p->index_data;
-	base += 256 * 4 + ((p->index_version < 2) ? 4 : 8);
-	step = (p->index_version < 2) ? 24 : 20;
-	while (off < p->num_objects * step) {
-		llist_insert_back(l.all_objects, base + off);
-		off += step;
-	}
-	/* this list will be pruned in cmp_two_packs later */
-	l.unique_objects = llist_copy(l.all_objects);
-	if (p->pack_local)
-		return pack_list_insert(&local_packs, &l);
-	else
-		return pack_list_insert(&altodb_packs, &l);
-}
-
-static struct pack_list * add_pack_file(char *filename)
-{
-	struct packed_git *p = packed_git;
-
-	if (strlen(filename) < 40)
-		die("Bad pack filename: %s", filename);
-
-	while (p) {
-		if (strstr(p->pack_name, filename))
-			return add_pack(p);
-		p = p->next;
-	}
-	die("Filename %s not found in packed_git", filename);
-}
-
-static void load_all(void)
-{
-	struct packed_git *p = packed_git;
-
-	while (p) {
-		add_pack(p);
-		p = p->next;
-	}
-}
-
-int main(int argc, char **argv)
-{
-	int i;
-	struct pack_list *min, *red, *pl;
-	struct llist *ignore;
-	unsigned char *sha1;
-	char buf[42]; /* 40 byte sha1 + \n + \0 */
-
-	git_extract_argv0_path(argv[0]);
-
-	setup_git_directory();
-
-	for (i = 1; i < argc; i++) {
-		const char *arg = argv[i];
-		if(!strcmp(arg, "--")) {
-			i++;
-			break;
-		}
-		if(!strcmp(arg, "--all")) {
-			load_all_packs = 1;
-			continue;
-		}
-		if(!strcmp(arg, "--verbose")) {
-			verbose = 1;
-			continue;
-		}
-		if(!strcmp(arg, "--alt-odb")) {
-			alt_odb = 1;
-			continue;
-		}
-		if(*arg == '-')
-			usage(pack_redundant_usage);
-		else
-			break;
-	}
-
-	prepare_packed_git();
-
-	if (load_all_packs)
-		load_all();
-	else
-		while (*(argv + i) != NULL)
-			add_pack_file(*(argv + i++));
-
-	if (local_packs == NULL)
-		die("Zero packs found!");
-
-	load_all_objects();
-
-	cmp_local_packs();
-	if (alt_odb)
-		scan_alt_odb_packs();
-
-	/* ignore objects given on stdin */
-	llist_init(&ignore);
-	if (!isatty(0)) {
-		while (fgets(buf, sizeof(buf), stdin)) {
-			sha1 = xmalloc(20);
-			if (get_sha1_hex(buf, sha1))
-				die("Bad sha1 on stdin: %s", buf);
-			llist_insert_sorted_unique(ignore, sha1, NULL);
-		}
-	}
-	llist_sorted_difference_inplace(all_objects, ignore);
-	pl = local_packs;
-	while (pl) {
-		llist_sorted_difference_inplace(pl->unique_objects, ignore);
-		pl = pl->next;
-	}
-
-	minimize(&min);
-
-	if (verbose) {
-		fprintf(stderr, "There are %lu packs available in alt-odbs.\n",
-			(unsigned long)pack_list_size(altodb_packs));
-		fprintf(stderr, "The smallest (bytewise) set of packs is:\n");
-		pl = min;
-		while (pl) {
-			fprintf(stderr, "\t%s\n", pl->pack->pack_name);
-			pl = pl->next;
-		}
-		fprintf(stderr, "containing %lu duplicate objects "
-				"with a total size of %lukb.\n",
-			(unsigned long)get_pack_redundancy(min),
-			(unsigned long)pack_set_bytecount(min)/1024);
-		fprintf(stderr, "A total of %lu unique objects were considered.\n",
-			(unsigned long)all_objects->size);
-		fprintf(stderr, "Redundant packs (with indexes):\n");
-	}
-	pl = red = pack_list_difference(local_packs, min);
-	while (pl) {
-		printf("%s\n%s\n",
-		       sha1_pack_index_name(pl->pack->sha1),
-		       pl->pack->pack_name);
-		pl = pl->next;
-	}
-	if (verbose)
-		fprintf(stderr, "%luMB of redundant packs in total.\n",
-			(unsigned long)pack_set_bytecount(red)/(1024*1024));
-
-	return 0;
-}
diff --git a/pack-refs.c b/pack-refs.c
index 7f43f8a..f09a054 100644
--- a/pack-refs.c
+++ b/pack-refs.c
@@ -60,6 +60,37 @@
 	return 0;
 }
 
+/*
+ * Remove empty parents, but spare refs/ and immediate subdirs.
+ * Note: munges *name.
+ */
+static void try_remove_empty_parents(char *name)
+{
+	char *p, *q;
+	int i;
+	p = name;
+	for (i = 0; i < 2; i++) { /* refs/{heads,tags,...}/ */
+		while (*p && *p != '/')
+			p++;
+		/* tolerate duplicate slashes; see check_refname_format() */
+		while (*p == '/')
+			p++;
+	}
+	for (q = p; *q; q++)
+		;
+	while (1) {
+		while (q > p && *q != '/')
+			q--;
+		while (q > p && *(q-1) == '/')
+			q--;
+		if (q == p)
+			break;
+		*q = '\0';
+		if (rmdir(git_path("%s", name)))
+			break;
+	}
+}
+
 /* make sure nobody touched the ref, and unlink */
 static void prune_ref(struct ref_to_prune *r)
 {
@@ -68,6 +99,7 @@
 	if (lock) {
 		unlink_or_warn(git_path("%s", r->name));
 		unlock_ref(lock);
+		try_remove_empty_parents(r->name);
 	}
 }
 
@@ -111,7 +143,6 @@
 	packed.fd = -1;
 	if (commit_lock_file(&packed) < 0)
 		die_errno("unable to overwrite old ref-pack file");
-	if (cbdata.flags & PACK_REFS_PRUNE)
-		prune_refs(cbdata.ref_to_prune);
+	prune_refs(cbdata.ref_to_prune);
 	return 0;
 }
diff --git a/pack-write.c b/pack-write.c
index 741efcd..ca9e63b 100644
--- a/pack-write.c
+++ b/pack-write.c
@@ -2,8 +2,12 @@
 #include "pack.h"
 #include "csum-file.h"
 
-uint32_t pack_idx_default_version = 2;
-uint32_t pack_idx_off32_limit = 0x7fffffff;
+void reset_pack_idx_option(struct pack_idx_option *opts)
+{
+	memset(opts, 0, sizeof(*opts));
+	opts->version = 2;
+	opts->off32_limit = 0x7fffffff;
+}
 
 static int sha1_compare(const void *_a, const void *_b)
 {
@@ -12,13 +16,35 @@
 	return hashcmp(a->sha1, b->sha1);
 }
 
+static int cmp_uint32(const void *a_, const void *b_)
+{
+	uint32_t a = *((uint32_t *)a_);
+	uint32_t b = *((uint32_t *)b_);
+
+	return (a < b) ? -1 : (a != b);
+}
+
+static int need_large_offset(off_t offset, const struct pack_idx_option *opts)
+{
+	uint32_t ofsval;
+
+	if ((offset >> 31) || (opts->off32_limit < offset))
+		return 1;
+	if (!opts->anomaly_nr)
+		return 0;
+	ofsval = offset;
+	return !!bsearch(&ofsval, opts->anomaly, opts->anomaly_nr,
+			 sizeof(ofsval), cmp_uint32);
+}
+
 /*
  * On entry *sha1 contains the pack content SHA1 hash, on exit it is
  * the SHA1 hash of sorted object names. The objects array passed in
  * will be sorted by SHA1 on exit.
  */
-char *write_idx_file(char *index_name, struct pack_idx_entry **objects,
-		     int nr_objects, unsigned char *sha1)
+const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects,
+			   int nr_objects, const struct pack_idx_option *opts,
+			   unsigned char *sha1)
 {
 	struct sha1file *f;
 	struct pack_idx_entry **sorted_by_sha, **list, **last;
@@ -42,20 +68,25 @@
 	else
 		sorted_by_sha = list = last = NULL;
 
-	if (!index_name) {
-		static char tmpfile[PATH_MAX];
-		fd = odb_mkstemp(tmpfile, sizeof(tmpfile), "pack/tmp_idx_XXXXXX");
-		index_name = xstrdup(tmpfile);
+	if (opts->flags & WRITE_IDX_VERIFY) {
+		assert(index_name);
+		f = sha1fd_check(index_name);
 	} else {
-		unlink(index_name);
-		fd = open(index_name, O_CREAT|O_EXCL|O_WRONLY, 0600);
+		if (!index_name) {
+			static char tmp_file[PATH_MAX];
+			fd = odb_mkstemp(tmp_file, sizeof(tmp_file), "pack/tmp_idx_XXXXXX");
+			index_name = xstrdup(tmp_file);
+		} else {
+			unlink(index_name);
+			fd = open(index_name, O_CREAT|O_EXCL|O_WRONLY, 0600);
+		}
+		if (fd < 0)
+			die_errno("unable to create '%s'", index_name);
+		f = sha1fd(fd, index_name);
 	}
-	if (fd < 0)
-		die_errno("unable to create '%s'", index_name);
-	f = sha1fd(fd, index_name);
 
 	/* if last object's offset is >= 2^31 we should use index V2 */
-	index_version = (last_obj_offset >> 31) ? 2 : pack_idx_default_version;
+	index_version = need_large_offset(last_obj_offset, opts) ? 2 : opts->version;
 
 	/* index versions 2 and above need a header */
 	if (index_version >= 2) {
@@ -98,6 +129,10 @@
 		}
 		sha1write(f, obj->sha1, 20);
 		git_SHA1_Update(&ctx, obj->sha1, 20);
+		if ((opts->flags & WRITE_IDX_STRICT) &&
+		    (i && !hashcmp(list[-2]->sha1, obj->sha1)))
+			die("The same object %s appears twice in the pack",
+			    sha1_to_hex(obj->sha1));
 	}
 
 	if (index_version >= 2) {
@@ -115,8 +150,11 @@
 		list = sorted_by_sha;
 		for (i = 0; i < nr_objects; i++) {
 			struct pack_idx_entry *obj = *list++;
-			uint32_t offset = (obj->offset <= pack_idx_off32_limit) ?
-				obj->offset : (0x80000000 | nr_large_offset++);
+			uint32_t offset;
+
+			offset = (need_large_offset(obj->offset, opts)
+				  ? (0x80000000 | nr_large_offset++)
+				  : obj->offset);
 			offset = htonl(offset);
 			sha1write(f, &offset, 4);
 		}
@@ -126,22 +164,36 @@
 		while (nr_large_offset) {
 			struct pack_idx_entry *obj = *list++;
 			uint64_t offset = obj->offset;
-			if (offset > pack_idx_off32_limit) {
-				uint32_t split[2];
-				split[0] = htonl(offset >> 32);
-				split[1] = htonl(offset & 0xffffffff);
-				sha1write(f, split, 8);
-				nr_large_offset--;
-			}
+			uint32_t split[2];
+
+			if (!need_large_offset(offset, opts))
+				continue;
+			split[0] = htonl(offset >> 32);
+			split[1] = htonl(offset & 0xffffffff);
+			sha1write(f, split, 8);
+			nr_large_offset--;
 		}
 	}
 
 	sha1write(f, sha1, 20);
-	sha1close(f, NULL, CSUM_FSYNC);
+	sha1close(f, NULL, ((opts->flags & WRITE_IDX_VERIFY)
+			    ? CSUM_CLOSE : CSUM_FSYNC));
 	git_SHA1_Final(sha1, &ctx);
 	return index_name;
 }
 
+off_t write_pack_header(struct sha1file *f, uint32_t nr_entries)
+{
+	struct pack_header hdr;
+
+	hdr.hdr_signature = htonl(PACK_SIGNATURE);
+	hdr.hdr_version = htonl(PACK_VERSION);
+	hdr.hdr_entries = htonl(nr_entries);
+	if (sha1write(f, &hdr, sizeof(hdr)))
+		return 0;
+	return sizeof(hdr);
+}
+
 /*
  * Update pack header with object_count and compute new SHA1 for pack data
  * associated to pack_fd, and write that SHA1 at the end.  That new SHA1
@@ -253,3 +305,71 @@
 	}
 	return NULL;
 }
+
+/*
+ * The per-object header is a pretty dense thing, which is
+ *  - first byte: low four bits are "size", then three bits of "type",
+ *    and the high bit is "size continues".
+ *  - each byte afterwards: low seven bits are size continuation,
+ *    with the high bit being "size continues"
+ */
+int encode_in_pack_object_header(enum object_type type, uintmax_t size, unsigned char *hdr)
+{
+	int n = 1;
+	unsigned char c;
+
+	if (type < OBJ_COMMIT || type > OBJ_REF_DELTA)
+		die("bad type %d", type);
+
+	c = (type << 4) | (size & 15);
+	size >>= 4;
+	while (size) {
+		*hdr++ = c | 0x80;
+		c = size & 0x7f;
+		size >>= 7;
+		n++;
+	}
+	*hdr = c;
+	return n;
+}
+
+struct sha1file *create_tmp_packfile(char **pack_tmp_name)
+{
+	char tmpname[PATH_MAX];
+	int fd;
+
+	fd = odb_mkstemp(tmpname, sizeof(tmpname), "pack/tmp_pack_XXXXXX");
+	*pack_tmp_name = xstrdup(tmpname);
+	return sha1fd(fd, *pack_tmp_name);
+}
+
+void finish_tmp_packfile(char *name_buffer,
+			 const char *pack_tmp_name,
+			 struct pack_idx_entry **written_list,
+			 uint32_t nr_written,
+			 struct pack_idx_option *pack_idx_opts,
+			 unsigned char sha1[])
+{
+	const char *idx_tmp_name;
+	char *end_of_name_prefix = strrchr(name_buffer, 0);
+
+	if (adjust_shared_perm(pack_tmp_name))
+		die_errno("unable to make temporary pack file readable");
+
+	idx_tmp_name = write_idx_file(NULL, written_list, nr_written,
+				      pack_idx_opts, sha1);
+	if (adjust_shared_perm(idx_tmp_name))
+		die_errno("unable to make temporary index file readable");
+
+	sprintf(end_of_name_prefix, "%s.pack", sha1_to_hex(sha1));
+	free_pack_by_name(name_buffer);
+
+	if (rename(pack_tmp_name, name_buffer))
+		die_errno("unable to rename temporary pack file");
+
+	sprintf(end_of_name_prefix, "%s.idx", sha1_to_hex(sha1));
+	if (rename(idx_tmp_name, name_buffer))
+		die_errno("unable to rename temporary index file");
+
+	free((void *)idx_tmp_name);
+}
diff --git a/pack.h b/pack.h
index a883334..aa6ee7d 100644
--- a/pack.h
+++ b/pack.h
@@ -2,6 +2,7 @@
 #define PACK_H
 
 #include "object.h"
+#include "csum-file.h"
 
 /*
  * Packed object header
@@ -34,9 +35,25 @@
  */
 #define PACK_IDX_SIGNATURE 0xff744f63	/* "\377tOc" */
 
-/* These may be overridden by command-line parameters */
-extern uint32_t pack_idx_default_version;
-extern uint32_t pack_idx_off32_limit;
+struct pack_idx_option {
+	unsigned flags;
+	/* flag bits */
+#define WRITE_IDX_VERIFY 01 /* verify only, do not write the idx file */
+#define WRITE_IDX_STRICT 02
+
+	uint32_t version;
+	uint32_t off32_limit;
+
+	/*
+	 * List of offsets that would fit within off32_limit but
+	 * need to be written out as 64-bit entity for byte-for-byte
+	 * verification.
+	 */
+	int anomaly_alloc, anomaly_nr;
+	uint32_t *anomaly;
+};
+
+extern void reset_pack_idx_option(struct pack_idx_option *);
 
 /*
  * Packed object index header
@@ -55,14 +72,25 @@
 	off_t offset;
 };
 
-extern char *write_idx_file(char *index_name, struct pack_idx_entry **objects, int nr_objects, unsigned char *sha1);
+
+struct progress;
+typedef int (*verify_fn)(const unsigned char*, enum object_type, unsigned long, void*, int*);
+
+extern const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects, int nr_objects, const struct pack_idx_option *, unsigned char *sha1);
 extern int check_pack_crc(struct packed_git *p, struct pack_window **w_curs, off_t offset, off_t len, unsigned int nr);
-extern int verify_pack(struct packed_git *);
+extern int verify_pack_index(struct packed_git *);
+extern int verify_pack(struct packed_git *, verify_fn fn, struct progress *, uint32_t);
+extern off_t write_pack_header(struct sha1file *f, uint32_t);
 extern void fixup_pack_header_footer(int, unsigned char *, const char *, uint32_t, unsigned char *, off_t);
 extern char *index_pack_lockfile(int fd);
+extern int encode_in_pack_object_header(enum object_type, uintmax_t, unsigned char *);
 
 #define PH_ERROR_EOF		(-1)
 #define PH_ERROR_PACK_SIGNATURE	(-2)
 #define PH_ERROR_PROTOCOL	(-3)
 extern int read_pack_header(int fd, struct pack_header *);
+
+extern struct sha1file *create_tmp_packfile(char **pack_tmp_name);
+extern void finish_tmp_packfile(char *name_buffer, const char *pack_tmp_name, struct pack_idx_entry **written_list, uint32_t nr_written, struct pack_idx_option *pack_idx_opts, unsigned char sha1[]);
+
 #endif
diff --git a/pager.c b/pager.c
index 4921843..4dcb08d 100644
--- a/pager.c
+++ b/pager.c
@@ -2,14 +2,16 @@
 #include "run-command.h"
 #include "sigchain.h"
 
+#ifndef DEFAULT_PAGER
+#define DEFAULT_PAGER "less"
+#endif
+
 /*
  * This is split up from the rest of git so that we can do
  * something different on Windows.
  */
 
-static int spawned_pager;
-
-#ifndef __MINGW32__
+#ifndef WIN32
 static void pager_preexec(void)
 {
 	/*
@@ -21,12 +23,10 @@
 	FD_ZERO(&in);
 	FD_SET(0, &in);
 	select(1, &in, NULL, &in, NULL);
-
-	setenv("LESS", "FRSX", 0);
 }
 #endif
 
-static const char *pager_argv[] = { "sh", "-c", NULL, NULL };
+static const char *pager_argv[] = { NULL, NULL };
 static struct child_process pager_process;
 
 static void wait_for_pager(void)
@@ -46,12 +46,14 @@
 	raise(signo);
 }
 
-void setup_pager(void)
+const char *git_pager(int stdout_is_tty)
 {
-	const char *pager = getenv("GIT_PAGER");
+	const char *pager;
 
-	if (!isatty(1))
-		return;
+	if (!stdout_is_tty)
+		return NULL;
+
+	pager = getenv("GIT_PAGER");
 	if (!pager) {
 		if (!pager_program)
 			git_config(git_default_config, NULL);
@@ -60,17 +62,38 @@
 	if (!pager)
 		pager = getenv("PAGER");
 	if (!pager)
-		pager = "less";
+		pager = DEFAULT_PAGER;
 	else if (!*pager || !strcmp(pager, "cat"))
+		pager = NULL;
+
+	return pager;
+}
+
+void setup_pager(void)
+{
+	const char *pager = git_pager(isatty(1));
+
+	if (!pager || pager_in_use())
 		return;
 
-	spawned_pager = 1; /* means we are emitting to terminal */
+	/*
+	 * force computing the width of the terminal before we redirect
+	 * the standard output to the pager.
+	 */
+	(void) term_columns();
+
+	setenv("GIT_PAGER_IN_USE", "true", 1);
 
 	/* spawn the pager */
-	pager_argv[2] = pager;
+	pager_argv[0] = pager;
+	pager_process.use_shell = 1;
 	pager_process.argv = pager_argv;
 	pager_process.in = -1;
-#ifndef __MINGW32__
+	if (!getenv("LESS")) {
+		static const char *env[] = { "LESS=FRSX", NULL };
+		pager_process.env = env;
+	}
+#ifndef WIN32
 	pager_process.preexec_cb = pager_preexec;
 #endif
 	if (start_command(&pager_process))
@@ -90,10 +113,49 @@
 int pager_in_use(void)
 {
 	const char *env;
-
-	if (spawned_pager)
-		return 1;
-
 	env = getenv("GIT_PAGER_IN_USE");
 	return env ? git_config_bool("GIT_PAGER_IN_USE", env) : 0;
 }
+
+/*
+ * Return cached value (if set) or $COLUMNS environment variable (if
+ * set and positive) or ioctl(1, TIOCGWINSZ).ws_col (if positive),
+ * and default to 80 if all else fails.
+ */
+int term_columns(void)
+{
+	static int term_columns_at_startup;
+
+	char *col_string;
+	int n_cols;
+
+	if (term_columns_at_startup)
+		return term_columns_at_startup;
+
+	term_columns_at_startup = 80;
+
+	col_string = getenv("COLUMNS");
+	if (col_string && (n_cols = atoi(col_string)) > 0)
+		term_columns_at_startup = n_cols;
+#ifdef TIOCGWINSZ
+	else {
+		struct winsize ws;
+		if (!ioctl(1, TIOCGWINSZ, &ws) && ws.ws_col)
+			term_columns_at_startup = ws.ws_col;
+	}
+#endif
+
+	return term_columns_at_startup;
+}
+
+/*
+ * How many columns do we need to show this number in decimal?
+ */
+int decimal_width(int number)
+{
+	int i, width;
+
+	for (width = 1, i = 10; i <= number; width++)
+		i *= 10;
+	return width;
+}
diff --git a/parse-options-cb.c b/parse-options-cb.c
new file mode 100644
index 0000000..0de5fb1
--- /dev/null
+++ b/parse-options-cb.c
@@ -0,0 +1,130 @@
+#include "git-compat-util.h"
+#include "parse-options.h"
+#include "cache.h"
+#include "commit.h"
+#include "color.h"
+#include "string-list.h"
+
+/*----- some often used options -----*/
+
+int parse_opt_abbrev_cb(const struct option *opt, const char *arg, int unset)
+{
+	int v;
+
+	if (!arg) {
+		v = unset ? 0 : DEFAULT_ABBREV;
+	} else {
+		v = strtol(arg, (char **)&arg, 10);
+		if (*arg)
+			return opterror(opt, "expects a numerical value", 0);
+		if (v && v < MINIMUM_ABBREV)
+			v = MINIMUM_ABBREV;
+		else if (v > 40)
+			v = 40;
+	}
+	*(int *)(opt->value) = v;
+	return 0;
+}
+
+int parse_opt_approxidate_cb(const struct option *opt, const char *arg,
+			     int unset)
+{
+	*(unsigned long *)(opt->value) = approxidate(arg);
+	return 0;
+}
+
+int parse_opt_color_flag_cb(const struct option *opt, const char *arg,
+			    int unset)
+{
+	int value;
+
+	if (!arg)
+		arg = unset ? "never" : (const char *)opt->defval;
+	value = git_config_colorbool(NULL, arg);
+	if (value < 0)
+		return opterror(opt,
+			"expects \"always\", \"auto\", or \"never\"", 0);
+	*(int *)opt->value = value;
+	return 0;
+}
+
+int parse_opt_verbosity_cb(const struct option *opt, const char *arg,
+			   int unset)
+{
+	int *target = opt->value;
+
+	if (unset)
+		/* --no-quiet, --no-verbose */
+		*target = 0;
+	else if (opt->short_name == 'v') {
+		if (*target >= 0)
+			(*target)++;
+		else
+			*target = 1;
+	} else {
+		if (*target <= 0)
+			(*target)--;
+		else
+			*target = -1;
+	}
+	return 0;
+}
+
+int parse_opt_with_commit(const struct option *opt, const char *arg, int unset)
+{
+	unsigned char sha1[20];
+	struct commit *commit;
+
+	if (!arg)
+		return -1;
+	if (get_sha1(arg, sha1))
+		return error("malformed object name %s", arg);
+	commit = lookup_commit_reference(sha1);
+	if (!commit)
+		return error("no such commit %s", arg);
+	commit_list_insert(commit, opt->value);
+	return 0;
+}
+
+int parse_opt_tertiary(const struct option *opt, const char *arg, int unset)
+{
+	int *target = opt->value;
+	*target = unset ? 2 : 1;
+	return 0;
+}
+
+int parse_options_concat(struct option *dst, size_t dst_size, struct option *src)
+{
+	int i, j;
+
+	for (i = 0; i < dst_size; i++)
+		if (dst[i].type == OPTION_END)
+			break;
+	for (j = 0; i < dst_size; i++, j++) {
+		dst[i] = src[j];
+		if (src[j].type == OPTION_END)
+			return 0;
+	}
+	return -1;
+}
+
+int parse_opt_string_list(const struct option *opt, const char *arg, int unset)
+{
+	struct string_list *v = opt->value;
+
+	if (unset) {
+		string_list_clear(v, 0);
+		return 0;
+	}
+
+	if (!arg)
+		return -1;
+
+	string_list_append(v, xstrdup(arg));
+	return 0;
+}
+
+int parse_opt_noop_cb(const struct option *opt, const char *arg, int unset)
+{
+	return 0;
+}
diff --git a/parse-options.c b/parse-options.c
index 3b71fbb..c1c66bd 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -2,11 +2,23 @@
 #include "parse-options.h"
 #include "cache.h"
 #include "commit.h"
+#include "color.h"
+
+static int parse_options_usage(struct parse_opt_ctx_t *ctx,
+			       const char * const *usagestr,
+			       const struct option *opts, int err);
 
 #define OPT_SHORT 1
 #define OPT_UNSET 2
 
-static int opterror(const struct option *opt, const char *reason, int flags)
+int optbug(const struct option *opt, const char *reason)
+{
+	if (opt->long_name)
+		return error("BUG: option '%s' %s", opt->long_name, reason);
+	return error("BUG: switch '%c' %s", opt->short_name, reason);
+}
+
+int opterror(const struct option *opt, const char *reason, int flags)
 {
 	if (flags & OPT_SHORT)
 		return error("switch `%c' %s", opt->short_name, reason);
@@ -50,25 +62,13 @@
 		return opterror(opt, "takes no value", flags);
 	if (unset && (opt->flags & PARSE_OPT_NONEG))
 		return opterror(opt, "isn't available", flags);
-
-	if (!(flags & OPT_SHORT) && p->opt) {
-		switch (opt->type) {
-		case OPTION_CALLBACK:
-			if (!(opt->flags & PARSE_OPT_NOARG))
-				break;
-			/* FALLTHROUGH */
-		case OPTION_BOOLEAN:
-		case OPTION_BIT:
-		case OPTION_NEGBIT:
-		case OPTION_SET_INT:
-		case OPTION_SET_PTR:
-			return opterror(opt, "takes no value", flags);
-		default:
-			break;
-		}
-	}
+	if (!(flags & OPT_SHORT) && p->opt && (opt->flags & PARSE_OPT_NOARG))
+		return opterror(opt, "takes no value", flags);
 
 	switch (opt->type) {
+	case OPTION_LOWLEVEL_CALLBACK:
+		return (*(parse_opt_ll_cb *)opt->callback)(p, opt, unset);
+
 	case OPTION_BIT:
 		if (unset)
 			*(int *)opt->value &= ~opt->defval;
@@ -83,7 +83,7 @@
 			*(int *)opt->value &= ~opt->defval;
 		return 0;
 
-	case OPTION_BOOLEAN:
+	case OPTION_COUNTUP:
 		*(int *)opt->value = unset ? 0 : *(int *)opt->value + 1;
 		return 0;
 
@@ -193,13 +193,14 @@
 		arg_end = arg + strlen(arg);
 
 	for (; options->type != OPTION_END; options++) {
-		const char *rest;
-		int flags = 0;
+		const char *rest, *long_name = options->long_name;
+		int flags = 0, opt_flags = 0;
 
-		if (!options->long_name)
+		if (!long_name)
 			continue;
 
-		rest = skip_prefix(arg, options->long_name);
+again:
+		rest = skip_prefix(arg, long_name);
 		if (options->type == OPTION_ARGUMENT) {
 			if (!rest)
 				continue;
@@ -212,7 +213,7 @@
 		}
 		if (!rest) {
 			/* abbreviated? */
-			if (!strncmp(options->long_name, arg, arg_end - arg)) {
+			if (!strncmp(long_name, arg, arg_end - arg)) {
 is_abbreviated:
 				if (abbrev_option) {
 					/*
@@ -227,21 +228,30 @@
 				if (!(flags & OPT_UNSET) && *arg_end)
 					p->opt = arg_end + 1;
 				abbrev_option = options;
-				abbrev_flags = flags;
+				abbrev_flags = flags ^ opt_flags;
 				continue;
 			}
+			/* negation allowed? */
+			if (options->flags & PARSE_OPT_NONEG)
+				continue;
 			/* negated and abbreviated very much? */
 			if (!prefixcmp("no-", arg)) {
 				flags |= OPT_UNSET;
 				goto is_abbreviated;
 			}
 			/* negated? */
-			if (strncmp(arg, "no-", 3))
+			if (prefixcmp(arg, "no-")) {
+				if (!prefixcmp(long_name, "no-")) {
+					long_name += 3;
+					opt_flags |= OPT_UNSET;
+					goto again;
+				}
 				continue;
+			}
 			flags |= OPT_UNSET;
-			rest = skip_prefix(arg + 3, options->long_name);
+			rest = skip_prefix(arg + 3, long_name);
 			/* abbreviated and negated? */
-			if (!rest && !prefixcmp(options->long_name, arg + 3))
+			if (!rest && !prefixcmp(long_name, arg + 3))
 				goto is_abbreviated;
 			if (!rest)
 				continue;
@@ -251,7 +261,7 @@
 				continue;
 			p->opt = rest + 1;
 		}
-		return get_value(p, options, flags);
+		return get_value(p, options, flags ^ opt_flags);
 	}
 
 	if (ambiguous_option)
@@ -273,13 +283,6 @@
 	for (; options->type != OPTION_END; options++) {
 		if (!(options->flags & PARSE_OPT_NODASH))
 			continue;
-		if ((options->flags & PARSE_OPT_OPTARG) ||
-		    !(options->flags & PARSE_OPT_NOARG))
-			die("BUG: dashless options don't support arguments");
-		if (!(options->flags & PARSE_OPT_NONEG))
-			die("BUG: dashless options don't support negation");
-		if (options->long_name)
-			die("BUG: dashless options can't be long");
 		if (options->short_name == arg[0] && arg[1] == '\0')
 			return get_value(p, options, OPT_SHORT);
 	}
@@ -312,25 +315,37 @@
 
 	for (; opts->type != OPTION_END; opts++) {
 		if ((opts->flags & PARSE_OPT_LASTARG_DEFAULT) &&
-		    (opts->flags & PARSE_OPT_OPTARG)) {
-			if (opts->long_name) {
-				error("`--%s` uses incompatible flags "
-				      "LASTARG_DEFAULT and OPTARG", opts->long_name);
-			} else {
-				error("`-%c` uses incompatible flags "
-				      "LASTARG_DEFAULT and OPTARG", opts->short_name);
-			}
-			err |= 1;
+		    (opts->flags & PARSE_OPT_OPTARG))
+			err |= optbug(opts, "uses incompatible flags "
+					"LASTARG_DEFAULT and OPTARG");
+		if (opts->flags & PARSE_OPT_NODASH &&
+		    ((opts->flags & PARSE_OPT_OPTARG) ||
+		     !(opts->flags & PARSE_OPT_NOARG) ||
+		     !(opts->flags & PARSE_OPT_NONEG) ||
+		     opts->long_name))
+			err |= optbug(opts, "uses feature "
+					"not supported for dashless options");
+		switch (opts->type) {
+		case OPTION_COUNTUP:
+		case OPTION_BIT:
+		case OPTION_NEGBIT:
+		case OPTION_SET_INT:
+		case OPTION_SET_PTR:
+		case OPTION_NUMBER:
+			if ((opts->flags & PARSE_OPT_OPTARG) ||
+			    !(opts->flags & PARSE_OPT_NOARG))
+				err |= optbug(opts, "should not accept an argument");
+		default:
+			; /* ok. (usually accepts an argument) */
 		}
 	}
-
 	if (err)
-		exit(129);
+		exit(128);
 }
 
 void parse_options_start(struct parse_opt_ctx_t *ctx,
 			 int argc, const char **argv, const char *prefix,
-			 int flags)
+			 const struct option *options, int flags)
 {
 	memset(ctx, 0, sizeof(*ctx));
 	ctx->argc = argc - 1;
@@ -342,10 +357,12 @@
 	if ((flags & PARSE_OPT_KEEP_UNKNOWN) &&
 	    (flags & PARSE_OPT_STOP_AT_NON_OPTION))
 		die("STOP_AT_NON_OPTION and KEEP_UNKNOWN don't go together");
+	parse_options_check(options);
 }
 
-static int usage_with_options_internal(const char * const *,
-				       const struct option *, int);
+static int usage_with_options_internal(struct parse_opt_ctx_t *,
+				       const char * const *,
+				       const struct option *, int, int);
 
 int parse_options_step(struct parse_opt_ctx_t *ctx,
 		       const struct option *options,
@@ -353,8 +370,6 @@
 {
 	int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP);
 
-	parse_options_check(options);
-
 	/* we must reset ->opt, unknown short option leave it dangling */
 	ctx->opt = NULL;
 
@@ -365,7 +380,7 @@
 			if (parse_nodash_opt(ctx, arg, options) == 0)
 				continue;
 			if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION)
-				break;
+				return PARSE_OPT_NON_OPTION;
 			ctx->out[ctx->cpidx++] = ctx->argv[0];
 			continue;
 		}
@@ -373,21 +388,23 @@
 		if (arg[1] != '-') {
 			ctx->opt = arg + 1;
 			if (internal_help && *ctx->opt == 'h')
-				return parse_options_usage(usagestr, options);
+				return parse_options_usage(ctx, usagestr, options, 0);
 			switch (parse_short_opt(ctx, options)) {
 			case -1:
-				return parse_options_usage(usagestr, options);
+				return parse_options_usage(ctx, usagestr, options, 1);
 			case -2:
+				if (ctx->opt)
+					check_typos(arg + 1, options);
 				goto unknown;
 			}
 			if (ctx->opt)
 				check_typos(arg + 1, options);
 			while (ctx->opt) {
 				if (internal_help && *ctx->opt == 'h')
-					return parse_options_usage(usagestr, options);
+					return parse_options_usage(ctx, usagestr, options, 0);
 				switch (parse_short_opt(ctx, options)) {
 				case -1:
-					return parse_options_usage(usagestr, options);
+					return parse_options_usage(ctx, usagestr, options, 1);
 				case -2:
 					/* fake a short option thing to hide the fact that we may have
 					 * started to parse aggregated stuff
@@ -411,12 +428,12 @@
 		}
 
 		if (internal_help && !strcmp(arg + 2, "help-all"))
-			return usage_with_options_internal(usagestr, options, 1);
+			return usage_with_options_internal(ctx, usagestr, options, 1, 0);
 		if (internal_help && !strcmp(arg + 2, "help"))
-			return parse_options_usage(usagestr, options);
+			return parse_options_usage(ctx, usagestr, options, 0);
 		switch (parse_long_opt(ctx, arg + 2, options)) {
 		case -1:
-			return parse_options_usage(usagestr, options);
+			return parse_options_usage(ctx, usagestr, options, 1);
 		case -2:
 			goto unknown;
 		}
@@ -443,10 +460,11 @@
 {
 	struct parse_opt_ctx_t ctx;
 
-	parse_options_start(&ctx, argc, argv, prefix, flags);
+	parse_options_start(&ctx, argc, argv, prefix, options, flags);
 	switch (parse_options_step(&ctx, options, usagestr)) {
 	case PARSE_OPT_HELP:
 		exit(129);
+	case PARSE_OPT_NON_OPTION:
 	case PARSE_OPT_DONE:
 		break;
 	default: /* PARSE_OPT_UNKNOWN */
@@ -458,10 +476,11 @@
 		usage_with_options(usagestr, options);
 	}
 
+	precompose_argv(argc, argv);
 	return parse_options_end(&ctx);
 }
 
-static int usage_argh(const struct option *opts)
+static int usage_argh(const struct option *opts, FILE *outfile)
 {
 	const char *s;
 	int literal = (opts->flags & PARSE_OPT_LITERAL_ARGHELP) || !opts->argh;
@@ -472,153 +491,106 @@
 			s = literal ? "[%s]" : "[<%s>]";
 	else
 		s = literal ? " %s" : " <%s>";
-	return fprintf(stderr, s, opts->argh ? opts->argh : "...");
+	return fprintf(outfile, s, opts->argh ? _(opts->argh) : _("..."));
 }
 
 #define USAGE_OPTS_WIDTH 24
 #define USAGE_GAP         2
 
-static int usage_with_options_internal(const char * const *usagestr,
-				const struct option *opts, int full)
+static int usage_with_options_internal(struct parse_opt_ctx_t *ctx,
+				       const char * const *usagestr,
+				       const struct option *opts, int full, int err)
 {
+	FILE *outfile = err ? stderr : stdout;
+
 	if (!usagestr)
 		return PARSE_OPT_HELP;
 
-	fprintf(stderr, "usage: %s\n", *usagestr++);
+	if (!err && ctx && ctx->flags & PARSE_OPT_SHELL_EVAL)
+		fprintf(outfile, "cat <<\\EOF\n");
+
+	fprintf_ln(outfile, _("usage: %s"), _(*usagestr++));
 	while (*usagestr && **usagestr)
-		fprintf(stderr, "   or: %s\n", *usagestr++);
+		/* TRANSLATORS: the colon here should align with the
+		   one in "usage: %s" translation */
+		fprintf_ln(outfile, _("   or: %s"), _(*usagestr++));
 	while (*usagestr) {
-		fprintf(stderr, "%s%s\n",
-				**usagestr ? "    " : "",
-				*usagestr);
+		if (**usagestr)
+			fprintf_ln(outfile, _("    %s"), _(*usagestr));
+		else
+			putchar('\n');
 		usagestr++;
 	}
 
 	if (opts->type != OPTION_GROUP)
-		fputc('\n', stderr);
+		fputc('\n', outfile);
 
 	for (; opts->type != OPTION_END; opts++) {
 		size_t pos;
 		int pad;
 
 		if (opts->type == OPTION_GROUP) {
-			fputc('\n', stderr);
+			fputc('\n', outfile);
 			if (*opts->help)
-				fprintf(stderr, "%s\n", opts->help);
+				fprintf(outfile, "%s\n", _(opts->help));
 			continue;
 		}
 		if (!full && (opts->flags & PARSE_OPT_HIDDEN))
 			continue;
 
-		pos = fprintf(stderr, "    ");
-		if (opts->short_name && !(opts->flags & PARSE_OPT_NEGHELP)) {
+		pos = fprintf(outfile, "    ");
+		if (opts->short_name) {
 			if (opts->flags & PARSE_OPT_NODASH)
-				pos += fprintf(stderr, "%c", opts->short_name);
+				pos += fprintf(outfile, "%c", opts->short_name);
 			else
-				pos += fprintf(stderr, "-%c", opts->short_name);
+				pos += fprintf(outfile, "-%c", opts->short_name);
 		}
 		if (opts->long_name && opts->short_name)
-			pos += fprintf(stderr, ", ");
+			pos += fprintf(outfile, ", ");
 		if (opts->long_name)
-			pos += fprintf(stderr, "--%s%s",
-				(opts->flags & PARSE_OPT_NEGHELP) ?  "no-" : "",
-				opts->long_name);
+			pos += fprintf(outfile, "--%s", opts->long_name);
 		if (opts->type == OPTION_NUMBER)
-			pos += fprintf(stderr, "-NUM");
+			pos += fprintf(outfile, "-NUM");
 
-		if (!(opts->flags & PARSE_OPT_NOARG))
-			pos += usage_argh(opts);
+		if ((opts->flags & PARSE_OPT_LITERAL_ARGHELP) ||
+		    !(opts->flags & PARSE_OPT_NOARG))
+			pos += usage_argh(opts, outfile);
 
 		if (pos <= USAGE_OPTS_WIDTH)
 			pad = USAGE_OPTS_WIDTH - pos;
 		else {
-			fputc('\n', stderr);
+			fputc('\n', outfile);
 			pad = USAGE_OPTS_WIDTH;
 		}
-		fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help);
+		fprintf(outfile, "%*s%s\n", pad + USAGE_GAP, "", _(opts->help));
 	}
-	fputc('\n', stderr);
+	fputc('\n', outfile);
+
+	if (!err && ctx && ctx->flags & PARSE_OPT_SHELL_EVAL)
+		fputs("EOF\n", outfile);
 
 	return PARSE_OPT_HELP;
 }
 
-void usage_with_options(const char * const *usagestr,
+void NORETURN usage_with_options(const char * const *usagestr,
 			const struct option *opts)
 {
-	usage_with_options_internal(usagestr, opts, 0);
+	usage_with_options_internal(NULL, usagestr, opts, 0, 1);
 	exit(129);
 }
 
-int parse_options_usage(const char * const *usagestr,
-			const struct option *opts)
+void NORETURN usage_msg_opt(const char *msg,
+		   const char * const *usagestr,
+		   const struct option *options)
 {
-	return usage_with_options_internal(usagestr, opts, 0);
+	fprintf(stderr, "%s\n\n", msg);
+	usage_with_options(usagestr, options);
 }
 
-
-/*----- some often used options -----*/
-#include "cache.h"
-
-int parse_opt_abbrev_cb(const struct option *opt, const char *arg, int unset)
+static int parse_options_usage(struct parse_opt_ctx_t *ctx,
+			       const char * const *usagestr,
+			       const struct option *opts, int err)
 {
-	int v;
-
-	if (!arg) {
-		v = unset ? 0 : DEFAULT_ABBREV;
-	} else {
-		v = strtol(arg, (char **)&arg, 10);
-		if (*arg)
-			return opterror(opt, "expects a numerical value", 0);
-		if (v && v < MINIMUM_ABBREV)
-			v = MINIMUM_ABBREV;
-		else if (v > 40)
-			v = 40;
-	}
-	*(int *)(opt->value) = v;
-	return 0;
+	return usage_with_options_internal(ctx, usagestr, opts, 0, err);
 }
 
-int parse_opt_approxidate_cb(const struct option *opt, const char *arg,
-			     int unset)
-{
-	*(unsigned long *)(opt->value) = approxidate(arg);
-	return 0;
-}
-
-int parse_opt_verbosity_cb(const struct option *opt, const char *arg,
-			   int unset)
-{
-	int *target = opt->value;
-
-	if (unset)
-		/* --no-quiet, --no-verbose */
-		*target = 0;
-	else if (opt->short_name == 'v') {
-		if (*target >= 0)
-			(*target)++;
-		else
-			*target = 1;
-	} else {
-		if (*target <= 0)
-			(*target)--;
-		else
-			*target = -1;
-	}
-	return 0;
-}
-
-int parse_opt_with_commit(const struct option *opt, const char *arg, int unset)
-{
-	unsigned char sha1[20];
-	struct commit *commit;
-
-	if (!arg)
-		return -1;
-	if (get_sha1(arg, sha1))
-		return error("malformed object name %s", arg);
-	commit = lookup_commit_reference(sha1);
-	if (!commit)
-		return error("no such commit %s", arg);
-	commit_list_insert(commit, opt->value);
-	return 0;
-}
diff --git a/parse-options.h b/parse-options.h
index b32587a..77a4a8b 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -10,22 +10,26 @@
 	/* options with no arguments */
 	OPTION_BIT,
 	OPTION_NEGBIT,
-	OPTION_BOOLEAN, /* _INCR would have been a better name */
+	OPTION_COUNTUP,
 	OPTION_SET_INT,
 	OPTION_SET_PTR,
 	/* options with arguments (usually) */
 	OPTION_STRING,
 	OPTION_INTEGER,
 	OPTION_CALLBACK,
+	OPTION_LOWLEVEL_CALLBACK,
 	OPTION_FILENAME
 };
 
+/* Deprecated synonym */
+#define OPTION_BOOLEAN OPTION_COUNTUP
+
 enum parse_opt_flags {
 	PARSE_OPT_KEEP_DASHDASH = 1,
 	PARSE_OPT_STOP_AT_NON_OPTION = 2,
 	PARSE_OPT_KEEP_ARGV0 = 4,
 	PARSE_OPT_KEEP_UNKNOWN = 8,
-	PARSE_OPT_NO_INTERNAL_HELP = 16,
+	PARSE_OPT_NO_INTERNAL_HELP = 16
 };
 
 enum parse_opt_option_flags {
@@ -36,12 +40,16 @@
 	PARSE_OPT_LASTARG_DEFAULT = 16,
 	PARSE_OPT_NODASH = 32,
 	PARSE_OPT_LITERAL_ARGHELP = 64,
-	PARSE_OPT_NEGHELP = 128,
+	PARSE_OPT_SHELL_EVAL = 256
 };
 
 struct option;
 typedef int parse_opt_cb(const struct option *, const char *arg, int unset);
 
+struct parse_opt_ctx_t;
+typedef int parse_opt_ll_cb(struct parse_opt_ctx_t *ctx,
+				const struct option *opt, int unset);
+
 /*
  * `type`::
  *   holds the type of the option, you must have an OPTION_END last in your
@@ -58,17 +66,19 @@
  *
  * `argh`::
  *   token to explain the kind of argument this option wants. Keep it
- *   homogeneous across the repository.
+ *   homogeneous across the repository. Should be wrapped by N_() for
+ *   translation.
  *
  * `help`::
  *   the short help associated to what the option does.
  *   Must never be NULL (except for OPTION_END).
  *   OPTION_GROUP uses this pointer to store the group header.
+ *   Should be wrapped by N_() for translation.
  *
  * `flags`::
  *   mask of parse_opt_option_flags.
  *   PARSE_OPT_OPTARG: says that the argument is optional (not for BOOLEANs)
- *   PARSE_OPT_NOARG: says that this option takes no argument
+ *   PARSE_OPT_NOARG: says that this option does not take an argument
  *   PARSE_OPT_NONEG: says that this option cannot be negated
  *   PARSE_OPT_HIDDEN: this option is skipped in the default usage, and
  *                     shown only in the full usage.
@@ -81,12 +91,10 @@
  *   PARSE_OPT_LITERAL_ARGHELP: says that argh shouldn't be enclosed in brackets
  *				(i.e. '<argh>') in the help message.
  *				Useful for options with multiple parameters.
- *   PARSE_OPT_NEGHELP: says that the long option should always be shown with
- *				the --no prefix in the usage message. Sometimes
- *				useful for users of OPTION_NEGBIT.
  *
  * `callback`::
- *   pointer to the callback to use for OPTION_CALLBACK.
+ *   pointer to the callback to use for OPTION_CALLBACK or
+ *   OPTION_LOWLEVEL_CALLBACK.
  *
  * `defval`::
  *   default value to fill (*->value) with for PARSE_OPT_OPTARG.
@@ -115,16 +123,22 @@
 				      PARSE_OPT_NOARG, NULL, (b) }
 #define OPT_NEGBIT(s, l, v, h, b)   { OPTION_NEGBIT, (s), (l), (v), NULL, \
 				      (h), PARSE_OPT_NOARG, NULL, (b) }
-#define OPT_BOOLEAN(s, l, v, h)     { OPTION_BOOLEAN, (s), (l), (v), NULL, \
+#define OPT_COUNTUP(s, l, v, h)     { OPTION_COUNTUP, (s), (l), (v), NULL, \
 				      (h), PARSE_OPT_NOARG }
 #define OPT_SET_INT(s, l, v, h, i)  { OPTION_SET_INT, (s), (l), (v), NULL, \
 				      (h), PARSE_OPT_NOARG, NULL, (i) }
+#define OPT_BOOL(s, l, v, h)        OPT_SET_INT(s, l, v, h, 1)
 #define OPT_SET_PTR(s, l, v, h, p)  { OPTION_SET_PTR, (s), (l), (v), NULL, \
 				      (h), PARSE_OPT_NOARG, NULL, (p) }
-#define OPT_INTEGER(s, l, v, h)     { OPTION_INTEGER, (s), (l), (v), "n", (h) }
+#define OPT_INTEGER(s, l, v, h)     { OPTION_INTEGER, (s), (l), (v), N_("n"), (h) }
 #define OPT_STRING(s, l, v, a, h)   { OPTION_STRING,  (s), (l), (v), (a), (h) }
+#define OPT_STRING_LIST(s, l, v, a, h) \
+				    { OPTION_CALLBACK, (s), (l), (v), (a), \
+				      (h), 0, &parse_opt_string_list }
+#define OPT_UYN(s, l, v, h)         { OPTION_CALLBACK, (s), (l), (v), NULL, \
+				      (h), PARSE_OPT_NOARG, &parse_opt_tertiary }
 #define OPT_DATE(s, l, v, h) \
-	{ OPTION_CALLBACK, (s), (l), (v), "time",(h), 0, \
+	{ OPTION_CALLBACK, (s), (l), (v), N_("time"),(h), 0,	\
 	  parse_opt_approxidate_cb }
 #define OPT_CALLBACK(s, l, v, a, h, f) \
 	{ OPTION_CALLBACK, (s), (l), (v), (a), (h), 0, (f) }
@@ -132,10 +146,22 @@
 	{ OPTION_NUMBER, 0, NULL, (v), NULL, (h), \
 	  PARSE_OPT_NOARG | PARSE_OPT_NONEG, (f) }
 #define OPT_FILENAME(s, l, v, h)    { OPTION_FILENAME, (s), (l), (v), \
-				       "FILE", (h) }
+				       N_("file"), (h) }
+#define OPT_COLOR_FLAG(s, l, v, h) \
+	{ OPTION_CALLBACK, (s), (l), (v), N_("when"), (h), PARSE_OPT_OPTARG, \
+		parse_opt_color_flag_cb, (intptr_t)"always" }
+
+#define OPT_NOOP_NOARG(s, l) \
+	{ OPTION_CALLBACK, (s), (l), NULL, NULL, \
+	  N_("no-op (backward compatibility)"),		\
+	  PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, parse_opt_noop_cb }
+
+/* Deprecated synonym */
+#define OPT_BOOLEAN OPT_COUNTUP
 
 /* parse_options() will filter out the processed options and leave the
- * non-option arguments in argv[].
+ * non-option arguments in argv[]. usagestr strings should be marked
+ * for translation with N_().
  * Returns the number of arguments left in argv[].
  */
 extern int parse_options(int argc, const char **argv, const char *prefix,
@@ -145,12 +171,19 @@
 extern NORETURN void usage_with_options(const char * const *usagestr,
                                         const struct option *options);
 
+extern NORETURN void usage_msg_opt(const char *msg,
+				   const char * const *usagestr,
+				   const struct option *options);
+
+extern int optbug(const struct option *opt, const char *reason);
+extern int opterror(const struct option *opt, const char *reason, int flags);
 /*----- incremental advanced APIs -----*/
 
 enum {
 	PARSE_OPT_HELP = -1,
 	PARSE_OPT_DONE,
-	PARSE_OPT_UNKNOWN,
+	PARSE_OPT_NON_OPTION,
+	PARSE_OPT_UNKNOWN
 };
 
 /*
@@ -167,12 +200,9 @@
 	const char *prefix;
 };
 
-extern int parse_options_usage(const char * const *usagestr,
-			       const struct option *opts);
-
 extern void parse_options_start(struct parse_opt_ctx_t *ctx,
 				int argc, const char **argv, const char *prefix,
-				int flags);
+				const struct option *options, int flags);
 
 extern int parse_options_step(struct parse_opt_ctx_t *ctx,
 			      const struct option *options,
@@ -180,24 +210,34 @@
 
 extern int parse_options_end(struct parse_opt_ctx_t *ctx);
 
+extern int parse_options_concat(struct option *dst, size_t, struct option *src);
 
 /*----- some often used options -----*/
 extern int parse_opt_abbrev_cb(const struct option *, const char *, int);
 extern int parse_opt_approxidate_cb(const struct option *, const char *, int);
+extern int parse_opt_color_flag_cb(const struct option *, const char *, int);
 extern int parse_opt_verbosity_cb(const struct option *, const char *, int);
 extern int parse_opt_with_commit(const struct option *, const char *, int);
+extern int parse_opt_tertiary(const struct option *, const char *, int);
+extern int parse_opt_string_list(const struct option *, const char *, int);
+extern int parse_opt_noop_cb(const struct option *, const char *, int);
 
-#define OPT__VERBOSE(var)  OPT_BOOLEAN('v', "verbose", (var), "be verbose")
-#define OPT__QUIET(var)    OPT_BOOLEAN('q', "quiet",   (var), "be quiet")
+#define OPT__VERBOSE(var, h)  OPT_BOOLEAN('v', "verbose", (var), (h))
+#define OPT__QUIET(var, h)    OPT_BOOLEAN('q', "quiet",   (var), (h))
 #define OPT__VERBOSITY(var) \
-	{ OPTION_CALLBACK, 'v', "verbose", (var), NULL, "be more verbose", \
+	{ OPTION_CALLBACK, 'v', "verbose", (var), NULL, N_("be more verbose"), \
 	  PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }, \
-	{ OPTION_CALLBACK, 'q', "quiet", (var), NULL, "be more quiet", \
+	{ OPTION_CALLBACK, 'q', "quiet", (var), NULL, N_("be more quiet"), \
 	  PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }
-#define OPT__DRY_RUN(var)  OPT_BOOLEAN('n', "dry-run", (var), "dry run")
+#define OPT__DRY_RUN(var, h)  OPT_BOOLEAN('n', "dry-run", (var), (h))
+#define OPT__FORCE(var, h)    OPT_BOOLEAN('f', "force",   (var), (h))
 #define OPT__ABBREV(var)  \
-	{ OPTION_CALLBACK, 0, "abbrev", (var), "n", \
-	  "use <n> digits to display SHA-1s", \
+	{ OPTION_CALLBACK, 0, "abbrev", (var), N_("n"),	\
+	  N_("use <n> digits to display SHA-1s"),	\
 	  PARSE_OPT_OPTARG, &parse_opt_abbrev_cb, 0 }
+#define OPT__COLOR(var, h) \
+	OPT_COLOR_FLAG(0, "color", (var), (h))
+#define OPT_COLUMN(s, l, v, h) \
+	{ OPTION_CALLBACK, (s), (l), (v), "style", (h), PARSE_OPT_OPTARG, parseopt_column_callback }
 
 #endif
diff --git a/patch-delta.c b/patch-delta.c
index ef748ce..56e0a5e 100644
--- a/patch-delta.c
+++ b/patch-delta.c
@@ -2,7 +2,7 @@
  * patch-delta.c:
  * recreate a buffer from a source and the delta produced by diff-delta.c
  *
- * (C) 2005 Nicolas Pitre <nico@cam.org>
+ * (C) 2005 Nicolas Pitre <nico@fluxnic.net>
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -33,8 +33,7 @@
 
 	/* now the result size */
 	size = get_delta_hdr_size(&data, top);
-	dst_buf = xmalloc(size + 1);
-	dst_buf[size] = 0;
+	dst_buf = xmallocz(size);
 
 	out = dst_buf;
 	while (data < top) {
@@ -49,7 +48,7 @@
 			if (cmd & 0x20) cp_size |= (*data++ << 8);
 			if (cmd & 0x40) cp_size |= (*data++ << 16);
 			if (cp_size == 0) cp_size = 0x10000;
-			if (cp_off + cp_size < cp_size ||
+			if (unsigned_add_overflows(cp_off, cp_size) ||
 			    cp_off + cp_size > src_size ||
 			    cp_size > size)
 				break;
diff --git a/patch-id.c b/patch-id.c
deleted file mode 100644
index 0df4cb0..0000000
--- a/patch-id.c
+++ /dev/null
@@ -1,87 +0,0 @@
-#include "cache.h"
-#include "exec_cmd.h"
-
-static void flush_current_id(int patchlen, unsigned char *id, git_SHA_CTX *c)
-{
-	unsigned char result[20];
-	char name[50];
-
-	if (!patchlen)
-		return;
-
-	git_SHA1_Final(result, c);
-	memcpy(name, sha1_to_hex(id), 41);
-	printf("%s %s\n", sha1_to_hex(result), name);
-	git_SHA1_Init(c);
-}
-
-static int remove_space(char *line)
-{
-	char *src = line;
-	char *dst = line;
-	unsigned char c;
-
-	while ((c = *src++) != '\0') {
-		if (!isspace(c))
-			*dst++ = c;
-	}
-	return dst - line;
-}
-
-static void generate_id_list(void)
-{
-	static unsigned char sha1[20];
-	static char line[1000];
-	git_SHA_CTX ctx;
-	int patchlen = 0;
-
-	git_SHA1_Init(&ctx);
-	while (fgets(line, sizeof(line), stdin) != NULL) {
-		unsigned char n[20];
-		char *p = line;
-		int len;
-
-		if (!memcmp(line, "diff-tree ", 10))
-			p += 10;
-		else if (!memcmp(line, "commit ", 7))
-			p += 7;
-
-		if (!get_sha1_hex(p, n)) {
-			flush_current_id(patchlen, sha1, &ctx);
-			hashcpy(sha1, n);
-			patchlen = 0;
-			continue;
-		}
-
-		/* Ignore commit comments */
-		if (!patchlen && memcmp(line, "diff ", 5))
-			continue;
-
-		/* Ignore git-diff index header */
-		if (!memcmp(line, "index ", 6))
-			continue;
-
-		/* Ignore line numbers when computing the SHA1 of the patch */
-		if (!memcmp(line, "@@ -", 4))
-			continue;
-
-		/* Compute the sha without whitespace */
-		len = remove_space(line);
-		patchlen += len;
-		git_SHA1_Update(&ctx, line, len);
-	}
-	flush_current_id(patchlen, sha1, &ctx);
-}
-
-static const char patch_id_usage[] = "git patch-id < patch";
-
-int main(int argc, char **argv)
-{
-	if (argc != 1)
-		usage(patch_id_usage);
-
-	git_extract_argv0_path(argv[0]);
-
-	generate_id_list();
-	return 0;
-}
diff --git a/path.c b/path.c
index 047fdb0..66acd24 100644
--- a/path.c
+++ b/path.c
@@ -11,6 +11,7 @@
  * which is what it's designed for.
  */
 #include "cache.h"
+#include "strbuf.h"
 
 static char bad_path[] = "/bad-path/";
 
@@ -86,6 +87,21 @@
 	return xstrdup(path);
 }
 
+char *mkpathdup(const char *fmt, ...)
+{
+	char *path;
+	struct strbuf sb = STRBUF_INIT;
+	va_list args;
+
+	va_start(args, fmt);
+	strbuf_vaddf(&sb, fmt, args);
+	va_end(args);
+	path = xstrdup(cleanup_path(sb.buf));
+
+	strbuf_release(&sb);
+	return path;
+}
+
 char *mkpath(const char *fmt, ...)
 {
 	va_list args;
@@ -121,39 +137,69 @@
 	return cleanup_path(pathname);
 }
 
-
-/* git_mkstemp() - create tmp file honoring TMPDIR variable */
-int git_mkstemp(char *path, size_t len, const char *template)
+void home_config_paths(char **global, char **xdg, char *file)
 {
-	const char *tmp;
-	size_t n;
+	char *xdg_home = getenv("XDG_CONFIG_HOME");
+	char *home = getenv("HOME");
+	char *to_free = NULL;
 
-	tmp = getenv("TMPDIR");
-	if (!tmp)
-		tmp = "/tmp";
-	n = snprintf(path, len, "%s/%s", tmp, template);
-	if (len <= n) {
-		errno = ENAMETOOLONG;
-		return -1;
+	if (!home) {
+		if (global)
+			*global = NULL;
+	} else {
+		if (!xdg_home) {
+			to_free = mkpathdup("%s/.config", home);
+			xdg_home = to_free;
+		}
+		if (global)
+			*global = mkpathdup("%s/.gitconfig", home);
 	}
-	return mkstemp(path);
+
+	if (!xdg_home)
+		*xdg = NULL;
+	else
+		*xdg = mkpathdup("%s/git/%s", xdg_home, file);
+
+	free(to_free);
 }
 
-/* git_mkstemps() - create tmp file with suffix honoring TMPDIR variable. */
-int git_mkstemps(char *path, size_t len, const char *template, int suffix_len)
+char *git_path_submodule(const char *path, const char *fmt, ...)
 {
-	const char *tmp;
-	size_t n;
+	char *pathname = get_pathname();
+	struct strbuf buf = STRBUF_INIT;
+	const char *git_dir;
+	va_list args;
+	unsigned len;
 
-	tmp = getenv("TMPDIR");
-	if (!tmp)
-		tmp = "/tmp";
-	n = snprintf(path, len, "%s/%s", tmp, template);
-	if (len <= n) {
-		errno = ENAMETOOLONG;
-		return -1;
+	len = strlen(path);
+	if (len > PATH_MAX-100)
+		return bad_path;
+
+	strbuf_addstr(&buf, path);
+	if (len && path[len-1] != '/')
+		strbuf_addch(&buf, '/');
+	strbuf_addstr(&buf, ".git");
+
+	git_dir = read_gitfile(buf.buf);
+	if (git_dir) {
+		strbuf_reset(&buf);
+		strbuf_addstr(&buf, git_dir);
 	}
-	return mkstemps(path, suffix_len);
+	strbuf_addch(&buf, '/');
+
+	if (buf.len >= PATH_MAX)
+		return bad_path;
+	memcpy(pathname, buf.buf, buf.len + 1);
+
+	strbuf_release(&buf);
+	len = strlen(pathname);
+
+	va_start(args, fmt);
+	len += vsnprintf(pathname + len, PATH_MAX - len, fmt, args);
+	va_end(args);
+	if (len >= PATH_MAX)
+		return bad_path;
+	return cleanup_path(pathname);
 }
 
 int validate_headref(const char *path)
@@ -207,43 +253,51 @@
 	return -1;
 }
 
-static char *user_path(char *buf, char *path, int sz)
+static struct passwd *getpw_str(const char *username, size_t len)
 {
 	struct passwd *pw;
-	char *slash;
-	int len, baselen;
+	char *username_z = xmalloc(len + 1);
+	memcpy(username_z, username, len);
+	username_z[len] = '\0';
+	pw = getpwnam(username_z);
+	free(username_z);
+	return pw;
+}
 
-	if (!path || path[0] != '~')
-		return NULL;
-	path++;
-	slash = strchr(path, '/');
-	if (path[0] == '/' || !path[0]) {
-		pw = getpwuid(getuid());
-	}
-	else {
-		if (slash) {
-			*slash = 0;
-			pw = getpwnam(path);
-			*slash = '/';
+/*
+ * Return a string with ~ and ~user expanded via getpw*.  If buf != NULL,
+ * then it is a newly allocated string. Returns NULL on getpw failure or
+ * if path is NULL.
+ */
+char *expand_user_path(const char *path)
+{
+	struct strbuf user_path = STRBUF_INIT;
+	const char *first_slash = strchrnul(path, '/');
+	const char *to_copy = path;
+
+	if (path == NULL)
+		goto return_null;
+	if (path[0] == '~') {
+		const char *username = path + 1;
+		size_t username_len = first_slash - username;
+		if (username_len == 0) {
+			const char *home = getenv("HOME");
+			if (!home)
+				goto return_null;
+			strbuf_add(&user_path, home, strlen(home));
+		} else {
+			struct passwd *pw = getpw_str(username, username_len);
+			if (!pw)
+				goto return_null;
+			strbuf_add(&user_path, pw->pw_dir, strlen(pw->pw_dir));
 		}
-		else
-			pw = getpwnam(path);
+		to_copy = first_slash;
 	}
-	if (!pw || !pw->pw_dir || sz <= strlen(pw->pw_dir))
-		return NULL;
-	baselen = strlen(pw->pw_dir);
-	memcpy(buf, pw->pw_dir, baselen);
-	while ((1 < baselen) && (buf[baselen-1] == '/')) {
-		buf[baselen-1] = 0;
-		baselen--;
-	}
-	if (slash && slash[1]) {
-		len = strlen(slash);
-		if (sz <= baselen + len)
-			return NULL;
-		memcpy(buf + baselen, slash, len + 1);
-	}
-	return buf;
+	strbuf_add(&user_path, to_copy, strlen(to_copy));
+	return strbuf_detach(&user_path, NULL);
+return_null:
+	strbuf_release(&user_path);
+	return NULL;
 }
 
 /*
@@ -270,7 +324,7 @@
  * links.  User relative paths are also returned as they are given,
  * except DWIM suffixing.
  */
-char *enter_repo(char *path, int strict)
+const char *enter_repo(const char *path, int strict)
 {
 	static char used_path[PATH_MAX];
 	static char validated_path[PATH_MAX];
@@ -280,37 +334,52 @@
 
 	if (!strict) {
 		static const char *suffix[] = {
-			".git/.git", "/.git", ".git", "", NULL,
+			"/.git", "", ".git/.git", ".git", NULL,
 		};
+		const char *gitfile;
 		int len = strlen(path);
 		int i;
-		while ((1 < len) && (path[len-1] == '/')) {
-			path[len-1] = 0;
+		while ((1 < len) && (path[len-1] == '/'))
 			len--;
-		}
+
 		if (PATH_MAX <= len)
 			return NULL;
-		if (path[0] == '~') {
-			if (!user_path(used_path, path, PATH_MAX))
+		strncpy(used_path, path, len); used_path[len] = 0 ;
+		strcpy(validated_path, used_path);
+
+		if (used_path[0] == '~') {
+			char *newpath = expand_user_path(used_path);
+			if (!newpath || (PATH_MAX - 10 < strlen(newpath))) {
+				free(newpath);
 				return NULL;
-			strcpy(validated_path, path);
-			path = used_path;
+			}
+			/*
+			 * Copy back into the static buffer. A pity
+			 * since newpath was not bounded, but other
+			 * branches of the if are limited by PATH_MAX
+			 * anyway.
+			 */
+			strcpy(used_path, newpath); free(newpath);
 		}
 		else if (PATH_MAX - 10 < len)
 			return NULL;
-		else {
-			path = strcpy(used_path, path);
-			strcpy(validated_path, path);
-		}
-		len = strlen(path);
+		len = strlen(used_path);
 		for (i = 0; suffix[i]; i++) {
-			strcpy(path + len, suffix[i]);
-			if (!access(path, F_OK)) {
+			struct stat st;
+			strcpy(used_path + len, suffix[i]);
+			if (!stat(used_path, &st) &&
+			    (S_ISREG(st.st_mode) ||
+			    (S_ISDIR(st.st_mode) && is_git_directory(used_path)))) {
 				strcat(validated_path, suffix[i]);
 				break;
 			}
 		}
-		if (!suffix[i] || chdir(path))
+		if (!suffix[i])
+			return NULL;
+		gitfile = read_gitfile(used_path) ;
+		if (gitfile)
+			strcpy(used_path, gitfile);
+		if (chdir(used_path))
 			return NULL;
 		path = validated_path;
 	}
@@ -319,7 +388,7 @@
 
 	if (access("objects", X_OK) == 0 && access("refs", X_OK) == 0 &&
 	    validate_headref("HEAD") == 0) {
-		setenv(GIT_DIR_ENVIRONMENT, ".", 1);
+		set_git_dir(".");
 		check_repository_format();
 		return path;
 	}
@@ -374,20 +443,41 @@
 	return 0;
 }
 
-const char *make_relative_path(const char *abs, const char *base)
+const char *relative_path(const char *abs, const char *base)
 {
 	static char buf[PATH_MAX + 1];
-	int baselen;
-	if (!base)
+	int i = 0, j = 0;
+
+	if (!base || !base[0])
 		return abs;
-	baselen = strlen(base);
-	if (prefixcmp(abs, base))
+	while (base[i]) {
+		if (is_dir_sep(base[i])) {
+			if (!is_dir_sep(abs[j]))
+				return abs;
+			while (is_dir_sep(base[i]))
+				i++;
+			while (is_dir_sep(abs[j]))
+				j++;
+			continue;
+		} else if (abs[j] != base[i]) {
+			return abs;
+		}
+		i++;
+		j++;
+	}
+	if (
+	    /* "/foo" is a prefix of "/foo" */
+	    abs[j] &&
+	    /* "/foo" is not a prefix of "/foobar" */
+	    !is_dir_sep(base[i-1]) && !is_dir_sep(abs[j])
+	   )
 		return abs;
-	if (abs[baselen] == '/')
-		baselen++;
-	else if (base[baselen - 1] != '/')
-		return abs;
-	strcpy(buf, abs + baselen);
+	while (is_dir_sep(abs[j]))
+		j++;
+	if (!abs[j])
+		strcpy(buf, ".");
+	else
+		strcpy(buf, abs + j);
 	return buf;
 }
 
@@ -564,3 +654,57 @@
 		return NULL;
 	return xstrndup(path, chomp_trailing_dir_sep(path, path_len));
 }
+
+int daemon_avoid_alias(const char *p)
+{
+	int sl, ndot;
+
+	/*
+	 * This resurrects the belts and suspenders paranoia check by HPA
+	 * done in <435560F7.4080006@zytor.com> thread, now enter_repo()
+	 * does not do getcwd() based path canonicalization.
+	 *
+	 * sl becomes true immediately after seeing '/' and continues to
+	 * be true as long as dots continue after that without intervening
+	 * non-dot character.
+	 */
+	if (!p || (*p != '/' && *p != '~'))
+		return -1;
+	sl = 1; ndot = 0;
+	p++;
+
+	while (1) {
+		char ch = *p++;
+		if (sl) {
+			if (ch == '.')
+				ndot++;
+			else if (ch == '/') {
+				if (ndot < 3)
+					/* reject //, /./ and /../ */
+					return -1;
+				ndot = 0;
+			}
+			else if (ch == 0) {
+				if (0 < ndot && ndot < 3)
+					/* reject /.$ and /..$ */
+					return -1;
+				return 0;
+			}
+			else
+				sl = ndot = 0;
+		}
+		else if (ch == 0)
+			return 0;
+		else if (ch == '/') {
+			sl = 1;
+			ndot = 0;
+		}
+	}
+}
+
+int offset_1st_component(const char *path)
+{
+	if (has_dos_drive_prefix(path))
+		return 2 + is_dir_sep(path[2]);
+	return is_dir_sep(path[0]);
+}
diff --git a/perl/.gitignore b/perl/.gitignore
index 98b2477..d5c6e22 100644
--- a/perl/.gitignore
+++ b/perl/.gitignore
@@ -1,5 +1,7 @@
 perl.mak
 perl.mak.old
+MYMETA.json
+MYMETA.yml
 blib
 blibdirs
 pm_to_blib
diff --git a/perl/Git.pm b/perl/Git.pm
index e8df55d..497f420 100644
--- a/perl/Git.pm
+++ b/perl/Git.pm
@@ -7,6 +7,7 @@
 
 package Git;
 
+use 5.008;
 use strict;
 
 
@@ -98,7 +99,7 @@
 
 use Carp qw(carp croak); # but croak is bad - throw instead
 use Error qw(:try);
-use Cwd qw(abs_path);
+use Cwd qw(abs_path cwd);
 use IPC::Open2 qw(open2);
 use Fcntl qw(SEEK_SET SEEK_CUR);
 }
@@ -172,7 +173,7 @@
 	}
 
 	if (defined $opts{Directory}) {
-		-d $opts{Directory} or throw Error::Simple("Directory not found: $!");
+		-d $opts{Directory} or throw Error::Simple("Directory not found: $opts{Directory} $!");
 
 		my $search = Git->repository(WorkingCopy => $opts{Directory});
 		my $dir;
@@ -204,14 +205,14 @@
 			$dir = $opts{Directory};
 
 			unless (-d "$dir/refs" and -d "$dir/objects" and -e "$dir/HEAD") {
-				# Mimick git-rev-parse --git-dir error message:
+				# Mimic git-rev-parse --git-dir error message:
 				throw Error::Simple("fatal: Not a git repository: $dir");
 			}
 			my $search = Git->repository(Repository => $dir);
 			try {
 				$search->command('symbolic-ref', 'HEAD');
 			} catch Git::Error::Command with {
-				# Mimick git-rev-parse --git-dir error message:
+				# Mimic git-rev-parse --git-dir error message:
 				throw Error::Simple("fatal: Not a git repository: $dir");
 			}
 
@@ -395,7 +396,16 @@
 
 sub command_bidi_pipe {
 	my ($pid, $in, $out);
+	my ($self) = _maybe_self(@_);
+	local %ENV = %ENV;
+	my $cwd_save = undef;
+	if ($self) {
+		shift;
+		$cwd_save = cwd();
+		_setup_git_cmd_env($self);
+	}
 	$pid = open2($in, $out, 'git', @_);
+	chdir($cwd_save) if $cwd_save;
 	return ($pid, $in, $out, join(' ', @_));
 }
 
@@ -545,7 +555,7 @@
 		or throw Error::Simple("bare repository");
 
 	-d $self->wc_path().'/'.$subdir
-		or throw Error::Simple("subdir not found: $!");
+		or throw Error::Simple("subdir not found: $subdir $!");
 	# Of course we will not "hold" the subdirectory so anyone
 	# can delete it now and we will never know. But at least we tried.
 
@@ -560,15 +570,68 @@
 (exception is thrown otherwise), in array context returns allows the
 variable to be set multiple times and returns all the values.
 
-This currently wraps command('config') so it is not so fast.
-
 =cut
 
 sub config {
+	return _config_common({}, @_);
+}
+
+
+=item config_bool ( VARIABLE )
+
+Retrieve the bool configuration C<VARIABLE>. The return value
+is usable as a boolean in perl (and C<undef> if it's not defined,
+of course).
+
+=cut
+
+sub config_bool {
+	my $val = scalar _config_common({'kind' => '--bool'}, @_);
+
+	# Do not rewrite this as return (defined $val && $val eq 'true')
+	# as some callers do care what kind of falsehood they receive.
+	if (!defined $val) {
+		return undef;
+	} else {
+		return $val eq 'true';
+	}
+}
+
+
+=item config_path ( VARIABLE )
+
+Retrieve the path configuration C<VARIABLE>. The return value
+is an expanded path or C<undef> if it's not defined.
+
+=cut
+
+sub config_path {
+	return _config_common({'kind' => '--path'}, @_);
+}
+
+
+=item config_int ( VARIABLE )
+
+Retrieve the integer configuration C<VARIABLE>. The return value
+is simple decimal number.  An optional value suffix of 'k', 'm',
+or 'g' in the config file will cause the value to be multiplied
+by 1024, 1048576 (1024^2), or 1073741824 (1024^3) prior to output.
+It would return C<undef> if configuration variable is not defined,
+
+=cut
+
+sub config_int {
+	return scalar _config_common({'kind' => '--int'}, @_);
+}
+
+# Common subroutine to implement bulk of what the config* family of methods
+# do. This curently wraps command('config') so it is not so fast.
+sub _config_common {
+	my ($opts) = shift @_;
 	my ($self, $var) = _maybe_self(@_);
 
 	try {
-		my @cmd = ('config');
+		my @cmd = ('config', $opts->{'kind'} ? $opts->{'kind'} : ());
 		unshift @cmd, $self if $self;
 		if (wantarray) {
 			return command(@cmd, '--get-all', $var);
@@ -586,67 +649,6 @@
 	};
 }
 
-
-=item config_bool ( VARIABLE )
-
-Retrieve the bool configuration C<VARIABLE>. The return value
-is usable as a boolean in perl (and C<undef> if it's not defined,
-of course).
-
-This currently wraps command('config') so it is not so fast.
-
-=cut
-
-sub config_bool {
-	my ($self, $var) = _maybe_self(@_);
-
-	try {
-		my @cmd = ('config', '--bool', '--get', $var);
-		unshift @cmd, $self if $self;
-		my $val = command_oneline(@cmd);
-		return undef unless defined $val;
-		return $val eq 'true';
-	} catch Git::Error::Command with {
-		my $E = shift;
-		if ($E->value() == 1) {
-			# Key not found.
-			return undef;
-		} else {
-			throw $E;
-		}
-	};
-}
-
-=item config_int ( VARIABLE )
-
-Retrieve the integer configuration C<VARIABLE>. The return value
-is simple decimal number.  An optional value suffix of 'k', 'm',
-or 'g' in the config file will cause the value to be multiplied
-by 1024, 1048576 (1024^2), or 1073741824 (1024^3) prior to output.
-It would return C<undef> if configuration variable is not defined,
-
-This currently wraps command('config') so it is not so fast.
-
-=cut
-
-sub config_int {
-	my ($self, $var) = _maybe_self(@_);
-
-	try {
-		my @cmd = ('config', '--int', '--get', $var);
-		unshift @cmd, $self if $self;
-		return command_oneline(@cmd);
-	} catch Git::Error::Command with {
-		my $E = shift;
-		if ($E->value() == 1) {
-			# Key not found.
-			return undef;
-		} else {
-			throw $E;
-		}
-	};
-}
-
 =item get_colorbool ( NAME )
 
 Finds if color should be used for NAMEd operation from the configuration,
@@ -689,7 +691,7 @@
 contains the tag object while a C<refname^{}> entry gives the tagged objects.
 
 C<REPOSITORY> has the same meaning as the appropriate C<git-ls-remote>
-argument; either an URL or a remote name (if called on a repository instance).
+argument; either a URL or a remote name (if called on a repository instance).
 C<GROUPS> is an optional arrayref that can contain 'tags' to return all the
 tags and/or 'heads' to return all the heads. C<REFGLOB> is an optional array
 of strings containing a shell-like glob to further limit the refs returned in
@@ -842,7 +844,7 @@
 
 	($self->{hash_object_pid}, $self->{hash_object_in},
 	 $self->{hash_object_out}, $self->{hash_object_ctx}) =
-		command_bidi_pipe(qw(hash-object -w --stdin-paths));
+		$self->command_bidi_pipe(qw(hash-object -w --stdin-paths --no-filters));
 }
 
 sub _close_hash_and_insert_object {
@@ -931,7 +933,7 @@
 
 	($self->{cat_blob_pid}, $self->{cat_blob_in},
 	 $self->{cat_blob_out}, $self->{cat_blob_ctx}) =
-		command_bidi_pipe(qw(cat-file --batch));
+		$self->command_bidi_pipe(qw(cat-file --batch));
 }
 
 sub _close_cat_blob {
@@ -1278,6 +1280,14 @@
 # for the given repository and execute the git command.
 sub _cmd_exec {
 	my ($self, @args) = @_;
+	_setup_git_cmd_env($self);
+	_execv_git_cmd(@args);
+	die qq[exec "@args" failed: $!];
+}
+
+# set up the appropriate state for git command
+sub _setup_git_cmd_env {
+	my $self = shift;
 	if ($self) {
 		$self->repo_path() and $ENV{'GIT_DIR'} = $self->repo_path();
 		$self->repo_path() and $self->wc_path()
@@ -1285,8 +1295,6 @@
 		$self->wc_path() and chdir($self->wc_path());
 		$self->wc_subdir() and chdir($self->wc_subdir());
 	}
-	_execv_git_cmd(@args);
-	die qq[exec "@args" failed: $!];
 }
 
 # Execute the given Git command ($_[0]) with arguments ($_[1..])
diff --git a/perl/Git/I18N.pm b/perl/Git/I18N.pm
new file mode 100644
index 0000000..40dd897
--- /dev/null
+++ b/perl/Git/I18N.pm
@@ -0,0 +1,98 @@
+package Git::I18N;
+use 5.008;
+use strict;
+use warnings;
+BEGIN {
+	require Exporter;
+	if ($] < 5.008003) {
+		*import = \&Exporter::import;
+	} else {
+		# Exporter 5.57 which supports this invocation was
+		# released with perl 5.8.3
+		Exporter->import('import');
+	}
+}
+
+our @EXPORT = qw(__);
+our @EXPORT_OK = @EXPORT;
+
+sub __bootstrap_locale_messages {
+	our $TEXTDOMAIN = 'git';
+	our $TEXTDOMAINDIR = $ENV{GIT_TEXTDOMAINDIR} || '++LOCALEDIR++';
+
+	require POSIX;
+	POSIX->import(qw(setlocale));
+	# Non-core prerequisite module
+	require Locale::Messages;
+	Locale::Messages->import(qw(:locale_h :libintl_h));
+
+	setlocale(LC_MESSAGES(), '');
+	setlocale(LC_CTYPE(), '');
+	textdomain($TEXTDOMAIN);
+	bindtextdomain($TEXTDOMAIN => $TEXTDOMAINDIR);
+
+	return;
+}
+
+BEGIN
+{
+	# Used by our test script to see if it should test fallbacks or
+	# not.
+	our $__HAS_LIBRARY = 1;
+
+	local $@;
+	eval {
+		__bootstrap_locale_messages();
+		*__ = \&Locale::Messages::gettext;
+		1;
+	} or do {
+		# Tell test.pl that we couldn't load the gettext library.
+		$Git::I18N::__HAS_LIBRARY = 0;
+
+		# Just a fall-through no-op
+		*__ = sub ($) { $_[0] };
+	};
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Git::I18N - Perl interface to Git's Gettext localizations
+
+=head1 SYNOPSIS
+
+	use Git::I18N;
+
+	print __("Welcome to Git!\n");
+
+	printf __("The following error occured: %s\n"), $error;
+
+=head1 DESCRIPTION
+
+Git's internal Perl interface to gettext via L<Locale::Messages>. If
+L<Locale::Messages> can't be loaded (it's not a core module) we
+provide stub passthrough fallbacks.
+
+This is a distilled interface to gettext, see C<info '(gettext)Perl'>
+for the full interface. This module implements only a small part of
+it.
+
+=head1 FUNCTIONS
+
+=head2 __($)
+
+L<Locale::Messages>'s gettext function if all goes well, otherwise our
+passthrough fallback function.
+
+=head1 AUTHOR
+
+E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason <avarab@gmail.com>
+
+=head1 COPYRIGHT
+
+Copyright 2010 E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason <avarab@gmail.com>
+
+=cut
diff --git a/perl/Git/SVN/Editor.pm b/perl/Git/SVN/Editor.pm
new file mode 100644
index 0000000..755092f
--- /dev/null
+++ b/perl/Git/SVN/Editor.pm
@@ -0,0 +1,536 @@
+package Git::SVN::Editor;
+use vars qw/@ISA $_rmdir $_cp_similarity $_find_copies_harder $_rename_limit/;
+use strict;
+use warnings;
+use SVN::Core;
+use SVN::Delta;
+use Carp qw/croak/;
+use IO::File;
+use Git qw/command command_oneline command_noisy command_output_pipe
+           command_input_pipe command_close_pipe
+           command_bidi_pipe command_close_bidi_pipe/;
+BEGIN {
+	@ISA = qw(SVN::Delta::Editor);
+}
+
+sub new {
+	my ($class, $opts) = @_;
+	foreach (qw/svn_path r ra tree_a tree_b log editor_cb/) {
+		die "$_ required!\n" unless (defined $opts->{$_});
+	}
+
+	my $pool = SVN::Pool->new;
+	my $mods = generate_diff($opts->{tree_a}, $opts->{tree_b});
+	my $types = check_diff_paths($opts->{ra}, $opts->{svn_path},
+	                             $opts->{r}, $mods);
+
+	# $opts->{ra} functions should not be used after this:
+	my @ce  = $opts->{ra}->get_commit_editor($opts->{log},
+	                                        $opts->{editor_cb}, $pool);
+	my $self = SVN::Delta::Editor->new(@ce, $pool);
+	bless $self, $class;
+	foreach (qw/svn_path r tree_a tree_b/) {
+		$self->{$_} = $opts->{$_};
+	}
+	$self->{url} = $opts->{ra}->{url};
+	$self->{mods} = $mods;
+	$self->{types} = $types;
+	$self->{pool} = $pool;
+	$self->{bat} = { '' => $self->open_root($self->{r}, $self->{pool}) };
+	$self->{rm} = { };
+	$self->{path_prefix} = length $self->{svn_path} ?
+	                       "$self->{svn_path}/" : '';
+	$self->{config} = $opts->{config};
+	$self->{mergeinfo} = $opts->{mergeinfo};
+	return $self;
+}
+
+sub generate_diff {
+	my ($tree_a, $tree_b) = @_;
+	my @diff_tree = qw(diff-tree -z -r);
+	if ($_cp_similarity) {
+		push @diff_tree, "-C$_cp_similarity";
+	} else {
+		push @diff_tree, '-C';
+	}
+	push @diff_tree, '--find-copies-harder' if $_find_copies_harder;
+	push @diff_tree, "-l$_rename_limit" if defined $_rename_limit;
+	push @diff_tree, $tree_a, $tree_b;
+	my ($diff_fh, $ctx) = command_output_pipe(@diff_tree);
+	local $/ = "\0";
+	my $state = 'meta';
+	my @mods;
+	while (<$diff_fh>) {
+		chomp $_; # this gets rid of the trailing "\0"
+		if ($state eq 'meta' && /^:(\d{6})\s(\d{6})\s
+					($::sha1)\s($::sha1)\s
+					([MTCRAD])\d*$/xo) {
+			push @mods, {	mode_a => $1, mode_b => $2,
+					sha1_a => $3, sha1_b => $4,
+					chg => $5 };
+			if ($5 =~ /^(?:C|R)$/) {
+				$state = 'file_a';
+			} else {
+				$state = 'file_b';
+			}
+		} elsif ($state eq 'file_a') {
+			my $x = $mods[$#mods] or croak "Empty array\n";
+			if ($x->{chg} !~ /^(?:C|R)$/) {
+				croak "Error parsing $_, $x->{chg}\n";
+			}
+			$x->{file_a} = $_;
+			$state = 'file_b';
+		} elsif ($state eq 'file_b') {
+			my $x = $mods[$#mods] or croak "Empty array\n";
+			if (exists $x->{file_a} && $x->{chg} !~ /^(?:C|R)$/) {
+				croak "Error parsing $_, $x->{chg}\n";
+			}
+			if (!exists $x->{file_a} && $x->{chg} =~ /^(?:C|R)$/) {
+				croak "Error parsing $_, $x->{chg}\n";
+			}
+			$x->{file_b} = $_;
+			$state = 'meta';
+		} else {
+			croak "Error parsing $_\n";
+		}
+	}
+	command_close_pipe($diff_fh, $ctx);
+	\@mods;
+}
+
+sub check_diff_paths {
+	my ($ra, $pfx, $rev, $mods) = @_;
+	my %types;
+	$pfx .= '/' if length $pfx;
+
+	sub type_diff_paths {
+		my ($ra, $types, $path, $rev) = @_;
+		my @p = split m#/+#, $path;
+		my $c = shift @p;
+		unless (defined $types->{$c}) {
+			$types->{$c} = $ra->check_path($c, $rev);
+		}
+		while (@p) {
+			$c .= '/' . shift @p;
+			next if defined $types->{$c};
+			$types->{$c} = $ra->check_path($c, $rev);
+		}
+	}
+
+	foreach my $m (@$mods) {
+		foreach my $f (qw/file_a file_b/) {
+			next unless defined $m->{$f};
+			my ($dir) = ($m->{$f} =~ m#^(.*?)/?(?:[^/]+)$#);
+			if (length $pfx.$dir && ! defined $types{$dir}) {
+				type_diff_paths($ra, \%types, $pfx.$dir, $rev);
+			}
+		}
+	}
+	\%types;
+}
+
+sub split_path {
+	return ($_[0] =~ m#^(.*?)/?([^/]+)$#);
+}
+
+sub repo_path {
+	my ($self, $path) = @_;
+	if (my $enc = $self->{pathnameencoding}) {
+		require Encode;
+		Encode::from_to($path, $enc, 'UTF-8');
+	}
+	$self->{path_prefix}.(defined $path ? $path : '');
+}
+
+sub url_path {
+	my ($self, $path) = @_;
+	if ($self->{url} =~ m#^https?://#) {
+		$path =~ s!([^~a-zA-Z0-9_./-])!uc sprintf("%%%02x",ord($1))!eg;
+	}
+	$self->{url} . '/' . $self->repo_path($path);
+}
+
+sub rmdirs {
+	my ($self) = @_;
+	my $rm = $self->{rm};
+	delete $rm->{''}; # we never delete the url we're tracking
+	return unless %$rm;
+
+	foreach (keys %$rm) {
+		my @d = split m#/#, $_;
+		my $c = shift @d;
+		$rm->{$c} = 1;
+		while (@d) {
+			$c .= '/' . shift @d;
+			$rm->{$c} = 1;
+		}
+	}
+	delete $rm->{$self->{svn_path}};
+	delete $rm->{''}; # we never delete the url we're tracking
+	return unless %$rm;
+
+	my ($fh, $ctx) = command_output_pipe(qw/ls-tree --name-only -r -z/,
+	                                     $self->{tree_b});
+	local $/ = "\0";
+	while (<$fh>) {
+		chomp;
+		my @dn = split m#/#, $_;
+		while (pop @dn) {
+			delete $rm->{join '/', @dn};
+		}
+		unless (%$rm) {
+			close $fh;
+			return;
+		}
+	}
+	command_close_pipe($fh, $ctx);
+
+	my ($r, $p, $bat) = ($self->{r}, $self->{pool}, $self->{bat});
+	foreach my $d (sort { $b =~ tr#/#/# <=> $a =~ tr#/#/# } keys %$rm) {
+		$self->close_directory($bat->{$d}, $p);
+		my ($dn) = ($d =~ m#^(.*?)/?(?:[^/]+)$#);
+		print "\tD+\t$d/\n" unless $::_q;
+		$self->SUPER::delete_entry($d, $r, $bat->{$dn}, $p);
+		delete $bat->{$d};
+	}
+}
+
+sub open_or_add_dir {
+	my ($self, $full_path, $baton, $deletions) = @_;
+	my $t = $self->{types}->{$full_path};
+	if (!defined $t) {
+		die "$full_path not known in r$self->{r} or we have a bug!\n";
+	}
+	{
+		no warnings 'once';
+		# SVN::Node::none and SVN::Node::file are used only once,
+		# so we're shutting up Perl's warnings about them.
+		if ($t == $SVN::Node::none || defined($deletions->{$full_path})) {
+			return $self->add_directory($full_path, $baton,
+			    undef, -1, $self->{pool});
+		} elsif ($t == $SVN::Node::dir) {
+			return $self->open_directory($full_path, $baton,
+			    $self->{r}, $self->{pool});
+		} # no warnings 'once'
+		print STDERR "$full_path already exists in repository at ",
+		    "r$self->{r} and it is not a directory (",
+		    ($t == $SVN::Node::file ? 'file' : 'unknown'),"/$t)\n";
+	} # no warnings 'once'
+	exit 1;
+}
+
+sub ensure_path {
+	my ($self, $path, $deletions) = @_;
+	my $bat = $self->{bat};
+	my $repo_path = $self->repo_path($path);
+	return $bat->{''} unless (length $repo_path);
+
+	my @p = split m#/+#, $repo_path;
+	my $c = shift @p;
+	$bat->{$c} ||= $self->open_or_add_dir($c, $bat->{''}, $deletions);
+	while (@p) {
+		my $c0 = $c;
+		$c .= '/' . shift @p;
+		$bat->{$c} ||= $self->open_or_add_dir($c, $bat->{$c0}, $deletions);
+	}
+	return $bat->{$c};
+}
+
+# Subroutine to convert a globbing pattern to a regular expression.
+# From perl cookbook.
+sub glob2pat {
+	my $globstr = shift;
+	my %patmap = ('*' => '.*', '?' => '.', '[' => '[', ']' => ']');
+	$globstr =~ s{(.)} { $patmap{$1} || "\Q$1" }ge;
+	return '^' . $globstr . '$';
+}
+
+sub check_autoprop {
+	my ($self, $pattern, $properties, $file, $fbat) = @_;
+	# Convert the globbing pattern to a regular expression.
+	my $regex = glob2pat($pattern);
+	# Check if the pattern matches the file name.
+	if($file =~ m/($regex)/) {
+		# Parse the list of properties to set.
+		my @props = split(/;/, $properties);
+		foreach my $prop (@props) {
+			# Parse 'name=value' syntax and set the property.
+			if ($prop =~ /([^=]+)=(.*)/) {
+				my ($n,$v) = ($1,$2);
+				for ($n, $v) {
+					s/^\s+//; s/\s+$//;
+				}
+				$self->change_file_prop($fbat, $n, $v);
+			}
+		}
+	}
+}
+
+sub apply_autoprops {
+	my ($self, $file, $fbat) = @_;
+	my $conf_t = ${$self->{config}}{'config'};
+	no warnings 'once';
+	# Check [miscellany]/enable-auto-props in svn configuration.
+	if (SVN::_Core::svn_config_get_bool(
+		$conf_t,
+		$SVN::_Core::SVN_CONFIG_SECTION_MISCELLANY,
+		$SVN::_Core::SVN_CONFIG_OPTION_ENABLE_AUTO_PROPS,
+		0)) {
+		# Auto-props are enabled.  Enumerate them to look for matches.
+		my $callback = sub {
+			$self->check_autoprop($_[0], $_[1], $file, $fbat);
+		};
+		SVN::_Core::svn_config_enumerate(
+			$conf_t,
+			$SVN::_Core::SVN_CONFIG_SECTION_AUTO_PROPS,
+			$callback);
+	}
+}
+
+sub A {
+	my ($self, $m, $deletions) = @_;
+	my ($dir, $file) = split_path($m->{file_b});
+	my $pbat = $self->ensure_path($dir, $deletions);
+	my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat,
+					undef, -1);
+	print "\tA\t$m->{file_b}\n" unless $::_q;
+	$self->apply_autoprops($file, $fbat);
+	$self->chg_file($fbat, $m);
+	$self->close_file($fbat,undef,$self->{pool});
+}
+
+sub C {
+	my ($self, $m, $deletions) = @_;
+	my ($dir, $file) = split_path($m->{file_b});
+	my $pbat = $self->ensure_path($dir, $deletions);
+	my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat,
+				$self->url_path($m->{file_a}), $self->{r});
+	print "\tC\t$m->{file_a} => $m->{file_b}\n" unless $::_q;
+	$self->chg_file($fbat, $m);
+	$self->close_file($fbat,undef,$self->{pool});
+}
+
+sub delete_entry {
+	my ($self, $path, $pbat) = @_;
+	my $rpath = $self->repo_path($path);
+	my ($dir, $file) = split_path($rpath);
+	$self->{rm}->{$dir} = 1;
+	$self->SUPER::delete_entry($rpath, $self->{r}, $pbat, $self->{pool});
+}
+
+sub R {
+	my ($self, $m, $deletions) = @_;
+	my ($dir, $file) = split_path($m->{file_b});
+	my $pbat = $self->ensure_path($dir, $deletions);
+	my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat,
+				$self->url_path($m->{file_a}), $self->{r});
+	print "\tR\t$m->{file_a} => $m->{file_b}\n" unless $::_q;
+	$self->apply_autoprops($file, $fbat);
+	$self->chg_file($fbat, $m);
+	$self->close_file($fbat,undef,$self->{pool});
+
+	($dir, $file) = split_path($m->{file_a});
+	$pbat = $self->ensure_path($dir, $deletions);
+	$self->delete_entry($m->{file_a}, $pbat);
+}
+
+sub M {
+	my ($self, $m, $deletions) = @_;
+	my ($dir, $file) = split_path($m->{file_b});
+	my $pbat = $self->ensure_path($dir, $deletions);
+	my $fbat = $self->open_file($self->repo_path($m->{file_b}),
+				$pbat,$self->{r},$self->{pool});
+	print "\t$m->{chg}\t$m->{file_b}\n" unless $::_q;
+	$self->chg_file($fbat, $m);
+	$self->close_file($fbat,undef,$self->{pool});
+}
+
+sub T { shift->M(@_) }
+
+sub change_file_prop {
+	my ($self, $fbat, $pname, $pval) = @_;
+	$self->SUPER::change_file_prop($fbat, $pname, $pval, $self->{pool});
+}
+
+sub change_dir_prop {
+	my ($self, $pbat, $pname, $pval) = @_;
+	$self->SUPER::change_dir_prop($pbat, $pname, $pval, $self->{pool});
+}
+
+sub _chg_file_get_blob ($$$$) {
+	my ($self, $fbat, $m, $which) = @_;
+	my $fh = $::_repository->temp_acquire("git_blob_$which");
+	if ($m->{"mode_$which"} =~ /^120/) {
+		print $fh 'link ' or croak $!;
+		$self->change_file_prop($fbat,'svn:special','*');
+	} elsif ($m->{mode_a} =~ /^120/ && $m->{"mode_$which"} !~ /^120/) {
+		$self->change_file_prop($fbat,'svn:special',undef);
+	}
+	my $blob = $m->{"sha1_$which"};
+	return ($fh,) if ($blob =~ /^0{40}$/);
+	my $size = $::_repository->cat_blob($blob, $fh);
+	croak "Failed to read object $blob" if ($size < 0);
+	$fh->flush == 0 or croak $!;
+	seek $fh, 0, 0 or croak $!;
+
+	my $exp = ::md5sum($fh);
+	seek $fh, 0, 0 or croak $!;
+	return ($fh, $exp);
+}
+
+sub chg_file {
+	my ($self, $fbat, $m) = @_;
+	if ($m->{mode_b} =~ /755$/ && $m->{mode_a} !~ /755$/) {
+		$self->change_file_prop($fbat,'svn:executable','*');
+	} elsif ($m->{mode_b} !~ /755$/ && $m->{mode_a} =~ /755$/) {
+		$self->change_file_prop($fbat,'svn:executable',undef);
+	}
+	my ($fh_a, $exp_a) = _chg_file_get_blob $self, $fbat, $m, 'a';
+	my ($fh_b, $exp_b) = _chg_file_get_blob $self, $fbat, $m, 'b';
+	my $pool = SVN::Pool->new;
+	my $atd = $self->apply_textdelta($fbat, $exp_a, $pool);
+	if (-s $fh_a) {
+		my $txstream = SVN::TxDelta::new ($fh_a, $fh_b, $pool);
+		my $res = SVN::TxDelta::send_txstream($txstream, @$atd, $pool);
+		if (defined $res) {
+			die "Unexpected result from send_txstream: $res\n",
+			    "(SVN::Core::VERSION: $SVN::Core::VERSION)\n";
+		}
+	} else {
+		my $got = SVN::TxDelta::send_stream($fh_b, @$atd, $pool);
+		die "Checksum mismatch\nexpected: $exp_b\ngot: $got\n"
+		    if ($got ne $exp_b);
+	}
+	Git::temp_release($fh_b, 1);
+	Git::temp_release($fh_a, 1);
+	$pool->clear;
+}
+
+sub D {
+	my ($self, $m, $deletions) = @_;
+	my ($dir, $file) = split_path($m->{file_b});
+	my $pbat = $self->ensure_path($dir, $deletions);
+	print "\tD\t$m->{file_b}\n" unless $::_q;
+	$self->delete_entry($m->{file_b}, $pbat);
+}
+
+sub close_edit {
+	my ($self) = @_;
+	my ($p,$bat) = ($self->{pool}, $self->{bat});
+	foreach (sort { $b =~ tr#/#/# <=> $a =~ tr#/#/# } keys %$bat) {
+		next if $_ eq '';
+		$self->close_directory($bat->{$_}, $p);
+	}
+	$self->close_directory($bat->{''}, $p);
+	$self->SUPER::close_edit($p);
+	$p->clear;
+}
+
+sub abort_edit {
+	my ($self) = @_;
+	$self->SUPER::abort_edit($self->{pool});
+}
+
+sub DESTROY {
+	my $self = shift;
+	$self->SUPER::DESTROY(@_);
+	$self->{pool}->clear;
+}
+
+# this drives the editor
+sub apply_diff {
+	my ($self) = @_;
+	my $mods = $self->{mods};
+	my %o = ( D => 0, C => 1, R => 2, A => 3, M => 4, T => 5 );
+	my %deletions;
+
+	foreach my $m (@$mods) {
+		if ($m->{chg} eq "D") {
+			$deletions{$m->{file_b}} = 1;
+		}
+	}
+
+	foreach my $m (sort { $o{$a->{chg}} <=> $o{$b->{chg}} } @$mods) {
+		my $f = $m->{chg};
+		if (defined $o{$f}) {
+			$self->$f($m, \%deletions);
+		} else {
+			fatal("Invalid change type: $f");
+		}
+	}
+
+	if (defined($self->{mergeinfo})) {
+		$self->change_dir_prop($self->{bat}{''}, "svn:mergeinfo",
+			               $self->{mergeinfo});
+	}
+	$self->rmdirs if $_rmdir;
+	if (@$mods == 0 && !defined($self->{mergeinfo})) {
+		$self->abort_edit;
+	} else {
+		$self->close_edit;
+	}
+	return scalar @$mods;
+}
+
+1;
+__END__
+
+Git::SVN::Editor - commit driver for "git svn set-tree" and dcommit
+
+=head1 SYNOPSIS
+
+	use Git::SVN::Editor;
+	use Git::SVN::Ra;
+
+	my $ra = Git::SVN::Ra->new($url);
+	my %opts = (
+		r => 19,
+		log => "log message",
+		ra => $ra,
+		config => SVN::Core::config_get_config($svn_config_dir),
+		tree_a => "$commit^",
+		tree_b => "$commit",
+		editor_cb => sub { print "Committed r$_[0]\n"; },
+		mergeinfo => "/branches/foo:1-10",
+		svn_path => "trunk"
+	);
+	Git::SVN::Editor->new(\%opts)->apply_diff or print "No changes\n";
+
+	my $re = Git::SVN::Editor::glob2pat("trunk/*");
+	if ($branchname =~ /$re/) {
+		print "matched!\n";
+	}
+
+=head1 DESCRIPTION
+
+This module is an implementation detail of the "git svn" command.
+Do not use it unless you are developing git-svn.
+
+This module adapts the C<SVN::Delta::Editor> object returned by
+C<SVN::Delta::get_commit_editor> and drives it to convey the
+difference between two git tree objects to a remote Subversion
+repository.
+
+The interface will change as git-svn evolves.
+
+=head1 DEPENDENCIES
+
+Subversion perl bindings,
+the core L<Carp> and L<IO::File> modules,
+and git's L<Git> helper module.
+
+C<Git::SVN::Editor> has not been tested using callers other than
+B<git-svn> itself.
+
+=head1 SEE ALSO
+
+L<SVN::Delta>,
+L<Git::SVN::Fetcher>.
+
+=head1 INCOMPATIBILITIES
+
+None reported.
+
+=head1 BUGS
+
+None.
diff --git a/perl/Git/SVN/Fetcher.pm b/perl/Git/SVN/Fetcher.pm
new file mode 100644
index 0000000..ef8e9ed
--- /dev/null
+++ b/perl/Git/SVN/Fetcher.pm
@@ -0,0 +1,603 @@
+package Git::SVN::Fetcher;
+use vars qw/@ISA $_ignore_regex $_preserve_empty_dirs $_placeholder_filename
+            @deleted_gpath %added_placeholder $repo_id/;
+use strict;
+use warnings;
+use SVN::Delta;
+use Carp qw/croak/;
+use File::Basename qw/dirname/;
+use IO::File qw//;
+use Git qw/command command_oneline command_noisy command_output_pipe
+           command_input_pipe command_close_pipe
+           command_bidi_pipe command_close_bidi_pipe/;
+BEGIN {
+	@ISA = qw(SVN::Delta::Editor);
+}
+
+# file baton members: path, mode_a, mode_b, pool, fh, blob, base
+sub new {
+	my ($class, $git_svn, $switch_path) = @_;
+	my $self = SVN::Delta::Editor->new;
+	bless $self, $class;
+	if (exists $git_svn->{last_commit}) {
+		$self->{c} = $git_svn->{last_commit};
+		$self->{empty_symlinks} =
+		                  _mark_empty_symlinks($git_svn, $switch_path);
+	}
+
+	# some options are read globally, but can be overridden locally
+	# per [svn-remote "..."] section.  Command-line options will *NOT*
+	# override options set in an [svn-remote "..."] section
+	$repo_id = $git_svn->{repo_id};
+	my $k = "svn-remote.$repo_id.ignore-paths";
+	my $v = eval { command_oneline('config', '--get', $k) };
+	$self->{ignore_regex} = $v;
+
+	$k = "svn-remote.$repo_id.preserve-empty-dirs";
+	$v = eval { command_oneline('config', '--get', '--bool', $k) };
+	if ($v && $v eq 'true') {
+		$_preserve_empty_dirs = 1;
+		$k = "svn-remote.$repo_id.placeholder-filename";
+		$v = eval { command_oneline('config', '--get', $k) };
+		$_placeholder_filename = $v;
+	}
+
+	# Load the list of placeholder files added during previous invocations.
+	$k = "svn-remote.$repo_id.added-placeholder";
+	$v = eval { command_oneline('config', '--get-all', $k) };
+	if ($_preserve_empty_dirs && $v) {
+		# command() prints errors to stderr, so we only call it if
+		# command_oneline() succeeded.
+		my @v = command('config', '--get-all', $k);
+		$added_placeholder{ dirname($_) } = $_ foreach @v;
+	}
+
+	$self->{empty} = {};
+	$self->{dir_prop} = {};
+	$self->{file_prop} = {};
+	$self->{absent_dir} = {};
+	$self->{absent_file} = {};
+	$self->{gii} = $git_svn->tmp_index_do(sub { Git::IndexInfo->new });
+	$self->{pathnameencoding} = Git::config('svn.pathnameencoding');
+	$self;
+}
+
+# this uses the Ra object, so it must be called before do_{switch,update},
+# not inside them (when the Git::SVN::Fetcher object is passed) to
+# do_{switch,update}
+sub _mark_empty_symlinks {
+	my ($git_svn, $switch_path) = @_;
+	my $bool = Git::config_bool('svn.brokenSymlinkWorkaround');
+	return {} if (!defined($bool)) || (defined($bool) && ! $bool);
+
+	my %ret;
+	my ($rev, $cmt) = $git_svn->last_rev_commit;
+	return {} unless ($rev && $cmt);
+
+	# allow the warning to be printed for each revision we fetch to
+	# ensure the user sees it.  The user can also disable the workaround
+	# on the repository even while git svn is running and the next
+	# revision fetched will skip this expensive function.
+	my $printed_warning;
+	chomp(my $empty_blob = `git hash-object -t blob --stdin < /dev/null`);
+	my ($ls, $ctx) = command_output_pipe(qw/ls-tree -r -z/, $cmt);
+	local $/ = "\0";
+	my $pfx = defined($switch_path) ? $switch_path : $git_svn->{path};
+	$pfx .= '/' if length($pfx);
+	while (<$ls>) {
+		chomp;
+		s/\A100644 blob $empty_blob\t//o or next;
+		unless ($printed_warning) {
+			print STDERR "Scanning for empty symlinks, ",
+			             "this may take a while if you have ",
+				     "many empty files\n",
+				     "You may disable this with `",
+				     "git config svn.brokenSymlinkWorkaround ",
+				     "false'.\n",
+				     "This may be done in a different ",
+				     "terminal without restarting ",
+				     "git svn\n";
+			$printed_warning = 1;
+		}
+		my $path = $_;
+		my (undef, $props) =
+		               $git_svn->ra->get_file($pfx.$path, $rev, undef);
+		if ($props->{'svn:special'}) {
+			$ret{$path} = 1;
+		}
+	}
+	command_close_pipe($ls, $ctx);
+	\%ret;
+}
+
+# returns true if a given path is inside a ".git" directory
+sub in_dot_git {
+	$_[0] =~ m{(?:^|/)\.git(?:/|$)};
+}
+
+# return value: 0 -- don't ignore, 1 -- ignore
+sub is_path_ignored {
+	my ($self, $path) = @_;
+	return 1 if in_dot_git($path);
+	return 1 if defined($self->{ignore_regex}) &&
+	            $path =~ m!$self->{ignore_regex}!;
+	return 0 unless defined($_ignore_regex);
+	return 1 if $path =~ m!$_ignore_regex!o;
+	return 0;
+}
+
+sub set_path_strip {
+	my ($self, $path) = @_;
+	$self->{path_strip} = qr/^\Q$path\E(\/|$)/ if length $path;
+}
+
+sub open_root {
+	{ path => '' };
+}
+
+sub open_directory {
+	my ($self, $path, $pb, $rev) = @_;
+	{ path => $path };
+}
+
+sub git_path {
+	my ($self, $path) = @_;
+	if (my $enc = $self->{pathnameencoding}) {
+		require Encode;
+		Encode::from_to($path, 'UTF-8', $enc);
+	}
+	if ($self->{path_strip}) {
+		$path =~ s!$self->{path_strip}!! or
+		  die "Failed to strip path '$path' ($self->{path_strip})\n";
+	}
+	$path;
+}
+
+sub delete_entry {
+	my ($self, $path, $rev, $pb) = @_;
+	return undef if $self->is_path_ignored($path);
+
+	my $gpath = $self->git_path($path);
+	return undef if ($gpath eq '');
+
+	# remove entire directories.
+	my ($tree) = (command('ls-tree', '-z', $self->{c}, "./$gpath")
+	                 =~ /\A040000 tree ([a-f\d]{40})\t\Q$gpath\E\0/);
+	if ($tree) {
+		my ($ls, $ctx) = command_output_pipe(qw/ls-tree
+		                                     -r --name-only -z/,
+				                     $tree);
+		local $/ = "\0";
+		while (<$ls>) {
+			chomp;
+			my $rmpath = "$gpath/$_";
+			$self->{gii}->remove($rmpath);
+			print "\tD\t$rmpath\n" unless $::_q;
+		}
+		print "\tD\t$gpath/\n" unless $::_q;
+		command_close_pipe($ls, $ctx);
+	} else {
+		$self->{gii}->remove($gpath);
+		print "\tD\t$gpath\n" unless $::_q;
+	}
+	# Don't add to @deleted_gpath if we're deleting a placeholder file.
+	push @deleted_gpath, $gpath unless $added_placeholder{dirname($path)};
+	$self->{empty}->{$path} = 0;
+	undef;
+}
+
+sub open_file {
+	my ($self, $path, $pb, $rev) = @_;
+	my ($mode, $blob);
+
+	goto out if $self->is_path_ignored($path);
+
+	my $gpath = $self->git_path($path);
+	($mode, $blob) = (command('ls-tree', '-z', $self->{c}, "./$gpath")
+	                     =~ /\A(\d{6}) blob ([a-f\d]{40})\t\Q$gpath\E\0/);
+	unless (defined $mode && defined $blob) {
+		die "$path was not found in commit $self->{c} (r$rev)\n";
+	}
+	if ($mode eq '100644' && $self->{empty_symlinks}->{$path}) {
+		$mode = '120000';
+	}
+out:
+	{ path => $path, mode_a => $mode, mode_b => $mode, blob => $blob,
+	  pool => SVN::Pool->new, action => 'M' };
+}
+
+sub add_file {
+	my ($self, $path, $pb, $cp_path, $cp_rev) = @_;
+	my $mode;
+
+	if (!$self->is_path_ignored($path)) {
+		my ($dir, $file) = ($path =~ m#^(.*?)/?([^/]+)$#);
+		delete $self->{empty}->{$dir};
+		$mode = '100644';
+
+		if ($added_placeholder{$dir}) {
+			# Remove our placeholder file, if we created one.
+			delete_entry($self, $added_placeholder{$dir})
+				unless $path eq $added_placeholder{$dir};
+			delete $added_placeholder{$dir}
+		}
+	}
+
+	{ path => $path, mode_a => $mode, mode_b => $mode,
+	  pool => SVN::Pool->new, action => 'A' };
+}
+
+sub add_directory {
+	my ($self, $path, $cp_path, $cp_rev) = @_;
+	goto out if $self->is_path_ignored($path);
+	my $gpath = $self->git_path($path);
+	if ($gpath eq '') {
+		my ($ls, $ctx) = command_output_pipe(qw/ls-tree
+		                                     -r --name-only -z/,
+				                     $self->{c});
+		local $/ = "\0";
+		while (<$ls>) {
+			chomp;
+			$self->{gii}->remove($_);
+			print "\tD\t$_\n" unless $::_q;
+			push @deleted_gpath, $gpath;
+		}
+		command_close_pipe($ls, $ctx);
+		$self->{empty}->{$path} = 0;
+	}
+	my ($dir, $file) = ($path =~ m#^(.*?)/?([^/]+)$#);
+	delete $self->{empty}->{$dir};
+	$self->{empty}->{$path} = 1;
+
+	if ($added_placeholder{$dir}) {
+		# Remove our placeholder file, if we created one.
+		delete_entry($self, $added_placeholder{$dir});
+		delete $added_placeholder{$dir}
+	}
+
+out:
+	{ path => $path };
+}
+
+sub change_dir_prop {
+	my ($self, $db, $prop, $value) = @_;
+	return undef if $self->is_path_ignored($db->{path});
+	$self->{dir_prop}->{$db->{path}} ||= {};
+	$self->{dir_prop}->{$db->{path}}->{$prop} = $value;
+	undef;
+}
+
+sub absent_directory {
+	my ($self, $path, $pb) = @_;
+	return undef if $self->is_path_ignored($path);
+	$self->{absent_dir}->{$pb->{path}} ||= [];
+	push @{$self->{absent_dir}->{$pb->{path}}}, $path;
+	undef;
+}
+
+sub absent_file {
+	my ($self, $path, $pb) = @_;
+	return undef if $self->is_path_ignored($path);
+	$self->{absent_file}->{$pb->{path}} ||= [];
+	push @{$self->{absent_file}->{$pb->{path}}}, $path;
+	undef;
+}
+
+sub change_file_prop {
+	my ($self, $fb, $prop, $value) = @_;
+	return undef if $self->is_path_ignored($fb->{path});
+	if ($prop eq 'svn:executable') {
+		if ($fb->{mode_b} != 120000) {
+			$fb->{mode_b} = defined $value ? 100755 : 100644;
+		}
+	} elsif ($prop eq 'svn:special') {
+		$fb->{mode_b} = defined $value ? 120000 : 100644;
+	} else {
+		$self->{file_prop}->{$fb->{path}} ||= {};
+		$self->{file_prop}->{$fb->{path}}->{$prop} = $value;
+	}
+	undef;
+}
+
+sub apply_textdelta {
+	my ($self, $fb, $exp) = @_;
+	return undef if $self->is_path_ignored($fb->{path});
+	my $fh = $::_repository->temp_acquire('svn_delta');
+	# $fh gets auto-closed() by SVN::TxDelta::apply(),
+	# (but $base does not,) so dup() it for reading in close_file
+	open my $dup, '<&', $fh or croak $!;
+	my $base = $::_repository->temp_acquire('git_blob');
+
+	if ($fb->{blob}) {
+		my ($base_is_link, $size);
+
+		if ($fb->{mode_a} eq '120000' &&
+		    ! $self->{empty_symlinks}->{$fb->{path}}) {
+			print $base 'link ' or die "print $!\n";
+			$base_is_link = 1;
+		}
+	retry:
+		$size = $::_repository->cat_blob($fb->{blob}, $base);
+		die "Failed to read object $fb->{blob}" if ($size < 0);
+
+		if (defined $exp) {
+			seek $base, 0, 0 or croak $!;
+			my $got = ::md5sum($base);
+			if ($got ne $exp) {
+				my $err = "Checksum mismatch: ".
+				       "$fb->{path} $fb->{blob}\n" .
+				       "expected: $exp\n" .
+				       "     got: $got\n";
+				if ($base_is_link) {
+					warn $err,
+					     "Retrying... (possibly ",
+					     "a bad symlink from SVN)\n";
+					$::_repository->temp_reset($base);
+					$base_is_link = 0;
+					goto retry;
+				}
+				die $err;
+			}
+		}
+	}
+	seek $base, 0, 0 or croak $!;
+	$fb->{fh} = $fh;
+	$fb->{base} = $base;
+	[ SVN::TxDelta::apply($base, $dup, undef, $fb->{path}, $fb->{pool}) ];
+}
+
+sub close_file {
+	my ($self, $fb, $exp) = @_;
+	return undef if $self->is_path_ignored($fb->{path});
+
+	my $hash;
+	my $path = $self->git_path($fb->{path});
+	if (my $fh = $fb->{fh}) {
+		if (defined $exp) {
+			seek($fh, 0, 0) or croak $!;
+			my $got = ::md5sum($fh);
+			if ($got ne $exp) {
+				die "Checksum mismatch: $path\n",
+				    "expected: $exp\n    got: $got\n";
+			}
+		}
+		if ($fb->{mode_b} == 120000) {
+			sysseek($fh, 0, 0) or croak $!;
+			my $rd = sysread($fh, my $buf, 5);
+
+			if (!defined $rd) {
+				croak "sysread: $!\n";
+			} elsif ($rd == 0) {
+				warn "$path has mode 120000",
+				     " but it points to nothing\n",
+				     "converting to an empty file with mode",
+				     " 100644\n";
+				$fb->{mode_b} = '100644';
+			} elsif ($buf ne 'link ') {
+				warn "$path has mode 120000",
+				     " but is not a link\n";
+			} else {
+				my $tmp_fh = $::_repository->temp_acquire(
+					'svn_hash');
+				my $res;
+				while ($res = sysread($fh, my $str, 1024)) {
+					my $out = syswrite($tmp_fh, $str, $res);
+					defined($out) && $out == $res
+						or croak("write ",
+							Git::temp_path($tmp_fh),
+							": $!\n");
+				}
+				defined $res or croak $!;
+
+				($fh, $tmp_fh) = ($tmp_fh, $fh);
+				Git::temp_release($tmp_fh, 1);
+			}
+		}
+
+		$hash = $::_repository->hash_and_insert_object(
+				Git::temp_path($fh));
+		$hash =~ /^[a-f\d]{40}$/ or die "not a sha1: $hash\n";
+
+		Git::temp_release($fb->{base}, 1);
+		Git::temp_release($fh, 1);
+	} else {
+		$hash = $fb->{blob} or die "no blob information\n";
+	}
+	$fb->{pool}->clear;
+	$self->{gii}->update($fb->{mode_b}, $hash, $path) or croak $!;
+	print "\t$fb->{action}\t$path\n" if $fb->{action} && ! $::_q;
+	undef;
+}
+
+sub abort_edit {
+	my $self = shift;
+	$self->{nr} = $self->{gii}->{nr};
+	delete $self->{gii};
+	$self->SUPER::abort_edit(@_);
+}
+
+sub close_edit {
+	my $self = shift;
+
+	if ($_preserve_empty_dirs) {
+		my @empty_dirs;
+
+		# Any entry flagged as empty that also has an associated
+		# dir_prop represents a newly created empty directory.
+		foreach my $i (keys %{$self->{empty}}) {
+			push @empty_dirs, $i if exists $self->{dir_prop}->{$i};
+		}
+
+		# Search for directories that have become empty due subsequent
+		# file deletes.
+		push @empty_dirs, $self->find_empty_directories();
+
+		# Finally, add a placeholder file to each empty directory.
+		$self->add_placeholder_file($_) foreach (@empty_dirs);
+
+		$self->stash_placeholder_list();
+	}
+
+	$self->{git_commit_ok} = 1;
+	$self->{nr} = $self->{gii}->{nr};
+	delete $self->{gii};
+	$self->SUPER::close_edit(@_);
+}
+
+sub find_empty_directories {
+	my ($self) = @_;
+	my @empty_dirs;
+	my %dirs = map { dirname($_) => 1 } @deleted_gpath;
+
+	foreach my $dir (sort keys %dirs) {
+		next if $dir eq ".";
+
+		# If there have been any additions to this directory, there is
+		# no reason to check if it is empty.
+		my $skip_added = 0;
+		foreach my $t (qw/dir_prop file_prop/) {
+			foreach my $path (keys %{ $self->{$t} }) {
+				if (exists $self->{$t}->{dirname($path)}) {
+					$skip_added = 1;
+					last;
+				}
+			}
+			last if $skip_added;
+		}
+		next if $skip_added;
+
+		# Use `git ls-tree` to get the filenames of this directory
+		# that existed prior to this particular commit.
+		my $ls = command('ls-tree', '-z', '--name-only',
+				 $self->{c}, "$dir/");
+		my %files = map { $_ => 1 } split(/\0/, $ls);
+
+		# Remove the filenames that were deleted during this commit.
+		delete $files{$_} foreach (@deleted_gpath);
+
+		# Report the directory if there are no filenames left.
+		push @empty_dirs, $dir unless (scalar %files);
+	}
+	@empty_dirs;
+}
+
+sub add_placeholder_file {
+	my ($self, $dir) = @_;
+	my $path = "$dir/$_placeholder_filename";
+	my $gpath = $self->git_path($path);
+
+	my $fh = $::_repository->temp_acquire($gpath);
+	my $hash = $::_repository->hash_and_insert_object(Git::temp_path($fh));
+	Git::temp_release($fh, 1);
+	$self->{gii}->update('100644', $hash, $gpath) or croak $!;
+
+	# The directory should no longer be considered empty.
+	delete $self->{empty}->{$dir} if exists $self->{empty}->{$dir};
+
+	# Keep track of any placeholder files we create.
+	$added_placeholder{$dir} = $path;
+}
+
+sub stash_placeholder_list {
+	my ($self) = @_;
+	my $k = "svn-remote.$repo_id.added-placeholder";
+	my $v = eval { command_oneline('config', '--get-all', $k) };
+	command_noisy('config', '--unset-all', $k) if $v;
+	foreach (values %added_placeholder) {
+		command_noisy('config', '--add', $k, $_);
+	}
+}
+
+1;
+__END__
+
+Git::SVN::Fetcher - tree delta consumer for "git svn fetch"
+
+=head1 SYNOPSIS
+
+    use SVN::Core;
+    use SVN::Ra;
+    use Git::SVN;
+    use Git::SVN::Fetcher;
+    use Git;
+
+    my $gs = Git::SVN->find_by_url($url);
+    my $ra = SVN::Ra->new(url => $url);
+    my $editor = Git::SVN::Fetcher->new($gs);
+    my $reporter = $ra->do_update($SVN::Core::INVALID_REVNUM, '',
+                                  1, $editor);
+    $reporter->set_path('', $old_rev, 0);
+    $reporter->finish_report;
+    my $tree = $gs->tmp_index_do(sub { command_oneline('write-tree') });
+
+    foreach my $path (keys %{$editor->{dir_prop}) {
+        my $props = $editor->{dir_prop}{$path};
+        foreach my $prop (keys %$props) {
+            print "property $prop at $path changed to $props->{$prop}\n";
+        }
+    }
+    foreach my $path (keys %{$editor->{empty}) {
+        my $action = $editor->{empty}{$path} ? 'added' : 'removed';
+        print "empty directory $path $action\n";
+    }
+    foreach my $path (keys %{$editor->{file_prop}) { ... }
+    foreach my $parent (keys %{$editor->{absent_dir}}) {
+        my @children = @{$editor->{abstent_dir}{$parent}};
+        print "cannot fetch directory $parent/$_: not authorized?\n"
+            foreach @children;
+    }
+    foreach my $parent (keys %{$editor->{absent_file}) { ... }
+
+=head1 DESCRIPTION
+
+This is a subclass of C<SVN::Delta::Editor>, which means it implements
+callbacks to act as a consumer of Subversion tree deltas.  This
+particular implementation of those callbacks is meant to store
+information about the resulting content which B<git svn fetch> could
+use to populate new commits and new entries for F<unhandled.log>.
+More specifically:
+
+=over
+
+=item * Additions, removals, and modifications of files are propagated
+to git-svn's index file F<$GIT_DIR/svn/$refname/index> using
+B<git update-index>.
+
+=item * Changes in Subversion path properties are recorded in the
+C<dir_prop> and C<file_prop> fields (which are hashes).
+
+=item * Addition and removal of empty directories are indicated by
+entries with value 1 and 0 respectively in the C<empty> hash.
+
+=item * Paths that are present but cannot be conveyed (presumably due
+to permissions) are recorded in the C<absent_file> and
+C<absent_dirs> hashes.  For each key, the corresponding value is
+a list of paths under that directory that were present but
+could not be conveyed.
+
+=back
+
+The interface is unstable.  Do not use this module unless you are
+developing git-svn.
+
+=head1 DEPENDENCIES
+
+L<SVN::Delta> from the Subversion perl bindings,
+the core L<Carp>, L<File::Basename>, and L<IO::File> modules,
+and git's L<Git> helper module.
+
+C<Git::SVN::Fetcher> has not been tested using callers other than
+B<git-svn> itself.
+
+=head1 SEE ALSO
+
+L<SVN::Delta>,
+L<Git::SVN::Editor>.
+
+=head1 INCOMPATIBILITIES
+
+None reported.
+
+=head1 BUGS
+
+None.
diff --git a/perl/Git/SVN/Memoize/YAML.pm b/perl/Git/SVN/Memoize/YAML.pm
new file mode 100644
index 0000000..9676b8f
--- /dev/null
+++ b/perl/Git/SVN/Memoize/YAML.pm
@@ -0,0 +1,93 @@
+package Git::SVN::Memoize::YAML;
+use warnings;
+use strict;
+use YAML::Any ();
+
+# based on Memoize::Storable.
+
+sub TIEHASH {
+	my $package = shift;
+	my $filename = shift;
+	my $truehash = (-e $filename) ? YAML::Any::LoadFile($filename) : {};
+	my $self = {FILENAME => $filename, H => $truehash};
+	bless $self => $package;
+}
+
+sub STORE {
+	my $self = shift;
+	$self->{H}{$_[0]} = $_[1];
+}
+
+sub FETCH {
+	my $self = shift;
+	$self->{H}{$_[0]};
+}
+
+sub EXISTS {
+	my $self = shift;
+	exists $self->{H}{$_[0]};
+}
+
+sub DESTROY {
+	my $self = shift;
+	YAML::Any::DumpFile($self->{FILENAME}, $self->{H});
+}
+
+sub SCALAR {
+	my $self = shift;
+	scalar(%{$self->{H}});
+}
+
+sub FIRSTKEY {
+	'Fake hash from Git::SVN::Memoize::YAML';
+}
+
+sub NEXTKEY {
+	undef;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Git::SVN::Memoize::YAML - store Memoized data in YAML format
+
+=head1 SYNOPSIS
+
+    use Memoize;
+    use Git::SVN::Memoize::YAML;
+
+    tie my %cache => 'Git::SVN::Memoize::YAML', $filename;
+    memoize('slow_function', SCALAR_CACHE => [HASH => \%cache]);
+    slow_function(arguments);
+
+=head1 DESCRIPTION
+
+This module provides a class that can be used to tie a hash to a
+YAML file.  The file is read when the hash is initialized and
+rewritten when the hash is destroyed.
+
+The intent is to allow L<Memoize> to back its cache with a file in
+YAML format, just like L<Memoize::Storable> allows L<Memoize> to
+back its cache with a file in Storable format.  Unlike the Storable
+format, the YAML format is platform-independent and fairly stable.
+
+Carps on error.
+
+=head1 DIAGNOSTICS
+
+See L<YAML::Any>.
+
+=head1 DEPENDENCIES
+
+L<YAML::Any> from CPAN.
+
+=head1 INCOMPATIBILITIES
+
+None reported.
+
+=head1 BUGS
+
+The entire cache is read into a Perl hash when loading the file,
+so this is not very scalable.
diff --git a/perl/Git/SVN/Prompt.pm b/perl/Git/SVN/Prompt.pm
new file mode 100644
index 0000000..3a6f8af
--- /dev/null
+++ b/perl/Git/SVN/Prompt.pm
@@ -0,0 +1,202 @@
+package Git::SVN::Prompt;
+use strict;
+use warnings;
+require SVN::Core;
+use vars qw/$_no_auth_cache $_username/;
+
+sub simple {
+	my ($cred, $realm, $default_username, $may_save, $pool) = @_;
+	$may_save = undef if $_no_auth_cache;
+	$default_username = $_username if defined $_username;
+	if (defined $default_username && length $default_username) {
+		if (defined $realm && length $realm) {
+			print STDERR "Authentication realm: $realm\n";
+			STDERR->flush;
+		}
+		$cred->username($default_username);
+	} else {
+		username($cred, $realm, $may_save, $pool);
+	}
+	$cred->password(_read_password("Password for '" .
+	                               $cred->username . "': ", $realm));
+	$cred->may_save($may_save);
+	$SVN::_Core::SVN_NO_ERROR;
+}
+
+sub ssl_server_trust {
+	my ($cred, $realm, $failures, $cert_info, $may_save, $pool) = @_;
+	$may_save = undef if $_no_auth_cache;
+	print STDERR "Error validating server certificate for '$realm':\n";
+	{
+		no warnings 'once';
+		# All variables SVN::Auth::SSL::* are used only once,
+		# so we're shutting up Perl warnings about this.
+		if ($failures & $SVN::Auth::SSL::UNKNOWNCA) {
+			print STDERR " - The certificate is not issued ",
+			    "by a trusted authority. Use the\n",
+			    "   fingerprint to validate ",
+			    "the certificate manually!\n";
+		}
+		if ($failures & $SVN::Auth::SSL::CNMISMATCH) {
+			print STDERR " - The certificate hostname ",
+			    "does not match.\n";
+		}
+		if ($failures & $SVN::Auth::SSL::NOTYETVALID) {
+			print STDERR " - The certificate is not yet valid.\n";
+		}
+		if ($failures & $SVN::Auth::SSL::EXPIRED) {
+			print STDERR " - The certificate has expired.\n";
+		}
+		if ($failures & $SVN::Auth::SSL::OTHER) {
+			print STDERR " - The certificate has ",
+			    "an unknown error.\n";
+		}
+	} # no warnings 'once'
+	printf STDERR
+	        "Certificate information:\n".
+	        " - Hostname: %s\n".
+	        " - Valid: from %s until %s\n".
+	        " - Issuer: %s\n".
+	        " - Fingerprint: %s\n",
+	        map $cert_info->$_, qw(hostname valid_from valid_until
+	                               issuer_dname fingerprint);
+	my $choice;
+prompt:
+	print STDERR $may_save ?
+	      "(R)eject, accept (t)emporarily or accept (p)ermanently? " :
+	      "(R)eject or accept (t)emporarily? ";
+	STDERR->flush;
+	$choice = lc(substr(<STDIN> || 'R', 0, 1));
+	if ($choice =~ /^t$/i) {
+		$cred->may_save(undef);
+	} elsif ($choice =~ /^r$/i) {
+		return -1;
+	} elsif ($may_save && $choice =~ /^p$/i) {
+		$cred->may_save($may_save);
+	} else {
+		goto prompt;
+	}
+	$cred->accepted_failures($failures);
+	$SVN::_Core::SVN_NO_ERROR;
+}
+
+sub ssl_client_cert {
+	my ($cred, $realm, $may_save, $pool) = @_;
+	$may_save = undef if $_no_auth_cache;
+	print STDERR "Client certificate filename: ";
+	STDERR->flush;
+	chomp(my $filename = <STDIN>);
+	$cred->cert_file($filename);
+	$cred->may_save($may_save);
+	$SVN::_Core::SVN_NO_ERROR;
+}
+
+sub ssl_client_cert_pw {
+	my ($cred, $realm, $may_save, $pool) = @_;
+	$may_save = undef if $_no_auth_cache;
+	$cred->password(_read_password("Password: ", $realm));
+	$cred->may_save($may_save);
+	$SVN::_Core::SVN_NO_ERROR;
+}
+
+sub username {
+	my ($cred, $realm, $may_save, $pool) = @_;
+	$may_save = undef if $_no_auth_cache;
+	if (defined $realm && length $realm) {
+		print STDERR "Authentication realm: $realm\n";
+	}
+	my $username;
+	if (defined $_username) {
+		$username = $_username;
+	} else {
+		print STDERR "Username: ";
+		STDERR->flush;
+		chomp($username = <STDIN>);
+	}
+	$cred->username($username);
+	$cred->may_save($may_save);
+	$SVN::_Core::SVN_NO_ERROR;
+}
+
+sub _read_password {
+	my ($prompt, $realm) = @_;
+	my $password = '';
+	if (exists $ENV{GIT_ASKPASS}) {
+		open(PH, "-|", $ENV{GIT_ASKPASS}, $prompt);
+		$password = <PH>;
+		$password =~ s/[\012\015]//; # \n\r
+		close(PH);
+	} else {
+		print STDERR $prompt;
+		STDERR->flush;
+		require Term::ReadKey;
+		Term::ReadKey::ReadMode('noecho');
+		while (defined(my $key = Term::ReadKey::ReadKey(0))) {
+			last if $key =~ /[\012\015]/; # \n\r
+			$password .= $key;
+		}
+		Term::ReadKey::ReadMode('restore');
+		print STDERR "\n";
+		STDERR->flush;
+	}
+	$password;
+}
+
+1;
+__END__
+
+Git::SVN::Prompt - authentication callbacks for git-svn
+
+=head1 SYNOPSIS
+
+    use Git::SVN::Prompt qw(simple ssl_client_cert ssl_client_cert_pw
+                            ssl_server_trust username);
+    use SVN::Client ();
+
+    my $cached_simple = SVN::Client::get_simple_provider();
+    my $git_simple = SVN::Client::get_simple_prompt_provider(\&simple, 2);
+    my $cached_ssl = SVN::Client::get_ssl_server_trust_file_provider();
+    my $git_ssl = SVN::Client::get_ssl_server_trust_prompt_provider(
+        \&ssl_server_trust);
+    my $cached_cert = SVN::Client::get_ssl_client_cert_file_provider();
+    my $git_cert = SVN::Client::get_ssl_client_cert_prompt_provider(
+        \&ssl_client_cert, 2);
+    my $cached_cert_pw = SVN::Client::get_ssl_client_cert_pw_file_provider();
+    my $git_cert_pw = SVN::Client::get_ssl_client_cert_pw_prompt_provider(
+        \&ssl_client_cert_pw, 2);
+    my $cached_username = SVN::Client::get_username_provider();
+    my $git_username = SVN::Client::get_username_prompt_provider(
+        \&username, 2);
+
+    my $ctx = new SVN::Client(
+        auth => [
+            $cached_simple, $git_simple,
+            $cached_ssl, $git_ssl,
+            $cached_cert, $git_cert,
+            $cached_cert_pw, $git_cert_pw,
+            $cached_username, $git_username
+        ]);
+
+=head1 DESCRIPTION
+
+This module is an implementation detail of the "git svn" command.
+It implements git-svn's authentication policy.  Do not use it unless
+you are developing git-svn.
+
+The interface will change as git-svn evolves.
+
+=head1 DEPENDENCIES
+
+L<SVN::Core>.
+
+=head1 SEE ALSO
+
+L<SVN::Client>.
+
+=head1 INCOMPATIBILITIES
+
+None reported.
+
+=head1 BUGS
+
+None.
diff --git a/perl/Git/SVN/Ra.pm b/perl/Git/SVN/Ra.pm
new file mode 100644
index 0000000..23ff43e
--- /dev/null
+++ b/perl/Git/SVN/Ra.pm
@@ -0,0 +1,658 @@
+package Git::SVN::Ra;
+use vars qw/@ISA $config_dir $_ignore_refs_regex $_log_window_size/;
+use strict;
+use warnings;
+use SVN::Client;
+use SVN::Ra;
+BEGIN {
+	@ISA = qw(SVN::Ra);
+}
+
+my ($ra_invalid, $can_do_switch, %ignored_err, $RA);
+
+BEGIN {
+	# enforce temporary pool usage for some simple functions
+	no strict 'refs';
+	for my $f (qw/rev_proplist get_latest_revnum get_uuid get_repos_root
+	              get_file/) {
+		my $SUPER = "SUPER::$f";
+		*$f = sub {
+			my $self = shift;
+			my $pool = SVN::Pool->new;
+			my @ret = $self->$SUPER(@_,$pool);
+			$pool->clear;
+			wantarray ? @ret : $ret[0];
+		};
+	}
+}
+
+sub _auth_providers () {
+	my @rv = (
+	  SVN::Client::get_simple_provider(),
+	  SVN::Client::get_ssl_server_trust_file_provider(),
+	  SVN::Client::get_simple_prompt_provider(
+	    \&Git::SVN::Prompt::simple, 2),
+	  SVN::Client::get_ssl_client_cert_file_provider(),
+	  SVN::Client::get_ssl_client_cert_prompt_provider(
+	    \&Git::SVN::Prompt::ssl_client_cert, 2),
+	  SVN::Client::get_ssl_client_cert_pw_file_provider(),
+	  SVN::Client::get_ssl_client_cert_pw_prompt_provider(
+	    \&Git::SVN::Prompt::ssl_client_cert_pw, 2),
+	  SVN::Client::get_username_provider(),
+	  SVN::Client::get_ssl_server_trust_prompt_provider(
+	    \&Git::SVN::Prompt::ssl_server_trust),
+	  SVN::Client::get_username_prompt_provider(
+	    \&Git::SVN::Prompt::username, 2)
+	);
+
+	# earlier 1.6.x versions would segfault, and <= 1.5.x didn't have
+	# this function
+	if (::compare_svn_version('1.6.15') >= 0) {
+		my $config = SVN::Core::config_get_config($config_dir);
+		my ($p, @a);
+		# config_get_config returns all config files from
+		# ~/.subversion, auth_get_platform_specific_client_providers
+		# just wants the config "file".
+		@a = ($config->{'config'}, undef);
+		$p = SVN::Core::auth_get_platform_specific_client_providers(@a);
+		# Insert the return value from
+		# auth_get_platform_specific_providers
+		unshift @rv, @$p;
+	}
+	\@rv;
+}
+
+sub escape_uri_only {
+	my ($uri) = @_;
+	my @tmp;
+	foreach (split m{/}, $uri) {
+		s/([^~\w.%+-]|%(?![a-fA-F0-9]{2}))/sprintf("%%%02X",ord($1))/eg;
+		push @tmp, $_;
+	}
+	join('/', @tmp);
+}
+
+sub escape_url {
+	my ($url) = @_;
+	if ($url =~ m#^(https?)://([^/]+)(.*)$#) {
+		my ($scheme, $domain, $uri) = ($1, $2, escape_uri_only($3));
+		$url = "$scheme://$domain$uri";
+	}
+	$url;
+}
+
+sub new {
+	my ($class, $url) = @_;
+	$url =~ s!/+$!!;
+	return $RA if ($RA && $RA->{url} eq $url);
+
+	::_req_svn();
+
+	SVN::_Core::svn_config_ensure($config_dir, undef);
+	my ($baton, $callbacks) = SVN::Core::auth_open_helper(_auth_providers);
+	my $config = SVN::Core::config_get_config($config_dir);
+	$RA = undef;
+	my $dont_store_passwords = 1;
+	my $conf_t = ${$config}{'config'};
+	{
+		no warnings 'once';
+		# The usage of $SVN::_Core::SVN_CONFIG_* variables
+		# produces warnings that variables are used only once.
+		# I had not found the better way to shut them up, so
+		# the warnings of type 'once' are disabled in this block.
+		if (SVN::_Core::svn_config_get_bool($conf_t,
+		    $SVN::_Core::SVN_CONFIG_SECTION_AUTH,
+		    $SVN::_Core::SVN_CONFIG_OPTION_STORE_PASSWORDS,
+		    1) == 0) {
+			SVN::_Core::svn_auth_set_parameter($baton,
+			    $SVN::_Core::SVN_AUTH_PARAM_DONT_STORE_PASSWORDS,
+			    bless (\$dont_store_passwords, "_p_void"));
+		}
+		if (SVN::_Core::svn_config_get_bool($conf_t,
+		    $SVN::_Core::SVN_CONFIG_SECTION_AUTH,
+		    $SVN::_Core::SVN_CONFIG_OPTION_STORE_AUTH_CREDS,
+		    1) == 0) {
+			$Git::SVN::Prompt::_no_auth_cache = 1;
+		}
+	} # no warnings 'once'
+	my $self = SVN::Ra->new(url => escape_url($url), auth => $baton,
+	                      config => $config,
+			      pool => SVN::Pool->new,
+	                      auth_provider_callbacks => $callbacks);
+	$self->{url} = $url;
+	$self->{svn_path} = $url;
+	$self->{repos_root} = $self->get_repos_root;
+	$self->{svn_path} =~ s#^\Q$self->{repos_root}\E(/|$)##;
+	$self->{cache} = { check_path => { r => 0, data => {} },
+	                   get_dir => { r => 0, data => {} } };
+	$RA = bless $self, $class;
+}
+
+sub check_path {
+	my ($self, $path, $r) = @_;
+	my $cache = $self->{cache}->{check_path};
+	if ($r == $cache->{r} && exists $cache->{data}->{$path}) {
+		return $cache->{data}->{$path};
+	}
+	my $pool = SVN::Pool->new;
+	my $t = $self->SUPER::check_path($path, $r, $pool);
+	$pool->clear;
+	if ($r != $cache->{r}) {
+		%{$cache->{data}} = ();
+		$cache->{r} = $r;
+	}
+	$cache->{data}->{$path} = $t;
+}
+
+sub get_dir {
+	my ($self, $dir, $r) = @_;
+	my $cache = $self->{cache}->{get_dir};
+	if ($r == $cache->{r}) {
+		if (my $x = $cache->{data}->{$dir}) {
+			return wantarray ? @$x : $x->[0];
+		}
+	}
+	my $pool = SVN::Pool->new;
+	my ($d, undef, $props) = $self->SUPER::get_dir($dir, $r, $pool);
+	my %dirents = map { $_ => { kind => $d->{$_}->kind } } keys %$d;
+	$pool->clear;
+	if ($r != $cache->{r}) {
+		%{$cache->{data}} = ();
+		$cache->{r} = $r;
+	}
+	$cache->{data}->{$dir} = [ \%dirents, $r, $props ];
+	wantarray ? (\%dirents, $r, $props) : \%dirents;
+}
+
+sub DESTROY {
+	# do not call the real DESTROY since we store ourselves in $RA
+}
+
+# get_log(paths, start, end, limit,
+#         discover_changed_paths, strict_node_history, receiver)
+sub get_log {
+	my ($self, @args) = @_;
+	my $pool = SVN::Pool->new;
+
+	# svn_log_changed_path_t objects passed to get_log are likely to be
+	# overwritten even if only the refs are copied to an external variable,
+	# so we should dup the structures in their entirety.  Using an
+	# externally passed pool (instead of our temporary and quickly cleared
+	# pool in Git::SVN::Ra) does not help matters at all...
+	my $receiver = pop @args;
+	my $prefix = "/".$self->{svn_path};
+	$prefix =~ s#/+($)##;
+	my $prefix_regex = qr#^\Q$prefix\E#;
+	push(@args, sub {
+		my ($paths) = $_[0];
+		return &$receiver(@_) unless $paths;
+		$_[0] = ();
+		foreach my $p (keys %$paths) {
+			my $i = $paths->{$p};
+			# Make path relative to our url, not repos_root
+			$p =~ s/$prefix_regex//;
+			my %s = map { $_ => $i->$_; }
+				qw/copyfrom_path copyfrom_rev action/;
+			if ($s{'copyfrom_path'}) {
+				$s{'copyfrom_path'} =~ s/$prefix_regex//;
+			}
+			$_[0]{$p} = \%s;
+		}
+		&$receiver(@_);
+	});
+
+
+	# the limit parameter was not supported in SVN 1.1.x, so we
+	# drop it.  Therefore, the receiver callback passed to it
+	# is made aware of this limitation by being wrapped if
+	# the limit passed to is being wrapped.
+	if (::compare_svn_version('1.2.0') <= 0) {
+		my $limit = splice(@args, 3, 1);
+		if ($limit > 0) {
+			my $receiver = pop @args;
+			push(@args, sub { &$receiver(@_) if (--$limit >= 0) });
+		}
+	}
+	my $ret = $self->SUPER::get_log(@args, $pool);
+	$pool->clear;
+	$ret;
+}
+
+sub trees_match {
+	my ($self, $url1, $rev1, $url2, $rev2) = @_;
+	my $ctx = SVN::Client->new(auth => _auth_providers);
+	my $out = IO::File->new_tmpfile;
+
+	# older SVN (1.1.x) doesn't take $pool as the last parameter for
+	# $ctx->diff(), so we'll create a default one
+	my $pool = SVN::Pool->new_default_sub;
+
+	$ra_invalid = 1; # this will open a new SVN::Ra connection to $url1
+	$ctx->diff([], $url1, $rev1, $url2, $rev2, 1, 1, 0, $out, $out);
+	$out->flush;
+	my $ret = (($out->stat)[7] == 0);
+	close $out or croak $!;
+
+	$ret;
+}
+
+sub get_commit_editor {
+	my ($self, $log, $cb, $pool) = @_;
+
+	my @lock = (::compare_svn_version('1.2.0') >= 0) ? (undef, 0) : ();
+	$self->SUPER::get_commit_editor($log, $cb, @lock, $pool);
+}
+
+sub gs_do_update {
+	my ($self, $rev_a, $rev_b, $gs, $editor) = @_;
+	my $new = ($rev_a == $rev_b);
+	my $path = $gs->{path};
+
+	if ($new && -e $gs->{index}) {
+		unlink $gs->{index} or die
+		  "Couldn't unlink index: $gs->{index}: $!\n";
+	}
+	my $pool = SVN::Pool->new;
+	$editor->set_path_strip($path);
+	my (@pc) = split m#/#, $path;
+	my $reporter = $self->do_update($rev_b, (@pc ? shift @pc : ''),
+	                                1, $editor, $pool);
+	my @lock = (::compare_svn_version('1.2.0') >= 0) ? (undef) : ();
+
+	# Since we can't rely on svn_ra_reparent being available, we'll
+	# just have to do some magic with set_path to make it so
+	# we only want a partial path.
+	my $sp = '';
+	my $final = join('/', @pc);
+	while (@pc) {
+		$reporter->set_path($sp, $rev_b, 0, @lock, $pool);
+		$sp .= '/' if length $sp;
+		$sp .= shift @pc;
+	}
+	die "BUG: '$sp' != '$final'\n" if ($sp ne $final);
+
+	$reporter->set_path($sp, $rev_a, $new, @lock, $pool);
+
+	$reporter->finish_report($pool);
+	$pool->clear;
+	$editor->{git_commit_ok};
+}
+
+# this requires SVN 1.4.3 or later (do_switch didn't work before 1.4.3, and
+# svn_ra_reparent didn't work before 1.4)
+sub gs_do_switch {
+	my ($self, $rev_a, $rev_b, $gs, $url_b, $editor) = @_;
+	my $path = $gs->{path};
+	my $pool = SVN::Pool->new;
+
+	my $full_url = $self->{url};
+	my $old_url = $full_url;
+	$full_url .= '/' . $path if length $path;
+	my ($ra, $reparented);
+
+	if ($old_url =~ m#^svn(\+ssh)?://# ||
+	    ($full_url =~ m#^https?://# &&
+	     escape_url($full_url) ne $full_url)) {
+		$_[0] = undef;
+		$self = undef;
+		$RA = undef;
+		$ra = Git::SVN::Ra->new($full_url);
+		$ra_invalid = 1;
+	} elsif ($old_url ne $full_url) {
+		SVN::_Ra::svn_ra_reparent($self->{session}, $full_url, $pool);
+		$self->{url} = $full_url;
+		$reparented = 1;
+	}
+
+	$ra ||= $self;
+	$url_b = escape_url($url_b);
+	my $reporter = $ra->do_switch($rev_b, '', 1, $url_b, $editor, $pool);
+	my @lock = (::compare_svn_version('1.2.0') >= 0) ? (undef) : ();
+	$reporter->set_path('', $rev_a, 0, @lock, $pool);
+	$reporter->finish_report($pool);
+
+	if ($reparented) {
+		SVN::_Ra::svn_ra_reparent($self->{session}, $old_url, $pool);
+		$self->{url} = $old_url;
+	}
+
+	$pool->clear;
+	$editor->{git_commit_ok};
+}
+
+sub longest_common_path {
+	my ($gsv, $globs) = @_;
+	my %common;
+	my $common_max = scalar @$gsv;
+
+	foreach my $gs (@$gsv) {
+		my @tmp = split m#/#, $gs->{path};
+		my $p = '';
+		foreach (@tmp) {
+			$p .= length($p) ? "/$_" : $_;
+			$common{$p} ||= 0;
+			$common{$p}++;
+		}
+	}
+	$globs ||= [];
+	$common_max += scalar @$globs;
+	foreach my $glob (@$globs) {
+		my @tmp = split m#/#, $glob->{path}->{left};
+		my $p = '';
+		foreach (@tmp) {
+			$p .= length($p) ? "/$_" : $_;
+			$common{$p} ||= 0;
+			$common{$p}++;
+		}
+	}
+
+	my $longest_path = '';
+	foreach (sort {length $b <=> length $a} keys %common) {
+		if ($common{$_} == $common_max) {
+			$longest_path = $_;
+			last;
+		}
+	}
+	$longest_path;
+}
+
+sub gs_fetch_loop_common {
+	my ($self, $base, $head, $gsv, $globs) = @_;
+	return if ($base > $head);
+	my $inc = $_log_window_size;
+	my ($min, $max) = ($base, $head < $base + $inc ? $head : $base + $inc);
+	my $longest_path = longest_common_path($gsv, $globs);
+	my $ra_url = $self->{url};
+	my $find_trailing_edge;
+	while (1) {
+		my %revs;
+		my $err;
+		my $err_handler = $SVN::Error::handler;
+		$SVN::Error::handler = sub {
+			($err) = @_;
+			skip_unknown_revs($err);
+		};
+		sub _cb {
+			my ($paths, $r, $author, $date, $log) = @_;
+			[ $paths,
+			  { author => $author, date => $date, log => $log } ];
+		}
+		$self->get_log([$longest_path], $min, $max, 0, 1, 1,
+		               sub { $revs{$_[1]} = _cb(@_) });
+		if ($err) {
+			print "Checked through r$max\r";
+		} else {
+			$find_trailing_edge = 1;
+		}
+		if ($err and $find_trailing_edge) {
+			print STDERR "Path '$longest_path' ",
+				     "was probably deleted:\n",
+				     $err->expanded_message,
+				     "\nWill attempt to follow ",
+				     "revisions r$min .. r$max ",
+				     "committed before the deletion\n";
+			my $hi = $max;
+			while (--$hi >= $min) {
+				my $ok;
+				$self->get_log([$longest_path], $min, $hi,
+				               0, 1, 1, sub {
+				               $ok = $_[1];
+				               $revs{$_[1]} = _cb(@_) });
+				if ($ok) {
+					print STDERR "r$min .. r$ok OK\n";
+					last;
+				}
+			}
+			$find_trailing_edge = 0;
+		}
+		$SVN::Error::handler = $err_handler;
+
+		my %exists = map { $_->{path} => $_ } @$gsv;
+		foreach my $r (sort {$a <=> $b} keys %revs) {
+			my ($paths, $logged) = @{$revs{$r}};
+
+			foreach my $gs ($self->match_globs(\%exists, $paths,
+			                                   $globs, $r)) {
+				if ($gs->rev_map_max >= $r) {
+					next;
+				}
+				next unless $gs->match_paths($paths, $r);
+				$gs->{logged_rev_props} = $logged;
+				if (my $last_commit = $gs->last_commit) {
+					$gs->assert_index_clean($last_commit);
+				}
+				my $log_entry = $gs->do_fetch($paths, $r);
+				if ($log_entry) {
+					$gs->do_git_commit($log_entry);
+				}
+				$Git::SVN::INDEX_FILES{$gs->{index}} = 1;
+			}
+			foreach my $g (@$globs) {
+				my $k = "svn-remote.$g->{remote}." .
+				        "$g->{t}-maxRev";
+				Git::SVN::tmp_config($k, $r);
+			}
+			if ($ra_invalid) {
+				$_[0] = undef;
+				$self = undef;
+				$RA = undef;
+				$self = Git::SVN::Ra->new($ra_url);
+				$ra_invalid = undef;
+			}
+		}
+		# pre-fill the .rev_db since it'll eventually get filled in
+		# with '0' x40 if something new gets committed
+		foreach my $gs (@$gsv) {
+			next if $gs->rev_map_max >= $max;
+			next if defined $gs->rev_map_get($max);
+			$gs->rev_map_set($max, 0 x40);
+		}
+		foreach my $g (@$globs) {
+			my $k = "svn-remote.$g->{remote}.$g->{t}-maxRev";
+			Git::SVN::tmp_config($k, $max);
+		}
+		last if $max >= $head;
+		$min = $max + 1;
+		$max += $inc;
+		$max = $head if ($max > $head);
+	}
+	Git::SVN::gc();
+}
+
+sub get_dir_globbed {
+	my ($self, $left, $depth, $r) = @_;
+
+	my @x = eval { $self->get_dir($left, $r) };
+	return unless scalar @x == 3;
+	my $dirents = $x[0];
+	my @finalents;
+	foreach my $de (keys %$dirents) {
+		next if $dirents->{$de}->{kind} != $SVN::Node::dir;
+		if ($depth > 1) {
+			my @args = ("$left/$de", $depth - 1, $r);
+			foreach my $dir ($self->get_dir_globbed(@args)) {
+				push @finalents, "$de/$dir";
+			}
+		} else {
+			push @finalents, $de;
+		}
+	}
+	@finalents;
+}
+
+# return value: 0 -- don't ignore, 1 -- ignore
+sub is_ref_ignored {
+	my ($g, $p) = @_;
+	my $refname = $g->{ref}->full_path($p);
+	return 1 if defined($g->{ignore_refs_regex}) &&
+	            $refname =~ m!$g->{ignore_refs_regex}!;
+	return 0 unless defined($_ignore_refs_regex);
+	return 1 if $refname =~ m!$_ignore_refs_regex!o;
+	return 0;
+}
+
+sub match_globs {
+	my ($self, $exists, $paths, $globs, $r) = @_;
+
+	sub get_dir_check {
+		my ($self, $exists, $g, $r) = @_;
+
+		my @dirs = $self->get_dir_globbed($g->{path}->{left},
+		                                  $g->{path}->{depth},
+		                                  $r);
+
+		foreach my $de (@dirs) {
+			my $p = $g->{path}->full_path($de);
+			next if $exists->{$p};
+			next if (length $g->{path}->{right} &&
+				 ($self->check_path($p, $r) !=
+				  $SVN::Node::dir));
+			next unless $p =~ /$g->{path}->{regex}/;
+			$exists->{$p} = Git::SVN->init($self->{url}, $p, undef,
+					 $g->{ref}->full_path($de), 1);
+		}
+	}
+	foreach my $g (@$globs) {
+		if (my $path = $paths->{"/$g->{path}->{left}"}) {
+			if ($path->{action} =~ /^[AR]$/) {
+				get_dir_check($self, $exists, $g, $r);
+			}
+		}
+		foreach (keys %$paths) {
+			if (/$g->{path}->{left_regex}/ &&
+			    !/$g->{path}->{regex}/) {
+				next if $paths->{$_}->{action} !~ /^[AR]$/;
+				get_dir_check($self, $exists, $g, $r);
+			}
+			next unless /$g->{path}->{regex}/;
+			my $p = $1;
+			my $pathname = $g->{path}->full_path($p);
+			next if is_ref_ignored($g, $p);
+			next if $exists->{$pathname};
+			next if ($self->check_path($pathname, $r) !=
+			         $SVN::Node::dir);
+			$exists->{$pathname} = Git::SVN->init(
+			                      $self->{url}, $pathname, undef,
+			                      $g->{ref}->full_path($p), 1);
+		}
+		my $c = '';
+		foreach (split m#/#, $g->{path}->{left}) {
+			$c .= "/$_";
+			next unless ($paths->{$c} &&
+			             ($paths->{$c}->{action} =~ /^[AR]$/));
+			get_dir_check($self, $exists, $g, $r);
+		}
+	}
+	values %$exists;
+}
+
+sub minimize_url {
+	my ($self) = @_;
+	return $self->{url} if ($self->{url} eq $self->{repos_root});
+	my $url = $self->{repos_root};
+	my @components = split(m!/!, $self->{svn_path});
+	my $c = '';
+	do {
+		$url .= "/$c" if length $c;
+		eval {
+			my $ra = (ref $self)->new($url);
+			my $latest = $ra->get_latest_revnum;
+			$ra->get_log("", $latest, 0, 1, 0, 1, sub {});
+		};
+	} while ($@ && ($c = shift @components));
+	$url;
+}
+
+sub can_do_switch {
+	my $self = shift;
+	unless (defined $can_do_switch) {
+		my $pool = SVN::Pool->new;
+		my $rep = eval {
+			$self->do_switch(1, '', 0, $self->{url},
+			                 SVN::Delta::Editor->new, $pool);
+		};
+		if ($@) {
+			$can_do_switch = 0;
+		} else {
+			$rep->abort_report($pool);
+			$can_do_switch = 1;
+		}
+		$pool->clear;
+	}
+	$can_do_switch;
+}
+
+sub skip_unknown_revs {
+	my ($err) = @_;
+	my $errno = $err->apr_err();
+	# Maybe the branch we're tracking didn't
+	# exist when the repo started, so it's
+	# not an error if it doesn't, just continue
+	#
+	# Wonderfully consistent library, eh?
+	# 160013 - svn:// and file://
+	# 175002 - http(s)://
+	# 175007 - http(s):// (this repo required authorization, too...)
+	#   More codes may be discovered later...
+	if ($errno == 175007 || $errno == 175002 || $errno == 160013) {
+		my $err_key = $err->expanded_message;
+		# revision numbers change every time, filter them out
+		$err_key =~ s/\d+/\0/g;
+		$err_key = "$errno\0$err_key";
+		unless ($ignored_err{$err_key}) {
+			warn "W: Ignoring error from SVN, path probably ",
+			     "does not exist: ($errno): ",
+			     $err->expanded_message,"\n";
+			warn "W: Do not be alarmed at the above message ",
+			     "git-svn is just searching aggressively for ",
+			     "old history.\n",
+			     "This may take a while on large repositories\n";
+			$ignored_err{$err_key} = 1;
+		}
+		return;
+	}
+	die "Error from SVN, ($errno): ", $err->expanded_message,"\n";
+}
+
+1;
+__END__
+
+Git::SVN::Ra - Subversion remote access functions for git-svn
+
+=head1 SYNOPSIS
+
+    use Git::SVN::Ra;
+
+    my $ra = Git::SVN::Ra->new($branchurl);
+    my ($dirents, $fetched_revnum, $props) =
+        $ra->get_dir('.', $SVN::Core::INVALID_REVNUM);
+
+=head1 DESCRIPTION
+
+This is a wrapper around the L<SVN::Ra> module for use by B<git-svn>.
+It fills in some default parameters (such as the authentication
+scheme), smooths over incompatibilities between libsvn versions, adds
+caching, and implements some functions specific to B<git-svn>.
+
+Do not use it unless you are developing git-svn.  The interface will
+change as git-svn evolves.
+
+=head1 DEPENDENCIES
+
+Subversion perl bindings,
+L<Git::SVN>.
+
+C<Git::SVN::Ra> has not been tested using callers other than
+B<git-svn> itself.
+
+=head1 SEE ALSO
+
+L<SVN::Ra>.
+
+=head1 INCOMPATIBILITIES
+
+None reported.
+
+=head1 BUGS
+
+None.
diff --git a/perl/Makefile b/perl/Makefile
index e3dd1a5..6ca7d47 100644
--- a/perl/Makefile
+++ b/perl/Makefile
@@ -2,9 +2,11 @@
 # Makefile for perl support modules and routine
 #
 makfile:=perl.mak
+modules =
 
 PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
 prefix_SQ = $(subst ','\'',$(prefix))
+localedir_SQ = $(subst ','\'',$(localedir))
 
 ifndef V
 	QUIET = @
@@ -21,24 +23,55 @@
 
 ifdef NO_PERL_MAKEMAKER
 instdir_SQ = $(subst ','\'',$(prefix)/lib)
+
+modules += Git
+modules += Git/I18N
+modules += Git/SVN/Memoize/YAML
+modules += Git/SVN/Fetcher
+modules += Git/SVN/Editor
+modules += Git/SVN/Prompt
+modules += Git/SVN/Ra
+
 $(makfile): ../GIT-CFLAGS Makefile
-	echo all: private-Error.pm Git.pm > $@
-	echo '	mkdir -p blib/lib' >> $@
-	echo '	$(RM) blib/lib/Git.pm; cp Git.pm blib/lib/' >> $@
+	echo all: private-Error.pm Git.pm Git/I18N.pm > $@
+	set -e; \
+	for i in $(modules); \
+	do \
+		if test $$i = $${i%/*}; \
+		then \
+			subdir=; \
+		else \
+			subdir=/$${i%/*}; \
+		fi; \
+		echo '	$(RM) blib/lib/'$$i'.pm' >> $@; \
+		echo '	mkdir -p blib/lib'$$subdir >> $@; \
+		echo '	cp '$$i'.pm blib/lib/'$$i'.pm' >> $@; \
+	done
 	echo '	$(RM) blib/lib/Error.pm' >> $@
 	'$(PERL_PATH_SQ)' -MError -e 'exit($$Error::VERSION < 0.15009)' || \
 	echo '	cp private-Error.pm blib/lib/Error.pm' >> $@
 	echo install: >> $@
-	echo '	mkdir -p "$(instdir_SQ)"' >> $@
-	echo '	$(RM) "$(instdir_SQ)/Git.pm"; cp Git.pm "$(instdir_SQ)"' >> $@
-	echo '	$(RM) "$(instdir_SQ)/Error.pm"' >> $@
+	set -e; \
+	for i in $(modules); \
+	do \
+		if test $$i = $${i%/*}; \
+		then \
+			subdir=; \
+		else \
+			subdir=/$${i%/*}; \
+		fi; \
+		echo '	$(RM) "$$(DESTDIR)$(instdir_SQ)/'$$i'.pm"' >> $@; \
+		echo '	mkdir -p "$$(DESTDIR)$(instdir_SQ)'$$subdir'"' >> $@; \
+		echo '	cp '$$i'.pm "$$(DESTDIR)$(instdir_SQ)/'$$i'.pm"' >> $@; \
+	done
+	echo '	$(RM) "$$(DESTDIR)$(instdir_SQ)/Error.pm"' >> $@
 	'$(PERL_PATH_SQ)' -MError -e 'exit($$Error::VERSION < 0.15009)' || \
-	echo '	cp private-Error.pm "$(instdir_SQ)/Error.pm"' >> $@
+	echo '	cp private-Error.pm "$$(DESTDIR)$(instdir_SQ)/Error.pm"' >> $@
 	echo instlibdir: >> $@
 	echo '	echo $(instdir_SQ)' >> $@
 else
 $(makfile): Makefile.PL ../GIT-CFLAGS
-	$(PERL_PATH) $< PREFIX='$(prefix_SQ)'
+	$(PERL_PATH) $< PREFIX='$(prefix_SQ)' INSTALL_BASE='' --localedir='$(localedir_SQ)'
 endif
 
 # this is just added comfort for calling make directly in perl dir
diff --git a/perl/Makefile.PL b/perl/Makefile.PL
index 320253e..b54b04a 100644
--- a/perl/Makefile.PL
+++ b/perl/Makefile.PL
@@ -1,14 +1,42 @@
+use strict;
+use warnings;
 use ExtUtils::MakeMaker;
+use Getopt::Long;
+
+# Sanity: die at first unknown option
+Getopt::Long::Configure qw/ pass_through /;
+
+GetOptions("localedir=s" => \my $localedir);
 
 sub MY::postamble {
 	return <<'MAKE_FRAG';
 instlibdir:
 	@echo '$(INSTALLSITELIB)'
 
+ifneq (,$(DESTDIR))
+ifeq (0,$(shell expr '$(MM_VERSION)' '>' 6.10))
+$(error ExtUtils::MakeMaker version "$(MM_VERSION)" is older than 6.11 and so \
+	is likely incompatible with the DESTDIR mechanism.  Try setting \
+	NO_PERL_MAKEMAKER=1 instead)
+endif
+endif
+
 MAKE_FRAG
 }
 
-my %pm = ('Git.pm' => '$(INST_LIBDIR)/Git.pm');
+# XXX. When editing this list:
+#
+# * Please update perl/Makefile, too.
+# * Don't forget to test with NO_PERL_MAKEMAKER=YesPlease
+my %pm = (
+	'Git.pm' => '$(INST_LIBDIR)/Git.pm',
+	'Git/I18N.pm' => '$(INST_LIBDIR)/Git/I18N.pm',
+	'Git/SVN/Memoize/YAML.pm' => '$(INST_LIBDIR)/Git/SVN/Memoize/YAML.pm',
+	'Git/SVN/Fetcher.pm' => '$(INST_LIBDIR)/Git/SVN/Fetcher.pm',
+	'Git/SVN/Editor.pm' => '$(INST_LIBDIR)/Git/SVN/Editor.pm',
+	'Git/SVN/Prompt.pm' => '$(INST_LIBDIR)/Git/SVN/Prompt.pm',
+	'Git/SVN/Ra.pm' => '$(INST_LIBDIR)/Git/SVN/Ra.pm',
+);
 
 # We come with our own bundled Error.pm. It's not in the set of default
 # Perl modules so install it if it's not available on the system yet.
@@ -25,6 +53,7 @@
 	NAME            => 'Git',
 	VERSION_FROM    => 'Git.pm',
 	PM		=> \%pm,
+	PM_FILTER	=> qq[\$(PERL) -pe "s<\\Q++LOCALEDIR++\\E><$localedir>"],
 	MAKEFILE	=> 'perl.mak',
 	INSTALLSITEMAN3DIR => '$(SITEPREFIX)/share/man/man3'
 );
diff --git a/pkt-line.c b/pkt-line.c
index b691abe..eaba15f 100644
--- a/pkt-line.c
+++ b/pkt-line.c
@@ -1,6 +1,51 @@
 #include "cache.h"
 #include "pkt-line.h"
 
+static const char *packet_trace_prefix = "git";
+static const char trace_key[] = "GIT_TRACE_PACKET";
+
+void packet_trace_identity(const char *prog)
+{
+	packet_trace_prefix = xstrdup(prog);
+}
+
+static void packet_trace(const char *buf, unsigned int len, int write)
+{
+	int i;
+	struct strbuf out;
+
+	if (!trace_want(trace_key))
+		return;
+
+	/* +32 is just a guess for header + quoting */
+	strbuf_init(&out, len+32);
+
+	strbuf_addf(&out, "packet: %12s%c ",
+		    packet_trace_prefix, write ? '>' : '<');
+
+	if ((len >= 4 && !prefixcmp(buf, "PACK")) ||
+	    (len >= 5 && !prefixcmp(buf+1, "PACK"))) {
+		strbuf_addstr(&out, "PACK ...");
+		unsetenv(trace_key);
+	}
+	else {
+		/* XXX we should really handle printable utf8 */
+		for (i = 0; i < len; i++) {
+			/* suppress newlines */
+			if (buf[i] == '\n')
+				continue;
+			if (buf[i] >= 0x20 && buf[i] <= 0x7e)
+				strbuf_addch(&out, buf[i]);
+			else
+				strbuf_addf(&out, "\\%o", buf[i]);
+		}
+	}
+
+	strbuf_addch(&out, '\n');
+	trace_strbuf(trace_key, &out);
+	strbuf_release(&out);
+}
+
 /*
  * Write a packetized stream, where each line is preceded by
  * its length (including the header) as a 4-byte hex number.
@@ -39,20 +84,24 @@
  */
 void packet_flush(int fd)
 {
+	packet_trace("0000", 4, 1);
 	safe_write(fd, "0000", 4);
 }
 
-#define hex(a) (hexchar[(a) & 15])
-void packet_write(int fd, const char *fmt, ...)
+void packet_buf_flush(struct strbuf *buf)
 {
-	static char buffer[1000];
+	packet_trace("0000", 4, 1);
+	strbuf_add(buf, "0000", 4);
+}
+
+#define hex(a) (hexchar[(a) & 15])
+static char buffer[1000];
+static unsigned format_packet(const char *fmt, va_list args)
+{
 	static char hexchar[] = "0123456789abcdef";
-	va_list args;
 	unsigned n;
 
-	va_start(args, fmt);
 	n = vsnprintf(buffer + 4, sizeof(buffer) - 4, fmt, args);
-	va_end(args);
 	if (n >= sizeof(buffer)-4)
 		die("protocol error: impossibly long line");
 	n += 4;
@@ -60,27 +109,52 @@
 	buffer[1] = hex(n >> 8);
 	buffer[2] = hex(n >> 4);
 	buffer[3] = hex(n);
+	packet_trace(buffer+4, n-4, 1);
+	return n;
+}
+
+void packet_write(int fd, const char *fmt, ...)
+{
+	va_list args;
+	unsigned n;
+
+	va_start(args, fmt);
+	n = format_packet(fmt, args);
+	va_end(args);
 	safe_write(fd, buffer, n);
 }
 
-static void safe_read(int fd, void *buffer, unsigned size)
+void packet_buf_write(struct strbuf *buf, const char *fmt, ...)
+{
+	va_list args;
+	unsigned n;
+
+	va_start(args, fmt);
+	n = format_packet(fmt, args);
+	va_end(args);
+	strbuf_add(buf, buffer, n);
+}
+
+static int safe_read(int fd, void *buffer, unsigned size, int return_line_fail)
 {
 	ssize_t ret = read_in_full(fd, buffer, size);
 	if (ret < 0)
 		die_errno("read error");
-	else if (ret < size)
+	else if (ret < size) {
+		if (return_line_fail)
+			return -1;
+
 		die("The remote end hung up unexpectedly");
+	}
+
+	return ret;
 }
 
-int packet_read_line(int fd, char *buffer, unsigned size)
+static int packet_length(const char *linelen)
 {
 	int n;
-	unsigned len;
-	char linelen[4];
+	int len = 0;
 
-	safe_read(fd, linelen, 4);
-
-	len = 0;
 	for (n = 0; n < 4; n++) {
 		unsigned char c = linelen[n];
 		len <<= 4;
@@ -96,14 +170,73 @@
 			len += c - 'A' + 10;
 			continue;
 		}
-		die("protocol error: bad line length character");
+		return -1;
 	}
-	if (!len)
+	return len;
+}
+
+static int packet_read_internal(int fd, char *buffer, unsigned size, int return_line_fail)
+{
+	int len, ret;
+	char linelen[4];
+
+	ret = safe_read(fd, linelen, 4, return_line_fail);
+	if (return_line_fail && ret < 0)
+		return ret;
+	len = packet_length(linelen);
+	if (len < 0)
+		die("protocol error: bad line length character: %.4s", linelen);
+	if (!len) {
+		packet_trace("0000", 4, 0);
 		return 0;
+	}
 	len -= 4;
 	if (len >= size)
 		die("protocol error: bad line length %d", len);
-	safe_read(fd, buffer, len);
+	ret = safe_read(fd, buffer, len, return_line_fail);
+	if (return_line_fail && ret < 0)
+		return ret;
 	buffer[len] = 0;
+	packet_trace(buffer, len, 0);
+	return len;
+}
+
+int packet_read(int fd, char *buffer, unsigned size)
+{
+	return packet_read_internal(fd, buffer, size, 1);
+}
+
+int packet_read_line(int fd, char *buffer, unsigned size)
+{
+	return packet_read_internal(fd, buffer, size, 0);
+}
+
+int packet_get_line(struct strbuf *out,
+	char **src_buf, size_t *src_len)
+{
+	int len;
+
+	if (*src_len < 4)
+		return -1;
+	len = packet_length(*src_buf);
+	if (len < 0)
+		return -1;
+	if (!len) {
+		*src_buf += 4;
+		*src_len -= 4;
+		packet_trace("0000", 4, 0);
+		return 0;
+	}
+	if (*src_len < len)
+		return -2;
+
+	*src_buf += 4;
+	*src_len -= 4;
+	len -= 4;
+
+	strbuf_add(out, *src_buf, len);
+	*src_buf += len;
+	*src_len -= len;
+	packet_trace(out->buf, out->len, 0);
 	return len;
 }
diff --git a/pkt-line.h b/pkt-line.h
index 9df653f..8cfeb0c 100644
--- a/pkt-line.h
+++ b/pkt-line.h
@@ -2,14 +2,19 @@
 #define PKTLINE_H
 
 #include "git-compat-util.h"
+#include "strbuf.h"
 
 /*
  * Silly packetized line writing interface
  */
 void packet_flush(int fd);
 void packet_write(int fd, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
+void packet_buf_flush(struct strbuf *buf);
+void packet_buf_write(struct strbuf *buf, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
 
 int packet_read_line(int fd, char *buffer, unsigned size);
+int packet_read(int fd, char *buffer, unsigned size);
+int packet_get_line(struct strbuf *out, char **src_buf, size_t *src_len);
 ssize_t safe_write(int, const void *, ssize_t);
 
 #endif
diff --git a/po/.gitignore b/po/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/po/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/po/README b/po/README
new file mode 100644
index 0000000..c1520e8
--- /dev/null
+++ b/po/README
@@ -0,0 +1,292 @@
+Core GIT Translations
+=====================
+
+This directory holds the translations for the core of Git. This document
+describes how you can contribute to the effort of enhancing the language
+coverage and maintaining the translation.
+
+The localization (l10n) coordinator, Jiang Xin <worldhello.net@gmail.com>,
+coordinates our localization effort in the l10 coordinator repository:
+
+        https://github.com/git-l10n/git-po/
+
+As a contributor for a language XX, you should first check TEAMS file in
+this directory to see whether a dedicated repository for your language XX
+exists. Fork the dedicated repository and start to work if it exists.
+
+If you are the first contributor for the language XX, please fork this
+repository, prepare and/or update the translated message file po/XX.po
+(described later), and ask the l10n coordinator to pull your work.
+
+If there are multiple contributors for the same language, please first
+coordinate among yourselves and nominate the team leader for your
+language, so that the l10n coordinator only needs to interact with one
+person per language.
+
+The overall data-flow looks like this:
+
+    +-------------------+            +------------------+
+    | Git source code   | ---(1)---> | L10n coordinator |
+    | repository        | <---(4)--- | repository       |
+    +-------------------+            +------------------+
+                                          |      ^
+                                         (2)    (3)
+                                          V      |
+                                     +------------------+
+                                     | Language Team XX |
+                                     +------------------+
+
+ * Translatable strings are marked in the source file.
+ * L10n coordinator pulls from the source (1)
+ * L10n coordinator updates the message template po/git.pot
+ * Language team pulls from L10n coordinator (2)
+ * Language team updates the message file po/XX.po
+ * L10n coordinator pulls from Language team (3)
+ * L10n coordinator asks the result to be pulled (4).
+
+
+Maintaining the po/git.pot file
+-------------------------------
+
+(This is done by the l10n coordinator).
+
+The po/git.pot file contains a message catalog extracted from Git's
+sources. The l10n coordinator maintains it by adding new translations with
+msginit(1), or update existing ones with msgmerge(1).  In order to update
+the Git sources to extract the messages from, the l10n coordinator is
+expected to pull from the main git repository at strategic point in
+history (e.g. when a major release and release candidates are tagged),
+and then run "make pot" at the top-level directory.
+
+Language contributors use this file to prepare translations for their
+language, but they are not expected to modify it.
+
+
+Initializing a XX.po file
+-------------------------
+
+(This is done by the language teams).
+
+If your language XX does not have translated message file po/XX.po yet,
+you add a translation for the first time by running:
+
+    msginit --locale=XX
+
+in the po/ directory, where XX is the locale, e.g. "de", "is", "pt_BR",
+"zh_CN", etc.
+
+Then edit the automatically generated copyright info in your new XX.po
+to be correct, e.g. for Icelandic:
+
+    @@ -1,6 +1,6 @@
+    -# Icelandic translations for PACKAGE package.
+    -# Copyright (C) 2010 THE PACKAGE'S COPYRIGHT HOLDER
+    -# This file is distributed under the same license as the PACKAGE package.
+    +# Icelandic translations for Git.
+    +# Copyright (C) 2010 Ævar Arnfjörð Bjarmason <avarab@gmail.com>
+    +# This file is distributed under the same license as the Git package.
+     # Ævar Arnfjörð Bjarmason <avarab@gmail.com>, 2010.
+
+And change references to PACKAGE VERSION in the PO Header Entry to
+just "Git":
+
+    perl -pi -e 's/(?<="Project-Id-Version: )PACKAGE VERSION/Git/' XX.po
+
+Once you are done testing the translation (see below), commit the result
+and ask the l10n coordinator to pull from you.
+
+
+Updating a XX.po file
+---------------------
+
+(This is done by the language teams).
+
+If you are replacing translation strings in an existing XX.po file to
+improve the translation, just edit the file.
+
+If there's an existing XX.po file for your language, but the repository
+of the l10n coordinator has newer po/git.pot file, you would need to first
+pull from the l10n coordinator (see the beginning of this document for its
+URL), and then update the existing translation by running:
+
+    msgmerge --add-location --backup=off -U XX.po git.pot
+
+in the po/ directory, where XX.po is the file you want to update.
+
+Once you are done testing the translation (see below), commit the result
+and ask the l10n coordinator to pull from you.
+
+
+Testing your changes
+--------------------
+
+(This is done by the language teams, after creating or updating XX.po file).
+
+Before you submit your changes go back to the top-level and do:
+
+    make
+
+On systems with GNU gettext (i.e. not Solaris) this will compile your
+changed PO file with `msgfmt --check`, the --check option flags many
+common errors, e.g. missing printf format strings, or translated
+messages that deviate from the originals in whether they begin/end
+with a newline or not.
+
+
+Marking strings for translation
+-------------------------------
+
+(This is done by the core developers).
+
+Before strings can be translated they first have to be marked for
+translation.
+
+Git uses an internationalization interface that wraps the system's
+gettext library, so most of the advice in your gettext documentation
+(on GNU systems `info gettext` in a terminal) applies.
+
+General advice:
+
+ - Don't mark everything for translation, only strings which will be
+   read by humans (the porcelain interface) should be translated.
+
+   The output from Git's plumbing utilities will primarily be read by
+   programs and would break scripts under non-C locales if it was
+   translated. Plumbing strings should not be translated, since
+   they're part of Git's API.
+
+ - Adjust the strings so that they're easy to translate. Most of the
+   advice in `info '(gettext)Preparing Strings'` applies here.
+
+ - If something is unclear or ambiguous you can use a "TRANSLATORS"
+   comment to tell the translators what to make of it. These will be
+   extracted by xgettext(1) and put in the po/*.po files, e.g. from
+   git-am.sh:
+
+       # TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
+       # in your translation. The program will only accept English
+       # input at this point.
+       gettext "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
+
+   Or in C, from builtin/revert.c:
+
+       /* TRANSLATORS: %s will be "revert" or "cherry-pick" */
+       die(_("%s: Unable to write new index file"), action_name(opts));
+
+We provide wrappers for C, Shell and Perl programs. Here's how they're
+used:
+
+C:
+
+ - Include builtin.h at the top, it'll pull in gettext.h, which
+   defines the gettext interface. Consult with the list if you need to
+   use gettext.h directly.
+
+ - The C interface is a subset of the normal GNU gettext
+   interface. We currently export these functions:
+
+   - _()
+
+    Mark and translate a string. E.g.:
+
+        printf(_("HEAD is now at %s"), hex);
+
+   - Q_()
+
+    Mark and translate a plural string. E.g.:
+
+        printf(Q_("%d commit", "%d commits", number_of_commits));
+
+    This is just a wrapper for the ngettext() function.
+
+   - N_()
+
+    A no-op pass-through macro for marking strings inside static
+    initializations, e.g.:
+
+        static const char *reset_type_names[] = {
+            N_("mixed"), N_("soft"), N_("hard"), N_("merge"), N_("keep"), NULL
+        };
+
+    And then, later:
+
+        die(_("%s reset is not allowed in a bare repository"),
+               _(reset_type_names[reset_type]));
+
+    Here _() couldn't have statically determined what the translation
+    string will be, but since it was already marked for translation
+    with N_() the look-up in the message catalog will succeed.
+
+Shell:
+
+ - The Git gettext shell interface is just a wrapper for
+   gettext.sh. Import it right after git-sh-setup like this:
+
+       . git-sh-setup
+       . git-sh-i18n
+
+   And then use the gettext or eval_gettext functions:
+
+       # For constant interface messages:
+       gettext "A message for the user"; echo
+
+       # To interpolate variables:
+       details="oh noes"
+       eval_gettext "An error occured: \$details"; echo
+
+   In addition we have wrappers for messages that end with a trailing
+   newline. I.e. you could write the above as:
+
+       # For constant interface messages:
+       gettextln "A message for the user"
+
+       # To interpolate variables:
+       details="oh noes"
+       eval_gettextln "An error occured: \$details"
+
+   More documentation about the interface is available in the GNU info
+   page: `info '(gettext)sh'`. Looking at git-am.sh (the first shell
+   command to be translated) for examples is also useful:
+
+       git log --reverse -p --grep=i18n git-am.sh
+
+Perl:
+
+ - The Git::I18N module provides a limited subset of the
+   Locale::Messages functionality, e.g.:
+
+       use Git::I18N;
+       print __("Welcome to Git!\n");
+       printf __("The following error occured: %s\n"), $error;
+
+   Run `perldoc perl/Git/I18N.pm` for more info.
+
+
+Testing marked strings
+----------------------
+
+Even if you've correctly marked porcelain strings for translation
+something in the test suite might still depend on the US English
+version of the strings, e.g. to grep some error message or other
+output.
+
+To smoke out issues like these Git can be compiled with gettext poison
+support, at the top-level:
+
+    make GETTEXT_POISON=YesPlease
+
+That'll give you a git which emits gibberish on every call to
+gettext. It's obviously not meant to be installed, but you should run
+the test suite with it:
+
+    cd t && prove -j 9 ./t[0-9]*.sh
+
+If tests break with it you should inspect them manually and see if
+what you're translating is sane, i.e. that you're not translating
+plumbing output.
+
+If not you should replace calls to grep with test_i18ngrep, or
+test_cmp calls with test_i18ncmp. If that's not enough you can skip
+the whole test by making it depend on the C_LOCALE_OUTPUT
+prerequisite. See existing test files with this prerequisite for
+examples.
diff --git a/po/TEAMS b/po/TEAMS
new file mode 100644
index 0000000..e9ebc8c
--- /dev/null
+++ b/po/TEAMS
@@ -0,0 +1,47 @@
+Core Git translation language teams
+(please keep the list sorted alphabetically on language field)
+
+Language:	da (Danish)
+Repository:	https://github.com/git-da/git-po/
+Leader:		Byrial Jensen <byrial@vip.cybercity.dk>
+
+Language:	de (German)
+Repository:	https://github.com/ralfth/git-po-de
+Leader:		Ralf Thielow <ralf.thielow@googlemail.com>
+Members:	Thomas Rast <trast@student.ethz.ch>
+		Jan Krüger <jk@jk.gs>
+		Christian Stimming <stimming@tuhh.de>
+
+Language:	is (Icelandic)
+Leader:		Ævar Arnfjörð Bjarmason <avarab@gmail.com>
+
+Language:	it (Italian)
+Repository:	https://github.com/quizzlo/git-po-it/
+Leader:		Marco Paolone <marcopaolone AT gmail.com>
+Members:	Stefano Lattarini <stefano.lattarini AT gmail.com>
+
+Language:	nl (Dutch)
+Repository:	https://github.com/vfr-nl/git-po/
+Leader:		Vincent van Ravesteijn <vfr@lyx.org>
+
+Language:	pt_PT (Portuguese - Portugal)
+Repository:	https://github.com/marcomsousa/git-l10n-pt_PT/
+Leader:		Marco Sousa <marcomsousa AT gmail.com>
+
+Language:	sv (Swedish)
+Repository:	https://github.com/nafmo/git-l10n-sv/
+Leader:		Peter Krefting <peter@softwolves.pp.se>
+
+Language:	vi (Vietnamese)
+Repository:	https://github.com/vnwildman/git.git
+Leader:		Trần Ngọc Quân <vnwildman AT gmail.com>
+
+Language:	zh_CN (Simplified Chinese)
+Repository:	https://github.com/gotgit/git-po-zh_CN/
+Leader:		Jiang Xin <worldhello.net@gmail.com>
+Members:	Riku <lu.riku AT gmail.com>
+		Zhuang Ya <zhuangya AT me.com>
+		Lian Cheng <rhythm.mail AT gmail.com>
+		Yichao Yu <yyc1992 AT gmail.com>
+		ws3389 <willsmith3389 AT gmail.com>
+		Thynson <lanxingcan AT gmail.com>
diff --git a/po/da.po b/po/da.po
new file mode 100644
index 0000000..20a88ea
--- /dev/null
+++ b/po/da.po
@@ -0,0 +1,3503 @@
+# Danish translations for Git.
+# This file is distributed under the same license as the PACKAGE package.
+# Byrial Jensen <byrial@vip.cybercity.dk>, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Git\n"
+"Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
+"POT-Creation-Date: 2012-03-16 20:18+0800\n"
+"PO-Revision-Date: 2012-04-10 18:41+0200\n"
+"Last-Translator: Byrial Jensen <byrial@vip.cybercity.dk>\n"
+"Language-Team: Danish <dansk@dansk-gruppen.dk>\n"
+"Language: da\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: advice.c:34
+#, c-format
+msgid "hint: %.*s\n"
+msgstr ""
+
+#.
+#. * Message used both when 'git commit' fails and when
+#. * other commands doing a merge do.
+#.
+#: advice.c:64
+msgid ""
+"Fix them up in the work tree,\n"
+"and then use 'git add/rm <file>' as\n"
+"appropriate to mark resolution and make a commit,\n"
+"or use 'git commit -a'."
+msgstr ""
+
+#: commit.c:47
+#, c-format
+msgid "could not parse %s"
+msgstr ""
+
+#: commit.c:49
+#, c-format
+msgid "%s %s is not a commit!"
+msgstr ""
+
+#: compat/obstack.c:406 compat/obstack.c:408
+msgid "memory exhausted"
+msgstr ""
+
+#: connected.c:39
+msgid "Could not run 'git rev-list'"
+msgstr ""
+
+#: connected.c:48
+#, c-format
+msgid "failed write to rev-list: %s"
+msgstr ""
+
+#: connected.c:56
+#, c-format
+msgid "failed to close rev-list's stdin: %s"
+msgstr ""
+
+#: diff.c:105
+#, c-format
+msgid "  Failed to parse dirstat cut-off percentage '%.*s'\n"
+msgstr ""
+
+#: diff.c:110
+#, c-format
+msgid "  Unknown dirstat parameter '%.*s'\n"
+msgstr ""
+
+#: diff.c:210
+#, c-format
+msgid ""
+"Found errors in 'diff.dirstat' config variable:\n"
+"%s"
+msgstr ""
+
+#: diff.c:1336
+msgid " 0 files changed\n"
+msgstr ""
+
+#: diff.c:1340
+#, c-format
+msgid " %d file changed"
+msgid_plural " %d files changed"
+msgstr[0] ""
+msgstr[1] ""
+
+#: diff.c:1357
+#, c-format
+msgid ", %d insertion(+)"
+msgid_plural ", %d insertions(+)"
+msgstr[0] ""
+msgstr[1] ""
+
+#: diff.c:1368
+#, c-format
+msgid ", %d deletion(-)"
+msgid_plural ", %d deletions(-)"
+msgstr[0] ""
+msgstr[1] ""
+
+#: diff.c:3424
+#, c-format
+msgid ""
+"Failed to parse --dirstat/-X option parameter:\n"
+"%s"
+msgstr ""
+
+#: gpg-interface.c:59
+msgid "could not run gpg."
+msgstr ""
+
+#: gpg-interface.c:71
+msgid "gpg did not accept the data"
+msgstr ""
+
+#: gpg-interface.c:82
+msgid "gpg failed to sign the data"
+msgstr ""
+
+#: grep.c:1280
+#, c-format
+msgid "'%s': unable to read %s"
+msgstr ""
+
+#: grep.c:1297
+#, c-format
+msgid "'%s': %s"
+msgstr ""
+
+#: grep.c:1308
+#, c-format
+msgid "'%s': short read %s"
+msgstr ""
+
+#: help.c:287
+#, c-format
+msgid ""
+"'%s' appears to be a git command, but we were not\n"
+"able to execute it. Maybe git-%s is broken?"
+msgstr ""
+
+#: remote.c:1607
+#, c-format
+msgid "Your branch is ahead of '%s' by %d commit.\n"
+msgid_plural "Your branch is ahead of '%s' by %d commits.\n"
+msgstr[0] ""
+msgstr[1] ""
+
+#: remote.c:1613
+#, c-format
+msgid "Your branch is behind '%s' by %d commit, and can be fast-forwarded.\n"
+msgid_plural ""
+"Your branch is behind '%s' by %d commits, and can be fast-forwarded.\n"
+msgstr[0] ""
+msgstr[1] ""
+
+#: remote.c:1621
+#, c-format
+msgid ""
+"Your branch and '%s' have diverged,\n"
+"and have %d and %d different commit each, respectively.\n"
+msgid_plural ""
+"Your branch and '%s' have diverged,\n"
+"and have %d and %d different commits each, respectively.\n"
+msgstr[0] ""
+msgstr[1] ""
+
+#: sequencer.c:120 builtin/merge.c:864 builtin/merge.c:985
+#: builtin/merge.c:1095 builtin/merge.c:1105
+#, c-format
+msgid "Could not open '%s' for writing"
+msgstr ""
+
+#: sequencer.c:122 builtin/merge.c:334 builtin/merge.c:867
+#: builtin/merge.c:1097 builtin/merge.c:1110
+#, c-format
+msgid "Could not write to '%s'"
+msgstr ""
+
+#: sequencer.c:143
+msgid ""
+"after resolving the conflicts, mark the corrected paths\n"
+"with 'git add <paths>' or 'git rm <paths>'"
+msgstr ""
+
+#: sequencer.c:146
+msgid ""
+"after resolving the conflicts, mark the corrected paths\n"
+"with 'git add <paths>' or 'git rm <paths>'\n"
+"and commit the result with 'git commit'"
+msgstr ""
+
+#: sequencer.c:159 sequencer.c:685 sequencer.c:768
+#, c-format
+msgid "Could not write to %s"
+msgstr ""
+
+#: sequencer.c:162
+#, c-format
+msgid "Error wrapping up %s"
+msgstr ""
+
+#: sequencer.c:177
+msgid "Your local changes would be overwritten by cherry-pick."
+msgstr ""
+
+#: sequencer.c:179
+msgid "Your local changes would be overwritten by revert."
+msgstr ""
+
+#: sequencer.c:182
+msgid "Commit your changes or stash them to proceed."
+msgstr ""
+
+#. TRANSLATORS: %s will be "revert" or "cherry-pick"
+#: sequencer.c:232
+#, c-format
+msgid "%s: Unable to write new index file"
+msgstr ""
+
+#: sequencer.c:298
+msgid "Your index file is unmerged."
+msgstr ""
+
+#: sequencer.c:301
+msgid "You do not have a valid HEAD"
+msgstr ""
+
+#: sequencer.c:316
+#, c-format
+msgid "Commit %s is a merge but no -m option was given."
+msgstr ""
+
+#: sequencer.c:324
+#, c-format
+msgid "Commit %s does not have parent %d"
+msgstr ""
+
+#: sequencer.c:328
+#, c-format
+msgid "Mainline was specified but commit %s is not a merge."
+msgstr ""
+
+#. TRANSLATORS: The first %s will be "revert" or
+#. "cherry-pick", the second %s a SHA1
+#: sequencer.c:339
+#, c-format
+msgid "%s: cannot parse parent commit %s"
+msgstr ""
+
+#: sequencer.c:343
+#, c-format
+msgid "Cannot get commit message for %s"
+msgstr ""
+
+#: sequencer.c:427
+#, c-format
+msgid "could not revert %s... %s"
+msgstr ""
+
+#: sequencer.c:428
+#, c-format
+msgid "could not apply %s... %s"
+msgstr ""
+
+#: sequencer.c:450 sequencer.c:909 builtin/log.c:288 builtin/log.c:713
+#: builtin/log.c:1329 builtin/log.c:1548 builtin/merge.c:348
+#: builtin/shortlog.c:181
+msgid "revision walk setup failed"
+msgstr ""
+
+#: sequencer.c:453
+msgid "empty commit set passed"
+msgstr ""
+
+#: sequencer.c:461
+#, c-format
+msgid "git %s: failed to read the index"
+msgstr ""
+
+#: sequencer.c:466
+#, c-format
+msgid "git %s: failed to refresh the index"
+msgstr ""
+
+#: sequencer.c:551
+#, c-format
+msgid "Cannot %s during a %s"
+msgstr ""
+
+#: sequencer.c:573
+#, c-format
+msgid "Could not parse line %d."
+msgstr ""
+
+#: sequencer.c:578
+msgid "No commits parsed."
+msgstr ""
+
+#: sequencer.c:591
+#, c-format
+msgid "Could not open %s"
+msgstr ""
+
+#: sequencer.c:595
+#, c-format
+msgid "Could not read %s."
+msgstr ""
+
+#: sequencer.c:602
+#, c-format
+msgid "Unusable instruction sheet: %s"
+msgstr ""
+
+#: sequencer.c:630
+#, c-format
+msgid "Invalid key: %s"
+msgstr ""
+
+#: sequencer.c:633
+#, c-format
+msgid "Invalid value for %s: %s"
+msgstr ""
+
+#: sequencer.c:645
+#, c-format
+msgid "Malformed options sheet: %s"
+msgstr ""
+
+#: sequencer.c:666
+msgid "a cherry-pick or revert is already in progress"
+msgstr ""
+
+#: sequencer.c:667
+msgid "try \"git cherry-pick (--continue | --quit | --abort)\""
+msgstr ""
+
+#: sequencer.c:671
+#, c-format
+msgid "Could not create sequencer directory %s"
+msgstr ""
+
+#: sequencer.c:687 sequencer.c:772
+#, c-format
+msgid "Error wrapping up %s."
+msgstr ""
+
+#: sequencer.c:706 sequencer.c:840
+msgid "no cherry-pick or revert in progress"
+msgstr ""
+
+#: sequencer.c:708
+msgid "cannot resolve HEAD"
+msgstr ""
+
+#: sequencer.c:710
+msgid "cannot abort from a branch yet to be born"
+msgstr ""
+
+#: sequencer.c:732
+#, c-format
+msgid "cannot open %s: %s"
+msgstr ""
+
+#: sequencer.c:735
+#, c-format
+msgid "cannot read %s: %s"
+msgstr ""
+
+#: sequencer.c:736
+msgid "unexpected end of file"
+msgstr ""
+
+#: sequencer.c:742
+#, c-format
+msgid "stored pre-cherry-pick HEAD file '%s' is corrupt"
+msgstr ""
+
+#: sequencer.c:765
+#, c-format
+msgid "Could not format %s."
+msgstr ""
+
+#: sequencer.c:927
+msgid "Can't revert as initial commit"
+msgstr ""
+
+#: sequencer.c:928
+msgid "Can't cherry-pick into empty head"
+msgstr ""
+
+#: wt-status.c:134
+msgid "Unmerged paths:"
+msgstr ""
+
+#: wt-status.c:140 wt-status.c:157
+#, c-format
+msgid "  (use \"git reset %s <file>...\" to unstage)"
+msgstr ""
+
+#: wt-status.c:142 wt-status.c:159
+msgid "  (use \"git rm --cached <file>...\" to unstage)"
+msgstr ""
+
+#: wt-status.c:143
+msgid "  (use \"git add/rm <file>...\" as appropriate to mark resolution)"
+msgstr ""
+
+#: wt-status.c:151
+msgid "Changes to be committed:"
+msgstr ""
+
+#: wt-status.c:169
+msgid "Changes not staged for commit:"
+msgstr ""
+
+#: wt-status.c:173
+msgid "  (use \"git add <file>...\" to update what will be committed)"
+msgstr ""
+
+#: wt-status.c:175
+msgid "  (use \"git add/rm <file>...\" to update what will be committed)"
+msgstr ""
+
+#: wt-status.c:176
+msgid ""
+"  (use \"git checkout -- <file>...\" to discard changes in working directory)"
+msgstr ""
+
+#: wt-status.c:178
+msgid "  (commit or discard the untracked or modified content in submodules)"
+msgstr ""
+
+#: wt-status.c:187
+#, c-format
+msgid "%s files:"
+msgstr ""
+
+#: wt-status.c:190
+#, c-format
+msgid "  (use \"git %s <file>...\" to include in what will be committed)"
+msgstr ""
+
+#: wt-status.c:207
+msgid "bug"
+msgstr ""
+
+#: wt-status.c:212
+msgid "both deleted:"
+msgstr ""
+
+#: wt-status.c:213
+msgid "added by us:"
+msgstr ""
+
+#: wt-status.c:214
+msgid "deleted by them:"
+msgstr ""
+
+#: wt-status.c:215
+msgid "added by them:"
+msgstr ""
+
+#: wt-status.c:216
+msgid "deleted by us:"
+msgstr ""
+
+#: wt-status.c:217
+msgid "both added:"
+msgstr ""
+
+#: wt-status.c:218
+msgid "both modified:"
+msgstr ""
+
+#: wt-status.c:248
+msgid "new commits, "
+msgstr ""
+
+#: wt-status.c:250
+msgid "modified content, "
+msgstr ""
+
+#: wt-status.c:252
+msgid "untracked content, "
+msgstr ""
+
+#: wt-status.c:266
+#, c-format
+msgid "new file:   %s"
+msgstr ""
+
+#: wt-status.c:269
+#, c-format
+msgid "copied:     %s -> %s"
+msgstr ""
+
+#: wt-status.c:272
+#, c-format
+msgid "deleted:    %s"
+msgstr ""
+
+#: wt-status.c:275
+#, c-format
+msgid "modified:   %s"
+msgstr ""
+
+#: wt-status.c:278
+#, c-format
+msgid "renamed:    %s -> %s"
+msgstr ""
+
+#: wt-status.c:281
+#, c-format
+msgid "typechange: %s"
+msgstr ""
+
+#: wt-status.c:284
+#, c-format
+msgid "unknown:    %s"
+msgstr ""
+
+#: wt-status.c:287
+#, c-format
+msgid "unmerged:   %s"
+msgstr ""
+
+#: wt-status.c:290
+#, c-format
+msgid "bug: unhandled diff status %c"
+msgstr ""
+
+#: wt-status.c:713
+msgid "On branch "
+msgstr ""
+
+#: wt-status.c:720
+msgid "Not currently on any branch."
+msgstr ""
+
+#: wt-status.c:731
+msgid "Initial commit"
+msgstr ""
+
+#: wt-status.c:745
+msgid "Untracked"
+msgstr ""
+
+#: wt-status.c:747
+msgid "Ignored"
+msgstr ""
+
+#: wt-status.c:749
+#, c-format
+msgid "Untracked files not listed%s"
+msgstr ""
+
+#: wt-status.c:751
+msgid " (use -u option to show untracked files)"
+msgstr ""
+
+#: wt-status.c:757
+msgid "No changes"
+msgstr ""
+
+#: wt-status.c:761
+#, c-format
+msgid "no changes added to commit%s\n"
+msgstr ""
+
+#: wt-status.c:763
+msgid " (use \"git add\" and/or \"git commit -a\")"
+msgstr ""
+
+#: wt-status.c:765
+#, c-format
+msgid "nothing added to commit but untracked files present%s\n"
+msgstr ""
+
+#: wt-status.c:767
+msgid " (use \"git add\" to track)"
+msgstr ""
+
+#: wt-status.c:769 wt-status.c:772 wt-status.c:775
+#, c-format
+msgid "nothing to commit%s\n"
+msgstr ""
+
+#: wt-status.c:770
+msgid " (create/copy files and use \"git add\" to track)"
+msgstr ""
+
+#: wt-status.c:773
+msgid " (use -u to show untracked files)"
+msgstr ""
+
+#: wt-status.c:776
+msgid " (working directory clean)"
+msgstr ""
+
+#: wt-status.c:884
+msgid "HEAD (no branch)"
+msgstr ""
+
+#: wt-status.c:890
+msgid "Initial commit on "
+msgstr ""
+
+#: wt-status.c:905
+msgid "behind "
+msgstr ""
+
+#: wt-status.c:908 wt-status.c:911
+msgid "ahead "
+msgstr ""
+
+#: wt-status.c:913
+msgid ", behind "
+msgstr ""
+
+#: builtin/add.c:62
+#, c-format
+msgid "unexpected diff status %c"
+msgstr ""
+
+#: builtin/add.c:67 builtin/commit.c:298
+msgid "updating files failed"
+msgstr ""
+
+#: builtin/add.c:77
+#, c-format
+msgid "remove '%s'\n"
+msgstr ""
+
+#: builtin/add.c:176
+#, c-format
+msgid "Path '%s' is in submodule '%.*s'"
+msgstr ""
+
+#: builtin/add.c:192
+msgid "Unstaged changes after refreshing the index:"
+msgstr ""
+
+#: builtin/add.c:195 builtin/add.c:456 builtin/rm.c:186
+#, c-format
+msgid "pathspec '%s' did not match any files"
+msgstr ""
+
+#: builtin/add.c:209
+#, c-format
+msgid "'%s' is beyond a symbolic link"
+msgstr ""
+
+#: builtin/add.c:276
+msgid "Could not read the index"
+msgstr ""
+
+#: builtin/add.c:286
+#, c-format
+msgid "Could not open '%s' for writing."
+msgstr ""
+
+#: builtin/add.c:290
+msgid "Could not write patch"
+msgstr ""
+
+#: builtin/add.c:295
+#, c-format
+msgid "Could not stat '%s'"
+msgstr ""
+
+#: builtin/add.c:297
+msgid "Empty patch. Aborted."
+msgstr ""
+
+#: builtin/add.c:303
+#, c-format
+msgid "Could not apply '%s'"
+msgstr ""
+
+#: builtin/add.c:312
+msgid "The following paths are ignored by one of your .gitignore files:\n"
+msgstr ""
+
+#: builtin/add.c:352
+#, c-format
+msgid "Use -f if you really want to add them.\n"
+msgstr ""
+
+#: builtin/add.c:353
+msgid "no files added"
+msgstr ""
+
+#: builtin/add.c:359
+msgid "adding files failed"
+msgstr ""
+
+#: builtin/add.c:391
+msgid "-A and -u are mutually incompatible"
+msgstr ""
+
+#: builtin/add.c:393
+msgid "Option --ignore-missing can only be used together with --dry-run"
+msgstr ""
+
+#: builtin/add.c:413
+#, c-format
+msgid "Nothing specified, nothing added.\n"
+msgstr ""
+
+#: builtin/add.c:414
+#, c-format
+msgid "Maybe you wanted to say 'git add .'?\n"
+msgstr ""
+
+#: builtin/add.c:420 builtin/clean.c:95 builtin/commit.c:358 builtin/mv.c:82
+#: builtin/rm.c:162
+msgid "index file corrupt"
+msgstr ""
+
+#: builtin/add.c:476 builtin/mv.c:229 builtin/rm.c:260
+msgid "Unable to write new index file"
+msgstr ""
+
+#: builtin/archive.c:17
+#, c-format
+msgid "could not create archive file '%s'"
+msgstr ""
+
+#: builtin/archive.c:20
+msgid "could not redirect output"
+msgstr ""
+
+#: builtin/archive.c:37
+msgid "git archive: Remote with no URL"
+msgstr ""
+
+#: builtin/archive.c:58
+msgid "git archive: expected ACK/NAK, got EOF"
+msgstr ""
+
+#: builtin/archive.c:63
+#, c-format
+msgid "git archive: NACK %s"
+msgstr ""
+
+#: builtin/archive.c:65
+#, c-format
+msgid "remote error: %s"
+msgstr ""
+
+#: builtin/archive.c:66
+msgid "git archive: protocol error"
+msgstr ""
+
+#: builtin/archive.c:71
+msgid "git archive: expected a flush"
+msgstr ""
+
+#: builtin/branch.c:137
+#, c-format
+msgid ""
+"deleting branch '%s' that has been merged to\n"
+"         '%s', but not yet merged to HEAD."
+msgstr ""
+
+#: builtin/branch.c:141
+#, c-format
+msgid ""
+"not deleting branch '%s' that is not yet merged to\n"
+"         '%s', even though it is merged to HEAD."
+msgstr ""
+
+#. TRANSLATORS: This is "remote " in "remote branch '%s' not found"
+#: builtin/branch.c:163
+msgid "remote "
+msgstr ""
+
+#: builtin/branch.c:171
+msgid "cannot use -a with -d"
+msgstr ""
+
+#: builtin/branch.c:177
+msgid "Couldn't look up commit object for HEAD"
+msgstr ""
+
+#: builtin/branch.c:182
+#, c-format
+msgid "Cannot delete the branch '%s' which you are currently on."
+msgstr ""
+
+#: builtin/branch.c:192
+#, c-format
+msgid "%sbranch '%s' not found."
+msgstr ""
+
+#: builtin/branch.c:200
+#, c-format
+msgid "Couldn't look up commit object for '%s'"
+msgstr ""
+
+#: builtin/branch.c:206
+#, c-format
+msgid ""
+"The branch '%s' is not fully merged.\n"
+"If you are sure you want to delete it, run 'git branch -D %s'."
+msgstr ""
+
+#: builtin/branch.c:214
+#, c-format
+msgid "Error deleting %sbranch '%s'"
+msgstr ""
+
+#: builtin/branch.c:219
+#, c-format
+msgid "Deleted %sbranch %s (was %s).\n"
+msgstr ""
+
+#: builtin/branch.c:224
+msgid "Update of config-file failed"
+msgstr ""
+
+#: builtin/branch.c:322
+#, c-format
+msgid "branch '%s' does not point at a commit"
+msgstr ""
+
+#: builtin/branch.c:394
+#, c-format
+msgid "behind %d] "
+msgstr ""
+
+#: builtin/branch.c:396
+#, c-format
+msgid "ahead %d] "
+msgstr ""
+
+#: builtin/branch.c:398
+#, c-format
+msgid "ahead %d, behind %d] "
+msgstr ""
+
+#: builtin/branch.c:501
+msgid "(no branch)"
+msgstr ""
+
+#: builtin/branch.c:566
+msgid "some refs could not be read"
+msgstr ""
+
+#: builtin/branch.c:579
+msgid "cannot rename the current branch while not on any."
+msgstr ""
+
+#: builtin/branch.c:589
+#, c-format
+msgid "Invalid branch name: '%s'"
+msgstr ""
+
+#: builtin/branch.c:604
+msgid "Branch rename failed"
+msgstr ""
+
+#: builtin/branch.c:608
+#, c-format
+msgid "Renamed a misnamed branch '%s' away"
+msgstr ""
+
+#: builtin/branch.c:612
+#, c-format
+msgid "Branch renamed to %s, but HEAD is not updated!"
+msgstr ""
+
+#: builtin/branch.c:619
+msgid "Branch is renamed, but update of config-file failed"
+msgstr ""
+
+#: builtin/branch.c:634
+#, c-format
+msgid "malformed object name %s"
+msgstr ""
+
+#: builtin/branch.c:658
+#, c-format
+msgid "could not write branch description template: %s\n"
+msgstr ""
+
+#: builtin/branch.c:746
+msgid "Failed to resolve HEAD as a valid ref."
+msgstr ""
+
+#: builtin/branch.c:751 builtin/clone.c:558
+msgid "HEAD not found below refs/heads!"
+msgstr ""
+
+#: builtin/branch.c:809
+msgid "-a and -r options to 'git branch' do not make sense with a branch name"
+msgstr ""
+
+#: builtin/bundle.c:47
+#, c-format
+msgid "%s is okay\n"
+msgstr ""
+
+#: builtin/bundle.c:56
+msgid "Need a repository to create a bundle."
+msgstr ""
+
+#: builtin/bundle.c:60
+msgid "Need a repository to unbundle."
+msgstr ""
+
+#: builtin/checkout.c:113 builtin/checkout.c:146
+#, c-format
+msgid "path '%s' does not have our version"
+msgstr ""
+
+#: builtin/checkout.c:115 builtin/checkout.c:148
+#, c-format
+msgid "path '%s' does not have their version"
+msgstr ""
+
+#: builtin/checkout.c:131
+#, c-format
+msgid "path '%s' does not have all necessary versions"
+msgstr ""
+
+#: builtin/checkout.c:175
+#, c-format
+msgid "path '%s' does not have necessary versions"
+msgstr ""
+
+#: builtin/checkout.c:192
+#, c-format
+msgid "path '%s': cannot merge"
+msgstr ""
+
+#: builtin/checkout.c:209
+#, c-format
+msgid "Unable to add merge result for '%s'"
+msgstr ""
+
+#: builtin/checkout.c:212 builtin/reset.c:158
+#, c-format
+msgid "make_cache_entry failed for path '%s'"
+msgstr ""
+
+#: builtin/checkout.c:234 builtin/checkout.c:392
+msgid "corrupt index file"
+msgstr ""
+
+#: builtin/checkout.c:264 builtin/checkout.c:271
+#, c-format
+msgid "path '%s' is unmerged"
+msgstr ""
+
+#: builtin/checkout.c:302 builtin/checkout.c:498 builtin/clone.c:583
+#: builtin/merge.c:811
+msgid "unable to write new index file"
+msgstr ""
+
+#: builtin/checkout.c:319 builtin/diff.c:302 builtin/merge.c:408
+msgid "diff_setup_done failed"
+msgstr ""
+
+#: builtin/checkout.c:414
+msgid "you need to resolve your current index first"
+msgstr ""
+
+#: builtin/checkout.c:533
+#, c-format
+msgid "Can not do reflog for '%s'\n"
+msgstr ""
+
+#: builtin/checkout.c:565
+msgid "HEAD is now at"
+msgstr ""
+
+#: builtin/checkout.c:572
+#, c-format
+msgid "Reset branch '%s'\n"
+msgstr ""
+
+#: builtin/checkout.c:575
+#, c-format
+msgid "Already on '%s'\n"
+msgstr ""
+
+#: builtin/checkout.c:579
+#, c-format
+msgid "Switched to and reset branch '%s'\n"
+msgstr ""
+
+#: builtin/checkout.c:581
+#, c-format
+msgid "Switched to a new branch '%s'\n"
+msgstr ""
+
+#: builtin/checkout.c:583
+#, c-format
+msgid "Switched to branch '%s'\n"
+msgstr ""
+
+#: builtin/checkout.c:639
+#, c-format
+msgid " ... and %d more.\n"
+msgstr ""
+
+#. The singular version
+#: builtin/checkout.c:645
+#, c-format
+msgid ""
+"Warning: you are leaving %d commit behind, not connected to\n"
+"any of your branches:\n"
+"\n"
+"%s\n"
+msgid_plural ""
+"Warning: you are leaving %d commits behind, not connected to\n"
+"any of your branches:\n"
+"\n"
+"%s\n"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/checkout.c:663
+#, c-format
+msgid ""
+"If you want to keep them by creating a new branch, this may be a good time\n"
+"to do so with:\n"
+"\n"
+" git branch new_branch_name %s\n"
+"\n"
+msgstr ""
+
+#: builtin/checkout.c:692
+msgid "internal error in revision walk"
+msgstr ""
+
+#: builtin/checkout.c:696
+msgid "Previous HEAD position was"
+msgstr ""
+
+#: builtin/checkout.c:722
+msgid "You are on a branch yet to be born"
+msgstr ""
+
+#. case (1)
+#: builtin/checkout.c:853
+#, c-format
+msgid "invalid reference: %s"
+msgstr ""
+
+#. case (1): want a tree
+#: builtin/checkout.c:892
+#, c-format
+msgid "reference is not a tree: %s"
+msgstr ""
+
+#: builtin/checkout.c:972
+msgid "-B cannot be used with -b"
+msgstr ""
+
+#: builtin/checkout.c:981
+msgid "--patch is incompatible with all other options"
+msgstr ""
+
+#: builtin/checkout.c:984
+msgid "--detach cannot be used with -b/-B/--orphan"
+msgstr ""
+
+#: builtin/checkout.c:986
+msgid "--detach cannot be used with -t"
+msgstr ""
+
+#: builtin/checkout.c:992
+msgid "--track needs a branch name"
+msgstr ""
+
+#: builtin/checkout.c:999
+msgid "Missing branch name; try -b"
+msgstr ""
+
+#: builtin/checkout.c:1005
+msgid "--orphan and -b|-B are mutually exclusive"
+msgstr ""
+
+#: builtin/checkout.c:1007
+msgid "--orphan cannot be used with -t"
+msgstr ""
+
+#: builtin/checkout.c:1017
+msgid "git checkout: -f and -m are incompatible"
+msgstr ""
+
+#: builtin/checkout.c:1051
+msgid "invalid path specification"
+msgstr ""
+
+#: builtin/checkout.c:1059
+#, c-format
+msgid ""
+"git checkout: updating paths is incompatible with switching branches.\n"
+"Did you intend to checkout '%s' which can not be resolved as commit?"
+msgstr ""
+
+#: builtin/checkout.c:1061
+msgid "git checkout: updating paths is incompatible with switching branches."
+msgstr ""
+
+#: builtin/checkout.c:1066
+msgid "git checkout: --detach does not take a path argument"
+msgstr ""
+
+#: builtin/checkout.c:1069
+msgid ""
+"git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
+"checking out of the index."
+msgstr ""
+
+#: builtin/checkout.c:1088
+msgid "Cannot switch branch to a non-commit."
+msgstr ""
+
+#: builtin/checkout.c:1091
+msgid "--ours/--theirs is incompatible with switching branches."
+msgstr ""
+
+#: builtin/clean.c:78
+msgid "-x and -X cannot be used together"
+msgstr ""
+
+#: builtin/clean.c:82
+msgid ""
+"clean.requireForce set to true and neither -n nor -f given; refusing to clean"
+msgstr ""
+
+#: builtin/clean.c:85
+msgid ""
+"clean.requireForce defaults to true and neither -n nor -f given; refusing to "
+"clean"
+msgstr ""
+
+#: builtin/clean.c:155 builtin/clean.c:176
+#, c-format
+msgid "Would remove %s\n"
+msgstr ""
+
+#: builtin/clean.c:159 builtin/clean.c:179
+#, c-format
+msgid "Removing %s\n"
+msgstr ""
+
+#: builtin/clean.c:162 builtin/clean.c:182
+#, c-format
+msgid "failed to remove %s"
+msgstr ""
+
+#: builtin/clean.c:166
+#, c-format
+msgid "Would not remove %s\n"
+msgstr ""
+
+#: builtin/clean.c:168
+#, c-format
+msgid "Not removing %s\n"
+msgstr ""
+
+#: builtin/clone.c:243
+#, c-format
+msgid "reference repository '%s' is not a local directory."
+msgstr ""
+
+#: builtin/clone.c:302
+#, c-format
+msgid "failed to open '%s'"
+msgstr ""
+
+#: builtin/clone.c:306
+#, c-format
+msgid "failed to create directory '%s'"
+msgstr ""
+
+#: builtin/clone.c:308 builtin/diff.c:75
+#, c-format
+msgid "failed to stat '%s'"
+msgstr ""
+
+#: builtin/clone.c:310
+#, c-format
+msgid "%s exists and is not a directory"
+msgstr ""
+
+#: builtin/clone.c:324
+#, c-format
+msgid "failed to stat %s\n"
+msgstr ""
+
+#: builtin/clone.c:341
+#, c-format
+msgid "failed to unlink '%s'"
+msgstr ""
+
+#: builtin/clone.c:346
+#, c-format
+msgid "failed to create link '%s'"
+msgstr ""
+
+#: builtin/clone.c:350
+#, c-format
+msgid "failed to copy file to '%s'"
+msgstr ""
+
+#: builtin/clone.c:373
+#, c-format
+msgid "done.\n"
+msgstr ""
+
+#: builtin/clone.c:440
+#, c-format
+msgid "Could not find remote branch %s to clone."
+msgstr ""
+
+#: builtin/clone.c:549
+msgid "remote HEAD refers to nonexistent ref, unable to checkout.\n"
+msgstr ""
+
+#: builtin/clone.c:639
+msgid "Too many arguments."
+msgstr ""
+
+#: builtin/clone.c:643
+msgid "You must specify a repository to clone."
+msgstr ""
+
+#: builtin/clone.c:654
+#, c-format
+msgid "--bare and --origin %s options are incompatible."
+msgstr ""
+
+#: builtin/clone.c:668
+#, c-format
+msgid "repository '%s' does not exist"
+msgstr ""
+
+#: builtin/clone.c:673
+msgid "--depth is ignored in local clones; use file:// instead."
+msgstr ""
+
+#: builtin/clone.c:683
+#, c-format
+msgid "destination path '%s' already exists and is not an empty directory."
+msgstr ""
+
+#: builtin/clone.c:693
+#, c-format
+msgid "working tree '%s' already exists."
+msgstr ""
+
+#: builtin/clone.c:706 builtin/clone.c:720
+#, c-format
+msgid "could not create leading directories of '%s'"
+msgstr ""
+
+#: builtin/clone.c:709
+#, c-format
+msgid "could not create work tree dir '%s'."
+msgstr ""
+
+#: builtin/clone.c:728
+#, c-format
+msgid "Cloning into bare repository '%s'...\n"
+msgstr ""
+
+#: builtin/clone.c:730
+#, c-format
+msgid "Cloning into '%s'...\n"
+msgstr ""
+
+#: builtin/clone.c:786
+#, c-format
+msgid "Don't know how to clone %s"
+msgstr ""
+
+#: builtin/clone.c:835
+#, c-format
+msgid "Remote branch %s not found in upstream %s"
+msgstr ""
+
+#: builtin/clone.c:842
+msgid "You appear to have cloned an empty repository."
+msgstr ""
+
+#: builtin/commit.c:42
+msgid ""
+"Your name and email address were configured automatically based\n"
+"on your username and hostname. Please check that they are accurate.\n"
+"You can suppress this message by setting them explicitly:\n"
+"\n"
+"    git config --global user.name \"Your Name\"\n"
+"    git config --global user.email you@example.com\n"
+"\n"
+"After doing this, you may fix the identity used for this commit with:\n"
+"\n"
+"    git commit --amend --reset-author\n"
+msgstr ""
+
+#: builtin/commit.c:54
+msgid ""
+"You asked to amend the most recent commit, but doing so would make\n"
+"it empty. You can repeat your command with --allow-empty, or you can\n"
+"remove the commit entirely with \"git reset HEAD^\".\n"
+msgstr ""
+
+#: builtin/commit.c:59
+msgid ""
+"The previous cherry-pick is now empty, possibly due to conflict resolution.\n"
+"If you wish to commit it anyway, use:\n"
+"\n"
+"    git commit --allow-empty\n"
+"\n"
+"Otherwise, please use 'git reset'\n"
+msgstr ""
+
+#: builtin/commit.c:205 builtin/reset.c:33
+msgid "merge"
+msgstr ""
+
+#: builtin/commit.c:208
+msgid "cherry-pick"
+msgstr ""
+
+#: builtin/commit.c:325
+msgid "failed to unpack HEAD tree object"
+msgstr ""
+
+#: builtin/commit.c:367
+msgid "unable to create temporary index"
+msgstr ""
+
+#: builtin/commit.c:373
+msgid "interactive add failed"
+msgstr ""
+
+#: builtin/commit.c:406 builtin/commit.c:427 builtin/commit.c:473
+msgid "unable to write new_index file"
+msgstr ""
+
+#: builtin/commit.c:457
+#, c-format
+msgid "cannot do a partial commit during a %s."
+msgstr ""
+
+#: builtin/commit.c:466
+msgid "cannot read the index"
+msgstr ""
+
+#: builtin/commit.c:486
+msgid "unable to write temporary index file"
+msgstr ""
+
+#: builtin/commit.c:550 builtin/commit.c:556
+#, c-format
+msgid "invalid commit: %s"
+msgstr ""
+
+#: builtin/commit.c:579
+msgid "malformed --author parameter"
+msgstr ""
+
+#: builtin/commit.c:635
+#, c-format
+msgid "Malformed ident string: '%s'"
+msgstr ""
+
+#: builtin/commit.c:670 builtin/commit.c:703 builtin/commit.c:1000
+#, c-format
+msgid "could not lookup commit %s"
+msgstr ""
+
+#: builtin/commit.c:682 builtin/shortlog.c:296
+#, c-format
+msgid "(reading log message from standard input)\n"
+msgstr ""
+
+#: builtin/commit.c:684
+msgid "could not read log from standard input"
+msgstr ""
+
+#: builtin/commit.c:688
+#, c-format
+msgid "could not read log file '%s'"
+msgstr ""
+
+#: builtin/commit.c:694
+msgid "commit has empty message"
+msgstr ""
+
+#: builtin/commit.c:710
+msgid "could not read MERGE_MSG"
+msgstr ""
+
+#: builtin/commit.c:714
+msgid "could not read SQUASH_MSG"
+msgstr ""
+
+#: builtin/commit.c:718
+#, c-format
+msgid "could not read '%s'"
+msgstr ""
+
+#: builtin/commit.c:746
+#, c-format
+msgid "could not open '%s'"
+msgstr ""
+
+#: builtin/commit.c:770
+msgid "could not write commit template"
+msgstr ""
+
+#: builtin/commit.c:783
+#, c-format
+msgid ""
+"\n"
+"It looks like you may be committing a %s.\n"
+"If this is not correct, please remove the file\n"
+"\t%s\n"
+"and try again.\n"
+msgstr ""
+
+#: builtin/commit.c:796
+msgid "Please enter the commit message for your changes."
+msgstr ""
+
+#: builtin/commit.c:799
+msgid ""
+" Lines starting\n"
+"with '#' will be ignored, and an empty message aborts the commit.\n"
+msgstr ""
+
+#: builtin/commit.c:804
+msgid ""
+" Lines starting\n"
+"with '#' will be kept; you may remove them yourself if you want to.\n"
+"An empty message aborts the commit.\n"
+msgstr ""
+
+#: builtin/commit.c:816
+#, c-format
+msgid "%sAuthor:    %s"
+msgstr ""
+
+#: builtin/commit.c:823
+#, c-format
+msgid "%sCommitter: %s"
+msgstr ""
+
+#: builtin/commit.c:843
+msgid "Cannot read index"
+msgstr ""
+
+#: builtin/commit.c:880
+msgid "Error building trees"
+msgstr ""
+
+#: builtin/commit.c:895 builtin/tag.c:357
+#, c-format
+msgid "Please supply the message using either -m or -F option.\n"
+msgstr ""
+
+#: builtin/commit.c:975
+#, c-format
+msgid "No existing author found with '%s'"
+msgstr ""
+
+#: builtin/commit.c:990 builtin/commit.c:1182
+#, c-format
+msgid "Invalid untracked files mode '%s'"
+msgstr ""
+
+#: builtin/commit.c:1030
+msgid "Using both --reset-author and --author does not make sense"
+msgstr ""
+
+#: builtin/commit.c:1041
+msgid "You have nothing to amend."
+msgstr ""
+
+#: builtin/commit.c:1043
+#, c-format
+msgid "You are in the middle of a %s -- cannot amend."
+msgstr ""
+
+#: builtin/commit.c:1045
+msgid "Options --squash and --fixup cannot be used together"
+msgstr ""
+
+#: builtin/commit.c:1055
+msgid "Only one of -c/-C/-F/--fixup can be used."
+msgstr ""
+
+#: builtin/commit.c:1057
+msgid "Option -m cannot be combined with -c/-C/-F/--fixup."
+msgstr ""
+
+#: builtin/commit.c:1063
+msgid "--reset-author can be used only with -C, -c or --amend."
+msgstr ""
+
+#: builtin/commit.c:1080
+msgid "Only one of --include/--only/--all/--interactive/--patch can be used."
+msgstr ""
+
+#: builtin/commit.c:1082
+msgid "No paths with --include/--only does not make sense."
+msgstr ""
+
+#: builtin/commit.c:1084
+msgid "Clever... amending the last one with dirty index."
+msgstr ""
+
+#: builtin/commit.c:1086
+msgid "Explicit paths specified without -i nor -o; assuming --only paths..."
+msgstr ""
+
+#: builtin/commit.c:1096 builtin/tag.c:556
+#, c-format
+msgid "Invalid cleanup mode %s"
+msgstr ""
+
+#: builtin/commit.c:1101
+msgid "Paths with -a does not make sense."
+msgstr ""
+
+#: builtin/commit.c:1280
+msgid "couldn't look up newly created commit"
+msgstr ""
+
+#: builtin/commit.c:1282
+msgid "could not parse newly created commit"
+msgstr ""
+
+#: builtin/commit.c:1323
+msgid "detached HEAD"
+msgstr ""
+
+#: builtin/commit.c:1325
+msgid " (root-commit)"
+msgstr ""
+
+#: builtin/commit.c:1415
+msgid "could not parse HEAD commit"
+msgstr ""
+
+#: builtin/commit.c:1452 builtin/merge.c:509
+#, c-format
+msgid "could not open '%s' for reading"
+msgstr ""
+
+#: builtin/commit.c:1459
+#, c-format
+msgid "Corrupt MERGE_HEAD file (%s)"
+msgstr ""
+
+#: builtin/commit.c:1466
+msgid "could not read MERGE_MODE"
+msgstr ""
+
+#: builtin/commit.c:1485
+#, c-format
+msgid "could not read commit message: %s"
+msgstr ""
+
+#: builtin/commit.c:1499
+#, c-format
+msgid "Aborting commit due to empty commit message.\n"
+msgstr ""
+
+#: builtin/commit.c:1514 builtin/merge.c:935 builtin/merge.c:968
+msgid "failed to write commit object"
+msgstr ""
+
+#: builtin/commit.c:1535
+msgid "cannot lock HEAD ref"
+msgstr ""
+
+#: builtin/commit.c:1539
+msgid "cannot update HEAD ref"
+msgstr ""
+
+#: builtin/commit.c:1550
+msgid ""
+"Repository has been updated, but unable to write\n"
+"new_index file. Check that disk is not full or quota is\n"
+"not exceeded, and then \"git reset HEAD\" to recover."
+msgstr ""
+
+#: builtin/describe.c:234
+#, c-format
+msgid "annotated tag %s not available"
+msgstr ""
+
+#: builtin/describe.c:238
+#, c-format
+msgid "annotated tag %s has no embedded name"
+msgstr ""
+
+#: builtin/describe.c:240
+#, c-format
+msgid "tag '%s' is really '%s' here"
+msgstr ""
+
+#: builtin/describe.c:267
+#, c-format
+msgid "Not a valid object name %s"
+msgstr ""
+
+#: builtin/describe.c:270
+#, c-format
+msgid "%s is not a valid '%s' object"
+msgstr ""
+
+#: builtin/describe.c:287
+#, c-format
+msgid "no tag exactly matches '%s'"
+msgstr ""
+
+#: builtin/describe.c:289
+#, c-format
+msgid "searching to describe %s\n"
+msgstr ""
+
+#: builtin/describe.c:329
+#, c-format
+msgid "finished search at %s\n"
+msgstr ""
+
+#: builtin/describe.c:353
+#, c-format
+msgid ""
+"No annotated tags can describe '%s'.\n"
+"However, there were unannotated tags: try --tags."
+msgstr ""
+
+#: builtin/describe.c:357
+#, c-format
+msgid ""
+"No tags can describe '%s'.\n"
+"Try --always, or create some tags."
+msgstr ""
+
+#: builtin/describe.c:378
+#, c-format
+msgid "traversed %lu commits\n"
+msgstr ""
+
+#: builtin/describe.c:381
+#, c-format
+msgid ""
+"more than %i tags found; listed %i most recent\n"
+"gave up search at %s\n"
+msgstr ""
+
+#: builtin/describe.c:436
+msgid "--long is incompatible with --abbrev=0"
+msgstr ""
+
+#: builtin/describe.c:462
+msgid "No names found, cannot describe anything."
+msgstr ""
+
+#: builtin/describe.c:482
+msgid "--dirty is incompatible with committishes"
+msgstr ""
+
+#: builtin/diff.c:77
+#, c-format
+msgid "'%s': not a regular file or symlink"
+msgstr ""
+
+#: builtin/diff.c:220
+#, c-format
+msgid "invalid option: %s"
+msgstr ""
+
+#: builtin/diff.c:297
+msgid "Not a git repository"
+msgstr ""
+
+#: builtin/diff.c:347
+#, c-format
+msgid "invalid object '%s' given."
+msgstr ""
+
+#: builtin/diff.c:352
+#, c-format
+msgid "more than %d trees given: '%s'"
+msgstr ""
+
+#: builtin/diff.c:362
+#, c-format
+msgid "more than two blobs given: '%s'"
+msgstr ""
+
+#: builtin/diff.c:370
+#, c-format
+msgid "unhandled object '%s' given."
+msgstr ""
+
+#: builtin/fetch.c:200
+msgid "Couldn't find remote ref HEAD"
+msgstr ""
+
+#: builtin/fetch.c:252
+#, c-format
+msgid "object %s not found"
+msgstr ""
+
+#: builtin/fetch.c:258
+msgid "[up to date]"
+msgstr ""
+
+#: builtin/fetch.c:272
+#, c-format
+msgid "! %-*s %-*s -> %s  (can't fetch in current branch)"
+msgstr ""
+
+#: builtin/fetch.c:273 builtin/fetch.c:351
+msgid "[rejected]"
+msgstr ""
+
+#: builtin/fetch.c:284
+msgid "[tag update]"
+msgstr ""
+
+#: builtin/fetch.c:286 builtin/fetch.c:313 builtin/fetch.c:331
+msgid "  (unable to update local ref)"
+msgstr ""
+
+#: builtin/fetch.c:298
+msgid "[new tag]"
+msgstr ""
+
+#: builtin/fetch.c:302
+msgid "[new branch]"
+msgstr ""
+
+#: builtin/fetch.c:347
+msgid "unable to update local ref"
+msgstr ""
+
+#: builtin/fetch.c:347
+msgid "forced update"
+msgstr ""
+
+#: builtin/fetch.c:353
+msgid "(non-fast-forward)"
+msgstr ""
+
+#: builtin/fetch.c:384 builtin/fetch.c:676
+#, c-format
+msgid "cannot open %s: %s\n"
+msgstr ""
+
+#: builtin/fetch.c:393
+#, c-format
+msgid "%s did not send all necessary objects\n"
+msgstr ""
+
+#: builtin/fetch.c:479
+#, c-format
+msgid "From %.*s\n"
+msgstr ""
+
+#: builtin/fetch.c:490
+#, c-format
+msgid ""
+"some local refs could not be updated; try running\n"
+" 'git remote prune %s' to remove any old, conflicting branches"
+msgstr ""
+
+#: builtin/fetch.c:540
+#, c-format
+msgid "   (%s will become dangling)\n"
+msgstr ""
+
+#: builtin/fetch.c:541
+#, c-format
+msgid "   (%s has become dangling)\n"
+msgstr ""
+
+#: builtin/fetch.c:548
+msgid "[deleted]"
+msgstr ""
+
+#: builtin/fetch.c:549
+msgid "(none)"
+msgstr ""
+
+#: builtin/fetch.c:666
+#, c-format
+msgid "Refusing to fetch into current branch %s of non-bare repository"
+msgstr ""
+
+#: builtin/fetch.c:700
+#, c-format
+msgid "Don't know how to fetch from %s"
+msgstr ""
+
+#: builtin/fetch.c:777
+#, c-format
+msgid "Option \"%s\" value \"%s\" is not valid for %s"
+msgstr ""
+
+#: builtin/fetch.c:780
+#, c-format
+msgid "Option \"%s\" is ignored for %s\n"
+msgstr ""
+
+#: builtin/fetch.c:879
+#, c-format
+msgid "Fetching %s\n"
+msgstr ""
+
+#: builtin/fetch.c:881
+#, c-format
+msgid "Could not fetch %s"
+msgstr ""
+
+#: builtin/fetch.c:898
+msgid ""
+"No remote repository specified.  Please, specify either a URL or a\n"
+"remote name from which new revisions should be fetched."
+msgstr ""
+
+#: builtin/fetch.c:918
+msgid "You need to specify a tag name."
+msgstr ""
+
+#: builtin/fetch.c:970
+msgid "fetch --all does not take a repository argument"
+msgstr ""
+
+#: builtin/fetch.c:972
+msgid "fetch --all does not make sense with refspecs"
+msgstr ""
+
+#: builtin/fetch.c:983
+#, c-format
+msgid "No such remote or remote group: %s"
+msgstr ""
+
+#: builtin/fetch.c:991
+msgid "Fetching a group and specifying refspecs does not make sense"
+msgstr ""
+
+#: builtin/gc.c:63
+#, c-format
+msgid "Invalid %s: '%s'"
+msgstr ""
+
+#: builtin/gc.c:78
+msgid "Too many options specified"
+msgstr ""
+
+#: builtin/gc.c:103
+#, c-format
+msgid "insanely long object directory %.*s"
+msgstr ""
+
+#: builtin/gc.c:223
+#, c-format
+msgid "Auto packing the repository for optimum performance.\n"
+msgstr ""
+
+#: builtin/gc.c:226
+#, c-format
+msgid ""
+"Auto packing the repository for optimum performance. You may also\n"
+"run \"git gc\" manually. See \"git help gc\" for more information.\n"
+msgstr ""
+
+#: builtin/gc.c:256
+msgid ""
+"There are too many unreachable loose objects; run 'git prune' to remove them."
+msgstr ""
+
+#: builtin/grep.c:216
+#, c-format
+msgid "grep: failed to create thread: %s"
+msgstr ""
+
+#: builtin/grep.c:402
+#, c-format
+msgid "Failed to chdir: %s"
+msgstr ""
+
+#: builtin/grep.c:478 builtin/grep.c:512
+#, c-format
+msgid "unable to read tree (%s)"
+msgstr ""
+
+#: builtin/grep.c:526
+#, c-format
+msgid "unable to grep from object of type %s"
+msgstr ""
+
+#: builtin/grep.c:584
+#, c-format
+msgid "switch `%c' expects a numerical value"
+msgstr ""
+
+#: builtin/grep.c:601
+#, c-format
+msgid "cannot open '%s'"
+msgstr ""
+
+#: builtin/grep.c:888
+msgid "no pattern given."
+msgstr ""
+
+#: builtin/grep.c:902
+#, c-format
+msgid "bad object %s"
+msgstr ""
+
+#: builtin/grep.c:943
+msgid "--open-files-in-pager only works on the worktree"
+msgstr ""
+
+#: builtin/grep.c:966
+msgid "--cached or --untracked cannot be used with --no-index."
+msgstr ""
+
+#: builtin/grep.c:971
+msgid "--no-index or --untracked cannot be used with revs."
+msgstr ""
+
+#: builtin/grep.c:974
+msgid "--[no-]exclude-standard cannot be used for tracked contents."
+msgstr ""
+
+#: builtin/grep.c:982
+msgid "both --cached and trees are given."
+msgstr ""
+
+#: builtin/init-db.c:35
+#, c-format
+msgid "Could not make %s writable by group"
+msgstr ""
+
+#: builtin/init-db.c:62
+#, c-format
+msgid "insanely long template name %s"
+msgstr ""
+
+#: builtin/init-db.c:67
+#, c-format
+msgid "cannot stat '%s'"
+msgstr ""
+
+#: builtin/init-db.c:73
+#, c-format
+msgid "cannot stat template '%s'"
+msgstr ""
+
+#: builtin/init-db.c:80
+#, c-format
+msgid "cannot opendir '%s'"
+msgstr ""
+
+#: builtin/init-db.c:97
+#, c-format
+msgid "cannot readlink '%s'"
+msgstr ""
+
+#: builtin/init-db.c:99
+#, c-format
+msgid "insanely long symlink %s"
+msgstr ""
+
+#: builtin/init-db.c:102
+#, c-format
+msgid "cannot symlink '%s' '%s'"
+msgstr ""
+
+#: builtin/init-db.c:106
+#, c-format
+msgid "cannot copy '%s' to '%s'"
+msgstr ""
+
+#: builtin/init-db.c:110
+#, c-format
+msgid "ignoring template %s"
+msgstr ""
+
+#: builtin/init-db.c:133
+#, c-format
+msgid "insanely long template path %s"
+msgstr ""
+
+#: builtin/init-db.c:141
+#, c-format
+msgid "templates not found %s"
+msgstr ""
+
+#: builtin/init-db.c:154
+#, c-format
+msgid "not copying templates of a wrong format version %d from '%s'"
+msgstr ""
+
+#: builtin/init-db.c:192
+#, c-format
+msgid "insane git directory %s"
+msgstr ""
+
+#: builtin/init-db.c:322 builtin/init-db.c:325
+#, c-format
+msgid "%s already exists"
+msgstr ""
+
+#: builtin/init-db.c:354
+#, c-format
+msgid "unable to handle file type %d"
+msgstr ""
+
+#: builtin/init-db.c:357
+#, c-format
+msgid "unable to move %s to %s"
+msgstr ""
+
+#: builtin/init-db.c:362
+#, c-format
+msgid "Could not create git link %s"
+msgstr ""
+
+#.
+#. * TRANSLATORS: The first '%s' is either "Reinitialized
+#. * existing" or "Initialized empty", the second " shared" or
+#. * "", and the last '%s%s' is the verbatim directory name.
+#.
+#: builtin/init-db.c:419
+#, c-format
+msgid "%s%s Git repository in %s%s\n"
+msgstr ""
+
+#: builtin/init-db.c:420
+msgid "Reinitialized existing"
+msgstr ""
+
+#: builtin/init-db.c:420
+msgid "Initialized empty"
+msgstr ""
+
+#: builtin/init-db.c:421
+msgid " shared"
+msgstr ""
+
+#: builtin/init-db.c:440
+msgid "cannot tell cwd"
+msgstr ""
+
+#: builtin/init-db.c:521 builtin/init-db.c:528
+#, c-format
+msgid "cannot mkdir %s"
+msgstr ""
+
+#: builtin/init-db.c:532
+#, c-format
+msgid "cannot chdir to %s"
+msgstr ""
+
+#: builtin/init-db.c:554
+#, c-format
+msgid ""
+"%s (or --work-tree=<directory>) not allowed without specifying %s (or --git-"
+"dir=<directory>)"
+msgstr ""
+
+#: builtin/init-db.c:578
+msgid "Cannot access current working directory"
+msgstr ""
+
+#: builtin/init-db.c:585
+#, c-format
+msgid "Cannot access work tree '%s'"
+msgstr ""
+
+#: builtin/log.c:187
+#, c-format
+msgid "Final output: %d %s\n"
+msgstr ""
+
+#: builtin/log.c:395 builtin/log.c:483
+#, c-format
+msgid "Could not read object %s"
+msgstr ""
+
+#: builtin/log.c:507
+#, c-format
+msgid "Unknown type: %d"
+msgstr ""
+
+#: builtin/log.c:596
+msgid "format.headers without value"
+msgstr ""
+
+#: builtin/log.c:669
+msgid "name of output directory is too long"
+msgstr ""
+
+#: builtin/log.c:680
+#, c-format
+msgid "Cannot open patch file %s"
+msgstr ""
+
+#: builtin/log.c:694
+msgid "Need exactly one range."
+msgstr ""
+
+#: builtin/log.c:702
+msgid "Not a range."
+msgstr ""
+
+#: builtin/log.c:739
+msgid "Could not extract email from committer identity."
+msgstr ""
+
+#: builtin/log.c:785
+msgid "Cover letter needs email format"
+msgstr ""
+
+#: builtin/log.c:879
+#, c-format
+msgid "insane in-reply-to: %s"
+msgstr ""
+
+#: builtin/log.c:952
+msgid "Two output directories?"
+msgstr ""
+
+#: builtin/log.c:1173
+#, c-format
+msgid "bogus committer info %s"
+msgstr ""
+
+#: builtin/log.c:1218
+msgid "-n and -k are mutually exclusive."
+msgstr ""
+
+#: builtin/log.c:1220
+msgid "--subject-prefix and -k are mutually exclusive."
+msgstr ""
+
+#: builtin/log.c:1225 builtin/shortlog.c:284
+#, c-format
+msgid "unrecognized argument: %s"
+msgstr ""
+
+#: builtin/log.c:1228
+msgid "--name-only does not make sense"
+msgstr ""
+
+#: builtin/log.c:1230
+msgid "--name-status does not make sense"
+msgstr ""
+
+#: builtin/log.c:1232
+msgid "--check does not make sense"
+msgstr ""
+
+#: builtin/log.c:1255
+msgid "standard output, or directory, which one?"
+msgstr ""
+
+#: builtin/log.c:1257
+#, c-format
+msgid "Could not create directory '%s'"
+msgstr ""
+
+#: builtin/log.c:1410
+msgid "Failed to create output files"
+msgstr ""
+
+#: builtin/log.c:1514
+#, c-format
+msgid ""
+"Could not find a tracked remote branch, please specify <upstream> manually.\n"
+msgstr ""
+
+#: builtin/log.c:1530 builtin/log.c:1532 builtin/log.c:1544
+#, c-format
+msgid "Unknown commit %s"
+msgstr ""
+
+#: builtin/merge.c:91
+msgid "switch `m' requires a value"
+msgstr ""
+
+#: builtin/merge.c:128
+#, c-format
+msgid "Could not find merge strategy '%s'.\n"
+msgstr ""
+
+#: builtin/merge.c:129
+#, c-format
+msgid "Available strategies are:"
+msgstr ""
+
+#: builtin/merge.c:134
+#, c-format
+msgid "Available custom strategies are:"
+msgstr ""
+
+#: builtin/merge.c:241
+msgid "could not run stash."
+msgstr ""
+
+#: builtin/merge.c:246
+msgid "stash failed"
+msgstr ""
+
+#: builtin/merge.c:251
+#, c-format
+msgid "not a valid object: %s"
+msgstr ""
+
+#: builtin/merge.c:270 builtin/merge.c:287
+msgid "read-tree failed"
+msgstr ""
+
+#: builtin/merge.c:317
+msgid " (nothing to squash)"
+msgstr ""
+
+#: builtin/merge.c:330
+#, c-format
+msgid "Squash commit -- not updating HEAD\n"
+msgstr ""
+
+#: builtin/merge.c:362
+msgid "Writing SQUASH_MSG"
+msgstr ""
+
+#: builtin/merge.c:364
+msgid "Finishing SQUASH_MSG"
+msgstr ""
+
+#: builtin/merge.c:386
+#, c-format
+msgid "No merge message -- not updating HEAD\n"
+msgstr ""
+
+#: builtin/merge.c:437
+#, c-format
+msgid "'%s' does not point to a commit"
+msgstr ""
+
+#: builtin/merge.c:536
+#, c-format
+msgid "Bad branch.%s.mergeoptions string: %s"
+msgstr ""
+
+#: builtin/merge.c:629
+msgid "git write-tree failed to write a tree"
+msgstr ""
+
+#: builtin/merge.c:679
+msgid "failed to read the cache"
+msgstr ""
+
+#: builtin/merge.c:696
+msgid "Unable to write index."
+msgstr ""
+
+#: builtin/merge.c:709
+msgid "Not handling anything other than two heads merge."
+msgstr ""
+
+#: builtin/merge.c:723
+#, c-format
+msgid "Unknown option for merge-recursive: -X%s"
+msgstr ""
+
+#: builtin/merge.c:737
+#, c-format
+msgid "unable to write %s"
+msgstr ""
+
+#: builtin/merge.c:876
+#, c-format
+msgid "Could not read from '%s'"
+msgstr ""
+
+#: builtin/merge.c:885
+#, c-format
+msgid "Not committing merge; use 'git commit' to complete the merge.\n"
+msgstr ""
+
+#: builtin/merge.c:891
+msgid ""
+"Please enter a commit message to explain why this merge is necessary,\n"
+"especially if it merges an updated upstream into a topic branch.\n"
+"\n"
+"Lines starting with '#' will be ignored, and an empty message aborts\n"
+"the commit.\n"
+msgstr ""
+
+#: builtin/merge.c:915
+msgid "Empty commit message."
+msgstr ""
+
+#: builtin/merge.c:927
+#, c-format
+msgid "Wonderful.\n"
+msgstr ""
+
+#: builtin/merge.c:1000
+#, c-format
+msgid "Automatic merge failed; fix conflicts and then commit the result.\n"
+msgstr ""
+
+#: builtin/merge.c:1016
+#, c-format
+msgid "'%s' is not a commit"
+msgstr ""
+
+#: builtin/merge.c:1057
+msgid "No current branch."
+msgstr ""
+
+#: builtin/merge.c:1059
+msgid "No remote for the current branch."
+msgstr ""
+
+#: builtin/merge.c:1061
+msgid "No default upstream defined for the current branch."
+msgstr ""
+
+#: builtin/merge.c:1066
+#, c-format
+msgid "No remote tracking branch for %s from %s"
+msgstr ""
+
+#: builtin/merge.c:1188
+msgid "There is no merge to abort (MERGE_HEAD missing)."
+msgstr ""
+
+#: builtin/merge.c:1204 git-pull.sh:31
+msgid ""
+"You have not concluded your merge (MERGE_HEAD exists).\n"
+"Please, commit your changes before you can merge."
+msgstr ""
+
+#: builtin/merge.c:1207 git-pull.sh:34
+msgid "You have not concluded your merge (MERGE_HEAD exists)."
+msgstr ""
+
+#: builtin/merge.c:1211
+msgid ""
+"You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
+"Please, commit your changes before you can merge."
+msgstr ""
+
+#: builtin/merge.c:1214
+msgid "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."
+msgstr ""
+
+#: builtin/merge.c:1223
+msgid "You cannot combine --squash with --no-ff."
+msgstr ""
+
+#: builtin/merge.c:1228
+msgid "You cannot combine --no-ff with --ff-only."
+msgstr ""
+
+#: builtin/merge.c:1235
+msgid "No commit specified and merge.defaultToUpstream not set."
+msgstr ""
+
+#: builtin/merge.c:1266
+msgid "Can merge only exactly one commit into empty head"
+msgstr ""
+
+#: builtin/merge.c:1269
+msgid "Squash commit into empty head not supported yet"
+msgstr ""
+
+#: builtin/merge.c:1271
+msgid "Non-fast-forward commit does not make sense into an empty head"
+msgstr ""
+
+#: builtin/merge.c:1275 builtin/merge.c:1319
+#, c-format
+msgid "%s - not something we can merge"
+msgstr ""
+
+#: builtin/merge.c:1385
+#, c-format
+msgid "Updating %s..%s\n"
+msgstr ""
+
+#: builtin/merge.c:1423
+#, c-format
+msgid "Trying really trivial in-index merge...\n"
+msgstr ""
+
+#: builtin/merge.c:1430
+#, c-format
+msgid "Nope.\n"
+msgstr ""
+
+#: builtin/merge.c:1462
+msgid "Not possible to fast-forward, aborting."
+msgstr ""
+
+#: builtin/merge.c:1485 builtin/merge.c:1562
+#, c-format
+msgid "Rewinding the tree to pristine...\n"
+msgstr ""
+
+#: builtin/merge.c:1489
+#, c-format
+msgid "Trying merge strategy %s...\n"
+msgstr ""
+
+#: builtin/merge.c:1553
+#, c-format
+msgid "No merge strategy handled the merge.\n"
+msgstr ""
+
+#: builtin/merge.c:1555
+#, c-format
+msgid "Merge with strategy %s failed.\n"
+msgstr ""
+
+#: builtin/merge.c:1564
+#, c-format
+msgid "Using the %s to prepare resolving by hand.\n"
+msgstr ""
+
+#: builtin/merge.c:1575
+#, c-format
+msgid "Automatic merge went well; stopped before committing as requested\n"
+msgstr ""
+
+#: builtin/mv.c:108
+#, c-format
+msgid "Checking rename of '%s' to '%s'\n"
+msgstr ""
+
+#: builtin/mv.c:112
+msgid "bad source"
+msgstr ""
+
+#: builtin/mv.c:115
+msgid "can not move directory into itself"
+msgstr ""
+
+#: builtin/mv.c:118
+msgid "cannot move directory over file"
+msgstr ""
+
+#: builtin/mv.c:128
+#, c-format
+msgid "Huh? %.*s is in index?"
+msgstr ""
+
+#: builtin/mv.c:140
+msgid "source directory is empty"
+msgstr ""
+
+#: builtin/mv.c:171
+msgid "not under version control"
+msgstr ""
+
+#: builtin/mv.c:173
+msgid "destination exists"
+msgstr ""
+
+#: builtin/mv.c:181
+#, c-format
+msgid "overwriting '%s'"
+msgstr ""
+
+#: builtin/mv.c:184
+msgid "Cannot overwrite"
+msgstr ""
+
+#: builtin/mv.c:187
+msgid "multiple sources for the same target"
+msgstr ""
+
+#: builtin/mv.c:202
+#, c-format
+msgid "%s, source=%s, destination=%s"
+msgstr ""
+
+#: builtin/mv.c:212
+#, c-format
+msgid "Renaming %s to %s\n"
+msgstr ""
+
+#: builtin/mv.c:215
+#, c-format
+msgid "renaming '%s' failed"
+msgstr ""
+
+#: builtin/notes.c:139
+#, c-format
+msgid "unable to start 'show' for object '%s'"
+msgstr ""
+
+#: builtin/notes.c:145
+msgid "can't fdopen 'show' output fd"
+msgstr ""
+
+#: builtin/notes.c:155
+#, c-format
+msgid "failed to close pipe to 'show' for object '%s'"
+msgstr ""
+
+#: builtin/notes.c:158
+#, c-format
+msgid "failed to finish 'show' for object '%s'"
+msgstr ""
+
+#: builtin/notes.c:175 builtin/tag.c:343
+#, c-format
+msgid "could not create file '%s'"
+msgstr ""
+
+#: builtin/notes.c:189
+msgid "Please supply the note contents using either -m or -F option"
+msgstr ""
+
+#: builtin/notes.c:210 builtin/notes.c:973
+#, c-format
+msgid "Removing note for object %s\n"
+msgstr ""
+
+#: builtin/notes.c:215
+msgid "unable to write note object"
+msgstr ""
+
+#: builtin/notes.c:217
+#, c-format
+msgid "The note contents has been left in %s"
+msgstr ""
+
+#: builtin/notes.c:251 builtin/tag.c:521
+#, c-format
+msgid "cannot read '%s'"
+msgstr ""
+
+#: builtin/notes.c:253 builtin/tag.c:524
+#, c-format
+msgid "could not open or read '%s'"
+msgstr ""
+
+#: builtin/notes.c:272 builtin/notes.c:445 builtin/notes.c:447
+#: builtin/notes.c:507 builtin/notes.c:561 builtin/notes.c:644
+#: builtin/notes.c:649 builtin/notes.c:724 builtin/notes.c:766
+#: builtin/notes.c:968 builtin/reset.c:293 builtin/tag.c:537
+#, c-format
+msgid "Failed to resolve '%s' as a valid ref."
+msgstr ""
+
+#: builtin/notes.c:275
+#, c-format
+msgid "Failed to read object '%s'."
+msgstr ""
+
+#: builtin/notes.c:299
+msgid "Cannot commit uninitialized/unreferenced notes tree"
+msgstr ""
+
+#: builtin/notes.c:340
+#, c-format
+msgid "Bad notes.rewriteMode value: '%s'"
+msgstr ""
+
+#: builtin/notes.c:350
+#, c-format
+msgid "Refusing to rewrite notes in %s (outside of refs/notes/)"
+msgstr ""
+
+#. TRANSLATORS: The first %s is the name of the
+#. environment variable, the second %s is its value
+#: builtin/notes.c:377
+#, c-format
+msgid "Bad %s value: '%s'"
+msgstr ""
+
+#: builtin/notes.c:441
+#, c-format
+msgid "Malformed input line: '%s'."
+msgstr ""
+
+#: builtin/notes.c:456
+#, c-format
+msgid "Failed to copy notes from '%s' to '%s'"
+msgstr ""
+
+#: builtin/notes.c:500 builtin/notes.c:554 builtin/notes.c:627
+#: builtin/notes.c:639 builtin/notes.c:712 builtin/notes.c:759
+#: builtin/notes.c:1033
+msgid "too many parameters"
+msgstr ""
+
+#: builtin/notes.c:513 builtin/notes.c:772
+#, c-format
+msgid "No note found for object %s."
+msgstr ""
+
+#: builtin/notes.c:580
+#, c-format
+msgid ""
+"Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite "
+"existing notes"
+msgstr ""
+
+#: builtin/notes.c:585 builtin/notes.c:662
+#, c-format
+msgid "Overwriting existing notes for object %s\n"
+msgstr ""
+
+#: builtin/notes.c:635
+msgid "too few parameters"
+msgstr ""
+
+#: builtin/notes.c:656
+#, c-format
+msgid ""
+"Cannot copy notes. Found existing notes for object %s. Use '-f' to overwrite "
+"existing notes"
+msgstr ""
+
+#: builtin/notes.c:668
+#, c-format
+msgid "Missing notes on source object %s. Cannot copy."
+msgstr ""
+
+#: builtin/notes.c:717
+#, c-format
+msgid ""
+"The -m/-F/-c/-C options have been deprecated for the 'edit' subcommand.\n"
+"Please use 'git notes add -f -m/-F/-c/-C' instead.\n"
+msgstr ""
+
+#: builtin/notes.c:971
+#, c-format
+msgid "Object %s has no note\n"
+msgstr ""
+
+#: builtin/notes.c:1103
+#, c-format
+msgid "Unknown subcommand: %s"
+msgstr ""
+
+#: builtin/pack-objects.c:2310
+#, c-format
+msgid "unsupported index version %s"
+msgstr ""
+
+#: builtin/pack-objects.c:2314
+#, c-format
+msgid "bad index version '%s'"
+msgstr ""
+
+#: builtin/pack-objects.c:2322
+#, c-format
+msgid "option %s does not accept negative form"
+msgstr ""
+
+#: builtin/pack-objects.c:2326
+#, c-format
+msgid "unable to parse value '%s' for option %s"
+msgstr ""
+
+#: builtin/push.c:44
+msgid "tag shorthand without <tag>"
+msgstr ""
+
+#: builtin/push.c:63
+msgid "--delete only accepts plain target ref names"
+msgstr ""
+
+#: builtin/push.c:73
+#, c-format
+msgid ""
+"You are not currently on a branch.\n"
+"To push the history leading to the current (detached HEAD)\n"
+"state now, use\n"
+"\n"
+"    git push %s HEAD:<name-of-remote-branch>\n"
+msgstr ""
+
+#: builtin/push.c:80
+#, c-format
+msgid ""
+"The current branch %s has no upstream branch.\n"
+"To push the current branch and set the remote as upstream, use\n"
+"\n"
+"    git push --set-upstream %s %s\n"
+msgstr ""
+
+#: builtin/push.c:88
+#, c-format
+msgid "The current branch %s has multiple upstream branches, refusing to push."
+msgstr ""
+
+#: builtin/push.c:111
+msgid ""
+"You didn't specify any refspecs to push, and push.default is \"nothing\"."
+msgstr ""
+
+#: builtin/push.c:131
+#, c-format
+msgid "Pushing to %s\n"
+msgstr ""
+
+#: builtin/push.c:135
+#, c-format
+msgid "failed to push some refs to '%s'"
+msgstr ""
+
+#: builtin/push.c:143
+#, c-format
+msgid ""
+"To prevent you from losing history, non-fast-forward updates were rejected\n"
+"Merge the remote changes (e.g. 'git pull') before pushing again.  See the\n"
+"'Note about fast-forwards' section of 'git push --help' for details.\n"
+msgstr ""
+
+#: builtin/push.c:160
+#, c-format
+msgid "bad repository '%s'"
+msgstr ""
+
+#: builtin/push.c:161
+msgid ""
+"No configured push destination.\n"
+"Either specify the URL from the command-line or configure a remote "
+"repository using\n"
+"\n"
+"    git remote add <name> <url>\n"
+"\n"
+"and then push using the remote name\n"
+"\n"
+"    git push <name>\n"
+msgstr ""
+
+#: builtin/push.c:176
+msgid "--all and --tags are incompatible"
+msgstr ""
+
+#: builtin/push.c:177
+msgid "--all can't be combined with refspecs"
+msgstr ""
+
+#: builtin/push.c:182
+msgid "--mirror and --tags are incompatible"
+msgstr ""
+
+#: builtin/push.c:183
+msgid "--mirror can't be combined with refspecs"
+msgstr ""
+
+#: builtin/push.c:188
+msgid "--all and --mirror are incompatible"
+msgstr ""
+
+#: builtin/push.c:274
+msgid "--delete is incompatible with --all, --mirror and --tags"
+msgstr ""
+
+#: builtin/push.c:276
+msgid "--delete doesn't make sense without any refs"
+msgstr ""
+
+#: builtin/reset.c:33
+msgid "mixed"
+msgstr ""
+
+#: builtin/reset.c:33
+msgid "soft"
+msgstr ""
+
+#: builtin/reset.c:33
+msgid "hard"
+msgstr ""
+
+#: builtin/reset.c:33
+msgid "keep"
+msgstr ""
+
+#: builtin/reset.c:77
+msgid "You do not have a valid HEAD."
+msgstr ""
+
+#: builtin/reset.c:79
+msgid "Failed to find tree of HEAD."
+msgstr ""
+
+#: builtin/reset.c:85
+#, c-format
+msgid "Failed to find tree of %s."
+msgstr ""
+
+#: builtin/reset.c:96
+msgid "Could not write new index file."
+msgstr ""
+
+#: builtin/reset.c:106
+#, c-format
+msgid "HEAD is now at %s"
+msgstr ""
+
+#: builtin/reset.c:130
+msgid "Could not read index"
+msgstr ""
+
+#: builtin/reset.c:133
+msgid "Unstaged changes after reset:"
+msgstr ""
+
+#: builtin/reset.c:223
+#, c-format
+msgid "Cannot do a %s reset in the middle of a merge."
+msgstr ""
+
+#: builtin/reset.c:297
+#, c-format
+msgid "Could not parse object '%s'."
+msgstr ""
+
+#: builtin/reset.c:302
+msgid "--patch is incompatible with --{hard,mixed,soft}"
+msgstr ""
+
+#: builtin/reset.c:311
+msgid "--mixed with paths is deprecated; use 'git reset -- <paths>' instead."
+msgstr ""
+
+#: builtin/reset.c:313
+#, c-format
+msgid "Cannot do %s reset with paths."
+msgstr ""
+
+#: builtin/reset.c:325
+#, c-format
+msgid "%s reset is not allowed in a bare repository"
+msgstr ""
+
+#: builtin/reset.c:341
+#, c-format
+msgid "Could not reset index file to revision '%s'."
+msgstr ""
+
+#: builtin/revert.c:70 builtin/revert.c:91
+#, c-format
+msgid "%s: %s cannot be used with %s"
+msgstr ""
+
+#: builtin/revert.c:126
+msgid "program error"
+msgstr ""
+
+#: builtin/revert.c:209
+msgid "revert failed"
+msgstr ""
+
+#: builtin/revert.c:224
+msgid "cherry-pick failed"
+msgstr ""
+
+#: builtin/rm.c:109
+#, c-format
+msgid ""
+"'%s' has staged content different from both the file and the HEAD\n"
+"(use -f to force removal)"
+msgstr ""
+
+#: builtin/rm.c:115
+#, c-format
+msgid ""
+"'%s' has changes staged in the index\n"
+"(use --cached to keep the file, or -f to force removal)"
+msgstr ""
+
+#: builtin/rm.c:119
+#, c-format
+msgid ""
+"'%s' has local modifications\n"
+"(use --cached to keep the file, or -f to force removal)"
+msgstr ""
+
+#: builtin/rm.c:194
+#, c-format
+msgid "not removing '%s' recursively without -r"
+msgstr ""
+
+#: builtin/rm.c:230
+#, c-format
+msgid "git rm: unable to remove %s"
+msgstr ""
+
+#: builtin/shortlog.c:157
+#, c-format
+msgid "Missing author: %s"
+msgstr ""
+
+#: builtin/tag.c:58
+#, c-format
+msgid "malformed object at '%s'"
+msgstr ""
+
+#: builtin/tag.c:205
+#, c-format
+msgid "tag name too long: %.*s..."
+msgstr ""
+
+#: builtin/tag.c:210
+#, c-format
+msgid "tag '%s' not found."
+msgstr ""
+
+#: builtin/tag.c:225
+#, c-format
+msgid "Deleted tag '%s' (was %s)\n"
+msgstr ""
+
+#: builtin/tag.c:237
+#, c-format
+msgid "could not verify the tag '%s'"
+msgstr ""
+
+#: builtin/tag.c:247
+msgid ""
+"\n"
+"#\n"
+"# Write a tag message\n"
+"# Lines starting with '#' will be ignored.\n"
+"#\n"
+msgstr ""
+
+#: builtin/tag.c:254
+msgid ""
+"\n"
+"#\n"
+"# Write a tag message\n"
+"# Lines starting with '#' will be kept; you may remove them yourself if you "
+"want to.\n"
+"#\n"
+msgstr ""
+
+#: builtin/tag.c:294
+msgid "unable to sign the tag"
+msgstr ""
+
+#: builtin/tag.c:296
+msgid "unable to write tag file"
+msgstr ""
+
+#: builtin/tag.c:321
+msgid "bad object type."
+msgstr ""
+
+#: builtin/tag.c:334
+msgid "tag header too big."
+msgstr ""
+
+#: builtin/tag.c:366
+msgid "no tag message?"
+msgstr ""
+
+#: builtin/tag.c:372
+#, c-format
+msgid "The tag message has been left in %s\n"
+msgstr ""
+
+#: builtin/tag.c:421
+msgid "switch 'points-at' requires an object"
+msgstr ""
+
+#: builtin/tag.c:423
+#, c-format
+msgid "malformed object name '%s'"
+msgstr ""
+
+#: builtin/tag.c:502
+msgid "-n option is only allowed with -l."
+msgstr ""
+
+#: builtin/tag.c:504
+msgid "--contains option is only allowed with -l."
+msgstr ""
+
+#: builtin/tag.c:506
+msgid "--points-at option is only allowed with -l."
+msgstr ""
+
+#: builtin/tag.c:514
+msgid "only one -F or -m option is allowed."
+msgstr ""
+
+#: builtin/tag.c:534
+msgid "too many params"
+msgstr ""
+
+#: builtin/tag.c:540
+#, c-format
+msgid "'%s' is not a valid tag name."
+msgstr ""
+
+#: builtin/tag.c:545
+#, c-format
+msgid "tag '%s' already exists"
+msgstr ""
+
+#: builtin/tag.c:563
+#, c-format
+msgid "%s: cannot lock the ref"
+msgstr ""
+
+#: builtin/tag.c:565
+#, c-format
+msgid "%s: cannot update the ref"
+msgstr ""
+
+#: builtin/tag.c:567
+#, c-format
+msgid "Updated tag '%s' (was %s)\n"
+msgstr ""
+
+#: git-am.sh:49
+msgid "You need to set your committer info first"
+msgstr ""
+
+#: git-am.sh:136
+msgid "Repository lacks necessary blobs to fall back on 3-way merge."
+msgstr ""
+
+#: git-am.sh:147
+msgid ""
+"Did you hand edit your patch?\n"
+"It does not apply to blobs recorded in its index."
+msgstr ""
+
+#: git-am.sh:156
+msgid "Falling back to patching base and 3-way merge..."
+msgstr ""
+
+#: git-am.sh:268
+msgid "Only one StGIT patch series can be applied at once"
+msgstr ""
+
+#: git-am.sh:355
+#, sh-format
+msgid "Patch format $patch_format is not supported."
+msgstr ""
+
+#: git-am.sh:357
+msgid "Patch format detection failed."
+msgstr ""
+
+#: git-am.sh:411
+msgid "-d option is no longer supported.  Do not use."
+msgstr ""
+
+#: git-am.sh:474
+#, sh-format
+msgid "previous rebase directory $dotest still exists but mbox given."
+msgstr ""
+
+#: git-am.sh:479
+msgid "Please make up your mind. --skip or --abort?"
+msgstr ""
+
+#: git-am.sh:506
+msgid "Resolve operation not in progress, we are not resuming."
+msgstr ""
+
+#: git-am.sh:572
+#, sh-format
+msgid "Dirty index: cannot apply patches (dirty: $files)"
+msgstr ""
+
+#: git-am.sh:748
+msgid "cannot be interactive without stdin connected to a terminal."
+msgstr ""
+
+#. TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
+#. in your translation. The program will only accept English
+#. input at this point.
+#: git-am.sh:759
+msgid "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
+msgstr ""
+
+#: git-am.sh:795
+#, sh-format
+msgid "Applying: $FIRSTLINE"
+msgstr ""
+
+#: git-am.sh:840
+msgid "No changes -- Patch already applied."
+msgstr ""
+
+#: git-am.sh:866
+msgid "applying to an empty history"
+msgstr ""
+
+#. TRANSLATORS: Make sure to include [Y] and [n] in your
+#. translation. The program will only accept English input
+#. at this point.
+#: git-bisect.sh:54
+msgid "Do you want me to do it for you [Y/n]? "
+msgstr ""
+
+#: git-bisect.sh:95
+#, sh-format
+msgid "unrecognised option: '$arg'"
+msgstr ""
+
+#: git-bisect.sh:99
+#, sh-format
+msgid "'$arg' does not appear to be a valid revision"
+msgstr ""
+
+#: git-bisect.sh:117
+msgid "Bad HEAD - I need a HEAD"
+msgstr ""
+
+#: git-bisect.sh:130
+#, sh-format
+msgid ""
+"Checking out '$start_head' failed. Try 'git bisect reset <validbranch>'."
+msgstr ""
+
+#: git-bisect.sh:140
+msgid "won't bisect on seeked tree"
+msgstr ""
+
+#: git-bisect.sh:144
+msgid "Bad HEAD - strange symbolic ref"
+msgstr ""
+
+#: git-bisect.sh:189
+#, sh-format
+msgid "Bad bisect_write argument: $state"
+msgstr ""
+
+#: git-bisect.sh:218
+#, sh-format
+msgid "Bad rev input: $arg"
+msgstr ""
+
+#: git-bisect.sh:232
+msgid "Please call 'bisect_state' with at least one argument."
+msgstr ""
+
+#: git-bisect.sh:244
+#, sh-format
+msgid "Bad rev input: $rev"
+msgstr ""
+
+#: git-bisect.sh:250
+msgid "'git bisect bad' can take only one argument."
+msgstr ""
+
+#. TRANSLATORS: Make sure to include [Y] and [n] in your
+#. translation. The program will only accept English input
+#. at this point.
+#: git-bisect.sh:279
+msgid "Are you sure [Y/n]? "
+msgstr ""
+
+#: git-bisect.sh:354
+#, sh-format
+msgid "'$invalid' is not a valid commit"
+msgstr ""
+
+#: git-bisect.sh:363
+#, sh-format
+msgid ""
+"Could not check out original HEAD '$branch'.\n"
+"Try 'git bisect reset <commit>'."
+msgstr ""
+
+#: git-bisect.sh:390
+msgid "No logfile given"
+msgstr ""
+
+#: git-bisect.sh:391
+#, sh-format
+msgid "cannot read $file for replaying"
+msgstr ""
+
+#: git-bisect.sh:408
+msgid "?? what are you talking about?"
+msgstr ""
+
+#: git-bisect.sh:474
+msgid "We are not bisecting."
+msgstr ""
+
+#: git-pull.sh:21
+msgid ""
+"Pull is not possible because you have unmerged files.\n"
+"Please, fix them up in the work tree, and then use 'git add/rm <file>'\n"
+"as appropriate to mark resolution, or use 'git commit -a'."
+msgstr ""
+
+#: git-pull.sh:25
+msgid "Pull is not possible because you have unmerged files."
+msgstr ""
+
+#: git-pull.sh:197
+msgid "updating an unborn branch with changes added to the index"
+msgstr ""
+
+#: git-pull.sh:253
+msgid "Cannot merge multiple branches into empty head"
+msgstr ""
+
+#: git-pull.sh:257
+msgid "Cannot rebase onto multiple branches"
+msgstr ""
+
+#: git-stash.sh:51
+msgid "git stash clear with parameters is unimplemented"
+msgstr ""
+
+#: git-stash.sh:74
+msgid "You do not have the initial commit yet"
+msgstr ""
+
+#: git-stash.sh:89
+msgid "Cannot save the current index state"
+msgstr ""
+
+#: git-stash.sh:123 git-stash.sh:136
+msgid "Cannot save the current worktree state"
+msgstr ""
+
+#: git-stash.sh:140
+msgid "No changes selected"
+msgstr ""
+
+#: git-stash.sh:143
+msgid "Cannot remove temporary index (can't happen)"
+msgstr ""
+
+#: git-stash.sh:156
+msgid "Cannot record working tree state"
+msgstr ""
+
+#: git-stash.sh:223
+msgid "No local changes to save"
+msgstr ""
+
+#: git-stash.sh:227
+msgid "Cannot initialize stash"
+msgstr ""
+
+#: git-stash.sh:235
+msgid "Cannot save the current status"
+msgstr ""
+
+#: git-stash.sh:253
+msgid "Cannot remove worktree changes"
+msgstr ""
+
+#: git-stash.sh:352
+msgid "No stash found."
+msgstr ""
+
+#: git-stash.sh:359
+#, sh-format
+msgid "Too many revisions specified: $REV"
+msgstr ""
+
+#: git-stash.sh:365
+#, sh-format
+msgid "$reference is not valid reference"
+msgstr ""
+
+#: git-stash.sh:393
+#, sh-format
+msgid "'$args' is not a stash-like commit"
+msgstr ""
+
+#: git-stash.sh:404
+#, sh-format
+msgid "'$args' is not a stash reference"
+msgstr ""
+
+#: git-stash.sh:412
+msgid "unable to refresh index"
+msgstr ""
+
+#: git-stash.sh:416
+msgid "Cannot apply a stash in the middle of a merge"
+msgstr ""
+
+#: git-stash.sh:424
+msgid "Conflicts in index. Try without --index."
+msgstr ""
+
+#: git-stash.sh:426
+msgid "Could not save index tree"
+msgstr ""
+
+#: git-stash.sh:460
+msgid "Cannot unstage modified files"
+msgstr ""
+
+#: git-stash.sh:491
+#, sh-format
+msgid "Dropped ${REV} ($s)"
+msgstr ""
+
+#: git-stash.sh:492
+#, sh-format
+msgid "${REV}: Could not drop stash entry"
+msgstr ""
+
+#: git-stash.sh:499
+msgid "No branch name specified"
+msgstr ""
+
+#: git-stash.sh:570
+msgid "(To restore them type \"git stash apply\")"
+msgstr ""
+
+#: git-submodule.sh:56
+#, sh-format
+msgid "cannot strip one component off url '$remoteurl'"
+msgstr ""
+
+#: git-submodule.sh:108
+#, sh-format
+msgid "No submodule mapping found in .gitmodules for path '$path'"
+msgstr ""
+
+#: git-submodule.sh:149
+#, sh-format
+msgid "Clone of '$url' into submodule path '$path' failed"
+msgstr ""
+
+#: git-submodule.sh:159
+#, sh-format
+msgid "Gitdir '$a' is part of the submodule path '$b' or vice versa"
+msgstr ""
+
+#: git-submodule.sh:247
+#, sh-format
+msgid "repo URL: '$repo' must be absolute or begin with ./|../"
+msgstr ""
+
+#: git-submodule.sh:264
+#, sh-format
+msgid "'$path' already exists in the index"
+msgstr ""
+
+#: git-submodule.sh:281
+#, sh-format
+msgid "'$path' already exists and is not a valid git repo"
+msgstr ""
+
+#: git-submodule.sh:295
+#, sh-format
+msgid "Unable to checkout submodule '$path'"
+msgstr ""
+
+#: git-submodule.sh:300
+#, sh-format
+msgid "Failed to add submodule '$path'"
+msgstr ""
+
+#: git-submodule.sh:305
+#, sh-format
+msgid "Failed to register submodule '$path'"
+msgstr ""
+
+#: git-submodule.sh:347
+#, sh-format
+msgid "Entering '$prefix$path'"
+msgstr ""
+
+#: git-submodule.sh:359
+#, sh-format
+msgid "Stopping at '$path'; script returned non-zero status."
+msgstr ""
+
+#: git-submodule.sh:401
+#, sh-format
+msgid "No url found for submodule path '$path' in .gitmodules"
+msgstr ""
+
+#: git-submodule.sh:410
+#, sh-format
+msgid "Failed to register url for submodule path '$path'"
+msgstr ""
+
+#: git-submodule.sh:418
+#, sh-format
+msgid "Failed to register update mode for submodule path '$path'"
+msgstr ""
+
+#: git-submodule.sh:420
+#, sh-format
+msgid "Submodule '$name' ($url) registered for path '$path'"
+msgstr ""
+
+#: git-submodule.sh:519
+#, sh-format
+msgid ""
+"Submodule path '$path' not initialized\n"
+"Maybe you want to use 'update --init'?"
+msgstr ""
+
+#: git-submodule.sh:532
+#, sh-format
+msgid "Unable to find current revision in submodule path '$path'"
+msgstr ""
+
+#: git-submodule.sh:551
+#, sh-format
+msgid "Unable to fetch in submodule path '$path'"
+msgstr ""
+
+#: git-submodule.sh:565
+#, sh-format
+msgid "Unable to rebase '$sha1' in submodule path '$path'"
+msgstr ""
+
+#: git-submodule.sh:566
+#, sh-format
+msgid "Submodule path '$path': rebased into '$sha1'"
+msgstr ""
+
+#: git-submodule.sh:571
+#, sh-format
+msgid "Unable to merge '$sha1' in submodule path '$path'"
+msgstr ""
+
+#: git-submodule.sh:572
+#, sh-format
+msgid "Submodule path '$path': merged in '$sha1'"
+msgstr ""
+
+#: git-submodule.sh:577
+#, sh-format
+msgid "Unable to checkout '$sha1' in submodule path '$path'"
+msgstr ""
+
+#: git-submodule.sh:578
+#, sh-format
+msgid "Submodule path '$path': checked out '$sha1'"
+msgstr ""
+
+#: git-submodule.sh:600 git-submodule.sh:923
+#, sh-format
+msgid "Failed to recurse into submodule path '$path'"
+msgstr ""
+
+#: git-submodule.sh:708
+msgid "--"
+msgstr ""
+
+#: git-submodule.sh:766
+#, sh-format
+msgid "  Warn: $name doesn't contain commit $sha1_src"
+msgstr ""
+
+#: git-submodule.sh:769
+#, sh-format
+msgid "  Warn: $name doesn't contain commit $sha1_dst"
+msgstr ""
+
+#: git-submodule.sh:772
+#, sh-format
+msgid "  Warn: $name doesn't contain commits $sha1_src and $sha1_dst"
+msgstr ""
+
+#: git-submodule.sh:797
+msgid "blob"
+msgstr ""
+
+#: git-submodule.sh:798
+msgid "submodule"
+msgstr ""
+
+#: git-submodule.sh:969
+#, sh-format
+msgid "Synchronizing submodule url for '$name'"
+msgstr ""
diff --git a/po/de.po b/po/de.po
new file mode 100644
index 0000000..0cbcd64
--- /dev/null
+++ b/po/de.po
@@ -0,0 +1,5776 @@
+# German translations for Git.
+# Copyright (C) 2010-2012 Ralf Thielow <ralf.thielow@googlemail.com>
+# This file is distributed under the same license as the Git package.
+# Ralf Thielow <ralf.thielow@googlemail.com>, 2010, 2011, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: git 1.7.11\n"
+"Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
+"POT-Creation-Date: 2012-07-03 10:23+0800\n"
+"PO-Revision-Date: 2012-03-28 18:46+0200\n"
+"Last-Translator: Ralf Thielow <ralf.thielow@googlemail.com>\n"
+"Language-Team: German\n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: advice.c:40
+#, c-format
+msgid "hint: %.*s\n"
+msgstr "Hinweis: %.*s\n"
+
+#.
+#. * Message used both when 'git commit' fails and when
+#. * other commands doing a merge do.
+#.
+#: advice.c:70
+msgid ""
+"Fix them up in the work tree,\n"
+"and then use 'git add/rm <file>' as\n"
+"appropriate to mark resolution and make a commit,\n"
+"or use 'git commit -a'."
+msgstr ""
+"Korrigiere dies im Arbeitsbaum,\n"
+"und benutze dann 'git add/rm <Datei>'\n"
+"um die Auflösung entsprechend zu markieren und einzutragen,\n"
+"oder benutze 'git commit -a'."
+
+#: bundle.c:36
+#, c-format
+msgid "'%s' does not look like a v2 bundle file"
+msgstr "'%s' sieht nicht wie eine v2 Paketdatei aus"
+
+#: bundle.c:63
+#, c-format
+msgid "unrecognized header: %s%s (%d)"
+msgstr "nicht erkannter Kopfbereich: %s%s (%d)"
+
+#: bundle.c:89 builtin/commit.c:696
+#, c-format
+msgid "could not open '%s'"
+msgstr "Konnte '%s' nicht öffnen"
+
+#: bundle.c:140
+msgid "Repository lacks these prerequisite commits:"
+msgstr "Dem Projektarchiv fehlen folgende vorrausgesetzte Versionen:"
+
+#: bundle.c:164 sequencer.c:550 sequencer.c:982 builtin/log.c:290
+#: builtin/log.c:721 builtin/log.c:1310 builtin/log.c:1529 builtin/merge.c:347
+#: builtin/shortlog.c:181
+msgid "revision walk setup failed"
+msgstr "Einrichtung des Revisionsgangs fehlgeschlagen"
+
+#: bundle.c:186
+#, c-format
+msgid "The bundle contains %d ref"
+msgid_plural "The bundle contains %d refs"
+msgstr[0] "Das Paket enthält %d Referenz"
+msgstr[1] "Das Paket enthält %d Referenzen"
+
+#: bundle.c:192
+msgid "The bundle records a complete history."
+msgstr "Das Paket speichert eine komplette Historie."
+
+#: bundle.c:195
+#, c-format
+msgid "The bundle requires this ref"
+msgid_plural "The bundle requires these %d refs"
+msgstr[0] "Das Paket benötigt diese Referenz"
+msgstr[1] "Das Paket benötigt diese %d Referenzen"
+
+#: bundle.c:294
+msgid "rev-list died"
+msgstr "\"rev-list\" abgebrochen"
+
+#: bundle.c:300 builtin/log.c:1206 builtin/shortlog.c:284
+#, c-format
+msgid "unrecognized argument: %s"
+msgstr "nicht erkanntes Argument: %s"
+
+#: bundle.c:335
+#, c-format
+msgid "ref '%s' is excluded by the rev-list options"
+msgstr "Referenz '%s' wird durch \"rev-list\" Optionen ausgeschlossen"
+
+#: bundle.c:380
+msgid "Refusing to create empty bundle."
+msgstr "Erstellung eines leeren Pakets zurückgewiesen."
+
+#: bundle.c:398
+msgid "Could not spawn pack-objects"
+msgstr "Konnte Paketobjekte nicht erstellen"
+
+#: bundle.c:416
+msgid "pack-objects died"
+msgstr "Erstellung der Paketobjekte abgebrochen"
+
+#: bundle.c:419
+#, c-format
+msgid "cannot create '%s'"
+msgstr "kann '%s' nicht erstellen"
+
+#: bundle.c:441
+msgid "index-pack died"
+msgstr "Erstellung der Paketindexdatei abgebrochen"
+
+#: commit.c:48
+#, c-format
+msgid "could not parse %s"
+msgstr "konnte %s nicht parsen"
+
+#: commit.c:50
+#, c-format
+msgid "%s %s is not a commit!"
+msgstr "%s %s ist keine Version!"
+
+#: compat/obstack.c:406 compat/obstack.c:408
+msgid "memory exhausted"
+msgstr "Speicher verbraucht"
+
+#: connected.c:39
+msgid "Could not run 'git rev-list'"
+msgstr "Konnte 'git rev-list' nicht ausführen"
+
+#: connected.c:48
+#, c-format
+msgid "failed write to rev-list: %s"
+msgstr "Fehler beim Schreiben nach rev-list: %s"
+
+#: connected.c:56
+#, c-format
+msgid "failed to close rev-list's stdin: %s"
+msgstr "Fehler beim Schließen von rev-list's Standard-Eingabe: %s"
+
+#: date.c:95
+msgid "in the future"
+msgstr "in der Zukunft"
+
+#: date.c:101
+#, c-format
+msgid "%lu second ago"
+msgid_plural "%lu seconds ago"
+msgstr[0] "vor %lu Sekunde"
+msgstr[1] "vor %lu Sekunden"
+
+#: date.c:108
+#, c-format
+msgid "%lu minute ago"
+msgid_plural "%lu minutes ago"
+msgstr[0] "vor %lu Minute"
+msgstr[1] "vor %lu Minuten"
+
+#: date.c:115
+#, c-format
+msgid "%lu hour ago"
+msgid_plural "%lu hours ago"
+msgstr[0] "vor %lu Stunde"
+msgstr[1] "vor %lu Stunden"
+
+#: date.c:122
+#, c-format
+msgid "%lu day ago"
+msgid_plural "%lu days ago"
+msgstr[0] "vor %lu Tag"
+msgstr[1] "vor %lu Tagen"
+
+#: date.c:128
+#, c-format
+msgid "%lu week ago"
+msgid_plural "%lu weeks ago"
+msgstr[0] "vor %lu Woche"
+msgstr[1] "vor %lu Wochen"
+
+#: date.c:135
+#, c-format
+msgid "%lu month ago"
+msgid_plural "%lu months ago"
+msgstr[0] "vor %lu Monat"
+msgstr[1] "vor %lu Monaten"
+
+#: date.c:146
+#, c-format
+msgid "%lu year"
+msgid_plural "%lu years"
+msgstr[0] "vor %lu Jahr"
+msgstr[1] "vor %lu Jahren"
+
+#: date.c:149
+#, c-format
+msgid "%s, %lu month ago"
+msgid_plural "%s, %lu months ago"
+msgstr[0] "%s, und %lu Monat"
+msgstr[1] "%s, und %lu Monaten"
+
+#: date.c:154 date.c:159
+#, c-format
+msgid "%lu year ago"
+msgid_plural "%lu years ago"
+msgstr[0] "vor %lu Jahr"
+msgstr[1] "vor %lu Jahren"
+
+#: diff.c:105
+#, c-format
+msgid "  Failed to parse dirstat cut-off percentage '%.*s'\n"
+msgstr ""
+"  Fehler beim Parsen des abgeschnittenen \"dirstat\" Prozentsatzes '%.*s'\n"
+
+#: diff.c:110
+#, c-format
+msgid "  Unknown dirstat parameter '%.*s'\n"
+msgstr "  Unbekannter \"dirstat\" Parameter '%.*s'\n"
+
+#: diff.c:210
+#, c-format
+msgid ""
+"Found errors in 'diff.dirstat' config variable:\n"
+"%s"
+msgstr ""
+"Fehler in 'diff.dirstat' Konfigurationsvariable gefunden:\n"
+"%s"
+
+#: diff.c:1400
+msgid " 0 files changed\n"
+msgstr " 0 Dateien geändert\n"
+
+#: diff.c:1404
+#, c-format
+msgid " %d file changed"
+msgid_plural " %d files changed"
+msgstr[0] " %d Datei geändert"
+msgstr[1] " %d Dateien geändert"
+
+#: diff.c:1421
+#, c-format
+msgid ", %d insertion(+)"
+msgid_plural ", %d insertions(+)"
+msgstr[0] ", %d Zeile hinzugefügt(+)"
+msgstr[1] ", %d Zeilen hinzugefügt(+)"
+
+#: diff.c:1432
+#, c-format
+msgid ", %d deletion(-)"
+msgid_plural ", %d deletions(-)"
+msgstr[0] ", %d Zeile entfernt(-)"
+msgstr[1] ", %d Zeilen entfernt(-)"
+
+#: diff.c:3478
+#, c-format
+msgid ""
+"Failed to parse --dirstat/-X option parameter:\n"
+"%s"
+msgstr ""
+"Fehler beim Parsen des --dirstat/-X Optionsparameters:\n"
+"%s"
+
+#: gpg-interface.c:59
+msgid "could not run gpg."
+msgstr "konnte gpg nicht ausführen"
+
+#: gpg-interface.c:71
+msgid "gpg did not accept the data"
+msgstr "gpg hat die Daten nicht akzeptiert"
+
+#: gpg-interface.c:82
+msgid "gpg failed to sign the data"
+msgstr "gpg beim Signieren der Daten fehlgeschlagen"
+
+#: grep.c:1320
+#, c-format
+msgid "'%s': unable to read %s"
+msgstr "'%s': konnte nicht lesen %s"
+
+#: grep.c:1337
+#, c-format
+msgid "'%s': %s"
+msgstr "'%s': %s"
+
+#: grep.c:1348
+#, c-format
+msgid "'%s': short read %s"
+msgstr "'%s': read() zu kurz %s"
+
+#: help.c:208
+#, c-format
+msgid "available git commands in '%s'"
+msgstr "Vorhandene Git-Kommandos in '%s'"
+
+#: help.c:215
+msgid "git commands available from elsewhere on your $PATH"
+msgstr "Vorhandene Git-Kommandos irgendwo in deinem $PATH"
+
+#: help.c:271
+#, c-format
+msgid ""
+"'%s' appears to be a git command, but we were not\n"
+"able to execute it. Maybe git-%s is broken?"
+msgstr ""
+"'%s' scheint ein git-Kommando zu sein, konnte aber\n"
+"nicht ausgeführt werden. Vielleicht ist git-%s fehlerhaft?"
+
+#: help.c:328
+msgid "Uh oh. Your system reports no Git commands at all."
+msgstr "Uh oh. Keine Git-Kommandos auf deinem System vorhanden."
+
+#: help.c:350
+#, c-format
+msgid ""
+"WARNING: You called a Git command named '%s', which does not exist.\n"
+"Continuing under the assumption that you meant '%s'"
+msgstr ""
+"Warnung: Du hast das nicht existierende Git-Kommando '%s' ausgeführt.\n"
+"Setze fort unter der Annahme das du '%s' gemeint hast"
+
+#: help.c:355
+#, c-format
+msgid "in %0.1f seconds automatically..."
+msgstr "automatisch in %0.1f Sekunden..."
+
+#: help.c:362
+#, c-format
+msgid "git: '%s' is not a git command. See 'git --help'."
+msgstr "git: '%s' ist kein Git-Kommando. Siehe 'git --help'."
+
+#: help.c:366
+msgid ""
+"\n"
+"Did you mean this?"
+msgid_plural ""
+"\n"
+"Did you mean one of these?"
+msgstr[0] ""
+"\n"
+"Hast du das gemeint?"
+msgstr[1] ""
+"\n"
+"Hast du eines von diesen gemeint?"
+
+#: parse-options.c:493
+msgid "..."
+msgstr "..."
+
+#: parse-options.c:511
+#, c-format
+msgid "usage: %s"
+msgstr "Verwendung: %s"
+
+#. TRANSLATORS: the colon here should align with the
+#. one in "usage: %s" translation
+#: parse-options.c:515
+#, c-format
+msgid "   or: %s"
+msgstr "      oder: %s"
+
+#: parse-options.c:518
+#, c-format
+msgid "    %s"
+msgstr "    %s"
+
+#: remote.c:1629
+#, c-format
+msgid "Your branch is ahead of '%s' by %d commit.\n"
+msgid_plural "Your branch is ahead of '%s' by %d commits.\n"
+msgstr[0] "Dein Zweig ist vor '%s' um %d Version.\n"
+msgstr[1] "Dein Zweig ist vor '%s' um %d Versionen.\n"
+
+#: remote.c:1635
+#, c-format
+msgid "Your branch is behind '%s' by %d commit, and can be fast-forwarded.\n"
+msgid_plural ""
+"Your branch is behind '%s' by %d commits, and can be fast-forwarded.\n"
+msgstr[0] ""
+"Dein Zweig ist zu '%s' um %d Version hinterher, und kann vorgespult werden.\n"
+msgstr[1] ""
+"Dein Zweig ist zu '%s' um %d Versionen hinterher, und kann vorgespult "
+"werden.\n"
+
+#: remote.c:1643
+#, c-format
+msgid ""
+"Your branch and '%s' have diverged,\n"
+"and have %d and %d different commit each, respectively.\n"
+msgid_plural ""
+"Your branch and '%s' have diverged,\n"
+"and have %d and %d different commits each, respectively.\n"
+msgstr[0] ""
+"Dein Zweig und '%s' sind divergiert,\n"
+"und haben jeweils %d und %d unterschiedliche Versionen.\n"
+msgstr[1] ""
+"Dein Zweig und '%s' sind divergiert,\n"
+"und haben jeweils %d und %d unterschiedliche Versionen.\n"
+
+#: sequencer.c:121 builtin/merge.c:865 builtin/merge.c:978
+#: builtin/merge.c:1088 builtin/merge.c:1098
+#, c-format
+msgid "Could not open '%s' for writing"
+msgstr "Konnte '%s' nicht zum Schreiben öffnen."
+
+#: sequencer.c:123 builtin/merge.c:333 builtin/merge.c:868
+#: builtin/merge.c:1090 builtin/merge.c:1103
+#, c-format
+msgid "Could not write to '%s'"
+msgstr "Konnte nicht nach '%s' schreiben."
+
+#: sequencer.c:144
+msgid ""
+"after resolving the conflicts, mark the corrected paths\n"
+"with 'git add <paths>' or 'git rm <paths>'"
+msgstr ""
+"nach Auflösung der Konflikte, markiere die korrigierten Pfade\n"
+"mit 'git add <Pfade>' oder 'git rm <Pfade>'"
+
+#: sequencer.c:147
+msgid ""
+"after resolving the conflicts, mark the corrected paths\n"
+"with 'git add <paths>' or 'git rm <paths>'\n"
+"and commit the result with 'git commit'"
+msgstr ""
+"nach Auflösung der Konflikte, markiere die korrigierten Pfade\n"
+"mit 'git add <Pfade>' oder 'git rm <Pfade>'und trage das Ergebnis ein mit "
+"'git commit'"
+
+#: sequencer.c:160 sequencer.c:758 sequencer.c:841
+#, c-format
+msgid "Could not write to %s"
+msgstr "Konnte nicht nach %s schreiben"
+
+#: sequencer.c:163
+#, c-format
+msgid "Error wrapping up %s"
+msgstr "Fehler bei Nachbereitung von %s"
+
+#: sequencer.c:178
+msgid "Your local changes would be overwritten by cherry-pick."
+msgstr ""
+"Deine lokalen Änderungen würden von \"cherry-pick\" überschrieben werden."
+
+#: sequencer.c:180
+msgid "Your local changes would be overwritten by revert."
+msgstr "Deine lokalen Änderungen würden von \"revert\" überschrieben werden."
+
+#: sequencer.c:183
+msgid "Commit your changes or stash them to proceed."
+msgstr "Trage deine Änderungen ein oder benutze \"stash\" um fortzufahren."
+
+#. TRANSLATORS: %s will be "revert" or "cherry-pick"
+#: sequencer.c:233
+#, c-format
+msgid "%s: Unable to write new index file"
+msgstr "%s: Konnte neue Bereitstellungsdatei nicht schreiben"
+
+#: sequencer.c:261
+msgid "Could not resolve HEAD commit\n"
+msgstr "Konnte Version der Zweigspitze (HEAD) nicht auflösen\n"
+
+#: sequencer.c:282
+msgid "Unable to update cache tree\n"
+msgstr "Konnte zwischengespeicherten Baum nicht aktualisieren\n"
+
+#: sequencer.c:324
+#, c-format
+msgid "Could not parse commit %s\n"
+msgstr "Konnte Version %s nicht parsen\n"
+
+#: sequencer.c:329
+#, c-format
+msgid "Could not parse parent commit %s\n"
+msgstr "Konnte Elternversion %s nicht parsen\n"
+
+#: sequencer.c:395
+msgid "Your index file is unmerged."
+msgstr "Deine Bereitstellungsdatei ist nicht zusammengeführt."
+
+#: sequencer.c:398
+msgid "You do not have a valid HEAD"
+msgstr "Du hast keine gültige Zweigspitze (HEAD)"
+
+#: sequencer.c:413
+#, c-format
+msgid "Commit %s is a merge but no -m option was given."
+msgstr ""
+"Version %s ist eine Zusammenführung, aber die Option -m wurde nicht "
+"angegeben."
+
+#: sequencer.c:421
+#, c-format
+msgid "Commit %s does not have parent %d"
+msgstr "Version %s hat keinen Elternteil %d"
+
+#: sequencer.c:425
+#, c-format
+msgid "Mainline was specified but commit %s is not a merge."
+msgstr ""
+"Hauptlinie wurde spezifiziert, aber Version %s ist keine Zusammenführung."
+
+#. TRANSLATORS: The first %s will be "revert" or
+#. "cherry-pick", the second %s a SHA1
+#: sequencer.c:436
+#, c-format
+msgid "%s: cannot parse parent commit %s"
+msgstr "%s: kann Elternversion %s nicht parsen"
+
+#: sequencer.c:440
+#, c-format
+msgid "Cannot get commit message for %s"
+msgstr "Kann keine Versionsbeschreibung für %s bekommen"
+
+#: sequencer.c:524
+#, c-format
+msgid "could not revert %s... %s"
+msgstr "Konnte %s nicht zurücksetzen... %s"
+
+#: sequencer.c:525
+#, c-format
+msgid "could not apply %s... %s"
+msgstr "Konnte %s nicht anwenden... %s"
+
+#: sequencer.c:553
+msgid "empty commit set passed"
+msgstr "leere Menge von Versionen übergeben"
+
+#: sequencer.c:561
+#, c-format
+msgid "git %s: failed to read the index"
+msgstr "git %s: Fehler beim Lesen der Bereitstellung"
+
+#: sequencer.c:566
+#, c-format
+msgid "git %s: failed to refresh the index"
+msgstr "git %s: Fehler beim Aktualisieren der Bereitstellung"
+
+#: sequencer.c:624
+#, c-format
+msgid "Cannot %s during a %s"
+msgstr "Kann %s nicht während eines %s durchführen"
+
+#: sequencer.c:646
+#, c-format
+msgid "Could not parse line %d."
+msgstr "Konnte Zeile %d nicht parsen."
+
+#: sequencer.c:651
+msgid "No commits parsed."
+msgstr "Keine Versionen geparst."
+
+#: sequencer.c:664
+#, c-format
+msgid "Could not open %s"
+msgstr "Konnte %s nicht öffnen"
+
+#: sequencer.c:668
+#, c-format
+msgid "Could not read %s."
+msgstr "Konnte %s nicht lesen."
+
+#: sequencer.c:675
+#, c-format
+msgid "Unusable instruction sheet: %s"
+msgstr "Unbenutzbares Instruktionsblatt: %s"
+
+#: sequencer.c:703
+#, c-format
+msgid "Invalid key: %s"
+msgstr "Ungültiger Schlüssel: %s"
+
+#: sequencer.c:706
+#, c-format
+msgid "Invalid value for %s: %s"
+msgstr "Ungültiger Wert für %s: %s"
+
+#: sequencer.c:718
+#, c-format
+msgid "Malformed options sheet: %s"
+msgstr "Fehlerhaftes Optionsblatt: %s"
+
+#: sequencer.c:739
+msgid "a cherry-pick or revert is already in progress"
+msgstr "\"cherry-pick\" oder \"revert\" ist bereits im Gang"
+
+#: sequencer.c:740
+msgid "try \"git cherry-pick (--continue | --quit | --abort)\""
+msgstr "versuche \"git cherry-pick (--continue | --quit | --abort)\""
+
+#: sequencer.c:744
+#, c-format
+msgid "Could not create sequencer directory %s"
+msgstr "Konnte \"sequencer\"-Verzeichnis %s nicht erstellen"
+
+#: sequencer.c:760 sequencer.c:845
+#, c-format
+msgid "Error wrapping up %s."
+msgstr "Fehler beim Einpacken von %s."
+
+#: sequencer.c:779 sequencer.c:913
+msgid "no cherry-pick or revert in progress"
+msgstr "kein \"cherry-pick\" oder \"revert\" im Gang"
+
+#: sequencer.c:781
+msgid "cannot resolve HEAD"
+msgstr "kann Zweigspitze (HEAD) nicht auflösen"
+
+#: sequencer.c:783
+msgid "cannot abort from a branch yet to be born"
+msgstr "kann nicht abbrechen: bin auf einem Zweig, der noch geboren wird"
+
+#: sequencer.c:805 builtin/apply.c:3697
+#, c-format
+msgid "cannot open %s: %s"
+msgstr "Kann %s nicht öffnen: %s"
+
+#: sequencer.c:808
+#, c-format
+msgid "cannot read %s: %s"
+msgstr "Kann %s nicht lesen: %s"
+
+#: sequencer.c:809
+msgid "unexpected end of file"
+msgstr "Unerwartetes Dateiende"
+
+#: sequencer.c:815
+#, c-format
+msgid "stored pre-cherry-pick HEAD file '%s' is corrupt"
+msgstr ""
+"gespeicherte \"pre-cherry-pick\" Datei der Zweigspitze (HEAD) '%s' ist "
+"beschädigt"
+
+#: sequencer.c:838
+#, c-format
+msgid "Could not format %s."
+msgstr "Konnte %s nicht formatieren."
+
+#: sequencer.c:1000
+msgid "Can't revert as initial commit"
+msgstr "Kann nicht zu initialer Version zurücksetzen."
+
+#: sequencer.c:1001
+msgid "Can't cherry-pick into empty head"
+msgstr "Kann \"cherry-pick\" nicht in einem leerem Kopf ausführen."
+
+#: sha1_name.c:864
+msgid "HEAD does not point to a branch"
+msgstr "Zweigspitze (HEAD) zeigt auf keinen Zweig"
+
+#: sha1_name.c:867
+#, c-format
+msgid "No such branch: '%s'"
+msgstr "Kein solcher Zweig '%s'"
+
+#: sha1_name.c:869
+#, c-format
+msgid "No upstream configured for branch '%s'"
+msgstr "Kein entferntes Projektarchiv für Zweig '%s' konfiguriert."
+
+#: sha1_name.c:872
+#, c-format
+msgid "Upstream branch '%s' not stored as a remote-tracking branch"
+msgstr ""
+"Zweig '%s' des entfernten Projektarchivs ist kein gefolgter Übernahmezweig"
+
+#: wrapper.c:413
+#, c-format
+msgid "unable to look up current user in the passwd file: %s"
+msgstr "konnte aktuellen Benutzer nicht in Passwort-Datei finden: %s"
+
+#: wrapper.c:414
+msgid "no such user"
+msgstr "kein solcher Benutzer"
+
+#: wt-status.c:141
+msgid "Unmerged paths:"
+msgstr "Nicht zusammengeführte Pfade:"
+
+#: wt-status.c:168 wt-status.c:195
+#, c-format
+msgid "  (use \"git reset %s <file>...\" to unstage)"
+msgstr ""
+"  (benutze \"git reset %s <Datei>...\" zum Herausnehmen aus der "
+"Bereitstellung)"
+
+#: wt-status.c:170 wt-status.c:197
+msgid "  (use \"git rm --cached <file>...\" to unstage)"
+msgstr ""
+"  (benutze \"git rm --cached <Datei>...\" zum Herausnehmen aus der "
+"Bereitstellung)"
+
+#: wt-status.c:174
+msgid "  (use \"git add <file>...\" to mark resolution)"
+msgstr "  (benutze \"git add/rm <Datei>...\" um die Auflösung zu markieren)"
+
+#: wt-status.c:176 wt-status.c:180
+msgid "  (use \"git add/rm <file>...\" as appropriate to mark resolution)"
+msgstr ""
+"  (benutze \"git add/rm <Datei>...\" um die Auflösung entsprechend zu "
+"markieren)"
+
+#: wt-status.c:178
+msgid "  (use \"git rm <file>...\" to mark resolution)"
+msgstr "  (benutze \"git add/rm <Datei>...\" um die Auflösung zu markieren)"
+
+#: wt-status.c:189
+msgid "Changes to be committed:"
+msgstr "zum Eintragen bereitgestellte Änderungen:"
+
+#: wt-status.c:207
+msgid "Changes not staged for commit:"
+msgstr "Änderungen, die nicht zum Eintragen bereitgestellt sind:"
+
+#: wt-status.c:211
+msgid "  (use \"git add <file>...\" to update what will be committed)"
+msgstr "  (benutze \"git add <Datei>...\" zum Bereitstellen)"
+
+#: wt-status.c:213
+msgid "  (use \"git add/rm <file>...\" to update what will be committed)"
+msgstr "  (benutze \"git add/rm <Datei>...\" zum Bereitstellen)"
+
+#: wt-status.c:214
+msgid ""
+"  (use \"git checkout -- <file>...\" to discard changes in working directory)"
+msgstr ""
+"  (benutze \"git checkout -- <Datei>...\" um die Änderungen im "
+"Arbeitsverzeichnis zu verwerfen)"
+
+#: wt-status.c:216
+msgid "  (commit or discard the untracked or modified content in submodules)"
+msgstr ""
+"  (trage ein oder verwerfe den unbeobachteten oder geänderten Inhalt in den "
+"Unterprojekten)"
+
+#: wt-status.c:225
+#, c-format
+msgid "%s files:"
+msgstr "%s Dateien:"
+
+#: wt-status.c:228
+#, c-format
+msgid "  (use \"git %s <file>...\" to include in what will be committed)"
+msgstr "  (benutze \"git %s <Datei>...\" zum Einfügen in die Eintragung)"
+
+#: wt-status.c:245
+msgid "bug"
+msgstr "Fehler"
+
+#: wt-status.c:250
+msgid "both deleted:"
+msgstr "beide gelöscht:"
+
+#: wt-status.c:251
+msgid "added by us:"
+msgstr "von uns hinzugefügt:"
+
+#: wt-status.c:252
+msgid "deleted by them:"
+msgstr "von denen gelöscht:"
+
+#: wt-status.c:253
+msgid "added by them:"
+msgstr "von denen hinzugefügt:"
+
+#: wt-status.c:254
+msgid "deleted by us:"
+msgstr "von uns gelöscht:"
+
+#: wt-status.c:255
+msgid "both added:"
+msgstr "von beiden hinzugefügt:"
+
+#: wt-status.c:256
+msgid "both modified:"
+msgstr "von beiden geändert:"
+
+#: wt-status.c:286
+msgid "new commits, "
+msgstr "neue Versionen, "
+
+#: wt-status.c:288
+msgid "modified content, "
+msgstr "geänderter Inhalt, "
+
+#: wt-status.c:290
+msgid "untracked content, "
+msgstr "unbeobachteter Inhalt, "
+
+#: wt-status.c:304
+#, c-format
+msgid "new file:   %s"
+msgstr "neue Datei:   %s"
+
+#: wt-status.c:307
+#, c-format
+msgid "copied:     %s -> %s"
+msgstr "kopiert:     %s -> %s"
+
+#: wt-status.c:310
+#, c-format
+msgid "deleted:    %s"
+msgstr "gelöscht:    %s"
+
+#: wt-status.c:313
+#, c-format
+msgid "modified:   %s"
+msgstr "geändert:   %s"
+
+#: wt-status.c:316
+#, c-format
+msgid "renamed:    %s -> %s"
+msgstr "umbenannt:    %s -> %s"
+
+#: wt-status.c:319
+#, c-format
+msgid "typechange: %s"
+msgstr "Typänderung: %s"
+
+#: wt-status.c:322
+#, c-format
+msgid "unknown:    %s"
+msgstr "unbekannt:    %s"
+
+#: wt-status.c:325
+#, c-format
+msgid "unmerged:   %s"
+msgstr "nicht zusammengeführt:   %s"
+
+#: wt-status.c:328
+#, c-format
+msgid "bug: unhandled diff status %c"
+msgstr "Fehler: unbehandelter Differenz-Status %c"
+
+#: wt-status.c:786
+msgid "You have unmerged paths."
+msgstr "Du hast nicht zusammengeführte Pfade."
+
+#: wt-status.c:789 wt-status.c:913
+msgid "  (fix conflicts and run \"git commit\")"
+msgstr " (behebe die Konflikte und führe \"git commit\" aus)"
+
+#: wt-status.c:792
+msgid "All conflicts fixed but you are still merging."
+msgstr "Alle Konflikte sind behoben, aber du bist immer noch beim "
+"Zusammenführen."
+
+#: wt-status.c:795
+msgid "  (use \"git commit\" to conclude merge)"
+msgstr "  (benutze \"git commit\" um die Zusammenführung abzuschließen)"
+
+#: wt-status.c:805
+msgid "You are in the middle of an am session."
+msgstr "Eine \"am\"-Sitzung ist im Gange."
+
+#: wt-status.c:808
+msgid "The current patch is empty."
+msgstr "Der aktuelle Patch ist leer."
+
+#: wt-status.c:812
+msgid "  (fix conflicts and then run \"git am --resolved\")"
+msgstr "  (behebe die Konflikte und führe dann \"git am --resolved\" aus)"
+
+#: wt-status.c:814
+msgid "  (use \"git am --skip\" to skip this patch)"
+msgstr " (benutze \"git am --skip\" um diesen Patch auszulassen)"
+
+#: wt-status.c:816
+msgid "  (use \"git am --abort\" to restore the original branch)"
+msgstr "  (benutze \"git am --abort\" um den ursprünglichen Zweig "
+"wiederherzustellen)"
+
+#: wt-status.c:874 wt-status.c:884
+msgid "You are currently rebasing."
+msgstr "Du bist gerade beim Neuaufbau."
+
+#: wt-status.c:877
+msgid "  (fix conflicts and then run \"git rebase --continue\")"
+msgstr "  (behebe die Konflikte und führe dann \"git rebase --continue\" aus)"
+
+#: wt-status.c:879
+msgid "  (use \"git rebase --skip\" to skip this patch)"
+msgstr "  (benutze \"git rebase --skip\" um diesen Patch auszulassen)"
+
+#: wt-status.c:881
+msgid "  (use \"git rebase --abort\" to check out the original branch)"
+msgstr "  (benutze \"git rebase --abort\" um den ursprünglichen Zweig "
+"auszuchecken)"
+
+#: wt-status.c:887
+msgid "  (all conflicts fixed: run \"git rebase --continue\")"
+msgstr "  (alle Konflikte behoben: führe \"git rebase --continue\" aus)"
+
+#: wt-status.c:889
+msgid "You are currently splitting a commit during a rebase."
+msgstr "Du teilst gerade eine Version während eines Neuaufbaus auf."
+
+#: wt-status.c:892
+msgid "  (Once your working directory is clean, run \"git rebase --continue\")"
+msgstr "  (Sobald dein Arbeitsverzeichnis sauber ist, führe "
+"\"git rebase --continue\" aus)"
+
+#: wt-status.c:894
+msgid "You are currently editing a commit during a rebase."
+msgstr "Du editierst gerade eine Version während eines Neuaufbaus."
+
+#: wt-status.c:897
+msgid "  (use \"git commit --amend\" to amend the current commit)"
+msgstr "  (benutze \"git commit --amend\" um die aktuelle Version "
+"nachzubessern)"
+
+#: wt-status.c:899
+msgid ""
+"  (use \"git rebase --continue\" once you are satisfied with your changes)"
+msgstr "  (benutze \"git rebase --continue\" sobald deine Änderungen "
+"abgeschlossen sind)"
+
+#: wt-status.c:909
+msgid "You are currently cherry-picking."
+msgstr "Du führst gerade \"cherry-pick\" aus."
+
+#: wt-status.c:916
+msgid "  (all conflicts fixed: run \"git commit\")"
+msgstr "  (alle Konflikte behoben: führe \"git commit\" aus)"
+
+#: wt-status.c:925
+msgid "You are currently bisecting."
+msgstr "Du bist gerade beim Halbieren."
+
+#: wt-status.c:928
+msgid "  (use \"git bisect reset\" to get back to the original branch)"
+msgstr "  (benutze \"git bisect reset\" um zum ursprünglichen Zweig "
+"zurückzukehren)"
+
+#: wt-status.c:979
+msgid "On branch "
+msgstr "Auf Zweig "
+
+#: wt-status.c:986
+msgid "Not currently on any branch."
+msgstr "Im Moment auf keinem Zweig."
+
+#: wt-status.c:998
+msgid "Initial commit"
+msgstr "Initiale Version"
+
+#: wt-status.c:1012
+msgid "Untracked"
+msgstr "Unbeobachtete"
+
+#: wt-status.c:1014
+msgid "Ignored"
+msgstr "Ignorierte"
+
+#: wt-status.c:1016
+#, c-format
+msgid "Untracked files not listed%s"
+msgstr "Unbeobachtete Dateien nicht aufgelistet%s"
+
+#: wt-status.c:1018
+msgid " (use -u option to show untracked files)"
+msgstr " (benutze die Option -u um unbeobachteten Dateien anzuzeigen)"
+
+#: wt-status.c:1024
+msgid "No changes"
+msgstr "Keine Änderungen"
+
+#: wt-status.c:1028
+#, c-format
+msgid "no changes added to commit%s\n"
+msgstr "keine Änderungen zum Eintragen hinzugefügt%s\n"
+
+#: wt-status.c:1030
+msgid " (use \"git add\" and/or \"git commit -a\")"
+msgstr " (benutze \"git add\" und/oder \"git commit -a\")"
+
+#: wt-status.c:1032
+#, c-format
+msgid "nothing added to commit but untracked files present%s\n"
+msgstr ""
+"nichts zum Eintragen hinzugefügt, aber es gibt unbeobachtete Dateien%s\n"
+
+#: wt-status.c:1034
+msgid " (use \"git add\" to track)"
+msgstr " (benutze \"git add\" zum Beobachten)"
+
+#: wt-status.c:1036 wt-status.c:1039 wt-status.c:1042
+#, c-format
+msgid "nothing to commit%s\n"
+msgstr "nichts zum Eintragen%s\n"
+
+#: wt-status.c:1037
+msgid " (create/copy files and use \"git add\" to track)"
+msgstr " (Erstelle/Kopiere Dateien und benutze \"git add\" zum Beobachten)"
+
+#: wt-status.c:1040
+msgid " (use -u to show untracked files)"
+msgstr " (benutze die Option -u um unbeobachtete Dateien anzuzeigen)"
+
+#: wt-status.c:1043
+msgid " (working directory clean)"
+msgstr " (Arbeitsverzeichnis sauber)"
+
+#: wt-status.c:1151
+msgid "HEAD (no branch)"
+msgstr "HEAD (kein Zweig)"
+
+#: wt-status.c:1157
+msgid "Initial commit on "
+msgstr "Initiale Version auf "
+
+#: wt-status.c:1172
+msgid "behind "
+msgstr "hinterher "
+
+#: wt-status.c:1175 wt-status.c:1178
+msgid "ahead "
+msgstr "voraus "
+
+#: wt-status.c:1180
+msgid ", behind "
+msgstr ", hinterher "
+
+#: builtin/add.c:62
+#, c-format
+msgid "unexpected diff status %c"
+msgstr "unerwarteter Differenz-Status %c"
+
+#: builtin/add.c:67 builtin/commit.c:226
+msgid "updating files failed"
+msgstr "Aktualisierung der Dateien fehlgeschlagen"
+
+#: builtin/add.c:77
+#, c-format
+msgid "remove '%s'\n"
+msgstr "entferne '%s'\n"
+
+#: builtin/add.c:176
+#, c-format
+msgid "Path '%s' is in submodule '%.*s'"
+msgstr "Pfad '%s' befindet sich in Unterprojekt '%.*s'"
+
+#: builtin/add.c:192
+msgid "Unstaged changes after refreshing the index:"
+msgstr ""
+"Nicht bereitgestellte Änderungen nach Aktualisierung der Bereitstellung:"
+
+#: builtin/add.c:195 builtin/add.c:459 builtin/rm.c:186
+#, c-format
+msgid "pathspec '%s' did not match any files"
+msgstr "Pfadspezifikation '%s' stimmt mit keinen Dateien überein"
+
+#: builtin/add.c:209
+#, c-format
+msgid "'%s' is beyond a symbolic link"
+msgstr "'%s' ist über einem symbolischen Link"
+
+#: builtin/add.c:276
+msgid "Could not read the index"
+msgstr "Konnte die Bereitstellung nicht lesen"
+
+#: builtin/add.c:286
+#, c-format
+msgid "Could not open '%s' for writing."
+msgstr "Konnte '%s' nicht zum Schreiben öffnen."
+
+#: builtin/add.c:290
+msgid "Could not write patch"
+msgstr "Konnte Patch nicht schreiben"
+
+#: builtin/add.c:295
+#, c-format
+msgid "Could not stat '%s'"
+msgstr "Konnte Verzeichnis '%s' nicht lesen"
+
+#: builtin/add.c:297
+msgid "Empty patch. Aborted."
+msgstr "Leerer Patch. Abgebrochen."
+
+#: builtin/add.c:303
+#, c-format
+msgid "Could not apply '%s'"
+msgstr "Konnte '%s' nicht anwenden."
+
+#: builtin/add.c:312
+msgid "The following paths are ignored by one of your .gitignore files:\n"
+msgstr ""
+"Die folgenden Pfade werden durch eine deiner \".gitignore\" Dateien "
+"ignoriert:\n"
+
+#: builtin/add.c:352
+#, c-format
+msgid "Use -f if you really want to add them.\n"
+msgstr "Verwende -f wenn du diese wirklich hinzufügen möchtest.\n"
+
+#: builtin/add.c:353
+msgid "no files added"
+msgstr "keine Dateien hinzugefügt"
+
+#: builtin/add.c:359
+msgid "adding files failed"
+msgstr "Hinzufügen von Dateien fehlgeschlagen"
+
+#: builtin/add.c:391
+msgid "-A and -u are mutually incompatible"
+msgstr "-A und -u sind zueinander inkompatibel"
+
+#: builtin/add.c:393
+msgid "Option --ignore-missing can only be used together with --dry-run"
+msgstr ""
+"Die Option --ignore-missing kann nur zusammen mit --dry-run benutzt werden."
+
+#: builtin/add.c:413
+#, c-format
+msgid "Nothing specified, nothing added.\n"
+msgstr "Nichts spezifiziert, nichts hinzugefügt.\n"
+
+#: builtin/add.c:414
+#, c-format
+msgid "Maybe you wanted to say 'git add .'?\n"
+msgstr "Wolltest du vielleicht 'git add .' sagen?\n"
+
+#: builtin/add.c:420 builtin/clean.c:95 builtin/commit.c:286 builtin/mv.c:82
+#: builtin/rm.c:162
+msgid "index file corrupt"
+msgstr "Bereitstellungsdatei beschädigt"
+
+#: builtin/add.c:480 builtin/apply.c:4108 builtin/mv.c:229 builtin/rm.c:260
+msgid "Unable to write new index file"
+msgstr "Konnte neue Bereitstellungsdatei nicht schreiben."
+
+#: builtin/apply.c:53
+msgid "git apply [options] [<patch>...]"
+msgstr "git apply [Optionen] [<Patch>...]"
+
+#: builtin/apply.c:106
+#, c-format
+msgid "unrecognized whitespace option '%s'"
+msgstr "nicht erkannte Option für Leerzeichen: '%s'"
+
+#: builtin/apply.c:121
+#, c-format
+msgid "unrecognized whitespace ignore option '%s'"
+msgstr "nicht erkannte Option zum Ignorieren von Leerzeichen: '%s'"
+
+#: builtin/apply.c:815
+#, c-format
+msgid "Cannot prepare timestamp regexp %s"
+msgstr "Kann regulären Ausdruck für Zeitstempel %s nicht verarbeiten"
+
+#: builtin/apply.c:824
+#, c-format
+msgid "regexec returned %d for input: %s"
+msgstr "Ausführung des regulären Ausdrucks gab %d zurück. Eingabe: %s"
+
+#: builtin/apply.c:905
+#, c-format
+msgid "unable to find filename in patch at line %d"
+msgstr "Konnte keinen Dateinamen in Zeile %d des Patches finden."
+
+#: builtin/apply.c:937
+#, c-format
+msgid "git apply: bad git-diff - expected /dev/null, got %s on line %d"
+msgstr ""
+"git apply: ungültiges 'git-diff' - erwartete /dev/null, erhielt %s in Zeile "
+"%d"
+
+#: builtin/apply.c:941
+#, c-format
+msgid "git apply: bad git-diff - inconsistent new filename on line %d"
+msgstr ""
+"git apply: ungültiges 'git-diff' - Inkonsistenter neuer Dateiname in Zeile %d"
+
+#: builtin/apply.c:942
+#, c-format
+msgid "git apply: bad git-diff - inconsistent old filename on line %d"
+msgstr ""
+"git apply: ungültiges 'git-diff' - Inkonsistenter alter Dateiname in Zeile %d"
+
+#: builtin/apply.c:949
+#, c-format
+msgid "git apply: bad git-diff - expected /dev/null on line %d"
+msgstr "git apply: ungültiges 'git-diff' - erwartete /dev/null in Zeile %d"
+
+#: builtin/apply.c:1394
+#, c-format
+msgid "recount: unexpected line: %.*s"
+msgstr "recount: unerwartete Zeile: %.*s"
+
+#: builtin/apply.c:1451
+#, c-format
+msgid "patch fragment without header at line %d: %.*s"
+msgstr "Patch-Fragment ohne Kopfbereich bei Zeile %d: %.*s"
+
+#: builtin/apply.c:1468
+#, c-format
+msgid ""
+"git diff header lacks filename information when removing %d leading pathname "
+"component (line %d)"
+msgid_plural ""
+"git diff header lacks filename information when removing %d leading pathname "
+"components (line %d)"
+msgstr[0] ""
+"Dem Kopfbereich von \"git diff\" fehlen Informationen zum Dateinamen, wenn "
+"%d vorangestellter Teil des Pfades entfernt wird (Zeile %d)"
+msgstr[1] ""
+"Dem Kopfbereich von \"git diff\" fehlen Informationen zum Dateinamen, wenn "
+"%d vorangestellte Teile des Pfades entfernt werden (Zeile %d)"
+
+#: builtin/apply.c:1628
+msgid "new file depends on old contents"
+msgstr "neue Datei hängt von alten Inhalten ab"
+
+#: builtin/apply.c:1630
+msgid "deleted file still has contents"
+msgstr "entfernte Datei hat noch Inhalte"
+
+#: builtin/apply.c:1656
+#, c-format
+msgid "corrupt patch at line %d"
+msgstr "fehlerhafter Patch bei Zeile %d"
+
+#: builtin/apply.c:1692
+#, c-format
+msgid "new file %s depends on old contents"
+msgstr "neue Datei %s hängt von alten Inhalten ab"
+
+#: builtin/apply.c:1694
+#, c-format
+msgid "deleted file %s still has contents"
+msgstr "entfernte Datei %s hat noch Inhalte"
+
+#: builtin/apply.c:1697
+#, c-format
+msgid "** warning: file %s becomes empty but is not deleted"
+msgstr "** Warnung: Datei %s wird leer, aber nicht entfernt."
+
+#: builtin/apply.c:1843
+#, c-format
+msgid "corrupt binary patch at line %d: %.*s"
+msgstr "fehlerhafter Binär-Patch bei Zeile %d: %.*s"
+
+#. there has to be one hunk (forward hunk)
+#: builtin/apply.c:1872
+#, c-format
+msgid "unrecognized binary patch at line %d"
+msgstr "nicht erkannter Binär-Patch bei Zeile %d"
+
+#: builtin/apply.c:1958
+#, c-format
+msgid "patch with only garbage at line %d"
+msgstr "Patch mit nutzlosen Informationen bei Zeile %d"
+
+#: builtin/apply.c:2048
+#, c-format
+msgid "unable to read symlink %s"
+msgstr "konnte symbolische Verknüpfung %s nicht lesen"
+
+#: builtin/apply.c:2052
+#, c-format
+msgid "unable to open or read %s"
+msgstr "konnte %s nicht öffnen oder lesen"
+
+#: builtin/apply.c:2123
+msgid "oops"
+msgstr "Ups"
+
+#: builtin/apply.c:2645
+#, c-format
+msgid "invalid start of line: '%c'"
+msgstr "Ungültiger Zeilenanfang: '%c'"
+
+#: builtin/apply.c:2763
+#, c-format
+msgid "Hunk #%d succeeded at %d (offset %d line)."
+msgid_plural "Hunk #%d succeeded at %d (offset %d lines)."
+msgstr[0] "Patch-Bereich #%d erfolgreich angewendet bei %d (%d Zeile versetzt)"
+msgstr[1] ""
+"Patch-Bereich #%d erfolgreich angewendet bei %d (%d Zeilen versetzt)"
+
+#: builtin/apply.c:2775
+#, c-format
+msgid "Context reduced to (%ld/%ld) to apply fragment at %d"
+msgstr "Kontext reduziert zu (%ld/%ld) um Patch-Bereich bei %d anzuwenden"
+
+#: builtin/apply.c:2781
+#, c-format
+msgid ""
+"while searching for:\n"
+"%.*s"
+msgstr ""
+"bei der Suche nach:\n"
+"%.*s"
+
+#: builtin/apply.c:2800
+#, c-format
+msgid "missing binary patch data for '%s'"
+msgstr "keine Daten in Binär-Patch für '%s'"
+
+#: builtin/apply.c:2903
+#, c-format
+msgid "binary patch does not apply to '%s'"
+msgstr "Konnte Binär-Patch nicht auf '%s' anwenden"
+
+#: builtin/apply.c:2909
+#, c-format
+msgid "binary patch to '%s' creates incorrect result (expecting %s, got %s)"
+msgstr ""
+"Binär-Patch für '%s' erzeugt falsches Ergebnis (erwartete %s, bekam %s)"
+
+#: builtin/apply.c:2930
+#, c-format
+msgid "patch failed: %s:%ld"
+msgstr "Anwendung des Patches fehlgeschlagen: %s:%ld"
+
+#: builtin/apply.c:3045
+#, c-format
+msgid "patch %s has been renamed/deleted"
+msgstr "Patch %s wurde umbenannt/gelöscht"
+
+#: builtin/apply.c:3052 builtin/apply.c:3069
+#, c-format
+msgid "read of %s failed"
+msgstr "Konnte %s nicht lesen"
+
+#: builtin/apply.c:3084
+msgid "removal patch leaves file contents"
+msgstr "Lösch-Patch hinterlässt Dateiinhalte"
+
+#: builtin/apply.c:3105
+#, c-format
+msgid "%s: already exists in working directory"
+msgstr "%s existiert bereits im Arbeitsverzeichnis"
+
+#: builtin/apply.c:3143
+#, c-format
+msgid "%s: has been deleted/renamed"
+msgstr "%s wurde gelöscht/umbenannt"
+
+#: builtin/apply.c:3148 builtin/apply.c:3179
+#, c-format
+msgid "%s: %s"
+msgstr "%s: %s"
+
+#: builtin/apply.c:3159
+#, c-format
+msgid "%s: does not exist in index"
+msgstr "%s ist nicht bereitgestellt"
+
+#: builtin/apply.c:3173
+#, c-format
+msgid "%s: does not match index"
+msgstr "%s entspricht nicht der Bereitstellung"
+
+#: builtin/apply.c:3190
+#, c-format
+msgid "%s: wrong type"
+msgstr "%s: falscher Typ"
+
+#: builtin/apply.c:3192
+#, c-format
+msgid "%s has type %o, expected %o"
+msgstr "%s ist vom Typ %o, erwartete %o"
+
+#: builtin/apply.c:3247
+#, c-format
+msgid "%s: already exists in index"
+msgstr "%s ist bereits bereitgestellt"
+
+#: builtin/apply.c:3267
+#, c-format
+msgid "new mode (%o) of %s does not match old mode (%o)"
+msgstr "neuer Modus (%o) von %s entspricht nicht dem alten Modus (%o)"
+
+#: builtin/apply.c:3272
+#, c-format
+msgid "new mode (%o) of %s does not match old mode (%o) of %s"
+msgstr "neuer Modus (%o) von %s entspricht nicht dem alten Modus (%o) von %s"
+
+#: builtin/apply.c:3280
+#, c-format
+msgid "%s: patch does not apply"
+msgstr "%s: Patch konnte nicht angewendet werden"
+
+#: builtin/apply.c:3293
+#, c-format
+msgid "Checking patch %s..."
+msgstr "Prüfe Patch %s..."
+
+#: builtin/apply.c:3348 builtin/checkout.c:212 builtin/reset.c:158
+#, c-format
+msgid "make_cache_entry failed for path '%s'"
+msgstr "make_cache_entry für Pfad '%s' fehlgeschlagen"
+
+#: builtin/apply.c:3491
+#, c-format
+msgid "unable to remove %s from index"
+msgstr "konnte %s nicht aus der Bereitstellung entfernen"
+
+#: builtin/apply.c:3518
+#, c-format
+msgid "corrupt patch for subproject %s"
+msgstr "fehlerhafter Patch für Unterprojekt %s"
+
+#: builtin/apply.c:3522
+#, c-format
+msgid "unable to stat newly created file '%s'"
+msgstr "konnte neu erstellte Datei '%s' nicht lesen"
+
+#: builtin/apply.c:3527
+#, c-format
+msgid "unable to create backing store for newly created file %s"
+msgstr "kann internen Speicher für eben erstellte Datei %s nicht erzeugen"
+
+#: builtin/apply.c:3530
+#, c-format
+msgid "unable to add cache entry for %s"
+msgstr "kann für %s keinen Eintrag in den Zwischenspeicher hinzufügen"
+
+#: builtin/apply.c:3563
+#, c-format
+msgid "closing file '%s'"
+msgstr "schließe Datei '%s'"
+
+#: builtin/apply.c:3612
+#, c-format
+msgid "unable to write file '%s' mode %o"
+msgstr "konnte Datei '%s' mit Modus %o nicht schreiben"
+
+#: builtin/apply.c:3668
+#, c-format
+msgid "Applied patch %s cleanly."
+msgstr "Patch %s sauber angewendet"
+
+#: builtin/apply.c:3676
+msgid "internal error"
+msgstr "interner Fehler"
+
+#. Say this even without --verbose
+#: builtin/apply.c:3679
+#, c-format
+msgid "Applying patch %%s with %d reject..."
+msgid_plural "Applying patch %%s with %d rejects..."
+msgstr[0] "Wende Patch %%s mit %d Zurückweisung an..."
+msgstr[1] "Wende Patch %%s mit %d Zurückweisungen an..."
+
+#: builtin/apply.c:3689
+#, c-format
+msgid "truncating .rej filename to %.*s.rej"
+msgstr "Verkürze Name von .rej Datei zu %.*s.rej"
+
+#: builtin/apply.c:3710
+#, c-format
+msgid "Hunk #%d applied cleanly."
+msgstr "Patch-Bereich #%d sauber angewendet."
+
+#: builtin/apply.c:3713
+#, c-format
+msgid "Rejected hunk #%d."
+msgstr "Patch-Bereich #%d zurückgewiesen."
+
+#: builtin/apply.c:3844
+msgid "unrecognized input"
+msgstr "nicht erkannte Eingabe"
+
+#: builtin/apply.c:3855
+msgid "unable to read index file"
+msgstr "Konnte Bereitstellungsdatei nicht lesen"
+
+#: builtin/apply.c:3970 builtin/apply.c:3973
+msgid "path"
+msgstr "Pfad"
+
+#: builtin/apply.c:3971
+msgid "don't apply changes matching the given path"
+msgstr "wendet keine Änderungen im angegebenen Pfad an"
+
+#: builtin/apply.c:3974
+msgid "apply changes matching the given path"
+msgstr "wendet Änderungen nur im angegebenen Pfad an"
+
+#: builtin/apply.c:3976
+msgid "num"
+msgstr "Anzahl"
+
+#: builtin/apply.c:3977
+msgid "remove <num> leading slashes from traditional diff paths"
+msgstr ""
+"entfernt <Anzahl> vorrangestellte Schrägstriche von herkömmlichen "
+"Differenzpfaden"
+
+#: builtin/apply.c:3980
+msgid "ignore additions made by the patch"
+msgstr "ignoriert hinzugefügte Zeilen des Patches"
+
+#: builtin/apply.c:3982
+msgid "instead of applying the patch, output diffstat for the input"
+msgstr ""
+"anstatt der Anwendung des Patches, wird der \"diffstat\" für die Eingabe "
+"ausgegeben"
+
+#: builtin/apply.c:3986
+msgid "shows number of added and deleted lines in decimal notation"
+msgstr ""
+"zeigt die Anzahl von hinzugefügten/entfernten Zeilen in Dezimalnotation"
+
+#: builtin/apply.c:3988
+msgid "instead of applying the patch, output a summary for the input"
+msgstr ""
+"anstatt der Anwendung des Patches, wird eine Zusammenfassung für die Eingabe "
+"ausgegeben"
+
+#: builtin/apply.c:3990
+msgid "instead of applying the patch, see if the patch is applicable"
+msgstr ""
+"anstatt der Anwendung des Patches, zeige ob Patch angewendet werden kann"
+
+#: builtin/apply.c:3992
+msgid "make sure the patch is applicable to the current index"
+msgstr ""
+"stellt sicher, dass der Patch in der aktuellen Bereitstellung angewendet "
+"werden kann"
+
+#: builtin/apply.c:3994
+msgid "apply a patch without touching the working tree"
+msgstr "wendet einen Patch an, ohne Änderungen im Arbeitszweig vorzunehmen"
+
+#: builtin/apply.c:3996
+msgid "also apply the patch (use with --stat/--summary/--check)"
+msgstr "wendet den Patch an (Benutzung mit --stat/--summary/--check)"
+
+#: builtin/apply.c:3998
+msgid "build a temporary index based on embedded index information"
+msgstr ""
+"erstellt eine temporäre Bereitstellung basierend auf den integrierten "
+"Bereitstellungsinformationen"
+
+#: builtin/apply.c:4000
+msgid "paths are separated with NUL character"
+msgstr "Pfade sind getrennt durch NUL Zeichen"
+
+#: builtin/apply.c:4003
+msgid "ensure at least <n> lines of context match"
+msgstr ""
+"stellt sicher, dass mindestens <Anzahl> Zeilen des Kontextes übereinstimmen"
+
+#: builtin/apply.c:4004
+msgid "action"
+msgstr "Aktion"
+
+#: builtin/apply.c:4005
+msgid "detect new or modified lines that have whitespace errors"
+msgstr "ermittelt neue oder geänderte Zeilen die Fehler in Leerzeichen haben"
+
+#: builtin/apply.c:4008 builtin/apply.c:4011
+msgid "ignore changes in whitespace when finding context"
+msgstr "ignoriert Änderungen in Leerzeichen bei der Suche des Kontextes"
+
+#: builtin/apply.c:4014
+msgid "apply the patch in reverse"
+msgstr "wendet den Patch in umgekehrter Reihenfolge an"
+
+#: builtin/apply.c:4016
+msgid "don't expect at least one line of context"
+msgstr "erwartet keinen Kontext"
+
+#: builtin/apply.c:4018
+msgid "leave the rejected hunks in corresponding *.rej files"
+msgstr ""
+"hinterlässt zurückgewiesene Patch-Bereiche in den entsprechenden *.rej "
+"Dateien"
+
+#: builtin/apply.c:4020
+msgid "allow overlapping hunks"
+msgstr "erlaubt sich überlappende Patch-Bereiche"
+
+#: builtin/apply.c:4021
+msgid "be verbose"
+msgstr "erweiterte Ausgaben"
+
+#: builtin/apply.c:4023
+msgid "tolerate incorrectly detected missing new-line at the end of file"
+msgstr "toleriert fehlerhaft erkannten fehlenden Zeilenumbruch am Dateiende"
+
+#: builtin/apply.c:4026
+msgid "do not trust the line counts in the hunk headers"
+msgstr "vertraut nicht den Zeilennummern im Kopf des Patch-Bereiches"
+
+#: builtin/apply.c:4028
+msgid "root"
+msgstr "Wurzelverzeichnis"
+
+#: builtin/apply.c:4029
+msgid "prepend <root> to all filenames"
+msgstr "stellt <Wurzelverzeichnis> vor alle Dateinamen"
+
+#: builtin/apply.c:4050
+msgid "--index outside a repository"
+msgstr "--index außerhalb eines Projektarchivs"
+
+#: builtin/apply.c:4053
+msgid "--cached outside a repository"
+msgstr "--cached außerhalb eines Projektarchivs"
+
+#: builtin/apply.c:4069
+#, c-format
+msgid "can't open patch '%s'"
+msgstr "kann Patch '%s' nicht öffnen"
+
+#: builtin/apply.c:4083
+#, c-format
+msgid "squelched %d whitespace error"
+msgid_plural "squelched %d whitespace errors"
+msgstr[0] "unterdrückte %d Fehler in Leerzeichen"
+msgstr[1] "unterdrückte %d Fehler in Leerzeichen"
+
+#: builtin/apply.c:4089 builtin/apply.c:4099
+#, c-format
+msgid "%d line adds whitespace errors."
+msgid_plural "%d lines add whitespace errors."
+msgstr[0] "%d Zeile fügt Fehler in Leerzeichen hinzu."
+msgstr[1] "%d Zeilen fügen Fehler in Leerzeichen hinzu."
+
+#: builtin/archive.c:17
+#, c-format
+msgid "could not create archive file '%s'"
+msgstr "Konnte Archiv-Datei '%s' nicht erstellen."
+
+#: builtin/archive.c:20
+msgid "could not redirect output"
+msgstr "Konnte Ausgabe nicht umleiten."
+
+#: builtin/archive.c:37
+msgid "git archive: Remote with no URL"
+msgstr "git archive: Externes Archiv ohne URL"
+
+#: builtin/archive.c:58
+msgid "git archive: expected ACK/NAK, got EOF"
+msgstr "git archive: habe ACK/NAK erwartet, aber EOF bekommen"
+
+#: builtin/archive.c:63
+#, c-format
+msgid "git archive: NACK %s"
+msgstr "git archive: NACK %s"
+
+#: builtin/archive.c:65
+#, c-format
+msgid "remote error: %s"
+msgstr "Fehler am anderen Ende: %s"
+
+#: builtin/archive.c:66
+msgid "git archive: protocol error"
+msgstr "git archive: Protokollfehler"
+
+#: builtin/archive.c:71
+msgid "git archive: expected a flush"
+msgstr "git archive: erwartete eine Spülung (flush)"
+
+#: builtin/branch.c:144
+#, c-format
+msgid ""
+"deleting branch '%s' that has been merged to\n"
+"         '%s', but not yet merged to HEAD."
+msgstr ""
+"entferne Zweig '%s', der zusammengeführt wurde mit\n"
+"         '%s', aber noch nicht mit der Zweigspitze (HEAD) zusammengeführt "
+"wurde."
+
+#: builtin/branch.c:148
+#, c-format
+msgid ""
+"not deleting branch '%s' that is not yet merged to\n"
+"         '%s', even though it is merged to HEAD."
+msgstr ""
+"entferne Zweig '%s' nicht, der noch nicht zusammengeführt wurde mit\n"
+"         '%s', obwohl er mit der Zweigspitze (HEAD) zusammengeführt wurde."
+
+#: builtin/branch.c:180
+msgid "cannot use -a with -d"
+msgstr "kann -a nicht mit -d benutzen"
+
+#: builtin/branch.c:186
+msgid "Couldn't look up commit object for HEAD"
+msgstr "Konnte Versionsobjekt für Zweigspitze (HEAD) nicht nachschlagen."
+
+#: builtin/branch.c:191
+#, c-format
+msgid "Cannot delete the branch '%s' which you are currently on."
+msgstr ""
+"Kann Zweig '%s' nicht entfernen, da du dich gerade auf diesem befindest."
+
+#: builtin/branch.c:202
+#, c-format
+msgid "remote branch '%s' not found."
+msgstr "externer Zweig '%s' nicht gefunden"
+
+#: builtin/branch.c:203
+#, c-format
+msgid "branch '%s' not found."
+msgstr "Zweig '%s' nicht gefunden."
+
+#: builtin/branch.c:210
+#, c-format
+msgid "Couldn't look up commit object for '%s'"
+msgstr "Konnte Versionsobjekt für '%s' nicht nachschlagen."
+
+#: builtin/branch.c:216
+#, c-format
+msgid ""
+"The branch '%s' is not fully merged.\n"
+"If you are sure you want to delete it, run 'git branch -D %s'."
+msgstr ""
+"Der Zweig '%s' ist nicht vollständig zusammengeführt.\n"
+"Wenn du sicher bist diesen Zweig zu entfernen, führe 'git branch -D %s' aus."
+
+#: builtin/branch.c:225
+#, c-format
+msgid "Error deleting remote branch '%s'"
+msgstr "Fehler beim Entfernen des externen Zweiges '%s'"
+
+#: builtin/branch.c:226
+#, c-format
+msgid "Error deleting branch '%s'"
+msgstr "Fehler beim Entfernen des Zweiges '%s'"
+
+#: builtin/branch.c:233
+#, c-format
+msgid "Deleted remote branch %s (was %s).\n"
+msgstr "Externer Zweig %s entfernt (war %s).\n"
+
+#: builtin/branch.c:234
+#, c-format
+msgid "Deleted branch %s (was %s).\n"
+msgstr "Zweig %s entfernt (war %s).\n"
+
+#: builtin/branch.c:239
+msgid "Update of config-file failed"
+msgstr "Aktualisierung der Konfigurationsdatei fehlgeschlagen."
+
+#: builtin/branch.c:337
+#, c-format
+msgid "branch '%s' does not point at a commit"
+msgstr "Zweig '%s' zeigt auf keine Version"
+
+#: builtin/branch.c:409
+#, c-format
+msgid "[%s: behind %d]"
+msgstr "[%s: %d hinterher]"
+
+#: builtin/branch.c:411
+#, c-format
+msgid "[behind %d]"
+msgstr "[%d hinterher]"
+
+#: builtin/branch.c:415
+#, c-format
+msgid "[%s: ahead %d]"
+msgstr "[%s: %d voraus]"
+
+#: builtin/branch.c:417
+#, c-format
+msgid "[ahead %d]"
+msgstr "[%d voraus]"
+
+#: builtin/branch.c:420
+#, c-format
+msgid "[%s: ahead %d, behind %d]"
+msgstr "[%s: %d voraus, %d hinterher]"
+
+#: builtin/branch.c:423
+#, c-format
+msgid "[ahead %d, behind %d]"
+msgstr "[%d voraus, %d hinterher]"
+
+#: builtin/branch.c:535
+msgid "(no branch)"
+msgstr "(kein Zweig)"
+
+#: builtin/branch.c:600
+msgid "some refs could not be read"
+msgstr "Konnte einige Referenzen nicht lesen"
+
+#: builtin/branch.c:613
+msgid "cannot rename the current branch while not on any."
+msgstr ""
+"Kann aktuellen Zweig nicht umbennen, solange du dich auf keinem befindest."
+
+#: builtin/branch.c:623
+#, c-format
+msgid "Invalid branch name: '%s'"
+msgstr "Ungültiger Zweig-Name: '%s'"
+
+#: builtin/branch.c:638
+msgid "Branch rename failed"
+msgstr "Umbenennung des Zweiges fehlgeschlagen"
+
+#: builtin/branch.c:642
+#, c-format
+msgid "Renamed a misnamed branch '%s' away"
+msgstr "falsch benannten Zweig '%s' umbenannt"
+
+#: builtin/branch.c:646
+#, c-format
+msgid "Branch renamed to %s, but HEAD is not updated!"
+msgstr "Zweig umbenannt zu %s, aber Zweigspitze (HEAD) ist nicht aktualisiert!"
+
+#: builtin/branch.c:653
+msgid "Branch is renamed, but update of config-file failed"
+msgstr ""
+"Zweig ist umbenannt, aber die Aktualisierung der Konfigurationsdatei ist "
+"fehlgeschlagen."
+
+#: builtin/branch.c:668
+#, c-format
+msgid "malformed object name %s"
+msgstr "Missgebildeter Objektname %s"
+
+#: builtin/branch.c:692
+#, c-format
+msgid "could not write branch description template: %s"
+msgstr "Konnte Beschreibungsvorlage für Zweig nicht schreiben: %s"
+
+#: builtin/branch.c:783
+msgid "Failed to resolve HEAD as a valid ref."
+msgstr "Konnte Zweigspitze (HEAD) nicht als gültige Referenz auflösen."
+
+#: builtin/branch.c:788 builtin/clone.c:558
+msgid "HEAD not found below refs/heads!"
+msgstr "Zweigspitze (HEAD) wurde nicht unter \"refs/heads\" gefunden!"
+
+#: builtin/branch.c:808
+msgid "--column and --verbose are incompatible"
+msgstr "--column und --verbose sind inkompatibel"
+
+#: builtin/branch.c:857
+msgid "-a and -r options to 'git branch' do not make sense with a branch name"
+msgstr ""
+"Die Optionen -a und -r bei 'git branch' machen mit einem Zweignamen keinen "
+"Sinn."
+
+#: builtin/bundle.c:47
+#, c-format
+msgid "%s is okay\n"
+msgstr "%s ist in Ordnung\n"
+
+#: builtin/bundle.c:56
+msgid "Need a repository to create a bundle."
+msgstr "Um ein Paket zu erstellen wird ein Projektarchiv benötigt."
+
+#: builtin/bundle.c:60
+msgid "Need a repository to unbundle."
+msgstr "Zum Entpacken wird ein Projektarchiv benötigt."
+
+#: builtin/checkout.c:113 builtin/checkout.c:146
+#, c-format
+msgid "path '%s' does not have our version"
+msgstr "Pfad '%s' hat nicht unsere Version."
+
+#: builtin/checkout.c:115 builtin/checkout.c:148
+#, c-format
+msgid "path '%s' does not have their version"
+msgstr "Pfad '%s' hat nicht deren Version."
+
+#: builtin/checkout.c:131
+#, c-format
+msgid "path '%s' does not have all necessary versions"
+msgstr "Pfad '%s' hat nicht alle notwendigen Versionen."
+
+#: builtin/checkout.c:175
+#, c-format
+msgid "path '%s' does not have necessary versions"
+msgstr "Pfad '%s' hat nicht die notwendigen Versionen."
+
+#: builtin/checkout.c:192
+#, c-format
+msgid "path '%s': cannot merge"
+msgstr "Pfad '%s': kann nicht zusammenführen"
+
+#: builtin/checkout.c:209
+#, c-format
+msgid "Unable to add merge result for '%s'"
+msgstr "Konnte Ergebnis der Zusammenführung von '%s' nicht hinzufügen."
+
+#: builtin/checkout.c:234 builtin/checkout.c:392
+msgid "corrupt index file"
+msgstr "beschädigte Bereitstellungsdatei"
+
+#: builtin/checkout.c:264 builtin/checkout.c:271
+#, c-format
+msgid "path '%s' is unmerged"
+msgstr "Pfad '%s' ist nicht zusammengeführt."
+
+#: builtin/checkout.c:302 builtin/checkout.c:498 builtin/clone.c:583
+#: builtin/merge.c:812
+msgid "unable to write new index file"
+msgstr "Konnte neue Bereitstellungsdatei nicht schreiben."
+
+#: builtin/checkout.c:319 builtin/diff.c:302 builtin/merge.c:408
+msgid "diff_setup_done failed"
+msgstr "diff_setup_done fehlgeschlagen"
+
+#: builtin/checkout.c:414
+msgid "you need to resolve your current index first"
+msgstr "Du musst zuerst deine aktuelle Bereitstellung auflösen."
+
+#: builtin/checkout.c:533
+#, c-format
+msgid "Can not do reflog for '%s'\n"
+msgstr "Konnte \"reflog\" für '%s' nicht durchführen\n"
+
+#: builtin/checkout.c:566
+msgid "HEAD is now at"
+msgstr "Zweigspitze (HEAD) ist jetzt bei"
+
+#: builtin/checkout.c:573
+#, c-format
+msgid "Reset branch '%s'\n"
+msgstr "Setze Zweig '%s' zurück\n"
+
+#: builtin/checkout.c:576
+#, c-format
+msgid "Already on '%s'\n"
+msgstr "Bereits auf '%s'\n"
+
+#: builtin/checkout.c:580
+#, c-format
+msgid "Switched to and reset branch '%s'\n"
+msgstr "Gewechselt zu zurückgesetztem Zweig '%s'\n"
+
+#: builtin/checkout.c:582
+#, c-format
+msgid "Switched to a new branch '%s'\n"
+msgstr "Gewechselt zu einem neuen Zweig '%s'\n"
+
+#: builtin/checkout.c:584
+#, c-format
+msgid "Switched to branch '%s'\n"
+msgstr "Gewechselt zu Zweig '%s'\n"
+
+#: builtin/checkout.c:640
+#, c-format
+msgid " ... and %d more.\n"
+msgstr " ... und %d weitere.\n"
+
+#. The singular version
+#: builtin/checkout.c:646
+#, c-format
+msgid ""
+"Warning: you are leaving %d commit behind, not connected to\n"
+"any of your branches:\n"
+"\n"
+"%s\n"
+msgid_plural ""
+"Warning: you are leaving %d commits behind, not connected to\n"
+"any of your branches:\n"
+"\n"
+"%s\n"
+msgstr[0] ""
+"Warnung: Du bist um %d Version hinterher, nicht verbunden zu\n"
+"einem deiner Zweige:\n"
+"\n"
+"%s\n"
+msgstr[1] ""
+"Warnung: Du bist um %d Versionen hinterher, nicht verbunden zu\n"
+"einem deiner Zweige:\n"
+"\n"
+"%s\n"
+
+#: builtin/checkout.c:664
+#, c-format
+msgid ""
+"If you want to keep them by creating a new branch, this may be a good time\n"
+"to do so with:\n"
+"\n"
+" git branch new_branch_name %s\n"
+"\n"
+msgstr ""
+"Wenn du diese durch einen neuen Zweig behalten möchtest, dann könnte jetzt\n"
+"ein guter Zeitpunkt sein dies zu tun mit:\n"
+"\n"
+" git branch neuer_zweig_name %s\n"
+"\n"
+
+#: builtin/checkout.c:694
+msgid "internal error in revision walk"
+msgstr "interner Fehler im Revisionsgang"
+
+#: builtin/checkout.c:698
+msgid "Previous HEAD position was"
+msgstr "Vorherige Position der Zweigspitze (HEAD) war"
+
+#: builtin/checkout.c:724
+msgid "You are on a branch yet to be born"
+msgstr "du bist auf einem Zweig, der noch geboren wird"
+
+#. case (1)
+#: builtin/checkout.c:855
+#, c-format
+msgid "invalid reference: %s"
+msgstr "Ungültige Referenz: %s"
+
+#. case (1): want a tree
+#: builtin/checkout.c:894
+#, c-format
+msgid "reference is not a tree: %s"
+msgstr "Referenz ist kein Baum: %s"
+
+#: builtin/checkout.c:974
+msgid "-B cannot be used with -b"
+msgstr "-B kann nicht mit -b benutzt werden"
+
+#: builtin/checkout.c:983
+msgid "--patch is incompatible with all other options"
+msgstr "--patch ist inkompatibel mit allen anderen Optionen"
+
+#: builtin/checkout.c:986
+msgid "--detach cannot be used with -b/-B/--orphan"
+msgstr "--detach kann nicht mit -b/-B/--orphan benutzt werden"
+
+#: builtin/checkout.c:988
+msgid "--detach cannot be used with -t"
+msgstr "--detach kann nicht mit -t benutzt werden"
+
+#: builtin/checkout.c:994
+msgid "--track needs a branch name"
+msgstr "--track benötigt einen Zweignamen"
+
+#: builtin/checkout.c:1001
+msgid "Missing branch name; try -b"
+msgstr "Vermisse Zweignamen; versuche -b"
+
+#: builtin/checkout.c:1007
+msgid "--orphan and -b|-B are mutually exclusive"
+msgstr "--orphan und -b|-B sind gegenseitig exklusiv"
+
+#: builtin/checkout.c:1009
+msgid "--orphan cannot be used with -t"
+msgstr "--orphan kann nicht mit -t benutzt werden"
+
+#: builtin/checkout.c:1019
+msgid "git checkout: -f and -m are incompatible"
+msgstr "git checkout: -f und -m sind inkompatibel"
+
+#: builtin/checkout.c:1053
+msgid "invalid path specification"
+msgstr "ungültige Pfadspezifikation"
+
+#: builtin/checkout.c:1061
+#, c-format
+msgid ""
+"git checkout: updating paths is incompatible with switching branches.\n"
+"Did you intend to checkout '%s' which can not be resolved as commit?"
+msgstr ""
+"git checkout: Die Aktualisierung von Pfaden ist inkompatibel mit dem Wechsel "
+"von Zweigen.\n"
+"Hast du beabsichtigt '%s' auszuchecken, welcher nicht als Version aufgelöst "
+"werden kann?"
+
+#: builtin/checkout.c:1063
+msgid "git checkout: updating paths is incompatible with switching branches."
+msgstr ""
+"git checkout: Die Aktualisierung von Pfaden ist inkompatibel mit dem Wechsel "
+"von Zweigen."
+
+#: builtin/checkout.c:1068
+msgid "git checkout: --detach does not take a path argument"
+msgstr "git checkout: --detach nimmt kein Pfad-Argument"
+
+#: builtin/checkout.c:1071
+msgid ""
+"git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
+"checking out of the index."
+msgstr ""
+"git checkout: --ours/--theirs, --force and --merge sind inkompatibel wenn\n"
+"du aus der Bereitstellung auscheckst."
+
+#: builtin/checkout.c:1090
+msgid "Cannot switch branch to a non-commit."
+msgstr "Kann Zweig nur zu einer Version wechseln."
+
+#: builtin/checkout.c:1093
+msgid "--ours/--theirs is incompatible with switching branches."
+msgstr "--ours/--theirs ist inkompatibel mit den Wechseln von Zweigen."
+
+#: builtin/clean.c:78
+msgid "-x and -X cannot be used together"
+msgstr "-x und -X können nicht zusammen benutzt werden"
+
+#: builtin/clean.c:82
+msgid ""
+"clean.requireForce set to true and neither -n nor -f given; refusing to clean"
+msgstr ""
+"clean.requireForce auf \"true\" gesetzt und weder -n noch -f gegeben; "
+"Säuberung verweigert"
+
+#: builtin/clean.c:85
+msgid ""
+"clean.requireForce defaults to true and neither -n nor -f given; refusing to "
+"clean"
+msgstr ""
+"clean.requireForce standardmäßig auf \"true\" gesetzt und weder -n noch -f "
+"gegeben; Säuberung verweigert"
+
+#: builtin/clean.c:155 builtin/clean.c:176
+#, c-format
+msgid "Would remove %s\n"
+msgstr "Würde %s entfernen\n"
+
+#: builtin/clean.c:159 builtin/clean.c:179
+#, c-format
+msgid "Removing %s\n"
+msgstr "Entferne %s\n"
+
+#: builtin/clean.c:162 builtin/clean.c:182
+#, c-format
+msgid "failed to remove %s"
+msgstr "Fehler beim Entfernen von %s"
+
+#: builtin/clean.c:166
+#, c-format
+msgid "Would not remove %s\n"
+msgstr "Würde nicht entfernen %s\n"
+
+#: builtin/clean.c:168
+#, c-format
+msgid "Not removing %s\n"
+msgstr "Entferne nicht %s\n"
+
+#: builtin/clone.c:243
+#, c-format
+msgid "reference repository '%s' is not a local directory."
+msgstr "Referenziertes Projektarchiv '%s' ist kein lokales Verzeichnis."
+
+#: builtin/clone.c:302
+#, c-format
+msgid "failed to open '%s'"
+msgstr "Fehler beim Öffnen von '%s'"
+
+#: builtin/clone.c:306
+#, c-format
+msgid "failed to create directory '%s'"
+msgstr "Fehler beim Erstellen von Verzeichnis '%s'"
+
+#: builtin/clone.c:308 builtin/diff.c:75
+#, c-format
+msgid "failed to stat '%s'"
+msgstr "Konnte '%s' nicht lesen"
+
+#: builtin/clone.c:310
+#, c-format
+msgid "%s exists and is not a directory"
+msgstr "%s existiert und ist kein Verzeichnis"
+
+#: builtin/clone.c:324
+#, c-format
+msgid "failed to stat %s\n"
+msgstr "Konnte %s nicht lesen\n"
+
+#: builtin/clone.c:341
+#, c-format
+msgid "failed to unlink '%s'"
+msgstr "Konnte '%s' nicht entfernen"
+
+#: builtin/clone.c:346
+#, c-format
+msgid "failed to create link '%s'"
+msgstr "Konnte Verknüpfung '%s' nicht erstellen"
+
+#: builtin/clone.c:350
+#, c-format
+msgid "failed to copy file to '%s'"
+msgstr "Konnte Datei nicht nach '%s' kopieren"
+
+#: builtin/clone.c:373
+#, c-format
+msgid "done.\n"
+msgstr "Fertig.\n"
+
+#: builtin/clone.c:440
+#, c-format
+msgid "Could not find remote branch %s to clone."
+msgstr "Konnte zu klonenden externer Zweig %s nicht finden."
+
+#: builtin/clone.c:549
+msgid "remote HEAD refers to nonexistent ref, unable to checkout.\n"
+msgstr ""
+"Externe Zweigspitze (HEAD) bezieht sich auf eine nicht existierende Referenz "
+"und kann nicht ausgecheckt werden.\n"
+
+#: builtin/clone.c:639
+msgid "Too many arguments."
+msgstr "Zu viele Argumente."
+
+#: builtin/clone.c:643
+msgid "You must specify a repository to clone."
+msgstr "Du musst ein Projektarchiv zum Klonen angeben."
+
+#: builtin/clone.c:654
+#, c-format
+msgid "--bare and --origin %s options are incompatible."
+msgstr "--bare und --origin %s Optionen sind inkompatibel."
+
+#: builtin/clone.c:668
+#, c-format
+msgid "repository '%s' does not exist"
+msgstr "Projektarchiv '%s' existiert nicht."
+
+#: builtin/clone.c:673
+msgid "--depth is ignored in local clones; use file:// instead."
+msgstr "--depth wird in lokalen Klonen ignoriert; benutze stattdessen file://."
+
+#: builtin/clone.c:683
+#, c-format
+msgid "destination path '%s' already exists and is not an empty directory."
+msgstr "Zielpfad '%s' existiert bereits und ist kein leeres Verzeichnis."
+
+#: builtin/clone.c:693
+#, c-format
+msgid "working tree '%s' already exists."
+msgstr "Arbeitsbaum '%s' existiert bereits."
+
+#: builtin/clone.c:706 builtin/clone.c:720
+#, c-format
+msgid "could not create leading directories of '%s'"
+msgstr "Konnte führende Verzeichnisse von '%s' nicht erstellen."
+
+#: builtin/clone.c:709
+#, c-format
+msgid "could not create work tree dir '%s'."
+msgstr "Konnte Arbeitsverzeichnis '%s' nicht erstellen."
+
+#: builtin/clone.c:728
+#, c-format
+msgid "Cloning into bare repository '%s'...\n"
+msgstr "Klone in bloßes Projektarchiv '%s'...\n"
+
+#: builtin/clone.c:730
+#, c-format
+msgid "Cloning into '%s'...\n"
+msgstr "Klone nach '%s'...\n"
+
+#: builtin/clone.c:786
+#, c-format
+msgid "Don't know how to clone %s"
+msgstr "Weiß nicht wie %s zu klonen ist."
+
+#: builtin/clone.c:835
+#, c-format
+msgid "Remote branch %s not found in upstream %s"
+msgstr "externer Zweig %s nicht im anderen Projektarchiv %s gefunden"
+
+#: builtin/clone.c:842
+msgid "You appear to have cloned an empty repository."
+msgstr "Du scheinst ein leeres Projektarchiv geklont zu haben."
+
+#: builtin/column.c:51
+msgid "--command must be the first argument"
+msgstr "Option --command muss zuerst angegeben werden"
+
+#: builtin/commit.c:43
+msgid ""
+"Your name and email address were configured automatically based\n"
+"on your username and hostname. Please check that they are accurate.\n"
+"You can suppress this message by setting them explicitly:\n"
+"\n"
+"    git config --global user.name \"Your Name\"\n"
+"    git config --global user.email you@example.com\n"
+"\n"
+"After doing this, you may fix the identity used for this commit with:\n"
+"\n"
+"    git commit --amend --reset-author\n"
+msgstr ""
+"Dein Name und E-Mail Adresse wurden automatisch auf Basis\n"
+"deines Benutzer- und Rechnernamens konfiguriert. Bitte prüfe, dass diese\n"
+"zutreffend sind. Du kannst diese Meldung unterdrücken, indem du diese\n"
+"explizit setzt:\n"
+"\n"
+"    git config --global user.name \"Dein Name\"\n"
+"    git config --global user.email deine@emailadresse.de\n"
+"\n"
+"Nachdem du das getan hast, kannst du deine Identität für diese Version "
+"ändern mit:\n"
+"\n"
+"    git commit --amend --reset-author\n"
+
+#: builtin/commit.c:55
+msgid ""
+"You asked to amend the most recent commit, but doing so would make\n"
+"it empty. You can repeat your command with --allow-empty, or you can\n"
+"remove the commit entirely with \"git reset HEAD^\".\n"
+msgstr ""
+"Du fragtest die jüngste Version nachzubessern, aber das würde diese leer\n"
+"machen. Du kannst Dein Kommando mit --allow-empty wiederholen, oder die\n"
+"Version mit \"git reset HEAD^\" vollständig entfernen.\n"
+
+#: builtin/commit.c:60
+msgid ""
+"The previous cherry-pick is now empty, possibly due to conflict resolution.\n"
+"If you wish to commit it anyway, use:\n"
+"\n"
+"    git commit --allow-empty\n"
+"\n"
+"Otherwise, please use 'git reset'\n"
+msgstr ""
+"Der letzte \"cherry-pick\" ist jetzt leer, möglicherweise durch eine "
+"Konfliktauflösung.\n"
+"Wenn du dies trotzdem eintragen willst, benutze:\n"
+"\n"
+"    git commit --allow-empty\n"
+"\n"
+"Andernfalls benutze bitte 'git reset'\n"
+
+#: builtin/commit.c:253
+msgid "failed to unpack HEAD tree object"
+msgstr "Fehler beim Entpacken des Baum-Objektes der Zweigspitze (HEAD)."
+
+#: builtin/commit.c:295
+msgid "unable to create temporary index"
+msgstr "Konnte temporäre Bereitstellung nicht erstellen."
+
+#: builtin/commit.c:301
+msgid "interactive add failed"
+msgstr "interaktives Hinzufügen fehlgeschlagen"
+
+#: builtin/commit.c:334 builtin/commit.c:355 builtin/commit.c:405
+msgid "unable to write new_index file"
+msgstr "Konnte new_index Datei nicht schreiben"
+
+#: builtin/commit.c:386
+msgid "cannot do a partial commit during a merge."
+msgstr ""
+"Kann keine partielle Eintragung durchführen, während eine Zusammenführung im "
+"Gange ist."
+
+#: builtin/commit.c:388
+msgid "cannot do a partial commit during a cherry-pick."
+msgstr ""
+"Kann keine partielle Eintragung durchführen, während \"cherry-pick\" im "
+"Gange ist."
+
+#: builtin/commit.c:398
+msgid "cannot read the index"
+msgstr "Kann Bereitstellung nicht lesen"
+
+#: builtin/commit.c:418
+msgid "unable to write temporary index file"
+msgstr "Konnte temporäre Bereitstellungsdatei nicht schreiben."
+
+#: builtin/commit.c:493 builtin/commit.c:499
+#, c-format
+msgid "invalid commit: %s"
+msgstr "Ungültige Version: %s"
+
+#: builtin/commit.c:522
+msgid "malformed --author parameter"
+msgstr "Fehlerhafter --author Parameter"
+
+#: builtin/commit.c:582
+#, c-format
+msgid "Malformed ident string: '%s'"
+msgstr "Fehlerhafte Identifikations-String: '%s'"
+
+#: builtin/commit.c:620 builtin/commit.c:653 builtin/commit.c:967
+#, c-format
+msgid "could not lookup commit %s"
+msgstr "Konnte Version %s nicht nachschlagen"
+
+#: builtin/commit.c:632 builtin/shortlog.c:296
+#, c-format
+msgid "(reading log message from standard input)\n"
+msgstr "(lese Log-Nachricht von Standard-Eingabe)\n"
+
+#: builtin/commit.c:634
+msgid "could not read log from standard input"
+msgstr "Konnte Log nicht von Standard-Eingabe lesen."
+
+#: builtin/commit.c:638
+#, c-format
+msgid "could not read log file '%s'"
+msgstr "Konnte Log-Datei '%s' nicht lesen"
+
+#: builtin/commit.c:644
+msgid "commit has empty message"
+msgstr "Version hat eine leere Beschreibung"
+
+#: builtin/commit.c:660
+msgid "could not read MERGE_MSG"
+msgstr "Konnte MERGE_MSG nicht lesen"
+
+#: builtin/commit.c:664
+msgid "could not read SQUASH_MSG"
+msgstr "Konnte SQUASH_MSG nicht lesen"
+
+#: builtin/commit.c:668
+#, c-format
+msgid "could not read '%s'"
+msgstr "Konnte '%s' nicht lesen"
+
+#: builtin/commit.c:720
+msgid "could not write commit template"
+msgstr "Konnte Versionsvorlage nicht schreiben"
+
+#: builtin/commit.c:731
+#, c-format
+msgid ""
+"\n"
+"It looks like you may be committing a merge.\n"
+"If this is not correct, please remove the file\n"
+"\t%s\n"
+"and try again.\n"
+msgstr ""
+"\n"
+"Es sieht so aus, als trägst du eine Zusammenführung ein.\n"
+"Falls das nicht korrekt ist, entferne bitte die Datei\n"
+"\t%s\n"
+"und versuche es erneut.\n"
+
+#: builtin/commit.c:736
+#, c-format
+msgid ""
+"\n"
+"It looks like you may be committing a cherry-pick.\n"
+"If this is not correct, please remove the file\n"
+"\t%s\n"
+"and try again.\n"
+msgstr ""
+"\n"
+"Es sieht so aus, als trägst du ein \"cherry-pick\" ein.\n"
+"Falls das nicht korrekt ist, entferne bitte die Datei\n"
+"\t%s\n"
+"und versuche es erneut.\n"
+
+#: builtin/commit.c:748
+msgid ""
+"Please enter the commit message for your changes. Lines starting\n"
+"with '#' will be ignored, and an empty message aborts the commit.\n"
+msgstr ""
+"Bitte gebe eine Versionsbeschreibung für deine Änderungen ein. Zeilen,\n"
+"die mit '#' beginnen, werden ignoriert, und eine leere Versionsbeschreibung\n"
+"bricht die Eintragung ab.\n"
+
+#: builtin/commit.c:753
+msgid ""
+"Please enter the commit message for your changes. Lines starting\n"
+"with '#' will be kept; you may remove them yourself if you want to.\n"
+"An empty message aborts the commit.\n"
+msgstr ""
+"Bitte gebe eine Versionsbeschreibung für deine Änderungen ein. Zeilen, die\n"
+"mit '#' beginnen, werden beibehalten; wenn du möchtest, kannst du diese "
+"entfernen.\n"
+"Eine leere Versionsbeschreibung bricht die Eintragung ab.\n"
+
+#: builtin/commit.c:766
+#, c-format
+msgid "%sAuthor:    %s"
+msgstr "%sAutor:    %s"
+
+#: builtin/commit.c:773
+#, c-format
+msgid "%sCommitter: %s"
+msgstr "%sEintragender: %s"
+
+#: builtin/commit.c:793
+msgid "Cannot read index"
+msgstr "Kann Bereitstellung nicht lesen"
+
+#: builtin/commit.c:830
+msgid "Error building trees"
+msgstr "Fehler beim Erzeugen der Zweige"
+
+#: builtin/commit.c:845 builtin/tag.c:361
+#, c-format
+msgid "Please supply the message using either -m or -F option.\n"
+msgstr "Bitte liefere eine Beschreibung entweder mit der Option -m oder -F.\n"
+
+#: builtin/commit.c:942
+#, c-format
+msgid "No existing author found with '%s'"
+msgstr "Kein existierender Autor mit '%s' gefunden."
+
+#: builtin/commit.c:957 builtin/commit.c:1157
+#, c-format
+msgid "Invalid untracked files mode '%s'"
+msgstr "Ungültiger Modus '%s' für unbeobachtete Dateien"
+
+#: builtin/commit.c:997
+msgid "Using both --reset-author and --author does not make sense"
+msgstr "Verwendung von --reset-author und --author macht keinen Sinn."
+
+#: builtin/commit.c:1008
+msgid "You have nothing to amend."
+msgstr "Du hast nichts zum nachbessern."
+
+#: builtin/commit.c:1011
+msgid "You are in the middle of a merge -- cannot amend."
+msgstr "Eine Zusammenführung ist im Gange -- kann nicht nachbessern."
+
+#: builtin/commit.c:1013
+msgid "You are in the middle of a cherry-pick -- cannot amend."
+msgstr "\"cherry-pick\" ist im Gange -- kann nicht nachbessern."
+
+#: builtin/commit.c:1016
+msgid "Options --squash and --fixup cannot be used together"
+msgstr ""
+"Die Optionen --squash und --fixup können nicht gemeinsam benutzt werden."
+
+#: builtin/commit.c:1026
+msgid "Only one of -c/-C/-F/--fixup can be used."
+msgstr "Nur eines von -c/-C/-F/--fixup kann benutzt werden."
+
+#: builtin/commit.c:1028
+msgid "Option -m cannot be combined with -c/-C/-F/--fixup."
+msgstr "Option -m kann nicht mit -c/-C/-F/--fixup kombiniert werden"
+
+#: builtin/commit.c:1036
+msgid "--reset-author can be used only with -C, -c or --amend."
+msgstr "--reset--author kann nur mit -C, -c oder --amend benutzt werden"
+
+#: builtin/commit.c:1053
+msgid "Only one of --include/--only/--all/--interactive/--patch can be used."
+msgstr ""
+"Nur eines von --include/--only/--all/--interactive/--patch kann benutzt "
+"werden."
+
+#: builtin/commit.c:1055
+msgid "No paths with --include/--only does not make sense."
+msgstr "--include/--only machen ohne Pfade keinen Sinn."
+
+#: builtin/commit.c:1057
+msgid "Clever... amending the last one with dirty index."
+msgstr ""
+"Klug... die letzte Version mit einer unsauberen Bereitstellung nachbessern."
+
+#: builtin/commit.c:1059
+msgid "Explicit paths specified without -i nor -o; assuming --only paths..."
+msgstr ""
+"Explizite Pfade ohne -i oder -o angegeben; unter der Annahme von --only "
+"Pfaden..."
+
+#: builtin/commit.c:1069 builtin/tag.c:577
+#, c-format
+msgid "Invalid cleanup mode %s"
+msgstr "Ungültiger \"cleanup\" Modus %s"
+
+#: builtin/commit.c:1074
+msgid "Paths with -a does not make sense."
+msgstr "Pfade mit -a machen keinen Sinn."
+
+#: builtin/commit.c:1257
+msgid "couldn't look up newly created commit"
+msgstr "Konnte neu erstellte Version nicht nachschlagen."
+
+#: builtin/commit.c:1259
+msgid "could not parse newly created commit"
+msgstr "Konnte neulich erstellte Version nicht analysieren."
+
+#: builtin/commit.c:1300
+msgid "detached HEAD"
+msgstr "losgelöste Zweigspitze (HEAD)"
+
+#: builtin/commit.c:1302
+msgid " (root-commit)"
+msgstr " (Basis-Version)"
+
+#: builtin/commit.c:1446
+msgid "could not parse HEAD commit"
+msgstr "Konnte Version der Zweigspitze (HEAD) nicht analysieren."
+
+#: builtin/commit.c:1484 builtin/merge.c:509
+#, c-format
+msgid "could not open '%s' for reading"
+msgstr "Konnte '%s' nicht zum Lesen öffnen."
+
+#: builtin/commit.c:1491
+#, c-format
+msgid "Corrupt MERGE_HEAD file (%s)"
+msgstr "Beschädigte MERGE_HEAD-Datei (%s)"
+
+#: builtin/commit.c:1498
+msgid "could not read MERGE_MODE"
+msgstr "Konnte MERGE_MODE nicht lesen"
+
+#: builtin/commit.c:1517
+#, c-format
+msgid "could not read commit message: %s"
+msgstr "Konnte Versionsbeschreibung nicht lesen: %s"
+
+#: builtin/commit.c:1531
+#, c-format
+msgid "Aborting commit; you did not edit the message.\n"
+msgstr "Eintragung abgebrochen; du hast die Beschreibung nicht editiert.\n"
+
+#: builtin/commit.c:1536
+#, c-format
+msgid "Aborting commit due to empty commit message.\n"
+msgstr "Eintragung aufgrund leerer Versionsbeschreibung abgebrochen.\n"
+
+#: builtin/commit.c:1551 builtin/merge.c:936 builtin/merge.c:961
+msgid "failed to write commit object"
+msgstr "Fehler beim Schreiben des Versionsobjektes."
+
+#: builtin/commit.c:1572
+msgid "cannot lock HEAD ref"
+msgstr "Kann Referenz der Zweigspitze (HEAD) nicht sperren."
+
+#: builtin/commit.c:1576
+msgid "cannot update HEAD ref"
+msgstr "Kann Referenz der Zweigspitze (HEAD) nicht aktualisieren."
+
+#: builtin/commit.c:1587
+msgid ""
+"Repository has been updated, but unable to write\n"
+"new_index file. Check that disk is not full or quota is\n"
+"not exceeded, and then \"git reset HEAD\" to recover."
+msgstr ""
+"Das Projektarchiv wurde aktualisiert, aber die \"new_index\"-Datei\n"
+"konnte nicht geschrieben werden. Prüfe, dass dein Speicher nicht\n"
+"voll und Dein Kontingent nicht aufgebraucht ist und führe\n"
+"anschließend \"git reset HEAD\" zu Wiederherstellung aus."
+
+#: builtin/describe.c:234
+#, c-format
+msgid "annotated tag %s not available"
+msgstr "annotierte Markierung %s ist nicht verfügbar"
+
+#: builtin/describe.c:238
+#, c-format
+msgid "annotated tag %s has no embedded name"
+msgstr "annotierte Markierung %s hat keinen eingebetteten Namen"
+
+#: builtin/describe.c:240
+#, c-format
+msgid "tag '%s' is really '%s' here"
+msgstr "Markierung '%s' ist eigentlich '%s' hier"
+
+#: builtin/describe.c:267
+#, c-format
+msgid "Not a valid object name %s"
+msgstr "%s ist kein gültiger Objekt-Name"
+
+#: builtin/describe.c:270
+#, c-format
+msgid "%s is not a valid '%s' object"
+msgstr "%s ist kein gültiges '%s' Objekt"
+
+#: builtin/describe.c:287
+#, c-format
+msgid "no tag exactly matches '%s'"
+msgstr "kein Markierung entspricht exakt '%s'"
+
+#: builtin/describe.c:289
+#, c-format
+msgid "searching to describe %s\n"
+msgstr "suche zur Beschreibung von %s\n"
+
+#: builtin/describe.c:329
+#, c-format
+msgid "finished search at %s\n"
+msgstr "beendete Suche bei %s\n"
+
+#: builtin/describe.c:353
+#, c-format
+msgid ""
+"No annotated tags can describe '%s'.\n"
+"However, there were unannotated tags: try --tags."
+msgstr ""
+"Keine annotierten Markierungen können '%s' beschreiben.\n"
+"Jedoch gab es nicht annotierte Markierungen: versuche --tags."
+
+#: builtin/describe.c:357
+#, c-format
+msgid ""
+"No tags can describe '%s'.\n"
+"Try --always, or create some tags."
+msgstr ""
+"Keine Markierungen können '%s' beschreiben.\n"
+"Versuche --always oder erstelle einige Markierungen."
+
+#: builtin/describe.c:378
+#, c-format
+msgid "traversed %lu commits\n"
+msgstr "%lu Versionen durchlaufen\n"
+
+#: builtin/describe.c:381
+#, c-format
+msgid ""
+"more than %i tags found; listed %i most recent\n"
+"gave up search at %s\n"
+msgstr ""
+"mehr als %i Markierungen gefunden; Führe die ersten %i auf\n"
+"Suche bei %s aufgegeben\n"
+
+#: builtin/describe.c:436
+msgid "--long is incompatible with --abbrev=0"
+msgstr "--long ist inkompatibel mit --abbrev=0"
+
+#: builtin/describe.c:462
+msgid "No names found, cannot describe anything."
+msgstr "Keine Namen gefunden, kann nichts beschreiben."
+
+#: builtin/describe.c:482
+msgid "--dirty is incompatible with committishes"
+msgstr "--dirty ist inkompatibel mit Versionen"
+
+#: builtin/diff.c:77
+#, c-format
+msgid "'%s': not a regular file or symlink"
+msgstr "'%s': keine reguläre Datei oder symbolischer Link"
+
+#: builtin/diff.c:220
+#, c-format
+msgid "invalid option: %s"
+msgstr "Ungültige Option: %s"
+
+#: builtin/diff.c:297
+msgid "Not a git repository"
+msgstr "Kein Git-Projektarchiv"
+
+#: builtin/diff.c:341
+#, c-format
+msgid "invalid object '%s' given."
+msgstr "Objekt '%s' ist ungültig."
+
+#: builtin/diff.c:346
+#, c-format
+msgid "more than %d trees given: '%s'"
+msgstr "Mehr als %d Zweige angegeben: '%s'"
+
+#: builtin/diff.c:356
+#, c-format
+msgid "more than two blobs given: '%s'"
+msgstr "Mehr als zwei Blobs angegeben: '%s'"
+
+#: builtin/diff.c:364
+#, c-format
+msgid "unhandled object '%s' given."
+msgstr "unbehandeltes Objekt '%s' angegeben"
+
+#: builtin/fetch.c:200
+msgid "Couldn't find remote ref HEAD"
+msgstr "Konnte externe Referenz der Zweigspitze (HEAD) nicht finden."
+
+#: builtin/fetch.c:253
+#, c-format
+msgid "object %s not found"
+msgstr "Objekt %s nicht gefunden"
+
+#: builtin/fetch.c:259
+msgid "[up to date]"
+msgstr "[aktuell]"
+
+#: builtin/fetch.c:273
+#, c-format
+msgid "! %-*s %-*s -> %s  (can't fetch in current branch)"
+msgstr "! %-*s %-*s -> %s  (kann nicht im aktuellen Zweig anfordern)"
+
+#: builtin/fetch.c:274 builtin/fetch.c:360
+msgid "[rejected]"
+msgstr "[zurückgewiesen]"
+
+#: builtin/fetch.c:285
+msgid "[tag update]"
+msgstr "[Markierungsaktualisierung]"
+
+#: builtin/fetch.c:287 builtin/fetch.c:322 builtin/fetch.c:340
+msgid "  (unable to update local ref)"
+msgstr "  (kann lokale Referenz nicht aktualisieren)"
+
+#: builtin/fetch.c:305
+msgid "[new tag]"
+msgstr "[neue Markierung]"
+
+#: builtin/fetch.c:308
+msgid "[new branch]"
+msgstr "[neuer Zweig]"
+
+#: builtin/fetch.c:311
+msgid "[new ref]"
+msgstr "[neue Referenz]"
+
+#: builtin/fetch.c:356
+msgid "unable to update local ref"
+msgstr "kann lokale Referenz nicht aktualisieren"
+
+#: builtin/fetch.c:356
+msgid "forced update"
+msgstr "Aktualisierung erzwungen"
+
+#: builtin/fetch.c:362
+msgid "(non-fast-forward)"
+msgstr "(kein Vorspulen)"
+
+#: builtin/fetch.c:393 builtin/fetch.c:685
+#, c-format
+msgid "cannot open %s: %s\n"
+msgstr "kann %s nicht öffnen: %s\n"
+
+#: builtin/fetch.c:402
+#, c-format
+msgid "%s did not send all necessary objects\n"
+msgstr "%s hat nicht alle erforderlichen Objekte gesendet\n"
+
+#: builtin/fetch.c:488
+#, c-format
+msgid "From %.*s\n"
+msgstr "Von %.*s\n"
+
+#: builtin/fetch.c:499
+#, c-format
+msgid ""
+"some local refs could not be updated; try running\n"
+" 'git remote prune %s' to remove any old, conflicting branches"
+msgstr ""
+"Einige lokale Referenzen konnten nicht aktualisiert werden; versuche\n"
+"'git remote prune %s' um jeden älteren, widersprüchlichen Zweig zu entfernen."
+
+#: builtin/fetch.c:549
+#, c-format
+msgid "   (%s will become dangling)"
+msgstr "   (%s wird unreferenziert)"
+
+#: builtin/fetch.c:550
+#, c-format
+msgid "   (%s has become dangling)"
+msgstr "   (%s wurde unreferenziert)"
+
+#: builtin/fetch.c:557
+msgid "[deleted]"
+msgstr "[gelöscht]"
+
+#: builtin/fetch.c:558 builtin/remote.c:1055
+msgid "(none)"
+msgstr "(nichts)"
+
+#: builtin/fetch.c:675
+#, c-format
+msgid "Refusing to fetch into current branch %s of non-bare repository"
+msgstr ""
+"Das Anfordern in den aktuellen Zweig %s von einem nicht-bloßen Projektarchiv "
+"wurde verweigert."
+
+#: builtin/fetch.c:709
+#, c-format
+msgid "Don't know how to fetch from %s"
+msgstr "Weiß nicht wie von %s angefordert wird."
+
+#: builtin/fetch.c:786
+#, c-format
+msgid "Option \"%s\" value \"%s\" is not valid for %s"
+msgstr "Option \"%s\" Wert \"%s\" ist nicht gültig für %s"
+
+#: builtin/fetch.c:789
+#, c-format
+msgid "Option \"%s\" is ignored for %s\n"
+msgstr "Option \"%s\" wird ignoriert für %s\n"
+
+#: builtin/fetch.c:888
+#, c-format
+msgid "Fetching %s\n"
+msgstr "Fordere an von %s\n"
+
+#: builtin/fetch.c:890 builtin/remote.c:100
+#, c-format
+msgid "Could not fetch %s"
+msgstr "Konnte nicht von %s anfordern"
+
+#: builtin/fetch.c:907
+msgid ""
+"No remote repository specified.  Please, specify either a URL or a\n"
+"remote name from which new revisions should be fetched."
+msgstr ""
+"Kein externes Projektarchiv angegeben. Bitte gebe entweder eine URL\n"
+"oder den Namen des externen Archivs an, von welchem neue\n"
+"Versionen angefordert werden sollen."
+
+#: builtin/fetch.c:927
+msgid "You need to specify a tag name."
+msgstr "Du musst den Namen der Markierung angeben."
+
+#: builtin/fetch.c:979
+msgid "fetch --all does not take a repository argument"
+msgstr "fetch --all akzeptiert kein Projektarchiv als Argument"
+
+#: builtin/fetch.c:981
+msgid "fetch --all does not make sense with refspecs"
+msgstr "fetch --all macht keinen Sinn mit Referenzspezifikationen"
+
+#: builtin/fetch.c:992
+#, c-format
+msgid "No such remote or remote group: %s"
+msgstr "Kein externes Archiv (einzeln oder Gruppe): %s"
+
+#: builtin/fetch.c:1000
+msgid "Fetching a group and specifying refspecs does not make sense"
+msgstr ""
+"Abholen einer Gruppe mit Angabe von Referenzspezifikationen macht keinen "
+"Sinn."
+
+#: builtin/gc.c:63
+#, c-format
+msgid "Invalid %s: '%s'"
+msgstr "Ungültiger %s: '%s'"
+
+#: builtin/gc.c:90
+#, c-format
+msgid "insanely long object directory %.*s"
+msgstr "zu langes Objekt-Verzeichnis %.*s"
+
+#: builtin/gc.c:221
+#, c-format
+msgid "Auto packing the repository for optimum performance.\n"
+msgstr ""
+"Die Datenbank des Projektarchivs wird für eine optimale Performance "
+"komprimiert.\n"
+
+#: builtin/gc.c:224
+#, c-format
+msgid ""
+"Auto packing the repository for optimum performance. You may also\n"
+"run \"git gc\" manually. See \"git help gc\" for more information.\n"
+msgstr ""
+"Die Datenbank des Projektarchivs wird für eine optimale Performance\n"
+"komprimiert. Du kannst auch \"git gc\" manuell ausführen.\n"
+"Siehe \"git help gc\" für weitere Informationen.\n"
+
+#: builtin/gc.c:251
+msgid ""
+"There are too many unreachable loose objects; run 'git prune' to remove them."
+msgstr ""
+"Es gibt zu viele unerreichbare lose Objekte; führe 'git prune' aus, um diese "
+"zu entfernen."
+
+#: builtin/grep.c:216
+#, c-format
+msgid "grep: failed to create thread: %s"
+msgstr "grep: Fehler beim Erzeugen eines Thread: %s"
+
+#: builtin/grep.c:402
+#, c-format
+msgid "Failed to chdir: %s"
+msgstr "Fehler beim Verzeichniswechsel: %s"
+
+#: builtin/grep.c:478 builtin/grep.c:512
+#, c-format
+msgid "unable to read tree (%s)"
+msgstr "konnte Zweig (%s) nicht lesen"
+
+#: builtin/grep.c:526
+#, c-format
+msgid "unable to grep from object of type %s"
+msgstr "kann \"grep\" nicht mit Objekten des Typs %s durchführen"
+
+#: builtin/grep.c:584
+#, c-format
+msgid "switch `%c' expects a numerical value"
+msgstr "Schalter '%c' erwartet einen numerischen Wert"
+
+#: builtin/grep.c:601
+#, c-format
+msgid "cannot open '%s'"
+msgstr "kann '%s' nicht öffnen"
+
+#: builtin/grep.c:885
+msgid "no pattern given."
+msgstr "keine Muster angegeben"
+
+#: builtin/grep.c:899
+#, c-format
+msgid "bad object %s"
+msgstr "ungültiges Objekt %s"
+
+#: builtin/grep.c:940
+msgid "--open-files-in-pager only works on the worktree"
+msgstr "--open-files-in-pager arbeitet nur innerhalb des Arbeitsbaums"
+
+#: builtin/grep.c:963
+msgid "--cached or --untracked cannot be used with --no-index."
+msgstr "--cached oder --untracked kann nicht mit --no-index benutzt werden"
+
+#: builtin/grep.c:968
+msgid "--no-index or --untracked cannot be used with revs."
+msgstr "--no-index oder --untracked kann nicht mit Versionen benutzt werden"
+
+#: builtin/grep.c:971
+msgid "--[no-]exclude-standard cannot be used for tracked contents."
+msgstr ""
+"--[no-]exlude-standard kann nicht mit beobachteten Inhalten benutzt werden"
+
+#: builtin/grep.c:979
+msgid "both --cached and trees are given."
+msgstr "sowohl --cached als auch Zweige gegeben"
+
+#: builtin/help.c:63
+#, c-format
+msgid "unrecognized help format '%s'"
+msgstr "nicht erkanntes Hilfeformat: %s"
+
+#: builtin/help.c:91
+msgid "Failed to start emacsclient."
+msgstr "Konnte emacsclient nicht starten."
+
+#: builtin/help.c:104
+msgid "Failed to parse emacsclient version."
+msgstr "Konnte Version des emacsclient nicht parsen."
+
+#: builtin/help.c:112
+#, c-format
+msgid "emacsclient version '%d' too old (< 22)."
+msgstr "Version des emacsclient '%d' ist zu alt (< 22)."
+
+#: builtin/help.c:130 builtin/help.c:158 builtin/help.c:167 builtin/help.c:175
+#, c-format
+msgid "failed to exec '%s': %s"
+msgstr "Fehler beim Ausführen von '%s': %s"
+
+#: builtin/help.c:215
+#, c-format
+msgid ""
+"'%s': path for unsupported man viewer.\n"
+"Please consider using 'man.<tool>.cmd' instead."
+msgstr ""
+"'%s': Pfad für nicht unterstützten Handbuchbetrachter.\n"
+"Du könntest stattdessen 'man.<Werkzeug>.cmd' benutzen."
+
+#: builtin/help.c:227
+#, c-format
+msgid ""
+"'%s': cmd for supported man viewer.\n"
+"Please consider using 'man.<tool>.path' instead."
+msgstr ""
+"'%s': Kommando für unterstützten Handbuchbetrachter.\n"
+"Du könntest stattdessen 'man.<Werkzeug>.path' benutzen."
+
+#: builtin/help.c:291
+msgid "The most commonly used git commands are:"
+msgstr "Die allgemein verwendeten Git-Kommandos sind:"
+
+#: builtin/help.c:359
+#, c-format
+msgid "'%s': unknown man viewer."
+msgstr "'%s': unbekannter Handbuch-Betrachter."
+
+#: builtin/help.c:376
+msgid "no man viewer handled the request"
+msgstr "kein Handbuch-Betrachter konnte mit dieser Anfrage umgehen"
+
+#: builtin/help.c:384
+msgid "no info viewer handled the request"
+msgstr "kein Informations-Betrachter konnte mit dieser Anfrage umgehen"
+
+#: builtin/help.c:395
+#, c-format
+msgid "'%s': not a documentation directory."
+msgstr "'%s' ist kein Dokumentationsverzeichnis"
+
+#: builtin/help.c:436 builtin/help.c:443
+#, c-format
+msgid "usage: %s%s"
+msgstr "Verwendung: %s%s"
+
+#: builtin/help.c:459
+#, c-format
+msgid "`git %s' is aliased to `%s'"
+msgstr "für `git %s' wurde der Alias `%s' angelegt"
+
+#: builtin/index-pack.c:170
+#, c-format
+msgid "object type mismatch at %s"
+msgstr "Objekt-Typen passen bei %s nicht zusammen"
+
+#: builtin/index-pack.c:190
+msgid "object of unexpected type"
+msgstr "Objekt hat unerwarteten Typ"
+
+#: builtin/index-pack.c:227
+#, c-format
+msgid "cannot fill %d byte"
+msgid_plural "cannot fill %d bytes"
+msgstr[0] "kann %d Byte nicht lesen"
+msgstr[1] "kann %d Bytes nicht lesen"
+
+#: builtin/index-pack.c:237
+msgid "early EOF"
+msgstr "zu frühes Dateiende"
+
+#: builtin/index-pack.c:238
+msgid "read error on input"
+msgstr "Fehler beim Lesen der Eingabe"
+
+#: builtin/index-pack.c:250
+msgid "used more bytes than were available"
+msgstr "verwendete mehr Bytes als verfügbar waren"
+
+#: builtin/index-pack.c:257
+msgid "pack too large for current definition of off_t"
+msgstr "Paket ist zu groß für die aktuelle Definition von off_t"
+
+#: builtin/index-pack.c:273
+#, c-format
+msgid "unable to create '%s'"
+msgstr "konnte '%s' nicht erstellen"
+
+#: builtin/index-pack.c:278
+#, c-format
+msgid "cannot open packfile '%s'"
+msgstr "Kann Paketdatei '%s' nicht öffnen"
+
+#: builtin/index-pack.c:292
+msgid "pack signature mismatch"
+msgstr "Paketsignatur stimmt nicht überein"
+
+#: builtin/index-pack.c:312
+#, c-format
+msgid "pack has bad object at offset %lu: %s"
+msgstr "Paket hat ein ungültiges Objekt bei Versatz %lu: %s"
+
+#: builtin/index-pack.c:434
+#, c-format
+msgid "inflate returned %d"
+msgstr "Dekomprimierung gab %d zurück"
+
+#: builtin/index-pack.c:483
+msgid "offset value overflow for delta base object"
+msgstr "Wert für Versatz bei Differenzobjekt übergelaufen"
+
+#: builtin/index-pack.c:491
+msgid "delta base offset is out of bound"
+msgstr ""
+"Wert für Versatz bei Differenzobjekt liegt außerhalb des gültigen Bereichs"
+
+#: builtin/index-pack.c:499
+#, c-format
+msgid "unknown object type %d"
+msgstr "Unbekannter Objekt-Typ %d"
+
+#: builtin/index-pack.c:531
+msgid "cannot pread pack file"
+msgstr "Kann Paketdatei %s nicht lesen"
+
+#: builtin/index-pack.c:533
+#, c-format
+msgid "premature end of pack file, %lu byte missing"
+msgid_plural "premature end of pack file, %lu bytes missing"
+msgstr[0] "frühzeitiges Ende der Paketdatei, vermisse %lu Byte"
+msgstr[1] "frühzeitiges Ende der Paketdatei, vermisse %lu Bytes"
+
+#: builtin/index-pack.c:555
+msgid "serious inflate inconsistency"
+msgstr "ernsthafte Inkonsistenz nach Dekomprimierung"
+
+#: builtin/index-pack.c:646 builtin/index-pack.c:652 builtin/index-pack.c:675
+#: builtin/index-pack.c:709 builtin/index-pack.c:718
+#, c-format
+msgid "SHA1 COLLISION FOUND WITH %s !"
+msgstr "SHA1 KOLLISION MIT %s GEFUNDEN !"
+
+#: builtin/index-pack.c:649 builtin/pack-objects.c:170
+#: builtin/pack-objects.c:262
+#, c-format
+msgid "unable to read %s"
+msgstr "kann %s nicht lesen"
+
+#: builtin/index-pack.c:715
+#, c-format
+msgid "cannot read existing object %s"
+msgstr "Kann existierendes Objekt %s nicht lesen."
+
+#: builtin/index-pack.c:729
+#, c-format
+msgid "invalid blob object %s"
+msgstr "ungültiges Blob-Objekt %s"
+
+#: builtin/index-pack.c:744
+#, c-format
+msgid "invalid %s"
+msgstr "Ungültiger Objekt-Typ %s"
+
+#: builtin/index-pack.c:746
+msgid "Error in object"
+msgstr "Fehler in Objekt"
+
+#: builtin/index-pack.c:748
+#, c-format
+msgid "Not all child objects of %s are reachable"
+msgstr "Nicht alle Kind-Objekte von %s sind erreichbar"
+
+#: builtin/index-pack.c:818 builtin/index-pack.c:844
+msgid "failed to apply delta"
+msgstr "Konnte Dateiunterschied nicht anwenden"
+
+#: builtin/index-pack.c:983
+msgid "Receiving objects"
+msgstr "Empfange Objekte"
+
+#: builtin/index-pack.c:983
+msgid "Indexing objects"
+msgstr "Indiziere Objekte"
+
+#: builtin/index-pack.c:1009
+msgid "pack is corrupted (SHA1 mismatch)"
+msgstr "Paket ist beschädigt (SHA1 unterschiedlich)"
+
+#: builtin/index-pack.c:1014
+msgid "cannot fstat packfile"
+msgstr "kann Paketdatei nicht lesen"
+
+#: builtin/index-pack.c:1017
+msgid "pack has junk at the end"
+msgstr "Paketende enthält nicht verwendbaren Inhalt"
+
+#: builtin/index-pack.c:1028
+msgid "confusion beyond insanity in parse_pack_objects()"
+msgstr "Fehler beim Ausführen von \"parse_pack_objects()\""
+
+#: builtin/index-pack.c:1051
+msgid "Resolving deltas"
+msgstr "Löse Unterschiede auf"
+
+#: builtin/index-pack.c:1102
+msgid "confusion beyond insanity"
+msgstr "Fehler beim Auflösen der Unterschiede"
+
+#: builtin/index-pack.c:1121
+#, c-format
+msgid "pack has %d unresolved delta"
+msgid_plural "pack has %d unresolved deltas"
+msgstr[0] "Paket hat %d unaufgelöste Unterschied"
+msgstr[1] "Paket hat %d unaufgelöste Unterschiede"
+
+#: builtin/index-pack.c:1146
+#, c-format
+msgid "unable to deflate appended object (%d)"
+msgstr "Konnte angehängtes Objekt (%d) nicht komprimieren"
+
+#: builtin/index-pack.c:1225
+#, c-format
+msgid "local object %s is corrupt"
+msgstr "lokales Objekt %s ist beschädigt"
+
+#: builtin/index-pack.c:1249
+msgid "error while closing pack file"
+msgstr "Fehler beim Schließen der Paketdatei"
+
+#: builtin/index-pack.c:1262
+#, c-format
+msgid "cannot write keep file '%s'"
+msgstr "Kann Paketbeschreibungsdatei '%s' nicht schreiben"
+
+#: builtin/index-pack.c:1270
+#, c-format
+msgid "cannot close written keep file '%s'"
+msgstr "Kann eben erstellte Paketbeschreibungsdatei '%s' nicht schließen"
+
+#: builtin/index-pack.c:1283
+msgid "cannot store pack file"
+msgstr "Kann Paketdatei nicht speichern"
+
+#: builtin/index-pack.c:1294
+msgid "cannot store index file"
+msgstr "Kann Indexdatei nicht speichern"
+
+#: builtin/index-pack.c:1395
+#, c-format
+msgid "Cannot open existing pack file '%s'"
+msgstr "Kann existierende Paketdatei '%s' nicht öffnen"
+
+#: builtin/index-pack.c:1397
+#, c-format
+msgid "Cannot open existing pack idx file for '%s'"
+msgstr "Kann existierende Indexdatei für Paket '%s' nicht öffnen"
+
+#: builtin/index-pack.c:1444
+#, c-format
+msgid "non delta: %d object"
+msgid_plural "non delta: %d objects"
+msgstr[0] "kein Unterschied: %d Objekt"
+msgstr[1] "kein Unterschied: %d Objekte"
+
+#: builtin/index-pack.c:1451
+#, c-format
+msgid "chain length = %d: %lu object"
+msgid_plural "chain length = %d: %lu objects"
+msgstr[0] "Länge der Objekt-Liste = %d: %lu Objekt"
+msgstr[1] "Länge der Objekt-Liste = %d: %lu Objekte"
+
+#: builtin/index-pack.c:1478
+msgid "Cannot come back to cwd"
+msgstr "Kann nicht zurück zu Arbeitsverzeichnis wechseln"
+
+#: builtin/index-pack.c:1522 builtin/index-pack.c:1525
+#: builtin/index-pack.c:1537 builtin/index-pack.c:1541
+#, c-format
+msgid "bad %s"
+msgstr "%s ist ungültig"
+
+#: builtin/index-pack.c:1555
+msgid "--fix-thin cannot be used without --stdin"
+msgstr "--fix-thin kann nicht ohne --stdin benutzt werden"
+
+#: builtin/index-pack.c:1559 builtin/index-pack.c:1569
+#, c-format
+msgid "packfile name '%s' does not end with '.pack'"
+msgstr "Name der Paketdatei '%s' endet nicht mit '.pack'"
+
+#: builtin/index-pack.c:1578
+msgid "--verify with no packfile name given"
+msgstr "--verify ohne Name der Paketdatei angegeben"
+
+#: builtin/init-db.c:35
+#, c-format
+msgid "Could not make %s writable by group"
+msgstr "Konnte Gruppenschreibrecht für %s nicht setzen."
+
+#: builtin/init-db.c:62
+#, c-format
+msgid "insanely long template name %s"
+msgstr "zu langer Vorlagen-Name %s"
+
+#: builtin/init-db.c:67
+#, c-format
+msgid "cannot stat '%s'"
+msgstr "Kann '%s' nicht lesen"
+
+#: builtin/init-db.c:73
+#, c-format
+msgid "cannot stat template '%s'"
+msgstr "kann Vorlage '%s' nicht lesen"
+
+#: builtin/init-db.c:80
+#, c-format
+msgid "cannot opendir '%s'"
+msgstr "kann Verzeichnis '%s' nicht öffnen"
+
+#: builtin/init-db.c:97
+#, c-format
+msgid "cannot readlink '%s'"
+msgstr "kann Verknüpfung '%s' nicht lesen"
+
+#: builtin/init-db.c:99
+#, c-format
+msgid "insanely long symlink %s"
+msgstr "zu lange symbolische Verknüpfung %s"
+
+#: builtin/init-db.c:102
+#, c-format
+msgid "cannot symlink '%s' '%s'"
+msgstr "kann '%s' nicht mit '%s' symbolisch verknüpfen"
+
+#: builtin/init-db.c:106
+#, c-format
+msgid "cannot copy '%s' to '%s'"
+msgstr "kann '%s' nicht nach '%s' kopieren"
+
+#: builtin/init-db.c:110
+#, c-format
+msgid "ignoring template %s"
+msgstr "ignoriere Vorlage %s"
+
+#: builtin/init-db.c:133
+#, c-format
+msgid "insanely long template path %s"
+msgstr "zu langer Vorlagen-Pfad %s"
+
+#: builtin/init-db.c:141
+#, c-format
+msgid "templates not found %s"
+msgstr "keine Vorlagen in '%s' gefunden"
+
+#: builtin/init-db.c:154
+#, c-format
+msgid "not copying templates of a wrong format version %d from '%s'"
+msgstr "kopiere keine Vorlagen mit einer falschen Formatversion %d von '%s'"
+
+#: builtin/init-db.c:192
+#, c-format
+msgid "insane git directory %s"
+msgstr "ungültiges Git-Verzeichnis %s"
+
+#: builtin/init-db.c:322 builtin/init-db.c:325
+#, c-format
+msgid "%s already exists"
+msgstr "%s existiert bereits"
+
+#: builtin/init-db.c:354
+#, c-format
+msgid "unable to handle file type %d"
+msgstr "kann nicht mit Dateityp %d umgehen"
+
+#: builtin/init-db.c:357
+#, c-format
+msgid "unable to move %s to %s"
+msgstr "Konnte %s nicht nach %s verschieben"
+
+#: builtin/init-db.c:362
+#, c-format
+msgid "Could not create git link %s"
+msgstr "Konnte git-Verknüfung %s nicht erstellen"
+
+#.
+#. * TRANSLATORS: The first '%s' is either "Reinitialized
+#. * existing" or "Initialized empty", the second " shared" or
+#. * "", and the last '%s%s' is the verbatim directory name.
+#.
+#: builtin/init-db.c:419
+#, c-format
+msgid "%s%s Git repository in %s%s\n"
+msgstr "%s%s Git-Projektarchiv in %s%s\n"
+
+#: builtin/init-db.c:420
+msgid "Reinitialized existing"
+msgstr "Reinitialisierte existierendes"
+
+#: builtin/init-db.c:420
+msgid "Initialized empty"
+msgstr "Initialisierte leeres"
+
+#: builtin/init-db.c:421
+msgid " shared"
+msgstr " gemeinsames"
+
+#: builtin/init-db.c:440
+msgid "cannot tell cwd"
+msgstr "kann aktuelles Arbeitsverzeichnis nicht ermitteln"
+
+#: builtin/init-db.c:521 builtin/init-db.c:528
+#, c-format
+msgid "cannot mkdir %s"
+msgstr "kann Verzeichnis %s nicht erstellen"
+
+#: builtin/init-db.c:532
+#, c-format
+msgid "cannot chdir to %s"
+msgstr "kann nicht in Verzeichnis %s wechseln"
+
+#: builtin/init-db.c:554
+#, c-format
+msgid ""
+"%s (or --work-tree=<directory>) not allowed without specifying %s (or --git-"
+"dir=<directory>)"
+msgstr ""
+"%s (oder --work-tree=<Verzeichnis>) nicht erlaubt ohne Spezifizierung von %s "
+"(oder --git-dir=<Verzeichnis>)"
+
+#: builtin/init-db.c:578
+msgid "Cannot access current working directory"
+msgstr "Kann nicht auf aktuelles Arbeitsverzeichnis zugreifen."
+
+#: builtin/init-db.c:585
+#, c-format
+msgid "Cannot access work tree '%s'"
+msgstr "Kann nicht auf Arbeitsbaum '%s' zugreifen."
+
+#: builtin/log.c:189
+#, c-format
+msgid "Final output: %d %s\n"
+msgstr "letzte Ausgabe: %d %s\n"
+
+#: builtin/log.c:402 builtin/log.c:490
+#, c-format
+msgid "Could not read object %s"
+msgstr "Kann Objekt %s nicht lesen."
+
+#: builtin/log.c:514
+#, c-format
+msgid "Unknown type: %d"
+msgstr "Unbekannter Typ: %d"
+
+#: builtin/log.c:603
+msgid "format.headers without value"
+msgstr "format.headers ohne Wert"
+
+#: builtin/log.c:677
+msgid "name of output directory is too long"
+msgstr "Name des Ausgabeverzeichnisses ist zu lang."
+
+#: builtin/log.c:688
+#, c-format
+msgid "Cannot open patch file %s"
+msgstr "Kann Patch-Datei %s nicht öffnen"
+
+#: builtin/log.c:702
+msgid "Need exactly one range."
+msgstr "Brauche genau einen Versionsbereich."
+
+#: builtin/log.c:710
+msgid "Not a range."
+msgstr "Kein Versionsbereich."
+
+#: builtin/log.c:787
+msgid "Cover letter needs email format"
+msgstr "Anschreiben benötigt E-Mail-Format"
+
+#: builtin/log.c:860
+#, c-format
+msgid "insane in-reply-to: %s"
+msgstr "ungültiges in-reply-to: %s"
+
+#: builtin/log.c:933
+msgid "Two output directories?"
+msgstr "Zwei Ausgabeverzeichnisse?"
+
+#: builtin/log.c:1154
+#, c-format
+msgid "bogus committer info %s"
+msgstr "unechte Einreicher-Informationen %s"
+
+#: builtin/log.c:1199
+msgid "-n and -k are mutually exclusive."
+msgstr "-n und -k schliessen sich gegenseitig aus"
+
+#: builtin/log.c:1201
+msgid "--subject-prefix and -k are mutually exclusive."
+msgstr "--subject-prefix und -k schliessen sich gegenseitig aus"
+
+#: builtin/log.c:1209
+msgid "--name-only does not make sense"
+msgstr "--name-only macht keinen Sinn"
+
+#: builtin/log.c:1211
+msgid "--name-status does not make sense"
+msgstr "--name-status macht keinen Sinn"
+
+#: builtin/log.c:1213
+msgid "--check does not make sense"
+msgstr "--check macht keinen Sinn"
+
+#: builtin/log.c:1236
+msgid "standard output, or directory, which one?"
+msgstr "Standard-Ausgabe oder Verzeichnis, welches von beidem?"
+
+#: builtin/log.c:1238
+#, c-format
+msgid "Could not create directory '%s'"
+msgstr "Konnte Verzeichnis '%s' nicht erstellen."
+
+#: builtin/log.c:1391
+msgid "Failed to create output files"
+msgstr "Fehler beim Erstellen der Ausgabedateien."
+
+#: builtin/log.c:1495
+#, c-format
+msgid ""
+"Could not find a tracked remote branch, please specify <upstream> manually.\n"
+msgstr ""
+"Konnte gefolgten, externen Zweig nicht finden, bitte gebe <upstream> manuell "
+"an.\n"
+
+#: builtin/log.c:1511 builtin/log.c:1513 builtin/log.c:1525
+#, c-format
+msgid "Unknown commit %s"
+msgstr "Unbekannte Version %s"
+
+#: builtin/merge.c:90
+msgid "switch `m' requires a value"
+msgstr "Schalter 'm' erfordert einen Wert."
+
+#: builtin/merge.c:127
+#, c-format
+msgid "Could not find merge strategy '%s'.\n"
+msgstr "Konnte Zusammenführungsstrategie '%s' nicht finden.\n"
+
+#: builtin/merge.c:128
+#, c-format
+msgid "Available strategies are:"
+msgstr "Verfügbare Strategien sind:"
+
+#: builtin/merge.c:133
+#, c-format
+msgid "Available custom strategies are:"
+msgstr "Verfügbare benutzerdefinierte Strategien sind:"
+
+#: builtin/merge.c:240
+msgid "could not run stash."
+msgstr "Konnte \"stash\" nicht ausführen."
+
+#: builtin/merge.c:245
+msgid "stash failed"
+msgstr "\"stash\" fehlgeschlagen"
+
+#: builtin/merge.c:250
+#, c-format
+msgid "not a valid object: %s"
+msgstr "kein gültiges Objekt: %s"
+
+#: builtin/merge.c:269 builtin/merge.c:286
+msgid "read-tree failed"
+msgstr "read-tree fehlgeschlagen"
+
+#: builtin/merge.c:316
+msgid " (nothing to squash)"
+msgstr " (nichts zu quetschen)"
+
+#: builtin/merge.c:329
+#, c-format
+msgid "Squash commit -- not updating HEAD\n"
+msgstr "Quetsche Version -- Zweigspitze (HEAD) wird nicht aktualisiert\n"
+
+#: builtin/merge.c:361
+msgid "Writing SQUASH_MSG"
+msgstr "Schreibe SQUASH_MSG"
+
+#: builtin/merge.c:363
+msgid "Finishing SQUASH_MSG"
+msgstr "Schließe SQUASH_MSG ab"
+
+#: builtin/merge.c:386
+#, c-format
+msgid "No merge message -- not updating HEAD\n"
+msgstr ""
+"Keine Zusammenführungsbeschreibung -- Zweigspitze (HEAD) wird nicht "
+"aktualisiert\n"
+
+#: builtin/merge.c:437
+#, c-format
+msgid "'%s' does not point to a commit"
+msgstr "'%s' zeigt auf keine Version"
+
+#: builtin/merge.c:536
+#, c-format
+msgid "Bad branch.%s.mergeoptions string: %s"
+msgstr "Ungültiger branch.%s.mergeoptions String: %s"
+
+#: builtin/merge.c:629
+msgid "git write-tree failed to write a tree"
+msgstr "\"git write-tree\" schlug beim Schreiben eines Baumes fehl"
+
+#: builtin/merge.c:679
+msgid "failed to read the cache"
+msgstr "Lesen des Zwischenspeichers fehlgeschlagen"
+
+#: builtin/merge.c:697
+msgid "Unable to write index."
+msgstr "Konnte Bereitstellung nicht schreiben."
+
+#: builtin/merge.c:710
+msgid "Not handling anything other than two heads merge."
+msgstr "Es wird nur die Zusammenführung von zwei Zweigen behandelt."
+
+#: builtin/merge.c:724
+#, c-format
+msgid "Unknown option for merge-recursive: -X%s"
+msgstr "Unbekannte Option für merge-recursive: -X%s"
+
+#: builtin/merge.c:738
+#, c-format
+msgid "unable to write %s"
+msgstr "konnte %s nicht schreiben"
+
+#: builtin/merge.c:877
+#, c-format
+msgid "Could not read from '%s'"
+msgstr "konnte nicht von '%s' lesen"
+
+#: builtin/merge.c:886
+#, c-format
+msgid "Not committing merge; use 'git commit' to complete the merge.\n"
+msgstr ""
+"Zusammenführung wurde nicht eingetragen; benutze 'git commit' um die "
+"Zusammenführung abzuschließen.\n"
+
+#: builtin/merge.c:892
+msgid ""
+"Please enter a commit message to explain why this merge is necessary,\n"
+"especially if it merges an updated upstream into a topic branch.\n"
+"\n"
+"Lines starting with '#' will be ignored, and an empty message aborts\n"
+"the commit.\n"
+msgstr ""
+"Bitte gebe eine Versionsbeschreibung ein um zu erklären, warum diese "
+"Zusammenführung erforderlich ist,\n"
+"insbesondere wenn es einen aktualisierten, externen Zweig mit einem Thema-"
+"Zweig zusammenführt.\n"
+"\n"
+"Zeilen beginnend mit '#' werden ignoriert, und eine leere Beschreibung "
+"bricht die Eintragung ab.\n"
+
+#: builtin/merge.c:916
+msgid "Empty commit message."
+msgstr "Leere Versionsbeschreibung"
+
+#: builtin/merge.c:928
+#, c-format
+msgid "Wonderful.\n"
+msgstr "Wunderbar.\n"
+
+#: builtin/merge.c:993
+#, c-format
+msgid "Automatic merge failed; fix conflicts and then commit the result.\n"
+msgstr ""
+"Automatische Zusammenführung fehlgeschlagen; behebe die Konflikte und trage "
+"dann das Ergebnis ein.\n"
+
+#: builtin/merge.c:1009
+#, c-format
+msgid "'%s' is not a commit"
+msgstr "'%s' ist keine Version"
+
+#: builtin/merge.c:1050
+msgid "No current branch."
+msgstr "Du befindest dich auf keinem Zweig."
+
+#: builtin/merge.c:1052
+msgid "No remote for the current branch."
+msgstr "Kein externes Archiv für den aktuellen Zweig."
+
+#: builtin/merge.c:1054
+msgid "No default upstream defined for the current branch."
+msgstr ""
+"Es ist kein externes Standard-Projektarchiv für den aktuellen Zweig "
+"definiert."
+
+#: builtin/merge.c:1059
+#, c-format
+msgid "No remote tracking branch for %s from %s"
+msgstr "Kein externer Übernahmezweig für %s von %s"
+
+#: builtin/merge.c:1146 builtin/merge.c:1303
+#, c-format
+msgid "%s - not something we can merge"
+msgstr "%s - nichts was wir zusammenführen können"
+
+#: builtin/merge.c:1214
+msgid "There is no merge to abort (MERGE_HEAD missing)."
+msgstr "Es gibt keine Zusammenführung zum Abbrechen (vermisse MERGE_HEAD)"
+
+#: builtin/merge.c:1230 git-pull.sh:31
+msgid ""
+"You have not concluded your merge (MERGE_HEAD exists).\n"
+"Please, commit your changes before you can merge."
+msgstr ""
+"Du hast deine Zusammenführung nicht abgeschlossen (MERGE_HEAD existiert).\n"
+"Bitte trage deine Änderungen ein, bevor du zusammenführen kannst."
+
+#: builtin/merge.c:1233 git-pull.sh:34
+msgid "You have not concluded your merge (MERGE_HEAD exists)."
+msgstr ""
+"Du hast deine Zusammenführung nicht abgeschlossen (MERGE_HEAD existiert)."
+
+#: builtin/merge.c:1237
+msgid ""
+"You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
+"Please, commit your changes before you can merge."
+msgstr ""
+"Du hast \"cherry-pick\" nicht abgeschlossen (CHERRY_PICK_HEAD existiert).\n"
+"Bitte trage deine Änderungen ein, bevor du zusammenführen kannst."
+
+#: builtin/merge.c:1240
+msgid "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."
+msgstr ""
+"Du hast \"cherry-pick\" nicht abgeschlossen (CHERRY_PICK_HEAD existiert)."
+
+#: builtin/merge.c:1249
+msgid "You cannot combine --squash with --no-ff."
+msgstr "Du kannst --squash nicht mit --no-ff kombinieren."
+
+#: builtin/merge.c:1254
+msgid "You cannot combine --no-ff with --ff-only."
+msgstr "Du kannst --no-ff nicht mit --ff--only kombinieren."
+
+#: builtin/merge.c:1261
+msgid "No commit specified and merge.defaultToUpstream not set."
+msgstr "Keine Version angegeben und merge.defaultToUpstream ist nicht gesetzt."
+
+#: builtin/merge.c:1293
+msgid "Can merge only exactly one commit into empty head"
+msgstr "Kann nur exakt eine Version in einem leeren Zweig zusammenführen."
+
+#: builtin/merge.c:1296
+msgid "Squash commit into empty head not supported yet"
+msgstr "Bin auf einem Zweig, der noch geboren wird; kann nicht quetschen."
+
+#: builtin/merge.c:1298
+msgid "Non-fast-forward commit does not make sense into an empty head"
+msgstr "nicht vorzuspulende Version macht in einem leeren Zweig keinen Sinn"
+
+#: builtin/merge.c:1413
+#, c-format
+msgid "Updating %s..%s\n"
+msgstr "Aktualisiere %s..%s\n"
+
+#: builtin/merge.c:1451
+#, c-format
+msgid "Trying really trivial in-index merge...\n"
+msgstr "Probiere wirklich triviale \"in-index\"-Zusammenführung...\n"
+
+#: builtin/merge.c:1458
+#, c-format
+msgid "Nope.\n"
+msgstr "Nein.\n"
+
+#: builtin/merge.c:1490
+msgid "Not possible to fast-forward, aborting."
+msgstr "Vorspulen nicht möglich, breche ab."
+
+#: builtin/merge.c:1513 builtin/merge.c:1592
+#, c-format
+msgid "Rewinding the tree to pristine...\n"
+msgstr "Rücklauf des Zweiges bis zum Ursprung...\n"
+
+#: builtin/merge.c:1517
+#, c-format
+msgid "Trying merge strategy %s...\n"
+msgstr "Probiere Zusammenführungsstrategie %s...\n"
+
+#: builtin/merge.c:1583
+#, c-format
+msgid "No merge strategy handled the merge.\n"
+msgstr "Keine Zusammenführungsstrategie behandelt diese Zusammenführung.\n"
+
+#: builtin/merge.c:1585
+#, c-format
+msgid "Merge with strategy %s failed.\n"
+msgstr "Zusammenführung mit Strategie %s fehlgeschlagen.\n"
+
+#: builtin/merge.c:1594
+#, c-format
+msgid "Using the %s to prepare resolving by hand.\n"
+msgstr "Benutze \"%s\" um die Auflösung per Hand vorzubereiten.\n"
+
+#: builtin/merge.c:1606
+#, c-format
+msgid "Automatic merge went well; stopped before committing as requested\n"
+msgstr ""
+"Automatische Zusammenführung abgeschlossen; halte, wie gewünscht, vor der "
+"Eintragung an\n"
+
+#: builtin/mv.c:108
+#, c-format
+msgid "Checking rename of '%s' to '%s'\n"
+msgstr "Prüfe Umbenennung von '%s' nach '%s'\n"
+
+#: builtin/mv.c:112
+msgid "bad source"
+msgstr "ungültige Quelle"
+
+#: builtin/mv.c:115
+msgid "can not move directory into itself"
+msgstr "kann Verzeichnis nicht in sich selbst verschieben"
+
+#: builtin/mv.c:118
+msgid "cannot move directory over file"
+msgstr "kann Verzeichnis nicht über Datei verschieben"
+
+#: builtin/mv.c:128
+#, c-format
+msgid "Huh? %.*s is in index?"
+msgstr "Huh? %.*s ist in der Bereitstellung?"
+
+#: builtin/mv.c:140
+msgid "source directory is empty"
+msgstr "Quellverzeichnis ist leer"
+
+#: builtin/mv.c:171
+msgid "not under version control"
+msgstr "nicht unter Versionskontrolle"
+
+#: builtin/mv.c:173
+msgid "destination exists"
+msgstr "Ziel existiert bereits"
+
+#: builtin/mv.c:181
+#, c-format
+msgid "overwriting '%s'"
+msgstr "überschreibe '%s'"
+
+#: builtin/mv.c:184
+msgid "Cannot overwrite"
+msgstr "Kann nicht überschreiben"
+
+#: builtin/mv.c:187
+msgid "multiple sources for the same target"
+msgstr "mehrere Quellen für das selbe Ziel"
+
+#: builtin/mv.c:202
+#, c-format
+msgid "%s, source=%s, destination=%s"
+msgstr "%s, Quelle=%s, Ziel=%s"
+
+#: builtin/mv.c:212
+#, c-format
+msgid "Renaming %s to %s\n"
+msgstr "Benenne %s nach %s um\n"
+
+#: builtin/mv.c:215 builtin/remote.c:731
+#, c-format
+msgid "renaming '%s' failed"
+msgstr "Umbenennung von '%s' fehlgeschlagen"
+
+#: builtin/notes.c:139
+#, c-format
+msgid "unable to start 'show' for object '%s'"
+msgstr "konnte 'show' für Objekt '%s' nicht starten"
+
+#: builtin/notes.c:145
+msgid "can't fdopen 'show' output fd"
+msgstr "konnte Datei-Deskriptor für Ausgabe von 'show' nicht öffnen"
+
+#: builtin/notes.c:155
+#, c-format
+msgid "failed to close pipe to 'show' for object '%s'"
+msgstr "Schließen der Verbindung zu 'show' ist für Objekt '%s' fehlgeschlagen."
+
+#: builtin/notes.c:158
+#, c-format
+msgid "failed to finish 'show' for object '%s'"
+msgstr "konnte 'show' für Objekt '%s' nicht abschließen"
+
+#: builtin/notes.c:175 builtin/tag.c:347
+#, c-format
+msgid "could not create file '%s'"
+msgstr "konnte Datei '%s' nicht erstellen"
+
+#: builtin/notes.c:189
+msgid "Please supply the note contents using either -m or -F option"
+msgstr "Bitte liefere den Notiz-Inhalt unter Verwendung der Option -m oder -F."
+
+#: builtin/notes.c:210 builtin/notes.c:973
+#, c-format
+msgid "Removing note for object %s\n"
+msgstr "Entferne Notiz für Objekt %s\n"
+
+#: builtin/notes.c:215
+msgid "unable to write note object"
+msgstr "Konnte Notiz-Objekt nicht schreiben"
+
+#: builtin/notes.c:217
+#, c-format
+msgid "The note contents has been left in %s"
+msgstr "Die Notiz-Inhalte wurden in %s belassen"
+
+#: builtin/notes.c:251 builtin/tag.c:542
+#, c-format
+msgid "cannot read '%s'"
+msgstr "kann '%s' nicht lesen"
+
+#: builtin/notes.c:253 builtin/tag.c:545
+#, c-format
+msgid "could not open or read '%s'"
+msgstr "konnte '%s' nicht öffnen oder lesen"
+
+#: builtin/notes.c:272 builtin/notes.c:445 builtin/notes.c:447
+#: builtin/notes.c:507 builtin/notes.c:561 builtin/notes.c:644
+#: builtin/notes.c:649 builtin/notes.c:724 builtin/notes.c:766
+#: builtin/notes.c:968 builtin/reset.c:293 builtin/tag.c:558
+#, c-format
+msgid "Failed to resolve '%s' as a valid ref."
+msgstr "Konnte '%s' nicht als gültige Referenz auflösen."
+
+#: builtin/notes.c:275
+#, c-format
+msgid "Failed to read object '%s'."
+msgstr "Fehler beim Lesen des Objektes '%s'."
+
+#: builtin/notes.c:299
+msgid "Cannot commit uninitialized/unreferenced notes tree"
+msgstr "Kann uninitialisierten/unreferenzierten Notiz-Baum nicht eintragen."
+
+#: builtin/notes.c:340
+#, c-format
+msgid "Bad notes.rewriteMode value: '%s'"
+msgstr "Ungültiger notes.rewriteMode Wert: '%s'"
+
+#: builtin/notes.c:350
+#, c-format
+msgid "Refusing to rewrite notes in %s (outside of refs/notes/)"
+msgstr ""
+"Neuschreiben der Notizen in %s zurückgewiesen (außerhalb von refs/notes/)"
+
+#. TRANSLATORS: The first %s is the name of the
+#. environment variable, the second %s is its value
+#: builtin/notes.c:377
+#, c-format
+msgid "Bad %s value: '%s'"
+msgstr "Ungültiger %s Wert: '%s'"
+
+#: builtin/notes.c:441
+#, c-format
+msgid "Malformed input line: '%s'."
+msgstr "Fehlerhafte Eingabezeile: '%s'."
+
+#: builtin/notes.c:456
+#, c-format
+msgid "Failed to copy notes from '%s' to '%s'"
+msgstr "Fehler beim Kopieren der Notizen von '%s' nach '%s'"
+
+#: builtin/notes.c:500 builtin/notes.c:554 builtin/notes.c:627
+#: builtin/notes.c:639 builtin/notes.c:712 builtin/notes.c:759
+#: builtin/notes.c:1033
+msgid "too many parameters"
+msgstr "zu viele Parameter"
+
+#: builtin/notes.c:513 builtin/notes.c:772
+#, c-format
+msgid "No note found for object %s."
+msgstr "Kein Notiz für Objekt %s gefunden."
+
+#: builtin/notes.c:580
+#, c-format
+msgid ""
+"Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite "
+"existing notes"
+msgstr ""
+"Konnte Notizen nicht hinzufügen. Existierende Notizen für Objekt %s "
+"gefunden. Verwende '-f' um die existierenden Notizen zu überschreiben."
+
+#: builtin/notes.c:585 builtin/notes.c:662
+#, c-format
+msgid "Overwriting existing notes for object %s\n"
+msgstr "Überschreibe existierende Notizen für Objekt %s\n"
+
+#: builtin/notes.c:635
+msgid "too few parameters"
+msgstr "zu wenig Parameter"
+
+#: builtin/notes.c:656
+#, c-format
+msgid ""
+"Cannot copy notes. Found existing notes for object %s. Use '-f' to overwrite "
+"existing notes"
+msgstr ""
+"Kann Notizen nicht kopieren. Existierende Notizen für Objekt %s gefunden. "
+"Verwende '-f' um die existierenden Notizen zu überschreiben."
+
+#: builtin/notes.c:668
+#, c-format
+msgid "Missing notes on source object %s. Cannot copy."
+msgstr "Keine Notizen für Quell-Objekt %s. Kopie nicht möglich."
+
+#: builtin/notes.c:717
+#, c-format
+msgid ""
+"The -m/-F/-c/-C options have been deprecated for the 'edit' subcommand.\n"
+"Please use 'git notes add -f -m/-F/-c/-C' instead.\n"
+msgstr ""
+"Die Optionen -m/-F/-c/-C sind für das Unterkommando 'edit' veraltet.\n"
+"Bitte benutze stattdessen 'git notes add -f -m/-F/-c/-C'.\n"
+
+#: builtin/notes.c:971
+#, c-format
+msgid "Object %s has no note\n"
+msgstr "Objekt %s hat keine Notiz\n"
+
+#: builtin/notes.c:1103 builtin/remote.c:1598
+#, c-format
+msgid "Unknown subcommand: %s"
+msgstr "Unbekanntes Unterkommando: %s"
+
+#: builtin/pack-objects.c:183 builtin/pack-objects.c:186
+#, c-format
+msgid "deflate error (%d)"
+msgstr "Fehler beim Komprimieren (%d)"
+
+#: builtin/pack-objects.c:2398
+#, c-format
+msgid "unsupported index version %s"
+msgstr "Nicht unterstützte Bereitstellungsversion %s"
+
+#: builtin/pack-objects.c:2402
+#, c-format
+msgid "bad index version '%s'"
+msgstr "Ungültige Bereitstellungsversion '%s'"
+
+#: builtin/pack-objects.c:2425
+#, c-format
+msgid "option %s does not accept negative form"
+msgstr "Option %s akzeptiert keine negative Form"
+
+#: builtin/pack-objects.c:2429
+#, c-format
+msgid "unable to parse value '%s' for option %s"
+msgstr "konnte Wert '%s' für Option %s nicht parsen"
+
+#: builtin/push.c:45
+msgid "tag shorthand without <tag>"
+msgstr "Kurzschrift für Markierung ohne <Markierung>"
+
+#: builtin/push.c:64
+msgid "--delete only accepts plain target ref names"
+msgstr "--delete akzeptiert nur reine Referenz-Namen als Ziel"
+
+#: builtin/push.c:99
+msgid ""
+"\n"
+"To choose either option permanently, see push.default in 'git help config'."
+msgstr ""
+"\n"
+"Um eine Variante permanent zu verwenden, siehe push.default in 'git help "
+"config'."
+
+#: builtin/push.c:102
+#, c-format
+msgid ""
+"The upstream branch of your current branch does not match\n"
+"the name of your current branch.  To push to the upstream branch\n"
+"on the remote, use\n"
+"\n"
+"    git push %s HEAD:%s\n"
+"\n"
+"To push to the branch of the same name on the remote, use\n"
+"\n"
+"    git push %s %s\n"
+"%s"
+msgstr ""
+"Der Name des externen Übernahmezweiges stimmt nicht mit dem Namen deines\n"
+"aktuellen Zweiges überein. Um auf den Übernahmezweig in dem externen\n"
+"Projektarchiv zu versenden, benutze:\n"
+"\n"
+"    git push %s HEAD:%s\n"
+"\n"
+"Um auf den Zweig mit dem selben Namen in dem externen Projekarchiv\n"
+"zu versenden, benutze:\n"
+"\n"
+"    git push %s %s\n"
+"%s"
+
+#: builtin/push.c:121
+#, c-format
+msgid ""
+"You are not currently on a branch.\n"
+"To push the history leading to the current (detached HEAD)\n"
+"state now, use\n"
+"\n"
+"    git push %s HEAD:<name-of-remote-branch>\n"
+msgstr ""
+"Du befindest dich sich im Moment auf keinem Zweig.\n"
+"Um die Historie, führend zum aktuellen (freistehende Zweigspitze (HEAD))\n"
+"Status zu versenden, benutze\n"
+"\n"
+"    git push %s HEAD:<Name-des-externen-Zweiges>\n"
+
+#: builtin/push.c:128
+#, c-format
+msgid ""
+"The current branch %s has no upstream branch.\n"
+"To push the current branch and set the remote as upstream, use\n"
+"\n"
+"    git push --set-upstream %s %s\n"
+msgstr ""
+"Der aktuelle Zweig %s hat keinen Zweig im externen Projektarchiv.\n"
+"Um den aktuellen Zweig zu versenden und das Fernarchiv als externes\n"
+"Projektarchiv zu verwenden, benutze\n"
+"\n"
+"    git push --set-upstream %s %s\n"
+
+#: builtin/push.c:136
+#, c-format
+msgid "The current branch %s has multiple upstream branches, refusing to push."
+msgstr "Der aktuelle Zweig %s hat mehrere externe Zweige, Versand verweigert."
+
+#: builtin/push.c:139
+#, c-format
+msgid ""
+"You are pushing to remote '%s', which is not the upstream of\n"
+"your current branch '%s', without telling me what to push\n"
+"to update which remote branch."
+msgstr ""
+"Du versendest nach '%s', welches kein externes Projektarchiv deines\n"
+"aktuellen Zweiges '%s' ist, ohne mir mitzuteilen, was ich versenden\n"
+"soll, um welchen externen Zweig zu aktualisieren."
+
+#: builtin/push.c:174
+msgid ""
+"You didn't specify any refspecs to push, and push.default is \"nothing\"."
+msgstr ""
+"Du hast keine Referenzspezifikationen zum Versenden angegeben, und push."
+"default ist \"nothing\"."
+
+#: builtin/push.c:181
+msgid ""
+"Updates were rejected because the tip of your current branch is behind\n"
+"its remote counterpart. Merge the remote changes (e.g. 'git pull')\n"
+"before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
+msgstr ""
+"Aktualisierungen wurden zurückgewiesen, weil die Spitze deines aktuellen\n"
+"Zweiges hinter seinem externen Gegenstück zurückgefallen ist. Führe die\n"
+"externen Änderungen zusammen (z.B. 'git pull') bevor du erneut versendest.\n"
+"Siehe auch die Sektion 'Note about fast-forwards' in 'git push --help'\n"
+"für weitere Details."
+
+#: builtin/push.c:187
+msgid ""
+"Updates were rejected because a pushed branch tip is behind its remote\n"
+"counterpart. If you did not intend to push that branch, you may want to\n"
+"specify branches to push or set the 'push.default' configuration\n"
+"variable to 'current' or 'upstream' to push only the current branch."
+msgstr ""
+"Aktualisierungen wurden zurückgewiesen, weil die Spitze eines versendeten\n"
+"Zweiges hinter seinem externen Gegenstück zurückgefallen ist. Wenn du nicht\n"
+"beabsichtigt hast, diesen Zweig zu versenden, kannst du auch den zu "
+"versendenden\n"
+"Zweig spezifizieren oder die Konfigurationsvariable 'push.default' zu "
+"'current'\n"
+"oder 'upstream' setzen, um nur den aktuellen Zweig zu versenden."
+
+#: builtin/push.c:193
+msgid ""
+"Updates were rejected because a pushed branch tip is behind its remote\n"
+"counterpart. Check out this branch and merge the remote changes\n"
+"(e.g. 'git pull') before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
+msgstr ""
+"Aktualisierungen wurden zurückgewiesen, weil die Spitze eines versendeten\n"
+"Zweiges hinter seinem externen Gegenstück zurückgefallen ist. Checke diesen\n"
+"Zweig aus und führe die externen Änderungen zusammen (z.B. 'git pull')\n"
+"bevor du erneut versendest.\n"
+"Siehe auch die Sektion 'Note about fast-forwards' in 'git push --help'\n"
+"für weitere Details."
+
+#: builtin/push.c:233
+#, c-format
+msgid "Pushing to %s\n"
+msgstr "Versende nach %s\n"
+
+#: builtin/push.c:237
+#, c-format
+msgid "failed to push some refs to '%s'"
+msgstr "Fehler beim Versenden einiger Referenzen nach '%s'"
+
+#: builtin/push.c:269
+#, c-format
+msgid "bad repository '%s'"
+msgstr "ungültiges Projektarchiv '%s'"
+
+#: builtin/push.c:270
+msgid ""
+"No configured push destination.\n"
+"Either specify the URL from the command-line or configure a remote "
+"repository using\n"
+"\n"
+"    git remote add <name> <url>\n"
+"\n"
+"and then push using the remote name\n"
+"\n"
+"    git push <name>\n"
+msgstr ""
+"Kein Ziel zum Versenden konfiguriert.\n"
+"Entweder spezifizierst du die URL von der Kommandozeile oder konfigurierst "
+"ein externes Projektarchiv unter Benutzung von\n"
+"\n"
+"    git remote add <Name> <URL>\n"
+"\n"
+"und versendest dann unter Benutzung dieses Namens\n"
+"\n"
+"    git push <Name>\n"
+
+#: builtin/push.c:285
+msgid "--all and --tags are incompatible"
+msgstr "--all und --tags sind inkompatibel"
+
+#: builtin/push.c:286
+msgid "--all can't be combined with refspecs"
+msgstr "--all kann nicht mit Referenzspezifikationen kombiniert werden"
+
+#: builtin/push.c:291
+msgid "--mirror and --tags are incompatible"
+msgstr "--mirror und --tags sind inkompatibel"
+
+#: builtin/push.c:292
+msgid "--mirror can't be combined with refspecs"
+msgstr "--mirror kann nicht mit Referenzspezifikationen kombiniert werden"
+
+#: builtin/push.c:297
+msgid "--all and --mirror are incompatible"
+msgstr "--all und --mirror sind inkompatibel"
+
+#: builtin/push.c:385
+msgid "--delete is incompatible with --all, --mirror and --tags"
+msgstr "--delete ist inkompatibel mit --all, --mirror und --tags"
+
+#: builtin/push.c:387
+msgid "--delete doesn't make sense without any refs"
+msgstr "--delete macht ohne irgendeine Referenz keinen Sinn"
+
+#: builtin/remote.c:98
+#, c-format
+msgid "Updating %s"
+msgstr "Aktualisiere %s"
+
+#: builtin/remote.c:130
+msgid ""
+"--mirror is dangerous and deprecated; please\n"
+"\t use --mirror=fetch or --mirror=push instead"
+msgstr ""
+"--mirror ist gefährlich und veraltet; bitte\n"
+"\t benutze stattdessen --mirror=fetch oder --mirror=push"
+
+#: builtin/remote.c:147
+#, c-format
+msgid "unknown mirror argument: %s"
+msgstr "unbekanntes Argument für Option --mirror: %s"
+
+#: builtin/remote.c:185
+msgid "specifying a master branch makes no sense with --mirror"
+msgstr "Angabe eines Hauptzweiges macht mit --mirror keinen Sinn"
+
+#: builtin/remote.c:187
+msgid "specifying branches to track makes sense only with fetch mirrors"
+msgstr ""
+"die Angabe von zu folgenden Zweigen macht nur mit dem Abholen von "
+"Spiegelarchiven Sinn"
+
+#: builtin/remote.c:195 builtin/remote.c:646
+#, c-format
+msgid "remote %s already exists."
+msgstr "externes Projektarchiv %s existiert bereits"
+
+#: builtin/remote.c:199 builtin/remote.c:650
+#, c-format
+msgid "'%s' is not a valid remote name"
+msgstr "'%s' ist kein gültiger Name für ein externes Projektarchiv"
+
+#: builtin/remote.c:243
+#, c-format
+msgid "Could not setup master '%s'"
+msgstr "Konnte symbolische Referenz für Hauptzweig von '%s' nicht einrichten"
+
+#: builtin/remote.c:299
+#, c-format
+msgid "more than one %s"
+msgstr "mehr als ein %s"
+
+#: builtin/remote.c:339
+#, c-format
+msgid "Could not get fetch map for refspec %s"
+msgstr "Konnte Abholungszuordnung für Referenzspezifikation %s nicht bekommen"
+
+#: builtin/remote.c:440 builtin/remote.c:448
+msgid "(matching)"
+msgstr "(übereinstimmend)"
+
+#: builtin/remote.c:452
+msgid "(delete)"
+msgstr "(lösche)"
+
+#: builtin/remote.c:595 builtin/remote.c:601 builtin/remote.c:607
+#, c-format
+msgid "Could not append '%s' to '%s'"
+msgstr "Konnte '%s' nicht an '%s' anhängen."
+
+#: builtin/remote.c:639 builtin/remote.c:792 builtin/remote.c:890
+#, c-format
+msgid "No such remote: %s"
+msgstr "Kein solches externes Archiv: %s"
+
+#: builtin/remote.c:656
+#, c-format
+msgid "Could not rename config section '%s' to '%s'"
+msgstr "Konnte Sektion '%s' in Konfiguration nicht nach '%s' umbenennen"
+
+#: builtin/remote.c:662 builtin/remote.c:799
+#, c-format
+msgid "Could not remove config section '%s'"
+msgstr "Konnte Sektion '%s' nicht aus Konfiguration entfernen"
+
+#: builtin/remote.c:677
+#, c-format
+msgid ""
+"Not updating non-default fetch refspec\n"
+"\t%s\n"
+"\tPlease update the configuration manually if necessary."
+msgstr ""
+"Keine Aktualisierung der nicht standardmäßigen Referenzspezifikation zum "
+"Abholen\n"
+"\t%s\n"
+"\tBitte aktualisiere, falls notwendig, die Konfiguration manuell."
+
+#: builtin/remote.c:683
+#, c-format
+msgid "Could not append '%s'"
+msgstr "Konnte '%s' nicht anhängen."
+
+#: builtin/remote.c:694
+#, c-format
+msgid "Could not set '%s'"
+msgstr "Konnte '%s' nicht setzen"
+
+#: builtin/remote.c:716
+#, c-format
+msgid "deleting '%s' failed"
+msgstr "Konnte '%s' nicht löschen"
+
+#: builtin/remote.c:750
+#, c-format
+msgid "creating '%s' failed"
+msgstr "Konnte '%s' nicht erstellen"
+
+#: builtin/remote.c:764
+#, c-format
+msgid "Could not remove branch %s"
+msgstr "Konnte Zweig %s nicht entfernen"
+
+#: builtin/remote.c:834
+msgid ""
+"Note: A branch outside the refs/remotes/ hierarchy was not removed;\n"
+"to delete it, use:"
+msgid_plural ""
+"Note: Some branches outside the refs/remotes/ hierarchy were not removed;\n"
+"to delete them, use:"
+msgstr[0] ""
+"Hinweis: Ein Zweig außerhalb der /refs/remotes/ Hierachie wurde nicht "
+"entfernt;\n"
+"um diesen zu entfernen, benutze:"
+msgstr[1] ""
+"Hinweis: Einige Zweige außer der /refs/remotes/ Hierarchie wurden nicht "
+"entfernt;\n"
+"um diese zu entfernen, benutze:"
+
+#: builtin/remote.c:943
+#, c-format
+msgid " new (next fetch will store in remotes/%s)"
+msgstr " neu (wird bei nächster Abholung in remotes/%s gespeichert)"
+
+#: builtin/remote.c:946
+msgid " tracked"
+msgstr " gefolgt"
+
+#: builtin/remote.c:948
+msgid " stale (use 'git remote prune' to remove)"
+msgstr " veraltet (benutze 'git remote prune' zum Entfernen)"
+
+#: builtin/remote.c:950
+msgid " ???"
+msgstr " ???"
+
+#: builtin/remote.c:991
+#, c-format
+msgid "invalid branch.%s.merge; cannot rebase onto > 1 branch"
+msgstr "ungültiges branch.%s.merge; kann nicht auf > 1 Zweig neu aufbauen"
+
+#: builtin/remote.c:998
+#, c-format
+msgid "rebases onto remote %s"
+msgstr "baut neu auf externen Zweig %s auf"
+
+#: builtin/remote.c:1001
+#, c-format
+msgid " merges with remote %s"
+msgstr " führt mit externem Zweig %s zusammen"
+
+#: builtin/remote.c:1002
+msgid "    and with remote"
+msgstr "    und mit externem Zweig"
+
+#: builtin/remote.c:1004
+#, c-format
+msgid "merges with remote %s"
+msgstr "führt mit externem Zweig %s zusammen"
+
+#: builtin/remote.c:1005
+msgid "   and with remote"
+msgstr "   und mit externem Zweig"
+
+#: builtin/remote.c:1051
+msgid "create"
+msgstr "erstellt"
+
+#: builtin/remote.c:1054
+msgid "delete"
+msgstr "gelöscht"
+
+#: builtin/remote.c:1058
+msgid "up to date"
+msgstr "aktuell"
+
+#: builtin/remote.c:1061
+msgid "fast-forwardable"
+msgstr "vorspulbar"
+
+#: builtin/remote.c:1064
+msgid "local out of date"
+msgstr "lokal nicht aktuell"
+
+#: builtin/remote.c:1071
+#, c-format
+msgid "    %-*s forces to %-*s (%s)"
+msgstr "    %-*s erzwingt Versandt nach %-*s (%s)"
+
+#: builtin/remote.c:1074
+#, c-format
+msgid "    %-*s pushes to %-*s (%s)"
+msgstr "    %-*s versendet nach %-*s (%s)"
+
+#: builtin/remote.c:1078
+#, c-format
+msgid "    %-*s forces to %s"
+msgstr "    %-*s erzwingt Versand nach %s"
+
+#: builtin/remote.c:1081
+#, c-format
+msgid "    %-*s pushes to %s"
+msgstr "    %-*s versendet nach %s"
+
+#: builtin/remote.c:1118
+#, c-format
+msgid "* remote %s"
+msgstr "* externes Projektarchiv %s"
+
+#: builtin/remote.c:1119
+#, c-format
+msgid "  Fetch URL: %s"
+msgstr "  URL zum Abholen: %s"
+
+#: builtin/remote.c:1120 builtin/remote.c:1285
+msgid "(no URL)"
+msgstr "(keine URL)"
+
+#: builtin/remote.c:1129 builtin/remote.c:1131
+#, c-format
+msgid "  Push  URL: %s"
+msgstr "  URL zum Versenden: %s"
+
+#: builtin/remote.c:1133 builtin/remote.c:1135 builtin/remote.c:1137
+#, c-format
+msgid "  HEAD branch: %s"
+msgstr "  Hauptzweig: %s"
+
+#: builtin/remote.c:1139
+#, c-format
+msgid ""
+"  HEAD branch (remote HEAD is ambiguous, may be one of the following):\n"
+msgstr ""
+"  Hauptzweig (externer Hauptzweig ist mehrdeutig, könnte einer der folgenden "
+"sein):\n"
+
+#: builtin/remote.c:1151
+#, c-format
+msgid "  Remote branch:%s"
+msgid_plural "  Remote branches:%s"
+msgstr[0] "  externer Zweig:%s"
+msgstr[1] "  externe Zweige:%s"
+
+#: builtin/remote.c:1154 builtin/remote.c:1181
+msgid " (status not queried)"
+msgstr " (Zustand nicht abgefragt)"
+
+#: builtin/remote.c:1163
+msgid "  Local branch configured for 'git pull':"
+msgid_plural "  Local branches configured for 'git pull':"
+msgstr[0] "  Lokaler Zweig konfiguriert für 'git pull':"
+msgstr[1] "  Lokale Zweige konfiguriert für 'git pull':"
+
+#: builtin/remote.c:1171
+msgid "  Local refs will be mirrored by 'git push'"
+msgstr "  Lokale Referenzen werden von 'git push' gespiegelt"
+
+#: builtin/remote.c:1178
+#, c-format
+msgid "  Local ref configured for 'git push'%s:"
+msgid_plural "  Local refs configured for 'git push'%s:"
+msgstr[0] "  Lokale Referenz konfiguriert für 'git push'%s:"
+msgstr[1] "  Lokale Referenzen konfiguriert für 'git push'%s:"
+
+#: builtin/remote.c:1216
+msgid "Cannot determine remote HEAD"
+msgstr "Kann Hauptzweig des externen Projektarchivs nicht bestimmen"
+
+#: builtin/remote.c:1218
+msgid "Multiple remote HEAD branches. Please choose one explicitly with:"
+msgstr ""
+"Mehrere Hauptzweige im externen Projektarchiv. Bitte wähle explizit einen "
+"aus mit:"
+
+#: builtin/remote.c:1228
+#, c-format
+msgid "Could not delete %s"
+msgstr "Konnte %s nicht entfernen"
+
+#: builtin/remote.c:1236
+#, c-format
+msgid "Not a valid ref: %s"
+msgstr "keine gültige Referenz: %s"
+
+#: builtin/remote.c:1238
+#, c-format
+msgid "Could not setup %s"
+msgstr "Konnte %s nicht einrichten"
+
+#: builtin/remote.c:1274
+#, c-format
+msgid " %s will become dangling!"
+msgstr " %s wird unreferenziert!"
+
+#: builtin/remote.c:1275
+#, c-format
+msgid " %s has become dangling!"
+msgstr " %s wurde unreferenziert!"
+
+#: builtin/remote.c:1281
+#, c-format
+msgid "Pruning %s"
+msgstr "entferne veraltete Zweige von %s"
+
+#: builtin/remote.c:1282
+#, c-format
+msgid "URL: %s"
+msgstr "URL: %s"
+
+#: builtin/remote.c:1295
+#, c-format
+msgid " * [would prune] %s"
+msgstr " * [würde veralteten Zweig entfernen] %s"
+
+#: builtin/remote.c:1298
+#, c-format
+msgid " * [pruned] %s"
+msgstr "* [veralteten Zweig entfernt] %s"
+
+#: builtin/remote.c:1387 builtin/remote.c:1461
+#, c-format
+msgid "No such remote '%s'"
+msgstr "Kein solches externes Projektarchiv '%s'"
+
+#: builtin/remote.c:1414
+msgid "no remote specified"
+msgstr "kein externes Projektarchiv angegeben"
+
+#: builtin/remote.c:1447
+msgid "--add --delete doesn't make sense"
+msgstr "--add --delete macht keinen Sinn"
+
+#: builtin/remote.c:1487
+#, c-format
+msgid "Invalid old URL pattern: %s"
+msgstr "ungültiges altes URL Format: %s"
+
+#: builtin/remote.c:1495
+#, c-format
+msgid "No such URL found: %s"
+msgstr "Keine solche URL gefunden: %s"
+
+#: builtin/remote.c:1497
+msgid "Will not delete all non-push URLs"
+msgstr "Werde keine URLs entfernen, die nicht für den Versand bestimmt sind"
+
+#: builtin/reset.c:33
+msgid "mixed"
+msgstr "mixed"
+
+#: builtin/reset.c:33
+msgid "soft"
+msgstr "soft"
+
+#: builtin/reset.c:33
+msgid "hard"
+msgstr "hard"
+
+#: builtin/reset.c:33
+msgid "merge"
+msgstr "zusammenführen"
+
+#: builtin/reset.c:33
+msgid "keep"
+msgstr "keep"
+
+#: builtin/reset.c:77
+msgid "You do not have a valid HEAD."
+msgstr "Du hast keine gültige Zweigspitze (HEAD)."
+
+#: builtin/reset.c:79
+msgid "Failed to find tree of HEAD."
+msgstr "Fehler beim Finden des Baumes der Zweigspitze (HEAD)."
+
+#: builtin/reset.c:85
+#, c-format
+msgid "Failed to find tree of %s."
+msgstr "Fehler beim Finden des Baumes von %s."
+
+#: builtin/reset.c:96
+msgid "Could not write new index file."
+msgstr "Konnte neue Bereitstellungsdatei nicht schreiben."
+
+#: builtin/reset.c:106
+#, c-format
+msgid "HEAD is now at %s"
+msgstr "Zweigspitze (HEAD) ist jetzt bei %s"
+
+#: builtin/reset.c:130
+msgid "Could not read index"
+msgstr "Konnte Bereitstellung nicht lesen"
+
+#: builtin/reset.c:133
+msgid "Unstaged changes after reset:"
+msgstr "Nicht bereitgestellte Änderungen nach Zurücksetzung:"
+
+#: builtin/reset.c:223
+#, c-format
+msgid "Cannot do a %s reset in the middle of a merge."
+msgstr ""
+"Kann keine '%s' Zurücksetzung durchführen, während eine Zusammenführung im "
+"Gange ist."
+
+#: builtin/reset.c:297
+#, c-format
+msgid "Could not parse object '%s'."
+msgstr "Konnte Objekt '%s' nicht parsen."
+
+#: builtin/reset.c:302
+msgid "--patch is incompatible with --{hard,mixed,soft}"
+msgstr "--patch ist inkompatibel mit --{hard,mixed,soft}"
+
+#: builtin/reset.c:311
+msgid "--mixed with paths is deprecated; use 'git reset -- <paths>' instead."
+msgstr ""
+"--mixed mit Pfaden ist veraltet; benutze stattdessen 'git reset -- <Pfade>'."
+
+#: builtin/reset.c:313
+#, c-format
+msgid "Cannot do %s reset with paths."
+msgstr "Eine '%s' Zurücksetzung mit Pfaden ist nicht möglich."
+
+#: builtin/reset.c:325
+#, c-format
+msgid "%s reset is not allowed in a bare repository"
+msgstr "'%s' Zurücksetzung ist in einem bloßen Projektarchiv nicht erlaubt"
+
+#: builtin/reset.c:341
+#, c-format
+msgid "Could not reset index file to revision '%s'."
+msgstr "Konnte Bereitstellungsdatei nicht zu Version '%s' zurücksetzen."
+
+#: builtin/revert.c:70 builtin/revert.c:92
+#, c-format
+msgid "%s: %s cannot be used with %s"
+msgstr "%s: %s kann nicht mit %s benutzt werden"
+
+#: builtin/revert.c:131
+msgid "program error"
+msgstr "Programmfehler"
+
+#: builtin/revert.c:221
+msgid "revert failed"
+msgstr "\"revert\" fehlgeschlagen"
+
+#: builtin/revert.c:236
+msgid "cherry-pick failed"
+msgstr "\"cherry-pick\" fehlgeschlagen"
+
+#: builtin/rm.c:109
+#, c-format
+msgid ""
+"'%s' has staged content different from both the file and the HEAD\n"
+"(use -f to force removal)"
+msgstr ""
+"'%s' hat bereitgestellten Inhalt unterschiedlich zu der Datei und der\n"
+"Zweigspitze (HEAD) (benutze -f um die Entfernung zu erzwingen)"
+
+#: builtin/rm.c:115
+#, c-format
+msgid ""
+"'%s' has changes staged in the index\n"
+"(use --cached to keep the file, or -f to force removal)"
+msgstr ""
+"'%s' hat Änderungen in der Bereitstellung\n"
+"(benutze --cached um die Datei zu behalten, oder -f um die Entfernung zu "
+"erzwingen)"
+
+#: builtin/rm.c:119
+#, c-format
+msgid ""
+"'%s' has local modifications\n"
+"(use --cached to keep the file, or -f to force removal)"
+msgstr ""
+"'%s' hat lokale Modifikationen\n"
+"(benutze --cached um die Datei zu behalten, oder -f um die Entfernung zu "
+"erzwingen)"
+
+#: builtin/rm.c:194
+#, c-format
+msgid "not removing '%s' recursively without -r"
+msgstr "'%s' wird nicht ohne -r rekursiv entfernt"
+
+#: builtin/rm.c:230
+#, c-format
+msgid "git rm: unable to remove %s"
+msgstr "git rm: konnte %s nicht entfernen"
+
+#: builtin/shortlog.c:157
+#, c-format
+msgid "Missing author: %s"
+msgstr "fehlender Autor: %s"
+
+#: builtin/tag.c:60
+#, c-format
+msgid "malformed object at '%s'"
+msgstr "fehlerhaftes Objekt bei '%s'"
+
+#: builtin/tag.c:207
+#, c-format
+msgid "tag name too long: %.*s..."
+msgstr "Markierungsname zu lang: %.*s..."
+
+#: builtin/tag.c:212
+#, c-format
+msgid "tag '%s' not found."
+msgstr "Markierung '%s' nicht gefunden."
+
+#: builtin/tag.c:227
+#, c-format
+msgid "Deleted tag '%s' (was %s)\n"
+msgstr "Gelöschte Markierung '%s' (war %s)\n"
+
+#: builtin/tag.c:239
+#, c-format
+msgid "could not verify the tag '%s'"
+msgstr "Konnte Markierung '%s' nicht verifizieren"
+
+#: builtin/tag.c:249
+msgid ""
+"\n"
+"#\n"
+"# Write a tag message\n"
+"# Lines starting with '#' will be ignored.\n"
+"#\n"
+msgstr ""
+"\n"
+"#\n"
+"# Gebe eine Markierungsbeschreibung ein\n"
+"# Zeilen, die mit '#' beginnen, werden ignoriert.\n"
+"#\n"
+
+#: builtin/tag.c:256
+msgid ""
+"\n"
+"#\n"
+"# Write a tag message\n"
+"# Lines starting with '#' will be kept; you may remove them yourself if you "
+"want to.\n"
+"#\n"
+msgstr ""
+"\n"
+"#\n"
+"# Gebe eine Markierungsbeschreibung ein\n"
+"# Zeilen, die mit '#' beginnen, werden behalten; du darfst diese\n"
+"# selbst entfernen wenn du möchtest.\n"
+"#\n"
+
+#: builtin/tag.c:298
+msgid "unable to sign the tag"
+msgstr "konnte Markierung nicht signieren"
+
+#: builtin/tag.c:300
+msgid "unable to write tag file"
+msgstr "konnte Markierungsdatei nicht schreiben"
+
+#: builtin/tag.c:325
+msgid "bad object type."
+msgstr "ungültiger Objekt-Typ"
+
+#: builtin/tag.c:338
+msgid "tag header too big."
+msgstr "Markierungskopf zu groß."
+
+#: builtin/tag.c:370
+msgid "no tag message?"
+msgstr "keine Markierungsbeschreibung?"
+
+#: builtin/tag.c:376
+#, c-format
+msgid "The tag message has been left in %s\n"
+msgstr "Die Markierungsbeschreibung wurde gelassen in %s\n"
+
+#: builtin/tag.c:425
+msgid "switch 'points-at' requires an object"
+msgstr "Option 'points-at' erfordert ein Objekt"
+
+#: builtin/tag.c:427
+#, c-format
+msgid "malformed object name '%s'"
+msgstr "fehlerhafter Objekt-Name '%s'"
+
+#: builtin/tag.c:506
+msgid "--column and -n are incompatible"
+msgstr "--column und -n sind inkompatibel"
+
+#: builtin/tag.c:523
+msgid "-n option is only allowed with -l."
+msgstr "-n Option ist nur erlaubt mit -l."
+
+#: builtin/tag.c:525
+msgid "--contains option is only allowed with -l."
+msgstr "--contains Option ist nur erlaubt mit -l."
+
+#: builtin/tag.c:527
+msgid "--points-at option is only allowed with -l."
+msgstr "--points-at Option ist nur erlaubt mit -l."
+
+#: builtin/tag.c:535
+msgid "only one -F or -m option is allowed."
+msgstr "nur eine -F oder -m Option ist erlaubt."
+
+#: builtin/tag.c:555
+msgid "too many params"
+msgstr "zu viele Parameter"
+
+#: builtin/tag.c:561
+#, c-format
+msgid "'%s' is not a valid tag name."
+msgstr "'%s' ist kein gültiger Markierungsname."
+
+#: builtin/tag.c:566
+#, c-format
+msgid "tag '%s' already exists"
+msgstr "Markierung '%s' existiert bereits"
+
+#: builtin/tag.c:584
+#, c-format
+msgid "%s: cannot lock the ref"
+msgstr "%s: kann Referenz nicht sperren"
+
+#: builtin/tag.c:586
+#, c-format
+msgid "%s: cannot update the ref"
+msgstr "%s: kann Referenz nicht aktualisieren"
+
+#: builtin/tag.c:588
+#, c-format
+msgid "Updated tag '%s' (was %s)\n"
+msgstr "Aktualisierte Markierung '%s' (war %s)\n"
+
+#: git.c:16
+msgid "See 'git help <command>' for more information on a specific command."
+msgstr ""
+"Siehe 'git help <Kommando>' für weitere Informationen zu einem spezifischen "
+"Kommando"
+
+#: parse-options.h:133 parse-options.h:235
+msgid "n"
+msgstr "Anzahl"
+
+#: parse-options.h:141
+msgid "time"
+msgstr "Zeit"
+
+#: parse-options.h:149
+msgid "file"
+msgstr "Datei"
+
+#: parse-options.h:151
+msgid "when"
+msgstr "wann"
+
+#: parse-options.h:156
+msgid "no-op (backward compatibility)"
+msgstr "Kein Effekt (Rückwärtskompatibilität)"
+
+#: parse-options.h:228
+msgid "be more verbose"
+msgstr "erweiterte Ausgaben"
+
+#: parse-options.h:230
+msgid "be more quiet"
+msgstr "weniger Ausgaben"
+
+#: parse-options.h:236
+msgid "use <n> digits to display SHA-1s"
+msgstr "benutze <n> Ziffern zur Anzeige von SHA-1s"
+
+#: common-cmds.h:8
+msgid "Add file contents to the index"
+msgstr "stellt Dateiinhalte zur Eintragung bereit"
+
+#: common-cmds.h:9
+msgid "Find by binary search the change that introduced a bug"
+msgstr ""
+"Findet über eine Binärsuche die Änderungen, die einen Fehler verursacht haben"
+
+#: common-cmds.h:10
+msgid "List, create, or delete branches"
+msgstr "Zeigt an, erstellt oder entfernt Zweige"
+
+#: common-cmds.h:11
+msgid "Checkout a branch or paths to the working tree"
+msgstr "Checkt Zweige oder Pfade im Arbeitszweig aus"
+
+#: common-cmds.h:12
+msgid "Clone a repository into a new directory"
+msgstr "Klont ein Projektarchiv in einem neuen Verzeichnis"
+
+#: common-cmds.h:13
+msgid "Record changes to the repository"
+msgstr "Trägt Änderungen in das Projektarchiv ein"
+
+#: common-cmds.h:14
+msgid "Show changes between commits, commit and working tree, etc"
+msgstr "Zeigt Änderungen zwischen Versionen, Version und Arbeitszweig, etc. an"
+
+#: common-cmds.h:15
+msgid "Download objects and refs from another repository"
+msgstr "Lädt Objekte und Referenzen von einem anderen Projektarchiv herunter"
+
+#: common-cmds.h:16
+msgid "Print lines matching a pattern"
+msgstr "Stellt Zeilen dar, die einem Muster entsprechen"
+
+#: common-cmds.h:17
+msgid "Create an empty git repository or reinitialize an existing one"
+msgstr ""
+"Erstellt ein leeres Git-Projektarchiv oder initialisiert ein bestehendes neu"
+
+#: common-cmds.h:18
+msgid "Show commit logs"
+msgstr "Zeigt Versionshistorie an"
+
+#: common-cmds.h:19
+msgid "Join two or more development histories together"
+msgstr "Führt zwei oder mehr Entwicklungszweige zusammen"
+
+#: common-cmds.h:20
+msgid "Move or rename a file, a directory, or a symlink"
+msgstr ""
+"Verschiebt oder benennt eine Datei, ein Verzeichnis, oder eine symbolische "
+"Verknüpfung um"
+
+#: common-cmds.h:21
+msgid "Fetch from and merge with another repository or a local branch"
+msgstr ""
+"Fordert Objekte von einem externen Projektarchiv an und führt sie mit einem "
+"anderen Projektarchiv oder einem lokalen Zweig zusammen"
+
+#: common-cmds.h:22
+msgid "Update remote refs along with associated objects"
+msgstr "Aktualisiert externe Referenzen mitsamt den verbundenen Objekten"
+
+#: common-cmds.h:23
+msgid "Forward-port local commits to the updated upstream head"
+msgstr "Baut lokale Versionen auf einem aktuellerem externen Zweig neu auf"
+
+#: common-cmds.h:24
+msgid "Reset current HEAD to the specified state"
+msgstr ""
+"Setzt die aktuelle Zweigspitze (HEAD) zu einem spezifizierten Zustand zurück"
+
+#: common-cmds.h:25
+msgid "Remove files from the working tree and from the index"
+msgstr "Löscht Dateien im Arbeitszweig und von der Bereitstellung"
+
+#: common-cmds.h:26
+msgid "Show various types of objects"
+msgstr "Zeigt verschiedene Arten von Objekten an"
+
+#: common-cmds.h:27
+msgid "Show the working tree status"
+msgstr "Zeigt den Zustand des Arbeitszweiges an"
+
+#: common-cmds.h:28
+msgid "Create, list, delete or verify a tag object signed with GPG"
+msgstr ""
+"Erzeugt, listet auf, löscht oder verifiziert ein mit GPG signiertes "
+"Markierungsobjekt"
+
+#: git-am.sh:50
+msgid "You need to set your committer info first"
+msgstr "Du musst zuerst die Informationen des Eintragenden setzen."
+
+#: git-am.sh:95
+msgid ""
+"You seem to have moved HEAD since the last 'am' failure.\n"
+"Not rewinding to ORIG_HEAD"
+msgstr ""
+"Du scheinst seit dem letzten gescheiterten 'am' die Zweigspitze (HEAD)\n"
+"geändert zu haben.\n"
+"Keine Zurücksetzung zu ORIG_HEAD."
+
+#: git-am.sh:105
+#, sh-format
+msgid ""
+"When you have resolved this problem run \"$cmdline --resolved\".\n"
+"If you would prefer to skip this patch, instead run \"$cmdline --skip\".\n"
+"To restore the original branch and stop patching run \"$cmdline --abort\"."
+msgstr ""
+"Wenn du das Problem aufgelöst hast, führe \"$cmdline --resolved\" aus.\n"
+"Falls du diesen Patch auslassen möchtest, führe stattdessen \"$cmdline --skip"
+"\" aus.\n"
+"Um den ursprünglichen Zweig wiederherzustellen und die Anwendung der "
+"Patches\n"
+"abzubrechen, führe \"$cmdline --abort\" aus."
+
+#: git-am.sh:121
+msgid "Cannot fall back to three-way merge."
+msgstr "Kann nicht zu 3-Wege-Zusammenführung zurückfallen."
+
+#: git-am.sh:137
+msgid "Repository lacks necessary blobs to fall back on 3-way merge."
+msgstr ""
+"Dem Projektarchiv fehlen notwendige Blobs um auf eine 3-Wege-Zusammenführung "
+"zurückzufallen."
+
+#: git-am.sh:154
+msgid ""
+"Did you hand edit your patch?\n"
+"It does not apply to blobs recorded in its index."
+msgstr ""
+"Hast du den Patch per Hand editiert?\n"
+"Er kann nicht auf die Blobs in seiner 'index' Zeile angewendet werden."
+
+#: git-am.sh:163
+msgid "Falling back to patching base and 3-way merge..."
+msgstr "Falle zurück zum Patchen der Basis und der 3-Wege-Zusammenführung..."
+
+#: git-am.sh:275
+msgid "Only one StGIT patch series can be applied at once"
+msgstr "Es kann nur eine StGIT Patch-Serie auf einmal angewendet werden."
+
+#: git-am.sh:362
+#, sh-format
+msgid "Patch format $patch_format is not supported."
+msgstr "Patch-Format $patch_format wird nicht unterstützt."
+
+#: git-am.sh:364
+msgid "Patch format detection failed."
+msgstr "Patch-Formaterkennung fehlgeschlagen."
+
+#: git-am.sh:418
+msgid "-d option is no longer supported.  Do not use."
+msgstr "-d Option wird nicht länger unterstützt. Nicht benutzen."
+
+#: git-am.sh:481
+#, sh-format
+msgid "previous rebase directory $dotest still exists but mbox given."
+msgstr ""
+"Vorheriges Verzeichnis des Neuaufbaus $dotest existiert noch, aber mbox "
+"gegeben."
+
+#: git-am.sh:486
+msgid "Please make up your mind. --skip or --abort?"
+msgstr "Bitte werde dir klar. --skip oder --abort?"
+
+#: git-am.sh:513
+msgid "Resolve operation not in progress, we are not resuming."
+msgstr "Es ist keine Auflösung im Gange, es wird nicht fortgesetzt."
+
+#: git-am.sh:579
+#, sh-format
+msgid "Dirty index: cannot apply patches (dirty: $files)"
+msgstr ""
+"Unsaubere Bereitstellung: kann Patches nicht anwenden (unsauber: $files)"
+
+#: git-am.sh:671
+#, sh-format
+msgid ""
+"Patch is empty.  Was it split wrong?\n"
+"If you would prefer to skip this patch, instead run \"$cmdline --skip\".\n"
+"To restore the original branch and stop patching run \"$cmdline --abort\"."
+msgstr ""
+"Patch ist leer. Wurde er falsch aufgeteilt?\n"
+"Wenn du diesen Patch auslassen möchtest, führe stattdessen \"$cmdline --skip"
+"\" aus.\n"
+"Um den ursprünglichen Zweig wiederherzustellen und die Anwendung der "
+"Patches\n"
+"abzubrechen, führe \"$cmdline --abort\" aus."
+
+#: git-am.sh:708
+msgid "Patch does not have a valid e-mail address."
+msgstr "Patch enthält keine gültige eMail-Adresse."
+
+#: git-am.sh:755
+msgid "cannot be interactive without stdin connected to a terminal."
+msgstr ""
+"Kann nicht interaktiv sein, ohne dass die Standard-Eingabe mit einem "
+"Terminal verbunden ist."
+
+#: git-am.sh:759
+msgid "Commit Body is:"
+msgstr "Beschreibung der Eintragung ist:"
+
+#. TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
+#. in your translation. The program will only accept English
+#. input at this point.
+#: git-am.sh:766
+msgid "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
+msgstr "Anwenden? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
+
+#: git-am.sh:802
+#, sh-format
+msgid "Applying: $FIRSTLINE"
+msgstr "Wende an: $FIRSTLINE"
+
+#: git-am.sh:823
+msgid ""
+"No changes - did you forget to use 'git add'?\n"
+"If there is nothing left to stage, chances are that something else\n"
+"already introduced the same changes; you might want to skip this patch."
+msgstr ""
+"Keine Änderungen - hast du vergessen 'git add' zu benutzen?\n"
+"Wenn keine Änderungen mehr zum Bereitstellen vorhanden sind, könnten\n"
+"diese bereits anderweitig eingefügt worden sein; du könntest diesen Patch\n"
+"auslassen."
+
+#: git-am.sh:831
+msgid ""
+"You still have unmerged paths in your index\n"
+"did you forget to use 'git add'?"
+msgstr ""
+"Du hast immer noch nicht zusammengeführte Pfade in der Bereitstellung.\n"
+"Hast du vergessen 'git add' zu benutzen?"
+
+#: git-am.sh:847
+msgid "No changes -- Patch already applied."
+msgstr "Keine Änderungen -- Patches bereits angewendet."
+
+#: git-am.sh:857
+#, sh-format
+msgid "Patch failed at $msgnum $FIRSTLINE"
+msgstr "Anwendung des Patches fehlgeschlagen bei $msgnum $FIRSTLINE"
+
+#: git-am.sh:873
+msgid "applying to an empty history"
+msgstr "wende zu leerer Historie an"
+
+#: git-bisect.sh:48
+msgid "You need to start by \"git bisect start\""
+msgstr "Du musst mit \"git bisect start\" beginnen."
+
+#. TRANSLATORS: Make sure to include [Y] and [n] in your
+#. translation. The program will only accept English input
+#. at this point.
+#: git-bisect.sh:54
+msgid "Do you want me to do it for you [Y/n]? "
+msgstr "Willst du, dass ich es für dich mache [Y/n]? "
+
+#: git-bisect.sh:95
+#, sh-format
+msgid "unrecognised option: '$arg'"
+msgstr "nicht erkannte Option: '$arg'"
+
+#: git-bisect.sh:99
+#, sh-format
+msgid "'$arg' does not appear to be a valid revision"
+msgstr "'$arg' scheint keine gültige Version zu sein"
+
+#: git-bisect.sh:117
+msgid "Bad HEAD - I need a HEAD"
+msgstr "Ungültige Zweigspitze (HEAD) - Zweigspitze (HEAD) wird benötigt"
+
+#: git-bisect.sh:130
+#, sh-format
+msgid ""
+"Checking out '$start_head' failed. Try 'git bisect reset <validbranch>'."
+msgstr ""
+"Auschecken von '$start_head' fehlgeschlagen. Versuche 'git bisect reset "
+"<gueltigerzweig>'."
+
+#: git-bisect.sh:140
+msgid "won't bisect on seeked tree"
+msgstr "\"bisect\" auf gesuchtem Zweig nicht möglich"
+
+#: git-bisect.sh:144
+msgid "Bad HEAD - strange symbolic ref"
+msgstr "Ungültige Zweigspitze (HEAD) - merkwürdige symbolische Referenz"
+
+#: git-bisect.sh:189
+#, sh-format
+msgid "Bad bisect_write argument: $state"
+msgstr "Ungültiges \"bisect_write\" Argument: $state"
+
+#: git-bisect.sh:218
+#, sh-format
+msgid "Bad rev input: $arg"
+msgstr "Ungültige Referenz-Eingabe: $arg"
+
+#: git-bisect.sh:232
+msgid "Please call 'bisect_state' with at least one argument."
+msgstr "Bitte rufe 'bisect_state' mit mindestens einem Argument auf."
+
+#: git-bisect.sh:244
+#, sh-format
+msgid "Bad rev input: $rev"
+msgstr "Ungültige Referenz-Eingabe: $rev"
+
+#: git-bisect.sh:250
+msgid "'git bisect bad' can take only one argument."
+msgstr "'git bisect bad' kann nur ein Argument entgegennehmen."
+
+#. have bad but not good.  we could bisect although
+#. this is less optimum.
+#: git-bisect.sh:273
+msgid "Warning: bisecting only with a bad commit."
+msgstr "Warnung: halbiere nur mit einer fehlerhaften Version"
+
+#. TRANSLATORS: Make sure to include [Y] and [n] in your
+#. translation. The program will only accept English input
+#. at this point.
+#: git-bisect.sh:279
+msgid "Are you sure [Y/n]? "
+msgstr "Bist du sicher [Y/n]? "
+
+#: git-bisect.sh:289
+msgid ""
+"You need to give me at least one good and one bad revisions.\n"
+"(You can use \"git bisect bad\" and \"git bisect good\" for that.)"
+msgstr ""
+"Du musst mindestens eine korrekte und eine fehlerhafte Version angeben.\n"
+"(Du kannst dafür \"git bisect bad\" und \"git bisect good\" benutzen.)"
+
+#: git-bisect.sh:292
+msgid ""
+"You need to start by \"git bisect start\".\n"
+"You then need to give me at least one good and one bad revisions.\n"
+"(You can use \"git bisect bad\" and \"git bisect good\" for that.)"
+msgstr ""
+"Du musst mit \"git bisect start\" beginnen.\n"
+"Danach musst du mindestens eine korrekte und eine fehlerhafte Version "
+"angeben.\n"
+"(Du kannst dafür \"git bisect bad\" und \"git bisect good\" benutzen.)"
+
+#: git-bisect.sh:347 git-bisect.sh:474
+msgid "We are not bisecting."
+msgstr "Wir sind nicht beim Halbieren."
+
+#: git-bisect.sh:354
+#, sh-format
+msgid "'$invalid' is not a valid commit"
+msgstr "'$invalid' ist keine gültige Version"
+
+#: git-bisect.sh:363
+#, sh-format
+msgid ""
+"Could not check out original HEAD '$branch'.\n"
+"Try 'git bisect reset <commit>'."
+msgstr ""
+"Konnte die ursprüngliche Zweigspitze (HEAD) '$branch' nicht auschecken.\n"
+"Versuche 'git bisect reset <Version>'."
+
+#: git-bisect.sh:390
+msgid "No logfile given"
+msgstr "Keine Log-Datei gegeben"
+
+#: git-bisect.sh:391
+#, sh-format
+msgid "cannot read $file for replaying"
+msgstr "kann $file nicht für das Abspielen lesen"
+
+#: git-bisect.sh:408
+msgid "?? what are you talking about?"
+msgstr "?? Was redest du da?"
+
+#: git-bisect.sh:420
+#, sh-format
+msgid "running $command"
+msgstr "führe $command aus"
+
+#: git-bisect.sh:427
+#, sh-format
+msgid ""
+"bisect run failed:\n"
+"exit code $res from '$command' is < 0 or >= 128"
+msgstr ""
+"Ausführung der Halbierung fehlgeschlagen:\n"
+"Rückkehrwert $res von '$command' ist < 0 oder >= 128"
+
+#: git-bisect.sh:453
+msgid "bisect run cannot continue any more"
+msgstr "Ausführung der Halbierung kann nicht mehr fortgesetzt werden"
+
+#: git-bisect.sh:459
+#, sh-format
+msgid ""
+"bisect run failed:\n"
+"'bisect_state $state' exited with error code $res"
+msgstr ""
+"Ausführung der Halbierung fehlgeschlagen:\n"
+"'bisect_state $state' wurde mit Fehlerwert $res beendet"
+
+#: git-bisect.sh:466
+msgid "bisect run success"
+msgstr "Halbierung erfolgreich ausgeführt"
+
+#: git-pull.sh:21
+msgid ""
+"Pull is not possible because you have unmerged files.\n"
+"Please, fix them up in the work tree, and then use 'git add/rm <file>'\n"
+"as appropriate to mark resolution, or use 'git commit -a'."
+msgstr ""
+"\"pull\" ist nicht möglich, weil du nicht zusammengeführte Dateien hast.\n"
+"Bitte korrigiere dies im Arbeitsbaum und benutze dann 'git add/rm <Datei>'\n"
+"um die Auflösung entsprechend zu markieren, oder benutze 'git commit -a'."
+
+#: git-pull.sh:25
+msgid "Pull is not possible because you have unmerged files."
+msgstr ""
+"\"pull\" ist nicht möglich, weil du nicht zusammengeführte Dateien hast."
+
+#: git-pull.sh:197
+msgid "updating an unborn branch with changes added to the index"
+msgstr ""
+"Aktualisiere eine ungeborenen Zweig mit Änderungen, die zur Bereitstellung "
+"hinzugefügt wurden"
+
+#. The fetch involved updating the current branch.
+#. The working tree and the index file is still based on the
+#. $orig_head commit, but we are merging into $curr_head.
+#. First update the working tree to match $curr_head.
+#: git-pull.sh:228
+#, sh-format
+msgid ""
+"Warning: fetch updated the current branch head.\n"
+"Warning: fast-forwarding your working tree from\n"
+"Warning: commit $orig_head."
+msgstr ""
+"Warnung: Die Anforderung aktualisierte die Spitze des aktuellen Zweiges.\n"
+"Warnung: Spule deinen Arbeitszweig von Version $orig_head vor."
+
+#: git-pull.sh:253
+msgid "Cannot merge multiple branches into empty head"
+msgstr "Kann nicht mehrere Zweige in einen ungeborenen Zweig zusammenführen"
+
+#: git-pull.sh:257
+msgid "Cannot rebase onto multiple branches"
+msgstr "kann nicht auf mehrere Zweige neu aufbauen"
+
+#: git-stash.sh:51
+msgid "git stash clear with parameters is unimplemented"
+msgstr "git stash clear mit Parametern ist nicht implementiert"
+
+#: git-stash.sh:74
+msgid "You do not have the initial commit yet"
+msgstr "Du hast bisher noch keine initiale Version"
+
+#: git-stash.sh:89
+msgid "Cannot save the current index state"
+msgstr "Kann den aktuellen Zustand der Bereitstellung nicht speichern"
+
+#: git-stash.sh:123 git-stash.sh:136
+msgid "Cannot save the current worktree state"
+msgstr "Kann den aktuellen Zustand des Arbeitsbaumes nicht speichern"
+
+#: git-stash.sh:140
+msgid "No changes selected"
+msgstr "Keine Änderungen ausgewählt"
+
+#: git-stash.sh:143
+msgid "Cannot remove temporary index (can't happen)"
+msgstr "Kann temporäre Bereitstellung nicht entfernen (kann nicht passieren)"
+
+#: git-stash.sh:156
+msgid "Cannot record working tree state"
+msgstr "Kann Zustand des Arbeitsbaumes nicht aufzeichnen"
+
+#. TRANSLATORS: $option is an invalid option, like
+#. `--blah-blah'. The 7 spaces at the beginning of the
+#. second line correspond to "error: ". So you should line
+#. up the second line with however many characters the
+#. translation of "error: " takes in your language. E.g. in
+#. English this is:
+#.
+#. $ git stash save --blah-blah 2>&1 | head -n 2
+#. error: unknown option for 'stash save': --blah-blah
+#. To provide a message, use git stash save -- '--blah-blah'
+#: git-stash.sh:202
+#, sh-format
+msgid ""
+"error: unknown option for 'stash save': $option\n"
+"       To provide a message, use git stash save -- '$option'"
+msgstr ""
+"Fehler: unbekannte Option für 'stash save': $option\n"
+"        Um eine Beschreibung anzugeben, benutze \"git stash save -- "
+"'$option'\""
+
+#: git-stash.sh:223
+msgid "No local changes to save"
+msgstr "Keine lokalen Änderungen zum Speichern"
+
+#: git-stash.sh:227
+msgid "Cannot initialize stash"
+msgstr "Kann \"stash\" nicht initialisieren"
+
+#: git-stash.sh:235
+msgid "Cannot save the current status"
+msgstr "Kann den aktuellen Status nicht speichern"
+
+#: git-stash.sh:253
+msgid "Cannot remove worktree changes"
+msgstr "Kann Änderungen am Arbeitsbaum nicht entfernen"
+
+#: git-stash.sh:352
+msgid "No stash found."
+msgstr "Kein \"stash\" gefunden."
+
+#: git-stash.sh:359
+#, sh-format
+msgid "Too many revisions specified: $REV"
+msgstr "Zu viele Revisionen angegeben: $REV"
+
+#: git-stash.sh:365
+#, sh-format
+msgid "$reference is not valid reference"
+msgstr "$reference ist keine gültige Referenz"
+
+#: git-stash.sh:393
+#, sh-format
+msgid "'$args' is not a stash-like commit"
+msgstr "'$args' ist keine \"stash\"-artige Version"
+
+#: git-stash.sh:404
+#, sh-format
+msgid "'$args' is not a stash reference"
+msgstr "'$args' ist keine \"stash\"-Referenz"
+
+#: git-stash.sh:412
+msgid "unable to refresh index"
+msgstr "unfähig die Bereitstellung zu aktualisieren"
+
+#: git-stash.sh:416
+msgid "Cannot apply a stash in the middle of a merge"
+msgstr ""
+"Kann \"stash\" nicht anwenden, solang eine Zusammenführung im Gange ist"
+
+#: git-stash.sh:424
+msgid "Conflicts in index. Try without --index."
+msgstr "Konflikte in der Bereitstellung. Versuche es ohne --index."
+
+#: git-stash.sh:426
+msgid "Could not save index tree"
+msgstr "Konnte Bereitstellungsbaum nicht speichern"
+
+#: git-stash.sh:460
+msgid "Cannot unstage modified files"
+msgstr "Kann geänderte Dateien nicht aus der Bereitstellung herausnehmen"
+
+#: git-stash.sh:474
+msgid "Index was not unstashed."
+msgstr "Bereitstellung wurde nicht ausgelagert."
+
+#: git-stash.sh:491
+#, sh-format
+msgid "Dropped ${REV} ($s)"
+msgstr "Gelöscht ${REV} ($s)"
+
+#: git-stash.sh:492
+#, sh-format
+msgid "${REV}: Could not drop stash entry"
+msgstr "${REV}: Konnte \"stash\"-Eintrag nicht löschen"
+
+#: git-stash.sh:499
+msgid "No branch name specified"
+msgstr "Kein Zweigname spezifiziert"
+
+#: git-stash.sh:570
+msgid "(To restore them type \"git stash apply\")"
+msgstr "(Zur Wiederherstellung gebe \"git stash apply\" ein)"
+
+#: git-submodule.sh:88
+#, sh-format
+msgid "cannot strip one component off url '$remoteurl'"
+msgstr "Kann eine Komponente von URL '$remoteurl' nicht extrahieren"
+
+#: git-submodule.sh:145
+#, sh-format
+msgid "No submodule mapping found in .gitmodules for path '$sm_path'"
+msgstr ""
+"Keine Unterprojekt-Zuordnung in .gitmodules für Pfad '$sm_path' gefunden"
+
+#: git-submodule.sh:186
+#, sh-format
+msgid "Clone of '$url' into submodule path '$sm_path' failed"
+msgstr "Klonen von '$url' in Unterprojekt-Pfad '$sm_path' fehlgeschlagen"
+
+#: git-submodule.sh:196
+#, sh-format
+msgid "Gitdir '$a' is part of the submodule path '$b' or vice versa"
+msgstr ""
+"Git-Verzeichnis '$a' ist Teil des Unterprojekt-Pfades '$b', oder umgekehrt"
+
+#: git-submodule.sh:285
+#, sh-format
+msgid "repo URL: '$repo' must be absolute or begin with ./|../"
+msgstr "repo URL: '$repo' muss absolut sein oder mit ./|../ beginnen"
+
+#: git-submodule.sh:302
+#, sh-format
+msgid "'$sm_path' already exists in the index"
+msgstr "'$sm_path' existiert bereits in der Bereitstellung"
+
+#: git-submodule.sh:306
+#, sh-format
+msgid ""
+"The following path is ignored by one of your .gitignore files:\n"
+"$sm_path\n"
+"Use -f if you really want to add it."
+msgstr ""
+"Der folgende Pfad wird durch eine deiner \".gitignore\" Dateien ignoriert:\n"
+"$sm_path\n"
+"Benutze -f wenn du diesen wirklich hinzufügen möchtest."
+
+#: git-submodule.sh:317
+#, sh-format
+msgid "Adding existing repo at '$sm_path' to the index"
+msgstr ""
+"Füge existierendes Projektarchiv in '$sm_path' der Bereitstellung hinzu."
+
+#: git-submodule.sh:319
+#, sh-format
+msgid "'$sm_path' already exists and is not a valid git repo"
+msgstr "'$sm_path' existiert bereits und ist kein gültiges Git-Projektarchiv"
+
+#: git-submodule.sh:333
+#, sh-format
+msgid "Unable to checkout submodule '$sm_path'"
+msgstr "Unfähig Unterprojekt '$sm_path' auszuchecken"
+
+#: git-submodule.sh:338
+#, sh-format
+msgid "Failed to add submodule '$sm_path'"
+msgstr "Hinzufügen von Unterprojekt '$sm_path' fehlgeschlagen"
+
+#: git-submodule.sh:343
+#, sh-format
+msgid "Failed to register submodule '$sm_path'"
+msgstr "Registierung von Unterprojekt '$sm_path' fehlgeschlagen"
+
+#: git-submodule.sh:385
+#, sh-format
+msgid "Entering '$prefix$sm_path'"
+msgstr "Betrete '$prefix$sm_path'"
+
+#: git-submodule.sh:399
+#, sh-format
+msgid "Stopping at '$sm_path'; script returned non-zero status."
+msgstr "Stoppe bei '$sm_path'; Skript gab nicht-Null Status zurück."
+
+#: git-submodule.sh:442
+#, sh-format
+msgid "No url found for submodule path '$sm_path' in .gitmodules"
+msgstr "Keine URL für Unterprojekt-Pfad '$sm_path' in .gitmodules gefunden"
+
+#: git-submodule.sh:451
+#, sh-format
+msgid "Failed to register url for submodule path '$sm_path'"
+msgstr "Registrierung der URL für Unterprojekt-Pfad '$sm_path' fehlgeschlagen"
+
+#: git-submodule.sh:453
+#, sh-format
+msgid "Submodule '$name' ($url) registered for path '$sm_path'"
+msgstr "Unterprojekt '$name' ($url) ist für Pfad '$sm_path' registriert"
+
+#: git-submodule.sh:461
+#, sh-format
+msgid "Failed to register update mode for submodule path '$sm_path'"
+msgstr ""
+"Registrierung des Aktualisierungsmodus für Unterprojekt-Pfad '$sm_path' "
+"fehlgeschlagen"
+
+#: git-submodule.sh:560
+#, sh-format
+msgid ""
+"Submodule path '$sm_path' not initialized\n"
+"Maybe you want to use 'update --init'?"
+msgstr ""
+"Unterprojekt-Pfad '$sm_path' ist nicht initialisiert\n"
+"Vielleicht möchtest du 'update --init' benutzen?"
+
+#: git-submodule.sh:573
+#, sh-format
+msgid "Unable to find current revision in submodule path '$sm_path'"
+msgstr "Konnte aktuelle Version in Unterprojekt-Pfad '$sm_path' nicht finden"
+
+#: git-submodule.sh:592
+#, sh-format
+msgid "Unable to fetch in submodule path '$sm_path'"
+msgstr "Konnte in Unterprojekt-Pfad '$sm_path' nicht anfordern"
+
+#: git-submodule.sh:606
+#, sh-format
+msgid "Unable to rebase '$sha1' in submodule path '$sm_path'"
+msgstr "Neuaufbau von '$sha1' in Unterprojekt-Pfad '$sm_path' nicht möglich"
+
+#: git-submodule.sh:607
+#, sh-format
+msgid "Submodule path '$sm_path': rebased into '$sha1'"
+msgstr "Unterprojekt-Pfad '$sm_path': neu aufgebaut in '$sha1'"
+
+#: git-submodule.sh:612
+#, sh-format
+msgid "Unable to merge '$sha1' in submodule path '$sm_path'"
+msgstr ""
+"Zusammenführung von '$sha1' in Unterprojekt-Pfad '$sm_path' fehlgeschlagen"
+
+#: git-submodule.sh:613
+#, sh-format
+msgid "Submodule path '$sm_path': merged in '$sha1'"
+msgstr "Unterprojekt-Pfad '$sm_path': zusammengeführt in '$sha1'"
+
+#: git-submodule.sh:618
+#, sh-format
+msgid "Unable to checkout '$sha1' in submodule path '$sm_path'"
+msgstr "Konnte '$sha1' in Unterprojekt-Pfad '$sm_path' nicht auschecken."
+
+#: git-submodule.sh:619
+#, sh-format
+msgid "Submodule path '$sm_path': checked out '$sha1'"
+msgstr "Unterprojekt-Pfad: '$sm_path': '$sha1' ausgecheckt"
+
+#: git-submodule.sh:641 git-submodule.sh:964
+#, sh-format
+msgid "Failed to recurse into submodule path '$sm_path'"
+msgstr "Fehler bei Rekursion in Unterprojekt-Pfad '$sm_path'"
+
+#: git-submodule.sh:749
+msgid "--cached cannot be used with --files"
+msgstr "--cached kann nicht mit --files benutzt werden"
+
+#. unexpected type
+#: git-submodule.sh:789
+#, sh-format
+msgid "unexpected mode $mod_dst"
+msgstr "unerwarteter Modus $mod_dst"
+
+#: git-submodule.sh:807
+#, sh-format
+msgid "  Warn: $name doesn't contain commit $sha1_src"
+msgstr "  Warnung: $name beinhaltet nicht Version $sha1_src"
+
+#: git-submodule.sh:810
+#, sh-format
+msgid "  Warn: $name doesn't contain commit $sha1_dst"
+msgstr "  Warnung: $name beinhaltet nicht Version $sha1_dst"
+
+#: git-submodule.sh:813
+#, sh-format
+msgid "  Warn: $name doesn't contain commits $sha1_src and $sha1_dst"
+msgstr ""
+"  Warnung: $name beinhaltet nicht die Versionen $sha1_src und $sha1_dst"
+
+#: git-submodule.sh:838
+msgid "blob"
+msgstr "Blob"
+
+#: git-submodule.sh:839
+msgid "submodule"
+msgstr "Unterprojekt"
+
+#: git-submodule.sh:876
+msgid "# Submodules changed but not updated:"
+msgstr "# Unterprojekte geändert, aber nicht aktualisiert:"
+
+#: git-submodule.sh:878
+msgid "# Submodule changes to be committed:"
+msgstr "# Änderungen in Unterprojekt zum Eintragen:"
+
+#: git-submodule.sh:1022
+#, sh-format
+msgid "Synchronizing submodule url for '$name'"
+msgstr "Synchronisiere Unterprojekt-URL für '$name'"
+
+#~ msgid "--"
+#~ msgstr "--"
+
+#~ msgid "Could not extract email from committer identity."
+#~ msgstr "Konnte E-Mail-Adresse des Einreichers nicht extrahieren."
+
+#~ msgid "cherry-pick"
+#~ msgstr "cherry-pick"
+
+#~ msgid "Please enter the commit message for your changes."
+#~ msgstr "Bitte gebe die Versionsbeschreibung für deine Änderungen ein."
+
+#~ msgid "Too many options specified"
+#~ msgstr "Zu viele Optionen angegeben"
+
+#~ msgid ""
+#~ "To prevent you from losing history, non-fast-forward updates were "
+#~ "rejected\n"
+#~ "Merge the remote changes (e.g. 'git pull') before pushing again.  See "
+#~ "the\n"
+#~ "'Note about fast-forwards' section of 'git push --help' for details.\n"
+#~ msgstr ""
+#~ "Um dich vor Verlust von Historie zu bewahren, wurden nicht vorzuspulende "
+#~ "Aktualisierungen zurückgewiesen.\n"
+#~ "Führe die externen Änderungen zusammen (z.B. 'git pull') bevor du erneut "
+#~ "versendest. Siehe auch die 'Note about fast-forwards' Sektion von \n"
+#~ "'git push --help' für weitere Details.\n"
diff --git a/po/git.pot b/po/git.pot
new file mode 100644
index 0000000..3d9ae75
--- /dev/null
+++ b/po/git.pot
@@ -0,0 +1,5334 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
+"POT-Creation-Date: 2012-07-03 10:23+0800\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
+
+#: advice.c:40
+#, c-format
+msgid "hint: %.*s\n"
+msgstr ""
+
+#.
+#. * Message used both when 'git commit' fails and when
+#. * other commands doing a merge do.
+#.
+#: advice.c:70
+msgid ""
+"Fix them up in the work tree,\n"
+"and then use 'git add/rm <file>' as\n"
+"appropriate to mark resolution and make a commit,\n"
+"or use 'git commit -a'."
+msgstr ""
+
+#: bundle.c:36
+#, c-format
+msgid "'%s' does not look like a v2 bundle file"
+msgstr ""
+
+#: bundle.c:63
+#, c-format
+msgid "unrecognized header: %s%s (%d)"
+msgstr ""
+
+#: bundle.c:89 builtin/commit.c:696
+#, c-format
+msgid "could not open '%s'"
+msgstr ""
+
+#: bundle.c:140
+msgid "Repository lacks these prerequisite commits:"
+msgstr ""
+
+#: bundle.c:164 sequencer.c:550 sequencer.c:982 builtin/log.c:290
+#: builtin/log.c:721 builtin/log.c:1310 builtin/log.c:1529 builtin/merge.c:347
+#: builtin/shortlog.c:181
+msgid "revision walk setup failed"
+msgstr ""
+
+#: bundle.c:186
+#, c-format
+msgid "The bundle contains %d ref"
+msgid_plural "The bundle contains %d refs"
+msgstr[0] ""
+msgstr[1] ""
+
+#: bundle.c:192
+msgid "The bundle records a complete history."
+msgstr ""
+
+#: bundle.c:195
+#, c-format
+msgid "The bundle requires this ref"
+msgid_plural "The bundle requires these %d refs"
+msgstr[0] ""
+msgstr[1] ""
+
+#: bundle.c:294
+msgid "rev-list died"
+msgstr ""
+
+#: bundle.c:300 builtin/log.c:1206 builtin/shortlog.c:284
+#, c-format
+msgid "unrecognized argument: %s"
+msgstr ""
+
+#: bundle.c:335
+#, c-format
+msgid "ref '%s' is excluded by the rev-list options"
+msgstr ""
+
+#: bundle.c:380
+msgid "Refusing to create empty bundle."
+msgstr ""
+
+#: bundle.c:398
+msgid "Could not spawn pack-objects"
+msgstr ""
+
+#: bundle.c:416
+msgid "pack-objects died"
+msgstr ""
+
+#: bundle.c:419
+#, c-format
+msgid "cannot create '%s'"
+msgstr ""
+
+#: bundle.c:441
+msgid "index-pack died"
+msgstr ""
+
+#: commit.c:48
+#, c-format
+msgid "could not parse %s"
+msgstr ""
+
+#: commit.c:50
+#, c-format
+msgid "%s %s is not a commit!"
+msgstr ""
+
+#: compat/obstack.c:406 compat/obstack.c:408
+msgid "memory exhausted"
+msgstr ""
+
+#: connected.c:39
+msgid "Could not run 'git rev-list'"
+msgstr ""
+
+#: connected.c:48
+#, c-format
+msgid "failed write to rev-list: %s"
+msgstr ""
+
+#: connected.c:56
+#, c-format
+msgid "failed to close rev-list's stdin: %s"
+msgstr ""
+
+#: date.c:95
+msgid "in the future"
+msgstr ""
+
+#: date.c:101
+#, c-format
+msgid "%lu second ago"
+msgid_plural "%lu seconds ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: date.c:108
+#, c-format
+msgid "%lu minute ago"
+msgid_plural "%lu minutes ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: date.c:115
+#, c-format
+msgid "%lu hour ago"
+msgid_plural "%lu hours ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: date.c:122
+#, c-format
+msgid "%lu day ago"
+msgid_plural "%lu days ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: date.c:128
+#, c-format
+msgid "%lu week ago"
+msgid_plural "%lu weeks ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: date.c:135
+#, c-format
+msgid "%lu month ago"
+msgid_plural "%lu months ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: date.c:146
+#, c-format
+msgid "%lu year"
+msgid_plural "%lu years"
+msgstr[0] ""
+msgstr[1] ""
+
+#: date.c:149
+#, c-format
+msgid "%s, %lu month ago"
+msgid_plural "%s, %lu months ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: date.c:154 date.c:159
+#, c-format
+msgid "%lu year ago"
+msgid_plural "%lu years ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: diff.c:105
+#, c-format
+msgid "  Failed to parse dirstat cut-off percentage '%.*s'\n"
+msgstr ""
+
+#: diff.c:110
+#, c-format
+msgid "  Unknown dirstat parameter '%.*s'\n"
+msgstr ""
+
+#: diff.c:210
+#, c-format
+msgid ""
+"Found errors in 'diff.dirstat' config variable:\n"
+"%s"
+msgstr ""
+
+#: diff.c:1400
+msgid " 0 files changed\n"
+msgstr ""
+
+#: diff.c:1404
+#, c-format
+msgid " %d file changed"
+msgid_plural " %d files changed"
+msgstr[0] ""
+msgstr[1] ""
+
+#: diff.c:1421
+#, c-format
+msgid ", %d insertion(+)"
+msgid_plural ", %d insertions(+)"
+msgstr[0] ""
+msgstr[1] ""
+
+#: diff.c:1432
+#, c-format
+msgid ", %d deletion(-)"
+msgid_plural ", %d deletions(-)"
+msgstr[0] ""
+msgstr[1] ""
+
+#: diff.c:3478
+#, c-format
+msgid ""
+"Failed to parse --dirstat/-X option parameter:\n"
+"%s"
+msgstr ""
+
+#: gpg-interface.c:59
+msgid "could not run gpg."
+msgstr ""
+
+#: gpg-interface.c:71
+msgid "gpg did not accept the data"
+msgstr ""
+
+#: gpg-interface.c:82
+msgid "gpg failed to sign the data"
+msgstr ""
+
+#: grep.c:1320
+#, c-format
+msgid "'%s': unable to read %s"
+msgstr ""
+
+#: grep.c:1337
+#, c-format
+msgid "'%s': %s"
+msgstr ""
+
+#: grep.c:1348
+#, c-format
+msgid "'%s': short read %s"
+msgstr ""
+
+#: help.c:208
+#, c-format
+msgid "available git commands in '%s'"
+msgstr ""
+
+#: help.c:215
+msgid "git commands available from elsewhere on your $PATH"
+msgstr ""
+
+#: help.c:271
+#, c-format
+msgid ""
+"'%s' appears to be a git command, but we were not\n"
+"able to execute it. Maybe git-%s is broken?"
+msgstr ""
+
+#: help.c:328
+msgid "Uh oh. Your system reports no Git commands at all."
+msgstr ""
+
+#: help.c:350
+#, c-format
+msgid ""
+"WARNING: You called a Git command named '%s', which does not exist.\n"
+"Continuing under the assumption that you meant '%s'"
+msgstr ""
+
+#: help.c:355
+#, c-format
+msgid "in %0.1f seconds automatically..."
+msgstr ""
+
+#: help.c:362
+#, c-format
+msgid "git: '%s' is not a git command. See 'git --help'."
+msgstr ""
+
+#: help.c:366
+msgid ""
+"\n"
+"Did you mean this?"
+msgid_plural ""
+"\n"
+"Did you mean one of these?"
+msgstr[0] ""
+msgstr[1] ""
+
+#: parse-options.c:493
+msgid "..."
+msgstr ""
+
+#: parse-options.c:511
+#, c-format
+msgid "usage: %s"
+msgstr ""
+
+#. TRANSLATORS: the colon here should align with the
+#. one in "usage: %s" translation
+#: parse-options.c:515
+#, c-format
+msgid "   or: %s"
+msgstr ""
+
+#: parse-options.c:518
+#, c-format
+msgid "    %s"
+msgstr ""
+
+#: remote.c:1629
+#, c-format
+msgid "Your branch is ahead of '%s' by %d commit.\n"
+msgid_plural "Your branch is ahead of '%s' by %d commits.\n"
+msgstr[0] ""
+msgstr[1] ""
+
+#: remote.c:1635
+#, c-format
+msgid "Your branch is behind '%s' by %d commit, and can be fast-forwarded.\n"
+msgid_plural ""
+"Your branch is behind '%s' by %d commits, and can be fast-forwarded.\n"
+msgstr[0] ""
+msgstr[1] ""
+
+#: remote.c:1643
+#, c-format
+msgid ""
+"Your branch and '%s' have diverged,\n"
+"and have %d and %d different commit each, respectively.\n"
+msgid_plural ""
+"Your branch and '%s' have diverged,\n"
+"and have %d and %d different commits each, respectively.\n"
+msgstr[0] ""
+msgstr[1] ""
+
+#: sequencer.c:121 builtin/merge.c:865 builtin/merge.c:978
+#: builtin/merge.c:1088 builtin/merge.c:1098
+#, c-format
+msgid "Could not open '%s' for writing"
+msgstr ""
+
+#: sequencer.c:123 builtin/merge.c:333 builtin/merge.c:868
+#: builtin/merge.c:1090 builtin/merge.c:1103
+#, c-format
+msgid "Could not write to '%s'"
+msgstr ""
+
+#: sequencer.c:144
+msgid ""
+"after resolving the conflicts, mark the corrected paths\n"
+"with 'git add <paths>' or 'git rm <paths>'"
+msgstr ""
+
+#: sequencer.c:147
+msgid ""
+"after resolving the conflicts, mark the corrected paths\n"
+"with 'git add <paths>' or 'git rm <paths>'\n"
+"and commit the result with 'git commit'"
+msgstr ""
+
+#: sequencer.c:160 sequencer.c:758 sequencer.c:841
+#, c-format
+msgid "Could not write to %s"
+msgstr ""
+
+#: sequencer.c:163
+#, c-format
+msgid "Error wrapping up %s"
+msgstr ""
+
+#: sequencer.c:178
+msgid "Your local changes would be overwritten by cherry-pick."
+msgstr ""
+
+#: sequencer.c:180
+msgid "Your local changes would be overwritten by revert."
+msgstr ""
+
+#: sequencer.c:183
+msgid "Commit your changes or stash them to proceed."
+msgstr ""
+
+#. TRANSLATORS: %s will be "revert" or "cherry-pick"
+#: sequencer.c:233
+#, c-format
+msgid "%s: Unable to write new index file"
+msgstr ""
+
+#: sequencer.c:261
+msgid "Could not resolve HEAD commit\n"
+msgstr ""
+
+#: sequencer.c:282
+msgid "Unable to update cache tree\n"
+msgstr ""
+
+#: sequencer.c:324
+#, c-format
+msgid "Could not parse commit %s\n"
+msgstr ""
+
+#: sequencer.c:329
+#, c-format
+msgid "Could not parse parent commit %s\n"
+msgstr ""
+
+#: sequencer.c:395
+msgid "Your index file is unmerged."
+msgstr ""
+
+#: sequencer.c:398
+msgid "You do not have a valid HEAD"
+msgstr ""
+
+#: sequencer.c:413
+#, c-format
+msgid "Commit %s is a merge but no -m option was given."
+msgstr ""
+
+#: sequencer.c:421
+#, c-format
+msgid "Commit %s does not have parent %d"
+msgstr ""
+
+#: sequencer.c:425
+#, c-format
+msgid "Mainline was specified but commit %s is not a merge."
+msgstr ""
+
+#. TRANSLATORS: The first %s will be "revert" or
+#. "cherry-pick", the second %s a SHA1
+#: sequencer.c:436
+#, c-format
+msgid "%s: cannot parse parent commit %s"
+msgstr ""
+
+#: sequencer.c:440
+#, c-format
+msgid "Cannot get commit message for %s"
+msgstr ""
+
+#: sequencer.c:524
+#, c-format
+msgid "could not revert %s... %s"
+msgstr ""
+
+#: sequencer.c:525
+#, c-format
+msgid "could not apply %s... %s"
+msgstr ""
+
+#: sequencer.c:553
+msgid "empty commit set passed"
+msgstr ""
+
+#: sequencer.c:561
+#, c-format
+msgid "git %s: failed to read the index"
+msgstr ""
+
+#: sequencer.c:566
+#, c-format
+msgid "git %s: failed to refresh the index"
+msgstr ""
+
+#: sequencer.c:624
+#, c-format
+msgid "Cannot %s during a %s"
+msgstr ""
+
+#: sequencer.c:646
+#, c-format
+msgid "Could not parse line %d."
+msgstr ""
+
+#: sequencer.c:651
+msgid "No commits parsed."
+msgstr ""
+
+#: sequencer.c:664
+#, c-format
+msgid "Could not open %s"
+msgstr ""
+
+#: sequencer.c:668
+#, c-format
+msgid "Could not read %s."
+msgstr ""
+
+#: sequencer.c:675
+#, c-format
+msgid "Unusable instruction sheet: %s"
+msgstr ""
+
+#: sequencer.c:703
+#, c-format
+msgid "Invalid key: %s"
+msgstr ""
+
+#: sequencer.c:706
+#, c-format
+msgid "Invalid value for %s: %s"
+msgstr ""
+
+#: sequencer.c:718
+#, c-format
+msgid "Malformed options sheet: %s"
+msgstr ""
+
+#: sequencer.c:739
+msgid "a cherry-pick or revert is already in progress"
+msgstr ""
+
+#: sequencer.c:740
+msgid "try \"git cherry-pick (--continue | --quit | --abort)\""
+msgstr ""
+
+#: sequencer.c:744
+#, c-format
+msgid "Could not create sequencer directory %s"
+msgstr ""
+
+#: sequencer.c:760 sequencer.c:845
+#, c-format
+msgid "Error wrapping up %s."
+msgstr ""
+
+#: sequencer.c:779 sequencer.c:913
+msgid "no cherry-pick or revert in progress"
+msgstr ""
+
+#: sequencer.c:781
+msgid "cannot resolve HEAD"
+msgstr ""
+
+#: sequencer.c:783
+msgid "cannot abort from a branch yet to be born"
+msgstr ""
+
+#: sequencer.c:805 builtin/apply.c:3697
+#, c-format
+msgid "cannot open %s: %s"
+msgstr ""
+
+#: sequencer.c:808
+#, c-format
+msgid "cannot read %s: %s"
+msgstr ""
+
+#: sequencer.c:809
+msgid "unexpected end of file"
+msgstr ""
+
+#: sequencer.c:815
+#, c-format
+msgid "stored pre-cherry-pick HEAD file '%s' is corrupt"
+msgstr ""
+
+#: sequencer.c:838
+#, c-format
+msgid "Could not format %s."
+msgstr ""
+
+#: sequencer.c:1000
+msgid "Can't revert as initial commit"
+msgstr ""
+
+#: sequencer.c:1001
+msgid "Can't cherry-pick into empty head"
+msgstr ""
+
+#: sha1_name.c:864
+msgid "HEAD does not point to a branch"
+msgstr ""
+
+#: sha1_name.c:867
+#, c-format
+msgid "No such branch: '%s'"
+msgstr ""
+
+#: sha1_name.c:869
+#, c-format
+msgid "No upstream configured for branch '%s'"
+msgstr ""
+
+#: sha1_name.c:872
+#, c-format
+msgid "Upstream branch '%s' not stored as a remote-tracking branch"
+msgstr ""
+
+#: wrapper.c:413
+#, c-format
+msgid "unable to look up current user in the passwd file: %s"
+msgstr ""
+
+#: wrapper.c:414
+msgid "no such user"
+msgstr ""
+
+#: wt-status.c:141
+msgid "Unmerged paths:"
+msgstr ""
+
+#: wt-status.c:168 wt-status.c:195
+#, c-format
+msgid "  (use \"git reset %s <file>...\" to unstage)"
+msgstr ""
+
+#: wt-status.c:170 wt-status.c:197
+msgid "  (use \"git rm --cached <file>...\" to unstage)"
+msgstr ""
+
+#: wt-status.c:174
+msgid "  (use \"git add <file>...\" to mark resolution)"
+msgstr ""
+
+#: wt-status.c:176 wt-status.c:180
+msgid "  (use \"git add/rm <file>...\" as appropriate to mark resolution)"
+msgstr ""
+
+#: wt-status.c:178
+msgid "  (use \"git rm <file>...\" to mark resolution)"
+msgstr ""
+
+#: wt-status.c:189
+msgid "Changes to be committed:"
+msgstr ""
+
+#: wt-status.c:207
+msgid "Changes not staged for commit:"
+msgstr ""
+
+#: wt-status.c:211
+msgid "  (use \"git add <file>...\" to update what will be committed)"
+msgstr ""
+
+#: wt-status.c:213
+msgid "  (use \"git add/rm <file>...\" to update what will be committed)"
+msgstr ""
+
+#: wt-status.c:214
+msgid ""
+"  (use \"git checkout -- <file>...\" to discard changes in working directory)"
+msgstr ""
+
+#: wt-status.c:216
+msgid "  (commit or discard the untracked or modified content in submodules)"
+msgstr ""
+
+#: wt-status.c:225
+#, c-format
+msgid "%s files:"
+msgstr ""
+
+#: wt-status.c:228
+#, c-format
+msgid "  (use \"git %s <file>...\" to include in what will be committed)"
+msgstr ""
+
+#: wt-status.c:245
+msgid "bug"
+msgstr ""
+
+#: wt-status.c:250
+msgid "both deleted:"
+msgstr ""
+
+#: wt-status.c:251
+msgid "added by us:"
+msgstr ""
+
+#: wt-status.c:252
+msgid "deleted by them:"
+msgstr ""
+
+#: wt-status.c:253
+msgid "added by them:"
+msgstr ""
+
+#: wt-status.c:254
+msgid "deleted by us:"
+msgstr ""
+
+#: wt-status.c:255
+msgid "both added:"
+msgstr ""
+
+#: wt-status.c:256
+msgid "both modified:"
+msgstr ""
+
+#: wt-status.c:286
+msgid "new commits, "
+msgstr ""
+
+#: wt-status.c:288
+msgid "modified content, "
+msgstr ""
+
+#: wt-status.c:290
+msgid "untracked content, "
+msgstr ""
+
+#: wt-status.c:304
+#, c-format
+msgid "new file:   %s"
+msgstr ""
+
+#: wt-status.c:307
+#, c-format
+msgid "copied:     %s -> %s"
+msgstr ""
+
+#: wt-status.c:310
+#, c-format
+msgid "deleted:    %s"
+msgstr ""
+
+#: wt-status.c:313
+#, c-format
+msgid "modified:   %s"
+msgstr ""
+
+#: wt-status.c:316
+#, c-format
+msgid "renamed:    %s -> %s"
+msgstr ""
+
+#: wt-status.c:319
+#, c-format
+msgid "typechange: %s"
+msgstr ""
+
+#: wt-status.c:322
+#, c-format
+msgid "unknown:    %s"
+msgstr ""
+
+#: wt-status.c:325
+#, c-format
+msgid "unmerged:   %s"
+msgstr ""
+
+#: wt-status.c:328
+#, c-format
+msgid "bug: unhandled diff status %c"
+msgstr ""
+
+#: wt-status.c:786
+msgid "You have unmerged paths."
+msgstr ""
+
+#: wt-status.c:789 wt-status.c:913
+msgid "  (fix conflicts and run \"git commit\")"
+msgstr ""
+
+#: wt-status.c:792
+msgid "All conflicts fixed but you are still merging."
+msgstr ""
+
+#: wt-status.c:795
+msgid "  (use \"git commit\" to conclude merge)"
+msgstr ""
+
+#: wt-status.c:805
+msgid "You are in the middle of an am session."
+msgstr ""
+
+#: wt-status.c:808
+msgid "The current patch is empty."
+msgstr ""
+
+#: wt-status.c:812
+msgid "  (fix conflicts and then run \"git am --resolved\")"
+msgstr ""
+
+#: wt-status.c:814
+msgid "  (use \"git am --skip\" to skip this patch)"
+msgstr ""
+
+#: wt-status.c:816
+msgid "  (use \"git am --abort\" to restore the original branch)"
+msgstr ""
+
+#: wt-status.c:874 wt-status.c:884
+msgid "You are currently rebasing."
+msgstr ""
+
+#: wt-status.c:877
+msgid "  (fix conflicts and then run \"git rebase --continue\")"
+msgstr ""
+
+#: wt-status.c:879
+msgid "  (use \"git rebase --skip\" to skip this patch)"
+msgstr ""
+
+#: wt-status.c:881
+msgid "  (use \"git rebase --abort\" to check out the original branch)"
+msgstr ""
+
+#: wt-status.c:887
+msgid "  (all conflicts fixed: run \"git rebase --continue\")"
+msgstr ""
+
+#: wt-status.c:889
+msgid "You are currently splitting a commit during a rebase."
+msgstr ""
+
+#: wt-status.c:892
+msgid "  (Once your working directory is clean, run \"git rebase --continue\")"
+msgstr ""
+
+#: wt-status.c:894
+msgid "You are currently editing a commit during a rebase."
+msgstr ""
+
+#: wt-status.c:897
+msgid "  (use \"git commit --amend\" to amend the current commit)"
+msgstr ""
+
+#: wt-status.c:899
+msgid ""
+"  (use \"git rebase --continue\" once you are satisfied with your changes)"
+msgstr ""
+
+#: wt-status.c:909
+msgid "You are currently cherry-picking."
+msgstr ""
+
+#: wt-status.c:916
+msgid "  (all conflicts fixed: run \"git commit\")"
+msgstr ""
+
+#: wt-status.c:925
+msgid "You are currently bisecting."
+msgstr ""
+
+#: wt-status.c:928
+msgid "  (use \"git bisect reset\" to get back to the original branch)"
+msgstr ""
+
+#: wt-status.c:979
+msgid "On branch "
+msgstr ""
+
+#: wt-status.c:986
+msgid "Not currently on any branch."
+msgstr ""
+
+#: wt-status.c:998
+msgid "Initial commit"
+msgstr ""
+
+#: wt-status.c:1012
+msgid "Untracked"
+msgstr ""
+
+#: wt-status.c:1014
+msgid "Ignored"
+msgstr ""
+
+#: wt-status.c:1016
+#, c-format
+msgid "Untracked files not listed%s"
+msgstr ""
+
+#: wt-status.c:1018
+msgid " (use -u option to show untracked files)"
+msgstr ""
+
+#: wt-status.c:1024
+msgid "No changes"
+msgstr ""
+
+#: wt-status.c:1028
+#, c-format
+msgid "no changes added to commit%s\n"
+msgstr ""
+
+#: wt-status.c:1030
+msgid " (use \"git add\" and/or \"git commit -a\")"
+msgstr ""
+
+#: wt-status.c:1032
+#, c-format
+msgid "nothing added to commit but untracked files present%s\n"
+msgstr ""
+
+#: wt-status.c:1034
+msgid " (use \"git add\" to track)"
+msgstr ""
+
+#: wt-status.c:1036 wt-status.c:1039 wt-status.c:1042
+#, c-format
+msgid "nothing to commit%s\n"
+msgstr ""
+
+#: wt-status.c:1037
+msgid " (create/copy files and use \"git add\" to track)"
+msgstr ""
+
+#: wt-status.c:1040
+msgid " (use -u to show untracked files)"
+msgstr ""
+
+#: wt-status.c:1043
+msgid " (working directory clean)"
+msgstr ""
+
+#: wt-status.c:1151
+msgid "HEAD (no branch)"
+msgstr ""
+
+#: wt-status.c:1157
+msgid "Initial commit on "
+msgstr ""
+
+#: wt-status.c:1172
+msgid "behind "
+msgstr ""
+
+#: wt-status.c:1175 wt-status.c:1178
+msgid "ahead "
+msgstr ""
+
+#: wt-status.c:1180
+msgid ", behind "
+msgstr ""
+
+#: builtin/add.c:62
+#, c-format
+msgid "unexpected diff status %c"
+msgstr ""
+
+#: builtin/add.c:67 builtin/commit.c:226
+msgid "updating files failed"
+msgstr ""
+
+#: builtin/add.c:77
+#, c-format
+msgid "remove '%s'\n"
+msgstr ""
+
+#: builtin/add.c:176
+#, c-format
+msgid "Path '%s' is in submodule '%.*s'"
+msgstr ""
+
+#: builtin/add.c:192
+msgid "Unstaged changes after refreshing the index:"
+msgstr ""
+
+#: builtin/add.c:195 builtin/add.c:459 builtin/rm.c:186
+#, c-format
+msgid "pathspec '%s' did not match any files"
+msgstr ""
+
+#: builtin/add.c:209
+#, c-format
+msgid "'%s' is beyond a symbolic link"
+msgstr ""
+
+#: builtin/add.c:276
+msgid "Could not read the index"
+msgstr ""
+
+#: builtin/add.c:286
+#, c-format
+msgid "Could not open '%s' for writing."
+msgstr ""
+
+#: builtin/add.c:290
+msgid "Could not write patch"
+msgstr ""
+
+#: builtin/add.c:295
+#, c-format
+msgid "Could not stat '%s'"
+msgstr ""
+
+#: builtin/add.c:297
+msgid "Empty patch. Aborted."
+msgstr ""
+
+#: builtin/add.c:303
+#, c-format
+msgid "Could not apply '%s'"
+msgstr ""
+
+#: builtin/add.c:312
+msgid "The following paths are ignored by one of your .gitignore files:\n"
+msgstr ""
+
+#: builtin/add.c:352
+#, c-format
+msgid "Use -f if you really want to add them.\n"
+msgstr ""
+
+#: builtin/add.c:353
+msgid "no files added"
+msgstr ""
+
+#: builtin/add.c:359
+msgid "adding files failed"
+msgstr ""
+
+#: builtin/add.c:391
+msgid "-A and -u are mutually incompatible"
+msgstr ""
+
+#: builtin/add.c:393
+msgid "Option --ignore-missing can only be used together with --dry-run"
+msgstr ""
+
+#: builtin/add.c:413
+#, c-format
+msgid "Nothing specified, nothing added.\n"
+msgstr ""
+
+#: builtin/add.c:414
+#, c-format
+msgid "Maybe you wanted to say 'git add .'?\n"
+msgstr ""
+
+#: builtin/add.c:420 builtin/clean.c:95 builtin/commit.c:286 builtin/mv.c:82
+#: builtin/rm.c:162
+msgid "index file corrupt"
+msgstr ""
+
+#: builtin/add.c:480 builtin/apply.c:4108 builtin/mv.c:229 builtin/rm.c:260
+msgid "Unable to write new index file"
+msgstr ""
+
+#: builtin/apply.c:53
+msgid "git apply [options] [<patch>...]"
+msgstr ""
+
+#: builtin/apply.c:106
+#, c-format
+msgid "unrecognized whitespace option '%s'"
+msgstr ""
+
+#: builtin/apply.c:121
+#, c-format
+msgid "unrecognized whitespace ignore option '%s'"
+msgstr ""
+
+#: builtin/apply.c:815
+#, c-format
+msgid "Cannot prepare timestamp regexp %s"
+msgstr ""
+
+#: builtin/apply.c:824
+#, c-format
+msgid "regexec returned %d for input: %s"
+msgstr ""
+
+#: builtin/apply.c:905
+#, c-format
+msgid "unable to find filename in patch at line %d"
+msgstr ""
+
+#: builtin/apply.c:937
+#, c-format
+msgid "git apply: bad git-diff - expected /dev/null, got %s on line %d"
+msgstr ""
+
+#: builtin/apply.c:941
+#, c-format
+msgid "git apply: bad git-diff - inconsistent new filename on line %d"
+msgstr ""
+
+#: builtin/apply.c:942
+#, c-format
+msgid "git apply: bad git-diff - inconsistent old filename on line %d"
+msgstr ""
+
+#: builtin/apply.c:949
+#, c-format
+msgid "git apply: bad git-diff - expected /dev/null on line %d"
+msgstr ""
+
+#: builtin/apply.c:1394
+#, c-format
+msgid "recount: unexpected line: %.*s"
+msgstr ""
+
+#: builtin/apply.c:1451
+#, c-format
+msgid "patch fragment without header at line %d: %.*s"
+msgstr ""
+
+#: builtin/apply.c:1468
+#, c-format
+msgid ""
+"git diff header lacks filename information when removing %d leading pathname "
+"component (line %d)"
+msgid_plural ""
+"git diff header lacks filename information when removing %d leading pathname "
+"components (line %d)"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/apply.c:1628
+msgid "new file depends on old contents"
+msgstr ""
+
+#: builtin/apply.c:1630
+msgid "deleted file still has contents"
+msgstr ""
+
+#: builtin/apply.c:1656
+#, c-format
+msgid "corrupt patch at line %d"
+msgstr ""
+
+#: builtin/apply.c:1692
+#, c-format
+msgid "new file %s depends on old contents"
+msgstr ""
+
+#: builtin/apply.c:1694
+#, c-format
+msgid "deleted file %s still has contents"
+msgstr ""
+
+#: builtin/apply.c:1697
+#, c-format
+msgid "** warning: file %s becomes empty but is not deleted"
+msgstr ""
+
+#: builtin/apply.c:1843
+#, c-format
+msgid "corrupt binary patch at line %d: %.*s"
+msgstr ""
+
+#. there has to be one hunk (forward hunk)
+#: builtin/apply.c:1872
+#, c-format
+msgid "unrecognized binary patch at line %d"
+msgstr ""
+
+#: builtin/apply.c:1958
+#, c-format
+msgid "patch with only garbage at line %d"
+msgstr ""
+
+#: builtin/apply.c:2048
+#, c-format
+msgid "unable to read symlink %s"
+msgstr ""
+
+#: builtin/apply.c:2052
+#, c-format
+msgid "unable to open or read %s"
+msgstr ""
+
+#: builtin/apply.c:2123
+msgid "oops"
+msgstr ""
+
+#: builtin/apply.c:2645
+#, c-format
+msgid "invalid start of line: '%c'"
+msgstr ""
+
+#: builtin/apply.c:2763
+#, c-format
+msgid "Hunk #%d succeeded at %d (offset %d line)."
+msgid_plural "Hunk #%d succeeded at %d (offset %d lines)."
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/apply.c:2775
+#, c-format
+msgid "Context reduced to (%ld/%ld) to apply fragment at %d"
+msgstr ""
+
+#: builtin/apply.c:2781
+#, c-format
+msgid ""
+"while searching for:\n"
+"%.*s"
+msgstr ""
+
+#: builtin/apply.c:2800
+#, c-format
+msgid "missing binary patch data for '%s'"
+msgstr ""
+
+#: builtin/apply.c:2903
+#, c-format
+msgid "binary patch does not apply to '%s'"
+msgstr ""
+
+#: builtin/apply.c:2909
+#, c-format
+msgid "binary patch to '%s' creates incorrect result (expecting %s, got %s)"
+msgstr ""
+
+#: builtin/apply.c:2930
+#, c-format
+msgid "patch failed: %s:%ld"
+msgstr ""
+
+#: builtin/apply.c:3045
+#, c-format
+msgid "patch %s has been renamed/deleted"
+msgstr ""
+
+#: builtin/apply.c:3052 builtin/apply.c:3069
+#, c-format
+msgid "read of %s failed"
+msgstr ""
+
+#: builtin/apply.c:3084
+msgid "removal patch leaves file contents"
+msgstr ""
+
+#: builtin/apply.c:3105
+#, c-format
+msgid "%s: already exists in working directory"
+msgstr ""
+
+#: builtin/apply.c:3143
+#, c-format
+msgid "%s: has been deleted/renamed"
+msgstr ""
+
+#: builtin/apply.c:3148 builtin/apply.c:3179
+#, c-format
+msgid "%s: %s"
+msgstr ""
+
+#: builtin/apply.c:3159
+#, c-format
+msgid "%s: does not exist in index"
+msgstr ""
+
+#: builtin/apply.c:3173
+#, c-format
+msgid "%s: does not match index"
+msgstr ""
+
+#: builtin/apply.c:3190
+#, c-format
+msgid "%s: wrong type"
+msgstr ""
+
+#: builtin/apply.c:3192
+#, c-format
+msgid "%s has type %o, expected %o"
+msgstr ""
+
+#: builtin/apply.c:3247
+#, c-format
+msgid "%s: already exists in index"
+msgstr ""
+
+#: builtin/apply.c:3267
+#, c-format
+msgid "new mode (%o) of %s does not match old mode (%o)"
+msgstr ""
+
+#: builtin/apply.c:3272
+#, c-format
+msgid "new mode (%o) of %s does not match old mode (%o) of %s"
+msgstr ""
+
+#: builtin/apply.c:3280
+#, c-format
+msgid "%s: patch does not apply"
+msgstr ""
+
+#: builtin/apply.c:3293
+#, c-format
+msgid "Checking patch %s..."
+msgstr ""
+
+#: builtin/apply.c:3348 builtin/checkout.c:212 builtin/reset.c:158
+#, c-format
+msgid "make_cache_entry failed for path '%s'"
+msgstr ""
+
+#: builtin/apply.c:3491
+#, c-format
+msgid "unable to remove %s from index"
+msgstr ""
+
+#: builtin/apply.c:3518
+#, c-format
+msgid "corrupt patch for subproject %s"
+msgstr ""
+
+#: builtin/apply.c:3522
+#, c-format
+msgid "unable to stat newly created file '%s'"
+msgstr ""
+
+#: builtin/apply.c:3527
+#, c-format
+msgid "unable to create backing store for newly created file %s"
+msgstr ""
+
+#: builtin/apply.c:3530
+#, c-format
+msgid "unable to add cache entry for %s"
+msgstr ""
+
+#: builtin/apply.c:3563
+#, c-format
+msgid "closing file '%s'"
+msgstr ""
+
+#: builtin/apply.c:3612
+#, c-format
+msgid "unable to write file '%s' mode %o"
+msgstr ""
+
+#: builtin/apply.c:3668
+#, c-format
+msgid "Applied patch %s cleanly."
+msgstr ""
+
+#: builtin/apply.c:3676
+msgid "internal error"
+msgstr ""
+
+#. Say this even without --verbose
+#: builtin/apply.c:3679
+#, c-format
+msgid "Applying patch %%s with %d reject..."
+msgid_plural "Applying patch %%s with %d rejects..."
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/apply.c:3689
+#, c-format
+msgid "truncating .rej filename to %.*s.rej"
+msgstr ""
+
+#: builtin/apply.c:3710
+#, c-format
+msgid "Hunk #%d applied cleanly."
+msgstr ""
+
+#: builtin/apply.c:3713
+#, c-format
+msgid "Rejected hunk #%d."
+msgstr ""
+
+#: builtin/apply.c:3844
+msgid "unrecognized input"
+msgstr ""
+
+#: builtin/apply.c:3855
+msgid "unable to read index file"
+msgstr ""
+
+#: builtin/apply.c:3970 builtin/apply.c:3973
+msgid "path"
+msgstr ""
+
+#: builtin/apply.c:3971
+msgid "don't apply changes matching the given path"
+msgstr ""
+
+#: builtin/apply.c:3974
+msgid "apply changes matching the given path"
+msgstr ""
+
+#: builtin/apply.c:3976
+msgid "num"
+msgstr ""
+
+#: builtin/apply.c:3977
+msgid "remove <num> leading slashes from traditional diff paths"
+msgstr ""
+
+#: builtin/apply.c:3980
+msgid "ignore additions made by the patch"
+msgstr ""
+
+#: builtin/apply.c:3982
+msgid "instead of applying the patch, output diffstat for the input"
+msgstr ""
+
+#: builtin/apply.c:3986
+msgid "shows number of added and deleted lines in decimal notation"
+msgstr ""
+
+#: builtin/apply.c:3988
+msgid "instead of applying the patch, output a summary for the input"
+msgstr ""
+
+#: builtin/apply.c:3990
+msgid "instead of applying the patch, see if the patch is applicable"
+msgstr ""
+
+#: builtin/apply.c:3992
+msgid "make sure the patch is applicable to the current index"
+msgstr ""
+
+#: builtin/apply.c:3994
+msgid "apply a patch without touching the working tree"
+msgstr ""
+
+#: builtin/apply.c:3996
+msgid "also apply the patch (use with --stat/--summary/--check)"
+msgstr ""
+
+#: builtin/apply.c:3998
+msgid "build a temporary index based on embedded index information"
+msgstr ""
+
+#: builtin/apply.c:4000
+msgid "paths are separated with NUL character"
+msgstr ""
+
+#: builtin/apply.c:4003
+msgid "ensure at least <n> lines of context match"
+msgstr ""
+
+#: builtin/apply.c:4004
+msgid "action"
+msgstr ""
+
+#: builtin/apply.c:4005
+msgid "detect new or modified lines that have whitespace errors"
+msgstr ""
+
+#: builtin/apply.c:4008 builtin/apply.c:4011
+msgid "ignore changes in whitespace when finding context"
+msgstr ""
+
+#: builtin/apply.c:4014
+msgid "apply the patch in reverse"
+msgstr ""
+
+#: builtin/apply.c:4016
+msgid "don't expect at least one line of context"
+msgstr ""
+
+#: builtin/apply.c:4018
+msgid "leave the rejected hunks in corresponding *.rej files"
+msgstr ""
+
+#: builtin/apply.c:4020
+msgid "allow overlapping hunks"
+msgstr ""
+
+#: builtin/apply.c:4021
+msgid "be verbose"
+msgstr ""
+
+#: builtin/apply.c:4023
+msgid "tolerate incorrectly detected missing new-line at the end of file"
+msgstr ""
+
+#: builtin/apply.c:4026
+msgid "do not trust the line counts in the hunk headers"
+msgstr ""
+
+#: builtin/apply.c:4028
+msgid "root"
+msgstr ""
+
+#: builtin/apply.c:4029
+msgid "prepend <root> to all filenames"
+msgstr ""
+
+#: builtin/apply.c:4050
+msgid "--index outside a repository"
+msgstr ""
+
+#: builtin/apply.c:4053
+msgid "--cached outside a repository"
+msgstr ""
+
+#: builtin/apply.c:4069
+#, c-format
+msgid "can't open patch '%s'"
+msgstr ""
+
+#: builtin/apply.c:4083
+#, c-format
+msgid "squelched %d whitespace error"
+msgid_plural "squelched %d whitespace errors"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/apply.c:4089 builtin/apply.c:4099
+#, c-format
+msgid "%d line adds whitespace errors."
+msgid_plural "%d lines add whitespace errors."
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/archive.c:17
+#, c-format
+msgid "could not create archive file '%s'"
+msgstr ""
+
+#: builtin/archive.c:20
+msgid "could not redirect output"
+msgstr ""
+
+#: builtin/archive.c:37
+msgid "git archive: Remote with no URL"
+msgstr ""
+
+#: builtin/archive.c:58
+msgid "git archive: expected ACK/NAK, got EOF"
+msgstr ""
+
+#: builtin/archive.c:63
+#, c-format
+msgid "git archive: NACK %s"
+msgstr ""
+
+#: builtin/archive.c:65
+#, c-format
+msgid "remote error: %s"
+msgstr ""
+
+#: builtin/archive.c:66
+msgid "git archive: protocol error"
+msgstr ""
+
+#: builtin/archive.c:71
+msgid "git archive: expected a flush"
+msgstr ""
+
+#: builtin/branch.c:144
+#, c-format
+msgid ""
+"deleting branch '%s' that has been merged to\n"
+"         '%s', but not yet merged to HEAD."
+msgstr ""
+
+#: builtin/branch.c:148
+#, c-format
+msgid ""
+"not deleting branch '%s' that is not yet merged to\n"
+"         '%s', even though it is merged to HEAD."
+msgstr ""
+
+#: builtin/branch.c:180
+msgid "cannot use -a with -d"
+msgstr ""
+
+#: builtin/branch.c:186
+msgid "Couldn't look up commit object for HEAD"
+msgstr ""
+
+#: builtin/branch.c:191
+#, c-format
+msgid "Cannot delete the branch '%s' which you are currently on."
+msgstr ""
+
+#: builtin/branch.c:202
+#, c-format
+msgid "remote branch '%s' not found."
+msgstr ""
+
+#: builtin/branch.c:203
+#, c-format
+msgid "branch '%s' not found."
+msgstr ""
+
+#: builtin/branch.c:210
+#, c-format
+msgid "Couldn't look up commit object for '%s'"
+msgstr ""
+
+#: builtin/branch.c:216
+#, c-format
+msgid ""
+"The branch '%s' is not fully merged.\n"
+"If you are sure you want to delete it, run 'git branch -D %s'."
+msgstr ""
+
+#: builtin/branch.c:225
+#, c-format
+msgid "Error deleting remote branch '%s'"
+msgstr ""
+
+#: builtin/branch.c:226
+#, c-format
+msgid "Error deleting branch '%s'"
+msgstr ""
+
+#: builtin/branch.c:233
+#, c-format
+msgid "Deleted remote branch %s (was %s).\n"
+msgstr ""
+
+#: builtin/branch.c:234
+#, c-format
+msgid "Deleted branch %s (was %s).\n"
+msgstr ""
+
+#: builtin/branch.c:239
+msgid "Update of config-file failed"
+msgstr ""
+
+#: builtin/branch.c:337
+#, c-format
+msgid "branch '%s' does not point at a commit"
+msgstr ""
+
+#: builtin/branch.c:409
+#, c-format
+msgid "[%s: behind %d]"
+msgstr ""
+
+#: builtin/branch.c:411
+#, c-format
+msgid "[behind %d]"
+msgstr ""
+
+#: builtin/branch.c:415
+#, c-format
+msgid "[%s: ahead %d]"
+msgstr ""
+
+#: builtin/branch.c:417
+#, c-format
+msgid "[ahead %d]"
+msgstr ""
+
+#: builtin/branch.c:420
+#, c-format
+msgid "[%s: ahead %d, behind %d]"
+msgstr ""
+
+#: builtin/branch.c:423
+#, c-format
+msgid "[ahead %d, behind %d]"
+msgstr ""
+
+#: builtin/branch.c:535
+msgid "(no branch)"
+msgstr ""
+
+#: builtin/branch.c:600
+msgid "some refs could not be read"
+msgstr ""
+
+#: builtin/branch.c:613
+msgid "cannot rename the current branch while not on any."
+msgstr ""
+
+#: builtin/branch.c:623
+#, c-format
+msgid "Invalid branch name: '%s'"
+msgstr ""
+
+#: builtin/branch.c:638
+msgid "Branch rename failed"
+msgstr ""
+
+#: builtin/branch.c:642
+#, c-format
+msgid "Renamed a misnamed branch '%s' away"
+msgstr ""
+
+#: builtin/branch.c:646
+#, c-format
+msgid "Branch renamed to %s, but HEAD is not updated!"
+msgstr ""
+
+#: builtin/branch.c:653
+msgid "Branch is renamed, but update of config-file failed"
+msgstr ""
+
+#: builtin/branch.c:668
+#, c-format
+msgid "malformed object name %s"
+msgstr ""
+
+#: builtin/branch.c:692
+#, c-format
+msgid "could not write branch description template: %s"
+msgstr ""
+
+#: builtin/branch.c:783
+msgid "Failed to resolve HEAD as a valid ref."
+msgstr ""
+
+#: builtin/branch.c:788 builtin/clone.c:558
+msgid "HEAD not found below refs/heads!"
+msgstr ""
+
+#: builtin/branch.c:808
+msgid "--column and --verbose are incompatible"
+msgstr ""
+
+#: builtin/branch.c:857
+msgid "-a and -r options to 'git branch' do not make sense with a branch name"
+msgstr ""
+
+#: builtin/bundle.c:47
+#, c-format
+msgid "%s is okay\n"
+msgstr ""
+
+#: builtin/bundle.c:56
+msgid "Need a repository to create a bundle."
+msgstr ""
+
+#: builtin/bundle.c:60
+msgid "Need a repository to unbundle."
+msgstr ""
+
+#: builtin/checkout.c:113 builtin/checkout.c:146
+#, c-format
+msgid "path '%s' does not have our version"
+msgstr ""
+
+#: builtin/checkout.c:115 builtin/checkout.c:148
+#, c-format
+msgid "path '%s' does not have their version"
+msgstr ""
+
+#: builtin/checkout.c:131
+#, c-format
+msgid "path '%s' does not have all necessary versions"
+msgstr ""
+
+#: builtin/checkout.c:175
+#, c-format
+msgid "path '%s' does not have necessary versions"
+msgstr ""
+
+#: builtin/checkout.c:192
+#, c-format
+msgid "path '%s': cannot merge"
+msgstr ""
+
+#: builtin/checkout.c:209
+#, c-format
+msgid "Unable to add merge result for '%s'"
+msgstr ""
+
+#: builtin/checkout.c:234 builtin/checkout.c:392
+msgid "corrupt index file"
+msgstr ""
+
+#: builtin/checkout.c:264 builtin/checkout.c:271
+#, c-format
+msgid "path '%s' is unmerged"
+msgstr ""
+
+#: builtin/checkout.c:302 builtin/checkout.c:498 builtin/clone.c:583
+#: builtin/merge.c:812
+msgid "unable to write new index file"
+msgstr ""
+
+#: builtin/checkout.c:319 builtin/diff.c:302 builtin/merge.c:408
+msgid "diff_setup_done failed"
+msgstr ""
+
+#: builtin/checkout.c:414
+msgid "you need to resolve your current index first"
+msgstr ""
+
+#: builtin/checkout.c:533
+#, c-format
+msgid "Can not do reflog for '%s'\n"
+msgstr ""
+
+#: builtin/checkout.c:566
+msgid "HEAD is now at"
+msgstr ""
+
+#: builtin/checkout.c:573
+#, c-format
+msgid "Reset branch '%s'\n"
+msgstr ""
+
+#: builtin/checkout.c:576
+#, c-format
+msgid "Already on '%s'\n"
+msgstr ""
+
+#: builtin/checkout.c:580
+#, c-format
+msgid "Switched to and reset branch '%s'\n"
+msgstr ""
+
+#: builtin/checkout.c:582
+#, c-format
+msgid "Switched to a new branch '%s'\n"
+msgstr ""
+
+#: builtin/checkout.c:584
+#, c-format
+msgid "Switched to branch '%s'\n"
+msgstr ""
+
+#: builtin/checkout.c:640
+#, c-format
+msgid " ... and %d more.\n"
+msgstr ""
+
+#. The singular version
+#: builtin/checkout.c:646
+#, c-format
+msgid ""
+"Warning: you are leaving %d commit behind, not connected to\n"
+"any of your branches:\n"
+"\n"
+"%s\n"
+msgid_plural ""
+"Warning: you are leaving %d commits behind, not connected to\n"
+"any of your branches:\n"
+"\n"
+"%s\n"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/checkout.c:664
+#, c-format
+msgid ""
+"If you want to keep them by creating a new branch, this may be a good time\n"
+"to do so with:\n"
+"\n"
+" git branch new_branch_name %s\n"
+"\n"
+msgstr ""
+
+#: builtin/checkout.c:694
+msgid "internal error in revision walk"
+msgstr ""
+
+#: builtin/checkout.c:698
+msgid "Previous HEAD position was"
+msgstr ""
+
+#: builtin/checkout.c:724
+msgid "You are on a branch yet to be born"
+msgstr ""
+
+#. case (1)
+#: builtin/checkout.c:855
+#, c-format
+msgid "invalid reference: %s"
+msgstr ""
+
+#. case (1): want a tree
+#: builtin/checkout.c:894
+#, c-format
+msgid "reference is not a tree: %s"
+msgstr ""
+
+#: builtin/checkout.c:974
+msgid "-B cannot be used with -b"
+msgstr ""
+
+#: builtin/checkout.c:983
+msgid "--patch is incompatible with all other options"
+msgstr ""
+
+#: builtin/checkout.c:986
+msgid "--detach cannot be used with -b/-B/--orphan"
+msgstr ""
+
+#: builtin/checkout.c:988
+msgid "--detach cannot be used with -t"
+msgstr ""
+
+#: builtin/checkout.c:994
+msgid "--track needs a branch name"
+msgstr ""
+
+#: builtin/checkout.c:1001
+msgid "Missing branch name; try -b"
+msgstr ""
+
+#: builtin/checkout.c:1007
+msgid "--orphan and -b|-B are mutually exclusive"
+msgstr ""
+
+#: builtin/checkout.c:1009
+msgid "--orphan cannot be used with -t"
+msgstr ""
+
+#: builtin/checkout.c:1019
+msgid "git checkout: -f and -m are incompatible"
+msgstr ""
+
+#: builtin/checkout.c:1053
+msgid "invalid path specification"
+msgstr ""
+
+#: builtin/checkout.c:1061
+#, c-format
+msgid ""
+"git checkout: updating paths is incompatible with switching branches.\n"
+"Did you intend to checkout '%s' which can not be resolved as commit?"
+msgstr ""
+
+#: builtin/checkout.c:1063
+msgid "git checkout: updating paths is incompatible with switching branches."
+msgstr ""
+
+#: builtin/checkout.c:1068
+msgid "git checkout: --detach does not take a path argument"
+msgstr ""
+
+#: builtin/checkout.c:1071
+msgid ""
+"git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
+"checking out of the index."
+msgstr ""
+
+#: builtin/checkout.c:1090
+msgid "Cannot switch branch to a non-commit."
+msgstr ""
+
+#: builtin/checkout.c:1093
+msgid "--ours/--theirs is incompatible with switching branches."
+msgstr ""
+
+#: builtin/clean.c:78
+msgid "-x and -X cannot be used together"
+msgstr ""
+
+#: builtin/clean.c:82
+msgid ""
+"clean.requireForce set to true and neither -n nor -f given; refusing to clean"
+msgstr ""
+
+#: builtin/clean.c:85
+msgid ""
+"clean.requireForce defaults to true and neither -n nor -f given; refusing to "
+"clean"
+msgstr ""
+
+#: builtin/clean.c:155 builtin/clean.c:176
+#, c-format
+msgid "Would remove %s\n"
+msgstr ""
+
+#: builtin/clean.c:159 builtin/clean.c:179
+#, c-format
+msgid "Removing %s\n"
+msgstr ""
+
+#: builtin/clean.c:162 builtin/clean.c:182
+#, c-format
+msgid "failed to remove %s"
+msgstr ""
+
+#: builtin/clean.c:166
+#, c-format
+msgid "Would not remove %s\n"
+msgstr ""
+
+#: builtin/clean.c:168
+#, c-format
+msgid "Not removing %s\n"
+msgstr ""
+
+#: builtin/clone.c:243
+#, c-format
+msgid "reference repository '%s' is not a local directory."
+msgstr ""
+
+#: builtin/clone.c:302
+#, c-format
+msgid "failed to open '%s'"
+msgstr ""
+
+#: builtin/clone.c:306
+#, c-format
+msgid "failed to create directory '%s'"
+msgstr ""
+
+#: builtin/clone.c:308 builtin/diff.c:75
+#, c-format
+msgid "failed to stat '%s'"
+msgstr ""
+
+#: builtin/clone.c:310
+#, c-format
+msgid "%s exists and is not a directory"
+msgstr ""
+
+#: builtin/clone.c:324
+#, c-format
+msgid "failed to stat %s\n"
+msgstr ""
+
+#: builtin/clone.c:341
+#, c-format
+msgid "failed to unlink '%s'"
+msgstr ""
+
+#: builtin/clone.c:346
+#, c-format
+msgid "failed to create link '%s'"
+msgstr ""
+
+#: builtin/clone.c:350
+#, c-format
+msgid "failed to copy file to '%s'"
+msgstr ""
+
+#: builtin/clone.c:373
+#, c-format
+msgid "done.\n"
+msgstr ""
+
+#: builtin/clone.c:440
+#, c-format
+msgid "Could not find remote branch %s to clone."
+msgstr ""
+
+#: builtin/clone.c:549
+msgid "remote HEAD refers to nonexistent ref, unable to checkout.\n"
+msgstr ""
+
+#: builtin/clone.c:639
+msgid "Too many arguments."
+msgstr ""
+
+#: builtin/clone.c:643
+msgid "You must specify a repository to clone."
+msgstr ""
+
+#: builtin/clone.c:654
+#, c-format
+msgid "--bare and --origin %s options are incompatible."
+msgstr ""
+
+#: builtin/clone.c:668
+#, c-format
+msgid "repository '%s' does not exist"
+msgstr ""
+
+#: builtin/clone.c:673
+msgid "--depth is ignored in local clones; use file:// instead."
+msgstr ""
+
+#: builtin/clone.c:683
+#, c-format
+msgid "destination path '%s' already exists and is not an empty directory."
+msgstr ""
+
+#: builtin/clone.c:693
+#, c-format
+msgid "working tree '%s' already exists."
+msgstr ""
+
+#: builtin/clone.c:706 builtin/clone.c:720
+#, c-format
+msgid "could not create leading directories of '%s'"
+msgstr ""
+
+#: builtin/clone.c:709
+#, c-format
+msgid "could not create work tree dir '%s'."
+msgstr ""
+
+#: builtin/clone.c:728
+#, c-format
+msgid "Cloning into bare repository '%s'...\n"
+msgstr ""
+
+#: builtin/clone.c:730
+#, c-format
+msgid "Cloning into '%s'...\n"
+msgstr ""
+
+#: builtin/clone.c:786
+#, c-format
+msgid "Don't know how to clone %s"
+msgstr ""
+
+#: builtin/clone.c:835
+#, c-format
+msgid "Remote branch %s not found in upstream %s"
+msgstr ""
+
+#: builtin/clone.c:842
+msgid "You appear to have cloned an empty repository."
+msgstr ""
+
+#: builtin/column.c:51
+msgid "--command must be the first argument"
+msgstr ""
+
+#: builtin/commit.c:43
+msgid ""
+"Your name and email address were configured automatically based\n"
+"on your username and hostname. Please check that they are accurate.\n"
+"You can suppress this message by setting them explicitly:\n"
+"\n"
+"    git config --global user.name \"Your Name\"\n"
+"    git config --global user.email you@example.com\n"
+"\n"
+"After doing this, you may fix the identity used for this commit with:\n"
+"\n"
+"    git commit --amend --reset-author\n"
+msgstr ""
+
+#: builtin/commit.c:55
+msgid ""
+"You asked to amend the most recent commit, but doing so would make\n"
+"it empty. You can repeat your command with --allow-empty, or you can\n"
+"remove the commit entirely with \"git reset HEAD^\".\n"
+msgstr ""
+
+#: builtin/commit.c:60
+msgid ""
+"The previous cherry-pick is now empty, possibly due to conflict resolution.\n"
+"If you wish to commit it anyway, use:\n"
+"\n"
+"    git commit --allow-empty\n"
+"\n"
+"Otherwise, please use 'git reset'\n"
+msgstr ""
+
+#: builtin/commit.c:253
+msgid "failed to unpack HEAD tree object"
+msgstr ""
+
+#: builtin/commit.c:295
+msgid "unable to create temporary index"
+msgstr ""
+
+#: builtin/commit.c:301
+msgid "interactive add failed"
+msgstr ""
+
+#: builtin/commit.c:334 builtin/commit.c:355 builtin/commit.c:405
+msgid "unable to write new_index file"
+msgstr ""
+
+#: builtin/commit.c:386
+msgid "cannot do a partial commit during a merge."
+msgstr ""
+
+#: builtin/commit.c:388
+msgid "cannot do a partial commit during a cherry-pick."
+msgstr ""
+
+#: builtin/commit.c:398
+msgid "cannot read the index"
+msgstr ""
+
+#: builtin/commit.c:418
+msgid "unable to write temporary index file"
+msgstr ""
+
+#: builtin/commit.c:493 builtin/commit.c:499
+#, c-format
+msgid "invalid commit: %s"
+msgstr ""
+
+#: builtin/commit.c:522
+msgid "malformed --author parameter"
+msgstr ""
+
+#: builtin/commit.c:582
+#, c-format
+msgid "Malformed ident string: '%s'"
+msgstr ""
+
+#: builtin/commit.c:620 builtin/commit.c:653 builtin/commit.c:967
+#, c-format
+msgid "could not lookup commit %s"
+msgstr ""
+
+#: builtin/commit.c:632 builtin/shortlog.c:296
+#, c-format
+msgid "(reading log message from standard input)\n"
+msgstr ""
+
+#: builtin/commit.c:634
+msgid "could not read log from standard input"
+msgstr ""
+
+#: builtin/commit.c:638
+#, c-format
+msgid "could not read log file '%s'"
+msgstr ""
+
+#: builtin/commit.c:644
+msgid "commit has empty message"
+msgstr ""
+
+#: builtin/commit.c:660
+msgid "could not read MERGE_MSG"
+msgstr ""
+
+#: builtin/commit.c:664
+msgid "could not read SQUASH_MSG"
+msgstr ""
+
+#: builtin/commit.c:668
+#, c-format
+msgid "could not read '%s'"
+msgstr ""
+
+#: builtin/commit.c:720
+msgid "could not write commit template"
+msgstr ""
+
+#: builtin/commit.c:731
+#, c-format
+msgid ""
+"\n"
+"It looks like you may be committing a merge.\n"
+"If this is not correct, please remove the file\n"
+"\t%s\n"
+"and try again.\n"
+msgstr ""
+
+#: builtin/commit.c:736
+#, c-format
+msgid ""
+"\n"
+"It looks like you may be committing a cherry-pick.\n"
+"If this is not correct, please remove the file\n"
+"\t%s\n"
+"and try again.\n"
+msgstr ""
+
+#: builtin/commit.c:748
+msgid ""
+"Please enter the commit message for your changes. Lines starting\n"
+"with '#' will be ignored, and an empty message aborts the commit.\n"
+msgstr ""
+
+#: builtin/commit.c:753
+msgid ""
+"Please enter the commit message for your changes. Lines starting\n"
+"with '#' will be kept; you may remove them yourself if you want to.\n"
+"An empty message aborts the commit.\n"
+msgstr ""
+
+#: builtin/commit.c:766
+#, c-format
+msgid "%sAuthor:    %s"
+msgstr ""
+
+#: builtin/commit.c:773
+#, c-format
+msgid "%sCommitter: %s"
+msgstr ""
+
+#: builtin/commit.c:793
+msgid "Cannot read index"
+msgstr ""
+
+#: builtin/commit.c:830
+msgid "Error building trees"
+msgstr ""
+
+#: builtin/commit.c:845 builtin/tag.c:361
+#, c-format
+msgid "Please supply the message using either -m or -F option.\n"
+msgstr ""
+
+#: builtin/commit.c:942
+#, c-format
+msgid "No existing author found with '%s'"
+msgstr ""
+
+#: builtin/commit.c:957 builtin/commit.c:1157
+#, c-format
+msgid "Invalid untracked files mode '%s'"
+msgstr ""
+
+#: builtin/commit.c:997
+msgid "Using both --reset-author and --author does not make sense"
+msgstr ""
+
+#: builtin/commit.c:1008
+msgid "You have nothing to amend."
+msgstr ""
+
+#: builtin/commit.c:1011
+msgid "You are in the middle of a merge -- cannot amend."
+msgstr ""
+
+#: builtin/commit.c:1013
+msgid "You are in the middle of a cherry-pick -- cannot amend."
+msgstr ""
+
+#: builtin/commit.c:1016
+msgid "Options --squash and --fixup cannot be used together"
+msgstr ""
+
+#: builtin/commit.c:1026
+msgid "Only one of -c/-C/-F/--fixup can be used."
+msgstr ""
+
+#: builtin/commit.c:1028
+msgid "Option -m cannot be combined with -c/-C/-F/--fixup."
+msgstr ""
+
+#: builtin/commit.c:1036
+msgid "--reset-author can be used only with -C, -c or --amend."
+msgstr ""
+
+#: builtin/commit.c:1053
+msgid "Only one of --include/--only/--all/--interactive/--patch can be used."
+msgstr ""
+
+#: builtin/commit.c:1055
+msgid "No paths with --include/--only does not make sense."
+msgstr ""
+
+#: builtin/commit.c:1057
+msgid "Clever... amending the last one with dirty index."
+msgstr ""
+
+#: builtin/commit.c:1059
+msgid "Explicit paths specified without -i nor -o; assuming --only paths..."
+msgstr ""
+
+#: builtin/commit.c:1069 builtin/tag.c:577
+#, c-format
+msgid "Invalid cleanup mode %s"
+msgstr ""
+
+#: builtin/commit.c:1074
+msgid "Paths with -a does not make sense."
+msgstr ""
+
+#: builtin/commit.c:1257
+msgid "couldn't look up newly created commit"
+msgstr ""
+
+#: builtin/commit.c:1259
+msgid "could not parse newly created commit"
+msgstr ""
+
+#: builtin/commit.c:1300
+msgid "detached HEAD"
+msgstr ""
+
+#: builtin/commit.c:1302
+msgid " (root-commit)"
+msgstr ""
+
+#: builtin/commit.c:1446
+msgid "could not parse HEAD commit"
+msgstr ""
+
+#: builtin/commit.c:1484 builtin/merge.c:509
+#, c-format
+msgid "could not open '%s' for reading"
+msgstr ""
+
+#: builtin/commit.c:1491
+#, c-format
+msgid "Corrupt MERGE_HEAD file (%s)"
+msgstr ""
+
+#: builtin/commit.c:1498
+msgid "could not read MERGE_MODE"
+msgstr ""
+
+#: builtin/commit.c:1517
+#, c-format
+msgid "could not read commit message: %s"
+msgstr ""
+
+#: builtin/commit.c:1531
+#, c-format
+msgid "Aborting commit; you did not edit the message.\n"
+msgstr ""
+
+#: builtin/commit.c:1536
+#, c-format
+msgid "Aborting commit due to empty commit message.\n"
+msgstr ""
+
+#: builtin/commit.c:1551 builtin/merge.c:936 builtin/merge.c:961
+msgid "failed to write commit object"
+msgstr ""
+
+#: builtin/commit.c:1572
+msgid "cannot lock HEAD ref"
+msgstr ""
+
+#: builtin/commit.c:1576
+msgid "cannot update HEAD ref"
+msgstr ""
+
+#: builtin/commit.c:1587
+msgid ""
+"Repository has been updated, but unable to write\n"
+"new_index file. Check that disk is not full or quota is\n"
+"not exceeded, and then \"git reset HEAD\" to recover."
+msgstr ""
+
+#: builtin/describe.c:234
+#, c-format
+msgid "annotated tag %s not available"
+msgstr ""
+
+#: builtin/describe.c:238
+#, c-format
+msgid "annotated tag %s has no embedded name"
+msgstr ""
+
+#: builtin/describe.c:240
+#, c-format
+msgid "tag '%s' is really '%s' here"
+msgstr ""
+
+#: builtin/describe.c:267
+#, c-format
+msgid "Not a valid object name %s"
+msgstr ""
+
+#: builtin/describe.c:270
+#, c-format
+msgid "%s is not a valid '%s' object"
+msgstr ""
+
+#: builtin/describe.c:287
+#, c-format
+msgid "no tag exactly matches '%s'"
+msgstr ""
+
+#: builtin/describe.c:289
+#, c-format
+msgid "searching to describe %s\n"
+msgstr ""
+
+#: builtin/describe.c:329
+#, c-format
+msgid "finished search at %s\n"
+msgstr ""
+
+#: builtin/describe.c:353
+#, c-format
+msgid ""
+"No annotated tags can describe '%s'.\n"
+"However, there were unannotated tags: try --tags."
+msgstr ""
+
+#: builtin/describe.c:357
+#, c-format
+msgid ""
+"No tags can describe '%s'.\n"
+"Try --always, or create some tags."
+msgstr ""
+
+#: builtin/describe.c:378
+#, c-format
+msgid "traversed %lu commits\n"
+msgstr ""
+
+#: builtin/describe.c:381
+#, c-format
+msgid ""
+"more than %i tags found; listed %i most recent\n"
+"gave up search at %s\n"
+msgstr ""
+
+#: builtin/describe.c:436
+msgid "--long is incompatible with --abbrev=0"
+msgstr ""
+
+#: builtin/describe.c:462
+msgid "No names found, cannot describe anything."
+msgstr ""
+
+#: builtin/describe.c:482
+msgid "--dirty is incompatible with committishes"
+msgstr ""
+
+#: builtin/diff.c:77
+#, c-format
+msgid "'%s': not a regular file or symlink"
+msgstr ""
+
+#: builtin/diff.c:220
+#, c-format
+msgid "invalid option: %s"
+msgstr ""
+
+#: builtin/diff.c:297
+msgid "Not a git repository"
+msgstr ""
+
+#: builtin/diff.c:341
+#, c-format
+msgid "invalid object '%s' given."
+msgstr ""
+
+#: builtin/diff.c:346
+#, c-format
+msgid "more than %d trees given: '%s'"
+msgstr ""
+
+#: builtin/diff.c:356
+#, c-format
+msgid "more than two blobs given: '%s'"
+msgstr ""
+
+#: builtin/diff.c:364
+#, c-format
+msgid "unhandled object '%s' given."
+msgstr ""
+
+#: builtin/fetch.c:200
+msgid "Couldn't find remote ref HEAD"
+msgstr ""
+
+#: builtin/fetch.c:253
+#, c-format
+msgid "object %s not found"
+msgstr ""
+
+#: builtin/fetch.c:259
+msgid "[up to date]"
+msgstr ""
+
+#: builtin/fetch.c:273
+#, c-format
+msgid "! %-*s %-*s -> %s  (can't fetch in current branch)"
+msgstr ""
+
+#: builtin/fetch.c:274 builtin/fetch.c:360
+msgid "[rejected]"
+msgstr ""
+
+#: builtin/fetch.c:285
+msgid "[tag update]"
+msgstr ""
+
+#: builtin/fetch.c:287 builtin/fetch.c:322 builtin/fetch.c:340
+msgid "  (unable to update local ref)"
+msgstr ""
+
+#: builtin/fetch.c:305
+msgid "[new tag]"
+msgstr ""
+
+#: builtin/fetch.c:308
+msgid "[new branch]"
+msgstr ""
+
+#: builtin/fetch.c:311
+msgid "[new ref]"
+msgstr ""
+
+#: builtin/fetch.c:356
+msgid "unable to update local ref"
+msgstr ""
+
+#: builtin/fetch.c:356
+msgid "forced update"
+msgstr ""
+
+#: builtin/fetch.c:362
+msgid "(non-fast-forward)"
+msgstr ""
+
+#: builtin/fetch.c:393 builtin/fetch.c:685
+#, c-format
+msgid "cannot open %s: %s\n"
+msgstr ""
+
+#: builtin/fetch.c:402
+#, c-format
+msgid "%s did not send all necessary objects\n"
+msgstr ""
+
+#: builtin/fetch.c:488
+#, c-format
+msgid "From %.*s\n"
+msgstr ""
+
+#: builtin/fetch.c:499
+#, c-format
+msgid ""
+"some local refs could not be updated; try running\n"
+" 'git remote prune %s' to remove any old, conflicting branches"
+msgstr ""
+
+#: builtin/fetch.c:549
+#, c-format
+msgid "   (%s will become dangling)"
+msgstr ""
+
+#: builtin/fetch.c:550
+#, c-format
+msgid "   (%s has become dangling)"
+msgstr ""
+
+#: builtin/fetch.c:557
+msgid "[deleted]"
+msgstr ""
+
+#: builtin/fetch.c:558 builtin/remote.c:1055
+msgid "(none)"
+msgstr ""
+
+#: builtin/fetch.c:675
+#, c-format
+msgid "Refusing to fetch into current branch %s of non-bare repository"
+msgstr ""
+
+#: builtin/fetch.c:709
+#, c-format
+msgid "Don't know how to fetch from %s"
+msgstr ""
+
+#: builtin/fetch.c:786
+#, c-format
+msgid "Option \"%s\" value \"%s\" is not valid for %s"
+msgstr ""
+
+#: builtin/fetch.c:789
+#, c-format
+msgid "Option \"%s\" is ignored for %s\n"
+msgstr ""
+
+#: builtin/fetch.c:888
+#, c-format
+msgid "Fetching %s\n"
+msgstr ""
+
+#: builtin/fetch.c:890 builtin/remote.c:100
+#, c-format
+msgid "Could not fetch %s"
+msgstr ""
+
+#: builtin/fetch.c:907
+msgid ""
+"No remote repository specified.  Please, specify either a URL or a\n"
+"remote name from which new revisions should be fetched."
+msgstr ""
+
+#: builtin/fetch.c:927
+msgid "You need to specify a tag name."
+msgstr ""
+
+#: builtin/fetch.c:979
+msgid "fetch --all does not take a repository argument"
+msgstr ""
+
+#: builtin/fetch.c:981
+msgid "fetch --all does not make sense with refspecs"
+msgstr ""
+
+#: builtin/fetch.c:992
+#, c-format
+msgid "No such remote or remote group: %s"
+msgstr ""
+
+#: builtin/fetch.c:1000
+msgid "Fetching a group and specifying refspecs does not make sense"
+msgstr ""
+
+#: builtin/gc.c:63
+#, c-format
+msgid "Invalid %s: '%s'"
+msgstr ""
+
+#: builtin/gc.c:90
+#, c-format
+msgid "insanely long object directory %.*s"
+msgstr ""
+
+#: builtin/gc.c:221
+#, c-format
+msgid "Auto packing the repository for optimum performance.\n"
+msgstr ""
+
+#: builtin/gc.c:224
+#, c-format
+msgid ""
+"Auto packing the repository for optimum performance. You may also\n"
+"run \"git gc\" manually. See \"git help gc\" for more information.\n"
+msgstr ""
+
+#: builtin/gc.c:251
+msgid ""
+"There are too many unreachable loose objects; run 'git prune' to remove them."
+msgstr ""
+
+#: builtin/grep.c:216
+#, c-format
+msgid "grep: failed to create thread: %s"
+msgstr ""
+
+#: builtin/grep.c:402
+#, c-format
+msgid "Failed to chdir: %s"
+msgstr ""
+
+#: builtin/grep.c:478 builtin/grep.c:512
+#, c-format
+msgid "unable to read tree (%s)"
+msgstr ""
+
+#: builtin/grep.c:526
+#, c-format
+msgid "unable to grep from object of type %s"
+msgstr ""
+
+#: builtin/grep.c:584
+#, c-format
+msgid "switch `%c' expects a numerical value"
+msgstr ""
+
+#: builtin/grep.c:601
+#, c-format
+msgid "cannot open '%s'"
+msgstr ""
+
+#: builtin/grep.c:885
+msgid "no pattern given."
+msgstr ""
+
+#: builtin/grep.c:899
+#, c-format
+msgid "bad object %s"
+msgstr ""
+
+#: builtin/grep.c:940
+msgid "--open-files-in-pager only works on the worktree"
+msgstr ""
+
+#: builtin/grep.c:963
+msgid "--cached or --untracked cannot be used with --no-index."
+msgstr ""
+
+#: builtin/grep.c:968
+msgid "--no-index or --untracked cannot be used with revs."
+msgstr ""
+
+#: builtin/grep.c:971
+msgid "--[no-]exclude-standard cannot be used for tracked contents."
+msgstr ""
+
+#: builtin/grep.c:979
+msgid "both --cached and trees are given."
+msgstr ""
+
+#: builtin/help.c:63
+#, c-format
+msgid "unrecognized help format '%s'"
+msgstr ""
+
+#: builtin/help.c:91
+msgid "Failed to start emacsclient."
+msgstr ""
+
+#: builtin/help.c:104
+msgid "Failed to parse emacsclient version."
+msgstr ""
+
+#: builtin/help.c:112
+#, c-format
+msgid "emacsclient version '%d' too old (< 22)."
+msgstr ""
+
+#: builtin/help.c:130 builtin/help.c:158 builtin/help.c:167 builtin/help.c:175
+#, c-format
+msgid "failed to exec '%s': %s"
+msgstr ""
+
+#: builtin/help.c:215
+#, c-format
+msgid ""
+"'%s': path for unsupported man viewer.\n"
+"Please consider using 'man.<tool>.cmd' instead."
+msgstr ""
+
+#: builtin/help.c:227
+#, c-format
+msgid ""
+"'%s': cmd for supported man viewer.\n"
+"Please consider using 'man.<tool>.path' instead."
+msgstr ""
+
+#: builtin/help.c:291
+msgid "The most commonly used git commands are:"
+msgstr ""
+
+#: builtin/help.c:359
+#, c-format
+msgid "'%s': unknown man viewer."
+msgstr ""
+
+#: builtin/help.c:376
+msgid "no man viewer handled the request"
+msgstr ""
+
+#: builtin/help.c:384
+msgid "no info viewer handled the request"
+msgstr ""
+
+#: builtin/help.c:395
+#, c-format
+msgid "'%s': not a documentation directory."
+msgstr ""
+
+#: builtin/help.c:436 builtin/help.c:443
+#, c-format
+msgid "usage: %s%s"
+msgstr ""
+
+#: builtin/help.c:459
+#, c-format
+msgid "`git %s' is aliased to `%s'"
+msgstr ""
+
+#: builtin/index-pack.c:170
+#, c-format
+msgid "object type mismatch at %s"
+msgstr ""
+
+#: builtin/index-pack.c:190
+msgid "object of unexpected type"
+msgstr ""
+
+#: builtin/index-pack.c:227
+#, c-format
+msgid "cannot fill %d byte"
+msgid_plural "cannot fill %d bytes"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/index-pack.c:237
+msgid "early EOF"
+msgstr ""
+
+#: builtin/index-pack.c:238
+msgid "read error on input"
+msgstr ""
+
+#: builtin/index-pack.c:250
+msgid "used more bytes than were available"
+msgstr ""
+
+#: builtin/index-pack.c:257
+msgid "pack too large for current definition of off_t"
+msgstr ""
+
+#: builtin/index-pack.c:273
+#, c-format
+msgid "unable to create '%s'"
+msgstr ""
+
+#: builtin/index-pack.c:278
+#, c-format
+msgid "cannot open packfile '%s'"
+msgstr ""
+
+#: builtin/index-pack.c:292
+msgid "pack signature mismatch"
+msgstr ""
+
+#: builtin/index-pack.c:312
+#, c-format
+msgid "pack has bad object at offset %lu: %s"
+msgstr ""
+
+#: builtin/index-pack.c:434
+#, c-format
+msgid "inflate returned %d"
+msgstr ""
+
+#: builtin/index-pack.c:483
+msgid "offset value overflow for delta base object"
+msgstr ""
+
+#: builtin/index-pack.c:491
+msgid "delta base offset is out of bound"
+msgstr ""
+
+#: builtin/index-pack.c:499
+#, c-format
+msgid "unknown object type %d"
+msgstr ""
+
+#: builtin/index-pack.c:531
+msgid "cannot pread pack file"
+msgstr ""
+
+#: builtin/index-pack.c:533
+#, c-format
+msgid "premature end of pack file, %lu byte missing"
+msgid_plural "premature end of pack file, %lu bytes missing"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/index-pack.c:555
+msgid "serious inflate inconsistency"
+msgstr ""
+
+#: builtin/index-pack.c:646 builtin/index-pack.c:652 builtin/index-pack.c:675
+#: builtin/index-pack.c:709 builtin/index-pack.c:718
+#, c-format
+msgid "SHA1 COLLISION FOUND WITH %s !"
+msgstr ""
+
+#: builtin/index-pack.c:649 builtin/pack-objects.c:170
+#: builtin/pack-objects.c:262
+#, c-format
+msgid "unable to read %s"
+msgstr ""
+
+#: builtin/index-pack.c:715
+#, c-format
+msgid "cannot read existing object %s"
+msgstr ""
+
+#: builtin/index-pack.c:729
+#, c-format
+msgid "invalid blob object %s"
+msgstr ""
+
+#: builtin/index-pack.c:744
+#, c-format
+msgid "invalid %s"
+msgstr ""
+
+#: builtin/index-pack.c:746
+msgid "Error in object"
+msgstr ""
+
+#: builtin/index-pack.c:748
+#, c-format
+msgid "Not all child objects of %s are reachable"
+msgstr ""
+
+#: builtin/index-pack.c:818 builtin/index-pack.c:844
+msgid "failed to apply delta"
+msgstr ""
+
+#: builtin/index-pack.c:983
+msgid "Receiving objects"
+msgstr ""
+
+#: builtin/index-pack.c:983
+msgid "Indexing objects"
+msgstr ""
+
+#: builtin/index-pack.c:1009
+msgid "pack is corrupted (SHA1 mismatch)"
+msgstr ""
+
+#: builtin/index-pack.c:1014
+msgid "cannot fstat packfile"
+msgstr ""
+
+#: builtin/index-pack.c:1017
+msgid "pack has junk at the end"
+msgstr ""
+
+#: builtin/index-pack.c:1028
+msgid "confusion beyond insanity in parse_pack_objects()"
+msgstr ""
+
+#: builtin/index-pack.c:1051
+msgid "Resolving deltas"
+msgstr ""
+
+#: builtin/index-pack.c:1102
+msgid "confusion beyond insanity"
+msgstr ""
+
+#: builtin/index-pack.c:1121
+#, c-format
+msgid "pack has %d unresolved delta"
+msgid_plural "pack has %d unresolved deltas"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/index-pack.c:1146
+#, c-format
+msgid "unable to deflate appended object (%d)"
+msgstr ""
+
+#: builtin/index-pack.c:1225
+#, c-format
+msgid "local object %s is corrupt"
+msgstr ""
+
+#: builtin/index-pack.c:1249
+msgid "error while closing pack file"
+msgstr ""
+
+#: builtin/index-pack.c:1262
+#, c-format
+msgid "cannot write keep file '%s'"
+msgstr ""
+
+#: builtin/index-pack.c:1270
+#, c-format
+msgid "cannot close written keep file '%s'"
+msgstr ""
+
+#: builtin/index-pack.c:1283
+msgid "cannot store pack file"
+msgstr ""
+
+#: builtin/index-pack.c:1294
+msgid "cannot store index file"
+msgstr ""
+
+#: builtin/index-pack.c:1395
+#, c-format
+msgid "Cannot open existing pack file '%s'"
+msgstr ""
+
+#: builtin/index-pack.c:1397
+#, c-format
+msgid "Cannot open existing pack idx file for '%s'"
+msgstr ""
+
+#: builtin/index-pack.c:1444
+#, c-format
+msgid "non delta: %d object"
+msgid_plural "non delta: %d objects"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/index-pack.c:1451
+#, c-format
+msgid "chain length = %d: %lu object"
+msgid_plural "chain length = %d: %lu objects"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/index-pack.c:1478
+msgid "Cannot come back to cwd"
+msgstr ""
+
+#: builtin/index-pack.c:1522 builtin/index-pack.c:1525
+#: builtin/index-pack.c:1537 builtin/index-pack.c:1541
+#, c-format
+msgid "bad %s"
+msgstr ""
+
+#: builtin/index-pack.c:1555
+msgid "--fix-thin cannot be used without --stdin"
+msgstr ""
+
+#: builtin/index-pack.c:1559 builtin/index-pack.c:1569
+#, c-format
+msgid "packfile name '%s' does not end with '.pack'"
+msgstr ""
+
+#: builtin/index-pack.c:1578
+msgid "--verify with no packfile name given"
+msgstr ""
+
+#: builtin/init-db.c:35
+#, c-format
+msgid "Could not make %s writable by group"
+msgstr ""
+
+#: builtin/init-db.c:62
+#, c-format
+msgid "insanely long template name %s"
+msgstr ""
+
+#: builtin/init-db.c:67
+#, c-format
+msgid "cannot stat '%s'"
+msgstr ""
+
+#: builtin/init-db.c:73
+#, c-format
+msgid "cannot stat template '%s'"
+msgstr ""
+
+#: builtin/init-db.c:80
+#, c-format
+msgid "cannot opendir '%s'"
+msgstr ""
+
+#: builtin/init-db.c:97
+#, c-format
+msgid "cannot readlink '%s'"
+msgstr ""
+
+#: builtin/init-db.c:99
+#, c-format
+msgid "insanely long symlink %s"
+msgstr ""
+
+#: builtin/init-db.c:102
+#, c-format
+msgid "cannot symlink '%s' '%s'"
+msgstr ""
+
+#: builtin/init-db.c:106
+#, c-format
+msgid "cannot copy '%s' to '%s'"
+msgstr ""
+
+#: builtin/init-db.c:110
+#, c-format
+msgid "ignoring template %s"
+msgstr ""
+
+#: builtin/init-db.c:133
+#, c-format
+msgid "insanely long template path %s"
+msgstr ""
+
+#: builtin/init-db.c:141
+#, c-format
+msgid "templates not found %s"
+msgstr ""
+
+#: builtin/init-db.c:154
+#, c-format
+msgid "not copying templates of a wrong format version %d from '%s'"
+msgstr ""
+
+#: builtin/init-db.c:192
+#, c-format
+msgid "insane git directory %s"
+msgstr ""
+
+#: builtin/init-db.c:322 builtin/init-db.c:325
+#, c-format
+msgid "%s already exists"
+msgstr ""
+
+#: builtin/init-db.c:354
+#, c-format
+msgid "unable to handle file type %d"
+msgstr ""
+
+#: builtin/init-db.c:357
+#, c-format
+msgid "unable to move %s to %s"
+msgstr ""
+
+#: builtin/init-db.c:362
+#, c-format
+msgid "Could not create git link %s"
+msgstr ""
+
+#.
+#. * TRANSLATORS: The first '%s' is either "Reinitialized
+#. * existing" or "Initialized empty", the second " shared" or
+#. * "", and the last '%s%s' is the verbatim directory name.
+#.
+#: builtin/init-db.c:419
+#, c-format
+msgid "%s%s Git repository in %s%s\n"
+msgstr ""
+
+#: builtin/init-db.c:420
+msgid "Reinitialized existing"
+msgstr ""
+
+#: builtin/init-db.c:420
+msgid "Initialized empty"
+msgstr ""
+
+#: builtin/init-db.c:421
+msgid " shared"
+msgstr ""
+
+#: builtin/init-db.c:440
+msgid "cannot tell cwd"
+msgstr ""
+
+#: builtin/init-db.c:521 builtin/init-db.c:528
+#, c-format
+msgid "cannot mkdir %s"
+msgstr ""
+
+#: builtin/init-db.c:532
+#, c-format
+msgid "cannot chdir to %s"
+msgstr ""
+
+#: builtin/init-db.c:554
+#, c-format
+msgid ""
+"%s (or --work-tree=<directory>) not allowed without specifying %s (or --git-"
+"dir=<directory>)"
+msgstr ""
+
+#: builtin/init-db.c:578
+msgid "Cannot access current working directory"
+msgstr ""
+
+#: builtin/init-db.c:585
+#, c-format
+msgid "Cannot access work tree '%s'"
+msgstr ""
+
+#: builtin/log.c:189
+#, c-format
+msgid "Final output: %d %s\n"
+msgstr ""
+
+#: builtin/log.c:402 builtin/log.c:490
+#, c-format
+msgid "Could not read object %s"
+msgstr ""
+
+#: builtin/log.c:514
+#, c-format
+msgid "Unknown type: %d"
+msgstr ""
+
+#: builtin/log.c:603
+msgid "format.headers without value"
+msgstr ""
+
+#: builtin/log.c:677
+msgid "name of output directory is too long"
+msgstr ""
+
+#: builtin/log.c:688
+#, c-format
+msgid "Cannot open patch file %s"
+msgstr ""
+
+#: builtin/log.c:702
+msgid "Need exactly one range."
+msgstr ""
+
+#: builtin/log.c:710
+msgid "Not a range."
+msgstr ""
+
+#: builtin/log.c:787
+msgid "Cover letter needs email format"
+msgstr ""
+
+#: builtin/log.c:860
+#, c-format
+msgid "insane in-reply-to: %s"
+msgstr ""
+
+#: builtin/log.c:933
+msgid "Two output directories?"
+msgstr ""
+
+#: builtin/log.c:1154
+#, c-format
+msgid "bogus committer info %s"
+msgstr ""
+
+#: builtin/log.c:1199
+msgid "-n and -k are mutually exclusive."
+msgstr ""
+
+#: builtin/log.c:1201
+msgid "--subject-prefix and -k are mutually exclusive."
+msgstr ""
+
+#: builtin/log.c:1209
+msgid "--name-only does not make sense"
+msgstr ""
+
+#: builtin/log.c:1211
+msgid "--name-status does not make sense"
+msgstr ""
+
+#: builtin/log.c:1213
+msgid "--check does not make sense"
+msgstr ""
+
+#: builtin/log.c:1236
+msgid "standard output, or directory, which one?"
+msgstr ""
+
+#: builtin/log.c:1238
+#, c-format
+msgid "Could not create directory '%s'"
+msgstr ""
+
+#: builtin/log.c:1391
+msgid "Failed to create output files"
+msgstr ""
+
+#: builtin/log.c:1495
+#, c-format
+msgid ""
+"Could not find a tracked remote branch, please specify <upstream> manually.\n"
+msgstr ""
+
+#: builtin/log.c:1511 builtin/log.c:1513 builtin/log.c:1525
+#, c-format
+msgid "Unknown commit %s"
+msgstr ""
+
+#: builtin/merge.c:90
+msgid "switch `m' requires a value"
+msgstr ""
+
+#: builtin/merge.c:127
+#, c-format
+msgid "Could not find merge strategy '%s'.\n"
+msgstr ""
+
+#: builtin/merge.c:128
+#, c-format
+msgid "Available strategies are:"
+msgstr ""
+
+#: builtin/merge.c:133
+#, c-format
+msgid "Available custom strategies are:"
+msgstr ""
+
+#: builtin/merge.c:240
+msgid "could not run stash."
+msgstr ""
+
+#: builtin/merge.c:245
+msgid "stash failed"
+msgstr ""
+
+#: builtin/merge.c:250
+#, c-format
+msgid "not a valid object: %s"
+msgstr ""
+
+#: builtin/merge.c:269 builtin/merge.c:286
+msgid "read-tree failed"
+msgstr ""
+
+#: builtin/merge.c:316
+msgid " (nothing to squash)"
+msgstr ""
+
+#: builtin/merge.c:329
+#, c-format
+msgid "Squash commit -- not updating HEAD\n"
+msgstr ""
+
+#: builtin/merge.c:361
+msgid "Writing SQUASH_MSG"
+msgstr ""
+
+#: builtin/merge.c:363
+msgid "Finishing SQUASH_MSG"
+msgstr ""
+
+#: builtin/merge.c:386
+#, c-format
+msgid "No merge message -- not updating HEAD\n"
+msgstr ""
+
+#: builtin/merge.c:437
+#, c-format
+msgid "'%s' does not point to a commit"
+msgstr ""
+
+#: builtin/merge.c:536
+#, c-format
+msgid "Bad branch.%s.mergeoptions string: %s"
+msgstr ""
+
+#: builtin/merge.c:629
+msgid "git write-tree failed to write a tree"
+msgstr ""
+
+#: builtin/merge.c:679
+msgid "failed to read the cache"
+msgstr ""
+
+#: builtin/merge.c:697
+msgid "Unable to write index."
+msgstr ""
+
+#: builtin/merge.c:710
+msgid "Not handling anything other than two heads merge."
+msgstr ""
+
+#: builtin/merge.c:724
+#, c-format
+msgid "Unknown option for merge-recursive: -X%s"
+msgstr ""
+
+#: builtin/merge.c:738
+#, c-format
+msgid "unable to write %s"
+msgstr ""
+
+#: builtin/merge.c:877
+#, c-format
+msgid "Could not read from '%s'"
+msgstr ""
+
+#: builtin/merge.c:886
+#, c-format
+msgid "Not committing merge; use 'git commit' to complete the merge.\n"
+msgstr ""
+
+#: builtin/merge.c:892
+msgid ""
+"Please enter a commit message to explain why this merge is necessary,\n"
+"especially if it merges an updated upstream into a topic branch.\n"
+"\n"
+"Lines starting with '#' will be ignored, and an empty message aborts\n"
+"the commit.\n"
+msgstr ""
+
+#: builtin/merge.c:916
+msgid "Empty commit message."
+msgstr ""
+
+#: builtin/merge.c:928
+#, c-format
+msgid "Wonderful.\n"
+msgstr ""
+
+#: builtin/merge.c:993
+#, c-format
+msgid "Automatic merge failed; fix conflicts and then commit the result.\n"
+msgstr ""
+
+#: builtin/merge.c:1009
+#, c-format
+msgid "'%s' is not a commit"
+msgstr ""
+
+#: builtin/merge.c:1050
+msgid "No current branch."
+msgstr ""
+
+#: builtin/merge.c:1052
+msgid "No remote for the current branch."
+msgstr ""
+
+#: builtin/merge.c:1054
+msgid "No default upstream defined for the current branch."
+msgstr ""
+
+#: builtin/merge.c:1059
+#, c-format
+msgid "No remote tracking branch for %s from %s"
+msgstr ""
+
+#: builtin/merge.c:1146 builtin/merge.c:1303
+#, c-format
+msgid "%s - not something we can merge"
+msgstr ""
+
+#: builtin/merge.c:1214
+msgid "There is no merge to abort (MERGE_HEAD missing)."
+msgstr ""
+
+#: builtin/merge.c:1230 git-pull.sh:31
+msgid ""
+"You have not concluded your merge (MERGE_HEAD exists).\n"
+"Please, commit your changes before you can merge."
+msgstr ""
+
+#: builtin/merge.c:1233 git-pull.sh:34
+msgid "You have not concluded your merge (MERGE_HEAD exists)."
+msgstr ""
+
+#: builtin/merge.c:1237
+msgid ""
+"You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
+"Please, commit your changes before you can merge."
+msgstr ""
+
+#: builtin/merge.c:1240
+msgid "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."
+msgstr ""
+
+#: builtin/merge.c:1249
+msgid "You cannot combine --squash with --no-ff."
+msgstr ""
+
+#: builtin/merge.c:1254
+msgid "You cannot combine --no-ff with --ff-only."
+msgstr ""
+
+#: builtin/merge.c:1261
+msgid "No commit specified and merge.defaultToUpstream not set."
+msgstr ""
+
+#: builtin/merge.c:1293
+msgid "Can merge only exactly one commit into empty head"
+msgstr ""
+
+#: builtin/merge.c:1296
+msgid "Squash commit into empty head not supported yet"
+msgstr ""
+
+#: builtin/merge.c:1298
+msgid "Non-fast-forward commit does not make sense into an empty head"
+msgstr ""
+
+#: builtin/merge.c:1413
+#, c-format
+msgid "Updating %s..%s\n"
+msgstr ""
+
+#: builtin/merge.c:1451
+#, c-format
+msgid "Trying really trivial in-index merge...\n"
+msgstr ""
+
+#: builtin/merge.c:1458
+#, c-format
+msgid "Nope.\n"
+msgstr ""
+
+#: builtin/merge.c:1490
+msgid "Not possible to fast-forward, aborting."
+msgstr ""
+
+#: builtin/merge.c:1513 builtin/merge.c:1592
+#, c-format
+msgid "Rewinding the tree to pristine...\n"
+msgstr ""
+
+#: builtin/merge.c:1517
+#, c-format
+msgid "Trying merge strategy %s...\n"
+msgstr ""
+
+#: builtin/merge.c:1583
+#, c-format
+msgid "No merge strategy handled the merge.\n"
+msgstr ""
+
+#: builtin/merge.c:1585
+#, c-format
+msgid "Merge with strategy %s failed.\n"
+msgstr ""
+
+#: builtin/merge.c:1594
+#, c-format
+msgid "Using the %s to prepare resolving by hand.\n"
+msgstr ""
+
+#: builtin/merge.c:1606
+#, c-format
+msgid "Automatic merge went well; stopped before committing as requested\n"
+msgstr ""
+
+#: builtin/mv.c:108
+#, c-format
+msgid "Checking rename of '%s' to '%s'\n"
+msgstr ""
+
+#: builtin/mv.c:112
+msgid "bad source"
+msgstr ""
+
+#: builtin/mv.c:115
+msgid "can not move directory into itself"
+msgstr ""
+
+#: builtin/mv.c:118
+msgid "cannot move directory over file"
+msgstr ""
+
+#: builtin/mv.c:128
+#, c-format
+msgid "Huh? %.*s is in index?"
+msgstr ""
+
+#: builtin/mv.c:140
+msgid "source directory is empty"
+msgstr ""
+
+#: builtin/mv.c:171
+msgid "not under version control"
+msgstr ""
+
+#: builtin/mv.c:173
+msgid "destination exists"
+msgstr ""
+
+#: builtin/mv.c:181
+#, c-format
+msgid "overwriting '%s'"
+msgstr ""
+
+#: builtin/mv.c:184
+msgid "Cannot overwrite"
+msgstr ""
+
+#: builtin/mv.c:187
+msgid "multiple sources for the same target"
+msgstr ""
+
+#: builtin/mv.c:202
+#, c-format
+msgid "%s, source=%s, destination=%s"
+msgstr ""
+
+#: builtin/mv.c:212
+#, c-format
+msgid "Renaming %s to %s\n"
+msgstr ""
+
+#: builtin/mv.c:215 builtin/remote.c:731
+#, c-format
+msgid "renaming '%s' failed"
+msgstr ""
+
+#: builtin/notes.c:139
+#, c-format
+msgid "unable to start 'show' for object '%s'"
+msgstr ""
+
+#: builtin/notes.c:145
+msgid "can't fdopen 'show' output fd"
+msgstr ""
+
+#: builtin/notes.c:155
+#, c-format
+msgid "failed to close pipe to 'show' for object '%s'"
+msgstr ""
+
+#: builtin/notes.c:158
+#, c-format
+msgid "failed to finish 'show' for object '%s'"
+msgstr ""
+
+#: builtin/notes.c:175 builtin/tag.c:347
+#, c-format
+msgid "could not create file '%s'"
+msgstr ""
+
+#: builtin/notes.c:189
+msgid "Please supply the note contents using either -m or -F option"
+msgstr ""
+
+#: builtin/notes.c:210 builtin/notes.c:973
+#, c-format
+msgid "Removing note for object %s\n"
+msgstr ""
+
+#: builtin/notes.c:215
+msgid "unable to write note object"
+msgstr ""
+
+#: builtin/notes.c:217
+#, c-format
+msgid "The note contents has been left in %s"
+msgstr ""
+
+#: builtin/notes.c:251 builtin/tag.c:542
+#, c-format
+msgid "cannot read '%s'"
+msgstr ""
+
+#: builtin/notes.c:253 builtin/tag.c:545
+#, c-format
+msgid "could not open or read '%s'"
+msgstr ""
+
+#: builtin/notes.c:272 builtin/notes.c:445 builtin/notes.c:447
+#: builtin/notes.c:507 builtin/notes.c:561 builtin/notes.c:644
+#: builtin/notes.c:649 builtin/notes.c:724 builtin/notes.c:766
+#: builtin/notes.c:968 builtin/reset.c:293 builtin/tag.c:558
+#, c-format
+msgid "Failed to resolve '%s' as a valid ref."
+msgstr ""
+
+#: builtin/notes.c:275
+#, c-format
+msgid "Failed to read object '%s'."
+msgstr ""
+
+#: builtin/notes.c:299
+msgid "Cannot commit uninitialized/unreferenced notes tree"
+msgstr ""
+
+#: builtin/notes.c:340
+#, c-format
+msgid "Bad notes.rewriteMode value: '%s'"
+msgstr ""
+
+#: builtin/notes.c:350
+#, c-format
+msgid "Refusing to rewrite notes in %s (outside of refs/notes/)"
+msgstr ""
+
+#. TRANSLATORS: The first %s is the name of the
+#. environment variable, the second %s is its value
+#: builtin/notes.c:377
+#, c-format
+msgid "Bad %s value: '%s'"
+msgstr ""
+
+#: builtin/notes.c:441
+#, c-format
+msgid "Malformed input line: '%s'."
+msgstr ""
+
+#: builtin/notes.c:456
+#, c-format
+msgid "Failed to copy notes from '%s' to '%s'"
+msgstr ""
+
+#: builtin/notes.c:500 builtin/notes.c:554 builtin/notes.c:627
+#: builtin/notes.c:639 builtin/notes.c:712 builtin/notes.c:759
+#: builtin/notes.c:1033
+msgid "too many parameters"
+msgstr ""
+
+#: builtin/notes.c:513 builtin/notes.c:772
+#, c-format
+msgid "No note found for object %s."
+msgstr ""
+
+#: builtin/notes.c:580
+#, c-format
+msgid ""
+"Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite "
+"existing notes"
+msgstr ""
+
+#: builtin/notes.c:585 builtin/notes.c:662
+#, c-format
+msgid "Overwriting existing notes for object %s\n"
+msgstr ""
+
+#: builtin/notes.c:635
+msgid "too few parameters"
+msgstr ""
+
+#: builtin/notes.c:656
+#, c-format
+msgid ""
+"Cannot copy notes. Found existing notes for object %s. Use '-f' to overwrite "
+"existing notes"
+msgstr ""
+
+#: builtin/notes.c:668
+#, c-format
+msgid "Missing notes on source object %s. Cannot copy."
+msgstr ""
+
+#: builtin/notes.c:717
+#, c-format
+msgid ""
+"The -m/-F/-c/-C options have been deprecated for the 'edit' subcommand.\n"
+"Please use 'git notes add -f -m/-F/-c/-C' instead.\n"
+msgstr ""
+
+#: builtin/notes.c:971
+#, c-format
+msgid "Object %s has no note\n"
+msgstr ""
+
+#: builtin/notes.c:1103 builtin/remote.c:1598
+#, c-format
+msgid "Unknown subcommand: %s"
+msgstr ""
+
+#: builtin/pack-objects.c:183 builtin/pack-objects.c:186
+#, c-format
+msgid "deflate error (%d)"
+msgstr ""
+
+#: builtin/pack-objects.c:2398
+#, c-format
+msgid "unsupported index version %s"
+msgstr ""
+
+#: builtin/pack-objects.c:2402
+#, c-format
+msgid "bad index version '%s'"
+msgstr ""
+
+#: builtin/pack-objects.c:2425
+#, c-format
+msgid "option %s does not accept negative form"
+msgstr ""
+
+#: builtin/pack-objects.c:2429
+#, c-format
+msgid "unable to parse value '%s' for option %s"
+msgstr ""
+
+#: builtin/push.c:45
+msgid "tag shorthand without <tag>"
+msgstr ""
+
+#: builtin/push.c:64
+msgid "--delete only accepts plain target ref names"
+msgstr ""
+
+#: builtin/push.c:99
+msgid ""
+"\n"
+"To choose either option permanently, see push.default in 'git help config'."
+msgstr ""
+
+#: builtin/push.c:102
+#, c-format
+msgid ""
+"The upstream branch of your current branch does not match\n"
+"the name of your current branch.  To push to the upstream branch\n"
+"on the remote, use\n"
+"\n"
+"    git push %s HEAD:%s\n"
+"\n"
+"To push to the branch of the same name on the remote, use\n"
+"\n"
+"    git push %s %s\n"
+"%s"
+msgstr ""
+
+#: builtin/push.c:121
+#, c-format
+msgid ""
+"You are not currently on a branch.\n"
+"To push the history leading to the current (detached HEAD)\n"
+"state now, use\n"
+"\n"
+"    git push %s HEAD:<name-of-remote-branch>\n"
+msgstr ""
+
+#: builtin/push.c:128
+#, c-format
+msgid ""
+"The current branch %s has no upstream branch.\n"
+"To push the current branch and set the remote as upstream, use\n"
+"\n"
+"    git push --set-upstream %s %s\n"
+msgstr ""
+
+#: builtin/push.c:136
+#, c-format
+msgid "The current branch %s has multiple upstream branches, refusing to push."
+msgstr ""
+
+#: builtin/push.c:139
+#, c-format
+msgid ""
+"You are pushing to remote '%s', which is not the upstream of\n"
+"your current branch '%s', without telling me what to push\n"
+"to update which remote branch."
+msgstr ""
+
+#: builtin/push.c:174
+msgid ""
+"You didn't specify any refspecs to push, and push.default is \"nothing\"."
+msgstr ""
+
+#: builtin/push.c:181
+msgid ""
+"Updates were rejected because the tip of your current branch is behind\n"
+"its remote counterpart. Merge the remote changes (e.g. 'git pull')\n"
+"before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
+msgstr ""
+
+#: builtin/push.c:187
+msgid ""
+"Updates were rejected because a pushed branch tip is behind its remote\n"
+"counterpart. If you did not intend to push that branch, you may want to\n"
+"specify branches to push or set the 'push.default' configuration\n"
+"variable to 'current' or 'upstream' to push only the current branch."
+msgstr ""
+
+#: builtin/push.c:193
+msgid ""
+"Updates were rejected because a pushed branch tip is behind its remote\n"
+"counterpart. Check out this branch and merge the remote changes\n"
+"(e.g. 'git pull') before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
+msgstr ""
+
+#: builtin/push.c:233
+#, c-format
+msgid "Pushing to %s\n"
+msgstr ""
+
+#: builtin/push.c:237
+#, c-format
+msgid "failed to push some refs to '%s'"
+msgstr ""
+
+#: builtin/push.c:269
+#, c-format
+msgid "bad repository '%s'"
+msgstr ""
+
+#: builtin/push.c:270
+msgid ""
+"No configured push destination.\n"
+"Either specify the URL from the command-line or configure a remote "
+"repository using\n"
+"\n"
+"    git remote add <name> <url>\n"
+"\n"
+"and then push using the remote name\n"
+"\n"
+"    git push <name>\n"
+msgstr ""
+
+#: builtin/push.c:285
+msgid "--all and --tags are incompatible"
+msgstr ""
+
+#: builtin/push.c:286
+msgid "--all can't be combined with refspecs"
+msgstr ""
+
+#: builtin/push.c:291
+msgid "--mirror and --tags are incompatible"
+msgstr ""
+
+#: builtin/push.c:292
+msgid "--mirror can't be combined with refspecs"
+msgstr ""
+
+#: builtin/push.c:297
+msgid "--all and --mirror are incompatible"
+msgstr ""
+
+#: builtin/push.c:385
+msgid "--delete is incompatible with --all, --mirror and --tags"
+msgstr ""
+
+#: builtin/push.c:387
+msgid "--delete doesn't make sense without any refs"
+msgstr ""
+
+#: builtin/remote.c:98
+#, c-format
+msgid "Updating %s"
+msgstr ""
+
+#: builtin/remote.c:130
+msgid ""
+"--mirror is dangerous and deprecated; please\n"
+"\t use --mirror=fetch or --mirror=push instead"
+msgstr ""
+
+#: builtin/remote.c:147
+#, c-format
+msgid "unknown mirror argument: %s"
+msgstr ""
+
+#: builtin/remote.c:185
+msgid "specifying a master branch makes no sense with --mirror"
+msgstr ""
+
+#: builtin/remote.c:187
+msgid "specifying branches to track makes sense only with fetch mirrors"
+msgstr ""
+
+#: builtin/remote.c:195 builtin/remote.c:646
+#, c-format
+msgid "remote %s already exists."
+msgstr ""
+
+#: builtin/remote.c:199 builtin/remote.c:650
+#, c-format
+msgid "'%s' is not a valid remote name"
+msgstr ""
+
+#: builtin/remote.c:243
+#, c-format
+msgid "Could not setup master '%s'"
+msgstr ""
+
+#: builtin/remote.c:299
+#, c-format
+msgid "more than one %s"
+msgstr ""
+
+#: builtin/remote.c:339
+#, c-format
+msgid "Could not get fetch map for refspec %s"
+msgstr ""
+
+#: builtin/remote.c:440 builtin/remote.c:448
+msgid "(matching)"
+msgstr ""
+
+#: builtin/remote.c:452
+msgid "(delete)"
+msgstr ""
+
+#: builtin/remote.c:595 builtin/remote.c:601 builtin/remote.c:607
+#, c-format
+msgid "Could not append '%s' to '%s'"
+msgstr ""
+
+#: builtin/remote.c:639 builtin/remote.c:792 builtin/remote.c:890
+#, c-format
+msgid "No such remote: %s"
+msgstr ""
+
+#: builtin/remote.c:656
+#, c-format
+msgid "Could not rename config section '%s' to '%s'"
+msgstr ""
+
+#: builtin/remote.c:662 builtin/remote.c:799
+#, c-format
+msgid "Could not remove config section '%s'"
+msgstr ""
+
+#: builtin/remote.c:677
+#, c-format
+msgid ""
+"Not updating non-default fetch refspec\n"
+"\t%s\n"
+"\tPlease update the configuration manually if necessary."
+msgstr ""
+
+#: builtin/remote.c:683
+#, c-format
+msgid "Could not append '%s'"
+msgstr ""
+
+#: builtin/remote.c:694
+#, c-format
+msgid "Could not set '%s'"
+msgstr ""
+
+#: builtin/remote.c:716
+#, c-format
+msgid "deleting '%s' failed"
+msgstr ""
+
+#: builtin/remote.c:750
+#, c-format
+msgid "creating '%s' failed"
+msgstr ""
+
+#: builtin/remote.c:764
+#, c-format
+msgid "Could not remove branch %s"
+msgstr ""
+
+#: builtin/remote.c:834
+msgid ""
+"Note: A branch outside the refs/remotes/ hierarchy was not removed;\n"
+"to delete it, use:"
+msgid_plural ""
+"Note: Some branches outside the refs/remotes/ hierarchy were not removed;\n"
+"to delete them, use:"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/remote.c:943
+#, c-format
+msgid " new (next fetch will store in remotes/%s)"
+msgstr ""
+
+#: builtin/remote.c:946
+msgid " tracked"
+msgstr ""
+
+#: builtin/remote.c:948
+msgid " stale (use 'git remote prune' to remove)"
+msgstr ""
+
+#: builtin/remote.c:950
+msgid " ???"
+msgstr ""
+
+#: builtin/remote.c:991
+#, c-format
+msgid "invalid branch.%s.merge; cannot rebase onto > 1 branch"
+msgstr ""
+
+#: builtin/remote.c:998
+#, c-format
+msgid "rebases onto remote %s"
+msgstr ""
+
+#: builtin/remote.c:1001
+#, c-format
+msgid " merges with remote %s"
+msgstr ""
+
+#: builtin/remote.c:1002
+msgid "    and with remote"
+msgstr ""
+
+#: builtin/remote.c:1004
+#, c-format
+msgid "merges with remote %s"
+msgstr ""
+
+#: builtin/remote.c:1005
+msgid "   and with remote"
+msgstr ""
+
+#: builtin/remote.c:1051
+msgid "create"
+msgstr ""
+
+#: builtin/remote.c:1054
+msgid "delete"
+msgstr ""
+
+#: builtin/remote.c:1058
+msgid "up to date"
+msgstr ""
+
+#: builtin/remote.c:1061
+msgid "fast-forwardable"
+msgstr ""
+
+#: builtin/remote.c:1064
+msgid "local out of date"
+msgstr ""
+
+#: builtin/remote.c:1071
+#, c-format
+msgid "    %-*s forces to %-*s (%s)"
+msgstr ""
+
+#: builtin/remote.c:1074
+#, c-format
+msgid "    %-*s pushes to %-*s (%s)"
+msgstr ""
+
+#: builtin/remote.c:1078
+#, c-format
+msgid "    %-*s forces to %s"
+msgstr ""
+
+#: builtin/remote.c:1081
+#, c-format
+msgid "    %-*s pushes to %s"
+msgstr ""
+
+#: builtin/remote.c:1118
+#, c-format
+msgid "* remote %s"
+msgstr ""
+
+#: builtin/remote.c:1119
+#, c-format
+msgid "  Fetch URL: %s"
+msgstr ""
+
+#: builtin/remote.c:1120 builtin/remote.c:1285
+msgid "(no URL)"
+msgstr ""
+
+#: builtin/remote.c:1129 builtin/remote.c:1131
+#, c-format
+msgid "  Push  URL: %s"
+msgstr ""
+
+#: builtin/remote.c:1133 builtin/remote.c:1135 builtin/remote.c:1137
+#, c-format
+msgid "  HEAD branch: %s"
+msgstr ""
+
+#: builtin/remote.c:1139
+#, c-format
+msgid ""
+"  HEAD branch (remote HEAD is ambiguous, may be one of the following):\n"
+msgstr ""
+
+#: builtin/remote.c:1151
+#, c-format
+msgid "  Remote branch:%s"
+msgid_plural "  Remote branches:%s"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/remote.c:1154 builtin/remote.c:1181
+msgid " (status not queried)"
+msgstr ""
+
+#: builtin/remote.c:1163
+msgid "  Local branch configured for 'git pull':"
+msgid_plural "  Local branches configured for 'git pull':"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/remote.c:1171
+msgid "  Local refs will be mirrored by 'git push'"
+msgstr ""
+
+#: builtin/remote.c:1178
+#, c-format
+msgid "  Local ref configured for 'git push'%s:"
+msgid_plural "  Local refs configured for 'git push'%s:"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/remote.c:1216
+msgid "Cannot determine remote HEAD"
+msgstr ""
+
+#: builtin/remote.c:1218
+msgid "Multiple remote HEAD branches. Please choose one explicitly with:"
+msgstr ""
+
+#: builtin/remote.c:1228
+#, c-format
+msgid "Could not delete %s"
+msgstr ""
+
+#: builtin/remote.c:1236
+#, c-format
+msgid "Not a valid ref: %s"
+msgstr ""
+
+#: builtin/remote.c:1238
+#, c-format
+msgid "Could not setup %s"
+msgstr ""
+
+#: builtin/remote.c:1274
+#, c-format
+msgid " %s will become dangling!"
+msgstr ""
+
+#: builtin/remote.c:1275
+#, c-format
+msgid " %s has become dangling!"
+msgstr ""
+
+#: builtin/remote.c:1281
+#, c-format
+msgid "Pruning %s"
+msgstr ""
+
+#: builtin/remote.c:1282
+#, c-format
+msgid "URL: %s"
+msgstr ""
+
+#: builtin/remote.c:1295
+#, c-format
+msgid " * [would prune] %s"
+msgstr ""
+
+#: builtin/remote.c:1298
+#, c-format
+msgid " * [pruned] %s"
+msgstr ""
+
+#: builtin/remote.c:1387 builtin/remote.c:1461
+#, c-format
+msgid "No such remote '%s'"
+msgstr ""
+
+#: builtin/remote.c:1414
+msgid "no remote specified"
+msgstr ""
+
+#: builtin/remote.c:1447
+msgid "--add --delete doesn't make sense"
+msgstr ""
+
+#: builtin/remote.c:1487
+#, c-format
+msgid "Invalid old URL pattern: %s"
+msgstr ""
+
+#: builtin/remote.c:1495
+#, c-format
+msgid "No such URL found: %s"
+msgstr ""
+
+#: builtin/remote.c:1497
+msgid "Will not delete all non-push URLs"
+msgstr ""
+
+#: builtin/reset.c:33
+msgid "mixed"
+msgstr ""
+
+#: builtin/reset.c:33
+msgid "soft"
+msgstr ""
+
+#: builtin/reset.c:33
+msgid "hard"
+msgstr ""
+
+#: builtin/reset.c:33
+msgid "merge"
+msgstr ""
+
+#: builtin/reset.c:33
+msgid "keep"
+msgstr ""
+
+#: builtin/reset.c:77
+msgid "You do not have a valid HEAD."
+msgstr ""
+
+#: builtin/reset.c:79
+msgid "Failed to find tree of HEAD."
+msgstr ""
+
+#: builtin/reset.c:85
+#, c-format
+msgid "Failed to find tree of %s."
+msgstr ""
+
+#: builtin/reset.c:96
+msgid "Could not write new index file."
+msgstr ""
+
+#: builtin/reset.c:106
+#, c-format
+msgid "HEAD is now at %s"
+msgstr ""
+
+#: builtin/reset.c:130
+msgid "Could not read index"
+msgstr ""
+
+#: builtin/reset.c:133
+msgid "Unstaged changes after reset:"
+msgstr ""
+
+#: builtin/reset.c:223
+#, c-format
+msgid "Cannot do a %s reset in the middle of a merge."
+msgstr ""
+
+#: builtin/reset.c:297
+#, c-format
+msgid "Could not parse object '%s'."
+msgstr ""
+
+#: builtin/reset.c:302
+msgid "--patch is incompatible with --{hard,mixed,soft}"
+msgstr ""
+
+#: builtin/reset.c:311
+msgid "--mixed with paths is deprecated; use 'git reset -- <paths>' instead."
+msgstr ""
+
+#: builtin/reset.c:313
+#, c-format
+msgid "Cannot do %s reset with paths."
+msgstr ""
+
+#: builtin/reset.c:325
+#, c-format
+msgid "%s reset is not allowed in a bare repository"
+msgstr ""
+
+#: builtin/reset.c:341
+#, c-format
+msgid "Could not reset index file to revision '%s'."
+msgstr ""
+
+#: builtin/revert.c:70 builtin/revert.c:92
+#, c-format
+msgid "%s: %s cannot be used with %s"
+msgstr ""
+
+#: builtin/revert.c:131
+msgid "program error"
+msgstr ""
+
+#: builtin/revert.c:221
+msgid "revert failed"
+msgstr ""
+
+#: builtin/revert.c:236
+msgid "cherry-pick failed"
+msgstr ""
+
+#: builtin/rm.c:109
+#, c-format
+msgid ""
+"'%s' has staged content different from both the file and the HEAD\n"
+"(use -f to force removal)"
+msgstr ""
+
+#: builtin/rm.c:115
+#, c-format
+msgid ""
+"'%s' has changes staged in the index\n"
+"(use --cached to keep the file, or -f to force removal)"
+msgstr ""
+
+#: builtin/rm.c:119
+#, c-format
+msgid ""
+"'%s' has local modifications\n"
+"(use --cached to keep the file, or -f to force removal)"
+msgstr ""
+
+#: builtin/rm.c:194
+#, c-format
+msgid "not removing '%s' recursively without -r"
+msgstr ""
+
+#: builtin/rm.c:230
+#, c-format
+msgid "git rm: unable to remove %s"
+msgstr ""
+
+#: builtin/shortlog.c:157
+#, c-format
+msgid "Missing author: %s"
+msgstr ""
+
+#: builtin/tag.c:60
+#, c-format
+msgid "malformed object at '%s'"
+msgstr ""
+
+#: builtin/tag.c:207
+#, c-format
+msgid "tag name too long: %.*s..."
+msgstr ""
+
+#: builtin/tag.c:212
+#, c-format
+msgid "tag '%s' not found."
+msgstr ""
+
+#: builtin/tag.c:227
+#, c-format
+msgid "Deleted tag '%s' (was %s)\n"
+msgstr ""
+
+#: builtin/tag.c:239
+#, c-format
+msgid "could not verify the tag '%s'"
+msgstr ""
+
+#: builtin/tag.c:249
+msgid ""
+"\n"
+"#\n"
+"# Write a tag message\n"
+"# Lines starting with '#' will be ignored.\n"
+"#\n"
+msgstr ""
+
+#: builtin/tag.c:256
+msgid ""
+"\n"
+"#\n"
+"# Write a tag message\n"
+"# Lines starting with '#' will be kept; you may remove them yourself if you "
+"want to.\n"
+"#\n"
+msgstr ""
+
+#: builtin/tag.c:298
+msgid "unable to sign the tag"
+msgstr ""
+
+#: builtin/tag.c:300
+msgid "unable to write tag file"
+msgstr ""
+
+#: builtin/tag.c:325
+msgid "bad object type."
+msgstr ""
+
+#: builtin/tag.c:338
+msgid "tag header too big."
+msgstr ""
+
+#: builtin/tag.c:370
+msgid "no tag message?"
+msgstr ""
+
+#: builtin/tag.c:376
+#, c-format
+msgid "The tag message has been left in %s\n"
+msgstr ""
+
+#: builtin/tag.c:425
+msgid "switch 'points-at' requires an object"
+msgstr ""
+
+#: builtin/tag.c:427
+#, c-format
+msgid "malformed object name '%s'"
+msgstr ""
+
+#: builtin/tag.c:506
+msgid "--column and -n are incompatible"
+msgstr ""
+
+#: builtin/tag.c:523
+msgid "-n option is only allowed with -l."
+msgstr ""
+
+#: builtin/tag.c:525
+msgid "--contains option is only allowed with -l."
+msgstr ""
+
+#: builtin/tag.c:527
+msgid "--points-at option is only allowed with -l."
+msgstr ""
+
+#: builtin/tag.c:535
+msgid "only one -F or -m option is allowed."
+msgstr ""
+
+#: builtin/tag.c:555
+msgid "too many params"
+msgstr ""
+
+#: builtin/tag.c:561
+#, c-format
+msgid "'%s' is not a valid tag name."
+msgstr ""
+
+#: builtin/tag.c:566
+#, c-format
+msgid "tag '%s' already exists"
+msgstr ""
+
+#: builtin/tag.c:584
+#, c-format
+msgid "%s: cannot lock the ref"
+msgstr ""
+
+#: builtin/tag.c:586
+#, c-format
+msgid "%s: cannot update the ref"
+msgstr ""
+
+#: builtin/tag.c:588
+#, c-format
+msgid "Updated tag '%s' (was %s)\n"
+msgstr ""
+
+#: git.c:16
+msgid "See 'git help <command>' for more information on a specific command."
+msgstr ""
+
+#: parse-options.h:133 parse-options.h:235
+msgid "n"
+msgstr ""
+
+#: parse-options.h:141
+msgid "time"
+msgstr ""
+
+#: parse-options.h:149
+msgid "file"
+msgstr ""
+
+#: parse-options.h:151
+msgid "when"
+msgstr ""
+
+#: parse-options.h:156
+msgid "no-op (backward compatibility)"
+msgstr ""
+
+#: parse-options.h:228
+msgid "be more verbose"
+msgstr ""
+
+#: parse-options.h:230
+msgid "be more quiet"
+msgstr ""
+
+#: parse-options.h:236
+msgid "use <n> digits to display SHA-1s"
+msgstr ""
+
+#: common-cmds.h:8
+msgid "Add file contents to the index"
+msgstr ""
+
+#: common-cmds.h:9
+msgid "Find by binary search the change that introduced a bug"
+msgstr ""
+
+#: common-cmds.h:10
+msgid "List, create, or delete branches"
+msgstr ""
+
+#: common-cmds.h:11
+msgid "Checkout a branch or paths to the working tree"
+msgstr ""
+
+#: common-cmds.h:12
+msgid "Clone a repository into a new directory"
+msgstr ""
+
+#: common-cmds.h:13
+msgid "Record changes to the repository"
+msgstr ""
+
+#: common-cmds.h:14
+msgid "Show changes between commits, commit and working tree, etc"
+msgstr ""
+
+#: common-cmds.h:15
+msgid "Download objects and refs from another repository"
+msgstr ""
+
+#: common-cmds.h:16
+msgid "Print lines matching a pattern"
+msgstr ""
+
+#: common-cmds.h:17
+msgid "Create an empty git repository or reinitialize an existing one"
+msgstr ""
+
+#: common-cmds.h:18
+msgid "Show commit logs"
+msgstr ""
+
+#: common-cmds.h:19
+msgid "Join two or more development histories together"
+msgstr ""
+
+#: common-cmds.h:20
+msgid "Move or rename a file, a directory, or a symlink"
+msgstr ""
+
+#: common-cmds.h:21
+msgid "Fetch from and merge with another repository or a local branch"
+msgstr ""
+
+#: common-cmds.h:22
+msgid "Update remote refs along with associated objects"
+msgstr ""
+
+#: common-cmds.h:23
+msgid "Forward-port local commits to the updated upstream head"
+msgstr ""
+
+#: common-cmds.h:24
+msgid "Reset current HEAD to the specified state"
+msgstr ""
+
+#: common-cmds.h:25
+msgid "Remove files from the working tree and from the index"
+msgstr ""
+
+#: common-cmds.h:26
+msgid "Show various types of objects"
+msgstr ""
+
+#: common-cmds.h:27
+msgid "Show the working tree status"
+msgstr ""
+
+#: common-cmds.h:28
+msgid "Create, list, delete or verify a tag object signed with GPG"
+msgstr ""
+
+#: git-am.sh:50
+msgid "You need to set your committer info first"
+msgstr ""
+
+#: git-am.sh:95
+msgid ""
+"You seem to have moved HEAD since the last 'am' failure.\n"
+"Not rewinding to ORIG_HEAD"
+msgstr ""
+
+#: git-am.sh:105
+#, sh-format
+msgid ""
+"When you have resolved this problem run \"$cmdline --resolved\".\n"
+"If you would prefer to skip this patch, instead run \"$cmdline --skip\".\n"
+"To restore the original branch and stop patching run \"$cmdline --abort\"."
+msgstr ""
+
+#: git-am.sh:121
+msgid "Cannot fall back to three-way merge."
+msgstr ""
+
+#: git-am.sh:137
+msgid "Repository lacks necessary blobs to fall back on 3-way merge."
+msgstr ""
+
+#: git-am.sh:154
+msgid ""
+"Did you hand edit your patch?\n"
+"It does not apply to blobs recorded in its index."
+msgstr ""
+
+#: git-am.sh:163
+msgid "Falling back to patching base and 3-way merge..."
+msgstr ""
+
+#: git-am.sh:275
+msgid "Only one StGIT patch series can be applied at once"
+msgstr ""
+
+#: git-am.sh:362
+#, sh-format
+msgid "Patch format $patch_format is not supported."
+msgstr ""
+
+#: git-am.sh:364
+msgid "Patch format detection failed."
+msgstr ""
+
+#: git-am.sh:418
+msgid "-d option is no longer supported.  Do not use."
+msgstr ""
+
+#: git-am.sh:481
+#, sh-format
+msgid "previous rebase directory $dotest still exists but mbox given."
+msgstr ""
+
+#: git-am.sh:486
+msgid "Please make up your mind. --skip or --abort?"
+msgstr ""
+
+#: git-am.sh:513
+msgid "Resolve operation not in progress, we are not resuming."
+msgstr ""
+
+#: git-am.sh:579
+#, sh-format
+msgid "Dirty index: cannot apply patches (dirty: $files)"
+msgstr ""
+
+#: git-am.sh:671
+#, sh-format
+msgid ""
+"Patch is empty.  Was it split wrong?\n"
+"If you would prefer to skip this patch, instead run \"$cmdline --skip\".\n"
+"To restore the original branch and stop patching run \"$cmdline --abort\"."
+msgstr ""
+
+#: git-am.sh:708
+msgid "Patch does not have a valid e-mail address."
+msgstr ""
+
+#: git-am.sh:755
+msgid "cannot be interactive without stdin connected to a terminal."
+msgstr ""
+
+#: git-am.sh:759
+msgid "Commit Body is:"
+msgstr ""
+
+#. TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
+#. in your translation. The program will only accept English
+#. input at this point.
+#: git-am.sh:766
+msgid "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
+msgstr ""
+
+#: git-am.sh:802
+#, sh-format
+msgid "Applying: $FIRSTLINE"
+msgstr ""
+
+#: git-am.sh:823
+msgid ""
+"No changes - did you forget to use 'git add'?\n"
+"If there is nothing left to stage, chances are that something else\n"
+"already introduced the same changes; you might want to skip this patch."
+msgstr ""
+
+#: git-am.sh:831
+msgid ""
+"You still have unmerged paths in your index\n"
+"did you forget to use 'git add'?"
+msgstr ""
+
+#: git-am.sh:847
+msgid "No changes -- Patch already applied."
+msgstr ""
+
+#: git-am.sh:857
+#, sh-format
+msgid "Patch failed at $msgnum $FIRSTLINE"
+msgstr ""
+
+#: git-am.sh:873
+msgid "applying to an empty history"
+msgstr ""
+
+#: git-bisect.sh:48
+msgid "You need to start by \"git bisect start\""
+msgstr ""
+
+#. TRANSLATORS: Make sure to include [Y] and [n] in your
+#. translation. The program will only accept English input
+#. at this point.
+#: git-bisect.sh:54
+msgid "Do you want me to do it for you [Y/n]? "
+msgstr ""
+
+#: git-bisect.sh:95
+#, sh-format
+msgid "unrecognised option: '$arg'"
+msgstr ""
+
+#: git-bisect.sh:99
+#, sh-format
+msgid "'$arg' does not appear to be a valid revision"
+msgstr ""
+
+#: git-bisect.sh:117
+msgid "Bad HEAD - I need a HEAD"
+msgstr ""
+
+#: git-bisect.sh:130
+#, sh-format
+msgid ""
+"Checking out '$start_head' failed. Try 'git bisect reset <validbranch>'."
+msgstr ""
+
+#: git-bisect.sh:140
+msgid "won't bisect on seeked tree"
+msgstr ""
+
+#: git-bisect.sh:144
+msgid "Bad HEAD - strange symbolic ref"
+msgstr ""
+
+#: git-bisect.sh:189
+#, sh-format
+msgid "Bad bisect_write argument: $state"
+msgstr ""
+
+#: git-bisect.sh:218
+#, sh-format
+msgid "Bad rev input: $arg"
+msgstr ""
+
+#: git-bisect.sh:232
+msgid "Please call 'bisect_state' with at least one argument."
+msgstr ""
+
+#: git-bisect.sh:244
+#, sh-format
+msgid "Bad rev input: $rev"
+msgstr ""
+
+#: git-bisect.sh:250
+msgid "'git bisect bad' can take only one argument."
+msgstr ""
+
+#. have bad but not good.  we could bisect although
+#. this is less optimum.
+#: git-bisect.sh:273
+msgid "Warning: bisecting only with a bad commit."
+msgstr ""
+
+#. TRANSLATORS: Make sure to include [Y] and [n] in your
+#. translation. The program will only accept English input
+#. at this point.
+#: git-bisect.sh:279
+msgid "Are you sure [Y/n]? "
+msgstr ""
+
+#: git-bisect.sh:289
+msgid ""
+"You need to give me at least one good and one bad revisions.\n"
+"(You can use \"git bisect bad\" and \"git bisect good\" for that.)"
+msgstr ""
+
+#: git-bisect.sh:292
+msgid ""
+"You need to start by \"git bisect start\".\n"
+"You then need to give me at least one good and one bad revisions.\n"
+"(You can use \"git bisect bad\" and \"git bisect good\" for that.)"
+msgstr ""
+
+#: git-bisect.sh:347 git-bisect.sh:474
+msgid "We are not bisecting."
+msgstr ""
+
+#: git-bisect.sh:354
+#, sh-format
+msgid "'$invalid' is not a valid commit"
+msgstr ""
+
+#: git-bisect.sh:363
+#, sh-format
+msgid ""
+"Could not check out original HEAD '$branch'.\n"
+"Try 'git bisect reset <commit>'."
+msgstr ""
+
+#: git-bisect.sh:390
+msgid "No logfile given"
+msgstr ""
+
+#: git-bisect.sh:391
+#, sh-format
+msgid "cannot read $file for replaying"
+msgstr ""
+
+#: git-bisect.sh:408
+msgid "?? what are you talking about?"
+msgstr ""
+
+#: git-bisect.sh:420
+#, sh-format
+msgid "running $command"
+msgstr ""
+
+#: git-bisect.sh:427
+#, sh-format
+msgid ""
+"bisect run failed:\n"
+"exit code $res from '$command' is < 0 or >= 128"
+msgstr ""
+
+#: git-bisect.sh:453
+msgid "bisect run cannot continue any more"
+msgstr ""
+
+#: git-bisect.sh:459
+#, sh-format
+msgid ""
+"bisect run failed:\n"
+"'bisect_state $state' exited with error code $res"
+msgstr ""
+
+#: git-bisect.sh:466
+msgid "bisect run success"
+msgstr ""
+
+#: git-pull.sh:21
+msgid ""
+"Pull is not possible because you have unmerged files.\n"
+"Please, fix them up in the work tree, and then use 'git add/rm <file>'\n"
+"as appropriate to mark resolution, or use 'git commit -a'."
+msgstr ""
+
+#: git-pull.sh:25
+msgid "Pull is not possible because you have unmerged files."
+msgstr ""
+
+#: git-pull.sh:197
+msgid "updating an unborn branch with changes added to the index"
+msgstr ""
+
+#. The fetch involved updating the current branch.
+#. The working tree and the index file is still based on the
+#. $orig_head commit, but we are merging into $curr_head.
+#. First update the working tree to match $curr_head.
+#: git-pull.sh:228
+#, sh-format
+msgid ""
+"Warning: fetch updated the current branch head.\n"
+"Warning: fast-forwarding your working tree from\n"
+"Warning: commit $orig_head."
+msgstr ""
+
+#: git-pull.sh:253
+msgid "Cannot merge multiple branches into empty head"
+msgstr ""
+
+#: git-pull.sh:257
+msgid "Cannot rebase onto multiple branches"
+msgstr ""
+
+#: git-stash.sh:51
+msgid "git stash clear with parameters is unimplemented"
+msgstr ""
+
+#: git-stash.sh:74
+msgid "You do not have the initial commit yet"
+msgstr ""
+
+#: git-stash.sh:89
+msgid "Cannot save the current index state"
+msgstr ""
+
+#: git-stash.sh:123 git-stash.sh:136
+msgid "Cannot save the current worktree state"
+msgstr ""
+
+#: git-stash.sh:140
+msgid "No changes selected"
+msgstr ""
+
+#: git-stash.sh:143
+msgid "Cannot remove temporary index (can't happen)"
+msgstr ""
+
+#: git-stash.sh:156
+msgid "Cannot record working tree state"
+msgstr ""
+
+#. TRANSLATORS: $option is an invalid option, like
+#. `--blah-blah'. The 7 spaces at the beginning of the
+#. second line correspond to "error: ". So you should line
+#. up the second line with however many characters the
+#. translation of "error: " takes in your language. E.g. in
+#. English this is:
+#.
+#. $ git stash save --blah-blah 2>&1 | head -n 2
+#. error: unknown option for 'stash save': --blah-blah
+#. To provide a message, use git stash save -- '--blah-blah'
+#: git-stash.sh:202
+#, sh-format
+msgid ""
+"error: unknown option for 'stash save': $option\n"
+"       To provide a message, use git stash save -- '$option'"
+msgstr ""
+
+#: git-stash.sh:223
+msgid "No local changes to save"
+msgstr ""
+
+#: git-stash.sh:227
+msgid "Cannot initialize stash"
+msgstr ""
+
+#: git-stash.sh:235
+msgid "Cannot save the current status"
+msgstr ""
+
+#: git-stash.sh:253
+msgid "Cannot remove worktree changes"
+msgstr ""
+
+#: git-stash.sh:352
+msgid "No stash found."
+msgstr ""
+
+#: git-stash.sh:359
+#, sh-format
+msgid "Too many revisions specified: $REV"
+msgstr ""
+
+#: git-stash.sh:365
+#, sh-format
+msgid "$reference is not valid reference"
+msgstr ""
+
+#: git-stash.sh:393
+#, sh-format
+msgid "'$args' is not a stash-like commit"
+msgstr ""
+
+#: git-stash.sh:404
+#, sh-format
+msgid "'$args' is not a stash reference"
+msgstr ""
+
+#: git-stash.sh:412
+msgid "unable to refresh index"
+msgstr ""
+
+#: git-stash.sh:416
+msgid "Cannot apply a stash in the middle of a merge"
+msgstr ""
+
+#: git-stash.sh:424
+msgid "Conflicts in index. Try without --index."
+msgstr ""
+
+#: git-stash.sh:426
+msgid "Could not save index tree"
+msgstr ""
+
+#: git-stash.sh:460
+msgid "Cannot unstage modified files"
+msgstr ""
+
+#: git-stash.sh:474
+msgid "Index was not unstashed."
+msgstr ""
+
+#: git-stash.sh:491
+#, sh-format
+msgid "Dropped ${REV} ($s)"
+msgstr ""
+
+#: git-stash.sh:492
+#, sh-format
+msgid "${REV}: Could not drop stash entry"
+msgstr ""
+
+#: git-stash.sh:499
+msgid "No branch name specified"
+msgstr ""
+
+#: git-stash.sh:570
+msgid "(To restore them type \"git stash apply\")"
+msgstr ""
+
+#: git-submodule.sh:88
+#, sh-format
+msgid "cannot strip one component off url '$remoteurl'"
+msgstr ""
+
+#: git-submodule.sh:145
+#, sh-format
+msgid "No submodule mapping found in .gitmodules for path '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:186
+#, sh-format
+msgid "Clone of '$url' into submodule path '$sm_path' failed"
+msgstr ""
+
+#: git-submodule.sh:196
+#, sh-format
+msgid "Gitdir '$a' is part of the submodule path '$b' or vice versa"
+msgstr ""
+
+#: git-submodule.sh:285
+#, sh-format
+msgid "repo URL: '$repo' must be absolute or begin with ./|../"
+msgstr ""
+
+#: git-submodule.sh:302
+#, sh-format
+msgid "'$sm_path' already exists in the index"
+msgstr ""
+
+#: git-submodule.sh:306
+#, sh-format
+msgid ""
+"The following path is ignored by one of your .gitignore files:\n"
+"$sm_path\n"
+"Use -f if you really want to add it."
+msgstr ""
+
+#: git-submodule.sh:317
+#, sh-format
+msgid "Adding existing repo at '$sm_path' to the index"
+msgstr ""
+
+#: git-submodule.sh:319
+#, sh-format
+msgid "'$sm_path' already exists and is not a valid git repo"
+msgstr ""
+
+#: git-submodule.sh:333
+#, sh-format
+msgid "Unable to checkout submodule '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:338
+#, sh-format
+msgid "Failed to add submodule '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:343
+#, sh-format
+msgid "Failed to register submodule '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:385
+#, sh-format
+msgid "Entering '$prefix$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:399
+#, sh-format
+msgid "Stopping at '$sm_path'; script returned non-zero status."
+msgstr ""
+
+#: git-submodule.sh:442
+#, sh-format
+msgid "No url found for submodule path '$sm_path' in .gitmodules"
+msgstr ""
+
+#: git-submodule.sh:451
+#, sh-format
+msgid "Failed to register url for submodule path '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:453
+#, sh-format
+msgid "Submodule '$name' ($url) registered for path '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:461
+#, sh-format
+msgid "Failed to register update mode for submodule path '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:560
+#, sh-format
+msgid ""
+"Submodule path '$sm_path' not initialized\n"
+"Maybe you want to use 'update --init'?"
+msgstr ""
+
+#: git-submodule.sh:573
+#, sh-format
+msgid "Unable to find current revision in submodule path '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:592
+#, sh-format
+msgid "Unable to fetch in submodule path '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:606
+#, sh-format
+msgid "Unable to rebase '$sha1' in submodule path '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:607
+#, sh-format
+msgid "Submodule path '$sm_path': rebased into '$sha1'"
+msgstr ""
+
+#: git-submodule.sh:612
+#, sh-format
+msgid "Unable to merge '$sha1' in submodule path '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:613
+#, sh-format
+msgid "Submodule path '$sm_path': merged in '$sha1'"
+msgstr ""
+
+#: git-submodule.sh:618
+#, sh-format
+msgid "Unable to checkout '$sha1' in submodule path '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:619
+#, sh-format
+msgid "Submodule path '$sm_path': checked out '$sha1'"
+msgstr ""
+
+#: git-submodule.sh:641 git-submodule.sh:964
+#, sh-format
+msgid "Failed to recurse into submodule path '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:749
+msgid "--cached cannot be used with --files"
+msgstr ""
+
+#. unexpected type
+#: git-submodule.sh:789
+#, sh-format
+msgid "unexpected mode $mod_dst"
+msgstr ""
+
+#: git-submodule.sh:807
+#, sh-format
+msgid "  Warn: $name doesn't contain commit $sha1_src"
+msgstr ""
+
+#: git-submodule.sh:810
+#, sh-format
+msgid "  Warn: $name doesn't contain commit $sha1_dst"
+msgstr ""
+
+#: git-submodule.sh:813
+#, sh-format
+msgid "  Warn: $name doesn't contain commits $sha1_src and $sha1_dst"
+msgstr ""
+
+#: git-submodule.sh:838
+msgid "blob"
+msgstr ""
+
+#: git-submodule.sh:839
+msgid "submodule"
+msgstr ""
+
+#: git-submodule.sh:876
+msgid "# Submodules changed but not updated:"
+msgstr ""
+
+#: git-submodule.sh:878
+msgid "# Submodule changes to be committed:"
+msgstr ""
+
+#: git-submodule.sh:1022
+#, sh-format
+msgid "Synchronizing submodule url for '$name'"
+msgstr ""
diff --git a/po/is.po b/po/is.po
new file mode 100644
index 0000000..8692a8b
--- /dev/null
+++ b/po/is.po
@@ -0,0 +1,93 @@
+# Icelandic translations for Git.
+# Copyright (C) 2010 Ævar Arnfjörð Bjarmason <avarab@gmail.com>
+# This file is distributed under the same license as the Git package.
+# Ævar Arnfjörð Bjarmason <avarab@gmail.com>, 2010.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Git\n"
+"Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
+"POT-Creation-Date: 2010-09-20 14:44+0000\n"
+"PO-Revision-Date: 2010-06-05 19:06 +0000\n"
+"Last-Translator: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n"
+"Language-Team: Git Mailing List <git@vger.kernel.org>\n"
+"Language: is\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#. TRANSLATORS: This is a test. You don't need to translate it.
+#: t/t0200/test.c:5
+msgid "See 'git help COMMAND' for more information on a specific command."
+msgstr "Sjá 'git help SKIPUN' til að sjá hjálp fyrir tiltekna skipun."
+
+#. TRANSLATORS: This is a test. You don't need to translate it.
+#: t/t0200/test.c:10
+msgid "TEST: A C test string"
+msgstr "TILRAUN: C tilraunastrengur"
+
+#. TRANSLATORS: This is a test. You don't need to translate it.
+#: t/t0200/test.c:13
+#, c-format
+msgid "TEST: A C test string %s"
+msgstr "TILRAUN: C tilraunastrengur %s"
+
+#. TRANSLATORS: This is a test. You don't need to translate it.
+#: t/t0200/test.c:16
+#, c-format
+msgid "TEST: Hello World!"
+msgstr "TILRAUN: Halló Heimur!"
+
+#. TRANSLATORS: This is a test. You don't need to translate it.
+#: t/t0200/test.c:19
+#, c-format
+msgid "TEST: Old English Runes"
+msgstr "TILRAUN: ᚻᛖ ᚳᚹᚫᚦ ᚦᚫᛏ ᚻᛖ ᛒᚢᛞᛖ ᚩᚾ ᚦᚫᛗ ᛚᚪᚾᛞᛖ ᚾᚩᚱᚦᚹᛖᚪᚱᛞᚢᛗ ᚹᛁᚦ ᚦᚪ ᚹᛖᛥᚫ"
+
+#. TRANSLATORS: This is a test. You don't need to translate it.
+#: t/t0200/test.c:22
+#, c-format
+msgid "TEST: ‘single’ and “double” quotes"
+msgstr "TILRAUN: ‚einfaldar‘ og „tvöfaldar“ gæsalappir"
+
+#. TRANSLATORS: This is a test. You don't need to translate it.
+#: t/t0200/test.sh:8
+msgid "TEST: A Shell test string"
+msgstr "TILRAUN: Skeljartilraunastrengur"
+
+#. TRANSLATORS: This is a test. You don't need to translate it.
+#: t/t0200/test.sh:11
+#, sh-format
+msgid "TEST: A Shell test $variable"
+msgstr "TILRAUN: Skeljartilraunastrengur með breytunni $variable"
+
+#. TRANSLATORS: This is a test. You don't need to translate it.
+#: t/t0200/test.perl:8
+msgid "TEST: A Perl test string"
+msgstr "TILRAUN: Perl tilraunastrengur"
+
+#. TRANSLATORS: This is a test. You don't need to translate it.
+#: t/t0200/test.perl:11
+#, perl-format
+msgid "TEST: A Perl test variable %s"
+msgstr "TILRAUN: Perl tilraunastrengur með breytunni %s"
+
+#. TRANSLATORS: The first '%s' is either "Reinitialized
+#. existing" or "Initialized empty", the second " shared" or
+#. "", and the last '%s%s' is the verbatim directory name.
+#: builtin/init-db.c:355
+#, c-format
+msgid "%s%s Git repository in %s%s\n"
+msgstr "%s%s Git lind í %s%s\n"
+
+#: builtin/init-db.c:356
+msgid "Reinitialized existing"
+msgstr "Endurgerði"
+
+#: builtin/init-db.c:356
+msgid "Initialized empty"
+msgstr "Bjó til tóma"
+
+#: builtin/init-db.c:357
+msgid " shared"
+msgstr " sameiginlega"
diff --git a/po/it.po b/po/it.po
new file mode 100644
index 0000000..fe61f1a
--- /dev/null
+++ b/po/it.po
@@ -0,0 +1,5367 @@
+# Italian translations for Git.
+# Copyright (C) 2012 Marco Paolone <marcopaolone@gmail.com>
+# This file is distributed under the same license as the Git package.
+msgid ""
+msgstr ""
+"Project-Id-Version: Git\n"
+"Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
+"POT-Creation-Date: 2012-06-08 10:20+0800\n"
+"PO-Revision-Date: 2012-06-14 14:13+0200\n"
+"Last-Translator: Marco Paolone <marcopaolone@gmail.com>\n"
+"Language-Team: Italian\n"
+"Language: it\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: advice.c:40
+#, c-format
+msgid "hint: %.*s\n"
+msgstr "suggerimento: %.*s\n"
+
+#.
+#. * Message used both when 'git commit' fails and when
+#. * other commands doing a merge do.
+#.
+#: advice.c:70
+msgid ""
+"Fix them up in the work tree,\n"
+"and then use 'git add/rm <file>' as\n"
+"appropriate to mark resolution and make a commit,\n"
+"or use 'git commit -a'."
+msgstr ""
+
+#: bundle.c:36
+#, c-format
+msgid "'%s' does not look like a v2 bundle file"
+msgstr ""
+
+#: bundle.c:63
+#, c-format
+msgid "unrecognized header: %s%s (%d)"
+msgstr "header non riconosciuto: %s%s (%d)"
+
+#: bundle.c:89 builtin/commit.c:696
+#, c-format
+msgid "could not open '%s'"
+msgstr "non è stato possibile aprire '%s'"
+
+#: bundle.c:140
+msgid "Repository lacks these prerequisite commits:"
+msgstr ""
+
+#: bundle.c:164 sequencer.c:550 sequencer.c:982 builtin/log.c:289
+#: builtin/log.c:720 builtin/log.c:1309 builtin/log.c:1528 builtin/merge.c:347
+#: builtin/shortlog.c:181
+msgid "revision walk setup failed"
+msgstr ""
+
+#: bundle.c:186
+#, c-format
+msgid "The bundle contains %d ref"
+msgid_plural "The bundle contains %d refs"
+msgstr[0] ""
+msgstr[1] ""
+
+#: bundle.c:192
+#, c-format
+msgid "The bundle requires this ref"
+msgid_plural "The bundle requires these %d refs"
+msgstr[0] ""
+msgstr[1] ""
+
+#: bundle.c:290
+msgid "rev-list died"
+msgstr ""
+
+#: bundle.c:296 builtin/log.c:1205 builtin/shortlog.c:284
+#, c-format
+msgid "unrecognized argument: %s"
+msgstr "argomento non riconosciuto: %s"
+
+#: bundle.c:331
+#, c-format
+msgid "ref '%s' is excluded by the rev-list options"
+msgstr "il ref '%s' è escluso dalle opzioni di rev-list"
+
+#: bundle.c:376
+msgid "Refusing to create empty bundle."
+msgstr ""
+
+#: bundle.c:394
+msgid "Could not spawn pack-objects"
+msgstr ""
+
+#: bundle.c:412
+msgid "pack-objects died"
+msgstr ""
+
+#: bundle.c:415
+#, c-format
+msgid "cannot create '%s'"
+msgstr "impossibile creare '%s'"
+
+#: bundle.c:437
+msgid "index-pack died"
+msgstr ""
+
+#: commit.c:48
+#, c-format
+msgid "could not parse %s"
+msgstr "non è stato possibile analizzare %s"
+
+#: commit.c:50
+#, c-format
+msgid "%s %s is not a commit!"
+msgstr "%s %s non è un commit!"
+
+#: compat/obstack.c:406 compat/obstack.c:408
+msgid "memory exhausted"
+msgstr "memoria esaurita"
+
+#: connected.c:39
+msgid "Could not run 'git rev-list'"
+msgstr "Non è stato possibile eseguire 'git-rev-list'"
+
+#: connected.c:48
+#, c-format
+msgid "failed write to rev-list: %s"
+msgstr "scrittura nella rev-list non riuscita: %s"
+
+#: connected.c:56
+#, c-format
+msgid "failed to close rev-list's stdin: %s"
+msgstr ""
+
+#: date.c:95
+msgid "in the future"
+msgstr "in futuro"
+
+#: date.c:101
+#, c-format
+msgid "%lu second ago"
+msgid_plural "%lu seconds ago"
+msgstr[0] "%lu secondo fa"
+msgstr[1] "%lu secondi fa"
+
+#: date.c:108
+#, c-format
+msgid "%lu minute ago"
+msgid_plural "%lu minutes ago"
+msgstr[0] "%lu un minuto fa"
+msgstr[1] "%lu minuti fa"
+
+#: date.c:115
+#, c-format
+msgid "%lu hour ago"
+msgid_plural "%lu hours ago"
+msgstr[0] "%lu ora fa"
+msgstr[1] "%lu ore fa"
+
+#: date.c:122
+#, c-format
+msgid "%lu day ago"
+msgid_plural "%lu days ago"
+msgstr[0] "%lu giorno fa"
+msgstr[1] "%lu giorni fa"
+
+#: date.c:128
+#, c-format
+msgid "%lu week ago"
+msgid_plural "%lu weeks ago"
+msgstr[0] "%lu settimana fa"
+msgstr[1] "%lu settimane fa"
+
+#: date.c:135
+#, c-format
+msgid "%lu month ago"
+msgid_plural "%lu months ago"
+msgstr[0] "%lu mese fa"
+msgstr[1] "%lu mesi fa"
+
+#: date.c:146
+#, c-format
+msgid "%lu year"
+msgid_plural "%lu years"
+msgstr[0] "%lu anno"
+msgstr[1] "%lu anni"
+
+#: date.c:149
+#, c-format
+msgid "%s, %lu month ago"
+msgid_plural "%s, %lu months ago"
+msgstr[0] "%s, %lu mese fa"
+msgstr[1] "%s, %lu mesi fa"
+
+#: date.c:154 date.c:159
+#, c-format
+msgid "%lu year ago"
+msgid_plural "%lu years ago"
+msgstr[0] "%lu anno fa"
+msgstr[1] "%lu anni fa"
+
+#: diff.c:105
+#, c-format
+msgid "  Failed to parse dirstat cut-off percentage '%.*s'\n"
+msgstr ""
+
+#: diff.c:110
+#, c-format
+msgid "  Unknown dirstat parameter '%.*s'\n"
+msgstr "  Parametro dirstat '%.*s' sconosciuto\n"
+
+#: diff.c:210
+#, c-format
+msgid ""
+"Found errors in 'diff.dirstat' config variable:\n"
+"%s"
+msgstr ""
+"Trovati errori nella variabile di configurazione 'diff.dirstat':\n"
+"%s"
+
+#: diff.c:1400
+msgid " 0 files changed\n"
+msgstr " 0 file modificati\n"
+
+#: diff.c:1404
+#, c-format
+msgid " %d file changed"
+msgid_plural " %d files changed"
+msgstr[0] " %d file modificato"
+msgstr[1] " %d file modificati"
+
+#: diff.c:1421
+#, c-format
+msgid ", %d insertion(+)"
+msgid_plural ", %d insertions(+)"
+msgstr[0] ", %d inserzione(+)"
+msgstr[1] ", %d inserzioni(+)"
+
+#: diff.c:1432
+#, c-format
+msgid ", %d deletion(-)"
+msgid_plural ", %d deletions(-)"
+msgstr[0] ". %d rimozione(-)"
+msgstr[1] ", %d rimozioni(-)"
+
+#: diff.c:3478
+#, c-format
+msgid ""
+"Failed to parse --dirstat/-X option parameter:\n"
+"%s"
+msgstr ""
+"Analisi del parametro dell'opzione --dirstat/-X non riuscita:\n"
+"%s"
+
+#: gpg-interface.c:59
+msgid "could not run gpg."
+msgstr "non è stato possibile eseguire gpg."
+
+#: gpg-interface.c:71
+msgid "gpg did not accept the data"
+msgstr "gpg non ha accettato i dati"
+
+#: gpg-interface.c:82
+msgid "gpg failed to sign the data"
+msgstr "gpg non è riuscito a firmare i dati"
+
+#: grep.c:1320
+#, c-format
+msgid "'%s': unable to read %s"
+msgstr "'%s': impossibile leggere %s"
+
+#: grep.c:1337
+#, c-format
+msgid "'%s': %s"
+msgstr "'%s': %s"
+
+#: grep.c:1348
+#, c-format
+msgid "'%s': short read %s"
+msgstr ""
+
+#: help.c:207
+#, c-format
+msgid "available git commands in '%s'"
+msgstr "comandi git disponibili in '%s'"
+
+#: help.c:214
+msgid "git commands available from elsewhere on your $PATH"
+msgstr "comandi git disponibili altrove nel tuo $PATH"
+
+#: help.c:270
+#, c-format
+msgid ""
+"'%s' appears to be a git command, but we were not\n"
+"able to execute it. Maybe git-%s is broken?"
+msgstr ""
+"'%s' sembra essere un comando git, ma non è stato\n"
+"possibile eseguirlo. Forse git-%s è corrotto?"
+
+#: help.c:327
+msgid "Uh oh. Your system reports no Git commands at all."
+msgstr "Oh oh. Il tuo sistema non riporta alcun comando Git."
+
+#: help.c:349
+#, c-format
+msgid ""
+"WARNING: You called a Git command named '%s', which does not exist.\n"
+"Continuing under the assumption that you meant '%s'"
+msgstr ""
+
+#: help.c:354
+#, c-format
+msgid "in %0.1f seconds automatically..."
+msgstr "automaticamente tra %0.1f secondi..."
+
+#: help.c:361
+#, c-format
+msgid "git: '%s' is not a git command. See 'git --help'."
+msgstr "git: '%s' non è un comando git. Vedi 'git --help'."
+
+#: help.c:365
+msgid ""
+"\n"
+"Did you mean this?"
+msgid_plural ""
+"\n"
+"Did you mean one of these?"
+msgstr[0] ""
+"\n"
+"Intendevi questo?"
+msgstr[1] ""
+"\n"
+"Intendevi uno di questi?"
+
+#: parse-options.c:493
+msgid "..."
+msgstr "..."
+
+#: parse-options.c:511
+#, c-format
+msgid "usage: %s"
+msgstr "   uso: %s"
+
+#. TRANSLATORS: the colon here should align with the
+#. one in "usage: %s" translation
+#: parse-options.c:515
+#, c-format
+msgid "   or: %s"
+msgstr "oppure: %s"
+
+#: parse-options.c:518
+#, c-format
+msgid "    %s"
+msgstr "    %s"
+
+#: remote.c:1629
+#, c-format
+msgid "Your branch is ahead of '%s' by %d commit.\n"
+msgid_plural "Your branch is ahead of '%s' by %d commits.\n"
+msgstr[0] "Il tuo branch è avanti rispetto a '%s' di %d commit.\n"
+msgstr[1] "Il tuo branch è avanti rispetto a '%s' di %d commit.\n"
+
+#: remote.c:1635
+#, c-format
+msgid "Your branch is behind '%s' by %d commit, and can be fast-forwarded.\n"
+msgid_plural ""
+"Your branch is behind '%s' by %d commits, and can be fast-forwarded.\n"
+msgstr[0] ""
+msgstr[1] ""
+
+#: remote.c:1643
+#, c-format
+msgid ""
+"Your branch and '%s' have diverged,\n"
+"and have %d and %d different commit each, respectively.\n"
+msgid_plural ""
+"Your branch and '%s' have diverged,\n"
+"and have %d and %d different commits each, respectively.\n"
+msgstr[0] ""
+msgstr[1] ""
+
+#: sequencer.c:121 builtin/merge.c:865 builtin/merge.c:978
+#: builtin/merge.c:1088 builtin/merge.c:1098
+#, c-format
+msgid "Could not open '%s' for writing"
+msgstr "Non è stato possibile aprire '%s' per la scrittura"
+
+#: sequencer.c:123 builtin/merge.c:333 builtin/merge.c:868
+#: builtin/merge.c:1090 builtin/merge.c:1103
+#, c-format
+msgid "Could not write to '%s'"
+msgstr "Non è stato possibile scrivere su '%s'"
+
+#: sequencer.c:144
+msgid ""
+"after resolving the conflicts, mark the corrected paths\n"
+"with 'git add <paths>' or 'git rm <paths>'"
+msgstr ""
+"dopo aver risolto i conflitti, segna i path corretti\n"
+"con 'git add <path>' o 'git rm <path>'"
+
+#: sequencer.c:147
+msgid ""
+"after resolving the conflicts, mark the corrected paths\n"
+"with 'git add <paths>' or 'git rm <paths>'\n"
+"and commit the result with 'git commit'"
+msgstr ""
+"dopo aver risolto i conflitti, segna i path corretti\n"
+"con 'git add <path>' o 'git rm <path>' ed eseguire\n"
+"il commit del risultato con 'git commit'"
+
+#: sequencer.c:160 sequencer.c:758 sequencer.c:841
+#, c-format
+msgid "Could not write to %s"
+msgstr "Non è stato possibile scrivere su %s"
+
+#: sequencer.c:163
+#, c-format
+msgid "Error wrapping up %s"
+msgstr ""
+
+#: sequencer.c:178
+msgid "Your local changes would be overwritten by cherry-pick."
+msgstr "Le tue modifiche locali verranno sovrascritte da cherry-pick"
+
+#: sequencer.c:180
+msgid "Your local changes would be overwritten by revert."
+msgstr "Le tue modifiche locali verranno sovrascritte da revert."
+
+#: sequencer.c:183
+msgid "Commit your changes or stash them to proceed."
+msgstr ""
+
+#. TRANSLATORS: %s will be "revert" or "cherry-pick"
+#: sequencer.c:233
+#, c-format
+msgid "%s: Unable to write new index file"
+msgstr "%s: impossibile scrivere il nuovo index file"
+
+#: sequencer.c:261
+msgid "Could not resolve HEAD commit\n"
+msgstr "Non è stato possibile risolvere il commit HEAD\n"
+
+#: sequencer.c:282
+msgid "Unable to update cache tree\n"
+msgstr ""
+
+#: sequencer.c:324
+#, c-format
+msgid "Could not parse commit %s\n"
+msgstr "Non è stato possibile analizzare il commit %s\n"
+
+#: sequencer.c:329
+#, c-format
+msgid "Could not parse parent commit %s\n"
+msgstr ""
+
+#: sequencer.c:395
+msgid "Your index file is unmerged."
+msgstr ""
+
+#: sequencer.c:398
+msgid "You do not have a valid HEAD"
+msgstr "Non hai un HEAD valido"
+
+#: sequencer.c:413
+#, c-format
+msgid "Commit %s is a merge but no -m option was given."
+msgstr "Il commit %s è un merge ma non è stata specificata l'opzione -m."
+
+#: sequencer.c:421
+#, c-format
+msgid "Commit %s does not have parent %d"
+msgstr ""
+
+#: sequencer.c:425
+#, c-format
+msgid "Mainline was specified but commit %s is not a merge."
+msgstr ""
+
+#. TRANSLATORS: The first %s will be "revert" or
+#. "cherry-pick", the second %s a SHA1
+#: sequencer.c:436
+#, c-format
+msgid "%s: cannot parse parent commit %s"
+msgstr ""
+
+#: sequencer.c:440
+#, c-format
+msgid "Cannot get commit message for %s"
+msgstr "Impossibile ottenere il messaggio di commit per %s"
+
+#: sequencer.c:524
+#, c-format
+msgid "could not revert %s... %s"
+msgstr "non è stato possibile eseguire il revert di %s... %s"
+
+#: sequencer.c:525
+#, c-format
+msgid "could not apply %s... %s"
+msgstr "non è stato possibile applicare %s... %s"
+
+#: sequencer.c:553
+msgid "empty commit set passed"
+msgstr "è stato passato un set di commit vuoto"
+
+#: sequencer.c:561
+#, c-format
+msgid "git %s: failed to read the index"
+msgstr "git %s: lettura di index non riuscita"
+
+#: sequencer.c:566
+#, c-format
+msgid "git %s: failed to refresh the index"
+msgstr "git %s: aggiornamento di index non riuscito"
+
+#: sequencer.c:624
+#, c-format
+msgid "Cannot %s during a %s"
+msgstr ""
+
+#: sequencer.c:646
+#, c-format
+msgid "Could not parse line %d."
+msgstr "Non è stato possibile analizzare la riga %d."
+
+#: sequencer.c:651
+msgid "No commits parsed."
+msgstr "Nessun commit analizzato."
+
+#: sequencer.c:664
+#, c-format
+msgid "Could not open %s"
+msgstr "Non è stato possibile aprire %s"
+
+#: sequencer.c:668
+#, c-format
+msgid "Could not read %s."
+msgstr "Non è stato possibile leggere %s."
+
+#: sequencer.c:675
+#, c-format
+msgid "Unusable instruction sheet: %s"
+msgstr ""
+
+#: sequencer.c:703
+#, c-format
+msgid "Invalid key: %s"
+msgstr "Chiave non valida: %s"
+
+#: sequencer.c:706
+#, c-format
+msgid "Invalid value for %s: %s"
+msgstr "Valore non valido per %s: %s"
+
+#: sequencer.c:718
+#, c-format
+msgid "Malformed options sheet: %s"
+msgstr ""
+
+#: sequencer.c:739
+msgid "a cherry-pick or revert is already in progress"
+msgstr "è già in corso un'operazione di cherry-pick o di revert"
+
+#: sequencer.c:740
+msgid "try \"git cherry-pick (--continue | --quit | --abort)\""
+msgstr "prova \"git cherry-pick (--continue | --quit | -- abort)\""
+
+#: sequencer.c:744
+#, c-format
+msgid "Could not create sequencer directory %s"
+msgstr ""
+
+#: sequencer.c:760 sequencer.c:845
+#, c-format
+msgid "Error wrapping up %s."
+msgstr ""
+
+#: sequencer.c:779 sequencer.c:913
+msgid "no cherry-pick or revert in progress"
+msgstr "nessuna operazione di cherry-pick o revert in corso"
+
+#: sequencer.c:781
+msgid "cannot resolve HEAD"
+msgstr "impossibile risolvere HEAD"
+
+#: sequencer.c:783
+msgid "cannot abort from a branch yet to be born"
+msgstr ""
+
+#: sequencer.c:805 builtin/apply.c:3697
+#, c-format
+msgid "cannot open %s: %s"
+msgstr "impossibile aprire %s: %s"
+
+#: sequencer.c:808
+#, c-format
+msgid "cannot read %s: %s"
+msgstr "impossibile leggere %s: %s"
+
+#: sequencer.c:809
+msgid "unexpected end of file"
+msgstr "fine del file inattesa"
+
+#: sequencer.c:815
+#, c-format
+msgid "stored pre-cherry-pick HEAD file '%s' is corrupt"
+msgstr ""
+
+#: sequencer.c:838
+#, c-format
+msgid "Could not format %s."
+msgstr ""
+
+#: sequencer.c:1000
+msgid "Can't revert as initial commit"
+msgstr "Impossibile eseguire il revert come commit iniziale"
+
+#: sequencer.c:1001
+msgid "Can't cherry-pick into empty head"
+msgstr ""
+
+#: sha1_name.c:864
+msgid "HEAD does not point to a branch"
+msgstr "HEAD non punta ad un branch"
+
+#: sha1_name.c:867
+#, c-format
+msgid "No such branch: '%s'"
+msgstr "Nessun branch esistente: '%s'"
+
+#: sha1_name.c:869
+#, c-format
+msgid "No upstream configured for branch '%s'"
+msgstr "Nessun upstream configurato per il branch '%s'"
+
+#: sha1_name.c:872
+#, c-format
+msgid "Upstream branch '%s' not stored as a remote-tracking branch"
+msgstr ""
+
+#: wrapper.c:413
+#, c-format
+msgid "unable to look up current user in the passwd file: %s"
+msgstr "impossibile trovare l'utente corrente nel file passwd: %s"
+
+#: wrapper.c:414
+msgid "no such user"
+msgstr "utente non esistente"
+
+#: wt-status.c:135
+msgid "Unmerged paths:"
+msgstr ""
+
+#: wt-status.c:141 wt-status.c:158
+#, c-format
+msgid "  (use \"git reset %s <file>...\" to unstage)"
+msgstr ""
+
+#: wt-status.c:143 wt-status.c:160
+msgid "  (use \"git rm --cached <file>...\" to unstage)"
+msgstr ""
+
+#: wt-status.c:144
+msgid "  (use \"git add/rm <file>...\" as appropriate to mark resolution)"
+msgstr "  (usa \"git add/rm <file>...\" come appropriato per la risoluzione)"
+
+#: wt-status.c:152
+msgid "Changes to be committed:"
+msgstr ""
+
+#: wt-status.c:170
+msgid "Changes not staged for commit:"
+msgstr ""
+
+#: wt-status.c:174
+msgid "  (use \"git add <file>...\" to update what will be committed)"
+msgstr ""
+
+#: wt-status.c:176
+msgid "  (use \"git add/rm <file>...\" to update what will be committed)"
+msgstr ""
+
+#: wt-status.c:177
+msgid ""
+"  (use \"git checkout -- <file>...\" to discard changes in working directory)"
+msgstr ""
+
+#: wt-status.c:179
+msgid "  (commit or discard the untracked or modified content in submodules)"
+msgstr ""
+
+#: wt-status.c:188
+#, c-format
+msgid "%s files:"
+msgstr "%s file:"
+
+#: wt-status.c:191
+#, c-format
+msgid "  (use \"git %s <file>...\" to include in what will be committed)"
+msgstr ""
+
+#: wt-status.c:208
+msgid "bug"
+msgstr "bug"
+
+#: wt-status.c:213
+msgid "both deleted:"
+msgstr "entrambi eliminati:"
+
+#: wt-status.c:214
+msgid "added by us:"
+msgstr "aggiunto da noi:"
+
+#: wt-status.c:215
+msgid "deleted by them:"
+msgstr "eliminato da loro:"
+
+#: wt-status.c:216
+msgid "added by them:"
+msgstr "aggiunto da loro:"
+
+#: wt-status.c:217
+msgid "deleted by us:"
+msgstr "eliminato da noi:"
+
+#: wt-status.c:218
+msgid "both added:"
+msgstr "entrambi aggiunti:"
+
+#: wt-status.c:219
+msgid "both modified:"
+msgstr "entrambi modificati:"
+
+#: wt-status.c:249
+msgid "new commits, "
+msgstr "nuovi commit, "
+
+#: wt-status.c:251
+msgid "modified content, "
+msgstr "contenuto modificato, "
+
+#: wt-status.c:253
+msgid "untracked content, "
+msgstr ""
+
+#: wt-status.c:267
+#, c-format
+msgid "new file:   %s"
+msgstr "nuovo file:   %s"
+
+#: wt-status.c:270
+#, c-format
+msgid "copied:     %s -> %s"
+msgstr "copiato:     %s -> %s"
+
+#: wt-status.c:273
+#, c-format
+msgid "deleted:    %s"
+msgstr "eliminato:    %s"
+
+#: wt-status.c:276
+#, c-format
+msgid "modified:   %s"
+msgstr "modificato:   %s"
+
+#: wt-status.c:279
+#, c-format
+msgid "renamed:    %s -> %s"
+msgstr "rinominato:    %s -> %s"
+
+#: wt-status.c:282
+#, c-format
+msgid "typechange: %s"
+msgstr ""
+
+#: wt-status.c:285
+#, c-format
+msgid "unknown:    %s"
+msgstr "sconosciuto:    %s"
+
+#: wt-status.c:288
+#, c-format
+msgid "unmerged:   %s"
+msgstr ""
+
+#: wt-status.c:291
+#, c-format
+msgid "bug: unhandled diff status %c"
+msgstr ""
+
+#: wt-status.c:737
+msgid "On branch "
+msgstr "Sul branch "
+
+#: wt-status.c:744
+msgid "Not currently on any branch."
+msgstr ""
+
+#: wt-status.c:755
+msgid "Initial commit"
+msgstr "Commit iniziale"
+
+#: wt-status.c:769
+msgid "Untracked"
+msgstr ""
+
+#: wt-status.c:771
+msgid "Ignored"
+msgstr "Ignorato"
+
+#: wt-status.c:773
+#, c-format
+msgid "Untracked files not listed%s"
+msgstr ""
+
+#: wt-status.c:775
+msgid " (use -u option to show untracked files)"
+msgstr ""
+
+#: wt-status.c:781
+msgid "No changes"
+msgstr "Nessuna modifica"
+
+#: wt-status.c:785
+#, c-format
+msgid "no changes added to commit%s\n"
+msgstr "nessuna modifica aggiunta al commit%s\n"
+
+#: wt-status.c:787
+msgid " (use \"git add\" and/or \"git commit -a\")"
+msgstr " (usa \"git add\" e/o \"git commit -a\")"
+
+#: wt-status.c:789
+#, c-format
+msgid "nothing added to commit but untracked files present%s\n"
+msgstr ""
+
+#: wt-status.c:791
+msgid " (use \"git add\" to track)"
+msgstr ""
+
+#: wt-status.c:793 wt-status.c:796 wt-status.c:799
+#, c-format
+msgid "nothing to commit%s\n"
+msgstr ""
+
+#: wt-status.c:794
+msgid " (create/copy files and use \"git add\" to track)"
+msgstr ""
+
+#: wt-status.c:797
+msgid " (use -u to show untracked files)"
+msgstr ""
+
+#: wt-status.c:800
+msgid " (working directory clean)"
+msgstr ""
+
+#: wt-status.c:908
+msgid "HEAD (no branch)"
+msgstr "HEAD (nessun branch)"
+
+#: wt-status.c:914
+msgid "Initial commit on "
+msgstr "Commit iniziale su "
+
+#: wt-status.c:929
+msgid "behind "
+msgstr "indietro "
+
+#: wt-status.c:932 wt-status.c:935
+msgid "ahead "
+msgstr "avanti "
+
+#: wt-status.c:937
+msgid ", behind "
+msgstr ", indietro "
+
+#: builtin/add.c:62
+#, c-format
+msgid "unexpected diff status %c"
+msgstr "status diff %c inatteso"
+
+#: builtin/add.c:67 builtin/commit.c:226
+msgid "updating files failed"
+msgstr "aggiornamento dei file non riuscito"
+
+#: builtin/add.c:77
+#, c-format
+msgid "remove '%s'\n"
+msgstr "elimina '%s'\n"
+
+#: builtin/add.c:176
+#, c-format
+msgid "Path '%s' is in submodule '%.*s'"
+msgstr "Il path '%s' è nel sottomodulo '%.*s'"
+
+#: builtin/add.c:192
+msgid "Unstaged changes after refreshing the index:"
+msgstr ""
+
+#: builtin/add.c:195 builtin/add.c:456 builtin/rm.c:186
+#, c-format
+msgid "pathspec '%s' did not match any files"
+msgstr ""
+
+#: builtin/add.c:209
+#, c-format
+msgid "'%s' is beyond a symbolic link"
+msgstr "'%s' si trova oltre un link simbolico"
+
+#: builtin/add.c:276
+msgid "Could not read the index"
+msgstr "Non è stato possibile leggere index"
+
+#: builtin/add.c:286
+#, c-format
+msgid "Could not open '%s' for writing."
+msgstr "Non è stato possibile aprire '%s' per la scrittura."
+
+#: builtin/add.c:290
+msgid "Could not write patch"
+msgstr "Non è stato possibile scrivere la patch"
+
+#: builtin/add.c:295
+#, c-format
+msgid "Could not stat '%s'"
+msgstr "Non è stato possibile eseguire lo stat di '%s'"
+
+#: builtin/add.c:297
+msgid "Empty patch. Aborted."
+msgstr "Patch vuota. Operazione interrotta."
+
+#: builtin/add.c:303
+#, c-format
+msgid "Could not apply '%s'"
+msgstr "Non è stato possibile applicare '%s'"
+
+#: builtin/add.c:312
+msgid "The following paths are ignored by one of your .gitignore files:\n"
+msgstr "I seguenti path sono stati ignorati da uno o più dei tuoi file .gitignore:\n"
+
+#: builtin/add.c:352
+#, c-format
+msgid "Use -f if you really want to add them.\n"
+msgstr "Usa -f se vuoi davvero aggiungerli.\n"
+
+#: builtin/add.c:353
+msgid "no files added"
+msgstr "nessun file aggiunto"
+
+#: builtin/add.c:359
+msgid "adding files failed"
+msgstr "aggiunta dei file non riuscita"
+
+#: builtin/add.c:391
+msgid "-A and -u are mutually incompatible"
+msgstr "-A e -u sono reciprocamente incompatibili"
+
+#: builtin/add.c:393
+msgid "Option --ignore-missing can only be used together with --dry-run"
+msgstr "L'opzione --ignore-missing può essere usata solo con --dry-run"
+
+#: builtin/add.c:413
+#, c-format
+msgid "Nothing specified, nothing added.\n"
+msgstr ""
+
+#: builtin/add.c:414
+#, c-format
+msgid "Maybe you wanted to say 'git add .'?\n"
+msgstr "Forse intendevi dire 'git add .'?\n"
+
+#: builtin/add.c:420 builtin/clean.c:95 builtin/commit.c:286 builtin/mv.c:82
+#: builtin/rm.c:162
+msgid "index file corrupt"
+msgstr "index file corrotto"
+
+#: builtin/add.c:476 builtin/apply.c:4108 builtin/mv.c:229 builtin/rm.c:260
+msgid "Unable to write new index file"
+msgstr "Impossibile scrivere il nuovo index file"
+
+#: builtin/apply.c:53
+msgid "git apply [options] [<patch>...]"
+msgstr "git apply [opzioni] [<patch>...]"
+
+#: builtin/apply.c:106
+#, c-format
+msgid "unrecognized whitespace option '%s'"
+msgstr ""
+
+#: builtin/apply.c:121
+#, c-format
+msgid "unrecognized whitespace ignore option '%s'"
+msgstr ""
+
+#: builtin/apply.c:815
+#, c-format
+msgid "Cannot prepare timestamp regexp %s"
+msgstr ""
+
+#: builtin/apply.c:824
+#, c-format
+msgid "regexec returned %d for input: %s"
+msgstr "regexec ha restituito %d per l'input: %s"
+
+#: builtin/apply.c:905
+#, c-format
+msgid "unable to find filename in patch at line %d"
+msgstr "impossibile trovare il nome del file nella patch alla riga %d"
+
+#: builtin/apply.c:937
+#, c-format
+msgid "git apply: bad git-diff - expected /dev/null, got %s on line %d"
+msgstr "git apply: git-diff errato - atteso /dev/null, ricevuto %s alla riga %d"
+
+#: builtin/apply.c:941
+#, c-format
+msgid "git apply: bad git-diff - inconsistent new filename on line %d"
+msgstr "git apply: git-diff errato - nuovo nome del file inconsistente alla riga %d"
+
+#: builtin/apply.c:942
+#, c-format
+msgid "git apply: bad git-diff - inconsistent old filename on line %d"
+msgstr "git apply: git-diff errato - vecchio nome del file inconsistente alla riga %d"
+
+#: builtin/apply.c:949
+#, c-format
+msgid "git apply: bad git-diff - expected /dev/null on line %d"
+msgstr "git apply: git-diff errato - atteso /dev/nulla alla riga %d"
+
+#: builtin/apply.c:1394
+#, c-format
+msgid "recount: unexpected line: %.*s"
+msgstr "recount: riga inattesa: %.*s"
+
+#: builtin/apply.c:1451
+#, c-format
+msgid "patch fragment without header at line %d: %.*s"
+msgstr "frammento di patch senza intestazione alla riga %d: %.*s"
+
+#: builtin/apply.c:1468
+#, c-format
+msgid ""
+"git diff header lacks filename information when removing %d leading pathname "
+"component (line %d)"
+msgid_plural ""
+"git diff header lacks filename information when removing %d leading pathname "
+"components (line %d)"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/apply.c:1628
+msgid "new file depends on old contents"
+msgstr "il nuovo file dipende da contenuti precedenti"
+
+#: builtin/apply.c:1630
+msgid "deleted file still has contents"
+msgstr "il file eliminato ha ancora dei contenuti"
+
+#: builtin/apply.c:1656
+#, c-format
+msgid "corrupt patch at line %d"
+msgstr ""
+
+#: builtin/apply.c:1692
+#, c-format
+msgid "new file %s depends on old contents"
+msgstr "il nuovo file %s dipende da contenuti precedenti"
+
+#: builtin/apply.c:1694
+#, c-format
+msgid "deleted file %s still has contents"
+msgstr "il file eliminato %s ha ancora dei contenuti"
+
+#: builtin/apply.c:1697
+#, c-format
+msgid "** warning: file %s becomes empty but is not deleted"
+msgstr "** attenzione: il file %s diventa vuoto ma non è eliminato"
+
+#: builtin/apply.c:1843
+#, c-format
+msgid "corrupt binary patch at line %d: %.*s"
+msgstr "patch binaria corrotta alla riga %d: %.*s"
+
+#. there has to be one hunk (forward hunk)
+#: builtin/apply.c:1872
+#, c-format
+msgid "unrecognized binary patch at line %d"
+msgstr "patch binaria non riconosciuta alla riga %d"
+
+#: builtin/apply.c:1958
+#, c-format
+msgid "patch with only garbage at line %d"
+msgstr ""
+
+#: builtin/apply.c:2048
+#, c-format
+msgid "unable to read symlink %s"
+msgstr "impossibile leggere il link simbolico %s"
+
+#: builtin/apply.c:2052
+#, c-format
+msgid "unable to open or read %s"
+msgstr "impossibile aprire o leggere %s"
+
+#: builtin/apply.c:2123
+msgid "oops"
+msgstr "oops"
+
+#: builtin/apply.c:2645
+#, c-format
+msgid "invalid start of line: '%c'"
+msgstr "inizio della riga non valido: '%c'"
+
+#: builtin/apply.c:2763
+#, c-format
+msgid "Hunk #%d succeeded at %d (offset %d line)."
+msgid_plural "Hunk #%d succeeded at %d (offset %d lines)."
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/apply.c:2775
+#, c-format
+msgid "Context reduced to (%ld/%ld) to apply fragment at %d"
+msgstr ""
+
+#: builtin/apply.c:2781
+#, c-format
+msgid ""
+"while searching for:\n"
+"%.*s"
+msgstr ""
+"durante la ricerca per:\n"
+"%.*s"
+
+#: builtin/apply.c:2800
+#, c-format
+msgid "missing binary patch data for '%s'"
+msgstr "dati della patch binaria mancanti per '%s'"
+
+#: builtin/apply.c:2903
+#, c-format
+msgid "binary patch does not apply to '%s'"
+msgstr "la patch binaria non può essere applicata a '%s'"
+
+#: builtin/apply.c:2909
+#, c-format
+msgid "binary patch to '%s' creates incorrect result (expecting %s, got %s)"
+msgstr "la patch binaria su '%s' crea risultati non corretti (atteso %s, ricevuto %s)"
+
+#: builtin/apply.c:2930
+#, c-format
+msgid "patch failed: %s:%ld"
+msgstr "patch non riuscita: %s:%ld"
+
+#: builtin/apply.c:3045
+#, c-format
+msgid "patch %s has been renamed/deleted"
+msgstr "la patch %s è stata rinominata/eliminata"
+
+#: builtin/apply.c:3052 builtin/apply.c:3069
+#, c-format
+msgid "read of %s failed"
+msgstr "lettura di %s non riuscita"
+
+#: builtin/apply.c:3084
+msgid "removal patch leaves file contents"
+msgstr "la rimozione della patch lascia contenuti del file"
+
+#: builtin/apply.c:3105
+#, c-format
+msgid "%s: already exists in working directory"
+msgstr "%s: esiste già nella directory di lavoro"
+
+#: builtin/apply.c:3143
+#, c-format
+msgid "%s: has been deleted/renamed"
+msgstr "%s: è stata eliminata/rinominata"
+
+#: builtin/apply.c:3148 builtin/apply.c:3179
+#, c-format
+msgid "%s: %s"
+msgstr "%s: %s"
+
+#: builtin/apply.c:3159
+#, c-format
+msgid "%s: does not exist in index"
+msgstr "%s: non esiste in index"
+
+#: builtin/apply.c:3173
+#, c-format
+msgid "%s: does not match index"
+msgstr "%s: non corrisponde a index"
+
+#: builtin/apply.c:3190
+#, c-format
+msgid "%s: wrong type"
+msgstr "%s: tipo errato"
+
+#: builtin/apply.c:3192
+#, c-format
+msgid "%s has type %o, expected %o"
+msgstr "%s ha il tipo %o, atteso %o"
+
+#: builtin/apply.c:3247
+#, c-format
+msgid "%s: already exists in index"
+msgstr "%s: esiste già in index"
+
+#: builtin/apply.c:3267
+#, c-format
+msgid "new mode (%o) of %s does not match old mode (%o)"
+msgstr ""
+
+#: builtin/apply.c:3272
+#, c-format
+msgid "new mode (%o) of %s does not match old mode (%o) of %s"
+msgstr ""
+
+#: builtin/apply.c:3280
+#, c-format
+msgid "%s: patch does not apply"
+msgstr "%s: la patch non può essere applicata"
+
+#: builtin/apply.c:3293
+#, c-format
+msgid "Checking patch %s..."
+msgstr "Controllo della patch %s..."
+
+#: builtin/apply.c:3348 builtin/checkout.c:212 builtin/reset.c:158
+#, c-format
+msgid "make_cache_entry failed for path '%s'"
+msgstr "make_cache_entry non riuscito per il path '%s'"
+
+#: builtin/apply.c:3491
+#, c-format
+msgid "unable to remove %s from index"
+msgstr "impossibile rimuovere %s da index"
+
+#: builtin/apply.c:3518
+#, c-format
+msgid "corrupt patch for subproject %s"
+msgstr "patch corrotta per il sottoprogetto %s"
+
+#: builtin/apply.c:3522
+#, c-format
+msgid "unable to stat newly created file '%s'"
+msgstr "impossibile eseguire lo stat del file appena creato '%s'"
+
+#: builtin/apply.c:3527
+#, c-format
+msgid "unable to create backing store for newly created file %s"
+msgstr ""
+
+#: builtin/apply.c:3530
+#, c-format
+msgid "unable to add cache entry for %s"
+msgstr "impossibile aggiungere la voce della cache per %s"
+
+#: builtin/apply.c:3563
+#, c-format
+msgid "closing file '%s'"
+msgstr "chiusura del file '%s'"
+
+#: builtin/apply.c:3612
+#, c-format
+msgid "unable to write file '%s' mode %o"
+msgstr "impossibile scrivere il file '%s' in modalità %o"
+
+#: builtin/apply.c:3668
+#, c-format
+msgid "Applied patch %s cleanly."
+msgstr "Patch %s applicata correttamente."
+
+#: builtin/apply.c:3676
+msgid "internal error"
+msgstr "errore interno"
+
+#. Say this even without --verbose
+#: builtin/apply.c:3679
+#, c-format
+msgid "Applying patch %%s with %d reject..."
+msgid_plural "Applying patch %%s with %d rejects..."
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/apply.c:3689
+#, c-format
+msgid "truncating .rej filename to %.*s.rej"
+msgstr ""
+
+#: builtin/apply.c:3710
+#, c-format
+msgid "Hunk #%d applied cleanly."
+msgstr "Frammento #%d applicato correttamente."
+
+#: builtin/apply.c:3713
+#, c-format
+msgid "Rejected hunk #%d."
+msgstr "Frammento #%d respinto."
+
+#: builtin/apply.c:3844
+msgid "unrecognized input"
+msgstr "input non riconosciuto"
+
+#: builtin/apply.c:3855
+msgid "unable to read index file"
+msgstr "impossibile leggere index file"
+
+#: builtin/apply.c:3970 builtin/apply.c:3973
+msgid "path"
+msgstr "path"
+
+#: builtin/apply.c:3971
+msgid "don't apply changes matching the given path"
+msgstr "non applica le modifiche corrispondenti al path specificato"
+
+#: builtin/apply.c:3974
+msgid "apply changes matching the given path"
+msgstr "applica le modifiche corrispondenti al path specificato"
+
+#: builtin/apply.c:3976
+msgid "num"
+msgstr "num"
+
+#: builtin/apply.c:3977
+msgid "remove <num> leading slashes from traditional diff paths"
+msgstr ""
+
+#: builtin/apply.c:3980
+msgid "ignore additions made by the patch"
+msgstr "ignora le aggiunte create dalla patch"
+
+#: builtin/apply.c:3982
+msgid "instead of applying the patch, output diffstat for the input"
+msgstr "invece di applicare la patch, mostra l'output di diffstat per l'input"
+
+#: builtin/apply.c:3986
+msgid "shows number of added and deleted lines in decimal notation"
+msgstr "mostra il numero di righe aggiunte e eliminate in notazione decimale"
+
+#: builtin/apply.c:3988
+msgid "instead of applying the patch, output a summary for the input"
+msgstr "invece di applicare la patch, mostra un riassunto per l'input"
+
+#: builtin/apply.c:3990
+msgid "instead of applying the patch, see if the patch is applicable"
+msgstr "invece di applicare la patch, verifica se può essere applicata"
+
+#: builtin/apply.c:3992
+msgid "make sure the patch is applicable to the current index"
+msgstr "assicura che la patch sia applicabile all'attuale index"
+
+#: builtin/apply.c:3994
+msgid "apply a patch without touching the working tree"
+msgstr ""
+
+#: builtin/apply.c:3996
+msgid "also apply the patch (use with --stat/--summary/--check)"
+msgstr "applica anche la patch (da usare con --stat/--summary/--check)"
+
+#: builtin/apply.c:3998
+msgid "build a temporary index based on embedded index information"
+msgstr ""
+
+#: builtin/apply.c:4000
+msgid "paths are separated with NUL character"
+msgstr "i path sono separati con un carattere NUL"
+
+#: builtin/apply.c:4003
+msgid "ensure at least <n> lines of context match"
+msgstr "assicura almeno <n> righe di contesto corrispondente"
+
+#: builtin/apply.c:4004
+msgid "action"
+msgstr "azione"
+
+#: builtin/apply.c:4005
+msgid "detect new or modified lines that have whitespace errors"
+msgstr "rileva righe nuove o modificate che hanno errori di spazi bianchi"
+
+#: builtin/apply.c:4008 builtin/apply.c:4011
+msgid "ignore changes in whitespace when finding context"
+msgstr ""
+
+#: builtin/apply.c:4014
+msgid "apply the patch in reverse"
+msgstr "applica la patch in maniera inversa"
+
+#: builtin/apply.c:4016
+msgid "don't expect at least one line of context"
+msgstr ""
+
+#: builtin/apply.c:4018
+msgid "leave the rejected hunks in corresponding *.rej files"
+msgstr "lascia i frammenti respinti nei file .rej corrispondenti"
+
+#: builtin/apply.c:4020
+msgid "allow overlapping hunks"
+msgstr "consente la sovrapposizione dei frammenti"
+
+#: builtin/apply.c:4021
+msgid "be verbose"
+msgstr "dettagliato"
+
+#: builtin/apply.c:4023
+msgid "tolerate incorrectly detected missing new-line at the end of file"
+msgstr ""
+
+#: builtin/apply.c:4026
+msgid "do not trust the line counts in the hunk headers"
+msgstr ""
+
+#: builtin/apply.c:4028
+msgid "root"
+msgstr "radice"
+
+#: builtin/apply.c:4029
+msgid "prepend <root> to all filenames"
+msgstr "antepone <root> a tutti i nomi file"
+
+#: builtin/apply.c:4050
+msgid "--index outside a repository"
+msgstr "--index al di fuori di un repository"
+
+#: builtin/apply.c:4053
+msgid "--cached outside a repository"
+msgstr "--cached al di fuori di un repository"
+
+#: builtin/apply.c:4069
+#, c-format
+msgid "can't open patch '%s'"
+msgstr "impossibile aprire la patch '%s'"
+
+#: builtin/apply.c:4083
+#, c-format
+msgid "squelched %d whitespace error"
+msgid_plural "squelched %d whitespace errors"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/apply.c:4089 builtin/apply.c:4099
+#, c-format
+msgid "%d line adds whitespace errors."
+msgid_plural "%d lines add whitespace errors."
+msgstr[0] "%d riga aggiunge errori di spazi bianchi."
+msgstr[1] "%d righe aggiungono errori di spazi bianchi."
+
+#: builtin/archive.c:17
+#, c-format
+msgid "could not create archive file '%s'"
+msgstr "non è stato possibile creare il file del repository '%s'"
+
+#: builtin/archive.c:20
+msgid "could not redirect output"
+msgstr "non è stato possibile redirigere l'output"
+
+#: builtin/archive.c:37
+msgid "git archive: Remote with no URL"
+msgstr "git archive: Remote non ha un URL"
+
+#: builtin/archive.c:58
+msgid "git archive: expected ACK/NAK, got EOF"
+msgstr "git archive: atteso ACK/NAK, ricevuto EOF"
+
+#: builtin/archive.c:63
+#, c-format
+msgid "git archive: NACK %s"
+msgstr "git archive: NACK %s"
+
+#: builtin/archive.c:65
+#, c-format
+msgid "remote error: %s"
+msgstr "errore remoto: %s"
+
+#: builtin/archive.c:66
+msgid "git archive: protocol error"
+msgstr "git archive: errore del protocollo"
+
+#: builtin/archive.c:71
+msgid "git archive: expected a flush"
+msgstr "git archive: atteso un flush"
+
+#: builtin/branch.c:144
+#, c-format
+msgid ""
+"deleting branch '%s' that has been merged to\n"
+"         '%s', but not yet merged to HEAD."
+msgstr ""
+
+#: builtin/branch.c:148
+#, c-format
+msgid ""
+"not deleting branch '%s' that is not yet merged to\n"
+"         '%s', even though it is merged to HEAD."
+msgstr ""
+
+#: builtin/branch.c:180
+msgid "cannot use -a with -d"
+msgstr "impossibile usare -a con -d"
+
+#: builtin/branch.c:186
+msgid "Couldn't look up commit object for HEAD"
+msgstr ""
+
+#: builtin/branch.c:191
+#, c-format
+msgid "Cannot delete the branch '%s' which you are currently on."
+msgstr ""
+
+#: builtin/branch.c:202
+#, c-format
+msgid "remote branch '%s' not found."
+msgstr "il branch remoto '%s' non è stato trovato."
+
+#: builtin/branch.c:203
+#, c-format
+msgid "branch '%s' not found."
+msgstr "branch '%s' non trovato."
+
+#: builtin/branch.c:210
+#, c-format
+msgid "Couldn't look up commit object for '%s'"
+msgstr ""
+
+#: builtin/branch.c:216
+#, c-format
+msgid ""
+"The branch '%s' is not fully merged.\n"
+"If you are sure you want to delete it, run 'git branch -D %s'."
+msgstr ""
+
+#: builtin/branch.c:225
+#, c-format
+msgid "Error deleting remote branch '%s'"
+msgstr "Errore nella rimozione del branch remoto '%s'"
+
+#: builtin/branch.c:226
+#, c-format
+msgid "Error deleting branch '%s'"
+msgstr "Errore nella rimozione del branch '%s'"
+
+#: builtin/branch.c:233
+#, c-format
+msgid "Deleted remote branch %s (was %s).\n"
+msgstr "Ramo remoto %s eliminato (era %s).\n"
+
+#: builtin/branch.c:234
+#, c-format
+msgid "Deleted branch %s (was %s).\n"
+msgstr "Ramo %s eliminato (era %s).\n"
+
+#: builtin/branch.c:239
+msgid "Update of config-file failed"
+msgstr "Aggiornamento del file di configurazione fallito"
+
+#: builtin/branch.c:337
+#, c-format
+msgid "branch '%s' does not point at a commit"
+msgstr "il branch '%s' non punta ad un commit"
+
+#: builtin/branch.c:409
+#, c-format
+msgid "[%s: behind %d]"
+msgstr "[%s: dietro %d]"
+
+#: builtin/branch.c:411
+#, c-format
+msgid "[behind %d]"
+msgstr "[dietro %d]"
+
+#: builtin/branch.c:415
+#, c-format
+msgid "[%s: ahead %d]"
+msgstr "[%s: avanti %d]"
+
+#: builtin/branch.c:417
+#, c-format
+msgid "[ahead %d]"
+msgstr ""
+
+#: builtin/branch.c:420
+#, c-format
+msgid "[%s: ahead %d, behind %d]"
+msgstr ""
+
+#: builtin/branch.c:423
+#, c-format
+msgid "[ahead %d, behind %d]"
+msgstr ""
+
+#: builtin/branch.c:535
+msgid "(no branch)"
+msgstr "(nessun branch)"
+
+#: builtin/branch.c:600
+msgid "some refs could not be read"
+msgstr ""
+
+#: builtin/branch.c:613
+msgid "cannot rename the current branch while not on any."
+msgstr ""
+
+#: builtin/branch.c:623
+#, c-format
+msgid "Invalid branch name: '%s'"
+msgstr "Nome del branch non valido: '%s'"
+
+#: builtin/branch.c:638
+msgid "Branch rename failed"
+msgstr ""
+
+#: builtin/branch.c:642
+#, c-format
+msgid "Renamed a misnamed branch '%s' away"
+msgstr ""
+
+#: builtin/branch.c:646
+#, c-format
+msgid "Branch renamed to %s, but HEAD is not updated!"
+msgstr "Ramo rinominato in %s, ma HEAD non è aggiornato!"
+
+#: builtin/branch.c:653
+msgid "Branch is renamed, but update of config-file failed"
+msgstr ""
+"Il branch è stato rinominato, ma l'aggiornamento del file di configurazione "
+"è fallito"
+
+#: builtin/branch.c:668
+#, c-format
+msgid "malformed object name %s"
+msgstr "nome dell'oggetto %s errato"
+
+#: builtin/branch.c:692
+#, c-format
+msgid "could not write branch description template: %s"
+msgstr ""
+
+#: builtin/branch.c:783
+msgid "Failed to resolve HEAD as a valid ref."
+msgstr ""
+
+#: builtin/branch.c:788 builtin/clone.c:558
+msgid "HEAD not found below refs/heads!"
+msgstr ""
+
+#: builtin/branch.c:808
+msgid "--column and --verbose are incompatible"
+msgstr "--column e --verbose non sono compatibili"
+
+#: builtin/branch.c:857
+msgid "-a and -r options to 'git branch' do not make sense with a branch name"
+msgstr ""
+"le opzioni -a e -r per 'git branch' non hanno senso con il nome di un branch"
+
+#: builtin/bundle.c:47
+#, c-format
+msgid "%s is okay\n"
+msgstr "%s è corretto\n"
+
+#: builtin/bundle.c:56
+msgid "Need a repository to create a bundle."
+msgstr ""
+
+#: builtin/bundle.c:60
+msgid "Need a repository to unbundle."
+msgstr ""
+
+#: builtin/checkout.c:113 builtin/checkout.c:146
+#, c-format
+msgid "path '%s' does not have our version"
+msgstr "il path '%s' non ha la nostra versione"
+
+#: builtin/checkout.c:115 builtin/checkout.c:148
+#, c-format
+msgid "path '%s' does not have their version"
+msgstr "il path '%s' non ha la loro versione"
+
+#: builtin/checkout.c:131
+#, c-format
+msgid "path '%s' does not have all necessary versions"
+msgstr ""
+
+#: builtin/checkout.c:175
+#, c-format
+msgid "path '%s' does not have necessary versions"
+msgstr "il path '%s' non ha le versioni necessarie"
+
+#: builtin/checkout.c:192
+#, c-format
+msgid "path '%s': cannot merge"
+msgstr ""
+
+#: builtin/checkout.c:209
+#, c-format
+msgid "Unable to add merge result for '%s'"
+msgstr ""
+
+#: builtin/checkout.c:234 builtin/checkout.c:392
+msgid "corrupt index file"
+msgstr "file index corrotto"
+
+#: builtin/checkout.c:264 builtin/checkout.c:271
+#, c-format
+msgid "path '%s' is unmerged"
+msgstr ""
+
+#: builtin/checkout.c:302 builtin/checkout.c:498 builtin/clone.c:583
+#: builtin/merge.c:812
+msgid "unable to write new index file"
+msgstr "impossibile scrivere il nuovo file index"
+
+#: builtin/checkout.c:319 builtin/diff.c:302 builtin/merge.c:408
+msgid "diff_setup_done failed"
+msgstr "diff_setup_done non riuscito"
+
+#: builtin/checkout.c:414
+msgid "you need to resolve your current index first"
+msgstr "è necessario risolvere prima l'attuale index"
+
+#: builtin/checkout.c:533
+#, c-format
+msgid "Can not do reflog for '%s'\n"
+msgstr ""
+
+#: builtin/checkout.c:566
+msgid "HEAD is now at"
+msgstr "HEAD si trova ora a"
+
+#: builtin/checkout.c:573
+#, c-format
+msgid "Reset branch '%s'\n"
+msgstr "Ripristina il branch '%s'\n"
+
+#: builtin/checkout.c:576
+#, c-format
+msgid "Already on '%s'\n"
+msgstr "Si è già su '%s'\n"
+
+#: builtin/checkout.c:580
+#, c-format
+msgid "Switched to and reset branch '%s'\n"
+msgstr ""
+
+#: builtin/checkout.c:582
+#, c-format
+msgid "Switched to a new branch '%s'\n"
+msgstr ""
+
+#: builtin/checkout.c:584
+#, c-format
+msgid "Switched to branch '%s'\n"
+msgstr "Si è passati al branch '%s'\n"
+
+#: builtin/checkout.c:640
+#, c-format
+msgid " ... and %d more.\n"
+msgstr " ... e %d altri.\n"
+
+#. The singular version
+#: builtin/checkout.c:646
+#, c-format
+msgid ""
+"Warning: you are leaving %d commit behind, not connected to\n"
+"any of your branches:\n"
+"\n"
+"%s\n"
+msgid_plural ""
+"Warning: you are leaving %d commits behind, not connected to\n"
+"any of your branches:\n"
+"\n"
+"%s\n"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/checkout.c:664
+#, c-format
+msgid ""
+"If you want to keep them by creating a new branch, this may be a good time\n"
+"to do so with:\n"
+"\n"
+" git branch new_branch_name %s\n"
+"\n"
+msgstr ""
+"Se si vuole mantenerle creando un nuovo branch, questo potrebbe essere\n"
+"un buon momento per farlo con:\n"
+"\n"
+" git branch nuovo_nome_branch %s\n"
+"\n"
+
+#: builtin/checkout.c:694
+msgid "internal error in revision walk"
+msgstr ""
+
+#: builtin/checkout.c:698
+msgid "Previous HEAD position was"
+msgstr "La precedente posizione di HEAD era"
+
+#: builtin/checkout.c:724
+msgid "You are on a branch yet to be born"
+msgstr ""
+
+#. case (1)
+#: builtin/checkout.c:855
+#, c-format
+msgid "invalid reference: %s"
+msgstr "riferimento non valido: %s"
+
+#. case (1): want a tree
+#: builtin/checkout.c:894
+#, c-format
+msgid "reference is not a tree: %s"
+msgstr ""
+
+#: builtin/checkout.c:974
+msgid "-B cannot be used with -b"
+msgstr "-B non può essere usata con -b"
+
+#: builtin/checkout.c:983
+msgid "--patch is incompatible with all other options"
+msgstr "--patch non è compatibile con tutte le altre opzioni"
+
+#: builtin/checkout.c:986
+msgid "--detach cannot be used with -b/-B/--orphan"
+msgstr "--detach non può essere usata con -b/-B/--orphan"
+
+#: builtin/checkout.c:988
+msgid "--detach cannot be used with -t"
+msgstr "--detach non può essere usata con -t"
+
+#: builtin/checkout.c:994
+msgid "--track needs a branch name"
+msgstr "--track necessita del nome di un branch"
+
+#: builtin/checkout.c:1001
+msgid "Missing branch name; try -b"
+msgstr "Nome del branch mancante; prova con -b"
+
+#: builtin/checkout.c:1007
+msgid "--orphan and -b|-B are mutually exclusive"
+msgstr ""
+
+#: builtin/checkout.c:1009
+msgid "--orphan cannot be used with -t"
+msgstr "--orphan non può essere usata con -t"
+
+#: builtin/checkout.c:1019
+msgid "git checkout: -f and -m are incompatible"
+msgstr "git checkout: -f e -m non sono compatibili"
+
+#: builtin/checkout.c:1053
+msgid "invalid path specification"
+msgstr ""
+
+#: builtin/checkout.c:1061
+#, c-format
+msgid ""
+"git checkout: updating paths is incompatible with switching branches.\n"
+"Did you intend to checkout '%s' which can not be resolved as commit?"
+msgstr ""
+
+#: builtin/checkout.c:1063
+msgid "git checkout: updating paths is incompatible with switching branches."
+msgstr ""
+
+#: builtin/checkout.c:1068
+msgid "git checkout: --detach does not take a path argument"
+msgstr "git checkout: --detach non prende un path come argomento"
+
+#: builtin/checkout.c:1071
+msgid ""
+"git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
+"checking out of the index."
+msgstr ""
+
+#: builtin/checkout.c:1090
+msgid "Cannot switch branch to a non-commit."
+msgstr ""
+
+#: builtin/checkout.c:1093
+msgid "--ours/--theirs is incompatible with switching branches."
+msgstr "--ours/--theirs non sono compatibili con il passaggio ai branch."
+
+#: builtin/clean.c:78
+msgid "-x and -X cannot be used together"
+msgstr "-x e -X non possono essere usati insieme"
+
+#: builtin/clean.c:82
+msgid ""
+"clean.requireForce set to true and neither -n nor -f given; refusing to clean"
+msgstr ""
+"clean.requireForce è impostato a vero, ma né -n né -f sono state specificate; "
+"clean interrotto"
+
+#: builtin/clean.c:85
+msgid ""
+"clean.requireForce defaults to true and neither -n nor -f given; refusing to "
+"clean"
+msgstr ""
+"clean.requireForce è vero per default, ma né -n né -f sono state specificate; "
+"clean interrotto"
+
+#: builtin/clean.c:155 builtin/clean.c:176
+#, c-format
+msgid "Would remove %s\n"
+msgstr ""
+
+#: builtin/clean.c:159 builtin/clean.c:179
+#, c-format
+msgid "Removing %s\n"
+msgstr "Rimozione di %s\n"
+
+#: builtin/clean.c:162 builtin/clean.c:182
+#, c-format
+msgid "failed to remove %s"
+msgstr "rimozione di %s non riuscita"
+
+#: builtin/clean.c:166
+#, c-format
+msgid "Would not remove %s\n"
+msgstr ""
+
+#: builtin/clean.c:168
+#, c-format
+msgid "Not removing %s\n"
+msgstr ""
+
+#: builtin/clone.c:243
+#, c-format
+msgid "reference repository '%s' is not a local directory."
+msgstr "il repository di riferimento '%s' non è una directory locale."
+
+#: builtin/clone.c:302
+#, c-format
+msgid "failed to open '%s'"
+msgstr "apertura di '%s' non riuscita"
+
+#: builtin/clone.c:306
+#, c-format
+msgid "failed to create directory '%s'"
+msgstr "creazione della directory '%s' non riuscita"
+
+#: builtin/clone.c:308 builtin/diff.c:75
+#, c-format
+msgid "failed to stat '%s'"
+msgstr "stat di '%s' non riuscito"
+
+#: builtin/clone.c:310
+#, c-format
+msgid "%s exists and is not a directory"
+msgstr "%s esiste e non è una directory"
+
+#: builtin/clone.c:324
+#, c-format
+msgid "failed to stat %s\n"
+msgstr "stat di %s non riuscito\n"
+
+#: builtin/clone.c:341
+#, c-format
+msgid "failed to unlink '%s'"
+msgstr "rimozione del link '%s' non riuscita"
+
+#: builtin/clone.c:346
+#, c-format
+msgid "failed to create link '%s'"
+msgstr "creazione del link '%s' non riuscita"
+
+#: builtin/clone.c:350
+#, c-format
+msgid "failed to copy file to '%s'"
+msgstr "copia del file in '%s' non riuscita"
+
+#: builtin/clone.c:373
+#, c-format
+msgid "done.\n"
+msgstr "fatto.\n"
+
+#: builtin/clone.c:440
+#, c-format
+msgid "Could not find remote branch %s to clone."
+msgstr "Non è stato possibile trovare il branch remoto %s da clonare."
+
+#: builtin/clone.c:549
+msgid "remote HEAD refers to nonexistent ref, unable to checkout.\n"
+msgstr ""
+"l'HEAD remoto si riferisce ad un ref inesistente, impossibile eseguire il "
+"checkout.\n"
+
+#: builtin/clone.c:639
+msgid "Too many arguments."
+msgstr "Troppi argomenti."
+
+#: builtin/clone.c:643
+msgid "You must specify a repository to clone."
+msgstr "Devi specificare un repository da clonare."
+
+#: builtin/clone.c:654
+#, c-format
+msgid "--bare and --origin %s options are incompatible."
+msgstr "le opzioni --bare e --origin %s non sono compatibili."
+
+#: builtin/clone.c:668
+#, c-format
+msgid "repository '%s' does not exist"
+msgstr "il repository '%s' non esiste"
+
+#: builtin/clone.c:673
+msgid "--depth is ignored in local clones; use file:// instead."
+msgstr ""
+
+#: builtin/clone.c:683
+#, c-format
+msgid "destination path '%s' already exists and is not an empty directory."
+msgstr ""
+"il path di destinazione '%s' esiste già e non è una directory vuota."
+
+#: builtin/clone.c:693
+#, c-format
+msgid "working tree '%s' already exists."
+msgstr ""
+
+#: builtin/clone.c:706 builtin/clone.c:720
+#, c-format
+msgid "could not create leading directories of '%s'"
+msgstr ""
+
+#: builtin/clone.c:709
+#, c-format
+msgid "could not create work tree dir '%s'."
+msgstr ""
+
+#: builtin/clone.c:728
+#, c-format
+msgid "Cloning into bare repository '%s'...\n"
+msgstr "Clone nel repository spoglio '%s'...\n"
+
+#: builtin/clone.c:730
+#, c-format
+msgid "Cloning into '%s'...\n"
+msgstr ""
+
+#: builtin/clone.c:786
+#, c-format
+msgid "Don't know how to clone %s"
+msgstr "Non so come clonare %s"
+
+#: builtin/clone.c:835
+#, c-format
+msgid "Remote branch %s not found in upstream %s"
+msgstr "Il branch remoto %s non è stato trovato in upstream %s"
+
+#: builtin/clone.c:842
+msgid "You appear to have cloned an empty repository."
+msgstr "Sembra che tu abbia clonato un repository vuoto."
+
+#: builtin/column.c:51
+msgid "--command must be the first argument"
+msgstr "--command deve essere il primo argomento"
+
+#: builtin/commit.c:43
+msgid ""
+"Your name and email address were configured automatically based\n"
+"on your username and hostname. Please check that they are accurate.\n"
+"You can suppress this message by setting them explicitly:\n"
+"\n"
+"    git config --global user.name \"Your Name\"\n"
+"    git config --global user.email you@example.com\n"
+"\n"
+"After doing this, you may fix the identity used for this commit with:\n"
+"\n"
+"    git commit --amend --reset-author\n"
+msgstr ""
+"Il tuo nome e l'indirizzo email sono stati configurati automaticamente usando\n"
+"il tuo nome utente ed il nome host. Per favore, verifica che siano esatti.\n"
+"È possibile eliminare questo messaggio impostandoli esplicitamente:\n"
+"\n"
+"    git config --global user.name \"Tuo Nome\"\n"
+"    git config --global user.email tu@esempio.com\n"
+"\n"
+"Dopo questa operazione, puoi ripristinare l'identità usata in questo commit con:\n"
+"\n"
+"    git commit --amend --reset-author\n"
+
+#: builtin/commit.c:55
+msgid ""
+"You asked to amend the most recent commit, but doing so would make\n"
+"it empty. You can repeat your command with --allow-empty, or you can\n"
+"remove the commit entirely with \"git reset HEAD^\".\n"
+msgstr ""
+
+#: builtin/commit.c:60
+msgid ""
+"The previous cherry-pick is now empty, possibly due to conflict resolution.\n"
+"If you wish to commit it anyway, use:\n"
+"\n"
+"    git commit --allow-empty\n"
+"\n"
+"Otherwise, please use 'git reset'\n"
+msgstr ""
+
+#: builtin/commit.c:253
+msgid "failed to unpack HEAD tree object"
+msgstr ""
+
+#: builtin/commit.c:295
+msgid "unable to create temporary index"
+msgstr ""
+
+#: builtin/commit.c:301
+msgid "interactive add failed"
+msgstr "add interattivo non riuscito"
+
+#: builtin/commit.c:334 builtin/commit.c:355 builtin/commit.c:405
+msgid "unable to write new_index file"
+msgstr "impossibile scrivere il file new_index"
+
+#: builtin/commit.c:386
+msgid "cannot do a partial commit during a merge."
+msgstr "impossibile eseguire un commit parziale durante un merge."
+
+#: builtin/commit.c:388
+msgid "cannot do a partial commit during a cherry-pick."
+msgstr "impossibile eseguire un commit parziale durante un cherry-pick."
+
+#: builtin/commit.c:398
+msgid "cannot read the index"
+msgstr "impossibile leggere index"
+
+#: builtin/commit.c:418
+msgid "unable to write temporary index file"
+msgstr ""
+
+#: builtin/commit.c:493 builtin/commit.c:499
+#, c-format
+msgid "invalid commit: %s"
+msgstr "commit non valido: %s"
+
+#: builtin/commit.c:522
+msgid "malformed --author parameter"
+msgstr "parametro --author malformato"
+
+#: builtin/commit.c:582
+#, c-format
+msgid "Malformed ident string: '%s'"
+msgstr ""
+
+#: builtin/commit.c:620 builtin/commit.c:653 builtin/commit.c:967
+#, c-format
+msgid "could not lookup commit %s"
+msgstr "non è stato possibile trovare il commit %s"
+
+#: builtin/commit.c:632 builtin/shortlog.c:296
+#, c-format
+msgid "(reading log message from standard input)\n"
+msgstr "(lettura del messaggio di log dallo standard input)\n"
+
+#: builtin/commit.c:634
+msgid "could not read log from standard input"
+msgstr "non è stato possibile leggere il log dallo standard input"
+
+#: builtin/commit.c:638
+#, c-format
+msgid "could not read log file '%s'"
+msgstr "non è stato possibile leggere il file di log '%s'"
+
+#: builtin/commit.c:644
+msgid "commit has empty message"
+msgstr "il commit ha un messaggio vuoto"
+
+#: builtin/commit.c:660
+msgid "could not read MERGE_MSG"
+msgstr "non è stato possibile leggere MERGE_MSG"
+
+#: builtin/commit.c:664
+msgid "could not read SQUASH_MSG"
+msgstr "non è stato possibile leggere SQUASH_MSG"
+
+#: builtin/commit.c:668
+#, c-format
+msgid "could not read '%s'"
+msgstr "non è stato possibile leggere '%s'"
+
+#: builtin/commit.c:720
+msgid "could not write commit template"
+msgstr "non è stato possibile scrivere il modello di commit"
+
+#: builtin/commit.c:731
+#, c-format
+msgid ""
+"\n"
+"It looks like you may be committing a merge.\n"
+"If this is not correct, please remove the file\n"
+"\t%s\n"
+"and try again.\n"
+msgstr ""
+"\n"
+"Sembra che si stia eseguendo il commit di un merge.\n"
+"Se l'operazione non è corretta, per favore elimina il file\n"
+"\t%s\n"
+"e riprova.\n"
+
+#: builtin/commit.c:736
+#, c-format
+msgid ""
+"\n"
+"It looks like you may be committing a cherry-pick.\n"
+"If this is not correct, please remove the file\n"
+"\t%s\n"
+"and try again.\n"
+msgstr ""
+"\n"
+"Sembra che si stia eseguendo il commit di un cherry-pick.\n"
+"Se l'operazione non è corretta, per favore rimuovi il file\n"
+"\t%s\n"
+"e riprova.\n"
+
+#: builtin/commit.c:748
+msgid ""
+"Please enter the commit message for your changes. Lines starting\n"
+"with '#' will be ignored, and an empty message aborts the commit.\n"
+msgstr ""
+"Per favore inserisci il messaggio di commit per le modifiche. Le righe\n"
+"che iniziano con '#' verranno ignorate, e un messaggio vuoto annulla il commit.\n"
+
+#: builtin/commit.c:753
+msgid ""
+"Please enter the commit message for your changes. Lines starting\n"
+"with '#' will be kept; you may remove them yourself if you want to.\n"
+"An empty message aborts the commit.\n"
+msgstr ""
+"Per favore inserisci il messaggio di commit per le modifiche. Le\n"
+"righe che iniziano con '#' verranno mantenute; possono comunque essere\n"
+"rimosse manualmente. Un messaggio vuoto annulla il commit.\n"
+
+#: builtin/commit.c:766
+#, c-format
+msgid "%sAuthor:    %s"
+msgstr "%sAutore:    %s"
+
+#: builtin/commit.c:773
+#, c-format
+msgid "%sCommitter: %s"
+msgstr "%sCommitter: %s"
+
+#: builtin/commit.c:793
+msgid "Cannot read index"
+msgstr "Impossibile leggere index"
+
+#: builtin/commit.c:830
+msgid "Error building trees"
+msgstr ""
+
+#: builtin/commit.c:845 builtin/tag.c:361
+#, c-format
+msgid "Please supply the message using either -m or -F option.\n"
+msgstr "Per favore, specifica il messaggio usando l'opzione -m o -F.\n"
+
+#: builtin/commit.c:942
+#, c-format
+msgid "No existing author found with '%s'"
+msgstr "Nessun autore esistente trovato con '%s'"
+
+#: builtin/commit.c:957 builtin/commit.c:1157
+#, c-format
+msgid "Invalid untracked files mode '%s'"
+msgstr ""
+
+#: builtin/commit.c:997
+msgid "Using both --reset-author and --author does not make sense"
+msgstr "L'uso di entrambe le opzioni --reset-author e --author non ha senso"
+
+#: builtin/commit.c:1008
+msgid "You have nothing to amend."
+msgstr ""
+
+#: builtin/commit.c:1011
+msgid "You are in the middle of a merge -- cannot amend."
+msgstr ""
+
+#: builtin/commit.c:1013
+msgid "You are in the middle of a cherry-pick -- cannot amend."
+msgstr ""
+
+#: builtin/commit.c:1016
+msgid "Options --squash and --fixup cannot be used together"
+msgstr "Le opzioni --squash e --fixup non possono essere usate insieme"
+
+#: builtin/commit.c:1026
+msgid "Only one of -c/-C/-F/--fixup can be used."
+msgstr "Solo una delle opzioni -c/-C/-F/--fixup può essere usata."
+
+#: builtin/commit.c:1028
+msgid "Option -m cannot be combined with -c/-C/-F/--fixup."
+msgstr "L'opzione -m non può essere combinata con -c/-C/-F/--fixup."
+
+#: builtin/commit.c:1036
+msgid "--reset-author can be used only with -C, -c or --amend."
+msgstr "L'opzione --reset-author può essere usata solo con -C, -c o --amend."
+
+#: builtin/commit.c:1053
+msgid "Only one of --include/--only/--all/--interactive/--patch can be used."
+msgstr ""
+"Può essere usata solo una delle opzioni --include/--only/--all/--"
+"interactive/--patch ."
+
+#: builtin/commit.c:1055
+msgid "No paths with --include/--only does not make sense."
+msgstr "Devi specificare un path se usi --include/--only."
+
+#: builtin/commit.c:1057
+msgid "Clever... amending the last one with dirty index."
+msgstr ""
+
+#: builtin/commit.c:1059
+msgid "Explicit paths specified without -i nor -o; assuming --only paths..."
+msgstr ""
+
+#: builtin/commit.c:1069 builtin/tag.c:577
+#, c-format
+msgid "Invalid cleanup mode %s"
+msgstr ""
+
+#: builtin/commit.c:1074
+msgid "Paths with -a does not make sense."
+msgstr "I path con -a non hanno senso."
+
+#: builtin/commit.c:1257
+msgid "couldn't look up newly created commit"
+msgstr "non è stato possibile trovare il commit appena creato"
+
+#: builtin/commit.c:1259
+msgid "could not parse newly created commit"
+msgstr "non è stato possibile analizzare il commit appena creato"
+
+#: builtin/commit.c:1300
+msgid "detached HEAD"
+msgstr ""
+
+#: builtin/commit.c:1302
+msgid " (root-commit)"
+msgstr " (root-commit)"
+
+#: builtin/commit.c:1446
+msgid "could not parse HEAD commit"
+msgstr "non è stato possibile analizzare il commit HEAD"
+
+#: builtin/commit.c:1484 builtin/merge.c:509
+#, c-format
+msgid "could not open '%s' for reading"
+msgstr "non è stato possibile aprire '%s' per la lettura"
+
+#: builtin/commit.c:1491
+#, c-format
+msgid "Corrupt MERGE_HEAD file (%s)"
+msgstr "File MERGE_HEAD corrotto (%s)"
+
+#: builtin/commit.c:1498
+msgid "could not read MERGE_MODE"
+msgstr "non è stato possibile leggere MERGE_MODE"
+
+#: builtin/commit.c:1517
+#, c-format
+msgid "could not read commit message: %s"
+msgstr "non è stato possibile leggere il messaggio di commit: %s"
+
+#: builtin/commit.c:1531
+#, c-format
+msgid "Aborting commit; you did not edit the message.\n"
+msgstr "Commit interrotto; il messaggio non è stato modificato.\n"
+
+#: builtin/commit.c:1536
+#, c-format
+msgid "Aborting commit due to empty commit message.\n"
+msgstr "Interruzione del commit a causa di un messaggio di commit vuoto.\n"
+
+#: builtin/commit.c:1551 builtin/merge.c:936 builtin/merge.c:961
+msgid "failed to write commit object"
+msgstr "scrittura dell'oggetto di commit non riuscita"
+
+#: builtin/commit.c:1572
+msgid "cannot lock HEAD ref"
+msgstr ""
+
+#: builtin/commit.c:1576
+msgid "cannot update HEAD ref"
+msgstr ""
+
+#: builtin/commit.c:1587
+msgid ""
+"Repository has been updated, but unable to write\n"
+"new_index file. Check that disk is not full or quota is\n"
+"not exceeded, and then \"git reset HEAD\" to recover."
+msgstr ""
+"Il repository è stato aggiornato, ma non è stato possibile scrivere il file\n"
+"new_index. Verifica che l'unità disco non sia piena o che la quota non sia\n"
+"stata superata, ed esegui \"git reset HEAD\" per il ripristino."
+
+#: builtin/describe.c:234
+#, c-format
+msgid "annotated tag %s not available"
+msgstr ""
+
+#: builtin/describe.c:238
+#, c-format
+msgid "annotated tag %s has no embedded name"
+msgstr ""
+
+#: builtin/describe.c:240
+#, c-format
+msgid "tag '%s' is really '%s' here"
+msgstr "il tag '%s' è davvero '%s' qui"
+
+#: builtin/describe.c:267
+#, c-format
+msgid "Not a valid object name %s"
+msgstr "Non è il nome di un oggetto valido %s"
+
+#: builtin/describe.c:270
+#, c-format
+msgid "%s is not a valid '%s' object"
+msgstr "%s non è un oggetto '%s' valido"
+
+#: builtin/describe.c:287
+#, c-format
+msgid "no tag exactly matches '%s'"
+msgstr "nessun tag corrisponde esattamente a '%s'"
+
+#: builtin/describe.c:289
+#, c-format
+msgid "searching to describe %s\n"
+msgstr ""
+
+#: builtin/describe.c:329
+#, c-format
+msgid "finished search at %s\n"
+msgstr ""
+
+#: builtin/describe.c:353
+#, c-format
+msgid ""
+"No annotated tags can describe '%s'.\n"
+"However, there were unannotated tags: try --tags."
+msgstr ""
+
+#: builtin/describe.c:357
+#, c-format
+msgid ""
+"No tags can describe '%s'.\n"
+"Try --always, or create some tags."
+msgstr ""
+"Nessun tag può descrivere '%s'.\n"
+"Prova con --always, o crea dei tag."
+
+#: builtin/describe.c:378
+#, c-format
+msgid "traversed %lu commits\n"
+msgstr ""
+
+#: builtin/describe.c:381
+#, c-format
+msgid ""
+"more than %i tags found; listed %i most recent\n"
+"gave up search at %s\n"
+msgstr ""
+
+#: builtin/describe.c:436
+msgid "--long is incompatible with --abbrev=0"
+msgstr "--long non è compatibile con --abbrev=0"
+
+#: builtin/describe.c:462
+msgid "No names found, cannot describe anything."
+msgstr ""
+
+#: builtin/describe.c:482
+msgid "--dirty is incompatible with committishes"
+msgstr ""
+
+#: builtin/diff.c:77
+#, c-format
+msgid "'%s': not a regular file or symlink"
+msgstr "'%s': non è un file regolare o un link simbolico"
+
+#: builtin/diff.c:220
+#, c-format
+msgid "invalid option: %s"
+msgstr "opzione non valida: %s"
+
+#: builtin/diff.c:297
+msgid "Not a git repository"
+msgstr "Non è un repository git"
+
+#: builtin/diff.c:347
+#, c-format
+msgid "invalid object '%s' given."
+msgstr "oggetto non valido '%s' specificato."
+
+#: builtin/diff.c:352
+#, c-format
+msgid "more than %d trees given: '%s'"
+msgstr ""
+
+#: builtin/diff.c:362
+#, c-format
+msgid "more than two blobs given: '%s'"
+msgstr "più di due blob specificati: '%s'"
+
+#: builtin/diff.c:370
+#, c-format
+msgid "unhandled object '%s' given."
+msgstr "oggetto non gestito '%s' specificato."
+
+#: builtin/fetch.c:200
+msgid "Couldn't find remote ref HEAD"
+msgstr ""
+
+#: builtin/fetch.c:253
+#, c-format
+msgid "object %s not found"
+msgstr "oggetto %s non trovato"
+
+#: builtin/fetch.c:259
+msgid "[up to date]"
+msgstr "[aggiornato]"
+
+#: builtin/fetch.c:273
+#, c-format
+msgid "! %-*s %-*s -> %s  (can't fetch in current branch)"
+msgstr ""
+
+#: builtin/fetch.c:274 builtin/fetch.c:360
+msgid "[rejected]"
+msgstr "[respinto]"
+
+#: builtin/fetch.c:285
+msgid "[tag update]"
+msgstr "[tag aggiornata]"
+
+#: builtin/fetch.c:287 builtin/fetch.c:322 builtin/fetch.c:340
+msgid "  (unable to update local ref)"
+msgstr "  (impossibile aggiornare il ref locale)"
+
+#: builtin/fetch.c:305
+msgid "[new tag]"
+msgstr "[nuova tag]"
+
+#: builtin/fetch.c:308
+msgid "[new branch]"
+msgstr "[nuovo branch]"
+
+#: builtin/fetch.c:311
+msgid "[new ref]"
+msgstr "[nuovo ref]"
+
+#: builtin/fetch.c:356
+msgid "unable to update local ref"
+msgstr "impossibile aggiornare il ref locale"
+
+#: builtin/fetch.c:356
+msgid "forced update"
+msgstr "aggiornamento forzato"
+
+#: builtin/fetch.c:362
+msgid "(non-fast-forward)"
+msgstr ""
+
+#: builtin/fetch.c:393 builtin/fetch.c:685
+#, c-format
+msgid "cannot open %s: %s\n"
+msgstr "impossibile aprire %s: %s\n"
+
+#: builtin/fetch.c:402
+#, c-format
+msgid "%s did not send all necessary objects\n"
+msgstr "%s non ha inviato tutti gli oggetti necessari\n"
+
+#: builtin/fetch.c:488
+#, c-format
+msgid "From %.*s\n"
+msgstr "Da %.*s\n"
+
+#: builtin/fetch.c:499
+#, c-format
+msgid ""
+"some local refs could not be updated; try running\n"
+" 'git remote prune %s' to remove any old, conflicting branches"
+msgstr ""
+"non è stato possibile aggiornare alcuni ref locali; prova con\n"
+" 'git remote prune %s' per rimuovere ogni branch che vada in conflitto"
+
+#: builtin/fetch.c:549
+#, c-format
+msgid "   (%s will become dangling)"
+msgstr ""
+
+#: builtin/fetch.c:550
+#, c-format
+msgid "   (%s has become dangling)"
+msgstr ""
+
+#: builtin/fetch.c:557
+msgid "[deleted]"
+msgstr "[eliminato]"
+
+#: builtin/fetch.c:558 builtin/remote.c:1055
+msgid "(none)"
+msgstr "(nessuno)"
+
+#: builtin/fetch.c:675
+#, c-format
+msgid "Refusing to fetch into current branch %s of non-bare repository"
+msgstr ""
+
+#: builtin/fetch.c:709
+#, c-format
+msgid "Don't know how to fetch from %s"
+msgstr ""
+
+#: builtin/fetch.c:786
+#, c-format
+msgid "Option \"%s\" value \"%s\" is not valid for %s"
+msgstr ""
+
+#: builtin/fetch.c:789
+#, c-format
+msgid "Option \"%s\" is ignored for %s\n"
+msgstr "L'opzione \"%s\" è ignorata per %s\n"
+
+#: builtin/fetch.c:888
+#, c-format
+msgid "Fetching %s\n"
+msgstr ""
+
+#: builtin/fetch.c:890 builtin/remote.c:100
+#, c-format
+msgid "Could not fetch %s"
+msgstr ""
+
+#: builtin/fetch.c:907
+msgid ""
+"No remote repository specified.  Please, specify either a URL or a\n"
+"remote name from which new revisions should be fetched."
+msgstr ""
+"Nessun repository remoto specificato. Per favore, specifica un URL o\n"
+"il nome di un remote da cui prelevare nuove revisioni."
+
+#: builtin/fetch.c:927
+msgid "You need to specify a tag name."
+msgstr "Devi specificare il nome di un tag."
+
+#: builtin/fetch.c:979
+msgid "fetch --all does not take a repository argument"
+msgstr "fetch --all non richiede il repository come argomento"
+
+#: builtin/fetch.c:981
+msgid "fetch --all does not make sense with refspecs"
+msgstr ""
+
+#: builtin/fetch.c:992
+#, c-format
+msgid "No such remote or remote group: %s"
+msgstr ""
+
+#: builtin/fetch.c:1000
+msgid "Fetching a group and specifying refspecs does not make sense"
+msgstr ""
+
+#: builtin/gc.c:63
+#, c-format
+msgid "Invalid %s: '%s'"
+msgstr "%s non valido: '%s'"
+
+#: builtin/gc.c:90
+#, c-format
+msgid "insanely long object directory %.*s"
+msgstr ""
+
+#: builtin/gc.c:221
+#, c-format
+msgid "Auto packing the repository for optimum performance.\n"
+msgstr ""
+
+#: builtin/gc.c:224
+#, c-format
+msgid ""
+"Auto packing the repository for optimum performance. You may also\n"
+"run \"git gc\" manually. See \"git help gc\" for more information.\n"
+msgstr ""
+
+#: builtin/gc.c:251
+msgid ""
+"There are too many unreachable loose objects; run 'git prune' to remove them."
+msgstr ""
+
+#: builtin/grep.c:216
+#, c-format
+msgid "grep: failed to create thread: %s"
+msgstr "grep: creazione del thread non riuscita: %s"
+
+#: builtin/grep.c:402
+#, c-format
+msgid "Failed to chdir: %s"
+msgstr ""
+
+#: builtin/grep.c:478 builtin/grep.c:512
+#, c-format
+msgid "unable to read tree (%s)"
+msgstr "impossibile leggere il tree (%s)"
+
+#: builtin/grep.c:526
+#, c-format
+msgid "unable to grep from object of type %s"
+msgstr ""
+
+#: builtin/grep.c:584
+#, c-format
+msgid "switch `%c' expects a numerical value"
+msgstr "switch '%c' richiede un valore numerico"
+
+#: builtin/grep.c:601
+#, c-format
+msgid "cannot open '%s'"
+msgstr "impossibile aprire '%s'"
+
+#: builtin/grep.c:885
+msgid "no pattern given."
+msgstr "nessun modello specificato."
+
+#: builtin/grep.c:899
+#, c-format
+msgid "bad object %s"
+msgstr "oggetto %s errato"
+
+#: builtin/grep.c:940
+msgid "--open-files-in-pager only works on the worktree"
+msgstr ""
+
+#: builtin/grep.c:963
+msgid "--cached or --untracked cannot be used with --no-index."
+msgstr "--cached o --untracked non può essere usato con --no-index."
+
+#: builtin/grep.c:968
+msgid "--no-index or --untracked cannot be used with revs."
+msgstr "--no-index o --untracked non possono essere usate con le revisioni."
+
+#: builtin/grep.c:971
+msgid "--[no-]exclude-standard cannot be used for tracked contents."
+msgstr ""
+
+#: builtin/grep.c:979
+msgid "both --cached and trees are given."
+msgstr ""
+
+#: builtin/help.c:59
+#, c-format
+msgid "unrecognized help format '%s'"
+msgstr "formato di aiuto '%s' non riconosciuto"
+
+#: builtin/help.c:87
+msgid "Failed to start emacsclient."
+msgstr "Esecuzione di emacsclient non riuscita."
+
+#: builtin/help.c:100
+msgid "Failed to parse emacsclient version."
+msgstr "Verifica della versione di emacsclient non riuscita."
+
+#: builtin/help.c:108
+#, c-format
+msgid "emacsclient version '%d' too old (< 22)."
+msgstr "la versione '%d' di emacsclient è troppo vecchia (<22)."
+
+#: builtin/help.c:126 builtin/help.c:154 builtin/help.c:163 builtin/help.c:171
+#, c-format
+msgid "failed to exec '%s': %s"
+msgstr "esecuzione di '%s' non riuscita: %s"
+
+#: builtin/help.c:211
+#, c-format
+msgid ""
+"'%s': path for unsupported man viewer.\n"
+"Please consider using 'man.<tool>.cmd' instead."
+msgstr ""
+"'%s': path ad un visualizzatore man pages non supportato.\n"
+"Usa invece 'man.<tool>.cmd'."
+
+#: builtin/help.c:223
+#, c-format
+msgid ""
+"'%s': cmd for supported man viewer.\n"
+"Please consider using 'man.<tool>.path' instead."
+msgstr ""
+"'%s': comando per visualizzatore man pages supportato.\n"
+"Per favore usa 'man.<tool>.path' invece."
+
+#: builtin/help.c:287
+msgid "The most commonly used git commands are:"
+msgstr "I comandi git usati più di frequente sono:"
+
+#: builtin/help.c:355
+#, c-format
+msgid "'%s': unknown man viewer."
+msgstr "'%s': visualizzatore man sconosciuto."
+
+#: builtin/help.c:372
+msgid "no man viewer handled the request"
+msgstr "nessun visualizzatore man ha gestito la richiesta"
+
+#: builtin/help.c:380
+msgid "no info viewer handled the request"
+msgstr "nessun visualizzatore info ha gestito la richiesta"
+
+#: builtin/help.c:391
+#, c-format
+msgid "'%s': not a documentation directory."
+msgstr "'%s': non è una directory della documentazione."
+
+#: builtin/help.c:432 builtin/help.c:439
+#, c-format
+msgid "usage: %s%s"
+msgstr "uso: %s%s"
+
+#: builtin/help.c:453
+#, c-format
+msgid "`git %s' is aliased to `%s'"
+msgstr "'git %s è un alias di '%s'"
+
+#: builtin/index-pack.c:169
+#, c-format
+msgid "object type mismatch at %s"
+msgstr ""
+
+#: builtin/index-pack.c:189
+msgid "object of unexpected type"
+msgstr ""
+
+#: builtin/index-pack.c:226
+#, c-format
+msgid "cannot fill %d byte"
+msgid_plural "cannot fill %d bytes"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/index-pack.c:236
+msgid "early EOF"
+msgstr "EOF prematuro"
+
+#: builtin/index-pack.c:237
+msgid "read error on input"
+msgstr "errore di lettura in input"
+
+#: builtin/index-pack.c:249
+msgid "used more bytes than were available"
+msgstr "usati più byte di quelli disponibili"
+
+#: builtin/index-pack.c:256
+msgid "pack too large for current definition of off_t"
+msgstr "pack troppo largo per la definizione corrente di off_t"
+
+#: builtin/index-pack.c:272
+#, c-format
+msgid "unable to create '%s'"
+msgstr "impossibile creare '%s'"
+
+#: builtin/index-pack.c:277
+#, c-format
+msgid "cannot open packfile '%s'"
+msgstr "impossibile aprire il file pack '%s'"
+
+#: builtin/index-pack.c:291
+msgid "pack signature mismatch"
+msgstr "la firma del pack non coincide"
+
+#: builtin/index-pack.c:311
+#, c-format
+msgid "pack has bad object at offset %lu: %s"
+msgstr ""
+
+#: builtin/index-pack.c:405
+#, c-format
+msgid "inflate returned %d"
+msgstr ""
+
+#: builtin/index-pack.c:450
+msgid "offset value overflow for delta base object"
+msgstr ""
+
+#: builtin/index-pack.c:458
+msgid "delta base offset is out of bound"
+msgstr ""
+
+#: builtin/index-pack.c:466
+#, c-format
+msgid "unknown object type %d"
+msgstr "tipo di oggetto %d sconosciuto"
+
+#: builtin/index-pack.c:495
+msgid "cannot pread pack file"
+msgstr ""
+
+#: builtin/index-pack.c:497
+#, c-format
+msgid "premature end of pack file, %lu byte missing"
+msgid_plural "premature end of pack file, %lu bytes missing"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/index-pack.c:510
+msgid "serious inflate inconsistency"
+msgstr ""
+
+#: builtin/index-pack.c:583
+#, c-format
+msgid "cannot read existing object %s"
+msgstr "non è possibile leggere l'oggetto %s esistente"
+
+#: builtin/index-pack.c:586
+#, c-format
+msgid "SHA1 COLLISION FOUND WITH %s !"
+msgstr "TROVATA COLLISIONE SHA1 CON %s !"
+
+#: builtin/index-pack.c:598
+#, c-format
+msgid "invalid blob object %s"
+msgstr "oggetto blob %s non valido"
+
+#: builtin/index-pack.c:610
+#, c-format
+msgid "invalid %s"
+msgstr "%s non valido"
+
+#: builtin/index-pack.c:612
+msgid "Error in object"
+msgstr "Errore nell'oggetto"
+
+#: builtin/index-pack.c:614
+#, c-format
+msgid "Not all child objects of %s are reachable"
+msgstr "Non tutti gli oggetti figlio di %s sono raggiungibili"
+
+#: builtin/index-pack.c:687 builtin/index-pack.c:713
+msgid "failed to apply delta"
+msgstr "applicazione del delta non riuscita"
+
+#: builtin/index-pack.c:850
+msgid "Receiving objects"
+msgstr "Ricezione degli oggetti"
+
+#: builtin/index-pack.c:850
+msgid "Indexing objects"
+msgstr "Indicizzazione degli oggetti"
+
+#: builtin/index-pack.c:872
+msgid "pack is corrupted (SHA1 mismatch)"
+msgstr "il pack è corrotto (SHA1 non corrisponde)"
+
+#: builtin/index-pack.c:877
+msgid "cannot fstat packfile"
+msgstr ""
+
+#: builtin/index-pack.c:880
+msgid "pack has junk at the end"
+msgstr ""
+
+#: builtin/index-pack.c:903
+msgid "Resolving deltas"
+msgstr "Risoluzione dei delta"
+
+#: builtin/index-pack.c:954
+msgid "confusion beyond insanity"
+msgstr "confusione al di là della follia"
+
+#: builtin/index-pack.c:973
+#, c-format
+msgid "pack has %d unresolved delta"
+msgid_plural "pack has %d unresolved deltas"
+msgstr[0] "pack ha %d delta irrisolto"
+msgstr[1] "pack ha %d delta irrisolti"
+
+#: builtin/index-pack.c:998
+#, c-format
+msgid "unable to deflate appended object (%d)"
+msgstr ""
+
+#: builtin/index-pack.c:1077
+#, c-format
+msgid "local object %s is corrupt"
+msgstr "l'oggetto locale %s è corrotto"
+
+#: builtin/index-pack.c:1101
+msgid "error while closing pack file"
+msgstr "errore nella chiusura del file pack"
+
+#: builtin/index-pack.c:1114
+#, c-format
+msgid "cannot write keep file '%s'"
+msgstr "impossibile scrivere il file keep '%s'"
+
+#: builtin/index-pack.c:1122
+#, c-format
+msgid "cannot close written keep file '%s'"
+msgstr ""
+
+#: builtin/index-pack.c:1135
+msgid "cannot store pack file"
+msgstr "impossibile archiviare il file pack"
+
+#: builtin/index-pack.c:1146
+msgid "cannot store index file"
+msgstr "impossibile archiviare index file"
+
+#: builtin/index-pack.c:1247
+#, c-format
+msgid "Cannot open existing pack file '%s'"
+msgstr "Impossibile aprire il file pack '%s' esistente"
+
+#: builtin/index-pack.c:1249
+#, c-format
+msgid "Cannot open existing pack idx file for '%s'"
+msgstr ""
+
+#: builtin/index-pack.c:1296
+#, c-format
+msgid "non delta: %d object"
+msgid_plural "non delta: %d objects"
+msgstr[0] "non delta: %d oggetto"
+msgstr[1] "non delta: %d oggetti"
+
+#: builtin/index-pack.c:1303
+#, c-format
+msgid "chain length = %d: %lu object"
+msgid_plural "chain length = %d: %lu objects"
+msgstr[0] "lunghezza della catena = %d: %lu oggetto"
+msgstr[1] "lunghezza della catena = %d: %lu oggetti"
+
+#: builtin/index-pack.c:1330
+msgid "Cannot come back to cwd"
+msgstr ""
+
+#: builtin/index-pack.c:1374 builtin/index-pack.c:1377
+#: builtin/index-pack.c:1389 builtin/index-pack.c:1393
+#, c-format
+msgid "bad %s"
+msgstr "%s errato"
+
+#: builtin/index-pack.c:1407
+msgid "--fix-thin cannot be used without --stdin"
+msgstr "--fix-thin non può essere usato senza --stdin"
+
+#: builtin/index-pack.c:1411 builtin/index-pack.c:1421
+#, c-format
+msgid "packfile name '%s' does not end with '.pack'"
+msgstr "il nome del file pack '%s' non termina con '.pack'"
+
+#: builtin/index-pack.c:1430
+msgid "--verify with no packfile name given"
+msgstr "--verify senza un nome del file pack specificato"
+
+#: builtin/init-db.c:35
+#, c-format
+msgid "Could not make %s writable by group"
+msgstr "Non è stato possible rendere %s scrivibile dal gruppo"
+
+#: builtin/init-db.c:62
+#, c-format
+msgid "insanely long template name %s"
+msgstr ""
+
+#: builtin/init-db.c:67
+#, c-format
+msgid "cannot stat '%s'"
+msgstr "impossibile eseguire lo stat di '%s'"
+
+#: builtin/init-db.c:73
+#, c-format
+msgid "cannot stat template '%s'"
+msgstr ""
+
+#: builtin/init-db.c:80
+#, c-format
+msgid "cannot opendir '%s'"
+msgstr ""
+
+#: builtin/init-db.c:97
+#, c-format
+msgid "cannot readlink '%s'"
+msgstr ""
+
+#: builtin/init-db.c:99
+#, c-format
+msgid "insanely long symlink %s"
+msgstr ""
+
+#: builtin/init-db.c:102
+#, c-format
+msgid "cannot symlink '%s' '%s'"
+msgstr ""
+
+#: builtin/init-db.c:106
+#, c-format
+msgid "cannot copy '%s' to '%s'"
+msgstr ""
+
+#: builtin/init-db.c:110
+#, c-format
+msgid "ignoring template %s"
+msgstr ""
+
+#: builtin/init-db.c:133
+#, c-format
+msgid "insanely long template path %s"
+msgstr ""
+
+#: builtin/init-db.c:141
+#, c-format
+msgid "templates not found %s"
+msgstr ""
+
+#: builtin/init-db.c:154
+#, c-format
+msgid "not copying templates of a wrong format version %d from '%s'"
+msgstr ""
+
+#: builtin/init-db.c:192
+#, c-format
+msgid "insane git directory %s"
+msgstr ""
+
+#: builtin/init-db.c:322 builtin/init-db.c:325
+#, c-format
+msgid "%s already exists"
+msgstr "%s esiste già"
+
+#: builtin/init-db.c:354
+#, c-format
+msgid "unable to handle file type %d"
+msgstr "impossibile gestire il tipo di file %d"
+
+#: builtin/init-db.c:357
+#, c-format
+msgid "unable to move %s to %s"
+msgstr "impossibile spostare %s in %s"
+
+#: builtin/init-db.c:362
+#, c-format
+msgid "Could not create git link %s"
+msgstr "Non è stato possibile creare il link git %s"
+
+#.
+#. * TRANSLATORS: The first '%s' is either "Reinitialized
+#. * existing" or "Initialized empty", the second " shared" or
+#. * "", and the last '%s%s' is the verbatim directory name.
+#.
+#: builtin/init-db.c:419
+#, c-format
+msgid "%s%s Git repository in %s%s\n"
+msgstr "%s%s repository Git in %s%s\n"
+
+#: builtin/init-db.c:420
+msgid "Reinitialized existing"
+msgstr "Reinizializzato un esistente"
+
+#: builtin/init-db.c:420
+msgid "Initialized empty"
+msgstr "Inizializzato un"
+
+#: builtin/init-db.c:421
+msgid " shared"
+msgstr " condiviso"
+
+#: builtin/init-db.c:440
+msgid "cannot tell cwd"
+msgstr ""
+
+#: builtin/init-db.c:521 builtin/init-db.c:528
+#, c-format
+msgid "cannot mkdir %s"
+msgstr ""
+
+#: builtin/init-db.c:532
+#, c-format
+msgid "cannot chdir to %s"
+msgstr ""
+
+#: builtin/init-db.c:554
+#, c-format
+msgid ""
+"%s (or --work-tree=<directory>) not allowed without specifying %s (or --git-"
+"dir=<directory>)"
+msgstr ""
+"%s (o --work-tree=<directory>) non consentito senza specificare %s (o --git-"
+"dir=<directory>)"
+
+#: builtin/init-db.c:578
+msgid "Cannot access current working directory"
+msgstr "Impossibile accedere alla directory di lavoro corrente"
+
+#: builtin/init-db.c:585
+#, c-format
+msgid "Cannot access work tree '%s'"
+msgstr ""
+
+#: builtin/log.c:188
+#, c-format
+msgid "Final output: %d %s\n"
+msgstr "Output finale: %d %s\n"
+
+#: builtin/log.c:401 builtin/log.c:489
+#, c-format
+msgid "Could not read object %s"
+msgstr "Non è stato possibile leggere l'oggetto %s"
+
+#: builtin/log.c:513
+#, c-format
+msgid "Unknown type: %d"
+msgstr "Tipo sconosciuto: %d"
+
+#: builtin/log.c:602
+msgid "format.headers without value"
+msgstr "format.headers non ha alcun valore"
+
+#: builtin/log.c:676
+msgid "name of output directory is too long"
+msgstr "il nome della directory di output è troppo lungo"
+
+#: builtin/log.c:687
+#, c-format
+msgid "Cannot open patch file %s"
+msgstr "Impossibile aprire il file patch %s"
+
+#: builtin/log.c:701
+msgid "Need exactly one range."
+msgstr ""
+
+#: builtin/log.c:709
+msgid "Not a range."
+msgstr ""
+
+#: builtin/log.c:786
+msgid "Cover letter needs email format"
+msgstr ""
+
+#: builtin/log.c:859
+#, c-format
+msgid "insane in-reply-to: %s"
+msgstr ""
+
+#: builtin/log.c:932
+msgid "Two output directories?"
+msgstr "Due directory di output?"
+
+#: builtin/log.c:1153
+#, c-format
+msgid "bogus committer info %s"
+msgstr ""
+
+#: builtin/log.c:1198
+msgid "-n and -k are mutually exclusive."
+msgstr ""
+
+#: builtin/log.c:1200
+msgid "--subject-prefix and -k are mutually exclusive."
+msgstr ""
+
+#: builtin/log.c:1208
+msgid "--name-only does not make sense"
+msgstr "--name-only non ha senso"
+
+#: builtin/log.c:1210
+msgid "--name-status does not make sense"
+msgstr "--name-status non ha senso"
+
+#: builtin/log.c:1212
+msgid "--check does not make sense"
+msgstr "--check non ha senso"
+
+#: builtin/log.c:1235
+msgid "standard output, or directory, which one?"
+msgstr "standard output, o directory, quale dei due?"
+
+#: builtin/log.c:1237
+#, c-format
+msgid "Could not create directory '%s'"
+msgstr "Non è stato possibile creare la directory '%s'"
+
+#: builtin/log.c:1390
+msgid "Failed to create output files"
+msgstr "Creazione dei file di output non riuscita"
+
+#: builtin/log.c:1494
+#, c-format
+msgid ""
+"Could not find a tracked remote branch, please specify <upstream> manually.\n"
+msgstr ""
+
+#: builtin/log.c:1510 builtin/log.c:1512 builtin/log.c:1524
+#, c-format
+msgid "Unknown commit %s"
+msgstr "Commit %s sconosciuto"
+
+#: builtin/merge.c:90
+msgid "switch `m' requires a value"
+msgstr "lo switch 'm' richiede un valore"
+
+#: builtin/merge.c:127
+#, c-format
+msgid "Could not find merge strategy '%s'.\n"
+msgstr "Non è stato possibile trovare la strategia di merge '%s'.\n"
+
+#: builtin/merge.c:128
+#, c-format
+msgid "Available strategies are:"
+msgstr "Le strategie disponibili sono:"
+
+#: builtin/merge.c:133
+#, c-format
+msgid "Available custom strategies are:"
+msgstr "Le strategie personalizzate disponibili sono:"
+
+#: builtin/merge.c:240
+msgid "could not run stash."
+msgstr "non è stato possibile eseguire stash."
+
+#: builtin/merge.c:245
+msgid "stash failed"
+msgstr "esecuzione di stash non riuscita"
+
+#: builtin/merge.c:250
+#, c-format
+msgid "not a valid object: %s"
+msgstr "non è un oggetto valido: %s"
+
+#: builtin/merge.c:269 builtin/merge.c:286
+msgid "read-tree failed"
+msgstr "read-tree non riuscito"
+
+#: builtin/merge.c:316
+msgid " (nothing to squash)"
+msgstr ""
+
+#: builtin/merge.c:329
+#, c-format
+msgid "Squash commit -- not updating HEAD\n"
+msgstr ""
+
+#: builtin/merge.c:361
+msgid "Writing SQUASH_MSG"
+msgstr "Scrittura di SQUASH_MSG"
+
+#: builtin/merge.c:363
+msgid "Finishing SQUASH_MSG"
+msgstr "Completamento di SQUASH_MSG"
+
+#: builtin/merge.c:386
+#, c-format
+msgid "No merge message -- not updating HEAD\n"
+msgstr "Nessun messaggio di merge -- HEAD non viene aggiornato\n"
+
+#: builtin/merge.c:437
+#, c-format
+msgid "'%s' does not point to a commit"
+msgstr "'%s' non punta ad un commit"
+
+#: builtin/merge.c:536
+#, c-format
+msgid "Bad branch.%s.mergeoptions string: %s"
+msgstr "Stringa branch.%s.mergeoptions errata: %s"
+
+#: builtin/merge.c:629
+msgid "git write-tree failed to write a tree"
+msgstr ""
+
+#: builtin/merge.c:679
+msgid "failed to read the cache"
+msgstr "lettura della cache non riuscita"
+
+#: builtin/merge.c:697
+msgid "Unable to write index."
+msgstr "Impossibile scrivere index."
+
+#: builtin/merge.c:710
+msgid "Not handling anything other than two heads merge."
+msgstr ""
+
+#: builtin/merge.c:724
+#, c-format
+msgid "Unknown option for merge-recursive: -X%s"
+msgstr "Opzione per merge-recursive sconosciuta: -X%s"
+
+#: builtin/merge.c:738
+#, c-format
+msgid "unable to write %s"
+msgstr "non è possibile scrivere %s"
+
+#: builtin/merge.c:877
+#, c-format
+msgid "Could not read from '%s'"
+msgstr "Non è stato possibile leggere da '%s'"
+
+#: builtin/merge.c:886
+#, c-format
+msgid "Not committing merge; use 'git commit' to complete the merge.\n"
+msgstr ""
+
+#: builtin/merge.c:892
+msgid ""
+"Please enter a commit message to explain why this merge is necessary,\n"
+"especially if it merges an updated upstream into a topic branch.\n"
+"\n"
+"Lines starting with '#' will be ignored, and an empty message aborts\n"
+"the commit.\n"
+msgstr ""
+
+#: builtin/merge.c:916
+msgid "Empty commit message."
+msgstr "Messaggio di commit vuoto."
+
+#: builtin/merge.c:928
+#, c-format
+msgid "Wonderful.\n"
+msgstr "Splendido.\n"
+
+#: builtin/merge.c:993
+#, c-format
+msgid "Automatic merge failed; fix conflicts and then commit the result.\n"
+msgstr ""
+"Merge automatico fallito; risolvi i conflitti ed eseguire il commit\n"
+"del risultato.\n"
+
+#: builtin/merge.c:1009
+#, c-format
+msgid "'%s' is not a commit"
+msgstr "'%s' non è un commit"
+
+#: builtin/merge.c:1050
+msgid "No current branch."
+msgstr "Nessun branch corrente."
+
+#: builtin/merge.c:1052
+msgid "No remote for the current branch."
+msgstr "Nessun remote per il branch corrente."
+
+#: builtin/merge.c:1054
+msgid "No default upstream defined for the current branch."
+msgstr "Nessun upstream di default definito per il branch corrente."
+
+#: builtin/merge.c:1059
+#, c-format
+msgid "No remote tracking branch for %s from %s"
+msgstr ""
+
+#: builtin/merge.c:1146 builtin/merge.c:1303
+#, c-format
+msgid "%s - not something we can merge"
+msgstr "%s - non è qualcosa per cui possiamo eseguire il merge"
+
+#: builtin/merge.c:1214
+msgid "There is no merge to abort (MERGE_HEAD missing)."
+msgstr "Non c'è nessun merge da interrompere (manca MERGE_HEAD)"
+
+#: builtin/merge.c:1230 git-pull.sh:31
+msgid ""
+"You have not concluded your merge (MERGE_HEAD exists).\n"
+"Please, commit your changes before you can merge."
+msgstr ""
+"Il merge non è stato concluso (esiste MERGE_HEAD).\n"
+"Per favore, esegui il commit delle modifiche prima del merge."
+
+#: builtin/merge.c:1233 git-pull.sh:34
+msgid "You have not concluded your merge (MERGE_HEAD exists)."
+msgstr "Il merge non è stato concluso (esiste MERGE_HEAD)."
+
+#: builtin/merge.c:1237
+msgid ""
+"You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
+"Please, commit your changes before you can merge."
+msgstr ""
+"Il cherry-pick non è stato concluso (esiste CHERRY_PICK_HEAD).\n"
+"Per favore, esegui il commit delle modifiche prima del merge."
+
+#: builtin/merge.c:1240
+msgid "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."
+msgstr "Il tuo cherry-pick non è stato concluso (CHERRY_PICK_HEAD esiste)."
+
+#: builtin/merge.c:1249
+msgid "You cannot combine --squash with --no-ff."
+msgstr "Impossibile combinare --squash con --no-off."
+
+#: builtin/merge.c:1254
+msgid "You cannot combine --no-ff with --ff-only."
+msgstr "Impossibile combinare --no-ff con --ff-only."
+
+#: builtin/merge.c:1261
+msgid "No commit specified and merge.defaultToUpstream not set."
+msgstr "Nessun commit specificato e merge.defaultToUpstream non definito."
+
+#: builtin/merge.c:1293
+msgid "Can merge only exactly one commit into empty head"
+msgstr ""
+
+#: builtin/merge.c:1296
+msgid "Squash commit into empty head not supported yet"
+msgstr ""
+
+#: builtin/merge.c:1298
+msgid "Non-fast-forward commit does not make sense into an empty head"
+msgstr ""
+
+#: builtin/merge.c:1413
+#, c-format
+msgid "Updating %s..%s\n"
+msgstr "Aggiornamento di %s..%s\n"
+
+#: builtin/merge.c:1451
+#, c-format
+msgid "Trying really trivial in-index merge...\n"
+msgstr ""
+
+#: builtin/merge.c:1458
+#, c-format
+msgid "Nope.\n"
+msgstr "No.\n"
+
+#: builtin/merge.c:1490
+msgid "Not possible to fast-forward, aborting."
+msgstr "Fast-forward non possibile, stop."
+
+#: builtin/merge.c:1513 builtin/merge.c:1592
+#, c-format
+msgid "Rewinding the tree to pristine...\n"
+msgstr ""
+
+#: builtin/merge.c:1517
+#, c-format
+msgid "Trying merge strategy %s...\n"
+msgstr "Tentativo con la strategia di merge %s...\n"
+
+#: builtin/merge.c:1583
+#, c-format
+msgid "No merge strategy handled the merge.\n"
+msgstr "Nessuna strategia di merge ha gestito il merge.\n"
+
+#: builtin/merge.c:1585
+#, c-format
+msgid "Merge with strategy %s failed.\n"
+msgstr "Merge con la strategia %s fallito.\n"
+
+#: builtin/merge.c:1594
+#, c-format
+msgid "Using the %s to prepare resolving by hand.\n"
+msgstr ""
+
+#: builtin/merge.c:1606
+#, c-format
+msgid "Automatic merge went well; stopped before committing as requested\n"
+msgstr ""
+"Il merge automatico è andato a buon fine; fermato prima del commit come "
+"richiesto\n"
+
+#: builtin/mv.c:108
+#, c-format
+msgid "Checking rename of '%s' to '%s'\n"
+msgstr ""
+
+#: builtin/mv.c:112
+msgid "bad source"
+msgstr ""
+
+#: builtin/mv.c:115
+msgid "can not move directory into itself"
+msgstr "non è possibile spostare la directory in sé stessa"
+
+#: builtin/mv.c:118
+msgid "cannot move directory over file"
+msgstr "non è possibile spostare la directory su un file"
+
+#: builtin/mv.c:128
+#, c-format
+msgid "Huh? %.*s is in index?"
+msgstr "Eh? %.*s si trova in index?"
+
+#: builtin/mv.c:140
+msgid "source directory is empty"
+msgstr "la directory sorgente è vuota"
+
+#: builtin/mv.c:171
+msgid "not under version control"
+msgstr "non è sotto controllo di versione"
+
+#: builtin/mv.c:173
+msgid "destination exists"
+msgstr "la destinazione esiste"
+
+#: builtin/mv.c:181
+#, c-format
+msgid "overwriting '%s'"
+msgstr "sovrascrittura di %s in corso"
+
+#: builtin/mv.c:184
+msgid "Cannot overwrite"
+msgstr "Impossibile sovrascrivere"
+
+#: builtin/mv.c:187
+msgid "multiple sources for the same target"
+msgstr "fonti multiple per la stessa destinazione"
+
+#: builtin/mv.c:202
+#, c-format
+msgid "%s, source=%s, destination=%s"
+msgstr "%s, sorgente=%s, destinazione=%s"
+
+#: builtin/mv.c:212
+#, c-format
+msgid "Renaming %s to %s\n"
+msgstr "Rinominazione di %s in %s in corso\n"
+
+#: builtin/mv.c:215 builtin/remote.c:731
+#, c-format
+msgid "renaming '%s' failed"
+msgstr "rinomina di '%s' non riuscita"
+
+#: builtin/notes.c:139
+#, c-format
+msgid "unable to start 'show' for object '%s'"
+msgstr "impossibile avviare 'show' per l'oggetto '%s'"
+
+#: builtin/notes.c:145
+msgid "can't fdopen 'show' output fd"
+msgstr ""
+
+#: builtin/notes.c:155
+#, c-format
+msgid "failed to close pipe to 'show' for object '%s'"
+msgstr ""
+
+#: builtin/notes.c:158
+#, c-format
+msgid "failed to finish 'show' for object '%s'"
+msgstr ""
+
+#: builtin/notes.c:175 builtin/tag.c:347
+#, c-format
+msgid "could not create file '%s'"
+msgstr "non è stato possibile creare il file '%s'"
+
+#: builtin/notes.c:189
+msgid "Please supply the note contents using either -m or -F option"
+msgstr "Per favore specifica il contenuto delle note usando le opzioni -m o -F"
+
+#: builtin/notes.c:210 builtin/notes.c:973
+#, c-format
+msgid "Removing note for object %s\n"
+msgstr "Rimozione della nota per l'oggetto %s\n"
+
+#: builtin/notes.c:215
+msgid "unable to write note object"
+msgstr "impossibile scrivere l'oggetto nota"
+
+#: builtin/notes.c:217
+#, c-format
+msgid "The note contents has been left in %s"
+msgstr "Il contenuto della nota è stato lasciato in %s"
+
+#: builtin/notes.c:251 builtin/tag.c:542
+#, c-format
+msgid "cannot read '%s'"
+msgstr "impossibile leggere '%s'"
+
+#: builtin/notes.c:253 builtin/tag.c:545
+#, c-format
+msgid "could not open or read '%s'"
+msgstr "non è stato possibile aprire o leggere '%s'"
+
+#: builtin/notes.c:272 builtin/notes.c:445 builtin/notes.c:447
+#: builtin/notes.c:507 builtin/notes.c:561 builtin/notes.c:644
+#: builtin/notes.c:649 builtin/notes.c:724 builtin/notes.c:766
+#: builtin/notes.c:968 builtin/reset.c:293 builtin/tag.c:558
+#, c-format
+msgid "Failed to resolve '%s' as a valid ref."
+msgstr ""
+
+#: builtin/notes.c:275
+#, c-format
+msgid "Failed to read object '%s'."
+msgstr "Lettura dell'oggetto '%s' non riuscita."
+
+#: builtin/notes.c:299
+msgid "Cannot commit uninitialized/unreferenced notes tree"
+msgstr ""
+
+#: builtin/notes.c:340
+#, c-format
+msgid "Bad notes.rewriteMode value: '%s'"
+msgstr "Valore di notes.rewriteMode errato: '%s'"
+
+#: builtin/notes.c:350
+#, c-format
+msgid "Refusing to rewrite notes in %s (outside of refs/notes/)"
+msgstr "Impossibile riscrivere le note in %s (al di fuori di refs/notes/)"
+
+#. TRANSLATORS: The first %s is the name of the
+#. environment variable, the second %s is its value
+#: builtin/notes.c:377
+#, c-format
+msgid "Bad %s value: '%s'"
+msgstr "Valore di %s errato: '%s'"
+
+#: builtin/notes.c:441
+#, c-format
+msgid "Malformed input line: '%s'."
+msgstr "Riga di input malformata: '%s'."
+
+#: builtin/notes.c:456
+#, c-format
+msgid "Failed to copy notes from '%s' to '%s'"
+msgstr "Copia delle note da '%s' a '%s' non riuscita"
+
+#: builtin/notes.c:500 builtin/notes.c:554 builtin/notes.c:627
+#: builtin/notes.c:639 builtin/notes.c:712 builtin/notes.c:759
+#: builtin/notes.c:1033
+msgid "too many parameters"
+msgstr "troppi parametri"
+
+#: builtin/notes.c:513 builtin/notes.c:772
+#, c-format
+msgid "No note found for object %s."
+msgstr "Nessuna nota trovata per l'oggetto %s."
+
+#: builtin/notes.c:580
+#, c-format
+msgid ""
+"Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite "
+"existing notes"
+msgstr ""
+
+#: builtin/notes.c:585 builtin/notes.c:662
+#, c-format
+msgid "Overwriting existing notes for object %s\n"
+msgstr "Sovrascrittura delle note esistenti per l'oggetto %s\n"
+
+#: builtin/notes.c:635
+msgid "too few parameters"
+msgstr "troppi pochi parametri"
+
+#: builtin/notes.c:656
+#, c-format
+msgid ""
+"Cannot copy notes. Found existing notes for object %s. Use '-f' to overwrite "
+"existing notes"
+msgstr ""
+"Impossibile copiare le note. Trovate note esistenti per l'oggetto %s. Usa "
+"'-f' per sovrascrivere le note esistenti"
+
+#: builtin/notes.c:668
+#, c-format
+msgid "Missing notes on source object %s. Cannot copy."
+msgstr "Note mancanti per l'oggetto sorgente %s. Impossibile copiare."
+
+#: builtin/notes.c:717
+#, c-format
+msgid ""
+"The -m/-F/-c/-C options have been deprecated for the 'edit' subcommand.\n"
+"Please use 'git notes add -f -m/-F/-c/-C' instead.\n"
+msgstr ""
+"Le opzioni -m/-F/-c/-C per il sottocomando 'edit' sono deprecate.\n"
+"Per favore usa 'git notes add -f -m/-F/-c/-C' invece.\n"
+
+#: builtin/notes.c:971
+#, c-format
+msgid "Object %s has no note\n"
+msgstr "L'oggetto %s non ha note.\n"
+
+#: builtin/notes.c:1103 builtin/remote.c:1598
+#, c-format
+msgid "Unknown subcommand: %s"
+msgstr "Sottocomando sconosciuto: %s"
+
+#: builtin/pack-objects.c:2337
+#, c-format
+msgid "unsupported index version %s"
+msgstr "versione %s di index non supportata"
+
+#: builtin/pack-objects.c:2341
+#, c-format
+msgid "bad index version '%s'"
+msgstr "versione '%s' di index errata"
+
+#: builtin/pack-objects.c:2364
+#, c-format
+msgid "option %s does not accept negative form"
+msgstr "l'opzione %s non accetta forme negative"
+
+#: builtin/pack-objects.c:2368
+#, c-format
+msgid "unable to parse value '%s' for option %s"
+msgstr "impossibile analizzare il valore '%s' per l'opzione %s"
+
+#: builtin/push.c:45
+msgid "tag shorthand without <tag>"
+msgstr ""
+
+#: builtin/push.c:64
+msgid "--delete only accepts plain target ref names"
+msgstr "--delete accetta solo nomi dei ref di destinazione in chiaro"
+
+#: builtin/push.c:99
+msgid ""
+"\n"
+"To choose either option permanently, see push.default in 'git help config'."
+msgstr ""
+
+#: builtin/push.c:102
+#, c-format
+msgid ""
+"The upstream branch of your current branch does not match\n"
+"the name of your current branch.  To push to the upstream branch\n"
+"on the remote, use\n"
+"\n"
+"    git push %s HEAD:%s\n"
+"\n"
+"To push to the branch of the same name on the remote, use\n"
+"\n"
+"    git push %s %s\n"
+"%s"
+msgstr ""
+
+#: builtin/push.c:121
+#, c-format
+msgid ""
+"You are not currently on a branch.\n"
+"To push the history leading to the current (detached HEAD)\n"
+"state now, use\n"
+"\n"
+"    git push %s HEAD:<name-of-remote-branch>\n"
+msgstr ""
+
+#: builtin/push.c:128
+#, c-format
+msgid ""
+"The current branch %s has no upstream branch.\n"
+"To push the current branch and set the remote as upstream, use\n"
+"\n"
+"    git push --set-upstream %s %s\n"
+msgstr ""
+"Il branch corrente %s non ha alcun branch upstream.\n"
+"Per eseguire il push del branch corrente ed impostare remote come upstream, usa\n"
+"\n"
+"    git push --set-upstream %s %s\n"
+
+#: builtin/push.c:136
+#, c-format
+msgid "The current branch %s has multiple upstream branches, refusing to push."
+msgstr "Il branch corrente %s ha branch multipli in upstream; push non eseguito."
+
+#: builtin/push.c:139
+#, c-format
+msgid ""
+"You are pushing to remote '%s', which is not the upstream of\n"
+"your current branch '%s', without telling me what to push\n"
+"to update which remote branch."
+msgstr ""
+
+#: builtin/push.c:174
+msgid ""
+"You didn't specify any refspecs to push, and push.default is \"nothing\"."
+msgstr ""
+"Non è stato specificato alcun refspec per il push, e push.default è \"nothing\"."
+
+#: builtin/push.c:181
+msgid ""
+"Updates were rejected because the tip of your current branch is behind\n"
+"its remote counterpart. Merge the remote changes (e.g. 'git pull')\n"
+"before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
+msgstr ""
+
+#: builtin/push.c:187
+msgid ""
+"Updates were rejected because a pushed branch tip is behind its remote\n"
+"counterpart. If you did not intend to push that branch, you may want to\n"
+"specify branches to push or set the 'push.default' configuration\n"
+"variable to 'current' or 'upstream' to push only the current branch."
+msgstr ""
+
+#: builtin/push.c:193
+msgid ""
+"Updates were rejected because a pushed branch tip is behind its remote\n"
+"counterpart. Check out this branch and merge the remote changes\n"
+"(e.g. 'git pull') before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
+msgstr ""
+
+#: builtin/push.c:233
+#, c-format
+msgid "Pushing to %s\n"
+msgstr ""
+
+#: builtin/push.c:237
+#, c-format
+msgid "failed to push some refs to '%s'"
+msgstr ""
+
+#: builtin/push.c:269
+#, c-format
+msgid "bad repository '%s'"
+msgstr "repository '%s' errato"
+
+#: builtin/push.c:270
+msgid ""
+"No configured push destination.\n"
+"Either specify the URL from the command-line or configure a remote "
+"repository using\n"
+"\n"
+"    git remote add <name> <url>\n"
+"\n"
+"and then push using the remote name\n"
+"\n"
+"    git push <name>\n"
+msgstr ""
+"Nessuna destinazione per il push configurata.\n"
+"Specifica un URL dalla riga di comando oppure configurare un repository "
+"remoto usando\n"
+"\n"
+"    git remote add <nome> <url>\n"
+"\n"
+"e poi eseguire il push usando il nome del remote\n"
+"\n"
+"    git push <nome>\n"
+
+#: builtin/push.c:285
+msgid "--all and --tags are incompatible"
+msgstr "--all e --tags non sono compatibili"
+
+#: builtin/push.c:286
+msgid "--all can't be combined with refspecs"
+msgstr ""
+
+#: builtin/push.c:291
+msgid "--mirror and --tags are incompatible"
+msgstr "--mirror e --tags non sono compatibili"
+
+#: builtin/push.c:292
+msgid "--mirror can't be combined with refspecs"
+msgstr ""
+
+#: builtin/push.c:297
+msgid "--all and --mirror are incompatible"
+msgstr "--all e --mirror non sono compatibili"
+
+#: builtin/push.c:385
+msgid "--delete is incompatible with --all, --mirror and --tags"
+msgstr "--delete non è compatibile con --all, --mirror e --tags"
+
+#: builtin/push.c:387
+msgid "--delete doesn't make sense without any refs"
+msgstr "--delete non ha senso senza alcun ref"
+
+#: builtin/remote.c:98
+#, c-format
+msgid "Updating %s"
+msgstr "Aggiornamento di %s"
+
+#: builtin/remote.c:130
+msgid ""
+"--mirror is dangerous and deprecated; please\n"
+"\t use --mirror=fetch or --mirror=push instead"
+msgstr ""
+"--mirror è pericoloso e deprecato; per favore\n"
+"\t usa invece --mirror-fetch o --mirror-push"
+
+#: builtin/remote.c:147
+#, c-format
+msgid "unknown mirror argument: %s"
+msgstr "argomento di mirror sconosciuto: %s"
+
+#: builtin/remote.c:185
+msgid "specifying a master branch makes no sense with --mirror"
+msgstr "specificare un branch master con --mirror non ha senso"
+
+#: builtin/remote.c:187
+msgid "specifying branches to track makes sense only with fetch mirrors"
+msgstr ""
+
+#: builtin/remote.c:195 builtin/remote.c:646
+#, c-format
+msgid "remote %s already exists."
+msgstr "il remoto %s esiste già."
+
+#: builtin/remote.c:199 builtin/remote.c:650
+#, c-format
+msgid "'%s' is not a valid remote name"
+msgstr "'%s' non è un nome di remoto valido"
+
+#: builtin/remote.c:243
+#, c-format
+msgid "Could not setup master '%s'"
+msgstr "Non è stato possibile configurare il master '%s'"
+
+#: builtin/remote.c:299
+#, c-format
+msgid "more than one %s"
+msgstr "più di un %s"
+
+#: builtin/remote.c:339
+#, c-format
+msgid "Could not get fetch map for refspec %s"
+msgstr ""
+
+#: builtin/remote.c:440 builtin/remote.c:448
+msgid "(matching)"
+msgstr "(corrispondente)"
+
+#: builtin/remote.c:452
+msgid "(delete)"
+msgstr "(elimina)"
+
+#: builtin/remote.c:595 builtin/remote.c:601 builtin/remote.c:607
+#, c-format
+msgid "Could not append '%s' to '%s'"
+msgstr "Non è stato possibile aggiungere '%s' a '%s'"
+
+#: builtin/remote.c:639 builtin/remote.c:792 builtin/remote.c:890
+#, c-format
+msgid "No such remote: %s"
+msgstr "Remote non esistente: %s"
+
+#: builtin/remote.c:656
+#, c-format
+msgid "Could not rename config section '%s' to '%s'"
+msgstr "Non è stato possibile rinominare la sezione di configurazione da '%s' in '%s'"
+
+#: builtin/remote.c:662 builtin/remote.c:799
+#, c-format
+msgid "Could not remove config section '%s'"
+msgstr "Non è stato possibile rimuovere la sezione di configurazione '%s'"
+
+#: builtin/remote.c:677
+#, c-format
+msgid ""
+"Not updating non-default fetch refspec\n"
+"\t%s\n"
+"\tPlease update the configuration manually if necessary."
+msgstr ""
+
+#: builtin/remote.c:683
+#, c-format
+msgid "Could not append '%s'"
+msgstr ""
+
+#: builtin/remote.c:694
+#, c-format
+msgid "Could not set '%s'"
+msgstr "Non è stato possibile impostare '%s'"
+
+#: builtin/remote.c:716
+#, c-format
+msgid "deleting '%s' failed"
+msgstr "eliminazione di '%s' non riuscita"
+
+#: builtin/remote.c:750
+#, c-format
+msgid "creating '%s' failed"
+msgstr "creazione di '%s' non riuscita"
+
+#: builtin/remote.c:764
+#, c-format
+msgid "Could not remove branch %s"
+msgstr "Non è stato possibile rimuovere il branch %s"
+
+#: builtin/remote.c:834
+msgid ""
+"Note: A branch outside the refs/remotes/ hierarchy was not removed;\n"
+"to delete it, use:"
+msgid_plural ""
+"Note: Some branches outside the refs/remotes/ hierarchy were not removed;\n"
+"to delete them, use:"
+msgstr[0] ""
+"Nota: un branch al di fuori della gerarchia refs/remotes/ non è stato "
+"eliminato;\n"
+"per eliminarlo, usare:"
+msgstr[1] ""
+"Nota: alcuni branch al di fuori della gerarchia refs/remotes/ non sono stati "
+"eliminati;\n"
+"per eliminarli, usare:"
+
+#: builtin/remote.c:943
+#, c-format
+msgid " new (next fetch will store in remotes/%s)"
+msgstr ""
+
+#: builtin/remote.c:946
+msgid " tracked"
+msgstr ""
+
+#: builtin/remote.c:948
+msgid " stale (use 'git remote prune' to remove)"
+msgstr " vecchio (usare 'git remote prune' per rimuoverlo)"
+
+#: builtin/remote.c:950
+msgid " ???"
+msgstr "???"
+
+#: builtin/remote.c:991
+#, c-format
+msgid "invalid branch.%s.merge; cannot rebase onto > 1 branch"
+msgstr ""
+"branch.%s.merge non valido; impossibile eseguire il rebase su > 1 branch"
+
+#: builtin/remote.c:998
+#, c-format
+msgid "rebases onto remote %s"
+msgstr ""
+
+#: builtin/remote.c:1001
+#, c-format
+msgid " merges with remote %s"
+msgstr " merge con il remote %s"
+
+#: builtin/remote.c:1002
+msgid "    and with remote"
+msgstr "    e con il remote"
+
+#: builtin/remote.c:1004
+#, c-format
+msgid "merges with remote %s"
+msgstr "merge con il remote %s"
+
+#: builtin/remote.c:1005
+msgid "   and with remote"
+msgstr ""
+
+#: builtin/remote.c:1051
+msgid "create"
+msgstr "crea"
+
+#: builtin/remote.c:1054
+msgid "delete"
+msgstr "elimina"
+
+#: builtin/remote.c:1058
+msgid "up to date"
+msgstr "aggiornato"
+
+#: builtin/remote.c:1061
+msgid "fast-forwardable"
+msgstr ""
+
+#: builtin/remote.c:1064
+msgid "local out of date"
+msgstr "locale non aggiornato"
+
+#: builtin/remote.c:1071
+#, c-format
+msgid "    %-*s forces to %-*s (%s)"
+msgstr ""
+
+#: builtin/remote.c:1074
+#, c-format
+msgid "    %-*s pushes to %-*s (%s)"
+msgstr ""
+
+#: builtin/remote.c:1078
+#, c-format
+msgid "    %-*s forces to %s"
+msgstr ""
+
+#: builtin/remote.c:1081
+#, c-format
+msgid "    %-*s pushes to %s"
+msgstr "    %-*s esegue il push su %s"
+
+#: builtin/remote.c:1118
+#, c-format
+msgid "* remote %s"
+msgstr "* remote %s"
+
+#: builtin/remote.c:1119
+#, c-format
+msgid "  Fetch URL: %s"
+msgstr ""
+
+#: builtin/remote.c:1120 builtin/remote.c:1285
+msgid "(no URL)"
+msgstr "(nessun URL)"
+
+#: builtin/remote.c:1129 builtin/remote.c:1131
+#, c-format
+msgid "  Push  URL: %s"
+msgstr ""
+
+#: builtin/remote.c:1133 builtin/remote.c:1135 builtin/remote.c:1137
+#, c-format
+msgid "  HEAD branch: %s"
+msgstr "  branch HEAD: %s"
+
+#: builtin/remote.c:1139
+#, c-format
+msgid ""
+"  HEAD branch (remote HEAD is ambiguous, may be one of the following):\n"
+msgstr ""
+"  branch HEAD (l'HEAD remoto è ambiguo, potrebbe essere uno dei seguenti):\n"
+
+#: builtin/remote.c:1151
+#, c-format
+msgid "  Remote branch:%s"
+msgid_plural "  Remote branches:%s"
+msgstr[0] "  Branch remoto:%s"
+msgstr[1] "  Branch remoti:%s"
+
+#: builtin/remote.c:1154 builtin/remote.c:1181
+msgid " (status not queried)"
+msgstr ""
+
+#: builtin/remote.c:1163
+msgid "  Local branch configured for 'git pull':"
+msgid_plural "  Local branches configured for 'git pull':"
+msgstr[0] "  Branch locale configurato per 'git pull':"
+msgstr[1] "  Branch locali configurati per 'git pull':"
+
+#: builtin/remote.c:1171
+msgid "  Local refs will be mirrored by 'git push'"
+msgstr ""
+
+#: builtin/remote.c:1178
+#, c-format
+msgid "  Local ref configured for 'git push'%s:"
+msgid_plural "  Local refs configured for 'git push'%s:"
+msgstr[0] "  Ref locale configurato per 'git push'%s:"
+msgstr[1] "  Ref locali configurati per 'git push'%s:"
+
+#: builtin/remote.c:1216
+msgid "Cannot determine remote HEAD"
+msgstr "Impossibile determinare l'HEAD remoto"
+
+#: builtin/remote.c:1218
+msgid "Multiple remote HEAD branches. Please choose one explicitly with:"
+msgstr ""
+
+#: builtin/remote.c:1228
+#, c-format
+msgid "Could not delete %s"
+msgstr "Non è stato possibile eliminare %s"
+
+#: builtin/remote.c:1236
+#, c-format
+msgid "Not a valid ref: %s"
+msgstr "Non è un ref valido: %s"
+
+#: builtin/remote.c:1238
+#, c-format
+msgid "Could not setup %s"
+msgstr "Non è stato possibile configurare %s"
+
+#: builtin/remote.c:1274
+#, c-format
+msgid " %s will become dangling!"
+msgstr ""
+
+#: builtin/remote.c:1275
+#, c-format
+msgid " %s has become dangling!"
+msgstr ""
+
+#: builtin/remote.c:1281
+#, c-format
+msgid "Pruning %s"
+msgstr ""
+
+#: builtin/remote.c:1282
+#, c-format
+msgid "URL: %s"
+msgstr "URL: %s"
+
+#: builtin/remote.c:1295
+#, c-format
+msgid " * [would prune] %s"
+msgstr ""
+
+#: builtin/remote.c:1298
+#, c-format
+msgid " * [pruned] %s"
+msgstr ""
+
+#: builtin/remote.c:1387 builtin/remote.c:1461
+#, c-format
+msgid "No such remote '%s'"
+msgstr "Remote '%s' non esistente"
+
+#: builtin/remote.c:1414
+msgid "no remote specified"
+msgstr "nessun remote specificato"
+
+#: builtin/remote.c:1447
+msgid "--add --delete doesn't make sense"
+msgstr "--add --delete non ha senso"
+
+#: builtin/remote.c:1487
+#, c-format
+msgid "Invalid old URL pattern: %s"
+msgstr ""
+
+#: builtin/remote.c:1495
+#, c-format
+msgid "No such URL found: %s"
+msgstr "Nessuna URL trovata: %s"
+
+#: builtin/remote.c:1497
+msgid "Will not delete all non-push URLs"
+msgstr ""
+
+#: builtin/reset.c:33
+msgid "mixed"
+msgstr ""
+
+#: builtin/reset.c:33
+msgid "soft"
+msgstr ""
+
+#: builtin/reset.c:33
+msgid "hard"
+msgstr ""
+
+#: builtin/reset.c:33
+msgid "merge"
+msgstr ""
+
+#: builtin/reset.c:33
+msgid "keep"
+msgstr ""
+
+#: builtin/reset.c:77
+msgid "You do not have a valid HEAD."
+msgstr ""
+
+#: builtin/reset.c:79
+msgid "Failed to find tree of HEAD."
+msgstr ""
+
+#: builtin/reset.c:85
+#, c-format
+msgid "Failed to find tree of %s."
+msgstr ""
+
+#: builtin/reset.c:96
+msgid "Could not write new index file."
+msgstr "Non è stato possibile scrivere il nuovo index file."
+
+#: builtin/reset.c:106
+#, c-format
+msgid "HEAD is now at %s"
+msgstr "HEAD ora si trova a %s"
+
+#: builtin/reset.c:130
+msgid "Could not read index"
+msgstr "Non è stato possibile leggere index"
+
+#: builtin/reset.c:133
+msgid "Unstaged changes after reset:"
+msgstr ""
+
+#: builtin/reset.c:223
+#, c-format
+msgid "Cannot do a %s reset in the middle of a merge."
+msgstr "Impossibile eseguire un %s reset nel corso di un merge."
+
+#: builtin/reset.c:297
+#, c-format
+msgid "Could not parse object '%s'."
+msgstr "Non è stato possibile analizzare l'oggetto '%s'."
+
+#: builtin/reset.c:302
+msgid "--patch is incompatible with --{hard,mixed,soft}"
+msgstr "--patch non è compatibile con --{hard,mixed,soft}"
+
+#: builtin/reset.c:311
+msgid "--mixed with paths is deprecated; use 'git reset -- <paths>' instead."
+msgstr "--mixed con i path è deprecata; usa invece 'git reset -- <path>'."
+
+#: builtin/reset.c:313
+#, c-format
+msgid "Cannot do %s reset with paths."
+msgstr ""
+
+#: builtin/reset.c:325
+#, c-format
+msgid "%s reset is not allowed in a bare repository"
+msgstr "%s reset non è consentito in un repository spoglio"
+
+#: builtin/reset.c:341
+#, c-format
+msgid "Could not reset index file to revision '%s'."
+msgstr ""
+"Non è stato possibile ripristinare index file "
+"alla revisione '%s'."
+
+#: builtin/revert.c:70 builtin/revert.c:92
+#, c-format
+msgid "%s: %s cannot be used with %s"
+msgstr "%s: %s non può essere usata con %s"
+
+#: builtin/revert.c:131
+msgid "program error"
+msgstr "errore del programma"
+
+#: builtin/revert.c:221
+msgid "revert failed"
+msgstr "revert non riuscito"
+
+#: builtin/revert.c:236
+msgid "cherry-pick failed"
+msgstr "cherry-pick non riuscito"
+
+#: builtin/rm.c:109
+#, c-format
+msgid ""
+"'%s' has staged content different from both the file and the HEAD\n"
+"(use -f to force removal)"
+msgstr ""
+
+#: builtin/rm.c:115
+#, c-format
+msgid ""
+"'%s' has changes staged in the index\n"
+"(use --cached to keep the file, or -f to force removal)"
+msgstr ""
+
+#: builtin/rm.c:119
+#, c-format
+msgid ""
+"'%s' has local modifications\n"
+"(use --cached to keep the file, or -f to force removal)"
+msgstr ""
+"'%s' contiene delle modifiche locali\n"
+"(usa --cached per mantenere il file, o -f per forzare la rimozione)"
+
+#: builtin/rm.c:194
+#, c-format
+msgid "not removing '%s' recursively without -r"
+msgstr ""
+
+#: builtin/rm.c:230
+#, c-format
+msgid "git rm: unable to remove %s"
+msgstr "git rm: non è possibile eliminare %s"
+
+#: builtin/shortlog.c:157
+#, c-format
+msgid "Missing author: %s"
+msgstr "Autore mancante: %s"
+
+#: builtin/tag.c:60
+#, c-format
+msgid "malformed object at '%s'"
+msgstr ""
+
+#: builtin/tag.c:207
+#, c-format
+msgid "tag name too long: %.*s..."
+msgstr "nome tag troppo lungo: %.*s..."
+
+#: builtin/tag.c:212
+#, c-format
+msgid "tag '%s' not found."
+msgstr "tag '%s' non trovato."
+
+#: builtin/tag.c:227
+#, c-format
+msgid "Deleted tag '%s' (was %s)\n"
+msgstr "Tag '%s' eliminato (era %s)\n"
+
+#: builtin/tag.c:239
+#, c-format
+msgid "could not verify the tag '%s'"
+msgstr "non è stato possibile verificare il tag '%s'"
+
+#: builtin/tag.c:249
+msgid ""
+"\n"
+"#\n"
+"# Write a tag message\n"
+"# Lines starting with '#' will be ignored.\n"
+"#\n"
+msgstr ""
+"\n"
+"#\n"
+"# Scrivere un messaggio associato al tag\n"
+"# Le righe che iniziano con '#' verranno ignorate.\n"
+"#\n"
+
+#: builtin/tag.c:256
+msgid ""
+"\n"
+"#\n"
+"# Write a tag message\n"
+"# Lines starting with '#' will be kept; you may remove them yourself if you "
+"want to.\n"
+"#\n"
+msgstr ""
+"\n"
+"#\n"
+"# Scrivere un messaggio associato al tag\n"
+"# Le righe che iniziano con '#' verranno mantenute; possono essere comunque "
+"rimosse manualmente.\n"
+"#\n"
+
+#: builtin/tag.c:298
+msgid "unable to sign the tag"
+msgstr "impossibile firmare il tag"
+
+#: builtin/tag.c:300
+msgid "unable to write tag file"
+msgstr "impossibile scrivere il file di tag"
+
+#: builtin/tag.c:325
+msgid "bad object type."
+msgstr "tipo di oggetto errato."
+
+#: builtin/tag.c:338
+msgid "tag header too big."
+msgstr "intestazione del tag troppo grande."
+
+#: builtin/tag.c:370
+msgid "no tag message?"
+msgstr "nessun messaggio per il tag?"
+
+#: builtin/tag.c:376
+#, c-format
+msgid "The tag message has been left in %s\n"
+msgstr "Il messaggio del tag è stato lasciato in %s\n"
+
+#: builtin/tag.c:425
+msgid "switch 'points-at' requires an object"
+msgstr "lo switch 'points-at' richiede un oggetto"
+
+#: builtin/tag.c:427
+#, c-format
+msgid "malformed object name '%s'"
+msgstr "nome oggetto '%s' malformato"
+
+#: builtin/tag.c:506
+msgid "--column and -n are incompatible"
+msgstr "--column e -n non sono compatibili"
+
+#: builtin/tag.c:523
+msgid "-n option is only allowed with -l."
+msgstr "l'opzione -n è consentita solo con -l."
+
+#: builtin/tag.c:525
+msgid "--contains option is only allowed with -l."
+msgstr "l'opzione --contains è consentita solo con -l."
+
+#: builtin/tag.c:527
+msgid "--points-at option is only allowed with -l."
+msgstr "l'opzione --points-at è consentita solo con -l."
+
+#: builtin/tag.c:535
+msgid "only one -F or -m option is allowed."
+msgstr "è consentita una sola opzione tra -F e -m."
+
+#: builtin/tag.c:555
+msgid "too many params"
+msgstr "troppi parametri"
+
+#: builtin/tag.c:561
+#, c-format
+msgid "'%s' is not a valid tag name."
+msgstr "'%s' non è un nome tag valido."
+
+#: builtin/tag.c:566
+#, c-format
+msgid "tag '%s' already exists"
+msgstr "il tag '%s' esiste già"
+
+#: builtin/tag.c:584
+#, c-format
+msgid "%s: cannot lock the ref"
+msgstr "%s: impossibile riservare il ref"
+
+#: builtin/tag.c:586
+#, c-format
+msgid "%s: cannot update the ref"
+msgstr "%s: impossibile aggiornare il ref"
+
+#: builtin/tag.c:588
+#, c-format
+msgid "Updated tag '%s' (was %s)\n"
+msgstr "Tag '%s' aggiornato (era %s)\n"
+
+#: git.c:16
+msgid "See 'git help <command>' for more information on a specific command."
+msgstr ""
+"Vedi 'git help <comando> per maggiori informazioni su un comando "
+"specifico."
+
+#: parse-options.h:133 parse-options.h:235
+msgid "n"
+msgstr "n"
+
+#: parse-options.h:141
+msgid "time"
+msgstr "tempo"
+
+#: parse-options.h:149
+msgid "file"
+msgstr "file"
+
+#: parse-options.h:151
+msgid "when"
+msgstr "quando"
+
+#: parse-options.h:156
+msgid "no-op (backward compatibility)"
+msgstr ""
+
+#: parse-options.h:228
+msgid "be more verbose"
+msgstr "più dettagliato"
+
+#: parse-options.h:230
+msgid "be more quiet"
+msgstr "meno dettagliato"
+
+#: parse-options.h:236
+msgid "use <n> digits to display SHA-1s"
+msgstr "usare <n> cifre per mostrare gli hash SHA-1"
+
+#: common-cmds.h:8
+msgid "Add file contents to the index"
+msgstr "Aggiunge il contenuto del file a index"
+
+#: common-cmds.h:9
+msgid "Find by binary search the change that introduced a bug"
+msgstr "Cerca mediante ricerca binaria la modifica che ha introdotto un bug"
+
+#: common-cmds.h:10
+msgid "List, create, or delete branches"
+msgstr "Elenca, crea o elimina branch"
+
+#: common-cmds.h:11
+msgid "Checkout a branch or paths to the working tree"
+msgstr ""
+
+#: common-cmds.h:12
+msgid "Clone a repository into a new directory"
+msgstr "Clona un repository in una nuova directory"
+
+#: common-cmds.h:13
+msgid "Record changes to the repository"
+msgstr "Registra modifiche nel repository"
+
+#: common-cmds.h:14
+msgid "Show changes between commits, commit and working tree, etc"
+msgstr ""
+
+#: common-cmds.h:15
+msgid "Download objects and refs from another repository"
+msgstr "Scarica oggetti e ref da un altro repository"
+
+#: common-cmds.h:16
+msgid "Print lines matching a pattern"
+msgstr "Stampa le righe corrispondenti ad un modello"
+
+#: common-cmds.h:17
+msgid "Create an empty git repository or reinitialize an existing one"
+msgstr "Crea un repository git vuoto o reinizializza uno esistente"
+
+#: common-cmds.h:18
+msgid "Show commit logs"
+msgstr "Mostra log del commit"
+
+#: common-cmds.h:19
+msgid "Join two or more development histories together"
+msgstr "Unisce due o più cronologie di sviluppo"
+
+#: common-cmds.h:20
+msgid "Move or rename a file, a directory, or a symlink"
+msgstr "Sposta o rinomina un file, una directory o un link simbolico"
+
+#: common-cmds.h:21
+msgid "Fetch from and merge with another repository or a local branch"
+msgstr "Combina fetche + merge da un altro repository o un branch locale"
+
+#: common-cmds.h:22
+msgid "Update remote refs along with associated objects"
+msgstr "Aggiorna i ref remoti insieme agli oggetti associati"
+
+#: common-cmds.h:23
+msgid "Forward-port local commits to the updated upstream head"
+msgstr ""
+
+#: common-cmds.h:24
+msgid "Reset current HEAD to the specified state"
+msgstr "Ripristina l'HEAD corrente allo stato specificato"
+
+#: common-cmds.h:25
+msgid "Remove files from the working tree and from the index"
+msgstr ""
+
+#: common-cmds.h:26
+msgid "Show various types of objects"
+msgstr "Mostra vari tipi di oggetti"
+
+#: common-cmds.h:27
+msgid "Show the working tree status"
+msgstr ""
+
+#: common-cmds.h:28
+msgid "Create, list, delete or verify a tag object signed with GPG"
+msgstr "Crea, elenca, elimina o verifica un oggetto tag firmato con GPG"
+
+#: git-am.sh:50
+msgid "You need to set your committer info first"
+msgstr "È necessario impostare le informazioni sul committer"
+
+#: git-am.sh:95
+msgid ""
+"You seem to have moved HEAD since the last 'am' failure.\n"
+"Not rewinding to ORIG_HEAD"
+msgstr ""
+
+#: git-am.sh:105
+#, sh-format
+msgid ""
+"When you have resolved this problem run \"$cmdline --resolved\".\n"
+"If you would prefer to skip this patch, instead run \"$cmdline --skip\".\n"
+"To restore the original branch and stop patching run \"$cmdline --abort\"."
+msgstr ""
+"Quando hai risolto il problema esegui \"$cmdline --resolved\".\n"
+"Se vuoi saltare questa patch, esegui invece \"$cmdline --skip\".\n"
+"Per ripristinare il branch originale e interrompere l'applicazione delle "
+"patch esegui \"$cmdline --abort\"."
+
+#: git-am.sh:121
+msgid "Cannot fall back to three-way merge."
+msgstr ""
+
+#: git-am.sh:137
+msgid "Repository lacks necessary blobs to fall back on 3-way merge."
+msgstr ""
+
+#: git-am.sh:154
+msgid ""
+"Did you hand edit your patch?\n"
+"It does not apply to blobs recorded in its index."
+msgstr ""
+"La tua patch è stata modificata manualmente?\n"
+"Non può essere applicata ai blob registrati nel proprio index."
+
+#: git-am.sh:163
+msgid "Falling back to patching base and 3-way merge..."
+msgstr ""
+
+#: git-am.sh:275
+msgid "Only one StGIT patch series can be applied at once"
+msgstr "Può essere applicata solo una serie di patch StGIT per volta"
+
+#: git-am.sh:362
+#, sh-format
+msgid "Patch format $patch_format is not supported."
+msgstr "Il formato patch $patch_format non è supportato."
+
+#: git-am.sh:364
+msgid "Patch format detection failed."
+msgstr "Rilevamento del formato della patch non riuscito."
+
+#: git-am.sh:418
+msgid "-d option is no longer supported.  Do not use."
+msgstr "l'opzione -d non è più supportata.  Non utilizzarla."
+
+#: git-am.sh:481
+#, sh-format
+msgid "previous rebase directory $dotest still exists but mbox given."
+msgstr ""
+
+#: git-am.sh:486
+msgid "Please make up your mind. --skip or --abort?"
+msgstr "Per favore, deciditi. --skip o --abort?"
+
+#: git-am.sh:513
+msgid "Resolve operation not in progress, we are not resuming."
+msgstr ""
+
+#: git-am.sh:579
+#, sh-format
+msgid "Dirty index: cannot apply patches (dirty: $files)"
+msgstr ""
+
+#: git-am.sh:671
+#, sh-format
+msgid ""
+"Patch is empty.  Was it split wrong?\n"
+"If you would prefer to skip this patch, instead run \"$cmdline --skip\".\n"
+"To restore the original branch and stop patching run \"$cmdline --abort\"."
+msgstr ""
+
+#: git-am.sh:708
+msgid "Patch does not have a valid e-mail address."
+msgstr "La patch non contiene un indirizzo email valido."
+
+#: git-am.sh:755
+msgid "cannot be interactive without stdin connected to a terminal."
+msgstr ""
+
+#: git-am.sh:759
+msgid "Commit Body is:"
+msgstr ""
+
+#. TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
+#. in your translation. The program will only accept English
+#. input at this point.
+#: git-am.sh:766
+msgid "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
+msgstr "Applicare? sì[y]/no[n]/modifica[e]/visualizza patch[v]/accetta tutto[a] "
+
+#: git-am.sh:802
+#, sh-format
+msgid "Applying: $FIRSTLINE"
+msgstr ""
+
+#: git-am.sh:823
+msgid ""
+"No changes - did you forget to use 'git add'?\n"
+"If there is nothing left to stage, chances are that something else\n"
+"already introduced the same changes; you might want to skip this patch."
+msgstr ""
+
+#: git-am.sh:831
+msgid ""
+"You still have unmerged paths in your index\n"
+"did you forget to use 'git add'?"
+msgstr ""
+
+#: git-am.sh:847
+msgid "No changes -- Patch already applied."
+msgstr "Nessuna modifica -- patch già applicata."
+
+#: git-am.sh:857
+#, sh-format
+msgid "Patch failed at $msgnum $FIRSTLINE"
+msgstr "Patch non riuscita a $msgnum $FIRSTLINE"
+
+#: git-am.sh:873
+msgid "applying to an empty history"
+msgstr ""
+
+#: git-bisect.sh:48
+msgid "You need to start by \"git bisect start\""
+msgstr "Devi iniziare con \"git bisect start\""
+
+#. TRANSLATORS: Make sure to include [Y] and [n] in your
+#. translation. The program will only accept English input
+#. at this point.
+#: git-bisect.sh:54
+msgid "Do you want me to do it for you [Y/n]? "
+msgstr "Vuoi che me ne occupi io [Y/n]? "
+
+#: git-bisect.sh:95
+#, sh-format
+msgid "unrecognised option: '$arg'"
+msgstr "opzione non riconosciuta: '$arg'"
+
+#: git-bisect.sh:99
+#, sh-format
+msgid "'$arg' does not appear to be a valid revision"
+msgstr "'$arg' non sembra essere una revisione valida"
+
+#: git-bisect.sh:117
+msgid "Bad HEAD - I need a HEAD"
+msgstr ""
+
+#: git-bisect.sh:130
+#, sh-format
+msgid ""
+"Checking out '$start_head' failed. Try 'git bisect reset <validbranch>'."
+msgstr ""
+"Checkout di '$start_head' non riuscito. Prova 'git bisect reset "
+"<branch-valido>'."
+
+#: git-bisect.sh:140
+msgid "won't bisect on seeked tree"
+msgstr ""
+
+#: git-bisect.sh:144
+msgid "Bad HEAD - strange symbolic ref"
+msgstr "HEAD errato - strano ref simbolico"
+
+#: git-bisect.sh:189
+#, sh-format
+msgid "Bad bisect_write argument: $state"
+msgstr "Argomento bisect_write errato: $state"
+
+#: git-bisect.sh:218
+#, sh-format
+msgid "Bad rev input: $arg"
+msgstr ""
+
+#: git-bisect.sh:232
+msgid "Please call 'bisect_state' with at least one argument."
+msgstr "Per favore, chiama 'bisect_state' con almeno un argomento."
+
+#: git-bisect.sh:244
+#, sh-format
+msgid "Bad rev input: $rev"
+msgstr ""
+
+#: git-bisect.sh:250
+msgid "'git bisect bad' can take only one argument."
+msgstr "'git bisect bad' può prendere un solo argomento."
+
+#. have bad but not good.  we could bisect although
+#. this is less optimum.
+#: git-bisect.sh:273
+msgid "Warning: bisecting only with a bad commit."
+msgstr ""
+
+#. TRANSLATORS: Make sure to include [Y] and [n] in your
+#. translation. The program will only accept English input
+#. at this point.
+#: git-bisect.sh:279
+msgid "Are you sure [Y/n]? "
+msgstr "Sei sicuro? [Y/n] "
+
+#: git-bisect.sh:289
+msgid ""
+"You need to give me at least one good and one bad revisions.\n"
+"(You can use \"git bisect bad\" and \"git bisect good\" for that.)"
+msgstr ""
+"Devi specificare almeno una revisione corretta ed una errata.\n"
+"(Puoi usare \"git bisect bad\" e \"git bisect good\" per questo scopo.)"
+
+#: git-bisect.sh:292
+msgid ""
+"You need to start by \"git bisect start\".\n"
+"You then need to give me at least one good and one bad revisions.\n"
+"(You can use \"git bisect bad\" and \"git bisect good\" for that.)"
+msgstr ""
+
+#: git-bisect.sh:347 git-bisect.sh:474
+msgid "We are not bisecting."
+msgstr "Non stiamo eseguendo un bisect."
+
+#: git-bisect.sh:354
+#, sh-format
+msgid "'$invalid' is not a valid commit"
+msgstr "'$invalid' non è un commit valido"
+
+#: git-bisect.sh:363
+#, sh-format
+msgid ""
+"Could not check out original HEAD '$branch'.\n"
+"Try 'git bisect reset <commit>'."
+msgstr ""
+
+#: git-bisect.sh:390
+msgid "No logfile given"
+msgstr "Nessun file di log specificato"
+
+#: git-bisect.sh:391
+#, sh-format
+msgid "cannot read $file for replaying"
+msgstr ""
+
+#: git-bisect.sh:408
+msgid "?? what are you talking about?"
+msgstr "?? di cosa si sta parlando?"
+
+#: git-bisect.sh:420
+#, sh-format
+msgid "running $command"
+msgstr "sto eseguendo $command"
+
+#: git-bisect.sh:427
+#, sh-format
+msgid ""
+"bisect run failed:\n"
+"exit code $res from '$command' is < 0 or >= 128"
+msgstr ""
+"bisect run non riuscito:\n"
+"il codice di uscita $res da '$command' è < 0 oppure >= 128"
+
+#: git-bisect.sh:453
+msgid "bisect run cannot continue any more"
+msgstr "bisect run non può più proseguire"
+
+#: git-bisect.sh:459
+#, sh-format
+msgid ""
+"bisect run failed:\n"
+"'bisect_state $state' exited with error code $res"
+msgstr ""
+"bisect run non riuscito:\n"
+"bisect_state $state è uscito con il codice di errore $res"
+
+#: git-bisect.sh:466
+msgid "bisect run success"
+msgstr "bisect run eseguito con successo"
+
+#: git-pull.sh:21
+msgid ""
+"Pull is not possible because you have unmerged files.\n"
+"Please, fix them up in the work tree, and then use 'git add/rm <file>'\n"
+"as appropriate to mark resolution, or use 'git commit -a'."
+msgstr ""
+
+#: git-pull.sh:25
+msgid "Pull is not possible because you have unmerged files."
+msgstr ""
+"Il pull non è possibile perché ci sono file di cui non è stato eseguito il "
+"merge."
+
+#: git-pull.sh:197
+msgid "updating an unborn branch with changes added to the index"
+msgstr ""
+
+#. The fetch involved updating the current branch.
+#. The working tree and the index file is still based on the
+#. $orig_head commit, but we are merging into $curr_head.
+#. First update the working tree to match $curr_head.
+#: git-pull.sh:228
+#, sh-format
+msgid ""
+"Warning: fetch updated the current branch head.\n"
+"Warning: fast-forwarding your working tree from\n"
+"Warning: commit $orig_head."
+msgstr ""
+
+#: git-pull.sh:253
+msgid "Cannot merge multiple branches into empty head"
+msgstr "Impossibile eseguire il merge di branch multipli in un head vuoto"
+
+#: git-pull.sh:257
+msgid "Cannot rebase onto multiple branches"
+msgstr "Impossibile eseguire il rebase su branch multipli"
+
+#: git-stash.sh:51
+msgid "git stash clear with parameters is unimplemented"
+msgstr "git stash clear con parametri non è implementato"
+
+#: git-stash.sh:74
+msgid "You do not have the initial commit yet"
+msgstr "Non hai ancora un commit iniziale"
+
+#: git-stash.sh:89
+msgid "Cannot save the current index state"
+msgstr "Impossibile salvare lo stato corrente di index"
+
+#: git-stash.sh:123 git-stash.sh:136
+msgid "Cannot save the current worktree state"
+msgstr ""
+
+#: git-stash.sh:140
+msgid "No changes selected"
+msgstr "Nessuna modifica selezionata"
+
+#: git-stash.sh:143
+msgid "Cannot remove temporary index (can't happen)"
+msgstr ""
+
+#: git-stash.sh:156
+msgid "Cannot record working tree state"
+msgstr ""
+
+#. TRANSLATORS: $option is an invalid option, like
+#. `--blah-blah'. The 7 spaces at the beginning of the
+#. second line correspond to "error: ". So you should line
+#. up the second line with however many characters the
+#. translation of "error: " takes in your language. E.g. in
+#. English this is:
+#.
+#. $ git stash save --blah-blah 2>&1 | head -n 2
+#. error: unknown option for 'stash save': --blah-blah
+#. To provide a message, use git stash save -- '--blah-blah'
+#: git-stash.sh:202
+#, sh-format
+msgid ""
+"error: unknown option for 'stash save': $option\n"
+"       To provide a message, use git stash save -- '$option'"
+msgstr ""
+"errore: opzione sconosciuta per 'stash save': $option\n"
+"        Per aggiungere un messaggio, usare git stash save -- '$option'"
+
+#: git-stash.sh:223
+msgid "No local changes to save"
+msgstr "Nessuna modifica locale da salvare"
+
+#: git-stash.sh:227
+msgid "Cannot initialize stash"
+msgstr "Impossibile inizializzare stash"
+
+#: git-stash.sh:235
+msgid "Cannot save the current status"
+msgstr "Impossibile salvare lo stato attuale"
+
+#: git-stash.sh:253
+msgid "Cannot remove worktree changes"
+msgstr ""
+
+#: git-stash.sh:352
+msgid "No stash found."
+msgstr "Nessuno stash trovato."
+
+#: git-stash.sh:359
+#, sh-format
+msgid "Too many revisions specified: $REV"
+msgstr "Troppe revisioni specificate: $REV"
+
+#: git-stash.sh:365
+#, sh-format
+msgid "$reference is not valid reference"
+msgstr "$reference non è un riferimento valido"
+
+#: git-stash.sh:393
+#, sh-format
+msgid "'$args' is not a stash-like commit"
+msgstr "'$args' non è un commit di tipo stash"
+
+#: git-stash.sh:404
+#, sh-format
+msgid "'$args' is not a stash reference"
+msgstr "'$args' non è un referimento a uno stash"
+
+#: git-stash.sh:412
+msgid "unable to refresh index"
+msgstr "impossibile aggiornare index"
+
+#: git-stash.sh:416
+msgid "Cannot apply a stash in the middle of a merge"
+msgstr "Impossibile applicare uno stash nel mezzo di un merge"
+
+#: git-stash.sh:424
+msgid "Conflicts in index. Try without --index."
+msgstr "Ci sono conflitti in index. Prova senza --index."
+
+#: git-stash.sh:426
+msgid "Could not save index tree"
+msgstr ""
+
+#: git-stash.sh:460
+msgid "Cannot unstage modified files"
+msgstr ""
+
+#: git-stash.sh:474
+msgid "Index was not unstashed."
+msgstr ""
+
+#: git-stash.sh:491
+#, sh-format
+msgid "Dropped ${REV} ($s)"
+msgstr "${REV} eliminata ($s)"
+
+#: git-stash.sh:492
+#, sh-format
+msgid "${REV}: Could not drop stash entry"
+msgstr "${REV}: non è stato possibile rimuovere la voce di stash"
+
+#: git-stash.sh:499
+msgid "No branch name specified"
+msgstr "Nome del branch non specificato"
+
+#: git-stash.sh:570
+msgid "(To restore them type \"git stash apply\")"
+msgstr "(Per ripristinarli digita \"git stash apply\")"
+
+#: git-submodule.sh:56
+#, sh-format
+msgid "cannot strip one component off url '$remoteurl'"
+msgstr ""
+
+#: git-submodule.sh:109
+#, sh-format
+msgid "No submodule mapping found in .gitmodules for path '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:150
+#, sh-format
+msgid "Clone of '$url' into submodule path '$sm_path' failed"
+msgstr ""
+
+#: git-submodule.sh:160
+#, sh-format
+msgid "Gitdir '$a' is part of the submodule path '$b' or vice versa"
+msgstr ""
+
+#: git-submodule.sh:249
+#, sh-format
+msgid "repo URL: '$repo' must be absolute or begin with ./|../"
+msgstr "repo URL: '$repo' deve essere assoluto o iniziare con ./|../"
+
+#: git-submodule.sh:266
+#, sh-format
+msgid "'$sm_path' already exists in the index"
+msgstr "'$sm_path' esiste già in index"
+
+#: git-submodule.sh:270
+#, sh-format
+msgid ""
+"The following path is ignored by one of your .gitignore files:\n"
+"$sm_path\n"
+"Use -f if you really want to add it."
+msgstr ""
+"Il seguente path è ignorato da uno dei tuoi file .gitignore:\n"
+"$sm_path\n"
+"Usa -f se vuoi davvero aggiungerlo."
+
+#: git-submodule.sh:281
+#, sh-format
+msgid "Adding existing repo at '$sm_path' to the index"
+msgstr ""
+
+#: git-submodule.sh:283
+#, sh-format
+msgid "'$sm_path' already exists and is not a valid git repo"
+msgstr "'$sm_path' esiste già e non è un repository git valido"
+
+#: git-submodule.sh:297
+#, sh-format
+msgid "Unable to checkout submodule '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:302
+#, sh-format
+msgid "Failed to add submodule '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:307
+#, sh-format
+msgid "Failed to register submodule '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:349
+#, sh-format
+msgid "Entering '$prefix$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:363
+#, sh-format
+msgid "Stopping at '$sm_path'; script returned non-zero status."
+msgstr ""
+"Interruzione a '$sm_path'; lo script ha restituito uno stato diverso da zero."
+
+#: git-submodule.sh:406
+#, sh-format
+msgid "No url found for submodule path '$sm_path' in .gitmodules"
+msgstr ""
+
+#: git-submodule.sh:415
+#, sh-format
+msgid "Failed to register url for submodule path '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:417
+#, sh-format
+msgid "Submodule '$name' ($url) registered for path '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:425
+#, sh-format
+msgid "Failed to register update mode for submodule path '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:524
+#, sh-format
+msgid ""
+"Submodule path '$sm_path' not initialized\n"
+"Maybe you want to use 'update --init'?"
+msgstr ""
+
+#: git-submodule.sh:537
+#, sh-format
+msgid "Unable to find current revision in submodule path '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:556
+#, sh-format
+msgid "Unable to fetch in submodule path '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:570
+#, sh-format
+msgid "Unable to rebase '$sha1' in submodule path '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:571
+#, sh-format
+msgid "Submodule path '$sm_path': rebased into '$sha1'"
+msgstr ""
+
+#: git-submodule.sh:576
+#, sh-format
+msgid "Unable to merge '$sha1' in submodule path '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:577
+#, sh-format
+msgid "Submodule path '$sm_path': merged in '$sha1'"
+msgstr ""
+
+#: git-submodule.sh:582
+#, sh-format
+msgid "Unable to checkout '$sha1' in submodule path '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:583
+#, sh-format
+msgid "Submodule path '$sm_path': checked out '$sha1'"
+msgstr ""
+
+#: git-submodule.sh:605 git-submodule.sh:928
+#, sh-format
+msgid "Failed to recurse into submodule path '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:713
+msgid "--cached cannot be used with --files"
+msgstr "--cached non può essere usata con --files"
+
+#. unexpected type
+#: git-submodule.sh:753
+#, sh-format
+msgid "unexpected mode $mod_dst"
+msgstr "modalità $mod_dst inattesa"
+
+#: git-submodule.sh:771
+#, sh-format
+msgid "  Warn: $name doesn't contain commit $sha1_src"
+msgstr "  Attenzione: $name non contiene commit $sha1_src"
+
+#: git-submodule.sh:774
+#, sh-format
+msgid "  Warn: $name doesn't contain commit $sha1_dst"
+msgstr "  Attenzione: $name non contiene commit $sha1_dst"
+
+#: git-submodule.sh:777
+#, sh-format
+msgid "  Warn: $name doesn't contain commits $sha1_src and $sha1_dst"
+msgstr "  Attenzione: $name non contiene commit $sha1_src e $sha1_dst"
+
+#: git-submodule.sh:802
+msgid "blob"
+msgstr "blob"
+
+#: git-submodule.sh:803
+msgid "submodule"
+msgstr "sottomodulo"
+
+#: git-submodule.sh:840
+msgid "# Submodules changed but not updated:"
+msgstr ""
+
+#: git-submodule.sh:842
+msgid "# Submodule changes to be committed:"
+msgstr ""
+
+#: git-submodule.sh:974
+#, sh-format
+msgid "Synchronizing submodule url for '$name'"
+msgstr ""
+
+#~ msgid "--"
+#~ msgstr "--"
+
+#~ msgid "Could not extract email from committer identity."
+#~ msgstr ""
+#~ "Non è stato possibile estrarre l'indirizzo email dall'identità del "
+#~ "committer."
diff --git a/po/nl.po b/po/nl.po
new file mode 100644
index 0000000..e1399e2
--- /dev/null
+++ b/po/nl.po
@@ -0,0 +1,3493 @@
+# Dutch translations for Git.
+# Copyright (C) 2012 Vincent van Ravesteijn <vfr@lyx.org>
+# This file is distributed under the same license as the Git package.
+# Vincent van Ravesteijn <vfr@lyx.org>, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Git\n"
+"Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
+"POT-Creation-Date: 2012-02-28 09:17+0800\n"
+"PO-Revision-Date: 2012-03-07 12:02+0100\n"
+"Last-Translator: Vincent van Ravesteijn <vfr@lyx.org>\n"
+"Language-Team: Dutch\n"
+"Language: nl\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ASCII\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: advice.c:34
+#, c-format
+msgid "hint: %.*s\n"
+msgstr ""
+
+#.
+#. * Message used both when 'git commit' fails and when
+#. * other commands doing a merge do.
+#.
+#: advice.c:64
+msgid ""
+"Fix them up in the work tree,\n"
+"and then use 'git add/rm <file>' as\n"
+"appropriate to mark resolution and make a commit,\n"
+"or use 'git commit -a'."
+msgstr ""
+
+#: commit.c:47
+#, c-format
+msgid "could not parse %s"
+msgstr ""
+
+#: commit.c:49
+#, c-format
+msgid "%s %s is not a commit!"
+msgstr ""
+
+#: compat/obstack.c:406 compat/obstack.c:408
+msgid "memory exhausted"
+msgstr ""
+
+#: connected.c:39
+msgid "Could not run 'git rev-list'"
+msgstr ""
+
+#: connected.c:48
+#, c-format
+msgid "failed write to rev-list: %s"
+msgstr ""
+
+#: connected.c:56
+#, c-format
+msgid "failed to close rev-list's stdin: %s"
+msgstr ""
+
+#: diff.c:104
+#, c-format
+msgid "  Failed to parse dirstat cut-off percentage '%.*s'\n"
+msgstr ""
+
+#: diff.c:109
+#, c-format
+msgid "  Unknown dirstat parameter '%.*s'\n"
+msgstr ""
+
+#: diff.c:205
+#, c-format
+msgid ""
+"Found errors in 'diff.dirstat' config variable:\n"
+"%s"
+msgstr ""
+
+#: diff.c:1331
+msgid " 0 files changed\n"
+msgstr ""
+
+#: diff.c:1335
+#, c-format
+msgid " %d file changed"
+msgid_plural " %d files changed"
+msgstr[0] ""
+msgstr[1] ""
+
+#: diff.c:1352
+#, c-format
+msgid ", %d insertion(+)"
+msgid_plural ", %d insertions(+)"
+msgstr[0] ""
+msgstr[1] ""
+
+#: diff.c:1363
+#, c-format
+msgid ", %d deletion(-)"
+msgid_plural ", %d deletions(-)"
+msgstr[0] ""
+msgstr[1] ""
+
+#: diff.c:3364
+#, c-format
+msgid ""
+"Failed to parse --dirstat/-X option parameter:\n"
+"%s"
+msgstr ""
+
+#: gpg-interface.c:59
+msgid "could not run gpg."
+msgstr ""
+
+#: gpg-interface.c:71
+msgid "gpg did not accept the data"
+msgstr ""
+
+#: gpg-interface.c:82
+msgid "gpg failed to sign the data"
+msgstr ""
+
+#: grep.c:1285
+#, c-format
+msgid "'%s': unable to read %s"
+msgstr ""
+
+#: grep.c:1302
+#, c-format
+msgid "'%s': %s"
+msgstr ""
+
+#: grep.c:1313
+#, c-format
+msgid "'%s': short read %s"
+msgstr ""
+
+#: help.c:287
+#, c-format
+msgid ""
+"'%s' appears to be a git command, but we were not\n"
+"able to execute it. Maybe git-%s is broken?"
+msgstr ""
+
+#: remote.c:1607
+#, c-format
+msgid "Your branch is ahead of '%s' by %d commit.\n"
+msgid_plural "Your branch is ahead of '%s' by %d commits.\n"
+msgstr[0] ""
+msgstr[1] ""
+
+#: remote.c:1613
+#, c-format
+msgid "Your branch is behind '%s' by %d commit, and can be fast-forwarded.\n"
+msgid_plural ""
+"Your branch is behind '%s' by %d commits, and can be fast-forwarded.\n"
+msgstr[0] ""
+msgstr[1] ""
+
+#: remote.c:1621
+#, c-format
+msgid ""
+"Your branch and '%s' have diverged,\n"
+"and have %d and %d different commit each, respectively.\n"
+msgid_plural ""
+"Your branch and '%s' have diverged,\n"
+"and have %d and %d different commits each, respectively.\n"
+msgstr[0] ""
+msgstr[1] ""
+
+#: sequencer.c:120 builtin/merge.c:862 builtin/merge.c:983
+#: builtin/merge.c:1093 builtin/merge.c:1103
+#, c-format
+msgid "Could not open '%s' for writing"
+msgstr ""
+
+#: sequencer.c:122 builtin/merge.c:334 builtin/merge.c:865
+#: builtin/merge.c:1095 builtin/merge.c:1108
+#, c-format
+msgid "Could not write to '%s'"
+msgstr ""
+
+#: sequencer.c:142
+msgid ""
+"after resolving the conflicts, mark the corrected paths\n"
+"with 'git add <paths>' or 'git rm <paths>'\n"
+"and commit the result with 'git commit'"
+msgstr ""
+
+#: sequencer.c:154 sequencer.c:680 sequencer.c:763
+#, c-format
+msgid "Could not write to %s"
+msgstr ""
+
+#: sequencer.c:157
+#, c-format
+msgid "Error wrapping up %s"
+msgstr ""
+
+#: sequencer.c:172
+msgid "Your local changes would be overwritten by cherry-pick."
+msgstr ""
+
+#: sequencer.c:174
+msgid "Your local changes would be overwritten by revert."
+msgstr ""
+
+#: sequencer.c:177
+msgid "Commit your changes or stash them to proceed."
+msgstr ""
+
+#. TRANSLATORS: %s will be "revert" or "cherry-pick"
+#: sequencer.c:227
+#, c-format
+msgid "%s: Unable to write new index file"
+msgstr ""
+
+#: sequencer.c:293
+msgid "Your index file is unmerged."
+msgstr ""
+
+#: sequencer.c:296
+msgid "You do not have a valid HEAD"
+msgstr ""
+
+#: sequencer.c:311
+#, c-format
+msgid "Commit %s is a merge but no -m option was given."
+msgstr ""
+
+#: sequencer.c:319
+#, c-format
+msgid "Commit %s does not have parent %d"
+msgstr ""
+
+#: sequencer.c:323
+#, c-format
+msgid "Mainline was specified but commit %s is not a merge."
+msgstr ""
+
+#. TRANSLATORS: The first %s will be "revert" or
+#. "cherry-pick", the second %s a SHA1
+#: sequencer.c:334
+#, c-format
+msgid "%s: cannot parse parent commit %s"
+msgstr ""
+
+#: sequencer.c:338
+#, c-format
+msgid "Cannot get commit message for %s"
+msgstr ""
+
+#: sequencer.c:422
+#, c-format
+msgid "could not revert %s... %s"
+msgstr ""
+
+#: sequencer.c:423
+#, c-format
+msgid "could not apply %s... %s"
+msgstr ""
+
+#: sequencer.c:445 sequencer.c:904 builtin/log.c:286 builtin/log.c:709
+#: builtin/log.c:1325 builtin/log.c:1544 builtin/merge.c:348
+#: builtin/shortlog.c:181
+msgid "revision walk setup failed"
+msgstr ""
+
+#: sequencer.c:448
+msgid "empty commit set passed"
+msgstr ""
+
+#: sequencer.c:456
+#, c-format
+msgid "git %s: failed to read the index"
+msgstr ""
+
+#: sequencer.c:461
+#, c-format
+msgid "git %s: failed to refresh the index"
+msgstr ""
+
+#: sequencer.c:546
+#, c-format
+msgid "Cannot %s during a %s"
+msgstr ""
+
+#: sequencer.c:568
+#, c-format
+msgid "Could not parse line %d."
+msgstr ""
+
+#: sequencer.c:573
+msgid "No commits parsed."
+msgstr ""
+
+#: sequencer.c:586
+#, c-format
+msgid "Could not open %s"
+msgstr ""
+
+#: sequencer.c:590
+#, c-format
+msgid "Could not read %s."
+msgstr ""
+
+#: sequencer.c:597
+#, c-format
+msgid "Unusable instruction sheet: %s"
+msgstr ""
+
+#: sequencer.c:625
+#, c-format
+msgid "Invalid key: %s"
+msgstr ""
+
+#: sequencer.c:628
+#, c-format
+msgid "Invalid value for %s: %s"
+msgstr ""
+
+#: sequencer.c:640
+#, c-format
+msgid "Malformed options sheet: %s"
+msgstr ""
+
+#: sequencer.c:661
+msgid "a cherry-pick or revert is already in progress"
+msgstr ""
+
+#: sequencer.c:662
+msgid "try \"git cherry-pick (--continue | --quit | --abort)\""
+msgstr ""
+
+#: sequencer.c:666
+#, c-format
+msgid "Could not create sequencer directory %s"
+msgstr ""
+
+#: sequencer.c:682 sequencer.c:767
+#, c-format
+msgid "Error wrapping up %s."
+msgstr ""
+
+#: sequencer.c:701 sequencer.c:835
+msgid "no cherry-pick or revert in progress"
+msgstr ""
+
+#: sequencer.c:703
+msgid "cannot resolve HEAD"
+msgstr ""
+
+#: sequencer.c:705
+msgid "cannot abort from a branch yet to be born"
+msgstr ""
+
+#: sequencer.c:727
+#, c-format
+msgid "cannot open %s: %s"
+msgstr ""
+
+#: sequencer.c:730
+#, c-format
+msgid "cannot read %s: %s"
+msgstr ""
+
+#: sequencer.c:731
+msgid "unexpected end of file"
+msgstr ""
+
+#: sequencer.c:737
+#, c-format
+msgid "stored pre-cherry-pick HEAD file '%s' is corrupt"
+msgstr ""
+
+#: sequencer.c:760
+#, c-format
+msgid "Could not format %s."
+msgstr ""
+
+#: sequencer.c:922
+msgid "Can't revert as initial commit"
+msgstr ""
+
+#: sequencer.c:923
+msgid "Can't cherry-pick into empty head"
+msgstr ""
+
+#: wt-status.c:134
+msgid "Unmerged paths:"
+msgstr ""
+
+#: wt-status.c:140 wt-status.c:157
+#, c-format
+msgid "  (use \"git reset %s <file>...\" to unstage)"
+msgstr ""
+
+#: wt-status.c:142 wt-status.c:159
+msgid "  (use \"git rm --cached <file>...\" to unstage)"
+msgstr ""
+
+#: wt-status.c:143
+msgid "  (use \"git add/rm <file>...\" as appropriate to mark resolution)"
+msgstr ""
+
+#: wt-status.c:151
+msgid "Changes to be committed:"
+msgstr ""
+
+#: wt-status.c:169
+msgid "Changes not staged for commit:"
+msgstr ""
+
+#: wt-status.c:173
+msgid "  (use \"git add <file>...\" to update what will be committed)"
+msgstr ""
+
+#: wt-status.c:175
+msgid "  (use \"git add/rm <file>...\" to update what will be committed)"
+msgstr ""
+
+#: wt-status.c:176
+msgid ""
+"  (use \"git checkout -- <file>...\" to discard changes in working directory)"
+msgstr ""
+
+#: wt-status.c:178
+msgid "  (commit or discard the untracked or modified content in submodules)"
+msgstr ""
+
+#: wt-status.c:187
+#, c-format
+msgid "%s files:"
+msgstr ""
+
+#: wt-status.c:190
+#, c-format
+msgid "  (use \"git %s <file>...\" to include in what will be committed)"
+msgstr ""
+
+#: wt-status.c:207
+msgid "bug"
+msgstr ""
+
+#: wt-status.c:212
+msgid "both deleted:"
+msgstr ""
+
+#: wt-status.c:213
+msgid "added by us:"
+msgstr ""
+
+#: wt-status.c:214
+msgid "deleted by them:"
+msgstr ""
+
+#: wt-status.c:215
+msgid "added by them:"
+msgstr ""
+
+#: wt-status.c:216
+msgid "deleted by us:"
+msgstr ""
+
+#: wt-status.c:217
+msgid "both added:"
+msgstr ""
+
+#: wt-status.c:218
+msgid "both modified:"
+msgstr ""
+
+#: wt-status.c:248
+msgid "new commits, "
+msgstr ""
+
+#: wt-status.c:250
+msgid "modified content, "
+msgstr ""
+
+#: wt-status.c:252
+msgid "untracked content, "
+msgstr ""
+
+#: wt-status.c:266
+#, c-format
+msgid "new file:   %s"
+msgstr ""
+
+#: wt-status.c:269
+#, c-format
+msgid "copied:     %s -> %s"
+msgstr ""
+
+#: wt-status.c:272
+#, c-format
+msgid "deleted:    %s"
+msgstr ""
+
+#: wt-status.c:275
+#, c-format
+msgid "modified:   %s"
+msgstr ""
+
+#: wt-status.c:278
+#, c-format
+msgid "renamed:    %s -> %s"
+msgstr ""
+
+#: wt-status.c:281
+#, c-format
+msgid "typechange: %s"
+msgstr ""
+
+#: wt-status.c:284
+#, c-format
+msgid "unknown:    %s"
+msgstr ""
+
+#: wt-status.c:287
+#, c-format
+msgid "unmerged:   %s"
+msgstr ""
+
+#: wt-status.c:290
+#, c-format
+msgid "bug: unhandled diff status %c"
+msgstr ""
+
+#: wt-status.c:713
+msgid "On branch "
+msgstr ""
+
+#: wt-status.c:720
+msgid "Not currently on any branch."
+msgstr ""
+
+#: wt-status.c:731
+msgid "Initial commit"
+msgstr ""
+
+#: wt-status.c:745
+msgid "Untracked"
+msgstr ""
+
+#: wt-status.c:747
+msgid "Ignored"
+msgstr ""
+
+#: wt-status.c:749
+#, c-format
+msgid "Untracked files not listed%s"
+msgstr ""
+
+#: wt-status.c:751
+msgid " (use -u option to show untracked files)"
+msgstr ""
+
+#: wt-status.c:757
+msgid "No changes"
+msgstr ""
+
+#: wt-status.c:761
+#, c-format
+msgid "no changes added to commit%s\n"
+msgstr ""
+
+#: wt-status.c:763
+msgid " (use \"git add\" and/or \"git commit -a\")"
+msgstr ""
+
+#: wt-status.c:765
+#, c-format
+msgid "nothing added to commit but untracked files present%s\n"
+msgstr ""
+
+#: wt-status.c:767
+msgid " (use \"git add\" to track)"
+msgstr ""
+
+#: wt-status.c:769 wt-status.c:772 wt-status.c:775
+#, c-format
+msgid "nothing to commit%s\n"
+msgstr ""
+
+#: wt-status.c:770
+msgid " (create/copy files and use \"git add\" to track)"
+msgstr ""
+
+#: wt-status.c:773
+msgid " (use -u to show untracked files)"
+msgstr ""
+
+#: wt-status.c:776
+msgid " (working directory clean)"
+msgstr ""
+
+#: wt-status.c:884
+msgid "HEAD (no branch)"
+msgstr ""
+
+#: wt-status.c:890
+msgid "Initial commit on "
+msgstr ""
+
+#: wt-status.c:905
+msgid "behind "
+msgstr ""
+
+#: wt-status.c:908 wt-status.c:911
+msgid "ahead "
+msgstr ""
+
+#: wt-status.c:913
+msgid ", behind "
+msgstr ""
+
+#: builtin/add.c:62
+#, c-format
+msgid "unexpected diff status %c"
+msgstr ""
+
+#: builtin/add.c:67 builtin/commit.c:298
+msgid "updating files failed"
+msgstr ""
+
+#: builtin/add.c:77
+#, c-format
+msgid "remove '%s'\n"
+msgstr ""
+
+#: builtin/add.c:176
+#, c-format
+msgid "Path '%s' is in submodule '%.*s'"
+msgstr ""
+
+#: builtin/add.c:192
+msgid "Unstaged changes after refreshing the index:"
+msgstr ""
+
+#: builtin/add.c:195 builtin/add.c:456 builtin/rm.c:186
+#, c-format
+msgid "pathspec '%s' did not match any files"
+msgstr ""
+
+#: builtin/add.c:209
+#, c-format
+msgid "'%s' is beyond a symbolic link"
+msgstr ""
+
+#: builtin/add.c:276
+msgid "Could not read the index"
+msgstr ""
+
+#: builtin/add.c:286
+#, c-format
+msgid "Could not open '%s' for writing."
+msgstr ""
+
+#: builtin/add.c:290
+msgid "Could not write patch"
+msgstr ""
+
+#: builtin/add.c:295
+#, c-format
+msgid "Could not stat '%s'"
+msgstr ""
+
+#: builtin/add.c:297
+msgid "Empty patch. Aborted."
+msgstr ""
+
+#: builtin/add.c:303
+#, c-format
+msgid "Could not apply '%s'"
+msgstr ""
+
+#: builtin/add.c:312
+msgid "The following paths are ignored by one of your .gitignore files:\n"
+msgstr ""
+
+#: builtin/add.c:352
+#, c-format
+msgid "Use -f if you really want to add them.\n"
+msgstr ""
+
+#: builtin/add.c:353
+msgid "no files added"
+msgstr ""
+
+#: builtin/add.c:359
+msgid "adding files failed"
+msgstr ""
+
+#: builtin/add.c:391
+msgid "-A and -u are mutually incompatible"
+msgstr ""
+
+#: builtin/add.c:393
+msgid "Option --ignore-missing can only be used together with --dry-run"
+msgstr ""
+
+#: builtin/add.c:413
+#, c-format
+msgid "Nothing specified, nothing added.\n"
+msgstr ""
+
+#: builtin/add.c:414
+#, c-format
+msgid "Maybe you wanted to say 'git add .'?\n"
+msgstr ""
+
+#: builtin/add.c:420 builtin/clean.c:95 builtin/commit.c:358 builtin/mv.c:82
+#: builtin/rm.c:162
+msgid "index file corrupt"
+msgstr ""
+
+#: builtin/add.c:476 builtin/mv.c:229 builtin/rm.c:260
+msgid "Unable to write new index file"
+msgstr ""
+
+#: builtin/archive.c:17
+#, c-format
+msgid "could not create archive file '%s'"
+msgstr ""
+
+#: builtin/archive.c:20
+msgid "could not redirect output"
+msgstr ""
+
+#: builtin/archive.c:37
+msgid "git archive: Remote with no URL"
+msgstr ""
+
+#: builtin/archive.c:58
+msgid "git archive: expected ACK/NAK, got EOF"
+msgstr ""
+
+#: builtin/archive.c:63
+#, c-format
+msgid "git archive: NACK %s"
+msgstr ""
+
+#: builtin/archive.c:65
+#, c-format
+msgid "remote error: %s"
+msgstr ""
+
+#: builtin/archive.c:66
+msgid "git archive: protocol error"
+msgstr ""
+
+#: builtin/archive.c:71
+msgid "git archive: expected a flush"
+msgstr ""
+
+#: builtin/branch.c:137
+#, c-format
+msgid ""
+"deleting branch '%s' that has been merged to\n"
+"         '%s', but not yet merged to HEAD."
+msgstr ""
+
+#: builtin/branch.c:141
+#, c-format
+msgid ""
+"not deleting branch '%s' that is not yet merged to\n"
+"         '%s', even though it is merged to HEAD."
+msgstr ""
+
+#. TRANSLATORS: This is "remote " in "remote branch '%s' not found"
+#: builtin/branch.c:163
+msgid "remote "
+msgstr ""
+
+#: builtin/branch.c:171
+msgid "cannot use -a with -d"
+msgstr ""
+
+#: builtin/branch.c:177
+msgid "Couldn't look up commit object for HEAD"
+msgstr ""
+
+#: builtin/branch.c:182
+#, c-format
+msgid "Cannot delete the branch '%s' which you are currently on."
+msgstr ""
+
+#: builtin/branch.c:192
+#, c-format
+msgid "%sbranch '%s' not found."
+msgstr ""
+
+#: builtin/branch.c:200
+#, c-format
+msgid "Couldn't look up commit object for '%s'"
+msgstr ""
+
+#: builtin/branch.c:206
+#, c-format
+msgid ""
+"The branch '%s' is not fully merged.\n"
+"If you are sure you want to delete it, run 'git branch -D %s'."
+msgstr ""
+
+#: builtin/branch.c:214
+#, c-format
+msgid "Error deleting %sbranch '%s'"
+msgstr ""
+
+#: builtin/branch.c:219
+#, c-format
+msgid "Deleted %sbranch %s (was %s).\n"
+msgstr ""
+
+#: builtin/branch.c:224
+msgid "Update of config-file failed"
+msgstr ""
+
+#: builtin/branch.c:322
+#, c-format
+msgid "branch '%s' does not point at a commit"
+msgstr ""
+
+#: builtin/branch.c:394
+#, c-format
+msgid "behind %d] "
+msgstr ""
+
+#: builtin/branch.c:396
+#, c-format
+msgid "ahead %d] "
+msgstr ""
+
+#: builtin/branch.c:398
+#, c-format
+msgid "ahead %d, behind %d] "
+msgstr ""
+
+#: builtin/branch.c:501
+msgid "(no branch)"
+msgstr ""
+
+#: builtin/branch.c:562
+msgid "some refs could not be read"
+msgstr ""
+
+#: builtin/branch.c:575
+msgid "cannot rename the current branch while not on any."
+msgstr ""
+
+#: builtin/branch.c:585
+#, c-format
+msgid "Invalid branch name: '%s'"
+msgstr ""
+
+#: builtin/branch.c:600
+msgid "Branch rename failed"
+msgstr ""
+
+#: builtin/branch.c:604
+#, c-format
+msgid "Renamed a misnamed branch '%s' away"
+msgstr ""
+
+#: builtin/branch.c:608
+#, c-format
+msgid "Branch renamed to %s, but HEAD is not updated!"
+msgstr ""
+
+#: builtin/branch.c:615
+msgid "Branch is renamed, but update of config-file failed"
+msgstr ""
+
+#: builtin/branch.c:630
+#, c-format
+msgid "malformed object name %s"
+msgstr ""
+
+#: builtin/branch.c:654
+#, c-format
+msgid "could not write branch description template: %s\n"
+msgstr ""
+
+#: builtin/branch.c:742
+msgid "Failed to resolve HEAD as a valid ref."
+msgstr ""
+
+#: builtin/branch.c:747 builtin/clone.c:558
+msgid "HEAD not found below refs/heads!"
+msgstr ""
+
+#: builtin/branch.c:805
+msgid "-a and -r options to 'git branch' do not make sense with a branch name"
+msgstr ""
+
+#: builtin/bundle.c:47
+#, c-format
+msgid "%s is okay\n"
+msgstr ""
+
+#: builtin/bundle.c:56
+msgid "Need a repository to create a bundle."
+msgstr ""
+
+#: builtin/bundle.c:60
+msgid "Need a repository to unbundle."
+msgstr ""
+
+#: builtin/checkout.c:113 builtin/checkout.c:146
+#, c-format
+msgid "path '%s' does not have our version"
+msgstr ""
+
+#: builtin/checkout.c:115 builtin/checkout.c:148
+#, c-format
+msgid "path '%s' does not have their version"
+msgstr ""
+
+#: builtin/checkout.c:131
+#, c-format
+msgid "path '%s' does not have all necessary versions"
+msgstr ""
+
+#: builtin/checkout.c:175
+#, c-format
+msgid "path '%s' does not have necessary versions"
+msgstr ""
+
+#: builtin/checkout.c:192
+#, c-format
+msgid "path '%s': cannot merge"
+msgstr ""
+
+#: builtin/checkout.c:209
+#, c-format
+msgid "Unable to add merge result for '%s'"
+msgstr ""
+
+#: builtin/checkout.c:212 builtin/reset.c:158
+#, c-format
+msgid "make_cache_entry failed for path '%s'"
+msgstr ""
+
+#: builtin/checkout.c:234 builtin/checkout.c:392
+msgid "corrupt index file"
+msgstr ""
+
+#: builtin/checkout.c:264 builtin/checkout.c:271
+#, c-format
+msgid "path '%s' is unmerged"
+msgstr ""
+
+#: builtin/checkout.c:302 builtin/checkout.c:498 builtin/clone.c:583
+#: builtin/merge.c:809
+msgid "unable to write new index file"
+msgstr ""
+
+#: builtin/checkout.c:319 builtin/diff.c:298 builtin/merge.c:406
+msgid "diff_setup_done failed"
+msgstr ""
+
+#: builtin/checkout.c:414
+msgid "you need to resolve your current index first"
+msgstr ""
+
+#: builtin/checkout.c:533
+#, c-format
+msgid "Can not do reflog for '%s'\n"
+msgstr ""
+
+#: builtin/checkout.c:565
+msgid "HEAD is now at"
+msgstr ""
+
+#: builtin/checkout.c:572
+#, c-format
+msgid "Reset branch '%s'\n"
+msgstr ""
+
+#: builtin/checkout.c:575
+#, c-format
+msgid "Already on '%s'\n"
+msgstr ""
+
+#: builtin/checkout.c:579
+#, c-format
+msgid "Switched to and reset branch '%s'\n"
+msgstr ""
+
+#: builtin/checkout.c:581
+#, c-format
+msgid "Switched to a new branch '%s'\n"
+msgstr ""
+
+#: builtin/checkout.c:583
+#, c-format
+msgid "Switched to branch '%s'\n"
+msgstr ""
+
+#: builtin/checkout.c:639
+#, c-format
+msgid " ... and %d more.\n"
+msgstr ""
+
+#. The singular version
+#: builtin/checkout.c:645
+#, c-format
+msgid ""
+"Warning: you are leaving %d commit behind, not connected to\n"
+"any of your branches:\n"
+"\n"
+"%s\n"
+msgid_plural ""
+"Warning: you are leaving %d commits behind, not connected to\n"
+"any of your branches:\n"
+"\n"
+"%s\n"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/checkout.c:663
+#, c-format
+msgid ""
+"If you want to keep them by creating a new branch, this may be a good time\n"
+"to do so with:\n"
+"\n"
+" git branch new_branch_name %s\n"
+"\n"
+msgstr ""
+
+#: builtin/checkout.c:692
+msgid "internal error in revision walk"
+msgstr ""
+
+#: builtin/checkout.c:696
+msgid "Previous HEAD position was"
+msgstr ""
+
+#: builtin/checkout.c:722
+msgid "You are on a branch yet to be born"
+msgstr ""
+
+#. case (1)
+#: builtin/checkout.c:853
+#, c-format
+msgid "invalid reference: %s"
+msgstr ""
+
+#. case (1): want a tree
+#: builtin/checkout.c:892
+#, c-format
+msgid "reference is not a tree: %s"
+msgstr ""
+
+#: builtin/checkout.c:972
+msgid "-B cannot be used with -b"
+msgstr ""
+
+#: builtin/checkout.c:981
+msgid "--patch is incompatible with all other options"
+msgstr ""
+
+#: builtin/checkout.c:984
+msgid "--detach cannot be used with -b/-B/--orphan"
+msgstr ""
+
+#: builtin/checkout.c:986
+msgid "--detach cannot be used with -t"
+msgstr ""
+
+#: builtin/checkout.c:992
+msgid "--track needs a branch name"
+msgstr ""
+
+#: builtin/checkout.c:999
+msgid "Missing branch name; try -b"
+msgstr ""
+
+#: builtin/checkout.c:1005
+msgid "--orphan and -b|-B are mutually exclusive"
+msgstr ""
+
+#: builtin/checkout.c:1007
+msgid "--orphan cannot be used with -t"
+msgstr ""
+
+#: builtin/checkout.c:1017
+msgid "git checkout: -f and -m are incompatible"
+msgstr ""
+
+#: builtin/checkout.c:1051
+msgid "invalid path specification"
+msgstr ""
+
+#: builtin/checkout.c:1059
+#, c-format
+msgid ""
+"git checkout: updating paths is incompatible with switching branches.\n"
+"Did you intend to checkout '%s' which can not be resolved as commit?"
+msgstr ""
+
+#: builtin/checkout.c:1061
+msgid "git checkout: updating paths is incompatible with switching branches."
+msgstr ""
+
+#: builtin/checkout.c:1066
+msgid "git checkout: --detach does not take a path argument"
+msgstr ""
+
+#: builtin/checkout.c:1069
+msgid ""
+"git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
+"checking out of the index."
+msgstr ""
+
+#: builtin/checkout.c:1088
+msgid "Cannot switch branch to a non-commit."
+msgstr ""
+
+#: builtin/checkout.c:1091
+msgid "--ours/--theirs is incompatible with switching branches."
+msgstr ""
+
+#: builtin/clean.c:78
+msgid "-x and -X cannot be used together"
+msgstr ""
+
+#: builtin/clean.c:82
+msgid ""
+"clean.requireForce set to true and neither -n nor -f given; refusing to clean"
+msgstr ""
+
+#: builtin/clean.c:85
+msgid ""
+"clean.requireForce defaults to true and neither -n nor -f given; refusing to "
+"clean"
+msgstr ""
+
+#: builtin/clean.c:155 builtin/clean.c:176
+#, c-format
+msgid "Would remove %s\n"
+msgstr ""
+
+#: builtin/clean.c:159 builtin/clean.c:179
+#, c-format
+msgid "Removing %s\n"
+msgstr ""
+
+#: builtin/clean.c:162 builtin/clean.c:182
+#, c-format
+msgid "failed to remove %s"
+msgstr ""
+
+#: builtin/clean.c:166
+#, c-format
+msgid "Would not remove %s\n"
+msgstr ""
+
+#: builtin/clean.c:168
+#, c-format
+msgid "Not removing %s\n"
+msgstr ""
+
+#: builtin/clone.c:243
+#, c-format
+msgid "reference repository '%s' is not a local directory."
+msgstr ""
+
+#: builtin/clone.c:302
+#, c-format
+msgid "failed to open '%s'"
+msgstr ""
+
+#: builtin/clone.c:306
+#, c-format
+msgid "failed to create directory '%s'"
+msgstr ""
+
+#: builtin/clone.c:308 builtin/diff.c:75
+#, c-format
+msgid "failed to stat '%s'"
+msgstr ""
+
+#: builtin/clone.c:310
+#, c-format
+msgid "%s exists and is not a directory"
+msgstr ""
+
+#: builtin/clone.c:324
+#, c-format
+msgid "failed to stat %s\n"
+msgstr ""
+
+#: builtin/clone.c:341
+#, c-format
+msgid "failed to unlink '%s'"
+msgstr ""
+
+#: builtin/clone.c:346
+#, c-format
+msgid "failed to create link '%s'"
+msgstr ""
+
+#: builtin/clone.c:350
+#, c-format
+msgid "failed to copy file to '%s'"
+msgstr ""
+
+#: builtin/clone.c:373
+#, c-format
+msgid "done.\n"
+msgstr ""
+
+#: builtin/clone.c:440
+#, c-format
+msgid "Could not find remote branch %s to clone."
+msgstr ""
+
+#: builtin/clone.c:549
+msgid "remote HEAD refers to nonexistent ref, unable to checkout.\n"
+msgstr ""
+
+#: builtin/clone.c:639
+msgid "Too many arguments."
+msgstr ""
+
+#: builtin/clone.c:643
+msgid "You must specify a repository to clone."
+msgstr ""
+
+#: builtin/clone.c:654
+#, c-format
+msgid "--bare and --origin %s options are incompatible."
+msgstr ""
+
+#: builtin/clone.c:668
+#, c-format
+msgid "repository '%s' does not exist"
+msgstr ""
+
+#: builtin/clone.c:673
+msgid "--depth is ignored in local clones; use file:// instead."
+msgstr ""
+
+#: builtin/clone.c:683
+#, c-format
+msgid "destination path '%s' already exists and is not an empty directory."
+msgstr ""
+
+#: builtin/clone.c:693
+#, c-format
+msgid "working tree '%s' already exists."
+msgstr ""
+
+#: builtin/clone.c:706 builtin/clone.c:720
+#, c-format
+msgid "could not create leading directories of '%s'"
+msgstr ""
+
+#: builtin/clone.c:709
+#, c-format
+msgid "could not create work tree dir '%s'."
+msgstr ""
+
+#: builtin/clone.c:728
+#, c-format
+msgid "Cloning into bare repository '%s'...\n"
+msgstr ""
+
+#: builtin/clone.c:730
+#, c-format
+msgid "Cloning into '%s'...\n"
+msgstr ""
+
+#: builtin/clone.c:786
+#, c-format
+msgid "Don't know how to clone %s"
+msgstr ""
+
+#: builtin/clone.c:835
+#, c-format
+msgid "Remote branch %s not found in upstream %s"
+msgstr ""
+
+#: builtin/clone.c:842
+msgid "You appear to have cloned an empty repository."
+msgstr ""
+
+#: builtin/commit.c:42
+msgid ""
+"Your name and email address were configured automatically based\n"
+"on your username and hostname. Please check that they are accurate.\n"
+"You can suppress this message by setting them explicitly:\n"
+"\n"
+"    git config --global user.name \"Your Name\"\n"
+"    git config --global user.email you@example.com\n"
+"\n"
+"After doing this, you may fix the identity used for this commit with:\n"
+"\n"
+"    git commit --amend --reset-author\n"
+msgstr ""
+
+#: builtin/commit.c:54
+msgid ""
+"You asked to amend the most recent commit, but doing so would make\n"
+"it empty. You can repeat your command with --allow-empty, or you can\n"
+"remove the commit entirely with \"git reset HEAD^\".\n"
+msgstr ""
+
+#: builtin/commit.c:59
+msgid ""
+"The previous cherry-pick is now empty, possibly due to conflict resolution.\n"
+"If you wish to commit it anyway, use:\n"
+"\n"
+"    git commit --allow-empty\n"
+"\n"
+"Otherwise, please use 'git reset'\n"
+msgstr ""
+
+#: builtin/commit.c:205 builtin/reset.c:33
+msgid "merge"
+msgstr ""
+
+#: builtin/commit.c:208
+msgid "cherry-pick"
+msgstr ""
+
+#: builtin/commit.c:325
+msgid "failed to unpack HEAD tree object"
+msgstr ""
+
+#: builtin/commit.c:367
+msgid "unable to create temporary index"
+msgstr ""
+
+#: builtin/commit.c:373
+msgid "interactive add failed"
+msgstr ""
+
+#: builtin/commit.c:406 builtin/commit.c:427 builtin/commit.c:473
+msgid "unable to write new_index file"
+msgstr ""
+
+#: builtin/commit.c:457
+#, c-format
+msgid "cannot do a partial commit during a %s."
+msgstr ""
+
+#: builtin/commit.c:466
+msgid "cannot read the index"
+msgstr ""
+
+#: builtin/commit.c:486
+msgid "unable to write temporary index file"
+msgstr ""
+
+#: builtin/commit.c:550 builtin/commit.c:556
+#, c-format
+msgid "invalid commit: %s"
+msgstr ""
+
+#: builtin/commit.c:579
+msgid "malformed --author parameter"
+msgstr ""
+
+#: builtin/commit.c:635
+#, c-format
+msgid "Malformed ident string: '%s'"
+msgstr ""
+
+#: builtin/commit.c:670 builtin/commit.c:703 builtin/commit.c:1000
+#, c-format
+msgid "could not lookup commit %s"
+msgstr ""
+
+#: builtin/commit.c:682 builtin/shortlog.c:296
+#, c-format
+msgid "(reading log message from standard input)\n"
+msgstr ""
+
+#: builtin/commit.c:684
+msgid "could not read log from standard input"
+msgstr ""
+
+#: builtin/commit.c:688
+#, c-format
+msgid "could not read log file '%s'"
+msgstr ""
+
+#: builtin/commit.c:694
+msgid "commit has empty message"
+msgstr ""
+
+#: builtin/commit.c:710
+msgid "could not read MERGE_MSG"
+msgstr ""
+
+#: builtin/commit.c:714
+msgid "could not read SQUASH_MSG"
+msgstr ""
+
+#: builtin/commit.c:718
+#, c-format
+msgid "could not read '%s'"
+msgstr ""
+
+#: builtin/commit.c:746
+#, c-format
+msgid "could not open '%s'"
+msgstr ""
+
+#: builtin/commit.c:770
+msgid "could not write commit template"
+msgstr ""
+
+#: builtin/commit.c:783
+#, c-format
+msgid ""
+"\n"
+"It looks like you may be committing a %s.\n"
+"If this is not correct, please remove the file\n"
+"\t%s\n"
+"and try again.\n"
+msgstr ""
+
+#: builtin/commit.c:796
+msgid "Please enter the commit message for your changes."
+msgstr ""
+
+#: builtin/commit.c:799
+msgid ""
+" Lines starting\n"
+"with '#' will be ignored, and an empty message aborts the commit.\n"
+msgstr ""
+
+#: builtin/commit.c:804
+msgid ""
+" Lines starting\n"
+"with '#' will be kept; you may remove them yourself if you want to.\n"
+"An empty message aborts the commit.\n"
+msgstr ""
+
+#: builtin/commit.c:816
+#, c-format
+msgid "%sAuthor:    %s"
+msgstr ""
+
+#: builtin/commit.c:823
+#, c-format
+msgid "%sCommitter: %s"
+msgstr ""
+
+#: builtin/commit.c:843
+msgid "Cannot read index"
+msgstr ""
+
+#: builtin/commit.c:880
+msgid "Error building trees"
+msgstr ""
+
+#: builtin/commit.c:895 builtin/tag.c:357
+#, c-format
+msgid "Please supply the message using either -m or -F option.\n"
+msgstr ""
+
+#: builtin/commit.c:975
+#, c-format
+msgid "No existing author found with '%s'"
+msgstr ""
+
+#: builtin/commit.c:990 builtin/commit.c:1182
+#, c-format
+msgid "Invalid untracked files mode '%s'"
+msgstr ""
+
+#: builtin/commit.c:1030
+msgid "Using both --reset-author and --author does not make sense"
+msgstr ""
+
+#: builtin/commit.c:1041
+msgid "You have nothing to amend."
+msgstr ""
+
+#: builtin/commit.c:1043
+#, c-format
+msgid "You are in the middle of a %s -- cannot amend."
+msgstr ""
+
+#: builtin/commit.c:1045
+msgid "Options --squash and --fixup cannot be used together"
+msgstr ""
+
+#: builtin/commit.c:1055
+msgid "Only one of -c/-C/-F/--fixup can be used."
+msgstr ""
+
+#: builtin/commit.c:1057
+msgid "Option -m cannot be combined with -c/-C/-F/--fixup."
+msgstr ""
+
+#: builtin/commit.c:1063
+msgid "--reset-author can be used only with -C, -c or --amend."
+msgstr ""
+
+#: builtin/commit.c:1080
+msgid "Only one of --include/--only/--all/--interactive/--patch can be used."
+msgstr ""
+
+#: builtin/commit.c:1082
+msgid "No paths with --include/--only does not make sense."
+msgstr ""
+
+#: builtin/commit.c:1084
+msgid "Clever... amending the last one with dirty index."
+msgstr ""
+
+#: builtin/commit.c:1086
+msgid "Explicit paths specified without -i nor -o; assuming --only paths..."
+msgstr ""
+
+#: builtin/commit.c:1096 builtin/tag.c:556
+#, c-format
+msgid "Invalid cleanup mode %s"
+msgstr ""
+
+#: builtin/commit.c:1101
+msgid "Paths with -a does not make sense."
+msgstr ""
+
+#: builtin/commit.c:1280
+msgid "couldn't look up newly created commit"
+msgstr ""
+
+#: builtin/commit.c:1282
+msgid "could not parse newly created commit"
+msgstr ""
+
+#: builtin/commit.c:1323
+msgid "detached HEAD"
+msgstr ""
+
+#: builtin/commit.c:1325
+msgid " (root-commit)"
+msgstr ""
+
+#: builtin/commit.c:1415
+msgid "could not parse HEAD commit"
+msgstr ""
+
+#: builtin/commit.c:1452 builtin/merge.c:507
+#, c-format
+msgid "could not open '%s' for reading"
+msgstr ""
+
+#: builtin/commit.c:1459
+#, c-format
+msgid "Corrupt MERGE_HEAD file (%s)"
+msgstr ""
+
+#: builtin/commit.c:1466
+msgid "could not read MERGE_MODE"
+msgstr ""
+
+#: builtin/commit.c:1485
+#, c-format
+msgid "could not read commit message: %s"
+msgstr ""
+
+#: builtin/commit.c:1499
+#, c-format
+msgid "Aborting commit due to empty commit message.\n"
+msgstr ""
+
+#: builtin/commit.c:1514 builtin/merge.c:933 builtin/merge.c:966
+msgid "failed to write commit object"
+msgstr ""
+
+#: builtin/commit.c:1535
+msgid "cannot lock HEAD ref"
+msgstr ""
+
+#: builtin/commit.c:1539
+msgid "cannot update HEAD ref"
+msgstr ""
+
+#: builtin/commit.c:1550
+msgid ""
+"Repository has been updated, but unable to write\n"
+"new_index file. Check that disk is not full or quota is\n"
+"not exceeded, and then \"git reset HEAD\" to recover."
+msgstr ""
+
+#: builtin/describe.c:234
+#, c-format
+msgid "annotated tag %s not available"
+msgstr ""
+
+#: builtin/describe.c:238
+#, c-format
+msgid "annotated tag %s has no embedded name"
+msgstr ""
+
+#: builtin/describe.c:240
+#, c-format
+msgid "tag '%s' is really '%s' here"
+msgstr ""
+
+#: builtin/describe.c:267
+#, c-format
+msgid "Not a valid object name %s"
+msgstr ""
+
+#: builtin/describe.c:270
+#, c-format
+msgid "%s is not a valid '%s' object"
+msgstr ""
+
+#: builtin/describe.c:287
+#, c-format
+msgid "no tag exactly matches '%s'"
+msgstr ""
+
+#: builtin/describe.c:289
+#, c-format
+msgid "searching to describe %s\n"
+msgstr ""
+
+#: builtin/describe.c:329
+#, c-format
+msgid "finished search at %s\n"
+msgstr ""
+
+#: builtin/describe.c:353
+#, c-format
+msgid ""
+"No annotated tags can describe '%s'.\n"
+"However, there were unannotated tags: try --tags."
+msgstr ""
+
+#: builtin/describe.c:357
+#, c-format
+msgid ""
+"No tags can describe '%s'.\n"
+"Try --always, or create some tags."
+msgstr ""
+
+#: builtin/describe.c:378
+#, c-format
+msgid "traversed %lu commits\n"
+msgstr ""
+
+#: builtin/describe.c:381
+#, c-format
+msgid ""
+"more than %i tags found; listed %i most recent\n"
+"gave up search at %s\n"
+msgstr ""
+
+#: builtin/describe.c:436
+msgid "--long is incompatible with --abbrev=0"
+msgstr ""
+
+#: builtin/describe.c:462
+msgid "No names found, cannot describe anything."
+msgstr ""
+
+#: builtin/describe.c:482
+msgid "--dirty is incompatible with committishes"
+msgstr ""
+
+#: builtin/diff.c:77
+#, c-format
+msgid "'%s': not a regular file or symlink"
+msgstr ""
+
+#: builtin/diff.c:220
+#, c-format
+msgid "invalid option: %s"
+msgstr ""
+
+#: builtin/diff.c:293
+msgid "Not a git repository"
+msgstr ""
+
+#: builtin/diff.c:343
+#, c-format
+msgid "invalid object '%s' given."
+msgstr ""
+
+#: builtin/diff.c:348
+#, c-format
+msgid "more than %d trees given: '%s'"
+msgstr ""
+
+#: builtin/diff.c:358
+#, c-format
+msgid "more than two blobs given: '%s'"
+msgstr ""
+
+#: builtin/diff.c:366
+#, c-format
+msgid "unhandled object '%s' given."
+msgstr ""
+
+#: builtin/fetch.c:200
+msgid "Couldn't find remote ref HEAD"
+msgstr ""
+
+#: builtin/fetch.c:252
+#, c-format
+msgid "object %s not found"
+msgstr ""
+
+#: builtin/fetch.c:258
+msgid "[up to date]"
+msgstr ""
+
+#: builtin/fetch.c:272
+#, c-format
+msgid "! %-*s %-*s -> %s  (can't fetch in current branch)"
+msgstr ""
+
+#: builtin/fetch.c:273 builtin/fetch.c:351
+msgid "[rejected]"
+msgstr ""
+
+#: builtin/fetch.c:284
+msgid "[tag update]"
+msgstr ""
+
+#: builtin/fetch.c:286 builtin/fetch.c:313 builtin/fetch.c:331
+msgid "  (unable to update local ref)"
+msgstr ""
+
+#: builtin/fetch.c:298
+msgid "[new tag]"
+msgstr ""
+
+#: builtin/fetch.c:302
+msgid "[new branch]"
+msgstr ""
+
+#: builtin/fetch.c:347
+msgid "unable to update local ref"
+msgstr ""
+
+#: builtin/fetch.c:347
+msgid "forced update"
+msgstr ""
+
+#: builtin/fetch.c:353
+msgid "(non-fast-forward)"
+msgstr ""
+
+#: builtin/fetch.c:384 builtin/fetch.c:676
+#, c-format
+msgid "cannot open %s: %s\n"
+msgstr ""
+
+#: builtin/fetch.c:393
+#, c-format
+msgid "%s did not send all necessary objects\n"
+msgstr ""
+
+#: builtin/fetch.c:479
+#, c-format
+msgid "From %.*s\n"
+msgstr ""
+
+#: builtin/fetch.c:490
+#, c-format
+msgid ""
+"some local refs could not be updated; try running\n"
+" 'git remote prune %s' to remove any old, conflicting branches"
+msgstr ""
+
+#: builtin/fetch.c:540
+#, c-format
+msgid "   (%s will become dangling)\n"
+msgstr ""
+
+#: builtin/fetch.c:541
+#, c-format
+msgid "   (%s has become dangling)\n"
+msgstr ""
+
+#: builtin/fetch.c:548
+msgid "[deleted]"
+msgstr ""
+
+#: builtin/fetch.c:549
+msgid "(none)"
+msgstr ""
+
+#: builtin/fetch.c:666
+#, c-format
+msgid "Refusing to fetch into current branch %s of non-bare repository"
+msgstr ""
+
+#: builtin/fetch.c:700
+#, c-format
+msgid "Don't know how to fetch from %s"
+msgstr ""
+
+#: builtin/fetch.c:777
+#, c-format
+msgid "Option \"%s\" value \"%s\" is not valid for %s"
+msgstr ""
+
+#: builtin/fetch.c:780
+#, c-format
+msgid "Option \"%s\" is ignored for %s\n"
+msgstr ""
+
+#: builtin/fetch.c:879
+#, c-format
+msgid "Fetching %s\n"
+msgstr ""
+
+#: builtin/fetch.c:881
+#, c-format
+msgid "Could not fetch %s"
+msgstr ""
+
+#: builtin/fetch.c:898
+msgid ""
+"No remote repository specified.  Please, specify either a URL or a\n"
+"remote name from which new revisions should be fetched."
+msgstr ""
+
+#: builtin/fetch.c:918
+msgid "You need to specify a tag name."
+msgstr ""
+
+#: builtin/fetch.c:970
+msgid "fetch --all does not take a repository argument"
+msgstr ""
+
+#: builtin/fetch.c:972
+msgid "fetch --all does not make sense with refspecs"
+msgstr ""
+
+#: builtin/fetch.c:983
+#, c-format
+msgid "No such remote or remote group: %s"
+msgstr ""
+
+#: builtin/fetch.c:991
+msgid "Fetching a group and specifying refspecs does not make sense"
+msgstr ""
+
+#: builtin/gc.c:63
+#, c-format
+msgid "Invalid %s: '%s'"
+msgstr ""
+
+#: builtin/gc.c:78
+msgid "Too many options specified"
+msgstr ""
+
+#: builtin/gc.c:103
+#, c-format
+msgid "insanely long object directory %.*s"
+msgstr ""
+
+#: builtin/gc.c:223
+#, c-format
+msgid "Auto packing the repository for optimum performance.\n"
+msgstr ""
+
+#: builtin/gc.c:226
+#, c-format
+msgid ""
+"Auto packing the repository for optimum performance. You may also\n"
+"run \"git gc\" manually. See \"git help gc\" for more information.\n"
+msgstr ""
+
+#: builtin/gc.c:256
+msgid ""
+"There are too many unreachable loose objects; run 'git prune' to remove them."
+msgstr ""
+
+#: builtin/grep.c:216
+#, c-format
+msgid "grep: failed to create thread: %s"
+msgstr ""
+
+#: builtin/grep.c:402
+#, c-format
+msgid "Failed to chdir: %s"
+msgstr ""
+
+#: builtin/grep.c:478 builtin/grep.c:512
+#, c-format
+msgid "unable to read tree (%s)"
+msgstr ""
+
+#: builtin/grep.c:526
+#, c-format
+msgid "unable to grep from object of type %s"
+msgstr ""
+
+#: builtin/grep.c:584
+#, c-format
+msgid "switch `%c' expects a numerical value"
+msgstr ""
+
+#: builtin/grep.c:601
+#, c-format
+msgid "cannot open '%s'"
+msgstr ""
+
+#: builtin/grep.c:889
+msgid "no pattern given."
+msgstr ""
+
+#: builtin/grep.c:903
+#, c-format
+msgid "bad object %s"
+msgstr ""
+
+#: builtin/grep.c:944
+msgid "--open-files-in-pager only works on the worktree"
+msgstr ""
+
+#: builtin/grep.c:967
+msgid "--cached or --untracked cannot be used with --no-index."
+msgstr ""
+
+#: builtin/grep.c:972
+msgid "--no-index or --untracked cannot be used with revs."
+msgstr ""
+
+#: builtin/grep.c:975
+msgid "--[no-]exclude-standard cannot be used for tracked contents."
+msgstr ""
+
+#: builtin/grep.c:983
+msgid "both --cached and trees are given."
+msgstr ""
+
+#: builtin/init-db.c:35
+#, c-format
+msgid "Could not make %s writable by group"
+msgstr ""
+
+#: builtin/init-db.c:62
+#, c-format
+msgid "insanely long template name %s"
+msgstr ""
+
+#: builtin/init-db.c:67
+#, c-format
+msgid "cannot stat '%s'"
+msgstr ""
+
+#: builtin/init-db.c:73
+#, c-format
+msgid "cannot stat template '%s'"
+msgstr ""
+
+#: builtin/init-db.c:80
+#, c-format
+msgid "cannot opendir '%s'"
+msgstr ""
+
+#: builtin/init-db.c:97
+#, c-format
+msgid "cannot readlink '%s'"
+msgstr ""
+
+#: builtin/init-db.c:99
+#, c-format
+msgid "insanely long symlink %s"
+msgstr ""
+
+#: builtin/init-db.c:102
+#, c-format
+msgid "cannot symlink '%s' '%s'"
+msgstr ""
+
+#: builtin/init-db.c:106
+#, c-format
+msgid "cannot copy '%s' to '%s'"
+msgstr ""
+
+#: builtin/init-db.c:110
+#, c-format
+msgid "ignoring template %s"
+msgstr ""
+
+#: builtin/init-db.c:133
+#, c-format
+msgid "insanely long template path %s"
+msgstr ""
+
+#: builtin/init-db.c:141
+#, c-format
+msgid "templates not found %s"
+msgstr ""
+
+#: builtin/init-db.c:154
+#, c-format
+msgid "not copying templates of a wrong format version %d from '%s'"
+msgstr ""
+
+#: builtin/init-db.c:192
+#, c-format
+msgid "insane git directory %s"
+msgstr ""
+
+#: builtin/init-db.c:322 builtin/init-db.c:325
+#, c-format
+msgid "%s already exists"
+msgstr ""
+
+#: builtin/init-db.c:354
+#, c-format
+msgid "unable to handle file type %d"
+msgstr ""
+
+#: builtin/init-db.c:357
+#, c-format
+msgid "unable to move %s to %s"
+msgstr ""
+
+#: builtin/init-db.c:362
+#, c-format
+msgid "Could not create git link %s"
+msgstr ""
+
+#.
+#. * TRANSLATORS: The first '%s' is either "Reinitialized
+#. * existing" or "Initialized empty", the second " shared" or
+#. * "", and the last '%s%s' is the verbatim directory name.
+#.
+#: builtin/init-db.c:419
+#, c-format
+msgid "%s%s Git repository in %s%s\n"
+msgstr ""
+
+#: builtin/init-db.c:420
+msgid "Reinitialized existing"
+msgstr ""
+
+#: builtin/init-db.c:420
+msgid "Initialized empty"
+msgstr ""
+
+#: builtin/init-db.c:421
+msgid " shared"
+msgstr ""
+
+#: builtin/init-db.c:440
+msgid "cannot tell cwd"
+msgstr ""
+
+#: builtin/init-db.c:521 builtin/init-db.c:528
+#, c-format
+msgid "cannot mkdir %s"
+msgstr ""
+
+#: builtin/init-db.c:532
+#, c-format
+msgid "cannot chdir to %s"
+msgstr ""
+
+#: builtin/init-db.c:554
+#, c-format
+msgid ""
+"%s (or --work-tree=<directory>) not allowed without specifying %s (or --git-"
+"dir=<directory>)"
+msgstr ""
+
+#: builtin/init-db.c:578
+msgid "Cannot access current working directory"
+msgstr ""
+
+#: builtin/init-db.c:585
+#, c-format
+msgid "Cannot access work tree '%s'"
+msgstr ""
+
+#: builtin/log.c:185
+#, c-format
+msgid "Final output: %d %s\n"
+msgstr ""
+
+#: builtin/log.c:393 builtin/log.c:479
+#, c-format
+msgid "Could not read object %s"
+msgstr ""
+
+#: builtin/log.c:503
+#, c-format
+msgid "Unknown type: %d"
+msgstr ""
+
+#: builtin/log.c:592
+msgid "format.headers without value"
+msgstr ""
+
+#: builtin/log.c:665
+msgid "name of output directory is too long"
+msgstr ""
+
+#: builtin/log.c:676
+#, c-format
+msgid "Cannot open patch file %s"
+msgstr ""
+
+#: builtin/log.c:690
+msgid "Need exactly one range."
+msgstr ""
+
+#: builtin/log.c:698
+msgid "Not a range."
+msgstr ""
+
+#: builtin/log.c:735
+msgid "Could not extract email from committer identity."
+msgstr ""
+
+#: builtin/log.c:781
+msgid "Cover letter needs email format"
+msgstr ""
+
+#: builtin/log.c:875
+#, c-format
+msgid "insane in-reply-to: %s"
+msgstr ""
+
+#: builtin/log.c:948
+msgid "Two output directories?"
+msgstr ""
+
+#: builtin/log.c:1169
+#, c-format
+msgid "bogus committer info %s"
+msgstr ""
+
+#: builtin/log.c:1214
+msgid "-n and -k are mutually exclusive."
+msgstr ""
+
+#: builtin/log.c:1216
+msgid "--subject-prefix and -k are mutually exclusive."
+msgstr ""
+
+#: builtin/log.c:1221 builtin/shortlog.c:284
+#, c-format
+msgid "unrecognized argument: %s"
+msgstr ""
+
+#: builtin/log.c:1224
+msgid "--name-only does not make sense"
+msgstr ""
+
+#: builtin/log.c:1226
+msgid "--name-status does not make sense"
+msgstr ""
+
+#: builtin/log.c:1228
+msgid "--check does not make sense"
+msgstr ""
+
+#: builtin/log.c:1251
+msgid "standard output, or directory, which one?"
+msgstr ""
+
+#: builtin/log.c:1253
+#, c-format
+msgid "Could not create directory '%s'"
+msgstr ""
+
+#: builtin/log.c:1406
+msgid "Failed to create output files"
+msgstr ""
+
+#: builtin/log.c:1510
+#, c-format
+msgid ""
+"Could not find a tracked remote branch, please specify <upstream> manually.\n"
+msgstr ""
+
+#: builtin/log.c:1526 builtin/log.c:1528 builtin/log.c:1540
+#, c-format
+msgid "Unknown commit %s"
+msgstr ""
+
+#: builtin/merge.c:91
+msgid "switch `m' requires a value"
+msgstr ""
+
+#: builtin/merge.c:128
+#, c-format
+msgid "Could not find merge strategy '%s'.\n"
+msgstr ""
+
+#: builtin/merge.c:129
+#, c-format
+msgid "Available strategies are:"
+msgstr ""
+
+#: builtin/merge.c:134
+#, c-format
+msgid "Available custom strategies are:"
+msgstr ""
+
+#: builtin/merge.c:241
+msgid "could not run stash."
+msgstr ""
+
+#: builtin/merge.c:246
+msgid "stash failed"
+msgstr ""
+
+#: builtin/merge.c:251
+#, c-format
+msgid "not a valid object: %s"
+msgstr ""
+
+#: builtin/merge.c:270 builtin/merge.c:287
+msgid "read-tree failed"
+msgstr ""
+
+#: builtin/merge.c:317
+msgid " (nothing to squash)"
+msgstr ""
+
+#: builtin/merge.c:330
+#, c-format
+msgid "Squash commit -- not updating HEAD\n"
+msgstr ""
+
+#: builtin/merge.c:362
+msgid "Writing SQUASH_MSG"
+msgstr ""
+
+#: builtin/merge.c:364
+msgid "Finishing SQUASH_MSG"
+msgstr ""
+
+#: builtin/merge.c:386
+#, c-format
+msgid "No merge message -- not updating HEAD\n"
+msgstr ""
+
+#: builtin/merge.c:435
+#, c-format
+msgid "'%s' does not point to a commit"
+msgstr ""
+
+#: builtin/merge.c:534
+#, c-format
+msgid "Bad branch.%s.mergeoptions string: %s"
+msgstr ""
+
+#: builtin/merge.c:627
+msgid "git write-tree failed to write a tree"
+msgstr ""
+
+#: builtin/merge.c:677
+msgid "failed to read the cache"
+msgstr ""
+
+#: builtin/merge.c:694
+msgid "Unable to write index."
+msgstr ""
+
+#: builtin/merge.c:707
+msgid "Not handling anything other than two heads merge."
+msgstr ""
+
+#: builtin/merge.c:721
+#, c-format
+msgid "Unknown option for merge-recursive: -X%s"
+msgstr ""
+
+#: builtin/merge.c:735
+#, c-format
+msgid "unable to write %s"
+msgstr ""
+
+#: builtin/merge.c:874
+#, c-format
+msgid "Could not read from '%s'"
+msgstr ""
+
+#: builtin/merge.c:883
+#, c-format
+msgid "Not committing merge; use 'git commit' to complete the merge.\n"
+msgstr ""
+
+#: builtin/merge.c:889
+msgid ""
+"Please enter a commit message to explain why this merge is necessary,\n"
+"especially if it merges an updated upstream into a topic branch.\n"
+"\n"
+"Lines starting with '#' will be ignored, and an empty message aborts\n"
+"the commit.\n"
+msgstr ""
+
+#: builtin/merge.c:913
+msgid "Empty commit message."
+msgstr ""
+
+#: builtin/merge.c:925
+#, c-format
+msgid "Wonderful.\n"
+msgstr ""
+
+#: builtin/merge.c:998
+#, c-format
+msgid "Automatic merge failed; fix conflicts and then commit the result.\n"
+msgstr ""
+
+#: builtin/merge.c:1014
+#, c-format
+msgid "'%s' is not a commit"
+msgstr ""
+
+#: builtin/merge.c:1055
+msgid "No current branch."
+msgstr ""
+
+#: builtin/merge.c:1057
+msgid "No remote for the current branch."
+msgstr ""
+
+#: builtin/merge.c:1059
+msgid "No default upstream defined for the current branch."
+msgstr ""
+
+#: builtin/merge.c:1064
+#, c-format
+msgid "No remote tracking branch for %s from %s"
+msgstr ""
+
+#: builtin/merge.c:1186
+msgid "There is no merge to abort (MERGE_HEAD missing)."
+msgstr ""
+
+#: builtin/merge.c:1202 git-pull.sh:31
+msgid ""
+"You have not concluded your merge (MERGE_HEAD exists).\n"
+"Please, commit your changes before you can merge."
+msgstr ""
+
+#: builtin/merge.c:1205 git-pull.sh:34
+msgid "You have not concluded your merge (MERGE_HEAD exists)."
+msgstr ""
+
+#: builtin/merge.c:1209
+msgid ""
+"You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
+"Please, commit your changes before you can merge."
+msgstr ""
+
+#: builtin/merge.c:1212
+msgid "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."
+msgstr ""
+
+#: builtin/merge.c:1221
+msgid "You cannot combine --squash with --no-ff."
+msgstr ""
+
+#: builtin/merge.c:1226
+msgid "You cannot combine --no-ff with --ff-only."
+msgstr ""
+
+#: builtin/merge.c:1233
+msgid "No commit specified and merge.defaultToUpstream not set."
+msgstr ""
+
+#: builtin/merge.c:1264
+msgid "Can merge only exactly one commit into empty head"
+msgstr ""
+
+#: builtin/merge.c:1267
+msgid "Squash commit into empty head not supported yet"
+msgstr ""
+
+#: builtin/merge.c:1269
+msgid "Non-fast-forward commit does not make sense into an empty head"
+msgstr ""
+
+#: builtin/merge.c:1273 builtin/merge.c:1317
+#, c-format
+msgid "%s - not something we can merge"
+msgstr ""
+
+#: builtin/merge.c:1383
+#, c-format
+msgid "Updating %s..%s\n"
+msgstr ""
+
+#: builtin/merge.c:1421
+#, c-format
+msgid "Trying really trivial in-index merge...\n"
+msgstr ""
+
+#: builtin/merge.c:1428
+#, c-format
+msgid "Nope.\n"
+msgstr ""
+
+#: builtin/merge.c:1460
+msgid "Not possible to fast-forward, aborting."
+msgstr ""
+
+#: builtin/merge.c:1483 builtin/merge.c:1560
+#, c-format
+msgid "Rewinding the tree to pristine...\n"
+msgstr ""
+
+#: builtin/merge.c:1487
+#, c-format
+msgid "Trying merge strategy %s...\n"
+msgstr ""
+
+#: builtin/merge.c:1551
+#, c-format
+msgid "No merge strategy handled the merge.\n"
+msgstr ""
+
+#: builtin/merge.c:1553
+#, c-format
+msgid "Merge with strategy %s failed.\n"
+msgstr ""
+
+#: builtin/merge.c:1562
+#, c-format
+msgid "Using the %s to prepare resolving by hand.\n"
+msgstr ""
+
+#: builtin/merge.c:1573
+#, c-format
+msgid "Automatic merge went well; stopped before committing as requested\n"
+msgstr ""
+
+#: builtin/mv.c:108
+#, c-format
+msgid "Checking rename of '%s' to '%s'\n"
+msgstr ""
+
+#: builtin/mv.c:112
+msgid "bad source"
+msgstr ""
+
+#: builtin/mv.c:115
+msgid "can not move directory into itself"
+msgstr ""
+
+#: builtin/mv.c:118
+msgid "cannot move directory over file"
+msgstr ""
+
+#: builtin/mv.c:128
+#, c-format
+msgid "Huh? %.*s is in index?"
+msgstr ""
+
+#: builtin/mv.c:140
+msgid "source directory is empty"
+msgstr ""
+
+#: builtin/mv.c:171
+msgid "not under version control"
+msgstr ""
+
+#: builtin/mv.c:173
+msgid "destination exists"
+msgstr ""
+
+#: builtin/mv.c:181
+#, c-format
+msgid "overwriting '%s'"
+msgstr ""
+
+#: builtin/mv.c:184
+msgid "Cannot overwrite"
+msgstr ""
+
+#: builtin/mv.c:187
+msgid "multiple sources for the same target"
+msgstr ""
+
+#: builtin/mv.c:202
+#, c-format
+msgid "%s, source=%s, destination=%s"
+msgstr ""
+
+#: builtin/mv.c:212
+#, c-format
+msgid "Renaming %s to %s\n"
+msgstr ""
+
+#: builtin/mv.c:215
+#, c-format
+msgid "renaming '%s' failed"
+msgstr ""
+
+#: builtin/notes.c:139
+#, c-format
+msgid "unable to start 'show' for object '%s'"
+msgstr ""
+
+#: builtin/notes.c:145
+msgid "can't fdopen 'show' output fd"
+msgstr ""
+
+#: builtin/notes.c:155
+#, c-format
+msgid "failed to close pipe to 'show' for object '%s'"
+msgstr ""
+
+#: builtin/notes.c:158
+#, c-format
+msgid "failed to finish 'show' for object '%s'"
+msgstr ""
+
+#: builtin/notes.c:175 builtin/tag.c:343
+#, c-format
+msgid "could not create file '%s'"
+msgstr ""
+
+#: builtin/notes.c:189
+msgid "Please supply the note contents using either -m or -F option"
+msgstr ""
+
+#: builtin/notes.c:210 builtin/notes.c:973
+#, c-format
+msgid "Removing note for object %s\n"
+msgstr ""
+
+#: builtin/notes.c:215
+msgid "unable to write note object"
+msgstr ""
+
+#: builtin/notes.c:217
+#, c-format
+msgid "The note contents has been left in %s"
+msgstr ""
+
+#: builtin/notes.c:251 builtin/tag.c:521
+#, c-format
+msgid "cannot read '%s'"
+msgstr ""
+
+#: builtin/notes.c:253 builtin/tag.c:524
+#, c-format
+msgid "could not open or read '%s'"
+msgstr ""
+
+#: builtin/notes.c:272 builtin/notes.c:445 builtin/notes.c:447
+#: builtin/notes.c:507 builtin/notes.c:561 builtin/notes.c:644
+#: builtin/notes.c:649 builtin/notes.c:724 builtin/notes.c:766
+#: builtin/notes.c:968 builtin/reset.c:293 builtin/tag.c:537
+#, c-format
+msgid "Failed to resolve '%s' as a valid ref."
+msgstr ""
+
+#: builtin/notes.c:275
+#, c-format
+msgid "Failed to read object '%s'."
+msgstr ""
+
+#: builtin/notes.c:299
+msgid "Cannot commit uninitialized/unreferenced notes tree"
+msgstr ""
+
+#: builtin/notes.c:340
+#, c-format
+msgid "Bad notes.rewriteMode value: '%s'"
+msgstr ""
+
+#: builtin/notes.c:350
+#, c-format
+msgid "Refusing to rewrite notes in %s (outside of refs/notes/)"
+msgstr ""
+
+#. TRANSLATORS: The first %s is the name of the
+#. environment variable, the second %s is its value
+#: builtin/notes.c:377
+#, c-format
+msgid "Bad %s value: '%s'"
+msgstr ""
+
+#: builtin/notes.c:441
+#, c-format
+msgid "Malformed input line: '%s'."
+msgstr ""
+
+#: builtin/notes.c:456
+#, c-format
+msgid "Failed to copy notes from '%s' to '%s'"
+msgstr ""
+
+#: builtin/notes.c:500 builtin/notes.c:554 builtin/notes.c:627
+#: builtin/notes.c:639 builtin/notes.c:712 builtin/notes.c:759
+#: builtin/notes.c:1033
+msgid "too many parameters"
+msgstr ""
+
+#: builtin/notes.c:513 builtin/notes.c:772
+#, c-format
+msgid "No note found for object %s."
+msgstr ""
+
+#: builtin/notes.c:580
+#, c-format
+msgid ""
+"Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite "
+"existing notes"
+msgstr ""
+
+#: builtin/notes.c:585 builtin/notes.c:662
+#, c-format
+msgid "Overwriting existing notes for object %s\n"
+msgstr ""
+
+#: builtin/notes.c:635
+msgid "too few parameters"
+msgstr ""
+
+#: builtin/notes.c:656
+#, c-format
+msgid ""
+"Cannot copy notes. Found existing notes for object %s. Use '-f' to overwrite "
+"existing notes"
+msgstr ""
+
+#: builtin/notes.c:668
+#, c-format
+msgid "Missing notes on source object %s. Cannot copy."
+msgstr ""
+
+#: builtin/notes.c:717
+#, c-format
+msgid ""
+"The -m/-F/-c/-C options have been deprecated for the 'edit' subcommand.\n"
+"Please use 'git notes add -f -m/-F/-c/-C' instead.\n"
+msgstr ""
+
+#: builtin/notes.c:971
+#, c-format
+msgid "Object %s has no note\n"
+msgstr ""
+
+#: builtin/notes.c:1103
+#, c-format
+msgid "Unknown subcommand: %s"
+msgstr ""
+
+#: builtin/pack-objects.c:2310
+#, c-format
+msgid "unsupported index version %s"
+msgstr ""
+
+#: builtin/pack-objects.c:2314
+#, c-format
+msgid "bad index version '%s'"
+msgstr ""
+
+#: builtin/pack-objects.c:2322
+#, c-format
+msgid "option %s does not accept negative form"
+msgstr ""
+
+#: builtin/pack-objects.c:2326
+#, c-format
+msgid "unable to parse value '%s' for option %s"
+msgstr ""
+
+#: builtin/push.c:44
+msgid "tag shorthand without <tag>"
+msgstr ""
+
+#: builtin/push.c:63
+msgid "--delete only accepts plain target ref names"
+msgstr ""
+
+#: builtin/push.c:73
+#, c-format
+msgid ""
+"You are not currently on a branch.\n"
+"To push the history leading to the current (detached HEAD)\n"
+"state now, use\n"
+"\n"
+"    git push %s HEAD:<name-of-remote-branch>\n"
+msgstr ""
+
+#: builtin/push.c:80
+#, c-format
+msgid ""
+"The current branch %s has no upstream branch.\n"
+"To push the current branch and set the remote as upstream, use\n"
+"\n"
+"    git push --set-upstream %s %s\n"
+msgstr ""
+
+#: builtin/push.c:88
+#, c-format
+msgid "The current branch %s has multiple upstream branches, refusing to push."
+msgstr ""
+
+#: builtin/push.c:111
+msgid ""
+"You didn't specify any refspecs to push, and push.default is \"nothing\"."
+msgstr ""
+
+#: builtin/push.c:131
+#, c-format
+msgid "Pushing to %s\n"
+msgstr ""
+
+#: builtin/push.c:135
+#, c-format
+msgid "failed to push some refs to '%s'"
+msgstr ""
+
+#: builtin/push.c:143
+#, c-format
+msgid ""
+"To prevent you from losing history, non-fast-forward updates were rejected\n"
+"Merge the remote changes (e.g. 'git pull') before pushing again.  See the\n"
+"'Note about fast-forwards' section of 'git push --help' for details.\n"
+msgstr ""
+
+#: builtin/push.c:160
+#, c-format
+msgid "bad repository '%s'"
+msgstr ""
+
+#: builtin/push.c:161
+msgid ""
+"No configured push destination.\n"
+"Either specify the URL from the command-line or configure a remote "
+"repository using\n"
+"\n"
+"    git remote add <name> <url>\n"
+"\n"
+"and then push using the remote name\n"
+"\n"
+"    git push <name>\n"
+msgstr ""
+
+#: builtin/push.c:176
+msgid "--all and --tags are incompatible"
+msgstr ""
+
+#: builtin/push.c:177
+msgid "--all can't be combined with refspecs"
+msgstr ""
+
+#: builtin/push.c:182
+msgid "--mirror and --tags are incompatible"
+msgstr ""
+
+#: builtin/push.c:183
+msgid "--mirror can't be combined with refspecs"
+msgstr ""
+
+#: builtin/push.c:188
+msgid "--all and --mirror are incompatible"
+msgstr ""
+
+#: builtin/push.c:274
+msgid "--delete is incompatible with --all, --mirror and --tags"
+msgstr ""
+
+#: builtin/push.c:276
+msgid "--delete doesn't make sense without any refs"
+msgstr ""
+
+#: builtin/reset.c:33
+msgid "mixed"
+msgstr ""
+
+#: builtin/reset.c:33
+msgid "soft"
+msgstr ""
+
+#: builtin/reset.c:33
+msgid "hard"
+msgstr ""
+
+#: builtin/reset.c:33
+msgid "keep"
+msgstr ""
+
+#: builtin/reset.c:77
+msgid "You do not have a valid HEAD."
+msgstr ""
+
+#: builtin/reset.c:79
+msgid "Failed to find tree of HEAD."
+msgstr ""
+
+#: builtin/reset.c:85
+#, c-format
+msgid "Failed to find tree of %s."
+msgstr ""
+
+#: builtin/reset.c:96
+msgid "Could not write new index file."
+msgstr ""
+
+#: builtin/reset.c:106
+#, c-format
+msgid "HEAD is now at %s"
+msgstr ""
+
+#: builtin/reset.c:130
+msgid "Could not read index"
+msgstr ""
+
+#: builtin/reset.c:133
+msgid "Unstaged changes after reset:"
+msgstr ""
+
+#: builtin/reset.c:223
+#, c-format
+msgid "Cannot do a %s reset in the middle of a merge."
+msgstr ""
+
+#: builtin/reset.c:297
+#, c-format
+msgid "Could not parse object '%s'."
+msgstr ""
+
+#: builtin/reset.c:302
+msgid "--patch is incompatible with --{hard,mixed,soft}"
+msgstr ""
+
+#: builtin/reset.c:311
+msgid "--mixed with paths is deprecated; use 'git reset -- <paths>' instead."
+msgstr ""
+
+#: builtin/reset.c:313
+#, c-format
+msgid "Cannot do %s reset with paths."
+msgstr ""
+
+#: builtin/reset.c:325
+#, c-format
+msgid "%s reset is not allowed in a bare repository"
+msgstr ""
+
+#: builtin/reset.c:341
+#, c-format
+msgid "Could not reset index file to revision '%s'."
+msgstr ""
+
+#: builtin/revert.c:70 builtin/revert.c:91
+#, c-format
+msgid "%s: %s cannot be used with %s"
+msgstr ""
+
+#: builtin/revert.c:126
+msgid "program error"
+msgstr ""
+
+#: builtin/revert.c:209
+msgid "revert failed"
+msgstr ""
+
+#: builtin/revert.c:224
+msgid "cherry-pick failed"
+msgstr ""
+
+#: builtin/rm.c:109
+#, c-format
+msgid ""
+"'%s' has staged content different from both the file and the HEAD\n"
+"(use -f to force removal)"
+msgstr ""
+
+#: builtin/rm.c:115
+#, c-format
+msgid ""
+"'%s' has changes staged in the index\n"
+"(use --cached to keep the file, or -f to force removal)"
+msgstr ""
+
+#: builtin/rm.c:119
+#, c-format
+msgid ""
+"'%s' has local modifications\n"
+"(use --cached to keep the file, or -f to force removal)"
+msgstr ""
+
+#: builtin/rm.c:194
+#, c-format
+msgid "not removing '%s' recursively without -r"
+msgstr ""
+
+#: builtin/rm.c:230
+#, c-format
+msgid "git rm: unable to remove %s"
+msgstr ""
+
+#: builtin/shortlog.c:157
+#, c-format
+msgid "Missing author: %s"
+msgstr ""
+
+#: builtin/tag.c:58
+#, c-format
+msgid "malformed object at '%s'"
+msgstr ""
+
+#: builtin/tag.c:205
+#, c-format
+msgid "tag name too long: %.*s..."
+msgstr ""
+
+#: builtin/tag.c:210
+#, c-format
+msgid "tag '%s' not found."
+msgstr ""
+
+#: builtin/tag.c:225
+#, c-format
+msgid "Deleted tag '%s' (was %s)\n"
+msgstr ""
+
+#: builtin/tag.c:237
+#, c-format
+msgid "could not verify the tag '%s'"
+msgstr ""
+
+#: builtin/tag.c:247
+msgid ""
+"\n"
+"#\n"
+"# Write a tag message\n"
+"# Lines starting with '#' will be ignored.\n"
+"#\n"
+msgstr ""
+
+#: builtin/tag.c:254
+msgid ""
+"\n"
+"#\n"
+"# Write a tag message\n"
+"# Lines starting with '#' will be kept; you may remove them yourself if you "
+"want to.\n"
+"#\n"
+msgstr ""
+
+#: builtin/tag.c:294
+msgid "unable to sign the tag"
+msgstr ""
+
+#: builtin/tag.c:296
+msgid "unable to write tag file"
+msgstr ""
+
+#: builtin/tag.c:321
+msgid "bad object type."
+msgstr ""
+
+#: builtin/tag.c:334
+msgid "tag header too big."
+msgstr ""
+
+#: builtin/tag.c:366
+msgid "no tag message?"
+msgstr ""
+
+#: builtin/tag.c:372
+#, c-format
+msgid "The tag message has been left in %s\n"
+msgstr ""
+
+#: builtin/tag.c:421
+msgid "switch 'points-at' requires an object"
+msgstr ""
+
+#: builtin/tag.c:423
+#, c-format
+msgid "malformed object name '%s'"
+msgstr ""
+
+#: builtin/tag.c:502
+msgid "-n option is only allowed with -l."
+msgstr ""
+
+#: builtin/tag.c:504
+msgid "--contains option is only allowed with -l."
+msgstr ""
+
+#: builtin/tag.c:506
+msgid "--points-at option is only allowed with -l."
+msgstr ""
+
+#: builtin/tag.c:514
+msgid "only one -F or -m option is allowed."
+msgstr ""
+
+#: builtin/tag.c:534
+msgid "too many params"
+msgstr ""
+
+#: builtin/tag.c:540
+#, c-format
+msgid "'%s' is not a valid tag name."
+msgstr ""
+
+#: builtin/tag.c:545
+#, c-format
+msgid "tag '%s' already exists"
+msgstr ""
+
+#: builtin/tag.c:563
+#, c-format
+msgid "%s: cannot lock the ref"
+msgstr ""
+
+#: builtin/tag.c:565
+#, c-format
+msgid "%s: cannot update the ref"
+msgstr ""
+
+#: builtin/tag.c:567
+#, c-format
+msgid "Updated tag '%s' (was %s)\n"
+msgstr ""
+
+#: git-am.sh:49
+msgid "You need to set your committer info first"
+msgstr ""
+
+#: git-am.sh:135
+msgid "Repository lacks necessary blobs to fall back on 3-way merge."
+msgstr ""
+
+#: git-am.sh:144
+msgid ""
+"Did you hand edit your patch?\n"
+"It does not apply to blobs recorded in its index."
+msgstr ""
+
+#: git-am.sh:153
+msgid "Falling back to patching base and 3-way merge..."
+msgstr ""
+
+#: git-am.sh:265
+msgid "Only one StGIT patch series can be applied at once"
+msgstr ""
+
+#: git-am.sh:352
+#, sh-format
+msgid "Patch format $patch_format is not supported."
+msgstr ""
+
+#: git-am.sh:354
+msgid "Patch format detection failed."
+msgstr ""
+
+#: git-am.sh:406
+msgid "-d option is no longer supported.  Do not use."
+msgstr ""
+
+#: git-am.sh:469
+#, sh-format
+msgid "previous rebase directory $dotest still exists but mbox given."
+msgstr ""
+
+#: git-am.sh:474
+msgid "Please make up your mind. --skip or --abort?"
+msgstr ""
+
+#: git-am.sh:501
+msgid "Resolve operation not in progress, we are not resuming."
+msgstr ""
+
+#: git-am.sh:567
+#, sh-format
+msgid "Dirty index: cannot apply patches (dirty: $files)"
+msgstr ""
+
+#: git-am.sh:743
+msgid "cannot be interactive without stdin connected to a terminal."
+msgstr ""
+
+#. TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
+#. in your translation. The program will only accept English
+#. input at this point.
+#: git-am.sh:754
+msgid "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
+msgstr ""
+
+#: git-am.sh:790
+#, sh-format
+msgid "Applying: $FIRSTLINE"
+msgstr ""
+
+#: git-am.sh:835
+msgid "No changes -- Patch already applied."
+msgstr ""
+
+#: git-am.sh:861
+msgid "applying to an empty history"
+msgstr ""
+
+#. TRANSLATORS: Make sure to include [Y] and [n] in your
+#. translation. The program will only accept English input
+#. at this point.
+#: git-bisect.sh:54
+msgid "Do you want me to do it for you [Y/n]? "
+msgstr ""
+
+#: git-bisect.sh:95
+#, sh-format
+msgid "unrecognised option: '$arg'"
+msgstr ""
+
+#: git-bisect.sh:99
+#, sh-format
+msgid "'$arg' does not appear to be a valid revision"
+msgstr ""
+
+#: git-bisect.sh:117
+msgid "Bad HEAD - I need a HEAD"
+msgstr ""
+
+#: git-bisect.sh:130
+#, sh-format
+msgid ""
+"Checking out '$start_head' failed. Try 'git bisect reset <validbranch>'."
+msgstr ""
+
+#: git-bisect.sh:140
+msgid "won't bisect on seeked tree"
+msgstr ""
+
+#: git-bisect.sh:144
+msgid "Bad HEAD - strange symbolic ref"
+msgstr ""
+
+#: git-bisect.sh:189
+#, sh-format
+msgid "Bad bisect_write argument: $state"
+msgstr ""
+
+#: git-bisect.sh:218
+#, sh-format
+msgid "Bad rev input: $arg"
+msgstr ""
+
+#: git-bisect.sh:232
+msgid "Please call 'bisect_state' with at least one argument."
+msgstr ""
+
+#: git-bisect.sh:244
+#, sh-format
+msgid "Bad rev input: $rev"
+msgstr ""
+
+#: git-bisect.sh:250
+msgid "'git bisect bad' can take only one argument."
+msgstr ""
+
+#. TRANSLATORS: Make sure to include [Y] and [n] in your
+#. translation. The program will only accept English input
+#. at this point.
+#: git-bisect.sh:279
+msgid "Are you sure [Y/n]? "
+msgstr ""
+
+#: git-bisect.sh:354
+#, sh-format
+msgid "'$invalid' is not a valid commit"
+msgstr ""
+
+#: git-bisect.sh:363
+#, sh-format
+msgid ""
+"Could not check out original HEAD '$branch'.\n"
+"Try 'git bisect reset <commit>'."
+msgstr ""
+
+#: git-bisect.sh:390
+msgid "No logfile given"
+msgstr ""
+
+#: git-bisect.sh:391
+#, sh-format
+msgid "cannot read $file for replaying"
+msgstr ""
+
+#: git-bisect.sh:408
+msgid "?? what are you talking about?"
+msgstr ""
+
+#: git-bisect.sh:474
+msgid "We are not bisecting."
+msgstr ""
+
+#: git-pull.sh:21
+msgid ""
+"Pull is not possible because you have unmerged files.\n"
+"Please, fix them up in the work tree, and then use 'git add/rm <file>'\n"
+"as appropriate to mark resolution, or use 'git commit -a'."
+msgstr ""
+
+#: git-pull.sh:25
+msgid "Pull is not possible because you have unmerged files."
+msgstr ""
+
+#: git-pull.sh:197
+msgid "updating an unborn branch with changes added to the index"
+msgstr ""
+
+#: git-pull.sh:253
+msgid "Cannot merge multiple branches into empty head"
+msgstr ""
+
+#: git-pull.sh:257
+msgid "Cannot rebase onto multiple branches"
+msgstr ""
+
+#: git-stash.sh:51
+msgid "git stash clear with parameters is unimplemented"
+msgstr ""
+
+#: git-stash.sh:74
+msgid "You do not have the initial commit yet"
+msgstr ""
+
+#: git-stash.sh:89
+msgid "Cannot save the current index state"
+msgstr ""
+
+#: git-stash.sh:123 git-stash.sh:136
+msgid "Cannot save the current worktree state"
+msgstr ""
+
+#: git-stash.sh:140
+msgid "No changes selected"
+msgstr ""
+
+#: git-stash.sh:143
+msgid "Cannot remove temporary index (can't happen)"
+msgstr ""
+
+#: git-stash.sh:156
+msgid "Cannot record working tree state"
+msgstr ""
+
+#: git-stash.sh:223
+msgid "No local changes to save"
+msgstr ""
+
+#: git-stash.sh:227
+msgid "Cannot initialize stash"
+msgstr ""
+
+#: git-stash.sh:235
+msgid "Cannot save the current status"
+msgstr ""
+
+#: git-stash.sh:253
+msgid "Cannot remove worktree changes"
+msgstr ""
+
+#: git-stash.sh:352
+msgid "No stash found."
+msgstr ""
+
+#: git-stash.sh:359
+#, sh-format
+msgid "Too many revisions specified: $REV"
+msgstr ""
+
+#: git-stash.sh:365
+#, sh-format
+msgid "$reference is not valid reference"
+msgstr ""
+
+#: git-stash.sh:393
+#, sh-format
+msgid "'$args' is not a stash-like commit"
+msgstr ""
+
+#: git-stash.sh:404
+#, sh-format
+msgid "'$args' is not a stash reference"
+msgstr ""
+
+#: git-stash.sh:412
+msgid "unable to refresh index"
+msgstr ""
+
+#: git-stash.sh:416
+msgid "Cannot apply a stash in the middle of a merge"
+msgstr ""
+
+#: git-stash.sh:424
+msgid "Conflicts in index. Try without --index."
+msgstr ""
+
+#: git-stash.sh:426
+msgid "Could not save index tree"
+msgstr ""
+
+#: git-stash.sh:460
+msgid "Cannot unstage modified files"
+msgstr ""
+
+#: git-stash.sh:491
+#, sh-format
+msgid "Dropped ${REV} ($s)"
+msgstr ""
+
+#: git-stash.sh:492
+#, sh-format
+msgid "${REV}: Could not drop stash entry"
+msgstr ""
+
+#: git-stash.sh:499
+msgid "No branch name specified"
+msgstr ""
+
+#: git-stash.sh:570
+msgid "(To restore them type \"git stash apply\")"
+msgstr ""
+
+#: git-submodule.sh:56
+#, sh-format
+msgid "cannot strip one component off url '$remoteurl'"
+msgstr ""
+
+#: git-submodule.sh:108
+#, sh-format
+msgid "No submodule mapping found in .gitmodules for path '$path'"
+msgstr ""
+
+#: git-submodule.sh:173
+#, sh-format
+msgid "Clone of '$url' into submodule path '$path' failed"
+msgstr ""
+
+#: git-submodule.sh:247
+#, sh-format
+msgid "repo URL: '$repo' must be absolute or begin with ./|../"
+msgstr ""
+
+#: git-submodule.sh:264
+#, sh-format
+msgid "'$path' already exists in the index"
+msgstr ""
+
+#: git-submodule.sh:281
+#, sh-format
+msgid "'$path' already exists and is not a valid git repo"
+msgstr ""
+
+#: git-submodule.sh:295
+#, sh-format
+msgid "Unable to checkout submodule '$path'"
+msgstr ""
+
+#: git-submodule.sh:300
+#, sh-format
+msgid "Failed to add submodule '$path'"
+msgstr ""
+
+#: git-submodule.sh:305
+#, sh-format
+msgid "Failed to register submodule '$path'"
+msgstr ""
+
+#: git-submodule.sh:347
+#, sh-format
+msgid "Entering '$prefix$path'"
+msgstr ""
+
+#: git-submodule.sh:359
+#, sh-format
+msgid "Stopping at '$path'; script returned non-zero status."
+msgstr ""
+
+#: git-submodule.sh:401
+#, sh-format
+msgid "No url found for submodule path '$path' in .gitmodules"
+msgstr ""
+
+#: git-submodule.sh:410
+#, sh-format
+msgid "Failed to register url for submodule path '$path'"
+msgstr ""
+
+#: git-submodule.sh:418
+#, sh-format
+msgid "Failed to register update mode for submodule path '$path'"
+msgstr ""
+
+#: git-submodule.sh:420
+#, sh-format
+msgid "Submodule '$name' ($url) registered for path '$path'"
+msgstr ""
+
+#: git-submodule.sh:519
+#, sh-format
+msgid ""
+"Submodule path '$path' not initialized\n"
+"Maybe you want to use 'update --init'?"
+msgstr ""
+
+#: git-submodule.sh:532
+#, sh-format
+msgid "Unable to find current revision in submodule path '$path'"
+msgstr ""
+
+#: git-submodule.sh:551
+#, sh-format
+msgid "Unable to fetch in submodule path '$path'"
+msgstr ""
+
+#: git-submodule.sh:565
+#, sh-format
+msgid "Unable to rebase '$sha1' in submodule path '$path'"
+msgstr ""
+
+#: git-submodule.sh:566
+#, sh-format
+msgid "Submodule path '$path': rebased into '$sha1'"
+msgstr ""
+
+#: git-submodule.sh:571
+#, sh-format
+msgid "Unable to merge '$sha1' in submodule path '$path'"
+msgstr ""
+
+#: git-submodule.sh:572
+#, sh-format
+msgid "Submodule path '$path': merged in '$sha1'"
+msgstr ""
+
+#: git-submodule.sh:577
+#, sh-format
+msgid "Unable to checkout '$sha1' in submodule path '$path'"
+msgstr ""
+
+#: git-submodule.sh:578
+#, sh-format
+msgid "Submodule path '$path': checked out '$sha1'"
+msgstr ""
+
+#: git-submodule.sh:600 git-submodule.sh:923
+#, sh-format
+msgid "Failed to recurse into submodule path '$path'"
+msgstr ""
+
+#: git-submodule.sh:708
+msgid "--"
+msgstr ""
+
+#: git-submodule.sh:766
+#, sh-format
+msgid "  Warn: $name doesn't contain commit $sha1_src"
+msgstr ""
+
+#: git-submodule.sh:769
+#, sh-format
+msgid "  Warn: $name doesn't contain commit $sha1_dst"
+msgstr ""
+
+#: git-submodule.sh:772
+#, sh-format
+msgid "  Warn: $name doesn't contain commits $sha1_src and $sha1_dst"
+msgstr ""
+
+#: git-submodule.sh:797
+msgid "blob"
+msgstr ""
+
+#: git-submodule.sh:798
+msgid "submodule"
+msgstr ""
+
+#: git-submodule.sh:969
+#, sh-format
+msgid "Synchronizing submodule url for '$name'"
+msgstr ""
diff --git a/po/pt_PT.po b/po/pt_PT.po
new file mode 100644
index 0000000..517ec29
--- /dev/null
+++ b/po/pt_PT.po
@@ -0,0 +1,4980 @@
+# Portuguese translations for Git package.
+# Copyright (C) 2012 Marco Sousa <marcomsousa AT gmail.com>
+# This file is distributed under the same license as the Git package.
+# Contributers:
+#   - Marco Sousa <marcomsousa AT gmail.com>
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Git\n"
+"Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
+"POT-Creation-Date: 2012-05-08 16:06+0800\n"
+"PO-Revision-Date: 2012-05-14 21:17+0100\n"
+"Last-Translator: Marco Sousa <marcomsousa AT gmail.com>\n"
+"Language-Team: Portuguese\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: pt\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: advice.c:40
+#, c-format
+msgid "hint: %.*s\n"
+msgstr "dica: %.*s\n"
+
+#.
+#. * Message used both when 'git commit' fails and when
+#. * other commands doing a merge do.
+#.
+#: advice.c:70
+msgid ""
+"Fix them up in the work tree,\n"
+"and then use 'git add/rm <file>' as\n"
+"appropriate to mark resolution and make a commit,\n"
+"or use 'git commit -a'."
+msgstr ""
+
+#: bundle.c:36
+#, c-format
+msgid "'%s' does not look like a v2 bundle file"
+msgstr ""
+
+#: bundle.c:63
+#, c-format
+msgid "unrecognized header: %s%s (%d)"
+msgstr "cabeçalho não reconhecido: %s%s (%d)"
+
+#: bundle.c:89
+#: builtin/commit.c:753
+#, c-format
+msgid "could not open '%s'"
+msgstr "não é possivel abrir '%s'"
+
+#: bundle.c:140
+msgid "Repository lacks these prerequisite commits:"
+msgstr ""
+
+#: bundle.c:164
+#: sequencer.c:533
+#: sequencer.c:965
+#: builtin/log.c:289
+#: builtin/log.c:719
+#: builtin/log.c:1335
+#: builtin/log.c:1554
+#: builtin/merge.c:347
+#: builtin/shortlog.c:181
+msgid "revision walk setup failed"
+msgstr ""
+
+#: bundle.c:186
+#, c-format
+msgid "The bundle contains %d ref"
+msgid_plural "The bundle contains %d refs"
+msgstr[0] ""
+msgstr[1] ""
+
+#: bundle.c:192
+#, c-format
+msgid "The bundle requires this ref"
+msgid_plural "The bundle requires these %d refs"
+msgstr[0] ""
+msgstr[1] ""
+
+#: bundle.c:290
+msgid "rev-list died"
+msgstr "rev-list morreu"
+
+#: bundle.c:296
+#: builtin/log.c:1231
+#: builtin/shortlog.c:284
+#, c-format
+msgid "unrecognized argument: %s"
+msgstr "argumento não reconhecido: %s"
+
+#: bundle.c:331
+#, c-format
+msgid "ref '%s' is excluded by the rev-list options"
+msgstr ""
+
+#: bundle.c:376
+msgid "Refusing to create empty bundle."
+msgstr ""
+
+#: bundle.c:394
+msgid "Could not spawn pack-objects"
+msgstr "Não foi possível pawn pack-objects"
+
+#: bundle.c:412
+msgid "pack-objects died"
+msgstr ""
+
+#: bundle.c:415
+#, c-format
+msgid "cannot create '%s'"
+msgstr "não consegue crear '%s'"
+
+#: bundle.c:437
+msgid "index-pack died"
+msgstr ""
+
+#: commit.c:48
+#, c-format
+msgid "could not parse %s"
+msgstr "não consigo parsear %s"
+
+#: commit.c:50
+#, c-format
+msgid "%s %s is not a commit!"
+msgstr "%s %s não é um commit!"
+
+#: compat/obstack.c:406
+#: compat/obstack.c:408
+msgid "memory exhausted"
+msgstr "memória esgotada"
+
+#: connected.c:39
+msgid "Could not run 'git rev-list'"
+msgstr "Não foi possível executar 'git rev-list'"
+
+#: connected.c:48
+#, c-format
+msgid "failed write to rev-list: %s"
+msgstr ""
+
+#: connected.c:56
+#, c-format
+msgid "failed to close rev-list's stdin: %s"
+msgstr ""
+
+#: date.c:95
+msgid "in the future"
+msgstr ""
+
+#: date.c:101
+#, c-format
+msgid "%lu second ago"
+msgid_plural "%lu seconds ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: date.c:108
+#, c-format
+msgid "%lu minute ago"
+msgid_plural "%lu minutes ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: date.c:115
+#, c-format
+msgid "%lu hour ago"
+msgid_plural "%lu hours ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: date.c:122
+#, c-format
+msgid "%lu day ago"
+msgid_plural "%lu days ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: date.c:128
+#, c-format
+msgid "%lu week ago"
+msgid_plural "%lu weeks ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: date.c:135
+#, c-format
+msgid "%lu month ago"
+msgid_plural "%lu months ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: date.c:146
+#, c-format
+msgid "%lu year"
+msgid_plural "%lu years"
+msgstr[0] ""
+msgstr[1] ""
+
+#: date.c:149
+#, c-format
+msgid "%s, %lu month ago"
+msgid_plural "%s, %lu months ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: date.c:154
+#: date.c:159
+#, c-format
+msgid "%lu year ago"
+msgid_plural "%lu years ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: diff.c:105
+#, c-format
+msgid "  Failed to parse dirstat cut-off percentage '%.*s'\n"
+msgstr ""
+
+#: diff.c:110
+#, c-format
+msgid "  Unknown dirstat parameter '%.*s'\n"
+msgstr ""
+
+#: diff.c:210
+#, c-format
+msgid ""
+"Found errors in 'diff.dirstat' config variable:\n"
+"%s"
+msgstr ""
+
+#: diff.c:1400
+msgid " 0 files changed\n"
+msgstr " 0 ficheros modificados\n"
+
+#: diff.c:1404
+#, c-format
+msgid " %d file changed"
+msgid_plural " %d files changed"
+msgstr[0] " %d ficheiro modificado"
+msgstr[1] " %d ficheiros modificados"
+
+#: diff.c:1421
+#, c-format
+msgid ", %d insertion(+)"
+msgid_plural ", %d insertions(+)"
+msgstr[0] ", %d adição(+)"
+msgstr[1] ", %d adições(+)"
+
+#: diff.c:1432
+#, c-format
+msgid ", %d deletion(-)"
+msgid_plural ", %d deletions(-)"
+msgstr[0] ", %d eliminado(-)"
+msgstr[1] ", %d eliminados(-)"
+
+#: diff.c:3478
+#, c-format
+msgid ""
+"Failed to parse --dirstat/-X option parameter:\n"
+"%s"
+msgstr ""
+
+#: gpg-interface.c:59
+msgid "could not run gpg."
+msgstr "não consegue ejecutar gpg."
+
+#: gpg-interface.c:71
+msgid "gpg did not accept the data"
+msgstr ""
+
+#: gpg-interface.c:82
+msgid "gpg failed to sign the data"
+msgstr ""
+
+#: grep.c:1280
+#, c-format
+msgid "'%s': unable to read %s"
+msgstr ""
+
+#: grep.c:1297
+#, c-format
+msgid "'%s': %s"
+msgstr "'%s': %s"
+
+#: grep.c:1308
+#, c-format
+msgid "'%s': short read %s"
+msgstr ""
+
+#: help.c:207
+#, c-format
+msgid "available git commands in '%s'"
+msgstr ""
+
+#: help.c:214
+msgid "git commands available from elsewhere on your $PATH"
+msgstr ""
+
+#: help.c:270
+#, c-format
+msgid ""
+"'%s' appears to be a git command, but we were not\n"
+"able to execute it. Maybe git-%s is broken?"
+msgstr ""
+
+#: help.c:327
+msgid "Uh oh. Your system reports no Git commands at all."
+msgstr ""
+
+#: help.c:349
+#, c-format
+msgid ""
+"WARNING: You called a Git command named '%s', which does not exist.\n"
+"Continuing under the assumption that you meant '%s'"
+msgstr ""
+
+#: help.c:354
+#, c-format
+msgid "in %0.1f seconds automatically..."
+msgstr ""
+
+#: help.c:361
+#, c-format
+msgid "git: '%s' is not a git command. See 'git --help'."
+msgstr ""
+
+#: help.c:365
+msgid ""
+"\n"
+"Did you mean this?"
+msgid_plural ""
+"\n"
+"Did you mean one of these?"
+msgstr[0] ""
+msgstr[1] ""
+
+#: remote.c:1607
+#, c-format
+msgid "Your branch is ahead of '%s' by %d commit.\n"
+msgid_plural "Your branch is ahead of '%s' by %d commits.\n"
+msgstr[0] "A sua rama está à frente de '%s' pelo commit %d.\n"
+msgstr[1] "A sua rama está à frente de '%s' pelos commites %d.\n"
+
+#: remote.c:1613
+#, c-format
+msgid "Your branch is behind '%s' by %d commit, and can be fast-forwarded.\n"
+msgid_plural "Your branch is behind '%s' by %d commits, and can be fast-forwarded.\n"
+msgstr[0] ""
+msgstr[1] ""
+
+#: remote.c:1621
+#, c-format
+msgid ""
+"Your branch and '%s' have diverged,\n"
+"and have %d and %d different commit each, respectively.\n"
+msgid_plural ""
+"Your branch and '%s' have diverged,\n"
+"and have %d and %d different commits each, respectively.\n"
+msgstr[0] ""
+msgstr[1] ""
+
+#: sequencer.c:121
+#: builtin/merge.c:865
+#: builtin/merge.c:978
+#: builtin/merge.c:1088
+#: builtin/merge.c:1098
+#, c-format
+msgid "Could not open '%s' for writing"
+msgstr ""
+
+#: sequencer.c:123
+#: builtin/merge.c:333
+#: builtin/merge.c:868
+#: builtin/merge.c:1090
+#: builtin/merge.c:1103
+#, c-format
+msgid "Could not write to '%s'"
+msgstr "Não foi possível escrever para '%s'"
+
+#: sequencer.c:144
+msgid ""
+"after resolving the conflicts, mark the corrected paths\n"
+"with 'git add <paths>' or 'git rm <paths>'"
+msgstr ""
+
+#: sequencer.c:147
+msgid ""
+"after resolving the conflicts, mark the corrected paths\n"
+"with 'git add <paths>' or 'git rm <paths>'\n"
+"and commit the result with 'git commit'"
+msgstr ""
+
+#: sequencer.c:160
+#: sequencer.c:741
+#: sequencer.c:824
+#, c-format
+msgid "Could not write to %s"
+msgstr "Não foi possível gravar para %s"
+
+#: sequencer.c:163
+#, c-format
+msgid "Error wrapping up %s"
+msgstr ""
+
+#: sequencer.c:178
+msgid "Your local changes would be overwritten by cherry-pick."
+msgstr ""
+
+#: sequencer.c:180
+msgid "Your local changes would be overwritten by revert."
+msgstr ""
+
+#: sequencer.c:183
+msgid "Commit your changes or stash them to proceed."
+msgstr ""
+
+#. TRANSLATORS: %s will be "revert" or "cherry-pick"
+#: sequencer.c:233
+#, c-format
+msgid "%s: Unable to write new index file"
+msgstr ""
+
+#: sequencer.c:261
+msgid "Could not resolve HEAD commit\n"
+msgstr ""
+
+#: sequencer.c:282
+msgid "Unable to update cache tree\n"
+msgstr ""
+
+#: sequencer.c:323
+#, c-format
+msgid "Could not parse commit %s\n"
+msgstr "Não foi possível analisar commit %s\n"
+
+#: sequencer.c:328
+#, c-format
+msgid "Could not parse parent commit %s\n"
+msgstr "Não foi possível analisar commit parent %s\n"
+
+#: sequencer.c:358
+msgid "Your index file is unmerged."
+msgstr "O seu ficheiro de índice é não fundido."
+
+#: sequencer.c:361
+msgid "You do not have a valid HEAD"
+msgstr "Você não tem uma HEAD válida"
+
+#: sequencer.c:376
+#, c-format
+msgid "Commit %s is a merge but no -m option was given."
+msgstr ""
+
+#: sequencer.c:384
+#, c-format
+msgid "Commit %s does not have parent %d"
+msgstr ""
+
+#: sequencer.c:388
+#, c-format
+msgid "Mainline was specified but commit %s is not a merge."
+msgstr ""
+
+#. TRANSLATORS: The first %s will be "revert" or
+#. "cherry-pick", the second %s a SHA1
+#: sequencer.c:399
+#, c-format
+msgid "%s: cannot parse parent commit %s"
+msgstr ""
+
+#: sequencer.c:403
+#, c-format
+msgid "Cannot get commit message for %s"
+msgstr "Não é possível obter mensagem commit para %s"
+
+#: sequencer.c:491
+#, c-format
+msgid "could not revert %s... %s"
+msgstr ""
+
+#: sequencer.c:492
+#, c-format
+msgid "could not apply %s... %s"
+msgstr ""
+
+#: sequencer.c:536
+msgid "empty commit set passed"
+msgstr "passado commit com o set vazio"
+
+#: sequencer.c:544
+#, c-format
+msgid "git %s: failed to read the index"
+msgstr ""
+
+#: sequencer.c:549
+#, c-format
+msgid "git %s: failed to refresh the index"
+msgstr ""
+
+#: sequencer.c:607
+#, c-format
+msgid "Cannot %s during a %s"
+msgstr "Não foi possível abrir %s durante um %s"
+
+#: sequencer.c:629
+#, c-format
+msgid "Could not parse line %d."
+msgstr "Não foi possível parsear linha %d."
+
+#: sequencer.c:634
+msgid "No commits parsed."
+msgstr "Nenhum commit parseado."
+
+#: sequencer.c:647
+#, c-format
+msgid "Could not open %s"
+msgstr "Não foi possível abrir %s"
+
+#: sequencer.c:651
+#, c-format
+msgid "Could not read %s."
+msgstr "Não foi possível ler %s."
+
+#: sequencer.c:658
+#, c-format
+msgid "Unusable instruction sheet: %s"
+msgstr ""
+
+#: sequencer.c:686
+#, c-format
+msgid "Invalid key: %s"
+msgstr ""
+
+#: sequencer.c:689
+#, c-format
+msgid "Invalid value for %s: %s"
+msgstr "Valor inválido para %s: %s"
+
+#: sequencer.c:701
+#, c-format
+msgid "Malformed options sheet: %s"
+msgstr ""
+
+#: sequencer.c:722
+msgid "a cherry-pick or revert is already in progress"
+msgstr ""
+
+#: sequencer.c:723
+msgid "try \"git cherry-pick (--continue | --quit | --abort)\""
+msgstr ""
+
+#: sequencer.c:727
+#, c-format
+msgid "Could not create sequencer directory %s"
+msgstr ""
+
+#: sequencer.c:743
+#: sequencer.c:828
+#, c-format
+msgid "Error wrapping up %s."
+msgstr ""
+
+#: sequencer.c:762
+#: sequencer.c:896
+msgid "no cherry-pick or revert in progress"
+msgstr ""
+
+#: sequencer.c:764
+msgid "cannot resolve HEAD"
+msgstr ""
+
+#: sequencer.c:766
+msgid "cannot abort from a branch yet to be born"
+msgstr ""
+
+#: sequencer.c:788
+#: builtin/apply.c:3682
+#, c-format
+msgid "cannot open %s: %s"
+msgstr "não foi possível abrir %s: %s"
+
+#: sequencer.c:791
+#, c-format
+msgid "cannot read %s: %s"
+msgstr "não foi possível ler %s: %s"
+
+#: sequencer.c:792
+msgid "unexpected end of file"
+msgstr ""
+
+#: sequencer.c:798
+#, c-format
+msgid "stored pre-cherry-pick HEAD file '%s' is corrupt"
+msgstr ""
+
+#: sequencer.c:821
+#, c-format
+msgid "Could not format %s."
+msgstr "Não foi possível formatear %s."
+
+#: sequencer.c:983
+msgid "Can't revert as initial commit"
+msgstr ""
+
+#: sequencer.c:984
+msgid "Can't cherry-pick into empty head"
+msgstr ""
+
+#: sha1_name.c:864
+msgid "HEAD does not point to a branch"
+msgstr ""
+
+#: sha1_name.c:867
+#, c-format
+msgid "No such branch: '%s'"
+msgstr "Não existe rama '%s'"
+
+#: sha1_name.c:869
+#, c-format
+msgid "No upstream configured for branch '%s'"
+msgstr ""
+
+#: sha1_name.c:872
+#, c-format
+msgid "Upstream branch '%s' not stored as a remote-tracking branch"
+msgstr ""
+
+#: wt-status.c:135
+msgid "Unmerged paths:"
+msgstr "caminhos não fundidos:"
+
+#: wt-status.c:141
+#: wt-status.c:158
+#, c-format
+msgid "  (use \"git reset %s <file>...\" to unstage)"
+msgstr ""
+
+#: wt-status.c:143
+#: wt-status.c:160
+msgid "  (use \"git rm --cached <file>...\" to unstage)"
+msgstr ""
+
+#: wt-status.c:144
+msgid "  (use \"git add/rm <file>...\" as appropriate to mark resolution)"
+msgstr "  (usa \"git add/rm <ficheiro>...\" para marcar como resolvido)"
+
+#: wt-status.c:152
+msgid "Changes to be committed:"
+msgstr "Mudanças a serem commitadas"
+
+#: wt-status.c:170
+msgid "Changes not staged for commit:"
+msgstr ""
+
+#: wt-status.c:174
+msgid "  (use \"git add <file>...\" to update what will be committed)"
+msgstr "  (usa \"git add <ficheiro>...\" para actualizar o que vai ser commitado)"
+
+#: wt-status.c:176
+msgid "  (use \"git add/rm <file>...\" to update what will be committed)"
+msgstr "  (usa \"git add/rm <ficheiro>...\" para actualizar o que vai ser commitado)"
+
+#: wt-status.c:177
+msgid "  (use \"git checkout -- <file>...\" to discard changes in working directory)"
+msgstr ""
+
+#: wt-status.c:179
+msgid "  (commit or discard the untracked or modified content in submodules)"
+msgstr ""
+
+#: wt-status.c:188
+#, c-format
+msgid "%s files:"
+msgstr "%s ficheros:"
+
+#: wt-status.c:191
+#, c-format
+msgid "  (use \"git %s <file>...\" to include in what will be committed)"
+msgstr ""
+
+#: wt-status.c:208
+msgid "bug"
+msgstr "erro"
+
+#: wt-status.c:213
+msgid "both deleted:"
+msgstr "eliminados em ambos:"
+
+#: wt-status.c:214
+msgid "added by us:"
+msgstr "adicionado por nós:"
+
+#: wt-status.c:215
+msgid "deleted by them:"
+msgstr "eliminados por eles:"
+
+#: wt-status.c:216
+msgid "added by them:"
+msgstr "adicionados por eles:"
+
+#: wt-status.c:217
+msgid "deleted by us:"
+msgstr "eliminados por nós:"
+
+#: wt-status.c:218
+msgid "both added:"
+msgstr "adicionados em ambos:"
+
+#: wt-status.c:219
+msgid "both modified:"
+msgstr "modificados em ambos:"
+
+#: wt-status.c:249
+msgid "new commits, "
+msgstr "novos commits, "
+
+#: wt-status.c:251
+msgid "modified content, "
+msgstr "conteúdo modificado, "
+
+#: wt-status.c:253
+msgid "untracked content, "
+msgstr "conteúdo não seguido"
+
+#: wt-status.c:267
+#, c-format
+msgid "new file:   %s"
+msgstr "novo ficheiro:   %s"
+
+#: wt-status.c:270
+#, c-format
+msgid "copied:     %s -> %s"
+msgstr "copiado:     %s -> %s"
+
+#: wt-status.c:273
+#, c-format
+msgid "deleted:    %s"
+msgstr "eliminado:    %s"
+
+#: wt-status.c:276
+#, c-format
+msgid "modified:   %s"
+msgstr "modificado:   %s"
+
+#: wt-status.c:279
+#, c-format
+msgid "renamed:    %s -> %s"
+msgstr "mudado de nome:    %s -> %s"
+
+#: wt-status.c:282
+#, c-format
+msgid "typechange: %s"
+msgstr ""
+
+#: wt-status.c:285
+#, c-format
+msgid "unknown:    %s"
+msgstr "desconhecido:    %s"
+
+#: wt-status.c:288
+#, c-format
+msgid "unmerged:   %s"
+msgstr "não fundidos:   %s"
+
+#: wt-status.c:291
+#, c-format
+msgid "bug: unhandled diff status %c"
+msgstr ""
+
+#: wt-status.c:737
+msgid "On branch "
+msgstr "Na rama"
+
+#: wt-status.c:744
+msgid "Not currently on any branch."
+msgstr "Não está em nenhuma rama."
+
+#: wt-status.c:755
+msgid "Initial commit"
+msgstr "Commit inicial"
+
+#: wt-status.c:769
+msgid "Untracked"
+msgstr "Não seguido"
+
+#: wt-status.c:771
+msgid "Ignored"
+msgstr "Ignorado"
+
+#: wt-status.c:773
+#, c-format
+msgid "Untracked files not listed%s"
+msgstr ""
+
+#: wt-status.c:775
+msgid " (use -u option to show untracked files)"
+msgstr ""
+
+#: wt-status.c:781
+msgid "No changes"
+msgstr "Sem mudanças"
+
+#: wt-status.c:785
+#, c-format
+msgid "no changes added to commit%s\n"
+msgstr "nenhuma alteração adicionado ao commit%s\n"
+
+#: wt-status.c:787
+msgid " (use \"git add\" and/or \"git commit -a\")"
+msgstr " (usa \"git add\" e/ou \"git commit -a\")"
+
+#: wt-status.c:789
+#, c-format
+msgid "nothing added to commit but untracked files present%s\n"
+msgstr ""
+
+#: wt-status.c:791
+msgid " (use \"git add\" to track)"
+msgstr " (usa \"git add\" para seguir)"
+
+#: wt-status.c:793
+#: wt-status.c:796
+#: wt-status.c:799
+#, c-format
+msgid "nothing to commit%s\n"
+msgstr "nada para fazer commit%s\n"
+
+#: wt-status.c:794
+msgid " (create/copy files and use \"git add\" to track)"
+msgstr ""
+
+#: wt-status.c:797
+msgid " (use -u to show untracked files)"
+msgstr ""
+
+#: wt-status.c:800
+msgid " (working directory clean)"
+msgstr " (directório de trabalho vacio)"
+
+#: wt-status.c:908
+msgid "HEAD (no branch)"
+msgstr "HEAD (Não é rama)"
+
+#: wt-status.c:914
+msgid "Initial commit on "
+msgstr "Commit inicial em "
+
+#: wt-status.c:929
+msgid "behind "
+msgstr "atrás "
+
+#: wt-status.c:932
+#: wt-status.c:935
+msgid "ahead "
+msgstr "a frente "
+
+#: wt-status.c:937
+msgid ", behind "
+msgstr ", atrás "
+
+#: builtin/add.c:62
+#, c-format
+msgid "unexpected diff status %c"
+msgstr ""
+
+#: builtin/add.c:67
+#: builtin/commit.c:282
+msgid "updating files failed"
+msgstr "Falou a atualização dos ficheiros"
+
+#: builtin/add.c:77
+#, c-format
+msgid "remove '%s'\n"
+msgstr "eliminar '%s'\n"
+
+#: builtin/add.c:176
+#, c-format
+msgid "Path '%s' is in submodule '%.*s'"
+msgstr ""
+
+#: builtin/add.c:192
+msgid "Unstaged changes after refreshing the index:"
+msgstr ""
+
+#: builtin/add.c:195
+#: builtin/add.c:456
+#: builtin/rm.c:186
+#, c-format
+msgid "pathspec '%s' did not match any files"
+msgstr ""
+
+#: builtin/add.c:209
+#, c-format
+msgid "'%s' is beyond a symbolic link"
+msgstr ""
+
+#: builtin/add.c:276
+msgid "Could not read the index"
+msgstr "Não foi possível ler o indíce"
+
+#: builtin/add.c:286
+#, c-format
+msgid "Could not open '%s' for writing."
+msgstr "Não foi possível abrir '%s' para escrever."
+
+#: builtin/add.c:290
+msgid "Could not write patch"
+msgstr "Não consegue escrever patch"
+
+#: builtin/add.c:295
+#, c-format
+msgid "Could not stat '%s'"
+msgstr ""
+
+#: builtin/add.c:297
+msgid "Empty patch. Aborted."
+msgstr "Patch vazio. Aborted."
+
+#: builtin/add.c:303
+#, c-format
+msgid "Could not apply '%s'"
+msgstr "Não foi possível aplicar o '%s'"
+
+#: builtin/add.c:312
+msgid "The following paths are ignored by one of your .gitignore files:\n"
+msgstr ""
+
+#: builtin/add.c:352
+#, c-format
+msgid "Use -f if you really want to add them.\n"
+msgstr ""
+
+#: builtin/add.c:353
+msgid "no files added"
+msgstr "nenhum ficheiros adicionado"
+
+#: builtin/add.c:359
+msgid "adding files failed"
+msgstr "falhou a adicionar ficheiros"
+
+#: builtin/add.c:391
+msgid "-A and -u are mutually incompatible"
+msgstr ""
+
+#: builtin/add.c:393
+msgid "Option --ignore-missing can only be used together with --dry-run"
+msgstr ""
+
+#: builtin/add.c:413
+#, c-format
+msgid "Nothing specified, nothing added.\n"
+msgstr ""
+
+#: builtin/add.c:414
+#, c-format
+msgid "Maybe you wanted to say 'git add .'?\n"
+msgstr ""
+
+#: builtin/add.c:420
+#: builtin/clean.c:95
+#: builtin/commit.c:342
+#: builtin/mv.c:82
+#: builtin/rm.c:162
+msgid "index file corrupt"
+msgstr "ficheiro index corrupto"
+
+#: builtin/add.c:476
+#: builtin/apply.c:4093
+#: builtin/mv.c:229
+#: builtin/rm.c:260
+msgid "Unable to write new index file"
+msgstr ""
+
+#: builtin/apply.c:106
+#, c-format
+msgid "unrecognized whitespace option '%s'"
+msgstr "espaço em braco não reconhecido: '%s'"
+
+#: builtin/apply.c:121
+#, c-format
+msgid "unrecognized whitespace ignore option '%s'"
+msgstr ""
+
+#: builtin/apply.c:815
+#, c-format
+msgid "Cannot prepare timestamp regexp %s"
+msgstr ""
+
+#: builtin/apply.c:824
+#, c-format
+msgid "regexec returned %d for input: %s"
+msgstr ""
+
+#: builtin/apply.c:905
+#, c-format
+msgid "unable to find filename in patch at line %d"
+msgstr ""
+
+#: builtin/apply.c:934
+#, c-format
+msgid "git apply: bad git-diff - expected /dev/null, got %s on line %d"
+msgstr ""
+
+#: builtin/apply.c:937
+#, c-format
+msgid "git apply: bad git-diff - inconsistent %s filename on line %d"
+msgstr ""
+
+#: builtin/apply.c:944
+#, c-format
+msgid "git apply: bad git-diff - expected /dev/null on line %d"
+msgstr ""
+
+#: builtin/apply.c:1387
+#, c-format
+msgid "recount: unexpected line: %.*s"
+msgstr ""
+
+#: builtin/apply.c:1444
+#, c-format
+msgid "patch fragment without header at line %d: %.*s"
+msgstr ""
+
+#: builtin/apply.c:1461
+#, c-format
+msgid "git diff header lacks filename information when removing %d leading pathname component (line %d)"
+msgid_plural "git diff header lacks filename information when removing %d leading pathname components (line %d)"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/apply.c:1621
+msgid "new file depends on old contents"
+msgstr ""
+
+#: builtin/apply.c:1623
+msgid "deleted file still has contents"
+msgstr ""
+
+#: builtin/apply.c:1649
+#, c-format
+msgid "corrupt patch at line %d"
+msgstr ""
+
+#: builtin/apply.c:1685
+#, c-format
+msgid "new file %s depends on old contents"
+msgstr ""
+
+#: builtin/apply.c:1687
+#, c-format
+msgid "deleted file %s still has contents"
+msgstr ""
+
+#: builtin/apply.c:1690
+#, c-format
+msgid "** warning: file %s becomes empty but is not deleted"
+msgstr ""
+
+#: builtin/apply.c:1836
+#, c-format
+msgid "corrupt binary patch at line %d: %.*s"
+msgstr ""
+
+#. there has to be one hunk (forward hunk)
+#: builtin/apply.c:1865
+#, c-format
+msgid "unrecognized binary patch at line %d"
+msgstr ""
+
+#: builtin/apply.c:1951
+#, c-format
+msgid "patch with only garbage at line %d"
+msgstr ""
+
+#: builtin/apply.c:2041
+#, c-format
+msgid "unable to read symlink %s"
+msgstr ""
+
+#: builtin/apply.c:2045
+#, c-format
+msgid "unable to open or read %s"
+msgstr "Não foi possível abrir o ler %s"
+
+#: builtin/apply.c:2116
+msgid "oops"
+msgstr ""
+
+#: builtin/apply.c:2638
+#, c-format
+msgid "invalid start of line: '%c'"
+msgstr "começo de linha inválido: '%c'"
+
+#: builtin/apply.c:2756
+#, c-format
+msgid "Hunk #%d succeeded at %d (offset %d line)."
+msgid_plural "Hunk #%d succeeded at %d (offset %d lines)."
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/apply.c:2768
+#, c-format
+msgid "Context reduced to (%ld/%ld) to apply fragment at %d"
+msgstr ""
+
+#: builtin/apply.c:2774
+#, c-format
+msgid ""
+"while searching for:\n"
+"%.*s"
+msgstr ""
+
+#: builtin/apply.c:2793
+#, c-format
+msgid "missing binary patch data for '%s'"
+msgstr ""
+
+#: builtin/apply.c:2896
+#, c-format
+msgid "binary patch does not apply to '%s'"
+msgstr ""
+
+#: builtin/apply.c:2902
+#, c-format
+msgid "binary patch to '%s' creates incorrect result (expecting %s, got %s)"
+msgstr ""
+
+#: builtin/apply.c:2923
+#, c-format
+msgid "patch failed: %s:%ld"
+msgstr ""
+
+#: builtin/apply.c:3038
+#, c-format
+msgid "patch %s has been renamed/deleted"
+msgstr ""
+
+#: builtin/apply.c:3045
+#: builtin/apply.c:3062
+#, c-format
+msgid "read of %s failed"
+msgstr "ler %s falhou"
+
+#: builtin/apply.c:3077
+msgid "removal patch leaves file contents"
+msgstr ""
+
+#: builtin/apply.c:3098
+#, c-format
+msgid "%s: already exists in working directory"
+msgstr "%s: já existe no espaço de trabalho"
+
+#: builtin/apply.c:3136
+#, c-format
+msgid "%s: has been deleted/renamed"
+msgstr ""
+
+#: builtin/apply.c:3141
+#: builtin/apply.c:3172
+#, c-format
+msgid "%s: %s"
+msgstr "%s: %s"
+
+#: builtin/apply.c:3152
+#, c-format
+msgid "%s: does not exist in index"
+msgstr ""
+
+#: builtin/apply.c:3166
+#, c-format
+msgid "%s: does not match index"
+msgstr "%s: não tem correspondencia ao index"
+
+#: builtin/apply.c:3183
+#, c-format
+msgid "%s: wrong type"
+msgstr ""
+
+#: builtin/apply.c:3185
+#, c-format
+msgid "%s has type %o, expected %o"
+msgstr ""
+
+#: builtin/apply.c:3240
+#, c-format
+msgid "%s: already exists in index"
+msgstr "%s: já existe no indíce"
+
+#: builtin/apply.c:3259
+#, c-format
+msgid "new mode (%o) of %s does not match old mode (%o)%s%s"
+msgstr ""
+
+#: builtin/apply.c:3265
+#, c-format
+msgid "%s: patch does not apply"
+msgstr ""
+
+#: builtin/apply.c:3278
+#, c-format
+msgid "Checking patch %s..."
+msgstr ""
+
+#: builtin/apply.c:3333
+#: builtin/checkout.c:212
+#: builtin/reset.c:158
+#, c-format
+msgid "make_cache_entry failed for path '%s'"
+msgstr ""
+
+#: builtin/apply.c:3476
+#, c-format
+msgid "unable to remove %s from index"
+msgstr ""
+
+#: builtin/apply.c:3503
+#, c-format
+msgid "corrupt patch for subproject %s"
+msgstr ""
+
+#: builtin/apply.c:3507
+#, c-format
+msgid "unable to stat newly created file '%s'"
+msgstr "não é possivel 'stat' o novo ficheiro creado '%s'"
+
+#: builtin/apply.c:3512
+#, c-format
+msgid "unable to create backing store for newly created file %s"
+msgstr ""
+
+#: builtin/apply.c:3515
+#, c-format
+msgid "unable to add cache entry for %s"
+msgstr ""
+
+#: builtin/apply.c:3548
+#, c-format
+msgid "closing file '%s'"
+msgstr "fechar fichero '%s'"
+
+#: builtin/apply.c:3597
+#, c-format
+msgid "unable to write file '%s' mode %o"
+msgstr ""
+
+#: builtin/apply.c:3653
+#, c-format
+msgid "Applied patch %s cleanly."
+msgstr ""
+
+#: builtin/apply.c:3661
+msgid "internal error"
+msgstr ""
+
+#. Say this even without --verbose
+#: builtin/apply.c:3664
+#, c-format
+msgid "Applying patch %%s with %d reject..."
+msgid_plural "Applying patch %%s with %d rejects..."
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/apply.c:3674
+#, c-format
+msgid "truncating .rej filename to %.*s.rej"
+msgstr ""
+
+#: builtin/apply.c:3695
+#, c-format
+msgid "Hunk #%d applied cleanly."
+msgstr ""
+
+#: builtin/apply.c:3698
+#, c-format
+msgid "Rejected hunk #%d."
+msgstr ""
+
+#: builtin/apply.c:3829
+msgid "unrecognized input"
+msgstr "entrada não reconhecida"
+
+#: builtin/apply.c:3840
+msgid "unable to read index file"
+msgstr "Não foi possível ler o fichero indíce"
+
+#: builtin/apply.c:4035
+msgid "--index outside a repository"
+msgstr "--index fora de um repositorio"
+
+#: builtin/apply.c:4038
+msgid "--cached outside a repository"
+msgstr "--cached fora de um repositorio"
+
+#: builtin/apply.c:4054
+#, c-format
+msgid "can't open patch '%s'"
+msgstr "não é possivel abrir patch '%s'"
+
+#: builtin/apply.c:4068
+#, c-format
+msgid "squelched %d whitespace error"
+msgid_plural "squelched %d whitespace errors"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/apply.c:4074
+#: builtin/apply.c:4084
+#, c-format
+msgid "%d line adds whitespace errors."
+msgid_plural "%d lines add whitespace errors."
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/archive.c:17
+#, c-format
+msgid "could not create archive file '%s'"
+msgstr ""
+
+#: builtin/archive.c:20
+msgid "could not redirect output"
+msgstr ""
+
+#: builtin/archive.c:37
+msgid "git archive: Remote with no URL"
+msgstr ""
+
+#: builtin/archive.c:58
+msgid "git archive: expected ACK/NAK, got EOF"
+msgstr ""
+
+#: builtin/archive.c:63
+#, c-format
+msgid "git archive: NACK %s"
+msgstr ""
+
+#: builtin/archive.c:65
+#, c-format
+msgid "remote error: %s"
+msgstr "erro remoto: %s"
+
+#: builtin/archive.c:66
+msgid "git archive: protocol error"
+msgstr ""
+
+#: builtin/archive.c:71
+msgid "git archive: expected a flush"
+msgstr ""
+
+#: builtin/branch.c:144
+#, c-format
+msgid ""
+"deleting branch '%s' that has been merged to\n"
+"         '%s', but not yet merged to HEAD."
+msgstr ""
+
+#: builtin/branch.c:148
+#, c-format
+msgid ""
+"not deleting branch '%s' that is not yet merged to\n"
+"         '%s', even though it is merged to HEAD."
+msgstr ""
+
+#: builtin/branch.c:180
+msgid "cannot use -a with -d"
+msgstr "Não é possível usar -a com um -d"
+
+#: builtin/branch.c:186
+msgid "Couldn't look up commit object for HEAD"
+msgstr ""
+
+#: builtin/branch.c:191
+#, c-format
+msgid "Cannot delete the branch '%s' which you are currently on."
+msgstr ""
+
+#: builtin/branch.c:202
+#, c-format
+msgid "remote branch '%s' not found."
+msgstr "rama remota '%s não encontrada."
+
+#: builtin/branch.c:203
+#, c-format
+msgid "branch '%s' not found."
+msgstr "rama '%s' não encontrado."
+
+#: builtin/branch.c:210
+#, c-format
+msgid "Couldn't look up commit object for '%s'"
+msgstr ""
+
+#: builtin/branch.c:216
+#, c-format
+msgid ""
+"The branch '%s' is not fully merged.\n"
+"If you are sure you want to delete it, run 'git branch -D %s'."
+msgstr ""
+
+#: builtin/branch.c:225
+#, c-format
+msgid "Error deleting remote branch '%s'"
+msgstr ""
+
+#: builtin/branch.c:226
+#, c-format
+msgid "Error deleting branch '%s'"
+msgstr "Erro a eliminar rama '%s'"
+
+#: builtin/branch.c:233
+#, c-format
+msgid "Deleted remote branch %s (was %s).\n"
+msgstr ""
+
+#: builtin/branch.c:234
+#, c-format
+msgid "Deleted branch %s (was %s).\n"
+msgstr "Eliminar rama %s (era %s).\n"
+
+#: builtin/branch.c:239
+msgid "Update of config-file failed"
+msgstr ""
+
+#: builtin/branch.c:337
+#, c-format
+msgid "branch '%s' does not point at a commit"
+msgstr ""
+
+#: builtin/branch.c:409
+#, c-format
+msgid "behind %d] "
+msgstr "atrás %d] "
+
+#: builtin/branch.c:411
+#, c-format
+msgid "ahead %d] "
+msgstr "a frente %d] "
+
+#: builtin/branch.c:413
+#, c-format
+msgid "ahead %d, behind %d] "
+msgstr "a frente %d, atrás %d] "
+
+#: builtin/branch.c:521
+msgid "(no branch)"
+msgstr "(não é rama)"
+
+#: builtin/branch.c:586
+msgid "some refs could not be read"
+msgstr ""
+
+#: builtin/branch.c:599
+msgid "cannot rename the current branch while not on any."
+msgstr ""
+
+#: builtin/branch.c:609
+#, c-format
+msgid "Invalid branch name: '%s'"
+msgstr "Nome da rama inválida: '%s'"
+
+#: builtin/branch.c:624
+msgid "Branch rename failed"
+msgstr "Falhou renomeação da rama"
+
+#: builtin/branch.c:628
+#, c-format
+msgid "Renamed a misnamed branch '%s' away"
+msgstr "Renomeado uma rama erronea '%s'"
+
+#: builtin/branch.c:632
+#, c-format
+msgid "Branch renamed to %s, but HEAD is not updated!"
+msgstr ""
+
+#: builtin/branch.c:639
+msgid "Branch is renamed, but update of config-file failed"
+msgstr ""
+
+#: builtin/branch.c:654
+#, c-format
+msgid "malformed object name %s"
+msgstr ""
+
+#: builtin/branch.c:678
+#, c-format
+msgid "could not write branch description template: %s"
+msgstr ""
+
+#: builtin/branch.c:769
+msgid "Failed to resolve HEAD as a valid ref."
+msgstr ""
+
+#: builtin/branch.c:774
+#: builtin/clone.c:558
+msgid "HEAD not found below refs/heads!"
+msgstr ""
+
+#: builtin/branch.c:794
+msgid "--column and --verbose are incompatible"
+msgstr "--column e --verbose são incompatíveis"
+
+#: builtin/branch.c:843
+msgid "-a and -r options to 'git branch' do not make sense with a branch name"
+msgstr ""
+
+#: builtin/bundle.c:47
+#, c-format
+msgid "%s is okay\n"
+msgstr "%s está bem\n"
+
+#: builtin/bundle.c:56
+msgid "Need a repository to create a bundle."
+msgstr ""
+
+#: builtin/bundle.c:60
+msgid "Need a repository to unbundle."
+msgstr ""
+
+#: builtin/checkout.c:113
+#: builtin/checkout.c:146
+#, c-format
+msgid "path '%s' does not have our version"
+msgstr ""
+
+#: builtin/checkout.c:115
+#: builtin/checkout.c:148
+#, c-format
+msgid "path '%s' does not have their version"
+msgstr ""
+
+#: builtin/checkout.c:131
+#, c-format
+msgid "path '%s' does not have all necessary versions"
+msgstr ""
+
+#: builtin/checkout.c:175
+#, c-format
+msgid "path '%s' does not have necessary versions"
+msgstr ""
+
+#: builtin/checkout.c:192
+#, c-format
+msgid "path '%s': cannot merge"
+msgstr ""
+
+#: builtin/checkout.c:209
+#, c-format
+msgid "Unable to add merge result for '%s'"
+msgstr ""
+
+#: builtin/checkout.c:234
+#: builtin/checkout.c:392
+msgid "corrupt index file"
+msgstr "ficheiro index corrupto"
+
+#: builtin/checkout.c:264
+#: builtin/checkout.c:271
+#, c-format
+msgid "path '%s' is unmerged"
+msgstr ""
+
+#: builtin/checkout.c:302
+#: builtin/checkout.c:498
+#: builtin/clone.c:583
+#: builtin/merge.c:812
+msgid "unable to write new index file"
+msgstr ""
+
+#: builtin/checkout.c:319
+#: builtin/diff.c:302
+#: builtin/merge.c:408
+msgid "diff_setup_done failed"
+msgstr ""
+
+#: builtin/checkout.c:414
+msgid "you need to resolve your current index first"
+msgstr ""
+
+#: builtin/checkout.c:533
+#, c-format
+msgid "Can not do reflog for '%s'\n"
+msgstr ""
+
+#: builtin/checkout.c:566
+msgid "HEAD is now at"
+msgstr "HEAD é agora em "
+
+#: builtin/checkout.c:573
+#, c-format
+msgid "Reset branch '%s'\n"
+msgstr "Reset rama '%s'\n"
+
+#: builtin/checkout.c:576
+#, c-format
+msgid "Already on '%s'\n"
+msgstr "Já em '%s'\n"
+
+#: builtin/checkout.c:580
+#, c-format
+msgid "Switched to and reset branch '%s'\n"
+msgstr ""
+
+#: builtin/checkout.c:582
+#, c-format
+msgid "Switched to a new branch '%s'\n"
+msgstr "Mudado para a nova rama '%s'\n"
+
+#: builtin/checkout.c:584
+#, c-format
+msgid "Switched to branch '%s'\n"
+msgstr "Mudado para a rama '%s'\n"
+
+#: builtin/checkout.c:640
+#, c-format
+msgid " ... and %d more.\n"
+msgstr " ... e %d mais.\n"
+
+#. The singular version
+#: builtin/checkout.c:646
+#, c-format
+msgid ""
+"Warning: you are leaving %d commit behind, not connected to\n"
+"any of your branches:\n"
+"\n"
+"%s\n"
+msgid_plural ""
+"Warning: you are leaving %d commits behind, not connected to\n"
+"any of your branches:\n"
+"\n"
+"%s\n"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/checkout.c:664
+#, c-format
+msgid ""
+"If you want to keep them by creating a new branch, this may be a good time\n"
+"to do so with:\n"
+"\n"
+" git branch new_branch_name %s\n"
+"\n"
+msgstr ""
+
+#: builtin/checkout.c:693
+msgid "internal error in revision walk"
+msgstr ""
+
+#: builtin/checkout.c:697
+msgid "Previous HEAD position was"
+msgstr ""
+
+#: builtin/checkout.c:723
+msgid "You are on a branch yet to be born"
+msgstr ""
+
+#. case (1)
+#: builtin/checkout.c:854
+#, c-format
+msgid "invalid reference: %s"
+msgstr ""
+
+#. case (1): want a tree
+#: builtin/checkout.c:893
+#, c-format
+msgid "reference is not a tree: %s"
+msgstr ""
+
+#: builtin/checkout.c:973
+msgid "-B cannot be used with -b"
+msgstr ""
+
+#: builtin/checkout.c:982
+msgid "--patch is incompatible with all other options"
+msgstr ""
+
+#: builtin/checkout.c:985
+msgid "--detach cannot be used with -b/-B/--orphan"
+msgstr ""
+
+#: builtin/checkout.c:987
+msgid "--detach cannot be used with -t"
+msgstr ""
+
+#: builtin/checkout.c:993
+msgid "--track needs a branch name"
+msgstr ""
+
+#: builtin/checkout.c:1000
+msgid "Missing branch name; try -b"
+msgstr ""
+
+#: builtin/checkout.c:1006
+msgid "--orphan and -b|-B are mutually exclusive"
+msgstr ""
+
+#: builtin/checkout.c:1008
+msgid "--orphan cannot be used with -t"
+msgstr ""
+
+#: builtin/checkout.c:1018
+msgid "git checkout: -f and -m are incompatible"
+msgstr ""
+
+#: builtin/checkout.c:1052
+msgid "invalid path specification"
+msgstr ""
+
+#: builtin/checkout.c:1060
+#, c-format
+msgid ""
+"git checkout: updating paths is incompatible with switching branches.\n"
+"Did you intend to checkout '%s' which can not be resolved as commit?"
+msgstr ""
+
+#: builtin/checkout.c:1062
+msgid "git checkout: updating paths is incompatible with switching branches."
+msgstr ""
+
+#: builtin/checkout.c:1067
+msgid "git checkout: --detach does not take a path argument"
+msgstr ""
+
+#: builtin/checkout.c:1070
+msgid ""
+"git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
+"checking out of the index."
+msgstr ""
+
+#: builtin/checkout.c:1089
+msgid "Cannot switch branch to a non-commit."
+msgstr ""
+
+#: builtin/checkout.c:1092
+msgid "--ours/--theirs is incompatible with switching branches."
+msgstr ""
+
+#: builtin/clean.c:78
+msgid "-x and -X cannot be used together"
+msgstr ""
+
+#: builtin/clean.c:82
+msgid "clean.requireForce set to true and neither -n nor -f given; refusing to clean"
+msgstr ""
+
+#: builtin/clean.c:85
+msgid "clean.requireForce defaults to true and neither -n nor -f given; refusing to clean"
+msgstr ""
+
+#: builtin/clean.c:155
+#: builtin/clean.c:176
+#, c-format
+msgid "Would remove %s\n"
+msgstr ""
+
+#: builtin/clean.c:159
+#: builtin/clean.c:179
+#, c-format
+msgid "Removing %s\n"
+msgstr "Eliminando %s\n"
+
+#: builtin/clean.c:162
+#: builtin/clean.c:182
+#, c-format
+msgid "failed to remove %s"
+msgstr ""
+
+#: builtin/clean.c:166
+#, c-format
+msgid "Would not remove %s\n"
+msgstr ""
+
+#: builtin/clean.c:168
+#, c-format
+msgid "Not removing %s\n"
+msgstr ""
+
+#: builtin/clone.c:243
+#, c-format
+msgid "reference repository '%s' is not a local directory."
+msgstr ""
+
+#: builtin/clone.c:302
+#, c-format
+msgid "failed to open '%s'"
+msgstr "falhou a abrir '%s'"
+
+#: builtin/clone.c:306
+#, c-format
+msgid "failed to create directory '%s'"
+msgstr "falhou a criar o directório '%s'"
+
+#: builtin/clone.c:308
+#: builtin/diff.c:75
+#, c-format
+msgid "failed to stat '%s'"
+msgstr ""
+
+#: builtin/clone.c:310
+#, c-format
+msgid "%s exists and is not a directory"
+msgstr ""
+
+#: builtin/clone.c:324
+#, c-format
+msgid "failed to stat %s\n"
+msgstr ""
+
+#: builtin/clone.c:341
+#, c-format
+msgid "failed to unlink '%s'"
+msgstr ""
+
+#: builtin/clone.c:346
+#, c-format
+msgid "failed to create link '%s'"
+msgstr ""
+
+#: builtin/clone.c:350
+#, c-format
+msgid "failed to copy file to '%s'"
+msgstr "falhou a copiar o ficheiro para '%s'"
+
+#: builtin/clone.c:373
+#, c-format
+msgid "done.\n"
+msgstr "terminado.\n"
+
+#: builtin/clone.c:440
+#, c-format
+msgid "Could not find remote branch %s to clone."
+msgstr ""
+
+#: builtin/clone.c:549
+msgid "remote HEAD refers to nonexistent ref, unable to checkout.\n"
+msgstr ""
+
+#: builtin/clone.c:639
+msgid "Too many arguments."
+msgstr "Demasiados parametros."
+
+#: builtin/clone.c:643
+msgid "You must specify a repository to clone."
+msgstr ""
+
+#: builtin/clone.c:654
+#, c-format
+msgid "--bare and --origin %s options are incompatible."
+msgstr ""
+
+#: builtin/clone.c:668
+#, c-format
+msgid "repository '%s' does not exist"
+msgstr ""
+
+#: builtin/clone.c:673
+msgid "--depth is ignored in local clones; use file:// instead."
+msgstr ""
+
+#: builtin/clone.c:683
+#, c-format
+msgid "destination path '%s' already exists and is not an empty directory."
+msgstr ""
+
+#: builtin/clone.c:693
+#, c-format
+msgid "working tree '%s' already exists."
+msgstr ""
+
+#: builtin/clone.c:706
+#: builtin/clone.c:720
+#, c-format
+msgid "could not create leading directories of '%s'"
+msgstr ""
+
+#: builtin/clone.c:709
+#, c-format
+msgid "could not create work tree dir '%s'."
+msgstr ""
+
+#: builtin/clone.c:728
+#, c-format
+msgid "Cloning into bare repository '%s'...\n"
+msgstr "Clonando em um repositorio nu (bare) '%s'...\n"
+
+#: builtin/clone.c:730
+#, c-format
+msgid "Cloning into '%s'...\n"
+msgstr "Clonar em '%s'...\n"
+
+#: builtin/clone.c:786
+#, c-format
+msgid "Don't know how to clone %s"
+msgstr "Não sei como clonar %s"
+
+#: builtin/clone.c:835
+#, c-format
+msgid "Remote branch %s not found in upstream %s"
+msgstr ""
+
+#: builtin/clone.c:842
+msgid "You appear to have cloned an empty repository."
+msgstr ""
+
+#: builtin/column.c:51
+msgid "--command must be the first argument"
+msgstr ""
+
+#: builtin/commit.c:43
+msgid ""
+"Your name and email address were configured automatically based\n"
+"on your username and hostname. Please check that they are accurate.\n"
+"You can suppress this message by setting them explicitly:\n"
+"\n"
+"    git config --global user.name \"Your Name\"\n"
+"    git config --global user.email you@example.com\n"
+"\n"
+"After doing this, you may fix the identity used for this commit with:\n"
+"\n"
+"    git commit --amend --reset-author\n"
+msgstr ""
+"O seu nome e endereço de e-mail foram configurados automaticamente com base\n"
+"no o seu usuario e nome da maquina. Por favor, verifique se eles são precisos.\n"
+"Você pode suprimir esta mensagem, configurando-los explicitamente:\n"
+"\n"
+"    git config --global user.name \"O teu Nome\"\n"
+"    git config --global user.email tu@examplo.com\n"
+"\n"
+"Após fazer isso, você pode corregir a identidade usada em este commit com:\n"
+"\n"
+"    git commit --amend --reset-author\n"
+
+#: builtin/commit.c:55
+msgid ""
+"You asked to amend the most recent commit, but doing so would make\n"
+"it empty. You can repeat your command with --allow-empty, or you can\n"
+"remove the commit entirely with \"git reset HEAD^\".\n"
+msgstr ""
+
+#: builtin/commit.c:60
+msgid ""
+"The previous cherry-pick is now empty, possibly due to conflict resolution.\n"
+"If you wish to commit it anyway, use:\n"
+"\n"
+"    git commit --allow-empty\n"
+"\n"
+"Otherwise, please use 'git reset'\n"
+msgstr ""
+
+#: builtin/commit.c:309
+msgid "failed to unpack HEAD tree object"
+msgstr ""
+
+#: builtin/commit.c:351
+msgid "unable to create temporary index"
+msgstr ""
+
+#: builtin/commit.c:357
+msgid "interactive add failed"
+msgstr "falhou adicionar interativo"
+
+#: builtin/commit.c:390
+#: builtin/commit.c:411
+#: builtin/commit.c:461
+msgid "unable to write new_index file"
+msgstr ""
+
+#: builtin/commit.c:442
+msgid "cannot do a partial commit during a merge."
+msgstr ""
+
+#: builtin/commit.c:444
+msgid "cannot do a partial commit during a cherry-pick."
+msgstr ""
+
+#: builtin/commit.c:454
+msgid "cannot read the index"
+msgstr "não foi possível ler o indíce"
+
+#: builtin/commit.c:474
+msgid "unable to write temporary index file"
+msgstr ""
+
+#: builtin/commit.c:549
+#: builtin/commit.c:555
+#, c-format
+msgid "invalid commit: %s"
+msgstr "commit inválido: %s"
+
+#: builtin/commit.c:578
+msgid "malformed --author parameter"
+msgstr ""
+
+#: builtin/commit.c:639
+#, c-format
+msgid "Malformed ident string: '%s'"
+msgstr ""
+
+#: builtin/commit.c:677
+#: builtin/commit.c:710
+#: builtin/commit.c:1024
+#, c-format
+msgid "could not lookup commit %s"
+msgstr ""
+
+#: builtin/commit.c:689
+#: builtin/shortlog.c:296
+#, c-format
+msgid "(reading log message from standard input)\n"
+msgstr ""
+
+#: builtin/commit.c:691
+msgid "could not read log from standard input"
+msgstr ""
+
+#: builtin/commit.c:695
+#, c-format
+msgid "could not read log file '%s'"
+msgstr "não é possivel ler o ficheiro de log '%s'"
+
+#: builtin/commit.c:701
+msgid "commit has empty message"
+msgstr "a mensagem do commit está vazia"
+
+#: builtin/commit.c:717
+msgid "could not read MERGE_MSG"
+msgstr "não é possivel ler MERGE_MSG"
+
+#: builtin/commit.c:721
+msgid "could not read SQUASH_MSG"
+msgstr "não é possivel ler SQUASH_MSG"
+
+#: builtin/commit.c:725
+#, c-format
+msgid "could not read '%s'"
+msgstr "não é possivel ler '%s'"
+
+#: builtin/commit.c:777
+msgid "could not write commit template"
+msgstr ""
+
+#: builtin/commit.c:788
+#, c-format
+msgid ""
+"\n"
+"It looks like you may be committing a merge.\n"
+"If this is not correct, please remove the file\n"
+"\t%s\n"
+"and try again.\n"
+msgstr ""
+
+#: builtin/commit.c:793
+#, c-format
+msgid ""
+"\n"
+"It looks like you may be committing a cherry-pick.\n"
+"If this is not correct, please remove the file\n"
+"\t%s\n"
+"and try again.\n"
+msgstr ""
+
+#: builtin/commit.c:805
+msgid ""
+"Please enter the commit message for your changes. Lines starting\n"
+"with '#' will be ignored, and an empty message aborts the commit.\n"
+msgstr ""
+
+#: builtin/commit.c:810
+msgid ""
+"Please enter the commit message for your changes. Lines starting\n"
+"with '#' will be kept; you may remove them yourself if you want to.\n"
+"An empty message aborts the commit.\n"
+msgstr ""
+
+#: builtin/commit.c:823
+#, c-format
+msgid "%sAuthor:    %s"
+msgstr "%sAutor:    %s"
+
+#: builtin/commit.c:830
+#, c-format
+msgid "%sCommitter: %s"
+msgstr "%sCommitador: %s"
+
+#: builtin/commit.c:850
+msgid "Cannot read index"
+msgstr ""
+
+#: builtin/commit.c:887
+msgid "Error building trees"
+msgstr ""
+
+#: builtin/commit.c:902
+#: builtin/tag.c:361
+#, c-format
+msgid "Please supply the message using either -m or -F option.\n"
+msgstr ""
+
+#: builtin/commit.c:999
+#, c-format
+msgid "No existing author found with '%s'"
+msgstr ""
+
+#: builtin/commit.c:1014
+#: builtin/commit.c:1214
+#, c-format
+msgid "Invalid untracked files mode '%s'"
+msgstr ""
+
+#: builtin/commit.c:1054
+msgid "Using both --reset-author and --author does not make sense"
+msgstr ""
+
+#: builtin/commit.c:1065
+msgid "You have nothing to amend."
+msgstr "Você não tem nada a corregir."
+
+#: builtin/commit.c:1068
+msgid "You are in the middle of a merge -- cannot amend."
+msgstr ""
+
+#: builtin/commit.c:1070
+msgid "You are in the middle of a cherry-pick -- cannot amend."
+msgstr ""
+
+#: builtin/commit.c:1073
+msgid "Options --squash and --fixup cannot be used together"
+msgstr ""
+
+#: builtin/commit.c:1083
+msgid "Only one of -c/-C/-F/--fixup can be used."
+msgstr ""
+
+#: builtin/commit.c:1085
+msgid "Option -m cannot be combined with -c/-C/-F/--fixup."
+msgstr ""
+
+#: builtin/commit.c:1093
+msgid "--reset-author can be used only with -C, -c or --amend."
+msgstr ""
+
+#: builtin/commit.c:1110
+msgid "Only one of --include/--only/--all/--interactive/--patch can be used."
+msgstr ""
+
+#: builtin/commit.c:1112
+msgid "No paths with --include/--only does not make sense."
+msgstr ""
+
+#: builtin/commit.c:1114
+msgid "Clever... amending the last one with dirty index."
+msgstr ""
+
+#: builtin/commit.c:1116
+msgid "Explicit paths specified without -i nor -o; assuming --only paths..."
+msgstr ""
+
+#: builtin/commit.c:1126
+#: builtin/tag.c:577
+#, c-format
+msgid "Invalid cleanup mode %s"
+msgstr ""
+
+#: builtin/commit.c:1131
+msgid "Paths with -a does not make sense."
+msgstr ""
+
+#: builtin/commit.c:1315
+msgid "couldn't look up newly created commit"
+msgstr ""
+
+#: builtin/commit.c:1317
+msgid "could not parse newly created commit"
+msgstr ""
+
+#: builtin/commit.c:1358
+msgid "detached HEAD"
+msgstr ""
+
+#: builtin/commit.c:1360
+msgid " (root-commit)"
+msgstr " (root-commit)"
+
+#: builtin/commit.c:1450
+msgid "could not parse HEAD commit"
+msgstr ""
+
+#: builtin/commit.c:1487
+#: builtin/merge.c:509
+#, c-format
+msgid "could not open '%s' for reading"
+msgstr ""
+
+#: builtin/commit.c:1494
+#, c-format
+msgid "Corrupt MERGE_HEAD file (%s)"
+msgstr ""
+
+#: builtin/commit.c:1501
+msgid "could not read MERGE_MODE"
+msgstr ""
+
+#: builtin/commit.c:1520
+#, c-format
+msgid "could not read commit message: %s"
+msgstr ""
+
+#: builtin/commit.c:1534
+#, c-format
+msgid "Aborting commit; you did not edit the message.\n"
+msgstr ""
+
+#: builtin/commit.c:1539
+#, c-format
+msgid "Aborting commit due to empty commit message.\n"
+msgstr ""
+
+#: builtin/commit.c:1554
+#: builtin/merge.c:936
+#: builtin/merge.c:961
+msgid "failed to write commit object"
+msgstr ""
+
+#: builtin/commit.c:1575
+msgid "cannot lock HEAD ref"
+msgstr ""
+
+#: builtin/commit.c:1579
+msgid "cannot update HEAD ref"
+msgstr ""
+
+#: builtin/commit.c:1590
+msgid ""
+"Repository has been updated, but unable to write\n"
+"new_index file. Check that disk is not full or quota is\n"
+"not exceeded, and then \"git reset HEAD\" to recover."
+msgstr ""
+
+#: builtin/describe.c:234
+#, c-format
+msgid "annotated tag %s not available"
+msgstr ""
+
+#: builtin/describe.c:238
+#, c-format
+msgid "annotated tag %s has no embedded name"
+msgstr ""
+
+#: builtin/describe.c:240
+#, c-format
+msgid "tag '%s' is really '%s' here"
+msgstr ""
+
+#: builtin/describe.c:267
+#, c-format
+msgid "Not a valid object name %s"
+msgstr ""
+
+#: builtin/describe.c:270
+#, c-format
+msgid "%s is not a valid '%s' object"
+msgstr ""
+
+#: builtin/describe.c:287
+#, c-format
+msgid "no tag exactly matches '%s'"
+msgstr ""
+
+#: builtin/describe.c:289
+#, c-format
+msgid "searching to describe %s\n"
+msgstr ""
+
+#: builtin/describe.c:329
+#, c-format
+msgid "finished search at %s\n"
+msgstr ""
+
+#: builtin/describe.c:353
+#, c-format
+msgid ""
+"No annotated tags can describe '%s'.\n"
+"However, there were unannotated tags: try --tags."
+msgstr ""
+
+#: builtin/describe.c:357
+#, c-format
+msgid ""
+"No tags can describe '%s'.\n"
+"Try --always, or create some tags."
+msgstr ""
+
+#: builtin/describe.c:378
+#, c-format
+msgid "traversed %lu commits\n"
+msgstr ""
+
+#: builtin/describe.c:381
+#, c-format
+msgid ""
+"more than %i tags found; listed %i most recent\n"
+"gave up search at %s\n"
+msgstr ""
+
+#: builtin/describe.c:436
+msgid "--long is incompatible with --abbrev=0"
+msgstr ""
+
+#: builtin/describe.c:462
+msgid "No names found, cannot describe anything."
+msgstr "Nenhum nome encontrado, não descreve nada."
+
+#: builtin/describe.c:482
+msgid "--dirty is incompatible with committishes"
+msgstr ""
+
+#: builtin/diff.c:77
+#, c-format
+msgid "'%s': not a regular file or symlink"
+msgstr ""
+
+#: builtin/diff.c:220
+#, c-format
+msgid "invalid option: %s"
+msgstr ""
+
+#: builtin/diff.c:297
+msgid "Not a git repository"
+msgstr "Não é um repositorio git"
+
+#: builtin/diff.c:347
+#, c-format
+msgid "invalid object '%s' given."
+msgstr ""
+
+#: builtin/diff.c:352
+#, c-format
+msgid "more than %d trees given: '%s'"
+msgstr ""
+
+#: builtin/diff.c:362
+#, c-format
+msgid "more than two blobs given: '%s'"
+msgstr ""
+
+#: builtin/diff.c:370
+#, c-format
+msgid "unhandled object '%s' given."
+msgstr ""
+
+#: builtin/fetch.c:200
+msgid "Couldn't find remote ref HEAD"
+msgstr ""
+
+#: builtin/fetch.c:253
+#, c-format
+msgid "object %s not found"
+msgstr "objecto %s não encontrado"
+
+#: builtin/fetch.c:259
+msgid "[up to date]"
+msgstr "[Actualizada]"
+
+#: builtin/fetch.c:273
+#, c-format
+msgid "! %-*s %-*s -> %s  (can't fetch in current branch)"
+msgstr ""
+
+#: builtin/fetch.c:274
+#: builtin/fetch.c:360
+msgid "[rejected]"
+msgstr "[rejeitado]"
+
+#: builtin/fetch.c:285
+msgid "[tag update]"
+msgstr "[etiqueta actualizada]"
+
+#: builtin/fetch.c:287
+#: builtin/fetch.c:322
+#: builtin/fetch.c:340
+msgid "  (unable to update local ref)"
+msgstr ""
+
+#: builtin/fetch.c:305
+msgid "[new tag]"
+msgstr "[nova etiqueta]"
+
+#: builtin/fetch.c:308
+msgid "[new branch]"
+msgstr "[nova rama]"
+
+#: builtin/fetch.c:311
+msgid "[new ref]"
+msgstr "[nova ref]"
+
+#: builtin/fetch.c:356
+msgid "unable to update local ref"
+msgstr ""
+
+#: builtin/fetch.c:356
+msgid "forced update"
+msgstr "actualização forçada"
+
+#: builtin/fetch.c:362
+msgid "(non-fast-forward)"
+msgstr "(non-fast-forward)"
+
+#: builtin/fetch.c:393
+#: builtin/fetch.c:685
+#, c-format
+msgid "cannot open %s: %s\n"
+msgstr "não é possivel abrir %s: %s\n"
+
+#: builtin/fetch.c:402
+#, c-format
+msgid "%s did not send all necessary objects\n"
+msgstr ""
+
+#: builtin/fetch.c:488
+#, c-format
+msgid "From %.*s\n"
+msgstr "Para %.*s\n"
+
+#: builtin/fetch.c:499
+#, c-format
+msgid ""
+"some local refs could not be updated; try running\n"
+" 'git remote prune %s' to remove any old, conflicting branches"
+msgstr ""
+
+#: builtin/fetch.c:549
+#, c-format
+msgid "   (%s will become dangling)"
+msgstr ""
+
+#: builtin/fetch.c:550
+#, c-format
+msgid "   (%s has become dangling)"
+msgstr ""
+
+#: builtin/fetch.c:557
+msgid "[deleted]"
+msgstr "[eliminado]"
+
+#: builtin/fetch.c:558
+#: builtin/remote.c:1055
+msgid "(none)"
+msgstr "(nenhum)"
+
+#: builtin/fetch.c:675
+#, c-format
+msgid "Refusing to fetch into current branch %s of non-bare repository"
+msgstr ""
+
+#: builtin/fetch.c:709
+#, c-format
+msgid "Don't know how to fetch from %s"
+msgstr ""
+
+#: builtin/fetch.c:786
+#, c-format
+msgid "Option \"%s\" value \"%s\" is not valid for %s"
+msgstr ""
+
+#: builtin/fetch.c:789
+#, c-format
+msgid "Option \"%s\" is ignored for %s\n"
+msgstr ""
+
+#: builtin/fetch.c:888
+#, c-format
+msgid "Fetching %s\n"
+msgstr "Baixando %s\n"
+
+#: builtin/fetch.c:890
+#: builtin/remote.c:100
+#, c-format
+msgid "Could not fetch %s"
+msgstr ""
+
+#: builtin/fetch.c:907
+msgid ""
+"No remote repository specified.  Please, specify either a URL or a\n"
+"remote name from which new revisions should be fetched."
+msgstr ""
+"Nenhum repositório remoto especificado. Por favor, especifique um URL ou o\n"
+"nome remoto a partir do qual novas revisões devem ser obtida."
+
+#: builtin/fetch.c:927
+msgid "You need to specify a tag name."
+msgstr "Você precisa especificar um nome da etiqueta."
+
+#: builtin/fetch.c:979
+msgid "fetch --all does not take a repository argument"
+msgstr ""
+
+#: builtin/fetch.c:981
+msgid "fetch --all does not make sense with refspecs"
+msgstr ""
+
+#: builtin/fetch.c:992
+#, c-format
+msgid "No such remote or remote group: %s"
+msgstr ""
+
+#: builtin/fetch.c:1000
+msgid "Fetching a group and specifying refspecs does not make sense"
+msgstr ""
+
+#: builtin/gc.c:63
+#, c-format
+msgid "Invalid %s: '%s'"
+msgstr "Inválido %s: '%s'"
+
+#: builtin/gc.c:90
+#, c-format
+msgid "insanely long object directory %.*s"
+msgstr ""
+
+#: builtin/gc.c:221
+#, c-format
+msgid "Auto packing the repository for optimum performance.\n"
+msgstr ""
+
+#: builtin/gc.c:224
+#, c-format
+msgid ""
+"Auto packing the repository for optimum performance. You may also\n"
+"run \"git gc\" manually. See \"git help gc\" for more information.\n"
+msgstr ""
+
+#: builtin/gc.c:251
+msgid "There are too many unreachable loose objects; run 'git prune' to remove them."
+msgstr ""
+
+#: builtin/grep.c:216
+#, c-format
+msgid "grep: failed to create thread: %s"
+msgstr ""
+
+#: builtin/grep.c:402
+#, c-format
+msgid "Failed to chdir: %s"
+msgstr ""
+
+#: builtin/grep.c:478
+#: builtin/grep.c:512
+#, c-format
+msgid "unable to read tree (%s)"
+msgstr ""
+
+#: builtin/grep.c:526
+#, c-format
+msgid "unable to grep from object of type %s"
+msgstr ""
+
+#: builtin/grep.c:584
+#, c-format
+msgid "switch `%c' expects a numerical value"
+msgstr ""
+
+#: builtin/grep.c:601
+#, c-format
+msgid "cannot open '%s'"
+msgstr ""
+
+#: builtin/grep.c:888
+msgid "no pattern given."
+msgstr ""
+
+#: builtin/grep.c:902
+#, c-format
+msgid "bad object %s"
+msgstr ""
+
+#: builtin/grep.c:943
+msgid "--open-files-in-pager only works on the worktree"
+msgstr ""
+
+#: builtin/grep.c:966
+msgid "--cached or --untracked cannot be used with --no-index."
+msgstr ""
+
+#: builtin/grep.c:971
+msgid "--no-index or --untracked cannot be used with revs."
+msgstr ""
+
+#: builtin/grep.c:974
+msgid "--[no-]exclude-standard cannot be used for tracked contents."
+msgstr ""
+
+#: builtin/grep.c:982
+msgid "both --cached and trees are given."
+msgstr ""
+
+#: builtin/help.c:59
+#, c-format
+msgid "unrecognized help format '%s'"
+msgstr "formato ajuda não reconhecido '%s'"
+
+#: builtin/help.c:87
+msgid "Failed to start emacsclient."
+msgstr ""
+
+#: builtin/help.c:100
+msgid "Failed to parse emacsclient version."
+msgstr ""
+
+#: builtin/help.c:108
+#, c-format
+msgid "emacsclient version '%d' too old (< 22)."
+msgstr ""
+
+#: builtin/help.c:126
+#: builtin/help.c:154
+#: builtin/help.c:163
+#: builtin/help.c:171
+#, c-format
+msgid "failed to exec '%s': %s"
+msgstr ""
+
+#: builtin/help.c:211
+#, c-format
+msgid ""
+"'%s': path for unsupported man viewer.\n"
+"Please consider using 'man.<tool>.cmd' instead."
+msgstr ""
+
+#: builtin/help.c:223
+#, c-format
+msgid ""
+"'%s': cmd for supported man viewer.\n"
+"Please consider using 'man.<tool>.path' instead."
+msgstr ""
+
+#: builtin/help.c:287
+msgid "The most commonly used git commands are:"
+msgstr ""
+
+#: builtin/help.c:355
+#, c-format
+msgid "'%s': unknown man viewer."
+msgstr ""
+
+#: builtin/help.c:372
+msgid "no man viewer handled the request"
+msgstr ""
+
+#: builtin/help.c:380
+msgid "no info viewer handled the request"
+msgstr ""
+
+#: builtin/help.c:391
+#, c-format
+msgid "'%s': not a documentation directory."
+msgstr ""
+
+#: builtin/help.c:432
+#: builtin/help.c:439
+#, c-format
+msgid "usage: %s%s"
+msgstr ""
+
+#: builtin/help.c:453
+#, c-format
+msgid "`git %s' is aliased to `%s'"
+msgstr ""
+
+#: builtin/index-pack.c:84
+#, c-format
+msgid "object type mismatch at %s"
+msgstr ""
+
+#: builtin/index-pack.c:104
+msgid "object of unexpected type"
+msgstr ""
+
+#: builtin/index-pack.c:141
+#, c-format
+msgid "cannot fill %d byte"
+msgid_plural "cannot fill %d bytes"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/index-pack.c:151
+msgid "early EOF"
+msgstr ""
+
+#: builtin/index-pack.c:152
+msgid "read error on input"
+msgstr ""
+
+#: builtin/index-pack.c:164
+msgid "used more bytes than were available"
+msgstr ""
+
+#: builtin/index-pack.c:171
+msgid "pack too large for current definition of off_t"
+msgstr ""
+
+#: builtin/index-pack.c:187
+#, c-format
+msgid "unable to create '%s'"
+msgstr "não é possivel crear '%s'"
+
+#: builtin/index-pack.c:192
+#, c-format
+msgid "cannot open packfile '%s'"
+msgstr "Não é possivel abrir o ficheiro packfile '%s'"
+
+#: builtin/index-pack.c:206
+msgid "pack signature mismatch"
+msgstr ""
+
+#: builtin/index-pack.c:226
+#, c-format
+msgid "pack has bad object at offset %lu: %s"
+msgstr ""
+
+#: builtin/index-pack.c:300
+#, c-format
+msgid "inflate returned %d"
+msgstr ""
+
+#: builtin/index-pack.c:345
+msgid "offset value overflow for delta base object"
+msgstr ""
+
+#: builtin/index-pack.c:353
+msgid "delta base offset is out of bound"
+msgstr ""
+
+#: builtin/index-pack.c:361
+#, c-format
+msgid "unknown object type %d"
+msgstr "ojecto com tipo desconhecido %d"
+
+#: builtin/index-pack.c:390
+msgid "cannot pread pack file"
+msgstr "Não é possivel pread pack file"
+
+#: builtin/index-pack.c:392
+#, c-format
+msgid "premature end of pack file, %lu byte missing"
+msgid_plural "premature end of pack file, %lu bytes missing"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/index-pack.c:405
+msgid "serious inflate inconsistency"
+msgstr ""
+
+#: builtin/index-pack.c:476
+#, c-format
+msgid "cannot read existing object %s"
+msgstr "não foi possível ler objecto existente %s"
+
+#: builtin/index-pack.c:479
+#, c-format
+msgid "SHA1 COLLISION FOUND WITH %s !"
+msgstr ""
+
+#: builtin/index-pack.c:488
+#, c-format
+msgid "invalid blob object %s"
+msgstr "inválido objecto blob %s"
+
+#: builtin/index-pack.c:500
+#, c-format
+msgid "invalid %s"
+msgstr "inválido: %s"
+
+#: builtin/index-pack.c:502
+msgid "Error in object"
+msgstr ""
+
+#: builtin/index-pack.c:504
+#, c-format
+msgid "Not all child objects of %s are reachable"
+msgstr ""
+
+#: builtin/index-pack.c:576
+#: builtin/index-pack.c:602
+msgid "failed to apply delta"
+msgstr ""
+
+#: builtin/index-pack.c:706
+msgid "Receiving objects"
+msgstr ""
+
+#: builtin/index-pack.c:706
+msgid "Indexing objects"
+msgstr ""
+
+#: builtin/index-pack.c:728
+msgid "pack is corrupted (SHA1 mismatch)"
+msgstr ""
+
+#: builtin/index-pack.c:733
+msgid "cannot fstat packfile"
+msgstr "Não é possivel fstat packfile"
+
+#: builtin/index-pack.c:736
+msgid "pack has junk at the end"
+msgstr ""
+
+#: builtin/index-pack.c:754
+msgid "Resolving deltas"
+msgstr "Resolvendo deltas"
+
+#: builtin/index-pack.c:787
+#, c-format
+msgid "unable to deflate appended object (%d)"
+msgstr ""
+
+#: builtin/index-pack.c:866
+#, c-format
+msgid "local object %s is corrupt"
+msgstr ""
+
+#: builtin/index-pack.c:890
+msgid "error while closing pack file"
+msgstr ""
+
+#: builtin/index-pack.c:903
+#, c-format
+msgid "cannot write keep file '%s'"
+msgstr "não é possivel escrever o fichero kepp '%s'"
+
+#: builtin/index-pack.c:911
+#, c-format
+msgid "cannot close written keep file '%s'"
+msgstr "Não é possivel fechar o fichero escrito '%s'"
+
+#: builtin/index-pack.c:924
+msgid "cannot store pack file"
+msgstr "Não é possivel guardar o fichero pack"
+
+#: builtin/index-pack.c:935
+msgid "cannot store index file"
+msgstr "Não é possivel guardar fichero index"
+
+#: builtin/index-pack.c:1024
+#, c-format
+msgid "Cannot open existing pack file '%s'"
+msgstr "Não é possivel abrir o existente ficheiro pack %s"
+
+#: builtin/index-pack.c:1026
+#, c-format
+msgid "Cannot open existing pack idx file for '%s'"
+msgstr "Não é possivel abrir o ficheiro 'pack idx' para '%s'"
+
+#: builtin/index-pack.c:1073
+#, c-format
+msgid "non delta: %d object"
+msgid_plural "non delta: %d objects"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/index-pack.c:1080
+#, c-format
+msgid "chain length = %d: %lu object"
+msgid_plural "chain length = %d: %lu objects"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/index-pack.c:1107
+msgid "Cannot come back to cwd"
+msgstr ""
+
+#: builtin/index-pack.c:1140
+#: builtin/index-pack.c:1143
+#: builtin/index-pack.c:1155
+#: builtin/index-pack.c:1159
+#, c-format
+msgid "bad %s"
+msgstr "inválido %s"
+
+#: builtin/index-pack.c:1173
+msgid "--fix-thin cannot be used without --stdin"
+msgstr ""
+
+#: builtin/index-pack.c:1177
+#: builtin/index-pack.c:1187
+#, c-format
+msgid "packfile name '%s' does not end with '.pack'"
+msgstr ""
+
+#: builtin/index-pack.c:1196
+msgid "--verify with no packfile name given"
+msgstr ""
+
+#: builtin/index-pack.c:1220
+msgid "confusion beyond insanity"
+msgstr ""
+
+#: builtin/index-pack.c:1239
+#, c-format
+msgid "pack has %d unresolved delta"
+msgid_plural "pack has %d unresolved deltas"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/init-db.c:35
+#, c-format
+msgid "Could not make %s writable by group"
+msgstr ""
+
+#: builtin/init-db.c:62
+#, c-format
+msgid "insanely long template name %s"
+msgstr ""
+
+#: builtin/init-db.c:67
+#, c-format
+msgid "cannot stat '%s'"
+msgstr ""
+
+#: builtin/init-db.c:73
+#, c-format
+msgid "cannot stat template '%s'"
+msgstr ""
+
+#: builtin/init-db.c:80
+#, c-format
+msgid "cannot opendir '%s'"
+msgstr ""
+
+#: builtin/init-db.c:97
+#, c-format
+msgid "cannot readlink '%s'"
+msgstr ""
+
+#: builtin/init-db.c:99
+#, c-format
+msgid "insanely long symlink %s"
+msgstr ""
+
+#: builtin/init-db.c:102
+#, c-format
+msgid "cannot symlink '%s' '%s'"
+msgstr ""
+
+#: builtin/init-db.c:106
+#, c-format
+msgid "cannot copy '%s' to '%s'"
+msgstr ""
+
+#: builtin/init-db.c:110
+#, c-format
+msgid "ignoring template %s"
+msgstr ""
+
+#: builtin/init-db.c:133
+#, c-format
+msgid "insanely long template path %s"
+msgstr ""
+
+#: builtin/init-db.c:141
+#, c-format
+msgid "templates not found %s"
+msgstr ""
+
+#: builtin/init-db.c:154
+#, c-format
+msgid "not copying templates of a wrong format version %d from '%s'"
+msgstr ""
+
+#: builtin/init-db.c:192
+#, c-format
+msgid "insane git directory %s"
+msgstr ""
+
+#: builtin/init-db.c:322
+#: builtin/init-db.c:325
+#, c-format
+msgid "%s already exists"
+msgstr "%s já existe"
+
+#: builtin/init-db.c:354
+#, c-format
+msgid "unable to handle file type %d"
+msgstr ""
+
+#: builtin/init-db.c:357
+#, c-format
+msgid "unable to move %s to %s"
+msgstr ""
+
+#: builtin/init-db.c:362
+#, c-format
+msgid "Could not create git link %s"
+msgstr ""
+
+#.
+#. * TRANSLATORS: The first '%s' is either "Reinitialized
+#. * existing" or "Initialized empty", the second " shared" or
+#. * "", and the last '%s%s' is the verbatim directory name.
+#.
+#: builtin/init-db.c:419
+#, c-format
+msgid "%s%s Git repository in %s%s\n"
+msgstr ""
+
+#: builtin/init-db.c:420
+msgid "Reinitialized existing"
+msgstr ""
+
+#: builtin/init-db.c:420
+msgid "Initialized empty"
+msgstr "Inicializada vazio"
+
+#: builtin/init-db.c:421
+msgid " shared"
+msgstr " partilhado"
+
+#: builtin/init-db.c:440
+msgid "cannot tell cwd"
+msgstr ""
+
+#: builtin/init-db.c:521
+#: builtin/init-db.c:528
+#, c-format
+msgid "cannot mkdir %s"
+msgstr ""
+
+#: builtin/init-db.c:532
+#, c-format
+msgid "cannot chdir to %s"
+msgstr ""
+
+#: builtin/init-db.c:554
+#, c-format
+msgid "%s (or --work-tree=<directory>) not allowed without specifying %s (or --git-dir=<directory>)"
+msgstr ""
+
+#: builtin/init-db.c:578
+msgid "Cannot access current working directory"
+msgstr ""
+
+#: builtin/init-db.c:585
+#, c-format
+msgid "Cannot access work tree '%s'"
+msgstr ""
+
+#: builtin/log.c:188
+#, c-format
+msgid "Final output: %d %s\n"
+msgstr ""
+
+#: builtin/log.c:401
+#: builtin/log.c:489
+#, c-format
+msgid "Could not read object %s"
+msgstr ""
+
+#: builtin/log.c:513
+#, c-format
+msgid "Unknown type: %d"
+msgstr "Tipo desconhecido: %d"
+
+#: builtin/log.c:602
+msgid "format.headers without value"
+msgstr ""
+
+#: builtin/log.c:675
+msgid "name of output directory is too long"
+msgstr "nome do diretório de saída é demasiado longo"
+
+#: builtin/log.c:686
+#, c-format
+msgid "Cannot open patch file %s"
+msgstr "Não é possivel abrir o ficheiro patch %s"
+
+#: builtin/log.c:700
+msgid "Need exactly one range."
+msgstr "Necessita de exatamente um intervalo."
+
+#: builtin/log.c:708
+msgid "Not a range."
+msgstr "Não é um intervalo."
+
+#: builtin/log.c:745
+msgid "Could not extract email from committer identity."
+msgstr "Não foi possível extrair a identidade do committer do e-mail."
+
+#: builtin/log.c:791
+msgid "Cover letter needs email format"
+msgstr "Carta de apresentação necessita um modelo de e-mail"
+
+#: builtin/log.c:885
+#, c-format
+msgid "insane in-reply-to: %s"
+msgstr ""
+
+#: builtin/log.c:958
+msgid "Two output directories?"
+msgstr "Dois diretórios de saída?"
+
+#: builtin/log.c:1179
+#, c-format
+msgid "bogus committer info %s"
+msgstr ""
+
+#: builtin/log.c:1224
+msgid "-n and -k are mutually exclusive."
+msgstr ""
+
+#: builtin/log.c:1226
+msgid "--subject-prefix and -k are mutually exclusive."
+msgstr ""
+
+#: builtin/log.c:1234
+msgid "--name-only does not make sense"
+msgstr ""
+
+#: builtin/log.c:1236
+msgid "--name-status does not make sense"
+msgstr ""
+
+#: builtin/log.c:1238
+msgid "--check does not make sense"
+msgstr ""
+
+#: builtin/log.c:1261
+msgid "standard output, or directory, which one?"
+msgstr "saída padrão, ou diretório, qual deles?"
+
+#: builtin/log.c:1263
+#, c-format
+msgid "Could not create directory '%s'"
+msgstr ""
+
+#: builtin/log.c:1416
+msgid "Failed to create output files"
+msgstr "Falhou ao criar ficheiros de saída"
+
+#: builtin/log.c:1520
+#, c-format
+msgid "Could not find a tracked remote branch, please specify <upstream> manually.\n"
+msgstr ""
+
+#: builtin/log.c:1536
+#: builtin/log.c:1538
+#: builtin/log.c:1550
+#, c-format
+msgid "Unknown commit %s"
+msgstr "Commit desconhecido %s"
+
+#: builtin/merge.c:90
+msgid "switch `m' requires a value"
+msgstr ""
+
+#: builtin/merge.c:127
+#, c-format
+msgid "Could not find merge strategy '%s'.\n"
+msgstr ""
+
+#: builtin/merge.c:128
+#, c-format
+msgid "Available strategies are:"
+msgstr "As estratégias disponíveis são:"
+
+#: builtin/merge.c:133
+#, c-format
+msgid "Available custom strategies are:"
+msgstr "Estratégias personalizadas disponíveis são:"
+
+#: builtin/merge.c:240
+msgid "could not run stash."
+msgstr ""
+
+#: builtin/merge.c:245
+msgid "stash failed"
+msgstr "falhou o stash"
+
+#: builtin/merge.c:250
+#, c-format
+msgid "not a valid object: %s"
+msgstr ""
+
+#: builtin/merge.c:269
+#: builtin/merge.c:286
+msgid "read-tree failed"
+msgstr ""
+
+#: builtin/merge.c:316
+msgid " (nothing to squash)"
+msgstr " (nada para squash)"
+
+#: builtin/merge.c:329
+#, c-format
+msgid "Squash commit -- not updating HEAD\n"
+msgstr ""
+
+#: builtin/merge.c:361
+msgid "Writing SQUASH_MSG"
+msgstr "Escrevendo SQUASH_MSG"
+
+#: builtin/merge.c:363
+msgid "Finishing SQUASH_MSG"
+msgstr "Terminando SQUASH_MSG"
+
+#: builtin/merge.c:386
+#, c-format
+msgid "No merge message -- not updating HEAD\n"
+msgstr ""
+
+#: builtin/merge.c:437
+#, c-format
+msgid "'%s' does not point to a commit"
+msgstr ""
+
+#: builtin/merge.c:536
+#, c-format
+msgid "Bad branch.%s.mergeoptions string: %s"
+msgstr ""
+
+#: builtin/merge.c:629
+msgid "git write-tree failed to write a tree"
+msgstr ""
+
+#: builtin/merge.c:679
+msgid "failed to read the cache"
+msgstr ""
+
+#: builtin/merge.c:697
+msgid "Unable to write index."
+msgstr ""
+
+#: builtin/merge.c:710
+msgid "Not handling anything other than two heads merge."
+msgstr ""
+
+#: builtin/merge.c:724
+#, c-format
+msgid "Unknown option for merge-recursive: -X%s"
+msgstr ""
+
+#: builtin/merge.c:738
+#, c-format
+msgid "unable to write %s"
+msgstr ""
+
+#: builtin/merge.c:877
+#, c-format
+msgid "Could not read from '%s'"
+msgstr ""
+
+#: builtin/merge.c:886
+#, c-format
+msgid "Not committing merge; use 'git commit' to complete the merge.\n"
+msgstr "Não commitando um merge; usa 'git commit' para completar o merge.\n"
+
+#: builtin/merge.c:892
+msgid ""
+"Please enter a commit message to explain why this merge is necessary,\n"
+"especially if it merges an updated upstream into a topic branch.\n"
+"\n"
+"Lines starting with '#' will be ignored, and an empty message aborts\n"
+"the commit.\n"
+msgstr ""
+
+#: builtin/merge.c:916
+msgid "Empty commit message."
+msgstr "Mensagem de commit vazia."
+
+#: builtin/merge.c:928
+#, c-format
+msgid "Wonderful.\n"
+msgstr "Fastastico.\n"
+
+#: builtin/merge.c:993
+#, c-format
+msgid "Automatic merge failed; fix conflicts and then commit the result.\n"
+msgstr ""
+
+#: builtin/merge.c:1009
+#, c-format
+msgid "'%s' is not a commit"
+msgstr "'%s' não é um commit"
+
+#: builtin/merge.c:1050
+msgid "No current branch."
+msgstr "Nenhuma rama actual"
+
+#: builtin/merge.c:1052
+msgid "No remote for the current branch."
+msgstr ""
+
+#: builtin/merge.c:1054
+msgid "No default upstream defined for the current branch."
+msgstr ""
+
+#: builtin/merge.c:1059
+#, c-format
+msgid "No remote tracking branch for %s from %s"
+msgstr ""
+
+#: builtin/merge.c:1146
+#: builtin/merge.c:1303
+#, c-format
+msgid "%s - not something we can merge"
+msgstr ""
+
+#: builtin/merge.c:1214
+msgid "There is no merge to abort (MERGE_HEAD missing)."
+msgstr ""
+
+#: builtin/merge.c:1230
+#: git-pull.sh:31
+msgid ""
+"You have not concluded your merge (MERGE_HEAD exists).\n"
+"Please, commit your changes before you can merge."
+msgstr ""
+
+#: builtin/merge.c:1233
+#: git-pull.sh:34
+msgid "You have not concluded your merge (MERGE_HEAD exists)."
+msgstr ""
+
+#: builtin/merge.c:1237
+msgid ""
+"You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
+"Please, commit your changes before you can merge."
+msgstr ""
+
+#: builtin/merge.c:1240
+msgid "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."
+msgstr ""
+
+#: builtin/merge.c:1249
+msgid "You cannot combine --squash with --no-ff."
+msgstr ""
+
+#: builtin/merge.c:1254
+msgid "You cannot combine --no-ff with --ff-only."
+msgstr ""
+
+#: builtin/merge.c:1261
+msgid "No commit specified and merge.defaultToUpstream not set."
+msgstr ""
+
+#: builtin/merge.c:1293
+msgid "Can merge only exactly one commit into empty head"
+msgstr ""
+
+#: builtin/merge.c:1296
+msgid "Squash commit into empty head not supported yet"
+msgstr ""
+
+#: builtin/merge.c:1298
+msgid "Non-fast-forward commit does not make sense into an empty head"
+msgstr ""
+
+#: builtin/merge.c:1413
+#, c-format
+msgid "Updating %s..%s\n"
+msgstr "Actualizando %s..%s\n"
+
+#: builtin/merge.c:1451
+#, c-format
+msgid "Trying really trivial in-index merge...\n"
+msgstr ""
+
+#: builtin/merge.c:1458
+#, c-format
+msgid "Nope.\n"
+msgstr "Não.\n"
+
+#: builtin/merge.c:1490
+msgid "Not possible to fast-forward, aborting."
+msgstr ""
+
+#: builtin/merge.c:1513
+#: builtin/merge.c:1592
+#, c-format
+msgid "Rewinding the tree to pristine...\n"
+msgstr ""
+
+#: builtin/merge.c:1517
+#, c-format
+msgid "Trying merge strategy %s...\n"
+msgstr ""
+
+#: builtin/merge.c:1583
+#, c-format
+msgid "No merge strategy handled the merge.\n"
+msgstr ""
+
+#: builtin/merge.c:1585
+#, c-format
+msgid "Merge with strategy %s failed.\n"
+msgstr "Fundir com a estratégia %s falhou.\n"
+
+#: builtin/merge.c:1594
+#, c-format
+msgid "Using the %s to prepare resolving by hand.\n"
+msgstr ""
+
+#: builtin/merge.c:1606
+#, c-format
+msgid "Automatic merge went well; stopped before committing as requested\n"
+msgstr ""
+
+#: builtin/mv.c:108
+#, c-format
+msgid "Checking rename of '%s' to '%s'\n"
+msgstr ""
+
+#: builtin/mv.c:112
+msgid "bad source"
+msgstr "fonte inválida"
+
+#: builtin/mv.c:115
+msgid "can not move directory into itself"
+msgstr ""
+
+#: builtin/mv.c:118
+msgid "cannot move directory over file"
+msgstr ""
+
+#: builtin/mv.c:128
+#, c-format
+msgid "Huh? %.*s is in index?"
+msgstr ""
+
+#: builtin/mv.c:140
+msgid "source directory is empty"
+msgstr "o directorio fonte está vazio"
+
+#: builtin/mv.c:171
+msgid "not under version control"
+msgstr "não está no controlo de versões"
+
+#: builtin/mv.c:173
+msgid "destination exists"
+msgstr "existe destino"
+
+#: builtin/mv.c:181
+#, c-format
+msgid "overwriting '%s'"
+msgstr "subscrevendo '%s'"
+
+#: builtin/mv.c:184
+msgid "Cannot overwrite"
+msgstr "Não consegue subscrever"
+
+#: builtin/mv.c:187
+msgid "multiple sources for the same target"
+msgstr "múltiplas fontes para o mesmo alvo"
+
+#: builtin/mv.c:202
+#, c-format
+msgid "%s, source=%s, destination=%s"
+msgstr ""
+
+#: builtin/mv.c:212
+#, c-format
+msgid "Renaming %s to %s\n"
+msgstr "Mudar de nome %s para %s\n"
+
+#: builtin/mv.c:215
+#: builtin/remote.c:731
+#, c-format
+msgid "renaming '%s' failed"
+msgstr "mudar de nome '%s' falhou"
+
+#: builtin/notes.c:139
+#, c-format
+msgid "unable to start 'show' for object '%s'"
+msgstr ""
+
+#: builtin/notes.c:145
+msgid "can't fdopen 'show' output fd"
+msgstr ""
+
+#: builtin/notes.c:155
+#, c-format
+msgid "failed to close pipe to 'show' for object '%s'"
+msgstr ""
+
+#: builtin/notes.c:158
+#, c-format
+msgid "failed to finish 'show' for object '%s'"
+msgstr ""
+
+#: builtin/notes.c:175
+#: builtin/tag.c:347
+#, c-format
+msgid "could not create file '%s'"
+msgstr ""
+
+#: builtin/notes.c:189
+msgid "Please supply the note contents using either -m or -F option"
+msgstr ""
+
+#: builtin/notes.c:210
+#: builtin/notes.c:973
+#, c-format
+msgid "Removing note for object %s\n"
+msgstr ""
+
+#: builtin/notes.c:215
+msgid "unable to write note object"
+msgstr ""
+
+#: builtin/notes.c:217
+#, c-format
+msgid "The note contents has been left in %s"
+msgstr ""
+
+#: builtin/notes.c:251
+#: builtin/tag.c:542
+#, c-format
+msgid "cannot read '%s'"
+msgstr "não consegue ler '%s'"
+
+#: builtin/notes.c:253
+#: builtin/tag.c:545
+#, c-format
+msgid "could not open or read '%s'"
+msgstr ""
+
+#: builtin/notes.c:272
+#: builtin/notes.c:445
+#: builtin/notes.c:447
+#: builtin/notes.c:507
+#: builtin/notes.c:561
+#: builtin/notes.c:644
+#: builtin/notes.c:649
+#: builtin/notes.c:724
+#: builtin/notes.c:766
+#: builtin/notes.c:968
+#: builtin/reset.c:293
+#: builtin/tag.c:558
+#, c-format
+msgid "Failed to resolve '%s' as a valid ref."
+msgstr ""
+
+#: builtin/notes.c:275
+#, c-format
+msgid "Failed to read object '%s'."
+msgstr ""
+
+#: builtin/notes.c:299
+msgid "Cannot commit uninitialized/unreferenced notes tree"
+msgstr ""
+
+#: builtin/notes.c:340
+#, c-format
+msgid "Bad notes.rewriteMode value: '%s'"
+msgstr ""
+
+#: builtin/notes.c:350
+#, c-format
+msgid "Refusing to rewrite notes in %s (outside of refs/notes/)"
+msgstr ""
+
+#. TRANSLATORS: The first %s is the name of the
+#. environment variable, the second %s is its value
+#: builtin/notes.c:377
+#, c-format
+msgid "Bad %s value: '%s'"
+msgstr "Inválido %s valor: '%s'"
+
+#: builtin/notes.c:441
+#, c-format
+msgid "Malformed input line: '%s'."
+msgstr ""
+
+#: builtin/notes.c:456
+#, c-format
+msgid "Failed to copy notes from '%s' to '%s'"
+msgstr ""
+
+#: builtin/notes.c:500
+#: builtin/notes.c:554
+#: builtin/notes.c:627
+#: builtin/notes.c:639
+#: builtin/notes.c:712
+#: builtin/notes.c:759
+#: builtin/notes.c:1033
+msgid "too many parameters"
+msgstr "demasiado parametros"
+
+#: builtin/notes.c:513
+#: builtin/notes.c:772
+#, c-format
+msgid "No note found for object %s."
+msgstr "Nenhuma nota encontrada para o objecto %s."
+
+#: builtin/notes.c:580
+#, c-format
+msgid "Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite existing notes"
+msgstr ""
+
+#: builtin/notes.c:585
+#: builtin/notes.c:662
+#, c-format
+msgid "Overwriting existing notes for object %s\n"
+msgstr ""
+
+#: builtin/notes.c:635
+msgid "too few parameters"
+msgstr ""
+
+#: builtin/notes.c:656
+#, c-format
+msgid "Cannot copy notes. Found existing notes for object %s. Use '-f' to overwrite existing notes"
+msgstr ""
+
+#: builtin/notes.c:668
+#, c-format
+msgid "Missing notes on source object %s. Cannot copy."
+msgstr ""
+
+#: builtin/notes.c:717
+#, c-format
+msgid ""
+"The -m/-F/-c/-C options have been deprecated for the 'edit' subcommand.\n"
+"Please use 'git notes add -f -m/-F/-c/-C' instead.\n"
+msgstr ""
+
+#: builtin/notes.c:971
+#, c-format
+msgid "Object %s has no note\n"
+msgstr ""
+
+#: builtin/notes.c:1103
+#: builtin/remote.c:1598
+#, c-format
+msgid "Unknown subcommand: %s"
+msgstr ""
+
+#: builtin/pack-objects.c:2315
+#, c-format
+msgid "unsupported index version %s"
+msgstr ""
+
+#: builtin/pack-objects.c:2319
+#, c-format
+msgid "bad index version '%s'"
+msgstr ""
+
+#: builtin/pack-objects.c:2342
+#, c-format
+msgid "option %s does not accept negative form"
+msgstr "opção %s não aceita formato negativo"
+
+#: builtin/pack-objects.c:2346
+#, c-format
+msgid "unable to parse value '%s' for option %s"
+msgstr ""
+
+#: builtin/push.c:45
+msgid "tag shorthand without <tag>"
+msgstr ""
+
+#: builtin/push.c:64
+msgid "--delete only accepts plain target ref names"
+msgstr "--delete só aceita nomes simples para o ref de destino"
+
+#: builtin/push.c:99
+msgid ""
+"\n"
+"To choose either option permanently, see push.default in 'git help config'."
+msgstr ""
+
+#: builtin/push.c:102
+#, c-format
+msgid ""
+"The upstream branch of your current branch does not match\n"
+"the name of your current branch.  To push to the upstream branch\n"
+"on the remote, use\n"
+"\n"
+"    git push %s HEAD:%s\n"
+"\n"
+"To push to the branch of the same name on the remote, use\n"
+"\n"
+"    git push %s %s\n"
+"%s"
+msgstr ""
+
+#: builtin/push.c:121
+#, c-format
+msgid ""
+"You are not currently on a branch.\n"
+"To push the history leading to the current (detached HEAD)\n"
+"state now, use\n"
+"\n"
+"    git push %s HEAD:<name-of-remote-branch>\n"
+msgstr ""
+
+#: builtin/push.c:128
+#, c-format
+msgid ""
+"The current branch %s has no upstream branch.\n"
+"To push the current branch and set the remote as upstream, use\n"
+"\n"
+"    git push --set-upstream %s %s\n"
+msgstr ""
+
+#: builtin/push.c:136
+#, c-format
+msgid "The current branch %s has multiple upstream branches, refusing to push."
+msgstr ""
+
+#: builtin/push.c:139
+#, c-format
+msgid ""
+"You are pushing to remote '%s', which is not the upstream of\n"
+"your current branch '%s', without telling me what to push\n"
+"to update which remote branch."
+msgstr ""
+
+#: builtin/push.c:174
+msgid "You didn't specify any refspecs to push, and push.default is \"nothing\"."
+msgstr ""
+
+#: builtin/push.c:181
+msgid ""
+"Updates were rejected because the tip of your current branch is behind\n"
+"its remote counterpart. Merge the remote changes (e.g. 'git pull')\n"
+"before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
+msgstr ""
+
+#: builtin/push.c:187
+msgid ""
+"Updates were rejected because a pushed branch tip is behind its remote\n"
+"counterpart. If you did not intend to push that branch, you may want to\n"
+"specify branches to push or set the 'push.default' configuration\n"
+"variable to 'current' or 'upstream' to push only the current branch."
+msgstr ""
+
+#: builtin/push.c:193
+msgid ""
+"Updates were rejected because a pushed branch tip is behind its remote\n"
+"counterpart. Check out this branch and merge the remote changes\n"
+"(e.g. 'git pull') before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
+msgstr ""
+
+#: builtin/push.c:233
+#, c-format
+msgid "Pushing to %s\n"
+msgstr "Pushing para %s\n"
+
+#: builtin/push.c:237
+#, c-format
+msgid "failed to push some refs to '%s'"
+msgstr ""
+
+#: builtin/push.c:269
+#, c-format
+msgid "bad repository '%s'"
+msgstr "repositorio inválido '%s'"
+
+#: builtin/push.c:270
+msgid ""
+"No configured push destination.\n"
+"Either specify the URL from the command-line or configure a remote repository using\n"
+"\n"
+"    git remote add <name> <url>\n"
+"\n"
+"and then push using the remote name\n"
+"\n"
+"    git push <name>\n"
+msgstr ""
+
+#: builtin/push.c:285
+msgid "--all and --tags are incompatible"
+msgstr "--all e --tags are são incompatíveis"
+
+#: builtin/push.c:286
+msgid "--all can't be combined with refspecs"
+msgstr ""
+
+#: builtin/push.c:291
+msgid "--mirror and --tags are incompatible"
+msgstr ""
+
+#: builtin/push.c:292
+msgid "--mirror can't be combined with refspecs"
+msgstr ""
+
+#: builtin/push.c:297
+msgid "--all and --mirror are incompatible"
+msgstr ""
+
+#: builtin/push.c:385
+msgid "--delete is incompatible with --all, --mirror and --tags"
+msgstr ""
+
+#: builtin/push.c:387
+msgid "--delete doesn't make sense without any refs"
+msgstr ""
+
+#: builtin/remote.c:98
+#, c-format
+msgid "Updating %s"
+msgstr "Actualizando %s"
+
+#: builtin/remote.c:130
+msgid ""
+"--mirror is dangerous and deprecated; please\n"
+"\t use --mirror=fetch or --mirror=push instead"
+msgstr ""
+
+#: builtin/remote.c:147
+#, c-format
+msgid "unknown mirror argument: %s"
+msgstr "argumento mirror não conhecido: %s"
+
+#: builtin/remote.c:185
+msgid "specifying a master branch makes no sense with --mirror"
+msgstr ""
+
+#: builtin/remote.c:187
+msgid "specifying branches to track makes sense only with fetch mirrors"
+msgstr ""
+
+#: builtin/remote.c:195
+#: builtin/remote.c:646
+#, c-format
+msgid "remote %s already exists."
+msgstr "o remoto %s já existe"
+
+#: builtin/remote.c:199
+#: builtin/remote.c:650
+#, c-format
+msgid "'%s' is not a valid remote name"
+msgstr "'%s' não é um nombe remoto valido"
+
+#: builtin/remote.c:243
+#, c-format
+msgid "Could not setup master '%s'"
+msgstr "Não foi possível configurar a rama master '%s'"
+
+#: builtin/remote.c:299
+#, c-format
+msgid "more than one %s"
+msgstr ""
+
+#: builtin/remote.c:339
+#, c-format
+msgid "Could not get fetch map for refspec %s"
+msgstr ""
+
+#: builtin/remote.c:440
+#: builtin/remote.c:448
+msgid "(matching)"
+msgstr ""
+
+#: builtin/remote.c:452
+msgid "(delete)"
+msgstr "(eliminado)"
+
+#: builtin/remote.c:595
+#: builtin/remote.c:601
+#: builtin/remote.c:607
+#, c-format
+msgid "Could not append '%s' to '%s'"
+msgstr "Não foi possível adicionar o '%s' para o '%s'"
+
+#: builtin/remote.c:639
+#: builtin/remote.c:792
+#: builtin/remote.c:890
+#, c-format
+msgid "No such remote: %s"
+msgstr ""
+
+#: builtin/remote.c:656
+#, c-format
+msgid "Could not rename config section '%s' to '%s'"
+msgstr "Não foi possível renombrar a secção da configuração de '%s' para '%s'"
+
+#: builtin/remote.c:662
+#: builtin/remote.c:799
+#, c-format
+msgid "Could not remove config section '%s'"
+msgstr "Não foi possível remover a secção da configuração '%s'"
+
+#: builtin/remote.c:677
+#, c-format
+msgid ""
+"Not updating non-default fetch respec\n"
+"\t%s\n"
+"\tPlease update the configuration manually if necessary."
+msgstr ""
+
+#: builtin/remote.c:683
+#, c-format
+msgid "Could not append '%s'"
+msgstr "Não foi possível adicionar '%s'"
+
+#: builtin/remote.c:694
+#, c-format
+msgid "Could not set '%s'"
+msgstr "Não foi possível atribuir '%s'"
+
+#: builtin/remote.c:716
+#, c-format
+msgid "deleting '%s' failed"
+msgstr "falhou eliminar '%s'"
+
+#: builtin/remote.c:750
+#, c-format
+msgid "creating '%s' failed"
+msgstr "falhou a criar '%s'"
+
+#: builtin/remote.c:764
+#, c-format
+msgid "Could not remove branch %s"
+msgstr "Não foi possível remover rama %s"
+
+#: builtin/remote.c:834
+msgid ""
+"Note: A branch outside the refs/remotes/ hierarchy was not removed;\n"
+"to delete it, use:"
+msgid_plural ""
+"Note: Some branches outside the refs/remotes/ hierarchy were not removed;\n"
+"to delete them, use:"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/remote.c:943
+#, c-format
+msgid " new (next fetch will store in remotes/%s)"
+msgstr ""
+
+#: builtin/remote.c:946
+msgid " tracked"
+msgstr "seguido"
+
+#: builtin/remote.c:948
+msgid " stale (use 'git remote prune' to remove)"
+msgstr ""
+
+#: builtin/remote.c:950
+msgid " ???"
+msgstr " ???"
+
+#: builtin/remote.c:991
+#, c-format
+msgid "invalid branch.%s.merge; cannot rebase onto > 1 branch"
+msgstr ""
+
+#: builtin/remote.c:998
+#, c-format
+msgid "rebases onto remote %s"
+msgstr ""
+
+#: builtin/remote.c:1001
+#, c-format
+msgid " merges with remote %s"
+msgstr "Fundir com servidor remoto %s"
+
+#: builtin/remote.c:1002
+msgid "    and with remote"
+msgstr "    e com remoto"
+
+#: builtin/remote.c:1004
+#, c-format
+msgid "merges with remote %s"
+msgstr "Fundir com servidor remoto %s"
+
+#: builtin/remote.c:1005
+msgid "   and with remote"
+msgstr "   e com remoto"
+
+#: builtin/remote.c:1051
+msgid "create"
+msgstr "creado"
+
+#: builtin/remote.c:1054
+msgid "delete"
+msgstr "eliminado"
+
+#: builtin/remote.c:1058
+msgid "up to date"
+msgstr "actualizado"
+
+#: builtin/remote.c:1061
+msgid "fast-forwardable"
+msgstr "fast-forwardable"
+
+#: builtin/remote.c:1064
+msgid "local out of date"
+msgstr "local desatualizada"
+
+#: builtin/remote.c:1071
+#, c-format
+msgid "    %-*s forces to %-*s (%s)"
+msgstr ""
+
+#: builtin/remote.c:1074
+#, c-format
+msgid "    %-*s pushes to %-*s (%s)"
+msgstr ""
+
+#: builtin/remote.c:1078
+#, c-format
+msgid "    %-*s forces to %s"
+msgstr ""
+
+#: builtin/remote.c:1081
+#, c-format
+msgid "    %-*s pushes to %s"
+msgstr ""
+
+#: builtin/remote.c:1118
+#, c-format
+msgid "* remote %s"
+msgstr "* remota %s"
+
+#: builtin/remote.c:1119
+#, c-format
+msgid "  Fetch URL: %s"
+msgstr ""
+
+#: builtin/remote.c:1120
+#: builtin/remote.c:1285
+msgid "(no URL)"
+msgstr "(nenhum URL)"
+
+#: builtin/remote.c:1129
+#: builtin/remote.c:1131
+#, c-format
+msgid "  Push  URL: %s"
+msgstr ""
+
+#: builtin/remote.c:1133
+#: builtin/remote.c:1135
+#: builtin/remote.c:1137
+#, c-format
+msgid "  HEAD branch: %s"
+msgstr "Rama HEAD: %s"
+
+#: builtin/remote.c:1139
+#, c-format
+msgid "  HEAD branch (remote HEAD is ambiguous, may be one of the following):\n"
+msgstr ""
+
+#: builtin/remote.c:1151
+#, c-format
+msgid "  Remote branch:%s"
+msgid_plural "  Remote branches:%s"
+msgstr[0] "Rama remota:%s"
+msgstr[1] "Ramas remotas:%s'"
+
+#: builtin/remote.c:1154
+#: builtin/remote.c:1181
+msgid " (status not queried)"
+msgstr ""
+
+#: builtin/remote.c:1163
+msgid "  Local branch configured for 'git pull':"
+msgid_plural "  Local branches configured for 'git pull':"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/remote.c:1171
+msgid "  Local refs will be mirrored by 'git push'"
+msgstr ""
+
+#: builtin/remote.c:1178
+#, c-format
+msgid "  Local ref configured for 'git push'%s:"
+msgid_plural "  Local refs configured for 'git push'%s:"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/remote.c:1216
+msgid "Cannot determine remote HEAD"
+msgstr ""
+
+#: builtin/remote.c:1218
+msgid "Multiple remote HEAD branches. Please choose one explicitly with:"
+msgstr ""
+
+#: builtin/remote.c:1228
+#, c-format
+msgid "Could not delete %s"
+msgstr "Não foi possível abrir %s"
+
+#: builtin/remote.c:1236
+#, c-format
+msgid "Not a valid ref: %s"
+msgstr ""
+
+#: builtin/remote.c:1238
+#, c-format
+msgid "Could not setup %s"
+msgstr "Não foi possível configurar %s"
+
+#: builtin/remote.c:1274
+#, c-format
+msgid " %s will become dangling!"
+msgstr ""
+
+#: builtin/remote.c:1275
+#, c-format
+msgid " %s has become dangling!"
+msgstr ""
+
+#: builtin/remote.c:1281
+#, c-format
+msgid "Pruning %s"
+msgstr "Apagando %s"
+
+#: builtin/remote.c:1282
+#, c-format
+msgid "URL: %s"
+msgstr "URL: %s"
+
+#: builtin/remote.c:1295
+#, c-format
+msgid " * [would prune] %s"
+msgstr ""
+
+#: builtin/remote.c:1298
+#, c-format
+msgid " * [pruned] %s"
+msgstr ""
+
+#: builtin/remote.c:1387
+#: builtin/remote.c:1461
+#, c-format
+msgid "No such remote '%s'"
+msgstr "Não existe este remoto '%s'"
+
+#: builtin/remote.c:1414
+msgid "no remote specified"
+msgstr "Nenhum remoto especificado"
+
+#: builtin/remote.c:1447
+msgid "--add --delete doesn't make sense"
+msgstr ""
+
+#: builtin/remote.c:1487
+#, c-format
+msgid "Invalid old URL pattern: %s"
+msgstr ""
+
+#: builtin/remote.c:1495
+#, c-format
+msgid "No such URL found: %s"
+msgstr "Nenhum URL encontrado: %s"
+
+#: builtin/remote.c:1497
+msgid "Will not delete all non-push URLs"
+msgstr ""
+
+#: builtin/reset.c:33
+msgid "mixed"
+msgstr "mistura"
+
+#: builtin/reset.c:33
+msgid "soft"
+msgstr "leve"
+
+#: builtin/reset.c:33
+msgid "hard"
+msgstr "forte"
+
+#: builtin/reset.c:33
+msgid "merge"
+msgstr "juntar"
+
+#: builtin/reset.c:33
+msgid "keep"
+msgstr "manter"
+
+#: builtin/reset.c:77
+msgid "You do not have a valid HEAD."
+msgstr "Não tens a HEAD válida."
+
+#: builtin/reset.c:79
+msgid "Failed to find tree of HEAD."
+msgstr ""
+
+#: builtin/reset.c:85
+#, c-format
+msgid "Failed to find tree of %s."
+msgstr ""
+
+#: builtin/reset.c:96
+msgid "Could not write new index file."
+msgstr ""
+
+#: builtin/reset.c:106
+#, c-format
+msgid "HEAD is now at %s"
+msgstr "HEAD é agora em %s"
+
+#: builtin/reset.c:130
+msgid "Could not read index"
+msgstr ""
+
+#: builtin/reset.c:133
+msgid "Unstaged changes after reset:"
+msgstr ""
+
+#: builtin/reset.c:223
+#, c-format
+msgid "Cannot do a %s reset in the middle of a merge."
+msgstr ""
+
+#: builtin/reset.c:297
+#, c-format
+msgid "Could not parse object '%s'."
+msgstr "Não foi possível analisar objeto '%s'."
+
+#: builtin/reset.c:302
+msgid "--patch is incompatible with --{hard,mixed,soft}"
+msgstr ""
+
+#: builtin/reset.c:311
+msgid "--mixed with paths is deprecated; use 'git reset -- <paths>' instead."
+msgstr ""
+
+#: builtin/reset.c:313
+#, c-format
+msgid "Cannot do %s reset with paths."
+msgstr ""
+
+#: builtin/reset.c:325
+#, c-format
+msgid "%s reset is not allowed in a bare repository"
+msgstr ""
+
+#: builtin/reset.c:341
+#, c-format
+msgid "Could not reset index file to revision '%s'."
+msgstr ""
+
+#: builtin/revert.c:70
+#: builtin/revert.c:92
+#, c-format
+msgid "%s: %s cannot be used with %s"
+msgstr ""
+
+#: builtin/revert.c:131
+msgid "program error"
+msgstr "erro do programa"
+
+#: builtin/revert.c:221
+msgid "revert failed"
+msgstr "falhou o revert"
+
+#: builtin/revert.c:236
+msgid "cherry-pick failed"
+msgstr "cherry-pick falhou"
+
+#: builtin/rm.c:109
+#, c-format
+msgid ""
+"'%s' has staged content different from both the file and the HEAD\n"
+"(use -f to force removal)"
+msgstr ""
+
+#: builtin/rm.c:115
+#, c-format
+msgid ""
+"'%s' has changes staged in the index\n"
+"(use --cached to keep the file, or -f to force removal)"
+msgstr ""
+
+#: builtin/rm.c:119
+#, c-format
+msgid ""
+"'%s' has local modifications\n"
+"(use --cached to keep the file, or -f to force removal)"
+msgstr ""
+
+#: builtin/rm.c:194
+#, c-format
+msgid "not removing '%s' recursively without -r"
+msgstr ""
+
+#: builtin/rm.c:230
+#, c-format
+msgid "git rm: unable to remove %s"
+msgstr ""
+
+#: builtin/shortlog.c:157
+#, c-format
+msgid "Missing author: %s"
+msgstr "Autor em falta: %s"
+
+#: builtin/tag.c:60
+#, c-format
+msgid "malformed object at '%s'"
+msgstr ""
+
+#: builtin/tag.c:207
+#, c-format
+msgid "tag name too long: %.*s..."
+msgstr ""
+
+#: builtin/tag.c:212
+#, c-format
+msgid "tag '%s' not found."
+msgstr "etiqueta '%s' não foi encontrada."
+
+#: builtin/tag.c:227
+#, c-format
+msgid "Deleted tag '%s' (was %s)\n"
+msgstr ""
+
+#: builtin/tag.c:239
+#, c-format
+msgid "could not verify the tag '%s'"
+msgstr ""
+
+#: builtin/tag.c:249
+msgid ""
+"\n"
+"#\n"
+"# Write a tag message\n"
+"# Lines starting with '#' will be ignored.\n"
+"#\n"
+msgstr ""
+
+#: builtin/tag.c:256
+msgid ""
+"\n"
+"#\n"
+"# Write a tag message\n"
+"# Lines starting with '#' will be kept; you may remove them yourself if you want to.\n"
+"#\n"
+msgstr ""
+
+#: builtin/tag.c:298
+msgid "unable to sign the tag"
+msgstr ""
+
+#: builtin/tag.c:300
+msgid "unable to write tag file"
+msgstr ""
+
+#: builtin/tag.c:325
+msgid "bad object type."
+msgstr ""
+
+#: builtin/tag.c:338
+msgid "tag header too big."
+msgstr ""
+
+#: builtin/tag.c:370
+msgid "no tag message?"
+msgstr "nenhuma mensaje para a etiqueta?"
+
+#: builtin/tag.c:376
+#, c-format
+msgid "The tag message has been left in %s\n"
+msgstr ""
+
+#: builtin/tag.c:425
+msgid "switch 'points-at' requires an object"
+msgstr ""
+
+#: builtin/tag.c:427
+#, c-format
+msgid "malformed object name '%s'"
+msgstr ""
+
+#: builtin/tag.c:506
+msgid "--column and -n are incompatible"
+msgstr "--column e -n are são incompatíveis"
+
+#: builtin/tag.c:523
+msgid "-n option is only allowed with -l."
+msgstr ""
+
+#: builtin/tag.c:525
+msgid "--contains option is only allowed with -l."
+msgstr ""
+
+#: builtin/tag.c:527
+msgid "--points-at option is only allowed with -l."
+msgstr ""
+
+#: builtin/tag.c:535
+msgid "only one -F or -m option is allowed."
+msgstr ""
+
+#: builtin/tag.c:555
+msgid "too many params"
+msgstr "demasiado parametros"
+
+#: builtin/tag.c:561
+#, c-format
+msgid "'%s' is not a valid tag name."
+msgstr ""
+
+#: builtin/tag.c:566
+#, c-format
+msgid "tag '%s' already exists"
+msgstr "etiqueta '%s' já existe"
+
+#: builtin/tag.c:584
+#, c-format
+msgid "%s: cannot lock the ref"
+msgstr ""
+
+#: builtin/tag.c:586
+#, c-format
+msgid "%s: cannot update the ref"
+msgstr ""
+
+#: builtin/tag.c:588
+#, c-format
+msgid "Updated tag '%s' (was %s)\n"
+msgstr ""
+
+#: git.c:16
+msgid "See 'git help <command>' for more information on a specific command."
+msgstr ""
+
+#: common-cmds.h:8
+msgid "Add file contents to the index"
+msgstr ""
+
+#: common-cmds.h:9
+msgid "Find by binary search the change that introduced a bug"
+msgstr ""
+
+#: common-cmds.h:10
+msgid "List, create, or delete branches"
+msgstr "Listar, criar ou apagar ramas"
+
+#: common-cmds.h:11
+msgid "Checkout a branch or paths to the working tree"
+msgstr ""
+
+#: common-cmds.h:12
+msgid "Clone a repository into a new directory"
+msgstr ""
+
+#: common-cmds.h:13
+msgid "Record changes to the repository"
+msgstr "Gravar alterações para o repositório"
+
+#: common-cmds.h:14
+msgid "Show changes between commits, commit and working tree, etc"
+msgstr ""
+
+#: common-cmds.h:15
+msgid "Download objects and refs from another repository"
+msgstr ""
+
+#: common-cmds.h:16
+msgid "Print lines matching a pattern"
+msgstr ""
+
+#: common-cmds.h:17
+msgid "Create an empty git repository or reinitialize an existing one"
+msgstr ""
+
+#: common-cmds.h:18
+msgid "Show commit logs"
+msgstr "Mostrado logs de commits"
+
+#: common-cmds.h:19
+msgid "Join two or more development histories together"
+msgstr ""
+
+#: common-cmds.h:20
+msgid "Move or rename a file, a directory, or a symlink"
+msgstr ""
+
+#: common-cmds.h:21
+msgid "Fetch from and merge with another repository or a local branch"
+msgstr ""
+
+#: common-cmds.h:22
+msgid "Update remote refs along with associated objects"
+msgstr ""
+
+#: common-cmds.h:23
+msgid "Forward-port local commits to the updated upstream head"
+msgstr ""
+
+#: common-cmds.h:24
+msgid "Reset current HEAD to the specified state"
+msgstr ""
+
+#: common-cmds.h:25
+msgid "Remove files from the working tree and from the index"
+msgstr ""
+
+#: common-cmds.h:26
+msgid "Show various types of objects"
+msgstr ""
+
+#: common-cmds.h:27
+msgid "Show the working tree status"
+msgstr "Mostrar o estado los ramos das árvores de trabalho"
+
+#: common-cmds.h:28
+msgid "Create, list, delete or verify a tag object signed with GPG"
+msgstr ""
+
+#: git-am.sh:50
+msgid "You need to set your committer info first"
+msgstr "Necessitas primeiro de especificiar os teus dados de committer"
+
+#: git-am.sh:137
+msgid "Repository lacks necessary blobs to fall back on 3-way merge."
+msgstr ""
+
+#: git-am.sh:154
+msgid ""
+"Did you hand edit your patch?\n"
+"It does not apply to blobs recorded in its index."
+msgstr ""
+
+#: git-am.sh:163
+msgid "Falling back to patching base and 3-way merge..."
+msgstr ""
+
+#: git-am.sh:275
+msgid "Only one StGIT patch series can be applied at once"
+msgstr ""
+
+#: git-am.sh:362
+#, sh-format
+msgid "Patch format $patch_format is not supported."
+msgstr ""
+
+#: git-am.sh:364
+msgid "Patch format detection failed."
+msgstr "Falhou a detecção do formato do patch."
+
+#: git-am.sh:418
+msgid "-d option is no longer supported.  Do not use."
+msgstr ""
+
+#: git-am.sh:481
+#, sh-format
+msgid "previous rebase directory $dotest still exists but mbox given."
+msgstr ""
+
+#: git-am.sh:486
+msgid "Please make up your mind. --skip or --abort?"
+msgstr ""
+
+#: git-am.sh:513
+msgid "Resolve operation not in progress, we are not resuming."
+msgstr ""
+
+#: git-am.sh:579
+#, sh-format
+msgid "Dirty index: cannot apply patches (dirty: $files)"
+msgstr ""
+
+#: git-am.sh:755
+msgid "cannot be interactive without stdin connected to a terminal."
+msgstr ""
+
+#. TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
+#. in your translation. The program will only accept English
+#. input at this point.
+#: git-am.sh:766
+msgid "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
+msgstr "Aplicar? Sim[y]/[n]ão/[e]ditar/[v]er patch/[a]ceitar todos "
+
+#: git-am.sh:802
+#, sh-format
+msgid "Applying: $FIRSTLINE"
+msgstr "Aplicando: $FIRSTLINE"
+
+#: git-am.sh:847
+msgid "No changes -- Patch already applied."
+msgstr "Nenhuma mudança -- Já foi aplicado o patch."
+
+#: git-am.sh:873
+msgid "applying to an empty history"
+msgstr ""
+
+#. TRANSLATORS: Make sure to include [Y] and [n] in your
+#. translation. The program will only accept English input
+#. at this point.
+#: git-bisect.sh:54
+msgid "Do you want me to do it for you [Y/n]? "
+msgstr "Queres que eu faça por sí [Y/n]?"
+
+#: git-bisect.sh:95
+#, sh-format
+msgid "unrecognised option: '$arg'"
+msgstr ""
+
+#: git-bisect.sh:99
+#, sh-format
+msgid "'$arg' does not appear to be a valid revision"
+msgstr ""
+
+#: git-bisect.sh:117
+msgid "Bad HEAD - I need a HEAD"
+msgstr ""
+
+#: git-bisect.sh:130
+#, sh-format
+msgid "Checking out '$start_head' failed. Try 'git bisect reset <validbranch>'."
+msgstr ""
+
+#: git-bisect.sh:140
+msgid "won't bisect on seeked tree"
+msgstr ""
+
+#: git-bisect.sh:144
+msgid "Bad HEAD - strange symbolic ref"
+msgstr ""
+
+#: git-bisect.sh:189
+#, sh-format
+msgid "Bad bisect_write argument: $state"
+msgstr ""
+
+#: git-bisect.sh:218
+#, sh-format
+msgid "Bad rev input: $arg"
+msgstr ""
+
+#: git-bisect.sh:232
+msgid "Please call 'bisect_state' with at least one argument."
+msgstr ""
+
+#: git-bisect.sh:244
+#, sh-format
+msgid "Bad rev input: $rev"
+msgstr ""
+
+#: git-bisect.sh:250
+msgid "'git bisect bad' can take only one argument."
+msgstr ""
+
+#. TRANSLATORS: Make sure to include [Y] and [n] in your
+#. translation. The program will only accept English input
+#. at this point.
+#: git-bisect.sh:279
+msgid "Are you sure [Y/n]? "
+msgstr "Tens a certeza [Y/n]? "
+
+#: git-bisect.sh:354
+#, sh-format
+msgid "'$invalid' is not a valid commit"
+msgstr ""
+
+#: git-bisect.sh:363
+#, sh-format
+msgid ""
+"Could not check out original HEAD '$branch'.\n"
+"Try 'git bisect reset <commit>'."
+msgstr ""
+
+#: git-bisect.sh:390
+msgid "No logfile given"
+msgstr "Nenhum ficheiro de log dado"
+
+#: git-bisect.sh:391
+#, sh-format
+msgid "cannot read $file for replaying"
+msgstr ""
+
+#: git-bisect.sh:408
+msgid "?? what are you talking about?"
+msgstr ""
+
+#: git-bisect.sh:474
+msgid "We are not bisecting."
+msgstr "Não estamos a bisseccionar."
+
+#: git-pull.sh:21
+msgid ""
+"Pull is not possible because you have unmerged files.\n"
+"Please, fix them up in the work tree, and then use 'git add/rm <file>'\n"
+"as appropriate to mark resolution, or use 'git commit -a'."
+msgstr ""
+
+#: git-pull.sh:25
+msgid "Pull is not possible because you have unmerged files."
+msgstr ""
+
+#: git-pull.sh:197
+msgid "updating an unborn branch with changes added to the index"
+msgstr ""
+
+#: git-pull.sh:253
+msgid "Cannot merge multiple branches into empty head"
+msgstr ""
+
+#: git-pull.sh:257
+msgid "Cannot rebase onto multiple branches"
+msgstr "Não é possível fazer rebase com várias ramas"
+
+#: git-stash.sh:51
+msgid "git stash clear with parameters is unimplemented"
+msgstr ""
+
+#: git-stash.sh:74
+msgid "You do not have the initial commit yet"
+msgstr "Tu ainda não tens o commit inicial"
+
+#: git-stash.sh:89
+msgid "Cannot save the current index state"
+msgstr ""
+
+#: git-stash.sh:123
+#: git-stash.sh:136
+msgid "Cannot save the current worktree state"
+msgstr ""
+
+#: git-stash.sh:140
+msgid "No changes selected"
+msgstr "Não há alterações seleccionadas"
+
+#: git-stash.sh:143
+msgid "Cannot remove temporary index (can't happen)"
+msgstr ""
+
+#: git-stash.sh:156
+msgid "Cannot record working tree state"
+msgstr ""
+
+#: git-stash.sh:223
+msgid "No local changes to save"
+msgstr "Sem alterações locais para guardar"
+
+#: git-stash.sh:227
+msgid "Cannot initialize stash"
+msgstr "Não é possível inicializar o stash"
+
+#: git-stash.sh:235
+msgid "Cannot save the current status"
+msgstr ""
+
+#: git-stash.sh:253
+msgid "Cannot remove worktree changes"
+msgstr ""
+
+#: git-stash.sh:352
+msgid "No stash found."
+msgstr "nenhum stash encontrado."
+
+#: git-stash.sh:359
+#, sh-format
+msgid "Too many revisions specified: $REV"
+msgstr ""
+
+#: git-stash.sh:365
+#, sh-format
+msgid "$reference is not valid reference"
+msgstr ""
+
+#: git-stash.sh:393
+#, sh-format
+msgid "'$args' is not a stash-like commit"
+msgstr ""
+
+#: git-stash.sh:404
+#, sh-format
+msgid "'$args' is not a stash reference"
+msgstr ""
+
+#: git-stash.sh:412
+msgid "unable to refresh index"
+msgstr ""
+
+#: git-stash.sh:416
+msgid "Cannot apply a stash in the middle of a merge"
+msgstr ""
+
+#: git-stash.sh:424
+msgid "Conflicts in index. Try without --index."
+msgstr ""
+
+#: git-stash.sh:426
+msgid "Could not save index tree"
+msgstr "Não foi posivel guardar o index tree"
+
+#: git-stash.sh:460
+msgid "Cannot unstage modified files"
+msgstr ""
+
+#: git-stash.sh:491
+#, sh-format
+msgid "Dropped ${REV} ($s)"
+msgstr "Deixado cair ${REV} ($s)"
+
+#: git-stash.sh:492
+#, sh-format
+msgid "${REV}: Could not drop stash entry"
+msgstr ""
+
+#: git-stash.sh:499
+msgid "No branch name specified"
+msgstr "Nenhum nome para a rama especificado"
+
+#: git-stash.sh:570
+msgid "(To restore them type \"git stash apply\")"
+msgstr ""
+
+#: git-submodule.sh:56
+#, sh-format
+msgid "cannot strip one component off url '$remoteurl'"
+msgstr ""
+
+#: git-submodule.sh:109
+#, sh-format
+msgid "No submodule mapping found in .gitmodules for path '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:150
+#, sh-format
+msgid "Clone of '$url' into submodule path '$sm_path' failed"
+msgstr ""
+
+#: git-submodule.sh:160
+#, sh-format
+msgid "Gitdir '$a' is part of the submodule path '$b' or vice versa"
+msgstr ""
+
+#: git-submodule.sh:249
+#, sh-format
+msgid "repo URL: '$repo' must be absolute or begin with ./|../"
+msgstr ""
+
+#: git-submodule.sh:266
+#, sh-format
+msgid "'$sm_path' already exists in the index"
+msgstr ""
+
+#: git-submodule.sh:283
+#, sh-format
+msgid "'$sm_path' already exists and is not a valid git repo"
+msgstr ""
+
+#: git-submodule.sh:297
+#, sh-format
+msgid "Unable to checkout submodule '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:302
+#, sh-format
+msgid "Failed to add submodule '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:307
+#, sh-format
+msgid "Failed to register submodule '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:349
+#, sh-format
+msgid "Entering '$prefix$sm_path'"
+msgstr "Entrando '$prefix$sm_path'"
+
+#: git-submodule.sh:363
+#, sh-format
+msgid "Stopping at '$sm_path'; script returned non-zero status."
+msgstr ""
+
+#: git-submodule.sh:405
+#, sh-format
+msgid "No url found for submodule path '$sm_path' in .gitmodules"
+msgstr ""
+
+#: git-submodule.sh:414
+#, sh-format
+msgid "Failed to register url for submodule path '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:422
+#, sh-format
+msgid "Failed to register update mode for submodule path '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:424
+#, sh-format
+msgid "Submodule '$name' ($url) registered for path '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:523
+#, sh-format
+msgid ""
+"Submodule path '$sm_path' not initialized\n"
+"Maybe you want to use 'update --init'?"
+msgstr ""
+
+#: git-submodule.sh:536
+#, sh-format
+msgid "Unable to find current revision in submodule path '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:555
+#, sh-format
+msgid "Unable to fetch in submodule path '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:569
+#, sh-format
+msgid "Unable to rebase '$sha1' in submodule path '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:570
+#, sh-format
+msgid "Submodule path '$sm_path': rebased into '$sha1'"
+msgstr ""
+
+#: git-submodule.sh:575
+#, sh-format
+msgid "Unable to merge '$sha1' in submodule path '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:576
+#, sh-format
+msgid "Submodule path '$sm_path': merged in '$sha1'"
+msgstr ""
+
+#: git-submodule.sh:581
+#, sh-format
+msgid "Unable to checkout '$sha1' in submodule path '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:582
+#, sh-format
+msgid "Submodule path '$sm_path': checked out '$sha1'"
+msgstr ""
+
+#: git-submodule.sh:604
+#: git-submodule.sh:927
+#, sh-format
+msgid "Failed to recurse into submodule path '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:712
+msgid "--"
+msgstr "--"
+
+#: git-submodule.sh:770
+#, sh-format
+msgid "  Warn: $name doesn't contain commit $sha1_src"
+msgstr ""
+
+#: git-submodule.sh:773
+#, sh-format
+msgid "  Warn: $name doesn't contain commit $sha1_dst"
+msgstr ""
+
+#: git-submodule.sh:776
+#, sh-format
+msgid "  Warn: $name doesn't contain commits $sha1_src and $sha1_dst"
+msgstr ""
+
+#: git-submodule.sh:801
+msgid "blob"
+msgstr "blob"
+
+#: git-submodule.sh:802
+msgid "submodule"
+msgstr "submódulos"
+
+#: git-submodule.sh:973
+#, sh-format
+msgid "Synchronizing submodule url for '$name'"
+msgstr ""
+
+#~ msgid "cherry-pick"
+#~ msgstr "cherry-pick"
+
+#~ msgid "Please enter the commit message for your changes."
+#~ msgstr "Por favor insira a mensagem de commit das suas alterações."
+
+#~ msgid "Too many options specified"
+#~ msgstr "Demasiadas opções especificadas"
diff --git a/po/sv.po b/po/sv.po
new file mode 100644
index 0000000..2635c2c
--- /dev/null
+++ b/po/sv.po
@@ -0,0 +1,5738 @@
+# Swedish translations for Git.
+# Copyright (C) 2010-2012 Peter krefting <peter@softwolves.pp.se>
+# This file is distributed under the same license as the Git package.
+# Peter Krefting <peter@softwolves.pp.se>, 2010, 2011, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: git 1.7.10\n"
+"Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
+"POT-Creation-Date: 2012-07-03 10:23+0800\n"
+"PO-Revision-Date: 2012-07-04 19:33+0100\n"
+"Last-Translator: Peter Krefting <peter@softwolves.pp.se>\n"
+"Language-Team: Swedish <tp-sv@listor.tp-sv.se>\n"
+"Language: sv\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: advice.c:40
+#, c-format
+msgid "hint: %.*s\n"
+msgstr "tips: %.*s\n"
+
+#.
+#. * Message used both when 'git commit' fails and when
+#. * other commands doing a merge do.
+#.
+#: advice.c:70
+msgid ""
+"Fix them up in the work tree,\n"
+"and then use 'git add/rm <file>' as\n"
+"appropriate to mark resolution and make a commit,\n"
+"or use 'git commit -a'."
+msgstr ""
+"Rätta dem i din arbetskatalog,\n"
+"och använd sedan \"git add/rm <fil>\" som\n"
+"lämpligt för att ange lösning och checka in,\n"
+"eller använd \"git commit -a\"."
+
+#: bundle.c:36
+#, c-format
+msgid "'%s' does not look like a v2 bundle file"
+msgstr "'%s' ser inte ut som en v2-bundle-fil"
+
+#: bundle.c:63
+#, c-format
+msgid "unrecognized header: %s%s (%d)"
+msgstr "okänt huvud: %s%s (%d)"
+
+#: bundle.c:89 builtin/commit.c:696
+#, c-format
+msgid "could not open '%s'"
+msgstr "kunde inte öppna \"%s\""
+
+#: bundle.c:140
+msgid "Repository lacks these prerequisite commits:"
+msgstr "Arkivet saknar dessa nödvändiga incheckningar:"
+
+#: bundle.c:164 sequencer.c:550 sequencer.c:982 builtin/log.c:290
+#: builtin/log.c:721 builtin/log.c:1310 builtin/log.c:1529 builtin/merge.c:347
+#: builtin/shortlog.c:181
+msgid "revision walk setup failed"
+msgstr "misslyckades skapa revisionstraversering"
+
+#: bundle.c:186
+#, c-format
+msgid "The bundle contains %d ref"
+msgid_plural "The bundle contains %d refs"
+msgstr[0] "Paketet (bundlen) innehåller %d referens"
+msgstr[1] "Paketet (bundlen) innehåller %d referenser"
+
+#: bundle.c:192
+msgid "The bundle records a complete history."
+msgstr "Paketet (bundlen) beskriver en komplett historik."
+
+#: bundle.c:195
+#, c-format
+msgid "The bundle requires this ref"
+msgid_plural "The bundle requires these %d refs"
+msgstr[0] "Paketet (bundlen) kräver denna referens"
+msgstr[1] "Paketet (bundlen) kräver dessa %d referenser"
+
+#: bundle.c:294
+msgid "rev-list died"
+msgstr "rev-list dog"
+
+#: bundle.c:300 builtin/log.c:1206 builtin/shortlog.c:284
+#, c-format
+msgid "unrecognized argument: %s"
+msgstr "okänt argument: %s"
+
+#: bundle.c:335
+#, c-format
+msgid "ref '%s' is excluded by the rev-list options"
+msgstr "referensen \"%s\" exkluderas av argumenten till rev-list"
+
+#: bundle.c:380
+msgid "Refusing to create empty bundle."
+msgstr "Vägrar skapa ett tomt paket (bundle)."
+
+#: bundle.c:398
+msgid "Could not spawn pack-objects"
+msgstr "Kunde inte starta pack-objects"
+
+#: bundle.c:416
+msgid "pack-objects died"
+msgstr "pack-objects misslyckades"
+
+#: bundle.c:419
+#, c-format
+msgid "cannot create '%s'"
+msgstr "kan inte skapa \"%s\""
+
+#: bundle.c:441
+msgid "index-pack died"
+msgstr "index-pack dog"
+
+#: commit.c:48
+#, c-format
+msgid "could not parse %s"
+msgstr "kunde inte tolka %s"
+
+#: commit.c:50
+#, c-format
+msgid "%s %s is not a commit!"
+msgstr "%s %s är inte en incheckning!"
+
+#: compat/obstack.c:406 compat/obstack.c:408
+msgid "memory exhausted"
+msgstr "minnet slut"
+
+#: connected.c:39
+msgid "Could not run 'git rev-list'"
+msgstr "Kunde inte köra \"git rev-list\""
+
+#: connected.c:48
+#, c-format
+msgid "failed write to rev-list: %s"
+msgstr "kunde inte skriva till rev-list: %s"
+
+#: connected.c:56
+#, c-format
+msgid "failed to close rev-list's stdin: %s"
+msgstr "kunde inte stänga rev-list:s standard in: %s"
+
+#: date.c:95
+msgid "in the future"
+msgstr "i framtiden"
+
+#: date.c:101
+#, c-format
+msgid "%lu second ago"
+msgid_plural "%lu seconds ago"
+msgstr[0] "%lu sekund sedan"
+msgstr[1] "%lu sekunder sedan"
+
+#: date.c:108
+#, c-format
+msgid "%lu minute ago"
+msgid_plural "%lu minutes ago"
+msgstr[0] "%lu minut sedan"
+msgstr[1] "%lu minuter sedan"
+
+#: date.c:115
+#, c-format
+msgid "%lu hour ago"
+msgid_plural "%lu hours ago"
+msgstr[0] "%lu timme sedan"
+msgstr[1] "%lu timmar sedan"
+
+#: date.c:122
+#, c-format
+msgid "%lu day ago"
+msgid_plural "%lu days ago"
+msgstr[0] "%lu dag sedan"
+msgstr[1] "%lu dagar sedan"
+
+#: date.c:128
+#, c-format
+msgid "%lu week ago"
+msgid_plural "%lu weeks ago"
+msgstr[0] "%lu vecka sedan"
+msgstr[1] "%lu veckor sedan"
+
+#: date.c:135
+#, c-format
+msgid "%lu month ago"
+msgid_plural "%lu months ago"
+msgstr[0] "%lu månad sedan"
+msgstr[1] "%lu månader sedan"
+
+#: date.c:146
+#, c-format
+msgid "%lu year"
+msgid_plural "%lu years"
+msgstr[0] "%lu år"
+msgstr[1] "%lu år"
+
+#: date.c:149
+#, c-format
+msgid "%s, %lu month ago"
+msgid_plural "%s, %lu months ago"
+msgstr[0] "%s, %lu månad sedan"
+msgstr[1] "%s, %lu månader sedan"
+
+#: date.c:154 date.c:159
+#, c-format
+msgid "%lu year ago"
+msgid_plural "%lu years ago"
+msgstr[0] "%lu år sedan"
+msgstr[1] "%lu år sedan"
+
+#: diff.c:105
+#, c-format
+msgid "  Failed to parse dirstat cut-off percentage '%.*s'\n"
+msgstr "  Misslyckades tolka dirstat-avskärningsprocentandel \"%.*s\"\n"
+
+#: diff.c:110
+#, c-format
+msgid "  Unknown dirstat parameter '%.*s'\n"
+msgstr "  Okänd dirstat-parameter \"%.*s\"\n"
+
+#: diff.c:210
+#, c-format
+msgid ""
+"Found errors in 'diff.dirstat' config variable:\n"
+"%s"
+msgstr ""
+"Hittade fel i konfigurationsvariabeln \"diff.dirstat\":\n"
+"%s"
+
+#: diff.c:1400
+msgid " 0 files changed\n"
+msgstr " 0 filer ändrade\n"
+
+#: diff.c:1404
+#, c-format
+msgid " %d file changed"
+msgid_plural " %d files changed"
+msgstr[0] " %d fil ändrad"
+msgstr[1] " %d filer ändrade"
+
+#: diff.c:1421
+#, c-format
+msgid ", %d insertion(+)"
+msgid_plural ", %d insertions(+)"
+msgstr[0] ", %d tillägg(+)"
+msgstr[1] ", %d tillägg(+)"
+
+#: diff.c:1432
+#, c-format
+msgid ", %d deletion(-)"
+msgid_plural ", %d deletions(-)"
+msgstr[0] ", %d borttagning(-)"
+msgstr[1] ", %d borttagningar(-)"
+
+#: diff.c:3478
+#, c-format
+msgid ""
+"Failed to parse --dirstat/-X option parameter:\n"
+"%s"
+msgstr ""
+"Misslyckades tolka argument till flaggan --dirstat/-X;\n"
+"%s"
+
+#: gpg-interface.c:59
+msgid "could not run gpg."
+msgstr "kunde inte köra gpg."
+
+#: gpg-interface.c:71
+msgid "gpg did not accept the data"
+msgstr "gpg godtog inte data"
+
+#: gpg-interface.c:82
+msgid "gpg failed to sign the data"
+msgstr "gpg misslyckades signera data"
+
+#: grep.c:1320
+#, c-format
+msgid "'%s': unable to read %s"
+msgstr "\"%s\" kunde inte läsa %s"
+
+#: grep.c:1337
+#, c-format
+msgid "'%s': %s"
+msgstr "\"%s\": %s"
+
+#: grep.c:1348
+#, c-format
+msgid "'%s': short read %s"
+msgstr "\"%s\": kort läsning %s"
+
+#: help.c:208
+#, c-format
+msgid "available git commands in '%s'"
+msgstr "git-kommandon tillgängliga i \"%s\""
+
+#: help.c:215
+msgid "git commands available from elsewhere on your $PATH"
+msgstr "git-kommandon från andra platser i din $PATH"
+
+#: help.c:271
+#, c-format
+msgid ""
+"'%s' appears to be a git command, but we were not\n"
+"able to execute it. Maybe git-%s is broken?"
+msgstr ""
+"\"%s\" verkar vara ett git-kommando, men vi kan inte\n"
+"köra det. Kanske git-%s är trasigt?"
+
+#: help.c:328
+msgid "Uh oh. Your system reports no Git commands at all."
+msgstr "Oj då. Ditt system rapporterar inga Git-kommandon alls."
+
+#: help.c:350
+#, c-format
+msgid ""
+"WARNING: You called a Git command named '%s', which does not exist.\n"
+"Continuing under the assumption that you meant '%s'"
+msgstr ""
+"VARNING: Du anropade ett Git-kommando vid namn \"%s\", som inte finns.\n"
+"Fortsätter under förutsättningen att du menade \"%s\""
+
+#: help.c:355
+#, c-format
+msgid "in %0.1f seconds automatically..."
+msgstr "automatiskt om %0.1f sekunder..."
+
+#: help.c:362
+#, c-format
+msgid "git: '%s' is not a git command. See 'git --help'."
+msgstr "git: \"%s\" är inte ett git-kommando. Se \"git --help\"."
+
+#: help.c:366
+msgid ""
+"\n"
+"Did you mean this?"
+msgid_plural ""
+"\n"
+"Did you mean one of these?"
+msgstr[0] ""
+"\n"
+"Menade du detta?"
+msgstr[1] ""
+"\n"
+"Menade du ett av dessa?"
+
+#: parse-options.c:493
+msgid "..."
+msgstr "..."
+
+#: parse-options.c:511
+#, c-format
+msgid "usage: %s"
+msgstr "användning: %s"
+
+#. TRANSLATORS: the colon here should align with the
+#. one in "usage: %s" translation
+#: parse-options.c:515
+#, c-format
+msgid "   or: %s"
+msgstr "     eller: %s"
+
+#: parse-options.c:518
+#, c-format
+msgid "    %s"
+msgstr "    %s"
+
+#: remote.c:1629
+#, c-format
+msgid "Your branch is ahead of '%s' by %d commit.\n"
+msgid_plural "Your branch is ahead of '%s' by %d commits.\n"
+msgstr[0] "Din gren ligger före \"%s\" med %d incheckning.\n"
+msgstr[1] "Din gren ligger före \"%s\" med %d incheckningar.\n"
+
+#: remote.c:1635
+#, c-format
+msgid "Your branch is behind '%s' by %d commit, and can be fast-forwarded.\n"
+msgid_plural ""
+"Your branch is behind '%s' by %d commits, and can be fast-forwarded.\n"
+msgstr[0] ""
+"Din gren ligger efter \"%s\" med %d incheckning, och kan snabbspolas.\n"
+msgstr[1] ""
+"Din gren ligger efter \"%s\" med %d incheckningar, och kan snabbspolas.\n"
+
+#: remote.c:1643
+#, c-format
+msgid ""
+"Your branch and '%s' have diverged,\n"
+"and have %d and %d different commit each, respectively.\n"
+msgid_plural ""
+"Your branch and '%s' have diverged,\n"
+"and have %d and %d different commits each, respectively.\n"
+msgstr[0] ""
+"Din gren och \"%s\" har divergerat,\n"
+"och har %d respektive %d olika incheckning.\n"
+msgstr[1] ""
+"Din gren och \"%s\" har divergerat,\n"
+"och har %d respektive %d olika incheckningar.\n"
+
+#: sequencer.c:121 builtin/merge.c:865 builtin/merge.c:978
+#: builtin/merge.c:1088 builtin/merge.c:1098
+#, c-format
+msgid "Could not open '%s' for writing"
+msgstr "Kunde inte öppna \"%s\" för skrivning"
+
+#: sequencer.c:123 builtin/merge.c:333 builtin/merge.c:868
+#: builtin/merge.c:1090 builtin/merge.c:1103
+#, c-format
+msgid "Could not write to '%s'"
+msgstr "Kunde inte skriva till \"%s\""
+
+#: sequencer.c:144
+msgid ""
+"after resolving the conflicts, mark the corrected paths\n"
+"with 'git add <paths>' or 'git rm <paths>'"
+msgstr ""
+"efter att ha löst konflikterna, markera de rättade sökvägarna\n"
+"med \"git add <sökvägar>\" eller \"git rm <sökvägar>\""
+
+#: sequencer.c:147
+msgid ""
+"after resolving the conflicts, mark the corrected paths\n"
+"with 'git add <paths>' or 'git rm <paths>'\n"
+"and commit the result with 'git commit'"
+msgstr ""
+"efter att ha löst konflikterna, markera de rättade sökvägarna\n"
+"med \"git add <sökvägar>\" eller \"git rm <sökvägar>\"\n"
+"och checka in resultatet med \"git commit\""
+
+#: sequencer.c:160 sequencer.c:758 sequencer.c:841
+#, c-format
+msgid "Could not write to %s"
+msgstr "Kunde inte skriva till %s"
+
+#: sequencer.c:163
+#, c-format
+msgid "Error wrapping up %s"
+msgstr "Fel vid ombrytning av %s"
+
+#: sequencer.c:178
+msgid "Your local changes would be overwritten by cherry-pick."
+msgstr "Dina lokala ändringar skulle skrivas över av \"cherry-pick\"."
+
+#: sequencer.c:180
+msgid "Your local changes would be overwritten by revert."
+msgstr "Dina lokala ändringar skulle skrivas över av \"revert\"."
+
+#: sequencer.c:183
+msgid "Commit your changes or stash them to proceed."
+msgstr "Checka in dina ändringar eller använd \"stash\" för att fortsätta."
+
+#. TRANSLATORS: %s will be "revert" or "cherry-pick"
+#: sequencer.c:233
+#, c-format
+msgid "%s: Unable to write new index file"
+msgstr "%s: Kunde inte skriva ny indexfil"
+
+#: sequencer.c:261
+msgid "Could not resolve HEAD commit\n"
+msgstr "Kunde inte bestämma HEAD:s incheckning\n"
+
+#: sequencer.c:282
+msgid "Unable to update cache tree\n"
+msgstr "Kan inte uppdatera cacheträd\n"
+
+#: sequencer.c:324
+#, c-format
+msgid "Could not parse commit %s\n"
+msgstr "Kunde inte tolka incheckningen %s\n"
+
+#: sequencer.c:329
+#, c-format
+msgid "Could not parse parent commit %s\n"
+msgstr "Kunde inte tolka föräldraincheckningen %s\n"
+
+#: sequencer.c:395
+msgid "Your index file is unmerged."
+msgstr "Din indexfil har inte slagits ihop."
+
+#: sequencer.c:398
+msgid "You do not have a valid HEAD"
+msgstr "Du har ingen giltig HEAD"
+
+#: sequencer.c:413
+#, c-format
+msgid "Commit %s is a merge but no -m option was given."
+msgstr "Incheckning %s är en sammanslagning, men flaggan -m angavs inte."
+
+#: sequencer.c:421
+#, c-format
+msgid "Commit %s does not have parent %d"
+msgstr "Incheckning %s har inte förälder %d"
+
+#: sequencer.c:425
+#, c-format
+msgid "Mainline was specified but commit %s is not a merge."
+msgstr "Huvudlinje angavs, men incheckningen %s är inte en sammanslagning"
+
+#. TRANSLATORS: The first %s will be "revert" or
+#. "cherry-pick", the second %s a SHA1
+#: sequencer.c:436
+#, c-format
+msgid "%s: cannot parse parent commit %s"
+msgstr "%s: kan inte tolka föräldraincheckningen %s"
+
+#: sequencer.c:440
+#, c-format
+msgid "Cannot get commit message for %s"
+msgstr "Kan inte hämta incheckningsmeddelande för %s"
+
+#: sequencer.c:524
+#, c-format
+msgid "could not revert %s... %s"
+msgstr "kunde inte ångra %s... %s"
+
+#: sequencer.c:525
+#, c-format
+msgid "could not apply %s... %s"
+msgstr "kunde inte tillämpa %s... %s"
+
+#: sequencer.c:553
+msgid "empty commit set passed"
+msgstr "den angivna uppsättningen incheckningar är tom"
+
+#: sequencer.c:561
+#, c-format
+msgid "git %s: failed to read the index"
+msgstr "git %s: misslyckades läsa indexet"
+
+#: sequencer.c:566
+#, c-format
+msgid "git %s: failed to refresh the index"
+msgstr "git %s: misslyckades uppdatera indexet"
+
+#: sequencer.c:624
+#, c-format
+msgid "Cannot %s during a %s"
+msgstr "kan inte %s under en %s"
+
+#: sequencer.c:646
+#, c-format
+msgid "Could not parse line %d."
+msgstr "Kan inte tolka rad %d."
+
+#: sequencer.c:651
+msgid "No commits parsed."
+msgstr "Inga incheckningar lästes."
+
+#: sequencer.c:664
+#, c-format
+msgid "Could not open %s"
+msgstr "Kunde inte öppna %s"
+
+#: sequencer.c:668
+#, c-format
+msgid "Could not read %s."
+msgstr "kunde inte läsa %s."
+
+#: sequencer.c:675
+#, c-format
+msgid "Unusable instruction sheet: %s"
+msgstr "Oanvändbart manus: %s"
+
+#: sequencer.c:703
+#, c-format
+msgid "Invalid key: %s"
+msgstr "Felaktig nyckel: %s"
+
+#: sequencer.c:706
+#, c-format
+msgid "Invalid value for %s: %s"
+msgstr "Felaktigt värde för %s: %s"
+
+#: sequencer.c:718
+#, c-format
+msgid "Malformed options sheet: %s"
+msgstr "Trasigt manus: %s"
+
+#: sequencer.c:739
+msgid "a cherry-pick or revert is already in progress"
+msgstr "en \"cherry-pick\" eller \"revert\" pågår redan"
+
+#: sequencer.c:740
+msgid "try \"git cherry-pick (--continue | --quit | --abort)\""
+msgstr "testa \"git cherry-pick (--continue | --quit | --abort)\""
+
+#: sequencer.c:744
+#, c-format
+msgid "Could not create sequencer directory %s"
+msgstr "Kunde inte skapa \"sequencer\"-katalogen \"%s\""
+
+#: sequencer.c:760 sequencer.c:845
+#, c-format
+msgid "Error wrapping up %s."
+msgstr "Fel vid ombrytning av %s."
+
+#: sequencer.c:779 sequencer.c:913
+msgid "no cherry-pick or revert in progress"
+msgstr "ingen \"cherry-pick\" eller \"revert\" pågår"
+
+#: sequencer.c:781
+msgid "cannot resolve HEAD"
+msgstr "kan inte bestämma HEAD"
+
+#: sequencer.c:783
+msgid "cannot abort from a branch yet to be born"
+msgstr "kan inte avbryta från en gren som ännu inte är född"
+
+#: sequencer.c:805 builtin/apply.c:3697
+#, c-format
+msgid "cannot open %s: %s"
+msgstr "kan inte öppna %s: %s"
+
+#: sequencer.c:808
+#, c-format
+msgid "cannot read %s: %s"
+msgstr "kan inte läsa %s: %s"
+
+#: sequencer.c:809
+msgid "unexpected end of file"
+msgstr "oväntat filslut"
+
+#: sequencer.c:815
+#, c-format
+msgid "stored pre-cherry-pick HEAD file '%s' is corrupt"
+msgstr "sparad HEAD-fil från före \"cherry-pick\", \"%s\", är trasig"
+
+#: sequencer.c:838
+#, c-format
+msgid "Could not format %s."
+msgstr "Kunde inte formatera %s."
+
+#: sequencer.c:1000
+msgid "Can't revert as initial commit"
+msgstr "Kan inte ångra som första incheckning"
+
+#: sequencer.c:1001
+msgid "Can't cherry-pick into empty head"
+msgstr "Kan inte göra \"cherry-pick\" i ett tomt huvud"
+
+#: sha1_name.c:864
+msgid "HEAD does not point to a branch"
+msgstr "HEAD pekar inte på en gren"
+
+#: sha1_name.c:867
+#, c-format
+msgid "No such branch: '%s'"
+msgstr "Okänd gren: \"%s\""
+
+#: sha1_name.c:869
+#, c-format
+msgid "No upstream configured for branch '%s'"
+msgstr "Ingen standarduppström angiven för grenen \"%s\""
+
+#: sha1_name.c:872
+#, c-format
+msgid "Upstream branch '%s' not stored as a remote-tracking branch"
+msgstr "Uppströmsgrenen \"%s\" är inte lagrad som en fjärrspårande gren"
+
+#: wrapper.c:413
+#, c-format
+msgid "unable to look up current user in the passwd file: %s"
+msgstr "kan inte slå upp aktuell användare i passwd-filen: %s"
+
+#: wrapper.c:414
+msgid "no such user"
+msgstr "okänd användare"
+
+#: wt-status.c:141
+msgid "Unmerged paths:"
+msgstr "Ej sammanslagna sökvägar:"
+
+#: wt-status.c:168 wt-status.c:195
+#, c-format
+msgid "  (use \"git reset %s <file>...\" to unstage)"
+msgstr "  (använd \"git reset %s <fil>...\" för att ta bort från kö)"
+
+#: wt-status.c:170 wt-status.c:197
+msgid "  (use \"git rm --cached <file>...\" to unstage)"
+msgstr "  (använd \"git rm --cached <fil>...\" för att ta bort från kö)"
+
+#: wt-status.c:174
+msgid "  (use \"git add <file>...\" to mark resolution)"
+msgstr "  (använd \"git add <fil>...\" för att ange lösning)"
+
+#: wt-status.c:176 wt-status.c:180
+msgid "  (use \"git add/rm <file>...\" as appropriate to mark resolution)"
+msgstr "  (använd \"git add/rm <fil>...\" som lämpligt för att ange lösning)"
+
+#: wt-status.c:178
+msgid "  (use \"git rm <file>...\" to mark resolution)"
+msgstr "  (använd \"git rm <fil>...\" för att ange lösning)"
+
+#: wt-status.c:189
+msgid "Changes to be committed:"
+msgstr "Ändringar att checka in:"
+
+#: wt-status.c:207
+msgid "Changes not staged for commit:"
+msgstr "Ändringar ej i incheckningskön:"
+
+#: wt-status.c:211
+msgid "  (use \"git add <file>...\" to update what will be committed)"
+msgstr ""
+"  (använd \"git add <fil>...\" för att uppdatera vad som skall checkas in)"
+
+#: wt-status.c:213
+msgid "  (use \"git add/rm <file>...\" to update what will be committed)"
+msgstr ""
+"  (använd \"git add/rm <fil>...\" för att uppdatera vad som skall checkas in)"
+
+#: wt-status.c:214
+msgid ""
+"  (use \"git checkout -- <file>...\" to discard changes in working directory)"
+msgstr ""
+"  (använd \"git checkout -- <fil>...\" för att förkasta ändringar i "
+"arbetskatalogen)"
+
+#: wt-status.c:216
+msgid "  (commit or discard the untracked or modified content in submodules)"
+msgstr ""
+"  (checka in eller förkasta ospårat eller ändrat innehåll i undermoduler)"
+
+# %s är ett verb ("Untracked"/"Ignored"); lägg till ett -e.
+#: wt-status.c:225
+#, c-format
+msgid "%s files:"
+msgstr "%se filer:"
+
+#: wt-status.c:228
+#, c-format
+msgid "  (use \"git %s <file>...\" to include in what will be committed)"
+msgstr ""
+"  (använd \"git %s <fil>...\" för att ta med i vad som skall checkas in)"
+
+#: wt-status.c:245
+msgid "bug"
+msgstr "programfel"
+
+#: wt-status.c:250
+msgid "both deleted:"
+msgstr "borttaget av bägge:"
+
+#: wt-status.c:251
+msgid "added by us:"
+msgstr "tillagt av oss:"
+
+#: wt-status.c:252
+msgid "deleted by them:"
+msgstr "borttaget av dem:"
+
+#: wt-status.c:253
+msgid "added by them:"
+msgstr "tillagt av dem:"
+
+#: wt-status.c:254
+msgid "deleted by us:"
+msgstr "borttaget av oss:"
+
+#: wt-status.c:255
+msgid "both added:"
+msgstr "tillagt av bägge:"
+
+#: wt-status.c:256
+msgid "both modified:"
+msgstr "ändrat av bägge:"
+
+#: wt-status.c:286
+msgid "new commits, "
+msgstr "nya incheckningar, "
+
+#: wt-status.c:288
+msgid "modified content, "
+msgstr "ändrat innehåll, "
+
+#: wt-status.c:290
+msgid "untracked content, "
+msgstr "ospårat innehåll, "
+
+#: wt-status.c:304
+#, c-format
+msgid "new file:   %s"
+msgstr "ny fil:     %s"
+
+#: wt-status.c:307
+#, c-format
+msgid "copied:     %s -> %s"
+msgstr "kopierad:   %s -> %s"
+
+#: wt-status.c:310
+#, c-format
+msgid "deleted:    %s"
+msgstr "borttagen:  %s"
+
+#: wt-status.c:313
+#, c-format
+msgid "modified:   %s"
+msgstr "ändrad:     %s"
+
+#: wt-status.c:316
+#, c-format
+msgid "renamed:    %s -> %s"
+msgstr "namnbyte:   %s -> %s"
+
+#: wt-status.c:319
+#, c-format
+msgid "typechange: %s"
+msgstr "typbyte:    %s"
+
+#: wt-status.c:322
+#, c-format
+msgid "unknown:    %s"
+msgstr "okänd:      %s"
+
+#: wt-status.c:325
+#, c-format
+msgid "unmerged:   %s"
+msgstr "osammansl.: %s"
+
+#: wt-status.c:328
+#, c-format
+msgid "bug: unhandled diff status %c"
+msgstr "programfel: diff-status %c ej hanterad"
+
+#: wt-status.c:786
+msgid "You have unmerged paths."
+msgstr "Du har ej sammanslagna sökvägar."
+
+#: wt-status.c:789 wt-status.c:913
+msgid "  (fix conflicts and run \"git commit\")"
+msgstr "  (rätta konflikter och kör \"git commit\")"
+
+#: wt-status.c:792
+msgid "All conflicts fixed but you are still merging."
+msgstr "Alla konflikter har rättats men du är fortfarande i en sammanslagning."
+
+#: wt-status.c:795
+msgid "  (use \"git commit\" to conclude merge)"
+msgstr "  (använd \"git commit\" för att slutföra sammanslagningen)"
+
+#: wt-status.c:805
+msgid "You are in the middle of an am session."
+msgstr "Du är i mitten av en körning av \"git am\"."
+
+#: wt-status.c:808
+msgid "The current patch is empty."
+msgstr "Aktuell patch är tom."
+
+#: wt-status.c:812
+msgid "  (fix conflicts and then run \"git am --resolved\")"
+msgstr "  (rätta konflikter och kör sedan \"git am --resolved\")"
+
+#: wt-status.c:814
+msgid "  (use \"git am --skip\" to skip this patch)"
+msgstr "  (använd \"git am --skip\" för att hoppa över patchen)"
+
+#: wt-status.c:816
+msgid "  (use \"git am --abort\" to restore the original branch)"
+msgstr "  (använd \"git am --abort\" för att återställa ursprungsgrenen)"
+
+#: wt-status.c:874 wt-status.c:884
+msgid "You are currently rebasing."
+msgstr "Du håller på med en ombasering."
+
+#: wt-status.c:877
+msgid "  (fix conflicts and then run \"git rebase --continue\")"
+msgstr "  (rätta konflikter och kör sedan \"git rebase --continue\")"
+
+#: wt-status.c:879
+msgid "  (use \"git rebase --skip\" to skip this patch)"
+msgstr "  (använd \"git rebase --skip\" för att hoppa över patchen)"
+
+#: wt-status.c:881
+msgid "  (use \"git rebase --abort\" to check out the original branch)"
+msgstr "  (använd \"git rebase --abort\" för att checka ut ursprungsgrenen)"
+
+#: wt-status.c:887
+msgid "  (all conflicts fixed: run \"git rebase --continue\")"
+msgstr "  (alla konflikter rättade: kör \"git rebase --continue\")"
+
+#: wt-status.c:889
+msgid "You are currently splitting a commit during a rebase."
+msgstr "Du håller på att dela upp en incheckning i en ombasering."
+
+#: wt-status.c:892
+msgid "  (Once your working directory is clean, run \"git rebase --continue\")"
+msgstr "  (Så fort din arbetskatalog är ren, kör \"git rebase --continue\")"
+
+#: wt-status.c:894
+msgid "You are currently editing a commit during a rebase."
+msgstr "Du håller på att redigera en incheckning under en ombasering."
+
+#: wt-status.c:897
+msgid "  (use \"git commit --amend\" to amend the current commit)"
+msgstr ""
+"  (använd \"git commit --amend\" för att lägga till på aktuell incheckning)"
+
+#: wt-status.c:899
+msgid ""
+"  (use \"git rebase --continue\" once you are satisfied with your changes)"
+msgstr "  (använd \"git rebase --continue\" när du är nöjd med dina ändringar)"
+
+#: wt-status.c:909
+msgid "You are currently cherry-picking."
+msgstr "Du håller på med en \"cherry-pick\"."
+
+#: wt-status.c:916
+msgid "  (all conflicts fixed: run \"git commit\")"
+msgstr "  (alla konflikter har rättats: kör \"git commit\")"
+
+#: wt-status.c:925
+msgid "You are currently bisecting."
+msgstr "Du håller på med en \"bisect\"."
+
+#: wt-status.c:928
+msgid "  (use \"git bisect reset\" to get back to the original branch)"
+msgstr ""
+"  (använd \"git bisect reset\" för att komma tillbaka till ursprungsgrenen)"
+
+#: wt-status.c:979
+msgid "On branch "
+msgstr "På grenen "
+
+#: wt-status.c:986
+msgid "Not currently on any branch."
+msgstr "Inte på någon gren för närvarande."
+
+#: wt-status.c:998
+msgid "Initial commit"
+msgstr "Första incheckning"
+
+#: wt-status.c:1012
+msgid "Untracked"
+msgstr "Ospårad"
+
+#: wt-status.c:1014
+msgid "Ignored"
+msgstr "Ignorerad"
+
+# %s är nästa sträng eller tom.
+#: wt-status.c:1016
+#, c-format
+msgid "Untracked files not listed%s"
+msgstr "Ospårade filer visas ej%s"
+
+#: wt-status.c:1018
+msgid " (use -u option to show untracked files)"
+msgstr " (använd flaggan -u för att visa ospårade filer)"
+
+#: wt-status.c:1024
+msgid "No changes"
+msgstr "Inga ändringar"
+
+#: wt-status.c:1028
+#, c-format
+msgid "no changes added to commit%s\n"
+msgstr "inga ändringar att checka in%s\n"
+
+#: wt-status.c:1030
+msgid " (use \"git add\" and/or \"git commit -a\")"
+msgstr " (använd \"git add\" och/eller \"git commit -a\")"
+
+#: wt-status.c:1032
+#, c-format
+msgid "nothing added to commit but untracked files present%s\n"
+msgstr "inget köat för incheckning, men ospårade filer finns%s\n"
+
+#: wt-status.c:1034
+msgid " (use \"git add\" to track)"
+msgstr " (använd \"git add\" för att spåra)"
+
+#: wt-status.c:1036 wt-status.c:1039 wt-status.c:1042
+#, c-format
+msgid "nothing to commit%s\n"
+msgstr "inget att checka in%s\n"
+
+#: wt-status.c:1037
+msgid " (create/copy files and use \"git add\" to track)"
+msgstr " (skapa/kopiera filer och använd \"git add\" för att spåra)"
+
+#: wt-status.c:1040
+msgid " (use -u to show untracked files)"
+msgstr " (använd -u för att visa ospårade filer)"
+
+#: wt-status.c:1043
+msgid " (working directory clean)"
+msgstr " (arbetskatalogen ren)"
+
+#: wt-status.c:1151
+msgid "HEAD (no branch)"
+msgstr "HEAD (ingen gren)"
+
+#: wt-status.c:1157
+msgid "Initial commit on "
+msgstr "Första incheckning på "
+
+#: wt-status.c:1172
+msgid "behind "
+msgstr "efter "
+
+#: wt-status.c:1175 wt-status.c:1178
+msgid "ahead "
+msgstr "före "
+
+#: wt-status.c:1180
+msgid ", behind "
+msgstr ", efter "
+
+#: builtin/add.c:62
+#, c-format
+msgid "unexpected diff status %c"
+msgstr "diff-status %c förväntades inte"
+
+#: builtin/add.c:67 builtin/commit.c:226
+msgid "updating files failed"
+msgstr "misslyckades uppdatera filer"
+
+#: builtin/add.c:77
+#, c-format
+msgid "remove '%s'\n"
+msgstr "ta bort \"%s\"\n"
+
+#: builtin/add.c:176
+#, c-format
+msgid "Path '%s' is in submodule '%.*s'"
+msgstr "Sökvägen \"%s\" är i undermodulen \"%.*s\""
+
+#: builtin/add.c:192
+msgid "Unstaged changes after refreshing the index:"
+msgstr "Ospårade ändringar efter att ha uppdaterat indexet:"
+
+#: builtin/add.c:195 builtin/add.c:459 builtin/rm.c:186
+#, c-format
+msgid "pathspec '%s' did not match any files"
+msgstr "sökvägsangivelsen \"%s\" motsvarade inte några filer"
+
+#: builtin/add.c:209
+#, c-format
+msgid "'%s' is beyond a symbolic link"
+msgstr "\"%s\" är på andra sidan av en symbolisk länk"
+
+#: builtin/add.c:276
+msgid "Could not read the index"
+msgstr "Kunde inte läsa indexet"
+
+#: builtin/add.c:286
+#, c-format
+msgid "Could not open '%s' for writing."
+msgstr "Kunde inte öppna \"%s\" för skrivning"
+
+#: builtin/add.c:290
+msgid "Could not write patch"
+msgstr "Kunde inte skriva patch"
+
+#: builtin/add.c:295
+#, c-format
+msgid "Could not stat '%s'"
+msgstr "Kunde inte ta status på \"%s\""
+
+#: builtin/add.c:297
+msgid "Empty patch. Aborted."
+msgstr "Tom patch. Avbryter."
+
+#: builtin/add.c:303
+#, c-format
+msgid "Could not apply '%s'"
+msgstr "Kunde inte tillämpa \"%s\""
+
+#: builtin/add.c:312
+msgid "The following paths are ignored by one of your .gitignore files:\n"
+msgstr "Följande sökvägar ignoreras av en av dina .gitignore-filer:\n"
+
+#: builtin/add.c:352
+#, c-format
+msgid "Use -f if you really want to add them.\n"
+msgstr "Använd -f om du verkligen vill lägga till dem.\n"
+
+#: builtin/add.c:353
+msgid "no files added"
+msgstr "inga filer har lagts till"
+
+#: builtin/add.c:359
+msgid "adding files failed"
+msgstr "misslyckades lägga till filer"
+
+#: builtin/add.c:391
+msgid "-A and -u are mutually incompatible"
+msgstr "-A och -u är ömsesidigt inkompatibla"
+
+#: builtin/add.c:393
+msgid "Option --ignore-missing can only be used together with --dry-run"
+msgstr "Flaggan --ignore-missing kan endast användas tillsammans med --dry-run"
+
+#: builtin/add.c:413
+#, c-format
+msgid "Nothing specified, nothing added.\n"
+msgstr "Inget angivet, inget tillagt.\n"
+
+#: builtin/add.c:414
+#, c-format
+msgid "Maybe you wanted to say 'git add .'?\n"
+msgstr "Kanske menade du att skriva \"git add .\"?\n"
+
+#: builtin/add.c:420 builtin/clean.c:95 builtin/commit.c:286 builtin/mv.c:82
+#: builtin/rm.c:162
+msgid "index file corrupt"
+msgstr "indexfilen trasig"
+
+#: builtin/add.c:480 builtin/apply.c:4108 builtin/mv.c:229 builtin/rm.c:260
+msgid "Unable to write new index file"
+msgstr "Kunde inte skriva ny indexfil"
+
+#: builtin/apply.c:53
+msgid "git apply [options] [<patch>...]"
+msgstr "git apply [flaggor] [<patch>...]"
+
+#: builtin/apply.c:106
+#, c-format
+msgid "unrecognized whitespace option '%s'"
+msgstr "okänt alternativ för whitespace: \"%s\""
+
+#: builtin/apply.c:121
+#, c-format
+msgid "unrecognized whitespace ignore option '%s'"
+msgstr "okänt alternativ för ignore-whitespace: \"%s\""
+
+#: builtin/apply.c:815
+#, c-format
+msgid "Cannot prepare timestamp regexp %s"
+msgstr "Kan inte förbereda reguljärt uttryck för tidsstämpeln %s"
+
+#: builtin/apply.c:824
+#, c-format
+msgid "regexec returned %d for input: %s"
+msgstr "regexec returnerade %d för indata: %s"
+
+#: builtin/apply.c:905
+#, c-format
+msgid "unable to find filename in patch at line %d"
+msgstr "kan inte hitta filnamn i patchen på rad %d"
+
+#: builtin/apply.c:937
+#, c-format
+msgid "git apply: bad git-diff - expected /dev/null, got %s on line %d"
+msgstr "git apply: dålig git-diff - förväntade /dev/null, fick %s på rad %d"
+
+#: builtin/apply.c:941
+#, c-format
+msgid "git apply: bad git-diff - inconsistent new filename on line %d"
+msgstr "git apply: dålig git-diff - motsägande nytt filnamn på rad %d"
+
+#: builtin/apply.c:942
+#, c-format
+msgid "git apply: bad git-diff - inconsistent old filename on line %d"
+msgstr "git apply: dålig git-diff - motsägande gammalt filnamn på rad %d"
+
+#: builtin/apply.c:949
+#, c-format
+msgid "git apply: bad git-diff - expected /dev/null on line %d"
+msgstr "git apply: dålig git-diff - förväntade /dev/null på rad %d"
+
+#: builtin/apply.c:1394
+#, c-format
+msgid "recount: unexpected line: %.*s"
+msgstr "recount: förväntade rad: %.*s"
+
+#: builtin/apply.c:1451
+#, c-format
+msgid "patch fragment without header at line %d: %.*s"
+msgstr "patch-fragment utan huvud på rad %d: %.*s"
+
+#: builtin/apply.c:1468
+#, c-format
+msgid ""
+"git diff header lacks filename information when removing %d leading pathname "
+"component (line %d)"
+msgid_plural ""
+"git diff header lacks filename information when removing %d leading pathname "
+"components (line %d)"
+msgstr[0] ""
+"git-diff-huvudet saknar filnamnsinformation när %d ledande sökvägskomponent\n"
+"tas bort (rad %d)"
+msgstr[1] ""
+"git-diff-huvudet saknar filnamnsinformation när %d ledande "
+"sökvägskomponenter\n"
+"tas bort (rad %d)"
+
+#: builtin/apply.c:1628
+msgid "new file depends on old contents"
+msgstr "ny fil beror på gammalt innehåll"
+
+#: builtin/apply.c:1630
+msgid "deleted file still has contents"
+msgstr "borttagen fil har fortfarande innehåll"
+
+#: builtin/apply.c:1656
+#, c-format
+msgid "corrupt patch at line %d"
+msgstr "trasig patch på rad %d"
+
+#: builtin/apply.c:1692
+#, c-format
+msgid "new file %s depends on old contents"
+msgstr "nya filen %s beror på gammalt innehåll"
+
+#: builtin/apply.c:1694
+#, c-format
+msgid "deleted file %s still has contents"
+msgstr "borttagna filen %s har fortfarande innehåll"
+
+#: builtin/apply.c:1697
+#, c-format
+msgid "** warning: file %s becomes empty but is not deleted"
+msgstr "** varning: filen %s blir tom men har inte tagits bort"
+
+#: builtin/apply.c:1843
+#, c-format
+msgid "corrupt binary patch at line %d: %.*s"
+msgstr "trasig binärpatch på rad %d: %.*s"
+
+#. there has to be one hunk (forward hunk)
+#: builtin/apply.c:1872
+#, c-format
+msgid "unrecognized binary patch at line %d"
+msgstr "binärpatchen på rad %d känns inte igen"
+
+#: builtin/apply.c:1958
+#, c-format
+msgid "patch with only garbage at line %d"
+msgstr "patch med bara skräp på rad %d"
+
+#: builtin/apply.c:2048
+#, c-format
+msgid "unable to read symlink %s"
+msgstr "kunde inte läsa symboliska länken %s"
+
+#: builtin/apply.c:2052
+#, c-format
+msgid "unable to open or read %s"
+msgstr "kunde inte öppna eller läsa %s"
+
+#: builtin/apply.c:2123
+msgid "oops"
+msgstr "hoppsan"
+
+#: builtin/apply.c:2645
+#, c-format
+msgid "invalid start of line: '%c'"
+msgstr "felaktig inledning på rad: \"%c\""
+
+#: builtin/apply.c:2763
+#, c-format
+msgid "Hunk #%d succeeded at %d (offset %d line)."
+msgid_plural "Hunk #%d succeeded at %d (offset %d lines)."
+msgstr[0] "Stycke %d lyckades på %d (offset %d rad)."
+msgstr[1] "Stycke %d lyckades på %d (offset %d rader)."
+
+#: builtin/apply.c:2775
+#, c-format
+msgid "Context reduced to (%ld/%ld) to apply fragment at %d"
+msgstr "Sammanhang reducerat till (%ld/%ld) för att tillämpa fragment vid %d"
+
+#: builtin/apply.c:2781
+#, c-format
+msgid ""
+"while searching for:\n"
+"%.*s"
+msgstr ""
+"vid sökning efter:\n"
+"%.*s"
+
+#: builtin/apply.c:2800
+#, c-format
+msgid "missing binary patch data for '%s'"
+msgstr "saknar binära patchdata för \"%s\""
+
+#: builtin/apply.c:2903
+#, c-format
+msgid "binary patch does not apply to '%s'"
+msgstr "binärpatchen kan inte tillämpas på \"%s\""
+
+#: builtin/apply.c:2909
+#, c-format
+msgid "binary patch to '%s' creates incorrect result (expecting %s, got %s)"
+msgstr "binärpatchen på \"%s\" ger felaktigt resultat (förväntade %s, fick %s)"
+
+#: builtin/apply.c:2930
+#, c-format
+msgid "patch failed: %s:%ld"
+msgstr "patch misslyckades: %s:%ld"
+
+#: builtin/apply.c:3045
+#, c-format
+msgid "patch %s has been renamed/deleted"
+msgstr "patchen %s har ändrat namn/tagits bort"
+
+#: builtin/apply.c:3052 builtin/apply.c:3069
+#, c-format
+msgid "read of %s failed"
+msgstr "misslyckades läsa %s"
+
+#: builtin/apply.c:3084
+msgid "removal patch leaves file contents"
+msgstr "patch för borttagning lämnar kvar filinnehåll"
+
+#: builtin/apply.c:3105
+#, c-format
+msgid "%s: already exists in working directory"
+msgstr "%s: finns redan i arbetskatalogen"
+
+#: builtin/apply.c:3143
+#, c-format
+msgid "%s: has been deleted/renamed"
+msgstr "%s: har tagits bort/ändrat namn"
+
+#: builtin/apply.c:3148 builtin/apply.c:3179
+#, c-format
+msgid "%s: %s"
+msgstr "%s: %s"
+
+#: builtin/apply.c:3159
+#, c-format
+msgid "%s: does not exist in index"
+msgstr "%s: finns inte i indexet"
+
+#: builtin/apply.c:3173
+#, c-format
+msgid "%s: does not match index"
+msgstr "%s: motsvarar inte indexet"
+
+#: builtin/apply.c:3190
+#, c-format
+msgid "%s: wrong type"
+msgstr "%s: fel typ"
+
+#: builtin/apply.c:3192
+#, c-format
+msgid "%s has type %o, expected %o"
+msgstr "%s har typen %o, förväntade %o"
+
+#: builtin/apply.c:3247
+#, c-format
+msgid "%s: already exists in index"
+msgstr "%s: finns redan i indexet"
+
+#: builtin/apply.c:3267
+#, c-format
+msgid "new mode (%o) of %s does not match old mode (%o)"
+msgstr "nytt läge (%o) för %s motsvarar inte gammalt läge (%o)"
+
+#: builtin/apply.c:3272
+#, c-format
+msgid "new mode (%o) of %s does not match old mode (%o) of %s"
+msgstr "nytt läge (%o) för %s motsvarar inte gammalt läge (%o) för %s"
+
+#: builtin/apply.c:3280
+#, c-format
+msgid "%s: patch does not apply"
+msgstr "%s: patchen kan inte tillämpas"
+
+#: builtin/apply.c:3293
+#, c-format
+msgid "Checking patch %s..."
+msgstr "Kontrollerar patchen %s..."
+
+#: builtin/apply.c:3348 builtin/checkout.c:212 builtin/reset.c:158
+#, c-format
+msgid "make_cache_entry failed for path '%s'"
+msgstr "make_cache_entry misslyckades för sökvägen \"%s\""
+
+#: builtin/apply.c:3491
+#, c-format
+msgid "unable to remove %s from index"
+msgstr "kan inte ta bort %s från indexet"
+
+#: builtin/apply.c:3518
+#, c-format
+msgid "corrupt patch for subproject %s"
+msgstr "trasig patch för underprojektet %s"
+
+#: builtin/apply.c:3522
+#, c-format
+msgid "unable to stat newly created file '%s'"
+msgstr "kan inte ta status på nyligen skapade filen \"%s\""
+
+#: builtin/apply.c:3527
+#, c-format
+msgid "unable to create backing store for newly created file %s"
+msgstr "kan inte skapa säkerhetsminne för nyligen skapade filen %s"
+
+#: builtin/apply.c:3530
+#, c-format
+msgid "unable to add cache entry for %s"
+msgstr "kan inte lägga till cachepost för %s"
+
+#: builtin/apply.c:3563
+#, c-format
+msgid "closing file '%s'"
+msgstr "stänger filen \"%s\""
+
+#: builtin/apply.c:3612
+#, c-format
+msgid "unable to write file '%s' mode %o"
+msgstr "kan inte skriva filen \"%s\" läge %o"
+
+#: builtin/apply.c:3668
+#, c-format
+msgid "Applied patch %s cleanly."
+msgstr "Tillämpade patchen %s rent."
+
+#: builtin/apply.c:3676
+msgid "internal error"
+msgstr "internt fel"
+
+#. Say this even without --verbose
+#: builtin/apply.c:3679
+#, c-format
+msgid "Applying patch %%s with %d reject..."
+msgid_plural "Applying patch %%s with %d rejects..."
+msgstr[0] "Tillämpade patchen %%s med %d refuserad..."
+msgstr[1] "Tillämpade patchen %%s med %d refuserade..."
+
+#: builtin/apply.c:3689
+#, c-format
+msgid "truncating .rej filename to %.*s.rej"
+msgstr "trunkerar .rej-filnamnet till %.*s.rej"
+
+#: builtin/apply.c:3710
+#, c-format
+msgid "Hunk #%d applied cleanly."
+msgstr "Stycke %d tillämpades rent."
+
+#: builtin/apply.c:3713
+#, c-format
+msgid "Rejected hunk #%d."
+msgstr "Refuserar stycke %d."
+
+#: builtin/apply.c:3844
+msgid "unrecognized input"
+msgstr "indata känns inte igen"
+
+#: builtin/apply.c:3855
+msgid "unable to read index file"
+msgstr "kan inte läsa indexfilen"
+
+#: builtin/apply.c:3970 builtin/apply.c:3973
+msgid "path"
+msgstr "sökväg"
+
+#: builtin/apply.c:3971
+msgid "don't apply changes matching the given path"
+msgstr "tillämpa inte ändringar som motsvarar given sökväg"
+
+#: builtin/apply.c:3974
+msgid "apply changes matching the given path"
+msgstr "tillämpa ändringar som motsvarar given sökväg"
+
+#: builtin/apply.c:3976
+msgid "num"
+msgstr "antal"
+
+#: builtin/apply.c:3977
+msgid "remove <num> leading slashes from traditional diff paths"
+msgstr "ta bort <antal> inledande snedstreck från traditionella diff-sökvägar"
+
+#: builtin/apply.c:3980
+msgid "ignore additions made by the patch"
+msgstr "ignorera tillägg gjorda av patchen"
+
+#: builtin/apply.c:3982
+msgid "instead of applying the patch, output diffstat for the input"
+msgstr "istället för att tillämpa patchen, skriv ut diffstat för indata"
+
+#: builtin/apply.c:3986
+msgid "shows number of added and deleted lines in decimal notation"
+msgstr "visar antal tillagda och borttagna rader decimalt"
+
+#: builtin/apply.c:3988
+msgid "instead of applying the patch, output a summary for the input"
+msgstr "istället för att tillämpa patchen, skriv ut en summering av indata"
+
+#: builtin/apply.c:3990
+msgid "instead of applying the patch, see if the patch is applicable"
+msgstr "istället för att tillämpa patchen, se om patchen kan tillämpas"
+
+#: builtin/apply.c:3992
+msgid "make sure the patch is applicable to the current index"
+msgstr "se till att patchen kan tillämpas på aktuellt index"
+
+#: builtin/apply.c:3994
+msgid "apply a patch without touching the working tree"
+msgstr "tillämpa en patch utan att röra arbetskatalogen"
+
+#: builtin/apply.c:3996
+msgid "also apply the patch (use with --stat/--summary/--check)"
+msgstr "tillämpa också patchen (använd med --stat/--summary/--check)"
+
+#: builtin/apply.c:3998
+msgid "build a temporary index based on embedded index information"
+msgstr "bygg ett temporärt index baserat på inbyggd indexinformation"
+
+#: builtin/apply.c:4000
+msgid "paths are separated with NUL character"
+msgstr "sökvägar avdelas med NUL-tecken"
+
+#: builtin/apply.c:4003
+msgid "ensure at least <n> lines of context match"
+msgstr "se till att åtminstone <n> rader sammanhang är lika"
+
+#: builtin/apply.c:4004
+msgid "action"
+msgstr "åtgärd"
+
+#: builtin/apply.c:4005
+msgid "detect new or modified lines that have whitespace errors"
+msgstr "detektera nya eller ändrade rader som har fel i blanktecken"
+
+#: builtin/apply.c:4008 builtin/apply.c:4011
+msgid "ignore changes in whitespace when finding context"
+msgstr "ignorera ändringar i blanktecken för sammanhang"
+
+#: builtin/apply.c:4014
+msgid "apply the patch in reverse"
+msgstr "tillämpa patchen baklänges"
+
+#: builtin/apply.c:4016
+msgid "don't expect at least one line of context"
+msgstr "förvänta inte minst en rad sammanhang"
+
+#: builtin/apply.c:4018
+msgid "leave the rejected hunks in corresponding *.rej files"
+msgstr "lämna refuserade stycken i motsvarande *.rej-filer"
+
+#: builtin/apply.c:4020
+msgid "allow overlapping hunks"
+msgstr "tillåt överlappande stycken"
+
+#: builtin/apply.c:4021
+msgid "be verbose"
+msgstr "var pratsam"
+
+#: builtin/apply.c:4023
+msgid "tolerate incorrectly detected missing new-line at the end of file"
+msgstr "tolerera felaktigt detekterade saknade nyradstecken vid filslut"
+
+#: builtin/apply.c:4026
+msgid "do not trust the line counts in the hunk headers"
+msgstr "lite inte på antalet linjer i styckehuvuden"
+
+#: builtin/apply.c:4028
+msgid "root"
+msgstr "rot"
+
+#: builtin/apply.c:4029
+msgid "prepend <root> to all filenames"
+msgstr "lägg till <rot> i alla filnamn"
+
+#: builtin/apply.c:4050
+msgid "--index outside a repository"
+msgstr "--index utanför arkiv"
+
+#: builtin/apply.c:4053
+msgid "--cached outside a repository"
+msgstr "--cached utanför arkiv"
+
+#: builtin/apply.c:4069
+#, c-format
+msgid "can't open patch '%s'"
+msgstr "kan inte öppna patchen \"%s\""
+
+#: builtin/apply.c:4083
+#, c-format
+msgid "squelched %d whitespace error"
+msgid_plural "squelched %d whitespace errors"
+msgstr[0] "undertryckte %d fel i blanksteg"
+msgstr[1] "undertryckte %d fel i blanksteg"
+
+#: builtin/apply.c:4089 builtin/apply.c:4099
+#, c-format
+msgid "%d line adds whitespace errors."
+msgid_plural "%d lines add whitespace errors."
+msgstr[0] "%d rad lägger till fel i blanksteg."
+msgstr[1] "%d rader lägger till fel i blanksteg."
+
+#: builtin/archive.c:17
+#, c-format
+msgid "could not create archive file '%s'"
+msgstr "Kunde inte skapa arkivfilen \"%s\""
+
+#: builtin/archive.c:20
+msgid "could not redirect output"
+msgstr "kunde inte omdirigera utdata"
+
+#: builtin/archive.c:37
+msgid "git archive: Remote with no URL"
+msgstr "git archive: Fjärr utan URL"
+
+#: builtin/archive.c:58
+msgid "git archive: expected ACK/NAK, got EOF"
+msgstr "git archive: förväntade ACK/NAK, fick EOF"
+
+#: builtin/archive.c:63
+#, c-format
+msgid "git archive: NACK %s"
+msgstr "git archive: NACK %s"
+
+#: builtin/archive.c:65
+#, c-format
+msgid "remote error: %s"
+msgstr "fjärrfel: %s"
+
+#: builtin/archive.c:66
+msgid "git archive: protocol error"
+msgstr "git archive: protokollfel"
+
+#: builtin/archive.c:71
+msgid "git archive: expected a flush"
+msgstr "git archive: förväntade en tömning (flush)"
+
+#: builtin/branch.c:144
+#, c-format
+msgid ""
+"deleting branch '%s' that has been merged to\n"
+"         '%s', but not yet merged to HEAD."
+msgstr ""
+"tar bort grenen \"%s\" som har slagits ihop med\n"
+"         \"%s\", men ännu inte slagits ihop med HEAD."
+
+#: builtin/branch.c:148
+#, c-format
+msgid ""
+"not deleting branch '%s' that is not yet merged to\n"
+"         '%s', even though it is merged to HEAD."
+msgstr ""
+"tar inte bort grenen \"%s\" som inte har slagits ihop med\n"
+"         \"%s\", trots att den har slagits ihop med HEAD."
+
+#: builtin/branch.c:180
+msgid "cannot use -a with -d"
+msgstr "kan inte ange -a med -d"
+
+#: builtin/branch.c:186
+msgid "Couldn't look up commit object for HEAD"
+msgstr "Kunde inte slå upp incheckningsobjekt för HEAD"
+
+#: builtin/branch.c:191
+#, c-format
+msgid "Cannot delete the branch '%s' which you are currently on."
+msgstr "Kan inte ta bort grenen \"%s\" som du befinner dig på för närvarande."
+
+#: builtin/branch.c:202
+#, c-format
+msgid "remote branch '%s' not found."
+msgstr "fjärrgrenen \"%s\" hittades inte."
+
+#: builtin/branch.c:203
+#, c-format
+msgid "branch '%s' not found."
+msgstr "grenen \"%s\" hittades inte."
+
+#: builtin/branch.c:210
+#, c-format
+msgid "Couldn't look up commit object for '%s'"
+msgstr "Kunde inte slå upp incheckningsobjekt för \"%s\""
+
+#: builtin/branch.c:216
+#, c-format
+msgid ""
+"The branch '%s' is not fully merged.\n"
+"If you are sure you want to delete it, run 'git branch -D %s'."
+msgstr ""
+"Grenen \"%s\" har inte slagits samman i sin helhet.\n"
+"Om du är säker på att du vill ta bort den, kör \"git branch -D %s\"."
+
+#: builtin/branch.c:225
+#, c-format
+msgid "Error deleting remote branch '%s'"
+msgstr "Fel vid borttagning av fjärrgrenen \"%s\""
+
+#: builtin/branch.c:226
+#, c-format
+msgid "Error deleting branch '%s'"
+msgstr "Fel vid borttagning av grenen \"%s\""
+
+#: builtin/branch.c:233
+#, c-format
+msgid "Deleted remote branch %s (was %s).\n"
+msgstr "Tog bort fjärrgrenen %s (var %s).\n"
+
+#: builtin/branch.c:234
+#, c-format
+msgid "Deleted branch %s (was %s).\n"
+msgstr "Tog bort grenen %s (var %s).\n"
+
+#: builtin/branch.c:239
+msgid "Update of config-file failed"
+msgstr "Misslyckades uppdatera konfigurationsfil"
+
+#: builtin/branch.c:337
+#, c-format
+msgid "branch '%s' does not point at a commit"
+msgstr "grenen \"%s\" pekar inte på en incheckning"
+
+#: builtin/branch.c:409
+#, c-format
+msgid "[%s: behind %d]"
+msgstr "[%s: bakom %d] "
+
+#: builtin/branch.c:411
+#, c-format
+msgid "[behind %d]"
+msgstr "[bakom %d] "
+
+#: builtin/branch.c:415
+#, c-format
+msgid "[%s: ahead %d]"
+msgstr "[%s: före %d] "
+
+#: builtin/branch.c:417
+#, c-format
+msgid "[ahead %d]"
+msgstr "[före %d] "
+
+#: builtin/branch.c:420
+#, c-format
+msgid "[%s: ahead %d, behind %d]"
+msgstr "[%s: före %d, bakom %d] "
+
+#: builtin/branch.c:423
+#, c-format
+msgid "[ahead %d, behind %d]"
+msgstr "[före %d, bakom %d] "
+
+#: builtin/branch.c:535
+msgid "(no branch)"
+msgstr "(ingen gren)"
+
+#: builtin/branch.c:600
+msgid "some refs could not be read"
+msgstr "vissa referenser kunde inte läsas"
+
+#: builtin/branch.c:613
+msgid "cannot rename the current branch while not on any."
+msgstr ""
+"kunde inte byta namn på aktuell gren när du inte befinner dig på någon."
+
+#: builtin/branch.c:623
+#, c-format
+msgid "Invalid branch name: '%s'"
+msgstr "Felaktigt namn på gren: \"%s\""
+
+#: builtin/branch.c:638
+msgid "Branch rename failed"
+msgstr "Misslyckades byta namn på gren"
+
+#: builtin/branch.c:642
+#, c-format
+msgid "Renamed a misnamed branch '%s' away"
+msgstr "Bytte bort namn på en felaktigt namngiven gren \"%s\""
+
+#: builtin/branch.c:646
+#, c-format
+msgid "Branch renamed to %s, but HEAD is not updated!"
+msgstr "Grenen namnbytt till %s, men HEAD har inte uppdaterats!"
+
+#: builtin/branch.c:653
+msgid "Branch is renamed, but update of config-file failed"
+msgstr "Grenen namnbytt, men misslyckades uppdatera konfigurationsfilen"
+
+#: builtin/branch.c:668
+#, c-format
+msgid "malformed object name %s"
+msgstr "felformat objektnamn %s"
+
+#: builtin/branch.c:692
+#, c-format
+msgid "could not write branch description template: %s"
+msgstr "kunde inte skriva grenbeskrivningsmall: %s"
+
+#: builtin/branch.c:783
+msgid "Failed to resolve HEAD as a valid ref."
+msgstr "Misslyckades slå upp HEAD som giltig referens"
+
+#: builtin/branch.c:788 builtin/clone.c:558
+msgid "HEAD not found below refs/heads!"
+msgstr "HEAD hittades inte under refs/heads!"
+
+#: builtin/branch.c:808
+msgid "--column and --verbose are incompatible"
+msgstr "--column och --verbose är inkompatibla"
+
+#: builtin/branch.c:857
+msgid "-a and -r options to 'git branch' do not make sense with a branch name"
+msgstr ""
+"flaggorna -a och -r på \"git branch\" kan inte anges tillsammans med ett "
+"grennamn"
+
+#: builtin/bundle.c:47
+#, c-format
+msgid "%s is okay\n"
+msgstr "%s är okej\n"
+
+#: builtin/bundle.c:56
+msgid "Need a repository to create a bundle."
+msgstr "Behöver ett arkiv för att skapa ett paket (bundle)."
+
+#: builtin/bundle.c:60
+msgid "Need a repository to unbundle."
+msgstr "Behöver ett arkiv för att packa upp ett paket (bundle)."
+
+#: builtin/checkout.c:113 builtin/checkout.c:146
+#, c-format
+msgid "path '%s' does not have our version"
+msgstr "sökvägen \"%s\" har inte vår version"
+
+#: builtin/checkout.c:115 builtin/checkout.c:148
+#, c-format
+msgid "path '%s' does not have their version"
+msgstr "sökvägen \"%s\" har inte deras version"
+
+#: builtin/checkout.c:131
+#, c-format
+msgid "path '%s' does not have all necessary versions"
+msgstr "sökvägen \"%s\" innehåller inte alla nödvändiga versioner"
+
+#: builtin/checkout.c:175
+#, c-format
+msgid "path '%s' does not have necessary versions"
+msgstr "sökvägen \"%s\" innehåller inte nödvändiga versioner"
+
+#: builtin/checkout.c:192
+#, c-format
+msgid "path '%s': cannot merge"
+msgstr "sökväg \"%s\": kan inte slå ihop"
+
+#: builtin/checkout.c:209
+#, c-format
+msgid "Unable to add merge result for '%s'"
+msgstr "Kunde inte lägga till sammanslagningsresultat för \"%s\""
+
+#: builtin/checkout.c:234 builtin/checkout.c:392
+msgid "corrupt index file"
+msgstr "indexfilen är trasig"
+
+#: builtin/checkout.c:264 builtin/checkout.c:271
+#, c-format
+msgid "path '%s' is unmerged"
+msgstr "sökvägen \"%s\" har inte slagits ihop"
+
+#: builtin/checkout.c:302 builtin/checkout.c:498 builtin/clone.c:583
+#: builtin/merge.c:812
+msgid "unable to write new index file"
+msgstr "kunde inte skriva ny indexfil"
+
+#: builtin/checkout.c:319 builtin/diff.c:302 builtin/merge.c:408
+msgid "diff_setup_done failed"
+msgstr "diff_setup_done misslyckades"
+
+#: builtin/checkout.c:414
+msgid "you need to resolve your current index first"
+msgstr "du måste lösa ditt befintliga index först"
+
+#: builtin/checkout.c:533
+#, c-format
+msgid "Can not do reflog for '%s'\n"
+msgstr "Kan inte skapa referenslog för \"%s\"\n"
+
+#: builtin/checkout.c:566
+msgid "HEAD is now at"
+msgstr "HEAD är nu på"
+
+#: builtin/checkout.c:573
+#, c-format
+msgid "Reset branch '%s'\n"
+msgstr "Återställ gren \"%s\"\n"
+
+#: builtin/checkout.c:576
+#, c-format
+msgid "Already on '%s'\n"
+msgstr "Redan på \"%s\"\n"
+
+#: builtin/checkout.c:580
+#, c-format
+msgid "Switched to and reset branch '%s'\n"
+msgstr "Växlade till och nollställde grenen \"%s\"\n"
+
+#: builtin/checkout.c:582
+#, c-format
+msgid "Switched to a new branch '%s'\n"
+msgstr "Växlade till en ny gren \"%s\"\n"
+
+#: builtin/checkout.c:584
+#, c-format
+msgid "Switched to branch '%s'\n"
+msgstr "Växlade till grenen \"%s\"\n"
+
+#: builtin/checkout.c:640
+#, c-format
+msgid " ... and %d more.\n"
+msgstr " ... och %d till.\n"
+
+#. The singular version
+#: builtin/checkout.c:646
+#, c-format
+msgid ""
+"Warning: you are leaving %d commit behind, not connected to\n"
+"any of your branches:\n"
+"\n"
+"%s\n"
+msgid_plural ""
+"Warning: you are leaving %d commits behind, not connected to\n"
+"any of your branches:\n"
+"\n"
+"%s\n"
+msgstr[0] ""
+"Varning: du lämnar %d incheckning bakom dig som inte är ansluten till\n"
+"någon av dina grenar:\n"
+"\n"
+"%s\n"
+msgstr[1] ""
+"Varning: du lämnar %d incheckningar bakom dig som inte är ansluta till\n"
+"någon av dina grenar:\n"
+"\n"
+"%s\n"
+
+#: builtin/checkout.c:664
+#, c-format
+msgid ""
+"If you want to keep them by creating a new branch, this may be a good time\n"
+"to do so with:\n"
+"\n"
+" git branch new_branch_name %s\n"
+"\n"
+msgstr ""
+"Om du vill behålla dem genom att skapa en ny gren är nu en bra tidpunkt\n"
+"att göra så, med:\n"
+"\n"
+" git branch nytt_grennamn %s\n"
+"\n"
+
+#: builtin/checkout.c:694
+msgid "internal error in revision walk"
+msgstr "internt fel vid genomgång av revisioner (revision walk)"
+
+#: builtin/checkout.c:698
+msgid "Previous HEAD position was"
+msgstr "Tidigare position för HEAD var"
+
+#: builtin/checkout.c:724
+msgid "You are on a branch yet to be born"
+msgstr "Du är på en gren som ännu inte är född"
+
+#. case (1)
+#: builtin/checkout.c:855
+#, c-format
+msgid "invalid reference: %s"
+msgstr "felaktig referens: %s"
+
+#. case (1): want a tree
+#: builtin/checkout.c:894
+#, c-format
+msgid "reference is not a tree: %s"
+msgstr "referensen är inte ett träd: %s"
+
+#: builtin/checkout.c:974
+msgid "-B cannot be used with -b"
+msgstr "-B kan inte användas med -b"
+
+#: builtin/checkout.c:983
+msgid "--patch is incompatible with all other options"
+msgstr "--patch är inkompatibel med alla andra flaggor"
+
+#: builtin/checkout.c:986
+msgid "--detach cannot be used with -b/-B/--orphan"
+msgstr "--detcah kan inte användas med -b/-B/--orphan"
+
+#: builtin/checkout.c:988
+msgid "--detach cannot be used with -t"
+msgstr "--detach kan inte användas med -t"
+
+#: builtin/checkout.c:994
+msgid "--track needs a branch name"
+msgstr "--track behöver ett namn på en gren"
+
+#: builtin/checkout.c:1001
+msgid "Missing branch name; try -b"
+msgstr "Grennamn saknas; försök med -b"
+
+#: builtin/checkout.c:1007
+msgid "--orphan and -b|-B are mutually exclusive"
+msgstr "--orphan och -b|-B kan inte användas samtidigt"
+
+#: builtin/checkout.c:1009
+msgid "--orphan cannot be used with -t"
+msgstr "--orphan kan inte användas med -t"
+
+#: builtin/checkout.c:1019
+msgid "git checkout: -f and -m are incompatible"
+msgstr "git checkout: -f och -m är inkompatibla"
+
+#: builtin/checkout.c:1053
+msgid "invalid path specification"
+msgstr "felaktig sökvägsangivelse"
+
+#: builtin/checkout.c:1061
+#, c-format
+msgid ""
+"git checkout: updating paths is incompatible with switching branches.\n"
+"Did you intend to checkout '%s' which can not be resolved as commit?"
+msgstr ""
+"git checkout: uppdatera sökvägar är inkompatibelt med att växla gren.\n"
+"Ville du checka ut \"%s\" som inte kan lösas som en sammanslaning?"
+
+#: builtin/checkout.c:1063
+msgid "git checkout: updating paths is incompatible with switching branches."
+msgstr "git checkout: uppdatera sökvägar är inkompatibelt med att växla gren."
+
+#: builtin/checkout.c:1068
+msgid "git checkout: --detach does not take a path argument"
+msgstr "git checkout: --detach tar inte en sökväg som argument"
+
+#: builtin/checkout.c:1071
+msgid ""
+"git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
+"checking out of the index."
+msgstr ""
+"git checkout: --ours/--theirs, --force och --merge är inkompatibla när\n"
+"du checkar ut från indexet."
+
+#: builtin/checkout.c:1090
+msgid "Cannot switch branch to a non-commit."
+msgstr "Kan inte växla gren på en icke-incheckning."
+
+#: builtin/checkout.c:1093
+msgid "--ours/--theirs is incompatible with switching branches."
+msgstr "--ours/--theirs är inkompatibla med att byta gren."
+
+#: builtin/clean.c:78
+msgid "-x and -X cannot be used together"
+msgstr "-x och -X kan inte användas samtidigt"
+
+#: builtin/clean.c:82
+msgid ""
+"clean.requireForce set to true and neither -n nor -f given; refusing to clean"
+msgstr ""
+"clean.requireForce satt till true, men varken -n eller -f angavs; vägrar "
+"städa"
+
+#: builtin/clean.c:85
+msgid ""
+"clean.requireForce defaults to true and neither -n nor -f given; refusing to "
+"clean"
+msgstr ""
+"clean.requireForce har standardvärdet true, men varken -n eller -f angavs; "
+"vägrar städa"
+
+#: builtin/clean.c:155 builtin/clean.c:176
+#, c-format
+msgid "Would remove %s\n"
+msgstr "Skulle ta bort %s\n"
+
+#: builtin/clean.c:159 builtin/clean.c:179
+#, c-format
+msgid "Removing %s\n"
+msgstr "Tar bort %s\n"
+
+#: builtin/clean.c:162 builtin/clean.c:182
+#, c-format
+msgid "failed to remove %s"
+msgstr "misslyckades ta bort %s"
+
+#: builtin/clean.c:166
+#, c-format
+msgid "Would not remove %s\n"
+msgstr "Skulle inte ta bort %s\n"
+
+#: builtin/clean.c:168
+#, c-format
+msgid "Not removing %s\n"
+msgstr "Tar inte bort %s\n"
+
+#: builtin/clone.c:243
+#, c-format
+msgid "reference repository '%s' is not a local directory."
+msgstr "referensarkivet \"%s\" är inte en lokal katalog."
+
+#: builtin/clone.c:302
+#, c-format
+msgid "failed to open '%s'"
+msgstr "misslyckades öppna \"%s\""
+
+#: builtin/clone.c:306
+#, c-format
+msgid "failed to create directory '%s'"
+msgstr "misslyckades skapa katalogen \"%s\""
+
+#: builtin/clone.c:308 builtin/diff.c:75
+#, c-format
+msgid "failed to stat '%s'"
+msgstr "misslyckades ta status på \"%s\""
+
+#: builtin/clone.c:310
+#, c-format
+msgid "%s exists and is not a directory"
+msgstr "%s finns och är ingen katalog"
+
+#: builtin/clone.c:324
+#, c-format
+msgid "failed to stat %s\n"
+msgstr "misslyckades ta status på %s\n"
+
+#: builtin/clone.c:341
+#, c-format
+msgid "failed to unlink '%s'"
+msgstr "misslyckades ta bort länken \"%s\""
+
+#: builtin/clone.c:346
+#, c-format
+msgid "failed to create link '%s'"
+msgstr "misslyckades skapa länken \"%s\""
+
+#: builtin/clone.c:350
+#, c-format
+msgid "failed to copy file to '%s'"
+msgstr "misslyckades kopiera filen till \"%s\""
+
+#: builtin/clone.c:373
+#, c-format
+msgid "done.\n"
+msgstr "klart.\n"
+
+#: builtin/clone.c:440
+#, c-format
+msgid "Could not find remote branch %s to clone."
+msgstr "Kunde inte hitta fjärrgrenen %s för att klona."
+
+#: builtin/clone.c:549
+msgid "remote HEAD refers to nonexistent ref, unable to checkout.\n"
+msgstr ""
+"HEAD hos fjärren pekar på en obefintlig referens, kan inte checka ut.\n"
+
+#: builtin/clone.c:639
+msgid "Too many arguments."
+msgstr "För många argument."
+
+#: builtin/clone.c:643
+msgid "You must specify a repository to clone."
+msgstr "Du måste ange ett arkiv att klona."
+
+#: builtin/clone.c:654
+#, c-format
+msgid "--bare and --origin %s options are incompatible."
+msgstr "flaggorna --bare och --origin %s är inkompatibla."
+
+#: builtin/clone.c:668
+#, c-format
+msgid "repository '%s' does not exist"
+msgstr "arkivet \"%s\" finns inte"
+
+#: builtin/clone.c:673
+msgid "--depth is ignored in local clones; use file:// instead."
+msgstr "--depth ignoreras i lokala kloningar; använd file:// istället"
+
+#: builtin/clone.c:683
+#, c-format
+msgid "destination path '%s' already exists and is not an empty directory."
+msgstr "destinationssökvägen \"%s\" finns redan och är inte en tom katalog."
+
+#: builtin/clone.c:693
+#, c-format
+msgid "working tree '%s' already exists."
+msgstr "arbetsträdet \"%s\" finns redan."
+
+#: builtin/clone.c:706 builtin/clone.c:720
+#, c-format
+msgid "could not create leading directories of '%s'"
+msgstr "kunde inte skapa inledande kataloger för \"%s\""
+
+#: builtin/clone.c:709
+#, c-format
+msgid "could not create work tree dir '%s'."
+msgstr "kunde inte skapa arbetskatalogen \"%s\""
+
+#: builtin/clone.c:728
+#, c-format
+msgid "Cloning into bare repository '%s'...\n"
+msgstr "Klonar till ett naket arkiv \"%s\"...\n"
+
+#: builtin/clone.c:730
+#, c-format
+msgid "Cloning into '%s'...\n"
+msgstr "Klonar till \"%s\"...\n"
+
+#: builtin/clone.c:786
+#, c-format
+msgid "Don't know how to clone %s"
+msgstr "Vet inte hur man klonar %s"
+
+#: builtin/clone.c:835
+#, c-format
+msgid "Remote branch %s not found in upstream %s"
+msgstr "Fjärrgrenen %s hittades inte i uppströmsarkivet %s"
+
+#: builtin/clone.c:842
+msgid "You appear to have cloned an empty repository."
+msgstr "Du verkar ha klonat ett tomt arkiv."
+
+#: builtin/column.c:51
+msgid "--command must be the first argument"
+msgstr "--command måste vara första argument"
+
+#: builtin/commit.c:43
+msgid ""
+"Your name and email address were configured automatically based\n"
+"on your username and hostname. Please check that they are accurate.\n"
+"You can suppress this message by setting them explicitly:\n"
+"\n"
+"    git config --global user.name \"Your Name\"\n"
+"    git config --global user.email you@example.com\n"
+"\n"
+"After doing this, you may fix the identity used for this commit with:\n"
+"\n"
+"    git commit --amend --reset-author\n"
+msgstr ""
+"Ditt namn och e-postadress konfigurerades automatiskt baserat på\n"
+"ditt användar-id och värdnamn. Kontrollera att de är riktiga. Du\n"
+"kan förhindra det här meddelandet genom att ställa dem explicit:\n"
+"\n"
+"    git config --global user.name \"Ditt namn\"\n"
+"    git config --global user.email du@example.com\n"
+"\n"
+"När du gjort det kan du rätta identiteten som användes för den här\n"
+"incheckningen med:\n"
+"\n"
+"    git commit --amend --reset-author\n"
+
+#: builtin/commit.c:55
+msgid ""
+"You asked to amend the most recent commit, but doing so would make\n"
+"it empty. You can repeat your command with --allow-empty, or you can\n"
+"remove the commit entirely with \"git reset HEAD^\".\n"
+msgstr ""
+"Du bad om att utöka den senaste incheckningen, men om du gör det\n"
+"blir den tom. Du kan köra kommandot på nytt med --allow-empty, eller\n"
+"så kan du ta bort incheckningen helt med \"git reset HEAD^\".\n"
+
+#: builtin/commit.c:60
+msgid ""
+"The previous cherry-pick is now empty, possibly due to conflict resolution.\n"
+"If you wish to commit it anyway, use:\n"
+"\n"
+"    git commit --allow-empty\n"
+"\n"
+"Otherwise, please use 'git reset'\n"
+msgstr ""
+"Den tidigare \"cherry-pick\":en är nu tom, kanske på grund av en löst\n"
+"konflikt. Om du vill checka in den ändå använder du:\n"
+"\n"
+"    git commit --allow-empty\n"
+"\n"
+"Annars använder du \"git reset\"\n"
+
+#: builtin/commit.c:253
+msgid "failed to unpack HEAD tree object"
+msgstr "misslyckades packa upp HEAD:s trädobjekt"
+
+#: builtin/commit.c:295
+msgid "unable to create temporary index"
+msgstr "kunde inte skapa temporär indexfil"
+
+#: builtin/commit.c:301
+msgid "interactive add failed"
+msgstr "interaktiv tilläggning misslyckades"
+
+#: builtin/commit.c:334 builtin/commit.c:355 builtin/commit.c:405
+msgid "unable to write new_index file"
+msgstr "kunde inte skriva filen new_index"
+
+#: builtin/commit.c:386
+msgid "cannot do a partial commit during a merge."
+msgstr "kan inte utföra en delvis incheckning under en sammanslagning."
+
+#: builtin/commit.c:388
+msgid "cannot do a partial commit during a cherry-pick."
+msgstr "kan inte utföra en delvis incheckning under en cherry-pick."
+
+#: builtin/commit.c:398
+msgid "cannot read the index"
+msgstr "kan inte läsa indexet"
+
+#: builtin/commit.c:418
+msgid "unable to write temporary index file"
+msgstr "kunde inte skriva temporär indexfil"
+
+#: builtin/commit.c:493 builtin/commit.c:499
+#, c-format
+msgid "invalid commit: %s"
+msgstr "felaktig incheckning: %s"
+
+#: builtin/commit.c:522
+msgid "malformed --author parameter"
+msgstr "felformad \"--author\"-flagga"
+
+#: builtin/commit.c:582
+#, c-format
+msgid "Malformed ident string: '%s'"
+msgstr "Felaktig indragningssträng: \"%s\""
+
+#: builtin/commit.c:620 builtin/commit.c:653 builtin/commit.c:967
+#, c-format
+msgid "could not lookup commit %s"
+msgstr "kunde inte slå upp incheckningen %s"
+
+#: builtin/commit.c:632 builtin/shortlog.c:296
+#, c-format
+msgid "(reading log message from standard input)\n"
+msgstr "(läser loggmeddelande från standard in)\n"
+
+#: builtin/commit.c:634
+msgid "could not read log from standard input"
+msgstr "kunde inte läsa logg från standard in"
+
+#: builtin/commit.c:638
+#, c-format
+msgid "could not read log file '%s'"
+msgstr "kunde inte läsa loggfilen \"%s\""
+
+#: builtin/commit.c:644
+msgid "commit has empty message"
+msgstr "incheckningen har ett tomt meddelande"
+
+#: builtin/commit.c:660
+msgid "could not read MERGE_MSG"
+msgstr "kunde inte läsa MERGE_MSG"
+
+#: builtin/commit.c:664
+msgid "could not read SQUASH_MSG"
+msgstr "kunde inte läsa SQUASH_MSG"
+
+#: builtin/commit.c:668
+#, c-format
+msgid "could not read '%s'"
+msgstr "kunde inte läsa \"%s\""
+
+#: builtin/commit.c:720
+msgid "could not write commit template"
+msgstr "kunde inte skriva incheckningsmall"
+
+#: builtin/commit.c:731
+#, c-format
+msgid ""
+"\n"
+"It looks like you may be committing a merge.\n"
+"If this is not correct, please remove the file\n"
+"\t%s\n"
+"and try again.\n"
+msgstr ""
+"\n"
+"Det verkar som du checkar in en sammanslagning.\n"
+"Om det inte stämmer tar du bort filen\n"
+"\t%s\n"
+"och försöker igen.\n"
+
+#: builtin/commit.c:736
+#, c-format
+msgid ""
+"\n"
+"It looks like you may be committing a cherry-pick.\n"
+"If this is not correct, please remove the file\n"
+"\t%s\n"
+"and try again.\n"
+msgstr ""
+"\n"
+"Det verkar som du checkar in en cherry-pick.\n"
+"Om det inte stämmer tar du bort filen\n"
+"\t%s\n"
+"och försöker igen.\n"
+
+#: builtin/commit.c:748
+msgid ""
+"Please enter the commit message for your changes. Lines starting\n"
+"with '#' will be ignored, and an empty message aborts the commit.\n"
+msgstr ""
+"Ange incheckningsmeddelandet för dina ändringar. Rader som inleds\n"
+"med \"#\" kommer ignoreras, och ett tomt meddelande avbryter incheckningen.\n"
+
+#: builtin/commit.c:753
+msgid ""
+"Please enter the commit message for your changes. Lines starting\n"
+"with '#' will be kept; you may remove them yourself if you want to.\n"
+"An empty message aborts the commit.\n"
+msgstr ""
+"Ange incheckningsmeddelandet för dina ändringar. Rader som inleds\n"
+"med \"#\" kommer behållas; du kan själv ta bort dem om du vill.\n"
+"Ett tomt meddelande avbryter incheckningen.\n"
+
+#: builtin/commit.c:766
+#, c-format
+msgid "%sAuthor:    %s"
+msgstr "%sFörfattare: %s"
+
+#: builtin/commit.c:773
+#, c-format
+msgid "%sCommitter: %s"
+msgstr "%sIncheckare: %s"
+
+#: builtin/commit.c:793
+msgid "Cannot read index"
+msgstr "Kan inte läsa indexet"
+
+#: builtin/commit.c:830
+msgid "Error building trees"
+msgstr "Fel vid byggande av träd"
+
+#: builtin/commit.c:845 builtin/tag.c:361
+#, c-format
+msgid "Please supply the message using either -m or -F option.\n"
+msgstr "Ange meddelandet en av flaggorna -m eller -F.\n"
+
+#: builtin/commit.c:942
+#, c-format
+msgid "No existing author found with '%s'"
+msgstr "Hittade ingen befintlig författare med \"%s\""
+
+#: builtin/commit.c:957 builtin/commit.c:1157
+#, c-format
+msgid "Invalid untracked files mode '%s'"
+msgstr "Ogiltigt läge för ospårade filer: \"%s\""
+
+#: builtin/commit.c:997
+msgid "Using both --reset-author and --author does not make sense"
+msgstr "Kan inte använda både --reset-author och --author"
+
+#: builtin/commit.c:1008
+msgid "You have nothing to amend."
+msgstr "Du har inget att utöka."
+
+#: builtin/commit.c:1011
+msgid "You are in the middle of a merge -- cannot amend."
+msgstr "Du är i mitten av en sammanslagning -- kan inte utöka."
+
+#: builtin/commit.c:1013
+msgid "You are in the middle of a cherry-pick -- cannot amend."
+msgstr "Du är i mitten av en cherry-pick -- kan inte utöka."
+
+#: builtin/commit.c:1016
+msgid "Options --squash and --fixup cannot be used together"
+msgstr "Flaggorna --squash och --fixup kan inte användas samtidigt"
+
+#: builtin/commit.c:1026
+msgid "Only one of -c/-C/-F/--fixup can be used."
+msgstr "Endast en av -c/-C/-F/--fixup kan användas."
+
+#: builtin/commit.c:1028
+msgid "Option -m cannot be combined with -c/-C/-F/--fixup."
+msgstr "Flaggan -m kan inte kombineras med -c/-C/-F/--fixup."
+
+#: builtin/commit.c:1036
+msgid "--reset-author can be used only with -C, -c or --amend."
+msgstr "--reset-author kan endast användas med -C, -c eller --amend."
+
+#: builtin/commit.c:1053
+msgid "Only one of --include/--only/--all/--interactive/--patch can be used."
+msgstr ""
+"Endast en av --include/--only/--all/--interactive/--patch kan användas."
+
+#: builtin/commit.c:1055
+msgid "No paths with --include/--only does not make sense."
+msgstr "Du måste ange sökvägar tillsammans med --include/--only."
+
+#: builtin/commit.c:1057
+msgid "Clever... amending the last one with dirty index."
+msgstr "Smart... utöka den senaste med smutsigt index."
+
+#: builtin/commit.c:1059
+msgid "Explicit paths specified without -i nor -o; assuming --only paths..."
+msgstr "Explicita sökvägar angavs utan -i eller -o; antar --only sökvägar..."
+
+#: builtin/commit.c:1069 builtin/tag.c:577
+#, c-format
+msgid "Invalid cleanup mode %s"
+msgstr "Felaktigt städningsläge %s"
+
+#: builtin/commit.c:1074
+msgid "Paths with -a does not make sense."
+msgstr "Kan inte ange sökvägar med -a."
+
+#: builtin/commit.c:1257
+msgid "couldn't look up newly created commit"
+msgstr "kunde inte slå upp en precis skapad incheckning"
+
+#: builtin/commit.c:1259
+msgid "could not parse newly created commit"
+msgstr "kunde inte tolka en precis skapad incheckning"
+
+#: builtin/commit.c:1300
+msgid "detached HEAD"
+msgstr "frånkopplad HEAD"
+
+#: builtin/commit.c:1302
+msgid " (root-commit)"
+msgstr " (rotincheckning)"
+
+#: builtin/commit.c:1446
+msgid "could not parse HEAD commit"
+msgstr "kunde inte tolka HEAD:s incheckning"
+
+#: builtin/commit.c:1484 builtin/merge.c:509
+#, c-format
+msgid "could not open '%s' for reading"
+msgstr "kunde inte öppna \"%s\" för läsning"
+
+#: builtin/commit.c:1491
+#, c-format
+msgid "Corrupt MERGE_HEAD file (%s)"
+msgstr "Trasig MERGE_HEAD-fil (%s)"
+
+#: builtin/commit.c:1498
+msgid "could not read MERGE_MODE"
+msgstr "kunde inte läsa MERGE_MODE"
+
+#: builtin/commit.c:1517
+#, c-format
+msgid "could not read commit message: %s"
+msgstr "kunde inte läsa incheckningsmeddelande: %s"
+
+#: builtin/commit.c:1531
+#, c-format
+msgid "Aborting commit; you did not edit the message.\n"
+msgstr "Avbryter incheckning; meddelandet inte redigerat.\n"
+
+#: builtin/commit.c:1536
+#, c-format
+msgid "Aborting commit due to empty commit message.\n"
+msgstr "Avbryter på grund av tomt incheckningsmeddelande.\n"
+
+#: builtin/commit.c:1551 builtin/merge.c:936 builtin/merge.c:961
+msgid "failed to write commit object"
+msgstr "kunde inte skriva incheckningsobjekt"
+
+#: builtin/commit.c:1572
+msgid "cannot lock HEAD ref"
+msgstr "kunde inte låsa HEAD-referens"
+
+#: builtin/commit.c:1576
+msgid "cannot update HEAD ref"
+msgstr "kunde inte uppdatera HEAD-referens"
+
+#: builtin/commit.c:1587
+msgid ""
+"Repository has been updated, but unable to write\n"
+"new_index file. Check that disk is not full or quota is\n"
+"not exceeded, and then \"git reset HEAD\" to recover."
+msgstr ""
+"Arkivet har uppdaterats, men kunde inte skriva filen\n"
+"new_index. Kontrollera att disken inte är full och\n"
+"att kvoten inte har överskridits, och kör sedan\n"
+"\"git reset HEAD\" för att återställa."
+
+#: builtin/describe.c:234
+#, c-format
+msgid "annotated tag %s not available"
+msgstr "den annoterade taggen %s inte tillgänglig"
+
+#: builtin/describe.c:238
+#, c-format
+msgid "annotated tag %s has no embedded name"
+msgstr "den annoterade taggen %s har inget inbäddat namn"
+
+#: builtin/describe.c:240
+#, c-format
+msgid "tag '%s' is really '%s' here"
+msgstr "taggen \"%s\" är i verkligheten \"%s\" här"
+
+#: builtin/describe.c:267
+#, c-format
+msgid "Not a valid object name %s"
+msgstr "Objektnamnet är inte giltigt: %s"
+
+#: builtin/describe.c:270
+#, c-format
+msgid "%s is not a valid '%s' object"
+msgstr "%s är inte ett giltigt \"%s\"-objekt"
+
+#: builtin/describe.c:287
+#, c-format
+msgid "no tag exactly matches '%s'"
+msgstr "ingen tagg motsvarar \"%s\" exakt"
+
+#: builtin/describe.c:289
+#, c-format
+msgid "searching to describe %s\n"
+msgstr "söker för att beskriva %s\n"
+
+#: builtin/describe.c:329
+#, c-format
+msgid "finished search at %s\n"
+msgstr "avslutade sökning på %s\n"
+
+#: builtin/describe.c:353
+#, c-format
+msgid ""
+"No annotated tags can describe '%s'.\n"
+"However, there were unannotated tags: try --tags."
+msgstr ""
+"Inga annoterade taggar kan beskriva \"%s\".\n"
+"Det finns dock oannoterade taggar: testa --tags."
+
+#: builtin/describe.c:357
+#, c-format
+msgid ""
+"No tags can describe '%s'.\n"
+"Try --always, or create some tags."
+msgstr ""
+"Inga taggar kan beskriva \"%s\".\n"
+"Testa --always, eller skapa några taggar."
+
+#: builtin/describe.c:378
+#, c-format
+msgid "traversed %lu commits\n"
+msgstr "traverserade %lu incheckningar\n"
+
+#: builtin/describe.c:381
+#, c-format
+msgid ""
+"more than %i tags found; listed %i most recent\n"
+"gave up search at %s\n"
+msgstr ""
+"mer än %i taggar hittades; listar de %i senaste\n"
+"gav upp sökningen vid %s\n"
+
+#: builtin/describe.c:436
+msgid "--long is incompatible with --abbrev=0"
+msgstr "--long är inkompatibel med --abbrev=0"
+
+#: builtin/describe.c:462
+msgid "No names found, cannot describe anything."
+msgstr "Inga namn hittades, kan inte beskriva något."
+
+#: builtin/describe.c:482
+msgid "--dirty is incompatible with committishes"
+msgstr "--dirty är inkompatibelt med \"committish\"-värden"
+
+#: builtin/diff.c:77
+#, c-format
+msgid "'%s': not a regular file or symlink"
+msgstr "\"%s\": inte en normal fil eller symbolisk länk"
+
+#: builtin/diff.c:220
+#, c-format
+msgid "invalid option: %s"
+msgstr "ogiltig flagga: %s"
+
+#: builtin/diff.c:297
+msgid "Not a git repository"
+msgstr "Inte ett git-arkiv"
+
+#: builtin/diff.c:341
+#, c-format
+msgid "invalid object '%s' given."
+msgstr "objektet \"%s\" som angavs är felaktigt."
+
+#: builtin/diff.c:346
+#, c-format
+msgid "more than %d trees given: '%s'"
+msgstr "mer än %d träd angavs: \"%s\""
+
+#: builtin/diff.c:356
+#, c-format
+msgid "more than two blobs given: '%s'"
+msgstr "mer än två blobbar angavs: \"%s\""
+
+#: builtin/diff.c:364
+#, c-format
+msgid "unhandled object '%s' given."
+msgstr "ej hanterat objekt \"%s\" angavs."
+
+#: builtin/fetch.c:200
+msgid "Couldn't find remote ref HEAD"
+msgstr "Kunde inte hitta fjärr-referensen HEAD"
+
+#: builtin/fetch.c:253
+#, c-format
+msgid "object %s not found"
+msgstr "objektet %s hittades inte"
+
+#: builtin/fetch.c:259
+msgid "[up to date]"
+msgstr "[àjour]"
+
+#: builtin/fetch.c:273
+#, c-format
+msgid "! %-*s %-*s -> %s  (can't fetch in current branch)"
+msgstr "! %-*s %-*s -> %s  (kan inte hämta i aktuell gren)"
+
+#: builtin/fetch.c:274 builtin/fetch.c:360
+msgid "[rejected]"
+msgstr "[refuserad]"
+
+#: builtin/fetch.c:285
+msgid "[tag update]"
+msgstr "[uppdaterad tagg]"
+
+#: builtin/fetch.c:287 builtin/fetch.c:322 builtin/fetch.c:340
+msgid "  (unable to update local ref)"
+msgstr "  (kunde inte uppdatera lokal ref)"
+
+#: builtin/fetch.c:305
+msgid "[new tag]"
+msgstr "[ny tagg]"
+
+#: builtin/fetch.c:308
+msgid "[new branch]"
+msgstr "[ny gren]"
+
+#: builtin/fetch.c:311
+msgid "[new ref]"
+msgstr "[ny ref]"
+
+#: builtin/fetch.c:356
+msgid "unable to update local ref"
+msgstr "kunde inte uppdatera lokal ref"
+
+#: builtin/fetch.c:356
+msgid "forced update"
+msgstr "tvingad uppdatering"
+
+#: builtin/fetch.c:362
+msgid "(non-fast-forward)"
+msgstr "(ej snabbspolad)"
+
+#: builtin/fetch.c:393 builtin/fetch.c:685
+#, c-format
+msgid "cannot open %s: %s\n"
+msgstr "kan inte öppna %s: %s\n"
+
+#: builtin/fetch.c:402
+#, c-format
+msgid "%s did not send all necessary objects\n"
+msgstr "%s sände inte alla nödvändiga objekt\n"
+
+#: builtin/fetch.c:488
+#, c-format
+msgid "From %.*s\n"
+msgstr "Från %.*s\n"
+
+#: builtin/fetch.c:499
+#, c-format
+msgid ""
+"some local refs could not be updated; try running\n"
+" 'git remote prune %s' to remove any old, conflicting branches"
+msgstr ""
+"vissa lokala referenser kunde inte uppdateras; testa att köra\n"
+" \"git remote prune %s\" för att ta bort gamla grenar som står i konflikt"
+
+#: builtin/fetch.c:549
+#, c-format
+msgid "   (%s will become dangling)"
+msgstr "   (%s kommer bli dinglande)"
+
+#: builtin/fetch.c:550
+#, c-format
+msgid "   (%s has become dangling)"
+msgstr "   (%s har blivit dinglande)"
+
+#: builtin/fetch.c:557
+msgid "[deleted]"
+msgstr "[borttagen]"
+
+#: builtin/fetch.c:558 builtin/remote.c:1055
+msgid "(none)"
+msgstr "(ingen)"
+
+#: builtin/fetch.c:675
+#, c-format
+msgid "Refusing to fetch into current branch %s of non-bare repository"
+msgstr "Vägrar hämta till aktuell gren %s i ett icke-naket arkiv"
+
+#: builtin/fetch.c:709
+#, c-format
+msgid "Don't know how to fetch from %s"
+msgstr "Vet inte hur man hämtar från %s"
+
+#: builtin/fetch.c:786
+#, c-format
+msgid "Option \"%s\" value \"%s\" is not valid for %s"
+msgstr "Flaggan \"%s\" och värdet \"%s\" är inte giltigt för %s"
+
+#: builtin/fetch.c:789
+#, c-format
+msgid "Option \"%s\" is ignored for %s\n"
+msgstr "Flaggan \"%s\" ignoreras för %s\n"
+
+#: builtin/fetch.c:888
+#, c-format
+msgid "Fetching %s\n"
+msgstr "Hämtar %s\n"
+
+#: builtin/fetch.c:890 builtin/remote.c:100
+#, c-format
+msgid "Could not fetch %s"
+msgstr "Kunde inte hämta %s"
+
+#: builtin/fetch.c:907
+msgid ""
+"No remote repository specified.  Please, specify either a URL or a\n"
+"remote name from which new revisions should be fetched."
+msgstr ""
+"Inget fjärrarkiv angavs. Ange antingen en URL eller namnet på ett\n"
+"fjärrarkiv som nya incheckningar skall hämtas från."
+
+#: builtin/fetch.c:927
+msgid "You need to specify a tag name."
+msgstr "Du måste ange namnet på en tagg."
+
+#: builtin/fetch.c:979
+msgid "fetch --all does not take a repository argument"
+msgstr "fetch --all tar inte namnet på ett arkiv som argument"
+
+#: builtin/fetch.c:981
+msgid "fetch --all does not make sense with refspecs"
+msgstr "fetch --all kan inte anges med referensspecifikationer"
+
+#: builtin/fetch.c:992
+#, c-format
+msgid "No such remote or remote group: %s"
+msgstr "Fjärren eller fjärrgruppen finns inte: %s"
+
+#: builtin/fetch.c:1000
+msgid "Fetching a group and specifying refspecs does not make sense"
+msgstr "Kan inte hämta från grupp och ange referensspecifikationer"
+
+#: builtin/gc.c:63
+#, c-format
+msgid "Invalid %s: '%s'"
+msgstr "Felaktigt %s: \"%s\""
+
+#: builtin/gc.c:90
+#, c-format
+msgid "insanely long object directory %.*s"
+msgstr "tokigt lång objektkatalog %.*s"
+
+#: builtin/gc.c:221
+#, c-format
+msgid "Auto packing the repository for optimum performance.\n"
+msgstr "Packar arkivet automatiskt för optimal prestanda.\n"
+
+#: builtin/gc.c:224
+#, c-format
+msgid ""
+"Auto packing the repository for optimum performance. You may also\n"
+"run \"git gc\" manually. See \"git help gc\" for more information.\n"
+msgstr ""
+"Packar arkivet automatiskt för optimal prestanda. Du kan även\n"
+"köra \"git gc\" manuellt. Se \"git help gc\" för mer information.\n"
+
+#: builtin/gc.c:251
+msgid ""
+"There are too many unreachable loose objects; run 'git prune' to remove them."
+msgstr ""
+"Det finns för många onåbara lösa objekt; kör \"git prune\" för att ta bort "
+"dem."
+
+#: builtin/grep.c:216
+#, c-format
+msgid "grep: failed to create thread: %s"
+msgstr "grep: misslyckades skapa tråd. %s"
+
+#: builtin/grep.c:402
+#, c-format
+msgid "Failed to chdir: %s"
+msgstr "Kunde inte byta katalog (chdir): %s"
+
+#: builtin/grep.c:478 builtin/grep.c:512
+#, c-format
+msgid "unable to read tree (%s)"
+msgstr "kunde inte läsa träd (%s)"
+
+#: builtin/grep.c:526
+#, c-format
+msgid "unable to grep from object of type %s"
+msgstr "Kunde inte \"grep\" från objekt av typen %s"
+
+#: builtin/grep.c:584
+#, c-format
+msgid "switch `%c' expects a numerical value"
+msgstr "flaggan \"%c\" antar ett numeriskt värde"
+
+#: builtin/grep.c:601
+#, c-format
+msgid "cannot open '%s'"
+msgstr "kan inte öppna \"%s\""
+
+#: builtin/grep.c:885
+msgid "no pattern given."
+msgstr "inget mönster angavs."
+
+#: builtin/grep.c:899
+#, c-format
+msgid "bad object %s"
+msgstr "felaktigt objekt %s"
+
+#: builtin/grep.c:940
+msgid "--open-files-in-pager only works on the worktree"
+msgstr "--open-files-in-pager fungerar endast i arbetskatalogen"
+
+#: builtin/grep.c:963
+msgid "--cached or --untracked cannot be used with --no-index."
+msgstr "--cached och --untracked kan inte användas med --no-index."
+
+#: builtin/grep.c:968
+msgid "--no-index or --untracked cannot be used with revs."
+msgstr "--no-index och --untracked kan inte användas med revisioner."
+
+#: builtin/grep.c:971
+msgid "--[no-]exclude-standard cannot be used for tracked contents."
+msgstr "--[no-]exclude-standard kan inte användas för spårat innehåll."
+
+#: builtin/grep.c:979
+msgid "both --cached and trees are given."
+msgstr "både --cached och träd angavs."
+
+#: builtin/help.c:63
+#, c-format
+msgid "unrecognized help format '%s'"
+msgstr "okänt hjälpformat: %s"
+
+#: builtin/help.c:91
+msgid "Failed to start emacsclient."
+msgstr "Misslyckades starta emacsclient."
+
+#: builtin/help.c:104
+msgid "Failed to parse emacsclient version."
+msgstr "Kunde inte tolka emacsclient-version."
+
+#: builtin/help.c:112
+#, c-format
+msgid "emacsclient version '%d' too old (< 22)."
+msgstr "emacsclient version \"%d\" för gammal (< 22)."
+
+#: builtin/help.c:130 builtin/help.c:158 builtin/help.c:167 builtin/help.c:175
+#, c-format
+msgid "failed to exec '%s': %s"
+msgstr "exec misslyckades för \"%s\": %s"
+
+#: builtin/help.c:215
+#, c-format
+msgid ""
+"'%s': path for unsupported man viewer.\n"
+"Please consider using 'man.<tool>.cmd' instead."
+msgstr ""
+"\"%s\": sökväg för man-visare som ej stöds.\n"
+"Använd \"man.<verktyg>.cmd\" istället."
+
+#: builtin/help.c:227
+#, c-format
+msgid ""
+"'%s': cmd for supported man viewer.\n"
+"Please consider using 'man.<tool>.path' instead."
+msgstr ""
+"\"%s\": kommando för man-visare som stöds.\n"
+"Använd \"man.<verktyg>.path\" istället."
+
+#: builtin/help.c:291
+msgid "The most commonly used git commands are:"
+msgstr "De mest använda git-kommandona är:"
+
+#: builtin/help.c:359
+#, c-format
+msgid "'%s': unknown man viewer."
+msgstr "\"%s\": okänd man-visare."
+
+#: builtin/help.c:376
+msgid "no man viewer handled the request"
+msgstr "ingen man-visare hanterade förfrågan"
+
+#: builtin/help.c:384
+msgid "no info viewer handled the request"
+msgstr "ingen info-visare hanterade förfrågan"
+
+#: builtin/help.c:395
+#, c-format
+msgid "'%s': not a documentation directory."
+msgstr "\"%s\": inte en dokumentationskatalog."
+
+#: builtin/help.c:436 builtin/help.c:443
+#, c-format
+msgid "usage: %s%s"
+msgstr "användning: %s%s"
+
+#: builtin/help.c:459
+#, c-format
+msgid "`git %s' is aliased to `%s'"
+msgstr "\"git %s\" är ett alias för \"%s\""
+
+#: builtin/index-pack.c:170
+#, c-format
+msgid "object type mismatch at %s"
+msgstr "objekttyp stämmer inte överens vid %s"
+
+#: builtin/index-pack.c:190
+msgid "object of unexpected type"
+msgstr "objekt av oväntad typ"
+
+#: builtin/index-pack.c:227
+#, c-format
+msgid "cannot fill %d byte"
+msgid_plural "cannot fill %d bytes"
+msgstr[0] "kan inte fylla %d byte"
+msgstr[1] "kan inte fylla %d byte"
+
+#: builtin/index-pack.c:237
+msgid "early EOF"
+msgstr "tidigt filslut"
+
+#: builtin/index-pack.c:238
+msgid "read error on input"
+msgstr "indataläsfel"
+
+#: builtin/index-pack.c:250
+msgid "used more bytes than were available"
+msgstr "använde fler byte än tillgängligt"
+
+#: builtin/index-pack.c:257
+msgid "pack too large for current definition of off_t"
+msgstr "paket för stort för nuvarande definition av off_t"
+
+#: builtin/index-pack.c:273
+#, c-format
+msgid "unable to create '%s'"
+msgstr "kunde inte skapa \"%s\""
+
+#: builtin/index-pack.c:278
+#, c-format
+msgid "cannot open packfile '%s'"
+msgstr "kan inte öppna paketfilen \"%s\""
+
+#: builtin/index-pack.c:292
+msgid "pack signature mismatch"
+msgstr "paketsignatur stämmer inte överens"
+
+#: builtin/index-pack.c:312
+#, c-format
+msgid "pack has bad object at offset %lu: %s"
+msgstr "paketet har felaktigt objekt vid index %lu: %s"
+
+#: builtin/index-pack.c:434
+#, c-format
+msgid "inflate returned %d"
+msgstr "inflate returnerade %d"
+
+#: builtin/index-pack.c:483
+msgid "offset value overflow for delta base object"
+msgstr "indexvärdespill för deltabasobjekt"
+
+#: builtin/index-pack.c:491
+msgid "delta base offset is out of bound"
+msgstr "deltabasindex utanför gränsen"
+
+#: builtin/index-pack.c:499
+#, c-format
+msgid "unknown object type %d"
+msgstr "okänd objekttyp %d"
+
+#: builtin/index-pack.c:531
+msgid "cannot pread pack file"
+msgstr "kan inte utföra \"pread\" på paketfil"
+
+#: builtin/index-pack.c:533
+#, c-format
+msgid "premature end of pack file, %lu byte missing"
+msgid_plural "premature end of pack file, %lu bytes missing"
+msgstr[0] "för tidigt slut på paketfilen, %lu byte saknas"
+msgstr[1] "för tidigt slut på paketfilen, %lu byte saknas"
+
+#: builtin/index-pack.c:555
+msgid "serious inflate inconsistency"
+msgstr "allvarlig inflate-inkonsekvens"
+
+#: builtin/index-pack.c:646 builtin/index-pack.c:652 builtin/index-pack.c:675
+#: builtin/index-pack.c:709 builtin/index-pack.c:718
+#, c-format
+msgid "SHA1 COLLISION FOUND WITH %s !"
+msgstr "SHA1-KOLLISION UPPTÄCKT VID %s !"
+
+#: builtin/index-pack.c:649 builtin/pack-objects.c:170
+#: builtin/pack-objects.c:262
+#, c-format
+msgid "unable to read %s"
+msgstr "kunde inte läsa %s"
+
+#: builtin/index-pack.c:715
+#, c-format
+msgid "cannot read existing object %s"
+msgstr "kan inte läsa befintligt objekt %s"
+
+#: builtin/index-pack.c:729
+#, c-format
+msgid "invalid blob object %s"
+msgstr "ogiltigt blob-objekt %s"
+
+#: builtin/index-pack.c:744
+#, c-format
+msgid "invalid %s"
+msgstr "ogiltigt %s"
+
+#: builtin/index-pack.c:746
+msgid "Error in object"
+msgstr "Fel i objekt"
+
+#: builtin/index-pack.c:748
+#, c-format
+msgid "Not all child objects of %s are reachable"
+msgstr "Inte alla barnobjekt för %s kan nås"
+
+#: builtin/index-pack.c:818 builtin/index-pack.c:844
+msgid "failed to apply delta"
+msgstr "misslyckades tillämpa delta"
+
+#: builtin/index-pack.c:983
+msgid "Receiving objects"
+msgstr "Tar bort objeckt"
+
+#: builtin/index-pack.c:983
+msgid "Indexing objects"
+msgstr "Skapar index för objekt"
+
+#: builtin/index-pack.c:1009
+msgid "pack is corrupted (SHA1 mismatch)"
+msgstr "paketet är trasigt (SHA1 stämmer inte)"
+
+#: builtin/index-pack.c:1014
+msgid "cannot fstat packfile"
+msgstr "kan inte utföra \"fstat\" på paketfil"
+
+#: builtin/index-pack.c:1017
+msgid "pack has junk at the end"
+msgstr "paket har skräp i slutet"
+
+#: builtin/index-pack.c:1028
+msgid "confusion beyond insanity in parse_pack_objects()"
+msgstr "förvirrad bortom vanvett i parse_pack_objects()"
+
+#: builtin/index-pack.c:1051
+msgid "Resolving deltas"
+msgstr "Analyserar delta"
+
+#: builtin/index-pack.c:1102
+msgid "confusion beyond insanity"
+msgstr "förvirrad bortom vanvett"
+
+#: builtin/index-pack.c:1121
+#, c-format
+msgid "pack has %d unresolved delta"
+msgid_plural "pack has %d unresolved deltas"
+msgstr[0] "paketet har %d oanalyserat delta"
+msgstr[1] "paketet har %d oanalyserade delta"
+
+#: builtin/index-pack.c:1146
+#, c-format
+msgid "unable to deflate appended object (%d)"
+msgstr "kunde inte utföra \"deflate\" på tillagt objekt (%d)"
+
+#: builtin/index-pack.c:1225
+#, c-format
+msgid "local object %s is corrupt"
+msgstr "lokalt objekt %s är trasigt"
+
+#: builtin/index-pack.c:1249
+msgid "error while closing pack file"
+msgstr "fel vid stängning av paketfil"
+
+#: builtin/index-pack.c:1262
+#, c-format
+msgid "cannot write keep file '%s'"
+msgstr "kan inte ta skriva \"keep\"-fil \"%s\""
+
+#: builtin/index-pack.c:1270
+#, c-format
+msgid "cannot close written keep file '%s'"
+msgstr "akn inte stänga skriven \"keep\"-fil \"%s\""
+
+#: builtin/index-pack.c:1283
+msgid "cannot store pack file"
+msgstr "kan inte spara paketfil"
+
+#: builtin/index-pack.c:1294
+msgid "cannot store index file"
+msgstr "kan inte spara indexfil"
+
+#: builtin/index-pack.c:1395
+#, c-format
+msgid "Cannot open existing pack file '%s'"
+msgstr "Kan inte öppna befintlig paketfil \"%s\""
+
+#: builtin/index-pack.c:1397
+#, c-format
+msgid "Cannot open existing pack idx file for '%s'"
+msgstr "Kan inte öppna befintligt paket-idx-fil för \"%s\""
+
+#: builtin/index-pack.c:1444
+#, c-format
+msgid "non delta: %d object"
+msgid_plural "non delta: %d objects"
+msgstr[0] "icke-delta: %d objekt"
+msgstr[1] "icke-delta: %d objekt"
+
+#: builtin/index-pack.c:1451
+#, c-format
+msgid "chain length = %d: %lu object"
+msgid_plural "chain length = %d: %lu objects"
+msgstr[0] "kedjelängd = %d: %lu objekt"
+msgstr[1] "kedjelängd = %d: %lu objekt"
+
+#: builtin/index-pack.c:1478
+msgid "Cannot come back to cwd"
+msgstr "Kan inte gå tillbaka till arbetskatalogen (cwd)"
+
+#: builtin/index-pack.c:1522 builtin/index-pack.c:1525
+#: builtin/index-pack.c:1537 builtin/index-pack.c:1541
+#, c-format
+msgid "bad %s"
+msgstr "felaktig %s"
+
+#: builtin/index-pack.c:1555
+msgid "--fix-thin cannot be used without --stdin"
+msgstr "--fix-thin kan inte användas med --stdin"
+
+#: builtin/index-pack.c:1559 builtin/index-pack.c:1569
+#, c-format
+msgid "packfile name '%s' does not end with '.pack'"
+msgstr "paketfilnamnet \"%s\" slutar inte med \".pack\""
+
+#: builtin/index-pack.c:1578
+msgid "--verify with no packfile name given"
+msgstr "--verify angavs utan paketfilnamn"
+
+#: builtin/init-db.c:35
+#, c-format
+msgid "Could not make %s writable by group"
+msgstr "Kunde inte göra %s skrivbar för gruppen"
+
+#: builtin/init-db.c:62
+#, c-format
+msgid "insanely long template name %s"
+msgstr "tokigt långt namn på mallen %s"
+
+#: builtin/init-db.c:67
+#, c-format
+msgid "cannot stat '%s'"
+msgstr "kan inte ta status på \"%s\""
+
+#: builtin/init-db.c:73
+#, c-format
+msgid "cannot stat template '%s'"
+msgstr "kan inte ta status på mallen \"%s\""
+
+#: builtin/init-db.c:80
+#, c-format
+msgid "cannot opendir '%s'"
+msgstr "kan inte öppna katalogen (opendir) \"%s\""
+
+#: builtin/init-db.c:97
+#, c-format
+msgid "cannot readlink '%s'"
+msgstr "kan inte läsa länk (readlink) \"%s\""
+
+#: builtin/init-db.c:99
+#, c-format
+msgid "insanely long symlink %s"
+msgstr "tokigt lång symbolisk länk %s"
+
+#: builtin/init-db.c:102
+#, c-format
+msgid "cannot symlink '%s' '%s'"
+msgstr "kan inte skapa symbolisk länk \"%s\" \"%s\""
+
+#: builtin/init-db.c:106
+#, c-format
+msgid "cannot copy '%s' to '%s'"
+msgstr "kan inte kopiera \"%s\" till \"%s\""
+
+#: builtin/init-db.c:110
+#, c-format
+msgid "ignoring template %s"
+msgstr "ignorerar mallen %s"
+
+#: builtin/init-db.c:133
+#, c-format
+msgid "insanely long template path %s"
+msgstr "tokigt lång mallsökväg %s"
+
+#: builtin/init-db.c:141
+#, c-format
+msgid "templates not found %s"
+msgstr "mallarna hittades inte %s"
+
+#: builtin/init-db.c:154
+#, c-format
+msgid "not copying templates of a wrong format version %d from '%s'"
+msgstr "kopierade inte mallar från felaktig formatversion %d från \"%s\""
+
+#: builtin/init-db.c:192
+#, c-format
+msgid "insane git directory %s"
+msgstr "tokig git-katalog %s"
+
+#: builtin/init-db.c:322 builtin/init-db.c:325
+#, c-format
+msgid "%s already exists"
+msgstr "%s finns redan"
+
+#: builtin/init-db.c:354
+#, c-format
+msgid "unable to handle file type %d"
+msgstr "kan inte hantera filtyp %d"
+
+#: builtin/init-db.c:357
+#, c-format
+msgid "unable to move %s to %s"
+msgstr "kan inte flytta %s till %s"
+
+#: builtin/init-db.c:362
+#, c-format
+msgid "Could not create git link %s"
+msgstr "Kunde inte skapa gitlänk %s"
+
+#.
+#. * TRANSLATORS: The first '%s' is either "Reinitialized
+#. * existing" or "Initialized empty", the second " shared" or
+#. * "", and the last '%s%s' is the verbatim directory name.
+#.
+#: builtin/init-db.c:419
+#, c-format
+msgid "%s%s Git repository in %s%s\n"
+msgstr "%s%s Git-arkiv i %s%s\n"
+
+#: builtin/init-db.c:420
+msgid "Reinitialized existing"
+msgstr "Ominitierade befintligt"
+
+#: builtin/init-db.c:420
+msgid "Initialized empty"
+msgstr "Initierade tomt"
+
+#: builtin/init-db.c:421
+msgid " shared"
+msgstr " delat"
+
+#: builtin/init-db.c:440
+msgid "cannot tell cwd"
+msgstr "kan inte läsa aktuell katalog (cwd)"
+
+#: builtin/init-db.c:521 builtin/init-db.c:528
+#, c-format
+msgid "cannot mkdir %s"
+msgstr "kan inte skapa katalogen (mkdir) %s"
+
+#: builtin/init-db.c:532
+#, c-format
+msgid "cannot chdir to %s"
+msgstr "kan inte byta katalog (chdir) till %s"
+
+#: builtin/init-db.c:554
+#, c-format
+msgid ""
+"%s (or --work-tree=<directory>) not allowed without specifying %s (or --git-"
+"dir=<directory>)"
+msgstr ""
+"%s (eller --work-tree=<katalog>) inte tillåtet utan att ange %s (eller --git-"
+"dir=<katalog>)"
+
+#: builtin/init-db.c:578
+msgid "Cannot access current working directory"
+msgstr "Kan inte komma åt aktuell arbetskatalog"
+
+#: builtin/init-db.c:585
+#, c-format
+msgid "Cannot access work tree '%s'"
+msgstr "Kan inte komma åt arbetskatalogen \"%s\""
+
+#: builtin/log.c:189
+#, c-format
+msgid "Final output: %d %s\n"
+msgstr "Slututdata: %d %s\n"
+
+#: builtin/log.c:402 builtin/log.c:490
+#, c-format
+msgid "Could not read object %s"
+msgstr "Kunde inte läsa objektet %s"
+
+#: builtin/log.c:514
+#, c-format
+msgid "Unknown type: %d"
+msgstr "Okänd typ: %d"
+
+#: builtin/log.c:603
+msgid "format.headers without value"
+msgstr "format.headers utan värde"
+
+#: builtin/log.c:677
+msgid "name of output directory is too long"
+msgstr "namnet på utdatakatalogen är för långt"
+
+#: builtin/log.c:688
+#, c-format
+msgid "Cannot open patch file %s"
+msgstr "Kan inte öppna patchfilen %s"
+
+#: builtin/log.c:702
+msgid "Need exactly one range."
+msgstr "Behöver precis ett intervall."
+
+#: builtin/log.c:710
+msgid "Not a range."
+msgstr "Inte ett intervall."
+
+#: builtin/log.c:787
+msgid "Cover letter needs email format"
+msgstr "Omslagsbrevet behöver e-postformat"
+
+#: builtin/log.c:860
+#, c-format
+msgid "insane in-reply-to: %s"
+msgstr "tokigt in-reply-to: %s"
+
+#: builtin/log.c:933
+msgid "Two output directories?"
+msgstr "Två utdatakataloger?"
+
+#: builtin/log.c:1154
+#, c-format
+msgid "bogus committer info %s"
+msgstr "felaktig incheckarinformation %s"
+
+#: builtin/log.c:1199
+msgid "-n and -k are mutually exclusive."
+msgstr "-n och -k kan inte användas samtidigt."
+
+#: builtin/log.c:1201
+msgid "--subject-prefix and -k are mutually exclusive."
+msgstr "--subject-prefix och -k kan inte användas samtidigt."
+
+#: builtin/log.c:1209
+msgid "--name-only does not make sense"
+msgstr "kan inte använda --name-only"
+
+#: builtin/log.c:1211
+msgid "--name-status does not make sense"
+msgstr "kan inte använda --name-status"
+
+#: builtin/log.c:1213
+msgid "--check does not make sense"
+msgstr "kan inte använda --check"
+
+#: builtin/log.c:1236
+msgid "standard output, or directory, which one?"
+msgstr "standard ut, eller katalog, vilken skall det vara?"
+
+#: builtin/log.c:1238
+#, c-format
+msgid "Could not create directory '%s'"
+msgstr "Kunde inte skapa katalogen \"%s\""
+
+#: builtin/log.c:1391
+msgid "Failed to create output files"
+msgstr "Misslyckades skapa utdatafiler"
+
+#: builtin/log.c:1495
+#, c-format
+msgid ""
+"Could not find a tracked remote branch, please specify <upstream> manually.\n"
+msgstr "Kunde inte hitta en spårad fjärrgren, ange <uppström> manuellt.\n"
+
+#: builtin/log.c:1511 builtin/log.c:1513 builtin/log.c:1525
+#, c-format
+msgid "Unknown commit %s"
+msgstr "Okänd incheckning %s"
+
+#: builtin/merge.c:90
+msgid "switch `m' requires a value"
+msgstr "flaggan \"m\" behöver ett värde"
+
+#: builtin/merge.c:127
+#, c-format
+msgid "Could not find merge strategy '%s'.\n"
+msgstr "Kunde inte hitta sammanslagningsstrategin \"%s\".\n"
+
+#: builtin/merge.c:128
+#, c-format
+msgid "Available strategies are:"
+msgstr "Tillgängliga strategier är:"
+
+#: builtin/merge.c:133
+#, c-format
+msgid "Available custom strategies are:"
+msgstr "Tillgängliga skräddarsydda strategier är:"
+
+#: builtin/merge.c:240
+msgid "could not run stash."
+msgstr "kunde köra stash."
+
+#: builtin/merge.c:245
+msgid "stash failed"
+msgstr "stash misslyckades"
+
+#: builtin/merge.c:250
+#, c-format
+msgid "not a valid object: %s"
+msgstr "inte ett giltigt objekt: %s"
+
+#: builtin/merge.c:269 builtin/merge.c:286
+msgid "read-tree failed"
+msgstr "read-tree misslyckades"
+
+#: builtin/merge.c:316
+msgid " (nothing to squash)"
+msgstr " (inget att platta till)"
+
+#: builtin/merge.c:329
+#, c-format
+msgid "Squash commit -- not updating HEAD\n"
+msgstr "Tillplattningsincheckning -- uppdaterar inte HEAD\n"
+
+#: builtin/merge.c:361
+msgid "Writing SQUASH_MSG"
+msgstr "Skriver SQUASH_MSG"
+
+#: builtin/merge.c:363
+msgid "Finishing SQUASH_MSG"
+msgstr "Avslutar SQUASH_MSG"
+
+#: builtin/merge.c:386
+#, c-format
+msgid "No merge message -- not updating HEAD\n"
+msgstr "Inget sammanslagningsmeddelande -- uppdaterar inte HEAD\n"
+
+#: builtin/merge.c:437
+#, c-format
+msgid "'%s' does not point to a commit"
+msgstr "\"%s\" verkar inte peka på en incheckning"
+
+#: builtin/merge.c:536
+#, c-format
+msgid "Bad branch.%s.mergeoptions string: %s"
+msgstr "Felaktig branch.%s.mergeoptions-sträng: %s"
+
+#: builtin/merge.c:629
+msgid "git write-tree failed to write a tree"
+msgstr "git write-tree misslyckades skriva ett träd"
+
+#: builtin/merge.c:679
+msgid "failed to read the cache"
+msgstr "misslyckads läsa cachen"
+
+#: builtin/merge.c:697
+msgid "Unable to write index."
+msgstr "Kunde inte skriva indexet."
+
+#: builtin/merge.c:710
+msgid "Not handling anything other than two heads merge."
+msgstr "Hanterar inte något annat än en sammanslagning av två huvuden."
+
+#: builtin/merge.c:724
+#, c-format
+msgid "Unknown option for merge-recursive: -X%s"
+msgstr "Felaktig flagga för merge-recursive: -X%s"
+
+#: builtin/merge.c:738
+#, c-format
+msgid "unable to write %s"
+msgstr "kunde inte skriva %s"
+
+#: builtin/merge.c:877
+#, c-format
+msgid "Could not read from '%s'"
+msgstr "Kunde inte läsa från \"%s\""
+
+#: builtin/merge.c:886
+#, c-format
+msgid "Not committing merge; use 'git commit' to complete the merge.\n"
+msgstr ""
+"Checkar inte in sammanslagningen; använd \"git commit\" för att slutföra "
+"den.\n"
+
+#: builtin/merge.c:892
+msgid ""
+"Please enter a commit message to explain why this merge is necessary,\n"
+"especially if it merges an updated upstream into a topic branch.\n"
+"\n"
+"Lines starting with '#' will be ignored, and an empty message aborts\n"
+"the commit.\n"
+msgstr ""
+"Ange ett incheckningsmeddelande för att förklara varför sammanslagningen\n"
+"är nödvändig, speciellt om den slår in en uppdaterad uppström i en\n"
+"temagren.\n"
+"\n"
+"Rader som inleds med \"#\" kommer ignoreras, och ett tomt meddelande\n"
+"avbryter incheckningen.\n"
+
+#: builtin/merge.c:916
+msgid "Empty commit message."
+msgstr "Tomt incheckningsmeddelande."
+
+#: builtin/merge.c:928
+#, c-format
+msgid "Wonderful.\n"
+msgstr "Underbart.\n"
+
+#: builtin/merge.c:993
+#, c-format
+msgid "Automatic merge failed; fix conflicts and then commit the result.\n"
+msgstr ""
+"Kunde inte slå ihop automatiskt; fixa konflikter och checka in resultatet.\n"
+
+#: builtin/merge.c:1009
+#, c-format
+msgid "'%s' is not a commit"
+msgstr "\"%s\" är inte en incheckning"
+
+#: builtin/merge.c:1050
+msgid "No current branch."
+msgstr "Inte på någon gren."
+
+#: builtin/merge.c:1052
+msgid "No remote for the current branch."
+msgstr "Ingen fjärr för aktuell gren."
+
+#: builtin/merge.c:1054
+msgid "No default upstream defined for the current branch."
+msgstr "Ingen standarduppström angiven för aktuell gren."
+
+#: builtin/merge.c:1059
+#, c-format
+msgid "No remote tracking branch for %s from %s"
+msgstr "Ingen fjärrspårande gren för %s från %s"
+
+#: builtin/merge.c:1146 builtin/merge.c:1303
+#, c-format
+msgid "%s - not something we can merge"
+msgstr "%s - inte något vi kan slå ihop"
+
+#: builtin/merge.c:1214
+msgid "There is no merge to abort (MERGE_HEAD missing)."
+msgstr "Det finns ingen sammanslagning att avbryta (MERGE_HEAD saknas)."
+
+#: builtin/merge.c:1230 git-pull.sh:31
+msgid ""
+"You have not concluded your merge (MERGE_HEAD exists).\n"
+"Please, commit your changes before you can merge."
+msgstr ""
+"Du har inte avslutat sammanslagningen (MERGE_HEAD finns).\n"
+"Checka in dina ändringar innan du kan slå ihop."
+
+#: builtin/merge.c:1233 git-pull.sh:34
+msgid "You have not concluded your merge (MERGE_HEAD exists)."
+msgstr "Du har inte avslutat sammanslagningen (MERGE_HEAD finns)."
+
+#: builtin/merge.c:1237
+msgid ""
+"You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
+"Please, commit your changes before you can merge."
+msgstr ""
+"Du har inte avslutat din \"cherry-pick\" (CHERRY_PICK_HEAD finns).\n"
+"Checka in dina ändringar innan du kan slå ihop."
+
+#: builtin/merge.c:1240
+msgid "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."
+msgstr "Du har inte avslutat din \"cherry-pick\" (CHERRY_PICK_HEAD finns)."
+
+#: builtin/merge.c:1249
+msgid "You cannot combine --squash with --no-ff."
+msgstr "Du kan inte kombinera --squash med --no-ff."
+
+#: builtin/merge.c:1254
+msgid "You cannot combine --no-ff with --ff-only."
+msgstr "Du kan inte kombinera --no-ff med --ff-only."
+
+#: builtin/merge.c:1261
+msgid "No commit specified and merge.defaultToUpstream not set."
+msgstr "Ingen incheckning angiven och merge.defaultToUpstream är ej satt."
+
+#: builtin/merge.c:1293
+msgid "Can merge only exactly one commit into empty head"
+msgstr "Kan endast slå ihop en enda incheckning i ett tomt huvud."
+
+#: builtin/merge.c:1296
+msgid "Squash commit into empty head not supported yet"
+msgstr "Stöder inte en tillplattningsincheckning på ett tomt huvud ännu"
+
+#: builtin/merge.c:1298
+msgid "Non-fast-forward commit does not make sense into an empty head"
+msgstr "Icke-snabbspolad incheckning kan inte användas med ett tomt huvud"
+
+#: builtin/merge.c:1413
+#, c-format
+msgid "Updating %s..%s\n"
+msgstr "Uppdaterar %s..%s\n"
+
+#: builtin/merge.c:1451
+#, c-format
+msgid "Trying really trivial in-index merge...\n"
+msgstr "Försöker riktigt enkel sammanslagning i indexet...\n"
+
+#: builtin/merge.c:1458
+#, c-format
+msgid "Nope.\n"
+msgstr "Nej.\n"
+
+#: builtin/merge.c:1490
+msgid "Not possible to fast-forward, aborting."
+msgstr "Kan inte snabbspola, avbryter."
+
+#: builtin/merge.c:1513 builtin/merge.c:1592
+#, c-format
+msgid "Rewinding the tree to pristine...\n"
+msgstr "Återspolar trädet till orört...\n"
+
+#: builtin/merge.c:1517
+#, c-format
+msgid "Trying merge strategy %s...\n"
+msgstr "Försöker sammanslagninsstrategin %s...\n"
+
+#: builtin/merge.c:1583
+#, c-format
+msgid "No merge strategy handled the merge.\n"
+msgstr "Ingen sammanslagningsstrategi hanterade sammanslagningen.\n"
+
+#: builtin/merge.c:1585
+#, c-format
+msgid "Merge with strategy %s failed.\n"
+msgstr "Sammanslaning med strategin %s misslyckades.\n"
+
+#: builtin/merge.c:1594
+#, c-format
+msgid "Using the %s to prepare resolving by hand.\n"
+msgstr "Använder %s för att förbereda lösning för hand.\n"
+
+#: builtin/merge.c:1606
+#, c-format
+msgid "Automatic merge went well; stopped before committing as requested\n"
+msgstr ""
+"Automatisk sammanslagning lyckades; stoppar före incheckning som önskat\n"
+
+#: builtin/mv.c:108
+#, c-format
+msgid "Checking rename of '%s' to '%s'\n"
+msgstr "Kontrollerar namnbyte av \"%s\" till \"%s\"\n"
+
+#: builtin/mv.c:112
+msgid "bad source"
+msgstr "felaktig källa"
+
+#: builtin/mv.c:115
+msgid "can not move directory into itself"
+msgstr "kan inte flytta katalog till sig själv"
+
+#: builtin/mv.c:118
+msgid "cannot move directory over file"
+msgstr "kan inte flytta katalog över fil"
+
+#: builtin/mv.c:128
+#, c-format
+msgid "Huh? %.*s is in index?"
+msgstr "Vad? %.*s är i indexet?"
+
+#: builtin/mv.c:140
+msgid "source directory is empty"
+msgstr "källkatalogen är tom"
+
+#: builtin/mv.c:171
+msgid "not under version control"
+msgstr "inte versionshanterad"
+
+#: builtin/mv.c:173
+msgid "destination exists"
+msgstr "destinationen finns"
+
+#: builtin/mv.c:181
+#, c-format
+msgid "overwriting '%s'"
+msgstr "skriver över \"%s\""
+
+#: builtin/mv.c:184
+msgid "Cannot overwrite"
+msgstr "Kan inte skriva över"
+
+#: builtin/mv.c:187
+msgid "multiple sources for the same target"
+msgstr "flera källor för samma mål"
+
+#: builtin/mv.c:202
+#, c-format
+msgid "%s, source=%s, destination=%s"
+msgstr "%s, källa=%s, mål=%s"
+
+#: builtin/mv.c:212
+#, c-format
+msgid "Renaming %s to %s\n"
+msgstr "Byter namn på %s till %s\n"
+
+#: builtin/mv.c:215 builtin/remote.c:731
+#, c-format
+msgid "renaming '%s' failed"
+msgstr "misslyckades byta namn på \"%s\""
+
+#: builtin/notes.c:139
+#, c-format
+msgid "unable to start 'show' for object '%s'"
+msgstr "kunde inte starta \"show\" för objektet \"%s\""
+
+#: builtin/notes.c:145
+msgid "can't fdopen 'show' output fd"
+msgstr "kunde inte öppna (fdopen) \"show\"-utdata-filhandtag"
+
+#: builtin/notes.c:155
+#, c-format
+msgid "failed to close pipe to 'show' for object '%s'"
+msgstr "kunde inte stänga röret till \"show\" för objektet \"%s\""
+
+#: builtin/notes.c:158
+#, c-format
+msgid "failed to finish 'show' for object '%s'"
+msgstr "kunde inte avsluta \"show\" för objektet \"%s\""
+
+#: builtin/notes.c:175 builtin/tag.c:347
+#, c-format
+msgid "could not create file '%s'"
+msgstr "kunde inte skapa filen \"%s\""
+
+#: builtin/notes.c:189
+msgid "Please supply the note contents using either -m or -F option"
+msgstr "Ange innehåll för anteckningen med antingen -m eller -F"
+
+#: builtin/notes.c:210 builtin/notes.c:973
+#, c-format
+msgid "Removing note for object %s\n"
+msgstr "Tar bort anteckning för objektet %s\n"
+
+#: builtin/notes.c:215
+msgid "unable to write note object"
+msgstr "kunde inte skriva anteckningsobjekt"
+
+#: builtin/notes.c:217
+#, c-format
+msgid "The note contents has been left in %s"
+msgstr "Anteckningens innehåll har lämnats kvar i %s"
+
+#: builtin/notes.c:251 builtin/tag.c:542
+#, c-format
+msgid "cannot read '%s'"
+msgstr "kunde inte läsa \"%s\""
+
+#: builtin/notes.c:253 builtin/tag.c:545
+#, c-format
+msgid "could not open or read '%s'"
+msgstr "kunde inte öppna eller läsa \"%s\""
+
+#: builtin/notes.c:272 builtin/notes.c:445 builtin/notes.c:447
+#: builtin/notes.c:507 builtin/notes.c:561 builtin/notes.c:644
+#: builtin/notes.c:649 builtin/notes.c:724 builtin/notes.c:766
+#: builtin/notes.c:968 builtin/reset.c:293 builtin/tag.c:558
+#, c-format
+msgid "Failed to resolve '%s' as a valid ref."
+msgstr "Kunde inte slå upp \"%s\" som en giltig referens."
+
+#: builtin/notes.c:275
+#, c-format
+msgid "Failed to read object '%s'."
+msgstr "Kunde inte läsa objektet \"%s\"."
+
+#: builtin/notes.c:299
+msgid "Cannot commit uninitialized/unreferenced notes tree"
+msgstr "Kan inte checka in oinitierat/orefererat anteckningsträd"
+
+#: builtin/notes.c:340
+#, c-format
+msgid "Bad notes.rewriteMode value: '%s'"
+msgstr "Felaktigt värde för notes.rewriteMode: '%s'"
+
+#: builtin/notes.c:350
+#, c-format
+msgid "Refusing to rewrite notes in %s (outside of refs/notes/)"
+msgstr "Vägrar skriva över anteckningar i %s (utanför refs/notes/)"
+
+#. TRANSLATORS: The first %s is the name of the
+#. environment variable, the second %s is its value
+#: builtin/notes.c:377
+#, c-format
+msgid "Bad %s value: '%s'"
+msgstr "Felaktigt värde på %s: \"%s\""
+
+#: builtin/notes.c:441
+#, c-format
+msgid "Malformed input line: '%s'."
+msgstr "Felaktig indatarad: \"%s\"."
+
+#: builtin/notes.c:456
+#, c-format
+msgid "Failed to copy notes from '%s' to '%s'"
+msgstr "Misslyckades kopiera anteckningar från \"%s\" till \"%s\""
+
+#: builtin/notes.c:500 builtin/notes.c:554 builtin/notes.c:627
+#: builtin/notes.c:639 builtin/notes.c:712 builtin/notes.c:759
+#: builtin/notes.c:1033
+msgid "too many parameters"
+msgstr "för många parametrar"
+
+#: builtin/notes.c:513 builtin/notes.c:772
+#, c-format
+msgid "No note found for object %s."
+msgstr "Inga anteckningar hittades för objektet %s."
+
+#: builtin/notes.c:580
+#, c-format
+msgid ""
+"Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite "
+"existing notes"
+msgstr ""
+"Kan inte lägga till anteckningar. Hittade befintliga anteckningar för "
+"objektet %s. Använd \"-f\" för att skriva över befintliga anteckningar"
+
+#: builtin/notes.c:585 builtin/notes.c:662
+#, c-format
+msgid "Overwriting existing notes for object %s\n"
+msgstr "Skriver över befintliga anteckningar för objektet %s\n"
+
+#: builtin/notes.c:635
+msgid "too few parameters"
+msgstr "för få parametrar"
+
+#: builtin/notes.c:656
+#, c-format
+msgid ""
+"Cannot copy notes. Found existing notes for object %s. Use '-f' to overwrite "
+"existing notes"
+msgstr ""
+"Kan inte kopiera anteckningar. Hittade befintliga anteckningar för objektet "
+"%s. Använd \"-f\" för att skriva över befintliga anteckningar"
+
+#: builtin/notes.c:668
+#, c-format
+msgid "Missing notes on source object %s. Cannot copy."
+msgstr "Anteckningar på källobjektet %s saknas. Kan inte kopiera."
+
+#: builtin/notes.c:717
+#, c-format
+msgid ""
+"The -m/-F/-c/-C options have been deprecated for the 'edit' subcommand.\n"
+"Please use 'git notes add -f -m/-F/-c/-C' instead.\n"
+msgstr ""
+"Flaggorna -m/-F/-c/-C rekommenderas inte för underkommandot \"edit\".\n"
+"Använd \"git notes add -f -m/-F/-c/-C\" istället.\n"
+
+#: builtin/notes.c:971
+#, c-format
+msgid "Object %s has no note\n"
+msgstr "Objektet %s har ingen anteckning\n"
+
+#: builtin/notes.c:1103 builtin/remote.c:1598
+#, c-format
+msgid "Unknown subcommand: %s"
+msgstr "Okänt underkommando: %s"
+
+#: builtin/pack-objects.c:183 builtin/pack-objects.c:186
+#, c-format
+msgid "deflate error (%d)"
+msgstr "fel i deflate (%d)"
+
+#: builtin/pack-objects.c:2398
+#, c-format
+msgid "unsupported index version %s"
+msgstr "indexversionen %s stöds ej"
+
+#: builtin/pack-objects.c:2402
+#, c-format
+msgid "bad index version '%s'"
+msgstr "felaktig indexversion \"%s\""
+
+#: builtin/pack-objects.c:2425
+#, c-format
+msgid "option %s does not accept negative form"
+msgstr "flaggan %s godtar inte negativ form"
+
+#: builtin/pack-objects.c:2429
+#, c-format
+msgid "unable to parse value '%s' for option %s"
+msgstr "kunde inte tolka värdet \"%s\" för flaggan %s"
+
+#: builtin/push.c:45
+msgid "tag shorthand without <tag>"
+msgstr "taggförkortning utan <tagg>"
+
+#: builtin/push.c:64
+msgid "--delete only accepts plain target ref names"
+msgstr "--delete godtar endast enkla målreferensnamn"
+
+#: builtin/push.c:99
+msgid ""
+"\n"
+"To choose either option permanently, see push.default in 'git help config'."
+msgstr ""
+"\n"
+"För att välja ett av alternativen permanent, se push.default i \"git help "
+"config\"."
+
+#: builtin/push.c:102
+#, c-format
+msgid ""
+"The upstream branch of your current branch does not match\n"
+"the name of your current branch.  To push to the upstream branch\n"
+"on the remote, use\n"
+"\n"
+"    git push %s HEAD:%s\n"
+"\n"
+"To push to the branch of the same name on the remote, use\n"
+"\n"
+"    git push %s %s\n"
+"%s"
+msgstr ""
+"Uppströmsgrenen för din nuvarande gren stämmer inte överens\n"
+"med namnet på din aktuella gren. För att sända till uppströmsgrenen\n"
+"i fjärrarkivet använder du\n"
+"\n"
+"    git push %s HEAD:%s\n"
+"\n"
+"För att sända till grenen med samma namn i fjärrarkivet använder du\n"
+"\n"
+"    git push %s %s\n"
+"%s"
+
+#: builtin/push.c:121
+#, c-format
+msgid ""
+"You are not currently on a branch.\n"
+"To push the history leading to the current (detached HEAD)\n"
+"state now, use\n"
+"\n"
+"    git push %s HEAD:<name-of-remote-branch>\n"
+msgstr ""
+"Du är inte på någon gren för närvarande.\n"
+"För att sända in historiken som leder till den aktuella (frånkopplat\n"
+"HEAD) situationen använder du\n"
+"\n"
+"    git push %s HEAD:<namn-på-fjärrgren>\n"
+
+#: builtin/push.c:128
+#, c-format
+msgid ""
+"The current branch %s has no upstream branch.\n"
+"To push the current branch and set the remote as upstream, use\n"
+"\n"
+"    git push --set-upstream %s %s\n"
+msgstr ""
+"Den aktuella grenen %s har ingen uppströmsgren.\n"
+"För att sända aktuell gren och ange fjärrarkiv som uppström använder du\n"
+"\n"
+"    git push --set-upstream %s %s\n"
+
+#: builtin/push.c:136
+#, c-format
+msgid "The current branch %s has multiple upstream branches, refusing to push."
+msgstr "Den aktuella grenen %s har flera uppströmsgrenar, vägrar sända."
+
+#: builtin/push.c:139
+#, c-format
+msgid ""
+"You are pushing to remote '%s', which is not the upstream of\n"
+"your current branch '%s', without telling me what to push\n"
+"to update which remote branch."
+msgstr ""
+"Du sänder till fjärren \"%s\", som inte är uppströms för den\n"
+"aktuella grenen \"%s\", utan att tala om för mig vad som\n"
+"skall sändas för att uppdatera fjärrgrenen."
+
+#: builtin/push.c:174
+msgid ""
+"You didn't specify any refspecs to push, and push.default is \"nothing\"."
+msgstr ""
+"Du angav inga referensspecifikationer att sända, och push.default är "
+"\"nothing\"."
+
+#: builtin/push.c:181
+msgid ""
+"Updates were rejected because the tip of your current branch is behind\n"
+"its remote counterpart. Merge the remote changes (e.g. 'git pull')\n"
+"before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
+msgstr ""
+"Uppdateringar avvisades då änden på din befintliga gren är bakom\n"
+"dess fjärrmotsvarighet. Slå ihop fjärrändringarna (t.ex. \"git pull\")\n"
+"innan du sänder igen.\n"
+"Se avsnittet \"Note about fast-forward\" i \"git push --help\" för detaljer."
+
+#: builtin/push.c:187
+msgid ""
+"Updates were rejected because a pushed branch tip is behind its remote\n"
+"counterpart. If you did not intend to push that branch, you may want to\n"
+"specify branches to push or set the 'push.default' configuration\n"
+"variable to 'current' or 'upstream' to push only the current branch."
+msgstr ""
+"Uppdateringar avvisades då änden på en insänd gren är bakom dess\n"
+"fjärrmotsvarighet. Om det inte var meningen att sända in grenen, bör\n"
+"du specificera grenar att sända, eller ändra inställningsvariabeln\n"
+"\"push-default\" till \"current\" eller \"upstream\" för att endast sända\n"
+"aktuell gren."
+
+#: builtin/push.c:193
+msgid ""
+"Updates were rejected because a pushed branch tip is behind its remote\n"
+"counterpart. Check out this branch and merge the remote changes\n"
+"(e.g. 'git pull') before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
+msgstr ""
+"Uppdateringar avvisades då änden på en gren som sänds in är bakom dess\n"
+"fjärrmotsvarighet. Checka ut grenen och slå ihop fjärrändringarna (t.ex.\n"
+"\"git pull\") innan du sänder igen.\n"
+"Se avsnittet \"Note about fast-forward\" i \"git push --help\" för detaljer."
+
+#: builtin/push.c:233
+#, c-format
+msgid "Pushing to %s\n"
+msgstr "Sänder till %s\n"
+
+#: builtin/push.c:237
+#, c-format
+msgid "failed to push some refs to '%s'"
+msgstr "misslyckades sända vissa referenser till \"%s\""
+
+#: builtin/push.c:269
+#, c-format
+msgid "bad repository '%s'"
+msgstr "felaktigt arkiv \"%s\""
+
+#: builtin/push.c:270
+msgid ""
+"No configured push destination.\n"
+"Either specify the URL from the command-line or configure a remote "
+"repository using\n"
+"\n"
+"    git remote add <name> <url>\n"
+"\n"
+"and then push using the remote name\n"
+"\n"
+"    git push <name>\n"
+msgstr ""
+"Ingen destination har angivits.\n"
+"Ange antingen URL:en på kommandoraden eller ställ in ett uppströmsarkiv med\n"
+"\n"
+"    git remote add <namn> <url>\n"
+"\n"
+"och sänd sedan med hjälp av fjärrnamnet\n"
+"\n"
+"    git push <namn>\n"
+
+#: builtin/push.c:285
+msgid "--all and --tags are incompatible"
+msgstr "--all och --tags är inkompatibla"
+
+#: builtin/push.c:286
+msgid "--all can't be combined with refspecs"
+msgstr "--all kan inte kombineras med referensspecifikationer"
+
+#: builtin/push.c:291
+msgid "--mirror and --tags are incompatible"
+msgstr "--mirror och --tags är inkompatibla"
+
+#: builtin/push.c:292
+msgid "--mirror can't be combined with refspecs"
+msgstr "--mirror kan inte kombineras med referensspecifikationer"
+
+#: builtin/push.c:297
+msgid "--all and --mirror are incompatible"
+msgstr "--all och --mirror är inkompatibla"
+
+#: builtin/push.c:385
+msgid "--delete is incompatible with --all, --mirror and --tags"
+msgstr "--delete är imkompatibel med --all, --mirror och --tags"
+
+#: builtin/push.c:387
+msgid "--delete doesn't make sense without any refs"
+msgstr "--delete kan inte användas utan referenser"
+
+#: builtin/remote.c:98
+#, c-format
+msgid "Updating %s"
+msgstr "Uppdaterar %s"
+
+#: builtin/remote.c:130
+msgid ""
+"--mirror is dangerous and deprecated; please\n"
+"\t use --mirror=fetch or --mirror=push instead"
+msgstr ""
+"--mirror är farlig och föråldrad; använd\n"
+"\t --mirror=fetch eller --mirror=push istället"
+
+#: builtin/remote.c:147
+#, c-format
+msgid "unknown mirror argument: %s"
+msgstr "okänt argument till mirror: %s"
+
+#: builtin/remote.c:185
+msgid "specifying a master branch makes no sense with --mirror"
+msgstr "att ange en master-gren ger ingen mening med --mirror"
+
+#: builtin/remote.c:187
+msgid "specifying branches to track makes sense only with fetch mirrors"
+msgstr "att ange grenar att spåra ger mening bara med hämtningsspeglar"
+
+#: builtin/remote.c:195 builtin/remote.c:646
+#, c-format
+msgid "remote %s already exists."
+msgstr "fjärrarkivet %s finns redan."
+
+#: builtin/remote.c:199 builtin/remote.c:650
+#, c-format
+msgid "'%s' is not a valid remote name"
+msgstr "\"%s\" är inte ett giltigt namn på fjärrarkiv"
+
+#: builtin/remote.c:243
+#, c-format
+msgid "Could not setup master '%s'"
+msgstr "Kunde inte skapa master \"%s\""
+
+#: builtin/remote.c:299
+#, c-format
+msgid "more than one %s"
+msgstr "mer än en %s"
+
+#: builtin/remote.c:339
+#, c-format
+msgid "Could not get fetch map for refspec %s"
+msgstr "Kunde inte hämta mappning för referensspecifikation %s"
+
+#: builtin/remote.c:440 builtin/remote.c:448
+msgid "(matching)"
+msgstr "(matchande)"
+
+#: builtin/remote.c:452
+msgid "(delete)"
+msgstr "(ta bort)"
+
+#: builtin/remote.c:595 builtin/remote.c:601 builtin/remote.c:607
+#, c-format
+msgid "Could not append '%s' to '%s'"
+msgstr "Kunde inte tillämpa \"%s\" på \"%s\""
+
+#: builtin/remote.c:639 builtin/remote.c:792 builtin/remote.c:890
+#, c-format
+msgid "No such remote: %s"
+msgstr "Inget sådant fjärrarkiv: %s"
+
+#: builtin/remote.c:656
+#, c-format
+msgid "Could not rename config section '%s' to '%s'"
+msgstr "Kunde inte byta namn på konfigurationssektionen \"%s\" till \"%s\""
+
+#: builtin/remote.c:662 builtin/remote.c:799
+#, c-format
+msgid "Could not remove config section '%s'"
+msgstr "Kunde inte ta bort konfigurationssektionen \"%s\""
+
+#: builtin/remote.c:677
+#, c-format
+msgid ""
+"Not updating non-default fetch refspec\n"
+"\t%s\n"
+"\tPlease update the configuration manually if necessary."
+msgstr ""
+"Uppdaterar inte icke-standard hämtningsreferensspecifikation\n"
+"\t%s\n"
+"\tUppdatera konfigurationen manuellt om nödvändigt."
+
+#: builtin/remote.c:683
+#, c-format
+msgid "Could not append '%s'"
+msgstr "Kunde inte lägga till på \"%s\""
+
+#: builtin/remote.c:694
+#, c-format
+msgid "Could not set '%s'"
+msgstr "Kunde inte sätta \"%s\""
+
+#: builtin/remote.c:716
+#, c-format
+msgid "deleting '%s' failed"
+msgstr "misslyckades ta bort \"%s\""
+
+#: builtin/remote.c:750
+#, c-format
+msgid "creating '%s' failed"
+msgstr "misslyckades skapa \"%s\""
+
+#: builtin/remote.c:764
+#, c-format
+msgid "Could not remove branch %s"
+msgstr "Kunde inte ta bort grenen %s"
+
+#: builtin/remote.c:834
+msgid ""
+"Note: A branch outside the refs/remotes/ hierarchy was not removed;\n"
+"to delete it, use:"
+msgid_plural ""
+"Note: Some branches outside the refs/remotes/ hierarchy were not removed;\n"
+"to delete them, use:"
+msgstr[0] ""
+"Observera: En gren utanför hierarkin refs/remotes/ togs inte bort;\n"
+"för att ta bort den, använd:"
+msgstr[1] ""
+"Observera: Några grenar utanför hierarkin refs/remotes/ togs inte bort;\n"
+"för att ta bort dem, använd:"
+
+#: builtin/remote.c:943
+#, c-format
+msgid " new (next fetch will store in remotes/%s)"
+msgstr " ny (nästa hämtning sparar i remotes/%s)"
+
+#: builtin/remote.c:946
+msgid " tracked"
+msgstr " spårad"
+
+#: builtin/remote.c:948
+msgid " stale (use 'git remote prune' to remove)"
+msgstr " förlegad (använd \"git remote prune\" för att ta bort)"
+
+#: builtin/remote.c:950
+msgid " ???"
+msgstr " ???"
+
+#: builtin/remote.c:991
+#, c-format
+msgid "invalid branch.%s.merge; cannot rebase onto > 1 branch"
+msgstr "ogiltig branch.%s.merge; kan inte ombasera över > 1 gren"
+
+#: builtin/remote.c:998
+#, c-format
+msgid "rebases onto remote %s"
+msgstr "ombaseras på fjärren %s"
+
+#: builtin/remote.c:1001
+#, c-format
+msgid " merges with remote %s"
+msgstr " sammanslås med fjärren %s"
+
+#: builtin/remote.c:1002
+msgid "    and with remote"
+msgstr "    och med fjärren"
+
+#: builtin/remote.c:1004
+#, c-format
+msgid "merges with remote %s"
+msgstr "sammanslås med fjärren %s"
+
+#: builtin/remote.c:1005
+msgid "   and with remote"
+msgstr "   och med fjärren"
+
+#: builtin/remote.c:1051
+msgid "create"
+msgstr "skapa"
+
+#: builtin/remote.c:1054
+msgid "delete"
+msgstr "ta bort"
+
+#: builtin/remote.c:1058
+msgid "up to date"
+msgstr "àjour"
+
+#: builtin/remote.c:1061
+msgid "fast-forwardable"
+msgstr "kan snabbspolas"
+
+#: builtin/remote.c:1064
+msgid "local out of date"
+msgstr "lokal föråldrad"
+
+#: builtin/remote.c:1071
+#, c-format
+msgid "    %-*s forces to %-*s (%s)"
+msgstr "    %-*s tvingar till %-*s (%s)"
+
+#: builtin/remote.c:1074
+#, c-format
+msgid "    %-*s pushes to %-*s (%s)"
+msgstr "    %-*s sänder till %-*s (%s)"
+
+#: builtin/remote.c:1078
+#, c-format
+msgid "    %-*s forces to %s"
+msgstr "    %-*s tvingar till %s"
+
+#: builtin/remote.c:1081
+#, c-format
+msgid "    %-*s pushes to %s"
+msgstr "    %-*s sänder till %s"
+
+#: builtin/remote.c:1118
+#, c-format
+msgid "* remote %s"
+msgstr "* fjärr %s"
+
+#: builtin/remote.c:1119
+#, c-format
+msgid "  Fetch URL: %s"
+msgstr "  Hämt-URL: %s"
+
+#: builtin/remote.c:1120 builtin/remote.c:1285
+msgid "(no URL)"
+msgstr "(ingen URL)"
+
+#: builtin/remote.c:1129 builtin/remote.c:1131
+#, c-format
+msgid "  Push  URL: %s"
+msgstr "  Sänd-URL: %s"
+
+#: builtin/remote.c:1133 builtin/remote.c:1135 builtin/remote.c:1137
+#, c-format
+msgid "  HEAD branch: %s"
+msgstr "  HEAD-gren: %s"
+
+#: builtin/remote.c:1139
+#, c-format
+msgid ""
+"  HEAD branch (remote HEAD is ambiguous, may be one of the following):\n"
+msgstr "  HEAD-gren (HEAD på fjärr är tvetydig, kan vara en av följande):\n"
+
+#: builtin/remote.c:1151
+#, c-format
+msgid "  Remote branch:%s"
+msgid_plural "  Remote branches:%s"
+msgstr[0] "  Fjärrgren:%s"
+msgstr[1] "  Fjärrgrenar:%s"
+
+#: builtin/remote.c:1154 builtin/remote.c:1181
+msgid " (status not queried)"
+msgstr " (status inte förfrågad)"
+
+#: builtin/remote.c:1163
+msgid "  Local branch configured for 'git pull':"
+msgid_plural "  Local branches configured for 'git pull':"
+msgstr[0] "  Lokal gren konfigurerad för \"git pull\":"
+msgstr[1] "  Lokala grenar konfigurerade för \"git pull\":"
+
+#: builtin/remote.c:1171
+msgid "  Local refs will be mirrored by 'git push'"
+msgstr "  Lokala referenser speglas av \"git push\""
+
+#: builtin/remote.c:1178
+#, c-format
+msgid "  Local ref configured for 'git push'%s:"
+msgid_plural "  Local refs configured for 'git push'%s:"
+msgstr[0] "  Lokal referens konfigurerad för \"git push\"%s:"
+msgstr[1] "  Lokala referenser konfigurerade för \"git push\"%s:"
+
+#: builtin/remote.c:1216
+msgid "Cannot determine remote HEAD"
+msgstr "Kan inte bestämma HEAD på fjärren"
+
+#: builtin/remote.c:1218
+msgid "Multiple remote HEAD branches. Please choose one explicitly with:"
+msgstr "Flera HEAD-grenar på fjärren. Välj en explicit med:"
+
+#: builtin/remote.c:1228
+#, c-format
+msgid "Could not delete %s"
+msgstr "Kunde inte ta bort %s"
+
+#: builtin/remote.c:1236
+#, c-format
+msgid "Not a valid ref: %s"
+msgstr "Inte en giltig referens: %s"
+
+#: builtin/remote.c:1238
+#, c-format
+msgid "Could not setup %s"
+msgstr "Kunde inte ställa in %s"
+
+#: builtin/remote.c:1274
+#, c-format
+msgid " %s will become dangling!"
+msgstr " %s kommer bli dinglande!"
+
+#: builtin/remote.c:1275
+#, c-format
+msgid " %s has become dangling!"
+msgstr " %s har blivit dinglande!"
+
+#: builtin/remote.c:1281
+#, c-format
+msgid "Pruning %s"
+msgstr "Rensar %s"
+
+#: builtin/remote.c:1282
+#, c-format
+msgid "URL: %s"
+msgstr "URL: %s"
+
+#: builtin/remote.c:1295
+#, c-format
+msgid " * [would prune] %s"
+msgstr " * [skulle rensa] %s"
+
+#: builtin/remote.c:1298
+#, c-format
+msgid " * [pruned] %s"
+msgstr " * [rensad] %s"
+
+#: builtin/remote.c:1387 builtin/remote.c:1461
+#, c-format
+msgid "No such remote '%s'"
+msgstr "Ingen sådan fjärr \"%s\""
+
+#: builtin/remote.c:1414
+msgid "no remote specified"
+msgstr "ingen fjärr angavs"
+
+#: builtin/remote.c:1447
+msgid "--add --delete doesn't make sense"
+msgstr "--add --delete ger ingen mening"
+
+#: builtin/remote.c:1487
+#, c-format
+msgid "Invalid old URL pattern: %s"
+msgstr "Felaktig gammalt URL-mönster: %s"
+
+#: builtin/remote.c:1495
+#, c-format
+msgid "No such URL found: %s"
+msgstr "Ingen sådan URL hittades: %s"
+
+#: builtin/remote.c:1497
+msgid "Will not delete all non-push URLs"
+msgstr "Kommer inte ta bort alla icke-sänd-URL:er"
+
+#: builtin/reset.c:33
+msgid "mixed"
+msgstr "blandad"
+
+#: builtin/reset.c:33
+msgid "soft"
+msgstr "mjuk"
+
+#: builtin/reset.c:33
+msgid "hard"
+msgstr "hård"
+
+#: builtin/reset.c:33
+msgid "merge"
+msgstr "sammanslagning"
+
+#: builtin/reset.c:33
+msgid "keep"
+msgstr "behåll"
+
+#: builtin/reset.c:77
+msgid "You do not have a valid HEAD."
+msgstr "Du har inte en giltig HEAD."
+
+#: builtin/reset.c:79
+msgid "Failed to find tree of HEAD."
+msgstr "Kunde inte hitta träder för HEAD."
+
+#: builtin/reset.c:85
+#, c-format
+msgid "Failed to find tree of %s."
+msgstr "Kunde inte hitta träder för %s."
+
+#: builtin/reset.c:96
+msgid "Could not write new index file."
+msgstr "Kunde inte skriva ny indexfil."
+
+#: builtin/reset.c:106
+#, c-format
+msgid "HEAD is now at %s"
+msgstr "HEAD är nu på %s"
+
+#: builtin/reset.c:130
+msgid "Could not read index"
+msgstr "Kunde inte läsa indexet"
+
+#: builtin/reset.c:133
+msgid "Unstaged changes after reset:"
+msgstr "Oköade ändringar efter återställning:"
+
+#: builtin/reset.c:223
+#, c-format
+msgid "Cannot do a %s reset in the middle of a merge."
+msgstr "Kan inte utföra en %s återställning mitt i en sammanslagning."
+
+#: builtin/reset.c:297
+#, c-format
+msgid "Could not parse object '%s'."
+msgstr "Kan inte tolka objektet \"%s\""
+
+#: builtin/reset.c:302
+msgid "--patch is incompatible with --{hard,mixed,soft}"
+msgstr "--patch är inkompatibel med --{hard,mixed,soft}"
+
+#: builtin/reset.c:311
+msgid "--mixed with paths is deprecated; use 'git reset -- <paths>' instead."
+msgstr ""
+"--mixed rekommenderas inte med sökvägar; använd \"git reset -- <sökvägar>\"."
+
+#: builtin/reset.c:313
+#, c-format
+msgid "Cannot do %s reset with paths."
+msgstr "Kan inte göra %s återställning med sökvägar."
+
+#: builtin/reset.c:325
+#, c-format
+msgid "%s reset is not allowed in a bare repository"
+msgstr "%s återställning tillåts inte i ett naket arkiv"
+
+#: builtin/reset.c:341
+#, c-format
+msgid "Could not reset index file to revision '%s'."
+msgstr "Kunde inte återställa indexfilen till versionen \"%s\"."
+
+#: builtin/revert.c:70 builtin/revert.c:92
+#, c-format
+msgid "%s: %s cannot be used with %s"
+msgstr "%s: %s kan inte användas med %s"
+
+#: builtin/revert.c:131
+msgid "program error"
+msgstr "programfel"
+
+#: builtin/revert.c:221
+msgid "revert failed"
+msgstr "\"revert\" misslyckades"
+
+#: builtin/revert.c:236
+msgid "cherry-pick failed"
+msgstr "\"cherry-pick\" misslyckades"
+
+#: builtin/rm.c:109
+#, c-format
+msgid ""
+"'%s' has staged content different from both the file and the HEAD\n"
+"(use -f to force removal)"
+msgstr ""
+"\"%s\" har köat ändringar som skiljer sig både från filen och HEAD\n"
+"(använd -f för att tvinga borttagning)"
+
+#: builtin/rm.c:115
+#, c-format
+msgid ""
+"'%s' has changes staged in the index\n"
+"(use --cached to keep the file, or -f to force removal)"
+msgstr ""
+"\"%s\" har köade ändringar i indexet\n"
+"(använd --cached för att behålla filen eller -f för att tvinga borttagning)"
+
+#: builtin/rm.c:119
+#, c-format
+msgid ""
+"'%s' has local modifications\n"
+"(use --cached to keep the file, or -f to force removal)"
+msgstr ""
+"\"%s\" har lokala ändringar\n"
+"(använd --cached för att behålla filen eller -f för att tvinga borttagning)"
+
+#: builtin/rm.c:194
+#, c-format
+msgid "not removing '%s' recursively without -r"
+msgstr "tar inte bort \"%s\" rekursivt utan -r"
+
+#: builtin/rm.c:230
+#, c-format
+msgid "git rm: unable to remove %s"
+msgstr "git rm: kan inte ta bort %s"
+
+#: builtin/shortlog.c:157
+#, c-format
+msgid "Missing author: %s"
+msgstr "Författare saknas: %s"
+
+#: builtin/tag.c:60
+#, c-format
+msgid "malformed object at '%s'"
+msgstr "felformat objekt vid \"%s\""
+
+#: builtin/tag.c:207
+#, c-format
+msgid "tag name too long: %.*s..."
+msgstr "taggnamnet för långt: %.*s..."
+
+#: builtin/tag.c:212
+#, c-format
+msgid "tag '%s' not found."
+msgstr "taggen \"%s\" hittades inte."
+
+#: builtin/tag.c:227
+#, c-format
+msgid "Deleted tag '%s' (was %s)\n"
+msgstr "Tog bort tagg \"%s\" (var %s)\n"
+
+#: builtin/tag.c:239
+#, c-format
+msgid "could not verify the tag '%s'"
+msgstr "kunde inte bekräfta taggen \"%s\""
+
+#: builtin/tag.c:249
+msgid ""
+"\n"
+"#\n"
+"# Write a tag message\n"
+"# Lines starting with '#' will be ignored.\n"
+"#\n"
+msgstr ""
+"\n"
+"#\n"
+"# Skriv ett taggmeddelande\n"
+"# Rader som inleds med \"#\" ignoreras.\n"
+"#\n"
+
+#: builtin/tag.c:256
+msgid ""
+"\n"
+"#\n"
+"# Write a tag message\n"
+"# Lines starting with '#' will be kept; you may remove them yourself if you "
+"want to.\n"
+"#\n"
+msgstr ""
+"\n"
+"#\n"
+"# Skriv ett taggmeddelande\n"
+"# Rader som inleds med \"#\" kommer behållas; du kan själv ta bort dem om\n"
+"# du vill.\n"
+"#\n"
+
+#: builtin/tag.c:298
+msgid "unable to sign the tag"
+msgstr "kunde inte signera taggen"
+
+#: builtin/tag.c:300
+msgid "unable to write tag file"
+msgstr "kunde inte skriva tagg-filen"
+
+#: builtin/tag.c:325
+msgid "bad object type."
+msgstr "felaktig objekttyp"
+
+#: builtin/tag.c:338
+msgid "tag header too big."
+msgstr "tagghuvud för stort."
+
+#: builtin/tag.c:370
+msgid "no tag message?"
+msgstr "inget taggmeddelande?"
+
+#: builtin/tag.c:376
+#, c-format
+msgid "The tag message has been left in %s\n"
+msgstr "Taggmeddelandet har lämnats i %s\n"
+
+#: builtin/tag.c:425
+msgid "switch 'points-at' requires an object"
+msgstr "flaggan \"points-at\" behöver ett object"
+
+#: builtin/tag.c:427
+#, c-format
+msgid "malformed object name '%s'"
+msgstr "felformat objektnamn \"%s\""
+
+#: builtin/tag.c:506
+msgid "--column and -n are incompatible"
+msgstr "--column och -n är inkompatibla"
+
+#: builtin/tag.c:523
+msgid "-n option is only allowed with -l."
+msgstr "Flaggan -n är endast tillåten tillsammans med -l."
+
+#: builtin/tag.c:525
+msgid "--contains option is only allowed with -l."
+msgstr "Flaggan --contains är endast tillåten tillsammans med -l"
+
+#: builtin/tag.c:527
+msgid "--points-at option is only allowed with -l."
+msgstr "Flaggan --points-at är endast tillåten tillsammans med -l."
+
+#: builtin/tag.c:535
+msgid "only one -F or -m option is allowed."
+msgstr "endast en av flaggorna -F eller -m tillåts."
+
+#: builtin/tag.c:555
+msgid "too many params"
+msgstr "för många parametrar"
+
+#: builtin/tag.c:561
+#, c-format
+msgid "'%s' is not a valid tag name."
+msgstr "\"%s\" är inte ett giltigt taggnamn."
+
+#: builtin/tag.c:566
+#, c-format
+msgid "tag '%s' already exists"
+msgstr "taggen \"%s\" finns redan"
+
+#: builtin/tag.c:584
+#, c-format
+msgid "%s: cannot lock the ref"
+msgstr "%s: kan inte låsa referensen"
+
+#: builtin/tag.c:586
+#, c-format
+msgid "%s: cannot update the ref"
+msgstr "%s: kan inte uppdatera referensen"
+
+#: builtin/tag.c:588
+#, c-format
+msgid "Updated tag '%s' (was %s)\n"
+msgstr "Uppdaterad tagg \"%s\" (var %s)\n"
+
+#: git.c:16
+msgid "See 'git help <command>' for more information on a specific command."
+msgstr ""
+"Se \"git help <kommando>\" för mer information om ett specifikt kommando."
+
+#: parse-options.h:133 parse-options.h:235
+msgid "n"
+msgstr "n"
+
+#: parse-options.h:141
+msgid "time"
+msgstr "tid"
+
+# %s är ett verb ("Untracked"/"Ignored"); lägg till ett -e.
+#: parse-options.h:149
+msgid "file"
+msgstr "fil"
+
+#: parse-options.h:151
+msgid "when"
+msgstr "när"
+
+#: parse-options.h:156
+msgid "no-op (backward compatibility)"
+msgstr "ingen funktion (bakåtkompatibilitet)"
+
+#: parse-options.h:228
+msgid "be more verbose"
+msgstr "var mer pratsam"
+
+#: parse-options.h:230
+msgid "be more quiet"
+msgstr "var mer tyst"
+
+#: parse-options.h:236
+msgid "use <n> digits to display SHA-1s"
+msgstr "använd <n> siffror för att visa SHA-1:or"
+
+#: common-cmds.h:8
+msgid "Add file contents to the index"
+msgstr "Lägg filinnehåll till indexet"
+
+#: common-cmds.h:9
+msgid "Find by binary search the change that introduced a bug"
+msgstr "Binärsök för att hitta ändringen som introducerade ett fel"
+
+#: common-cmds.h:10
+msgid "List, create, or delete branches"
+msgstr "Visa, skapa eller ta bort grenar"
+
+#: common-cmds.h:11
+msgid "Checkout a branch or paths to the working tree"
+msgstr "Checka ut en gren eller filer i arbetskatalogen"
+
+#: common-cmds.h:12
+msgid "Clone a repository into a new directory"
+msgstr "Klona ett arkiv till en ny katalog"
+
+#: common-cmds.h:13
+msgid "Record changes to the repository"
+msgstr "Protokollför ändringar i arkivet"
+
+#: common-cmds.h:14
+msgid "Show changes between commits, commit and working tree, etc"
+msgstr "Visa ändringar mellan incheckningar, med arbetskatalogen, osv"
+
+#: common-cmds.h:15
+msgid "Download objects and refs from another repository"
+msgstr "Hämta objekt och referenser från annat arkiv"
+
+#: common-cmds.h:16
+msgid "Print lines matching a pattern"
+msgstr "Visa rader som motsvarar mönster"
+
+#: common-cmds.h:17
+msgid "Create an empty git repository or reinitialize an existing one"
+msgstr "Skapa tomt git-arkiv eller ominitiera ett befintligt"
+
+#: common-cmds.h:18
+msgid "Show commit logs"
+msgstr "Visa incheckningsloggar"
+
+#: common-cmds.h:19
+msgid "Join two or more development histories together"
+msgstr "Slå ihop två eller flera utvecklingshistorier"
+
+#: common-cmds.h:20
+msgid "Move or rename a file, a directory, or a symlink"
+msgstr "Flytta eller byt namn på en fil, katalog eller symbolisk länk"
+
+#: common-cmds.h:21
+msgid "Fetch from and merge with another repository or a local branch"
+msgstr "Hämta från och slå ihop med annat arkiv eller en lokal gren"
+
+#: common-cmds.h:22
+msgid "Update remote refs along with associated objects"
+msgstr "Uppdatera fjärr-referenser och tillhörande objekt"
+
+#: common-cmds.h:23
+msgid "Forward-port local commits to the updated upstream head"
+msgstr "Framåtanpassa lokala kommandon på uppdaterat uppströmshuvud"
+
+#: common-cmds.h:24
+msgid "Reset current HEAD to the specified state"
+msgstr "Återställ aktuell HEAD till angivet tillstånd"
+
+#: common-cmds.h:25
+msgid "Remove files from the working tree and from the index"
+msgstr "Ta bort filer från arbetskatalogen och från indexet"
+
+#: common-cmds.h:26
+msgid "Show various types of objects"
+msgstr "Visa olika sorters objekt"
+
+#: common-cmds.h:27
+msgid "Show the working tree status"
+msgstr "Visa status för arbetskatalogen"
+
+#: common-cmds.h:28
+msgid "Create, list, delete or verify a tag object signed with GPG"
+msgstr "Skapa, visa, ta bort eller verifiera ett taggobjekt signerat med GPG"
+
+#: git-am.sh:50
+msgid "You need to set your committer info first"
+msgstr "Du måste ställa in din incheckarinformation först"
+
+#: git-am.sh:95
+msgid ""
+"You seem to have moved HEAD since the last 'am' failure.\n"
+"Not rewinding to ORIG_HEAD"
+msgstr ""
+"Du verkar ha flyttat HEAD sedan \"am\" sist misslyckades.\n"
+"Återställer inte till ORIG_HEAD"
+
+#: git-am.sh:105
+#, sh-format
+msgid ""
+"When you have resolved this problem run \"$cmdline --resolved\".\n"
+"If you would prefer to skip this patch, instead run \"$cmdline --skip\".\n"
+"To restore the original branch and stop patching run \"$cmdline --abort\"."
+msgstr ""
+"När du har löst problemet kör du \"$cmdline --resolved\".\n"
+"Om du vill hoppa över patchen kör du istället \"$cmdline --skip\".\n"
+"För att återställa originalgrenen och avbryta kör du \"$cmdline --abort\"."
+
+#: git-am.sh:121
+msgid "Cannot fall back to three-way merge."
+msgstr "Kan inte falla tillbaka på trevägssammanslagning."
+
+#: git-am.sh:137
+msgid "Repository lacks necessary blobs to fall back on 3-way merge."
+msgstr ""
+"Arkivet saknar objekt som behövs för att falla tillbaka på 3-"
+"vägssammanslagning."
+
+#: git-am.sh:154
+msgid ""
+"Did you hand edit your patch?\n"
+"It does not apply to blobs recorded in its index."
+msgstr ""
+"Har du handredigerat din patch?\n"
+"Den kan inte tillämpas på blobbar som antecknats i dess index."
+
+#: git-am.sh:163
+msgid "Falling back to patching base and 3-way merge..."
+msgstr ""
+"Faller tillbaka på att pacha grundversionen och trevägssammanslagning..."
+
+#: git-am.sh:275
+msgid "Only one StGIT patch series can be applied at once"
+msgstr "Endast en StGIT-patchserie kan tillämpas åt gången"
+
+#: git-am.sh:362
+#, sh-format
+msgid "Patch format $patch_format is not supported."
+msgstr "Patchformatet $patch_format stöds inte."
+
+#: git-am.sh:364
+msgid "Patch format detection failed."
+msgstr "Misslyckades detektera patchformat."
+
+#: git-am.sh:418
+msgid "-d option is no longer supported.  Do not use."
+msgstr "Flaggan -d stöds inte lägre. Använd inte."
+
+#: git-am.sh:481
+#, sh-format
+msgid "previous rebase directory $dotest still exists but mbox given."
+msgstr "tidigare rebase-katalog $dotest finns fortfarande, men mbox angavs."
+
+#: git-am.sh:486
+msgid "Please make up your mind. --skip or --abort?"
+msgstr "Bestäm dig. --skip eller --abort?"
+
+#: git-am.sh:513
+msgid "Resolve operation not in progress, we are not resuming."
+msgstr "Lösningsoperation pågår inte, vi återupptar inte."
+
+#: git-am.sh:579
+#, sh-format
+msgid "Dirty index: cannot apply patches (dirty: $files)"
+msgstr "Smutsigt index: kan inte tillämpa patchar (smutsiga: $files)"
+
+#: git-am.sh:671
+#, sh-format
+msgid ""
+"Patch is empty.  Was it split wrong?\n"
+"If you would prefer to skip this patch, instead run \"$cmdline --skip\".\n"
+"To restore the original branch and stop patching run \"$cmdline --abort\"."
+msgstr ""
+"Patchen är tom. Delades den upp felaktigt?\n"
+"Om du vill hoppa över patchen kör du istället \"$cmdline --skip\".\n"
+"För att återställa originalgrenen och avbryta kör du \"$cmdline --abort\"."
+
+#: git-am.sh:708
+msgid "Patch does not have a valid e-mail address."
+msgstr "Patchen har inte någon giltig e-postadress."
+
+#: git-am.sh:755
+msgid "cannot be interactive without stdin connected to a terminal."
+msgstr ""
+"kan inte vara interaktiv om standard in inte är ansluten till en terminal."
+
+#: git-am.sh:759
+msgid "Commit Body is:"
+msgstr "Incheckningskroppen är:"
+
+#. TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
+#. in your translation. The program will only accept English
+#. input at this point.
+#: git-am.sh:766
+msgid "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
+msgstr "Tillämpa? Y=ja/N=nej/E=redigera/V=visa patch/A=godta alla "
+
+#: git-am.sh:802
+#, sh-format
+msgid "Applying: $FIRSTLINE"
+msgstr "Tillämpar: $FIRSTLINE"
+
+#: git-am.sh:823
+msgid ""
+"No changes - did you forget to use 'git add'?\n"
+"If there is nothing left to stage, chances are that something else\n"
+"already introduced the same changes; you might want to skip this patch."
+msgstr ""
+"Inga ändringar - glömde du använda \"git add\"?\n"
+"Om det inte är något kvar att köa kan det hända att något annat redan\n"
+"introducerat samma ändringar; kanske du bör hoppa över patchen."
+
+#: git-am.sh:831
+msgid ""
+"You still have unmerged paths in your index\n"
+"did you forget to use 'git add'?"
+msgstr ""
+"Du har fortfarande sökvägar som inte slagits samman i ditt index\n"
+"glömde du använda \"git add\"?"
+
+#: git-am.sh:847
+msgid "No changes -- Patch already applied."
+msgstr "Inga ändringar -- Patchen har redan tillämpats."
+
+#: git-am.sh:857
+#, sh-format
+msgid "Patch failed at $msgnum $FIRSTLINE"
+msgstr "Patchen misslyckades vid $msgnum $FIRSTLINE"
+
+#: git-am.sh:873
+msgid "applying to an empty history"
+msgstr "tillämpar på en tom historik"
+
+#: git-bisect.sh:48
+msgid "You need to start by \"git bisect start\""
+msgstr "Du måste starta med \"git bisect start\""
+
+#. TRANSLATORS: Make sure to include [Y] and [n] in your
+#. translation. The program will only accept English input
+#. at this point.
+#: git-bisect.sh:54
+msgid "Do you want me to do it for you [Y/n]? "
+msgstr "Vill du att jag ska göra det åt dig [Y=ja/N=nej]?"
+
+#: git-bisect.sh:95
+#, sh-format
+msgid "unrecognised option: '$arg'"
+msgstr "flaggan känns inte igen: \"$arg\""
+
+#: git-bisect.sh:99
+#, sh-format
+msgid "'$arg' does not appear to be a valid revision"
+msgstr "\"$arg\" verkar inte vara en giltig revision"
+
+#: git-bisect.sh:117
+msgid "Bad HEAD - I need a HEAD"
+msgstr "Felaktigt HEAD - Jag behöver ett HEAD"
+
+#: git-bisect.sh:130
+#, sh-format
+msgid ""
+"Checking out '$start_head' failed. Try 'git bisect reset <validbranch>'."
+msgstr ""
+"Misslyckades checka ut \"$start_head\". Försök \"git bisect reset "
+"<giltiggren>\""
+
+# cogito-relaterat
+#: git-bisect.sh:140
+msgid "won't bisect on seeked tree"
+msgstr "kör inte \"bisect\" på ett \"seeked\"-träd"
+
+#: git-bisect.sh:144
+msgid "Bad HEAD - strange symbolic ref"
+msgstr "Felaktigt HEAD - konstig symbolisk referens"
+
+#: git-bisect.sh:189
+#, sh-format
+msgid "Bad bisect_write argument: $state"
+msgstr "Felaktigt argument till bisect_write: $state"
+
+#: git-bisect.sh:218
+#, sh-format
+msgid "Bad rev input: $arg"
+msgstr "Felaktig rev-indata: $arg"
+
+#: git-bisect.sh:232
+msgid "Please call 'bisect_state' with at least one argument."
+msgstr "Anropa \"bisect_state\" med minst ett argument."
+
+#: git-bisect.sh:244
+#, sh-format
+msgid "Bad rev input: $rev"
+msgstr "Felaktig rev-indata: $rev"
+
+#: git-bisect.sh:250
+msgid "'git bisect bad' can take only one argument."
+msgstr "\"git bisect bad\" kan bara ta ett argument."
+
+#. have bad but not good.  we could bisect although
+#. this is less optimum.
+#: git-bisect.sh:273
+msgid "Warning: bisecting only with a bad commit."
+msgstr "Varning: utför \"bisect\" med endast en dålig incheckning"
+
+#. TRANSLATORS: Make sure to include [Y] and [n] in your
+#. translation. The program will only accept English input
+#. at this point.
+#: git-bisect.sh:279
+msgid "Are you sure [Y/n]? "
+msgstr "Är du säker [Y=ja/N=nej]? "
+
+#: git-bisect.sh:289
+msgid ""
+"You need to give me at least one good and one bad revisions.\n"
+"(You can use \"git bisect bad\" and \"git bisect good\" for that.)"
+msgstr ""
+"Du måste ange åtminstone en bra och en dålig version.\n"
+"(Du kan använda \"git bisect bad\" och \"git bisect good\" för detta.)"
+
+#: git-bisect.sh:292
+msgid ""
+"You need to start by \"git bisect start\".\n"
+"You then need to give me at least one good and one bad revisions.\n"
+"(You can use \"git bisect bad\" and \"git bisect good\" for that.)"
+msgstr ""
+"Du måste starta med \"git bisect start\".\n"
+"Du måste sedan ange åtminstone en bra och en dålig version.\n"
+"(Du kan använda \"git bisect bad\" och \"git bisect good\" för detta.)"
+
+#: git-bisect.sh:347 git-bisect.sh:474
+msgid "We are not bisecting."
+msgstr "Vi utför ingen bisect för tillfället."
+
+#: git-bisect.sh:354
+#, sh-format
+msgid "'$invalid' is not a valid commit"
+msgstr "\"$invalid\" är inte en giltig incheckning"
+
+#: git-bisect.sh:363
+#, sh-format
+msgid ""
+"Could not check out original HEAD '$branch'.\n"
+"Try 'git bisect reset <commit>'."
+msgstr ""
+"Kunde inte checka ut original-HEAD \"$branch\".\n"
+"Försök \"git bisect reset <incheckning>\"."
+
+#: git-bisect.sh:390
+msgid "No logfile given"
+msgstr "Ingen loggfil angiven"
+
+#: git-bisect.sh:391
+#, sh-format
+msgid "cannot read $file for replaying"
+msgstr "kan inte läsa $file för uppspelning"
+
+#: git-bisect.sh:408
+msgid "?? what are you talking about?"
+msgstr "?? vad menar du?"
+
+#: git-bisect.sh:420
+#, sh-format
+msgid "running $command"
+msgstr "kör $command"
+
+#: git-bisect.sh:427
+#, sh-format
+msgid ""
+"bisect run failed:\n"
+"exit code $res from '$command' is < 0 or >= 128"
+msgstr ""
+"\"bisect\"-körningen misslyckades:\n"
+"felkod $res från \"$command\" är < 0 eller >= 128"
+
+#: git-bisect.sh:453
+msgid "bisect run cannot continue any more"
+msgstr "\"bisect\"-körningen kan inte fortsätta längre"
+
+#: git-bisect.sh:459
+#, sh-format
+msgid ""
+"bisect run failed:\n"
+"'bisect_state $state' exited with error code $res"
+msgstr ""
+"\"bisect\"-körningen misslyckades:\n"
+"\"bisect_state $state\" avslutades med felkoden $res"
+
+#: git-bisect.sh:466
+msgid "bisect run success"
+msgstr "\"bisect\"-körningen lyckades"
+
+#: git-pull.sh:21
+msgid ""
+"Pull is not possible because you have unmerged files.\n"
+"Please, fix them up in the work tree, and then use 'git add/rm <file>'\n"
+"as appropriate to mark resolution, or use 'git commit -a'."
+msgstr ""
+"Du kan inte göra en \"pull\" då du har ändringar som inte checkats in.\n"
+"Rätta dem i din arbetskatalog och använd sedan \"git add/rm <fil>\"\n"
+"där det är lämpligt för att ange lösning, eller använd \"git commit -a\"."
+
+#: git-pull.sh:25
+msgid "Pull is not possible because you have unmerged files."
+msgstr "Du kan inte göra en \"pull\" då du har ändringar som inte checkats in."
+
+#: git-pull.sh:197
+msgid "updating an unborn branch with changes added to the index"
+msgstr "uppdaterar en ofödd gren med ändringar som lagts till i indexet"
+
+#. The fetch involved updating the current branch.
+#. The working tree and the index file is still based on the
+#. $orig_head commit, but we are merging into $curr_head.
+#. First update the working tree to match $curr_head.
+#: git-pull.sh:228
+#, sh-format
+msgid ""
+"Warning: fetch updated the current branch head.\n"
+"Warning: fast-forwarding your working tree from\n"
+"Warning: commit $orig_head."
+msgstr ""
+"Varning: fetch uppdaterade huvudet för aktuell gren.\n"
+"Varning: snabbspolar din arbetskatalog från\n"
+"Varning: incheckningen $orig_head."
+
+#: git-pull.sh:253
+msgid "Cannot merge multiple branches into empty head"
+msgstr "Kan inte slå ihop flera grenar i ett tomt huvud."
+
+#: git-pull.sh:257
+msgid "Cannot rebase onto multiple branches"
+msgstr "Kan inte utföra en \"rebase\" ovanpå flera grenar"
+
+#: git-stash.sh:51
+msgid "git stash clear with parameters is unimplemented"
+msgstr "\"git stash clear\" med parametrar har inte implementerats"
+
+#: git-stash.sh:74
+msgid "You do not have the initial commit yet"
+msgstr "Du har inte den första incheckningen ännu"
+
+#: git-stash.sh:89
+msgid "Cannot save the current index state"
+msgstr "Kan inte spara aktuellt tillstånd för indexet"
+
+#: git-stash.sh:123 git-stash.sh:136
+msgid "Cannot save the current worktree state"
+msgstr "Kan inte spara aktuellt tillstånd för arbetskatalogen"
+
+#: git-stash.sh:140
+msgid "No changes selected"
+msgstr "Inga ändringar valda"
+
+#: git-stash.sh:143
+msgid "Cannot remove temporary index (can't happen)"
+msgstr "Kan inte ta bort temporärt index (kan inte inträffa)"
+
+#: git-stash.sh:156
+msgid "Cannot record working tree state"
+msgstr "Kan inte registrera tillstånd för arbetskatalog"
+
+#. TRANSLATORS: $option is an invalid option, like
+#. `--blah-blah'. The 7 spaces at the beginning of the
+#. second line correspond to "error: ". So you should line
+#. up the second line with however many characters the
+#. translation of "error: " takes in your language. E.g. in
+#. English this is:
+#.
+#. $ git stash save --blah-blah 2>&1 | head -n 2
+#. error: unknown option for 'stash save': --blah-blah
+#. To provide a message, use git stash save -- '--blah-blah'
+#: git-stash.sh:202
+#, sh-format
+msgid ""
+"error: unknown option for 'stash save': $option\n"
+"       To provide a message, use git stash save -- '$option'"
+msgstr ""
+"fel: felaktig flagga för \"stash save\": $option\n"
+"     För att ange ett meddelande, använd git stash save -- \"$option\""
+
+#: git-stash.sh:223
+msgid "No local changes to save"
+msgstr "Inga lokala ändringar att spara"
+
+#: git-stash.sh:227
+msgid "Cannot initialize stash"
+msgstr "Kan inte initiera \"stash\""
+
+#: git-stash.sh:235
+msgid "Cannot save the current status"
+msgstr "Kan inte spara aktuell status"
+
+#: git-stash.sh:253
+msgid "Cannot remove worktree changes"
+msgstr "Kan inte ta bort ändringar i arbetskatalogen"
+
+#: git-stash.sh:352
+msgid "No stash found."
+msgstr "Ingen \"stash\" hittades."
+
+#: git-stash.sh:359
+#, sh-format
+msgid "Too many revisions specified: $REV"
+msgstr "För många revisioner angivna: $REV"
+
+#: git-stash.sh:365
+#, sh-format
+msgid "$reference is not valid reference"
+msgstr "$reference är inte en giltig referens."
+
+#: git-stash.sh:393
+#, sh-format
+msgid "'$args' is not a stash-like commit"
+msgstr "\"$args\" är inte en \"stash\"-liknande incheckning"
+
+#: git-stash.sh:404
+#, sh-format
+msgid "'$args' is not a stash reference"
+msgstr "\"$args\" är inte en \"stash\"-referens"
+
+#: git-stash.sh:412
+msgid "unable to refresh index"
+msgstr "kan inte uppdatera indexet"
+
+#: git-stash.sh:416
+msgid "Cannot apply a stash in the middle of a merge"
+msgstr "Kan inte tillämpa en \"stash\" mitt i en sammanslagning"
+
+#: git-stash.sh:424
+msgid "Conflicts in index. Try without --index."
+msgstr "Konflikter i indexet. Testa utan --index."
+
+#: git-stash.sh:426
+msgid "Could not save index tree"
+msgstr "Kunde inte spara indexträd"
+
+#: git-stash.sh:460
+msgid "Cannot unstage modified files"
+msgstr "Kan inte ta bort ändrade filer ur kön"
+
+#: git-stash.sh:474
+msgid "Index was not unstashed."
+msgstr "Indexet har inte tagits ur kön."
+
+#: git-stash.sh:491
+#, sh-format
+msgid "Dropped ${REV} ($s)"
+msgstr "Kastade ${REV} ($s)"
+
+#: git-stash.sh:492
+#, sh-format
+msgid "${REV}: Could not drop stash entry"
+msgstr "${REV}: Kunde inte kasta \"stash\"-post"
+
+#: git-stash.sh:499
+msgid "No branch name specified"
+msgstr "Inget grennamn angavs"
+
+#: git-stash.sh:570
+msgid "(To restore them type \"git stash apply\")"
+msgstr "(För att återställa dem, skriv \"git stash apply\")"
+
+#: git-submodule.sh:88
+#, sh-format
+msgid "cannot strip one component off url '$remoteurl'"
+msgstr "kan inte ta bort en komponent från url:en \"$remoteurl\""
+
+#: git-submodule.sh:145
+#, sh-format
+msgid "No submodule mapping found in .gitmodules for path '$sm_path'"
+msgstr ""
+"Hittade ingen undermodulmappning i .gitmodules för sökvägen \"$sm_path\""
+
+#: git-submodule.sh:186
+#, sh-format
+msgid "Clone of '$url' into submodule path '$sm_path' failed"
+msgstr "Misslyckades klona \"$url\" till undermodulsökvägen \"$sm_path\""
+
+#: git-submodule.sh:196
+#, sh-format
+msgid "Gitdir '$a' is part of the submodule path '$b' or vice versa"
+msgstr "Gitkatalog \"$a\" ingår i underkatalogsökvägen \"$b\" eller omvänt"
+
+#: git-submodule.sh:285
+#, sh-format
+msgid "repo URL: '$repo' must be absolute or begin with ./|../"
+msgstr "arkiv-URL: \"$repo\" måste vara absolut eller börja med ./|../"
+
+#: git-submodule.sh:302
+#, sh-format
+msgid "'$sm_path' already exists in the index"
+msgstr "\"$sm_path\" finns redan i indexet"
+
+#: git-submodule.sh:306
+#, sh-format
+msgid ""
+"The following path is ignored by one of your .gitignore files:\n"
+"$sm_path\n"
+"Use -f if you really want to add it."
+msgstr ""
+"Följande sökvägar ignoreras av en av dina .gitignore-filer:\n"
+"$sm_path\n"
+"Använd -f om du verkligen vill lägga till den"
+
+#: git-submodule.sh:317
+#, sh-format
+msgid "Adding existing repo at '$sm_path' to the index"
+msgstr "Lägger till befintligt arkiv i \"$sm_path\" i indexet"
+
+#: git-submodule.sh:319
+#, sh-format
+msgid "'$sm_path' already exists and is not a valid git repo"
+msgstr "\"$sm_path\" finns redan och är inte ett giltigt git-arkiv"
+
+#: git-submodule.sh:333
+#, sh-format
+msgid "Unable to checkout submodule '$sm_path'"
+msgstr "Kan inte checka ut undermodulen \"$sm_path\""
+
+#: git-submodule.sh:338
+#, sh-format
+msgid "Failed to add submodule '$sm_path'"
+msgstr "Misslyckades lägga till undermodulen \"$sm_path\""
+
+#: git-submodule.sh:343
+#, sh-format
+msgid "Failed to register submodule '$sm_path'"
+msgstr "Misslyckades registrera undermodulen \"$sm_path\""
+
+#: git-submodule.sh:385
+#, sh-format
+msgid "Entering '$prefix$sm_path'"
+msgstr "Går in i \"$prefix$sm_path\""
+
+#: git-submodule.sh:399
+#, sh-format
+msgid "Stopping at '$sm_path'; script returned non-zero status."
+msgstr ""
+"Stoppar på \"$sm_path\"; skriptet returnerade en status skild från noll."
+
+#: git-submodule.sh:442
+#, sh-format
+msgid "No url found for submodule path '$sm_path' in .gitmodules"
+msgstr "Hittade ingen url för undermodulsökvägen \"$sm_path\" i .gitmodules"
+
+#: git-submodule.sh:451
+#, sh-format
+msgid "Failed to register url for submodule path '$sm_path'"
+msgstr "Misslyckades registrera url för underkatalogsökväg \"$sm_path\""
+
+#: git-submodule.sh:453
+#, sh-format
+msgid "Submodule '$name' ($url) registered for path '$sm_path'"
+msgstr "Undermodulen \"$name\" ($url) registrerad för sökvägen \"$sm_path\""
+
+#: git-submodule.sh:461
+#, sh-format
+msgid "Failed to register update mode for submodule path '$sm_path'"
+msgstr ""
+"Misslyckades registrera uppdateringsläge för undermodulsökväg \"$sm_path\""
+
+#: git-submodule.sh:560
+#, sh-format
+msgid ""
+"Submodule path '$sm_path' not initialized\n"
+"Maybe you want to use 'update --init'?"
+msgstr ""
+"Undermodulen \"$sm_path\" har inte initierats\n"
+"Kanske du vill köra \"update --init\"?"
+
+#: git-submodule.sh:573
+#, sh-format
+msgid "Unable to find current revision in submodule path '$sm_path'"
+msgstr "Kan inte hitta aktuell revision i undermodulsökväg \"$sm_path\""
+
+#: git-submodule.sh:592
+#, sh-format
+msgid "Unable to fetch in submodule path '$sm_path'"
+msgstr "Kan inte hämta i undermodulsökväg \"$sm_path\""
+
+#: git-submodule.sh:606
+#, sh-format
+msgid "Unable to rebase '$sha1' in submodule path '$sm_path'"
+msgstr "Kan inte ombasera \"$sha1\" i undermodulsökväg \"$sm_path\""
+
+#: git-submodule.sh:607
+#, sh-format
+msgid "Submodule path '$sm_path': rebased into '$sha1'"
+msgstr "Undermodulsökvägen \"$sm_path\": ombaserade in i \"$sha1\""
+
+#: git-submodule.sh:612
+#, sh-format
+msgid "Unable to merge '$sha1' in submodule path '$sm_path'"
+msgstr "Kan inte slå ihop \"$sha1\" i undermodulsökvägen \"$sm_path\""
+
+#: git-submodule.sh:613
+#, sh-format
+msgid "Submodule path '$sm_path': merged in '$sha1'"
+msgstr "Undermodulsökvägen \"$sm_path\": sammanslagen i \"$sha1\""
+
+#: git-submodule.sh:618
+#, sh-format
+msgid "Unable to checkout '$sha1' in submodule path '$sm_path'"
+msgstr "Kan inte checka ut \"$sha1\" i undermodulsökvägen \"$sm_path\""
+
+#: git-submodule.sh:619
+#, sh-format
+msgid "Submodule path '$sm_path': checked out '$sha1'"
+msgstr "Undermodulsökvägen \"$sm_path\": checkade ut \"$sha1\""
+
+#: git-submodule.sh:641 git-submodule.sh:964
+#, sh-format
+msgid "Failed to recurse into submodule path '$sm_path'"
+msgstr "Misslyckades rekursera in i undermodulsökvägen \"$sm_path\""
+
+#: git-submodule.sh:749
+msgid "--cached cannot be used with --files"
+msgstr "--cached kan inte användas med --files"
+
+#. unexpected type
+#: git-submodule.sh:789
+#, sh-format
+msgid "unexpected mode $mod_dst"
+msgstr "oväntat läge $mod_dst"
+
+#: git-submodule.sh:807
+#, sh-format
+msgid "  Warn: $name doesn't contain commit $sha1_src"
+msgstr "  Varning: $name innehåller inte incheckning $sha1_src"
+
+#: git-submodule.sh:810
+#, sh-format
+msgid "  Warn: $name doesn't contain commit $sha1_dst"
+msgstr "  Varning: $name innehåller inte incheckning $sha1_dst"
+
+#: git-submodule.sh:813
+#, sh-format
+msgid "  Warn: $name doesn't contain commits $sha1_src and $sha1_dst"
+msgstr "  Varning: $name innehåller inte incheckningar $sha1_src och $sha1_dst"
+
+#: git-submodule.sh:838
+msgid "blob"
+msgstr "blob"
+
+#: git-submodule.sh:839
+msgid "submodule"
+msgstr "undermodul"
+
+#: git-submodule.sh:876
+msgid "# Submodules changed but not updated:"
+msgstr "# Undermoduler ändrade men inte uppdaterade:"
+
+#: git-submodule.sh:878
+msgid "# Submodule changes to be committed:"
+msgstr "# Undermodulers ändringar att checka in:"
+
+#: git-submodule.sh:1022
+#, sh-format
+msgid "Synchronizing submodule url for '$name'"
+msgstr "Synkroniserar undermodul-url för \"$name\""
+
+#~ msgid "cherry-pick"
+#~ msgstr "cherry-pick"
+
+#~ msgid "Please enter the commit message for your changes."
+#~ msgstr "Ange ett incheckningsmeddelande för dina ändringar."
+
+#~ msgid "Could not extract email from committer identity."
+#~ msgstr "Kunde inte extrahera e-postadress från incheckarens identitet."
+
+#~ msgid "--"
+#~ msgstr "--"
+
+#~ msgid "Too many options specified"
+#~ msgstr "För många flaggor angavs"
+
+#~ msgid "# Changed but not updated:"
+#~ msgstr "# Ändrade men inte uppdaterade:"
+
+#~ msgid "A branch named '%s' already exists."
+#~ msgstr "Det finns redan en gren som heter \"%s\""
+
+#~ msgid "path '%s' does not have all 3 versions"
+#~ msgstr "sökvägen \"%s\" har inte alla 3 versionerna"
+
+#~ msgid "git checkout: we do not like '%s' as a branch name."
+#~ msgstr "git checkout: vi tycker inte om \"%s\" som namn på en gren."
+
+#~ msgid "git checkout: branch %s already exists"
+#~ msgstr "git checkout: grenen %s finns redan"
+
+#~ msgid "Paths with --interactive does not make sense."
+#~ msgstr "Kan inte ange sökvägar med --interactive."
+
+#~ msgid "No HEAD commit to compare with (yet)"
+#~ msgstr "Ingen HEAD-incheckning att jämföra med (ännu)"
+
+#~ msgid "cannot mix --fixed-strings and regexp"
+#~ msgstr "kan inte blanda --fixed-strings och reguljära uttryck"
+
+#~ msgid "invalid --decorate option: %s"
+#~ msgstr "felaktigt värde till --decorate: %s"
+
+#~ msgid "%s; will overwrite!"
+#~ msgstr "%s; kommer skriva över!"
+
+#~ msgid "Failed to write current notes tree to database"
+#~ msgstr "Kunde inte skriva aktuellt anteckningsträd till databasen"
+
+#~ msgid "Failed to commit notes tree to database"
+#~ msgstr "Kunde inte checka in anteckningsträd i databasen"
+
+# FIXME: Untranslatable!
+#
+#~ msgid "Refusing to %s notes in %s (outside of refs/notes/)"
+#~ msgstr "Vägrar %s anteckningar i %s (utanför refs/notes/)"
+
+#~ msgid "list"
+#~ msgstr "list"
+
+#~ msgid "add"
+#~ msgstr "add"
+
+#~ msgid "copy"
+#~ msgstr "copy"
+
+#~ msgid "show"
+#~ msgstr "show"
+
+#~ msgid "remove"
+#~ msgstr "remove"
+
+#~ msgid "prune"
+#~ msgstr "prune"
+
+#~ msgid "The current branch %s is not tracking anything."
+#~ msgstr "Den aktuella grenen %s spårar ingenting."
+
+#~ msgid "No destination configured to push to."
+#~ msgstr "Har inte ställt in någon destination att sända till."
+
+#~ msgid "Reflog action message too long: %.*s..."
+#~ msgstr "Reflog-händelsemeddelande för långt: %.*s..."
+
+#~ msgid "Could not read commit message of %s"
+#~ msgstr "Kunde inte läsa incheckningsmeddelandet för %s"
+
+#~ msgid "Could not extract author email from %s"
+#~ msgstr "Kunde inte hämta författarens e-postadress från %s"
+
+#~ msgid "Could not extract author time from %s"
+#~ msgstr "Kunde inte hämta författartid från %s"
+
+#~ msgid "No author information found in %s"
+#~ msgstr "Hittade ingen författarinformation i %s"
+
+#~ msgid "cherry-pick --ff cannot be used with --signoff"
+#~ msgstr "cherry-pick --ff kan inte användas med --signoff"
+
+#~ msgid "cherry-pick --ff cannot be used with --no-commit"
+#~ msgstr "cherry-pick --ff kan inte användas med --no-commit"
+
+#~ msgid "cherry-pick --ff cannot be used with -x"
+#~ msgstr "cherry-pick --ff kan inte användas med -x"
+
+#~ msgid "cherry-pick --ff cannot be used with --edit"
+#~ msgstr "cherry-pick --ff kan inte användas med --edit"
+
+#~ msgid "committer info too long."
+#~ msgstr "incheckarinformation för lång."
+
+#~ msgid ""
+#~ "\n"
+#~ "#\n"
+#~ "# Write a tag message\n"
+#~ "#\n"
+#~ msgstr ""
+#~ "\n"
+#~ "#\n"
+#~ "# Skriv ett taggmeddelande\n"
+#~ "#\n"
+
+#~ msgid "signing key value too long (%.10s...)"
+#~ msgstr "signeringsnyckelvärdet för långt (%.10s...)"
diff --git a/po/vi.po b/po/vi.po
new file mode 100644
index 0000000..9c8c6fe
--- /dev/null
+++ b/po/vi.po
@@ -0,0 +1,5698 @@
+# Vietnamese translation for GIT-CORE.
+# Copyright (C) 2012, Trần Ngọc Quân.
+# This file is distributed under the same license as the git-core package.
+# First translated by Trần Ngọc Quân <vnwildman@gmail.com>, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: git-1.7.11.1-107-g72601\n"
+"Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
+"POT-Creation-Date: 2012-07-03 10:23+0800\n"
+"PO-Revision-Date: 2012-07-03 14:21+0700\n"
+"Last-Translator: Trần Ngọc Quân <vnwildman@gmail.com>\n"
+"Language-Team: Vietnamese <translation-team-vi@lists.sourceforge.net>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: \n"
+"Plural-Forms: nplurals=2; plural=1;\n"
+"X-Poedit-Language: Vietnamese\n"
+"X-Poedit-Country: VIET NAM\n"
+"X-Poedit-SourceCharset: utf-8\n"
+"X-Poedit-Basepath: ../\n"
+
+#: advice.c:40
+#, c-format
+msgid "hint: %.*s\n"
+msgstr "gợi ý: %.*s\n"
+
+#.
+#. * Message used both when 'git commit' fails and when
+#. * other commands doing a merge do.
+#.
+#: advice.c:70
+msgid ""
+"Fix them up in the work tree,\n"
+"and then use 'git add/rm <file>' as\n"
+"appropriate to mark resolution and make a commit,\n"
+"or use 'git commit -a'."
+msgstr ""
+"Sửa chúng trong cây làm việc,\n"
+"và sau đó sử dụng lệnh 'git add/rm <tập-tin>'\n"
+"dành riêng cho việc đánh dấu cần giải quyết và tạo lần chuyển giao,\n"
+"hoặc là sử dụng lệnh 'git commit -a'."
+
+#: bundle.c:36
+#, c-format
+msgid "'%s' does not look like a v2 bundle file"
+msgstr "'%s' không giống như tập tin v2 bundle (cụm)"
+
+#: bundle.c:63
+#, c-format
+msgid "unrecognized header: %s%s (%d)"
+msgstr "phần đầu (header) không được thừa nhận: %s%s (%d)"
+
+#: bundle.c:89
+#: builtin/commit.c:696
+#, c-format
+msgid "could not open '%s'"
+msgstr "không thể mở '%s'"
+
+#: bundle.c:140
+msgid "Repository lacks these prerequisite commits:"
+msgstr "Khó chứa thiếu những lần chuyển giao (commit) cần trước hết này:"
+
+#: bundle.c:164
+#: sequencer.c:550
+#: sequencer.c:982
+#: builtin/log.c:290
+#: builtin/log.c:721
+#: builtin/log.c:1310
+#: builtin/log.c:1529
+#: builtin/merge.c:347
+#: builtin/shortlog.c:181
+msgid "revision walk setup failed"
+msgstr "Cài đặt việc di chuyển qua các điểm xét lại gặp lỗi"
+
+#: bundle.c:186
+#, c-format
+msgid "The bundle contains %d ref"
+msgid_plural "The bundle contains %d refs"
+msgstr[0] "Bundle chứa %d tham chiếu (refs)"
+msgstr[1] "Bundle chứa %d tham chiếu (refs)"
+
+#: bundle.c:192
+msgid "The bundle records a complete history."
+msgstr "Lệnh bundle ghi lại toàn bộ lịch sử."
+
+#: bundle.c:195
+#, c-format
+msgid "The bundle requires this ref"
+msgid_plural "The bundle requires these %d refs"
+msgstr[0] "Lệnh bundle yêu cầu tham chiếu (refs) này"
+msgstr[1] "Lệnh bundle yêu cầu %d tham chiếu (refs) này"
+
+#: bundle.c:294
+msgid "rev-list died"
+msgstr "rev-list bị chết"
+
+#: bundle.c:300
+#: builtin/log.c:1206
+#: builtin/shortlog.c:284
+#, c-format
+msgid "unrecognized argument: %s"
+msgstr "đối số không được thừa nhận: %s"
+
+#: bundle.c:335
+#, c-format
+msgid "ref '%s' is excluded by the rev-list options"
+msgstr "tham chiếu '%s' bị loại trừ bởi các tùy chọn rev-list"
+
+#: bundle.c:380
+msgid "Refusing to create empty bundle."
+msgstr "Từ chối tạo một bundle trống rỗng."
+
+#: bundle.c:398
+msgid "Could not spawn pack-objects"
+msgstr "Không thể sản sinh pack-objects"
+
+#: bundle.c:416
+msgid "pack-objects died"
+msgstr "pack-objects đã chết"
+
+#: bundle.c:419
+#, c-format
+msgid "cannot create '%s'"
+msgstr "không thể tạo '%s'"
+
+#: bundle.c:441
+msgid "index-pack died"
+msgstr "index-pack đã chết"
+
+#: commit.c:48
+#, c-format
+msgid "could not parse %s"
+msgstr "không thể phân tích %s"
+
+#: commit.c:50
+#, c-format
+msgid "%s %s is not a commit!"
+msgstr "%s %s không phải là một lần commit!"
+
+#: compat/obstack.c:406
+#: compat/obstack.c:408
+msgid "memory exhausted"
+msgstr "cạn bộ nhớ"
+
+#: connected.c:39
+msgid "Could not run 'git rev-list'"
+msgstr "Không thể chạy 'git rev-list'"
+
+#: connected.c:48
+#, c-format
+msgid "failed write to rev-list: %s"
+msgstr "gặp lỗi khi ghi vào rev-list: %s"
+
+#: connected.c:56
+#, c-format
+msgid "failed to close rev-list's stdin: %s"
+msgstr "gặp lỗi khi đóng đầu vào chuẩn stdin của rev-list: %s"
+
+#: date.c:95
+msgid "in the future"
+msgstr "trong tương lai"
+
+#: date.c:101
+#, c-format
+msgid "%lu second ago"
+msgid_plural "%lu seconds ago"
+msgstr[0] "%lu giây trước"
+msgstr[1] "%lu giây trước"
+
+#: date.c:108
+#, c-format
+msgid "%lu minute ago"
+msgid_plural "%lu minutes ago"
+msgstr[0] "%lu phút trước"
+msgstr[1] "%lu phút trước"
+
+#: date.c:115
+#, c-format
+msgid "%lu hour ago"
+msgid_plural "%lu hours ago"
+msgstr[0] "%lu giờ trước"
+msgstr[1] "%lu giờ trước"
+
+#: date.c:122
+#, c-format
+msgid "%lu day ago"
+msgid_plural "%lu days ago"
+msgstr[0] "%lu ngày trước"
+msgstr[1] "%lu ngày trước"
+
+#: date.c:128
+#, c-format
+msgid "%lu week ago"
+msgid_plural "%lu weeks ago"
+msgstr[0] "%lu tuần trước"
+msgstr[1] "%lu tuần trước"
+
+#: date.c:135
+#, c-format
+msgid "%lu month ago"
+msgid_plural "%lu months ago"
+msgstr[0] "%lu tháng trước"
+msgstr[1] "%lu tháng trước"
+
+#: date.c:146
+#, c-format
+msgid "%lu year"
+msgid_plural "%lu years"
+msgstr[0] "%lu năm"
+msgstr[1] "%lu năm"
+
+#: date.c:149
+#, c-format
+msgid "%s, %lu month ago"
+msgid_plural "%s, %lu months ago"
+msgstr[0] "%s, %lu tháng trước"
+msgstr[1] "%s, %lu tháng trước"
+
+#: date.c:154
+#: date.c:159
+#, c-format
+msgid "%lu year ago"
+msgid_plural "%lu years ago"
+msgstr[0] "%lu năm trước"
+msgstr[1] "%lu năm trước"
+
+#: diff.c:105
+#, c-format
+msgid "  Failed to parse dirstat cut-off percentage '%.*s'\n"
+msgstr "  Gặp lỗi khi phân tích dirstat cắt bỏ phần trăm '%.*s'\n"
+
+#: diff.c:110
+#, c-format
+msgid "  Unknown dirstat parameter '%.*s'\n"
+msgstr "  Không hiểu đối số dirstat '%.*s'\n"
+
+#: diff.c:210
+#, c-format
+msgid ""
+"Found errors in 'diff.dirstat' config variable:\n"
+"%s"
+msgstr ""
+"Tìm thấy các lỗi trong biến cấu hình 'diff.dirstat':\n"
+"%s"
+
+#: diff.c:1400
+msgid " 0 files changed\n"
+msgstr " 0 tập tin nào bị thay đổi\n"
+
+#: diff.c:1404
+#, c-format
+msgid " %d file changed"
+msgid_plural " %d files changed"
+msgstr[0] " %d tập tin đã bị thay đổi"
+msgstr[1] " %d tập tin đã bị thay đổi"
+
+#: diff.c:1421
+#, c-format
+msgid ", %d insertion(+)"
+msgid_plural ", %d insertions(+)"
+msgstr[0] ", %d được thêm vào(+)"
+msgstr[1] ", %d được thêm vào(+)"
+
+#: diff.c:1432
+#, c-format
+msgid ", %d deletion(-)"
+msgid_plural ", %d deletions(-)"
+msgstr[0] ", %d bị xóa(-)"
+msgstr[1] ", %d bị xóa(-)"
+
+#: diff.c:3478
+#, c-format
+msgid ""
+"Failed to parse --dirstat/-X option parameter:\n"
+"%s"
+msgstr ""
+"Gặp lỗi khi phân tích đối số tùy chọn --dirstat/-X:\n"
+"%s"
+
+#: gpg-interface.c:59
+msgid "could not run gpg."
+msgstr "không thể chạy gpg."
+
+#: gpg-interface.c:71
+msgid "gpg did not accept the data"
+msgstr "gpg đã không đồng ý dữ liệu"
+
+#: gpg-interface.c:82
+msgid "gpg failed to sign the data"
+msgstr "gpg gặp lỗi khi ký dữ liệu"
+
+#: grep.c:1320
+#, c-format
+msgid "'%s': unable to read %s"
+msgstr "'%s': không thể đọc %s"
+
+#: grep.c:1337
+#, c-format
+msgid "'%s': %s"
+msgstr "'%s': %s"
+
+#: grep.c:1348
+#, c-format
+msgid "'%s': short read %s"
+msgstr "'%s': đọc ngắn %s"
+
+#: help.c:208
+#, c-format
+msgid "available git commands in '%s'"
+msgstr "các lệnh git sẵn sàng để dùng trong '%s'"
+
+#: help.c:215
+msgid "git commands available from elsewhere on your $PATH"
+msgstr "các lệnh git sẵn sàng để dùng từ một nơi khác trong $PATH của bạn"
+
+#: help.c:271
+#, c-format
+msgid ""
+"'%s' appears to be a git command, but we were not\n"
+"able to execute it. Maybe git-%s is broken?"
+msgstr ""
+"'%s' trông như là một lệnh git, nhưng chúng tôi không\n"
+"thể thực thi nó. Có lẽ là lệnh git-%s đã bị hỏng?"
+
+#: help.c:328
+msgid "Uh oh. Your system reports no Git commands at all."
+msgstr "Ối chà. Hệ thống của bạn báo rằng chẳng có lệnh Git nào cả."
+
+#: help.c:350
+#, c-format
+msgid ""
+"WARNING: You called a Git command named '%s', which does not exist.\n"
+"Continuing under the assumption that you meant '%s'"
+msgstr ""
+"CẢNH BÁO: Bạn đã gọi lệnh Git có tên '%s', mà nó lại không sẵn có.\n"
+"Giả định rằng ý bạn là '%s'"
+
+#: help.c:355
+#, c-format
+msgid "in %0.1f seconds automatically..."
+msgstr "trong %0.1f giây một cách tự động..."
+
+#: help.c:362
+#, c-format
+msgid "git: '%s' is not a git command. See 'git --help'."
+msgstr "git: '%s' không phải là một lệnh của git. Xem thêm 'git --help'."
+
+#: help.c:366
+msgid ""
+"\n"
+"Did you mean this?"
+msgid_plural ""
+"\n"
+"Did you mean one of these?"
+msgstr[0] ""
+"\n"
+"Có phải ý bạn là cái này không?"
+msgstr[1] ""
+"\n"
+"Có phải ý bạn là một trong số những cái này không?"
+
+#: parse-options.c:493
+msgid "..."
+msgstr "..."
+
+#: parse-options.c:511
+#, c-format
+msgid "usage: %s"
+msgstr "cách sử dụng: %s"
+
+#. TRANSLATORS: the colon here should align with the
+#. one in "usage: %s" translation
+#: parse-options.c:515
+#, c-format
+msgid "   or: %s"
+msgstr "   hoặc: %s"
+
+#: parse-options.c:518
+#, c-format
+msgid "    %s"
+msgstr "    %s"
+
+#: remote.c:1629
+#, c-format
+msgid "Your branch is ahead of '%s' by %d commit.\n"
+msgid_plural "Your branch is ahead of '%s' by %d commits.\n"
+msgstr[0] "Nhánh của bạn là đầu của '%s' bởi %d lần chuyển giao (commit).\n"
+msgstr[1] "Nhánh của bạn là đầu của '%s' bởi %d lần chuyển giao (commit).\n"
+
+#: remote.c:1635
+#, c-format
+msgid "Your branch is behind '%s' by %d commit, and can be fast-forwarded.\n"
+msgid_plural "Your branch is behind '%s' by %d commits, and can be fast-forwarded.\n"
+msgstr[0] "Nhánh của bạn thì ở đằng sau '%s' bởi %d lần chuyển giao (commit), và có thể được fast-forward.\n"
+msgstr[1] "Nhánh của bạn thì ở đằng sau '%s' bởi %d lần chuyển giao (commit), và có thể được fast-forward.\n"
+
+#: remote.c:1643
+#, c-format
+msgid ""
+"Your branch and '%s' have diverged,\n"
+"and have %d and %d different commit each, respectively.\n"
+msgid_plural ""
+"Your branch and '%s' have diverged,\n"
+"and have %d and %d different commits each, respectively.\n"
+msgstr[0] ""
+"Nhánh của bạn và '%s' bị phân kỳ,\n"
+"và có %d và %d lần chuyển giao (commit) khác nhau cho từng cái,\n"
+"tương ứng với mỗi lần.\n"
+msgstr[1] ""
+"Your branch and '%s' have diverged,\n"
+"and have %d and %d different commit each, respectively.\n"
+
+#: sequencer.c:121
+#: builtin/merge.c:865
+#: builtin/merge.c:978
+#: builtin/merge.c:1088
+#: builtin/merge.c:1098
+#, c-format
+msgid "Could not open '%s' for writing"
+msgstr "Không thể mở %s' để ghi"
+
+#: sequencer.c:123
+#: builtin/merge.c:333
+#: builtin/merge.c:868
+#: builtin/merge.c:1090
+#: builtin/merge.c:1103
+#, c-format
+msgid "Could not write to '%s'"
+msgstr "Không thể ghi vào '%s'"
+
+#: sequencer.c:144
+msgid ""
+"after resolving the conflicts, mark the corrected paths\n"
+"with 'git add <paths>' or 'git rm <paths>'"
+msgstr ""
+"sau khi giải quyết các xung đột, đánh dấu đường dẫn đã sửa\n"
+"với lệnh 'git add <đường_dẫn>' hoặc 'git rm <đường_dẫn>'"
+
+#: sequencer.c:147
+msgid ""
+"after resolving the conflicts, mark the corrected paths\n"
+"with 'git add <paths>' or 'git rm <paths>'\n"
+"and commit the result with 'git commit'"
+msgstr ""
+"sau khi giải quyết các xung đột, đánh dấu đường dẫn đã sửa\n"
+"với lệnh 'git add <đường_dẫn>' hoặc 'git rm <đường_dẫn>'\n"
+"và chuyển giao (commit) kết quả bằng lệnh 'git commit'"
+
+#: sequencer.c:160
+#: sequencer.c:758
+#: sequencer.c:841
+#, c-format
+msgid "Could not write to %s"
+msgstr "Không thể ghi vào %s"
+
+#: sequencer.c:163
+#, c-format
+msgid "Error wrapping up %s"
+msgstr "Lỗi bao bọc %s"
+
+#: sequencer.c:178
+msgid "Your local changes would be overwritten by cherry-pick."
+msgstr "Các thay đổi nội bộ của bạn có thể bị ghi đè bởi lệnh cherry-pick."
+
+#: sequencer.c:180
+msgid "Your local changes would be overwritten by revert."
+msgstr "Các thay đổi nội bộ của bạn có thể bị ghi đè bởi lệnh revert."
+
+#: sequencer.c:183
+msgid "Commit your changes or stash them to proceed."
+msgstr "Chuyển giao (commit) các thay đổi hay stash chúng để tiến hành."
+
+#. TRANSLATORS: %s will be "revert" or "cherry-pick"
+#: sequencer.c:233
+#, c-format
+msgid "%s: Unable to write new index file"
+msgstr "%s: Không thể ghi tập tin lưu bảng mục lục mới"
+
+#: sequencer.c:261
+msgid "Could not resolve HEAD commit\n"
+msgstr "Không thể phân giải commit (lần chuyển giao) HEAD\n"
+
+#: sequencer.c:282
+msgid "Unable to update cache tree\n"
+msgstr "Không thể cập nhật cây bộ nhớ đệm\n"
+
+#: sequencer.c:324
+#, c-format
+msgid "Could not parse commit %s\n"
+msgstr "Không thể phân tích commit (lần chuyển giao) %s\n"
+
+#: sequencer.c:329
+#, c-format
+msgid "Could not parse parent commit %s\n"
+msgstr "Không thể phân tích commit (lần chuyển giao) cha mẹ %s\n"
+
+#: sequencer.c:395
+msgid "Your index file is unmerged."
+msgstr "Tập tin lưu mục lục của bạn không được hòa trộn."
+
+#: sequencer.c:398
+msgid "You do not have a valid HEAD"
+msgstr "Bạn không có HEAD nào hợp lệ"
+
+#: sequencer.c:413
+#, c-format
+msgid "Commit %s is a merge but no -m option was given."
+msgstr "Lần chuyển giao (commit) %s là một lần hòa trộn nhưng không đưa ra tùy chọn  -m."
+
+#: sequencer.c:421
+#, c-format
+msgid "Commit %s does not have parent %d"
+msgstr "Lần chuyển giao (commit) %s không có cha mẹ %d"
+
+#: sequencer.c:425
+#, c-format
+msgid "Mainline was specified but commit %s is not a merge."
+msgstr "Luồng chính được chỉ định nhưng lần chuyển giao (commit) %s không phải là một lần hòa trộn."
+
+#. TRANSLATORS: The first %s will be "revert" or
+#. "cherry-pick", the second %s a SHA1
+#: sequencer.c:436
+#, c-format
+msgid "%s: cannot parse parent commit %s"
+msgstr "%s: không thể phân tích lần chuyển giao mẹ của %s"
+
+#: sequencer.c:440
+#, c-format
+msgid "Cannot get commit message for %s"
+msgstr "Không thể lấy thông điệp lần chuyển giao (commit) cho %s"
+
+#: sequencer.c:524
+#, c-format
+msgid "could not revert %s... %s"
+msgstr "không thể revert %s... %s"
+
+#: sequencer.c:525
+#, c-format
+msgid "could not apply %s... %s"
+msgstr "không thể apply (áp dụng miếng vá) %s... %s"
+
+#: sequencer.c:553
+msgid "empty commit set passed"
+msgstr "lần chuyển giao (commit) trống rỗng đặt là hợp quy cách"
+
+#: sequencer.c:561
+#, c-format
+msgid "git %s: failed to read the index"
+msgstr "git %s: gặp lỗi đọc bảng mục lục"
+
+#: sequencer.c:566
+#, c-format
+msgid "git %s: failed to refresh the index"
+msgstr "git %s: gặp lỗi khi làm tươi mới bảng mục lục"
+
+#: sequencer.c:624
+#, c-format
+msgid "Cannot %s during a %s"
+msgstr "Không thể %s trong khi %s"
+
+#: sequencer.c:646
+#, c-format
+msgid "Could not parse line %d."
+msgstr "Không phân tích được dòng %d."
+
+#: sequencer.c:651
+msgid "No commits parsed."
+msgstr "Không có lần chuyển giao (commit) nào được phân tích."
+
+#: sequencer.c:664
+#, c-format
+msgid "Could not open %s"
+msgstr "Không thể mở %s"
+
+#: sequencer.c:668
+#, c-format
+msgid "Could not read %s."
+msgstr "Không thể đọc %s."
+
+#: sequencer.c:675
+#, c-format
+msgid "Unusable instruction sheet: %s"
+msgstr "Bảng chỉ thị không thể dùng được: %s"
+
+#: sequencer.c:703
+#, c-format
+msgid "Invalid key: %s"
+msgstr "Khóa không đúng: %s"
+
+#: sequencer.c:706
+#, c-format
+msgid "Invalid value for %s: %s"
+msgstr "Giá trị không hợp lệ %s: %s"
+
+#: sequencer.c:718
+#, c-format
+msgid "Malformed options sheet: %s"
+msgstr "Bảng tùy chọn dị hình: %s"
+
+#: sequencer.c:739
+msgid "a cherry-pick or revert is already in progress"
+msgstr "một thao tác cherry-pick hoặc revert đang được thực hiện"
+
+#: sequencer.c:740
+msgid "try \"git cherry-pick (--continue | --quit | --abort)\""
+msgstr "hãy thử \"git cherry-pick (--continue | --quit | --abort)\""
+
+#: sequencer.c:744
+#, c-format
+msgid "Could not create sequencer directory %s"
+msgstr "Không thể tạo thư mục xếp dãy %s"
+
+#: sequencer.c:760
+#: sequencer.c:845
+#, c-format
+msgid "Error wrapping up %s."
+msgstr "Lỗi bao bọc %s."
+
+#: sequencer.c:779
+#: sequencer.c:913
+msgid "no cherry-pick or revert in progress"
+msgstr "không cherry-pick hay revert trong tiến trình"
+
+#: sequencer.c:781
+msgid "cannot resolve HEAD"
+msgstr "không thể phân giải HEAD"
+
+#: sequencer.c:783
+msgid "cannot abort from a branch yet to be born"
+msgstr "không thể hủy bỏ từ một nhánh mà nó còn chưa được tạo ra"
+
+#: sequencer.c:805
+#: builtin/apply.c:3697
+#, c-format
+msgid "cannot open %s: %s"
+msgstr "không thể mở %s: %s"
+
+#: sequencer.c:808
+#, c-format
+msgid "cannot read %s: %s"
+msgstr "không thể đọc %s: %s"
+
+#: sequencer.c:809
+msgid "unexpected end of file"
+msgstr "kết thúc tập tin đột xuất"
+
+#: sequencer.c:815
+#, c-format
+msgid "stored pre-cherry-pick HEAD file '%s' is corrupt"
+msgstr "tập tin HEAD 'pre-cherry-pick' đã lưu '%s' bị hỏng"
+
+#: sequencer.c:838
+#, c-format
+msgid "Could not format %s."
+msgstr "Không thể định dạng %s."
+
+#: sequencer.c:1000
+msgid "Can't revert as initial commit"
+msgstr "Không thể revert một lần chuyển giao (commit) khởi tạo"
+
+#: sequencer.c:1001
+msgid "Can't cherry-pick into empty head"
+msgstr "Không thể cherry-pick vào một đầu (head) trống rỗng"
+
+#: sha1_name.c:864
+msgid "HEAD does not point to a branch"
+msgstr "HEAD không chỉ đến một nhánh nào cả"
+
+#: sha1_name.c:867
+#, c-format
+msgid "No such branch: '%s'"
+msgstr "Không có nhánh nào như thế: '%s'"
+
+#: sha1_name.c:869
+#, c-format
+msgid "No upstream configured for branch '%s'"
+msgstr "Không có dòng ngược (upstream) được cấu hình cho nhánh '%s'"
+
+#: sha1_name.c:872
+#, c-format
+msgid "Upstream branch '%s' not stored as a remote-tracking branch"
+msgstr "Nhánh dòng ngược (upstream) '%s' không được lưu lại như là một nhánh 'remote-tracking'"
+
+#: wrapper.c:413
+#, c-format
+msgid "unable to look up current user in the passwd file: %s"
+msgstr "không tìm thấy người dùng hiện tại trong tập tin passwd: %s"
+
+#: wrapper.c:414
+msgid "no such user"
+msgstr "không có người dùng như vậy"
+
+#: wt-status.c:141
+msgid "Unmerged paths:"
+msgstr "Những đường dẫn chưa được hòa trộn:"
+
+#: wt-status.c:168
+#: wt-status.c:195
+#, c-format
+msgid "  (use \"git reset %s <file>...\" to unstage)"
+msgstr "  (sử dụng \"git reset %s <tập-tin>...\" để bỏ một stage (trạng thái))"
+
+#: wt-status.c:170
+#: wt-status.c:197
+msgid "  (use \"git rm --cached <file>...\" to unstage)"
+msgstr "  (sử dụng \"git rm --cached <tập-tin>...\" để bỏ trạng thái (stage))"
+
+#: wt-status.c:174
+msgid "  (use \"git add <file>...\" to mark resolution)"
+msgstr "  (sử dụng \"git add <tập-tin>...\" để đánh dấu là cần giải quyết)"
+
+#: wt-status.c:176
+#: wt-status.c:180
+msgid "  (use \"git add/rm <file>...\" as appropriate to mark resolution)"
+msgstr "  (sử dụng \"git add/rm <tập-tin>...\" như là một cách  thích hợp để đánh dấu là cần được giải quyết)"
+
+#: wt-status.c:178
+msgid "  (use \"git rm <file>...\" to mark resolution)"
+msgstr "  (sử dụng \"git rm <tập-tin>...\" để đánh dấu là cần giải quyết)"
+
+#: wt-status.c:189
+msgid "Changes to be committed:"
+msgstr "Những thay đổi sẽ được chuyển giao:"
+
+#: wt-status.c:207
+msgid "Changes not staged for commit:"
+msgstr "Các thay đổi không được đặt trạng thái (stage) cho lần chuyển giao (commit):"
+
+#: wt-status.c:211
+msgid "  (use \"git add <file>...\" to update what will be committed)"
+msgstr "  (sử dụng \"git add <tập-tin>...\" để cập nhật những gì cần chuyển giao (commit))"
+
+#: wt-status.c:213
+msgid "  (use \"git add/rm <file>...\" to update what will be committed)"
+msgstr "  (sử dụng \"git add/rm <tập_tin>...\" để cập nhật những gì sẽ được chuyển giao)"
+
+#: wt-status.c:214
+msgid "  (use \"git checkout -- <file>...\" to discard changes in working directory)"
+msgstr "  (sử dụng \"git checkout -- <tập_tin>...\" để loại bỏ những thay đổi trong thư mục làm việc)"
+
+#: wt-status.c:216
+msgid "  (commit or discard the untracked or modified content in submodules)"
+msgstr "  (chuyển giao (commit) hoặc là loại bỏ các nội dung không-bị-theo-vết hay đã bị chỉnh sửa trong mô-đun-con)"
+
+#: wt-status.c:225
+#, c-format
+msgid "%s files:"
+msgstr "%s tệp tin:"
+
+#: wt-status.c:228
+#, c-format
+msgid "  (use \"git %s <file>...\" to include in what will be committed)"
+msgstr "  (sử dụng \"git %s <tập-tin>...\" để bao gồm thêm vào những gì cần chuyển giao (commit))"
+
+#: wt-status.c:245
+msgid "bug"
+msgstr "lỗi"
+
+#: wt-status.c:250
+msgid "both deleted:"
+msgstr "bị xóa bởi cả hai:"
+
+#: wt-status.c:251
+msgid "added by us:"
+msgstr "được thêm vào bởi chúng tôi:"
+
+#: wt-status.c:252
+msgid "deleted by them:"
+msgstr "bị xóa đi bởi họ:"
+
+#: wt-status.c:253
+msgid "added by them:"
+msgstr "được thêm vào bởi họ:"
+
+#: wt-status.c:254
+msgid "deleted by us:"
+msgstr "bị xóa bởi chúng tôi:"
+
+#: wt-status.c:255
+msgid "both added:"
+msgstr "được thêm vào bởi cả hai:"
+
+#: wt-status.c:256
+msgid "both modified:"
+msgstr "bị sửa bởi cả hai:"
+
+#: wt-status.c:286
+msgid "new commits, "
+msgstr " lần chuyển giao (commit) mới, "
+
+#: wt-status.c:288
+msgid "modified content, "
+msgstr "nội dung được sửa đổi,"
+
+#: wt-status.c:290
+msgid "untracked content, "
+msgstr "nội dung chưa được theo dõi"
+
+#: wt-status.c:304
+#, c-format
+msgid "new file:   %s"
+msgstr "tập tin mới:   %s"
+
+#: wt-status.c:307
+#, c-format
+msgid "copied:     %s -> %s"
+msgstr "đã sao chép:     %s -> %s"
+
+#: wt-status.c:310
+#, c-format
+msgid "deleted:    %s"
+msgstr "bị xóa:    %s"
+
+#: wt-status.c:313
+#, c-format
+msgid "modified:   %s"
+msgstr "bị sửa đổi:   %s"
+
+#: wt-status.c:316
+#, c-format
+msgid "renamed:    %s -> %s"
+msgstr "đã đổi tên:    %s -> %s"
+
+#: wt-status.c:319
+#, c-format
+msgid "typechange: %s"
+msgstr "đổi-kiểu: %s"
+
+#: wt-status.c:322
+#, c-format
+msgid "unknown:    %s"
+msgstr "không rõ:    %s"
+
+#: wt-status.c:325
+#, c-format
+msgid "unmerged:   %s"
+msgstr "chưa hòa trộn:   %s"
+
+#: wt-status.c:328
+#, c-format
+msgid "bug: unhandled diff status %c"
+msgstr "lỗi: không lấy được trạng thái lệnh diff %c"
+
+#: wt-status.c:786
+msgid "You have unmerged paths."
+msgstr "Bạn có những đường dẫn chưa được hòa trộn."
+
+#: wt-status.c:789
+#: wt-status.c:913
+msgid "  (fix conflicts and run \"git commit\")"
+msgstr "  (sửa các xung đột sau đó chạy \"git commit\")"
+
+#: wt-status.c:792
+msgid "All conflicts fixed but you are still merging."
+msgstr "Tất cả các xung đột đã được giải quyết nhưng bạn vẫn đang hòa trộn."
+
+#: wt-status.c:795
+msgid "  (use \"git commit\" to conclude merge)"
+msgstr "  (sử dụng \"git commit\" để hoàn tất việc hòa trộn)"
+
+#: wt-status.c:805
+msgid "You are in the middle of an am session."
+msgstr "Bạn đang ở giữa của một phiên 'am'."
+
+#: wt-status.c:808
+msgid "The current patch is empty."
+msgstr "Miếng vá hiện tại bị trống rỗng."
+
+#: wt-status.c:812
+msgid "  (fix conflicts and then run \"git am --resolved\")"
+msgstr "  (sửa các xung đột và sau đó chạy lệnh \"git am --resolved\")"
+
+#: wt-status.c:814
+msgid "  (use \"git am --skip\" to skip this patch)"
+msgstr "  (sử dụng \"git am --skip\" để bỏ qua lần vá này)"
+
+#: wt-status.c:816
+msgid "  (use \"git am --abort\" to restore the original branch)"
+msgstr "  (sử dụng \"git am --abort\" để phục hồi lại nhánh nguyên thủy)"
+
+#: wt-status.c:874
+#: wt-status.c:884
+msgid "You are currently rebasing."
+msgstr "Bạn hiện nay đang thực hiện việc rebase (tái cấu trúc)."
+
+#: wt-status.c:877
+msgid "  (fix conflicts and then run \"git rebase --continue\")"
+msgstr "  (sửa các xung đột và sau đó chạy lệnh \"git rebase --continue\")"
+
+#: wt-status.c:879
+msgid "  (use \"git rebase --skip\" to skip this patch)"
+msgstr "  (sử dụng \"git rebase --skip\" để bỏ qua lần vá này)"
+
+#: wt-status.c:881
+msgid "  (use \"git rebase --abort\" to check out the original branch)"
+msgstr "  (sử dụng \"git rebase --abort\" để check-out nhánh nguyên thủy)"
+
+#: wt-status.c:887
+msgid "  (all conflicts fixed: run \"git rebase --continue\")"
+msgstr "  (khi tất cả các xung đột đã sửa xong: chạy lệnh \"git rebase --continue\")"
+
+#: wt-status.c:889
+msgid "You are currently splitting a commit during a rebase."
+msgstr "Bạn hiện tại đang cắt đôi một lần chuyển giao trong khi đang thực hiện việc rebase."
+
+#: wt-status.c:892
+msgid "  (Once your working directory is clean, run \"git rebase --continue\")"
+msgstr "  (Một khi thư mục làm việc của bạn đã gọn gàng, chạy \"git rebase --continue\")"
+
+#: wt-status.c:894
+msgid "You are currently editing a commit during a rebase."
+msgstr "Bạn hiện đang sửa một lần chuyển giao trong khi bạn thực hiện rebase."
+
+#: wt-status.c:897
+msgid "  (use \"git commit --amend\" to amend the current commit)"
+msgstr "  (sử dụng \"git commit --amend\" để tu bổ lần chuyển giao (commit) hiện tại)"
+
+#: wt-status.c:899
+msgid "  (use \"git rebase --continue\" once you are satisfied with your changes)"
+msgstr "  (sử dụng \"git rebase --continue\" một khi bạn cảm thấy hài lòng về những thay đổi của mình)"
+
+#: wt-status.c:909
+msgid "You are currently cherry-picking."
+msgstr "Bạn hiện nay đang thực hiện việc cherry-pick."
+
+#: wt-status.c:916
+msgid "  (all conflicts fixed: run \"git commit\")"
+msgstr "  (khi tất cả các xung đột đã sửa xong: chạy lệnh \"git commit\")"
+
+#: wt-status.c:925
+msgid "You are currently bisecting."
+msgstr "Bạn hiện tại đang thực hiện việc bisect (chia đôi)."
+
+#: wt-status.c:928
+msgid "  (use \"git bisect reset\" to get back to the original branch)"
+msgstr "  (sử dụng \"git bisect reset\" để quay trở lại nhánh nguyên thủy)"
+
+#: wt-status.c:979
+msgid "On branch "
+msgstr "Trên nhánh"
+
+#: wt-status.c:986
+msgid "Not currently on any branch."
+msgstr "Hiện tại chẳng ở nhánh nào cả."
+
+#: wt-status.c:998
+msgid "Initial commit"
+msgstr "Lần chuyển giao (commit) khởi đầu"
+
+#: wt-status.c:1012
+msgid "Untracked"
+msgstr "Không được theo vết"
+
+#: wt-status.c:1014
+msgid "Ignored"
+msgstr "Bị bỏ qua"
+
+#: wt-status.c:1016
+#, c-format
+msgid "Untracked files not listed%s"
+msgstr "Những tập tin không bị theo vết không được liệt kê ra %s"
+
+#: wt-status.c:1018
+msgid " (use -u option to show untracked files)"
+msgstr " (sử dụng tùy chọn -u để hiển thị các tập tin chưa được theo dõi)"
+
+#: wt-status.c:1024
+msgid "No changes"
+msgstr "Không có thay đổi nào"
+
+#: wt-status.c:1028
+#, c-format
+msgid "no changes added to commit%s\n"
+msgstr "không có thay đổi nào được thêm vào lần chuyển giao (commit)%s\n"
+
+#: wt-status.c:1030
+msgid " (use \"git add\" and/or \"git commit -a\")"
+msgstr " (sử dụng \"git add\" và/hoặc \"git commit -a\")"
+
+#: wt-status.c:1032
+#, c-format
+msgid "nothing added to commit but untracked files present%s\n"
+msgstr "không có gì được thêm vào lần chuyển giao (commit) nhưng có những tập tin không được theo dấu vết hiện diện%s\n"
+
+#: wt-status.c:1034
+msgid " (use \"git add\" to track)"
+msgstr " (sử dụng \"git add\" để theo dõi dấu vết)"
+
+#: wt-status.c:1036
+#: wt-status.c:1039
+#: wt-status.c:1042
+#, c-format
+msgid "nothing to commit%s\n"
+msgstr "không có gì để chuyển giao (commit) %s\n"
+
+#: wt-status.c:1037
+msgid " (create/copy files and use \"git add\" to track)"
+msgstr " (tạo/sao-chép các tập tin và sử dụng \"git add\" để theo dõi dấu vết)"
+
+#: wt-status.c:1040
+msgid " (use -u to show untracked files)"
+msgstr " (sử dụng tùy chọn -u để hiển thị các tập tin chưa được theo dõi)"
+
+#: wt-status.c:1043
+msgid " (working directory clean)"
+msgstr " (thư mục làm việc sạch sẽ)"
+
+#: wt-status.c:1151
+msgid "HEAD (no branch)"
+msgstr "HEAD (chưa có nhánh nào)"
+
+#: wt-status.c:1157
+msgid "Initial commit on "
+msgstr "Lần chuyển giao (commit)  khởi tạo trên"
+
+#: wt-status.c:1172
+msgid "behind "
+msgstr "đằng sau"
+
+#: wt-status.c:1175
+#: wt-status.c:1178
+msgid "ahead "
+msgstr "phía trước"
+
+#: wt-status.c:1180
+msgid ", behind "
+msgstr ", đằng sau"
+
+#: builtin/add.c:62
+#, c-format
+msgid "unexpected diff status %c"
+msgstr "trạng thái lệnh diff không như mong đợi %c"
+
+#: builtin/add.c:67
+#: builtin/commit.c:226
+msgid "updating files failed"
+msgstr "Cập nhật tập tin gặp lỗi"
+
+#: builtin/add.c:77
+#, c-format
+msgid "remove '%s'\n"
+msgstr "gỡ bỏ '%s'\n"
+
+#: builtin/add.c:176
+#, c-format
+msgid "Path '%s' is in submodule '%.*s'"
+msgstr "Đường dẫn '%s' thì ở trong mô-đun-con '%.*s'"
+
+#: builtin/add.c:192
+msgid "Unstaged changes after refreshing the index:"
+msgstr "Các thay đổi không được lưu trạng thái sau khi làm tươi mới lại bảng mục lục:"
+
+#: builtin/add.c:195
+#: builtin/add.c:459
+#: builtin/rm.c:186
+#, c-format
+msgid "pathspec '%s' did not match any files"
+msgstr "pathspec '%s' không khớp với bất kỳ tập tin nào"
+
+#: builtin/add.c:209
+#, c-format
+msgid "'%s' is beyond a symbolic link"
+msgstr "'%s' nằm ngoài một liên kết tượng trưng"
+
+#: builtin/add.c:276
+msgid "Could not read the index"
+msgstr "Không thể đọc bảng mục lục"
+
+#: builtin/add.c:286
+#, c-format
+msgid "Could not open '%s' for writing."
+msgstr "Không thể mở '%s' để ghi"
+
+#: builtin/add.c:290
+msgid "Could not write patch"
+msgstr "Không thể ghi ra miếng vá"
+
+#: builtin/add.c:295
+#, c-format
+msgid "Could not stat '%s'"
+msgstr "không thể lấy trạng thái về '%s'"
+
+#: builtin/add.c:297
+msgid "Empty patch. Aborted."
+msgstr "Miếng vá trống rỗng. Đã bỏ qua."
+
+#: builtin/add.c:303
+#, c-format
+msgid "Could not apply '%s'"
+msgstr "Không thể apply (áp dụng miếng vá) '%s'"
+
+#: builtin/add.c:312
+msgid "The following paths are ignored by one of your .gitignore files:\n"
+msgstr "Các đường dẫn theo sau đây sẽ bị lờ đi bởi một trong các tập tin .gitignore của bạn:\n"
+
+#: builtin/add.c:352
+#, c-format
+msgid "Use -f if you really want to add them.\n"
+msgstr "Sử dụng tùy chọn -f nếu bạn thực sự muốn thêm chúng vào.\n"
+
+#: builtin/add.c:353
+msgid "no files added"
+msgstr "chưa có tập tin nào được thêm vào"
+
+#: builtin/add.c:359
+msgid "adding files failed"
+msgstr "thêm tập tin gặp lỗi"
+
+#: builtin/add.c:391
+msgid "-A and -u are mutually incompatible"
+msgstr "-A và -u xung khắc nhau"
+
+#: builtin/add.c:393
+msgid "Option --ignore-missing can only be used together with --dry-run"
+msgstr "Tùy chọn --ignore-missing chỉ có thể được sử dụng cùng với --dry-run"
+
+#: builtin/add.c:413
+#, c-format
+msgid "Nothing specified, nothing added.\n"
+msgstr "Không có gì được chỉ ra, không có gì được thêm vào.\n"
+
+#: builtin/add.c:414
+#, c-format
+msgid "Maybe you wanted to say 'git add .'?\n"
+msgstr "Có lẽ bạn muốn nói là 'git add .' phải không?\n"
+
+#: builtin/add.c:420
+#: builtin/clean.c:95
+#: builtin/commit.c:286
+#: builtin/mv.c:82
+#: builtin/rm.c:162
+msgid "index file corrupt"
+msgstr "tập tin ghi bảng mục lục bị hỏng"
+
+#: builtin/add.c:480
+#: builtin/apply.c:4108
+#: builtin/mv.c:229
+#: builtin/rm.c:260
+msgid "Unable to write new index file"
+msgstr "Không thể ghi tập tin lưu bảng mục lục mới"
+
+#: builtin/apply.c:53
+msgid "git apply [options] [<patch>...]"
+msgstr "git apply [các-tùy-chọn] [<miếng-vá>...]"
+
+#: builtin/apply.c:106
+#, c-format
+msgid "unrecognized whitespace option '%s'"
+msgstr "không nhận ra tùy chọn về khoảng trắng '%s'"
+
+#: builtin/apply.c:121
+#, c-format
+msgid "unrecognized whitespace ignore option '%s'"
+msgstr "không nhận ra tùy chọn bỏ qua khoảng trắng '%s'"
+
+#: builtin/apply.c:815
+#, c-format
+msgid "Cannot prepare timestamp regexp %s"
+msgstr "Không thể chuẩn bị biểu thức chính qui dấu vết thời gian (timestamp regexp) %s"
+
+#: builtin/apply.c:824
+#, c-format
+msgid "regexec returned %d for input: %s"
+msgstr "thi hành biểu thức chính quy trả về %d cho kết xuất: %s"
+
+#: builtin/apply.c:905
+#, c-format
+msgid "unable to find filename in patch at line %d"
+msgstr "không thể tìm thấy tên tập tin trong miếng vá tại dòng %d"
+
+#: builtin/apply.c:937
+#, c-format
+msgid "git apply: bad git-diff - expected /dev/null, got %s on line %d"
+msgstr "git apply: git-diff sai - mong đợi /dev/null, đã nhận %s trên dòng %d"
+
+#: builtin/apply.c:941
+#, c-format
+msgid "git apply: bad git-diff - inconsistent new filename on line %d"
+msgstr "git apply: git-diff sai - tên tập tin mới mâu thuấn trên dòng %d"
+
+#: builtin/apply.c:942
+#, c-format
+msgid "git apply: bad git-diff - inconsistent old filename on line %d"
+msgstr "git apply: git-diff sai - tên tập tin cũ mâu thuấn trên dòng %d"
+
+#: builtin/apply.c:949
+#, c-format
+msgid "git apply: bad git-diff - expected /dev/null on line %d"
+msgstr "git apply: git-diff sai - mong đợi /dev/null trên dòng %d"
+
+#: builtin/apply.c:1394
+#, c-format
+msgid "recount: unexpected line: %.*s"
+msgstr "chi tiết: dòng không được mong đợi: %.*s"
+
+#: builtin/apply.c:1451
+#, c-format
+msgid "patch fragment without header at line %d: %.*s"
+msgstr "miếng vá phân mảnh mà không có phần đầu tại dòng %d: %.*s"
+
+#: builtin/apply.c:1468
+#, c-format
+msgid "git diff header lacks filename information when removing %d leading pathname component (line %d)"
+msgid_plural "git diff header lacks filename information when removing %d leading pathname components (line %d)"
+msgstr[0] "phần đầu diff cho git  thiếu thông tin tên tập tin khi gỡ bỏ đi %d trong thành phần dẫn đầu tên của đường dẫn (dòng %d)"
+msgstr[1] "phần đầu diff cho git  thiếu thông tin tên tập tin khi gỡ bỏ đi %d trong thành phần dẫn đầu tên của đường dẫn (dòng %d)"
+
+#: builtin/apply.c:1628
+msgid "new file depends on old contents"
+msgstr "tập tin mới phụ thuộc vào nội dung cũ"
+
+#: builtin/apply.c:1630
+msgid "deleted file still has contents"
+msgstr "tập tin đã xóa vẫn còn nội dung"
+
+#: builtin/apply.c:1656
+#, c-format
+msgid "corrupt patch at line %d"
+msgstr "miếng vá hỏng tại dòng %d"
+
+#: builtin/apply.c:1692
+#, c-format
+msgid "new file %s depends on old contents"
+msgstr "tập tin mới %s phụ thuộc vào nội dung cũ"
+
+#: builtin/apply.c:1694
+#, c-format
+msgid "deleted file %s still has contents"
+msgstr "tập tin đã xóa %s vẫn còn nội dung"
+
+#: builtin/apply.c:1697
+#, c-format
+msgid "** warning: file %s becomes empty but is not deleted"
+msgstr "** cảnh báo: tập tin %s trở nên trống rỗng nhưng không bị xóa"
+
+#: builtin/apply.c:1843
+#, c-format
+msgid "corrupt binary patch at line %d: %.*s"
+msgstr "miếng vá định dạng nhị phân sai hỏng tại dòng %d: %.*s"
+
+#. there has to be one hunk (forward hunk)
+#: builtin/apply.c:1872
+#, c-format
+msgid "unrecognized binary patch at line %d"
+msgstr "miếng vá định dạng nhị phân không được nhận ra tại dòng %d"
+
+#: builtin/apply.c:1958
+#, c-format
+msgid "patch with only garbage at line %d"
+msgstr "vá chỉ với 'garbage' tại dòng %d"
+
+#: builtin/apply.c:2048
+#, c-format
+msgid "unable to read symlink %s"
+msgstr "không thể đọc liên kết tượng trưng %s"
+
+#: builtin/apply.c:2052
+#, c-format
+msgid "unable to open or read %s"
+msgstr "không thể mở để đọc hay ghi %s"
+
+#: builtin/apply.c:2123
+msgid "oops"
+msgstr "ôi?"
+
+#: builtin/apply.c:2645
+#, c-format
+msgid "invalid start of line: '%c'"
+msgstr "sai khởi đầu dòng: '%c'"
+
+#: builtin/apply.c:2763
+#, c-format
+msgid "Hunk #%d succeeded at %d (offset %d line)."
+msgid_plural "Hunk #%d succeeded at %d (offset %d lines)."
+msgstr[0] "Khối dữ liệu #%d thành công tại %d (offset %d dòng)."
+msgstr[1] "Khối dữ liệu #%d thành công tại %d (offset %d dòng)."
+
+#: builtin/apply.c:2775
+#, c-format
+msgid "Context reduced to (%ld/%ld) to apply fragment at %d"
+msgstr "Nội dung được giảm xuống (%ld/%ld) để áp dụng mảnh dữ liệu tại %d"
+
+#: builtin/apply.c:2781
+#, c-format
+msgid ""
+"while searching for:\n"
+"%.*s"
+msgstr ""
+"Trong khi đang tìm kiếm cho:\n"
+"%.*s"
+
+#: builtin/apply.c:2800
+#, c-format
+msgid "missing binary patch data for '%s'"
+msgstr "thiếu dữ liệu của miếng vá định dạng nhị phân cho '%s'"
+
+#: builtin/apply.c:2903
+#, c-format
+msgid "binary patch does not apply to '%s'"
+msgstr "miếng vá định dạng nhị phân không được áp dụng cho '%s'"
+
+#: builtin/apply.c:2909
+#, c-format
+msgid "binary patch to '%s' creates incorrect result (expecting %s, got %s)"
+msgstr "vá nhị phân cho '%s' tạo ra kết quả không chính xác (đang chờ %s, đã nhận %s)"
+
+#: builtin/apply.c:2930
+#, c-format
+msgid "patch failed: %s:%ld"
+msgstr "vá gặp lỗi: %s:%ld"
+
+#: builtin/apply.c:3045
+#, c-format
+msgid "patch %s has been renamed/deleted"
+msgstr "miếng vá %s đã bị xóa/đổi tên"
+
+#: builtin/apply.c:3052
+#: builtin/apply.c:3069
+#, c-format
+msgid "read of %s failed"
+msgstr "đọc %s gặp lỗi"
+
+#: builtin/apply.c:3084
+msgid "removal patch leaves file contents"
+msgstr "loại bỏ miếng vá để lại nội dung tập tin"
+
+#: builtin/apply.c:3105
+#, c-format
+msgid "%s: already exists in working directory"
+msgstr "%s: đã sẵn có trong thư mục đang làm việc"
+
+#: builtin/apply.c:3143
+#, c-format
+msgid "%s: has been deleted/renamed"
+msgstr "%s: đã được xóa/thay-tên"
+
+#: builtin/apply.c:3148
+#: builtin/apply.c:3179
+#, c-format
+msgid "%s: %s"
+msgstr "%s: %s"
+
+#: builtin/apply.c:3159
+#, c-format
+msgid "%s: does not exist in index"
+msgstr "%s: không tồn tại trong bảng mục lục"
+
+#: builtin/apply.c:3173
+#, c-format
+msgid "%s: does not match index"
+msgstr "%s: không khớp trong mục lục"
+
+#: builtin/apply.c:3190
+#, c-format
+msgid "%s: wrong type"
+msgstr "%s: sai kiểu"
+
+#: builtin/apply.c:3192
+#, c-format
+msgid "%s has type %o, expected %o"
+msgstr "%s có kiểu %o, mong chờ %o"
+
+#: builtin/apply.c:3247
+#, c-format
+msgid "%s: already exists in index"
+msgstr "%s: đã có từ trước trong bảng mục lục"
+
+#: builtin/apply.c:3267
+#, c-format
+msgid "new mode (%o) of %s does not match old mode (%o)"
+msgstr "chế độ mới (%o) của %s không khớp với chế độ cũ (%o)"
+
+#: builtin/apply.c:3272
+#, c-format
+msgid "new mode (%o) of %s does not match old mode (%o) of %s"
+msgstr "chế độ mới (%o) của %s không khớp với chế độ cũ (%o) của %s"
+
+#: builtin/apply.c:3280
+#, c-format
+msgid "%s: patch does not apply"
+msgstr "%s: miếng vá không được áp dụng"
+
+#: builtin/apply.c:3293
+#, c-format
+msgid "Checking patch %s..."
+msgstr "Đang kiểm tra miếng vá %s..."
+
+#: builtin/apply.c:3348
+#: builtin/checkout.c:212
+#: builtin/reset.c:158
+#, c-format
+msgid "make_cache_entry failed for path '%s'"
+msgstr "make_cache_entry gặp lỗi đối với đường dẫn '%s'"
+
+#: builtin/apply.c:3491
+#, c-format
+msgid "unable to remove %s from index"
+msgstr "không thể gỡ bỏ %s từ mục lục"
+
+#: builtin/apply.c:3518
+#, c-format
+msgid "corrupt patch for subproject %s"
+msgstr "miếng vá sai hỏng cho dự án con (subproject) %s"
+
+#: builtin/apply.c:3522
+#, c-format
+msgid "unable to stat newly created file '%s'"
+msgstr "không thể lấy trạng thái về tập tin %s mới hơn đã được tạo"
+
+#: builtin/apply.c:3527
+#, c-format
+msgid "unable to create backing store for newly created file %s"
+msgstr "không thể tạo 'backing store' cho tập tin được tạo mới hơn %s"
+
+#: builtin/apply.c:3530
+#, c-format
+msgid "unable to add cache entry for %s"
+msgstr "không thể thêm mục nhớ tạm cho %s"
+
+#: builtin/apply.c:3563
+#, c-format
+msgid "closing file '%s'"
+msgstr "đang đóng tập tin '%s'"
+
+#: builtin/apply.c:3612
+#, c-format
+msgid "unable to write file '%s' mode %o"
+msgstr "không thể ghi vào tập tin '%s' chế độ (mode) %o"
+
+#: builtin/apply.c:3668
+#, c-format
+msgid "Applied patch %s cleanly."
+msgstr "Đã áp dụng miếng và %s một cách sạch sẽ."
+
+#: builtin/apply.c:3676
+msgid "internal error"
+msgstr "lỗi nội bộ"
+
+#. Say this even without --verbose
+#: builtin/apply.c:3679
+#, c-format
+msgid "Applying patch %%s with %d reject..."
+msgid_plural "Applying patch %%s with %d rejects..."
+msgstr[0] "Đang áp dụng miếng vá %%s với %d lần từ chối..."
+msgstr[1] "Đang áp dụng miếng vá %%s với %d lần từ chối..."
+
+#: builtin/apply.c:3689
+#, c-format
+msgid "truncating .rej filename to %.*s.rej"
+msgstr "đang cắt cụt tên tập tin .rej thành %.*s.rej"
+
+#: builtin/apply.c:3710
+#, c-format
+msgid "Hunk #%d applied cleanly."
+msgstr "Khối nhớ #%d được áp dụng gọn gàng."
+
+#: builtin/apply.c:3713
+#, c-format
+msgid "Rejected hunk #%d."
+msgstr "hunk #%d bị từ chối."
+
+#: builtin/apply.c:3844
+msgid "unrecognized input"
+msgstr "không thừa nhận đầu vào"
+
+#: builtin/apply.c:3855
+msgid "unable to read index file"
+msgstr "không thể đọc tập tin lưu bảng mục lục"
+
+#: builtin/apply.c:3970
+#: builtin/apply.c:3973
+msgid "path"
+msgstr "đường-dẫn"
+
+#: builtin/apply.c:3971
+msgid "don't apply changes matching the given path"
+msgstr "không áp dụng các thay đổi khớp với đường dẫn đã cho"
+
+#: builtin/apply.c:3974
+msgid "apply changes matching the given path"
+msgstr "áp dụng các thay đổi khớp với đường dẫn đã cho"
+
+#: builtin/apply.c:3976
+msgid "num"
+msgstr "số"
+
+#: builtin/apply.c:3977
+msgid "remove <num> leading slashes from traditional diff paths"
+msgstr "gỡ bỏ <số> phần dẫn đầu (slashe) từ đường dẫn diff cổ điển"
+
+#: builtin/apply.c:3980
+msgid "ignore additions made by the patch"
+msgstr "lờ đi phần phụ thêm tạo ra bởi miếng vá"
+
+#: builtin/apply.c:3982
+msgid "instead of applying the patch, output diffstat for the input"
+msgstr "thay vì áp dụng một miếng vá, kết xuất kết quả từ lệnh diffstat cho đầu ra"
+
+#: builtin/apply.c:3986
+msgid "shows number of added and deleted lines in decimal notation"
+msgstr "hiển thị số lượng các dòng được thêm vào và xóa đi theo ký hiệu thập phân"
+
+#: builtin/apply.c:3988
+msgid "instead of applying the patch, output a summary for the input"
+msgstr "thay vì áp dụng một miếng vá, kết xuất kết quả cho đầu vào"
+
+#: builtin/apply.c:3990
+msgid "instead of applying the patch, see if the patch is applicable"
+msgstr "thay vì áp dụng miếng vá, hãy xem xem miếng vá có thích hợp không"
+
+#: builtin/apply.c:3992
+msgid "make sure the patch is applicable to the current index"
+msgstr "hãy chắc chắn là miếng vá thích hợp với bảng mục lục hiện hành"
+
+#: builtin/apply.c:3994
+msgid "apply a patch without touching the working tree"
+msgstr "áp dụng một miếng vá mà không động chạm đến cây làm việc"
+
+#: builtin/apply.c:3996
+msgid "also apply the patch (use with --stat/--summary/--check)"
+msgstr "đồng thời áp dụng miếng vá (sử dụng với tùy chọn --stat/--summary/--check)"
+
+#: builtin/apply.c:3998
+msgid "build a temporary index based on embedded index information"
+msgstr "xây dựng bảng mục lục tạm thời trên cơ sở thông tin bảng mục lục được nhúng"
+
+#: builtin/apply.c:4000
+msgid "paths are separated with NUL character"
+msgstr "các đường dẫn bị ngăn cách bởi ký tự NULL"
+
+#: builtin/apply.c:4003
+msgid "ensure at least <n> lines of context match"
+msgstr "đảm bảo rằng có ít nhất <n> dòng nội dung khớp"
+
+#: builtin/apply.c:4004
+msgid "action"
+msgstr "hành động"
+
+#: builtin/apply.c:4005
+msgid "detect new or modified lines that have whitespace errors"
+msgstr "tìm thấy một dòng mới hoặc bị sửa đổi mà nó có lỗi do khoảng trắng"
+
+#: builtin/apply.c:4008
+#: builtin/apply.c:4011
+msgid "ignore changes in whitespace when finding context"
+msgstr "lờ đi sự thay đổi do khoảng trắng khi quét nội dung"
+
+#: builtin/apply.c:4014
+msgid "apply the patch in reverse"
+msgstr "áp dụng miếng vá theo chiều ngược"
+
+#: builtin/apply.c:4016
+msgid "don't expect at least one line of context"
+msgstr "đừng hy vọng có ít nhất một dòng nội dung"
+
+#: builtin/apply.c:4018
+msgid "leave the rejected hunks in corresponding *.rej files"
+msgstr "để lại khối dữ liệu bị từ chối trong các tập tin *.rej tương ứng"
+
+#: builtin/apply.c:4020
+msgid "allow overlapping hunks"
+msgstr "cho phép chồng khối nhớ"
+
+#: builtin/apply.c:4021
+msgid "be verbose"
+msgstr "chi tiết"
+
+#: builtin/apply.c:4023
+msgid "tolerate incorrectly detected missing new-line at the end of file"
+msgstr "dung sai không chính xác đã tìm thấy thiếu dòng mới tại cuối tập tin"
+
+#: builtin/apply.c:4026
+msgid "do not trust the line counts in the hunk headers"
+msgstr "không tin số lượng dòng trong phần đầu khối dữ liệu"
+
+#: builtin/apply.c:4028
+msgid "root"
+msgstr "root"
+
+#: builtin/apply.c:4029
+msgid "prepend <root> to all filenames"
+msgstr "treo thêm <root> vào tất cả các tên tập tin"
+
+#: builtin/apply.c:4050
+msgid "--index outside a repository"
+msgstr "--index ở ngoài một kho chứa"
+
+#: builtin/apply.c:4053
+msgid "--cached outside a repository"
+msgstr "--cached ở ngoài một kho chứa"
+
+#: builtin/apply.c:4069
+#, c-format
+msgid "can't open patch '%s'"
+msgstr "không thể mở miếng vá '%s'"
+
+#: builtin/apply.c:4083
+#, c-format
+msgid "squelched %d whitespace error"
+msgid_plural "squelched %d whitespace errors"
+msgstr[0] "đã chấm dứt %d lỗi khoảng trắng"
+msgstr[1] "đã chấm dứt %d lỗi khoảng trắng"
+
+#: builtin/apply.c:4089
+#: builtin/apply.c:4099
+#, c-format
+msgid "%d line adds whitespace errors."
+msgid_plural "%d lines add whitespace errors."
+msgstr[0] "%d dòng thêm khoảng trắng lỗi."
+msgstr[1] "%d dòng thêm khoảng trắng lỗi."
+
+#: builtin/archive.c:17
+#, c-format
+msgid "could not create archive file '%s'"
+msgstr "không thể tạo tập tin kho (lưu trữ, nén) '%s'"
+
+#: builtin/archive.c:20
+msgid "could not redirect output"
+msgstr "không thể chuyển hướng kết xuất"
+
+#: builtin/archive.c:37
+msgid "git archive: Remote with no URL"
+msgstr "git archive: Máy chủ không có địa chỉ URL"
+
+#: builtin/archive.c:58
+msgid "git archive: expected ACK/NAK, got EOF"
+msgstr "git archive: mong đợi ACK/NAK, nhận EOF"
+
+#: builtin/archive.c:63
+#, c-format
+msgid "git archive: NACK %s"
+msgstr "git archive: NACK %s"
+
+#: builtin/archive.c:65
+#, c-format
+msgid "remote error: %s"
+msgstr "lỗi máy chủ: %s"
+
+#: builtin/archive.c:66
+msgid "git archive: protocol error"
+msgstr "git archive: lỗi giao thức"
+
+#: builtin/archive.c:71
+msgid "git archive: expected a flush"
+msgstr "git archive: đã mong chờ một flush"
+
+#: builtin/branch.c:144
+#, c-format
+msgid ""
+"deleting branch '%s' that has been merged to\n"
+"         '%s', but not yet merged to HEAD."
+msgstr ""
+"đang xóa nhánh '%s' mà nó lại đã được hòa trộn vào\n"
+"         '%s', nhưng vẫn chưa được hòa trộn vào HEAD."
+
+#: builtin/branch.c:148
+#, c-format
+msgid ""
+"not deleting branch '%s' that is not yet merged to\n"
+"         '%s', even though it is merged to HEAD."
+msgstr ""
+"không xóa nhánh '%s' cái mà chưa được hòa trộng vào\n"
+"         '%s', cho dù là nó đã được hòa trộn vào HEAD."
+
+#: builtin/branch.c:180
+msgid "cannot use -a with -d"
+msgstr "không thể sử dụng -a với -d"
+
+#: builtin/branch.c:186
+msgid "Couldn't look up commit object for HEAD"
+msgstr "Không thể tìm kiếm đối tượng chuyển giao (commit) cho HEAD"
+
+#: builtin/branch.c:191
+#, c-format
+msgid "Cannot delete the branch '%s' which you are currently on."
+msgstr "Không thể xóa nhánh '%s' cái mà bạn hiện nay đang ở."
+
+#: builtin/branch.c:202
+#, c-format
+msgid "remote branch '%s' not found."
+msgstr "nhánh máy chủ '%s' không tìm thấy."
+
+#: builtin/branch.c:203
+#, c-format
+msgid "branch '%s' not found."
+msgstr "không tìm thấy nhánh '%s'."
+
+#: builtin/branch.c:210
+#, c-format
+msgid "Couldn't look up commit object for '%s'"
+msgstr "Không thể tìm kiếm đối tượng chuyển giao (commit) cho '%s'"
+
+#: builtin/branch.c:216
+#, c-format
+msgid ""
+"The branch '%s' is not fully merged.\n"
+"If you are sure you want to delete it, run 'git branch -D %s'."
+msgstr ""
+"Nhánh '%s' không được trộn một cách đầy đủ.\n"
+"Nếu bạn thực sự muốn xóa nó, thì chạy lệnh 'git branch -D %s'."
+
+#: builtin/branch.c:225
+#, c-format
+msgid "Error deleting remote branch '%s'"
+msgstr "Gặp lỗi khi đang xóa nhánh máy chủ '%s'"
+
+#: builtin/branch.c:226
+#, c-format
+msgid "Error deleting branch '%s'"
+msgstr "Lỗi khi xoá bỏ nhánh '%s'"
+
+#: builtin/branch.c:233
+#, c-format
+msgid "Deleted remote branch %s (was %s).\n"
+msgstr "Nhánh máy chủ đã xóa %s (trước là %s).\n"
+
+#: builtin/branch.c:234
+#, c-format
+msgid "Deleted branch %s (was %s).\n"
+msgstr "Nhánh đã bị xóa '%s' (trước là %s)\n"
+
+#: builtin/branch.c:239
+msgid "Update of config-file failed"
+msgstr "Cập nhật tệp tin cấu hình gặp lỗi"
+
+#: builtin/branch.c:337
+#, c-format
+msgid "branch '%s' does not point at a commit"
+msgstr "nhánh '%s' không chỉ đến một lần chuyển giao (commit) nào cả"
+
+#: builtin/branch.c:409
+#, c-format
+msgid "[%s: behind %d]"
+msgstr "[%s: đằng sau %d]"
+
+#: builtin/branch.c:411
+#, c-format
+msgid "[behind %d]"
+msgstr "[đằng sau %d]"
+
+#: builtin/branch.c:415
+#, c-format
+msgid "[%s: ahead %d]"
+msgstr "[%s: phía trước %d]"
+
+#: builtin/branch.c:417
+#, c-format
+msgid "[ahead %d]"
+msgstr "[phía trước %d]"
+
+#: builtin/branch.c:420
+#, c-format
+msgid "[%s: ahead %d, behind %d]"
+msgstr "[%s: phía trước %d, phía sau %d]"
+
+#: builtin/branch.c:423
+#, c-format
+msgid "[ahead %d, behind %d]"
+msgstr "[phía trước %d, phía sau %d]"
+
+#: builtin/branch.c:535
+msgid "(no branch)"
+msgstr "(không có nhánh nào)"
+
+#: builtin/branch.c:600
+msgid "some refs could not be read"
+msgstr "một số tham chiếu đã không thể đọc được"
+
+#: builtin/branch.c:613
+msgid "cannot rename the current branch while not on any."
+msgstr "không thể đổi tên nhánh hiện hành trong khi nó chẳng ở đâu cả."
+
+#: builtin/branch.c:623
+#, c-format
+msgid "Invalid branch name: '%s'"
+msgstr "tên nhánh sai: '%s'"
+
+#: builtin/branch.c:638
+msgid "Branch rename failed"
+msgstr "Đổi tên nhánh gặp lỗi"
+
+#: builtin/branch.c:642
+#, c-format
+msgid "Renamed a misnamed branch '%s' away"
+msgstr "Đã đổi tên nhánh khuyết danh '%s' đi"
+
+#: builtin/branch.c:646
+#, c-format
+msgid "Branch renamed to %s, but HEAD is not updated!"
+msgstr "Nhánh bị đổi tên thành %s, nhưng HEAD lại không được cập nhật!"
+
+#: builtin/branch.c:653
+msgid "Branch is renamed, but update of config-file failed"
+msgstr "Nhánh bị đổi tên, nhưng cập nhật tập tin cấu hình gặp lỗi"
+
+#: builtin/branch.c:668
+#, c-format
+msgid "malformed object name %s"
+msgstr "tên đối tượng dị hình %s"
+
+#: builtin/branch.c:692
+#, c-format
+msgid "could not write branch description template: %s"
+msgstr "không thể ghi vào mẫu mô tả nhánh: %s"
+
+#: builtin/branch.c:783
+msgid "Failed to resolve HEAD as a valid ref."
+msgstr "Gặp lỗi khi giải quyết HEAD như là một tham chiếu (ref) hợp lệ."
+
+#: builtin/branch.c:788
+#: builtin/clone.c:558
+msgid "HEAD not found below refs/heads!"
+msgstr "HEAD không tìm thấy ở dưới refs/heads!"
+
+#: builtin/branch.c:808
+msgid "--column and --verbose are incompatible"
+msgstr "--column và --verbose xung khắc nhau"
+
+#: builtin/branch.c:857
+msgid "-a and -r options to 'git branch' do not make sense with a branch name"
+msgstr "hai tùy chọn -a và -r áp dụng cho lệnh 'git branch' không hợp lý đối với tên nhánh"
+
+#: builtin/bundle.c:47
+#, c-format
+msgid "%s is okay\n"
+msgstr "'%s' tốt\n"
+
+#: builtin/bundle.c:56
+msgid "Need a repository to create a bundle."
+msgstr "Cần một kho chứa để mà tạo một bundle."
+
+#: builtin/bundle.c:60
+msgid "Need a repository to unbundle."
+msgstr "Cần một kho chứa để mà bung một bundle."
+
+#: builtin/checkout.c:113
+#: builtin/checkout.c:146
+#, c-format
+msgid "path '%s' does not have our version"
+msgstr "đường dẫn '%s' không có các phiên bản của chúng ta"
+
+#: builtin/checkout.c:115
+#: builtin/checkout.c:148
+#, c-format
+msgid "path '%s' does not have their version"
+msgstr "đường dẫn '%s' không có các phiên bản của chúng"
+
+#: builtin/checkout.c:131
+#, c-format
+msgid "path '%s' does not have all necessary versions"
+msgstr "đường dẫn '%s' không có tất cả các phiên bản cần thiết"
+
+#: builtin/checkout.c:175
+#, c-format
+msgid "path '%s' does not have necessary versions"
+msgstr "đường dẫn '%s' không có các phiên bản cần thiết"
+
+#: builtin/checkout.c:192
+#, c-format
+msgid "path '%s': cannot merge"
+msgstr "đường dẫn '%s': không thể hòa trộn"
+
+#: builtin/checkout.c:209
+#, c-format
+msgid "Unable to add merge result for '%s'"
+msgstr "Không thể thêm kết quả hòa trộn cho '%s'"
+
+#: builtin/checkout.c:234
+#: builtin/checkout.c:392
+msgid "corrupt index file"
+msgstr "tập tin ghi bảng mục lục bị hỏng"
+
+#: builtin/checkout.c:264
+#: builtin/checkout.c:271
+#, c-format
+msgid "path '%s' is unmerged"
+msgstr "đường dẫn '%s' không được hòa trộn"
+
+#: builtin/checkout.c:302
+#: builtin/checkout.c:498
+#: builtin/clone.c:583
+#: builtin/merge.c:812
+msgid "unable to write new index file"
+msgstr "không thể ghi tập tin lưu bảng mục lục mới"
+
+#: builtin/checkout.c:319
+#: builtin/diff.c:302
+#: builtin/merge.c:408
+msgid "diff_setup_done failed"
+msgstr "diff_setup_done gặp lỗi"
+
+#: builtin/checkout.c:414
+msgid "you need to resolve your current index first"
+msgstr "bạn cần phải giải quyết bảng mục lục hiện tại của bạn trước đã!"
+
+#: builtin/checkout.c:533
+#, c-format
+msgid "Can not do reflog for '%s'\n"
+msgstr "Không thể thực hiện reflog cho '%s'\n"
+
+#: builtin/checkout.c:566
+msgid "HEAD is now at"
+msgstr "HEAD hiện giờ tại"
+
+#: builtin/checkout.c:573
+#, c-format
+msgid "Reset branch '%s'\n"
+msgstr "Đặt lại nhánh '%s'\n"
+
+#: builtin/checkout.c:576
+#, c-format
+msgid "Already on '%s'\n"
+msgstr "Đã sẵn sàng trên '%s'\n"
+
+#: builtin/checkout.c:580
+#, c-format
+msgid "Switched to and reset branch '%s'\n"
+msgstr "Đã chuyển tới và reset nhánh '%s'\n"
+
+#: builtin/checkout.c:582
+#, c-format
+msgid "Switched to a new branch '%s'\n"
+msgstr "Đã chuyển đến nhánh mới '%s'\n"
+
+#: builtin/checkout.c:584
+#, c-format
+msgid "Switched to branch '%s'\n"
+msgstr "Đã chuyển đến nhánh '%s'\n"
+
+#: builtin/checkout.c:640
+#, c-format
+msgid " ... and %d more.\n"
+msgstr " ... và nhiều hơn %d.\n"
+
+#. The singular version
+#: builtin/checkout.c:646
+#, c-format
+msgid ""
+"Warning: you are leaving %d commit behind, not connected to\n"
+"any of your branches:\n"
+"\n"
+"%s\n"
+msgid_plural ""
+"Warning: you are leaving %d commits behind, not connected to\n"
+"any of your branches:\n"
+"\n"
+"%s\n"
+msgstr[0] ""
+"Cảnh báo: bạn đã rời bở %d lần chuyển giao (commit) lại đằng sau, không được kết nối đến\n"
+"bất kỳ nhánh nào của bạn:\n"
+"\n"
+"%s\n"
+msgstr[1] ""
+"Cảnh báo: bạn đã rời bở %d lần chuyển giao (commit) lại đằng sau, không được kết nối đến\n"
+"bất kỳ nhánh nào của bạn:\n"
+"\n"
+"%s\n"
+
+#: builtin/checkout.c:664
+#, c-format
+msgid ""
+"If you want to keep them by creating a new branch, this may be a good time\n"
+"to do so with:\n"
+"\n"
+" git branch new_branch_name %s\n"
+"\n"
+msgstr ""
+"Nếu bạn muốn giữ chúng bằng cách tạo ra một nhánh mới, đây có lẽ là một thời điểm thích hợp\n"
+"để làm thế bằng lệnh:\n"
+"\n"
+" git branch tên_nhánh_mới %s\n"
+"\n"
+
+#: builtin/checkout.c:694
+msgid "internal error in revision walk"
+msgstr "lỗi nội bộ trong khi di chuyển qua các điểm xét lại"
+
+#: builtin/checkout.c:698
+msgid "Previous HEAD position was"
+msgstr "Vị trí kế trước của HEAD là"
+
+#: builtin/checkout.c:724
+msgid "You are on a branch yet to be born"
+msgstr "Bạn tại nhánh mà nó chưa hề được sinh ra"
+
+#. case (1)
+#: builtin/checkout.c:855
+#, c-format
+msgid "invalid reference: %s"
+msgstr "tham chiếu sai: %s"
+
+#. case (1): want a tree
+#: builtin/checkout.c:894
+#, c-format
+msgid "reference is not a tree: %s"
+msgstr "tham chiếu không phải là cây:%s"
+
+#: builtin/checkout.c:974
+msgid "-B cannot be used with -b"
+msgstr "-B không thể được sử dụng với -b"
+
+#: builtin/checkout.c:983
+msgid "--patch is incompatible with all other options"
+msgstr "--patch xung khắc với tất cả các tùy chọn khác"
+
+#: builtin/checkout.c:986
+msgid "--detach cannot be used with -b/-B/--orphan"
+msgstr "--detach không thể được sử dụng với -b/-B/--orphan"
+
+#: builtin/checkout.c:988
+msgid "--detach cannot be used with -t"
+msgstr "--detach không thể được sử dụng với tùy chọn -t"
+
+#: builtin/checkout.c:994
+msgid "--track needs a branch name"
+msgstr "--track cần tên một nhánh"
+
+#: builtin/checkout.c:1001
+msgid "Missing branch name; try -b"
+msgstr "Thiếu tên nhánh; hãy thử -b"
+
+#: builtin/checkout.c:1007
+msgid "--orphan and -b|-B are mutually exclusive"
+msgstr "Tùy chọn --orphan và -b|-B loại từ lẫn nhau"
+
+#: builtin/checkout.c:1009
+msgid "--orphan cannot be used with -t"
+msgstr "--orphan không thể được sử dụng với tùy chọn -t"
+
+#: builtin/checkout.c:1019
+msgid "git checkout: -f and -m are incompatible"
+msgstr "git checkout: -f và -m xung khắc nhau"
+
+#: builtin/checkout.c:1053
+msgid "invalid path specification"
+msgstr "đường dẫn đã cho không hợp lệ"
+
+#: builtin/checkout.c:1061
+#, c-format
+msgid ""
+"git checkout: updating paths is incompatible with switching branches.\n"
+"Did you intend to checkout '%s' which can not be resolved as commit?"
+msgstr ""
+"git checkout: việc cập nhật các đường dẫn là xung khắc với việc chuyển đổi các nhánh..\n"
+"Bạn đã có ý định checkout '%s' cái mà không thể được phân giải như là lần chuyển giao (commit)?"
+
+#: builtin/checkout.c:1063
+msgid "git checkout: updating paths is incompatible with switching branches."
+msgstr "git checkout: việc cập nhật các đường dẫn là xung khắc với việc chuyển đổi các nhánh."
+
+#: builtin/checkout.c:1068
+msgid "git checkout: --detach does not take a path argument"
+msgstr "git checkout: --detach không nhận một đối số đường dẫn"
+
+#: builtin/checkout.c:1071
+msgid ""
+"git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
+"checking out of the index."
+msgstr ""
+"git checkout: --ours/--theirs, --force và --merge là xung khắc với nhau khi\n"
+"checkout."
+
+#: builtin/checkout.c:1090
+msgid "Cannot switch branch to a non-commit."
+msgstr "Không thể chuyển đến một non-commit."
+
+#: builtin/checkout.c:1093
+msgid "--ours/--theirs is incompatible with switching branches."
+msgstr "--ours/--theirs là xung khắc nhau khi chuyển đổi các nhánh."
+
+#: builtin/clean.c:78
+msgid "-x and -X cannot be used together"
+msgstr "-x và -X không thể dùng cùng một lúc với nhau"
+
+#: builtin/clean.c:82
+msgid "clean.requireForce set to true and neither -n nor -f given; refusing to clean"
+msgstr "clean.requireForce được đặt thành true và không đưa ra tùy chọn  -n mà cũng không -f; từ chối lệnh dọn dẹp (clean)"
+
+#: builtin/clean.c:85
+msgid "clean.requireForce defaults to true and neither -n nor -f given; refusing to clean"
+msgstr "clean.requireForce mặc định được đặt thành true và không đưa ra tùy chọn  -n mà cũng không -f; từ chối lệnh dọn dẹp (clean)"
+
+#: builtin/clean.c:155
+#: builtin/clean.c:176
+#, c-format
+msgid "Would remove %s\n"
+msgstr "Có thể gỡ bỏ %s\n"
+
+#: builtin/clean.c:159
+#: builtin/clean.c:179
+#, c-format
+msgid "Removing %s\n"
+msgstr "Đang gỡ bỏ %s\n"
+
+#: builtin/clean.c:162
+#: builtin/clean.c:182
+#, c-format
+msgid "failed to remove %s"
+msgstr "gặp lỗi khi gỡ bỏ %s"
+
+#: builtin/clean.c:166
+#, c-format
+msgid "Would not remove %s\n"
+msgstr "Không thể gỡ bỏ %s\n"
+
+#: builtin/clean.c:168
+#, c-format
+msgid "Not removing %s\n"
+msgstr "Không xóa %s\n"
+
+#: builtin/clone.c:243
+#, c-format
+msgid "reference repository '%s' is not a local directory."
+msgstr "kho tham chiếu '%s' không phải là một thư mục nội bộ."
+
+#: builtin/clone.c:302
+#, c-format
+msgid "failed to open '%s'"
+msgstr "gặp lỗi khi mở '%s'"
+
+#: builtin/clone.c:306
+#, c-format
+msgid "failed to create directory '%s'"
+msgstr "tạo thư mục \"%s\" gặp lỗi"
+
+#: builtin/clone.c:308
+#: builtin/diff.c:75
+#, c-format
+msgid "failed to stat '%s'"
+msgstr "gặp lỗi stat (lấy trạng thái về) '%s'"
+
+#: builtin/clone.c:310
+#, c-format
+msgid "%s exists and is not a directory"
+msgstr "%s tồn tại nhưng không phải là một thư mục"
+
+#: builtin/clone.c:324
+#, c-format
+msgid "failed to stat %s\n"
+msgstr "lỗi stat (lấy trạng thái về) %s\n"
+
+#: builtin/clone.c:341
+#, c-format
+msgid "failed to unlink '%s'"
+msgstr "bỏ liên kết (unlink) %s không thành công"
+
+#: builtin/clone.c:346
+#, c-format
+msgid "failed to create link '%s'"
+msgstr "tạo được liên kết mềm tới %s gặp lỗi"
+
+#: builtin/clone.c:350
+#, c-format
+msgid "failed to copy file to '%s'"
+msgstr "sao chép tệp tin tới '%s' gặp lỗi"
+
+#: builtin/clone.c:373
+#, c-format
+msgid "done.\n"
+msgstr "hoàn tất.\n"
+
+#: builtin/clone.c:440
+#, c-format
+msgid "Could not find remote branch %s to clone."
+msgstr "Không tìm thấy nhánh máy chủ %s để nhân bản (clone)."
+
+#: builtin/clone.c:549
+msgid "remote HEAD refers to nonexistent ref, unable to checkout.\n"
+msgstr "refers HEAD máy chủ  chỉ đến ref không tồn tại, không thể checkout.\n"
+
+#: builtin/clone.c:639
+msgid "Too many arguments."
+msgstr "Có quá nhiều đối số."
+
+#: builtin/clone.c:643
+msgid "You must specify a repository to clone."
+msgstr "Bạn phải chỉ định một kho để mà nhân bản (clone)."
+
+#: builtin/clone.c:654
+#, c-format
+msgid "--bare and --origin %s options are incompatible."
+msgstr "tùy chọn --bare và --origin %s xung khắc nhau."
+
+#: builtin/clone.c:668
+#, c-format
+msgid "repository '%s' does not exist"
+msgstr "kho chứa '%s' chưa tồn tại"
+
+#: builtin/clone.c:673
+msgid "--depth is ignored in local clones; use file:// instead."
+msgstr "--depth bị lờ đi khi nhân bản nội bộ; hãy sử dụng file:// để thay thế."
+
+#: builtin/clone.c:683
+#, c-format
+msgid "destination path '%s' already exists and is not an empty directory."
+msgstr "đường dẫn đích '%s' đã có từ trước và không phải là một thư mục rỗng."
+
+#: builtin/clone.c:693
+#, c-format
+msgid "working tree '%s' already exists."
+msgstr "cây làm việc '%s' đã sẵn tồn tại rồi."
+
+#: builtin/clone.c:706
+#: builtin/clone.c:720
+#, c-format
+msgid "could not create leading directories of '%s'"
+msgstr "không thể tạo các thư mục dẫn đầu của '%s'"
+
+#: builtin/clone.c:709
+#, c-format
+msgid "could not create work tree dir '%s'."
+msgstr "không thể tạo cây thư mục làm việc dir '%s'."
+
+#: builtin/clone.c:728
+#, c-format
+msgid "Cloning into bare repository '%s'...\n"
+msgstr "Đang nhân bản thành kho chứa bare '%s'...\n"
+
+#: builtin/clone.c:730
+#, c-format
+msgid "Cloning into '%s'...\n"
+msgstr "Đang nhân bản thành '%s'...\n"
+
+#: builtin/clone.c:786
+#, c-format
+msgid "Don't know how to clone %s"
+msgstr "Không biết làm cách nào để nhân bản (clone) %s"
+
+#: builtin/clone.c:835
+#, c-format
+msgid "Remote branch %s not found in upstream %s"
+msgstr "Nhánh máy chủ %s không tìm thấy trong dòng ngược (upstream) %s"
+
+#: builtin/clone.c:842
+msgid "You appear to have cloned an empty repository."
+msgstr "Bạn hình như là đã nhân bản một kho trống rỗng."
+
+#: builtin/column.c:51
+msgid "--command must be the first argument"
+msgstr "--command phải là đối số đầu tiên"
+
+#: builtin/commit.c:43
+msgid ""
+"Your name and email address were configured automatically based\n"
+"on your username and hostname. Please check that they are accurate.\n"
+"You can suppress this message by setting them explicitly:\n"
+"\n"
+"    git config --global user.name \"Your Name\"\n"
+"    git config --global user.email you@example.com\n"
+"\n"
+"After doing this, you may fix the identity used for this commit with:\n"
+"\n"
+"    git commit --amend --reset-author\n"
+msgstr ""
+"Tên và địa chỉ thư điện tử của bạn được cấu hình một cách tự động trên cơ sở\n"
+"tài khoản và địa chỉ máy chủ của bạn. Xin hãy kiểm tra xem chúng có chính xác không.\n"
+"Bạn có thể chặn những thông báo kiểu này bằng cách cài đặt các thông tin trên một cách rõ ràng:\n"
+"\n"
+"    git config --global user.name \"Tên của bạn\"\n"
+"    git config --global user.email you@example.com\n"
+"\n"
+"Sau khi thực hiện xong, bạn có thể sửa chữa định danh được sử dụng cho lần chuyển giao (commit) này với lệnh:\n"
+"\n"
+"    git commit --amend --reset-author\n"
+
+#: builtin/commit.c:55
+msgid ""
+"You asked to amend the most recent commit, but doing so would make\n"
+"it empty. You can repeat your command with --allow-empty, or you can\n"
+"remove the commit entirely with \"git reset HEAD^\".\n"
+msgstr ""
+"Bạn đã yêu cầu amend (tu bổ) phần lớn các lần chuyển giao (commit) gần đây, nhưng làm như thế\n"
+"có thể làm cho nó trở nên trống rỗng. Bạn có thể lặp lại lệnh của mình bằng --allow-empty,\n"
+"hoặc là bạn gỡ bỏ các lần chuyển giao một cách hoàn toàn bằng lệnh:\n"
+"\"git reset HEAD^\".\n"
+
+#: builtin/commit.c:60
+msgid ""
+"The previous cherry-pick is now empty, possibly due to conflict resolution.\n"
+"If you wish to commit it anyway, use:\n"
+"\n"
+"    git commit --allow-empty\n"
+"\n"
+"Otherwise, please use 'git reset'\n"
+msgstr ""
+"Lần cherry-pick trước hiện nay trống rỗng, có lẽ là bởi vì sự phân giải xung đột.\n"
+"Nếu bạn muốn chuyển giao nó cho dù thế nào đi nữa, sử dụng:\n"
+"\n"
+"    git commit --allow-empty\n"
+"\n"
+"Nếu không, hãy thử sử dụng 'git reset'\n"
+
+#: builtin/commit.c:253
+msgid "failed to unpack HEAD tree object"
+msgstr "gặp lỗi khi tháo dỡ HEAD đối tượng cây"
+
+#: builtin/commit.c:295
+msgid "unable to create temporary index"
+msgstr "không thể tạo bảng mục lục tạm thời"
+
+#: builtin/commit.c:301
+msgid "interactive add failed"
+msgstr "việc thêm tương tác gặp lỗi"
+
+#: builtin/commit.c:334
+#: builtin/commit.c:355
+#: builtin/commit.c:405
+msgid "unable to write new_index file"
+msgstr "không thể ghi tập tin lưu bảng mục lục mới (new_index)"
+
+#: builtin/commit.c:386
+msgid "cannot do a partial commit during a merge."
+msgstr "không thể thực hiện việc chuyển giao (commit) cục bộ trong khi đang được hòa trộn."
+
+#: builtin/commit.c:388
+msgid "cannot do a partial commit during a cherry-pick."
+msgstr "không thể thực hiện việc chuyển giao (commit) bộ phận trong khi đang cherry-pick."
+
+#: builtin/commit.c:398
+msgid "cannot read the index"
+msgstr "không đọc được bảng mục lục"
+
+#: builtin/commit.c:418
+msgid "unable to write temporary index file"
+msgstr "không thể ghi tập tin lưu bảng mục lục tạm thời"
+
+#: builtin/commit.c:493
+#: builtin/commit.c:499
+#, c-format
+msgid "invalid commit: %s"
+msgstr "lần chuyển giao (commit) không hợp lệ: %s"
+
+#: builtin/commit.c:522
+msgid "malformed --author parameter"
+msgstr "đối số --author bị dị hình"
+
+#: builtin/commit.c:582
+#, c-format
+msgid "Malformed ident string: '%s'"
+msgstr "Chuỗi thụt lề đầu dòng dị hình: '%s'"
+
+#: builtin/commit.c:620
+#: builtin/commit.c:653
+#: builtin/commit.c:967
+#, c-format
+msgid "could not lookup commit %s"
+msgstr "không thể tìm kiếm commit (lần chuyển giao) %s"
+
+#: builtin/commit.c:632
+#: builtin/shortlog.c:296
+#, c-format
+msgid "(reading log message from standard input)\n"
+msgstr "(đang đọc thông điệp nhật ký từ đầu vào tiêu chuẩn)\n"
+
+#: builtin/commit.c:634
+msgid "could not read log from standard input"
+msgstr "không thể đọc nhật ký từ đầu vào tiêu chuẩn"
+
+#: builtin/commit.c:638
+#, c-format
+msgid "could not read log file '%s'"
+msgstr "không đọc được tệp nhật ký '%s'"
+
+#: builtin/commit.c:644
+msgid "commit has empty message"
+msgstr "lần chuyển giao (commit) có ghi chú trống rỗng"
+
+#: builtin/commit.c:660
+msgid "could not read MERGE_MSG"
+msgstr "không thể đọc MERGE_MSG"
+
+#: builtin/commit.c:664
+msgid "could not read SQUASH_MSG"
+msgstr "không thể đọc SQUASH_MSG"
+
+#: builtin/commit.c:668
+#, c-format
+msgid "could not read '%s'"
+msgstr "Không thể đọc '%s'."
+
+#: builtin/commit.c:720
+msgid "could not write commit template"
+msgstr "không thể ghi mẫu commit"
+
+#: builtin/commit.c:731
+#, c-format
+msgid ""
+"\n"
+"It looks like you may be committing a merge.\n"
+"If this is not correct, please remove the file\n"
+"\t%s\n"
+"and try again.\n"
+msgstr ""
+"\n"
+"Nó trông giống với việc bạn đang chuyển giao một lần hòa trộn.\n"
+"Nếu không phải vậy, xin hãy gỡ bỏ tập tin\n"
+"\t%s\n"
+"và thử lại.\n"
+
+#: builtin/commit.c:736
+#, c-format
+msgid ""
+"\n"
+"It looks like you may be committing a cherry-pick.\n"
+"If this is not correct, please remove the file\n"
+"\t%s\n"
+"and try again.\n"
+msgstr ""
+"\n"
+"Nó trông giống với việc bạn đang chuyển giao một lần cherry-pick.\n"
+"Nếu không phải vậy, xin hãy gỡ bỏ tập tin\n"
+"\t%s\n"
+"và thử lại.\n"
+
+#: builtin/commit.c:748
+msgid ""
+"Please enter the commit message for your changes. Lines starting\n"
+"with '#' will be ignored, and an empty message aborts the commit.\n"
+msgstr ""
+"Hãy nhập vào các thông tin để giải thích các thay đổi của bạn. Những dòng được\n"
+"bắt đầu bằng '#' sẽ được bỏ qua, phần chú thích này nếu rỗng sẽ làm hủy bỏ lần chuyển giao (commit).\n"
+
+#: builtin/commit.c:753
+msgid ""
+"Please enter the commit message for your changes. Lines starting\n"
+"with '#' will be kept; you may remove them yourself if you want to.\n"
+"An empty message aborts the commit.\n"
+msgstr ""
+"Hãy nhập vào các thông tin để giải thích các thay đổi của bạn.Những dòng được\n"
+"bắt đầu bằng '#' sẽ được bỏ qua; bạn có thể xóa chúng đi nếu muốn.\n"
+"Phần chú thích này nếu rỗng sẽ làm hủy bỏ lần chuyển giao (commit).\n"
+
+#: builtin/commit.c:766
+#, c-format
+msgid "%sAuthor:    %s"
+msgstr "%sTác giả:    %s"
+
+#: builtin/commit.c:773
+#, c-format
+msgid "%sCommitter: %s"
+msgstr "%sNgười chuyển giao (commit): %s"
+
+#: builtin/commit.c:793
+msgid "Cannot read index"
+msgstr "không đọc được bảng mục lục"
+
+#: builtin/commit.c:830
+msgid "Error building trees"
+msgstr "Gặp lỗi khi xây dựng cây"
+
+#: builtin/commit.c:845
+#: builtin/tag.c:361
+#, c-format
+msgid "Please supply the message using either -m or -F option.\n"
+msgstr "Xin hãy áp dụng thông điệp sử dụng hoặc là tùy chọn -m hoặc là -F.\n"
+
+#: builtin/commit.c:942
+#, c-format
+msgid "No existing author found with '%s'"
+msgstr "Không tìm thấy tác giả đã sẵn có với '%s'"
+
+#: builtin/commit.c:957
+#: builtin/commit.c:1157
+#, c-format
+msgid "Invalid untracked files mode '%s'"
+msgstr "Chế độ cho các tập tin không bị theo vết không hợp lệ '%s'"
+
+#: builtin/commit.c:997
+msgid "Using both --reset-author and --author does not make sense"
+msgstr "Sử dụng cả hai tùy chọn --reset-author và --author không hợp lý"
+
+#: builtin/commit.c:1008
+msgid "You have nothing to amend."
+msgstr "Không có gì để amend (tu bổ) cả."
+
+#: builtin/commit.c:1011
+msgid "You are in the middle of a merge -- cannot amend."
+msgstr "Bạn đang ở giữa của quá trình hòa trộn -- không thể thực hiện amend (tu bổ)."
+
+#: builtin/commit.c:1013
+msgid "You are in the middle of a cherry-pick -- cannot amend."
+msgstr "Bạn đang ở giữa của quá trình cherry-pick -- không thể thực hiện amend (tu bổ)."
+
+#: builtin/commit.c:1016
+msgid "Options --squash and --fixup cannot be used together"
+msgstr "Các tùy chọn --squash và --fixup không thể sử dụng cùng với nhau"
+
+#: builtin/commit.c:1026
+msgid "Only one of -c/-C/-F/--fixup can be used."
+msgstr "Chỉ một tùy chọn trong số -c/-C/-F/--fixup được sử dụng"
+
+#: builtin/commit.c:1028
+msgid "Option -m cannot be combined with -c/-C/-F/--fixup."
+msgstr "Tùy chọn -m không thể được tổ hợp cùng với -c/-C/-F/--fixup."
+
+#: builtin/commit.c:1036
+msgid "--reset-author can be used only with -C, -c or --amend."
+msgstr "--reset-author chỉ có thể được sử dụng với tùy chọn -C, -c hay --amend."
+
+#: builtin/commit.c:1053
+msgid "Only one of --include/--only/--all/--interactive/--patch can be used."
+msgstr "Chỉ một trong các tùy chọn --include/--only/--all/--interactive/--patch được sử dụng."
+
+#: builtin/commit.c:1055
+msgid "No paths with --include/--only does not make sense."
+msgstr "Không đường dẫn với các tùy chọn --include/--only không hợp lý."
+
+#: builtin/commit.c:1057
+msgid "Clever... amending the last one with dirty index."
+msgstr "Giỏi... đang tu bổ cái cuối với bảng mục lục phi nghĩa."
+
+#: builtin/commit.c:1059
+msgid "Explicit paths specified without -i nor -o; assuming --only paths..."
+msgstr "Những đường dẫn rõ ràng được chỉ ra không có tùy chọn -i cũng không -o; đang giả định --only những-đường-dẫn..."
+
+#: builtin/commit.c:1069
+#: builtin/tag.c:577
+#, c-format
+msgid "Invalid cleanup mode %s"
+msgstr "Chế độ dọn dẹp không hợp lệ %s"
+
+#: builtin/commit.c:1074
+msgid "Paths with -a does not make sense."
+msgstr "Các đường dẫn với tùy chọn -a không hợp lý."
+
+#: builtin/commit.c:1257
+msgid "couldn't look up newly created commit"
+msgstr "không thể tìm thấy lần chuyển giao (commit) mới hơn đã được tạo"
+
+#: builtin/commit.c:1259
+msgid "could not parse newly created commit"
+msgstr "không thể phân tích cú pháp của đối tượng chuyển giao mới hơn đã được tạo"
+
+#: builtin/commit.c:1300
+msgid "detached HEAD"
+msgstr "đã rời khỏi HEAD"
+
+#: builtin/commit.c:1302
+msgid " (root-commit)"
+msgstr " (root-commit)"
+
+#: builtin/commit.c:1446
+msgid "could not parse HEAD commit"
+msgstr "không thể phân tích commit (lần chuyển giao) HEAD"
+
+#: builtin/commit.c:1484
+#: builtin/merge.c:509
+#, c-format
+msgid "could not open '%s' for reading"
+msgstr "không thể mở %s' để đọc"
+
+#: builtin/commit.c:1491
+#, c-format
+msgid "Corrupt MERGE_HEAD file (%s)"
+msgstr "Tập tin MERGE_HEAD sai hỏng (%s)"
+
+#: builtin/commit.c:1498
+msgid "could not read MERGE_MODE"
+msgstr "không thể đọc MERGE_MODE"
+
+#: builtin/commit.c:1517
+#, c-format
+msgid "could not read commit message: %s"
+msgstr "không thể đọc thông điệp (message) commit (lần chuyển giao): %s"
+
+#: builtin/commit.c:1531
+#, c-format
+msgid "Aborting commit; you did not edit the message.\n"
+msgstr "Đang bỏ qua việc chuyển giao (commit); bạn đã không biên soạn thông điệp (message).\n"
+
+#: builtin/commit.c:1536
+#, c-format
+msgid "Aborting commit due to empty commit message.\n"
+msgstr "Đang bỏ qua lần chuyển giao (commit) bởi vì thông điệp của nó trống rỗng.\n"
+
+#: builtin/commit.c:1551
+#: builtin/merge.c:936
+#: builtin/merge.c:961
+msgid "failed to write commit object"
+msgstr "gặp lỗi khi ghi đối tượng chuyển giao (commit)"
+
+#: builtin/commit.c:1572
+msgid "cannot lock HEAD ref"
+msgstr "không thể khóa HEAD ref (tham chiếu)"
+
+#: builtin/commit.c:1576
+msgid "cannot update HEAD ref"
+msgstr "không thể cập nhật HEAD ref (tham chiếu)"
+
+#: builtin/commit.c:1587
+msgid ""
+"Repository has been updated, but unable to write\n"
+"new_index file. Check that disk is not full or quota is\n"
+"not exceeded, and then \"git reset HEAD\" to recover."
+msgstr ""
+"Kho chứa đã hoàn tất việc cập nhật, nhưng không thể ghi vào\n"
+"tập tin new_index (bảng mục lục mới). Hãy kiểm tra xem đĩa có bị đầy quá\n"
+"hay quota (hạn nghạch đĩa cứng) bị vượt quá, và sau đó \"git reset HEAD\" để khắc phục."
+
+#: builtin/describe.c:234
+#, c-format
+msgid "annotated tag %s not available"
+msgstr "thẻ đã được ghi chú %s không sẵn để dùng"
+
+#: builtin/describe.c:238
+#, c-format
+msgid "annotated tag %s has no embedded name"
+msgstr "thẻ được chú giải %s không có tên nhúng"
+
+#: builtin/describe.c:240
+#, c-format
+msgid "tag '%s' is really '%s' here"
+msgstr "thẻ '%s' đã thực sự ở đây '%s' rồi"
+
+#: builtin/describe.c:267
+#, c-format
+msgid "Not a valid object name %s"
+msgstr "Không phải tên đối tượng %s hợp lệ"
+
+#: builtin/describe.c:270
+#, c-format
+msgid "%s is not a valid '%s' object"
+msgstr "%s không phải là một đối tượng '%s' hợp lệ"
+
+#: builtin/describe.c:287
+#, c-format
+msgid "no tag exactly matches '%s'"
+msgstr "không có thẻ nào khớp chính xác với '%s'"
+
+#: builtin/describe.c:289
+#, c-format
+msgid "searching to describe %s\n"
+msgstr "Đang tìm kiếm để mô tả %s\n"
+
+#: builtin/describe.c:329
+#, c-format
+msgid "finished search at %s\n"
+msgstr "việc tìm kiếm đã kết thúc tại %s\n"
+
+#: builtin/describe.c:353
+#, c-format
+msgid ""
+"No annotated tags can describe '%s'.\n"
+"However, there were unannotated tags: try --tags."
+msgstr ""
+"Không có thẻ được chú giải nào được mô tả là '%s'.\n"
+"Tuy nhiên, ở đây có những thẻ không được chú giải: hãy thử --tags."
+
+#: builtin/describe.c:357
+#, c-format
+msgid ""
+"No tags can describe '%s'.\n"
+"Try --always, or create some tags."
+msgstr ""
+"Không có thẻ (tag) có thể mô tả '%s'.\n"
+"Hãy thử --always, hoặt tạo một số thẻ."
+
+#: builtin/describe.c:378
+#, c-format
+msgid "traversed %lu commits\n"
+msgstr "đã xuyên %lu qua lần chuyển giao (commit)\n"
+
+#: builtin/describe.c:381
+#, c-format
+msgid ""
+"more than %i tags found; listed %i most recent\n"
+"gave up search at %s\n"
+msgstr ""
+"tìm thấy nhiều hơn %i thẻ (tag); đã liệt kê %i gần đây nhất\n"
+"bỏ đi tìm kiếm tại %s\n"
+
+#: builtin/describe.c:436
+msgid "--long is incompatible with --abbrev=0"
+msgstr "--long là xung khắc với tùy chọn --abbrev=0"
+
+#: builtin/describe.c:462
+msgid "No names found, cannot describe anything."
+msgstr "Không tìm thấy các tên, không thể mô tả gì cả."
+
+#: builtin/describe.c:482
+msgid "--dirty is incompatible with committishes"
+msgstr "--dirty là xung khắc với các tùy chọn dành cho chuyển giao (commit)"
+
+#: builtin/diff.c:77
+#, c-format
+msgid "'%s': not a regular file or symlink"
+msgstr "'%s': không phải tập tin bình thường hay liên kết tượng trưng"
+
+#: builtin/diff.c:220
+#, c-format
+msgid "invalid option: %s"
+msgstr "tùy chọn sai: %s"
+
+#: builtin/diff.c:297
+msgid "Not a git repository"
+msgstr "Không phải là kho git"
+
+#: builtin/diff.c:341
+#, c-format
+msgid "invalid object '%s' given."
+msgstr "đối tượng đã cho '%s' không hợp lệ."
+
+#: builtin/diff.c:346
+#, c-format
+msgid "more than %d trees given: '%s'"
+msgstr "đã chỉ ra nhiều hơn %d cây (tree): '%s'"
+
+#: builtin/diff.c:356
+#, c-format
+msgid "more than two blobs given: '%s'"
+msgstr "đã cho nhiều hơn hai đối tượng blob: '%s'"
+
+#: builtin/diff.c:364
+#, c-format
+msgid "unhandled object '%s' given."
+msgstr "đã cho đối tượng không thể nắm giữ '%s'."
+
+#: builtin/fetch.c:200
+msgid "Couldn't find remote ref HEAD"
+msgstr "Không thể tìm thấy máy chủ cho tham chiếu HEAD"
+
+#: builtin/fetch.c:253
+#, c-format
+msgid "object %s not found"
+msgstr "Không tìm thấy đối tượng %s"
+
+#: builtin/fetch.c:259
+msgid "[up to date]"
+msgstr "[đã cập nhật]"
+
+#: builtin/fetch.c:273
+#, c-format
+msgid "! %-*s %-*s -> %s  (can't fetch in current branch)"
+msgstr "! %-*s %-*s -> %s  (không thể fetch (lấy về) trong nhánh hiện hành)"
+
+#: builtin/fetch.c:274
+#: builtin/fetch.c:360
+msgid "[rejected]"
+msgstr "[Bị từ chối]"
+
+#: builtin/fetch.c:285
+msgid "[tag update]"
+msgstr "[cập nhật thẻ]"
+
+#: builtin/fetch.c:287
+#: builtin/fetch.c:322
+#: builtin/fetch.c:340
+msgid "  (unable to update local ref)"
+msgstr "  (không thể cập nhật tham chiếu (ref) nội bộ)"
+
+#: builtin/fetch.c:305
+msgid "[new tag]"
+msgstr "[thẻ mới]"
+
+#: builtin/fetch.c:308
+msgid "[new branch]"
+msgstr "[nhánh mới]"
+
+#: builtin/fetch.c:311
+msgid "[new ref]"
+msgstr "[ref (tham chiếu) mới]"
+
+#: builtin/fetch.c:356
+msgid "unable to update local ref"
+msgstr "không thể cập nhật tham chiếu (ref) nội bộ"
+
+#: builtin/fetch.c:356
+msgid "forced update"
+msgstr "cưỡng bức cập nhật"
+
+#: builtin/fetch.c:362
+msgid "(non-fast-forward)"
+msgstr "(non-fast-forward)"
+
+#: builtin/fetch.c:393
+#: builtin/fetch.c:685
+#, c-format
+msgid "cannot open %s: %s\n"
+msgstr "không thể mở %s: %s\n"
+
+#: builtin/fetch.c:402
+#, c-format
+msgid "%s did not send all necessary objects\n"
+msgstr "%s đã không gửi tất cả các đối tượng cần thiết\n"
+
+#: builtin/fetch.c:488
+#, c-format
+msgid "From %.*s\n"
+msgstr "Từ %.*s\n"
+
+#: builtin/fetch.c:499
+#, c-format
+msgid ""
+"some local refs could not be updated; try running\n"
+" 'git remote prune %s' to remove any old, conflicting branches"
+msgstr ""
+"một số tham chiếu (refs) nội bộ không thể được cập nhật; hãy thử chạy\n"
+" 'git remote prune %s' để bỏ đi những nhánh cũ, hay bị xung đột"
+
+#: builtin/fetch.c:549
+#, c-format
+msgid "   (%s will become dangling)"
+msgstr "   (%s sẽ trở thành lủng lẳng (không được quản lý))"
+
+#: builtin/fetch.c:550
+#, c-format
+msgid "   (%s has become dangling)"
+msgstr "   (%s phải trở thành lủng lẳng (không được quản lý))"
+
+#: builtin/fetch.c:557
+msgid "[deleted]"
+msgstr "[đã xóa]"
+
+#: builtin/fetch.c:558
+#: builtin/remote.c:1055
+msgid "(none)"
+msgstr "(không)"
+
+#: builtin/fetch.c:675
+#, c-format
+msgid "Refusing to fetch into current branch %s of non-bare repository"
+msgstr "Từ chối việc lấy (fetch) vào trong nhánh hiện tại %s của một kho chứa không phải kho trần (bare)"
+
+#: builtin/fetch.c:709
+#, c-format
+msgid "Don't know how to fetch from %s"
+msgstr "Không biết làm cách nào để lấy về (fetch) từ %s"
+
+#: builtin/fetch.c:786
+#, c-format
+msgid "Option \"%s\" value \"%s\" is not valid for %s"
+msgstr "Tùy chọn \"%s\" có giá trị \"%s\" là không hợp lệ cho %s"
+
+#: builtin/fetch.c:789
+#, c-format
+msgid "Option \"%s\" is ignored for %s\n"
+msgstr "Tùy chọn \"%s\" bị bỏ qua với %s\n"
+
+#: builtin/fetch.c:888
+#, c-format
+msgid "Fetching %s\n"
+msgstr "Đang lấy (fetch) %s\n"
+
+#: builtin/fetch.c:890
+#: builtin/remote.c:100
+#, c-format
+msgid "Could not fetch %s"
+msgstr "không thể fetch (lấy) %s"
+
+#: builtin/fetch.c:907
+msgid ""
+"No remote repository specified.  Please, specify either a URL or a\n"
+"remote name from which new revisions should be fetched."
+msgstr ""
+"Chưa chỉ ra kho chứa máy chủ.  Xin hãy chỉ định hoặc là URL hoặc\n"
+"tên máy chủ từ cái mà những điểm xét duyệt mới có thể được fetch (lấy về)."
+
+#: builtin/fetch.c:927
+msgid "You need to specify a tag name."
+msgstr "Bạn phải định rõ tên thẻ."
+
+#: builtin/fetch.c:979
+msgid "fetch --all does not take a repository argument"
+msgstr "lệnh lấy về sử dụng tùy chọn --all sẽ không lấy đối số kho chứa"
+
+#: builtin/fetch.c:981
+msgid "fetch --all does not make sense with refspecs"
+msgstr "lệnh lấy về fetch sử dụng tùy chọn --all không hợp lý với refspecs"
+
+#: builtin/fetch.c:992
+#, c-format
+msgid "No such remote or remote group: %s"
+msgstr "không có nhóm máy chủ hay máy chủ như thế: %s"
+
+#: builtin/fetch.c:1000
+msgid "Fetching a group and specifying refspecs does not make sense"
+msgstr "Việc lấy về cả một nhóm và chỉ định refspecs không hợp lý"
+
+#: builtin/gc.c:63
+#, c-format
+msgid "Invalid %s: '%s'"
+msgstr "%s không hợp lệ: '%s'"
+
+#: builtin/gc.c:90
+#, c-format
+msgid "insanely long object directory %.*s"
+msgstr "thư mục đối tượng dài một cách điên rồ  %.*s"
+
+#: builtin/gc.c:221
+#, c-format
+msgid "Auto packing the repository for optimum performance.\n"
+msgstr "Tự động đóng gói kho chứa để tối ưu hóa hiệu suất làm việc.\n"
+
+#: builtin/gc.c:224
+#, c-format
+msgid ""
+"Auto packing the repository for optimum performance. You may also\n"
+"run \"git gc\" manually. See \"git help gc\" for more information.\n"
+msgstr ""
+"Tự động đóng gói kho chứa để tối ưu hóa hiệu suất làm việc.\n"
+"chạy lệnh \"git gc\" một cách thủ công. Hãy xem \"git help gc\" để biết thêm chi tiết.\n"
+
+#: builtin/gc.c:251
+msgid "There are too many unreachable loose objects; run 'git prune' to remove them."
+msgstr "Có quá nhiều đối tượng tự do không được dùng đến; hãy chạy lệnh 'git prune' để xóa bỏ chúng đi."
+
+#: builtin/grep.c:216
+#, c-format
+msgid "grep: failed to create thread: %s"
+msgstr "grep: gặp lỗi tạo tuyến (thread): %s"
+
+#: builtin/grep.c:402
+#, c-format
+msgid "Failed to chdir: %s"
+msgstr "Gặp lỗi với lệnh chdir: %s"
+
+#: builtin/grep.c:478
+#: builtin/grep.c:512
+#, c-format
+msgid "unable to read tree (%s)"
+msgstr "không thể đọc cây (%s)"
+
+#: builtin/grep.c:526
+#, c-format
+msgid "unable to grep from object of type %s"
+msgstr "không thể thực hiện lệnh grep (lọc tìm) từ đối tượng thuộc kiểu %s"
+
+#: builtin/grep.c:584
+#, c-format
+msgid "switch `%c' expects a numerical value"
+msgstr "chuyển đến `%c' mong chờ một giá trị bằng số"
+
+#: builtin/grep.c:601
+#, c-format
+msgid "cannot open '%s'"
+msgstr "không mở được '%s'"
+
+#: builtin/grep.c:885
+msgid "no pattern given."
+msgstr "chưa chỉ ra mẫu."
+
+#: builtin/grep.c:899
+#, c-format
+msgid "bad object %s"
+msgstr "đối tượng sai %s"
+
+#: builtin/grep.c:940
+msgid "--open-files-in-pager only works on the worktree"
+msgstr "--open-files-in-pager chỉ làm việc trên cây-làm-việc"
+
+#: builtin/grep.c:963
+msgid "--cached or --untracked cannot be used with --no-index."
+msgstr "--cached hay --untracked không được sử dụng với --no-index."
+
+#: builtin/grep.c:968
+msgid "--no-index or --untracked cannot be used with revs."
+msgstr "--no-index hay --untracked không được sử dụng cùng với các tùy chọn liên quan đến revs."
+
+#: builtin/grep.c:971
+msgid "--[no-]exclude-standard cannot be used for tracked contents."
+msgstr "--[no-]exclude-standard không thể sử dụng cho nội dung lưu dấu vết."
+
+#: builtin/grep.c:979
+msgid "both --cached and trees are given."
+msgstr "cả hai --cached và các cây phải được chỉ ra."
+
+#: builtin/help.c:63
+#, c-format
+msgid "unrecognized help format '%s'"
+msgstr "không nhận ra định dạng trợ giúp '%s'"
+
+#: builtin/help.c:91
+msgid "Failed to start emacsclient."
+msgstr "Lỗi khởi chạy emacsclient."
+
+#: builtin/help.c:104
+msgid "Failed to parse emacsclient version."
+msgstr "Gặp lỗi khi phân tích phiên bản emacsclient."
+
+#: builtin/help.c:112
+#, c-format
+msgid "emacsclient version '%d' too old (< 22)."
+msgstr "phiên bản của emacsclient '%d' quá cũ (< 22)."
+
+#: builtin/help.c:130
+#: builtin/help.c:158
+#: builtin/help.c:167
+#: builtin/help.c:175
+#, c-format
+msgid "failed to exec '%s': %s"
+msgstr "gặp lỗi khi thực thi '%s': %s"
+
+#: builtin/help.c:215
+#, c-format
+msgid ""
+"'%s': path for unsupported man viewer.\n"
+"Please consider using 'man.<tool>.cmd' instead."
+msgstr ""
+"'%s': đường dẫn không hỗ trợ bộ trình chiếu man.\n"
+"Hãy cân nhắc đến việc sử dụng 'man.<tool>.cmd' để thay thế."
+
+#: builtin/help.c:227
+#, c-format
+msgid ""
+"'%s': cmd for supported man viewer.\n"
+"Please consider using 'man.<tool>.path' instead."
+msgstr ""
+"'%s': cmd (lệnh) hỗ trợ bộ trình chiếu man.\n"
+"Hãy cân nhắc đến việc sử dụng 'man.<tool>.path' để thay thế."
+
+#: builtin/help.c:291
+msgid "The most commonly used git commands are:"
+msgstr "Những lệnh git hay được sử dụng nhất là:"
+
+#: builtin/help.c:359
+#, c-format
+msgid "'%s': unknown man viewer."
+msgstr "'%s': không rõ chương trình xem man."
+
+#: builtin/help.c:376
+msgid "no man viewer handled the request"
+msgstr "không có trình xem trợ giúp dạng manpage tiếp hợp với yêu cầu"
+
+#: builtin/help.c:384
+msgid "no info viewer handled the request"
+msgstr "không có trình xem trợ giúp dạng info tiếp hợp với yêu cầu"
+
+#: builtin/help.c:395
+#, c-format
+msgid "'%s': not a documentation directory."
+msgstr "'%s': không phải là một thư mục tài liệu."
+
+#: builtin/help.c:436
+#: builtin/help.c:443
+#, c-format
+msgid "usage: %s%s"
+msgstr "cách sử dụng: %s%s"
+
+#: builtin/help.c:459
+#, c-format
+msgid "`git %s' is aliased to `%s'"
+msgstr "`git %s' được đặt bí danh thành `%s'"
+
+#: builtin/index-pack.c:170
+#, c-format
+msgid "object type mismatch at %s"
+msgstr "kiểu đối tượng không khớp tại %s"
+
+#: builtin/index-pack.c:190
+msgid "object of unexpected type"
+msgstr "đối tượng của kiểu không mong đợi"
+
+#: builtin/index-pack.c:227
+#, c-format
+msgid "cannot fill %d byte"
+msgid_plural "cannot fill %d bytes"
+msgstr[0] "không thể điền vào %d byte"
+msgstr[1] "không thể điền vào %d byte"
+
+#: builtin/index-pack.c:237
+msgid "early EOF"
+msgstr "vừa đúng lúc EOF"
+
+#: builtin/index-pack.c:238
+msgid "read error on input"
+msgstr "lỗi đọc ở đầu vào"
+
+#: builtin/index-pack.c:250
+msgid "used more bytes than were available"
+msgstr "sử dụng nhiều hơn số lượng byte mà nó sẵn có"
+
+#: builtin/index-pack.c:257
+msgid "pack too large for current definition of off_t"
+msgstr "pack quá lớn so với định nghĩa hiện tại của kiểu off_t"
+
+#: builtin/index-pack.c:273
+#, c-format
+msgid "unable to create '%s'"
+msgstr "không thể tạo '%s'"
+
+#: builtin/index-pack.c:278
+#, c-format
+msgid "cannot open packfile '%s'"
+msgstr "không thể mở packfile '%s'"
+
+#: builtin/index-pack.c:292
+msgid "pack signature mismatch"
+msgstr "chữ ký cho pack không khớp"
+
+#: builtin/index-pack.c:312
+#, c-format
+msgid "pack has bad object at offset %lu: %s"
+msgstr "pack có đối tượng sai khoảng bù (offset) %lu: %s"
+
+#: builtin/index-pack.c:434
+#, c-format
+msgid "inflate returned %d"
+msgstr "xả nén trả về %d"
+
+#: builtin/index-pack.c:483
+msgid "offset value overflow for delta base object"
+msgstr "tràn giá trị khoảng bù cho đối tượng delta cơ sở"
+
+#: builtin/index-pack.c:491
+msgid "delta base offset is out of bound"
+msgstr "khoảng bù cơ sở cho delta nằm ngoài phạm vi"
+
+#: builtin/index-pack.c:499
+#, c-format
+msgid "unknown object type %d"
+msgstr "không hiểu kiểu đối tượng %d"
+
+#: builtin/index-pack.c:531
+msgid "cannot pread pack file"
+msgstr "không thể chạy hàm pread cho tập tin pack"
+
+#: builtin/index-pack.c:533
+#, c-format
+msgid "premature end of pack file, %lu byte missing"
+msgid_plural "premature end of pack file, %lu bytes missing"
+msgstr[0] "tập tin pack bị kết thúc sớm, %lu byte bị thiếu"
+msgstr[1] "tập tin pack bị kết thúc sớm, %lu byte bị thiếu"
+
+#: builtin/index-pack.c:555
+msgid "serious inflate inconsistency"
+msgstr "sự mâu thuẫn xả nén nghiêm trọng"
+
+#: builtin/index-pack.c:646
+#: builtin/index-pack.c:652
+#: builtin/index-pack.c:675
+#: builtin/index-pack.c:709
+#: builtin/index-pack.c:718
+#, c-format
+msgid "SHA1 COLLISION FOUND WITH %s !"
+msgstr "SỰ VA CHẠM SHA1 ĐÃ XẢY RA VỚI %s!"
+
+#: builtin/index-pack.c:649
+#: builtin/pack-objects.c:170
+#: builtin/pack-objects.c:262
+#, c-format
+msgid "unable to read %s"
+msgstr "không thể đọc %s"
+
+#: builtin/index-pack.c:715
+#, c-format
+msgid "cannot read existing object %s"
+msgstr "không thể đọc đối tượng đã tồn tại %s"
+
+#: builtin/index-pack.c:729
+#, c-format
+msgid "invalid blob object %s"
+msgstr "đối tượng blob không hợp lệ %s"
+
+#: builtin/index-pack.c:744
+#, c-format
+msgid "invalid %s"
+msgstr "%s không hợp lệ"
+
+#: builtin/index-pack.c:746
+msgid "Error in object"
+msgstr "Lỗi trong đối tượng"
+
+#: builtin/index-pack.c:748
+#, c-format
+msgid "Not all child objects of %s are reachable"
+msgstr "Không phải tất cả các đối tượng con của %s là có thể với tới được"
+
+#: builtin/index-pack.c:818
+#: builtin/index-pack.c:844
+msgid "failed to apply delta"
+msgstr "gặp lỗi khi áp dụng delta"
+
+#: builtin/index-pack.c:983
+msgid "Receiving objects"
+msgstr "Đang nhận về các đối tượng"
+
+#: builtin/index-pack.c:983
+msgid "Indexing objects"
+msgstr "Các đối tượng bảng mục lục"
+
+#: builtin/index-pack.c:1009
+msgid "pack is corrupted (SHA1 mismatch)"
+msgstr "pack bị sai hỏng (SHA1 không khớp)"
+
+#: builtin/index-pack.c:1014
+msgid "cannot fstat packfile"
+msgstr "không thể fstat packfile"
+
+#: builtin/index-pack.c:1017
+msgid "pack has junk at the end"
+msgstr "pack có phần thừa ở cuối"
+
+#: builtin/index-pack.c:1028
+msgid "confusion beyond insanity in parse_pack_objects()"
+msgstr "lộn xộn hơn cả điên rồ khi chạy hàm parse_pack_objects()"
+
+#: builtin/index-pack.c:1051
+msgid "Resolving deltas"
+msgstr "Đang phân giải các delta"
+
+#: builtin/index-pack.c:1102
+msgid "confusion beyond insanity"
+msgstr "lộn xộn hơn cả điên rồ"
+
+#: builtin/index-pack.c:1121
+#, c-format
+msgid "pack has %d unresolved delta"
+msgid_plural "pack has %d unresolved deltas"
+msgstr[0] "pack có %d delta chưa được giải quyết"
+msgstr[1] "pack có %d delta chưa được giải quyết"
+
+#: builtin/index-pack.c:1146
+#, c-format
+msgid "unable to deflate appended object (%d)"
+msgstr "không thể xả đối tượng nối thêm (%d)"
+
+#: builtin/index-pack.c:1225
+#, c-format
+msgid "local object %s is corrupt"
+msgstr "đối tượng nội bộ %s bị hỏng"
+
+#: builtin/index-pack.c:1249
+msgid "error while closing pack file"
+msgstr "gặp lỗi trong khi đóng tập tin pack"
+
+#: builtin/index-pack.c:1262
+#, c-format
+msgid "cannot write keep file '%s'"
+msgstr "không thể ghi tập tin giữ lại '%s'"
+
+#: builtin/index-pack.c:1270
+#, c-format
+msgid "cannot close written keep file '%s'"
+msgstr "không thể đóng tập tin giữ lại đã được ghi '%s'"
+
+#: builtin/index-pack.c:1283
+msgid "cannot store pack file"
+msgstr "không thể lưu tập tin pack"
+
+#: builtin/index-pack.c:1294
+msgid "cannot store index file"
+msgstr "không thể lưu trữ tập tin ghi mục lục"
+
+#: builtin/index-pack.c:1395
+#, c-format
+msgid "Cannot open existing pack file '%s'"
+msgstr "Không thể mở tập tin pack đã sẵn có '%s' "
+
+#: builtin/index-pack.c:1397
+#, c-format
+msgid "Cannot open existing pack idx file for '%s'"
+msgstr "Không thể mở tập tin 'pack idx' cho '%s'"
+
+#: builtin/index-pack.c:1444
+#, c-format
+msgid "non delta: %d object"
+msgid_plural "non delta: %d objects"
+msgstr[0] "không delta: %d đối tượng"
+msgstr[1] "không delta: %d đối tượng"
+
+#: builtin/index-pack.c:1451
+#, c-format
+msgid "chain length = %d: %lu object"
+msgid_plural "chain length = %d: %lu objects"
+msgstr[0] "chiều dài xích = %d: %lu đối tượng"
+msgstr[1] "chiều dài xích = %d: %lu đối tượng"
+
+#: builtin/index-pack.c:1478
+msgid "Cannot come back to cwd"
+msgstr "Không thể quay lại cwd"
+
+#: builtin/index-pack.c:1522
+#: builtin/index-pack.c:1525
+#: builtin/index-pack.c:1537
+#: builtin/index-pack.c:1541
+#, c-format
+msgid "bad %s"
+msgstr "%s sai"
+
+#: builtin/index-pack.c:1555
+msgid "--fix-thin cannot be used without --stdin"
+msgstr "--fix-thin không thể được dùng mà không có --stdin"
+
+#: builtin/index-pack.c:1559
+#: builtin/index-pack.c:1569
+#, c-format
+msgid "packfile name '%s' does not end with '.pack'"
+msgstr "tên tập tin packfile '%s' không được kết thúc bằng đuôi '.pack'"
+
+#: builtin/index-pack.c:1578
+msgid "--verify with no packfile name given"
+msgstr "dùng tùy chọn --verify mà không đưa ra tên packfile"
+
+#: builtin/init-db.c:35
+#, c-format
+msgid "Could not make %s writable by group"
+msgstr "Không thể làm %s được ghi bởi nhóm"
+
+#: builtin/init-db.c:62
+#, c-format
+msgid "insanely long template name %s"
+msgstr "tên mẫu dài một cách điên rồ %s"
+
+#: builtin/init-db.c:67
+#, c-format
+msgid "cannot stat '%s'"
+msgstr "không thể lấy trạng thái (stat) về '%s'"
+
+#: builtin/init-db.c:73
+#, c-format
+msgid "cannot stat template '%s'"
+msgstr "không thể stat (lấy trạng thái về) mẫu '%s'"
+
+#: builtin/init-db.c:80
+#, c-format
+msgid "cannot opendir '%s'"
+msgstr "không thể opendir '%s'"
+
+#: builtin/init-db.c:97
+#, c-format
+msgid "cannot readlink '%s'"
+msgstr "không thể readlink '%s'"
+
+#: builtin/init-db.c:99
+#, c-format
+msgid "insanely long symlink %s"
+msgstr "liên kết tượng trưng dài một cách điên rồ %s"
+
+#: builtin/init-db.c:102
+#, c-format
+msgid "cannot symlink '%s' '%s'"
+msgstr "không thể tạo liên kết tượng trưng (symlink) '%s' '%s'"
+
+#: builtin/init-db.c:106
+#, c-format
+msgid "cannot copy '%s' to '%s'"
+msgstr "không thể sao chép %s sang %s"
+
+#: builtin/init-db.c:110
+#, c-format
+msgid "ignoring template %s"
+msgstr "đang lờ đi mẫu %s"
+
+#: builtin/init-db.c:133
+#, c-format
+msgid "insanely long template path %s"
+msgstr "đường dẫn mẫu dài một cách điên rồ  %s"
+
+#: builtin/init-db.c:141
+#, c-format
+msgid "templates not found %s"
+msgstr "các mẫu không được tìm thấy %s"
+
+#: builtin/init-db.c:154
+#, c-format
+msgid "not copying templates of a wrong format version %d from '%s'"
+msgstr "không sao chép các mẫu của phiên bản sai định dạng %d từ '%s'"
+
+#: builtin/init-db.c:192
+#, c-format
+msgid "insane git directory %s"
+msgstr "thư mục git điên rồ %s"
+
+#: builtin/init-db.c:322
+#: builtin/init-db.c:325
+#, c-format
+msgid "%s already exists"
+msgstr "%s đã tồn tại rồi"
+
+#: builtin/init-db.c:354
+#, c-format
+msgid "unable to handle file type %d"
+msgstr "không thể handle tệp tin kiểu %d"
+
+#: builtin/init-db.c:357
+#, c-format
+msgid "unable to move %s to %s"
+msgstr "không di chuyển được %s vào %s"
+
+#: builtin/init-db.c:362
+#, c-format
+msgid "Could not create git link %s"
+msgstr "Không thể tạo liên kết git '%s'"
+
+#.
+#. * TRANSLATORS: The first '%s' is either "Reinitialized
+#. * existing" or "Initialized empty", the second " shared" or
+#. * "", and the last '%s%s' is the verbatim directory name.
+#.
+#: builtin/init-db.c:419
+#, c-format
+msgid "%s%s Git repository in %s%s\n"
+msgstr "%s%s kho Git trong %s%s\n"
+
+#: builtin/init-db.c:420
+msgid "Reinitialized existing"
+msgstr "Khởi tạo lại đã sẵn có rồi"
+
+#: builtin/init-db.c:420
+msgid "Initialized empty"
+msgstr "Khởi tạo trống rỗng"
+
+#: builtin/init-db.c:421
+msgid " shared"
+msgstr " đã chia sẻ"
+
+#: builtin/init-db.c:440
+msgid "cannot tell cwd"
+msgstr "không nói chuyện được với lệnh cwd"
+
+#: builtin/init-db.c:521
+#: builtin/init-db.c:528
+#, c-format
+msgid "cannot mkdir %s"
+msgstr "không thể mkdir (tạo thư mục): %s"
+
+#: builtin/init-db.c:532
+#, c-format
+msgid "cannot chdir to %s"
+msgstr "không thể chdir (chuyển đổi thư mục) sang %s"
+
+#: builtin/init-db.c:554
+#, c-format
+msgid "%s (or --work-tree=<directory>) not allowed without specifying %s (or --git-dir=<directory>)"
+msgstr "%s (hoặc --work-tree=<thư-mục>) không cho phép không chỉ định %s (hoặc --git-dir=<thư-mục>)"
+
+#: builtin/init-db.c:578
+msgid "Cannot access current working directory"
+msgstr "Không thể truy cập thư mục làm việc hiện hành"
+
+#: builtin/init-db.c:585
+#, c-format
+msgid "Cannot access work tree '%s'"
+msgstr "không thể truy cập cây (tree) làm việc '%s'"
+
+#: builtin/log.c:189
+#, c-format
+msgid "Final output: %d %s\n"
+msgstr "Kết xuất cuối cùng: %d %s\n"
+
+#: builtin/log.c:402
+#: builtin/log.c:490
+#, c-format
+msgid "Could not read object %s"
+msgstr "Không thể đọc đối tượng %s"
+
+#: builtin/log.c:514
+#, c-format
+msgid "Unknown type: %d"
+msgstr "Không nhận ra kiểu: %d"
+
+#: builtin/log.c:603
+msgid "format.headers without value"
+msgstr "format.headers không có giá trị cụ thể"
+
+#: builtin/log.c:677
+msgid "name of output directory is too long"
+msgstr "tên của thư mục kết xuất quá dài"
+
+#: builtin/log.c:688
+#, c-format
+msgid "Cannot open patch file %s"
+msgstr "Không thể mở tập tin miếng vá: %s"
+
+#: builtin/log.c:702
+msgid "Need exactly one range."
+msgstr "Cần chính xác một vùng."
+
+#: builtin/log.c:710
+msgid "Not a range."
+msgstr "Không phải là một vùng."
+
+#: builtin/log.c:787
+msgid "Cover letter needs email format"
+msgstr "'Cover letter' cần cho định dạng thư"
+
+#: builtin/log.c:860
+#, c-format
+msgid "insane in-reply-to: %s"
+msgstr "in-reply-to điên rồ: %s"
+
+#: builtin/log.c:933
+msgid "Two output directories?"
+msgstr "Hai thư mục kết xuất?"
+
+#: builtin/log.c:1154
+#, c-format
+msgid "bogus committer info %s"
+msgstr "thông tin người chuyển giao không có thực %s"
+
+#: builtin/log.c:1199
+msgid "-n and -k are mutually exclusive."
+msgstr "-n và  -k loại từ lẫn nhau."
+
+#: builtin/log.c:1201
+msgid "--subject-prefix and -k are mutually exclusive."
+msgstr "--subject-prefix và -k xung khắc nhau."
+
+#: builtin/log.c:1209
+msgid "--name-only does not make sense"
+msgstr "--name-only không hợp lý"
+
+#: builtin/log.c:1211
+msgid "--name-status does not make sense"
+msgstr "--name-status không hợp lý"
+
+#: builtin/log.c:1213
+msgid "--check does not make sense"
+msgstr "--check không hợp lý"
+
+#: builtin/log.c:1236
+msgid "standard output, or directory, which one?"
+msgstr "đầu ra chuẩn, hay thư mục, chọn cái nào?"
+
+#: builtin/log.c:1238
+#, c-format
+msgid "Could not create directory '%s'"
+msgstr "Không thể tạo thư mục '%s'"
+
+#: builtin/log.c:1391
+msgid "Failed to create output files"
+msgstr "Gặp lỗi khi tạo các tập tin kết xuất"
+
+#: builtin/log.c:1495
+#, c-format
+msgid "Could not find a tracked remote branch, please specify <upstream> manually.\n"
+msgstr "Không tìm thấy nhánh mạng bị theo vết, hãy chỉ định <dòng-ngược> một cách thủ công.\n"
+
+#: builtin/log.c:1511
+#: builtin/log.c:1513
+#: builtin/log.c:1525
+#, c-format
+msgid "Unknown commit %s"
+msgstr "Không hiểu lần chuyển giao (commit) %s"
+
+#: builtin/merge.c:90
+msgid "switch `m' requires a value"
+msgstr "switch `m' yêu cầu một giá trị"
+
+#: builtin/merge.c:127
+#, c-format
+msgid "Could not find merge strategy '%s'.\n"
+msgstr "Không tìm thấy chiến lược hòa trộn '%s'.\n"
+
+#: builtin/merge.c:128
+#, c-format
+msgid "Available strategies are:"
+msgstr "Các chiến lược sẵn sàng là:"
+
+#: builtin/merge.c:133
+#, c-format
+msgid "Available custom strategies are:"
+msgstr "Các chiến lược tùy chỉnh sẵn sàng là:"
+
+#: builtin/merge.c:240
+msgid "could not run stash."
+msgstr "không thể chạy stash."
+
+#: builtin/merge.c:245
+msgid "stash failed"
+msgstr "stash gặp lỗi"
+
+#: builtin/merge.c:250
+#, c-format
+msgid "not a valid object: %s"
+msgstr "không phải là một đối tượng hợp lệ: %s"
+
+#: builtin/merge.c:269
+#: builtin/merge.c:286
+msgid "read-tree failed"
+msgstr "read-tree gặp lỗi"
+
+#: builtin/merge.c:316
+msgid " (nothing to squash)"
+msgstr " (không có ghì để squash)"
+
+#: builtin/merge.c:329
+#, c-format
+msgid "Squash commit -- not updating HEAD\n"
+msgstr "Squash commit -- không cập nhật HEAD\n"
+
+#: builtin/merge.c:361
+msgid "Writing SQUASH_MSG"
+msgstr "Đang ghi SQUASH_MSG"
+
+#: builtin/merge.c:363
+msgid "Finishing SQUASH_MSG"
+msgstr "Hoàn thành SQUASH_MSG"
+
+#: builtin/merge.c:386
+#, c-format
+msgid "No merge message -- not updating HEAD\n"
+msgstr "Không thông điệp hòa trộn -- không cập nhật HEAD\n"
+
+#: builtin/merge.c:437
+#, c-format
+msgid "'%s' does not point to a commit"
+msgstr "'%s' không chỉ đến một lần chuyển giao (commit) nào cả"
+
+#: builtin/merge.c:536
+#, c-format
+msgid "Bad branch.%s.mergeoptions string: %s"
+msgstr "Chuỗi branch.%s.mergeoptions sai: %s"
+
+#: builtin/merge.c:629
+msgid "git write-tree failed to write a tree"
+msgstr "lệnh git write-tree gặp lỗi khi ghi một cây"
+
+#: builtin/merge.c:679
+msgid "failed to read the cache"
+msgstr "gặp lỗi khi đọc bộ nhớ tạm"
+
+#: builtin/merge.c:697
+msgid "Unable to write index."
+msgstr "Không thể ghi bảng mục lục"
+
+#: builtin/merge.c:710
+msgid "Not handling anything other than two heads merge."
+msgstr "Không cầm nắm gì ngoài hai head hòa trộn"
+
+#: builtin/merge.c:724
+#, c-format
+msgid "Unknown option for merge-recursive: -X%s"
+msgstr "Không hiểu tùy chọn cho merge-recursive: -X%s"
+
+#: builtin/merge.c:738
+#, c-format
+msgid "unable to write %s"
+msgstr "không ghi được %s"
+
+#: builtin/merge.c:877
+#, c-format
+msgid "Could not read from '%s'"
+msgstr "Không thể đọc từ '%s'"
+
+#: builtin/merge.c:886
+#, c-format
+msgid "Not committing merge; use 'git commit' to complete the merge.\n"
+msgstr "Vẫn chưa hòa trộn các lần chuyển giao (commit); sử dụng lệnh 'git commit' để hoàn tất việc hòa trộn.\n"
+
+#: builtin/merge.c:892
+msgid ""
+"Please enter a commit message to explain why this merge is necessary,\n"
+"especially if it merges an updated upstream into a topic branch.\n"
+"\n"
+"Lines starting with '#' will be ignored, and an empty message aborts\n"
+"the commit.\n"
+msgstr ""
+"Hãy nhập vào các thông tin để giải thích tại sao sự hòa trộn này là cần thiết,\n"
+"đặc biệt là khi nó hòa trộn dòng ngược đã cập nhật vào trong một nhánh topic.\n"
+"\n"
+"Những dòng được bắt đầu bằng '#' sẽ được bỏ qua, và phần chú thích này nếu rỗng\n"
+"sẽ làm hủy bỏ lần chuyển giao (commit).\n"
+
+#: builtin/merge.c:916
+msgid "Empty commit message."
+msgstr "Chú thích của lần commit (chuyển giao) bị trống rỗng."
+
+#: builtin/merge.c:928
+#, c-format
+msgid "Wonderful.\n"
+msgstr "Thần kỳ.\n"
+
+#: builtin/merge.c:993
+#, c-format
+msgid "Automatic merge failed; fix conflicts and then commit the result.\n"
+msgstr "Việc tự động hòa trộn gặp lỗi; hãy sửa các xung đột sau đó chuyển giao (commit) kết quả.\n"
+
+#: builtin/merge.c:1009
+#, c-format
+msgid "'%s' is not a commit"
+msgstr "%s không phải là một lần commit (chuyển giao)"
+
+#: builtin/merge.c:1050
+msgid "No current branch."
+msgstr "không phải nhánh hiện hành"
+
+#: builtin/merge.c:1052
+msgid "No remote for the current branch."
+msgstr "Không có máy chủ cho nhánh hiện hành."
+
+#: builtin/merge.c:1054
+msgid "No default upstream defined for the current branch."
+msgstr "Không có dòng ngược mặc định được định nghĩa cho nhánh hiện hành."
+
+#: builtin/merge.c:1059
+#, c-format
+msgid "No remote tracking branch for %s from %s"
+msgstr "Không nhánh mạng theo vết cho %s từ %s"
+
+#: builtin/merge.c:1146
+#: builtin/merge.c:1303
+#, c-format
+msgid "%s - not something we can merge"
+msgstr "%s - không phải là một số thứ chúng tôi có thể hòa trộn"
+
+#: builtin/merge.c:1214
+msgid "There is no merge to abort (MERGE_HEAD missing)."
+msgstr "Ở đây không có lần hòa trộn nào được hủy bỏ giữa chừng cả (không thấy MERGE_HEAD)."
+
+#: builtin/merge.c:1230
+#: git-pull.sh:31
+msgid ""
+"You have not concluded your merge (MERGE_HEAD exists).\n"
+"Please, commit your changes before you can merge."
+msgstr ""
+"Bạn chưa kết thúc việc hòa trộng (MERGE_HEAD vẫn tồn tại).\n"
+"Hãy chuyển giao (commit) các thay đổi trước khi bạn có thể hòa trộn."
+
+#: builtin/merge.c:1233
+#: git-pull.sh:34
+msgid "You have not concluded your merge (MERGE_HEAD exists)."
+msgstr "Bạn chưa kết thúc việc hòa trộng (MERGE_HEAD vẫn tồn tại)."
+
+#: builtin/merge.c:1237
+msgid ""
+"You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
+"Please, commit your changes before you can merge."
+msgstr ""
+"Bạn chưa kết thúc việc cherry-pick (CHERRY_PICK_HEAD vẫn tồn tại).\n"
+"Hãy chuyển giao (commit) các thay đổi trước khi bạn có thể hòa trộn."
+
+#: builtin/merge.c:1240
+msgid "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."
+msgstr "Bạn chưa kết thúc việc cherry-pick (CHERRY_PICK_HEAD vẫn tồn tại)."
+
+#: builtin/merge.c:1249
+msgid "You cannot combine --squash with --no-ff."
+msgstr "Bạn không thể tổ hợp --squash với --no-ff."
+
+#: builtin/merge.c:1254
+msgid "You cannot combine --no-ff with --ff-only."
+msgstr "Bạn không thể tổ hợp --no-ff với --ff-only."
+
+#: builtin/merge.c:1261
+msgid "No commit specified and merge.defaultToUpstream not set."
+msgstr "Không chỉ ra lần chuyển giao (commit) và merge.defaultToUpstream chưa được đặt."
+
+#: builtin/merge.c:1293
+msgid "Can merge only exactly one commit into empty head"
+msgstr "Không thể hòa trộn một cách đúng đắn một lần chuyển giao (commit) vào một head rỗng"
+
+#: builtin/merge.c:1296
+msgid "Squash commit into empty head not supported yet"
+msgstr "Squash commit vào một head trống rỗng vẫn chưa được hỗ trợ"
+
+#: builtin/merge.c:1298
+msgid "Non-fast-forward commit does not make sense into an empty head"
+msgstr "Chuyển giao (commit) không-fast-forward không hợp lý ở trong một head trống rỗng"
+
+#: builtin/merge.c:1413
+#, c-format
+msgid "Updating %s..%s\n"
+msgstr "Đang cập nhật %s..%s\n"
+
+#: builtin/merge.c:1451
+#, c-format
+msgid "Trying really trivial in-index merge...\n"
+msgstr "Đang thử hòa trộn kiểu 'trivial in-index'...\n"
+
+#: builtin/merge.c:1458
+#, c-format
+msgid "Nope.\n"
+msgstr "Không.\n"
+
+#: builtin/merge.c:1490
+msgid "Not possible to fast-forward, aborting."
+msgstr "Thực hiện lệnh fast-forward là không thể được, đang bỏ qua."
+
+#: builtin/merge.c:1513
+#: builtin/merge.c:1592
+#, c-format
+msgid "Rewinding the tree to pristine...\n"
+msgstr "Đang tua lại cây thành thời xa xưa...\n"
+
+#: builtin/merge.c:1517
+#, c-format
+msgid "Trying merge strategy %s...\n"
+msgstr "Đang thử chiến lược hòa trộn %s...\n"
+
+#: builtin/merge.c:1583
+#, c-format
+msgid "No merge strategy handled the merge.\n"
+msgstr "Không có chiến lược hòa trộn nào được nắm giữ (handle) sự hòa trộn.\n"
+
+#: builtin/merge.c:1585
+#, c-format
+msgid "Merge with strategy %s failed.\n"
+msgstr "Hòa trộn với chiến lược %s gặp lỗi.\n"
+
+#: builtin/merge.c:1594
+#, c-format
+msgid "Using the %s to prepare resolving by hand.\n"
+msgstr "Sử dụng %s để chuẩn bị giải quyết bằng tay.\n"
+
+#: builtin/merge.c:1606
+#, c-format
+msgid "Automatic merge went well; stopped before committing as requested\n"
+msgstr "Hòa trộn tự động đã trở nên tốt; bị dừng trước khi việc chuyển giao được yêu cầu\n"
+
+#: builtin/mv.c:108
+#, c-format
+msgid "Checking rename of '%s' to '%s'\n"
+msgstr "Đang kiểm tra việc đổi tên của '%s' thành '%s'\n"
+
+#: builtin/mv.c:112
+msgid "bad source"
+msgstr "nguồn sai"
+
+#: builtin/mv.c:115
+msgid "can not move directory into itself"
+msgstr "không thể di chuyển một thư mục vào trong chính nó được"
+
+#: builtin/mv.c:118
+msgid "cannot move directory over file"
+msgstr "không di chuyển được thư mục thông qua tập tin"
+
+#: builtin/mv.c:128
+#, c-format
+msgid "Huh? %.*s is in index?"
+msgstr "Hả? %.*s trong bảng mục lục à?"
+
+#: builtin/mv.c:140
+msgid "source directory is empty"
+msgstr "thư mục nguồn là trống rỗng"
+
+#: builtin/mv.c:171
+msgid "not under version control"
+msgstr "không nằm dưới sự quản lý mã nguồn"
+
+#: builtin/mv.c:173
+msgid "destination exists"
+msgstr "đích đã tồn tại sẵn rồi"
+
+#: builtin/mv.c:181
+#, c-format
+msgid "overwriting '%s'"
+msgstr "đang ghi đè lên '%s'"
+
+#: builtin/mv.c:184
+msgid "Cannot overwrite"
+msgstr "Không thể ghi chèn"
+
+#: builtin/mv.c:187
+msgid "multiple sources for the same target"
+msgstr "Nhiều nguồn cho cùng một đích"
+
+#: builtin/mv.c:202
+#, c-format
+msgid "%s, source=%s, destination=%s"
+msgstr "%s, nguồn=%s, đích=%s"
+
+#: builtin/mv.c:212
+#, c-format
+msgid "Renaming %s to %s\n"
+msgstr "Đang thay đổi tên %s thành %s\n"
+
+#: builtin/mv.c:215
+#: builtin/remote.c:731
+#, c-format
+msgid "renaming '%s' failed"
+msgstr "đổi tên %s gặp lỗi"
+
+#: builtin/notes.c:139
+#, c-format
+msgid "unable to start 'show' for object '%s'"
+msgstr "không thể khởi chạy 'show' cho đối tượng '%s'"
+
+#: builtin/notes.c:145
+msgid "can't fdopen 'show' output fd"
+msgstr "không thể fdopen 'show' (lệnh hiển thị) mô tả tập tin (fd) kết xuất"
+
+#: builtin/notes.c:155
+#, c-format
+msgid "failed to close pipe to 'show' for object '%s'"
+msgstr "gặp lỗi khi đóng đường ống cho lệnh 'show' cho đối tượng '%s'"
+
+#: builtin/notes.c:158
+#, c-format
+msgid "failed to finish 'show' for object '%s'"
+msgstr "gặp lỗi khi hoàn thành 'show' cho đối tượng '%s'"
+
+#: builtin/notes.c:175
+#: builtin/tag.c:347
+#, c-format
+msgid "could not create file '%s'"
+msgstr "không thể tạo tập tin '%s'"
+
+#: builtin/notes.c:189
+msgid "Please supply the note contents using either -m or -F option"
+msgstr "Xin hãy áp dụng nội dung của ghi chú sử dụng hoặc là tùy chọn -m hoặc là -F"
+
+#: builtin/notes.c:210
+#: builtin/notes.c:973
+#, c-format
+msgid "Removing note for object %s\n"
+msgstr "Đang gỡ bỏ ghi chú (note) cho đối tượng %s\n"
+
+#: builtin/notes.c:215
+msgid "unable to write note object"
+msgstr "không thể ghi đối tượng ghi chú (note)"
+
+#: builtin/notes.c:217
+#, c-format
+msgid "The note contents has been left in %s"
+msgstr "Nội dung ghi chú còn lại %s"
+
+#: builtin/notes.c:251
+#: builtin/tag.c:542
+#, c-format
+msgid "cannot read '%s'"
+msgstr "không thể đọc '%s'"
+
+#: builtin/notes.c:253
+#: builtin/tag.c:545
+#, c-format
+msgid "could not open or read '%s'"
+msgstr "không thể mở để đọc hay ghi '%s'"
+
+#: builtin/notes.c:272
+#: builtin/notes.c:445
+#: builtin/notes.c:447
+#: builtin/notes.c:507
+#: builtin/notes.c:561
+#: builtin/notes.c:644
+#: builtin/notes.c:649
+#: builtin/notes.c:724
+#: builtin/notes.c:766
+#: builtin/notes.c:968
+#: builtin/reset.c:293
+#: builtin/tag.c:558
+#, c-format
+msgid "Failed to resolve '%s' as a valid ref."
+msgstr "Gặp lỗi khi giải quyết '%s' như là một tham chiếu (ref) hợp lệ."
+
+#: builtin/notes.c:275
+#, c-format
+msgid "Failed to read object '%s'."
+msgstr "Gặp lỗi khi đọc đối tượng '%s'."
+
+#: builtin/notes.c:299
+msgid "Cannot commit uninitialized/unreferenced notes tree"
+msgstr "Không thể chuyển giao (commit) chưa được khởi tạo hoặc không được tham chiếu cây ghi chú"
+
+#: builtin/notes.c:340
+#, c-format
+msgid "Bad notes.rewriteMode value: '%s'"
+msgstr "Giá trị notes.rewriteMode sai: '%s'"
+
+#: builtin/notes.c:350
+#, c-format
+msgid "Refusing to rewrite notes in %s (outside of refs/notes/)"
+msgstr "Từ chối ghi đè ghi chú trong %s (nằm ngoài của refs/notes/)"
+
+#. TRANSLATORS: The first %s is the name of the
+#. environment variable, the second %s is its value
+#: builtin/notes.c:377
+#, c-format
+msgid "Bad %s value: '%s'"
+msgstr "Giá trị %s sai: '%s'"
+
+#: builtin/notes.c:441
+#, c-format
+msgid "Malformed input line: '%s'."
+msgstr "Dòng nhập vào dị hình: '%s'."
+
+#: builtin/notes.c:456
+#, c-format
+msgid "Failed to copy notes from '%s' to '%s'"
+msgstr "Gặp lỗi khi sao chép ghi chú (note) từ '%s' tới '%s'"
+
+#: builtin/notes.c:500
+#: builtin/notes.c:554
+#: builtin/notes.c:627
+#: builtin/notes.c:639
+#: builtin/notes.c:712
+#: builtin/notes.c:759
+#: builtin/notes.c:1033
+msgid "too many parameters"
+msgstr "quá nhiều đối số"
+
+#: builtin/notes.c:513
+#: builtin/notes.c:772
+#, c-format
+msgid "No note found for object %s."
+msgstr "không ghi chú được tìm thấy cho đối tượng %s."
+
+#: builtin/notes.c:580
+#, c-format
+msgid "Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite existing notes"
+msgstr "Không thể thêm các ghi chú. Đã tìm thấy các ghi chú đã sẵn có cho đối tượng %s. Sử dụng tùy chọn '-f' để ghi đè lên các ghi chú cũ"
+
+#: builtin/notes.c:585
+#: builtin/notes.c:662
+#, c-format
+msgid "Overwriting existing notes for object %s\n"
+msgstr "Đang ghi đè lên ghi chú cũ cho đối tượng %s\n"
+
+#: builtin/notes.c:635
+msgid "too few parameters"
+msgstr "quá ít đối số"
+
+#: builtin/notes.c:656
+#, c-format
+msgid "Cannot copy notes. Found existing notes for object %s. Use '-f' to overwrite existing notes"
+msgstr "Không thể sao chép các ghi chú. Đã tìm thấy các ghi chú đã sẵn có cho đối tượng %s. Sử dụng tùy chọn '-f' để ghi đè lên các ghi chú cũ"
+
+#: builtin/notes.c:668
+#, c-format
+msgid "Missing notes on source object %s. Cannot copy."
+msgstr "Thiếu ghi chú trên đối tượng nguốn %s. Không thể sao chép."
+
+#: builtin/notes.c:717
+#, c-format
+msgid ""
+"The -m/-F/-c/-C options have been deprecated for the 'edit' subcommand.\n"
+"Please use 'git notes add -f -m/-F/-c/-C' instead.\n"
+msgstr ""
+"Các tùy chọn -m/-F/-c/-C đã cổ không còn dùng nữa cho lệnh con 'edit'.\n"
+"Xin hãy sử dụng lệnh sau để thay thế: 'git notes add -f -m/-F/-c/-C'.\n"
+
+#: builtin/notes.c:971
+#, c-format
+msgid "Object %s has no note\n"
+msgstr "Đối tượng %s không có ghi chú (note)\n"
+
+#: builtin/notes.c:1103
+#: builtin/remote.c:1598
+#, c-format
+msgid "Unknown subcommand: %s"
+msgstr "Không hiểu câu lệnh con: %s"
+
+#: builtin/pack-objects.c:183
+#: builtin/pack-objects.c:186
+#, c-format
+msgid "deflate error (%d)"
+msgstr "lỗi giải nén (%d)"
+
+#: builtin/pack-objects.c:2398
+#, c-format
+msgid "unsupported index version %s"
+msgstr "phiên bản mục lục không được hỗ trợ %s"
+
+#: builtin/pack-objects.c:2402
+#, c-format
+msgid "bad index version '%s'"
+msgstr "phiên bản mục lục sai '%s'"
+
+#: builtin/pack-objects.c:2425
+#, c-format
+msgid "option %s does not accept negative form"
+msgstr "tùy chọn %s không chấp nhận dạng thức âm"
+
+#: builtin/pack-objects.c:2429
+#, c-format
+msgid "unable to parse value '%s' for option %s"
+msgstr "không thể phân tích giá trị '%s' cho tùy chọn %s"
+
+#: builtin/push.c:45
+msgid "tag shorthand without <tag>"
+msgstr "dùng tốc ký tag không có <thẻ>"
+
+#: builtin/push.c:64
+msgid "--delete only accepts plain target ref names"
+msgstr "--delete chỉ chấp nhận các tên tham chiếu (ref) dạng thường"
+
+#: builtin/push.c:99
+msgid ""
+"\n"
+"To choose either option permanently, see push.default in 'git help config'."
+msgstr ""
+"\n"
+"Để chọn mỗi tùy chọn một cách cố định, xem push.default trong 'git help config'."
+
+#: builtin/push.c:102
+#, c-format
+msgid ""
+"The upstream branch of your current branch does not match\n"
+"the name of your current branch.  To push to the upstream branch\n"
+"on the remote, use\n"
+"\n"
+"    git push %s HEAD:%s\n"
+"\n"
+"To push to the branch of the same name on the remote, use\n"
+"\n"
+"    git push %s %s\n"
+"%s"
+msgstr ""
+"Nhánh dòng ngược (upstream) của nhánh hiện tại của bạn không khớp\n"
+"với tên của nhánh hiện tại của bạn.  Để push đến nhánh dòng ngược\n"
+"trên máy chủ, sử dụng\n"
+"\n"
+"    git push %s HEAD:%s\n"
+"\n"
+"Để push tới nhánh cùng tên trên máy chủ, sử dụng\n"
+"\n"
+"    git push %s %s\n"
+"%s"
+
+#: builtin/push.c:121
+#, c-format
+msgid ""
+"You are not currently on a branch.\n"
+"To push the history leading to the current (detached HEAD)\n"
+"state now, use\n"
+"\n"
+"    git push %s HEAD:<name-of-remote-branch>\n"
+msgstr ""
+"Bạn hiện nay không ở một nhánh.\n"
+"Để push lịch sử hướng tới trạng thái hiện hành (HEAD đã bị tách rời)\n"
+"ngay bây giờ, sử dụng\n"
+"\n"
+"    git push %s HEAD:<tên-của-nhánh-máy-chủ>\n"
+
+#: builtin/push.c:128
+#, c-format
+msgid ""
+"The current branch %s has no upstream branch.\n"
+"To push the current branch and set the remote as upstream, use\n"
+"\n"
+"    git push --set-upstream %s %s\n"
+msgstr ""
+"Nhánh hiện tại %s không có nhánh dòng ngược (upstream) nào.\n"
+"Để push (đẩy lên) nhánh hiện tại và đặt máy chủ như là dòng ngược (upstream), sử dụng\n"
+"\n"
+"    git push --set-upstream %s %s\n"
+
+#: builtin/push.c:136
+#, c-format
+msgid "The current branch %s has multiple upstream branches, refusing to push."
+msgstr "Nhánh hiện tại %s có đa nhánh dòng ngược (upstream), từ chối push."
+
+#: builtin/push.c:139
+#, c-format
+msgid ""
+"You are pushing to remote '%s', which is not the upstream of\n"
+"your current branch '%s', without telling me what to push\n"
+"to update which remote branch."
+msgstr ""
+"Bạn đang push (đẩy lên) máy chủ '%s', mà nó không phải là dòng ngược (upstream) của\n"
+"nhánh hiện tại '%s' của bạn, mà không báo cho tôi biết là cái gì được push\n"
+"để cập nhật nhánh máy chủ nào."
+
+#: builtin/push.c:174
+msgid "You didn't specify any refspecs to push, and push.default is \"nothing\"."
+msgstr "Bạn đã không chỉ ra một refspecs nào để push, và push.default là \"không là gì cả\"."
+
+#: builtin/push.c:181
+msgid ""
+"Updates were rejected because the tip of your current branch is behind\n"
+"its remote counterpart. Merge the remote changes (e.g. 'git pull')\n"
+"before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
+msgstr ""
+"Việc cập nhật bị từ chối bởi vì đầu mút của nhánh được push nằm đằng sau bộ\n"
+"phận tương ứng của máy chủ. Hòa trộn với các thay đổi từ máy chủ (v.d. 'git pull')\n"
+"trước khi lại push lần nữa.\n"
+"Xem trong phần 'Note about fast-forwards' trong nội dung từ lệnh 'git push --help'."
+
+#: builtin/push.c:187
+msgid ""
+"Updates were rejected because a pushed branch tip is behind its remote\n"
+"counterpart. If you did not intend to push that branch, you may want to\n"
+"specify branches to push or set the 'push.default' configuration\n"
+"variable to 'current' or 'upstream' to push only the current branch."
+msgstr ""
+"Việc cập nhật bị từ chối bởi vì đầu mút của nhánh được push nằm đằng sau bộ\n"
+"phận tương ứng của máy chủ. Nếu bạn không có ý định push nhánh đó, bạn có lẽ muốn\n"
+"chỉ định các nhánh để push hoặt là đặt nội dung cho biến cấu hình 'push.default'\n"
+"thành 'current' hoặc 'upstream' để push chỉ nhánh hiện hành mà thôi."
+
+#: builtin/push.c:193
+msgid ""
+"Updates were rejected because a pushed branch tip is behind its remote\n"
+"counterpart. Check out this branch and merge the remote changes\n"
+"(e.g. 'git pull') before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
+msgstr ""
+"Việc cập nhật bị từ chối bởi vì đầu mút của nhánh được push nằm đằng sau bộ\n"
+"phận tương ứng của máy chủ. Checkou nhánh này và hòa trộn với các thay đổi từ máy chủ\n"
+"(v.d. 'git pull') trước khi lại push lần nữa.\n"
+"Xem trong phần 'Note about fast-forwards' trong nội dung từ lệnh 'git push --help'."
+
+#: builtin/push.c:233
+#, c-format
+msgid "Pushing to %s\n"
+msgstr "Đang push (đẩy) lên %s\n"
+
+#: builtin/push.c:237
+#, c-format
+msgid "failed to push some refs to '%s'"
+msgstr "gặp lỗi khi push (đẩy lên) một số tham chiếu (ref) đến '%s'"
+
+#: builtin/push.c:269
+#, c-format
+msgid "bad repository '%s'"
+msgstr "repository (kho) sai '%s'"
+
+#: builtin/push.c:270
+msgid ""
+"No configured push destination.\n"
+"Either specify the URL from the command-line or configure a remote repository using\n"
+"\n"
+"    git remote add <name> <url>\n"
+"\n"
+"and then push using the remote name\n"
+"\n"
+"    git push <name>\n"
+msgstr ""
+"Chưa cấu hình đích để push (đẩy lên).\n"
+"Hoặc là chỉ ra URL từ dòng lệnh hoặc là cấu hình một kho máy chủ sử dụng\n"
+"\n"
+"    git remote add <tên> <url>\n"
+"\n"
+"và sau đó push sử dụng tên máy chủ\n"
+"\n"
+"    git push <tên>\n"
+
+#: builtin/push.c:285
+msgid "--all and --tags are incompatible"
+msgstr "--all và --tags xung khắc nhau"
+
+#: builtin/push.c:286
+msgid "--all can't be combined with refspecs"
+msgstr "--all không thể được tổ hợp cùng với refspecs"
+
+#: builtin/push.c:291
+msgid "--mirror and --tags are incompatible"
+msgstr "--mirror và --tags xung khắc nhau"
+
+#: builtin/push.c:292
+msgid "--mirror can't be combined with refspecs"
+msgstr "--mirror không thể được tổ hợp cùng với refspecs"
+
+#: builtin/push.c:297
+msgid "--all and --mirror are incompatible"
+msgstr "--all và --mirror xung khắc nhau"
+
+#: builtin/push.c:385
+msgid "--delete is incompatible with --all, --mirror and --tags"
+msgstr "--delete là xung khắc với các tùy chọn --all, --mirror và --tags"
+
+#: builtin/push.c:387
+msgid "--delete doesn't make sense without any refs"
+msgstr "--delete không hợp lý nếu không có bất kỳ tham chiếu (refs) nào"
+
+#: builtin/remote.c:98
+#, c-format
+msgid "Updating %s"
+msgstr "Đang cập nhật %s"
+
+#: builtin/remote.c:130
+msgid ""
+"--mirror is dangerous and deprecated; please\n"
+"\t use --mirror=fetch or --mirror=push instead"
+msgstr ""
+"--mirror nguy hiểm và không dùng nữa; xin hãy\n"
+"\t sử dụng tùy chọn --mirror=fetch hoặc --mirror=push để thay thế"
+
+#: builtin/remote.c:147
+#, c-format
+msgid "unknown mirror argument: %s"
+msgstr "không hiểu tham số máy bản sao (mirror): %s"
+
+#: builtin/remote.c:185
+msgid "specifying a master branch makes no sense with --mirror"
+msgstr "đang chỉ định một nhánh master không phân biệt HOA/thường với tùy chọn --mirror"
+
+#: builtin/remote.c:187
+msgid "specifying branches to track makes sense only with fetch mirrors"
+msgstr "chỉ định những nhánh để theo vết chỉ hợp lý với các 'fetch mirror'"
+
+#: builtin/remote.c:195
+#: builtin/remote.c:646
+#, c-format
+msgid "remote %s already exists."
+msgstr "máy chủ %s đã tồn tại rồi."
+
+#: builtin/remote.c:199
+#: builtin/remote.c:650
+#, c-format
+msgid "'%s' is not a valid remote name"
+msgstr "'%s' không phải tên máy chủ hợp lệ"
+
+#: builtin/remote.c:243
+#, c-format
+msgid "Could not setup master '%s'"
+msgstr "Không thể cài đặt nhánh master '%s'"
+
+#: builtin/remote.c:299
+#, c-format
+msgid "more than one %s"
+msgstr "nhiều hơn một %s"
+
+#: builtin/remote.c:339
+#, c-format
+msgid "Could not get fetch map for refspec %s"
+msgstr "Không thể lấy ánh xạ (map) fetch cho refspec %s"
+
+#: builtin/remote.c:440
+#: builtin/remote.c:448
+msgid "(matching)"
+msgstr "(mẫu)"
+
+#: builtin/remote.c:452
+msgid "(delete)"
+msgstr "(xoá)"
+
+#: builtin/remote.c:595
+#: builtin/remote.c:601
+#: builtin/remote.c:607
+#, c-format
+msgid "Could not append '%s' to '%s'"
+msgstr "Không thể nối thêm '%s' vào '%s'"
+
+#: builtin/remote.c:639
+#: builtin/remote.c:792
+#: builtin/remote.c:890
+#, c-format
+msgid "No such remote: %s"
+msgstr "Không có máy chủ nào như thế: %s"
+
+#: builtin/remote.c:656
+#, c-format
+msgid "Could not rename config section '%s' to '%s'"
+msgstr "Không thể đổi tên chương (section) cấu hình từ '%s' thành '%s'"
+
+#: builtin/remote.c:662
+#: builtin/remote.c:799
+#, c-format
+msgid "Could not remove config section '%s'"
+msgstr "Không thể gỡ bỏ chương (section) cấu hình '%s'"
+
+#: builtin/remote.c:677
+#, c-format
+msgid ""
+"Not updating non-default fetch refspec\n"
+"\t%s\n"
+"\tPlease update the configuration manually if necessary."
+msgstr ""
+"Không cập nhật 'non-default fetch respec'\n"
+"\t%s\n"
+"\tXin hãy cập nhật phần cấu hình một cách thủ công nếu thấy cần thiết."
+
+#: builtin/remote.c:683
+#, c-format
+msgid "Could not append '%s'"
+msgstr "Không thể nối thêm '%s'"
+
+#: builtin/remote.c:694
+#, c-format
+msgid "Could not set '%s'"
+msgstr "Không thể đặt '%s'"
+
+#: builtin/remote.c:716
+#, c-format
+msgid "deleting '%s' failed"
+msgstr "việc xoá %s gặp lỗi"
+
+#: builtin/remote.c:750
+#, c-format
+msgid "creating '%s' failed"
+msgstr "tạo %s gặp lỗi"
+
+#: builtin/remote.c:764
+#, c-format
+msgid "Could not remove branch %s"
+msgstr "Không thể gỡ nhánh %s"
+
+#: builtin/remote.c:834
+msgid ""
+"Note: A branch outside the refs/remotes/ hierarchy was not removed;\n"
+"to delete it, use:"
+msgid_plural ""
+"Note: Some branches outside the refs/remotes/ hierarchy were not removed;\n"
+"to delete them, use:"
+msgstr[0] ""
+"Chú ý: Một nhánh nằm ngoài hệ thống refs/remotes/ đã không được gỡ bỏ đi;\n"
+"để xóa đi, sử dụng:"
+msgstr[1] ""
+"Chú ý: Một số nhánh nằm ngoài hệ thống refs/remotes/ đã không được gỡ bỏ đi;\n"
+"để xóa đi, sử dụng:"
+
+#: builtin/remote.c:943
+#, c-format
+msgid " new (next fetch will store in remotes/%s)"
+msgstr " mới (lần lấy về tiếp theo sẽ lưu trong remotes/%s)"
+
+#: builtin/remote.c:946
+msgid " tracked"
+msgstr " bị theo vết"
+
+#: builtin/remote.c:948
+msgid " stale (use 'git remote prune' to remove)"
+msgstr " cũ (sử dụng 'git remote prune' để gỡ bỏ)"
+
+#: builtin/remote.c:950
+msgid " ???"
+msgstr " ???"
+
+#: builtin/remote.c:991
+#, c-format
+msgid "invalid branch.%s.merge; cannot rebase onto > 1 branch"
+msgstr "branch.%s.merge không hợp lệ; không thể rebase về phía > 1 nhánh"
+
+#: builtin/remote.c:998
+#, c-format
+msgid "rebases onto remote %s"
+msgstr "thực hiện rebase trên máy chủ %s"
+
+#: builtin/remote.c:1001
+#, c-format
+msgid " merges with remote %s"
+msgstr " hòa trộn với máy chủ %s"
+
+#: builtin/remote.c:1002
+msgid "    and with remote"
+msgstr "    và với máy chủ"
+
+#: builtin/remote.c:1004
+#, c-format
+msgid "merges with remote %s"
+msgstr "hòa trộn với máy chủ  %s"
+
+#: builtin/remote.c:1005
+msgid "   and with remote"
+msgstr "   và với máy chủ"
+
+#: builtin/remote.c:1051
+msgid "create"
+msgstr "tạo"
+
+#: builtin/remote.c:1054
+msgid "delete"
+msgstr "xoá"
+
+#: builtin/remote.c:1058
+msgid "up to date"
+msgstr "đã cập nhật"
+
+#: builtin/remote.c:1061
+msgid "fast-forwardable"
+msgstr "có-thể-fast-forward"
+
+#: builtin/remote.c:1064
+msgid "local out of date"
+msgstr "dữ liệu nội bộ đã cũ"
+
+#: builtin/remote.c:1071
+#, c-format
+msgid "    %-*s forces to %-*s (%s)"
+msgstr "    %-*s ép buộc thành %-*s (%s)"
+
+#: builtin/remote.c:1074
+#, c-format
+msgid "    %-*s pushes to %-*s (%s)"
+msgstr "    %-*s push tới %-*s (%s)"
+
+#: builtin/remote.c:1078
+#, c-format
+msgid "    %-*s forces to %s"
+msgstr "    %-*s ép buộc thành %s"
+
+#: builtin/remote.c:1081
+#, c-format
+msgid "    %-*s pushes to %s"
+msgstr "    %-*s push tới %s"
+
+#: builtin/remote.c:1118
+#, c-format
+msgid "* remote %s"
+msgstr "* máy chủ %s"
+
+#: builtin/remote.c:1119
+#, c-format
+msgid "  Fetch URL: %s"
+msgstr "  URL để lấy về (fetch): %s"
+
+#: builtin/remote.c:1120
+#: builtin/remote.c:1285
+msgid "(no URL)"
+msgstr "(không có URL nào)"
+
+#: builtin/remote.c:1129
+#: builtin/remote.c:1131
+#, c-format
+msgid "  Push  URL: %s"
+msgstr "  URL để đẩy lên (push)  : %s"
+
+#: builtin/remote.c:1133
+#: builtin/remote.c:1135
+#: builtin/remote.c:1137
+#, c-format
+msgid "  HEAD branch: %s"
+msgstr "  Nhánh HEAD: %s"
+
+#: builtin/remote.c:1139
+#, c-format
+msgid "  HEAD branch (remote HEAD is ambiguous, may be one of the following):\n"
+msgstr "  nhánh HEAD (HEAD máy chủ là không rõ ràng, có lẽ là một trong số sau):\n"
+
+#: builtin/remote.c:1151
+#, c-format
+msgid "  Remote branch:%s"
+msgid_plural "  Remote branches:%s"
+msgstr[0] "  Nhánh trên máy chủ:%s"
+msgstr[1] "  Những nhánh trên máy chủ:%s"
+
+#: builtin/remote.c:1154
+#: builtin/remote.c:1181
+msgid " (status not queried)"
+msgstr " (trạng thái không được yêu cầu)"
+
+#: builtin/remote.c:1163
+msgid "  Local branch configured for 'git pull':"
+msgid_plural "  Local branches configured for 'git pull':"
+msgstr[0] "  Nhánh nội bộ đã được cấu hình cho lệnh 'git pull':"
+msgstr[1] "  Những nhánh nội bộ đã được cấu hình cho lệnh 'git pull':"
+
+#: builtin/remote.c:1171
+msgid "  Local refs will be mirrored by 'git push'"
+msgstr "  refs nội bộ sẽ được phản chiếu bởi lệnh 'git push'"
+
+#: builtin/remote.c:1178
+#, c-format
+msgid "  Local ref configured for 'git push'%s:"
+msgid_plural "  Local refs configured for 'git push'%s:"
+msgstr[0] "  Tham chiếu nội bộ được cấu hình cho lệnh 'git push'%s:"
+msgstr[1] "  Những tham chiếu nội bộ được cấu hình cho lệnh 'git push'%s:"
+
+#: builtin/remote.c:1216
+msgid "Cannot determine remote HEAD"
+msgstr "Không thể xác định được HEAD máy chủ"
+
+#: builtin/remote.c:1218
+msgid "Multiple remote HEAD branches. Please choose one explicitly with:"
+msgstr "Nhiều nhánh HEAD máy chủ. Hãy chọn rõ ràng một:"
+
+#: builtin/remote.c:1228
+#, c-format
+msgid "Could not delete %s"
+msgstr "Không thể xóa bỏ %s"
+
+#: builtin/remote.c:1236
+#, c-format
+msgid "Not a valid ref: %s"
+msgstr "Không phải là tham chiếu (ref) hợp lệ: %s"
+
+#: builtin/remote.c:1238
+#, c-format
+msgid "Could not setup %s"
+msgstr "Không thể cài đặt %s"
+
+#: builtin/remote.c:1274
+#, c-format
+msgid " %s will become dangling!"
+msgstr " %s sẽ trở thành lủng lẳng (không được quản lý)!"
+
+#: builtin/remote.c:1275
+#, c-format
+msgid " %s has become dangling!"
+msgstr " %s phải trở thành lủng lẳng (không được quản lý)!"
+
+#: builtin/remote.c:1281
+#, c-format
+msgid "Pruning %s"
+msgstr "Đang xén bớt %s"
+
+#: builtin/remote.c:1282
+#, c-format
+msgid "URL: %s"
+msgstr "URL: %s"
+
+#: builtin/remote.c:1295
+#, c-format
+msgid " * [would prune] %s"
+msgstr " * [nên xén bớt] %s"
+
+#: builtin/remote.c:1298
+#, c-format
+msgid " * [pruned] %s"
+msgstr " *[đã xén bớ] %s"
+
+#: builtin/remote.c:1387
+#: builtin/remote.c:1461
+#, c-format
+msgid "No such remote '%s'"
+msgstr "Không có máy chủ nào có tên '%s'"
+
+#: builtin/remote.c:1414
+msgid "no remote specified"
+msgstr "chưa chỉ ra máy chủ nào"
+
+#: builtin/remote.c:1447
+msgid "--add --delete doesn't make sense"
+msgstr "--add --delete không hợp lý"
+
+#: builtin/remote.c:1487
+#, c-format
+msgid "Invalid old URL pattern: %s"
+msgstr "Kiểu mẫu URL cũ không hợp lệ: %s"
+
+#: builtin/remote.c:1495
+#, c-format
+msgid "No such URL found: %s"
+msgstr "Không tìm thấy URL như vậy: %s"
+
+#: builtin/remote.c:1497
+msgid "Will not delete all non-push URLs"
+msgstr "Sẽ không xóa những địa chỉ URL không-push"
+
+#: builtin/reset.c:33
+msgid "mixed"
+msgstr "pha trộn"
+
+#: builtin/reset.c:33
+msgid "soft"
+msgstr "mềm"
+
+#: builtin/reset.c:33
+msgid "hard"
+msgstr "cứng"
+
+#: builtin/reset.c:33
+msgid "merge"
+msgstr "hòa trộn"
+
+#: builtin/reset.c:33
+msgid "keep"
+msgstr "giữ lại"
+
+#: builtin/reset.c:77
+msgid "You do not have a valid HEAD."
+msgstr "Bạn không có HEAD nào hợp lệ."
+
+#: builtin/reset.c:79
+msgid "Failed to find tree of HEAD."
+msgstr "Gặp lỗi khi tìm cây của HEAD."
+
+#: builtin/reset.c:85
+#, c-format
+msgid "Failed to find tree of %s."
+msgstr "Gặp lỗi khi tìm cây của %s."
+
+#: builtin/reset.c:96
+msgid "Could not write new index file."
+msgstr "Không thể ghi tập tin lưu bảng mục lục mới."
+
+#: builtin/reset.c:106
+#, c-format
+msgid "HEAD is now at %s"
+msgstr "HEAD hiện giờ tại %s"
+
+#: builtin/reset.c:130
+msgid "Could not read index"
+msgstr "Không thể đọc bảng mục lục"
+
+#: builtin/reset.c:133
+msgid "Unstaged changes after reset:"
+msgstr "Những thay đổi bị bỏ trạng thái (stage) sau khi reset:"
+
+#: builtin/reset.c:223
+#, c-format
+msgid "Cannot do a %s reset in the middle of a merge."
+msgstr "Không thể thực hiện một %s reset ở giữa của quá trình hòa trộn."
+
+#: builtin/reset.c:297
+#, c-format
+msgid "Could not parse object '%s'."
+msgstr "không thể phân tích đối tượng '%s'."
+
+#: builtin/reset.c:302
+msgid "--patch is incompatible with --{hard,mixed,soft}"
+msgstr "--patch xung khắc với --{hard,mixed,soft}"
+
+#: builtin/reset.c:311
+msgid "--mixed with paths is deprecated; use 'git reset -- <paths>' instead."
+msgstr "--mixed với các đường dẫn không còn dùng nữa; hãy thay thế bằng lệnh 'git reset -- <đường_dẫn>'."
+
+#: builtin/reset.c:313
+#, c-format
+msgid "Cannot do %s reset with paths."
+msgstr "Không thể thực hiện lệnh %s reset với các đường dẫn."
+
+#: builtin/reset.c:325
+#, c-format
+msgid "%s reset is not allowed in a bare repository"
+msgstr "%s reset không được phép trên kho bare (trên máy chủ)"
+
+#: builtin/reset.c:341
+#, c-format
+msgid "Could not reset index file to revision '%s'."
+msgstr "Không thể đặt lại (reset) bảng mục lục thành điểm xét lại '%s'."
+
+#: builtin/revert.c:70
+#: builtin/revert.c:92
+#, c-format
+msgid "%s: %s cannot be used with %s"
+msgstr "%s: %s không thể được sử dụng với %s"
+
+#: builtin/revert.c:131
+msgid "program error"
+msgstr "lỗi chương trình"
+
+#: builtin/revert.c:221
+msgid "revert failed"
+msgstr "revert gặp lỗi"
+
+#: builtin/revert.c:236
+msgid "cherry-pick failed"
+msgstr "cherry-pick gặp lỗi"
+
+#: builtin/rm.c:109
+#, c-format
+msgid ""
+"'%s' has staged content different from both the file and the HEAD\n"
+"(use -f to force removal)"
+msgstr ""
+"'%s' có nội dung được lưu trạng thái khác biệt từ cả tập tin và cả HEAD\n"
+"(sử dụng -f để ép buộc gỡ bỏ)"
+
+#: builtin/rm.c:115
+#, c-format
+msgid ""
+"'%s' has changes staged in the index\n"
+"(use --cached to keep the file, or -f to force removal)"
+msgstr ""
+"'%s' có các thay đổi được lưu trạng thái trong bảng mục lục\n"
+"(sử dụng --cached để giữ tập tin, hoặc -f để ép buộc gỡ bỏ)"
+
+#: builtin/rm.c:119
+#, c-format
+msgid ""
+"'%s' has local modifications\n"
+"(use --cached to keep the file, or -f to force removal)"
+msgstr ""
+"'%s' có các thay đổi nội bộ\n"
+"(sử dụng --cached để giữ tập tin, hoặc -f để ép buộc gỡ bỏ)"
+
+#: builtin/rm.c:194
+#, c-format
+msgid "not removing '%s' recursively without -r"
+msgstr "không thể gỡ bỏ '%s' một cách đệ qui mà không có tùy chọn -r"
+
+#: builtin/rm.c:230
+#, c-format
+msgid "git rm: unable to remove %s"
+msgstr "git rm: không thể gỡ bỏ %s"
+
+#: builtin/shortlog.c:157
+#, c-format
+msgid "Missing author: %s"
+msgstr "Thiếu tên tác giả: %s"
+
+#: builtin/tag.c:60
+#, c-format
+msgid "malformed object at '%s'"
+msgstr "đối tượng dị hình tại '%s'"
+
+#: builtin/tag.c:207
+#, c-format
+msgid "tag name too long: %.*s..."
+msgstr "tên thẻ quá dài: %.*s..."
+
+#: builtin/tag.c:212
+#, c-format
+msgid "tag '%s' not found."
+msgstr "không tìm thấy tìm thấy thẻ '%s'."
+
+#: builtin/tag.c:227
+#, c-format
+msgid "Deleted tag '%s' (was %s)\n"
+msgstr "Thẻ đã bị xóa '%s' (trước là %s)\n"
+
+#: builtin/tag.c:239
+#, c-format
+msgid "could not verify the tag '%s'"
+msgstr "không thể thẩm tra thẻ '%s'"
+
+#: builtin/tag.c:249
+msgid ""
+"\n"
+"#\n"
+"# Write a tag message\n"
+"# Lines starting with '#' will be ignored.\n"
+"#\n"
+msgstr ""
+"\n"
+"#\n"
+"# Viết các ghi chú cho (thẻ) tag\n"
+"# Những dòng được bắt đầu bằng '#' sẽ được bỏ qua.\n"
+"#\n"
+
+#: builtin/tag.c:256
+msgid ""
+"\n"
+"#\n"
+"# Write a tag message\n"
+"# Lines starting with '#' will be kept; you may remove them yourself if you want to.\n"
+"#\n"
+msgstr ""
+"\n"
+"#\n"
+"# Viết các ghi chú cho (thẻ) tag\n"
+"# Những dòng được bắt đầu bằng '#' sẽ được bỏ qua; bạn có thể xóa chúng đi nếu muốn.\n"
+"#\n"
+
+#: builtin/tag.c:298
+msgid "unable to sign the tag"
+msgstr "không thể ký thẻ"
+
+#: builtin/tag.c:300
+msgid "unable to write tag file"
+msgstr "không thể ghi vào tập tin lưu thẻ"
+
+#: builtin/tag.c:325
+msgid "bad object type."
+msgstr "kiểu đối tượng sai."
+
+#: builtin/tag.c:338
+msgid "tag header too big."
+msgstr "đầu thẻ (tag) quá lớn."
+
+#: builtin/tag.c:370
+msgid "no tag message?"
+msgstr "không có thông điệp (message) cho thẻ (tag)?"
+
+#: builtin/tag.c:376
+#, c-format
+msgid "The tag message has been left in %s\n"
+msgstr "Nội dung ghi chú còn lại %s\n"
+
+#: builtin/tag.c:425
+msgid "switch 'points-at' requires an object"
+msgstr "chuyển đến 'points-at' yêu cần một đối tượng"
+
+#: builtin/tag.c:427
+#, c-format
+msgid "malformed object name '%s'"
+msgstr "tên đối tượng dị hình '%s'"
+
+#: builtin/tag.c:506
+msgid "--column and -n are incompatible"
+msgstr "--column và -n xung khắc nhau"
+
+#: builtin/tag.c:523
+msgid "-n option is only allowed with -l."
+msgstr "tùy chọn -n chỉ cho phép dùng với -l."
+
+#: builtin/tag.c:525
+msgid "--contains option is only allowed with -l."
+msgstr "tùy chọn --contains chỉ cho phép dùng với -l."
+
+#: builtin/tag.c:527
+msgid "--points-at option is only allowed with -l."
+msgstr "tùy chọn --points-at chỉ cho phép dùng với -l."
+
+#: builtin/tag.c:535
+msgid "only one -F or -m option is allowed."
+msgstr "chỉ có một tùy chọn -F hoặc -m là được phép."
+
+#: builtin/tag.c:555
+msgid "too many params"
+msgstr "quá nhiều đối số"
+
+#: builtin/tag.c:561
+#, c-format
+msgid "'%s' is not a valid tag name."
+msgstr "'%s' không phải thẻ hợp lệ."
+
+#: builtin/tag.c:566
+#, c-format
+msgid "tag '%s' already exists"
+msgstr "Thẻ '%s' đã tồn tại rồi"
+
+#: builtin/tag.c:584
+#, c-format
+msgid "%s: cannot lock the ref"
+msgstr "%s: không thể khóa ref (tham chiếu)"
+
+#: builtin/tag.c:586
+#, c-format
+msgid "%s: cannot update the ref"
+msgstr "%s: không thể cập nhật ref (tham chiếu)"
+
+#: builtin/tag.c:588
+#, c-format
+msgid "Updated tag '%s' (was %s)\n"
+msgstr "Thẻ đã cập nhật '%s' (cũ là %s)\n"
+
+#: git.c:16
+msgid "See 'git help <command>' for more information on a specific command."
+msgstr "Chạy lệnh 'git help <tên-lệnh>' để có thêm thông tin về lệnh được chỉ ra."
+
+#: parse-options.h:133
+#: parse-options.h:235
+msgid "n"
+msgstr "n"
+
+#: parse-options.h:141
+msgid "time"
+msgstr "thời-gian"
+
+#: parse-options.h:149
+msgid "file"
+msgstr "tập-tin"
+
+#: parse-options.h:151
+msgid "when"
+msgstr "khi"
+
+#: parse-options.h:156
+msgid "no-op (backward compatibility)"
+msgstr "no-op (tương thích ngược)"
+
+#: parse-options.h:228
+msgid "be more verbose"
+msgstr "chi tiết hơn nữa"
+
+#: parse-options.h:230
+msgid "be more quiet"
+msgstr "im lặng hơn nữa"
+
+#: parse-options.h:236
+msgid "use <n> digits to display SHA-1s"
+msgstr "sử dụng <n> chữ số để hiển thị SHA-1s"
+
+#: common-cmds.h:8
+msgid "Add file contents to the index"
+msgstr "Thêm nội dung tập tin vào bảng mục lục"
+
+#: common-cmds.h:9
+msgid "Find by binary search the change that introduced a bug"
+msgstr "Tìm kiếm bằng điều tra nhị phân các thay đổi mà nó bắt đầu lỗi"
+
+#: common-cmds.h:10
+msgid "List, create, or delete branches"
+msgstr "Liệt kê, tạo hay là xóa các nhánh"
+
+#: common-cmds.h:11
+msgid "Checkout a branch or paths to the working tree"
+msgstr "Checkout một nhánh hay các đường dẫn tời cây làm việc"
+
+#: common-cmds.h:12
+msgid "Clone a repository into a new directory"
+msgstr "Nhân bản một kho chứa đến một thư mục mới"
+
+#: common-cmds.h:13
+msgid "Record changes to the repository"
+msgstr "Ghi các thay đổi vào kho chứa"
+
+#: common-cmds.h:14
+msgid "Show changes between commits, commit and working tree, etc"
+msgstr "Hiển thị các thay đổi giữa những lần chuyển giao (commit), commit và cây làm việc, v.v.."
+
+#: common-cmds.h:15
+msgid "Download objects and refs from another repository"
+msgstr "Các đối tượng và tham chiếu được tải về từ kho chứa khác"
+
+#: common-cmds.h:16
+msgid "Print lines matching a pattern"
+msgstr "In ra những dòng khớp với một mẫu"
+
+#: common-cmds.h:17
+msgid "Create an empty git repository or reinitialize an existing one"
+msgstr "Tạo một kho git trống rỗng hay khởi tạo lại một kho đã tồn tại từ trước"
+
+#: common-cmds.h:18
+msgid "Show commit logs"
+msgstr "hiển thị nhật ký các lần commit (chuyển giao)"
+
+#: common-cmds.h:19
+msgid "Join two or more development histories together"
+msgstr "Hợp nhất hai hay nhiều hơn lịch sử của các nhà phát triển phần mềm lại với nhau"
+
+#: common-cmds.h:20
+msgid "Move or rename a file, a directory, or a symlink"
+msgstr "Di chuyển, đổi tên một tập tin, thư mục hay liên kết tượng trưng"
+
+#: common-cmds.h:21
+msgid "Fetch from and merge with another repository or a local branch"
+msgstr "Fetch (lấy về) và hòa trộng với kho khác hay nhánh nội bộ"
+
+#: common-cmds.h:22
+msgid "Update remote refs along with associated objects"
+msgstr "Cập nhật tham chiếu (refs) máy chủ cùng với các đối tượng liên quan đến nó"
+
+#: common-cmds.h:23
+msgid "Forward-port local commits to the updated upstream head"
+msgstr "Forward-port những lần chuyển giao nội bộ tới head dòng ngược đã cập nhật"
+
+#: common-cmds.h:24
+msgid "Reset current HEAD to the specified state"
+msgstr "Đặt lại HEAD hiện hành thành một trạng thái được chỉ ra"
+
+#: common-cmds.h:25
+msgid "Remove files from the working tree and from the index"
+msgstr "Gỡ bỏ các tập tin từ cây làm việc và từ bảng mục lục"
+
+#: common-cmds.h:26
+msgid "Show various types of objects"
+msgstr "Hiển thị các kiểu khác nhau của các đối tượng"
+
+#: common-cmds.h:27
+msgid "Show the working tree status"
+msgstr "Hiển thị trạng thái cây làm việc"
+
+#: common-cmds.h:28
+msgid "Create, list, delete or verify a tag object signed with GPG"
+msgstr "Tạo, liệt kê, xóa hay xác thực một đối tượng thẻ (tag) mà nó được ký sử dụng GPG"
+
+#: git-am.sh:50
+msgid "You need to set your committer info first"
+msgstr "Bạn cần đặt thông tin về người chuyển giao mã nguồn trước đã"
+
+#: git-am.sh:95
+msgid ""
+"You seem to have moved HEAD since the last 'am' failure.\n"
+"Not rewinding to ORIG_HEAD"
+msgstr ""
+"Bạn có lẽ đã có HEAD đã bị di chuyển đi kể từ lần 'am' thất bại cuối cùng.\n"
+"Không thể chuyển tới ORIG_HEAD"
+
+#: git-am.sh:105
+#, sh-format
+msgid ""
+"When you have resolved this problem run \"$cmdline --resolved\".\n"
+"If you would prefer to skip this patch, instead run \"$cmdline --skip\".\n"
+"To restore the original branch and stop patching run \"$cmdline --abort\"."
+msgstr ""
+"Khi bạn cần giải quyết vấn đề này hãy chạy lệnh \"$cmdline --resolved\".\n"
+"Nếu bạn có ý định bỏ qua miếng vá, thay vào đó bạn chạy \"$cmdline --skip\".\n"
+"Để phục hồi lại thành nhánh nguyên thủy và dừng việc vá lại thì chạy \"$cmdline --abort\"."
+
+#: git-am.sh:121
+msgid "Cannot fall back to three-way merge."
+msgstr "Đang trở lại để hòa trộn kiểu 'three-way'."
+
+#: git-am.sh:137
+msgid "Repository lacks necessary blobs to fall back on 3-way merge."
+msgstr "Kho thiếu đối tượng blob cần thiết để trở về trên '3-way merge'."
+
+#: git-am.sh:154
+msgid ""
+"Did you hand edit your patch?\n"
+"It does not apply to blobs recorded in its index."
+msgstr ""
+"Bạn đã sửa miếng vá của mình bằng cách thủ công à?\n"
+"Nó không thể áp dụng các blob đã được ghi lại trong bảng mục lục của nó."
+
+#: git-am.sh:163
+msgid "Falling back to patching base and 3-way merge..."
+msgstr "Đang trở lại để vá cơ sở và '3-way merge'..."
+
+#: git-am.sh:275
+msgid "Only one StGIT patch series can be applied at once"
+msgstr "Chỉ có một sê-ri miếng vá StGIT được áp dụng một lúc"
+
+#: git-am.sh:362
+#, sh-format
+msgid "Patch format $patch_format is not supported."
+msgstr "Định dạng miếng vá $patch_format không được hỗ trợ."
+
+#: git-am.sh:364
+msgid "Patch format detection failed."
+msgstr "Dò tìm định dạng miếng vá gặp lỗi."
+
+#: git-am.sh:418
+msgid "-d option is no longer supported.  Do not use."
+msgstr "Tùy chọn -d không còn được hỗ trợ nữa. Xin đừng sử dụng."
+
+#: git-am.sh:481
+#, sh-format
+msgid "previous rebase directory $dotest still exists but mbox given."
+msgstr "thư mục rebase trước $dotest vẫn chưa sẵn sàng nhưng mbox được đưa ra."
+
+#: git-am.sh:486
+msgid "Please make up your mind. --skip or --abort?"
+msgstr "Xin hãy rõ ràng. --skip hay --abort?"
+
+#: git-am.sh:513
+msgid "Resolve operation not in progress, we are not resuming."
+msgstr "Thao tác phân giải không đang được tiến hành, chúng ta không phục hồi lại."
+
+#: git-am.sh:579
+#, sh-format
+msgid "Dirty index: cannot apply patches (dirty: $files)"
+msgstr "Bảng mục lục sai: không thể áp dụng các miếng vá (sai: $files)"
+
+#: git-am.sh:671
+#, sh-format
+msgid ""
+"Patch is empty.  Was it split wrong?\n"
+"If you would prefer to skip this patch, instead run \"$cmdline --skip\".\n"
+"To restore the original branch and stop patching run \"$cmdline --abort\"."
+msgstr ""
+"Miếng vá trống rỗng.  Nó đã bị chia cắt sai phải không?\n"
+"Nếu bạn thích bỏ qua miếng vá này, hãy chạy lệnh sau để thay thế \"$cmdline --skip\".\n"
+"Để phục hồi lại nhánh nguyên thủy và dừng vá lại hãy chạy lệnh \"$cmdline --abort\"."
+
+#: git-am.sh:708
+msgid "Patch does not have a valid e-mail address."
+msgstr "Miếng vá không có địa chỉ e-mail hợp lệ."
+
+#: git-am.sh:755
+msgid "cannot be interactive without stdin connected to a terminal."
+msgstr "không thể được tương tác mà không có stdin kết nối với một thiết bị cuối"
+
+#: git-am.sh:759
+msgid "Commit Body is:"
+msgstr "Thân của lần chuyển giao (commit) là:"
+
+#. TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
+#. in your translation. The program will only accept English
+#. input at this point.
+#: git-am.sh:766
+msgid "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
+msgstr "Áp dụng? đồng ý [y]/không [n]/chỉnh sửa [e]/hiển thị miếng [v]á/đồng ý tất cả [a]"
+
+#: git-am.sh:802
+#, sh-format
+msgid "Applying: $FIRSTLINE"
+msgstr "Đang áp dụng (miếng vá): $FIRSTLINE"
+
+#: git-am.sh:823
+msgid ""
+"No changes - did you forget to use 'git add'?\n"
+"If there is nothing left to stage, chances are that something else\n"
+"already introduced the same changes; you might want to skip this patch."
+msgstr ""
+"Không có thay đổi nào - bạn đã quên sử dụng lệnh 'git add' à?\n"
+"Nếu ở đây không có gì còn lại stage, tình cờ là có một số thứ khác\n"
+"đã sẵn được đưa vào với cùng nội dung thay đổi; bạn có lẽ muốn bỏ qua miếng vá này."
+
+#: git-am.sh:831
+msgid ""
+"You still have unmerged paths in your index\n"
+"did you forget to use 'git add'?"
+msgstr ""
+"Bạn vẫn có những đường dẫn chưa được hòa trộn trong bảng mục lục của mình\n"
+"bạn đã quên sử dụng lệnh 'git add' à?"
+
+#: git-am.sh:847
+msgid "No changes -- Patch already applied."
+msgstr "Không thay đổi gì cả -- Miếng vá đã được áp dụng rồi."
+
+#: git-am.sh:857
+#, sh-format
+msgid "Patch failed at $msgnum $FIRSTLINE"
+msgstr "Vá gặp lỗi tại $msgnum $FIRSTLINE"
+
+#: git-am.sh:873
+msgid "applying to an empty history"
+msgstr "áp dụng vào một lịch sử trống rỗng"
+
+#: git-bisect.sh:48
+msgid "You need to start by \"git bisect start\""
+msgstr "Bạn cần khởi đầu bằng \"git bisect start\""
+
+#. TRANSLATORS: Make sure to include [Y] and [n] in your
+#. translation. The program will only accept English input
+#. at this point.
+#: git-bisect.sh:54
+msgid "Do you want me to do it for you [Y/n]? "
+msgstr "Bạn có muốn tôi thực hiện điều này cho bạn không [Y/n]? "
+
+#: git-bisect.sh:95
+#, sh-format
+msgid "unrecognised option: '$arg'"
+msgstr "không công nhận tùy chọn: '$arg'"
+
+#: git-bisect.sh:99
+#, sh-format
+msgid "'$arg' does not appear to be a valid revision"
+msgstr "'$arg' không có vẻ như là một sự xét lại hợp lệ"
+
+#: git-bisect.sh:117
+msgid "Bad HEAD - I need a HEAD"
+msgstr "HEAD sai - Tôi cần một HEAD"
+
+#: git-bisect.sh:130
+#, sh-format
+msgid "Checking out '$start_head' failed. Try 'git bisect reset <validbranch>'."
+msgstr "Việc checkout '$start_head' gặp lỗi. Hãy thử 'git bisect reset <nhánh_hợp_lệ>'."
+
+#: git-bisect.sh:140
+msgid "won't bisect on seeked tree"
+msgstr "sẽ không bisect trêm cây được seek"
+
+#: git-bisect.sh:144
+msgid "Bad HEAD - strange symbolic ref"
+msgstr "HEAD sai - tham chiếu (ref) tượng trưng kỳ lạ"
+
+#: git-bisect.sh:189
+#, sh-format
+msgid "Bad bisect_write argument: $state"
+msgstr "Đối số bisect_write sai: $state"
+
+#: git-bisect.sh:218
+#, sh-format
+msgid "Bad rev input: $arg"
+msgstr "Đầu vào rev sai: $arg"
+
+#: git-bisect.sh:232
+msgid "Please call 'bisect_state' with at least one argument."
+msgstr "Hãy gọi lệnhl 'bisect_state' với ít nhất một đối số."
+
+#: git-bisect.sh:244
+#, sh-format
+msgid "Bad rev input: $rev"
+msgstr "Đầu vào rev sai: $rev"
+
+#: git-bisect.sh:250
+msgid "'git bisect bad' can take only one argument."
+msgstr "'git bisect bad' có thể lấy chỉ một đối số."
+
+#. have bad but not good.  we could bisect although
+#. this is less optimum.
+#: git-bisect.sh:273
+msgid "Warning: bisecting only with a bad commit."
+msgstr "Cảnh báo: chỉ thực hiện việc bisect với một lần chuyển giao (commit) sai."
+
+#. TRANSLATORS: Make sure to include [Y] and [n] in your
+#. translation. The program will only accept English input
+#. at this point.
+#: git-bisect.sh:279
+msgid "Are you sure [Y/n]? "
+msgstr "Bạn có chắc chắn chưa [Y/n]?"
+
+#: git-bisect.sh:289
+msgid ""
+"You need to give me at least one good and one bad revisions.\n"
+"(You can use \"git bisect bad\" and \"git bisect good\" for that.)"
+msgstr ""
+"Bạn phải chỉ cho tôi ít nhất một điểm xét duyệt tốt và một điểm sai.\n"
+"(Bạn có thể sử dụng \"git bisect bad\" và \"git bisect good\" cho cái đó.)"
+
+#: git-bisect.sh:292
+msgid ""
+"You need to start by \"git bisect start\".\n"
+"You then need to give me at least one good and one bad revisions.\n"
+"(You can use \"git bisect bad\" and \"git bisect good\" for that.)"
+msgstr ""
+"Bạn cần bắt đầu bằng lệnh \"git bisect start\".\n"
+"Bạn sau đó cần phải chỉ cho tôi ít nhất một điểm xét duyệt đúng và một điểm sai.\n"
+"(Bạn có thể sử dụng \"git bisect bad\" và \"git bisect good\" cho chúng.)"
+
+#: git-bisect.sh:347
+#: git-bisect.sh:474
+msgid "We are not bisecting."
+msgstr "Chúng tôi không bisect."
+
+#: git-bisect.sh:354
+#, sh-format
+msgid "'$invalid' is not a valid commit"
+msgstr "'$invalid' không phải là lần chuyển giao (commit) hợp lệ"
+
+#: git-bisect.sh:363
+#, sh-format
+msgid ""
+"Could not check out original HEAD '$branch'.\n"
+"Try 'git bisect reset <commit>'."
+msgstr ""
+"Không thể check-out HEAD nguyên thủy của '$branch'.\n"
+"Hãy thử 'git bisect reset <lần-chuyển-giao>'."
+
+#: git-bisect.sh:390
+msgid "No logfile given"
+msgstr "Chưa chỉ ra tập tin ghi nhật ký"
+
+#: git-bisect.sh:391
+#, sh-format
+msgid "cannot read $file for replaying"
+msgstr "không thể đọc $file để thao diễn lại"
+
+#: git-bisect.sh:408
+msgid "?? what are you talking about?"
+msgstr "?? bạn đang nói gì thế?"
+
+#: git-bisect.sh:420
+#, sh-format
+msgid "running $command"
+msgstr "đang chạy lệnh $command"
+
+#: git-bisect.sh:427
+#, sh-format
+msgid ""
+"bisect run failed:\n"
+"exit code $res from '$command' is < 0 or >= 128"
+msgstr ""
+"chạy bisect gặp lỗi:\n"
+"mã trả về $res từ lệnh '$command' là < 0 hoặc >= 128"
+
+#: git-bisect.sh:453
+msgid "bisect run cannot continue any more"
+msgstr "bisect không thể tiếp tục thêm được nữa"
+
+#: git-bisect.sh:459
+#, sh-format
+msgid ""
+"bisect run failed:\n"
+"'bisect_state $state' exited with error code $res"
+msgstr ""
+"chạy bisect gặp lỗi:\n"
+"'bisect_state $state' đã thoát ra với mã lỗi $res"
+
+#: git-bisect.sh:466
+msgid "bisect run success"
+msgstr "bisect chạy thành công"
+
+#: git-pull.sh:21
+msgid ""
+"Pull is not possible because you have unmerged files.\n"
+"Please, fix them up in the work tree, and then use 'git add/rm <file>'\n"
+"as appropriate to mark resolution, or use 'git commit -a'."
+msgstr ""
+"Pull là không thể được bởi vì bạn có những tập tin chưa được hòa trộn.\n"
+"Xin hãy sửa chữa chúng trước, và sau đó sử dụng lệnh 'git add/rm <tập-tin>'\n"
+"để phê chuẩn việc đánh dấu đây cần được giải quyết, hoặc là sử dụng 'git commit -a'."
+
+#: git-pull.sh:25
+msgid "Pull is not possible because you have unmerged files."
+msgstr "Full là không thể thực hiện bởi vì bạn có những tập tin chưa được hòa trộn."
+
+#: git-pull.sh:197
+msgid "updating an unborn branch with changes added to the index"
+msgstr "đang cập nhật một nhánh chưa được sinh ra với các thay đổi được thêm vào bảng mục lục"
+
+#. The fetch involved updating the current branch.
+#. The working tree and the index file is still based on the
+#. $orig_head commit, but we are merging into $curr_head.
+#. First update the working tree to match $curr_head.
+#: git-pull.sh:228
+#, sh-format
+msgid ""
+"Warning: fetch updated the current branch head.\n"
+"Warning: fast-forwarding your working tree from\n"
+"Warning: commit $orig_head."
+msgstr ""
+"Cảnh báo: fetch đã cập nhật head nhánh hiện tại.\n"
+"Cảnh báo: đang fast-forward cây làm việc của bạn từ\n"
+"Cảnh báo: commit $orig_head."
+
+#: git-pull.sh:253
+msgid "Cannot merge multiple branches into empty head"
+msgstr "Không thể hòa trộn nhiều nhánh và trong một head trống rỗng"
+
+#: git-pull.sh:257
+msgid "Cannot rebase onto multiple branches"
+msgstr "Không thể thực hiện lệnh rebase (cơ cấu lại) trên nhiều nhánh"
+
+#: git-stash.sh:51
+msgid "git stash clear with parameters is unimplemented"
+msgstr "git stash clear với các tham số là chưa được thực hiện (không nhận đối số)"
+
+#: git-stash.sh:74
+msgid "You do not have the initial commit yet"
+msgstr "Bạn chưa còn có lần chuyển giao (commit) khởi tạo"
+
+#: git-stash.sh:89
+msgid "Cannot save the current index state"
+msgstr "Không thể ghi lại trạng thái bảng mục lục hiện hành"
+
+#: git-stash.sh:123
+#: git-stash.sh:136
+msgid "Cannot save the current worktree state"
+msgstr "Không thể ghi lại trạng thái cây-làm-việc hiện hành"
+
+#: git-stash.sh:140
+msgid "No changes selected"
+msgstr "Chưa có thay đổi nào được chọn"
+
+#: git-stash.sh:143
+msgid "Cannot remove temporary index (can't happen)"
+msgstr "Không thể gỡ bỏ bảng mục lục tạm thời (không thể xảy ra)"
+
+#: git-stash.sh:156
+msgid "Cannot record working tree state"
+msgstr "Không thể ghi lại trạng thái cây làm việc hiện hành"
+
+#. TRANSLATORS: $option is an invalid option, like
+#. `--blah-blah'. The 7 spaces at the beginning of the
+#. second line correspond to "error: ". So you should line
+#. up the second line with however many characters the
+#. translation of "error: " takes in your language. E.g. in
+#. English this is:
+#.
+#. $ git stash save --blah-blah 2>&1 | head -n 2
+#. error: unknown option for 'stash save': --blah-blah
+#. To provide a message, use git stash save -- '--blah-blah'
+#: git-stash.sh:202
+#, sh-format
+msgid ""
+"error: unknown option for 'stash save': $option\n"
+"       To provide a message, use git stash save -- '$option'"
+msgstr ""
+"lỗi: không hiểu tùy chọn cho 'stash save': $option\n"
+"       Để cung cấp một thông điệp, sử dụng git stash save -- '$option'"
+
+#: git-stash.sh:223
+msgid "No local changes to save"
+msgstr "Không có thay đổi nội bộ nào được ghi lại"
+
+#: git-stash.sh:227
+msgid "Cannot initialize stash"
+msgstr "Không thể khởi tạo stash"
+
+#: git-stash.sh:235
+msgid "Cannot save the current status"
+msgstr "Không thể ghi lại trạng thái hiện hành"
+
+#: git-stash.sh:253
+msgid "Cannot remove worktree changes"
+msgstr "Không thể gỡ bỏ các thay đổi cây-làm-việc"
+
+#: git-stash.sh:352
+msgid "No stash found."
+msgstr "Không tìm thấy stast nào."
+
+#: git-stash.sh:359
+#, sh-format
+msgid "Too many revisions specified: $REV"
+msgstr "Chỉ ra quá nhiều điểm xét lại: $REV"
+
+#: git-stash.sh:365
+#, sh-format
+msgid "$reference is not valid reference"
+msgstr "$reference không phải là tham chiếu hợp lệ"
+
+#: git-stash.sh:393
+#, sh-format
+msgid "'$args' is not a stash-like commit"
+msgstr "'$args' không phải là lần chuyển giao (commit) giống-stash"
+
+#: git-stash.sh:404
+#, sh-format
+msgid "'$args' is not a stash reference"
+msgstr "'$args' không phải tham chiếu đến stash"
+
+#: git-stash.sh:412
+msgid "unable to refresh index"
+msgstr "không thể làm tươi mới bảng mục lục"
+
+#: git-stash.sh:416
+msgid "Cannot apply a stash in the middle of a merge"
+msgstr "Không thể áp dụng một stash ở giữa của quá trình hòa trộn"
+
+#: git-stash.sh:424
+msgid "Conflicts in index. Try without --index."
+msgstr "Xung đột trong bảng mục lục. Hãy thử mà không dùng tùy chọn --index."
+
+#: git-stash.sh:426
+msgid "Could not save index tree"
+msgstr "Không thể ghi lại cây chỉ mục"
+
+#: git-stash.sh:460
+msgid "Cannot unstage modified files"
+msgstr "Không thể bỏ trạng thía của các tập tin đã được sửa chữa"
+
+#: git-stash.sh:474
+msgid "Index was not unstashed."
+msgstr "Bảng mục lục đã không được bỏ stash."
+
+#: git-stash.sh:491
+#, sh-format
+msgid "Dropped ${REV} ($s)"
+msgstr "Đã hạ xuống ${REV} ($s)"
+
+#: git-stash.sh:492
+#, sh-format
+msgid "${REV}: Could not drop stash entry"
+msgstr "${REV}: Không thể xóa bỏ mục stash"
+
+#: git-stash.sh:499
+msgid "No branch name specified"
+msgstr "Chưa chỉ ra tên của nhánh"
+
+#: git-stash.sh:570
+msgid "(To restore them type \"git stash apply\")"
+msgstr "(Để phục hồi lại chúng hãy gõ \"git stash apply\")"
+
+#: git-submodule.sh:88
+#, sh-format
+msgid "cannot strip one component off url '$remoteurl'"
+msgstr "không thể tháo bỏ một thành phần ra khỏi url '$remoteurl'"
+
+#: git-submodule.sh:145
+#, sh-format
+msgid "No submodule mapping found in .gitmodules for path '$sm_path'"
+msgstr "Không tìm thấy ánh xạ (mapping) mô-đun-con trong .gitmodules cho đường dẫn '$sm_path'"
+
+#: git-submodule.sh:186
+#, sh-format
+msgid "Clone of '$url' into submodule path '$sm_path' failed"
+msgstr "Nhân bản '$url' vào đường dẫn mô-đun-con '$sm_path' gặp lỗi"
+
+#: git-submodule.sh:196
+#, sh-format
+msgid "Gitdir '$a' is part of the submodule path '$b' or vice versa"
+msgstr "Gitdir '$a' là bộ phận của đường dẫn mô-đun-con '$b' hoặc \"vice versa\""
+
+#: git-submodule.sh:285
+#, sh-format
+msgid "repo URL: '$repo' must be absolute or begin with ./|../"
+msgstr "repo URL: '$repo' phải là đường dẫn tuyệt đối hoặc là bắt đầu bằng ./|../"
+
+#: git-submodule.sh:302
+#, sh-format
+msgid "'$sm_path' already exists in the index"
+msgstr "'$sm_path' thực sự đã tồn tại ở bảng mục lục rồi"
+
+#: git-submodule.sh:306
+#, sh-format
+msgid ""
+"The following path is ignored by one of your .gitignore files:\n"
+"$sm_path\n"
+"Use -f if you really want to add it."
+msgstr ""
+"Các đường dẫn theo sau đây sẽ bị lờ đi bởi một trong các tập tin .gitignore của bạn:\n"
+"$sm_path\n"
+"Sử dụng -f nếu bạn thực sự muốn thêm nó vào."
+
+#: git-submodule.sh:317
+#, sh-format
+msgid "Adding existing repo at '$sm_path' to the index"
+msgstr "Đang thêm repo có sẵn tại '$sm_path' vào bảng mục lục"
+
+#: git-submodule.sh:319
+#, sh-format
+msgid "'$sm_path' already exists and is not a valid git repo"
+msgstr "'$sm_path' đã tồn tại từ trước và không phải là một kho git hợp lệ"
+
+#: git-submodule.sh:333
+#, sh-format
+msgid "Unable to checkout submodule '$sm_path'"
+msgstr "Không thể checkout mô-đun con '$sm_path'"
+
+#: git-submodule.sh:338
+#, sh-format
+msgid "Failed to add submodule '$sm_path'"
+msgstr "Gặp lỗi khi thêm mô-đun con '$sm_path'"
+
+#: git-submodule.sh:343
+#, sh-format
+msgid "Failed to register submodule '$sm_path'"
+msgstr "Gặp lỗi khi đăng ký với hệ thống mô-đun con '$sm_path'"
+
+#: git-submodule.sh:385
+#, sh-format
+msgid "Entering '$prefix$sm_path'"
+msgstr "Đang nhập '$prefix$sm_path'"
+
+#: git-submodule.sh:399
+#, sh-format
+msgid "Stopping at '$sm_path'; script returned non-zero status."
+msgstr "Dừng lại tại '$sm_path'; script trả về trạng thái khác không."
+
+#: git-submodule.sh:442
+#, sh-format
+msgid "No url found for submodule path '$sm_path' in .gitmodules"
+msgstr "Không tìm thấy url cho đường dẫn mô-đun-con '$sm_path' trong .gitmodules"
+
+#: git-submodule.sh:451
+#, sh-format
+msgid "Failed to register url for submodule path '$sm_path'"
+msgstr "Gặp lỗi khi đăng ký url cho đường dẫn mô-đun-con '$sm_path'"
+
+#: git-submodule.sh:453
+#, sh-format
+msgid "Submodule '$name' ($url) registered for path '$sm_path'"
+msgstr "Mô-đun-con '$name' ($url) được đăng ký cho đường dẫn '$sm_path'"
+
+#: git-submodule.sh:461
+#, sh-format
+msgid "Failed to register update mode for submodule path '$sm_path'"
+msgstr "Gặp lỗi khi đăng ký chế độ cập nhật cho đường dẫn mô-đun-con '$sm_path'"
+
+#: git-submodule.sh:560
+#, sh-format
+msgid ""
+"Submodule path '$sm_path' not initialized\n"
+"Maybe you want to use 'update --init'?"
+msgstr ""
+"Đường dẫn mô-đun-con '$sm_path' chưa được khởi tạo\n"
+"Có lẽ bạn muốn sử dụng lệnh 'update --init'?"
+
+#: git-submodule.sh:573
+#, sh-format
+msgid "Unable to find current revision in submodule path '$sm_path'"
+msgstr "Không tìm thấy điểm xét lại hiện hành trong đường dẫn mô-đun-con '$sm_path'"
+
+#: git-submodule.sh:592
+#, sh-format
+msgid "Unable to fetch in submodule path '$sm_path'"
+msgstr "Không thể lấy về (fetch) trong đường dẫn mô-đun-con '$sm_path'"
+
+#: git-submodule.sh:606
+#, sh-format
+msgid "Unable to rebase '$sha1' in submodule path '$sm_path'"
+msgstr "Không thể rebase '$sha1' trong đường dẫn mô-đun-con '$sm_path'"
+
+#: git-submodule.sh:607
+#, sh-format
+msgid "Submodule path '$sm_path': rebased into '$sha1'"
+msgstr "Đường dẫn mô-đun-con '$sm_path': được rebase vào trong '$sha1'"
+
+#: git-submodule.sh:612
+#, sh-format
+msgid "Unable to merge '$sha1' in submodule path '$sm_path'"
+msgstr "Không thể hòa trộn (merge) '$sha1' trong đường dẫn mô-đun-con '$sm_path'"
+
+#: git-submodule.sh:613
+#, sh-format
+msgid "Submodule path '$sm_path': merged in '$sha1'"
+msgstr "Đường dẫn mô-đun-con '$sm_path': được hòa trộn vào '$sha1'"
+
+#: git-submodule.sh:618
+#, sh-format
+msgid "Unable to checkout '$sha1' in submodule path '$sm_path'"
+msgstr "Không thể checkout '$sha1' trong đường dẫn mô-đun-con '$sm_path'"
+
+#: git-submodule.sh:619
+#, sh-format
+msgid "Submodule path '$sm_path': checked out '$sha1'"
+msgstr "Đường dẫn mô-đun-con '$sm_path': được checkout '$sha1'"
+
+#: git-submodule.sh:641
+#: git-submodule.sh:964
+#, sh-format
+msgid "Failed to recurse into submodule path '$sm_path'"
+msgstr "Gặp lỗi khi đệ quy vào trong đường dẫn mô-đun-con '$sm_path'"
+
+#: git-submodule.sh:749
+msgid "--cached cannot be used with --files"
+msgstr "--cached không thể được sử dụng cùng với --files"
+
+#. unexpected type
+#: git-submodule.sh:789
+#, sh-format
+msgid "unexpected mode $mod_dst"
+msgstr "chế độ không như mong chờ $mod_dst"
+
+#: git-submodule.sh:807
+#, sh-format
+msgid "  Warn: $name doesn't contain commit $sha1_src"
+msgstr "  Cảnh báo: $name không chứa lần chuyển giao (commit) $sha1_src"
+
+#: git-submodule.sh:810
+#, sh-format
+msgid "  Warn: $name doesn't contain commit $sha1_dst"
+msgstr "  Cảnh báo: $name không chứa lần chuyển giao (commit) $sha1_dst"
+
+#: git-submodule.sh:813
+#, sh-format
+msgid "  Warn: $name doesn't contain commits $sha1_src and $sha1_dst"
+msgstr "  Cảnh báo: $name không chứa những lần chuyển giao (commit) $sha1_src và $sha1_dst"
+
+#: git-submodule.sh:838
+msgid "blob"
+msgstr "blob"
+
+#: git-submodule.sh:839
+msgid "submodule"
+msgstr "mô-đun con"
+
+#: git-submodule.sh:876
+msgid "# Submodules changed but not updated:"
+msgstr "# Những mô-đun-con đã bị thay đổi nhưng chưa được cập nhật:"
+
+#: git-submodule.sh:878
+msgid "# Submodule changes to be committed:"
+msgstr "# Những thay đổi mô-đun-con được chuyển giao (commit):"
+
+#: git-submodule.sh:1022
+#, sh-format
+msgid "Synchronizing submodule url for '$name'"
+msgstr "Đang đồng bộ hóa url mô-đun-con cho '$name'"
+
+#~ msgid "--"
+#~ msgstr "--"
+
+#~ msgid "Could not extract email from committer identity."
+#~ msgstr ""
+#~ "Không thể rút trích địa chỉ thư điện tử từ định danh người chuyển giao"
+
+#, fuzzy
+#~ msgid "could not parse commit %s\n"
+#~ msgstr "Không thể phân tích commit (lần chuyển giao) %s\n"
+
+#, fuzzy
+#~ msgid "cherry-pick"
+#~ msgstr "< Chọn D-Mod"
+
+#, fuzzy
+#~ msgid "Too many options specified"
+#~ msgstr "đã ghi rõ quá nhiều kích cỡ"
diff --git a/po/zh_CN.po b/po/zh_CN.po
new file mode 100644
index 0000000..b3e1a54c
--- /dev/null
+++ b/po/zh_CN.po
@@ -0,0 +1,5607 @@
+# Chinese translations for Git package
+# Git 软件包的简体中文翻译.
+# Copyright (C) 2012 Jiang Xin <worldhello.net AT gmail.com>
+# This file is distributed under the same license as the Git package.
+# Contributers:
+#   - Jiang Xin <worldhello.net AT gmail.com>
+#   - Riku <lu.riku AT gmail.com>
+#   - Zhuang Ya <zhuangya AT me.com>
+#   - Lian Cheng <rhythm.mail AT gmail.com>
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Git\n"
+"Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
+"POT-Creation-Date: 2012-07-03 10:23+0800\n"
+"PO-Revision-Date: 2012-07-04 22:38+0800\n"
+"Last-Translator: Jiang Xin <worldhello.net@gmail.com>\n"
+"Language-Team: GitHub <https://github.com/gotgit/git/>\n"
+"Language: zh_CN\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: advice.c:40
+#, c-format
+msgid "hint: %.*s\n"
+msgstr "提示:%.*s\n"
+
+#.
+#. * Message used both when 'git commit' fails and when
+#. * other commands doing a merge do.
+#.
+#: advice.c:70
+msgid ""
+"Fix them up in the work tree,\n"
+"and then use 'git add/rm <file>' as\n"
+"appropriate to mark resolution and make a commit,\n"
+"or use 'git commit -a'."
+msgstr ""
+"请先在工作区改正文件,然后酌情使用\n"
+"'git add/rm <file>' 标记解决方案,\n"
+"或使用 'git commit -a'。"
+
+#: bundle.c:36
+#, c-format
+msgid "'%s' does not look like a v2 bundle file"
+msgstr "'%s' 不像是一个 v2 版本的包文件"
+
+#: bundle.c:63
+#, c-format
+msgid "unrecognized header: %s%s (%d)"
+msgstr "未能识别的包头:%s%s (%d)"
+
+#: bundle.c:89 builtin/commit.c:696
+#, c-format
+msgid "could not open '%s'"
+msgstr "不能打开 '%s'"
+
+#: bundle.c:140
+msgid "Repository lacks these prerequisite commits:"
+msgstr "版本库缺少这些必备的提交:"
+
+#: bundle.c:164 sequencer.c:550 sequencer.c:982 builtin/log.c:290
+#: builtin/log.c:721 builtin/log.c:1310 builtin/log.c:1529 builtin/merge.c:347
+#: builtin/shortlog.c:181
+msgid "revision walk setup failed"
+msgstr "版本遍历设置失败"
+
+#: bundle.c:186
+#, c-format
+msgid "The bundle contains %d ref"
+msgid_plural "The bundle contains %d refs"
+msgstr[0] "这个包中含有 %d 个引用"
+msgstr[1] "这个包中含有 %d 个引用"
+
+#: bundle.c:192
+msgid "The bundle records a complete history."
+msgstr "这个包记录一个完整历史。"
+
+#: bundle.c:195
+#, c-format
+msgid "The bundle requires this ref"
+msgid_plural "The bundle requires these %d refs"
+msgstr[0] "这个包需要这个引用"
+msgstr[1] "这个包需要 %d 个这些引用"
+
+#: bundle.c:294
+msgid "rev-list died"
+msgstr "rev-list 终止"
+
+#: bundle.c:300 builtin/log.c:1206 builtin/shortlog.c:284
+#, c-format
+msgid "unrecognized argument: %s"
+msgstr "未能识别的参数:%s"
+
+#: bundle.c:335
+#, c-format
+msgid "ref '%s' is excluded by the rev-list options"
+msgstr "引用 '%s' 被 rev-list 选项排除"
+
+#: bundle.c:380
+msgid "Refusing to create empty bundle."
+msgstr "不能创建空包。"
+
+#: bundle.c:398
+msgid "Could not spawn pack-objects"
+msgstr "不能生成 pack-objects 进程"
+
+#: bundle.c:416
+msgid "pack-objects died"
+msgstr "pack-objects 终止"
+
+#: bundle.c:419
+#, c-format
+msgid "cannot create '%s'"
+msgstr "不能创建 '%s'"
+
+#: bundle.c:441
+msgid "index-pack died"
+msgstr "index-pack 终止"
+
+#: commit.c:48
+#, c-format
+msgid "could not parse %s"
+msgstr "不能解析 %s"
+
+#: commit.c:50
+#, c-format
+msgid "%s %s is not a commit!"
+msgstr "%s %s 不是一个提交!"
+
+#: compat/obstack.c:406 compat/obstack.c:408
+msgid "memory exhausted"
+msgstr "内存耗尽"
+
+#: connected.c:39
+msgid "Could not run 'git rev-list'"
+msgstr "不能执行 'git rev-list'"
+
+#: connected.c:48
+#, c-format
+msgid "failed write to rev-list: %s"
+msgstr "无法写入 rev-list:%s"
+
+#: connected.c:56
+#, c-format
+msgid "failed to close rev-list's stdin: %s"
+msgstr "无法关闭 rev-list 的标准输入:%s"
+
+#: date.c:95
+msgid "in the future"
+msgstr "在将来"
+
+#: date.c:101
+#, c-format
+msgid "%lu second ago"
+msgid_plural "%lu seconds ago"
+msgstr[0] "%lu 秒钟之前"
+msgstr[1] "%lu 秒钟之前"
+
+#: date.c:108
+#, c-format
+msgid "%lu minute ago"
+msgid_plural "%lu minutes ago"
+msgstr[0] "%lu 分钟之前"
+msgstr[1] "%lu 分钟之前"
+
+#: date.c:115
+#, c-format
+msgid "%lu hour ago"
+msgid_plural "%lu hours ago"
+msgstr[0] "%lu 小时之前"
+msgstr[1] "%lu 小时之前"
+
+#: date.c:122
+#, c-format
+msgid "%lu day ago"
+msgid_plural "%lu days ago"
+msgstr[0] "%lu 天之前"
+msgstr[1] "%lu 天之前"
+
+#: date.c:128
+#, c-format
+msgid "%lu week ago"
+msgid_plural "%lu weeks ago"
+msgstr[0] "%lu 周之前"
+msgstr[1] "%lu 周之前"
+
+#: date.c:135
+#, c-format
+msgid "%lu month ago"
+msgid_plural "%lu months ago"
+msgstr[0] "%lu 个月之前"
+msgstr[1] "%lu 个月之前"
+
+#: date.c:146
+#, c-format
+msgid "%lu year"
+msgid_plural "%lu years"
+msgstr[0] "%lu 年"
+msgstr[1] "%lu 年"
+
+#: date.c:149
+#, c-format
+msgid "%s, %lu month ago"
+msgid_plural "%s, %lu months ago"
+msgstr[0] "%s,%lu 个月之前"
+msgstr[1] "%s,%lu 个月之前"
+
+#: date.c:154 date.c:159
+#, c-format
+msgid "%lu year ago"
+msgid_plural "%lu years ago"
+msgstr[0] "%lu 年前"
+msgstr[1] "%lu 年前"
+
+#  译者:注意保持前导空格
+#: diff.c:105
+#, c-format
+msgid "  Failed to parse dirstat cut-off percentage '%.*s'\n"
+msgstr "  无法解析 dirstat 阈值 '%.*s'\n"
+
+#  译者:注意保持前导空格
+#: diff.c:110
+#, c-format
+msgid "  Unknown dirstat parameter '%.*s'\n"
+msgstr "  未知 dirstat 参数 '%.*s'\n"
+
+#: diff.c:210
+#, c-format
+msgid ""
+"Found errors in 'diff.dirstat' config variable:\n"
+"%s"
+msgstr ""
+"发现配置变量 'diff.dirstat' 中的错误:\n"
+"%s"
+
+#: diff.c:1400
+msgid " 0 files changed\n"
+msgstr " 0 个文件被修改\n"
+
+#: diff.c:1404
+#, c-format
+msgid " %d file changed"
+msgid_plural " %d files changed"
+msgstr[0] " %d 个文件被修改"
+msgstr[1] " %d 个文件被修改"
+
+#: diff.c:1421
+#, c-format
+msgid ", %d insertion(+)"
+msgid_plural ", %d insertions(+)"
+msgstr[0] ",插入 %d 行(+)"
+msgstr[1] ",插入 %d 行(+)"
+
+#: diff.c:1432
+#, c-format
+msgid ", %d deletion(-)"
+msgid_plural ", %d deletions(-)"
+msgstr[0] ",删除 %d 行(-)"
+msgstr[1] ",删除 %d 行(-)"
+
+#: diff.c:3478
+#, c-format
+msgid ""
+"Failed to parse --dirstat/-X option parameter:\n"
+"%s"
+msgstr ""
+"无法解析 --dirstat/-X 选项的参数:\n"
+"%s"
+
+#: gpg-interface.c:59
+msgid "could not run gpg."
+msgstr "不能执行 gpg。"
+
+#: gpg-interface.c:71
+msgid "gpg did not accept the data"
+msgstr "gpg 没有接受数据"
+
+#: gpg-interface.c:82
+msgid "gpg failed to sign the data"
+msgstr "gpg 无法为数据签名"
+
+#: grep.c:1320
+#, c-format
+msgid "'%s': unable to read %s"
+msgstr "'%s':无法读取 %s"
+
+#: grep.c:1337
+#, c-format
+msgid "'%s': %s"
+msgstr "'%s':%s"
+
+#: grep.c:1348
+#, c-format
+msgid "'%s': short read %s"
+msgstr "'%s':读取不完整 %s"
+
+#: help.c:208
+#, c-format
+msgid "available git commands in '%s'"
+msgstr "在 '%s' 下可用的 git 命令"
+
+#: help.c:215
+msgid "git commands available from elsewhere on your $PATH"
+msgstr "在 $PATH 路径中的其他地方可用的 git 命令"
+
+#: help.c:271
+#, c-format
+msgid ""
+"'%s' appears to be a git command, but we were not\n"
+"able to execute it. Maybe git-%s is broken?"
+msgstr ""
+"'%s' 像是一个 git 命令,但却无法运行。\n"
+"可能是 git-%s 受损?"
+
+#: help.c:328
+msgid "Uh oh. Your system reports no Git commands at all."
+msgstr "唉呀,您的系统中未发现 Git 命令。"
+
+#: help.c:350
+#, c-format
+msgid ""
+"WARNING: You called a Git command named '%s', which does not exist.\n"
+"Continuing under the assumption that you meant '%s'"
+msgstr ""
+"警告:您运行一个不存在的 Git 命令 '%s'。继续执行假定您要要运行的\n"
+"是 '%s'"
+
+#: help.c:355
+#, c-format
+msgid "in %0.1f seconds automatically..."
+msgstr "在 %0.1f 秒钟后自动运行..."
+
+#: help.c:362
+#, c-format
+msgid "git: '%s' is not a git command. See 'git --help'."
+msgstr "git:'%s' 不是一个 git 命令。参见 'git --help'。"
+
+#: help.c:366
+msgid ""
+"\n"
+"Did you mean this?"
+msgid_plural ""
+"\n"
+"Did you mean one of these?"
+msgstr[0] ""
+"\n"
+"您指的是这个么?"
+msgstr[1] ""
+"\n"
+"您指的是这些其中一个么?"
+
+#: parse-options.c:493
+msgid "..."
+msgstr "..."
+
+#: parse-options.c:511
+#, c-format
+msgid "usage: %s"
+msgstr "用法:%s"
+
+#. TRANSLATORS: the colon here should align with the
+#. one in "usage: %s" translation
+#: parse-options.c:515
+#, c-format
+msgid "   or: %s"
+msgstr "  或:%s"
+
+#  译者:为保证在输出中对齐,注意调整句中空格!
+#: parse-options.c:518
+#, c-format
+msgid "    %s"
+msgstr "    %s"
+
+#: remote.c:1629
+#, c-format
+msgid "Your branch is ahead of '%s' by %d commit.\n"
+msgid_plural "Your branch is ahead of '%s' by %d commits.\n"
+msgstr[0] "您的分支领先 '%s' 共 %d 个提交。\n"
+msgstr[1] "您的分支领先 '%s' 共 %d 个提交。\n"
+
+#: remote.c:1635
+#, c-format
+msgid "Your branch is behind '%s' by %d commit, and can be fast-forwarded.\n"
+msgid_plural ""
+"Your branch is behind '%s' by %d commits, and can be fast-forwarded.\n"
+msgstr[0] "您的分支落后 '%s' 共 %d 个提交,并且可以快进。\n"
+msgstr[1] "您的分支落后 '%s' 共 %d 个提交,并且可以快进。\n"
+
+#: remote.c:1643
+#, c-format
+msgid ""
+"Your branch and '%s' have diverged,\n"
+"and have %d and %d different commit each, respectively.\n"
+msgid_plural ""
+"Your branch and '%s' have diverged,\n"
+"and have %d and %d different commits each, respectively.\n"
+msgstr[0] ""
+"您的分支和 '%s' 出现了偏离,\n"
+"并且各自分别有 %d 和 %d 处不同的提交。\n"
+msgstr[1] ""
+"您的分支和 '%s' 出现了偏离,\n"
+"并且各自分别有 %d 和 %d 处不同的提交。\n"
+
+#: sequencer.c:121 builtin/merge.c:865 builtin/merge.c:978
+#: builtin/merge.c:1088 builtin/merge.c:1098
+#, c-format
+msgid "Could not open '%s' for writing"
+msgstr "不能为写入打开 '%s'"
+
+#: sequencer.c:123 builtin/merge.c:333 builtin/merge.c:868
+#: builtin/merge.c:1090 builtin/merge.c:1103
+#, c-format
+msgid "Could not write to '%s'"
+msgstr "不能写入 '%s'"
+
+#: sequencer.c:144
+msgid ""
+"after resolving the conflicts, mark the corrected paths\n"
+"with 'git add <paths>' or 'git rm <paths>'"
+msgstr ""
+"冲突解决完毕后,用 'git add <paths>' 或 'git rm <paths>'\n"
+"命令标记修正后的文件"
+
+#: sequencer.c:147
+msgid ""
+"after resolving the conflicts, mark the corrected paths\n"
+"with 'git add <paths>' or 'git rm <paths>'\n"
+"and commit the result with 'git commit'"
+msgstr ""
+"冲突解决完毕后,用 'git add <paths>' 或 'git rm <paths>'\n"
+"对修正后的文件做标记,然后用 'git commit' 提交"
+
+#: sequencer.c:160 sequencer.c:758 sequencer.c:841
+#, c-format
+msgid "Could not write to %s"
+msgstr "不能写入 %s"
+
+#: sequencer.c:163
+#, c-format
+msgid "Error wrapping up %s"
+msgstr "错误收尾 %s"
+
+#: sequencer.c:178
+msgid "Your local changes would be overwritten by cherry-pick."
+msgstr "您的本地修改将被拣选操作覆盖。"
+
+#: sequencer.c:180
+msgid "Your local changes would be overwritten by revert."
+msgstr "您的本地修改将被还原操作覆盖。"
+
+#: sequencer.c:183
+msgid "Commit your changes or stash them to proceed."
+msgstr "提交您的修改或保存进度后再继续。"
+
+#. TRANSLATORS: %s will be "revert" or "cherry-pick"
+#: sequencer.c:233
+#, c-format
+msgid "%s: Unable to write new index file"
+msgstr "%s:无法写入新索引文件"
+
+#: sequencer.c:261
+msgid "Could not resolve HEAD commit\n"
+msgstr "不能解析 HEAD 提交\n"
+
+#: sequencer.c:282
+msgid "Unable to update cache tree\n"
+msgstr "不能更新缓存\n"
+
+#: sequencer.c:324
+#, c-format
+msgid "Could not parse commit %s\n"
+msgstr "不能解析提交 %s\n"
+
+#: sequencer.c:329
+#, c-format
+msgid "Could not parse parent commit %s\n"
+msgstr "不能解析父提交 %s\n"
+
+#: sequencer.c:395
+msgid "Your index file is unmerged."
+msgstr "您的索引文件未完成合并。"
+
+#: sequencer.c:398
+msgid "You do not have a valid HEAD"
+msgstr "您没有一个有效的 HEAD"
+
+#: sequencer.c:413
+#, c-format
+msgid "Commit %s is a merge but no -m option was given."
+msgstr "提交 %s 是一个合并提交但未提供 -m 选项。"
+
+#: sequencer.c:421
+#, c-format
+msgid "Commit %s does not have parent %d"
+msgstr "提交 %s 没有父提交 %d"
+
+#: sequencer.c:425
+#, c-format
+msgid "Mainline was specified but commit %s is not a merge."
+msgstr "指定了主线但提交 %s 不是一个合并。"
+
+#. TRANSLATORS: The first %s will be "revert" or
+#. "cherry-pick", the second %s a SHA1
+#: sequencer.c:436
+#, c-format
+msgid "%s: cannot parse parent commit %s"
+msgstr "%s:不能解析父提交 %s"
+
+#: sequencer.c:440
+#, c-format
+msgid "Cannot get commit message for %s"
+msgstr "不能得到 %s 的提交说明"
+
+#: sequencer.c:524
+#, c-format
+msgid "could not revert %s... %s"
+msgstr "不能还原 %s... %s"
+
+#: sequencer.c:525
+#, c-format
+msgid "could not apply %s... %s"
+msgstr "不能应用 %s... %s"
+
+#: sequencer.c:553
+msgid "empty commit set passed"
+msgstr "提供了空的提交集"
+
+#: sequencer.c:561
+#, c-format
+msgid "git %s: failed to read the index"
+msgstr "git %s:无法读取索引"
+
+#: sequencer.c:566
+#, c-format
+msgid "git %s: failed to refresh the index"
+msgstr "git %s:无法刷新索引"
+
+#: sequencer.c:624
+#, c-format
+msgid "Cannot %s during a %s"
+msgstr "无法 %s 在一个 %s 过程中"
+
+#: sequencer.c:646
+#, c-format
+msgid "Could not parse line %d."
+msgstr "不能解析第 %d 行。"
+
+#: sequencer.c:651
+msgid "No commits parsed."
+msgstr "没有提交被解析。"
+
+#: sequencer.c:664
+#, c-format
+msgid "Could not open %s"
+msgstr "不能打开 %s"
+
+#: sequencer.c:668
+#, c-format
+msgid "Could not read %s."
+msgstr "不能读取 %s。"
+
+#: sequencer.c:675
+#, c-format
+msgid "Unusable instruction sheet: %s"
+msgstr "无用的指令表单:%s"
+
+#: sequencer.c:703
+#, c-format
+msgid "Invalid key: %s"
+msgstr "无效键名:%s"
+
+#: sequencer.c:706
+#, c-format
+msgid "Invalid value for %s: %s"
+msgstr "%s 的值无效:%s"
+
+#: sequencer.c:718
+#, c-format
+msgid "Malformed options sheet: %s"
+msgstr "非法的选项表单:%s"
+
+#: sequencer.c:739
+msgid "a cherry-pick or revert is already in progress"
+msgstr "一个拣选或还原操作已在进行"
+
+#: sequencer.c:740
+msgid "try \"git cherry-pick (--continue | --quit | --abort)\""
+msgstr "尝试 \"git cherry-pick (--continue | --quit | --abort)\""
+
+#: sequencer.c:744
+#, c-format
+msgid "Could not create sequencer directory %s"
+msgstr "不能创建序列目录 %s"
+
+#: sequencer.c:760 sequencer.c:845
+#, c-format
+msgid "Error wrapping up %s."
+msgstr "错误收尾 %s。"
+
+#: sequencer.c:779 sequencer.c:913
+msgid "no cherry-pick or revert in progress"
+msgstr "没有拣选或还原操作在进行"
+
+#: sequencer.c:781
+msgid "cannot resolve HEAD"
+msgstr "不能解析 HEAD"
+
+#: sequencer.c:783
+msgid "cannot abort from a branch yet to be born"
+msgstr "不能从尚未建立的分支终止"
+
+#: sequencer.c:805 builtin/apply.c:3697
+#, c-format
+msgid "cannot open %s: %s"
+msgstr "不能打开 %s:%s"
+
+#: sequencer.c:808
+#, c-format
+msgid "cannot read %s: %s"
+msgstr "不能读取 %s:%s"
+
+#: sequencer.c:809
+msgid "unexpected end of file"
+msgstr "未预期的文件结束"
+
+#: sequencer.c:815
+#, c-format
+msgid "stored pre-cherry-pick HEAD file '%s' is corrupt"
+msgstr "保存拣选提交前的 HEAD 文件 '%s' 损坏"
+
+#: sequencer.c:838
+#, c-format
+msgid "Could not format %s."
+msgstr "不能格式化 %s。"
+
+#: sequencer.c:1000
+msgid "Can't revert as initial commit"
+msgstr "不能作为初始提交还原"
+
+#: sequencer.c:1001
+msgid "Can't cherry-pick into empty head"
+msgstr "不能拣选到空分支"
+
+#: sha1_name.c:864
+msgid "HEAD does not point to a branch"
+msgstr "HEAD 没有指向一个分支"
+
+#: sha1_name.c:867
+#, c-format
+msgid "No such branch: '%s'"
+msgstr "没有此分支:'%s'"
+
+#: sha1_name.c:869
+#, c-format
+msgid "No upstream configured for branch '%s'"
+msgstr "尚未给分支 '%s' 设置上游"
+
+#: sha1_name.c:872
+#, c-format
+msgid "Upstream branch '%s' not stored as a remote-tracking branch"
+msgstr "上游分支 '%s' 没有存储为一个远程跟踪分支"
+
+#: wrapper.c:413
+#, c-format
+msgid "unable to look up current user in the passwd file: %s"
+msgstr "无法在 passwd 文件中查询到当前用户:%s"
+
+#: wrapper.c:414
+msgid "no such user"
+msgstr "无此用户"
+
+#: wt-status.c:141
+msgid "Unmerged paths:"
+msgstr "未合并的路径:"
+
+#  译者:注意保持前导空格
+#: wt-status.c:168 wt-status.c:195
+#, c-format
+msgid "  (use \"git reset %s <file>...\" to unstage)"
+msgstr "  (使用 \"git reset %s <file>...\" 撤出暂存区)"
+
+#  译者:注意保持前导空格
+#: wt-status.c:170 wt-status.c:197
+msgid "  (use \"git rm --cached <file>...\" to unstage)"
+msgstr "  (使用 \"git rm --cached <file>...\" 撤出暂存区)"
+
+#  译者:注意保持前导空格
+#: wt-status.c:174
+msgid "  (use \"git add <file>...\" to mark resolution)"
+msgstr "  (使用 \"git add <file>...\" 标记解决方案)"
+
+#  译者:注意保持前导空格
+#: wt-status.c:176 wt-status.c:180
+msgid "  (use \"git add/rm <file>...\" as appropriate to mark resolution)"
+msgstr "  (酌情使用 \"git add/rm <file>...\" 标记解决方案)"
+
+#  译者:注意保持前导空格
+#: wt-status.c:178
+msgid "  (use \"git rm <file>...\" to mark resolution)"
+msgstr "  (使用 \"git rm <file>...\" 标记解决方案)"
+
+#: wt-status.c:189
+msgid "Changes to be committed:"
+msgstr "要提交的变更:"
+
+#: wt-status.c:207
+msgid "Changes not staged for commit:"
+msgstr "尚未暂存以备提交的变更:"
+
+#  译者:注意保持前导空格
+#: wt-status.c:211
+msgid "  (use \"git add <file>...\" to update what will be committed)"
+msgstr "  (使用 \"git add <file>...\" 更新要提交的内容)"
+
+#  译者:注意保持前导空格
+#: wt-status.c:213
+msgid "  (use \"git add/rm <file>...\" to update what will be committed)"
+msgstr "  (使用 \"git add/rm <file>...\" 更新要提交的内容)"
+
+#  译者:注意保持前导空格
+#: wt-status.c:214
+msgid ""
+"  (use \"git checkout -- <file>...\" to discard changes in working directory)"
+msgstr "  (使用 \"git checkout -- <file>...\" 丢弃工作区的改动)"
+
+#  译者:注意保持前导空格
+#: wt-status.c:216
+msgid "  (commit or discard the untracked or modified content in submodules)"
+msgstr "  (提交或丢弃子模组中未跟踪或修改的内容)"
+
+#: wt-status.c:225
+#, c-format
+msgid "%s files:"
+msgstr "%s文件:"
+
+#  译者:注意保持前导空格
+#: wt-status.c:228
+#, c-format
+msgid "  (use \"git %s <file>...\" to include in what will be committed)"
+msgstr "  (使用 \"git %s <file>...\" 以包含要提交的内容)"
+
+#: wt-status.c:245
+msgid "bug"
+msgstr "bug"
+
+#: wt-status.c:250
+msgid "both deleted:"
+msgstr "双方删除:"
+
+#: wt-status.c:251
+msgid "added by us:"
+msgstr "由我们添加:"
+
+#: wt-status.c:252
+msgid "deleted by them:"
+msgstr "由他们删除:"
+
+#: wt-status.c:253
+msgid "added by them:"
+msgstr "由他们添加:"
+
+#: wt-status.c:254
+msgid "deleted by us:"
+msgstr "由我们删除:"
+
+#: wt-status.c:255
+msgid "both added:"
+msgstr "双方添加:"
+
+#: wt-status.c:256
+msgid "both modified:"
+msgstr "双方修改:"
+
+#  译者:末尾两个字节可能被删减,如果翻译为中文标点会出现半个汉字
+#: wt-status.c:286
+msgid "new commits, "
+msgstr "新提交, "
+
+#  译者:末尾两个字节可能被删减,如果翻译为中文标点会出现半个汉字
+#: wt-status.c:288
+msgid "modified content, "
+msgstr "修改的内容, "
+
+#  译者:末尾两个字节可能被删减,如果翻译为中文标点会出现半个汉字
+#: wt-status.c:290
+msgid "untracked content, "
+msgstr "未跟踪的内容, "
+
+#  译者:为保证在输出中对齐,注意调整句中空格!
+#: wt-status.c:304
+#, c-format
+msgid "new file:   %s"
+msgstr "新文件:    %s"
+
+#  译者:为保证在输出中对齐,注意调整句中空格!
+#: wt-status.c:307
+#, c-format
+msgid "copied:     %s -> %s"
+msgstr "拷贝:      %s -> %s"
+
+#  译者:为保证在输出中对齐,注意调整句中空格!
+#: wt-status.c:310
+#, c-format
+msgid "deleted:    %s"
+msgstr "删除:      %s"
+
+#  译者:为保证在输出中对齐,注意调整句中空格!
+#: wt-status.c:313
+#, c-format
+msgid "modified:   %s"
+msgstr "修改:      %s"
+
+#  译者:为保证在输出中对齐,注意调整句中空格!
+#: wt-status.c:316
+#, c-format
+msgid "renamed:    %s -> %s"
+msgstr "重命名:    %s -> %s"
+
+#  译者:为保证在输出中对齐,注意调整句中空格!
+#: wt-status.c:319
+#, c-format
+msgid "typechange: %s"
+msgstr "类型变更:  %s"
+
+#  译者:为保证在输出中对齐,注意调整句中空格!
+#: wt-status.c:322
+#, c-format
+msgid "unknown:    %s"
+msgstr "未知:      %s"
+
+#  译者:为保证在输出中对齐,注意调整句中空格!
+#: wt-status.c:325
+#, c-format
+msgid "unmerged:   %s"
+msgstr "未合并:    %s"
+
+#: wt-status.c:328
+#, c-format
+msgid "bug: unhandled diff status %c"
+msgstr "bug:未处理的差异状态 %c"
+
+#: wt-status.c:786
+msgid "You have unmerged paths."
+msgstr "您有路径尚未合并。"
+
+#  译者:注意保持前导空格
+#: wt-status.c:789 wt-status.c:913
+msgid "  (fix conflicts and run \"git commit\")"
+msgstr "  (解决冲突并运行 \"git commit\")"
+
+#: wt-status.c:792
+msgid "All conflicts fixed but you are still merging."
+msgstr "所有冲突已解决但您仍处于合并中。"
+
+#  译者:注意保持前导空格
+#: wt-status.c:795
+msgid "  (use \"git commit\" to conclude merge)"
+msgstr "  (使用 \"git commit\" 结束合并)"
+
+#: wt-status.c:805
+msgid "You are in the middle of an am session."
+msgstr "您正处于一个 am 过程中。"
+
+#: wt-status.c:808
+msgid "The current patch is empty."
+msgstr "当前的补丁为空。"
+
+#  译者:注意保持前导空格
+#: wt-status.c:812
+msgid "  (fix conflicts and then run \"git am --resolved\")"
+msgstr "  (解决冲突,然后运行 \"git am --resolved\")"
+
+#  译者:注意保持前导空格
+#: wt-status.c:814
+msgid "  (use \"git am --skip\" to skip this patch)"
+msgstr "  (使用 \"git am --skip\" 跳过此补丁)"
+
+#  译者:注意保持前导空格
+#: wt-status.c:816
+msgid "  (use \"git am --abort\" to restore the original branch)"
+msgstr "  (使用 \"git am --abort\" 恢复原有分支)"
+
+#: wt-status.c:874 wt-status.c:884
+msgid "You are currently rebasing."
+msgstr "您正在变基。"
+
+#  译者:注意保持前导空格
+#: wt-status.c:877
+msgid "  (fix conflicts and then run \"git rebase --continue\")"
+msgstr "  (解决冲突,然后运行 \"git rebase --continue\")"
+
+#  译者:注意保持前导空格
+#: wt-status.c:879
+msgid "  (use \"git rebase --skip\" to skip this patch)"
+msgstr "  (使用 \"git rebase --skip\" 跳过此补丁)"
+
+#  译者:注意保持前导空格
+#: wt-status.c:881
+msgid "  (use \"git rebase --abort\" to check out the original branch)"
+msgstr "  (使用 \"git rebase --abort\" 以检出原有分支)"
+
+#  译者:注意保持前导空格
+#: wt-status.c:887
+msgid "  (all conflicts fixed: run \"git rebase --continue\")"
+msgstr "  (所有冲突已解决:运行 \"git rebase --continue\")"
+
+#: wt-status.c:889
+msgid "You are currently splitting a commit during a rebase."
+msgstr "您正在变基过程中拆分一个提交。"
+
+#  译者:注意保持前导空格
+#: wt-status.c:892
+msgid "  (Once your working directory is clean, run \"git rebase --continue\")"
+msgstr "  (一旦您工作目录提交干净后,运行 \"git rebase --continue\")"
+
+#: wt-status.c:894
+msgid "You are currently editing a commit during a rebase."
+msgstr "您正在变基过程中编辑一个提交。"
+
+#  译者:注意保持前导空格
+#: wt-status.c:897
+msgid "  (use \"git commit --amend\" to amend the current commit)"
+msgstr "  (使用 \"git commit --amend\" 修补当前提交)"
+
+#  译者:注意保持前导空格
+#: wt-status.c:899
+msgid ""
+"  (use \"git rebase --continue\" once you are satisfied with your changes)"
+msgstr ""
+"  (执行 \"git rebase --continue\" 一旦您满意您的修改)"
+
+#: wt-status.c:909
+msgid "You are currently cherry-picking."
+msgstr "您正在做拣选操作。"
+
+#  译者:注意保持前导空格
+#: wt-status.c:916
+msgid "  (all conflicts fixed: run \"git commit\")"
+msgstr "  (解决所有冲突后,执行 \"git commit\")"
+
+#: wt-status.c:925
+msgid "You are currently bisecting."
+msgstr "您正在做二分查找。"
+
+#  译者:注意保持前导空格
+#: wt-status.c:928
+msgid "  (use \"git bisect reset\" to get back to the original branch)"
+msgstr "  (使用 \"git bisect reset\" 以回到原有分支)"
+
+#: wt-status.c:979
+msgid "On branch "
+msgstr "位于分支 "
+
+#: wt-status.c:986
+msgid "Not currently on any branch."
+msgstr "当前不在任何分支上。"
+
+#: wt-status.c:998
+msgid "Initial commit"
+msgstr "初始提交"
+
+#: wt-status.c:1012
+msgid "Untracked"
+msgstr "未跟踪的"
+
+#: wt-status.c:1014
+msgid "Ignored"
+msgstr "忽略的"
+
+#: wt-status.c:1016
+#, c-format
+msgid "Untracked files not listed%s"
+msgstr "未跟踪的文件没有列出%s"
+
+#  译者:中文字符串拼接,可删除前导空格
+#: wt-status.c:1018
+msgid " (use -u option to show untracked files)"
+msgstr "(使用 -u 参数显示未跟踪的文件)"
+
+#: wt-status.c:1024
+msgid "No changes"
+msgstr "没有修改"
+
+#: wt-status.c:1028
+#, c-format
+msgid "no changes added to commit%s\n"
+msgstr "修改尚未加入提交%s\n"
+
+#  译者:中文字符串拼接,可删除前导空格
+#: wt-status.c:1030
+msgid " (use \"git add\" and/or \"git commit -a\")"
+msgstr "(使用 \"git add\" 和/或 \"git commit -a\")"
+
+#: wt-status.c:1032
+#, c-format
+msgid "nothing added to commit but untracked files present%s\n"
+msgstr "空提交但存在未跟踪文件%s\n"
+
+#  译者:中文字符串拼接,可删除前导空格
+#: wt-status.c:1034
+msgid " (use \"git add\" to track)"
+msgstr "(使用 \"git add\" 建立跟踪)"
+
+#: wt-status.c:1036 wt-status.c:1039 wt-status.c:1042
+#, c-format
+msgid "nothing to commit%s\n"
+msgstr "无须提交%s\n"
+
+#  译者:中文字符串拼接,可删除前导空格
+#: wt-status.c:1037
+msgid " (create/copy files and use \"git add\" to track)"
+msgstr "(新建/拷贝的文件使用 \"git add\" 建立跟踪)"
+
+#  译者:中文字符串拼接,可删除前导空格
+#: wt-status.c:1040
+msgid " (use -u to show untracked files)"
+msgstr "(使用 -u 显示未跟踪文件)"
+
+#  译者:中文字符串拼接,可删除前导空格
+#: wt-status.c:1043
+msgid " (working directory clean)"
+msgstr "(干净的工作区)"
+
+#: wt-status.c:1151
+msgid "HEAD (no branch)"
+msgstr "HEAD(非分支)"
+
+#  译者:注意保持句尾空格
+#: wt-status.c:1157
+msgid "Initial commit on "
+msgstr "初始提交于 "
+
+#  译者:注意保持句尾空格
+#: wt-status.c:1172
+msgid "behind "
+msgstr "落后 "
+
+#  译者:注意保持句尾空格
+#: wt-status.c:1175 wt-status.c:1178
+msgid "ahead "
+msgstr "领先 "
+
+#  译者:注意保持句尾空格
+#: wt-status.c:1180
+msgid ", behind "
+msgstr ",落后 "
+
+#: builtin/add.c:62
+#, c-format
+msgid "unexpected diff status %c"
+msgstr "意外的差异状态 %c"
+
+#: builtin/add.c:67 builtin/commit.c:226
+msgid "updating files failed"
+msgstr "更新文件失败"
+
+#: builtin/add.c:77
+#, c-format
+msgid "remove '%s'\n"
+msgstr "删除 '%s'\n"
+
+#: builtin/add.c:176
+#, c-format
+msgid "Path '%s' is in submodule '%.*s'"
+msgstr "路径 '%s' 属于模组 '%.*s'"
+
+#: builtin/add.c:192
+msgid "Unstaged changes after refreshing the index:"
+msgstr "刷新索引之后尚未被暂存的变更:"
+
+#: builtin/add.c:195 builtin/add.c:459 builtin/rm.c:186
+#, c-format
+msgid "pathspec '%s' did not match any files"
+msgstr "路径 '%s' 未匹配任何文件"
+
+#: builtin/add.c:209
+#, c-format
+msgid "'%s' is beyond a symbolic link"
+msgstr "'%s' 位于符号链接中"
+
+#: builtin/add.c:276
+msgid "Could not read the index"
+msgstr "不能读取索引"
+
+#: builtin/add.c:286
+#, c-format
+msgid "Could not open '%s' for writing."
+msgstr "不能为写入打开 '%s'。"
+
+#: builtin/add.c:290
+msgid "Could not write patch"
+msgstr "不能写补丁"
+
+#: builtin/add.c:295
+#, c-format
+msgid "Could not stat '%s'"
+msgstr "不能查看文件状态 '%s'"
+
+#: builtin/add.c:297
+msgid "Empty patch. Aborted."
+msgstr "空补丁。异常终止。"
+
+#: builtin/add.c:303
+#, c-format
+msgid "Could not apply '%s'"
+msgstr "不能应用 '%s'"
+
+#: builtin/add.c:312
+msgid "The following paths are ignored by one of your .gitignore files:\n"
+msgstr "下列路径被您的一个 .gitignore 文件所忽略:\n"
+
+#: builtin/add.c:352
+#, c-format
+msgid "Use -f if you really want to add them.\n"
+msgstr "使用 -f 参数如果您确实要添加它们。\n"
+
+#: builtin/add.c:353
+msgid "no files added"
+msgstr "没有文件被添加"
+
+#: builtin/add.c:359
+msgid "adding files failed"
+msgstr "添加文件失败"
+
+#: builtin/add.c:391
+msgid "-A and -u are mutually incompatible"
+msgstr "-A 和 -u 选项互斥"
+
+#: builtin/add.c:393
+msgid "Option --ignore-missing can only be used together with --dry-run"
+msgstr "选项 --ignore-missing 只能和 --dry-run 共用"
+
+#: builtin/add.c:413
+#, c-format
+msgid "Nothing specified, nothing added.\n"
+msgstr "没有指定文件,也没有文件被添加。\n"
+
+#: builtin/add.c:414
+#, c-format
+msgid "Maybe you wanted to say 'git add .'?\n"
+msgstr "也许您想要执行 'git add .'?\n"
+
+#: builtin/add.c:420 builtin/clean.c:95 builtin/commit.c:286 builtin/mv.c:82
+#: builtin/rm.c:162
+msgid "index file corrupt"
+msgstr "索引文件损坏"
+
+#: builtin/add.c:480 builtin/apply.c:4108 builtin/mv.c:229 builtin/rm.c:260
+msgid "Unable to write new index file"
+msgstr "无法写入新索引文件"
+
+#: builtin/apply.c:53
+msgid "git apply [options] [<patch>...]"
+msgstr "git apply [选项] [<补丁>...]"
+
+#: builtin/apply.c:106
+#, c-format
+msgid "unrecognized whitespace option '%s'"
+msgstr "未能识别的空白字符选项 '%s'"
+
+#: builtin/apply.c:121
+#, c-format
+msgid "unrecognized whitespace ignore option '%s'"
+msgstr "未能识别的空白字符忽略选项 '%s'"
+
+#: builtin/apply.c:815
+#, c-format
+msgid "Cannot prepare timestamp regexp %s"
+msgstr "无法准备时间戳正则表达式 %s"
+
+#: builtin/apply.c:824
+#, c-format
+msgid "regexec returned %d for input: %s"
+msgstr "regexec 返回 %d,输入为:%s"
+
+#: builtin/apply.c:905
+#, c-format
+msgid "unable to find filename in patch at line %d"
+msgstr "不能在补丁的第 %d 行找到文件名"
+
+#: builtin/apply.c:937
+#, c-format
+msgid "git apply: bad git-diff - expected /dev/null, got %s on line %d"
+msgstr "git apply:错误的 git-diff - 期望 /dev/null,但在第 %2$d 行得到 %1$s"
+
+#: builtin/apply.c:941
+#, c-format
+msgid "git apply: bad git-diff - inconsistent new filename on line %d"
+msgstr "git apply:错误的 git-diff - 第 %d 行上新文件名不一致"
+
+#: builtin/apply.c:942
+#, c-format
+msgid "git apply: bad git-diff - inconsistent old filename on line %d"
+msgstr "git apply:错误的 git-diff - 第 %d 行上旧文件名不一致"
+
+#: builtin/apply.c:949
+#, c-format
+msgid "git apply: bad git-diff - expected /dev/null on line %d"
+msgstr "git apply:错误的 git-diff - 期望 /dev/null 于第 %d 行"
+
+#: builtin/apply.c:1394
+#, c-format
+msgid "recount: unexpected line: %.*s"
+msgstr "recount:意外的行:%.*s"
+
+#: builtin/apply.c:1451
+#, c-format
+msgid "patch fragment without header at line %d: %.*s"
+msgstr "第 %d 行的补丁片段没有头信息:%.*s"
+
+#: builtin/apply.c:1468
+#, c-format
+msgid ""
+"git diff header lacks filename information when removing %d leading pathname "
+"component (line %d)"
+msgid_plural ""
+"git diff header lacks filename information when removing %d leading pathname "
+"components (line %d)"
+msgstr[0] "当移除 %d 个前导路径后 git diff 头缺乏文件名信息(第 %d 行)"
+msgstr[1] "当移除 %d 个前导路径后 git diff 头缺乏文件名信息(第 %d 行)"
+
+#: builtin/apply.c:1628
+msgid "new file depends on old contents"
+msgstr "新文件依赖旧内容"
+
+#: builtin/apply.c:1630
+msgid "deleted file still has contents"
+msgstr "删除的文件仍有内容"
+
+#: builtin/apply.c:1656
+#, c-format
+msgid "corrupt patch at line %d"
+msgstr "补丁损坏位于第 %d 行"
+
+#: builtin/apply.c:1692
+#, c-format
+msgid "new file %s depends on old contents"
+msgstr "新文件 %s 依赖旧内容"
+
+#: builtin/apply.c:1694
+#, c-format
+msgid "deleted file %s still has contents"
+msgstr "删除的文件 %s 仍有内容"
+
+#: builtin/apply.c:1697
+#, c-format
+msgid "** warning: file %s becomes empty but is not deleted"
+msgstr "** 警告:文件 %s 成为空文件但并未删除"
+
+#: builtin/apply.c:1843
+#, c-format
+msgid "corrupt binary patch at line %d: %.*s"
+msgstr "二进制补丁在第 %d 行损坏:%.*s"
+
+#. there has to be one hunk (forward hunk)
+#: builtin/apply.c:1872
+#, c-format
+msgid "unrecognized binary patch at line %d"
+msgstr "未能识别的二进制补丁位于第 %d 行"
+
+#: builtin/apply.c:1958
+#, c-format
+msgid "patch with only garbage at line %d"
+msgstr "补丁文件的第 %d 行只有垃圾数据"
+
+#: builtin/apply.c:2048
+#, c-format
+msgid "unable to read symlink %s"
+msgstr "无法读取符号链接 %s"
+
+#: builtin/apply.c:2052
+#, c-format
+msgid "unable to open or read %s"
+msgstr "不能打开或读取 %s"
+
+#: builtin/apply.c:2123
+msgid "oops"
+msgstr "哎哟"
+
+#: builtin/apply.c:2645
+#, c-format
+msgid "invalid start of line: '%c'"
+msgstr "无效的行首字符:'%c'"
+
+#: builtin/apply.c:2763
+#, c-format
+msgid "Hunk #%d succeeded at %d (offset %d line)."
+msgid_plural "Hunk #%d succeeded at %d (offset %d lines)."
+msgstr[0] "块 #%d 成功应用于 %d (偏移 %d 行)"
+msgstr[1] "块 #%d 成功应用于 %d (偏移 %d 行)"
+
+#: builtin/apply.c:2775
+#, c-format
+msgid "Context reduced to (%ld/%ld) to apply fragment at %d"
+msgstr "上下文减少到(%ld/%ld)以在第 %d 行应用补丁片段"
+
+#: builtin/apply.c:2781
+#, c-format
+msgid ""
+"while searching for:\n"
+"%.*s"
+msgstr ""
+"当查询:\n"
+"%.*s"
+
+#: builtin/apply.c:2800
+#, c-format
+msgid "missing binary patch data for '%s'"
+msgstr "缺失 '%s' 的二进制补丁数据"
+
+#: builtin/apply.c:2903
+#, c-format
+msgid "binary patch does not apply to '%s'"
+msgstr "二进制补丁未应用到 '%s'"
+
+#: builtin/apply.c:2909
+#, c-format
+msgid "binary patch to '%s' creates incorrect result (expecting %s, got %s)"
+msgstr "到 '%s' 的二进制补丁产生了不正确的结果(预期 %s,得到 %s)"
+
+#: builtin/apply.c:2930
+#, c-format
+msgid "patch failed: %s:%ld"
+msgstr "打补丁失败:%s:%ld"
+
+#: builtin/apply.c:3045
+#, c-format
+msgid "patch %s has been renamed/deleted"
+msgstr "补丁 %s 已经被重命名/删除"
+
+#: builtin/apply.c:3052 builtin/apply.c:3069
+#, c-format
+msgid "read of %s failed"
+msgstr "读取 %s 失败"
+
+#: builtin/apply.c:3084
+msgid "removal patch leaves file contents"
+msgstr "移除补丁仍留下了文件内容"
+
+#: builtin/apply.c:3105
+#, c-format
+msgid "%s: already exists in working directory"
+msgstr "%s:已经存在于工作区中"
+
+#: builtin/apply.c:3143
+#, c-format
+msgid "%s: has been deleted/renamed"
+msgstr "%s:已经被删除/重命名"
+
+#: builtin/apply.c:3148 builtin/apply.c:3179
+#, c-format
+msgid "%s: %s"
+msgstr "%s:%s"
+
+#: builtin/apply.c:3159
+#, c-format
+msgid "%s: does not exist in index"
+msgstr "%s:不存在于索引中"
+
+#: builtin/apply.c:3173
+#, c-format
+msgid "%s: does not match index"
+msgstr "%s:和索引不匹配"
+
+#: builtin/apply.c:3190
+#, c-format
+msgid "%s: wrong type"
+msgstr "%s:错误类型"
+
+#: builtin/apply.c:3192
+#, c-format
+msgid "%s has type %o, expected %o"
+msgstr "%s 的类型是 %o,预期是 %o"
+
+#: builtin/apply.c:3247
+#, c-format
+msgid "%s: already exists in index"
+msgstr "%s:已经存在于索引中"
+
+#: builtin/apply.c:3267
+#, c-format
+msgid "new mode (%o) of %s does not match old mode (%o)"
+msgstr "%2$s 的新模式(%1$o)和旧模式(%3$o)不匹配"
+
+#: builtin/apply.c:3272
+#, c-format
+msgid "new mode (%o) of %s does not match old mode (%o) of %s"
+msgstr "%2$s 的新模式(%1$o)和 %4$s 的旧模式(%3$o)不匹配"
+
+#: builtin/apply.c:3280
+#, c-format
+msgid "%s: patch does not apply"
+msgstr "%s:补丁未应用"
+
+#: builtin/apply.c:3293
+#, c-format
+msgid "Checking patch %s..."
+msgstr "检查补丁 %s..."
+
+#: builtin/apply.c:3348 builtin/checkout.c:212 builtin/reset.c:158
+#, c-format
+msgid "make_cache_entry failed for path '%s'"
+msgstr "对路径 '%s' 的 make_cache_entry 操作失败"
+
+#: builtin/apply.c:3491
+#, c-format
+msgid "unable to remove %s from index"
+msgstr "不能从索引中移除 %s"
+
+#: builtin/apply.c:3518
+#, c-format
+msgid "corrupt patch for subproject %s"
+msgstr "子项目 %s 损坏的补丁"
+
+#: builtin/apply.c:3522
+#, c-format
+msgid "unable to stat newly created file '%s'"
+msgstr "不能枚举新建文件 '%s' 的状态"
+
+#: builtin/apply.c:3527
+#, c-format
+msgid "unable to create backing store for newly created file %s"
+msgstr "不能为新建文件 %s 创建后端存储"
+
+#: builtin/apply.c:3530
+#, c-format
+msgid "unable to add cache entry for %s"
+msgstr "无法为 %s 添加缓存条目"
+
+#: builtin/apply.c:3563
+#, c-format
+msgid "closing file '%s'"
+msgstr "关闭文件 '%s'"
+
+#: builtin/apply.c:3612
+#, c-format
+msgid "unable to write file '%s' mode %o"
+msgstr "不能写文件 '%s' 权限 %o"
+
+#: builtin/apply.c:3668
+#, c-format
+msgid "Applied patch %s cleanly."
+msgstr "成功应用补丁 %s。"
+
+#: builtin/apply.c:3676
+msgid "internal error"
+msgstr "内部错误"
+
+#. Say this even without --verbose
+#: builtin/apply.c:3679
+#, c-format
+msgid "Applying patch %%s with %d reject..."
+msgid_plural "Applying patch %%s with %d rejects..."
+msgstr[0] "应用补丁 %%s 时 %d 个被拒绝..."
+msgstr[1] "应用补丁 %%s 时 %d 个被拒绝..."
+
+#: builtin/apply.c:3689
+#, c-format
+msgid "truncating .rej filename to %.*s.rej"
+msgstr "截短 .rej 文件名为 %.*s.rej"
+
+#: builtin/apply.c:3710
+#, c-format
+msgid "Hunk #%d applied cleanly."
+msgstr "第 #%d 个片段成功应用。"
+
+#: builtin/apply.c:3713
+#, c-format
+msgid "Rejected hunk #%d."
+msgstr "拒绝第 #%d 个片段。"
+
+#: builtin/apply.c:3844
+msgid "unrecognized input"
+msgstr "未能识别的输入"
+
+#: builtin/apply.c:3855
+msgid "unable to read index file"
+msgstr "无法读取索引文件"
+
+#: builtin/apply.c:3970 builtin/apply.c:3973
+msgid "path"
+msgstr "路径"
+
+#: builtin/apply.c:3971
+msgid "don't apply changes matching the given path"
+msgstr "不要应用与给出路径向匹配的变更"
+
+#: builtin/apply.c:3974
+msgid "apply changes matching the given path"
+msgstr "应用与给出路径向匹配的变更"
+
+#: builtin/apply.c:3976
+msgid "num"
+msgstr "数字"
+
+#: builtin/apply.c:3977
+msgid "remove <num> leading slashes from traditional diff paths"
+msgstr "从传统的 diff 路径中移除 <数字> 个前导路径"
+
+#: builtin/apply.c:3980
+msgid "ignore additions made by the patch"
+msgstr "忽略补丁中的添加的文件"
+
+#: builtin/apply.c:3982
+msgid "instead of applying the patch, output diffstat for the input"
+msgstr "不应用补丁,而是显示输入的差异统计(diffstat)"
+
+#: builtin/apply.c:3986
+msgid "shows number of added and deleted lines in decimal notation"
+msgstr "以数字方式显示添加或删除行的数量"
+
+#: builtin/apply.c:3988
+msgid "instead of applying the patch, output a summary for the input"
+msgstr "不应用补丁,而是显示输入的概要"
+
+#: builtin/apply.c:3990
+msgid "instead of applying the patch, see if the patch is applicable"
+msgstr "不应用补丁,而是查看补丁是否可应用"
+
+#: builtin/apply.c:3992
+msgid "make sure the patch is applicable to the current index"
+msgstr "确认补丁可以应用到当前索引"
+
+#: builtin/apply.c:3994
+msgid "apply a patch without touching the working tree"
+msgstr "应用补丁而不修改工作区"
+
+#: builtin/apply.c:3996
+msgid "also apply the patch (use with --stat/--summary/--check)"
+msgstr "同时应用此补丁(和 --stat/--summary/--check 共用)"
+
+#: builtin/apply.c:3998
+msgid "build a temporary index based on embedded index information"
+msgstr "创建一个临时索引基于嵌入的索引信息"
+
+#: builtin/apply.c:4000
+msgid "paths are separated with NUL character"
+msgstr "路径以 NUL 字符分隔"
+
+#: builtin/apply.c:4003
+msgid "ensure at least <n> lines of context match"
+msgstr "确保至少匹配 <n> 行上下文"
+
+#: builtin/apply.c:4004
+msgid "action"
+msgstr "动作"
+
+#: builtin/apply.c:4005
+msgid "detect new or modified lines that have whitespace errors"
+msgstr "检查新增和修改的行中间的空白字符滥用"
+
+#: builtin/apply.c:4008 builtin/apply.c:4011
+msgid "ignore changes in whitespace when finding context"
+msgstr "查找上下文时忽略空白字符的变更"
+
+#: builtin/apply.c:4014
+msgid "apply the patch in reverse"
+msgstr "反向应用补丁"
+
+#: builtin/apply.c:4016
+msgid "don't expect at least one line of context"
+msgstr "无需至少一行上下文"
+
+#: builtin/apply.c:4018
+msgid "leave the rejected hunks in corresponding *.rej files"
+msgstr "将拒绝的补丁片段保存在对应的 *.rej 文件中"
+
+#: builtin/apply.c:4020
+msgid "allow overlapping hunks"
+msgstr "允许重叠的补丁片段"
+
+#: builtin/apply.c:4021
+msgid "be verbose"
+msgstr "冗长输出"
+
+#: builtin/apply.c:4023
+msgid "tolerate incorrectly detected missing new-line at the end of file"
+msgstr "宽容不正确的文件末尾换行符"
+
+#: builtin/apply.c:4026
+msgid "do not trust the line counts in the hunk headers"
+msgstr "不信任补丁片段的头信息中的行号"
+
+#: builtin/apply.c:4028
+msgid "root"
+msgstr "根目录"
+
+#: builtin/apply.c:4029
+msgid "prepend <root> to all filenames"
+msgstr "为所有文件名前添加 <根目录>"
+
+#: builtin/apply.c:4050
+msgid "--index outside a repository"
+msgstr "--index 在一个版本库之外"
+
+#: builtin/apply.c:4053
+msgid "--cached outside a repository"
+msgstr "--cached 在一个版本库之外"
+
+#: builtin/apply.c:4069
+#, c-format
+msgid "can't open patch '%s'"
+msgstr "不能打开补丁 '%s'"
+
+#: builtin/apply.c:4083
+#, c-format
+msgid "squelched %d whitespace error"
+msgid_plural "squelched %d whitespace errors"
+msgstr[0] "抑制下仍有 %d 个空白字符误用"
+msgstr[1] "抑制下仍有 %d 个空白字符误用"
+
+#: builtin/apply.c:4089 builtin/apply.c:4099
+#, c-format
+msgid "%d line adds whitespace errors."
+msgid_plural "%d lines add whitespace errors."
+msgstr[0] "%d 行有空白字符误用。"
+msgstr[1] "%d 行有空白字符误用。"
+
+#: builtin/archive.c:17
+#, c-format
+msgid "could not create archive file '%s'"
+msgstr "不能创建归档文件 '%s'"
+
+#: builtin/archive.c:20
+msgid "could not redirect output"
+msgstr "不能输出重定向"
+
+#: builtin/archive.c:37
+msgid "git archive: Remote with no URL"
+msgstr "git archive:未提供远程URL"
+
+#: builtin/archive.c:58
+msgid "git archive: expected ACK/NAK, got EOF"
+msgstr "git archive:期待ACK/NACK,却得到EOF"
+
+#: builtin/archive.c:63
+#, c-format
+msgid "git archive: NACK %s"
+msgstr "git archive:NACK %s"
+
+#: builtin/archive.c:65
+#, c-format
+msgid "remote error: %s"
+msgstr "远程错误:%s"
+
+#: builtin/archive.c:66
+msgid "git archive: protocol error"
+msgstr "git archive:协议错误"
+
+#: builtin/archive.c:71
+msgid "git archive: expected a flush"
+msgstr "git archive:预期一个刷新"
+
+#  译者:保持原换行格式,在输出时 %s 的替代内容会让字符串变长
+#: builtin/branch.c:144
+#, c-format
+msgid ""
+"deleting branch '%s' that has been merged to\n"
+"         '%s', but not yet merged to HEAD."
+msgstr ""
+"将要删除的分支 '%s' 已经被合并到\n"
+"         '%s',但未合并到 HEAD。"
+
+#  译者:保持原换行格式,在输出时 %s 的替代内容会让字符串变长
+#: builtin/branch.c:148
+#, c-format
+msgid ""
+"not deleting branch '%s' that is not yet merged to\n"
+"         '%s', even though it is merged to HEAD."
+msgstr ""
+"并未删除分支 '%s', 虽然它已经合并到 HEAD,\n"
+"         然而却尚未被合并到分支 '%s' 。"
+
+#: builtin/branch.c:180
+msgid "cannot use -a with -d"
+msgstr "不能将 -a 和 -d 共用"
+
+#: builtin/branch.c:186
+msgid "Couldn't look up commit object for HEAD"
+msgstr "无法查询 HEAD 指向的提交对象"
+
+#: builtin/branch.c:191
+#, c-format
+msgid "Cannot delete the branch '%s' which you are currently on."
+msgstr "无法删除您当前所在的分支 '%s'。"
+
+#: builtin/branch.c:202
+#, c-format
+msgid "remote branch '%s' not found."
+msgstr "远程分支 '%s' 未发现。"
+
+#: builtin/branch.c:203
+#, c-format
+msgid "branch '%s' not found."
+msgstr "分支 '%s' 未发现。"
+
+#: builtin/branch.c:210
+#, c-format
+msgid "Couldn't look up commit object for '%s'"
+msgstr "无法查询 '%s' 指向的提交对象"
+
+#: builtin/branch.c:216
+#, c-format
+msgid ""
+"The branch '%s' is not fully merged.\n"
+"If you are sure you want to delete it, run 'git branch -D %s'."
+msgstr ""
+"分支 '%s' 没有完全合并。\n"
+"如果您确认要删除它,执行 'git branch -D %s'。"
+
+#: builtin/branch.c:225
+#, c-format
+msgid "Error deleting remote branch '%s'"
+msgstr "删除远程分支 '%s' 时出错"
+
+#: builtin/branch.c:226
+#, c-format
+msgid "Error deleting branch '%s'"
+msgstr "删除分支 '%s' 时出错"
+
+#: builtin/branch.c:233
+#, c-format
+msgid "Deleted remote branch %s (was %s).\n"
+msgstr "已删除远程分支 %s(曾为 %s)。\n"
+
+#: builtin/branch.c:234
+#, c-format
+msgid "Deleted branch %s (was %s).\n"
+msgstr "已删除分支 %s(曾为 %s)。\n"
+
+#: builtin/branch.c:239
+msgid "Update of config-file failed"
+msgstr "无法更新 config 文件"
+
+#: builtin/branch.c:337
+#, c-format
+msgid "branch '%s' does not point at a commit"
+msgstr "分支 '%s' 未指向一个提交"
+
+#: builtin/branch.c:409
+#, c-format
+msgid "[%s: behind %d]"
+msgstr "[%s:落后 %d]"
+
+#: builtin/branch.c:411
+#, c-format
+msgid "[behind %d]"
+msgstr "[落后 %d]"
+
+#: builtin/branch.c:415
+#, c-format
+msgid "[%s: ahead %d]"
+msgstr "[%s:领先 %d]"
+
+#: builtin/branch.c:417
+#, c-format
+msgid "[ahead %d]"
+msgstr "[领先 %d]"
+
+#: builtin/branch.c:420
+#, c-format
+msgid "[%s: ahead %d, behind %d]"
+msgstr "[%s:领先 %d,落后 %d]"
+
+#: builtin/branch.c:423
+#, c-format
+msgid "[ahead %d, behind %d]"
+msgstr "[领先 %d,落后 %d]"
+
+#: builtin/branch.c:535
+msgid "(no branch)"
+msgstr "(非分支)"
+
+#: builtin/branch.c:600
+msgid "some refs could not be read"
+msgstr "一些引用不能读取"
+
+#: builtin/branch.c:613
+msgid "cannot rename the current branch while not on any."
+msgstr "无法重命名当前分支因为不处于任何分支上。"
+
+#: builtin/branch.c:623
+#, c-format
+msgid "Invalid branch name: '%s'"
+msgstr "无效的分支名:'%s'"
+
+#: builtin/branch.c:638
+msgid "Branch rename failed"
+msgstr "分支重命名失败"
+
+#: builtin/branch.c:642
+#, c-format
+msgid "Renamed a misnamed branch '%s' away"
+msgstr "重命名掉一个错误命名的旧分支 '%s'"
+
+#: builtin/branch.c:646
+#, c-format
+msgid "Branch renamed to %s, but HEAD is not updated!"
+msgstr "分支重命名为 %s,但 HEAD 没有更新!"
+
+#: builtin/branch.c:653
+msgid "Branch is renamed, but update of config-file failed"
+msgstr "分支被重命名,但更新 config 文件失败"
+
+#: builtin/branch.c:668
+#, c-format
+msgid "malformed object name %s"
+msgstr "非法的对象名 %s"
+
+#: builtin/branch.c:692
+#, c-format
+msgid "could not write branch description template: %s"
+msgstr "不能写分支描述模版:%s"
+
+#: builtin/branch.c:783
+msgid "Failed to resolve HEAD as a valid ref."
+msgstr "无法将 HEAD 解析为有效引用。"
+
+#: builtin/branch.c:788 builtin/clone.c:558
+msgid "HEAD not found below refs/heads!"
+msgstr "HEAD 没有位于 /refs/heads 之下!"
+
+#: builtin/branch.c:808
+msgid "--column and --verbose are incompatible"
+msgstr "--column 和 --verbose 不兼容"
+
+#: builtin/branch.c:857
+msgid "-a and -r options to 'git branch' do not make sense with a branch name"
+msgstr "'git branch' 的 -a 和 -r 选项带一个分支名参数没有意义"
+
+#: builtin/bundle.c:47
+#, c-format
+msgid "%s is okay\n"
+msgstr "%s 可以\n"
+
+#: builtin/bundle.c:56
+msgid "Need a repository to create a bundle."
+msgstr "需要一个版本库来创建包。"
+
+#: builtin/bundle.c:60
+msgid "Need a repository to unbundle."
+msgstr "需要一个版本库来解包。"
+
+#: builtin/checkout.c:113 builtin/checkout.c:146
+#, c-format
+msgid "path '%s' does not have our version"
+msgstr "路径 '%s' 没有我们的版本"
+
+#: builtin/checkout.c:115 builtin/checkout.c:148
+#, c-format
+msgid "path '%s' does not have their version"
+msgstr "路径 '%s' 没有他们的版本"
+
+#: builtin/checkout.c:131
+#, c-format
+msgid "path '%s' does not have all necessary versions"
+msgstr "路径 '%s' 没有全部必须的版本"
+
+#: builtin/checkout.c:175
+#, c-format
+msgid "path '%s' does not have necessary versions"
+msgstr "路径 '%s' 没有必须的版本"
+
+#: builtin/checkout.c:192
+#, c-format
+msgid "path '%s': cannot merge"
+msgstr "path '%s':无法合并"
+
+#: builtin/checkout.c:209
+#, c-format
+msgid "Unable to add merge result for '%s'"
+msgstr "无法为 '%s' 添加合并结果"
+
+#: builtin/checkout.c:234 builtin/checkout.c:392
+msgid "corrupt index file"
+msgstr "损坏的索引文件"
+
+#: builtin/checkout.c:264 builtin/checkout.c:271
+#, c-format
+msgid "path '%s' is unmerged"
+msgstr "路径 '%s' 未合并"
+
+#: builtin/checkout.c:302 builtin/checkout.c:498 builtin/clone.c:583
+#: builtin/merge.c:812
+msgid "unable to write new index file"
+msgstr "无法写新的索引文件"
+
+#: builtin/checkout.c:319 builtin/diff.c:302 builtin/merge.c:408
+msgid "diff_setup_done failed"
+msgstr "diff_setup_done 失败"
+
+#: builtin/checkout.c:414
+msgid "you need to resolve your current index first"
+msgstr "您需要先解决当前索引的冲突"
+
+#: builtin/checkout.c:533
+#, c-format
+msgid "Can not do reflog for '%s'\n"
+msgstr "不能对 '%s' 执行 reflog 操作\n"
+
+#: builtin/checkout.c:566
+msgid "HEAD is now at"
+msgstr "HEAD 目前位于"
+
+#: builtin/checkout.c:573
+#, c-format
+msgid "Reset branch '%s'\n"
+msgstr "重置分支 '%s'\n"
+
+#: builtin/checkout.c:576
+#, c-format
+msgid "Already on '%s'\n"
+msgstr "已经位于 '%s'\n"
+
+#: builtin/checkout.c:580
+#, c-format
+msgid "Switched to and reset branch '%s'\n"
+msgstr "切换并重置分支 '%s'\n"
+
+#: builtin/checkout.c:582
+#, c-format
+msgid "Switched to a new branch '%s'\n"
+msgstr "切换到一个新分支 '%s'\n"
+
+#: builtin/checkout.c:584
+#, c-format
+msgid "Switched to branch '%s'\n"
+msgstr "切换到分支 '%s'\n"
+
+#  译者:注意保持前导空格
+#: builtin/checkout.c:640
+#, c-format
+msgid " ... and %d more.\n"
+msgstr " ... 及其它 %d 个。\n"
+
+#. The singular version
+#: builtin/checkout.c:646
+#, c-format
+msgid ""
+"Warning: you are leaving %d commit behind, not connected to\n"
+"any of your branches:\n"
+"\n"
+"%s\n"
+msgid_plural ""
+"Warning: you are leaving %d commits behind, not connected to\n"
+"any of your branches:\n"
+"\n"
+"%s\n"
+msgstr[0] ""
+"警告:您正丢下 %d 个提交,未和任何分支关联:\n"
+"\n"
+"%s\n"
+msgstr[1] ""
+"警告:您正丢下 %d 个提交,未和任何分支关联:\n"
+"\n"
+"%s\n"
+
+#: builtin/checkout.c:664
+#, c-format
+msgid ""
+"If you want to keep them by creating a new branch, this may be a good time\n"
+"to do so with:\n"
+"\n"
+" git branch new_branch_name %s\n"
+"\n"
+msgstr ""
+"如果您想要通过创建新分支保存他们,这可能是一个好时候。\n"
+"如下操作:\n"
+"\n"
+" git branch new_branch_name %s\n"
+"\n"
+
+#: builtin/checkout.c:694
+msgid "internal error in revision walk"
+msgstr "在版本遍历时遇到内部错误"
+
+#: builtin/checkout.c:698
+msgid "Previous HEAD position was"
+msgstr "之前的 HEAD 位置是"
+
+#: builtin/checkout.c:724
+msgid "You are on a branch yet to be born"
+msgstr "您位于一个尚未初始化的分支"
+
+#. case (1)
+#: builtin/checkout.c:855
+#, c-format
+msgid "invalid reference: %s"
+msgstr "无效引用:%s"
+
+#. case (1): want a tree
+#: builtin/checkout.c:894
+#, c-format
+msgid "reference is not a tree: %s"
+msgstr "引用不是一个树:%s"
+
+#: builtin/checkout.c:974
+msgid "-B cannot be used with -b"
+msgstr "-B 不能和 -b 共用"
+
+#: builtin/checkout.c:983
+msgid "--patch is incompatible with all other options"
+msgstr "--patch 选项和其他选项不兼容"
+
+#: builtin/checkout.c:986
+msgid "--detach cannot be used with -b/-B/--orphan"
+msgstr "--detach 不能和 -b/-B/--orphan 共用"
+
+#: builtin/checkout.c:988
+msgid "--detach cannot be used with -t"
+msgstr "--detach 不能和 -t 共用"
+
+#: builtin/checkout.c:994
+msgid "--track needs a branch name"
+msgstr "--track 需要一个分支名"
+
+#: builtin/checkout.c:1001
+msgid "Missing branch name; try -b"
+msgstr "缺少分支名;尝试 -b"
+
+#: builtin/checkout.c:1007
+msgid "--orphan and -b|-B are mutually exclusive"
+msgstr "--orphan 和 -b|-B 互斥"
+
+#: builtin/checkout.c:1009
+msgid "--orphan cannot be used with -t"
+msgstr "--orphan 不能和 -t 共用"
+
+#: builtin/checkout.c:1019
+msgid "git checkout: -f and -m are incompatible"
+msgstr "git checkout:-f 和 -m 不兼容"
+
+#: builtin/checkout.c:1053
+msgid "invalid path specification"
+msgstr "无效的路径规格"
+
+#: builtin/checkout.c:1061
+#, c-format
+msgid ""
+"git checkout: updating paths is incompatible with switching branches.\n"
+"Did you intend to checkout '%s' which can not be resolved as commit?"
+msgstr ""
+"git checkout:更新路径和切换分支不兼容。\n"
+"您是想要检出 '%s' 但未能将其解析为提交么?"
+
+#: builtin/checkout.c:1063
+msgid "git checkout: updating paths is incompatible with switching branches."
+msgstr "git checkout:更新路径和切换分支不兼容。"
+
+#: builtin/checkout.c:1068
+msgid "git checkout: --detach does not take a path argument"
+msgstr "git checkout:--detach 不跟路径参数"
+
+#: builtin/checkout.c:1071
+msgid ""
+"git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
+"checking out of the index."
+msgstr ""
+"git checkout:在从索引检出时,--ours/--theirs、--force 和 --merge 不兼容。"
+
+#: builtin/checkout.c:1090
+msgid "Cannot switch branch to a non-commit."
+msgstr "无法切换分支到一个非提交。"
+
+#: builtin/checkout.c:1093
+msgid "--ours/--theirs is incompatible with switching branches."
+msgstr "--ours/--theirs 和切换分支不兼容。"
+
+#: builtin/clean.c:78
+msgid "-x and -X cannot be used together"
+msgstr "-x 和 -X 不能共用"
+
+#: builtin/clean.c:82
+msgid ""
+"clean.requireForce set to true and neither -n nor -f given; refusing to clean"
+msgstr ""
+"clean.requireForce 设置为 true 且未提供 -n 或 -f 选项,拒绝执行清理动作"
+
+#: builtin/clean.c:85
+msgid ""
+"clean.requireForce defaults to true and neither -n nor -f given; refusing to "
+"clean"
+msgstr ""
+"clean.requireForce 默认为 true 且未提供 -n 或 -f 选项,拒绝执行清理动作"
+
+#: builtin/clean.c:155 builtin/clean.c:176
+#, c-format
+msgid "Would remove %s\n"
+msgstr "将删除 %s\n"
+
+#: builtin/clean.c:159 builtin/clean.c:179
+#, c-format
+msgid "Removing %s\n"
+msgstr "正删除 %s\n"
+
+#: builtin/clean.c:162 builtin/clean.c:182
+#, c-format
+msgid "failed to remove %s"
+msgstr "无法删除 %s"
+
+#: builtin/clean.c:166
+#, c-format
+msgid "Would not remove %s\n"
+msgstr "不会删除 %s\n"
+
+#: builtin/clean.c:168
+#, c-format
+msgid "Not removing %s\n"
+msgstr "未删除 %s\n"
+
+#: builtin/clone.c:243
+#, c-format
+msgid "reference repository '%s' is not a local directory."
+msgstr "引用版本库 '%s' 不是一个本地目录。"
+
+#: builtin/clone.c:302
+#, c-format
+msgid "failed to open '%s'"
+msgstr "无法打开 '%s'"
+
+#: builtin/clone.c:306
+#, c-format
+msgid "failed to create directory '%s'"
+msgstr "无法创建目录 '%s'"
+
+#: builtin/clone.c:308 builtin/diff.c:75
+#, c-format
+msgid "failed to stat '%s'"
+msgstr "无法枚举 '%s' 状态"
+
+#: builtin/clone.c:310
+#, c-format
+msgid "%s exists and is not a directory"
+msgstr "%s 存在且不是一个目录"
+
+#: builtin/clone.c:324
+#, c-format
+msgid "failed to stat %s\n"
+msgstr "无法枚举 %s 状态\n"
+
+#: builtin/clone.c:341
+#, c-format
+msgid "failed to unlink '%s'"
+msgstr "无法删除 '%s'"
+
+#: builtin/clone.c:346
+#, c-format
+msgid "failed to create link '%s'"
+msgstr "无法创建链接 '%s'"
+
+#: builtin/clone.c:350
+#, c-format
+msgid "failed to copy file to '%s'"
+msgstr "无法拷贝文件至 '%s'"
+
+#: builtin/clone.c:373
+#, c-format
+msgid "done.\n"
+msgstr "完成。\n"
+
+#: builtin/clone.c:440
+#, c-format
+msgid "Could not find remote branch %s to clone."
+msgstr "不能发现要克隆的远程分支 %s。"
+
+#: builtin/clone.c:549
+msgid "remote HEAD refers to nonexistent ref, unable to checkout.\n"
+msgstr "远程 HEAD 指向一个不存在的引用,无法检出。\n"
+
+#: builtin/clone.c:639
+msgid "Too many arguments."
+msgstr "太多参数。"
+
+#: builtin/clone.c:643
+msgid "You must specify a repository to clone."
+msgstr "您必须指定一个版本库来克隆。"
+
+#: builtin/clone.c:654
+#, c-format
+msgid "--bare and --origin %s options are incompatible."
+msgstr "--bare 和 --origin %s 选项不兼容。"
+
+#: builtin/clone.c:668
+#, c-format
+msgid "repository '%s' does not exist"
+msgstr "版本库 '%s' 不存在"
+
+#: builtin/clone.c:673
+msgid "--depth is ignored in local clones; use file:// instead."
+msgstr "--depth 在本地克隆被忽略,改为 file:// 协议试试。"
+
+#: builtin/clone.c:683
+#, c-format
+msgid "destination path '%s' already exists and is not an empty directory."
+msgstr "目标路径 '%s' 已经存在,并且不是一个空目录。"
+
+#: builtin/clone.c:693
+#, c-format
+msgid "working tree '%s' already exists."
+msgstr "工作区 '%s' 已经存在。"
+
+#: builtin/clone.c:706 builtin/clone.c:720
+#, c-format
+msgid "could not create leading directories of '%s'"
+msgstr "不能为 '%s' 创建先导目录"
+
+#: builtin/clone.c:709
+#, c-format
+msgid "could not create work tree dir '%s'."
+msgstr "不能为 '%s' 创建工作区目录。"
+
+#: builtin/clone.c:728
+#, c-format
+msgid "Cloning into bare repository '%s'...\n"
+msgstr "克隆到裸版本库 '%s'...\n"
+
+#: builtin/clone.c:730
+#, c-format
+msgid "Cloning into '%s'...\n"
+msgstr "正克隆到 '%s'...\n"
+
+#: builtin/clone.c:786
+#, c-format
+msgid "Don't know how to clone %s"
+msgstr "不知道如何克隆 %s"
+
+#: builtin/clone.c:835
+#, c-format
+msgid "Remote branch %s not found in upstream %s"
+msgstr "远程分支 %s 在上游 %s 未发现"
+
+#: builtin/clone.c:842
+msgid "You appear to have cloned an empty repository."
+msgstr "您似乎克隆了一个空版本库。"
+
+#: builtin/column.c:51
+msgid "--command must be the first argument"
+msgstr "--command 必须是第一个参数"
+
+#: builtin/commit.c:43
+msgid ""
+"Your name and email address were configured automatically based\n"
+"on your username and hostname. Please check that they are accurate.\n"
+"You can suppress this message by setting them explicitly:\n"
+"\n"
+"    git config --global user.name \"Your Name\"\n"
+"    git config --global user.email you@example.com\n"
+"\n"
+"After doing this, you may fix the identity used for this commit with:\n"
+"\n"
+"    git commit --amend --reset-author\n"
+msgstr ""
+"您的姓名和邮件地址基于登录名和主机名进行了自动设置。请检查它们正确\n"
+"与否。您可以通过下面的命令对其进行明确地设置以免再出现本提示信息:\n"
+"\n"
+"    git config --global user.name \"Your Name\"\n"
+"    git config --global user.email you@example.com\n"
+"\n"
+"设置完毕后,您可以用下面的命令来修正本次提交所使用的用户身份:\n"
+"\n"
+"    git commit --amend --reset-author\n"
+
+#: builtin/commit.c:55
+msgid ""
+"You asked to amend the most recent commit, but doing so would make\n"
+"it empty. You can repeat your command with --allow-empty, or you can\n"
+"remove the commit entirely with \"git reset HEAD^\".\n"
+msgstr ""
+"您要修补最近的提交,但这么做会让它成为空提交。您可以重复您的命令并带上\n"
+"--allow-empty 选项,或者您可用命令 \"git reset HEAD^\" 整个删除该提交。\n"
+
+#: builtin/commit.c:60
+msgid ""
+"The previous cherry-pick is now empty, possibly due to conflict resolution.\n"
+"If you wish to commit it anyway, use:\n"
+"\n"
+"    git commit --allow-empty\n"
+"\n"
+"Otherwise, please use 'git reset'\n"
+msgstr ""
+"之前的拣选操作现在是一个空提交,可能是由冲突解决导致的。如果您无论如何\n"
+"也要提交,使用命令:\n"
+"\n"
+"    git commit --allow-empty\n"
+"\n"
+"否则,请使用命令 'git reset'\n"
+
+#: builtin/commit.c:253
+msgid "failed to unpack HEAD tree object"
+msgstr "无法解包 HEAD 树对象"
+
+#: builtin/commit.c:295
+msgid "unable to create temporary index"
+msgstr "不能创建临时索引"
+
+#: builtin/commit.c:301
+msgid "interactive add failed"
+msgstr "交互式添加失败"
+
+#: builtin/commit.c:334 builtin/commit.c:355 builtin/commit.c:405
+msgid "unable to write new_index file"
+msgstr "无法写 new_index 文件"
+
+#: builtin/commit.c:386
+msgid "cannot do a partial commit during a merge."
+msgstr "在合并过程中不能做部分提交。"
+
+#: builtin/commit.c:388
+msgid "cannot do a partial commit during a cherry-pick."
+msgstr "在拣选过程中不能做部分提交。"
+
+#: builtin/commit.c:398
+msgid "cannot read the index"
+msgstr "无法读取索引"
+
+#: builtin/commit.c:418
+msgid "unable to write temporary index file"
+msgstr "无法写临时索引文件"
+
+#: builtin/commit.c:493 builtin/commit.c:499
+#, c-format
+msgid "invalid commit: %s"
+msgstr "无效的提交:%s"
+
+#: builtin/commit.c:522
+msgid "malformed --author parameter"
+msgstr "非法的 --author 参数"
+
+#: builtin/commit.c:582
+#, c-format
+msgid "Malformed ident string: '%s'"
+msgstr "非法的身份字符串:'%s'"
+
+#: builtin/commit.c:620 builtin/commit.c:653 builtin/commit.c:967
+#, c-format
+msgid "could not lookup commit %s"
+msgstr "不能查询提交 %s"
+
+#: builtin/commit.c:632 builtin/shortlog.c:296
+#, c-format
+msgid "(reading log message from standard input)\n"
+msgstr "(正从标准输入中读取日志信息)\n"
+
+#: builtin/commit.c:634
+msgid "could not read log from standard input"
+msgstr "不能从标准输入中读取日志信息"
+
+#: builtin/commit.c:638
+#, c-format
+msgid "could not read log file '%s'"
+msgstr "不能读取日志文件 '%s'"
+
+#: builtin/commit.c:644
+msgid "commit has empty message"
+msgstr "提交说明为空"
+
+#: builtin/commit.c:660
+msgid "could not read MERGE_MSG"
+msgstr "不能读取 MERGE_MSG"
+
+#: builtin/commit.c:664
+msgid "could not read SQUASH_MSG"
+msgstr "不能读取 SQUASH_MSG"
+
+#: builtin/commit.c:668
+#, c-format
+msgid "could not read '%s'"
+msgstr "不能读取 '%s'"
+
+#: builtin/commit.c:720
+msgid "could not write commit template"
+msgstr "不能写提交模版"
+
+#: builtin/commit.c:731
+#, c-format
+msgid ""
+"\n"
+"It looks like you may be committing a merge.\n"
+"If this is not correct, please remove the file\n"
+"\t%s\n"
+"and try again.\n"
+msgstr ""
+"\n"
+"看起来您正在做一个合并提交。如果不对,请删除文件\n"
+"\t%s\n"
+"然后重试。\n"
+
+#: builtin/commit.c:736
+#, c-format
+msgid ""
+"\n"
+"It looks like you may be committing a cherry-pick.\n"
+"If this is not correct, please remove the file\n"
+"\t%s\n"
+"and try again.\n"
+msgstr ""
+"\n"
+"看起来您正在做一个拣选提交。如果不对,请删除文件\n"
+"\t%s\n"
+"然后重试。\n"
+
+#: builtin/commit.c:748
+msgid ""
+"Please enter the commit message for your changes. Lines starting\n"
+"with '#' will be ignored, and an empty message aborts the commit.\n"
+msgstr ""
+"请为您的变更输入提交说明。以 '#' 开始的行将被忽略,而一个空的提交\n"
+"说明将会终止提交。\n"
+
+#: builtin/commit.c:753
+msgid ""
+"Please enter the commit message for your changes. Lines starting\n"
+"with '#' will be kept; you may remove them yourself if you want to.\n"
+"An empty message aborts the commit.\n"
+msgstr ""
+"请为您的变更输入提交说明。以 '#' 开始的行将被保留,您可以删除它们\n"
+"如果您想这样做的话。而一个空的提交说明将会终止提交。\n"
+
+#  译者:为保证在输出中对齐,注意调整句中空格!
+#: builtin/commit.c:766
+#, c-format
+msgid "%sAuthor:    %s"
+msgstr "%s作者:     %s"
+
+#  译者:为保证在输出中对齐,注意调整句中空格!
+#: builtin/commit.c:773
+#, c-format
+msgid "%sCommitter: %s"
+msgstr "%s提交者:   %s"
+
+#: builtin/commit.c:793
+msgid "Cannot read index"
+msgstr "无法读取索引"
+
+#: builtin/commit.c:830
+msgid "Error building trees"
+msgstr "无法创建树对象"
+
+#: builtin/commit.c:845 builtin/tag.c:361
+#, c-format
+msgid "Please supply the message using either -m or -F option.\n"
+msgstr "请使用 -m 或者 -F 选项提供提交说明。\n"
+
+#: builtin/commit.c:942
+#, c-format
+msgid "No existing author found with '%s'"
+msgstr "没有找到匹配 '%s' 的作者"
+
+#: builtin/commit.c:957 builtin/commit.c:1157
+#, c-format
+msgid "Invalid untracked files mode '%s'"
+msgstr "无效的未追踪文件参数 '%s'"
+
+#: builtin/commit.c:997
+msgid "Using both --reset-author and --author does not make sense"
+msgstr "同时使用 --reset-author 和 --author 没有意义"
+
+#: builtin/commit.c:1008
+msgid "You have nothing to amend."
+msgstr "您没有可修补的提交。"
+
+#: builtin/commit.c:1011
+msgid "You are in the middle of a merge -- cannot amend."
+msgstr "您正处于一个合并过程中 -- 无法修补提交。"
+
+#: builtin/commit.c:1013
+msgid "You are in the middle of a cherry-pick -- cannot amend."
+msgstr "您正处于一个拣选过程中 -- 无法修补提交。"
+
+#: builtin/commit.c:1016
+msgid "Options --squash and --fixup cannot be used together"
+msgstr "选项 --squash 和 --fixup 不能共用"
+
+#: builtin/commit.c:1026
+msgid "Only one of -c/-C/-F/--fixup can be used."
+msgstr "只能用一个 -c/-C/-F/--fixup 选项。"
+
+#: builtin/commit.c:1028
+msgid "Option -m cannot be combined with -c/-C/-F/--fixup."
+msgstr "选项 -m 不能和 -c/-C/-F/--fixup 共用。"
+
+#: builtin/commit.c:1036
+msgid "--reset-author can be used only with -C, -c or --amend."
+msgstr "--reset-author 只能和 -C、-c 或 --amend 共用。"
+
+#: builtin/commit.c:1053
+msgid "Only one of --include/--only/--all/--interactive/--patch can be used."
+msgstr "只能用一个 --include/--only/--all/--interactive/--patch 选项。"
+
+#: builtin/commit.c:1055
+msgid "No paths with --include/--only does not make sense."
+msgstr "参数 --include/--only 不跟路径没有意义。"
+
+#: builtin/commit.c:1057
+msgid "Clever... amending the last one with dirty index."
+msgstr "聪明... 在索引不干净下修补最后的提交。"
+
+#: builtin/commit.c:1059
+msgid "Explicit paths specified without -i nor -o; assuming --only paths..."
+msgstr "指定了明确的路径而没有使用 -i 或 -o 选项;认为是 --only paths..."
+
+#: builtin/commit.c:1069 builtin/tag.c:577
+#, c-format
+msgid "Invalid cleanup mode %s"
+msgstr "无效的清理模式 %s"
+
+#: builtin/commit.c:1074
+msgid "Paths with -a does not make sense."
+msgstr "路径和 -a 选项共用没有意义。"
+
+#: builtin/commit.c:1257
+msgid "couldn't look up newly created commit"
+msgstr "无法找到新创建的提交"
+
+#: builtin/commit.c:1259
+msgid "could not parse newly created commit"
+msgstr "不能解析新创建的提交"
+
+#: builtin/commit.c:1300
+msgid "detached HEAD"
+msgstr "分离头指针"
+
+#  译者:中文字符串拼接,可删除前导空格
+#: builtin/commit.c:1302
+msgid " (root-commit)"
+msgstr "(根提交)"
+
+#: builtin/commit.c:1446
+msgid "could not parse HEAD commit"
+msgstr "不能解析 HEAD 提交"
+
+#: builtin/commit.c:1484 builtin/merge.c:509
+#, c-format
+msgid "could not open '%s' for reading"
+msgstr "不能为读入打开 '%s'"
+
+#: builtin/commit.c:1491
+#, c-format
+msgid "Corrupt MERGE_HEAD file (%s)"
+msgstr "损坏的 MERGE_HEAD 文件(%s)"
+
+#: builtin/commit.c:1498
+msgid "could not read MERGE_MODE"
+msgstr "不能读取 MERGE_MODE"
+
+#: builtin/commit.c:1517
+#, c-format
+msgid "could not read commit message: %s"
+msgstr "不能读取提交说明:%s"
+
+#: builtin/commit.c:1531
+#, c-format
+msgid "Aborting commit; you did not edit the message.\n"
+msgstr "终止提交;您未更改来自模版的提交说明。\n"
+
+#: builtin/commit.c:1536
+#, c-format
+msgid "Aborting commit due to empty commit message.\n"
+msgstr "终止提交因为提交说明为空。\n"
+
+#: builtin/commit.c:1551 builtin/merge.c:936 builtin/merge.c:961
+msgid "failed to write commit object"
+msgstr "无法写提交对象"
+
+#: builtin/commit.c:1572
+msgid "cannot lock HEAD ref"
+msgstr "无法锁定 HEAD 引用"
+
+#: builtin/commit.c:1576
+msgid "cannot update HEAD ref"
+msgstr "无法更新 HEAD 引用"
+
+#: builtin/commit.c:1587
+msgid ""
+"Repository has been updated, but unable to write\n"
+"new_index file. Check that disk is not full or quota is\n"
+"not exceeded, and then \"git reset HEAD\" to recover."
+msgstr ""
+"版本库已更新,但无法写 new_index 文件。检查是否磁盘已满\n"
+"或磁盘配额已耗尽,然后执行 \"git reset HEAD\" 恢复。"
+
+#: builtin/describe.c:234
+#, c-format
+msgid "annotated tag %s not available"
+msgstr "注释 tag %s 无效"
+
+#: builtin/describe.c:238
+#, c-format
+msgid "annotated tag %s has no embedded name"
+msgstr "注释 tag %s 没有嵌入名称"
+
+#: builtin/describe.c:240
+#, c-format
+msgid "tag '%s' is really '%s' here"
+msgstr "tag '%s' 的确是在 '%s'"
+
+#: builtin/describe.c:267
+#, c-format
+msgid "Not a valid object name %s"
+msgstr "不是一个有效的对象名 %s"
+
+#: builtin/describe.c:270
+#, c-format
+msgid "%s is not a valid '%s' object"
+msgstr "%s 不是一个有效的 '%s' 对象"
+
+#: builtin/describe.c:287
+#, c-format
+msgid "no tag exactly matches '%s'"
+msgstr "没有 tag 准确匹配 '%s'"
+
+#: builtin/describe.c:289
+#, c-format
+msgid "searching to describe %s\n"
+msgstr "搜索描述 %s\n"
+
+#: builtin/describe.c:329
+#, c-format
+msgid "finished search at %s\n"
+msgstr "完成搜索 %s\n"
+
+#: builtin/describe.c:353
+#, c-format
+msgid ""
+"No annotated tags can describe '%s'.\n"
+"However, there were unannotated tags: try --tags."
+msgstr ""
+"没有注释 tag 能描述 '%s'。\n"
+"然而,有非注释 tag:尝试 --tags。"
+
+#: builtin/describe.c:357
+#, c-format
+msgid ""
+"No tags can describe '%s'.\n"
+"Try --always, or create some tags."
+msgstr ""
+"没有注释 tag 能描述 '%s'。\n"
+"尝试 --always,或者创建一些 tag。"
+
+#: builtin/describe.c:378
+#, c-format
+msgid "traversed %lu commits\n"
+msgstr "已遍历 %lu 个提交\n"
+
+#: builtin/describe.c:381
+#, c-format
+msgid ""
+"more than %i tags found; listed %i most recent\n"
+"gave up search at %s\n"
+msgstr ""
+"发现多于 %i 个 tag,列出最近的 %i 个\n"
+"在 %s 放弃搜索\n"
+
+#: builtin/describe.c:436
+msgid "--long is incompatible with --abbrev=0"
+msgstr "--long 与 --abbrev=0 不兼容"
+
+#: builtin/describe.c:462
+msgid "No names found, cannot describe anything."
+msgstr "没有发现名称,无法描述任何东西。"
+
+#: builtin/describe.c:482
+msgid "--dirty is incompatible with committishes"
+msgstr "--dirty 不能与提交共用"
+
+#: builtin/diff.c:77
+#, c-format
+msgid "'%s': not a regular file or symlink"
+msgstr "'%s':不是一个正规文件或符号链接"
+
+#: builtin/diff.c:220
+#, c-format
+msgid "invalid option: %s"
+msgstr "无效选项:%s"
+
+#: builtin/diff.c:297
+msgid "Not a git repository"
+msgstr "不是一个 git 版本库"
+
+#: builtin/diff.c:341
+#, c-format
+msgid "invalid object '%s' given."
+msgstr "提供了无效对象 '%s'。"
+
+#: builtin/diff.c:346
+#, c-format
+msgid "more than %d trees given: '%s'"
+msgstr "提供了超过 %d 个树对象:'%s'"
+
+#: builtin/diff.c:356
+#, c-format
+msgid "more than two blobs given: '%s'"
+msgstr "提供了超过两个 blob 对象:'%s'"
+
+#: builtin/diff.c:364
+#, c-format
+msgid "unhandled object '%s' given."
+msgstr "提供了无法处理的对象 '%s'。"
+
+#: builtin/fetch.c:200
+msgid "Couldn't find remote ref HEAD"
+msgstr "无法发现远程 HEAD 引用"
+
+#: builtin/fetch.c:253
+#, c-format
+msgid "object %s not found"
+msgstr "对象 %s 未发现"
+
+#: builtin/fetch.c:259
+msgid "[up to date]"
+msgstr "[最新]"
+
+#: builtin/fetch.c:273
+#, c-format
+msgid "! %-*s %-*s -> %s  (can't fetch in current branch)"
+msgstr "! %-*s %-*s -> %s  (在当前分支下不能获取)"
+
+#: builtin/fetch.c:274 builtin/fetch.c:360
+msgid "[rejected]"
+msgstr "[已拒绝]"
+
+#: builtin/fetch.c:285
+msgid "[tag update]"
+msgstr "[tag更新]"
+
+#  译者:注意保持前导空格
+#: builtin/fetch.c:287 builtin/fetch.c:322 builtin/fetch.c:340
+msgid "  (unable to update local ref)"
+msgstr "  (不能更新本地引用)"
+
+#: builtin/fetch.c:305
+msgid "[new tag]"
+msgstr "[新tag]"
+
+#: builtin/fetch.c:308
+msgid "[new branch]"
+msgstr "[新分支]"
+
+#: builtin/fetch.c:311
+msgid "[new ref]"
+msgstr "[新引用]"
+
+#: builtin/fetch.c:356
+msgid "unable to update local ref"
+msgstr "不能更新本地引用"
+
+#: builtin/fetch.c:356
+msgid "forced update"
+msgstr "强制更新"
+
+#: builtin/fetch.c:362
+msgid "(non-fast-forward)"
+msgstr "(非快进式)"
+
+#: builtin/fetch.c:393 builtin/fetch.c:685
+#, c-format
+msgid "cannot open %s: %s\n"
+msgstr "无法打开 %s:%s\n"
+
+#: builtin/fetch.c:402
+#, c-format
+msgid "%s did not send all necessary objects\n"
+msgstr "%s 未发送所有必须的对象\n"
+
+#: builtin/fetch.c:488
+#, c-format
+msgid "From %.*s\n"
+msgstr "来自 %.*s\n"
+
+#: builtin/fetch.c:499
+#, c-format
+msgid ""
+"some local refs could not be updated; try running\n"
+" 'git remote prune %s' to remove any old, conflicting branches"
+msgstr ""
+"一些本地引用不能被更新;尝试运行\n"
+" 'git remote prune %s' 来删除旧的、有冲突的分支"
+
+#  译者:注意保持前导空格
+#: builtin/fetch.c:549
+#, c-format
+msgid "   (%s will become dangling)"
+msgstr "   (%s 将成为悬空状态)"
+
+#  译者:注意保持前导空格
+#: builtin/fetch.c:550
+#, c-format
+msgid "   (%s has become dangling)"
+msgstr "   (%s 已成为悬空状态)"
+
+#: builtin/fetch.c:557
+msgid "[deleted]"
+msgstr "[已删除]"
+
+#: builtin/fetch.c:558 builtin/remote.c:1055
+msgid "(none)"
+msgstr "(无)"
+
+#: builtin/fetch.c:675
+#, c-format
+msgid "Refusing to fetch into current branch %s of non-bare repository"
+msgstr "拒绝获取到非裸版本库的当前分支 %s"
+
+#: builtin/fetch.c:709
+#, c-format
+msgid "Don't know how to fetch from %s"
+msgstr "不知道如何从 %s 获取"
+
+#: builtin/fetch.c:786
+#, c-format
+msgid "Option \"%s\" value \"%s\" is not valid for %s"
+msgstr "选项 \"%s\" 的值 \"%s\" 对于 %s 是无效的"
+
+#: builtin/fetch.c:789
+#, c-format
+msgid "Option \"%s\" is ignored for %s\n"
+msgstr "选项 \"%s\" 为 %s 所忽略\n"
+
+#: builtin/fetch.c:888
+#, c-format
+msgid "Fetching %s\n"
+msgstr "正在获取 %s\n"
+
+#: builtin/fetch.c:890 builtin/remote.c:100
+#, c-format
+msgid "Could not fetch %s"
+msgstr "不能获取 %s"
+
+#: builtin/fetch.c:907
+msgid ""
+"No remote repository specified.  Please, specify either a URL or a\n"
+"remote name from which new revisions should be fetched."
+msgstr "未指定远程版本库。请通过一个URL或远程版本库名指定,用以获取新提交。"
+
+#: builtin/fetch.c:927
+msgid "You need to specify a tag name."
+msgstr "您需要指定一个 tag 名称。"
+
+#: builtin/fetch.c:979
+msgid "fetch --all does not take a repository argument"
+msgstr "fetch --all 不能带一个版本库参数"
+
+#: builtin/fetch.c:981
+msgid "fetch --all does not make sense with refspecs"
+msgstr "fetch --all 带引用表达式没有任何意义"
+
+#: builtin/fetch.c:992
+#, c-format
+msgid "No such remote or remote group: %s"
+msgstr "没有这样的远程或远程组:%s"
+
+#: builtin/fetch.c:1000
+msgid "Fetching a group and specifying refspecs does not make sense"
+msgstr "获取组并指定引用表达式没有意义"
+
+#: builtin/gc.c:63
+#, c-format
+msgid "Invalid %s: '%s'"
+msgstr "无效的 %s:'%s'"
+
+#: builtin/gc.c:90
+#, c-format
+msgid "insanely long object directory %.*s"
+msgstr "不正常的长对象目录 %.*s"
+
+#: builtin/gc.c:221
+#, c-format
+msgid "Auto packing the repository for optimum performance.\n"
+msgstr "自动打包版本库以求最佳性能。\n"
+
+#: builtin/gc.c:224
+#, c-format
+msgid ""
+"Auto packing the repository for optimum performance. You may also\n"
+"run \"git gc\" manually. See \"git help gc\" for more information.\n"
+msgstr ""
+"自动打包版本库以求最佳性能。您还可以手动运行 \"git gc\"。\n"
+"参见 \"git help gc\" 以获取更多信息。\n"
+
+#: builtin/gc.c:251
+msgid ""
+"There are too many unreachable loose objects; run 'git prune' to remove them."
+msgstr "有太多不可达的松散对象,运行 'git prune' 删除它们。"
+
+#: builtin/grep.c:216
+#, c-format
+msgid "grep: failed to create thread: %s"
+msgstr "grep:无法创建线程:%s"
+
+#: builtin/grep.c:402
+#, c-format
+msgid "Failed to chdir: %s"
+msgstr "无法切换目录:%s"
+
+#: builtin/grep.c:478 builtin/grep.c:512
+#, c-format
+msgid "unable to read tree (%s)"
+msgstr "无法读取树(%s)"
+
+#: builtin/grep.c:526
+#, c-format
+msgid "unable to grep from object of type %s"
+msgstr "无法抓取来自于 %s 类型的对象"
+
+#: builtin/grep.c:584
+#, c-format
+msgid "switch `%c' expects a numerical value"
+msgstr "开关 `%c' 期望一个数字值"
+
+#: builtin/grep.c:601
+#, c-format
+msgid "cannot open '%s'"
+msgstr "不能打开 '%s'"
+
+#: builtin/grep.c:885
+msgid "no pattern given."
+msgstr "未提供模式匹配。"
+
+#: builtin/grep.c:899
+#, c-format
+msgid "bad object %s"
+msgstr "坏对象 %s"
+
+#: builtin/grep.c:940
+msgid "--open-files-in-pager only works on the worktree"
+msgstr "--open-files-in-pager 仅用于工作区"
+
+#: builtin/grep.c:963
+msgid "--cached or --untracked cannot be used with --no-index."
+msgstr "--cached 或 --untracked 不能与 --no-index 共用。"
+
+#: builtin/grep.c:968
+msgid "--no-index or --untracked cannot be used with revs."
+msgstr "--no-index 或 --untracked 不能和版本共用。"
+
+#: builtin/grep.c:971
+msgid "--[no-]exclude-standard cannot be used for tracked contents."
+msgstr "--[no-]exclude-standard 不能用于已跟踪内容。"
+
+#: builtin/grep.c:979
+msgid "both --cached and trees are given."
+msgstr "同时给出了 --cached 和树对象。"
+
+#: builtin/help.c:63
+#, c-format
+msgid "unrecognized help format '%s'"
+msgstr "未能识别的帮助格式 '%s'"
+
+#: builtin/help.c:91
+msgid "Failed to start emacsclient."
+msgstr "无法启动 emacsclient。"
+
+#: builtin/help.c:104
+msgid "Failed to parse emacsclient version."
+msgstr "无法解析 emacsclient 版本。"
+
+#: builtin/help.c:112
+#, c-format
+msgid "emacsclient version '%d' too old (< 22)."
+msgstr "emacsclient 版本 '%d' 太老 (< 22)。"
+
+#: builtin/help.c:130 builtin/help.c:158 builtin/help.c:167 builtin/help.c:175
+#, c-format
+msgid "failed to exec '%s': %s"
+msgstr "无法执行 '%s':%s"
+
+#: builtin/help.c:215
+#, c-format
+msgid ""
+"'%s': path for unsupported man viewer.\n"
+"Please consider using 'man.<tool>.cmd' instead."
+msgstr ""
+"'%s':不支持的 man 手册查看器的路径。\n"
+"请使用 'man.<tool>.cmd'。"
+
+#: builtin/help.c:227
+#, c-format
+msgid ""
+"'%s': cmd for supported man viewer.\n"
+"Please consider using 'man.<tool>.path' instead."
+msgstr ""
+"'%s': 支持的 man 手册查看器命令。\n"
+"请使用 'man.<tool>.path'。"
+
+#: builtin/help.c:291
+msgid "The most commonly used git commands are:"
+msgstr "最常用的 git 命令有:"
+
+#: builtin/help.c:359
+#, c-format
+msgid "'%s': unknown man viewer."
+msgstr "'%s':未知的 man 查看器。"
+
+#: builtin/help.c:376
+msgid "no man viewer handled the request"
+msgstr "没有 man 查看器处理此请求"
+
+#: builtin/help.c:384
+msgid "no info viewer handled the request"
+msgstr "没有 info 查看器处理此请求"
+
+#: builtin/help.c:395
+#, c-format
+msgid "'%s': not a documentation directory."
+msgstr "'%s':不是一个文档目录。"
+
+#: builtin/help.c:436 builtin/help.c:443
+#, c-format
+msgid "usage: %s%s"
+msgstr "用法:%s%s"
+
+#: builtin/help.c:459
+#, c-format
+msgid "`git %s' is aliased to `%s'"
+msgstr "`git %s' 是 `%s' 的别名"
+
+#: builtin/index-pack.c:170
+#, c-format
+msgid "object type mismatch at %s"
+msgstr "%s 的对象类型不匹配"
+
+#: builtin/index-pack.c:190
+msgid "object of unexpected type"
+msgstr "意外的类型的对象"
+
+#: builtin/index-pack.c:227
+#, c-format
+msgid "cannot fill %d byte"
+msgid_plural "cannot fill %d bytes"
+msgstr[0] "无法填充 %d 字节"
+msgstr[1] "无法填充 %d 字节"
+
+#: builtin/index-pack.c:237
+msgid "early EOF"
+msgstr "过早的文件结束符(EOF)"
+
+#: builtin/index-pack.c:238
+msgid "read error on input"
+msgstr "输入上的读错误"
+
+#: builtin/index-pack.c:250
+msgid "used more bytes than were available"
+msgstr "用掉了超过可用的字节"
+
+#: builtin/index-pack.c:257
+msgid "pack too large for current definition of off_t"
+msgstr "包太大超过了当前 off_t 的定义"
+
+#: builtin/index-pack.c:273
+#, c-format
+msgid "unable to create '%s'"
+msgstr "不能创建 '%s'"
+
+#: builtin/index-pack.c:278
+#, c-format
+msgid "cannot open packfile '%s'"
+msgstr "无法打开包文件 '%s'"
+
+#: builtin/index-pack.c:292
+msgid "pack signature mismatch"
+msgstr "包签名不匹配"
+
+#: builtin/index-pack.c:312
+#, c-format
+msgid "pack has bad object at offset %lu: %s"
+msgstr "包中有错误的对象位于 %lu:%s"
+
+#: builtin/index-pack.c:434
+#, c-format
+msgid "inflate returned %d"
+msgstr "解压缩返回 %d"
+
+#: builtin/index-pack.c:483
+msgid "offset value overflow for delta base object"
+msgstr "偏移值覆盖了 delta 基准对象"
+
+#: builtin/index-pack.c:491
+msgid "delta base offset is out of bound"
+msgstr "delta 基准偏移越界"
+
+#: builtin/index-pack.c:499
+#, c-format
+msgid "unknown object type %d"
+msgstr "未知对象类型 %d"
+
+#: builtin/index-pack.c:531
+msgid "cannot pread pack file"
+msgstr "无法读取包文件"
+
+#: builtin/index-pack.c:533
+#, c-format
+msgid "premature end of pack file, %lu byte missing"
+msgid_plural "premature end of pack file, %lu bytes missing"
+msgstr[0] "包文件过早结束,缺少 %lu 字节"
+msgstr[1] "包文件过早结束,缺少 %lu 字节"
+
+#: builtin/index-pack.c:555
+msgid "serious inflate inconsistency"
+msgstr "解压缩严重的不一致"
+
+#: builtin/index-pack.c:646 builtin/index-pack.c:652 builtin/index-pack.c:675
+#: builtin/index-pack.c:709 builtin/index-pack.c:718
+#, c-format
+msgid "SHA1 COLLISION FOUND WITH %s !"
+msgstr "发现 %s 出现 SHA1 冲突!"
+
+#: builtin/index-pack.c:649 builtin/pack-objects.c:170
+#: builtin/pack-objects.c:262
+#, c-format
+msgid "unable to read %s"
+msgstr "不能读 %s"
+
+#: builtin/index-pack.c:715
+#, c-format
+msgid "cannot read existing object %s"
+msgstr "不能读取现存对象 %s"
+
+#: builtin/index-pack.c:729
+#, c-format
+msgid "invalid blob object %s"
+msgstr "无效的 blob 对象 %s"
+
+#: builtin/index-pack.c:744
+#, c-format
+msgid "invalid %s"
+msgstr "无效的 %s"
+
+#: builtin/index-pack.c:746
+msgid "Error in object"
+msgstr "对象中出错"
+
+#: builtin/index-pack.c:748
+#, c-format
+msgid "Not all child objects of %s are reachable"
+msgstr "%s 的所有子对象并非都可达"
+
+#: builtin/index-pack.c:818 builtin/index-pack.c:844
+msgid "failed to apply delta"
+msgstr "无法应用 delta"
+
+#: builtin/index-pack.c:983
+msgid "Receiving objects"
+msgstr "接收对象中"
+
+#: builtin/index-pack.c:983
+msgid "Indexing objects"
+msgstr "索引对象中"
+
+#: builtin/index-pack.c:1009
+msgid "pack is corrupted (SHA1 mismatch)"
+msgstr "包冲突(SHA1 不匹配)"
+
+#: builtin/index-pack.c:1014
+msgid "cannot fstat packfile"
+msgstr "不能枚举包文件状态"
+
+#: builtin/index-pack.c:1017
+msgid "pack has junk at the end"
+msgstr "包的结尾有垃圾数据"
+
+#: builtin/index-pack.c:1028
+msgid "confusion beyond insanity in parse_pack_objects()"
+msgstr "parse_pack_objects() 中遇到不可理喻的问题"
+
+#: builtin/index-pack.c:1051
+msgid "Resolving deltas"
+msgstr "处理 delta 中"
+
+#: builtin/index-pack.c:1102
+msgid "confusion beyond insanity"
+msgstr "不可理喻"
+
+#: builtin/index-pack.c:1121
+#, c-format
+msgid "pack has %d unresolved delta"
+msgid_plural "pack has %d unresolved deltas"
+msgstr[0] "包有 %d 个未解决的 delta"
+msgstr[1] "包有 %d 个未解决的 delta"
+
+#: builtin/index-pack.c:1146
+#, c-format
+msgid "unable to deflate appended object (%d)"
+msgstr "不能缩小附加对象(%d)"
+
+#: builtin/index-pack.c:1225
+#, c-format
+msgid "local object %s is corrupt"
+msgstr "本地对象 %s 已损坏"
+
+#: builtin/index-pack.c:1249
+msgid "error while closing pack file"
+msgstr "关闭包文件时出错"
+
+#: builtin/index-pack.c:1262
+#, c-format
+msgid "cannot write keep file '%s'"
+msgstr "无法写保留文件 '%s'"
+
+#: builtin/index-pack.c:1270
+#, c-format
+msgid "cannot close written keep file '%s'"
+msgstr "无法关闭保留文件 '%s'"
+
+#: builtin/index-pack.c:1283
+msgid "cannot store pack file"
+msgstr "无法存储包文件"
+
+#: builtin/index-pack.c:1294
+msgid "cannot store index file"
+msgstr "无法存储索引文件"
+
+#: builtin/index-pack.c:1395
+#, c-format
+msgid "Cannot open existing pack file '%s'"
+msgstr "无法打开现存包文件 '%s'"
+
+#: builtin/index-pack.c:1397
+#, c-format
+msgid "Cannot open existing pack idx file for '%s'"
+msgstr "无法为 %s 打开包索引文件"
+
+#: builtin/index-pack.c:1444
+#, c-format
+msgid "non delta: %d object"
+msgid_plural "non delta: %d objects"
+msgstr[0] "非 delta:%d 个对象"
+msgstr[1] "非 delta:%d 个对象"
+
+#: builtin/index-pack.c:1451
+#, c-format
+msgid "chain length = %d: %lu object"
+msgid_plural "chain length = %d: %lu objects"
+msgstr[0] "链长 = %d: %lu 对象"
+msgstr[1] "链长 = %d: %lu 对象"
+
+#: builtin/index-pack.c:1478
+msgid "Cannot come back to cwd"
+msgstr "无法返回当前工作目录"
+
+#: builtin/index-pack.c:1522 builtin/index-pack.c:1525
+#: builtin/index-pack.c:1537 builtin/index-pack.c:1541
+#, c-format
+msgid "bad %s"
+msgstr "错误选项 %s"
+
+#: builtin/index-pack.c:1555
+msgid "--fix-thin cannot be used without --stdin"
+msgstr "--fix-thin 不能和 --stdin 共用"
+
+#: builtin/index-pack.c:1559 builtin/index-pack.c:1569
+#, c-format
+msgid "packfile name '%s' does not end with '.pack'"
+msgstr "包名 '%s' 没有以 '.pack' 结尾"
+
+#: builtin/index-pack.c:1578
+msgid "--verify with no packfile name given"
+msgstr "--verify 没有提供包名参数"
+
+#: builtin/init-db.c:35
+#, c-format
+msgid "Could not make %s writable by group"
+msgstr "不能设置 %s 为组可写"
+
+#: builtin/init-db.c:62
+#, c-format
+msgid "insanely long template name %s"
+msgstr "太长的模版名 %s"
+
+#: builtin/init-db.c:67
+#, c-format
+msgid "cannot stat '%s'"
+msgstr "不能枚举 '%s' 状态"
+
+#: builtin/init-db.c:73
+#, c-format
+msgid "cannot stat template '%s'"
+msgstr "不能枚举模版 '%s' 状态"
+
+#: builtin/init-db.c:80
+#, c-format
+msgid "cannot opendir '%s'"
+msgstr "不能打开目录 '%s'"
+
+#: builtin/init-db.c:97
+#, c-format
+msgid "cannot readlink '%s'"
+msgstr "不能读取链接 '%s'"
+
+#: builtin/init-db.c:99
+#, c-format
+msgid "insanely long symlink %s"
+msgstr "太长的符号链接 %s"
+
+#: builtin/init-db.c:102
+#, c-format
+msgid "cannot symlink '%s' '%s'"
+msgstr "不能自 '%s' 到 '%s' 创建符号链接"
+
+#: builtin/init-db.c:106
+#, c-format
+msgid "cannot copy '%s' to '%s'"
+msgstr "不能拷贝 '%s' 至 '%s'"
+
+#: builtin/init-db.c:110
+#, c-format
+msgid "ignoring template %s"
+msgstr "忽略模版 %s"
+
+#: builtin/init-db.c:133
+#, c-format
+msgid "insanely long template path %s"
+msgstr "太长的模版路径 %s"
+
+#: builtin/init-db.c:141
+#, c-format
+msgid "templates not found %s"
+msgstr "模版未找到 %s"
+
+#: builtin/init-db.c:154
+#, c-format
+msgid "not copying templates of a wrong format version %d from '%s'"
+msgstr "没有从 '%2$s' 复制带有错误版本 %1$d 的模版"
+
+#: builtin/init-db.c:192
+#, c-format
+msgid "insane git directory %s"
+msgstr "不正常的 git 目录 %s"
+
+#: builtin/init-db.c:322 builtin/init-db.c:325
+#, c-format
+msgid "%s already exists"
+msgstr "%s 已经存在"
+
+#: builtin/init-db.c:354
+#, c-format
+msgid "unable to handle file type %d"
+msgstr "不能处理 %d 类型的文件"
+
+#: builtin/init-db.c:357
+#, c-format
+msgid "unable to move %s to %s"
+msgstr "不能移动 %s 至 %s"
+
+#: builtin/init-db.c:362
+#, c-format
+msgid "Could not create git link %s"
+msgstr "不能创建 git link %s"
+
+#.
+#. * TRANSLATORS: The first '%s' is either "Reinitialized
+#. * existing" or "Initialized empty", the second " shared" or
+#. * "", and the last '%s%s' is the verbatim directory name.
+#.
+#: builtin/init-db.c:419
+#, c-format
+msgid "%s%s Git repository in %s%s\n"
+msgstr "%s%s Git 版本库于 %s%s\n"
+
+#: builtin/init-db.c:420
+msgid "Reinitialized existing"
+msgstr "重新初始化现存的"
+
+#: builtin/init-db.c:420
+msgid "Initialized empty"
+msgstr "初始化空的"
+
+#  译者:中文字符串拼接,可删除前导空格
+#: builtin/init-db.c:421
+msgid " shared"
+msgstr "共享"
+
+#: builtin/init-db.c:440
+msgid "cannot tell cwd"
+msgstr "无法获知当前路径"
+
+#: builtin/init-db.c:521 builtin/init-db.c:528
+#, c-format
+msgid "cannot mkdir %s"
+msgstr "不能创建目录 %s"
+
+#: builtin/init-db.c:532
+#, c-format
+msgid "cannot chdir to %s"
+msgstr "不能切换目录到 %s"
+
+#: builtin/init-db.c:554
+#, c-format
+msgid ""
+"%s (or --work-tree=<directory>) not allowed without specifying %s (or --git-"
+"dir=<directory>)"
+msgstr ""
+"不允许 %s(或 --work-tree=<directory>)而没有指定 %s(或 --git-"
+"dir=<directory>)"
+
+#: builtin/init-db.c:578
+msgid "Cannot access current working directory"
+msgstr "不能访问当前工作目录"
+
+#: builtin/init-db.c:585
+#, c-format
+msgid "Cannot access work tree '%s'"
+msgstr "不能访问工作区 '%s'"
+
+#: builtin/log.c:189
+#, c-format
+msgid "Final output: %d %s\n"
+msgstr "最终输出:%d %s\n"
+
+#: builtin/log.c:402 builtin/log.c:490
+#, c-format
+msgid "Could not read object %s"
+msgstr "不能读取对象 %s"
+
+#: builtin/log.c:514
+#, c-format
+msgid "Unknown type: %d"
+msgstr "未知类型:%d"
+
+#: builtin/log.c:603
+msgid "format.headers without value"
+msgstr "format.headers 没有值"
+
+#: builtin/log.c:677
+msgid "name of output directory is too long"
+msgstr "输出目录名太长"
+
+#: builtin/log.c:688
+#, c-format
+msgid "Cannot open patch file %s"
+msgstr "无法打开补丁文件 %s"
+
+#: builtin/log.c:702
+msgid "Need exactly one range."
+msgstr "只需要一个范围。"
+
+#: builtin/log.c:710
+msgid "Not a range."
+msgstr "不是一个范围。"
+
+#: builtin/log.c:787
+msgid "Cover letter needs email format"
+msgstr "信封需要邮件地址格式"
+
+#: builtin/log.c:860
+#, c-format
+msgid "insane in-reply-to: %s"
+msgstr "不正常的 in-reply-to:%s"
+
+#: builtin/log.c:933
+msgid "Two output directories?"
+msgstr "两个输出目录?"
+
+#: builtin/log.c:1154
+#, c-format
+msgid "bogus committer info %s"
+msgstr "虚假的提交者信息 %s"
+
+#: builtin/log.c:1199
+msgid "-n and -k are mutually exclusive."
+msgstr "-n 和 -k 互斥。"
+
+#: builtin/log.c:1201
+msgid "--subject-prefix and -k are mutually exclusive."
+msgstr "--subject-prefix 和 -k 互斥。"
+
+#: builtin/log.c:1209
+msgid "--name-only does not make sense"
+msgstr "--name-only 无意义"
+
+#: builtin/log.c:1211
+msgid "--name-status does not make sense"
+msgstr "--name-status 无意义"
+
+#: builtin/log.c:1213
+msgid "--check does not make sense"
+msgstr "--check 无意义"
+
+#: builtin/log.c:1236
+msgid "standard output, or directory, which one?"
+msgstr "标准输出或目录,哪一个?"
+
+#: builtin/log.c:1238
+#, c-format
+msgid "Could not create directory '%s'"
+msgstr "不能创建目录 '%s'"
+
+#: builtin/log.c:1391
+msgid "Failed to create output files"
+msgstr "无法创建输出文件"
+
+#: builtin/log.c:1495
+#, c-format
+msgid ""
+"Could not find a tracked remote branch, please specify <upstream> manually.\n"
+msgstr "不能找到跟踪的远程分支,请手工指定 <upstream>。\n"
+
+#: builtin/log.c:1511 builtin/log.c:1513 builtin/log.c:1525
+#, c-format
+msgid "Unknown commit %s"
+msgstr "未知提交 %s"
+
+#: builtin/merge.c:90
+msgid "switch `m' requires a value"
+msgstr "开关 `m' 需要一个值"
+
+#: builtin/merge.c:127
+#, c-format
+msgid "Could not find merge strategy '%s'.\n"
+msgstr "不能找到合并策略 '%s'。\n"
+
+#: builtin/merge.c:128
+#, c-format
+msgid "Available strategies are:"
+msgstr "可用的策略有:"
+
+#: builtin/merge.c:133
+#, c-format
+msgid "Available custom strategies are:"
+msgstr "可用的自定义策略有:"
+
+#: builtin/merge.c:240
+msgid "could not run stash."
+msgstr "不能进行进度保存。"
+
+#: builtin/merge.c:245
+msgid "stash failed"
+msgstr "进度保存失败"
+
+#: builtin/merge.c:250
+#, c-format
+msgid "not a valid object: %s"
+msgstr "不是一个有效对象:%s"
+
+#: builtin/merge.c:269 builtin/merge.c:286
+msgid "read-tree failed"
+msgstr "读取树失败"
+
+#  译者:注意保持前导空格
+#: builtin/merge.c:316
+msgid " (nothing to squash)"
+msgstr " (无可压缩)"
+
+#: builtin/merge.c:329
+#, c-format
+msgid "Squash commit -- not updating HEAD\n"
+msgstr "压缩提交 -- 未更新 HEAD\n"
+
+#: builtin/merge.c:361
+msgid "Writing SQUASH_MSG"
+msgstr "写入 SQUASH_MSG"
+
+#: builtin/merge.c:363
+msgid "Finishing SQUASH_MSG"
+msgstr "完成 SQUASH_MSG"
+
+#: builtin/merge.c:386
+#, c-format
+msgid "No merge message -- not updating HEAD\n"
+msgstr "无合并信息 -- 未更新 HEAD\n"
+
+#: builtin/merge.c:437
+#, c-format
+msgid "'%s' does not point to a commit"
+msgstr "'%s' 没有指向一个提交"
+
+#: builtin/merge.c:536
+#, c-format
+msgid "Bad branch.%s.mergeoptions string: %s"
+msgstr "坏的 branch.%s.mergeoptions 字符串:%s"
+
+#: builtin/merge.c:629
+msgid "git write-tree failed to write a tree"
+msgstr "git write-tree 无法写入一树对象"
+
+#: builtin/merge.c:679
+msgid "failed to read the cache"
+msgstr "无法读取缓存"
+
+#: builtin/merge.c:697
+msgid "Unable to write index."
+msgstr "不能写索引。"
+
+#: builtin/merge.c:710
+msgid "Not handling anything other than two heads merge."
+msgstr "不能处理两个头合并之外的任何操作。"
+
+#: builtin/merge.c:724
+#, c-format
+msgid "Unknown option for merge-recursive: -X%s"
+msgstr "merge-recursive 的未知选项:-X%s"
+
+#: builtin/merge.c:738
+#, c-format
+msgid "unable to write %s"
+msgstr "不能写 %s"
+
+#: builtin/merge.c:877
+#, c-format
+msgid "Could not read from '%s'"
+msgstr "不能从 '%s' 读取"
+
+#: builtin/merge.c:886
+#, c-format
+msgid "Not committing merge; use 'git commit' to complete the merge.\n"
+msgstr "未提交合并,使用 'git commit' 完成此次合并。\n"
+
+#: builtin/merge.c:892
+msgid ""
+"Please enter a commit message to explain why this merge is necessary,\n"
+"especially if it merges an updated upstream into a topic branch.\n"
+"\n"
+"Lines starting with '#' will be ignored, and an empty message aborts\n"
+"the commit.\n"
+msgstr ""
+"请输入一个提交信息以解释此合并的必要性,尤其是将一个更新后的上游分支\n"
+"合并到主题分支。\n"
+"\n"
+"以 '#' 开头的行将被忽略,而且空提交说明将会终止提交。\n"
+
+#: builtin/merge.c:916
+msgid "Empty commit message."
+msgstr "空提交信息。"
+
+#: builtin/merge.c:928
+#, c-format
+msgid "Wonderful.\n"
+msgstr "太棒了。\n"
+
+#: builtin/merge.c:993
+#, c-format
+msgid "Automatic merge failed; fix conflicts and then commit the result.\n"
+msgstr "自动合并失败,修正冲突然后提交修正的结果。\n"
+
+#: builtin/merge.c:1009
+#, c-format
+msgid "'%s' is not a commit"
+msgstr "'%s' 不是一个提交"
+
+#: builtin/merge.c:1050
+msgid "No current branch."
+msgstr "没有当前分支。"
+
+#: builtin/merge.c:1052
+msgid "No remote for the current branch."
+msgstr "当前分支没有对应的远程版本库。"
+
+#: builtin/merge.c:1054
+msgid "No default upstream defined for the current branch."
+msgstr "当前分支没有定义默认的上游分支。"
+
+#: builtin/merge.c:1059
+#, c-format
+msgid "No remote tracking branch for %s from %s"
+msgstr "%s 没有来自 %s 的远程跟踪分支"
+
+#: builtin/merge.c:1146 builtin/merge.c:1303
+#, c-format
+msgid "%s - not something we can merge"
+msgstr "%s - 不能被合并"
+
+#: builtin/merge.c:1214
+msgid "There is no merge to abort (MERGE_HEAD missing)."
+msgstr "没有要终止的合并(MERGE_HEAD 丢失)。"
+
+#: builtin/merge.c:1230 git-pull.sh:31
+msgid ""
+"You have not concluded your merge (MERGE_HEAD exists).\n"
+"Please, commit your changes before you can merge."
+msgstr ""
+"您尚未结束您的合并(存在 MERGE_HEAD)。\n"
+"请在合并前先提交您的修改。"
+
+#: builtin/merge.c:1233 git-pull.sh:34
+msgid "You have not concluded your merge (MERGE_HEAD exists)."
+msgstr "您尚未结束您的合并(存在 MERGE_HEAD)。"
+
+#: builtin/merge.c:1237
+msgid ""
+"You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
+"Please, commit your changes before you can merge."
+msgstr ""
+"您尚未结束您的拣选(存在 CHERRY_PICK_HEAD)。\n"
+"请在合并前先提交您的修改。"
+
+#: builtin/merge.c:1240
+msgid "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."
+msgstr "您尚未结束您的拣选(存在 CHERRY_PICK_HEAD)。"
+
+#: builtin/merge.c:1249
+msgid "You cannot combine --squash with --no-ff."
+msgstr "您不能将 --squash 与 --no-ff 共用。"
+
+#: builtin/merge.c:1254
+msgid "You cannot combine --no-ff with --ff-only."
+msgstr "您不能将 --no-ff 与 --ff-only 共用。"
+
+#: builtin/merge.c:1261
+msgid "No commit specified and merge.defaultToUpstream not set."
+msgstr "未指定提交并且 merge.defaultToUpstream 未设置。"
+
+#: builtin/merge.c:1293
+msgid "Can merge only exactly one commit into empty head"
+msgstr "只能将一个提交合并到空分支上"
+
+#: builtin/merge.c:1296
+msgid "Squash commit into empty head not supported yet"
+msgstr "尚不支持到空分支的压缩提交"
+
+#: builtin/merge.c:1298
+msgid "Non-fast-forward commit does not make sense into an empty head"
+msgstr "到空分支的非快进式提交没有意义"
+
+#: builtin/merge.c:1413
+#, c-format
+msgid "Updating %s..%s\n"
+msgstr "更新 %s..%s\n"
+
+#: builtin/merge.c:1451
+#, c-format
+msgid "Trying really trivial in-index merge...\n"
+msgstr "尝试非常小的索引内合并...\n"
+
+#: builtin/merge.c:1458
+#, c-format
+msgid "Nope.\n"
+msgstr "无。\n"
+
+#: builtin/merge.c:1490
+msgid "Not possible to fast-forward, aborting."
+msgstr "无法快进,终止。"
+
+#: builtin/merge.c:1513 builtin/merge.c:1592
+#, c-format
+msgid "Rewinding the tree to pristine...\n"
+msgstr "将树回滚至原始状态...\n"
+
+#: builtin/merge.c:1517
+#, c-format
+msgid "Trying merge strategy %s...\n"
+msgstr "尝试合并策略 %s...\n"
+
+#: builtin/merge.c:1583
+#, c-format
+msgid "No merge strategy handled the merge.\n"
+msgstr "没有合并策略处理此合并。\n"
+
+#: builtin/merge.c:1585
+#, c-format
+msgid "Merge with strategy %s failed.\n"
+msgstr "使用策略 %s 合并失败。\n"
+
+#: builtin/merge.c:1594
+#, c-format
+msgid "Using the %s to prepare resolving by hand.\n"
+msgstr "使用 %s 以准备手工解决。\n"
+
+#: builtin/merge.c:1606
+#, c-format
+msgid "Automatic merge went well; stopped before committing as requested\n"
+msgstr "自动合并进展顺利,按要求在提交前停止\n"
+
+#: builtin/mv.c:108
+#, c-format
+msgid "Checking rename of '%s' to '%s'\n"
+msgstr "检查 '%s' 到 '%s' 的重命名\n"
+
+#: builtin/mv.c:112
+msgid "bad source"
+msgstr "坏的源"
+
+#: builtin/mv.c:115
+msgid "can not move directory into itself"
+msgstr "不能将目录移动到自身"
+
+#: builtin/mv.c:118
+msgid "cannot move directory over file"
+msgstr "不能将目录移动到文件"
+
+#: builtin/mv.c:128
+#, c-format
+msgid "Huh? %.*s is in index?"
+msgstr "嗯?%.*s 在索引中?"
+
+#: builtin/mv.c:140
+msgid "source directory is empty"
+msgstr "源目录为空"
+
+#: builtin/mv.c:171
+msgid "not under version control"
+msgstr "不在版本控制之下"
+
+#: builtin/mv.c:173
+msgid "destination exists"
+msgstr "目标已存在"
+
+#: builtin/mv.c:181
+#, c-format
+msgid "overwriting '%s'"
+msgstr "覆盖 '%s'"
+
+#: builtin/mv.c:184
+msgid "Cannot overwrite"
+msgstr "不能覆盖"
+
+#: builtin/mv.c:187
+msgid "multiple sources for the same target"
+msgstr "同一目标具有多个源"
+
+#: builtin/mv.c:202
+#, c-format
+msgid "%s, source=%s, destination=%s"
+msgstr "%s,源=%s,目标=%s"
+
+#: builtin/mv.c:212
+#, c-format
+msgid "Renaming %s to %s\n"
+msgstr "重命名 %s 至 %s\n"
+
+#: builtin/mv.c:215 builtin/remote.c:731
+#, c-format
+msgid "renaming '%s' failed"
+msgstr "重命名 '%s' 失败"
+
+#: builtin/notes.c:139
+#, c-format
+msgid "unable to start 'show' for object '%s'"
+msgstr "不能为对象 '%s' 开始 'show'"
+
+#: builtin/notes.c:145
+msgid "can't fdopen 'show' output fd"
+msgstr "不能打开 'show' 输出文件句柄"
+
+#: builtin/notes.c:155
+#, c-format
+msgid "failed to close pipe to 'show' for object '%s'"
+msgstr "无法为对象 '%s' 的 'show' 关闭管道"
+
+#: builtin/notes.c:158
+#, c-format
+msgid "failed to finish 'show' for object '%s'"
+msgstr "无法为对象 '%s' 完成 'show'"
+
+#: builtin/notes.c:175 builtin/tag.c:347
+#, c-format
+msgid "could not create file '%s'"
+msgstr "不能创建文件 '%s'"
+
+#: builtin/notes.c:189
+msgid "Please supply the note contents using either -m or -F option"
+msgstr "请通过 -m 或 -F 选项为注解提供内容"
+
+#: builtin/notes.c:210 builtin/notes.c:973
+#, c-format
+msgid "Removing note for object %s\n"
+msgstr "删除对象 %s 的注解\n"
+
+#: builtin/notes.c:215
+msgid "unable to write note object"
+msgstr "不能写注解对象"
+
+#: builtin/notes.c:217
+#, c-format
+msgid "The note contents has been left in %s"
+msgstr "注解内容被留在文件 %s 中"
+
+#: builtin/notes.c:251 builtin/tag.c:542
+#, c-format
+msgid "cannot read '%s'"
+msgstr "不能读取 '%s'"
+
+#: builtin/notes.c:253 builtin/tag.c:545
+#, c-format
+msgid "could not open or read '%s'"
+msgstr "不能打开或读取 '%s'"
+
+#: builtin/notes.c:272 builtin/notes.c:445 builtin/notes.c:447
+#: builtin/notes.c:507 builtin/notes.c:561 builtin/notes.c:644
+#: builtin/notes.c:649 builtin/notes.c:724 builtin/notes.c:766
+#: builtin/notes.c:968 builtin/reset.c:293 builtin/tag.c:558
+#, c-format
+msgid "Failed to resolve '%s' as a valid ref."
+msgstr "无法解析 '%s' 为一个有效引用。"
+
+#: builtin/notes.c:275
+#, c-format
+msgid "Failed to read object '%s'."
+msgstr "无法读取对象 '%s'。"
+
+#: builtin/notes.c:299
+msgid "Cannot commit uninitialized/unreferenced notes tree"
+msgstr "不能提交未初始化/未引用的注解树"
+
+#: builtin/notes.c:340
+#, c-format
+msgid "Bad notes.rewriteMode value: '%s'"
+msgstr "坏的 notes.rewriteMode 值:'%s'"
+
+#: builtin/notes.c:350
+#, c-format
+msgid "Refusing to rewrite notes in %s (outside of refs/notes/)"
+msgstr "拒绝向 %s(在 refs/notes/ 之外)写入注解"
+
+#. TRANSLATORS: The first %s is the name of the
+#. environment variable, the second %s is its value
+#: builtin/notes.c:377
+#, c-format
+msgid "Bad %s value: '%s'"
+msgstr "坏的 %s 值:'%s'"
+
+#: builtin/notes.c:441
+#, c-format
+msgid "Malformed input line: '%s'."
+msgstr "非法的输入行:'%s'。"
+
+#: builtin/notes.c:456
+#, c-format
+msgid "Failed to copy notes from '%s' to '%s'"
+msgstr "无法从 '%s' 到 '%s' 拷贝注解"
+
+#: builtin/notes.c:500 builtin/notes.c:554 builtin/notes.c:627
+#: builtin/notes.c:639 builtin/notes.c:712 builtin/notes.c:759
+#: builtin/notes.c:1033
+msgid "too many parameters"
+msgstr "参数太多"
+
+#: builtin/notes.c:513 builtin/notes.c:772
+#, c-format
+msgid "No note found for object %s."
+msgstr "未发现对象 %s 的注解。"
+
+#: builtin/notes.c:580
+#, c-format
+msgid ""
+"Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite "
+"existing notes"
+msgstr "不能添加注解。发现对象 %s 已存在注解。使用 '-f' 覆盖现存注解"
+
+#: builtin/notes.c:585 builtin/notes.c:662
+#, c-format
+msgid "Overwriting existing notes for object %s\n"
+msgstr "覆盖对象 %s 现存注解\n"
+
+#: builtin/notes.c:635
+msgid "too few parameters"
+msgstr "参数太少"
+
+#: builtin/notes.c:656
+#, c-format
+msgid ""
+"Cannot copy notes. Found existing notes for object %s. Use '-f' to overwrite "
+"existing notes"
+msgstr "不能拷贝注解。发现对象 %s 已存在注解。使用 '-f' 覆盖现存注解"
+
+#: builtin/notes.c:668
+#, c-format
+msgid "Missing notes on source object %s. Cannot copy."
+msgstr "源对象 %s 缺少注解。不能拷贝。"
+
+#: builtin/notes.c:717
+#, c-format
+msgid ""
+"The -m/-F/-c/-C options have been deprecated for the 'edit' subcommand.\n"
+"Please use 'git notes add -f -m/-F/-c/-C' instead.\n"
+msgstr ""
+"子命令 'edit' 的选项 -m/-F/-c/-C 已弃用。\n"
+"请换用 'git notes add -f -m/-F/-c/-C'。\n"
+
+#: builtin/notes.c:971
+#, c-format
+msgid "Object %s has no note\n"
+msgstr "对象 %s 没有注解\n"
+
+#: builtin/notes.c:1103 builtin/remote.c:1598
+#, c-format
+msgid "Unknown subcommand: %s"
+msgstr "未知子命令:%s"
+
+#: builtin/pack-objects.c:183 builtin/pack-objects.c:186
+#, c-format
+msgid "deflate error (%d)"
+msgstr "压缩错误 (%d)"
+
+#: builtin/pack-objects.c:2398
+#, c-format
+msgid "unsupported index version %s"
+msgstr "不支持的索引版本 %s"
+
+#: builtin/pack-objects.c:2402
+#, c-format
+msgid "bad index version '%s'"
+msgstr "坏的索引版本 '%s'"
+
+#: builtin/pack-objects.c:2425
+#, c-format
+msgid "option %s does not accept negative form"
+msgstr "选项 %s 不接受否定格式"
+
+#: builtin/pack-objects.c:2429
+#, c-format
+msgid "unable to parse value '%s' for option %s"
+msgstr "不能解析选项 %1$s 的值 '%2$s'"
+
+#: builtin/push.c:45
+msgid "tag shorthand without <tag>"
+msgstr "tag 简写没有跟 <tag> 参数"
+
+#: builtin/push.c:64
+msgid "--delete only accepts plain target ref names"
+msgstr "--delete 只接受简单的目标引用名"
+
+#: builtin/push.c:99
+msgid ""
+"\n"
+"To choose either option permanently, see push.default in 'git help config'."
+msgstr ""
+"\n"
+"为了永久地选择任一选项,参见 'git help config' 中的 push.default。"
+
+#: builtin/push.c:102
+#, c-format
+msgid ""
+"The upstream branch of your current branch does not match\n"
+"the name of your current branch.  To push to the upstream branch\n"
+"on the remote, use\n"
+"\n"
+"    git push %s HEAD:%s\n"
+"\n"
+"To push to the branch of the same name on the remote, use\n"
+"\n"
+"    git push %s %s\n"
+"%s"
+msgstr ""
+"如果您当前分支的上游分支和您当前分支名不匹配,为推送到远程的\n"
+"上游分支,使用\n"
+"\n"
+"    git push %s HEAD:%s\n"
+"\n"
+"为推送至远程同名分支,使用\n"
+"\n"
+"    git push %s %s\n"
+"%s"
+
+#: builtin/push.c:121
+#, c-format
+msgid ""
+"You are not currently on a branch.\n"
+"To push the history leading to the current (detached HEAD)\n"
+"state now, use\n"
+"\n"
+"    git push %s HEAD:<name-of-remote-branch>\n"
+msgstr ""
+"您当前不在一个分支上。\n"
+"现在为推送当前(分离头指针)的历史,使用\n"
+"\n"
+"    git push %s HEAD:<name-of-remote-branch>\n"
+
+#: builtin/push.c:128
+#, c-format
+msgid ""
+"The current branch %s has no upstream branch.\n"
+"To push the current branch and set the remote as upstream, use\n"
+"\n"
+"    git push --set-upstream %s %s\n"
+msgstr ""
+"当前分支 %s 没有对应的上游分支。\n"
+"为推送当前分支并建立与远程上游的跟踪,使用\n"
+"\n"
+"    git push --set-upstream %s %s\n"
+
+#: builtin/push.c:136
+#, c-format
+msgid "The current branch %s has multiple upstream branches, refusing to push."
+msgstr "当前分支 %s 有多个上游分支,拒绝推送。"
+
+#: builtin/push.c:139
+#, c-format
+msgid ""
+"You are pushing to remote '%s', which is not the upstream of\n"
+"your current branch '%s', without telling me what to push\n"
+"to update which remote branch."
+msgstr ""
+"您正推送至远程 '%s'(其并非当前分支 '%s' 的上游),\n"
+"而没有告诉我要推送什么、更新哪个远程分支。"
+
+#: builtin/push.c:174
+msgid ""
+"You didn't specify any refspecs to push, and push.default is \"nothing\"."
+msgstr "您没有为推送指定任何引用表达式,并且 push.default 为 \"nothing\"。"
+
+#: builtin/push.c:181
+msgid ""
+"Updates were rejected because the tip of your current branch is behind\n"
+"its remote counterpart. Merge the remote changes (e.g. 'git pull')\n"
+"before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
+msgstr ""
+"更新被拒绝,因为您当前分支的最新提交落后于其对应的远程分支。\n"
+"再次推送前,先与远程变更合并(如 'git pull')。详见\n"
+"'git push --help' 中的 'Note about fast-forwards' 小节。"
+
+#: builtin/push.c:187
+msgid ""
+"Updates were rejected because a pushed branch tip is behind its remote\n"
+"counterpart. If you did not intend to push that branch, you may want to\n"
+"specify branches to push or set the 'push.default' configuration\n"
+"variable to 'current' or 'upstream' to push only the current branch."
+msgstr ""
+"更新被拒绝,因为推送的一个分支的最新提交落后于其对应的远程分支。\n"
+"如果您并非有意推送该分支,您可以在推送时指定要推送的分支,或者将\n"
+"配置变量 'push.default' 设置为 'current' 或 'upstream' 以便只推送当前分支。"
+
+#: builtin/push.c:193
+msgid ""
+"Updates were rejected because a pushed branch tip is behind its remote\n"
+"counterpart. Check out this branch and merge the remote changes\n"
+"(e.g. 'git pull') before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
+msgstr ""
+"更新被拒绝,因为推送的一个分支的最新提交落后于其对应的远程分支。\n"
+"检出该分支并与远程变更合并(如 'git pull'),然后再推送。详见\n"
+"'git push --help' 中的 'Note about fast-forwards' 小节。"
+
+#: builtin/push.c:233
+#, c-format
+msgid "Pushing to %s\n"
+msgstr "推送到 %s\n"
+
+#: builtin/push.c:237
+#, c-format
+msgid "failed to push some refs to '%s'"
+msgstr "无法推送一些引用到 '%s'"
+
+#: builtin/push.c:269
+#, c-format
+msgid "bad repository '%s'"
+msgstr "坏的版本库 '%s'"
+
+#: builtin/push.c:270
+msgid ""
+"No configured push destination.\n"
+"Either specify the URL from the command-line or configure a remote "
+"repository using\n"
+"\n"
+"    git remote add <name> <url>\n"
+"\n"
+"and then push using the remote name\n"
+"\n"
+"    git push <name>\n"
+msgstr ""
+"没有配置推送目标。\n"
+"或者通过命令行指定URL,或者用下面命令配置一个远程版本库\n"
+"\n"
+"    git remote add <name> <url>\n"
+"\n"
+"然后使用该远程版本库名执行推送\n"
+"\n"
+"    git push <name>\n"
+
+#: builtin/push.c:285
+msgid "--all and --tags are incompatible"
+msgstr "--all 和 --tags 不兼容"
+
+#: builtin/push.c:286
+msgid "--all can't be combined with refspecs"
+msgstr "--all 不能和引用表达式共用"
+
+#: builtin/push.c:291
+msgid "--mirror and --tags are incompatible"
+msgstr "--mirror 和 --tags 不兼容"
+
+#: builtin/push.c:292
+msgid "--mirror can't be combined with refspecs"
+msgstr "--mirror 不能和引用表达式共用"
+
+#: builtin/push.c:297
+msgid "--all and --mirror are incompatible"
+msgstr "--all 和 --mirror 不兼容"
+
+#: builtin/push.c:385
+msgid "--delete is incompatible with --all, --mirror and --tags"
+msgstr "--delete 与 --all、--mirror 及 --tags 不兼容"
+
+#: builtin/push.c:387
+msgid "--delete doesn't make sense without any refs"
+msgstr "--delete 未接任何引用没有意义"
+
+#: builtin/remote.c:98
+#, c-format
+msgid "Updating %s"
+msgstr "更新 %s 中"
+
+#: builtin/remote.c:130
+msgid ""
+"--mirror is dangerous and deprecated; please\n"
+"\t use --mirror=fetch or --mirror=push instead"
+msgstr ""
+"--mirror 选项危险且过时,请使用 --mirror=fetch\n"
+"\t 或 --mirror=push"
+
+#: builtin/remote.c:147
+#, c-format
+msgid "unknown mirror argument: %s"
+msgstr "未知的镜像参数:%s"
+
+#: builtin/remote.c:185
+msgid "specifying a master branch makes no sense with --mirror"
+msgstr "指定一个 master 分支并使用 --mirror 选项没有意义"
+
+#: builtin/remote.c:187
+msgid "specifying branches to track makes sense only with fetch mirrors"
+msgstr "指定要跟踪的分支只在与获取镜像共用才有意义"
+
+#: builtin/remote.c:195 builtin/remote.c:646
+#, c-format
+msgid "remote %s already exists."
+msgstr "远程 %s 已经存在。"
+
+#: builtin/remote.c:199 builtin/remote.c:650
+#, c-format
+msgid "'%s' is not a valid remote name"
+msgstr "'%s' 不是一个有效的远程名称"
+
+#: builtin/remote.c:243
+#, c-format
+msgid "Could not setup master '%s'"
+msgstr "无法设置 master '%s'"
+
+#: builtin/remote.c:299
+#, c-format
+msgid "more than one %s"
+msgstr "多于一个 %s"
+
+#: builtin/remote.c:339
+#, c-format
+msgid "Could not get fetch map for refspec %s"
+msgstr "无法得到引用表达式 %s 的获取列表"
+
+#: builtin/remote.c:440 builtin/remote.c:448
+msgid "(matching)"
+msgstr "(匹配)"
+
+#: builtin/remote.c:452
+msgid "(delete)"
+msgstr "(删除)"
+
+#: builtin/remote.c:595 builtin/remote.c:601 builtin/remote.c:607
+#, c-format
+msgid "Could not append '%s' to '%s'"
+msgstr "不能添加 '%s' 至 '%s'"
+
+#: builtin/remote.c:639 builtin/remote.c:792 builtin/remote.c:890
+#, c-format
+msgid "No such remote: %s"
+msgstr "没有这样的远程:%s"
+
+#: builtin/remote.c:656
+#, c-format
+msgid "Could not rename config section '%s' to '%s'"
+msgstr "不能重命名配置小节 '%s' 到 '%s'"
+
+#: builtin/remote.c:662 builtin/remote.c:799
+#, c-format
+msgid "Could not remove config section '%s'"
+msgstr "不能移除配置小节 '%s'"
+
+#: builtin/remote.c:677
+#, c-format
+msgid ""
+"Not updating non-default fetch refspec\n"
+"\t%s\n"
+"\tPlease update the configuration manually if necessary."
+msgstr ""
+"没有更新非默认的获取引用表达式\n"
+"\t%s\n"
+"\t如果必要请手动更新配置。"
+
+#: builtin/remote.c:683
+#, c-format
+msgid "Could not append '%s'"
+msgstr "不能追加 '%s'"
+
+#: builtin/remote.c:694
+#, c-format
+msgid "Could not set '%s'"
+msgstr "不能设置 '%s'"
+
+#: builtin/remote.c:716
+#, c-format
+msgid "deleting '%s' failed"
+msgstr "删除 '%s' 失败"
+
+#: builtin/remote.c:750
+#, c-format
+msgid "creating '%s' failed"
+msgstr "创建 '%s' 失败"
+
+#: builtin/remote.c:764
+#, c-format
+msgid "Could not remove branch %s"
+msgstr "无法移除分支 %s"
+
+#: builtin/remote.c:834
+msgid ""
+"Note: A branch outside the refs/remotes/ hierarchy was not removed;\n"
+"to delete it, use:"
+msgid_plural ""
+"Note: Some branches outside the refs/remotes/ hierarchy were not removed;\n"
+"to delete them, use:"
+msgstr[0] "注意:ref/remotes 层级之外的一个分支未被移除。要删除它,使用:"
+msgstr[1] "注意:ref/remotes 层级之外的一些分支未被移除。要删除它们,使用:"
+
+#: builtin/remote.c:943
+#, c-format
+msgid " new (next fetch will store in remotes/%s)"
+msgstr " 新的(下一次获取将存储于 remotes/%s)"
+
+#: builtin/remote.c:946
+msgid " tracked"
+msgstr " 已跟踪"
+
+#: builtin/remote.c:948
+msgid " stale (use 'git remote prune' to remove)"
+msgstr " 过时(使用 'git remote prune' 来移除)"
+
+#: builtin/remote.c:950
+msgid " ???"
+msgstr " ???"
+
+#: builtin/remote.c:991
+#, c-format
+msgid "invalid branch.%s.merge; cannot rebase onto > 1 branch"
+msgstr "无效的 branch.%s.merge,不能变基到一个以上的分支"
+
+#: builtin/remote.c:998
+#, c-format
+msgid "rebases onto remote %s"
+msgstr "变基到远程 %s"
+
+#: builtin/remote.c:1001
+#, c-format
+msgid " merges with remote %s"
+msgstr " 与远程 %s 合并"
+
+#: builtin/remote.c:1002
+msgid "    and with remote"
+msgstr "    且有远程"
+
+#: builtin/remote.c:1004
+#, c-format
+msgid "merges with remote %s"
+msgstr "与远程 %s 合并"
+
+#: builtin/remote.c:1005
+msgid "   and with remote"
+msgstr "   且有远程"
+
+#: builtin/remote.c:1051
+msgid "create"
+msgstr "创建"
+
+#: builtin/remote.c:1054
+msgid "delete"
+msgstr "删除"
+
+#: builtin/remote.c:1058
+msgid "up to date"
+msgstr "最新"
+
+#: builtin/remote.c:1061
+msgid "fast-forwardable"
+msgstr "可快进"
+
+#: builtin/remote.c:1064
+msgid "local out of date"
+msgstr "本地已过时"
+
+#: builtin/remote.c:1071
+#, c-format
+msgid "    %-*s forces to %-*s (%s)"
+msgstr "    %-*s 强制推送至 %-*s (%s)"
+
+#: builtin/remote.c:1074
+#, c-format
+msgid "    %-*s pushes to %-*s (%s)"
+msgstr "    %-*s 推送至 %-*s (%s)"
+
+#: builtin/remote.c:1078
+#, c-format
+msgid "    %-*s forces to %s"
+msgstr "    %-*s 强制推送至 %s"
+
+#: builtin/remote.c:1081
+#, c-format
+msgid "    %-*s pushes to %s"
+msgstr "    %-*s 推送至 %s"
+
+#: builtin/remote.c:1118
+#, c-format
+msgid "* remote %s"
+msgstr "* 远程 %s"
+
+#: builtin/remote.c:1119
+#, c-format
+msgid "  Fetch URL: %s"
+msgstr "  获取地址:%s"
+
+#: builtin/remote.c:1120 builtin/remote.c:1285
+msgid "(no URL)"
+msgstr "(无 URL)"
+
+#: builtin/remote.c:1129 builtin/remote.c:1131
+#, c-format
+msgid "  Push  URL: %s"
+msgstr "  推送地址:%s"
+
+#: builtin/remote.c:1133 builtin/remote.c:1135 builtin/remote.c:1137
+#, c-format
+msgid "  HEAD branch: %s"
+msgstr "  HEAD分支:%s"
+
+#: builtin/remote.c:1139
+#, c-format
+msgid ""
+"  HEAD branch (remote HEAD is ambiguous, may be one of the following):\n"
+msgstr "  HEAD 分支(远程 HEAD 模糊,可能是下列中的一个):\n"
+
+#: builtin/remote.c:1151
+#, c-format
+msgid "  Remote branch:%s"
+msgid_plural "  Remote branches:%s"
+msgstr[0] "  远程分支:%s"
+msgstr[1] "  远程分支:%s"
+
+#  译者:中文字符串拼接,可删除前导空格
+#: builtin/remote.c:1154 builtin/remote.c:1181
+msgid " (status not queried)"
+msgstr "(状态未查询)"
+
+#: builtin/remote.c:1163
+msgid "  Local branch configured for 'git pull':"
+msgid_plural "  Local branches configured for 'git pull':"
+msgstr[0] "  为 'git pull' 配置的本地分支:"
+msgstr[1] "  为 'git pull' 配置的本地分支:"
+
+#: builtin/remote.c:1171
+msgid "  Local refs will be mirrored by 'git push'"
+msgstr "  本地引用将在 'git push' 时被镜像"
+
+#: builtin/remote.c:1178
+#, c-format
+msgid "  Local ref configured for 'git push'%s:"
+msgid_plural "  Local refs configured for 'git push'%s:"
+msgstr[0] "  为 'git push' 配置的本地引用%s:"
+msgstr[1] "  为 'git push' 配置的本地引用%s:"
+
+#: builtin/remote.c:1216
+msgid "Cannot determine remote HEAD"
+msgstr "无法确定远程 HEAD"
+
+#: builtin/remote.c:1218
+msgid "Multiple remote HEAD branches. Please choose one explicitly with:"
+msgstr "多个远程 HEAD 分支。请明确地选择一个用命令:"
+
+#: builtin/remote.c:1228
+#, c-format
+msgid "Could not delete %s"
+msgstr "无法删除 %s"
+
+#: builtin/remote.c:1236
+#, c-format
+msgid "Not a valid ref: %s"
+msgstr "不是一个有效引用:%s"
+
+#: builtin/remote.c:1238
+#, c-format
+msgid "Could not setup %s"
+msgstr "不能设置 %s"
+
+#  译者:注意保持前导空格
+#: builtin/remote.c:1274
+#, c-format
+msgid " %s will become dangling!"
+msgstr " %s 将成为悬空状态!"
+
+#  译者:注意保持前导空格
+#: builtin/remote.c:1275
+#, c-format
+msgid " %s has become dangling!"
+msgstr " %s 已成为悬空状态!"
+
+#: builtin/remote.c:1281
+#, c-format
+msgid "Pruning %s"
+msgstr "修剪 %s"
+
+#: builtin/remote.c:1282
+#, c-format
+msgid "URL: %s"
+msgstr "URL:%s"
+
+#: builtin/remote.c:1295
+#, c-format
+msgid " * [would prune] %s"
+msgstr " * [将删除] %s"
+
+#: builtin/remote.c:1298
+#, c-format
+msgid " * [pruned] %s"
+msgstr " * [已删除] %s"
+
+#: builtin/remote.c:1387 builtin/remote.c:1461
+#, c-format
+msgid "No such remote '%s'"
+msgstr "没有此远程 '%s'"
+
+#: builtin/remote.c:1414
+msgid "no remote specified"
+msgstr "未指定远程"
+
+#: builtin/remote.c:1447
+msgid "--add --delete doesn't make sense"
+msgstr "--add --delete 无意义"
+
+#: builtin/remote.c:1487
+#, c-format
+msgid "Invalid old URL pattern: %s"
+msgstr "无效的旧URL匹配模版:%s"
+
+#: builtin/remote.c:1495
+#, c-format
+msgid "No such URL found: %s"
+msgstr "未找到此URL:%s"
+
+#: builtin/remote.c:1497
+msgid "Will not delete all non-push URLs"
+msgstr "将不会删除所有非推送URL地址"
+
+#: builtin/reset.c:33
+msgid "mixed"
+msgstr "混杂"
+
+#: builtin/reset.c:33
+msgid "soft"
+msgstr "软性"
+
+#: builtin/reset.c:33
+msgid "hard"
+msgstr "硬性"
+
+#: builtin/reset.c:33
+msgid "merge"
+msgstr "合并"
+
+#: builtin/reset.c:33
+msgid "keep"
+msgstr "保持"
+
+#: builtin/reset.c:77
+msgid "You do not have a valid HEAD."
+msgstr "您没有一个有效的 HEAD。"
+
+#: builtin/reset.c:79
+msgid "Failed to find tree of HEAD."
+msgstr "无法找到 HEAD 指向的树。"
+
+#: builtin/reset.c:85
+#, c-format
+msgid "Failed to find tree of %s."
+msgstr "无法找到 %s 指向的树。"
+
+#: builtin/reset.c:96
+msgid "Could not write new index file."
+msgstr "不能写入新的索引文件。"
+
+#: builtin/reset.c:106
+#, c-format
+msgid "HEAD is now at %s"
+msgstr "HEAD 现在位于 %s"
+
+#: builtin/reset.c:130
+msgid "Could not read index"
+msgstr "不能读取索引"
+
+#: builtin/reset.c:133
+msgid "Unstaged changes after reset:"
+msgstr "重置后撤出暂存区的变更:"
+
+#  译者:汉字之间无空格,故删除%s前后空格
+#: builtin/reset.c:223
+#, c-format
+msgid "Cannot do a %s reset in the middle of a merge."
+msgstr "在合并过程中不能做%s重置操作。"
+
+#: builtin/reset.c:297
+#, c-format
+msgid "Could not parse object '%s'."
+msgstr "不能解析对象 '%s'。"
+
+#: builtin/reset.c:302
+msgid "--patch is incompatible with --{hard,mixed,soft}"
+msgstr "--patch 与 --{hard,mixed,soft} 不兼容"
+
+#: builtin/reset.c:311
+msgid "--mixed with paths is deprecated; use 'git reset -- <paths>' instead."
+msgstr "--mixed 带路径已弃用,代之以 'git reset -- <paths>'。"
+
+#  译者:汉字之间无空格,故删除%s前后空格
+#: builtin/reset.c:313
+#, c-format
+msgid "Cannot do %s reset with paths."
+msgstr "不能带路径进行%s重置。"
+
+#  译者:汉字之间无空格,故删除%s前后空格
+#: builtin/reset.c:325
+#, c-format
+msgid "%s reset is not allowed in a bare repository"
+msgstr "不能对裸版本库进行%s重置"
+
+#: builtin/reset.c:341
+#, c-format
+msgid "Could not reset index file to revision '%s'."
+msgstr "不能重置索引文件至版本 '%s'。"
+
+#: builtin/revert.c:70 builtin/revert.c:92
+#, c-format
+msgid "%s: %s cannot be used with %s"
+msgstr "%s:%s 不能和 %s 共用"
+
+#: builtin/revert.c:131
+msgid "program error"
+msgstr "程序错误"
+
+#: builtin/revert.c:221
+msgid "revert failed"
+msgstr "还原失败"
+
+#: builtin/revert.c:236
+msgid "cherry-pick failed"
+msgstr "拣选失败"
+
+#: builtin/rm.c:109
+#, c-format
+msgid ""
+"'%s' has staged content different from both the file and the HEAD\n"
+"(use -f to force removal)"
+msgstr ""
+"'%s' 暂存的内容和工作区及 HEAD 中的都不一样\n"
+"(使用 -f 强制删除)"
+
+#: builtin/rm.c:115
+#, c-format
+msgid ""
+"'%s' has changes staged in the index\n"
+"(use --cached to keep the file, or -f to force removal)"
+msgstr ""
+"'%s' 有变更已暂存至索引中\n"
+"(使用 --cached 保存文件,或用 -f 强制删除)"
+
+#: builtin/rm.c:119
+#, c-format
+msgid ""
+"'%s' has local modifications\n"
+"(use --cached to keep the file, or -f to force removal)"
+msgstr ""
+"'%s' 有本地修改\n"
+"(使用 --cached 保存文件,或用 -f 强制删除)"
+
+#: builtin/rm.c:194
+#, c-format
+msgid "not removing '%s' recursively without -r"
+msgstr "未提供 -r 选项不会递归删除 '%s'"
+
+#: builtin/rm.c:230
+#, c-format
+msgid "git rm: unable to remove %s"
+msgstr "git rm:不能删除 %s"
+
+#: builtin/shortlog.c:157
+#, c-format
+msgid "Missing author: %s"
+msgstr "缺少作者:%s"
+
+#: builtin/tag.c:60
+#, c-format
+msgid "malformed object at '%s'"
+msgstr "非法的对象于 '%s'"
+
+#: builtin/tag.c:207
+#, c-format
+msgid "tag name too long: %.*s..."
+msgstr "tag 名字太长:%.*s..."
+
+#: builtin/tag.c:212
+#, c-format
+msgid "tag '%s' not found."
+msgstr "tag '%s' 未发现。"
+
+#: builtin/tag.c:227
+#, c-format
+msgid "Deleted tag '%s' (was %s)\n"
+msgstr "已删除 tag '%s'(曾为 %s)\n"
+
+#: builtin/tag.c:239
+#, c-format
+msgid "could not verify the tag '%s'"
+msgstr "不能校验 tag '%s'"
+
+#: builtin/tag.c:249
+msgid ""
+"\n"
+"#\n"
+"# Write a tag message\n"
+"# Lines starting with '#' will be ignored.\n"
+"#\n"
+msgstr ""
+"\n"
+"#\n"
+"# 输入一个 tag 说明\n"
+"# 以 '#' 开头的行将被忽略。\n"
+"#\n"
+
+#: builtin/tag.c:256
+msgid ""
+"\n"
+"#\n"
+"# Write a tag message\n"
+"# Lines starting with '#' will be kept; you may remove them yourself if you "
+"want to.\n"
+"#\n"
+msgstr ""
+"\n"
+"#\n"
+"# 输入一个 tag 说明\n"
+"# 以 '#' 开头的行将被忽略,您可以删除它们如果您想这样做。\n"
+"#\n"
+
+#: builtin/tag.c:298
+msgid "unable to sign the tag"
+msgstr "无法签署 tag"
+
+#: builtin/tag.c:300
+msgid "unable to write tag file"
+msgstr "无法写 tag 文件"
+
+#: builtin/tag.c:325
+msgid "bad object type."
+msgstr "坏的对象类型。"
+
+#: builtin/tag.c:338
+msgid "tag header too big."
+msgstr "tag 头信息太大。"
+
+#: builtin/tag.c:370
+msgid "no tag message?"
+msgstr "无 tag 说明?"
+
+#: builtin/tag.c:376
+#, c-format
+msgid "The tag message has been left in %s\n"
+msgstr "tag 说明被保留在 %s\n"
+
+#: builtin/tag.c:425
+msgid "switch 'points-at' requires an object"
+msgstr "开关 'points-at' 需要一个对象"
+
+#: builtin/tag.c:427
+#, c-format
+msgid "malformed object name '%s'"
+msgstr "非法的对象名 '%s'"
+
+#: builtin/tag.c:506
+msgid "--column and -n are incompatible"
+msgstr "--column 和 -n 不兼容"
+
+#: builtin/tag.c:523
+msgid "-n option is only allowed with -l."
+msgstr "-n 选项只允许和 -l 共用。"
+
+#: builtin/tag.c:525
+msgid "--contains option is only allowed with -l."
+msgstr "--contains 选项只允许和 -l 共用。"
+
+#: builtin/tag.c:527
+msgid "--points-at option is only allowed with -l."
+msgstr "--points-at 选项只允许和 -l 共用。"
+
+#: builtin/tag.c:535
+msgid "only one -F or -m option is allowed."
+msgstr "只允许一个 -F 或 -m 选项。"
+
+#: builtin/tag.c:555
+msgid "too many params"
+msgstr "太多参数"
+
+#: builtin/tag.c:561
+#, c-format
+msgid "'%s' is not a valid tag name."
+msgstr "'%s' 不是一个有效的tag名称。"
+
+#: builtin/tag.c:566
+#, c-format
+msgid "tag '%s' already exists"
+msgstr "tag '%s' 已存在"
+
+#: builtin/tag.c:584
+#, c-format
+msgid "%s: cannot lock the ref"
+msgstr "%s:不能锁定引用"
+
+#: builtin/tag.c:586
+#, c-format
+msgid "%s: cannot update the ref"
+msgstr "%s:不能更新引用"
+
+#: builtin/tag.c:588
+#, c-format
+msgid "Updated tag '%s' (was %s)\n"
+msgstr "已更新tag '%s'(曾为 %s)\n"
+
+#: git.c:16
+msgid "See 'git help <command>' for more information on a specific command."
+msgstr "参见 'git help <command>' 以获得该特定命令的详细信息。"
+
+#: parse-options.h:133 parse-options.h:235
+msgid "n"
+msgstr "数字"
+
+#: parse-options.h:141
+msgid "time"
+msgstr "时间"
+
+#: parse-options.h:149
+msgid "file"
+msgstr "文件"
+
+#: parse-options.h:151
+msgid "when"
+msgstr "何时"
+
+#: parse-options.h:156
+msgid "no-op (backward compatibility)"
+msgstr "空操作(向后兼容)"
+
+#: parse-options.h:228
+msgid "be more verbose"
+msgstr "更加详细"
+
+#: parse-options.h:230
+msgid "be more quiet"
+msgstr "更加安静"
+
+#: parse-options.h:236
+msgid "use <n> digits to display SHA-1s"
+msgstr "用 <n> 位数字显示 SHA-1 哈希值"
+
+#: common-cmds.h:8
+msgid "Add file contents to the index"
+msgstr "添加文件内容至索引"
+
+#: common-cmds.h:9
+msgid "Find by binary search the change that introduced a bug"
+msgstr "通过二分查找定位引入 bug 的变更"
+
+#: common-cmds.h:10
+msgid "List, create, or delete branches"
+msgstr "列出、创建或删除分支"
+
+#: common-cmds.h:11
+msgid "Checkout a branch or paths to the working tree"
+msgstr "检出一个分支或路径到工作区"
+
+#: common-cmds.h:12
+msgid "Clone a repository into a new directory"
+msgstr "克隆一个版本库到一个新目录"
+
+#: common-cmds.h:13
+msgid "Record changes to the repository"
+msgstr "记录变更到版本库"
+
+#: common-cmds.h:14
+msgid "Show changes between commits, commit and working tree, etc"
+msgstr "显示提交之间、提交和工作区之间等的差异"
+
+#: common-cmds.h:15
+msgid "Download objects and refs from another repository"
+msgstr "从另外一个版本库下载对象和引用"
+
+#: common-cmds.h:16
+msgid "Print lines matching a pattern"
+msgstr "输出和模式匹配的行"
+
+#: common-cmds.h:17
+msgid "Create an empty git repository or reinitialize an existing one"
+msgstr "创建一个空的 git 版本库或者重新初始化一个"
+
+#: common-cmds.h:18
+msgid "Show commit logs"
+msgstr "显示提交日志"
+
+#: common-cmds.h:19
+msgid "Join two or more development histories together"
+msgstr "合并两个或更多开发历史"
+
+#: common-cmds.h:20
+msgid "Move or rename a file, a directory, or a symlink"
+msgstr "移动或重命名一个文件、目录或符号链接"
+
+#: common-cmds.h:21
+msgid "Fetch from and merge with another repository or a local branch"
+msgstr "获取并合并另外的版本库或一个本地分支"
+
+#: common-cmds.h:22
+msgid "Update remote refs along with associated objects"
+msgstr "更新远程引用和相关的对象"
+
+#: common-cmds.h:23
+msgid "Forward-port local commits to the updated upstream head"
+msgstr "本地提交转移至更新后的上游分支中"
+
+#: common-cmds.h:24
+msgid "Reset current HEAD to the specified state"
+msgstr "重置当前HEAD到指定状态"
+
+#: common-cmds.h:25
+msgid "Remove files from the working tree and from the index"
+msgstr "从工作区和索引中删除文件"
+
+#: common-cmds.h:26
+msgid "Show various types of objects"
+msgstr "显示各种类型的对象"
+
+#: common-cmds.h:27
+msgid "Show the working tree status"
+msgstr "显示工作区状态"
+
+#: common-cmds.h:28
+msgid "Create, list, delete or verify a tag object signed with GPG"
+msgstr "创建、列出、删除或校验一个GPG签名的 tag 对象"
+
+#: git-am.sh:50
+msgid "You need to set your committer info first"
+msgstr "您需要先设置你的提交者信息"
+
+#: git-am.sh:95
+msgid ""
+"You seem to have moved HEAD since the last 'am' failure.\n"
+"Not rewinding to ORIG_HEAD"
+msgstr "您好像在上一次 'am' 失败后移动了 HEAD。未回退至 ORIG_HEAD"
+
+#: git-am.sh:105
+#, sh-format
+msgid ""
+"When you have resolved this problem run \"$cmdline --resolved\".\n"
+"If you would prefer to skip this patch, instead run \"$cmdline --skip\".\n"
+"To restore the original branch and stop patching run \"$cmdline --abort\"."
+msgstr ""
+"当您解决了此问题后,执行 \"$cmdline --resolved\"。\n"
+"如果您想跳过此补丁,则执行 \"$cmdline --skip\"。\n"
+"要恢复原分支并停止打补丁,执行 \"$cmdline --abort\"。"
+
+#: git-am.sh:121
+msgid "Cannot fall back to three-way merge."
+msgstr "无法求助于三路合并。"
+
+#: git-am.sh:137
+msgid "Repository lacks necessary blobs to fall back on 3-way merge."
+msgstr "版本库缺乏必要的 blob 数据以进行三路合并。"
+
+#: git-am.sh:154
+msgid ""
+"Did you hand edit your patch?\n"
+"It does not apply to blobs recorded in its index."
+msgstr ""
+"您是否曾手动编辑过您的补丁?\n"
+"无法应用补丁到索引中的数据上。"
+
+#: git-am.sh:163
+msgid "Falling back to patching base and 3-way merge..."
+msgstr "转而在基础版本上打补丁及进行三路合并..."
+
+#: git-am.sh:275
+msgid "Only one StGIT patch series can be applied at once"
+msgstr "一次只能有一个 StGIT 补丁队列被应用"
+
+#: git-am.sh:362
+#, sh-format
+msgid "Patch format $patch_format is not supported."
+msgstr "不支持 $patch_format 补丁格式。"
+
+#: git-am.sh:364
+msgid "Patch format detection failed."
+msgstr "补丁格式检测失败。"
+
+#: git-am.sh:418
+msgid "-d option is no longer supported.  Do not use."
+msgstr "不再支持 -d 选项。不要使用。"
+
+#: git-am.sh:481
+#, sh-format
+msgid "previous rebase directory $dotest still exists but mbox given."
+msgstr "之前的变基目录 $dotest 仍然存在但给出了mbox。"
+
+#: git-am.sh:486
+msgid "Please make up your mind. --skip or --abort?"
+msgstr "请下决心。--skip 或是 --abort ?"
+
+#: git-am.sh:513
+msgid "Resolve operation not in progress, we are not resuming."
+msgstr "解决操作未进行,我们不会继续。"
+
+#: git-am.sh:579
+#, sh-format
+msgid "Dirty index: cannot apply patches (dirty: $files)"
+msgstr "脏的索引:不能应用补丁(脏文件:$files)"
+
+#: git-am.sh:671
+#, sh-format
+msgid ""
+"Patch is empty.  Was it split wrong?\n"
+"If you would prefer to skip this patch, instead run \"$cmdline --skip\".\n"
+"To restore the original branch and stop patching run \"$cmdline --abort\"."
+msgstr ""
+"补丁为空。是不是切分错误?\n"
+"如果您想要跳过这个补丁,执行 \"$cmdline --skip\"。\n"
+"要恢复原分支并停止打补丁,执行 \"$cmdline --abort\"。"
+
+#: git-am.sh:708
+msgid "Patch does not have a valid e-mail address."
+msgstr "补丁中没有一个有效的邮件地址。"
+
+#: git-am.sh:755
+msgid "cannot be interactive without stdin connected to a terminal."
+msgstr "标准输入没有和终端关联,不能进行交互式操作。"
+
+#: git-am.sh:759
+msgid "Commit Body is:"
+msgstr "提交内容为:"
+
+#  译者:注意保持句尾空格
+#. TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
+#. in your translation. The program will only accept English
+#. input at this point.
+#: git-am.sh:766
+msgid "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
+msgstr "应用?[y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
+
+#: git-am.sh:802
+#, sh-format
+msgid "Applying: $FIRSTLINE"
+msgstr "正应用:$FIRSTLINE"
+
+#: git-am.sh:823
+msgid ""
+"No changes - did you forget to use 'git add'?\n"
+"If there is nothing left to stage, chances are that something else\n"
+"already introduced the same changes; you might want to skip this patch."
+msgstr ""
+"没有变更 —— 您是不是忘了执行 'git add'?\n"
+"如果没有什么要添加到暂存区的,则很可能是其它提交已经引入了相同的变更。\n"
+"您也许想要跳过这个补丁。"
+
+#: git-am.sh:831
+msgid ""
+"You still have unmerged paths in your index\n"
+"did you forget to use 'git add'?"
+msgstr "您的索引中仍有未合并的路径。您是否忘了执行 'git add'?"
+
+#: git-am.sh:847
+msgid "No changes -- Patch already applied."
+msgstr "没有变更 -- 补丁已经应用过。"
+
+#: git-am.sh:857
+#, sh-format
+msgid "Patch failed at $msgnum $FIRSTLINE"
+msgstr "补丁失败于 $msgnum $FIRSTLINE"
+
+#: git-am.sh:873
+msgid "applying to an empty history"
+msgstr "正应用到一个空历史上"
+
+#: git-bisect.sh:48
+msgid "You need to start by \"git bisect start\""
+msgstr "您需要执行 \"git bisect start\" 来开始"
+
+#  译者:注意保持句尾空格
+#. TRANSLATORS: Make sure to include [Y] and [n] in your
+#. translation. The program will only accept English input
+#. at this point.
+#: git-bisect.sh:54
+msgid "Do you want me to do it for you [Y/n]? "
+msgstr "您想让我为您这样做么[Y/n]? "
+
+#: git-bisect.sh:95
+#, sh-format
+msgid "unrecognised option: '$arg'"
+msgstr "未能识别的选项:'$arg'"
+
+#: git-bisect.sh:99
+#, sh-format
+msgid "'$arg' does not appear to be a valid revision"
+msgstr "'$arg' 看起来不像是一个有效的版本"
+
+#: git-bisect.sh:117
+msgid "Bad HEAD - I need a HEAD"
+msgstr "坏的 HEAD - 我需要一个 HEAD"
+
+#: git-bisect.sh:130
+#, sh-format
+msgid ""
+"Checking out '$start_head' failed. Try 'git bisect reset <validbranch>'."
+msgstr "检出 '$start_head' 失败。尝试 'git bisect reset <validbranch>'。"
+
+#: git-bisect.sh:140
+msgid "won't bisect on seeked tree"
+msgstr "不会在已查找过的树上二分查找"
+
+#: git-bisect.sh:144
+msgid "Bad HEAD - strange symbolic ref"
+msgstr "坏的 HEAD - 奇怪的符号引用"
+
+#: git-bisect.sh:189
+#, sh-format
+msgid "Bad bisect_write argument: $state"
+msgstr "坏的 bisect_write 参数:$state"
+
+#: git-bisect.sh:218
+#, sh-format
+msgid "Bad rev input: $arg"
+msgstr "输入坏的版本:$arg"
+
+#: git-bisect.sh:232
+msgid "Please call 'bisect_state' with at least one argument."
+msgstr "请在调用 'bisect_state' 时跟至少一个参数。"
+
+#: git-bisect.sh:244
+#, sh-format
+msgid "Bad rev input: $rev"
+msgstr "输入坏的版本:$rev"
+
+#: git-bisect.sh:250
+msgid "'git bisect bad' can take only one argument."
+msgstr "'git bisect bad' 只能带一个参数。"
+
+#. have bad but not good.  we could bisect although
+#. this is less optimum.
+#: git-bisect.sh:273
+msgid "Warning: bisecting only with a bad commit."
+msgstr "警告:在仅有一个坏提交下进行二分查找。"
+
+#  译者:注意保持句尾空格
+#. TRANSLATORS: Make sure to include [Y] and [n] in your
+#. translation. The program will only accept English input
+#. at this point.
+#: git-bisect.sh:279
+msgid "Are you sure [Y/n]? "
+msgstr "您确认么[Y/n]? "
+
+#: git-bisect.sh:289
+msgid ""
+"You need to give me at least one good and one bad revisions.\n"
+"(You can use \"git bisect bad\" and \"git bisect good\" for that.)"
+msgstr ""
+"您需要给我至少一个好版本和一个坏版本。\n"
+"(您可以用 \"git bisect bad\" 和 \"git bisect good\" 命令来标识。)"
+
+#: git-bisect.sh:292
+msgid ""
+"You need to start by \"git bisect start\".\n"
+"You then need to give me at least one good and one bad revisions.\n"
+"(You can use \"git bisect bad\" and \"git bisect good\" for that.)"
+msgstr ""
+"您需要执行 \"git bisect start\" 来开始。\n"
+"然后需要提供我至少一个好版本和一个坏版本。\n"
+"(您可以用 \"git bisect bad\" 和 \"git bisect good\" 命令来标识。)"
+
+#: git-bisect.sh:347 git-bisect.sh:474
+msgid "We are not bisecting."
+msgstr "我们没有在二分查找。"
+
+#: git-bisect.sh:354
+#, sh-format
+msgid "'$invalid' is not a valid commit"
+msgstr "'$invalid' 不是一个有效的提交"
+
+#: git-bisect.sh:363
+#, sh-format
+msgid ""
+"Could not check out original HEAD '$branch'.\n"
+"Try 'git bisect reset <commit>'."
+msgstr ""
+"不能检出原始 HEAD '$branch'。\n"
+"尝试 'git bisect reset <commit>'。"
+
+#: git-bisect.sh:390
+msgid "No logfile given"
+msgstr "未提供日志文件"
+
+#: git-bisect.sh:391
+#, sh-format
+msgid "cannot read $file for replaying"
+msgstr "不能读取 $file 来重放"
+
+#: git-bisect.sh:408
+msgid "?? what are you talking about?"
+msgstr "?? 您在说什么?"
+
+#: git-bisect.sh:420
+#, sh-format
+msgid "running $command"
+msgstr "运行 $command"
+
+#: git-bisect.sh:427
+#, sh-format
+msgid ""
+"bisect run failed:\n"
+"exit code $res from '$command' is < 0 or >= 128"
+msgstr ""
+"二分查找运行失败:\n"
+"命令 '$command' 的退出码 $res 或者小于 0 或者大于等于 128"
+
+#: git-bisect.sh:453
+msgid "bisect run cannot continue any more"
+msgstr "二分查找不能继续运行"
+
+#: git-bisect.sh:459
+#, sh-format
+msgid ""
+"bisect run failed:\n"
+"'bisect_state $state' exited with error code $res"
+msgstr ""
+"二分查找运行失败:\n"
+"'bisect_state $state' 退出码为 $res"
+
+#: git-bisect.sh:466
+msgid "bisect run success"
+msgstr "二分查找运行成功"
+
+#: git-pull.sh:21
+msgid ""
+"Pull is not possible because you have unmerged files.\n"
+"Please, fix them up in the work tree, and then use 'git add/rm <file>'\n"
+"as appropriate to mark resolution, or use 'git commit -a'."
+msgstr ""
+"Pull 不可用,因为您尚有未合并的文件。请先在工作区改正文件,\n"
+"然后酌情使用 'git add/rm <file>' 标记解决方案,\n"
+"或者使用 'git commit -a'。"
+
+#: git-pull.sh:25
+msgid "Pull is not possible because you have unmerged files."
+msgstr "Pull 不可用,因为您尚有未合并的文件。"
+
+#: git-pull.sh:197
+msgid "updating an unborn branch with changes added to the index"
+msgstr "更新尚未诞生的分支,变更添加至索引"
+
+#. The fetch involved updating the current branch.
+#. The working tree and the index file is still based on the
+#. $orig_head commit, but we are merging into $curr_head.
+#. First update the working tree to match $curr_head.
+#: git-pull.sh:228
+#, sh-format
+msgid ""
+"Warning: fetch updated the current branch head.\n"
+"Warning: fast-forwarding your working tree from\n"
+"Warning: commit $orig_head."
+msgstr ""
+"警告:fetch 更新了当前的分支。您的工作区\n"
+"警告:从原提交 $orig_head 快进。"
+
+#: git-pull.sh:253
+msgid "Cannot merge multiple branches into empty head"
+msgstr "无法将多个分支合并到空分支"
+
+#: git-pull.sh:257
+msgid "Cannot rebase onto multiple branches"
+msgstr "无法变基到多个分支"
+
+#: git-stash.sh:51
+msgid "git stash clear with parameters is unimplemented"
+msgstr "git stash clear 不支持参数"
+
+#: git-stash.sh:74
+msgid "You do not have the initial commit yet"
+msgstr "您尚未建立初始提交"
+
+#: git-stash.sh:89
+msgid "Cannot save the current index state"
+msgstr "无法保存当前索引状态"
+
+#: git-stash.sh:123 git-stash.sh:136
+msgid "Cannot save the current worktree state"
+msgstr "无法保存当前工作区状态"
+
+#: git-stash.sh:140
+msgid "No changes selected"
+msgstr "没有选择变更"
+
+#: git-stash.sh:143
+msgid "Cannot remove temporary index (can't happen)"
+msgstr "无法删除临时索引(不应发生)"
+
+#: git-stash.sh:156
+msgid "Cannot record working tree state"
+msgstr "不能记录工作区状态"
+
+#. TRANSLATORS: $option is an invalid option, like
+#. `--blah-blah'. The 7 spaces at the beginning of the
+#. second line correspond to "error: ". So you should line
+#. up the second line with however many characters the
+#. translation of "error: " takes in your language. E.g. in
+#. English this is:
+#.
+#. $ git stash save --blah-blah 2>&1 | head -n 2
+#. error: unknown option for 'stash save': --blah-blah
+#. To provide a message, use git stash save -- '--blah-blah'
+#: git-stash.sh:202
+#, sh-format
+msgid ""
+"error: unknown option for 'stash save': $option\n"
+"       To provide a message, use git stash save -- '$option'"
+msgstr ""
+"错误:'stash save' 的未知选项:$option\n"
+"      要提供一个描述信息,使用 git stash save -- '$option'"
+
+#: git-stash.sh:223
+msgid "No local changes to save"
+msgstr "没有要保存的本地修改"
+
+#: git-stash.sh:227
+msgid "Cannot initialize stash"
+msgstr "无法初始化 stash"
+
+#: git-stash.sh:235
+msgid "Cannot save the current status"
+msgstr "无法保存当前状态"
+
+#: git-stash.sh:253
+msgid "Cannot remove worktree changes"
+msgstr "无法删除工作区变更"
+
+#: git-stash.sh:352
+msgid "No stash found."
+msgstr "未发现 stash。"
+
+#: git-stash.sh:359
+#, sh-format
+msgid "Too many revisions specified: $REV"
+msgstr "指定了太多的版本:$REV"
+
+#: git-stash.sh:365
+#, sh-format
+msgid "$reference is not valid reference"
+msgstr "$reference 不是有效的引用"
+
+#: git-stash.sh:393
+#, sh-format
+msgid "'$args' is not a stash-like commit"
+msgstr "'$args' 不是 stash 样提交"
+
+#: git-stash.sh:404
+#, sh-format
+msgid "'$args' is not a stash reference"
+msgstr "'$args' 不是一个 stash 引用"
+
+#: git-stash.sh:412
+msgid "unable to refresh index"
+msgstr "无法刷新索引"
+
+#: git-stash.sh:416
+msgid "Cannot apply a stash in the middle of a merge"
+msgstr "无法在合并过程中恢复进度"
+
+#: git-stash.sh:424
+msgid "Conflicts in index. Try without --index."
+msgstr "索引中有冲突。尝试不使用 --index。"
+
+#: git-stash.sh:426
+msgid "Could not save index tree"
+msgstr "不能保存索引树"
+
+#: git-stash.sh:460
+msgid "Cannot unstage modified files"
+msgstr "无法还原修改的文件"
+
+#: git-stash.sh:474
+msgid "Index was not unstashed."
+msgstr "索引的进度没有被恢复。"
+
+#: git-stash.sh:491
+#, sh-format
+msgid "Dropped ${REV} ($s)"
+msgstr "丢弃了 ${REV} ($s)"
+
+#: git-stash.sh:492
+#, sh-format
+msgid "${REV}: Could not drop stash entry"
+msgstr "${REV}:不能丢弃进度条目"
+
+#: git-stash.sh:499
+msgid "No branch name specified"
+msgstr "未指定分支名"
+
+#: git-stash.sh:570
+msgid "(To restore them type \"git stash apply\")"
+msgstr "(为恢复数据输入 \"git stash apply\")"
+
+#: git-submodule.sh:88
+#, sh-format
+msgid "cannot strip one component off url '$remoteurl'"
+msgstr "无法从 url '$remoteurl' 剥离一个组件"
+
+#: git-submodule.sh:145
+#, sh-format
+msgid "No submodule mapping found in .gitmodules for path '$sm_path'"
+msgstr "未在 .gitmodules 中发现路径 '$sm_path' 的子模组映射"
+
+#: git-submodule.sh:186
+#, sh-format
+msgid "Clone of '$url' into submodule path '$sm_path' failed"
+msgstr "无法克隆 '$url' 到子模组路径 '$sm_path'"
+
+#: git-submodule.sh:196
+#, sh-format
+msgid "Gitdir '$a' is part of the submodule path '$b' or vice versa"
+msgstr "Gitdir '$a' 在子模组路径 '$b' 之下或者相反"
+
+#: git-submodule.sh:285
+#, sh-format
+msgid "repo URL: '$repo' must be absolute or begin with ./|../"
+msgstr "版本库URL:'$repo' 必须是绝对路径或以 ./|../ 起始"
+
+#: git-submodule.sh:302
+#, sh-format
+msgid "'$sm_path' already exists in the index"
+msgstr "'$sm_path' 已经存在于索引中"
+
+#: git-submodule.sh:306
+#, sh-format
+msgid ""
+"The following path is ignored by one of your .gitignore files:\n"
+"$sm_path\n"
+"Use -f if you really want to add it."
+msgstr ""
+"以下路径被您的一个 .gitignore 文件所忽略:\n"
+"$sm_path\n"
+"如果您确实想添加它,使用 -f 参数。"
+
+#: git-submodule.sh:317
+#, sh-format
+msgid "Adding existing repo at '$sm_path' to the index"
+msgstr "添加位于 '$sm_path' 的现存版本库到索引"
+
+#: git-submodule.sh:319
+#, sh-format
+msgid "'$sm_path' already exists and is not a valid git repo"
+msgstr "'$sm_path' 已存在且不是一个有效的 git 版本库"
+
+#: git-submodule.sh:333
+#, sh-format
+msgid "Unable to checkout submodule '$sm_path'"
+msgstr "不能检出子模组 '$sm_path'"
+
+#: git-submodule.sh:338
+#, sh-format
+msgid "Failed to add submodule '$sm_path'"
+msgstr "无法添加子模组 '$sm_path'"
+
+#: git-submodule.sh:343
+#, sh-format
+msgid "Failed to register submodule '$sm_path'"
+msgstr "无法注册子模组 '$sm_path'"
+
+#: git-submodule.sh:385
+#, sh-format
+msgid "Entering '$prefix$sm_path'"
+msgstr "正在进入 '$prefix$sm_path'"
+
+#: git-submodule.sh:399
+#, sh-format
+msgid "Stopping at '$sm_path'; script returned non-zero status."
+msgstr "停止于 '$sm_path',脚本返回非零值。"
+
+#: git-submodule.sh:442
+#, sh-format
+msgid "No url found for submodule path '$sm_path' in .gitmodules"
+msgstr "在 .gitmodules 中未找到子模组路径 '$sm_path' 的 url"
+
+#: git-submodule.sh:451
+#, sh-format
+msgid "Failed to register url for submodule path '$sm_path'"
+msgstr "无法为子模组路径 '$sm_path' 注册 url"
+
+#: git-submodule.sh:453
+#, sh-format
+msgid "Submodule '$name' ($url) registered for path '$sm_path'"
+msgstr "子模组 '$name' ($url) 已为路径 '$sm_path' 注册"
+
+#: git-submodule.sh:461
+#, sh-format
+msgid "Failed to register update mode for submodule path '$sm_path'"
+msgstr "无法为子模组路径 '$sm_path' 注册更新模式"
+
+#: git-submodule.sh:560
+#, sh-format
+msgid ""
+"Submodule path '$sm_path' not initialized\n"
+"Maybe you want to use 'update --init'?"
+msgstr ""
+"子模组路径 '$sm_path' 没有初始化\n"
+"也许您想用 'update --init'?"
+
+#: git-submodule.sh:573
+#, sh-format
+msgid "Unable to find current revision in submodule path '$sm_path'"
+msgstr "无法在子模组路径 '$sm_path' 中找到当前版本"
+
+#: git-submodule.sh:592
+#, sh-format
+msgid "Unable to fetch in submodule path '$sm_path'"
+msgstr "无法在子模组路径 '$sm_path' 中获取"
+
+#: git-submodule.sh:606
+#, sh-format
+msgid "Unable to rebase '$sha1' in submodule path '$sm_path'"
+msgstr "无法在子模组路径 '$sm_path' 中变基 '$sha1'"
+
+#: git-submodule.sh:607
+#, sh-format
+msgid "Submodule path '$sm_path': rebased into '$sha1'"
+msgstr "子模组路径 '$sm_path':变基至 '$sha1'"
+
+#: git-submodule.sh:612
+#, sh-format
+msgid "Unable to merge '$sha1' in submodule path '$sm_path'"
+msgstr "无法合并 '$sha1' 到子模组路径 '$sm_path' 中"
+
+#: git-submodule.sh:613
+#, sh-format
+msgid "Submodule path '$sm_path': merged in '$sha1'"
+msgstr "子模组路径 '$sm_path':已合并入 '$sha1'"
+
+#: git-submodule.sh:618
+#, sh-format
+msgid "Unable to checkout '$sha1' in submodule path '$sm_path'"
+msgstr "无法在子模组路径 '$sm_path' 中检出 '$sha1'"
+
+#: git-submodule.sh:619
+#, sh-format
+msgid "Submodule path '$sm_path': checked out '$sha1'"
+msgstr "子模组路径 '$sm_path':检出 '$sha1'"
+
+#: git-submodule.sh:641 git-submodule.sh:964
+#, sh-format
+msgid "Failed to recurse into submodule path '$sm_path'"
+msgstr "无法递归进子模组路径 '$sm_path'"
+
+#: git-submodule.sh:749
+msgid "--cached cannot be used with --files"
+msgstr "--cached 不能和 --files 共用"
+
+#. unexpected type
+#: git-submodule.sh:789
+#, sh-format
+msgid "unexpected mode $mod_dst"
+msgstr "意外的模式 $mod_dst"
+
+#  译者:注意保持前导空格
+#: git-submodule.sh:807
+#, sh-format
+msgid "  Warn: $name doesn't contain commit $sha1_src"
+msgstr "  警告:$name 未包含提交 $sha1_src"
+
+#  译者:注意保持前导空格
+#: git-submodule.sh:810
+#, sh-format
+msgid "  Warn: $name doesn't contain commit $sha1_dst"
+msgstr "  警告:$name 未包含提交 $sha1_dst"
+
+#  译者:注意保持前导空格
+#: git-submodule.sh:813
+#, sh-format
+msgid "  Warn: $name doesn't contain commits $sha1_src and $sha1_dst"
+msgstr "  警告:$name 未包含提交 $sha1_src 和 $sha1_dst"
+
+#: git-submodule.sh:838
+msgid "blob"
+msgstr "blob"
+
+#: git-submodule.sh:839
+msgid "submodule"
+msgstr "子模组"
+
+#: git-submodule.sh:876
+msgid "# Submodules changed but not updated:"
+msgstr "# 子模组已修改但尚未更新:"
+
+#: git-submodule.sh:878
+msgid "# Submodule changes to be committed:"
+msgstr "要提交的子模组变更:"
+
+#: git-submodule.sh:1022
+#, sh-format
+msgid "Synchronizing submodule url for '$name'"
+msgstr "为 '$name' 同步子模组 url"
diff --git a/preload-index.c b/preload-index.c
index 9289933..49cb08d 100644
--- a/preload-index.c
+++ b/preload-index.c
@@ -35,7 +35,9 @@
 	struct index_state *index = p->index;
 	struct cache_entry **cep = index->cache + p->offset;
 	struct cache_def cache;
+	struct pathspec pathspec;
 
+	init_pathspec(&pathspec, p->pathspec);
 	memset(&cache, 0, sizeof(cache));
 	nr = p->nr;
 	if (nr + p->offset > index->cache_nr)
@@ -47,9 +49,11 @@
 
 		if (ce_stage(ce))
 			continue;
+		if (S_ISGITLINK(ce->ce_mode))
+			continue;
 		if (ce_uptodate(ce))
 			continue;
-		if (!ce_path_match(ce, p->pathspec))
+		if (!ce_path_match(ce, &pathspec))
 			continue;
 		if (threaded_has_symlink_leading_path(&cache, ce->name, ce_namelen(ce)))
 			continue;
@@ -59,6 +63,7 @@
 			continue;
 		ce_mark_uptodate(ce);
 	} while (--nr > 0);
+	free_pathspec(&pathspec);
 	return NULL;
 }
 
diff --git a/pretty.c b/pretty.c
index e5328da..8b1ea9f 100644
--- a/pretty.c
+++ b/pretty.c
@@ -6,9 +6,23 @@
 #include "string-list.h"
 #include "mailmap.h"
 #include "log-tree.h"
+#include "notes.h"
 #include "color.h"
+#include "reflog-walk.h"
+#include "gpg-interface.h"
 
 static char *user_format;
+static struct cmt_fmt_map {
+	const char *name;
+	enum cmit_fmt format;
+	int is_tformat;
+	int is_alias;
+	const char *user_format;
+} *commit_formats;
+static size_t builtin_formats_len;
+static size_t commit_formats_len;
+static size_t commit_formats_alloc;
+static struct cmt_fmt_map *find_commit_format(const char *sought);
 
 static void save_user_format(struct rev_info *rev, const char *cp, int is_tformat)
 {
@@ -19,22 +33,118 @@
 	rev->commit_format = CMIT_FMT_USERFORMAT;
 }
 
+static int git_pretty_formats_config(const char *var, const char *value, void *cb)
+{
+	struct cmt_fmt_map *commit_format = NULL;
+	const char *name;
+	const char *fmt;
+	int i;
+
+	if (prefixcmp(var, "pretty."))
+		return 0;
+
+	name = var + strlen("pretty.");
+	for (i = 0; i < builtin_formats_len; i++) {
+		if (!strcmp(commit_formats[i].name, name))
+			return 0;
+	}
+
+	for (i = builtin_formats_len; i < commit_formats_len; i++) {
+		if (!strcmp(commit_formats[i].name, name)) {
+			commit_format = &commit_formats[i];
+			break;
+		}
+	}
+
+	if (!commit_format) {
+		ALLOC_GROW(commit_formats, commit_formats_len+1,
+			   commit_formats_alloc);
+		commit_format = &commit_formats[commit_formats_len];
+		memset(commit_format, 0, sizeof(*commit_format));
+		commit_formats_len++;
+	}
+
+	commit_format->name = xstrdup(name);
+	commit_format->format = CMIT_FMT_USERFORMAT;
+	git_config_string(&fmt, var, value);
+	if (!prefixcmp(fmt, "format:") || !prefixcmp(fmt, "tformat:")) {
+		commit_format->is_tformat = fmt[0] == 't';
+		fmt = strchr(fmt, ':') + 1;
+	} else if (strchr(fmt, '%'))
+		commit_format->is_tformat = 1;
+	else
+		commit_format->is_alias = 1;
+	commit_format->user_format = fmt;
+
+	return 0;
+}
+
+static void setup_commit_formats(void)
+{
+	struct cmt_fmt_map builtin_formats[] = {
+		{ "raw",	CMIT_FMT_RAW,		0 },
+		{ "medium",	CMIT_FMT_MEDIUM,	0 },
+		{ "short",	CMIT_FMT_SHORT,		0 },
+		{ "email",	CMIT_FMT_EMAIL,		0 },
+		{ "fuller",	CMIT_FMT_FULLER,	0 },
+		{ "full",	CMIT_FMT_FULL,		0 },
+		{ "oneline",	CMIT_FMT_ONELINE,	1 }
+	};
+	commit_formats_len = ARRAY_SIZE(builtin_formats);
+	builtin_formats_len = commit_formats_len;
+	ALLOC_GROW(commit_formats, commit_formats_len, commit_formats_alloc);
+	memcpy(commit_formats, builtin_formats,
+	       sizeof(*builtin_formats)*ARRAY_SIZE(builtin_formats));
+
+	git_config(git_pretty_formats_config, NULL);
+}
+
+static struct cmt_fmt_map *find_commit_format_recursive(const char *sought,
+							const char *original,
+							int num_redirections)
+{
+	struct cmt_fmt_map *found = NULL;
+	size_t found_match_len = 0;
+	int i;
+
+	if (num_redirections >= commit_formats_len)
+		die("invalid --pretty format: "
+		    "'%s' references an alias which points to itself",
+		    original);
+
+	for (i = 0; i < commit_formats_len; i++) {
+		size_t match_len;
+
+		if (prefixcmp(commit_formats[i].name, sought))
+			continue;
+
+		match_len = strlen(commit_formats[i].name);
+		if (found == NULL || found_match_len > match_len) {
+			found = &commit_formats[i];
+			found_match_len = match_len;
+		}
+	}
+
+	if (found && found->is_alias) {
+		found = find_commit_format_recursive(found->user_format,
+						     original,
+						     num_redirections+1);
+	}
+
+	return found;
+}
+
+static struct cmt_fmt_map *find_commit_format(const char *sought)
+{
+	if (!commit_formats)
+		setup_commit_formats();
+
+	return find_commit_format_recursive(sought, sought, 0);
+}
+
 void get_commit_format(const char *arg, struct rev_info *rev)
 {
-	int i;
-	static struct cmt_fmt_map {
-		const char *n;
-		size_t cmp_len;
-		enum cmit_fmt v;
-	} cmt_fmts[] = {
-		{ "raw",	1,	CMIT_FMT_RAW },
-		{ "medium",	1,	CMIT_FMT_MEDIUM },
-		{ "short",	1,	CMIT_FMT_SHORT },
-		{ "email",	1,	CMIT_FMT_EMAIL },
-		{ "full",	5,	CMIT_FMT_FULL },
-		{ "fuller",	5,	CMIT_FMT_FULLER },
-		{ "oneline",	1,	CMIT_FMT_ONELINE },
-	};
+	struct cmt_fmt_map *commit_format;
 
 	rev->use_terminator = 0;
 	if (!arg || !*arg) {
@@ -45,21 +155,22 @@
 		save_user_format(rev, strchr(arg, ':') + 1, arg[0] == 't');
 		return;
 	}
-	for (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) {
-		if (!strncmp(arg, cmt_fmts[i].n, cmt_fmts[i].cmp_len) &&
-		    !strncmp(arg, cmt_fmts[i].n, strlen(arg))) {
-			if (cmt_fmts[i].v == CMIT_FMT_ONELINE)
-				rev->use_terminator = 1;
-			rev->commit_format = cmt_fmts[i].v;
-			return;
-		}
-	}
+
 	if (strchr(arg, '%')) {
 		save_user_format(rev, arg, 1);
 		return;
 	}
 
-	die("invalid --pretty format: %s", arg);
+	commit_format = find_commit_format(arg);
+	if (!commit_format)
+		die("invalid --pretty format: %s", arg);
+
+	rev->commit_format = commit_format->format;
+	rev->use_terminator = commit_format->is_tformat;
+	if (commit_format->format == CMIT_FMT_USERFORMAT) {
+		save_user_format(rev, commit_format->user_format,
+				 commit_format->is_tformat);
+	}
 }
 
 /*
@@ -81,11 +192,75 @@
 }
 
 /* High bit set, or ISO-2022-INT */
-int non_ascii(int ch)
+static int non_ascii(int ch)
 {
 	return !isascii(ch) || ch == '\033';
 }
 
+int has_non_ascii(const char *s)
+{
+	int ch;
+	if (!s)
+		return 0;
+	while ((ch = *s++) != '\0') {
+		if (non_ascii(ch))
+			return 1;
+	}
+	return 0;
+}
+
+static int is_rfc822_special(char ch)
+{
+	switch (ch) {
+	case '(':
+	case ')':
+	case '<':
+	case '>':
+	case '[':
+	case ']':
+	case ':':
+	case ';':
+	case '@':
+	case ',':
+	case '.':
+	case '"':
+	case '\\':
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+static int has_rfc822_specials(const char *s, int len)
+{
+	int i;
+	for (i = 0; i < len; i++)
+		if (is_rfc822_special(s[i]))
+			return 1;
+	return 0;
+}
+
+static void add_rfc822_quoted(struct strbuf *out, const char *s, int len)
+{
+	int i;
+
+	/* just a guess, we may have to also backslash-quote */
+	strbuf_grow(out, len + 2);
+
+	strbuf_addch(out, '"');
+	for (i = 0; i < len; i++) {
+		switch (s[i]) {
+		case '"':
+		case '\\':
+			strbuf_addch(out, '\\');
+			/* fall through */
+		default:
+			strbuf_addch(out, s[i]);
+		}
+	}
+	strbuf_addch(out, '"');
+}
+
 static int is_rfc2047_special(char ch)
 {
 	return (non_ascii(ch) || (ch == '=') || (ch == '?') || (ch == '_'));
@@ -94,49 +269,66 @@
 static void add_rfc2047(struct strbuf *sb, const char *line, int len,
 		       const char *encoding)
 {
-	int i, last;
+	static const int max_length = 78; /* per rfc2822 */
+	int i;
+	int line_len;
+
+	/* How many bytes are already used on the current line? */
+	for (i = sb->len - 1; i >= 0; i--)
+		if (sb->buf[i] == '\n')
+			break;
+	line_len = sb->len - (i+1);
 
 	for (i = 0; i < len; i++) {
 		int ch = line[i];
-		if (non_ascii(ch))
+		if (non_ascii(ch) || ch == '\n')
 			goto needquote;
 		if ((i + 1 < len) && (ch == '=' && line[i+1] == '?'))
 			goto needquote;
 	}
-	strbuf_add(sb, line, len);
+	strbuf_add_wrapped_bytes(sb, line, len, 0, 1, max_length - line_len);
 	return;
 
 needquote:
 	strbuf_grow(sb, len * 3 + strlen(encoding) + 100);
 	strbuf_addf(sb, "=?%s?q?", encoding);
-	for (i = last = 0; i < len; i++) {
+	line_len += strlen(encoding) + 5; /* 5 for =??q? */
+	for (i = 0; i < len; i++) {
 		unsigned ch = line[i] & 0xFF;
+
+		if (line_len >= max_length - 2) {
+			strbuf_addf(sb, "?=\n =?%s?q?", encoding);
+			line_len = strlen(encoding) + 5 + 1; /* =??q? plus SP */
+		}
+
 		/*
 		 * We encode ' ' using '=20' even though rfc2047
 		 * allows using '_' for readability.  Unfortunately,
 		 * many programs do not understand this and just
 		 * leave the underscore in place.
 		 */
-		if (is_rfc2047_special(ch) || ch == ' ') {
-			strbuf_add(sb, line + last, i - last);
+		if (is_rfc2047_special(ch) || ch == ' ' || ch == '\n') {
 			strbuf_addf(sb, "=%02X", ch);
-			last = i + 1;
+			line_len += 3;
+		}
+		else {
+			strbuf_addch(sb, ch);
+			line_len++;
 		}
 	}
-	strbuf_add(sb, line + last, len - last);
 	strbuf_addstr(sb, "?=");
 }
 
-void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
-		  const char *line, enum date_mode dmode,
-		  const char *encoding)
+void pp_user_info(const struct pretty_print_context *pp,
+		  const char *what, struct strbuf *sb,
+		  const char *line, const char *encoding)
 {
 	char *date;
 	int namelen;
 	unsigned long time;
 	int tz;
 
-	if (fmt == CMIT_FMT_ONELINE)
+	if (pp->fmt == CMIT_FMT_ONELINE)
 		return;
 	date = strchr(line, '>');
 	if (!date)
@@ -145,32 +337,48 @@
 	time = strtoul(date, &date, 10);
 	tz = strtol(date, NULL, 10);
 
-	if (fmt == CMIT_FMT_EMAIL) {
+	if (pp->fmt == CMIT_FMT_EMAIL) {
 		char *name_tail = strchr(line, '<');
 		int display_name_length;
+		int final_line;
 		if (!name_tail)
 			return;
 		while (line < name_tail && isspace(name_tail[-1]))
 			name_tail--;
 		display_name_length = name_tail - line;
 		strbuf_addstr(sb, "From: ");
-		add_rfc2047(sb, line, display_name_length, encoding);
+		if (!has_rfc822_specials(line, display_name_length)) {
+			add_rfc2047(sb, line, display_name_length, encoding);
+		} else {
+			struct strbuf quoted = STRBUF_INIT;
+			add_rfc822_quoted(&quoted, line, display_name_length);
+			add_rfc2047(sb, quoted.buf, quoted.len, encoding);
+			strbuf_release(&quoted);
+		}
+		for (final_line = 0; final_line < sb->len; final_line++)
+			if (sb->buf[sb->len - final_line - 1] == '\n')
+				break;
+		if (namelen - display_name_length + final_line > 78) {
+			strbuf_addch(sb, '\n');
+			if (!isspace(name_tail[0]))
+				strbuf_addch(sb, ' ');
+		}
 		strbuf_add(sb, name_tail, namelen - display_name_length);
 		strbuf_addch(sb, '\n');
 	} else {
 		strbuf_addf(sb, "%s: %.*s%.*s\n", what,
-			      (fmt == CMIT_FMT_FULLER) ? 4 : 0,
+			      (pp->fmt == CMIT_FMT_FULLER) ? 4 : 0,
 			      "    ", namelen, line);
 	}
-	switch (fmt) {
+	switch (pp->fmt) {
 	case CMIT_FMT_MEDIUM:
-		strbuf_addf(sb, "Date:   %s\n", show_date(time, tz, dmode));
+		strbuf_addf(sb, "Date:   %s\n", show_date(time, tz, pp->date_mode));
 		break;
 	case CMIT_FMT_EMAIL:
 		strbuf_addf(sb, "Date: %s\n", show_date(time, tz, DATE_RFC2822));
 		break;
 	case CMIT_FMT_FULLER:
-		strbuf_addf(sb, "%sDate: %s\n", what, show_date(time, tz, dmode));
+		strbuf_addf(sb, "%sDate: %s\n", what, show_date(time, tz, pp->date_mode));
 		break;
 	default:
 		/* notin' */
@@ -201,12 +409,12 @@
 	return msg;
 }
 
-static void add_merge_info(enum cmit_fmt fmt, struct strbuf *sb,
-			const struct commit *commit, int abbrev)
+static void add_merge_info(const struct pretty_print_context *pp,
+			   struct strbuf *sb, const struct commit *commit)
 {
 	struct commit_list *parent = commit->parents;
 
-	if ((fmt == CMIT_FMT_ONELINE) || (fmt == CMIT_FMT_EMAIL) ||
+	if ((pp->fmt == CMIT_FMT_ONELINE) || (pp->fmt == CMIT_FMT_EMAIL) ||
 	    !parent || !parent->next)
 		return;
 
@@ -215,8 +423,8 @@
 	while (parent) {
 		struct commit *p = parent->item;
 		const char *hex = NULL;
-		if (abbrev)
-			hex = find_unique_abbrev(p->object.sha1, abbrev);
+		if (pp->abbrev)
+			hex = find_unique_abbrev(p->object.sha1, pp->abbrev);
 		if (!hex)
 			hex = sha1_to_hex(p->object.sha1);
 		parent = parent->next;
@@ -231,12 +439,14 @@
 	int key_len = strlen(key);
 	const char *line = commit->buffer;
 
-	for (;;) {
+	while (line) {
 		const char *eol = strchr(line, '\n'), *next;
 
 		if (line == eol)
 			return NULL;
 		if (!eol) {
+			warning("malformed commit (header is missing newline): %s",
+				sha1_to_hex(commit->object.sha1));
 			eol = line + strlen(line);
 			next = NULL;
 		} else
@@ -248,6 +458,7 @@
 		}
 		line = next;
 	}
+	return NULL;
 }
 
 static char *replace_encoding_header(char *buf, const char *encoding)
@@ -281,8 +492,8 @@
 	return strbuf_detach(&tmp, NULL);
 }
 
-static char *logmsg_reencode(const struct commit *commit,
-			     const char *output_encoding)
+char *logmsg_reencode(const struct commit *commit,
+		      const char *output_encoding)
 {
 	static const char *utf8 = "UTF-8";
 	const char *use_encoding;
@@ -323,41 +534,26 @@
 {
 	/* currently all placeholders have same length */
 	const int placeholder_len = 2;
-	int start, end, tz = 0;
+	int tz;
 	unsigned long date = 0;
-	char *ep;
-	const char *name_start, *name_end, *mail_start, *mail_end, *msg_end = msg+len;
 	char person_name[1024];
 	char person_mail[1024];
+	struct ident_split s;
+	const char *name_start, *name_end, *mail_start, *mail_end;
 
-	/* advance 'end' to point to email start delimiter */
-	for (end = 0; end < len && msg[end] != '<'; end++)
-		; /* do nothing */
-
-	/*
-	 * When end points at the '<' that we found, it should have
-	 * matching '>' later, which means 'end' must be strictly
-	 * below len - 1.
-	 */
-	if (end >= len - 2)
+	if (split_ident_line(&s, msg, len) < 0)
 		goto skip;
 
-	/* Seek for both name and email part */
-	name_start = msg;
-	name_end = msg+end;
-	while (name_end > name_start && isspace(*(name_end-1)))
-		name_end--;
-	mail_start = msg+end+1;
-	mail_end = mail_start;
-	while (mail_end < msg_end && *mail_end != '>')
-		mail_end++;
-	if (mail_end == msg_end)
-		goto skip;
-	end = mail_end-msg;
+	name_start = s.name_begin;
+	name_end = s.name_end;
+	mail_start = s.mail_begin;
+	mail_end = s.mail_end;
 
 	if (part == 'N' || part == 'E') { /* mailmap lookup */
-		strlcpy(person_name, name_start, name_end-name_start+1);
-		strlcpy(person_mail, mail_start, mail_end-mail_start+1);
+		snprintf(person_name, sizeof(person_name), "%.*s",
+			 (int)(name_end - name_start), name_start);
+		snprintf(person_mail, sizeof(person_mail), "%.*s",
+			 (int)(mail_end - mail_start), mail_start);
 		mailmap_name(person_mail, sizeof(person_mail), person_name, sizeof(person_name));
 		name_start = person_name;
 		name_end = name_start + strlen(person_name);
@@ -373,28 +569,20 @@
 		return placeholder_len;
 	}
 
-	/* advance 'start' to point to date start delimiter */
-	for (start = end + 1; start < len && isspace(msg[start]); start++)
-		; /* do nothing */
-	if (start >= len)
-		goto skip;
-	date = strtoul(msg + start, &ep, 10);
-	if (msg + start == ep)
+	if (!s.date_begin)
 		goto skip;
 
+	date = strtoul(s.date_begin, NULL, 10);
+
 	if (part == 't') {	/* date, UNIX timestamp */
-		strbuf_add(sb, msg + start, ep - (msg + start));
+		strbuf_add(sb, s.date_begin, s.date_end - s.date_begin);
 		return placeholder_len;
 	}
 
 	/* parse tz */
-	for (start = ep - msg + 1; start < len && isspace(msg[start]); start++)
-		; /* do nothing */
-	if (start + 1 < len) {
-		tz = strtoul(msg + start + 1, NULL, 10);
-		if (msg[start] == '-')
-			tz = -tz;
-	}
+	tz = strtoul(s.tz_begin + 1, NULL, 10);
+	if (*s.tz_begin == '-')
+		tz = -tz;
 
 	switch (part) {
 	case 'd':	/* date */
@@ -413,8 +601,9 @@
 
 skip:
 	/*
-	 * bogus commit, 'sb' cannot be updated, but we still need to
-	 * compute a valid return value.
+	 * reading from either a bogus commit, or a reflog entry with
+	 * %gn, %ge, etc.; 'sb' cannot be updated, but we still need
+	 * to compute a valid return value.
 	 */
 	if (part == 'n' || part == 'e' || part == 't' || part == 'd'
 	    || part == 'D' || part == 'r' || part == 'i')
@@ -430,9 +619,17 @@
 
 struct format_commit_context {
 	const struct commit *commit;
-	enum date_mode dmode;
+	const struct pretty_print_context *pretty_ctx;
 	unsigned commit_header_parsed:1;
 	unsigned commit_message_parsed:1;
+	unsigned commit_signature_parsed:1;
+	struct {
+		char *gpg_output;
+		char good_bad;
+		char *signer;
+	} signature;
+	char *message;
+	size_t width, indent1, indent2;
 
 	/* These offsets are relative to the start of the commit message. */
 	struct chunk author;
@@ -446,6 +643,7 @@
 	struct chunk abbrev_commit_hash;
 	struct chunk abbrev_tree_hash;
 	struct chunk abbrev_parent_hashes;
+	size_t wrap_start;
 };
 
 static int add_again(struct strbuf *sb, struct chunk *chunk)
@@ -467,7 +665,7 @@
 
 static void parse_commit_header(struct format_commit_context *context)
 {
-	const char *msg = context->commit->buffer;
+	const char *msg = context->message;
 	int i;
 
 	for (i = 0; msg[i]; i++) {
@@ -553,8 +751,8 @@
 
 static void parse_commit_message(struct format_commit_context *c)
 {
-	const char *msg = c->commit->buffer + c->message_off;
-	const char *start = c->commit->buffer;
+	const char *msg = c->message + c->message_off;
+	const char *start = c->message;
 
 	msg = skip_empty_lines(msg);
 	c->subject_off = msg - start;
@@ -571,7 +769,7 @@
 	struct name_decoration *d;
 	const char *prefix = " (";
 
-	load_ref_decorations();
+	load_ref_decorations(DECORATE_SHORT_REFS);
 	d = lookup_decoration(&name_decoration, &commit->object);
 	while (d) {
 		strbuf_addstr(sb, prefix);
@@ -583,12 +781,111 @@
 		strbuf_addch(sb, ')');
 }
 
-static size_t format_commit_item(struct strbuf *sb, const char *placeholder,
-                               void *context)
+static void strbuf_wrap(struct strbuf *sb, size_t pos,
+			size_t width, size_t indent1, size_t indent2)
+{
+	struct strbuf tmp = STRBUF_INIT;
+
+	if (pos)
+		strbuf_add(&tmp, sb->buf, pos);
+	strbuf_add_wrapped_text(&tmp, sb->buf + pos,
+				(int) indent1, (int) indent2, (int) width);
+	strbuf_swap(&tmp, sb);
+	strbuf_release(&tmp);
+}
+
+static void rewrap_message_tail(struct strbuf *sb,
+				struct format_commit_context *c,
+				size_t new_width, size_t new_indent1,
+				size_t new_indent2)
+{
+	if (c->width == new_width && c->indent1 == new_indent1 &&
+	    c->indent2 == new_indent2)
+		return;
+	if (c->wrap_start < sb->len)
+		strbuf_wrap(sb, c->wrap_start, c->width, c->indent1, c->indent2);
+	c->wrap_start = sb->len;
+	c->width = new_width;
+	c->indent1 = new_indent1;
+	c->indent2 = new_indent2;
+}
+
+static struct {
+	char result;
+	const char *check;
+} signature_check[] = {
+	{ 'G', ": Good signature from " },
+	{ 'B', ": BAD signature from " },
+};
+
+static void parse_signature_lines(struct format_commit_context *ctx)
+{
+	const char *buf = ctx->signature.gpg_output;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(signature_check); i++) {
+		const char *found = strstr(buf, signature_check[i].check);
+		const char *next;
+		if (!found)
+			continue;
+		ctx->signature.good_bad = signature_check[i].result;
+		found += strlen(signature_check[i].check);
+		next = strchrnul(found, '\n');
+		ctx->signature.signer = xmemdupz(found, next - found);
+		break;
+	}
+}
+
+static void parse_commit_signature(struct format_commit_context *ctx)
+{
+	struct strbuf payload = STRBUF_INIT;
+	struct strbuf signature = STRBUF_INIT;
+	struct strbuf gpg_output = STRBUF_INIT;
+	int status;
+
+	ctx->commit_signature_parsed = 1;
+
+	if (parse_signed_commit(ctx->commit->object.sha1,
+				&payload, &signature) <= 0)
+		goto out;
+	status = verify_signed_buffer(payload.buf, payload.len,
+				      signature.buf, signature.len,
+				      &gpg_output);
+	if (status && !gpg_output.len)
+		goto out;
+	ctx->signature.gpg_output = strbuf_detach(&gpg_output, NULL);
+	parse_signature_lines(ctx);
+
+ out:
+	strbuf_release(&gpg_output);
+	strbuf_release(&payload);
+	strbuf_release(&signature);
+}
+
+
+static int format_reflog_person(struct strbuf *sb,
+				char part,
+				struct reflog_walk_info *log,
+				enum date_mode dmode)
+{
+	const char *ident;
+
+	if (!log)
+		return 2;
+
+	ident = get_reflog_ident(log);
+	if (!ident)
+		return 2;
+
+	return format_person_part(sb, part, ident, strlen(ident), dmode);
+}
+
+static size_t format_commit_one(struct strbuf *sb, const char *placeholder,
+				void *context)
 {
 	struct format_commit_context *c = context;
 	const struct commit *commit = c->commit;
-	const char *msg = commit->buffer;
+	const char *msg = c->message;
 	struct commit_list *p;
 	int h1, h2;
 
@@ -633,6 +930,30 @@
 			return 3;
 		} else
 			return 0;
+	case 'w':
+		if (placeholder[1] == '(') {
+			unsigned long width = 0, indent1 = 0, indent2 = 0;
+			char *next;
+			const char *start = placeholder + 2;
+			const char *end = strchr(start, ')');
+			if (!end)
+				return 0;
+			if (end > start) {
+				width = strtoul(start, &next, 10);
+				if (*next == ',') {
+					indent1 = strtoul(next + 1, &next, 10);
+					if (*next == ',') {
+						indent2 = strtoul(next + 1,
+								 &next, 10);
+					}
+				}
+				if (*next != ')')
+					return 0;
+			}
+			rewrap_message_tail(sb, c, width, indent1, indent2);
+			return end - placeholder + 1;
+		} else
+			return 0;
 	}
 
 	/* these depend on the commit */
@@ -647,7 +968,7 @@
 		if (add_again(sb, &c->abbrev_commit_hash))
 			return 1;
 		strbuf_addstr(sb, find_unique_abbrev(commit->object.sha1,
-		                                     DEFAULT_ABBREV));
+						     c->pretty_ctx->abbrev));
 		c->abbrev_commit_hash.len = sb->len - c->abbrev_commit_hash.off;
 		return 1;
 	case 'T':		/* tree hash */
@@ -657,7 +978,7 @@
 		if (add_again(sb, &c->abbrev_tree_hash))
 			return 1;
 		strbuf_addstr(sb, find_unique_abbrev(commit->tree->object.sha1,
-		                                     DEFAULT_ABBREV));
+						     c->pretty_ctx->abbrev));
 		c->abbrev_tree_hash.len = sb->len - c->abbrev_tree_hash.off;
 		return 1;
 	case 'P':		/* parent hashes */
@@ -674,23 +995,76 @@
 			if (p != commit->parents)
 				strbuf_addch(sb, ' ');
 			strbuf_addstr(sb, find_unique_abbrev(
-					p->item->object.sha1, DEFAULT_ABBREV));
+					p->item->object.sha1,
+					c->pretty_ctx->abbrev));
 		}
 		c->abbrev_parent_hashes.len = sb->len -
 		                              c->abbrev_parent_hashes.off;
 		return 1;
 	case 'm':		/* left/right/bottom */
-		strbuf_addch(sb, (commit->object.flags & BOUNDARY)
-		                 ? '-'
-		                 : (commit->object.flags & SYMMETRIC_LEFT)
-		                 ? '<'
-		                 : '>');
+		strbuf_addstr(sb, get_revision_mark(NULL, commit));
 		return 1;
 	case 'd':
 		format_decoration(sb, commit);
 		return 1;
+	case 'g':		/* reflog info */
+		switch(placeholder[1]) {
+		case 'd':	/* reflog selector */
+		case 'D':
+			if (c->pretty_ctx->reflog_info)
+				get_reflog_selector(sb,
+						    c->pretty_ctx->reflog_info,
+						    c->pretty_ctx->date_mode,
+						    c->pretty_ctx->date_mode_explicit,
+						    (placeholder[1] == 'd'));
+			return 2;
+		case 's':	/* reflog message */
+			if (c->pretty_ctx->reflog_info)
+				get_reflog_message(sb, c->pretty_ctx->reflog_info);
+			return 2;
+		case 'n':
+		case 'N':
+		case 'e':
+		case 'E':
+			return format_reflog_person(sb,
+						    placeholder[1],
+						    c->pretty_ctx->reflog_info,
+						    c->pretty_ctx->date_mode);
+		}
+		return 0;	/* unknown %g placeholder */
+	case 'N':
+		if (c->pretty_ctx->show_notes) {
+			format_display_notes(commit->object.sha1, sb,
+				    get_log_output_encoding(), 0);
+			return 1;
+		}
+		return 0;
 	}
 
+	if (placeholder[0] == 'G') {
+		if (!c->commit_signature_parsed)
+			parse_commit_signature(c);
+		switch (placeholder[1]) {
+		case 'G':
+			if (c->signature.gpg_output)
+				strbuf_addstr(sb, c->signature.gpg_output);
+			break;
+		case '?':
+			switch (c->signature.good_bad) {
+			case 'G':
+			case 'B':
+				strbuf_addch(sb, c->signature.good_bad);
+			}
+			break;
+		case 'S':
+			if (c->signature.signer)
+				strbuf_addstr(sb, c->signature.signer);
+			break;
+		}
+		return 2;
+	}
+
+
 	/* For the rest we have to parse the commit header. */
 	if (!c->commit_header_parsed)
 		parse_commit_header(c);
@@ -699,14 +1073,18 @@
 	case 'a':	/* author ... */
 		return format_person_part(sb, placeholder[1],
 				   msg + c->author.off, c->author.len,
-				   c->dmode);
+				   c->pretty_ctx->date_mode);
 	case 'c':	/* committer ... */
 		return format_person_part(sb, placeholder[1],
 				   msg + c->committer.off, c->committer.len,
-				   c->dmode);
+				   c->pretty_ctx->date_mode);
 	case 'e':	/* encoding */
 		strbuf_add(sb, msg + c->encoding.off, c->encoding.len);
 		return 1;
+	case 'B':	/* raw body */
+		/* message_off is always left at the initial newline */
+		strbuf_addstr(sb, msg + c->message_off + 1);
+		return 1;
 	}
 
 	/* Now we need to parse the commit message. */
@@ -727,21 +1105,113 @@
 	return 0;	/* unknown placeholder */
 }
 
+static size_t format_commit_item(struct strbuf *sb, const char *placeholder,
+				 void *context)
+{
+	int consumed;
+	size_t orig_len;
+	enum {
+		NO_MAGIC,
+		ADD_LF_BEFORE_NON_EMPTY,
+		DEL_LF_BEFORE_EMPTY,
+		ADD_SP_BEFORE_NON_EMPTY
+	} magic = NO_MAGIC;
+
+	switch (placeholder[0]) {
+	case '-':
+		magic = DEL_LF_BEFORE_EMPTY;
+		break;
+	case '+':
+		magic = ADD_LF_BEFORE_NON_EMPTY;
+		break;
+	case ' ':
+		magic = ADD_SP_BEFORE_NON_EMPTY;
+		break;
+	default:
+		break;
+	}
+	if (magic != NO_MAGIC)
+		placeholder++;
+
+	orig_len = sb->len;
+	consumed = format_commit_one(sb, placeholder, context);
+	if (magic == NO_MAGIC)
+		return consumed;
+
+	if ((orig_len == sb->len) && magic == DEL_LF_BEFORE_EMPTY) {
+		while (sb->len && sb->buf[sb->len - 1] == '\n')
+			strbuf_setlen(sb, sb->len - 1);
+	} else if (orig_len != sb->len) {
+		if (magic == ADD_LF_BEFORE_NON_EMPTY)
+			strbuf_insert(sb, orig_len, "\n", 1);
+		else if (magic == ADD_SP_BEFORE_NON_EMPTY)
+			strbuf_insert(sb, orig_len, " ", 1);
+	}
+	return consumed + 1;
+}
+
+static size_t userformat_want_item(struct strbuf *sb, const char *placeholder,
+				   void *context)
+{
+	struct userformat_want *w = context;
+
+	if (*placeholder == '+' || *placeholder == '-' || *placeholder == ' ')
+		placeholder++;
+
+	switch (*placeholder) {
+	case 'N':
+		w->notes = 1;
+		break;
+	}
+	return 0;
+}
+
+void userformat_find_requirements(const char *fmt, struct userformat_want *w)
+{
+	struct strbuf dummy = STRBUF_INIT;
+
+	if (!fmt) {
+		if (!user_format)
+			return;
+		fmt = user_format;
+	}
+	strbuf_expand(&dummy, fmt, userformat_want_item, w);
+	strbuf_release(&dummy);
+}
+
 void format_commit_message(const struct commit *commit,
-			   const void *format, struct strbuf *sb,
-			   enum date_mode dmode)
+			   const char *format, struct strbuf *sb,
+			   const struct pretty_print_context *pretty_ctx)
 {
 	struct format_commit_context context;
+	static const char utf8[] = "UTF-8";
+	const char *output_enc = pretty_ctx->output_encoding;
 
 	memset(&context, 0, sizeof(context));
 	context.commit = commit;
-	context.dmode = dmode;
+	context.pretty_ctx = pretty_ctx;
+	context.wrap_start = sb->len;
+	context.message = commit->buffer;
+	if (output_enc) {
+		char *enc = get_header(commit, "encoding");
+		if (strcmp(enc ? enc : utf8, output_enc)) {
+			context.message = logmsg_reencode(commit, output_enc);
+			if (!context.message)
+				context.message = commit->buffer;
+		}
+		free(enc);
+	}
+
 	strbuf_expand(sb, format, format_commit_item, &context);
+	rewrap_message_tail(sb, &context, 0, 0, 0);
+
+	if (context.message != commit->buffer)
+		free(context.message);
+	free(context.signature.gpg_output);
+	free(context.signature.signer);
 }
 
-static void pp_header(enum cmit_fmt fmt,
-		      int abbrev,
-		      enum date_mode dmode,
+static void pp_header(const struct pretty_print_context *pp,
 		      const char *encoding,
 		      const struct commit *commit,
 		      const char **msg_p,
@@ -761,7 +1231,7 @@
 			/* End of header */
 			return;
 
-		if (fmt == CMIT_FMT_RAW) {
+		if (pp->fmt == CMIT_FMT_RAW) {
 			strbuf_add(sb, line, linelen);
 			continue;
 		}
@@ -781,7 +1251,7 @@
 				;
 			/* with enough slop */
 			strbuf_grow(sb, num * 50 + 20);
-			add_merge_info(fmt, sb, commit, abbrev);
+			add_merge_info(pp, sb, commit);
 			parents_shown = 1;
 		}
 
@@ -792,33 +1262,31 @@
 		 */
 		if (!memcmp(line, "author ", 7)) {
 			strbuf_grow(sb, linelen + 80);
-			pp_user_info("Author", fmt, sb, line + 7, dmode, encoding);
+			pp_user_info(pp, "Author", sb, line + 7, encoding);
 		}
 		if (!memcmp(line, "committer ", 10) &&
-		    (fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER)) {
+		    (pp->fmt == CMIT_FMT_FULL || pp->fmt == CMIT_FMT_FULLER)) {
 			strbuf_grow(sb, linelen + 80);
-			pp_user_info("Commit", fmt, sb, line + 10, dmode, encoding);
+			pp_user_info(pp, "Commit", sb, line + 10, encoding);
 		}
 	}
 }
 
-void pp_title_line(enum cmit_fmt fmt,
+void pp_title_line(const struct pretty_print_context *pp,
 		   const char **msg_p,
 		   struct strbuf *sb,
-		   const char *subject,
-		   const char *after_subject,
 		   const char *encoding,
 		   int need_8bit_cte)
 {
-	const char *line_separator = (fmt == CMIT_FMT_EMAIL) ? "\n " : " ";
 	struct strbuf title;
 
 	strbuf_init(&title, 80);
-	*msg_p = format_subject(&title, *msg_p, line_separator);
+	*msg_p = format_subject(&title, *msg_p,
+				pp->preserve_subject ? "\n" : " ");
 
 	strbuf_grow(sb, title.len + 1024);
-	if (subject) {
-		strbuf_addstr(sb, subject);
+	if (pp->subject) {
+		strbuf_addstr(sb, pp->subject);
 		add_rfc2047(sb, title.buf, title.len, encoding);
 	} else {
 		strbuf_addbuf(sb, &title);
@@ -832,16 +1300,16 @@
 			"Content-Transfer-Encoding: 8bit\n";
 		strbuf_addf(sb, header_fmt, encoding);
 	}
-	if (after_subject) {
-		strbuf_addstr(sb, after_subject);
+	if (pp->after_subject) {
+		strbuf_addstr(sb, pp->after_subject);
 	}
-	if (fmt == CMIT_FMT_EMAIL) {
+	if (pp->fmt == CMIT_FMT_EMAIL) {
 		strbuf_addch(sb, '\n');
 	}
 	strbuf_release(&title);
 }
 
-void pp_remainder(enum cmit_fmt fmt,
+void pp_remainder(const struct pretty_print_context *pp,
 		  const char **msg_p,
 		  struct strbuf *sb,
 		  int indent)
@@ -858,7 +1326,7 @@
 		if (is_empty_line(line, &linelen)) {
 			if (first)
 				continue;
-			if (fmt == CMIT_FMT_SHORT)
+			if (pp->fmt == CMIT_FMT_SHORT)
 				break;
 		}
 		first = 0;
@@ -877,29 +1345,25 @@
 {
 	const char *encoding;
 
-	encoding = (git_log_output_encoding
-		    ? git_log_output_encoding
-		    : git_commit_encoding);
-	if (!encoding)
-		encoding = "UTF-8";
+	encoding = get_log_output_encoding();
 	if (encoding_p)
 		*encoding_p = encoding;
 	return logmsg_reencode(commit, encoding);
 }
 
-void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
-			 struct strbuf *sb, int abbrev,
-			 const char *subject, const char *after_subject,
-			 enum date_mode dmode, int need_8bit_cte)
+void pretty_print_commit(const struct pretty_print_context *pp,
+			 const struct commit *commit,
+			 struct strbuf *sb)
 {
 	unsigned long beginning_of_body;
 	int indent = 4;
 	const char *msg = commit->buffer;
 	char *reencoded;
 	const char *encoding;
+	int need_8bit_cte = pp->need_8bit_cte;
 
-	if (fmt == CMIT_FMT_USERFORMAT) {
-		format_commit_message(commit, user_format, sb, dmode);
+	if (pp->fmt == CMIT_FMT_USERFORMAT) {
+		format_commit_message(commit, user_format, sb, pp);
 		return;
 	}
 
@@ -908,14 +1372,14 @@
 		msg = reencoded;
 	}
 
-	if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
+	if (pp->fmt == CMIT_FMT_ONELINE || pp->fmt == CMIT_FMT_EMAIL)
 		indent = 0;
 
 	/*
 	 * We need to check and emit Content-type: to mark it
 	 * as 8-bit if we haven't done so.
 	 */
-	if (fmt == CMIT_FMT_EMAIL && need_8bit_cte == 0) {
+	if (pp->fmt == CMIT_FMT_EMAIL && need_8bit_cte == 0) {
 		int i, ch, in_body;
 
 		for (in_body = i = 0; (ch = msg[i]); i++) {
@@ -934,8 +1398,8 @@
 		}
 	}
 
-	pp_header(fmt, abbrev, dmode, encoding, commit, &msg, sb);
-	if (fmt != CMIT_FMT_ONELINE && !subject) {
+	pp_header(pp, encoding, commit, &msg, sb);
+	if (pp->fmt != CMIT_FMT_ONELINE && !pp->subject) {
 		strbuf_addch(sb, '\n');
 	}
 
@@ -943,17 +1407,16 @@
 	msg = skip_empty_lines(msg);
 
 	/* These formats treat the title line specially. */
-	if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
-		pp_title_line(fmt, &msg, sb, subject,
-			      after_subject, encoding, need_8bit_cte);
+	if (pp->fmt == CMIT_FMT_ONELINE || pp->fmt == CMIT_FMT_EMAIL)
+		pp_title_line(pp, &msg, sb, encoding, need_8bit_cte);
 
 	beginning_of_body = sb->len;
-	if (fmt != CMIT_FMT_ONELINE)
-		pp_remainder(fmt, &msg, sb, indent);
+	if (pp->fmt != CMIT_FMT_ONELINE)
+		pp_remainder(pp, &msg, sb, indent);
 	strbuf_rtrim(sb);
 
 	/* Make sure there is an EOLN for the non-oneline case */
-	if (fmt != CMIT_FMT_ONELINE)
+	if (pp->fmt != CMIT_FMT_ONELINE)
 		strbuf_addch(sb, '\n');
 
 	/*
@@ -961,7 +1424,20 @@
 	 * format.  Make sure we did not strip the blank line
 	 * between the header and the body.
 	 */
-	if (fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body)
+	if (pp->fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body)
 		strbuf_addch(sb, '\n');
+
+	if (pp->show_notes)
+		format_display_notes(commit->object.sha1, sb, encoding,
+				     NOTES_SHOW_HEADER | NOTES_INDENT);
+
 	free(reencoded);
 }
+
+void pp_commit_easy(enum cmit_fmt fmt, const struct commit *commit,
+		    struct strbuf *sb)
+{
+	struct pretty_print_context pp = {0};
+	pp.fmt = fmt;
+	pretty_print_commit(&pp, commit, sb);
+}
diff --git a/progress.c b/progress.c
index 621c34e..3971f49 100644
--- a/progress.c
+++ b/progress.c
@@ -1,7 +1,7 @@
 /*
  * Simple text-based progress display module for GIT
  *
- * Copyright (c) 2007 by Nicolas Pitre <nico@cam.org>
+ * Copyright (c) 2007 by Nicolas Pitre <nico@fluxnic.net>
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -131,7 +131,13 @@
 	} else {
 		l -= snprintf(tp->display, l, ", %u bytes", (int)total);
 	}
-	if (rate)
+
+	if (rate > 1 << 10) {
+		int x = rate + 5;  /* for rounding */
+		snprintf(tp->display + sizeof(tp->display) - l, l,
+			 " | %u.%2.2u MiB/s",
+			 x >> 10, ((x & ((1 << 10) - 1)) * 100) >> 10);
+	} else if (rate)
 		snprintf(tp->display + sizeof(tp->display) - l, l,
 			 " | %u KiB/s", rate);
 }
diff --git a/prompt.c b/prompt.c
new file mode 100644
index 0000000..d851807
--- /dev/null
+++ b/prompt.c
@@ -0,0 +1,72 @@
+#include "cache.h"
+#include "run-command.h"
+#include "strbuf.h"
+#include "prompt.h"
+#include "compat/terminal.h"
+
+static char *do_askpass(const char *cmd, const char *prompt)
+{
+	struct child_process pass;
+	const char *args[3];
+	static struct strbuf buffer = STRBUF_INIT;
+	int err = 0;
+
+	args[0] = cmd;
+	args[1]	= prompt;
+	args[2] = NULL;
+
+	memset(&pass, 0, sizeof(pass));
+	pass.argv = args;
+	pass.out = -1;
+
+	if (start_command(&pass))
+		return NULL;
+
+	if (strbuf_read(&buffer, pass.out, 20) < 0)
+		err = 1;
+
+	close(pass.out);
+
+	if (finish_command(&pass))
+		err = 1;
+
+	if (err) {
+		error("unable to read askpass response from '%s'", cmd);
+		strbuf_release(&buffer);
+		return NULL;
+	}
+
+	strbuf_setlen(&buffer, strcspn(buffer.buf, "\r\n"));
+
+	return strbuf_detach(&buffer, NULL);
+}
+
+char *git_prompt(const char *prompt, int flags)
+{
+	char *r = NULL;
+
+	if (flags & PROMPT_ASKPASS) {
+		const char *askpass;
+
+		askpass = getenv("GIT_ASKPASS");
+		if (!askpass)
+			askpass = askpass_program;
+		if (!askpass)
+			askpass = getenv("SSH_ASKPASS");
+		if (askpass && *askpass)
+			r = do_askpass(askpass, prompt);
+	}
+
+	if (!r)
+		r = git_terminal_prompt(prompt, flags & PROMPT_ECHO);
+	if (!r) {
+		/* prompts already contain ": " at the end */
+		die("could not read %s%s", prompt, strerror(errno));
+	}
+	return r;
+}
+
+char *git_getpass(const char *prompt)
+{
+	return git_prompt(prompt, PROMPT_ASKPASS);
+}
diff --git a/prompt.h b/prompt.h
new file mode 100644
index 0000000..04f321a
--- /dev/null
+++ b/prompt.h
@@ -0,0 +1,10 @@
+#ifndef PROMPT_H
+#define PROMPT_H
+
+#define PROMPT_ASKPASS (1<<0)
+#define PROMPT_ECHO    (1<<1)
+
+char *git_prompt(const char *prompt, int flags);
+char *git_getpass(const char *prompt);
+
+#endif /* PROMPT_H */
diff --git a/quote.c b/quote.c
index 848d174..911229f 100644
--- a/quote.c
+++ b/quote.c
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "quote.h"
+#include "argv-array.h"
 
 int quote_path_fully = 1;
 
@@ -72,7 +73,7 @@
 	}
 }
 
-char *sq_dequote_step(char *arg, char **next)
+static char *sq_dequote_step(char *arg, char **next)
 {
 	char *dst = arg;
 	char *src = arg;
@@ -120,7 +121,9 @@
 	return sq_dequote_step(arg, NULL);
 }
 
-int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc)
+static int sq_dequote_to_argv_internal(char *arg,
+				       const char ***argv, int *nr, int *alloc,
+				       struct argv_array *array)
 {
 	char *next = arg;
 
@@ -130,13 +133,27 @@
 		char *dequoted = sq_dequote_step(next, &next);
 		if (!dequoted)
 			return -1;
-		ALLOC_GROW(*argv, *nr + 1, *alloc);
-		(*argv)[(*nr)++] = dequoted;
+		if (argv) {
+			ALLOC_GROW(*argv, *nr + 1, *alloc);
+			(*argv)[(*nr)++] = dequoted;
+		}
+		if (array)
+			argv_array_push(array, dequoted);
 	} while (next);
 
 	return 0;
 }
 
+int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc)
+{
+	return sq_dequote_to_argv_internal(arg, argv, nr, alloc, NULL);
+}
+
+int sq_dequote_to_argv_array(char *arg, struct argv_array *array)
+{
+	return sq_dequote_to_argv_internal(arg, NULL, NULL, NULL, array);
+}
+
 /* 1 means: quote as octal
  * 0 means: quote as octal if (quote_path_fully)
  * -1 means: never quote
@@ -213,7 +230,7 @@
 		int ch;
 
 		len = next_quote_pos(p, maxlen);
-		if (len == maxlen || !p[len])
+		if (len == maxlen || (maxlen < 0 && !p[len]))
 			break;
 
 		if (!no_dq && p == name)
@@ -223,6 +240,8 @@
 		EMIT('\\');
 		p += len;
 		ch = (unsigned char)*p++;
+		if (maxlen >= 0)
+			maxlen -= len + 1;
 		if (sq_lookup[ch] >= ' ') {
 			EMIT(sq_lookup[ch]);
 		} else {
@@ -293,42 +312,79 @@
 	fputc(terminator, fp);
 }
 
+static const char *path_relative(const char *in, int len,
+				 struct strbuf *sb, const char *prefix,
+				 int prefix_len);
+
+void write_name_quoted_relative(const char *name, size_t len,
+				const char *prefix, size_t prefix_len,
+				FILE *fp, int terminator)
+{
+	struct strbuf sb = STRBUF_INIT;
+
+	name = path_relative(name, len, &sb, prefix, prefix_len);
+	write_name_quoted(name, fp, terminator);
+
+	strbuf_release(&sb);
+}
+
+/*
+ * Give path as relative to prefix.
+ *
+ * The strbuf may or may not be used, so do not assume it contains the
+ * returned path.
+ */
+static const char *path_relative(const char *in, int len,
+				 struct strbuf *sb, const char *prefix,
+				 int prefix_len)
+{
+	int off, i;
+
+	if (len < 0)
+		len = strlen(in);
+	if (prefix_len < 0) {
+		if (prefix)
+			prefix_len = strlen(prefix);
+		else
+			prefix_len = 0;
+	}
+
+	off = 0;
+	i = 0;
+	while (i < prefix_len && i < len && prefix[i] == in[i]) {
+		if (prefix[i] == '/')
+			off = i + 1;
+		i++;
+	}
+	in += off;
+	len -= off;
+
+	if (i >= prefix_len)
+		return in;
+
+	strbuf_reset(sb);
+	strbuf_grow(sb, len);
+
+	while (i < prefix_len) {
+		if (prefix[i] == '/')
+			strbuf_addstr(sb, "../");
+		i++;
+	}
+	strbuf_add(sb, in, len);
+
+	return sb->buf;
+}
+
 /* quote path as relative to the given prefix */
 char *quote_path_relative(const char *in, int len,
 			  struct strbuf *out, const char *prefix)
 {
-	int needquote;
+	struct strbuf sb = STRBUF_INIT;
+	const char *rel = path_relative(in, len, &sb, prefix, -1);
+	strbuf_reset(out);
+	quote_c_style_counted(rel, strlen(rel), out, NULL, 0);
+	strbuf_release(&sb);
 
-	if (len < 0)
-		len = strlen(in);
-
-	/* "../" prefix itself does not need quoting, but "in" might. */
-	needquote = next_quote_pos(in, len) < len;
-	strbuf_setlen(out, 0);
-	strbuf_grow(out, len);
-
-	if (needquote)
-		strbuf_addch(out, '"');
-	if (prefix) {
-		int off = 0;
-		while (prefix[off] && off < len && prefix[off] == in[off])
-			if (prefix[off] == '/') {
-				prefix += off + 1;
-				in += off + 1;
-				len -= off + 1;
-				off = 0;
-			} else
-				off++;
-
-		for (; *prefix; prefix++)
-			if (*prefix == '/')
-				strbuf_addstr(out, "../");
-	}
-
-	quote_c_style_counted (in, len, out, NULL, 1);
-
-	if (needquote)
-		strbuf_addch(out, '"');
 	if (!out->len)
 		strbuf_addstr(out, "./");
 
diff --git a/quote.h b/quote.h
index 66730f2..133155a 100644
--- a/quote.h
+++ b/quote.h
@@ -1,8 +1,7 @@
 #ifndef QUOTE_H
 #define QUOTE_H
 
-#include <stddef.h>
-#include <stdio.h>
+struct strbuf;
 
 /* Help to copy the thing properly quoted for the shell safety.
  * any single quote is replaced with '\'', any exclamation point
@@ -41,13 +40,19 @@
 
 /*
  * Same as the above, but can be used to unwrap many arguments in the
- * same string separated by space. "next" is changed to point to the
- * next argument that should be passed as first parameter. When there
- * is no more argument to be dequoted, "next" is updated to point to NULL.
+ * same string separated by space. Like sq_quote, it works in place,
+ * modifying arg and appending pointers into it to argv.
  */
-extern char *sq_dequote_step(char *arg, char **next);
 extern int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc);
 
+/*
+ * Same as above, but store the unquoted strings in an argv_array. We will
+ * still modify arg in place, but unlike sq_dequote_to_argv, the argv_array
+ * will duplicate and take ownership of the strings.
+ */
+struct argv_array;
+extern int sq_dequote_to_argv_array(char *arg, struct argv_array *);
+
 extern int unquote_c_style(struct strbuf *, const char *quoted, const char **endp);
 extern size_t quote_c_style(const char *name, struct strbuf *, FILE *, int no_dq);
 extern void quote_two_c_style(struct strbuf *, const char *, const char *, int);
@@ -55,9 +60,12 @@
 extern void write_name_quoted(const char *name, FILE *, int terminator);
 extern void write_name_quotedpfx(const char *pfx, size_t pfxlen,
                                  const char *name, FILE *, int terminator);
+extern void write_name_quoted_relative(const char *name, size_t len,
+		const char *prefix, size_t prefix_len,
+		FILE *fp, int terminator);
 
 /* quote path as relative to the given prefix */
-char *quote_path_relative(const char *in, int len,
+extern char *quote_path_relative(const char *in, int len,
 			  struct strbuf *out, const char *prefix);
 
 /* quoting as a string literal for other languages */
diff --git a/reachable.c b/reachable.c
index b515fa2..bf79706 100644
--- a/reachable.c
+++ b/reachable.c
@@ -7,11 +7,25 @@
 #include "revision.h"
 #include "reachable.h"
 #include "cache-tree.h"
+#include "progress.h"
+
+struct connectivity_progress {
+	struct progress *progress;
+	unsigned long count;
+};
+
+static void update_progress(struct connectivity_progress *cp)
+{
+	cp->count++;
+	if ((cp->count & 1023) == 0)
+		display_progress(cp->progress, cp->count);
+}
 
 static void process_blob(struct blob *blob,
 			 struct object_array *p,
 			 struct name_path *path,
-			 const char *name)
+			 const char *name,
+			 struct connectivity_progress *cp)
 {
 	struct object *obj = &blob->object;
 
@@ -20,6 +34,7 @@
 	if (obj->flags & SEEN)
 		return;
 	obj->flags |= SEEN;
+	update_progress(cp);
 	/* Nothing to do, really .. The blob lookup was the important part */
 }
 
@@ -34,7 +49,8 @@
 static void process_tree(struct tree *tree,
 			 struct object_array *p,
 			 struct name_path *path,
-			 const char *name)
+			 const char *name,
+			 struct connectivity_progress *cp)
 {
 	struct object *obj = &tree->object;
 	struct tree_desc desc;
@@ -46,6 +62,7 @@
 	if (obj->flags & SEEN)
 		return;
 	obj->flags |= SEEN;
+	update_progress(cp);
 	if (parse_tree(tree) < 0)
 		die("bad tree object %s", sha1_to_hex(obj->sha1));
 	add_object(obj, p, path, name);
@@ -57,28 +74,25 @@
 
 	while (tree_entry(&desc, &entry)) {
 		if (S_ISDIR(entry.mode))
-			process_tree(lookup_tree(entry.sha1), p, &me, entry.path);
+			process_tree(lookup_tree(entry.sha1), p, &me, entry.path, cp);
 		else if (S_ISGITLINK(entry.mode))
 			process_gitlink(entry.sha1, p, &me, entry.path);
 		else
-			process_blob(lookup_blob(entry.sha1), p, &me, entry.path);
+			process_blob(lookup_blob(entry.sha1), p, &me, entry.path, cp);
 	}
 	free(tree->buffer);
 	tree->buffer = NULL;
 }
 
-static void process_tag(struct tag *tag, struct object_array *p, const char *name)
+static void process_tag(struct tag *tag, struct object_array *p,
+			const char *name, struct connectivity_progress *cp)
 {
 	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;
+	update_progress(cp);
 
 	if (parse_tag(tag) < 0)
 		die("bad tag object %s", sha1_to_hex(obj->sha1));
@@ -86,15 +100,18 @@
 		add_object(tag->tagged, p, NULL, name);
 }
 
-static void walk_commit_list(struct rev_info *revs)
+static void walk_commit_list(struct rev_info *revs,
+			     struct connectivity_progress *cp)
 {
 	int i;
 	struct commit *commit;
-	struct object_array objects = { 0, 0, NULL };
+	struct object_array objects = OBJECT_ARRAY_INIT;
 
 	/* Walk all commits, process their trees */
-	while ((commit = get_revision(revs)) != NULL)
-		process_tree(commit->tree, &objects, NULL, "");
+	while ((commit = get_revision(revs)) != NULL) {
+		process_tree(commit->tree, &objects, NULL, "", cp);
+		update_progress(cp);
+	}
 
 	/* Then walk all the pending objects, recursively processing them too */
 	for (i = 0; i < revs->pending.nr; i++) {
@@ -102,15 +119,15 @@
 		struct object *obj = pending->item;
 		const char *name = pending->name;
 		if (obj->type == OBJ_TAG) {
-			process_tag((struct tag *) obj, &objects, name);
+			process_tag((struct tag *) obj, &objects, name, cp);
 			continue;
 		}
 		if (obj->type == OBJ_TREE) {
-			process_tree((struct tree *)obj, &objects, NULL, name);
+			process_tree((struct tree *)obj, &objects, NULL, name, cp);
 			continue;
 		}
 		if (obj->type == OBJ_BLOB) {
-			process_blob((struct blob *)obj, &objects, NULL, name);
+			process_blob((struct blob *)obj, &objects, NULL, name, cp);
 			continue;
 		}
 		die("unknown pending object %s (%s)", sha1_to_hex(obj->sha1), name);
@@ -196,8 +213,11 @@
 		add_cache_tree(active_cache_tree, revs);
 }
 
-void mark_reachable_objects(struct rev_info *revs, int mark_reflog)
+void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
+			    struct progress *progress)
 {
+	struct connectivity_progress cp;
+
 	/*
 	 * Set up revision parsing, and mark us as being interested
 	 * in all object types, not just commits.
@@ -216,11 +236,15 @@
 	if (mark_reflog)
 		for_each_reflog(add_one_reflog, revs);
 
+	cp.progress = progress;
+	cp.count = 0;
+
 	/*
 	 * Set up the revision walk - this will move all commits
 	 * from the pending list to the commit walking list.
 	 */
 	if (prepare_revision_walk(revs))
 		die("revision walk setup failed");
-	walk_commit_list(revs);
+	walk_commit_list(revs, &cp);
+	display_progress(cp.progress, cp.count);
 }
diff --git a/reachable.h b/reachable.h
index 4075181..5d082ad 100644
--- a/reachable.h
+++ b/reachable.h
@@ -1,6 +1,7 @@
 #ifndef REACHEABLE_H
 #define REACHEABLE_H
 
-extern void mark_reachable_objects(struct rev_info *revs, int mark_reflog);
+struct progress;
+extern void mark_reachable_objects(struct rev_info *revs, int mark_reflog, struct progress *);
 
 #endif
diff --git a/read-cache.c b/read-cache.c
index 4e3e272..2f8159f 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -10,10 +10,16 @@
 #include "dir.h"
 #include "tree.h"
 #include "commit.h"
-#include "diff.h"
-#include "diffcore.h"
-#include "revision.h"
 #include "blob.h"
+#include "resolve-undo.h"
+#include "strbuf.h"
+#include "varint.h"
+
+static struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
+
+/* Mask for the name length in ce_flags in the on-disk index */
+
+#define CE_NAMEMASK  (0x0fff)
 
 /* Index extensions.
  *
@@ -26,6 +32,7 @@
 
 #define CACHE_EXT(s) ( (s[0]<<24)|(s[1]<<16)|(s[2]<<8)|(s[3]) )
 #define CACHE_EXT_TREE 0x54524545	/* "TREE" */
+#define CACHE_EXT_RESOLVE_UNDO 0x52455543 /* "REUC" */
 
 struct index_state the_index;
 
@@ -51,8 +58,8 @@
 
 	new = xmalloc(cache_entry_size(namelen));
 	copy_cache_entry(new, old);
-	new->ce_flags &= ~(CE_STATE_MASK | CE_NAMEMASK);
-	new->ce_flags |= (namelen >= CE_NAMEMASK ? CE_NAMEMASK : namelen);
+	new->ce_flags &= ~CE_STATE_MASK;
+	new->ce_namelen = namelen;
 	memcpy(new->name, new_name, namelen + 1);
 
 	cache_tree_invalidate_path(istate->cache_tree, old->name);
@@ -91,7 +98,7 @@
 
 	if (fd >= 0) {
 		unsigned char sha1[20];
-		if (!index_fd(sha1, fd, st, 0, OBJ_BLOB, ce->name))
+		if (!index_fd(sha1, fd, st, OBJ_BLOB, ce->name, 0))
 			match = hashcmp(sha1, ce->sha1);
 		/* index_fd() closed the file descriptor already */
 	}
@@ -156,16 +163,6 @@
 	return 0;
 }
 
-int is_empty_blob_sha1(const unsigned char *sha1)
-{
-	static const unsigned char empty_blob_sha1[20] = {
-		0xe6,0x9d,0xe2,0x9b,0xb2,0xd1,0xd6,0x43,0x4b,0x8b,
-		0x29,0xae,0x77,0x5a,0xd8,0xc2,0xe4,0x8c,0x53,0x91
-	};
-
-	return !hashcmp(sha1, empty_blob_sha1);
-}
-
 static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
 {
 	unsigned int changed = 0;
@@ -259,12 +256,17 @@
 {
 	unsigned int changed;
 	int ignore_valid = options & CE_MATCH_IGNORE_VALID;
+	int ignore_skip_worktree = options & CE_MATCH_IGNORE_SKIP_WORKTREE;
 	int assume_racy_is_modified = options & CE_MATCH_RACY_IS_DIRTY;
 
 	/*
 	 * If it's marked as always valid in the index, it's
 	 * valid whatever the checked-out copy says.
+	 *
+	 * skip-worktree has the same effect with higher precedence
 	 */
+	if (!ignore_skip_worktree && ce_skip_worktree(ce))
+		return 0;
 	if (!ignore_valid && (ce->ce_flags & CE_VALID))
 		return 0;
 
@@ -397,10 +399,8 @@
 	return c1 - c2;
 }
 
-int cache_name_compare(const char *name1, int flags1, const char *name2, int flags2)
+int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2)
 {
-	int len1 = flags1 & CE_NAMEMASK;
-	int len2 = flags2 & CE_NAMEMASK;
 	int len = len1 < len2 ? len1 : len2;
 	int cmp;
 
@@ -412,18 +412,19 @@
 	if (len1 > len2)
 		return 1;
 
-	/* Compare stages  */
-	flags1 &= CE_STAGEMASK;
-	flags2 &= CE_STAGEMASK;
-
-	if (flags1 < flags2)
+	if (stage1 < stage2)
 		return -1;
-	if (flags1 > flags2)
+	if (stage1 > stage2)
 		return 1;
 	return 0;
 }
 
-int index_name_pos(const struct index_state *istate, const char *name, int namelen)
+int cache_name_compare(const char *name1, int len1, const char *name2, int len2)
+{
+	return cache_name_stage_compare(name1, len1, 0, name2, len2, 0);
+}
+
+int index_name_stage_pos(const struct index_state *istate, const char *name, int namelen, int stage)
 {
 	int first, last;
 
@@ -432,7 +433,7 @@
 	while (last > first) {
 		int next = (last + first) >> 1;
 		struct cache_entry *ce = istate->cache[next];
-		int cmp = cache_name_compare(name, namelen, ce->name, ce->ce_flags);
+		int cmp = cache_name_stage_compare(name, namelen, stage, ce->name, ce_namelen(ce), ce_stage(ce));
 		if (!cmp)
 			return next;
 		if (cmp < 0) {
@@ -444,11 +445,17 @@
 	return -first-1;
 }
 
+int index_name_pos(const struct index_state *istate, const char *name, int namelen)
+{
+	return index_name_stage_pos(istate, name, namelen, 0);
+}
+
 /* Remove entry, return true if there are more entries to go.. */
 int remove_index_entry_at(struct index_state *istate, int pos)
 {
 	struct cache_entry *ce = istate->cache[pos];
 
+	record_resolve_undo(istate, ce);
 	remove_name_hash(ce);
 	istate->cache_changed = 1;
 	istate->cache_nr--;
@@ -564,7 +571,7 @@
 	int size, namelen, was_same;
 	mode_t st_mode = st->st_mode;
 	struct cache_entry *ce, *alias;
-	unsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_RACY_IS_DIRTY;
+	unsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE|CE_MATCH_RACY_IS_DIRTY;
 	int verbose = flags & (ADD_CACHE_VERBOSE | ADD_CACHE_PRETEND);
 	int pretend = flags & ADD_CACHE_PRETEND;
 	int intent_only = flags & ADD_CACHE_INTENT;
@@ -582,7 +589,7 @@
 	size = cache_entry_size(namelen);
 	ce = xcalloc(1, size);
 	memcpy(ce->name, path, namelen);
-	ce->ce_flags = namelen;
+	ce->ce_namelen = namelen;
 	if (!intent_only)
 		fill_stat_cache_info(ce, st);
 	else
@@ -601,16 +608,40 @@
 		ce->ce_mode = ce_mode_from_stat(ent, st_mode);
 	}
 
+	/* When core.ignorecase=true, determine if a directory of the same name but differing
+	 * case already exists within the Git repository.  If it does, ensure the directory
+	 * case of the file being added to the repository matches (is folded into) the existing
+	 * entry's directory case.
+	 */
+	if (ignore_case) {
+		const char *startPtr = ce->name;
+		const char *ptr = startPtr;
+		while (*ptr) {
+			while (*ptr && *ptr != '/')
+				++ptr;
+			if (*ptr == '/') {
+				struct cache_entry *foundce;
+				++ptr;
+				foundce = index_name_exists(&the_index, ce->name, ptr - ce->name, ignore_case);
+				if (foundce) {
+					memcpy((void *)startPtr, foundce->name + (startPtr - ce->name), ptr - startPtr);
+					startPtr = ptr;
+				}
+			}
+		}
+	}
+
 	alias = index_name_exists(istate, ce->name, ce_namelen(ce), ignore_case);
 	if (alias && !ce_stage(alias) && !ie_match_stat(istate, alias, st, ce_option)) {
 		/* Nothing changed, really */
 		free(ce);
-		ce_mark_uptodate(alias);
+		if (!S_ISGITLINK(alias->ce_mode))
+			ce_mark_uptodate(alias);
 		alias->ce_flags |= CE_ADDED;
 		return 0;
 	}
 	if (!intent_only) {
-		if (index_path(ce->sha1, path, st, 1))
+		if (index_path(ce->sha1, path, st, HASH_WRITE_OBJECT))
 			return error("unable to index file %s", path);
 	} else
 		record_intent_to_add(ce);
@@ -660,7 +691,8 @@
 
 	hashcpy(ce->sha1, sha1);
 	memcpy(ce->name, path, len);
-	ce->ce_flags = create_ce_flags(len, stage);
+	ce->ce_flags = create_ce_flags(stage);
+	ce->ce_namelen = len;
 	ce->ce_mode = create_ce_mode(mode);
 
 	if (refresh)
@@ -675,30 +707,9 @@
 	return ce_namelen(b) == len && !memcmp(a->name, b->name, len);
 }
 
-int ce_path_match(const struct cache_entry *ce, const char **pathspec)
+int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec)
 {
-	const char *match, *name;
-	int len;
-
-	if (!pathspec)
-		return 1;
-
-	len = ce_namelen(ce);
-	name = ce->name;
-	while ((match = *pathspec++) != NULL) {
-		int matchlen = strlen(match);
-		if (matchlen > len)
-			continue;
-		if (memcmp(name, match, matchlen))
-			continue;
-		if (matchlen && name[matchlen-1] == '/')
-			return 1;
-		if (name[matchlen] == '/' || !name[matchlen])
-			return 1;
-		if (!matchlen)
-			return 1;
-	}
-	return 0;
+	return match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, NULL);
 }
 
 /*
@@ -716,11 +727,12 @@
 	 * has already been discarded, we now test
 	 * the rest.
 	 */
-	switch (*rest) {
+
 	/* "." is not allowed */
-	case '\0': case '/':
+	if (*rest == '\0' || is_dir_sep(*rest))
 		return 0;
 
+	switch (*rest) {
 	/*
 	 * ".git" followed by  NUL or slash is bad. This
 	 * shares the path end test with the ".." case.
@@ -733,7 +745,7 @@
 		rest += 2;
 	/* fallthrough */
 	case '.':
-		if (rest[1] == '\0' || rest[1] == '/')
+		if (rest[1] == '\0' || is_dir_sep(rest[1]))
 			return 0;
 	}
 	return 1;
@@ -743,23 +755,19 @@
 {
 	char c;
 
+	if (has_dos_drive_prefix(path))
+		return 0;
+
 	goto inside;
 	for (;;) {
 		if (!c)
 			return 1;
-		if (c == '/') {
+		if (is_dir_sep(c)) {
 inside:
 			c = *path++;
-			switch (c) {
-			default:
-				continue;
-			case '/': case '\0':
-				break;
-			case '.':
-				if (verify_dotfile(path))
-					continue;
-			}
-			return 0;
+			if ((c == '.' && !verify_dotfile(path)) ||
+			    is_dir_sep(c) || c == '\0')
+				return 0;
 		}
 		c = *path++;
 	}
@@ -821,7 +829,7 @@
 		}
 		len = slash - name;
 
-		pos = index_name_pos(istate, name, create_ce_flags(len, stage));
+		pos = index_name_stage_pos(istate, name, len, stage);
 		if (pos >= 0) {
 			/*
 			 * Found one, but not so fast.  This could
@@ -911,7 +919,7 @@
 	int new_only = option & ADD_CACHE_NEW_ONLY;
 
 	cache_tree_invalidate_path(istate->cache_tree, ce->name);
-	pos = index_name_pos(istate, ce->name, ce->ce_flags);
+	pos = index_name_stage_pos(istate, ce->name, ce_namelen(ce), ce_stage(ce));
 
 	/* existing match? Just replace it. */
 	if (pos >= 0) {
@@ -943,7 +951,7 @@
 		if (!ok_to_replace)
 			return error("'%s' appears as both a file and as a directory",
 				     ce->name);
-		pos = index_name_pos(istate, ce->name, ce->ce_flags);
+		pos = index_name_stage_pos(istate, ce->name, ce_namelen(ce), ce_stage(ce));
 		pos = -pos-1;
 	}
 	return pos + 1;
@@ -994,20 +1002,27 @@
  */
 static struct cache_entry *refresh_cache_ent(struct index_state *istate,
 					     struct cache_entry *ce,
-					     unsigned int options, int *err)
+					     unsigned int options, int *err,
+					     int *changed_ret)
 {
 	struct stat st;
 	struct cache_entry *updated;
 	int changed, size;
 	int ignore_valid = options & CE_MATCH_IGNORE_VALID;
+	int ignore_skip_worktree = options & CE_MATCH_IGNORE_SKIP_WORKTREE;
 
 	if (ce_uptodate(ce))
 		return ce;
 
 	/*
-	 * CE_VALID means the user promised us that the change to
-	 * the work tree does not matter and told us not to worry.
+	 * CE_VALID or CE_SKIP_WORKTREE means the user promised us
+	 * that the change to the work tree does not matter and told
+	 * us not to worry.
 	 */
+	if (!ignore_skip_worktree && ce_skip_worktree(ce)) {
+		ce_mark_uptodate(ce);
+		return ce;
+	}
 	if (!ignore_valid && (ce->ce_flags & CE_VALID)) {
 		ce_mark_uptodate(ce);
 		return ce;
@@ -1020,6 +1035,8 @@
 	}
 
 	changed = ie_match_stat(istate, ce, &st, options);
+	if (changed_ret)
+		*changed_ret = changed;
 	if (!changed) {
 		/*
 		 * The path is unchanged.  If we were told to ignore
@@ -1037,7 +1054,8 @@
 			 * because CE_UPTODATE flag is in-core only;
 			 * we are not going to write this change out.
 			 */
-			ce_mark_uptodate(ce);
+			if (!S_ISGITLINK(ce->ce_mode))
+				ce_mark_uptodate(ce);
 			return ce;
 		}
 	}
@@ -1065,7 +1083,18 @@
 	return updated;
 }
 
-int refresh_index(struct index_state *istate, unsigned int flags, const char **pathspec, char *seen)
+static void show_file(const char * fmt, const char * name, int in_porcelain,
+		      int * first, const char *header_msg)
+{
+	if (in_porcelain && *first && header_msg) {
+		printf("%s\n", header_msg);
+		*first = 0;
+	}
+	printf(fmt, name);
+}
+
+int refresh_index(struct index_state *istate, unsigned int flags, const char **pathspec,
+		  char *seen, const char *header_msg)
 {
 	int i;
 	int has_errors = 0;
@@ -1074,19 +1103,34 @@
 	int quiet = (flags & REFRESH_QUIET) != 0;
 	int not_new = (flags & REFRESH_IGNORE_MISSING) != 0;
 	int ignore_submodules = (flags & REFRESH_IGNORE_SUBMODULES) != 0;
+	int first = 1;
+	int in_porcelain = (flags & REFRESH_IN_PORCELAIN);
 	unsigned int options = really ? CE_MATCH_IGNORE_VALID : 0;
-	const char *needs_update_message;
+	const char *modified_fmt;
+	const char *deleted_fmt;
+	const char *typechange_fmt;
+	const char *added_fmt;
+	const char *unmerged_fmt;
 
-	needs_update_message = ((flags & REFRESH_SAY_CHANGED)
-				? "locally modified" : "needs update");
+	modified_fmt = (in_porcelain ? "M\t%s\n" : "%s: needs update\n");
+	deleted_fmt = (in_porcelain ? "D\t%s\n" : "%s: needs update\n");
+	typechange_fmt = (in_porcelain ? "T\t%s\n" : "%s needs update\n");
+	added_fmt = (in_porcelain ? "A\t%s\n" : "%s needs update\n");
+	unmerged_fmt = (in_porcelain ? "U\t%s\n" : "%s: needs merge\n");
 	for (i = 0; i < istate->cache_nr; i++) {
 		struct cache_entry *ce, *new;
 		int cache_errno = 0;
+		int changed = 0;
+		int filtered = 0;
 
 		ce = istate->cache[i];
 		if (ignore_submodules && S_ISGITLINK(ce->ce_mode))
 			continue;
 
+		if (pathspec &&
+		    !match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen))
+			filtered = 1;
+
 		if (ce_stage(ce)) {
 			while ((i < istate->cache_nr) &&
 			       ! strcmp(istate->cache[i]->name, ce->name))
@@ -1094,18 +1138,22 @@
 			i--;
 			if (allow_unmerged)
 				continue;
-			printf("%s: needs merge\n", ce->name);
+			if (!filtered)
+				show_file(unmerged_fmt, ce->name, in_porcelain,
+					  &first, header_msg);
 			has_errors = 1;
 			continue;
 		}
 
-		if (pathspec && !match_pathspec(pathspec, ce->name, strlen(ce->name), 0, seen))
+		if (filtered)
 			continue;
 
-		new = refresh_cache_ent(istate, ce, options, &cache_errno);
+		new = refresh_cache_ent(istate, ce, options, &cache_errno, &changed);
 		if (new == ce)
 			continue;
 		if (!new) {
+			const char *fmt;
+
 			if (not_new && cache_errno == ENOENT)
 				continue;
 			if (really && cache_errno == EINVAL) {
@@ -1117,7 +1165,17 @@
 			}
 			if (quiet)
 				continue;
-			printf("%s: %s\n", ce->name, needs_update_message);
+
+			if (cache_errno == ENOENT)
+				fmt = deleted_fmt;
+			else if (ce->ce_flags & CE_INTENT_TO_ADD)
+				fmt = added_fmt; /* must be before other checks */
+			else if (changed & TYPE_CHANGED)
+				fmt = typechange_fmt;
+			else
+				fmt = modified_fmt;
+			show_file(fmt,
+				  ce->name, in_porcelain, &first, header_msg);
 			has_errors = 1;
 			continue;
 		}
@@ -1127,20 +1185,79 @@
 	return has_errors;
 }
 
-struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really)
+static struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really)
 {
-	return refresh_cache_ent(&the_index, ce, really, NULL);
+	return refresh_cache_ent(&the_index, ce, really, NULL, NULL);
 }
 
+
+/*****************************************************************
+ * Index File I/O
+ *****************************************************************/
+
+#define INDEX_FORMAT_DEFAULT 3
+
+/*
+ * dev/ino/uid/gid/size are also just tracked to the low 32 bits
+ * Again - this is just a (very strong in practice) heuristic that
+ * the inode hasn't changed.
+ *
+ * We save the fields in big-endian order to allow using the
+ * index file over NFS transparently.
+ */
+struct ondisk_cache_entry {
+	struct cache_time ctime;
+	struct cache_time mtime;
+	unsigned int dev;
+	unsigned int ino;
+	unsigned int mode;
+	unsigned int uid;
+	unsigned int gid;
+	unsigned int size;
+	unsigned char sha1[20];
+	unsigned short flags;
+	char name[FLEX_ARRAY]; /* more */
+};
+
+/*
+ * This struct is used when CE_EXTENDED bit is 1
+ * The struct must match ondisk_cache_entry exactly from
+ * ctime till flags
+ */
+struct ondisk_cache_entry_extended {
+	struct cache_time ctime;
+	struct cache_time mtime;
+	unsigned int dev;
+	unsigned int ino;
+	unsigned int mode;
+	unsigned int uid;
+	unsigned int gid;
+	unsigned int size;
+	unsigned char sha1[20];
+	unsigned short flags;
+	unsigned short flags2;
+	char name[FLEX_ARRAY]; /* more */
+};
+
+/* These are only used for v3 or lower */
+#define align_flex_name(STRUCT,len) ((offsetof(struct STRUCT,name) + (len) + 8) & ~7)
+#define ondisk_cache_entry_size(len) align_flex_name(ondisk_cache_entry,len)
+#define ondisk_cache_entry_extended_size(len) align_flex_name(ondisk_cache_entry_extended,len)
+#define ondisk_ce_size(ce) (((ce)->ce_flags & CE_EXTENDED) ? \
+			    ondisk_cache_entry_extended_size(ce_namelen(ce)) : \
+			    ondisk_cache_entry_size(ce_namelen(ce)))
+
 static int verify_hdr(struct cache_header *hdr, unsigned long size)
 {
 	git_SHA_CTX c;
 	unsigned char sha1[20];
+	int hdr_version;
 
 	if (hdr->hdr_signature != htonl(CACHE_SIGNATURE))
 		return error("bad signature");
-	if (hdr->hdr_version != htonl(2) && hdr->hdr_version != htonl(3))
-		return error("bad index version");
+	hdr_version = ntohl(hdr->hdr_version);
+	if (hdr_version < 2 || 4 < hdr_version)
+		return error("bad index version %d", hdr_version);
 	git_SHA1_Init(&c);
 	git_SHA1_Update(&c, hdr, size - 20);
 	git_SHA1_Final(sha1, &c);
@@ -1156,6 +1273,9 @@
 	case CACHE_EXT_TREE:
 		istate->cache_tree = cache_tree_read(data, sz);
 		break;
+	case CACHE_EXT_RESOLVE_UNDO:
+		istate->resolve_undo = resolve_undo_read(data, sz);
+		break;
 	default:
 		if (*ext < 'A' || 'Z' < *ext)
 			return error("index uses %.4s extension, which we do not understand",
@@ -1171,63 +1291,116 @@
 	return read_index_from(istate, get_index_file());
 }
 
-static void convert_from_disk(struct ondisk_cache_entry *ondisk, struct cache_entry *ce)
+#ifndef NEEDS_ALIGNED_ACCESS
+#define ntoh_s(var) ntohs(var)
+#define ntoh_l(var) ntohl(var)
+#else
+static inline uint16_t ntoh_s_force_align(void *p)
 {
+	uint16_t x;
+	memcpy(&x, p, sizeof(x));
+	return ntohs(x);
+}
+static inline uint32_t ntoh_l_force_align(void *p)
+{
+	uint32_t x;
+	memcpy(&x, p, sizeof(x));
+	return ntohl(x);
+}
+#define ntoh_s(var) ntoh_s_force_align(&(var))
+#define ntoh_l(var) ntoh_l_force_align(&(var))
+#endif
+
+static struct cache_entry *cache_entry_from_ondisk(struct ondisk_cache_entry *ondisk,
+						   unsigned int flags,
+						   const char *name,
+						   size_t len)
+{
+	struct cache_entry *ce = xmalloc(cache_entry_size(len));
+
+	ce->ce_ctime.sec = ntoh_l(ondisk->ctime.sec);
+	ce->ce_mtime.sec = ntoh_l(ondisk->mtime.sec);
+	ce->ce_ctime.nsec = ntoh_l(ondisk->ctime.nsec);
+	ce->ce_mtime.nsec = ntoh_l(ondisk->mtime.nsec);
+	ce->ce_dev   = ntoh_l(ondisk->dev);
+	ce->ce_ino   = ntoh_l(ondisk->ino);
+	ce->ce_mode  = ntoh_l(ondisk->mode);
+	ce->ce_uid   = ntoh_l(ondisk->uid);
+	ce->ce_gid   = ntoh_l(ondisk->gid);
+	ce->ce_size  = ntoh_l(ondisk->size);
+	ce->ce_flags = flags & ~CE_NAMEMASK;
+	ce->ce_namelen = len;
+	hashcpy(ce->sha1, ondisk->sha1);
+	memcpy(ce->name, name, len);
+	ce->name[len] = '\0';
+	return ce;
+}
+
+/*
+ * Adjacent cache entries tend to share the leading paths, so it makes
+ * sense to only store the differences in later entries.  In the v4
+ * on-disk format of the index, each on-disk cache entry stores the
+ * number of bytes to be stripped from the end of the previous name,
+ * and the bytes to append to the result, to come up with its name.
+ */
+static unsigned long expand_name_field(struct strbuf *name, const char *cp_)
+{
+	const unsigned char *ep, *cp = (const unsigned char *)cp_;
+	size_t len = decode_varint(&cp);
+
+	if (name->len < len)
+		die("malformed name field in the index");
+	strbuf_remove(name, name->len - len, len);
+	for (ep = cp; *ep; ep++)
+		; /* find the end */
+	strbuf_add(name, cp, ep - cp);
+	return (const char *)ep + 1 - cp_;
+}
+
+static struct cache_entry *create_from_disk(struct ondisk_cache_entry *ondisk,
+					    unsigned long *ent_size,
+					    struct strbuf *previous_name)
+{
+	struct cache_entry *ce;
 	size_t len;
 	const char *name;
+	unsigned int flags;
 
-	ce->ce_ctime.sec = ntohl(ondisk->ctime.sec);
-	ce->ce_mtime.sec = ntohl(ondisk->mtime.sec);
-	ce->ce_ctime.nsec = ntohl(ondisk->ctime.nsec);
-	ce->ce_mtime.nsec = ntohl(ondisk->mtime.nsec);
-	ce->ce_dev   = ntohl(ondisk->dev);
-	ce->ce_ino   = ntohl(ondisk->ino);
-	ce->ce_mode  = ntohl(ondisk->mode);
-	ce->ce_uid   = ntohl(ondisk->uid);
-	ce->ce_gid   = ntohl(ondisk->gid);
-	ce->ce_size  = ntohl(ondisk->size);
 	/* On-disk flags are just 16 bits */
-	ce->ce_flags = ntohs(ondisk->flags);
+	flags = ntoh_s(ondisk->flags);
+	len = flags & CE_NAMEMASK;
 
-	hashcpy(ce->sha1, ondisk->sha1);
-
-	len = ce->ce_flags & CE_NAMEMASK;
-
-	if (ce->ce_flags & CE_EXTENDED) {
+	if (flags & CE_EXTENDED) {
 		struct ondisk_cache_entry_extended *ondisk2;
 		int extended_flags;
 		ondisk2 = (struct ondisk_cache_entry_extended *)ondisk;
-		extended_flags = ntohs(ondisk2->flags2) << 16;
+		extended_flags = ntoh_s(ondisk2->flags2) << 16;
 		/* We do not yet understand any bit out of CE_EXTENDED_FLAGS */
 		if (extended_flags & ~CE_EXTENDED_FLAGS)
 			die("Unknown index entry format %08x", extended_flags);
-		ce->ce_flags |= extended_flags;
+		flags |= extended_flags;
 		name = ondisk2->name;
 	}
 	else
 		name = ondisk->name;
 
-	if (len == CE_NAMEMASK)
-		len = strlen(name);
-	/*
-	 * NEEDSWORK: If the original index is crafted, this copy could
-	 * go unchecked.
-	 */
-	memcpy(ce->name, name, len + 1);
-}
+	if (!previous_name) {
+		/* v3 and earlier */
+		if (len == CE_NAMEMASK)
+			len = strlen(name);
+		ce = cache_entry_from_ondisk(ondisk, flags, name, len);
 
-static inline size_t estimate_cache_size(size_t ondisk_size, unsigned int entries)
-{
-	long per_entry;
+		*ent_size = ondisk_ce_size(ce);
+	} else {
+		unsigned long consumed;
+		consumed = expand_name_field(previous_name, name);
+		ce = cache_entry_from_ondisk(ondisk, flags,
+					     previous_name->buf,
+					     previous_name->len);
 
-	per_entry = sizeof(struct cache_entry) - sizeof(struct ondisk_cache_entry);
-
-	/*
-	 * Alignment can cause differences. This should be "alignof", but
-	 * since that's a gcc'ism, just use the size of a pointer.
-	 */
-	per_entry += sizeof(void *);
-	return ondisk_size + entries*per_entry;
+		*ent_size = (name - ((char *)ondisk)) + consumed;
+	}
+	return ce;
 }
 
 /* remember to discard_cache() before reading a different cache! */
@@ -1235,10 +1408,11 @@
 {
 	int fd, i;
 	struct stat st;
-	unsigned long src_offset, dst_offset;
+	unsigned long src_offset;
 	struct cache_header *hdr;
 	void *mmap;
 	size_t mmap_size;
+	struct strbuf previous_name_buf = STRBUF_INIT, *previous_name;
 
 	errno = EBUSY;
 	if (istate->initialized)
@@ -1271,33 +1445,30 @@
 	if (verify_hdr(hdr, mmap_size) < 0)
 		goto unmap;
 
+	istate->version = ntohl(hdr->hdr_version);
 	istate->cache_nr = ntohl(hdr->hdr_entries);
 	istate->cache_alloc = alloc_nr(istate->cache_nr);
 	istate->cache = xcalloc(istate->cache_alloc, sizeof(struct cache_entry *));
-
-	/*
-	 * The disk format is actually larger than the in-memory format,
-	 * due to space for nsec etc, so even though the in-memory one
-	 * has room for a few  more flags, we can allocate using the same
-	 * index size
-	 */
-	istate->alloc = xmalloc(estimate_cache_size(mmap_size, istate->cache_nr));
 	istate->initialized = 1;
 
+	if (istate->version == 4)
+		previous_name = &previous_name_buf;
+	else
+		previous_name = NULL;
+
 	src_offset = sizeof(*hdr);
-	dst_offset = 0;
 	for (i = 0; i < istate->cache_nr; i++) {
 		struct ondisk_cache_entry *disk_ce;
 		struct cache_entry *ce;
+		unsigned long consumed;
 
 		disk_ce = (struct ondisk_cache_entry *)((char *)mmap + src_offset);
-		ce = (struct cache_entry *)((char *)istate->alloc + dst_offset);
-		convert_from_disk(disk_ce, ce);
+		ce = create_from_disk(disk_ce, &consumed, previous_name);
 		set_index_entry(istate, i, ce);
 
-		src_offset += ondisk_ce_size(ce);
-		dst_offset += ce_size(ce);
+		src_offset += consumed;
 	}
+	strbuf_release(&previous_name_buf);
 	istate->timestamp.sec = st.st_mtime;
 	istate->timestamp.nsec = ST_MTIME_NSEC(st);
 
@@ -1308,7 +1479,7 @@
 		 * extension name (4-byte) and section length
 		 * in 4-byte network byte order.
 		 */
-		unsigned long extsize;
+		uint32_t extsize;
 		memcpy(&extsize, (char *)mmap + src_offset + 4, 4);
 		extsize = ntohl(extsize);
 		if (read_index_extension(istate,
@@ -1330,11 +1501,16 @@
 
 int is_index_unborn(struct index_state *istate)
 {
-	return (!istate->cache_nr && !istate->alloc && !istate->timestamp.sec);
+	return (!istate->cache_nr && !istate->timestamp.sec);
 }
 
 int discard_index(struct index_state *istate)
 {
+	int i;
+
+	for (i = 0; i < istate->cache_nr; i++)
+		free(istate->cache[i]);
+	resolve_undo_clear_index(istate);
 	istate->cache_nr = 0;
 	istate->cache_changed = 0;
 	istate->timestamp.sec = 0;
@@ -1342,8 +1518,6 @@
 	istate->name_hash_initialized = 0;
 	free_hash(&istate->name_hash);
 	cache_tree_free(&(istate->cache_tree));
-	free(istate->alloc);
-	istate->alloc = NULL;
 	istate->initialized = 0;
 
 	/* no need to throw away allocated active_cache */
@@ -1478,11 +1652,11 @@
 	}
 }
 
-static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce)
+/* Copy miscellaneous fields but not the name */
+static char *copy_cache_entry_to_ondisk(struct ondisk_cache_entry *ondisk,
+				       struct cache_entry *ce)
 {
-	int size = ondisk_ce_size(ce);
-	struct ondisk_cache_entry *ondisk = xcalloc(1, size);
-	char *name;
+	short flags;
 
 	ondisk->ctime.sec = htonl(ce->ce_ctime.sec);
 	ondisk->mtime.sec = htonl(ce->ce_mtime.sec);
@@ -1495,28 +1669,100 @@
 	ondisk->gid  = htonl(ce->ce_gid);
 	ondisk->size = htonl(ce->ce_size);
 	hashcpy(ondisk->sha1, ce->sha1);
-	ondisk->flags = htons(ce->ce_flags);
+
+	flags = ce->ce_flags;
+	flags |= (ce_namelen(ce) >= CE_NAMEMASK ? CE_NAMEMASK : ce_namelen(ce));
+	ondisk->flags = htons(flags);
 	if (ce->ce_flags & CE_EXTENDED) {
 		struct ondisk_cache_entry_extended *ondisk2;
 		ondisk2 = (struct ondisk_cache_entry_extended *)ondisk;
 		ondisk2->flags2 = htons((ce->ce_flags & CE_EXTENDED_FLAGS) >> 16);
-		name = ondisk2->name;
+		return ondisk2->name;
 	}
-	else
-		name = ondisk->name;
-	memcpy(name, ce->name, ce_namelen(ce));
+	else {
+		return ondisk->name;
+	}
+}
 
-	return ce_write(c, fd, ondisk, size);
+static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce,
+			  struct strbuf *previous_name)
+{
+	int size;
+	struct ondisk_cache_entry *ondisk;
+	char *name;
+	int result;
+
+	if (!previous_name) {
+		size = ondisk_ce_size(ce);
+		ondisk = xcalloc(1, size);
+		name = copy_cache_entry_to_ondisk(ondisk, ce);
+		memcpy(name, ce->name, ce_namelen(ce));
+	} else {
+		int common, to_remove, prefix_size;
+		unsigned char to_remove_vi[16];
+		for (common = 0;
+		     (ce->name[common] &&
+		      common < previous_name->len &&
+		      ce->name[common] == previous_name->buf[common]);
+		     common++)
+			; /* still matching */
+		to_remove = previous_name->len - common;
+		prefix_size = encode_varint(to_remove, to_remove_vi);
+
+		if (ce->ce_flags & CE_EXTENDED)
+			size = offsetof(struct ondisk_cache_entry_extended, name);
+		else
+			size = offsetof(struct ondisk_cache_entry, name);
+		size += prefix_size + (ce_namelen(ce) - common + 1);
+
+		ondisk = xcalloc(1, size);
+		name = copy_cache_entry_to_ondisk(ondisk, ce);
+		memcpy(name, to_remove_vi, prefix_size);
+		memcpy(name + prefix_size, ce->name + common, ce_namelen(ce) - common);
+
+		strbuf_splice(previous_name, common, to_remove,
+			      ce->name + common, ce_namelen(ce) - common);
+	}
+
+	result = ce_write(c, fd, ondisk, size);
+	free(ondisk);
+	return result;
+}
+
+static int has_racy_timestamp(struct index_state *istate)
+{
+	int entries = istate->cache_nr;
+	int i;
+
+	for (i = 0; i < entries; i++) {
+		struct cache_entry *ce = istate->cache[i];
+		if (is_racy_timestamp(istate, ce))
+			return 1;
+	}
+	return 0;
+}
+
+/*
+ * Opportunisticly update the index but do not complain if we can't
+ */
+void update_index_if_able(struct index_state *istate, struct lock_file *lockfile)
+{
+	if ((istate->cache_changed || has_racy_timestamp(istate)) &&
+	    !write_index(istate, lockfile->fd))
+		commit_locked_index(lockfile);
+	else
+		rollback_lock_file(lockfile);
 }
 
 int write_index(struct index_state *istate, int newfd)
 {
 	git_SHA_CTX c;
 	struct cache_header hdr;
-	int i, err, removed, extended;
+	int i, err, removed, extended, hdr_version;
 	struct cache_entry **cache = istate->cache;
 	int entries = istate->cache_nr;
 	struct stat st;
+	struct strbuf previous_name_buf = STRBUF_INIT, *previous_name;
 
 	for (i = removed = extended = 0; i < entries; i++) {
 		if (cache[i]->ce_flags & CE_REMOVE)
@@ -1530,24 +1776,34 @@
 		}
 	}
 
+	if (!istate->version)
+		istate->version = INDEX_FORMAT_DEFAULT;
+
+	/* demote version 3 to version 2 when the latter suffices */
+	if (istate->version == 3 || istate->version == 2)
+		istate->version = extended ? 3 : 2;
+
+	hdr_version = istate->version;
+
 	hdr.hdr_signature = htonl(CACHE_SIGNATURE);
-	/* for extended format, increase version so older git won't try to read it */
-	hdr.hdr_version = htonl(extended ? 3 : 2);
+	hdr.hdr_version = htonl(hdr_version);
 	hdr.hdr_entries = htonl(entries - removed);
 
 	git_SHA1_Init(&c);
 	if (ce_write(&c, newfd, &hdr, sizeof(hdr)) < 0)
 		return -1;
 
+	previous_name = (hdr_version == 4) ? &previous_name_buf : NULL;
 	for (i = 0; i < entries; i++) {
 		struct cache_entry *ce = cache[i];
 		if (ce->ce_flags & CE_REMOVE)
 			continue;
 		if (!ce_uptodate(ce) && is_racy_timestamp(istate, ce))
 			ce_smudge_racily_clean_entry(ce);
-		if (ce_write_entry(&c, newfd, ce) < 0)
+		if (ce_write_entry(&c, newfd, ce, previous_name) < 0)
 			return -1;
 	}
+	strbuf_release(&previous_name_buf);
 
 	/* Write extension data here */
 	if (istate->cache_tree) {
@@ -1560,6 +1816,17 @@
 		if (err)
 			return -1;
 	}
+	if (istate->resolve_undo) {
+		struct strbuf sb = STRBUF_INIT;
+
+		resolve_undo_write(&sb, istate->resolve_undo);
+		err = write_index_ext_header(&c, newfd, CACHE_EXT_RESOLVE_UNDO,
+					     sb.len) < 0
+			|| ce_write(&c, newfd, sb.buf, sb.len) < 0;
+		strbuf_release(&sb);
+		if (err)
+			return -1;
+	}
 
 	if (ce_flush(&c, newfd) || fstat(newfd, &st))
 		return -1;
@@ -1589,12 +1856,12 @@
 		if (!ce_stage(ce))
 			continue;
 		unmerged = 1;
-		len = strlen(ce->name);
+		len = ce_namelen(ce);
 		size = cache_entry_size(len);
 		new_ce = xcalloc(1, size);
-		hashcpy(new_ce->sha1, ce->sha1);
 		memcpy(new_ce->name, ce->name, len);
-		new_ce->ce_flags = create_ce_flags(len, 0);
+		new_ce->ce_flags = create_ce_flags(0) | CE_CONFLICTED;
+		new_ce->ce_namelen = len;
 		new_ce->ce_mode = ce->ce_mode;
 		if (add_index_entry(istate, new_ce, 0))
 			return error("%s: cannot drop to stage #0",
@@ -1604,81 +1871,6 @@
 	return unmerged;
 }
 
-struct update_callback_data
-{
-	int flags;
-	int add_errors;
-};
-
-static void update_callback(struct diff_queue_struct *q,
-			    struct diff_options *opt, void *cbdata)
-{
-	int i;
-	struct update_callback_data *data = cbdata;
-
-	for (i = 0; i < q->nr; i++) {
-		struct diff_filepair *p = q->queue[i];
-		const char *path = p->one->path;
-		switch (p->status) {
-		default:
-			die("unexpected diff status %c", p->status);
-		case DIFF_STATUS_UNMERGED:
-			/*
-			 * ADD_CACHE_IGNORE_REMOVAL is unset if "git
-			 * add -u" is calling us, In such a case, a
-			 * missing work tree file needs to be removed
-			 * if there is an unmerged entry at stage #2,
-			 * but such a diff record is followed by
-			 * another with DIFF_STATUS_DELETED (and if
-			 * there is no stage #2, we won't see DELETED
-			 * nor MODIFIED).  We can simply continue
-			 * either way.
-			 */
-			if (!(data->flags & ADD_CACHE_IGNORE_REMOVAL))
-				continue;
-			/*
-			 * Otherwise, it is "git add path" is asking
-			 * to explicitly add it; we fall through.  A
-			 * missing work tree file is an error and is
-			 * caught by add_file_to_index() in such a
-			 * case.
-			 */
-		case DIFF_STATUS_MODIFIED:
-		case DIFF_STATUS_TYPE_CHANGED:
-			if (add_file_to_index(&the_index, path, data->flags)) {
-				if (!(data->flags & ADD_CACHE_IGNORE_ERRORS))
-					die("updating files failed");
-				data->add_errors++;
-			}
-			break;
-		case DIFF_STATUS_DELETED:
-			if (data->flags & ADD_CACHE_IGNORE_REMOVAL)
-				break;
-			if (!(data->flags & ADD_CACHE_PRETEND))
-				remove_file_from_index(&the_index, path);
-			if (data->flags & (ADD_CACHE_PRETEND|ADD_CACHE_VERBOSE))
-				printf("remove '%s'\n", path);
-			break;
-		}
-	}
-}
-
-int add_files_to_cache(const char *prefix, const char **pathspec, int flags)
-{
-	struct update_callback_data data;
-	struct rev_info rev;
-	init_revisions(&rev, prefix);
-	setup_revisions(0, NULL, &rev, NULL);
-	rev.prune_data = pathspec;
-	rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
-	rev.diffopt.format_callback = update_callback;
-	data.flags = flags;
-	data.add_errors = 0;
-	rev.diffopt.format_callback_data = &data;
-	run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
-	return !!data.add_errors;
-}
-
 /*
  * Returns 1 if the path is an "other" path with respect to
  * the index; that is, the path is not mentioned in the index at all,
diff --git a/reflog-walk.c b/reflog-walk.c
index 5623ea6..b2fbdb2 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -8,6 +8,7 @@
 
 struct complete_reflogs {
 	char *ref;
+	const char *short_ref;
 	struct reflog_info {
 		unsigned char osha1[20], nsha1[20];
 		char *email;
@@ -49,9 +50,13 @@
 	for_each_reflog_ent(ref, read_one_reflog, reflogs);
 	if (reflogs->nr == 0) {
 		unsigned char sha1[20];
-		const char *name = resolve_ref(ref, sha1, 1, NULL);
-		if (name)
+		const char *name;
+		void *name_to_free;
+		name = name_to_free = resolve_refdup(ref, sha1, 1, NULL);
+		if (name) {
 			for_each_reflog_ent(name, read_one_reflog, reflogs);
+			free(name_to_free);
+		}
 	}
 	if (reflogs->nr == 0) {
 		int len = strlen(ref);
@@ -121,7 +126,12 @@
 }
 
 struct commit_reflog {
-	int flag, recno;
+	int recno;
+	enum selector_type {
+		SELECTOR_NONE,
+		SELECTOR_INDEX,
+		SELECTOR_DATE
+	} selector;
 	struct complete_reflogs *reflogs;
 };
 
@@ -145,6 +155,7 @@
 	struct complete_reflogs *reflogs;
 	char *branch, *at = strchr(name, '@');
 	struct commit_reflog *commit_reflog;
+	enum selector_type selector = SELECTOR_NONE;
 
 	if (commit->object.flags & UNINTERESTING)
 		die ("Cannot walk reflogs for %s", name);
@@ -157,21 +168,24 @@
 		if (*ep != '}') {
 			recno = -1;
 			timestamp = approxidate(at + 2);
+			selector = SELECTOR_DATE;
 		}
+		else
+			selector = SELECTOR_INDEX;
 	} else
 		recno = 0;
 
-	item = string_list_lookup(branch, &info->complete_reflogs);
+	item = string_list_lookup(&info->complete_reflogs, branch);
 	if (item)
 		reflogs = item->util;
 	else {
 		if (*branch == '\0') {
 			unsigned char sha1[20];
-			const char *head = resolve_ref("HEAD", sha1, 0, NULL);
-			if (!head)
-				die ("No current branch");
 			free(branch);
-			branch = xstrdup(head);
+			branch = resolve_refdup("HEAD", sha1, 0, NULL);
+			if (!branch)
+				die ("No current branch");
+
 		}
 		reflogs = read_complete_reflog(branch);
 		if (!reflogs || reflogs->nr == 0) {
@@ -189,13 +203,12 @@
 		}
 		if (!reflogs || reflogs->nr == 0)
 			return -1;
-		string_list_insert(branch, &info->complete_reflogs)->util
+		string_list_insert(&info->complete_reflogs, branch)->util
 			= reflogs;
 	}
 
 	commit_reflog = xcalloc(sizeof(struct commit_reflog), 1);
 	if (recno < 0) {
-		commit_reflog->flag = 1;
 		commit_reflog->recno = get_reflog_recno_by_time(reflogs, timestamp);
 		if (commit_reflog->recno < 0) {
 			free(branch);
@@ -204,6 +217,7 @@
 		}
 	} else
 		commit_reflog->recno = reflogs->nr - recno - 1;
+	commit_reflog->selector = selector;
 	commit_reflog->reflogs = reflogs;
 
 	add_commit_info(commit, commit_reflog, &info->reflogs);
@@ -238,39 +252,89 @@
 
 	commit->parents = xcalloc(sizeof(struct commit_list), 1);
 	commit->parents->item = commit_info->commit;
-	commit->object.flags &= ~(ADDED | SEEN | SHOWN);
 }
 
-void show_reflog_message(struct reflog_walk_info *info, int oneline,
-	enum date_mode dmode)
+void get_reflog_selector(struct strbuf *sb,
+			 struct reflog_walk_info *reflog_info,
+			 enum date_mode dmode, int force_date,
+			 int shorten)
 {
-	if (info && info->last_commit_reflog) {
-		struct commit_reflog *commit_reflog = info->last_commit_reflog;
+	struct commit_reflog *commit_reflog = reflog_info->last_commit_reflog;
+	struct reflog_info *info;
+	const char *printed_ref;
+
+	if (!commit_reflog)
+		return;
+
+	if (shorten) {
+		if (!commit_reflog->reflogs->short_ref)
+			commit_reflog->reflogs->short_ref
+				= shorten_unambiguous_ref(commit_reflog->reflogs->ref, 0);
+		printed_ref = commit_reflog->reflogs->short_ref;
+	} else {
+		printed_ref = commit_reflog->reflogs->ref;
+	}
+
+	strbuf_addf(sb, "%s@{", printed_ref);
+	if (commit_reflog->selector == SELECTOR_DATE ||
+	    (commit_reflog->selector == SELECTOR_NONE && force_date)) {
+		info = &commit_reflog->reflogs->items[commit_reflog->recno+1];
+		strbuf_addstr(sb, show_date(info->timestamp, info->tz, dmode));
+	} else {
+		strbuf_addf(sb, "%d", commit_reflog->reflogs->nr
+			    - 2 - commit_reflog->recno);
+	}
+
+	strbuf_addch(sb, '}');
+}
+
+void get_reflog_message(struct strbuf *sb,
+			struct reflog_walk_info *reflog_info)
+{
+	struct commit_reflog *commit_reflog = reflog_info->last_commit_reflog;
+	struct reflog_info *info;
+	size_t len;
+
+	if (!commit_reflog)
+		return;
+
+	info = &commit_reflog->reflogs->items[commit_reflog->recno+1];
+	len = strlen(info->message);
+	if (len > 0)
+		len--; /* strip away trailing newline */
+	strbuf_add(sb, info->message, len);
+}
+
+const char *get_reflog_ident(struct reflog_walk_info *reflog_info)
+{
+	struct commit_reflog *commit_reflog = reflog_info->last_commit_reflog;
+	struct reflog_info *info;
+
+	if (!commit_reflog)
+		return NULL;
+
+	info = &commit_reflog->reflogs->items[commit_reflog->recno+1];
+	return info->email;
+}
+
+void show_reflog_message(struct reflog_walk_info *reflog_info, int oneline,
+			 enum date_mode dmode, int force_date)
+{
+	if (reflog_info && reflog_info->last_commit_reflog) {
+		struct commit_reflog *commit_reflog = reflog_info->last_commit_reflog;
 		struct reflog_info *info;
+		struct strbuf selector = STRBUF_INIT;
 
 		info = &commit_reflog->reflogs->items[commit_reflog->recno+1];
+		get_reflog_selector(&selector, reflog_info, dmode, force_date, 0);
 		if (oneline) {
-			printf("%s@{", commit_reflog->reflogs->ref);
-			if (commit_reflog->flag || dmode)
-				printf("%s", show_date(info->timestamp,
-						       info->tz,
-						       dmode));
-			else
-				printf("%d", commit_reflog->reflogs->nr
-				       - 2 - commit_reflog->recno);
-			printf("}: %s", info->message);
+			printf("%s: %s", selector.buf, info->message);
 		}
 		else {
-			printf("Reflog: %s@{", commit_reflog->reflogs->ref);
-			if (commit_reflog->flag || dmode)
-				printf("%s", show_date(info->timestamp,
-							info->tz,
-							dmode));
-			else
-				printf("%d", commit_reflog->reflogs->nr
-				       - 2 - commit_reflog->recno);
-			printf("} (%s)\nReflog message: %s",
-			       info->email, info->message);
+			printf("Reflog: %s (%s)\nReflog message: %s",
+			       selector.buf, info->email, info->message);
 		}
+
+		strbuf_release(&selector);
 	}
 }
diff --git a/reflog-walk.h b/reflog-walk.h
index 74c9096..50265f5 100644
--- a/reflog-walk.h
+++ b/reflog-walk.h
@@ -3,12 +3,21 @@
 
 #include "cache.h"
 
+struct reflog_walk_info;
+
 extern void init_reflog_walk(struct reflog_walk_info** info);
 extern int add_reflog_for_walk(struct reflog_walk_info *info,
 		struct commit *commit, const char *name);
 extern void fake_reflog_parent(struct reflog_walk_info *info,
 		struct commit *commit);
 extern void show_reflog_message(struct reflog_walk_info *info, int,
-		enum date_mode);
+				enum date_mode, int force_date);
+extern void get_reflog_message(struct strbuf *sb,
+		struct reflog_walk_info *reflog_info);
+extern const char *get_reflog_ident(struct reflog_walk_info *reflog_info);
+extern void get_reflog_selector(struct strbuf *sb,
+		struct reflog_walk_info *reflog_info,
+		enum date_mode dmode, int force_date,
+		int shorten);
 
 #endif
diff --git a/refs.c b/refs.c
index e49eaa3..da74a2b 100644
--- a/refs.c
+++ b/refs.c
@@ -4,17 +4,778 @@
 #include "tag.h"
 #include "dir.h"
 
-/* ISSYMREF=01 and ISPACKED=02 are public interfaces */
-#define REF_KNOWS_PEELED 04
+/*
+ * Make sure "ref" is something reasonable to have under ".git/refs/";
+ * We do not like it if:
+ *
+ * - any path component of it begins with ".", or
+ * - it has double dots "..", or
+ * - it has ASCII control character, "~", "^", ":" or SP, anywhere, or
+ * - it ends with a "/".
+ * - it ends with ".lock"
+ * - it contains a "\" (backslash)
+ */
 
-struct ref_list {
-	struct ref_list *next;
-	unsigned char flag; /* ISSYMREF? ISPACKED? */
+/* Return true iff ch is not allowed in reference names. */
+static inline int bad_ref_char(int ch)
+{
+	if (((unsigned) ch) <= ' ' || ch == 0x7f ||
+	    ch == '~' || ch == '^' || ch == ':' || ch == '\\')
+		return 1;
+	/* 2.13 Pattern Matching Notation */
+	if (ch == '*' || ch == '?' || ch == '[') /* Unsupported */
+		return 1;
+	return 0;
+}
+
+/*
+ * Try to read one refname component from the front of refname.  Return
+ * the length of the component found, or -1 if the component is not
+ * legal.
+ */
+static int check_refname_component(const char *refname, int flags)
+{
+	const char *cp;
+	char last = '\0';
+
+	for (cp = refname; ; cp++) {
+		char ch = *cp;
+		if (ch == '\0' || ch == '/')
+			break;
+		if (bad_ref_char(ch))
+			return -1; /* Illegal character in refname. */
+		if (last == '.' && ch == '.')
+			return -1; /* Refname contains "..". */
+		if (last == '@' && ch == '{')
+			return -1; /* Refname contains "@{". */
+		last = ch;
+	}
+	if (cp == refname)
+		return 0; /* Component has zero length. */
+	if (refname[0] == '.') {
+		if (!(flags & REFNAME_DOT_COMPONENT))
+			return -1; /* Component starts with '.'. */
+		/*
+		 * Even if leading dots are allowed, don't allow "."
+		 * as a component (".." is prevented by a rule above).
+		 */
+		if (refname[1] == '\0')
+			return -1; /* Component equals ".". */
+	}
+	if (cp - refname >= 5 && !memcmp(cp - 5, ".lock", 5))
+		return -1; /* Refname ends with ".lock". */
+	return cp - refname;
+}
+
+int check_refname_format(const char *refname, int flags)
+{
+	int component_len, component_count = 0;
+
+	while (1) {
+		/* We are at the start of a path component. */
+		component_len = check_refname_component(refname, flags);
+		if (component_len <= 0) {
+			if ((flags & REFNAME_REFSPEC_PATTERN) &&
+					refname[0] == '*' &&
+					(refname[1] == '\0' || refname[1] == '/')) {
+				/* Accept one wildcard as a full refname component. */
+				flags &= ~REFNAME_REFSPEC_PATTERN;
+				component_len = 1;
+			} else {
+				return -1;
+			}
+		}
+		component_count++;
+		if (refname[component_len] == '\0')
+			break;
+		/* Skip to next component. */
+		refname += component_len + 1;
+	}
+
+	if (refname[component_len - 1] == '.')
+		return -1; /* Refname ends with '.'. */
+	if (!(flags & REFNAME_ALLOW_ONELEVEL) && component_count < 2)
+		return -1; /* Refname has only one component. */
+	return 0;
+}
+
+struct ref_entry;
+
+/*
+ * Information used (along with the information in ref_entry) to
+ * describe a single cached reference.  This data structure only
+ * occurs embedded in a union in struct ref_entry, and only when
+ * (ref_entry->flag & REF_DIR) is zero.
+ */
+struct ref_value {
 	unsigned char sha1[20];
 	unsigned char peeled[20];
+};
+
+struct ref_cache;
+
+/*
+ * Information used (along with the information in ref_entry) to
+ * describe a level in the hierarchy of references.  This data
+ * structure only occurs embedded in a union in struct ref_entry, and
+ * only when (ref_entry.flag & REF_DIR) is set.  In that case,
+ * (ref_entry.flag & REF_INCOMPLETE) determines whether the references
+ * in the directory have already been read:
+ *
+ *     (ref_entry.flag & REF_INCOMPLETE) unset -- a directory of loose
+ *         or packed references, already read.
+ *
+ *     (ref_entry.flag & REF_INCOMPLETE) set -- a directory of loose
+ *         references that hasn't been read yet (nor has any of its
+ *         subdirectories).
+ *
+ * Entries within a directory are stored within a growable array of
+ * pointers to ref_entries (entries, nr, alloc).  Entries 0 <= i <
+ * sorted are sorted by their component name in strcmp() order and the
+ * remaining entries are unsorted.
+ *
+ * Loose references are read lazily, one directory at a time.  When a
+ * directory of loose references is read, then all of the references
+ * in that directory are stored, and REF_INCOMPLETE stubs are created
+ * for any subdirectories, but the subdirectories themselves are not
+ * read.  The reading is triggered by get_ref_dir().
+ */
+struct ref_dir {
+	int nr, alloc;
+
+	/*
+	 * Entries with index 0 <= i < sorted are sorted by name.  New
+	 * entries are appended to the list unsorted, and are sorted
+	 * only when required; thus we avoid the need to sort the list
+	 * after the addition of every reference.
+	 */
+	int sorted;
+
+	/* A pointer to the ref_cache that contains this ref_dir. */
+	struct ref_cache *ref_cache;
+
+	struct ref_entry **entries;
+};
+
+/* ISSYMREF=0x01, ISPACKED=0x02, and ISBROKEN=0x04 are public interfaces */
+#define REF_KNOWS_PEELED 0x08
+
+/* ref_entry represents a directory of references */
+#define REF_DIR 0x10
+
+/*
+ * Entry has not yet been read from disk (used only for REF_DIR
+ * entries representing loose references)
+ */
+#define REF_INCOMPLETE 0x20
+
+/*
+ * A ref_entry represents either a reference or a "subdirectory" of
+ * references.
+ *
+ * Each directory in the reference namespace is represented by a
+ * ref_entry with (flags & REF_DIR) set and containing a subdir member
+ * that holds the entries in that directory that have been read so
+ * far.  If (flags & REF_INCOMPLETE) is set, then the directory and
+ * its subdirectories haven't been read yet.  REF_INCOMPLETE is only
+ * used for loose reference directories.
+ *
+ * References are represented by a ref_entry with (flags & REF_DIR)
+ * unset and a value member that describes the reference's value.  The
+ * flag member is at the ref_entry level, but it is also needed to
+ * interpret the contents of the value field (in other words, a
+ * ref_value object is not very much use without the enclosing
+ * ref_entry).
+ *
+ * Reference names cannot end with slash and directories' names are
+ * always stored with a trailing slash (except for the top-level
+ * directory, which is always denoted by "").  This has two nice
+ * consequences: (1) when the entries in each subdir are sorted
+ * lexicographically by name (as they usually are), the references in
+ * a whole tree can be generated in lexicographic order by traversing
+ * the tree in left-to-right, depth-first order; (2) the names of
+ * references and subdirectories cannot conflict, and therefore the
+ * presence of an empty subdirectory does not block the creation of a
+ * similarly-named reference.  (The fact that reference names with the
+ * same leading components can conflict *with each other* is a
+ * separate issue that is regulated by is_refname_available().)
+ *
+ * Please note that the name field contains the fully-qualified
+ * reference (or subdirectory) name.  Space could be saved by only
+ * storing the relative names.  But that would require the full names
+ * to be generated on the fly when iterating in do_for_each_ref(), and
+ * would break callback functions, who have always been able to assume
+ * that the name strings that they are passed will not be freed during
+ * the iteration.
+ */
+struct ref_entry {
+	unsigned char flag; /* ISSYMREF? ISPACKED? */
+	union {
+		struct ref_value value; /* if not (flags&REF_DIR) */
+		struct ref_dir subdir; /* if (flags&REF_DIR) */
+	} u;
+	/*
+	 * The full name of the reference (e.g., "refs/heads/master")
+	 * or the full name of the directory with a trailing slash
+	 * (e.g., "refs/heads/"):
+	 */
 	char name[FLEX_ARRAY];
 };
 
+static void read_loose_refs(const char *dirname, struct ref_dir *dir);
+
+static struct ref_dir *get_ref_dir(struct ref_entry *entry)
+{
+	struct ref_dir *dir;
+	assert(entry->flag & REF_DIR);
+	dir = &entry->u.subdir;
+	if (entry->flag & REF_INCOMPLETE) {
+		read_loose_refs(entry->name, dir);
+		entry->flag &= ~REF_INCOMPLETE;
+	}
+	return dir;
+}
+
+static struct ref_entry *create_ref_entry(const char *refname,
+					  const unsigned char *sha1, int flag,
+					  int check_name)
+{
+	int len;
+	struct ref_entry *ref;
+
+	if (check_name &&
+	    check_refname_format(refname, REFNAME_ALLOW_ONELEVEL|REFNAME_DOT_COMPONENT))
+		die("Reference has invalid format: '%s'", refname);
+	len = strlen(refname) + 1;
+	ref = xmalloc(sizeof(struct ref_entry) + len);
+	hashcpy(ref->u.value.sha1, sha1);
+	hashclr(ref->u.value.peeled);
+	memcpy(ref->name, refname, len);
+	ref->flag = flag;
+	return ref;
+}
+
+static void clear_ref_dir(struct ref_dir *dir);
+
+static void free_ref_entry(struct ref_entry *entry)
+{
+	if (entry->flag & REF_DIR) {
+		/*
+		 * Do not use get_ref_dir() here, as that might
+		 * trigger the reading of loose refs.
+		 */
+		clear_ref_dir(&entry->u.subdir);
+	}
+	free(entry);
+}
+
+/*
+ * Add a ref_entry to the end of dir (unsorted).  Entry is always
+ * stored directly in dir; no recursion into subdirectories is
+ * done.
+ */
+static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry)
+{
+	ALLOC_GROW(dir->entries, dir->nr + 1, dir->alloc);
+	dir->entries[dir->nr++] = entry;
+	/* optimize for the case that entries are added in order */
+	if (dir->nr == 1 ||
+	    (dir->nr == dir->sorted + 1 &&
+	     strcmp(dir->entries[dir->nr - 2]->name,
+		    dir->entries[dir->nr - 1]->name) < 0))
+		dir->sorted = dir->nr;
+}
+
+/*
+ * Clear and free all entries in dir, recursively.
+ */
+static void clear_ref_dir(struct ref_dir *dir)
+{
+	int i;
+	for (i = 0; i < dir->nr; i++)
+		free_ref_entry(dir->entries[i]);
+	free(dir->entries);
+	dir->sorted = dir->nr = dir->alloc = 0;
+	dir->entries = NULL;
+}
+
+/*
+ * Create a struct ref_entry object for the specified dirname.
+ * dirname is the name of the directory with a trailing slash (e.g.,
+ * "refs/heads/") or "" for the top-level directory.
+ */
+static struct ref_entry *create_dir_entry(struct ref_cache *ref_cache,
+					  const char *dirname, size_t len,
+					  int incomplete)
+{
+	struct ref_entry *direntry;
+	direntry = xcalloc(1, sizeof(struct ref_entry) + len + 1);
+	memcpy(direntry->name, dirname, len);
+	direntry->name[len] = '\0';
+	direntry->u.subdir.ref_cache = ref_cache;
+	direntry->flag = REF_DIR | (incomplete ? REF_INCOMPLETE : 0);
+	return direntry;
+}
+
+static int ref_entry_cmp(const void *a, const void *b)
+{
+	struct ref_entry *one = *(struct ref_entry **)a;
+	struct ref_entry *two = *(struct ref_entry **)b;
+	return strcmp(one->name, two->name);
+}
+
+static void sort_ref_dir(struct ref_dir *dir);
+
+struct string_slice {
+	size_t len;
+	const char *str;
+};
+
+static int ref_entry_cmp_sslice(const void *key_, const void *ent_)
+{
+	struct string_slice *key = (struct string_slice *)key_;
+	struct ref_entry *ent = *(struct ref_entry **)ent_;
+	int entlen = strlen(ent->name);
+	int cmplen = key->len < entlen ? key->len : entlen;
+	int cmp = memcmp(key->str, ent->name, cmplen);
+	if (cmp)
+		return cmp;
+	return key->len - entlen;
+}
+
+/*
+ * Return the entry with the given refname from the ref_dir
+ * (non-recursively), sorting dir if necessary.  Return NULL if no
+ * such entry is found.  dir must already be complete.
+ */
+static struct ref_entry *search_ref_dir(struct ref_dir *dir,
+					const char *refname, size_t len)
+{
+	struct ref_entry **r;
+	struct string_slice key;
+
+	if (refname == NULL || !dir->nr)
+		return NULL;
+
+	sort_ref_dir(dir);
+	key.len = len;
+	key.str = refname;
+	r = bsearch(&key, dir->entries, dir->nr, sizeof(*dir->entries),
+		    ref_entry_cmp_sslice);
+
+	if (r == NULL)
+		return NULL;
+
+	return *r;
+}
+
+/*
+ * Search for a directory entry directly within dir (without
+ * recursing).  Sort dir if necessary.  subdirname must be a directory
+ * name (i.e., end in '/').  If mkdir is set, then create the
+ * directory if it is missing; otherwise, return NULL if the desired
+ * directory cannot be found.  dir must already be complete.
+ */
+static struct ref_dir *search_for_subdir(struct ref_dir *dir,
+					 const char *subdirname, size_t len,
+					 int mkdir)
+{
+	struct ref_entry *entry = search_ref_dir(dir, subdirname, len);
+	if (!entry) {
+		if (!mkdir)
+			return NULL;
+		/*
+		 * Since dir is complete, the absence of a subdir
+		 * means that the subdir really doesn't exist;
+		 * therefore, create an empty record for it but mark
+		 * the record complete.
+		 */
+		entry = create_dir_entry(dir->ref_cache, subdirname, len, 0);
+		add_entry_to_dir(dir, entry);
+	}
+	return get_ref_dir(entry);
+}
+
+/*
+ * If refname is a reference name, find the ref_dir within the dir
+ * tree that should hold refname.  If refname is a directory name
+ * (i.e., ends in '/'), then return that ref_dir itself.  dir must
+ * represent the top-level directory and must already be complete.
+ * Sort ref_dirs and recurse into subdirectories as necessary.  If
+ * mkdir is set, then create any missing directories; otherwise,
+ * return NULL if the desired directory cannot be found.
+ */
+static struct ref_dir *find_containing_dir(struct ref_dir *dir,
+					   const char *refname, int mkdir)
+{
+	const char *slash;
+	for (slash = strchr(refname, '/'); slash; slash = strchr(slash + 1, '/')) {
+		size_t dirnamelen = slash - refname + 1;
+		struct ref_dir *subdir;
+		subdir = search_for_subdir(dir, refname, dirnamelen, mkdir);
+		if (!subdir) {
+			dir = NULL;
+			break;
+		}
+		dir = subdir;
+	}
+
+	return dir;
+}
+
+/*
+ * Find the value entry with the given name in dir, sorting ref_dirs
+ * and recursing into subdirectories as necessary.  If the name is not
+ * found or it corresponds to a directory entry, return NULL.
+ */
+static struct ref_entry *find_ref(struct ref_dir *dir, const char *refname)
+{
+	struct ref_entry *entry;
+	dir = find_containing_dir(dir, refname, 0);
+	if (!dir)
+		return NULL;
+	entry = search_ref_dir(dir, refname, strlen(refname));
+	return (entry && !(entry->flag & REF_DIR)) ? entry : NULL;
+}
+
+/*
+ * Add a ref_entry to the ref_dir (unsorted), recursing into
+ * subdirectories as necessary.  dir must represent the top-level
+ * directory.  Return 0 on success.
+ */
+static int add_ref(struct ref_dir *dir, struct ref_entry *ref)
+{
+	dir = find_containing_dir(dir, ref->name, 1);
+	if (!dir)
+		return -1;
+	add_entry_to_dir(dir, ref);
+	return 0;
+}
+
+/*
+ * Emit a warning and return true iff ref1 and ref2 have the same name
+ * and the same sha1.  Die if they have the same name but different
+ * sha1s.
+ */
+static int is_dup_ref(const struct ref_entry *ref1, const struct ref_entry *ref2)
+{
+	if (strcmp(ref1->name, ref2->name))
+		return 0;
+
+	/* Duplicate name; make sure that they don't conflict: */
+
+	if ((ref1->flag & REF_DIR) || (ref2->flag & REF_DIR))
+		/* This is impossible by construction */
+		die("Reference directory conflict: %s", ref1->name);
+
+	if (hashcmp(ref1->u.value.sha1, ref2->u.value.sha1))
+		die("Duplicated ref, and SHA1s don't match: %s", ref1->name);
+
+	warning("Duplicated ref: %s", ref1->name);
+	return 1;
+}
+
+/*
+ * Sort the entries in dir non-recursively (if they are not already
+ * sorted) and remove any duplicate entries.
+ */
+static void sort_ref_dir(struct ref_dir *dir)
+{
+	int i, j;
+	struct ref_entry *last = NULL;
+
+	/*
+	 * This check also prevents passing a zero-length array to qsort(),
+	 * which is a problem on some platforms.
+	 */
+	if (dir->sorted == dir->nr)
+		return;
+
+	qsort(dir->entries, dir->nr, sizeof(*dir->entries), ref_entry_cmp);
+
+	/* Remove any duplicates: */
+	for (i = 0, j = 0; j < dir->nr; j++) {
+		struct ref_entry *entry = dir->entries[j];
+		if (last && is_dup_ref(last, entry))
+			free_ref_entry(entry);
+		else
+			last = dir->entries[i++] = entry;
+	}
+	dir->sorted = dir->nr = i;
+}
+
+#define DO_FOR_EACH_INCLUDE_BROKEN 01
+
+static struct ref_entry *current_ref;
+
+static int do_one_ref(const char *base, each_ref_fn fn, int trim,
+		      int flags, void *cb_data, struct ref_entry *entry)
+{
+	int retval;
+	if (prefixcmp(entry->name, base))
+		return 0;
+
+	if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) {
+		if (entry->flag & REF_ISBROKEN)
+			return 0; /* ignore broken refs e.g. dangling symref */
+		if (!has_sha1_file(entry->u.value.sha1)) {
+			error("%s does not point to a valid object!", entry->name);
+			return 0;
+		}
+	}
+	current_ref = entry;
+	retval = fn(entry->name + trim, entry->u.value.sha1, entry->flag, cb_data);
+	current_ref = NULL;
+	return retval;
+}
+
+/*
+ * Call fn for each reference in dir that has index in the range
+ * offset <= index < dir->nr.  Recurse into subdirectories that are in
+ * that index range, sorting them before iterating.  This function
+ * does not sort dir itself; it should be sorted beforehand.
+ */
+static int do_for_each_ref_in_dir(struct ref_dir *dir, int offset,
+				  const char *base,
+				  each_ref_fn fn, int trim, int flags, void *cb_data)
+{
+	int i;
+	assert(dir->sorted == dir->nr);
+	for (i = offset; i < dir->nr; i++) {
+		struct ref_entry *entry = dir->entries[i];
+		int retval;
+		if (entry->flag & REF_DIR) {
+			struct ref_dir *subdir = get_ref_dir(entry);
+			sort_ref_dir(subdir);
+			retval = do_for_each_ref_in_dir(subdir, 0,
+							base, fn, trim, flags, cb_data);
+		} else {
+			retval = do_one_ref(base, fn, trim, flags, cb_data, entry);
+		}
+		if (retval)
+			return retval;
+	}
+	return 0;
+}
+
+/*
+ * Call fn for each reference in the union of dir1 and dir2, in order
+ * by refname.  Recurse into subdirectories.  If a value entry appears
+ * in both dir1 and dir2, then only process the version that is in
+ * dir2.  The input dirs must already be sorted, but subdirs will be
+ * sorted as needed.
+ */
+static int do_for_each_ref_in_dirs(struct ref_dir *dir1,
+				   struct ref_dir *dir2,
+				   const char *base, each_ref_fn fn, int trim,
+				   int flags, void *cb_data)
+{
+	int retval;
+	int i1 = 0, i2 = 0;
+
+	assert(dir1->sorted == dir1->nr);
+	assert(dir2->sorted == dir2->nr);
+	while (1) {
+		struct ref_entry *e1, *e2;
+		int cmp;
+		if (i1 == dir1->nr) {
+			return do_for_each_ref_in_dir(dir2, i2,
+						      base, fn, trim, flags, cb_data);
+		}
+		if (i2 == dir2->nr) {
+			return do_for_each_ref_in_dir(dir1, i1,
+						      base, fn, trim, flags, cb_data);
+		}
+		e1 = dir1->entries[i1];
+		e2 = dir2->entries[i2];
+		cmp = strcmp(e1->name, e2->name);
+		if (cmp == 0) {
+			if ((e1->flag & REF_DIR) && (e2->flag & REF_DIR)) {
+				/* Both are directories; descend them in parallel. */
+				struct ref_dir *subdir1 = get_ref_dir(e1);
+				struct ref_dir *subdir2 = get_ref_dir(e2);
+				sort_ref_dir(subdir1);
+				sort_ref_dir(subdir2);
+				retval = do_for_each_ref_in_dirs(
+						subdir1, subdir2,
+						base, fn, trim, flags, cb_data);
+				i1++;
+				i2++;
+			} else if (!(e1->flag & REF_DIR) && !(e2->flag & REF_DIR)) {
+				/* Both are references; ignore the one from dir1. */
+				retval = do_one_ref(base, fn, trim, flags, cb_data, e2);
+				i1++;
+				i2++;
+			} else {
+				die("conflict between reference and directory: %s",
+				    e1->name);
+			}
+		} else {
+			struct ref_entry *e;
+			if (cmp < 0) {
+				e = e1;
+				i1++;
+			} else {
+				e = e2;
+				i2++;
+			}
+			if (e->flag & REF_DIR) {
+				struct ref_dir *subdir = get_ref_dir(e);
+				sort_ref_dir(subdir);
+				retval = do_for_each_ref_in_dir(
+						subdir, 0,
+						base, fn, trim, flags, cb_data);
+			} else {
+				retval = do_one_ref(base, fn, trim, flags, cb_data, e);
+			}
+		}
+		if (retval)
+			return retval;
+	}
+	if (i1 < dir1->nr)
+		return do_for_each_ref_in_dir(dir1, i1,
+					      base, fn, trim, flags, cb_data);
+	if (i2 < dir2->nr)
+		return do_for_each_ref_in_dir(dir2, i2,
+					      base, fn, trim, flags, cb_data);
+	return 0;
+}
+
+/*
+ * Return true iff refname1 and refname2 conflict with each other.
+ * Two reference names conflict if one of them exactly matches the
+ * leading components of the other; e.g., "foo/bar" conflicts with
+ * both "foo" and with "foo/bar/baz" but not with "foo/bar" or
+ * "foo/barbados".
+ */
+static int names_conflict(const char *refname1, const char *refname2)
+{
+	for (; *refname1 && *refname1 == *refname2; refname1++, refname2++)
+		;
+	return (*refname1 == '\0' && *refname2 == '/')
+		|| (*refname1 == '/' && *refname2 == '\0');
+}
+
+struct name_conflict_cb {
+	const char *refname;
+	const char *oldrefname;
+	const char *conflicting_refname;
+};
+
+static int name_conflict_fn(const char *existingrefname, const unsigned char *sha1,
+			    int flags, void *cb_data)
+{
+	struct name_conflict_cb *data = (struct name_conflict_cb *)cb_data;
+	if (data->oldrefname && !strcmp(data->oldrefname, existingrefname))
+		return 0;
+	if (names_conflict(data->refname, existingrefname)) {
+		data->conflicting_refname = existingrefname;
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * Return true iff a reference named refname could be created without
+ * conflicting with the name of an existing reference in array.  If
+ * oldrefname is non-NULL, ignore potential conflicts with oldrefname
+ * (e.g., because oldrefname is scheduled for deletion in the same
+ * operation).
+ */
+static int is_refname_available(const char *refname, const char *oldrefname,
+				struct ref_dir *dir)
+{
+	struct name_conflict_cb data;
+	data.refname = refname;
+	data.oldrefname = oldrefname;
+	data.conflicting_refname = NULL;
+
+	sort_ref_dir(dir);
+	if (do_for_each_ref_in_dir(dir, 0, "", name_conflict_fn,
+				   0, DO_FOR_EACH_INCLUDE_BROKEN,
+				   &data)) {
+		error("'%s' exists; cannot create '%s'",
+		      data.conflicting_refname, refname);
+		return 0;
+	}
+	return 1;
+}
+
+/*
+ * Future: need to be in "struct repository"
+ * when doing a full libification.
+ */
+static struct ref_cache {
+	struct ref_cache *next;
+	struct ref_entry *loose;
+	struct ref_entry *packed;
+	/* The submodule name, or "" for the main repo. */
+	char name[FLEX_ARRAY];
+} *ref_cache;
+
+static void clear_packed_ref_cache(struct ref_cache *refs)
+{
+	if (refs->packed) {
+		free_ref_entry(refs->packed);
+		refs->packed = NULL;
+	}
+}
+
+static void clear_loose_ref_cache(struct ref_cache *refs)
+{
+	if (refs->loose) {
+		free_ref_entry(refs->loose);
+		refs->loose = NULL;
+	}
+}
+
+static struct ref_cache *create_ref_cache(const char *submodule)
+{
+	int len;
+	struct ref_cache *refs;
+	if (!submodule)
+		submodule = "";
+	len = strlen(submodule) + 1;
+	refs = xcalloc(1, sizeof(struct ref_cache) + len);
+	memcpy(refs->name, submodule, len);
+	return refs;
+}
+
+/*
+ * Return a pointer to a ref_cache for the specified submodule. For
+ * the main repository, use submodule==NULL. The returned structure
+ * will be allocated and initialized but not necessarily populated; it
+ * should not be freed.
+ */
+static struct ref_cache *get_ref_cache(const char *submodule)
+{
+	struct ref_cache *refs = ref_cache;
+	if (!submodule)
+		submodule = "";
+	while (refs) {
+		if (!strcmp(submodule, refs->name))
+			return refs;
+		refs = refs->next;
+	}
+
+	refs = create_ref_cache(submodule);
+	refs->next = ref_cache;
+	ref_cache = refs;
+	return refs;
+}
+
+void invalidate_ref_cache(const char *submodule)
+{
+	struct ref_cache *refs = get_ref_cache(submodule);
+	clear_packed_ref_cache(refs);
+	clear_loose_ref_cache(refs);
+}
+
+/*
+ * Parse one line from a packed-refs file.  Write the SHA1 to sha1.
+ * Return a pointer to the refname within the line (null-terminated),
+ * or NULL if there was a problem.
+ */
 static const char *parse_ref_line(char *line, unsigned char *sha1)
 {
 	/*
@@ -43,155 +804,15 @@
 	return line;
 }
 
-static struct ref_list *add_ref(const char *name, const unsigned char *sha1,
-				int flag, struct ref_list *list,
-				struct ref_list **new_entry)
+static void read_packed_refs(FILE *f, struct ref_dir *dir)
 {
-	int len;
-	struct ref_list *entry;
-
-	/* Allocate it and add it in.. */
-	len = strlen(name) + 1;
-	entry = xmalloc(sizeof(struct ref_list) + len);
-	hashcpy(entry->sha1, sha1);
-	hashclr(entry->peeled);
-	memcpy(entry->name, name, len);
-	entry->flag = flag;
-	entry->next = list;
-	if (new_entry)
-		*new_entry = entry;
-	return entry;
-}
-
-/* merge sort the ref list */
-static struct ref_list *sort_ref_list(struct ref_list *list)
-{
-	int psize, qsize, last_merge_count, cmp;
-	struct ref_list *p, *q, *l, *e;
-	struct ref_list *new_list = list;
-	int k = 1;
-	int merge_count = 0;
-
-	if (!list)
-		return list;
-
-	do {
-		last_merge_count = merge_count;
-		merge_count = 0;
-
-		psize = 0;
-
-		p = new_list;
-		q = new_list;
-		new_list = NULL;
-		l = NULL;
-
-		while (p) {
-			merge_count++;
-
-			while (psize < k && q->next) {
-				q = q->next;
-				psize++;
-			}
-			qsize = k;
-
-			while ((psize > 0) || (qsize > 0 && q)) {
-				if (qsize == 0 || !q) {
-					e = p;
-					p = p->next;
-					psize--;
-				} else if (psize == 0) {
-					e = q;
-					q = q->next;
-					qsize--;
-				} else {
-					cmp = strcmp(q->name, p->name);
-					if (cmp < 0) {
-						e = q;
-						q = q->next;
-						qsize--;
-					} else if (cmp > 0) {
-						e = p;
-						p = p->next;
-						psize--;
-					} else {
-						if (hashcmp(q->sha1, p->sha1))
-							die("Duplicated ref, and SHA1s don't match: %s",
-							    q->name);
-						warning("Duplicated ref: %s", q->name);
-						e = q;
-						q = q->next;
-						qsize--;
-						free(e);
-						e = p;
-						p = p->next;
-						psize--;
-					}
-				}
-
-				e->next = NULL;
-
-				if (l)
-					l->next = e;
-				if (!new_list)
-					new_list = e;
-				l = e;
-			}
-
-			p = q;
-		};
-
-		k = k * 2;
-	} while ((last_merge_count != merge_count) || (last_merge_count != 1));
-
-	return new_list;
-}
-
-/*
- * Future: need to be in "struct repository"
- * when doing a full libification.
- */
-static struct cached_refs {
-	char did_loose;
-	char did_packed;
-	struct ref_list *loose;
-	struct ref_list *packed;
-} cached_refs;
-static struct ref_list *current_ref;
-
-static struct ref_list *extra_refs;
-
-static void free_ref_list(struct ref_list *list)
-{
-	struct ref_list *next;
-	for ( ; list; list = next) {
-		next = list->next;
-		free(list);
-	}
-}
-
-static void invalidate_cached_refs(void)
-{
-	struct cached_refs *ca = &cached_refs;
-
-	if (ca->did_loose && ca->loose)
-		free_ref_list(ca->loose);
-	if (ca->did_packed && ca->packed)
-		free_ref_list(ca->packed);
-	ca->loose = ca->packed = NULL;
-	ca->did_loose = ca->did_packed = 0;
-}
-
-static void read_packed_refs(FILE *f, struct cached_refs *cached_refs)
-{
-	struct ref_list *list = NULL;
-	struct ref_list *last = NULL;
+	struct ref_entry *last = NULL;
 	char refline[PATH_MAX];
 	int flag = REF_ISPACKED;
 
 	while (fgets(refline, sizeof(refline), f)) {
 		unsigned char sha1[20];
-		const char *name;
+		const char *refname;
 		static const char header[] = "# pack-refs with:";
 
 		if (!strncmp(refline, header, sizeof(header)-1)) {
@@ -202,9 +823,10 @@
 			continue;
 		}
 
-		name = parse_ref_line(refline, sha1);
-		if (name) {
-			list = add_ref(name, sha1, flag, list, &last);
+		refname = parse_ref_line(refline, sha1);
+		if (refname) {
+			last = create_ref_entry(refname, sha1, flag, 1);
+			add_ref(dir, last);
 			continue;
 		}
 		if (last &&
@@ -212,159 +834,161 @@
 		    strlen(refline) == 42 &&
 		    refline[41] == '\n' &&
 		    !get_sha1_hex(refline + 1, sha1))
-			hashcpy(last->peeled, sha1);
+			hashcpy(last->u.value.peeled, sha1);
 	}
-	cached_refs->packed = sort_ref_list(list);
 }
 
-void add_extra_ref(const char *name, const unsigned char *sha1, int flag)
+static struct ref_dir *get_packed_refs(struct ref_cache *refs)
 {
-	extra_refs = add_ref(name, sha1, flag, extra_refs, NULL);
-}
+	if (!refs->packed) {
+		const char *packed_refs_file;
+		FILE *f;
 
-void clear_extra_refs(void)
-{
-	free_ref_list(extra_refs);
-	extra_refs = NULL;
-}
-
-static struct ref_list *get_packed_refs(void)
-{
-	if (!cached_refs.did_packed) {
-		FILE *f = fopen(git_path("packed-refs"), "r");
-		cached_refs.packed = NULL;
+		refs->packed = create_dir_entry(refs, "", 0, 0);
+		if (*refs->name)
+			packed_refs_file = git_path_submodule(refs->name, "packed-refs");
+		else
+			packed_refs_file = git_path("packed-refs");
+		f = fopen(packed_refs_file, "r");
 		if (f) {
-			read_packed_refs(f, &cached_refs);
+			read_packed_refs(f, get_ref_dir(refs->packed));
 			fclose(f);
 		}
-		cached_refs.did_packed = 1;
 	}
-	return cached_refs.packed;
+	return get_ref_dir(refs->packed);
 }
 
-static struct ref_list *get_ref_dir(const char *base, struct ref_list *list)
+void add_packed_ref(const char *refname, const unsigned char *sha1)
 {
-	DIR *dir = opendir(git_path("%s", base));
+	add_ref(get_packed_refs(get_ref_cache(NULL)),
+			create_ref_entry(refname, sha1, REF_ISPACKED, 1));
+}
 
-	if (dir) {
-		struct dirent *de;
-		int baselen = strlen(base);
-		char *ref = xmalloc(baselen + 257);
+/*
+ * Read the loose references from the namespace dirname into dir
+ * (without recursing).  dirname must end with '/'.  dir must be the
+ * directory entry corresponding to dirname.
+ */
+static void read_loose_refs(const char *dirname, struct ref_dir *dir)
+{
+	struct ref_cache *refs = dir->ref_cache;
+	DIR *d;
+	const char *path;
+	struct dirent *de;
+	int dirnamelen = strlen(dirname);
+	struct strbuf refname;
 
-		memcpy(ref, base, baselen);
-		if (baselen && base[baselen-1] != '/')
-			ref[baselen++] = '/';
+	if (*refs->name)
+		path = git_path_submodule(refs->name, "%s", dirname);
+	else
+		path = git_path("%s", dirname);
 
-		while ((de = readdir(dir)) != NULL) {
-			unsigned char sha1[20];
-			struct stat st;
-			int flag;
-			int namelen;
+	d = opendir(path);
+	if (!d)
+		return;
 
-			if (de->d_name[0] == '.')
-				continue;
-			namelen = strlen(de->d_name);
-			if (namelen > 255)
-				continue;
-			if (has_extension(de->d_name, ".lock"))
-				continue;
-			memcpy(ref + baselen, de->d_name, namelen+1);
-			if (stat(git_path("%s", ref), &st) < 0)
-				continue;
-			if (S_ISDIR(st.st_mode)) {
-				list = get_ref_dir(ref, list);
-				continue;
-			}
-			if (!resolve_ref(ref, sha1, 1, &flag))
+	strbuf_init(&refname, dirnamelen + 257);
+	strbuf_add(&refname, dirname, dirnamelen);
+
+	while ((de = readdir(d)) != NULL) {
+		unsigned char sha1[20];
+		struct stat st;
+		int flag;
+		const char *refdir;
+
+		if (de->d_name[0] == '.')
+			continue;
+		if (has_extension(de->d_name, ".lock"))
+			continue;
+		strbuf_addstr(&refname, de->d_name);
+		refdir = *refs->name
+			? git_path_submodule(refs->name, "%s", refname.buf)
+			: git_path("%s", refname.buf);
+		if (stat(refdir, &st) < 0) {
+			; /* silently ignore */
+		} else if (S_ISDIR(st.st_mode)) {
+			strbuf_addch(&refname, '/');
+			add_entry_to_dir(dir,
+					 create_dir_entry(refs, refname.buf,
+							  refname.len, 1));
+		} else {
+			if (*refs->name) {
 				hashclr(sha1);
-			list = add_ref(ref, sha1, flag, list, NULL);
+				flag = 0;
+				if (resolve_gitlink_ref(refs->name, refname.buf, sha1) < 0) {
+					hashclr(sha1);
+					flag |= REF_ISBROKEN;
+				}
+			} else if (read_ref_full(refname.buf, sha1, 1, &flag)) {
+				hashclr(sha1);
+				flag |= REF_ISBROKEN;
+			}
+			add_entry_to_dir(dir,
+					 create_ref_entry(refname.buf, sha1, flag, 1));
 		}
-		free(ref);
-		closedir(dir);
+		strbuf_setlen(&refname, dirnamelen);
 	}
-	return sort_ref_list(list);
+	strbuf_release(&refname);
+	closedir(d);
 }
 
-struct warn_if_dangling_data {
-	const char *refname;
-	const char *msg_fmt;
-};
-
-static int warn_if_dangling_symref(const char *refname, const unsigned char *sha1,
-				   int flags, void *cb_data)
+static struct ref_dir *get_loose_refs(struct ref_cache *refs)
 {
-	struct warn_if_dangling_data *d = cb_data;
-	const char *resolves_to;
-	unsigned char junk[20];
-
-	if (!(flags & REF_ISSYMREF))
-		return 0;
-
-	resolves_to = resolve_ref(refname, junk, 0, NULL);
-	if (!resolves_to || strcmp(resolves_to, d->refname))
-		return 0;
-
-	printf(d->msg_fmt, refname);
-	return 0;
-}
-
-void warn_dangling_symref(const char *msg_fmt, const char *refname)
-{
-	struct warn_if_dangling_data data = { refname, msg_fmt };
-	for_each_rawref(warn_if_dangling_symref, &data);
-}
-
-static struct ref_list *get_loose_refs(void)
-{
-	if (!cached_refs.did_loose) {
-		cached_refs.loose = get_ref_dir("refs", NULL);
-		cached_refs.did_loose = 1;
+	if (!refs->loose) {
+		/*
+		 * Mark the top-level directory complete because we
+		 * are about to read the only subdirectory that can
+		 * hold references:
+		 */
+		refs->loose = create_dir_entry(refs, "", 0, 0);
+		/*
+		 * Create an incomplete entry for "refs/":
+		 */
+		add_entry_to_dir(get_ref_dir(refs->loose),
+				 create_dir_entry(refs, "refs/", 5, 1));
 	}
-	return cached_refs.loose;
+	return get_ref_dir(refs->loose);
 }
 
 /* We allow "recursive" symbolic refs. Only within reason, though */
 #define MAXDEPTH 5
 #define MAXREFLEN (1024)
 
-static int resolve_gitlink_packed_ref(char *name, int pathlen, const char *refname, unsigned char *result)
+/*
+ * Called by resolve_gitlink_ref_recursive() after it failed to read
+ * from the loose refs in ref_cache refs. Find <refname> in the
+ * packed-refs file for the submodule.
+ */
+static int resolve_gitlink_packed_ref(struct ref_cache *refs,
+				      const char *refname, unsigned char *sha1)
 {
-	FILE *f;
-	struct cached_refs refs;
-	struct ref_list *ref;
-	int retval;
+	struct ref_entry *ref;
+	struct ref_dir *dir = get_packed_refs(refs);
 
-	strcpy(name + pathlen, "packed-refs");
-	f = fopen(name, "r");
-	if (!f)
+	ref = find_ref(dir, refname);
+	if (ref == NULL)
 		return -1;
-	read_packed_refs(f, &refs);
-	fclose(f);
-	ref = refs.packed;
-	retval = -1;
-	while (ref) {
-		if (!strcmp(ref->name, refname)) {
-			retval = 0;
-			memcpy(result, ref->sha1, 20);
-			break;
-		}
-		ref = ref->next;
-	}
-	free_ref_list(refs.packed);
-	return retval;
+
+	memcpy(sha1, ref->u.value.sha1, 20);
+	return 0;
 }
 
-static int resolve_gitlink_ref_recursive(char *name, int pathlen, const char *refname, unsigned char *result, int recursion)
+static int resolve_gitlink_ref_recursive(struct ref_cache *refs,
+					 const char *refname, unsigned char *sha1,
+					 int recursion)
 {
-	int fd, len = strlen(refname);
+	int fd, len;
 	char buffer[128], *p;
+	char *path;
 
-	if (recursion > MAXDEPTH || len > MAXREFLEN)
+	if (recursion > MAXDEPTH || strlen(refname) > MAXREFLEN)
 		return -1;
-	memcpy(name + pathlen, refname, len+1);
-	fd = open(name, O_RDONLY);
+	path = *refs->name
+		? git_path_submodule(refs->name, "%s", refname)
+		: git_path("%s", refname);
+	fd = open(path, O_RDONLY);
 	if (fd < 0)
-		return resolve_gitlink_packed_ref(name, pathlen, refname, result);
+		return resolve_gitlink_packed_ref(refs, refname, sha1);
 
 	len = read(fd, buffer, sizeof(buffer)-1);
 	close(fd);
@@ -375,7 +999,7 @@
 	buffer[len] = 0;
 
 	/* Was it a detached head or an old-fashioned symlink? */
-	if (!get_sha1_hex(buffer, result))
+	if (!get_sha1_hex(buffer, sha1))
 		return 0;
 
 	/* Symref? */
@@ -385,60 +1009,55 @@
 	while (isspace(*p))
 		p++;
 
-	return resolve_gitlink_ref_recursive(name, pathlen, p, result, recursion+1);
+	return resolve_gitlink_ref_recursive(refs, p, sha1, recursion+1);
 }
 
-int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *result)
+int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sha1)
 {
 	int len = strlen(path), retval;
-	char *gitdir;
-	const char *tmp;
+	char *submodule;
+	struct ref_cache *refs;
 
 	while (len && path[len-1] == '/')
 		len--;
 	if (!len)
 		return -1;
-	gitdir = xmalloc(len + MAXREFLEN + 8);
-	memcpy(gitdir, path, len);
-	memcpy(gitdir + len, "/.git", 6);
-	len += 5;
+	submodule = xstrndup(path, len);
+	refs = get_ref_cache(submodule);
+	free(submodule);
 
-	tmp = read_gitfile_gently(gitdir);
-	if (tmp) {
-		free(gitdir);
-		len = strlen(tmp);
-		gitdir = xmalloc(len + MAXREFLEN + 3);
-		memcpy(gitdir, tmp, len);
-	}
-	gitdir[len] = '/';
-	gitdir[++len] = '\0';
-	retval = resolve_gitlink_ref_recursive(gitdir, len, refname, result, 0);
-	free(gitdir);
+	retval = resolve_gitlink_ref_recursive(refs, refname, sha1, 0);
 	return retval;
 }
 
 /*
- * If the "reading" argument is set, this function finds out what _object_
- * the ref points at by "reading" the ref.  The ref, if it is not symbolic,
- * has to exist, and if it is symbolic, it has to point at an existing ref,
- * because the "read" goes through the symref to the ref it points at.
- *
- * The access that is not "reading" may often be "writing", but does not
- * have to; it can be merely checking _where it leads to_. If it is a
- * prelude to "writing" to the ref, a write to a symref that points at
- * yet-to-be-born ref will create the real ref pointed by the symref.
- * reading=0 allows the caller to check where such a symref leads to.
+ * Try to read ref from the packed references.  On success, set sha1
+ * and return 0; otherwise, return -1.
  */
-const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag)
+static int get_packed_ref(const char *refname, unsigned char *sha1)
+{
+	struct ref_dir *packed = get_packed_refs(get_ref_cache(NULL));
+	struct ref_entry *entry = find_ref(packed, refname);
+	if (entry) {
+		hashcpy(sha1, entry->u.value.sha1);
+		return 0;
+	}
+	return -1;
+}
+
+const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int reading, int *flag)
 {
 	int depth = MAXDEPTH;
 	ssize_t len;
 	char buffer[256];
-	static char ref_buffer[256];
+	static char refname_buffer[256];
 
 	if (flag)
 		*flag = 0;
 
+	if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
+		return NULL;
+
 	for (;;) {
 		char path[PATH_MAX];
 		struct stat st;
@@ -448,32 +1067,39 @@
 		if (--depth < 0)
 			return NULL;
 
-		git_snpath(path, sizeof(path), "%s", ref);
-		/* Special case: non-existing file. */
+		git_snpath(path, sizeof(path), "%s", refname);
+
 		if (lstat(path, &st) < 0) {
-			struct ref_list *list = get_packed_refs();
-			while (list) {
-				if (!strcmp(ref, list->name)) {
-					hashcpy(sha1, list->sha1);
-					if (flag)
-						*flag |= REF_ISPACKED;
-					return ref;
-				}
-				list = list->next;
-			}
-			if (reading || errno != ENOENT)
+			if (errno != ENOENT)
 				return NULL;
-			hashclr(sha1);
-			return ref;
+			/*
+			 * The loose reference file does not exist;
+			 * check for a packed reference.
+			 */
+			if (!get_packed_ref(refname, sha1)) {
+				if (flag)
+					*flag |= REF_ISPACKED;
+				return refname;
+			}
+			/* The reference is not a packed reference, either. */
+			if (reading) {
+				return NULL;
+			} else {
+				hashclr(sha1);
+				return refname;
+			}
 		}
 
 		/* Follow "normalized" - ie "refs/.." symlinks by hand */
 		if (S_ISLNK(st.st_mode)) {
 			len = readlink(path, buffer, sizeof(buffer)-1);
-			if (len >= 5 && !memcmp("refs/", buffer, 5)) {
-				buffer[len] = 0;
-				strcpy(ref_buffer, buffer);
-				ref = ref_buffer;
+			if (len < 0)
+				return NULL;
+			buffer[len] = 0;
+			if (!prefixcmp(buffer, "refs/") &&
+					!check_refname_format(buffer, 0)) {
+				strcpy(refname_buffer, buffer);
+				refname = refname_buffer;
 				if (flag)
 					*flag |= REF_ISSYMREF;
 				continue;
@@ -495,94 +1121,111 @@
 			return NULL;
 		len = read_in_full(fd, buffer, sizeof(buffer)-1);
 		close(fd);
+		if (len < 0)
+			return NULL;
+		while (len && isspace(buffer[len-1]))
+			len--;
+		buffer[len] = '\0';
 
 		/*
 		 * Is it a symbolic ref?
 		 */
-		if (len < 4 || memcmp("ref:", buffer, 4))
+		if (prefixcmp(buffer, "ref:"))
 			break;
-		buf = buffer + 4;
-		len -= 4;
-		while (len && isspace(*buf))
-			buf++, len--;
-		while (len && isspace(buf[len-1]))
-			len--;
-		buf[len] = 0;
-		memcpy(ref_buffer, buf, len + 1);
-		ref = ref_buffer;
 		if (flag)
 			*flag |= REF_ISSYMREF;
+		buf = buffer + 4;
+		while (isspace(*buf))
+			buf++;
+		if (check_refname_format(buf, REFNAME_ALLOW_ONELEVEL)) {
+			if (flag)
+				*flag |= REF_ISBROKEN;
+			return NULL;
+		}
+		refname = strcpy(refname_buffer, buf);
 	}
-	if (len < 40 || get_sha1_hex(buffer, sha1))
+	/* Please note that FETCH_HEAD has a second line containing other data. */
+	if (get_sha1_hex(buffer, sha1) || (buffer[40] != '\0' && !isspace(buffer[40]))) {
+		if (flag)
+			*flag |= REF_ISBROKEN;
 		return NULL;
-	return ref;
+	}
+	return refname;
 }
 
-int read_ref(const char *ref, unsigned char *sha1)
+char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag)
 {
-	if (resolve_ref(ref, sha1, 1, NULL))
+	const char *ret = resolve_ref_unsafe(ref, sha1, reading, flag);
+	return ret ? xstrdup(ret) : NULL;
+}
+
+/* The argument to filter_refs */
+struct ref_filter {
+	const char *pattern;
+	each_ref_fn *fn;
+	void *cb_data;
+};
+
+int read_ref_full(const char *refname, unsigned char *sha1, int reading, int *flags)
+{
+	if (resolve_ref_unsafe(refname, sha1, reading, flags))
 		return 0;
 	return -1;
 }
 
-#define DO_FOR_EACH_INCLUDE_BROKEN 01
-static int do_one_ref(const char *base, each_ref_fn fn, int trim,
-		      int flags, void *cb_data, struct ref_list *entry)
+int read_ref(const char *refname, unsigned char *sha1)
 {
-	if (strncmp(base, entry->name, trim))
-		return 0;
-	/* Is this a "negative ref" that represents a deleted ref? */
-	if (is_null_sha1(entry->sha1))
-		return 0;
-	if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) {
-		if (!has_sha1_file(entry->sha1)) {
-			error("%s does not point to a valid object!", entry->name);
-			return 0;
-		}
-	}
-	current_ref = entry;
-	return fn(entry->name + trim, entry->sha1, entry->flag, cb_data);
+	return read_ref_full(refname, sha1, 1, NULL);
 }
 
-int peel_ref(const char *ref, unsigned char *sha1)
+int ref_exists(const char *refname)
+{
+	unsigned char sha1[20];
+	return !!resolve_ref_unsafe(refname, sha1, 1, NULL);
+}
+
+static int filter_refs(const char *refname, const unsigned char *sha1, int flags,
+		       void *data)
+{
+	struct ref_filter *filter = (struct ref_filter *)data;
+	if (fnmatch(filter->pattern, refname, 0))
+		return 0;
+	return filter->fn(refname, sha1, flags, filter->cb_data);
+}
+
+int peel_ref(const char *refname, unsigned char *sha1)
 {
 	int flag;
 	unsigned char base[20];
 	struct object *o;
 
-	if (current_ref && (current_ref->name == ref
-		|| !strcmp(current_ref->name, ref))) {
+	if (current_ref && (current_ref->name == refname
+		|| !strcmp(current_ref->name, refname))) {
 		if (current_ref->flag & REF_KNOWS_PEELED) {
-			hashcpy(sha1, current_ref->peeled);
+			hashcpy(sha1, current_ref->u.value.peeled);
 			return 0;
 		}
-		hashcpy(base, current_ref->sha1);
+		hashcpy(base, current_ref->u.value.sha1);
 		goto fallback;
 	}
 
-	if (!resolve_ref(ref, base, 1, &flag))
+	if (read_ref_full(refname, base, 1, &flag))
 		return -1;
 
 	if ((flag & REF_ISPACKED)) {
-		struct ref_list *list = get_packed_refs();
+		struct ref_dir *dir = get_packed_refs(get_ref_cache(NULL));
+		struct ref_entry *r = find_ref(dir, refname);
 
-		while (list) {
-			if (!strcmp(list->name, ref)) {
-				if (list->flag & REF_KNOWS_PEELED) {
-					hashcpy(sha1, list->peeled);
-					return 0;
-				}
-				/* older pack-refs did not leave peeled ones */
-				break;
-			}
-			list = list->next;
+		if (r != NULL && r->flag & REF_KNOWS_PEELED) {
+			hashcpy(sha1, r->u.value.peeled);
+			return 0;
 		}
 	}
 
 fallback:
 	o = parse_object(base);
 	if (o && o->type == OBJ_TAG) {
-		o = deref_tag(o, ref, 0);
+		o = deref_tag(o, refname, 0);
 		if (o) {
 			hashcpy(sha1, o->sha1);
 			return 0;
@@ -591,66 +1234,122 @@
 	return -1;
 }
 
-static int do_for_each_ref(const char *base, each_ref_fn fn, int trim,
-			   int flags, void *cb_data)
+struct warn_if_dangling_data {
+	FILE *fp;
+	const char *refname;
+	const char *msg_fmt;
+};
+
+static int warn_if_dangling_symref(const char *refname, const unsigned char *sha1,
+				   int flags, void *cb_data)
 {
+	struct warn_if_dangling_data *d = cb_data;
+	const char *resolves_to;
+	unsigned char junk[20];
+
+	if (!(flags & REF_ISSYMREF))
+		return 0;
+
+	resolves_to = resolve_ref_unsafe(refname, junk, 0, NULL);
+	if (!resolves_to || strcmp(resolves_to, d->refname))
+		return 0;
+
+	fprintf(d->fp, d->msg_fmt, refname);
+	fputc('\n', d->fp);
+	return 0;
+}
+
+void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname)
+{
+	struct warn_if_dangling_data data;
+
+	data.fp = fp;
+	data.refname = refname;
+	data.msg_fmt = msg_fmt;
+	for_each_rawref(warn_if_dangling_symref, &data);
+}
+
+static int do_for_each_ref(const char *submodule, const char *base, each_ref_fn fn,
+			   int trim, int flags, void *cb_data)
+{
+	struct ref_cache *refs = get_ref_cache(submodule);
+	struct ref_dir *packed_dir = get_packed_refs(refs);
+	struct ref_dir *loose_dir = get_loose_refs(refs);
 	int retval = 0;
-	struct ref_list *packed = get_packed_refs();
-	struct ref_list *loose = get_loose_refs();
 
-	struct ref_list *extra;
-
-	for (extra = extra_refs; extra; extra = extra->next)
-		retval = do_one_ref(base, fn, trim, flags, cb_data, extra);
-
-	while (packed && loose) {
-		struct ref_list *entry;
-		int cmp = strcmp(packed->name, loose->name);
-		if (!cmp) {
-			packed = packed->next;
-			continue;
-		}
-		if (cmp > 0) {
-			entry = loose;
-			loose = loose->next;
-		} else {
-			entry = packed;
-			packed = packed->next;
-		}
-		retval = do_one_ref(base, fn, trim, flags, cb_data, entry);
-		if (retval)
-			goto end_each;
+	if (base && *base) {
+		packed_dir = find_containing_dir(packed_dir, base, 0);
+		loose_dir = find_containing_dir(loose_dir, base, 0);
 	}
 
-	for (packed = packed ? packed : loose; packed; packed = packed->next) {
-		retval = do_one_ref(base, fn, trim, flags, cb_data, packed);
-		if (retval)
-			goto end_each;
+	if (packed_dir && loose_dir) {
+		sort_ref_dir(packed_dir);
+		sort_ref_dir(loose_dir);
+		retval = do_for_each_ref_in_dirs(
+				packed_dir, loose_dir,
+				base, fn, trim, flags, cb_data);
+	} else if (packed_dir) {
+		sort_ref_dir(packed_dir);
+		retval = do_for_each_ref_in_dir(
+				packed_dir, 0,
+				base, fn, trim, flags, cb_data);
+	} else if (loose_dir) {
+		sort_ref_dir(loose_dir);
+		retval = do_for_each_ref_in_dir(
+				loose_dir, 0,
+				base, fn, trim, flags, cb_data);
 	}
 
-end_each:
-	current_ref = NULL;
 	return retval;
 }
 
-int head_ref(each_ref_fn fn, void *cb_data)
+static int do_head_ref(const char *submodule, each_ref_fn fn, void *cb_data)
 {
 	unsigned char sha1[20];
 	int flag;
 
-	if (resolve_ref("HEAD", sha1, 1, &flag))
+	if (submodule) {
+		if (resolve_gitlink_ref(submodule, "HEAD", sha1) == 0)
+			return fn("HEAD", sha1, 0, cb_data);
+
+		return 0;
+	}
+
+	if (!read_ref_full("HEAD", sha1, 1, &flag))
 		return fn("HEAD", sha1, flag, cb_data);
+
 	return 0;
 }
 
+int head_ref(each_ref_fn fn, void *cb_data)
+{
+	return do_head_ref(NULL, fn, cb_data);
+}
+
+int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
+{
+	return do_head_ref(submodule, fn, cb_data);
+}
+
 int for_each_ref(each_ref_fn fn, void *cb_data)
 {
-	return do_for_each_ref("refs/", fn, 0, 0, cb_data);
+	return do_for_each_ref(NULL, "", fn, 0, 0, cb_data);
+}
+
+int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
+{
+	return do_for_each_ref(submodule, "", fn, 0, 0, cb_data);
 }
 
 int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
 {
-	return do_for_each_ref(prefix, fn, strlen(prefix), 0, cb_data);
+	return do_for_each_ref(NULL, prefix, fn, strlen(prefix), 0, cb_data);
+}
+
+int for_each_ref_in_submodule(const char *submodule, const char *prefix,
+		each_ref_fn fn, void *cb_data)
+{
+	return do_for_each_ref(submodule, prefix, fn, strlen(prefix), 0, cb_data);
 }
 
 int for_each_tag_ref(each_ref_fn fn, void *cb_data)
@@ -658,100 +1357,102 @@
 	return for_each_ref_in("refs/tags/", fn, cb_data);
 }
 
+int for_each_tag_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
+{
+	return for_each_ref_in_submodule(submodule, "refs/tags/", fn, cb_data);
+}
+
 int for_each_branch_ref(each_ref_fn fn, void *cb_data)
 {
 	return for_each_ref_in("refs/heads/", fn, cb_data);
 }
 
+int for_each_branch_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
+{
+	return for_each_ref_in_submodule(submodule, "refs/heads/", fn, cb_data);
+}
+
 int for_each_remote_ref(each_ref_fn fn, void *cb_data)
 {
 	return for_each_ref_in("refs/remotes/", fn, cb_data);
 }
 
+int for_each_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
+{
+	return for_each_ref_in_submodule(submodule, "refs/remotes/", fn, cb_data);
+}
+
+int for_each_replace_ref(each_ref_fn fn, void *cb_data)
+{
+	return do_for_each_ref(NULL, "refs/replace/", fn, 13, 0, cb_data);
+}
+
+int head_ref_namespaced(each_ref_fn fn, void *cb_data)
+{
+	struct strbuf buf = STRBUF_INIT;
+	int ret = 0;
+	unsigned char sha1[20];
+	int flag;
+
+	strbuf_addf(&buf, "%sHEAD", get_git_namespace());
+	if (!read_ref_full(buf.buf, sha1, 1, &flag))
+		ret = fn(buf.buf, sha1, flag, cb_data);
+	strbuf_release(&buf);
+
+	return ret;
+}
+
+int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)
+{
+	struct strbuf buf = STRBUF_INIT;
+	int ret;
+	strbuf_addf(&buf, "%srefs/", get_git_namespace());
+	ret = do_for_each_ref(NULL, buf.buf, fn, 0, 0, cb_data);
+	strbuf_release(&buf);
+	return ret;
+}
+
+int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
+	const char *prefix, void *cb_data)
+{
+	struct strbuf real_pattern = STRBUF_INIT;
+	struct ref_filter filter;
+	int ret;
+
+	if (!prefix && prefixcmp(pattern, "refs/"))
+		strbuf_addstr(&real_pattern, "refs/");
+	else if (prefix)
+		strbuf_addstr(&real_pattern, prefix);
+	strbuf_addstr(&real_pattern, pattern);
+
+	if (!has_glob_specials(pattern)) {
+		/* Append implied '/' '*' if not present. */
+		if (real_pattern.buf[real_pattern.len - 1] != '/')
+			strbuf_addch(&real_pattern, '/');
+		/* No need to check for '*', there is none. */
+		strbuf_addch(&real_pattern, '*');
+	}
+
+	filter.pattern = real_pattern.buf;
+	filter.fn = fn;
+	filter.cb_data = cb_data;
+	ret = for_each_ref(filter_refs, &filter);
+
+	strbuf_release(&real_pattern);
+	return ret;
+}
+
+int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data)
+{
+	return for_each_glob_ref_in(fn, pattern, NULL, cb_data);
+}
+
 int for_each_rawref(each_ref_fn fn, void *cb_data)
 {
-	return do_for_each_ref("refs/", fn, 0,
+	return do_for_each_ref(NULL, "", fn, 0,
 			       DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
 }
 
-/*
- * Make sure "ref" is something reasonable to have under ".git/refs/";
- * We do not like it if:
- *
- * - any path component of it begins with ".", or
- * - it has double dots "..", or
- * - it has ASCII control character, "~", "^", ":" or SP, anywhere, or
- * - it ends with a "/".
- * - it ends with ".lock"
- * - it contains a "\" (backslash)
- */
-
-static inline int bad_ref_char(int ch)
-{
-	if (((unsigned) ch) <= ' ' ||
-	    ch == '~' || ch == '^' || ch == ':' || ch == '\\')
-		return 1;
-	/* 2.13 Pattern Matching Notation */
-	if (ch == '?' || ch == '[') /* Unsupported */
-		return 1;
-	if (ch == '*') /* Supported at the end */
-		return 2;
-	return 0;
-}
-
-int check_ref_format(const char *ref)
-{
-	int ch, level, bad_type, last;
-	int ret = CHECK_REF_FORMAT_OK;
-	const char *cp = ref;
-
-	level = 0;
-	while (1) {
-		while ((ch = *cp++) == '/')
-			; /* tolerate duplicated slashes */
-		if (!ch)
-			/* should not end with slashes */
-			return CHECK_REF_FORMAT_ERROR;
-
-		/* we are at the beginning of the path component */
-		if (ch == '.')
-			return CHECK_REF_FORMAT_ERROR;
-		bad_type = bad_ref_char(ch);
-		if (bad_type) {
-			if (bad_type == 2 && (!*cp || *cp == '/') &&
-			    ret == CHECK_REF_FORMAT_OK)
-				ret = CHECK_REF_FORMAT_WILDCARD;
-			else
-				return CHECK_REF_FORMAT_ERROR;
-		}
-
-		last = ch;
-		/* scan the rest of the path component */
-		while ((ch = *cp++) != 0) {
-			bad_type = bad_ref_char(ch);
-			if (bad_type)
-				return CHECK_REF_FORMAT_ERROR;
-			if (ch == '/')
-				break;
-			if (last == '.' && ch == '.')
-				return CHECK_REF_FORMAT_ERROR;
-			if (last == '@' && ch == '{')
-				return CHECK_REF_FORMAT_ERROR;
-			last = ch;
-		}
-		level++;
-		if (!ch) {
-			if (ref <= cp - 2 && cp[-2] == '.')
-				return CHECK_REF_FORMAT_ERROR;
-			if (level < 2)
-				return CHECK_REF_FORMAT_ONELEVEL;
-			if (has_extension(ref, ".lock"))
-				return CHECK_REF_FORMAT_ERROR;
-			return ret;
-		}
-	}
-}
-
 const char *prettify_refname(const char *name)
 {
 	return name + (
@@ -771,13 +1472,6 @@
 	NULL
 };
 
-const char *ref_fetch_rules[] = {
-	"%.*s",
-	"refs/%.*s",
-	"refs/heads/%.*s",
-	NULL
-};
-
 int refname_match(const char *abbrev_name, const char *full_name, const char **rules)
 {
 	const char **p;
@@ -795,7 +1489,7 @@
 static struct ref_lock *verify_lock(struct ref_lock *lock,
 	const unsigned char *old_sha1, int mustexist)
 {
-	if (!resolve_ref(lock->ref_name, lock->old_sha1, mustexist, NULL)) {
+	if (read_ref_full(lock->ref_name, lock->old_sha1, mustexist, NULL)) {
 		error("Can't verify ref %s", lock->ref_name);
 		unlock_ref(lock);
 		return NULL;
@@ -821,40 +1515,107 @@
 	strbuf_init(&path, 20);
 	strbuf_addstr(&path, file);
 
-	result = remove_dir_recursively(&path, 1);
+	result = remove_dir_recursively(&path, REMOVE_DIR_EMPTY_ONLY);
 
 	strbuf_release(&path);
 
 	return result;
 }
 
-static int is_refname_available(const char *ref, const char *oldref,
-				struct ref_list *list, int quiet)
+/*
+ * *string and *len will only be substituted, and *string returned (for
+ * later free()ing) if the string passed in is a magic short-hand form
+ * to name a branch.
+ */
+static char *substitute_branch_name(const char **string, int *len)
 {
-	int namlen = strlen(ref); /* e.g. 'foo/bar' */
-	while (list) {
-		/* list->name could be 'foo' or 'foo/bar/baz' */
-		if (!oldref || strcmp(oldref, list->name)) {
-			int len = strlen(list->name);
-			int cmplen = (namlen < len) ? namlen : len;
-			const char *lead = (namlen < len) ? list->name : ref;
-			if (!strncmp(ref, list->name, cmplen) &&
-			    lead[cmplen] == '/') {
-				if (!quiet)
-					error("'%s' exists; cannot create '%s'",
-					      list->name, ref);
-				return 0;
-			}
-		}
-		list = list->next;
+	struct strbuf buf = STRBUF_INIT;
+	int ret = interpret_branch_name(*string, &buf);
+
+	if (ret == *len) {
+		size_t size;
+		*string = strbuf_detach(&buf, &size);
+		*len = size;
+		return (char *)*string;
 	}
-	return 1;
+
+	return NULL;
 }
 
-static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1, int flags, int *type_p)
+int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)
+{
+	char *last_branch = substitute_branch_name(&str, &len);
+	const char **p, *r;
+	int refs_found = 0;
+
+	*ref = NULL;
+	for (p = ref_rev_parse_rules; *p; p++) {
+		char fullref[PATH_MAX];
+		unsigned char sha1_from_ref[20];
+		unsigned char *this_result;
+		int flag;
+
+		this_result = refs_found ? sha1_from_ref : sha1;
+		mksnpath(fullref, sizeof(fullref), *p, len, str);
+		r = resolve_ref_unsafe(fullref, this_result, 1, &flag);
+		if (r) {
+			if (!refs_found++)
+				*ref = xstrdup(r);
+			if (!warn_ambiguous_refs)
+				break;
+		} else if ((flag & REF_ISSYMREF) && strcmp(fullref, "HEAD")) {
+			warning("ignoring dangling symref %s.", fullref);
+		} else if ((flag & REF_ISBROKEN) && strchr(fullref, '/')) {
+			warning("ignoring broken ref %s.", fullref);
+		}
+	}
+	free(last_branch);
+	return refs_found;
+}
+
+int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
+{
+	char *last_branch = substitute_branch_name(&str, &len);
+	const char **p;
+	int logs_found = 0;
+
+	*log = NULL;
+	for (p = ref_rev_parse_rules; *p; p++) {
+		struct stat st;
+		unsigned char hash[20];
+		char path[PATH_MAX];
+		const char *ref, *it;
+
+		mksnpath(path, sizeof(path), *p, len, str);
+		ref = resolve_ref_unsafe(path, hash, 1, NULL);
+		if (!ref)
+			continue;
+		if (!stat(git_path("logs/%s", path), &st) &&
+		    S_ISREG(st.st_mode))
+			it = path;
+		else if (strcmp(ref, path) &&
+			 !stat(git_path("logs/%s", ref), &st) &&
+			 S_ISREG(st.st_mode))
+			it = ref;
+		else
+			continue;
+		if (!logs_found++) {
+			*log = xstrdup(it);
+			hashcpy(sha1, hash);
+		}
+		if (!warn_ambiguous_refs)
+			break;
+	}
+	free(last_branch);
+	return logs_found;
+}
+
+static struct ref_lock *lock_ref_sha1_basic(const char *refname,
+					    const unsigned char *old_sha1,
+					    int flags, int *type_p)
 {
 	char *ref_file;
-	const char *orig_ref = ref;
+	const char *orig_refname = refname;
 	struct ref_lock *lock;
 	int last_errno = 0;
 	int type, lflags;
@@ -864,27 +1625,27 @@
 	lock = xcalloc(1, sizeof(struct ref_lock));
 	lock->lock_fd = -1;
 
-	ref = resolve_ref(ref, lock->old_sha1, mustexist, &type);
-	if (!ref && errno == EISDIR) {
+	refname = resolve_ref_unsafe(refname, lock->old_sha1, mustexist, &type);
+	if (!refname && errno == EISDIR) {
 		/* we are trying to lock foo but we used to
 		 * have foo/bar which now does not exist;
 		 * it is normal for the empty directory 'foo'
 		 * to remain.
 		 */
-		ref_file = git_path("%s", orig_ref);
+		ref_file = git_path("%s", orig_refname);
 		if (remove_empty_directories(ref_file)) {
 			last_errno = errno;
-			error("there are still refs under '%s'", orig_ref);
+			error("there are still refs under '%s'", orig_refname);
 			goto error_return;
 		}
-		ref = resolve_ref(orig_ref, lock->old_sha1, mustexist, &type);
+		refname = resolve_ref_unsafe(orig_refname, lock->old_sha1, mustexist, &type);
 	}
 	if (type_p)
 	    *type_p = type;
-	if (!ref) {
+	if (!refname) {
 		last_errno = errno;
 		error("unable to resolve reference %s: %s",
-			orig_ref, strerror(errno));
+			orig_refname, strerror(errno));
 		goto error_return;
 	}
 	missing = is_null_sha1(lock->old_sha1);
@@ -894,7 +1655,7 @@
 	 * name is a proper prefix of our refname.
 	 */
 	if (missing &&
-	     !is_refname_available(ref, NULL, get_packed_refs(), 0)) {
+	     !is_refname_available(refname, NULL, get_packed_refs(get_ref_cache(NULL)))) {
 		last_errno = ENOTDIR;
 		goto error_return;
 	}
@@ -903,12 +1664,12 @@
 
 	lflags = LOCK_DIE_ON_ERROR;
 	if (flags & REF_NODEREF) {
-		ref = orig_ref;
+		refname = orig_refname;
 		lflags |= LOCK_NODEREF;
 	}
-	lock->ref_name = xstrdup(ref);
-	lock->orig_ref_name = xstrdup(orig_ref);
-	ref_file = git_path("%s", ref);
+	lock->ref_name = xstrdup(refname);
+	lock->orig_ref_name = xstrdup(orig_refname);
+	ref_file = git_path("%s", refname);
 	if (missing)
 		lock->force_write = 1;
 	if ((flags & REF_NODEREF) && (type & REF_ISSYMREF))
@@ -929,60 +1690,61 @@
 	return NULL;
 }
 
-struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1)
+struct ref_lock *lock_ref_sha1(const char *refname, const unsigned char *old_sha1)
 {
 	char refpath[PATH_MAX];
-	if (check_ref_format(ref))
+	if (check_refname_format(refname, 0))
 		return NULL;
-	strcpy(refpath, mkpath("refs/%s", ref));
+	strcpy(refpath, mkpath("refs/%s", refname));
 	return lock_ref_sha1_basic(refpath, old_sha1, 0, NULL);
 }
 
-struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1, int flags)
+struct ref_lock *lock_any_ref_for_update(const char *refname,
+					 const unsigned char *old_sha1, int flags)
 {
-	switch (check_ref_format(ref)) {
-	default:
+	if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
 		return NULL;
-	case 0:
-	case CHECK_REF_FORMAT_ONELEVEL:
-		return lock_ref_sha1_basic(ref, old_sha1, flags, NULL);
-	}
+	return lock_ref_sha1_basic(refname, old_sha1, flags, NULL);
+}
+
+struct repack_without_ref_sb {
+	const char *refname;
+	int fd;
+};
+
+static int repack_without_ref_fn(const char *refname, const unsigned char *sha1,
+				 int flags, void *cb_data)
+{
+	struct repack_without_ref_sb *data = cb_data;
+	char line[PATH_MAX + 100];
+	int len;
+
+	if (!strcmp(data->refname, refname))
+		return 0;
+	len = snprintf(line, sizeof(line), "%s %s\n",
+		       sha1_to_hex(sha1), refname);
+	/* this should not happen but just being defensive */
+	if (len > sizeof(line))
+		die("too long a refname '%s'", refname);
+	write_or_die(data->fd, line, len);
+	return 0;
 }
 
 static struct lock_file packlock;
 
 static int repack_without_ref(const char *refname)
 {
-	struct ref_list *list, *packed_ref_list;
-	int fd;
-	int found = 0;
-
-	packed_ref_list = get_packed_refs();
-	for (list = packed_ref_list; list; list = list->next) {
-		if (!strcmp(refname, list->name)) {
-			found = 1;
-			break;
-		}
-	}
-	if (!found)
+	struct repack_without_ref_sb data;
+	struct ref_dir *packed = get_packed_refs(get_ref_cache(NULL));
+	if (find_ref(packed, refname) == NULL)
 		return 0;
-	fd = hold_lock_file_for_update(&packlock, git_path("packed-refs"), 0);
-	if (fd < 0)
+	data.refname = refname;
+	data.fd = hold_lock_file_for_update(&packlock, git_path("packed-refs"), 0);
+	if (data.fd < 0) {
+		unable_to_lock_error(git_path("packed-refs"), errno);
 		return error("cannot delete '%s' from packed refs", refname);
-
-	for (list = packed_ref_list; list; list = list->next) {
-		char line[PATH_MAX + 100];
-		int len;
-
-		if (!strcmp(refname, list->name))
-			continue;
-		len = snprintf(line, sizeof(line), "%s %s\n",
-			       sha1_to_hex(list->sha1), list->name);
-		/* this should not happen but just being defensive */
-		if (len > sizeof(line))
-			die("too long a refname '%s'", list->name);
-		write_or_die(fd, line, len);
 	}
+	do_for_each_ref_in_dir(packed, 0, "", repack_without_ref_fn, 0, 0, &data);
 	return commit_lock_file(&packlock);
 }
 
@@ -1019,109 +1781,112 @@
 	ret |= repack_without_ref(refname);
 
 	unlink_or_warn(git_path("logs/%s", lock->ref_name));
-	invalidate_cached_refs();
+	invalidate_ref_cache(NULL);
 	unlock_ref(lock);
 	return ret;
 }
 
-int rename_ref(const char *oldref, const char *newref, const char *logmsg)
+/*
+ * People using contrib's git-new-workdir have .git/logs/refs ->
+ * /some/other/path/.git/logs/refs, and that may live on another device.
+ *
+ * IOW, to avoid cross device rename errors, the temporary renamed log must
+ * live into logs/refs.
+ */
+#define TMP_RENAMED_LOG  "logs/refs/.tmp-renamed-log"
+
+int rename_ref(const char *oldrefname, const char *newrefname, const char *logmsg)
 {
-	static const char renamed_ref[] = "RENAMED-REF";
 	unsigned char sha1[20], orig_sha1[20];
 	int flag = 0, logmoved = 0;
 	struct ref_lock *lock;
 	struct stat loginfo;
-	int log = !lstat(git_path("logs/%s", oldref), &loginfo);
+	int log = !lstat(git_path("logs/%s", oldrefname), &loginfo);
 	const char *symref = NULL;
+	struct ref_cache *refs = get_ref_cache(NULL);
 
 	if (log && S_ISLNK(loginfo.st_mode))
-		return error("reflog for %s is a symlink", oldref);
+		return error("reflog for %s is a symlink", oldrefname);
 
-	symref = resolve_ref(oldref, orig_sha1, 1, &flag);
+	symref = resolve_ref_unsafe(oldrefname, orig_sha1, 1, &flag);
 	if (flag & REF_ISSYMREF)
 		return error("refname %s is a symbolic ref, renaming it is not supported",
-			oldref);
+			oldrefname);
 	if (!symref)
-		return error("refname %s not found", oldref);
+		return error("refname %s not found", oldrefname);
 
-	if (!is_refname_available(newref, oldref, get_packed_refs(), 0))
+	if (!is_refname_available(newrefname, oldrefname, get_packed_refs(refs)))
 		return 1;
 
-	if (!is_refname_available(newref, oldref, get_loose_refs(), 0))
+	if (!is_refname_available(newrefname, oldrefname, get_loose_refs(refs)))
 		return 1;
 
-	lock = lock_ref_sha1_basic(renamed_ref, NULL, 0, NULL);
-	if (!lock)
-		return error("unable to lock %s", renamed_ref);
-	lock->force_write = 1;
-	if (write_ref_sha1(lock, orig_sha1, logmsg))
-		return error("unable to save current sha1 in %s", renamed_ref);
+	if (log && rename(git_path("logs/%s", oldrefname), git_path(TMP_RENAMED_LOG)))
+		return error("unable to move logfile logs/%s to "TMP_RENAMED_LOG": %s",
+			oldrefname, strerror(errno));
 
-	if (log && rename(git_path("logs/%s", oldref), git_path("tmp-renamed-log")))
-		return error("unable to move logfile logs/%s to tmp-renamed-log: %s",
-			oldref, strerror(errno));
-
-	if (delete_ref(oldref, orig_sha1, REF_NODEREF)) {
-		error("unable to delete old %s", oldref);
+	if (delete_ref(oldrefname, orig_sha1, REF_NODEREF)) {
+		error("unable to delete old %s", oldrefname);
 		goto rollback;
 	}
 
-	if (resolve_ref(newref, sha1, 1, &flag) && delete_ref(newref, sha1, REF_NODEREF)) {
+	if (!read_ref_full(newrefname, sha1, 1, &flag) &&
+	    delete_ref(newrefname, sha1, REF_NODEREF)) {
 		if (errno==EISDIR) {
-			if (remove_empty_directories(git_path("%s", newref))) {
-				error("Directory not empty: %s", newref);
+			if (remove_empty_directories(git_path("%s", newrefname))) {
+				error("Directory not empty: %s", newrefname);
 				goto rollback;
 			}
 		} else {
-			error("unable to delete existing %s", newref);
+			error("unable to delete existing %s", newrefname);
 			goto rollback;
 		}
 	}
 
-	if (log && safe_create_leading_directories(git_path("logs/%s", newref))) {
-		error("unable to create directory for %s", newref);
+	if (log && safe_create_leading_directories(git_path("logs/%s", newrefname))) {
+		error("unable to create directory for %s", newrefname);
 		goto rollback;
 	}
 
  retry:
-	if (log && rename(git_path("tmp-renamed-log"), git_path("logs/%s", newref))) {
+	if (log && rename(git_path(TMP_RENAMED_LOG), git_path("logs/%s", newrefname))) {
 		if (errno==EISDIR || errno==ENOTDIR) {
 			/*
 			 * rename(a, b) when b is an existing
 			 * directory ought to result in ISDIR, but
 			 * Solaris 5.8 gives ENOTDIR.  Sheesh.
 			 */
-			if (remove_empty_directories(git_path("logs/%s", newref))) {
-				error("Directory not empty: logs/%s", newref);
+			if (remove_empty_directories(git_path("logs/%s", newrefname))) {
+				error("Directory not empty: logs/%s", newrefname);
 				goto rollback;
 			}
 			goto retry;
 		} else {
-			error("unable to move logfile tmp-renamed-log to logs/%s: %s",
-				newref, strerror(errno));
+			error("unable to move logfile "TMP_RENAMED_LOG" to logs/%s: %s",
+				newrefname, strerror(errno));
 			goto rollback;
 		}
 	}
 	logmoved = log;
 
-	lock = lock_ref_sha1_basic(newref, NULL, 0, NULL);
+	lock = lock_ref_sha1_basic(newrefname, NULL, 0, NULL);
 	if (!lock) {
-		error("unable to lock %s for update", newref);
+		error("unable to lock %s for update", newrefname);
 		goto rollback;
 	}
 	lock->force_write = 1;
 	hashcpy(lock->old_sha1, orig_sha1);
 	if (write_ref_sha1(lock, orig_sha1, logmsg)) {
-		error("unable to write current sha1 into %s", newref);
+		error("unable to write current sha1 into %s", newrefname);
 		goto rollback;
 	}
 
 	return 0;
 
  rollback:
-	lock = lock_ref_sha1_basic(oldref, NULL, 0, NULL);
+	lock = lock_ref_sha1_basic(oldrefname, NULL, 0, NULL);
 	if (!lock) {
-		error("unable to lock %s for rollback", oldref);
+		error("unable to lock %s for rollback", oldrefname);
 		goto rollbacklog;
 	}
 
@@ -1129,17 +1894,17 @@
 	flag = log_all_ref_updates;
 	log_all_ref_updates = 0;
 	if (write_ref_sha1(lock, orig_sha1, NULL))
-		error("unable to write current sha1 into %s", oldref);
+		error("unable to write current sha1 into %s", oldrefname);
 	log_all_ref_updates = flag;
 
  rollbacklog:
-	if (logmoved && rename(git_path("logs/%s", newref), git_path("logs/%s", oldref)))
+	if (logmoved && rename(git_path("logs/%s", newrefname), git_path("logs/%s", oldrefname)))
 		error("unable to restore logfile %s from %s: %s",
-			oldref, newref, strerror(errno));
+			oldrefname, newrefname, strerror(errno));
 	if (!logmoved && log &&
-	    rename(git_path("tmp-renamed-log"), git_path("logs/%s", oldref)))
-		error("unable to restore logfile %s from tmp-renamed-log: %s",
-			oldref, strerror(errno));
+	    rename(git_path(TMP_RENAMED_LOG), git_path("logs/%s", oldrefname)))
+		error("unable to restore logfile %s from "TMP_RENAMED_LOG": %s",
+			oldrefname, strerror(errno));
 
 	return 1;
 }
@@ -1196,10 +1961,49 @@
 	return cp - buf;
 }
 
-static int log_ref_write(const char *ref_name, const unsigned char *old_sha1,
+int log_ref_setup(const char *refname, char *logfile, int bufsize)
+{
+	int logfd, oflags = O_APPEND | O_WRONLY;
+
+	git_snpath(logfile, bufsize, "logs/%s", refname);
+	if (log_all_ref_updates &&
+	    (!prefixcmp(refname, "refs/heads/") ||
+	     !prefixcmp(refname, "refs/remotes/") ||
+	     !prefixcmp(refname, "refs/notes/") ||
+	     !strcmp(refname, "HEAD"))) {
+		if (safe_create_leading_directories(logfile) < 0)
+			return error("unable to create directory for %s",
+				     logfile);
+		oflags |= O_CREAT;
+	}
+
+	logfd = open(logfile, oflags, 0666);
+	if (logfd < 0) {
+		if (!(oflags & O_CREAT) && errno == ENOENT)
+			return 0;
+
+		if ((oflags & O_CREAT) && errno == EISDIR) {
+			if (remove_empty_directories(logfile)) {
+				return error("There are still logs under '%s'",
+					     logfile);
+			}
+			logfd = open(logfile, oflags, 0666);
+		}
+
+		if (logfd < 0)
+			return error("Unable to append to %s: %s",
+				     logfile, strerror(errno));
+	}
+
+	adjust_shared_perm(logfile);
+	close(logfd);
+	return 0;
+}
+
+static int log_ref_write(const char *refname, const unsigned char *old_sha1,
 			 const unsigned char *new_sha1, const char *msg)
 {
-	int logfd, written, oflags = O_APPEND | O_WRONLY;
+	int logfd, result, written, oflags = O_APPEND | O_WRONLY;
 	unsigned maxlen, len;
 	int msglen;
 	char log_file[PATH_MAX];
@@ -1209,38 +2013,13 @@
 	if (log_all_ref_updates < 0)
 		log_all_ref_updates = !is_bare_repository();
 
-	git_snpath(log_file, sizeof(log_file), "logs/%s", ref_name);
+	result = log_ref_setup(refname, log_file, sizeof(log_file));
+	if (result)
+		return result;
 
-	if (log_all_ref_updates &&
-	    (!prefixcmp(ref_name, "refs/heads/") ||
-	     !prefixcmp(ref_name, "refs/remotes/") ||
-	     !strcmp(ref_name, "HEAD"))) {
-		if (safe_create_leading_directories(log_file) < 0)
-			return error("unable to create directory for %s",
-				     log_file);
-		oflags |= O_CREAT;
-	}
-
-	logfd = open(log_file, oflags, 0666);
-	if (logfd < 0) {
-		if (!(oflags & O_CREAT) && errno == ENOENT)
-			return 0;
-
-		if ((oflags & O_CREAT) && errno == EISDIR) {
-			if (remove_empty_directories(log_file)) {
-				return error("There are still logs under '%s'",
-					     log_file);
-			}
-			logfd = open(log_file, oflags, 0666);
-		}
-
-		if (logfd < 0)
-			return error("Unable to append to %s: %s",
-				     log_file, strerror(errno));
-	}
-
-	adjust_shared_perm(log_file);
-
+	logfd = open(log_file, oflags);
+	if (logfd < 0)
+		return 0;
 	msglen = msg ? strlen(msg) : 0;
 	committer = git_committer_info(0);
 	maxlen = strlen(committer) + msglen + 100;
@@ -1277,7 +2056,7 @@
 	}
 	o = parse_object(sha1);
 	if (!o) {
-		error("Trying to write ref %s with nonexistant object %s",
+		error("Trying to write ref %s with nonexistent object %s",
 			lock->ref_name, sha1_to_hex(sha1));
 		unlock_ref(lock);
 		return -1;
@@ -1295,7 +2074,7 @@
 		unlock_ref(lock);
 		return -1;
 	}
-	invalidate_cached_refs();
+	clear_loose_ref_cache(get_ref_cache(NULL));
 	if (log_ref_write(lock->ref_name, lock->old_sha1, sha1, logmsg) < 0 ||
 	    (strcmp(lock->ref_name, lock->orig_ref_name) &&
 	     log_ref_write(lock->orig_ref_name, lock->old_sha1, sha1, logmsg) < 0)) {
@@ -1318,7 +2097,7 @@
 		unsigned char head_sha1[20];
 		int head_flag;
 		const char *head_ref;
-		head_ref = resolve_ref("HEAD", head_sha1, 1, &head_flag);
+		head_ref = resolve_ref_unsafe("HEAD", head_sha1, 1, &head_flag);
 		if (head_ref && (head_flag & REF_ISSYMREF) &&
 		    !strcmp(head_ref, lock->ref_name))
 			log_ref_write("HEAD", lock->old_sha1, sha1, logmsg);
@@ -1405,7 +2184,9 @@
 	return xmemdupz(line, ep - line);
 }
 
-int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *sha1, char **msg, unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
+int read_ref_at(const char *refname, unsigned long at_time, int cnt,
+		unsigned char *sha1, char **msg,
+		unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
 {
 	const char *logfile, *logdata, *logend, *rec, *lastgt, *lastrec;
 	char *tz_c;
@@ -1416,7 +2197,7 @@
 	void *log_mapped;
 	size_t mapsz;
 
-	logfile = git_path("logs/%s", ref);
+	logfile = git_path("logs/%s", refname);
 	logfd = open(logfile, O_RDONLY, 0);
 	if (logfd < 0)
 		die_errno("Unable to read log '%s'", logfile);
@@ -1509,14 +2290,14 @@
 	return 1;
 }
 
-int for_each_recent_reflog_ent(const char *ref, each_reflog_ent_fn fn, long ofs, void *cb_data)
+int for_each_recent_reflog_ent(const char *refname, each_reflog_ent_fn fn, long ofs, void *cb_data)
 {
 	const char *logfile;
 	FILE *logfp;
-	char buf[1024];
+	struct strbuf sb = STRBUF_INIT;
 	int ret = 0;
 
-	logfile = git_path("logs/%s", ref);
+	logfile = git_path("logs/%s", refname);
 	logfp = fopen(logfile, "r");
 	if (!logfp)
 		return -1;
@@ -1526,24 +2307,24 @@
 		if (fstat(fileno(logfp), &statbuf) ||
 		    statbuf.st_size < ofs ||
 		    fseek(logfp, -ofs, SEEK_END) ||
-		    fgets(buf, sizeof(buf), logfp)) {
+		    strbuf_getwholeline(&sb, logfp, '\n')) {
 			fclose(logfp);
+			strbuf_release(&sb);
 			return -1;
 		}
 	}
 
-	while (fgets(buf, sizeof(buf), logfp)) {
+	while (!strbuf_getwholeline(&sb, logfp, '\n')) {
 		unsigned char osha1[20], nsha1[20];
 		char *email_end, *message;
 		unsigned long timestamp;
-		int len, tz;
+		int tz;
 
 		/* old SP new SP name <email> SP time TAB msg LF */
-		len = strlen(buf);
-		if (len < 83 || buf[len-1] != '\n' ||
-		    get_sha1_hex(buf, osha1) || buf[40] != ' ' ||
-		    get_sha1_hex(buf + 41, nsha1) || buf[81] != ' ' ||
-		    !(email_end = strchr(buf + 82, '>')) ||
+		if (sb.len < 83 || sb.buf[sb.len - 1] != '\n' ||
+		    get_sha1_hex(sb.buf, osha1) || sb.buf[40] != ' ' ||
+		    get_sha1_hex(sb.buf + 41, nsha1) || sb.buf[81] != ' ' ||
+		    !(email_end = strchr(sb.buf + 82, '>')) ||
 		    email_end[1] != ' ' ||
 		    !(timestamp = strtoul(email_end + 2, &message, 10)) ||
 		    !message || message[0] != ' ' ||
@@ -1557,70 +2338,74 @@
 			message += 6;
 		else
 			message += 7;
-		ret = fn(osha1, nsha1, buf+82, timestamp, tz, message, cb_data);
+		ret = fn(osha1, nsha1, sb.buf + 82, timestamp, tz, message,
+			 cb_data);
 		if (ret)
 			break;
 	}
 	fclose(logfp);
+	strbuf_release(&sb);
 	return ret;
 }
 
-int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data)
+int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_data)
 {
-	return for_each_recent_reflog_ent(ref, fn, 0, cb_data);
+	return for_each_recent_reflog_ent(refname, fn, 0, cb_data);
 }
 
-static int do_for_each_reflog(const char *base, each_ref_fn fn, void *cb_data)
+/*
+ * Call fn for each reflog in the namespace indicated by name.  name
+ * must be empty or end with '/'.  Name will be used as a scratch
+ * space, but its contents will be restored before return.
+ */
+static int do_for_each_reflog(struct strbuf *name, each_ref_fn fn, void *cb_data)
 {
-	DIR *dir = opendir(git_path("logs/%s", base));
+	DIR *d = opendir(git_path("logs/%s", name->buf));
 	int retval = 0;
+	struct dirent *de;
+	int oldlen = name->len;
 
-	if (dir) {
-		struct dirent *de;
-		int baselen = strlen(base);
-		char *log = xmalloc(baselen + 257);
+	if (!d)
+		return name->len ? errno : 0;
 
-		memcpy(log, base, baselen);
-		if (baselen && base[baselen-1] != '/')
-			log[baselen++] = '/';
+	while ((de = readdir(d)) != NULL) {
+		struct stat st;
 
-		while ((de = readdir(dir)) != NULL) {
-			struct stat st;
-			int namelen;
-
-			if (de->d_name[0] == '.')
-				continue;
-			namelen = strlen(de->d_name);
-			if (namelen > 255)
-				continue;
-			if (has_extension(de->d_name, ".lock"))
-				continue;
-			memcpy(log + baselen, de->d_name, namelen+1);
-			if (stat(git_path("logs/%s", log), &st) < 0)
-				continue;
+		if (de->d_name[0] == '.')
+			continue;
+		if (has_extension(de->d_name, ".lock"))
+			continue;
+		strbuf_addstr(name, de->d_name);
+		if (stat(git_path("logs/%s", name->buf), &st) < 0) {
+			; /* silently ignore */
+		} else {
 			if (S_ISDIR(st.st_mode)) {
-				retval = do_for_each_reflog(log, fn, cb_data);
+				strbuf_addch(name, '/');
+				retval = do_for_each_reflog(name, fn, cb_data);
 			} else {
 				unsigned char sha1[20];
-				if (!resolve_ref(log, sha1, 0, NULL))
-					retval = error("bad ref for %s", log);
+				if (read_ref_full(name->buf, sha1, 0, NULL))
+					retval = error("bad ref for %s", name->buf);
 				else
-					retval = fn(log, sha1, 0, cb_data);
+					retval = fn(name->buf, sha1, 0, cb_data);
 			}
 			if (retval)
 				break;
 		}
-		free(log);
-		closedir(dir);
+		strbuf_setlen(name, oldlen);
 	}
-	else if (*base)
-		return errno;
+	closedir(d);
 	return retval;
 }
 
 int for_each_reflog(each_ref_fn fn, void *cb_data)
 {
-	return do_for_each_reflog("", fn, cb_data);
+	int retval;
+	struct strbuf name;
+	strbuf_init(&name, PATH_MAX);
+	retval = do_for_each_reflog(&name, fn, cb_data);
+	strbuf_release(&name);
+	return retval;
 }
 
 int update_ref(const char *action, const char *refname,
@@ -1681,7 +2466,7 @@
 	return;
 }
 
-char *shorten_unambiguous_ref(const char *ref, int strict)
+char *shorten_unambiguous_ref(const char *refname, int strict)
 {
 	int i;
 	static char **scanf_fmts;
@@ -1710,10 +2495,10 @@
 
 	/* bail out if there are no rules */
 	if (!nr_rules)
-		return xstrdup(ref);
+		return xstrdup(refname);
 
-	/* buffer for scanf result, at most ref must fit */
-	short_name = xstrdup(ref);
+	/* buffer for scanf result, at most refname must fit */
+	short_name = xstrdup(refname);
 
 	/* skip first rule, it will always match */
 	for (i = nr_rules - 1; i > 0 ; --i) {
@@ -1721,7 +2506,7 @@
 		int rules_to_fail = i;
 		int short_name_len;
 
-		if (1 != sscanf(ref, scanf_fmts[i], short_name))
+		if (1 != sscanf(refname, scanf_fmts[i], short_name))
 			continue;
 
 		short_name_len = strlen(short_name);
@@ -1739,7 +2524,6 @@
 		 */
 		for (j = 0; j < rules_to_fail; j++) {
 			const char *rule = ref_rev_parse_rules[j];
-			unsigned char short_objectname[20];
 			char refname[PATH_MAX];
 
 			/* skip matched rule */
@@ -1753,7 +2537,7 @@
 			 */
 			mksnpath(refname, sizeof(refname),
 				 rule, short_name_len, short_name);
-			if (!read_ref(refname, short_objectname))
+			if (ref_exists(refname))
 				break;
 		}
 
@@ -1766,5 +2550,5 @@
 	}
 
 	free(short_name);
-	return xstrdup(ref);
+	return xstrdup(refname);
 }
diff --git a/refs.h b/refs.h
index c11f6a6..d6c2fe2 100644
--- a/refs.h
+++ b/refs.h
@@ -10,12 +10,16 @@
 	int force_write;
 };
 
-#define REF_ISSYMREF 01
-#define REF_ISPACKED 02
+#define REF_ISSYMREF 0x01
+#define REF_ISPACKED 0x02
+#define REF_ISBROKEN 0x04
 
 /*
- * Calls the specified function for each ref file until it returns nonzero,
- * and returns the value
+ * Calls the specified function for each ref file until it returns
+ * nonzero, and returns the value.  Please note that it is not safe to
+ * modify references while an iteration is in progress, unless the
+ * same callback function invocation that modifies the reference also
+ * returns a nonzero value to immediately stop the iteration.
  */
 typedef int each_ref_fn(const char *refname, const unsigned char *sha1, int flags, void *cb_data);
 extern int head_ref(each_ref_fn, void *);
@@ -24,29 +28,49 @@
 extern int for_each_tag_ref(each_ref_fn, void *);
 extern int for_each_branch_ref(each_ref_fn, void *);
 extern int for_each_remote_ref(each_ref_fn, void *);
+extern int for_each_replace_ref(each_ref_fn, void *);
+extern int for_each_glob_ref(each_ref_fn, const char *pattern, void *);
+extern int for_each_glob_ref_in(each_ref_fn, const char *pattern, const char* prefix, void *);
+
+extern int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
+extern int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
+extern int for_each_ref_in_submodule(const char *submodule, const char *prefix,
+		each_ref_fn fn, void *cb_data);
+extern int for_each_tag_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
+extern int for_each_branch_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
+extern int for_each_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
+
+extern int head_ref_namespaced(each_ref_fn fn, void *cb_data);
+extern int for_each_namespaced_ref(each_ref_fn fn, void *cb_data);
+
+static inline const char *has_glob_specials(const char *pattern)
+{
+	return strpbrk(pattern, "?*[");
+}
 
 /* can be used to learn about broken ref and symref */
 extern int for_each_rawref(each_ref_fn, void *);
 
-extern void warn_dangling_symref(const char *msg_fmt, const char *refname);
+extern void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname);
 
 /*
- * Extra refs will be listed by for_each_ref() before any actual refs
- * for the duration of this process or until clear_extra_refs() is
- * called. Only extra refs added before for_each_ref() is called will
- * be listed on a given call of for_each_ref().
+ * Add a reference to the in-memory packed reference cache.  To actually
+ * write the reference to the packed-refs file, call pack_refs().
  */
-extern void add_extra_ref(const char *refname, const unsigned char *sha1, int flags);
-extern void clear_extra_refs(void);
+extern void add_packed_ref(const char *refname, const unsigned char *sha1);
 
-extern int peel_ref(const char *, unsigned char *);
+extern int ref_exists(const char *);
+
+extern int peel_ref(const char *refname, unsigned char *sha1);
 
 /** Locks a "refs/" ref returning the lock on success and NULL on failure. **/
-extern struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1);
+extern struct ref_lock *lock_ref_sha1(const char *refname, const unsigned char *old_sha1);
 
 /** Locks any ref (for 'HEAD' type refs). */
 #define REF_NODEREF	0x01
-extern struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1, int flags);
+extern struct ref_lock *lock_any_ref_for_update(const char *refname,
+						const unsigned char *old_sha1,
+						int flags);
 
 /** Close the file descriptor owned by a lock and return the status */
 extern int close_ref(struct ref_lock *lock);
@@ -60,13 +84,26 @@
 /** Writes sha1 into the ref specified by the lock. **/
 extern int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1, const char *msg);
 
+/*
+ * Invalidate the reference cache for the specified submodule.  Use
+ * submodule=NULL to invalidate the cache for the main module.  This
+ * function must be called if references are changed via a mechanism
+ * other than the refs API.
+ */
+extern void invalidate_ref_cache(const char *submodule);
+
+/** Setup reflog before using. **/
+int log_ref_setup(const char *ref_name, char *logfile, int bufsize);
+
 /** Reads log for the value of ref during at_time. **/
-extern int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *sha1, char **msg, unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt);
+extern int read_ref_at(const char *refname, unsigned long at_time, int cnt,
+		       unsigned char *sha1, char **msg,
+		       unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt);
 
 /* iterate over reflog entries */
 typedef int each_reflog_ent_fn(unsigned char *osha1, unsigned char *nsha1, const char *, unsigned long, int, const char *, void *);
-int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data);
-int for_each_recent_reflog_ent(const char *ref, each_reflog_ent_fn fn, long, void *cb_data);
+int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_data);
+int for_each_recent_reflog_ent(const char *refname, each_reflog_ent_fn fn, long, void *cb_data);
 
 /*
  * Calls the specified function for each reflog file until it returns nonzero,
@@ -74,20 +111,35 @@
  */
 extern int for_each_reflog(each_ref_fn, void *);
 
-#define CHECK_REF_FORMAT_OK 0
-#define CHECK_REF_FORMAT_ERROR (-1)
-#define CHECK_REF_FORMAT_ONELEVEL (-2)
-#define CHECK_REF_FORMAT_WILDCARD (-3)
-extern int check_ref_format(const char *target);
+#define REFNAME_ALLOW_ONELEVEL 1
+#define REFNAME_REFSPEC_PATTERN 2
+#define REFNAME_DOT_COMPONENT 4
+
+/*
+ * Return 0 iff refname has the correct format for a refname according
+ * to the rules described in Documentation/git-check-ref-format.txt.
+ * If REFNAME_ALLOW_ONELEVEL is set in flags, then accept one-level
+ * reference names.  If REFNAME_REFSPEC_PATTERN is set in flags, then
+ * allow a "*" wildcard character in place of one of the name
+ * components.  No leading or repeated slashes are accepted.  If
+ * REFNAME_DOT_COMPONENT is set in flags, then allow refname
+ * components to start with "." (but not a whole component equal to
+ * "." or "..").
+ */
+extern int check_refname_format(const char *refname, int flags);
 
 extern const char *prettify_refname(const char *refname);
-extern char *shorten_unambiguous_ref(const char *ref, int strict);
+extern char *shorten_unambiguous_ref(const char *refname, int strict);
 
 /** rename ref, return 0 on success **/
 extern int rename_ref(const char *oldref, const char *newref, const char *logmsg);
 
-/** resolve ref in nested "gitlink" repository */
-extern int resolve_gitlink_ref(const char *name, const char *refname, unsigned char *result);
+/**
+ * Resolve refname in the nested "gitlink" repository that is located
+ * at path.  If the resolution is successful, return 0 and set sha1 to
+ * the name of the object; otherwise, return a non-zero value.
+ */
+extern int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sha1);
 
 /** lock a ref and then write its file */
 enum action_on_err { MSG_ON_ERR, DIE_ON_ERR, QUIET_ON_ERR };
diff --git a/remote-curl.c b/remote-curl.c
new file mode 100644
index 0000000..04a9d62
--- /dev/null
+++ b/remote-curl.c
@@ -0,0 +1,934 @@
+#include "cache.h"
+#include "remote.h"
+#include "strbuf.h"
+#include "walker.h"
+#include "http.h"
+#include "exec_cmd.h"
+#include "run-command.h"
+#include "pkt-line.h"
+#include "sideband.h"
+
+static struct remote *remote;
+static const char *url; /* always ends with a trailing slash */
+
+struct options {
+	int verbosity;
+	unsigned long depth;
+	unsigned progress : 1,
+		followtags : 1,
+		dry_run : 1,
+		thin : 1;
+};
+static struct options options;
+
+static int set_option(const char *name, const char *value)
+{
+	if (!strcmp(name, "verbosity")) {
+		char *end;
+		int v = strtol(value, &end, 10);
+		if (value == end || *end)
+			return -1;
+		options.verbosity = v;
+		return 0;
+	}
+	else if (!strcmp(name, "progress")) {
+		if (!strcmp(value, "true"))
+			options.progress = 1;
+		else if (!strcmp(value, "false"))
+			options.progress = 0;
+		else
+			return -1;
+		return 0;
+	}
+	else if (!strcmp(name, "depth")) {
+		char *end;
+		unsigned long v = strtoul(value, &end, 10);
+		if (value == end || *end)
+			return -1;
+		options.depth = v;
+		return 0;
+	}
+	else if (!strcmp(name, "followtags")) {
+		if (!strcmp(value, "true"))
+			options.followtags = 1;
+		else if (!strcmp(value, "false"))
+			options.followtags = 0;
+		else
+			return -1;
+		return 0;
+	}
+	else if (!strcmp(name, "dry-run")) {
+		if (!strcmp(value, "true"))
+			options.dry_run = 1;
+		else if (!strcmp(value, "false"))
+			options.dry_run = 0;
+		else
+			return -1;
+		return 0;
+	}
+	else {
+		return 1 /* unsupported */;
+	}
+}
+
+struct discovery {
+	const char *service;
+	char *buf_alloc;
+	char *buf;
+	size_t len;
+	unsigned proto_git : 1;
+};
+static struct discovery *last_discovery;
+
+static void free_discovery(struct discovery *d)
+{
+	if (d) {
+		if (d == last_discovery)
+			last_discovery = NULL;
+		free(d->buf_alloc);
+		free(d);
+	}
+}
+
+static struct discovery* discover_refs(const char *service)
+{
+	struct strbuf buffer = STRBUF_INIT;
+	struct discovery *last = last_discovery;
+	char *refs_url;
+	int http_ret, is_http = 0, proto_git_candidate = 1;
+
+	if (last && !strcmp(service, last->service))
+		return last;
+	free_discovery(last);
+
+	strbuf_addf(&buffer, "%sinfo/refs", url);
+	if (!prefixcmp(url, "http://") || !prefixcmp(url, "https://")) {
+		is_http = 1;
+		if (!strchr(url, '?'))
+			strbuf_addch(&buffer, '?');
+		else
+			strbuf_addch(&buffer, '&');
+		strbuf_addf(&buffer, "service=%s", service);
+	}
+	refs_url = strbuf_detach(&buffer, NULL);
+
+	http_ret = http_get_strbuf(refs_url, &buffer, HTTP_NO_CACHE);
+
+	/* try again with "plain" url (no ? or & appended) */
+	if (http_ret != HTTP_OK && http_ret != HTTP_NOAUTH) {
+		free(refs_url);
+		strbuf_reset(&buffer);
+
+		proto_git_candidate = 0;
+		strbuf_addf(&buffer, "%sinfo/refs", url);
+		refs_url = strbuf_detach(&buffer, NULL);
+
+		http_ret = http_get_strbuf(refs_url, &buffer, HTTP_NO_CACHE);
+	}
+
+	switch (http_ret) {
+	case HTTP_OK:
+		break;
+	case HTTP_MISSING_TARGET:
+		die("%s not found: did you run git update-server-info on the"
+		    " server?", refs_url);
+	case HTTP_NOAUTH:
+		die("Authentication failed");
+	default:
+		http_error(refs_url, http_ret);
+		die("HTTP request failed");
+	}
+
+	last= xcalloc(1, sizeof(*last_discovery));
+	last->service = service;
+	last->buf_alloc = strbuf_detach(&buffer, &last->len);
+	last->buf = last->buf_alloc;
+
+	if (is_http && proto_git_candidate
+		&& 5 <= last->len && last->buf[4] == '#') {
+		/* smart HTTP response; validate that the service
+		 * pkt-line matches our request.
+		 */
+		struct strbuf exp = STRBUF_INIT;
+
+		if (packet_get_line(&buffer, &last->buf, &last->len) <= 0)
+			die("%s has invalid packet header", refs_url);
+		if (buffer.len && buffer.buf[buffer.len - 1] == '\n')
+			strbuf_setlen(&buffer, buffer.len - 1);
+
+		strbuf_addf(&exp, "# service=%s", service);
+		if (strbuf_cmp(&exp, &buffer))
+			die("invalid server response; got '%s'", buffer.buf);
+		strbuf_release(&exp);
+
+		/* The header can include additional metadata lines, up
+		 * until a packet flush marker.  Ignore these now, but
+		 * in the future we might start to scan them.
+		 */
+		strbuf_reset(&buffer);
+		while (packet_get_line(&buffer, &last->buf, &last->len) > 0)
+			strbuf_reset(&buffer);
+
+		last->proto_git = 1;
+	}
+
+	free(refs_url);
+	strbuf_release(&buffer);
+	last_discovery = last;
+	return last;
+}
+
+static int write_discovery(int in, int out, void *data)
+{
+	struct discovery *heads = data;
+	int err = 0;
+	if (write_in_full(out, heads->buf, heads->len) != heads->len)
+		err = 1;
+	close(out);
+	return err;
+}
+
+static struct ref *parse_git_refs(struct discovery *heads, int for_push)
+{
+	struct ref *list = NULL;
+	struct async async;
+
+	memset(&async, 0, sizeof(async));
+	async.proc = write_discovery;
+	async.data = heads;
+	async.out = -1;
+
+	if (start_async(&async))
+		die("cannot start thread to parse advertised refs");
+	get_remote_heads(async.out, &list,
+			for_push ? REF_NORMAL : 0, NULL);
+	close(async.out);
+	if (finish_async(&async))
+		die("ref parsing thread failed");
+	return list;
+}
+
+static struct ref *parse_info_refs(struct discovery *heads)
+{
+	char *data, *start, *mid;
+	char *ref_name;
+	int i = 0;
+
+	struct ref *refs = NULL;
+	struct ref *ref = NULL;
+	struct ref *last_ref = NULL;
+
+	data = heads->buf;
+	start = NULL;
+	mid = data;
+	while (i < heads->len) {
+		if (!start) {
+			start = &data[i];
+		}
+		if (data[i] == '\t')
+			mid = &data[i];
+		if (data[i] == '\n') {
+			if (mid - start != 40)
+				die("%sinfo/refs not valid: is this a git repository?", url);
+			data[i] = 0;
+			ref_name = mid + 1;
+			ref = xmalloc(sizeof(struct ref) +
+				      strlen(ref_name) + 1);
+			memset(ref, 0, sizeof(struct ref));
+			strcpy(ref->name, ref_name);
+			get_sha1_hex(start, ref->old_sha1);
+			if (!refs)
+				refs = ref;
+			if (last_ref)
+				last_ref->next = ref;
+			last_ref = ref;
+			start = NULL;
+		}
+		i++;
+	}
+
+	ref = alloc_ref("HEAD");
+	if (!http_fetch_ref(url, ref) &&
+	    !resolve_remote_symref(ref, refs)) {
+		ref->next = refs;
+		refs = ref;
+	} else {
+		free(ref);
+	}
+
+	return refs;
+}
+
+static struct ref *get_refs(int for_push)
+{
+	struct discovery *heads;
+
+	if (for_push)
+		heads = discover_refs("git-receive-pack");
+	else
+		heads = discover_refs("git-upload-pack");
+
+	if (heads->proto_git)
+		return parse_git_refs(heads, for_push);
+	return parse_info_refs(heads);
+}
+
+static void output_refs(struct ref *refs)
+{
+	struct ref *posn;
+	for (posn = refs; posn; posn = posn->next) {
+		if (posn->symref)
+			printf("@%s %s\n", posn->symref, posn->name);
+		else
+			printf("%s %s\n", sha1_to_hex(posn->old_sha1), posn->name);
+	}
+	printf("\n");
+	fflush(stdout);
+	free_refs(refs);
+}
+
+struct rpc_state {
+	const char *service_name;
+	const char **argv;
+	struct strbuf *stdin_preamble;
+	char *service_url;
+	char *hdr_content_type;
+	char *hdr_accept;
+	char *buf;
+	size_t alloc;
+	size_t len;
+	size_t pos;
+	int in;
+	int out;
+	struct strbuf result;
+	unsigned gzip_request : 1;
+	unsigned initial_buffer : 1;
+};
+
+static size_t rpc_out(void *ptr, size_t eltsize,
+		size_t nmemb, void *buffer_)
+{
+	size_t max = eltsize * nmemb;
+	struct rpc_state *rpc = buffer_;
+	size_t avail = rpc->len - rpc->pos;
+
+	if (!avail) {
+		rpc->initial_buffer = 0;
+		avail = packet_read_line(rpc->out, rpc->buf, rpc->alloc);
+		if (!avail)
+			return 0;
+		rpc->pos = 0;
+		rpc->len = avail;
+	}
+
+	if (max < avail)
+		avail = max;
+	memcpy(ptr, rpc->buf + rpc->pos, avail);
+	rpc->pos += avail;
+	return avail;
+}
+
+#ifndef NO_CURL_IOCTL
+static curlioerr rpc_ioctl(CURL *handle, int cmd, void *clientp)
+{
+	struct rpc_state *rpc = clientp;
+
+	switch (cmd) {
+	case CURLIOCMD_NOP:
+		return CURLIOE_OK;
+
+	case CURLIOCMD_RESTARTREAD:
+		if (rpc->initial_buffer) {
+			rpc->pos = 0;
+			return CURLIOE_OK;
+		}
+		fprintf(stderr, "Unable to rewind rpc post data - try increasing http.postBuffer\n");
+		return CURLIOE_FAILRESTART;
+
+	default:
+		return CURLIOE_UNKNOWNCMD;
+	}
+}
+#endif
+
+static size_t rpc_in(char *ptr, size_t eltsize,
+		size_t nmemb, void *buffer_)
+{
+	size_t size = eltsize * nmemb;
+	struct rpc_state *rpc = buffer_;
+	write_or_die(rpc->in, ptr, size);
+	return size;
+}
+
+static int run_slot(struct active_request_slot *slot)
+{
+	int err = 0;
+	struct slot_results results;
+
+	slot->results = &results;
+	slot->curl_result = curl_easy_perform(slot->curl);
+	finish_active_slot(slot);
+
+	if (results.curl_result != CURLE_OK) {
+		err |= error("RPC failed; result=%d, HTTP code = %ld",
+			results.curl_result, results.http_code);
+	}
+
+	return err;
+}
+
+static int probe_rpc(struct rpc_state *rpc)
+{
+	struct active_request_slot *slot;
+	struct curl_slist *headers = NULL;
+	struct strbuf buf = STRBUF_INIT;
+	int err;
+
+	slot = get_active_slot();
+
+	headers = curl_slist_append(headers, rpc->hdr_content_type);
+	headers = curl_slist_append(headers, rpc->hdr_accept);
+
+	curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
+	curl_easy_setopt(slot->curl, CURLOPT_POST, 1);
+	curl_easy_setopt(slot->curl, CURLOPT_URL, rpc->service_url);
+	curl_easy_setopt(slot->curl, CURLOPT_ENCODING, "");
+	curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, "0000");
+	curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE, 4);
+	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
+	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
+	curl_easy_setopt(slot->curl, CURLOPT_FILE, &buf);
+
+	err = run_slot(slot);
+
+	curl_slist_free_all(headers);
+	strbuf_release(&buf);
+	return err;
+}
+
+static int post_rpc(struct rpc_state *rpc)
+{
+	struct active_request_slot *slot;
+	struct curl_slist *headers = NULL;
+	int use_gzip = rpc->gzip_request;
+	char *gzip_body = NULL;
+	int err, large_request = 0;
+
+	/* Try to load the entire request, if we can fit it into the
+	 * allocated buffer space we can use HTTP/1.0 and avoid the
+	 * chunked encoding mess.
+	 */
+	while (1) {
+		size_t left = rpc->alloc - rpc->len;
+		char *buf = rpc->buf + rpc->len;
+		int n;
+
+		if (left < LARGE_PACKET_MAX) {
+			large_request = 1;
+			use_gzip = 0;
+			break;
+		}
+
+		n = packet_read_line(rpc->out, buf, left);
+		if (!n)
+			break;
+		rpc->len += n;
+	}
+
+	if (large_request) {
+		err = probe_rpc(rpc);
+		if (err)
+			return err;
+	}
+
+	slot = get_active_slot();
+
+	curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
+	curl_easy_setopt(slot->curl, CURLOPT_POST, 1);
+	curl_easy_setopt(slot->curl, CURLOPT_URL, rpc->service_url);
+	curl_easy_setopt(slot->curl, CURLOPT_ENCODING, "");
+
+	headers = curl_slist_append(headers, rpc->hdr_content_type);
+	headers = curl_slist_append(headers, rpc->hdr_accept);
+	headers = curl_slist_append(headers, "Expect:");
+
+	if (large_request) {
+		/* The request body is large and the size cannot be predicted.
+		 * We must use chunked encoding to send it.
+		 */
+		headers = curl_slist_append(headers, "Transfer-Encoding: chunked");
+		rpc->initial_buffer = 1;
+		curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, rpc_out);
+		curl_easy_setopt(slot->curl, CURLOPT_INFILE, rpc);
+#ifndef NO_CURL_IOCTL
+		curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, rpc_ioctl);
+		curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, rpc);
+#endif
+		if (options.verbosity > 1) {
+			fprintf(stderr, "POST %s (chunked)\n", rpc->service_name);
+			fflush(stderr);
+		}
+
+	} else if (use_gzip && 1024 < rpc->len) {
+		/* The client backend isn't giving us compressed data so
+		 * we can try to deflate it ourselves, this may save on.
+		 * the transfer time.
+		 */
+		size_t size;
+		git_zstream stream;
+		int ret;
+
+		memset(&stream, 0, sizeof(stream));
+		git_deflate_init_gzip(&stream, Z_BEST_COMPRESSION);
+		size = git_deflate_bound(&stream, rpc->len);
+		gzip_body = xmalloc(size);
+
+		stream.next_in = (unsigned char *)rpc->buf;
+		stream.avail_in = rpc->len;
+		stream.next_out = (unsigned char *)gzip_body;
+		stream.avail_out = size;
+
+		ret = git_deflate(&stream, Z_FINISH);
+		if (ret != Z_STREAM_END)
+			die("cannot deflate request; zlib deflate error %d", ret);
+
+		ret = git_deflate_end_gently(&stream);
+		if (ret != Z_OK)
+			die("cannot deflate request; zlib end error %d", ret);
+
+		size = stream.total_out;
+
+		headers = curl_slist_append(headers, "Content-Encoding: gzip");
+		curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, gzip_body);
+		curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE, size);
+
+		if (options.verbosity > 1) {
+			fprintf(stderr, "POST %s (gzip %lu to %lu bytes)\n",
+				rpc->service_name,
+				(unsigned long)rpc->len, (unsigned long)size);
+			fflush(stderr);
+		}
+	} else {
+		/* We know the complete request size in advance, use the
+		 * more normal Content-Length approach.
+		 */
+		curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, rpc->buf);
+		curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE, rpc->len);
+		if (options.verbosity > 1) {
+			fprintf(stderr, "POST %s (%lu bytes)\n",
+				rpc->service_name, (unsigned long)rpc->len);
+			fflush(stderr);
+		}
+	}
+
+	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
+	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, rpc_in);
+	curl_easy_setopt(slot->curl, CURLOPT_FILE, rpc);
+
+	err = run_slot(slot);
+
+	curl_slist_free_all(headers);
+	free(gzip_body);
+	return err;
+}
+
+static int rpc_service(struct rpc_state *rpc, struct discovery *heads)
+{
+	const char *svc = rpc->service_name;
+	struct strbuf buf = STRBUF_INIT;
+	struct strbuf *preamble = rpc->stdin_preamble;
+	struct child_process client;
+	int err = 0;
+
+	memset(&client, 0, sizeof(client));
+	client.in = -1;
+	client.out = -1;
+	client.git_cmd = 1;
+	client.argv = rpc->argv;
+	if (start_command(&client))
+		exit(1);
+	if (preamble)
+		write_or_die(client.in, preamble->buf, preamble->len);
+	if (heads)
+		write_or_die(client.in, heads->buf, heads->len);
+
+	rpc->alloc = http_post_buffer;
+	rpc->buf = xmalloc(rpc->alloc);
+	rpc->in = client.in;
+	rpc->out = client.out;
+	strbuf_init(&rpc->result, 0);
+
+	strbuf_addf(&buf, "%s%s", url, svc);
+	rpc->service_url = strbuf_detach(&buf, NULL);
+
+	strbuf_addf(&buf, "Content-Type: application/x-%s-request", svc);
+	rpc->hdr_content_type = strbuf_detach(&buf, NULL);
+
+	strbuf_addf(&buf, "Accept: application/x-%s-result", svc);
+	rpc->hdr_accept = strbuf_detach(&buf, NULL);
+
+	while (!err) {
+		int n = packet_read_line(rpc->out, rpc->buf, rpc->alloc);
+		if (!n)
+			break;
+		rpc->pos = 0;
+		rpc->len = n;
+		err |= post_rpc(rpc);
+	}
+
+	close(client.in);
+	client.in = -1;
+	if (!err) {
+		strbuf_read(&rpc->result, client.out, 0);
+	} else {
+		char buf[4096];
+		for (;;)
+			if (xread(client.out, buf, sizeof(buf)) <= 0)
+				break;
+	}
+
+	close(client.out);
+	client.out = -1;
+
+	err |= finish_command(&client);
+	free(rpc->service_url);
+	free(rpc->hdr_content_type);
+	free(rpc->hdr_accept);
+	free(rpc->buf);
+	strbuf_release(&buf);
+	return err;
+}
+
+static int fetch_dumb(int nr_heads, struct ref **to_fetch)
+{
+	struct walker *walker;
+	char **targets = xmalloc(nr_heads * sizeof(char*));
+	int ret, i;
+
+	if (options.depth)
+		die("dumb http transport does not support --depth");
+	for (i = 0; i < nr_heads; i++)
+		targets[i] = xstrdup(sha1_to_hex(to_fetch[i]->old_sha1));
+
+	walker = get_http_walker(url);
+	walker->get_all = 1;
+	walker->get_tree = 1;
+	walker->get_history = 1;
+	walker->get_verbosely = options.verbosity >= 3;
+	walker->get_recover = 0;
+	ret = walker_fetch(walker, nr_heads, targets, NULL, NULL);
+	walker_free(walker);
+
+	for (i = 0; i < nr_heads; i++)
+		free(targets[i]);
+	free(targets);
+
+	return ret ? error("Fetch failed.") : 0;
+}
+
+static int fetch_git(struct discovery *heads,
+	int nr_heads, struct ref **to_fetch)
+{
+	struct rpc_state rpc;
+	struct strbuf preamble = STRBUF_INIT;
+	char *depth_arg = NULL;
+	int argc = 0, i, err;
+	const char *argv[15];
+
+	argv[argc++] = "fetch-pack";
+	argv[argc++] = "--stateless-rpc";
+	argv[argc++] = "--stdin";
+	argv[argc++] = "--lock-pack";
+	if (options.followtags)
+		argv[argc++] = "--include-tag";
+	if (options.thin)
+		argv[argc++] = "--thin";
+	if (options.verbosity >= 3) {
+		argv[argc++] = "-v";
+		argv[argc++] = "-v";
+	}
+	if (!options.progress)
+		argv[argc++] = "--no-progress";
+	if (options.depth) {
+		struct strbuf buf = STRBUF_INIT;
+		strbuf_addf(&buf, "--depth=%lu", options.depth);
+		depth_arg = strbuf_detach(&buf, NULL);
+		argv[argc++] = depth_arg;
+	}
+	argv[argc++] = url;
+	argv[argc++] = NULL;
+
+	for (i = 0; i < nr_heads; i++) {
+		struct ref *ref = to_fetch[i];
+		if (!ref->name || !*ref->name)
+			die("cannot fetch by sha1 over smart http");
+		packet_buf_write(&preamble, "%s\n", ref->name);
+	}
+	packet_buf_flush(&preamble);
+
+	memset(&rpc, 0, sizeof(rpc));
+	rpc.service_name = "git-upload-pack",
+	rpc.argv = argv;
+	rpc.stdin_preamble = &preamble;
+	rpc.gzip_request = 1;
+
+	err = rpc_service(&rpc, heads);
+	if (rpc.result.len)
+		safe_write(1, rpc.result.buf, rpc.result.len);
+	strbuf_release(&rpc.result);
+	strbuf_release(&preamble);
+	free(depth_arg);
+	return err;
+}
+
+static int fetch(int nr_heads, struct ref **to_fetch)
+{
+	struct discovery *d = discover_refs("git-upload-pack");
+	if (d->proto_git)
+		return fetch_git(d, nr_heads, to_fetch);
+	else
+		return fetch_dumb(nr_heads, to_fetch);
+}
+
+static void parse_fetch(struct strbuf *buf)
+{
+	struct ref **to_fetch = NULL;
+	struct ref *list_head = NULL;
+	struct ref **list = &list_head;
+	int alloc_heads = 0, nr_heads = 0;
+
+	do {
+		if (!prefixcmp(buf->buf, "fetch ")) {
+			char *p = buf->buf + strlen("fetch ");
+			char *name;
+			struct ref *ref;
+			unsigned char old_sha1[20];
+
+			if (strlen(p) < 40 || get_sha1_hex(p, old_sha1))
+				die("protocol error: expected sha/ref, got %s'", p);
+			if (p[40] == ' ')
+				name = p + 41;
+			else if (!p[40])
+				name = "";
+			else
+				die("protocol error: expected sha/ref, got %s'", p);
+
+			ref = alloc_ref(name);
+			hashcpy(ref->old_sha1, old_sha1);
+
+			*list = ref;
+			list = &ref->next;
+
+			ALLOC_GROW(to_fetch, nr_heads + 1, alloc_heads);
+			to_fetch[nr_heads++] = ref;
+		}
+		else
+			die("http transport does not support %s", buf->buf);
+
+		strbuf_reset(buf);
+		if (strbuf_getline(buf, stdin, '\n') == EOF)
+			return;
+		if (!*buf->buf)
+			break;
+	} while (1);
+
+	if (fetch(nr_heads, to_fetch))
+		exit(128); /* error already reported */
+	free_refs(list_head);
+	free(to_fetch);
+
+	printf("\n");
+	fflush(stdout);
+	strbuf_reset(buf);
+}
+
+static int push_dav(int nr_spec, char **specs)
+{
+	const char **argv = xmalloc((10 + nr_spec) * sizeof(char*));
+	int argc = 0, i;
+
+	argv[argc++] = "http-push";
+	argv[argc++] = "--helper-status";
+	if (options.dry_run)
+		argv[argc++] = "--dry-run";
+	if (options.verbosity > 1)
+		argv[argc++] = "--verbose";
+	argv[argc++] = url;
+	for (i = 0; i < nr_spec; i++)
+		argv[argc++] = specs[i];
+	argv[argc++] = NULL;
+
+	if (run_command_v_opt(argv, RUN_GIT_CMD))
+		die("git-%s failed", argv[0]);
+	free(argv);
+	return 0;
+}
+
+static int push_git(struct discovery *heads, int nr_spec, char **specs)
+{
+	struct rpc_state rpc;
+	const char **argv;
+	int argc = 0, i, err;
+
+	argv = xmalloc((10 + nr_spec) * sizeof(char*));
+	argv[argc++] = "send-pack";
+	argv[argc++] = "--stateless-rpc";
+	argv[argc++] = "--helper-status";
+	if (options.thin)
+		argv[argc++] = "--thin";
+	if (options.dry_run)
+		argv[argc++] = "--dry-run";
+	if (options.verbosity == 0)
+		argv[argc++] = "--quiet";
+	else if (options.verbosity > 1)
+		argv[argc++] = "--verbose";
+	argv[argc++] = options.progress ? "--progress" : "--no-progress";
+	argv[argc++] = url;
+	for (i = 0; i < nr_spec; i++)
+		argv[argc++] = specs[i];
+	argv[argc++] = NULL;
+
+	memset(&rpc, 0, sizeof(rpc));
+	rpc.service_name = "git-receive-pack",
+	rpc.argv = argv;
+
+	err = rpc_service(&rpc, heads);
+	if (rpc.result.len)
+		safe_write(1, rpc.result.buf, rpc.result.len);
+	strbuf_release(&rpc.result);
+	free(argv);
+	return err;
+}
+
+static int push(int nr_spec, char **specs)
+{
+	struct discovery *heads = discover_refs("git-receive-pack");
+	int ret;
+
+	if (heads->proto_git)
+		ret = push_git(heads, nr_spec, specs);
+	else
+		ret = push_dav(nr_spec, specs);
+	free_discovery(heads);
+	return ret;
+}
+
+static void parse_push(struct strbuf *buf)
+{
+	char **specs = NULL;
+	int alloc_spec = 0, nr_spec = 0, i, ret;
+
+	do {
+		if (!prefixcmp(buf->buf, "push ")) {
+			ALLOC_GROW(specs, nr_spec + 1, alloc_spec);
+			specs[nr_spec++] = xstrdup(buf->buf + 5);
+		}
+		else
+			die("http transport does not support %s", buf->buf);
+
+		strbuf_reset(buf);
+		if (strbuf_getline(buf, stdin, '\n') == EOF)
+			goto free_specs;
+		if (!*buf->buf)
+			break;
+	} while (1);
+
+	ret = push(nr_spec, specs);
+	printf("\n");
+	fflush(stdout);
+
+	if (ret)
+		exit(128); /* error already reported */
+
+ free_specs:
+	for (i = 0; i < nr_spec; i++)
+		free(specs[i]);
+	free(specs);
+}
+
+int main(int argc, const char **argv)
+{
+	struct strbuf buf = STRBUF_INIT;
+	int nongit;
+
+	git_extract_argv0_path(argv[0]);
+	setup_git_directory_gently(&nongit);
+	if (argc < 2) {
+		fprintf(stderr, "Remote needed\n");
+		return 1;
+	}
+
+	options.verbosity = 1;
+	options.progress = !!isatty(2);
+	options.thin = 1;
+
+	remote = remote_get(argv[1]);
+
+	if (argc > 2) {
+		end_url_with_slash(&buf, argv[2]);
+	} else {
+		end_url_with_slash(&buf, remote->url[0]);
+	}
+
+	url = strbuf_detach(&buf, NULL);
+
+	http_init(remote, url, 0);
+
+	do {
+		if (strbuf_getline(&buf, stdin, '\n') == EOF) {
+			if (ferror(stdin))
+				fprintf(stderr, "Error reading command stream\n");
+			else
+				fprintf(stderr, "Unexpected end of command stream\n");
+			return 1;
+		}
+		if (buf.len == 0)
+			break;
+		if (!prefixcmp(buf.buf, "fetch ")) {
+			if (nongit)
+				die("Fetch attempted without a local repo");
+			parse_fetch(&buf);
+
+		} else if (!strcmp(buf.buf, "list") || !prefixcmp(buf.buf, "list ")) {
+			int for_push = !!strstr(buf.buf + 4, "for-push");
+			output_refs(get_refs(for_push));
+
+		} else if (!prefixcmp(buf.buf, "push ")) {
+			parse_push(&buf);
+
+		} else if (!prefixcmp(buf.buf, "option ")) {
+			char *name = buf.buf + strlen("option ");
+			char *value = strchr(name, ' ');
+			int result;
+
+			if (value)
+				*value++ = '\0';
+			else
+				value = "true";
+
+			result = set_option(name, value);
+			if (!result)
+				printf("ok\n");
+			else if (result < 0)
+				printf("error invalid value\n");
+			else
+				printf("unsupported\n");
+			fflush(stdout);
+
+		} else if (!strcmp(buf.buf, "capabilities")) {
+			printf("fetch\n");
+			printf("option\n");
+			printf("push\n");
+			printf("\n");
+			fflush(stdout);
+		} else {
+			fprintf(stderr, "Unknown command '%s'\n", buf.buf);
+			return 1;
+		}
+		strbuf_reset(&buf);
+	} while (1);
+
+	http_cleanup();
+
+	return 0;
+}
diff --git a/remote.c b/remote.c
index c3ada2d..04fd9ea 100644
--- a/remote.c
+++ b/remote.c
@@ -6,6 +6,10 @@
 #include "revision.h"
 #include "dir.h"
 #include "tag.h"
+#include "string-list.h"
+#include "mergesort.h"
+
+enum map_direction { FROM_SRC, FROM_DST };
 
 static struct refspec s_tag_refspec = {
 	0,
@@ -28,6 +32,11 @@
 	int instead_of_nr;
 	int instead_of_alloc;
 };
+struct rewrites {
+	struct rewrite **rewrite;
+	int rewrite_alloc;
+	int rewrite_nr;
+};
 
 static struct remote **remotes;
 static int remotes_alloc;
@@ -41,14 +50,18 @@
 static const char *default_remote_name;
 static int explicit_default_remote_name;
 
-static struct rewrite **rewrite;
-static int rewrite_alloc;
-static int rewrite_nr;
+static struct rewrites rewrites;
+static struct rewrites rewrites_push;
 
 #define BUF_SIZE (2048)
 static char buffer[BUF_SIZE];
 
-static const char *alias_url(const char *url)
+static int valid_remote(const struct remote *remote)
+{
+	return (!!remote->url) || (!!remote->foreign_vcs);
+}
+
+static const char *alias_url(const char *url, struct rewrites *r)
 {
 	int i, j;
 	char *ret;
@@ -57,14 +70,14 @@
 
 	longest = NULL;
 	longest_i = -1;
-	for (i = 0; i < rewrite_nr; i++) {
-		if (!rewrite[i])
+	for (i = 0; i < r->rewrite_nr; i++) {
+		if (!r->rewrite[i])
 			continue;
-		for (j = 0; j < rewrite[i]->instead_of_nr; j++) {
-			if (!prefixcmp(url, rewrite[i]->instead_of[j].s) &&
+		for (j = 0; j < r->rewrite[i]->instead_of_nr; j++) {
+			if (!prefixcmp(url, r->rewrite[i]->instead_of[j].s) &&
 			    (!longest ||
-			     longest->len < rewrite[i]->instead_of[j].len)) {
-				longest = &(rewrite[i]->instead_of[j]);
+			     longest->len < r->rewrite[i]->instead_of[j].len)) {
+				longest = &(r->rewrite[i]->instead_of[j]);
 				longest_i = i;
 			}
 		}
@@ -72,10 +85,10 @@
 	if (!longest)
 		return url;
 
-	ret = xmalloc(rewrite[longest_i]->baselen +
+	ret = xmalloc(r->rewrite[longest_i]->baselen +
 		     (strlen(url) - longest->len) + 1);
-	strcpy(ret, rewrite[longest_i]->base);
-	strcpy(ret + rewrite[longest_i]->baselen, url + longest->len);
+	strcpy(ret, r->rewrite[longest_i]->base);
+	strcpy(ret + r->rewrite[longest_i]->baselen, url + longest->len);
 	return ret;
 }
 
@@ -101,17 +114,25 @@
 	remote->url[remote->url_nr++] = url;
 }
 
-static void add_url_alias(struct remote *remote, const char *url)
-{
-	add_url(remote, alias_url(url));
-}
-
 static void add_pushurl(struct remote *remote, const char *pushurl)
 {
 	ALLOC_GROW(remote->pushurl, remote->pushurl_nr + 1, remote->pushurl_alloc);
 	remote->pushurl[remote->pushurl_nr++] = pushurl;
 }
 
+static void add_pushurl_alias(struct remote *remote, const char *url)
+{
+	const char *pushurl = alias_url(url, &rewrites_push);
+	if (pushurl != url)
+		add_pushurl(remote, pushurl);
+}
+
+static void add_url_alias(struct remote *remote, const char *url)
+{
+	add_url(remote, alias_url(url, &rewrites));
+	add_pushurl_alias(remote, url);
+}
+
 static struct remote *make_remote(const char *name, int len)
 {
 	struct remote *ret;
@@ -169,22 +190,22 @@
 	return ret;
 }
 
-static struct rewrite *make_rewrite(const char *base, int len)
+static struct rewrite *make_rewrite(struct rewrites *r, const char *base, int len)
 {
 	struct rewrite *ret;
 	int i;
 
-	for (i = 0; i < rewrite_nr; i++) {
+	for (i = 0; i < r->rewrite_nr; i++) {
 		if (len
-		    ? (len == rewrite[i]->baselen &&
-		       !strncmp(base, rewrite[i]->base, len))
-		    : !strcmp(base, rewrite[i]->base))
-			return rewrite[i];
+		    ? (len == r->rewrite[i]->baselen &&
+		       !strncmp(base, r->rewrite[i]->base, len))
+		    : !strcmp(base, r->rewrite[i]->base))
+			return r->rewrite[i];
 	}
 
-	ALLOC_GROW(rewrite, rewrite_nr + 1, rewrite_alloc);
+	ALLOC_GROW(r->rewrite, r->rewrite_nr + 1, r->rewrite_alloc);
 	ret = xcalloc(1, sizeof(struct rewrite));
-	rewrite[rewrite_nr++] = ret;
+	r->rewrite[r->rewrite_nr++] = ret;
 	if (len) {
 		ret->base = xstrndup(base, len);
 		ret->baselen = len;
@@ -355,8 +376,13 @@
 		subkey = strrchr(name, '.');
 		if (!subkey)
 			return 0;
-		rewrite = make_rewrite(name, subkey - name);
 		if (!strcmp(subkey, ".insteadof")) {
+			rewrite = make_rewrite(&rewrites, name, subkey - name);
+			if (!value)
+				return config_error_nonbool(key);
+			add_instead_of(rewrite, xstrdup(value));
+		} else if (!strcmp(subkey, ".pushinsteadof")) {
+			rewrite = make_rewrite(&rewrites_push, name, subkey - name);
 			if (!value)
 				return config_error_nonbool(key);
 			add_instead_of(rewrite, xstrdup(value));
@@ -379,7 +405,8 @@
 		remote->mirror = git_config_bool(key, value);
 	else if (!strcmp(subkey, ".skipdefaultupdate"))
 		remote->skip_default_update = git_config_bool(key, value);
-
+	else if (!strcmp(subkey, ".skipfetchall"))
+		remote->skip_default_update = git_config_bool(key, value);
 	else if (!strcmp(subkey, ".url")) {
 		const char *v;
 		if (git_config_string(&v, key, value))
@@ -419,9 +446,13 @@
 	} else if (!strcmp(subkey, ".tagopt")) {
 		if (!strcmp(value, "--no-tags"))
 			remote->fetch_tags = -1;
+		else if (!strcmp(value, "--tags"))
+			remote->fetch_tags = 2;
 	} else if (!strcmp(subkey, ".proxy")) {
 		return git_config_string((const char **)&remote->http_proxy,
 					 key, value);
+	} else if (!strcmp(subkey, ".vcs")) {
+		return git_config_string(&remote->foreign_vcs, key, value);
 	}
 	return 0;
 }
@@ -430,13 +461,17 @@
 {
 	int i, j;
 	for (i = 0; i < remotes_nr; i++) {
+		int add_pushurl_aliases;
 		if (!remotes[i])
 			continue;
-		for (j = 0; j < remotes[i]->url_nr; j++) {
-			remotes[i]->url[j] = alias_url(remotes[i]->url[j]);
-		}
 		for (j = 0; j < remotes[i]->pushurl_nr; j++) {
-			remotes[i]->pushurl[j] = alias_url(remotes[i]->pushurl[j]);
+			remotes[i]->pushurl[j] = alias_url(remotes[i]->pushurl[j], &rewrites);
+		}
+		add_pushurl_aliases = remotes[i]->pushurl_nr == 0;
+		for (j = 0; j < remotes[i]->url_nr; j++) {
+			if (add_pushurl_aliases)
+				add_pushurl_alias(remotes[i], remotes[i]->url[j]);
+			remotes[i]->url[j] = alias_url(remotes[i]->url[j], &rewrites);
 		}
 	}
 }
@@ -446,11 +481,11 @@
 	unsigned char sha1[20];
 	const char *head_ref;
 	int flag;
-	if (default_remote_name) // did this already
+	if (default_remote_name) /* did this already */
 		return;
 	default_remote_name = xstrdup("origin");
 	current_branch = NULL;
-	head_ref = resolve_ref("HEAD", sha1, 0, &flag);
+	head_ref = resolve_ref_unsafe("HEAD", sha1, 0, &flag);
 	if (head_ref && (flag & REF_ISSYMREF) &&
 	    !prefixcmp(head_ref, "refs/heads/")) {
 		current_branch =
@@ -461,23 +496,6 @@
 }
 
 /*
- * We need to make sure the tracking branches are well formed, but a
- * wildcard refspec in "struct refspec" must have a trailing slash. We
- * temporarily drop the trailing '/' while calling check_ref_format(),
- * and put it back.  The caller knows that a CHECK_REF_FORMAT_ONELEVEL
- * error return is Ok for a wildcard refspec.
- */
-static int verify_refname(char *name, int is_glob)
-{
-	int result;
-
-	result = check_ref_format(name);
-	if (is_glob && result == CHECK_REF_FORMAT_WILDCARD)
-		result = CHECK_REF_FORMAT_OK;
-	return result;
-}
-
-/*
  * This function frees a refspec array.
  * Warning: code paths should be checked to ensure that the src
  *          and dst pointers are always freeable pointers as well
@@ -500,13 +518,13 @@
 static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)
 {
 	int i;
-	int st;
 	struct refspec *rs = xcalloc(sizeof(*rs), nr_refspec);
 
 	for (i = 0; i < nr_refspec; i++) {
 		size_t llen;
 		int is_glob;
 		const char *lhs, *rhs;
+		int flags;
 
 		is_glob = 0;
 
@@ -544,6 +562,7 @@
 
 		rs[i].pattern = is_glob;
 		rs[i].src = xstrndup(lhs, llen);
+		flags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0);
 
 		if (fetch) {
 			/*
@@ -553,26 +572,20 @@
 			 */
 			if (!*rs[i].src)
 				; /* empty is ok */
-			else {
-				st = verify_refname(rs[i].src, is_glob);
-				if (st && st != CHECK_REF_FORMAT_ONELEVEL)
-					goto invalid;
-			}
+			else if (check_refname_format(rs[i].src, flags))
+				goto invalid;
 			/*
 			 * RHS
 			 * - missing is ok, and is same as empty.
 			 * - empty is ok; it means not to store.
 			 * - otherwise it must be a valid looking ref.
 			 */
-			if (!rs[i].dst) {
+			if (!rs[i].dst)
 				; /* ok */
-			} else if (!*rs[i].dst) {
+			else if (!*rs[i].dst)
 				; /* ok */
-			} else {
-				st = verify_refname(rs[i].dst, is_glob);
-				if (st && st != CHECK_REF_FORMAT_ONELEVEL)
-					goto invalid;
-			}
+			else if (check_refname_format(rs[i].dst, flags))
+				goto invalid;
 		} else {
 			/*
 			 * LHS
@@ -584,8 +597,7 @@
 			if (!*rs[i].src)
 				; /* empty is ok */
 			else if (is_glob) {
-				st = verify_refname(rs[i].src, is_glob);
-				if (st && st != CHECK_REF_FORMAT_ONELEVEL)
+				if (check_refname_format(rs[i].src, flags))
 					goto invalid;
 			}
 			else
@@ -598,14 +610,12 @@
 			 * - otherwise it must be a valid looking ref.
 			 */
 			if (!rs[i].dst) {
-				st = verify_refname(rs[i].src, is_glob);
-				if (st && st != CHECK_REF_FORMAT_ONELEVEL)
+				if (check_refname_format(rs[i].src, flags))
 					goto invalid;
 			} else if (!*rs[i].dst) {
 				goto invalid;
 			} else {
-				st = verify_refname(rs[i].dst, is_glob);
-				if (st && st != CHECK_REF_FORMAT_ONELEVEL)
+				if (check_refname_format(rs[i].dst, flags))
 					goto invalid;
 			}
 		}
@@ -627,10 +637,9 @@
 
 int valid_fetch_refspec(const char *fetch_refspec_str)
 {
-	const char *fetch_refspec[] = { fetch_refspec_str };
 	struct refspec *refspec;
 
-	refspec = parse_refspec_internal(1, fetch_refspec, 1, 1);
+	refspec = parse_refspec_internal(1, &fetch_refspec_str, 1, 1);
 	free_refspecs(refspec, 1);
 	return !!refspec;
 }
@@ -645,6 +654,16 @@
 	return parse_refspec_internal(nr_refspec, refspec, 0, 0);
 }
 
+void free_refspec(int nr_refspec, struct refspec *refspec)
+{
+	int i;
+	for (i = 0; i < nr_refspec; i++) {
+		free(refspec[i].src);
+		free(refspec[i].dst);
+	}
+	free(refspec);
+}
+
 static int valid_remote_nick(const char *name)
 {
 	if (!name[0] || is_dot_or_dotdot(name))
@@ -667,14 +686,14 @@
 
 	ret = make_remote(name, 0);
 	if (valid_remote_nick(name)) {
-		if (!ret->url)
+		if (!valid_remote(ret))
 			read_remotes_file(ret);
-		if (!ret->url)
+		if (!valid_remote(ret))
 			read_branches_file(ret);
 	}
-	if (name_given && !ret->url)
+	if (name_given && !valid_remote(ret))
 		add_url_alias(ret, name);
-	if (!ret->url)
+	if (!valid_remote(ret))
 		return NULL;
 	ret->fetch = parse_fetch_refspec(ret->fetch_refspec_nr, ret->fetch_refspec);
 	ret->push = parse_push_refspec(ret->push_refspec_nr, ret->push_refspec);
@@ -713,29 +732,33 @@
 
 void ref_remove_duplicates(struct ref *ref_map)
 {
-	struct ref **posn;
-	struct ref *next;
-	for (; ref_map; ref_map = ref_map->next) {
+	struct string_list refs = STRING_LIST_INIT_NODUP;
+	struct string_list_item *item = NULL;
+	struct ref *prev = NULL, *next = NULL;
+	for (; ref_map; prev = ref_map, ref_map = next) {
+		next = ref_map->next;
 		if (!ref_map->peer_ref)
 			continue;
-		posn = &ref_map->next;
-		while (*posn) {
-			if ((*posn)->peer_ref &&
-			    !strcmp((*posn)->peer_ref->name,
-				    ref_map->peer_ref->name)) {
-				if (strcmp((*posn)->name, ref_map->name))
-					die("%s tracks both %s and %s",
-					    ref_map->peer_ref->name,
-					    (*posn)->name, ref_map->name);
-				next = (*posn)->next;
-				free((*posn)->peer_ref);
-				free(*posn);
-				*posn = next;
-			} else {
-				posn = &(*posn)->next;
-			}
+
+		item = string_list_lookup(&refs, ref_map->peer_ref->name);
+		if (item) {
+			if (strcmp(((struct ref *)item->util)->name,
+				   ref_map->name))
+				die("%s tracks both %s and %s",
+				    ref_map->peer_ref->name,
+				    ((struct ref *)item->util)->name,
+				    ref_map->name);
+			prev->next = ref_map->next;
+			free(ref_map->peer_ref);
+			free(ref_map);
+			ref_map = prev; /* skip this; we freed it */
+			continue;
 		}
+
+		item = string_list_insert(&refs, ref_map->peer_ref->name);
+		item->util = ref_map;
 	}
+	string_list_clear(&refs, 0);
 }
 
 int remote_has_url(struct remote *remote, const char *url)
@@ -783,42 +806,56 @@
 	return ret;
 }
 
-int remote_find_tracking(struct remote *remote, struct refspec *refspec)
+static int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query)
 {
-	int find_src = refspec->src == NULL;
-	char *needle, **result;
 	int i;
+	int find_src = !query->src;
 
-	if (find_src) {
+	if (find_src && !query->dst)
+		return error("query_refspecs: need either src or dst");
+
+	for (i = 0; i < ref_count; i++) {
+		struct refspec *refspec = &refs[i];
+		const char *key = find_src ? refspec->dst : refspec->src;
+		const char *value = find_src ? refspec->src : refspec->dst;
+		const char *needle = find_src ? query->dst : query->src;
+		char **result = find_src ? &query->src : &query->dst;
+
 		if (!refspec->dst)
-			return error("find_tracking: need either src or dst");
-		needle = refspec->dst;
-		result = &refspec->src;
-	} else {
-		needle = refspec->src;
-		result = &refspec->dst;
-	}
-
-	for (i = 0; i < remote->fetch_refspec_nr; i++) {
-		struct refspec *fetch = &remote->fetch[i];
-		const char *key = find_src ? fetch->dst : fetch->src;
-		const char *value = find_src ? fetch->src : fetch->dst;
-		if (!fetch->dst)
 			continue;
-		if (fetch->pattern) {
+		if (refspec->pattern) {
 			if (match_name_with_pattern(key, needle, value, result)) {
-				refspec->force = fetch->force;
+				query->force = refspec->force;
 				return 0;
 			}
 		} else if (!strcmp(needle, key)) {
 			*result = xstrdup(value);
-			refspec->force = fetch->force;
+			query->force = refspec->force;
 			return 0;
 		}
 	}
 	return -1;
 }
 
+char *apply_refspecs(struct refspec *refspecs, int nr_refspec,
+		     const char *name)
+{
+	struct refspec query;
+
+	memset(&query, 0, sizeof(struct refspec));
+	query.src = (char *)name;
+
+	if (query_refspecs(refspecs, nr_refspec, &query))
+		return NULL;
+
+	return query.dst;
+}
+
+int remote_find_tracking(struct remote *remote, struct refspec *refspec)
+{
+	return query_refspecs(remote->fetch, remote->fetch_refspec_nr, refspec);
+}
+
 static struct ref *alloc_ref_with_prefix(const char *prefix, size_t prefixlen,
 		const char *name)
 {
@@ -834,7 +871,7 @@
 	return alloc_ref_with_prefix("", 0, name);
 }
 
-static struct ref *copy_ref(const struct ref *ref)
+struct ref *copy_ref(const struct ref *ref)
 {
 	struct ref *cpy;
 	size_t len;
@@ -882,6 +919,27 @@
 	}
 }
 
+int ref_compare_name(const void *va, const void *vb)
+{
+	const struct ref *a = va, *b = vb;
+	return strcmp(a->name, b->name);
+}
+
+static void *ref_list_get_next(const void *a)
+{
+	return ((const struct ref *)a)->next;
+}
+
+static void ref_list_set_next(void *a, void *next)
+{
+	((struct ref *)a)->next = next;
+}
+
+void sort_ref_list(struct ref **l, int (*cmp)(const void *, const void *))
+{
+	*l = llist_mergesort(*l, ref_list_get_next, ref_list_set_next, cmp);
+}
+
 static int count_refspec_match(const char *pattern,
 			       struct ref *refs,
 			       struct ref **matched_ref)
@@ -944,16 +1002,20 @@
 	*tail = &ref->next;
 }
 
+static struct ref *alloc_delete_ref(void)
+{
+	struct ref *ref = alloc_ref("(delete)");
+	hashclr(ref->new_sha1);
+	return ref;
+}
+
 static struct ref *try_explicit_object_name(const char *name)
 {
 	unsigned char sha1[20];
 	struct ref *ref;
 
-	if (!*name) {
-		ref = alloc_ref("(delete)");
-		hashclr(ref->new_sha1);
-		return ref;
-	}
+	if (!*name)
+		return alloc_delete_ref();
 	if (get_sha1(name, sha1))
 		return NULL;
 	ref = alloc_ref(name);
@@ -973,7 +1035,7 @@
 	struct strbuf buf = STRBUF_INIT;
 	unsigned char sha1[20];
 
-	const char *r = resolve_ref(peer->name, sha1, 1, NULL);
+	const char *r = resolve_ref_unsafe(peer->name, sha1, 1, NULL);
 	if (!r)
 		return NULL;
 
@@ -1024,7 +1086,7 @@
 		unsigned char sha1[20];
 		int flag;
 
-		dst_value = resolve_ref(matched_src->name, sha1, 1, &flag);
+		dst_value = resolve_ref_unsafe(matched_src->name, sha1, 1, &flag);
 		if (!dst_value ||
 		    ((flag & REF_ISSYMREF) &&
 		     prefixcmp(dst_value, "refs/heads/")))
@@ -1038,7 +1100,10 @@
 	case 0:
 		if (!memcmp(dst_value, "refs/", 5))
 			matched_dst = make_linked_ref(dst_value, dst_tail);
-		else if((dst_guess = guess_ref(dst_value, matched_src)))
+		else if (is_null_sha1(matched_src->new_sha1))
+			error("unable to delete '%s': remote ref does not exist",
+			      dst_value);
+		else if ((dst_guess = guess_ref(dst_value, matched_src)))
 			matched_dst = make_linked_ref(dst_guess, dst_tail);
 		else
 			error("unable to push to unqualified destination: %s\n"
@@ -1076,10 +1141,11 @@
 	return errs;
 }
 
-static const struct refspec *check_pattern_match(const struct refspec *rs,
-						 int rs_nr,
-						 const struct ref *src)
+static char *get_ref_match(const struct refspec *rs, int rs_nr, const struct ref *ref,
+		int send_mirror, int direction, const struct refspec **ret_pat)
 {
+	const struct refspec *pat;
+	char *name;
 	int i;
 	int matching_refs = -1;
 	for (i = 0; i < rs_nr; i++) {
@@ -1089,14 +1155,36 @@
 			continue;
 		}
 
-		if (rs[i].pattern && match_name_with_pattern(rs[i].src, src->name,
-							     NULL, NULL))
-			return rs + i;
+		if (rs[i].pattern) {
+			const char *dst_side = rs[i].dst ? rs[i].dst : rs[i].src;
+			int match;
+			if (direction == FROM_SRC)
+				match = match_name_with_pattern(rs[i].src, ref->name, dst_side, &name);
+			else
+				match = match_name_with_pattern(dst_side, ref->name, rs[i].src, &name);
+			if (match) {
+				matching_refs = i;
+				break;
+			}
+		}
 	}
-	if (matching_refs != -1)
-		return rs + matching_refs;
-	else
+	if (matching_refs == -1)
 		return NULL;
+
+	pat = rs + matching_refs;
+	if (pat->matching) {
+		/*
+		 * "matching refs"; traditionally we pushed everything
+		 * including refs outside refs/heads/ hierarchy, but
+		 * that does not make much sense these days.
+		 */
+		if (!send_mirror && prefixcmp(ref->name, "refs/heads/"))
+			return NULL;
+		name = xstrdup(ref->name);
+	}
+	if (ret_pat)
+		*ret_pat = pat;
+	return name;
 }
 
 static struct ref **tail_ref(struct ref **head)
@@ -1108,19 +1196,23 @@
 }
 
 /*
- * Note. This is used only by "push"; refspec matching rules for
- * push and fetch are subtly different, so do not try to reuse it
- * without thinking.
+ * Given the set of refs the local repository has, the set of refs the
+ * remote repository has, and the refspec used for push, determine
+ * what remote refs we will update and with what value by setting
+ * peer_ref (which object is being pushed) and force (if the push is
+ * forced) in elements of "dst". The function may add new elements to
+ * dst (e.g. pushing to a new branch, done in match_explicit_refs).
  */
-int match_refs(struct ref *src, struct ref **dst,
-	       int nr_refspec, const char **refspec, int flags)
+int match_push_refs(struct ref *src, struct ref **dst,
+		    int nr_refspec, const char **refspec, int flags)
 {
 	struct refspec *rs;
 	int send_all = flags & MATCH_REFS_ALL;
 	int send_mirror = flags & MATCH_REFS_MIRROR;
+	int send_prune = flags & MATCH_REFS_PRUNE;
 	int errs;
 	static const char *default_refspec[] = { ":", NULL };
-	struct ref **dst_tail = tail_ref(dst);
+	struct ref *ref, **dst_tail = tail_ref(dst);
 
 	if (!nr_refspec) {
 		nr_refspec = 1;
@@ -1130,39 +1222,23 @@
 	errs = match_explicit_refs(src, *dst, &dst_tail, rs, nr_refspec);
 
 	/* pick the remainder */
-	for ( ; src; src = src->next) {
+	for (ref = src; ref; ref = ref->next) {
 		struct ref *dst_peer;
 		const struct refspec *pat = NULL;
 		char *dst_name;
-		if (src->peer_ref)
+
+		if (ref->peer_ref)
 			continue;
 
-		pat = check_pattern_match(rs, nr_refspec, src);
-		if (!pat)
+		dst_name = get_ref_match(rs, nr_refspec, ref, send_mirror, FROM_SRC, &pat);
+		if (!dst_name)
 			continue;
 
-		if (pat->matching) {
-			/*
-			 * "matching refs"; traditionally we pushed everything
-			 * including refs outside refs/heads/ hierarchy, but
-			 * that does not make much sense these days.
-			 */
-			if (!send_mirror && prefixcmp(src->name, "refs/heads/"))
-				continue;
-			dst_name = xstrdup(src->name);
-
-		} else {
-			const char *dst_side = pat->dst ? pat->dst : pat->src;
-			if (!match_name_with_pattern(pat->src, src->name,
-						     dst_side, &dst_name))
-				die("Didn't think it matches any more");
-		}
 		dst_peer = find_ref_by_name(*dst, dst_name);
 		if (dst_peer) {
 			if (dst_peer->peer_ref)
 				/* We're already sending something to this ref. */
 				goto free_name;
-
 		} else {
 			if (pat->matching && !(send_all || send_mirror))
 				/*
@@ -1174,18 +1250,85 @@
 
 			/* Create a new one and link it */
 			dst_peer = make_linked_ref(dst_name, &dst_tail);
-			hashcpy(dst_peer->new_sha1, src->new_sha1);
+			hashcpy(dst_peer->new_sha1, ref->new_sha1);
 		}
-		dst_peer->peer_ref = copy_ref(src);
+		dst_peer->peer_ref = copy_ref(ref);
 		dst_peer->force = pat->force;
 	free_name:
 		free(dst_name);
 	}
+	if (send_prune) {
+		/* check for missing refs on the remote */
+		for (ref = *dst; ref; ref = ref->next) {
+			char *src_name;
+
+			if (ref->peer_ref)
+				/* We're already sending something to this ref. */
+				continue;
+
+			src_name = get_ref_match(rs, nr_refspec, ref, send_mirror, FROM_DST, NULL);
+			if (src_name) {
+				if (!find_ref_by_name(src, src_name))
+					ref->peer_ref = alloc_delete_ref();
+				free(src_name);
+			}
+		}
+	}
 	if (errs)
 		return -1;
 	return 0;
 }
 
+void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
+	int force_update)
+{
+	struct ref *ref;
+
+	for (ref = remote_refs; ref; ref = ref->next) {
+		if (ref->peer_ref)
+			hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
+		else if (!send_mirror)
+			continue;
+
+		ref->deletion = is_null_sha1(ref->new_sha1);
+		if (!ref->deletion &&
+			!hashcmp(ref->old_sha1, ref->new_sha1)) {
+			ref->status = REF_STATUS_UPTODATE;
+			continue;
+		}
+
+		/* This part determines what can overwrite what.
+		 * The rules are:
+		 *
+		 * (0) you can always use --force or +A:B notation to
+		 *     selectively force individual ref pairs.
+		 *
+		 * (1) if the old thing does not exist, it is OK.
+		 *
+		 * (2) if you do not have the old thing, you are not allowed
+		 *     to overwrite it; you would not know what you are losing
+		 *     otherwise.
+		 *
+		 * (3) if both new and old are commit-ish, and new is a
+		 *     descendant of old, it is OK.
+		 *
+		 * (4) regardless of all of the above, removing :B is
+		 *     always allowed.
+		 */
+
+		ref->nonfastforward =
+			!ref->deletion &&
+			!is_null_sha1(ref->old_sha1) &&
+			(!has_sha1_file(ref->old_sha1)
+			  || !ref_newer(ref->new_sha1, ref->old_sha1));
+
+		if (ref->nonfastforward && !ref->force && !force_update) {
+			ref->status = REF_STATUS_REJECT_NONFASTFORWARD;
+			continue;
+		}
+	}
+}
+
 struct branch *branch_get(const char *name)
 {
 	struct branch *ret;
@@ -1315,8 +1458,8 @@
 
 	for (rmp = &ref_map; *rmp; ) {
 		if ((*rmp)->peer_ref) {
-			int st = check_ref_format((*rmp)->peer_ref->name + 5);
-			if (st && st != CHECK_REF_FORMAT_ONELEVEL) {
+			if (check_refname_format((*rmp)->peer_ref->name + 5,
+				REFNAME_ALLOW_ONELEVEL)) {
 				struct ref *ignore = *rmp;
 				error("* Ignoring funny ref '%s' locally",
 				      (*rmp)->peer_ref->name);
@@ -1420,13 +1563,13 @@
 	 * nothing to report.
 	 */
 	base = branch->merge[0]->dst;
-	if (!resolve_ref(base, sha1, 1, NULL))
+	if (read_ref(base, sha1))
 		return 0;
 	theirs = lookup_commit_reference(sha1);
 	if (!theirs)
 		return 0;
 
-	if (!resolve_ref(branch->refname, sha1, 1, NULL))
+	if (read_ref(branch->refname, sha1))
 		return 0;
 	ours = lookup_commit_reference(sha1);
 	if (!ours)
@@ -1485,19 +1628,29 @@
 	base = branch->merge[0]->dst;
 	base = shorten_unambiguous_ref(base, 0);
 	if (!num_theirs)
-		strbuf_addf(sb, "Your branch is ahead of '%s' "
-			    "by %d commit%s.\n",
-			    base, num_ours, (num_ours == 1) ? "" : "s");
+		strbuf_addf(sb,
+			Q_("Your branch is ahead of '%s' by %d commit.\n",
+			   "Your branch is ahead of '%s' by %d commits.\n",
+			   num_ours),
+			base, num_ours);
 	else if (!num_ours)
-		strbuf_addf(sb, "Your branch is behind '%s' "
-			    "by %d commit%s, "
-			    "and can be fast-forwarded.\n",
-			    base, num_theirs, (num_theirs == 1) ? "" : "s");
+		strbuf_addf(sb,
+			Q_("Your branch is behind '%s' by %d commit, "
+			       "and can be fast-forwarded.\n",
+			   "Your branch is behind '%s' by %d commits, "
+			       "and can be fast-forwarded.\n",
+			   num_theirs),
+			base, num_theirs);
 	else
-		strbuf_addf(sb, "Your branch and '%s' have diverged,\n"
-			    "and have %d and %d different commit(s) each, "
-			    "respectively.\n",
-			    base, num_ours, num_theirs);
+		strbuf_addf(sb,
+			Q_("Your branch and '%s' have diverged,\n"
+			       "and have %d and %d different commit each, "
+			       "respectively.\n",
+			   "Your branch and '%s' have diverged,\n"
+			       "and have %d and %d different commits each, "
+			       "respectively.\n",
+			   num_theirs),
+			base, num_ours, num_theirs);
 	return 1;
 }
 
@@ -1508,7 +1661,7 @@
 	int len;
 
 	/* we already know it starts with refs/ to get here */
-	if (check_ref_format(refname + 5))
+	if (check_refname_format(refname + 5, 0))
 		return 0;
 
 	len = strlen(refname) + 1;
@@ -1555,7 +1708,9 @@
 
 	/* Look for another ref that points there */
 	for (r = refs; r; r = r->next) {
-		if (r != head && !hashcmp(r->old_sha1, head->old_sha1)) {
+		if (r != head &&
+		    !prefixcmp(r->name, "refs/heads/") &&
+		    !hashcmp(r->old_sha1, head->old_sha1)) {
 			*tail = copy_ref(r);
 			tail = &((*tail)->next);
 			if (!all)
@@ -1565,3 +1720,53 @@
 
 	return list;
 }
+
+struct stale_heads_info {
+	struct string_list *ref_names;
+	struct ref **stale_refs_tail;
+	struct refspec *refs;
+	int ref_count;
+};
+
+static int get_stale_heads_cb(const char *refname,
+	const unsigned char *sha1, int flags, void *cb_data)
+{
+	struct stale_heads_info *info = cb_data;
+	struct refspec query;
+	memset(&query, 0, sizeof(struct refspec));
+	query.dst = (char *)refname;
+
+	if (query_refspecs(info->refs, info->ref_count, &query))
+		return 0; /* No matches */
+
+	/*
+	 * If we did find a suitable refspec and it's not a symref and
+	 * it's not in the list of refs that currently exist in that
+	 * remote we consider it to be stale.
+	 */
+	if (!((flags & REF_ISSYMREF) ||
+	      string_list_has_string(info->ref_names, query.src))) {
+		struct ref *ref = make_linked_ref(refname, &info->stale_refs_tail);
+		hashcpy(ref->new_sha1, sha1);
+	}
+
+	free(query.src);
+	return 0;
+}
+
+struct ref *get_stale_heads(struct refspec *refs, int ref_count, struct ref *fetch_map)
+{
+	struct ref *ref, *stale_refs = NULL;
+	struct string_list ref_names = STRING_LIST_INIT_NODUP;
+	struct stale_heads_info info;
+	info.ref_names = &ref_names;
+	info.stale_refs_tail = &stale_refs;
+	info.refs = refs;
+	info.ref_count = ref_count;
+	for (ref = fetch_map; ref; ref = ref->next)
+		string_list_append(&ref_names, ref->name);
+	sort_string_list(&ref_names);
+	for_each_ref(get_stale_heads_cb, &info);
+	string_list_clear(&ref_names, 0);
+	return stale_refs;
+}
diff --git a/remote.h b/remote.h
index 5db8420..251d8fd 100644
--- a/remote.h
+++ b/remote.h
@@ -11,6 +11,8 @@
 	const char *name;
 	int origin;
 
+	const char *foreign_vcs;
+
 	const char **url;
 	int url_nr;
 	int url_alloc;
@@ -68,8 +70,10 @@
 extern const struct refspec *tag_refspec;
 
 struct ref *alloc_ref(const char *name);
-
+struct ref *copy_ref(const struct ref *ref);
 struct ref *copy_ref_list(const struct ref *ref);
+void sort_ref_list(struct ref **, int (*cmp)(const void *, const void *));
+int ref_compare_name(const void *, const void *);
 
 int check_ref_type(const struct ref *ref, int flags);
 
@@ -89,8 +93,15 @@
 int valid_fetch_refspec(const char *refspec);
 struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec);
 
-int match_refs(struct ref *src, struct ref **dst,
-	       int nr_refspec, const char **refspec, int all);
+void free_refspec(int nr_refspec, struct refspec *refspec);
+
+char *apply_refspecs(struct refspec *refspecs, int nr_refspec,
+		     const char *name);
+
+int match_push_refs(struct ref *src, struct ref **dst,
+		    int nr_refspec, const char **refspec, int all);
+void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
+	int force_update);
 
 /*
  * Given a list of the remote refs and the specification of things to
@@ -137,6 +148,7 @@
 	MATCH_REFS_NONE		= 0,
 	MATCH_REFS_ALL 		= (1 << 0),
 	MATCH_REFS_MIRROR	= (1 << 1),
+	MATCH_REFS_PRUNE	= (1 << 2)
 };
 
 /* Reporting of tracking info */
@@ -154,4 +166,7 @@
 			      const struct ref *refs,
 			      int all);
 
+/* Return refs which no longer exist on remote */
+struct ref *get_stale_heads(struct refspec *refs, int ref_count, struct ref *fetch_map);
+
 #endif
diff --git a/replace_object.c b/replace_object.c
new file mode 100644
index 0000000..d0b1548
--- /dev/null
+++ b/replace_object.c
@@ -0,0 +1,117 @@
+#include "cache.h"
+#include "sha1-lookup.h"
+#include "refs.h"
+#include "commit.h"
+
+static struct replace_object {
+	unsigned char sha1[2][20];
+} **replace_object;
+
+static int replace_object_alloc, replace_object_nr;
+
+static const unsigned char *replace_sha1_access(size_t index, void *table)
+{
+	struct replace_object **replace = table;
+	return replace[index]->sha1[0];
+}
+
+static int replace_object_pos(const unsigned char *sha1)
+{
+	return sha1_pos(sha1, replace_object, replace_object_nr,
+			replace_sha1_access);
+}
+
+static int register_replace_object(struct replace_object *replace,
+				   int ignore_dups)
+{
+	int pos = replace_object_pos(replace->sha1[0]);
+
+	if (0 <= pos) {
+		if (ignore_dups)
+			free(replace);
+		else {
+			free(replace_object[pos]);
+			replace_object[pos] = replace;
+		}
+		return 1;
+	}
+	pos = -pos - 1;
+	if (replace_object_alloc <= ++replace_object_nr) {
+		replace_object_alloc = alloc_nr(replace_object_alloc);
+		replace_object = xrealloc(replace_object,
+					  sizeof(*replace_object) *
+					  replace_object_alloc);
+	}
+	if (pos < replace_object_nr)
+		memmove(replace_object + pos + 1,
+			replace_object + pos,
+			(replace_object_nr - pos - 1) *
+			sizeof(*replace_object));
+	replace_object[pos] = replace;
+	return 0;
+}
+
+static int register_replace_ref(const char *refname,
+				const unsigned char *sha1,
+				int flag, void *cb_data)
+{
+	/* Get sha1 from refname */
+	const char *slash = strrchr(refname, '/');
+	const char *hash = slash ? slash + 1 : refname;
+	struct replace_object *repl_obj = xmalloc(sizeof(*repl_obj));
+
+	if (strlen(hash) != 40 || get_sha1_hex(hash, repl_obj->sha1[0])) {
+		free(repl_obj);
+		warning("bad replace ref name: %s", refname);
+		return 0;
+	}
+
+	/* Copy sha1 from the read ref */
+	hashcpy(repl_obj->sha1[1], sha1);
+
+	/* Register new object */
+	if (register_replace_object(repl_obj, 1))
+		die("duplicate replace ref: %s", refname);
+
+	return 0;
+}
+
+static void prepare_replace_object(void)
+{
+	static int replace_object_prepared;
+
+	if (replace_object_prepared)
+		return;
+
+	for_each_replace_ref(register_replace_ref, NULL);
+	replace_object_prepared = 1;
+	if (!replace_object_nr)
+		read_replace_refs = 0;
+}
+
+/* We allow "recursive" replacement. Only within reason, though */
+#define MAXREPLACEDEPTH 5
+
+const unsigned char *do_lookup_replace_object(const unsigned char *sha1)
+{
+	int pos, depth = MAXREPLACEDEPTH;
+	const unsigned char *cur = sha1;
+
+	if (!read_replace_refs)
+		return sha1;
+
+	prepare_replace_object();
+
+	/* Try to recursively replace the object */
+	do {
+		if (--depth < 0)
+			die("replace depth too high for object %s",
+			    sha1_to_hex(sha1));
+
+		pos = replace_object_pos(cur);
+		if (0 <= pos)
+			cur = replace_object[pos]->sha1[1];
+	} while (0 <= pos);
+
+	return cur;
+}
diff --git a/rerere.c b/rerere.c
index 87360dc..b57a34d 100644
--- a/rerere.c
+++ b/rerere.c
@@ -1,8 +1,16 @@
 #include "cache.h"
 #include "string-list.h"
 #include "rerere.h"
-#include "xdiff/xdiff.h"
 #include "xdiff-interface.h"
+#include "dir.h"
+#include "resolve-undo.h"
+#include "ll-merge.h"
+#include "attr.h"
+
+#define RESOLVED 0
+#define PUNTED 1
+#define THREE_STAGED 2
+void *RERERE_RESOLVED = &RERERE_RESOLVED;
 
 /* if rerere_enabled == -1, fall back to detection of .git/rr-cache */
 static int rerere_enabled = -1;
@@ -39,11 +47,17 @@
 		name = xstrdup(buf);
 		if (fgetc(in) != '\t')
 			die("corrupt MERGE_RR");
-		for (i = 0; i < sizeof(buf) && (buf[i] = fgetc(in)); i++)
-			; /* do nothing */
+		for (i = 0; i < sizeof(buf); i++) {
+			int c = fgetc(in);
+			if (c < 0)
+				die("corrupt MERGE_RR");
+			buf[i] = c;
+			if (c == 0)
+				 break;
+		}
 		if (i == sizeof(buf))
 			die("filename too long");
-		string_list_insert(buf, rr)->util = name;
+		string_list_insert(rr, buf)->util = name;
 	}
 	fclose(in);
 }
@@ -61,7 +75,7 @@
 		path = rr->items[i].string;
 		length = strlen(path) + 1;
 		if (write_in_full(out_fd, rr->items[i].util, 40) != 40 ||
-		    write_in_full(out_fd, "\t", 1) != 1 ||
+		    write_str_in_full(out_fd, "\t") != 1 ||
 		    write_in_full(out_fd, path, length) != length)
 			die("unable to write rerere record");
 	}
@@ -83,61 +97,106 @@
 	ferr_write(s, strlen(s), fp, err);
 }
 
-static int handle_file(const char *path,
-	 unsigned char *sha1, const char *output)
+struct rerere_io {
+	int (*getline)(struct strbuf *, struct rerere_io *);
+	FILE *output;
+	int wrerror;
+	/* some more stuff */
+};
+
+static void rerere_io_putstr(const char *str, struct rerere_io *io)
+{
+	if (io->output)
+		ferr_puts(str, io->output, &io->wrerror);
+}
+
+static void rerere_io_putconflict(int ch, int size, struct rerere_io *io)
+{
+	char buf[64];
+
+	while (size) {
+		if (size < sizeof(buf) - 2) {
+			memset(buf, ch, size);
+			buf[size] = '\n';
+			buf[size + 1] = '\0';
+			size = 0;
+		} else {
+			int sz = sizeof(buf) - 1;
+			if (size <= sz)
+				sz -= (sz - size) + 1;
+			memset(buf, ch, sz);
+			buf[sz] = '\0';
+			size -= sz;
+		}
+		rerere_io_putstr(buf, io);
+	}
+}
+
+static void rerere_io_putmem(const char *mem, size_t sz, struct rerere_io *io)
+{
+	if (io->output)
+		ferr_write(mem, sz, io->output, &io->wrerror);
+}
+
+struct rerere_io_file {
+	struct rerere_io io;
+	FILE *input;
+};
+
+static int rerere_file_getline(struct strbuf *sb, struct rerere_io *io_)
+{
+	struct rerere_io_file *io = (struct rerere_io_file *)io_;
+	return strbuf_getwholeline(sb, io->input, '\n');
+}
+
+static int is_cmarker(char *buf, int marker_char, int marker_size, int want_sp)
+{
+	while (marker_size--)
+		if (*buf++ != marker_char)
+			return 0;
+	if (want_sp && *buf != ' ')
+		return 0;
+	return isspace(*buf);
+}
+
+static int handle_path(unsigned char *sha1, struct rerere_io *io, int marker_size)
 {
 	git_SHA_CTX ctx;
-	char buf[1024];
 	int hunk_no = 0;
 	enum {
-		RR_CONTEXT = 0, RR_SIDE_1, RR_SIDE_2, RR_ORIGINAL,
+		RR_CONTEXT = 0, RR_SIDE_1, RR_SIDE_2, RR_ORIGINAL
 	} hunk = RR_CONTEXT;
 	struct strbuf one = STRBUF_INIT, two = STRBUF_INIT;
-	FILE *f = fopen(path, "r");
-	FILE *out = NULL;
-	int wrerror = 0;
-
-	if (!f)
-		return error("Could not open %s", path);
-
-	if (output) {
-		out = fopen(output, "w");
-		if (!out) {
-			fclose(f);
-			return error("Could not write %s", output);
-		}
-	}
+	struct strbuf buf = STRBUF_INIT;
 
 	if (sha1)
 		git_SHA1_Init(&ctx);
 
-	while (fgets(buf, sizeof(buf), f)) {
-		if (!prefixcmp(buf, "<<<<<<< ")) {
+	while (!io->getline(&buf, io)) {
+		if (is_cmarker(buf.buf, '<', marker_size, 1)) {
 			if (hunk != RR_CONTEXT)
 				goto bad;
 			hunk = RR_SIDE_1;
-		} else if (!prefixcmp(buf, "|||||||") && isspace(buf[7])) {
+		} else if (is_cmarker(buf.buf, '|', marker_size, 0)) {
 			if (hunk != RR_SIDE_1)
 				goto bad;
 			hunk = RR_ORIGINAL;
-		} else if (!prefixcmp(buf, "=======") && isspace(buf[7])) {
+		} else if (is_cmarker(buf.buf, '=', marker_size, 0)) {
 			if (hunk != RR_SIDE_1 && hunk != RR_ORIGINAL)
 				goto bad;
 			hunk = RR_SIDE_2;
-		} else if (!prefixcmp(buf, ">>>>>>> ")) {
+		} else if (is_cmarker(buf.buf, '>', marker_size, 1)) {
 			if (hunk != RR_SIDE_2)
 				goto bad;
 			if (strbuf_cmp(&one, &two) > 0)
 				strbuf_swap(&one, &two);
 			hunk_no++;
 			hunk = RR_CONTEXT;
-			if (out) {
-				ferr_puts("<<<<<<<\n", out, &wrerror);
-				ferr_write(one.buf, one.len, out, &wrerror);
-				ferr_puts("=======\n", out, &wrerror);
-				ferr_write(two.buf, two.len, out, &wrerror);
-				ferr_puts(">>>>>>>\n", out, &wrerror);
-			}
+			rerere_io_putconflict('<', marker_size, io);
+			rerere_io_putmem(one.buf, one.len, io);
+			rerere_io_putconflict('=', marker_size, io);
+			rerere_io_putmem(two.buf, two.len, io);
+			rerere_io_putconflict('>', marker_size, io);
 			if (sha1) {
 				git_SHA1_Update(&ctx, one.buf ? one.buf : "",
 					    one.len + 1);
@@ -147,13 +206,13 @@
 			strbuf_reset(&one);
 			strbuf_reset(&two);
 		} else if (hunk == RR_SIDE_1)
-			strbuf_addstr(&one, buf);
+			strbuf_addstr(&one, buf.buf);
 		else if (hunk == RR_ORIGINAL)
 			; /* discard */
 		else if (hunk == RR_SIDE_2)
-			strbuf_addstr(&two, buf);
-		else if (out)
-			ferr_puts(buf, out, &wrerror);
+			strbuf_addstr(&two, buf.buf);
+		else
+			rerere_io_putstr(buf.buf, io);
 		continue;
 	bad:
 		hunk = 99; /* force error exit */
@@ -161,41 +220,210 @@
 	}
 	strbuf_release(&one);
 	strbuf_release(&two);
+	strbuf_release(&buf);
 
-	fclose(f);
-	if (wrerror)
-		error("There were errors while writing %s (%s)",
-		      path, strerror(wrerror));
-	if (out && fclose(out))
-		wrerror = error("Failed to flush %s: %s",
-				path, strerror(errno));
 	if (sha1)
 		git_SHA1_Final(sha1, &ctx);
-	if (hunk != RR_CONTEXT) {
+	if (hunk != RR_CONTEXT)
+		return -1;
+	return hunk_no;
+}
+
+static int handle_file(const char *path, unsigned char *sha1, const char *output)
+{
+	int hunk_no = 0;
+	struct rerere_io_file io;
+	int marker_size = ll_merge_marker_size(path);
+
+	memset(&io, 0, sizeof(io));
+	io.io.getline = rerere_file_getline;
+	io.input = fopen(path, "r");
+	io.io.wrerror = 0;
+	if (!io.input)
+		return error("Could not open %s", path);
+
+	if (output) {
+		io.io.output = fopen(output, "w");
+		if (!io.io.output) {
+			fclose(io.input);
+			return error("Could not write %s", output);
+		}
+	}
+
+	hunk_no = handle_path(sha1, (struct rerere_io *)&io, marker_size);
+
+	fclose(io.input);
+	if (io.io.wrerror)
+		error("There were errors while writing %s (%s)",
+		      path, strerror(io.io.wrerror));
+	if (io.io.output && fclose(io.io.output))
+		io.io.wrerror = error("Failed to flush %s: %s",
+				      path, strerror(errno));
+
+	if (hunk_no < 0) {
 		if (output)
 			unlink_or_warn(output);
 		return error("Could not parse conflict hunks in %s", path);
 	}
-	if (wrerror)
+	if (io.io.wrerror)
 		return -1;
 	return hunk_no;
 }
 
+struct rerere_io_mem {
+	struct rerere_io io;
+	struct strbuf input;
+};
+
+static int rerere_mem_getline(struct strbuf *sb, struct rerere_io *io_)
+{
+	struct rerere_io_mem *io = (struct rerere_io_mem *)io_;
+	char *ep;
+	size_t len;
+
+	strbuf_release(sb);
+	if (!io->input.len)
+		return -1;
+	ep = strchrnul(io->input.buf, '\n');
+	if (*ep == '\n')
+		ep++;
+	len = ep - io->input.buf;
+	strbuf_add(sb, io->input.buf, len);
+	strbuf_remove(&io->input, 0, len);
+	return 0;
+}
+
+static int handle_cache(const char *path, unsigned char *sha1, const char *output)
+{
+	mmfile_t mmfile[3];
+	mmbuffer_t result = {NULL, 0};
+	struct cache_entry *ce;
+	int pos, len, i, hunk_no;
+	struct rerere_io_mem io;
+	int marker_size = ll_merge_marker_size(path);
+
+	/*
+	 * Reproduce the conflicted merge in-core
+	 */
+	len = strlen(path);
+	pos = cache_name_pos(path, len);
+	if (0 <= pos)
+		return -1;
+	pos = -pos - 1;
+
+	for (i = 0; i < 3; i++) {
+		enum object_type type;
+		unsigned long size;
+
+		mmfile[i].size = 0;
+		mmfile[i].ptr = NULL;
+		if (active_nr <= pos)
+			break;
+		ce = active_cache[pos++];
+		if (ce_namelen(ce) != len || memcmp(ce->name, path, len)
+		    || ce_stage(ce) != i + 1)
+			break;
+		mmfile[i].ptr = read_sha1_file(ce->sha1, &type, &size);
+		mmfile[i].size = size;
+	}
+	for (i = 0; i < 3; i++) {
+		if (!mmfile[i].ptr && !mmfile[i].size)
+			mmfile[i].ptr = xstrdup("");
+	}
+	/*
+	 * NEEDSWORK: handle conflicts from merges with
+	 * merge.renormalize set, too
+	 */
+	ll_merge(&result, path, &mmfile[0], NULL,
+		 &mmfile[1], "ours",
+		 &mmfile[2], "theirs", NULL);
+	for (i = 0; i < 3; i++)
+		free(mmfile[i].ptr);
+
+	memset(&io, 0, sizeof(io));
+	io.io.getline = rerere_mem_getline;
+	if (output)
+		io.io.output = fopen(output, "w");
+	else
+		io.io.output = NULL;
+	strbuf_init(&io.input, 0);
+	strbuf_attach(&io.input, result.ptr, result.size, result.size);
+
+	hunk_no = handle_path(sha1, (struct rerere_io *)&io, marker_size);
+	strbuf_release(&io.input);
+	if (io.io.output)
+		fclose(io.io.output);
+	return hunk_no;
+}
+
+static int check_one_conflict(int i, int *type)
+{
+	struct cache_entry *e = active_cache[i];
+
+	if (!ce_stage(e)) {
+		*type = RESOLVED;
+		return i + 1;
+	}
+
+	*type = PUNTED;
+	if (ce_stage(e) == 1) {
+		if (active_nr <= ++i)
+			return i + 1;
+	}
+
+	/* Only handle regular files with both stages #2 and #3 */
+	if (i + 1 < active_nr) {
+		struct cache_entry *e2 = active_cache[i];
+		struct cache_entry *e3 = active_cache[i + 1];
+		if (ce_stage(e2) == 2 &&
+		    ce_stage(e3) == 3 &&
+		    ce_same_name(e, e3) &&
+		    S_ISREG(e2->ce_mode) &&
+		    S_ISREG(e3->ce_mode))
+			*type = THREE_STAGED;
+	}
+
+	/* Skip the entries with the same name */
+	while (i < active_nr && ce_same_name(e, active_cache[i]))
+		i++;
+	return i;
+}
+
 static int find_conflict(struct string_list *conflict)
 {
 	int i;
 	if (read_cache() < 0)
 		return error("Could not read index");
-	for (i = 0; i+1 < active_nr; i++) {
-		struct cache_entry *e2 = active_cache[i];
-		struct cache_entry *e3 = active_cache[i+1];
-		if (ce_stage(e2) == 2 &&
-		    ce_stage(e3) == 3 &&
-		    ce_same_name(e2, e3) &&
-		    S_ISREG(e2->ce_mode) &&
-		    S_ISREG(e3->ce_mode)) {
-			string_list_insert((const char *)e2->name, conflict);
-			i++; /* skip over both #2 and #3 */
+
+	for (i = 0; i < active_nr;) {
+		int conflict_type;
+		struct cache_entry *e = active_cache[i];
+		i = check_one_conflict(i, &conflict_type);
+		if (conflict_type == THREE_STAGED)
+			string_list_insert(conflict, (const char *)e->name);
+	}
+	return 0;
+}
+
+int rerere_remaining(struct string_list *merge_rr)
+{
+	int i;
+	if (read_cache() < 0)
+		return error("Could not read index");
+
+	for (i = 0; i < active_nr;) {
+		int conflict_type;
+		struct cache_entry *e = active_cache[i];
+		i = check_one_conflict(i, &conflict_type);
+		if (conflict_type == PUNTED)
+			string_list_insert(merge_rr, (const char *)e->name);
+		else if (conflict_type == RESOLVED) {
+			struct string_list_item *it;
+			it = string_list_lookup(merge_rr, (const char *)e->name);
+			if (it != NULL) {
+				free(it->util);
+				it->util = RERERE_RESOLVED;
+			}
 		}
 	}
 	return 0;
@@ -204,21 +432,27 @@
 static int merge(const char *name, const char *path)
 {
 	int ret;
-	mmfile_t cur, base, other;
+	mmfile_t cur = {NULL, 0}, base = {NULL, 0}, other = {NULL, 0};
 	mmbuffer_t result = {NULL, 0};
-	xpparam_t xpp = {XDF_NEED_MINIMAL};
 
 	if (handle_file(path, NULL, rerere_path(name, "thisimage")) < 0)
 		return 1;
 
 	if (read_mmfile(&cur, rerere_path(name, "thisimage")) ||
 			read_mmfile(&base, rerere_path(name, "preimage")) ||
-			read_mmfile(&other, rerere_path(name, "postimage")))
-		return 1;
-	ret = xdl_merge(&base, &cur, "", &other, "",
-			&xpp, XDL_MERGE_ZEALOUS, &result);
+			read_mmfile(&other, rerere_path(name, "postimage"))) {
+		ret = 1;
+		goto out;
+	}
+	ret = ll_merge(&result, path, &base, NULL, &cur, "", &other, "", NULL);
 	if (!ret) {
-		FILE *f = fopen(path, "w");
+		FILE *f;
+
+		if (utime(rerere_path(name, "postimage"), NULL) < 0)
+			warning("failed utime() on %s: %s",
+					rerere_path(name, "postimage"),
+					strerror(errno));
+		f = fopen(path, "w");
 		if (!f)
 			return error("Could not open %s: %s", path,
 				     strerror(errno));
@@ -229,6 +463,7 @@
 				     strerror(errno));
 	}
 
+out:
 	free(cur.ptr);
 	free(base.ptr);
 	free(other.ptr);
@@ -265,8 +500,8 @@
 
 static int do_plain_rerere(struct string_list *rr, int fd)
 {
-	struct string_list conflict = { NULL, 0, 0, 1 };
-	struct string_list update = { NULL, 0, 0, 1 };
+	struct string_list conflict = STRING_LIST_INIT_DUP;
+	struct string_list update = STRING_LIST_INIT_DUP;
 	int i;
 
 	find_conflict(&conflict);
@@ -288,8 +523,8 @@
 			if (ret < 1)
 				continue;
 			hex = xstrdup(sha1_to_hex(sha1));
-			string_list_insert(path, rr)->util = hex;
-			if (mkdir(git_path("rr-cache/%s", hex), 0755))
+			string_list_insert(rr, path)->util = hex;
+			if (mkdir_in_gitdir(git_path("rr-cache/%s", hex)))
 				continue;
 			handle_file(path, NULL, rerere_path(hex, "preimage"));
 			fprintf(stderr, "Recorded preimage for '%s'\n", path);
@@ -309,13 +544,13 @@
 
 		if (has_rerere_resolution(name)) {
 			if (!merge(name, path)) {
-				if (rerere_autoupdate)
-					string_list_insert(path, &update);
-				fprintf(stderr,
-					"%s '%s' using previous resolution.\n",
-					rerere_autoupdate
-					? "Staged" : "Resolved",
-					path);
+				const char *msg;
+				if (rerere_autoupdate) {
+					string_list_insert(&update, path);
+					msg = "Staged '%s' using previous resolution.\n";
+				} else
+					msg = "Resolved '%s' using previous resolution.\n";
+				fprintf(stderr, msg, path);
 				goto mark_resolved;
 			}
 		}
@@ -361,13 +596,12 @@
 	if (rerere_enabled < 0)
 		return rr_cache_exists;
 
-	if (!rr_cache_exists &&
-	    (mkdir(rr_cache, 0777) || adjust_shared_perm(rr_cache)))
+	if (!rr_cache_exists && mkdir_in_gitdir(rr_cache))
 		die("Could not create directory %s", rr_cache);
 	return 1;
 }
 
-int setup_rerere(struct string_list *merge_rr)
+int setup_rerere(struct string_list *merge_rr, int flags)
 {
 	int fd;
 
@@ -375,6 +609,8 @@
 	if (!is_rerere_enabled())
 		return -1;
 
+	if (flags & (RERERE_AUTOUPDATE|RERERE_NOAUTOUPDATE))
+		rerere_autoupdate = !!(flags & RERERE_AUTOUPDATE);
 	merge_rr_path = git_pathdup("MERGE_RR");
 	fd = hold_lock_file_for_update(&write_lock, merge_rr_path,
 				       LOCK_DIE_ON_ERROR);
@@ -382,13 +618,147 @@
 	return fd;
 }
 
-int rerere(void)
+int rerere(int flags)
 {
-	struct string_list merge_rr = { NULL, 0, 0, 1 };
+	struct string_list merge_rr = STRING_LIST_INIT_DUP;
 	int fd;
 
-	fd = setup_rerere(&merge_rr);
+	fd = setup_rerere(&merge_rr, flags);
 	if (fd < 0)
 		return 0;
 	return do_plain_rerere(&merge_rr, fd);
 }
+
+static int rerere_forget_one_path(const char *path, struct string_list *rr)
+{
+	const char *filename;
+	char *hex;
+	unsigned char sha1[20];
+	int ret;
+
+	ret = handle_cache(path, sha1, NULL);
+	if (ret < 1)
+		return error("Could not parse conflict hunks in '%s'", path);
+	hex = xstrdup(sha1_to_hex(sha1));
+	filename = rerere_path(hex, "postimage");
+	if (unlink(filename))
+		return (errno == ENOENT
+			? error("no remembered resolution for %s", path)
+			: error("cannot unlink %s: %s", filename, strerror(errno)));
+
+	handle_cache(path, sha1, rerere_path(hex, "preimage"));
+	fprintf(stderr, "Updated preimage for '%s'\n", path);
+
+
+	string_list_insert(rr, path)->util = hex;
+	fprintf(stderr, "Forgot resolution for %s\n", path);
+	return 0;
+}
+
+int rerere_forget(const char **pathspec)
+{
+	int i, fd;
+	struct string_list conflict = STRING_LIST_INIT_DUP;
+	struct string_list merge_rr = STRING_LIST_INIT_DUP;
+
+	if (read_cache() < 0)
+		return error("Could not read index");
+
+	fd = setup_rerere(&merge_rr, RERERE_NOAUTOUPDATE);
+
+	unmerge_cache(pathspec);
+	find_conflict(&conflict);
+	for (i = 0; i < conflict.nr; i++) {
+		struct string_list_item *it = &conflict.items[i];
+		if (!match_pathspec(pathspec, it->string, strlen(it->string),
+				    0, NULL))
+			continue;
+		rerere_forget_one_path(it->string, &merge_rr);
+	}
+	return write_rr(&merge_rr, fd);
+}
+
+static time_t rerere_created_at(const char *name)
+{
+	struct stat st;
+	return stat(rerere_path(name, "preimage"), &st) ? (time_t) 0 : st.st_mtime;
+}
+
+static time_t rerere_last_used_at(const char *name)
+{
+	struct stat st;
+	return stat(rerere_path(name, "postimage"), &st) ? (time_t) 0 : st.st_mtime;
+}
+
+static void unlink_rr_item(const char *name)
+{
+	unlink(rerere_path(name, "thisimage"));
+	unlink(rerere_path(name, "preimage"));
+	unlink(rerere_path(name, "postimage"));
+	rmdir(git_path("rr-cache/%s", name));
+}
+
+struct rerere_gc_config_cb {
+	int cutoff_noresolve;
+	int cutoff_resolve;
+};
+
+static int git_rerere_gc_config(const char *var, const char *value, void *cb)
+{
+	struct rerere_gc_config_cb *cf = cb;
+
+	if (!strcmp(var, "gc.rerereresolved"))
+		cf->cutoff_resolve = git_config_int(var, value);
+	else if (!strcmp(var, "gc.rerereunresolved"))
+		cf->cutoff_noresolve = git_config_int(var, value);
+	else
+		return git_default_config(var, value, cb);
+	return 0;
+}
+
+void rerere_gc(struct string_list *rr)
+{
+	struct string_list to_remove = STRING_LIST_INIT_DUP;
+	DIR *dir;
+	struct dirent *e;
+	int i, cutoff;
+	time_t now = time(NULL), then;
+	struct rerere_gc_config_cb cf = { 15, 60 };
+
+	git_config(git_rerere_gc_config, &cf);
+	dir = opendir(git_path("rr-cache"));
+	if (!dir)
+		die_errno("unable to open rr-cache directory");
+	while ((e = readdir(dir))) {
+		if (is_dot_or_dotdot(e->d_name))
+			continue;
+
+		then = rerere_last_used_at(e->d_name);
+		if (then) {
+			cutoff = cf.cutoff_resolve;
+		} else {
+			then = rerere_created_at(e->d_name);
+			if (!then)
+				continue;
+			cutoff = cf.cutoff_noresolve;
+		}
+		if (then < now - cutoff * 86400)
+			string_list_append(&to_remove, e->d_name);
+	}
+	closedir(dir);
+	for (i = 0; i < to_remove.nr; i++)
+		unlink_rr_item(to_remove.items[i].string);
+	string_list_clear(&to_remove, 0);
+}
+
+void rerere_clear(struct string_list *merge_rr)
+{
+	int i;
+
+	for (i = 0; i < merge_rr->nr; i++) {
+		const char *name = (const char *)merge_rr->items[i].util;
+		if (!has_rerere_resolution(name))
+			unlink_rr_item(name);
+	}
+	unlink_or_warn(git_path("MERGE_RR"));
+}
diff --git a/rerere.h b/rerere.h
index 13313f3..fcd8bc1 100644
--- a/rerere.h
+++ b/rerere.h
@@ -3,9 +3,26 @@
 
 #include "string-list.h"
 
-extern int setup_rerere(struct string_list *);
-extern int rerere(void);
+#define RERERE_AUTOUPDATE   01
+#define RERERE_NOAUTOUPDATE 02
+
+/*
+ * Marks paths that have been hand-resolved and added to the
+ * index. Set in the util field of such paths after calling
+ * rerere_remaining.
+ */
+extern void *RERERE_RESOLVED;
+
+extern int setup_rerere(struct string_list *, int);
+extern int rerere(int);
 extern const char *rerere_path(const char *hex, const char *file);
 extern int has_rerere_resolution(const char *hex);
+extern int rerere_forget(const char **);
+extern int rerere_remaining(struct string_list *);
+extern void rerere_clear(struct string_list *);
+extern void rerere_gc(struct string_list *);
+
+#define OPT_RERERE_AUTOUPDATE(v) OPT_UYN(0, "rerere-autoupdate", (v), \
+	"update the index with reused conflict resolution if possible")
 
 #endif
diff --git a/resolve-undo.c b/resolve-undo.c
new file mode 100644
index 0000000..72b4612
--- /dev/null
+++ b/resolve-undo.c
@@ -0,0 +1,172 @@
+#include "cache.h"
+#include "dir.h"
+#include "resolve-undo.h"
+#include "string-list.h"
+
+/* The only error case is to run out of memory in string-list */
+void record_resolve_undo(struct index_state *istate, struct cache_entry *ce)
+{
+	struct string_list_item *lost;
+	struct resolve_undo_info *ui;
+	struct string_list *resolve_undo;
+	int stage = ce_stage(ce);
+
+	if (!stage)
+		return;
+
+	if (!istate->resolve_undo) {
+		resolve_undo = xcalloc(1, sizeof(*resolve_undo));
+		resolve_undo->strdup_strings = 1;
+		istate->resolve_undo = resolve_undo;
+	}
+	resolve_undo = istate->resolve_undo;
+	lost = string_list_insert(resolve_undo, ce->name);
+	if (!lost->util)
+		lost->util = xcalloc(1, sizeof(*ui));
+	ui = lost->util;
+	hashcpy(ui->sha1[stage - 1], ce->sha1);
+	ui->mode[stage - 1] = ce->ce_mode;
+}
+
+void resolve_undo_write(struct strbuf *sb, struct string_list *resolve_undo)
+{
+	struct string_list_item *item;
+	for_each_string_list_item(item, resolve_undo) {
+		struct resolve_undo_info *ui = item->util;
+		int i;
+
+		if (!ui)
+			continue;
+		strbuf_addstr(sb, item->string);
+		strbuf_addch(sb, 0);
+		for (i = 0; i < 3; i++)
+			strbuf_addf(sb, "%o%c", ui->mode[i], 0);
+		for (i = 0; i < 3; i++) {
+			if (!ui->mode[i])
+				continue;
+			strbuf_add(sb, ui->sha1[i], 20);
+		}
+	}
+}
+
+struct string_list *resolve_undo_read(const char *data, unsigned long size)
+{
+	struct string_list *resolve_undo;
+	size_t len;
+	char *endptr;
+	int i;
+
+	resolve_undo = xcalloc(1, sizeof(*resolve_undo));
+	resolve_undo->strdup_strings = 1;
+
+	while (size) {
+		struct string_list_item *lost;
+		struct resolve_undo_info *ui;
+
+		len = strlen(data) + 1;
+		if (size <= len)
+			goto error;
+		lost = string_list_insert(resolve_undo, data);
+		if (!lost->util)
+			lost->util = xcalloc(1, sizeof(*ui));
+		ui = lost->util;
+		size -= len;
+		data += len;
+
+		for (i = 0; i < 3; i++) {
+			ui->mode[i] = strtoul(data, &endptr, 8);
+			if (!endptr || endptr == data || *endptr)
+				goto error;
+			len = (endptr + 1) - (char*)data;
+			if (size <= len)
+				goto error;
+			size -= len;
+			data += len;
+		}
+
+		for (i = 0; i < 3; i++) {
+			if (!ui->mode[i])
+				continue;
+			if (size < 20)
+				goto error;
+			hashcpy(ui->sha1[i], (const unsigned char *)data);
+			size -= 20;
+			data += 20;
+		}
+	}
+	return resolve_undo;
+
+error:
+	string_list_clear(resolve_undo, 1);
+	error("Index records invalid resolve-undo information");
+	return NULL;
+}
+
+void resolve_undo_clear_index(struct index_state *istate)
+{
+	struct string_list *resolve_undo = istate->resolve_undo;
+	if (!resolve_undo)
+		return;
+	string_list_clear(resolve_undo, 1);
+	free(resolve_undo);
+	istate->resolve_undo = NULL;
+	istate->cache_changed = 1;
+}
+
+int unmerge_index_entry_at(struct index_state *istate, int pos)
+{
+	struct cache_entry *ce;
+	struct string_list_item *item;
+	struct resolve_undo_info *ru;
+	int i, err = 0;
+
+	if (!istate->resolve_undo)
+		return pos;
+
+	ce = istate->cache[pos];
+	if (ce_stage(ce)) {
+		/* already unmerged */
+		while ((pos < istate->cache_nr) &&
+		       ! strcmp(istate->cache[pos]->name, ce->name))
+			pos++;
+		return pos - 1; /* return the last entry processed */
+	}
+	item = string_list_lookup(istate->resolve_undo, ce->name);
+	if (!item)
+		return pos;
+	ru = item->util;
+	if (!ru)
+		return pos;
+	remove_index_entry_at(istate, pos);
+	for (i = 0; i < 3; i++) {
+		struct cache_entry *nce;
+		if (!ru->mode[i])
+			continue;
+		nce = make_cache_entry(ru->mode[i], ru->sha1[i],
+				       ce->name, i + 1, 0);
+		if (add_index_entry(istate, nce, ADD_CACHE_OK_TO_ADD)) {
+			err = 1;
+			error("cannot unmerge '%s'", ce->name);
+		}
+	}
+	if (err)
+		return pos;
+	free(ru);
+	item->util = NULL;
+	return unmerge_index_entry_at(istate, pos);
+}
+
+void unmerge_index(struct index_state *istate, const char **pathspec)
+{
+	int i;
+
+	if (!istate->resolve_undo)
+		return;
+
+	for (i = 0; i < istate->cache_nr; i++) {
+		struct cache_entry *ce = istate->cache[i];
+		if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL))
+			continue;
+		i = unmerge_index_entry_at(istate, i);
+	}
+}
diff --git a/resolve-undo.h b/resolve-undo.h
new file mode 100644
index 0000000..8458769
--- /dev/null
+++ b/resolve-undo.h
@@ -0,0 +1,16 @@
+#ifndef RESOLVE_UNDO_H
+#define RESOLVE_UNDO_H
+
+struct resolve_undo_info {
+	unsigned int mode[3];
+	unsigned char sha1[3][20];
+};
+
+extern void record_resolve_undo(struct index_state *, struct cache_entry *);
+extern void resolve_undo_write(struct strbuf *, struct string_list *);
+extern struct string_list *resolve_undo_read(const char *, unsigned long);
+extern void resolve_undo_clear_index(struct index_state *);
+extern int unmerge_index_entry_at(struct index_state *, int);
+extern void unmerge_index(struct index_state *, const char **);
+
+#endif
diff --git a/revision.c b/revision.c
index 9f5dac5..9e8f47a 100644
--- a/revision.c
+++ b/revision.c
@@ -12,6 +12,7 @@
 #include "patch-ids.h"
 #include "decorate.h"
 #include "log-tree.h"
+#include "string-list.h"
 
 volatile show_early_output_fn_t show_early_output;
 
@@ -39,6 +40,47 @@
 	return n;
 }
 
+static int show_path_component_truncated(FILE *out, const char *name, int len)
+{
+	int cnt;
+	for (cnt = 0; cnt < len; cnt++) {
+		int ch = name[cnt];
+		if (!ch || ch == '\n')
+			return -1;
+		fputc(ch, out);
+	}
+	return len;
+}
+
+static int show_path_truncated(FILE *out, const struct name_path *path)
+{
+	int emitted, ours;
+
+	if (!path)
+		return 0;
+	emitted = show_path_truncated(out, path->up);
+	if (emitted < 0)
+		return emitted;
+	if (emitted)
+		fputc('/', out);
+	ours = show_path_component_truncated(out, path->elem, path->elem_len);
+	if (ours < 0)
+		return ours;
+	return ours || emitted;
+}
+
+void show_object_with_name(FILE *out, struct object *obj, const struct name_path *path, const char *component)
+{
+	struct name_path leaf;
+	leaf.up = (struct name_path *)path;
+	leaf.elem = component;
+	leaf.elem_len = strlen(component);
+
+	fprintf(out, "%s ", sha1_to_hex(obj->sha1));
+	show_path_truncated(out, &leaf);
+	fputc('\n', out);
+}
+
 void add_object(struct object *obj,
 		struct object_array *p,
 		struct name_path *path,
@@ -97,11 +139,32 @@
 
 void mark_parents_uninteresting(struct commit *commit)
 {
-	struct commit_list *parents = commit->parents;
+	struct commit_list *parents = NULL, *l;
+
+	for (l = commit->parents; l; l = l->next)
+		commit_list_insert(l->item, &parents);
 
 	while (parents) {
 		struct commit *commit = parents->item;
-		if (!(commit->object.flags & UNINTERESTING)) {
+		l = parents;
+		parents = parents->next;
+		free(l);
+
+		while (commit) {
+			/*
+			 * A missing commit is ok iff its parent is marked
+			 * uninteresting.
+			 *
+			 * We just mark such a thing parsed, so that when
+			 * it is popped next time around, we won't be trying
+			 * to parse it and get an error.
+			 */
+			if (!has_sha1_file(commit->object.sha1))
+				commit->object.parsed = 1;
+
+			if (commit->object.flags & UNINTERESTING)
+				break;
+
 			commit->object.flags |= UNINTERESTING;
 
 			/*
@@ -112,32 +175,36 @@
 			 * wasn't uninteresting), in which case we need
 			 * to mark its parents recursively too..
 			 */
-			if (commit->parents)
-				mark_parents_uninteresting(commit);
-		}
+			if (!commit->parents)
+				break;
 
-		/*
-		 * A missing commit is ok iff its parent is marked
-		 * uninteresting.
-		 *
-		 * We just mark such a thing parsed, so that when
-		 * it is popped next time around, we won't be trying
-		 * to parse it and get an error.
-		 */
-		if (!has_sha1_file(commit->object.sha1))
-			commit->object.parsed = 1;
-		parents = parents->next;
+			for (l = commit->parents->next; l; l = l->next)
+				commit_list_insert(l->item, &parents);
+			commit = commit->parents->item;
+		}
 	}
 }
 
 static void add_pending_object_with_mode(struct rev_info *revs, struct object *obj, const char *name, unsigned mode)
 {
+	if (!obj)
+		return;
 	if (revs->no_walk && (obj->flags & UNINTERESTING))
 		revs->no_walk = 0;
-	if (revs->reflog_info && obj->type == OBJ_COMMIT &&
-			add_reflog_for_walk(revs->reflog_info,
-				(struct commit *)obj, name))
-		return;
+	if (revs->reflog_info && obj->type == OBJ_COMMIT) {
+		struct strbuf buf = STRBUF_INIT;
+		int len = interpret_branch_name(name, &buf);
+		int st;
+
+		if (0 < len && name[len] && buf.len)
+			strbuf_addstr(&buf, name + len);
+		st = add_reflog_for_walk(revs->reflog_info,
+					 (struct commit *)obj,
+					 buf.buf[0] ? buf.buf: name);
+		strbuf_release(&buf);
+		if (st)
+			return;
+	}
 	add_object_array_with_mode(obj, name, &revs->pending, mode);
 }
 
@@ -163,12 +230,22 @@
 	struct object *object;
 
 	object = parse_object(sha1);
-	if (!object)
+	if (!object) {
+		if (revs->ignore_missing)
+			return object;
 		die("bad object %s", name);
+	}
 	object->flags |= flags;
 	return object;
 }
 
+void add_pending_sha1(struct rev_info *revs, const char *name,
+		      const unsigned char *sha1, unsigned int flags)
+{
+	struct object *object = get_reference(revs, name, sha1, flags);
+	add_pending_object(revs, object, name);
+}
+
 static struct commit *handle_commit(struct rev_info *revs, struct object *object, const char *name)
 {
 	unsigned long flags = object->flags;
@@ -268,7 +345,7 @@
 static void file_add_remove(struct diff_options *options,
 		    int addremove, unsigned mode,
 		    const unsigned char *sha1,
-		    const char *fullpath)
+		    const char *fullpath, unsigned dirty_submodule)
 {
 	int diff = addremove == '+' ? REV_TREE_NEW : REV_TREE_OLD;
 
@@ -281,7 +358,8 @@
 		 unsigned old_mode, unsigned new_mode,
 		 const unsigned char *old_sha1,
 		 const unsigned char *new_sha1,
-		 const char *fullpath)
+		 const char *fullpath,
+		 unsigned old_dirty_submodule, unsigned new_dirty_submodule)
 {
 	tree_difference = REV_TREE_DIFFERENT;
 	DIFF_OPT_SET(options, HAS_CHANGES);
@@ -311,7 +389,7 @@
 		 * tagged commit by specifying both --simplify-by-decoration
 		 * and pathspec.
 		 */
-		if (!revs->prune_data)
+		if (!revs->prune_data.nr)
 			return REV_TREE_SAME;
 	}
 
@@ -351,7 +429,7 @@
 static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
 {
 	struct commit_list **pp, *parent;
-	int tree_changed = 0, tree_same = 0;
+	int tree_changed = 0, tree_same = 0, nth_parent = 0;
 
 	/*
 	 * If we don't do pruning, everything is interesting
@@ -379,6 +457,14 @@
 	while ((parent = *pp) != NULL) {
 		struct commit *p = parent->item;
 
+		/*
+		 * Do not compare with later parents when we care only about
+		 * the first parent chain, in order to avoid derailing the
+		 * traversal to follow a side branch that brought everything
+		 * in the path we are limited to by the pathspec.
+		 */
+		if (revs->first_parent_only && nth_parent++)
+			break;
 		if (parse_commit(p) < 0)
 			die("cannot simplify commit %s (because of %s)",
 			    sha1_to_hex(commit->object.sha1),
@@ -432,15 +518,15 @@
 	commit->object.flags |= TREESAME;
 }
 
-static void insert_by_date_cached(struct commit *p, struct commit_list **head,
+static void commit_list_insert_by_date_cached(struct commit *p, struct commit_list **head,
 		    struct commit_list *cached_base, struct commit_list **cache)
 {
 	struct commit_list *new_entry;
 
 	if (cached_base && p->date < cached_base->item->date)
-		new_entry = insert_by_date(p, &cached_base->next);
+		new_entry = commit_list_insert_by_date(p, &cached_base->next);
 	else
-		new_entry = insert_by_date(p, head);
+		new_entry = commit_list_insert_by_date(p, head);
 
 	if (cache && (!*cache || p->date < (*cache)->item->date))
 		*cache = new_entry;
@@ -482,7 +568,7 @@
 			if (p->object.flags & SEEN)
 				continue;
 			p->object.flags |= SEEN;
-			insert_by_date_cached(p, list, cached_base, cache_ptr);
+			commit_list_insert_by_date_cached(p, list, cached_base, cache_ptr);
 		}
 		return 0;
 	}
@@ -509,7 +595,7 @@
 		p->object.flags |= left_flag;
 		if (!(p->object.flags & SEEN)) {
 			p->object.flags |= SEEN;
-			insert_by_date_cached(p, list, cached_base, cache_ptr);
+			commit_list_insert_by_date_cached(p, list, cached_base, cache_ptr);
 		}
 		if (revs->first_parent_only)
 			break;
@@ -523,6 +609,7 @@
 	int left_count = 0, right_count = 0;
 	int left_first;
 	struct patch_ids ids;
+	unsigned cherry_flag;
 
 	/* First count the commits on the left and on the right */
 	for (p = list; p; p = p->next) {
@@ -536,13 +623,12 @@
 			right_count++;
 	}
 
+	if (!left_count || !right_count)
+		return;
+
 	left_first = left_count < right_count;
 	init_patch_ids(&ids);
-	if (revs->diffopt.nr_paths) {
-		ids.diffopts.nr_paths = revs->diffopt.nr_paths;
-		ids.diffopts.paths = revs->diffopt.paths;
-		ids.diffopts.pathlens = revs->diffopt.pathlens;
-	}
+	ids.diffopts.pathspec = revs->diffopt.pathspec;
 
 	/* Compute patch-ids for one side */
 	for (p = list; p; p = p->next) {
@@ -561,6 +647,9 @@
 		commit->util = add_commit_patch_id(commit, &ids);
 	}
 
+	/* either cherry_mark or cherry_pick are true */
+	cherry_flag = revs->cherry_mark ? PATCHSAME : SHOWN;
+
 	/* Check the other side */
 	for (p = list; p; p = p->next) {
 		struct commit *commit = p->item;
@@ -583,7 +672,7 @@
 		if (!id)
 			continue;
 		id->seen = 1;
-		commit->object.flags |= SHOWN;
+		commit->object.flags |= cherry_flag;
 	}
 
 	/* Now check the original side for seen ones */
@@ -595,7 +684,7 @@
 		if (!ent)
 			continue;
 		if (ent->seen)
-			commit->object.flags |= SHOWN;
+			commit->object.flags |= cherry_flag;
 		commit->util = NULL;
 	}
 
@@ -631,6 +720,114 @@
 	return slop-1;
 }
 
+/*
+ * "rev-list --ancestry-path A..B" computes commits that are ancestors
+ * of B but not ancestors of A but further limits the result to those
+ * that are descendants of A.  This takes the list of bottom commits and
+ * the result of "A..B" without --ancestry-path, and limits the latter
+ * further to the ones that can reach one of the commits in "bottom".
+ */
+static void limit_to_ancestry(struct commit_list *bottom, struct commit_list *list)
+{
+	struct commit_list *p;
+	struct commit_list *rlist = NULL;
+	int made_progress;
+
+	/*
+	 * Reverse the list so that it will be likely that we would
+	 * process parents before children.
+	 */
+	for (p = list; p; p = p->next)
+		commit_list_insert(p->item, &rlist);
+
+	for (p = bottom; p; p = p->next)
+		p->item->object.flags |= TMP_MARK;
+
+	/*
+	 * Mark the ones that can reach bottom commits in "list",
+	 * in a bottom-up fashion.
+	 */
+	do {
+		made_progress = 0;
+		for (p = rlist; p; p = p->next) {
+			struct commit *c = p->item;
+			struct commit_list *parents;
+			if (c->object.flags & (TMP_MARK | UNINTERESTING))
+				continue;
+			for (parents = c->parents;
+			     parents;
+			     parents = parents->next) {
+				if (!(parents->item->object.flags & TMP_MARK))
+					continue;
+				c->object.flags |= TMP_MARK;
+				made_progress = 1;
+				break;
+			}
+		}
+	} while (made_progress);
+
+	/*
+	 * NEEDSWORK: decide if we want to remove parents that are
+	 * not marked with TMP_MARK from commit->parents for commits
+	 * in the resulting list.  We may not want to do that, though.
+	 */
+
+	/*
+	 * The ones that are not marked with TMP_MARK are uninteresting
+	 */
+	for (p = list; p; p = p->next) {
+		struct commit *c = p->item;
+		if (c->object.flags & TMP_MARK)
+			continue;
+		c->object.flags |= UNINTERESTING;
+	}
+
+	/* We are done with the TMP_MARK */
+	for (p = list; p; p = p->next)
+		p->item->object.flags &= ~TMP_MARK;
+	for (p = bottom; p; p = p->next)
+		p->item->object.flags &= ~TMP_MARK;
+	free_commit_list(rlist);
+}
+
+/*
+ * Before walking the history, keep the set of "negative" refs the
+ * caller has asked to exclude.
+ *
+ * This is used to compute "rev-list --ancestry-path A..B", as we need
+ * to filter the result of "A..B" further to the ones that can actually
+ * reach A.
+ */
+static struct commit_list *collect_bottom_commits(struct rev_info *revs)
+{
+	struct commit_list *bottom = NULL;
+	int i;
+	for (i = 0; i < revs->cmdline.nr; i++) {
+		struct rev_cmdline_entry *elem = &revs->cmdline.rev[i];
+		if ((elem->flags & UNINTERESTING) &&
+		    elem->item->type == OBJ_COMMIT)
+			commit_list_insert((struct commit *)elem->item, &bottom);
+	}
+	return bottom;
+}
+
+/* Assumes either left_only or right_only is set */
+static void limit_left_right(struct commit_list *list, struct rev_info *revs)
+{
+	struct commit_list *p;
+
+	for (p = list; p; p = p->next) {
+		struct commit *commit = p->item;
+
+		if (revs->right_only) {
+			if (commit->object.flags & SYMMETRIC_LEFT)
+				commit->object.flags |= SHOWN;
+		} else	/* revs->left_only is set */
+			if (!(commit->object.flags & SYMMETRIC_LEFT))
+				commit->object.flags |= SHOWN;
+	}
+}
+
 static int limit_list(struct rev_info *revs)
 {
 	int slop = SLOP;
@@ -638,6 +835,13 @@
 	struct commit_list *list = revs->commits;
 	struct commit_list *newlist = NULL;
 	struct commit_list **p = &newlist;
+	struct commit_list *bottom = NULL;
+
+	if (revs->ancestry_path) {
+		bottom = collect_bottom_commits(revs);
+		if (!bottom)
+			die("--ancestry-path given but there are no bottom commits");
+	}
 
 	while (list) {
 		struct commit_list *entry = list;
@@ -676,13 +880,38 @@
 		show(revs, newlist);
 		show_early_output = NULL;
 	}
-	if (revs->cherry_pick)
+	if (revs->cherry_pick || revs->cherry_mark)
 		cherry_pick_list(newlist, revs);
 
+	if (revs->left_only || revs->right_only)
+		limit_left_right(newlist, revs);
+
+	if (bottom) {
+		limit_to_ancestry(bottom, newlist);
+		free_commit_list(bottom);
+	}
+
 	revs->commits = newlist;
 	return 0;
 }
 
+static void add_rev_cmdline(struct rev_info *revs,
+			    struct object *item,
+			    const char *name,
+			    int whence,
+			    unsigned flags)
+{
+	struct rev_cmdline_info *info = &revs->cmdline;
+	int nr = info->nr;
+
+	ALLOC_GROW(info->rev, nr + 1, info->alloc);
+	info->rev[nr].item = item;
+	info->rev[nr].name = name;
+	info->rev[nr].whence = whence;
+	info->rev[nr].flags = flags;
+	info->nr++;
+}
+
 struct all_refs_cb {
 	int all_flags;
 	int warned_bad_reflog;
@@ -695,17 +924,24 @@
 	struct all_refs_cb *cb = cb_data;
 	struct object *object = get_reference(cb->all_revs, path, sha1,
 					      cb->all_flags);
-	add_pending_object(cb->all_revs, object, path);
+	add_rev_cmdline(cb->all_revs, object, path, REV_CMD_REF, cb->all_flags);
+	add_pending_sha1(cb->all_revs, path, sha1, cb->all_flags);
 	return 0;
 }
 
-static void handle_refs(struct rev_info *revs, unsigned flags,
-		int (*for_each)(each_ref_fn, void *))
+static void init_all_refs_cb(struct all_refs_cb *cb, struct rev_info *revs,
+	unsigned flags)
+{
+	cb->all_revs = revs;
+	cb->all_flags = flags;
+}
+
+static void handle_refs(const char *submodule, struct rev_info *revs, unsigned flags,
+		int (*for_each)(const char *, each_ref_fn, void *))
 {
 	struct all_refs_cb cb;
-	cb.all_revs = revs;
-	cb.all_flags = flags;
-	for_each(handle_one_ref, &cb);
+	init_all_refs_cb(&cb, revs, flags);
+	for_each(submodule, handle_one_ref, &cb);
 }
 
 static void handle_one_reflog_commit(unsigned char *sha1, void *cb_data)
@@ -715,6 +951,7 @@
 		struct object *o = parse_object(sha1);
 		if (o) {
 			o->flags |= cb->all_flags;
+			/* ??? CMDLINEFLAGS ??? */
 			add_pending_object(cb->all_revs, o, "");
 		}
 		else if (!cb->warned_bad_reflog) {
@@ -751,21 +988,24 @@
 	for_each_reflog(handle_one_reflog, &cb);
 }
 
-static int add_parents_only(struct rev_info *revs, const char *arg, int flags)
+static int add_parents_only(struct rev_info *revs, const char *arg_, int flags)
 {
 	unsigned char sha1[20];
 	struct object *it;
 	struct commit *commit;
 	struct commit_list *parents;
+	const char *arg = arg_;
 
 	if (*arg == '^') {
 		flags ^= UNINTERESTING;
 		arg++;
 	}
-	if (get_sha1(arg, sha1))
+	if (get_sha1_committish(arg, sha1))
 		return 0;
 	while (1) {
 		it = get_reference(revs, arg, sha1, 0);
+		if (!it && revs->ignore_missing)
+			return 0;
 		if (it->type != OBJ_TAG)
 			break;
 		if (!((struct tag*)it)->tagged)
@@ -778,6 +1018,7 @@
 	for (parents = commit->parents; parents; parents = parents->next) {
 		it = &parents->item->object;
 		it->flags |= flags;
+		add_rev_cmdline(revs, it, arg_, REV_CMD_PARENTS_ONLY, flags);
 		add_pending_object(revs, it, arg);
 	}
 	return 1;
@@ -791,7 +1032,7 @@
 	revs->ignore_merges = 1;
 	revs->simplify_history = 1;
 	DIFF_OPT_SET(&revs->pruning, RECURSIVE);
-	DIFF_OPT_SET(&revs->pruning, QUIET);
+	DIFF_OPT_SET(&revs->pruning, QUICK);
 	revs->pruning.add_remove = file_add_remove;
 	revs->pruning.change = file_change;
 	revs->lifo = 1;
@@ -801,11 +1042,13 @@
 	revs->min_age = -1;
 	revs->skip_count = -1;
 	revs->max_count = -1;
+	revs->max_parents = -1;
 
 	revs->commit_format = CMIT_FMT_DEFAULT;
 
 	revs->grep_filter.status_only = 1;
 	revs->grep_filter.pattern_tail = &(revs->grep_filter.pattern_list);
+	revs->grep_filter.header_tail = &(revs->grep_filter.header_list);
 	revs->grep_filter.regflags = REG_NEWLINE;
 
 	diff_setup(&revs->diffopt);
@@ -813,6 +1056,8 @@
 		revs->diffopt.prefix = prefix;
 		revs->diffopt.prefix_length = strlen(prefix);
 	}
+
+	revs->notes_opt.use_default_notes = -1;
 }
 
 static void add_pending_commit_list(struct rev_info *revs,
@@ -835,10 +1080,12 @@
 	const char **prune = NULL;
 	int i, prune_num = 1; /* counting terminating NULL */
 
-	if (get_sha1("HEAD", sha1) || !(head = lookup_commit(sha1)))
+	if (get_sha1("HEAD", sha1))
 		die("--merge without HEAD?");
-	if (get_sha1("MERGE_HEAD", sha1) || !(other = lookup_commit(sha1)))
+	head = lookup_commit_or_die(sha1, "HEAD");
+	if (get_sha1("MERGE_HEAD", sha1))
 		die("--merge without MERGE_HEAD?");
+	other = lookup_commit_or_die(sha1, "MERGE_HEAD");
 	add_pending_object(revs, &head->object, "HEAD");
 	add_pending_object(revs, &other->object, "MERGE_HEAD");
 	bases = get_merge_bases(head, other, 1);
@@ -852,7 +1099,7 @@
 		struct cache_entry *ce = active_cache[i];
 		if (!ce_stage(ce))
 			continue;
-		if (ce_path_match(ce, revs->prune_data)) {
+		if (ce_path_match(ce, &revs->prune_data)) {
 			prune_num++;
 			prune = xrealloc(prune, sizeof(*prune) * prune_num);
 			prune[prune_num-2] = ce->name;
@@ -862,19 +1109,21 @@
 		       ce_same_name(ce, active_cache[i+1]))
 			i++;
 	}
-	revs->prune_data = prune;
+	free_pathspec(&revs->prune_data);
+	init_pathspec(&revs->prune_data, prune);
 	revs->limited = 1;
 }
 
-int handle_revision_arg(const char *arg, struct rev_info *revs,
-			int flags,
-			int cant_be_filename)
+int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsigned revarg_opt)
 {
-	unsigned mode;
+	struct object_context oc;
 	char *dotdot;
 	struct object *object;
 	unsigned char sha1[20];
 	int local_flags;
+	const char *arg = arg_;
+	int cant_be_filename = revarg_opt & REVARG_CANNOT_BE_FILENAME;
+	unsigned get_sha1_flags = 0;
 
 	dotdot = strstr(arg, "..");
 	if (dotdot) {
@@ -883,6 +1132,7 @@
 		const char *this = arg;
 		int symmetric = *next == '.';
 		unsigned int flags_exclude = flags ^ UNINTERESTING;
+		unsigned int a_flags;
 
 		*dotdot = 0;
 		next += symmetric;
@@ -891,14 +1141,16 @@
 			next = "HEAD";
 		if (dotdot == arg)
 			this = "HEAD";
-		if (!get_sha1(this, from_sha1) &&
-		    !get_sha1(next, sha1)) {
+		if (!get_sha1_committish(this, from_sha1) &&
+		    !get_sha1_committish(next, sha1)) {
 			struct commit *a, *b;
 			struct commit_list *exclude;
 
 			a = lookup_commit_reference(from_sha1);
 			b = lookup_commit_reference(sha1);
 			if (!a || !b) {
+				if (revs->ignore_missing)
+					return 0;
 				die(symmetric ?
 				    "Invalid symmetric difference expression %s...%s" :
 				    "Invalid revision range %s..%s",
@@ -915,10 +1167,15 @@
 				add_pending_commit_list(revs, exclude,
 							flags_exclude);
 				free_commit_list(exclude);
-				a->object.flags |= flags | SYMMETRIC_LEFT;
+				a_flags = flags | SYMMETRIC_LEFT;
 			} else
-				a->object.flags |= flags_exclude;
+				a_flags = flags_exclude;
+			a->object.flags |= a_flags;
 			b->object.flags |= flags;
+			add_rev_cmdline(revs, &a->object, this,
+					REV_CMD_LEFT, a_flags);
+			add_rev_cmdline(revs, &b->object, next,
+					REV_CMD_RIGHT, flags);
 			add_pending_object(revs, &a->object, this);
 			add_pending_object(revs, &b->object, next);
 			return 0;
@@ -944,30 +1201,72 @@
 		local_flags = UNINTERESTING;
 		arg++;
 	}
-	if (get_sha1_with_mode(arg, sha1, &mode))
-		return -1;
+
+	if (revarg_opt & REVARG_COMMITTISH)
+		get_sha1_flags = GET_SHA1_COMMITTISH;
+
+	if (get_sha1_with_context(arg, get_sha1_flags, sha1, &oc))
+		return revs->ignore_missing ? 0 : -1;
 	if (!cant_be_filename)
 		verify_non_filename(revs->prefix, arg);
 	object = get_reference(revs, arg, sha1, flags ^ local_flags);
-	add_pending_object_with_mode(revs, object, arg, mode);
+	add_rev_cmdline(revs, object, arg_, REV_CMD_REV, flags ^ local_flags);
+	add_pending_object_with_mode(revs, object, arg, oc.mode);
 	return 0;
 }
 
-void read_revisions_from_stdin(struct rev_info *revs)
-{
-	char line[1000];
+struct cmdline_pathspec {
+	int alloc;
+	int nr;
+	const char **path;
+};
 
-	while (fgets(line, sizeof(line), stdin) != NULL) {
-		int len = strlen(line);
-		if (len && line[len - 1] == '\n')
-			line[--len] = '\0';
+static void append_prune_data(struct cmdline_pathspec *prune, const char **av)
+{
+	while (*av) {
+		ALLOC_GROW(prune->path, prune->nr+1, prune->alloc);
+		prune->path[prune->nr++] = *(av++);
+	}
+}
+
+static void read_pathspec_from_stdin(struct rev_info *revs, struct strbuf *sb,
+				     struct cmdline_pathspec *prune)
+{
+	while (strbuf_getwholeline(sb, stdin, '\n') != EOF) {
+		int len = sb->len;
+		if (len && sb->buf[len - 1] == '\n')
+			sb->buf[--len] = '\0';
+		ALLOC_GROW(prune->path, prune->nr+1, prune->alloc);
+		prune->path[prune->nr++] = xstrdup(sb->buf);
+	}
+}
+
+static void read_revisions_from_stdin(struct rev_info *revs,
+				      struct cmdline_pathspec *prune)
+{
+	struct strbuf sb;
+	int seen_dashdash = 0;
+
+	strbuf_init(&sb, 1000);
+	while (strbuf_getwholeline(&sb, stdin, '\n') != EOF) {
+		int len = sb.len;
+		if (len && sb.buf[len - 1] == '\n')
+			sb.buf[--len] = '\0';
 		if (!len)
 			break;
-		if (line[0] == '-')
+		if (sb.buf[0] == '-') {
+			if (len == 2 && sb.buf[1] == '-') {
+				seen_dashdash = 1;
+				break;
+			}
 			die("options not supported in --stdin mode");
-		if (handle_revision_arg(line, revs, 0, 1))
-			die("bad revision '%s'", line);
+		}
+		if (handle_revision_arg(sb.buf, revs, 0, REVARG_CANNOT_BE_FILENAME))
+			die("bad revision '%s'", sb.buf);
 	}
+	if (seen_dashdash)
+		read_pathspec_from_stdin(revs, &sb, prune);
+	strbuf_release(&sb);
 }
 
 static void add_grep(struct rev_info *revs, const char *ptn, enum grep_pat_token what)
@@ -989,45 +1288,66 @@
 			       int *unkc, const char **unkv)
 {
 	const char *arg = argv[0];
+	const char *optarg;
+	int argcount;
 
 	/* pseudo revision arguments */
 	if (!strcmp(arg, "--all") || !strcmp(arg, "--branches") ||
 	    !strcmp(arg, "--tags") || !strcmp(arg, "--remotes") ||
 	    !strcmp(arg, "--reflog") || !strcmp(arg, "--not") ||
-	    !strcmp(arg, "--no-walk") || !strcmp(arg, "--do-walk"))
+	    !strcmp(arg, "--no-walk") || !strcmp(arg, "--do-walk") ||
+	    !strcmp(arg, "--bisect") || !prefixcmp(arg, "--glob=") ||
+	    !prefixcmp(arg, "--branches=") || !prefixcmp(arg, "--tags=") ||
+	    !prefixcmp(arg, "--remotes="))
 	{
 		unkv[(*unkc)++] = arg;
 		return 1;
 	}
 
-	if (!prefixcmp(arg, "--max-count=")) {
-		revs->max_count = atoi(arg + 12);
-	} else if (!prefixcmp(arg, "--skip=")) {
-		revs->skip_count = atoi(arg + 7);
+	if ((argcount = parse_long_opt("max-count", argv, &optarg))) {
+		revs->max_count = atoi(optarg);
+		revs->no_walk = 0;
+		return argcount;
+	} else if ((argcount = parse_long_opt("skip", argv, &optarg))) {
+		revs->skip_count = atoi(optarg);
+		return argcount;
 	} else if ((*arg == '-') && isdigit(arg[1])) {
 	/* accept -<digit>, like traditional "head" */
 		revs->max_count = atoi(arg + 1);
+		revs->no_walk = 0;
 	} else if (!strcmp(arg, "-n")) {
 		if (argc <= 1)
 			return error("-n requires an argument");
 		revs->max_count = atoi(argv[1]);
+		revs->no_walk = 0;
 		return 2;
 	} else if (!prefixcmp(arg, "-n")) {
 		revs->max_count = atoi(arg + 2);
-	} else if (!prefixcmp(arg, "--max-age=")) {
-		revs->max_age = atoi(arg + 10);
-	} else if (!prefixcmp(arg, "--since=")) {
-		revs->max_age = approxidate(arg + 8);
-	} else if (!prefixcmp(arg, "--after=")) {
-		revs->max_age = approxidate(arg + 8);
-	} else if (!prefixcmp(arg, "--min-age=")) {
-		revs->min_age = atoi(arg + 10);
-	} else if (!prefixcmp(arg, "--before=")) {
-		revs->min_age = approxidate(arg + 9);
-	} else if (!prefixcmp(arg, "--until=")) {
-		revs->min_age = approxidate(arg + 8);
+		revs->no_walk = 0;
+	} else if ((argcount = parse_long_opt("max-age", argv, &optarg))) {
+		revs->max_age = atoi(optarg);
+		return argcount;
+	} else if ((argcount = parse_long_opt("since", argv, &optarg))) {
+		revs->max_age = approxidate(optarg);
+		return argcount;
+	} else if ((argcount = parse_long_opt("after", argv, &optarg))) {
+		revs->max_age = approxidate(optarg);
+		return argcount;
+	} else if ((argcount = parse_long_opt("min-age", argv, &optarg))) {
+		revs->min_age = atoi(optarg);
+		return argcount;
+	} else if ((argcount = parse_long_opt("before", argv, &optarg))) {
+		revs->min_age = approxidate(optarg);
+		return argcount;
+	} else if ((argcount = parse_long_opt("until", argv, &optarg))) {
+		revs->min_age = approxidate(optarg);
+		return argcount;
 	} else if (!strcmp(arg, "--first-parent")) {
 		revs->first_parent_only = 1;
+	} else if (!strcmp(arg, "--ancestry-path")) {
+		revs->ancestry_path = 1;
+		revs->simplify_history = 0;
+		revs->limited = 1;
 	} else if (!strcmp(arg, "-g") || !strcmp(arg, "--walk-reflogs")) {
 		init_reflog_walk(&revs->reflog_info);
 	} else if (!strcmp(arg, "--default")) {
@@ -1042,17 +1362,19 @@
 		revs->topo_order = 1;
 	} else if (!strcmp(arg, "--simplify-merges")) {
 		revs->simplify_merges = 1;
+		revs->topo_order = 1;
 		revs->rewrite_parents = 1;
 		revs->simplify_history = 0;
 		revs->limited = 1;
 	} else if (!strcmp(arg, "--simplify-by-decoration")) {
 		revs->simplify_merges = 1;
+		revs->topo_order = 1;
 		revs->rewrite_parents = 1;
 		revs->simplify_history = 0;
 		revs->simplify_by_decoration = 1;
 		revs->limited = 1;
 		revs->prune = 1;
-		load_ref_decorations();
+		load_ref_decorations(DECORATE_SHORT_REFS);
 	} else if (!strcmp(arg, "--date-order")) {
 		revs->lifo = 0;
 		revs->topo_order = 1;
@@ -1078,14 +1400,47 @@
 	} else if (!strcmp(arg, "--remove-empty")) {
 		revs->remove_empty_trees = 1;
 	} else if (!strcmp(arg, "--merges")) {
-		revs->merges_only = 1;
+		revs->min_parents = 2;
 	} else if (!strcmp(arg, "--no-merges")) {
-		revs->no_merges = 1;
+		revs->max_parents = 1;
+	} else if (!prefixcmp(arg, "--min-parents=")) {
+		revs->min_parents = atoi(arg+14);
+	} else if (!prefixcmp(arg, "--no-min-parents")) {
+		revs->min_parents = 0;
+	} else if (!prefixcmp(arg, "--max-parents=")) {
+		revs->max_parents = atoi(arg+14);
+	} else if (!prefixcmp(arg, "--no-max-parents")) {
+		revs->max_parents = -1;
 	} else if (!strcmp(arg, "--boundary")) {
 		revs->boundary = 1;
 	} else if (!strcmp(arg, "--left-right")) {
 		revs->left_right = 1;
+	} else if (!strcmp(arg, "--left-only")) {
+		if (revs->right_only)
+			die("--left-only is incompatible with --right-only"
+			    " or --cherry");
+		revs->left_only = 1;
+	} else if (!strcmp(arg, "--right-only")) {
+		if (revs->left_only)
+			die("--right-only is incompatible with --left-only");
+		revs->right_only = 1;
+	} else if (!strcmp(arg, "--cherry")) {
+		if (revs->left_only)
+			die("--cherry is incompatible with --left-only");
+		revs->cherry_mark = 1;
+		revs->right_only = 1;
+		revs->max_parents = 1;
+		revs->limited = 1;
+	} else if (!strcmp(arg, "--count")) {
+		revs->count = 1;
+	} else if (!strcmp(arg, "--cherry-mark")) {
+		if (revs->cherry_pick)
+			die("--cherry-mark is incompatible with --cherry-pick");
+		revs->cherry_mark = 1;
+		revs->limited = 1; /* needs limit_list() */
 	} else if (!strcmp(arg, "--cherry-pick")) {
+		if (revs->cherry_mark)
+			die("--cherry-pick is incompatible with --cherry-mark");
 		revs->cherry_pick = 1;
 		revs->limited = 1;
 	} else if (!strcmp(arg, "--objects")) {
@@ -1097,6 +1452,11 @@
 		revs->tree_objects = 1;
 		revs->blob_objects = 1;
 		revs->edge_hint = 1;
+	} else if (!strcmp(arg, "--verify-objects")) {
+		revs->tag_objects = 1;
+		revs->tree_objects = 1;
+		revs->blob_objects = 1;
+		revs->verify_objects = 1;
 	} else if (!strcmp(arg, "--unpacked")) {
 		revs->unpacked = 1;
 	} else if (!prefixcmp(arg, "--unpacked=")) {
@@ -1122,13 +1482,55 @@
 		revs->verbose_header = 1;
 	} else if (!strcmp(arg, "--pretty")) {
 		revs->verbose_header = 1;
+		revs->pretty_given = 1;
 		get_commit_format(arg+8, revs);
 	} else if (!prefixcmp(arg, "--pretty=") || !prefixcmp(arg, "--format=")) {
+		/*
+		 * Detached form ("--pretty X" as opposed to "--pretty=X")
+		 * not allowed, since the argument is optional.
+		 */
 		revs->verbose_header = 1;
+		revs->pretty_given = 1;
 		get_commit_format(arg+9, revs);
+	} else if (!strcmp(arg, "--show-notes") || !strcmp(arg, "--notes")) {
+		revs->show_notes = 1;
+		revs->show_notes_given = 1;
+		revs->notes_opt.use_default_notes = 1;
+	} else if (!strcmp(arg, "--show-signature")) {
+		revs->show_signature = 1;
+	} else if (!prefixcmp(arg, "--show-notes=") ||
+		   !prefixcmp(arg, "--notes=")) {
+		struct strbuf buf = STRBUF_INIT;
+		revs->show_notes = 1;
+		revs->show_notes_given = 1;
+		if (!prefixcmp(arg, "--show-notes")) {
+			if (revs->notes_opt.use_default_notes < 0)
+				revs->notes_opt.use_default_notes = 1;
+			strbuf_addstr(&buf, arg+13);
+		}
+		else
+			strbuf_addstr(&buf, arg+8);
+		expand_notes_ref(&buf);
+		string_list_append(&revs->notes_opt.extra_notes_refs,
+				   strbuf_detach(&buf, NULL));
+	} else if (!strcmp(arg, "--no-notes")) {
+		revs->show_notes = 0;
+		revs->show_notes_given = 1;
+		revs->notes_opt.use_default_notes = -1;
+		/* we have been strdup'ing ourselves, so trick
+		 * string_list into free()ing strings */
+		revs->notes_opt.extra_notes_refs.strdup_strings = 1;
+		string_list_clear(&revs->notes_opt.extra_notes_refs, 0);
+		revs->notes_opt.extra_notes_refs.strdup_strings = 0;
+	} else if (!strcmp(arg, "--standard-notes")) {
+		revs->show_notes_given = 1;
+		revs->notes_opt.use_default_notes = 1;
+	} else if (!strcmp(arg, "--no-standard-notes")) {
+		revs->notes_opt.use_default_notes = 0;
 	} else if (!strcmp(arg, "--oneline")) {
 		revs->verbose_header = 1;
 		get_commit_format("oneline", revs);
+		revs->pretty_given = 1;
 		revs->abbrev_commit = 1;
 	} else if (!strcmp(arg, "--graph")) {
 		revs->topo_order = 1;
@@ -1152,6 +1554,9 @@
 			revs->abbrev = 40;
 	} else if (!strcmp(arg, "--abbrev-commit")) {
 		revs->abbrev_commit = 1;
+		revs->abbrev_commit_given = 1;
+	} else if (!strcmp(arg, "--no-abbrev-commit")) {
+		revs->abbrev_commit = 0;
 	} else if (!strcmp(arg, "--full-diff")) {
 		revs->diff = 1;
 		revs->full_diff = 1;
@@ -1159,39 +1564,48 @@
 		revs->simplify_history = 0;
 	} else if (!strcmp(arg, "--relative-date")) {
 		revs->date_mode = DATE_RELATIVE;
-	} else if (!strncmp(arg, "--date=", 7)) {
-		revs->date_mode = parse_date_format(arg + 7);
+		revs->date_mode_explicit = 1;
+	} else if ((argcount = parse_long_opt("date", argv, &optarg))) {
+		revs->date_mode = parse_date_format(optarg);
+		revs->date_mode_explicit = 1;
+		return argcount;
 	} else if (!strcmp(arg, "--log-size")) {
 		revs->show_log_size = 1;
 	}
 	/*
 	 * Grepping the commit log
 	 */
-	else if (!prefixcmp(arg, "--author=")) {
-		add_header_grep(revs, GREP_HEADER_AUTHOR, arg+9);
-	} else if (!prefixcmp(arg, "--committer=")) {
-		add_header_grep(revs, GREP_HEADER_COMMITTER, arg+12);
-	} else if (!prefixcmp(arg, "--grep=")) {
-		add_message_grep(revs, arg+7);
+	else if ((argcount = parse_long_opt("author", argv, &optarg))) {
+		add_header_grep(revs, GREP_HEADER_AUTHOR, optarg);
+		return argcount;
+	} else if ((argcount = parse_long_opt("committer", argv, &optarg))) {
+		add_header_grep(revs, GREP_HEADER_COMMITTER, optarg);
+		return argcount;
+	} else if ((argcount = parse_long_opt("grep", argv, &optarg))) {
+		add_message_grep(revs, optarg);
+		return argcount;
 	} else if (!strcmp(arg, "--extended-regexp") || !strcmp(arg, "-E")) {
 		revs->grep_filter.regflags |= REG_EXTENDED;
 	} else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) {
 		revs->grep_filter.regflags |= REG_ICASE;
+		DIFF_OPT_SET(&revs->diffopt, PICKAXE_IGNORE_CASE);
 	} else if (!strcmp(arg, "--fixed-strings") || !strcmp(arg, "-F")) {
 		revs->grep_filter.fixed = 1;
 	} else if (!strcmp(arg, "--all-match")) {
 		revs->grep_filter.all_match = 1;
-	} else if (!prefixcmp(arg, "--encoding=")) {
-		arg += 11;
-		if (strcmp(arg, "none"))
-			git_log_output_encoding = xstrdup(arg);
+	} else if ((argcount = parse_long_opt("encoding", argv, &optarg))) {
+		if (strcmp(optarg, "none"))
+			git_log_output_encoding = xstrdup(optarg);
 		else
 			git_log_output_encoding = "";
+		return argcount;
 	} else if (!strcmp(arg, "--reverse")) {
 		revs->reverse ^= 1;
 	} else if (!strcmp(arg, "--children")) {
 		revs->children.name = "children";
 		revs->limited = 1;
+	} else if (!strcmp(arg, "--ignore-missing")) {
+		revs->ignore_missing = 1;
 	} else {
 		int opts = diff_opt_parse(&revs->diffopt, argv, argc);
 		if (!opts)
@@ -1216,6 +1630,79 @@
 	ctx->argc -= n;
 }
 
+static int for_each_bad_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data)
+{
+	return for_each_ref_in_submodule(submodule, "refs/bisect/bad", fn, cb_data);
+}
+
+static int for_each_good_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data)
+{
+	return for_each_ref_in_submodule(submodule, "refs/bisect/good", fn, cb_data);
+}
+
+static int handle_revision_pseudo_opt(const char *submodule,
+				struct rev_info *revs,
+				int argc, const char **argv, int *flags)
+{
+	const char *arg = argv[0];
+	const char *optarg;
+	int argcount;
+
+	/*
+	 * NOTE!
+	 *
+	 * Commands like "git shortlog" will not accept the options below
+	 * unless parse_revision_opt queues them (as opposed to erroring
+	 * out).
+	 *
+	 * When implementing your new pseudo-option, remember to
+	 * register it in the list at the top of handle_revision_opt.
+	 */
+	if (!strcmp(arg, "--all")) {
+		handle_refs(submodule, revs, *flags, for_each_ref_submodule);
+		handle_refs(submodule, revs, *flags, head_ref_submodule);
+	} else if (!strcmp(arg, "--branches")) {
+		handle_refs(submodule, revs, *flags, for_each_branch_ref_submodule);
+	} else if (!strcmp(arg, "--bisect")) {
+		handle_refs(submodule, revs, *flags, for_each_bad_bisect_ref);
+		handle_refs(submodule, revs, *flags ^ UNINTERESTING, for_each_good_bisect_ref);
+		revs->bisect = 1;
+	} else if (!strcmp(arg, "--tags")) {
+		handle_refs(submodule, revs, *flags, for_each_tag_ref_submodule);
+	} else if (!strcmp(arg, "--remotes")) {
+		handle_refs(submodule, revs, *flags, for_each_remote_ref_submodule);
+	} else if ((argcount = parse_long_opt("glob", argv, &optarg))) {
+		struct all_refs_cb cb;
+		init_all_refs_cb(&cb, revs, *flags);
+		for_each_glob_ref(handle_one_ref, optarg, &cb);
+		return argcount;
+	} else if (!prefixcmp(arg, "--branches=")) {
+		struct all_refs_cb cb;
+		init_all_refs_cb(&cb, revs, *flags);
+		for_each_glob_ref_in(handle_one_ref, arg + 11, "refs/heads/", &cb);
+	} else if (!prefixcmp(arg, "--tags=")) {
+		struct all_refs_cb cb;
+		init_all_refs_cb(&cb, revs, *flags);
+		for_each_glob_ref_in(handle_one_ref, arg + 7, "refs/tags/", &cb);
+	} else if (!prefixcmp(arg, "--remotes=")) {
+		struct all_refs_cb cb;
+		init_all_refs_cb(&cb, revs, *flags);
+		for_each_glob_ref_in(handle_one_ref, arg + 10, "refs/remotes/", &cb);
+	} else if (!strcmp(arg, "--reflog")) {
+		handle_reflog(revs, *flags);
+	} else if (!strcmp(arg, "--not")) {
+		*flags ^= UNINTERESTING;
+	} else if (!strcmp(arg, "--no-walk")) {
+		revs->no_walk = 1;
+	} else if (!strcmp(arg, "--do-walk")) {
+		revs->no_walk = 0;
+	} else {
+		return 0;
+	}
+
+	return 1;
+}
+
 /*
  * Parse revision information, filling in the "rev_info" structure,
  * and removing the used arguments from the argument list.
@@ -1223,62 +1710,61 @@
  * Returns the number of arguments left that weren't recognized
  * (which are also moved to the head of the argument list)
  */
-int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def)
+int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct setup_revision_opt *opt)
 {
-	int i, flags, left, seen_dashdash;
+	int i, flags, left, seen_dashdash, read_from_stdin, got_rev_arg = 0, revarg_opt;
+	struct cmdline_pathspec prune_data;
+	const char *submodule = NULL;
+
+	memset(&prune_data, 0, sizeof(prune_data));
+	if (opt)
+		submodule = opt->submodule;
 
 	/* First, search for "--" */
-	seen_dashdash = 0;
-	for (i = 1; i < argc; i++) {
-		const char *arg = argv[i];
-		if (strcmp(arg, "--"))
-			continue;
-		argv[i] = NULL;
-		argc = i;
-		if (argv[i + 1])
-			revs->prune_data = get_pathspec(revs->prefix, argv + i + 1);
+	if (opt && opt->assume_dashdash) {
 		seen_dashdash = 1;
-		break;
+	} else {
+		seen_dashdash = 0;
+		for (i = 1; i < argc; i++) {
+			const char *arg = argv[i];
+			if (strcmp(arg, "--"))
+				continue;
+			argv[i] = NULL;
+			argc = i;
+			if (argv[i + 1])
+				append_prune_data(&prune_data, argv + i + 1);
+			seen_dashdash = 1;
+			break;
+		}
 	}
 
 	/* Second, deal with arguments and options */
 	flags = 0;
+	revarg_opt = opt ? opt->revarg_opt : 0;
+	if (seen_dashdash)
+		revarg_opt |= REVARG_CANNOT_BE_FILENAME;
+	read_from_stdin = 0;
 	for (left = i = 1; i < argc; i++) {
 		const char *arg = argv[i];
 		if (*arg == '-') {
 			int opts;
 
-			if (!strcmp(arg, "--all")) {
-				handle_refs(revs, flags, for_each_ref);
-				handle_refs(revs, flags, head_ref);
+			opts = handle_revision_pseudo_opt(submodule,
+						revs, argc - i, argv + i,
+						&flags);
+			if (opts > 0) {
+				i += opts - 1;
 				continue;
 			}
-			if (!strcmp(arg, "--branches")) {
-				handle_refs(revs, flags, for_each_branch_ref);
-				continue;
-			}
-			if (!strcmp(arg, "--tags")) {
-				handle_refs(revs, flags, for_each_tag_ref);
-				continue;
-			}
-			if (!strcmp(arg, "--remotes")) {
-				handle_refs(revs, flags, for_each_remote_ref);
-				continue;
-			}
-			if (!strcmp(arg, "--reflog")) {
-				handle_reflog(revs, flags);
-				continue;
-			}
-			if (!strcmp(arg, "--not")) {
-				flags ^= UNINTERESTING;
-				continue;
-			}
-			if (!strcmp(arg, "--no-walk")) {
-				revs->no_walk = 1;
-				continue;
-			}
-			if (!strcmp(arg, "--do-walk")) {
-				revs->no_walk = 0;
+
+			if (!strcmp(arg, "--stdin")) {
+				if (revs->disable_stdin) {
+					argv[left++] = arg;
+					continue;
+				}
+				if (read_from_stdin++)
+					die("--stdin given twice?");
+				read_revisions_from_stdin(revs, &prune_data);
 				continue;
 			}
 
@@ -1292,7 +1778,8 @@
 			continue;
 		}
 
-		if (handle_revision_arg(arg, revs, flags, seen_dashdash)) {
+
+		if (handle_revision_arg(arg, revs, flags, revarg_opt)) {
 			int j;
 			if (seen_dashdash || *arg == '^')
 				die("bad revision '%s'", arg);
@@ -1304,26 +1791,50 @@
 			 * but the latter we have checked in the main loop.
 			 */
 			for (j = i; j < argc; j++)
-				verify_filename(revs->prefix, argv[j]);
+				verify_filename(revs->prefix, argv[j], j == i);
 
-			revs->prune_data = get_pathspec(revs->prefix,
-							argv + i);
+			append_prune_data(&prune_data, argv + i);
 			break;
 		}
+		else
+			got_rev_arg = 1;
+	}
+
+	if (prune_data.nr) {
+		/*
+		 * If we need to introduce the magic "a lone ':' means no
+		 * pathspec whatsoever", here is the place to do so.
+		 *
+		 * if (prune_data.nr == 1 && !strcmp(prune_data[0], ":")) {
+		 *	prune_data.nr = 0;
+		 *	prune_data.alloc = 0;
+		 *	free(prune_data.path);
+		 *	prune_data.path = NULL;
+		 * } else {
+		 *	terminate prune_data.alloc with NULL and
+		 *	call init_pathspec() to set revs->prune_data here.
+		 * }
+		 */
+		ALLOC_GROW(prune_data.path, prune_data.nr+1, prune_data.alloc);
+		prune_data.path[prune_data.nr++] = NULL;
+		init_pathspec(&revs->prune_data,
+			      get_pathspec(revs->prefix, prune_data.path));
 	}
 
 	if (revs->def == NULL)
-		revs->def = def;
+		revs->def = opt ? opt->def : NULL;
+	if (opt && opt->tweak)
+		opt->tweak(revs, opt);
 	if (revs->show_merge)
 		prepare_show_merge(revs);
-	if (revs->def && !revs->pending.nr) {
+	if (revs->def && !revs->pending.nr && !got_rev_arg) {
 		unsigned char sha1[20];
 		struct object *object;
-		unsigned mode;
-		if (get_sha1_with_mode(revs->def, sha1, &mode))
+		struct object_context oc;
+		if (get_sha1_with_context(revs->def, 0, sha1, &oc))
 			die("bad default revision '%s'", revs->def);
 		object = get_reference(revs, revs->def, sha1, 0);
-		add_pending_object_with_mode(revs, object, revs->def, mode);
+		add_pending_object_with_mode(revs, object, revs->def, oc.mode);
 	}
 
 	/* Did the user ask for any diff output? Run the diff! */
@@ -1339,19 +1850,16 @@
 	if (revs->topo_order)
 		revs->limited = 1;
 
-	if (revs->prune_data) {
-		diff_tree_setup_paths(revs->prune_data, &revs->pruning);
+	if (revs->prune_data.nr) {
+		diff_tree_setup_paths(revs->prune_data.raw, &revs->pruning);
 		/* Can't prune commits with rename following: the paths change.. */
 		if (!DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES))
 			revs->prune = 1;
 		if (!revs->full_diff)
-			diff_tree_setup_paths(revs->prune_data, &revs->diffopt);
+			diff_tree_setup_paths(revs->prune_data.raw, &revs->diffopt);
 	}
-	if (revs->combine_merges) {
+	if (revs->combine_merges)
 		revs->ignore_merges = 0;
-		if (revs->dense_combined_merges && !revs->diffopt.output_format)
-			revs->diffopt.output_format = DIFF_FORMAT_PATCH;
-	}
 	revs->diffopt.abbrev = revs->abbrev;
 	if (diff_setup_done(&revs->diffopt) < 0)
 		die("diff_setup_done failed");
@@ -1449,8 +1957,9 @@
 	}
 
 	/*
-	 * Do we know what commit all of our parents should be rewritten to?
-	 * Otherwise we are not ready to rewrite this one yet.
+	 * Do we know what commit all of our parents that matter
+	 * should be rewritten to?  Otherwise we are not ready to
+	 * rewrite this one yet.
 	 */
 	for (cnt = 0, p = commit->parents; p; p = p->next) {
 		pst = locate_simplify_state(revs, p->item);
@@ -1458,6 +1967,8 @@
 			tail = &commit_list_insert(p->item, tail)->next;
 			cnt++;
 		}
+		if (revs->first_parent_only)
+			break;
 	}
 	if (cnt) {
 		tail = &commit_list_insert(commit, tail)->next;
@@ -1470,8 +1981,13 @@
 	for (p = commit->parents; p; p = p->next) {
 		pst = locate_simplify_state(revs, p->item);
 		p->item = pst->simplified;
+		if (revs->first_parent_only)
+			break;
 	}
-	cnt = remove_duplicate_parents(commit);
+	if (!revs->first_parent_only)
+		cnt = remove_duplicate_parents(commit);
+	else
+		cnt = 1;
 
 	/*
 	 * It is possible that we are a merge and one side branch
@@ -1515,25 +2031,31 @@
 
 static void simplify_merges(struct rev_info *revs)
 {
-	struct commit_list *list;
+	struct commit_list *list, *next;
 	struct commit_list *yet_to_do, **tail;
+	struct commit *commit;
 
-	if (!revs->topo_order)
-		sort_in_topological_order(&revs->commits, revs->lifo);
 	if (!revs->prune)
 		return;
 
 	/* feed the list reversed */
 	yet_to_do = NULL;
-	for (list = revs->commits; list; list = list->next)
-		commit_list_insert(list->item, &yet_to_do);
+	for (list = revs->commits; list; list = next) {
+		commit = list->item;
+		next = list->next;
+		/*
+		 * Do not free(list) here yet; the original list
+		 * is used later in this function.
+		 */
+		commit_list_insert(commit, &yet_to_do);
+	}
 	while (yet_to_do) {
 		list = yet_to_do;
 		yet_to_do = NULL;
 		tail = &yet_to_do;
 		while (list) {
-			struct commit *commit = list->item;
-			struct commit_list *next = list->next;
+			commit = list->item;
+			next = list->next;
 			free(list);
 			list = next;
 			tail = simplify_one(revs, commit, tail);
@@ -1545,9 +2067,10 @@
 	revs->commits = NULL;
 	tail = &revs->commits;
 	while (list) {
-		struct commit *commit = list->item;
-		struct commit_list *next = list->next;
 		struct merge_simplify_state *st;
+
+		commit = list->item;
+		next = list->next;
 		free(list);
 		list = next;
 		st = locate_simplify_state(revs, commit);
@@ -1568,10 +2091,16 @@
 	}
 }
 
+void reset_revision_walk(void)
+{
+	clear_object_flags(SEEN | ADDED | SHOWN);
+}
+
 int prepare_revision_walk(struct rev_info *revs)
 {
 	int nr = revs->pending.nr;
 	struct object_array_entry *e, *list;
+	struct commit_list **next = &revs->commits;
 
 	e = list = revs->pending.objects;
 	revs->pending.nr = 0;
@@ -1582,12 +2111,14 @@
 		if (commit) {
 			if (!(commit->object.flags & SEEN)) {
 				commit->object.flags |= SEEN;
-				insert_by_date(commit, &revs->commits);
+				next = commit_list_append(commit, next);
 			}
 		}
 		e++;
 	}
-	free(list);
+	commit_list_sort_by_date(&revs->commits);
+	if (!revs->leak_pending)
+		free(list);
 
 	if (revs->no_walk)
 		return 0;
@@ -1606,7 +2137,7 @@
 enum rewrite_result {
 	rewrite_one_ok,
 	rewrite_one_noparents,
-	rewrite_one_error,
+	rewrite_one_error
 };
 
 static enum rewrite_result rewrite_one(struct rev_info *revs, struct commit **pp)
@@ -1652,10 +2183,9 @@
 
 static int commit_match(struct commit *commit, struct rev_info *opt)
 {
-	if (!opt->grep_filter.pattern_list)
+	if (!opt->grep_filter.pattern_list && !opt->grep_filter.header_list)
 		return 1;
 	return grep_buffer(&opt->grep_filter,
-			   NULL, /* we say nothing, not even filename */
 			   commit->buffer, strlen(commit->buffer));
 }
 
@@ -1664,7 +2194,7 @@
 	return (revs->rewrite_parents || revs->children.name);
 }
 
-enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)
+enum commit_action get_commit_action(struct rev_info *revs, struct commit *commit)
 {
 	if (commit->object.flags & SHOWN)
 		return commit_ignore;
@@ -1676,10 +2206,15 @@
 		return commit_ignore;
 	if (revs->min_age != -1 && (commit->date > revs->min_age))
 		return commit_ignore;
-	if (revs->no_merges && commit->parents && commit->parents->next)
-		return commit_ignore;
-	if (revs->merges_only && !(commit->parents && commit->parents->next))
-		return commit_ignore;
+	if (revs->min_parents || (revs->max_parents >= 0)) {
+		int n = 0;
+		struct commit_list *p;
+		for (p = commit->parents; p; p = p->next)
+			n++;
+		if ((n < revs->min_parents) ||
+		    ((revs->max_parents >= 0) && (n > revs->max_parents)))
+			return commit_ignore;
+	}
 	if (!commit_match(commit, revs))
 		return commit_ignore;
 	if (revs->prune && revs->dense) {
@@ -1692,12 +2227,23 @@
 			if (!commit->parents || !commit->parents->next)
 				return commit_ignore;
 		}
-		if (want_ancestry(revs) && rewrite_parents(revs, commit) < 0)
-			return commit_error;
 	}
 	return commit_show;
 }
 
+enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)
+{
+	enum commit_action action = get_commit_action(revs, commit);
+
+	if (action == commit_show &&
+	    !revs->show_all &&
+	    revs->prune && revs->dense && want_ancestry(revs)) {
+		if (rewrite_parents(revs, commit) < 0)
+			return commit_error;
+	}
+	return action;
+}
+
 static struct commit *get_revision_1(struct rev_info *revs)
 {
 	if (!revs->commits)
@@ -1710,8 +2256,10 @@
 		revs->commits = entry->next;
 		free(entry);
 
-		if (revs->reflog_info)
+		if (revs->reflog_info) {
 			fake_reflog_parent(revs->reflog_info, commit);
+			commit->object.flags &= ~(ADDED | SEEN | SHOWN);
+		}
 
 		/*
 		 * If we haven't done the list limiting, we need to look at
@@ -1821,29 +2369,28 @@
 	}
 
 	/*
-	 * Now pick up what they want to give us
+	 * If our max_count counter has reached zero, then we are done. We
+	 * don't simply return NULL because we still might need to show
+	 * boundary commits. But we want to avoid calling get_revision_1, which
+	 * might do a considerable amount of work finding the next commit only
+	 * for us to throw it away.
+	 *
+	 * If it is non-zero, then either we don't have a max_count at all
+	 * (-1), or it is still counting, in which case we decrement.
 	 */
-	c = get_revision_1(revs);
-	if (c) {
-		while (0 < revs->skip_count) {
-			revs->skip_count--;
-			c = get_revision_1(revs);
-			if (!c)
-				break;
+	if (revs->max_count) {
+		c = get_revision_1(revs);
+		if (c) {
+			while (0 < revs->skip_count) {
+				revs->skip_count--;
+				c = get_revision_1(revs);
+				if (!c)
+					break;
+			}
 		}
-	}
 
-	/*
-	 * Check the max_count.
-	 */
-	switch (revs->max_count) {
-	case -1:
-		break;
-	case 0:
-		c = NULL;
-		break;
-	default:
-		revs->max_count--;
+		if (revs->max_count > 0)
+			revs->max_count--;
 	}
 
 	if (c)
@@ -1913,3 +2460,32 @@
 		graph_update(revs->graph, c);
 	return c;
 }
+
+char *get_revision_mark(const struct rev_info *revs, const struct commit *commit)
+{
+	if (commit->object.flags & BOUNDARY)
+		return "-";
+	else if (commit->object.flags & UNINTERESTING)
+		return "^";
+	else if (commit->object.flags & PATCHSAME)
+		return "=";
+	else if (!revs || revs->left_right) {
+		if (commit->object.flags & SYMMETRIC_LEFT)
+			return "<";
+		else
+			return ">";
+	} else if (revs->graph)
+		return "*";
+	else if (revs->cherry_mark)
+		return "+";
+	return "";
+}
+
+void put_revision_mark(const struct rev_info *revs, const struct commit *commit)
+{
+	char *mark = get_revision_mark(revs, commit);
+	if (!strlen(mark))
+		return;
+	fputs(mark, stdout);
+	putchar(' ');
+}
diff --git a/revision.h b/revision.h
index fb74492..cb5ab35 100644
--- a/revision.h
+++ b/revision.h
@@ -3,6 +3,7 @@
 
 #include "parse-options.h"
 #include "grep.h"
+#include "notes.h"
 
 #define SEEN		(1u<<0)
 #define UNINTERESTING   (1u<<1)
@@ -13,10 +14,32 @@
 #define CHILD_SHOWN	(1u<<6)
 #define ADDED		(1u<<7)	/* Parents already parsed and added? */
 #define SYMMETRIC_LEFT	(1u<<8)
-#define ALL_REV_FLAGS	((1u<<9)-1)
+#define PATCHSAME	(1u<<9)
+#define ALL_REV_FLAGS	((1u<<10)-1)
+
+#define DECORATE_SHORT_REFS	1
+#define DECORATE_FULL_REFS	2
 
 struct rev_info;
 struct log_info;
+struct string_list;
+
+struct rev_cmdline_info {
+	unsigned int nr;
+	unsigned int alloc;
+	struct rev_cmdline_entry {
+		struct object *item;
+		const char *name;
+		enum {
+			REV_CMD_REF,
+			REV_CMD_PARENTS_ONLY,
+			REV_CMD_LEFT,
+			REV_CMD_RIGHT,
+			REV_CMD_REV
+		} whence;
+		unsigned flags;
+	} *rev;
+};
 
 struct rev_info {
 	/* Starting list */
@@ -26,17 +49,19 @@
 	/* Parents of shown commits */
 	struct object_array boundary_commits;
 
+	/* The end-points specified by the end user */
+	struct rev_cmdline_info cmdline;
+
 	/* Basic information */
 	const char *prefix;
 	const char *def;
-	void *prune_data;
-	unsigned int early_output;
+	struct pathspec prune_data;
+	unsigned int	early_output:1,
+			ignore_missing:1;
 
 	/* Traversal flags */
 	unsigned int	dense:1,
 			prune:1,
-			no_merges:1,
-			merges_only:1,
 			no_walk:1,
 			show_all:1,
 			remove_empty_trees:1,
@@ -48,11 +73,15 @@
 			tag_objects:1,
 			tree_objects:1,
 			blob_objects:1,
+			verify_objects:1,
 			edge_hint:1,
 			limited:1,
 			unpacked:1,
 			boundary:2,
+			count:1,
 			left_right:1,
+			left_only:1,
+			right_only:1,
 			rewrite_parents:1,
 			print_parents:1,
 			show_source:1,
@@ -60,6 +89,9 @@
 			reverse:1,
 			reverse_output_stage:1,
 			cherry_pick:1,
+			cherry_mark:1,
+			bisect:1,
+			ancestry_path:1,
 			first_parent_only:1;
 
 	/* Diff flags */
@@ -76,9 +108,19 @@
 	/* Format info */
 	unsigned int	shown_one:1,
 			show_merge:1,
+			show_notes:1,
+			show_notes_given:1,
+			show_signature:1,
+			pretty_given:1,
 			abbrev_commit:1,
+			abbrev_commit_given:1,
 			use_terminator:1,
-			missing_newline:1;
+			missing_newline:1,
+			date_mode_explicit:1,
+			preserve_subject:1;
+	unsigned int	disable_stdin:1;
+	unsigned int	leak_pending:1;
+
 	enum date_mode date_mode;
 
 	unsigned int	abbrev;
@@ -108,6 +150,8 @@
 	int max_count;
 	unsigned long max_age;
 	unsigned long min_age;
+	int min_parents;
+	int max_parents;
 
 	/* diff info for patches and for paths limiting */
 	struct diff_options diffopt;
@@ -116,6 +160,14 @@
 	struct reflog_walk_info *reflog_info;
 	struct decoration children;
 	struct decoration merge_simplification;
+
+	/* notes-specific options: which refs to show */
+	struct display_notes_opt notes_opt;
+
+	/* commit counts */
+	int count_left;
+	int count_right;
+	int count_same;
 };
 
 #define REV_TREE_SAME		0
@@ -124,20 +176,31 @@
 #define REV_TREE_DIFFERENT	3	/* Mixed changes */
 
 /* revision.c */
-void read_revisions_from_stdin(struct rev_info *revs);
-
 typedef void (*show_early_output_fn_t)(struct rev_info *, struct commit_list *);
 extern volatile show_early_output_fn_t show_early_output;
 
+struct setup_revision_opt {
+	const char *def;
+	void (*tweak)(struct rev_info *, struct setup_revision_opt *);
+	const char *submodule;
+	int assume_dashdash;
+	unsigned revarg_opt;
+};
+
 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 int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct setup_revision_opt *);
 extern void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
 				 const struct option *options,
 				 const char * const usagestr[]);
-extern int handle_revision_arg(const char *arg, struct rev_info *revs,int flags,int cant_be_filename);
+#define REVARG_CANNOT_BE_FILENAME 01
+#define REVARG_COMMITTISH 02
+extern int handle_revision_arg(const char *arg, struct rev_info *revs, int flags, unsigned revarg_opt);
 
+extern void reset_revision_walk(void);
 extern int prepare_revision_walk(struct rev_info *revs);
 extern struct commit *get_revision(struct rev_info *revs);
+extern char *get_revision_mark(const struct rev_info *revs, const struct commit *commit);
+extern void put_revision_mark(const struct rev_info *revs, const struct commit *commit);
 
 extern void mark_parents_uninteresting(struct commit *commit);
 extern void mark_tree_uninteresting(struct tree *tree);
@@ -150,12 +213,15 @@
 
 char *path_name(const struct name_path *path, const char *name);
 
+extern void show_object_with_name(FILE *, struct object *, const struct name_path *, const char *);
+
 extern void add_object(struct object *obj,
 		       struct object_array *p,
 		       struct name_path *path,
 		       const char *name);
 
 extern void add_pending_object(struct rev_info *revs, struct object *obj, const char *name);
+extern void add_pending_sha1(struct rev_info *revs, const char *name, const unsigned char *sha1, unsigned int flags);
 
 extern void add_head_to_pending(struct rev_info *);
 
@@ -165,6 +231,7 @@
 	commit_error
 };
 
+extern enum commit_action get_commit_action(struct rev_info *revs, struct commit *commit);
 extern enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit);
 
 #endif
diff --git a/run-command.c b/run-command.c
index ff3d8e2..606791d 100644
--- a/run-command.c
+++ b/run-command.c
@@ -1,6 +1,69 @@
 #include "cache.h"
 #include "run-command.h"
 #include "exec_cmd.h"
+#include "sigchain.h"
+#include "argv-array.h"
+
+#ifndef SHELL_PATH
+# define SHELL_PATH "/bin/sh"
+#endif
+
+struct child_to_clean {
+	pid_t pid;
+	struct child_to_clean *next;
+};
+static struct child_to_clean *children_to_clean;
+static int installed_child_cleanup_handler;
+
+static void cleanup_children(int sig)
+{
+	while (children_to_clean) {
+		struct child_to_clean *p = children_to_clean;
+		children_to_clean = p->next;
+		kill(p->pid, sig);
+		free(p);
+	}
+}
+
+static void cleanup_children_on_signal(int sig)
+{
+	cleanup_children(sig);
+	sigchain_pop(sig);
+	raise(sig);
+}
+
+static void cleanup_children_on_exit(void)
+{
+	cleanup_children(SIGTERM);
+}
+
+static void mark_child_for_cleanup(pid_t pid)
+{
+	struct child_to_clean *p = xmalloc(sizeof(*p));
+	p->pid = pid;
+	p->next = children_to_clean;
+	children_to_clean = p;
+
+	if (!installed_child_cleanup_handler) {
+		atexit(cleanup_children_on_exit);
+		sigchain_push_common(cleanup_children_on_signal);
+		installed_child_cleanup_handler = 1;
+	}
+}
+
+static void clear_child_for_cleanup(pid_t pid)
+{
+	struct child_to_clean **last, *p;
+
+	last = &children_to_clean;
+	for (p = children_to_clean; p; p = p->next) {
+		if (p->pid == pid) {
+			*last = p->next;
+			free(p);
+			return;
+		}
+	}
+}
 
 static inline void close_pair(int fd[2])
 {
@@ -8,17 +71,205 @@
 	close(fd[1]);
 }
 
+#ifndef WIN32
 static inline void dup_devnull(int to)
 {
 	int fd = open("/dev/null", O_RDWR);
 	dup2(fd, to);
 	close(fd);
 }
+#endif
+
+static char *locate_in_PATH(const char *file)
+{
+	const char *p = getenv("PATH");
+	struct strbuf buf = STRBUF_INIT;
+
+	if (!p || !*p)
+		return NULL;
+
+	while (1) {
+		const char *end = strchrnul(p, ':');
+
+		strbuf_reset(&buf);
+
+		/* POSIX specifies an empty entry as the current directory. */
+		if (end != p) {
+			strbuf_add(&buf, p, end - p);
+			strbuf_addch(&buf, '/');
+		}
+		strbuf_addstr(&buf, file);
+
+		if (!access(buf.buf, F_OK))
+			return strbuf_detach(&buf, NULL);
+
+		if (!*end)
+			break;
+		p = end + 1;
+	}
+
+	strbuf_release(&buf);
+	return NULL;
+}
+
+static int exists_in_PATH(const char *file)
+{
+	char *r = locate_in_PATH(file);
+	free(r);
+	return r != NULL;
+}
+
+int sane_execvp(const char *file, char * const argv[])
+{
+	if (!execvp(file, argv))
+		return 0; /* cannot happen ;-) */
+
+	/*
+	 * When a command can't be found because one of the directories
+	 * listed in $PATH is unsearchable, execvp reports EACCES, but
+	 * careful usability testing (read: analysis of occasional bug
+	 * reports) reveals that "No such file or directory" is more
+	 * intuitive.
+	 *
+	 * We avoid commands with "/", because execvp will not do $PATH
+	 * lookups in that case.
+	 *
+	 * The reassignment of EACCES to errno looks like a no-op below,
+	 * but we need to protect against exists_in_PATH overwriting errno.
+	 */
+	if (errno == EACCES && !strchr(file, '/'))
+		errno = exists_in_PATH(file) ? EACCES : ENOENT;
+	return -1;
+}
+
+static const char **prepare_shell_cmd(const char **argv)
+{
+	int argc, nargc = 0;
+	const char **nargv;
+
+	for (argc = 0; argv[argc]; argc++)
+		; /* just counting */
+	/* +1 for NULL, +3 for "sh -c" plus extra $0 */
+	nargv = xmalloc(sizeof(*nargv) * (argc + 1 + 3));
+
+	if (argc < 1)
+		die("BUG: shell command is empty");
+
+	if (strcspn(argv[0], "|&;<>()$`\\\"' \t\n*?[#~=%") != strlen(argv[0])) {
+#ifndef WIN32
+		nargv[nargc++] = SHELL_PATH;
+#else
+		nargv[nargc++] = "sh";
+#endif
+		nargv[nargc++] = "-c";
+
+		if (argc < 2)
+			nargv[nargc++] = argv[0];
+		else {
+			struct strbuf arg0 = STRBUF_INIT;
+			strbuf_addf(&arg0, "%s \"$@\"", argv[0]);
+			nargv[nargc++] = strbuf_detach(&arg0, NULL);
+		}
+	}
+
+	for (argc = 0; argv[argc]; argc++)
+		nargv[nargc++] = argv[argc];
+	nargv[nargc] = NULL;
+
+	return nargv;
+}
+
+#ifndef WIN32
+static int execv_shell_cmd(const char **argv)
+{
+	const char **nargv = prepare_shell_cmd(argv);
+	trace_argv_printf(nargv, "trace: exec:");
+	sane_execvp(nargv[0], (char **)nargv);
+	free(nargv);
+	return -1;
+}
+#endif
+
+#ifndef WIN32
+static int child_err = 2;
+static int child_notifier = -1;
+
+static void notify_parent(void)
+{
+	/*
+	 * execvp failed.  If possible, we'd like to let start_command
+	 * know, so failures like ENOENT can be handled right away; but
+	 * otherwise, finish_command will still report the error.
+	 */
+	xwrite(child_notifier, "", 1);
+}
+
+static NORETURN void die_child(const char *err, va_list params)
+{
+	vwritef(child_err, "fatal: ", err, params);
+	exit(128);
+}
+
+static void error_child(const char *err, va_list params)
+{
+	vwritef(child_err, "error: ", err, params);
+}
+#endif
+
+static inline void set_cloexec(int fd)
+{
+	int flags = fcntl(fd, F_GETFD);
+	if (flags >= 0)
+		fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
+}
+
+static int wait_or_whine(pid_t pid, const char *argv0, int silent_exec_failure)
+{
+	int status, code = -1;
+	pid_t waiting;
+	int failed_errno = 0;
+
+	while ((waiting = waitpid(pid, &status, 0)) < 0 && errno == EINTR)
+		;	/* nothing */
+
+	if (waiting < 0) {
+		failed_errno = errno;
+		error("waitpid for %s failed: %s", argv0, strerror(errno));
+	} else if (waiting != pid) {
+		error("waitpid is confused (%s)", argv0);
+	} else if (WIFSIGNALED(status)) {
+		code = WTERMSIG(status);
+		error("%s died of signal %d", argv0, code);
+		/*
+		 * This return value is chosen so that code & 0xff
+		 * mimics the exit code that a POSIX shell would report for
+		 * a program that died from this signal.
+		 */
+		code -= 128;
+	} else if (WIFEXITED(status)) {
+		code = WEXITSTATUS(status);
+		/*
+		 * Convert special exit code when execvp failed.
+		 */
+		if (code == 127) {
+			code = -1;
+			failed_errno = ENOENT;
+		}
+	} else {
+		error("waitpid is confused (%s)", argv0);
+	}
+
+	clear_child_for_cleanup(pid);
+
+	errno = failed_errno;
+	return code;
+}
 
 int start_command(struct child_process *cmd)
 {
 	int need_in, need_out, need_err;
 	int fdin[2], fdout[2], fderr[2];
+	int failed_errno = failed_errno;
 
 	/*
 	 * In case of errors we must keep the promise to close FDs
@@ -28,9 +279,10 @@
 	need_in = !cmd->no_stdin && cmd->in < 0;
 	if (need_in) {
 		if (pipe(fdin) < 0) {
+			failed_errno = errno;
 			if (cmd->out > 0)
 				close(cmd->out);
-			return -ERR_RUN_COMMAND_PIPE;
+			goto fail_pipe;
 		}
 		cmd->in = fdin[1];
 	}
@@ -40,11 +292,12 @@
 		&& cmd->out < 0;
 	if (need_out) {
 		if (pipe(fdout) < 0) {
+			failed_errno = errno;
 			if (need_in)
 				close_pair(fdin);
 			else if (cmd->in)
 				close(cmd->in);
-			return -ERR_RUN_COMMAND_PIPE;
+			goto fail_pipe;
 		}
 		cmd->out = fdout[0];
 	}
@@ -52,6 +305,7 @@
 	need_err = !cmd->no_stderr && cmd->err < 0;
 	if (need_err) {
 		if (pipe(fderr) < 0) {
+			failed_errno = errno;
 			if (need_in)
 				close_pair(fdin);
 			else if (cmd->in)
@@ -60,17 +314,43 @@
 				close_pair(fdout);
 			else if (cmd->out)
 				close(cmd->out);
-			return -ERR_RUN_COMMAND_PIPE;
+fail_pipe:
+			error("cannot create pipe for %s: %s",
+				cmd->argv[0], strerror(failed_errno));
+			errno = failed_errno;
+			return -1;
 		}
 		cmd->err = fderr[0];
 	}
 
 	trace_argv_printf(cmd->argv, "trace: run_command:");
-
-#ifndef __MINGW32__
 	fflush(NULL);
+
+#ifndef WIN32
+{
+	int notify_pipe[2];
+	if (pipe(notify_pipe))
+		notify_pipe[0] = notify_pipe[1] = -1;
+
 	cmd->pid = fork();
 	if (!cmd->pid) {
+		/*
+		 * Redirect the channel to write syscall error messages to
+		 * before redirecting the process's stderr so that all die()
+		 * in subsequent call paths use the parent's stderr.
+		 */
+		if (cmd->no_stderr || need_err) {
+			child_err = dup(2);
+			set_cloexec(child_err);
+		}
+		set_die_routine(die_child);
+		set_error_routine(error_child);
+
+		close(notify_pipe[0]);
+		set_cloexec(notify_pipe[1]);
+		child_notifier = notify_pipe[1];
+		atexit(notify_parent);
+
 		if (cmd->no_stdin)
 			dup_devnull(0);
 		else if (need_in) {
@@ -86,6 +366,9 @@
 		else if (need_err) {
 			dup2(fderr[1], 2);
 			close_pair(fderr);
+		} else if (cmd->err > 1) {
+			dup2(cmd->err, 2);
+			close(cmd->err);
 		}
 
 		if (cmd->no_stdout)
@@ -111,68 +394,104 @@
 					unsetenv(*cmd->env);
 			}
 		}
-		if (cmd->preexec_cb)
+		if (cmd->preexec_cb) {
+			/*
+			 * We cannot predict what the pre-exec callback does.
+			 * Forgo parent notification.
+			 */
+			close(child_notifier);
+			child_notifier = -1;
+
 			cmd->preexec_cb();
+		}
 		if (cmd->git_cmd) {
 			execv_git_cmd(cmd->argv);
+		} else if (cmd->use_shell) {
+			execv_shell_cmd(cmd->argv);
 		} else {
-			execvp(cmd->argv[0], (char *const*) cmd->argv);
+			sane_execvp(cmd->argv[0], (char *const*) cmd->argv);
 		}
-		trace_printf("trace: exec '%s' failed: %s\n", cmd->argv[0],
-				strerror(errno));
-		exit(127);
+		if (errno == ENOENT) {
+			if (!cmd->silent_exec_failure)
+				error("cannot run %s: %s", cmd->argv[0],
+					strerror(ENOENT));
+			exit(127);
+		} else {
+			die_errno("cannot exec '%s'", cmd->argv[0]);
+		}
 	}
+	if (cmd->pid < 0)
+		error("cannot fork() for %s: %s", cmd->argv[0],
+			strerror(failed_errno = errno));
+	else if (cmd->clean_on_exit)
+		mark_child_for_cleanup(cmd->pid);
+
+	/*
+	 * Wait for child's execvp. If the execvp succeeds (or if fork()
+	 * failed), EOF is seen immediately by the parent. Otherwise, the
+	 * child process sends a single byte.
+	 * Note that use of this infrastructure is completely advisory,
+	 * therefore, we keep error checks minimal.
+	 */
+	close(notify_pipe[1]);
+	if (read(notify_pipe[0], &notify_pipe[1], 1) == 1) {
+		/*
+		 * At this point we know that fork() succeeded, but execvp()
+		 * failed. Errors have been reported to our stderr.
+		 */
+		wait_or_whine(cmd->pid, cmd->argv[0],
+			      cmd->silent_exec_failure);
+		failed_errno = errno;
+		cmd->pid = -1;
+	}
+	close(notify_pipe[0]);
+
+}
 #else
-	int s0 = -1, s1 = -1, s2 = -1;	/* backups of stdin, stdout, stderr */
+{
+	int fhin = 0, fhout = 1, fherr = 2;
 	const char **sargv = cmd->argv;
 	char **env = environ;
 
-	if (cmd->no_stdin) {
-		s0 = dup(0);
-		dup_devnull(0);
-	} else if (need_in) {
-		s0 = dup(0);
-		dup2(fdin[0], 0);
-	} else if (cmd->in) {
-		s0 = dup(0);
-		dup2(cmd->in, 0);
-	}
+	if (cmd->no_stdin)
+		fhin = open("/dev/null", O_RDWR);
+	else if (need_in)
+		fhin = dup(fdin[0]);
+	else if (cmd->in)
+		fhin = dup(cmd->in);
 
-	if (cmd->no_stderr) {
-		s2 = dup(2);
-		dup_devnull(2);
-	} else if (need_err) {
-		s2 = dup(2);
-		dup2(fderr[1], 2);
-	}
+	if (cmd->no_stderr)
+		fherr = open("/dev/null", O_RDWR);
+	else if (need_err)
+		fherr = dup(fderr[1]);
+	else if (cmd->err > 2)
+		fherr = dup(cmd->err);
 
-	if (cmd->no_stdout) {
-		s1 = dup(1);
-		dup_devnull(1);
-	} else if (cmd->stdout_to_stderr) {
-		s1 = dup(1);
-		dup2(2, 1);
-	} else if (need_out) {
-		s1 = dup(1);
-		dup2(fdout[1], 1);
-	} else if (cmd->out > 1) {
-		s1 = dup(1);
-		dup2(cmd->out, 1);
-	}
+	if (cmd->no_stdout)
+		fhout = open("/dev/null", O_RDWR);
+	else if (cmd->stdout_to_stderr)
+		fhout = dup(fherr);
+	else if (need_out)
+		fhout = dup(fdout[1]);
+	else if (cmd->out > 1)
+		fhout = dup(cmd->out);
 
-	if (cmd->dir)
-		die("chdir in start_command() not implemented");
-	if (cmd->env) {
-		env = copy_environ();
-		for (; *cmd->env; cmd->env++)
-			env = env_setenv(env, *cmd->env);
-	}
+	if (cmd->env)
+		env = make_augmented_environ(cmd->env);
 
 	if (cmd->git_cmd) {
 		cmd->argv = prepare_git_cmd(cmd->argv);
+	} else if (cmd->use_shell) {
+		cmd->argv = prepare_shell_cmd(cmd->argv);
 	}
 
-	cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, env);
+	cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, env, cmd->dir,
+				  fhin, fhout, fherr);
+	failed_errno = errno;
+	if (cmd->pid < 0 && (!cmd->silent_exec_failure || errno != ENOENT))
+		error("cannot spawn %s: %s", cmd->argv[0], strerror(errno));
+	if (cmd->clean_on_exit && cmd->pid >= 0)
+		mark_child_for_cleanup(cmd->pid);
 
 	if (cmd->env)
 		free_environ(env);
@@ -180,16 +499,16 @@
 		free(cmd->argv);
 
 	cmd->argv = sargv;
-	if (s0 >= 0)
-		dup2(s0, 0), close(s0);
-	if (s1 >= 0)
-		dup2(s1, 1), close(s1);
-	if (s2 >= 0)
-		dup2(s2, 2), close(s2);
+	if (fhin != 0)
+		close(fhin);
+	if (fhout != 1)
+		close(fhout);
+	if (fherr != 2)
+		close(fherr);
+}
 #endif
 
 	if (cmd->pid < 0) {
-		int err = errno;
 		if (need_in)
 			close_pair(fdin);
 		else if (cmd->in)
@@ -200,9 +519,10 @@
 			close(cmd->out);
 		if (need_err)
 			close_pair(fderr);
-		return err == ENOENT ?
-			-ERR_RUN_COMMAND_EXEC :
-			-ERR_RUN_COMMAND_FORK;
+		else if (cmd->err)
+			close(cmd->err);
+		errno = failed_errno;
+		return -1;
 	}
 
 	if (need_in)
@@ -217,44 +537,15 @@
 
 	if (need_err)
 		close(fderr[1]);
+	else if (cmd->err)
+		close(cmd->err);
 
 	return 0;
 }
 
-static int wait_or_whine(pid_t pid)
-{
-	for (;;) {
-		int status, code;
-		pid_t waiting = waitpid(pid, &status, 0);
-
-		if (waiting < 0) {
-			if (errno == EINTR)
-				continue;
-			error("waitpid failed (%s)", strerror(errno));
-			return -ERR_RUN_COMMAND_WAITPID;
-		}
-		if (waiting != pid)
-			return -ERR_RUN_COMMAND_WAITPID_WRONG_PID;
-		if (WIFSIGNALED(status))
-			return -ERR_RUN_COMMAND_WAITPID_SIGNAL;
-
-		if (!WIFEXITED(status))
-			return -ERR_RUN_COMMAND_WAITPID_NOEXIT;
-		code = WEXITSTATUS(status);
-		switch (code) {
-		case 127:
-			return -ERR_RUN_COMMAND_EXEC;
-		case 0:
-			return 0;
-		default:
-			return -code;
-		}
-	}
-}
-
 int finish_command(struct child_process *cmd)
 {
-	return wait_or_whine(cmd->pid);
+	return wait_or_whine(cmd->pid, cmd->argv[0], cmd->silent_exec_failure);
 }
 
 int run_command(struct child_process *cmd)
@@ -274,6 +565,9 @@
 	cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0;
 	cmd->git_cmd = opt & RUN_GIT_CMD ? 1 : 0;
 	cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;
+	cmd->silent_exec_failure = opt & RUN_SILENT_EXEC_FAILURE ? 1 : 0;
+	cmd->use_shell = opt & RUN_USING_SHELL ? 1 : 0;
+	cmd->clean_on_exit = opt & RUN_CLEAN_ON_EXIT ? 1 : 0;
 }
 
 int run_command_v_opt(const char **argv, int opt)
@@ -292,90 +586,182 @@
 	return run_command(&cmd);
 }
 
-#ifdef __MINGW32__
-static __stdcall unsigned run_thread(void *data)
+#ifndef NO_PTHREADS
+static pthread_t main_thread;
+static int main_thread_set;
+static pthread_key_t async_key;
+
+static void *run_thread(void *data)
 {
 	struct async *async = data;
-	return async->proc(async->fd_for_proc, async->data);
+	intptr_t ret;
+
+	pthread_setspecific(async_key, async);
+	ret = async->proc(async->proc_in, async->proc_out, async->data);
+	return (void *)ret;
+}
+
+static NORETURN void die_async(const char *err, va_list params)
+{
+	vreportf("fatal: ", err, params);
+
+	if (!pthread_equal(main_thread, pthread_self())) {
+		struct async *async = pthread_getspecific(async_key);
+		if (async->proc_in >= 0)
+			close(async->proc_in);
+		if (async->proc_out >= 0)
+			close(async->proc_out);
+		pthread_exit((void *)128);
+	}
+
+	exit(128);
 }
 #endif
 
 int start_async(struct async *async)
 {
-	int pipe_out[2];
+	int need_in, need_out;
+	int fdin[2], fdout[2];
+	int proc_in, proc_out;
 
-	if (pipe(pipe_out) < 0)
-		return error("cannot create pipe: %s", strerror(errno));
-	async->out = pipe_out[0];
+	need_in = async->in < 0;
+	if (need_in) {
+		if (pipe(fdin) < 0) {
+			if (async->out > 0)
+				close(async->out);
+			return error("cannot create pipe: %s", strerror(errno));
+		}
+		async->in = fdin[1];
+	}
 
-#ifndef __MINGW32__
+	need_out = async->out < 0;
+	if (need_out) {
+		if (pipe(fdout) < 0) {
+			if (need_in)
+				close_pair(fdin);
+			else if (async->in)
+				close(async->in);
+			return error("cannot create pipe: %s", strerror(errno));
+		}
+		async->out = fdout[0];
+	}
+
+	if (need_in)
+		proc_in = fdin[0];
+	else if (async->in)
+		proc_in = async->in;
+	else
+		proc_in = -1;
+
+	if (need_out)
+		proc_out = fdout[1];
+	else if (async->out)
+		proc_out = async->out;
+	else
+		proc_out = -1;
+
+#ifdef NO_PTHREADS
 	/* Flush stdio before fork() to avoid cloning buffers */
 	fflush(NULL);
 
 	async->pid = fork();
 	if (async->pid < 0) {
 		error("fork (async) failed: %s", strerror(errno));
-		close_pair(pipe_out);
-		return -1;
+		goto error;
 	}
 	if (!async->pid) {
-		close(pipe_out[0]);
-		exit(!!async->proc(pipe_out[1], async->data));
+		if (need_in)
+			close(fdin[1]);
+		if (need_out)
+			close(fdout[0]);
+		exit(!!async->proc(proc_in, proc_out, async->data));
 	}
-	close(pipe_out[1]);
+
+	mark_child_for_cleanup(async->pid);
+
+	if (need_in)
+		close(fdin[0]);
+	else if (async->in)
+		close(async->in);
+
+	if (need_out)
+		close(fdout[1]);
+	else if (async->out)
+		close(async->out);
 #else
-	async->fd_for_proc = pipe_out[1];
-	async->tid = (HANDLE) _beginthreadex(NULL, 0, run_thread, async, 0, NULL);
-	if (!async->tid) {
-		error("cannot create thread: %s", strerror(errno));
-		close_pair(pipe_out);
-		return -1;
+	if (!main_thread_set) {
+		/*
+		 * We assume that the first time that start_async is called
+		 * it is from the main thread.
+		 */
+		main_thread_set = 1;
+		main_thread = pthread_self();
+		pthread_key_create(&async_key, NULL);
+		set_die_routine(die_async);
+	}
+
+	if (proc_in >= 0)
+		set_cloexec(proc_in);
+	if (proc_out >= 0)
+		set_cloexec(proc_out);
+	async->proc_in = proc_in;
+	async->proc_out = proc_out;
+	{
+		int err = pthread_create(&async->tid, NULL, run_thread, async);
+		if (err) {
+			error("cannot create thread: %s", strerror(err));
+			goto error;
+		}
 	}
 #endif
 	return 0;
+
+error:
+	if (need_in)
+		close_pair(fdin);
+	else if (async->in)
+		close(async->in);
+
+	if (need_out)
+		close_pair(fdout);
+	else if (async->out)
+		close(async->out);
+	return -1;
 }
 
 int finish_async(struct async *async)
 {
-#ifndef __MINGW32__
-	int ret = 0;
-
-	if (wait_or_whine(async->pid))
-		ret = error("waitpid (async) failed");
+#ifdef NO_PTHREADS
+	return wait_or_whine(async->pid, "child process", 0);
 #else
-	DWORD ret = 0;
-	if (WaitForSingleObject(async->tid, INFINITE) != WAIT_OBJECT_0)
-		ret = error("waiting for thread failed: %lu", GetLastError());
-	else if (!GetExitCodeThread(async->tid, &ret))
-		ret = error("cannot get thread exit code: %lu", GetLastError());
-	CloseHandle(async->tid);
+	void *ret = (void *)(intptr_t)(-1);
+
+	if (pthread_join(async->tid, &ret))
+		error("pthread_join failed");
+	return (int)(intptr_t)ret;
 #endif
-	return ret;
 }
 
 int run_hook(const char *index_file, const char *name, ...)
 {
 	struct child_process hook;
-	const char **argv = NULL, *env[2];
+	struct argv_array argv = ARGV_ARRAY_INIT;
+	const char *p, *env[2];
 	char index[PATH_MAX];
 	va_list args;
 	int ret;
-	size_t i = 0, alloc = 0;
 
 	if (access(git_path("hooks/%s", name), X_OK) < 0)
 		return 0;
 
 	va_start(args, name);
-	ALLOC_GROW(argv, i + 1, alloc);
-	argv[i++] = git_path("hooks/%s", name);
-	while (argv[i-1]) {
-		ALLOC_GROW(argv, i + 1, alloc);
-		argv[i++] = va_arg(args, const char *);
-	}
+	argv_array_push(&argv, git_path("hooks/%s", name));
+	while ((p = va_arg(args, const char *)))
+		argv_array_push(&argv, p);
 	va_end(args);
 
 	memset(&hook, 0, sizeof(hook));
-	hook.argv = argv;
+	hook.argv = argv.argv;
 	hook.no_stdin = 1;
 	hook.stdout_to_stderr = 1;
 	if (index_file) {
@@ -385,15 +771,7 @@
 		hook.env = env;
 	}
 
-	ret = start_command(&hook);
-	free(argv);
-	if (ret) {
-		warning("Could not spawn %s", argv[0]);
-		return ret;
-	}
-	ret = finish_command(&hook);
-	if (ret == -ERR_RUN_COMMAND_WAITPID_SIGNAL)
-		warning("%s exited due to uncaught signal", argv[0]);
-
+	ret = run_command(&hook);
+	argv_array_clear(&argv);
 	return ret;
 }
diff --git a/run-command.h b/run-command.h
index e345502..44f7d2b 100644
--- a/run-command.h
+++ b/run-command.h
@@ -1,16 +1,9 @@
 #ifndef RUN_COMMAND_H
 #define RUN_COMMAND_H
 
-enum {
-	ERR_RUN_COMMAND_FORK = 10000,
-	ERR_RUN_COMMAND_EXEC,
-	ERR_RUN_COMMAND_PIPE,
-	ERR_RUN_COMMAND_WAITPID,
-	ERR_RUN_COMMAND_WAITPID_WRONG_PID,
-	ERR_RUN_COMMAND_WAITPID_SIGNAL,
-	ERR_RUN_COMMAND_WAITPID_NOEXIT,
-};
-#define IS_RUN_COMMAND_ERR(x) (-(x) >= ERR_RUN_COMMAND_FORK)
+#ifndef NO_PTHREADS
+#include <pthread.h>
+#endif
 
 struct child_process {
 	const char **argv;
@@ -29,7 +22,7 @@
 	 * - Specify > 0 to set a channel to a particular FD as follows:
 	 *     .in: a readable FD, becomes child's stdin
 	 *     .out: a writable FD, becomes child's stdout/stderr
-	 *     .err > 0 not supported
+	 *     .err: a writable FD, becomes child's stderr
 	 *   The specified FD is closed by start_command(), even in case
 	 *   of errors!
 	 */
@@ -42,7 +35,10 @@
 	unsigned no_stdout:1;
 	unsigned no_stderr:1;
 	unsigned git_cmd:1; /* if this is to be git sub-command */
+	unsigned silent_exec_failure:1;
 	unsigned stdout_to_stderr:1;
+	unsigned use_shell:1;
+	unsigned clean_on_exit:1;
 	void (*preexec_cb)(void);
 };
 
@@ -55,6 +51,9 @@
 #define RUN_COMMAND_NO_STDIN 1
 #define RUN_GIT_CMD	     2	/*If this is to be git sub-command */
 #define RUN_COMMAND_STDOUT_TO_STDERR 4
+#define RUN_SILENT_EXEC_FAILURE 8
+#define RUN_USING_SHELL 16
+#define RUN_CLEAN_ON_EXIT 32
 int run_command_v_opt(const char **argv, int opt);
 
 /*
@@ -73,17 +72,20 @@
  */
 struct async {
 	/*
-	 * proc writes to fd and closes it;
+	 * proc reads from in; closes it before return
+	 * proc writes to out; closes it before return
 	 * returns 0 on success, non-zero on failure
 	 */
-	int (*proc)(int fd, void *data);
+	int (*proc)(int in, int out, void *data);
 	void *data;
+	int in;		/* caller writes here and closes it */
 	int out;	/* caller reads from here and closes it */
-#ifndef __MINGW32__
+#ifdef NO_PTHREADS
 	pid_t pid;
 #else
-	HANDLE tid;
-	int fd_for_proc;
+	pthread_t tid;
+	int proc_in;
+	int proc_out;
 #endif
 };
 
diff --git a/send-pack.h b/send-pack.h
index 1d7b1b3..05d7ab1 100644
--- a/send-pack.h
+++ b/send-pack.h
@@ -3,11 +3,15 @@
 
 struct send_pack_args {
 	unsigned verbose:1,
+		quiet:1,
+		porcelain:1,
+		progress:1,
 		send_mirror:1,
 		force_update:1,
 		use_thin_pack:1,
 		use_ofs_delta:1,
-		dry_run:1;
+		dry_run:1,
+		stateless_rpc:1;
 };
 
 int send_pack(struct send_pack_args *args,
diff --git a/sequencer.c b/sequencer.c
new file mode 100644
index 0000000..bf078f2
--- /dev/null
+++ b/sequencer.c
@@ -0,0 +1,1006 @@
+#include "cache.h"
+#include "sequencer.h"
+#include "dir.h"
+#include "object.h"
+#include "commit.h"
+#include "tag.h"
+#include "run-command.h"
+#include "exec_cmd.h"
+#include "utf8.h"
+#include "cache-tree.h"
+#include "diff.h"
+#include "revision.h"
+#include "rerere.h"
+#include "merge-recursive.h"
+#include "refs.h"
+#include "argv-array.h"
+
+#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
+
+void remove_sequencer_state(void)
+{
+	struct strbuf seq_dir = STRBUF_INIT;
+
+	strbuf_addf(&seq_dir, "%s", git_path(SEQ_DIR));
+	remove_dir_recursively(&seq_dir, 0);
+	strbuf_release(&seq_dir);
+}
+
+static const char *action_name(const struct replay_opts *opts)
+{
+	return opts->action == REPLAY_REVERT ? "revert" : "cherry-pick";
+}
+
+static char *get_encoding(const char *message);
+
+struct commit_message {
+	char *parent_label;
+	const char *label;
+	const char *subject;
+	char *reencoded_message;
+	const char *message;
+};
+
+static int get_message(struct commit *commit, struct commit_message *out)
+{
+	const char *encoding;
+	const char *abbrev, *subject;
+	int abbrev_len, subject_len;
+	char *q;
+
+	if (!commit->buffer)
+		return -1;
+	encoding = get_encoding(commit->buffer);
+	if (!encoding)
+		encoding = "UTF-8";
+	if (!git_commit_encoding)
+		git_commit_encoding = "UTF-8";
+
+	out->reencoded_message = NULL;
+	out->message = commit->buffer;
+	if (strcmp(encoding, git_commit_encoding))
+		out->reencoded_message = reencode_string(commit->buffer,
+					git_commit_encoding, encoding);
+	if (out->reencoded_message)
+		out->message = out->reencoded_message;
+
+	abbrev = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
+	abbrev_len = strlen(abbrev);
+
+	subject_len = find_commit_subject(out->message, &subject);
+
+	out->parent_label = xmalloc(strlen("parent of ") + abbrev_len +
+			      strlen("... ") + subject_len + 1);
+	q = out->parent_label;
+	q = mempcpy(q, "parent of ", strlen("parent of "));
+	out->label = q;
+	q = mempcpy(q, abbrev, abbrev_len);
+	q = mempcpy(q, "... ", strlen("... "));
+	out->subject = q;
+	q = mempcpy(q, subject, subject_len);
+	*q = '\0';
+	return 0;
+}
+
+static void free_message(struct commit_message *msg)
+{
+	free(msg->parent_label);
+	free(msg->reencoded_message);
+}
+
+static char *get_encoding(const char *message)
+{
+	const char *p = message, *eol;
+
+	while (*p && *p != '\n') {
+		for (eol = p + 1; *eol && *eol != '\n'; eol++)
+			; /* do nothing */
+		if (!prefixcmp(p, "encoding ")) {
+			char *result = xmalloc(eol - 8 - p);
+			strlcpy(result, p + 9, eol - 8 - p);
+			return result;
+		}
+		p = eol;
+		if (*p == '\n')
+			p++;
+	}
+	return NULL;
+}
+
+static void write_cherry_pick_head(struct commit *commit, const char *pseudoref)
+{
+	const char *filename;
+	int fd;
+	struct strbuf buf = STRBUF_INIT;
+
+	strbuf_addf(&buf, "%s\n", sha1_to_hex(commit->object.sha1));
+
+	filename = git_path("%s", pseudoref);
+	fd = open(filename, O_WRONLY | O_CREAT, 0666);
+	if (fd < 0)
+		die_errno(_("Could not open '%s' for writing"), filename);
+	if (write_in_full(fd, buf.buf, buf.len) != buf.len || close(fd))
+		die_errno(_("Could not write to '%s'"), filename);
+	strbuf_release(&buf);
+}
+
+static void print_advice(int show_hint, struct replay_opts *opts)
+{
+	char *msg = getenv("GIT_CHERRY_PICK_HELP");
+
+	if (msg) {
+		fprintf(stderr, "%s\n", msg);
+		/*
+		 * A conflict has occured but the porcelain
+		 * (typically rebase --interactive) wants to take care
+		 * of the commit itself so remove CHERRY_PICK_HEAD
+		 */
+		unlink(git_path("CHERRY_PICK_HEAD"));
+		return;
+	}
+
+	if (show_hint) {
+		if (opts->no_commit)
+			advise(_("after resolving the conflicts, mark the corrected paths\n"
+				 "with 'git add <paths>' or 'git rm <paths>'"));
+		else
+			advise(_("after resolving the conflicts, mark the corrected paths\n"
+				 "with 'git add <paths>' or 'git rm <paths>'\n"
+				 "and commit the result with 'git commit'"));
+	}
+}
+
+static void write_message(struct strbuf *msgbuf, const char *filename)
+{
+	static struct lock_file msg_file;
+
+	int msg_fd = hold_lock_file_for_update(&msg_file, filename,
+					       LOCK_DIE_ON_ERROR);
+	if (write_in_full(msg_fd, msgbuf->buf, msgbuf->len) < 0)
+		die_errno(_("Could not write to %s"), filename);
+	strbuf_release(msgbuf);
+	if (commit_lock_file(&msg_file) < 0)
+		die(_("Error wrapping up %s"), filename);
+}
+
+static struct tree *empty_tree(void)
+{
+	return lookup_tree(EMPTY_TREE_SHA1_BIN);
+}
+
+static int error_dirty_index(struct replay_opts *opts)
+{
+	if (read_cache_unmerged())
+		return error_resolve_conflict(action_name(opts));
+
+	/* Different translation strings for cherry-pick and revert */
+	if (opts->action == REPLAY_PICK)
+		error(_("Your local changes would be overwritten by cherry-pick."));
+	else
+		error(_("Your local changes would be overwritten by revert."));
+
+	if (advice_commit_before_merge)
+		advise(_("Commit your changes or stash them to proceed."));
+	return -1;
+}
+
+static int fast_forward_to(const unsigned char *to, const unsigned char *from)
+{
+	struct ref_lock *ref_lock;
+
+	read_cache();
+	if (checkout_fast_forward(from, to))
+		exit(1); /* the callee should have complained already */
+	ref_lock = lock_any_ref_for_update("HEAD", from, 0);
+	return write_ref_sha1(ref_lock, to, "cherry-pick");
+}
+
+static int do_recursive_merge(struct commit *base, struct commit *next,
+			      const char *base_label, const char *next_label,
+			      unsigned char *head, struct strbuf *msgbuf,
+			      struct replay_opts *opts)
+{
+	struct merge_options o;
+	struct tree *result, *next_tree, *base_tree, *head_tree;
+	int clean, index_fd;
+	const char **xopt;
+	static struct lock_file index_lock;
+
+	index_fd = hold_locked_index(&index_lock, 1);
+
+	read_cache();
+
+	init_merge_options(&o);
+	o.ancestor = base ? base_label : "(empty tree)";
+	o.branch1 = "HEAD";
+	o.branch2 = next ? next_label : "(empty tree)";
+
+	head_tree = parse_tree_indirect(head);
+	next_tree = next ? next->tree : empty_tree();
+	base_tree = base ? base->tree : empty_tree();
+
+	for (xopt = opts->xopts; xopt != opts->xopts + opts->xopts_nr; xopt++)
+		parse_merge_opt(&o, *xopt);
+
+	clean = merge_trees(&o,
+			    head_tree,
+			    next_tree, base_tree, &result);
+
+	if (active_cache_changed &&
+	    (write_cache(index_fd, active_cache, active_nr) ||
+	     commit_locked_index(&index_lock)))
+		/* TRANSLATORS: %s will be "revert" or "cherry-pick" */
+		die(_("%s: Unable to write new index file"), action_name(opts));
+	rollback_lock_file(&index_lock);
+
+	if (!clean) {
+		int i;
+		strbuf_addstr(msgbuf, "\nConflicts:\n");
+		for (i = 0; i < active_nr;) {
+			struct cache_entry *ce = active_cache[i++];
+			if (ce_stage(ce)) {
+				strbuf_addch(msgbuf, '\t');
+				strbuf_addstr(msgbuf, ce->name);
+				strbuf_addch(msgbuf, '\n');
+				while (i < active_nr && !strcmp(ce->name,
+						active_cache[i]->name))
+					i++;
+			}
+		}
+	}
+
+	return !clean;
+}
+
+static int is_index_unchanged(void)
+{
+	unsigned char head_sha1[20];
+	struct commit *head_commit;
+
+	if (!resolve_ref_unsafe("HEAD", head_sha1, 1, NULL))
+		return error(_("Could not resolve HEAD commit\n"));
+
+	head_commit = lookup_commit(head_sha1);
+
+	/*
+	 * If head_commit is NULL, check_commit, called from
+	 * lookup_commit, would have indicated that head_commit is not
+	 * a commit object already.  parse_commit() will return failure
+	 * without further complaints in such a case.  Otherwise, if
+	 * the commit is invalid, parse_commit() will complain.  So
+	 * there is nothing for us to say here.  Just return failure.
+	 */
+	if (parse_commit(head_commit))
+		return -1;
+
+	if (!active_cache_tree)
+		active_cache_tree = cache_tree();
+
+	if (!cache_tree_fully_valid(active_cache_tree))
+		if (cache_tree_update(active_cache_tree, active_cache,
+				  active_nr, 0))
+			return error(_("Unable to update cache tree\n"));
+
+	return !hashcmp(active_cache_tree->sha1, head_commit->tree->object.sha1);
+}
+
+/*
+ * If we are cherry-pick, and if the merge did not result in
+ * hand-editing, we will hit this commit and inherit the original
+ * author date and name.
+ * If we are revert, or if our cherry-pick results in a hand merge,
+ * we had better say that the current user is responsible for that.
+ */
+static int run_git_commit(const char *defmsg, struct replay_opts *opts,
+			  int allow_empty)
+{
+	struct argv_array array;
+	int rc;
+
+	argv_array_init(&array);
+	argv_array_push(&array, "commit");
+	argv_array_push(&array, "-n");
+
+	if (opts->signoff)
+		argv_array_push(&array, "-s");
+	if (!opts->edit) {
+		argv_array_push(&array, "-F");
+		argv_array_push(&array, defmsg);
+	}
+
+	if (allow_empty)
+		argv_array_push(&array, "--allow-empty");
+
+	rc = run_command_v_opt(array.argv, RUN_GIT_CMD);
+	argv_array_clear(&array);
+	return rc;
+}
+
+static int is_original_commit_empty(struct commit *commit)
+{
+	const unsigned char *ptree_sha1;
+
+	if (parse_commit(commit))
+		return error(_("Could not parse commit %s\n"),
+			     sha1_to_hex(commit->object.sha1));
+	if (commit->parents) {
+		struct commit *parent = commit->parents->item;
+		if (parse_commit(parent))
+			return error(_("Could not parse parent commit %s\n"),
+				sha1_to_hex(parent->object.sha1));
+		ptree_sha1 = parent->tree->object.sha1;
+	} else {
+		ptree_sha1 = EMPTY_TREE_SHA1_BIN; /* commit is root */
+	}
+
+	return !hashcmp(ptree_sha1, commit->tree->object.sha1);
+}
+
+/*
+ * Do we run "git commit" with "--allow-empty"?
+ */
+static int allow_empty(struct replay_opts *opts, struct commit *commit)
+{
+	int index_unchanged, empty_commit;
+
+	/*
+	 * Three cases:
+	 *
+	 * (1) we do not allow empty at all and error out.
+	 *
+	 * (2) we allow ones that were initially empty, but
+	 * forbid the ones that become empty;
+	 *
+	 * (3) we allow both.
+	 */
+	if (!opts->allow_empty)
+		return 0; /* let "git commit" barf as necessary */
+
+	index_unchanged = is_index_unchanged();
+	if (index_unchanged < 0)
+		return index_unchanged;
+	if (!index_unchanged)
+		return 0; /* we do not have to say --allow-empty */
+
+	if (opts->keep_redundant_commits)
+		return 1;
+
+	empty_commit = is_original_commit_empty(commit);
+	if (empty_commit < 0)
+		return empty_commit;
+	if (!empty_commit)
+		return 0;
+	else
+		return 1;
+}
+
+static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
+{
+	unsigned char head[20];
+	struct commit *base, *next, *parent;
+	const char *base_label, *next_label;
+	struct commit_message msg = { NULL, NULL, NULL, NULL, NULL };
+	char *defmsg = NULL;
+	struct strbuf msgbuf = STRBUF_INIT;
+	int res;
+
+	if (opts->no_commit) {
+		/*
+		 * We do not intend to commit immediately.  We just want to
+		 * merge the differences in, so let's compute the tree
+		 * that represents the "current" state for merge-recursive
+		 * to work on.
+		 */
+		if (write_cache_as_tree(head, 0, NULL))
+			die (_("Your index file is unmerged."));
+	} else {
+		if (get_sha1("HEAD", head))
+			return error(_("You do not have a valid HEAD"));
+		if (index_differs_from("HEAD", 0))
+			return error_dirty_index(opts);
+	}
+	discard_cache();
+
+	if (!commit->parents) {
+		parent = NULL;
+	}
+	else if (commit->parents->next) {
+		/* Reverting or cherry-picking a merge commit */
+		int cnt;
+		struct commit_list *p;
+
+		if (!opts->mainline)
+			return error(_("Commit %s is a merge but no -m option was given."),
+				sha1_to_hex(commit->object.sha1));
+
+		for (cnt = 1, p = commit->parents;
+		     cnt != opts->mainline && p;
+		     cnt++)
+			p = p->next;
+		if (cnt != opts->mainline || !p)
+			return error(_("Commit %s does not have parent %d"),
+				sha1_to_hex(commit->object.sha1), opts->mainline);
+		parent = p->item;
+	} else if (0 < opts->mainline)
+		return error(_("Mainline was specified but commit %s is not a merge."),
+			sha1_to_hex(commit->object.sha1));
+	else
+		parent = commit->parents->item;
+
+	if (opts->allow_ff && parent && !hashcmp(parent->object.sha1, head))
+		return fast_forward_to(commit->object.sha1, head);
+
+	if (parent && parse_commit(parent) < 0)
+		/* TRANSLATORS: The first %s will be "revert" or
+		   "cherry-pick", the second %s a SHA1 */
+		return error(_("%s: cannot parse parent commit %s"),
+			action_name(opts), sha1_to_hex(parent->object.sha1));
+
+	if (get_message(commit, &msg) != 0)
+		return error(_("Cannot get commit message for %s"),
+			sha1_to_hex(commit->object.sha1));
+
+	/*
+	 * "commit" is an existing commit.  We would want to apply
+	 * the difference it introduces since its first parent "prev"
+	 * on top of the current HEAD if we are cherry-pick.  Or the
+	 * reverse of it if we are revert.
+	 */
+
+	defmsg = git_pathdup("MERGE_MSG");
+
+	if (opts->action == REPLAY_REVERT) {
+		base = commit;
+		base_label = msg.label;
+		next = parent;
+		next_label = msg.parent_label;
+		strbuf_addstr(&msgbuf, "Revert \"");
+		strbuf_addstr(&msgbuf, msg.subject);
+		strbuf_addstr(&msgbuf, "\"\n\nThis reverts commit ");
+		strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));
+
+		if (commit->parents && commit->parents->next) {
+			strbuf_addstr(&msgbuf, ", reversing\nchanges made to ");
+			strbuf_addstr(&msgbuf, sha1_to_hex(parent->object.sha1));
+		}
+		strbuf_addstr(&msgbuf, ".\n");
+	} else {
+		const char *p;
+
+		base = parent;
+		base_label = msg.parent_label;
+		next = commit;
+		next_label = msg.label;
+
+		/*
+		 * Append the commit log message to msgbuf; it starts
+		 * after the tree, parent, author, committer
+		 * information followed by "\n\n".
+		 */
+		p = strstr(msg.message, "\n\n");
+		if (p) {
+			p += 2;
+			strbuf_addstr(&msgbuf, p);
+		}
+
+		if (opts->record_origin) {
+			strbuf_addstr(&msgbuf, "(cherry picked from commit ");
+			strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));
+			strbuf_addstr(&msgbuf, ")\n");
+		}
+	}
+
+	if (!opts->strategy || !strcmp(opts->strategy, "recursive") || opts->action == REPLAY_REVERT) {
+		res = do_recursive_merge(base, next, base_label, next_label,
+					 head, &msgbuf, opts);
+		write_message(&msgbuf, defmsg);
+	} else {
+		struct commit_list *common = NULL;
+		struct commit_list *remotes = NULL;
+
+		write_message(&msgbuf, defmsg);
+
+		commit_list_insert(base, &common);
+		commit_list_insert(next, &remotes);
+		res = try_merge_command(opts->strategy, opts->xopts_nr, opts->xopts,
+					common, sha1_to_hex(head), remotes);
+		free_commit_list(common);
+		free_commit_list(remotes);
+	}
+
+	/*
+	 * If the merge was clean or if it failed due to conflict, we write
+	 * CHERRY_PICK_HEAD for the subsequent invocation of commit to use.
+	 * However, if the merge did not even start, then we don't want to
+	 * write it at all.
+	 */
+	if (opts->action == REPLAY_PICK && !opts->no_commit && (res == 0 || res == 1))
+		write_cherry_pick_head(commit, "CHERRY_PICK_HEAD");
+	if (opts->action == REPLAY_REVERT && ((opts->no_commit && res == 0) || res == 1))
+		write_cherry_pick_head(commit, "REVERT_HEAD");
+
+	if (res) {
+		error(opts->action == REPLAY_REVERT
+		      ? _("could not revert %s... %s")
+		      : _("could not apply %s... %s"),
+		      find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV),
+		      msg.subject);
+		print_advice(res == 1, opts);
+		rerere(opts->allow_rerere_auto);
+	} else {
+		int allow = allow_empty(opts, commit);
+		if (allow < 0)
+			return allow;
+		if (!opts->no_commit)
+			res = run_git_commit(defmsg, opts, allow);
+	}
+
+	free_message(&msg);
+	free(defmsg);
+
+	return res;
+}
+
+static void prepare_revs(struct replay_opts *opts)
+{
+	if (opts->action != REPLAY_REVERT)
+		opts->revs->reverse ^= 1;
+
+	if (prepare_revision_walk(opts->revs))
+		die(_("revision walk setup failed"));
+
+	if (!opts->revs->commits)
+		die(_("empty commit set passed"));
+}
+
+static void read_and_refresh_cache(struct replay_opts *opts)
+{
+	static struct lock_file index_lock;
+	int index_fd = hold_locked_index(&index_lock, 0);
+	if (read_index_preload(&the_index, NULL) < 0)
+		die(_("git %s: failed to read the index"), action_name(opts));
+	refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL);
+	if (the_index.cache_changed) {
+		if (write_index(&the_index, index_fd) ||
+		    commit_locked_index(&index_lock))
+			die(_("git %s: failed to refresh the index"), action_name(opts));
+	}
+	rollback_lock_file(&index_lock);
+}
+
+static int format_todo(struct strbuf *buf, struct commit_list *todo_list,
+		struct replay_opts *opts)
+{
+	struct commit_list *cur = NULL;
+	const char *sha1_abbrev = NULL;
+	const char *action_str = opts->action == REPLAY_REVERT ? "revert" : "pick";
+	const char *subject;
+	int subject_len;
+
+	for (cur = todo_list; cur; cur = cur->next) {
+		sha1_abbrev = find_unique_abbrev(cur->item->object.sha1, DEFAULT_ABBREV);
+		subject_len = find_commit_subject(cur->item->buffer, &subject);
+		strbuf_addf(buf, "%s %s %.*s\n", action_str, sha1_abbrev,
+			subject_len, subject);
+	}
+	return 0;
+}
+
+static struct commit *parse_insn_line(char *bol, char *eol, struct replay_opts *opts)
+{
+	unsigned char commit_sha1[20];
+	enum replay_action action;
+	char *end_of_object_name;
+	int saved, status, padding;
+
+	if (!prefixcmp(bol, "pick")) {
+		action = REPLAY_PICK;
+		bol += strlen("pick");
+	} else if (!prefixcmp(bol, "revert")) {
+		action = REPLAY_REVERT;
+		bol += strlen("revert");
+	} else
+		return NULL;
+
+	/* Eat up extra spaces/ tabs before object name */
+	padding = strspn(bol, " \t");
+	if (!padding)
+		return NULL;
+	bol += padding;
+
+	end_of_object_name = bol + strcspn(bol, " \t\n");
+	saved = *end_of_object_name;
+	*end_of_object_name = '\0';
+	status = get_sha1(bol, commit_sha1);
+	*end_of_object_name = saved;
+
+	/*
+	 * Verify that the action matches up with the one in
+	 * opts; we don't support arbitrary instructions
+	 */
+	if (action != opts->action) {
+		const char *action_str;
+		action_str = action == REPLAY_REVERT ? "revert" : "cherry-pick";
+		error(_("Cannot %s during a %s"), action_str, action_name(opts));
+		return NULL;
+	}
+
+	if (status < 0)
+		return NULL;
+
+	return lookup_commit_reference(commit_sha1);
+}
+
+static int parse_insn_buffer(char *buf, struct commit_list **todo_list,
+			struct replay_opts *opts)
+{
+	struct commit_list **next = todo_list;
+	struct commit *commit;
+	char *p = buf;
+	int i;
+
+	for (i = 1; *p; i++) {
+		char *eol = strchrnul(p, '\n');
+		commit = parse_insn_line(p, eol, opts);
+		if (!commit)
+			return error(_("Could not parse line %d."), i);
+		next = commit_list_append(commit, next);
+		p = *eol ? eol + 1 : eol;
+	}
+	if (!*todo_list)
+		return error(_("No commits parsed."));
+	return 0;
+}
+
+static void read_populate_todo(struct commit_list **todo_list,
+			struct replay_opts *opts)
+{
+	const char *todo_file = git_path(SEQ_TODO_FILE);
+	struct strbuf buf = STRBUF_INIT;
+	int fd, res;
+
+	fd = open(todo_file, O_RDONLY);
+	if (fd < 0)
+		die_errno(_("Could not open %s"), todo_file);
+	if (strbuf_read(&buf, fd, 0) < 0) {
+		close(fd);
+		strbuf_release(&buf);
+		die(_("Could not read %s."), todo_file);
+	}
+	close(fd);
+
+	res = parse_insn_buffer(buf.buf, todo_list, opts);
+	strbuf_release(&buf);
+	if (res)
+		die(_("Unusable instruction sheet: %s"), todo_file);
+}
+
+static int populate_opts_cb(const char *key, const char *value, void *data)
+{
+	struct replay_opts *opts = data;
+	int error_flag = 1;
+
+	if (!value)
+		error_flag = 0;
+	else if (!strcmp(key, "options.no-commit"))
+		opts->no_commit = git_config_bool_or_int(key, value, &error_flag);
+	else if (!strcmp(key, "options.edit"))
+		opts->edit = git_config_bool_or_int(key, value, &error_flag);
+	else if (!strcmp(key, "options.signoff"))
+		opts->signoff = git_config_bool_or_int(key, value, &error_flag);
+	else if (!strcmp(key, "options.record-origin"))
+		opts->record_origin = git_config_bool_or_int(key, value, &error_flag);
+	else if (!strcmp(key, "options.allow-ff"))
+		opts->allow_ff = git_config_bool_or_int(key, value, &error_flag);
+	else if (!strcmp(key, "options.mainline"))
+		opts->mainline = git_config_int(key, value);
+	else if (!strcmp(key, "options.strategy"))
+		git_config_string(&opts->strategy, key, value);
+	else if (!strcmp(key, "options.strategy-option")) {
+		ALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc);
+		opts->xopts[opts->xopts_nr++] = xstrdup(value);
+	} else
+		return error(_("Invalid key: %s"), key);
+
+	if (!error_flag)
+		return error(_("Invalid value for %s: %s"), key, value);
+
+	return 0;
+}
+
+static void read_populate_opts(struct replay_opts **opts_ptr)
+{
+	const char *opts_file = git_path(SEQ_OPTS_FILE);
+
+	if (!file_exists(opts_file))
+		return;
+	if (git_config_from_file(populate_opts_cb, opts_file, *opts_ptr) < 0)
+		die(_("Malformed options sheet: %s"), opts_file);
+}
+
+static void walk_revs_populate_todo(struct commit_list **todo_list,
+				struct replay_opts *opts)
+{
+	struct commit *commit;
+	struct commit_list **next;
+
+	prepare_revs(opts);
+
+	next = todo_list;
+	while ((commit = get_revision(opts->revs)))
+		next = commit_list_append(commit, next);
+}
+
+static int create_seq_dir(void)
+{
+	const char *seq_dir = git_path(SEQ_DIR);
+
+	if (file_exists(seq_dir)) {
+		error(_("a cherry-pick or revert is already in progress"));
+		advise(_("try \"git cherry-pick (--continue | --quit | --abort)\""));
+		return -1;
+	}
+	else if (mkdir(seq_dir, 0777) < 0)
+		die_errno(_("Could not create sequencer directory %s"), seq_dir);
+	return 0;
+}
+
+static void save_head(const char *head)
+{
+	const char *head_file = git_path(SEQ_HEAD_FILE);
+	static struct lock_file head_lock;
+	struct strbuf buf = STRBUF_INIT;
+	int fd;
+
+	fd = hold_lock_file_for_update(&head_lock, head_file, LOCK_DIE_ON_ERROR);
+	strbuf_addf(&buf, "%s\n", head);
+	if (write_in_full(fd, buf.buf, buf.len) < 0)
+		die_errno(_("Could not write to %s"), head_file);
+	if (commit_lock_file(&head_lock) < 0)
+		die(_("Error wrapping up %s."), head_file);
+}
+
+static int reset_for_rollback(const unsigned char *sha1)
+{
+	const char *argv[4];	/* reset --merge <arg> + NULL */
+	argv[0] = "reset";
+	argv[1] = "--merge";
+	argv[2] = sha1_to_hex(sha1);
+	argv[3] = NULL;
+	return run_command_v_opt(argv, RUN_GIT_CMD);
+}
+
+static int rollback_single_pick(void)
+{
+	unsigned char head_sha1[20];
+
+	if (!file_exists(git_path("CHERRY_PICK_HEAD")) &&
+	    !file_exists(git_path("REVERT_HEAD")))
+		return error(_("no cherry-pick or revert in progress"));
+	if (read_ref_full("HEAD", head_sha1, 0, NULL))
+		return error(_("cannot resolve HEAD"));
+	if (is_null_sha1(head_sha1))
+		return error(_("cannot abort from a branch yet to be born"));
+	return reset_for_rollback(head_sha1);
+}
+
+static int sequencer_rollback(struct replay_opts *opts)
+{
+	const char *filename;
+	FILE *f;
+	unsigned char sha1[20];
+	struct strbuf buf = STRBUF_INIT;
+
+	filename = git_path(SEQ_HEAD_FILE);
+	f = fopen(filename, "r");
+	if (!f && errno == ENOENT) {
+		/*
+		 * There is no multiple-cherry-pick in progress.
+		 * If CHERRY_PICK_HEAD or REVERT_HEAD indicates
+		 * a single-cherry-pick in progress, abort that.
+		 */
+		return rollback_single_pick();
+	}
+	if (!f)
+		return error(_("cannot open %s: %s"), filename,
+						strerror(errno));
+	if (strbuf_getline(&buf, f, '\n')) {
+		error(_("cannot read %s: %s"), filename, ferror(f) ?
+			strerror(errno) : _("unexpected end of file"));
+		fclose(f);
+		goto fail;
+	}
+	fclose(f);
+	if (get_sha1_hex(buf.buf, sha1) || buf.buf[40] != '\0') {
+		error(_("stored pre-cherry-pick HEAD file '%s' is corrupt"),
+			filename);
+		goto fail;
+	}
+	if (reset_for_rollback(sha1))
+		goto fail;
+	remove_sequencer_state();
+	strbuf_release(&buf);
+	return 0;
+fail:
+	strbuf_release(&buf);
+	return -1;
+}
+
+static void save_todo(struct commit_list *todo_list, struct replay_opts *opts)
+{
+	const char *todo_file = git_path(SEQ_TODO_FILE);
+	static struct lock_file todo_lock;
+	struct strbuf buf = STRBUF_INIT;
+	int fd;
+
+	fd = hold_lock_file_for_update(&todo_lock, todo_file, LOCK_DIE_ON_ERROR);
+	if (format_todo(&buf, todo_list, opts) < 0)
+		die(_("Could not format %s."), todo_file);
+	if (write_in_full(fd, buf.buf, buf.len) < 0) {
+		strbuf_release(&buf);
+		die_errno(_("Could not write to %s"), todo_file);
+	}
+	if (commit_lock_file(&todo_lock) < 0) {
+		strbuf_release(&buf);
+		die(_("Error wrapping up %s."), todo_file);
+	}
+	strbuf_release(&buf);
+}
+
+static void save_opts(struct replay_opts *opts)
+{
+	const char *opts_file = git_path(SEQ_OPTS_FILE);
+
+	if (opts->no_commit)
+		git_config_set_in_file(opts_file, "options.no-commit", "true");
+	if (opts->edit)
+		git_config_set_in_file(opts_file, "options.edit", "true");
+	if (opts->signoff)
+		git_config_set_in_file(opts_file, "options.signoff", "true");
+	if (opts->record_origin)
+		git_config_set_in_file(opts_file, "options.record-origin", "true");
+	if (opts->allow_ff)
+		git_config_set_in_file(opts_file, "options.allow-ff", "true");
+	if (opts->mainline) {
+		struct strbuf buf = STRBUF_INIT;
+		strbuf_addf(&buf, "%d", opts->mainline);
+		git_config_set_in_file(opts_file, "options.mainline", buf.buf);
+		strbuf_release(&buf);
+	}
+	if (opts->strategy)
+		git_config_set_in_file(opts_file, "options.strategy", opts->strategy);
+	if (opts->xopts) {
+		int i;
+		for (i = 0; i < opts->xopts_nr; i++)
+			git_config_set_multivar_in_file(opts_file,
+							"options.strategy-option",
+							opts->xopts[i], "^$", 0);
+	}
+}
+
+static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)
+{
+	struct commit_list *cur;
+	int res;
+
+	setenv(GIT_REFLOG_ACTION, action_name(opts), 0);
+	if (opts->allow_ff)
+		assert(!(opts->signoff || opts->no_commit ||
+				opts->record_origin || opts->edit));
+	read_and_refresh_cache(opts);
+
+	for (cur = todo_list; cur; cur = cur->next) {
+		save_todo(cur, opts);
+		res = do_pick_commit(cur->item, opts);
+		if (res)
+			return res;
+	}
+
+	/*
+	 * Sequence of picks finished successfully; cleanup by
+	 * removing the .git/sequencer directory
+	 */
+	remove_sequencer_state();
+	return 0;
+}
+
+static int continue_single_pick(void)
+{
+	const char *argv[] = { "commit", NULL };
+
+	if (!file_exists(git_path("CHERRY_PICK_HEAD")) &&
+	    !file_exists(git_path("REVERT_HEAD")))
+		return error(_("no cherry-pick or revert in progress"));
+	return run_command_v_opt(argv, RUN_GIT_CMD);
+}
+
+static int sequencer_continue(struct replay_opts *opts)
+{
+	struct commit_list *todo_list = NULL;
+
+	if (!file_exists(git_path(SEQ_TODO_FILE)))
+		return continue_single_pick();
+	read_populate_opts(&opts);
+	read_populate_todo(&todo_list, opts);
+
+	/* Verify that the conflict has been resolved */
+	if (file_exists(git_path("CHERRY_PICK_HEAD")) ||
+	    file_exists(git_path("REVERT_HEAD"))) {
+		int ret = continue_single_pick();
+		if (ret)
+			return ret;
+	}
+	if (index_differs_from("HEAD", 0))
+		return error_dirty_index(opts);
+	todo_list = todo_list->next;
+	return pick_commits(todo_list, opts);
+}
+
+static int single_pick(struct commit *cmit, struct replay_opts *opts)
+{
+	setenv(GIT_REFLOG_ACTION, action_name(opts), 0);
+	return do_pick_commit(cmit, opts);
+}
+
+int sequencer_pick_revisions(struct replay_opts *opts)
+{
+	struct commit_list *todo_list = NULL;
+	unsigned char sha1[20];
+
+	if (opts->subcommand == REPLAY_NONE)
+		assert(opts->revs);
+
+	read_and_refresh_cache(opts);
+
+	/*
+	 * Decide what to do depending on the arguments; a fresh
+	 * cherry-pick should be handled differently from an existing
+	 * one that is being continued
+	 */
+	if (opts->subcommand == REPLAY_REMOVE_STATE) {
+		remove_sequencer_state();
+		return 0;
+	}
+	if (opts->subcommand == REPLAY_ROLLBACK)
+		return sequencer_rollback(opts);
+	if (opts->subcommand == REPLAY_CONTINUE)
+		return sequencer_continue(opts);
+
+	/*
+	 * If we were called as "git cherry-pick <commit>", just
+	 * cherry-pick/revert it, set CHERRY_PICK_HEAD /
+	 * REVERT_HEAD, and don't touch the sequencer state.
+	 * This means it is possible to cherry-pick in the middle
+	 * of a cherry-pick sequence.
+	 */
+	if (opts->revs->cmdline.nr == 1 &&
+	    opts->revs->cmdline.rev->whence == REV_CMD_REV &&
+	    opts->revs->no_walk &&
+	    !opts->revs->cmdline.rev->flags) {
+		struct commit *cmit;
+		if (prepare_revision_walk(opts->revs))
+			die(_("revision walk setup failed"));
+		cmit = get_revision(opts->revs);
+		if (!cmit || get_revision(opts->revs))
+			die("BUG: expected exactly one commit from walk");
+		return single_pick(cmit, opts);
+	}
+
+	/*
+	 * Start a new cherry-pick/ revert sequence; but
+	 * first, make sure that an existing one isn't in
+	 * progress
+	 */
+
+	walk_revs_populate_todo(&todo_list, opts);
+	if (create_seq_dir() < 0)
+		return -1;
+	if (get_sha1("HEAD", sha1)) {
+		if (opts->action == REPLAY_REVERT)
+			return error(_("Can't revert as initial commit"));
+		return error(_("Can't cherry-pick into empty head"));
+	}
+	save_head(sha1_to_hex(sha1));
+	save_opts(opts);
+	return pick_commits(todo_list, opts);
+}
diff --git a/sequencer.h b/sequencer.h
new file mode 100644
index 0000000..aa5f17c
--- /dev/null
+++ b/sequencer.h
@@ -0,0 +1,51 @@
+#ifndef SEQUENCER_H
+#define SEQUENCER_H
+
+#define SEQ_DIR		"sequencer"
+#define SEQ_HEAD_FILE	"sequencer/head"
+#define SEQ_TODO_FILE	"sequencer/todo"
+#define SEQ_OPTS_FILE	"sequencer/opts"
+
+enum replay_action {
+	REPLAY_REVERT,
+	REPLAY_PICK
+};
+
+enum replay_subcommand {
+	REPLAY_NONE,
+	REPLAY_REMOVE_STATE,
+	REPLAY_CONTINUE,
+	REPLAY_ROLLBACK
+};
+
+struct replay_opts {
+	enum replay_action action;
+	enum replay_subcommand subcommand;
+
+	/* Boolean options */
+	int edit;
+	int record_origin;
+	int no_commit;
+	int signoff;
+	int allow_ff;
+	int allow_rerere_auto;
+	int allow_empty;
+	int keep_redundant_commits;
+
+	int mainline;
+
+	/* Merge strategy */
+	const char *strategy;
+	const char **xopts;
+	size_t xopts_nr, xopts_alloc;
+
+	/* Only used by REPLAY_NONE */
+	struct rev_info *revs;
+};
+
+/* Removes SEQ_DIR. */
+extern void remove_sequencer_state(void);
+
+int sequencer_pick_revisions(struct replay_opts *opts);
+
+#endif
diff --git a/server-info.c b/server-info.c
index 4098ca2..9ec744e 100644
--- a/server-info.c
+++ b/server-info.c
@@ -113,11 +113,8 @@
 				goto out_stale;
 			break;
 		case 'D': /* we used to emit D but that was misguided. */
-			goto out_stale;
-			break;
 		case 'T': /* we used to emit T but nobody uses it. */
 			goto out_stale;
-			break;
 		default:
 			error("unrecognized: %s", line);
 			break;
diff --git a/setup.c b/setup.c
index e3781b6..9139bee 100644
--- a/setup.c
+++ b/setup.c
@@ -4,13 +4,16 @@
 static int inside_git_dir = -1;
 static int inside_work_tree = -1;
 
-const char *prefix_path(const char *prefix, int len, const char *path)
+static char *prefix_path_gently(const char *prefix, int len, const char *path)
 {
 	const char *orig = path;
-	char *sanitized = xmalloc(len + strlen(path) + 1);
-	if (is_absolute_path(orig))
-		strcpy(sanitized, path);
-	else {
+	char *sanitized;
+	if (is_absolute_path(orig)) {
+		const char *temp = real_path(path);
+		sanitized = xmalloc(len + strlen(temp) + 1);
+		strcpy(sanitized, temp);
+	} else {
+		sanitized = xmalloc(len + strlen(path) + 1);
 		if (len)
 			memcpy(sanitized, prefix, len);
 		strcpy(sanitized + len, path);
@@ -18,13 +21,18 @@
 	if (normalize_path_copy(sanitized, sanitized))
 		goto error_out;
 	if (is_absolute_path(orig)) {
+		size_t root_len, len, total;
 		const char *work_tree = get_git_work_tree();
-		size_t len = strlen(work_tree);
-		size_t total = strlen(sanitized) + 1;
+		if (!work_tree)
+			goto error_out;
+		len = strlen(work_tree);
+		root_len = offset_1st_component(work_tree);
+		total = strlen(sanitized) + 1;
 		if (strncmp(sanitized, work_tree, len) ||
-		    (sanitized[len] != '\0' && sanitized[len] != '/')) {
+		    (len > root_len && sanitized[len] != '\0' && sanitized[len] != '/')) {
 		error_out:
-			die("'%s' is outside repository", orig);
+			free(sanitized);
+			return NULL;
 		}
 		if (sanitized[len] == '/')
 			len++;
@@ -33,32 +41,59 @@
 	return sanitized;
 }
 
-/*
- * Unlike prefix_path, this should be used if the named file does
- * not have to interact with index entry; i.e. name of a random file
- * on the filesystem.
- */
-const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
+char *prefix_path(const char *prefix, int len, const char *path)
 {
-	static char path[PATH_MAX];
-#ifndef __MINGW32__
-	if (!pfx || !*pfx || is_absolute_path(arg))
-		return arg;
-	memcpy(path, pfx, pfx_len);
-	strcpy(path + pfx_len, arg);
-#else
-	char *p;
-	/* don't add prefix to absolute paths, but still replace '\' by '/' */
-	if (is_absolute_path(arg))
-		pfx_len = 0;
-	else
-		memcpy(path, pfx, pfx_len);
-	strcpy(path + pfx_len, arg);
-	for (p = path + pfx_len; *p; p++)
-		if (*p == '\\')
-			*p = '/';
-#endif
-	return path;
+	char *r = prefix_path_gently(prefix, len, path);
+	if (!r)
+		die("'%s' is outside repository", path);
+	return r;
+}
+
+int path_inside_repo(const char *prefix, const char *path)
+{
+	int len = prefix ? strlen(prefix) : 0;
+	char *r = prefix_path_gently(prefix, len, path);
+	if (r) {
+		free(r);
+		return 1;
+	}
+	return 0;
+}
+
+int check_filename(const char *prefix, const char *arg)
+{
+	const char *name;
+	struct stat st;
+
+	name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg;
+	if (!lstat(name, &st))
+		return 1; /* file exists */
+	if (errno == ENOENT || errno == ENOTDIR)
+		return 0; /* file does not exist */
+	die_errno("failed to stat '%s'", arg);
+}
+
+static void NORETURN die_verify_filename(const char *prefix,
+					 const char *arg,
+					 int diagnose_misspelt_rev)
+{
+	if (!diagnose_misspelt_rev)
+		die("%s: no such path in the working tree.\n"
+		    "Use '-- <path>...' to specify paths that do not exist locally.",
+		    arg);
+	/*
+	 * Saying "'(icase)foo' does not exist in the index" when the
+	 * user gave us ":(icase)foo" is just stupid.  A magic pathspec
+	 * begins with a colon and is followed by a non-alnum; do not
+	 * let maybe_die_on_misspelt_object_name() even trigger.
+	 */
+	if (!(arg[0] == ':' && !isalnum(arg[1])))
+		maybe_die_on_misspelt_object_name(arg, prefix);
+
+	/* ... or fall back the most general message. */
+	die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
+	    "Use '--' to separate paths from revisions", arg);
+
 }
 
 /*
@@ -67,21 +102,29 @@
  * as true, because even if such a filename were to exist, we want
  * it to be preceded by the "--" marker (or we want the user to
  * use a format like "./-filename")
+ *
+ * The "diagnose_misspelt_rev" is used to provide a user-friendly
+ * diagnosis when dying upon finding that "name" is not a pathname.
+ * If set to 1, the diagnosis will try to diagnose "name" as an
+ * invalid object name (e.g. HEAD:foo). If set to 0, the diagnosis
+ * will only complain about an inexisting file.
+ *
+ * This function is typically called to check that a "file or rev"
+ * argument is unambiguous. In this case, the caller will want
+ * diagnose_misspelt_rev == 1 when verifying the first non-rev
+ * argument (which could have been a revision), and
+ * diagnose_misspelt_rev == 0 for the next ones (because we already
+ * saw a filename, there's not ambiguity anymore).
  */
-void verify_filename(const char *prefix, const char *arg)
+void verify_filename(const char *prefix,
+		     const char *arg,
+		     int diagnose_misspelt_rev)
 {
-	const char *name;
-	struct stat st;
-
 	if (*arg == '-')
 		die("bad flag '%s' used after filename", arg);
-	name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg;
-	if (!lstat(name, &st))
+	if (check_filename(prefix, arg))
 		return;
-	if (errno == ENOENT)
-		die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
-		    "Use '--' to separate paths from revisions", arg);
-	die_errno("failed to stat '%s'", arg);
+	die_verify_filename(prefix, arg, diagnose_misspelt_rev);
 }
 
 /*
@@ -91,19 +134,113 @@
  */
 void verify_non_filename(const char *prefix, const char *arg)
 {
-	const char *name;
-	struct stat st;
-
 	if (!is_inside_work_tree() || is_inside_git_dir())
 		return;
 	if (*arg == '-')
 		return; /* flag */
-	name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg;
-	if (!lstat(name, &st))
-		die("ambiguous argument '%s': both revision and filename\n"
-		    "Use '--' to separate filenames from revisions", arg);
-	if (errno != ENOENT && errno != ENOTDIR)
-		die_errno("failed to stat '%s'", arg);
+	if (!check_filename(prefix, arg))
+		return;
+	die("ambiguous argument '%s': both revision and filename\n"
+	    "Use '--' to separate filenames from revisions", arg);
+}
+
+/*
+ * Magic pathspec
+ *
+ * NEEDSWORK: These need to be moved to dir.h or even to a new
+ * pathspec.h when we restructure get_pathspec() users to use the
+ * "struct pathspec" interface.
+ *
+ * Possible future magic semantics include stuff like:
+ *
+ *	{ PATHSPEC_NOGLOB, '!', "noglob" },
+ *	{ PATHSPEC_ICASE, '\0', "icase" },
+ *	{ PATHSPEC_RECURSIVE, '*', "recursive" },
+ *	{ PATHSPEC_REGEXP, '\0', "regexp" },
+ *
+ */
+#define PATHSPEC_FROMTOP    (1<<0)
+
+static struct pathspec_magic {
+	unsigned bit;
+	char mnemonic; /* this cannot be ':'! */
+	const char *name;
+} pathspec_magic[] = {
+	{ PATHSPEC_FROMTOP, '/', "top" },
+};
+
+/*
+ * Take an element of a pathspec and check for magic signatures.
+ * Append the result to the prefix.
+ *
+ * For now, we only parse the syntax and throw out anything other than
+ * "top" magic.
+ *
+ * NEEDSWORK: This needs to be rewritten when we start migrating
+ * get_pathspec() users to use the "struct pathspec" interface.  For
+ * example, a pathspec element may be marked as case-insensitive, but
+ * the prefix part must always match literally, and a single stupid
+ * string cannot express such a case.
+ */
+static const char *prefix_pathspec(const char *prefix, int prefixlen, const char *elt)
+{
+	unsigned magic = 0;
+	const char *copyfrom = elt;
+	int i;
+
+	if (elt[0] != ':') {
+		; /* nothing to do */
+	} else if (elt[1] == '(') {
+		/* longhand */
+		const char *nextat;
+		for (copyfrom = elt + 2;
+		     *copyfrom && *copyfrom != ')';
+		     copyfrom = nextat) {
+			size_t len = strcspn(copyfrom, ",)");
+			if (copyfrom[len] == ')')
+				nextat = copyfrom + len;
+			else
+				nextat = copyfrom + len + 1;
+			if (!len)
+				continue;
+			for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
+				if (strlen(pathspec_magic[i].name) == len &&
+				    !strncmp(pathspec_magic[i].name, copyfrom, len)) {
+					magic |= pathspec_magic[i].bit;
+					break;
+				}
+			if (ARRAY_SIZE(pathspec_magic) <= i)
+				die("Invalid pathspec magic '%.*s' in '%s'",
+				    (int) len, copyfrom, elt);
+		}
+		if (*copyfrom == ')')
+			copyfrom++;
+	} else {
+		/* shorthand */
+		for (copyfrom = elt + 1;
+		     *copyfrom && *copyfrom != ':';
+		     copyfrom++) {
+			char ch = *copyfrom;
+
+			if (!is_pathspec_magic(ch))
+				break;
+			for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
+				if (pathspec_magic[i].mnemonic == ch) {
+					magic |= pathspec_magic[i].bit;
+					break;
+				}
+			if (ARRAY_SIZE(pathspec_magic) <= i)
+				die("Unimplemented pathspec magic '%c' in '%s'",
+				    ch, elt);
+		}
+		if (*copyfrom == ':')
+			copyfrom++;
+	}
+
+	if (magic & PATHSPEC_FROMTOP)
+		return xstrdup(copyfrom);
+	else
+		return prefix_path(prefix, prefixlen, copyfrom);
 }
 
 const char **get_pathspec(const char *prefix, const char **pathspec)
@@ -127,8 +264,7 @@
 	dst = pathspec;
 	prefixlen = prefix ? strlen(prefix) : 0;
 	while (*src) {
-		const char *p = prefix_path(prefix, prefixlen, *src);
-		*(dst++) = p;
+		*(dst++) = prefix_pathspec(prefix, prefixlen, *src);
 		src++;
 	}
 	*dst = NULL;
@@ -148,11 +284,13 @@
  *    a proper "ref:", or a regular file HEAD that has a properly
  *    formatted sha1 object name.
  */
-static int is_git_directory(const char *suspect)
+int is_git_directory(const char *suspect)
 {
 	char path[PATH_MAX];
 	size_t len = strlen(suspect);
 
+	if (PATH_MAX <= len + strlen("/objects"))
+		die("Too long path: %.*s", 60, suspect);
 	strcpy(path, suspect);
 	if (getenv(DB_ENVIRONMENT)) {
 		if (access(getenv(DB_ENVIRONMENT), X_OK))
@@ -189,24 +327,6 @@
 	return inside_work_tree;
 }
 
-/*
- * set_work_tree() is only ever called if you set GIT_DIR explicitely.
- * The old behaviour (which we retain here) is to set the work tree root
- * to the cwd, unless overridden by the config, the command line, or
- * GIT_WORK_TREE.
- */
-static const char *set_work_tree(const char *dir)
-{
-	char buffer[PATH_MAX + 1];
-
-	if (!getcwd(buffer, sizeof(buffer)))
-		die ("Could not get the current working directory");
-	git_work_tree_cfg = xstrdup(buffer);
-	inside_work_tree = 1;
-
-	return NULL;
-}
-
 void setup_work_tree(void)
 {
 	const char *work_tree, *git_dir;
@@ -217,16 +337,36 @@
 	work_tree = get_git_work_tree();
 	git_dir = get_git_dir();
 	if (!is_absolute_path(git_dir))
-		git_dir = make_absolute_path(git_dir);
+		git_dir = real_path(get_git_dir());
 	if (!work_tree || chdir(work_tree))
 		die("This operation must be run in a work tree");
-	set_git_dir(make_relative_path(git_dir, work_tree));
+
+	/*
+	 * Make sure subsequent git processes find correct worktree
+	 * if $GIT_WORK_TREE is set relative
+	 */
+	if (getenv(GIT_WORK_TREE_ENVIRONMENT))
+		setenv(GIT_WORK_TREE_ENVIRONMENT, ".", 1);
+
+	set_git_dir(relative_path(git_dir, work_tree));
 	initialized = 1;
 }
 
-static int check_repository_format_gently(int *nongit_ok)
+static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
 {
-	git_config(check_repository_format_version, NULL);
+	char repo_config[PATH_MAX+1];
+
+	/*
+	 * git_config() can't be used here because it calls git_pathdup()
+	 * to get $GIT_CONFIG/config. That call will make setup_git_env()
+	 * set git_dir to ".git".
+	 *
+	 * We are in gitdir setup, no git dir has been found useable yet.
+	 * Use a gentler version of git_config() to check if this repo
+	 * is a good one.
+	 */
+	snprintf(repo_config, PATH_MAX, "%s/config", gitdir);
+	git_config_early(check_repository_format_version, NULL, repo_config);
 	if (GIT_REPO_VERSION < repository_format_version) {
 		if (!nongit_ok)
 			die ("Expected git repo version <= %d, found %d",
@@ -244,12 +384,14 @@
  * Try to read the location of the git directory from the .git file,
  * return path to git directory if found.
  */
-const char *read_gitfile_gently(const char *path)
+const char *read_gitfile(const char *path)
 {
 	char *buf;
+	char *dir;
+	const char *slash;
 	struct stat st;
 	int fd;
-	size_t len;
+	ssize_t len;
 
 	if (stat(path, &st))
 		return NULL;
@@ -271,25 +413,224 @@
 	if (len < 9)
 		die("No path in gitfile: %s", path);
 	buf[len] = '\0';
-	if (!is_git_directory(buf + 8))
-		die("Not a git repository: %s", buf + 8);
-	path = make_absolute_path(buf + 8);
+	dir = buf + 8;
+
+	if (!is_absolute_path(dir) && (slash = strrchr(path, '/'))) {
+		size_t pathlen = slash+1 - path;
+		size_t dirlen = pathlen + len - 8;
+		dir = xmalloc(dirlen + 1);
+		strncpy(dir, path, pathlen);
+		strncpy(dir + pathlen, buf + 8, len - 8);
+		dir[dirlen] = '\0';
+		free(buf);
+		buf = dir;
+	}
+
+	if (!is_git_directory(dir))
+		die("Not a git repository: %s", dir);
+	path = real_path(dir);
+
 	free(buf);
 	return path;
 }
 
+static const char *setup_explicit_git_dir(const char *gitdirenv,
+					  char *cwd, int len,
+					  int *nongit_ok)
+{
+	const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
+	const char *worktree;
+	char *gitfile;
+	int offset;
+
+	if (PATH_MAX - 40 < strlen(gitdirenv))
+		die("'$%s' too big", GIT_DIR_ENVIRONMENT);
+
+	gitfile = (char*)read_gitfile(gitdirenv);
+	if (gitfile) {
+		gitfile = xstrdup(gitfile);
+		gitdirenv = gitfile;
+	}
+
+	if (!is_git_directory(gitdirenv)) {
+		if (nongit_ok) {
+			*nongit_ok = 1;
+			free(gitfile);
+			return NULL;
+		}
+		die("Not a git repository: '%s'", gitdirenv);
+	}
+
+	if (check_repository_format_gently(gitdirenv, nongit_ok)) {
+		free(gitfile);
+		return NULL;
+	}
+
+	/* #3, #7, #11, #15, #19, #23, #27, #31 (see t1510) */
+	if (work_tree_env)
+		set_git_work_tree(work_tree_env);
+	else if (is_bare_repository_cfg > 0) {
+		if (git_work_tree_cfg) /* #22.2, #30 */
+			die("core.bare and core.worktree do not make sense");
+
+		/* #18, #26 */
+		set_git_dir(gitdirenv);
+		free(gitfile);
+		return NULL;
+	}
+	else if (git_work_tree_cfg) { /* #6, #14 */
+		if (is_absolute_path(git_work_tree_cfg))
+			set_git_work_tree(git_work_tree_cfg);
+		else {
+			char core_worktree[PATH_MAX];
+			if (chdir(gitdirenv))
+				die_errno("Could not chdir to '%s'", gitdirenv);
+			if (chdir(git_work_tree_cfg))
+				die_errno("Could not chdir to '%s'", git_work_tree_cfg);
+			if (!getcwd(core_worktree, PATH_MAX))
+				die_errno("Could not get directory '%s'", git_work_tree_cfg);
+			if (chdir(cwd))
+				die_errno("Could not come back to cwd");
+			set_git_work_tree(core_worktree);
+		}
+	}
+	else /* #2, #10 */
+		set_git_work_tree(".");
+
+	/* set_git_work_tree() must have been called by now */
+	worktree = get_git_work_tree();
+
+	/* both get_git_work_tree() and cwd are already normalized */
+	if (!strcmp(cwd, worktree)) { /* cwd == worktree */
+		set_git_dir(gitdirenv);
+		free(gitfile);
+		return NULL;
+	}
+
+	offset = dir_inside_of(cwd, worktree);
+	if (offset >= 0) {	/* cwd inside worktree? */
+		set_git_dir(real_path(gitdirenv));
+		if (chdir(worktree))
+			die_errno("Could not chdir to '%s'", worktree);
+		cwd[len++] = '/';
+		cwd[len] = '\0';
+		free(gitfile);
+		return cwd + offset;
+	}
+
+	/* cwd outside worktree */
+	set_git_dir(gitdirenv);
+	free(gitfile);
+	return NULL;
+}
+
+static const char *setup_discovered_git_dir(const char *gitdir,
+					    char *cwd, int offset, int len,
+					    int *nongit_ok)
+{
+	if (check_repository_format_gently(gitdir, nongit_ok))
+		return NULL;
+
+	/* --work-tree is set without --git-dir; use discovered one */
+	if (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) {
+		if (offset != len && !is_absolute_path(gitdir))
+			gitdir = xstrdup(real_path(gitdir));
+		if (chdir(cwd))
+			die_errno("Could not come back to cwd");
+		return setup_explicit_git_dir(gitdir, cwd, len, nongit_ok);
+	}
+
+	/* #16.2, #17.2, #20.2, #21.2, #24, #25, #28, #29 (see t1510) */
+	if (is_bare_repository_cfg > 0) {
+		set_git_dir(offset == len ? gitdir : real_path(gitdir));
+		if (chdir(cwd))
+			die_errno("Could not come back to cwd");
+		return NULL;
+	}
+
+	/* #0, #1, #5, #8, #9, #12, #13 */
+	set_git_work_tree(".");
+	if (strcmp(gitdir, DEFAULT_GIT_DIR_ENVIRONMENT))
+		set_git_dir(gitdir);
+	inside_git_dir = 0;
+	inside_work_tree = 1;
+	if (offset == len)
+		return NULL;
+
+	/* Make "offset" point to past the '/', and add a '/' at the end */
+	offset++;
+	cwd[len++] = '/';
+	cwd[len] = 0;
+	return cwd + offset;
+}
+
+/* #16.1, #17.1, #20.1, #21.1, #22.1 (see t1510) */
+static const char *setup_bare_git_dir(char *cwd, int offset, int len, int *nongit_ok)
+{
+	int root_len;
+
+	if (check_repository_format_gently(".", nongit_ok))
+		return NULL;
+
+	/* --work-tree is set without --git-dir; use discovered one */
+	if (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) {
+		const char *gitdir;
+
+		gitdir = offset == len ? "." : xmemdupz(cwd, offset);
+		if (chdir(cwd))
+			die_errno("Could not come back to cwd");
+		return setup_explicit_git_dir(gitdir, cwd, len, nongit_ok);
+	}
+
+	inside_git_dir = 1;
+	inside_work_tree = 0;
+	if (offset != len) {
+		if (chdir(cwd))
+			die_errno("Cannot come back to cwd");
+		root_len = offset_1st_component(cwd);
+		cwd[offset > root_len ? offset : root_len] = '\0';
+		set_git_dir(cwd);
+	}
+	else
+		set_git_dir(".");
+	return NULL;
+}
+
+static const char *setup_nongit(const char *cwd, int *nongit_ok)
+{
+	if (!nongit_ok)
+		die("Not a git repository (or any of the parent directories): %s", DEFAULT_GIT_DIR_ENVIRONMENT);
+	if (chdir(cwd))
+		die_errno("Cannot come back to cwd");
+	*nongit_ok = 1;
+	return NULL;
+}
+
+static dev_t get_device_or_die(const char *path, const char *prefix, int prefix_len)
+{
+	struct stat buf;
+	if (stat(path, &buf)) {
+		die_errno("failed to stat '%*s%s%s'",
+				prefix_len,
+				prefix ? prefix : "",
+				prefix ? "/" : "", path);
+	}
+	return buf.st_dev;
+}
+
 /*
  * We cannot decide in this function whether we are in the work tree or
  * not, since the config can only be read _after_ this function was called.
  */
-const char *setup_git_directory_gently(int *nongit_ok)
+static const char *setup_git_directory_gently_1(int *nongit_ok)
 {
-	const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
 	const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT);
 	static char cwd[PATH_MAX+1];
-	const char *gitdirenv;
-	const char *gitfile_dir;
-	int len, offset, ceil_offset;
+	const char *gitdirenv, *ret;
+	char *gitfile;
+	int len, offset, offset_parent, ceil_offset;
+	dev_t current_device = 0;
+	int one_filesystem = 1;
 
 	/*
 	 * Let's assume that we are in a git repository.
@@ -299,47 +640,18 @@
 	if (nongit_ok)
 		*nongit_ok = 0;
 
+	if (!getcwd(cwd, sizeof(cwd)-1))
+		die_errno("Unable to read current working directory");
+	offset = len = strlen(cwd);
+
 	/*
 	 * If GIT_DIR is set explicitly, we're not going
 	 * to do any discovery, but we still do repository
 	 * validation.
 	 */
 	gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
-	if (gitdirenv) {
-		if (PATH_MAX - 40 < strlen(gitdirenv))
-			die("'$%s' too big", GIT_DIR_ENVIRONMENT);
-		if (is_git_directory(gitdirenv)) {
-			static char buffer[1024 + 1];
-			const char *retval;
-
-			if (!work_tree_env) {
-				retval = set_work_tree(gitdirenv);
-				/* config may override worktree */
-				if (check_repository_format_gently(nongit_ok))
-					return NULL;
-				return retval;
-			}
-			if (check_repository_format_gently(nongit_ok))
-				return NULL;
-			retval = get_relative_cwd(buffer, sizeof(buffer) - 1,
-					get_git_work_tree());
-			if (!retval || !*retval)
-				return NULL;
-			set_git_dir(make_absolute_path(gitdirenv));
-			if (chdir(work_tree_env) < 0)
-				die_errno ("Could not chdir to '%s'", work_tree_env);
-			strcat(buffer, "/");
-			return retval;
-		}
-		if (nongit_ok) {
-			*nongit_ok = 1;
-			return NULL;
-		}
-		die("Not a git repository: '%s'", gitdirenv);
-	}
-
-	if (!getcwd(cwd, sizeof(cwd)-1))
-		die_errno("Unable to read current working directory");
+	if (gitdirenv)
+		return setup_explicit_git_dir(gitdirenv, cwd, len, nongit_ok);
 
 	ceil_offset = longest_ancestor_length(cwd, env_ceiling_dirs);
 	if (ceil_offset < 0 && has_dos_drive_prefix(cwd))
@@ -356,56 +668,71 @@
 	 * - ../../.git/
 	 *   etc.
 	 */
-	offset = len = strlen(cwd);
+	one_filesystem = !git_env_bool("GIT_DISCOVERY_ACROSS_FILESYSTEM", 0);
+	if (one_filesystem)
+		current_device = get_device_or_die(".", NULL, 0);
 	for (;;) {
-		gitfile_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);
-		if (gitfile_dir) {
-			if (set_git_dir(gitfile_dir))
-				die("Repository setup failed");
-			break;
+		gitfile = (char*)read_gitfile(DEFAULT_GIT_DIR_ENVIRONMENT);
+		if (gitfile)
+			gitdirenv = gitfile = xstrdup(gitfile);
+		else {
+			if (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT))
+				gitdirenv = DEFAULT_GIT_DIR_ENVIRONMENT;
 		}
-		if (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT))
-			break;
-		if (is_git_directory(".")) {
-			inside_git_dir = 1;
-			if (!work_tree_env)
-				inside_work_tree = 0;
-			if (offset != len) {
+
+		if (gitdirenv) {
+			ret = setup_discovered_git_dir(gitdirenv,
+						       cwd, offset, len,
+						       nongit_ok);
+			free(gitfile);
+			return ret;
+		}
+		free(gitfile);
+
+		if (is_git_directory("."))
+			return setup_bare_git_dir(cwd, offset, len, nongit_ok);
+
+		offset_parent = offset;
+		while (--offset_parent > ceil_offset && cwd[offset_parent] != '/');
+		if (offset_parent <= ceil_offset)
+			return setup_nongit(cwd, nongit_ok);
+		if (one_filesystem) {
+			dev_t parent_device = get_device_or_die("..", cwd, offset);
+			if (parent_device != current_device) {
+				if (nongit_ok) {
+					if (chdir(cwd))
+						die_errno("Cannot come back to cwd");
+					*nongit_ok = 1;
+					return NULL;
+				}
 				cwd[offset] = '\0';
-				setenv(GIT_DIR_ENVIRONMENT, cwd, 1);
-			} else
-				setenv(GIT_DIR_ENVIRONMENT, ".", 1);
-			check_repository_format_gently(nongit_ok);
-			return NULL;
-		}
-		while (--offset > ceil_offset && cwd[offset] != '/');
-		if (offset <= ceil_offset) {
-			if (nongit_ok) {
-				if (chdir(cwd))
-					die_errno("Cannot come back to cwd");
-				*nongit_ok = 1;
-				return NULL;
+				die("Not a git repository (or any parent up to mount point %s)\n"
+				"Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).", cwd);
 			}
-			die("Not a git repository (or any of the parent directories): %s", DEFAULT_GIT_DIR_ENVIRONMENT);
 		}
-		if (chdir(".."))
+		if (chdir("..")) {
+			cwd[offset] = '\0';
 			die_errno("Cannot change to '%s/..'", cwd);
+		}
+		offset = offset_parent;
 	}
+}
 
-	inside_git_dir = 0;
-	if (!work_tree_env)
-		inside_work_tree = 1;
-	git_work_tree_cfg = xstrndup(cwd, offset);
-	if (check_repository_format_gently(nongit_ok))
-		return NULL;
-	if (offset == len)
-		return NULL;
+const char *setup_git_directory_gently(int *nongit_ok)
+{
+	const char *prefix;
 
-	/* Make "offset" point to past the '/', and add a '/' at the end */
-	offset++;
-	cwd[len++] = '/';
-	cwd[len] = 0;
-	return cwd + offset;
+	prefix = setup_git_directory_gently_1(nongit_ok);
+	if (prefix)
+		setenv("GIT_PREFIX", prefix, 1);
+	else
+		setenv("GIT_PREFIX", "", 1);
+
+	if (startup_info) {
+		startup_info->have_repository = !nongit_ok || !*nongit_ok;
+		startup_info->prefix = prefix;
+	}
+	return prefix;
 }
 
 int git_config_perm(const char *var, const char *value)
@@ -481,24 +808,23 @@
 
 int check_repository_format(void)
 {
-	return check_repository_format_gently(NULL);
+	return check_repository_format_gently(get_git_dir(), NULL);
 }
 
+/*
+ * Returns the "prefix", a path to the current working directory
+ * relative to the work tree root, or NULL, if the current working
+ * directory is not a strict subdirectory of the work tree root. The
+ * prefix always ends with a '/' character.
+ */
 const char *setup_git_directory(void)
 {
-	const char *retval = setup_git_directory_gently(NULL);
+	return setup_git_directory_gently(NULL);
+}
 
-	/* If the work tree is not the default one, recompute prefix */
-	if (inside_work_tree < 0) {
-		static char buffer[PATH_MAX + 1];
-		char *rel;
-		if (retval && chdir(retval))
-			die_errno ("Could not jump back into original cwd");
-		rel = get_relative_cwd(buffer, PATH_MAX, get_git_work_tree());
-		if (rel && *rel && chdir(get_git_work_tree()))
-			die_errno ("Could not jump to working directory");
-		return rel && *rel ? strcat(rel, "/") : NULL;
-	}
-
-	return retval;
+const char *resolve_gitdir(const char *suspect)
+{
+	if (is_git_directory(suspect))
+		return suspect;
+	return read_gitfile(suspect);
 }
diff --git a/sh-i18n--envsubst.c b/sh-i18n--envsubst.c
new file mode 100644
index 0000000..5ddd688
--- /dev/null
+++ b/sh-i18n--envsubst.c
@@ -0,0 +1,444 @@
+/*
+ * sh-i18n--envsubst.c - a stripped-down version of gettext's envsubst(1)
+ *
+ * Copyright (C) 2010 Ævar Arnfjörð Bjarmason
+ *
+ * This is a modified version of
+ * 67d0871a8c:gettext-runtime/src/envsubst.c from the gettext.git
+ * repository. It has been stripped down to only implement the
+ * envsubst(1) features that we need in the git-sh-i18n fallbacks.
+ *
+ * The "Close standard error" part in main() is from
+ * 8dac033df0:gnulib-local/lib/closeout.c. The copyright notices for
+ * both files are reproduced immediately below.
+ */
+
+#include "git-compat-util.h"
+
+/* Substitution of environment variables in shell format strings.
+   Copyright (C) 2003-2007 Free Software Foundation, Inc.
+   Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+/* closeout.c - close standard output and standard error
+   Copyright (C) 1998-2007 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* If true, substitution shall be performed on all variables.  */
+static unsigned short int all_variables;
+
+/* Forward declaration of local functions.  */
+static void print_variables (const char *string);
+static void note_variables (const char *string);
+static void subst_from_stdin (void);
+
+int
+main (int argc, char *argv[])
+{
+  /* Default values for command line options.  */
+  /* unsigned short int show_variables = 0; */
+
+  switch (argc)
+	{
+	case 1:
+	  error ("we won't substitute all variables on stdin for you");
+	  break;
+	  /*
+	  all_variables = 1;
+      subst_from_stdin ();
+	  */
+	case 2:
+	  /* echo '$foo and $bar' | git sh-i18n--envsubst --variables '$foo and $bar' */
+	  all_variables = 0;
+	  note_variables (argv[1]);
+      subst_from_stdin ();
+	  break;
+	case 3:
+	  /* git sh-i18n--envsubst --variables '$foo and $bar' */
+	  if (strcmp(argv[1], "--variables"))
+		error ("first argument must be --variables when two are given");
+	  /* show_variables = 1; */
+      print_variables (argv[2]);
+	  break;
+	default:
+	  error ("too many arguments");
+	  break;
+	}
+
+  /* Close standard error.  This is simpler than fwriteerror_no_ebadf, because
+     upon failure we don't need an errno - all we can do at this point is to
+     set an exit status.  */
+  errno = 0;
+  if (ferror (stderr) || fflush (stderr))
+    {
+      fclose (stderr);
+      exit (EXIT_FAILURE);
+    }
+  if (fclose (stderr) && errno != EBADF)
+    exit (EXIT_FAILURE);
+
+  exit (EXIT_SUCCESS);
+}
+
+/* Parse the string and invoke the callback each time a $VARIABLE or
+   ${VARIABLE} construct is seen, where VARIABLE is a nonempty sequence
+   of ASCII alphanumeric/underscore characters, starting with an ASCII
+   alphabetic/underscore character.
+   We allow only ASCII characters, to avoid dependencies w.r.t. the current
+   encoding: While "${\xe0}" looks like a variable access in ISO-8859-1
+   encoding, it doesn't look like one in the BIG5, BIG5-HKSCS, GBK, GB18030,
+   SHIFT_JIS, JOHAB encodings, because \xe0\x7d is a single character in these
+   encodings.  */
+static void
+find_variables (const char *string,
+		void (*callback) (const char *var_ptr, size_t var_len))
+{
+  for (; *string != '\0';)
+    if (*string++ == '$')
+      {
+	const char *variable_start;
+	const char *variable_end;
+	unsigned short int valid;
+	char c;
+
+	if (*string == '{')
+	  string++;
+
+	variable_start = string;
+	c = *string;
+	if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_')
+	  {
+	    do
+	      c = *++string;
+	    while ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
+		   || (c >= '0' && c <= '9') || c == '_');
+	    variable_end = string;
+
+	    if (variable_start[-1] == '{')
+	      {
+		if (*string == '}')
+		  {
+		    string++;
+		    valid = 1;
+		  }
+		else
+		  valid = 0;
+	      }
+	    else
+	      valid = 1;
+
+	    if (valid)
+	      callback (variable_start, variable_end - variable_start);
+	  }
+      }
+}
+
+
+/* Print a variable to stdout, followed by a newline.  */
+static void
+print_variable (const char *var_ptr, size_t var_len)
+{
+  fwrite (var_ptr, var_len, 1, stdout);
+  putchar ('\n');
+}
+
+/* Print the variables contained in STRING to stdout, each one followed by a
+   newline.  */
+static void
+print_variables (const char *string)
+{
+  find_variables (string, &print_variable);
+}
+
+
+/* Type describing list of immutable strings,
+   implemented using a dynamic array.  */
+typedef struct string_list_ty string_list_ty;
+struct string_list_ty
+{
+  const char **item;
+  size_t nitems;
+  size_t nitems_max;
+};
+
+/* Initialize an empty list of strings.  */
+static inline void
+string_list_init (string_list_ty *slp)
+{
+  slp->item = NULL;
+  slp->nitems = 0;
+  slp->nitems_max = 0;
+}
+
+/* Append a single string to the end of a list of strings.  */
+static inline void
+string_list_append (string_list_ty *slp, const char *s)
+{
+  /* Grow the list.  */
+  if (slp->nitems >= slp->nitems_max)
+    {
+      size_t nbytes;
+
+      slp->nitems_max = slp->nitems_max * 2 + 4;
+      nbytes = slp->nitems_max * sizeof (slp->item[0]);
+      slp->item = (const char **) xrealloc (slp->item, nbytes);
+    }
+
+  /* Add the string to the end of the list.  */
+  slp->item[slp->nitems++] = s;
+}
+
+/* Compare two strings given by reference.  */
+static int
+cmp_string (const void *pstr1, const void *pstr2)
+{
+  const char *str1 = *(const char **)pstr1;
+  const char *str2 = *(const char **)pstr2;
+
+  return strcmp (str1, str2);
+}
+
+/* Sort a list of strings.  */
+static inline void
+string_list_sort (string_list_ty *slp)
+{
+  if (slp->nitems > 0)
+    qsort (slp->item, slp->nitems, sizeof (slp->item[0]), cmp_string);
+}
+
+/* Test whether a string list contains a given string.  */
+static inline int
+string_list_member (const string_list_ty *slp, const char *s)
+{
+  size_t j;
+
+  for (j = 0; j < slp->nitems; ++j)
+    if (strcmp (slp->item[j], s) == 0)
+      return 1;
+  return 0;
+}
+
+/* Test whether a sorted string list contains a given string.  */
+static int
+sorted_string_list_member (const string_list_ty *slp, const char *s)
+{
+  size_t j1, j2;
+
+  j1 = 0;
+  j2 = slp->nitems;
+  if (j2 > 0)
+    {
+      /* Binary search.  */
+      while (j2 - j1 > 1)
+	{
+	  /* Here we know that if s is in the list, it is at an index j
+	     with j1 <= j < j2.  */
+	  size_t j = (j1 + j2) >> 1;
+	  int result = strcmp (slp->item[j], s);
+
+	  if (result > 0)
+	    j2 = j;
+	  else if (result == 0)
+	    return 1;
+	  else
+	    j1 = j + 1;
+	}
+      if (j2 > j1)
+	if (strcmp (slp->item[j1], s) == 0)
+	  return 1;
+    }
+  return 0;
+}
+
+
+/* Set of variables on which to perform substitution.
+   Used only if !all_variables.  */
+static string_list_ty variables_set;
+
+/* Adds a variable to variables_set.  */
+static void
+note_variable (const char *var_ptr, size_t var_len)
+{
+  char *string = xmalloc (var_len + 1);
+  memcpy (string, var_ptr, var_len);
+  string[var_len] = '\0';
+
+  string_list_append (&variables_set, string);
+}
+
+/* Stores the variables occurring in the string in variables_set.  */
+static void
+note_variables (const char *string)
+{
+  string_list_init (&variables_set);
+  find_variables (string, &note_variable);
+  string_list_sort (&variables_set);
+}
+
+
+static int
+do_getc (void)
+{
+  int c = getc (stdin);
+
+  if (c == EOF)
+    {
+      if (ferror (stdin))
+	error ("error while reading standard input");
+    }
+
+  return c;
+}
+
+static inline void
+do_ungetc (int c)
+{
+  if (c != EOF)
+    ungetc (c, stdin);
+}
+
+/* Copies stdin to stdout, performing substitutions.  */
+static void
+subst_from_stdin (void)
+{
+  static char *buffer;
+  static size_t bufmax;
+  static size_t buflen;
+  int c;
+
+  for (;;)
+    {
+      c = do_getc ();
+      if (c == EOF)
+	break;
+      /* Look for $VARIABLE or ${VARIABLE}.  */
+      if (c == '$')
+	{
+	  unsigned short int opening_brace = 0;
+	  unsigned short int closing_brace = 0;
+
+	  c = do_getc ();
+	  if (c == '{')
+	    {
+	      opening_brace = 1;
+	      c = do_getc ();
+	    }
+	  if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_')
+	    {
+	      unsigned short int valid;
+
+	      /* Accumulate the VARIABLE in buffer.  */
+	      buflen = 0;
+	      do
+		{
+		  if (buflen >= bufmax)
+		    {
+		      bufmax = 2 * bufmax + 10;
+		      buffer = xrealloc (buffer, bufmax);
+		    }
+		  buffer[buflen++] = c;
+
+		  c = do_getc ();
+		}
+	      while ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
+		     || (c >= '0' && c <= '9') || c == '_');
+
+	      if (opening_brace)
+		{
+		  if (c == '}')
+		    {
+		      closing_brace = 1;
+		      valid = 1;
+		    }
+		  else
+		    {
+		      valid = 0;
+		      do_ungetc (c);
+		    }
+		}
+	      else
+		{
+		  valid = 1;
+		  do_ungetc (c);
+		}
+
+	      if (valid)
+		{
+		  /* Terminate the variable in the buffer.  */
+		  if (buflen >= bufmax)
+		    {
+		      bufmax = 2 * bufmax + 10;
+		      buffer = xrealloc (buffer, bufmax);
+		    }
+		  buffer[buflen] = '\0';
+
+		  /* Test whether the variable shall be substituted.  */
+		  if (!all_variables
+		      && !sorted_string_list_member (&variables_set, buffer))
+		    valid = 0;
+		}
+
+	      if (valid)
+		{
+		  /* Substitute the variable's value from the environment.  */
+		  const char *env_value = getenv (buffer);
+
+		  if (env_value != NULL)
+		    fputs (env_value, stdout);
+		}
+	      else
+		{
+		  /* Perform no substitution at all.  Since the buffered input
+		     contains no other '$' than at the start, we can just
+		     output all the buffered contents.  */
+		  putchar ('$');
+		  if (opening_brace)
+		    putchar ('{');
+		  fwrite (buffer, buflen, 1, stdout);
+		  if (closing_brace)
+		    putchar ('}');
+		}
+	    }
+	  else
+	    {
+	      do_ungetc (c);
+	      putchar ('$');
+	      if (opening_brace)
+		putchar ('{');
+	    }
+	}
+      else
+	putchar (c);
+    }
+}
diff --git a/sha1-array.c b/sha1-array.c
new file mode 100644
index 0000000..b2f47f9
--- /dev/null
+++ b/sha1-array.c
@@ -0,0 +1,59 @@
+#include "cache.h"
+#include "sha1-array.h"
+#include "sha1-lookup.h"
+
+void sha1_array_append(struct sha1_array *array, const unsigned char *sha1)
+{
+	ALLOC_GROW(array->sha1, array->nr + 1, array->alloc);
+	hashcpy(array->sha1[array->nr++], sha1);
+	array->sorted = 0;
+}
+
+static int void_hashcmp(const void *a, const void *b)
+{
+	return hashcmp(a, b);
+}
+
+void sha1_array_sort(struct sha1_array *array)
+{
+	qsort(array->sha1, array->nr, sizeof(*array->sha1), void_hashcmp);
+	array->sorted = 1;
+}
+
+static const unsigned char *sha1_access(size_t index, void *table)
+{
+	unsigned char (*array)[20] = table;
+	return array[index];
+}
+
+int sha1_array_lookup(struct sha1_array *array, const unsigned char *sha1)
+{
+	if (!array->sorted)
+		sha1_array_sort(array);
+	return sha1_pos(sha1, array->sha1, array->nr, sha1_access);
+}
+
+void sha1_array_clear(struct sha1_array *array)
+{
+	free(array->sha1);
+	array->sha1 = NULL;
+	array->nr = 0;
+	array->alloc = 0;
+	array->sorted = 0;
+}
+
+void sha1_array_for_each_unique(struct sha1_array *array,
+				for_each_sha1_fn fn,
+				void *data)
+{
+	int i;
+
+	if (!array->sorted)
+		sha1_array_sort(array);
+
+	for (i = 0; i < array->nr; i++) {
+		if (i > 0 && !hashcmp(array->sha1[i], array->sha1[i-1]))
+			continue;
+		fn(array->sha1[i], data);
+	}
+}
diff --git a/sha1-array.h b/sha1-array.h
new file mode 100644
index 0000000..4499b5d
--- /dev/null
+++ b/sha1-array.h
@@ -0,0 +1,24 @@
+#ifndef SHA1_ARRAY_H
+#define SHA1_ARRAY_H
+
+struct sha1_array {
+	unsigned char (*sha1)[20];
+	int nr;
+	int alloc;
+	int sorted;
+};
+
+#define SHA1_ARRAY_INIT { NULL, 0, 0, 0 }
+
+void sha1_array_append(struct sha1_array *array, const unsigned char *sha1);
+void sha1_array_sort(struct sha1_array *array);
+int sha1_array_lookup(struct sha1_array *array, const unsigned char *sha1);
+void sha1_array_clear(struct sha1_array *array);
+
+typedef void (*for_each_sha1_fn)(const unsigned char sha1[20],
+				 void *data);
+void sha1_array_for_each_unique(struct sha1_array *array,
+				for_each_sha1_fn fn,
+				void *data);
+
+#endif /* SHA1_ARRAY_H */
diff --git a/sha1_file.c b/sha1_file.c
index 1d996a1..4ccaf7a 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -11,11 +11,15 @@
 #include "pack.h"
 #include "blob.h"
 #include "commit.h"
+#include "run-command.h"
 #include "tag.h"
 #include "tree.h"
+#include "tree-walk.h"
 #include "refs.h"
 #include "pack-revindex.h"
 #include "sha1-lookup.h"
+#include "bulk-checkin.h"
+#include "streaming.h"
 
 #ifndef O_NOATIME
 #if defined(__linux__) && (defined(__i386__) || defined(__PPC__))
@@ -25,69 +29,75 @@
 #endif
 #endif
 
-#ifdef NO_C99_FORMAT
-#define SZ_FMT "lu"
-static unsigned long sz_fmt(size_t s) { return (unsigned long)s; }
-#else
-#define SZ_FMT "zu"
-static size_t sz_fmt(size_t s) { return s; }
-#endif
+#define SZ_FMT PRIuMAX
+static inline uintmax_t sz_fmt(size_t s) { return s; }
 
 const unsigned char null_sha1[20];
 
-const signed char hexval_table[256] = {
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 00-07 */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 08-0f */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 10-17 */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 18-1f */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 20-27 */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 28-2f */
-	  0,  1,  2,  3,  4,  5,  6,  7,		/* 30-37 */
-	  8,  9, -1, -1, -1, -1, -1, -1,		/* 38-3f */
-	 -1, 10, 11, 12, 13, 14, 15, -1,		/* 40-47 */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 48-4f */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 50-57 */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 58-5f */
-	 -1, 10, 11, 12, 13, 14, 15, -1,		/* 60-67 */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 68-67 */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 70-77 */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 78-7f */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 80-87 */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 88-8f */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 90-97 */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 98-9f */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* a0-a7 */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* a8-af */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* b0-b7 */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* b8-bf */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* c0-c7 */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* c8-cf */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* d0-d7 */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* d8-df */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* e0-e7 */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* e8-ef */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* f0-f7 */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* f8-ff */
+/*
+ * This is meant to hold a *small* number of objects that you would
+ * want read_sha1_file() to be able to return, but yet you do not want
+ * to write them into the object store (e.g. a browse-only
+ * application).
+ */
+static struct cached_object {
+	unsigned char sha1[20];
+	enum object_type type;
+	void *buf;
+	unsigned long size;
+} *cached_objects;
+static int cached_object_nr, cached_object_alloc;
+
+static struct cached_object empty_tree = {
+	EMPTY_TREE_SHA1_BIN_LITERAL,
+	OBJ_TREE,
+	"",
+	0
 };
 
-int get_sha1_hex(const char *hex, unsigned char *sha1)
+static struct packed_git *last_found_pack;
+
+static struct cached_object *find_cached_object(const unsigned char *sha1)
 {
 	int i;
-	for (i = 0; i < 20; i++) {
-		unsigned int val = (hexval(hex[0]) << 4) | hexval(hex[1]);
-		if (val & ~0xff)
-			return -1;
-		*sha1++ = val;
-		hex += 2;
+	struct cached_object *co = cached_objects;
+
+	for (i = 0; i < cached_object_nr; i++, co++) {
+		if (!hashcmp(co->sha1, sha1))
+			return co;
 	}
-	return 0;
+	if (!hashcmp(sha1, empty_tree.sha1))
+		return &empty_tree;
+	return NULL;
 }
 
-static inline int offset_1st_component(const char *path)
+int mkdir_in_gitdir(const char *path)
 {
-	if (has_dos_drive_prefix(path))
-		return 2 + (path[2] == '/');
-	return *path == '/';
+	if (mkdir(path, 0777)) {
+		int saved_errno = errno;
+		struct stat st;
+		struct strbuf sb = STRBUF_INIT;
+
+		if (errno != EEXIST)
+			return -1;
+		/*
+		 * Are we looking at a path in a symlinked worktree
+		 * whose original repository does not yet have it?
+		 * e.g. .git/rr-cache pointing at its original
+		 * repository in which the user hasn't performed any
+		 * conflict resolution yet?
+		 */
+		if (lstat(path, &st) || !S_ISLNK(st.st_mode) ||
+		    strbuf_readlink(&sb, path, st.st_size) ||
+		    !is_absolute_path(sb.buf) ||
+		    mkdir(sb.buf, 0777)) {
+			strbuf_release(&sb);
+			errno = saved_errno;
+			return -1;
+		}
+		strbuf_release(&sb);
+	}
+	return adjust_shared_perm(path);
 }
 
 int safe_create_leading_directories(char *path)
@@ -133,24 +143,6 @@
 	return result;
 }
 
-char *sha1_to_hex(const unsigned char *sha1)
-{
-	static int bufno;
-	static char hexbuffer[4][50];
-	static const char hex[] = "0123456789abcdef";
-	char *buffer = hexbuffer[3 & ++bufno], *buf = buffer;
-	int i;
-
-	for (i = 0; i < 20; i++) {
-		unsigned int val = *sha1++;
-		*buf++ = hex[val >> 4];
-		*buf++ = hex[val & 0xf];
-	}
-	*buf = '\0';
-
-	return buffer;
-}
-
 static void fill_sha1_path(char *pathbuf, const unsigned char *sha1)
 {
 	int i;
@@ -175,20 +167,22 @@
  */
 char *sha1_file_name(const unsigned char *sha1)
 {
-	static char *name, *base;
+	static char buf[PATH_MAX];
+	const char *objdir;
+	int len;
 
-	if (!base) {
-		const char *sha1_file_directory = get_object_directory();
-		int len = strlen(sha1_file_directory);
-		base = xmalloc(len + 60);
-		memcpy(base, sha1_file_directory, len);
-		memset(base+len, 0, 60);
-		base[len] = '/';
-		base[len+3] = '/';
-		name = base + len + 1;
-	}
-	fill_sha1_path(name, sha1);
-	return base;
+	objdir = get_object_directory();
+	len = strlen(objdir);
+
+	/* '/' + sha1(2) + '/' + sha1(38) + '\0' */
+	if (len + 43 > PATH_MAX)
+		die("insanely long object directory %s", objdir);
+	memcpy(buf, objdir, len);
+	buf[len] = '/';
+	buf[len+3] = '/';
+	buf[len+42] = '\0';
+	fill_sha1_path(buf + len + 1, sha1);
+	return buf;
 }
 
 static char *sha1_get_pack_name(const unsigned char *sha1,
@@ -235,7 +229,7 @@
 struct alternate_object_database *alt_odb_list;
 static struct alternate_object_database **alt_odb_tail;
 
-static void read_info_alternates(const char * alternates, int depth);
+static int git_open_noatime(const char *name);
 
 /*
  * Prepare alternate object database registry.
@@ -257,27 +251,30 @@
 	const char *objdir = get_object_directory();
 	struct alternate_object_database *ent;
 	struct alternate_object_database *alt;
-	/* 43 = 40-byte + 2 '/' + terminating NUL */
-	int pfxlen = len;
-	int entlen = pfxlen + 43;
-	int base_len = -1;
+	int pfxlen, entlen;
+	struct strbuf pathbuf = STRBUF_INIT;
 
 	if (!is_absolute_path(entry) && relative_base) {
-		/* Relative alt-odb */
-		if (base_len < 0)
-			base_len = strlen(relative_base) + 1;
-		entlen += base_len;
-		pfxlen += base_len;
+		strbuf_addstr(&pathbuf, real_path(relative_base));
+		strbuf_addch(&pathbuf, '/');
 	}
+	strbuf_add(&pathbuf, entry, len);
+
+	normalize_path_copy(pathbuf.buf, pathbuf.buf);
+
+	pfxlen = strlen(pathbuf.buf);
+
+	/*
+	 * The trailing slash after the directory name is given by
+	 * this function at the end. Remove duplicates.
+	 */
+	while (pfxlen && pathbuf.buf[pfxlen-1] == '/')
+		pfxlen -= 1;
+
+	entlen = pfxlen + 43; /* '/' + 2 hex + '/' + 38 hex + NUL */
 	ent = xmalloc(sizeof(*ent) + entlen);
-
-	if (!is_absolute_path(entry) && relative_base) {
-		memcpy(ent->base, relative_base, base_len - 1);
-		ent->base[base_len - 1] = '/';
-		memcpy(ent->base + base_len, entry, len);
-	}
-	else
-		memcpy(ent->base, entry, pfxlen);
+	memcpy(ent->base, pathbuf.buf, pfxlen);
+	strbuf_release(&pathbuf);
 
 	ent->name = ent->base + pfxlen + 1;
 	ent->base[pfxlen + 3] = '/';
@@ -356,7 +353,7 @@
 	}
 }
 
-static void read_info_alternates(const char * relative_base, int depth)
+void read_info_alternates(const char * relative_base, int depth)
 {
 	char *map;
 	size_t mapsz;
@@ -369,7 +366,7 @@
 	int fd;
 
 	sprintf(path, "%s/%s", relative_base, alt_file_name);
-	fd = open(path, O_RDONLY);
+	fd = git_open_noatime(path);
 	if (fd < 0)
 		return;
 	if (fstat(fd, &st) || (st.st_size == 0)) {
@@ -389,7 +386,7 @@
 {
 	struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
 	int fd = hold_lock_file_for_append(lock, git_path("objects/info/alternates"), LOCK_DIE_ON_ERROR);
-	char *alt = mkpath("%s/objects\n", reference);
+	char *alt = mkpath("%s\n", reference);
 	write_or_die(fd, alt, strlen(alt));
 	if (commit_lock_file(lock))
 		die("could not close alternates file");
@@ -451,6 +448,8 @@
 static unsigned int pack_mmap_calls;
 static unsigned int peak_pack_open_windows;
 static unsigned int pack_open_windows;
+static unsigned int pack_open_fds;
+static unsigned int pack_max_fds;
 static size_t peak_pack_mapped;
 static size_t pack_mapped;
 struct packed_git *packed_git;
@@ -482,7 +481,7 @@
 	struct pack_idx_header *hdr;
 	size_t idx_size;
 	uint32_t version, nr, i, *index;
-	int fd = open(path, O_RDONLY);
+	int fd = git_open_noatime(path);
 	struct stat st;
 
 	if (fd < 0)
@@ -628,8 +627,10 @@
 			lru_l->next = lru_w->next;
 		else {
 			lru_p->windows = lru_w->next;
-			if (!lru_p->windows && lru_p->pack_fd != keep_fd) {
+			if (!lru_p->windows && lru_p->pack_fd != -1
+				&& lru_p->pack_fd != keep_fd) {
 				close(lru_p->pack_fd);
+				pack_open_fds--;
 				lru_p->pack_fd = -1;
 			}
 		}
@@ -647,6 +648,21 @@
 		; /* nothing */
 }
 
+void *xmmap(void *start, size_t length,
+	int prot, int flags, int fd, off_t offset)
+{
+	void *ret = mmap(start, length, prot, flags, fd, offset);
+	if (ret == MAP_FAILED) {
+		if (!length)
+			return NULL;
+		release_pack_memory(length, fd);
+		ret = mmap(start, length, prot, flags, fd, offset);
+		if (ret == MAP_FAILED)
+			die_errno("Out of memory? mmap failed");
+	}
+	return ret;
+}
+
 void close_pack_windows(struct packed_git *p)
 {
 	while (p->windows) {
@@ -672,6 +688,14 @@
 	}
 }
 
+void close_pack_index(struct packed_git *p)
+{
+	if (p->index_data) {
+		munmap((void *)p->index_data, p->index_size);
+		p->index_data = NULL;
+	}
+}
+
 /*
  * This is used by git-repack in case a newly created pack happens to
  * contain the same set of objects as an existing one.  In that case
@@ -691,12 +715,15 @@
 		if (strcmp(pack_name, p->pack_name) == 0) {
 			clear_delta_base_cache();
 			close_pack_windows(p);
-			if (p->pack_fd != -1)
+			if (p->pack_fd != -1) {
 				close(p->pack_fd);
-			if (p->index_data)
-				munmap((void *)p->index_data, p->index_size);
+				pack_open_fds--;
+			}
+			close_pack_index(p);
 			free(p->bad_object_sha1);
 			*pp = p->next;
+			if (last_found_pack == p)
+				last_found_pack = NULL;
 			free(p);
 			return;
 		}
@@ -719,11 +746,29 @@
 	if (!p->index_data && open_pack_index(p))
 		return error("packfile %s index unavailable", p->pack_name);
 
-	p->pack_fd = open(p->pack_name, O_RDONLY);
-	while (p->pack_fd < 0 && errno == EMFILE && unuse_one_window(p, -1))
-		p->pack_fd = open(p->pack_name, O_RDONLY);
+	if (!pack_max_fds) {
+		struct rlimit lim;
+		unsigned int max_fds;
+
+		if (getrlimit(RLIMIT_NOFILE, &lim))
+			die_errno("cannot get RLIMIT_NOFILE");
+
+		max_fds = lim.rlim_cur;
+
+		/* Save 3 for stdin/stdout/stderr, 22 for work */
+		if (25 < max_fds)
+			pack_max_fds = max_fds - 25;
+		else
+			pack_max_fds = 1;
+	}
+
+	while (pack_max_fds <= pack_open_fds && unuse_one_window(NULL, -1))
+		; /* nothing */
+
+	p->pack_fd = git_open_noatime(p->pack_name);
 	if (p->pack_fd < 0 || fstat(p->pack_fd, &st))
 		return -1;
+	pack_open_fds++;
 
 	/* If we created the struct before we had the pack we lack size. */
 	if (!p->pack_size) {
@@ -775,6 +820,7 @@
 		return 0;
 	if (p->pack_fd != -1) {
 		close(p->pack_fd);
+		pack_open_fds--;
 		p->pack_fd = -1;
 	}
 	return -1;
@@ -796,18 +842,17 @@
 unsigned char *use_pack(struct packed_git *p,
 		struct pack_window **w_cursor,
 		off_t offset,
-		unsigned int *left)
+		unsigned long *left)
 {
 	struct pack_window *win = *w_cursor;
 
-	if (p->pack_fd == -1 && open_packed_git(p))
-		die("packfile %s cannot be accessed", p->pack_name);
-
 	/* Since packfiles end in a hash of their content and it's
 	 * pointless to ask for an offset into the middle of that
 	 * hash, and the in_window function above wouldn't match
 	 * don't allow an offset too close to the end of the file.
 	 */
+	if (!p->pack_size && p->pack_fd == -1 && open_packed_git(p))
+		die("packfile %s cannot be accessed", p->pack_name);
 	if (offset > (p->pack_size - 20))
 		die("offset beyond end of packfile (truncated pack?)");
 
@@ -821,6 +866,10 @@
 		if (!win) {
 			size_t window_align = packed_git_window_size / 2;
 			off_t len;
+
+			if (p->pack_fd == -1 && open_packed_git(p))
+				die("packfile %s cannot be accessed", p->pack_name);
+
 			win = xcalloc(1, sizeof(*win));
 			win->offset = (offset / window_align) * window_align;
 			len = p->pack_size - win->offset;
@@ -838,6 +887,12 @@
 				die("packfile %s cannot be mapped: %s",
 					p->pack_name,
 					strerror(errno));
+			if (!win->offset && win->len == p->pack_size
+				&& !p->do_not_close) {
+				close(p->pack_fd);
+				pack_open_fds--;
+				p->pack_fd = -1;
+			}
 			pack_mmap_calls++;
 			pack_open_windows++;
 			if (pack_mapped > peak_pack_mapped)
@@ -867,11 +922,22 @@
 	return p;
 }
 
+static void try_to_free_pack_memory(size_t size)
+{
+	release_pack_memory(size, -1);
+}
+
 struct packed_git *add_packed_git(const char *path, int path_len, int local)
 {
+	static int have_set_try_to_free_routine;
 	struct stat st;
 	struct packed_git *p = alloc_packed_git(path_len + 2);
 
+	if (!have_set_try_to_free_routine) {
+		have_set_try_to_free_routine = 1;
+		set_try_to_free_routine(try_to_free_pack_memory);
+	}
+
 	/*
 	 * Make sure a corresponding .pack file exists and that
 	 * the index looks sane.
@@ -904,9 +970,8 @@
 	return p;
 }
 
-struct packed_git *parse_pack_index(unsigned char *sha1)
+struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path)
 {
-	const char *idx_path = sha1_pack_index_name(sha1);
 	const char *path = sha1_pack_name(sha1);
 	struct packed_git *p = alloc_packed_git(strlen(path) + 1);
 
@@ -922,6 +987,9 @@
 
 void install_packed_git(struct packed_git *pack)
 {
+	if (pack->pack_fd != -1)
+		pack_open_fds++;
+
 	pack->next = packed_git;
 	packed_git = pack;
 }
@@ -939,8 +1007,6 @@
 	sprintf(path, "%s/pack", objdir);
 	len = strlen(path);
 	dir = opendir(path);
-	while (!dir && errno == EMFILE && unuse_one_window(packed_git, -1))
-		dir = opendir(path);
 	if (!dir) {
 		if (errno != ENOENT)
 			error("unable to open object pack directory: %s: %s",
@@ -1068,7 +1134,7 @@
 	p->num_bad_objects++;
 }
 
-static int has_packed_and_bad(const unsigned char *sha1)
+static const struct packed_git *has_packed_and_bad(const unsigned char *sha1)
 {
 	struct packed_git *p;
 	unsigned i;
@@ -1076,29 +1142,71 @@
 	for (p = packed_git; p; p = p->next)
 		for (i = 0; i < p->num_bad_objects; i++)
 			if (!hashcmp(sha1, p->bad_object_sha1 + 20 * i))
-				return 1;
-	return 0;
+				return p;
+	return NULL;
 }
 
-int check_sha1_signature(const unsigned char *sha1, void *map, unsigned long size, const char *type)
+/*
+ * With an in-core object data in "map", rehash it to make sure the
+ * object name actually matches "sha1" to detect object corruption.
+ * With "map" == NULL, try reading the object named with "sha1" using
+ * the streaming interface and rehash it to do the same.
+ */
+int check_sha1_signature(const unsigned char *sha1, void *map,
+			 unsigned long size, const char *type)
 {
 	unsigned char real_sha1[20];
-	hash_sha1_file(map, size, type, real_sha1);
+	enum object_type obj_type;
+	struct git_istream *st;
+	git_SHA_CTX c;
+	char hdr[32];
+	int hdrlen;
+
+	if (map) {
+		hash_sha1_file(map, size, type, real_sha1);
+		return hashcmp(sha1, real_sha1) ? -1 : 0;
+	}
+
+	st = open_istream(sha1, &obj_type, &size, NULL);
+	if (!st)
+		return -1;
+
+	/* Generate the header */
+	hdrlen = sprintf(hdr, "%s %lu", typename(obj_type), size) + 1;
+
+	/* Sha1.. */
+	git_SHA1_Init(&c);
+	git_SHA1_Update(&c, hdr, hdrlen);
+	for (;;) {
+		char buf[1024 * 16];
+		ssize_t readlen = read_istream(st, buf, sizeof(buf));
+
+		if (!readlen)
+			break;
+		git_SHA1_Update(&c, buf, readlen);
+	}
+	git_SHA1_Final(real_sha1, &c);
+	close_istream(st);
 	return hashcmp(sha1, real_sha1) ? -1 : 0;
 }
 
 static int git_open_noatime(const char *name)
 {
 	static int sha1_file_open_flag = O_NOATIME;
-	int fd = open(name, O_RDONLY | sha1_file_open_flag);
 
-	/* Might the failure be due to O_NOATIME? */
-	if (fd < 0 && errno != ENOENT && sha1_file_open_flag) {
-		fd = open(name, O_RDONLY);
+	for (;;) {
+		int fd = open(name, O_RDONLY | sha1_file_open_flag);
 		if (fd >= 0)
+			return fd;
+
+		/* Might the failure be due to O_NOATIME? */
+		if (errno != ENOENT && sha1_file_open_flag) {
 			sha1_file_open_flag = 0;
+			continue;
+		}
+
+		return -1;
 	}
-	return fd;
 }
 
 static int open_sha1_file(const unsigned char *sha1)
@@ -1123,7 +1231,7 @@
 	return -1;
 }
 
-static void *map_sha1_file(const unsigned char *sha1, unsigned long *size)
+void *map_sha1_file(const unsigned char *sha1, unsigned long *size)
 {
 	void *map;
 	int fd;
@@ -1135,6 +1243,11 @@
 
 		if (!fstat(fd, &st)) {
 			*size = xsize_t(st.st_size);
+			if (!*size) {
+				/* mmap() is forbidden on empty files */
+				error("object file %s is empty", sha1_file_name(sha1));
+				return NULL;
+			}
 			map = xmmap(NULL, *size, PROT_READ, MAP_PRIVATE, fd, 0);
 		}
 		close(fd);
@@ -1142,20 +1255,49 @@
 	return map;
 }
 
-static int legacy_loose_object(unsigned char *map)
+/*
+ * There used to be a second loose object header format which
+ * was meant to mimic the in-pack format, allowing for direct
+ * copy of the object data.  This format turned up not to be
+ * really worth it and we no longer write loose objects in that
+ * format.
+ */
+static int experimental_loose_object(unsigned char *map)
 {
 	unsigned int word;
 
 	/*
-	 * 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
+	 * We must determine if the buffer contains the standard
+	 * zlib-deflated stream or the experimental format based
+	 * on the in-pack object format. Compare the header byte
+	 * for each format:
+	 *
+	 * RFC1950 zlib w/ deflate : 0www1000 : 0 <= www <= 7
+	 * Experimental pack-based : Stttssss : ttt = 1,2,3,4
+	 *
+	 * If bit 7 is clear and bits 0-3 equal 8, the buffer MUST be
+	 * in standard loose-object format, UNLESS it is a Git-pack
+	 * format object *exactly* 8 bytes in size when inflated.
+	 *
+	 * However, RFC1950 also specifies that the 1st 16-bit word
+	 * must be divisible by 31 - this checksum tells us our buffer
+	 * is in the standard format, giving a false positive only if
+	 * the 1st word of the Git-pack format object happens to be
+	 * divisible by 31, ie:
+	 *      ((byte0 * 256) + byte1) % 31 = 0
+	 *   =>        0ttt10000www1000 % 31 = 0
+	 *
+	 * As it happens, this case can only arise for www=3 & ttt=1
+	 * - ie, a Commit object, which would have to be 8 bytes in
+	 * size. As no Commit can be that small, we find that the
+	 * combination of these two criteria (bitmask & checksum)
+	 * can always correctly determine the buffer format.
 	 */
 	word = (map[0] << 8) + map[1];
-	if (map[0] == 0x78 && !(word % 31))
-		return 1;
-	else
+	if ((map[0] & 0x8F) == 0x08 && !(word % 31))
 		return 0;
+	else
+		return 1;
 }
 
 unsigned long unpack_object_header_buffer(const unsigned char *buf,
@@ -1172,7 +1314,8 @@
 	while (c & 0x80) {
 		if (len <= used || bitsizeof(long) <= shift) {
 			error("bad object header");
-			return 0;
+			size = used = 0;
+			break;
 		}
 		c = buf[used++];
 		size += (c & 0x7f) << shift;
@@ -1182,7 +1325,7 @@
 	return used;
 }
 
-static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz)
+int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz)
 {
 	unsigned long size, used;
 	static const char valid_loose_object_type[8] = {
@@ -1199,40 +1342,35 @@
 	stream->next_out = buffer;
 	stream->avail_out = bufsiz;
 
-	if (legacy_loose_object(map)) {
+	if (experimental_loose_object(map)) {
+		/*
+		 * The old experimental format we no longer produce;
+		 * we can still read it.
+		 */
+		used = unpack_object_header_buffer(map, mapsize, &type, &size);
+		if (!used || !valid_loose_object_type[type])
+			return -1;
+		map += used;
+		mapsize -= used;
+
+		/* Set up the stream for the rest.. */
+		stream->next_in = map;
+		stream->avail_in = mapsize;
 		git_inflate_init(stream);
-		return git_inflate(stream, 0);
+
+		/* And generate the fake traditional header */
+		stream->total_out = 1 + snprintf(buffer, bufsiz, "%s %lu",
+						 typename(type), size);
+		return 0;
 	}
-
-
-	/*
-	 * There used to be a second loose object header format which
-	 * was meant to mimic the in-pack format, allowing for direct
-	 * copy of the object data.  This format turned up not to be
-	 * really worth it and we don't write it any longer.  But we
-	 * can still read it.
-	 */
-	used = unpack_object_header_buffer(map, mapsize, &type, &size);
-	if (!used || !valid_loose_object_type[type])
-		return -1;
-	map += used;
-	mapsize -= used;
-
-	/* Set up the stream for the rest.. */
-	stream->next_in = map;
-	stream->avail_in = mapsize;
 	git_inflate_init(stream);
-
-	/* And generate the fake traditional header */
-	stream->total_out = 1 + snprintf(buffer, bufsiz, "%s %lu",
-					 typename(type), size);
-	return 0;
+	return git_inflate(stream, 0);
 }
 
-static void *unpack_sha1_rest(z_stream *stream, void *buffer, unsigned long size, const unsigned char *sha1)
+static void *unpack_sha1_rest(git_zstream *stream, void *buffer, unsigned long size, const unsigned char *sha1)
 {
 	int bytes = strlen(buffer) + 1;
-	unsigned char *buf = xmalloc(1+size);
+	unsigned char *buf = xmallocz(size);
 	unsigned long n;
 	int status = Z_OK;
 
@@ -1245,7 +1383,7 @@
 		/*
 		 * The above condition must be (bytes <= size), not
 		 * (bytes < size).  In other words, even though we
-		 * expect no more output and set avail_out to zer0,
+		 * expect no more output and set avail_out to zero,
 		 * the input zlib stream may have bytes that express
 		 * "this concludes the stream", and we *do* want to
 		 * eat that input.
@@ -1260,7 +1398,6 @@
 		while (status == Z_OK)
 			status = git_inflate(stream, Z_FINISH);
 	}
-	buf[size] = 0;
 	if (status == Z_STREAM_END && !stream->avail_in) {
 		git_inflate_end(stream);
 		return buf;
@@ -1280,7 +1417,7 @@
  * too permissive for what we want to check. So do an anal
  * object header parse by hand.
  */
-static int parse_sha1_header(const char *hdr, unsigned long *sizep)
+int parse_sha1_header(const char *hdr, unsigned long *sizep)
 {
 	char type[10];
 	int i;
@@ -1329,7 +1466,7 @@
 static void *unpack_sha1_file(void *map, unsigned long mapsize, enum object_type *type, unsigned long *size, const unsigned char *sha1)
 {
 	int ret;
-	z_stream stream;
+	git_zstream stream;
 	char hdr[8192];
 
 	ret = unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr));
@@ -1345,7 +1482,7 @@
 {
 	const unsigned char *data;
 	unsigned char delta_head[20], *in;
-	z_stream stream;
+	git_zstream stream;
 	int st;
 
 	memset(&stream, 0, sizeof(stream));
@@ -1419,7 +1556,7 @@
 
 /* forward declaration for a mutually recursive function */
 static int packed_object_info(struct packed_git *p, off_t offset,
-			      unsigned long *sizep);
+			      unsigned long *sizep, int *rtype);
 
 static int packed_delta_info(struct packed_git *p,
 			     struct pack_window **w_curs,
@@ -1433,7 +1570,7 @@
 	base_offset = get_delta_base(p, w_curs, &curpos, type, obj_offset);
 	if (!base_offset)
 		return OBJ_BAD;
-	type = packed_object_info(p, base_offset, NULL);
+	type = packed_object_info(p, base_offset, NULL, NULL);
 	if (type <= OBJ_NONE) {
 		struct revindex_entry *revidx;
 		const unsigned char *base_sha1;
@@ -1461,18 +1598,18 @@
 	return type;
 }
 
-static int unpack_object_header(struct packed_git *p,
-				struct pack_window **w_curs,
-				off_t *curpos,
-				unsigned long *sizep)
+int unpack_object_header(struct packed_git *p,
+			 struct pack_window **w_curs,
+			 off_t *curpos,
+			 unsigned long *sizep)
 {
 	unsigned char *base;
-	unsigned int left;
+	unsigned long left;
 	unsigned long used;
 	enum object_type type;
 
 	/* use_pack() assures us we have [base, base + 20) available
-	 * as a range that we can look at at.  (Its actually the hash
+	 * as a range that we can look at.  (Its actually the hash
 	 * size that is assured.)  With our object header encoding
 	 * the maximum deflated object size is 2^137, which is just
 	 * insane, so we know won't exceed what we have been given.
@@ -1487,63 +1624,8 @@
 	return type;
 }
 
-const char *packed_object_info_detail(struct packed_git *p,
-				      off_t obj_offset,
-				      unsigned long *size,
-				      unsigned long *store_size,
-				      unsigned int *delta_chain_length,
-				      unsigned char *base_sha1)
-{
-	struct pack_window *w_curs = NULL;
-	off_t curpos;
-	unsigned long dummy;
-	unsigned char *next_sha1;
-	enum object_type type;
-	struct revindex_entry *revidx;
-
-	*delta_chain_length = 0;
-	curpos = obj_offset;
-	type = unpack_object_header(p, &w_curs, &curpos, size);
-
-	revidx = find_pack_revindex(p, obj_offset);
-	*store_size = revidx[1].offset - obj_offset;
-
-	for (;;) {
-		switch (type) {
-		default:
-			die("pack %s contains unknown object type %d",
-			    p->pack_name, type);
-		case OBJ_COMMIT:
-		case OBJ_TREE:
-		case OBJ_BLOB:
-		case OBJ_TAG:
-			unuse_pack(&w_curs);
-			return typename(type);
-		case OBJ_OFS_DELTA:
-			obj_offset = get_delta_base(p, &w_curs, &curpos, type, obj_offset);
-			if (!obj_offset)
-				die("pack %s contains bad delta base reference of type %s",
-				    p->pack_name, typename(type));
-			if (*delta_chain_length == 0) {
-				revidx = find_pack_revindex(p, obj_offset);
-				hashcpy(base_sha1, nth_packed_object_sha1(p, revidx->nr));
-			}
-			break;
-		case OBJ_REF_DELTA:
-			next_sha1 = use_pack(p, &w_curs, curpos, NULL);
-			if (*delta_chain_length == 0)
-				hashcpy(base_sha1, next_sha1);
-			obj_offset = find_pack_entry_one(next_sha1, p);
-			break;
-		}
-		(*delta_chain_length)++;
-		curpos = obj_offset;
-		type = unpack_object_header(p, &w_curs, &curpos, &dummy);
-	}
-}
-
 static int packed_object_info(struct packed_git *p, off_t obj_offset,
-			      unsigned long *sizep)
+			      unsigned long *sizep, int *rtype)
 {
 	struct pack_window *w_curs = NULL;
 	unsigned long size;
@@ -1551,6 +1633,8 @@
 	enum object_type type;
 
 	type = unpack_object_header(p, &w_curs, &curpos, &size);
+	if (rtype)
+		*rtype = type; /* representation type */
 
 	switch (type) {
 	case OBJ_OFS_DELTA:
@@ -1580,20 +1664,21 @@
 				    unsigned long size)
 {
 	int st;
-	z_stream stream;
+	git_zstream stream;
 	unsigned char *buffer, *in;
 
-	buffer = xmalloc(size + 1);
-	buffer[size] = 0;
+	buffer = xmallocz(size);
 	memset(&stream, 0, sizeof(stream));
 	stream.next_out = buffer;
-	stream.avail_out = size;
+	stream.avail_out = size + 1;
 
 	git_inflate_init(&stream);
 	do {
 		in = use_pack(p, w_curs, curpos, &stream.avail_in);
 		stream.next_in = in;
 		st = git_inflate(&stream, Z_FINISH);
+		if (!stream.avail_out)
+			break; /* the payload is larger than it should be */
 		curpos += stream.next_in - in;
 	} while (st == Z_OK || st == Z_BUF_ERROR);
 	git_inflate_end(&stream);
@@ -1632,6 +1717,13 @@
 	return hash % MAX_DELTA_CACHE;
 }
 
+static int in_delta_base_cache(struct packed_git *p, off_t base_offset)
+{
+	unsigned long hash = pack_entry_hash(p, base_offset);
+	struct delta_base_cache_entry *ent = delta_base_cache + hash;
+	return (ent->data && ent->p == p && ent->base_offset == base_offset);
+}
+
 static void *cache_or_unpack_entry(struct packed_git *p, off_t base_offset,
 	unsigned long *base_size, enum object_type *type, int keep_cache)
 {
@@ -1776,6 +1868,24 @@
 	return result;
 }
 
+static void write_pack_access_log(struct packed_git *p, off_t obj_offset)
+{
+	static FILE *log_file;
+
+	if (!log_file) {
+		log_file = fopen(log_pack_access, "w");
+		if (!log_file) {
+			error("cannot open pack access log '%s' for writing: %s",
+			      log_pack_access, strerror(errno));
+			log_pack_access = NULL;
+			return;
+		}
+	}
+	fprintf(log_file, "%s %"PRIuMAX"\n",
+		p->pack_name, (uintmax_t)obj_offset);
+	fflush(log_file);
+}
+
 int do_check_packed_object_crc;
 
 void *unpack_entry(struct packed_git *p, off_t obj_offset,
@@ -1785,6 +1895,9 @@
 	off_t curpos = obj_offset;
 	void *data;
 
+	if (log_pack_access)
+		write_pack_access_log(p, obj_offset);
+
 	if (do_check_packed_object_crc && p->index_version > 1) {
 		struct revindex_entry *revidx = find_pack_revindex(p, obj_offset);
 		unsigned long len = revidx[1].offset - obj_offset;
@@ -1922,54 +2035,79 @@
 	return 0;
 }
 
+int is_pack_valid(struct packed_git *p)
+{
+	/* An already open pack is known to be valid. */
+	if (p->pack_fd != -1)
+		return 1;
+
+	/* If the pack has one window completely covering the
+	 * file size, the pack is known to be valid even if
+	 * the descriptor is not currently open.
+	 */
+	if (p->windows) {
+		struct pack_window *w = p->windows;
+
+		if (!w->offset && w->len == p->pack_size)
+			return 1;
+	}
+
+	/* Force the pack to open to prove its valid. */
+	return !open_packed_git(p);
+}
+
+static int fill_pack_entry(const unsigned char *sha1,
+			   struct pack_entry *e,
+			   struct packed_git *p)
+{
+	off_t offset;
+
+	if (p->num_bad_objects) {
+		unsigned i;
+		for (i = 0; i < p->num_bad_objects; i++)
+			if (!hashcmp(sha1, p->bad_object_sha1 + 20 * i))
+				return 0;
+	}
+
+	offset = find_pack_entry_one(sha1, p);
+	if (!offset)
+		return 0;
+
+	/*
+	 * We are about to tell the caller where they can locate the
+	 * requested object.  We better make sure the packfile is
+	 * still here and can be accessed before supplying that
+	 * answer, as it may have been deleted since the index was
+	 * loaded!
+	 */
+	if (!is_pack_valid(p)) {
+		warning("packfile %s cannot be accessed", p->pack_name);
+		return 0;
+	}
+	e->offset = offset;
+	e->p = p;
+	hashcpy(e->sha1, sha1);
+	return 1;
+}
+
 static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e)
 {
-	static struct packed_git *last_found = (void *)1;
 	struct packed_git *p;
-	off_t offset;
 
 	prepare_packed_git();
 	if (!packed_git)
 		return 0;
-	p = (last_found == (void *)1) ? packed_git : last_found;
 
-	do {
-		if (p->num_bad_objects) {
-			unsigned i;
-			for (i = 0; i < p->num_bad_objects; i++)
-				if (!hashcmp(sha1, p->bad_object_sha1 + 20 * i))
-					goto next;
-		}
+	if (last_found_pack && fill_pack_entry(sha1, e, last_found_pack))
+		return 1;
 
-		offset = find_pack_entry_one(sha1, p);
-		if (offset) {
-			/*
-			 * We are about to tell the caller where they can
-			 * locate the requested object.  We better make
-			 * sure the packfile is still here and can be
-			 * accessed before supplying that answer, as
-			 * it may have been deleted since the index
-			 * was loaded!
-			 */
-			if (p->pack_fd == -1 && open_packed_git(p)) {
-				error("packfile %s cannot be accessed", p->pack_name);
-				goto next;
-			}
-			e->offset = offset;
-			e->p = p;
-			hashcpy(e->sha1, sha1);
-			last_found = p;
-			return 1;
-		}
+	for (p = packed_git; p; p = p->next) {
+		if (p == last_found_pack || !fill_pack_entry(sha1, e, p))
+			continue;
 
-		next:
-		if (p == last_found)
-			p = packed_git;
-		else
-			p = p->next;
-		if (p == last_found)
-			p = p->next;
-	} while (p);
+		last_found_pack = p;
+		return 1;
+	}
 	return 0;
 }
 
@@ -1991,7 +2129,7 @@
 	int status;
 	unsigned long mapsize, size;
 	void *map;
-	z_stream stream;
+	git_zstream stream;
 	char hdr[32];
 
 	map = map_sha1_file(sha1, &mapsize);
@@ -2009,16 +2147,28 @@
 	return status;
 }
 
-int sha1_object_info(const unsigned char *sha1, unsigned long *sizep)
+/* returns enum object_type or negative */
+int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi)
 {
+	struct cached_object *co;
 	struct pack_entry e;
-	int status;
+	int status, rtype;
+
+	co = find_cached_object(sha1);
+	if (co) {
+		if (oi->sizep)
+			*(oi->sizep) = co->size;
+		oi->whence = OI_CACHED;
+		return co->type;
+	}
 
 	if (!find_pack_entry(sha1, &e)) {
 		/* Most likely it's a loose object. */
-		status = sha1_loose_object_info(sha1, sizep);
-		if (status >= 0)
+		status = sha1_loose_object_info(sha1, oi->sizep);
+		if (status >= 0) {
+			oi->whence = OI_LOOSE;
 			return status;
+		}
 
 		/* Not a loose object; someone else may have just packed it. */
 		reprepare_packed_git();
@@ -2026,15 +2176,31 @@
 			return status;
 	}
 
-	status = packed_object_info(e.p, e.offset, sizep);
+	status = packed_object_info(e.p, e.offset, oi->sizep, &rtype);
 	if (status < 0) {
 		mark_bad_packed_object(e.p, sha1);
-		status = sha1_object_info(sha1, sizep);
+		status = sha1_object_info_extended(sha1, oi);
+	} else if (in_delta_base_cache(e.p, e.offset)) {
+		oi->whence = OI_DBCACHED;
+	} else {
+		oi->whence = OI_PACKED;
+		oi->u.packed.offset = e.offset;
+		oi->u.packed.pack = e.p;
+		oi->u.packed.is_delta = (rtype == OBJ_REF_DELTA ||
+					 rtype == OBJ_OFS_DELTA);
 	}
 
 	return status;
 }
 
+int sha1_object_info(const unsigned char *sha1, unsigned long *sizep)
+{
+	struct object_info oi;
+
+	oi.sizep = sizep;
+	return sha1_object_info_extended(sha1, &oi);
+}
+
 static void *read_packed_sha1(const unsigned char *sha1,
 			      enum object_type *type, unsigned long *size)
 {
@@ -2059,41 +2225,6 @@
 	return data;
 }
 
-/*
- * This is meant to hold a *small* number of objects that you would
- * want read_sha1_file() to be able to return, but yet you do not want
- * to write them into the object store (e.g. a browse-only
- * application).
- */
-static struct cached_object {
-	unsigned char sha1[20];
-	enum object_type type;
-	void *buf;
-	unsigned long size;
-} *cached_objects;
-static int cached_object_nr, cached_object_alloc;
-
-static struct cached_object empty_tree = {
-	EMPTY_TREE_SHA1_BIN,
-	OBJ_TREE,
-	"",
-	0
-};
-
-static struct cached_object *find_cached_object(const unsigned char *sha1)
-{
-	int i;
-	struct cached_object *co = cached_objects;
-
-	for (i = 0; i < cached_object_nr; i++, co++) {
-		if (!hashcmp(co->sha1, sha1))
-			return co;
-	}
-	if (!hashcmp(sha1, empty_tree.sha1))
-		return &empty_tree;
-	return NULL;
-}
-
 int pretend_sha1_file(void *buf, unsigned long len, enum object_type type,
 		      unsigned char *sha1)
 {
@@ -2144,14 +2275,46 @@
 	return read_packed_sha1(sha1, type, size);
 }
 
-void *read_sha1_file(const unsigned char *sha1, enum object_type *type,
-		     unsigned long *size)
+/*
+ * This function dies on corrupt objects; the callers who want to
+ * deal with them should arrange to call read_object() and give error
+ * messages themselves.
+ */
+void *read_sha1_file_extended(const unsigned char *sha1,
+			      enum object_type *type,
+			      unsigned long *size,
+			      unsigned flag)
 {
-	void *data = read_object(sha1, type, size);
-	/* legacy behavior is to die on corrupted objects */
-	if (!data && (has_loose_object(sha1) || has_packed_and_bad(sha1)))
-		die("object %s is corrupted", sha1_to_hex(sha1));
-	return data;
+	void *data;
+	char *path;
+	const struct packed_git *p;
+	const unsigned char *repl = (flag & READ_SHA1_FILE_REPLACE)
+		? lookup_replace_object(sha1) : sha1;
+
+	errno = 0;
+	data = read_object(repl, type, size);
+	if (data)
+		return data;
+
+	if (errno && errno != ENOENT)
+		die_errno("failed to read object %s", sha1_to_hex(sha1));
+
+	/* die if we replaced an object with one that does not exist */
+	if (repl != sha1)
+		die("replacement %s not found for %s",
+		    sha1_to_hex(repl), sha1_to_hex(sha1));
+
+	if (has_loose_object(repl)) {
+		path = sha1_file_name(sha1);
+		die("loose object %s (stored in %s) is corrupt",
+		    sha1_to_hex(repl), path);
+	}
+
+	if ((p = has_packed_and_bad(repl)) != NULL)
+		die("packed object %s (stored in %s) is corrupt",
+		    sha1_to_hex(repl), p->pack_name);
+
+	return NULL;
 }
 
 void *read_object_with_reference(const unsigned char *sha1,
@@ -2253,13 +2416,13 @@
 	unlink_or_warn(tmpfile);
 	if (ret) {
 		if (ret != EEXIST) {
-			return error("unable to write sha1 filename %s: %s\n", filename, strerror(ret));
+			return error("unable to write sha1 filename %s: %s", filename, strerror(ret));
 		}
 		/* FIXME!!! Collision check here ? */
 	}
 
 out:
-	if (set_shared_perm(filename, (S_IFREG|0444)))
+	if (adjust_shared_perm(filename))
 		return error("unable to set permission to '%s'", filename);
 	return 0;
 }
@@ -2315,7 +2478,7 @@
 	}
 	memcpy(buffer, filename, dirlen);
 	strcpy(buffer + dirlen, "tmp_obj_XXXXXX");
-	fd = mkstemp(buffer);
+	fd = git_mkstemp_mode(buffer, 0444);
 	if (fd < 0 && dirlen && errno == ENOENT) {
 		/* Make sure the directory exists */
 		memcpy(buffer, filename, dirlen);
@@ -2325,79 +2488,82 @@
 
 		/* Try again */
 		strcpy(buffer + dirlen - 1, "/tmp_obj_XXXXXX");
-		fd = mkstemp(buffer);
+		fd = git_mkstemp_mode(buffer, 0444);
 	}
 	return fd;
 }
 
 static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
-			      void *buf, unsigned long len, time_t mtime)
+			      const void *buf, unsigned long len, time_t mtime)
 {
 	int fd, ret;
-	size_t size;
-	unsigned char *compressed;
-	z_stream stream;
+	unsigned char compressed[4096];
+	git_zstream stream;
+	git_SHA_CTX c;
+	unsigned char parano_sha1[20];
 	char *filename;
-	static char tmpfile[PATH_MAX];
+	static char tmp_file[PATH_MAX];
 
 	filename = sha1_file_name(sha1);
-	fd = create_tmpfile(tmpfile, sizeof(tmpfile), filename);
-	while (fd < 0 && errno == EMFILE && unuse_one_window(packed_git, -1))
-		fd = create_tmpfile(tmpfile, sizeof(tmpfile), filename);
+	fd = create_tmpfile(tmp_file, sizeof(tmp_file), filename);
 	if (fd < 0) {
 		if (errno == EACCES)
-			return error("insufficient permission for adding an object to repository database %s\n", get_object_directory());
+			return error("insufficient permission for adding an object to repository database %s", get_object_directory());
 		else
-			return error("unable to create temporary sha1 filename %s: %s\n", tmpfile, strerror(errno));
+			return error("unable to create temporary file: %s", strerror(errno));
 	}
 
 	/* Set it up */
 	memset(&stream, 0, sizeof(stream));
-	deflateInit(&stream, zlib_compression_level);
-	size = 8 + deflateBound(&stream, len+hdrlen);
-	compressed = xmalloc(size);
-
-	/* Compress it */
+	git_deflate_init(&stream, zlib_compression_level);
 	stream.next_out = compressed;
-	stream.avail_out = size;
+	stream.avail_out = sizeof(compressed);
+	git_SHA1_Init(&c);
 
 	/* First header.. */
 	stream.next_in = (unsigned char *)hdr;
 	stream.avail_in = hdrlen;
-	while (deflate(&stream, 0) == Z_OK)
-		/* nothing */;
+	while (git_deflate(&stream, 0) == Z_OK)
+		; /* nothing */
+	git_SHA1_Update(&c, hdr, hdrlen);
 
 	/* Then the data itself.. */
-	stream.next_in = buf;
+	stream.next_in = (void *)buf;
 	stream.avail_in = len;
-	ret = deflate(&stream, Z_FINISH);
+	do {
+		unsigned char *in0 = stream.next_in;
+		ret = git_deflate(&stream, Z_FINISH);
+		git_SHA1_Update(&c, in0, stream.next_in - in0);
+		if (write_buffer(fd, compressed, stream.next_out - compressed) < 0)
+			die("unable to write sha1 file");
+		stream.next_out = compressed;
+		stream.avail_out = sizeof(compressed);
+	} while (ret == Z_OK);
+
 	if (ret != Z_STREAM_END)
 		die("unable to deflate new object %s (%d)", sha1_to_hex(sha1), ret);
-
-	ret = deflateEnd(&stream);
+	ret = git_deflate_end_gently(&stream);
 	if (ret != Z_OK)
 		die("deflateEnd on object %s failed (%d)", sha1_to_hex(sha1), ret);
+	git_SHA1_Final(parano_sha1, &c);
+	if (hashcmp(sha1, parano_sha1) != 0)
+		die("confused by unstable object source data for %s", sha1_to_hex(sha1));
 
-	size = stream.total_out;
-
-	if (write_buffer(fd, compressed, size) < 0)
-		die("unable to write sha1 file");
 	close_sha1_file(fd);
-	free(compressed);
 
 	if (mtime) {
 		struct utimbuf utb;
 		utb.actime = mtime;
 		utb.modtime = mtime;
-		if (utime(tmpfile, &utb) < 0)
+		if (utime(tmp_file, &utb) < 0)
 			warning("failed utime() on %s: %s",
-				tmpfile, strerror(errno));
+				tmp_file, strerror(errno));
 	}
 
-	return move_temp_to_file(tmpfile, filename);
+	return move_temp_to_file(tmp_file, filename);
 }
 
-int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
+int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
 {
 	unsigned char sha1[20];
 	char hdr[32];
@@ -2443,14 +2609,6 @@
 	return 1;
 }
 
-int has_pack_file(const unsigned char *sha1)
-{
-	struct stat st;
-	if (stat(sha1_pack_name(sha1), &st))
-		return 0;
-	return 1;
-}
-
 int has_sha1_pack(const unsigned char *sha1)
 {
 	struct pack_entry e;
@@ -2466,10 +2624,40 @@
 	return has_loose_object(sha1);
 }
 
+static void check_tree(const void *buf, size_t size)
+{
+	struct tree_desc desc;
+	struct name_entry entry;
+
+	init_tree_desc(&desc, buf, size);
+	while (tree_entry(&desc, &entry))
+		/* do nothing
+		 * tree_entry() will die() on malformed entries */
+		;
+}
+
+static void check_commit(const void *buf, size_t size)
+{
+	struct commit c;
+	memset(&c, 0, sizeof(c));
+	if (parse_commit_buffer(&c, buf, size))
+		die("corrupt commit");
+}
+
+static void check_tag(const void *buf, size_t size)
+{
+	struct tag t;
+	memset(&t, 0, sizeof(t));
+	if (parse_tag_buffer(&t, buf, size))
+		die("corrupt tag");
+}
+
 static int index_mem(unsigned char *sha1, void *buf, size_t size,
-		     int write_object, enum object_type type, const char *path)
+		     enum object_type type,
+		     const char *path, unsigned flags)
 {
 	int ret, re_allocated = 0;
+	int write_object = flags & HASH_WRITE_OBJECT;
 
 	if (!type)
 		type = OBJ_BLOB;
@@ -2480,11 +2668,19 @@
 	if ((type == OBJ_BLOB) && path) {
 		struct strbuf nbuf = STRBUF_INIT;
 		if (convert_to_git(path, buf, size, &nbuf,
-		                   write_object ? safe_crlf : 0)) {
+				   write_object ? safe_crlf : SAFE_CRLF_FALSE)) {
 			buf = strbuf_detach(&nbuf, &size);
 			re_allocated = 1;
 		}
 	}
+	if (flags & HASH_FORMAT_CHECK) {
+		if (type == OBJ_TREE)
+			check_tree(buf, size);
+		if (type == OBJ_COMMIT)
+			check_commit(buf, size);
+		if (type == OBJ_TAG)
+			check_tag(buf, size);
+	}
 
 	if (write_object)
 		ret = write_sha1_file(buf, size, typename(type), sha1);
@@ -2495,31 +2691,85 @@
 	return ret;
 }
 
-int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object,
-	     enum object_type type, const char *path)
+static int index_pipe(unsigned char *sha1, int fd, enum object_type type,
+		      const char *path, unsigned flags)
+{
+	struct strbuf sbuf = STRBUF_INIT;
+	int ret;
+
+	if (strbuf_read(&sbuf, fd, 4096) >= 0)
+		ret = index_mem(sha1, sbuf.buf, sbuf.len, type,	path, flags);
+	else
+		ret = -1;
+	strbuf_release(&sbuf);
+	return ret;
+}
+
+#define SMALL_FILE_SIZE (32*1024)
+
+static int index_core(unsigned char *sha1, int fd, size_t size,
+		      enum object_type type, const char *path,
+		      unsigned flags)
+{
+	int ret;
+
+	if (!size) {
+		ret = index_mem(sha1, NULL, size, type, path, flags);
+	} else if (size <= SMALL_FILE_SIZE) {
+		char *buf = xmalloc(size);
+		if (size == read_in_full(fd, buf, size))
+			ret = index_mem(sha1, buf, size, type, path, flags);
+		else
+			ret = error("short read %s", strerror(errno));
+		free(buf);
+	} else {
+		void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
+		ret = index_mem(sha1, buf, size, type, path, flags);
+		munmap(buf, size);
+	}
+	return ret;
+}
+
+/*
+ * This creates one packfile per large blob unless bulk-checkin
+ * machinery is "plugged".
+ *
+ * This also bypasses the usual "convert-to-git" dance, and that is on
+ * purpose. We could write a streaming version of the converting
+ * functions and insert that before feeding the data to fast-import
+ * (or equivalent in-core API described above). However, that is
+ * somewhat complicated, as we do not know the size of the filter
+ * result, which we need to know beforehand when writing a git object.
+ * Since the primary motivation for trying to stream from the working
+ * tree file and to avoid mmaping it in core is to deal with large
+ * binary blobs, they generally do not want to get any conversion, and
+ * callers should avoid this code path when filters are requested.
+ */
+static int index_stream(unsigned char *sha1, int fd, size_t size,
+			enum object_type type, const char *path,
+			unsigned flags)
+{
+	return index_bulk_checkin(sha1, fd, size, type, path, flags);
+}
+
+int index_fd(unsigned char *sha1, int fd, struct stat *st,
+	     enum object_type type, const char *path, unsigned flags)
 {
 	int ret;
 	size_t size = xsize_t(st->st_size);
 
-	if (!S_ISREG(st->st_mode)) {
-		struct strbuf sbuf = STRBUF_INIT;
-		if (strbuf_read(&sbuf, fd, 4096) >= 0)
-			ret = index_mem(sha1, sbuf.buf, sbuf.len, write_object,
-					type, path);
-		else
-			ret = -1;
-		strbuf_release(&sbuf);
-	} else if (size) {
-		void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
-		ret = index_mem(sha1, buf, size, write_object, type, path);
-		munmap(buf, size);
-	} else
-		ret = index_mem(sha1, NULL, size, write_object, type, path);
+	if (!S_ISREG(st->st_mode))
+		ret = index_pipe(sha1, fd, type, path, flags);
+	else if (size <= big_file_threshold || type != OBJ_BLOB ||
+		 (path && would_convert_to_git(path, NULL, 0, 0)))
+		ret = index_core(sha1, fd, size, type, path, flags);
+	else
+		ret = index_stream(sha1, fd, size, type, path, flags);
 	close(fd);
 	return ret;
 }
 
-int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object)
+int index_path(unsigned char *sha1, const char *path, struct stat *st, unsigned flags)
 {
 	int fd;
 	struct strbuf sb = STRBUF_INIT;
@@ -2530,7 +2780,7 @@
 		if (fd < 0)
 			return error("open(\"%s\"): %s", path,
 				     strerror(errno));
-		if (index_fd(sha1, fd, st, write_object, OBJ_BLOB, path) < 0)
+		if (index_fd(sha1, fd, st, OBJ_BLOB, path, flags) < 0)
 			return error("%s: failed to insert into database",
 				     path);
 		break;
@@ -2540,7 +2790,7 @@
 			return error("readlink(\"%s\"): %s", path,
 			             errstr);
 		}
-		if (!write_object)
+		if (!(flags & HASH_WRITE_OBJECT))
 			hash_sha1_file(sb.buf, sb.len, blob_type, sha1);
 		else if (write_sha1_file(sb.buf, sb.len, blob_type, sha1))
 			return error("%s: failed to insert into database",
@@ -2569,3 +2819,13 @@
 		return PH_ERROR_PROTOCOL;
 	return 0;
 }
+
+void assert_sha1_type(const unsigned char *sha1, enum object_type expect)
+{
+	enum object_type type = sha1_object_info(sha1, NULL);
+	if (type < 0)
+		die("%s is not a valid object", sha1_to_hex(sha1));
+	if (type != expect)
+		die("%s is not a valid '%s' object", sha1_to_hex(sha1),
+		    typename(expect));
+}
diff --git a/sha1_name.c b/sha1_name.c
index 44bb62d..95003c7 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -5,15 +5,86 @@
 #include "blob.h"
 #include "tree-walk.h"
 #include "refs.h"
+#include "remote.h"
 
-static int find_short_object_filename(int len, const char *name, unsigned char *sha1)
+static int get_sha1_oneline(const char *, unsigned char *, struct commit_list *);
+
+typedef int (*disambiguate_hint_fn)(const unsigned char *, void *);
+
+struct disambiguate_state {
+	disambiguate_hint_fn fn;
+	void *cb_data;
+	unsigned char candidate[20];
+	unsigned candidate_exists:1;
+	unsigned candidate_checked:1;
+	unsigned candidate_ok:1;
+	unsigned disambiguate_fn_used:1;
+	unsigned ambiguous:1;
+	unsigned always_call_fn:1;
+};
+
+static void update_candidates(struct disambiguate_state *ds, const unsigned char *current)
+{
+	if (ds->always_call_fn) {
+		ds->ambiguous = ds->fn(current, ds->cb_data) ? 1 : 0;
+		return;
+	}
+	if (!ds->candidate_exists) {
+		/* this is the first candidate */
+		hashcpy(ds->candidate, current);
+		ds->candidate_exists = 1;
+		return;
+	} else if (!hashcmp(ds->candidate, current)) {
+		/* the same as what we already have seen */
+		return;
+	}
+
+	if (!ds->fn) {
+		/* cannot disambiguate between ds->candidate and current */
+		ds->ambiguous = 1;
+		return;
+	}
+
+	if (!ds->candidate_checked) {
+		ds->candidate_ok = ds->fn(ds->candidate, ds->cb_data);
+		ds->disambiguate_fn_used = 1;
+		ds->candidate_checked = 1;
+	}
+
+	if (!ds->candidate_ok) {
+		/* discard the candidate; we know it does not satisify fn */
+		hashcpy(ds->candidate, current);
+		ds->candidate_checked = 0;
+		return;
+	}
+
+	/* if we reach this point, we know ds->candidate satisfies fn */
+	if (ds->fn(current, ds->cb_data)) {
+		/*
+		 * if both current and candidate satisfy fn, we cannot
+		 * disambiguate.
+		 */
+		ds->candidate_ok = 0;
+		ds->ambiguous = 1;
+	}
+
+	/* otherwise, current can be discarded and candidate is still good */
+}
+
+static void find_short_object_filename(int len, const char *hex_pfx, struct disambiguate_state *ds)
 {
 	struct alternate_object_database *alt;
 	char hex[40];
-	int found = 0;
 	static struct alternate_object_database *fakeent;
 
 	if (!fakeent) {
+		/*
+		 * Create a "fake" alternate object database that
+		 * points to our own object database, to make it
+		 * easier to get a temporary working space in
+		 * alt->name/alt->base while iterating over the
+		 * object databases including our own.
+		 */
 		const char *objdir = get_object_directory();
 		int objdir_len = strlen(objdir);
 		int entlen = objdir_len + 43;
@@ -24,33 +95,28 @@
 	}
 	fakeent->next = alt_odb_list;
 
-	sprintf(hex, "%.2s", name);
-	for (alt = fakeent; alt && found < 2; alt = alt->next) {
+	sprintf(hex, "%.2s", hex_pfx);
+	for (alt = fakeent; alt && !ds->ambiguous; alt = alt->next) {
 		struct dirent *de;
 		DIR *dir;
-		sprintf(alt->name, "%.2s/", name);
+		sprintf(alt->name, "%.2s/", hex_pfx);
 		dir = opendir(alt->base);
 		if (!dir)
 			continue;
-		while ((de = readdir(dir)) != NULL) {
+
+		while (!ds->ambiguous && (de = readdir(dir)) != NULL) {
+			unsigned char sha1[20];
+
 			if (strlen(de->d_name) != 38)
 				continue;
-			if (memcmp(de->d_name, name + 2, len - 2))
+			if (memcmp(de->d_name, hex_pfx + 2, len - 2))
 				continue;
-			if (!found) {
-				memcpy(hex + 2, de->d_name, 38);
-				found++;
-			}
-			else if (memcmp(hex + 2, de->d_name, 38)) {
-				found = 2;
-				break;
-			}
+			memcpy(hex + 2, de->d_name, 38);
+			if (!get_sha1_hex(hex, sha1))
+				update_candidates(ds, sha1);
 		}
 		closedir(dir);
 	}
-	if (found == 1)
-		return get_sha1_hex(hex, sha1) == 0;
-	return found;
 }
 
 static int match_sha(unsigned len, const unsigned char *a, const unsigned char *b)
@@ -68,103 +134,157 @@
 	return 1;
 }
 
-static int find_short_packed_object(int len, const unsigned char *match, unsigned char *sha1)
+static void unique_in_pack(int len,
+			  const unsigned char *bin_pfx,
+			   struct packed_git *p,
+			   struct disambiguate_state *ds)
+{
+	uint32_t num, last, i, first = 0;
+	const unsigned char *current = NULL;
+
+	open_pack_index(p);
+	num = p->num_objects;
+	last = num;
+	while (first < last) {
+		uint32_t mid = (first + last) / 2;
+		const unsigned char *current;
+		int cmp;
+
+		current = nth_packed_object_sha1(p, mid);
+		cmp = hashcmp(bin_pfx, current);
+		if (!cmp) {
+			first = mid;
+			break;
+		}
+		if (cmp > 0) {
+			first = mid+1;
+			continue;
+		}
+		last = mid;
+	}
+
+	/*
+	 * At this point, "first" is the location of the lowest object
+	 * with an object name that could match "bin_pfx".  See if we have
+	 * 0, 1 or more objects that actually match(es).
+	 */
+	for (i = first; i < num && !ds->ambiguous; i++) {
+		current = nth_packed_object_sha1(p, i);
+		if (!match_sha(len, bin_pfx, current))
+			break;
+		update_candidates(ds, current);
+	}
+}
+
+static void find_short_packed_object(int len, const unsigned char *bin_pfx,
+				     struct disambiguate_state *ds)
 {
 	struct packed_git *p;
-	const unsigned char *found_sha1 = NULL;
-	int found = 0;
 
 	prepare_packed_git();
-	for (p = packed_git; p && found < 2; p = p->next) {
-		uint32_t num, last;
-		uint32_t first = 0;
-		open_pack_index(p);
-		num = p->num_objects;
-		last = num;
-		while (first < last) {
-			uint32_t mid = (first + last) / 2;
-			const unsigned char *now;
-			int cmp;
-
-			now = nth_packed_object_sha1(p, mid);
-			cmp = hashcmp(match, now);
-			if (!cmp) {
-				first = mid;
-				break;
-			}
-			if (cmp > 0) {
-				first = mid+1;
-				continue;
-			}
-			last = mid;
-		}
-		if (first < num) {
-			const unsigned char *now, *next;
-		       now = nth_packed_object_sha1(p, first);
-			if (match_sha(len, match, now)) {
-				next = nth_packed_object_sha1(p, first+1);
-			       if (!next|| !match_sha(len, match, next)) {
-					/* unique within this pack */
-					if (!found) {
-						found_sha1 = now;
-						found++;
-					}
-					else if (hashcmp(found_sha1, now)) {
-						found = 2;
-						break;
-					}
-				}
-				else {
-					/* not even unique within this pack */
-					found = 2;
-					break;
-				}
-			}
-		}
-	}
-	if (found == 1)
-		hashcpy(sha1, found_sha1);
-	return found;
+	for (p = packed_git; p && !ds->ambiguous; p = p->next)
+		unique_in_pack(len, bin_pfx, p, ds);
 }
 
 #define SHORT_NAME_NOT_FOUND (-1)
 #define SHORT_NAME_AMBIGUOUS (-2)
 
-static int find_unique_short_object(int len, char *canonical,
-				    unsigned char *res, unsigned char *sha1)
+static int finish_object_disambiguation(struct disambiguate_state *ds,
+					unsigned char *sha1)
 {
-	int has_unpacked, has_packed;
-	unsigned char unpacked_sha1[20], packed_sha1[20];
+	if (ds->ambiguous)
+		return SHORT_NAME_AMBIGUOUS;
 
-	prepare_alt_odb();
-	has_unpacked = find_short_object_filename(len, canonical, unpacked_sha1);
-	has_packed = find_short_packed_object(len, res, packed_sha1);
-	if (!has_unpacked && !has_packed)
+	if (!ds->candidate_exists)
 		return SHORT_NAME_NOT_FOUND;
-	if (1 < has_unpacked || 1 < has_packed)
+
+	if (!ds->candidate_checked)
+		/*
+		 * If this is the only candidate, there is no point
+		 * calling the disambiguation hint callback.
+		 *
+		 * On the other hand, if the current candidate
+		 * replaced an earlier candidate that did _not_ pass
+		 * the disambiguation hint callback, then we do have
+		 * more than one objects that match the short name
+		 * given, so we should make sure this one matches;
+		 * otherwise, if we discovered this one and the one
+		 * that we previously discarded in the reverse order,
+		 * we would end up showing different results in the
+		 * same repository!
+		 */
+		ds->candidate_ok = (!ds->disambiguate_fn_used ||
+				    ds->fn(ds->candidate, ds->cb_data));
+
+	if (!ds->candidate_ok)
 		return SHORT_NAME_AMBIGUOUS;
-	if (has_unpacked != has_packed) {
-		hashcpy(sha1, (has_packed ? packed_sha1 : unpacked_sha1));
-		return 0;
-	}
-	/* Both have unique ones -- do they match? */
-	if (hashcmp(packed_sha1, unpacked_sha1))
-		return SHORT_NAME_AMBIGUOUS;
-	hashcpy(sha1, packed_sha1);
+
+	hashcpy(sha1, ds->candidate);
 	return 0;
 }
 
-static int get_short_sha1(const char *name, int len, unsigned char *sha1,
-			  int quietly)
+static int disambiguate_commit_only(const unsigned char *sha1, void *cb_data_unused)
 {
-	int i, status;
-	char canonical[40];
-	unsigned char res[20];
+	int kind = sha1_object_info(sha1, NULL);
+	return kind == OBJ_COMMIT;
+}
 
-	if (len < MINIMUM_ABBREV || len > 40)
-		return -1;
-	hashclr(res);
-	memset(canonical, 'x', 40);
+static int disambiguate_committish_only(const unsigned char *sha1, void *cb_data_unused)
+{
+	struct object *obj;
+	int kind;
+
+	kind = sha1_object_info(sha1, NULL);
+	if (kind == OBJ_COMMIT)
+		return 1;
+	if (kind != OBJ_TAG)
+		return 0;
+
+	/* We need to do this the hard way... */
+	obj = deref_tag(lookup_object(sha1), NULL, 0);
+	if (obj && obj->type == OBJ_COMMIT)
+		return 1;
+	return 0;
+}
+
+static int disambiguate_tree_only(const unsigned char *sha1, void *cb_data_unused)
+{
+	int kind = sha1_object_info(sha1, NULL);
+	return kind == OBJ_TREE;
+}
+
+static int disambiguate_treeish_only(const unsigned char *sha1, void *cb_data_unused)
+{
+	struct object *obj;
+	int kind;
+
+	kind = sha1_object_info(sha1, NULL);
+	if (kind == OBJ_TREE || kind == OBJ_COMMIT)
+		return 1;
+	if (kind != OBJ_TAG)
+		return 0;
+
+	/* We need to do this the hard way... */
+	obj = deref_tag(lookup_object(sha1), NULL, 0);
+	if (obj && (obj->type == OBJ_TREE || obj->type == OBJ_COMMIT))
+		return 1;
+	return 0;
+}
+
+static int disambiguate_blob_only(const unsigned char *sha1, void *cb_data_unused)
+{
+	int kind = sha1_object_info(sha1, NULL);
+	return kind == OBJ_BLOB;
+}
+
+static int prepare_prefixes(const char *name, int len,
+			    unsigned char *bin_pfx,
+			    char *hex_pfx)
+{
+	int i;
+
+	hashclr(bin_pfx);
+	memset(hex_pfx, 'x', 40);
 	for (i = 0; i < len ;i++) {
 		unsigned char c = name[i];
 		unsigned char val;
@@ -178,18 +298,76 @@
 		}
 		else
 			return -1;
-		canonical[i] = c;
+		hex_pfx[i] = c;
 		if (!(i & 1))
 			val <<= 4;
-		res[i >> 1] |= val;
+		bin_pfx[i >> 1] |= val;
 	}
+	return 0;
+}
 
-	status = find_unique_short_object(i, canonical, res, sha1);
+static int get_short_sha1(const char *name, int len, unsigned char *sha1,
+			  unsigned flags)
+{
+	int status;
+	char hex_pfx[40];
+	unsigned char bin_pfx[20];
+	struct disambiguate_state ds;
+	int quietly = !!(flags & GET_SHA1_QUIETLY);
+
+	if (len < MINIMUM_ABBREV || len > 40)
+		return -1;
+	if (prepare_prefixes(name, len, bin_pfx, hex_pfx) < 0)
+		return -1;
+
+	prepare_alt_odb();
+
+	memset(&ds, 0, sizeof(ds));
+	if (flags & GET_SHA1_COMMIT)
+		ds.fn = disambiguate_commit_only;
+	else if (flags & GET_SHA1_COMMITTISH)
+		ds.fn = disambiguate_committish_only;
+	else if (flags & GET_SHA1_TREE)
+		ds.fn = disambiguate_tree_only;
+	else if (flags & GET_SHA1_TREEISH)
+		ds.fn = disambiguate_treeish_only;
+	else if (flags & GET_SHA1_BLOB)
+		ds.fn = disambiguate_blob_only;
+
+	find_short_object_filename(len, hex_pfx, &ds);
+	find_short_packed_object(len, bin_pfx, &ds);
+	status = finish_object_disambiguation(&ds, sha1);
+
 	if (!quietly && (status == SHORT_NAME_AMBIGUOUS))
-		return error("short SHA1 %.*s is ambiguous.", len, canonical);
+		return error("short SHA1 %.*s is ambiguous.", len, hex_pfx);
 	return status;
 }
 
+
+int for_each_abbrev(const char *prefix, each_abbrev_fn fn, void *cb_data)
+{
+	char hex_pfx[40];
+	unsigned char bin_pfx[20];
+	struct disambiguate_state ds;
+	int len = strlen(prefix);
+
+	if (len < MINIMUM_ABBREV || len > 40)
+		return -1;
+	if (prepare_prefixes(prefix, len, bin_pfx, hex_pfx) < 0)
+		return -1;
+
+	prepare_alt_odb();
+
+	memset(&ds, 0, sizeof(ds));
+	ds.always_call_fn = 1;
+	ds.cb_data = cb_data;
+	ds.fn = fn;
+
+	find_short_object_filename(len, hex_pfx, &ds);
+	find_short_packed_object(len, bin_pfx, &ds);
+	return ds.ambiguous;
+}
+
 const char *find_unique_abbrev(const unsigned char *sha1, int len)
 {
 	int status, exists;
@@ -201,7 +379,7 @@
 		return hex;
 	while (len < 40) {
 		unsigned char sha1_ret[20];
-		status = get_short_sha1(hex, len, sha1_ret, 1);
+		status = get_short_sha1(hex, len, sha1_ret, GET_SHA1_QUIETLY);
 		if (exists
 		    ? !status
 		    : status == SHORT_NAME_NOT_FOUND) {
@@ -238,96 +416,25 @@
 	return slash;
 }
 
-/*
- * *string and *len will only be substituted, and *string returned (for
- * later free()ing) if the string passed in is of the form @{-<n>}.
- */
-static char *substitute_branch_name(const char **string, int *len)
+static inline int upstream_mark(const char *string, int len)
 {
-	struct strbuf buf = STRBUF_INIT;
-	int ret = interpret_branch_name(*string, &buf);
+	const char *suffix[] = { "@{upstream}", "@{u}" };
+	int i;
 
-	if (ret == *len) {
-		size_t size;
-		*string = strbuf_detach(&buf, &size);
-		*len = size;
-		return (char *)*string;
+	for (i = 0; i < ARRAY_SIZE(suffix); i++) {
+		int suffix_len = strlen(suffix[i]);
+		if (suffix_len <= len
+		    && !memcmp(string, suffix[i], suffix_len))
+			return suffix_len;
 	}
-
-	return NULL;
+	return 0;
 }
 
-int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)
-{
-	char *last_branch = substitute_branch_name(&str, &len);
-	const char **p, *r;
-	int refs_found = 0;
-
-	*ref = NULL;
-	for (p = ref_rev_parse_rules; *p; p++) {
-		char fullref[PATH_MAX];
-		unsigned char sha1_from_ref[20];
-		unsigned char *this_result;
-		int flag;
-
-		this_result = refs_found ? sha1_from_ref : sha1;
-		mksnpath(fullref, sizeof(fullref), *p, len, str);
-		r = resolve_ref(fullref, this_result, 1, &flag);
-		if (r) {
-			if (!refs_found++)
-				*ref = xstrdup(r);
-			if (!warn_ambiguous_refs)
-				break;
-		} else if ((flag & REF_ISSYMREF) &&
-			   (len != 4 || strcmp(str, "HEAD")))
-			warning("ignoring dangling symref %s.", fullref);
-	}
-	free(last_branch);
-	return refs_found;
-}
-
-int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
-{
-	char *last_branch = substitute_branch_name(&str, &len);
-	const char **p;
-	int logs_found = 0;
-
-	*log = NULL;
-	for (p = ref_rev_parse_rules; *p; p++) {
-		struct stat st;
-		unsigned char hash[20];
-		char path[PATH_MAX];
-		const char *ref, *it;
-
-		mksnpath(path, sizeof(path), *p, len, str);
-		ref = resolve_ref(path, hash, 1, NULL);
-		if (!ref)
-			continue;
-		if (!stat(git_path("logs/%s", path), &st) &&
-		    S_ISREG(st.st_mode))
-			it = path;
-		else if (strcmp(ref, path) &&
-			 !stat(git_path("logs/%s", ref), &st) &&
-			 S_ISREG(st.st_mode))
-			it = ref;
-		else
-			continue;
-		if (!logs_found++) {
-			*log = xstrdup(it);
-			hashcpy(sha1, hash);
-		}
-		if (!warn_ambiguous_refs)
-			break;
-	}
-	free(last_branch);
-	return logs_found;
-}
-
-static int get_sha1_1(const char *name, int len, unsigned char *sha1);
+static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned lookup_flags);
 
 static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
 {
-	static const char *warning = "warning: refname '%.*s' is ambiguous.\n";
+	static const char *warn_msg = "refname '%.*s' is ambiguous.";
 	char *real_ref = NULL;
 	int refs_found = 0;
 	int at, reflog_len;
@@ -340,8 +447,10 @@
 	if (len && str[len-1] == '}') {
 		for (at = len-2; at >= 0; at--) {
 			if (str[at] == '@' && str[at+1] == '{') {
-				reflog_len = (len-1) - (at+2);
-				len = at;
+				if (!upstream_mark(str + at, len - at)) {
+					reflog_len = (len-1) - (at+2);
+					len = at;
+				}
 				break;
 			}
 		}
@@ -358,7 +467,7 @@
 		ret = interpret_branch_name(str+at, &buf);
 		if (ret > 0) {
 			/* substitute this branch name and restart */
-			return get_sha1_1(buf.buf, buf.len, sha1);
+			return get_sha1_1(buf.buf, buf.len, sha1, 0);
 		} else if (ret == 0) {
 			return -1;
 		}
@@ -373,7 +482,7 @@
 		return -1;
 
 	if (warn_ambiguous_refs && refs_found > 1)
-		fprintf(stderr, warning, len, str);
+		warning(warn_msg, len, str);
 
 	if (reflog_len) {
 		int nth, i;
@@ -381,6 +490,10 @@
 		unsigned long co_time;
 		int co_tz, co_cnt;
 
+		/* a @{-N} placed anywhere except the start is an error */
+		if (str[at+2] == '-')
+			return -1;
+
 		/* Is it asking for N-th entry, or approxidate? */
 		for (i = nth = 0; 0 <= nth && i < reflog_len; i++) {
 			char ch = str[at+2+i];
@@ -395,21 +508,24 @@
 		} else if (0 <= nth)
 			at_time = 0;
 		else {
+			int errors = 0;
 			char *tmp = xstrndup(str + at + 2, reflog_len);
-			at_time = approxidate(tmp);
+			at_time = approxidate_careful(tmp, &errors);
 			free(tmp);
+			if (errors)
+				return -1;
 		}
 		if (read_ref_at(real_ref, at_time, nth, sha1, NULL,
 				&co_time, &co_tz, &co_cnt)) {
 			if (at_time)
-				fprintf(stderr,
-					"warning: Log for '%.*s' only goes "
-					"back to %s.\n", len, str,
+				warning("Log for '%.*s' only goes "
+					"back to %s.", len, str,
 					show_date(co_time, co_tz, DATE_RFC2822));
-			else
-				fprintf(stderr,
-					"warning: Log for '%.*s' only has "
-					"%d entries.\n", len, str, co_cnt);
+			else {
+				free(real_ref);
+				die("Log for '%.*s' only has %d entries.",
+				    len, str, co_cnt);
+			}
 		}
 	}
 
@@ -421,7 +537,7 @@
 		      unsigned char *result, int idx)
 {
 	unsigned char sha1[20];
-	int ret = get_sha1_1(name, len, sha1);
+	int ret = get_sha1_1(name, len, sha1, GET_SHA1_COMMITTISH);
 	struct commit *commit;
 	struct commit_list *p;
 
@@ -454,7 +570,7 @@
 	struct commit *commit;
 	int ret;
 
-	ret = get_sha1_1(name, len, sha1);
+	ret = get_sha1_1(name, len, sha1, GET_SHA1_COMMITTISH);
 	if (ret)
 		return ret;
 	commit = lookup_commit_reference(sha1);
@@ -475,12 +591,6 @@
 {
 	if (name && !namelen)
 		namelen = strlen(name);
-	if (!o) {
-		unsigned char sha1[20];
-		if (get_sha1_1(name, namelen, sha1))
-			return NULL;
-		o = parse_object(sha1);
-	}
 	while (1) {
 		if (!o || (!o->parsed && !parse_object(o->sha1)))
 			return NULL;
@@ -506,6 +616,7 @@
 	unsigned char outer[20];
 	const char *sp;
 	unsigned int expected_type = 0;
+	unsigned lookup_flags = 0;
 	struct object *o;
 
 	/*
@@ -536,10 +647,15 @@
 		expected_type = OBJ_BLOB;
 	else if (sp[0] == '}')
 		expected_type = OBJ_NONE;
+	else if (sp[0] == '/')
+		expected_type = OBJ_COMMIT;
 	else
 		return -1;
 
-	if (get_sha1_1(name, sp - name - 2, outer))
+	if (expected_type == OBJ_COMMIT)
+		lookup_flags = GET_SHA1_COMMITTISH;
+
+	if (get_sha1_1(name, sp - name - 2, outer, lookup_flags))
 		return -1;
 
 	o = parse_object(outer);
@@ -550,19 +666,37 @@
 		if (!o || (!o->parsed && !parse_object(o->sha1)))
 			return -1;
 		hashcpy(sha1, o->sha1);
+		return 0;
 	}
-	else {
-		/*
-		 * At this point, the syntax look correct, so
-		 * if we do not get the needed object, we should
-		 * barf.
-		 */
-		o = peel_to_type(name, len, o, expected_type);
-		if (o) {
-			hashcpy(sha1, o->sha1);
-			return 0;
-		}
+
+	/*
+	 * At this point, the syntax look correct, so
+	 * if we do not get the needed object, we should
+	 * barf.
+	 */
+	o = peel_to_type(name, len, o, expected_type);
+	if (!o)
 		return -1;
+
+	hashcpy(sha1, o->sha1);
+	if (sp[0] == '/') {
+		/* "$commit^{/foo}" */
+		char *prefix;
+		int ret;
+		struct commit_list *list = NULL;
+
+		/*
+		 * $commit^{/}. Some regex implementation may reject.
+		 * We don't need regex anyway. '' pattern always matches.
+		 */
+		if (sp[1] == '}')
+			return 0;
+
+		prefix = xstrndup(sp + 1, name + len - 1 - (sp + 1));
+		commit_list_insert((struct commit *)o, &list);
+		ret = get_sha1_oneline(prefix, sha1, list);
+		free(prefix);
+		return ret;
 	}
 	return 0;
 }
@@ -570,6 +704,7 @@
 static int get_describe_name(const char *name, int len, unsigned char *sha1)
 {
 	const char *cp;
+	unsigned flags = GET_SHA1_QUIETLY | GET_SHA1_COMMIT;
 
 	for (cp = name + len - 1; name + 2 <= cp; cp--) {
 		char ch = *cp;
@@ -580,14 +715,14 @@
 			if (ch == 'g' && cp[-1] == '-') {
 				cp++;
 				len -= cp - name;
-				return get_short_sha1(cp, len, sha1, 1);
+				return get_short_sha1(cp, len, sha1, flags);
 			}
 		}
 	}
 	return -1;
 }
 
-static int get_sha1_1(const char *name, int len, unsigned char *sha1)
+static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned lookup_flags)
 {
 	int ret, has_suffix;
 	const char *cp;
@@ -632,9 +767,19 @@
 	if (!ret)
 		return 0;
 
-	return get_short_sha1(name, len, sha1, 0);
+	return get_short_sha1(name, len, sha1, lookup_flags);
 }
 
+/*
+ * This interprets names like ':/Initial revision of "git"' by searching
+ * through history and returning the first commit whose message starts
+ * the given regular expression.
+ *
+ * For future extension, ':/!' is reserved. If you want to match a message
+ * beginning with a '!', you have to repeat the exclamation mark.
+ */
+#define ONELINE_SEEN (1u<<20)
+
 static int handle_one_ref(const char *path,
 		const unsigned char *sha1, int flag, void *cb_data)
 {
@@ -649,65 +794,65 @@
 	}
 	if (object->type != OBJ_COMMIT)
 		return 0;
-	insert_by_date((struct commit *)object, list);
+	commit_list_insert_by_date((struct commit *)object, list);
 	return 0;
 }
 
-/*
- * This interprets names like ':/Initial revision of "git"' by searching
- * through history and returning the first commit whose message starts
- * with the given string.
- *
- * For future extension, ':/!' is reserved. If you want to match a message
- * beginning with a '!', you have to repeat the exclamation mark.
- */
-
-#define ONELINE_SEEN (1u<<20)
-static int get_sha1_oneline(const char *prefix, unsigned char *sha1)
+static int get_sha1_oneline(const char *prefix, unsigned char *sha1,
+			    struct commit_list *list)
 {
-	struct commit_list *list = NULL, *backup = NULL, *l;
-	int retval = -1;
-	char *temp_commit_buffer = NULL;
+	struct commit_list *backup = NULL, *l;
+	int found = 0;
+	regex_t regex;
 
 	if (prefix[0] == '!') {
 		if (prefix[1] != '!')
 			die ("Invalid search pattern: %s", prefix);
 		prefix++;
 	}
-	for_each_ref(handle_one_ref, &list);
-	for (l = list; l; l = l->next)
+
+	if (regcomp(&regex, prefix, REG_EXTENDED))
+		die("Invalid search pattern: %s", prefix);
+
+	for (l = list; l; l = l->next) {
+		l->item->object.flags |= ONELINE_SEEN;
 		commit_list_insert(l->item, &backup);
+	}
 	while (list) {
-		char *p;
+		char *p, *to_free = NULL;
 		struct commit *commit;
 		enum object_type type;
 		unsigned long size;
+		int matches;
 
 		commit = pop_most_recent_commit(&list, ONELINE_SEEN);
 		if (!parse_object(commit->object.sha1))
 			continue;
-		free(temp_commit_buffer);
 		if (commit->buffer)
 			p = commit->buffer;
 		else {
 			p = read_sha1_file(commit->object.sha1, &type, &size);
 			if (!p)
 				continue;
-			temp_commit_buffer = p;
+			to_free = p;
 		}
-		if (!(p = strstr(p, "\n\n")))
-			continue;
-		if (!prefixcmp(p + 2, prefix)) {
+
+		p = strstr(p, "\n\n");
+		matches = p && !regexec(&regex, p + 2, 0, NULL, 0);
+		free(to_free);
+
+		if (matches) {
 			hashcpy(sha1, commit->object.sha1);
-			retval = 0;
+			found = 1;
 			break;
 		}
 	}
-	free(temp_commit_buffer);
+	regfree(&regex);
 	free_commit_list(list);
 	for (l = backup; l; l = l->next)
 		clear_commit_marks(l->item, ONELINE_SEEN);
-	return retval;
+	free_commit_list(backup);
+	return found ? 0 : -1;
 }
 
 struct grab_nth_branch_switch_cbdata {
@@ -740,17 +885,10 @@
 }
 
 /*
- * This reads "@{-N}" syntax, finds the name of the Nth previous
- * branch we were on, and places the name of the branch in the given
- * buf and returns the number of characters parsed if successful.
- *
- * If the input is not of the accepted format, it returns a negative
- * number to signal an error.
- *
- * If the input was ok but there are not N branch switches in the
- * reflog, it returns 0.
+ * Parse @{-N} syntax, return the number of characters parsed
+ * if successful; otherwise signal an error with negative value.
  */
-int interpret_branch_name(const char *name, struct strbuf *buf)
+static int interpret_nth_prior_checkout(const char *name, struct strbuf *buf)
 {
 	long nth;
 	int i, retval;
@@ -794,36 +932,353 @@
 	return retval;
 }
 
+int get_sha1_mb(const char *name, unsigned char *sha1)
+{
+	struct commit *one, *two;
+	struct commit_list *mbs;
+	unsigned char sha1_tmp[20];
+	const char *dots;
+	int st;
+
+	dots = strstr(name, "...");
+	if (!dots)
+		return get_sha1(name, sha1);
+	if (dots == name)
+		st = get_sha1("HEAD", sha1_tmp);
+	else {
+		struct strbuf sb;
+		strbuf_init(&sb, dots - name);
+		strbuf_add(&sb, name, dots - name);
+		st = get_sha1_committish(sb.buf, sha1_tmp);
+		strbuf_release(&sb);
+	}
+	if (st)
+		return st;
+	one = lookup_commit_reference_gently(sha1_tmp, 0);
+	if (!one)
+		return -1;
+
+	if (get_sha1_committish(dots[3] ? (dots + 3) : "HEAD", sha1_tmp))
+		return -1;
+	two = lookup_commit_reference_gently(sha1_tmp, 0);
+	if (!two)
+		return -1;
+	mbs = get_merge_bases(one, two, 1);
+	if (!mbs || mbs->next)
+		st = -1;
+	else {
+		st = 0;
+		hashcpy(sha1, mbs->item->object.sha1);
+	}
+	free_commit_list(mbs);
+	return st;
+}
+
+/*
+ * This reads short-hand syntax that not only evaluates to a commit
+ * object name, but also can act as if the end user spelled the name
+ * of the branch from the command line.
+ *
+ * - "@{-N}" finds the name of the Nth previous branch we were on, and
+ *   places the name of the branch in the given buf and returns the
+ *   number of characters parsed if successful.
+ *
+ * - "<branch>@{upstream}" finds the name of the other ref that
+ *   <branch> is configured to merge with (missing <branch> defaults
+ *   to the current branch), and places the name of the branch in the
+ *   given buf and returns the number of characters parsed if
+ *   successful.
+ *
+ * If the input is not of the accepted format, it returns a negative
+ * number to signal an error.
+ *
+ * If the input was ok but there are not N branch switches in the
+ * reflog, it returns 0.
+ */
+int interpret_branch_name(const char *name, struct strbuf *buf)
+{
+	char *cp;
+	struct branch *upstream;
+	int namelen = strlen(name);
+	int len = interpret_nth_prior_checkout(name, buf);
+	int tmp_len;
+
+	if (!len)
+		return len; /* syntax Ok, not enough switches */
+	if (0 < len && len == namelen)
+		return len; /* consumed all */
+	else if (0 < len) {
+		/* we have extra data, which might need further processing */
+		struct strbuf tmp = STRBUF_INIT;
+		int used = buf->len;
+		int ret;
+
+		strbuf_add(buf, name + len, namelen - len);
+		ret = interpret_branch_name(buf->buf, &tmp);
+		/* that data was not interpreted, remove our cruft */
+		if (ret < 0) {
+			strbuf_setlen(buf, used);
+			return len;
+		}
+		strbuf_reset(buf);
+		strbuf_addbuf(buf, &tmp);
+		strbuf_release(&tmp);
+		/* tweak for size of {-N} versus expanded ref name */
+		return ret - used + len;
+	}
+
+	cp = strchr(name, '@');
+	if (!cp)
+		return -1;
+	tmp_len = upstream_mark(cp, namelen - (cp - name));
+	if (!tmp_len)
+		return -1;
+	len = cp + tmp_len - name;
+	cp = xstrndup(name, cp - name);
+	upstream = branch_get(*cp ? cp : NULL);
+	/*
+	 * Upstream can be NULL only if cp refers to HEAD and HEAD
+	 * points to something different than a branch.
+	 */
+	if (!upstream)
+		return error(_("HEAD does not point to a branch"));
+	if (!upstream->merge || !upstream->merge[0]->dst) {
+		if (!ref_exists(upstream->refname))
+			return error(_("No such branch: '%s'"), cp);
+		if (!upstream->merge)
+			return error(_("No upstream configured for branch '%s'"),
+				     upstream->name);
+		return error(
+			_("Upstream branch '%s' not stored as a remote-tracking branch"),
+			upstream->merge[0]->src);
+	}
+	free(cp);
+	cp = shorten_unambiguous_ref(upstream->merge[0]->dst, 0);
+	strbuf_reset(buf);
+	strbuf_addstr(buf, cp);
+	free(cp);
+	return len;
+}
+
+int strbuf_branchname(struct strbuf *sb, const char *name)
+{
+	int len = strlen(name);
+	if (interpret_branch_name(name, sb) == len)
+		return 0;
+	strbuf_add(sb, name, len);
+	return len;
+}
+
+int strbuf_check_branch_ref(struct strbuf *sb, const char *name)
+{
+	strbuf_branchname(sb, name);
+	if (name[0] == '-')
+		return -1;
+	strbuf_splice(sb, 0, 0, "refs/heads/", 11);
+	return check_refname_format(sb->buf, 0);
+}
+
 /*
  * This is like "get_sha1_basic()", except it allows "sha1 expressions",
  * notably "xyz^" for "parent of xyz"
  */
 int get_sha1(const char *name, unsigned char *sha1)
 {
-	unsigned unused;
-	return get_sha1_with_mode(name, sha1, &unused);
+	struct object_context unused;
+	return get_sha1_with_context(name, 0, sha1, &unused);
 }
 
-int get_sha1_with_mode(const char *name, unsigned char *sha1, unsigned *mode)
+/*
+ * Many callers know that the user meant to name a committish by
+ * syntactical positions where the object name appears.  Calling this
+ * function allows the machinery to disambiguate shorter-than-unique
+ * abbreviated object names between committish and others.
+ *
+ * Note that this does NOT error out when the named object is not a
+ * committish. It is merely to give a hint to the disambiguation
+ * machinery.
+ */
+int get_sha1_committish(const char *name, unsigned char *sha1)
+{
+	struct object_context unused;
+	return get_sha1_with_context(name, GET_SHA1_COMMITTISH,
+				     sha1, &unused);
+}
+
+int get_sha1_treeish(const char *name, unsigned char *sha1)
+{
+	struct object_context unused;
+	return get_sha1_with_context(name, GET_SHA1_TREEISH,
+				     sha1, &unused);
+}
+
+int get_sha1_commit(const char *name, unsigned char *sha1)
+{
+	struct object_context unused;
+	return get_sha1_with_context(name, GET_SHA1_COMMIT,
+				     sha1, &unused);
+}
+
+int get_sha1_tree(const char *name, unsigned char *sha1)
+{
+	struct object_context unused;
+	return get_sha1_with_context(name, GET_SHA1_TREE,
+				     sha1, &unused);
+}
+
+int get_sha1_blob(const char *name, unsigned char *sha1)
+{
+	struct object_context unused;
+	return get_sha1_with_context(name, GET_SHA1_BLOB,
+				     sha1, &unused);
+}
+
+/* Must be called only when object_name:filename doesn't exist. */
+static void diagnose_invalid_sha1_path(const char *prefix,
+				       const char *filename,
+				       const unsigned char *tree_sha1,
+				       const char *object_name)
+{
+	struct stat st;
+	unsigned char sha1[20];
+	unsigned mode;
+
+	if (!prefix)
+		prefix = "";
+
+	if (!lstat(filename, &st))
+		die("Path '%s' exists on disk, but not in '%s'.",
+		    filename, object_name);
+	if (errno == ENOENT || errno == ENOTDIR) {
+		char *fullname = xmalloc(strlen(filename)
+					     + strlen(prefix) + 1);
+		strcpy(fullname, prefix);
+		strcat(fullname, filename);
+
+		if (!get_tree_entry(tree_sha1, fullname,
+				    sha1, &mode)) {
+			die("Path '%s' exists, but not '%s'.\n"
+			    "Did you mean '%s:%s' aka '%s:./%s'?",
+			    fullname,
+			    filename,
+			    object_name,
+			    fullname,
+			    object_name,
+			    filename);
+		}
+		die("Path '%s' does not exist in '%s'",
+		    filename, object_name);
+	}
+}
+
+/* Must be called only when :stage:filename doesn't exist. */
+static void diagnose_invalid_index_path(int stage,
+					const char *prefix,
+					const char *filename)
+{
+	struct stat st;
+	struct cache_entry *ce;
+	int pos;
+	unsigned namelen = strlen(filename);
+	unsigned fullnamelen;
+	char *fullname;
+
+	if (!prefix)
+		prefix = "";
+
+	/* Wrong stage number? */
+	pos = cache_name_pos(filename, namelen);
+	if (pos < 0)
+		pos = -pos - 1;
+	if (pos < active_nr) {
+		ce = active_cache[pos];
+		if (ce_namelen(ce) == namelen &&
+		    !memcmp(ce->name, filename, namelen))
+			die("Path '%s' is in the index, but not at stage %d.\n"
+			    "Did you mean ':%d:%s'?",
+			    filename, stage,
+			    ce_stage(ce), filename);
+	}
+
+	/* Confusion between relative and absolute filenames? */
+	fullnamelen = namelen + strlen(prefix);
+	fullname = xmalloc(fullnamelen + 1);
+	strcpy(fullname, prefix);
+	strcat(fullname, filename);
+	pos = cache_name_pos(fullname, fullnamelen);
+	if (pos < 0)
+		pos = -pos - 1;
+	if (pos < active_nr) {
+		ce = active_cache[pos];
+		if (ce_namelen(ce) == fullnamelen &&
+		    !memcmp(ce->name, fullname, fullnamelen))
+			die("Path '%s' is in the index, but not '%s'.\n"
+			    "Did you mean ':%d:%s' aka ':%d:./%s'?",
+			    fullname, filename,
+			    ce_stage(ce), fullname,
+			    ce_stage(ce), filename);
+	}
+
+	if (!lstat(filename, &st))
+		die("Path '%s' exists on disk, but not in the index.", filename);
+	if (errno == ENOENT || errno == ENOTDIR)
+		die("Path '%s' does not exist (neither on disk nor in the index).",
+		    filename);
+
+	free(fullname);
+}
+
+
+static char *resolve_relative_path(const char *rel)
+{
+	if (prefixcmp(rel, "./") && prefixcmp(rel, "../"))
+		return NULL;
+
+	if (!startup_info)
+		die("BUG: startup_info struct is not initialized.");
+
+	if (!is_inside_work_tree())
+		die("relative path syntax can't be used outside working tree.");
+
+	/* die() inside prefix_path() if resolved path is outside worktree */
+	return prefix_path(startup_info->prefix,
+			   startup_info->prefix ? strlen(startup_info->prefix) : 0,
+			   rel);
+}
+
+static int get_sha1_with_context_1(const char *name,
+				   unsigned flags,
+				   const char *prefix,
+				   unsigned char *sha1,
+				   struct object_context *oc)
 {
 	int ret, bracket_depth;
 	int namelen = strlen(name);
 	const char *cp;
+	int only_to_die = flags & GET_SHA1_ONLY_TO_DIE;
 
-	*mode = S_IFINVALID;
-	ret = get_sha1_1(name, namelen, sha1);
+	memset(oc, 0, sizeof(*oc));
+	oc->mode = S_IFINVALID;
+	ret = get_sha1_1(name, namelen, sha1, flags);
 	if (!ret)
 		return ret;
-	/* sha1:path --> object name of path in ent sha1
-	 * :path -> object name of path in index
+	/*
+	 * sha1:path --> object name of path in ent sha1
+	 * :path -> object name of absolute path in index
+	 * :./path -> object name of path relative to cwd in index
 	 * :[0-3]:path -> object name of path in index at stage
+	 * :/foo -> recent commit matching foo
 	 */
 	if (name[0] == ':') {
 		int stage = 0;
 		struct cache_entry *ce;
+		char *new_path = NULL;
 		int pos;
-		if (namelen > 2 && name[1] == '/')
-			return get_sha1_oneline(name + 2, sha1);
+		if (!only_to_die && namelen > 2 && name[1] == '/') {
+			struct commit_list *list = NULL;
+			for_each_ref(handle_one_ref, &list);
+			return get_sha1_oneline(name + 2, sha1, list);
+		}
 		if (namelen < 3 ||
 		    name[2] != ':' ||
 		    name[1] < '0' || '3' < name[1])
@@ -832,7 +1287,18 @@
 			stage = name[1] - '0';
 			cp = name + 3;
 		}
-		namelen = namelen - (cp - name);
+		new_path = resolve_relative_path(cp);
+		if (!new_path) {
+			namelen = namelen - (cp - name);
+		} else {
+			cp = new_path;
+			namelen = strlen(cp);
+		}
+
+		strncpy(oc->path, cp,
+			sizeof(oc->path));
+		oc->path[sizeof(oc->path)-1] = '\0';
+
 		if (!active_cache)
 			read_cache();
 		pos = cache_name_pos(cp, namelen);
@@ -845,11 +1311,15 @@
 				break;
 			if (ce_stage(ce) == stage) {
 				hashcpy(sha1, ce->sha1);
-				*mode = ce->ce_mode;
+				oc->mode = ce->ce_mode;
+				free(new_path);
 				return 0;
 			}
 			pos++;
 		}
+		if (only_to_die && name[1] && name[1] != '/')
+			diagnose_invalid_index_path(stage, prefix, cp);
+		free(new_path);
 		return -1;
 	}
 	for (cp = name, bracket_depth = 0; *cp; cp++) {
@@ -862,9 +1332,55 @@
 	}
 	if (*cp == ':') {
 		unsigned char tree_sha1[20];
-		if (!get_sha1_1(name, cp-name, tree_sha1))
-			return get_tree_entry(tree_sha1, cp+1, sha1,
-					      mode);
+		char *object_name = NULL;
+		if (only_to_die) {
+			object_name = xmalloc(cp-name+1);
+			strncpy(object_name, name, cp-name);
+			object_name[cp-name] = '\0';
+		}
+		if (!get_sha1_1(name, cp-name, tree_sha1, GET_SHA1_TREEISH)) {
+			const char *filename = cp+1;
+			char *new_filename = NULL;
+
+			new_filename = resolve_relative_path(filename);
+			if (new_filename)
+				filename = new_filename;
+			ret = get_tree_entry(tree_sha1, filename, sha1, &oc->mode);
+			if (ret && only_to_die) {
+				diagnose_invalid_sha1_path(prefix, filename,
+							   tree_sha1, object_name);
+				free(object_name);
+			}
+			hashcpy(oc->tree, tree_sha1);
+			strncpy(oc->path, filename,
+				sizeof(oc->path));
+			oc->path[sizeof(oc->path)-1] = '\0';
+
+			free(new_filename);
+			return ret;
+		} else {
+			if (only_to_die)
+				die("Invalid object name '%s'.", object_name);
+		}
 	}
 	return ret;
 }
+
+/*
+ * Call this function when you know "name" given by the end user must
+ * name an object but it doesn't; the function _may_ die with a better
+ * diagnostic message than "no such object 'name'", e.g. "Path 'doc' does not
+ * exist in 'HEAD'" when given "HEAD:doc", or it may return in which case
+ * you have a chance to diagnose the error further.
+ */
+void maybe_die_on_misspelt_object_name(const char *name, const char *prefix)
+{
+	struct object_context oc;
+	unsigned char sha1[20];
+	get_sha1_with_context_1(name, GET_SHA1_ONLY_TO_DIE, prefix, sha1, &oc);
+}
+
+int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *orc)
+{
+	return get_sha1_with_context_1(str, flags, NULL, sha1, orc);
+}
diff --git a/shallow.c b/shallow.c
index 4d90eda..a0363de 100644
--- a/shallow.c
+++ b/shallow.c
@@ -47,7 +47,7 @@
 {
 	int i = 0, cur_depth = 0;
 	struct commit_list *result = NULL;
-	struct object_array stack = {0, 0, NULL};
+	struct object_array stack = OBJECT_ARRAY_INIT;
 	struct commit *commit = NULL;
 
 	while (commit || i < heads->nr || stack.nr) {
diff --git a/shell.c b/shell.c
index e4864e0..84b237f 100644
--- a/shell.c
+++ b/shell.c
@@ -2,6 +2,10 @@
 #include "quote.h"
 #include "exec_cmd.h"
 #include "strbuf.h"
+#include "run-command.h"
+
+#define COMMAND_DIR "git-shell-commands"
+#define HELP_COMMAND COMMAND_DIR "/help"
 
 static int do_generic_cmd(const char *me, char *arg)
 {
@@ -33,6 +37,86 @@
 	return execv_git_cmd(cvsserver_argv);
 }
 
+static int is_valid_cmd_name(const char *cmd)
+{
+	/* Test command contains no . or / characters */
+	return cmd[strcspn(cmd, "./")] == '\0';
+}
+
+static char *make_cmd(const char *prog)
+{
+	char *prefix = xmalloc((strlen(prog) + strlen(COMMAND_DIR) + 2));
+	strcpy(prefix, COMMAND_DIR);
+	strcat(prefix, "/");
+	strcat(prefix, prog);
+	return prefix;
+}
+
+static void cd_to_homedir(void)
+{
+	const char *home = getenv("HOME");
+	if (!home)
+		die("could not determine user's home directory; HOME is unset");
+	if (chdir(home) == -1)
+		die("could not chdir to user's home directory");
+}
+
+static void run_shell(void)
+{
+	int done = 0;
+	static const char *help_argv[] = { HELP_COMMAND, NULL };
+	/* Print help if enabled */
+	run_command_v_opt(help_argv, RUN_SILENT_EXEC_FAILURE);
+
+	do {
+		struct strbuf line = STRBUF_INIT;
+		const char *prog;
+		char *full_cmd;
+		char *rawargs;
+		char *split_args;
+		const char **argv;
+		int code;
+		int count;
+
+		fprintf(stderr, "git> ");
+		if (strbuf_getline(&line, stdin, '\n') == EOF) {
+			fprintf(stderr, "\n");
+			strbuf_release(&line);
+			break;
+		}
+		strbuf_trim(&line);
+		rawargs = strbuf_detach(&line, NULL);
+		split_args = xstrdup(rawargs);
+		count = split_cmdline(split_args, &argv);
+		if (count < 0) {
+			fprintf(stderr, "invalid command format '%s': %s\n", rawargs,
+				split_cmdline_strerror(count));
+			free(split_args);
+			free(rawargs);
+			continue;
+		}
+
+		prog = argv[0];
+		if (!strcmp(prog, "")) {
+		} else if (!strcmp(prog, "quit") || !strcmp(prog, "logout") ||
+			   !strcmp(prog, "exit") || !strcmp(prog, "bye")) {
+			done = 1;
+		} else if (is_valid_cmd_name(prog)) {
+			full_cmd = make_cmd(prog);
+			argv[0] = full_cmd;
+			code = run_command_v_opt(argv, RUN_SILENT_EXEC_FAILURE);
+			if (code == -1 && errno == ENOENT) {
+				fprintf(stderr, "unrecognized command '%s'\n", prog);
+			}
+			free(full_cmd);
+		} else {
+			fprintf(stderr, "invalid command format '%s'\n", prog);
+		}
+
+		free(argv);
+		free(rawargs);
+	} while (!done);
+}
 
 static struct commands {
 	const char *name;
@@ -48,8 +132,14 @@
 int main(int argc, char **argv)
 {
 	char *prog;
+	const char **user_argv;
 	struct commands *cmd;
 	int devnull_fd;
+	int count;
+
+	git_setup_gettext();
+
+	git_extract_argv0_path(argv[0]);
 
 	/*
 	 * Always open file descriptors 0/1/2 to avoid clobbering files
@@ -66,17 +156,28 @@
 	/*
 	 * Special hack to pretend to be a CVS server
 	 */
-	if (argc == 2 && !strcmp(argv[1], "cvs server"))
+	if (argc == 2 && !strcmp(argv[1], "cvs server")) {
 		argv--;
+	} else if (argc == 1) {
+		/* Allow the user to run an interactive shell */
+		cd_to_homedir();
+		if (access(COMMAND_DIR, R_OK | X_OK) == -1) {
+			die("Interactive git shell is not enabled.\n"
+			    "hint: ~/" COMMAND_DIR " should exist "
+			    "and have read and execute access.");
+		}
+		run_shell();
+		exit(0);
+	} else if (argc != 3 || strcmp(argv[1], "-c")) {
+		/*
+		 * We do not accept any other modes except "-c" followed by
+		 * "cmd arg", where "cmd" is a very limited subset of git
+		 * commands or a command in the COMMAND_DIR
+		 */
+		die("Run with no arguments or with -c cmd");
+	}
 
-	/*
-	 * We do not accept anything but "-c" followed by "cmd arg",
-	 * where "cmd" is a very limited subset of git commands.
-	 */
-	else if (argc != 3 || strcmp(argv[1], "-c"))
-		die("What do you think I am? A shell?");
-
-	prog = argv[2];
+	prog = xstrdup(argv[2]);
 	if (!strncmp(prog, "git", 3) && isspace(prog[3]))
 		/* Accept "git foo" as if the caller said "git-foo". */
 		prog[3] = '-';
@@ -99,5 +200,21 @@
 		}
 		exit(cmd->exec(cmd->name, arg));
 	}
-	die("unrecognized command '%s'", prog);
+
+	cd_to_homedir();
+	count = split_cmdline(prog, &user_argv);
+	if (count >= 0) {
+		if (is_valid_cmd_name(user_argv[0])) {
+			prog = make_cmd(user_argv[0]);
+			user_argv[0] = prog;
+			execv(user_argv[0], (char *const *) user_argv);
+		}
+		free(prog);
+		free(user_argv);
+		die("unrecognized command '%s'", argv[2]);
+	} else {
+		free(prog);
+		die("invalid command format '%s': %s", argv[2],
+		    split_cmdline_strerror(count));
+	}
 }
diff --git a/shortlog.h b/shortlog.h
index bc02cc2..de4f86f 100644
--- a/shortlog.h
+++ b/shortlog.h
@@ -12,6 +12,7 @@
 	int in1;
 	int in2;
 	int user_format;
+	int abbrev;
 
 	char *common_repo_prefix;
 	int email;
diff --git a/show-index.c b/show-index.c
index 45bb535..5a9eed7 100644
--- a/show-index.c
+++ b/show-index.c
@@ -1,6 +1,9 @@
 #include "cache.h"
 #include "pack.h"
 
+static const char show_index_usage[] =
+"git show-index < <packed archive index>";
+
 int main(int argc, char **argv)
 {
 	int i;
@@ -8,6 +11,10 @@
 	unsigned int version;
 	static unsigned int top_index[256];
 
+	git_setup_gettext();
+
+	if (argc != 1)
+		usage(show_index_usage);
 	if (fread(top_index, 2 * 4, 1, stdin) != 1)
 		die("unable to read header");
 	if (top_index[0] == htonl(PACK_IDX_SIGNATURE)) {
diff --git a/sideband.c b/sideband.c
index 899b1ff..d5ffa1c 100644
--- a/sideband.c
+++ b/sideband.c
@@ -135,9 +135,14 @@
 		n = sz;
 		if (packet_max - 5 < n)
 			n = packet_max - 5;
-		sprintf(hdr, "%04x", n + 5);
-		hdr[4] = band;
-		safe_write(fd, hdr, 5);
+		if (0 <= band) {
+			sprintf(hdr, "%04x", n + 5);
+			hdr[4] = band;
+			safe_write(fd, hdr, 5);
+		} else {
+			sprintf(hdr, "%04x", n + 4);
+			safe_write(fd, hdr, 4);
+		}
 		safe_write(fd, p, n);
 		p += n;
 		sz -= n;
diff --git a/strbuf.c b/strbuf.c
index f03d117..ec88266 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -10,6 +10,15 @@
 			return (unsigned char)*prefix - (unsigned char)*str;
 }
 
+int suffixcmp(const char *str, const char *suffix)
+{
+	int len = strlen(str), suflen = strlen(suffix);
+	if (len < suflen)
+		return -1;
+	else
+		return strcmp(str + len - suflen, suffix);
+}
+
 /*
  * Used as the default ->buf value, so that people can always assume
  * buf is non NULL and ->buf is NUL terminated even for a freshly
@@ -54,11 +63,15 @@
 
 void strbuf_grow(struct strbuf *sb, size_t extra)
 {
-	if (sb->len + extra + 1 <= sb->len)
+	int new_buf = !sb->alloc;
+	if (unsigned_add_overflows(extra, 1) ||
+	    unsigned_add_overflows(sb->len, extra + 1))
 		die("you want to use way too much memory");
-	if (!sb->alloc)
+	if (new_buf)
 		sb->buf = NULL;
 	ALLOC_GROW(sb->buf, sb->len + extra + 1, sb->alloc);
+	if (new_buf)
+		sb->buf[0] = '\0';
 }
 
 void strbuf_trim(struct strbuf *sb)
@@ -91,31 +104,27 @@
 	sb->buf[sb->len] = '\0';
 }
 
-void strbuf_tolower(struct strbuf *sb)
-{
-	int i;
-	for (i = 0; i < sb->len; i++)
-		sb->buf[i] = tolower(sb->buf[i]);
-}
-
-struct strbuf **strbuf_split(const struct strbuf *sb, int delim)
+struct strbuf **strbuf_split_buf(const char *str, size_t slen, int delim, int max)
 {
 	int alloc = 2, pos = 0;
-	char *n, *p;
+	const char *n, *p;
 	struct strbuf **ret;
 	struct strbuf *t;
 
 	ret = xcalloc(alloc, sizeof(struct strbuf *));
-	p = n = sb->buf;
-	while (n < sb->buf + sb->len) {
+	p = n = str;
+	while (n < str + slen) {
 		int len;
-		n = memchr(n, delim, sb->len - (n - sb->buf));
+		if (max <= 0 || pos + 1 < max)
+			n = memchr(n, delim, slen - (n - str));
+		else
+			n = NULL;
 		if (pos + 1 >= alloc) {
 			alloc = alloc * 2;
 			ret = xrealloc(ret, sizeof(struct strbuf *) * alloc);
 		}
 		if (!n)
-			n = sb->buf + sb->len - 1;
+			n = str + slen - 1;
 		len = n - p + 1;
 		t = xmalloc(sizeof(struct strbuf));
 		strbuf_init(t, len);
@@ -150,7 +159,7 @@
 void strbuf_splice(struct strbuf *sb, size_t pos, size_t len,
 				   const void *data, size_t dlen)
 {
-	if (pos + len < pos)
+	if (unsigned_add_overflows(pos, len))
 		die("you want to use way too much memory");
 	if (pos > sb->len)
 		die("`pos' is too far after the end of the buffer");
@@ -192,24 +201,29 @@
 
 void strbuf_addf(struct strbuf *sb, const char *fmt, ...)
 {
-	int len;
 	va_list ap;
+	va_start(ap, fmt);
+	strbuf_vaddf(sb, fmt, ap);
+	va_end(ap);
+}
+
+void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap)
+{
+	int len;
+	va_list cp;
 
 	if (!strbuf_avail(sb))
 		strbuf_grow(sb, 64);
-	va_start(ap, fmt);
-	len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap);
-	va_end(ap);
+	va_copy(cp, ap);
+	len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, cp);
+	va_end(cp);
 	if (len < 0)
-		die("your vsnprintf is broken");
+		die("BUG: your vsnprintf is broken (returned %d)", len);
 	if (len > strbuf_avail(sb)) {
 		strbuf_grow(sb, len);
-		va_start(ap, fmt);
 		len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap);
-		va_end(ap);
-		if (len > strbuf_avail(sb)) {
-			die("this should not happen, your snprintf is broken");
-		}
+		if (len > strbuf_avail(sb))
+			die("BUG: your vsnprintf is broken (insatiable)");
 	}
 	strbuf_setlen(sb, sb->len + len);
 }
@@ -227,6 +241,12 @@
 			break;
 		format = percent + 1;
 
+		if (*format == '%') {
+			strbuf_addch(sb, '%');
+			format++;
+			continue;
+		}
+
 		consumed = fn(sb, format, context);
 		if (consumed)
 			format += consumed;
@@ -251,6 +271,17 @@
 	return 0;
 }
 
+void strbuf_addbuf_percentquote(struct strbuf *dst, const struct strbuf *src)
+{
+	int i, len = src->len;
+
+	for (i = 0; i < len; i++) {
+		if (src->buf[i] == '%')
+			strbuf_addch(dst, '%');
+		strbuf_addch(dst, src->buf[i]);
+	}
+}
+
 size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *f)
 {
 	size_t res;
@@ -322,20 +353,19 @@
 	return -1;
 }
 
-int strbuf_getline(struct strbuf *sb, FILE *fp, int term)
+int strbuf_getwholeline(struct strbuf *sb, FILE *fp, int term)
 {
 	int ch;
 
-	strbuf_grow(sb, 0);
 	if (feof(fp))
 		return EOF;
 
 	strbuf_reset(sb);
 	while ((ch = fgetc(fp)) != EOF) {
-		if (ch == term)
-			break;
 		strbuf_grow(sb, 1);
 		sb->buf[sb->len++] = ch;
+		if (ch == term)
+			break;
 	}
 	if (ch == EOF && sb->len == 0)
 		return EOF;
@@ -344,6 +374,31 @@
 	return 0;
 }
 
+int strbuf_getline(struct strbuf *sb, FILE *fp, int term)
+{
+	if (strbuf_getwholeline(sb, fp, term))
+		return EOF;
+	if (sb->buf[sb->len-1] == term)
+		strbuf_setlen(sb, sb->len-1);
+	return 0;
+}
+
+int strbuf_getwholeline_fd(struct strbuf *sb, int fd, int term)
+{
+	strbuf_reset(sb);
+
+	while (1) {
+		char ch;
+		ssize_t len = xread(fd, &ch, 1);
+		if (len <= 0)
+			return EOF;
+		strbuf_addch(sb, ch);
+		if (ch == term)
+			break;
+	}
+	return 0;
+}
+
 int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint)
 {
 	int fd, len;
@@ -359,18 +414,86 @@
 	return len;
 }
 
-int strbuf_branchname(struct strbuf *sb, const char *name)
+void strbuf_add_lines(struct strbuf *out, const char *prefix,
+		      const char *buf, size_t size)
 {
-	int len = strlen(name);
-	if (interpret_branch_name(name, sb) == len)
-		return 0;
-	strbuf_add(sb, name, len);
-	return len;
+	while (size) {
+		const char *next = memchr(buf, '\n', size);
+		next = next ? (next + 1) : (buf + size);
+		strbuf_addstr(out, prefix);
+		strbuf_add(out, buf, next - buf);
+		size -= next - buf;
+		buf = next;
+	}
+	strbuf_complete_line(out);
 }
 
-int strbuf_check_branch_ref(struct strbuf *sb, const char *name)
+static int is_rfc3986_reserved(char ch)
 {
-	strbuf_branchname(sb, name);
-	strbuf_splice(sb, 0, 0, "refs/heads/", 11);
-	return check_ref_format(sb->buf);
+	switch (ch) {
+		case '!': case '*': case '\'': case '(': case ')': case ';':
+		case ':': case '@': case '&': case '=': case '+': case '$':
+		case ',': case '/': case '?': case '#': case '[': case ']':
+			return 1;
+	}
+	return 0;
+}
+
+static int is_rfc3986_unreserved(char ch)
+{
+	return isalnum(ch) ||
+		ch == '-' || ch == '_' || ch == '.' || ch == '~';
+}
+
+void strbuf_add_urlencode(struct strbuf *sb, const char *s, size_t len,
+			  int reserved)
+{
+	strbuf_grow(sb, len);
+	while (len--) {
+		char ch = *s++;
+		if (is_rfc3986_unreserved(ch) ||
+		    (!reserved && is_rfc3986_reserved(ch)))
+			strbuf_addch(sb, ch);
+		else
+			strbuf_addf(sb, "%%%02x", ch);
+	}
+}
+
+void strbuf_addstr_urlencode(struct strbuf *sb, const char *s,
+			     int reserved)
+{
+	strbuf_add_urlencode(sb, s, strlen(s), reserved);
+}
+
+void strbuf_addf_ln(struct strbuf *sb, const char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	strbuf_vaddf(sb, fmt, ap);
+	va_end(ap);
+	strbuf_addch(sb, '\n');
+}
+
+int printf_ln(const char *fmt, ...)
+{
+	int ret;
+	va_list ap;
+	va_start(ap, fmt);
+	ret = vprintf(fmt, ap);
+	va_end(ap);
+	if (ret < 0 || putchar('\n') == EOF)
+		return -1;
+	return ret + 1;
+}
+
+int fprintf_ln(FILE *fp, const char *fmt, ...)
+{
+	int ret;
+	va_list ap;
+	va_start(ap, fmt);
+	ret = vfprintf(fp, fmt, ap);
+	va_end(ap);
+	if (ret < 0 || putc('\n', fp) == EOF)
+		return -1;
+	return ret + 1;
 }
diff --git a/strbuf.h b/strbuf.h
index eaa8704..b888d40 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -1,44 +1,7 @@
 #ifndef STRBUF_H
 #define STRBUF_H
 
-/*
- * Strbuf's can be use in many ways: as a byte array, or to store arbitrary
- * long, overflow safe strings.
- *
- * Strbufs has some invariants that are very important to keep in mind:
- *
- * 1. the ->buf member is always malloc-ed, hence strbuf's can be used to
- *    build complex strings/buffers whose final size isn't easily known.
- *
- *    It is NOT legal to copy the ->buf pointer away.
- *    `strbuf_detach' is the operation that detaches a buffer from its shell
- *    while keeping the shell valid wrt its invariants.
- *
- * 2. the ->buf member is a byte array that has at least ->len + 1 bytes
- *    allocated. The extra byte is used to store a '\0', allowing the ->buf
- *    member to be a valid C-string. Every strbuf function ensure this
- *    invariant is preserved.
- *
- *    Note that it is OK to "play" with the buffer directly if you work it
- *    that way:
- *
- *    strbuf_grow(sb, SOME_SIZE);
- *       ... Here, the memory array starting at sb->buf, and of length
- *       ... strbuf_avail(sb) is all yours, and you are sure that
- *       ... strbuf_avail(sb) is at least SOME_SIZE.
- *    strbuf_setlen(sb, sb->len + SOME_OTHER_SIZE);
- *
- *    Of course, SOME_OTHER_SIZE must be smaller or equal to strbuf_avail(sb).
- *
- *    Doing so is safe, though if it has to be done in many places, adding the
- *    missing API to the strbuf module is the way to go.
- *
- *    XXX: do _not_ assume that the area that is yours is of size ->alloc - 1
- *         even if it's true in the current implementation. Alloc is somehow a
- *         "private" member that should not be messed with.
- */
-
-#include <assert.h>
+/* See Documentation/technical/api-strbuf.txt */
 
 extern char strbuf_slopbuf[];
 struct strbuf {
@@ -68,9 +31,8 @@
 extern void strbuf_grow(struct strbuf *, size_t);
 
 static inline void strbuf_setlen(struct strbuf *sb, size_t len) {
-	if (!sb->alloc)
-		strbuf_grow(sb, 0);
-	assert(len < sb->alloc);
+	if (len > (sb->alloc ? sb->alloc - 1 : 0))
+		die("BUG: strbuf_setlen() beyond buffer");
 	sb->len = len;
 	sb->buf[len] = '\0';
 }
@@ -81,9 +43,23 @@
 extern void strbuf_rtrim(struct strbuf *);
 extern void strbuf_ltrim(struct strbuf *);
 extern int strbuf_cmp(const struct strbuf *, const struct strbuf *);
-extern void strbuf_tolower(struct strbuf *);
 
-extern struct strbuf **strbuf_split(const struct strbuf *, int delim);
+extern struct strbuf **strbuf_split_buf(const char *, size_t,
+					int delim, int max);
+static inline struct strbuf **strbuf_split_str(const char *str,
+					       int delim, int max)
+{
+	return strbuf_split_buf(str, strlen(str), delim, max);
+}
+static inline struct strbuf **strbuf_split_max(const struct strbuf *sb,
+						int delim, int max)
+{
+	return strbuf_split_buf(sb->buf, sb->len, delim, max);
+}
+static inline struct strbuf **strbuf_split(const struct strbuf *sb, int delim)
+{
+	return strbuf_split_max(sb, delim, 0);
+}
 extern void strbuf_list_free(struct strbuf **);
 
 /*----- add data in your buffer -----*/
@@ -105,6 +81,7 @@
 	strbuf_add(sb, s, strlen(s));
 }
 static inline void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2) {
+	strbuf_grow(sb, sb2->len);
 	strbuf_add(sb, sb2->buf, sb2->len);
 }
 extern void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len);
@@ -116,9 +93,22 @@
 	const char *value;
 };
 extern size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder, void *context);
+extern void strbuf_addbuf_percentquote(struct strbuf *dst, const struct strbuf *src);
 
-__attribute__((format(printf,2,3)))
+__attribute__((format (printf,2,3)))
 extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
+__attribute__((format (printf,2,0)))
+extern void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap);
+__attribute__((format (printf,2,3)))
+extern void strbuf_addf_ln(struct strbuf *sb, const char *fmt, ...);
+
+extern void strbuf_add_lines(struct strbuf *sb, const char *prefix, const char *buf, size_t size);
+
+static inline void strbuf_complete_line(struct strbuf *sb)
+{
+	if (sb->len && sb->buf[sb->len - 1] != '\n')
+		strbuf_addch(sb, '\n');
+}
 
 extern size_t strbuf_fread(struct strbuf *, size_t, FILE *);
 /* XXX: if read fails, any partial read is undone */
@@ -126,7 +116,9 @@
 extern int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint);
 extern int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint);
 
+extern int strbuf_getwholeline(struct strbuf *, FILE *, int);
 extern int strbuf_getline(struct strbuf *, FILE *, int);
+extern int strbuf_getwholeline_fd(struct strbuf *, int, int);
 
 extern void stripspace(struct strbuf *buf, int skip_comments);
 extern int launch_editor(const char *path, struct strbuf *buffer, const char *const *env);
@@ -134,4 +126,14 @@
 extern int strbuf_branchname(struct strbuf *sb, const char *name);
 extern int strbuf_check_branch_ref(struct strbuf *sb, const char *name);
 
+extern void strbuf_add_urlencode(struct strbuf *, const char *, size_t,
+				 int reserved);
+extern void strbuf_addstr_urlencode(struct strbuf *, const char *,
+				    int reserved);
+
+__attribute__((format (printf,1,2)))
+extern int printf_ln(const char *fmt, ...);
+__attribute__((format (printf,2,3)))
+extern int fprintf_ln(FILE *fp, const char *fmt, ...);
+
 #endif /* STRBUF_H */
diff --git a/streaming.c b/streaming.c
new file mode 100644
index 0000000..4d978e5
--- /dev/null
+++ b/streaming.c
@@ -0,0 +1,546 @@
+/*
+ * Copyright (c) 2011, Google Inc.
+ */
+#include "cache.h"
+#include "streaming.h"
+
+enum input_source {
+	stream_error = -1,
+	incore = 0,
+	loose = 1,
+	pack_non_delta = 2
+};
+
+typedef int (*open_istream_fn)(struct git_istream *,
+			       struct object_info *,
+			       const unsigned char *,
+			       enum object_type *);
+typedef int (*close_istream_fn)(struct git_istream *);
+typedef ssize_t (*read_istream_fn)(struct git_istream *, char *, size_t);
+
+struct stream_vtbl {
+	close_istream_fn close;
+	read_istream_fn read;
+};
+
+#define open_method_decl(name) \
+	int open_istream_ ##name \
+	(struct git_istream *st, struct object_info *oi, \
+	 const unsigned char *sha1, \
+	 enum object_type *type)
+
+#define close_method_decl(name) \
+	int close_istream_ ##name \
+	(struct git_istream *st)
+
+#define read_method_decl(name) \
+	ssize_t read_istream_ ##name \
+	(struct git_istream *st, char *buf, size_t sz)
+
+/* forward declaration */
+static open_method_decl(incore);
+static open_method_decl(loose);
+static open_method_decl(pack_non_delta);
+static struct git_istream *attach_stream_filter(struct git_istream *st,
+						struct stream_filter *filter);
+
+
+static open_istream_fn open_istream_tbl[] = {
+	open_istream_incore,
+	open_istream_loose,
+	open_istream_pack_non_delta,
+};
+
+#define FILTER_BUFFER (1024*16)
+
+struct filtered_istream {
+	struct git_istream *upstream;
+	struct stream_filter *filter;
+	char ibuf[FILTER_BUFFER];
+	char obuf[FILTER_BUFFER];
+	int i_end, i_ptr;
+	int o_end, o_ptr;
+	int input_finished;
+};
+
+struct git_istream {
+	const struct stream_vtbl *vtbl;
+	unsigned long size; /* inflated size of full object */
+	git_zstream z;
+	enum { z_unused, z_used, z_done, z_error } z_state;
+
+	union {
+		struct {
+			char *buf; /* from read_object() */
+			unsigned long read_ptr;
+		} incore;
+
+		struct {
+			void *mapped;
+			unsigned long mapsize;
+			char hdr[32];
+			int hdr_avail;
+			int hdr_used;
+		} loose;
+
+		struct {
+			struct packed_git *pack;
+			off_t pos;
+		} in_pack;
+
+		struct filtered_istream filtered;
+	} u;
+};
+
+int close_istream(struct git_istream *st)
+{
+	int r = st->vtbl->close(st);
+	free(st);
+	return r;
+}
+
+ssize_t read_istream(struct git_istream *st, void *buf, size_t sz)
+{
+	return st->vtbl->read(st, buf, sz);
+}
+
+static enum input_source istream_source(const unsigned char *sha1,
+					enum object_type *type,
+					struct object_info *oi)
+{
+	unsigned long size;
+	int status;
+
+	oi->sizep = &size;
+	status = sha1_object_info_extended(sha1, oi);
+	if (status < 0)
+		return stream_error;
+	*type = status;
+
+	switch (oi->whence) {
+	case OI_LOOSE:
+		return loose;
+	case OI_PACKED:
+		if (!oi->u.packed.is_delta && big_file_threshold < size)
+			return pack_non_delta;
+		/* fallthru */
+	default:
+		return incore;
+	}
+}
+
+struct git_istream *open_istream(const unsigned char *sha1,
+				 enum object_type *type,
+				 unsigned long *size,
+				 struct stream_filter *filter)
+{
+	struct git_istream *st;
+	struct object_info oi;
+	const unsigned char *real = lookup_replace_object(sha1);
+	enum input_source src = istream_source(real, type, &oi);
+
+	if (src < 0)
+		return NULL;
+
+	st = xmalloc(sizeof(*st));
+	if (open_istream_tbl[src](st, &oi, real, type)) {
+		if (open_istream_incore(st, &oi, real, type)) {
+			free(st);
+			return NULL;
+		}
+	}
+	if (st && filter) {
+		/* Add "&& !is_null_stream_filter(filter)" for performance */
+		struct git_istream *nst = attach_stream_filter(st, filter);
+		if (!nst)
+			close_istream(st);
+		st = nst;
+	}
+
+	*size = st->size;
+	return st;
+}
+
+
+/*****************************************************************
+ *
+ * Common helpers
+ *
+ *****************************************************************/
+
+static void close_deflated_stream(struct git_istream *st)
+{
+	if (st->z_state == z_used)
+		git_inflate_end(&st->z);
+}
+
+
+/*****************************************************************
+ *
+ * Filtered stream
+ *
+ *****************************************************************/
+
+static close_method_decl(filtered)
+{
+	free_stream_filter(st->u.filtered.filter);
+	return close_istream(st->u.filtered.upstream);
+}
+
+static read_method_decl(filtered)
+{
+	struct filtered_istream *fs = &(st->u.filtered);
+	size_t filled = 0;
+
+	while (sz) {
+		/* do we already have filtered output? */
+		if (fs->o_ptr < fs->o_end) {
+			size_t to_move = fs->o_end - fs->o_ptr;
+			if (sz < to_move)
+				to_move = sz;
+			memcpy(buf + filled, fs->obuf + fs->o_ptr, to_move);
+			fs->o_ptr += to_move;
+			sz -= to_move;
+			filled += to_move;
+			continue;
+		}
+		fs->o_end = fs->o_ptr = 0;
+
+		/* do we have anything to feed the filter with? */
+		if (fs->i_ptr < fs->i_end) {
+			size_t to_feed = fs->i_end - fs->i_ptr;
+			size_t to_receive = FILTER_BUFFER;
+			if (stream_filter(fs->filter,
+					  fs->ibuf + fs->i_ptr, &to_feed,
+					  fs->obuf, &to_receive))
+				return -1;
+			fs->i_ptr = fs->i_end - to_feed;
+			fs->o_end = FILTER_BUFFER - to_receive;
+			continue;
+		}
+
+		/* tell the filter to drain upon no more input */
+		if (fs->input_finished) {
+			size_t to_receive = FILTER_BUFFER;
+			if (stream_filter(fs->filter,
+					  NULL, NULL,
+					  fs->obuf, &to_receive))
+				return -1;
+			fs->o_end = FILTER_BUFFER - to_receive;
+			if (!fs->o_end)
+				break;
+			continue;
+		}
+		fs->i_end = fs->i_ptr = 0;
+
+		/* refill the input from the upstream */
+		if (!fs->input_finished) {
+			fs->i_end = read_istream(fs->upstream, fs->ibuf, FILTER_BUFFER);
+			if (fs->i_end < 0)
+				break;
+			if (fs->i_end)
+				continue;
+		}
+		fs->input_finished = 1;
+	}
+	return filled;
+}
+
+static struct stream_vtbl filtered_vtbl = {
+	close_istream_filtered,
+	read_istream_filtered,
+};
+
+static struct git_istream *attach_stream_filter(struct git_istream *st,
+						struct stream_filter *filter)
+{
+	struct git_istream *ifs = xmalloc(sizeof(*ifs));
+	struct filtered_istream *fs = &(ifs->u.filtered);
+
+	ifs->vtbl = &filtered_vtbl;
+	fs->upstream = st;
+	fs->filter = filter;
+	fs->i_end = fs->i_ptr = 0;
+	fs->o_end = fs->o_ptr = 0;
+	fs->input_finished = 0;
+	ifs->size = -1; /* unknown */
+	return ifs;
+}
+
+/*****************************************************************
+ *
+ * Loose object stream
+ *
+ *****************************************************************/
+
+static read_method_decl(loose)
+{
+	size_t total_read = 0;
+
+	switch (st->z_state) {
+	case z_done:
+		return 0;
+	case z_error:
+		return -1;
+	default:
+		break;
+	}
+
+	if (st->u.loose.hdr_used < st->u.loose.hdr_avail) {
+		size_t to_copy = st->u.loose.hdr_avail - st->u.loose.hdr_used;
+		if (sz < to_copy)
+			to_copy = sz;
+		memcpy(buf, st->u.loose.hdr + st->u.loose.hdr_used, to_copy);
+		st->u.loose.hdr_used += to_copy;
+		total_read += to_copy;
+	}
+
+	while (total_read < sz) {
+		int status;
+
+		st->z.next_out = (unsigned char *)buf + total_read;
+		st->z.avail_out = sz - total_read;
+		status = git_inflate(&st->z, Z_FINISH);
+
+		total_read = st->z.next_out - (unsigned char *)buf;
+
+		if (status == Z_STREAM_END) {
+			git_inflate_end(&st->z);
+			st->z_state = z_done;
+			break;
+		}
+		if (status != Z_OK && status != Z_BUF_ERROR) {
+			git_inflate_end(&st->z);
+			st->z_state = z_error;
+			return -1;
+		}
+	}
+	return total_read;
+}
+
+static close_method_decl(loose)
+{
+	close_deflated_stream(st);
+	munmap(st->u.loose.mapped, st->u.loose.mapsize);
+	return 0;
+}
+
+static struct stream_vtbl loose_vtbl = {
+	close_istream_loose,
+	read_istream_loose,
+};
+
+static open_method_decl(loose)
+{
+	st->u.loose.mapped = map_sha1_file(sha1, &st->u.loose.mapsize);
+	if (!st->u.loose.mapped)
+		return -1;
+	if (unpack_sha1_header(&st->z,
+			       st->u.loose.mapped,
+			       st->u.loose.mapsize,
+			       st->u.loose.hdr,
+			       sizeof(st->u.loose.hdr)) < 0) {
+		git_inflate_end(&st->z);
+		munmap(st->u.loose.mapped, st->u.loose.mapsize);
+		return -1;
+	}
+
+	parse_sha1_header(st->u.loose.hdr, &st->size);
+	st->u.loose.hdr_used = strlen(st->u.loose.hdr) + 1;
+	st->u.loose.hdr_avail = st->z.total_out;
+	st->z_state = z_used;
+
+	st->vtbl = &loose_vtbl;
+	return 0;
+}
+
+
+/*****************************************************************
+ *
+ * Non-delta packed object stream
+ *
+ *****************************************************************/
+
+static read_method_decl(pack_non_delta)
+{
+	size_t total_read = 0;
+
+	switch (st->z_state) {
+	case z_unused:
+		memset(&st->z, 0, sizeof(st->z));
+		git_inflate_init(&st->z);
+		st->z_state = z_used;
+		break;
+	case z_done:
+		return 0;
+	case z_error:
+		return -1;
+	case z_used:
+		break;
+	}
+
+	while (total_read < sz) {
+		int status;
+		struct pack_window *window = NULL;
+		unsigned char *mapped;
+
+		mapped = use_pack(st->u.in_pack.pack, &window,
+				  st->u.in_pack.pos, &st->z.avail_in);
+
+		st->z.next_out = (unsigned char *)buf + total_read;
+		st->z.avail_out = sz - total_read;
+		st->z.next_in = mapped;
+		status = git_inflate(&st->z, Z_FINISH);
+
+		st->u.in_pack.pos += st->z.next_in - mapped;
+		total_read = st->z.next_out - (unsigned char *)buf;
+		unuse_pack(&window);
+
+		if (status == Z_STREAM_END) {
+			git_inflate_end(&st->z);
+			st->z_state = z_done;
+			break;
+		}
+		if (status != Z_OK && status != Z_BUF_ERROR) {
+			git_inflate_end(&st->z);
+			st->z_state = z_error;
+			return -1;
+		}
+	}
+	return total_read;
+}
+
+static close_method_decl(pack_non_delta)
+{
+	close_deflated_stream(st);
+	return 0;
+}
+
+static struct stream_vtbl pack_non_delta_vtbl = {
+	close_istream_pack_non_delta,
+	read_istream_pack_non_delta,
+};
+
+static open_method_decl(pack_non_delta)
+{
+	struct pack_window *window;
+	enum object_type in_pack_type;
+
+	st->u.in_pack.pack = oi->u.packed.pack;
+	st->u.in_pack.pos = oi->u.packed.offset;
+	window = NULL;
+
+	in_pack_type = unpack_object_header(st->u.in_pack.pack,
+					    &window,
+					    &st->u.in_pack.pos,
+					    &st->size);
+	unuse_pack(&window);
+	switch (in_pack_type) {
+	default:
+		return -1; /* we do not do deltas for now */
+	case OBJ_COMMIT:
+	case OBJ_TREE:
+	case OBJ_BLOB:
+	case OBJ_TAG:
+		break;
+	}
+	st->z_state = z_unused;
+	st->vtbl = &pack_non_delta_vtbl;
+	return 0;
+}
+
+
+/*****************************************************************
+ *
+ * In-core stream
+ *
+ *****************************************************************/
+
+static close_method_decl(incore)
+{
+	free(st->u.incore.buf);
+	return 0;
+}
+
+static read_method_decl(incore)
+{
+	size_t read_size = sz;
+	size_t remainder = st->size - st->u.incore.read_ptr;
+
+	if (remainder <= read_size)
+		read_size = remainder;
+	if (read_size) {
+		memcpy(buf, st->u.incore.buf + st->u.incore.read_ptr, read_size);
+		st->u.incore.read_ptr += read_size;
+	}
+	return read_size;
+}
+
+static struct stream_vtbl incore_vtbl = {
+	close_istream_incore,
+	read_istream_incore,
+};
+
+static open_method_decl(incore)
+{
+	st->u.incore.buf = read_sha1_file_extended(sha1, type, &st->size, 0);
+	st->u.incore.read_ptr = 0;
+	st->vtbl = &incore_vtbl;
+
+	return st->u.incore.buf ? 0 : -1;
+}
+
+
+/****************************************************************
+ * Users of streaming interface
+ ****************************************************************/
+
+int stream_blob_to_fd(int fd, unsigned const char *sha1, struct stream_filter *filter,
+		      int can_seek)
+{
+	struct git_istream *st;
+	enum object_type type;
+	unsigned long sz;
+	ssize_t kept = 0;
+	int result = -1;
+
+	st = open_istream(sha1, &type, &sz, filter);
+	if (!st)
+		return result;
+	if (type != OBJ_BLOB)
+		goto close_and_exit;
+	for (;;) {
+		char buf[1024 * 16];
+		ssize_t wrote, holeto;
+		ssize_t readlen = read_istream(st, buf, sizeof(buf));
+
+		if (!readlen)
+			break;
+		if (can_seek && sizeof(buf) == readlen) {
+			for (holeto = 0; holeto < readlen; holeto++)
+				if (buf[holeto])
+					break;
+			if (readlen == holeto) {
+				kept += holeto;
+				continue;
+			}
+		}
+
+		if (kept && lseek(fd, kept, SEEK_CUR) == (off_t) -1)
+			goto close_and_exit;
+		else
+			kept = 0;
+		wrote = write_in_full(fd, buf, readlen);
+
+		if (wrote != readlen)
+			goto close_and_exit;
+	}
+	if (kept && (lseek(fd, kept - 1, SEEK_CUR) == (off_t) -1 ||
+		     write(fd, "", 1) != 1))
+		goto close_and_exit;
+	result = 0;
+
+ close_and_exit:
+	close_istream(st);
+	return result;
+}
diff --git a/streaming.h b/streaming.h
new file mode 100644
index 0000000..1d05c2a
--- /dev/null
+++ b/streaming.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2011, Google Inc.
+ */
+#ifndef STREAMING_H
+#define STREAMING_H 1
+#include "cache.h"
+
+/* opaque */
+struct git_istream;
+
+extern struct git_istream *open_istream(const unsigned char *, enum object_type *, unsigned long *, struct stream_filter *);
+extern int close_istream(struct git_istream *);
+extern ssize_t read_istream(struct git_istream *, void *, size_t);
+
+extern int stream_blob_to_fd(int fd, const unsigned char *, struct stream_filter *, int can_seek);
+
+#endif /* STREAMING_H */
diff --git a/string-list.c b/string-list.c
index 1ac536e..d9810ab 100644
--- a/string-list.c
+++ b/string-list.c
@@ -51,13 +51,13 @@
 	return index;
 }
 
-struct string_list_item *string_list_insert(const char *string, struct string_list *list)
+struct string_list_item *string_list_insert(struct string_list *list, const char *string)
 {
-	return string_list_insert_at_index(-1, string, list);
+	return string_list_insert_at_index(list, -1, string);
 }
 
-struct string_list_item *string_list_insert_at_index(int insert_at,
-						     const char *string, struct string_list *list)
+struct string_list_item *string_list_insert_at_index(struct string_list *list,
+						     int insert_at, const char *string)
 {
 	int index = add_entry(insert_at, list, string);
 
@@ -84,7 +84,7 @@
 	return index;
 }
 
-struct string_list_item *string_list_lookup(const char *string, struct string_list *list)
+struct string_list_item *string_list_lookup(struct string_list *list, const char *string)
 {
 	int exact_match, i = get_entry_index(list, string, &exact_match);
 	if (!exact_match)
@@ -92,8 +92,8 @@
 	return list->items + i;
 }
 
-int for_each_string_list(string_list_each_func_t fn,
-			 struct string_list *list, void *cb_data)
+int for_each_string_list(struct string_list *list,
+			 string_list_each_func_t fn, void *cb_data)
 {
 	int i, ret = 0;
 	for (i = 0; i < list->nr; i++)
@@ -139,7 +139,7 @@
 }
 
 
-void print_string_list(const char *text, const struct string_list *p)
+void print_string_list(const struct string_list *p, const char *text)
 {
 	int i;
 	if ( text )
@@ -148,11 +148,12 @@
 		printf("%s:%p\n", p->items[i].string, p->items[i].util);
 }
 
-struct string_list_item *string_list_append(const char *string, struct string_list *list)
+struct string_list_item *string_list_append(struct string_list *list, const char *string)
 {
 	ALLOC_GROW(list->items, list->nr + 1, list->alloc);
 	list->items[list->nr].string =
 		list->strdup_strings ? xstrdup(string) : (char *)string;
+	list->items[list->nr].util = NULL;
 	return list->items + list->nr++;
 }
 
@@ -168,12 +169,28 @@
 	qsort(list->items, list->nr, sizeof(*list->items), cmp_items);
 }
 
-int unsorted_string_list_has_string(struct string_list *list, const char *string)
+struct string_list_item *unsorted_string_list_lookup(struct string_list *list,
+						     const char *string)
 {
 	int i;
 	for (i = 0; i < list->nr; i++)
 		if (!strcmp(string, list->items[i].string))
-			return 1;
-	return 0;
+			return list->items + i;
+	return NULL;
 }
 
+int unsorted_string_list_has_string(struct string_list *list,
+				    const char *string)
+{
+	return unsorted_string_list_lookup(list, string) != NULL;
+}
+
+void unsorted_string_list_delete_item(struct string_list *list, int i, int free_util)
+{
+	if (list->strdup_strings)
+		free(list->items[i].string);
+	if (free_util)
+		free(list->items[i].util);
+	list->items[i] = list->items[list->nr-1];
+	list->nr--;
+}
diff --git a/string-list.h b/string-list.h
index 14bbc47..0684cb7 100644
--- a/string-list.h
+++ b/string-list.h
@@ -1,18 +1,20 @@
-#ifndef PATH_LIST_H
-#define PATH_LIST_H
+#ifndef STRING_LIST_H
+#define STRING_LIST_H
 
 struct string_list_item {
 	char *string;
 	void *util;
 };
-struct string_list
-{
+struct string_list {
 	struct string_list_item *items;
 	unsigned int nr, alloc;
 	unsigned int strdup_strings:1;
 };
 
-void print_string_list(const char *text, const struct string_list *p);
+#define STRING_LIST_INIT_NODUP { NULL, 0, 0, 0 }
+#define STRING_LIST_INIT_DUP   { NULL, 0, 0, 1 }
+
+void print_string_list(const struct string_list *p, const char *text);
 void string_list_clear(struct string_list *list, int free_util);
 
 /* Use this function to call a custom clear function on each util pointer */
@@ -20,23 +22,27 @@
 typedef void (*string_list_clear_func_t)(void *p, const char *str);
 void string_list_clear_func(struct string_list *list, string_list_clear_func_t clearfunc);
 
-/* Use this function to iterate over each item */
+/* Use this function or the macro below to iterate over each item */
 typedef int (*string_list_each_func_t)(struct string_list_item *, void *);
-int for_each_string_list(string_list_each_func_t,
-			 struct string_list *list, void *cb_data);
+int for_each_string_list(struct string_list *list,
+			 string_list_each_func_t, void *cb_data);
+#define for_each_string_list_item(item,list) \
+	for (item = (list)->items; item < (list)->items + (list)->nr; ++item)
 
 /* Use these functions only on sorted lists: */
 int string_list_has_string(const struct string_list *list, const char *string);
 int string_list_find_insert_index(const struct string_list *list, const char *string,
 				  int negative_existing_index);
-struct string_list_item *string_list_insert(const char *string, struct string_list *list);
-struct string_list_item *string_list_insert_at_index(int insert_at,
-						     const char *string, struct string_list *list);
-struct string_list_item *string_list_lookup(const char *string, struct string_list *list);
+struct string_list_item *string_list_insert(struct string_list *list, const char *string);
+struct string_list_item *string_list_insert_at_index(struct string_list *list,
+						     int insert_at, const char *string);
+struct string_list_item *string_list_lookup(struct string_list *list, const char *string);
 
 /* Use these functions only on unsorted lists: */
-struct string_list_item *string_list_append(const char *string, struct string_list *list);
+struct string_list_item *string_list_append(struct string_list *list, const char *string);
 void sort_string_list(struct string_list *list);
 int unsorted_string_list_has_string(struct string_list *list, const char *string);
-
-#endif /* PATH_LIST_H */
+struct string_list_item *unsorted_string_list_lookup(struct string_list *list,
+						     const char *string);
+void unsorted_string_list_delete_item(struct string_list *list, int i, int free_util);
+#endif /* STRING_LIST_H */
diff --git a/submodule.c b/submodule.c
new file mode 100644
index 0000000..959d349
--- /dev/null
+++ b/submodule.c
@@ -0,0 +1,925 @@
+#include "cache.h"
+#include "submodule.h"
+#include "dir.h"
+#include "diff.h"
+#include "commit.h"
+#include "revision.h"
+#include "run-command.h"
+#include "diffcore.h"
+#include "refs.h"
+#include "string-list.h"
+#include "sha1-array.h"
+#include "argv-array.h"
+
+static struct string_list config_name_for_path;
+static struct string_list config_fetch_recurse_submodules_for_name;
+static struct string_list config_ignore_for_name;
+static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND;
+static struct string_list changed_submodule_paths;
+static int initialized_fetch_ref_tips;
+static struct sha1_array ref_tips_before_fetch;
+static struct sha1_array ref_tips_after_fetch;
+
+/*
+ * The following flag is set if the .gitmodules file is unmerged. We then
+ * disable recursion for all submodules where .git/config doesn't have a
+ * matching config entry because we can't guess what might be configured in
+ * .gitmodules unless the user resolves the conflict. When a command line
+ * option is given (which always overrides configuration) this flag will be
+ * ignored.
+ */
+static int gitmodules_is_unmerged;
+
+static int add_submodule_odb(const char *path)
+{
+	struct strbuf objects_directory = STRBUF_INIT;
+	struct alternate_object_database *alt_odb;
+	int ret = 0;
+	const char *git_dir;
+
+	strbuf_addf(&objects_directory, "%s/.git", path);
+	git_dir = read_gitfile(objects_directory.buf);
+	if (git_dir) {
+		strbuf_reset(&objects_directory);
+		strbuf_addstr(&objects_directory, git_dir);
+	}
+	strbuf_addstr(&objects_directory, "/objects/");
+	if (!is_directory(objects_directory.buf)) {
+		ret = -1;
+		goto done;
+	}
+	/* avoid adding it twice */
+	for (alt_odb = alt_odb_list; alt_odb; alt_odb = alt_odb->next)
+		if (alt_odb->name - alt_odb->base == objects_directory.len &&
+				!strncmp(alt_odb->base, objects_directory.buf,
+					objects_directory.len))
+			goto done;
+
+	alt_odb = xmalloc(objects_directory.len + 42 + sizeof(*alt_odb));
+	alt_odb->next = alt_odb_list;
+	strcpy(alt_odb->base, objects_directory.buf);
+	alt_odb->name = alt_odb->base + objects_directory.len;
+	alt_odb->name[2] = '/';
+	alt_odb->name[40] = '\0';
+	alt_odb->name[41] = '\0';
+	alt_odb_list = alt_odb;
+
+	/* add possible alternates from the submodule */
+	read_info_alternates(objects_directory.buf, 0);
+	prepare_alt_odb();
+done:
+	strbuf_release(&objects_directory);
+	return ret;
+}
+
+void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
+					     const char *path)
+{
+	struct string_list_item *path_option, *ignore_option;
+	path_option = unsorted_string_list_lookup(&config_name_for_path, path);
+	if (path_option) {
+		ignore_option = unsorted_string_list_lookup(&config_ignore_for_name, path_option->util);
+		if (ignore_option)
+			handle_ignore_submodules_arg(diffopt, ignore_option->util);
+		else if (gitmodules_is_unmerged)
+			DIFF_OPT_SET(diffopt, IGNORE_SUBMODULES);
+	}
+}
+
+int submodule_config(const char *var, const char *value, void *cb)
+{
+	if (!prefixcmp(var, "submodule."))
+		return parse_submodule_config_option(var, value);
+	else if (!strcmp(var, "fetch.recursesubmodules")) {
+		config_fetch_recurse_submodules = parse_fetch_recurse_submodules_arg(var, value);
+		return 0;
+	}
+	return 0;
+}
+
+void gitmodules_config(void)
+{
+	const char *work_tree = get_git_work_tree();
+	if (work_tree) {
+		struct strbuf gitmodules_path = STRBUF_INIT;
+		int pos;
+		strbuf_addstr(&gitmodules_path, work_tree);
+		strbuf_addstr(&gitmodules_path, "/.gitmodules");
+		if (read_cache() < 0)
+			die("index file corrupt");
+		pos = cache_name_pos(".gitmodules", 11);
+		if (pos < 0) { /* .gitmodules not found or isn't merged */
+			pos = -1 - pos;
+			if (active_nr > pos) {  /* there is a .gitmodules */
+				const struct cache_entry *ce = active_cache[pos];
+				if (ce_namelen(ce) == 11 &&
+				    !memcmp(ce->name, ".gitmodules", 11))
+					gitmodules_is_unmerged = 1;
+			}
+		}
+
+		if (!gitmodules_is_unmerged)
+			git_config_from_file(submodule_config, gitmodules_path.buf, NULL);
+		strbuf_release(&gitmodules_path);
+	}
+}
+
+int parse_submodule_config_option(const char *var, const char *value)
+{
+	int len;
+	struct string_list_item *config;
+	struct strbuf submodname = STRBUF_INIT;
+
+	var += 10;		/* Skip "submodule." */
+
+	len = strlen(var);
+	if ((len > 5) && !strcmp(var + len - 5, ".path")) {
+		strbuf_add(&submodname, var, len - 5);
+		config = unsorted_string_list_lookup(&config_name_for_path, value);
+		if (config)
+			free(config->util);
+		else
+			config = string_list_append(&config_name_for_path, xstrdup(value));
+		config->util = strbuf_detach(&submodname, NULL);
+		strbuf_release(&submodname);
+	} else if ((len > 23) && !strcmp(var + len - 23, ".fetchrecursesubmodules")) {
+		strbuf_add(&submodname, var, len - 23);
+		config = unsorted_string_list_lookup(&config_fetch_recurse_submodules_for_name, submodname.buf);
+		if (!config)
+			config = string_list_append(&config_fetch_recurse_submodules_for_name,
+						    strbuf_detach(&submodname, NULL));
+		config->util = (void *)(intptr_t)parse_fetch_recurse_submodules_arg(var, value);
+		strbuf_release(&submodname);
+	} else if ((len > 7) && !strcmp(var + len - 7, ".ignore")) {
+		if (strcmp(value, "untracked") && strcmp(value, "dirty") &&
+		    strcmp(value, "all") && strcmp(value, "none")) {
+			warning("Invalid parameter \"%s\" for config option \"submodule.%s.ignore\"", value, var);
+			return 0;
+		}
+
+		strbuf_add(&submodname, var, len - 7);
+		config = unsorted_string_list_lookup(&config_ignore_for_name, submodname.buf);
+		if (config)
+			free(config->util);
+		else
+			config = string_list_append(&config_ignore_for_name,
+						    strbuf_detach(&submodname, NULL));
+		strbuf_release(&submodname);
+		config->util = xstrdup(value);
+		return 0;
+	}
+	return 0;
+}
+
+void handle_ignore_submodules_arg(struct diff_options *diffopt,
+				  const char *arg)
+{
+	DIFF_OPT_CLR(diffopt, IGNORE_SUBMODULES);
+	DIFF_OPT_CLR(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES);
+	DIFF_OPT_CLR(diffopt, IGNORE_DIRTY_SUBMODULES);
+
+	if (!strcmp(arg, "all"))
+		DIFF_OPT_SET(diffopt, IGNORE_SUBMODULES);
+	else if (!strcmp(arg, "untracked"))
+		DIFF_OPT_SET(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES);
+	else if (!strcmp(arg, "dirty"))
+		DIFF_OPT_SET(diffopt, IGNORE_DIRTY_SUBMODULES);
+	else if (strcmp(arg, "none"))
+		die("bad --ignore-submodules argument: %s", arg);
+}
+
+static int prepare_submodule_summary(struct rev_info *rev, const char *path,
+		struct commit *left, struct commit *right,
+		int *fast_forward, int *fast_backward)
+{
+	struct commit_list *merge_bases, *list;
+
+	init_revisions(rev, NULL);
+	setup_revisions(0, NULL, rev, NULL);
+	rev->left_right = 1;
+	rev->first_parent_only = 1;
+	left->object.flags |= SYMMETRIC_LEFT;
+	add_pending_object(rev, &left->object, path);
+	add_pending_object(rev, &right->object, path);
+	merge_bases = get_merge_bases(left, right, 1);
+	if (merge_bases) {
+		if (merge_bases->item == left)
+			*fast_forward = 1;
+		else if (merge_bases->item == right)
+			*fast_backward = 1;
+	}
+	for (list = merge_bases; list; list = list->next) {
+		list->item->object.flags |= UNINTERESTING;
+		add_pending_object(rev, &list->item->object,
+			sha1_to_hex(list->item->object.sha1));
+	}
+	return prepare_revision_walk(rev);
+}
+
+static void print_submodule_summary(struct rev_info *rev, FILE *f,
+		const char *del, const char *add, const char *reset)
+{
+	static const char format[] = "  %m %s";
+	struct strbuf sb = STRBUF_INIT;
+	struct commit *commit;
+
+	while ((commit = get_revision(rev))) {
+		struct pretty_print_context ctx = {0};
+		ctx.date_mode = rev->date_mode;
+		strbuf_setlen(&sb, 0);
+		if (commit->object.flags & SYMMETRIC_LEFT) {
+			if (del)
+				strbuf_addstr(&sb, del);
+		}
+		else if (add)
+			strbuf_addstr(&sb, add);
+		format_commit_message(commit, format, &sb, &ctx);
+		if (reset)
+			strbuf_addstr(&sb, reset);
+		strbuf_addch(&sb, '\n');
+		fprintf(f, "%s", sb.buf);
+	}
+	strbuf_release(&sb);
+}
+
+int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg)
+{
+	switch (git_config_maybe_bool(opt, arg)) {
+	case 1:
+		return RECURSE_SUBMODULES_ON;
+	case 0:
+		return RECURSE_SUBMODULES_OFF;
+	default:
+		if (!strcmp(arg, "on-demand"))
+			return RECURSE_SUBMODULES_ON_DEMAND;
+		die("bad %s argument: %s", opt, arg);
+	}
+}
+
+void show_submodule_summary(FILE *f, const char *path,
+		unsigned char one[20], unsigned char two[20],
+		unsigned dirty_submodule,
+		const char *del, const char *add, const char *reset)
+{
+	struct rev_info rev;
+	struct commit *left = left, *right = right;
+	const char *message = NULL;
+	struct strbuf sb = STRBUF_INIT;
+	int fast_forward = 0, fast_backward = 0;
+
+	if (is_null_sha1(two))
+		message = "(submodule deleted)";
+	else if (add_submodule_odb(path))
+		message = "(not checked out)";
+	else if (is_null_sha1(one))
+		message = "(new submodule)";
+	else if (!(left = lookup_commit_reference(one)) ||
+		 !(right = lookup_commit_reference(two)))
+		message = "(commits not present)";
+
+	if (!message &&
+	    prepare_submodule_summary(&rev, path, left, right,
+					&fast_forward, &fast_backward))
+		message = "(revision walker failed)";
+
+	if (dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)
+		fprintf(f, "Submodule %s contains untracked content\n", path);
+	if (dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
+		fprintf(f, "Submodule %s contains modified content\n", path);
+
+	if (!hashcmp(one, two)) {
+		strbuf_release(&sb);
+		return;
+	}
+
+	strbuf_addf(&sb, "Submodule %s %s..", path,
+			find_unique_abbrev(one, DEFAULT_ABBREV));
+	if (!fast_backward && !fast_forward)
+		strbuf_addch(&sb, '.');
+	strbuf_addf(&sb, "%s", find_unique_abbrev(two, DEFAULT_ABBREV));
+	if (message)
+		strbuf_addf(&sb, " %s\n", message);
+	else
+		strbuf_addf(&sb, "%s:\n", fast_backward ? " (rewind)" : "");
+	fwrite(sb.buf, sb.len, 1, f);
+
+	if (!message) {
+		print_submodule_summary(&rev, f, del, add, reset);
+		clear_commit_marks(left, ~0);
+		clear_commit_marks(right, ~0);
+	}
+
+	strbuf_release(&sb);
+}
+
+void set_config_fetch_recurse_submodules(int value)
+{
+	config_fetch_recurse_submodules = value;
+}
+
+static int has_remote(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
+{
+	return 1;
+}
+
+static int submodule_needs_pushing(const char *path, const unsigned char sha1[20])
+{
+	if (add_submodule_odb(path) || !lookup_commit_reference(sha1))
+		return 0;
+
+	if (for_each_remote_ref_submodule(path, has_remote, NULL) > 0) {
+		struct child_process cp;
+		const char *argv[] = {"rev-list", NULL, "--not", "--remotes", "-n", "1" , NULL};
+		struct strbuf buf = STRBUF_INIT;
+		int needs_pushing = 0;
+
+		argv[1] = sha1_to_hex(sha1);
+		memset(&cp, 0, sizeof(cp));
+		cp.argv = argv;
+		cp.env = local_repo_env;
+		cp.git_cmd = 1;
+		cp.no_stdin = 1;
+		cp.out = -1;
+		cp.dir = path;
+		if (start_command(&cp))
+			die("Could not run 'git rev-list %s --not --remotes -n 1' command in submodule %s",
+				sha1_to_hex(sha1), path);
+		if (strbuf_read(&buf, cp.out, 41))
+			needs_pushing = 1;
+		finish_command(&cp);
+		close(cp.out);
+		strbuf_release(&buf);
+		return needs_pushing;
+	}
+
+	return 0;
+}
+
+static void collect_submodules_from_diff(struct diff_queue_struct *q,
+					 struct diff_options *options,
+					 void *data)
+{
+	int i;
+	struct string_list *needs_pushing = data;
+
+	for (i = 0; i < q->nr; i++) {
+		struct diff_filepair *p = q->queue[i];
+		if (!S_ISGITLINK(p->two->mode))
+			continue;
+		if (submodule_needs_pushing(p->two->path, p->two->sha1))
+			string_list_insert(needs_pushing, p->two->path);
+	}
+}
+
+static void find_unpushed_submodule_commits(struct commit *commit,
+		struct string_list *needs_pushing)
+{
+	struct rev_info rev;
+
+	init_revisions(&rev, NULL);
+	rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
+	rev.diffopt.format_callback = collect_submodules_from_diff;
+	rev.diffopt.format_callback_data = needs_pushing;
+	diff_tree_combined_merge(commit, 1, &rev);
+}
+
+int find_unpushed_submodules(unsigned char new_sha1[20],
+		const char *remotes_name, struct string_list *needs_pushing)
+{
+	struct rev_info rev;
+	struct commit *commit;
+	const char *argv[] = {NULL, NULL, "--not", "NULL", NULL};
+	int argc = ARRAY_SIZE(argv) - 1;
+	char *sha1_copy;
+
+	struct strbuf remotes_arg = STRBUF_INIT;
+
+	strbuf_addf(&remotes_arg, "--remotes=%s", remotes_name);
+	init_revisions(&rev, NULL);
+	sha1_copy = xstrdup(sha1_to_hex(new_sha1));
+	argv[1] = sha1_copy;
+	argv[3] = remotes_arg.buf;
+	setup_revisions(argc, argv, &rev, NULL);
+	if (prepare_revision_walk(&rev))
+		die("revision walk setup failed");
+
+	while ((commit = get_revision(&rev)) != NULL)
+		find_unpushed_submodule_commits(commit, needs_pushing);
+
+	reset_revision_walk();
+	free(sha1_copy);
+	strbuf_release(&remotes_arg);
+
+	return needs_pushing->nr;
+}
+
+static int push_submodule(const char *path)
+{
+	if (add_submodule_odb(path))
+		return 1;
+
+	if (for_each_remote_ref_submodule(path, has_remote, NULL) > 0) {
+		struct child_process cp;
+		const char *argv[] = {"push", NULL};
+
+		memset(&cp, 0, sizeof(cp));
+		cp.argv = argv;
+		cp.env = local_repo_env;
+		cp.git_cmd = 1;
+		cp.no_stdin = 1;
+		cp.dir = path;
+		if (run_command(&cp))
+			return 0;
+		close(cp.out);
+	}
+
+	return 1;
+}
+
+int push_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name)
+{
+	int i, ret = 1;
+	struct string_list needs_pushing;
+
+	memset(&needs_pushing, 0, sizeof(struct string_list));
+	needs_pushing.strdup_strings = 1;
+
+	if (!find_unpushed_submodules(new_sha1, remotes_name, &needs_pushing))
+		return 1;
+
+	for (i = 0; i < needs_pushing.nr; i++) {
+		const char *path = needs_pushing.items[i].string;
+		fprintf(stderr, "Pushing submodule '%s'\n", path);
+		if (!push_submodule(path)) {
+			fprintf(stderr, "Unable to push submodule '%s'\n", path);
+			ret = 0;
+		}
+	}
+
+	string_list_clear(&needs_pushing, 0);
+
+	return ret;
+}
+
+static int is_submodule_commit_present(const char *path, unsigned char sha1[20])
+{
+	int is_present = 0;
+	if (!add_submodule_odb(path) && lookup_commit_reference(sha1)) {
+		/* Even if the submodule is checked out and the commit is
+		 * present, make sure it is reachable from a ref. */
+		struct child_process cp;
+		const char *argv[] = {"rev-list", "-n", "1", NULL, "--not", "--all", NULL};
+		struct strbuf buf = STRBUF_INIT;
+
+		argv[3] = sha1_to_hex(sha1);
+		memset(&cp, 0, sizeof(cp));
+		cp.argv = argv;
+		cp.env = local_repo_env;
+		cp.git_cmd = 1;
+		cp.no_stdin = 1;
+		cp.out = -1;
+		cp.dir = path;
+		if (!run_command(&cp) && !strbuf_read(&buf, cp.out, 1024))
+			is_present = 1;
+
+		close(cp.out);
+		strbuf_release(&buf);
+	}
+	return is_present;
+}
+
+static void submodule_collect_changed_cb(struct diff_queue_struct *q,
+					 struct diff_options *options,
+					 void *data)
+{
+	int i;
+	for (i = 0; i < q->nr; i++) {
+		struct diff_filepair *p = q->queue[i];
+		if (!S_ISGITLINK(p->two->mode))
+			continue;
+
+		if (S_ISGITLINK(p->one->mode)) {
+			/* NEEDSWORK: We should honor the name configured in
+			 * the .gitmodules file of the commit we are examining
+			 * here to be able to correctly follow submodules
+			 * being moved around. */
+			struct string_list_item *path;
+			path = unsorted_string_list_lookup(&changed_submodule_paths, p->two->path);
+			if (!path && !is_submodule_commit_present(p->two->path, p->two->sha1))
+				string_list_append(&changed_submodule_paths, xstrdup(p->two->path));
+		} else {
+			/* Submodule is new or was moved here */
+			/* NEEDSWORK: When the .git directories of submodules
+			 * live inside the superprojects .git directory some
+			 * day we should fetch new submodules directly into
+			 * that location too when config or options request
+			 * that so they can be checked out from there. */
+			continue;
+		}
+	}
+}
+
+static int add_sha1_to_array(const char *ref, const unsigned char *sha1,
+			     int flags, void *data)
+{
+	sha1_array_append(data, sha1);
+	return 0;
+}
+
+void check_for_new_submodule_commits(unsigned char new_sha1[20])
+{
+	if (!initialized_fetch_ref_tips) {
+		for_each_ref(add_sha1_to_array, &ref_tips_before_fetch);
+		initialized_fetch_ref_tips = 1;
+	}
+
+	sha1_array_append(&ref_tips_after_fetch, new_sha1);
+}
+
+static void add_sha1_to_argv(const unsigned char sha1[20], void *data)
+{
+	argv_array_push(data, sha1_to_hex(sha1));
+}
+
+static void calculate_changed_submodule_paths(void)
+{
+	struct rev_info rev;
+	struct commit *commit;
+	struct argv_array argv = ARGV_ARRAY_INIT;
+
+	/* No need to check if there are no submodules configured */
+	if (!config_name_for_path.nr)
+		return;
+
+	init_revisions(&rev, NULL);
+	argv_array_push(&argv, "--"); /* argv[0] program name */
+	sha1_array_for_each_unique(&ref_tips_after_fetch,
+				   add_sha1_to_argv, &argv);
+	argv_array_push(&argv, "--not");
+	sha1_array_for_each_unique(&ref_tips_before_fetch,
+				   add_sha1_to_argv, &argv);
+	setup_revisions(argv.argc, argv.argv, &rev, NULL);
+	if (prepare_revision_walk(&rev))
+		die("revision walk setup failed");
+
+	/*
+	 * Collect all submodules (whether checked out or not) for which new
+	 * commits have been recorded upstream in "changed_submodule_paths".
+	 */
+	while ((commit = get_revision(&rev))) {
+		struct commit_list *parent = commit->parents;
+		while (parent) {
+			struct diff_options diff_opts;
+			diff_setup(&diff_opts);
+			DIFF_OPT_SET(&diff_opts, RECURSIVE);
+			diff_opts.output_format |= DIFF_FORMAT_CALLBACK;
+			diff_opts.format_callback = submodule_collect_changed_cb;
+			if (diff_setup_done(&diff_opts) < 0)
+				die("diff_setup_done failed");
+			diff_tree_sha1(parent->item->object.sha1, commit->object.sha1, "", &diff_opts);
+			diffcore_std(&diff_opts);
+			diff_flush(&diff_opts);
+			parent = parent->next;
+		}
+	}
+
+	argv_array_clear(&argv);
+	sha1_array_clear(&ref_tips_before_fetch);
+	sha1_array_clear(&ref_tips_after_fetch);
+	initialized_fetch_ref_tips = 0;
+}
+
+int fetch_populated_submodules(int num_options, const char **options,
+			       const char *prefix, int command_line_option,
+			       int quiet)
+{
+	int i, result = 0, argc = 0, default_argc;
+	struct child_process cp;
+	const char **argv;
+	struct string_list_item *name_for_path;
+	const char *work_tree = get_git_work_tree();
+	if (!work_tree)
+		goto out;
+
+	if (!the_index.initialized)
+		if (read_cache() < 0)
+			die("index file corrupt");
+
+	/* 6: "fetch" (options) --recurse-submodules-default default "--submodule-prefix" prefix NULL */
+	argv = xcalloc(num_options + 6, sizeof(const char *));
+	argv[argc++] = "fetch";
+	for (i = 0; i < num_options; i++)
+		argv[argc++] = options[i];
+	argv[argc++] = "--recurse-submodules-default";
+	default_argc = argc++;
+	argv[argc++] = "--submodule-prefix";
+
+	memset(&cp, 0, sizeof(cp));
+	cp.argv = argv;
+	cp.env = local_repo_env;
+	cp.git_cmd = 1;
+	cp.no_stdin = 1;
+
+	calculate_changed_submodule_paths();
+
+	for (i = 0; i < active_nr; i++) {
+		struct strbuf submodule_path = STRBUF_INIT;
+		struct strbuf submodule_git_dir = STRBUF_INIT;
+		struct strbuf submodule_prefix = STRBUF_INIT;
+		struct cache_entry *ce = active_cache[i];
+		const char *git_dir, *name, *default_argv;
+
+		if (!S_ISGITLINK(ce->ce_mode))
+			continue;
+
+		name = ce->name;
+		name_for_path = unsorted_string_list_lookup(&config_name_for_path, ce->name);
+		if (name_for_path)
+			name = name_for_path->util;
+
+		default_argv = "yes";
+		if (command_line_option == RECURSE_SUBMODULES_DEFAULT) {
+			struct string_list_item *fetch_recurse_submodules_option;
+			fetch_recurse_submodules_option = unsorted_string_list_lookup(&config_fetch_recurse_submodules_for_name, name);
+			if (fetch_recurse_submodules_option) {
+				if ((intptr_t)fetch_recurse_submodules_option->util == RECURSE_SUBMODULES_OFF)
+					continue;
+				if ((intptr_t)fetch_recurse_submodules_option->util == RECURSE_SUBMODULES_ON_DEMAND) {
+					if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name))
+						continue;
+					default_argv = "on-demand";
+				}
+			} else {
+				if ((config_fetch_recurse_submodules == RECURSE_SUBMODULES_OFF) ||
+				    gitmodules_is_unmerged)
+					continue;
+				if (config_fetch_recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND) {
+					if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name))
+						continue;
+					default_argv = "on-demand";
+				}
+			}
+		} else if (command_line_option == RECURSE_SUBMODULES_ON_DEMAND) {
+			if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name))
+				continue;
+			default_argv = "on-demand";
+		}
+
+		strbuf_addf(&submodule_path, "%s/%s", work_tree, ce->name);
+		strbuf_addf(&submodule_git_dir, "%s/.git", submodule_path.buf);
+		strbuf_addf(&submodule_prefix, "%s%s/", prefix, ce->name);
+		git_dir = read_gitfile(submodule_git_dir.buf);
+		if (!git_dir)
+			git_dir = submodule_git_dir.buf;
+		if (is_directory(git_dir)) {
+			if (!quiet)
+				printf("Fetching submodule %s%s\n", prefix, ce->name);
+			cp.dir = submodule_path.buf;
+			argv[default_argc] = default_argv;
+			argv[argc] = submodule_prefix.buf;
+			if (run_command(&cp))
+				result = 1;
+		}
+		strbuf_release(&submodule_path);
+		strbuf_release(&submodule_git_dir);
+		strbuf_release(&submodule_prefix);
+	}
+	free(argv);
+out:
+	string_list_clear(&changed_submodule_paths, 1);
+	return result;
+}
+
+unsigned is_submodule_modified(const char *path, int ignore_untracked)
+{
+	ssize_t len;
+	struct child_process cp;
+	const char *argv[] = {
+		"status",
+		"--porcelain",
+		NULL,
+		NULL,
+	};
+	struct strbuf buf = STRBUF_INIT;
+	unsigned dirty_submodule = 0;
+	const char *line, *next_line;
+	const char *git_dir;
+
+	strbuf_addf(&buf, "%s/.git", path);
+	git_dir = read_gitfile(buf.buf);
+	if (!git_dir)
+		git_dir = buf.buf;
+	if (!is_directory(git_dir)) {
+		strbuf_release(&buf);
+		/* The submodule is not checked out, so it is not modified */
+		return 0;
+
+	}
+	strbuf_reset(&buf);
+
+	if (ignore_untracked)
+		argv[2] = "-uno";
+
+	memset(&cp, 0, sizeof(cp));
+	cp.argv = argv;
+	cp.env = local_repo_env;
+	cp.git_cmd = 1;
+	cp.no_stdin = 1;
+	cp.out = -1;
+	cp.dir = path;
+	if (start_command(&cp))
+		die("Could not run 'git status --porcelain' in submodule %s", path);
+
+	len = strbuf_read(&buf, cp.out, 1024);
+	line = buf.buf;
+	while (len > 2) {
+		if ((line[0] == '?') && (line[1] == '?')) {
+			dirty_submodule |= DIRTY_SUBMODULE_UNTRACKED;
+			if (dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
+				break;
+		} else {
+			dirty_submodule |= DIRTY_SUBMODULE_MODIFIED;
+			if (ignore_untracked ||
+			    (dirty_submodule & DIRTY_SUBMODULE_UNTRACKED))
+				break;
+		}
+		next_line = strchr(line, '\n');
+		if (!next_line)
+			break;
+		next_line++;
+		len -= (next_line - line);
+		line = next_line;
+	}
+	close(cp.out);
+
+	if (finish_command(&cp))
+		die("'git status --porcelain' failed in submodule %s", path);
+
+	strbuf_release(&buf);
+	return dirty_submodule;
+}
+
+static int find_first_merges(struct object_array *result, const char *path,
+		struct commit *a, struct commit *b)
+{
+	int i, j;
+	struct object_array merges;
+	struct commit *commit;
+	int contains_another;
+
+	char merged_revision[42];
+	const char *rev_args[] = { "rev-list", "--merges", "--ancestry-path",
+				   "--all", merged_revision, NULL };
+	struct rev_info revs;
+	struct setup_revision_opt rev_opts;
+
+	memset(&merges, 0, sizeof(merges));
+	memset(result, 0, sizeof(struct object_array));
+	memset(&rev_opts, 0, sizeof(rev_opts));
+
+	/* get all revisions that merge commit a */
+	snprintf(merged_revision, sizeof(merged_revision), "^%s",
+			sha1_to_hex(a->object.sha1));
+	init_revisions(&revs, NULL);
+	rev_opts.submodule = path;
+	setup_revisions(sizeof(rev_args)/sizeof(char *)-1, rev_args, &revs, &rev_opts);
+
+	/* save all revisions from the above list that contain b */
+	if (prepare_revision_walk(&revs))
+		die("revision walk setup failed");
+	while ((commit = get_revision(&revs)) != NULL) {
+		struct object *o = &(commit->object);
+		if (in_merge_bases(b, &commit, 1))
+			add_object_array(o, NULL, &merges);
+	}
+	reset_revision_walk();
+
+	/* Now we've got all merges that contain a and b. Prune all
+	 * merges that contain another found merge and save them in
+	 * result.
+	 */
+	for (i = 0; i < merges.nr; i++) {
+		struct commit *m1 = (struct commit *) merges.objects[i].item;
+
+		contains_another = 0;
+		for (j = 0; j < merges.nr; j++) {
+			struct commit *m2 = (struct commit *) merges.objects[j].item;
+			if (i != j && in_merge_bases(m2, &m1, 1)) {
+				contains_another = 1;
+				break;
+			}
+		}
+
+		if (!contains_another)
+			add_object_array(merges.objects[i].item,
+					 merges.objects[i].name, result);
+	}
+
+	free(merges.objects);
+	return result->nr;
+}
+
+static void print_commit(struct commit *commit)
+{
+	struct strbuf sb = STRBUF_INIT;
+	struct pretty_print_context ctx = {0};
+	ctx.date_mode = DATE_NORMAL;
+	format_commit_message(commit, " %h: %m %s", &sb, &ctx);
+	fprintf(stderr, "%s\n", sb.buf);
+	strbuf_release(&sb);
+}
+
+#define MERGE_WARNING(path, msg) \
+	warning("Failed to merge submodule %s (%s)", path, msg);
+
+int merge_submodule(unsigned char result[20], const char *path,
+		    const unsigned char base[20], const unsigned char a[20],
+		    const unsigned char b[20], int search)
+{
+	struct commit *commit_base, *commit_a, *commit_b;
+	int parent_count;
+	struct object_array merges;
+
+	int i;
+
+	/* store a in result in case we fail */
+	hashcpy(result, a);
+
+	/* we can not handle deletion conflicts */
+	if (is_null_sha1(base))
+		return 0;
+	if (is_null_sha1(a))
+		return 0;
+	if (is_null_sha1(b))
+		return 0;
+
+	if (add_submodule_odb(path)) {
+		MERGE_WARNING(path, "not checked out");
+		return 0;
+	}
+
+	if (!(commit_base = lookup_commit_reference(base)) ||
+	    !(commit_a = lookup_commit_reference(a)) ||
+	    !(commit_b = lookup_commit_reference(b))) {
+		MERGE_WARNING(path, "commits not present");
+		return 0;
+	}
+
+	/* check whether both changes are forward */
+	if (!in_merge_bases(commit_base, &commit_a, 1) ||
+	    !in_merge_bases(commit_base, &commit_b, 1)) {
+		MERGE_WARNING(path, "commits don't follow merge-base");
+		return 0;
+	}
+
+	/* Case #1: a is contained in b or vice versa */
+	if (in_merge_bases(commit_a, &commit_b, 1)) {
+		hashcpy(result, b);
+		return 1;
+	}
+	if (in_merge_bases(commit_b, &commit_a, 1)) {
+		hashcpy(result, a);
+		return 1;
+	}
+
+	/*
+	 * Case #2: There are one or more merges that contain a and b in
+	 * the submodule. If there is only one, then present it as a
+	 * suggestion to the user, but leave it marked unmerged so the
+	 * user needs to confirm the resolution.
+	 */
+
+	/* Skip the search if makes no sense to the calling context.  */
+	if (!search)
+		return 0;
+
+	/* find commit which merges them */
+	parent_count = find_first_merges(&merges, path, commit_a, commit_b);
+	switch (parent_count) {
+	case 0:
+		MERGE_WARNING(path, "merge following commits not found");
+		break;
+
+	case 1:
+		MERGE_WARNING(path, "not fast-forward");
+		fprintf(stderr, "Found a possible merge resolution "
+				"for the submodule:\n");
+		print_commit((struct commit *) merges.objects[0].item);
+		fprintf(stderr,
+			"If this is correct simply add it to the index "
+			"for example\n"
+			"by using:\n\n"
+			"  git update-index --cacheinfo 160000 %s \"%s\"\n\n"
+			"which will accept this suggestion.\n",
+			sha1_to_hex(merges.objects[0].item->sha1), path);
+		break;
+
+	default:
+		MERGE_WARNING(path, "multiple merges found");
+		for (i = 0; i < merges.nr; i++)
+			print_commit((struct commit *) merges.objects[i].item);
+	}
+
+	free(merges.objects);
+	return 0;
+}
diff --git a/submodule.h b/submodule.h
new file mode 100644
index 0000000..e105b0e
--- /dev/null
+++ b/submodule.h
@@ -0,0 +1,36 @@
+#ifndef SUBMODULE_H
+#define SUBMODULE_H
+
+struct diff_options;
+
+enum {
+	RECURSE_SUBMODULES_ON_DEMAND = -1,
+	RECURSE_SUBMODULES_OFF = 0,
+	RECURSE_SUBMODULES_DEFAULT = 1,
+	RECURSE_SUBMODULES_ON = 2
+};
+
+void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
+		const char *path);
+int submodule_config(const char *var, const char *value, void *cb);
+void gitmodules_config(void);
+int parse_submodule_config_option(const char *var, const char *value);
+void handle_ignore_submodules_arg(struct diff_options *diffopt, const char *);
+int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg);
+void show_submodule_summary(FILE *f, const char *path,
+		unsigned char one[20], unsigned char two[20],
+		unsigned dirty_submodule,
+		const char *del, const char *add, const char *reset);
+void set_config_fetch_recurse_submodules(int value);
+void check_for_new_submodule_commits(unsigned char new_sha1[20]);
+int fetch_populated_submodules(int num_options, const char **options,
+			       const char *prefix, int command_line_option,
+			       int quiet);
+unsigned is_submodule_modified(const char *path, int ignore_untracked);
+int merge_submodule(unsigned char result[20], const char *path, const unsigned char base[20],
+		    const unsigned char a[20], const unsigned char b[20], int search);
+int find_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name,
+		struct string_list *needs_pushing);
+int push_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name);
+
+#endif
diff --git a/symlinks.c b/symlinks.c
index 4bdded3..2900367 100644
--- a/symlinks.c
+++ b/symlinks.c
@@ -64,11 +64,13 @@
  * of the prefix, where the cache should use the stat() function
  * instead of the lstat() function to test each path component.
  */
-static int lstat_cache(struct cache_def *cache, const char *name, int len,
-		       int track_flags, int prefix_len_stat_func)
+static int lstat_cache_matchlen(struct cache_def *cache,
+				const char *name, int len,
+				int *ret_flags, int track_flags,
+				int prefix_len_stat_func)
 {
 	int match_len, last_slash, last_slash_dir, previous_slash;
-	int match_flags, ret_flags, save_flags, max_len, ret;
+	int save_flags, max_len, ret;
 	struct stat st;
 
 	if (cache->track_flags != track_flags ||
@@ -90,9 +92,13 @@
 		match_len = last_slash =
 			longest_path_match(name, len, cache->path, cache->len,
 					   &previous_slash);
-		match_flags = cache->flags & track_flags & (FL_NOENT|FL_SYMLINK);
-		if (match_flags && match_len == cache->len)
-			return match_flags;
+		*ret_flags = cache->flags & track_flags & (FL_NOENT|FL_SYMLINK);
+
+		if (!(track_flags & FL_FULLPATH) && match_len == len)
+			match_len = last_slash = previous_slash;
+
+		if (*ret_flags && match_len == cache->len)
+			return match_len;
 		/*
 		 * If we now have match_len > 0, we would know that
 		 * the matched part will always be a directory.
@@ -101,16 +107,16 @@
 		 * a substring of the cache on a path component basis,
 		 * we can return immediately.
 		 */
-		match_flags = track_flags & FL_DIR;
-		if (match_flags && len == match_len)
-			return match_flags;
+		*ret_flags = track_flags & FL_DIR;
+		if (*ret_flags && len == match_len)
+			return match_len;
 	}
 
 	/*
 	 * Okay, no match from the cache so far, so now we have to
 	 * check the rest of the path components.
 	 */
-	ret_flags = FL_DIR;
+	*ret_flags = FL_DIR;
 	last_slash_dir = last_slash;
 	max_len = len < PATH_MAX ? len : PATH_MAX;
 	while (match_len < max_len) {
@@ -129,16 +135,16 @@
 			ret = lstat(cache->path, &st);
 
 		if (ret) {
-			ret_flags = FL_LSTATERR;
+			*ret_flags = FL_LSTATERR;
 			if (errno == ENOENT)
-				ret_flags |= FL_NOENT;
+				*ret_flags |= FL_NOENT;
 		} else if (S_ISDIR(st.st_mode)) {
 			last_slash_dir = last_slash;
 			continue;
 		} else if (S_ISLNK(st.st_mode)) {
-			ret_flags = FL_SYMLINK;
+			*ret_flags = FL_SYMLINK;
 		} else {
-			ret_flags = FL_ERR;
+			*ret_flags = FL_ERR;
 		}
 		break;
 	}
@@ -148,7 +154,7 @@
 	 * path types, FL_NOENT, FL_SYMLINK and FL_DIR, can be cached
 	 * for the moment!
 	 */
-	save_flags = ret_flags & track_flags & (FL_NOENT|FL_SYMLINK);
+	save_flags = *ret_flags & track_flags & (FL_NOENT|FL_SYMLINK);
 	if (save_flags && last_slash > 0 && last_slash <= PATH_MAX) {
 		cache->path[last_slash] = '\0';
 		cache->len = last_slash;
@@ -172,38 +178,16 @@
 	} else {
 		reset_lstat_cache(cache);
 	}
-	return ret_flags;
+	return match_len;
 }
 
-/*
- * Invalidate the given 'name' from the cache, if 'name' matches
- * completely with the cache.
- */
-void invalidate_lstat_cache(const char *name, int len)
+static int lstat_cache(struct cache_def *cache, const char *name, int len,
+		       int track_flags, int prefix_len_stat_func)
 {
-	int match_len, previous_slash;
-	struct cache_def *cache = &default_cache;	/* FIXME */
-
-	match_len = longest_path_match(name, len, cache->path, cache->len,
-				       &previous_slash);
-	if (len == match_len) {
-		if ((cache->track_flags & FL_DIR) && previous_slash > 0) {
-			cache->path[previous_slash] = '\0';
-			cache->len = previous_slash;
-			cache->flags = FL_DIR;
-		} else {
-			reset_lstat_cache(cache);
-		}
-	}
-}
-
-/*
- * Completely clear the contents of the cache
- */
-void clear_lstat_cache(void)
-{
-	struct cache_def *cache = &default_cache;	/* FIXME */
-	reset_lstat_cache(cache);
+	int flags;
+	(void)lstat_cache_matchlen(cache, name, len, &flags, track_flags,
+			prefix_len_stat_func);
+	return flags;
 }
 
 #define USE_ONLY_LSTAT  0
@@ -225,15 +209,39 @@
 }
 
 /*
- * Return non-zero if path 'name' has a leading symlink component or
+ * Return zero if path 'name' has a leading symlink component or
  * if some leading path component does not exists.
+ *
+ * Return -1 if leading path exists and is a directory.
+ *
+ * Return path length if leading path exists and is neither a
+ * directory nor a symlink.
  */
-int has_symlink_or_noent_leading_path(const char *name, int len)
+int check_leading_path(const char *name, int len)
 {
-	struct cache_def *cache = &default_cache;	/* FIXME */
-	return lstat_cache(cache, name, len,
-			   FL_SYMLINK|FL_NOENT|FL_DIR, USE_ONLY_LSTAT) &
-		(FL_SYMLINK|FL_NOENT);
+    return threaded_check_leading_path(&default_cache, name, len);
+}
+
+/*
+ * Return zero if path 'name' has a leading symlink component or
+ * if some leading path component does not exists.
+ *
+ * Return -1 if leading path exists and is a directory.
+ *
+ * Return path length if leading path exists and is neither a
+ * directory nor a symlink.
+ */
+int threaded_check_leading_path(struct cache_def *cache, const char *name, int len)
+{
+	int flags;
+	int match_len = lstat_cache_matchlen(cache, name, len, &flags,
+			   FL_SYMLINK|FL_NOENT|FL_DIR, USE_ONLY_LSTAT);
+	if (flags & FL_NOENT)
+		return 0;
+	else if (flags & FL_DIR)
+		return -1;
+	else
+		return match_len;
 }
 
 /*
@@ -245,7 +253,18 @@
  */
 int has_dirs_only_path(const char *name, int len, int prefix_len)
 {
-	struct cache_def *cache = &default_cache;	/* FIXME */
+	return threaded_has_dirs_only_path(&default_cache, name, len, prefix_len);
+}
+
+/*
+ * Return non-zero if all path components of 'name' exists as a
+ * directory.  If prefix_len > 0, we will test with the stat()
+ * function instead of the lstat() function for a prefix length of
+ * 'prefix_len', thus we then allow for symlinks in the prefix part as
+ * long as those points to real existing directories.
+ */
+int threaded_has_dirs_only_path(struct cache_def *cache, const char *name, int len, int prefix_len)
+{
 	return lstat_cache(cache, name, len,
 			   FL_DIR|FL_FULLPATH, prefix_len) &
 		FL_DIR;
diff --git a/t/.gitignore b/t/.gitignore
index 7dcbb23..4e731dc 100644
--- a/t/.gitignore
+++ b/t/.gitignore
@@ -1,2 +1,3 @@
 /trash directory*
 /test-results
+/.prove
diff --git a/t/Makefile b/t/Makefile
index bf816fc..88e289f 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -3,42 +3,117 @@
 # Copyright (c) 2005 Junio C Hamano
 #
 
+-include ../config.mak.autogen
+-include ../config.mak
+
 #GIT_TEST_OPTS=--verbose --debug
 SHELL_PATH ?= $(SHELL)
+PERL_PATH ?= /usr/bin/perl
 TAR ?= $(TAR)
 RM ?= rm -f
+PROVE ?= prove
+DEFAULT_TEST_TARGET ?= test
 
 # Shell quote;
 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)
+T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh))
+TSVN = $(sort $(wildcard t91[0-9][0-9]-*.sh))
+TGITWEB = $(sort $(wildcard t95[0-9][0-9]-*.sh))
 
-all: pre-clean
+all: $(DEFAULT_TEST_TARGET)
+
+test: pre-clean $(TEST_LINT)
 	$(MAKE) aggregate-results-and-cleanup
 
+prove: pre-clean $(TEST_LINT)
+	@echo "*** prove ***"; GIT_CONFIG=.git/config $(PROVE) --exec '$(SHELL_PATH_SQ)' $(GIT_PROVE_OPTS) $(T) :: $(GIT_TEST_OPTS)
+	$(MAKE) clean-except-prove-cache
+
 $(T):
 	@echo "*** $@ ***"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
 
 pre-clean:
 	$(RM) -r test-results
 
-clean:
+clean-except-prove-cache:
 	$(RM) -r 'trash directory'.* test-results
+	$(RM) -r valgrind/bin
+
+clean: clean-except-prove-cache
+	$(RM) .prove
+
+test-lint: test-lint-duplicates test-lint-executable
+
+test-lint-duplicates:
+	@dups=`echo $(T) | tr ' ' '\n' | sed 's/-.*//' | sort | uniq -d` && \
+		test -z "$$dups" || { \
+		echo >&2 "duplicate test numbers:" $$dups; exit 1; }
+
+test-lint-executable:
+	@bad=`for i in $(T); do test -x "$$i" || echo $$i; done` && \
+		test -z "$$bad" || { \
+		echo >&2 "non-executable tests:" $$bad; exit 1; }
 
 aggregate-results-and-cleanup: $(T)
 	$(MAKE) aggregate-results
 	$(MAKE) clean
 
 aggregate-results:
-	'$(SHELL_PATH_SQ)' ./aggregate-results.sh test-results/t*-*
+	for f in test-results/t*-*.counts; do \
+		echo "$$f"; \
+	done | '$(SHELL_PATH_SQ)' ./aggregate-results.sh
 
 # we can test NO_OPTIMIZE_COMMITS independently of LC_ALL
 full-svn-test:
 	$(MAKE) $(TSVN) GIT_SVN_NO_OPTIMIZE_COMMITS=1 LC_ALL=C
 	$(MAKE) $(TSVN) GIT_SVN_NO_OPTIMIZE_COMMITS=0 LC_ALL=en_US.UTF-8
 
-valgrind:
-	GIT_TEST_OPTS=--valgrind $(MAKE)
+gitweb-test:
+	$(MAKE) $(TGITWEB)
 
-.PHONY: pre-clean $(T) aggregate-results clean valgrind
+valgrind:
+	$(MAKE) GIT_TEST_OPTS="$(GIT_TEST_OPTS) --valgrind"
+
+perf:
+	$(MAKE) -C perf/ all
+
+# Smoke testing targets
+-include ../GIT-VERSION-FILE
+uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo unknown')
+uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo unknown')
+
+test-results:
+	mkdir -p test-results
+
+test-results/git-smoke.tar.gz: test-results
+	$(PERL_PATH) ./harness \
+		--archive="test-results/git-smoke.tar.gz" \
+		$(T)
+
+smoke: test-results/git-smoke.tar.gz
+
+SMOKE_UPLOAD_FLAGS =
+ifdef SMOKE_USERNAME
+	SMOKE_UPLOAD_FLAGS += -F username="$(SMOKE_USERNAME)" -F password="$(SMOKE_PASSWORD)"
+endif
+ifdef SMOKE_COMMENT
+	SMOKE_UPLOAD_FLAGS += -F comments="$(SMOKE_COMMENT)"
+endif
+ifdef SMOKE_TAGS
+	SMOKE_UPLOAD_FLAGS += -F tags="$(SMOKE_TAGS)"
+endif
+
+smoke_report: smoke
+	curl \
+		-H "Expect: " \
+		-F project=Git \
+		-F architecture="$(uname_M)" \
+		-F platform="$(uname_S)" \
+		-F revision="$(GIT_VERSION)" \
+		-F report_file=@test-results/git-smoke.tar.gz \
+		$(SMOKE_UPLOAD_FLAGS) \
+		http://smoke.git.nix.is/app/projects/process_add_report/1 \
+	| grep -v ^Redirecting
+
+.PHONY: pre-clean $(T) aggregate-results clean valgrind perf
diff --git a/t/README b/t/README
index d8f6c7d..4c3ea25 100644
--- a/t/README
+++ b/t/README
@@ -18,25 +18,54 @@
 the tests.
 
     *** t0000-basic.sh ***
-    *   ok 1: .git/objects should be empty after git-init in an empty repo.
-    *   ok 2: .git/objects should have 256 subdirectories.
-    *   ok 3: git-update-index without --add should fail adding.
+    ok 1 - .git/objects should be empty after git init in an empty repo.
+    ok 2 - .git/objects should have 3 subdirectories.
+    ok 3 - success is reported like this
     ...
-    *   ok 23: no diff after checkout and git-update-index --refresh.
-    * passed all 23 test(s)
-    *** t0100-environment-names.sh ***
-    *   ok 1: using old names should issue warnings.
-    *   ok 2: using old names but having new names should not issue warnings.
-    ...
+    ok 43 - very long name in the index handled sanely
+    # fixed 1 known breakage(s)
+    # still have 1 known breakage(s)
+    # passed all remaining 42 test(s)
+    1..43
+    *** t0001-init.sh ***
+    ok 1 - plain
+    ok 2 - plain with GIT_WORK_TREE
+    ok 3 - plain bare
 
-Or you can run each test individually from command line, like
-this:
+Since the tests all output TAP (see http://testanything.org) they can
+be run with any TAP harness. Here's an example of parallel testing
+powered by a recent version of prove(1):
 
-    $ sh ./t3001-ls-files-killed.sh
-    *   ok 1: git-update-index --add to add various paths.
-    *   ok 2: git-ls-files -k to show killed files.
-    *   ok 3: validate git-ls-files -k output.
-    * passed all 3 test(s)
+    $ prove --timer --jobs 15 ./t[0-9]*.sh
+    [19:17:33] ./t0005-signals.sh ................................... ok       36 ms
+    [19:17:33] ./t0022-crlf-rename.sh ............................... ok       69 ms
+    [19:17:33] ./t0024-crlf-archive.sh .............................. ok      154 ms
+    [19:17:33] ./t0004-unwritable.sh ................................ ok      289 ms
+    [19:17:33] ./t0002-gitfile.sh ................................... ok      480 ms
+    ===(     102;0  25/?  6/?  5/?  16/?  1/?  4/?  2/?  1/?  3/?  1... )===
+
+prove and other harnesses come with a lot of useful options. The
+--state option in particular is very useful:
+
+    # Repeat until no more failures
+    $ prove -j 15 --state=failed,save ./t[0-9]*.sh
+
+You can give DEFAULT_TEST_TARGET=prove on the make command (or define it
+in config.mak) to cause "make test" to run tests under prove.
+GIT_PROVE_OPTS can be used to pass additional options, e.g.
+
+    $ make DEFAULT_TEST_TARGET=prove GIT_PROVE_OPTS='--timer --jobs 16' test
+
+You can also run each test individually from command line, like this:
+
+    $ sh ./t3010-ls-files-killed-modified.sh
+    ok 1 - git update-index --add to add various paths.
+    ok 2 - git ls-files -k to show killed files.
+    ok 3 - validate git ls-files -k output.
+    ok 4 - git ls-files -m to show modified files.
+    ok 5 - validate git ls-files -m output.
+    # passed all 5 test(s)
+    1..5
 
 You can pass --verbose (or -v), --debug (or -d), and --immediate
 (or -i) command line argument to the test, or by setting GIT_TEST_OPTS
@@ -50,6 +79,10 @@
 --debug::
 	This may help the person who is developing a new test.
 	It causes the command defined with test_debug to run.
+	The "trash" directory (used to store all temporary data
+	during testing) is not deleted even if there are no
+	failed tests so that you can inspect its contents after
+	the test finished.
 
 --immediate::
 	This causes the test to immediately exit upon the first
@@ -69,12 +102,47 @@
 	not see any output, this option implies --verbose.  For
 	convenience, it also implies --tee.
 
+	Note that valgrind is run with the option --leak-check=no,
+	as the git process is short-lived and some errors are not
+	interesting. In order to run a single command under the same
+	conditions manually, you should set GIT_VALGRIND to point to
+	the 't/valgrind/' directory and use the commands under
+	't/valgrind/bin/'.
+
 --tee::
 	In addition to printing the test output to the terminal,
 	write it to files named 't/test-results/$TEST_NAME.out'.
 	As the names depend on the tests' file names, it is safe to
 	run the tests with this option in parallel.
 
+--with-dashes::
+	By default tests are run without dashed forms of
+	commands (like git-commit) in the PATH (it only uses
+	wrappers from ../bin-wrappers).  Use this option to include
+	the build directory (..) in the PATH, which contains all
+	the dashed forms of commands.  This option is currently
+	implied by other options like --valgrind and
+	GIT_TEST_INSTALLED.
+
+--root=<directory>::
+	Create "trash" directories used to store all temporary data during
+	testing under <directory>, instead of the t/ directory.
+	Using this option with a RAM-based filesystem (such as tmpfs)
+	can massively speed up the test suite.
+
+You can also set the GIT_TEST_INSTALLED environment variable to
+the bindir of an existing git installation to test that installation.
+You still need to have built this git sandbox, from which various
+test-* support programs, templates, and perl libraries are used.
+If your installed git is incomplete, it will silently test parts of
+your built version instead.
+
+When using GIT_TEST_INSTALLED, you can also set GIT_TEST_EXEC_PATH to
+override the location of the dashed-form subcommands (what
+GIT_EXEC_PATH would be used for during normal operation).
+GIT_TEST_EXEC_PATH defaults to `$GIT_TEST_INSTALLED/git --exec-path`.
+
+
 Skipping Tests
 --------------
 
@@ -133,7 +201,7 @@
 If you create files under t/ directory (i.e. here) that is not
 the top-level test script, never name the file to match the above
 pattern.  The Makefile here considers all such files as the
-top-level test script and tries to run all of them.  A care is
+top-level test script and tries to run all of them.  Care is
 especially needed if you are creating a common test library
 file, similar to test-lib.sh, because such a library file may
 not be suitable for standalone execution.
@@ -170,15 +238,149 @@
  - If the script is invoked with command line argument --help
    (or -h), it shows the test_description and exits.
 
- - Creates an empty test directory with an empty .git/objects
-   database and chdir(2) into it.  This directory is 't/trash directory'
-   if you must know, but I do not think you care.
+ - Creates an empty test directory with an empty .git/objects database
+   and chdir(2) into it.  This directory is 't/trash
+   directory.$test_name_without_dotsh', with t/ subject to change by
+   the --root option documented above.
 
  - Defines standard test helper functions for your scripts to
    use.  These functions are designed to make all scripts behave
    consistently when command line arguments --verbose (or -v),
    --debug (or -d), and --immediate (or -i) is given.
 
+Do's, don'ts & things to keep in mind
+-------------------------------------
+
+Here are a few examples of things you probably should and shouldn't do
+when writing tests.
+
+Do:
+
+ - Put all code inside test_expect_success and other assertions.
+
+   Even code that isn't a test per se, but merely some setup code
+   should be inside a test assertion.
+
+ - Chain your test assertions
+
+   Write test code like this:
+
+	git merge foo &&
+	git push bar &&
+	test ...
+
+   Instead of:
+
+	git merge hla
+	git push gh
+	test ...
+
+   That way all of the commands in your tests will succeed or fail. If
+   you must ignore the return value of something, consider using a
+   helper function (e.g. use sane_unset instead of unset, in order
+   to avoid unportable return value for unsetting a variable that was
+   already unset), or prepending the command with test_might_fail or
+   test_must_fail.
+
+ - Check the test coverage for your tests. See the "Test coverage"
+   below.
+
+   Don't blindly follow test coverage metrics; if a new function you added
+   doesn't have any coverage, then you're probably doing something wrong,
+   but having 100% coverage doesn't necessarily mean that you tested
+   everything.
+
+   Tests that are likely to smoke out future regressions are better
+   than tests that just inflate the coverage metrics.
+
+ - When a test checks for an absolute path that a git command generated,
+   construct the expected value using $(pwd) rather than $PWD,
+   $TEST_DIRECTORY, or $TRASH_DIRECTORY. It makes a difference on
+   Windows, where the shell (MSYS bash) mangles absolute path names.
+   For details, see the commit message of 4114156ae9.
+
+Don't:
+
+ - exit() within a <script> part.
+
+   The harness will catch this as a programming error of the test.
+   Use test_done instead if you need to stop the tests early (see
+   "Skipping tests" below).
+
+ - use '! git cmd' when you want to make sure the git command exits
+   with failure in a controlled way by calling "die()".  Instead,
+   use 'test_must_fail git cmd'.  This will signal a failure if git
+   dies in an unexpected way (e.g. segfault).
+
+ - use perl without spelling it as "$PERL_PATH". This is to help our
+   friends on Windows where the platform Perl often adds CR before
+   the end of line, and they bundle Git with a version of Perl that
+   does not do so, whose path is specified with $PERL_PATH.
+
+ - use sh without spelling it as "$SHELL_PATH", when the script can
+   be misinterpreted by broken platform shell (e.g. Solaris).
+
+ - chdir around in tests.  It is not sufficient to chdir to
+   somewhere and then chdir back to the original location later in
+   the test, as any intermediate step can fail and abort the test,
+   causing the next test to start in an unexpected directory.  Do so
+   inside a subshell if necessary.
+
+ - Break the TAP output
+
+   The raw output from your test may be interpreted by a TAP harness. TAP
+   harnesses will ignore everything they don't know about, but don't step
+   on their toes in these areas:
+
+   - Don't print lines like "$x..$y" where $x and $y are integers.
+
+   - Don't print lines that begin with "ok" or "not ok".
+
+   TAP harnesses expect a line that begins with either "ok" and "not
+   ok" to signal a test passed or failed (and our harness already
+   produces such lines), so your script shouldn't emit such lines to
+   their output.
+
+   You can glean some further possible issues from the TAP grammar
+   (see http://search.cpan.org/perldoc?TAP::Parser::Grammar#TAP_Grammar)
+   but the best indication is to just run the tests with prove(1),
+   it'll complain if anything is amiss.
+
+Keep in mind:
+
+ - Inside <script> part, the standard output and standard error
+   streams are discarded, and the test harness only reports "ok" or
+   "not ok" to the end user running the tests. Under --verbose, they
+   are shown to help debugging the tests.
+
+
+Skipping tests
+--------------
+
+If you need to skip tests you should do so by using the three-arg form
+of the test_* functions (see the "Test harness library" section
+below), e.g.:
+
+    test_expect_success PERL 'I need Perl' '
+        "$PERL_PATH" -e "hlagh() if unf_unf()"
+    '
+
+The advantage of skipping tests like this is that platforms that don't
+have the PERL and other optional dependencies get an indication of how
+many tests they're missing.
+
+If the test code is too hairy for that (i.e. does a lot of setup work
+outside test assertions) you can also skip all remaining tests by
+setting skip_all and immediately call test_done:
+
+	if ! test_have_prereq PERL
+	then
+	    skip_all='skipping perl interface tests, perl not available'
+	    test_done
+	fi
+
+The string you give to skip_all will be used as an explanation for why
+the test was skipped.
 
 End with test_done
 ------------------
@@ -194,9 +396,9 @@
 There are a handful helper functions defined in the test harness
 library for your script to use.
 
- - test_expect_success <message> <script>
+ - test_expect_success [<prereq>] <message> <script>
 
-   This takes two strings as parameter, and evaluates the
+   Usually takes two strings as parameters, and evaluates the
    <script>.  If it yields success, test is considered
    successful.  <message> should state what it is testing.
 
@@ -206,7 +408,20 @@
 	    'git-write-tree should be able to write an empty tree.' \
 	    'tree=$(git-write-tree)'
 
- - test_expect_failure <message> <script>
+   If you supply three parameters the first will be taken to be a
+   prerequisite; see the test_set_prereq and test_have_prereq
+   documentation below:
+
+	test_expect_success TTY 'git --paginate rev-list uses a pager' \
+	    ' ... '
+
+   You can also supply a comma-separated list of prerequisites, in the
+   rare case where your test depends on more than one:
+
+	test_expect_success PERL,PYTHON 'yo dawg' \
+	    ' test $(perl -E 'print eval "1 +" . qx[python -c "print 2"]') == "4" '
+
+ - test_expect_failure [<prereq>] <message> <script>
 
    This is NOT the opposite of test_expect_success, but is used
    to mark a test that demonstrates a known breakage.  Unlike
@@ -215,6 +430,9 @@
    success and "still broken" on failure.  Failures from these
    tests won't cause -i (immediate) to stop.
 
+   Like test_expect_success this function can optionally use a three
+   argument invocation with a prerequisite as the first argument.
+
  - test_debug <script>
 
    This takes a single argument, <script>, and evaluates it only
@@ -231,7 +449,7 @@
  - test_tick
 
    Make commit and tag names consistent by setting the author and
-   committer times to defined stated.  Subsequent calls will
+   committer times to defined state.  Subsequent calls will
    advance the times by a fixed amount.
 
  - test_commit <message> [<filename> [<contents>]]
@@ -247,6 +465,166 @@
    Merges the given rev using the given message.  Like test_commit,
    creates a tag and calls test_tick before committing.
 
+ - test_set_prereq <prereq>
+
+   Set a test prerequisite to be used later with test_have_prereq. The
+   test-lib will set some prerequisites for you, see the
+   "Prerequisites" section below for a full list of these.
+
+   Others you can set yourself and use later with either
+   test_have_prereq directly, or the three argument invocation of
+   test_expect_success and test_expect_failure.
+
+ - test_have_prereq <prereq>
+
+   Check if we have a prerequisite previously set with
+   test_set_prereq. The most common use of this directly is to skip
+   all the tests if we don't have some essential prerequisite:
+
+	if ! test_have_prereq PERL
+	then
+	    skip_all='skipping perl interface tests, perl not available'
+	    test_done
+	fi
+
+ - test_external [<prereq>] <message> <external> <script>
+
+   Execute a <script> with an <external> interpreter (like perl). This
+   was added for tests like t9700-perl-git.sh which do most of their
+   work in an external test script.
+
+	test_external \
+	    'GitwebCache::*FileCache*' \
+	    "$PERL_PATH" "$TEST_DIRECTORY"/t9503/test_cache_interface.pl
+
+   If the test is outputting its own TAP you should set the
+   test_external_has_tap variable somewhere before calling the first
+   test_external* function. See t9700-perl-git.sh for an example.
+
+	# The external test will outputs its own plan
+	test_external_has_tap=1
+
+ - test_external_without_stderr [<prereq>] <message> <external> <script>
+
+   Like test_external but fail if there's any output on stderr,
+   instead of checking the exit code.
+
+	test_external_without_stderr \
+	    'Perl API' \
+	    "$PERL_PATH" "$TEST_DIRECTORY"/t9700/test.pl
+
+ - test_expect_code <exit-code> <command>
+
+   Run a command and ensure that it exits with the given exit code.
+   For example:
+
+	test_expect_success 'Merge with d/f conflicts' '
+		test_expect_code 1 git merge "merge msg" B master
+	'
+
+ - test_must_fail <git-command>
+
+   Run a git command and ensure it fails in a controlled way.  Use
+   this instead of "! <git-command>".  When git-command dies due to a
+   segfault, test_must_fail diagnoses it as an error; "! <git-command>"
+   treats it as just another expected failure, which would let such a
+   bug go unnoticed.
+
+ - test_might_fail <git-command>
+
+   Similar to test_must_fail, but tolerate success, too.  Use this
+   instead of "<git-command> || :" to catch failures due to segv.
+
+ - test_cmp <expected> <actual>
+
+   Check whether the content of the <actual> file matches the
+   <expected> file.  This behaves like "cmp" but produces more
+   helpful output when the test is run with "-v" option.
+
+ - test_line_count (= | -lt | -ge | ...) <length> <file>
+
+   Check whether a file has the length it is expected to.
+
+ - test_path_is_file <path> [<diagnosis>]
+   test_path_is_dir <path> [<diagnosis>]
+   test_path_is_missing <path> [<diagnosis>]
+
+   Check if the named path is a file, if the named path is a
+   directory, or if the named path does not exist, respectively,
+   and fail otherwise, showing the <diagnosis> text.
+
+ - test_when_finished <script>
+
+   Prepend <script> to a list of commands to run to clean up
+   at the end of the current test.  If some clean-up command
+   fails, the test will not pass.
+
+   Example:
+
+	test_expect_success 'branch pointing to non-commit' '
+		git rev-parse HEAD^{tree} >.git/refs/heads/invalid &&
+		test_when_finished "git update-ref -d refs/heads/invalid" &&
+		...
+	'
+
+ - test_pause
+
+	This command is useful for writing and debugging tests and must be
+	removed before submitting. It halts the execution of the test and
+	spawns a shell in the trash directory. Exit the shell to continue
+	the test. Example:
+
+	test_expect_success 'test' '
+		git do-something >actual &&
+		test_pause &&
+		test_cmp expected actual
+	'
+
+Prerequisites
+-------------
+
+These are the prerequisites that the test library predefines with
+test_have_prereq.
+
+See the prereq argument to the test_* functions in the "Test harness
+library" section above and the "test_have_prereq" function for how to
+use these, and "test_set_prereq" for how to define your own.
+
+ - PERL & PYTHON
+
+   Git wasn't compiled with NO_PERL=YesPlease or
+   NO_PYTHON=YesPlease. Wrap any tests that need Perl or Python in
+   these.
+
+ - POSIXPERM
+
+   The filesystem supports POSIX style permission bits.
+
+ - BSLASHPSPEC
+
+   Backslashes in pathspec are not directory separators. This is not
+   set on Windows. See 6fd1106a for details.
+
+ - EXECKEEPSPID
+
+   The process retains the same pid across exec(2). See fb9a2bea for
+   details.
+
+ - SYMLINKS
+
+   The filesystem we're on supports symbolic links. E.g. a FAT
+   filesystem doesn't support these. See 704a3143 for details.
+
+ - SANITY
+
+   Test is not run by root user, and an attempt to write to an
+   unwritable file is expected to fail correctly.
+
+ - LIBPCRE
+
+   Git was compiled with USE_LIBPCRE=YesPlease. Wrap any tests
+   that use git-grep --perl-regexp or git-grep -P in these.
+
 Tips for Writing Tests
 ----------------------
 
@@ -273,3 +651,42 @@
 validation in one place.  Your test also ends up needing
 updating when such a change to the internal happens, so do _not_
 do it and leave the low level of validation to t0000-basic.sh.
+
+Test coverage
+-------------
+
+You can use the coverage tests to find code paths that are not being
+used or properly exercised yet.
+
+To do that, run the coverage target at the top-level (not in the t/
+directory):
+
+    make coverage
+
+That'll compile Git with GCC's coverage arguments, and generate a test
+report with gcov after the tests finish. Running the coverage tests
+can take a while, since running the tests in parallel is incompatible
+with GCC's coverage mode.
+
+After the tests have run you can generate a list of untested
+functions:
+
+    make coverage-untested-functions
+
+You can also generate a detailed per-file HTML report using the
+Devel::Cover module. To install it do:
+
+   # On Debian or Ubuntu:
+   sudo aptitude install libdevel-cover-perl
+
+   # From the CPAN with cpanminus
+   curl -L http://cpanmin.us | perl - --sudo --self-upgrade
+   cpanm --sudo Devel::Cover
+
+Then, at the top-level:
+
+    make cover_db_html
+
+That'll generate a detailed cover report in the "cover_db_html"
+directory, which you can then copy to a webserver, or inspect locally
+in a browser.
diff --git a/t/aggregate-results.sh b/t/aggregate-results.sh
index d5bab75..7913e20 100755
--- a/t/aggregate-results.sh
+++ b/t/aggregate-results.sh
@@ -1,12 +1,13 @@
 #!/bin/sh
 
+failed_tests=
 fixed=0
 success=0
 failed=0
 broken=0
 total=0
 
-for file
+while read file
 do
 	while read type value
 	do
@@ -18,7 +19,13 @@
 		success)
 			success=$(($success + $value)) ;;
 		failed)
-			failed=$(($failed + $value)) ;;
+			failed=$(($failed + $value))
+			if test $value != 0
+			then
+				testnum=$(expr "$file" : 'test-results/\(t[0-9]*\)-')
+				failed_tests="$failed_tests $testnum"
+			fi
+			;;
 		broken)
 			broken=$(($broken + $value)) ;;
 		total)
@@ -27,6 +34,11 @@
 	done <"$file"
 done
 
+if test -n "$failed_tests"
+then
+	printf "\nfailed test(s):$failed_tests\n\n"
+fi
+
 printf "%-8s%d\n" fixed $fixed
 printf "%-8s%d\n" success $success
 printf "%-8s%d\n" failed $failed
diff --git a/t/annotate-tests.sh b/t/annotate-tests.sh
index 396b965..c56a77d 100644
--- a/t/annotate-tests.sh
+++ b/t/annotate-tests.sh
@@ -1,5 +1,5 @@
 # This file isn't used as a test script directly, instead it is
-# sourced from t8001-annotate.sh and t8001-blame.sh.
+# sourced from t8001-annotate.sh and t8002-blame.sh.
 
 check_count () {
 	head=
@@ -8,27 +8,27 @@
 	$PROG file $head >.result || return 1
 	cat .result | perl -e '
 		my %expect = (@ARGV);
-		my %count = ();
+		my %count = map { $_ => 0 } keys %expect;
 		while (<STDIN>) {
 			if (/^[0-9a-f]+\t\(([^\t]+)\t/) {
 				my $author = $1;
 				for ($author) { s/^\s*//; s/\s*$//; }
-				if (exists $expect{$author}) {
-					$count{$author}++;
-				}
+				$count{$author}++;
 			}
 		}
 		my $bad = 0;
 		while (my ($author, $count) = each %count) {
 			my $ok;
-			if ($expect{$author} != $count) {
+			my $value = 0;
+			$value = $expect{$author} if defined $expect{$author};
+			if ($value != $count) {
 				$bad = 1;
 				$ok = "bad";
 			}
 			else {
 				$ok = "good";
 			}
-			print STDERR "Author $author (expected $expect{$author}, attributed $count) $ok\n";
+			print STDERR "Author $author (expected $value, attributed $count) $ok\n";
 		}
 		exit($bad);
 	' "$@"
@@ -38,8 +38,8 @@
     'prepare reference tree' \
     'echo "1A quick brown fox jumps over the" >file &&
      echo "lazy dog" >>file &&
-     git add file
-     GIT_AUTHOR_NAME="A" git commit -a -m "Initial."'
+     git add file &&
+     GIT_AUTHOR_NAME="A" GIT_AUTHOR_EMAIL="A@test.git" git commit -a -m "Initial."'
 
 test_expect_success \
     'check all lines blamed on A' \
@@ -49,7 +49,7 @@
     'Setup new lines blamed on B' \
     'echo "2A quick brown fox jumps over the" >>file &&
      echo "lazy dog" >> file &&
-     GIT_AUTHOR_NAME="B" git commit -a -m "Second."'
+     GIT_AUTHOR_NAME="B" GIT_AUTHOR_EMAIL="B@test.git" git commit -a -m "Second."'
 
 test_expect_success \
     'Two lines blamed on A, two on B' \
@@ -60,7 +60,7 @@
     'git checkout -b branch1 master &&
      echo "3A slow green fox jumps into the" >> file &&
      echo "well." >> file &&
-     GIT_AUTHOR_NAME="B1" git commit -a -m "Branch1-1"'
+     GIT_AUTHOR_NAME="B1" GIT_AUTHOR_EMAIL="B1@test.git" git commit -a -m "Branch1-1"'
 
 test_expect_success \
     'Two lines blamed on A, two on B, two on B1' \
@@ -71,7 +71,7 @@
     'git checkout -b branch2 master &&
      sed -e "s/2A quick brown/4A quick brown lazy dog/" < file > file.new &&
      mv file.new file &&
-     GIT_AUTHOR_NAME="B2" git commit -a -m "Branch2-1"'
+     GIT_AUTHOR_NAME="B2" GIT_AUTHOR_EMAIL="B2@test.git" git commit -a -m "Branch2-1"'
 
 test_expect_success \
     'Two lines blamed on A, one on B, one on B2' \
@@ -105,7 +105,7 @@
 test_expect_success \
     'an incomplete line added' \
     'echo "incomplete" | tr -d "\\012" >>file &&
-    GIT_AUTHOR_NAME="C" git commit -a -m "Incomplete"'
+    GIT_AUTHOR_NAME="C" GIT_AUTHOR_EMAIL="C@test.git" git commit -a -m "Incomplete"'
 
 test_expect_success \
     'With incomplete lines.' \
@@ -119,8 +119,19 @@
 	echo
     } | sed -e "s/^3A/99/" -e "/^1A/d" -e "/^incomplete/d" > file &&
     echo "incomplete" | tr -d "\\012" >>file &&
-    GIT_AUTHOR_NAME="D" git commit -a -m "edit"'
+    GIT_AUTHOR_NAME="D" GIT_AUTHOR_EMAIL="D@test.git" git commit -a -m "edit"'
 
 test_expect_success \
     'some edit' \
     'check_count A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1'
+
+test_expect_success \
+    'an obfuscated email added' \
+    'echo "No robots allowed" > file.new &&
+     cat file >> file.new &&
+     mv file.new file &&
+     GIT_AUTHOR_NAME="E" GIT_AUTHOR_EMAIL="E at test dot git" git commit -a -m "norobots"'
+
+test_expect_success \
+    'obfuscated email parsed' \
+    'check_count A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1 E 1'
diff --git a/t/diff-lib.sh b/t/diff-lib.sh
index 4bddeb5..75a35fc 100644
--- a/t/diff-lib.sh
+++ b/t/diff-lib.sh
@@ -1,7 +1,5 @@
 :
 
-_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
-_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
 sanitize_diff_raw='/^:/s/ '"$_x40"' '"$_x40"' \([A-Z]\)[0-9]*	/ X X \1#	/'
 compare_diff_raw () {
     # When heuristics are improved, the score numbers would change.
diff --git a/t/gitweb-lib.sh b/t/gitweb-lib.sh
new file mode 100644
index 0000000..ae2dc46
--- /dev/null
+++ b/t/gitweb-lib.sh
@@ -0,0 +1,116 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Jakub Narebski
+#
+
+gitweb_init () {
+	safe_pwd="$(perl -MPOSIX=getcwd -e 'print quotemeta(getcwd)')"
+	cat >gitweb_config.perl <<EOF
+#!/usr/bin/perl
+
+# gitweb configuration for tests
+
+our \$version = 'current';
+our \$GIT = 'git';
+our \$projectroot = "$safe_pwd";
+our \$project_maxdepth = 8;
+our \$home_link_str = 'projects';
+our \$site_name = '[localhost]';
+our \$site_html_head_string = '';
+our \$site_header = '';
+our \$site_footer = '';
+our \$home_text = 'indextext.html';
+our @stylesheets = ('file:///$GIT_BUILD_DIR/gitweb/static/gitweb.css');
+our \$logo = 'file:///$GIT_BUILD_DIR/gitweb/static/git-logo.png';
+our \$favicon = 'file:///$GIT_BUILD_DIR/gitweb/static/git-favicon.png';
+our \$projects_list = '';
+our \$export_ok = '';
+our \$strict_export = '';
+our \$maxload = undef;
+
+EOF
+
+	cat >.git/description <<EOF
+$0 test repository
+EOF
+
+	# You can set the GITWEB_TEST_INSTALLED environment variable to
+	# the gitwebdir (the directory where gitweb is installed / deployed to)
+	# of an existing gitweb instalation to test that installation,
+	# or simply to pathname of installed gitweb script.
+	if test -n "$GITWEB_TEST_INSTALLED" ; then
+		if test -d $GITWEB_TEST_INSTALLED; then
+			SCRIPT_NAME="$GITWEB_TEST_INSTALLED/gitweb.cgi"
+		else
+			SCRIPT_NAME="$GITWEB_TEST_INSTALLED"
+		fi
+		test -f "$SCRIPT_NAME" ||
+		error "Cannot find gitweb at $GITWEB_TEST_INSTALLED."
+		say "# Testing $SCRIPT_NAME"
+	else # normal case, use source version of gitweb
+		SCRIPT_NAME="$GIT_BUILD_DIR/gitweb/gitweb.perl"
+	fi
+	export SCRIPT_NAME
+}
+
+gitweb_run () {
+	GATEWAY_INTERFACE='CGI/1.1'
+	HTTP_ACCEPT='*/*'
+	REQUEST_METHOD='GET'
+	QUERY_STRING=""$1""
+	PATH_INFO=""$2""
+	export GATEWAY_INTERFACE HTTP_ACCEPT REQUEST_METHOD \
+		QUERY_STRING PATH_INFO
+
+	GITWEB_CONFIG=$(pwd)/gitweb_config.perl
+	export GITWEB_CONFIG
+
+	# some of git commands write to STDERR on error, but this is not
+	# written to web server logs, so we are not interested in that:
+	# we are interested only in properly formatted errors/warnings
+	rm -f gitweb.log &&
+	"$PERL_PATH" -- "$SCRIPT_NAME" \
+		>gitweb.output 2>gitweb.log &&
+	perl -w -e '
+		open O, ">gitweb.headers";
+		while (<>) {
+			print O;
+			last if (/^\r$/ || /^$/);
+		}
+		open O, ">gitweb.body";
+		while (<>) {
+			print O;
+		}
+		close O;
+	' gitweb.output &&
+	if grep '^[[]' gitweb.log >/dev/null 2>&1; then
+		test_debug 'cat gitweb.log >&2' &&
+		false
+	else
+		true
+	fi
+
+	# gitweb.log is left for debugging
+	# gitweb.output is used to parse HTTP output
+	# gitweb.headers contains only HTTP headers
+	# gitweb.body contains body of message, without headers
+}
+
+. ./test-lib.sh
+
+if ! test_have_prereq PERL; then
+	skip_all='skipping gitweb tests, perl not available'
+	test_done
+fi
+
+perl -MEncode -e '$e="";decode_utf8($e, Encode::FB_CROAK)' >/dev/null 2>&1 || {
+	skip_all='skipping gitweb tests, perl version is too old'
+	test_done
+}
+
+perl -MCGI -MCGI::Util -MCGI::Carp -e 0 >/dev/null 2>&1 || {
+	skip_all='skipping gitweb tests, CGI module unusable'
+	test_done
+}
+
+gitweb_init
diff --git a/t/lib-bash.sh b/t/lib-bash.sh
new file mode 100644
index 0000000..11397f7
--- /dev/null
+++ b/t/lib-bash.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+#
+# Ensures that tests are run under Bash; primarily intended for running tests
+# of the completion script.
+
+if test -n "$BASH" && test -z "$POSIXLY_CORRECT"; then
+	# we are in full-on bash mode
+	true
+elif type bash >/dev/null 2>&1; then
+	# execute in full-on bash mode
+	unset POSIXLY_CORRECT
+	exec bash "$0" "$@"
+else
+	echo '1..0 #SKIP skipping bash completion tests; bash not available'
+	exit 0
+fi
+
+. ./test-lib.sh
diff --git a/t/lib-credential.sh b/t/lib-credential.sh
new file mode 100755
index 0000000..957ae93
--- /dev/null
+++ b/t/lib-credential.sh
@@ -0,0 +1,289 @@
+#!/bin/sh
+
+# Try a set of credential helpers; the expected stdin,
+# stdout and stderr should be provided on stdin,
+# separated by "--".
+check() {
+	credential_opts=
+	credential_cmd=$1
+	shift
+	for arg in "$@"; do
+		credential_opts="$credential_opts -c credential.helper='$arg'"
+	done
+	read_chunk >stdin &&
+	read_chunk >expect-stdout &&
+	read_chunk >expect-stderr &&
+	if ! eval "git $credential_opts credential $credential_cmd <stdin >stdout 2>stderr"; then
+		echo "git credential failed with code $?" &&
+		cat stderr &&
+		false
+	fi &&
+	test_cmp expect-stdout stdout &&
+	test_cmp expect-stderr stderr
+}
+
+read_chunk() {
+	while read line; do
+		case "$line" in
+		--) break ;;
+		*) echo "$line" ;;
+		esac
+	done
+}
+
+# Clear any residual data from previous tests. We only
+# need this when testing third-party helpers which read and
+# write outside of our trash-directory sandbox.
+#
+# Don't bother checking for success here, as it is
+# outside the scope of tests and represents a best effort to
+# clean up after ourselves.
+helper_test_clean() {
+	reject $1 https example.com store-user
+	reject $1 https example.com user1
+	reject $1 https example.com user2
+	reject $1 http path.tld user
+	reject $1 https timeout.tld user
+}
+
+reject() {
+	(
+		echo protocol=$2
+		echo host=$3
+		echo username=$4
+	) | git -c credential.helper=$1 credential reject
+}
+
+helper_test() {
+	HELPER=$1
+
+	test_expect_success "helper ($HELPER) has no existing data" '
+		check fill $HELPER <<-\EOF
+		protocol=https
+		host=example.com
+		--
+		protocol=https
+		host=example.com
+		username=askpass-username
+		password=askpass-password
+		--
+		askpass: Username for '\''https://example.com'\'':
+		askpass: Password for '\''https://askpass-username@example.com'\'':
+		EOF
+	'
+
+	test_expect_success "helper ($HELPER) stores password" '
+		check approve $HELPER <<-\EOF
+		protocol=https
+		host=example.com
+		username=store-user
+		password=store-pass
+		EOF
+	'
+
+	test_expect_success "helper ($HELPER) can retrieve password" '
+		check fill $HELPER <<-\EOF
+		protocol=https
+		host=example.com
+		--
+		protocol=https
+		host=example.com
+		username=store-user
+		password=store-pass
+		--
+		EOF
+	'
+
+	test_expect_success "helper ($HELPER) requires matching protocol" '
+		check fill $HELPER <<-\EOF
+		protocol=http
+		host=example.com
+		--
+		protocol=http
+		host=example.com
+		username=askpass-username
+		password=askpass-password
+		--
+		askpass: Username for '\''http://example.com'\'':
+		askpass: Password for '\''http://askpass-username@example.com'\'':
+		EOF
+	'
+
+	test_expect_success "helper ($HELPER) requires matching host" '
+		check fill $HELPER <<-\EOF
+		protocol=https
+		host=other.tld
+		--
+		protocol=https
+		host=other.tld
+		username=askpass-username
+		password=askpass-password
+		--
+		askpass: Username for '\''https://other.tld'\'':
+		askpass: Password for '\''https://askpass-username@other.tld'\'':
+		EOF
+	'
+
+	test_expect_success "helper ($HELPER) requires matching username" '
+		check fill $HELPER <<-\EOF
+		protocol=https
+		host=example.com
+		username=other
+		--
+		protocol=https
+		host=example.com
+		username=other
+		password=askpass-password
+		--
+		askpass: Password for '\''https://other@example.com'\'':
+		EOF
+	'
+
+	test_expect_success "helper ($HELPER) requires matching path" '
+		test_config credential.usehttppath true &&
+		check approve $HELPER <<-\EOF &&
+		protocol=http
+		host=path.tld
+		path=foo.git
+		username=user
+		password=pass
+		EOF
+		check fill $HELPER <<-\EOF
+		protocol=http
+		host=path.tld
+		path=bar.git
+		--
+		protocol=http
+		host=path.tld
+		path=bar.git
+		username=askpass-username
+		password=askpass-password
+		--
+		askpass: Username for '\''http://path.tld/bar.git'\'':
+		askpass: Password for '\''http://askpass-username@path.tld/bar.git'\'':
+		EOF
+	'
+
+	test_expect_success "helper ($HELPER) can forget host" '
+		check reject $HELPER <<-\EOF &&
+		protocol=https
+		host=example.com
+		EOF
+		check fill $HELPER <<-\EOF
+		protocol=https
+		host=example.com
+		--
+		protocol=https
+		host=example.com
+		username=askpass-username
+		password=askpass-password
+		--
+		askpass: Username for '\''https://example.com'\'':
+		askpass: Password for '\''https://askpass-username@example.com'\'':
+		EOF
+	'
+
+	test_expect_success "helper ($HELPER) can store multiple users" '
+		check approve $HELPER <<-\EOF &&
+		protocol=https
+		host=example.com
+		username=user1
+		password=pass1
+		EOF
+		check approve $HELPER <<-\EOF &&
+		protocol=https
+		host=example.com
+		username=user2
+		password=pass2
+		EOF
+		check fill $HELPER <<-\EOF &&
+		protocol=https
+		host=example.com
+		username=user1
+		--
+		protocol=https
+		host=example.com
+		username=user1
+		password=pass1
+		EOF
+		check fill $HELPER <<-\EOF
+		protocol=https
+		host=example.com
+		username=user2
+		--
+		protocol=https
+		host=example.com
+		username=user2
+		password=pass2
+		EOF
+	'
+
+	test_expect_success "helper ($HELPER) can forget user" '
+		check reject $HELPER <<-\EOF &&
+		protocol=https
+		host=example.com
+		username=user1
+		EOF
+		check fill $HELPER <<-\EOF
+		protocol=https
+		host=example.com
+		username=user1
+		--
+		protocol=https
+		host=example.com
+		username=user1
+		password=askpass-password
+		--
+		askpass: Password for '\''https://user1@example.com'\'':
+		EOF
+	'
+
+	test_expect_success "helper ($HELPER) remembers other user" '
+		check fill $HELPER <<-\EOF
+		protocol=https
+		host=example.com
+		username=user2
+		--
+		protocol=https
+		host=example.com
+		username=user2
+		password=pass2
+		EOF
+	'
+}
+
+helper_test_timeout() {
+	HELPER="$*"
+
+	test_expect_success "helper ($HELPER) times out" '
+		check approve "$HELPER" <<-\EOF &&
+		protocol=https
+		host=timeout.tld
+		username=user
+		password=pass
+		EOF
+		sleep 2 &&
+		check fill "$HELPER" <<-\EOF
+		protocol=https
+		host=timeout.tld
+		--
+		protocol=https
+		host=timeout.tld
+		username=askpass-username
+		password=askpass-password
+		--
+		askpass: Username for '\''https://timeout.tld'\'':
+		askpass: Password for '\''https://askpass-username@timeout.tld'\'':
+		EOF
+	'
+}
+
+cat >askpass <<\EOF
+#!/bin/sh
+echo >&2 askpass: $*
+what=`echo $1 | cut -d" " -f1 | tr A-Z a-z | tr -cd a-z`
+echo "askpass-$what"
+EOF
+chmod +x askpass
+GIT_ASKPASS="$PWD/askpass"
+export GIT_ASKPASS
diff --git a/t/lib-cvs.sh b/t/lib-cvs.sh
index 4b3b793..44263ad 100644
--- a/t/lib-cvs.sh
+++ b/t/lib-cvs.sh
@@ -3,13 +3,10 @@
 . ./test-lib.sh
 
 unset CVS_SERVER
-# for clean cvsps cache
-HOME=$(pwd)
-export HOME
 
 if ! type cvs >/dev/null 2>&1
 then
-	say 'skipping cvsimport tests, cvs not found'
+	skip_all='skipping cvsimport tests, cvs not found'
 	test_done
 fi
 
@@ -21,15 +18,21 @@
 2.1 | 2.2*)
 	;;
 '')
-	say 'skipping cvsimport tests, cvsps not found'
+	skip_all='skipping cvsimport tests, cvsps not found'
 	test_done
 	;;
 *)
-	say 'skipping cvsimport tests, unsupported cvsps version'
+	skip_all='skipping cvsimport tests, unsupported cvsps version'
 	test_done
 	;;
 esac
 
+setup_cvs_test_repository () {
+	CVSROOT="$(pwd)/.cvsroot" &&
+	cp -r "$TEST_DIRECTORY/$1/cvsroot" "$CVSROOT" &&
+	export CVSROOT
+}
+
 test_cvs_co () {
 	# Usage: test_cvs_co BRANCH_NAME
 	rm -rf module-cvs-"$1"
diff --git a/t/lib-diff-alternative.sh b/t/lib-diff-alternative.sh
new file mode 100644
index 0000000..75ffd91
--- /dev/null
+++ b/t/lib-diff-alternative.sh
@@ -0,0 +1,165 @@
+#!/bin/sh
+
+test_diff_frobnitz() {
+	cat >file1 <<\EOF
+#include <stdio.h>
+
+// Frobs foo heartily
+int frobnitz(int foo)
+{
+    int i;
+    for(i = 0; i < 10; i++)
+    {
+        printf("Your answer is: ");
+        printf("%d\n", foo);
+    }
+}
+
+int fact(int n)
+{
+    if(n > 1)
+    {
+        return fact(n-1) * n;
+    }
+    return 1;
+}
+
+int main(int argc, char **argv)
+{
+    frobnitz(fact(10));
+}
+EOF
+
+	cat >file2 <<\EOF
+#include <stdio.h>
+
+int fib(int n)
+{
+    if(n > 2)
+    {
+        return fib(n-1) + fib(n-2);
+    }
+    return 1;
+}
+
+// Frobs foo heartily
+int frobnitz(int foo)
+{
+    int i;
+    for(i = 0; i < 10; i++)
+    {
+        printf("%d\n", foo);
+    }
+}
+
+int main(int argc, char **argv)
+{
+    frobnitz(fib(10));
+}
+EOF
+
+	cat >expect <<\EOF
+diff --git a/file1 b/file2
+index 6faa5a3..e3af329 100644
+--- a/file1
++++ b/file2
+@@ -1,26 +1,25 @@
+ #include <stdio.h>
+ 
++int fib(int n)
++{
++    if(n > 2)
++    {
++        return fib(n-1) + fib(n-2);
++    }
++    return 1;
++}
++
+ // Frobs foo heartily
+ int frobnitz(int foo)
+ {
+     int i;
+     for(i = 0; i < 10; i++)
+     {
+-        printf("Your answer is: ");
+         printf("%d\n", foo);
+     }
+ }
+ 
+-int fact(int n)
+-{
+-    if(n > 1)
+-    {
+-        return fact(n-1) * n;
+-    }
+-    return 1;
+-}
+-
+ int main(int argc, char **argv)
+ {
+-    frobnitz(fact(10));
++    frobnitz(fib(10));
+ }
+EOF
+
+	STRATEGY=$1
+
+	test_expect_success "$STRATEGY diff" '
+		test_must_fail git diff --no-index "--$STRATEGY" file1 file2 > output &&
+		test_cmp expect output
+	'
+
+	test_expect_success "$STRATEGY diff output is valid" '
+		mv file2 expect &&
+		git apply < output &&
+		test_cmp expect file2
+	'
+}
+
+test_diff_unique() {
+	cat >uniq1 <<\EOF
+1
+2
+3
+4
+5
+6
+EOF
+
+	cat >uniq2 <<\EOF
+a
+b
+c
+d
+e
+f
+EOF
+
+	cat >expect <<\EOF
+diff --git a/uniq1 b/uniq2
+index b414108..0fdf397 100644
+--- a/uniq1
++++ b/uniq2
+@@ -1,6 +1,6 @@
+-1
+-2
+-3
+-4
+-5
+-6
++a
++b
++c
++d
++e
++f
+EOF
+
+	STRATEGY=$1
+
+	test_expect_success 'completely different files' '
+		test_must_fail git diff --no-index "--$STRATEGY" uniq1 uniq2 > output &&
+		test_cmp expect output
+	'
+}
+
diff --git a/t/lib-gettext.sh b/t/lib-gettext.sh
new file mode 100644
index 0000000..0f76f6c
--- /dev/null
+++ b/t/lib-gettext.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Ævar Arnfjörð Bjarmason
+#
+
+. ./test-lib.sh
+
+GIT_TEXTDOMAINDIR="$GIT_BUILD_DIR/po/build/locale"
+GIT_PO_PATH="$GIT_BUILD_DIR/po"
+export GIT_TEXTDOMAINDIR GIT_PO_PATH
+
+. "$GIT_BUILD_DIR"/git-sh-i18n
+
+if test_have_prereq GETTEXT && ! test_have_prereq GETTEXT_POISON
+then
+	# is_IS.UTF-8 on Solaris and FreeBSD, is_IS.utf8 on Debian
+	is_IS_locale=$(locale -a | sed -n '/^is_IS\.[uU][tT][fF]-*8$/{
+		p
+		q
+	}')
+	# is_IS.ISO8859-1 on Solaris and FreeBSD, is_IS.iso88591 on Debian
+	is_IS_iso_locale=$(locale -a | sed -n '/^is_IS\.[iI][sS][oO]8859-*1$/{
+		p
+		q
+	}')
+
+	# Export them as an environment variable so the t0202/test.pl Perl
+	# test can use it too
+	export is_IS_locale is_IS_iso_locale
+
+	if test -n "$is_IS_locale" &&
+		test $GIT_INTERNAL_GETTEXT_SH_SCHEME != "fallthrough"
+	then
+		# Some of the tests need the reference Icelandic locale
+		test_set_prereq GETTEXT_LOCALE
+
+		# Exporting for t0202/test.pl
+		GETTEXT_LOCALE=1
+		export GETTEXT_LOCALE
+		say "# lib-gettext: Found '$is_IS_locale' as an is_IS UTF-8 locale"
+	else
+		say "# lib-gettext: No is_IS UTF-8 locale available"
+	fi
+
+	if test -n "$is_IS_iso_locale" &&
+		test $GIT_INTERNAL_GETTEXT_SH_SCHEME != "fallthrough"
+	then
+		# Some of the tests need the reference Icelandic locale
+		test_set_prereq GETTEXT_ISO_LOCALE
+
+		say "# lib-gettext: Found '$is_IS_iso_locale' as an is_IS ISO-8859-1 locale"
+	else
+		say "# lib-gettext: No is_IS ISO-8859-1 locale available"
+	fi
+fi
diff --git a/t/lib-git-daemon.sh b/t/lib-git-daemon.sh
new file mode 100644
index 0000000..87f0ad8
--- /dev/null
+++ b/t/lib-git-daemon.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+if test -z "$GIT_TEST_GIT_DAEMON"
+then
+	skip_all="git-daemon testing disabled (define GIT_TEST_GIT_DAEMON to enable)"
+	test_done
+fi
+
+LIB_GIT_DAEMON_PORT=${LIB_GIT_DAEMON_PORT-'8121'}
+
+GIT_DAEMON_PID=
+GIT_DAEMON_DOCUMENT_ROOT_PATH="$PWD"/repo
+GIT_DAEMON_URL=git://127.0.0.1:$LIB_GIT_DAEMON_PORT
+
+start_git_daemon() {
+	if test -n "$GIT_DAEMON_PID"
+	then
+		error "start_git_daemon already called"
+	fi
+
+	mkdir -p "$GIT_DAEMON_DOCUMENT_ROOT_PATH"
+
+	trap 'code=$?; stop_git_daemon; (exit $code); die' EXIT
+
+	say >&3 "Starting git daemon ..."
+	mkfifo git_daemon_output
+	git daemon --listen=127.0.0.1 --port="$LIB_GIT_DAEMON_PORT" \
+		--reuseaddr --verbose \
+		--base-path="$GIT_DAEMON_DOCUMENT_ROOT_PATH" \
+		"$@" "$GIT_DAEMON_DOCUMENT_ROOT_PATH" \
+		>&3 2>git_daemon_output &
+	GIT_DAEMON_PID=$!
+	{
+		read line <&7
+		echo >&4 "$line"
+		cat <&7 >&4 &
+	} 7<git_daemon_output &&
+
+	# Check expected output
+	if test x"$(expr "$line" : "\[[0-9]*\] \(.*\)")" != x"Ready to rumble"
+	then
+		kill "$GIT_DAEMON_PID"
+		wait "$GIT_DAEMON_PID"
+		trap 'die' EXIT
+		error "git daemon failed to start"
+	fi
+}
+
+stop_git_daemon() {
+	if test -z "$GIT_DAEMON_PID"
+	then
+		return
+	fi
+
+	trap 'die' EXIT
+
+	# kill git-daemon child of git
+	say >&3 "Stopping git daemon ..."
+	kill "$GIT_DAEMON_PID"
+	wait "$GIT_DAEMON_PID" >&3 2>&4
+	ret=$?
+	# expect exit with status 143 = 128+15 for signal TERM=15
+	if test $ret -ne 143
+	then
+		error "git daemon exited with status: $ret"
+	fi
+	GIT_DAEMON_PID=
+	rm -f git_daemon_output
+}
diff --git a/t/lib-git-p4.sh b/t/lib-git-p4.sh
new file mode 100644
index 0000000..2d753ab
--- /dev/null
+++ b/t/lib-git-p4.sh
@@ -0,0 +1,117 @@
+#
+# Library code for git p4 tests
+#
+
+# p4 tests never use the top-level repo; always build/clone into
+# a subdirectory called "$git"
+TEST_NO_CREATE_REPO=NoThanks
+
+. ./test-lib.sh
+
+if ! test_have_prereq PYTHON; then
+	skip_all='skipping git p4 tests; python not available'
+	test_done
+fi
+( p4 -h && p4d -h ) >/dev/null 2>&1 || {
+	skip_all='skipping git p4 tests; no p4 or p4d'
+	test_done
+}
+
+# Try to pick a unique port: guess a large number, then hope
+# no more than one of each test is running.
+#
+# This does not handle the case where somebody else is running the
+# same tests and has chosen the same ports.
+testid=${this_test#t}
+git_p4_test_start=9800
+P4DPORT=$((10669 + ($testid - $git_p4_test_start)))
+
+export P4PORT=localhost:$P4DPORT
+export P4CLIENT=client
+export P4EDITOR=:
+
+db="$TRASH_DIRECTORY/db"
+cli=$(test-path-utils real_path "$TRASH_DIRECTORY/cli")
+git="$TRASH_DIRECTORY/git"
+pidfile="$TRASH_DIRECTORY/p4d.pid"
+
+start_p4d() {
+	mkdir -p "$db" "$cli" "$git" &&
+	rm -f "$pidfile" &&
+	(
+		p4d -q -r "$db" -p $P4DPORT &
+		echo $! >"$pidfile"
+	) &&
+
+	# This gives p4d a long time to start up, as it can be
+	# quite slow depending on the machine.  Set this environment
+	# variable to something smaller to fail faster in, say,
+	# an automated test setup.  If the p4d process dies, that
+	# will be caught with the "kill -0" check below.
+	i=${P4D_START_PATIENCE:-300}
+	pid=$(cat "$pidfile")
+	ready=
+	while test $i -gt 0
+	do
+		# succeed when p4 client commands start to work
+		if p4 info >/dev/null 2>&1
+		then
+			ready=true
+			break
+		fi
+		# fail if p4d died
+		kill -0 $pid 2>/dev/null || break
+		echo waiting for p4d to start
+		sleep 1
+		i=$(( $i - 1 ))
+	done
+
+	if test -z "$ready"
+	then
+		# p4d failed to start
+		return 1
+	fi
+
+	# build a client
+	(
+		cd "$cli" &&
+		p4 client -i <<-EOF
+		Client: client
+		Description: client
+		Root: $cli
+		View: //depot/... //client/...
+		EOF
+	)
+	return 0
+}
+
+kill_p4d() {
+	pid=$(cat "$pidfile")
+	# it had better exist for the first kill
+	kill $pid &&
+	for i in 1 2 3 4 5 ; do
+		kill $pid >/dev/null 2>&1 || break
+		sleep 1
+	done &&
+	# complain if it would not die
+	test_must_fail kill $pid >/dev/null 2>&1 &&
+	rm -rf "$db" "$cli" "$pidfile"
+}
+
+cleanup_git() {
+	rm -rf "$git" &&
+	mkdir "$git"
+}
+
+marshal_dump() {
+	what=$1 &&
+	line=${2:-1} &&
+	cat >"$TRASH_DIRECTORY/marshal-dump.py" <<-EOF &&
+	import marshal
+	import sys
+	for i in range($line):
+	    d = marshal.load(sys.stdin)
+	print d['$what']
+	EOF
+	"$PYTHON_PATH" "$TRASH_DIRECTORY/marshal-dump.py"
+}
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
index 5654962..199f22c 100644
--- a/t/lib-git-svn.sh
+++ b/t/lib-git-svn.sh
@@ -5,22 +5,22 @@
 
 if test -n "$NO_SVN_TESTS"
 then
-	say 'skipping git svn tests, NO_SVN_TESTS defined'
+	skip_all='skipping git svn tests, NO_SVN_TESTS defined'
 	test_done
 fi
 if ! test_have_prereq PERL; then
-	say 'skipping git svn tests, perl not available'
+	skip_all='skipping git svn tests, perl not available'
 	test_done
 fi
 
 GIT_DIR=$PWD/.git
-GIT_SVN_DIR=$GIT_DIR/svn/git-svn
+GIT_SVN_DIR=$GIT_DIR/svn/refs/remotes/git-svn
 SVN_TREE=$GIT_SVN_DIR/svn-tree
 
 svn >/dev/null 2>&1
 if test $? -ne 1
 then
-    say 'skipping git svn tests, svn not found'
+    skip_all='skipping git svn tests, svn not found'
     test_done
 fi
 
@@ -29,7 +29,7 @@
 svnconf=$PWD/svnconf
 export svnconf
 
-perl -w -e "
+"$PERL_PATH" -w -e "
 use SVN::Core;
 use SVN::Repos;
 \$SVN::Core::VERSION gt '1.1.0' or exit(42);
@@ -39,13 +39,12 @@
 if test $x -ne 0
 then
 	if test $x -eq 42; then
-		err='Perl SVN libraries must be >= 1.1.0'
+		skip_all='Perl SVN libraries must be >= 1.1.0'
 	elif test $x -eq 41; then
-		err='svnadmin failed to create fsfs repository'
+		skip_all='svnadmin failed to create fsfs repository'
 	else
-		err='Perl SVN libraries not found or unusable, skipping test'
+		skip_all='Perl SVN libraries not found or unusable'
 	fi
-	say "$err"
 	test_done
 fi
 
@@ -69,41 +68,46 @@
 	svn "$orig_svncmd" --config-dir "$svnconf" "$@"
 }
 
-for d in \
-	"$SVN_HTTPD_PATH" \
-	/usr/sbin/apache2 \
-	/usr/sbin/httpd \
-; do
-	if test -f "$d"
+prepare_httpd () {
+	for d in \
+		"$SVN_HTTPD_PATH" \
+		/usr/sbin/apache2 \
+		/usr/sbin/httpd \
+	; do
+		if test -f "$d"
+		then
+			SVN_HTTPD_PATH="$d"
+			break
+		fi
+	done
+	if test -z "$SVN_HTTPD_PATH"
 	then
-		SVN_HTTPD_PATH="$d"
-		break
+		echo >&2 '*** error: Apache not found'
+		return 1
 	fi
-done
-for d in \
-	"$SVN_HTTPD_MODULE_PATH" \
-	/usr/lib/apache2/modules \
-	/usr/libexec/apache2 \
-; do
-	if test -d "$d"
+	for d in \
+		"$SVN_HTTPD_MODULE_PATH" \
+		/usr/lib/apache2/modules \
+		/usr/libexec/apache2 \
+	; do
+		if test -d "$d"
+		then
+			SVN_HTTPD_MODULE_PATH="$d"
+			break
+		fi
+	done
+	if test -z "$SVN_HTTPD_MODULE_PATH"
 	then
-		SVN_HTTPD_MODULE_PATH="$d"
-		break
+		echo >&2 '*** error: Apache module dir not found'
+		return 1
 	fi
-done
-
-start_httpd () {
-	repo_base_path="$1"
-	if test -z "$SVN_HTTPD_PORT"
+	if test ! -f "$SVN_HTTPD_MODULE_PATH/mod_dav_svn.so"
 	then
-		echo >&2 'SVN_HTTPD_PORT is not defined!'
-		return
-	fi
-	if test -z "$repo_base_path"
-	then
-		repo_base_path=svn
+		echo >&2 '*** error: Apache module "mod_dav_svn" not found'
+		return 1
 	fi
 
+	repo_base_path="${1-svn}"
 	mkdir "$GIT_DIR"/logs
 
 	cat > "$GIT_DIR/httpd.conf" <<EOF
@@ -120,17 +124,29 @@
 	SVNPath "$rawsvnrepo"
 </Location>
 EOF
+}
+
+start_httpd () {
+	if test -z "$SVN_HTTPD_PORT"
+	then
+		echo >&2 'SVN_HTTPD_PORT is not defined!'
+		return
+	fi
+
+	prepare_httpd "$1" || return 1
+
 	"$SVN_HTTPD_PATH" -f "$GIT_DIR"/httpd.conf -k start
 	svnrepo="http://127.0.0.1:$SVN_HTTPD_PORT/$repo_base_path"
 }
 
 stop_httpd () {
 	test -z "$SVN_HTTPD_PORT" && return
+	test ! -f "$GIT_DIR/httpd.conf" && return
 	"$SVN_HTTPD_PATH" -f "$GIT_DIR"/httpd.conf -k stop
 }
 
 convert_to_rev_db () {
-	perl -w -- - "$@" <<\EOF
+	"$PERL_PATH" -w -- - "$@" <<\EOF
 use strict;
 @ARGV == 2 or die "Usage: convert_to_rev_db <input> <output>";
 open my $wr, '+>', $ARGV[1] or die "$!: couldn't open: $ARGV[1]";
@@ -158,7 +174,7 @@
 require_svnserve () {
     if test -z "$SVNSERVE_PORT"
     then
-        say 'skipping svnserve test. (set $SVNSERVE_PORT to enable)'
+	skip_all='skipping svnserve test. (set $SVNSERVE_PORT to enable)'
         test_done
     fi
 }
diff --git a/t/lib-gpg.sh b/t/lib-gpg.sh
new file mode 100755
index 0000000..05824fa
--- /dev/null
+++ b/t/lib-gpg.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+gpg_version=`gpg --version 2>&1`
+if test $? = 127; then
+	say "You do not seem to have gpg installed"
+else
+	# As said here: http://www.gnupg.org/documentation/faqs.html#q6.19
+	# the gpg version 1.0.6 didn't parse trust packets correctly, so for
+	# that version, creation of signed tags using the generated key fails.
+	case "$gpg_version" in
+	'gpg (GnuPG) 1.0.6'*)
+		say "Your version of gpg (1.0.6) is too buggy for testing"
+		;;
+	*)
+		# key generation info: gpg --homedir t/lib-gpg --gen-key
+		# Type DSA and Elgamal, size 2048 bits, no expiration date.
+		# Name and email: C O Mitter <committer@example.com>
+		# No password given, to enable non-interactive operation.
+		cp -R "$TEST_DIRECTORY"/lib-gpg ./gpghome
+		chmod 0700 gpghome
+		GNUPGHOME="$(pwd)/gpghome"
+		export GNUPGHOME
+		test_set_prereq GPG
+		;;
+	esac
+fi
+
+sanitize_pgp() {
+	perl -ne '
+		/^-----END PGP/ and $in_pgp = 0;
+		print unless $in_pgp;
+		/^-----BEGIN PGP/ and $in_pgp = 1;
+	'
+}
diff --git a/t/t7004/pubring.gpg b/t/lib-gpg/pubring.gpg
similarity index 100%
rename from t/t7004/pubring.gpg
rename to t/lib-gpg/pubring.gpg
Binary files differ
diff --git a/t/t7004/random_seed b/t/lib-gpg/random_seed
similarity index 100%
rename from t/t7004/random_seed
rename to t/lib-gpg/random_seed
Binary files differ
diff --git a/t/t7004/secring.gpg b/t/lib-gpg/secring.gpg
similarity index 100%
rename from t/t7004/secring.gpg
rename to t/lib-gpg/secring.gpg
Binary files differ
diff --git a/t/t7004/trustdb.gpg b/t/lib-gpg/trustdb.gpg
similarity index 100%
rename from t/t7004/trustdb.gpg
rename to t/lib-gpg/trustdb.gpg
Binary files differ
diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh
index 6765b08..094d490 100644
--- a/t/lib-httpd.sh
+++ b/t/lib-httpd.sh
@@ -5,23 +5,35 @@
 
 if test -z "$GIT_TEST_HTTPD"
 then
-	say "skipping test, network testing disabled by default"
-	say "(define GIT_TEST_HTTPD to enable)"
+	skip_all="Network testing disabled (define GIT_TEST_HTTPD to enable)"
 	test_done
 fi
 
 HTTPD_PARA=""
 
+for DEFAULT_HTTPD_PATH in '/usr/sbin/httpd' '/usr/sbin/apache2'
+do
+	if test -x "$DEFAULT_HTTPD_PATH"
+	then
+		break
+	fi
+done
+
+for DEFAULT_HTTPD_MODULE_PATH in '/usr/libexec/apache2' \
+				 '/usr/lib/apache2/modules' \
+				 '/usr/lib64/httpd/modules' \
+				 '/usr/lib/httpd/modules'
+do
+	if test -d "$DEFAULT_HTTPD_MODULE_PATH"
+	then
+		break
+	fi
+done
+
 case $(uname) in
 	Darwin)
-		DEFAULT_HTTPD_PATH='/usr/sbin/httpd'
-		DEFAULT_HTTPD_MODULE_PATH='/usr/libexec/apache2'
 		HTTPD_PARA="$HTTPD_PARA -DDarwin"
 	;;
-	*)
-		DEFAULT_HTTPD_PATH='/usr/sbin/apache2'
-		DEFAULT_HTTPD_MODULE_PATH='/usr/lib/apache2/modules'
-	;;
 esac
 
 LIB_HTTPD_PATH=${LIB_HTTPD_PATH-"$DEFAULT_HTTPD_PATH"}
@@ -33,7 +45,7 @@
 
 if ! test -x "$LIB_HTTPD_PATH"
 then
-	say "skipping test, no web server found at '$LIB_HTTPD_PATH'"
+	skip_all="skipping test, no web server found at '$LIB_HTTPD_PATH'"
 	test_done
 fi
 
@@ -46,7 +58,12 @@
 	then
 		if ! test $HTTPD_VERSION -ge 2
 		then
-			say "skipping test, at least Apache version 2 is required"
+			skip_all="skipping test, at least Apache version 2 is required"
+			test_done
+		fi
+		if ! test -d "$DEFAULT_HTTPD_MODULE_PATH"
+		then
+			skip_all="Apache module directory not found.  Skipping tests."
 			test_done
 		fi
 
@@ -58,12 +75,13 @@
 
 prepare_httpd() {
 	mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH"
+	cp "$TEST_PATH"/passwd "$HTTPD_ROOT_PATH"
 
 	ln -s "$LIB_HTTPD_MODULE_PATH" "$HTTPD_ROOT_PATH/modules"
 
 	if test -n "$LIB_HTTPD_SSL"
 	then
-		HTTPD_URL=https://127.0.0.1:$LIB_HTTPD_PORT
+		HTTPD_PROTO=https
 
 		RANDFILE_PATH="$HTTPD_ROOT_PATH"/.rnd openssl req \
 			-config "$TEST_PATH/ssl.cnf" \
@@ -74,8 +92,12 @@
 		export GIT_SSL_NO_VERIFY
 		HTTPD_PARA="$HTTPD_PARA -DSSL"
 	else
-		HTTPD_URL=http://127.0.0.1:$LIB_HTTPD_PORT
+		HTTPD_PROTO=http
 	fi
+	HTTPD_DEST=127.0.0.1:$LIB_HTTPD_PORT
+	HTTPD_URL=$HTTPD_PROTO://$HTTPD_DEST
+	HTTPD_URL_USER=$HTTPD_PROTO://user%40host@$HTTPD_DEST
+	HTTPD_URL_USER_PASS=$HTTPD_PROTO://user%40host:user%40host@$HTTPD_DEST
 
 	if test -n "$LIB_HTTPD_DAV" -o -n "$LIB_HTTPD_SVN"
 	then
@@ -101,7 +123,7 @@
 		>&3 2>&4
 	if test $? -ne 0
 	then
-		say "skipping test, web server setup failed"
+		skip_all="skipping test, web server setup failed"
 		trap 'die' EXIT
 		test_done
 	fi
@@ -113,3 +135,31 @@
 	"$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \
 		-f "$TEST_PATH/apache.conf" $HTTPD_PARA -k stop
 }
+
+test_http_push_nonff() {
+	REMOTE_REPO=$1
+	LOCAL_REPO=$2
+	BRANCH=$3
+
+	test_expect_success 'non-fast-forward push fails' '
+		cd "$REMOTE_REPO" &&
+		HEAD=$(git rev-parse --verify HEAD) &&
+
+		cd "$LOCAL_REPO" &&
+		git checkout $BRANCH &&
+		echo "changed" > path2 &&
+		git commit -a -m path2 --amend &&
+
+		test_must_fail git push -v origin >output 2>&1 &&
+		(cd "$REMOTE_REPO" &&
+		 test $HEAD = $(git rev-parse --verify HEAD))
+	'
+
+	test_expect_success 'non-fast-forward push show ref status' '
+		grep "^ ! \[rejected\][ ]*$BRANCH -> $BRANCH (non-fast-forward)$" output
+	'
+
+	test_expect_success 'non-fast-forward push shows help message' '
+		test_i18ngrep "Updates were rejected because" output
+	'
+}
diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf
index 21aa42f..de3762e 100644
--- a/t/lib-httpd/apache.conf
+++ b/t/lib-httpd/apache.conf
@@ -8,6 +8,69 @@
 <IfModule !mod_log_config.c>
 	LoadModule log_config_module modules/mod_log_config.so
 </IfModule>
+<IfModule !mod_alias.c>
+	LoadModule alias_module modules/mod_alias.so
+</IfModule>
+<IfModule !mod_cgi.c>
+	LoadModule cgi_module modules/mod_cgi.so
+</IfModule>
+<IfModule !mod_env.c>
+	LoadModule env_module modules/mod_env.so
+</IfModule>
+<IfModule !mod_rewrite.c>
+	LoadModule rewrite_module modules/mod_rewrite.so
+</IFModule>
+<IfModule !mod_version.c>
+	LoadModule version_module modules/mod_version.so
+</IfModule>
+
+<IfVersion < 2.1>
+<IfModule !mod_auth.c>
+	LoadModule auth_module modules/mod_auth.so
+</IfModule>
+</IfVersion>
+
+<IfVersion >= 2.1>
+<IfModule !mod_auth_basic.c>
+	LoadModule auth_basic_module modules/mod_auth_basic.so
+</IfModule>
+<IfModule !mod_authn_file.c>
+	LoadModule authn_file_module modules/mod_authn_file.so
+</IfModule>
+<IfModule !mod_authz_user.c>
+	LoadModule authz_user_module modules/mod_authz_user.so
+</IfModule>
+</IfVersion>
+
+Alias /dumb/ www/
+Alias /auth/ www/auth/
+
+<Location /smart/>
+	SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
+	SetEnv GIT_HTTP_EXPORT_ALL
+</Location>
+<Location /smart_noexport/>
+	SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
+</Location>
+<Location /smart_custom_env/>
+	SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
+	SetEnv GIT_HTTP_EXPORT_ALL
+	SetEnv GIT_COMMITTER_NAME "Custom User"
+	SetEnv GIT_COMMITTER_EMAIL custom@example.com
+</Location>
+ScriptAlias /smart/ ${GIT_EXEC_PATH}/git-http-backend/
+ScriptAlias /smart_noexport/ ${GIT_EXEC_PATH}/git-http-backend/
+ScriptAlias /smart_custom_env/ ${GIT_EXEC_PATH}/git-http-backend/
+<Directory ${GIT_EXEC_PATH}>
+	Options None
+</Directory>
+<Files ${GIT_EXEC_PATH}/git-http-backend>
+	Options ExecCGI
+</Files>
+
+RewriteEngine on
+RewriteRule ^/smart-redir-perm/(.*)$ /smart/$1 [R=301]
+RewriteRule ^/smart-redir-temp/(.*)$ /smart/$1 [R=302]
 
 <IfDefine SSL>
 LoadModule ssl_module modules/mod_ssl.so
@@ -21,12 +84,22 @@
 SSLEngine On
 </IfDefine>
 
+<Location /auth/>
+	AuthType Basic
+	AuthName "git-auth"
+	AuthUserFile passwd
+	Require valid-user
+</Location>
+
 <IfDefine DAV>
 	LoadModule dav_module modules/mod_dav.so
 	LoadModule dav_fs_module modules/mod_dav_fs.so
 
 	DAVLockDB DAVLock
-	<Location />
+	<Location /dumb/>
+		Dav on
+	</Location>
+	<Location /auth/dumb>
 		Dav on
 	</Location>
 </IfDefine>
diff --git a/t/lib-httpd/passwd b/t/lib-httpd/passwd
new file mode 100644
index 0000000..f2fbcad
--- /dev/null
+++ b/t/lib-httpd/passwd
@@ -0,0 +1 @@
+user@host:nKpa8pZUHx/ic
diff --git a/t/lib-pager.sh b/t/lib-pager.sh
new file mode 100644
index 0000000..ba03eab
--- /dev/null
+++ b/t/lib-pager.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+test_expect_success 'determine default pager' '
+	test_might_fail git config --unset core.pager &&
+	less=$(
+		unset PAGER GIT_PAGER;
+		git var GIT_PAGER
+	) &&
+	test -n "$less"
+'
+
+if expr "$less" : '[a-z][a-z]*$' >/dev/null
+then
+	test_set_prereq SIMPLEPAGER
+fi
diff --git a/t/lib-patch-mode.sh b/t/lib-patch-mode.sh
new file mode 100644
index 0000000..06c3c91
--- /dev/null
+++ b/t/lib-patch-mode.sh
@@ -0,0 +1,38 @@
+: included from t2016 and others
+
+. ./test-lib.sh
+
+set_state () {
+	echo "$3" > "$1" &&
+	git add "$1" &&
+	echo "$2" > "$1"
+}
+
+save_state () {
+	noslash="$(echo "$1" | tr / _)" &&
+	cat "$1" > _worktree_"$noslash" &&
+	git show :"$1" > _index_"$noslash"
+}
+
+set_and_save_state () {
+	set_state "$@" &&
+	save_state "$1"
+}
+
+verify_state () {
+	test "$(cat "$1")" = "$2" &&
+	test "$(git show :"$1")" = "$3"
+}
+
+verify_saved_state () {
+	noslash="$(echo "$1" | tr / _)" &&
+	verify_state "$1" "$(cat _worktree_"$noslash")" "$(cat _index_"$noslash")"
+}
+
+save_head () {
+	git rev-parse HEAD > _head
+}
+
+verify_saved_head () {
+	test "$(cat _head)" = "$(git rev-parse HEAD)"
+}
diff --git a/t/lib-prereq-FILEMODE.sh b/t/lib-prereq-FILEMODE.sh
new file mode 100644
index 0000000..bce5a4c
--- /dev/null
+++ b/t/lib-prereq-FILEMODE.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Ævar Arnfjörð Bjarmason
+#
+
+if test "$(git config --bool core.filemode)" = false
+then
+	say 'filemode disabled on the filesystem'
+else
+	test_set_prereq FILEMODE
+fi
diff --git a/t/lib-read-tree.sh b/t/lib-read-tree.sh
new file mode 100644
index 0000000..abc2c6f
--- /dev/null
+++ b/t/lib-read-tree.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+#
+# Helper functions to check if read-tree would succeed/fail as expected with
+# and without the dry-run option. They also test that the dry-run does not
+# write the index and that together with -u it doesn't touch the work tree.
+#
+read_tree_must_succeed () {
+    git ls-files -s >pre-dry-run &&
+    git read-tree -n "$@" &&
+    git ls-files -s >post-dry-run &&
+    test_cmp pre-dry-run post-dry-run &&
+    git read-tree "$@"
+}
+
+read_tree_must_fail () {
+    git ls-files -s >pre-dry-run &&
+    test_must_fail git read-tree -n "$@" &&
+    git ls-files -s >post-dry-run &&
+    test_cmp pre-dry-run post-dry-run &&
+    test_must_fail git read-tree "$@"
+}
+
+read_tree_u_must_succeed () {
+    git ls-files -s >pre-dry-run &&
+    git diff-files -p >pre-dry-run-wt &&
+    git read-tree -n "$@" &&
+    git ls-files -s >post-dry-run &&
+    git diff-files -p >post-dry-run-wt &&
+    test_cmp pre-dry-run post-dry-run &&
+    test_cmp pre-dry-run-wt post-dry-run-wt &&
+    git read-tree "$@"
+}
+
+read_tree_u_must_fail () {
+    git ls-files -s >pre-dry-run &&
+    git diff-files -p >pre-dry-run-wt &&
+    test_must_fail git read-tree -n "$@" &&
+    git ls-files -s >post-dry-run &&
+    git diff-files -p >post-dry-run-wt &&
+    test_cmp pre-dry-run post-dry-run &&
+    test_cmp pre-dry-run-wt post-dry-run-wt &&
+    test_must_fail git read-tree "$@"
+}
diff --git a/t/lib-rebase.sh b/t/lib-rebase.sh
index 260a231..6ccf797 100644
--- a/t/lib-rebase.sh
+++ b/t/lib-rebase.sh
@@ -2,21 +2,33 @@
 
 # After setting the fake editor with this function, you can
 #
-# - override the commit message with $FAKE_COMMIT_MESSAGE,
+# - override the commit message with $FAKE_COMMIT_MESSAGE
 # - amend the commit message with $FAKE_COMMIT_AMEND
 # - check that non-commit messages have a certain line count with $EXPECT_COUNT
-# - rewrite a rebase -i script with $FAKE_LINES in the form
+# - check the commit count in the commit message header with $EXPECT_HEADER_COUNT
+# - rewrite a rebase -i script as directed by $FAKE_LINES.
+#   $FAKE_LINES consists of a sequence of words separated by spaces.
+#   The following word combinations are possible:
 #
-#	"[<lineno1>] [<lineno2>]..."
+#   "<lineno>" -- add a "pick" line with the SHA1 taken from the
+#       specified line.
 #
-#   If a line number is prefixed with "squash" or "edit", the respective line's
-#   command will be replaced with the specified one.
+#   "<cmd> <lineno>" -- add a line with the specified command
+#       ("squash", "fixup", "edit", or "reword") and the SHA1 taken
+#       from the specified line.
+#
+#   "#" -- Add a comment line.
+#
+#   ">" -- Add a blank line.
 
 set_fake_editor () {
 	echo "#!$SHELL_PATH" >fake-editor.sh
 	cat >> fake-editor.sh <<\EOF
 case "$1" in
 */COMMIT_EDITMSG)
+	test -z "$EXPECT_HEADER_COUNT" ||
+		test "$EXPECT_HEADER_COUNT" = "$(sed -n '1s/^# This is a combination of \(.*\) commits\./\1/p' < "$1")" ||
+		exit
 	test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1"
 	test -z "$FAKE_COMMIT_AMEND" || echo "$FAKE_COMMIT_AMEND" >> "$1"
 	exit
@@ -28,19 +40,26 @@
 test -z "$FAKE_LINES" && exit
 grep -v '^#' < "$1" > "$1".tmp
 rm -f "$1"
+echo 'rebase -i script before editing:'
 cat "$1".tmp
 action=pick
 for line in $FAKE_LINES; do
 	case $line in
-	squash|edit)
+	squash|fixup|edit|reword)
 		action="$line";;
+	exec*)
+		echo "$line" | sed 's/_/ /g' >> "$1";;
+	"#")
+		echo '# comment' >> "$1";;
+	">")
+		echo >> "$1";;
 	*)
-		echo sed -n "${line}s/^pick/$action/p"
-		sed -n "${line}p" < "$1".tmp
 		sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1"
 		action=pick;;
 	esac
 done
+echo 'rebase -i script after editing:'
+cat "$1"
 EOF
 
 	test_set_editor "$(pwd)/fake-editor.sh"
diff --git a/t/lib-t6000.sh b/t/lib-t6000.sh
new file mode 100644
index 0000000..ea25dd8
--- /dev/null
+++ b/t/lib-t6000.sh
@@ -0,0 +1,127 @@
+: included from 6002 and others
+
+[ -d .git/refs/tags ] || mkdir -p .git/refs/tags
+
+:> sed.script
+
+# Answer the sha1 has associated with the tag. The tag must exist in .git or .git/refs/tags
+tag()
+{
+	_tag=$1
+	[ -f .git/refs/tags/$_tag ] || error "tag: \"$_tag\" does not exist"
+	cat .git/refs/tags/$_tag
+}
+
+# Generate a commit using the text specified to make it unique and the tree
+# named by the tag specified.
+unique_commit()
+{
+	_text=$1
+        _tree=$2
+	shift 2
+	echo $_text | git commit-tree $(tag $_tree) "$@"
+}
+
+# Save the output of a command into the tag specified. Prepend
+# a substitution script for the tag onto the front of sed.script
+save_tag()
+{
+	_tag=$1
+	[ -n "$_tag" ] || error "usage: save_tag tag commit-args ..."
+	shift 1
+	"$@" >.git/refs/tags/$_tag
+
+        echo "s/$(tag $_tag)/$_tag/g" > sed.script.tmp
+	cat sed.script >> sed.script.tmp
+	rm sed.script
+	mv sed.script.tmp sed.script
+}
+
+# Replace unhelpful sha1 hashses with their symbolic equivalents
+entag()
+{
+	sed -f sed.script
+}
+
+# Execute a command after first saving, then setting the GIT_AUTHOR_EMAIL
+# tag to a specified value. Restore the original value on return.
+as_author()
+{
+	_author=$1
+	shift 1
+        _save=$GIT_AUTHOR_EMAIL
+
+	GIT_AUTHOR_EMAIL="$_author"
+	export GIT_AUTHOR_EMAIL
+	"$@"
+	if test -z "$_save"
+	then
+		unset GIT_AUTHOR_EMAIL
+	else
+		GIT_AUTHOR_EMAIL="$_save"
+		export GIT_AUTHOR_EMAIL
+	fi
+}
+
+commit_date()
+{
+        _commit=$1
+	git cat-file commit $_commit | sed -n "s/^committer .*> \([0-9]*\) .*/\1/p"
+}
+
+on_committer_date()
+{
+    _date=$1
+    shift 1
+    GIT_COMMITTER_DATE="$_date"
+    export GIT_COMMITTER_DATE
+    "$@"
+    unset GIT_COMMITTER_DATE
+}
+
+# Execute a command and suppress any error output.
+hide_error()
+{
+	"$@" 2>/dev/null
+}
+
+check_output()
+{
+	_name=$1
+	shift 1
+	if eval "$*" | entag > $_name.actual
+	then
+		test_cmp $_name.expected $_name.actual
+	else
+		return 1;
+	fi
+}
+
+# Turn a reasonable test description into a reasonable test name.
+# All alphanums translated into -'s which are then compressed and stripped
+# from front and back.
+name_from_description()
+{
+	perl -pe '
+		s/[^A-Za-z0-9.]/-/g;
+		s/-+/-/g;
+		s/-$//;
+		s/^-//;
+		y/A-Z/a-z/;
+	'
+}
+
+
+# Execute the test described by the first argument, by eval'ing
+# command line specified in the 2nd argument. Check the status code
+# is zero and that the output matches the stream read from
+# stdin.
+test_output_expect_success()
+{
+	_description=$1
+        _test=$2
+        [ $# -eq 2 ] || error "usage: test_output_expect_success description test <<EOF ... EOF"
+        _name=$(echo $_description | name_from_description)
+	cat > $_name.expected
+	test_expect_success "$_description" "check_output $_name \"$_test\""
+}
diff --git a/t/lib-terminal.sh b/t/lib-terminal.sh
new file mode 100644
index 0000000..58d911d
--- /dev/null
+++ b/t/lib-terminal.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+test_expect_success PERL 'set up terminal for tests' '
+	# Reading from the pty master seems to get stuck _sometimes_
+	# on Mac OS X 10.5.0, using Perl 5.10.0 or 5.8.9.
+	#
+	# Reproduction recipe: run
+	#
+	#	i=0
+	#	while ./test-terminal.perl echo hi $i
+	#	do
+	#		: $((i = $i + 1))
+	#	done
+	#
+	# After 2000 iterations or so it hangs.
+	# https://rt.cpan.org/Ticket/Display.html?id=65692
+	#
+	if test "$(uname -s)" = Darwin
+	then
+		:
+	elif
+		"$PERL_PATH" "$TEST_DIRECTORY"/test-terminal.perl \
+			sh -c "test -t 1 && test -t 2"
+	then
+		test_set_prereq TTY &&
+		test_terminal () {
+			if ! test_declared_prereq TTY
+			then
+				echo >&4 "test_terminal: need to declare TTY prerequisite"
+				return 127
+			fi
+			"$PERL_PATH" "$TEST_DIRECTORY"/test-terminal.perl "$@"
+		}
+	fi
+'
diff --git a/t/perf/.gitignore b/t/perf/.gitignore
new file mode 100644
index 0000000..50f5cc1
--- /dev/null
+++ b/t/perf/.gitignore
@@ -0,0 +1,2 @@
+build/
+test-results/
diff --git a/t/perf/Makefile b/t/perf/Makefile
new file mode 100644
index 0000000..8c47155
--- /dev/null
+++ b/t/perf/Makefile
@@ -0,0 +1,15 @@
+-include ../../config.mak
+export GIT_TEST_OPTIONS
+
+all: perf
+
+perf: pre-clean
+	./run
+
+pre-clean:
+	rm -rf test-results
+
+clean:
+	rm -rf build "trash directory".* test-results
+
+.PHONY: all perf pre-clean clean
diff --git a/t/perf/README b/t/perf/README
new file mode 100644
index 0000000..b2dbad4
--- /dev/null
+++ b/t/perf/README
@@ -0,0 +1,146 @@
+Git performance tests
+=====================
+
+This directory holds performance testing scripts for git tools.  The
+first part of this document describes the various ways in which you
+can run them.
+
+When fixing the tools or adding enhancements, you are strongly
+encouraged to add tests in this directory to cover what you are
+trying to fix or enhance.  The later part of this short document
+describes how your test scripts should be organized.
+
+
+Running Tests
+-------------
+
+The easiest way to run tests is to say "make".  This runs all
+the tests on the current git repository.
+
+    === Running 2 tests in this tree ===
+    [...]
+    Test                                     this tree
+    ---------------------------------------------------------
+    0001.1: rev-list --all                   0.54(0.51+0.02)
+    0001.2: rev-list --all --objects         6.14(5.99+0.11)
+    7810.1: grep worktree, cheap regex       0.16(0.16+0.35)
+    7810.2: grep worktree, expensive regex   7.90(29.75+0.37)
+    7810.3: grep --cached, cheap regex       3.07(3.02+0.25)
+    7810.4: grep --cached, expensive regex   9.39(30.57+0.24)
+
+You can compare multiple repositories and even git revisions with the
+'run' script:
+
+    $ ./run . origin/next /path/to/git-tree p0001-rev-list.sh
+
+where . stands for the current git tree.  The full invocation is
+
+    ./run [<revision|directory>...] [--] [<test-script>...]
+
+A '.' argument is implied if you do not pass any other
+revisions/directories.
+
+You can also manually test this or another git build tree, and then
+call the aggregation script to summarize the results:
+
+    $ ./p0001-rev-list.sh
+    [...]
+    $ GIT_BUILD_DIR=/path/to/other/git ./p0001-rev-list.sh
+    [...]
+    $ ./aggregate.perl . /path/to/other/git ./p0001-rev-list.sh
+
+aggregate.perl has the same invocation as 'run', it just does not run
+anything beforehand.
+
+You can set the following variables (also in your config.mak):
+
+    GIT_PERF_REPEAT_COUNT
+	Number of times a test should be repeated for best-of-N
+	measurements.  Defaults to 5.
+
+    GIT_PERF_MAKE_OPTS
+	Options to use when automatically building a git tree for
+	performance testing.  E.g., -j6 would be useful.
+
+    GIT_PERF_REPO
+    GIT_PERF_LARGE_REPO
+	Repositories to copy for the performance tests.  The normal
+	repo should be at least git.git size.  The large repo should
+	probably be about linux-2.6.git size for optimal results.
+	Both default to the git.git you are running from.
+
+You can also pass the options taken by ordinary git tests; the most
+useful one is:
+
+--root=<directory>::
+	Create "trash" directories used to store all temporary data during
+	testing under <directory>, instead of the t/ directory.
+	Using this option with a RAM-based filesystem (such as tmpfs)
+	can massively speed up the test suite.
+
+
+Naming Tests
+------------
+
+The performance test files are named as:
+
+	pNNNN-commandname-details.sh
+
+where N is a decimal digit.  The same conventions for choosing NNNN as
+for normal tests apply.
+
+
+Writing Tests
+-------------
+
+The perf script starts much like a normal test script, except it
+sources perf-lib.sh:
+
+	#!/bin/sh
+	#
+	# Copyright (c) 2005 Junio C Hamano
+	#
+
+	test_description='xxx performance test'
+	. ./perf-lib.sh
+
+After that you will want to use some of the following:
+
+	test_perf_default_repo  # sets up a "normal" repository
+	test_perf_large_repo    # sets up a "large" repository
+
+	test_perf_default_repo sub  # ditto, in a subdir "sub"
+
+        test_checkout_worktree  # if you need the worktree too
+
+At least one of the first two is required!
+
+You can use test_expect_success as usual.  For actual performance
+tests, use
+
+	test_perf 'descriptive string' '
+		command1 &&
+		command2
+	'
+
+test_perf spawns a subshell, for lack of better options.  This means
+that
+
+* you _must_ export all variables that you need in the subshell
+
+* you _must_ flag all variables that you want to persist from the
+  subshell with 'test_export':
+
+	test_perf 'descriptive string' '
+		foo=$(git rev-parse HEAD) &&
+		test_export foo
+	'
+
+  The so-exported variables are automatically marked for export in the
+  shell executing the perf test.  For your convenience, test_export is
+  the same as export in the main shell.
+
+  This feature relies on a bit of magic using 'set' and 'source'.
+  While we have tried to make sure that it can cope with embedded
+  whitespace and other special characters, it will not work with
+  multi-line data.
diff --git a/t/perf/aggregate.perl b/t/perf/aggregate.perl
new file mode 100755
index 0000000..15f7fc1
--- /dev/null
+++ b/t/perf/aggregate.perl
@@ -0,0 +1,166 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use Git;
+
+sub get_times {
+	my $name = shift;
+	open my $fh, "<", $name or return undef;
+	my $line = <$fh>;
+	return undef if not defined $line;
+	close $fh or die "cannot close $name: $!";
+	$line =~ /^(?:(\d+):)?(\d+):(\d+(?:\.\d+)?) (\d+(?:\.\d+)?) (\d+(?:\.\d+)?)$/
+		or die "bad input line: $line";
+	my $rt = ((defined $1 ? $1 : 0.0)*60+$2)*60+$3;
+	return ($rt, $4, $5);
+}
+
+sub format_times {
+	my ($r, $u, $s, $firstr) = @_;
+	if (!defined $r) {
+		return "<missing>";
+	}
+	my $out = sprintf "%.2f(%.2f+%.2f)", $r, $u, $s;
+	if (defined $firstr) {
+		if ($firstr > 0) {
+			$out .= sprintf " %+.1f%%", 100.0*($r-$firstr)/$firstr;
+		} elsif ($r == 0) {
+			$out .= " =";
+		} else {
+			$out .= " +inf";
+		}
+	}
+	return $out;
+}
+
+my (@dirs, %dirnames, %dirabbrevs, %prefixes, @tests);
+while (scalar @ARGV) {
+	my $arg = $ARGV[0];
+	my $dir;
+	last if -f $arg or $arg eq "--";
+	if (! -d $arg) {
+		my $rev = Git::command_oneline(qw(rev-parse --verify), $arg);
+		$dir = "build/".$rev;
+	} else {
+		$arg =~ s{/*$}{};
+		$dir = $arg;
+		$dirabbrevs{$dir} = $dir;
+	}
+	push @dirs, $dir;
+	$dirnames{$dir} = $arg;
+	my $prefix = $dir;
+	$prefix =~ tr/^a-zA-Z0-9/_/c;
+	$prefixes{$dir} = $prefix . '.';
+	shift @ARGV;
+}
+
+if (not @dirs) {
+	@dirs = ('.');
+}
+$dirnames{'.'} = $dirabbrevs{'.'} = "this tree";
+$prefixes{'.'} = '';
+
+shift @ARGV if scalar @ARGV and $ARGV[0] eq "--";
+
+@tests = @ARGV;
+if (not @tests) {
+	@tests = glob "p????-*.sh";
+}
+
+my @subtests;
+my %shorttests;
+for my $t (@tests) {
+	$t =~ s{(?:.*/)?(p(\d+)-[^/]+)\.sh$}{$1} or die "bad test name: $t";
+	my $n = $2;
+	my $fname = "test-results/$t.subtests";
+	open my $fp, "<", $fname or die "cannot open $fname: $!";
+	for (<$fp>) {
+		chomp;
+		/^(\d+)$/ or die "malformed subtest line: $_";
+		push @subtests, "$t.$1";
+		$shorttests{"$t.$1"} = "$n.$1";
+	}
+	close $fp or die "cannot close $fname: $!";
+}
+
+sub read_descr {
+	my $name = shift;
+	open my $fh, "<", $name or return "<error reading description>";
+	my $line = <$fh>;
+	close $fh or die "cannot close $name";
+	chomp $line;
+	return $line;
+}
+
+my %descrs;
+my $descrlen = 4; # "Test"
+for my $t (@subtests) {
+	$descrs{$t} = $shorttests{$t}.": ".read_descr("test-results/$t.descr");
+	$descrlen = length $descrs{$t} if length $descrs{$t}>$descrlen;
+}
+
+sub have_duplicate {
+	my %seen;
+	for (@_) {
+		return 1 if exists $seen{$_};
+		$seen{$_} = 1;
+	}
+	return 0;
+}
+sub have_slash {
+	for (@_) {
+		return 1 if m{/};
+	}
+	return 0;
+}
+
+my %newdirabbrevs = %dirabbrevs;
+while (!have_duplicate(values %newdirabbrevs)) {
+	%dirabbrevs = %newdirabbrevs;
+	last if !have_slash(values %dirabbrevs);
+	%newdirabbrevs = %dirabbrevs;
+	for (values %newdirabbrevs) {
+		s{^[^/]*/}{};
+	}
+}
+
+my %times;
+my @colwidth = ((0)x@dirs);
+for my $i (0..$#dirs) {
+	my $d = $dirs[$i];
+	my $w = length (exists $dirabbrevs{$d} ? $dirabbrevs{$d} : $dirnames{$d});
+	$colwidth[$i] = $w if $w > $colwidth[$i];
+}
+for my $t (@subtests) {
+	my $firstr;
+	for my $i (0..$#dirs) {
+		my $d = $dirs[$i];
+		$times{$prefixes{$d}.$t} = [get_times("test-results/$prefixes{$d}$t.times")];
+		my ($r,$u,$s) = @{$times{$prefixes{$d}.$t}};
+		my $w = length format_times($r,$u,$s,$firstr);
+		$colwidth[$i] = $w if $w > $colwidth[$i];
+		$firstr = $r unless defined $firstr;
+	}
+}
+my $totalwidth = 3*@dirs+$descrlen;
+$totalwidth += $_ for (@colwidth);
+
+printf "%-${descrlen}s", "Test";
+for my $i (0..$#dirs) {
+	my $d = $dirs[$i];
+	printf "   %-$colwidth[$i]s", (exists $dirabbrevs{$d} ? $dirabbrevs{$d} : $dirnames{$d});
+}
+print "\n";
+print "-"x$totalwidth, "\n";
+for my $t (@subtests) {
+	printf "%-${descrlen}s", $descrs{$t};
+	my $firstr;
+	for my $i (0..$#dirs) {
+		my $d = $dirs[$i];
+		my ($r,$u,$s) = @{$times{$prefixes{$d}.$t}};
+		printf "   %-$colwidth[$i]s", format_times($r,$u,$s,$firstr);
+		$firstr = $r unless defined $firstr;
+	}
+	print "\n";
+}
diff --git a/t/perf/min_time.perl b/t/perf/min_time.perl
new file mode 100755
index 0000000..c1a2717
--- /dev/null
+++ b/t/perf/min_time.perl
@@ -0,0 +1,21 @@
+#!/usr/bin/perl
+
+my $minrt = 1e100;
+my $min;
+
+while (<>) {
+	# [h:]m:s.xx U.xx S.xx
+	/^(?:(\d+):)?(\d+):(\d+(?:\.\d+)?) (\d+(?:\.\d+)?) (\d+(?:\.\d+)?)$/
+		or die "bad input line: $_";
+	my $rt = ((defined $1 ? $1 : 0.0)*60+$2)*60+$3;
+	if ($rt < $minrt) {
+		$min = $_;
+		$minrt = $rt;
+	}
+}
+
+if (!defined $min) {
+	die "no input found";
+}
+
+print $min;
diff --git a/t/perf/p0000-perf-lib-sanity.sh b/t/perf/p0000-perf-lib-sanity.sh
new file mode 100755
index 0000000..cf8e1ef
--- /dev/null
+++ b/t/perf/p0000-perf-lib-sanity.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+test_description='Tests whether perf-lib facilities work'
+. ./perf-lib.sh
+
+test_perf_default_repo
+
+test_perf 'test_perf_default_repo works' '
+	foo=$(git rev-parse HEAD) &&
+	test_export foo
+'
+
+test_checkout_worktree
+
+test_perf 'test_checkout_worktree works' '
+	wt=$(find . | wc -l) &&
+	idx=$(git ls-files | wc -l) &&
+	test $wt -gt $idx
+'
+
+baz=baz
+test_export baz
+
+test_expect_success 'test_export works' '
+	echo "$foo" &&
+	test "$foo" = "$(git rev-parse HEAD)" &&
+	echo "$baz" &&
+	test "$baz" = baz
+'
+
+test_perf 'export a weird var' '
+	bar="weird # variable" &&
+	test_export bar
+'
+
+test_expect_success 'test_export works with weird vars' '
+	echo "$bar" &&
+	test "$bar" = "weird # variable"
+'
+
+test_perf 'important variables available in subshells' '
+	test -n "$HOME" &&
+	test -n "$TEST_DIRECTORY" &&
+	test -n "$TRASH_DIRECTORY" &&
+	test -n "$GIT_BUILD_DIR"
+'
+
+test_perf 'test-lib-functions correctly loaded in subshells' '
+	: >a &&
+	test_path_is_file a &&
+	: >b &&
+	test_cmp a b
+'
+
+test_done
diff --git a/t/perf/p0001-rev-list.sh b/t/perf/p0001-rev-list.sh
new file mode 100755
index 0000000..4f71a63
--- /dev/null
+++ b/t/perf/p0001-rev-list.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+test_description="Tests history walking performance"
+
+. ./perf-lib.sh
+
+test_perf_default_repo
+
+test_perf 'rev-list --all' '
+	git rev-list --all >/dev/null
+'
+
+test_perf 'rev-list --all --objects' '
+	git rev-list --all --objects >/dev/null
+'
+
+test_done
diff --git a/t/perf/p4000-diff-algorithms.sh b/t/perf/p4000-diff-algorithms.sh
new file mode 100755
index 0000000..7e00c9d
--- /dev/null
+++ b/t/perf/p4000-diff-algorithms.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+test_description="Tests diff generation performance"
+
+. ./perf-lib.sh
+
+test_perf_default_repo
+
+test_perf 'log -3000 (baseline)' '
+	git log -3000 >/dev/null
+'
+
+test_perf 'log --raw -3000 (tree-only)' '
+	git log --raw -3000 >/dev/null
+'
+
+test_perf 'log -p -3000 (Myers)' '
+	git log -p -3000 >/dev/null
+'
+
+test_perf 'log -p -3000 --histogram' '
+	git log -p -3000 --histogram >/dev/null
+'
+
+test_perf 'log -p -3000 --patience' '
+	git log -p -3000 --patience >/dev/null
+'
+
+test_done
diff --git a/t/perf/p5302-pack-index.sh b/t/perf/p5302-pack-index.sh
new file mode 100755
index 0000000..6cb5b0d
--- /dev/null
+++ b/t/perf/p5302-pack-index.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+test_description="Tests index-pack performance"
+
+. ./perf-lib.sh
+
+test_perf_large_repo
+
+test_expect_success 'repack' '
+	git repack -ad &&
+	PACK=`ls .git/objects/pack/*.pack | head -n1` &&
+	test -f "$PACK" &&
+	export PACK
+'
+
+test_perf 'index-pack 0 threads' '
+	GIT_DIR=t1 git index-pack --threads=1 --stdin < $PACK
+'
+
+test_perf 'index-pack 1 thread ' '
+	GIT_DIR=t2 GIT_FORCE_THREADS=1 git index-pack --threads=1 --stdin < $PACK
+'
+
+test_perf 'index-pack 2 threads' '
+	GIT_DIR=t3 git index-pack --threads=2 --stdin < $PACK
+'
+
+test_perf 'index-pack 4 threads' '
+	GIT_DIR=t4 git index-pack --threads=4 --stdin < $PACK
+'
+
+test_perf 'index-pack 8 threads' '
+	GIT_DIR=t5 git index-pack --threads=8 --stdin < $PACK
+'
+
+test_perf 'index-pack default number of threads' '
+	GIT_DIR=t6 git index-pack --stdin < $PACK
+'
+
+test_done
diff --git a/t/perf/p7810-grep.sh b/t/perf/p7810-grep.sh
new file mode 100755
index 0000000..9f4ade6
--- /dev/null
+++ b/t/perf/p7810-grep.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+test_description="git-grep performance in various modes"
+
+. ./perf-lib.sh
+
+test_perf_large_repo
+test_checkout_worktree
+
+test_perf 'grep worktree, cheap regex' '
+	git grep some_nonexistent_string || :
+'
+test_perf 'grep worktree, expensive regex' '
+	git grep "^.* *some_nonexistent_string$" || :
+'
+test_perf 'grep --cached, cheap regex' '
+	git grep --cached some_nonexistent_string || :
+'
+test_perf 'grep --cached, expensive regex' '
+	git grep --cached "^.* *some_nonexistent_string$" || :
+'
+
+test_done
diff --git a/t/perf/perf-lib.sh b/t/perf/perf-lib.sh
new file mode 100644
index 0000000..5580c22
--- /dev/null
+++ b/t/perf/perf-lib.sh
@@ -0,0 +1,202 @@
+#!/bin/sh
+#
+# Copyright (c) 2011 Thomas Rast
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see http://www.gnu.org/licenses/ .
+
+# do the --tee work early; it otherwise confuses our careful
+# GIT_BUILD_DIR mangling
+case "$GIT_TEST_TEE_STARTED, $* " in
+done,*)
+	# do not redirect again
+	;;
+*' --tee '*|*' --va'*)
+	mkdir -p test-results
+	BASE=test-results/$(basename "$0" .sh)
+	(GIT_TEST_TEE_STARTED=done ${SHELL-sh} "$0" "$@" 2>&1;
+	 echo $? > $BASE.exit) | tee $BASE.out
+	test "$(cat $BASE.exit)" = 0
+	exit
+	;;
+esac
+
+TEST_DIRECTORY=$(pwd)/..
+TEST_OUTPUT_DIRECTORY=$(pwd)
+if test -z "$GIT_TEST_INSTALLED"; then
+	perf_results_prefix=
+else
+	perf_results_prefix=$(printf "%s" "${GIT_TEST_INSTALLED%/bin-wrappers}" | tr -c "[a-zA-Z0-9]" "[_*]")"."
+	# make the tested dir absolute
+	GIT_TEST_INSTALLED=$(cd "$GIT_TEST_INSTALLED" && pwd)
+fi
+
+TEST_NO_CREATE_REPO=t
+
+. ../test-lib.sh
+
+# Variables from test-lib that are normally internal to the tests; we
+# need to export them for test_perf subshells
+export TEST_DIRECTORY TRASH_DIRECTORY GIT_BUILD_DIR GIT_TEST_CMP
+
+perf_results_dir=$TEST_OUTPUT_DIRECTORY/test-results
+mkdir -p "$perf_results_dir"
+rm -f "$perf_results_dir"/$(basename "$0" .sh).subtests
+
+if test -z "$GIT_PERF_REPEAT_COUNT"; then
+	GIT_PERF_REPEAT_COUNT=3
+fi
+die_if_build_dir_not_repo () {
+	if ! ( cd "$TEST_DIRECTORY/.." &&
+		    git rev-parse --build-dir >/dev/null 2>&1 ); then
+		error "No $1 defined, and your build directory is not a repo"
+	fi
+}
+
+if test -z "$GIT_PERF_REPO"; then
+	die_if_build_dir_not_repo '$GIT_PERF_REPO'
+	GIT_PERF_REPO=$TEST_DIRECTORY/..
+fi
+if test -z "$GIT_PERF_LARGE_REPO"; then
+	die_if_build_dir_not_repo '$GIT_PERF_LARGE_REPO'
+	GIT_PERF_LARGE_REPO=$TEST_DIRECTORY/..
+fi
+
+test_perf_create_repo_from () {
+	test "$#" = 2 ||
+	error "bug in the test script: not 2 parameters to test-create-repo"
+	repo="$1"
+	source="$2"
+	source_git=$source/$(cd "$source" && git rev-parse --git-dir)
+	mkdir -p "$repo/.git"
+	(
+		cd "$repo/.git" &&
+		{ cp -Rl "$source_git/objects" . 2>/dev/null ||
+			cp -R "$source_git/objects" .; } &&
+		for stuff in "$source_git"/*; do
+			case "$stuff" in
+				*/objects|*/hooks|*/config)
+					;;
+				*)
+					cp -R "$stuff" . || break
+					;;
+			esac
+		done &&
+		cd .. &&
+		git init -q &&
+		mv .git/hooks .git/hooks-disabled 2>/dev/null
+	) || error "failed to copy repository '$source' to '$repo'"
+}
+
+# call at least one of these to establish an appropriately-sized repository
+test_perf_default_repo () {
+	test_perf_create_repo_from "${1:-$TRASH_DIRECTORY}" "$GIT_PERF_REPO"
+}
+test_perf_large_repo () {
+	if test "$GIT_PERF_LARGE_REPO" = "$GIT_BUILD_DIR"; then
+		echo "warning: \$GIT_PERF_LARGE_REPO is \$GIT_BUILD_DIR." >&2
+		echo "warning: This will work, but may not be a sufficiently large repo" >&2
+		echo "warning: for representative measurements." >&2
+	fi
+	test_perf_create_repo_from "${1:-$TRASH_DIRECTORY}" "$GIT_PERF_LARGE_REPO"
+}
+test_checkout_worktree () {
+	git checkout-index -u -a ||
+	error "git checkout-index failed"
+}
+
+# Performance tests should never fail.  If they do, stop immediately
+immediate=t
+
+test_run_perf_ () {
+	test_cleanup=:
+	test_export_="test_cleanup"
+	export test_cleanup test_export_
+	/usr/bin/time -f "%E %U %S" -o test_time.$i "$SHELL" -c '
+. '"$TEST_DIRECTORY"/test-lib-functions.sh'
+test_export () {
+	[ $# != 0 ] || return 0
+	test_export_="$test_export_\\|$1"
+	shift
+	test_export "$@"
+}
+'"$1"'
+ret=$?
+set | sed -n "s'"/'/'\\\\''/g"';s/^\\($test_export_\\)/export '"'&'"'/p" >test_vars
+exit $ret' >&3 2>&4
+	eval_ret=$?
+
+	if test $eval_ret = 0 || test -n "$expecting_failure"
+	then
+		test_eval_ "$test_cleanup"
+		. ./test_vars || error "failed to load updated environment"
+	fi
+	if test "$verbose" = "t" && test -n "$HARNESS_ACTIVE"; then
+		echo ""
+	fi
+	return "$eval_ret"
+}
+
+
+test_perf () {
+	test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
+	test "$#" = 2 ||
+	error "bug in the test script: not 2 or 3 parameters to test-expect-success"
+	export test_prereq
+	if ! test_skip "$@"
+	then
+		base=$(basename "$0" .sh)
+		echo "$test_count" >>"$perf_results_dir"/$base.subtests
+		echo "$1" >"$perf_results_dir"/$base.$test_count.descr
+		if test -z "$verbose"; then
+			echo -n "perf $test_count - $1:"
+		else
+			echo "perf $test_count - $1:"
+		fi
+		for i in $(seq 1 $GIT_PERF_REPEAT_COUNT); do
+			say >&3 "running: $2"
+			if test_run_perf_ "$2"
+			then
+				if test -z "$verbose"; then
+					echo -n " $i"
+				else
+					echo "* timing run $i/$GIT_PERF_REPEAT_COUNT:"
+				fi
+			else
+				test -z "$verbose" && echo
+				test_failure_ "$@"
+				break
+			fi
+		done
+		if test -z "$verbose"; then
+			echo " ok"
+		else
+			test_ok_ "$1"
+		fi
+		base="$perf_results_dir"/"$perf_results_prefix$(basename "$0" .sh)"."$test_count"
+		"$TEST_DIRECTORY"/perf/min_time.perl test_time.* >"$base".times
+	fi
+	echo >&3 ""
+}
+
+# We extend test_done to print timings at the end (./run disables this
+# and does it after running everything)
+test_at_end_hook_ () {
+	if test -z "$GIT_PERF_AGGREGATING_LATER"; then
+		( cd "$TEST_DIRECTORY"/perf && ./aggregate.perl $(basename "$0") )
+	fi
+}
+
+test_export () {
+	export "$@"
+}
diff --git a/t/perf/run b/t/perf/run
new file mode 100755
index 0000000..cfd7012
--- /dev/null
+++ b/t/perf/run
@@ -0,0 +1,82 @@
+#!/bin/sh
+
+case "$1" in
+	--help)
+		echo "usage: $0 [other_git_tree...] [--] [test_scripts]"
+		exit 0
+		;;
+esac
+
+die () {
+	echo >&2 "error: $*"
+	exit 1
+}
+
+run_one_dir () {
+	if test $# -eq 0; then
+		set -- p????-*.sh
+	fi
+	echo "=== Running $# tests in ${GIT_TEST_INSTALLED:-this tree} ==="
+	for t in "$@"; do
+		./$t $GIT_TEST_OPTS
+	done
+}
+
+unpack_git_rev () {
+	rev=$1
+	mkdir -p build/$rev
+	(cd "$(git rev-parse --show-cdup)" && git archive --format=tar $rev) |
+	(cd build/$rev && tar x)
+}
+build_git_rev () {
+	rev=$1
+	cp ../../config.mak build/$rev/config.mak
+	(cd build/$rev && make $GIT_PERF_MAKE_OPTS) ||
+	die "failed to build revision '$mydir'"
+}
+
+run_dirs_helper () {
+	mydir=${1%/}
+	shift
+	while test $# -gt 0 -a "$1" != -- -a ! -f "$1"; do
+		shift
+	done
+	if test $# -gt 0 -a "$1" = --; then
+		shift
+	fi
+	if [ ! -d "$mydir" ]; then
+		rev=$(git rev-parse --verify "$mydir" 2>/dev/null) ||
+		die "'$mydir' is neither a directory nor a valid revision"
+		if [ ! -d build/$rev ]; then
+			unpack_git_rev $rev
+		fi
+		build_git_rev $rev
+		mydir=build/$rev
+	fi
+	if test "$mydir" = .; then
+		unset GIT_TEST_INSTALLED
+	else
+		GIT_TEST_INSTALLED="$mydir/bin-wrappers"
+		export GIT_TEST_INSTALLED
+	fi
+	run_one_dir "$@"
+}
+
+run_dirs () {
+	while test $# -gt 0 -a "$1" != -- -a ! -f "$1"; do
+		run_dirs_helper "$@"
+		shift
+	done
+}
+
+GIT_PERF_AGGREGATING_LATER=t
+export GIT_PERF_AGGREGATING_LATER
+
+cd "$(dirname $0)"
+. ../../GIT-BUILD-OPTIONS
+
+if test $# = 0 -o "$1" = -- -o -f "$1"; then
+	set -- . "$@"
+fi
+run_dirs "$@"
+./aggregate.perl "$@"
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index f4ca4fc..ccb5435 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -34,38 +34,69 @@
 # git init has been done in an empty repository.
 # make sure it is empty.
 
-find .git/objects -type f -print >should-be-empty
-test_expect_success \
-    '.git/objects should be empty after git init in an empty repo.' \
-    'cmp -s /dev/null should-be-empty'
+test_expect_success '.git/objects should be empty after git init in an empty repo' '
+	find .git/objects -type f -print >should-be-empty &&
+	test_line_count = 0 should-be-empty
+'
 
 # also it should have 2 subdirectories; no fan-out anymore, pack, and info.
 # 3 is counting "objects" itself
-find .git/objects -type d -print >full-of-directories
-test_expect_success \
-    '.git/objects should have 3 subdirectories.' \
-    'test $(wc -l < full-of-directories) = 3'
+test_expect_success '.git/objects should have 3 subdirectories' '
+	find .git/objects -type d -print >full-of-directories &&
+	test_line_count = 3 full-of-directories
+'
 
 ################################################################
 # Test harness
 test_expect_success 'success is reported like this' '
-    :
+	:
 '
 test_expect_failure 'pretend we have a known breakage' '
-    false
+	false
 '
-test_expect_failure 'pretend we have fixed a known breakage' '
-    :
-'
+
+test_expect_success 'pretend we have fixed a known breakage (run in sub test-lib)' "
+	mkdir passing-todo &&
+	(cd passing-todo &&
+	cat >passing-todo.sh <<-EOF &&
+	#!$SHELL_PATH
+
+	test_description='A passing TODO test
+
+	This is run in a sub test-lib so that we do not get incorrect
+	passing metrics
+	'
+
+	# Point to the t/test-lib.sh, which isn't in ../ as usual
+	TEST_DIRECTORY=\"$TEST_DIRECTORY\"
+	. \"\$TEST_DIRECTORY\"/test-lib.sh
+
+	test_expect_failure 'pretend we have fixed a known breakage' '
+		:
+	'
+
+	test_done
+	EOF
+	chmod +x passing-todo.sh &&
+	./passing-todo.sh >out 2>err &&
+	! test -s err &&
+	sed -e 's/^> //' >expect <<-\\EOF &&
+	> ok 1 - pretend we have fixed a known breakage # TODO known breakage
+	> # fixed 1 known breakage(s)
+	> # passed all 1 test(s)
+	> 1..1
+	EOF
+	test_cmp expect out)
+"
 test_set_prereq HAVEIT
 haveit=no
 test_expect_success HAVEIT 'test runs if prerequisite is satisfied' '
-    test_have_prereq HAVEIT &&
-    haveit=yes
+	test_have_prereq HAVEIT &&
+	haveit=yes
 '
 donthaveit=yes
 test_expect_success DONTHAVEIT 'unmet prerequisite causes test to be skipped' '
-    donthaveit=no
+	donthaveit=no
 '
 if test $haveit$donthaveit != yesyes
 then
@@ -73,62 +104,133 @@
 	exit 1
 fi
 
+test_set_prereq HAVETHIS
+haveit=no
+test_expect_success HAVETHIS,HAVEIT 'test runs if prerequisites are satisfied' '
+	test_have_prereq HAVEIT &&
+	test_have_prereq HAVETHIS &&
+	haveit=yes
+'
+donthaveit=yes
+test_expect_success HAVEIT,DONTHAVEIT 'unmet prerequisites causes test to be skipped' '
+	donthaveit=no
+'
+donthaveiteither=yes
+test_expect_success DONTHAVEIT,HAVEIT 'unmet prerequisites causes test to be skipped' '
+	donthaveiteither=no
+'
+if test $haveit$donthaveit$donthaveiteither != yesyesyes
+then
+	say "bug in test framework: multiple prerequisite tags do not work reliably"
+	exit 1
+fi
+
+clean=no
+test_expect_success 'tests clean up after themselves' '
+	test_when_finished clean=yes
+'
+
+if test $clean != yes
+then
+	say "bug in test framework: basic cleanup command does not work reliably"
+	exit 1
+fi
+
+test_expect_success 'tests clean up even on failures' "
+	mkdir failing-cleanup &&
+	(
+	cd failing-cleanup &&
+
+	cat >failing-cleanup.sh <<-EOF &&
+	#!$SHELL_PATH
+
+	test_description='Failing tests with cleanup commands'
+
+	# Point to the t/test-lib.sh, which isn't in ../ as usual
+	TEST_DIRECTORY=\"$TEST_DIRECTORY\"
+	. \"\$TEST_DIRECTORY\"/test-lib.sh
+
+	test_expect_success 'tests clean up even after a failure' '
+		touch clean-after-failure &&
+		test_when_finished rm clean-after-failure &&
+		(exit 1)
+	'
+	test_expect_success 'failure to clean up causes the test to fail' '
+		test_when_finished \"(exit 2)\"
+	'
+	test_done
+
+	EOF
+
+	chmod +x failing-cleanup.sh &&
+	test_must_fail ./failing-cleanup.sh >out 2>err &&
+	! test -s err &&
+	! test -f \"trash directory.failing-cleanup/clean-after-failure\" &&
+	sed -e 's/Z$//' -e 's/^> //' >expect <<-\\EOF &&
+	> not ok - 1 tests clean up even after a failure
+	> #	Z
+	> #	touch clean-after-failure &&
+	> #	test_when_finished rm clean-after-failure &&
+	> #	(exit 1)
+	> #	Z
+	> not ok - 2 failure to clean up causes the test to fail
+	> #	Z
+	> #	test_when_finished \"(exit 2)\"
+	> #	Z
+	> # failed 2 among 2 test(s)
+	> 1..2
+	EOF
+	test_cmp expect out
+	)
+"
+
 ################################################################
 # Basics of the basics
 
 # updating a new file without --add should fail.
-test_expect_success 'git update-index without --add should fail adding.' '
-    test_must_fail git update-index should-be-empty
+test_expect_success 'git update-index without --add should fail adding' '
+	test_must_fail git update-index should-be-empty
 '
 
 # and with --add it should succeed, even if it is empty (it used to fail).
-test_expect_success \
-    'git update-index with --add should succeed.' \
-    'git update-index --add should-be-empty'
-
-test_expect_success \
-    'writing tree out with git write-tree' \
-    'tree=$(git write-tree)'
-
-# we know the shape and contents of the tree and know the object ID for it.
-test_expect_success \
-    'validate object ID of a known tree.' \
-    'test "$tree" = 7bb943559a305bdd6bdee2cef6e5df2413c3d30a'
-
-# Removing paths.
-rm -f should-be-empty full-of-directories
-test_expect_success 'git update-index without --remove should fail removing.' '
-    test_must_fail git update-index should-be-empty
+test_expect_success 'git update-index with --add should succeed' '
+	git update-index --add should-be-empty
 '
 
-test_expect_success \
-    'git update-index with --remove should be able to remove.' \
-    'git update-index --remove should-be-empty'
+test_expect_success 'writing tree out with git write-tree' '
+	tree=$(git write-tree)
+'
+
+# we know the shape and contents of the tree and know the object ID for it.
+test_expect_success 'validate object ID of a known tree' '
+	test "$tree" = 7bb943559a305bdd6bdee2cef6e5df2413c3d30a
+    '
+
+# Removing paths.
+test_expect_success 'git update-index without --remove should fail removing' '
+	rm -f should-be-empty full-of-directories &&
+	test_must_fail git update-index should-be-empty
+'
+
+test_expect_success 'git update-index with --remove should be able to remove' '
+	git update-index --remove should-be-empty
+'
 
 # Empty tree can be written with recent write-tree.
-test_expect_success \
-    'git write-tree should be able to write an empty tree.' \
-    'tree=$(git write-tree)'
+test_expect_success 'git write-tree should be able to write an empty tree' '
+	tree=$(git write-tree)
+'
 
-test_expect_success \
-    'validate object ID of a known tree.' \
-    'test "$tree" = 4b825dc642cb6eb9a060e54bf8d69288fbee4904'
+test_expect_success 'validate object ID of a known tree' '
+	test "$tree" = 4b825dc642cb6eb9a060e54bf8d69288fbee4904
+'
 
 # Various types of objects
+
 # Some filesystems do not support symblic links; on such systems
 # some expected values are different
-mkdir path2 path3 path3/subp3
-paths='path0 path2/file2 path3/file3 path3/subp3/file3'
-for p in $paths
-do
-    echo "hello $p" >$p
-done
 if test_have_prereq SYMLINKS
 then
-	for p in $paths
-	do
-		ln -s "hello $p" ${p}sym
-	done
 	expectfilter=cat
 	expectedtree=087704a96baf1c2d1c869a8b084481e121c88b5b
 	expectedptree1=21ae8269cacbe57ae09138dcc3a2887f904d02b3
@@ -140,135 +242,154 @@
 	expectedptree2=ce580448f0148b985a513b693fdf7d802cacb44f
 fi
 
-test_expect_success \
-    'adding various types of objects with git update-index --add.' \
-    'find path* ! -type d -print | xargs git update-index --add'
+
+test_expect_success 'adding various types of objects with git update-index --add' '
+	mkdir path2 path3 path3/subp3 &&
+	paths="path0 path2/file2 path3/file3 path3/subp3/file3" &&
+	(
+		for p in $paths
+		do
+			echo "hello $p" >$p || exit 1
+			if test_have_prereq SYMLINKS
+			then
+				ln -s "hello $p" ${p}sym || exit 1
+			fi
+		done
+	) &&
+	find path* ! -type d -print | xargs git update-index --add
+'
 
 # Show them and see that matches what we expect.
-test_expect_success \
-    'showing stage with git ls-files --stage' \
-    'git ls-files --stage >current'
+test_expect_success 'showing stage with git ls-files --stage' '
+	git ls-files --stage >current
+'
 
-$expectfilter >expected <<\EOF
-100644 f87290f8eb2cbbea7857214459a0739927eab154 0	path0
-120000 15a98433ae33114b085f3eb3bb03b832b3180a01 0	path0sym
-100644 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 0	path2/file2
-120000 d8ce161addc5173867a3c3c730924388daedbc38 0	path2/file2sym
-100644 0aa34cae68d0878578ad119c86ca2b5ed5b28376 0	path3/file3
-120000 8599103969b43aff7e430efea79ca4636466794f 0	path3/file3sym
-100644 00fb5908cb97c2564a9783c0c64087333b3b464f 0	path3/subp3/file3
-120000 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c 0	path3/subp3/file3sym
-EOF
-test_expect_success \
-    'validate git ls-files output for a known tree.' \
-    'test_cmp expected current'
+test_expect_success 'validate git ls-files output for a known tree' '
+	$expectfilter >expected <<-\EOF &&
+	100644 f87290f8eb2cbbea7857214459a0739927eab154 0	path0
+	120000 15a98433ae33114b085f3eb3bb03b832b3180a01 0	path0sym
+	100644 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 0	path2/file2
+	120000 d8ce161addc5173867a3c3c730924388daedbc38 0	path2/file2sym
+	100644 0aa34cae68d0878578ad119c86ca2b5ed5b28376 0	path3/file3
+	120000 8599103969b43aff7e430efea79ca4636466794f 0	path3/file3sym
+	100644 00fb5908cb97c2564a9783c0c64087333b3b464f 0	path3/subp3/file3
+	120000 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c 0	path3/subp3/file3sym
+	EOF
+	test_cmp expected current
+'
 
-test_expect_success \
-    'writing tree out with git write-tree.' \
-    'tree=$(git write-tree)'
-test_expect_success \
-    'validate object ID for a known tree.' \
-    'test "$tree" = "$expectedtree"'
+test_expect_success 'writing tree out with git write-tree' '
+	tree=$(git write-tree)
+'
 
-test_expect_success \
-    'showing tree with git ls-tree' \
-    'git ls-tree $tree >current'
-cat >expected <<\EOF
-100644 blob f87290f8eb2cbbea7857214459a0739927eab154	path0
-120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01	path0sym
-040000 tree 58a09c23e2ca152193f2786e06986b7b6712bdbe	path2
-040000 tree 21ae8269cacbe57ae09138dcc3a2887f904d02b3	path3
-EOF
-test_expect_success SYMLINKS \
-    'git ls-tree output for a known tree.' \
-    'test_cmp expected current'
+test_expect_success 'validate object ID for a known tree' '
+	test "$tree" = "$expectedtree"
+'
+
+test_expect_success 'showing tree with git ls-tree' '
+    git ls-tree $tree >current
+'
+
+test_expect_success SYMLINKS 'git ls-tree output for a known tree' '
+	cat >expected <<-\EOF &&
+	100644 blob f87290f8eb2cbbea7857214459a0739927eab154	path0
+	120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01	path0sym
+	040000 tree 58a09c23e2ca152193f2786e06986b7b6712bdbe	path2
+	040000 tree 21ae8269cacbe57ae09138dcc3a2887f904d02b3	path3
+	EOF
+	test_cmp expected current
+'
 
 # This changed in ls-tree pathspec change -- recursive does
 # not show tree nodes anymore.
-test_expect_success \
-    'showing tree with git ls-tree -r' \
-    'git ls-tree -r $tree >current'
-$expectfilter >expected <<\EOF
-100644 blob f87290f8eb2cbbea7857214459a0739927eab154	path0
-120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01	path0sym
-100644 blob 3feff949ed00a62d9f7af97c15cd8a30595e7ac7	path2/file2
-120000 blob d8ce161addc5173867a3c3c730924388daedbc38	path2/file2sym
-100644 blob 0aa34cae68d0878578ad119c86ca2b5ed5b28376	path3/file3
-120000 blob 8599103969b43aff7e430efea79ca4636466794f	path3/file3sym
-100644 blob 00fb5908cb97c2564a9783c0c64087333b3b464f	path3/subp3/file3
-120000 blob 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c	path3/subp3/file3sym
-EOF
-test_expect_success \
-    'git ls-tree -r output for a known tree.' \
-    'test_cmp expected current'
-
-# But with -r -t we can have both.
-test_expect_success \
-    'showing tree with git ls-tree -r -t' \
-    'git ls-tree -r -t $tree >current'
-cat >expected <<\EOF
-100644 blob f87290f8eb2cbbea7857214459a0739927eab154	path0
-120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01	path0sym
-040000 tree 58a09c23e2ca152193f2786e06986b7b6712bdbe	path2
-100644 blob 3feff949ed00a62d9f7af97c15cd8a30595e7ac7	path2/file2
-120000 blob d8ce161addc5173867a3c3c730924388daedbc38	path2/file2sym
-040000 tree 21ae8269cacbe57ae09138dcc3a2887f904d02b3	path3
-100644 blob 0aa34cae68d0878578ad119c86ca2b5ed5b28376	path3/file3
-120000 blob 8599103969b43aff7e430efea79ca4636466794f	path3/file3sym
-040000 tree 3c5e5399f3a333eddecce7a9b9465b63f65f51e2	path3/subp3
-100644 blob 00fb5908cb97c2564a9783c0c64087333b3b464f	path3/subp3/file3
-120000 blob 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c	path3/subp3/file3sym
-EOF
-test_expect_success SYMLINKS \
-    'git ls-tree -r output for a known tree.' \
-    'test_cmp expected current'
-
-test_expect_success \
-    'writing partial tree out with git write-tree --prefix.' \
-    'ptree=$(git write-tree --prefix=path3)'
-test_expect_success \
-    'validate object ID for a known tree.' \
-    'test "$ptree" = "$expectedptree1"'
-
-test_expect_success \
-    'writing partial tree out with git write-tree --prefix.' \
-    'ptree=$(git write-tree --prefix=path3/subp3)'
-test_expect_success \
-    'validate object ID for a known tree.' \
-    'test "$ptree" = "$expectedptree2"'
-
-cat >badobjects <<EOF
-100644 blob 1000000000000000000000000000000000000000	dir/file1
-100644 blob 2000000000000000000000000000000000000000	dir/file2
-100644 blob 3000000000000000000000000000000000000000	dir/file3
-100644 blob 4000000000000000000000000000000000000000	dir/file4
-100644 blob 5000000000000000000000000000000000000000	dir/file5
-EOF
-
-rm .git/index
-test_expect_success \
-    'put invalid objects into the index.' \
-    'git update-index --index-info < badobjects'
-
-test_expect_success 'writing this tree without --missing-ok.' '
-    test_must_fail git write-tree
+test_expect_success 'showing tree with git ls-tree -r' '
+	git ls-tree -r $tree >current
 '
 
-test_expect_success \
-    'writing this tree with --missing-ok.' \
-    'git write-tree --missing-ok'
+test_expect_success 'git ls-tree -r output for a known tree' '
+	$expectfilter >expected <<-\EOF &&
+	100644 blob f87290f8eb2cbbea7857214459a0739927eab154	path0
+	120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01	path0sym
+	100644 blob 3feff949ed00a62d9f7af97c15cd8a30595e7ac7	path2/file2
+	120000 blob d8ce161addc5173867a3c3c730924388daedbc38	path2/file2sym
+	100644 blob 0aa34cae68d0878578ad119c86ca2b5ed5b28376	path3/file3
+	120000 blob 8599103969b43aff7e430efea79ca4636466794f	path3/file3sym
+	100644 blob 00fb5908cb97c2564a9783c0c64087333b3b464f	path3/subp3/file3
+	120000 blob 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c	path3/subp3/file3sym
+	EOF
+	test_cmp expected current
+'
+
+# But with -r -t we can have both.
+test_expect_success 'showing tree with git ls-tree -r -t' '
+	git ls-tree -r -t $tree >current
+'
+
+test_expect_success SYMLINKS 'git ls-tree -r output for a known tree' '
+	cat >expected <<-\EOF &&
+	100644 blob f87290f8eb2cbbea7857214459a0739927eab154	path0
+	120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01	path0sym
+	040000 tree 58a09c23e2ca152193f2786e06986b7b6712bdbe	path2
+	100644 blob 3feff949ed00a62d9f7af97c15cd8a30595e7ac7	path2/file2
+	120000 blob d8ce161addc5173867a3c3c730924388daedbc38	path2/file2sym
+	040000 tree 21ae8269cacbe57ae09138dcc3a2887f904d02b3	path3
+	100644 blob 0aa34cae68d0878578ad119c86ca2b5ed5b28376	path3/file3
+	120000 blob 8599103969b43aff7e430efea79ca4636466794f	path3/file3sym
+	040000 tree 3c5e5399f3a333eddecce7a9b9465b63f65f51e2	path3/subp3
+	100644 blob 00fb5908cb97c2564a9783c0c64087333b3b464f	path3/subp3/file3
+	120000 blob 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c	path3/subp3/file3sym
+	EOF
+	test_cmp expected current
+'
+
+test_expect_success 'writing partial tree out with git write-tree --prefix' '
+	ptree=$(git write-tree --prefix=path3)
+'
+
+test_expect_success 'validate object ID for a known tree' '
+	test "$ptree" = "$expectedptree1"
+'
+
+test_expect_success 'writing partial tree out with git write-tree --prefix' '
+	ptree=$(git write-tree --prefix=path3/subp3)
+'
+
+test_expect_success 'validate object ID for a known tree' '
+	test "$ptree" = "$expectedptree2"
+'
+
+test_expect_success 'put invalid objects into the index' '
+	rm -f .git/index &&
+	cat >badobjects <<-\EOF &&
+	100644 blob 1000000000000000000000000000000000000000	dir/file1
+	100644 blob 2000000000000000000000000000000000000000	dir/file2
+	100644 blob 3000000000000000000000000000000000000000	dir/file3
+	100644 blob 4000000000000000000000000000000000000000	dir/file4
+	100644 blob 5000000000000000000000000000000000000000	dir/file5
+	EOF
+	git update-index --index-info <badobjects
+'
+
+test_expect_success 'writing this tree without --missing-ok' '
+	test_must_fail git write-tree
+'
+
+test_expect_success 'writing this tree with --missing-ok' '
+	git write-tree --missing-ok
+'
 
 
 ################################################################
-rm .git/index
-test_expect_success \
-    'git read-tree followed by write-tree should be idempotent.' \
-    'git read-tree $tree &&
-     test -f .git/index &&
-     newtree=$(git write-tree) &&
-     test "$newtree" = "$tree"'
+test_expect_success 'git read-tree followed by write-tree should be idempotent' '
+	rm -f .git/index
+	git read-tree $tree &&
+	test -f .git/index &&
+	newtree=$(git write-tree) &&
+	test "$newtree" = "$tree"
+'
 
-$expectfilter >expected <<\EOF
+test_expect_success 'validate git diff-files output for a know cache/work tree state' '
+	$expectfilter >expected <<\EOF &&
 :100644 100644 f87290f8eb2cbbea7857214459a0739927eab154 0000000000000000000000000000000000000000 M	path0
 :120000 120000 15a98433ae33114b085f3eb3bb03b832b3180a01 0000000000000000000000000000000000000000 M	path0sym
 :100644 100644 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 0000000000000000000000000000000000000000 M	path2/file2
@@ -278,45 +399,47 @@
 :100644 100644 00fb5908cb97c2564a9783c0c64087333b3b464f 0000000000000000000000000000000000000000 M	path3/subp3/file3
 :120000 120000 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c 0000000000000000000000000000000000000000 M	path3/subp3/file3sym
 EOF
-test_expect_success \
-    'validate git diff-files output for a know cache/work tree state.' \
-    'git diff-files >current && diff >/dev/null -b current expected'
+	git diff-files >current &&
+	test_cmp current expected
+'
 
-test_expect_success \
-    'git update-index --refresh should succeed.' \
-    'git update-index --refresh'
+test_expect_success 'git update-index --refresh should succeed' '
+	git update-index --refresh
+'
 
-test_expect_success \
-    'no diff after checkout and git update-index --refresh.' \
-    'git diff-files >current && cmp -s current /dev/null'
+test_expect_success 'no diff after checkout and git update-index --refresh' '
+	git diff-files >current &&
+	cmp -s current /dev/null
+'
 
 ################################################################
 P=$expectedtree
-test_expect_success \
-    'git commit-tree records the correct tree in a commit.' \
-    'commit0=$(echo NO | git commit-tree $P) &&
-     tree=$(git show --pretty=raw $commit0 |
-	 sed -n -e "s/^tree //p" -e "/^author /q") &&
-     test "z$tree" = "z$P"'
 
-test_expect_success \
-    'git commit-tree records the correct parent in a commit.' \
-    'commit1=$(echo NO | git commit-tree $P -p $commit0) &&
-     parent=$(git show --pretty=raw $commit1 |
-	 sed -n -e "s/^parent //p" -e "/^author /q") &&
-     test "z$commit0" = "z$parent"'
+test_expect_success 'git commit-tree records the correct tree in a commit' '
+	commit0=$(echo NO | git commit-tree $P) &&
+	tree=$(git show --pretty=raw $commit0 |
+		 sed -n -e "s/^tree //p" -e "/^author /q") &&
+	test "z$tree" = "z$P"
+'
 
-test_expect_success \
-    'git commit-tree omits duplicated parent in a commit.' \
-    'commit2=$(echo NO | git commit-tree $P -p $commit0 -p $commit0) &&
-     parent=$(git show --pretty=raw $commit2 |
-	 sed -n -e "s/^parent //p" -e "/^author /q" |
-	 sort -u) &&
-     test "z$commit0" = "z$parent" &&
-     numparent=$(git show --pretty=raw $commit2 |
-	 sed -n -e "s/^parent //p" -e "/^author /q" |
-	 wc -l) &&
-     test $numparent = 1'
+test_expect_success 'git commit-tree records the correct parent in a commit' '
+	commit1=$(echo NO | git commit-tree $P -p $commit0) &&
+	parent=$(git show --pretty=raw $commit1 |
+		sed -n -e "s/^parent //p" -e "/^author /q") &&
+	test "z$commit0" = "z$parent"
+'
+
+test_expect_success 'git commit-tree omits duplicated parent in a commit' '
+	commit2=$(echo NO | git commit-tree $P -p $commit0 -p $commit0) &&
+	     parent=$(git show --pretty=raw $commit2 |
+		sed -n -e "s/^parent //p" -e "/^author /q" |
+		sort -u) &&
+	test "z$commit0" = "z$parent" &&
+	numparent=$(git show --pretty=raw $commit2 |
+		sed -n -e "s/^parent //p" -e "/^author /q" |
+		wc -l) &&
+	test $numparent = 1
+'
 
 test_expect_success 'update-index D/F conflict' '
 	mv path0 tmp &&
@@ -327,7 +450,7 @@
 	test $numpath0 = 1
 '
 
-test_expect_success SYMLINKS 'absolute path works as expected' '
+test_expect_success SYMLINKS 'real path works as expected' '
 	mkdir first &&
 	ln -s ../.git first/.git &&
 	mkdir second &&
@@ -335,14 +458,14 @@
 	mkdir third &&
 	dir="$(cd .git; pwd -P)" &&
 	dir2=third/../second/other/.git &&
-	test "$dir" = "$(test-path-utils make_absolute_path $dir2)" &&
+	test "$dir" = "$(test-path-utils real_path $dir2)" &&
 	file="$dir"/index &&
-	test "$file" = "$(test-path-utils make_absolute_path $dir2/index)" &&
+	test "$file" = "$(test-path-utils real_path $dir2/index)" &&
 	basename=blub &&
-	test "$dir/$basename" = "$(cd .git && test-path-utils make_absolute_path "$basename")" &&
+	test "$dir/$basename" = "$(cd .git && test-path-utils real_path "$basename")" &&
 	ln -s ../first/file .git/syml &&
 	sym="$(cd first; pwd -P)"/file &&
-	test "$sym" = "$(test-path-utils make_absolute_path "$dir2/syml")"
+	test "$sym" = "$(test-path-utils real_path "$dir2/syml")"
 '
 
 test_expect_success 'very long name in the index handled sanely' '
diff --git a/t/t0001-init.sh b/t/t0001-init.sh
index 49caa29..ad66410 100755
--- a/t/t0001-init.sh
+++ b/t/t0001-init.sh
@@ -25,7 +25,7 @@
 
 test_expect_success 'plain' '
 	(
-		unset GIT_DIR GIT_WORK_TREE
+		sane_unset GIT_DIR GIT_WORK_TREE &&
 		mkdir plain &&
 		cd plain &&
 		git init
@@ -33,9 +33,65 @@
 	check_config plain/.git false unset
 '
 
+test_expect_success 'plain nested in bare' '
+	(
+		sane_unset GIT_DIR GIT_WORK_TREE &&
+		git init --bare bare-ancestor.git &&
+		cd bare-ancestor.git &&
+		mkdir plain-nested &&
+		cd plain-nested &&
+		git init
+	) &&
+	check_config bare-ancestor.git/plain-nested/.git false unset
+'
+
+test_expect_success 'plain through aliased command, outside any git repo' '
+	(
+		sane_unset GIT_DIR GIT_WORK_TREE &&
+		HOME=$(pwd)/alias-config &&
+		export HOME &&
+		mkdir alias-config &&
+		echo "[alias] aliasedinit = init" >alias-config/.gitconfig &&
+
+		GIT_CEILING_DIRECTORIES=$(pwd) &&
+		export GIT_CEILING_DIRECTORIES &&
+
+		mkdir plain-aliased &&
+		cd plain-aliased &&
+		git aliasedinit
+	) &&
+	check_config plain-aliased/.git false unset
+'
+
+test_expect_failure 'plain nested through aliased command' '
+	(
+		sane_unset GIT_DIR GIT_WORK_TREE &&
+		git init plain-ancestor-aliased &&
+		cd plain-ancestor-aliased &&
+		echo "[alias] aliasedinit = init" >>.git/config &&
+		mkdir plain-nested &&
+		cd plain-nested &&
+		git aliasedinit
+	) &&
+	check_config plain-ancestor-aliased/plain-nested/.git false unset
+'
+
+test_expect_failure 'plain nested in bare through aliased command' '
+	(
+		sane_unset GIT_DIR GIT_WORK_TREE &&
+		git init --bare bare-ancestor-aliased.git &&
+		cd bare-ancestor-aliased.git &&
+		echo "[alias] aliasedinit = init" >>config &&
+		mkdir plain-nested &&
+		cd plain-nested &&
+		git aliasedinit
+	) &&
+	check_config bare-ancestor-aliased.git/plain-nested/.git false unset
+'
+
 test_expect_success 'plain with GIT_WORK_TREE' '
 	if (
-		unset GIT_DIR
+		sane_unset GIT_DIR &&
 		mkdir plain-wt &&
 		cd plain-wt &&
 		GIT_WORK_TREE=$(pwd) git init
@@ -48,7 +104,7 @@
 
 test_expect_success 'plain bare' '
 	(
-		unset GIT_DIR GIT_WORK_TREE GIT_CONFIG
+		sane_unset GIT_DIR GIT_WORK_TREE GIT_CONFIG &&
 		mkdir plain-bare-1 &&
 		cd plain-bare-1 &&
 		git --bare init
@@ -58,7 +114,7 @@
 
 test_expect_success 'plain bare with GIT_WORK_TREE' '
 	if (
-		unset GIT_DIR GIT_CONFIG
+		sane_unset GIT_DIR GIT_CONFIG &&
 		mkdir plain-bare-2 &&
 		cd plain-bare-2 &&
 		GIT_WORK_TREE=$(pwd) git --bare init
@@ -72,7 +128,7 @@
 test_expect_success 'GIT_DIR bare' '
 
 	(
-		unset GIT_CONFIG
+		sane_unset GIT_CONFIG &&
 		mkdir git-dir-bare.git &&
 		GIT_DIR=git-dir-bare.git git init
 	) &&
@@ -82,7 +138,7 @@
 test_expect_success 'init --bare' '
 
 	(
-		unset GIT_DIR GIT_WORK_TREE GIT_CONFIG
+		sane_unset GIT_DIR GIT_WORK_TREE GIT_CONFIG &&
 		mkdir init-bare.git &&
 		cd init-bare.git &&
 		git init --bare
@@ -93,7 +149,7 @@
 test_expect_success 'GIT_DIR non-bare' '
 
 	(
-		unset GIT_CONFIG
+		sane_unset GIT_CONFIG &&
 		mkdir non-bare &&
 		cd non-bare &&
 		GIT_DIR=.git git init
@@ -104,7 +160,7 @@
 test_expect_success 'GIT_DIR & GIT_WORK_TREE (1)' '
 
 	(
-		unset GIT_CONFIG
+		sane_unset GIT_CONFIG &&
 		mkdir git-dir-wt-1.git &&
 		GIT_WORK_TREE=$(pwd) GIT_DIR=git-dir-wt-1.git git init
 	) &&
@@ -114,7 +170,7 @@
 test_expect_success 'GIT_DIR & GIT_WORK_TREE (2)' '
 
 	if (
-		unset GIT_CONFIG
+		sane_unset GIT_CONFIG &&
 		mkdir git-dir-wt-2.git &&
 		GIT_WORK_TREE=$(pwd) GIT_DIR=git-dir-wt-2.git git --bare init
 	)
@@ -127,18 +183,18 @@
 test_expect_success 'reinit' '
 
 	(
-		unset GIT_CONFIG GIT_WORK_TREE GIT_CONFIG
+		sane_unset GIT_CONFIG GIT_WORK_TREE GIT_CONFIG &&
 
 		mkdir again &&
 		cd again &&
 		git init >out1 2>err1 &&
 		git init >out2 2>err2
 	) &&
-	grep "Initialized empty" again/out1 &&
-	grep "Reinitialized existing" again/out2 &&
+	test_i18ngrep "Initialized empty" again/out1 &&
+	test_i18ngrep "Reinitialized existing" again/out2 &&
 	>again/empty &&
-	test_cmp again/empty again/err1 &&
-	test_cmp again/empty again/err2
+	test_i18ncmp again/empty again/err1 &&
+	test_i18ncmp again/empty again/err2
 '
 
 test_expect_success 'init with --template' '
@@ -167,12 +223,25 @@
 	! test -f template-blank/.git/info/exclude
 '
 
+test_expect_success 'init with init.templatedir set' '
+	mkdir templatedir-source &&
+	echo Content >templatedir-source/file &&
+	(
+		test_config="${HOME}/.gitconfig" &&
+		git config -f "$test_config"  init.templatedir "${HOME}/templatedir-source" &&
+		mkdir templatedir-set &&
+		cd templatedir-set &&
+		sane_unset GIT_TEMPLATE_DIR &&
+		NO_SET_GIT_TEMPLATE_DIR=t &&
+		export NO_SET_GIT_TEMPLATE_DIR &&
+		git init
+	) &&
+	test_cmp templatedir-source/file templatedir-set/.git/file
+'
+
 test_expect_success 'init --bare/--shared overrides system/global config' '
 	(
-		HOME="`pwd`" &&
-		export HOME &&
 		test_config="$HOME"/.gitconfig &&
-		unset GIT_CONFIG_NOGLOBAL &&
 		git config -f "$test_config" core.bare false &&
 		git config -f "$test_config" core.sharedRepository 0640 &&
 		mkdir init-bare-shared-override &&
@@ -186,10 +255,7 @@
 
 test_expect_success 'init honors global core.sharedRepository' '
 	(
-		HOME="`pwd`" &&
-		export HOME &&
 		test_config="$HOME"/.gitconfig &&
-		unset GIT_CONFIG_NOGLOBAL &&
 		git config -f "$test_config" core.sharedRepository 0666 &&
 		mkdir shared-honor-global &&
 		cd shared-honor-global &&
@@ -244,6 +310,12 @@
 
 test_expect_success 'init creates a new deep directory' '
 	rm -fr newdir &&
+	git init newdir/a/b/c &&
+	test -d newdir/a/b/c/.git/refs
+'
+
+test_expect_success POSIXPERM 'init creates a new deep directory (umask vs. shared)' '
+	rm -fr newdir &&
 	(
 		# Leading directories should honor umask while
 		# the repository itself should follow "shared"
@@ -251,7 +323,7 @@
 		git init --bare --shared=0660 newdir/a/b/c &&
 		test -d newdir/a/b/c/refs &&
 		ls -ld newdir/a newdir/a/b > lsab.out &&
-		! grep -v "^drwxrw[sx]r-x" ls.out &&
+		! grep -v "^drwxrw[sx]r-x" lsab.out &&
 		ls -ld newdir/a/b/c > lsc.out &&
 		! grep -v "^drwxrw[sx]---" lsc.out
 	)
@@ -276,7 +348,7 @@
 	)
 '
 
-test_expect_success POSIXPERM 'init notices EPERM' '
+test_expect_success POSIXPERM,SANITY 'init notices EPERM' '
 	rm -fr newdir &&
 	(
 		mkdir newdir &&
@@ -285,4 +357,64 @@
 	)
 '
 
+test_expect_success 'init creates a new bare directory with global --bare' '
+	rm -rf newdir &&
+	git --bare init newdir &&
+	test -d newdir/refs
+'
+
+test_expect_success 'init prefers command line to GIT_DIR' '
+	rm -rf newdir &&
+	mkdir otherdir &&
+	GIT_DIR=otherdir git --bare init newdir &&
+	test -d newdir/refs &&
+	! test -d otherdir/refs
+'
+
+test_expect_success 'init with separate gitdir' '
+	rm -rf newdir &&
+	git init --separate-git-dir realgitdir newdir &&
+	echo "gitdir: `pwd`/realgitdir" >expected &&
+	test_cmp expected newdir/.git &&
+	test -d realgitdir/refs
+'
+
+test_expect_success 're-init to update git link' '
+	(
+	cd newdir &&
+	git init --separate-git-dir ../surrealgitdir
+	) &&
+	echo "gitdir: `pwd`/surrealgitdir" >expected &&
+	test_cmp expected newdir/.git &&
+	test -d surrealgitdir/refs &&
+	! test -d realgitdir/refs
+'
+
+test_expect_success 're-init to move gitdir' '
+	rm -rf newdir realgitdir surrealgitdir &&
+	git init newdir &&
+	(
+	cd newdir &&
+	git init --separate-git-dir ../realgitdir
+	) &&
+	echo "gitdir: `pwd`/realgitdir" >expected &&
+	test_cmp expected newdir/.git &&
+	test -d realgitdir/refs
+'
+
+test_expect_success SYMLINKS 're-init to move gitdir symlink' '
+	rm -rf newdir realgitdir &&
+	git init newdir &&
+	(
+	cd newdir &&
+	mv .git here &&
+	ln -s here .git &&
+	git init --separate-git-dir ../realgitdir
+	) &&
+	echo "gitdir: `pwd`/realgitdir" >expected &&
+	test_cmp expected newdir/.git &&
+	test -d realgitdir/refs &&
+	! test -d newdir/here
+'
+
 test_done
diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh
index 1c77192..51f3045 100755
--- a/t/t0003-attributes.sh
+++ b/t/t0003-attributes.sh
@@ -5,23 +5,25 @@
 . ./test-lib.sh
 
 attr_check () {
+	path="$1" expect="$2"
 
-	path="$1"
-	expect="$2"
-
-	git check-attr test -- "$path" >actual &&
+	git $3 check-attr test -- "$path" >actual 2>err &&
 	echo "$path: test: $2" >expect &&
-	test_cmp expect actual
-
+	test_cmp expect actual &&
+	test_line_count = 0 err
 }
 
 
 test_expect_success 'setup' '
-
-	mkdir -p a/b/d a/c &&
+	mkdir -p a/b/d a/c b &&
 	(
+		echo "[attr]notest !test"
 		echo "f	test=f"
 		echo "a/i test=a/i"
+		echo "onoff test -test"
+		echo "offon -test test"
+		echo "no notest"
+		echo "A/e/F test=A/e/F"
 	) >.gitattributes &&
 	(
 		echo "g test=a/g" &&
@@ -30,12 +32,44 @@
 	(
 		echo "h test=a/b/h" &&
 		echo "d/* test=a/b/d/*"
-	) >a/b/.gitattributes
+		echo "d/yes notest"
+	) >a/b/.gitattributes &&
+	(
+		echo "global test=global"
+	) >"$HOME"/global-gitattributes &&
+	cat <<-EOF >expect-all
+	f: test: f
+	a/f: test: f
+	a/c/f: test: f
+	a/g: test: a/g
+	a/b/g: test: a/b/g
+	b/g: test: unspecified
+	a/b/h: test: a/b/h
+	a/b/d/g: test: a/b/d/*
+	onoff: test: unset
+	offon: test: set
+	no: notest: set
+	no: test: unspecified
+	a/b/d/no: notest: set
+	a/b/d/no: test: a/b/d/*
+	a/b/d/yes: notest: set
+	a/b/d/yes: test: unspecified
+	EOF
+'
 
+test_expect_success 'command line checks' '
+	test_must_fail git check-attr &&
+	test_must_fail git check-attr -- &&
+	test_must_fail git check-attr test &&
+	test_must_fail git check-attr test -- &&
+	test_must_fail git check-attr -- f &&
+	echo "f" | test_must_fail git check-attr --stdin &&
+	echo "f" | test_must_fail git check-attr --stdin -- f &&
+	echo "f" | test_must_fail git check-attr --stdin test -- f &&
+	test_must_fail git check-attr "" -- f
 '
 
 test_expect_success 'attribute test' '
-
 	attr_check f f &&
 	attr_check a/f f &&
 	attr_check a/c/f f &&
@@ -43,43 +77,141 @@
 	attr_check a/b/g a/b/g &&
 	attr_check b/g unspecified &&
 	attr_check a/b/h a/b/h &&
-	attr_check a/b/d/g "a/b/d/*"
+	attr_check a/b/d/g "a/b/d/*" &&
+	attr_check onoff unset &&
+	attr_check offon set &&
+	attr_check no unspecified &&
+	attr_check a/b/d/no "a/b/d/*" &&
+	attr_check a/b/d/yes unspecified
+'
+
+test_expect_success 'attribute matching is case sensitive when core.ignorecase=0' '
+
+	test_must_fail attr_check F f "-c core.ignorecase=0" &&
+	test_must_fail attr_check a/F f "-c core.ignorecase=0" &&
+	test_must_fail attr_check a/c/F f "-c core.ignorecase=0" &&
+	test_must_fail attr_check a/G a/g "-c core.ignorecase=0" &&
+	test_must_fail attr_check a/B/g a/b/g "-c core.ignorecase=0" &&
+	test_must_fail attr_check a/b/G a/b/g "-c core.ignorecase=0" &&
+	test_must_fail attr_check a/b/H a/b/h "-c core.ignorecase=0" &&
+	test_must_fail attr_check a/b/D/g "a/b/d/*" "-c core.ignorecase=0" &&
+	test_must_fail attr_check oNoFf unset "-c core.ignorecase=0" &&
+	test_must_fail attr_check oFfOn set "-c core.ignorecase=0" &&
+	attr_check NO unspecified "-c core.ignorecase=0" &&
+	test_must_fail attr_check a/b/D/NO "a/b/d/*" "-c core.ignorecase=0" &&
+	attr_check a/b/d/YES a/b/d/* "-c core.ignorecase=0" &&
+	test_must_fail attr_check a/E/f "A/e/F" "-c core.ignorecase=0"
 
 '
 
-test_expect_success 'attribute test: read paths from stdin' '
+test_expect_success 'attribute matching is case insensitive when core.ignorecase=1' '
 
-	cat <<EOF > expect
-f: test: f
-a/f: test: f
-a/c/f: test: f
-a/g: test: a/g
-a/b/g: test: a/b/g
-b/g: test: unspecified
-a/b/h: test: a/b/h
-a/b/d/g: test: a/b/d/*
-EOF
+	attr_check F f "-c core.ignorecase=1" &&
+	attr_check a/F f "-c core.ignorecase=1" &&
+	attr_check a/c/F f "-c core.ignorecase=1" &&
+	attr_check a/G a/g "-c core.ignorecase=1" &&
+	attr_check a/B/g a/b/g "-c core.ignorecase=1" &&
+	attr_check a/b/G a/b/g "-c core.ignorecase=1" &&
+	attr_check a/b/H a/b/h "-c core.ignorecase=1" &&
+	attr_check a/b/D/g "a/b/d/*" "-c core.ignorecase=1" &&
+	attr_check oNoFf unset "-c core.ignorecase=1" &&
+	attr_check oFfOn set "-c core.ignorecase=1" &&
+	attr_check NO unspecified "-c core.ignorecase=1" &&
+	attr_check a/b/D/NO "a/b/d/*" "-c core.ignorecase=1" &&
+	attr_check a/b/d/YES unspecified "-c core.ignorecase=1" &&
+	attr_check a/E/f "A/e/F" "-c core.ignorecase=1"
 
-	sed -e "s/:.*//" < expect | git check-attr --stdin test > actual &&
+'
+
+test_expect_success 'check whether FS is case-insensitive' '
+	mkdir junk &&
+	echo good >junk/CamelCase &&
+	echo bad >junk/camelcase &&
+	if test "$(cat junk/CamelCase)" != good
+	then
+		test_set_prereq CASE_INSENSITIVE_FS
+	fi
+'
+
+test_expect_success CASE_INSENSITIVE_FS 'additional case insensitivity tests' '
+	test_must_fail attr_check a/B/D/g "a/b/d/*" "-c core.ignorecase=0" &&
+	test_must_fail attr_check A/B/D/NO "a/b/d/*" "-c core.ignorecase=0" &&
+	attr_check A/b/h a/b/h "-c core.ignorecase=1" &&
+	attr_check a/B/D/g "a/b/d/*" "-c core.ignorecase=1" &&
+	attr_check A/B/D/NO "a/b/d/*" "-c core.ignorecase=1"
+'
+
+test_expect_success 'unnormalized paths' '
+	attr_check ./f f &&
+	attr_check ./a/g a/g &&
+	attr_check a/./g a/g &&
+	attr_check a/c/../b/g a/b/g
+'
+
+test_expect_success 'relative paths' '
+	(cd a && attr_check ../f f) &&
+	(cd a && attr_check f f) &&
+	(cd a && attr_check i a/i) &&
+	(cd a && attr_check g a/g) &&
+	(cd a && attr_check b/g a/b/g) &&
+	(cd b && attr_check ../a/f f) &&
+	(cd b && attr_check ../a/g a/g) &&
+	(cd b && attr_check ../a/b/g a/b/g)
+'
+
+test_expect_success 'prefixes are not confused with leading directories' '
+	attr_check a_plus/g unspecified &&
+	cat >expect <<-\EOF &&
+	a/g: test: a/g
+	a_plus/g: test: unspecified
+	EOF
+	git check-attr test a/g a_plus/g >actual &&
 	test_cmp expect actual
 '
 
-test_expect_success 'root subdir attribute test' '
+test_expect_success 'core.attributesfile' '
+	attr_check global unspecified &&
+	git config core.attributesfile "$HOME/global-gitattributes" &&
+	attr_check global global &&
+	git config core.attributesfile "~/global-gitattributes" &&
+	attr_check global global &&
+	echo "global test=precedence" >>.gitattributes &&
+	attr_check global precedence
+'
 
+test_expect_success 'attribute test: read paths from stdin' '
+	grep -v notest <expect-all >expect &&
+	sed -e "s/:.*//" <expect | git check-attr --stdin test >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'attribute test: --all option' '
+	grep -v unspecified <expect-all | sort >specified-all &&
+	sed -e "s/:.*//" <expect-all | uniq >stdin-all &&
+	git check-attr --stdin --all <stdin-all | sort >actual &&
+	test_cmp specified-all actual
+'
+
+test_expect_success 'attribute test: --cached option' '
+	: >empty &&
+	git check-attr --cached --stdin --all <stdin-all | sort >actual &&
+	test_cmp empty actual &&
+	git add .gitattributes a/.gitattributes a/b/.gitattributes &&
+	git check-attr --cached --stdin --all <stdin-all | sort >actual &&
+	test_cmp specified-all actual
+'
+
+test_expect_success 'root subdir attribute test' '
 	attr_check a/i a/i &&
 	attr_check subdir/a/i unspecified
-
 '
 
 test_expect_success 'setup bare' '
-
 	git clone --bare . bare.git &&
 	cd bare.git
-
 '
 
 test_expect_success 'bare repository: check that .gitattribute is ignored' '
-
 	(
 		echo "f	test=f"
 		echo "a/i test=a/i"
@@ -89,11 +221,16 @@
 	attr_check a/c/f unspecified &&
 	attr_check a/i unspecified &&
 	attr_check subdir/a/i unspecified
+'
 
+test_expect_success 'bare repository: check that --cached honors index' '
+	GIT_INDEX_FILE=../.git/index \
+	git check-attr --cached --stdin --all <../stdin-all |
+	sort >actual &&
+	test_cmp ../specified-all actual
 '
 
 test_expect_success 'bare repository: test info/attributes' '
-
 	(
 		echo "f	test=f"
 		echo "a/i test=a/i"
@@ -103,7 +240,6 @@
 	attr_check a/c/f f &&
 	attr_check a/i a/i &&
 	attr_check subdir/a/i unspecified
-
 '
 
 test_done
diff --git a/t/t0004-unwritable.sh b/t/t0004-unwritable.sh
index 2342ac5..e3137d6 100755
--- a/t/t0004-unwritable.sh
+++ b/t/t0004-unwritable.sh
@@ -15,54 +15,30 @@
 
 '
 
-test_expect_success POSIXPERM 'write-tree should notice unwritable repository' '
-
-	(
-		chmod a-w .git/objects .git/objects/?? &&
-		test_must_fail git write-tree
-	)
-	status=$?
-	chmod 775 .git/objects .git/objects/??
-	(exit $status)
-
+test_expect_success POSIXPERM,SANITY 'write-tree should notice unwritable repository' '
+	test_when_finished "chmod 775 .git/objects .git/objects/??" &&
+	chmod a-w .git/objects .git/objects/?? &&
+	test_must_fail git write-tree
 '
 
-test_expect_success POSIXPERM 'commit should notice unwritable repository' '
-
-	(
-		chmod a-w .git/objects .git/objects/?? &&
-		test_must_fail git commit -m second
-	)
-	status=$?
-	chmod 775 .git/objects .git/objects/??
-	(exit $status)
-
+test_expect_success POSIXPERM,SANITY 'commit should notice unwritable repository' '
+	test_when_finished "chmod 775 .git/objects .git/objects/??" &&
+	chmod a-w .git/objects .git/objects/?? &&
+	test_must_fail git commit -m second
 '
 
-test_expect_success POSIXPERM 'update-index should notice unwritable repository' '
-
-	(
-		echo 6O >file &&
-		chmod a-w .git/objects .git/objects/?? &&
-		test_must_fail git update-index file
-	)
-	status=$?
-	chmod 775 .git/objects .git/objects/??
-	(exit $status)
-
+test_expect_success POSIXPERM,SANITY 'update-index should notice unwritable repository' '
+	test_when_finished "chmod 775 .git/objects .git/objects/??" &&
+	echo 6O >file &&
+	chmod a-w .git/objects .git/objects/?? &&
+	test_must_fail git update-index file
 '
 
-test_expect_success POSIXPERM 'add should notice unwritable repository' '
-
-	(
-		echo b >file &&
-		chmod a-w .git/objects .git/objects/?? &&
-		test_must_fail git add file
-	)
-	status=$?
-	chmod 775 .git/objects .git/objects/??
-	(exit $status)
-
+test_expect_success POSIXPERM,SANITY 'add should notice unwritable repository' '
+	test_when_finished "chmod 775 .git/objects .git/objects/??" &&
+	echo b >file &&
+	chmod a-w .git/objects .git/objects/?? &&
+	test_must_fail git add file
 '
 
 test_done
diff --git a/t/t0005-signals.sh b/t/t0005-signals.sh
index 09f855a..93e58c0 100755
--- a/t/t0005-signals.sh
+++ b/t/t0005-signals.sh
@@ -13,6 +13,7 @@
 	test-sigchain >actual
 	case "$?" in
 	143) true ;; # POSIX w/ SIGTERM=15
+	271) true ;; # ksh w/ SIGTERM=15
 	  3) true ;; # Windows
 	  *) false ;;
 	esac &&
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
new file mode 100755
index 0000000..1d29810
--- /dev/null
+++ b/t/t0006-date.sh
@@ -0,0 +1,85 @@
+#!/bin/sh
+
+test_description='test date parsing and printing'
+. ./test-lib.sh
+
+# arbitrary reference time: 2009-08-30 19:20:00
+TEST_DATE_NOW=1251660000; export TEST_DATE_NOW
+
+check_show() {
+	t=$(($TEST_DATE_NOW - $1))
+	echo "$t -> $2" >expect
+	test_expect_${3:-success} "relative date ($2)" "
+	test-date show $t >actual &&
+	test_cmp expect actual
+	"
+}
+
+check_show 5 '5 seconds ago'
+check_show 300 '5 minutes ago'
+check_show 18000 '5 hours ago'
+check_show 432000 '5 days ago'
+check_show 1728000 '3 weeks ago'
+check_show 13000000 '5 months ago'
+check_show 37500000 '1 year, 2 months ago'
+check_show 55188000 '1 year, 9 months ago'
+check_show 630000000 '20 years ago'
+check_show 31449600 '12 months ago'
+check_show 62985600 '2 years ago'
+
+check_parse() {
+	echo "$1 -> $2" >expect
+	test_expect_${4:-success} "parse date ($1${3:+ TZ=$3})" "
+	TZ=${3:-$TZ} test-date parse '$1' >actual &&
+	test_cmp expect actual
+	"
+}
+
+check_parse 2008 bad
+check_parse 2008-02 bad
+check_parse 2008-02-14 bad
+check_parse '2008-02-14 20:30:45' '2008-02-14 20:30:45 +0000'
+check_parse '2008-02-14 20:30:45 -0500' '2008-02-14 20:30:45 -0500'
+check_parse '2008-02-14 20:30:45 -0015' '2008-02-14 20:30:45 -0015'
+check_parse '2008-02-14 20:30:45 -5' '2008-02-14 20:30:45 +0000'
+check_parse '2008-02-14 20:30:45 -5:' '2008-02-14 20:30:45 +0000'
+check_parse '2008-02-14 20:30:45 -05' '2008-02-14 20:30:45 -0500'
+check_parse '2008-02-14 20:30:45 -:30' '2008-02-14 20:30:45 +0000'
+check_parse '2008-02-14 20:30:45 -05:00' '2008-02-14 20:30:45 -0500'
+check_parse '2008-02-14 20:30:45' '2008-02-14 20:30:45 -0500' EST5
+
+check_approxidate() {
+	echo "$1 -> $2 +0000" >expect
+	test_expect_${3:-success} "parse approxidate ($1)" "
+	test-date approxidate '$1' >actual &&
+	test_cmp expect actual
+	"
+}
+
+check_approxidate now '2009-08-30 19:20:00'
+check_approxidate '5 seconds ago' '2009-08-30 19:19:55'
+check_approxidate 5.seconds.ago '2009-08-30 19:19:55'
+check_approxidate 10.minutes.ago '2009-08-30 19:10:00'
+check_approxidate yesterday '2009-08-29 19:20:00'
+check_approxidate 3.days.ago '2009-08-27 19:20:00'
+check_approxidate 3.weeks.ago '2009-08-09 19:20:00'
+check_approxidate 3.months.ago '2009-05-30 19:20:00'
+check_approxidate 2.years.3.months.ago '2007-05-30 19:20:00'
+
+check_approxidate '6am yesterday' '2009-08-29 06:00:00'
+check_approxidate '6pm yesterday' '2009-08-29 18:00:00'
+check_approxidate '3:00' '2009-08-30 03:00:00'
+check_approxidate '15:00' '2009-08-30 15:00:00'
+check_approxidate 'noon today' '2009-08-30 12:00:00'
+check_approxidate 'noon yesterday' '2009-08-29 12:00:00'
+
+check_approxidate 'last tuesday' '2009-08-25 19:20:00'
+check_approxidate 'July 5th' '2009-07-05 19:20:00'
+check_approxidate '06/05/2009' '2009-06-05 19:20:00'
+check_approxidate '06.05.2009' '2009-05-06 19:20:00'
+
+check_approxidate 'Jun 6, 5AM' '2009-06-06 05:00:00'
+check_approxidate '5AM Jun 6' '2009-06-06 05:00:00'
+check_approxidate '6AM, June 7, 2009' '2009-06-07 06:00:00'
+
+test_done
diff --git a/t/t0020-crlf.sh b/t/t0020-crlf.sh
index 4e72b53..1a8f44c 100755
--- a/t/t0020-crlf.sh
+++ b/t/t0020-crlf.sh
@@ -4,21 +4,8 @@
 
 . ./test-lib.sh
 
-q_to_nul () {
-	perl -pe 'y/Q/\000/'
-}
-
-q_to_cr () {
-	tr Q '\015'
-}
-
-append_cr () {
-	sed -e 's/$/Q/' | tr Q '\015'
-}
-
-remove_cr () {
-	tr '\015' Q <"$1" | grep Q >/dev/null &&
-	tr '\015' Q <"$1" | sed -ne 's/Q$//p'
+has_cr() {
+	tr '\015' Q <"$1" | grep Q >/dev/null
 }
 
 test_expect_success setup '
@@ -156,7 +143,7 @@
 
 	for f in one dir/two
 	do
-		remove_cr "$f" >tmp && mv -f tmp $f &&
+		remove_cr <"$f" >tmp && mv -f tmp $f &&
 		git update-index -- $f || {
 			echo "Eh? $f"
 			false
@@ -180,7 +167,7 @@
 
 	for f in one dir/two
 	do
-		if remove_cr "$f" >/dev/null
+		if has_cr "$f"
 		then
 			echo "Eh? $f"
 			false
@@ -245,7 +232,7 @@
 	git read-tree --reset -u HEAD &&
 
 	git apply patch.file &&
-	test "$patched" = "`remove_cr one | git hash-object --stdin`" || {
+	test "$patched" = "`remove_cr <one | git hash-object --stdin`" || {
 		echo "Eh?  apply without index"
 		false
 	}
@@ -272,7 +259,7 @@
 
 	git apply --index patch.file &&
 	test "$patched" = `git rev-parse :one` &&
-	test "$patched" = "`remove_cr one | git hash-object --stdin`" || {
+	test "$patched" = "`remove_cr <one | git hash-object --stdin`" || {
 		echo "Eh?  apply with --index"
 		false
 	}
@@ -285,7 +272,7 @@
 	git config core.autocrlf true &&
 	git read-tree --reset -u HEAD &&
 
-	if remove_cr dir/two >/dev/null
+	if has_cr dir/two
 	then
 		echo "Huh?"
 		false
@@ -293,7 +280,7 @@
 		: happy
 	fi &&
 
-	if remove_cr one >/dev/null
+	if has_cr one
 	then
 		: happy
 	else
@@ -301,7 +288,7 @@
 		false
 	fi &&
 
-	if remove_cr three >/dev/null
+	if has_cr three
 	then
 		echo "Huh?"
 		false
@@ -316,7 +303,7 @@
 	echo "two crlf=input" >.gitattributes &&
 	git read-tree --reset -u HEAD &&
 
-	if remove_cr dir/two >/dev/null
+	if has_cr dir/two
 	then
 		echo "Huh?"
 		false
@@ -331,7 +318,7 @@
 	echo "t* crlf" >.gitattributes &&
 	git read-tree --reset -u HEAD &&
 
-	if remove_cr dir/two >/dev/null
+	if has_cr dir/two
 	then
 		: happy
 	else
@@ -339,7 +326,7 @@
 		false
 	fi &&
 
-	if remove_cr three >/dev/null
+	if has_cr three
 	then
 		: happy
 	else
@@ -357,14 +344,14 @@
 	rm -rf tmp one dir .gitattributes patch.file three &&
 	git read-tree --reset -u HEAD &&
 
-	if remove_cr one >/dev/null
+	if has_cr one
 	then
 		echo "Eh? one should not have CRLF"
 		false
 	else
 		: happy
 	fi &&
-	remove_cr three >/dev/null || {
+	has_cr three || {
 		echo "Eh? three should still have CRLF"
 		false
 	}
@@ -376,14 +363,14 @@
 	git read-tree --reset HEAD &&
 	git checkout-index -f -q -u -a &&
 
-	if remove_cr one >/dev/null
+	if has_cr one
 	then
 		echo "Eh? one should not have CRLF"
 		false
 	else
 		: happy
 	fi &&
-	remove_cr three >/dev/null || {
+	has_cr three || {
 		echo "Eh? three should still have CRLF"
 		false
 	}
@@ -396,14 +383,14 @@
 	git checkout-index -u .gitattributes &&
 	git checkout-index -u one dir/two three &&
 
-	if remove_cr one >/dev/null
+	if has_cr one
 	then
 		echo "Eh? one should not have CRLF"
 		false
 	else
 		: happy
 	fi &&
-	remove_cr three >/dev/null || {
+	has_cr three || {
 		echo "Eh? three should still have CRLF"
 		false
 	}
@@ -416,14 +403,14 @@
 	git checkout-index -u one dir/two three &&
 	git checkout-index -u .gitattributes &&
 
-	if remove_cr one >/dev/null
+	if has_cr one
 	then
 		echo "Eh? one should not have CRLF"
 		false
 	else
 		: happy
 	fi &&
-	remove_cr three >/dev/null || {
+	has_cr three || {
 		echo "Eh? three should still have CRLF"
 		false
 	}
@@ -452,11 +439,11 @@
 	git rm .gitattributes &&
 	echo "contentsQ" | q_to_cr > .file2 &&
 	git add .file2 &&
-	git commit -m third
+	git commit -m third &&
 
 	git checkout master~1 &&
 	git checkout master &&
-	remove_cr .file2 >/dev/null
+	has_cr .file2
 
 '
 
@@ -466,5 +453,57 @@
 	git diff
 
 '
+# Some more tests here to add new autocrlf functionality.
+# We want to have a known state here, so start a bit from scratch
+
+test_expect_success 'setting up for new autocrlf tests' '
+	git config core.autocrlf false &&
+	git config core.safecrlf false &&
+	rm -rf .????* * &&
+	for w in I am all LF; do echo $w; done >alllf &&
+	for w in Oh here is CRLFQ in text; do echo $w; done | q_to_cr >mixed &&
+	for w in I am all CRLF; do echo $w; done | append_cr >allcrlf &&
+	git add -A . &&
+	git commit -m "alllf, allcrlf and mixed only" &&
+	git tag -a -m "message" autocrlf-checkpoint
+'
+
+test_expect_success 'report no change after setting autocrlf' '
+	git config core.autocrlf true &&
+	touch * &&
+	git diff --exit-code
+'
+
+test_expect_success 'files are clean after checkout' '
+	rm * &&
+	git checkout -f &&
+	git diff --exit-code
+'
+
+cr_to_Q_no_NL () {
+    tr '\015' Q | tr -d '\012'
+}
+
+test_expect_success 'LF only file gets CRLF with autocrlf' '
+	test "$(cr_to_Q_no_NL < alllf)" = "IQamQallQLFQ"
+'
+
+test_expect_success 'Mixed file is still mixed with autocrlf' '
+	test "$(cr_to_Q_no_NL < mixed)" = "OhhereisCRLFQintext"
+'
+
+test_expect_success 'CRLF only file has CRLF with autocrlf' '
+	test "$(cr_to_Q_no_NL < allcrlf)" = "IQamQallQCRLFQ"
+'
+
+test_expect_success 'New CRLF file gets LF in repo' '
+	tr -d "\015" < alllf | append_cr > alllf2 &&
+	git add alllf2 &&
+	git commit -m "alllf2 added" &&
+	git config core.autocrlf false &&
+	rm * &&
+	git checkout -f &&
+	test_cmp alllf alllf2
+'
 
 test_done
diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh
index 8fc39d7..e50f0f7 100755
--- a/t/t0021-conversion.sh
+++ b/t/t0021-conversion.sh
@@ -4,7 +4,8 @@
 
 . ./test-lib.sh
 
-cat <<\EOF >rot13.sh
+cat <<EOF >rot13.sh
+#!$SHELL_PATH
 tr \
   'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' \
   'nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM'
@@ -64,28 +65,129 @@
 		echo "\$Id:NoSpaceAtFront \$"
 		echo "\$Id:NoSpaceAtEitherEnd\$"
 		echo "\$Id: NoTerminatingSymbol"
-	} > expanded-keywords &&
+		echo "\$Id: Foreign Commit With Spaces \$"
+	} >expanded-keywords.0 &&
+
+	{
+		cat expanded-keywords.0 &&
+		printf "\$Id: NoTerminatingSymbolAtEOF"
+	} >expanded-keywords &&
+	cat expanded-keywords >expanded-keywords-crlf &&
+	git add expanded-keywords expanded-keywords-crlf &&
+	git commit -m "File with keywords expanded" &&
+	id=$(git rev-parse --verify :expanded-keywords) &&
 
 	{
 		echo "File with expanded keywords"
-		echo "\$Id: 4f21723e7b15065df7de95bd46c8ba6fb1818f4c \$"
-		echo "\$Id: 4f21723e7b15065df7de95bd46c8ba6fb1818f4c \$"
-		echo "\$Id: 4f21723e7b15065df7de95bd46c8ba6fb1818f4c \$"
-		echo "\$Id: 4f21723e7b15065df7de95bd46c8ba6fb1818f4c \$"
-		echo "\$Id: 4f21723e7b15065df7de95bd46c8ba6fb1818f4c \$"
-		echo "\$Id: 4f21723e7b15065df7de95bd46c8ba6fb1818f4c \$"
+		echo "\$Id: $id \$"
+		echo "\$Id: $id \$"
+		echo "\$Id: $id \$"
+		echo "\$Id: $id \$"
+		echo "\$Id: $id \$"
+		echo "\$Id: $id \$"
 		echo "\$Id: NoTerminatingSymbol"
-	} > expected-output &&
+		echo "\$Id: Foreign Commit With Spaces \$"
+	} >expected-output.0 &&
+	{
+		cat expected-output.0 &&
+		printf "\$Id: NoTerminatingSymbolAtEOF"
+	} >expected-output &&
+	{
+		append_cr <expected-output.0 &&
+		printf "\$Id: NoTerminatingSymbolAtEOF"
+	} >expected-output-crlf &&
+	{
+		echo "expanded-keywords ident"
+		echo "expanded-keywords-crlf ident text eol=crlf"
+	} >>.gitattributes &&
 
-	git add expanded-keywords &&
-	git commit -m "File with keywords expanded" &&
+	rm -f expanded-keywords expanded-keywords-crlf &&
 
-	echo "expanded-keywords ident" >> .gitattributes &&
-
-	rm -f expanded-keywords &&
 	git checkout -- expanded-keywords &&
-	cat expanded-keywords &&
-	cmp expanded-keywords expected-output
+	test_cmp expanded-keywords expected-output &&
+
+	git checkout -- expanded-keywords-crlf &&
+	test_cmp expanded-keywords-crlf expected-output-crlf
+'
+
+# The use of %f in a filter definition is expanded to the path to
+# the filename being smudged or cleaned.  It must be shell escaped.
+# First, set up some interesting file names and pet them in
+# .gitattributes.
+test_expect_success 'filter shell-escaped filenames' '
+	cat >argc.sh <<-EOF &&
+	#!$SHELL_PATH
+	cat >/dev/null
+	echo argc: \$# "\$@"
+	EOF
+	normal=name-no-magic &&
+	special="name  with '\''sq'\'' and \$x" &&
+	echo some test text >"$normal" &&
+	echo some test text >"$special" &&
+	git add "$normal" "$special" &&
+	git commit -q -m "add files" &&
+	echo "name* filter=argc" >.gitattributes &&
+
+	# delete the files and check them out again, using a smudge filter
+	# that will count the args and echo the command-line back to us
+	git config filter.argc.smudge "sh ./argc.sh %f" &&
+	rm "$normal" "$special" &&
+	git checkout -- "$normal" "$special" &&
+
+	# make sure argc.sh counted the right number of args
+	echo "argc: 1 $normal" >expect &&
+	test_cmp expect "$normal" &&
+	echo "argc: 1 $special" >expect &&
+	test_cmp expect "$special" &&
+
+	# do the same thing, but with more args in the filter expression
+	git config filter.argc.smudge "sh ./argc.sh %f --my-extra-arg" &&
+	rm "$normal" "$special" &&
+	git checkout -- "$normal" "$special" &&
+
+	# make sure argc.sh counted the right number of args
+	echo "argc: 2 $normal --my-extra-arg" >expect &&
+	test_cmp expect "$normal" &&
+	echo "argc: 2 $special --my-extra-arg" >expect &&
+	test_cmp expect "$special" &&
+	:
+'
+
+test_expect_success 'required filter success' '
+	git config filter.required.smudge cat &&
+	git config filter.required.clean cat &&
+	git config filter.required.required true &&
+
+	echo "*.r filter=required" >.gitattributes &&
+
+	echo test >test.r &&
+	git add test.r &&
+	rm -f test.r &&
+	git checkout -- test.r
+'
+
+test_expect_success 'required filter smudge failure' '
+	git config filter.failsmudge.smudge false &&
+	git config filter.failsmudge.clean cat &&
+	git config filter.failsmudge.required true &&
+
+	echo "*.fs filter=failsmudge" >.gitattributes &&
+
+	echo test >test.fs &&
+	git add test.fs &&
+	rm -f test.fs &&
+	test_must_fail git checkout -- test.fs
+'
+
+test_expect_success 'required filter clean failure' '
+	git config filter.failclean.smudge cat &&
+	git config filter.failclean.clean false &&
+	git config filter.failclean.required true &&
+
+	echo "*.fc filter=failclean" >.gitattributes &&
+
+	echo test >test.fc &&
+	test_must_fail git add test.fc
 '
 
 test_done
diff --git a/t/t0022-crlf-rename.sh b/t/t0022-crlf-rename.sh
index f1e1d48..7af3fbc 100755
--- a/t/t0022-crlf-rename.sh
+++ b/t/t0022-crlf-rename.sh
Binary files differ
diff --git a/t/t0023-crlf-am.sh b/t/t0023-crlf-am.sh
index aaed725..f9bbb91 100755
--- a/t/t0023-crlf-am.sh
+++ b/t/t0023-crlf-am.sh
@@ -11,7 +11,7 @@
 Subject: test1
 
 ---
- foo |    1 +
+ foo | 1 +
  1 files changed, 1 insertions(+), 0 deletions(-)
  create mode 100644 foo
 
diff --git a/t/t0024-crlf-archive.sh b/t/t0024-crlf-archive.sh
index c7d0324..ec6c1b3 100755
--- a/t/t0024-crlf-archive.sh
+++ b/t/t0024-crlf-archive.sh
@@ -7,7 +7,7 @@
 
 test_expect_success setup '
 
-	git config core.autocrlf true
+	git config core.autocrlf true &&
 
 	printf "CRLF line ending\r\nAnd another\r\n" > sample &&
 	git add sample &&
@@ -20,7 +20,7 @@
 test_expect_success 'tar archive' '
 
 	git archive --format=tar HEAD |
-	( mkdir untarred && cd untarred && "$TAR" -xf - )
+	( mkdir untarred && cd untarred && "$TAR" -xf - ) &&
 
 	test_cmp sample untarred/sample
 
diff --git a/t/t0025-crlf-auto.sh b/t/t0025-crlf-auto.sh
new file mode 100755
index 0000000..f5f67a6
--- /dev/null
+++ b/t/t0025-crlf-auto.sh
@@ -0,0 +1,155 @@
+#!/bin/sh
+
+test_description='CRLF conversion'
+
+. ./test-lib.sh
+
+has_cr() {
+	tr '\015' Q <"$1" | grep Q >/dev/null
+}
+
+test_expect_success setup '
+
+	git config core.autocrlf false &&
+
+	for w in Hello world how are you; do echo $w; done >one &&
+	for w in I am very very fine thank you; do echo ${w}Q; done | q_to_cr >two &&
+	for w in Oh here is a QNUL byte how alarming; do echo ${w}; done | q_to_nul >three &&
+	git add . &&
+
+	git commit -m initial &&
+
+	one=`git rev-parse HEAD:one` &&
+	two=`git rev-parse HEAD:two` &&
+	three=`git rev-parse HEAD:three` &&
+
+	echo happy.
+'
+
+test_expect_success 'default settings cause no changes' '
+
+	rm -f .gitattributes tmp one two three &&
+	git read-tree --reset -u HEAD &&
+
+	! has_cr one &&
+	has_cr two &&
+	onediff=`git diff one` &&
+	twodiff=`git diff two` &&
+	threediff=`git diff three` &&
+	test -z "$onediff" -a -z "$twodiff" -a -z "$threediff"
+'
+
+test_expect_success 'crlf=true causes a CRLF file to be normalized' '
+
+	# Backwards compatibility check
+	rm -f .gitattributes tmp one two three &&
+	echo "two crlf" > .gitattributes &&
+	git read-tree --reset -u HEAD &&
+
+	# Note, "normalized" means that git will normalize it if added
+	has_cr two &&
+	twodiff=`git diff two` &&
+	test -n "$twodiff"
+'
+
+test_expect_success 'text=true causes a CRLF file to be normalized' '
+
+	rm -f .gitattributes tmp one two three &&
+	echo "two text" > .gitattributes &&
+	git read-tree --reset -u HEAD &&
+
+	# Note, "normalized" means that git will normalize it if added
+	has_cr two &&
+	twodiff=`git diff two` &&
+	test -n "$twodiff"
+'
+
+test_expect_success 'eol=crlf gives a normalized file CRLFs with autocrlf=false' '
+
+	rm -f .gitattributes tmp one two three &&
+	git config core.autocrlf false &&
+	echo "one eol=crlf" > .gitattributes &&
+	git read-tree --reset -u HEAD &&
+
+	has_cr one &&
+	onediff=`git diff one` &&
+	test -z "$onediff"
+'
+
+test_expect_success 'eol=crlf gives a normalized file CRLFs with autocrlf=input' '
+
+	rm -f .gitattributes tmp one two three &&
+	git config core.autocrlf input &&
+	echo "one eol=crlf" > .gitattributes &&
+	git read-tree --reset -u HEAD &&
+
+	has_cr one &&
+	onediff=`git diff one` &&
+	test -z "$onediff"
+'
+
+test_expect_success 'eol=lf gives a normalized file LFs with autocrlf=true' '
+
+	rm -f .gitattributes tmp one two three &&
+	git config core.autocrlf true &&
+	echo "one eol=lf" > .gitattributes &&
+	git read-tree --reset -u HEAD &&
+
+	! has_cr one &&
+	onediff=`git diff one` &&
+	test -z "$onediff"
+'
+
+test_expect_success 'autocrlf=true does not normalize CRLF files' '
+
+	rm -f .gitattributes tmp one two three &&
+	git config core.autocrlf true &&
+	git read-tree --reset -u HEAD &&
+
+	has_cr one &&
+	has_cr two &&
+	onediff=`git diff one` &&
+	twodiff=`git diff two` &&
+	threediff=`git diff three` &&
+	test -z "$onediff" -a -z "$twodiff" -a -z "$threediff"
+'
+
+test_expect_success 'text=auto, autocrlf=true _does_ normalize CRLF files' '
+
+	rm -f .gitattributes tmp one two three &&
+	git config core.autocrlf true &&
+	echo "* text=auto" > .gitattributes &&
+	git read-tree --reset -u HEAD &&
+
+	has_cr one &&
+	has_cr two &&
+	onediff=`git diff one` &&
+	twodiff=`git diff two` &&
+	threediff=`git diff three` &&
+	test -z "$onediff" -a -n "$twodiff" -a -z "$threediff"
+'
+
+test_expect_success 'text=auto, autocrlf=true does not normalize binary files' '
+
+	rm -f .gitattributes tmp one two three &&
+	git config core.autocrlf true &&
+	echo "* text=auto" > .gitattributes &&
+	git read-tree --reset -u HEAD &&
+
+	! has_cr three &&
+	threediff=`git diff three` &&
+	test -z "$threediff"
+'
+
+test_expect_success 'eol=crlf _does_ normalize binary files' '
+
+	rm -f .gitattributes tmp one two three &&
+	echo "three eol=crlf" > .gitattributes &&
+	git read-tree --reset -u HEAD &&
+
+	has_cr three &&
+	threediff=`git diff three` &&
+	test -z "$threediff"
+'
+
+test_done
diff --git a/t/t0026-eol-config.sh b/t/t0026-eol-config.sh
new file mode 100755
index 0000000..fe0164b
--- /dev/null
+++ b/t/t0026-eol-config.sh
@@ -0,0 +1,83 @@
+#!/bin/sh
+
+test_description='CRLF conversion'
+
+. ./test-lib.sh
+
+has_cr() {
+	tr '\015' Q <"$1" | grep Q >/dev/null
+}
+
+test_expect_success setup '
+
+	git config core.autocrlf false &&
+
+	echo "one text" > .gitattributes &&
+
+	for w in Hello world how are you; do echo $w; done >one &&
+	for w in I am very very fine thank you; do echo $w; done >two &&
+	git add . &&
+
+	git commit -m initial &&
+
+	one=`git rev-parse HEAD:one` &&
+	two=`git rev-parse HEAD:two` &&
+
+	echo happy.
+'
+
+test_expect_success 'eol=lf puts LFs in normalized file' '
+
+	rm -f .gitattributes tmp one two &&
+	git config core.eol lf &&
+	git read-tree --reset -u HEAD &&
+
+	! has_cr one &&
+	! has_cr two &&
+	onediff=`git diff one` &&
+	twodiff=`git diff two` &&
+	test -z "$onediff" -a -z "$twodiff"
+'
+
+test_expect_success 'eol=crlf puts CRLFs in normalized file' '
+
+	rm -f .gitattributes tmp one two &&
+	git config core.eol crlf &&
+	git read-tree --reset -u HEAD &&
+
+	has_cr one &&
+	! has_cr two &&
+	onediff=`git diff one` &&
+	twodiff=`git diff two` &&
+	test -z "$onediff" -a -z "$twodiff"
+'
+
+test_expect_success 'autocrlf=true overrides eol=lf' '
+
+	rm -f .gitattributes tmp one two &&
+	git config core.eol lf &&
+	git config core.autocrlf true &&
+	git read-tree --reset -u HEAD &&
+
+	has_cr one &&
+	has_cr two &&
+	onediff=`git diff one` &&
+	twodiff=`git diff two` &&
+	test -z "$onediff" -a -z "$twodiff"
+'
+
+test_expect_success 'autocrlf=true overrides unset eol' '
+
+	rm -f .gitattributes tmp one two &&
+	git config --unset-all core.eol &&
+	git config core.autocrlf true &&
+	git read-tree --reset -u HEAD &&
+
+	has_cr one &&
+	has_cr two &&
+	onediff=`git diff one` &&
+	twodiff=`git diff two` &&
+	test -z "$onediff" -a -z "$twodiff"
+'
+
+test_done
diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh
index bbc821e..e3f354a 100755
--- a/t/t0040-parse-options.sh
+++ b/t/t0040-parse-options.sh
@@ -7,10 +7,13 @@
 
 . ./test-lib.sh
 
-cat > expect.err << EOF
+cat > expect << EOF
 usage: test-parse-options <options>
 
-    -b, --boolean         get a boolean
+    --yes                 get a boolean
+    -D, --no-doubt        begins with 'no-'
+    -B, --no-fear         be brave
+    -b, --boolean         increment by one
     -4, --or4             bitwise-or boolean with ...0100
     --neg-or4             same as --no-or4
 
@@ -19,7 +22,7 @@
     --set23               set integer to 23
     -t <time>             get timestamp of <time>
     -L, --length <str>    get length of <str>
-    -F, --file <FILE>     set file to <FILE>
+    -F, --file <file>     set file to <file>
 
 String options
     -s, --string <string>
@@ -28,11 +31,14 @@
     --st <st>             get another string (pervert ordering)
     -o <str>              get another string
     --default-string      set string to default
+    --list <str>          add str to list
 
 Magic arguments
     --quux                means --quux
     -NUM                  set integer to NUM
     +                     same as -b
+    --ambiguous           positive ambiguity
+    --no-ambiguous        negative ambiguity
 
 Standard options
     --abbrev[=<n>]        use <n> digits to display SHA-1s
@@ -44,10 +50,65 @@
 
 test_expect_success 'test help' '
 	test_must_fail test-parse-options -h > output 2> output.err &&
-	test ! -s output &&
-	test_cmp expect.err output.err
+	test ! -s output.err &&
+	test_cmp expect output
 '
 
+mv expect expect.err
+
+cat >expect.template <<EOF
+boolean: 0
+integer: 0
+timestamp: 0
+string: (not set)
+abbrev: 7
+verbose: 0
+quiet: no
+dry run: no
+file: (not set)
+EOF
+
+check() {
+	what="$1" &&
+	shift &&
+	expect="$1" &&
+	shift &&
+	sed "s/^$what .*/$what $expect/" <expect.template >expect &&
+	test-parse-options $* >output 2>output.err &&
+	test ! -s output.err &&
+	test_cmp expect output
+}
+
+check_unknown() {
+	case "$1" in
+	--*)
+		echo error: unknown option \`${1#--}\' >expect ;;
+	-*)
+		echo error: unknown switch \`${1#-}\' >expect ;;
+	esac &&
+	cat expect.err >>expect &&
+	test_must_fail test-parse-options $* >output 2>output.err &&
+	test ! -s output &&
+	test_cmp expect output.err
+}
+
+test_expect_success 'OPT_BOOL() #1' 'check boolean: 1 --yes'
+test_expect_success 'OPT_BOOL() #2' 'check boolean: 1 --no-doubt'
+test_expect_success 'OPT_BOOL() #3' 'check boolean: 1 -D'
+test_expect_success 'OPT_BOOL() #4' 'check boolean: 1 --no-fear'
+test_expect_success 'OPT_BOOL() #5' 'check boolean: 1 -B'
+
+test_expect_success 'OPT_BOOL() is idempotent #1' 'check boolean: 1 --yes --yes'
+test_expect_success 'OPT_BOOL() is idempotent #2' 'check boolean: 1 -DB'
+
+test_expect_success 'OPT_BOOL() negation #1' 'check boolean: 0 -D --no-yes'
+test_expect_success 'OPT_BOOL() negation #2' 'check boolean: 0 -D --no-no-doubt'
+
+test_expect_success 'OPT_BOOL() no negation #1' 'check_unknown --fear'
+test_expect_success 'OPT_BOOL() no negation #2' 'check_unknown --no-no-fear'
+
+test_expect_success 'OPT_BOOL() positivation' 'check boolean: 0 -D --doubt'
+
 cat > expect << EOF
 boolean: 2
 integer: 1729
@@ -82,7 +143,7 @@
 test_expect_success 'long options' '
 	test-parse-options --boolean --integer 1729 --boolean --string2=321 \
 		--verbose --verbose --no-dry-run --abbrev=10 --file fi.le\
-		> output 2> output.err &&
+		--obsolete > output 2> output.err &&
 	test ! -s output.err &&
 	test_cmp expect output
 '
@@ -175,6 +236,16 @@
 	test_cmp typo.err output.err
 '
 
+cat > typo.err << EOF
+error: did you mean \`--ambiguous\` (with two dashes ?)
+EOF
+
+test_expect_success 'detect possible typos' '
+	test_must_fail test-parse-options -ambiguous > output 2> output.err &&
+	test ! -s output &&
+	test_cmp typo.err output.err
+'
+
 cat > expect <<EOF
 boolean: 0
 integer: 0
@@ -291,7 +362,7 @@
 	test_cmp expect output
 '
 
-test_expect_success 'OPT_BOOLEAN() with PARSE_OPT_NODASH works' '
+test_expect_success 'OPT_COUNTUP() with PARSE_OPT_NODASH works' '
 	test-parse-options + + + + + + > output 2> output.err &&
 	test ! -s output.err &&
 	test_cmp expect output
@@ -315,4 +386,38 @@
 	test_cmp expect output
 '
 
+cat >expect <<EOF
+boolean: 0
+integer: 0
+timestamp: 0
+string: (not set)
+abbrev: 7
+verbose: 0
+quiet: no
+dry run: no
+file: (not set)
+EOF
+
+test_expect_success 'negation of OPT_NONEG flags is not ambiguous' '
+	test-parse-options --no-ambig >output 2>output.err &&
+	test ! -s output.err &&
+	test_cmp expect output
+'
+
+cat >>expect <<'EOF'
+list: foo
+list: bar
+list: baz
+EOF
+test_expect_success '--list keeps list of strings' '
+	test-parse-options --list foo --list=bar --list=baz >output &&
+	test_cmp expect output
+'
+
+test_expect_success '--no-list resets list' '
+	test-parse-options --list=other --list=irrelevant --list=options \
+		--no-list --list=foo --list=bar --list=baz >output &&
+	test_cmp expect output
+'
+
 test_done
diff --git a/t/t0050-filesystem.sh b/t/t0050-filesystem.sh
index 89282cc..1542cf6 100755
--- a/t/t0050-filesystem.sh
+++ b/t/t0050-filesystem.sh
@@ -4,22 +4,22 @@
 
 . ./test-lib.sh
 
-auml=`printf '\xc3\xa4'`
-aumlcdiar=`printf '\x61\xcc\x88'`
+auml=$(printf '\303\244')
+aumlcdiar=$(printf '\141\314\210')
 
 case_insensitive=
 unibad=
 no_symlinks=
 test_expect_success 'see what we expect' '
 
-	test_case=test_expect_success
-	test_unicode=test_expect_success
+	test_case=test_expect_success &&
+	test_unicode=test_expect_success &&
 	mkdir junk &&
 	echo good >junk/CamelCase &&
 	echo bad >junk/camelcase &&
 	if test "$(cat junk/CamelCase)" != good
 	then
-		test_case=test_expect_failure
+		test_case=test_expect_failure &&
 		case_insensitive=t
 	fi &&
 	rm -fr junk &&
@@ -27,7 +27,7 @@
 	>junk/"$auml" &&
 	case "$(cd junk && echo *)" in
 	"$aumlcdiar")
-		test_unicode=test_expect_failure
+		test_unicode=test_expect_failure &&
 		unibad=t
 		;;
 	*)	;;
@@ -36,7 +36,7 @@
 	{
 		ln -s x y 2> /dev/null &&
 		test -h y 2> /dev/null ||
-		no_symlinks=1
+		no_symlinks=1 &&
 		rm -f y
 	}
 '
@@ -108,13 +108,17 @@
 
 '
 
-$test_case 'add (with different case)' '
+
+
+test_expect_failure 'add (with different case)' '
 
 	git reset --hard initial &&
 	rm camelcase &&
 	echo 1 >CamelCase &&
 	git add CamelCase &&
-	test $(git ls-files | grep -i camelcase | wc -l) = 1
+	camel=$(git ls-files | grep -i camelcase) &&
+	test $(echo "$camel" | wc -l) = 1 &&
+	test "z$(git cat-file blob :$camel)" = z1
 
 '
 
@@ -124,7 +128,7 @@
   cd unicode &&
   touch "$aumlcdiar" &&
   git add "$aumlcdiar" &&
-  git commit -m initial
+  git commit -m initial &&
   git tag initial &&
   git checkout -b topic &&
   git mv $aumlcdiar tmp &&
diff --git a/t/t0061-run-command.sh b/t/t0061-run-command.sh
new file mode 100755
index 0000000..17e969d
--- /dev/null
+++ b/t/t0061-run-command.sh
@@ -0,0 +1,50 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Ilari Liusvaara
+#
+
+test_description='Test run command'
+
+. ./test-lib.sh
+
+cat >hello-script <<-EOF
+	#!$SHELL_PATH
+	cat hello-script
+EOF
+>empty
+
+test_expect_success 'start_command reports ENOENT' '
+	test-run-command start-command-ENOENT ./does-not-exist
+'
+
+test_expect_success 'run_command can run a command' '
+	cat hello-script >hello.sh &&
+	chmod +x hello.sh &&
+	test-run-command run-command ./hello.sh >actual 2>err &&
+
+	test_cmp hello-script actual &&
+	test_cmp empty err
+'
+
+test_expect_success POSIXPERM 'run_command reports EACCES' '
+	cat hello-script >hello.sh &&
+	chmod -x hello.sh &&
+	test_must_fail test-run-command run-command ./hello.sh 2>err &&
+
+	grep "fatal: cannot exec.*hello.sh" err
+'
+
+test_expect_success POSIXPERM 'unreadable directory in PATH' '
+	mkdir local-command &&
+	test_when_finished "chmod u+rwx local-command && rm -fr local-command" &&
+	git config alias.nitfol "!echo frotz" &&
+	chmod a-rx local-command &&
+	(
+		PATH=./local-command:$PATH &&
+		git nitfol >actual
+	) &&
+	echo frotz >expect &&
+	test_cmp expect actual
+'
+
+test_done
diff --git a/t/t0062-revision-walking.sh b/t/t0062-revision-walking.sh
new file mode 100755
index 0000000..3d98eb8
--- /dev/null
+++ b/t/t0062-revision-walking.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+#
+# Copyright (c) 2012 Heiko Voigt
+#
+
+test_description='Test revision walking api'
+
+. ./test-lib.sh
+
+cat >run_twice_expected <<-EOF
+1st
+ > add b
+ > add a
+2nd
+ > add b
+ > add a
+EOF
+
+test_expect_success 'setup' '
+	echo a > a &&
+	git add a &&
+	git commit -m "add a" &&
+	echo b > b &&
+	git add b &&
+	git commit -m "add b"
+'
+
+test_expect_success 'revision walking can be done twice' '
+	test-revision-walking run-twice > run_twice_actual
+	test_cmp run_twice_expected run_twice_actual
+'
+
+test_done
diff --git a/t/t0070-fundamental.sh b/t/t0070-fundamental.sh
index 680d7d6..9bee8bf 100755
--- a/t/t0070-fundamental.sh
+++ b/t/t0070-fundamental.sh
@@ -12,4 +12,17 @@
 	test-ctype
 '
 
+test_expect_success 'mktemp to nonexistent directory prints filename' '
+	test_must_fail test-mktemp doesnotexist/testXXXXXX 2>err &&
+	grep "doesnotexist/test" err
+'
+
+test_expect_success POSIXPERM 'mktemp to unwritable directory prints filename' '
+	mkdir cannotwrite &&
+	chmod -w cannotwrite &&
+	test_when_finished "chmod +w cannotwrite" &&
+	test_must_fail test-mktemp cannotwrite/testXXXXXX 2>err &&
+	grep "cannotwrite/test" err
+'
+
 test_done
diff --git a/t/t0081-line-buffer.sh b/t/t0081-line-buffer.sh
new file mode 100755
index 0000000..bd83ed3
--- /dev/null
+++ b/t/t0081-line-buffer.sh
@@ -0,0 +1,90 @@
+#!/bin/sh
+
+test_description="Test the svn importer's input handling routines.
+
+These tests provide some simple checks that the line_buffer API
+behaves as advertised.
+
+While at it, check that input of newlines and null bytes are handled
+correctly.
+"
+. ./test-lib.sh
+
+test_expect_success 'hello world' '
+	echo ">HELLO" >expect &&
+	test-line-buffer <<-\EOF >actual &&
+	binary 6
+	HELLO
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success '0-length read, send along greeting' '
+	echo ">HELLO" >expect &&
+	test-line-buffer <<-\EOF >actual &&
+	binary 0
+	copy 6
+	HELLO
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'read from file descriptor' '
+	rm -f input &&
+	echo hello >expect &&
+	echo hello >input &&
+	echo copy 6 |
+	test-line-buffer "&4" 4<input >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'skip, copy null byte' '
+	echo Q | q_to_nul >expect &&
+	q_to_nul <<-\EOF | test-line-buffer >actual &&
+	skip 2
+	Q
+	copy 2
+	Q
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'read null byte' '
+	echo ">QhelloQ" | q_to_nul >expect &&
+	q_to_nul <<-\EOF | test-line-buffer >actual &&
+	binary 8
+	QhelloQ
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'long reads are truncated' '
+	echo ">foo" >expect &&
+	test-line-buffer <<-\EOF >actual &&
+	binary 5
+	foo
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'long copies are truncated' '
+	printf "%s\n" ">" foo >expect &&
+	test-line-buffer <<-\EOF >actual &&
+	binary 1
+
+	copy 5
+	foo
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'long binary reads are truncated' '
+	echo ">foo" >expect &&
+	test-line-buffer <<-\EOF >actual &&
+	binary 5
+	foo
+	EOF
+	test_cmp expect actual
+'
+
+test_done
diff --git a/t/t0090-cache-tree.sh b/t/t0090-cache-tree.sh
new file mode 100755
index 0000000..6c33e28
--- /dev/null
+++ b/t/t0090-cache-tree.sh
@@ -0,0 +1,93 @@
+#!/bin/sh
+
+test_description="Test whether cache-tree is properly updated
+
+Tests whether various commands properly update and/or rewrite the
+cache-tree extension.
+"
+ . ./test-lib.sh
+
+cmp_cache_tree () {
+	test-dump-cache-tree >actual &&
+	sed "s/$_x40/SHA/" <actual >filtered &&
+	test_cmp "$1" filtered
+}
+
+# We don't bother with actually checking the SHA1:
+# test-dump-cache-tree already verifies that all existing data is
+# correct.
+test_shallow_cache_tree () {
+	printf "SHA  (%d entries, 0 subtrees)\n" $(git ls-files|wc -l) >expect &&
+	cmp_cache_tree expect
+}
+
+test_invalid_cache_tree () {
+	echo "invalid                                   (0 subtrees)" >expect &&
+	printf "SHA #(ref)  (%d entries, 0 subtrees)\n" $(git ls-files|wc -l) >>expect &&
+	cmp_cache_tree expect
+}
+
+test_no_cache_tree () {
+	: >expect &&
+	cmp_cache_tree expect
+}
+
+test_expect_failure 'initial commit has cache-tree' '
+	test_commit foo &&
+	test_shallow_cache_tree
+'
+
+test_expect_success 'read-tree HEAD establishes cache-tree' '
+	git read-tree HEAD &&
+	test_shallow_cache_tree
+'
+
+test_expect_success 'git-add invalidates cache-tree' '
+	test_when_finished "git reset --hard; git read-tree HEAD" &&
+	echo "I changed this file" > foo &&
+	git add foo &&
+	test_invalid_cache_tree
+'
+
+test_expect_success 'update-index invalidates cache-tree' '
+	test_when_finished "git reset --hard; git read-tree HEAD" &&
+	echo "I changed this file" > foo &&
+	git update-index --add foo &&
+	test_invalid_cache_tree
+'
+
+test_expect_success 'write-tree establishes cache-tree' '
+	test-scrap-cache-tree &&
+	git write-tree &&
+	test_shallow_cache_tree
+'
+
+test_expect_success 'test-scrap-cache-tree works' '
+	git read-tree HEAD &&
+	test-scrap-cache-tree &&
+	test_no_cache_tree
+'
+
+test_expect_success 'second commit has cache-tree' '
+	test_commit bar &&
+	test_shallow_cache_tree
+'
+
+test_expect_success 'reset --hard gives cache-tree' '
+	test-scrap-cache-tree &&
+	git reset --hard &&
+	test_shallow_cache_tree
+'
+
+test_expect_success 'reset --hard without index gives cache-tree' '
+	rm -f .git/index &&
+	git reset --hard &&
+	test_shallow_cache_tree
+'
+
+test_expect_failure 'checkout gives cache-tree' '
+	git checkout HEAD^ &&
+	test_shallow_cache_tree
+'
+
+test_done
diff --git a/t/t0101-at-syntax.sh b/t/t0101-at-syntax.sh
new file mode 100755
index 0000000..a1998b5
--- /dev/null
+++ b/t/t0101-at-syntax.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+test_description='various @{whatever} syntax tests'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	test_commit one &&
+	test_commit two
+'
+
+check_at() {
+	echo "$2" >expect &&
+	git log -1 --format=%s "$1" >actual &&
+	test_cmp expect actual
+}
+
+test_expect_success '@{0} shows current' '
+	check_at @{0} two
+'
+
+test_expect_success '@{1} shows old' '
+	check_at @{1} one
+'
+
+test_expect_success '@{now} shows current' '
+	check_at @{now} two
+'
+
+test_expect_success '@{2001-09-17} (before the first commit) shows old' '
+	check_at @{2001-09-17} one
+'
+
+test_expect_success 'silly approxidates work' '
+	check_at @{3.hot.dogs.on.2001-09-17} one
+'
+
+test_expect_success 'notice misspelled upstream' '
+	test_must_fail git log -1 --format=%s @{usptream}
+'
+
+test_expect_success 'complain about total nonsense' '
+	test_must_fail git log -1 --format=%s @{utter.bogosity}
+'
+
+test_done
diff --git a/t/t0200-gettext-basic.sh b/t/t0200-gettext-basic.sh
new file mode 100755
index 0000000..8853d8a
--- /dev/null
+++ b/t/t0200-gettext-basic.sh
@@ -0,0 +1,108 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Ævar Arnfjörð Bjarmason
+#
+
+test_description='Gettext support for Git'
+
+. ./lib-gettext.sh
+
+test_expect_success "sanity: \$GIT_INTERNAL_GETTEXT_SH_SCHEME is set (to $GIT_INTERNAL_GETTEXT_SH_SCHEME)" '
+    test -n "$GIT_INTERNAL_GETTEXT_SH_SCHEME"
+'
+
+test_expect_success 'sanity: $TEXTDOMAIN is git' '
+    test $TEXTDOMAIN = "git"
+'
+
+test_expect_success 'xgettext sanity: Perl _() strings are not extracted' '
+    ! grep "A Perl string xgettext will not get" "$GIT_PO_PATH"/is.po
+'
+
+test_expect_success 'xgettext sanity: Comment extraction with --add-comments' '
+    grep "TRANSLATORS: This is a test" "$TEST_DIRECTORY"/t0200/* | wc -l >expect &&
+    grep "TRANSLATORS: This is a test" "$GIT_PO_PATH"/is.po  | wc -l >actual &&
+    test_cmp expect actual
+'
+
+test_expect_success 'xgettext sanity: Comment extraction with --add-comments stops at statements' '
+    ! grep "This is a phony" "$GIT_PO_PATH"/is.po &&
+    ! grep "the above comment" "$GIT_PO_PATH"/is.po
+'
+
+test_expect_success GETTEXT 'sanity: $TEXTDOMAINDIR exists without NO_GETTEXT=YesPlease' '
+    test -d "$TEXTDOMAINDIR" &&
+    test "$TEXTDOMAINDIR" = "$GIT_TEXTDOMAINDIR"
+'
+
+test_expect_success GETTEXT 'sanity: Icelandic locale was compiled' '
+    test -f "$TEXTDOMAINDIR/is/LC_MESSAGES/git.mo"
+'
+
+# TODO: When we have more locales, generalize this to test them
+# all. Maybe we'll need a dir->locale map for that.
+test_expect_success GETTEXT_LOCALE 'sanity: gettext("") metadata is OK' '
+    # Return value may be non-zero
+    LANGUAGE=is LC_ALL="$is_IS_locale" gettext "" >zero-expect &&
+    grep "Project-Id-Version: Git" zero-expect &&
+    grep "Git Mailing List <git@vger.kernel.org>" zero-expect &&
+    grep "Content-Type: text/plain; charset=UTF-8" zero-expect &&
+    grep "Content-Transfer-Encoding: 8bit" zero-expect
+'
+
+test_expect_success GETTEXT_LOCALE 'sanity: gettext(unknown) is passed through' '
+    printf "This is not a translation string"  >expect &&
+    gettext "This is not a translation string" >actual &&
+    eval_gettext "This is not a translation string" >actual &&
+    test_cmp expect actual
+'
+
+# xgettext from C
+test_expect_success GETTEXT_LOCALE 'xgettext: C extraction of _() and N_() strings' '
+    printf "TILRAUN: C tilraunastrengur" >expect &&
+    printf "\n" >>expect &&
+    printf "Sjá '\''git help SKIPUN'\'' til að sjá hjálp fyrir tiltekna skipun." >>expect &&
+    LANGUAGE=is LC_ALL="$is_IS_locale" gettext "TEST: A C test string" >actual &&
+    printf "\n" >>actual &&
+    LANGUAGE=is LC_ALL="$is_IS_locale" gettext "See '\''git help COMMAND'\'' for more information on a specific command." >>actual &&
+    test_cmp expect actual
+'
+
+test_expect_success GETTEXT_LOCALE 'xgettext: C extraction with %s' '
+    printf "TILRAUN: C tilraunastrengur %%s" >expect &&
+    LANGUAGE=is LC_ALL="$is_IS_locale" gettext "TEST: A C test string %s" >actual &&
+    test_cmp expect actual
+'
+
+# xgettext from Shell
+test_expect_success GETTEXT_LOCALE 'xgettext: Shell extraction' '
+    printf "TILRAUN: Skeljartilraunastrengur" >expect &&
+    LANGUAGE=is LC_ALL="$is_IS_locale" gettext "TEST: A Shell test string" >actual &&
+    test_cmp expect actual
+'
+
+test_expect_success GETTEXT_LOCALE 'xgettext: Shell extraction with $variable' '
+    printf "TILRAUN: Skeljartilraunastrengur með breytunni a var i able" >x-expect &&
+    LANGUAGE=is LC_ALL="$is_IS_locale" variable="a var i able" eval_gettext "TEST: A Shell test \$variable" >x-actual &&
+    test_cmp x-expect x-actual
+'
+
+# xgettext from Perl
+test_expect_success GETTEXT_LOCALE 'xgettext: Perl extraction' '
+    printf "TILRAUN: Perl tilraunastrengur" >expect &&
+    LANGUAGE=is LC_ALL="$is_IS_locale" gettext "TEST: A Perl test string" >actual &&
+    test_cmp expect actual
+'
+
+test_expect_success GETTEXT_LOCALE 'xgettext: Perl extraction with %s' '
+    printf "TILRAUN: Perl tilraunastrengur með breytunni %%s" >expect &&
+    LANGUAGE=is LC_ALL="$is_IS_locale" gettext "TEST: A Perl test variable %s" >actual &&
+    test_cmp expect actual
+'
+
+test_expect_success GETTEXT_LOCALE 'sanity: Some gettext("") data for real locale' '
+    LANGUAGE=is LC_ALL="$is_IS_locale" gettext "" >real-locale &&
+    test -s real-locale
+'
+
+test_done
diff --git a/t/t0200/test.c b/t/t0200/test.c
new file mode 100644
index 0000000..584d45c
--- /dev/null
+++ b/t/t0200/test.c
@@ -0,0 +1,23 @@
+/* This is a phony C program that's only here to test xgettext message extraction */
+
+const char help[] =
+	/* TRANSLATORS: This is a test. You don't need to translate it. */
+	N_("See 'git help COMMAND' for more information on a specific command.");
+
+int main(void)
+{
+	/* TRANSLATORS: This is a test. You don't need to translate it. */
+	puts(_("TEST: A C test string"));
+
+	/* TRANSLATORS: This is a test. You don't need to translate it. */
+	printf(_("TEST: A C test string %s"), "variable");
+
+	/* TRANSLATORS: This is a test. You don't need to translate it. */
+	printf(_("TEST: Hello World!"));
+
+	/* TRANSLATORS: This is a test. You don't need to translate it. */
+	printf(_("TEST: Old English Runes"));
+
+	/* TRANSLATORS: This is a test. You don't need to translate it. */
+	printf(_("TEST: ‘single’ and “double” quotes"));
+}
diff --git a/t/t0200/test.perl b/t/t0200/test.perl
new file mode 100644
index 0000000..36fba34
--- /dev/null
+++ b/t/t0200/test.perl
@@ -0,0 +1,14 @@
+# This is a phony Perl program that's only here to test xgettext
+# message extraction
+
+# so the above comment won't be folded into the next one by xgettext
+1;
+
+# TRANSLATORS: This is a test. You don't need to translate it.
+print __("TEST: A Perl test string");
+
+# TRANSLATORS: This is a test. You don't need to translate it.
+printf __("TEST: A Perl test variable %s"), "moo";
+
+# TRANSLATORS: If you see this, Git has a bug
+print _"TEST: A Perl string xgettext will not get";
diff --git a/t/t0200/test.sh b/t/t0200/test.sh
new file mode 100644
index 0000000..022d607
--- /dev/null
+++ b/t/t0200/test.sh
@@ -0,0 +1,14 @@
+# This is a phony Shell program that's only here to test xgettext
+# message extraction
+
+# so the above comment won't be folded into the next one by xgettext
+echo
+
+# TRANSLATORS: This is a test. You don't need to translate it.
+gettext "TEST: A Shell test string"
+
+# TRANSLATORS: This is a test. You don't need to translate it.
+eval_gettext "TEST: A Shell test \$variable"
+
+# TRANSLATORS: If you see this, Git has a bug
+_("TEST: A Shell string xgettext won't get")
diff --git a/t/t0201-gettext-fallbacks.sh b/t/t0201-gettext-fallbacks.sh
new file mode 100755
index 0000000..52b1c27
--- /dev/null
+++ b/t/t0201-gettext-fallbacks.sh
@@ -0,0 +1,67 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Ævar Arnfjörð Bjarmason
+#
+
+test_description='Gettext Shell fallbacks'
+
+GIT_INTERNAL_GETTEXT_TEST_FALLBACKS=YesPlease
+export GIT_INTERNAL_GETTEXT_TEST_FALLBACKS
+
+. ./lib-gettext.sh
+
+test_expect_success "sanity: \$GIT_INTERNAL_GETTEXT_SH_SCHEME is set (to $GIT_INTERNAL_GETTEXT_SH_SCHEME)" '
+    test -n "$GIT_INTERNAL_GETTEXT_SH_SCHEME"
+'
+
+test_expect_success 'sanity: $GIT_INTERNAL_GETTEXT_TEST_FALLBACKS is set' '
+    test -n "$GIT_INTERNAL_GETTEXT_TEST_FALLBACKS"
+'
+
+test_expect_success C_LOCALE_OUTPUT 'sanity: $GIT_INTERNAL_GETTEXT_SH_SCHEME" is fallthrough' '
+    echo fallthrough >expect &&
+    echo $GIT_INTERNAL_GETTEXT_SH_SCHEME >actual &&
+    test_cmp expect actual
+'
+
+test_expect_success 'gettext: our gettext() fallback has pass-through semantics' '
+    printf "test" >expect &&
+    gettext "test" >actual &&
+    test_i18ncmp expect actual &&
+    printf "test more words" >expect &&
+    gettext "test more words" >actual &&
+    test_i18ncmp expect actual
+'
+
+test_expect_success 'eval_gettext: our eval_gettext() fallback has pass-through semantics' '
+    printf "test" >expect &&
+    eval_gettext "test" >actual &&
+    test_i18ncmp expect actual &&
+    printf "test more words" >expect &&
+    eval_gettext "test more words" >actual &&
+    test_i18ncmp expect actual
+'
+
+test_expect_success 'eval_gettext: our eval_gettext() fallback can interpolate variables' '
+    printf "test YesPlease" >expect &&
+    GIT_INTERNAL_GETTEXT_TEST_FALLBACKS=YesPlease eval_gettext "test \$GIT_INTERNAL_GETTEXT_TEST_FALLBACKS" >actual &&
+    test_i18ncmp expect actual
+'
+
+test_expect_success 'eval_gettext: our eval_gettext() fallback can interpolate variables with spaces' '
+    cmdline="git am" &&
+    export cmdline;
+    printf "When you have resolved this problem run git am --resolved." >expect &&
+    eval_gettext "When you have resolved this problem run \$cmdline --resolved." >actual
+    test_i18ncmp expect actual
+'
+
+test_expect_success 'eval_gettext: our eval_gettext() fallback can interpolate variables with spaces and quotes' '
+    cmdline="git am" &&
+    export cmdline;
+    printf "When you have resolved this problem run \"git am --resolved\"." >expect &&
+    eval_gettext "When you have resolved this problem run \"\$cmdline --resolved\"." >actual
+    test_i18ncmp expect actual
+'
+
+test_done
diff --git a/t/t0202-gettext-perl.sh b/t/t0202-gettext-perl.sh
new file mode 100755
index 0000000..428ebb0
--- /dev/null
+++ b/t/t0202-gettext-perl.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Ævar Arnfjörð Bjarmason
+#
+
+test_description='Perl gettext interface (Git::I18N)'
+
+. ./lib-gettext.sh
+
+if ! test_have_prereq PERL; then
+	skip_all='skipping perl interface tests, perl not available'
+	test_done
+fi
+
+"$PERL_PATH" -MTest::More -e 0 2>/dev/null || {
+	skip_all="Perl Test::More unavailable, skipping test"
+	test_done
+}
+
+# The external test will outputs its own plan
+test_external_has_tap=1
+
+test_external_without_stderr \
+    'Perl Git::I18N API' \
+    "$PERL_PATH" "$TEST_DIRECTORY"/t0202/test.pl
+
+test_done
diff --git a/t/t0202/test.pl b/t/t0202/test.pl
new file mode 100644
index 0000000..2c10cb4
--- /dev/null
+++ b/t/t0202/test.pl
@@ -0,0 +1,110 @@
+#!/usr/bin/perl
+use 5.008;
+use lib (split(/:/, $ENV{GITPERLLIB}));
+use strict;
+use warnings;
+use POSIX qw(:locale_h);
+use Test::More tests => 8;
+use Git::I18N;
+
+my $has_gettext_library = $Git::I18N::__HAS_LIBRARY;
+
+ok(1, "Testing Git::I18N with " .
+	 ($has_gettext_library
+	  ? (defined $Locale::Messages::VERSION
+		 ? "Locale::Messages version $Locale::Messages::VERSION"
+		 # Versions of Locale::Messages before 1.17 didn't have a
+		 # $VERSION variable.
+		 : "Locale::Messages version <1.17")
+	  : "NO Perl gettext library"));
+ok(1, "Git::I18N is located at $INC{'Git/I18N.pm'}");
+
+{
+	my $exports = @Git::I18N::EXPORT;
+	ok($exports, "sanity: Git::I18N has $exports export(s)");
+}
+is_deeply(\@Git::I18N::EXPORT, \@Git::I18N::EXPORT_OK, "sanity: Git::I18N exports everything by default");
+
+# prototypes
+{
+	# Add prototypes here when modifying the public interface to add
+	# more gettext wrapper functions.
+	my %prototypes = (qw(
+		__	$
+	));
+	while (my ($sub, $proto) = each %prototypes) {
+		is(prototype(\&{"Git::I18N::$sub"}), $proto, "sanity: $sub has a $proto prototype");
+	}
+}
+
+# Test basic passthrough in the C locale
+{
+	local $ENV{LANGUAGE} = 'C';
+	local $ENV{LC_ALL}   = 'C';
+	local $ENV{LANG}     = 'C';
+
+	my ($got, $expect) = (('TEST: A Perl test string') x 2);
+
+	is(__($got), $expect, "Passing a string through __() in the C locale works");
+}
+
+# Test a basic message on different locales
+SKIP: {
+	unless ($ENV{GETTEXT_LOCALE}) {
+		# Can't reliably test __() with a non-C locales because the
+		# required locales may not be installed on the system.
+		#
+		# We test for these anyway as part of the shell
+		# tests. Skipping these here will eliminate failures on odd
+		# platforms with incomplete locale data.
+
+		skip "GETTEXT_LOCALE must be set by lib-gettext.sh for exhaustive Git::I18N tests", 2;
+	}
+
+	# The is_IS UTF-8 locale passed from lib-gettext.sh
+	my $is_IS_locale = $ENV{is_IS_locale};
+
+	my $test = sub {
+		my ($got, $expect, $msg, $locale) = @_;
+		# Maybe this system doesn't have the locale we're trying to
+		# test.
+		my $locale_ok = setlocale(LC_ALL, $locale);
+		is(__($got), $expect, "$msg a gettext library + <$locale> locale <$got> turns into <$expect>");
+	};
+
+	my $env_C = sub {
+		$ENV{LANGUAGE} = 'C';
+		$ENV{LC_ALL}   = 'C';
+	};
+
+	my $env_is = sub {
+		$ENV{LANGUAGE} = 'is';
+		$ENV{LC_ALL}   = $is_IS_locale;
+	};
+
+	# Translation's the same as the original
+	my ($got, $expect) = (('TEST: A Perl test string') x 2);
+
+	if ($has_gettext_library) {
+		{
+			local %ENV; $env_C->();
+			$test->($got, $expect, "With", 'C');
+		}
+
+		{
+			my ($got, $expect) = ($got, 'TILRAUN: Perl tilraunastrengur');
+			local %ENV; $env_is->();
+			$test->($got, $expect, "With", $is_IS_locale);
+		}
+	} else {
+		{
+			local %ENV; $env_C->();
+			$test->($got, $expect, "Without", 'C');
+		}
+
+		{
+			local %ENV; $env_is->();
+			$test->($got, $expect, "Without", 'is');
+		}
+	}
+}
diff --git a/t/t0203-gettext-setlocale-sanity.sh b/t/t0203-gettext-setlocale-sanity.sh
new file mode 100755
index 0000000..a212460
--- /dev/null
+++ b/t/t0203-gettext-setlocale-sanity.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Ævar Arnfjörð Bjarmason
+#
+
+test_description="The Git C functions aren't broken by setlocale(3)"
+
+. ./lib-gettext.sh
+
+test_expect_success 'git show a ISO-8859-1 commit under C locale' '
+	. "$TEST_DIRECTORY"/t3901-8859-1.txt &&
+	test_commit "iso-c-commit" iso-under-c &&
+	git show >out 2>err &&
+	! test -s err &&
+	grep -q "iso-c-commit" out
+'
+
+test_expect_success GETTEXT_LOCALE 'git show a ISO-8859-1 commit under a UTF-8 locale' '
+	. "$TEST_DIRECTORY"/t3901-8859-1.txt &&
+	test_commit "iso-utf8-commit" iso-under-utf8 &&
+	LANGUAGE=is LC_ALL="$is_IS_locale" git show >out 2>err &&
+	! test -s err &&
+	grep -q "iso-utf8-commit" out
+'
+
+test_done
diff --git a/t/t0204-gettext-reencode-sanity.sh b/t/t0204-gettext-reencode-sanity.sh
new file mode 100755
index 0000000..8437e51
--- /dev/null
+++ b/t/t0204-gettext-reencode-sanity.sh
@@ -0,0 +1,87 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Ævar Arnfjörð Bjarmason
+#
+
+test_description="Gettext reencoding of our *.po/*.mo files works"
+
+. ./lib-gettext.sh
+
+# The constants used in a tricky observation for undefined behaviour
+RUNES="TILRAUN: ᚻᛖ ᚳᚹᚫᚦ ᚦᚫᛏ ᚻᛖ ᛒᚢᛞᛖ ᚩᚾ ᚦᚫᛗ ᛚᚪᚾᛞᛖ ᚾᚩᚱᚦᚹᛖᚪᚱᛞᚢᛗ ᚹᛁᚦ ᚦᚪ ᚹᛖᛥᚫ"
+PUNTS="TILRAUN: ?? ???? ??? ?? ???? ?? ??? ????? ??????????? ??? ?? ????"
+MSGKEY="TEST: Old English Runes"
+
+test_expect_success GETTEXT_LOCALE 'gettext: Emitting UTF-8 from our UTF-8 *.mo files / Icelandic' '
+    printf "TILRAUN: Halló Heimur!" >expect &&
+    LANGUAGE=is LC_ALL="$is_IS_locale" gettext "TEST: Hello World!" >actual &&
+    test_cmp expect actual
+'
+
+test_expect_success GETTEXT_LOCALE 'gettext: Emitting UTF-8 from our UTF-8 *.mo files / Runes' '
+    printf "%s" "$RUNES" >expect &&
+    LANGUAGE=is LC_ALL="$is_IS_locale" gettext "$MSGKEY" >actual &&
+    test_cmp expect actual
+'
+
+test_expect_success GETTEXT_ISO_LOCALE 'gettext: Emitting ISO-8859-1 from our UTF-8 *.mo files / Icelandic' '
+    printf "TILRAUN: Halló Heimur!" | iconv -f UTF-8 -t ISO8859-1 >expect &&
+    LANGUAGE=is LC_ALL="$is_IS_iso_locale" gettext "TEST: Hello World!" >actual &&
+    test_cmp expect actual
+'
+
+test_expect_success GETTEXT_ISO_LOCALE 'gettext: impossible ISO-8859-1 output' '
+	LANGUAGE=is LC_ALL="$is_IS_iso_locale" gettext "$MSGKEY" >runes &&
+	case "$(cat runes)" in
+	"$MSGKEY")
+		say "Your system gives back the key to message catalog"
+		;;
+	"$PUNTS")
+		say "Your system replaces an impossible character with ?"
+		;;
+	"$RUNES")
+		say "Your system gives back the raw message for an impossible request"
+		;;
+	*)
+		say "We never saw the error behaviour your system exhibits"
+		false
+		;;
+	esac
+'
+
+test_expect_success GETTEXT_LOCALE 'gettext: Fetching a UTF-8 msgid -> UTF-8' '
+    printf "TILRAUN: ‚einfaldar‘ og „tvöfaldar“ gæsalappir" >expect &&
+    LANGUAGE=is LC_ALL="$is_IS_locale" gettext "TEST: ‘single’ and “double” quotes" >actual &&
+    test_cmp expect actual
+'
+
+# How these quotes get transliterated depends on the gettext implementation:
+#
+#   Debian:  ,einfaldar' og ,,tvöfaldar" [GNU libintl]
+#   FreeBSD: `einfaldar` og "tvöfaldar"  [GNU libintl]
+#   Solaris: ?einfaldar? og ?tvöfaldar?  [Solaris libintl]
+#
+# Just make sure the contents are transliterated, and don't use grep -q
+# so that these differences are emitted under --verbose for curious
+# eyes.
+test_expect_success GETTEXT_ISO_LOCALE 'gettext: Fetching a UTF-8 msgid -> ISO-8859-1' '
+    LANGUAGE=is LC_ALL="$is_IS_iso_locale" gettext "TEST: ‘single’ and “double” quotes" >actual &&
+    grep "einfaldar" actual &&
+    grep "$(echo tvöfaldar | iconv -f UTF-8 -t ISO8859-1)" actual
+'
+
+test_expect_success GETTEXT_LOCALE 'gettext.c: git init UTF-8 -> UTF-8' '
+    printf "Bjó til tóma Git lind" >expect &&
+    LANGUAGE=is LC_ALL="$is_IS_locale" git init repo >actual &&
+    test_when_finished "rm -rf repo" &&
+    grep "^$(cat expect) " actual
+'
+
+test_expect_success GETTEXT_ISO_LOCALE 'gettext.c: git init UTF-8 -> ISO-8859-1' '
+    printf "Bjó til tóma Git lind" >expect &&
+    LANGUAGE=is LC_ALL="$is_IS_iso_locale" git init repo >actual &&
+    test_when_finished "rm -rf repo" &&
+    grep "^$(cat expect | iconv -f UTF-8 -t ISO8859-1) " actual
+'
+
+test_done
diff --git a/t/t0205-gettext-poison.sh b/t/t0205-gettext-poison.sh
new file mode 100755
index 0000000..2361590
--- /dev/null
+++ b/t/t0205-gettext-poison.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Ævar Arnfjörð Bjarmason
+#
+
+test_description='Gettext Shell poison'
+
+. ./lib-gettext.sh
+
+test_expect_success GETTEXT_POISON "sanity: \$GIT_INTERNAL_GETTEXT_SH_SCHEME is set (to $GIT_INTERNAL_GETTEXT_SH_SCHEME)" '
+    test -n "$GIT_INTERNAL_GETTEXT_SH_SCHEME"
+'
+
+test_expect_success GETTEXT_POISON 'sanity: $GIT_INTERNAL_GETTEXT_SH_SCHEME" is poison' '
+    test "$GIT_INTERNAL_GETTEXT_SH_SCHEME" = "poison"
+'
+
+test_expect_success GETTEXT_POISON 'gettext: our gettext() fallback has poison semantics' '
+    printf "# GETTEXT POISON #" >expect &&
+    gettext "test" >actual &&
+    test_cmp expect actual &&
+    printf "# GETTEXT POISON #" >expect &&
+    gettext "test more words" >actual &&
+    test_cmp expect actual
+'
+
+test_expect_success GETTEXT_POISON 'eval_gettext: our eval_gettext() fallback has poison semantics' '
+    printf "# GETTEXT POISON #" >expect &&
+    eval_gettext "test" >actual &&
+    test_cmp expect actual &&
+    printf "# GETTEXT POISON #" >expect &&
+    eval_gettext "test more words" >actual &&
+    test_cmp expect actual
+'
+
+test_done
diff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh
new file mode 100755
index 0000000..538ea5f
--- /dev/null
+++ b/t/t0300-credentials.sh
@@ -0,0 +1,292 @@
+#!/bin/sh
+
+test_description='basic credential helper tests'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-credential.sh
+
+test_expect_success 'setup helper scripts' '
+	cat >dump <<-\EOF &&
+	whoami=`echo $0 | sed s/.*git-credential-//`
+	echo >&2 "$whoami: $*"
+	OIFS=$IFS
+	IFS==
+	while read key value; do
+		echo >&2 "$whoami: $key=$value"
+		eval "$key=$value"
+	done
+	IFS=$OIFS
+	EOF
+
+	write_script git-credential-useless <<-\EOF &&
+	. ./dump
+	exit 0
+	EOF
+
+	write_script git-credential-verbatim <<-\EOF &&
+	user=$1; shift
+	pass=$1; shift
+	. ./dump
+	test -z "$user" || echo username=$user
+	test -z "$pass" || echo password=$pass
+	EOF
+
+	PATH="$PWD:$PATH"
+'
+
+test_expect_success 'credential_fill invokes helper' '
+	check fill "verbatim foo bar" <<-\EOF
+	--
+	username=foo
+	password=bar
+	--
+	verbatim: get
+	EOF
+'
+
+test_expect_success 'credential_fill invokes multiple helpers' '
+	check fill useless "verbatim foo bar" <<-\EOF
+	--
+	username=foo
+	password=bar
+	--
+	useless: get
+	verbatim: get
+	EOF
+'
+
+test_expect_success 'credential_fill stops when we get a full response' '
+	check fill "verbatim one two" "verbatim three four" <<-\EOF
+	--
+	username=one
+	password=two
+	--
+	verbatim: get
+	EOF
+'
+
+test_expect_success 'credential_fill continues through partial response' '
+	check fill "verbatim one \"\"" "verbatim two three" <<-\EOF
+	--
+	username=two
+	password=three
+	--
+	verbatim: get
+	verbatim: get
+	verbatim: username=one
+	EOF
+'
+
+test_expect_success 'credential_fill passes along metadata' '
+	check fill "verbatim one two" <<-\EOF
+	protocol=ftp
+	host=example.com
+	path=foo.git
+	--
+	protocol=ftp
+	host=example.com
+	path=foo.git
+	username=one
+	password=two
+	--
+	verbatim: get
+	verbatim: protocol=ftp
+	verbatim: host=example.com
+	verbatim: path=foo.git
+	EOF
+'
+
+test_expect_success 'credential_approve calls all helpers' '
+	check approve useless "verbatim one two" <<-\EOF
+	username=foo
+	password=bar
+	--
+	--
+	useless: store
+	useless: username=foo
+	useless: password=bar
+	verbatim: store
+	verbatim: username=foo
+	verbatim: password=bar
+	EOF
+'
+
+test_expect_success 'do not bother storing password-less credential' '
+	check approve useless <<-\EOF
+	username=foo
+	--
+	--
+	EOF
+'
+
+
+test_expect_success 'credential_reject calls all helpers' '
+	check reject useless "verbatim one two" <<-\EOF
+	username=foo
+	password=bar
+	--
+	--
+	useless: erase
+	useless: username=foo
+	useless: password=bar
+	verbatim: erase
+	verbatim: username=foo
+	verbatim: password=bar
+	EOF
+'
+
+test_expect_success 'usernames can be preserved' '
+	check fill "verbatim \"\" three" <<-\EOF
+	username=one
+	--
+	username=one
+	password=three
+	--
+	verbatim: get
+	verbatim: username=one
+	EOF
+'
+
+test_expect_success 'usernames can be overridden' '
+	check fill "verbatim two three" <<-\EOF
+	username=one
+	--
+	username=two
+	password=three
+	--
+	verbatim: get
+	verbatim: username=one
+	EOF
+'
+
+test_expect_success 'do not bother completing already-full credential' '
+	check fill "verbatim three four" <<-\EOF
+	username=one
+	password=two
+	--
+	username=one
+	password=two
+	--
+	EOF
+'
+
+# We can't test the basic terminal password prompt here because
+# getpass() tries too hard to find the real terminal. But if our
+# askpass helper is run, we know the internal getpass is working.
+test_expect_success 'empty helper list falls back to internal getpass' '
+	check fill <<-\EOF
+	--
+	username=askpass-username
+	password=askpass-password
+	--
+	askpass: Username:
+	askpass: Password:
+	EOF
+'
+
+test_expect_success 'internal getpass does not ask for known username' '
+	check fill <<-\EOF
+	username=foo
+	--
+	username=foo
+	password=askpass-password
+	--
+	askpass: Password:
+	EOF
+'
+
+HELPER="!f() {
+		cat >/dev/null
+		echo username=foo
+		echo password=bar
+	}; f"
+test_expect_success 'respect configured credentials' '
+	test_config credential.helper "$HELPER" &&
+	check fill <<-\EOF
+	--
+	username=foo
+	password=bar
+	--
+	EOF
+'
+
+test_expect_success 'match configured credential' '
+	test_config credential.https://example.com.helper "$HELPER" &&
+	check fill <<-\EOF
+	protocol=https
+	host=example.com
+	path=repo.git
+	--
+	protocol=https
+	host=example.com
+	username=foo
+	password=bar
+	--
+	EOF
+'
+
+test_expect_success 'do not match configured credential' '
+	test_config credential.https://foo.helper "$HELPER" &&
+	check fill <<-\EOF
+	protocol=https
+	host=bar
+	--
+	protocol=https
+	host=bar
+	username=askpass-username
+	password=askpass-password
+	--
+	askpass: Username for '\''https://bar'\'':
+	askpass: Password for '\''https://askpass-username@bar'\'':
+	EOF
+'
+
+test_expect_success 'pull username from config' '
+	test_config credential.https://example.com.username foo &&
+	check fill <<-\EOF
+	protocol=https
+	host=example.com
+	--
+	protocol=https
+	host=example.com
+	username=foo
+	password=askpass-password
+	--
+	askpass: Password for '\''https://foo@example.com'\'':
+	EOF
+'
+
+test_expect_success 'http paths can be part of context' '
+	check fill "verbatim foo bar" <<-\EOF &&
+	protocol=https
+	host=example.com
+	path=foo.git
+	--
+	protocol=https
+	host=example.com
+	username=foo
+	password=bar
+	--
+	verbatim: get
+	verbatim: protocol=https
+	verbatim: host=example.com
+	EOF
+	test_config credential.https://example.com.useHttpPath true &&
+	check fill "verbatim foo bar" <<-\EOF
+	protocol=https
+	host=example.com
+	path=foo.git
+	--
+	protocol=https
+	host=example.com
+	path=foo.git
+	username=foo
+	password=bar
+	--
+	verbatim: get
+	verbatim: protocol=https
+	verbatim: host=example.com
+	verbatim: path=foo.git
+	EOF
+'
+
+test_done
diff --git a/t/t0301-credential-cache.sh b/t/t0301-credential-cache.sh
new file mode 100755
index 0000000..82c8411
--- /dev/null
+++ b/t/t0301-credential-cache.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+test_description='credential-cache tests'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-credential.sh
+
+test -z "$NO_UNIX_SOCKETS" || {
+	skip_all='skipping credential-cache tests, unix sockets not available'
+	test_done
+}
+
+# don't leave a stale daemon running
+trap 'code=$?; git credential-cache exit; (exit $code); die' EXIT
+
+helper_test cache
+helper_test_timeout cache --timeout=1
+
+# we can't rely on our "trap" above working after test_done,
+# as test_done will delete the trash directory containing
+# our socket, leaving us with no way to access the daemon.
+git credential-cache exit
+
+test_done
diff --git a/t/t0302-credential-store.sh b/t/t0302-credential-store.sh
new file mode 100755
index 0000000..f61b40c
--- /dev/null
+++ b/t/t0302-credential-store.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+test_description='credential-store tests'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-credential.sh
+
+helper_test store
+
+test_done
diff --git a/t/t0303-credential-external.sh b/t/t0303-credential-external.sh
new file mode 100755
index 0000000..f028fd1
--- /dev/null
+++ b/t/t0303-credential-external.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+test_description='external credential helper tests
+
+This is a tool for authors of external helper tools to sanity-check
+their helpers. If you have written the "git-credential-foo" helper,
+you check it with:
+
+  make GIT_TEST_CREDENTIAL_HELPER=foo t0303-credential-external.sh
+
+This assumes that your helper is capable of both storing and
+retrieving credentials (some helpers may be read-only, and they will
+fail these tests).
+
+Please note that the individual tests do not verify all of the
+preconditions themselves, but rather build on each other. A failing
+test means that tests later in the sequence can return false "OK"
+results.
+
+If your helper supports time-based expiration with a configurable
+timeout, you can test that feature with:
+
+  make GIT_TEST_CREDENTIAL_HELPER=foo \
+       GIT_TEST_CREDENTIAL_HELPER_TIMEOUT="foo --timeout=1" \
+       t0303-credential-external.sh
+
+If your helper requires additional setup before the tests are started,
+you can set GIT_TEST_CREDENTIAL_HELPER_SETUP to a sequence of shell
+commands.
+'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-credential.sh
+
+if test -z "$GIT_TEST_CREDENTIAL_HELPER"; then
+	skip_all="used to test external credential helpers"
+	test_done
+fi
+
+test -z "$GIT_TEST_CREDENTIAL_HELPER_SETUP" ||
+	eval "$GIT_TEST_CREDENTIAL_HELPER_SETUP"
+
+# clean before the test in case there is cruft left
+# over from a previous run that would impact results
+helper_test_clean "$GIT_TEST_CREDENTIAL_HELPER"
+
+helper_test "$GIT_TEST_CREDENTIAL_HELPER"
+
+if test -z "$GIT_TEST_CREDENTIAL_HELPER_TIMEOUT"; then
+	say "# skipping timeout tests (GIT_TEST_CREDENTIAL_HELPER_TIMEOUT not set)"
+else
+	helper_test_timeout "$GIT_TEST_CREDENTIAL_HELPER_TIMEOUT"
+fi
+
+# clean afterwards so that we are good citizens
+# and don't leave cruft in the helper's storage, which
+# might be long-term system storage
+helper_test_clean "$GIT_TEST_CREDENTIAL_HELPER"
+
+test_done
diff --git a/t/t1000-read-tree-m-3way.sh b/t/t1000-read-tree-m-3way.sh
index 22ba7a5..babcdd2 100755
--- a/t/t1000-read-tree-m-3way.sh
+++ b/t/t1000-read-tree-m-3way.sh
@@ -72,6 +72,7 @@
 
 '
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-read-tree.sh
 . "$TEST_DIRECTORY"/lib-read-tree-m-3way.sh
 
 ################################################################
@@ -126,9 +127,6 @@
 100644 X 0	Z/NN
 EOF
 
-_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
-_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
-
 check_result () {
     git ls-files --stage | sed -e 's/ '"$_x40"' / X /' >current &&
     test_cmp expected current
@@ -140,7 +138,7 @@
     '3-way merge with git read-tree -m, empty cache' \
     "rm -fr [NDMALTS][NDMALTSF] Z &&
      rm .git/index &&
-     git read-tree -m $tree_O $tree_A $tree_B &&
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
      check_result"
 
 # This starts out with the first head, which is the normal
@@ -149,9 +147,9 @@
     '3-way merge with git read-tree -m, match H' \
     "rm -fr [NDMALTS][NDMALTSF] Z &&
      rm .git/index &&
-     git read-tree $tree_A &&
+     read_tree_must_succeed $tree_A &&
      git checkout-index -f -u -a &&
-     git read-tree -m $tree_O $tree_A $tree_B &&
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
      check_result"
 
 : <<\END_OF_CASE_TABLE
@@ -214,7 +212,7 @@
      rm -f .git/index XX &&
      echo XX >XX &&
      git update-index --add XX &&
-     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+     read_tree_must_fail -m $tree_O $tree_A $tree_B
 "
 
 test_expect_success \
@@ -222,7 +220,7 @@
     "rm -f .git/index NA &&
      cp .orig-B/NA NA &&
      git update-index --add NA &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B"
 
 test_expect_success \
     '2 - matching B alone is OK in !O && !A && B case.' \
@@ -230,14 +228,14 @@
      cp .orig-B/NA NA &&
      git update-index --add NA &&
      echo extra >>NA &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B"
 
 test_expect_success \
     '3 - must match A in !O && A && !B case.' \
     "rm -f .git/index AN &&
      cp .orig-A/AN AN &&
      git update-index --add AN &&
-     git read-tree -m $tree_O $tree_A $tree_B &&
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
      check_result"
 
 test_expect_success \
@@ -246,7 +244,7 @@
      cp .orig-A/AN AN &&
      git update-index --add AN &&
      echo extra >>AN &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B"
 
 test_expect_success \
     '3 (fail) - must match A in !O && A && !B case.' "
@@ -254,7 +252,7 @@
      cp .orig-A/AN AN &&
      echo extra >>AN &&
      git update-index --add AN &&
-     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+     read_tree_must_fail -m $tree_O $tree_A $tree_B
 "
 
 test_expect_success \
@@ -262,7 +260,7 @@
     "rm -f .git/index AA &&
      cp .orig-A/AA AA &&
      git update-index --add AA &&
-     git read-tree -m $tree_O $tree_A $tree_B &&
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
      check_result"
 
 test_expect_success \
@@ -271,7 +269,7 @@
      cp .orig-A/AA AA &&
      git update-index --add AA &&
      echo extra >>AA &&
-     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+     read_tree_must_fail -m $tree_O $tree_A $tree_B
 "
 
 test_expect_success \
@@ -280,7 +278,7 @@
      cp .orig-A/AA AA &&
      echo extra >>AA &&
      git update-index --add AA &&
-     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+     read_tree_must_fail -m $tree_O $tree_A $tree_B
 "
 
 test_expect_success \
@@ -288,7 +286,7 @@
     "rm -f .git/index LL &&
      cp .orig-A/LL LL &&
      git update-index --add LL &&
-     git read-tree -m $tree_O $tree_A $tree_B &&
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
      check_result"
 
 test_expect_success \
@@ -297,7 +295,7 @@
      cp .orig-A/LL LL &&
      git update-index --add LL &&
      echo extra >>LL &&
-     git read-tree -m $tree_O $tree_A $tree_B &&
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
      check_result"
 
 test_expect_success \
@@ -306,15 +304,15 @@
      cp .orig-A/LL LL &&
      echo extra >>LL &&
      git update-index --add LL &&
-     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+     read_tree_must_fail -m $tree_O $tree_A $tree_B
 "
 
 test_expect_success \
     '6 - must not exist in O && !A && !B case' "
      rm -f .git/index DD &&
-     echo DD >DD
+     echo DD >DD &&
      git update-index --add DD &&
-     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+     read_tree_must_fail -m $tree_O $tree_A $tree_B
 "
 
 test_expect_success \
@@ -322,7 +320,7 @@
      rm -f .git/index DM &&
      cp .orig-B/DM DM &&
      git update-index --add DM &&
-     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+     read_tree_must_fail -m $tree_O $tree_A $tree_B
 "
 
 test_expect_success \
@@ -330,7 +328,7 @@
      rm -f .git/index DN &&
      cp .orig-B/DN DN &&
      git update-index --add DN &&
-     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+     read_tree_must_fail -m $tree_O $tree_A $tree_B
 "
 
 test_expect_success \
@@ -338,7 +336,7 @@
     "rm -f .git/index MD &&
      cp .orig-A/MD MD &&
      git update-index --add MD &&
-     git read-tree -m $tree_O $tree_A $tree_B &&
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
      check_result"
 
 test_expect_success \
@@ -347,7 +345,7 @@
      cp .orig-A/MD MD &&
      git update-index --add MD &&
      echo extra >>MD &&
-     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+     read_tree_must_fail -m $tree_O $tree_A $tree_B
 "
 
 test_expect_success \
@@ -356,7 +354,7 @@
      cp .orig-A/MD MD &&
      echo extra >>MD &&
      git update-index --add MD &&
-     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+     read_tree_must_fail -m $tree_O $tree_A $tree_B
 "
 
 test_expect_success \
@@ -364,7 +362,7 @@
     "rm -f .git/index ND &&
      cp .orig-A/ND ND &&
      git update-index --add ND &&
-     git read-tree -m $tree_O $tree_A $tree_B &&
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
      check_result"
 
 test_expect_success \
@@ -373,7 +371,7 @@
      cp .orig-A/ND ND &&
      git update-index --add ND &&
      echo extra >>ND &&
-     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+     read_tree_must_fail -m $tree_O $tree_A $tree_B
 "
 
 test_expect_success \
@@ -382,7 +380,7 @@
      cp .orig-A/ND ND &&
      echo extra >>ND &&
      git update-index --add ND &&
-     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+     read_tree_must_fail -m $tree_O $tree_A $tree_B
 "
 
 test_expect_success \
@@ -390,7 +388,7 @@
     "rm -f .git/index MM &&
      cp .orig-A/MM MM &&
      git update-index --add MM &&
-     git read-tree -m $tree_O $tree_A $tree_B &&
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
      check_result"
 
 test_expect_success \
@@ -399,7 +397,7 @@
      cp .orig-A/MM MM &&
      git update-index --add MM &&
      echo extra >>MM &&
-     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+     read_tree_must_fail -m $tree_O $tree_A $tree_B
 "
 
 test_expect_success \
@@ -408,7 +406,7 @@
      cp .orig-A/MM MM &&
      echo extra >>MM &&
      git update-index --add MM &&
-     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+     read_tree_must_fail -m $tree_O $tree_A $tree_B
 "
 
 test_expect_success \
@@ -416,7 +414,7 @@
     "rm -f .git/index SS &&
      cp .orig-A/SS SS &&
      git update-index --add SS &&
-     git read-tree -m $tree_O $tree_A $tree_B &&
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
      check_result"
 
 test_expect_success \
@@ -425,7 +423,7 @@
      cp .orig-A/SS SS &&
      git update-index --add SS &&
      echo extra >>SS &&
-     git read-tree -m $tree_O $tree_A $tree_B &&
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
      check_result"
 
 test_expect_success \
@@ -434,7 +432,7 @@
      cp .orig-A/SS SS &&
      echo extra >>SS &&
      git update-index --add SS &&
-     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+     read_tree_must_fail -m $tree_O $tree_A $tree_B
 "
 
 test_expect_success \
@@ -442,7 +440,7 @@
     "rm -f .git/index MN &&
      cp .orig-A/MN MN &&
      git update-index --add MN &&
-     git read-tree -m $tree_O $tree_A $tree_B &&
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
      check_result"
 
 test_expect_success \
@@ -451,7 +449,7 @@
      cp .orig-A/MN MN &&
      git update-index --add MN &&
      echo extra >>MN &&
-     git read-tree -m $tree_O $tree_A $tree_B &&
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
      check_result"
 
 test_expect_success \
@@ -459,7 +457,7 @@
     "rm -f .git/index NM &&
      cp .orig-A/NM NM &&
      git update-index --add NM &&
-     git read-tree -m $tree_O $tree_A $tree_B &&
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
      check_result"
 
 test_expect_success \
@@ -468,7 +466,7 @@
      cp .orig-B/NM NM &&
      git update-index --add NM &&
      echo extra >>NM &&
-     git read-tree -m $tree_O $tree_A $tree_B &&
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
      check_result"
 
 test_expect_success \
@@ -477,7 +475,7 @@
      cp .orig-A/NM NM &&
      git update-index --add NM &&
      echo extra >>NM &&
-     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+     read_tree_must_fail -m $tree_O $tree_A $tree_B
 "
 
 test_expect_success \
@@ -486,7 +484,7 @@
      cp .orig-A/NM NM &&
      echo extra >>NM &&
      git update-index --add NM &&
-     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+     read_tree_must_fail -m $tree_O $tree_A $tree_B
 "
 
 test_expect_success \
@@ -494,7 +492,7 @@
     "rm -f .git/index NN &&
      cp .orig-A/NN NN &&
      git update-index --add NN &&
-     git read-tree -m $tree_O $tree_A $tree_B &&
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
      check_result"
 
 test_expect_success \
@@ -503,7 +501,7 @@
      cp .orig-A/NN NN &&
      git update-index --add NN &&
      echo extra >>NN &&
-     git read-tree -m $tree_O $tree_A $tree_B &&
+     read_tree_must_succeed -m $tree_O $tree_A $tree_B &&
      check_result"
 
 test_expect_success \
@@ -512,7 +510,7 @@
      cp .orig-A/NN NN &&
      echo extra >>NN &&
      git update-index --add NN &&
-     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+     read_tree_must_fail -m $tree_O $tree_A $tree_B
 "
 
 # #16
@@ -525,7 +523,7 @@
     echo E16 >F16 &&
     git update-index F16 &&
     tree1=`git write-tree` &&
-    git read-tree -m $tree0 $tree1 $tree1 $tree0 &&
+    read_tree_must_succeed -m $tree0 $tree1 $tree1 $tree0 &&
     git ls-files --stage'
 
 test_done
diff --git a/t/t1001-read-tree-m-2way.sh b/t/t1001-read-tree-m-2way.sh
index 271bc4e..acaab07 100755
--- a/t/t1001-read-tree-m-2way.sh
+++ b/t/t1001-read-tree-m-2way.sh
@@ -5,7 +5,7 @@
 
 test_description='Two way merge with read-tree -m $H $M
 
-This test tries two-way merge (aka fast forward with carry forward).
+This test tries two-way merge (aka fast-forward with carry forward).
 
 There is the head (called H) and another commit (called M), which is
 simply ahead of H.  The index and the work tree contains a state that
@@ -21,13 +21,12 @@
         yomin   - not in H nor M
 '
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-read-tree.sh
 
 read_tree_twoway () {
     git read-tree -m "$1" "$2" && git ls-files --stage
 }
 
-_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
-_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
 compare_change () {
 	sed -n >current \
 	    -e '/^--- /d; /^+++ /d; /^@@ /d;' \
@@ -51,7 +50,7 @@
 }
 
 cat >bozbar-old <<\EOF
-This is a sample file used in two-way fast forward merge
+This is a sample file used in two-way fast-forward merge
 tests.  Its second line ends with a magic word bozbar
 which will be modified by the merged head to gnusto.
 It has some extra lines so that external tools can
@@ -96,33 +95,33 @@
 test_expect_success \
     '4 - carry forward local addition.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      git update-index --add yomin &&
      read_tree_twoway $treeH $treeM &&
-     git ls-files --stage >4.out || return 1
-     git diff --no-index M.out 4.out >4diff.out
+     git ls-files --stage >4.out &&
+     test_must_fail git diff --no-index M.out 4.out >4diff.out &&
      compare_change 4diff.out expected &&
      check_cache_at yomin clean'
 
 test_expect_success \
     '5 - carry forward local addition.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      echo yomin >yomin &&
      git update-index --add yomin &&
      echo yomin yomin >yomin &&
      read_tree_twoway $treeH $treeM &&
-     git ls-files --stage >5.out || return 1
-     git diff --no-index M.out 5.out >5diff.out
+     git ls-files --stage >5.out &&
+     test_must_fail git diff --no-index M.out 5.out >5diff.out &&
      compare_change 5diff.out expected &&
      check_cache_at yomin dirty'
 
 test_expect_success \
     '6 - local addition already has the same.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      git update-index --add frotz &&
      read_tree_twoway $treeH $treeM &&
@@ -133,7 +132,7 @@
 test_expect_success \
     '7 - local addition already has the same.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      echo frotz >frotz &&
      git update-index --add frotz &&
@@ -146,7 +145,7 @@
 test_expect_success \
     '8 - conflicting addition.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      echo frotz frotz >frotz &&
      git update-index --add frotz &&
@@ -155,7 +154,7 @@
 test_expect_success \
     '9 - conflicting addition.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      echo frotz frotz >frotz &&
      git update-index --add frotz &&
@@ -165,7 +164,7 @@
 test_expect_success \
     '10 - path removed.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      echo rezrov >rezrov &&
      git update-index --add rezrov &&
@@ -176,7 +175,7 @@
 test_expect_success \
     '11 - dirty path removed.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      echo rezrov >rezrov &&
      git update-index --add rezrov &&
@@ -186,7 +185,7 @@
 test_expect_success \
     '12 - unmatching local changes being removed.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      echo rezrov rezrov >rezrov &&
      git update-index --add rezrov &&
@@ -195,7 +194,7 @@
 test_expect_success \
     '13 - unmatching local changes being removed.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      echo rezrov rezrov >rezrov &&
      git update-index --add rezrov &&
@@ -210,34 +209,34 @@
 test_expect_success \
     '14 - unchanged in two heads.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      echo nitfol nitfol >nitfol &&
      git update-index --add nitfol &&
      read_tree_twoway $treeH $treeM &&
-     git ls-files --stage >14.out || return 1
-     git diff --no-index M.out 14.out >14diff.out
+     git ls-files --stage >14.out &&
+     test_must_fail git diff --no-index M.out 14.out >14diff.out &&
      compare_change 14diff.out expected &&
      check_cache_at nitfol clean'
 
 test_expect_success \
     '15 - unchanged in two heads.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      echo nitfol nitfol >nitfol &&
      git update-index --add nitfol &&
      echo nitfol nitfol nitfol >nitfol &&
      read_tree_twoway $treeH $treeM &&
-     git ls-files --stage >15.out || return 1
-     git diff --no-index M.out 15.out >15diff.out
+     git ls-files --stage >15.out &&
+     test_must_fail git diff --no-index M.out 15.out >15diff.out &&
      compare_change 15diff.out expected &&
      check_cache_at nitfol dirty'
 
 test_expect_success \
     '16 - conflicting local change.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      echo bozbar bozbar >bozbar &&
      git update-index --add bozbar &&
@@ -246,7 +245,7 @@
 test_expect_success \
     '17 - conflicting local change.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      echo bozbar bozbar >bozbar &&
      git update-index --add bozbar &&
@@ -256,7 +255,7 @@
 test_expect_success \
     '18 - local change already having a good result.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      cat bozbar-new >bozbar &&
      git update-index --add bozbar &&
@@ -268,7 +267,7 @@
 test_expect_success \
     '19 - local change already having a good result, further modified.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      cat bozbar-new >bozbar &&
      git update-index --add bozbar &&
@@ -281,7 +280,7 @@
 test_expect_success \
     '20 - no local change, use new tree.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      cat bozbar-old >bozbar &&
      git update-index --add bozbar &&
@@ -293,18 +292,18 @@
 test_expect_success \
     '21 - no local change, dirty cache.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      cat bozbar-old >bozbar &&
      git update-index --add bozbar &&
      echo gnusto gnusto >bozbar &&
      if read_tree_twoway $treeH $treeM; then false; else :; fi'
 
-# This fails with straight two-way fast forward.
+# This fails with straight two-way fast-forward.
 test_expect_success \
     '22 - local change cache updated.' \
     'rm -f .git/index &&
-     git read-tree $treeH &&
+     read_tree_must_succeed $treeH &&
      git checkout-index -u -f -q -a &&
      sed -e "s/such as/SUCH AS/" bozbar-old >bozbar &&
      git update-index --add bozbar &&
@@ -361,7 +360,7 @@
 
 test_expect_success \
     'a/b (untracked) vs a, plus c/d case test.' \
-    '! git read-tree -u -m "$treeH" "$treeM" &&
+    'read_tree_u_must_fail -u -m "$treeH" "$treeM" &&
      git ls-files --stage &&
      test -f a/b'
 
@@ -379,7 +378,7 @@
      git ls-files --stage >treeM.out &&
 
      rm -f a &&
-     mkdir a
+     mkdir a &&
      : >a/b &&
      git update-index --add --remove a a/b &&
      treeH=`git write-tree` &&
@@ -388,8 +387,24 @@
 
 test_expect_success \
     'a/b vs a, plus c/d case test.' \
-    'git read-tree -u -m "$treeH" "$treeM" &&
+    'read_tree_u_must_succeed -u -m "$treeH" "$treeM" &&
      git ls-files --stage | tee >treeMcheck.out &&
      test_cmp treeM.out treeMcheck.out'
 
+test_expect_success '-m references the correct modified tree' '
+	echo >file-a &&
+	echo >file-b &&
+	git add file-a file-b &&
+	git commit -a -m "test for correct modified tree" &&
+	git branch initial-mod &&
+	echo b >file-b &&
+	git commit -a -m "B" &&
+	echo a >file-a &&
+	git add file-a &&
+	git ls-tree $(git write-tree) file-a >expect &&
+	read_tree_must_succeed -m HEAD initial-mod &&
+	git ls-tree $(git write-tree) file-a >actual &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t1002-read-tree-m-u-2way.sh b/t/t1002-read-tree-m-u-2way.sh
index 5e40cec..a847709 100755
--- a/t/t1002-read-tree-m-u-2way.sh
+++ b/t/t1002-read-tree-m-u-2way.sh
@@ -9,9 +9,8 @@
 
 '
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-read-tree.sh
 
-_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
-_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
 compare_change () {
 	sed >current \
 	    -e '1{/^diff --git /d;}' \
@@ -58,8 +57,8 @@
 test_expect_success \
     '1, 2, 3 - no carry forward' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
-     git read-tree -m -u $treeH $treeM &&
+     read_tree_u_must_succeed --reset -u $treeH &&
+     read_tree_u_must_succeed -m -u $treeH $treeM &&
      git ls-files --stage >1-3.out &&
      cmp M.out 1-3.out &&
      sum bozbar frotz nitfol >actual3.sum &&
@@ -71,11 +70,11 @@
 test_expect_success \
     '4 - carry forward local addition.' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
+     read_tree_u_must_succeed --reset -u $treeH &&
      echo "+100644 X 0	yomin" >expected &&
      echo yomin >yomin &&
      git update-index --add yomin &&
-     git read-tree -m -u $treeH $treeM &&
+     read_tree_u_must_succeed -m -u $treeH $treeM &&
      git ls-files --stage >4.out || return 1
      git diff -U0 --no-index M.out 4.out >4diff.out
      compare_change 4diff.out expected &&
@@ -89,12 +88,12 @@
 test_expect_success \
     '5 - carry forward local addition.' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
-     git read-tree -m -u $treeH &&
+     read_tree_u_must_succeed --reset -u $treeH &&
+     read_tree_u_must_succeed -m -u $treeH &&
      echo yomin >yomin &&
      git update-index --add yomin &&
      echo yomin yomin >yomin &&
-     git read-tree -m -u $treeH $treeM &&
+     read_tree_u_must_succeed -m -u $treeH $treeM &&
      git ls-files --stage >5.out || return 1
      git diff -U0 --no-index M.out 5.out >5diff.out
      compare_change 5diff.out expected &&
@@ -109,10 +108,10 @@
 test_expect_success \
     '6 - local addition already has the same.' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
+     read_tree_u_must_succeed --reset -u $treeH &&
      echo frotz >frotz &&
      git update-index --add frotz &&
-     git read-tree -m -u $treeH $treeM &&
+     read_tree_u_must_succeed -m -u $treeH $treeM &&
      git ls-files --stage >6.out &&
      test_cmp M.out 6.out &&
      check_cache_at frotz clean &&
@@ -125,11 +124,11 @@
 test_expect_success \
     '7 - local addition already has the same.' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
+     read_tree_u_must_succeed --reset -u $treeH &&
      echo frotz >frotz &&
      git update-index --add frotz &&
      echo frotz frotz >frotz &&
-     git read-tree -m -u $treeH $treeM &&
+     read_tree_u_must_succeed -m -u $treeH $treeM &&
      git ls-files --stage >7.out &&
      test_cmp M.out 7.out &&
      check_cache_at frotz dirty &&
@@ -143,27 +142,27 @@
 test_expect_success \
     '8 - conflicting addition.' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
+     read_tree_u_must_succeed --reset -u $treeH &&
      echo frotz frotz >frotz &&
      git update-index --add frotz &&
-     if git read-tree -m -u $treeH $treeM; then false; else :; fi'
+     if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
 
 test_expect_success \
     '9 - conflicting addition.' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
+     read_tree_u_must_succeed --reset -u $treeH &&
      echo frotz frotz >frotz &&
      git update-index --add frotz &&
      echo frotz >frotz &&
-     if git read-tree -m -u $treeH $treeM; then false; else :; fi'
+     if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
 
 test_expect_success \
     '10 - path removed.' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
+     read_tree_u_must_succeed --reset -u $treeH &&
      echo rezrov >rezrov &&
      git update-index --add rezrov &&
-     git read-tree -m -u $treeH $treeM &&
+     read_tree_u_must_succeed -m -u $treeH $treeM &&
      git ls-files --stage >10.out &&
      cmp M.out 10.out &&
      sum bozbar frotz nitfol >actual10.sum &&
@@ -172,28 +171,28 @@
 test_expect_success \
     '11 - dirty path removed.' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
+     read_tree_u_must_succeed --reset -u $treeH &&
      echo rezrov >rezrov &&
      git update-index --add rezrov &&
      echo rezrov rezrov >rezrov &&
-     if git read-tree -m -u $treeH $treeM; then false; else :; fi'
+     if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
 
 test_expect_success \
     '12 - unmatching local changes being removed.' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
+     read_tree_u_must_succeed --reset -u $treeH &&
      echo rezrov rezrov >rezrov &&
      git update-index --add rezrov &&
-     if git read-tree -m -u $treeH $treeM; then false; else :; fi'
+     if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
 
 test_expect_success \
     '13 - unmatching local changes being removed.' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
+     read_tree_u_must_succeed --reset -u $treeH &&
      echo rezrov rezrov >rezrov &&
      git update-index --add rezrov &&
      echo rezrov >rezrov &&
-     if git read-tree -m -u $treeH $treeM; then false; else :; fi'
+     if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
 
 cat >expected <<EOF
 -100644 X 0	nitfol
@@ -203,12 +202,12 @@
 test_expect_success \
     '14 - unchanged in two heads.' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
+     read_tree_u_must_succeed --reset -u $treeH &&
      echo nitfol nitfol >nitfol &&
      git update-index --add nitfol &&
-     git read-tree -m -u $treeH $treeM &&
-     git ls-files --stage >14.out || return 1
-     git diff -U0 --no-index M.out 14.out >14diff.out
+     read_tree_u_must_succeed -m -u $treeH $treeM &&
+     git ls-files --stage >14.out &&
+     test_must_fail git diff -U0 --no-index M.out 14.out >14diff.out &&
      compare_change 14diff.out expected &&
      sum bozbar frotz >actual14.sum &&
      grep -v nitfol M.sum > expected14.sum &&
@@ -223,13 +222,13 @@
 test_expect_success \
     '15 - unchanged in two heads.' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
+     read_tree_u_must_succeed --reset -u $treeH &&
      echo nitfol nitfol >nitfol &&
      git update-index --add nitfol &&
      echo nitfol nitfol nitfol >nitfol &&
-     git read-tree -m -u $treeH $treeM &&
-     git ls-files --stage >15.out || return 1
-     git diff -U0 --no-index M.out 15.out >15diff.out
+     read_tree_u_must_succeed -m -u $treeH $treeM &&
+     git ls-files --stage >15.out &&
+     test_must_fail git diff -U0 --no-index M.out 15.out >15diff.out &&
      compare_change 15diff.out expected &&
      check_cache_at nitfol dirty &&
      sum bozbar frotz >actual15.sum &&
@@ -244,27 +243,27 @@
 test_expect_success \
     '16 - conflicting local change.' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
+     read_tree_u_must_succeed --reset -u $treeH &&
      echo bozbar bozbar >bozbar &&
      git update-index --add bozbar &&
-     if git read-tree -m -u $treeH $treeM; then false; else :; fi'
+     if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
 
 test_expect_success \
     '17 - conflicting local change.' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
+     read_tree_u_must_succeed --reset -u $treeH &&
      echo bozbar bozbar >bozbar &&
      git update-index --add bozbar &&
      echo bozbar bozbar bozbar >bozbar &&
-     if git read-tree -m -u $treeH $treeM; then false; else :; fi'
+     if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
 
 test_expect_success \
     '18 - local change already having a good result.' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
+     read_tree_u_must_succeed --reset -u $treeH &&
      echo gnusto >bozbar &&
      git update-index --add bozbar &&
-     git read-tree -m -u $treeH $treeM &&
+     read_tree_u_must_succeed -m -u $treeH $treeM &&
      git ls-files --stage >18.out &&
      test_cmp M.out 18.out &&
      check_cache_at bozbar clean &&
@@ -274,11 +273,11 @@
 test_expect_success \
     '19 - local change already having a good result, further modified.' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
+     read_tree_u_must_succeed --reset -u $treeH &&
      echo gnusto >bozbar &&
      git update-index --add bozbar &&
      echo gnusto gnusto >bozbar &&
-     git read-tree -m -u $treeH $treeM &&
+     read_tree_u_must_succeed -m -u $treeH $treeM &&
      git ls-files --stage >19.out &&
      test_cmp M.out 19.out &&
      check_cache_at bozbar dirty &&
@@ -294,10 +293,10 @@
 test_expect_success \
     '20 - no local change, use new tree.' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
+     read_tree_u_must_succeed --reset -u $treeH &&
      echo bozbar >bozbar &&
      git update-index --add bozbar &&
-     git read-tree -m -u $treeH $treeM &&
+     read_tree_u_must_succeed -m -u $treeH $treeM &&
      git ls-files --stage >20.out &&
      test_cmp M.out 20.out &&
      check_cache_at bozbar clean &&
@@ -307,16 +306,16 @@
 test_expect_success \
     '21 - no local change, dirty cache.' \
     'rm -f .git/index nitfol bozbar rezrov frotz &&
-     git read-tree --reset -u $treeH &&
+     read_tree_u_must_succeed --reset -u $treeH &&
      echo bozbar >bozbar &&
      git update-index --add bozbar &&
      echo gnusto gnusto >bozbar &&
-     if git read-tree -m -u $treeH $treeM; then false; else :; fi'
+     if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
 
 # Also make sure we did not break DF vs DF/DF case.
 test_expect_success \
     'DF vs DF/DF case setup.' \
-    'rm -f .git/index
+    'rm -f .git/index &&
      echo DF >DF &&
      git update-index --add DF &&
      treeDF=`git write-tree` &&
@@ -338,7 +337,7 @@
      rm -fr DF &&
      echo DF >DF &&
      git update-index --add DF &&
-     git read-tree -m -u $treeDF $treeDFDF &&
+     read_tree_u_must_succeed -m -u $treeDF $treeDFDF &&
      git ls-files --stage >DFDFcheck.out &&
      test_cmp DFDF.out DFDFcheck.out &&
      check_cache_at DF/DF clean'
diff --git a/t/t1004-read-tree-m-u-wf.sh b/t/t1004-read-tree-m-u-wf.sh
index f19b4a2..b3ae7d5 100755
--- a/t/t1004-read-tree-m-u-wf.sh
+++ b/t/t1004-read-tree-m-u-wf.sh
@@ -3,6 +3,7 @@
 test_description='read-tree -m -u checks working tree files'
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-read-tree.sh
 
 # two-tree test
 
@@ -29,7 +30,7 @@
 
 	echo >file2 master creates untracked file2 &&
 	echo >subdir/file2 master creates untracked subdir/file2 &&
-	if err=`git read-tree -m -u master side 2>&1`
+	if err=`read_tree_u_must_succeed -m -u master side 2>&1`
 	then
 		echo should have complained
 		false
@@ -42,7 +43,7 @@
 
 test_expect_success 'two-way with incorrect --exclude-per-directory (1)' '
 
-	if err=`git read-tree -m --exclude-per-directory=.gitignore master side 2>&1`
+	if err=`read_tree_u_must_succeed -m --exclude-per-directory=.gitignore master side 2>&1`
 	then
 		echo should have complained
 		false
@@ -53,7 +54,7 @@
 
 test_expect_success 'two-way with incorrect --exclude-per-directory (2)' '
 
-	if err=`git read-tree -m -u --exclude-per-directory=foo --exclude-per-directory=.gitignore master side 2>&1`
+	if err=`read_tree_u_must_succeed -m -u --exclude-per-directory=foo --exclude-per-directory=.gitignore master side 2>&1`
 	then
 		echo should have complained
 		false
@@ -64,7 +65,7 @@
 
 test_expect_success 'two-way clobbering a ignored file' '
 
-	git read-tree -m -u --exclude-per-directory=.gitignore master side
+	read_tree_u_must_succeed -m -u --exclude-per-directory=.gitignore master side
 '
 
 rm -f .gitignore
@@ -84,7 +85,7 @@
 	echo >file2 file two is untracked on the master side &&
 	echo >subdir/file2 file two is untracked on the master side &&
 
-	git read-tree -m -u branch-point master side
+	read_tree_u_must_succeed -m -u branch-point master side
 '
 
 test_expect_success 'three-way not clobbering a working tree file' '
@@ -94,7 +95,7 @@
 	git checkout master &&
 	echo >file3 file three created in master, untracked &&
 	echo >subdir/file3 file three created in master, untracked &&
-	if err=`git read-tree -m -u branch-point master side 2>&1`
+	if err=`read_tree_u_must_succeed -m -u branch-point master side 2>&1`
 	then
 		echo should have complained
 		false
@@ -113,7 +114,7 @@
 	echo >file3 file three created in master, untracked &&
 	echo >subdir/file3 file three created in master, untracked &&
 
-	git read-tree -m -u --exclude-per-directory=.gitignore branch-point master side
+	read_tree_u_must_succeed -m -u --exclude-per-directory=.gitignore branch-point master side
 '
 
 test_expect_success '3-way not overwriting local changes (setup)' '
@@ -137,7 +138,7 @@
 	git reset --hard &&
 
 	echo >>file1 "local changes" &&
-	git read-tree -m -u branch-point side-a side-b &&
+	read_tree_u_must_succeed -m -u branch-point side-a side-b &&
 	grep "new line to be kept" file1 &&
 	grep "local changes" file1
 
@@ -151,7 +152,7 @@
 	git reset --hard &&
 
 	echo >>file2 "local changes" &&
-	test_must_fail git read-tree -m -u branch-point side-a side-b &&
+	read_tree_u_must_fail -m -u branch-point side-a side-b &&
 	! grep "new line to be kept" file2 &&
 	grep "local changes" file2
 
@@ -173,11 +174,11 @@
 	git add a/b &&
 	git commit -m "we add a/b" &&
 
-	git read-tree -m -u sym-a sym-a sym-b
+	read_tree_u_must_succeed -m -u sym-a sym-a sym-b
 
 '
 
-test_expect_success SYMLINKS 'funny symlink in work tree, un-unlink-able' '
+test_expect_success SYMLINKS,SANITY 'funny symlink in work tree, un-unlink-able' '
 
 	rm -fr a b &&
 	git reset --hard &&
@@ -209,7 +210,7 @@
 test_expect_success 'D/F' '
 
 	git checkout side-b &&
-	git read-tree -m -u branch-point side-b side-a &&
+	read_tree_u_must_succeed -m -u branch-point side-b side-a &&
 	git ls-files -u >actual &&
 	(
 		a=$(git rev-parse branch-point:subdir/file2)
diff --git a/t/t1005-read-tree-reset.sh b/t/t1005-read-tree-reset.sh
index 8499116..f53de79 100755
--- a/t/t1005-read-tree-reset.sh
+++ b/t/t1005-read-tree-reset.sh
@@ -3,6 +3,7 @@
 test_description='read-tree -u --reset'
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-read-tree.sh
 
 # two-tree test
 
@@ -22,13 +23,13 @@
 '
 
 test_expect_success 'reset should work' '
-  git read-tree -u --reset HEAD^ &&
+  read_tree_u_must_succeed -u --reset HEAD^ &&
   git ls-files >actual &&
   test_cmp expect actual
 '
 
 test_expect_success 'reset should remove remnants from a failed merge' '
-  git read-tree --reset -u HEAD &&
+  read_tree_u_must_succeed --reset -u HEAD &&
   git ls-files -s >expect &&
   sha1=$(git rev-parse :new) &&
   (
@@ -37,13 +38,13 @@
   ) | git update-index --index-info &&
   >old &&
   git ls-files -s &&
-  git read-tree --reset -u HEAD &&
+  read_tree_u_must_succeed --reset -u HEAD &&
   git ls-files -s >actual &&
   ! test -f old
 '
 
 test_expect_success 'Porcelain reset should remove remnants too' '
-  git read-tree --reset -u HEAD &&
+  read_tree_u_must_succeed --reset -u HEAD &&
   git ls-files -s >expect &&
   sha1=$(git rev-parse :new) &&
   (
@@ -58,7 +59,7 @@
 '
 
 test_expect_success 'Porcelain checkout -f should remove remnants too' '
-  git read-tree --reset -u HEAD &&
+  read_tree_u_must_succeed --reset -u HEAD &&
   git ls-files -s >expect &&
   sha1=$(git rev-parse :new) &&
   (
@@ -73,7 +74,7 @@
 '
 
 test_expect_success 'Porcelain checkout -f HEAD should remove remnants too' '
-  git read-tree --reset -u HEAD &&
+  read_tree_u_must_succeed --reset -u HEAD &&
   git ls-files -s >expect &&
   sha1=$(git rev-parse :new) &&
   (
diff --git a/t/t1007-hash-object.sh b/t/t1007-hash-object.sh
index fd98e44..f83df8e 100755
--- a/t/t1007-hash-object.sh
+++ b/t/t1007-hash-object.sh
@@ -65,10 +65,6 @@
 	echo example | test_must_fail git hash-object --stdin-paths --path=foo
 '
 
-test_expect_success "Can't use --stdin-paths with --no-filters" '
-	echo example | test_must_fail git hash-object --stdin-paths --no-filters
-'
-
 test_expect_success "Can't use --path with --no-filters" '
 	test_must_fail git hash-object --no-filters --path=foo
 '
@@ -141,6 +137,20 @@
 	git config --unset core.autocrlf
 '
 
+test_expect_success 'check that --no-filters option works with --stdin-paths' '
+	echo fooQ | tr Q "\\015" >file0 &&
+	cp file0 file1 &&
+	echo "file0 -crlf" >.gitattributes &&
+	echo "file1 crlf" >>.gitattributes &&
+	git config core.autocrlf true &&
+	file0_sha=$(git hash-object file0) &&
+	file1_sha=$(git hash-object file1) &&
+	test "$file0_sha" != "$file1_sha" &&
+	nofilters_file1=$(echo "file1" | git hash-object --stdin-paths --no-filters) &&
+	test "$file0_sha" = "$nofilters_file1" &&
+	git config --unset core.autocrlf
+'
+
 pop_repo
 
 for args in "-w --stdin" "--stdin -w"; do
@@ -178,4 +188,17 @@
 	pop_repo
 done
 
+test_expect_success 'corrupt tree' '
+	echo abc >malformed-tree &&
+	test_must_fail git hash-object -t tree malformed-tree
+'
+
+test_expect_success 'corrupt commit' '
+	test_must_fail git hash-object -t commit --stdin </dev/null
+'
+
+test_expect_success 'corrupt tag' '
+	test_must_fail git hash-object -t tag --stdin </dev/null
+'
+
 test_done
diff --git a/t/t1008-read-tree-overlay.sh b/t/t1008-read-tree-overlay.sh
index f9e0028..4c50ed9 100755
--- a/t/t1008-read-tree-overlay.sh
+++ b/t/t1008-read-tree-overlay.sh
@@ -3,6 +3,7 @@
 test_description='test multi-tree read-tree without merging'
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-read-tree.sh
 
 test_expect_success setup '
 	echo one >a &&
@@ -21,7 +22,7 @@
 '
 
 test_expect_success 'multi-read' '
-	git read-tree initial master side &&
+	read_tree_must_succeed initial master side &&
 	(echo a; echo b/c) >expect &&
 	git ls-files >actual &&
 	test_cmp expect actual
diff --git a/t/t1009-read-tree-new-index.sh b/t/t1009-read-tree-new-index.sh
new file mode 100755
index 0000000..59b3aa4
--- /dev/null
+++ b/t/t1009-read-tree-new-index.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+test_description='test read-tree into a fresh index file'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+	echo one >a &&
+	git add a &&
+	git commit -m initial
+'
+
+test_expect_success 'non-existent index file' '
+	rm -f new-index &&
+	GIT_INDEX_FILE=new-index git read-tree master
+'
+
+test_expect_success 'empty index file' '
+	rm -f new-index &&
+	> new-index &&
+	GIT_INDEX_FILE=new-index git read-tree master
+'
+
+test_done
+
diff --git a/t/t1010-mktree.sh b/t/t1010-mktree.sh
index 9956e3a..df573c4 100755
--- a/t/t1010-mktree.sh
+++ b/t/t1010-mktree.sh
@@ -42,13 +42,13 @@
 '
 
 test_expect_success 'ls-tree output in wrong order given to mktree (1)' '
-	perl -e "print reverse <>" <top |
+	"$PERL_PATH" -e "print reverse <>" <top |
 	git mktree >actual &&
 	test_cmp tree actual
 '
 
 test_expect_success 'ls-tree output in wrong order given to mktree (2)' '
-	perl -e "print reverse <>" <top.withsub |
+	"$PERL_PATH" -e "print reverse <>" <top.withsub |
 	git mktree >actual &&
 	test_cmp tree.withsub actual
 '
@@ -58,14 +58,12 @@
 	test_cmp tree.missing actual
 '
 
-test_expect_failure 'mktree reads ls-tree -r output (1)' '
-	git mktree <all >actual &&
-	test_cmp tree actual
+test_expect_success 'mktree refuses to read ls-tree -r output (1)' '
+	test_must_fail git mktree <all >actual
 '
 
-test_expect_failure 'mktree reads ls-tree -r output (2)' '
-	git mktree <all.withsub >actual &&
-	test_cmp tree.withsub actual
+test_expect_success 'mktree refuses to read ls-tree -r output (2)' '
+	test_must_fail git mktree <all.withsub >actual
 '
 
 test_done
diff --git a/t/t1011-read-tree-sparse-checkout.sh b/t/t1011-read-tree-sparse-checkout.sh
new file mode 100755
index 0000000..5c0053a
--- /dev/null
+++ b/t/t1011-read-tree-sparse-checkout.sh
@@ -0,0 +1,253 @@
+#!/bin/sh
+
+test_description='sparse checkout tests
+
+* (tag: removed, master) removed
+| D	sub/added
+* (HEAD, tag: top) modified and added
+| M	init.t
+| A	sub/added
+* (tag: init) init
+  A	init.t
+'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-read-tree.sh
+
+test_expect_success 'setup' '
+	cat >expected <<-\EOF &&
+	100644 77f0ba1734ed79d12881f81b36ee134de6a3327b 0	init.t
+	100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0	sub/added
+	100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0	sub/addedtoo
+	100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0	subsub/added
+	EOF
+	cat >expected.swt <<-\EOF &&
+	H init.t
+	H sub/added
+	H sub/addedtoo
+	H subsub/added
+	EOF
+
+	test_commit init &&
+	echo modified >>init.t &&
+	mkdir sub subsub &&
+	touch sub/added sub/addedtoo subsub/added &&
+	git add init.t sub/added sub/addedtoo subsub/added &&
+	git commit -m "modified and added" &&
+	git tag top &&
+	git rm sub/added &&
+	git commit -m removed &&
+	git tag removed &&
+	git checkout top &&
+	git ls-files --stage >result &&
+	test_cmp expected result
+'
+
+test_expect_success 'read-tree without .git/info/sparse-checkout' '
+	read_tree_u_must_succeed -m -u HEAD &&
+	git ls-files --stage >result &&
+	test_cmp expected result &&
+	git ls-files -t >result &&
+	test_cmp expected.swt result
+'
+
+test_expect_success 'read-tree with .git/info/sparse-checkout but disabled' '
+	echo >.git/info/sparse-checkout &&
+	read_tree_u_must_succeed -m -u HEAD &&
+	git ls-files -t >result &&
+	test_cmp expected.swt result &&
+	test -f init.t &&
+	test -f sub/added
+'
+
+test_expect_success 'read-tree --no-sparse-checkout with empty .git/info/sparse-checkout and enabled' '
+	git config core.sparsecheckout true &&
+	echo >.git/info/sparse-checkout &&
+	read_tree_u_must_succeed --no-sparse-checkout -m -u HEAD &&
+	git ls-files -t >result &&
+	test_cmp expected.swt result &&
+	test -f init.t &&
+	test -f sub/added
+'
+
+test_expect_success 'read-tree with empty .git/info/sparse-checkout' '
+	git config core.sparsecheckout true &&
+	echo >.git/info/sparse-checkout &&
+	read_tree_u_must_fail -m -u HEAD &&
+	git ls-files --stage >result &&
+	test_cmp expected result &&
+	git ls-files -t >result &&
+	test_cmp expected.swt result &&
+	test -f init.t &&
+	test -f sub/added
+'
+
+test_expect_success 'match directories with trailing slash' '
+	cat >expected.swt-noinit <<-\EOF &&
+	S init.t
+	H sub/added
+	H sub/addedtoo
+	S subsub/added
+	EOF
+
+	echo sub/ > .git/info/sparse-checkout &&
+	read_tree_u_must_succeed -m -u HEAD &&
+	git ls-files -t > result &&
+	test_cmp expected.swt-noinit result &&
+	test ! -f init.t &&
+	test -f sub/added
+'
+
+test_expect_success 'match directories without trailing slash' '
+	echo sub >.git/info/sparse-checkout &&
+	read_tree_u_must_succeed -m -u HEAD &&
+	git ls-files -t >result &&
+	test_cmp expected.swt-noinit result &&
+	test ! -f init.t &&
+	test -f sub/added
+'
+
+test_expect_success 'match directories with negated patterns' '
+	cat >expected.swt-negation <<\EOF &&
+S init.t
+S sub/added
+H sub/addedtoo
+S subsub/added
+EOF
+
+	cat >.git/info/sparse-checkout <<\EOF &&
+sub
+!sub/added
+EOF
+	git read-tree -m -u HEAD &&
+	git ls-files -t >result &&
+	test_cmp expected.swt-negation result &&
+	test ! -f init.t &&
+	test ! -f sub/added &&
+	test -f sub/addedtoo
+'
+
+test_expect_success 'match directories with negated patterns (2)' '
+	cat >expected.swt-negation2 <<\EOF &&
+H init.t
+H sub/added
+S sub/addedtoo
+H subsub/added
+EOF
+
+	cat >.git/info/sparse-checkout <<\EOF &&
+/*
+!sub
+sub/added
+EOF
+	git read-tree -m -u HEAD &&
+	git ls-files -t >result &&
+	test_cmp expected.swt-negation2 result &&
+	test -f init.t &&
+	test -f sub/added &&
+	test ! -f sub/addedtoo
+'
+
+test_expect_success 'match directory pattern' '
+	echo "s?b" >.git/info/sparse-checkout &&
+	read_tree_u_must_succeed -m -u HEAD &&
+	git ls-files -t >result &&
+	test_cmp expected.swt-noinit result &&
+	test ! -f init.t &&
+	test -f sub/added
+'
+
+test_expect_success 'checkout area changes' '
+	cat >expected.swt-nosub <<-\EOF &&
+	H init.t
+	S sub/added
+	S sub/addedtoo
+	S subsub/added
+	EOF
+
+	echo init.t >.git/info/sparse-checkout &&
+	read_tree_u_must_succeed -m -u HEAD &&
+	git ls-files -t >result &&
+	test_cmp expected.swt-nosub result &&
+	test -f init.t &&
+	test ! -f sub/added
+'
+
+test_expect_success 'read-tree updates worktree, absent case' '
+	echo sub/added >.git/info/sparse-checkout &&
+	git checkout -f top &&
+	read_tree_u_must_succeed -m -u HEAD^ &&
+	test ! -f init.t
+'
+
+test_expect_success 'read-tree updates worktree, dirty case' '
+	echo sub/added >.git/info/sparse-checkout &&
+	git checkout -f top &&
+	echo dirty >init.t &&
+	read_tree_u_must_succeed -m -u HEAD^ &&
+	grep -q dirty init.t &&
+	rm init.t
+'
+
+test_expect_success 'read-tree removes worktree, dirty case' '
+	echo init.t >.git/info/sparse-checkout &&
+	git checkout -f top &&
+	echo dirty >added &&
+	read_tree_u_must_succeed -m -u HEAD^ &&
+	grep -q dirty added
+'
+
+test_expect_success 'read-tree adds to worktree, absent case' '
+	echo init.t >.git/info/sparse-checkout &&
+	git checkout -f removed &&
+	read_tree_u_must_succeed -u -m HEAD^ &&
+	test ! -f sub/added
+'
+
+test_expect_success 'read-tree adds to worktree, dirty case' '
+	echo init.t >.git/info/sparse-checkout &&
+	git checkout -f removed &&
+	mkdir sub &&
+	echo dirty >sub/added &&
+	read_tree_u_must_succeed -u -m HEAD^ &&
+	grep -q dirty sub/added
+'
+
+test_expect_success 'index removal and worktree narrowing at the same time' '
+	>empty &&
+	echo init.t >.git/info/sparse-checkout &&
+	echo sub/added >>.git/info/sparse-checkout &&
+	git checkout -f top &&
+	echo init.t >.git/info/sparse-checkout &&
+	git checkout removed &&
+	git ls-files sub/added >result &&
+	test ! -f sub/added &&
+	test_cmp empty result
+'
+
+test_expect_success 'read-tree --reset removes outside worktree' '
+	>empty &&
+	echo init.t >.git/info/sparse-checkout &&
+	git checkout -f top &&
+	git reset --hard removed &&
+	git ls-files sub/added >result &&
+	test_cmp empty result
+'
+
+test_expect_success 'print errors when failed to update worktree' '
+	echo sub >.git/info/sparse-checkout &&
+	git checkout -f init &&
+	mkdir sub &&
+	touch sub/added sub/addedtoo &&
+	test_must_fail git checkout top 2>actual &&
+	cat >expected <<\EOF &&
+error: The following untracked working tree files would be overwritten by checkout:
+	sub/added
+	sub/addedtoo
+Please move or remove them before you can switch branches.
+Aborting
+EOF
+	test_cmp expected actual
+'
+
+test_done
diff --git a/t/t1012-read-tree-df.sh b/t/t1012-read-tree-df.sh
new file mode 100755
index 0000000..a6a04b6
--- /dev/null
+++ b/t/t1012-read-tree-df.sh
@@ -0,0 +1,103 @@
+#!/bin/sh
+
+test_description='read-tree D/F conflict corner cases'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-read-tree.sh
+
+maketree () {
+	(
+		rm -f .git/index .git/index.lock &&
+		git clean -d -f -f -q -x &&
+		name="$1" &&
+		shift &&
+		for it
+		do
+			path=$(expr "$it" : '\([^:]*\)') &&
+			mkdir -p $(dirname "$path") &&
+			echo "$it" >"$path" &&
+			git update-index --add "$path" || exit
+		done &&
+		git tag "$name" $(git write-tree)
+	)
+}
+
+settree () {
+	rm -f .git/index .git/index.lock &&
+	git clean -d -f -f -q -x &&
+	git read-tree "$1" &&
+	git checkout-index -f -q -u -a &&
+	git update-index --refresh
+}
+
+checkindex () {
+	git ls-files -s |
+	sed "s|^[0-7][0-7]* $_x40 \([0-3]\)	|\1 |" >current &&
+	cat >expect &&
+	test_cmp expect current
+}
+
+test_expect_success setup '
+	maketree O-000 a/b-2/c/d a/b/c/d a/x &&
+	maketree A-000 a/b-2/c/d a/b/c/d a/x &&
+	maketree A-001 a/b-2/c/d a/b/c/d a/b/c/e a/x &&
+	maketree B-000 a/b-2/c/d a/b     a/x &&
+
+	maketree O-010 t-0     t/1  t/2 t=3 &&
+	maketree A-010 t-0 t            t=3 &&
+	maketree B-010         t/1:     t=3: &&
+
+	maketree O-020 ds/dma/ioat.c ds/dma/ioat_dca.c &&
+	maketree A-020 ds/dma/ioat/Makefile ds/dma/ioat/registers.h &&
+	:
+'
+
+test_expect_success '3-way (1)' '
+	settree A-000 &&
+	read_tree_u_must_succeed -m -u O-000 A-000 B-000 &&
+	checkindex <<-EOF
+	3 a/b
+	0 a/b-2/c/d
+	1 a/b/c/d
+	2 a/b/c/d
+	0 a/x
+	EOF
+'
+
+test_expect_success '3-way (2)' '
+	settree A-001 &&
+	read_tree_u_must_succeed -m -u O-000 A-001 B-000 &&
+	checkindex <<-EOF
+	3 a/b
+	0 a/b-2/c/d
+	1 a/b/c/d
+	2 a/b/c/d
+	2 a/b/c/e
+	0 a/x
+	EOF
+'
+
+test_expect_success '3-way (3)' '
+	settree A-010 &&
+	read_tree_u_must_succeed -m -u O-010 A-010 B-010 &&
+	checkindex <<-EOF
+	2 t
+	1 t-0
+	2 t-0
+	1 t/1
+	3 t/1
+	1 t/2
+	0 t=3
+	EOF
+'
+
+test_expect_success '2-way (1)' '
+	settree O-020 &&
+	read_tree_u_must_succeed -m -u O-020 A-020 &&
+	checkindex <<-EOF
+	0 ds/dma/ioat/Makefile
+	0 ds/dma/ioat/registers.h
+	EOF
+'
+
+test_done
diff --git a/t/t1013-loose-object-format.sh b/t/t1013-loose-object-format.sh
new file mode 100755
index 0000000..fbf5f2f
--- /dev/null
+++ b/t/t1013-loose-object-format.sh
@@ -0,0 +1,66 @@
+#!/bin/sh
+#
+# Copyright (c) 2011 Roberto Tyley
+#
+
+test_description='Correctly identify and parse loose object headers
+
+There are two file formats for loose objects - the original standard
+format, and the experimental format introduced with Git v1.4.3, later
+deprecated with v1.5.3. Although Git no longer writes the
+experimental format, objects in both formats must be read, with the
+format for a given file being determined by the header.
+
+Detecting file format based on header is not entirely trivial, not
+least because the first byte of a zlib-deflated stream will vary
+depending on how much memory was allocated for the deflation window
+buffer when the object was written out (for example 4KB on Android,
+rather that 32KB on a normal PC).
+
+The loose objects used as test vectors have been generated with the
+following Git versions:
+
+standard format: Git v1.7.4.1
+experimental format: Git v1.4.3 (legacyheaders=false)
+standard format, deflated with 4KB window size: Agit/JGit on Android
+'
+
+. ./test-lib.sh
+
+assert_blob_equals() {
+	printf "%s" "$2" >expected &&
+	git cat-file -p "$1" >actual &&
+	test_cmp expected actual
+}
+
+test_expect_success setup '
+	cp -R "$TEST_DIRECTORY/t1013/objects" .git/ &&
+	git --version
+'
+
+test_expect_success 'read standard-format loose objects' '
+	git cat-file tag 8d4e360d6c70fbd72411991c02a09c442cf7a9fa &&
+	git cat-file commit 6baee0540ea990d9761a3eb9ab183003a71c3696 &&
+	git ls-tree 7a37b887a73791d12d26c0d3e39568a8fb0fa6e8 &&
+	assert_blob_equals "257cc5642cb1a054f08cc83f2d943e56fd3ebe99" "foo$LF"
+'
+
+test_expect_success 'read experimental-format loose objects' '
+	git cat-file tag 76e7fa9941f4d5f97f64fea65a2cba436bc79cbb &&
+	git cat-file commit 7875c6237d3fcdd0ac2f0decc7d3fa6a50b66c09 &&
+	git ls-tree 95b1625de3ba8b2214d1e0d0591138aea733f64f &&
+	assert_blob_equals "2e65efe2a145dda7ee51d1741299f848e5bf752e" "a" &&
+	assert_blob_equals "9ae9e86b7bd6cb1472d9373702d8249973da0832" "ab" &&
+	assert_blob_equals "85df50785d62d3b05ab03d9cbf7e4a0b49449730" "abcd" &&
+	assert_blob_equals "1656f9233d999f61ef23ef390b9c71d75399f435" "abcdefgh" &&
+	assert_blob_equals "1e72a6b2c4a577ab0338860fa9fe87f761fc9bbd" "abcdefghi" &&
+	assert_blob_equals "70e6a83d8dcb26fc8bc0cf702e2ddeb6adca18fd" "abcdefghijklmnop" &&
+	assert_blob_equals "bd15045f6ce8ff75747562173640456a394412c8" "abcdefghijklmnopqrstuvwx"
+'
+
+test_expect_success 'read standard-format objects deflated with smaller window buffer' '
+	git cat-file tag f816d5255855ac160652ee5253b06cd8ee14165a &&
+	git cat-file tag 149cedb5c46929d18e0f118e9fa31927487af3b6
+'
+
+test_done
diff --git a/t/t1013/objects/14/9cedb5c46929d18e0f118e9fa31927487af3b6 b/t/t1013/objects/14/9cedb5c46929d18e0f118e9fa31927487af3b6
new file mode 100644
index 0000000..472fd14
--- /dev/null
+++ b/t/t1013/objects/14/9cedb5c46929d18e0f118e9fa31927487af3b6
Binary files differ
diff --git a/t/t1013/objects/16/56f9233d999f61ef23ef390b9c71d75399f435 b/t/t1013/objects/16/56f9233d999f61ef23ef390b9c71d75399f435
new file mode 100644
index 0000000..c379d74
--- /dev/null
+++ b/t/t1013/objects/16/56f9233d999f61ef23ef390b9c71d75399f435
Binary files differ
diff --git a/t/t1013/objects/1e/72a6b2c4a577ab0338860fa9fe87f761fc9bbd b/t/t1013/objects/1e/72a6b2c4a577ab0338860fa9fe87f761fc9bbd
new file mode 100644
index 0000000..9370630
--- /dev/null
+++ b/t/t1013/objects/1e/72a6b2c4a577ab0338860fa9fe87f761fc9bbd
Binary files differ
diff --git a/t/t1013/objects/25/7cc5642cb1a054f08cc83f2d943e56fd3ebe99 b/t/t1013/objects/25/7cc5642cb1a054f08cc83f2d943e56fd3ebe99
new file mode 100644
index 0000000..bdcf704
--- /dev/null
+++ b/t/t1013/objects/25/7cc5642cb1a054f08cc83f2d943e56fd3ebe99
Binary files differ
diff --git a/t/t1013/objects/2e/65efe2a145dda7ee51d1741299f848e5bf752e b/t/t1013/objects/2e/65efe2a145dda7ee51d1741299f848e5bf752e
new file mode 100644
index 0000000..ad62c43
--- /dev/null
+++ b/t/t1013/objects/2e/65efe2a145dda7ee51d1741299f848e5bf752e
Binary files differ
diff --git a/t/t1013/objects/6b/aee0540ea990d9761a3eb9ab183003a71c3696 b/t/t1013/objects/6b/aee0540ea990d9761a3eb9ab183003a71c3696
new file mode 100644
index 0000000..3d2f033
--- /dev/null
+++ b/t/t1013/objects/6b/aee0540ea990d9761a3eb9ab183003a71c3696
Binary files differ
diff --git a/t/t1013/objects/70/e6a83d8dcb26fc8bc0cf702e2ddeb6adca18fd b/t/t1013/objects/70/e6a83d8dcb26fc8bc0cf702e2ddeb6adca18fd
new file mode 100644
index 0000000..b3f71a6
--- /dev/null
+++ b/t/t1013/objects/70/e6a83d8dcb26fc8bc0cf702e2ddeb6adca18fd
Binary files differ
diff --git a/t/t1013/objects/76/e7fa9941f4d5f97f64fea65a2cba436bc79cbb b/t/t1013/objects/76/e7fa9941f4d5f97f64fea65a2cba436bc79cbb
new file mode 100644
index 0000000..af4e9a7
--- /dev/null
+++ b/t/t1013/objects/76/e7fa9941f4d5f97f64fea65a2cba436bc79cbb
Binary files differ
diff --git a/t/t1013/objects/78/75c6237d3fcdd0ac2f0decc7d3fa6a50b66c09 b/t/t1013/objects/78/75c6237d3fcdd0ac2f0decc7d3fa6a50b66c09
new file mode 100644
index 0000000..3dd28be
--- /dev/null
+++ b/t/t1013/objects/78/75c6237d3fcdd0ac2f0decc7d3fa6a50b66c09
Binary files differ
diff --git a/t/t1013/objects/7a/37b887a73791d12d26c0d3e39568a8fb0fa6e8 b/t/t1013/objects/7a/37b887a73791d12d26c0d3e39568a8fb0fa6e8
new file mode 100644
index 0000000..2b97b26
--- /dev/null
+++ b/t/t1013/objects/7a/37b887a73791d12d26c0d3e39568a8fb0fa6e8
Binary files differ
diff --git a/t/t1013/objects/85/df50785d62d3b05ab03d9cbf7e4a0b49449730 b/t/t1013/objects/85/df50785d62d3b05ab03d9cbf7e4a0b49449730
new file mode 100644
index 0000000..6dff746
--- /dev/null
+++ b/t/t1013/objects/85/df50785d62d3b05ab03d9cbf7e4a0b49449730
Binary files differ
diff --git a/t/t1013/objects/8d/4e360d6c70fbd72411991c02a09c442cf7a9fa b/t/t1013/objects/8d/4e360d6c70fbd72411991c02a09c442cf7a9fa
new file mode 100644
index 0000000..cb41e92
--- /dev/null
+++ b/t/t1013/objects/8d/4e360d6c70fbd72411991c02a09c442cf7a9fa
Binary files differ
diff --git a/t/t1013/objects/95/b1625de3ba8b2214d1e0d0591138aea733f64f b/t/t1013/objects/95/b1625de3ba8b2214d1e0d0591138aea733f64f
new file mode 100644
index 0000000..7ac46b4
--- /dev/null
+++ b/t/t1013/objects/95/b1625de3ba8b2214d1e0d0591138aea733f64f
Binary files differ
diff --git a/t/t1013/objects/9a/e9e86b7bd6cb1472d9373702d8249973da0832 b/t/t1013/objects/9a/e9e86b7bd6cb1472d9373702d8249973da0832
new file mode 100644
index 0000000..9d8316d
--- /dev/null
+++ b/t/t1013/objects/9a/e9e86b7bd6cb1472d9373702d8249973da0832
Binary files differ
diff --git a/t/t1013/objects/bd/15045f6ce8ff75747562173640456a394412c8 b/t/t1013/objects/bd/15045f6ce8ff75747562173640456a394412c8
new file mode 100644
index 0000000..eebf239
--- /dev/null
+++ b/t/t1013/objects/bd/15045f6ce8ff75747562173640456a394412c8
Binary files differ
diff --git a/t/t1013/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/t/t1013/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644
index 0000000..134cf19
--- /dev/null
+++ b/t/t1013/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/t/t1013/objects/f8/16d5255855ac160652ee5253b06cd8ee14165a b/t/t1013/objects/f8/16d5255855ac160652ee5253b06cd8ee14165a
new file mode 100644
index 0000000..26b75ae
--- /dev/null
+++ b/t/t1013/objects/f8/16d5255855ac160652ee5253b06cd8ee14165a
Binary files differ
diff --git a/t/t1020-subdirectory.sh b/t/t1020-subdirectory.sh
index 210e594..e23ac0e 100755
--- a/t/t1020-subdirectory.sh
+++ b/t/t1020-subdirectory.sh
@@ -7,6 +7,7 @@
 '
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-read-tree.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" &&
@@ -16,123 +17,176 @@
 	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 ;;
+	one) echo pass one ;;
 	*) echo bad one; exit 1 ;;
 	esac &&
-	cd dir &&
-	git update-index --add two &&
+	(
+		cd dir &&
+		git update-index --add two &&
+		case "`git ls-files`" in
+		two) echo pass two ;;
+		*) echo bad two; exit 1 ;;
+		esac
+	) &&
 	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 ;;
+	dir/two"$LF"one) echo pass 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
+	(
+		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 ;;
+	dir/two"$LF"one) echo pass 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
+	(
+		cd dir &&
+		case "`git diff-files --name-only`" in
+		dir/two"$LF"one) echo pass subdir ;;
+		*) echo bad subdir; exit 1 ;;
+		esac &&
+		case "`git diff-files --name-only .`" in
+		dir/two) echo pass 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"
+	(
+		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
+	(
+		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" &&
+	read_tree_u_must_succeed --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
+	(
+		cd dir &&
+		rm -f two &&
+		read_tree_u_must_succeed --reset -u "$tree" &&
+		cmp two ../original.two &&
+		cmp ../one ../original.one
+	)
+'
+
+test_expect_success 'alias expansion' '
+	(
+		git config alias.ss status &&
+		cd dir &&
+		git status &&
+		git ss
+	)
+'
+
+test_expect_success NOT_MINGW '!alias expansion' '
+	pwd >expect &&
+	(
+		git config alias.test !pwd &&
+		cd dir &&
+		git test >../actual
+	) &&
+	test_cmp expect actual
+'
+
+test_expect_success 'GIT_PREFIX for !alias' '
+	printf "dir/" >expect &&
+	(
+		git config alias.test "!sh -c \"printf \$GIT_PREFIX\"" &&
+		cd dir &&
+		git test >../actual
+	) &&
+	test_cmp expect actual
+'
+
+test_expect_success 'GIT_PREFIX for built-ins' '
+	# Use GIT_EXTERNAL_DIFF to test that the "diff" built-in
+	# receives the GIT_PREFIX variable.
+	printf "dir/" >expect &&
+	printf "#!/bin/sh\n" >diff &&
+	printf "printf \"\$GIT_PREFIX\"" >>diff &&
+	chmod +x diff &&
+	(
+		cd dir &&
+		printf "change" >two &&
+		env GIT_EXTERNAL_DIFF=./diff git diff >../actual
+		git checkout -- two
+	) &&
+	test_cmp expect actual
 '
 
 test_expect_success 'no file/rev ambiguity check inside .git' '
-	cd "$HERE" &&
 	git commit -a -m 1 &&
-	cd "$HERE"/.git &&
-	git show -s HEAD
+	(
+		cd .git &&
+		git show -s HEAD
+	)
 '
 
 test_expect_success 'no file/rev ambiguity check inside a bare repo' '
-	cd "$HERE" &&
 	git clone -s --bare .git foo.git &&
-	cd foo.git && GIT_DIR=. git show -s HEAD
+	(
+		cd foo.git &&
+		GIT_DIR=. git show -s HEAD
+	)
 '
 
 # This still does not work as it should...
 : test_expect_success 'no file/rev ambiguity check inside a bare repo' '
-	cd "$HERE" &&
 	git clone -s --bare .git foo.git &&
-	cd foo.git && git show -s HEAD
+	(
+		cd foo.git &&
+		git show -s HEAD
+	)
 '
 
 test_expect_success SYMLINKS 'detection should not be fooled by a symlink' '
-	cd "$HERE" &&
 	rm -fr foo.git &&
 	git clone -s .git another &&
 	ln -s another yetanother &&
-	cd yetanother/.git &&
-	git show -s HEAD
+	(
+		cd yetanother/.git &&
+		git show -s HEAD
+	)
 '
 
 test_done
diff --git a/t/t1021-rerere-in-workdir.sh b/t/t1021-rerere-in-workdir.sh
new file mode 100755
index 0000000..301e071
--- /dev/null
+++ b/t/t1021-rerere-in-workdir.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+test_description='rerere run in a workdir'
+. ./test-lib.sh
+
+test_expect_success SYMLINKS setup '
+	git config rerere.enabled true &&
+	>world &&
+	git add world &&
+	test_tick &&
+	git commit -m initial &&
+
+	echo hello >world &&
+	test_tick &&
+	git commit -a -m hello &&
+
+	git checkout -b side HEAD^ &&
+	echo goodbye >world &&
+	test_tick &&
+	git commit -a -m goodbye &&
+
+	git checkout master
+'
+
+test_expect_success SYMLINKS 'rerere in workdir' '
+	rm -rf .git/rr-cache &&
+	"$SHELL_PATH" "$TEST_DIRECTORY/../contrib/workdir/git-new-workdir" . work &&
+	(
+		cd work &&
+		test_must_fail git merge side &&
+		git rerere status >actual &&
+		echo world >expect &&
+		test_cmp expect actual
+	)
+'
+
+# This fails because we don't resolve relative symlink in mkdir_in_gitdir()
+# For the purpose of helping contrib/workdir/git-new-workdir users, we do not
+# have to support relative symlinks, but it might be nicer to make this work
+# with a relative symbolic link someday.
+test_expect_failure SYMLINKS 'rerere in workdir (relative)' '
+	rm -rf .git/rr-cache &&
+	"$SHELL_PATH" "$TEST_DIRECTORY/../contrib/workdir/git-new-workdir" . krow &&
+	(
+		cd krow &&
+		rm -f .git/rr-cache &&
+		ln -s ../.git/rr-cache .git/rr-cache &&
+		test_must_fail git merge side &&
+		git rerere status >actual &&
+		echo world >expect &&
+		test_cmp expect actual
+	)
+'
+
+test_done
diff --git a/t/t1050-large.sh b/t/t1050-large.sh
new file mode 100755
index 0000000..fd10528
--- /dev/null
+++ b/t/t1050-large.sh
@@ -0,0 +1,166 @@
+#!/bin/sh
+# Copyright (c) 2011, Google Inc.
+
+test_description='adding and checking out large blobs'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+	# clone does not allow us to pass core.bigfilethreshold to
+	# new repos, so set core.bigfilethreshold globally
+	git config --global core.bigfilethreshold 200k &&
+	echo X | dd of=large1 bs=1k seek=2000 &&
+	echo X | dd of=large2 bs=1k seek=2000 &&
+	echo X | dd of=large3 bs=1k seek=2000 &&
+	echo Y | dd of=huge bs=1k seek=2500 &&
+	GIT_ALLOC_LIMIT=1500 &&
+	export GIT_ALLOC_LIMIT
+'
+
+test_expect_success 'add a large file or two' '
+	git add large1 huge large2 &&
+	# make sure we got a single packfile and no loose objects
+	bad= count=0 idx= &&
+	for p in .git/objects/pack/pack-*.pack
+	do
+		count=$(( $count + 1 ))
+		if test -f "$p" && idx=${p%.pack}.idx && test -f "$idx"
+		then
+			continue
+		fi
+		bad=t
+	done &&
+	test -z "$bad" &&
+	test $count = 1 &&
+	cnt=$(git show-index <"$idx" | wc -l) &&
+	test $cnt = 2 &&
+	for l in .git/objects/??/??????????????????????????????????????
+	do
+		test -f "$l" || continue
+		bad=t
+	done &&
+	test -z "$bad" &&
+
+	# attempt to add another copy of the same
+	git add large3 &&
+	bad= count=0 &&
+	for p in .git/objects/pack/pack-*.pack
+	do
+		count=$(( $count + 1 ))
+		if test -f "$p" && idx=${p%.pack}.idx && test -f "$idx"
+		then
+			continue
+		fi
+		bad=t
+	done &&
+	test -z "$bad" &&
+	test $count = 1
+'
+
+test_expect_success 'checkout a large file' '
+	large1=$(git rev-parse :large1) &&
+	git update-index --add --cacheinfo 100644 $large1 another &&
+	git checkout another &&
+	cmp large1 another ;# this must not be test_cmp
+'
+
+test_expect_success 'packsize limit' '
+	test_create_repo mid &&
+	(
+		cd mid &&
+		git config core.bigfilethreshold 64k &&
+		git config pack.packsizelimit 256k &&
+
+		# mid1 and mid2 will fit within 256k limit but
+		# appending mid3 will bust the limit and will
+		# result in a separate packfile.
+		test-genrandom "a" $(( 66 * 1024 )) >mid1 &&
+		test-genrandom "b" $(( 80 * 1024 )) >mid2 &&
+		test-genrandom "c" $(( 128 * 1024 )) >mid3 &&
+		git add mid1 mid2 mid3 &&
+
+		count=0
+		for pi in .git/objects/pack/pack-*.idx
+		do
+			test -f "$pi" && count=$(( $count + 1 ))
+		done &&
+		test $count = 2 &&
+
+		(
+			git hash-object --stdin <mid1
+			git hash-object --stdin <mid2
+			git hash-object --stdin <mid3
+		) |
+		sort >expect &&
+
+		for pi in .git/objects/pack/pack-*.idx
+		do
+			git show-index <"$pi"
+		done |
+		sed -e "s/^[0-9]* \([0-9a-f]*\) .*/\1/" |
+		sort >actual &&
+
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'diff --raw' '
+	git commit -q -m initial &&
+	echo modified >>large1 &&
+	git add large1 &&
+	git commit -q -m modified &&
+	git diff --raw HEAD^
+'
+
+test_expect_success 'hash-object' '
+	git hash-object large1
+'
+
+test_expect_success 'cat-file a large file' '
+	git cat-file blob :large1 >/dev/null
+'
+
+test_expect_success 'cat-file a large file from a tag' '
+	git tag -m largefile largefiletag :large1 &&
+	git cat-file blob largefiletag >/dev/null
+'
+
+test_expect_success 'git-show a large file' '
+	git show :large1 >/dev/null
+
+'
+
+test_expect_success 'index-pack' '
+	git clone file://"`pwd`"/.git foo &&
+	GIT_DIR=non-existent git index-pack --strict --verify foo/.git/objects/pack/*.pack
+'
+
+test_expect_success 'repack' '
+	git repack -ad
+'
+
+test_expect_success 'pack-objects with large loose object' '
+	SHA1=`git hash-object huge` &&
+	test_create_repo loose &&
+	echo $SHA1 | git pack-objects --stdout |
+		GIT_ALLOC_LIMIT=0 GIT_DIR=loose/.git git unpack-objects &&
+	echo $SHA1 | GIT_DIR=loose/.git git pack-objects pack &&
+	test_create_repo packed &&
+	mv pack-* packed/.git/objects/pack &&
+	GIT_DIR=packed/.git git cat-file blob $SHA1 >actual &&
+	cmp huge actual
+'
+
+test_expect_success 'tar achiving' '
+	git archive --format=tar HEAD >/dev/null
+'
+
+test_expect_success 'zip achiving, store only' '
+	git archive --format=zip -0 HEAD >/dev/null
+'
+
+test_expect_success 'zip achiving, deflate' '
+	git archive --format=zip HEAD >/dev/null
+'
+
+test_done
diff --git a/t/t1051-large-conversion.sh b/t/t1051-large-conversion.sh
new file mode 100755
index 0000000..8b7640b
--- /dev/null
+++ b/t/t1051-large-conversion.sh
@@ -0,0 +1,86 @@
+#!/bin/sh
+
+test_description='test conversion filters on large files'
+. ./test-lib.sh
+
+set_attr() {
+	test_when_finished 'rm -f .gitattributes' &&
+	echo "* $*" >.gitattributes
+}
+
+check_input() {
+	git read-tree --empty &&
+	git add small large &&
+	git cat-file blob :small >small.index &&
+	git cat-file blob :large | head -n 1 >large.index &&
+	test_cmp small.index large.index
+}
+
+check_output() {
+	rm -f small large &&
+	git checkout small large &&
+	head -n 1 large >large.head &&
+	test_cmp small large.head
+}
+
+test_expect_success 'setup input tests' '
+	printf "\$Id: foo\$\\r\\n" >small &&
+	cat small small >large &&
+	git config core.bigfilethreshold 20 &&
+	git config filter.test.clean "sed s/.*/CLEAN/"
+'
+
+test_expect_success 'autocrlf=true converts on input' '
+	test_config core.autocrlf true &&
+	check_input
+'
+
+test_expect_success 'eol=crlf converts on input' '
+	set_attr eol=crlf &&
+	check_input
+'
+
+test_expect_success 'ident converts on input' '
+	set_attr ident &&
+	check_input
+'
+
+test_expect_success 'user-defined filters convert on input' '
+	set_attr filter=test &&
+	check_input
+'
+
+test_expect_success 'setup output tests' '
+	echo "\$Id\$" >small &&
+	cat small small >large &&
+	git add small large &&
+	git config core.bigfilethreshold 7 &&
+	git config filter.test.smudge "sed s/.*/SMUDGE/"
+'
+
+test_expect_success 'autocrlf=true converts on output' '
+	test_config core.autocrlf true &&
+	check_output
+'
+
+test_expect_success 'eol=crlf converts on output' '
+	set_attr eol=crlf &&
+	check_output
+'
+
+test_expect_success 'user-defined filters convert on output' '
+	set_attr filter=test &&
+	check_output
+'
+
+test_expect_success 'ident converts on output' '
+	set_attr ident &&
+	rm -f small large &&
+	git checkout small large &&
+	sed -n "s/Id: .*/Id: SHA/p" <small >small.clean &&
+	head -n 1 large >large.head &&
+	sed -n "s/Id: .*/Id: SHA/p" <large.head >large.clean &&
+	test_cmp small.clean large.clean
+'
+
+test_done
diff --git a/t/t1100-commit-tree-options.sh b/t/t1100-commit-tree-options.sh
index c4414ff..a3b7723 100755
--- a/t/t1100-commit-tree-options.sh
+++ b/t/t1100-commit-tree-options.sh
@@ -7,6 +7,9 @@
 
 This test checks that git commit-tree can create a specific commit
 object by defining all environment variables that it understands.
+
+Also make sure that command line parser understands the normal
+"flags first and then non flag arguments" command line.
 '
 
 . ./test-lib.sh
@@ -42,4 +45,18 @@
     'compare commit' \
     'test_cmp expected commit'
 
+
+test_expect_success 'flags and then non flags' '
+	echo comment text |
+	git commit-tree $(cat treeid) >commitid &&
+	echo comment text |
+	git commit-tree $(cat treeid) -p $(cat commitid) >childid-1 &&
+	echo comment text |
+	git commit-tree -p $(cat commitid) $(cat treeid) >childid-2 &&
+	test_cmp childid-1 childid-2 &&
+	git commit-tree $(cat treeid) -m foo >childid-3 &&
+	git commit-tree -m foo $(cat treeid) >childid-4 &&
+	test_cmp childid-3 childid-4
+'
+
 test_done
diff --git a/t/t1200-tutorial.sh b/t/t1200-tutorial.sh
index 67e637b..397ccb6 100755
--- a/t/t1200-tutorial.sh
+++ b/t/t1200-tutorial.sh
@@ -7,14 +7,18 @@
 
 . ./test-lib.sh
 
-echo "Hello World" > hello
-echo "Silly example" > example
+test_expect_success 'blob'  '
+	echo "Hello World" > hello &&
+	echo "Silly example" > example &&
 
-git update-index --add hello example
+	git update-index --add hello example &&
 
-test_expect_success 'blob' "test blob = \"$(git cat-file -t 557db03)\""
+	test blob = "$(git cat-file -t 557db03)"
+'
 
-test_expect_success 'blob 557db03' "test \"Hello World\" = \"$(git cat-file blob 557db03)\""
+test_expect_success 'blob 557db03' '
+	test "Hello World" = "$(git cat-file blob 557db03)"
+'
 
 echo "It's a new day for git" >>hello
 cat > diff.expect << EOF
@@ -26,25 +30,35 @@
  Hello World
 +It's a new day for git
 EOF
-git diff-files -p > diff.output
-test_expect_success 'git diff-files -p' 'cmp diff.expect diff.output'
-git diff > diff.output
-test_expect_success 'git diff' 'cmp diff.expect diff.output'
 
-tree=$(git write-tree 2>/dev/null)
+test_expect_success 'git diff-files -p' '
+	git diff-files -p > diff.output &&
+	test_cmp diff.expect diff.output
+'
 
-test_expect_success 'tree' "test 8988da15d077d4829fc51d8544c097def6644dbb = $tree"
+test_expect_success 'git diff' '
+	git diff > diff.output &&
+	test_cmp diff.expect diff.output
+'
 
-output="$(echo "Initial commit" | git commit-tree $(git write-tree) 2>&1 > .git/refs/heads/master)"
+test_expect_success 'tree' '
+	tree=$(git write-tree 2>/dev/null) &&
+	test 8988da15d077d4829fc51d8544c097def6644dbb = $tree
+'
 
-git diff-index -p HEAD > diff.output
-test_expect_success 'git diff-index -p HEAD' 'cmp diff.expect diff.output'
+test_expect_success 'git diff-index -p HEAD' '
+	test_tick &&
+	tree=$(git write-tree) &&
+	commit=$(echo "Initial commit" | git commit-tree $tree) &&
+	git update-ref HEAD $commit &&
+	git diff-index -p HEAD > diff.output &&
+	test_cmp diff.expect diff.output
+'
 
-git diff HEAD > diff.output
-test_expect_success 'git diff HEAD' 'cmp diff.expect diff.output'
-
-#rm hello
-#test_expect_success 'git read-tree --reset HEAD' "git read-tree --reset HEAD ; test \"hello: needs update\" = \"$(git update-index --refresh)\""
+test_expect_success 'git diff HEAD' '
+	git diff HEAD > diff.output &&
+	test_cmp diff.expect diff.output
+'
 
 cat > whatchanged.expect << EOF
 commit VARIABLE
@@ -69,39 +83,47 @@
 +Hello World
 EOF
 
-git whatchanged -p --root | \
-	sed -e "1s/^\(.\{7\}\).\{40\}/\1VARIABLE/" \
+test_expect_success 'git whatchanged -p --root' '
+	git whatchanged -p --root |
+		sed -e "1s/^\(.\{7\}\).\{40\}/\1VARIABLE/" \
 		-e "2,3s/^\(.\{8\}\).*$/\1VARIABLE/" \
-> whatchanged.output
-test_expect_success 'git whatchanged -p --root' 'cmp whatchanged.expect whatchanged.output'
+	> whatchanged.output &&
+	test_cmp whatchanged.expect whatchanged.output
+'
 
-git tag my-first-tag
-test_expect_success 'git tag my-first-tag' 'cmp .git/refs/heads/master .git/refs/tags/my-first-tag'
+test_expect_success 'git tag my-first-tag' '
+	git tag my-first-tag &&
+	test_cmp .git/refs/heads/master .git/refs/tags/my-first-tag
+'
 
-# TODO: test git clone
-
-git checkout -b mybranch
-test_expect_success 'git checkout -b mybranch' 'cmp .git/refs/heads/master .git/refs/heads/mybranch'
+test_expect_success 'git checkout -b mybranch' '
+	git checkout -b mybranch &&
+	test_cmp .git/refs/heads/master .git/refs/heads/mybranch
+'
 
 cat > branch.expect <<EOF
   master
 * mybranch
 EOF
 
-git branch > branch.output
-test_expect_success 'git branch' 'cmp branch.expect branch.output'
-
-git checkout mybranch
-echo "Work, work, work" >>hello
-git commit -m 'Some work.' -i hello
-
-git checkout master
-
-echo "Play, play, play" >>hello
-echo "Lots of fun" >>example
-git commit -m 'Some fun.' -i hello example
+test_expect_success 'git branch' '
+	git branch > branch.output &&
+	test_cmp branch.expect branch.output
+'
 
 test_expect_success 'git resolve now fails' '
+	git checkout mybranch &&
+	echo "Work, work, work" >>hello &&
+	test_tick &&
+	git commit -m "Some work." -i hello &&
+
+	git checkout master &&
+
+	echo "Play, play, play" >>hello &&
+	echo "Lots of fun" >>example &&
+	test_tick &&
+	git commit -m "Some fun." -i hello example &&
+
 	test_must_fail git merge -m "Merge work in mybranch" mybranch
 '
 
@@ -112,52 +134,135 @@
 Work, work, work
 EOF
 
-git commit -m 'Merged "mybranch" changes.' -i hello
-
-test_done
-
 cat > show-branch.expect << EOF
-* [master] Merged "mybranch" changes.
+* [master] Merge work in mybranch
  ! [mybranch] Some work.
 --
--  [master] Merged "mybranch" changes.
+-  [master] Merge work in mybranch
 *+ [mybranch] Some work.
+*  [master^] Some fun.
 EOF
 
-git show-branch --topo-order master mybranch > show-branch.output
-test_expect_success 'git show-branch' 'cmp show-branch.expect show-branch.output'
-
-git checkout mybranch
+test_expect_success 'git show-branch' '
+	test_tick &&
+	git commit -m "Merge work in mybranch" -i hello &&
+	git show-branch --topo-order --more=1 master mybranch \
+		> show-branch.output &&
+	test_cmp show-branch.expect show-branch.output
+'
 
 cat > resolve.expect << EOF
-Updating from VARIABLE to VARIABLE
- example |    1 +
- hello   |    1 +
- 2 files changed, 2 insertions(+), 0 deletions(-)
+Updating VARIABLE..VARIABLE
+FASTFORWARD (no commit created; -m option ignored)
+ example | 1 +
+ hello   | 1 +
+ 2 files changed, 2 insertions(+)
 EOF
 
-git merge -s "Merge upstream changes." master | \
-	sed -e "1s/[0-9a-f]\{40\}/VARIABLE/g" >resolve.output
-test_expect_success 'git resolve' 'cmp resolve.expect resolve.output'
+test_expect_success 'git resolve' '
+	git checkout mybranch &&
+	git merge -m "Merge upstream changes." master |
+		sed -e "1s/[0-9a-f]\{7\}/VARIABLE/g" \
+		-e "s/^Fast[- ]forward /FASTFORWARD /" >resolve.output
+'
+
+test_expect_success 'git resolve output' '
+	test_i18ncmp resolve.expect resolve.output
+'
 
 cat > show-branch2.expect << EOF
-! [master] Merged "mybranch" changes.
- * [mybranch] Merged "mybranch" changes.
+! [master] Merge work in mybranch
+ * [mybranch] Merge work in mybranch
 --
--- [master] Merged "mybranch" changes.
+-- [master] Merge work in mybranch
 EOF
 
-git show-branch --topo-order master mybranch > show-branch2.output
-test_expect_success 'git show-branch' 'cmp show-branch2.expect show-branch2.output'
+test_expect_success 'git show-branch (part 2)' '
+	git show-branch --topo-order master mybranch > show-branch2.output &&
+	test_cmp show-branch2.expect show-branch2.output
+'
 
-# TODO: test git fetch
+cat > show-branch3.expect << EOF
+! [master] Merge work in mybranch
+ * [mybranch] Merge work in mybranch
+--
+-- [master] Merge work in mybranch
++* [master^2] Some work.
++* [master^] Some fun.
+EOF
 
-# TODO: test git push
+test_expect_success 'git show-branch (part 3)' '
+	git show-branch --topo-order --more=2 master mybranch \
+		> show-branch3.output &&
+	test_cmp show-branch3.expect show-branch3.output
+'
+
+test_expect_success 'rewind to "Some fun." and "Some work."' '
+	git checkout mybranch &&
+	git reset --hard master^2 &&
+	git checkout master &&
+	git reset --hard master^
+'
+
+cat > show-branch4.expect << EOF
+* [master] Some fun.
+ ! [mybranch] Some work.
+--
+*  [master] Some fun.
+ + [mybranch] Some work.
+*+ [master^] Initial commit
+EOF
+
+test_expect_success 'git show-branch (part 4)' '
+	git show-branch --topo-order > show-branch4.output &&
+	test_cmp show-branch4.expect show-branch4.output
+'
+
+test_expect_success 'manual merge' '
+	mb=$(git merge-base HEAD mybranch) &&
+	git name-rev --name-only --tags $mb > name-rev.output &&
+	test "my-first-tag" = $(cat name-rev.output) &&
+
+	git read-tree -m -u $mb HEAD mybranch
+'
+
+cat > ls-files.expect << EOF
+100644 7f8b141b65fdcee47321e399a2598a235a032422 0	example
+100644 557db03de997c86a4a028e1ebd3a1ceb225be238 1	hello
+100644 ba42a2a96e3027f3333e13ede4ccf4498c3ae942 2	hello
+100644 cc44c73eb783565da5831b4d820c962954019b69 3	hello
+EOF
+
+test_expect_success 'git ls-files --stage' '
+	git ls-files --stage > ls-files.output &&
+	test_cmp ls-files.expect ls-files.output
+'
+
+cat > ls-files-unmerged.expect << EOF
+100644 557db03de997c86a4a028e1ebd3a1ceb225be238 1	hello
+100644 ba42a2a96e3027f3333e13ede4ccf4498c3ae942 2	hello
+100644 cc44c73eb783565da5831b4d820c962954019b69 3	hello
+EOF
+
+test_expect_success 'git ls-files --unmerged' '
+	git ls-files --unmerged > ls-files-unmerged.output &&
+	test_cmp ls-files-unmerged.expect ls-files-unmerged.output
+'
+
+test_expect_success 'git-merge-index' '
+	test_must_fail git merge-index git-merge-one-file hello
+'
+
+test_expect_success 'git ls-files --stage (part 2)' '
+	git ls-files --stage > ls-files.output2 &&
+	test_cmp ls-files.expect ls-files.output2
+'
 
 test_expect_success 'git repack' 'git repack'
 test_expect_success 'git prune-packed' 'git prune-packed'
 test_expect_success '-> only packed objects' '
-	! find -type f .git/objects/[0-9a-f][0-9a-f]
+	git prune && # Remove conflict marked blobs
+	test $(find .git/objects/[0-9a-f][0-9a-f] -type f -print 2>/dev/null | wc -l) = 0
 '
 
 test_done
diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh
index 83b7294..a477453 100755
--- a/t/t1300-repo-config.sh
+++ b/t/t1300-repo-config.sh
@@ -7,28 +7,28 @@
 
 . ./test-lib.sh
 
-test -f .git/config && rm .git/config
-
-git config core.penguin "little blue"
+test_expect_success 'clear default config' '
+	rm -f .git/config
+'
 
 cat > expect << EOF
 [core]
 	penguin = little blue
 EOF
-
-test_expect_success 'initial' 'cmp .git/config expect'
-
-git config Core.Movie BadPhysics
+test_expect_success 'initial' '
+	git config core.penguin "little blue" &&
+	test_cmp expect .git/config
+'
 
 cat > expect << EOF
 [core]
 	penguin = little blue
 	Movie = BadPhysics
 EOF
-
-test_expect_success 'mixed case' 'cmp .git/config expect'
-
-git config Cores.WhatEver Second
+test_expect_success 'mixed case' '
+	git config Core.Movie BadPhysics &&
+	test_cmp expect .git/config
+'
 
 cat > expect << EOF
 [core]
@@ -37,10 +37,10 @@
 [Cores]
 	WhatEver = Second
 EOF
-
-test_expect_success 'similar section' 'cmp .git/config expect'
-
-git config CORE.UPPERCASE true
+test_expect_success 'similar section' '
+	git config Cores.WhatEver Second &&
+	test_cmp expect .git/config
+'
 
 cat > expect << EOF
 [core]
@@ -50,8 +50,10 @@
 [Cores]
 	WhatEver = Second
 EOF
-
-test_expect_success 'similar section' 'cmp .git/config expect'
+test_expect_success 'uppercase section' '
+	git config CORE.UPPERCASE true &&
+	test_cmp expect .git/config
+'
 
 test_expect_success 'replace with non-match' \
 	'git config core.penguin kingpin !blue'
@@ -69,7 +71,34 @@
 	WhatEver = Second
 EOF
 
-test_expect_success 'non-match result' 'cmp .git/config expect'
+test_expect_success 'non-match result' 'test_cmp expect .git/config'
+
+test_expect_success 'find mixed-case key by canonical name' '
+	echo Second >expect &&
+	git config cores.whatever >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'find mixed-case key by non-canonical name' '
+	echo Second >expect &&
+	git config CoReS.WhAtEvEr >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'subsections are not canonicalized by git-config' '
+	cat >>.git/config <<-\EOF &&
+	[section.SubSection]
+	key = one
+	[section "SubSection"]
+	key = two
+	EOF
+	echo one >expect &&
+	git config section.subsection.key >actual &&
+	test_cmp expect actual &&
+	echo two >expect &&
+	git config section.SubSection.key >actual &&
+	test_cmp expect actual
+'
 
 cat > .git/config <<\EOF
 [alpha]
@@ -88,7 +117,7 @@
 [beta]
 EOF
 
-test_expect_success 'unset with cont. lines is correct' 'cmp .git/config expect'
+test_expect_success 'unset with cont. lines is correct' 'test_cmp expect .git/config'
 
 cat > .git/config << EOF
 [beta] ; silly comment # another comment
@@ -116,7 +145,7 @@
 [nextSection] noNewline = ouch
 EOF
 
-test_expect_success 'multiple unset is correct' 'cmp .git/config expect'
+test_expect_success 'multiple unset is correct' 'test_cmp expect .git/config'
 
 cp .git/config2 .git/config
 
@@ -140,9 +169,7 @@
 [nextSection] noNewline = ouch
 EOF
 
-test_expect_success 'all replaced' 'cmp .git/config expect'
-
-git config beta.haha alpha
+test_expect_success 'all replaced' 'test_cmp expect .git/config'
 
 cat > expect << EOF
 [beta] ; silly comment # another comment
@@ -153,10 +180,10 @@
 	haha = alpha
 [nextSection] noNewline = ouch
 EOF
-
-test_expect_success 'really mean test' 'cmp .git/config expect'
-
-git config nextsection.nonewline wow
+test_expect_success 'really mean test' '
+	git config beta.haha alpha &&
+	test_cmp expect .git/config
+'
 
 cat > expect << EOF
 [beta] ; silly comment # another comment
@@ -168,11 +195,12 @@
 [nextSection]
 	nonewline = wow
 EOF
-
-test_expect_success 'really really mean test' 'cmp .git/config expect'
+test_expect_success 'really really mean test' '
+	git config nextsection.nonewline wow &&
+	test_cmp expect .git/config
+'
 
 test_expect_success 'get value' 'test alpha = $(git config beta.haha)'
-git config --unset beta.haha
 
 cat > expect << EOF
 [beta] ; silly comment # another comment
@@ -183,10 +211,10 @@
 [nextSection]
 	nonewline = wow
 EOF
-
-test_expect_success 'unset' 'cmp .git/config expect'
-
-git config nextsection.NoNewLine "wow2 for me" "for me$"
+test_expect_success 'unset' '
+	git config --unset beta.haha &&
+	test_cmp expect .git/config
+'
 
 cat > expect << EOF
 [beta] ; silly comment # another comment
@@ -198,8 +226,10 @@
 	nonewline = wow
 	NoNewLine = wow2 for me
 EOF
-
-test_expect_success 'multivar' 'cmp .git/config expect'
+test_expect_success 'multivar' '
+	git config nextsection.NoNewLine "wow2 for me" "for me$" &&
+	test_cmp expect .git/config
+'
 
 test_expect_success 'non-match' \
 	'git config --get nextsection.nonewline !for'
@@ -214,8 +244,6 @@
 test_expect_success 'get multivar' \
 	'git config --get-all nextsection.nonewline'
 
-git config nextsection.nonewline "wow3" "wow$"
-
 cat > expect << EOF
 [beta] ; silly comment # another comment
 noIndent= sillyValue ; 'nother silly comment
@@ -226,8 +254,10 @@
 	nonewline = wow3
 	NoNewLine = wow2 for me
 EOF
-
-test_expect_success 'multivar replace' 'cmp .git/config expect'
+test_expect_success 'multivar replace' '
+	git config nextsection.nonewline "wow3" "wow$" &&
+	test_cmp expect .git/config
+'
 
 test_expect_success 'ambiguous value' '
 	test_must_fail git config nextsection.nonewline
@@ -241,8 +271,6 @@
 	test_must_fail git config --unset somesection.nonewline
 '
 
-git config --unset nextsection.nonewline "wow3$"
-
 cat > expect << EOF
 [beta] ; silly comment # another comment
 noIndent= sillyValue ; 'nother silly comment
@@ -253,7 +281,10 @@
 	NoNewLine = wow2 for me
 EOF
 
-test_expect_success 'multivar unset' 'cmp .git/config expect'
+test_expect_success 'multivar unset' '
+	git config --unset nextsection.nonewline "wow3$" &&
+	test_cmp expect .git/config
+'
 
 test_expect_success 'invalid key' 'test_must_fail git config inval.2key blabla'
 
@@ -276,7 +307,7 @@
 	Alpha = beta
 EOF
 
-test_expect_success 'hierarchical section value' 'cmp .git/config expect'
+test_expect_success 'hierarchical section value' 'test_cmp expect .git/config'
 
 cat > expect << EOF
 beta.noindent=sillyValue
@@ -289,6 +320,14 @@
 	'git config --list > output && cmp output expect'
 
 cat > expect << EOF
+EOF
+
+test_expect_success '--list without repo produces empty output' '
+	git --git-dir=nonexistent config --list >output &&
+	test_cmp expect output
+'
+
+cat > expect << EOF
 beta.noindent sillyValue
 nextsection.nonewline wow2 for me
 EOF
@@ -296,15 +335,16 @@
 test_expect_success '--get-regexp' \
 	'git config --get-regexp in > output && cmp output expect'
 
-git config --add nextsection.nonewline "wow4 for you"
-
 cat > expect << EOF
 wow2 for me
 wow4 for you
 EOF
 
-test_expect_success '--add' \
-	'git config --get-all nextsection.nonewline > output && cmp output expect'
+test_expect_success '--add' '
+	git config --add nextsection.nonewline "wow4 for you" &&
+	git config --get-all nextsection.nonewline > output &&
+	test_cmp expect output
+'
 
 cat > .git/config << EOF
 [novalue]
@@ -325,6 +365,12 @@
 	'git config --get-regexp novalue > output &&
 	 cmp output expect'
 
+echo 'novalue.variable true' > expect
+
+test_expect_success 'get-regexp --bool variable with no value' \
+	'git config --bool --get-regexp novalue > output &&
+	 cmp output expect'
+
 echo 'emptyvalue.variable ' > expect
 
 test_expect_success 'get-regexp variable with empty value' \
@@ -353,8 +399,6 @@
 	c = d
 EOF
 
-git config a.x y
-
 cat > expect << EOF
 [a.b]
 	c = d
@@ -362,10 +406,10 @@
 	x = y
 EOF
 
-test_expect_success 'new section is partial match of another' 'cmp .git/config expect'
-
-git config b.x y
-git config a.b c
+test_expect_success 'new section is partial match of another' '
+	git config a.x y &&
+	test_cmp expect .git/config
+'
 
 cat > expect << EOF
 [a.b]
@@ -377,7 +421,11 @@
 	x = y
 EOF
 
-test_expect_success 'new variable inserts into proper section' 'cmp .git/config expect'
+test_expect_success 'new variable inserts into proper section' '
+	git config b.x y &&
+	git config a.b c &&
+	test_cmp expect .git/config
+'
 
 test_expect_success 'alternative GIT_CONFIG (non-existing file should fail)' \
 	'test_must_fail git config --file non-existing-config -l'
@@ -391,14 +439,32 @@
 ein.bahn=strasse
 EOF
 
-GIT_CONFIG=other-config git config -l > output
-
-test_expect_success 'alternative GIT_CONFIG' 'cmp output expect'
+test_expect_success 'alternative GIT_CONFIG' '
+	GIT_CONFIG=other-config git config -l >output &&
+	test_cmp expect output
+'
 
 test_expect_success 'alternative GIT_CONFIG (--file)' \
 	'git config --file other-config -l > output && cmp output expect'
 
-GIT_CONFIG=other-config git config anwohner.park ausweis
+test_expect_success 'refer config from subdirectory' '
+	mkdir x &&
+	(
+		cd x &&
+		echo strasse >expect &&
+		git config --get --file ../other-config ein.bahn >actual &&
+		test_cmp expect actual
+	)
+
+'
+
+test_expect_success 'refer config from subdirectory via GIT_CONFIG' '
+	(
+		cd x &&
+		GIT_CONFIG=../other-config git config --get ein.bahn >actual &&
+		test_cmp expect actual
+	)
+'
 
 cat > expect << EOF
 [ein]
@@ -407,7 +473,10 @@
 	park = ausweis
 EOF
 
-test_expect_success '--set in alternative GIT_CONFIG' 'cmp other-config expect'
+test_expect_success '--set in alternative GIT_CONFIG' '
+	GIT_CONFIG=other-config git config anwohner.park ausweis &&
+	test_cmp expect other-config
+'
 
 cat > .git/config << EOF
 # Hallo
@@ -481,6 +550,14 @@
 
 test_expect_success "rename succeeded" "test_cmp expect .git/config"
 
+test_expect_success 'renaming empty section name is rejected' '
+	test_must_fail git config --rename-section branch.zwei ""
+'
+
+test_expect_success 'renaming to bogus section is rejected' '
+	test_must_fail git config --rename-section branch.zwei "bogus name"
+'
+
 cat >> .git/config << EOF
   [branch "zwei"] a = 1 [branch "vier"]
 EOF
@@ -497,8 +574,6 @@
 test_expect_success "section was removed properly" \
 	"test_cmp expect .git/config"
 
-rm .git/config
-
 cat > expect << EOF
 [gitcvs]
 	enabled = true
@@ -509,10 +584,11 @@
 
 test_expect_success 'section ending' '
 
+	rm -f .git/config &&
 	git config gitcvs.enabled true &&
 	git config gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite &&
 	git config gitcvs.dbname %Ggitcvs2.%a.%m.sqlite &&
-	cmp .git/config expect
+	test_cmp expect .git/config
 
 '
 
@@ -581,8 +657,6 @@
 
 	test_must_fail git config --bool bool.nobool foobar'
 
-rm .git/config
-
 cat > expect <<\EOF
 [bool]
 	true1 = true
@@ -597,6 +671,7 @@
 
 test_expect_success 'set --bool' '
 
+	rm -f .git/config &&
 	git config --bool bool.true1 01 &&
 	git config --bool bool.true2 -1 &&
 	git config --bool bool.true3 YeS &&
@@ -607,8 +682,6 @@
 	git config --bool bool.false4 FALSE &&
 	cmp expect .git/config'
 
-rm .git/config
-
 cat > expect <<\EOF
 [int]
 	val1 = 1
@@ -618,13 +691,12 @@
 
 test_expect_success 'set --int' '
 
+	rm -f .git/config &&
 	git config --int int.val1 01 &&
 	git config --int int.val2 -1 &&
 	git config --int int.val3 5m &&
 	cmp expect .git/config'
 
-rm .git/config
-
 cat >expect <<\EOF
 [bool]
 	true1 = true
@@ -638,6 +710,7 @@
 EOF
 
 test_expect_success 'get --bool-or-int' '
+	rm -f .git/config &&
 	(
 		echo "[bool]"
 		echo true1
@@ -657,7 +730,6 @@
 
 '
 
-rm .git/config
 cat >expect <<\EOF
 [bool]
 	true1 = true
@@ -671,6 +743,7 @@
 EOF
 
 test_expect_success 'set --bool-or-int' '
+	rm -f .git/config &&
 	git config --bool-or-int bool.true1 true &&
 	git config --bool-or-int bool.false1 false &&
 	git config --bool-or-int bool.true2 yes &&
@@ -681,12 +754,54 @@
 	test_cmp expect .git/config
 '
 
-rm .git/config
+cat >expect <<\EOF
+[path]
+	home = ~/
+	normal = /dev/null
+	trailingtilde = foo~
+EOF
 
-git config quote.leading " test"
-git config quote.ending "test "
-git config quote.semicolon "test;test"
-git config quote.hash "test#test"
+test_expect_success NOT_MINGW 'set --path' '
+	rm -f .git/config &&
+	git config --path path.home "~/" &&
+	git config --path path.normal "/dev/null" &&
+	git config --path path.trailingtilde "foo~" &&
+	test_cmp expect .git/config'
+
+if test_have_prereq NOT_MINGW && test "${HOME+set}"
+then
+	test_set_prereq HOMEVAR
+fi
+
+cat >expect <<EOF
+$HOME/
+/dev/null
+foo~
+EOF
+
+test_expect_success HOMEVAR 'get --path' '
+	git config --get --path path.home > result &&
+	git config --get --path path.normal >> result &&
+	git config --get --path path.trailingtilde >> result &&
+	test_cmp expect result
+'
+
+cat >expect <<\EOF
+/dev/null
+foo~
+EOF
+
+test_expect_success NOT_MINGW 'get --path copes with unset $HOME' '
+	(
+		unset HOME;
+		test_must_fail git config --get --path path.home \
+			>result 2>msg &&
+		git config --get --path path.normal >>result &&
+		git config --get --path path.trailingtilde >>result
+	) &&
+	grep "[Ff]ailed to expand.*~/" msg &&
+	test_cmp expect result
+'
 
 cat > expect << EOF
 [quote]
@@ -695,8 +810,14 @@
 	semicolon = "test;test"
 	hash = "test#test"
 EOF
-
-test_expect_success 'quoting' 'cmp .git/config expect'
+test_expect_success 'quoting' '
+	rm -f .git/config &&
+	git config quote.leading " test" &&
+	git config quote.ending "test " &&
+	git config quote.semicolon "test;test" &&
+	git config quote.hash "test#test" &&
+	test_cmp expect .git/config
+'
 
 test_expect_success 'key with newline' '
 	test_must_fail git config "key.with
@@ -721,9 +842,10 @@
 section.quotecont=cont;inued
 EOF
 
-git config --list > result
-
-test_expect_success 'value continued on next line' 'cmp result expect'
+test_expect_success 'value continued on next line' '
+	git config --list > result &&
+	cmp result expect
+'
 
 cat > .git/config <<\EOF
 [section "sub=section"]
@@ -744,16 +866,17 @@
 Qsection.sub=section.val4
 Qsection.sub=section.val5Q
 EOF
+test_expect_success '--null --list' '
+	git config --null --list | nul_to_q >result &&
+	echo >>result &&
+	test_cmp expect result
+'
 
-git config --null --list | perl -pe 'y/\000/Q/' > result
-echo >>result
-
-test_expect_success '--null --list' 'cmp result expect'
-
-git config --null --get-regexp 'val[0-9]' | perl -pe 'y/\000/Q/' > result
-echo >>result
-
-test_expect_success '--null --get-regexp' 'cmp result expect'
+test_expect_success '--null --get-regexp' '
+	git config --null --get-regexp "val[0-9]" | nul_to_q >result &&
+	echo >>result &&
+	test_cmp expect result
+'
 
 test_expect_success 'inner whitespace kept verbatim' '
 	git config section.val "foo 	  bar" &&
@@ -775,6 +898,27 @@
 
 '
 
+test_expect_success 'nonexistent configuration' '
+	(
+		GIT_CONFIG=doesnotexist &&
+		export GIT_CONFIG &&
+		test_must_fail git config --list &&
+		test_must_fail git config test.xyzzy
+	)
+'
+
+test_expect_success SYMLINKS 'symlink to nonexistent configuration' '
+	ln -s doesnotexist linktonada &&
+	ln -s linktonada linktolinktonada &&
+	(
+		GIT_CONFIG=linktonada &&
+		export GIT_CONFIG &&
+		test_must_fail git config --list &&
+		GIT_CONFIG=linktolinktonada &&
+		test_must_fail git config --list
+	)
+'
+
 test_expect_success 'check split_cmdline return' "
 	git config alias.split-cmdline-fix 'echo \"' &&
 	test_must_fail git split-cmdline-fix &&
@@ -785,4 +929,99 @@
 	test_must_fail git merge master
 	"
 
+test_expect_success 'git -c "key=value" support' '
+	test "z$(git -c core.name=value config core.name)" = zvalue &&
+	test "z$(git -c foo.CamelCase=value config foo.camelcase)" = zvalue &&
+	test "z$(git -c foo.flag config --bool foo.flag)" = ztrue &&
+	test_must_fail git -c name=value config core.name
+'
+
+test_expect_success 'key sanity-checking' '
+	test_must_fail git config foo=bar &&
+	test_must_fail git config foo=.bar &&
+	test_must_fail git config foo.ba=r &&
+	test_must_fail git config foo.1bar &&
+	test_must_fail git config foo."ba
+				z".bar &&
+	test_must_fail git config . false &&
+	test_must_fail git config .foo false &&
+	test_must_fail git config foo. false &&
+	test_must_fail git config .foo. false &&
+	git config foo.bar true &&
+	git config foo."ba =z".bar false
+'
+
+test_expect_success 'git -c works with aliases of builtins' '
+	git config alias.checkconfig "-c foo.check=bar config foo.check" &&
+	echo bar >expect &&
+	git checkconfig >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git -c does not split values on equals' '
+	echo "value with = in it" >expect &&
+	git -c core.foo="value with = in it" config core.foo >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git -c dies on bogus config' '
+	test_must_fail git -c core.bare=foo rev-parse
+'
+
+test_expect_success 'git -c complains about empty key' '
+	test_must_fail git -c "=foo" rev-parse
+'
+
+test_expect_success 'git -c complains about empty key and value' '
+	test_must_fail git -c "" rev-parse
+'
+
+test_expect_success 'git config --edit works' '
+	git config -f tmp test.value no &&
+	echo test.value=yes >expect &&
+	GIT_EDITOR="echo [test]value=yes >" git config -f tmp --edit &&
+	git config -f tmp --list >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git config --edit respects core.editor' '
+	git config -f tmp test.value no &&
+	echo test.value=yes >expect &&
+	test_config core.editor "echo [test]value=yes >" &&
+	git config -f tmp --edit &&
+	git config -f tmp --list >actual &&
+	test_cmp expect actual
+'
+
+# malformed configuration files
+test_expect_success 'barf on syntax error' '
+	cat >.git/config <<-\EOF &&
+	# broken section line
+	[section]
+	key garbage
+	EOF
+	test_must_fail git config --get section.key >actual 2>error &&
+	grep " line 3 " error
+'
+
+test_expect_success 'barf on incomplete section header' '
+	cat >.git/config <<-\EOF &&
+	# broken section line
+	[section
+	key = value
+	EOF
+	test_must_fail git config --get section.key >actual 2>error &&
+	grep " line 2 " error
+'
+
+test_expect_success 'barf on incomplete string' '
+	cat >.git/config <<-\EOF &&
+	# broken section line
+	[section]
+	key = "value string
+	EOF
+	test_must_fail git config --get section.key >actual 2>error &&
+	grep " line 3 " error
+'
+
 test_done
diff --git a/t/t1302-repo-version.sh b/t/t1302-repo-version.sh
index 8d305b4..0e47662 100755
--- a/t/t1302-repo-version.sh
+++ b/t/t1302-repo-version.sh
@@ -7,41 +7,64 @@
 
 . ./test-lib.sh
 
-cat >test.patch <<EOF
-diff --git a/test.txt b/test.txt
-new file mode 100644
---- /dev/null
-+++ b/test.txt
-@@ -0,0 +1 @@
-+123
-EOF
+test_expect_success 'setup' '
+	cat >test.patch <<-\EOF &&
+	diff --git a/test.txt b/test.txt
+	new file mode 100644
+	--- /dev/null
+	+++ b/test.txt
+	@@ -0,0 +1 @@
+	+123
+	EOF
 
-test_create_repo "test"
-test_create_repo "test2"
-
-GIT_CONFIG=test2/.git/config git config core.repositoryformatversion 99 || exit 1
+	test_create_repo "test" &&
+	test_create_repo "test2" &&
+	GIT_CONFIG=test2/.git/config git config core.repositoryformatversion 99
+'
 
 test_expect_success 'gitdir selection on normal repos' '
-	(test "$(git config core.repositoryformatversion)" = 0 &&
-	cd test &&
-	test "$(git config core.repositoryformatversion)" = 0)'
+	echo 0 >expect &&
+	git config core.repositoryformatversion >actual &&
+	(
+		cd test &&
+		git config core.repositoryformatversion >../actual2
+	) &&
+	test_cmp expect actual &&
+	test_cmp expect actual2
+'
 
-# Make sure it would stop at test2, not trash
 test_expect_success 'gitdir selection on unsupported repo' '
-	(cd test2 &&
-	test "$(git config core.repositoryformatversion)" = 99)'
+	# Make sure it would stop at test2, not trash
+	echo 99 >expect &&
+	(
+		cd test2 &&
+		git config core.repositoryformatversion >../actual
+	) &&
+	test_cmp expect actual
+'
 
 test_expect_success 'gitdir not required mode' '
-	(git apply --stat test.patch &&
-	cd test && git apply --stat ../test.patch &&
-	cd ../test2 && git apply --stat ../test.patch)'
+	git apply --stat test.patch &&
+	(
+		cd test &&
+		git apply --stat ../test.patch
+	) &&
+	(
+		cd test2 &&
+		git apply --stat ../test.patch
+	)
+'
 
-test_expect_success 'gitdir required mode on normal repos' '
-	(git apply --check --index test.patch &&
-	cd test && git apply --check --index ../test.patch)'
-
-test_expect_success 'gitdir required mode on unsupported repo' '
-	(cd test2 && test_must_fail git apply --check --index ../test.patch)
+test_expect_success 'gitdir required mode' '
+	git apply --check --index test.patch &&
+	(
+		cd test &&
+		git apply --check --index ../test.patch
+	) &&
+	(
+		cd test2 &&
+		test_must_fail git apply --check --index ../test.patch
+	)
 '
 
 test_done
diff --git a/t/t1303-wacky-config.sh b/t/t1303-wacky-config.sh
index 080117c..46103a1 100755
--- a/t/t1303-wacky-config.sh
+++ b/t/t1303-wacky-config.sh
@@ -44,7 +44,7 @@
 test_expect_success 'do not crash on special long config line' '
 	setup &&
 	git config section.key "$LONG_VALUE" &&
-	check section.key "fatal: bad config file line 2 in .git/config"
+	check section.key "$LONG_VALUE"
 '
 
 test_done
diff --git a/t/t1304-default-acl.sh b/t/t1304-default-acl.sh
new file mode 100755
index 0000000..79045ab
--- /dev/null
+++ b/t/t1304-default-acl.sh
@@ -0,0 +1,65 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Matthieu Moy
+#
+
+test_description='Test repository with default ACL'
+
+# Create the test repo with restrictive umask
+# => this must come before . ./test-lib.sh
+umask 077
+
+. ./test-lib.sh
+
+# We need an arbitrary other user give permission to using ACLs. root
+# is a good candidate: exists on all unices, and it has permission
+# anyway, so we don't create a security hole running the testsuite.
+test_expect_success 'checking for a working acl setup' '
+	if setfacl -m d:m:rwx -m u:root:rwx . &&
+	   getfacl . | grep user:root:rwx &&
+	   touch should-have-readable-acl &&
+	   getfacl should-have-readable-acl | egrep "mask::?rw-"
+	then
+		test_set_prereq SETFACL
+	fi
+'
+
+if test -z "$LOGNAME"
+then
+	LOGNAME=$USER
+fi
+
+check_perms_and_acl () {
+	test -r "$1" &&
+	getfacl "$1" > actual &&
+	grep -q "user:root:rwx" actual &&
+	grep -q "user:${LOGNAME}:rwx" actual &&
+	egrep "mask::?r--" actual > /dev/null 2>&1 &&
+	grep -q "group::---" actual || false
+}
+
+dirs_to_set="./ .git/ .git/objects/ .git/objects/pack/"
+
+test_expect_success SETFACL 'Setup test repo' '
+	setfacl -m d:u::rwx,d:g::---,d:o:---,d:m:rwx $dirs_to_set &&
+	setfacl -m m:rwx               $dirs_to_set &&
+	setfacl -m u:root:rwx          $dirs_to_set &&
+	setfacl -m d:u:"$LOGNAME":rwx  $dirs_to_set &&
+	setfacl -m d:u:root:rwx        $dirs_to_set &&
+
+	touch file.txt &&
+	git add file.txt &&
+	git commit -m "init"
+'
+
+test_expect_success SETFACL 'Objects creation does not break ACLs with restrictive umask' '
+	# SHA1 for empty blob
+	check_perms_and_acl .git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
+'
+
+test_expect_success SETFACL 'git gc does not break ACLs with restrictive umask' '
+	git gc &&
+	check_perms_and_acl .git/objects/pack/*.pack
+'
+
+test_done
diff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh
new file mode 100755
index 0000000..a707076
--- /dev/null
+++ b/t/t1305-config-include.sh
@@ -0,0 +1,142 @@
+#!/bin/sh
+
+test_description='test config file include directives'
+. ./test-lib.sh
+
+test_expect_success 'include file by absolute path' '
+	echo "[test]one = 1" >one &&
+	echo "[include]path = \"$(pwd)/one\"" >.gitconfig &&
+	echo 1 >expect &&
+	git config test.one >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'include file by relative path' '
+	echo "[test]one = 1" >one &&
+	echo "[include]path = one" >.gitconfig &&
+	echo 1 >expect &&
+	git config test.one >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'chained relative paths' '
+	mkdir subdir &&
+	echo "[test]three = 3" >subdir/three &&
+	echo "[include]path = three" >subdir/two &&
+	echo "[include]path = subdir/two" >.gitconfig &&
+	echo 3 >expect &&
+	git config test.three >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'include paths get tilde-expansion' '
+	echo "[test]one = 1" >one &&
+	echo "[include]path = ~/one" >.gitconfig &&
+	echo 1 >expect &&
+	git config test.one >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'include options can still be examined' '
+	echo "[test]one = 1" >one &&
+	echo "[include]path = one" >.gitconfig &&
+	echo one >expect &&
+	git config include.path >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'listing includes option and expansion' '
+	echo "[test]one = 1" >one &&
+	echo "[include]path = one" >.gitconfig &&
+	cat >expect <<-\EOF &&
+	include.path=one
+	test.one=1
+	EOF
+	git config --list >actual.full &&
+	grep -v ^core actual.full >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'single file lookup does not expand includes by default' '
+	echo "[test]one = 1" >one &&
+	echo "[include]path = one" >.gitconfig &&
+	test_must_fail git config -f .gitconfig test.one &&
+	test_must_fail git config --global test.one &&
+	echo 1 >expect &&
+	git config --includes -f .gitconfig test.one >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'single file list does not expand includes by default' '
+	echo "[test]one = 1" >one &&
+	echo "[include]path = one" >.gitconfig &&
+	echo "include.path=one" >expect &&
+	git config -f .gitconfig --list >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'writing config file does not expand includes' '
+	echo "[test]one = 1" >one &&
+	echo "[include]path = one" >.gitconfig &&
+	git config test.two 2 &&
+	echo 2 >expect &&
+	git config --no-includes test.two >actual &&
+	test_cmp expect actual &&
+	test_must_fail git config --no-includes test.one
+'
+
+test_expect_success 'config modification does not affect includes' '
+	echo "[test]one = 1" >one &&
+	echo "[include]path = one" >.gitconfig &&
+	git config test.one 2 &&
+	echo 1 >expect &&
+	git config -f one test.one >actual &&
+	test_cmp expect actual &&
+	cat >expect <<-\EOF &&
+	1
+	2
+	EOF
+	git config --get-all test.one >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'missing include files are ignored' '
+	cat >.gitconfig <<-\EOF &&
+	[include]path = foo
+	[test]value = yes
+	EOF
+	echo yes >expect &&
+	git config test.value >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'absolute includes from command line work' '
+	echo "[test]one = 1" >one &&
+	echo 1 >expect &&
+	git -c include.path="$PWD/one" config test.one >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'relative includes from command line fail' '
+	echo "[test]one = 1" >one &&
+	test_must_fail git -c include.path=one config test.one
+'
+
+test_expect_success 'include cycles are detected' '
+	cat >.gitconfig <<-\EOF &&
+	[test]value = gitconfig
+	[include]path = cycle
+	EOF
+	cat >cycle <<-\EOF &&
+	[test]value = cycle
+	[include]path = .gitconfig
+	EOF
+	cat >expect <<-\EOF &&
+	gitconfig
+	cycle
+	EOF
+	test_must_fail git config --get-all test.value 2>stderr &&
+	grep "exceeded maximum include depth" stderr
+'
+
+test_done
diff --git a/t/t1306-xdg-files.sh b/t/t1306-xdg-files.sh
new file mode 100755
index 0000000..3c75c3f
--- /dev/null
+++ b/t/t1306-xdg-files.sh
@@ -0,0 +1,158 @@
+#!/bin/sh
+#
+# Copyright (c) 2012 Valentin Duperray, Lucien Kong, Franck Jonas,
+#		     Thomas Nguy, Khoi Nguyen
+#		     Grenoble INP Ensimag
+#
+
+test_description='Compatibility with $XDG_CONFIG_HOME/git/ files'
+
+. ./test-lib.sh
+
+test_expect_success 'read config: xdg file exists and ~/.gitconfig doesn'\''t' '
+	mkdir -p .config/git &&
+	echo "[alias]" >.config/git/config &&
+	echo "	myalias = !echo in_config" >>.config/git/config &&
+	echo in_config >expected &&
+	git myalias >actual &&
+	test_cmp expected actual
+'
+
+
+test_expect_success 'read config: xdg file exists and ~/.gitconfig exists' '
+	>.gitconfig &&
+	echo "[alias]" >.gitconfig &&
+	echo "	myalias = !echo in_gitconfig" >>.gitconfig &&
+	echo in_gitconfig >expected &&
+	git myalias >actual &&
+	test_cmp expected actual
+'
+
+
+test_expect_success 'read with --get: xdg file exists and ~/.gitconfig doesn'\''t' '
+	rm .gitconfig &&
+	echo "[user]" >.config/git/config &&
+	echo "	name = read_config" >>.config/git/config &&
+	echo read_config >expected &&
+	git config --get user.name >actual &&
+	test_cmp expected actual
+'
+
+
+test_expect_success 'read with --get: xdg file exists and ~/.gitconfig exists' '
+	>.gitconfig &&
+	echo "[user]" >.gitconfig &&
+	echo "	name = read_gitconfig" >>.gitconfig &&
+	echo read_gitconfig >expected &&
+	git config --get user.name >actual &&
+	test_cmp expected actual
+'
+
+
+test_expect_success 'read with --list: xdg file exists and ~/.gitconfig doesn'\''t' '
+	rm .gitconfig &&
+	echo user.name=read_config >expected &&
+	git config --global --list >actual &&
+	test_cmp expected actual
+'
+
+
+test_expect_success 'read with --list: xdg file exists and ~/.gitconfig exists' '
+	>.gitconfig &&
+	echo "[user]" >.gitconfig &&
+	echo "	name = read_gitconfig" >>.gitconfig &&
+	echo user.name=read_gitconfig >expected &&
+	git config --global --list >actual &&
+	test_cmp expected actual
+'
+
+
+test_expect_success 'Setup' '
+	git init git &&
+	cd git &&
+	echo foo >to_be_excluded
+'
+
+
+test_expect_success 'Exclusion of a file in the XDG ignore file' '
+	mkdir -p "$HOME"/.config/git/ &&
+	echo to_be_excluded >"$HOME"/.config/git/ignore &&
+	test_must_fail git add to_be_excluded
+'
+
+
+test_expect_success 'Exclusion in both XDG and local ignore files' '
+	echo to_be_excluded >.gitignore &&
+	test_must_fail git add to_be_excluded
+'
+
+
+test_expect_success 'Exclusion in a non-XDG global ignore file' '
+	rm .gitignore &&
+	echo >"$HOME"/.config/git/ignore &&
+	echo to_be_excluded >"$HOME"/my_gitignore &&
+	git config core.excludesfile "$HOME"/my_gitignore &&
+	test_must_fail git add to_be_excluded
+'
+
+
+test_expect_success 'Checking attributes in the XDG attributes file' '
+	echo foo >f &&
+	git check-attr -a f >actual &&
+	test_line_count -eq 0 actual &&
+	echo "f attr_f" >"$HOME"/.config/git/attributes &&
+	echo "f: attr_f: set" >expected &&
+	git check-attr -a f >actual &&
+	test_cmp expected actual
+'
+
+
+test_expect_success 'Checking attributes in both XDG and local attributes files' '
+	echo "f -attr_f" >.gitattributes &&
+	echo "f: attr_f: unset" >expected &&
+	git check-attr -a f >actual &&
+	test_cmp expected actual
+'
+
+
+test_expect_success 'Checking attributes in a non-XDG global attributes file' '
+	test_might_fail rm .gitattributes &&
+	echo "f attr_f=test" >"$HOME"/my_gitattributes &&
+	git config core.attributesfile "$HOME"/my_gitattributes &&
+	echo "f: attr_f: test" >expected &&
+	git check-attr -a f >actual &&
+	test_cmp expected actual
+'
+
+
+test_expect_success 'write: xdg file exists and ~/.gitconfig doesn'\''t' '
+	mkdir -p "$HOME"/.config/git &&
+	>"$HOME"/.config/git/config &&
+	test_might_fail rm "$HOME"/.gitconfig &&
+	git config --global user.name "write_config" &&
+	echo "[user]" >expected &&
+	echo "	name = write_config" >>expected &&
+	test_cmp expected "$HOME"/.config/git/config
+'
+
+
+test_expect_success 'write: xdg file exists and ~/.gitconfig exists' '
+	>"$HOME"/.gitconfig &&
+	git config --global user.name "write_gitconfig" &&
+	echo "[user]" >expected &&
+	echo "	name = write_gitconfig" >>expected &&
+	test_cmp expected "$HOME"/.gitconfig
+'
+
+
+test_expect_success 'write: ~/.config/git/ exists and config file doesn'\''t' '
+	test_might_fail rm "$HOME"/.gitconfig &&
+	test_might_fail rm "$HOME"/.config/git/config &&
+	git config --global user.name "write_gitconfig" &&
+	echo "[user]" >expected &&
+	echo "	name = write_gitconfig" >>expected &&
+	test_cmp expected "$HOME"/.gitconfig
+'
+
+
+test_done
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 54ba3df..4fd83a6 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -6,7 +6,7 @@
 test_description='Test git update-ref and basic ref logging'
 . ./test-lib.sh
 
-Z=0000000000000000000000000000000000000000
+Z=$_z40
 
 test_expect_success setup '
 
@@ -52,9 +52,8 @@
 
 test_expect_success \
 	"fail to create $n" \
-	"touch .git/$n_dir
-	 git update-ref $n $A >out 2>err"'
-	 test $? != 0'
+	"touch .git/$n_dir &&
+	 test_must_fail git update-ref $n $A >out 2>err"
 rm -f .git/$n_dir out err
 
 test_expect_success \
@@ -185,55 +184,55 @@
 ld="Thu, 26 May 2005 18:43:00 -0500"
 test_expect_success \
 	'Query "master@{May 25 2005}" (before history)' \
-	'rm -f o e
+	'rm -f o e &&
 	 git rev-parse --verify "master@{May 25 2005}" >o 2>e &&
 	 test '"$C"' = $(cat o) &&
 	 test "warning: Log for '\'master\'' only goes back to $ed." = "$(cat e)"'
 test_expect_success \
 	"Query master@{2005-05-25} (before history)" \
-	'rm -f o e
+	'rm -f o e &&
 	 git rev-parse --verify master@{2005-05-25} >o 2>e &&
 	 test '"$C"' = $(cat o) &&
 	 echo test "warning: Log for '\'master\'' only goes back to $ed." = "$(cat e)"'
 test_expect_success \
 	'Query "master@{May 26 2005 23:31:59}" (1 second before history)' \
-	'rm -f o e
+	'rm -f o e &&
 	 git rev-parse --verify "master@{May 26 2005 23:31:59}" >o 2>e &&
 	 test '"$C"' = $(cat o) &&
 	 test "warning: Log for '\''master'\'' only goes back to $ed." = "$(cat e)"'
 test_expect_success \
 	'Query "master@{May 26 2005 23:32:00}" (exactly history start)' \
-	'rm -f o e
+	'rm -f o e &&
 	 git rev-parse --verify "master@{May 26 2005 23:32:00}" >o 2>e &&
 	 test '"$C"' = $(cat o) &&
 	 test "" = "$(cat e)"'
 test_expect_success \
 	'Query "master@{May 26 2005 23:32:30}" (first non-creation change)' \
-	'rm -f o e
+	'rm -f o e &&
 	 git rev-parse --verify "master@{May 26 2005 23:32:30}" >o 2>e &&
 	 test '"$A"' = $(cat o) &&
 	 test "" = "$(cat e)"'
 test_expect_success \
 	'Query "master@{2005-05-26 23:33:01}" (middle of history with gap)' \
-	'rm -f o e
+	'rm -f o e &&
 	 git rev-parse --verify "master@{2005-05-26 23:33:01}" >o 2>e &&
 	 test '"$B"' = $(cat o) &&
 	 test "warning: Log .git/logs/'"$m has gap after $gd"'." = "$(cat e)"'
 test_expect_success \
 	'Query "master@{2005-05-26 23:38:00}" (middle of history)' \
-	'rm -f o e
+	'rm -f o e &&
 	 git rev-parse --verify "master@{2005-05-26 23:38:00}" >o 2>e &&
 	 test '"$Z"' = $(cat o) &&
 	 test "" = "$(cat e)"'
 test_expect_success \
 	'Query "master@{2005-05-26 23:43:00}" (exact end of history)' \
-	'rm -f o e
+	'rm -f o e &&
 	 git rev-parse --verify "master@{2005-05-26 23:43:00}" >o 2>e &&
 	 test '"$E"' = $(cat o) &&
 	 test "" = "$(cat e)"'
 test_expect_success \
 	'Query "master@{2005-05-28}" (past end of history)' \
-	'rm -f o e
+	'rm -f o e &&
 	 git rev-parse --verify "master@{2005-05-28}" >o 2>e &&
 	 test '"$D"' = $(cat o) &&
 	 test "warning: Log .git/logs/'"$m unexpectedly ended on $ld"'." = "$(cat e)"'
@@ -247,7 +246,7 @@
      git add F &&
 	 GIT_AUTHOR_DATE="2005-05-26 23:30" \
 	 GIT_COMMITTER_DATE="2005-05-26 23:30" git commit -m add -a &&
-	 h_TEST=$(git rev-parse --verify HEAD)
+	 h_TEST=$(git rev-parse --verify HEAD) &&
 	 echo The other day this did not work. >M &&
 	 echo And then Bob told me how to fix it. >>M &&
 	 echo OTHER >F &&
diff --git a/t/t1401-symbolic-ref.sh b/t/t1401-symbolic-ref.sh
index 7fa5f5b..2c96551 100755
--- a/t/t1401-symbolic-ref.sh
+++ b/t/t1401-symbolic-ref.sh
@@ -28,7 +28,7 @@
 reset_to_sane
 
 test_expect_success 'symbolic-ref refuses bare sha1' '
-	echo content >file && git add file && git commit -m one
+	echo content >file && git add file && git commit -m one &&
 	test_must_fail git symbolic-ref HEAD `git rev-parse HEAD`
 '
 reset_to_sane
diff --git a/t/t1402-check-ref-format.sh b/t/t1402-check-ref-format.sh
new file mode 100755
index 0000000..1ae4d87
--- /dev/null
+++ b/t/t1402-check-ref-format.sh
@@ -0,0 +1,197 @@
+#!/bin/sh
+
+test_description='Test git check-ref-format'
+
+. ./test-lib.sh
+
+valid_ref() {
+	prereq=
+	case $1 in
+	[A-Z]*)
+		prereq=$1
+		shift
+	esac
+	test_expect_success $prereq "ref name '$1' is valid${2:+ with options $2}" "
+		git check-ref-format $2 '$1'
+	"
+}
+invalid_ref() {
+	prereq=
+	case $1 in
+	[A-Z]*)
+		prereq=$1
+		shift
+	esac
+	test_expect_success $prereq "ref name '$1' is invalid${2:+ with options $2}" "
+		test_must_fail git check-ref-format $2 '$1'
+	"
+}
+
+invalid_ref ''
+invalid_ref NOT_MINGW '/'
+invalid_ref NOT_MINGW '/' --allow-onelevel
+invalid_ref NOT_MINGW '/' --normalize
+invalid_ref NOT_MINGW '/' '--allow-onelevel --normalize'
+valid_ref 'foo/bar/baz'
+valid_ref 'foo/bar/baz' --normalize
+invalid_ref 'refs///heads/foo'
+valid_ref 'refs///heads/foo' --normalize
+invalid_ref 'heads/foo/'
+invalid_ref NOT_MINGW '/heads/foo'
+valid_ref NOT_MINGW '/heads/foo' --normalize
+invalid_ref '///heads/foo'
+valid_ref '///heads/foo' --normalize
+invalid_ref './foo'
+invalid_ref './foo/bar'
+invalid_ref 'foo/./bar'
+invalid_ref 'foo/bar/.'
+invalid_ref '.refs/foo'
+invalid_ref 'heads/foo..bar'
+invalid_ref 'heads/foo?bar'
+valid_ref 'foo./bar'
+invalid_ref 'heads/foo.lock'
+invalid_ref 'heads///foo.lock'
+invalid_ref 'foo.lock/bar'
+invalid_ref 'foo.lock///bar'
+valid_ref 'heads/foo@bar'
+invalid_ref 'heads/v@{ation'
+invalid_ref 'heads/foo\bar'
+invalid_ref "$(printf 'heads/foo\t')"
+invalid_ref "$(printf 'heads/foo\177')"
+valid_ref "$(printf 'heads/fu\303\237')"
+invalid_ref 'heads/*foo/bar' --refspec-pattern
+invalid_ref 'heads/foo*/bar' --refspec-pattern
+invalid_ref 'heads/f*o/bar' --refspec-pattern
+
+ref='foo'
+invalid_ref "$ref"
+valid_ref "$ref" --allow-onelevel
+invalid_ref "$ref" --refspec-pattern
+valid_ref "$ref" '--refspec-pattern --allow-onelevel'
+invalid_ref "$ref" --normalize
+valid_ref "$ref" '--allow-onelevel --normalize'
+
+ref='foo/bar'
+valid_ref "$ref"
+valid_ref "$ref" --allow-onelevel
+valid_ref "$ref" --refspec-pattern
+valid_ref "$ref" '--refspec-pattern --allow-onelevel'
+valid_ref "$ref" --normalize
+
+ref='foo/*'
+invalid_ref "$ref"
+invalid_ref "$ref" --allow-onelevel
+valid_ref "$ref" --refspec-pattern
+valid_ref "$ref" '--refspec-pattern --allow-onelevel'
+
+ref='*/foo'
+invalid_ref "$ref"
+invalid_ref "$ref" --allow-onelevel
+valid_ref "$ref" --refspec-pattern
+valid_ref "$ref" '--refspec-pattern --allow-onelevel'
+invalid_ref "$ref" --normalize
+valid_ref "$ref" '--refspec-pattern --normalize'
+
+ref='foo/*/bar'
+invalid_ref "$ref"
+invalid_ref "$ref" --allow-onelevel
+valid_ref "$ref" --refspec-pattern
+valid_ref "$ref" '--refspec-pattern --allow-onelevel'
+
+ref='*'
+invalid_ref "$ref"
+invalid_ref "$ref" --allow-onelevel
+invalid_ref "$ref" --refspec-pattern
+valid_ref "$ref" '--refspec-pattern --allow-onelevel'
+
+ref='foo/*/*'
+invalid_ref "$ref" --refspec-pattern
+invalid_ref "$ref" '--refspec-pattern --allow-onelevel'
+
+ref='*/foo/*'
+invalid_ref "$ref" --refspec-pattern
+invalid_ref "$ref" '--refspec-pattern --allow-onelevel'
+
+ref='*/*/foo'
+invalid_ref "$ref" --refspec-pattern
+invalid_ref "$ref" '--refspec-pattern --allow-onelevel'
+
+ref='/foo'
+invalid_ref NOT_MINGW "$ref"
+invalid_ref NOT_MINGW "$ref" --allow-onelevel
+invalid_ref NOT_MINGW "$ref" --refspec-pattern
+invalid_ref NOT_MINGW "$ref" '--refspec-pattern --allow-onelevel'
+invalid_ref NOT_MINGW "$ref" --normalize
+valid_ref NOT_MINGW "$ref" '--allow-onelevel --normalize'
+invalid_ref NOT_MINGW "$ref" '--refspec-pattern --normalize'
+valid_ref NOT_MINGW "$ref" '--refspec-pattern --allow-onelevel --normalize'
+
+test_expect_success "check-ref-format --branch @{-1}" '
+	T=$(git write-tree) &&
+	sha1=$(echo A | git commit-tree $T) &&
+	git update-ref refs/heads/master $sha1 &&
+	git update-ref refs/remotes/origin/master $sha1 &&
+	git checkout master &&
+	git checkout origin/master &&
+	git checkout master &&
+	refname=$(git check-ref-format --branch @{-1}) &&
+	test "$refname" = "$sha1" &&
+	refname2=$(git check-ref-format --branch @{-2}) &&
+	test "$refname2" = master'
+
+test_expect_success 'check-ref-format --branch from subdir' '
+	mkdir subdir &&
+
+	T=$(git write-tree) &&
+	sha1=$(echo A | git commit-tree $T) &&
+	git update-ref refs/heads/master $sha1 &&
+	git update-ref refs/remotes/origin/master $sha1 &&
+	git checkout master &&
+	git checkout origin/master &&
+	git checkout master &&
+	refname=$(
+		cd subdir &&
+		git check-ref-format --branch @{-1}
+	) &&
+	test "$refname" = "$sha1"
+'
+
+valid_ref_normalized() {
+	prereq=
+	case $1 in
+	[A-Z]*)
+		prereq=$1
+		shift
+	esac
+	test_expect_success $prereq "ref name '$1' simplifies to '$2'" "
+		refname=\$(git check-ref-format --normalize '$1') &&
+		test \"\$refname\" = '$2'
+	"
+}
+invalid_ref_normalized() {
+	prereq=
+	case $1 in
+	[A-Z]*)
+		prereq=$1
+		shift
+	esac
+	test_expect_success $prereq "check-ref-format --normalize rejects '$1'" "
+		test_must_fail git check-ref-format --normalize '$1'
+	"
+}
+
+valid_ref_normalized 'heads/foo' 'heads/foo'
+valid_ref_normalized 'refs///heads/foo' 'refs/heads/foo'
+valid_ref_normalized NOT_MINGW '/heads/foo' 'heads/foo'
+valid_ref_normalized '///heads/foo' 'heads/foo'
+invalid_ref_normalized 'foo'
+invalid_ref_normalized NOT_MINGW '/foo'
+invalid_ref_normalized 'heads/foo/../bar'
+invalid_ref_normalized 'heads/./foo'
+invalid_ref_normalized 'heads\foo'
+invalid_ref_normalized 'heads/foo.lock'
+invalid_ref_normalized 'heads///foo.lock'
+invalid_ref_normalized 'foo.lock/bar'
+invalid_ref_normalized 'foo.lock///bar'
+
+test_done
diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh
index 80af6b9..236b13a 100755
--- a/t/t1410-reflog.sh
+++ b/t/t1410-reflog.sh
@@ -100,8 +100,7 @@
 
 	check_fsck &&
 
-	loglen=$(wc -l <.git/logs/refs/heads/master) &&
-	test $loglen = 4
+	test_line_count = 4 .git/logs/refs/heads/master
 '
 
 test_expect_success rewind '
@@ -117,8 +116,7 @@
 
 	check_have A B C D E F G H I J K L &&
 
-	loglen=$(wc -l <.git/logs/refs/heads/master) &&
-	test $loglen = 5
+	test_line_count = 5 .git/logs/refs/heads/master
 '
 
 test_expect_success 'corrupt and check' '
@@ -136,8 +134,7 @@
 		--stale-fix \
 		--all &&
 
-	loglen=$(wc -l <.git/logs/refs/heads/master) &&
-	test $loglen = 5 &&
+	test_line_count = 5 .git/logs/refs/heads/master &&
 
 	check_fsck "missing blob $F"
 '
@@ -150,8 +147,7 @@
 		--stale-fix \
 		--all &&
 
-	loglen=$(wc -l <.git/logs/refs/heads/master) &&
-	test $loglen = 2 &&
+	test_line_count = 2 .git/logs/refs/heads/master &&
 
 	check_fsck "dangling commit $K"
 '
@@ -186,8 +182,8 @@
 	test_tick &&
 	git commit -m tiger C &&
 
-	HEAD_entry_count=$(git reflog | wc -l)
-	master_entry_count=$(git reflog show master | wc -l)
+	HEAD_entry_count=$(git reflog | wc -l) &&
+	master_entry_count=$(git reflog show master | wc -l) &&
 
 	test $HEAD_entry_count = 5 &&
 	test $master_entry_count = 5 &&
@@ -199,13 +195,13 @@
 	test $HEAD_entry_count = $(git reflog | wc -l) &&
 	! grep ox < output &&
 
-	master_entry_count=$(wc -l < output)
+	master_entry_count=$(wc -l < output) &&
 
 	git reflog delete HEAD@{1} &&
 	test $(($HEAD_entry_count -1)) = $(git reflog | wc -l) &&
 	test $master_entry_count = $(git reflog show master | wc -l) &&
 
-	HEAD_entry_count=$(git reflog | wc -l)
+	HEAD_entry_count=$(git reflog | wc -l) &&
 
 	git reflog delete master@{07.04.2005.15:15:00.-0700} &&
 	git reflog show master > output &&
@@ -214,4 +210,39 @@
 
 '
 
+test_expect_success 'rewind2' '
+
+	test_tick && git reset --hard HEAD~2 &&
+	test_line_count = 4 .git/logs/refs/heads/master
+'
+
+test_expect_success '--expire=never' '
+
+	git reflog expire --verbose \
+		--expire=never \
+		--expire-unreachable=never \
+		--all &&
+	test_line_count = 4 .git/logs/refs/heads/master
+'
+
+test_expect_success 'gc.reflogexpire=never' '
+
+	git config gc.reflogexpire never &&
+	git config gc.reflogexpireunreachable never &&
+	git reflog expire --verbose --all &&
+	test_line_count = 4 .git/logs/refs/heads/master
+'
+
+test_expect_success 'gc.reflogexpire=false' '
+
+	git config gc.reflogexpire false &&
+	git config gc.reflogexpireunreachable false &&
+	git reflog expire --verbose --all &&
+	test_line_count = 4 .git/logs/refs/heads/master &&
+
+	git config --unset gc.reflogexpire &&
+	git config --unset gc.reflogexpireunreachable
+
+'
+
 test_done
diff --git a/t/t1411-reflog-show.sh b/t/t1411-reflog-show.sh
index c18ed8e..9a105fe 100755
--- a/t/t1411-reflog-show.sh
+++ b/t/t1411-reflog-show.sh
@@ -28,6 +28,24 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'reflog default format' '
+	git reflog -1 >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+commit e46513e
+Reflog: HEAD@{0} (C O Mitter <committer@example.com>)
+Reflog message: commit (initial): one
+Author: A U Thor <author@example.com>
+
+    one
+EOF
+test_expect_success 'override reflog default format' '
+	git reflog --format=short -1 >actual &&
+	test_cmp expect actual
+'
+
 cat >expect <<'EOF'
 Reflog: HEAD@{Thu Apr 7 15:13:13 2005 -0700} (C O Mitter <committer@example.com>)
 Reflog message: commit (initial): one
@@ -47,20 +65,82 @@
 '
 
 cat >expect <<'EOF'
-Reflog: HEAD@{1112911993 -0700} (C O Mitter <committer@example.com>)
+HEAD@{Thu Apr 7 15:13:13 2005 -0700}
+EOF
+test_expect_success 'using @{now} syntax shows reflog date (format=%gd)' '
+	git log -g -1 --format=%gd HEAD@{now} >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+Reflog: HEAD@{Thu Apr 7 15:13:13 2005 -0700} (C O Mitter <committer@example.com>)
 Reflog message: commit (initial): one
 EOF
 test_expect_success 'using --date= shows reflog date (multiline)' '
-	git log -g -1 --date=raw >tmp &&
+	git log -g -1 --date=default >tmp &&
 	grep ^Reflog <tmp >actual &&
 	test_cmp expect actual
 '
 
 cat >expect <<'EOF'
-e46513e HEAD@{1112911993 -0700}: commit (initial): one
+e46513e HEAD@{Thu Apr 7 15:13:13 2005 -0700}: commit (initial): one
 EOF
 test_expect_success 'using --date= shows reflog date (oneline)' '
-	git log -g -1 --oneline --date=raw >actual &&
+	git log -g -1 --oneline --date=default >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+HEAD@{1112911993 -0700}
+EOF
+test_expect_success 'using --date= shows reflog date (format=%gd)' '
+	git log -g -1 --format=%gd --date=raw >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+Reflog: HEAD@{0} (C O Mitter <committer@example.com>)
+Reflog message: commit (initial): one
+EOF
+test_expect_success 'log.date does not invoke "--date" magic (multiline)' '
+	test_config log.date raw &&
+	git log -g -1 >tmp &&
+	grep ^Reflog <tmp >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+e46513e HEAD@{0}: commit (initial): one
+EOF
+test_expect_success 'log.date does not invoke "--date" magic (oneline)' '
+	test_config log.date raw &&
+	git log -g -1 --oneline >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+HEAD@{0}
+EOF
+test_expect_success 'log.date does not invoke "--date" magic (format=%gd)' '
+	test_config log.date raw &&
+	git log -g -1 --format=%gd >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+HEAD@{0}
+EOF
+test_expect_success '--date magic does not override explicit @{0} syntax' '
+	git log -g -1 --format=%gd --date=raw HEAD@{0} >actual &&
+	test_cmp expect actual
+'
+
+: >expect
+test_expect_success 'empty reflog file' '
+	git branch empty &&
+	: >.git/logs/refs/heads/empty &&
+
+	git log -g empty >actual &&
 	test_cmp expect actual
 '
 
diff --git a/t/t1412-reflog-loop.sh b/t/t1412-reflog-loop.sh
new file mode 100755
index 0000000..3acd895
--- /dev/null
+++ b/t/t1412-reflog-loop.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+test_description='reflog walk shows repeated commits again'
+. ./test-lib.sh
+
+test_expect_success 'setup commits' '
+	test_tick &&
+	echo content >file && git add file && git commit -m one &&
+	git tag one &&
+	echo content >>file && git add file && git commit -m two &&
+	git tag two
+'
+
+test_expect_success 'setup reflog with alternating commits' '
+	git checkout -b topic &&
+	git reset one &&
+	git reset two &&
+	git reset one &&
+	git reset two
+'
+
+test_expect_success 'reflog shows all entries' '
+	cat >expect <<-\EOF &&
+		topic@{0} reset: moving to two
+		topic@{1} reset: moving to one
+		topic@{2} reset: moving to two
+		topic@{3} reset: moving to one
+		topic@{4} branch: Created from HEAD
+	EOF
+	git log -g --format="%gd %gs" topic >actual &&
+	test_cmp expect actual
+'
+
+test_done
diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh
index a22632f..5b79c51 100755
--- a/t/t1450-fsck.sh
+++ b/t/t1450-fsck.sh
@@ -1,19 +1,23 @@
 #!/bin/sh
 
-test_description='git fsck random collection of tests'
+test_description='git fsck random collection of tests
+
+* (HEAD) B
+* (master) A
+'
 
 . ./test-lib.sh
 
 test_expect_success setup '
+	git config gc.auto 0 &&
+	git config i18n.commitencoding ISO-8859-1 &&
 	test_commit A fileA one &&
+	git config --unset i18n.commitencoding &&
 	git checkout HEAD^0 &&
 	test_commit B fileB two &&
 	git tag -d A B &&
-	git reflog expire --expire=now --all
-'
-
-test_expect_success 'HEAD is part of refs' '
-	test 0 = $(git fsck | wc -l)
+	git reflog expire --expire=now --all &&
+	>empty
 '
 
 test_expect_success 'loose objects borrowed from alternate are not missing' '
@@ -23,76 +27,190 @@
 		git init &&
 		echo ../../../.git/objects >.git/objects/info/alternates &&
 		test_commit C fileC one &&
-		git fsck >out &&
-		! grep "missing blob" out
-	)
+		git fsck --no-dangling >../actual 2>&1
+	) &&
+	test_cmp empty actual
+'
+
+test_expect_success 'HEAD is part of refs, valid objects appear valid' '
+	git fsck >actual 2>&1 &&
+	test_cmp empty actual
 '
 
 # Corruption tests follow.  Make sure to remove all traces of the
 # specific corruption you test afterwards, lest a later test trip over
 # it.
 
+test_expect_success 'setup: helpers for corruption tests' '
+	sha1_file() {
+		echo "$*" | sed "s#..#.git/objects/&/#"
+	} &&
+
+	remove_object() {
+		file=$(sha1_file "$*") &&
+		test -e "$file" &&
+		rm -f "$file"
+	}
+'
+
 test_expect_success 'object with bad sha1' '
 	sha=$(echo blob | git hash-object -w --stdin) &&
-	echo $sha &&
 	old=$(echo $sha | sed "s+^..+&/+") &&
 	new=$(dirname $old)/ffffffffffffffffffffffffffffffffffffff &&
-	sha="$(dirname $new)$(basename $new)"
+	sha="$(dirname $new)$(basename $new)" &&
 	mv .git/objects/$old .git/objects/$new &&
+	test_when_finished "remove_object $sha" &&
 	git update-index --add --cacheinfo 100644 $sha foo &&
+	test_when_finished "git read-tree -u --reset HEAD" &&
 	tree=$(git write-tree) &&
+	test_when_finished "remove_object $tree" &&
 	cmt=$(echo bogus | git commit-tree $tree) &&
+	test_when_finished "remove_object $cmt" &&
 	git update-ref refs/heads/bogus $cmt &&
-	(git fsck 2>out; true) &&
-	grep "$sha.*corrupt" out &&
-	rm -f .git/objects/$new &&
-	git update-ref -d refs/heads/bogus &&
-	git read-tree -u --reset HEAD
+	test_when_finished "git update-ref -d refs/heads/bogus" &&
+
+	test_might_fail git fsck 2>out &&
+	cat out &&
+	grep "$sha.*corrupt" out
 '
 
 test_expect_success 'branch pointing to non-commit' '
-	git rev-parse HEAD^{tree} > .git/refs/heads/invalid &&
+	git rev-parse HEAD^{tree} >.git/refs/heads/invalid &&
+	test_when_finished "git update-ref -d refs/heads/invalid" &&
 	git fsck 2>out &&
-	grep "not a commit" out &&
-	git update-ref -d refs/heads/invalid
-'
-
-cat > invalid-tag <<EOF
-object ffffffffffffffffffffffffffffffffffffffff
-type commit
-tag invalid
-tagger T A Gger <tagger@example.com> 1234567890 -0000
-
-This is an invalid tag.
-EOF
-
-test_expect_failure 'tag pointing to nonexistent' '
-	tag=$(git hash-object -w --stdin < invalid-tag) &&
-	echo $tag > .git/refs/tags/invalid &&
-	git fsck --tags 2>out &&
 	cat out &&
-	grep "could not load tagged object" out &&
-	rm .git/refs/tags/invalid
+	grep "not a commit" out
 '
 
-cat > wrong-tag <<EOF
-object $(echo blob | git hash-object -w --stdin)
-type commit
-tag wrong
-tagger T A Gger <tagger@example.com> 1234567890 -0000
-
-This is an invalid tag.
-EOF
-
-test_expect_failure 'tag pointing to something else than its type' '
-	tag=$(git hash-object -w --stdin < wrong-tag) &&
-	echo $tag > .git/refs/tags/wrong &&
-	git fsck --tags 2>out &&
+test_expect_success 'email without @ is okay' '
+	git cat-file commit HEAD >basis &&
+	sed "s/@/AT/" basis >okay &&
+	new=$(git hash-object -t commit -w --stdin <okay) &&
+	test_when_finished "remove_object $new" &&
+	git update-ref refs/heads/bogus "$new" &&
+	test_when_finished "git update-ref -d refs/heads/bogus" &&
+	git fsck 2>out &&
 	cat out &&
-	grep "some sane error message" out &&
-	rm .git/refs/tags/wrong
+	! grep "commit $new" out
 '
 
+test_expect_success 'email with embedded > is not okay' '
+	git cat-file commit HEAD >basis &&
+	sed "s/@[a-z]/&>/" basis >bad-email &&
+	new=$(git hash-object -t commit -w --stdin <bad-email) &&
+	test_when_finished "remove_object $new" &&
+	git update-ref refs/heads/bogus "$new" &&
+	test_when_finished "git update-ref -d refs/heads/bogus" &&
+	git fsck 2>out &&
+	cat out &&
+	grep "error in commit $new" out
+'
 
+test_expect_success 'missing < email delimiter is reported nicely' '
+	git cat-file commit HEAD >basis &&
+	sed "s/<//" basis >bad-email-2 &&
+	new=$(git hash-object -t commit -w --stdin <bad-email-2) &&
+	test_when_finished "remove_object $new" &&
+	git update-ref refs/heads/bogus "$new" &&
+	test_when_finished "git update-ref -d refs/heads/bogus" &&
+	git fsck 2>out &&
+	cat out &&
+	grep "error in commit $new.* - bad name" out
+'
+
+test_expect_success 'missing email is reported nicely' '
+	git cat-file commit HEAD >basis &&
+	sed "s/[a-z]* <[^>]*>//" basis >bad-email-3 &&
+	new=$(git hash-object -t commit -w --stdin <bad-email-3) &&
+	test_when_finished "remove_object $new" &&
+	git update-ref refs/heads/bogus "$new" &&
+	test_when_finished "git update-ref -d refs/heads/bogus" &&
+	git fsck 2>out &&
+	cat out &&
+	grep "error in commit $new.* - missing email" out
+'
+
+test_expect_success '> in name is reported' '
+	git cat-file commit HEAD >basis &&
+	sed "s/ </> </" basis >bad-email-4 &&
+	new=$(git hash-object -t commit -w --stdin <bad-email-4) &&
+	test_when_finished "remove_object $new" &&
+	git update-ref refs/heads/bogus "$new" &&
+	test_when_finished "git update-ref -d refs/heads/bogus" &&
+	git fsck 2>out &&
+	cat out &&
+	grep "error in commit $new" out
+'
+
+test_expect_success 'tag pointing to nonexistent' '
+	cat >invalid-tag <<-\EOF &&
+	object ffffffffffffffffffffffffffffffffffffffff
+	type commit
+	tag invalid
+	tagger T A Gger <tagger@example.com> 1234567890 -0000
+
+	This is an invalid tag.
+	EOF
+
+	tag=$(git hash-object -t tag -w --stdin <invalid-tag) &&
+	test_when_finished "remove_object $tag" &&
+	echo $tag >.git/refs/tags/invalid &&
+	test_when_finished "git update-ref -d refs/tags/invalid" &&
+	test_must_fail git fsck --tags >out &&
+	cat out &&
+	grep "broken link" out
+'
+
+test_expect_success 'tag pointing to something else than its type' '
+	sha=$(echo blob | git hash-object -w --stdin) &&
+	test_when_finished "remove_object $sha" &&
+	cat >wrong-tag <<-EOF &&
+	object $sha
+	type commit
+	tag wrong
+	tagger T A Gger <tagger@example.com> 1234567890 -0000
+
+	This is an invalid tag.
+	EOF
+
+	tag=$(git hash-object -t tag -w --stdin <wrong-tag) &&
+	test_when_finished "remove_object $tag" &&
+	echo $tag >.git/refs/tags/wrong &&
+	test_when_finished "git update-ref -d refs/tags/wrong" &&
+	test_must_fail git fsck --tags 2>out &&
+	cat out &&
+	grep "error in tag.*broken links" out
+'
+
+test_expect_success 'cleaned up' '
+	git fsck >actual 2>&1 &&
+	test_cmp empty actual
+'
+
+test_expect_success 'rev-list --verify-objects' '
+	git rev-list --verify-objects --all >/dev/null 2>out &&
+	test_cmp empty out
+'
+
+test_expect_success 'rev-list --verify-objects with bad sha1' '
+	sha=$(echo blob | git hash-object -w --stdin) &&
+	old=$(echo $sha | sed "s+^..+&/+") &&
+	new=$(dirname $old)/ffffffffffffffffffffffffffffffffffffff &&
+	sha="$(dirname $new)$(basename $new)" &&
+	mv .git/objects/$old .git/objects/$new &&
+	test_when_finished "remove_object $sha" &&
+	git update-index --add --cacheinfo 100644 $sha foo &&
+	test_when_finished "git read-tree -u --reset HEAD" &&
+	tree=$(git write-tree) &&
+	test_when_finished "remove_object $tree" &&
+	cmt=$(echo bogus | git commit-tree $tree) &&
+	test_when_finished "remove_object $cmt" &&
+	git update-ref refs/heads/bogus $cmt &&
+	test_when_finished "git update-ref -d refs/heads/bogus" &&
+
+	test_might_fail git rev-list --verify-objects refs/heads/bogus >/dev/null 2>out &&
+	cat out &&
+	grep -q "error: sha1 mismatch 63ffffffffffffffffffffffffffffffffffffff" out
+'
 
 test_done
diff --git a/t/t1501-worktree.sh b/t/t1501-worktree.sh
index f6a6f83..8f36aa9 100755
--- a/t/t1501-worktree.sh
+++ b/t/t1501-worktree.sh
@@ -3,175 +3,347 @@
 test_description='test separate work tree'
 . ./test-lib.sh
 
-test_rev_parse() {
-	name=$1
-	shift
+test_expect_success 'setup' '
+	EMPTY_TREE=$(git write-tree) &&
+	EMPTY_BLOB=$(git hash-object -t blob --stdin </dev/null) &&
+	CHANGED_BLOB=$(echo changed | git hash-object -t blob --stdin) &&
+	EMPTY_BLOB7=$(echo $EMPTY_BLOB | sed "s/\(.......\).*/\1/") &&
+	CHANGED_BLOB7=$(echo $CHANGED_BLOB | sed "s/\(.......\).*/\1/") &&
 
-	test_expect_success "$name: is-bare-repository" \
-	"test '$1' = \"\$(git rev-parse --is-bare-repository)\""
-	shift
-	[ $# -eq 0 ] && return
-
-	test_expect_success "$name: is-inside-git-dir" \
-	"test '$1' = \"\$(git rev-parse --is-inside-git-dir)\""
-	shift
-	[ $# -eq 0 ] && return
-
-	test_expect_success "$name: is-inside-work-tree" \
-	"test '$1' = \"\$(git rev-parse --is-inside-work-tree)\""
-	shift
-	[ $# -eq 0 ] && return
-
-	test_expect_success "$name: prefix" \
-	"test '$1' = \"\$(git rev-parse --show-prefix)\""
-	shift
-	[ $# -eq 0 ] && return
-}
-
-EMPTY_TREE=$(git write-tree)
-mkdir -p work/sub/dir || exit 1
-mv .git repo.git || exit 1
-
-say "core.worktree = relative path"
-GIT_DIR=repo.git
-GIT_CONFIG="$(pwd)"/$GIT_DIR/config
-export GIT_DIR GIT_CONFIG
-unset GIT_WORK_TREE
-git config core.worktree ../work
-test_rev_parse 'outside'      false false false
-cd work || exit 1
-GIT_DIR=../repo.git
-GIT_CONFIG="$(pwd)"/$GIT_DIR/config
-test_rev_parse 'inside'       false false true ''
-cd sub/dir || exit 1
-GIT_DIR=../../../repo.git
-GIT_CONFIG="$(pwd)"/$GIT_DIR/config
-test_rev_parse 'subdirectory' false false true sub/dir/
-cd ../../.. || exit 1
-
-say "core.worktree = absolute path"
-GIT_DIR=$(pwd)/repo.git
-GIT_CONFIG=$GIT_DIR/config
-git config core.worktree "$(pwd)/work"
-test_rev_parse 'outside'      false false false
-cd work || exit 1
-test_rev_parse 'inside'       false false true ''
-cd sub/dir || exit 1
-test_rev_parse 'subdirectory' false false true sub/dir/
-cd ../../.. || exit 1
-
-say "GIT_WORK_TREE=relative path (override core.worktree)"
-GIT_DIR=$(pwd)/repo.git
-GIT_CONFIG=$GIT_DIR/config
-git config core.worktree non-existent
-GIT_WORK_TREE=work
-export GIT_WORK_TREE
-test_rev_parse 'outside'      false false false
-cd work || exit 1
-GIT_WORK_TREE=.
-test_rev_parse 'inside'       false false true ''
-cd sub/dir || exit 1
-GIT_WORK_TREE=../..
-test_rev_parse 'subdirectory' false false true sub/dir/
-cd ../../.. || exit 1
-
-mv work repo.git/work
-
-say "GIT_WORK_TREE=absolute path, work tree below git dir"
-GIT_DIR=$(pwd)/repo.git
-GIT_CONFIG=$GIT_DIR/config
-GIT_WORK_TREE=$(pwd)/repo.git/work
-test_rev_parse 'outside'              false false false
-cd repo.git || exit 1
-test_rev_parse 'in repo.git'              false true  false
-cd objects || exit 1
-test_rev_parse 'in repo.git/objects'      false true  false
-cd ../work || exit 1
-test_rev_parse 'in repo.git/work'         false true true ''
-cd sub/dir || exit 1
-test_rev_parse 'in repo.git/sub/dir' false true true sub/dir/
-cd ../../../.. || exit 1
-
-test_expect_success 'repo finds its work tree' '
-	(cd repo.git &&
-	 : > work/sub/dir/untracked &&
-	 test sub/dir/untracked = "$(git ls-files --others)")
+	mkdir -p work/sub/dir &&
+	mkdir -p work2 &&
+	mv .git repo.git
 '
 
-test_expect_success 'repo finds its work tree from work tree, too' '
-	(cd repo.git/work/sub/dir &&
-	 : > tracked &&
-	 git --git-dir=../../.. add tracked &&
-	 cd ../../.. &&
-	 test sub/dir/tracked = "$(git ls-files)")
+test_expect_success 'setup: helper for testing rev-parse' '
+	test_rev_parse() {
+		echo $1 >expected.bare &&
+		echo $2 >expected.inside-git &&
+		echo $3 >expected.inside-worktree &&
+		if test $# -ge 4
+		then
+			echo $4 >expected.prefix
+		fi &&
+
+		git rev-parse --is-bare-repository >actual.bare &&
+		git rev-parse --is-inside-git-dir >actual.inside-git &&
+		git rev-parse --is-inside-work-tree >actual.inside-worktree &&
+		if test $# -ge 4
+		then
+			git rev-parse --show-prefix >actual.prefix
+		fi &&
+
+		test_cmp expected.bare actual.bare &&
+		test_cmp expected.inside-git actual.inside-git &&
+		test_cmp expected.inside-worktree actual.inside-worktree &&
+		if test $# -ge 4
+		then
+			# rev-parse --show-prefix should output
+			# a single newline when at the top of the work tree,
+			# but we test for that separately.
+			test -z "$4" && ! test -s actual.prefix ||
+			test_cmp expected.prefix actual.prefix
+		fi
+	}
+'
+
+test_expect_success 'setup: core.worktree = relative path' '
+	sane_unset GIT_WORK_TREE &&
+	GIT_DIR=repo.git &&
+	GIT_CONFIG="$(pwd)"/$GIT_DIR/config &&
+	export GIT_DIR GIT_CONFIG &&
+	git config core.worktree ../work
+'
+
+test_expect_success 'outside' '
+	test_rev_parse false false false
+'
+
+test_expect_success 'inside work tree' '
+	(
+		cd work &&
+		GIT_DIR=../repo.git &&
+		GIT_CONFIG="$(pwd)"/$GIT_DIR/config &&
+		test_rev_parse false false true ""
+	)
+'
+
+test_expect_success 'empty prefix is actually written out' '
+	echo >expected &&
+	(
+		cd work &&
+		GIT_DIR=../repo.git &&
+		GIT_CONFIG="$(pwd)"/$GIT_DIR/config &&
+		git rev-parse --show-prefix >../actual
+	) &&
+	test_cmp expected actual
+'
+
+test_expect_success 'subdir of work tree' '
+	(
+		cd work/sub/dir &&
+		GIT_DIR=../../../repo.git &&
+		GIT_CONFIG="$(pwd)"/$GIT_DIR/config &&
+		test_rev_parse false false true sub/dir/
+	)
+'
+
+test_expect_success 'setup: core.worktree = absolute path' '
+	sane_unset GIT_WORK_TREE &&
+	GIT_DIR=$(pwd)/repo.git &&
+	GIT_CONFIG=$GIT_DIR/config &&
+	export GIT_DIR GIT_CONFIG &&
+	git config core.worktree "$(pwd)/work"
+'
+
+test_expect_success 'outside' '
+	test_rev_parse false false false &&
+	(
+		cd work2 &&
+		test_rev_parse false false false
+	)
+'
+
+test_expect_success 'inside work tree' '
+	(
+		cd work &&
+		test_rev_parse false false true ""
+	)
+'
+
+test_expect_success 'subdir of work tree' '
+	(
+		cd work/sub/dir &&
+		test_rev_parse false false true sub/dir/
+	)
+'
+
+test_expect_success 'setup: GIT_WORK_TREE=relative (override core.worktree)' '
+	GIT_DIR=$(pwd)/repo.git &&
+	GIT_CONFIG=$GIT_DIR/config &&
+	git config core.worktree non-existent &&
+	GIT_WORK_TREE=work &&
+	export GIT_DIR GIT_CONFIG GIT_WORK_TREE
+'
+
+test_expect_success 'outside' '
+	test_rev_parse false false false &&
+	(
+		cd work2 &&
+		test_rev_parse false false false
+	)
+'
+
+test_expect_success 'inside work tree' '
+	(
+		cd work &&
+		GIT_WORK_TREE=. &&
+		test_rev_parse false false true ""
+	)
+'
+
+test_expect_success 'subdir of work tree' '
+	(
+		cd work/sub/dir &&
+		GIT_WORK_TREE=../.. &&
+		test_rev_parse false false true sub/dir/
+	)
+'
+
+test_expect_success 'setup: GIT_WORK_TREE=absolute, below git dir' '
+	mv work repo.git/work &&
+	mv work2 repo.git/work2 &&
+	GIT_DIR=$(pwd)/repo.git &&
+	GIT_CONFIG=$GIT_DIR/config &&
+	GIT_WORK_TREE=$(pwd)/repo.git/work &&
+	export GIT_DIR GIT_CONFIG GIT_WORK_TREE
+'
+
+test_expect_success 'outside' '
+	echo outside &&
+	test_rev_parse false false false
+'
+
+test_expect_success 'in repo.git' '
+	(
+		cd repo.git &&
+		test_rev_parse false true false
+	) &&
+	(
+		cd repo.git/objects &&
+		test_rev_parse false true false
+	) &&
+	(
+		cd repo.git/work2 &&
+		test_rev_parse false true false
+	)
+'
+
+test_expect_success 'inside work tree' '
+	(
+		cd repo.git/work &&
+		test_rev_parse false true true ""
+	)
+'
+
+test_expect_success 'subdir of work tree' '
+	(
+		cd repo.git/work/sub/dir &&
+		test_rev_parse false true true sub/dir/
+	)
+'
+
+test_expect_success 'find work tree from repo' '
+	echo sub/dir/untracked >expected &&
+	cat <<-\EOF >repo.git/work/.gitignore &&
+	expected.*
+	actual.*
+	.gitignore
+	EOF
+	>repo.git/work/sub/dir/untracked &&
+	(
+		cd repo.git &&
+		git ls-files --others --exclude-standard >../actual
+	) &&
+	test_cmp expected actual
+'
+
+test_expect_success 'find work tree from work tree' '
+	echo sub/dir/tracked >expected &&
+	>repo.git/work/sub/dir/tracked &&
+	(
+		cd repo.git/work/sub/dir &&
+		git --git-dir=../../.. add tracked
+	) &&
+	(
+		cd repo.git &&
+		git ls-files >../actual
+	) &&
+	test_cmp expected actual
 '
 
 test_expect_success '_gently() groks relative GIT_DIR & GIT_WORK_TREE' '
-	(cd repo.git/work/sub/dir &&
-	GIT_DIR=../../.. GIT_WORK_TREE=../.. GIT_PAGER= \
+	(
+		cd repo.git/work/sub/dir &&
+		GIT_DIR=../../.. &&
+		GIT_WORK_TREE=../.. &&
+		GIT_PAGER= &&
+		export GIT_DIR GIT_WORK_TREE GIT_PAGER &&
+
 		git diff --exit-code tracked &&
-	echo changed > tracked &&
-	! GIT_DIR=../../.. GIT_WORK_TREE=../.. GIT_PAGER= \
-		git diff --exit-code tracked)
-'
-cat > diff-index-cached.expected <<\EOF
-:000000 100644 0000000000000000000000000000000000000000 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 A	sub/dir/tracked
-EOF
-cat > diff-index.expected <<\EOF
-:000000 100644 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 A	sub/dir/tracked
-EOF
-
-
-test_expect_success 'git diff-index' '
-	GIT_DIR=repo.git GIT_WORK_TREE=repo.git/work git diff-index $EMPTY_TREE > result &&
-	test_cmp diff-index.expected result &&
-	GIT_DIR=repo.git git diff-index --cached $EMPTY_TREE > result &&
-	test_cmp diff-index-cached.expected result
-'
-cat >diff-files.expected <<\EOF
-:100644 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0000000000000000000000000000000000000000 M	sub/dir/tracked
-EOF
-
-test_expect_success 'git diff-files' '
-	GIT_DIR=repo.git GIT_WORK_TREE=repo.git/work git diff-files > result &&
-	test_cmp diff-files.expected result
+		echo changed >tracked &&
+		test_must_fail git diff --exit-code tracked
+	)
 '
 
-cat >diff-TREE.expected <<\EOF
-diff --git a/sub/dir/tracked b/sub/dir/tracked
-new file mode 100644
-index 0000000..5ea2ed4
---- /dev/null
-+++ b/sub/dir/tracked
-@@ -0,0 +1 @@
-+changed
-EOF
-cat >diff-TREE-cached.expected <<\EOF
-diff --git a/sub/dir/tracked b/sub/dir/tracked
-new file mode 100644
-index 0000000..e69de29
-EOF
-cat >diff-FILES.expected <<\EOF
-diff --git a/sub/dir/tracked b/sub/dir/tracked
-index e69de29..5ea2ed4 100644
---- a/sub/dir/tracked
-+++ b/sub/dir/tracked
-@@ -0,0 +1 @@
-+changed
-EOF
+test_expect_success 'diff-index respects work tree under .git dir' '
+	cat >diff-index-cached.expected <<-EOF &&
+	:000000 100644 $_z40 $EMPTY_BLOB A	sub/dir/tracked
+	EOF
+	cat >diff-index.expected <<-EOF &&
+	:000000 100644 $_z40 $_z40 A	sub/dir/tracked
+	EOF
 
-test_expect_success 'git diff' '
-	GIT_DIR=repo.git GIT_WORK_TREE=repo.git/work git diff $EMPTY_TREE > result &&
-	test_cmp diff-TREE.expected result &&
-	GIT_DIR=repo.git git diff --cached $EMPTY_TREE > result &&
-	test_cmp diff-TREE-cached.expected result &&
-	GIT_DIR=repo.git GIT_WORK_TREE=repo.git/work git diff > result &&
-	test_cmp diff-FILES.expected result
+	(
+		GIT_DIR=repo.git &&
+		GIT_WORK_TREE=repo.git/work &&
+		export GIT_DIR GIT_WORK_TREE &&
+		git diff-index $EMPTY_TREE >diff-index.actual &&
+		git diff-index --cached $EMPTY_TREE >diff-index-cached.actual
+	) &&
+	test_cmp diff-index.expected diff-index.actual &&
+	test_cmp diff-index-cached.expected diff-index-cached.actual
+'
+
+test_expect_success 'diff-files respects work tree under .git dir' '
+	cat >diff-files.expected <<-EOF &&
+	:100644 100644 $EMPTY_BLOB $_z40 M	sub/dir/tracked
+	EOF
+
+	(
+		GIT_DIR=repo.git &&
+		GIT_WORK_TREE=repo.git/work &&
+		export GIT_DIR GIT_WORK_TREE &&
+		git diff-files >diff-files.actual
+	) &&
+	test_cmp diff-files.expected diff-files.actual
+'
+
+test_expect_success 'git diff respects work tree under .git dir' '
+	cat >diff-TREE.expected <<-EOF &&
+	diff --git a/sub/dir/tracked b/sub/dir/tracked
+	new file mode 100644
+	index 0000000..$CHANGED_BLOB7
+	--- /dev/null
+	+++ b/sub/dir/tracked
+	@@ -0,0 +1 @@
+	+changed
+	EOF
+	cat >diff-TREE-cached.expected <<-EOF &&
+	diff --git a/sub/dir/tracked b/sub/dir/tracked
+	new file mode 100644
+	index 0000000..$EMPTY_BLOB7
+	EOF
+	cat >diff-FILES.expected <<-EOF &&
+	diff --git a/sub/dir/tracked b/sub/dir/tracked
+	index $EMPTY_BLOB7..$CHANGED_BLOB7 100644
+	--- a/sub/dir/tracked
+	+++ b/sub/dir/tracked
+	@@ -0,0 +1 @@
+	+changed
+	EOF
+
+	(
+		GIT_DIR=repo.git &&
+		GIT_WORK_TREE=repo.git/work &&
+		export GIT_DIR GIT_WORK_TREE &&
+		git diff $EMPTY_TREE >diff-TREE.actual &&
+		git diff --cached $EMPTY_TREE >diff-TREE-cached.actual &&
+		git diff >diff-FILES.actual
+	) &&
+	test_cmp diff-TREE.expected diff-TREE.actual &&
+	test_cmp diff-TREE-cached.expected diff-TREE-cached.actual &&
+	test_cmp diff-FILES.expected diff-FILES.actual
 '
 
 test_expect_success 'git grep' '
-	(cd repo.git/work/sub &&
-	GIT_DIR=../.. GIT_WORK_TREE=.. git grep -l changed | grep dir/tracked)
+	echo dir/tracked >expected.grep &&
+	(
+		cd repo.git/work/sub &&
+		GIT_DIR=../.. &&
+		GIT_WORK_TREE=.. &&
+		export GIT_DIR GIT_WORK_TREE &&
+		git grep -l changed >../../../actual.grep
+	) &&
+	test_cmp expected.grep actual.grep
+'
+
+test_expect_success 'git commit' '
+	(
+		cd repo.git &&
+		GIT_DIR=. GIT_WORK_TREE=work git commit -a -m done
+	)
+'
+
+test_expect_success 'absolute pathspec should fail gracefully' '
+	(
+		cd repo.git &&
+		test_might_fail git config --unset core.worktree &&
+		test_must_fail git log HEAD -- /home
+	)
+'
+
+test_expect_success 'make_relative_path handles double slashes in GIT_DIR' '
+	>dummy_file &&
+	echo git --git-dir="$(pwd)//repo.git" --work-tree="$(pwd)" add dummy_file &&
+	git --git-dir="$(pwd)//repo.git" --work-tree="$(pwd)" add dummy_file
+'
+
+test_expect_success 'relative $GIT_WORK_TREE and git subprocesses' '
+	GIT_DIR=repo.git GIT_WORK_TREE=repo.git/work \
+	test-subprocess --setup-work-tree rev-parse --show-toplevel >actual &&
+	echo "$(pwd)/repo.git/work" >expected &&
+	test_cmp expected actual
 '
 
 test_done
diff --git a/t/t1502-rev-parse-parseopt.sh b/t/t1502-rev-parse-parseopt.sh
index e504058..1efd7f7 100755
--- a/t/t1502-rev-parse-parseopt.sh
+++ b/t/t1502-rev-parse-parseopt.sh
@@ -3,7 +3,8 @@
 test_description='test git rev-parse --parseopt'
 . ./test-lib.sh
 
-cat > expect.err <<EOF
+cat > expect <<\END_EXPECT
+cat <<\EOF
 usage: some-command [options] <args>...
 
     some-command does foo and bar!
@@ -19,6 +20,7 @@
     --extra1              line above used to cause a segfault but no longer does
 
 EOF
+END_EXPECT
 
 cat > optionspec << EOF
 some-command [options] <args>...
@@ -38,8 +40,8 @@
 EOF
 
 test_expect_success 'test --parseopt help output' '
-	git rev-parse --parseopt -- -h 2> output.err < optionspec
-	test_cmp expect.err output.err
+	test_expect_code 129 git rev-parse --parseopt -- -h > output < optionspec &&
+	test_cmp expect output
 '
 
 cat > expect <<EOF
@@ -79,4 +81,22 @@
 	test_cmp expect output
 '
 
+cat >expect <<EOF
+set -- --foo -- '--' 'arg' '--spam=ham'
+EOF
+
+test_expect_success 'test --parseopt --keep-dashdash --stop-at-non-option with --' '
+	git rev-parse --parseopt --keep-dashdash --stop-at-non-option -- --foo -- arg --spam=ham <optionspec >output &&
+	test_cmp expect output
+'
+
+cat > expect <<EOF
+set -- --foo -- 'arg' '--spam=ham'
+EOF
+
+test_expect_success 'test --parseopt --keep-dashdash --stop-at-non-option without --' '
+	git rev-parse --parseopt --keep-dashdash --stop-at-non-option -- --foo arg --spam=ham <optionspec >output &&
+	test_cmp expect output
+'
+
 test_done
diff --git a/t/t1503-rev-parse-verify.sh b/t/t1503-rev-parse-verify.sh
index cc65394..813cc1b 100755
--- a/t/t1503-rev-parse-verify.sh
+++ b/t/t1503-rev-parse-verify.sh
@@ -104,4 +104,15 @@
 	test_must_fail git rev-parse --verify --default bar
 '
 
+test_expect_success 'master@{n} for various n' '
+	N=$(git reflog | wc -l) &&
+	Nm1=$(($N-1)) &&
+	Np1=$(($N+1)) &&
+	git rev-parse --verify master@{0} &&
+	git rev-parse --verify master@{1} &&
+	git rev-parse --verify master@{$Nm1} &&
+	test_must_fail git rev-parse --verify master@{$N} &&
+	test_must_fail git rev-parse --verify master@{$Np1}
+'
+
 test_done
diff --git a/t/t1504-ceiling-dirs.sh b/t/t1504-ceiling-dirs.sh
index df5ad8c..cce87a5 100755
--- a/t/t1504-ceiling-dirs.sh
+++ b/t/t1504-ceiling-dirs.sh
@@ -9,8 +9,9 @@
 }
 
 test_fail() {
-	test_expect_code 128 "$1: prefix" \
-	"git rev-parse --show-prefix"
+	test_expect_success "$1: prefix" '
+		test_expect_code 128 git rev-parse --show-prefix
+	'
 }
 
 TRASH_ROOT="$PWD"
diff --git a/t/t1506-rev-parse-diagnosis.sh b/t/t1506-rev-parse-diagnosis.sh
new file mode 100755
index 0000000..c5cb77a
--- /dev/null
+++ b/t/t1506-rev-parse-diagnosis.sh
@@ -0,0 +1,185 @@
+#!/bin/sh
+
+test_description='test git rev-parse diagnosis for invalid argument'
+
+exec </dev/null
+
+. ./test-lib.sh
+
+test_did_you_mean ()
+{
+	sq="'" &&
+	cat >expected <<-EOF &&
+	fatal: Path '$2$3' $4, but not ${5:-$sq$3$sq}.
+	Did you mean '$1:$2$3'${2:+ aka $sq$1:./$3$sq}?
+	EOF
+	test_cmp expected error
+}
+
+HASH_file=
+
+test_expect_success 'set up basic repo' '
+	echo one > file.txt &&
+	mkdir subdir &&
+	echo two > subdir/file.txt &&
+	echo three > subdir/file2.txt &&
+	git add . &&
+	git commit -m init &&
+	echo four > index-only.txt &&
+	git add index-only.txt &&
+	echo five > disk-only.txt
+'
+
+test_expect_success 'correct file objects' '
+	HASH_file=$(git rev-parse HEAD:file.txt) &&
+	git rev-parse HEAD:subdir/file.txt &&
+	git rev-parse :index-only.txt &&
+	(cd subdir &&
+	 git rev-parse HEAD:subdir/file2.txt &&
+	 test $HASH_file = $(git rev-parse HEAD:file.txt) &&
+	 test $HASH_file = $(git rev-parse :file.txt) &&
+	 test $HASH_file = $(git rev-parse :0:file.txt) )
+'
+
+test_expect_success 'correct relative file objects (0)' '
+	git rev-parse :file.txt >expected &&
+	git rev-parse :./file.txt >result &&
+	test_cmp expected result &&
+	git rev-parse :0:./file.txt >result &&
+	test_cmp expected result
+'
+
+test_expect_success 'correct relative file objects (1)' '
+	git rev-parse HEAD:file.txt >expected &&
+	git rev-parse HEAD:./file.txt >result &&
+	test_cmp expected result
+'
+
+test_expect_success 'correct relative file objects (2)' '
+	(
+		cd subdir &&
+		git rev-parse HEAD:../file.txt >result &&
+		test_cmp ../expected result
+	)
+'
+
+test_expect_success 'correct relative file objects (3)' '
+	(
+		cd subdir &&
+		git rev-parse HEAD:../subdir/../file.txt >result &&
+		test_cmp ../expected result
+	)
+'
+
+test_expect_success 'correct relative file objects (4)' '
+	git rev-parse HEAD:subdir/file.txt >expected &&
+	(
+		cd subdir &&
+		git rev-parse HEAD:./file.txt >result &&
+		test_cmp ../expected result
+	)
+'
+
+test_expect_success 'correct relative file objects (5)' '
+	git rev-parse :subdir/file.txt >expected &&
+	(
+		cd subdir &&
+		git rev-parse :./file.txt >result &&
+		test_cmp ../expected result &&
+		git rev-parse :0:./file.txt >result &&
+		test_cmp ../expected result
+	)
+'
+
+test_expect_success 'correct relative file objects (6)' '
+	git rev-parse :file.txt >expected &&
+	(
+		cd subdir &&
+		git rev-parse :../file.txt >result &&
+		test_cmp ../expected result &&
+		git rev-parse :0:../file.txt >result &&
+		test_cmp ../expected result
+	)
+'
+
+test_expect_success 'incorrect revision id' '
+	test_must_fail git rev-parse foobar:file.txt 2>error &&
+	grep "Invalid object name '"'"'foobar'"'"'." error &&
+	test_must_fail git rev-parse foobar 2> error &&
+	grep "unknown revision or path not in the working tree." error
+'
+
+test_expect_success 'incorrect file in sha1:path' '
+	test_must_fail git rev-parse HEAD:nothing.txt 2> error &&
+	grep "fatal: Path '"'"'nothing.txt'"'"' does not exist in '"'"'HEAD'"'"'" error &&
+	test_must_fail git rev-parse HEAD:index-only.txt 2> error &&
+	grep "fatal: Path '"'"'index-only.txt'"'"' exists on disk, but not in '"'"'HEAD'"'"'." error &&
+	(cd subdir &&
+	 test_must_fail git rev-parse HEAD:file2.txt 2> error &&
+	 test_did_you_mean HEAD subdir/ file2.txt exists )
+'
+
+test_expect_success 'incorrect file in :path and :N:path' '
+	test_must_fail git rev-parse :nothing.txt 2> error &&
+	grep "fatal: Path '"'"'nothing.txt'"'"' does not exist (neither on disk nor in the index)." error &&
+	test_must_fail git rev-parse :1:nothing.txt 2> error &&
+	grep "Path '"'"'nothing.txt'"'"' does not exist (neither on disk nor in the index)." error &&
+	test_must_fail git rev-parse :1:file.txt 2> error &&
+	test_did_you_mean ":0" "" file.txt "is in the index" "at stage 1" &&
+	(cd subdir &&
+	 test_must_fail git rev-parse :1:file.txt 2> error &&
+	 test_did_you_mean ":0" "" file.txt "is in the index" "at stage 1" &&
+	 test_must_fail git rev-parse :file2.txt 2> error &&
+	 test_did_you_mean ":0" subdir/ file2.txt "is in the index" &&
+	 test_must_fail git rev-parse :2:file2.txt 2> error &&
+	 test_did_you_mean :0 subdir/ file2.txt "is in the index") &&
+	test_must_fail git rev-parse :disk-only.txt 2> error &&
+	grep "fatal: Path '"'"'disk-only.txt'"'"' exists on disk, but not in the index." error
+'
+
+test_expect_success 'invalid @{n} reference' '
+	test_must_fail git rev-parse master@{99999} >output 2>error &&
+	test -z "$(cat output)" &&
+	grep "fatal: Log for [^ ]* only has [0-9][0-9]* entries." error  &&
+	test_must_fail git rev-parse --verify master@{99999} >output 2>error &&
+	test -z "$(cat output)" &&
+	grep "fatal: Log for [^ ]* only has [0-9][0-9]* entries." error
+'
+
+test_expect_success 'relative path not found' '
+	(
+		cd subdir &&
+		test_must_fail git rev-parse HEAD:./nonexistent.txt 2>error &&
+		grep subdir/nonexistent.txt error
+	)
+'
+
+test_expect_success 'relative path outside worktree' '
+	test_must_fail git rev-parse HEAD:../file.txt >output 2>error &&
+	test -z "$(cat output)" &&
+	grep "outside repository" error
+'
+
+test_expect_success 'relative path when cwd is outside worktree' '
+	test_must_fail git --git-dir=.git --work-tree=subdir rev-parse HEAD:./file.txt >output 2>error &&
+	test -z "$(cat output)" &&
+	grep "relative path syntax can.t be used outside working tree." error
+'
+
+test_expect_success 'relative path when startup_info is NULL' '
+	test_must_fail test-match-trees HEAD:./file.txt HEAD:./file.txt 2>error &&
+	grep "BUG: startup_info struct is not initialized." error
+'
+
+test_expect_success '<commit>:file correctly diagnosed after a pathname' '
+	test_must_fail git rev-parse file.txt HEAD:file.txt 1>actual 2>error &&
+	test_i18ngrep ! "exists on disk" error &&
+	test_i18ngrep "no such path in the working tree" error &&
+	cat >expect <<-\EOF &&
+	file.txt
+	HEAD:file.txt
+	EOF
+	test_cmp expect actual
+'
+
+test_done
diff --git a/t/t1507-rev-parse-upstream.sh b/t/t1507-rev-parse-upstream.sh
new file mode 100755
index 0000000..d6e5761
--- /dev/null
+++ b/t/t1507-rev-parse-upstream.sh
@@ -0,0 +1,214 @@
+#!/bin/sh
+
+test_description='test <branch>@{upstream} syntax'
+
+. ./test-lib.sh
+
+
+test_expect_success 'setup' '
+
+	test_commit 1 &&
+	git checkout -b side &&
+	test_commit 2 &&
+	git checkout master &&
+	git clone . clone &&
+	test_commit 3 &&
+	(cd clone &&
+	 test_commit 4 &&
+	 git branch --track my-side origin/side &&
+	 git branch --track local-master master &&
+	 git remote add -t master master-only .. &&
+	 git fetch master-only &&
+	 git branch bad-upstream &&
+	 git config branch.bad-upstream.remote master-only &&
+	 git config branch.bad-upstream.merge refs/heads/side
+	)
+'
+
+sq="'"
+
+full_name () {
+	(cd clone &&
+	 git rev-parse --symbolic-full-name "$@")
+}
+
+commit_subject () {
+	(cd clone &&
+	 git show -s --pretty=format:%s "$@")
+}
+
+error_message () {
+	(cd clone &&
+	 test_must_fail git rev-parse --verify "$@")
+}
+
+test_expect_success '@{upstream} resolves to correct full name' '
+	test refs/remotes/origin/master = "$(full_name @{upstream})"
+'
+
+test_expect_success '@{u} resolves to correct full name' '
+	test refs/remotes/origin/master = "$(full_name @{u})"
+'
+
+test_expect_success 'my-side@{upstream} resolves to correct full name' '
+	test refs/remotes/origin/side = "$(full_name my-side@{u})"
+'
+
+test_expect_success 'my-side@{u} resolves to correct commit' '
+	git checkout side &&
+	test_commit 5 &&
+	(cd clone && git fetch) &&
+	test 2 = "$(commit_subject my-side)" &&
+	test 5 = "$(commit_subject my-side@{u})"
+'
+
+test_expect_success 'not-tracking@{u} fails' '
+	test_must_fail full_name non-tracking@{u} &&
+	(cd clone && git checkout --no-track -b non-tracking) &&
+	test_must_fail full_name non-tracking@{u}
+'
+
+test_expect_success '<branch>@{u}@{1} resolves correctly' '
+	test_commit 6 &&
+	(cd clone && git fetch) &&
+	test 5 = $(commit_subject my-side@{u}@{1})
+'
+
+test_expect_success '@{u} without specifying branch fails on a detached HEAD' '
+	git checkout HEAD^0 &&
+	test_must_fail git rev-parse @{u}
+'
+
+test_expect_success 'checkout -b new my-side@{u} forks from the same' '
+(
+	cd clone &&
+	git checkout -b new my-side@{u} &&
+	git rev-parse --symbolic-full-name my-side@{u} >expect &&
+	git rev-parse --symbolic-full-name new@{u} >actual &&
+	test_cmp expect actual
+)
+'
+
+test_expect_success 'merge my-side@{u} records the correct name' '
+(
+	cd clone || exit
+	git checkout master || exit
+	git branch -D new ;# can fail but is ok
+	git branch -t new my-side@{u} &&
+	git merge -s ours new@{u} &&
+	git show -s --pretty=format:%s >actual &&
+	echo "Merge remote-tracking branch ${sq}origin/side${sq}" >expect &&
+	test_cmp expect actual
+)
+'
+
+test_expect_success 'branch -d other@{u}' '
+	git checkout -t -b other master &&
+	git branch -d @{u} &&
+	git for-each-ref refs/heads/master >actual &&
+	>expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'checkout other@{u}' '
+	git branch -f master HEAD &&
+	git checkout -t -b another master &&
+	git checkout @{u} &&
+	git symbolic-ref HEAD >actual &&
+	echo refs/heads/master >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'branch@{u} works when tracking a local branch' '
+	test refs/heads/master = "$(full_name local-master@{u})"
+'
+
+test_expect_success 'branch@{u} error message when no upstream' '
+	cat >expect <<-EOF &&
+	error: No upstream configured for branch ${sq}non-tracking${sq}
+	fatal: Needed a single revision
+	EOF
+	error_message non-tracking@{u} 2>actual &&
+	test_i18ncmp expect actual
+'
+
+test_expect_success '@{u} error message when no upstream' '
+	cat >expect <<-EOF &&
+	error: No upstream configured for branch ${sq}master${sq}
+	fatal: Needed a single revision
+	EOF
+	test_must_fail git rev-parse --verify @{u} 2>actual &&
+	test_i18ncmp expect actual
+'
+
+test_expect_success 'branch@{u} error message with misspelt branch' '
+	cat >expect <<-EOF &&
+	error: No such branch: ${sq}no-such-branch${sq}
+	fatal: Needed a single revision
+	EOF
+	error_message no-such-branch@{u} 2>actual &&
+	test_i18ncmp expect actual
+'
+
+test_expect_success '@{u} error message when not on a branch' '
+	cat >expect <<-EOF &&
+	error: HEAD does not point to a branch
+	fatal: Needed a single revision
+	EOF
+	git checkout HEAD^0 &&
+	test_must_fail git rev-parse --verify @{u} 2>actual &&
+	test_i18ncmp expect actual
+'
+
+test_expect_success 'branch@{u} error message if upstream branch not fetched' '
+	cat >expect <<-EOF &&
+	error: Upstream branch ${sq}refs/heads/side${sq} not stored as a remote-tracking branch
+	fatal: Needed a single revision
+	EOF
+	error_message bad-upstream@{u} 2>actual &&
+	test_i18ncmp expect actual
+'
+
+test_expect_success 'pull works when tracking a local branch' '
+(
+	cd clone &&
+	git checkout local-master &&
+	git pull
+)
+'
+
+# makes sense if the previous one succeeded
+test_expect_success '@{u} works when tracking a local branch' '
+	test refs/heads/master = "$(full_name @{u})"
+'
+
+cat >expect <<EOF
+commit 8f489d01d0cc65c3b0f09504ec50b5ed02a70bd5
+Reflog: master@{0} (C O Mitter <committer@example.com>)
+Reflog message: branch: Created from HEAD
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:15:13 2005 -0700
+
+    3
+EOF
+test_expect_success 'log -g other@{u}' '
+	git log -1 -g other@{u} >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<EOF
+commit 8f489d01d0cc65c3b0f09504ec50b5ed02a70bd5
+Reflog: master@{Thu Apr 7 15:17:13 2005 -0700} (C O Mitter <committer@example.com>)
+Reflog message: branch: Created from HEAD
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:15:13 2005 -0700
+
+    3
+EOF
+
+test_expect_success 'log -g other@{u}@{now}' '
+	git log -1 -g other@{u}@{now} >actual &&
+	test_cmp expect actual
+'
+
+test_done
diff --git a/t/t1508-at-combinations.sh b/t/t1508-at-combinations.sh
new file mode 100755
index 0000000..d5d6244
--- /dev/null
+++ b/t/t1508-at-combinations.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+
+test_description='test various @{X} syntax combinations together'
+. ./test-lib.sh
+
+check() {
+test_expect_${3:-success} "$1 = $2" "
+	echo '$2' >expect &&
+	git log -1 --format=%s '$1' >actual &&
+	test_cmp expect actual
+"
+}
+nonsense() {
+test_expect_${2:-success} "$1 is nonsensical" "
+	test_must_fail git log -1 '$1'
+"
+}
+fail() {
+	"$@" failure
+}
+
+test_expect_success 'setup' '
+	test_commit master-one &&
+	test_commit master-two &&
+	git checkout -b upstream-branch &&
+	test_commit upstream-one &&
+	test_commit upstream-two &&
+	git checkout -b old-branch &&
+	test_commit old-one &&
+	test_commit old-two &&
+	git checkout -b new-branch &&
+	test_commit new-one &&
+	test_commit new-two &&
+	git config branch.old-branch.remote . &&
+	git config branch.old-branch.merge refs/heads/master &&
+	git config branch.new-branch.remote . &&
+	git config branch.new-branch.merge refs/heads/upstream-branch
+'
+
+check HEAD new-two
+check "@{1}" new-one
+check "@{-1}" old-two
+check "@{-1}@{1}" old-one
+check "@{u}" upstream-two
+check "@{u}@{1}" upstream-one
+check "@{-1}@{u}" master-two
+check "@{-1}@{u}@{1}" master-one
+nonsense "@{u}@{-1}"
+nonsense "@{1}@{u}"
+
+test_done
diff --git a/t/t1509-root-worktree.sh b/t/t1509-root-worktree.sh
new file mode 100755
index 0000000..335420f
--- /dev/null
+++ b/t/t1509-root-worktree.sh
@@ -0,0 +1,249 @@
+#!/bin/sh
+
+test_description='Test Git when git repository is located at root
+
+This test requires write access in root. Do not bother if you do not
+have a throwaway chroot or VM.
+
+Script t1509/prepare-chroot.sh may help you setup chroot, then you
+can chroot in and execute this test from there.
+'
+
+. ./test-lib.sh
+
+test_cmp_val() {
+	echo "$1" > expected
+	echo "$2" > result
+	test_cmp expected result
+}
+
+test_vars() {
+	test_expect_success "$1: gitdir" '
+		test_cmp_val "'"$2"'" "$(git rev-parse --git-dir)"
+	'
+
+	test_expect_success "$1: worktree" '
+		test_cmp_val "'"$3"'" "$(git rev-parse --show-toplevel)"
+	'
+
+	test_expect_success "$1: prefix" '
+		test_cmp_val "'"$4"'" "$(git rev-parse --show-prefix)"
+	'
+}
+
+test_foobar_root() {
+	test_expect_success 'add relative' '
+		test -z "$(cd / && git ls-files)" &&
+		git add foo/foome &&
+		git add foo/bar/barme &&
+		git add me &&
+		( cd / && git ls-files --stage ) > result &&
+		test_cmp /ls.expected result &&
+		rm "$(git rev-parse --git-dir)/index"
+	'
+
+	test_expect_success 'add absolute' '
+		test -z "$(cd / && git ls-files)" &&
+		git add /foo/foome &&
+		git add /foo/bar/barme &&
+		git add /me &&
+		( cd / && git ls-files --stage ) > result &&
+		test_cmp /ls.expected result &&
+		rm "$(git rev-parse --git-dir)/index"
+	'
+
+}
+
+test_foobar_foo() {
+	test_expect_success 'add relative' '
+		test -z "$(cd / && git ls-files)" &&
+		git add foome &&
+		git add bar/barme &&
+		git add ../me &&
+		( cd / && git ls-files --stage ) > result &&
+		test_cmp /ls.expected result &&
+		rm "$(git rev-parse --git-dir)/index"
+	'
+
+	test_expect_success 'add absolute' '
+		test -z "$(cd / && git ls-files)" &&
+		git add /foo/foome &&
+		git add /foo/bar/barme &&
+		git add /me &&
+		( cd / && git ls-files --stage ) > result &&
+		test_cmp /ls.expected result &&
+		rm "$(git rev-parse --git-dir)/index"
+	'
+}
+
+test_foobar_foobar() {
+	test_expect_success 'add relative' '
+		test -z "$(cd / && git ls-files)" &&
+		git add ../foome &&
+		git add barme &&
+		git add ../../me &&
+		( cd / && git ls-files --stage ) > result &&
+		test_cmp /ls.expected result &&
+		rm "$(git rev-parse --git-dir)/index"
+	'
+
+	test_expect_success 'add absolute' '
+		test -z "$(cd / && git ls-files)" &&
+		git add /foo/foome &&
+		git add /foo/bar/barme &&
+		git add /me &&
+		( cd / && git ls-files --stage ) > result &&
+		test_cmp /ls.expected result &&
+		rm "$(git rev-parse --git-dir)/index"
+	'
+}
+
+if ! test_have_prereq POSIXPERM || ! [ -w / ]; then
+	skip_all="Dangerous test skipped. Read this test if you want to execute it"
+	test_done
+fi
+
+if [ "$IKNOWWHATIAMDOING" != "YES" ]; then
+	skip_all="You must set env var IKNOWWHATIAMDOING=YES in order to run this test"
+	test_done
+fi
+
+if [ "$UID" = 0 ]; then
+	skip_all="No you can't run this with root"
+	test_done
+fi
+
+ONE_SHA1=d00491fd7e5bb6fa28c517a0bb32b8b506539d4d
+
+test_expect_success 'setup' '
+	rm -rf /foo
+	mkdir /foo &&
+	mkdir /foo/bar &&
+	echo 1 > /foo/foome &&
+	echo 1 > /foo/bar/barme &&
+	echo 1 > /me
+'
+
+say "GIT_DIR absolute, GIT_WORK_TREE set"
+
+test_expect_success 'go to /' 'cd /'
+
+cat >ls.expected <<EOF
+100644 $ONE_SHA1 0	foo/bar/barme
+100644 $ONE_SHA1 0	foo/foome
+100644 $ONE_SHA1 0	me
+EOF
+
+GIT_DIR="$TRASH_DIRECTORY/.git" && export GIT_DIR
+GIT_WORK_TREE=/ && export GIT_WORK_TREE
+
+test_vars 'abs gitdir, root' "$GIT_DIR" "/" ""
+test_foobar_root
+
+test_expect_success 'go to /foo' 'cd /foo'
+
+test_vars 'abs gitdir, foo' "$GIT_DIR" "/" "foo/"
+test_foobar_foo
+
+test_expect_success 'go to /foo/bar' 'cd /foo/bar'
+
+test_vars 'abs gitdir, foo/bar' "$GIT_DIR" "/" "foo/bar/"
+test_foobar_foobar
+
+say "GIT_DIR relative, GIT_WORK_TREE set"
+
+test_expect_success 'go to /' 'cd /'
+
+GIT_DIR="$(echo $TRASH_DIRECTORY|sed 's,^/,,')/.git" && export GIT_DIR
+GIT_WORK_TREE=/ && export GIT_WORK_TREE
+
+test_vars 'rel gitdir, root' "$GIT_DIR" "/" ""
+test_foobar_root
+
+test_expect_success 'go to /foo' 'cd /foo'
+
+GIT_DIR="../$TRASH_DIRECTORY/.git" && export GIT_DIR
+GIT_WORK_TREE=/ && export GIT_WORK_TREE
+
+test_vars 'rel gitdir, foo' "$TRASH_DIRECTORY/.git" "/" "foo/"
+test_foobar_foo
+
+test_expect_success 'go to /foo/bar' 'cd /foo/bar'
+
+GIT_DIR="../../$TRASH_DIRECTORY/.git" && export GIT_DIR
+GIT_WORK_TREE=/ && export GIT_WORK_TREE
+
+test_vars 'rel gitdir, foo/bar' "$TRASH_DIRECTORY/.git" "/" "foo/bar/"
+test_foobar_foobar
+
+say "GIT_DIR relative, GIT_WORK_TREE relative"
+
+test_expect_success 'go to /' 'cd /'
+
+GIT_DIR="$(echo $TRASH_DIRECTORY|sed 's,^/,,')/.git" && export GIT_DIR
+GIT_WORK_TREE=. && export GIT_WORK_TREE
+
+test_vars 'rel gitdir, root' "$GIT_DIR" "/" ""
+test_foobar_root
+
+test_expect_success 'go to /' 'cd /foo'
+
+GIT_DIR="../$TRASH_DIRECTORY/.git" && export GIT_DIR
+GIT_WORK_TREE=.. && export GIT_WORK_TREE
+
+test_vars 'rel gitdir, foo' "$TRASH_DIRECTORY/.git" "/" "foo/"
+test_foobar_foo
+
+test_expect_success 'go to /foo/bar' 'cd /foo/bar'
+
+GIT_DIR="../../$TRASH_DIRECTORY/.git" && export GIT_DIR
+GIT_WORK_TREE=../.. && export GIT_WORK_TREE
+
+test_vars 'rel gitdir, foo/bar' "$TRASH_DIRECTORY/.git" "/" "foo/bar/"
+test_foobar_foobar
+
+say ".git at root"
+
+unset GIT_DIR
+unset GIT_WORK_TREE
+
+test_expect_success 'go to /' 'cd /'
+test_expect_success 'setup' '
+	rm -rf /.git
+	echo "Initialized empty Git repository in /.git/" > expected &&
+	git init > result &&
+	test_cmp expected result
+'
+
+test_vars 'auto gitdir, root' ".git" "/" ""
+test_foobar_root
+
+test_expect_success 'go to /foo' 'cd /foo'
+test_vars 'auto gitdir, foo' "/.git" "/" "foo/"
+test_foobar_foo
+
+test_expect_success 'go to /foo/bar' 'cd /foo/bar'
+test_vars 'auto gitdir, foo/bar' "/.git" "/" "foo/bar/"
+test_foobar_foobar
+
+test_expect_success 'cleanup' 'rm -rf /.git'
+
+say "auto bare gitdir"
+
+# DESTROYYYYY!!!!!
+test_expect_success 'setup' '
+	rm -rf /refs /objects /info /hooks
+	rm /*
+	cd / &&
+	echo "Initialized empty Git repository in /" > expected &&
+	git init --bare > result &&
+	test_cmp expected result
+'
+
+test_vars 'auto gitdir, root' "." "" ""
+
+test_expect_success 'go to /foo' 'cd /foo'
+
+test_vars 'auto gitdir, root' "/" "" ""
+
+test_done
diff --git a/t/t1509/excludes b/t/t1509/excludes
new file mode 100644
index 0000000..d4d21d3
--- /dev/null
+++ b/t/t1509/excludes
@@ -0,0 +1,14 @@
+*.o
+*~
+*.bak
+*.c
+*.h
+.git
+contrib
+Documentation
+git-gui
+gitk-git
+gitweb
+t/t4013
+t/t5100
+t/t5515
diff --git a/t/t1509/prepare-chroot.sh b/t/t1509/prepare-chroot.sh
new file mode 100755
index 0000000..c5334a8
--- /dev/null
+++ b/t/t1509/prepare-chroot.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+die() {
+	echo >&2 "$@"
+	exit 1
+}
+
+xmkdir() {
+	while [ -n "$1" ]; do
+		[ -d "$1" ] || mkdir "$1" || die "Unable to mkdir $1"
+		shift
+	done
+}
+
+R="$1"
+
+[ -n "$R" ] || die "Usage: prepare-chroot.sh <root>"
+[ -x git ] || die "This script needs to be executed at git source code's top directory"
+[ -x /bin/busybox ] || die "You need busybox"
+
+xmkdir "$R" "$R/bin" "$R/etc" "$R/lib" "$R/dev"
+[ -c "$R/dev/null" ] || die "/dev/null is missing. Do mknod $R/dev/null c 1 3 && chmod 666 $R/dev/null"
+echo "root:x:0:0:root:/:/bin/sh" > "$R/etc/passwd"
+echo "$(id -nu):x:$(id -u):$(id -g)::$(pwd)/t:/bin/sh" >> "$R/etc/passwd"
+echo "root::0:root" > "$R/etc/group"
+echo "$(id -ng)::$(id -g):$(id -nu)" >> "$R/etc/group"
+
+[ -x "$R/bin/busybox" ] || cp /bin/busybox "$R/bin/busybox"
+[ -x "$R/bin/sh" ] || ln -s /bin/busybox "$R/bin/sh"
+[ -x "$R/bin/su" ] || ln -s /bin/busybox "$R/bin/su"
+
+mkdir -p "$R$(pwd)"
+rsync --exclude-from t/t1509/excludes -Ha . "$R$(pwd)"
+ldd git | grep '/' | sed 's,.*\s\(/[^ ]*\).*,\1,' | while read i; do
+	mkdir -p "$R$(dirname $i)"
+	cp "$i" "$R/$i"
+done
+echo "Execute this in root: 'chroot $R /bin/su - $(id -nu)'"
diff --git a/t/t1510-repo-setup.sh b/t/t1510-repo-setup.sh
new file mode 100755
index 0000000..80aedfc
--- /dev/null
+++ b/t/t1510-repo-setup.sh
@@ -0,0 +1,776 @@
+#!/bin/sh
+
+test_description="Tests of cwd/prefix/worktree/gitdir setup in all cases
+
+A few rules for repo setup:
+
+1. GIT_DIR is relative to user's cwd. --git-dir is equivalent to
+   GIT_DIR.
+
+2. .git file is relative to parent directory. .git file is basically
+   symlink in disguise. The directory where .git file points to will
+   become new git_dir.
+
+3. core.worktree is relative to git_dir.
+
+4. GIT_WORK_TREE is relative to user's cwd. --work-tree is
+   equivalent to GIT_WORK_TREE.
+
+5. GIT_WORK_TREE/core.worktree was originally meant to work only if
+   GIT_DIR is set, but earlier git didn't enforce it, and some scripts
+   depend on the implementation that happened to first discover .git by
+   going up from the users $cwd and then using the specified working tree
+   that may or may not have any relation to where .git was found in.  This
+   historical behaviour must be kept.
+
+6. Effective GIT_WORK_TREE overrides core.worktree and core.bare
+
+7. Effective core.worktree conflicts with core.bare
+
+8. If GIT_DIR is set but neither worktree nor bare setting is given,
+   original cwd becomes worktree.
+
+9. If .git discovery is done inside a repo, the repo becomes a bare
+   repo. .git discovery is performed if GIT_DIR is not set.
+
+10. If no worktree is available, cwd remains unchanged, prefix is
+    NULL.
+
+11. When user's cwd is outside worktree, cwd remains unchanged,
+    prefix is NULL.
+"
+. ./test-lib.sh
+
+here=$(pwd)
+
+test_repo () {
+	(
+		cd "$1" &&
+		if test -n "$2"
+		then
+			GIT_DIR="$2" &&
+			export GIT_DIR
+		fi &&
+		if test -n "$3"
+		then
+			GIT_WORK_TREE="$3" &&
+			export GIT_WORK_TREE
+		fi &&
+		rm -f trace &&
+		GIT_TRACE_SETUP="$(pwd)/trace" git symbolic-ref HEAD >/dev/null &&
+		grep '^setup: ' trace >result &&
+		test_cmp expected result
+	)
+}
+
+maybe_config () {
+	file=$1 var=$2 value=$3 &&
+	if test "$value" != unset
+	then
+		git config --file="$file" "$var" "$value"
+	fi
+}
+
+setup_repo () {
+	name=$1 worktreecfg=$2 gitfile=$3 barecfg=$4 &&
+	sane_unset GIT_DIR GIT_WORK_TREE &&
+
+	git init "$name" &&
+	maybe_config "$name/.git/config" core.worktree "$worktreecfg" &&
+	maybe_config "$name/.git/config" core.bare "$barecfg" &&
+	mkdir -p "$name/sub/sub" &&
+
+	if test "${gitfile:+set}"
+	then
+		mv "$name/.git" "$name.git" &&
+		echo "gitdir: ../$name.git" >"$name/.git"
+	fi
+}
+
+maybe_set () {
+	var=$1 value=$2 &&
+	if test "$value" != unset
+	then
+		eval "$var=\$value" &&
+		export $var
+	fi
+}
+
+setup_env () {
+	worktreenv=$1 gitdirenv=$2 &&
+	sane_unset GIT_DIR GIT_WORK_TREE &&
+	maybe_set GIT_DIR "$gitdirenv" &&
+	maybe_set GIT_WORK_TREE "$worktreeenv"
+}
+
+expect () {
+	cat >"$1/expected" <<-EOF
+	setup: git_dir: $2
+	setup: worktree: $3
+	setup: cwd: $4
+	setup: prefix: $5
+	EOF
+}
+
+try_case () {
+	name=$1 worktreeenv=$2 gitdirenv=$3 &&
+	setup_env "$worktreeenv" "$gitdirenv" &&
+	expect "$name" "$4" "$5" "$6" "$7" &&
+	test_repo "$name"
+}
+
+run_wt_tests () {
+	N=$1 gitfile=$2
+
+	absgit="$here/$N/.git"
+	dotgit=.git
+	dotdotgit=../../.git
+
+	if test "$gitfile"
+	then
+		absgit="$here/$N.git"
+		dotgit=$absgit dotdotgit=$absgit
+	fi
+
+	test_expect_success "#$N: explicit GIT_WORK_TREE and GIT_DIR at toplevel" '
+		try_case $N "$here/$N" .git \
+			"$dotgit" "$here/$N" "$here/$N" "(null)" &&
+		try_case $N . .git \
+			"$dotgit" "$here/$N" "$here/$N" "(null)" &&
+		try_case $N "$here/$N" "$here/$N/.git" \
+			"$absgit" "$here/$N" "$here/$N" "(null)" &&
+		try_case $N . "$here/$N/.git" \
+			"$absgit" "$here/$N" "$here/$N" "(null)"
+	'
+
+	test_expect_success "#$N: explicit GIT_WORK_TREE and GIT_DIR in subdir" '
+		try_case $N/sub/sub "$here/$N" ../../.git \
+			"$absgit" "$here/$N" "$here/$N" sub/sub/ &&
+		try_case $N/sub/sub ../.. ../../.git \
+			"$absgit" "$here/$N" "$here/$N" sub/sub/ &&
+		try_case $N/sub/sub "$here/$N" "$here/$N/.git" \
+			"$absgit" "$here/$N" "$here/$N" sub/sub/ &&
+		try_case $N/sub/sub ../.. "$here/$N/.git" \
+			"$absgit" "$here/$N" "$here/$N" sub/sub/
+	'
+
+	test_expect_success "#$N: explicit GIT_WORK_TREE from parent of worktree" '
+		try_case $N "$here/$N/wt" .git \
+			"$dotgit" "$here/$N/wt" "$here/$N" "(null)" &&
+		try_case $N wt .git \
+			"$dotgit" "$here/$N/wt" "$here/$N" "(null)" &&
+		try_case $N wt "$here/$N/.git" \
+			"$absgit" "$here/$N/wt" "$here/$N" "(null)" &&
+		try_case $N "$here/$N/wt" "$here/$N/.git" \
+			"$absgit" "$here/$N/wt" "$here/$N" "(null)"
+	'
+
+	test_expect_success "#$N: explicit GIT_WORK_TREE from nephew of worktree" '
+		try_case $N/sub/sub "$here/$N/wt" ../../.git \
+			"$dotdotgit" "$here/$N/wt" "$here/$N/sub/sub" "(null)" &&
+		try_case $N/sub/sub ../../wt ../../.git \
+			"$dotdotgit" "$here/$N/wt" "$here/$N/sub/sub" "(null)" &&
+		try_case $N/sub/sub ../../wt "$here/$N/.git" \
+			"$absgit" "$here/$N/wt" "$here/$N/sub/sub" "(null)" &&
+		try_case $N/sub/sub "$here/$N/wt" "$here/$N/.git" \
+			"$absgit" "$here/$N/wt" "$here/$N/sub/sub" "(null)"
+	'
+
+	test_expect_success "#$N: chdir_to_toplevel uses worktree, not git dir" '
+		try_case $N "$here" .git \
+			"$absgit" "$here" "$here" $N/ &&
+		try_case $N .. .git \
+			"$absgit" "$here" "$here" $N/ &&
+		try_case $N .. "$here/$N/.git" \
+			"$absgit" "$here" "$here" $N/ &&
+		try_case $N "$here" "$here/$N/.git" \
+			"$absgit" "$here" "$here" $N/
+	'
+
+	test_expect_success "#$N: chdir_to_toplevel uses worktree (from subdir)" '
+		try_case $N/sub/sub "$here" ../../.git \
+			"$absgit" "$here" "$here" $N/sub/sub/ &&
+		try_case $N/sub/sub ../../.. ../../.git \
+			"$absgit" "$here" "$here" $N/sub/sub/ &&
+		try_case $N/sub/sub ../../../ "$here/$N/.git" \
+			"$absgit" "$here" "$here" $N/sub/sub/ &&
+		try_case $N/sub/sub "$here" "$here/$N/.git" \
+			"$absgit" "$here" "$here" $N/sub/sub/
+	'
+}
+
+# try_repo #c GIT_WORK_TREE GIT_DIR core.worktree .gitfile? core.bare \
+#	(git dir) (work tree) (cwd) (prefix) \	<-- at toplevel
+#	(git dir) (work tree) (cwd) (prefix)	<-- from subdir
+try_repo () {
+	name=$1 worktreeenv=$2 gitdirenv=$3 &&
+	setup_repo "$name" "$4" "$5" "$6" &&
+	shift 6 &&
+	try_case "$name" "$worktreeenv" "$gitdirenv" \
+		"$1" "$2" "$3" "$4" &&
+	shift 4 &&
+	case "$gitdirenv" in
+	/* | ?:/* | unset) ;;
+	*)
+		gitdirenv=../$gitdirenv ;;
+	esac &&
+	try_case "$name/sub" "$worktreeenv" "$gitdirenv" \
+		"$1" "$2" "$3" "$4"
+}
+
+# Bit 0 = GIT_WORK_TREE
+# Bit 1 = GIT_DIR
+# Bit 2 = core.worktree
+# Bit 3 = .git is a file
+# Bit 4 = bare repo
+# Case# = encoding of the above 5 bits
+
+test_expect_success '#0: nonbare repo, no explicit configuration' '
+	try_repo 0 unset unset unset "" unset \
+		.git "$here/0" "$here/0" "(null)" \
+		.git "$here/0" "$here/0" sub/ 2>message &&
+	! test -s message
+'
+
+test_expect_success '#1: GIT_WORK_TREE without explicit GIT_DIR is accepted' '
+	mkdir -p wt &&
+	try_repo 1 "$here" unset unset "" unset \
+		"$here/1/.git" "$here" "$here" 1/ \
+		"$here/1/.git" "$here" "$here" 1/sub/ 2>message &&
+	! test -s message
+'
+
+test_expect_success '#2: worktree defaults to cwd with explicit GIT_DIR' '
+	try_repo 2 unset "$here/2/.git" unset "" unset \
+		"$here/2/.git" "$here/2" "$here/2" "(null)" \
+		"$here/2/.git" "$here/2/sub" "$here/2/sub" "(null)"
+'
+
+test_expect_success '#2b: relative GIT_DIR' '
+	try_repo 2b unset ".git" unset "" unset \
+		".git" "$here/2b" "$here/2b" "(null)" \
+		"../.git" "$here/2b/sub" "$here/2b/sub" "(null)"
+'
+
+test_expect_success '#3: setup' '
+	setup_repo 3 unset "" unset &&
+	mkdir -p 3/sub/sub 3/wt/sub
+'
+run_wt_tests 3
+
+test_expect_success '#4: core.worktree without GIT_DIR set is accepted' '
+	setup_repo 4 ../sub "" unset &&
+	mkdir -p 4/sub sub &&
+	try_case 4 unset unset \
+		.git "$here/4/sub" "$here/4" "(null)" \
+		"$here/4/.git" "$here/4/sub" "$here/4/sub" "(null)" 2>message &&
+	! test -s message
+'
+
+test_expect_success '#5: core.worktree + GIT_WORK_TREE is accepted' '
+	# or: you cannot intimidate away the lack of GIT_DIR setting
+	try_repo 5 "$here" unset "$here/5" "" unset \
+		"$here/5/.git" "$here" "$here" 5/ \
+		"$here/5/.git" "$here" "$here" 5/sub/ 2>message &&
+	try_repo 5a .. unset "$here/5a" "" unset \
+		"$here/5a/.git" "$here" "$here" 5a/ \
+		"$here/5a/.git" "$here/5a" "$here/5a" sub/ &&
+	! test -s message
+'
+
+test_expect_success '#6: setting GIT_DIR brings core.worktree to life' '
+	setup_repo 6 "$here/6" "" unset &&
+	try_case 6 unset .git \
+		.git "$here/6" "$here/6" "(null)" &&
+	try_case 6 unset "$here/6/.git" \
+		"$here/6/.git" "$here/6" "$here/6" "(null)" &&
+	try_case 6/sub/sub unset ../../.git \
+		"$here/6/.git" "$here/6" "$here/6" sub/sub/ &&
+	try_case 6/sub/sub unset "$here/6/.git" \
+		"$here/6/.git" "$here/6" "$here/6" sub/sub/
+'
+
+test_expect_success '#6b: GIT_DIR set, core.worktree relative' '
+	setup_repo 6b .. "" unset &&
+	try_case 6b unset .git \
+		.git "$here/6b" "$here/6b" "(null)" &&
+	try_case 6b unset "$here/6b/.git" \
+		"$here/6b/.git" "$here/6b" "$here/6b" "(null)" &&
+	try_case 6b/sub/sub unset ../../.git \
+		"$here/6b/.git" "$here/6b" "$here/6b" sub/sub/ &&
+	try_case 6b/sub/sub unset "$here/6b/.git" \
+		"$here/6b/.git" "$here/6b" "$here/6b" sub/sub/
+'
+
+test_expect_success '#6c: GIT_DIR set, core.worktree=../wt (absolute)' '
+	setup_repo 6c "$here/6c/wt" "" unset &&
+	mkdir -p 6c/wt/sub &&
+
+	try_case 6c unset .git \
+		.git "$here/6c/wt" "$here/6c" "(null)" &&
+	try_case 6c unset "$here/6c/.git" \
+		"$here/6c/.git" "$here/6c/wt" "$here/6c" "(null)" &&
+	try_case 6c/sub/sub unset ../../.git \
+		../../.git "$here/6c/wt" "$here/6c/sub/sub" "(null)" &&
+	try_case 6c/sub/sub unset "$here/6c/.git" \
+		"$here/6c/.git" "$here/6c/wt" "$here/6c/sub/sub" "(null)"
+'
+
+test_expect_success '#6d: GIT_DIR set, core.worktree=../wt (relative)' '
+	setup_repo 6d "$here/6d/wt" "" unset &&
+	mkdir -p 6d/wt/sub &&
+
+	try_case 6d unset .git \
+		.git "$here/6d/wt" "$here/6d" "(null)" &&
+	try_case 6d unset "$here/6d/.git" \
+		"$here/6d/.git" "$here/6d/wt" "$here/6d" "(null)" &&
+	try_case 6d/sub/sub unset ../../.git \
+		../../.git "$here/6d/wt" "$here/6d/sub/sub" "(null)" &&
+	try_case 6d/sub/sub unset "$here/6d/.git" \
+		"$here/6d/.git" "$here/6d/wt" "$here/6d/sub/sub" "(null)"
+'
+
+test_expect_success '#6e: GIT_DIR set, core.worktree=../.. (absolute)' '
+	setup_repo 6e "$here" "" unset &&
+	try_case 6e unset .git \
+		"$here/6e/.git" "$here" "$here" 6e/ &&
+	try_case 6e unset "$here/6e/.git" \
+		"$here/6e/.git" "$here" "$here" 6e/ &&
+	try_case 6e/sub/sub unset ../../.git \
+		"$here/6e/.git" "$here" "$here" 6e/sub/sub/ &&
+	try_case 6e/sub/sub unset "$here/6e/.git" \
+		"$here/6e/.git" "$here" "$here" 6e/sub/sub/
+'
+
+test_expect_success '#6f: GIT_DIR set, core.worktree=../.. (relative)' '
+	setup_repo 6f ../../ "" unset &&
+	try_case 6f unset .git \
+		"$here/6f/.git" "$here" "$here" 6f/ &&
+	try_case 6f unset "$here/6f/.git" \
+		"$here/6f/.git" "$here" "$here" 6f/ &&
+	try_case 6f/sub/sub unset ../../.git \
+		"$here/6f/.git" "$here" "$here" 6f/sub/sub/ &&
+	try_case 6f/sub/sub unset "$here/6f/.git" \
+		"$here/6f/.git" "$here" "$here" 6f/sub/sub/
+'
+
+# case #7: GIT_WORK_TREE overrides core.worktree.
+test_expect_success '#7: setup' '
+	setup_repo 7 non-existent "" unset &&
+	mkdir -p 7/sub/sub 7/wt/sub
+'
+run_wt_tests 7
+
+test_expect_success '#8: gitfile, easy case' '
+	try_repo 8 unset unset unset gitfile unset \
+		"$here/8.git" "$here/8" "$here/8" "(null)" \
+		"$here/8.git" "$here/8" "$here/8" sub/
+'
+
+test_expect_success '#9: GIT_WORK_TREE accepted with gitfile' '
+	mkdir -p 9/wt &&
+	try_repo 9 wt unset unset gitfile unset \
+		"$here/9.git" "$here/9/wt" "$here/9" "(null)" \
+		"$here/9.git" "$here/9/sub/wt" "$here/9/sub" "(null)" 2>message &&
+	! test -s message
+'
+
+test_expect_success '#10: GIT_DIR can point to gitfile' '
+	try_repo 10 unset "$here/10/.git" unset gitfile unset \
+		"$here/10.git" "$here/10" "$here/10" "(null)" \
+		"$here/10.git" "$here/10/sub" "$here/10/sub" "(null)"
+'
+
+test_expect_success '#10b: relative GIT_DIR can point to gitfile' '
+	try_repo 10b unset .git unset gitfile unset \
+		"$here/10b.git" "$here/10b" "$here/10b" "(null)" \
+		"$here/10b.git" "$here/10b/sub" "$here/10b/sub" "(null)"
+'
+
+# case #11: GIT_WORK_TREE works, gitfile case.
+test_expect_success '#11: setup' '
+	setup_repo 11 unset gitfile unset &&
+	mkdir -p 11/sub/sub 11/wt/sub
+'
+run_wt_tests 11 gitfile
+
+test_expect_success '#12: core.worktree with gitfile is accepted' '
+	try_repo 12 unset unset "$here/12" gitfile unset \
+		"$here/12.git" "$here/12" "$here/12" "(null)" \
+		"$here/12.git" "$here/12" "$here/12" sub/ 2>message &&
+	! test -s message
+'
+
+test_expect_success '#13: core.worktree+GIT_WORK_TREE accepted (with gitfile)' '
+	# or: you cannot intimidate away the lack of GIT_DIR setting
+	try_repo 13 non-existent-too unset non-existent gitfile unset \
+		"$here/13.git" "$here/13/non-existent-too" "$here/13" "(null)" \
+		"$here/13.git" "$here/13/sub/non-existent-too" "$here/13/sub" "(null)" 2>message &&
+	! test -s message
+'
+
+# case #14.
+# If this were more table-driven, it could share code with case #6.
+
+test_expect_success '#14: core.worktree with GIT_DIR pointing to gitfile' '
+	setup_repo 14 "$here/14" gitfile unset &&
+	try_case 14 unset .git \
+		"$here/14.git" "$here/14" "$here/14" "(null)" &&
+	try_case 14 unset "$here/14/.git" \
+		"$here/14.git" "$here/14" "$here/14" "(null)" &&
+	try_case 14/sub/sub unset ../../.git \
+		"$here/14.git" "$here/14" "$here/14" sub/sub/ &&
+	try_case 14/sub/sub unset "$here/14/.git" \
+		"$here/14.git" "$here/14" "$here/14" sub/sub/ &&
+
+	setup_repo 14c "$here/14c/wt" gitfile unset &&
+	mkdir -p 14c/wt/sub &&
+
+	try_case 14c unset .git \
+		"$here/14c.git" "$here/14c/wt" "$here/14c" "(null)" &&
+	try_case 14c unset "$here/14c/.git" \
+		"$here/14c.git" "$here/14c/wt" "$here/14c" "(null)" &&
+	try_case 14c/sub/sub unset ../../.git \
+		"$here/14c.git" "$here/14c/wt" "$here/14c/sub/sub" "(null)" &&
+	try_case 14c/sub/sub unset "$here/14c/.git" \
+		"$here/14c.git" "$here/14c/wt" "$here/14c/sub/sub" "(null)" &&
+
+	setup_repo 14d "$here/14d/wt" gitfile unset &&
+	mkdir -p 14d/wt/sub &&
+
+	try_case 14d unset .git \
+		"$here/14d.git" "$here/14d/wt" "$here/14d" "(null)" &&
+	try_case 14d unset "$here/14d/.git" \
+		"$here/14d.git" "$here/14d/wt" "$here/14d" "(null)" &&
+	try_case 14d/sub/sub unset ../../.git \
+		"$here/14d.git" "$here/14d/wt" "$here/14d/sub/sub" "(null)" &&
+	try_case 14d/sub/sub unset "$here/14d/.git" \
+		"$here/14d.git" "$here/14d/wt" "$here/14d/sub/sub" "(null)" &&
+
+	setup_repo 14e "$here" gitfile unset &&
+	try_case 14e unset .git \
+		"$here/14e.git" "$here" "$here" 14e/ &&
+	try_case 14e unset "$here/14e/.git" \
+		"$here/14e.git" "$here" "$here" 14e/ &&
+	try_case 14e/sub/sub unset ../../.git \
+		"$here/14e.git" "$here" "$here" 14e/sub/sub/ &&
+	try_case 14e/sub/sub unset "$here/14e/.git" \
+		"$here/14e.git" "$here" "$here" 14e/sub/sub/
+'
+
+test_expect_success '#14b: core.worktree is relative to actual git dir' '
+	setup_repo 14b ../14b gitfile unset &&
+	try_case 14b unset .git \
+		"$here/14b.git" "$here/14b" "$here/14b" "(null)" &&
+	try_case 14b unset "$here/14b/.git" \
+		"$here/14b.git" "$here/14b" "$here/14b" "(null)" &&
+	try_case 14b/sub/sub unset ../../.git \
+		"$here/14b.git" "$here/14b" "$here/14b" sub/sub/ &&
+	try_case 14b/sub/sub unset "$here/14b/.git" \
+		"$here/14b.git" "$here/14b" "$here/14b" sub/sub/ &&
+
+	setup_repo 14f ../ gitfile unset &&
+	try_case 14f unset .git \
+		"$here/14f.git" "$here" "$here" 14f/ &&
+	try_case 14f unset "$here/14f/.git" \
+		"$here/14f.git" "$here" "$here" 14f/ &&
+	try_case 14f/sub/sub unset ../../.git \
+		"$here/14f.git" "$here" "$here" 14f/sub/sub/ &&
+	try_case 14f/sub/sub unset "$here/14f/.git" \
+		"$here/14f.git" "$here" "$here" 14f/sub/sub/
+'
+
+# case #15: GIT_WORK_TREE overrides core.worktree (gitfile case).
+test_expect_success '#15: setup' '
+	setup_repo 15 non-existent gitfile unset &&
+	mkdir -p 15/sub/sub 15/wt/sub
+'
+run_wt_tests 15 gitfile
+
+test_expect_success '#16a: implicitly bare repo (cwd inside .git dir)' '
+	setup_repo 16a unset "" unset &&
+	mkdir -p 16a/.git/wt/sub &&
+
+	try_case 16a/.git unset unset \
+		. "(null)" "$here/16a/.git" "(null)" &&
+	try_case 16a/.git/wt unset unset \
+		"$here/16a/.git" "(null)" "$here/16a/.git/wt" "(null)" &&
+	try_case 16a/.git/wt/sub unset unset \
+		"$here/16a/.git" "(null)" "$here/16a/.git/wt/sub" "(null)"
+'
+
+test_expect_success '#16b: bare .git (cwd inside .git dir)' '
+	setup_repo 16b unset "" true &&
+	mkdir -p 16b/.git/wt/sub &&
+
+	try_case 16b/.git unset unset \
+		. "(null)" "$here/16b/.git" "(null)" &&
+	try_case 16b/.git/wt unset unset \
+		"$here/16b/.git" "(null)" "$here/16b/.git/wt" "(null)" &&
+	try_case 16b/.git/wt/sub unset unset \
+		"$here/16b/.git" "(null)" "$here/16b/.git/wt/sub" "(null)"
+'
+
+test_expect_success '#16c: bare .git has no worktree' '
+	try_repo 16c unset unset unset "" true \
+		.git "(null)" "$here/16c" "(null)" \
+		"$here/16c/.git" "(null)" "$here/16c/sub" "(null)"
+'
+
+test_expect_success '#17: GIT_WORK_TREE without explicit GIT_DIR is accepted (bare case)' '
+	# Just like #16.
+	setup_repo 17a unset "" true &&
+	setup_repo 17b unset "" true &&
+	mkdir -p 17a/.git/wt/sub &&
+	mkdir -p 17b/.git/wt/sub &&
+
+	try_case 17a/.git "$here/17a" unset \
+		"$here/17a/.git" "$here/17a" "$here/17a" .git/ \
+		2>message &&
+	try_case 17a/.git/wt "$here/17a" unset \
+		"$here/17a/.git" "$here/17a" "$here/17a" .git/wt/ &&
+	try_case 17a/.git/wt/sub "$here/17a" unset \
+		"$here/17a/.git" "$here/17a" "$here/17a" .git/wt/sub/ &&
+
+	try_case 17b/.git "$here/17b" unset \
+		"$here/17b/.git" "$here/17b" "$here/17b" .git/ &&
+	try_case 17b/.git/wt "$here/17b" unset \
+		"$here/17b/.git" "$here/17b" "$here/17b" .git/wt/ &&
+	try_case 17b/.git/wt/sub "$here/17b" unset \
+		"$here/17b/.git" "$here/17b" "$here/17b" .git/wt/sub/ &&
+
+	try_repo 17c "$here/17c" unset unset "" true \
+		.git "$here/17c" "$here/17c" "(null)" \
+		"$here/17c/.git" "$here/17c" "$here/17c" sub/ 2>message &&
+	! test -s message
+'
+
+test_expect_success '#18: bare .git named by GIT_DIR has no worktree' '
+	try_repo 18 unset .git unset "" true \
+		.git "(null)" "$here/18" "(null)" \
+		../.git "(null)" "$here/18/sub" "(null)" &&
+	try_repo 18b unset "$here/18b/.git" unset "" true \
+		"$here/18b/.git" "(null)" "$here/18b" "(null)" \
+		"$here/18b/.git" "(null)" "$here/18b/sub" "(null)"
+'
+
+# Case #19: GIT_DIR + GIT_WORK_TREE suppresses bareness.
+test_expect_success '#19: setup' '
+	setup_repo 19 unset "" true &&
+	mkdir -p 19/sub/sub 19/wt/sub
+'
+run_wt_tests 19
+
+test_expect_success '#20a: core.worktree without GIT_DIR accepted (inside .git)' '
+	# Unlike case #16a.
+	setup_repo 20a "$here/20a" "" unset &&
+	mkdir -p 20a/.git/wt/sub &&
+	try_case 20a/.git unset unset \
+		"$here/20a/.git" "$here/20a" "$here/20a" .git/ 2>message &&
+	try_case 20a/.git/wt unset unset \
+		"$here/20a/.git" "$here/20a" "$here/20a" .git/wt/ &&
+	try_case 20a/.git/wt/sub unset unset \
+		"$here/20a/.git" "$here/20a" "$here/20a" .git/wt/sub/ &&
+	! test -s message
+'
+
+test_expect_success '#20b/c: core.worktree and core.bare conflict' '
+	setup_repo 20b non-existent "" true &&
+	mkdir -p 20b/.git/wt/sub &&
+	(
+		cd 20b/.git &&
+		test_must_fail git symbolic-ref HEAD >/dev/null
+	) 2>message &&
+	grep "core.bare and core.worktree" message
+'
+
+# Case #21: core.worktree/GIT_WORK_TREE overrides core.bare' '
+test_expect_success '#21: setup, core.worktree warns before overriding core.bare' '
+	setup_repo 21 non-existent "" unset &&
+	mkdir -p 21/.git/wt/sub &&
+	(
+		cd 21/.git &&
+		GIT_WORK_TREE="$here/21" &&
+		export GIT_WORK_TREE &&
+		git symbolic-ref HEAD >/dev/null
+	) 2>message &&
+	! test -s message
+
+'
+run_wt_tests 21
+
+test_expect_success '#22a: core.worktree = GIT_DIR = .git dir' '
+	# like case #6.
+
+	setup_repo 22a "$here/22a/.git" "" unset &&
+	setup_repo 22ab . "" unset &&
+	mkdir -p 22a/.git/sub 22a/sub &&
+	mkdir -p 22ab/.git/sub 22ab/sub &&
+	try_case 22a/.git unset . \
+		. "$here/22a/.git" "$here/22a/.git" "(null)" &&
+	try_case 22a/.git unset "$here/22a/.git" \
+		"$here/22a/.git" "$here/22a/.git" "$here/22a/.git" "(null)" &&
+	try_case 22a/.git/sub unset .. \
+		"$here/22a/.git" "$here/22a/.git" "$here/22a/.git" sub/ &&
+	try_case 22a/.git/sub unset "$here/22a/.git" \
+		"$here/22a/.git" "$here/22a/.git" "$here/22a/.git" sub/ &&
+
+	try_case 22ab/.git unset . \
+		. "$here/22ab/.git" "$here/22ab/.git" "(null)" &&
+	try_case 22ab/.git unset "$here/22ab/.git" \
+		"$here/22ab/.git" "$here/22ab/.git" "$here/22ab/.git" "(null)" &&
+	try_case 22ab/.git/sub unset .. \
+		"$here/22ab/.git" "$here/22ab/.git" "$here/22ab/.git" sub/ &&
+	try_case 22ab/.git unset "$here/22ab/.git" \
+		"$here/22ab/.git" "$here/22ab/.git" "$here/22ab/.git" "(null)"
+'
+
+test_expect_success '#22b: core.worktree child of .git, GIT_DIR=.git' '
+	setup_repo 22b "$here/22b/.git/wt" "" unset &&
+	setup_repo 22bb wt "" unset &&
+	mkdir -p 22b/.git/sub 22b/sub 22b/.git/wt/sub 22b/wt/sub &&
+	mkdir -p 22bb/.git/sub 22bb/sub 22bb/.git/wt 22bb/wt &&
+
+	try_case 22b/.git unset . \
+		. "$here/22b/.git/wt" "$here/22b/.git" "(null)" &&
+	try_case 22b/.git unset "$here/22b/.git" \
+		"$here/22b/.git" "$here/22b/.git/wt" "$here/22b/.git" "(null)" &&
+	try_case 22b/.git/sub unset .. \
+		.. "$here/22b/.git/wt" "$here/22b/.git/sub" "(null)" &&
+	try_case 22b/.git/sub unset "$here/22b/.git" \
+		"$here/22b/.git" "$here/22b/.git/wt" "$here/22b/.git/sub" "(null)" &&
+
+	try_case 22bb/.git unset . \
+		. "$here/22bb/.git/wt" "$here/22bb/.git" "(null)" &&
+	try_case 22bb/.git unset "$here/22bb/.git" \
+		"$here/22bb/.git" "$here/22bb/.git/wt" "$here/22bb/.git" "(null)" &&
+	try_case 22bb/.git/sub unset .. \
+		.. "$here/22bb/.git/wt" "$here/22bb/.git/sub" "(null)" &&
+	try_case 22bb/.git/sub unset "$here/22bb/.git" \
+		"$here/22bb/.git" "$here/22bb/.git/wt" "$here/22bb/.git/sub" "(null)"
+'
+
+test_expect_success '#22c: core.worktree = .git/.., GIT_DIR=.git' '
+	setup_repo 22c "$here/22c" "" unset &&
+	setup_repo 22cb .. "" unset &&
+	mkdir -p 22c/.git/sub 22c/sub &&
+	mkdir -p 22cb/.git/sub 22cb/sub &&
+
+	try_case 22c/.git unset . \
+		"$here/22c/.git" "$here/22c" "$here/22c" .git/ &&
+	try_case 22c/.git unset "$here/22c/.git" \
+		"$here/22c/.git" "$here/22c" "$here/22c" .git/ &&
+	try_case 22c/.git/sub unset .. \
+		"$here/22c/.git" "$here/22c" "$here/22c" .git/sub/ &&
+	try_case 22c/.git/sub unset "$here/22c/.git" \
+		"$here/22c/.git" "$here/22c" "$here/22c" .git/sub/ &&
+
+	try_case 22cb/.git unset . \
+		"$here/22cb/.git" "$here/22cb" "$here/22cb" .git/ &&
+	try_case 22cb/.git unset "$here/22cb/.git" \
+		"$here/22cb/.git" "$here/22cb" "$here/22cb" .git/ &&
+	try_case 22cb/.git/sub unset .. \
+		"$here/22cb/.git" "$here/22cb" "$here/22cb" .git/sub/ &&
+	try_case 22cb/.git/sub unset "$here/22cb/.git" \
+		"$here/22cb/.git" "$here/22cb" "$here/22cb" .git/sub/
+'
+
+test_expect_success '#22.2: core.worktree and core.bare conflict' '
+	setup_repo 22 "$here/22" "" true &&
+	(
+		cd 22/.git &&
+		GIT_DIR=. &&
+		export GIT_DIR &&
+		test_must_fail git symbolic-ref HEAD 2>result
+	) &&
+	(
+		cd 22 &&
+		GIT_DIR=.git &&
+		export GIT_DIR &&
+		test_must_fail git symbolic-ref HEAD 2>result
+	) &&
+	grep "core.bare and core.worktree" 22/.git/result &&
+	grep "core.bare and core.worktree" 22/result
+'
+
+# Case #23: GIT_DIR + GIT_WORK_TREE(+core.worktree) suppresses bareness.
+test_expect_success '#23: setup' '
+	setup_repo 23 non-existent "" true &&
+	mkdir -p 23/sub/sub 23/wt/sub
+'
+run_wt_tests 23
+
+test_expect_success '#24: bare repo has no worktree (gitfile case)' '
+	try_repo 24 unset unset unset gitfile true \
+		"$here/24.git" "(null)" "$here/24" "(null)" \
+		"$here/24.git" "(null)" "$here/24/sub" "(null)"
+'
+
+test_expect_success '#25: GIT_WORK_TREE accepted if GIT_DIR unset (bare gitfile case)' '
+	try_repo 25 "$here/25" unset unset gitfile true \
+		"$here/25.git" "$here/25" "$here/25" "(null)"  \
+		"$here/25.git" "$here/25" "$here/25" "sub/" 2>message &&
+	! test -s message
+'
+
+test_expect_success '#26: bare repo has no worktree (GIT_DIR -> gitfile case)' '
+	try_repo 26 unset "$here/26/.git" unset gitfile true \
+		"$here/26.git" "(null)" "$here/26" "(null)" \
+		"$here/26.git" "(null)" "$here/26/sub" "(null)" &&
+	try_repo 26b unset .git unset gitfile true \
+		"$here/26b.git" "(null)" "$here/26b" "(null)" \
+		"$here/26b.git" "(null)" "$here/26b/sub" "(null)"
+'
+
+# Case #27: GIT_DIR + GIT_WORK_TREE suppresses bareness (with gitfile).
+test_expect_success '#27: setup' '
+	setup_repo 27 unset gitfile true &&
+	mkdir -p 27/sub/sub 27/wt/sub
+'
+run_wt_tests 27 gitfile
+
+test_expect_success '#28: core.worktree and core.bare conflict (gitfile case)' '
+	setup_repo 28 "$here/28" gitfile true &&
+	(
+		cd 28 &&
+		test_must_fail git symbolic-ref HEAD
+	) 2>message &&
+	! grep "^warning:" message &&
+	grep "core.bare and core.worktree" message
+'
+
+# Case #29: GIT_WORK_TREE(+core.worktree) overrides core.bare (gitfile case).
+test_expect_success '#29: setup' '
+	setup_repo 29 non-existent gitfile true &&
+	mkdir -p 29/sub/sub 29/wt/sub &&
+	(
+		cd 29 &&
+		GIT_WORK_TREE="$here/29" &&
+		export GIT_WORK_TREE &&
+		git symbolic-ref HEAD >/dev/null
+	) 2>message &&
+	! test -s message
+'
+run_wt_tests 29 gitfile
+
+test_expect_success '#30: core.worktree and core.bare conflict (gitfile version)' '
+	# Just like case #22.
+	setup_repo 30 "$here/30" gitfile true &&
+	(
+		cd 30 &&
+		GIT_DIR=.git &&
+		export GIT_DIR &&
+		test_must_fail git symbolic-ref HEAD 2>result
+	) &&
+	grep "core.bare and core.worktree" 30/result
+'
+
+# Case #31: GIT_DIR + GIT_WORK_TREE(+core.worktree) suppresses
+# bareness (gitfile version).
+test_expect_success '#31: setup' '
+	setup_repo 31 non-existent gitfile true &&
+	mkdir -p 31/sub/sub 31/wt/sub
+'
+run_wt_tests 31 gitfile
+
+test_done
diff --git a/t/t1511-rev-parse-caret.sh b/t/t1511-rev-parse-caret.sh
new file mode 100755
index 0000000..eaefc77
--- /dev/null
+++ b/t/t1511-rev-parse-caret.sh
@@ -0,0 +1,73 @@
+#!/bin/sh
+
+test_description='tests for ref^{stuff}'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	echo blob >a-blob &&
+	git tag -a -m blob blob-tag `git hash-object -w a-blob` &&
+	mkdir a-tree &&
+	echo moreblobs >a-tree/another-blob &&
+	git add . &&
+	TREE_SHA1=`git write-tree` &&
+	git tag -a -m tree tree-tag "$TREE_SHA1" &&
+	git commit -m Initial &&
+	git tag -a -m commit commit-tag &&
+	git branch ref &&
+	git checkout master &&
+	echo modified >>a-blob &&
+	git add -u &&
+	git commit -m Modified
+'
+
+test_expect_success 'ref^{non-existent}' '
+	test_must_fail git rev-parse ref^{non-existent}
+'
+
+test_expect_success 'ref^{}' '
+	git rev-parse ref >expected &&
+	git rev-parse ref^{} >actual &&
+	test_cmp expected actual &&
+	git rev-parse commit-tag^{} >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'ref^{commit}' '
+	git rev-parse ref >expected &&
+	git rev-parse ref^{commit} >actual &&
+	test_cmp expected actual &&
+	git rev-parse commit-tag^{commit} >actual &&
+	test_cmp expected actual &&
+	test_must_fail git rev-parse tree-tag^{commit} &&
+	test_must_fail git rev-parse blob-tag^{commit}
+'
+
+test_expect_success 'ref^{tree}' '
+	echo $TREE_SHA1 >expected &&
+	git rev-parse ref^{tree} >actual &&
+	test_cmp expected actual &&
+	git rev-parse commit-tag^{tree} >actual &&
+	test_cmp expected actual &&
+	git rev-parse tree-tag^{tree} >actual &&
+	test_cmp expected actual &&
+	test_must_fail git rev-parse blob-tag^{tree}
+'
+
+test_expect_success 'ref^{/.}' '
+	git rev-parse master >expected &&
+	git rev-parse master^{/.} >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'ref^{/non-existent}' '
+	test_must_fail git rev-parse master^{/non-existent}
+'
+
+test_expect_success 'ref^{/Initial}' '
+	git rev-parse ref >expected &&
+	git rev-parse master^{/Initial} >actual &&
+	test_cmp expected actual
+'
+
+test_done
diff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh
new file mode 100755
index 0000000..6b3d797
--- /dev/null
+++ b/t/t1512-rev-parse-disambiguation.sh
@@ -0,0 +1,264 @@
+#!/bin/sh
+
+test_description='object name disambiguation
+
+Create blobs, trees, commits and a tag that all share the same
+prefix, and make sure "git rev-parse" can take advantage of
+type information to disambiguate short object names that are
+not necessarily unique.
+
+The final history used in the test has five commits, with the bottom
+one tagged as v1.0.0.  They all have one regular file each.
+
+  +-------------------------------------------+
+  |                                           |
+  |           .-------b3wettvi---- ad2uee     |
+  |          /                   /            |
+  |  a2onsxbvj---czy8f73t--ioiley5o           |
+  |                                           |
+  +-------------------------------------------+
+
+'
+
+. ./test-lib.sh
+
+test_expect_success 'blob and tree' '
+	test_tick &&
+	(
+		for i in 0 1 2 3 4 5 6 7 8 9
+		do
+			echo $i
+		done
+		echo
+		echo b1rwzyc3
+	) >a0blgqsjc &&
+
+	# create one blob 0000000000b36
+	git add a0blgqsjc &&
+
+	# create one tree 0000000000cdc
+	git write-tree
+'
+
+test_expect_success 'warn ambiguity when no candidate matches type hint' '
+	test_must_fail git rev-parse --verify 000000000^{commit} 2>actual &&
+	grep "short SHA1 000000000 is ambiguous" actual
+'
+
+test_expect_success 'disambiguate tree-ish' '
+	# feed tree-ish in an unambiguous way
+	git rev-parse --verify 0000000000cdc:a0blgqsjc &&
+
+	# ambiguous at the object name level, but there is only one
+	# such tree-ish (the other is a blob)
+	git rev-parse --verify 000000000:a0blgqsjc
+'
+
+test_expect_success 'disambiguate blob' '
+	sed -e "s/|$//" >patch <<-EOF &&
+	diff --git a/frotz b/frotz
+	index 000000000..ffffff 100644
+	--- a/frotz
+	+++ b/frotz
+	@@ -10,3 +10,4 @@
+	 9
+	 |
+	 b1rwzyc3
+	+irwry
+	EOF
+	(
+		GIT_INDEX_FILE=frotz &&
+		export GIT_INDEX_FILE &&
+		git apply --build-fake-ancestor frotz patch &&
+		git cat-file blob :frotz >actual
+	) &&
+	test_cmp a0blgqsjc actual
+'
+
+test_expect_success 'disambiguate tree' '
+	commit=$(echo "d7xm" | git commit-tree 000000000) &&
+	test $(git rev-parse $commit^{tree}) = $(git rev-parse 0000000000cdc)
+'
+
+test_expect_success 'first commit' '
+	# create one commit 0000000000e4f
+	git commit -m a2onsxbvj
+'
+
+test_expect_success 'disambiguate commit-ish' '
+	# feed commit-ish in an unambiguous way
+	git rev-parse --verify 0000000000e4f^{commit} &&
+
+	# ambiguous at the object name level, but there is only one
+	# such commit (the others are tree and blob)
+	git rev-parse --verify 000000000^{commit} &&
+
+	# likewise
+	git rev-parse --verify 000000000^0
+'
+
+test_expect_success 'disambiguate commit' '
+	commit=$(echo "hoaxj" | git commit-tree 0000000000cdc -p 000000000) &&
+	test $(git rev-parse $commit^) = $(git rev-parse 0000000000e4f)
+'
+
+test_expect_success 'log name1..name2 takes only commit-ishes on both ends' '
+	git log 000000000..000000000 &&
+	git log ..000000000 &&
+	git log 000000000.. &&
+	git log 000000000...000000000 &&
+	git log ...000000000 &&
+	git log 000000000...
+'
+
+test_expect_success 'rev-parse name1..name2 takes only commit-ishes on both ends' '
+	git rev-parse 000000000..000000000 &&
+	git rev-parse ..000000000 &&
+	git rev-parse 000000000..
+'
+
+test_expect_success 'git log takes only commit-ish' '
+	git log 000000000
+'
+
+test_expect_success 'git reset takes only commit-ish' '
+	git reset 000000000
+'
+
+test_expect_success 'first tag' '
+	# create one tag 0000000000f8f
+	git tag -a -m j7cp83um v1.0.0
+'
+
+test_expect_failure 'two semi-ambiguous commit-ish' '
+	# Once the parser becomes ultra-smart, it could notice that
+	# 110282 before ^{commit} name many different objects, but
+	# that only two (HEAD and v1.0.0 tag) can be peeled to commit,
+	# and that peeling them down to commit yield the same commit
+	# without ambiguity.
+	git rev-parse --verify 110282^{commit} &&
+
+	# likewise
+	git log 000000000..000000000 &&
+	git log ..000000000 &&
+	git log 000000000.. &&
+	git log 000000000...000000000 &&
+	git log ...000000000 &&
+	git log 000000000...
+'
+
+test_expect_failure 'three semi-ambiguous tree-ish' '
+	# Likewise for tree-ish.  HEAD, v1.0.0 and HEAD^{tree} share
+	# the prefix but peeling them to tree yields the same thing
+	git rev-parse --verify 000000000^{tree}
+'
+
+test_expect_success 'parse describe name' '
+	# feed an unambiguous describe name
+	git rev-parse --verify v1.0.0-0-g0000000000e4f &&
+
+	# ambiguous at the object name level, but there is only one
+	# such commit (others are blob, tree and tag)
+	git rev-parse --verify v1.0.0-0-g000000000
+'
+
+test_expect_success 'more history' '
+	# commit 0000000000043
+	git mv a0blgqsjc d12cr3h8t &&
+	echo h62xsjeu >>d12cr3h8t &&
+	git add d12cr3h8t &&
+
+	test_tick &&
+	git commit -m czy8f73t &&
+
+	# commit 00000000008ec
+	git mv d12cr3h8t j000jmpzn &&
+	echo j08bekfvt >>j000jmpzn &&
+	git add j000jmpzn &&
+
+	test_tick &&
+	git commit -m ioiley5o &&
+
+	# commit 0000000005b0
+	git checkout v1.0.0^0 &&
+	git mv a0blgqsjc f5518nwu &&
+
+	for i in h62xsjeu j08bekfvt kg7xflhm
+	do
+		echo $i
+	done >>f5518nwu &&
+	git add f5518nwu &&
+
+	test_tick &&
+	git commit -m b3wettvi &&
+	side=$(git rev-parse HEAD) &&
+
+	# commit 000000000066
+	git checkout master &&
+
+	# If you use recursive, merge will fail and you will need to
+	# clean up a0blgqsjc as well.  If you use resolve, merge will
+	# succeed.
+	test_might_fail git merge --no-commit -s recursive $side &&
+	git rm -f f5518nwu j000jmpzn &&
+
+	test_might_fail git rm -f a0blgqsjc &&
+	(
+		git cat-file blob $side:f5518nwu
+		echo j3l0i9s6
+	) >ab2gs879 &&
+	git add ab2gs879 &&
+
+	test_tick &&
+	git commit -m ad2uee
+
+'
+
+test_expect_failure 'parse describe name taking advantage of generation' '
+	# ambiguous at the object name level, but there is only one
+	# such commit at generation 0
+	git rev-parse --verify v1.0.0-0-g000000000 &&
+
+	# likewise for generation 2 and 4
+	git rev-parse --verify v1.0.0-2-g000000000 &&
+	git rev-parse --verify v1.0.0-4-g000000000
+'
+
+# Note: because rev-parse does not even try to disambiguate based on
+# the generation number, this test currently succeeds for a wrong
+# reason.  When it learns to use the generation number, the previous
+# test should succeed, and also this test should fail because the
+# describe name used in the test with generation number can name two
+# commits.  Make sure that such a future enhancement does not randomly
+# pick one.
+test_expect_success 'parse describe name not ignoring ambiguity' '
+	# ambiguous at the object name level, and there are two such
+	# commits at generation 1
+	test_must_fail git rev-parse --verify v1.0.0-1-g000000000
+'
+
+test_expect_success 'ambiguous commit-ish' '
+	# Now there are many commits that begin with the
+	# common prefix, none of these should pick one at
+	# random.  They all should result in ambiguity errors.
+	test_must_fail git rev-parse --verify 110282^{commit} &&
+
+	# likewise
+	test_must_fail git log 000000000..000000000 &&
+	test_must_fail git log ..000000000 &&
+	test_must_fail git log 000000000.. &&
+	test_must_fail git log 000000000...000000000 &&
+	test_must_fail git log ...000000000 &&
+	test_must_fail git log 000000000...
+'
+
+test_expect_success 'rev-parse --disambiguate' '
+	# The test creates 16 objects that share the prefix and two
+	# commits created by commit-tree in earlier tests share a
+	# different prefix.
+	git rev-parse --disambiguate=000000000 >actual &&
+	test $(wc -l <actual) = 16 &&
+	test "$(sed -e "s/^\(.........\).*/\1/" actual | sort -u)" = 000000000
+'
+
+test_done
diff --git a/t/t2000-checkout-cache-clash.sh b/t/t2000-checkout-cache-clash.sh
index f7e1a73..de3edb5 100755
--- a/t/t2000-checkout-cache-clash.sh
+++ b/t/t2000-checkout-cache-clash.sh
@@ -48,4 +48,13 @@
     'git checkout-index conflicting paths.' \
     'test -f path0 && test -d path1 && test -f path1/file1'
 
+test_expect_success SYMLINKS 'checkout-index -f twice with --prefix' '
+	mkdir -p tar/get &&
+	ln -s tar/get there &&
+	echo first &&
+	git checkout-index -a -f --prefix=there/ &&
+	echo second &&
+	git checkout-index -a -f --prefix=there/
+'
+
 test_done
diff --git a/t/t2004-checkout-cache-temp.sh b/t/t2004-checkout-cache-temp.sh
index 36cca14..0f4b289 100755
--- a/t/t2004-checkout-cache-temp.sh
+++ b/t/t2004-checkout-cache-temp.sh
@@ -40,7 +40,7 @@
 rm -f path* .merge_* out .git/index &&
 git read-tree $t1 &&
 git checkout-index --temp -- path1 >out &&
-test $(wc -l <out) = 1 &&
+test_line_count = 1 out &&
 test $(cut "-d	" -f2 out) = path1 &&
 p=$(cut "-d	" -f1 out) &&
 test -f $p &&
@@ -51,7 +51,7 @@
 rm -f path* .merge_* out .git/index &&
 git read-tree $t1 &&
 git checkout-index -a --temp >out &&
-test $(wc -l <out) = 5 &&
+test_line_count = 5 out &&
 for f in path0 path1 path3 path4 asubdir/path5
 do
 	test $(grep $f out | cut "-d	" -f2) = $f &&
@@ -69,7 +69,7 @@
 'checkout one stage 2 to temporary file' '
 rm -f path* .merge_* out &&
 git checkout-index --stage=2 --temp -- path1 >out &&
-test $(wc -l <out) = 1 &&
+test_line_count = 1 out &&
 test $(cut "-d	" -f2 out) = path1 &&
 p=$(cut "-d	" -f1 out) &&
 test -f $p &&
@@ -79,7 +79,7 @@
 'checkout all stage 2 to temporary files' '
 rm -f path* .merge_* out &&
 git checkout-index --all --stage=2 --temp >out &&
-test $(wc -l <out) = 3 &&
+test_line_count = 3 out &&
 for f in path1 path2 path4
 do
 	test $(grep $f out | cut "-d	" -f2) = $f &&
@@ -92,13 +92,13 @@
 'checkout all stages/one file to nothing' '
 rm -f path* .merge_* out &&
 git checkout-index --stage=all --temp -- path0 >out &&
-test $(wc -l <out) = 0'
+test_line_count = 0 out'
 
 test_expect_success \
 'checkout all stages/one file to temporary files' '
 rm -f path* .merge_* out &&
 git checkout-index --stage=all --temp -- path1 >out &&
-test $(wc -l <out) = 1 &&
+test_line_count = 1 out &&
 test $(cut "-d	" -f2 out) = path1 &&
 cut "-d	" -f1 out | (read s1 s2 s3 &&
 test -f $s1 &&
@@ -112,7 +112,7 @@
 'checkout some stages/one file to temporary files' '
 rm -f path* .merge_* out &&
 git checkout-index --stage=all --temp -- path2 >out &&
-test $(wc -l <out) = 1 &&
+test_line_count = 1 out &&
 test $(cut "-d	" -f2 out) = path2 &&
 cut "-d	" -f1 out | (read s1 s2 s3 &&
 test $s1 = . &&
@@ -125,7 +125,7 @@
 'checkout all stages/all files to temporary files' '
 rm -f path* .merge_* out &&
 git checkout-index -a --stage=all --temp >out &&
-test $(wc -l <out) = 5'
+test_line_count = 5 out'
 
 test_expect_success \
 '-- path0: no entry' '
@@ -185,7 +185,7 @@
 'checkout --temp within subdir' '
 (cd asubdir &&
  git checkout-index -a --stage=all >out &&
- test $(wc -l <out) = 1 &&
+ test_line_count = 1 out &&
  test $(grep path5 out | cut "-d	" -f2) = path5 &&
  grep path5 out | cut "-d	" -f1 | (read s1 s2 s3 &&
  test -f ../$s1 &&
@@ -203,7 +203,7 @@
 rm -f .git/index &&
 git read-tree $t4 &&
 git checkout-index --temp -a >out &&
-test $(wc -l <out) = 1 &&
+test_line_count = 1 out &&
 test $(cut "-d	" -f2 out) = a &&
 p=$(cut "-d	" -f1 out) &&
 test -f $p &&
diff --git a/t/t2006-checkout-index-basic.sh b/t/t2006-checkout-index-basic.sh
new file mode 100755
index 0000000..b855983
--- /dev/null
+++ b/t/t2006-checkout-index-basic.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+test_description='basic checkout-index tests
+'
+
+. ./test-lib.sh
+
+test_expect_success 'checkout-index --gobbledegook' '
+	test_expect_code 129 git checkout-index --gobbledegook 2>err &&
+	grep "[Uu]sage" err
+'
+
+test_expect_success 'checkout-index -h in broken repository' '
+	mkdir broken &&
+	(
+		cd broken &&
+		git init &&
+		>.git/index &&
+		test_expect_code 129 git checkout-index -h >usage 2>&1
+	) &&
+	grep "[Uu]sage" broken/usage
+'
+
+test_done
diff --git a/t/t2007-checkout-symlink.sh b/t/t2007-checkout-symlink.sh
index 20f3343..e6f59f1 100755
--- a/t/t2007-checkout-symlink.sh
+++ b/t/t2007-checkout-symlink.sh
@@ -6,13 +6,7 @@
 
 . ./test-lib.sh
 
-if ! test_have_prereq SYMLINKS
-then
-	say "symbolic links not supported - skipping tests"
-	test_done
-fi
-
-test_expect_success setup '
+test_expect_success SYMLINKS setup '
 
 	mkdir frotz &&
 	echo hello >frotz/filfre &&
@@ -23,7 +17,7 @@
 	git branch side &&
 
 	echo goodbye >nitfol &&
-	git add nitfol
+	git add nitfol &&
 	test_tick &&
 	git commit -m "master adds file nitfol" &&
 
@@ -38,16 +32,18 @@
 
 '
 
-test_expect_success 'switch from symlink to dir' '
+test_expect_success SYMLINKS 'switch from symlink to dir' '
 
 	git checkout master
 
 '
 
-rm -fr frotz xyzzy nitfol &&
-git checkout -f master || exit
+test_expect_success SYMLINKS 'Remove temporary directories & switch to master' '
+	rm -fr frotz xyzzy nitfol &&
+	git checkout -f master
+'
 
-test_expect_success 'switch from dir to symlink' '
+test_expect_success SYMLINKS 'switch from dir to symlink' '
 
 	git checkout side
 
diff --git a/t/t2011-checkout-invalid-head.sh b/t/t2011-checkout-invalid-head.sh
index 15ebdc2..300f8bf 100755
--- a/t/t2011-checkout-invalid-head.sh
+++ b/t/t2011-checkout-invalid-head.sh
@@ -15,7 +15,7 @@
 '
 
 test_expect_success 'checkout master from invalid HEAD' '
-	echo 0000000000000000000000000000000000000000 >.git/HEAD &&
+	echo $_z40 >.git/HEAD &&
 	git checkout master --
 '
 
diff --git a/t/t2012-checkout-last.sh b/t/t2012-checkout-last.sh
index 87b30a2..b44de9d 100755
--- a/t/t2012-checkout-last.sh
+++ b/t/t2012-checkout-last.sh
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-test_description='checkout can switch to last branch'
+test_description='checkout can switch to last branch and merge base'
 
 . ./test-lib.sh
 
@@ -91,4 +91,29 @@
 	test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch13"
 '
 
+test_expect_success 'merge base test setup' '
+	git checkout -b another other &&
+	echo "hello again" >>world &&
+	git add world &&
+	git commit -m third
+'
+
+test_expect_success 'another...master' '
+	git checkout another &&
+	git checkout another...master &&
+	test "z$(git rev-parse --verify HEAD)" = "z$(git rev-parse --verify master^)"
+'
+
+test_expect_success '...master' '
+	git checkout another &&
+	git checkout ...master &&
+	test "z$(git rev-parse --verify HEAD)" = "z$(git rev-parse --verify master^)"
+'
+
+test_expect_success 'master...' '
+	git checkout another &&
+	git checkout master... &&
+	test "z$(git rev-parse --verify HEAD)" = "z$(git rev-parse --verify master^)"
+'
+
 test_done
diff --git a/t/t2013-checkout-submodule.sh b/t/t2013-checkout-submodule.sh
index fda3f0a..70edbb3 100755
--- a/t/t2013-checkout-submodule.sh
+++ b/t/t2013-checkout-submodule.sh
@@ -39,4 +39,27 @@
 	git diff-files --quiet
 '
 
+test_expect_success '"checkout <submodule>" honors diff.ignoreSubmodules' '
+	git config diff.ignoreSubmodules dirty &&
+	echo x> submodule/untracked &&
+	git checkout HEAD >actual 2>&1 &&
+	! test -s actual
+'
+
+test_expect_success '"checkout <submodule>" honors submodule.*.ignore from .gitmodules' '
+	git config diff.ignoreSubmodules none &&
+	git config -f .gitmodules submodule.submodule.path submodule &&
+	git config -f .gitmodules submodule.submodule.ignore untracked &&
+	git checkout HEAD >actual 2>&1 &&
+	! test -s actual
+'
+
+test_expect_success '"checkout <submodule>" honors submodule.*.ignore from .git/config' '
+	git config -f .gitmodules submodule.submodule.ignore none &&
+	git config submodule.submodule.path submodule &&
+	git config submodule.submodule.ignore all &&
+	git checkout HEAD >actual 2>&1 &&
+	! test -s actual
+'
+
 test_done
diff --git a/t/t2015-checkout-unborn.sh b/t/t2015-checkout-unborn.sh
new file mode 100755
index 0000000..37bdced
--- /dev/null
+++ b/t/t2015-checkout-unborn.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+test_description='checkout from unborn branch'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	mkdir parent &&
+	(cd parent &&
+	 git init &&
+	 echo content >file &&
+	 git add file &&
+	 git commit -m base
+	) &&
+	git fetch parent master:origin
+'
+
+test_expect_success 'checkout from unborn preserves untracked files' '
+	echo precious >expect &&
+	echo precious >file &&
+	test_must_fail git checkout -b new origin &&
+	test_cmp expect file
+'
+
+test_expect_success 'checkout from unborn preserves index contents' '
+	echo precious >expect &&
+	echo precious >file &&
+	git add file &&
+	test_must_fail git checkout -b new origin &&
+	test_cmp expect file &&
+	git show :file >file &&
+	test_cmp expect file
+'
+
+test_expect_success 'checkout from unborn merges identical index contents' '
+	echo content >file &&
+	git add file &&
+	git checkout -b new origin
+'
+
+test_expect_success 'checking out another branch from unborn state' '
+	git checkout --orphan newroot &&
+	git checkout -b anothername &&
+	test_must_fail git show-ref --verify refs/heads/newroot &&
+	git symbolic-ref HEAD >actual &&
+	echo refs/heads/anothername >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'checking out in a newly created repo' '
+	test_create_repo empty &&
+	(
+		cd empty &&
+		git symbolic-ref HEAD >expect &&
+		test_must_fail git checkout &&
+		git symbolic-ref HEAD >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_done
diff --git a/t/t2016-checkout-patch.sh b/t/t2016-checkout-patch.sh
new file mode 100755
index 0000000..9cd0ac4
--- /dev/null
+++ b/t/t2016-checkout-patch.sh
@@ -0,0 +1,115 @@
+#!/bin/sh
+
+test_description='git checkout --patch'
+
+. ./lib-patch-mode.sh
+
+test_expect_success PERL 'setup' '
+	mkdir dir &&
+	echo parent > dir/foo &&
+	echo dummy > bar &&
+	git add bar dir/foo &&
+	git commit -m initial &&
+	test_tick &&
+	test_commit second dir/foo head &&
+	set_and_save_state bar bar_work bar_index &&
+	save_head
+'
+
+# note: bar sorts before dir/foo, so the first 'n' is always to skip 'bar'
+
+test_expect_success PERL 'saying "n" does nothing' '
+	set_and_save_state dir/foo work head &&
+	(echo n; echo n) | git checkout -p &&
+	verify_saved_state bar &&
+	verify_saved_state dir/foo
+'
+
+test_expect_success PERL 'git checkout -p' '
+	(echo n; echo y) | git checkout -p &&
+	verify_saved_state bar &&
+	verify_state dir/foo head head
+'
+
+test_expect_success PERL 'git checkout -p with staged changes' '
+	set_state dir/foo work index &&
+	(echo n; echo y) | git checkout -p &&
+	verify_saved_state bar &&
+	verify_state dir/foo index index
+'
+
+test_expect_success PERL 'git checkout -p HEAD with NO staged changes: abort' '
+	set_and_save_state dir/foo work head &&
+	(echo n; echo y; echo n) | git checkout -p HEAD &&
+	verify_saved_state bar &&
+	verify_saved_state dir/foo
+'
+
+test_expect_success PERL 'git checkout -p HEAD with NO staged changes: apply' '
+	(echo n; echo y; echo y) | git checkout -p HEAD &&
+	verify_saved_state bar &&
+	verify_state dir/foo head head
+'
+
+test_expect_success PERL 'git checkout -p HEAD with change already staged' '
+	set_state dir/foo index index &&
+	# the third n is to get out in case it mistakenly does not apply
+	(echo n; echo y; echo n) | git checkout -p HEAD &&
+	verify_saved_state bar &&
+	verify_state dir/foo head head
+'
+
+test_expect_success PERL 'git checkout -p HEAD^' '
+	# the third n is to get out in case it mistakenly does not apply
+	(echo n; echo y; echo n) | git checkout -p HEAD^ &&
+	verify_saved_state bar &&
+	verify_state dir/foo parent parent
+'
+
+test_expect_success PERL 'git checkout -p handles deletion' '
+	set_state dir/foo work index &&
+	rm dir/foo &&
+	(echo n; echo y) | git checkout -p &&
+	verify_saved_state bar &&
+	verify_state dir/foo index index
+'
+
+# The idea in the rest is that bar sorts first, so we always say 'y'
+# first and if the path limiter fails it'll apply to bar instead of
+# dir/foo.  There's always an extra 'n' to reject edits to dir/foo in
+# the failure case (and thus get out of the loop).
+
+test_expect_success PERL 'path limiting works: dir' '
+	set_state dir/foo work head &&
+	(echo y; echo n) | git checkout -p dir &&
+	verify_saved_state bar &&
+	verify_state dir/foo head head
+'
+
+test_expect_success PERL 'path limiting works: -- dir' '
+	set_state dir/foo work head &&
+	(echo y; echo n) | git checkout -p -- dir &&
+	verify_saved_state bar &&
+	verify_state dir/foo head head
+'
+
+test_expect_success PERL 'path limiting works: HEAD^ -- dir' '
+	# the third n is to get out in case it mistakenly does not apply
+	(echo y; echo n; echo n) | git checkout -p HEAD^ -- dir &&
+	verify_saved_state bar &&
+	verify_state dir/foo parent parent
+'
+
+test_expect_success PERL 'path limiting works: foo inside dir' '
+	set_state dir/foo work head &&
+	# the third n is to get out in case it mistakenly does not apply
+	(echo y; echo n; echo n) | (cd dir && git checkout -p foo) &&
+	verify_saved_state bar &&
+	verify_state dir/foo head head
+'
+
+test_expect_success PERL 'none of this moved HEAD' '
+	verify_saved_head
+'
+
+test_done
diff --git a/t/t2017-checkout-orphan.sh b/t/t2017-checkout-orphan.sh
new file mode 100755
index 0000000..655f278
--- /dev/null
+++ b/t/t2017-checkout-orphan.sh
@@ -0,0 +1,125 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Erick Mattos
+#
+
+test_description='git checkout --orphan
+
+Main Tests for --orphan functionality.'
+
+. ./test-lib.sh
+
+TEST_FILE=foo
+
+test_expect_success 'Setup' '
+	echo "Initial" >"$TEST_FILE" &&
+	git add "$TEST_FILE" &&
+	git commit -m "First Commit" &&
+	test_tick &&
+	echo "State 1" >>"$TEST_FILE" &&
+	git add "$TEST_FILE" &&
+	test_tick &&
+	git commit -m "Second Commit"
+'
+
+test_expect_success '--orphan creates a new orphan branch from HEAD' '
+	git checkout --orphan alpha &&
+	test_must_fail git rev-parse --verify HEAD &&
+	test "refs/heads/alpha" = "$(git symbolic-ref HEAD)" &&
+	test_tick &&
+	git commit -m "Third Commit" &&
+	test_must_fail git rev-parse --verify HEAD^ &&
+	git diff-tree --quiet master alpha
+'
+
+test_expect_success '--orphan creates a new orphan branch from <start_point>' '
+	git checkout master &&
+	git checkout --orphan beta master^ &&
+	test_must_fail git rev-parse --verify HEAD &&
+	test "refs/heads/beta" = "$(git symbolic-ref HEAD)" &&
+	test_tick &&
+	git commit -m "Fourth Commit" &&
+	test_must_fail git rev-parse --verify HEAD^ &&
+	git diff-tree --quiet master^ beta
+'
+
+test_expect_success '--orphan must be rejected with -b' '
+	git checkout master &&
+	test_must_fail git checkout --orphan new -b newer &&
+	test refs/heads/master = "$(git symbolic-ref HEAD)"
+'
+
+test_expect_success '--orphan must be rejected with -t' '
+	git checkout master &&
+	test_must_fail git checkout --orphan new -t master &&
+	test refs/heads/master = "$(git symbolic-ref HEAD)"
+'
+
+test_expect_success '--orphan ignores branch.autosetupmerge' '
+	git checkout master &&
+	git config branch.autosetupmerge always &&
+	git checkout --orphan gamma &&
+	test -z "$(git config branch.gamma.merge)" &&
+	test refs/heads/gamma = "$(git symbolic-ref HEAD)" &&
+	test_must_fail git rev-parse --verify HEAD^
+'
+
+test_expect_success '--orphan makes reflog by default' '
+	git checkout master &&
+	git config --unset core.logAllRefUpdates &&
+	git checkout --orphan delta &&
+	test_must_fail git rev-parse --verify delta@{0} &&
+	git commit -m Delta &&
+	git rev-parse --verify delta@{0}
+'
+
+test_expect_success '--orphan does not make reflog when core.logAllRefUpdates = false' '
+	git checkout master &&
+	git config core.logAllRefUpdates false &&
+	git checkout --orphan epsilon &&
+	test_must_fail git rev-parse --verify epsilon@{0} &&
+	git commit -m Epsilon &&
+	test_must_fail git rev-parse --verify epsilon@{0}
+'
+
+test_expect_success '--orphan with -l makes reflog when core.logAllRefUpdates = false' '
+	git checkout master &&
+	git checkout -l --orphan zeta &&
+	test_must_fail git rev-parse --verify zeta@{0} &&
+	git commit -m Zeta &&
+	git rev-parse --verify zeta@{0}
+'
+
+test_expect_success 'giving up --orphan not committed when -l and core.logAllRefUpdates = false deletes reflog' '
+	git checkout master &&
+	git checkout -l --orphan eta &&
+	test_must_fail git rev-parse --verify eta@{0} &&
+	git checkout master &&
+	test_must_fail git rev-parse --verify eta@{0}
+'
+
+test_expect_success '--orphan is rejected with an existing name' '
+	git checkout master &&
+	test_must_fail git checkout --orphan master &&
+	test refs/heads/master = "$(git symbolic-ref HEAD)"
+'
+
+test_expect_success '--orphan refuses to switch if a merge is needed' '
+	git checkout master &&
+	git reset --hard &&
+	echo local >>"$TEST_FILE" &&
+	cat "$TEST_FILE" >"$TEST_FILE.saved" &&
+	test_must_fail git checkout --orphan new master^ &&
+	test refs/heads/master = "$(git symbolic-ref HEAD)" &&
+	test_cmp "$TEST_FILE" "$TEST_FILE.saved" &&
+	git diff-index --quiet --cached HEAD &&
+	git reset --hard
+'
+
+test_expect_success 'cannot --detach on an unborn branch' '
+	git checkout master &&
+	git checkout --orphan new &&
+	test_must_fail git checkout --detach
+'
+
+test_done
diff --git a/t/t2018-checkout-branch.sh b/t/t2018-checkout-branch.sh
new file mode 100755
index 0000000..2741262
--- /dev/null
+++ b/t/t2018-checkout-branch.sh
@@ -0,0 +1,201 @@
+#!/bin/sh
+
+test_description='checkout '
+
+. ./test-lib.sh
+
+# Arguments: <branch> <sha> [<checkout options>]
+#
+# Runs "git checkout" to switch to <branch>, testing that
+#
+#   1) we are on the specified branch, <branch>;
+#   2) HEAD is <sha>; if <sha> is not specified, the old HEAD is used.
+#
+# If <checkout options> is not specified, "git checkout" is run with -b.
+do_checkout() {
+	exp_branch=$1 &&
+	exp_ref="refs/heads/$exp_branch" &&
+
+	# if <sha> is not specified, use HEAD.
+	exp_sha=${2:-$(git rev-parse --verify HEAD)} &&
+
+	# default options for git checkout: -b
+	if [ -z "$3" ]; then
+		opts="-b"
+	else
+		opts="$3"
+	fi
+
+	git checkout $opts $exp_branch $exp_sha &&
+
+	test $exp_ref = $(git rev-parse --symbolic-full-name HEAD) &&
+	test $exp_sha = $(git rev-parse --verify HEAD)
+}
+
+test_dirty_unmergeable() {
+	! git diff --exit-code >/dev/null
+}
+
+setup_dirty_unmergeable() {
+	echo >>file1 change2
+}
+
+test_dirty_mergeable() {
+	! git diff --cached --exit-code >/dev/null
+}
+
+setup_dirty_mergeable() {
+	echo >file2 file2 &&
+	git add file2
+}
+
+test_expect_success 'setup' '
+	test_commit initial file1 &&
+	HEAD1=$(git rev-parse --verify HEAD) &&
+
+	test_commit change1 file1 &&
+	HEAD2=$(git rev-parse --verify HEAD) &&
+
+	git branch -m branch1
+'
+
+test_expect_success 'checkout -b to a new branch, set to HEAD' '
+	do_checkout branch2
+'
+
+test_expect_success 'checkout -b to a new branch, set to an explicit ref' '
+	git checkout branch1 &&
+	git branch -D branch2 &&
+
+	do_checkout branch2 $HEAD1
+'
+
+test_expect_success 'checkout -b to a new branch with unmergeable changes fails' '
+	git checkout branch1 &&
+
+	# clean up from previous test
+	git branch -D branch2 &&
+
+	setup_dirty_unmergeable &&
+	test_must_fail do_checkout branch2 $HEAD1 &&
+	test_dirty_unmergeable
+'
+
+test_expect_success 'checkout -f -b to a new branch with unmergeable changes discards changes' '
+	# still dirty and on branch1
+	do_checkout branch2 $HEAD1 "-f -b" &&
+	test_must_fail test_dirty_unmergeable
+'
+
+test_expect_success 'checkout -b to a new branch preserves mergeable changes' '
+	git checkout branch1 &&
+
+	# clean up from previous test
+	git branch -D branch2 &&
+
+	setup_dirty_mergeable &&
+	do_checkout branch2 $HEAD1 &&
+	test_dirty_mergeable
+'
+
+test_expect_success 'checkout -f -b to a new branch with mergeable changes discards changes' '
+	# clean up from previous test
+	git reset --hard &&
+
+	git checkout branch1 &&
+
+	# clean up from previous test
+	git branch -D branch2 &&
+
+	setup_dirty_mergeable &&
+	do_checkout branch2 $HEAD1 "-f -b" &&
+	test_must_fail test_dirty_mergeable
+'
+
+test_expect_success 'checkout -b to an existing branch fails' '
+	git reset --hard HEAD &&
+
+	test_must_fail do_checkout branch2 $HEAD2
+'
+
+test_expect_success 'checkout -b to @{-1} fails with the right branch name' '
+	git reset --hard HEAD &&
+	git checkout branch1 &&
+	git checkout branch2 &&
+	echo  >expect "fatal: A branch named '\''branch1'\'' already exists." &&
+	test_must_fail git checkout -b @{-1} 2>actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'checkout -B to an existing branch resets branch to HEAD' '
+	git checkout branch1 &&
+
+	do_checkout branch2 "" -B
+'
+
+test_expect_success 'checkout -B to an existing branch from detached HEAD resets branch to HEAD' '
+	git checkout $(git rev-parse --verify HEAD) &&
+
+	do_checkout branch2 "" -B
+'
+
+test_expect_success 'checkout -B to an existing branch with an explicit ref resets branch to that ref' '
+	git checkout branch1 &&
+
+	do_checkout branch2 $HEAD1 -B
+'
+
+test_expect_success 'checkout -B to an existing branch with unmergeable changes fails' '
+	git checkout branch1 &&
+
+	setup_dirty_unmergeable &&
+	test_must_fail do_checkout branch2 $HEAD1 -B &&
+	test_dirty_unmergeable
+'
+
+test_expect_success 'checkout -f -B to an existing branch with unmergeable changes discards changes' '
+	# still dirty and on branch1
+	do_checkout branch2 $HEAD1 "-f -B" &&
+	test_must_fail test_dirty_unmergeable
+'
+
+test_expect_success 'checkout -B to an existing branch preserves mergeable changes' '
+	git checkout branch1 &&
+
+	setup_dirty_mergeable &&
+	do_checkout branch2 $HEAD1 -B &&
+	test_dirty_mergeable
+'
+
+test_expect_success 'checkout -f -B to an existing branch with mergeable changes discards changes' '
+	# clean up from previous test
+	git reset --hard &&
+
+	git checkout branch1 &&
+
+	setup_dirty_mergeable &&
+	do_checkout branch2 $HEAD1 "-f -B" &&
+	test_must_fail test_dirty_mergeable
+'
+
+test_expect_success 'checkout -b <describe>' '
+	git tag -f -m "First commit" initial initial &&
+	git checkout -f change1 &&
+	name=$(git describe) &&
+	git checkout -b $name &&
+	git diff --exit-code change1 &&
+	echo "refs/heads/$name" >expect &&
+	git symbolic-ref HEAD >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'checkout -B to the current branch works' '
+	git checkout branch1 &&
+	git checkout -B branch1-scratch &&
+
+	setup_dirty_mergeable &&
+	git checkout -B branch1-scratch initial &&
+	test_dirty_mergeable
+'
+
+test_done
diff --git a/t/t2019-checkout-ambiguous-ref.sh b/t/t2019-checkout-ambiguous-ref.sh
new file mode 100755
index 0000000..b99d519
--- /dev/null
+++ b/t/t2019-checkout-ambiguous-ref.sh
@@ -0,0 +1,59 @@
+#!/bin/sh
+
+test_description='checkout handling of ambiguous (branch/tag) refs'
+. ./test-lib.sh
+
+test_expect_success 'setup ambiguous refs' '
+	test_commit branch file &&
+	git branch ambiguity &&
+	git branch vagueness &&
+	test_commit tag file &&
+	git tag ambiguity &&
+	git tag vagueness HEAD:file &&
+	test_commit other file
+'
+
+test_expect_success 'checkout ambiguous ref succeeds' '
+	git checkout ambiguity >stdout 2>stderr
+'
+
+test_expect_success 'checkout produces ambiguity warning' '
+	grep "warning.*ambiguous" stderr
+'
+
+test_expect_success 'checkout chooses branch over tag' '
+	echo refs/heads/ambiguity >expect &&
+	git symbolic-ref HEAD >actual &&
+	test_cmp expect actual &&
+	echo branch >expect &&
+	test_cmp expect file
+'
+
+test_expect_success 'checkout reports switch to branch' '
+	test_i18ngrep "Switched to branch" stderr &&
+	test_i18ngrep ! "^HEAD is now at" stderr
+'
+
+test_expect_success 'checkout vague ref succeeds' '
+	git checkout vagueness >stdout 2>stderr &&
+	test_set_prereq VAGUENESS_SUCCESS
+'
+
+test_expect_success VAGUENESS_SUCCESS 'checkout produces ambiguity warning' '
+	grep "warning.*ambiguous" stderr
+'
+
+test_expect_success VAGUENESS_SUCCESS 'checkout chooses branch over tag' '
+	echo refs/heads/vagueness >expect &&
+	git symbolic-ref HEAD >actual &&
+	test_cmp expect actual &&
+	echo branch >expect &&
+	test_cmp expect file
+'
+
+test_expect_success VAGUENESS_SUCCESS 'checkout reports switch to branch' '
+	test_i18ngrep "Switched to branch" stderr &&
+	test_i18ngrep ! "^HEAD is now at" stderr
+'
+
+test_done
diff --git a/t/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh
new file mode 100755
index 0000000..8100537
--- /dev/null
+++ b/t/t2020-checkout-detach.sh
@@ -0,0 +1,165 @@
+#!/bin/sh
+
+test_description='checkout into detached HEAD state'
+. ./test-lib.sh
+
+check_detached () {
+	test_must_fail git symbolic-ref -q HEAD >/dev/null
+}
+
+check_not_detached () {
+	git symbolic-ref -q HEAD >/dev/null
+}
+
+PREV_HEAD_DESC='Previous HEAD position was'
+check_orphan_warning() {
+	test_i18ngrep "you are leaving $2 behind" "$1" &&
+	test_i18ngrep ! "$PREV_HEAD_DESC" "$1"
+}
+check_no_orphan_warning() {
+	test_i18ngrep ! "you are leaving .* commit.*behind" "$1" &&
+	test_i18ngrep "$PREV_HEAD_DESC" "$1"
+}
+
+reset () {
+	git checkout master &&
+	check_not_detached
+}
+
+test_expect_success 'setup' '
+	test_commit one &&
+	test_commit two &&
+	test_commit three && git tag -d three &&
+	test_commit four && git tag -d four &&
+	git branch branch &&
+	git tag tag
+'
+
+test_expect_success 'checkout branch does not detach' '
+	reset &&
+	git checkout branch &&
+	check_not_detached
+'
+
+test_expect_success 'checkout tag detaches' '
+	reset &&
+	git checkout tag &&
+	check_detached
+'
+
+test_expect_success 'checkout branch by full name detaches' '
+	reset &&
+	git checkout refs/heads/branch &&
+	check_detached
+'
+
+test_expect_success 'checkout non-ref detaches' '
+	reset &&
+	git checkout branch^ &&
+	check_detached
+'
+
+test_expect_success 'checkout ref^0 detaches' '
+	reset &&
+	git checkout branch^0 &&
+	check_detached
+'
+
+test_expect_success 'checkout --detach detaches' '
+	reset &&
+	git checkout --detach branch &&
+	check_detached
+'
+
+test_expect_success 'checkout --detach without branch name' '
+	reset &&
+	git checkout --detach &&
+	check_detached
+'
+
+test_expect_success 'checkout --detach errors out for non-commit' '
+	reset &&
+	test_must_fail git checkout --detach one^{tree} &&
+	check_not_detached
+'
+
+test_expect_success 'checkout --detach errors out for extra argument' '
+	reset &&
+	git checkout master &&
+	test_must_fail git checkout --detach tag one.t &&
+	check_not_detached
+'
+
+test_expect_success 'checkout --detached and -b are incompatible' '
+	reset &&
+	test_must_fail git checkout --detach -b newbranch tag &&
+	check_not_detached
+'
+
+test_expect_success 'checkout --detach moves HEAD' '
+	reset &&
+	git checkout one &&
+	git checkout --detach two &&
+	git diff --exit-code HEAD &&
+	git diff --exit-code two
+'
+
+test_expect_success 'checkout warns on orphan commits' '
+	reset &&
+	git checkout --detach two &&
+	echo content >orphan &&
+	git add orphan &&
+	git commit -a -m orphan1 &&
+	echo new content >orphan &&
+	git commit -a -m orphan2 &&
+	orphan2=$(git rev-parse HEAD) &&
+	git checkout master 2>stderr
+'
+
+test_expect_success 'checkout warns on orphan commits: output' '
+	check_orphan_warning stderr "2 commits"
+'
+
+test_expect_success 'checkout warns orphaning 1 of 2 commits' '
+	git checkout "$orphan2" &&
+	git checkout HEAD^ 2>stderr
+'
+
+test_expect_success 'checkout warns orphaning 1 of 2 commits: output' '
+	check_orphan_warning stderr "1 commit"
+'
+
+test_expect_success 'checkout does not warn leaving ref tip' '
+	reset &&
+	git checkout --detach two &&
+	git checkout master 2>stderr
+'
+
+test_expect_success 'checkout does not warn leaving ref tip' '
+	check_no_orphan_warning stderr
+'
+
+test_expect_success 'checkout does not warn leaving reachable commit' '
+	reset &&
+	git checkout --detach HEAD^ &&
+	git checkout master 2>stderr
+'
+
+test_expect_success 'checkout does not warn leaving reachable commit' '
+	check_no_orphan_warning stderr
+'
+
+cat >expect <<'EOF'
+Your branch is behind 'master' by 1 commit, and can be fast-forwarded.
+EOF
+test_expect_success 'tracking count is accurate after orphan check' '
+	reset &&
+	git branch child master^ &&
+	git config branch.child.remote . &&
+	git config branch.child.merge refs/heads/master &&
+	git checkout child^ &&
+	git checkout child >stdout &&
+	test_i18ncmp expect stdout
+'
+
+test_done
diff --git a/t/t2021-checkout-overwrite.sh b/t/t2021-checkout-overwrite.sh
new file mode 100755
index 0000000..5da63e9
--- /dev/null
+++ b/t/t2021-checkout-overwrite.sh
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+test_description='checkout must not overwrite an untracked objects'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+
+	mkdir -p a/b/c &&
+	>a/b/c/d &&
+	git add -A &&
+	git commit -m base &&
+	git tag start
+'
+
+test_expect_success 'create a commit where dir a/b changed to file' '
+
+	git checkout -b file &&
+	rm -rf a/b &&
+	>a/b &&
+	git add -A &&
+	git commit -m "dir to file"
+'
+
+test_expect_success 'checkout commit with dir must not remove untracked a/b' '
+
+	git rm --cached a/b &&
+	git commit -m "un-track the file" &&
+	test_must_fail git checkout start &&
+	test -f a/b
+'
+
+test_expect_success SYMLINKS 'create a commit where dir a/b changed to symlink' '
+
+	rm -rf a/b &&	# cleanup if previous test failed
+	git checkout -f -b symlink start &&
+	rm -rf a/b &&
+	ln -s foo a/b &&
+	git add -A &&
+	git commit -m "dir to symlink"
+'
+
+test_expect_success SYMLINKS 'checkout commit with dir must not remove untracked a/b' '
+
+	git rm --cached a/b &&
+	git commit -m "un-track the symlink" &&
+	test_must_fail git checkout start &&
+	test -h a/b
+'
+
+test_done
diff --git a/t/t2022-checkout-paths.sh b/t/t2022-checkout-paths.sh
new file mode 100755
index 0000000..56090d2
--- /dev/null
+++ b/t/t2022-checkout-paths.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+test_description='checkout $tree -- $paths'
+. ./test-lib.sh
+
+test_expect_success setup '
+	mkdir dir &&
+	>dir/master &&
+	echo common >dir/common &&
+	git add dir/master dir/common &&
+	test_tick && git commit -m "master has dir/master" &&
+	git checkout -b next &&
+	git mv dir/master dir/next0 &&
+	echo next >dir/next1 &&
+	git add dir &&
+	test_tick && git commit -m "next has dir/next but not dir/master"
+'
+
+test_expect_success 'checking out paths out of a tree does not clobber unrelated paths' '
+	git checkout next &&
+	git reset --hard &&
+	rm dir/next0 &&
+	cat dir/common >expect.common &&
+	echo modified >expect.next1 &&
+	cat expect.next1 >dir/next1 &&
+	echo untracked >expect.next2 &&
+	cat expect.next2 >dir/next2 &&
+
+	git checkout master dir &&
+
+	test_cmp expect.common dir/common &&
+	test_path_is_file dir/master &&
+	git diff --exit-code master dir/master &&
+
+	test_path_is_missing dir/next0 &&
+	test_cmp expect.next1 dir/next1 &&
+	test_path_is_file dir/next2 &&
+	test_must_fail git ls-files --error-unmatch dir/next2 &&
+	test_cmp expect.next2 dir/next2
+'
+
+test_done
diff --git a/t/t2023-checkout-m.sh b/t/t2023-checkout-m.sh
new file mode 100755
index 0000000..7e18985
--- /dev/null
+++ b/t/t2023-checkout-m.sh
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+test_description='checkout -m -- <conflicted path>
+
+Ensures that checkout -m on a resolved file restores the conflicted file'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+	test_tick &&
+	test_commit both.txt both.txt initial &&
+	git branch topic &&
+	test_commit modified_in_master both.txt in_master &&
+	test_commit added_in_master each.txt in_master &&
+	git checkout topic &&
+	test_commit modified_in_topic both.txt in_topic &&
+	test_commit added_in_topic each.txt in_topic
+'
+
+test_expect_success 'git merge master' '
+    test_must_fail git merge master
+'
+
+clean_branchnames () {
+	# Remove branch names after conflict lines
+	sed 's/^\([<>]\{5,\}\) .*$/\1/'
+}
+
+test_expect_success '-m restores 2-way conflicted+resolved file' '
+	cp each.txt each.txt.conflicted &&
+	echo resolved >each.txt &&
+	git add each.txt &&
+	git checkout -m -- each.txt &&
+	clean_branchnames <each.txt >each.txt.cleaned &&
+	clean_branchnames <each.txt.conflicted >each.txt.conflicted.cleaned &&
+	test_cmp each.txt.conflicted.cleaned each.txt.cleaned
+'
+
+test_expect_success '-m restores 3-way conflicted+resolved file' '
+	cp both.txt both.txt.conflicted &&
+	echo resolved >both.txt &&
+	git add both.txt &&
+	git checkout -m -- both.txt &&
+	clean_branchnames <both.txt >both.txt.cleaned &&
+	clean_branchnames <both.txt.conflicted >both.txt.conflicted.cleaned &&
+	test_cmp both.txt.conflicted.cleaned both.txt.cleaned
+'
+
+test_done
diff --git a/t/t2030-unresolve-info.sh b/t/t2030-unresolve-info.sh
new file mode 100755
index 0000000..f262065
--- /dev/null
+++ b/t/t2030-unresolve-info.sh
@@ -0,0 +1,170 @@
+#!/bin/sh
+
+test_description='undoing resolution'
+
+. ./test-lib.sh
+
+check_resolve_undo () {
+	msg=$1
+	shift
+	while case $# in
+	0)	break ;;
+	1|2|3)	die "Bug in check-resolve-undo test" ;;
+	esac
+	do
+		path=$1
+		shift
+		for stage in 1 2 3
+		do
+			sha1=$1
+			shift
+			case "$sha1" in
+			'') continue ;;
+			esac
+			sha1=$(git rev-parse --verify "$sha1")
+			printf "100644 %s %s\t%s\n" $sha1 $stage $path
+		done
+	done >"$msg.expect" &&
+	git ls-files --resolve-undo >"$msg.actual" &&
+	test_cmp "$msg.expect" "$msg.actual"
+}
+
+prime_resolve_undo () {
+	git reset --hard &&
+	git checkout second^0 &&
+	test_tick &&
+	test_must_fail git merge third^0 &&
+	echo merge does not leave anything &&
+	check_resolve_undo empty &&
+	echo different >fi/le &&
+	git add fi/le &&
+	echo resolving records &&
+	check_resolve_undo recorded fi/le initial:fi/le second:fi/le third:fi/le
+}
+
+test_expect_success setup '
+	mkdir fi &&
+	test_commit initial fi/le first &&
+	git branch side &&
+	git branch another &&
+	test_commit second fi/le second &&
+	git checkout side &&
+	test_commit third fi/le third &&
+	git checkout another &&
+	test_commit fourth fi/le fourth &&
+	git checkout master
+'
+
+test_expect_success 'add records switch clears' '
+	prime_resolve_undo &&
+	test_tick &&
+	git commit -m merged &&
+	echo committing keeps &&
+	check_resolve_undo kept fi/le initial:fi/le second:fi/le third:fi/le &&
+	git checkout second^0 &&
+	echo switching clears &&
+	check_resolve_undo cleared
+'
+
+test_expect_success 'rm records reset clears' '
+	prime_resolve_undo &&
+	test_tick &&
+	git commit -m merged &&
+	echo committing keeps &&
+	check_resolve_undo kept fi/le initial:fi/le second:fi/le third:fi/le &&
+
+	echo merge clears upfront &&
+	test_must_fail git merge fourth^0 &&
+	check_resolve_undo nuked &&
+
+	git rm -f fi/le &&
+	echo resolving records &&
+	check_resolve_undo recorded fi/le initial:fi/le HEAD:fi/le fourth:fi/le &&
+
+	git reset --hard &&
+	echo resetting discards &&
+	check_resolve_undo discarded
+'
+
+test_expect_success 'plumbing clears' '
+	prime_resolve_undo &&
+	test_tick &&
+	git commit -m merged &&
+	echo committing keeps &&
+	check_resolve_undo kept fi/le initial:fi/le second:fi/le third:fi/le &&
+
+	echo plumbing clear &&
+	git update-index --clear-resolve-undo &&
+	check_resolve_undo cleared
+'
+
+test_expect_success 'add records checkout -m undoes' '
+	prime_resolve_undo &&
+	git diff HEAD &&
+	git checkout --conflict=merge fi/le &&
+	echo checkout used the record and removed it &&
+	check_resolve_undo removed &&
+	echo the index and the work tree is unmerged again &&
+	git diff >actual &&
+	grep "^++<<<<<<<" actual
+'
+
+test_expect_success 'unmerge with plumbing' '
+	prime_resolve_undo &&
+	git update-index --unresolve fi/le &&
+	git ls-files -u >actual &&
+	test_line_count = 3 actual
+'
+
+test_expect_success 'rerere and rerere forget' '
+	mkdir .git/rr-cache &&
+	prime_resolve_undo &&
+	echo record the resolution &&
+	git rerere &&
+	rerere_id=$(cd .git/rr-cache && echo */postimage) &&
+	rerere_id=${rerere_id%/postimage} &&
+	test -f .git/rr-cache/$rerere_id/postimage &&
+	git checkout -m fi/le &&
+	echo resurrect the conflict &&
+	grep "^=======" fi/le &&
+	echo reresolve the conflict &&
+	git rerere &&
+	test "z$(cat fi/le)" = zdifferent &&
+	echo register the resolution again &&
+	git add fi/le &&
+	check_resolve_undo kept fi/le initial:fi/le second:fi/le third:fi/le &&
+	test -z "$(git ls-files -u)" &&
+	git rerere forget fi/le &&
+	! test -f .git/rr-cache/$rerere_id/postimage &&
+	tr "\0" "\n" <.git/MERGE_RR >actual &&
+	echo "$rerere_id	fi/le" >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'rerere and rerere forget (subdirectory)' '
+	rm -fr .git/rr-cache &&
+	mkdir .git/rr-cache &&
+	prime_resolve_undo &&
+	echo record the resolution &&
+	(cd fi && git rerere) &&
+	rerere_id=$(cd .git/rr-cache && echo */postimage) &&
+	rerere_id=${rerere_id%/postimage} &&
+	test -f .git/rr-cache/$rerere_id/postimage &&
+	(cd fi && git checkout -m le) &&
+	echo resurrect the conflict &&
+	grep "^=======" fi/le &&
+	echo reresolve the conflict &&
+	(cd fi && git rerere) &&
+	test "z$(cat fi/le)" = zdifferent &&
+	echo register the resolution again &&
+	(cd fi && git add le) &&
+	check_resolve_undo kept fi/le initial:fi/le second:fi/le third:fi/le &&
+	test -z "$(git ls-files -u)" &&
+	(cd fi && git rerere forget le) &&
+	! test -f .git/rr-cache/$rerere_id/postimage &&
+	tr "\0" "\n" <.git/MERGE_RR >actual &&
+	echo "$rerere_id	fi/le" >expect &&
+	test_cmp expect actual
+'
+
+test_done
diff --git a/t/t2050-git-dir-relative.sh b/t/t2050-git-dir-relative.sh
index b7131d8..21f4659 100755
--- a/t/t2050-git-dir-relative.sh
+++ b/t/t2050-git-dir-relative.sh
@@ -26,7 +26,7 @@
 
 test_expect_success 'post-commit hook used ordinarily' '
 echo initial >top &&
-git add top
+git add top &&
 git commit -m initial &&
 test -r "${COMMIT_FILE}"
 '
@@ -45,7 +45,7 @@
 rm -rf "${COMMIT_FILE}"
 
 test_expect_success 'post-commit-hook from sub dir' '
-echo changed again >top
+echo changed again >top &&
 cd subdir &&
 git --git-dir .git --work-tree .. add ../top &&
 git --git-dir .git --work-tree .. commit -m subcommit &&
diff --git a/t/t2101-update-index-reupdate.sh b/t/t2101-update-index-reupdate.sh
index 648184f..c8bce8c 100755
--- a/t/t2101-update-index-reupdate.sh
+++ b/t/t2101-update-index-reupdate.sh
@@ -51,7 +51,7 @@
 	echo hello world >dir1/file3 &&
 	echo goodbye people >file2 &&
 	git update-index --add file2 dir1/file3 &&
-	echo hello everybody >file2
+	echo hello everybody >file2 &&
 	echo happy >dir1/file3 &&
 	git update-index --again &&
 	git ls-files -s >current &&
@@ -63,10 +63,10 @@
 EOF
 test_expect_success 'update-index --update from subdir' \
 	'echo not so happy >file2 &&
-	cd dir1 &&
+	(cd dir1 &&
 	cat ../file2 >file3 &&
-	git update-index --again &&
-	cd .. &&
+	git update-index --again
+	) &&
 	git ls-files -s >current &&
 	cmp current expected'
 
diff --git a/t/t2102-update-index-symlinks.sh b/t/t2102-update-index-symlinks.sh
index 1ed44ee..4d0d0a3 100755
--- a/t/t2102-update-index-symlinks.sh
+++ b/t/t2102-update-index-symlinks.sh
@@ -24,7 +24,7 @@
 test_expect_success \
 'the index entry must still be a symbolic link' '
 case "`git ls-files --stage --cached symlink`" in
-120000" "*symlink) echo ok;;
+120000" "*symlink) echo pass;;
 *) echo fail; git ls-files --stage --cached symlink; (exit 1);;
 esac'
 
diff --git a/t/t2104-update-index-skip-worktree.sh b/t/t2104-update-index-skip-worktree.sh
new file mode 100755
index 0000000..1d0879b
--- /dev/null
+++ b/t/t2104-update-index-skip-worktree.sh
@@ -0,0 +1,57 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Nguyễn Thái Ngọc Duy
+#
+
+test_description='skip-worktree bit test'
+
+. ./test-lib.sh
+
+cat >expect.full <<EOF
+H 1
+H 2
+H sub/1
+H sub/2
+EOF
+
+cat >expect.skip <<EOF
+S 1
+H 2
+S sub/1
+H sub/2
+EOF
+
+test_expect_success 'setup' '
+	mkdir sub &&
+	touch ./1 ./2 sub/1 sub/2 &&
+	git add 1 2 sub/1 sub/2 &&
+	git ls-files -t | test_cmp expect.full -
+'
+
+test_expect_success 'index is at version 2' '
+	test "$(test-index-version < .git/index)" = 2
+'
+
+test_expect_success 'update-index --skip-worktree' '
+	git update-index --skip-worktree 1 sub/1 &&
+	git ls-files -t | test_cmp expect.skip -
+'
+
+test_expect_success 'index is at version 3 after having some skip-worktree entries' '
+	test "$(test-index-version < .git/index)" = 3
+'
+
+test_expect_success 'ls-files -t' '
+	git ls-files -t | test_cmp expect.skip -
+'
+
+test_expect_success 'update-index --no-skip-worktree' '
+	git update-index --no-skip-worktree 1 sub/1 &&
+	git ls-files -t | test_cmp expect.full -
+'
+
+test_expect_success 'index version is back to 2 when there is no skip-worktree entry' '
+	test "$(test-index-version < .git/index)" = 2
+'
+
+test_done
diff --git a/t/t2105-update-index-gitfile.sh b/t/t2105-update-index-gitfile.sh
new file mode 100755
index 0000000..a7f3d47
--- /dev/null
+++ b/t/t2105-update-index-gitfile.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Brad King
+#
+
+test_description='git update-index for gitlink to .git file.
+'
+
+. ./test-lib.sh
+
+test_expect_success 'submodule with absolute .git file' '
+	mkdir sub1 &&
+	(cd sub1 &&
+	 git init &&
+	 REAL="$(pwd)/.real" &&
+	 mv .git "$REAL" &&
+	 echo "gitdir: $REAL" >.git &&
+	 test_commit first)
+'
+
+test_expect_success 'add gitlink to absolute .git file' '
+	git update-index --add -- sub1
+'
+
+test_expect_success 'submodule with relative .git file' '
+	mkdir sub2 &&
+	(cd sub2 &&
+	 git init &&
+	 mv .git .real &&
+	 echo "gitdir: .real" >.git &&
+	 test_commit first)
+'
+
+test_expect_success 'add gitlink to relative .git file' '
+	git update-index --add -- sub2
+'
+
+test_done
diff --git a/t/t2106-update-index-assume-unchanged.sh b/t/t2106-update-index-assume-unchanged.sh
new file mode 100755
index 0000000..99d858c
--- /dev/null
+++ b/t/t2106-update-index-assume-unchanged.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+test_description='git update-index --assume-unchanged test.
+'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' \
+	': >file &&
+	 git add file &&
+	 git commit -m initial &&
+	 git branch other &&
+	 echo upstream >file &&
+	 git add file &&
+	 git commit -m upstream'
+
+test_expect_success 'do not switch branches with dirty file' \
+	'git reset --hard &&
+	 git checkout other &&
+	 echo dirt >file &&
+	 git update-index --assume-unchanged file &&
+	 test_must_fail git checkout master'
+
+test_done
diff --git a/t/t2107-update-index-basic.sh b/t/t2107-update-index-basic.sh
new file mode 100755
index 0000000..809fafe
--- /dev/null
+++ b/t/t2107-update-index-basic.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+test_description='basic update-index tests
+
+Tests for command-line parsing and basic operation.
+'
+
+. ./test-lib.sh
+
+test_expect_success 'update-index --nonsense fails' '
+	test_must_fail git update-index --nonsense 2>msg &&
+	cat msg &&
+	test -s msg
+'
+
+test_expect_success 'update-index --nonsense dumps usage' '
+	test_expect_code 129 git update-index --nonsense 2>err &&
+	grep "[Uu]sage: git update-index" err
+'
+
+test_expect_success 'update-index -h with corrupt index' '
+	mkdir broken &&
+	(
+		cd broken &&
+		git init &&
+		>.git/index &&
+		test_expect_code 129 git update-index -h >usage 2>&1
+	) &&
+	grep "[Uu]sage: git update-index" broken/usage
+'
+
+test_done
diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh
index 9120750..4cdebda 100755
--- a/t/t2200-add-update.sh
+++ b/t/t2200-add-update.sh
@@ -25,7 +25,7 @@
 	echo initial >dir1/sub2 &&
 	echo initial >dir2/sub3 &&
 	git add check dir1 dir2 top foo &&
-	test_tick
+	test_tick &&
 	git commit -m initial &&
 
 	echo changed >check &&
@@ -124,7 +124,7 @@
 	after=$(git ls-files -s check top) &&
 
 	test "$before" = "$after" &&
-	test_cmp expect actual
+	test_i18ncmp expect actual
 
 '
 
@@ -149,31 +149,26 @@
 	echo 3 >path1 &&
 	echo 2 >path3 &&
 	echo 2 >path5 &&
+
+	# Explicit resolving by adding removed paths should fail
+	test_must_fail git add path4 &&
+	test_must_fail git add path6 &&
+
+	# "add -u" should notice removals no matter what stages
+	# the index entries are in.
 	git add -u &&
 	git ls-files -s path1 path2 path3 path4 path5 path6 >actual &&
 	{
 		echo "100644 $three 0	path1"
-		echo "100644 $one 1	path3"
-		echo "100644 $one 1	path4"
-		echo "100644 $one 3	path5"
-		echo "100644 $one 3	path6"
-	} >expect &&
-	test_cmp expect actual &&
-
-	# Bonus tests.  Explicit resolving
-	git add path3 path5 &&
-	test_must_fail git add path4 &&
-	test_must_fail git add path6 &&
-	git rm path4 &&
-	git rm path6 &&
-
-	git ls-files -s "path?" >actual &&
-	{
-		echo "100644 $three 0	path1"
 		echo "100644 $two 0	path3"
 		echo "100644 $two 0	path5"
-	} >expect
+	} >expect &&
+	test_cmp expect actual
+'
 
+test_expect_success '"add -u non-existent" should fail' '
+	test_must_fail git add -u non-existent &&
+	! (git ls-files | grep "non-existent")
 '
 
 test_done
diff --git a/t/t2201-add-update-typechange.sh b/t/t2201-add-update-typechange.sh
index 2e8f702..954fc51 100755
--- a/t/t2201-add-update-typechange.sh
+++ b/t/t2201-add-update-typechange.sh
@@ -4,8 +4,6 @@
 
 . ./test-lib.sh
 
-_z40=0000000000000000000000000000000000000000
-
 test_expect_success setup '
 	>xyzzy &&
 	_empty=$(git hash-object --stdin <xyzzy) &&
diff --git a/t/t2203-add-intent.sh b/t/t2203-add-intent.sh
index 58a3299..ec35409 100755
--- a/t/t2203-add-intent.sh
+++ b/t/t2203-add-intent.sh
@@ -32,7 +32,7 @@
 	! grep "$empty" actual
 '
 
-test_expect_success 'cannot commit with i-t-a entry' '
+test_expect_success 'i-t-a entry is simply ignored' '
 	test_tick &&
 	git commit -a -m initial &&
 	git reset --hard &&
@@ -41,12 +41,14 @@
 	echo frotz >nitfol &&
 	git add rezrov &&
 	git add -N nitfol &&
-	test_must_fail git commit
+	git commit -m second &&
+	test $(git ls-tree HEAD -- nitfol | wc -l) = 0 &&
+	test $(git diff --name-only HEAD -- nitfol | wc -l) = 1
 '
 
 test_expect_success 'can commit with an unrelated i-t-a entry in index' '
 	git reset --hard &&
-	echo xyzzy >rezrov &&
+	echo bozbar >rezrov &&
 	echo frotz >nitfol &&
 	git add rezrov &&
 	git add -N nitfol &&
diff --git a/t/t2204-add-ignored.sh b/t/t2204-add-ignored.sh
new file mode 100755
index 0000000..8340ac2
--- /dev/null
+++ b/t/t2204-add-ignored.sh
@@ -0,0 +1,92 @@
+#!/bin/sh
+
+test_description='giving ignored paths to git add'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+	mkdir sub dir dir/sub &&
+	echo sub >.gitignore &&
+	echo ign >>.gitignore &&
+	for p in . sub dir dir/sub
+	do
+		>"$p/ign" &&
+		>"$p/file" || exit 1
+	done
+'
+
+for i in file dir/file dir 'd*'
+do
+	test_expect_success "no complaints for unignored $i" '
+		rm -f .git/index &&
+		git add "$i" &&
+		git ls-files "$i" >out &&
+		test -s out
+	'
+done
+
+for i in ign dir/ign dir/sub dir/sub/*ign sub/file sub sub/*
+do
+	test_expect_success "complaints for ignored $i" '
+		rm -f .git/index &&
+		test_must_fail git add "$i" 2>err &&
+		git ls-files "$i" >out &&
+		! test -s out
+	'
+
+	test_expect_success "complaints for ignored $i output" '
+		test_i18ngrep -e "Use -f if" err
+	'
+
+	test_expect_success "complaints for ignored $i with unignored file" '
+		rm -f .git/index &&
+		test_must_fail git add "$i" file 2>err &&
+		git ls-files "$i" >out &&
+		! test -s out
+	'
+	test_expect_success "complaints for ignored $i with unignored file output" '
+		test_i18ngrep -e "Use -f if" err
+	'
+done
+
+for i in sub sub/*
+do
+	test_expect_success "complaints for ignored $i in dir" '
+		rm -f .git/index &&
+		(
+			cd dir &&
+			test_must_fail git add "$i" 2>err &&
+			git ls-files "$i" >out &&
+			! test -s out
+		)
+	'
+
+	test_expect_success "complaints for ignored $i in dir output" '
+		(
+			cd dir &&
+			test_i18ngrep -e "Use -f if" err
+		)
+	'
+done
+
+for i in ign file
+do
+	test_expect_success "complaints for ignored $i in sub" '
+		rm -f .git/index &&
+		(
+			cd sub &&
+			test_must_fail git add "$i" 2>err &&
+			git ls-files "$i" >out &&
+			! test -s out
+		)
+	'
+
+	test_expect_success "complaints for ignored $i in sub output" '
+		(
+			cd sub &&
+			test_i18ngrep -e "Use -f if" err
+		)
+	'
+done
+
+test_done
diff --git a/t/t2300-cd-to-toplevel.sh b/t/t2300-cd-to-toplevel.sh
index 3b01ad2..9965bc5 100755
--- a/t/t2300-cd-to-toplevel.sh
+++ b/t/t2300-cd-to-toplevel.sh
@@ -8,7 +8,7 @@
 	test_expect_success $3 "$2" '
 		(
 			cd '"'$1'"' &&
-			. git-sh-setup &&
+			. "$(git --exec-path)"/git-sh-setup &&
 			cd_to_toplevel &&
 			[ "$(pwd -P)" = "$TOPLEVEL" ]
 		)
diff --git a/t/t3000-ls-files-others.sh b/t/t3000-ls-files-others.sh
index 86291e8..88be904 100755
--- a/t/t3000-ls-files-others.sh
+++ b/t/t3000-ls-files-others.sh
@@ -17,57 +17,71 @@
 '
 . ./test-lib.sh
 
-date >path0
-if test_have_prereq SYMLINKS
-then
-	ln -s xyzzy path1
-else
-	date > path1
-fi
-mkdir path2 path3 path4
-date >path2/file2
-date >path2-junk
-date >path3/file3
-date >path3-junk
-git update-index --add path3-junk path3/file3
+test_expect_success 'setup ' '
+	date >path0 &&
+	if test_have_prereq SYMLINKS
+	then
+		ln -s xyzzy path1
+	else
+		date >path1
+	fi &&
+	mkdir path2 path3 path4 &&
+	date >path2/file2 &&
+	date >path2-junk &&
+	date >path3/file3 &&
+	date >path3-junk &&
+	git update-index --add path3-junk path3/file3
+'
 
-cat >expected1 <<EOF
-expected1
-expected2
-expected3
-output
-path0
-path1
-path2-junk
-path2/file2
-EOF
-sed -e 's|path2/file2|path2/|' <expected1 >expected2
-cat <expected2 >expected3
-echo path4/ >>expected2
+test_expect_success 'setup: expected output' '
+	cat >expected1 <<-\EOF &&
+	expected1
+	expected2
+	expected3
+	output
+	path0
+	path1
+	path2-junk
+	path2/file2
+	EOF
 
-test_expect_success \
-    'git ls-files --others to show output.' \
-    'git ls-files --others >output'
+	sed -e "s|path2/file2|path2/|" <expected1 >expected2 &&
+	cp expected2 expected3 &&
+	echo path4/ >>expected2
+'
 
-test_expect_success \
-    'git ls-files --others should pick up symlinks.' \
-    'test_cmp expected1 output'
+test_expect_success 'ls-files --others' '
+	git ls-files --others >output &&
+	test_cmp expected1 output
+'
 
-test_expect_success \
-    'git ls-files --others --directory to show output.' \
-    'git ls-files --others --directory >output'
+test_expect_success 'ls-files --others --directory' '
+	git ls-files --others --directory >output &&
+	test_cmp expected2 output
+'
 
+test_expect_success '--no-empty-directory hides empty directory' '
+	git ls-files --others --directory --no-empty-directory >output &&
+	test_cmp expected3 output
+'
 
-test_expect_success \
-    'git ls-files --others --directory should not get confused.' \
-    'test_cmp expected2 output'
-
-test_expect_success \
-    'git ls-files --others --directory --no-empty-directory to show output.' \
-    'git ls-files --others --directory --no-empty-directory >output'
-
-test_expect_success \
-    '--no-empty-directory hides empty directory' \
-    'test_cmp expected3 output'
+test_expect_success SYMLINKS 'ls-files --others with symlinked submodule' '
+	git init super &&
+	git init sub &&
+	(
+		cd sub &&
+		>a &&
+		git add a &&
+		git commit -m sub &&
+		git pack-refs --all
+	) &&
+	(
+		cd super &&
+		"$SHELL_PATH" "$TEST_DIRECTORY/../contrib/workdir/git-new-workdir" ../sub sub
+		git ls-files --others --exclude-standard >../actual
+	) &&
+	echo sub/ >expect &&
+	test_cmp expect actual
+'
 
 test_done
diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh
index c65bca8..c8fe978 100755
--- a/t/t3001-ls-files-others-exclude.sh
+++ b/t/t3001-ls-files-others-exclude.sh
@@ -64,6 +64,8 @@
 echo '!*.2
 !*.8' >one/two/.gitignore
 
+allignores='.gitignore one/.gitignore one/two/.gitignore'
+
 test_expect_success \
     'git ls-files --others with various exclude options.' \
     'git ls-files --others \
@@ -85,6 +87,26 @@
        >output &&
      test_cmp expect output'
 
+test_expect_success 'setup skip-worktree gitignore' '
+	git add $allignores &&
+	git update-index --skip-worktree $allignores &&
+	rm $allignores
+'
+
+test_expect_success \
+    'git ls-files --others with various exclude options.' \
+    'git ls-files --others \
+       --exclude=\*.6 \
+       --exclude-per-directory=.gitignore \
+       --exclude-from=.git/ignore \
+       >output &&
+     test_cmp expect output'
+
+test_expect_success 'restore gitignore' '
+	git checkout $allignores &&
+	rm .git/index
+'
+
 cat > excludes-file <<\EOF
 *.[1-8]
 e*
@@ -134,7 +156,7 @@
 
 test_expect_success 'trailing slash in exclude forces directory match (1)' '
 
-	>two
+	>two &&
 	git ls-files --others --exclude=two/ >output &&
 	grep "^two" output
 
@@ -153,4 +175,43 @@
 	grep "^a.1" output
 '
 
+test_expect_success 'subdirectory ignore (setup)' '
+	mkdir -p top/l1/l2 &&
+	(
+		cd top &&
+		git init &&
+		echo /.gitignore >.gitignore &&
+		echo l1 >>.gitignore &&
+		echo l2 >l1/.gitignore &&
+		>l1/l2/l1
+	)
+'
+
+test_expect_success 'subdirectory ignore (toplevel)' '
+	(
+		cd top &&
+		git ls-files -o --exclude-standard
+	) >actual &&
+	>expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'subdirectory ignore (l1/l2)' '
+	(
+		cd top/l1/l2 &&
+		git ls-files -o --exclude-standard
+	) >actual &&
+	>expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'subdirectory ignore (l1)' '
+	(
+		cd top/l1 &&
+		git ls-files -o --exclude-standard
+	) >actual &&
+	>expect &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t3003-ls-files-exclude.sh b/t/t3003-ls-files-exclude.sh
new file mode 100755
index 0000000..d5ec333
--- /dev/null
+++ b/t/t3003-ls-files-exclude.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+test_description='ls-files --exclude does not affect index files'
+. ./test-lib.sh
+
+test_expect_success 'create repo with file' '
+	echo content >file &&
+	git add file &&
+	git commit -m file &&
+	echo modification >file
+'
+
+check_output() {
+test_expect_success "ls-files output contains file ($1)" "
+	echo '$2' >expect &&
+	git ls-files --exclude-standard --$1 >output &&
+	test_cmp expect output
+"
+}
+
+check_all_output() {
+	check_output 'cached' 'file'
+	check_output 'modified' 'file'
+}
+
+check_all_output
+test_expect_success 'add file to gitignore' '
+	echo file >.gitignore
+'
+check_all_output
+
+test_expect_success 'ls-files -i lists only tracked-but-ignored files' '
+	echo content >other-file &&
+	git add other-file &&
+	echo file >expect &&
+	git ls-files -i --exclude-standard >output &&
+	test_cmp expect output
+'
+
+test_done
diff --git a/t/t3004-ls-files-basic.sh b/t/t3004-ls-files-basic.sh
new file mode 100755
index 0000000..490e052
--- /dev/null
+++ b/t/t3004-ls-files-basic.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+test_description='basic ls-files tests
+
+This test runs git ls-files with various unusual or malformed
+command-line arguments.
+'
+
+. ./test-lib.sh
+
+>empty
+
+test_expect_success 'ls-files in empty repository' '
+	git ls-files >actual &&
+	test_cmp empty actual
+'
+
+test_expect_success 'ls-files with nonexistent path' '
+	git ls-files doesnotexist >actual &&
+	test_cmp empty actual
+'
+
+test_expect_success 'ls-files with nonsense option' '
+	test_expect_code 129 git ls-files --nonsense 2>actual &&
+	grep "[Uu]sage: git ls-files" actual
+'
+
+test_expect_success 'ls-files -h in corrupt repository' '
+	mkdir broken &&
+	(
+		cd broken &&
+		git init &&
+		>.git/index &&
+		test_expect_code 129 git ls-files -h >usage 2>&1
+	) &&
+	grep "[Uu]sage: git ls-files " broken/usage
+'
+
+test_done
diff --git a/t/t3005-ls-files-relative.sh b/t/t3005-ls-files-relative.sh
new file mode 100755
index 0000000..3778694
--- /dev/null
+++ b/t/t3005-ls-files-relative.sh
@@ -0,0 +1,72 @@
+#!/bin/sh
+
+test_description='ls-files tests with relative paths
+
+This test runs git ls-files with various relative path arguments.
+'
+
+. ./test-lib.sh
+
+new_line='
+'
+sq=\'
+
+test_expect_success 'prepare' '
+	: >never-mind-me &&
+	git add never-mind-me &&
+	mkdir top &&
+	(
+		cd top &&
+		mkdir sub &&
+		x="x xa xbc xdef xghij xklmno" &&
+		y=$(echo "$x" | tr x y) &&
+		touch $x &&
+		touch $y &&
+		cd sub &&
+		git add ../x*
+	)
+'
+
+test_expect_success 'ls-files with mixed levels' '
+	(
+		cd top/sub &&
+		cat >expect <<-EOF &&
+		../../never-mind-me
+		../x
+		EOF
+		git ls-files $(cat expect) >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'ls-files -c' '
+	(
+		cd top/sub &&
+		for f in ../y*
+		do
+			echo "error: pathspec $sq$f$sq did not match any file(s) known to git."
+		done >expect.err &&
+		echo "Did you forget to ${sq}git add${sq}?" >>expect.err &&
+		ls ../x* >expect.out &&
+		test_must_fail git ls-files -c --error-unmatch ../[xy]* >actual.out 2>actual.err &&
+		test_cmp expect.out actual.out &&
+		test_cmp expect.err actual.err
+	)
+'
+
+test_expect_success 'ls-files -o' '
+	(
+		cd top/sub &&
+		for f in ../x*
+		do
+			echo "error: pathspec $sq$f$sq did not match any file(s) known to git."
+		done >expect.err &&
+		echo "Did you forget to ${sq}git add${sq}?" >>expect.err &&
+		ls ../y* >expect.out &&
+		test_must_fail git ls-files -o --error-unmatch ../[xy]* >actual.out 2>actual.err &&
+		test_cmp expect.out actual.out &&
+		test_cmp expect.err actual.err
+	)
+'
+
+test_done
diff --git a/t/t3006-ls-files-long.sh b/t/t3006-ls-files-long.sh
new file mode 100755
index 0000000..202ad65
--- /dev/null
+++ b/t/t3006-ls-files-long.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+test_description='overly long paths'
+. ./test-lib.sh
+
+test_expect_success setup '
+	p=filefilefilefilefilefilefilefile &&
+	p=$p$p$p$p$p$p$p$p$p$p$p$p$p$p$p$p &&
+	p=$p$p$p$p$p$p$p$p$p$p$p$p$p$p$p$p &&
+
+	path_a=${p}_a &&
+	path_z=${p}_z &&
+
+	blob_a=$(echo frotz | git hash-object -w --stdin) &&
+	blob_z=$(echo nitfol | git hash-object -w --stdin) &&
+
+	pat="100644 %s 0\t%s\n"
+'
+
+test_expect_success 'overly-long path by itself is not a problem' '
+	printf "$pat" "$blob_a" "$path_a" |
+	git update-index --add --index-info &&
+	echo "$path_a" >expect &&
+	git ls-files >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'overly-long path does not replace another by mistake' '
+	printf "$pat" "$blob_a" "$path_a" "$blob_z" "$path_z" |
+	git update-index --add --index-info &&
+	(
+		echo "$path_a"
+		echo "$path_z"
+	) >expect &&
+	git ls-files >actual &&
+	test_cmp expect actual
+'
+
+test_done
diff --git a/t/t3020-ls-files-error-unmatch.sh b/t/t3020-ls-files-error-unmatch.sh
index f4066cb..ca01053 100755
--- a/t/t3020-ls-files-error-unmatch.sh
+++ b/t/t3020-ls-files-error-unmatch.sh
@@ -11,9 +11,11 @@
 '
 . ./test-lib.sh
 
-touch foo bar
-git update-index --add foo bar
-git commit -m "add foo bar"
+test_expect_success 'setup' '
+	touch foo bar &&
+	git update-index --add foo bar &&
+	git commit -m "add foo bar"
+'
 
 test_expect_success \
     'git ls-files --error-unmatch should fail with unmatched path.' \
@@ -24,4 +26,3 @@
     'git ls-files --error-unmatch foo bar'
 
 test_done
-1
diff --git a/t/t3030-merge-recursive.sh b/t/t3030-merge-recursive.sh
index 9b3fa2b..a5e3da7 100755
--- a/t/t3030-merge-recursive.sh
+++ b/t/t3030-merge-recursive.sh
@@ -22,6 +22,13 @@
 	git branch df-2 &&
 	git branch df-3 &&
 	git branch remove &&
+	git branch submod &&
+	git branch copy &&
+	git branch rename &&
+	if test_have_prereq SYMLINKS
+	then
+		git branch rename-ln
+	fi &&
 
 	echo hello >>a &&
 	cp a d/e &&
@@ -236,22 +243,49 @@
 	test_cmp expected actual
 '
 
+test_expect_success 'setup 7' '
+
+	git checkout submod &&
+	git rm d/e &&
+	test_tick &&
+	git commit -m "remove d/e" &&
+	git update-index --add --cacheinfo 160000 $c1 d &&
+	test_tick &&
+	git commit -m "make d/ a submodule"
+'
+
+test_expect_success 'setup 8' '
+	git checkout rename &&
+	git mv a e &&
+	git add e &&
+	test_tick &&
+	git commit -m "rename a->e" &&
+	if test_have_prereq SYMLINKS
+	then
+		git checkout rename-ln &&
+		git mv a e &&
+		ln -s e a &&
+		git add a e &&
+		test_tick &&
+		git commit -m "rename a->e, symlink a->e" &&
+		oln=`printf e | git hash-object --stdin`
+	fi
+'
+
+test_expect_success 'setup 9' '
+	git checkout copy &&
+	cp a e &&
+	git add e &&
+	test_tick &&
+	git commit -m "copy a->e"
+'
+
 test_expect_success 'merge-recursive simple' '
 
 	rm -fr [abcd] &&
 	git checkout -f "$c2" &&
 
-	git merge-recursive "$c0" -- "$c2" "$c1"
-	status=$?
-	case "$status" in
-	1)
-		: happy
-		;;
-	*)
-		echo >&2 "why status $status!!!"
-		false
-		;;
-	esac
+	test_expect_code 1 git merge-recursive "$c0" -- "$c2" "$c1"
 '
 
 test_expect_success 'merge-recursive result' '
@@ -276,11 +310,13 @@
 
 	test_must_fail git merge "$c5" &&
 	test_must_fail git merge "$c5" 2> out &&
-	grep "You have not concluded your merge" out &&
+	test_i18ngrep "not possible because you have unmerged files" out &&
+	git add -u &&
+	test_must_fail git merge "$c5" 2> out &&
+	test_i18ngrep "You have not concluded your merge" out &&
 	rm -f .git/MERGE_HEAD &&
 	test_must_fail git merge "$c5" 2> out &&
-	grep "You are in the middle of a conflicted merge" out
-
+	test_i18ngrep "Your local changes to the following files would be overwritten by merge:" out
 '
 
 test_expect_success 'merge-recursive remove conflict' '
@@ -288,17 +324,7 @@
 	rm -fr [abcd] &&
 	git checkout -f "$c1" &&
 
-	git merge-recursive "$c0" -- "$c1" "$c5"
-	status=$?
-	case "$status" in
-	1)
-		: happy
-		;;
-	*)
-		echo >&2 "why status $status!!!"
-		false
-		;;
-	esac
+	test_expect_code 1 git merge-recursive "$c0" -- "$c1" "$c5"
 '
 
 test_expect_success 'merge-recursive remove conflict' '
@@ -342,17 +368,7 @@
 	git reset --hard &&
 	git checkout -f "$c1" &&
 
-	git merge-recursive "$c0" -- "$c1" "$c4"
-	status=$?
-	case "$status" in
-	1)
-		: happy
-		;;
-	*)
-		echo >&2 "why status $status!!!"
-		false
-		;;
-	esac
+	test_expect_code 1 git merge-recursive "$c0" -- "$c1" "$c4"
 '
 
 test_expect_success 'merge-recursive d/f conflict result' '
@@ -376,17 +392,7 @@
 	git reset --hard &&
 	git checkout -f "$c4" &&
 
-	git merge-recursive "$c0" -- "$c4" "$c1"
-	status=$?
-	case "$status" in
-	1)
-		: happy
-		;;
-	*)
-		echo >&2 "why status $status!!!"
-		false
-		;;
-	esac
+	test_expect_code 1 git merge-recursive "$c0" -- "$c4" "$c1"
 '
 
 test_expect_success 'merge-recursive d/f conflict result the other way' '
@@ -410,17 +416,7 @@
 	git reset --hard &&
 	git checkout -f "$c1" &&
 
-	git merge-recursive "$c0" -- "$c1" "$c6"
-	status=$?
-	case "$status" in
-	1)
-		: happy
-		;;
-	*)
-		echo >&2 "why status $status!!!"
-		false
-		;;
-	esac
+	test_expect_code 1 git merge-recursive "$c0" -- "$c1" "$c6"
 '
 
 test_expect_success 'merge-recursive d/f conflict result' '
@@ -444,17 +440,7 @@
 	git reset --hard &&
 	git checkout -f "$c6" &&
 
-	git merge-recursive "$c0" -- "$c6" "$c1"
-	status=$?
-	case "$status" in
-	1)
-		: happy
-		;;
-	*)
-		echo >&2 "why status $status!!!"
-		false
-		;;
-	esac
+	test_expect_code 1 git merge-recursive "$c0" -- "$c6" "$c1"
 '
 
 test_expect_success 'merge-recursive d/f conflict result' '
@@ -512,7 +498,7 @@
 		echo "100644 $o0 0	c"
 		echo "100644 $o1 0	d/e"
 	) >expected &&
-	test_cmp expected actual
+	test_cmp expected actual &&
 
 	git read-tree --prefix=z/ master &&
 	git ls-files -s >actual &&
@@ -549,4 +535,62 @@
 	test_must_fail test -d d
 '
 
+test_expect_failure 'merge-recursive simple w/submodule' '
+
+	git checkout submod &&
+	git merge remove
+'
+
+test_expect_failure 'merge-recursive simple w/submodule result' '
+
+	git ls-files -s >actual &&
+	(
+		echo "100644 $o5 0	a"
+		echo "100644 $o0 0	c"
+		echo "160000 $c1 0	d"
+	) >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'merge-recursive copy vs. rename' '
+	git checkout -f copy &&
+	git merge rename &&
+	( git ls-tree -r HEAD && git ls-files -s ) >actual &&
+	(
+		echo "100644 blob $o0	b"
+		echo "100644 blob $o0	c"
+		echo "100644 blob $o0	d/e"
+		echo "100644 blob $o0	e"
+		echo "100644 $o0 0	b"
+		echo "100644 $o0 0	c"
+		echo "100644 $o0 0	d/e"
+		echo "100644 $o0 0	e"
+	) >expected &&
+	test_cmp expected actual
+'
+
+if test_have_prereq SYMLINKS
+then
+	test_expect_failure 'merge-recursive rename vs. rename/symlink' '
+
+		git checkout -f rename &&
+		git merge rename-ln &&
+		( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+		(
+			echo "120000 blob $oln	a"
+			echo "100644 blob $o0	b"
+			echo "100644 blob $o0	c"
+			echo "100644 blob $o0	d/e"
+			echo "100644 blob $o0	e"
+			echo "120000 $oln 0	a"
+			echo "100644 $o0 0	b"
+			echo "100644 $o0 0	c"
+			echo "100644 $o0 0	d/e"
+			echo "100644 $o0 0	e"
+		) >expected &&
+		test_cmp expected actual
+	'
+fi
+
+
 test_done
diff --git a/t/t3032-merge-recursive-options.sh b/t/t3032-merge-recursive-options.sh
new file mode 100755
index 0000000..2b17311
--- /dev/null
+++ b/t/t3032-merge-recursive-options.sh
@@ -0,0 +1,203 @@
+#!/bin/sh
+
+test_description='merge-recursive options
+
+* [master] Clarify
+ ! [remote] Remove cruft
+--
+ + [remote] Remove cruft
+*  [master] Clarify
+*+ [remote^] Initial revision
+*   ok 1: setup
+'
+
+. ./test-lib.sh
+
+test_have_prereq SED_STRIPS_CR && SED_OPTIONS=-b
+test_have_prereq MINGW && export GREP_OPTIONS=-U
+
+test_expect_success 'setup' '
+	conflict_hunks () {
+		sed $SED_OPTIONS -n -e "
+			/^<<<</ b conflict
+			b
+			: conflict
+			p
+			/^>>>>/ b
+			n
+			b conflict
+		" "$@"
+	} &&
+
+	cat <<-\EOF >text.txt &&
+	    Hope, he says, cherishes the soul of him who lives in
+	    justice and holiness and is the nurse of his age and the
+	    companion of his journey;--hope which is mightiest to sway
+	    the restless soul of man.
+
+	How admirable are his words!  And the great blessing of riches, I do
+	not say to every man, but to a good man, is, that he has had no
+	occasion to deceive or to defraud others, either intentionally or
+	unintentionally; and when he departs to the world below he is not in
+	any apprehension about offerings due to the gods or debts which he owes
+	to men.  Now to this peace of mind the possession of wealth greatly
+	contributes; and therefore I say, that, setting one thing against
+	another, of the many advantages which wealth has to give, to a man of
+	sense this is in my opinion the greatest.
+
+	Well said, Cephalus, I replied; but as concerning justice, what is
+	it?--to speak the truth and to pay your debts--no more than this?  And
+	even to this are there not exceptions?  Suppose that a friend when in
+	his right mind has deposited arms with me and he asks for them when he
+	is not in his right mind, ought I to give them back to him?  No one
+	would say that I ought or that I should be right in doing so, any more
+	than they would say that I ought always to speak the truth to one who
+	is in his condition.
+
+	You are quite right, he replied.
+
+	But then, I said, speaking the truth and paying your debts is not a
+	correct definition of justice.
+
+	CEPHALUS - SOCRATES - POLEMARCHUS
+
+	Quite correct, Socrates, if Simonides is to be believed, said
+	Polemarchus interposing.
+
+	I fear, said Cephalus, that I must go now, for I have to look after the
+	sacrifices, and I hand over the argument to Polemarchus and the company.
+	EOF
+	git add text.txt &&
+	test_tick &&
+	git commit -m "Initial revision" &&
+
+	git checkout -b remote &&
+	sed -e "
+			s/\.  /\. /g
+			s/[?]  /? /g
+			s/    /	/g
+			s/--/---/g
+			s/but as concerning/but as con cerning/
+			/CEPHALUS - SOCRATES - POLEMARCHUS/ d
+		" text.txt >text.txt+ &&
+	mv text.txt+ text.txt &&
+	git commit -a -m "Remove cruft" &&
+
+	git checkout master &&
+	sed -e "
+			s/\(not in his right mind\),\(.*\)/\1;\2Q/
+			s/Quite correct\(.*\)/It is too correct\1Q/
+			s/unintentionally/un intentionally/
+			/un intentionally/ s/$/Q/
+			s/Polemarchus interposing./Polemarchus, interposing.Q/
+			/justice and holiness/ s/$/Q/
+			/pay your debts/ s/$/Q/
+		" text.txt | q_to_cr >text.txt+ &&
+	mv text.txt+ text.txt &&
+	git commit -a -m "Clarify" &&
+	git show-branch --all
+'
+
+test_expect_success 'naive merge fails' '
+	git read-tree --reset -u HEAD &&
+	test_must_fail git merge-recursive HEAD^ -- HEAD remote &&
+	test_must_fail git update-index --refresh &&
+	grep "<<<<<<" text.txt
+'
+
+test_expect_success '--ignore-space-change makes merge succeed' '
+	git read-tree --reset -u HEAD &&
+	git merge-recursive --ignore-space-change HEAD^ -- HEAD remote
+'
+
+test_expect_success 'naive cherry-pick fails' '
+	git read-tree --reset -u HEAD &&
+	test_must_fail git cherry-pick --no-commit remote &&
+	git read-tree --reset -u HEAD &&
+	test_must_fail git cherry-pick remote &&
+	test_must_fail git update-index --refresh &&
+	grep "<<<<<<" text.txt
+'
+
+test_expect_success '-Xignore-space-change makes cherry-pick succeed' '
+	git read-tree --reset -u HEAD &&
+	git cherry-pick --no-commit -Xignore-space-change remote
+'
+
+test_expect_success '--ignore-space-change: our w/s-only change wins' '
+	q_to_cr <<-\EOF >expected &&
+	    justice and holiness and is the nurse of his age and theQ
+	EOF
+
+	git read-tree --reset -u HEAD &&
+	git merge-recursive --ignore-space-change HEAD^ -- HEAD remote &&
+	grep "justice and holiness" text.txt >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success '--ignore-space-change: their real change wins over w/s' '
+	cat <<-\EOF >expected &&
+	it?---to speak the truth and to pay your debts---no more than this? And
+	EOF
+
+	git read-tree --reset -u HEAD &&
+	git merge-recursive --ignore-space-change HEAD^ -- HEAD remote &&
+	grep "pay your debts" text.txt >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success '--ignore-space-change: does not ignore new spaces' '
+	cat <<-\EOF >expected1 &&
+	Well said, Cephalus, I replied; but as con cerning justice, what is
+	EOF
+	q_to_cr <<-\EOF >expected2 &&
+	un intentionally; and when he departs to the world below he is not inQ
+	EOF
+
+	git read-tree --reset -u HEAD &&
+	git merge-recursive --ignore-space-change HEAD^ -- HEAD remote &&
+	grep "Well said" text.txt >actual1 &&
+	grep "when he departs" text.txt >actual2 &&
+	test_cmp expected1 actual1 &&
+	test_cmp expected2 actual2
+'
+
+test_expect_success '--ignore-all-space drops their new spaces' '
+	cat <<-\EOF >expected &&
+	Well said, Cephalus, I replied; but as concerning justice, what is
+	EOF
+
+	git read-tree --reset -u HEAD &&
+	git merge-recursive --ignore-all-space HEAD^ -- HEAD remote &&
+	grep "Well said" text.txt >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success '--ignore-all-space keeps our new spaces' '
+	q_to_cr <<-\EOF >expected &&
+	un intentionally; and when he departs to the world below he is not inQ
+	EOF
+
+	git read-tree --reset -u HEAD &&
+	git merge-recursive --ignore-all-space HEAD^ -- HEAD remote &&
+	grep "when he departs" text.txt >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success '--ignore-space-at-eol' '
+	q_to_cr <<-\EOF >expected &&
+	<<<<<<< HEAD
+	is not in his right mind; ought I to give them back to him?  No oneQ
+	=======
+	is not in his right mind, ought I to give them back to him? No one
+	>>>>>>> remote
+	EOF
+
+	git read-tree --reset -u HEAD &&
+	test_must_fail git merge-recursive --ignore-space-at-eol \
+						 HEAD^ -- HEAD remote &&
+	conflict_hunks text.txt >actual &&
+	test_cmp expected actual
+'
+
+test_done
diff --git a/t/t3040-subprojects-basic.sh b/t/t3040-subprojects-basic.sh
index f6973e9..0a4ff6d 100755
--- a/t/t3040-subprojects-basic.sh
+++ b/t/t3040-subprojects-basic.sh
@@ -3,81 +3,81 @@
 test_description='Basic subproject functionality'
 . ./test-lib.sh
 
-test_expect_success 'Super project creation' \
-    ': >Makefile &&
-    git add Makefile &&
-    git commit -m "Superproject created"'
+test_expect_success 'setup: create superproject' '
+	: >Makefile &&
+	git add Makefile &&
+	git commit -m "Superproject created"
+'
 
+test_expect_success 'setup: create subprojects' '
+	mkdir sub1 &&
+	( cd sub1 && git init && : >Makefile && git add * &&
+	git commit -q -m "subproject 1" ) &&
+	mkdir sub2 &&
+	( cd sub2 && git init && : >Makefile && git add * &&
+	git commit -q -m "subproject 2" ) &&
+	git update-index --add sub1 &&
+	git add sub2 &&
+	git commit -q -m "subprojects added" &&
+	git diff-tree --abbrev=5 HEAD^ HEAD |cut -d" " -f-3,5- >current &&
+	git branch save HEAD &&
+	cat >expected <<-\EOF &&
+	:000000 160000 00000... A	sub1
+	:000000 160000 00000... A	sub2
+	EOF
+	test_cmp expected current
+'
 
-cat >expected <<EOF
-:000000 160000 00000... A	sub1
-:000000 160000 00000... A	sub2
-EOF
-test_expect_success 'create subprojects' \
-    'mkdir sub1 &&
-    ( cd sub1 && git init && : >Makefile && git add * &&
-    git commit -q -m "subproject 1" ) &&
-    mkdir sub2 &&
-    ( cd sub2 && git init && : >Makefile && git add * &&
-    git commit -q -m "subproject 2" ) &&
-    git update-index --add sub1 &&
-    git add sub2 &&
-    git commit -q -m "subprojects added" &&
-    git diff-tree --abbrev=5 HEAD^ HEAD |cut -d" " -f-3,5- >current &&
-    test_cmp expected current'
+test_expect_success 'check if fsck ignores the subprojects' '
+	git fsck --full
+'
 
-git branch save HEAD
+test_expect_success 'check if commit in a subproject detected' '
+	( cd sub1 &&
+	echo "all:" >>Makefile &&
+	echo "	true" >>Makefile &&
+	git commit -q -a -m "make all" ) &&
+	test_expect_code 1 git diff-files --exit-code
+'
 
-test_expect_success 'check if fsck ignores the subprojects' \
-    'git fsck --full'
+test_expect_success 'check if a changed subproject HEAD can be committed' '
+	git commit -q -a -m "sub1 changed" &&
+	test_expect_code 1 git diff-tree --exit-code HEAD^ HEAD
+'
 
-test_expect_success 'check if commit in a subproject detected' \
-    '( cd sub1 &&
-    echo "all:" >>Makefile &&
-    echo "	true" >>Makefile &&
-    git commit -q -a -m "make all" ) && {
-        git diff-files --exit-code
-	test $? = 1
-    }'
+test_expect_success 'check if diff-index works for subproject elements' '
+	test_expect_code 1 git diff-index --exit-code --cached save -- sub1
+'
 
-test_expect_success 'check if a changed subproject HEAD can be committed' \
-    'git commit -q -a -m "sub1 changed" && {
-	git diff-tree --exit-code HEAD^ HEAD
-	test $? = 1
-    }'
+test_expect_success 'check if diff-tree works for subproject elements' '
+	test_expect_code 1 git diff-tree --exit-code HEAD^ HEAD -- sub1
+'
 
-test_expect_success 'check if diff-index works for subproject elements' \
-    'git diff-index --exit-code --cached save -- sub1
-    test $? = 1'
+test_expect_success 'check if git diff works for subproject elements' '
+	test_expect_code 1 git diff --exit-code HEAD^ HEAD
+'
 
-test_expect_success 'check if diff-tree works for subproject elements' \
-    'git diff-tree --exit-code HEAD^ HEAD -- sub1
-    test $? = 1'
+test_expect_success 'check if clone works' '
+	git ls-files -s >expected &&
+	git clone -l -s . cloned &&
+	( cd cloned && git ls-files -s ) >current &&
+	test_cmp expected current
+'
 
-test_expect_success 'check if git diff works for subproject elements' \
-    'git diff --exit-code HEAD^ HEAD
-    test $? = 1'
-
-test_expect_success 'check if clone works' \
-    'git ls-files -s >expected &&
-    git clone -l -s . cloned &&
-    ( cd cloned && git ls-files -s ) >current &&
-    test_cmp expected current'
-
-test_expect_success 'removing and adding subproject' \
-    'git update-index --force-remove -- sub2 &&
-    mv sub2 sub3 &&
-    git add sub3 &&
-    git commit -q -m "renaming a subproject" && {
-	git diff -M --name-status --exit-code HEAD^ HEAD
-	test $? = 1
-    }'
+test_expect_success 'removing and adding subproject' '
+	git update-index --force-remove -- sub2 &&
+	mv sub2 sub3 &&
+	git add sub3 &&
+	git commit -q -m "renaming a subproject" &&
+	test_expect_code 1 git diff -M --name-status --exit-code HEAD^ HEAD
+'
 
 # the index must contain the object name the HEAD of the
 # subproject sub1 was at the point "save"
-test_expect_success 'checkout in superproject' \
-    'git checkout save &&
-    git diff-index --exit-code --raw --cached save -- sub1'
+test_expect_success 'checkout in superproject' '
+	git checkout save &&
+	git diff-index --exit-code --raw --cached save -- sub1
+'
 
 # just interesting what happened...
 # git diff --name-status -M save master
diff --git a/t/t3050-subprojects-fetch.sh b/t/t3050-subprojects-fetch.sh
index 4261e96..2f5f41a 100755
--- a/t/t3050-subprojects-fetch.sh
+++ b/t/t3050-subprojects-fetch.sh
@@ -10,10 +10,10 @@
 		cd sub &&
 		git init &&
 		>subfile &&
-		git add subfile
+		git add subfile &&
 		git commit -m "subproject commit #1"
 	) &&
-	>mainfile
+	>mainfile &&
 	git add sub mainfile &&
 	test_tick &&
 	git commit -m "superproject commit #1"
diff --git a/t/t3060-ls-files-with-tree.sh b/t/t3060-ls-files-with-tree.sh
index 3ce501b..61c1f53 100755
--- a/t/t3060-ls-files-with-tree.sh
+++ b/t/t3060-ls-files-with-tree.sh
@@ -53,17 +53,15 @@
 	git add .
 '
 
-# We have to run from a sub-directory to trigger prune_path
-# Then we finally get to run our --with-tree test
-cd sub
-
 test_expect_success 'git -ls-files --with-tree should succeed from subdir' '
-
-	git ls-files --with-tree=HEAD~1 >../output
-
+	# We have to run from a sub-directory to trigger prune_path
+	# Then we finally get to run our --with-tree test
+	(
+		cd sub &&
+		git ls-files --with-tree=HEAD~1 >../output
+	)
 '
 
-cd ..
 test_expect_success \
     'git -ls-files --with-tree should add entries from named tree.' \
     'test_cmp expected output'
diff --git a/t/t3100-ls-tree-restrict.sh b/t/t3100-ls-tree-restrict.sh
index ee60d03..81d90b6 100755
--- a/t/t3100-ls-tree-restrict.sh
+++ b/t/t3100-ls-tree-restrict.sh
@@ -43,8 +43,6 @@
      tree=`git write-tree` &&
      echo $tree'
 
-_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
-_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
 test_output () {
     sed -e "s/ $_x40	/ X	/" <current >check
     test_cmp expected check
@@ -167,4 +165,13 @@
 EOF
      test_output'
 
+test_expect_success \
+    'ls-tree with one path a prefix of the other' \
+    'git ls-tree $tree path2/baz path2/bazbo >current &&
+     make_expected <<\EOF &&
+040000 tree X	path2/baz
+120000 blob X	path2/bazbo
+EOF
+     test_output'
+
 test_done
diff --git a/t/t3101-ls-tree-dirname.sh b/t/t3101-ls-tree-dirname.sh
index 51cb4a3..026f9f8 100755
--- a/t/t3101-ls-tree-dirname.sh
+++ b/t/t3101-ls-tree-dirname.sh
@@ -21,35 +21,32 @@
 '
 . ./test-lib.sh
 
-test_expect_success \
-    'setup' \
-    'echo 111 >1.txt &&
-     echo 222 >2.txt &&
-     mkdir path0 path0/a path0/a/b path0/a/b/c &&
-     echo 111 >path0/a/b/c/1.txt &&
-     mkdir path1 path1/b path1/b/c &&
-     echo 111 >path1/b/c/1.txt &&
-     mkdir path2 &&
-     echo 111 >path2/1.txt &&
-     mkdir path3 &&
-     echo 111 >path3/1.txt &&
-     echo 222 >path3/2.txt &&
-     find *.txt path* \( -type f -o -type l \) -print |
-     xargs git update-index --add &&
-     tree=`git write-tree` &&
-     echo $tree'
+test_expect_success 'setup' '
+	echo 111 >1.txt &&
+	echo 222 >2.txt &&
+	mkdir path0 path0/a path0/a/b path0/a/b/c &&
+	echo 111 >path0/a/b/c/1.txt &&
+	mkdir path1 path1/b path1/b/c &&
+	echo 111 >path1/b/c/1.txt &&
+	mkdir path2 &&
+	echo 111 >path2/1.txt &&
+	mkdir path3 &&
+	echo 111 >path3/1.txt &&
+	echo 222 >path3/2.txt &&
+	find *.txt path* \( -type f -o -type l \) -print |
+	xargs git update-index --add &&
+	tree=`git write-tree` &&
+	echo $tree
+'
 
-_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
-_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
 test_output () {
-    sed -e "s/ $_x40	/ X	/" <current >check
-    test_cmp expected check
+	sed -e "s/ $_x40	/ X	/" <current >check &&
+	test_cmp expected check
 }
 
-test_expect_success \
-    'ls-tree plain' \
-    'git ls-tree $tree >current &&
-     cat >expected <<\EOF &&
+test_expect_success 'ls-tree plain' '
+	git ls-tree $tree >current &&
+	cat >expected <<\EOF &&
 100644 blob X	1.txt
 100644 blob X	2.txt
 040000 tree X	path0
@@ -57,13 +54,13 @@
 040000 tree X	path2
 040000 tree X	path3
 EOF
-     test_output'
+	test_output
+'
 
 # Recursive does not show tree nodes anymore...
-test_expect_success \
-    'ls-tree recursive' \
-    'git ls-tree -r $tree >current &&
-     cat >expected <<\EOF &&
+test_expect_success 'ls-tree recursive' '
+	git ls-tree -r $tree >current &&
+	cat >expected <<\EOF &&
 100644 blob X	1.txt
 100644 blob X	2.txt
 100644 blob X	path0/a/b/c/1.txt
@@ -72,68 +69,71 @@
 100644 blob X	path3/1.txt
 100644 blob X	path3/2.txt
 EOF
-     test_output'
+	test_output
+'
 
-test_expect_success \
-    'ls-tree filter 1.txt' \
-    'git ls-tree $tree 1.txt >current &&
-     cat >expected <<\EOF &&
+test_expect_success 'ls-tree filter 1.txt' '
+	git ls-tree $tree 1.txt >current &&
+	cat >expected <<\EOF &&
 100644 blob X	1.txt
 EOF
-     test_output'
+	test_output
+'
 
-test_expect_success \
-    'ls-tree filter path1/b/c/1.txt' \
-    'git ls-tree $tree path1/b/c/1.txt >current &&
-     cat >expected <<\EOF &&
+test_expect_success 'ls-tree filter path1/b/c/1.txt' '
+	git ls-tree $tree path1/b/c/1.txt >current &&
+	cat >expected <<\EOF &&
 100644 blob X	path1/b/c/1.txt
 EOF
-     test_output'
+	test_output
+'
 
-test_expect_success \
-    'ls-tree filter all 1.txt files' \
-    'git ls-tree $tree 1.txt path0/a/b/c/1.txt path1/b/c/1.txt path2/1.txt path3/1.txt >current &&
-     cat >expected <<\EOF &&
+test_expect_success 'ls-tree filter all 1.txt files' '
+	git ls-tree $tree 1.txt path0/a/b/c/1.txt \
+		path1/b/c/1.txt path2/1.txt path3/1.txt >current &&
+	cat >expected <<\EOF &&
 100644 blob X	1.txt
 100644 blob X	path0/a/b/c/1.txt
 100644 blob X	path1/b/c/1.txt
 100644 blob X	path2/1.txt
 100644 blob X	path3/1.txt
 EOF
-     test_output'
+	test_output
+'
 
 # I am not so sure about this one after ls-tree doing pathspec match.
 # Having both path0/a and path0/a/b/c makes path0/a redundant, and
 # it behaves as if path0/a/b/c, path1/b/c, path2 and path3 are specified.
-test_expect_success \
-    'ls-tree filter directories' \
-    'git ls-tree $tree path3 path2 path0/a/b/c path1/b/c path0/a >current &&
-     cat >expected <<\EOF &&
+test_expect_success 'ls-tree filter directories' '
+	git ls-tree $tree path3 path2 path0/a/b/c path1/b/c path0/a >current &&
+	cat >expected <<\EOF &&
 040000 tree X	path0/a/b/c
 040000 tree X	path1/b/c
 040000 tree X	path2
 040000 tree X	path3
 EOF
-     test_output'
+	test_output
+'
 
 # Again, duplicates are filtered away so this is equivalent to
 # having 1.txt and path3
-test_expect_success \
-    'ls-tree filter odd names' \
-    'git ls-tree $tree 1.txt ./1.txt .//1.txt path3/1.txt path3/./1.txt path3 path3// >current &&
-     cat >expected <<\EOF &&
+test_expect_success 'ls-tree filter odd names' '
+	git ls-tree $tree 1.txt ./1.txt .//1.txt \
+		path3/1.txt path3/./1.txt path3 path3// >current &&
+	cat >expected <<\EOF &&
 100644 blob X	1.txt
 100644 blob X	path3/1.txt
 100644 blob X	path3/2.txt
 EOF
-     test_output'
+	test_output
+'
 
-test_expect_success \
-    'ls-tree filter missing files and extra slashes' \
-    'git ls-tree $tree 1.txt/ abc.txt path3//23.txt path3/2.txt/// >current &&
-     cat >expected <<\EOF &&
-EOF
-     test_output'
+test_expect_success 'ls-tree filter missing files and extra slashes' '
+	git ls-tree $tree 1.txt/ abc.txt \
+		path3//23.txt path3/2.txt/// >current &&
+	>expected &&
+	test_output
+'
 
 test_expect_success 'ls-tree filter is leading path match' '
 	git ls-tree $tree pa path3/a >current &&
@@ -141,4 +141,89 @@
 	test_output
 '
 
+test_expect_success 'ls-tree --full-name' '
+	(
+		cd path0 &&
+		git ls-tree --full-name $tree a
+	) >current &&
+	cat >expected <<\EOF &&
+040000 tree X	path0/a
+EOF
+	test_output
+'
+
+test_expect_success 'ls-tree --full-tree' '
+	(
+		cd path1/b/c &&
+		git ls-tree --full-tree $tree
+	) >current &&
+	cat >expected <<\EOF &&
+100644 blob X	1.txt
+100644 blob X	2.txt
+040000 tree X	path0
+040000 tree X	path1
+040000 tree X	path2
+040000 tree X	path3
+EOF
+	test_output
+'
+
+test_expect_success 'ls-tree --full-tree -r' '
+	(
+		cd path3/ &&
+		git ls-tree --full-tree -r $tree
+	) >current &&
+	cat >expected <<\EOF &&
+100644 blob X	1.txt
+100644 blob X	2.txt
+100644 blob X	path0/a/b/c/1.txt
+100644 blob X	path1/b/c/1.txt
+100644 blob X	path2/1.txt
+100644 blob X	path3/1.txt
+100644 blob X	path3/2.txt
+EOF
+	test_output
+'
+
+test_expect_success 'ls-tree --abbrev=5' '
+	git ls-tree --abbrev=5 $tree >current &&
+	sed -e "s/ $_x05[0-9a-f]*	/ X	/" <current >check &&
+	cat >expected <<\EOF &&
+100644 blob X	1.txt
+100644 blob X	2.txt
+040000 tree X	path0
+040000 tree X	path1
+040000 tree X	path2
+040000 tree X	path3
+EOF
+	test_cmp expected check
+'
+
+test_expect_success 'ls-tree --name-only' '
+	git ls-tree --name-only $tree >current &&
+	cat >expected <<\EOF &&
+1.txt
+2.txt
+path0
+path1
+path2
+path3
+EOF
+	test_output
+'
+
+test_expect_success 'ls-tree --name-only -r' '
+	git ls-tree --name-only -r $tree >current &&
+	cat >expected <<\EOF &&
+1.txt
+2.txt
+path0/a/b/c/1.txt
+path1/b/c/1.txt
+path2/1.txt
+path3/1.txt
+path3/2.txt
+EOF
+	test_output
+'
+
 test_done
diff --git a/t/t3102-ls-tree-wildcards.sh b/t/t3102-ls-tree-wildcards.sh
new file mode 100755
index 0000000..c286854
--- /dev/null
+++ b/t/t3102-ls-tree-wildcards.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+test_description='ls-tree with(out) globs'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	mkdir a aa "a[a]" &&
+	touch a/one aa/two "a[a]/three" &&
+	git add a/one aa/two "a[a]/three" &&
+	git commit -m test
+'
+
+test_expect_success 'ls-tree a[a] matches literally' '
+	cat >expected <<EOF &&
+100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391	a[a]/three
+EOF
+	git ls-tree -r HEAD "a[a]" >actual &&
+	test_cmp expected actual
+'
+
+test_done
diff --git a/t/t3103-ls-tree-misc.sh b/t/t3103-ls-tree-misc.sh
new file mode 100755
index 0000000..09dcf04
--- /dev/null
+++ b/t/t3103-ls-tree-misc.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+test_description='
+Miscellaneous tests for git ls-tree.
+
+	      1. git ls-tree fails in presence of tree damage.
+
+'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	mkdir a &&
+	touch a/one &&
+	git add a/one &&
+	git commit -m test
+'
+
+test_expect_success 'ls-tree fails with non-zero exit code on broken tree' '
+	rm -f .git/objects/5f/cffbd6e4c5c5b8d81f5e9314b20e338e3ffff5 &&
+	test_must_fail git ls-tree -r HEAD
+'
+
+test_done
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index d59a9b4..a17f8b2 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -3,11 +3,8 @@
 # Copyright (c) 2005 Amos Waterland
 #
 
-test_description='git branch --foo should not create bogus branch
+test_description='git branch assorted tests'
 
-This test runs git branch --help and checks that the argument is properly
-handled.  Specifically, that a bogus branch is not created.
-'
 . ./test-lib.sh
 
 test_expect_success \
@@ -22,34 +19,45 @@
 
 test_expect_success \
     'git branch --help should not have created a bogus branch' '
-     git branch --help </dev/null >/dev/null 2>/dev/null;
-     ! test -f .git/refs/heads/--help
+     test_might_fail git branch --help </dev/null >/dev/null 2>/dev/null &&
+     test_path_is_missing .git/refs/heads/--help
+'
+
+test_expect_success 'branch -h in broken repository' '
+	mkdir broken &&
+	(
+		cd broken &&
+		git init &&
+		>.git/refs/heads/master &&
+		test_expect_code 129 git branch -h >usage 2>&1
+	) &&
+	grep "[Uu]sage" broken/usage
 '
 
 test_expect_success \
     'git branch abc should create a branch' \
-    'git branch abc && test -f .git/refs/heads/abc'
+    'git branch abc && test_path_is_file .git/refs/heads/abc'
 
 test_expect_success \
     'git branch a/b/c should create a branch' \
-    'git branch a/b/c && test -f .git/refs/heads/a/b/c'
+    'git branch a/b/c && test_path_is_file .git/refs/heads/a/b/c'
 
 cat >expect <<EOF
-0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000	branch: Created from master
+$_z40 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000	branch: Created from master
 EOF
 test_expect_success \
     'git branch -l d/e/f should create a branch and a log' \
 	'GIT_COMMITTER_DATE="2005-05-26 23:30" \
      git branch -l d/e/f &&
-	 test -f .git/refs/heads/d/e/f &&
-	 test -f .git/logs/refs/heads/d/e/f &&
-	 diff expect .git/logs/refs/heads/d/e/f'
+	 test_path_is_file .git/refs/heads/d/e/f &&
+	 test_path_is_file .git/logs/refs/heads/d/e/f &&
+	 test_cmp expect .git/logs/refs/heads/d/e/f'
 
 test_expect_success \
     'git branch -d d/e/f should delete a branch and a log' \
 	'git branch -d d/e/f &&
-	 test ! -f .git/refs/heads/d/e/f &&
-	 test ! -f .git/logs/refs/heads/d/e/f'
+	 test_path_is_missing .git/refs/heads/d/e/f &&
+	 test_path_is_missing .git/logs/refs/heads/d/e/f'
 
 test_expect_success \
     'git branch j/k should work after branch j has been deleted' \
@@ -64,16 +72,21 @@
         git branch l'
 
 test_expect_success \
+    'git branch -m dumps usage' \
+       'test_expect_code 129 git branch -m 2>err &&
+	grep "[Uu]sage: git branch" err'
+
+test_expect_success \
     'git branch -m m m/m should work' \
        'git branch -l m &&
         git branch -m m m/m &&
-        test -f .git/logs/refs/heads/m/m'
+	test_path_is_file .git/logs/refs/heads/m/m'
 
 test_expect_success \
     'git branch -m n/n n should work' \
        'git branch -l n/n &&
-        git branch -m n/n n
-        test -f .git/logs/refs/heads/n'
+	git branch -m n/n n &&
+	test_path_is_file .git/logs/refs/heads/n'
 
 test_expect_success 'git branch -m o/o o should fail when o/p exists' '
 	git branch o/o &&
@@ -87,6 +100,143 @@
 	test_must_fail git branch -m q r/q
 '
 
+test_expect_success 'git branch -M foo bar should fail when bar is checked out' '
+	git branch bar &&
+	git checkout -b foo &&
+	test_must_fail git branch -M bar foo
+'
+
+test_expect_success 'git branch -M baz bam should succeed when baz is checked out' '
+	git checkout -b baz &&
+	git branch bam &&
+	git branch -M baz bam
+'
+
+test_expect_success 'git branch -M master should work when master is checked out' '
+	git checkout master &&
+	git branch -M master
+'
+
+test_expect_success 'git branch -M master master should work when master is checked out' '
+	git checkout master &&
+	git branch -M master master
+'
+
+test_expect_success 'git branch -M master2 master2 should work when master is checked out' '
+	git checkout master &&
+	git branch master2 &&
+	git branch -M master2 master2
+'
+
+test_expect_success 'git branch -v -d t should work' '
+	git branch t &&
+	test_path_is_file .git/refs/heads/t &&
+	git branch -v -d t &&
+	test_path_is_missing .git/refs/heads/t
+'
+
+test_expect_success 'git branch -v -m t s should work' '
+	git branch t &&
+	test_path_is_file .git/refs/heads/t &&
+	git branch -v -m t s &&
+	test_path_is_missing .git/refs/heads/t &&
+	test_path_is_file .git/refs/heads/s &&
+	git branch -d s
+'
+
+test_expect_success 'git branch -m -d t s should fail' '
+	git branch t &&
+	test_path_is_file .git/refs/heads/t &&
+	test_must_fail git branch -m -d t s &&
+	git branch -d t &&
+	test_path_is_missing .git/refs/heads/t
+'
+
+test_expect_success 'git branch --list -d t should fail' '
+	git branch t &&
+	test_path_is_file .git/refs/heads/t &&
+	test_must_fail git branch --list -d t &&
+	git branch -d t &&
+	test_path_is_missing .git/refs/heads/t
+'
+
+test_expect_success 'git branch --column' '
+	COLUMNS=81 git branch --column=column >actual &&
+	cat >expected <<\EOF &&
+  a/b/c     bam       foo       l       * master    n         o/p       r
+  abc       bar       j/k       m/m       master2   o/o       q
+EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'git branch --column with an extremely long branch name' '
+	long=this/is/a/part/of/long/branch/name &&
+	long=z$long/$long/$long/$long &&
+	test_when_finished "git branch -d $long" &&
+	git branch $long &&
+	COLUMNS=80 git branch --column=column >actual &&
+	cat >expected <<EOF &&
+  a/b/c
+  abc
+  bam
+  bar
+  foo
+  j/k
+  l
+  m/m
+* master
+  master2
+  n
+  o/o
+  o/p
+  q
+  r
+  $long
+EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'git branch with column.*' '
+	git config column.ui column &&
+	git config column.branch "dense" &&
+	COLUMNS=80 git branch >actual &&
+	git config --unset column.branch &&
+	git config --unset column.ui &&
+	cat >expected <<\EOF &&
+  a/b/c   bam   foo   l   * master    n     o/p   r
+  abc     bar   j/k   m/m   master2   o/o   q
+EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'git branch --column -v should fail' '
+	test_must_fail git branch --column -v
+'
+
+test_expect_success 'git branch -v with column.ui ignored' '
+	git config column.ui column &&
+	COLUMNS=80 git branch -v | cut -c -10 | sed "s/ *$//" >actual &&
+	git config --unset column.ui &&
+	cat >expected <<\EOF &&
+  a/b/c
+  abc
+  bam
+  bar
+  foo
+  j/k
+  l
+  m/m
+* master
+  master2
+  n
+  o/o
+  o/p
+  q
+  r
+EOF
+	test_cmp expected actual
+'
+
 mv .git/config .git/config-saved
 
 test_expect_success 'git branch -m q q2 without config should succeed' '
@@ -101,12 +251,12 @@
 test_expect_success \
     'git branch -m s/s s should work when s/t is deleted' \
        'git branch -l s/s &&
-        test -f .git/logs/refs/heads/s/s &&
+	test_path_is_file .git/logs/refs/heads/s/s &&
         git branch -l s/t &&
-        test -f .git/logs/refs/heads/s/t &&
+	test_path_is_file .git/logs/refs/heads/s/t &&
         git branch -d s/t &&
         git branch -m s/s s &&
-        test -f .git/logs/refs/heads/s'
+	test_path_is_file .git/logs/refs/heads/s'
 
 test_expect_success 'config information was renamed, too' \
 	"test $(git config branch.s.dummy) = Hello &&
@@ -117,8 +267,8 @@
 	git symbolic-ref refs/heads/master2 refs/heads/master &&
 	test_must_fail git branch -m master2 master3 &&
 	git symbolic-ref refs/heads/master2 &&
-	test -f .git/refs/heads/master &&
-	! test -f .git/refs/heads/master3
+	test_path_is_file .git/refs/heads/master &&
+	test_path_is_missing .git/refs/heads/master3
 '
 
 test_expect_success SYMLINKS \
@@ -195,7 +345,9 @@
 test_expect_success 'test deleting branch without config' \
     'git branch my7 s &&
      sha1=$(git rev-parse my7 | cut -c 1-7) &&
-     test "$(git branch -d my7 2>&1)" = "Deleted branch my7 (was $sha1)."'
+     echo "Deleted branch my7 (was $sha1)." >expect &&
+     git branch -d my7 >actual 2>&1 &&
+     test_i18ncmp expect actual'
 
 test_expect_success 'test --track without .fetch entries' \
     'git branch --track my8 &&
@@ -212,17 +364,43 @@
     'branch from non-branch HEAD w/--track causes failure' \
     'test_must_fail git branch --track my10 HEAD^'
 
+test_expect_success \
+    'branch from tag w/--track causes failure' \
+    'git tag foobar &&
+     test_must_fail git branch --track my11 foobar'
+
 # Keep this test last, as it changes the current branch
 cat >expect <<EOF
-0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000	branch: Created from master
+$_z40 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000	branch: Created from master
 EOF
 test_expect_success \
     'git checkout -b g/h/i -l should create a branch and a log' \
 	'GIT_COMMITTER_DATE="2005-05-26 23:30" \
      git checkout -b g/h/i -l master &&
-	 test -f .git/refs/heads/g/h/i &&
-	 test -f .git/logs/refs/heads/g/h/i &&
-	 diff expect .git/logs/refs/heads/g/h/i'
+	 test_path_is_file .git/refs/heads/g/h/i &&
+	 test_path_is_file .git/logs/refs/heads/g/h/i &&
+	 test_cmp expect .git/logs/refs/heads/g/h/i'
+
+test_expect_success 'checkout -b makes reflog by default' '
+	git checkout master &&
+	git config --unset core.logAllRefUpdates &&
+	git checkout -b alpha &&
+	git rev-parse --verify alpha@{0}
+'
+
+test_expect_success 'checkout -b does not make reflog when core.logAllRefUpdates = false' '
+	git checkout master &&
+	git config core.logAllRefUpdates false &&
+	git checkout -b beta &&
+	test_must_fail git rev-parse --verify beta@{0}
+'
+
+test_expect_success 'checkout -b with -l makes reflog when core.logAllRefUpdates = false' '
+	git checkout master &&
+	git checkout -lb gamma &&
+	git config --unset core.logAllRefUpdates &&
+	git rev-parse --verify gamma@{0}
+'
 
 test_expect_success 'avoid ambiguous track' '
 	git config branch.autosetupmerge true &&
@@ -456,6 +634,15 @@
 	test "z$(git config branch.myr20.rebase)" = z
 '
 
+test_expect_success 'autosetuprebase always on detached HEAD' '
+	git config branch.autosetupmerge always &&
+	test_when_finished git checkout master &&
+	git checkout HEAD^0 &&
+	git branch my11 &&
+	test -z "$(git config branch.my11.remote)" &&
+	test -z "$(git config branch.my11.merge)"
+'
+
 test_expect_success 'detect misconfigured autosetuprebase (bad value)' '
 	git config branch.autosetuprebase garbage &&
 	test_must_fail git branch
@@ -468,4 +655,83 @@
 	git config --unset branch.autosetuprebase
 '
 
+test_expect_success 'attempt to delete a branch without base and unmerged to HEAD' '
+	git checkout my9 &&
+	git config --unset branch.my8.merge &&
+	test_must_fail git branch -d my8
+'
+
+test_expect_success 'attempt to delete a branch merged to its base' '
+	# we are on my9 which is the initial commit; traditionally
+	# we would not have allowed deleting my8 that is not merged
+	# to my9, but it is set to track master that already has my8
+	git config branch.my8.merge refs/heads/master &&
+	git branch -d my8
+'
+
+test_expect_success 'attempt to delete a branch merged to its base' '
+	git checkout master &&
+	echo Third >>A &&
+	git commit -m "Third commit" A &&
+	git branch -t my10 my9 &&
+	git branch -f my10 HEAD^ &&
+	# we are on master which is at the third commit, and my10
+	# is behind us, so traditionally we would have allowed deleting
+	# it; but my10 is set to track my9 that is further behind.
+	test_must_fail git branch -d my10
+'
+
+test_expect_success 'use set-upstream on the current branch' '
+	git checkout master &&
+	git --bare init myupstream.git &&
+	git push myupstream.git master:refs/heads/frotz &&
+	git remote add origin myupstream.git &&
+	git fetch &&
+	git branch --set-upstream master origin/frotz &&
+
+	test "z$(git config branch.master.remote)" = "zorigin" &&
+	test "z$(git config branch.master.merge)" = "zrefs/heads/frotz"
+
+'
+
+test_expect_success 'use --edit-description' '
+	write_script editor <<-\EOF &&
+		echo "New contents" >"$1"
+	EOF
+	EDITOR=./editor git branch --edit-description &&
+		write_script editor <<-\EOF &&
+		git stripspace -s <"$1" >"EDITOR_OUTPUT"
+	EOF
+	EDITOR=./editor git branch --edit-description &&
+	echo "New contents" >expect &&
+	test_cmp EDITOR_OUTPUT expect
+'
+
+test_expect_success 'detect typo in branch name when using --edit-description' '
+	write_script editor <<-\EOF &&
+		echo "New contents" >"$1"
+	EOF
+	(
+		EDITOR=./editor &&
+		export EDITOR &&
+		test_must_fail git branch --edit-description no-such-branch
+	)
+'
+
+test_expect_success 'refuse --edit-description on unborn branch for now' '
+	write_script editor <<-\EOF &&
+		echo "New contents" >"$1"
+	EOF
+	git checkout --orphan unborn &&
+	(
+		EDITOR=./editor &&
+		export EDITOR &&
+		test_must_fail git branch --edit-description
+	)
+'
+
+test_expect_success '--merged catches invalid object names' '
+	test_must_fail git branch --merged 0000000000000000000000000000000000000000
+'
+
 test_done
diff --git a/t/t3202-show-branch-octopus.sh b/t/t3202-show-branch-octopus.sh
index 7fe4a6e..0a5d5e6 100755
--- a/t/t3202-show-branch-octopus.sh
+++ b/t/t3202-show-branch-octopus.sh
@@ -56,4 +56,12 @@
 
 '
 
+test_expect_success 'show-branch with showbranch.default' '
+	for i in $numbers; do
+		git config --add showbranch.default branch$i
+	done &&
+	git show-branch >out &&
+	test_cmp expect out
+'
+
 test_done
diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
index 809d1c4..76fe7e0 100755
--- a/t/t3203-branch-output.sh
+++ b/t/t3203-branch-output.sh
@@ -12,13 +12,13 @@
 '
 
 test_expect_success 'make branches' '
-	git branch branch-one
+	git branch branch-one &&
 	git branch branch-two HEAD^
 '
 
 test_expect_success 'make remote branches' '
-	git update-ref refs/remotes/origin/branch-one branch-one
-	git update-ref refs/remotes/origin/branch-two branch-two
+	git update-ref refs/remotes/origin/branch-one branch-one &&
+	git update-ref refs/remotes/origin/branch-two branch-two &&
 	git symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/branch-one
 '
 
@@ -32,6 +32,20 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'git branch --list shows local branches' '
+	git branch --list >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+  branch-one
+  branch-two
+EOF
+test_expect_success 'git branch --list pattern shows matching local branches' '
+	git branch --list branch* >actual &&
+	test_cmp expect actual
+'
+
 cat >expect <<'EOF'
   origin/HEAD -> origin/branch-one
   origin/branch-one
@@ -67,6 +81,20 @@
 '
 
 cat >expect <<'EOF'
+two
+one
+EOF
+test_expect_success 'git branch --list -v pattern shows branch summaries' '
+	git branch --list -v branch* >tmp &&
+	awk "{print \$NF}" <tmp >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git branch -v pattern does not show branch summaries' '
+	test_must_fail git branch -v branch*
+'
+
+cat >expect <<'EOF'
 * (no branch)
   branch-one
   branch-two
@@ -75,7 +103,7 @@
 test_expect_success 'git branch shows detached HEAD properly' '
 	git checkout HEAD^0 &&
 	git branch >actual &&
-	test_cmp expect actual
+	test_i18ncmp expect actual
 '
 
 test_done
diff --git a/t/t3210-pack-refs.sh b/t/t3210-pack-refs.sh
index 413019a..cd04361 100755
--- a/t/t3210-pack-refs.sh
+++ b/t/t3210-pack-refs.sh
@@ -28,7 +28,7 @@
      SHA1=`cat .git/refs/heads/a` &&
      echo "$SHA1 refs/heads/a" >expect &&
      git show-ref a >result &&
-     diff expect result'
+     test_cmp expect result'
 
 test_expect_success \
     'see if a branch still exists when packed' \
@@ -37,7 +37,7 @@
      rm -f .git/refs/heads/b &&
      echo "$SHA1 refs/heads/b" >expect &&
      git show-ref b >result &&
-     diff expect result'
+     test_cmp expect result'
 
 test_expect_success 'git branch c/d should barf if branch c exists' '
      git branch c &&
@@ -52,7 +52,7 @@
      git pack-refs --all --prune &&
      echo "$SHA1 refs/heads/e" >expect &&
      git show-ref e >result &&
-     diff expect result'
+     test_cmp expect result'
 
 test_expect_success 'see if git pack-refs --prune remove ref files' '
      git branch f &&
@@ -60,6 +60,12 @@
      ! test -f .git/refs/heads/f
 '
 
+test_expect_success 'see if git pack-refs --prune removes empty dirs' '
+     git branch r/s/t &&
+     git pack-refs --all --prune &&
+     ! test -e .git/refs/heads/r
+'
+
 test_expect_success \
     'git branch g should work when git branch g/h has been deleted' \
     'git branch g/h &&
@@ -109,7 +115,7 @@
 	git show-ref >all-of-them &&
 	git pack-refs &&
 	git show-ref >again &&
-	diff all-of-them again
+	test_cmp all-of-them again
 '
 
 test_done
diff --git a/t/t3300-funny-names.sh b/t/t3300-funny-names.sh
index db46d53..1f35e55 100755
--- a/t/t3300-funny-names.sh
+++ b/t/t3300-funny-names.sh
@@ -15,146 +15,204 @@
 p1='tabs	," (dq) and spaces'
 p2='just space'
 
-cat >"$p0" <<\EOF
-1. A quick brown fox jumps over the lazy cat, oops dog.
-2. A quick brown fox jumps over the lazy cat, oops dog.
-3. A quick brown fox jumps over the lazy cat, oops dog.
-EOF
+test_expect_success 'setup' '
+	cat >"$p0" <<-\EOF &&
+	1. A quick brown fox jumps over the lazy cat, oops dog.
+	2. A quick brown fox jumps over the lazy cat, oops dog.
+	3. A quick brown fox jumps over the lazy cat, oops dog.
+	EOF
 
-cat 2>/dev/null >"$p1" "$p0"
-echo 'Foo Bar Baz' >"$p2"
+	{ cat "$p0" >"$p1" || :; } &&
+	{ echo "Foo Bar Baz" >"$p2" || :; } &&
 
-test -f "$p1" && cmp "$p0" "$p1" || {
+	if test -f "$p1" && cmp "$p0" "$p1"
+	then
+		test_set_prereq TABS_IN_FILENAMES
+	fi
+'
+
+if ! test_have_prereq TABS_IN_FILENAMES
+then
 	# since FAT/NTFS does not allow tabs in filenames, skip this test
-	say 'Your filesystem does not allow tabs in filenames, test skipped.'
+	skip_all='Your filesystem does not allow tabs in filenames'
 	test_done
-}
+fi
 
-echo 'just space
-no-funny' >expected
-test_expect_success 'git ls-files no-funny' \
-	'git update-index --add "$p0" "$p2" &&
+test_expect_success 'setup: populate index and tree' '
+	git update-index --add "$p0" "$p2" &&
+	t0=$(git write-tree)
+'
+
+test_expect_success 'ls-files prints space in filename verbatim' '
+	printf "%s\n" "just space" no-funny >expected &&
 	git ls-files >current &&
-	test_cmp expected current'
+	test_cmp expected current
+'
 
-t0=`git write-tree`
-echo "$t0" >t0
+test_expect_success 'setup: add funny filename' '
+	git update-index --add "$p1" &&
+	t1=$(git write-tree)
+'
 
-cat > expected <<\EOF
-just space
-no-funny
-"tabs\t,\" (dq) and spaces"
-EOF
-test_expect_success 'git ls-files with-funny' \
-	'git update-index --add "$p1" &&
+test_expect_success 'ls-files quotes funny filename' '
+	cat >expected <<-\EOF &&
+	just space
+	no-funny
+	"tabs\t,\" (dq) and spaces"
+	EOF
 	git ls-files >current &&
-	test_cmp expected current'
+	test_cmp expected current
+'
 
-echo 'just space
-no-funny
-tabs	," (dq) and spaces' >expected
-test_expect_success 'git ls-files -z with-funny' \
-	'git ls-files -z | perl -pe y/\\000/\\012/ >current &&
-	test_cmp expected current'
+test_expect_success 'ls-files -z does not quote funny filename' '
+	cat >expected <<-\EOF &&
+	just space
+	no-funny
+	tabs	," (dq) and spaces
+	EOF
+	git ls-files -z >ls-files.z &&
+	"$PERL_PATH" -pe "y/\000/\012/" <ls-files.z >current &&
+	test_cmp expected current
+'
 
-t1=`git write-tree`
-echo "$t1" >t1
+test_expect_success 'ls-tree quotes funny filename' '
+	cat >expected <<-\EOF &&
+	just space
+	no-funny
+	"tabs\t,\" (dq) and spaces"
+	EOF
+	git ls-tree -r $t1 >ls-tree &&
+	sed -e "s/^[^	]*	//" <ls-tree >current &&
+	test_cmp expected current
+'
 
-cat > expected <<\EOF
-just space
-no-funny
-"tabs\t,\" (dq) and spaces"
-EOF
-test_expect_success 'git ls-tree with funny' \
-	'git ls-tree -r $t1 | sed -e "s/^[^	]*	//" >current &&
-	 test_cmp expected current'
+test_expect_success 'diff-index --name-status quotes funny filename' '
+	cat >expected <<-\EOF &&
+	A	"tabs\t,\" (dq) and spaces"
+	EOF
+	git diff-index --name-status $t0 >current &&
+	test_cmp expected current
+'
 
-cat > expected <<\EOF
-A	"tabs\t,\" (dq) and spaces"
-EOF
-test_expect_success 'git diff-index with-funny' \
-	'git diff-index --name-status $t0 >current &&
-	test_cmp expected current'
+test_expect_success 'diff-tree --name-status quotes funny filename' '
+	cat >expected <<-\EOF &&
+	A	"tabs\t,\" (dq) and spaces"
+	EOF
+	git diff-tree --name-status $t0 $t1 >current &&
+	test_cmp expected current
+'
 
-test_expect_success 'git diff-tree with-funny' \
-	'git diff-tree --name-status $t0 $t1 >current &&
-	test_cmp expected current'
+test_expect_success 'diff-index -z does not quote funny filename' '
+	cat >expected <<-\EOF &&
+	A
+	tabs	," (dq) and spaces
+	EOF
+	git diff-index -z --name-status $t0 >diff-index.z &&
+	"$PERL_PATH" -pe "y/\000/\012/" <diff-index.z >current &&
+	test_cmp expected current
+'
 
-echo 'A
-tabs	," (dq) and spaces' >expected
-test_expect_success 'git diff-index -z with-funny' \
-	'git diff-index -z --name-status $t0 | perl -pe y/\\000/\\012/ >current &&
-	test_cmp expected current'
+test_expect_success 'diff-tree -z does not quote funny filename' '
+	cat >expected <<-\EOF &&
+	A
+	tabs	," (dq) and spaces
+	EOF
+	git diff-tree -z --name-status $t0 $t1 >diff-tree.z &&
+	"$PERL_PATH" -pe y/\\000/\\012/ <diff-tree.z >current &&
+	test_cmp expected current
+'
 
-test_expect_success 'git diff-tree -z with-funny' \
-	'git diff-tree -z --name-status $t0 $t1 | perl -pe y/\\000/\\012/ >current &&
-	test_cmp expected current'
+test_expect_success 'diff-tree --find-copies-harder quotes funny filename' '
+	cat >expected <<-\EOF &&
+	CNUM	no-funny	"tabs\t,\" (dq) and spaces"
+	EOF
+	git diff-tree -C --find-copies-harder --name-status $t0 $t1 >out &&
+	sed -e "s/^C[0-9]*/CNUM/" <out >current &&
+	test_cmp expected current
+'
 
-cat > expected <<\EOF
-CNUM	no-funny	"tabs\t,\" (dq) and spaces"
-EOF
-test_expect_success 'git diff-tree -C with-funny' \
-	'git diff-tree -C --find-copies-harder --name-status \
-		$t0 $t1 | sed -e 's/^C[0-9]*/CNUM/' >current &&
-	test_cmp expected current'
+test_expect_success 'setup: remove unfunny index entry' '
+	git update-index --force-remove "$p0"
+'
 
-cat > expected <<\EOF
-RNUM	no-funny	"tabs\t,\" (dq) and spaces"
-EOF
-test_expect_success 'git diff-tree delete with-funny' \
-	'git update-index --force-remove "$p0" &&
-	git diff-index -M --name-status \
-		$t0 | sed -e 's/^R[0-9]*/RNUM/' >current &&
-	test_cmp expected current'
+test_expect_success 'diff-tree -M quotes funny filename' '
+	cat >expected <<-\EOF &&
+	RNUM	no-funny	"tabs\t,\" (dq) and spaces"
+	EOF
+	git diff-index -M --name-status $t0 >out &&
+	sed -e "s/^R[0-9]*/RNUM/" <out >current &&
+	test_cmp expected current
+'
 
-cat > expected <<\EOF
-diff --git a/no-funny "b/tabs\t,\" (dq) and spaces"
-similarity index NUM%
-rename from no-funny
-rename to "tabs\t,\" (dq) and spaces"
-EOF
-test_expect_success 'git diff-tree delete with-funny' \
-	'git diff-index -M -p $t0 |
-	 sed -e "s/index [0-9]*%/index NUM%/" >current &&
-	 test_cmp expected current'
+test_expect_success 'diff-index -M -p quotes funny filename' '
+	cat >expected <<-\EOF &&
+	diff --git a/no-funny "b/tabs\t,\" (dq) and spaces"
+	similarity index NUM%
+	rename from no-funny
+	rename to "tabs\t,\" (dq) and spaces"
+	EOF
+	git diff-index -M -p $t0 >diff &&
+	sed -e "s/index [0-9]*%/index NUM%/" <diff >current &&
+	test_cmp expected current
+'
 
-chmod +x "$p1"
-cat > expected <<\EOF
-diff --git a/no-funny "b/tabs\t,\" (dq) and spaces"
-old mode 100644
-new mode 100755
-similarity index NUM%
-rename from no-funny
-rename to "tabs\t,\" (dq) and spaces"
-EOF
-test_expect_success 'git diff-tree delete with-funny' \
-	'git diff-index -M -p $t0 |
-	 sed -e "s/index [0-9]*%/index NUM%/" >current &&
-	 test_cmp expected current'
+test_expect_success 'setup: mode change' '
+	chmod +x "$p1"
+'
 
-cat >expected <<\EOF
- "tabs\t,\" (dq) and spaces"
- 1 files changed, 0 insertions(+), 0 deletions(-)
-EOF
-test_expect_success 'git diff-tree rename with-funny applied' \
-	'git diff-index -M -p $t0 |
-	 git apply --stat | sed -e "s/|.*//" -e "s/ *\$//" >current &&
-	 test_cmp expected current'
+test_expect_success 'diff-index -M -p with mode change quotes funny filename' '
+	cat >expected <<-\EOF &&
+	diff --git a/no-funny "b/tabs\t,\" (dq) and spaces"
+	old mode 100644
+	new mode 100755
+	similarity index NUM%
+	rename from no-funny
+	rename to "tabs\t,\" (dq) and spaces"
+	EOF
+	git diff-index -M -p $t0 >diff &&
+	sed -e "s/index [0-9]*%/index NUM%/" <diff >current &&
+	test_cmp expected current
+'
 
-cat > expected <<\EOF
- no-funny
- "tabs\t,\" (dq) and spaces"
- 2 files changed, 3 insertions(+), 3 deletions(-)
-EOF
-test_expect_success 'git diff-tree delete with-funny applied' \
-	'git diff-index -p $t0 |
-	 git apply --stat | sed -e "s/|.*//" -e "s/ *\$//" >current &&
-	 test_cmp expected current'
+test_expect_success 'diffstat for rename quotes funny filename' '
+	cat >expected <<-\EOF &&
+	 "tabs\t,\" (dq) and spaces"
+	 1 file changed, 0 insertions(+), 0 deletions(-)
+	EOF
+	git diff-index -M -p $t0 >diff &&
+	git apply --stat <diff >diffstat &&
+	sed -e "s/|.*//" -e "s/ *\$//" <diffstat >current &&
+	test_i18ncmp expected current
+'
 
-test_expect_success 'git apply non-git diff' \
-	'git diff-index -p $t0 |
-	 sed -ne "/^[-+@]/p" |
-	 git apply --stat | sed -e "s/|.*//" -e "s/ *\$//" >current &&
-	 test_cmp expected current'
+test_expect_success 'numstat for rename quotes funny filename' '
+	cat >expected <<-\EOF &&
+	0	0	"tabs\t,\" (dq) and spaces"
+	EOF
+	git diff-index -M -p $t0 >diff &&
+	git apply --numstat <diff >current &&
+	test_cmp expected current
+'
+
+test_expect_success 'numstat without -M quotes funny filename' '
+	cat >expected <<-\EOF &&
+	0	3	no-funny
+	3	0	"tabs\t,\" (dq) and spaces"
+	EOF
+	git diff-index -p $t0 >diff &&
+	git apply --numstat <diff >current &&
+	test_cmp expected current
+'
+
+test_expect_success 'numstat for non-git rename diff quotes funny filename' '
+	cat >expected <<-\EOF &&
+	0	3	no-funny
+	3	0	"tabs\t,\" (dq) and spaces"
+	EOF
+	git diff-index -p $t0 >git-diff &&
+	sed -ne "/^[-+@]/p" <git-diff >diff &&
+	git apply --numstat <diff >current &&
+	test_cmp expected current
+'
 
 test_done
diff --git a/t/t3301-notes.sh b/t/t3301-notes.sh
new file mode 100755
index 0000000..16de05a
--- /dev/null
+++ b/t/t3301-notes.sh
@@ -0,0 +1,1225 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Johannes E. Schindelin
+#
+
+test_description='Test commit notes'
+
+. ./test-lib.sh
+
+cat > fake_editor.sh << \EOF
+#!/bin/sh
+echo "$MSG" > "$1"
+echo "$MSG" >& 2
+EOF
+chmod a+x fake_editor.sh
+GIT_EDITOR=./fake_editor.sh
+export GIT_EDITOR
+
+test_expect_success 'cannot annotate non-existing HEAD' '
+	(MSG=3 && export MSG && test_must_fail git notes add)
+'
+
+test_expect_success setup '
+	: > a1 &&
+	git add a1 &&
+	test_tick &&
+	git commit -m 1st &&
+	: > a2 &&
+	git add a2 &&
+	test_tick &&
+	git commit -m 2nd
+'
+
+test_expect_success 'need valid notes ref' '
+	(MSG=1 GIT_NOTES_REF=/ && export MSG GIT_NOTES_REF &&
+	 test_must_fail git notes add) &&
+	(MSG=2 GIT_NOTES_REF=/ && export MSG GIT_NOTES_REF &&
+	 test_must_fail git notes show)
+'
+
+test_expect_success 'refusing to add notes in refs/heads/' '
+	(MSG=1 GIT_NOTES_REF=refs/heads/bogus &&
+	 export MSG GIT_NOTES_REF &&
+	 test_must_fail git notes add)
+'
+
+test_expect_success 'refusing to edit notes in refs/remotes/' '
+	(MSG=1 GIT_NOTES_REF=refs/remotes/bogus &&
+	 export MSG GIT_NOTES_REF &&
+	 test_must_fail git notes edit)
+'
+
+# 1 indicates caught gracefully by die, 128 means git-show barked
+test_expect_success 'handle empty notes gracefully' '
+	test_expect_code 1 git notes show
+'
+
+test_expect_success 'show non-existent notes entry with %N' '
+	for l in A B
+	do
+		echo "$l"
+	done >expect &&
+	git show -s --format='A%n%NB' >output &&
+	test_cmp expect output
+'
+
+test_expect_success 'create notes' '
+	git config core.notesRef refs/notes/commits &&
+	MSG=b4 git notes add &&
+	test ! -f .git/NOTES_EDITMSG &&
+	test 1 = $(git ls-tree refs/notes/commits | wc -l) &&
+	test b4 = $(git notes show) &&
+	git show HEAD^ &&
+	test_must_fail git notes show HEAD^
+'
+
+test_expect_success 'show notes entry with %N' '
+	for l in A b4 B
+	do
+		echo "$l"
+	done >expect &&
+	git show -s --format='A%n%NB' >output &&
+	test_cmp expect output
+'
+
+cat >expect <<EOF
+d423f8c refs/notes/commits@{0}: notes: Notes added by 'git notes add'
+EOF
+
+test_expect_success 'create reflog entry' '
+	git reflog show refs/notes/commits >output &&
+	test_cmp expect output
+'
+
+test_expect_success 'edit existing notes' '
+	MSG=b3 git notes edit &&
+	test ! -f .git/NOTES_EDITMSG &&
+	test 1 = $(git ls-tree refs/notes/commits | wc -l) &&
+	test b3 = $(git notes show) &&
+	git show HEAD^ &&
+	test_must_fail git notes show HEAD^
+'
+
+test_expect_success 'cannot "git notes add -m" where notes already exists' '
+	test_must_fail git notes add -m "b2" &&
+	test ! -f .git/NOTES_EDITMSG &&
+	test 1 = $(git ls-tree refs/notes/commits | wc -l) &&
+	test b3 = $(git notes show) &&
+	git show HEAD^ &&
+	test_must_fail git notes show HEAD^
+'
+
+test_expect_success 'can overwrite existing note with "git notes add -f -m"' '
+	git notes add -f -m "b1" &&
+	test ! -f .git/NOTES_EDITMSG &&
+	test 1 = $(git ls-tree refs/notes/commits | wc -l) &&
+	test b1 = $(git notes show) &&
+	git show HEAD^ &&
+	test_must_fail git notes show HEAD^
+'
+
+test_expect_success 'add w/no options on existing note morphs into edit' '
+	MSG=b2 git notes add &&
+	test ! -f .git/NOTES_EDITMSG &&
+	test 1 = $(git ls-tree refs/notes/commits | wc -l) &&
+	test b2 = $(git notes show) &&
+	git show HEAD^ &&
+	test_must_fail git notes show HEAD^
+'
+
+test_expect_success 'can overwrite existing note with "git notes add -f"' '
+	MSG=b1 git notes add -f &&
+	test ! -f .git/NOTES_EDITMSG &&
+	test 1 = $(git ls-tree refs/notes/commits | wc -l) &&
+	test b1 = $(git notes show) &&
+	git show HEAD^ &&
+	test_must_fail git notes show HEAD^
+'
+
+cat > expect << EOF
+commit 268048bfb8a1fb38e703baceb8ab235421bf80c5
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:14:13 2005 -0700
+
+    2nd
+
+Notes:
+    b1
+EOF
+
+test_expect_success 'show notes' '
+	! (git cat-file commit HEAD | grep b1) &&
+	git log -1 > output &&
+	test_cmp expect output
+'
+
+test_expect_success 'create multi-line notes (setup)' '
+	: > a3 &&
+	git add a3 &&
+	test_tick &&
+	git commit -m 3rd &&
+	MSG="b3
+c3c3c3c3
+d3d3d3" git notes add
+'
+
+cat > expect-multiline << EOF
+commit 1584215f1d29c65e99c6c6848626553fdd07fd75
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:15:13 2005 -0700
+
+    3rd
+
+Notes:
+    b3
+    c3c3c3c3
+    d3d3d3
+EOF
+
+printf "\n" >> expect-multiline
+cat expect >> expect-multiline
+
+test_expect_success 'show multi-line notes' '
+	git log -2 > output &&
+	test_cmp expect-multiline output
+'
+test_expect_success 'create -F notes (setup)' '
+	: > a4 &&
+	git add a4 &&
+	test_tick &&
+	git commit -m 4th &&
+	echo "xyzzy" > note5 &&
+	git notes add -F note5
+'
+
+cat > expect-F << EOF
+commit 15023535574ded8b1a89052b32673f84cf9582b8
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:16:13 2005 -0700
+
+    4th
+
+Notes:
+    xyzzy
+EOF
+
+printf "\n" >> expect-F
+cat expect-multiline >> expect-F
+
+test_expect_success 'show -F notes' '
+	git log -3 > output &&
+	test_cmp expect-F output
+'
+
+test_expect_success 'Re-adding -F notes without -f fails' '
+	echo "zyxxy" > note5 &&
+	test_must_fail git notes add -F note5 &&
+	git log -3 > output &&
+	test_cmp expect-F output
+'
+
+cat >expect << EOF
+commit 15023535574ded8b1a89052b32673f84cf9582b8
+tree e070e3af51011e47b183c33adf9736736a525709
+parent 1584215f1d29c65e99c6c6848626553fdd07fd75
+author A U Thor <author@example.com> 1112912173 -0700
+committer C O Mitter <committer@example.com> 1112912173 -0700
+
+    4th
+EOF
+test_expect_success 'git log --pretty=raw does not show notes' '
+	git log -1 --pretty=raw >output &&
+	test_cmp expect output
+'
+
+cat >>expect <<EOF
+
+Notes:
+    xyzzy
+EOF
+test_expect_success 'git log --show-notes' '
+	git log -1 --pretty=raw --show-notes >output &&
+	test_cmp expect output
+'
+
+test_expect_success 'git log --no-notes' '
+	git log -1 --no-notes >output &&
+	! grep xyzzy output
+'
+
+test_expect_success 'git format-patch does not show notes' '
+	git format-patch -1 --stdout >output &&
+	! grep xyzzy output
+'
+
+test_expect_success 'git format-patch --show-notes does show notes' '
+	git format-patch --show-notes -1 --stdout >output &&
+	grep xyzzy output
+'
+
+for pretty in \
+	"" --pretty --pretty=raw --pretty=short --pretty=medium \
+	--pretty=full --pretty=fuller --pretty=format:%s --oneline
+do
+	case "$pretty" in
+	"") p= not= negate="" ;;
+	?*) p="$pretty" not=" not" negate="!" ;;
+	esac
+	test_expect_success "git show $pretty does$not show notes" '
+		git show $p >output &&
+		eval "$negate grep xyzzy output"
+	'
+done
+
+test_expect_success 'setup alternate notes ref' '
+	git notes --ref=alternate add -m alternate
+'
+
+test_expect_success 'git log --notes shows default notes' '
+	git log -1 --notes >output &&
+	grep xyzzy output &&
+	! grep alternate output
+'
+
+test_expect_success 'git log --notes=X shows only X' '
+	git log -1 --notes=alternate >output &&
+	! grep xyzzy output &&
+	grep alternate output
+'
+
+test_expect_success 'git log --notes --notes=X shows both' '
+	git log -1 --notes --notes=alternate >output &&
+	grep xyzzy output &&
+	grep alternate output
+'
+
+test_expect_success 'git log --no-notes resets default state' '
+	git log -1 --notes --notes=alternate \
+		--no-notes --notes=alternate \
+		>output &&
+	! grep xyzzy output &&
+	grep alternate output
+'
+
+test_expect_success 'git log --no-notes resets ref list' '
+	git log -1 --notes --notes=alternate \
+		--no-notes --notes \
+		>output &&
+	grep xyzzy output &&
+	! grep alternate output
+'
+
+test_expect_success 'create -m notes (setup)' '
+	: > a5 &&
+	git add a5 &&
+	test_tick &&
+	git commit -m 5th &&
+	git notes add -m spam -m "foo
+bar
+baz"
+'
+
+whitespace="    "
+cat > expect-m << EOF
+commit bd1753200303d0a0344be813e504253b3d98e74d
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:17:13 2005 -0700
+
+    5th
+
+Notes:
+    spam
+$whitespace
+    foo
+    bar
+    baz
+EOF
+
+printf "\n" >> expect-m
+cat expect-F >> expect-m
+
+test_expect_success 'show -m notes' '
+	git log -4 > output &&
+	test_cmp expect-m output
+'
+
+test_expect_success 'remove note with add -f -F /dev/null (setup)' '
+	git notes add -f -F /dev/null
+'
+
+cat > expect-rm-F << EOF
+commit bd1753200303d0a0344be813e504253b3d98e74d
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:17:13 2005 -0700
+
+    5th
+EOF
+
+printf "\n" >> expect-rm-F
+cat expect-F >> expect-rm-F
+
+test_expect_success 'verify note removal with -F /dev/null' '
+	git log -4 > output &&
+	test_cmp expect-rm-F output &&
+	test_must_fail git notes show
+'
+
+test_expect_success 'do not create empty note with -m "" (setup)' '
+	git notes add -m ""
+'
+
+test_expect_success 'verify non-creation of note with -m ""' '
+	git log -4 > output &&
+	test_cmp expect-rm-F output &&
+	test_must_fail git notes show
+'
+
+cat > expect-combine_m_and_F << EOF
+foo
+
+xyzzy
+
+bar
+
+zyxxy
+
+baz
+EOF
+
+test_expect_success 'create note with combination of -m and -F' '
+	echo "xyzzy" > note_a &&
+	echo "zyxxy" > note_b &&
+	git notes add -m "foo" -F note_a -m "bar" -F note_b -m "baz" &&
+	git notes show > output &&
+	test_cmp expect-combine_m_and_F output
+'
+
+test_expect_success 'remove note with "git notes remove" (setup)' '
+	git notes remove HEAD^ &&
+	git notes remove
+'
+
+cat > expect-rm-remove << EOF
+commit bd1753200303d0a0344be813e504253b3d98e74d
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:17:13 2005 -0700
+
+    5th
+
+commit 15023535574ded8b1a89052b32673f84cf9582b8
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:16:13 2005 -0700
+
+    4th
+EOF
+
+printf "\n" >> expect-rm-remove
+cat expect-multiline >> expect-rm-remove
+
+test_expect_success 'verify note removal with "git notes remove"' '
+	git log -4 > output &&
+	test_cmp expect-rm-remove output &&
+	test_must_fail git notes show HEAD^
+'
+
+cat > expect << EOF
+c18dc024e14f08d18d14eea0d747ff692d66d6a3 1584215f1d29c65e99c6c6848626553fdd07fd75
+c9c6af7f78bc47490dbf3e822cf2f3c24d4b9061 268048bfb8a1fb38e703baceb8ab235421bf80c5
+EOF
+
+test_expect_success 'removing non-existing note should not create new commit' '
+	git rev-parse --verify refs/notes/commits > before_commit &&
+	test_must_fail git notes remove HEAD^ &&
+	git rev-parse --verify refs/notes/commits > after_commit &&
+	test_cmp before_commit after_commit
+'
+
+test_expect_success 'removing more than one' '
+	before=$(git rev-parse --verify refs/notes/commits) &&
+	test_when_finished "git update-ref refs/notes/commits $before" &&
+
+	# We have only two -- add another and make sure it stays
+	git notes add -m "extra" &&
+	git notes list HEAD >after-removal-expect &&
+	git notes remove HEAD^^ HEAD^^^ &&
+	git notes list | sed -e "s/ .*//" >actual &&
+	test_cmp after-removal-expect actual
+'
+
+test_expect_success 'removing is atomic' '
+	before=$(git rev-parse --verify refs/notes/commits) &&
+	test_when_finished "git update-ref refs/notes/commits $before" &&
+	test_must_fail git notes remove HEAD^^ HEAD^^^ HEAD^ &&
+	after=$(git rev-parse --verify refs/notes/commits) &&
+	test "$before" = "$after"
+'
+
+test_expect_success 'removing with --ignore-missing' '
+	before=$(git rev-parse --verify refs/notes/commits) &&
+	test_when_finished "git update-ref refs/notes/commits $before" &&
+
+	# We have only two -- add another and make sure it stays
+	git notes add -m "extra" &&
+	git notes list HEAD >after-removal-expect &&
+	git notes remove --ignore-missing HEAD^^ HEAD^^^ HEAD^ &&
+	git notes list | sed -e "s/ .*//" >actual &&
+	test_cmp after-removal-expect actual
+'
+
+test_expect_success 'removing with --ignore-missing but bogus ref' '
+	before=$(git rev-parse --verify refs/notes/commits) &&
+	test_when_finished "git update-ref refs/notes/commits $before" &&
+	test_must_fail git notes remove --ignore-missing HEAD^^ HEAD^^^ NO-SUCH-COMMIT &&
+	after=$(git rev-parse --verify refs/notes/commits) &&
+	test "$before" = "$after"
+'
+
+test_expect_success 'remove reads from --stdin' '
+	before=$(git rev-parse --verify refs/notes/commits) &&
+	test_when_finished "git update-ref refs/notes/commits $before" &&
+
+	# We have only two -- add another and make sure it stays
+	git notes add -m "extra" &&
+	git notes list HEAD >after-removal-expect &&
+	git rev-parse HEAD^^ HEAD^^^ >input &&
+	git notes remove --stdin <input &&
+	git notes list | sed -e "s/ .*//" >actual &&
+	test_cmp after-removal-expect actual
+'
+
+test_expect_success 'remove --stdin is also atomic' '
+	before=$(git rev-parse --verify refs/notes/commits) &&
+	test_when_finished "git update-ref refs/notes/commits $before" &&
+	git rev-parse HEAD^^ HEAD^^^ HEAD^ >input &&
+	test_must_fail git notes remove --stdin <input &&
+	after=$(git rev-parse --verify refs/notes/commits) &&
+	test "$before" = "$after"
+'
+
+test_expect_success 'removing with --stdin --ignore-missing' '
+	before=$(git rev-parse --verify refs/notes/commits) &&
+	test_when_finished "git update-ref refs/notes/commits $before" &&
+
+	# We have only two -- add another and make sure it stays
+	git notes add -m "extra" &&
+	git notes list HEAD >after-removal-expect &&
+	git rev-parse HEAD^^ HEAD^^^ HEAD^ >input &&
+	git notes remove --ignore-missing --stdin <input &&
+	git notes list | sed -e "s/ .*//" >actual &&
+	test_cmp after-removal-expect actual
+'
+
+test_expect_success 'list notes with "git notes list"' '
+	git notes list > output &&
+	test_cmp expect output
+'
+
+test_expect_success 'list notes with "git notes"' '
+	git notes > output &&
+	test_cmp expect output
+'
+
+cat > expect << EOF
+c18dc024e14f08d18d14eea0d747ff692d66d6a3
+EOF
+
+test_expect_success 'list specific note with "git notes list <object>"' '
+	git notes list HEAD^^ > output &&
+	test_cmp expect output
+'
+
+cat > expect << EOF
+EOF
+
+test_expect_success 'listing non-existing notes fails' '
+	test_must_fail git notes list HEAD > output &&
+	test_cmp expect output
+'
+
+cat > expect << EOF
+Initial set of notes
+
+More notes appended with git notes append
+EOF
+
+test_expect_success 'append to existing note with "git notes append"' '
+	git notes add -m "Initial set of notes" &&
+	git notes append -m "More notes appended with git notes append" &&
+	git notes show > output &&
+	test_cmp expect output
+'
+
+cat > expect_list << EOF
+c18dc024e14f08d18d14eea0d747ff692d66d6a3 1584215f1d29c65e99c6c6848626553fdd07fd75
+c9c6af7f78bc47490dbf3e822cf2f3c24d4b9061 268048bfb8a1fb38e703baceb8ab235421bf80c5
+4b6ad22357cc8a1296720574b8d2fbc22fab0671 bd1753200303d0a0344be813e504253b3d98e74d
+EOF
+
+test_expect_success '"git notes list" does not expand to "git notes list HEAD"' '
+	git notes list > output &&
+	test_cmp expect_list output
+'
+
+test_expect_success 'appending empty string does not change existing note' '
+	git notes append -m "" &&
+	git notes show > output &&
+	test_cmp expect output
+'
+
+test_expect_success 'git notes append == add when there is no existing note' '
+	git notes remove HEAD &&
+	test_must_fail git notes list HEAD &&
+	git notes append -m "Initial set of notes
+
+More notes appended with git notes append" &&
+	git notes show > output &&
+	test_cmp expect output
+'
+
+test_expect_success 'appending empty string to non-existing note does not create note' '
+	git notes remove HEAD &&
+	test_must_fail git notes list HEAD &&
+	git notes append -m "" &&
+	test_must_fail git notes list HEAD
+'
+
+test_expect_success 'create other note on a different notes ref (setup)' '
+	: > a6 &&
+	git add a6 &&
+	test_tick &&
+	git commit -m 6th &&
+	GIT_NOTES_REF="refs/notes/other" git notes add -m "other note"
+'
+
+cat > expect-other << EOF
+commit 387a89921c73d7ed72cd94d179c1c7048ca47756
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:18:13 2005 -0700
+
+    6th
+
+Notes (other):
+    other note
+EOF
+
+cat > expect-not-other << EOF
+commit 387a89921c73d7ed72cd94d179c1c7048ca47756
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:18:13 2005 -0700
+
+    6th
+EOF
+
+test_expect_success 'Do not show note on other ref by default' '
+	git log -1 > output &&
+	test_cmp expect-not-other output
+'
+
+test_expect_success 'Do show note when ref is given in GIT_NOTES_REF' '
+	GIT_NOTES_REF="refs/notes/other" git log -1 > output &&
+	test_cmp expect-other output
+'
+
+test_expect_success 'Do show note when ref is given in core.notesRef config' '
+	git config core.notesRef "refs/notes/other" &&
+	git log -1 > output &&
+	test_cmp expect-other output
+'
+
+test_expect_success 'Do not show note when core.notesRef is overridden' '
+	GIT_NOTES_REF="refs/notes/wrong" git log -1 > output &&
+	test_cmp expect-not-other output
+'
+
+cat > expect-both << EOF
+commit 387a89921c73d7ed72cd94d179c1c7048ca47756
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:18:13 2005 -0700
+
+    6th
+
+Notes:
+    order test
+
+Notes (other):
+    other note
+
+commit bd1753200303d0a0344be813e504253b3d98e74d
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:17:13 2005 -0700
+
+    5th
+
+Notes:
+    replacement for deleted note
+EOF
+
+test_expect_success 'Show all notes when notes.displayRef=refs/notes/*' '
+	GIT_NOTES_REF=refs/notes/commits git notes add \
+		-m"replacement for deleted note" HEAD^ &&
+	GIT_NOTES_REF=refs/notes/commits git notes add -m"order test" &&
+	git config --unset core.notesRef &&
+	git config notes.displayRef "refs/notes/*" &&
+	git log -2 > output &&
+	test_cmp expect-both output
+'
+
+test_expect_success 'core.notesRef is implicitly in notes.displayRef' '
+	git config core.notesRef refs/notes/commits &&
+	git config notes.displayRef refs/notes/other &&
+	git log -2 > output &&
+	test_cmp expect-both output
+'
+
+test_expect_success 'notes.displayRef can be given more than once' '
+	git config --unset core.notesRef &&
+	git config notes.displayRef refs/notes/commits &&
+	git config --add notes.displayRef refs/notes/other &&
+	git log -2 > output &&
+	test_cmp expect-both output
+'
+
+cat > expect-both-reversed << EOF
+commit 387a89921c73d7ed72cd94d179c1c7048ca47756
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:18:13 2005 -0700
+
+    6th
+
+Notes (other):
+    other note
+
+Notes:
+    order test
+EOF
+
+test_expect_success 'notes.displayRef respects order' '
+	git config core.notesRef refs/notes/other &&
+	git config --unset-all notes.displayRef &&
+	git config notes.displayRef refs/notes/commits &&
+	git log -1 > output &&
+	test_cmp expect-both-reversed output
+'
+
+test_expect_success 'GIT_NOTES_DISPLAY_REF works' '
+	git config --unset-all core.notesRef &&
+	git config --unset-all notes.displayRef &&
+	GIT_NOTES_DISPLAY_REF=refs/notes/commits:refs/notes/other \
+		git log -2 > output &&
+	test_cmp expect-both output
+'
+
+cat > expect-none << EOF
+commit 387a89921c73d7ed72cd94d179c1c7048ca47756
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:18:13 2005 -0700
+
+    6th
+
+commit bd1753200303d0a0344be813e504253b3d98e74d
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:17:13 2005 -0700
+
+    5th
+EOF
+
+test_expect_success 'GIT_NOTES_DISPLAY_REF overrides config' '
+	git config notes.displayRef "refs/notes/*" &&
+	GIT_NOTES_REF= GIT_NOTES_DISPLAY_REF= git log -2 > output &&
+	test_cmp expect-none output
+'
+
+test_expect_success '--show-notes=* adds to GIT_NOTES_DISPLAY_REF' '
+	GIT_NOTES_REF= GIT_NOTES_DISPLAY_REF= git log --show-notes=* -2 > output &&
+	test_cmp expect-both output
+'
+
+cat > expect-commits << EOF
+commit 387a89921c73d7ed72cd94d179c1c7048ca47756
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:18:13 2005 -0700
+
+    6th
+
+Notes:
+    order test
+EOF
+
+test_expect_success '--no-standard-notes' '
+	git log --no-standard-notes --show-notes=commits -1 > output &&
+	test_cmp expect-commits output
+'
+
+test_expect_success '--standard-notes' '
+	git log --no-standard-notes --show-notes=commits \
+		--standard-notes -2 > output &&
+	test_cmp expect-both output
+'
+
+test_expect_success '--show-notes=ref accumulates' '
+	git log --show-notes=other --show-notes=commits \
+		 --no-standard-notes -1 > output &&
+	test_cmp expect-both-reversed output
+'
+
+test_expect_success 'Allow notes on non-commits (trees, blobs, tags)' '
+	git config core.notesRef refs/notes/other &&
+	echo "Note on a tree" > expect &&
+	git notes add -m "Note on a tree" HEAD: &&
+	git notes show HEAD: > actual &&
+	test_cmp expect actual &&
+	echo "Note on a blob" > expect &&
+	filename=$(git ls-tree --name-only HEAD | head -n1) &&
+	git notes add -m "Note on a blob" HEAD:$filename &&
+	git notes show HEAD:$filename > actual &&
+	test_cmp expect actual &&
+	echo "Note on a tag" > expect &&
+	git tag -a -m "This is an annotated tag" foobar HEAD^ &&
+	git notes add -m "Note on a tag" foobar &&
+	git notes show foobar > actual &&
+	test_cmp expect actual
+'
+
+cat > expect << EOF
+commit 2ede89468182a62d0bde2583c736089bcf7d7e92
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:19:13 2005 -0700
+
+    7th
+
+Notes (other):
+    other note
+EOF
+
+test_expect_success 'create note from other note with "git notes add -C"' '
+	: > a7 &&
+	git add a7 &&
+	test_tick &&
+	git commit -m 7th &&
+	git notes add -C $(git notes list HEAD^) &&
+	git log -1 > actual &&
+	test_cmp expect actual &&
+	test "$(git notes list HEAD)" = "$(git notes list HEAD^)"
+'
+
+test_expect_success 'create note from non-existing note with "git notes add -C" fails' '
+	: > a8 &&
+	git add a8 &&
+	test_tick &&
+	git commit -m 8th &&
+	test_must_fail git notes add -C deadbeef &&
+	test_must_fail git notes list HEAD
+'
+
+cat > expect << EOF
+commit 016e982bad97eacdbda0fcbd7ce5b0ba87c81f1b
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:21:13 2005 -0700
+
+    9th
+
+Notes (other):
+    yet another note
+EOF
+
+test_expect_success 'create note from other note with "git notes add -c"' '
+	: > a9 &&
+	git add a9 &&
+	test_tick &&
+	git commit -m 9th &&
+	MSG="yet another note" git notes add -c $(git notes list HEAD^^) &&
+	git log -1 > actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'create note from non-existing note with "git notes add -c" fails' '
+	: > a10 &&
+	git add a10 &&
+	test_tick &&
+	git commit -m 10th &&
+	(
+		MSG="yet another note" &&
+		export MSG &&
+		test_must_fail git notes add -c deadbeef
+	) &&
+	test_must_fail git notes list HEAD
+'
+
+cat > expect << EOF
+commit 016e982bad97eacdbda0fcbd7ce5b0ba87c81f1b
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:21:13 2005 -0700
+
+    9th
+
+Notes (other):
+    yet another note
+$whitespace
+    yet another note
+EOF
+
+test_expect_success 'append to note from other note with "git notes append -C"' '
+	git notes append -C $(git notes list HEAD^) HEAD^ &&
+	git log -1 HEAD^ > actual &&
+	test_cmp expect actual
+'
+
+cat > expect << EOF
+commit ffed603236bfa3891c49644257a83598afe8ae5a
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:22:13 2005 -0700
+
+    10th
+
+Notes (other):
+    other note
+EOF
+
+test_expect_success 'create note from other note with "git notes append -c"' '
+	MSG="other note" git notes append -c $(git notes list HEAD^) &&
+	git log -1 > actual &&
+	test_cmp expect actual
+'
+
+cat > expect << EOF
+commit ffed603236bfa3891c49644257a83598afe8ae5a
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:22:13 2005 -0700
+
+    10th
+
+Notes (other):
+    other note
+$whitespace
+    yet another note
+EOF
+
+test_expect_success 'append to note from other note with "git notes append -c"' '
+	MSG="yet another note" git notes append -c $(git notes list HEAD) &&
+	git log -1 > actual &&
+	test_cmp expect actual
+'
+
+cat > expect << EOF
+commit 6352c5e33dbcab725fe0579be16aa2ba8eb369be
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:23:13 2005 -0700
+
+    11th
+
+Notes (other):
+    other note
+$whitespace
+    yet another note
+EOF
+
+test_expect_success 'copy note with "git notes copy"' '
+	: > a11 &&
+	git add a11 &&
+	test_tick &&
+	git commit -m 11th &&
+	git notes copy HEAD^ HEAD &&
+	git log -1 > actual &&
+	test_cmp expect actual &&
+	test "$(git notes list HEAD)" = "$(git notes list HEAD^)"
+'
+
+test_expect_success 'prevent overwrite with "git notes copy"' '
+	test_must_fail git notes copy HEAD~2 HEAD &&
+	git log -1 > actual &&
+	test_cmp expect actual &&
+	test "$(git notes list HEAD)" = "$(git notes list HEAD^)"
+'
+
+cat > expect << EOF
+commit 6352c5e33dbcab725fe0579be16aa2ba8eb369be
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:23:13 2005 -0700
+
+    11th
+
+Notes (other):
+    yet another note
+$whitespace
+    yet another note
+EOF
+
+test_expect_success 'allow overwrite with "git notes copy -f"' '
+	git notes copy -f HEAD~2 HEAD &&
+	git log -1 > actual &&
+	test_cmp expect actual &&
+	test "$(git notes list HEAD)" = "$(git notes list HEAD~2)"
+'
+
+test_expect_success 'cannot copy note from object without notes' '
+	: > a12 &&
+	git add a12 &&
+	test_tick &&
+	git commit -m 12th &&
+	: > a13 &&
+	git add a13 &&
+	test_tick &&
+	git commit -m 13th &&
+	test_must_fail git notes copy HEAD^ HEAD
+'
+
+cat > expect << EOF
+commit e5d4fb5698d564ab8c73551538ecaf2b0c666185
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:25:13 2005 -0700
+
+    13th
+
+Notes (other):
+    yet another note
+$whitespace
+    yet another note
+
+commit 7038787dfe22a14c3867ce816dbba39845359719
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:24:13 2005 -0700
+
+    12th
+
+Notes (other):
+    other note
+$whitespace
+    yet another note
+EOF
+
+test_expect_success 'git notes copy --stdin' '
+	(echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \
+	echo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |
+	git notes copy --stdin &&
+	git log -2 > output &&
+	test_cmp expect output &&
+	test "$(git notes list HEAD)" = "$(git notes list HEAD~2)" &&
+	test "$(git notes list HEAD^)" = "$(git notes list HEAD~3)"
+'
+
+cat > expect << EOF
+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:27:13 2005 -0700
+
+    15th
+
+commit be28d8b4d9951ad940d229ee3b0b9ee3b1ec273d
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:26:13 2005 -0700
+
+    14th
+EOF
+
+test_expect_success 'git notes copy --for-rewrite (unconfigured)' '
+	test_commit 14th &&
+	test_commit 15th &&
+	(echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \
+	echo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |
+	git notes copy --for-rewrite=foo &&
+	git log -2 > output &&
+	test_cmp expect output
+'
+
+cat > expect << EOF
+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:27:13 2005 -0700
+
+    15th
+
+Notes (other):
+    yet another note
+$whitespace
+    yet another note
+
+commit be28d8b4d9951ad940d229ee3b0b9ee3b1ec273d
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:26:13 2005 -0700
+
+    14th
+
+Notes (other):
+    other note
+$whitespace
+    yet another note
+EOF
+
+test_expect_success 'git notes copy --for-rewrite (enabled)' '
+	git config notes.rewriteMode overwrite &&
+	git config notes.rewriteRef "refs/notes/*" &&
+	(echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \
+	echo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |
+	git notes copy --for-rewrite=foo &&
+	git log -2 > output &&
+	test_cmp expect output
+'
+
+test_expect_success 'git notes copy --for-rewrite (disabled)' '
+	git config notes.rewrite.bar false &&
+	echo $(git rev-parse HEAD~3) $(git rev-parse HEAD) |
+	git notes copy --for-rewrite=bar &&
+	git log -2 > output &&
+	test_cmp expect output
+'
+
+cat > expect << EOF
+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:27:13 2005 -0700
+
+    15th
+
+Notes (other):
+    a fresh note
+EOF
+
+test_expect_success 'git notes copy --for-rewrite (overwrite)' '
+	git notes add -f -m"a fresh note" HEAD^ &&
+	echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
+	git notes copy --for-rewrite=foo &&
+	git log -1 > output &&
+	test_cmp expect output
+'
+
+test_expect_success 'git notes copy --for-rewrite (ignore)' '
+	git config notes.rewriteMode ignore &&
+	echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
+	git notes copy --for-rewrite=foo &&
+	git log -1 > output &&
+	test_cmp expect output
+'
+
+cat > expect << EOF
+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:27:13 2005 -0700
+
+    15th
+
+Notes (other):
+    a fresh note
+$whitespace
+    another fresh note
+EOF
+
+test_expect_success 'git notes copy --for-rewrite (append)' '
+	git notes add -f -m"another fresh note" HEAD^ &&
+	git config notes.rewriteMode concatenate &&
+	echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
+	git notes copy --for-rewrite=foo &&
+	git log -1 > output &&
+	test_cmp expect output
+'
+
+cat > expect << EOF
+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:27:13 2005 -0700
+
+    15th
+
+Notes (other):
+    a fresh note
+$whitespace
+    another fresh note
+$whitespace
+    append 1
+$whitespace
+    append 2
+EOF
+
+test_expect_success 'git notes copy --for-rewrite (append two to one)' '
+	git notes add -f -m"append 1" HEAD^ &&
+	git notes add -f -m"append 2" HEAD^^ &&
+	(echo $(git rev-parse HEAD^) $(git rev-parse HEAD);
+	echo $(git rev-parse HEAD^^) $(git rev-parse HEAD)) |
+	git notes copy --for-rewrite=foo &&
+	git log -1 > output &&
+	test_cmp expect output
+'
+
+test_expect_success 'git notes copy --for-rewrite (append empty)' '
+	git notes remove HEAD^ &&
+	echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
+	git notes copy --for-rewrite=foo &&
+	git log -1 > output &&
+	test_cmp expect output
+'
+
+cat > expect << EOF
+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:27:13 2005 -0700
+
+    15th
+
+Notes (other):
+    replacement note 1
+EOF
+
+test_expect_success 'GIT_NOTES_REWRITE_MODE works' '
+	git notes add -f -m"replacement note 1" HEAD^ &&
+	echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
+	GIT_NOTES_REWRITE_MODE=overwrite git notes copy --for-rewrite=foo &&
+	git log -1 > output &&
+	test_cmp expect output
+'
+
+cat > expect << EOF
+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:27:13 2005 -0700
+
+    15th
+
+Notes (other):
+    replacement note 2
+EOF
+
+test_expect_success 'GIT_NOTES_REWRITE_REF works' '
+	git config notes.rewriteMode overwrite &&
+	git notes add -f -m"replacement note 2" HEAD^ &&
+	git config --unset-all notes.rewriteRef &&
+	echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
+	GIT_NOTES_REWRITE_REF=refs/notes/commits:refs/notes/other \
+		git notes copy --for-rewrite=foo &&
+	git log -1 > output &&
+	test_cmp expect output
+'
+
+test_expect_success 'GIT_NOTES_REWRITE_REF overrides config' '
+	git config notes.rewriteRef refs/notes/other &&
+	git notes add -f -m"replacement note 3" HEAD^ &&
+	echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
+	GIT_NOTES_REWRITE_REF= git notes copy --for-rewrite=foo &&
+	git log -1 > output &&
+	test_cmp expect output
+'
+
+test_expect_success 'git notes copy diagnoses too many or too few parameters' '
+	test_must_fail git notes copy &&
+	test_must_fail git notes copy one two three
+'
+
+test_expect_success 'git notes get-ref (no overrides)' '
+	git config --unset core.notesRef &&
+	sane_unset GIT_NOTES_REF &&
+	test "$(git notes get-ref)" = "refs/notes/commits"
+'
+
+test_expect_success 'git notes get-ref (core.notesRef)' '
+	git config core.notesRef refs/notes/foo &&
+	test "$(git notes get-ref)" = "refs/notes/foo"
+'
+
+test_expect_success 'git notes get-ref (GIT_NOTES_REF)' '
+	test "$(GIT_NOTES_REF=refs/notes/bar git notes get-ref)" = "refs/notes/bar"
+'
+
+test_expect_success 'git notes get-ref (--ref)' '
+	test "$(GIT_NOTES_REF=refs/notes/bar git notes --ref=baz get-ref)" = "refs/notes/baz"
+'
+
+test_done
diff --git a/t/t3302-notes-index-expensive.sh b/t/t3302-notes-index-expensive.sh
new file mode 100755
index 0000000..e35d781
--- /dev/null
+++ b/t/t3302-notes-index-expensive.sh
@@ -0,0 +1,126 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Johannes E. Schindelin
+#
+
+test_description='Test commit notes index (expensive!)'
+
+. ./test-lib.sh
+
+test_set_prereq NOT_EXPENSIVE
+test -n "$GIT_NOTES_TIMING_TESTS" && test_set_prereq EXPENSIVE
+test -x /usr/bin/time && test_set_prereq USR_BIN_TIME
+
+create_repo () {
+	number_of_commits=$1
+	nr=0
+	test -d .git || {
+	git init &&
+	(
+		while [ $nr -lt $number_of_commits ]; do
+			nr=$(($nr+1))
+			mark=$(($nr+$nr))
+			notemark=$(($mark+1))
+			test_tick &&
+			cat <<INPUT_END &&
+commit refs/heads/master
+mark :$mark
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+commit #$nr
+COMMIT
+
+M 644 inline file
+data <<EOF
+file in commit #$nr
+EOF
+
+blob
+mark :$notemark
+data <<EOF
+note for commit #$nr
+EOF
+
+INPUT_END
+
+			echo "N :$notemark :$mark" >> note_commit
+		done &&
+		test_tick &&
+		cat <<INPUT_END &&
+commit refs/notes/commits
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+notes
+COMMIT
+
+INPUT_END
+
+		cat note_commit
+	) |
+	git fast-import --quiet &&
+	git config core.notesRef refs/notes/commits
+	}
+}
+
+test_notes () {
+	count=$1 &&
+	git config core.notesRef refs/notes/commits &&
+	git log | grep "^    " > output &&
+	i=$count &&
+	while [ $i -gt 0 ]; do
+		echo "    commit #$i" &&
+		echo "    note for commit #$i" &&
+		i=$(($i-1));
+	done > expect &&
+	test_cmp expect output
+}
+
+cat > time_notes << \EOF
+	mode=$1
+	i=1
+	while [ $i -lt $2 ]; do
+		case $1 in
+		no-notes)
+			GIT_NOTES_REF=non-existing; export GIT_NOTES_REF
+		;;
+		notes)
+			unset GIT_NOTES_REF
+		;;
+		esac
+		git log >/dev/null
+		i=$(($i+1))
+	done
+EOF
+
+time_notes () {
+	for mode in no-notes notes
+	do
+		echo $mode
+		/usr/bin/time "$SHELL_PATH" ../time_notes $mode $1
+	done
+}
+
+do_tests () {
+	pr=$1
+	count=$2
+
+	test_expect_success $pr 'setup / mkdir' '
+		mkdir $count &&
+		cd $count
+	'
+
+	test_expect_success $pr "setup $count" "create_repo $count"
+
+	test_expect_success $pr 'notes work' "test_notes $count"
+
+	test_expect_success USR_BIN_TIME,$pr 'notes timing with /usr/bin/time' "time_notes 100"
+
+	test_expect_success $pr 'teardown / cd ..' 'cd ..'
+}
+
+do_tests NOT_EXPENSIVE 10
+for count in 100 1000 10000; do
+	do_tests EXPENSIVE $count
+done
+
+test_done
diff --git a/t/t3303-notes-subtrees.sh b/t/t3303-notes-subtrees.sh
new file mode 100755
index 0000000..704aee8
--- /dev/null
+++ b/t/t3303-notes-subtrees.sh
@@ -0,0 +1,195 @@
+#!/bin/sh
+
+test_description='Test commit notes organized in subtrees'
+
+. ./test-lib.sh
+
+number_of_commits=100
+
+start_note_commit () {
+	test_tick &&
+	cat <<INPUT_END
+commit refs/notes/commits
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+notes
+COMMIT
+
+from refs/notes/commits^0
+deleteall
+INPUT_END
+
+}
+
+verify_notes () {
+	git log | grep "^    " > output &&
+	i=$number_of_commits &&
+	while [ $i -gt 0 ]; do
+		echo "    commit #$i" &&
+		echo "    note for commit #$i" &&
+		i=$(($i-1));
+	done > expect &&
+	test_cmp expect output
+}
+
+test_expect_success "setup: create $number_of_commits commits" '
+
+	(
+		nr=0 &&
+		while [ $nr -lt $number_of_commits ]; do
+			nr=$(($nr+1)) &&
+			test_tick &&
+			cat <<INPUT_END
+commit refs/heads/master
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+commit #$nr
+COMMIT
+
+M 644 inline file
+data <<EOF
+file in commit #$nr
+EOF
+
+INPUT_END
+
+		done &&
+		test_tick &&
+		cat <<INPUT_END
+commit refs/notes/commits
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+no notes
+COMMIT
+
+deleteall
+
+INPUT_END
+
+	) |
+	git fast-import --quiet &&
+	git config core.notesRef refs/notes/commits
+'
+
+test_sha1_based () {
+	(
+		start_note_commit &&
+		nr=$number_of_commits &&
+		git rev-list refs/heads/master |
+		while read sha1; do
+			note_path=$(echo "$sha1" | sed "$1")
+			cat <<INPUT_END &&
+M 100644 inline $note_path
+data <<EOF
+note for commit #$nr
+EOF
+
+INPUT_END
+
+			nr=$(($nr-1))
+		done
+	) |
+	git fast-import --quiet
+}
+
+test_expect_success 'test notes in 2/38-fanout' 'test_sha1_based "s|^..|&/|"'
+test_expect_success 'verify notes in 2/38-fanout' 'verify_notes'
+
+test_expect_success 'test notes in 2/2/36-fanout' 'test_sha1_based "s|^\(..\)\(..\)|\1/\2/|"'
+test_expect_success 'verify notes in 2/2/36-fanout' 'verify_notes'
+
+test_expect_success 'test notes in 2/2/2/34-fanout' 'test_sha1_based "s|^\(..\)\(..\)\(..\)|\1/\2/\3/|"'
+test_expect_success 'verify notes in 2/2/2/34-fanout' 'verify_notes'
+
+test_same_notes () {
+	(
+		start_note_commit &&
+		nr=$number_of_commits &&
+		git rev-list refs/heads/master |
+		while read sha1; do
+			first_note_path=$(echo "$sha1" | sed "$1")
+			second_note_path=$(echo "$sha1" | sed "$2")
+			cat <<INPUT_END &&
+M 100644 inline $second_note_path
+data <<EOF
+note for commit #$nr
+EOF
+
+M 100644 inline $first_note_path
+data <<EOF
+note for commit #$nr
+EOF
+
+INPUT_END
+
+			nr=$(($nr-1))
+		done
+	) |
+	git fast-import --quiet
+}
+
+test_expect_success 'test same notes in no fanout and 2/38-fanout' 'test_same_notes "s|^..|&/|" ""'
+test_expect_success 'verify same notes in no fanout and 2/38-fanout' 'verify_notes'
+
+test_expect_success 'test same notes in no fanout and 2/2/36-fanout' 'test_same_notes "s|^\(..\)\(..\)|\1/\2/|" ""'
+test_expect_success 'verify same notes in no fanout and 2/2/36-fanout' 'verify_notes'
+
+test_expect_success 'test same notes in 2/38-fanout and 2/2/36-fanout' 'test_same_notes "s|^\(..\)\(..\)|\1/\2/|" "s|^..|&/|"'
+test_expect_success 'verify same notes in 2/38-fanout and 2/2/36-fanout' 'verify_notes'
+
+test_expect_success 'test same notes in 2/2/2/34-fanout and 2/2/36-fanout' 'test_same_notes "s|^\(..\)\(..\)|\1/\2/|" "s|^\(..\)\(..\)\(..\)|\1/\2/\3/|"'
+test_expect_success 'verify same notes in 2/2/2/34-fanout and 2/2/36-fanout' 'verify_notes'
+
+test_concatenated_notes () {
+	(
+		start_note_commit &&
+		nr=$number_of_commits &&
+		git rev-list refs/heads/master |
+		while read sha1; do
+			first_note_path=$(echo "$sha1" | sed "$1")
+			second_note_path=$(echo "$sha1" | sed "$2")
+			cat <<INPUT_END &&
+M 100644 inline $second_note_path
+data <<EOF
+second note for commit #$nr
+EOF
+
+M 100644 inline $first_note_path
+data <<EOF
+first note for commit #$nr
+EOF
+
+INPUT_END
+
+			nr=$(($nr-1))
+		done
+	) |
+	git fast-import --quiet
+}
+
+verify_concatenated_notes () {
+	git log | grep "^    " > output &&
+	i=$number_of_commits &&
+	while [ $i -gt 0 ]; do
+		echo "    commit #$i" &&
+		echo "    first note for commit #$i" &&
+		echo "    " &&
+		echo "    second note for commit #$i" &&
+		i=$(($i-1));
+	done > expect &&
+	test_cmp expect output
+}
+
+test_expect_success 'test notes in no fanout concatenated with 2/38-fanout' 'test_concatenated_notes "s|^..|&/|" ""'
+test_expect_success 'verify notes in no fanout concatenated with 2/38-fanout' 'verify_concatenated_notes'
+
+test_expect_success 'test notes in no fanout concatenated with 2/2/36-fanout' 'test_concatenated_notes "s|^\(..\)\(..\)|\1/\2/|" ""'
+test_expect_success 'verify notes in no fanout concatenated with 2/2/36-fanout' 'verify_concatenated_notes'
+
+test_expect_success 'test notes in 2/38-fanout concatenated with 2/2/36-fanout' 'test_concatenated_notes "s|^\(..\)\(..\)|\1/\2/|" "s|^..|&/|"'
+test_expect_success 'verify notes in 2/38-fanout concatenated with 2/2/36-fanout' 'verify_concatenated_notes'
+
+test_expect_success 'test notes in 2/2/36-fanout concatenated with 2/2/2/34-fanout' 'test_concatenated_notes "s|^\(..\)\(..\)\(..\)|\1/\2/\3/|" "s|^\(..\)\(..\)|\1/\2/|"'
+test_expect_success 'verify notes in 2/2/36-fanout concatenated with 2/2/2/34-fanout' 'verify_concatenated_notes'
+
+test_done
diff --git a/t/t3304-notes-mixed.sh b/t/t3304-notes-mixed.sh
new file mode 100755
index 0000000..1709e8c
--- /dev/null
+++ b/t/t3304-notes-mixed.sh
@@ -0,0 +1,206 @@
+#!/bin/sh
+
+test_description='Test notes trees that also contain non-notes'
+
+. ./test-lib.sh
+
+number_of_commits=100
+
+start_note_commit () {
+	test_tick &&
+	cat <<INPUT_END
+commit refs/notes/commits
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+notes
+COMMIT
+
+from refs/notes/commits^0
+deleteall
+INPUT_END
+
+}
+
+verify_notes () {
+	git log | grep "^    " > output &&
+	i=$number_of_commits &&
+	while [ $i -gt 0 ]; do
+		echo "    commit #$i" &&
+		echo "    note for commit #$i" &&
+		i=$(($i-1));
+	done > expect &&
+	test_cmp expect output
+}
+
+test_expect_success "setup: create a couple of commits" '
+
+	test_tick &&
+	cat <<INPUT_END >input &&
+commit refs/heads/master
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+commit #1
+COMMIT
+
+M 644 inline file
+data <<EOF
+file in commit #1
+EOF
+
+INPUT_END
+
+	test_tick &&
+	cat <<INPUT_END >>input &&
+commit refs/heads/master
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+commit #2
+COMMIT
+
+M 644 inline file
+data <<EOF
+file in commit #2
+EOF
+
+INPUT_END
+	git fast-import --quiet <input
+'
+
+test_expect_success "create a notes tree with both notes and non-notes" '
+
+	commit1=$(git rev-parse refs/heads/master^) &&
+	commit2=$(git rev-parse refs/heads/master) &&
+	test_tick &&
+	cat <<INPUT_END >input &&
+commit refs/notes/commits
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+notes commit #1
+COMMIT
+
+N inline $commit1
+data <<EOF
+note for commit #1
+EOF
+
+N inline $commit2
+data <<EOF
+note for commit #2
+EOF
+
+INPUT_END
+	test_tick &&
+	cat <<INPUT_END >>input &&
+commit refs/notes/commits
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+notes commit #2
+COMMIT
+
+M 644 inline foobar/non-note.txt
+data <<EOF
+A non-note in a notes tree
+EOF
+
+N inline $commit2
+data <<EOF
+edited note for commit #2
+EOF
+
+INPUT_END
+	test_tick &&
+	cat <<INPUT_END >>input &&
+commit refs/notes/commits
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+notes commit #3
+COMMIT
+
+N inline $commit1
+data <<EOF
+edited note for commit #1
+EOF
+
+M 644 inline deadbeef
+data <<EOF
+non-note with SHA1-like name
+EOF
+
+M 644 inline de/adbeef
+data <<EOF
+another non-note with SHA1-like name
+EOF
+
+M 644 inline de/adbeefdeadbeefdeadbeefdeadbeefdeadbeef
+data <<EOF
+This is actually a valid note, albeit to a non-existing object.
+It is needed in order to trigger the "mishandling" of the dead/beef non-note.
+EOF
+
+M 644 inline dead/beef
+data <<EOF
+yet another non-note with SHA1-like name
+EOF
+
+INPUT_END
+	git fast-import --quiet <input &&
+	git config core.notesRef refs/notes/commits
+'
+
+cat >expect <<EXPECT_END
+    commit #2
+    edited note for commit #2
+    commit #1
+    edited note for commit #1
+EXPECT_END
+
+test_expect_success "verify contents of notes" '
+
+	git log | grep "^    " > actual &&
+	test_cmp expect actual
+'
+
+cat >expect_nn1 <<EXPECT_END
+A non-note in a notes tree
+EXPECT_END
+cat >expect_nn2 <<EXPECT_END
+non-note with SHA1-like name
+EXPECT_END
+cat >expect_nn3 <<EXPECT_END
+another non-note with SHA1-like name
+EXPECT_END
+cat >expect_nn4 <<EXPECT_END
+yet another non-note with SHA1-like name
+EXPECT_END
+
+test_expect_success "verify contents of non-notes" '
+
+	git cat-file -p refs/notes/commits:foobar/non-note.txt > actual_nn1 &&
+	test_cmp expect_nn1 actual_nn1 &&
+	git cat-file -p refs/notes/commits:deadbeef > actual_nn2 &&
+	test_cmp expect_nn2 actual_nn2 &&
+	git cat-file -p refs/notes/commits:de/adbeef > actual_nn3 &&
+	test_cmp expect_nn3 actual_nn3 &&
+	git cat-file -p refs/notes/commits:dead/beef > actual_nn4 &&
+	test_cmp expect_nn4 actual_nn4
+'
+
+test_expect_success "git-notes preserves non-notes" '
+
+	test_tick &&
+	git notes add -f -m "foo bar"
+'
+
+test_expect_success "verify contents of non-notes after git-notes" '
+
+	git cat-file -p refs/notes/commits:foobar/non-note.txt > actual_nn1 &&
+	test_cmp expect_nn1 actual_nn1 &&
+	git cat-file -p refs/notes/commits:deadbeef > actual_nn2 &&
+	test_cmp expect_nn2 actual_nn2 &&
+	git cat-file -p refs/notes/commits:de/adbeef > actual_nn3 &&
+	test_cmp expect_nn3 actual_nn3 &&
+	git cat-file -p refs/notes/commits:dead/beef > actual_nn4 &&
+	test_cmp expect_nn4 actual_nn4
+'
+
+test_done
diff --git a/t/t3305-notes-fanout.sh b/t/t3305-notes-fanout.sh
new file mode 100755
index 0000000..b1ea64b
--- /dev/null
+++ b/t/t3305-notes-fanout.sh
@@ -0,0 +1,95 @@
+#!/bin/sh
+
+test_description='Test that adding/removing many notes triggers automatic fanout restructuring'
+
+. ./test-lib.sh
+
+test_expect_success 'creating many notes with git-notes' '
+	num_notes=300 &&
+	i=0 &&
+	while test $i -lt $num_notes
+	do
+		i=$(($i + 1)) &&
+		test_tick &&
+		echo "file for commit #$i" > file &&
+		git add file &&
+		git commit -q -m "commit #$i" &&
+		git notes add -m "note #$i" || return 1
+	done
+'
+
+test_expect_success 'many notes created correctly with git-notes' '
+	git log | grep "^    " > output &&
+	i=300 &&
+	while test $i -gt 0
+	do
+		echo "    commit #$i" &&
+		echo "    note #$i" &&
+		i=$(($i - 1));
+	done > expect &&
+	test_cmp expect output
+'
+
+test_expect_success 'many notes created with git-notes triggers fanout' '
+	# Expect entire notes tree to have a fanout == 1
+	git ls-tree -r --name-only refs/notes/commits |
+	while read path
+	do
+		case "$path" in
+		??/??????????????????????????????????????)
+			: true
+			;;
+		*)
+			echo "Invalid path \"$path\"" &&
+			return 1
+			;;
+		esac
+	done
+'
+
+test_expect_success 'deleting most notes with git-notes' '
+	num_notes=250 &&
+	i=0 &&
+	git rev-list HEAD |
+	while read sha1
+	do
+		i=$(($i + 1)) &&
+		if test $i -gt $num_notes
+		then
+			break
+		fi &&
+		test_tick &&
+		git notes remove "$sha1"
+	done
+'
+
+test_expect_success 'most notes deleted correctly with git-notes' '
+	git log HEAD~250 | grep "^    " > output &&
+	i=50 &&
+	while test $i -gt 0
+	do
+		echo "    commit #$i" &&
+		echo "    note #$i" &&
+		i=$(($i - 1));
+	done > expect &&
+	test_cmp expect output
+'
+
+test_expect_success 'deleting most notes triggers fanout consolidation' '
+	# Expect entire notes tree to have a fanout == 0
+	git ls-tree -r --name-only refs/notes/commits |
+	while read path
+	do
+		case "$path" in
+		????????????????????????????????????????)
+			: true
+			;;
+		*)
+			echo "Invalid path \"$path\"" &&
+			return 1
+			;;
+		esac
+	done
+'
+
+test_done
diff --git a/t/t3306-notes-prune.sh b/t/t3306-notes-prune.sh
new file mode 100755
index 0000000..86bf909
--- /dev/null
+++ b/t/t3306-notes-prune.sh
@@ -0,0 +1,141 @@
+#!/bin/sh
+
+test_description='Test git notes prune'
+
+. ./test-lib.sh
+
+test_expect_success 'setup: create a few commits with notes' '
+
+	: > file1 &&
+	git add file1 &&
+	test_tick &&
+	git commit -m 1st &&
+	git notes add -m "Note #1" &&
+	: > file2 &&
+	git add file2 &&
+	test_tick &&
+	git commit -m 2nd &&
+	git notes add -m "Note #2" &&
+	: > file3 &&
+	git add file3 &&
+	test_tick &&
+	git commit -m 3rd &&
+	COMMIT_FILE=.git/objects/5e/e1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
+	test -f $COMMIT_FILE &&
+	test-chmtime =+0 $COMMIT_FILE &&
+	git notes add -m "Note #3"
+'
+
+cat > expect <<END_OF_LOG
+commit 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:15:13 2005 -0700
+
+    3rd
+
+Notes:
+    Note #3
+
+commit 08341ad9e94faa089d60fd3f523affb25c6da189
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:14:13 2005 -0700
+
+    2nd
+
+Notes:
+    Note #2
+
+commit ab5f302035f2e7aaf04265f08b42034c23256e1f
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:13:13 2005 -0700
+
+    1st
+
+Notes:
+    Note #1
+END_OF_LOG
+
+test_expect_success 'verify commits and notes' '
+
+	git log > actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'remove some commits' '
+
+	git reset --hard HEAD~1 &&
+	git reflog expire --expire=now HEAD &&
+	git gc --prune=now
+'
+
+test_expect_success 'verify that commits are gone' '
+
+	test_must_fail git cat-file -p 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
+	git cat-file -p 08341ad9e94faa089d60fd3f523affb25c6da189 &&
+	git cat-file -p ab5f302035f2e7aaf04265f08b42034c23256e1f
+'
+
+test_expect_success 'verify that notes are still present' '
+
+	git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
+	git notes show 08341ad9e94faa089d60fd3f523affb25c6da189 &&
+	git notes show ab5f302035f2e7aaf04265f08b42034c23256e1f
+'
+
+test_expect_success 'prune -n does not remove notes' '
+
+	git notes list > expect &&
+	git notes prune -n &&
+	git notes list > actual &&
+	test_cmp expect actual
+'
+
+cat > expect <<EOF
+5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29
+EOF
+
+test_expect_success 'prune -n lists prunable notes' '
+
+
+	git notes prune -n > actual &&
+	test_cmp expect actual
+'
+
+
+test_expect_success 'prune notes' '
+
+	git notes prune
+'
+
+test_expect_success 'verify that notes are gone' '
+
+	test_must_fail git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
+	git notes show 08341ad9e94faa089d60fd3f523affb25c6da189 &&
+	git notes show ab5f302035f2e7aaf04265f08b42034c23256e1f
+'
+
+test_expect_success 'remove some commits' '
+
+	git reset --hard HEAD~1 &&
+	git reflog expire --expire=now HEAD &&
+	git gc --prune=now
+'
+
+cat > expect <<EOF
+08341ad9e94faa089d60fd3f523affb25c6da189
+EOF
+
+test_expect_success 'prune -v notes' '
+
+	git notes prune -v > actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'verify that notes are gone' '
+
+	test_must_fail git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
+	test_must_fail git notes show 08341ad9e94faa089d60fd3f523affb25c6da189 &&
+	git notes show ab5f302035f2e7aaf04265f08b42034c23256e1f
+'
+
+test_done
diff --git a/t/t3307-notes-man.sh b/t/t3307-notes-man.sh
new file mode 100755
index 0000000..1aa366a
--- /dev/null
+++ b/t/t3307-notes-man.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+test_description='Examples from the git-notes man page
+
+Make sure the manual is not full of lies.'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	test_commit A &&
+	test_commit B &&
+	test_commit C
+'
+
+test_expect_success 'example 1: notes to add an Acked-by line' '
+	cat <<-\EOF >expect &&
+	    B
+
+	Notes:
+	    Acked-by: A C Ker <acker@example.com>
+	EOF
+	git notes add -m "Acked-by: A C Ker <acker@example.com>" B &&
+	git show -s B^{commit} >log &&
+	tail -n 4 log >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'example 2: binary notes' '
+	cp "$TEST_DIRECTORY"/test-binary-1.png . &&
+	git checkout B &&
+	blob=$(git hash-object -w test-binary-1.png) &&
+	git notes --ref=logo add -C "$blob" &&
+	git notes --ref=logo copy B C &&
+	git notes --ref=logo show C >actual &&
+	test_cmp test-binary-1.png actual
+'
+
+test_done
diff --git a/t/t3308-notes-merge.sh b/t/t3308-notes-merge.sh
new file mode 100755
index 0000000..24d82b4
--- /dev/null
+++ b/t/t3308-notes-merge.sh
@@ -0,0 +1,368 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Johan Herland
+#
+
+test_description='Test merging of notes trees'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+	test_commit 1st &&
+	test_commit 2nd &&
+	test_commit 3rd &&
+	test_commit 4th &&
+	test_commit 5th &&
+	# Create notes on 4 first commits
+	git config core.notesRef refs/notes/x &&
+	git notes add -m "Notes on 1st commit" 1st &&
+	git notes add -m "Notes on 2nd commit" 2nd &&
+	git notes add -m "Notes on 3rd commit" 3rd &&
+	git notes add -m "Notes on 4th commit" 4th
+'
+
+commit_sha1=$(git rev-parse 1st^{commit})
+commit_sha2=$(git rev-parse 2nd^{commit})
+commit_sha3=$(git rev-parse 3rd^{commit})
+commit_sha4=$(git rev-parse 4th^{commit})
+commit_sha5=$(git rev-parse 5th^{commit})
+
+verify_notes () {
+	notes_ref="$1"
+	git -c core.notesRef="refs/notes/$notes_ref" notes |
+		sort >"output_notes_$notes_ref" &&
+	test_cmp "expect_notes_$notes_ref" "output_notes_$notes_ref" &&
+	git -c core.notesRef="refs/notes/$notes_ref" log --format="%H %s%n%N" \
+		>"output_log_$notes_ref" &&
+	test_cmp "expect_log_$notes_ref" "output_log_$notes_ref"
+}
+
+cat <<EOF | sort >expect_notes_x
+5e93d24084d32e1cb61f7070505b9d2530cca987 $commit_sha4
+8366731eeee53787d2bdf8fc1eff7d94757e8da0 $commit_sha3
+eede89064cd42441590d6afec6c37b321ada3389 $commit_sha2
+daa55ffad6cb99bf64226532147ffcaf5ce8bdd1 $commit_sha1
+EOF
+
+cat >expect_log_x <<EOF
+$commit_sha5 5th
+
+$commit_sha4 4th
+Notes on 4th commit
+
+$commit_sha3 3rd
+Notes on 3rd commit
+
+$commit_sha2 2nd
+Notes on 2nd commit
+
+$commit_sha1 1st
+Notes on 1st commit
+
+EOF
+
+test_expect_success 'verify initial notes (x)' '
+	verify_notes x
+'
+
+cp expect_notes_x expect_notes_y
+cp expect_log_x expect_log_y
+
+test_expect_success 'fail to merge empty notes ref into empty notes ref (z => y)' '
+	test_must_fail git -c "core.notesRef=refs/notes/y" notes merge z
+'
+
+test_expect_success 'fail to merge into various non-notes refs' '
+	test_must_fail git -c "core.notesRef=refs/notes" notes merge x &&
+	test_must_fail git -c "core.notesRef=refs/notes/" notes merge x &&
+	mkdir -p .git/refs/notes/dir &&
+	test_must_fail git -c "core.notesRef=refs/notes/dir" notes merge x &&
+	test_must_fail git -c "core.notesRef=refs/notes/dir/" notes merge x &&
+	test_must_fail git -c "core.notesRef=refs/heads/master" notes merge x &&
+	test_must_fail git -c "core.notesRef=refs/notes/y:" notes merge x &&
+	test_must_fail git -c "core.notesRef=refs/notes/y:foo" notes merge x &&
+	test_must_fail git -c "core.notesRef=refs/notes/foo^{bar" notes merge x
+'
+
+test_expect_success 'fail to merge various non-note-trees' '
+	git config core.notesRef refs/notes/y &&
+	test_must_fail git notes merge refs/notes &&
+	test_must_fail git notes merge refs/notes/ &&
+	test_must_fail git notes merge refs/notes/dir &&
+	test_must_fail git notes merge refs/notes/dir/ &&
+	test_must_fail git notes merge refs/heads/master &&
+	test_must_fail git notes merge x: &&
+	test_must_fail git notes merge x:foo &&
+	test_must_fail git notes merge foo^{bar
+'
+
+test_expect_success 'merge notes into empty notes ref (x => y)' '
+	git config core.notesRef refs/notes/y &&
+	git notes merge x &&
+	verify_notes y &&
+	# x and y should point to the same notes commit
+	test "$(git rev-parse refs/notes/x)" = "$(git rev-parse refs/notes/y)"
+'
+
+test_expect_success 'merge empty notes ref (z => y)' '
+	git notes merge z &&
+	# y should not change (still == x)
+	test "$(git rev-parse refs/notes/x)" = "$(git rev-parse refs/notes/y)"
+'
+
+test_expect_success 'change notes on other notes ref (y)' '
+	# Not touching notes to 1st commit
+	git notes remove 2nd &&
+	git notes append -m "More notes on 3rd commit" 3rd &&
+	git notes add -f -m "New notes on 4th commit" 4th &&
+	git notes add -m "Notes on 5th commit" 5th
+'
+
+test_expect_success 'merge previous notes commit (y^ => y) => No-op' '
+	pre_state="$(git rev-parse refs/notes/y)" &&
+	git notes merge y^ &&
+	# y should not move
+	test "$pre_state" = "$(git rev-parse refs/notes/y)"
+'
+
+cat <<EOF | sort >expect_notes_y
+0f2efbd00262f2fd41dfae33df8765618eeacd99 $commit_sha5
+dec2502dac3ea161543f71930044deff93fa945c $commit_sha4
+4069cdb399fd45463ec6eef8e051a16a03592d91 $commit_sha3
+daa55ffad6cb99bf64226532147ffcaf5ce8bdd1 $commit_sha1
+EOF
+
+cat >expect_log_y <<EOF
+$commit_sha5 5th
+Notes on 5th commit
+
+$commit_sha4 4th
+New notes on 4th commit
+
+$commit_sha3 3rd
+Notes on 3rd commit
+
+More notes on 3rd commit
+
+$commit_sha2 2nd
+
+$commit_sha1 1st
+Notes on 1st commit
+
+EOF
+
+test_expect_success 'verify changed notes on other notes ref (y)' '
+	verify_notes y
+'
+
+test_expect_success 'verify unchanged notes on original notes ref (x)' '
+	verify_notes x
+'
+
+test_expect_success 'merge original notes (x) into changed notes (y) => No-op' '
+	git notes merge -vvv x &&
+	verify_notes y &&
+	verify_notes x
+'
+
+cp expect_notes_y expect_notes_x
+cp expect_log_y expect_log_x
+
+test_expect_success 'merge changed (y) into original (x) => Fast-forward' '
+	git config core.notesRef refs/notes/x &&
+	git notes merge y &&
+	verify_notes x &&
+	verify_notes y &&
+	# x and y should point to same the notes commit
+	test "$(git rev-parse refs/notes/x)" = "$(git rev-parse refs/notes/y)"
+'
+
+test_expect_success 'merge empty notes ref (z => y)' '
+	# Prepare empty (but valid) notes ref (z)
+	git config core.notesRef refs/notes/z &&
+	git notes add -m "foo" &&
+	git notes remove &&
+	git notes >output_notes_z &&
+	test_cmp /dev/null output_notes_z &&
+	# Do the merge (z => y)
+	git config core.notesRef refs/notes/y &&
+	git notes merge z &&
+	verify_notes y &&
+	# y should no longer point to the same notes commit as x
+	test "$(git rev-parse refs/notes/x)" != "$(git rev-parse refs/notes/y)"
+'
+
+cat <<EOF | sort >expect_notes_y
+0f2efbd00262f2fd41dfae33df8765618eeacd99 $commit_sha5
+dec2502dac3ea161543f71930044deff93fa945c $commit_sha4
+4069cdb399fd45463ec6eef8e051a16a03592d91 $commit_sha3
+d000d30e6ddcfce3a8122c403226a2ce2fd04d9d $commit_sha2
+43add6bd0c8c0bc871ac7991e0f5573cfba27804 $commit_sha1
+EOF
+
+cat >expect_log_y <<EOF
+$commit_sha5 5th
+Notes on 5th commit
+
+$commit_sha4 4th
+New notes on 4th commit
+
+$commit_sha3 3rd
+Notes on 3rd commit
+
+More notes on 3rd commit
+
+$commit_sha2 2nd
+New notes on 2nd commit
+
+$commit_sha1 1st
+Notes on 1st commit
+
+More notes on 1st commit
+
+EOF
+
+test_expect_success 'change notes on other notes ref (y)' '
+	# Append to 1st commit notes
+	git notes append -m "More notes on 1st commit" 1st &&
+	# Add new notes to 2nd commit
+	git notes add -m "New notes on 2nd commit" 2nd &&
+	verify_notes y
+'
+
+cat <<EOF | sort >expect_notes_x
+0f2efbd00262f2fd41dfae33df8765618eeacd99 $commit_sha5
+1f257a3a90328557c452f0817d6cc50c89d315d4 $commit_sha4
+daa55ffad6cb99bf64226532147ffcaf5ce8bdd1 $commit_sha1
+EOF
+
+cat >expect_log_x <<EOF
+$commit_sha5 5th
+Notes on 5th commit
+
+$commit_sha4 4th
+New notes on 4th commit
+
+More notes on 4th commit
+
+$commit_sha3 3rd
+
+$commit_sha2 2nd
+
+$commit_sha1 1st
+Notes on 1st commit
+
+EOF
+
+test_expect_success 'change notes on notes ref (x)' '
+	git config core.notesRef refs/notes/x &&
+	git notes remove 3rd &&
+	git notes append -m "More notes on 4th commit" 4th &&
+	verify_notes x
+'
+
+cat <<EOF | sort >expect_notes_x
+0f2efbd00262f2fd41dfae33df8765618eeacd99 $commit_sha5
+1f257a3a90328557c452f0817d6cc50c89d315d4 $commit_sha4
+d000d30e6ddcfce3a8122c403226a2ce2fd04d9d $commit_sha2
+43add6bd0c8c0bc871ac7991e0f5573cfba27804 $commit_sha1
+EOF
+
+cat >expect_log_x <<EOF
+$commit_sha5 5th
+Notes on 5th commit
+
+$commit_sha4 4th
+New notes on 4th commit
+
+More notes on 4th commit
+
+$commit_sha3 3rd
+
+$commit_sha2 2nd
+New notes on 2nd commit
+
+$commit_sha1 1st
+Notes on 1st commit
+
+More notes on 1st commit
+
+EOF
+
+test_expect_success 'merge y into x => Non-conflicting 3-way merge' '
+	git notes merge y &&
+	verify_notes x &&
+	verify_notes y
+'
+
+cat <<EOF | sort >expect_notes_w
+05a4927951bcef347f51486575b878b2b60137f2 $commit_sha3
+d000d30e6ddcfce3a8122c403226a2ce2fd04d9d $commit_sha2
+EOF
+
+cat >expect_log_w <<EOF
+$commit_sha5 5th
+
+$commit_sha4 4th
+
+$commit_sha3 3rd
+New notes on 3rd commit
+
+$commit_sha2 2nd
+New notes on 2nd commit
+
+$commit_sha1 1st
+
+EOF
+
+test_expect_success 'create notes on new, separate notes ref (w)' '
+	git config core.notesRef refs/notes/w &&
+	# Add same note as refs/notes/y on 2nd commit
+	git notes add -m "New notes on 2nd commit" 2nd &&
+	# Add new note on 3rd commit (non-conflicting)
+	git notes add -m "New notes on 3rd commit" 3rd &&
+	# Verify state of notes on new, separate notes ref (w)
+	verify_notes w
+'
+
+cat <<EOF | sort >expect_notes_x
+0f2efbd00262f2fd41dfae33df8765618eeacd99 $commit_sha5
+1f257a3a90328557c452f0817d6cc50c89d315d4 $commit_sha4
+05a4927951bcef347f51486575b878b2b60137f2 $commit_sha3
+d000d30e6ddcfce3a8122c403226a2ce2fd04d9d $commit_sha2
+43add6bd0c8c0bc871ac7991e0f5573cfba27804 $commit_sha1
+EOF
+
+cat >expect_log_x <<EOF
+$commit_sha5 5th
+Notes on 5th commit
+
+$commit_sha4 4th
+New notes on 4th commit
+
+More notes on 4th commit
+
+$commit_sha3 3rd
+New notes on 3rd commit
+
+$commit_sha2 2nd
+New notes on 2nd commit
+
+$commit_sha1 1st
+Notes on 1st commit
+
+More notes on 1st commit
+
+EOF
+
+test_expect_success 'merge w into x => Non-conflicting history-less merge' '
+	git config core.notesRef refs/notes/x &&
+	git notes merge w &&
+	# Verify new state of notes on other notes ref (x)
+	verify_notes x &&
+	# Also verify that nothing changed on other notes refs (y and w)
+	verify_notes y &&
+	verify_notes w
+'
+
+test_done
diff --git a/t/t3309-notes-merge-auto-resolve.sh b/t/t3309-notes-merge-auto-resolve.sh
new file mode 100755
index 0000000..461fd84
--- /dev/null
+++ b/t/t3309-notes-merge-auto-resolve.sh
@@ -0,0 +1,647 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Johan Herland
+#
+
+test_description='Test notes merging with auto-resolving strategies'
+
+. ./test-lib.sh
+
+# Set up a notes merge scenario with all kinds of potential conflicts
+test_expect_success 'setup commits' '
+	test_commit 1st &&
+	test_commit 2nd &&
+	test_commit 3rd &&
+	test_commit 4th &&
+	test_commit 5th &&
+	test_commit 6th &&
+	test_commit 7th &&
+	test_commit 8th &&
+	test_commit 9th &&
+	test_commit 10th &&
+	test_commit 11th &&
+	test_commit 12th &&
+	test_commit 13th &&
+	test_commit 14th &&
+	test_commit 15th
+'
+
+commit_sha1=$(git rev-parse 1st^{commit})
+commit_sha2=$(git rev-parse 2nd^{commit})
+commit_sha3=$(git rev-parse 3rd^{commit})
+commit_sha4=$(git rev-parse 4th^{commit})
+commit_sha5=$(git rev-parse 5th^{commit})
+commit_sha6=$(git rev-parse 6th^{commit})
+commit_sha7=$(git rev-parse 7th^{commit})
+commit_sha8=$(git rev-parse 8th^{commit})
+commit_sha9=$(git rev-parse 9th^{commit})
+commit_sha10=$(git rev-parse 10th^{commit})
+commit_sha11=$(git rev-parse 11th^{commit})
+commit_sha12=$(git rev-parse 12th^{commit})
+commit_sha13=$(git rev-parse 13th^{commit})
+commit_sha14=$(git rev-parse 14th^{commit})
+commit_sha15=$(git rev-parse 15th^{commit})
+
+verify_notes () {
+	notes_ref="$1"
+	suffix="$2"
+	git -c core.notesRef="refs/notes/$notes_ref" notes |
+		sort >"output_notes_$suffix" &&
+	test_cmp "expect_notes_$suffix" "output_notes_$suffix" &&
+	git -c core.notesRef="refs/notes/$notes_ref" log --format="%H %s%n%N" \
+		>"output_log_$suffix" &&
+	test_cmp "expect_log_$suffix" "output_log_$suffix"
+}
+
+test_expect_success 'setup merge base (x)' '
+	git config core.notesRef refs/notes/x &&
+	git notes add -m "x notes on 6th commit" 6th &&
+	git notes add -m "x notes on 7th commit" 7th &&
+	git notes add -m "x notes on 8th commit" 8th &&
+	git notes add -m "x notes on 9th commit" 9th &&
+	git notes add -m "x notes on 10th commit" 10th &&
+	git notes add -m "x notes on 11th commit" 11th &&
+	git notes add -m "x notes on 12th commit" 12th &&
+	git notes add -m "x notes on 13th commit" 13th &&
+	git notes add -m "x notes on 14th commit" 14th &&
+	git notes add -m "x notes on 15th commit" 15th
+'
+
+cat <<EOF | sort >expect_notes_x
+457a85d6c814ea208550f15fcc48f804ac8dc023 $commit_sha15
+b0c95b954301d69da2bc3723f4cb1680d355937c $commit_sha14
+5d30216a129eeffa97d9694ffe8c74317a560315 $commit_sha13
+dd161bc149470fd890dd4ab52a4cbd79bbd18c36 $commit_sha12
+7abbc45126d680336fb24294f013a7cdfa3ed545 $commit_sha11
+b8d03e173f67f6505a76f6e00cf93440200dd9be $commit_sha10
+20c613c835011c48a5abe29170a2402ca6354910 $commit_sha9
+a3daf8a1e4e5dc3409a303ad8481d57bfea7f5d6 $commit_sha8
+897003322b53bc6ca098e9324ee508362347e734 $commit_sha7
+11d97fdebfa5ceee540a3da07bce6fa0222bc082 $commit_sha6
+EOF
+
+cat >expect_log_x <<EOF
+$commit_sha15 15th
+x notes on 15th commit
+
+$commit_sha14 14th
+x notes on 14th commit
+
+$commit_sha13 13th
+x notes on 13th commit
+
+$commit_sha12 12th
+x notes on 12th commit
+
+$commit_sha11 11th
+x notes on 11th commit
+
+$commit_sha10 10th
+x notes on 10th commit
+
+$commit_sha9 9th
+x notes on 9th commit
+
+$commit_sha8 8th
+x notes on 8th commit
+
+$commit_sha7 7th
+x notes on 7th commit
+
+$commit_sha6 6th
+x notes on 6th commit
+
+$commit_sha5 5th
+
+$commit_sha4 4th
+
+$commit_sha3 3rd
+
+$commit_sha2 2nd
+
+$commit_sha1 1st
+
+EOF
+
+test_expect_success 'verify state of merge base (x)' 'verify_notes x x'
+
+test_expect_success 'setup local branch (y)' '
+	git update-ref refs/notes/y refs/notes/x &&
+	git config core.notesRef refs/notes/y &&
+	git notes add -f -m "y notes on 3rd commit" 3rd &&
+	git notes add -f -m "y notes on 4th commit" 4th &&
+	git notes add -f -m "y notes on 5th commit" 5th &&
+	git notes remove 6th &&
+	git notes remove 7th &&
+	git notes remove 8th &&
+	git notes add -f -m "y notes on 12th commit" 12th &&
+	git notes add -f -m "y notes on 13th commit" 13th &&
+	git notes add -f -m "y notes on 14th commit" 14th &&
+	git notes add -f -m "y notes on 15th commit" 15th
+'
+
+cat <<EOF | sort >expect_notes_y
+68b8630d25516028bed862719855b3d6768d7833 $commit_sha15
+5de7ea7ad4f47e7ff91989fb82234634730f75df $commit_sha14
+3a631fdb6f41b05b55d8f4baf20728ba8f6fccbc $commit_sha13
+a66055fa82f7a03fe0c02a6aba3287a85abf7c62 $commit_sha12
+7abbc45126d680336fb24294f013a7cdfa3ed545 $commit_sha11
+b8d03e173f67f6505a76f6e00cf93440200dd9be $commit_sha10
+20c613c835011c48a5abe29170a2402ca6354910 $commit_sha9
+154508c7a0bcad82b6fe4b472bc4c26b3bf0825b $commit_sha5
+e2bfd06a37dd2031684a59a6e2b033e212239c78 $commit_sha4
+5772f42408c0dd6f097a7ca2d24de0e78d1c46b1 $commit_sha3
+EOF
+
+cat >expect_log_y <<EOF
+$commit_sha15 15th
+y notes on 15th commit
+
+$commit_sha14 14th
+y notes on 14th commit
+
+$commit_sha13 13th
+y notes on 13th commit
+
+$commit_sha12 12th
+y notes on 12th commit
+
+$commit_sha11 11th
+x notes on 11th commit
+
+$commit_sha10 10th
+x notes on 10th commit
+
+$commit_sha9 9th
+x notes on 9th commit
+
+$commit_sha8 8th
+
+$commit_sha7 7th
+
+$commit_sha6 6th
+
+$commit_sha5 5th
+y notes on 5th commit
+
+$commit_sha4 4th
+y notes on 4th commit
+
+$commit_sha3 3rd
+y notes on 3rd commit
+
+$commit_sha2 2nd
+
+$commit_sha1 1st
+
+EOF
+
+test_expect_success 'verify state of local branch (y)' 'verify_notes y y'
+
+test_expect_success 'setup remote branch (z)' '
+	git update-ref refs/notes/z refs/notes/x &&
+	git config core.notesRef refs/notes/z &&
+	git notes add -f -m "z notes on 2nd commit" 2nd &&
+	git notes add -f -m "y notes on 4th commit" 4th &&
+	git notes add -f -m "z notes on 5th commit" 5th &&
+	git notes remove 6th &&
+	git notes add -f -m "z notes on 8th commit" 8th &&
+	git notes remove 9th &&
+	git notes add -f -m "z notes on 11th commit" 11th &&
+	git notes remove 12th &&
+	git notes add -f -m "y notes on 14th commit" 14th &&
+	git notes add -f -m "z notes on 15th commit" 15th
+'
+
+cat <<EOF | sort >expect_notes_z
+9b4b2c61f0615412da3c10f98ff85b57c04ec765 $commit_sha15
+5de7ea7ad4f47e7ff91989fb82234634730f75df $commit_sha14
+5d30216a129eeffa97d9694ffe8c74317a560315 $commit_sha13
+7e3c53503a3db8dd996cb62e37c66e070b44b54d $commit_sha11
+b8d03e173f67f6505a76f6e00cf93440200dd9be $commit_sha10
+851e1638784a884c7dd26c5d41f3340f6387413a $commit_sha8
+897003322b53bc6ca098e9324ee508362347e734 $commit_sha7
+99fc34adfc400b95c67b013115e37e31aa9a6d23 $commit_sha5
+e2bfd06a37dd2031684a59a6e2b033e212239c78 $commit_sha4
+283b48219aee9a4105f6cab337e789065c82c2b9 $commit_sha2
+EOF
+
+cat >expect_log_z <<EOF
+$commit_sha15 15th
+z notes on 15th commit
+
+$commit_sha14 14th
+y notes on 14th commit
+
+$commit_sha13 13th
+x notes on 13th commit
+
+$commit_sha12 12th
+
+$commit_sha11 11th
+z notes on 11th commit
+
+$commit_sha10 10th
+x notes on 10th commit
+
+$commit_sha9 9th
+
+$commit_sha8 8th
+z notes on 8th commit
+
+$commit_sha7 7th
+x notes on 7th commit
+
+$commit_sha6 6th
+
+$commit_sha5 5th
+z notes on 5th commit
+
+$commit_sha4 4th
+y notes on 4th commit
+
+$commit_sha3 3rd
+
+$commit_sha2 2nd
+z notes on 2nd commit
+
+$commit_sha1 1st
+
+EOF
+
+test_expect_success 'verify state of remote branch (z)' 'verify_notes z z'
+
+# At this point, before merging z into y, we have the following status:
+#
+# commit | base/x  | local/y | remote/z | diff from x to y/z         | result
+# -------|---------|---------|----------|----------------------------|-------
+# 1st    | [none]  | [none]  | [none]   | unchanged / unchanged      | [none]
+# 2nd    | [none]  | [none]  | 283b482  | unchanged / added          | 283b482
+# 3rd    | [none]  | 5772f42 | [none]   | added     / unchanged      | 5772f42
+# 4th    | [none]  | e2bfd06 | e2bfd06  | added     / added (same)   | e2bfd06
+# 5th    | [none]  | 154508c | 99fc34a  | added     / added (diff)   | ???
+# 6th    | 11d97fd | [none]  | [none]   | removed   / removed        | [none]
+# 7th    | 8970033 | [none]  | 8970033  | removed   / unchanged      | [none]
+# 8th    | a3daf8a | [none]  | 851e163  | removed   / changed        | ???
+# 9th    | 20c613c | 20c613c | [none]   | unchanged / removed        | [none]
+# 10th   | b8d03e1 | b8d03e1 | b8d03e1  | unchanged / unchanged      | b8d03e1
+# 11th   | 7abbc45 | 7abbc45 | 7e3c535  | unchanged / changed        | 7e3c535
+# 12th   | dd161bc | a66055f | [none]   | changed   / removed        | ???
+# 13th   | 5d30216 | 3a631fd | 5d30216  | changed   / unchanged      | 3a631fd
+# 14th   | b0c95b9 | 5de7ea7 | 5de7ea7  | changed   / changed (same) | 5de7ea7
+# 15th   | 457a85d | 68b8630 | 9b4b2c6  | changed   / changed (diff) | ???
+
+test_expect_success 'merge z into y with invalid strategy => Fail/No changes' '
+	git config core.notesRef refs/notes/y &&
+	test_must_fail git notes merge --strategy=foo z &&
+	# Verify no changes (y)
+	verify_notes y y
+'
+
+cat <<EOF | sort >expect_notes_ours
+68b8630d25516028bed862719855b3d6768d7833 $commit_sha15
+5de7ea7ad4f47e7ff91989fb82234634730f75df $commit_sha14
+3a631fdb6f41b05b55d8f4baf20728ba8f6fccbc $commit_sha13
+a66055fa82f7a03fe0c02a6aba3287a85abf7c62 $commit_sha12
+7e3c53503a3db8dd996cb62e37c66e070b44b54d $commit_sha11
+b8d03e173f67f6505a76f6e00cf93440200dd9be $commit_sha10
+154508c7a0bcad82b6fe4b472bc4c26b3bf0825b $commit_sha5
+e2bfd06a37dd2031684a59a6e2b033e212239c78 $commit_sha4
+5772f42408c0dd6f097a7ca2d24de0e78d1c46b1 $commit_sha3
+283b48219aee9a4105f6cab337e789065c82c2b9 $commit_sha2
+EOF
+
+cat >expect_log_ours <<EOF
+$commit_sha15 15th
+y notes on 15th commit
+
+$commit_sha14 14th
+y notes on 14th commit
+
+$commit_sha13 13th
+y notes on 13th commit
+
+$commit_sha12 12th
+y notes on 12th commit
+
+$commit_sha11 11th
+z notes on 11th commit
+
+$commit_sha10 10th
+x notes on 10th commit
+
+$commit_sha9 9th
+
+$commit_sha8 8th
+
+$commit_sha7 7th
+
+$commit_sha6 6th
+
+$commit_sha5 5th
+y notes on 5th commit
+
+$commit_sha4 4th
+y notes on 4th commit
+
+$commit_sha3 3rd
+y notes on 3rd commit
+
+$commit_sha2 2nd
+z notes on 2nd commit
+
+$commit_sha1 1st
+
+EOF
+
+test_expect_success 'merge z into y with "ours" strategy => Non-conflicting 3-way merge' '
+	git notes merge --strategy=ours z &&
+	verify_notes y ours
+'
+
+test_expect_success 'reset to pre-merge state (y)' '
+	git update-ref refs/notes/y refs/notes/y^1 &&
+	# Verify pre-merge state
+	verify_notes y y
+'
+
+cat <<EOF | sort >expect_notes_theirs
+9b4b2c61f0615412da3c10f98ff85b57c04ec765 $commit_sha15
+5de7ea7ad4f47e7ff91989fb82234634730f75df $commit_sha14
+3a631fdb6f41b05b55d8f4baf20728ba8f6fccbc $commit_sha13
+7e3c53503a3db8dd996cb62e37c66e070b44b54d $commit_sha11
+b8d03e173f67f6505a76f6e00cf93440200dd9be $commit_sha10
+851e1638784a884c7dd26c5d41f3340f6387413a $commit_sha8
+99fc34adfc400b95c67b013115e37e31aa9a6d23 $commit_sha5
+e2bfd06a37dd2031684a59a6e2b033e212239c78 $commit_sha4
+5772f42408c0dd6f097a7ca2d24de0e78d1c46b1 $commit_sha3
+283b48219aee9a4105f6cab337e789065c82c2b9 $commit_sha2
+EOF
+
+cat >expect_log_theirs <<EOF
+$commit_sha15 15th
+z notes on 15th commit
+
+$commit_sha14 14th
+y notes on 14th commit
+
+$commit_sha13 13th
+y notes on 13th commit
+
+$commit_sha12 12th
+
+$commit_sha11 11th
+z notes on 11th commit
+
+$commit_sha10 10th
+x notes on 10th commit
+
+$commit_sha9 9th
+
+$commit_sha8 8th
+z notes on 8th commit
+
+$commit_sha7 7th
+
+$commit_sha6 6th
+
+$commit_sha5 5th
+z notes on 5th commit
+
+$commit_sha4 4th
+y notes on 4th commit
+
+$commit_sha3 3rd
+y notes on 3rd commit
+
+$commit_sha2 2nd
+z notes on 2nd commit
+
+$commit_sha1 1st
+
+EOF
+
+test_expect_success 'merge z into y with "theirs" strategy => Non-conflicting 3-way merge' '
+	git notes merge --strategy=theirs z &&
+	verify_notes y theirs
+'
+
+test_expect_success 'reset to pre-merge state (y)' '
+	git update-ref refs/notes/y refs/notes/y^1 &&
+	# Verify pre-merge state
+	verify_notes y y
+'
+
+cat <<EOF | sort >expect_notes_union
+7c4e546efd0fe939f876beb262ece02797880b54 $commit_sha15
+5de7ea7ad4f47e7ff91989fb82234634730f75df $commit_sha14
+3a631fdb6f41b05b55d8f4baf20728ba8f6fccbc $commit_sha13
+a66055fa82f7a03fe0c02a6aba3287a85abf7c62 $commit_sha12
+7e3c53503a3db8dd996cb62e37c66e070b44b54d $commit_sha11
+b8d03e173f67f6505a76f6e00cf93440200dd9be $commit_sha10
+851e1638784a884c7dd26c5d41f3340f6387413a $commit_sha8
+6c841cc36ea496027290967ca96bd2bef54dbb47 $commit_sha5
+e2bfd06a37dd2031684a59a6e2b033e212239c78 $commit_sha4
+5772f42408c0dd6f097a7ca2d24de0e78d1c46b1 $commit_sha3
+283b48219aee9a4105f6cab337e789065c82c2b9 $commit_sha2
+EOF
+
+cat >expect_log_union <<EOF
+$commit_sha15 15th
+y notes on 15th commit
+
+z notes on 15th commit
+
+$commit_sha14 14th
+y notes on 14th commit
+
+$commit_sha13 13th
+y notes on 13th commit
+
+$commit_sha12 12th
+y notes on 12th commit
+
+$commit_sha11 11th
+z notes on 11th commit
+
+$commit_sha10 10th
+x notes on 10th commit
+
+$commit_sha9 9th
+
+$commit_sha8 8th
+z notes on 8th commit
+
+$commit_sha7 7th
+
+$commit_sha6 6th
+
+$commit_sha5 5th
+y notes on 5th commit
+
+z notes on 5th commit
+
+$commit_sha4 4th
+y notes on 4th commit
+
+$commit_sha3 3rd
+y notes on 3rd commit
+
+$commit_sha2 2nd
+z notes on 2nd commit
+
+$commit_sha1 1st
+
+EOF
+
+test_expect_success 'merge z into y with "union" strategy => Non-conflicting 3-way merge' '
+	git notes merge --strategy=union z &&
+	verify_notes y union
+'
+
+test_expect_success 'reset to pre-merge state (y)' '
+	git update-ref refs/notes/y refs/notes/y^1 &&
+	# Verify pre-merge state
+	verify_notes y y
+'
+
+cat <<EOF | sort >expect_notes_union2
+d682107b8bf7a7aea1e537a8d5cb6a12b60135f1 $commit_sha15
+5de7ea7ad4f47e7ff91989fb82234634730f75df $commit_sha14
+3a631fdb6f41b05b55d8f4baf20728ba8f6fccbc $commit_sha13
+a66055fa82f7a03fe0c02a6aba3287a85abf7c62 $commit_sha12
+7e3c53503a3db8dd996cb62e37c66e070b44b54d $commit_sha11
+b8d03e173f67f6505a76f6e00cf93440200dd9be $commit_sha10
+851e1638784a884c7dd26c5d41f3340f6387413a $commit_sha8
+357b6ca14c7afd59b7f8b8aaaa6b8b723771135b $commit_sha5
+e2bfd06a37dd2031684a59a6e2b033e212239c78 $commit_sha4
+5772f42408c0dd6f097a7ca2d24de0e78d1c46b1 $commit_sha3
+283b48219aee9a4105f6cab337e789065c82c2b9 $commit_sha2
+EOF
+
+cat >expect_log_union2 <<EOF
+$commit_sha15 15th
+z notes on 15th commit
+
+y notes on 15th commit
+
+$commit_sha14 14th
+y notes on 14th commit
+
+$commit_sha13 13th
+y notes on 13th commit
+
+$commit_sha12 12th
+y notes on 12th commit
+
+$commit_sha11 11th
+z notes on 11th commit
+
+$commit_sha10 10th
+x notes on 10th commit
+
+$commit_sha9 9th
+
+$commit_sha8 8th
+z notes on 8th commit
+
+$commit_sha7 7th
+
+$commit_sha6 6th
+
+$commit_sha5 5th
+z notes on 5th commit
+
+y notes on 5th commit
+
+$commit_sha4 4th
+y notes on 4th commit
+
+$commit_sha3 3rd
+y notes on 3rd commit
+
+$commit_sha2 2nd
+z notes on 2nd commit
+
+$commit_sha1 1st
+
+EOF
+
+test_expect_success 'merge y into z with "union" strategy => Non-conflicting 3-way merge' '
+	git config core.notesRef refs/notes/z &&
+	git notes merge --strategy=union y &&
+	verify_notes z union2
+'
+
+test_expect_success 'reset to pre-merge state (z)' '
+	git update-ref refs/notes/z refs/notes/z^1 &&
+	# Verify pre-merge state
+	verify_notes z z
+'
+
+cat <<EOF | sort >expect_notes_cat_sort_uniq
+6be90240b5f54594203e25d9f2f64b7567175aee $commit_sha15
+5de7ea7ad4f47e7ff91989fb82234634730f75df $commit_sha14
+3a631fdb6f41b05b55d8f4baf20728ba8f6fccbc $commit_sha13
+a66055fa82f7a03fe0c02a6aba3287a85abf7c62 $commit_sha12
+7e3c53503a3db8dd996cb62e37c66e070b44b54d $commit_sha11
+b8d03e173f67f6505a76f6e00cf93440200dd9be $commit_sha10
+851e1638784a884c7dd26c5d41f3340f6387413a $commit_sha8
+660311d7f78dc53db12ac373a43fca7465381a7e $commit_sha5
+e2bfd06a37dd2031684a59a6e2b033e212239c78 $commit_sha4
+5772f42408c0dd6f097a7ca2d24de0e78d1c46b1 $commit_sha3
+283b48219aee9a4105f6cab337e789065c82c2b9 $commit_sha2
+EOF
+
+cat >expect_log_cat_sort_uniq <<EOF
+$commit_sha15 15th
+y notes on 15th commit
+z notes on 15th commit
+
+$commit_sha14 14th
+y notes on 14th commit
+
+$commit_sha13 13th
+y notes on 13th commit
+
+$commit_sha12 12th
+y notes on 12th commit
+
+$commit_sha11 11th
+z notes on 11th commit
+
+$commit_sha10 10th
+x notes on 10th commit
+
+$commit_sha9 9th
+
+$commit_sha8 8th
+z notes on 8th commit
+
+$commit_sha7 7th
+
+$commit_sha6 6th
+
+$commit_sha5 5th
+y notes on 5th commit
+z notes on 5th commit
+
+$commit_sha4 4th
+y notes on 4th commit
+
+$commit_sha3 3rd
+y notes on 3rd commit
+
+$commit_sha2 2nd
+z notes on 2nd commit
+
+$commit_sha1 1st
+
+EOF
+
+test_expect_success 'merge y into z with "cat_sort_uniq" strategy => Non-conflicting 3-way merge' '
+	git notes merge --strategy=cat_sort_uniq y &&
+	verify_notes z cat_sort_uniq
+'
+
+test_done
diff --git a/t/t3310-notes-merge-manual-resolve.sh b/t/t3310-notes-merge-manual-resolve.sh
new file mode 100755
index 0000000..195bb97
--- /dev/null
+++ b/t/t3310-notes-merge-manual-resolve.sh
@@ -0,0 +1,575 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Johan Herland
+#
+
+test_description='Test notes merging with manual conflict resolution'
+
+. ./test-lib.sh
+
+# Set up a notes merge scenario with different kinds of conflicts
+test_expect_success 'setup commits' '
+	test_commit 1st &&
+	test_commit 2nd &&
+	test_commit 3rd &&
+	test_commit 4th &&
+	test_commit 5th
+'
+
+commit_sha1=$(git rev-parse 1st^{commit})
+commit_sha2=$(git rev-parse 2nd^{commit})
+commit_sha3=$(git rev-parse 3rd^{commit})
+commit_sha4=$(git rev-parse 4th^{commit})
+commit_sha5=$(git rev-parse 5th^{commit})
+
+verify_notes () {
+	notes_ref="$1"
+	git -c core.notesRef="refs/notes/$notes_ref" notes |
+		sort >"output_notes_$notes_ref" &&
+	test_cmp "expect_notes_$notes_ref" "output_notes_$notes_ref" &&
+	git -c core.notesRef="refs/notes/$notes_ref" log --format="%H %s%n%N" \
+		>"output_log_$notes_ref" &&
+	test_cmp "expect_log_$notes_ref" "output_log_$notes_ref"
+}
+
+cat <<EOF | sort >expect_notes_x
+6e8e3febca3c2bb896704335cc4d0c34cb2f8715 $commit_sha4
+e5388c10860456ee60673025345fe2e153eb8cf8 $commit_sha3
+ceefa674873670e7ecd131814d909723cce2b669 $commit_sha2
+EOF
+
+cat >expect_log_x <<EOF
+$commit_sha5 5th
+
+$commit_sha4 4th
+x notes on 4th commit
+
+$commit_sha3 3rd
+x notes on 3rd commit
+
+$commit_sha2 2nd
+x notes on 2nd commit
+
+$commit_sha1 1st
+
+EOF
+
+test_expect_success 'setup merge base (x)' '
+	git config core.notesRef refs/notes/x &&
+	git notes add -m "x notes on 2nd commit" 2nd &&
+	git notes add -m "x notes on 3rd commit" 3rd &&
+	git notes add -m "x notes on 4th commit" 4th &&
+	verify_notes x
+'
+
+cat <<EOF | sort >expect_notes_y
+e2bfd06a37dd2031684a59a6e2b033e212239c78 $commit_sha4
+5772f42408c0dd6f097a7ca2d24de0e78d1c46b1 $commit_sha3
+b0a6021ec006d07e80e9b20ec9b444cbd9d560d3 $commit_sha1
+EOF
+
+cat >expect_log_y <<EOF
+$commit_sha5 5th
+
+$commit_sha4 4th
+y notes on 4th commit
+
+$commit_sha3 3rd
+y notes on 3rd commit
+
+$commit_sha2 2nd
+
+$commit_sha1 1st
+y notes on 1st commit
+
+EOF
+
+test_expect_success 'setup local branch (y)' '
+	git update-ref refs/notes/y refs/notes/x &&
+	git config core.notesRef refs/notes/y &&
+	git notes add -f -m "y notes on 1st commit" 1st &&
+	git notes remove 2nd &&
+	git notes add -f -m "y notes on 3rd commit" 3rd &&
+	git notes add -f -m "y notes on 4th commit" 4th &&
+	verify_notes y
+'
+
+cat <<EOF | sort >expect_notes_z
+cff59c793c20bb49a4e01bc06fb06bad642e0d54 $commit_sha4
+283b48219aee9a4105f6cab337e789065c82c2b9 $commit_sha2
+0a81da8956346e19bcb27a906f04af327e03e31b $commit_sha1
+EOF
+
+cat >expect_log_z <<EOF
+$commit_sha5 5th
+
+$commit_sha4 4th
+z notes on 4th commit
+
+$commit_sha3 3rd
+
+$commit_sha2 2nd
+z notes on 2nd commit
+
+$commit_sha1 1st
+z notes on 1st commit
+
+EOF
+
+test_expect_success 'setup remote branch (z)' '
+	git update-ref refs/notes/z refs/notes/x &&
+	git config core.notesRef refs/notes/z &&
+	git notes add -f -m "z notes on 1st commit" 1st &&
+	git notes add -f -m "z notes on 2nd commit" 2nd &&
+	git notes remove 3rd &&
+	git notes add -f -m "z notes on 4th commit" 4th &&
+	verify_notes z
+'
+
+# At this point, before merging z into y, we have the following status:
+#
+# commit | base/x  | local/y | remote/z | diff from x to y/z
+# -------|---------|---------|----------|---------------------------
+# 1st    | [none]  | b0a6021 | 0a81da8  | added     / added (diff)
+# 2nd    | ceefa67 | [none]  | 283b482  | removed   / changed
+# 3rd    | e5388c1 | 5772f42 | [none]   | changed   / removed
+# 4th    | 6e8e3fe | e2bfd06 | cff59c7  | changed   / changed (diff)
+# 5th    | [none]  | [none]  | [none]   | [none]
+
+cat <<EOF | sort >expect_conflicts
+$commit_sha1
+$commit_sha2
+$commit_sha3
+$commit_sha4
+EOF
+
+cat >expect_conflict_$commit_sha1 <<EOF
+<<<<<<< refs/notes/m
+y notes on 1st commit
+=======
+z notes on 1st commit
+>>>>>>> refs/notes/z
+EOF
+
+cat >expect_conflict_$commit_sha2 <<EOF
+z notes on 2nd commit
+EOF
+
+cat >expect_conflict_$commit_sha3 <<EOF
+y notes on 3rd commit
+EOF
+
+cat >expect_conflict_$commit_sha4 <<EOF
+<<<<<<< refs/notes/m
+y notes on 4th commit
+=======
+z notes on 4th commit
+>>>>>>> refs/notes/z
+EOF
+
+cp expect_notes_y expect_notes_m
+cp expect_log_y expect_log_m
+
+git rev-parse refs/notes/y > pre_merge_y
+git rev-parse refs/notes/z > pre_merge_z
+
+test_expect_success 'merge z into m (== y) with default ("manual") resolver => Conflicting 3-way merge' '
+	git update-ref refs/notes/m refs/notes/y &&
+	git config core.notesRef refs/notes/m &&
+	test_must_fail git notes merge z >output &&
+	# Output should point to where to resolve conflicts
+	grep -q "\\.git/NOTES_MERGE_WORKTREE" output &&
+	# Inspect merge conflicts
+	ls .git/NOTES_MERGE_WORKTREE >output_conflicts &&
+	test_cmp expect_conflicts output_conflicts &&
+	( for f in $(cat expect_conflicts); do
+		test_cmp "expect_conflict_$f" ".git/NOTES_MERGE_WORKTREE/$f" ||
+		exit 1
+	done ) &&
+	# Verify that current notes tree (pre-merge) has not changed (m == y)
+	verify_notes y &&
+	verify_notes m &&
+	test "$(git rev-parse refs/notes/m)" = "$(cat pre_merge_y)"
+'
+
+cat <<EOF | sort >expect_notes_z
+00494adecf2d9635a02fa431308d67993f853968 $commit_sha4
+283b48219aee9a4105f6cab337e789065c82c2b9 $commit_sha2
+0a81da8956346e19bcb27a906f04af327e03e31b $commit_sha1
+EOF
+
+cat >expect_log_z <<EOF
+$commit_sha5 5th
+
+$commit_sha4 4th
+z notes on 4th commit
+
+More z notes on 4th commit
+
+$commit_sha3 3rd
+
+$commit_sha2 2nd
+z notes on 2nd commit
+
+$commit_sha1 1st
+z notes on 1st commit
+
+EOF
+
+test_expect_success 'change notes in z' '
+	git notes --ref z append -m "More z notes on 4th commit" 4th &&
+	verify_notes z
+'
+
+test_expect_success 'cannot do merge w/conflicts when previous merge is unfinished' '
+	test -d .git/NOTES_MERGE_WORKTREE &&
+	test_must_fail git notes merge z >output 2>&1 &&
+	# Output should indicate what is wrong
+	grep -q "\\.git/NOTES_MERGE_\\* exists" output
+'
+
+# Setup non-conflicting merge between x and new notes ref w
+
+cat <<EOF | sort >expect_notes_w
+ceefa674873670e7ecd131814d909723cce2b669 $commit_sha2
+f75d1df88cbfe4258d49852f26cfc83f2ad4494b $commit_sha1
+EOF
+
+cat >expect_log_w <<EOF
+$commit_sha5 5th
+
+$commit_sha4 4th
+
+$commit_sha3 3rd
+
+$commit_sha2 2nd
+x notes on 2nd commit
+
+$commit_sha1 1st
+w notes on 1st commit
+
+EOF
+
+test_expect_success 'setup unrelated notes ref (w)' '
+	git config core.notesRef refs/notes/w &&
+	git notes add -m "w notes on 1st commit" 1st &&
+	git notes add -m "x notes on 2nd commit" 2nd &&
+	verify_notes w
+'
+
+cat <<EOF | sort >expect_notes_w
+6e8e3febca3c2bb896704335cc4d0c34cb2f8715 $commit_sha4
+e5388c10860456ee60673025345fe2e153eb8cf8 $commit_sha3
+ceefa674873670e7ecd131814d909723cce2b669 $commit_sha2
+f75d1df88cbfe4258d49852f26cfc83f2ad4494b $commit_sha1
+EOF
+
+cat >expect_log_w <<EOF
+$commit_sha5 5th
+
+$commit_sha4 4th
+x notes on 4th commit
+
+$commit_sha3 3rd
+x notes on 3rd commit
+
+$commit_sha2 2nd
+x notes on 2nd commit
+
+$commit_sha1 1st
+w notes on 1st commit
+
+EOF
+
+test_expect_success 'can do merge without conflicts even if previous merge is unfinished (x => w)' '
+	test -d .git/NOTES_MERGE_WORKTREE &&
+	git notes merge x &&
+	verify_notes w &&
+	# Verify that other notes refs has not changed (x and y)
+	verify_notes x &&
+	verify_notes y
+'
+
+cat <<EOF | sort >expect_notes_m
+021faa20e931fb48986ffc6282b4bb05553ac946 $commit_sha4
+5772f42408c0dd6f097a7ca2d24de0e78d1c46b1 $commit_sha3
+283b48219aee9a4105f6cab337e789065c82c2b9 $commit_sha2
+0a59e787e6d688aa6309e56e8c1b89431a0fc1c1 $commit_sha1
+EOF
+
+cat >expect_log_m <<EOF
+$commit_sha5 5th
+
+$commit_sha4 4th
+y and z notes on 4th commit
+
+$commit_sha3 3rd
+y notes on 3rd commit
+
+$commit_sha2 2nd
+z notes on 2nd commit
+
+$commit_sha1 1st
+y and z notes on 1st commit
+
+EOF
+
+test_expect_success 'finalize conflicting merge (z => m)' '
+	# Resolve conflicts and finalize merge
+	cat >.git/NOTES_MERGE_WORKTREE/$commit_sha1 <<EOF &&
+y and z notes on 1st commit
+EOF
+	cat >.git/NOTES_MERGE_WORKTREE/$commit_sha4 <<EOF &&
+y and z notes on 4th commit
+EOF
+	git notes merge --commit &&
+	# No .git/NOTES_MERGE_* files left
+	test_might_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&
+	test_cmp /dev/null output &&
+	# Merge commit has pre-merge y and pre-merge z as parents
+	test "$(git rev-parse refs/notes/m^1)" = "$(cat pre_merge_y)" &&
+	test "$(git rev-parse refs/notes/m^2)" = "$(cat pre_merge_z)" &&
+	# Merge commit mentions the notes refs merged
+	git log -1 --format=%B refs/notes/m > merge_commit_msg &&
+	grep -q refs/notes/m merge_commit_msg &&
+	grep -q refs/notes/z merge_commit_msg &&
+	# Merge commit mentions conflicting notes
+	grep -q "Conflicts" merge_commit_msg &&
+	( for sha1 in $(cat expect_conflicts); do
+		grep -q "$sha1" merge_commit_msg ||
+		exit 1
+	done ) &&
+	# Verify contents of merge result
+	verify_notes m &&
+	# Verify that other notes refs has not changed (w, x, y and z)
+	verify_notes w &&
+	verify_notes x &&
+	verify_notes y &&
+	verify_notes z
+'
+
+cat >expect_conflict_$commit_sha4 <<EOF
+<<<<<<< refs/notes/m
+y notes on 4th commit
+=======
+z notes on 4th commit
+
+More z notes on 4th commit
+>>>>>>> refs/notes/z
+EOF
+
+cp expect_notes_y expect_notes_m
+cp expect_log_y expect_log_m
+
+git rev-parse refs/notes/y > pre_merge_y
+git rev-parse refs/notes/z > pre_merge_z
+
+test_expect_success 'redo merge of z into m (== y) with default ("manual") resolver => Conflicting 3-way merge' '
+	git update-ref refs/notes/m refs/notes/y &&
+	git config core.notesRef refs/notes/m &&
+	test_must_fail git notes merge z >output &&
+	# Output should point to where to resolve conflicts
+	grep -q "\\.git/NOTES_MERGE_WORKTREE" output &&
+	# Inspect merge conflicts
+	ls .git/NOTES_MERGE_WORKTREE >output_conflicts &&
+	test_cmp expect_conflicts output_conflicts &&
+	( for f in $(cat expect_conflicts); do
+		test_cmp "expect_conflict_$f" ".git/NOTES_MERGE_WORKTREE/$f" ||
+		exit 1
+	done ) &&
+	# Verify that current notes tree (pre-merge) has not changed (m == y)
+	verify_notes y &&
+	verify_notes m &&
+	test "$(git rev-parse refs/notes/m)" = "$(cat pre_merge_y)"
+'
+
+test_expect_success 'abort notes merge' '
+	git notes merge --abort &&
+	# No .git/NOTES_MERGE_* files left
+	test_might_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&
+	test_cmp /dev/null output &&
+	# m has not moved (still == y)
+	test "$(git rev-parse refs/notes/m)" = "$(cat pre_merge_y)" &&
+	# Verify that other notes refs has not changed (w, x, y and z)
+	verify_notes w &&
+	verify_notes x &&
+	verify_notes y &&
+	verify_notes z
+'
+
+git rev-parse refs/notes/y > pre_merge_y
+git rev-parse refs/notes/z > pre_merge_z
+
+test_expect_success 'redo merge of z into m (== y) with default ("manual") resolver => Conflicting 3-way merge' '
+	test_must_fail git notes merge z >output &&
+	# Output should point to where to resolve conflicts
+	grep -q "\\.git/NOTES_MERGE_WORKTREE" output &&
+	# Inspect merge conflicts
+	ls .git/NOTES_MERGE_WORKTREE >output_conflicts &&
+	test_cmp expect_conflicts output_conflicts &&
+	( for f in $(cat expect_conflicts); do
+		test_cmp "expect_conflict_$f" ".git/NOTES_MERGE_WORKTREE/$f" ||
+		exit 1
+	done ) &&
+	# Verify that current notes tree (pre-merge) has not changed (m == y)
+	verify_notes y &&
+	verify_notes m &&
+	test "$(git rev-parse refs/notes/m)" = "$(cat pre_merge_y)"
+'
+
+cat <<EOF | sort >expect_notes_m
+304dfb4325cf243025b9957486eb605a9b51c199 $commit_sha5
+283b48219aee9a4105f6cab337e789065c82c2b9 $commit_sha2
+0a59e787e6d688aa6309e56e8c1b89431a0fc1c1 $commit_sha1
+EOF
+
+cat >expect_log_m <<EOF
+$commit_sha5 5th
+new note on 5th commit
+
+$commit_sha4 4th
+
+$commit_sha3 3rd
+
+$commit_sha2 2nd
+z notes on 2nd commit
+
+$commit_sha1 1st
+y and z notes on 1st commit
+
+EOF
+
+test_expect_success 'add + remove notes in finalized merge (z => m)' '
+	# Resolve one conflict
+	cat >.git/NOTES_MERGE_WORKTREE/$commit_sha1 <<EOF &&
+y and z notes on 1st commit
+EOF
+	# Remove another conflict
+	rm .git/NOTES_MERGE_WORKTREE/$commit_sha4 &&
+	# Remove a D/F conflict
+	rm .git/NOTES_MERGE_WORKTREE/$commit_sha3 &&
+	# Add a new note
+	echo "new note on 5th commit" > .git/NOTES_MERGE_WORKTREE/$commit_sha5 &&
+	# Finalize merge
+	git notes merge --commit &&
+	# No .git/NOTES_MERGE_* files left
+	test_might_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&
+	test_cmp /dev/null output &&
+	# Merge commit has pre-merge y and pre-merge z as parents
+	test "$(git rev-parse refs/notes/m^1)" = "$(cat pre_merge_y)" &&
+	test "$(git rev-parse refs/notes/m^2)" = "$(cat pre_merge_z)" &&
+	# Merge commit mentions the notes refs merged
+	git log -1 --format=%B refs/notes/m > merge_commit_msg &&
+	grep -q refs/notes/m merge_commit_msg &&
+	grep -q refs/notes/z merge_commit_msg &&
+	# Merge commit mentions conflicting notes
+	grep -q "Conflicts" merge_commit_msg &&
+	( for sha1 in $(cat expect_conflicts); do
+		grep -q "$sha1" merge_commit_msg ||
+		exit 1
+	done ) &&
+	# Verify contents of merge result
+	verify_notes m &&
+	# Verify that other notes refs has not changed (w, x, y and z)
+	verify_notes w &&
+	verify_notes x &&
+	verify_notes y &&
+	verify_notes z
+'
+
+cp expect_notes_y expect_notes_m
+cp expect_log_y expect_log_m
+
+test_expect_success 'redo merge of z into m (== y) with default ("manual") resolver => Conflicting 3-way merge' '
+	git update-ref refs/notes/m refs/notes/y &&
+	test_must_fail git notes merge z >output &&
+	# Output should point to where to resolve conflicts
+	grep -q "\\.git/NOTES_MERGE_WORKTREE" output &&
+	# Inspect merge conflicts
+	ls .git/NOTES_MERGE_WORKTREE >output_conflicts &&
+	test_cmp expect_conflicts output_conflicts &&
+	( for f in $(cat expect_conflicts); do
+		test_cmp "expect_conflict_$f" ".git/NOTES_MERGE_WORKTREE/$f" ||
+		exit 1
+	done ) &&
+	# Verify that current notes tree (pre-merge) has not changed (m == y)
+	verify_notes y &&
+	verify_notes m &&
+	test "$(git rev-parse refs/notes/m)" = "$(cat pre_merge_y)"
+'
+
+cp expect_notes_w expect_notes_m
+cp expect_log_w expect_log_m
+
+test_expect_success 'reset notes ref m to somewhere else (w)' '
+	git update-ref refs/notes/m refs/notes/w &&
+	verify_notes m &&
+	test "$(git rev-parse refs/notes/m)" = "$(git rev-parse refs/notes/w)"
+'
+
+test_expect_success 'fail to finalize conflicting merge if underlying ref has moved in the meantime (m != NOTES_MERGE_PARTIAL^1)' '
+	# Resolve conflicts
+	cat >.git/NOTES_MERGE_WORKTREE/$commit_sha1 <<EOF &&
+y and z notes on 1st commit
+EOF
+	cat >.git/NOTES_MERGE_WORKTREE/$commit_sha4 <<EOF &&
+y and z notes on 4th commit
+EOF
+	# Fail to finalize merge
+	test_must_fail git notes merge --commit >output 2>&1 &&
+	# .git/NOTES_MERGE_* must remain
+	test -f .git/NOTES_MERGE_PARTIAL &&
+	test -f .git/NOTES_MERGE_REF &&
+	test -f .git/NOTES_MERGE_WORKTREE/$commit_sha1 &&
+	test -f .git/NOTES_MERGE_WORKTREE/$commit_sha2 &&
+	test -f .git/NOTES_MERGE_WORKTREE/$commit_sha3 &&
+	test -f .git/NOTES_MERGE_WORKTREE/$commit_sha4 &&
+	# Refs are unchanged
+	test "$(git rev-parse refs/notes/m)" = "$(git rev-parse refs/notes/w)" &&
+	test "$(git rev-parse refs/notes/y)" = "$(git rev-parse NOTES_MERGE_PARTIAL^1)" &&
+	test "$(git rev-parse refs/notes/m)" != "$(git rev-parse NOTES_MERGE_PARTIAL^1)" &&
+	# Mention refs/notes/m, and its current and expected value in output
+	grep -q "refs/notes/m" output &&
+	grep -q "$(git rev-parse refs/notes/m)" output &&
+	grep -q "$(git rev-parse NOTES_MERGE_PARTIAL^1)" output &&
+	# Verify that other notes refs has not changed (w, x, y and z)
+	verify_notes w &&
+	verify_notes x &&
+	verify_notes y &&
+	verify_notes z
+'
+
+test_expect_success 'resolve situation by aborting the notes merge' '
+	git notes merge --abort &&
+	# No .git/NOTES_MERGE_* files left
+	test_might_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&
+	test_cmp /dev/null output &&
+	# m has not moved (still == w)
+	test "$(git rev-parse refs/notes/m)" = "$(git rev-parse refs/notes/w)" &&
+	# Verify that other notes refs has not changed (w, x, y and z)
+	verify_notes w &&
+	verify_notes x &&
+	verify_notes y &&
+	verify_notes z
+'
+
+cat >expect_notes <<EOF
+foo
+bar
+EOF
+
+test_expect_success 'switch cwd before committing notes merge' '
+	git notes add -m foo HEAD &&
+	git notes --ref=other add -m bar HEAD &&
+	test_must_fail git notes merge refs/notes/other &&
+	(
+		cd .git/NOTES_MERGE_WORKTREE &&
+		echo "foo" > $(git rev-parse HEAD) &&
+		echo "bar" >> $(git rev-parse HEAD) &&
+		git notes merge --commit
+	) &&
+	git notes show HEAD > actual_notes &&
+	test_cmp expect_notes actual_notes
+'
+
+test_done
diff --git a/t/t3311-notes-merge-fanout.sh b/t/t3311-notes-merge-fanout.sh
new file mode 100755
index 0000000..93516ef
--- /dev/null
+++ b/t/t3311-notes-merge-fanout.sh
@@ -0,0 +1,436 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Johan Herland
+#
+
+test_description='Test notes merging at various fanout levels'
+
+. ./test-lib.sh
+
+verify_notes () {
+	notes_ref="$1"
+	commit="$2"
+	if test -f "expect_notes_$notes_ref"
+	then
+		git -c core.notesRef="refs/notes/$notes_ref" notes |
+			sort >"output_notes_$notes_ref" &&
+		test_cmp "expect_notes_$notes_ref" "output_notes_$notes_ref" ||
+			return 1
+	fi &&
+	git -c core.notesRef="refs/notes/$notes_ref" log --format="%H %s%n%N" \
+		"$commit" >"output_log_$notes_ref" &&
+	test_cmp "expect_log_$notes_ref" "output_log_$notes_ref"
+}
+
+verify_fanout () {
+	notes_ref="$1"
+	# Expect entire notes tree to have a fanout == 1
+	git rev-parse --quiet --verify "refs/notes/$notes_ref" >/dev/null &&
+	git ls-tree -r --name-only "refs/notes/$notes_ref" |
+	while read path
+	do
+		case "$path" in
+		??/??????????????????????????????????????)
+			: true
+			;;
+		*)
+			echo "Invalid path \"$path\"" &&
+			return 1
+			;;
+		esac
+	done
+}
+
+verify_no_fanout () {
+	notes_ref="$1"
+	# Expect entire notes tree to have a fanout == 0
+	git rev-parse --quiet --verify "refs/notes/$notes_ref" >/dev/null &&
+	git ls-tree -r --name-only "refs/notes/$notes_ref" |
+	while read path
+	do
+		case "$path" in
+		????????????????????????????????????????)
+			: true
+			;;
+		*)
+			echo "Invalid path \"$path\"" &&
+			return 1
+			;;
+		esac
+	done
+}
+
+# Set up a notes merge scenario with different kinds of conflicts
+test_expect_success 'setup a few initial commits with notes (notes ref: x)' '
+	git config core.notesRef refs/notes/x &&
+	for i in 1 2 3 4 5
+	do
+		test_commit "commit$i" >/dev/null &&
+		git notes add -m "notes for commit$i" || return 1
+	done
+'
+
+commit_sha1=$(git rev-parse commit1^{commit})
+commit_sha2=$(git rev-parse commit2^{commit})
+commit_sha3=$(git rev-parse commit3^{commit})
+commit_sha4=$(git rev-parse commit4^{commit})
+commit_sha5=$(git rev-parse commit5^{commit})
+
+cat <<EOF | sort >expect_notes_x
+aed91155c7a72c2188e781fdf40e0f3761b299db $commit_sha5
+99fab268f9d7ee7b011e091a436c78def8eeee69 $commit_sha4
+953c20ae26c7aa0b428c20693fe38bc687f9d1a9 $commit_sha3
+6358796131b8916eaa2dde6902642942a1cb37e1 $commit_sha2
+b02d459c32f0e68f2fe0981033bb34f38776ba47 $commit_sha1
+EOF
+
+cat >expect_log_x <<EOF
+$commit_sha5 commit5
+notes for commit5
+
+$commit_sha4 commit4
+notes for commit4
+
+$commit_sha3 commit3
+notes for commit3
+
+$commit_sha2 commit2
+notes for commit2
+
+$commit_sha1 commit1
+notes for commit1
+
+EOF
+
+test_expect_success 'sanity check (x)' '
+	verify_notes x commit5 &&
+	verify_no_fanout x
+'
+
+num=300
+
+cp expect_log_x expect_log_y
+
+test_expect_success 'Add a few hundred commits w/notes to trigger fanout (x -> y)' '
+	git update-ref refs/notes/y refs/notes/x &&
+	git config core.notesRef refs/notes/y &&
+	i=5 &&
+	while test $i -lt $num
+	do
+		i=$(($i + 1)) &&
+		test_commit "commit$i" >/dev/null &&
+		git notes add -m "notes for commit$i" || return 1
+	done &&
+	test "$(git rev-parse refs/notes/y)" != "$(git rev-parse refs/notes/x)" &&
+	# Expected number of commits and notes
+	test $(git rev-list HEAD | wc -l) = $num &&
+	test $(git notes list | wc -l) = $num &&
+	# 5 first notes unchanged
+	verify_notes y commit5
+'
+
+test_expect_success 'notes tree has fanout (y)' 'verify_fanout y'
+
+test_expect_success 'No-op merge (already included) (x => y)' '
+	git update-ref refs/notes/m refs/notes/y &&
+	git config core.notesRef refs/notes/m &&
+	git notes merge x &&
+	test "$(git rev-parse refs/notes/m)" = "$(git rev-parse refs/notes/y)"
+'
+
+test_expect_success 'Fast-forward merge (y => x)' '
+	git update-ref refs/notes/m refs/notes/x &&
+	git notes merge y &&
+	test "$(git rev-parse refs/notes/m)" = "$(git rev-parse refs/notes/y)"
+'
+
+cat <<EOF | sort >expect_notes_z
+9f506ee70e20379d7f78204c77b334f43d77410d $commit_sha3
+23a47d6ea7d589895faf800752054818e1e7627b $commit_sha2
+b02d459c32f0e68f2fe0981033bb34f38776ba47 $commit_sha1
+EOF
+
+cat >expect_log_z <<EOF
+$commit_sha5 commit5
+
+$commit_sha4 commit4
+
+$commit_sha3 commit3
+notes for commit3
+
+appended notes for commit3
+
+$commit_sha2 commit2
+new notes for commit2
+
+$commit_sha1 commit1
+notes for commit1
+
+EOF
+
+test_expect_success 'change some of the initial 5 notes (x -> z)' '
+	git update-ref refs/notes/z refs/notes/x &&
+	git config core.notesRef refs/notes/z &&
+	git notes add -f -m "new notes for commit2" commit2 &&
+	git notes append -m "appended notes for commit3" commit3 &&
+	git notes remove commit4 &&
+	git notes remove commit5 &&
+	verify_notes z commit5
+'
+
+test_expect_success 'notes tree has no fanout (z)' 'verify_no_fanout z'
+
+cp expect_log_z expect_log_m
+
+test_expect_success 'successful merge without conflicts (y => z)' '
+	git update-ref refs/notes/m refs/notes/z &&
+	git config core.notesRef refs/notes/m &&
+	git notes merge y &&
+	verify_notes m commit5 &&
+	# x/y/z unchanged
+	verify_notes x commit5 &&
+	verify_notes y commit5 &&
+	verify_notes z commit5
+'
+
+test_expect_success 'notes tree still has fanout after merge (m)' 'verify_fanout m'
+
+cat >expect_log_w <<EOF
+$commit_sha5 commit5
+
+$commit_sha4 commit4
+other notes for commit4
+
+$commit_sha3 commit3
+other notes for commit3
+
+$commit_sha2 commit2
+notes for commit2
+
+$commit_sha1 commit1
+other notes for commit1
+
+EOF
+
+test_expect_success 'introduce conflicting changes (y -> w)' '
+	git update-ref refs/notes/w refs/notes/y &&
+	git config core.notesRef refs/notes/w &&
+	git notes add -f -m "other notes for commit1" commit1 &&
+	git notes add -f -m "other notes for commit3" commit3 &&
+	git notes add -f -m "other notes for commit4" commit4 &&
+	git notes remove commit5 &&
+	verify_notes w commit5
+'
+
+cat >expect_log_m <<EOF
+$commit_sha5 commit5
+
+$commit_sha4 commit4
+other notes for commit4
+
+$commit_sha3 commit3
+other notes for commit3
+
+$commit_sha2 commit2
+new notes for commit2
+
+$commit_sha1 commit1
+other notes for commit1
+
+EOF
+
+test_expect_success 'successful merge using "ours" strategy (z => w)' '
+	git update-ref refs/notes/m refs/notes/w &&
+	git config core.notesRef refs/notes/m &&
+	git notes merge -s ours z &&
+	verify_notes m commit5 &&
+	# w/x/y/z unchanged
+	verify_notes w commit5 &&
+	verify_notes x commit5 &&
+	verify_notes y commit5 &&
+	verify_notes z commit5
+'
+
+test_expect_success 'notes tree still has fanout after merge (m)' 'verify_fanout m'
+
+cat >expect_log_m <<EOF
+$commit_sha5 commit5
+
+$commit_sha4 commit4
+
+$commit_sha3 commit3
+notes for commit3
+
+appended notes for commit3
+
+$commit_sha2 commit2
+new notes for commit2
+
+$commit_sha1 commit1
+other notes for commit1
+
+EOF
+
+test_expect_success 'successful merge using "theirs" strategy (z => w)' '
+	git update-ref refs/notes/m refs/notes/w &&
+	git notes merge -s theirs z &&
+	verify_notes m commit5 &&
+	# w/x/y/z unchanged
+	verify_notes w commit5 &&
+	verify_notes x commit5 &&
+	verify_notes y commit5 &&
+	verify_notes z commit5
+'
+
+test_expect_success 'notes tree still has fanout after merge (m)' 'verify_fanout m'
+
+cat >expect_log_m <<EOF
+$commit_sha5 commit5
+
+$commit_sha4 commit4
+other notes for commit4
+
+$commit_sha3 commit3
+other notes for commit3
+
+notes for commit3
+
+appended notes for commit3
+
+$commit_sha2 commit2
+new notes for commit2
+
+$commit_sha1 commit1
+other notes for commit1
+
+EOF
+
+test_expect_success 'successful merge using "union" strategy (z => w)' '
+	git update-ref refs/notes/m refs/notes/w &&
+	git notes merge -s union z &&
+	verify_notes m commit5 &&
+	# w/x/y/z unchanged
+	verify_notes w commit5 &&
+	verify_notes x commit5 &&
+	verify_notes y commit5 &&
+	verify_notes z commit5
+'
+
+test_expect_success 'notes tree still has fanout after merge (m)' 'verify_fanout m'
+
+cat >expect_log_m <<EOF
+$commit_sha5 commit5
+
+$commit_sha4 commit4
+other notes for commit4
+
+$commit_sha3 commit3
+appended notes for commit3
+notes for commit3
+other notes for commit3
+
+$commit_sha2 commit2
+new notes for commit2
+
+$commit_sha1 commit1
+other notes for commit1
+
+EOF
+
+test_expect_success 'successful merge using "cat_sort_uniq" strategy (z => w)' '
+	git update-ref refs/notes/m refs/notes/w &&
+	git notes merge -s cat_sort_uniq z &&
+	verify_notes m commit5 &&
+	# w/x/y/z unchanged
+	verify_notes w commit5 &&
+	verify_notes x commit5 &&
+	verify_notes y commit5 &&
+	verify_notes z commit5
+'
+
+test_expect_success 'notes tree still has fanout after merge (m)' 'verify_fanout m'
+
+# We're merging z into w. Here are the conflicts we expect:
+#
+# commit | x -> w    | x -> z    | conflict?
+# -------|-----------|-----------|----------
+# 1      | changed   | unchanged | no, use w
+# 2      | unchanged | changed   | no, use z
+# 3      | changed   | changed   | yes (w, then z in conflict markers)
+# 4      | changed   | deleted   | yes (w)
+# 5      | deleted   | deleted   | no, deleted
+
+test_expect_success 'fails to merge using "manual" strategy (z => w)' '
+	git update-ref refs/notes/m refs/notes/w &&
+	test_must_fail git notes merge z
+'
+
+test_expect_success 'notes tree still has fanout after merge (m)' 'verify_fanout m'
+
+cat <<EOF | sort >expect_conflicts
+$commit_sha3
+$commit_sha4
+EOF
+
+cat >expect_conflict_$commit_sha3 <<EOF
+<<<<<<< refs/notes/m
+other notes for commit3
+=======
+notes for commit3
+
+appended notes for commit3
+>>>>>>> refs/notes/z
+EOF
+
+cat >expect_conflict_$commit_sha4 <<EOF
+other notes for commit4
+EOF
+
+test_expect_success 'verify conflict entries (with no fanout)' '
+	ls .git/NOTES_MERGE_WORKTREE >output_conflicts &&
+	test_cmp expect_conflicts output_conflicts &&
+	( for f in $(cat expect_conflicts); do
+		test_cmp "expect_conflict_$f" ".git/NOTES_MERGE_WORKTREE/$f" ||
+		exit 1
+	done ) &&
+	# Verify that current notes tree (pre-merge) has not changed (m == w)
+	test "$(git rev-parse refs/notes/m)" = "$(git rev-parse refs/notes/w)"
+'
+
+cat >expect_log_m <<EOF
+$commit_sha5 commit5
+
+$commit_sha4 commit4
+other notes for commit4
+
+$commit_sha3 commit3
+other notes for commit3
+
+appended notes for commit3
+
+$commit_sha2 commit2
+new notes for commit2
+
+$commit_sha1 commit1
+other notes for commit1
+
+EOF
+
+test_expect_success 'resolve and finalize merge (z => w)' '
+	cat >.git/NOTES_MERGE_WORKTREE/$commit_sha3 <<EOF &&
+other notes for commit3
+
+appended notes for commit3
+EOF
+	git notes merge --commit &&
+	verify_notes m commit5 &&
+	# w/x/y/z unchanged
+	verify_notes w commit5 &&
+	verify_notes x commit5 &&
+	verify_notes y commit5 &&
+	verify_notes z commit5
+'
+
+test_expect_success 'notes tree still has fanout after merge (m)' 'verify_fanout m'
+
+test_done
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index c5c29cc..7788ae0 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -3,134 +3,240 @@
 # Copyright (c) 2005 Amos Waterland
 #
 
-test_description='git rebase should not destroy author information
+test_description='git rebase assorted tests
 
-This test runs git rebase and checks that the author information is not lost.
+This test runs git rebase and checks that the author information is not lost
+among other things.
 '
 . ./test-lib.sh
 
-GIT_AUTHOR_EMAIL=bogus_email_address
-export GIT_AUTHOR_EMAIL
+GIT_AUTHOR_NAME=author@name
+GIT_AUTHOR_EMAIL=bogus@email@address
+export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL
 
-test_expect_success \
-    'prepare repository with topic branches' \
-    'git config core.logAllRefUpdates true &&
-     echo First > A &&
-     git update-index --add A &&
-     git commit -m "Add A." &&
-     git checkout -b my-topic-branch &&
-     echo Second > B &&
-     git update-index --add B &&
-     git commit -m "Add B." &&
-     git checkout -f master &&
-     echo Third >> A &&
-     git update-index A &&
-     git commit -m "Modify A." &&
-     git checkout -b side my-topic-branch &&
-     echo Side >> C &&
-     git add C &&
-     git commit -m "Add C" &&
-     git checkout -b nonlinear my-topic-branch &&
-     echo Edit >> B &&
-     git add B &&
-     git commit -m "Modify B" &&
-     git merge side &&
-     git checkout -b upstream-merged-nonlinear &&
-     git merge master &&
-     git checkout -f my-topic-branch &&
-     git tag topic
+test_expect_success 'prepare repository with topic branches' '
+	git config core.logAllRefUpdates true &&
+	echo First >A &&
+	git update-index --add A &&
+	git commit -m "Add A." &&
+	git checkout -b force-3way &&
+	echo Dummy >Y &&
+	git update-index --add Y &&
+	git commit -m "Add Y." &&
+	git checkout -b filemove &&
+	git reset --soft master &&
+	mkdir D &&
+	git mv A D/A &&
+	git commit -m "Move A." &&
+	git checkout -b my-topic-branch master &&
+	echo Second >B &&
+	git update-index --add B &&
+	git commit -m "Add B." &&
+	git checkout -f master &&
+	echo Third >>A &&
+	git update-index A &&
+	git commit -m "Modify A." &&
+	git checkout -b side my-topic-branch &&
+	echo Side >>C &&
+	git add C &&
+	git commit -m "Add C" &&
+	git checkout -b nonlinear my-topic-branch &&
+	echo Edit >>B &&
+	git add B &&
+	git commit -m "Modify B" &&
+	git merge side &&
+	git checkout -b upstream-merged-nonlinear &&
+	git merge master &&
+	git checkout -f my-topic-branch &&
+	git tag topic
 '
 
 test_expect_success 'rebase on dirty worktree' '
-     echo dirty >> A &&
-     test_must_fail git rebase master'
+	echo dirty >>A &&
+	test_must_fail git rebase master
+'
 
 test_expect_success 'rebase on dirty cache' '
-     git add A &&
-     test_must_fail git rebase master'
+	git add A &&
+	test_must_fail git rebase master
+'
 
 test_expect_success 'rebase against master' '
-     git reset --hard HEAD &&
-     git rebase master'
+	git reset --hard HEAD &&
+	git rebase master
+'
 
 test_expect_success 'rebase against master twice' '
-     git rebase master >out &&
-     grep "Current branch my-topic-branch is up to date" out
+	git rebase master >out &&
+	grep "Current branch my-topic-branch is up to date" out
 '
 
 test_expect_success 'rebase against master twice with --force' '
-     git rebase --force-rebase master >out &&
-     grep "Current branch my-topic-branch is up to date, rebase forced" out
+	git rebase --force-rebase master >out &&
+	grep "Current branch my-topic-branch is up to date, rebase forced" out
 '
 
 test_expect_success 'rebase against master twice from another branch' '
-     git checkout my-topic-branch^ &&
-     git rebase master my-topic-branch >out &&
-     grep "Current branch my-topic-branch is up to date" out
+	git checkout my-topic-branch^ &&
+	git rebase master my-topic-branch >out &&
+	grep "Current branch my-topic-branch is up to date" out
 '
 
 test_expect_success 'rebase fast-forward to master' '
-     git checkout my-topic-branch^ &&
-     git rebase my-topic-branch >out &&
-     grep "Fast-forwarded HEAD to my-topic-branch" out
+	git checkout my-topic-branch^ &&
+	git rebase my-topic-branch >out &&
+	grep "Fast-forwarded HEAD to my-topic-branch" out
 '
 
-test_expect_success \
-    'the rebase operation should not have destroyed author information' \
-    '! (git log | grep "Author:" | grep "<>")'
+test_expect_success 'the rebase operation should not have destroyed author information' '
+	! (git log | grep "Author:" | grep "<>")
+'
+
+test_expect_success 'the rebase operation should not have destroyed author information (2)' "
+	git log -1 |
+	grep 'Author: $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>'
+"
 
 test_expect_success 'HEAD was detached during rebase' '
-     test $(git rev-parse HEAD@{1}) != $(git rev-parse my-topic-branch@{1})
+	test $(git rev-parse HEAD@{1}) != $(git rev-parse my-topic-branch@{1})
 '
 
 test_expect_success 'rebase after merge master' '
-     git reset --hard topic &&
-     git merge master &&
-     git rebase master &&
-     ! (git show | grep "^Merge:")
+	git reset --hard topic &&
+	git merge master &&
+	git rebase master &&
+	! (git show | grep "^Merge:")
 '
 
 test_expect_success 'rebase of history with merges is linearized' '
-     git checkout nonlinear &&
-     test 4 = $(git rev-list master.. | wc -l) &&
-     git rebase master &&
-     test 3 = $(git rev-list master.. | wc -l)
+	git checkout nonlinear &&
+	test 4 = $(git rev-list master.. | wc -l) &&
+	git rebase master &&
+	test 3 = $(git rev-list master.. | wc -l)
 '
 
-test_expect_success \
-    'rebase of history with merges after upstream merge is linearized' '
-     git checkout upstream-merged-nonlinear &&
-     test 5 = $(git rev-list master.. | wc -l) &&
-     git rebase master &&
-     test 3 = $(git rev-list master.. | wc -l)
+test_expect_success 'rebase of history with merges after upstream merge is linearized' '
+	git checkout upstream-merged-nonlinear &&
+	test 5 = $(git rev-list master.. | wc -l) &&
+	git rebase master &&
+	test 3 = $(git rev-list master.. | wc -l)
 '
 
 test_expect_success 'rebase a single mode change' '
-     git checkout master &&
-     echo 1 > X &&
-     git add X &&
-     test_tick &&
-     git commit -m prepare &&
-     git checkout -b modechange HEAD^ &&
-     echo 1 > X &&
-     git add X &&
-     test_chmod +x A &&
-     test_tick &&
-     git commit -m modechange &&
-     GIT_TRACE=1 git rebase master
+	git checkout master &&
+	echo 1 >X &&
+	git add X &&
+	test_tick &&
+	git commit -m prepare &&
+	git checkout -b modechange HEAD^ &&
+	echo 1 >X &&
+	git add X &&
+	test_chmod +x A &&
+	test_tick &&
+	git commit -m modechange &&
+	GIT_TRACE=1 git rebase master
+'
+
+test_expect_success 'rebase is not broken by diff.renames' '
+	git config diff.renames copies &&
+	test_when_finished "git config --unset diff.renames" &&
+	git checkout filemove &&
+	GIT_TRACE=1 git rebase force-3way
+'
+
+test_expect_success 'setup: recover' '
+	test_might_fail git rebase --abort &&
+	git reset --hard &&
+	git checkout modechange
 '
 
 test_expect_success 'Show verbose error when HEAD could not be detached' '
-     : > B &&
-     test_must_fail git rebase topic 2> output.err > output.out &&
-     grep "Untracked working tree file .B. would be overwritten" output.err
+	>B &&
+	test_must_fail git rebase topic 2>output.err >output.out &&
+	grep "The following untracked working tree files would be overwritten by checkout:" output.err &&
+	grep B output.err
+'
+rm -f B
+
+test_expect_success 'fail when upstream arg is missing and not on branch' '
+	git checkout topic &&
+	test_must_fail git rebase
+'
+
+test_expect_success 'fail when upstream arg is missing and not configured' '
+	git checkout -b no-config topic &&
+	test_must_fail git rebase
+'
+
+test_expect_success 'default to @{upstream} when upstream arg is missing' '
+	git checkout -b default topic &&
+	git config branch.default.remote . &&
+	git config branch.default.merge refs/heads/master &&
+	git rebase &&
+	test "$(git rev-parse default~1)" = "$(git rev-parse master)"
 '
 
 test_expect_success 'rebase -q is quiet' '
-     rm B &&
-     git checkout -b quiet topic &&
-     git rebase -q master > output.out 2>&1 &&
-     test ! -s output.out
+	git checkout -b quiet topic &&
+	git rebase -q master >output.out 2>&1 &&
+	test ! -s output.out
+'
+
+test_expect_success 'Rebase a commit that sprinkles CRs in' '
+	(
+		echo "One"
+		echo "TwoQ"
+		echo "Three"
+		echo "FQur"
+		echo "Five"
+	) | q_to_cr >CR &&
+	git add CR &&
+	test_tick &&
+	git commit -a -m "A file with a line with CR" &&
+	git tag file-with-cr &&
+	git checkout HEAD^0 &&
+	git rebase --onto HEAD^^ HEAD^ &&
+	git diff --exit-code file-with-cr:CR HEAD:CR
+'
+
+test_expect_success 'rebase can copy notes' '
+	git config notes.rewrite.rebase true &&
+	git config notes.rewriteRef "refs/notes/*" &&
+	test_commit n1 &&
+	test_commit n2 &&
+	test_commit n3 &&
+	git notes add -m"a note" n3 &&
+	git rebase --onto n1 n2 &&
+	test "a note" = "$(git notes show HEAD)"
+'
+
+test_expect_success 'rebase -m can copy notes' '
+	git reset --hard n3 &&
+	git rebase -m --onto n1 n2 &&
+	test "a note" = "$(git notes show HEAD)"
+'
+
+test_expect_success 'rebase commit with an ancient timestamp' '
+	git reset --hard &&
+
+	>old.one && git add old.one && test_tick &&
+	git commit --date="@12345 +0400" -m "Old one" &&
+	>old.two && git add old.two && test_tick &&
+	git commit --date="@23456 +0500" -m "Old two" &&
+	>old.three && git add old.three && test_tick &&
+	git commit --date="@34567 +0600" -m "Old three" &&
+
+	git cat-file commit HEAD^^ >actual &&
+	grep "author .* 12345 +0400$" actual &&
+	git cat-file commit HEAD^ >actual &&
+	grep "author .* 23456 +0500$" actual &&
+	git cat-file commit HEAD >actual &&
+	grep "author .* 34567 +0600$" actual &&
+
+	git rebase --onto HEAD^^ HEAD^ &&
+
+	git cat-file commit HEAD >actual &&
+	grep "author .* 34567 +0600$" actual
 '
 
 test_done
diff --git a/t/t3401-rebase-partial.sh b/t/t3401-rebase-partial.sh
index aea6685..7f8693b 100755
--- a/t/t3401-rebase-partial.sh
+++ b/t/t3401-rebase-partial.sh
@@ -11,51 +11,43 @@
 '
 . ./test-lib.sh
 
-test_expect_success \
-    'prepare repository with topic branch' \
-    'echo First > A &&
-     git update-index --add A &&
-     git commit -m "Add A." &&
-
-     git checkout -b my-topic-branch &&
-
-     echo Second > B &&
-     git update-index --add B &&
-     git commit -m "Add B." &&
-
-     echo AnotherSecond > C &&
-     git update-index --add C &&
-     git commit -m "Add C." &&
-
-     git checkout -f master &&
-
-     echo Third >> A &&
-     git update-index A &&
-     git commit -m "Modify A."
+test_expect_success 'prepare repository with topic branch' '
+	test_commit A &&
+	git checkout -b my-topic-branch &&
+	test_commit B &&
+	test_commit C &&
+	git checkout -f master &&
+	test_commit A2 A.t
 '
 
-test_expect_success \
-    'pick top patch from topic branch into master' \
-    'git cherry-pick my-topic-branch^0 &&
-     git checkout -f my-topic-branch &&
-     git branch master-merge master &&
-     git branch my-topic-branch-merge my-topic-branch
+test_expect_success 'pick top patch from topic branch into master' '
+	git cherry-pick C &&
+	git checkout -f my-topic-branch
 '
 
-test_debug \
-    'git cherry master &&
-     git format-patch -k --stdout --full-index master >/dev/null &&
-     gitk --all & sleep 1
+test_debug '
+	git cherry master &&
+	git format-patch -k --stdout --full-index master >/dev/null &&
+	gitk --all & sleep 1
 '
 
-test_expect_success \
-    'rebase topic branch against new master and check git am did not get halted' \
-    'git rebase master && test ! -d .git/rebase-apply'
+test_expect_success 'rebase topic branch against new master and check git am did not get halted' '
+	git rebase master &&
+	test_path_is_missing .git/rebase-apply
+'
 
-test_expect_success \
-	'rebase --merge topic branch that was partially merged upstream' \
-	'git checkout -f my-topic-branch-merge &&
-	 git rebase --merge master-merge &&
-	 test ! -d .git/rebase-merge'
+test_expect_success 'rebase --merge topic branch that was partially merged upstream' '
+	git reset --hard C &&
+	git rebase --merge master &&
+	test_path_is_missing .git/rebase-merge
+'
+
+test_expect_success 'rebase ignores empty commit' '
+	git reset --hard A &&
+	git commit --allow-empty -m empty &&
+	test_commit D &&
+	git rebase C &&
+	test $(git log --format=%s C..) = "D"
+'
 
 test_done
diff --git a/t/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh
index 7b7d072..be8c1d5 100755
--- a/t/t3402-rebase-merge.sh
+++ b/t/t3402-rebase-merge.sh
@@ -74,6 +74,15 @@
 	git rebase --merge side
 '
 
+test_expect_success 'rebase -Xtheirs' '
+	git checkout -b conflicting master~2 &&
+	echo "AB $T" >> original &&
+	git commit -mconflicting original &&
+	git rebase -Xtheirs master &&
+	grep AB original &&
+	! grep 11 original
+'
+
 test_expect_success 'merge and rebase should match' '
 	git diff-tree -r test-rebase test-merge >difference &&
 	if test -s difference
@@ -108,4 +117,25 @@
 	esac
 '
 
+test_expect_success 'rebase -s funny -Xopt' '
+	test_when_finished "rm -fr test-bin funny.was.run" &&
+	mkdir test-bin &&
+	cat >test-bin/git-merge-funny <<-EOF &&
+	#!$SHELL_PATH
+	case "\$1" in --opt) ;; *) exit 2 ;; esac
+	shift &&
+	>funny.was.run &&
+	exec git merge-recursive "\$@"
+	EOF
+	chmod +x test-bin/git-merge-funny &&
+	git reset --hard &&
+	git checkout -b test-funny master^ &&
+	test_commit funny &&
+	(
+		PATH=./test-bin:$PATH
+		git rebase -s funny -Xopt master
+	) &&
+	test -f funny.was.run
+'
+
 test_done
diff --git a/t/t3403-rebase-skip.sh b/t/t3403-rebase-skip.sh
index 64446e3..826500b 100755
--- a/t/t3403-rebase-skip.sh
+++ b/t/t3403-rebase-skip.sh
@@ -35,6 +35,11 @@
 	test_must_fail git rebase master
 '
 
+test_expect_success 'rebase --skip can not be used with other options' '
+	test_must_fail git rebase -v --skip &&
+	test_must_fail git rebase --skip -v
+'
+
 test_expect_success 'rebase --skip with am -3' '
 	git rebase --skip
 	'
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index a973628..8078db6 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -7,65 +7,119 @@
 
 This test runs git rebase "interactively", by faking an edit, and verifies
 that the result still makes sense.
+
+Initial setup:
+
+     one - two - three - four (conflict-branch)
+   /
+ A - B - C - D - E            (master)
+ | \
+ |   F - G - H                (branch1)
+ |     \
+ |\      I                    (branch2)
+ | \
+ |   J - K - L - M            (no-conflict-branch)
+  \
+    N - O - P                 (no-ff-branch)
+
+ where A, B, D and G all touch file1, and one, two, three, four all
+ touch file "conflict".
 '
 . ./test-lib.sh
 
-. ../lib-rebase.sh
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+test_cmp_rev () {
+	git rev-parse --verify "$1" >expect.rev &&
+	git rev-parse --verify "$2" >actual.rev &&
+	test_cmp expect.rev actual.rev
+}
 
 set_fake_editor
 
-# set up two branches like this:
-#
-# A - B - C - D - E
-#   \
-#     F - G - H
-#       \
-#         I
-#
-# where B, D and G touch the same file.
+# WARNING: Modifications to the initial repository can change the SHA ID used
+# in the expect2 file for the 'stop on conflicting pick' test.
 
 test_expect_success 'setup' '
-	: > file1 &&
-	git add file1 &&
-	test_tick &&
-	git commit -m A &&
-	git tag A &&
-	echo 1 > file1 &&
-	test_tick &&
-	git commit -m B file1 &&
-	: > file2 &&
-	git add file2 &&
-	test_tick &&
-	git commit -m C &&
-	echo 2 > file1 &&
-	test_tick &&
-	git commit -m D file1 &&
-	: > file3 &&
-	git add file3 &&
-	test_tick &&
-	git commit -m E &&
+	test_commit A file1 &&
+	test_commit B file1 &&
+	test_commit C file2 &&
+	test_commit D file1 &&
+	test_commit E file3 &&
 	git checkout -b branch1 A &&
-	: > file4 &&
-	git add file4 &&
-	test_tick &&
-	git commit -m F &&
-	git tag F &&
-	echo 3 > file1 &&
-	test_tick &&
-	git commit -m G file1 &&
-	: > file5 &&
-	git add file5 &&
-	test_tick &&
-	git commit -m H &&
+	test_commit F file4 &&
+	test_commit G file1 &&
+	test_commit H file5 &&
 	git checkout -b branch2 F &&
-	: > file6 &&
-	git add file6 &&
-	test_tick &&
-	git commit -m I &&
-	git tag I
+	test_commit I file6 &&
+	git checkout -b conflict-branch A &&
+	test_commit one conflict &&
+	test_commit two conflict &&
+	test_commit three conflict &&
+	test_commit four conflict &&
+	git checkout -b no-conflict-branch A &&
+	test_commit J fileJ &&
+	test_commit K fileK &&
+	test_commit L fileL &&
+	test_commit M fileM &&
+	git checkout -b no-ff-branch A &&
+	test_commit N fileN &&
+	test_commit O fileO &&
+	test_commit P fileP
+'
+
+# "exec" commands are ran with the user shell by default, but this may
+# be non-POSIX. For example, if SHELL=zsh then ">file" doesn't work
+# to create a file. Unseting SHELL avoids such non-portable behavior
+# in tests. It must be exported for it to take effect where needed.
+SHELL=
+export SHELL
+
+test_expect_success 'rebase -i with the exec command' '
+	git checkout master &&
+	(
+	FAKE_LINES="1 exec_>touch-one
+		2 exec_>touch-two exec_false exec_>touch-three
+		3 4 exec_>\"touch-file__name_with_spaces\";_>touch-after-semicolon 5" &&
+	export FAKE_LINES &&
+	test_must_fail git rebase -i A
+	) &&
+	test_path_is_file touch-one &&
+	test_path_is_file touch-two &&
+	test_path_is_missing touch-three " (should have stopped before)" &&
+	test_cmp_rev C HEAD &&
+	git rebase --continue &&
+	test_path_is_file touch-three &&
+	test_path_is_file "touch-file  name with spaces" &&
+	test_path_is_file touch-after-semicolon &&
+	test_cmp_rev master HEAD &&
+	rm -f touch-*
+'
+
+test_expect_success 'rebase -i with the exec command runs from tree root' '
+	git checkout master &&
+	mkdir subdir && (cd subdir &&
+	FAKE_LINES="1 exec_>touch-subdir" \
+		git rebase -i HEAD^
+	) &&
+	test_path_is_file touch-subdir &&
+	rm -fr subdir
+'
+
+test_expect_success 'rebase -i with the exec command checks tree cleanness' '
+	git checkout master &&
+	(
+	FAKE_LINES="exec_echo_foo_>file1 1" &&
+	export FAKE_LINES &&
+	test_must_fail git rebase -i HEAD^
+	) &&
+	test_cmp_rev master^ HEAD &&
+	git reset --hard &&
+	git rebase --continue
 '
 
 test_expect_success 'no changes are a nop' '
+	git checkout branch2 &&
 	git rebase -i F &&
 	test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch2" &&
 	test $(git rev-parse I) = $(git rev-parse HEAD)
@@ -111,19 +165,20 @@
 
 cat > expect << EOF
 diff --git a/file1 b/file1
-index e69de29..00750ed 100644
+index f70f10e..fd79235 100644
 --- a/file1
 +++ b/file1
-@@ -0,0 +1 @@
-+3
+@@ -1 +1 @@
+-A
++G
 EOF
 
 cat > expect2 << EOF
 <<<<<<< HEAD
-2
+D
 =======
-3
->>>>>>> b7ca976... G
+G
+>>>>>>> 5d18e54... G
 EOF
 
 test_expect_success 'stop on conflicting pick' '
@@ -142,7 +197,18 @@
 	git rebase --abort &&
 	test $(git rev-parse new-branch1) = $(git rev-parse HEAD) &&
 	test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch1" &&
-	! test -d .git/rebase-merge
+	test_path_is_missing .git/rebase-merge
+'
+
+test_expect_success 'abort with error when new base cannot be checked out' '
+	git rm --cached file1 &&
+	git commit -m "remove file in base" &&
+	test_must_fail git rebase -i master > output 2>&1 &&
+	grep "The following untracked working tree files would be overwritten by checkout:" \
+		output &&
+	grep "file1" output &&
+	test_path_is_missing .git/rebase-merge &&
+	git reset --hard HEAD^
 '
 
 test_expect_success 'retain authorship' '
@@ -161,7 +227,8 @@
 	test_tick &&
 	GIT_AUTHOR_NAME="Nitfol" git commit -m "nitfol" file7 &&
 	echo "******************************" &&
-	FAKE_LINES="1 squash 2" git rebase -i --onto master HEAD~2 &&
+	FAKE_LINES="1 squash 2" EXPECT_HEADER_COUNT=2 \
+		git rebase -i --onto master HEAD~2 &&
 	test B = $(cat file7) &&
 	test $(git rev-parse HEAD^) = $(git rev-parse master)
 '
@@ -179,6 +246,13 @@
 	test $HEAD = $(git rev-parse HEAD)
 '
 
+test_expect_failure 'exchange two commits with -p' '
+	git checkout H &&
+	FAKE_LINES="2 1" git rebase -i -p HEAD~2 &&
+	test H = $(git cat-file commit HEAD^ | sed -ne \$p) &&
+	test G = $(git cat-file commit HEAD | sed -ne \$p)
+'
+
 test_expect_success 'preserve merges with -p' '
 	git checkout -b to-be-preserved master^ &&
 	: > unrelated-file &&
@@ -222,7 +296,7 @@
 '
 
 test_expect_success 'edit ancestor with -p' '
-	FAKE_LINES="1 edit 2 3 4" git rebase -i -p HEAD~3 &&
+	FAKE_LINES="1 2 edit 3 4" git rebase -i -p HEAD~3 &&
 	echo 2 > unrelated-file &&
 	test_tick &&
 	git commit -m L2-modified --amend unrelated-file &&
@@ -244,42 +318,125 @@
 '
 
 test_expect_success 'verbose flag is heeded, even after --continue' '
-	git reset --hard HEAD@{1} &&
+	git reset --hard master@{1} &&
 	test_tick &&
 	test_must_fail git rebase -v -i --onto new-branch1 HEAD^ &&
 	echo resolved > file1 &&
 	git add file1 &&
 	git rebase --continue > output &&
-	grep "^ file1 |    2 +-$" output
+	grep "^ file1 | 2 +-$" output
 '
 
 test_expect_success 'multi-squash only fires up editor once' '
 	base=$(git rev-parse HEAD~4) &&
 	FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="1 squash 2 squash 3 squash 4" \
+		EXPECT_HEADER_COUNT=4 \
 		git rebase -i $base &&
 	test $base = $(git rev-parse HEAD^) &&
 	test 1 = $(git show | grep ONCE | wc -l)
 '
 
+test_expect_success 'multi-fixup does not fire up editor' '
+	git checkout -b multi-fixup E &&
+	base=$(git rev-parse HEAD~4) &&
+	FAKE_COMMIT_AMEND="NEVER" FAKE_LINES="1 fixup 2 fixup 3 fixup 4" \
+		git rebase -i $base &&
+	test $base = $(git rev-parse HEAD^) &&
+	test 0 = $(git show | grep NEVER | wc -l) &&
+	git checkout to-be-rebased &&
+	git branch -D multi-fixup
+'
+
+test_expect_success 'commit message used after conflict' '
+	git checkout -b conflict-fixup conflict-branch &&
+	base=$(git rev-parse HEAD~4) &&
+	(
+		FAKE_LINES="1 fixup 3 fixup 4" &&
+		export FAKE_LINES &&
+		test_must_fail git rebase -i $base
+	) &&
+	echo three > conflict &&
+	git add conflict &&
+	FAKE_COMMIT_AMEND="ONCE" EXPECT_HEADER_COUNT=2 \
+		git rebase --continue &&
+	test $base = $(git rev-parse HEAD^) &&
+	test 1 = $(git show | grep ONCE | wc -l) &&
+	git checkout to-be-rebased &&
+	git branch -D conflict-fixup
+'
+
+test_expect_success 'commit message retained after conflict' '
+	git checkout -b conflict-squash conflict-branch &&
+	base=$(git rev-parse HEAD~4) &&
+	(
+		FAKE_LINES="1 fixup 3 squash 4" &&
+		export FAKE_LINES &&
+		test_must_fail git rebase -i $base
+	) &&
+	echo three > conflict &&
+	git add conflict &&
+	FAKE_COMMIT_AMEND="TWICE" EXPECT_HEADER_COUNT=2 \
+		git rebase --continue &&
+	test $base = $(git rev-parse HEAD^) &&
+	test 2 = $(git show | grep TWICE | wc -l) &&
+	git checkout to-be-rebased &&
+	git branch -D conflict-squash
+'
+
+cat > expect-squash-fixup << EOF
+B
+
+D
+
+ONCE
+EOF
+
+test_expect_success 'squash and fixup generate correct log messages' '
+	git checkout -b squash-fixup E &&
+	base=$(git rev-parse HEAD~4) &&
+	FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="1 fixup 2 squash 3 fixup 4" \
+		EXPECT_HEADER_COUNT=4 \
+		git rebase -i $base &&
+	git cat-file commit HEAD | sed -e 1,/^\$/d > actual-squash-fixup &&
+	test_cmp expect-squash-fixup actual-squash-fixup &&
+	git checkout to-be-rebased &&
+	git branch -D squash-fixup
+'
+
+test_expect_success 'squash ignores comments' '
+	git checkout -b skip-comments E &&
+	base=$(git rev-parse HEAD~4) &&
+	FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="# 1 # squash 2 # squash 3 # squash 4 #" \
+		EXPECT_HEADER_COUNT=4 \
+		git rebase -i $base &&
+	test $base = $(git rev-parse HEAD^) &&
+	test 1 = $(git show | grep ONCE | wc -l) &&
+	git checkout to-be-rebased &&
+	git branch -D skip-comments
+'
+
+test_expect_success 'squash ignores blank lines' '
+	git checkout -b skip-blank-lines E &&
+	base=$(git rev-parse HEAD~4) &&
+	FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="> 1 > squash 2 > squash 3 > squash 4 >" \
+		EXPECT_HEADER_COUNT=4 \
+		git rebase -i $base &&
+	test $base = $(git rev-parse HEAD^) &&
+	test 1 = $(git show | grep ONCE | wc -l) &&
+	git checkout to-be-rebased &&
+	git branch -D skip-blank-lines
+'
+
 test_expect_success 'squash works as expected' '
-	for n in one two three four
-	do
-		echo $n >> file$n &&
-		git add file$n &&
-		git commit -m $n
-	done &&
+	git checkout -b squash-works no-conflict-branch &&
 	one=$(git rev-parse HEAD~3) &&
-	FAKE_LINES="1 squash 3 2" git rebase -i HEAD~3 &&
+	FAKE_LINES="1 squash 3 2" EXPECT_HEADER_COUNT=2 \
+		git rebase -i HEAD~3 &&
 	test $one = $(git rev-parse HEAD~2)
 '
 
 test_expect_success 'interrupted squash works as expected' '
-	for n in one two three four
-	do
-		echo $n >> conflict &&
-		git add conflict &&
-		git commit -m $n
-	done &&
+	git checkout -b interrupted-squash conflict-branch &&
 	one=$(git rev-parse HEAD~3) &&
 	(
 		FAKE_LINES="1 squash 3 2" &&
@@ -296,12 +453,7 @@
 '
 
 test_expect_success 'interrupted squash works as expected (case 2)' '
-	for n in one two three four
-	do
-		echo $n >> conflict &&
-		git add conflict &&
-		git commit -m $n
-	done &&
+	git checkout -b interrupted-squash2 conflict-branch &&
 	one=$(git rev-parse HEAD~3) &&
 	(
 		FAKE_LINES="3 squash 1 2" &&
@@ -376,6 +528,20 @@
 	git rebase --abort
 '
 
+test_expect_success 'clean error after failed "exec"' '
+	test_tick &&
+	test_when_finished "git rebase --abort || :" &&
+	(
+		FAKE_LINES="1 exec_false" &&
+		export FAKE_LINES &&
+		test_must_fail git rebase -i HEAD^
+	) &&
+	echo "edited again" > file7 &&
+	git add file7 &&
+	test_must_fail git rebase --continue 2>error &&
+	grep "You have staged changes in your working tree." error
+'
+
 test_expect_success 'rebase a detached HEAD' '
 	grandparent=$(git rev-parse HEAD~2) &&
 	git checkout $(git rev-parse HEAD) &&
@@ -426,7 +592,7 @@
 
 	git checkout -b branch4 HEAD &&
 	GIT_EDITOR=: git commit --amend \
-		--author="Somebody else <somebody@else.com>" 
+		--author="Somebody else <somebody@else.com>" &&
 	test $(git rev-parse branch3) != $(git rev-parse branch4) &&
 	git rebase -i branch3 &&
 	test $(git rev-parse branch3) = $(git rev-parse branch4)
@@ -441,7 +607,7 @@
 		git add elif && git commit -m "submodule initial"
 	) &&
 	echo 1 >file1 &&
-	git add file1 sub
+	git add file1 sub &&
 	test_tick &&
 	git commit -m "One" &&
 	echo 2 >file1 &&
@@ -459,8 +625,38 @@
 	FAKE_LINES="1 squash 2 3" git rebase -i A
 '
 
+test_expect_success 'submodule conflict setup' '
+	git tag submodule-base &&
+	git checkout HEAD^ &&
+	(
+		cd sub && git checkout HEAD^ && echo 4 >elif &&
+		git add elif && git commit -m "submodule conflict"
+	) &&
+	git add sub &&
+	test_tick &&
+	git commit -m "Conflict in submodule" &&
+	git tag submodule-topic
+'
+
+test_expect_success 'rebase -i continue with only submodule staged' '
+	test_must_fail git rebase -i submodule-base &&
+	git add sub &&
+	git rebase --continue &&
+	test $(git rev-parse submodule-base) != $(git rev-parse HEAD)
+'
+
+test_expect_success 'rebase -i continue with unstaged submodule' '
+	git checkout submodule-topic &&
+	git reset --hard &&
+	test_must_fail git rebase -i submodule-base &&
+	git reset &&
+	git rebase --continue &&
+	test $(git rev-parse submodule-base) = $(git rev-parse HEAD)
+'
+
 test_expect_success 'avoid unnecessary reset' '
 	git checkout master &&
+	git reset --hard &&
 	test-chmtime =123456789 file3 &&
 	git update-index --refresh &&
 	HEAD=$(git rev-parse HEAD) &&
@@ -470,4 +666,241 @@
 	test 123456789 = $MTIME
 '
 
+test_expect_success 'reword' '
+	git checkout -b reword-branch master &&
+	FAKE_LINES="1 2 3 reword 4" FAKE_COMMIT_MESSAGE="E changed" git rebase -i A &&
+	git show HEAD | grep "E changed" &&
+	test $(git rev-parse master) != $(git rev-parse HEAD) &&
+	test $(git rev-parse master^) = $(git rev-parse HEAD^) &&
+	FAKE_LINES="1 2 reword 3 4" FAKE_COMMIT_MESSAGE="D changed" git rebase -i A &&
+	git show HEAD^ | grep "D changed" &&
+	FAKE_LINES="reword 1 2 3 4" FAKE_COMMIT_MESSAGE="B changed" git rebase -i A &&
+	git show HEAD~3 | grep "B changed" &&
+	FAKE_LINES="1 reword 2 3 4" FAKE_COMMIT_MESSAGE="C changed" git rebase -i A &&
+	git show HEAD~2 | grep "C changed"
+'
+
+test_expect_success 'rebase -i can copy notes' '
+	git config notes.rewrite.rebase true &&
+	git config notes.rewriteRef "refs/notes/*" &&
+	test_commit n1 &&
+	test_commit n2 &&
+	test_commit n3 &&
+	git notes add -m"a note" n3 &&
+	git rebase --onto n1 n2 &&
+	test "a note" = "$(git notes show HEAD)"
+'
+
+cat >expect <<EOF
+an earlier note
+
+a note
+EOF
+
+test_expect_success 'rebase -i can copy notes over a fixup' '
+	git reset --hard n3 &&
+	git notes add -m"an earlier note" n2 &&
+	GIT_NOTES_REWRITE_MODE=concatenate FAKE_LINES="1 fixup 2" git rebase -i n1 &&
+	git notes show > output &&
+	test_cmp expect output
+'
+
+test_expect_success 'rebase while detaching HEAD' '
+	git symbolic-ref HEAD &&
+	grandparent=$(git rev-parse HEAD~2) &&
+	test_tick &&
+	FAKE_LINES="2 1" git rebase -i HEAD~2 HEAD^0 &&
+	test $grandparent = $(git rev-parse HEAD~2) &&
+	test_must_fail git symbolic-ref HEAD
+'
+
+test_tick # Ensure that the rebased commits get a different timestamp.
+test_expect_success 'always cherry-pick with --no-ff' '
+	git checkout no-ff-branch &&
+	git tag original-no-ff-branch &&
+	git rebase -i --no-ff A &&
+	touch empty &&
+	for p in 0 1 2
+	do
+		test ! $(git rev-parse HEAD~$p) = $(git rev-parse original-no-ff-branch~$p) &&
+		git diff HEAD~$p original-no-ff-branch~$p > out &&
+		test_cmp empty out
+	done &&
+	test $(git rev-parse HEAD~3) = $(git rev-parse original-no-ff-branch~3) &&
+	git diff HEAD~3 original-no-ff-branch~3 > out &&
+	test_cmp empty out
+'
+
+test_expect_success 'set up commits with funny messages' '
+	git checkout -b funny A &&
+	echo >>file1 &&
+	test_tick &&
+	git commit -a -m "end with slash\\" &&
+	echo >>file1 &&
+	test_tick &&
+	git commit -a -m "something (\000) that looks like octal" &&
+	echo >>file1 &&
+	test_tick &&
+	git commit -a -m "something (\n) that looks like a newline" &&
+	echo >>file1 &&
+	test_tick &&
+	git commit -a -m "another commit"
+'
+
+test_expect_success 'rebase-i history with funny messages' '
+	git rev-list A..funny >expect &&
+	test_tick &&
+	FAKE_LINES="1 2 3 4" git rebase -i A &&
+	git rev-list A.. >actual &&
+	test_cmp expect actual
+'
+
+
+test_expect_success 'prepare for rebase -i --exec' '
+	git checkout master &&
+	git checkout -b execute &&
+	test_commit one_exec main.txt one_exec &&
+	test_commit two_exec main.txt two_exec &&
+	test_commit three_exec main.txt three_exec
+'
+
+
+test_expect_success 'running "git rebase -i --exec git show HEAD"' '
+	git rebase -i --exec "git show HEAD" HEAD~2 >actual &&
+	(
+		FAKE_LINES="1 exec_git_show_HEAD 2 exec_git_show_HEAD" &&
+		export FAKE_LINES &&
+		git rebase -i HEAD~2 >expect
+	) &&
+	sed -e "1,9d" expect >expected &&
+	test_cmp expected actual
+'
+
+
+test_expect_success 'running "git rebase --exec git show HEAD -i"' '
+	git reset --hard execute &&
+	git rebase --exec "git show HEAD" -i HEAD~2 >actual &&
+	(
+		FAKE_LINES="1 exec_git_show_HEAD 2 exec_git_show_HEAD" &&
+		export FAKE_LINES &&
+		git rebase -i HEAD~2 >expect
+	) &&
+	sed -e "1,9d" expect >expected &&
+	test_cmp expected actual
+'
+
+
+test_expect_success 'running "git rebase -ix git show HEAD"' '
+	git reset --hard execute &&
+	git rebase -ix "git show HEAD" HEAD~2 >actual &&
+	(
+		FAKE_LINES="1 exec_git_show_HEAD 2 exec_git_show_HEAD" &&
+		export FAKE_LINES &&
+		git rebase -i HEAD~2 >expect
+	) &&
+	sed -e "1,9d" expect >expected &&
+	test_cmp expected actual
+'
+
+
+test_expect_success 'rebase -ix with several <CMD>' '
+	git reset --hard execute &&
+	git rebase -ix "git show HEAD; pwd" HEAD~2 >actual &&
+	(
+		FAKE_LINES="1 exec_git_show_HEAD;_pwd 2 exec_git_show_HEAD;_pwd" &&
+		export FAKE_LINES &&
+		git rebase -i HEAD~2 >expect
+	) &&
+	sed -e "1,9d" expect >expected &&
+	test_cmp expected actual
+'
+
+
+test_expect_success 'rebase -ix with several instances of --exec' '
+	git reset --hard execute &&
+	git rebase -i --exec "git show HEAD" --exec "pwd" HEAD~2 >actual &&
+	(
+		FAKE_LINES="1 exec_git_show_HEAD exec_pwd 2
+				exec_git_show_HEAD exec_pwd" &&
+		export FAKE_LINES &&
+		git rebase -i HEAD~2 >expect
+	) &&
+	sed -e "1,11d" expect >expected &&
+	test_cmp expected actual
+'
+
+
+test_expect_success 'rebase -ix with --autosquash' '
+	git reset --hard execute &&
+	git checkout -b autosquash &&
+	echo second >second.txt &&
+	git add second.txt &&
+	git commit -m "fixup! two_exec" &&
+	echo bis >bis.txt &&
+	git add bis.txt &&
+	git commit -m "fixup! two_exec" &&
+	(
+		git checkout -b autosquash_actual &&
+		git rebase -i --exec "git show HEAD" --autosquash HEAD~4 >actual
+	) &&
+	git checkout autosquash &&
+	(
+		git checkout -b autosquash_expected &&
+		FAKE_LINES="1 fixup 3 fixup 4 exec_git_show_HEAD 2 exec_git_show_HEAD" &&
+		export FAKE_LINES &&
+		git rebase -i HEAD~4 >expect
+	) &&
+	sed -e "1,13d" expect >expected &&
+	test_cmp expected actual
+'
+
+
+test_expect_success 'rebase --exec without -i shows error message' '
+	git reset --hard execute &&
+	test_must_fail git rebase --exec "git show HEAD" HEAD~2 2>actual &&
+	echo "--exec option must be used with --interactive option" >expected &&
+	test_i18ncmp expected actual
+'
+
+
+test_expect_success 'rebase -i --exec without <CMD>' '
+	git reset --hard execute &&
+	test_must_fail git rebase -i --exec 2>tmp &&
+	sed -e "1d" tmp >actual &&
+	test_must_fail git rebase -h >expected &&
+	test_cmp expected actual &&
+	git checkout master
+'
+
+test_expect_success 'rebase -i --root re-order and drop commits' '
+	git checkout E &&
+	FAKE_LINES="3 1 2 5" git rebase -i --root &&
+	test E = $(git cat-file commit HEAD | sed -ne \$p) &&
+	test B = $(git cat-file commit HEAD^ | sed -ne \$p) &&
+	test A = $(git cat-file commit HEAD^^ | sed -ne \$p) &&
+	test C = $(git cat-file commit HEAD^^^ | sed -ne \$p) &&
+	test 0 = $(git cat-file commit HEAD^^^ | grep -c ^parent\ )
+'
+
+test_expect_success 'rebase -i --root retain root commit author and message' '
+	git checkout A &&
+	echo B >file7 &&
+	git add file7 &&
+	GIT_AUTHOR_NAME="Twerp Snog" git commit -m "different author" &&
+	FAKE_LINES="2" git rebase -i --root &&
+	git cat-file commit HEAD | grep -q "^author Twerp Snog" &&
+	git cat-file commit HEAD | grep -q "^different author$"
+'
+
+test_expect_success 'rebase -i --root temporary sentinel commit' '
+	git checkout B &&
+	(
+		FAKE_LINES="2" &&
+		export FAKE_LINES &&
+		test_must_fail git rebase -i --root
+	) &&
+	git cat-file commit HEAD | grep "^tree 4b825dc642cb" &&
+	git rebase --abort
+'
+
 test_done
diff --git a/t/t3405-rebase-malformed.sh b/t/t3405-rebase-malformed.sh
index e5ad67c..19eddad 100755
--- a/t/t3405-rebase-malformed.sh
+++ b/t/t3405-rebase-malformed.sh
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-test_description='rebase should not insist on git message convention'
+test_description='rebase should handle arbitrary git message'
 
 . ./test-lib.sh
 
@@ -12,6 +12,11 @@
 to oneline summary format.
 EOF
 
+cat >G <<\EOF
+commit log message containing a diff
+EOF
+
+
 test_expect_success setup '
 
 	>file1 &&
@@ -19,8 +24,9 @@
 	git add file1 file2 &&
 	test_tick &&
 	git commit -m "Initial commit" &&
+	git branch diff-in-message
 
-	git checkout -b side &&
+	git checkout -b multi-line-subject &&
 	cat F >file2 &&
 	git add file2 &&
 	test_tick &&
@@ -28,6 +34,17 @@
 
 	git cat-file commit HEAD | sed -e "1,/^\$/d" >F0 &&
 
+	git checkout diff-in-message &&
+	echo "commit log message containing a diff" >G &&
+	echo "" >>G
+	cat G >file2 &&
+	git add file2 &&
+	git diff --cached >>G &&
+	test_tick &&
+	git commit -F G &&
+
+	git cat-file commit HEAD | sed -e "1,/^\$/d" >G0 &&
+
 	git checkout master &&
 
 	echo One >file1 &&
@@ -36,13 +53,20 @@
 	git commit -m "Second commit"
 '
 
-test_expect_success rebase '
+test_expect_success 'rebase commit with multi-line subject' '
 
-	git rebase master side &&
+	git rebase master multi-line-subject &&
 	git cat-file commit HEAD | sed -e "1,/^\$/d" >F1 &&
 
 	test_cmp F0 F1 &&
 	test_cmp F F0
 '
 
+test_expect_success 'rebase commit with diff in message' '
+	git rebase master diff-in-message &&
+	git cat-file commit HEAD | sed -e "1,/^$/d" >G1 &&
+	test_cmp G0 G1 &&
+	test_cmp G G0
+'
+
 test_done
diff --git a/t/t3406-rebase-message.sh b/t/t3406-rebase-message.sh
index 85fc7c4..6898377 100755
--- a/t/t3406-rebase-message.sh
+++ b/t/t3406-rebase-message.sh
@@ -43,23 +43,28 @@
 '
 
 test_expect_success 'rebase --stat' '
-        git reset --hard start
+	git reset --hard start &&
         git rebase --stat master >diffstat.txt &&
         grep "^ fileX |  *1 +$" diffstat.txt
 '
 
 test_expect_success 'rebase w/config rebase.stat' '
-        git reset --hard start
+	git reset --hard start &&
         git config rebase.stat true &&
         git rebase master >diffstat.txt &&
         grep "^ fileX |  *1 +$" diffstat.txt
 '
 
 test_expect_success 'rebase -n overrides config rebase.stat config' '
-        git reset --hard start
+	git reset --hard start &&
         git config rebase.stat true &&
         git rebase -n master >diffstat.txt &&
         ! grep "^ fileX |  *1 +$" diffstat.txt
 '
 
+test_expect_success 'rebase --onto outputs the invalid ref' '
+	test_must_fail git rebase --onto invalid-ref HEAD HEAD 2>err &&
+	grep "invalid-ref" err
+'
+
 test_done
diff --git a/t/t3407-rebase-abort.sh b/t/t3407-rebase-abort.sh
index 2999e78..a6a6c40 100755
--- a/t/t3407-rebase-abort.sh
+++ b/t/t3407-rebase-abort.sh
@@ -38,7 +38,7 @@
 		# Clean up the state from the previous one
 		git reset --hard pre-rebase &&
 		test_must_fail git rebase$type master &&
-		test -d "$dotest" &&
+		test_path_is_dir "$dotest" &&
 		git rebase --abort &&
 		test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) &&
 		test ! -d "$dotest"
@@ -49,7 +49,7 @@
 		# Clean up the state from the previous one
 		git reset --hard pre-rebase &&
 		test_must_fail git rebase$type master &&
-		test -d "$dotest" &&
+		test_path_is_dir "$dotest" &&
 		test_must_fail git rebase --skip &&
 		test $(git rev-parse HEAD) = $(git rev-parse master) &&
 		git rebase --abort &&
@@ -62,7 +62,7 @@
 		# Clean up the state from the previous one
 		git reset --hard pre-rebase &&
 		test_must_fail git rebase$type master &&
-		test -d "$dotest" &&
+		test_path_is_dir "$dotest" &&
 		echo c > a &&
 		echo d >> a &&
 		git add a &&
@@ -72,6 +72,28 @@
 		test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) &&
 		test ! -d "$dotest"
 	'
+
+	test_expect_success "rebase$type --abort does not update reflog" '
+		cd "$work_dir" &&
+		# Clean up the state from the previous one
+		git reset --hard pre-rebase &&
+		git reflog show to-rebase > reflog_before &&
+		test_must_fail git rebase$type master &&
+		git rebase --abort &&
+		git reflog show to-rebase > reflog_after &&
+		test_cmp reflog_before reflog_after &&
+		rm reflog_before reflog_after
+	'
+
+	test_expect_success 'rebase --abort can not be used with other options' '
+		cd "$work_dir" &&
+		# Clean up the state from the previous one
+		git reset --hard pre-rebase &&
+		test_must_fail git rebase$type master &&
+		test_must_fail git rebase -v --abort &&
+		test_must_fail git rebase --abort -v &&
+		git rebase --abort
+	'
 }
 
 testrebase "" .git/rebase-apply
diff --git a/t/t3408-rebase-multi-line.sh b/t/t3408-rebase-multi-line.sh
index e12cd57..6b84e60 100755
--- a/t/t3408-rebase-multi-line.sh
+++ b/t/t3408-rebase-multi-line.sh
@@ -16,7 +16,7 @@
 	git commit -a -m "A sample commit log message that has a long
 summary that spills over multiple lines.
 
-But otherwise with a sane description."
+But otherwise with a sane description." &&
 
 	git branch side &&
 
@@ -32,8 +32,8 @@
 
 	git checkout side &&
 	git rebase master &&
-	git cat-file commit HEAD | sed -e "1,/^$/d" >actual &&
-	git cat-file commit side@{1} | sed -e "1,/^$/d" >expect &&
+	git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
+	git cat-file commit side@{1} | sed -e "1,/^\$/d" >expect &&
 	test_cmp expect actual
 
 '
diff --git a/t/t3409-rebase-preserve-merges.sh b/t/t3409-rebase-preserve-merges.sh
index e6c8327..6de4e22 100755
--- a/t/t3409-rebase-preserve-merges.sh
+++ b/t/t3409-rebase-preserve-merges.sh
@@ -27,38 +27,74 @@
 #    \
 #     B2       <-- origin/topic
 #
-# In both cases, 'topic' is rebased onto 'origin/topic'.
+# Clone 3 (no-ff merge):
+#
+# A1--A2--B3   <-- origin/master
+#  \
+#   B1------M  <-- topic
+#    \     /
+#     \--A3    <-- topic2
+#      \
+#       B2     <-- origin/topic
+#
+# Clone 4 (merge using second parent as base):
+#
+# A1--A2--B3   <-- origin/master
+#  \
+#   B1--A3--M  <-- topic
+#    \     /
+#     \--A4    <-- topic2
+#      \
+#       B2     <-- origin/topic
 
 test_expect_success 'setup for merge-preserving rebase' \
 	'echo First > A &&
 	git add A &&
-	git-commit -m "Add A1" &&
+	git commit -m "Add A1" &&
 	git checkout -b topic &&
 	echo Second > B &&
 	git add B &&
-	git-commit -m "Add B1" &&
+	git commit -m "Add B1" &&
 	git checkout -f master &&
 	echo Third >> A &&
-	git-commit -a -m "Modify A2" &&
+	git commit -a -m "Modify A2" &&
 
 	git clone ./. clone1 &&
-	cd clone1 &&
+	(cd clone1 &&
 	git checkout -b topic origin/topic &&
-	git merge origin/master &&
-	cd .. &&
+	git merge origin/master
+	) &&
+
+	git clone ./. clone4 &&
+	(
+		cd clone4 &&
+		git checkout -b topic origin/topic &&
+		git merge origin/master
+	) &&
 
 	echo Fifth > B &&
 	git add B &&
 	git commit -m "Add different B" &&
 
 	git clone ./. clone2 &&
-	cd clone2 &&
-	git checkout -b topic origin/topic &&
-	test_must_fail git merge origin/master &&
-	echo Resolved > B &&
-	git add B &&
-	git commit -m "Merge origin/master into topic" &&
-	cd .. &&
+	(
+		cd clone2 &&
+		git checkout -b topic origin/topic &&
+		test_must_fail git merge origin/master &&
+		echo Resolved >B &&
+		git add B &&
+		git commit -m "Merge origin/master into topic"
+	) &&
+
+	git clone ./. clone3 &&
+	(
+		cd clone3 &&
+		git checkout -b topic2 origin/topic &&
+		echo Sixth > A &&
+		git commit -a -m "Modify A3" &&
+		git checkout -b topic origin/topic &&
+		git merge --no-ff topic2
+	) &&
 
 	git checkout topic &&
 	echo Fourth >> B &&
@@ -71,7 +107,7 @@
 	git fetch &&
 	git rebase -p origin/topic &&
 	test 1 = $(git rev-list --all --pretty=oneline | grep "Modify A" | wc -l) &&
-	test 1 = $(git rev-list --all --pretty=oneline | grep "Merge commit" | wc -l)
+	test 1 = $(git rev-list --all --pretty=oneline | grep "Merge remote-tracking branch " | wc -l)
 	)
 '
 
@@ -92,4 +128,25 @@
 	)
 '
 
+test_expect_success 'rebase -p preserves no-ff merges' '
+	(
+	cd clone3 &&
+	git fetch &&
+	git rebase -p origin/topic &&
+	test 3 = $(git rev-list --all --pretty=oneline | grep "Modify A" | wc -l) &&
+	test 1 = $(git rev-list --all --pretty=oneline | grep "Merge branch" | wc -l)
+	)
+'
+
+test_expect_success 'rebase -p works when base inside second parent' '
+	(
+	cd clone4 &&
+	git fetch &&
+	git rebase -p HEAD^2 &&
+	test 1 = $(git rev-list --all --pretty=oneline | grep "Modify A" | wc -l) &&
+	test 1 = $(git rev-list --all --pretty=oneline | grep "Modify B" | wc -l) &&
+	test 1 = $(git rev-list --all --pretty=oneline | grep "Merge remote-tracking branch " | wc -l)
+	)
+'
+
 test_done
diff --git a/t/t3410-rebase-preserve-dropped-merges.sh b/t/t3410-rebase-preserve-dropped-merges.sh
index c49143a..6f73b95 100755
--- a/t/t3410-rebase-preserve-dropped-merges.sh
+++ b/t/t3410-rebase-preserve-dropped-merges.sh
@@ -43,11 +43,11 @@
 # G2 = same changes as G
 test_expect_success 'skip same-resolution merges with -p' '
 	git checkout H &&
-	! git merge E &&
+	test_must_fail git merge E &&
 	test_commit L file1 23 &&
 	git checkout I &&
 	test_commit G2 file1 3 &&
-	! git merge E &&
+	test_must_fail git merge E &&
 	test_commit J file1 23 &&
 	test_commit K file7 file7 &&
 	git rebase -i -p L &&
@@ -65,11 +65,11 @@
 # G2 = different changes as G
 test_expect_success 'keep different-resolution merges with -p' '
 	git checkout H &&
-	! git merge E &&
+	test_must_fail git merge E &&
 	test_commit L2 file1 23 &&
 	git checkout I &&
 	test_commit G3 file1 4 &&
-	! git merge E &&
+	test_must_fail git merge E &&
 	test_commit J2 file1 24 &&
 	test_commit K2 file7 file7 &&
 	test_must_fail git rebase -i -p L2 &&
diff --git a/t/t3411-rebase-preserve-around-merges.sh b/t/t3411-rebase-preserve-around-merges.sh
index 6533505..dc81bf2 100755
--- a/t/t3411-rebase-preserve-around-merges.sh
+++ b/t/t3411-rebase-preserve-around-merges.sh
@@ -10,7 +10,7 @@
 '
 . ./test-lib.sh
 
-. ../lib-rebase.sh
+. "$TEST_DIRECTORY"/lib-rebase.sh
 
 set_fake_editor
 
@@ -37,7 +37,7 @@
 #        -- C1 --
 #
 test_expect_success 'squash F1 into D1' '
-	FAKE_LINES="1 squash 3 2" git rebase -i -p B1 &&
+	FAKE_LINES="1 squash 4 2 3" git rebase -i -p B1 &&
 	test "$(git rev-parse HEAD^2)" = "$(git rev-parse C1)" &&
 	test "$(git rev-parse HEAD~2)" = "$(git rev-parse B1)" &&
 	git tag E2
@@ -56,6 +56,7 @@
 # And rebase G1..M1 onto E2
 
 test_expect_success 'rebase two levels of merge' '
+	git checkout A1 &&
 	test_commit G1 &&
 	test_commit H1 &&
 	test_commit I1 &&
diff --git a/t/t3412-rebase-root.sh b/t/t3412-rebase-root.sh
index 5869061..0b52105 100755
--- a/t/t3412-rebase-root.sh
+++ b/t/t3412-rebase-root.sh
@@ -22,8 +22,9 @@
 	test_commit 4 B
 '
 
-test_expect_success 'rebase --root expects --onto' '
-	test_must_fail git rebase --root
+test_expect_success 'rebase --root fails with too many args' '
+	git checkout -B fail other &&
+	test_must_fail git rebase --onto master --root fail fail
 '
 
 test_expect_success 'setup pre-rebase hook' '
@@ -42,7 +43,7 @@
 EOF
 
 test_expect_success 'rebase --root --onto <newbase>' '
-	git checkout -b work &&
+	git checkout -b work other &&
 	git rebase --root --onto master &&
 	git log --pretty=tformat:"%s" > rebased &&
 	test_cmp expect rebased
@@ -173,14 +174,14 @@
 test_expect_success 'pre-rebase hook stops rebase' '
 	git checkout -b stops1 other &&
 	test_must_fail git rebase --root --onto master &&
-	test "z$(git symbolic-ref HEAD)" = zrefs/heads/stops1
+	test "z$(git symbolic-ref HEAD)" = zrefs/heads/stops1 &&
 	test 0 = $(git rev-list other...stops1 | wc -l)
 '
 
 test_expect_success 'pre-rebase hook stops rebase -i' '
 	git checkout -b stops2 other &&
 	test_must_fail git rebase --root --onto master &&
-	test "z$(git symbolic-ref HEAD)" = zrefs/heads/stops2
+	test "z$(git symbolic-ref HEAD)" = zrefs/heads/stops2 &&
 	test 0 = $(git rev-list other...stops2 | wc -l)
 '
 
diff --git a/t/t3414-rebase-preserve-onto.sh b/t/t3414-rebase-preserve-onto.sh
index 80019ee..ee0a6cc 100755
--- a/t/t3414-rebase-preserve-onto.sh
+++ b/t/t3414-rebase-preserve-onto.sh
@@ -10,7 +10,7 @@
 '
 . ./test-lib.sh
 
-. ../lib-rebase.sh
+. "$TEST_DIRECTORY"/lib-rebase.sh
 
 # Set up branches like this:
 # A1---B1---E1---F1---G1
diff --git a/t/t3415-rebase-autosquash.sh b/t/t3415-rebase-autosquash.sh
new file mode 100755
index 0000000..a1e86c4
--- /dev/null
+++ b/t/t3415-rebase-autosquash.sh
@@ -0,0 +1,196 @@
+#!/bin/sh
+
+test_description='auto squash'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+	echo 0 >file0 &&
+	git add . &&
+	test_tick &&
+	git commit -m "initial commit" &&
+	echo 0 >file1 &&
+	echo 2 >file2 &&
+	git add . &&
+	test_tick &&
+	git commit -m "first commit" &&
+	git tag first-commit &&
+	echo 3 >file3 &&
+	git add . &&
+	test_tick &&
+	git commit -m "second commit" &&
+	git tag base
+'
+
+test_auto_fixup () {
+	git reset --hard base &&
+	echo 1 >file1 &&
+	git add -u &&
+	test_tick &&
+	git commit -m "fixup! first" &&
+
+	git tag $1 &&
+	test_tick &&
+	git rebase $2 -i HEAD^^^ &&
+	git log --oneline >actual &&
+	test_line_count = 3 actual &&
+	git diff --exit-code $1 &&
+	test 1 = "$(git cat-file blob HEAD^:file1)" &&
+	test 1 = $(git cat-file commit HEAD^ | grep first | wc -l)
+}
+
+test_expect_success 'auto fixup (option)' '
+	test_auto_fixup final-fixup-option --autosquash
+'
+
+test_expect_success 'auto fixup (config)' '
+	git config rebase.autosquash true &&
+	test_auto_fixup final-fixup-config-true &&
+	test_must_fail test_auto_fixup fixup-config-true-no --no-autosquash &&
+	git config rebase.autosquash false &&
+	test_must_fail test_auto_fixup final-fixup-config-false
+'
+
+test_auto_squash () {
+	git reset --hard base &&
+	echo 1 >file1 &&
+	git add -u &&
+	test_tick &&
+	git commit -m "squash! first" &&
+
+	git tag $1 &&
+	test_tick &&
+	git rebase $2 -i HEAD^^^ &&
+	git log --oneline >actual &&
+	test_line_count = 3 actual &&
+	git diff --exit-code $1 &&
+	test 1 = "$(git cat-file blob HEAD^:file1)" &&
+	test 2 = $(git cat-file commit HEAD^ | grep first | wc -l)
+}
+
+test_expect_success 'auto squash (option)' '
+	test_auto_squash final-squash --autosquash
+'
+
+test_expect_success 'auto squash (config)' '
+	git config rebase.autosquash true &&
+	test_auto_squash final-squash-config-true &&
+	test_must_fail test_auto_squash squash-config-true-no --no-autosquash &&
+	git config rebase.autosquash false &&
+	test_must_fail test_auto_squash final-squash-config-false
+'
+
+test_expect_success 'misspelled auto squash' '
+	git reset --hard base &&
+	echo 1 >file1 &&
+	git add -u &&
+	test_tick &&
+	git commit -m "squash! forst" &&
+	git tag final-missquash &&
+	test_tick &&
+	git rebase --autosquash -i HEAD^^^ &&
+	git log --oneline >actual &&
+	test_line_count = 4 actual &&
+	git diff --exit-code final-missquash &&
+	test 0 = $(git rev-list final-missquash...HEAD | wc -l)
+'
+
+test_expect_success 'auto squash that matches 2 commits' '
+	git reset --hard base &&
+	echo 4 >file4 &&
+	git add file4 &&
+	test_tick &&
+	git commit -m "first new commit" &&
+	echo 1 >file1 &&
+	git add -u &&
+	test_tick &&
+	git commit -m "squash! first" &&
+	git tag final-multisquash &&
+	test_tick &&
+	git rebase --autosquash -i HEAD~4 &&
+	git log --oneline >actual &&
+	test_line_count = 4 actual &&
+	git diff --exit-code final-multisquash &&
+	test 1 = "$(git cat-file blob HEAD^^:file1)" &&
+	test 2 = $(git cat-file commit HEAD^^ | grep first | wc -l) &&
+	test 1 = $(git cat-file commit HEAD | grep first | wc -l)
+'
+
+test_expect_success 'auto squash that matches a commit after the squash' '
+	git reset --hard base &&
+	echo 1 >file1 &&
+	git add -u &&
+	test_tick &&
+	git commit -m "squash! third" &&
+	echo 4 >file4 &&
+	git add file4 &&
+	test_tick &&
+	git commit -m "third commit" &&
+	git tag final-presquash &&
+	test_tick &&
+	git rebase --autosquash -i HEAD~4 &&
+	git log --oneline >actual &&
+	test_line_count = 5 actual &&
+	git diff --exit-code final-presquash &&
+	test 0 = "$(git cat-file blob HEAD^^:file1)" &&
+	test 1 = "$(git cat-file blob HEAD^:file1)" &&
+	test 1 = $(git cat-file commit HEAD | grep third | wc -l) &&
+	test 1 = $(git cat-file commit HEAD^ | grep third | wc -l)
+'
+test_expect_success 'auto squash that matches a sha1' '
+	git reset --hard base &&
+	echo 1 >file1 &&
+	git add -u &&
+	test_tick &&
+	git commit -m "squash! $(git rev-parse --short HEAD^)" &&
+	git tag final-shasquash &&
+	test_tick &&
+	git rebase --autosquash -i HEAD^^^ &&
+	git log --oneline >actual &&
+	test_line_count = 3 actual &&
+	git diff --exit-code final-shasquash &&
+	test 1 = "$(git cat-file blob HEAD^:file1)" &&
+	test 1 = $(git cat-file commit HEAD^ | grep squash | wc -l)
+'
+
+test_expect_success 'auto squash that matches longer sha1' '
+	git reset --hard base &&
+	echo 1 >file1 &&
+	git add -u &&
+	test_tick &&
+	git commit -m "squash! $(git rev-parse --short=11 HEAD^)" &&
+	git tag final-longshasquash &&
+	test_tick &&
+	git rebase --autosquash -i HEAD^^^ &&
+	git log --oneline >actual &&
+	test_line_count = 3 actual &&
+	git diff --exit-code final-longshasquash &&
+	test 1 = "$(git cat-file blob HEAD^:file1)" &&
+	test 1 = $(git cat-file commit HEAD^ | grep squash | wc -l)
+'
+
+test_auto_commit_flags () {
+	git reset --hard base &&
+	echo 1 >file1 &&
+	git add -u &&
+	test_tick &&
+	git commit --$1 first-commit &&
+	git tag final-commit-$1 &&
+	test_tick &&
+	git rebase --autosquash -i HEAD^^^ &&
+	git log --oneline >actual &&
+	test_line_count = 3 actual &&
+	git diff --exit-code final-commit-$1 &&
+	test 1 = "$(git cat-file blob HEAD^:file1)" &&
+	test $2 = $(git cat-file commit HEAD^ | grep first | wc -l)
+}
+
+test_expect_success 'use commit --fixup' '
+	test_auto_commit_flags fixup 1
+'
+
+test_expect_success 'use commit --squash' '
+	test_auto_commit_flags squash 2
+'
+
+test_done
diff --git a/t/t3416-rebase-onto-threedots.sh b/t/t3416-rebase-onto-threedots.sh
new file mode 100755
index 0000000..ddf2f64
--- /dev/null
+++ b/t/t3416-rebase-onto-threedots.sh
@@ -0,0 +1,105 @@
+#!/bin/sh
+
+test_description='git rebase --onto A...B'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-rebase.sh"
+
+# Rebase only the tip commit of "topic" on merge base between "master"
+# and "topic".  Cannot do this for "side" with "master" because there
+# is no single merge base.
+#
+#
+#	    F---G topic                             G'
+#	   /                                       /
+# A---B---C---D---E master      -->       A---B---C---D---E
+#      \   \ /
+#	\   x
+#	 \ / \
+#	  H---I---J---K side
+
+test_expect_success setup '
+	test_commit A &&
+	test_commit B &&
+	git branch side &&
+	test_commit C &&
+	git branch topic &&
+	git checkout side &&
+	test_commit H &&
+	git checkout master &&
+	test_tick &&
+	git merge H &&
+	git tag D &&
+	test_commit E &&
+	git checkout topic &&
+	test_commit F &&
+	test_commit G &&
+	git checkout side &&
+	test_tick &&
+	git merge C &&
+	git tag I &&
+	test_commit J &&
+	test_commit K
+'
+
+test_expect_success 'rebase --onto master...topic' '
+	git reset --hard &&
+	git checkout topic &&
+	git reset --hard G &&
+
+	git rebase --onto master...topic F &&
+	git rev-parse HEAD^1 >actual &&
+	git rev-parse C^0 >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'rebase --onto master...' '
+	git reset --hard &&
+	git checkout topic &&
+	git reset --hard G &&
+
+	git rebase --onto master... F &&
+	git rev-parse HEAD^1 >actual &&
+	git rev-parse C^0 >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'rebase --onto master...side' '
+	git reset --hard &&
+	git checkout side &&
+	git reset --hard K &&
+
+	test_must_fail git rebase --onto master...side J
+'
+
+test_expect_success 'rebase -i --onto master...topic' '
+	git reset --hard &&
+	git checkout topic &&
+	git reset --hard G &&
+	set_fake_editor &&
+	EXPECT_COUNT=1 git rebase -i --onto master...topic F &&
+	git rev-parse HEAD^1 >actual &&
+	git rev-parse C^0 >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'rebase -i --onto master...' '
+	git reset --hard &&
+	git checkout topic &&
+	git reset --hard G &&
+	set_fake_editor &&
+	EXPECT_COUNT=1 git rebase -i --onto master... F &&
+	git rev-parse HEAD^1 >actual &&
+	git rev-parse C^0 >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'rebase -i --onto master...side' '
+	git reset --hard &&
+	git checkout side &&
+	git reset --hard K &&
+
+	test_must_fail git rebase -i --onto master...side J
+'
+
+test_done
diff --git a/t/t3417-rebase-whitespace-fix.sh b/t/t3417-rebase-whitespace-fix.sh
new file mode 100755
index 0000000..1fb3e49
--- /dev/null
+++ b/t/t3417-rebase-whitespace-fix.sh
@@ -0,0 +1,126 @@
+#!/bin/sh
+
+test_description='git rebase --whitespace=fix
+
+This test runs git rebase --whitespace=fix and make sure that it works.
+'
+
+. ./test-lib.sh
+
+# prepare initial revision of "file" with a blank line at the end
+cat >file <<EOF
+a
+b
+c
+
+EOF
+
+# expected contents in "file" after rebase
+cat >expect-first <<EOF
+a
+b
+c
+EOF
+
+# prepare second revision of "file"
+cat >second <<EOF
+a
+b
+c
+
+d
+e
+f
+
+
+
+
+EOF
+
+# expected contents in second revision after rebase
+cat >expect-second <<EOF
+a
+b
+c
+
+d
+e
+f
+EOF
+
+test_expect_success 'blank line at end of file; extend at end of file' '
+	git commit --allow-empty -m "Initial empty commit" &&
+	git add file && git commit -m first &&
+	mv second file &&
+	git add file &&	git commit -m second &&
+	git rebase --whitespace=fix HEAD^^ &&
+	git diff --exit-code HEAD^:file expect-first &&
+	test_cmp file expect-second
+'
+
+# prepare third revision of "file"
+sed -e's/Z//' >third <<EOF
+a
+b
+c
+
+d
+e
+f
+    Z
+ Z
+h
+i
+j
+k
+l
+EOF
+
+sed -e's/ //g' <third >expect-third
+
+test_expect_success 'two blanks line at end of file; extend at end of file' '
+	cp third file && git add file && git commit -m third &&
+	git rebase --whitespace=fix HEAD^^ &&
+	git diff --exit-code HEAD^:file expect-second &&
+	test_cmp file expect-third
+'
+
+test_expect_success 'same, but do not remove trailing spaces' '
+	git config core.whitespace "-blank-at-eol" &&
+	git reset --hard HEAD^ &&
+	cp third file && git add file && git commit -m third &&
+	git rebase --whitespace=fix HEAD^^ &&
+	git diff --exit-code HEAD^:file expect-second &&
+	test_cmp file third
+'
+
+sed -e's/Z//' >beginning <<EOF
+a
+		    Z
+       Z
+EOF
+
+cat >expect-beginning <<EOF
+a
+
+
+1
+2
+3
+4
+5
+EOF
+
+test_expect_success 'at beginning of file' '
+	git config core.whitespace "blank-at-eol" &&
+	cp beginning file &&
+	git commit -m beginning file &&
+	for i in 1 2 3 4 5; do
+		echo $i
+	done >> file &&
+	git commit -m more file	&&
+	git rebase --whitespace=fix HEAD^^ &&
+	test_cmp file expect-beginning
+'
+
+test_done
diff --git a/t/t3418-rebase-continue.sh b/t/t3418-rebase-continue.sh
new file mode 100755
index 0000000..2680375
--- /dev/null
+++ b/t/t3418-rebase-continue.sh
@@ -0,0 +1,98 @@
+#!/bin/sh
+
+test_description='git rebase --continue tests'
+
+. ./test-lib.sh
+
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+set_fake_editor
+
+test_expect_success 'setup' '
+	test_commit "commit-new-file-F1" F1 1 &&
+	test_commit "commit-new-file-F2" F2 2 &&
+
+	git checkout -b topic HEAD^ &&
+	test_commit "commit-new-file-F2-on-topic-branch" F2 22 &&
+
+	git checkout master
+'
+
+test_expect_success 'interactive rebase --continue works with touched file' '
+	rm -fr .git/rebase-* &&
+	git reset --hard &&
+	git checkout master &&
+
+	FAKE_LINES="edit 1" git rebase -i HEAD^ &&
+	test-chmtime =-60 F1 &&
+	git rebase --continue
+'
+
+test_expect_success 'non-interactive rebase --continue works with touched file' '
+	rm -fr .git/rebase-* &&
+	git reset --hard &&
+	git checkout master &&
+
+	test_must_fail git rebase --onto master master topic &&
+	echo "Resolved" >F2 &&
+	git add F2 &&
+	test-chmtime =-60 F1 &&
+	git rebase --continue
+'
+
+test_expect_success 'rebase --continue can not be used with other options' '
+	test_must_fail git rebase -v --continue &&
+	test_must_fail git rebase --continue -v
+'
+
+test_expect_success 'rebase --continue remembers merge strategy and options' '
+	rm -fr .git/rebase-* &&
+	git reset --hard commit-new-file-F2-on-topic-branch &&
+	test_commit "commit-new-file-F3-on-topic-branch" F3 32 &&
+	test_when_finished "rm -fr test-bin funny.was.run" &&
+	mkdir test-bin &&
+	cat >test-bin/git-merge-funny <<-EOF &&
+	#!$SHELL_PATH
+	case "\$1" in --opt) ;; *) exit 2 ;; esac
+	shift &&
+	>funny.was.run &&
+	exec git merge-recursive "\$@"
+	EOF
+	chmod +x test-bin/git-merge-funny &&
+	(
+		PATH=./test-bin:$PATH
+		test_must_fail git rebase -s funny -Xopt master topic
+	) &&
+	test -f funny.was.run &&
+	rm funny.was.run &&
+	echo "Resolved" >F2 &&
+	git add F2 &&
+	(
+		PATH=./test-bin:$PATH
+		git rebase --continue
+	) &&
+	test -f funny.was.run
+'
+
+test_expect_success 'rebase --continue remembers --rerere-autoupdate' '
+	rm -fr .git/rebase-* &&
+	git reset --hard commit-new-file-F3-on-topic-branch &&
+	git checkout master &&
+	test_commit "commit-new-file-F3" F3 3 &&
+	git config rerere.enabled true &&
+	test_must_fail git rebase -m master topic &&
+	echo "Resolved" >F2 &&
+	git add F2 &&
+	test_must_fail git rebase --continue &&
+	echo "Resolved" >F3 &&
+	git add F3 &&
+	git rebase --continue &&
+	git reset --hard topic@{1} &&
+	test_must_fail git rebase -m --rerere-autoupdate master &&
+	test "$(cat F2)" = "Resolved" &&
+	test_must_fail git rebase --continue &&
+	test "$(cat F3)" = "Resolved" &&
+	git rebase --continue
+'
+
+test_done
diff --git a/t/t3419-rebase-patch-id.sh b/t/t3419-rebase-patch-id.sh
new file mode 100755
index 0000000..e70ac10
--- /dev/null
+++ b/t/t3419-rebase-patch-id.sh
@@ -0,0 +1,109 @@
+#!/bin/sh
+
+test_description='git rebase - test patch id computation'
+
+. ./test-lib.sh
+
+test_set_prereq NOT_EXPENSIVE
+test -n "$GIT_PATCHID_TIMING_TESTS" && test_set_prereq EXPENSIVE
+test -x /usr/bin/time && test_set_prereq USR_BIN_TIME
+
+count()
+{
+	i=0
+	while test $i -lt $1
+	do
+		echo "$i"
+		i=$(($i+1))
+	done
+}
+
+scramble()
+{
+	i=0
+	while read x
+	do
+		if test $i -ne 0
+		then
+			echo "$x"
+		fi
+		i=$((($i+1) % 10))
+	done < "$1" > "$1.new"
+	mv -f "$1.new" "$1"
+}
+
+run()
+{
+	echo \$ "$@"
+	/usr/bin/time "$@" >/dev/null
+}
+
+test_expect_success 'setup' '
+	git commit --allow-empty -m initial &&
+	git tag root
+'
+
+do_tests()
+{
+	pr=$1
+	nlines=$2
+
+	test_expect_success $pr "setup: $nlines lines" "
+		rm -f .gitattributes &&
+		git checkout -q -f master &&
+		git reset --hard root &&
+		count $nlines >file &&
+		git add file &&
+		git commit -q -m initial &&
+		git branch -f other &&
+
+		scramble file &&
+		git add file &&
+		git commit -q -m 'change big file' &&
+
+		git checkout -q other &&
+		: >newfile &&
+		git add newfile &&
+		git commit -q -m 'add small file' &&
+
+		git cherry-pick master >/dev/null 2>&1
+	"
+
+	test_debug "
+		run git diff master^\!
+	"
+
+	test_expect_success $pr 'setup attributes' "
+		echo 'file binary' >.gitattributes
+	"
+
+	test_debug "
+		run git format-patch --stdout master &&
+		run git format-patch --stdout --ignore-if-in-upstream master
+	"
+
+	test_expect_success $pr 'detect upstream patch' "
+		git checkout -q master &&
+		scramble file &&
+		git add file &&
+		git commit -q -m 'change big file again' &&
+		git checkout -q other^{} &&
+		git rebase master &&
+		test_must_fail test -n \"\$(git rev-list master...HEAD~)\"
+	"
+
+	test_expect_success $pr 'do not drop patch' "
+		git branch -f squashed master &&
+		git checkout -q -f squashed &&
+		git reset -q --soft HEAD~2 &&
+		git commit -q -m squashed &&
+		git checkout -q other^{} &&
+		test_must_fail git rebase squashed &&
+		rm -rf .git/rebase-apply
+	"
+}
+
+do_tests NOT_EXPENSIVE 500
+do_tests EXPENSIVE 50000
+
+test_done
diff --git a/t/t3500-cherry.sh b/t/t3500-cherry.sh
index dadbbc2..f038f34 100755
--- a/t/t3500-cherry.sh
+++ b/t/t3500-cherry.sh
@@ -17,17 +17,19 @@
     'prepare repository with topic branch, and check cherry finds the 2 patches from there' \
     'echo First > A &&
      git update-index --add A &&
+     test_tick &&
      git commit -m "Add A." &&
 
      git checkout -b my-topic-branch &&
 
      echo Second > B &&
      git update-index --add B &&
+     test_tick &&
      git commit -m "Add B." &&
 
-     sleep 2 &&
      echo AnotherSecond > C &&
      git update-index --add C &&
+     test_tick &&
      git commit -m "Add C." &&
 
      git checkout -f master &&
@@ -35,6 +37,7 @@
 
      echo Third >> A &&
      git update-index A &&
+     test_tick &&
      git commit -m "Modify A." &&
 
      expr "$(echo $(git cherry master my-topic-branch) )" : "+ [^ ]* + .*"
diff --git a/t/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh
index bb4cf00..595d2ff 100755
--- a/t/t3501-revert-cherry-pick.sh
+++ b/t/t3501-revert-cherry-pick.sh
@@ -41,13 +41,32 @@
 	git tag rename2
 '
 
+test_expect_success 'cherry-pick --nonsense' '
+
+	pos=$(git rev-parse HEAD) &&
+	git diff --exit-code HEAD &&
+	test_must_fail git cherry-pick --nonsense 2>msg &&
+	git diff --exit-code HEAD "$pos" &&
+	grep '[Uu]sage:' msg
+'
+
+test_expect_success 'revert --nonsense' '
+
+	pos=$(git rev-parse HEAD) &&
+	git diff --exit-code HEAD &&
+	test_must_fail git revert --nonsense 2>msg &&
+	git diff --exit-code HEAD "$pos" &&
+	grep '[Uu]sage:' msg
+'
+
 test_expect_success 'cherry-pick after renaming branch' '
 
 	git checkout rename2 &&
 	git cherry-pick added &&
 	test $(git rev-parse HEAD^) = $(git rev-parse rename2) &&
 	test -f opos &&
-	grep "Add extra line at the end" opos
+	grep "Add extra line at the end" opos &&
+	git reflog -1 | grep cherry-pick
 
 '
 
@@ -57,16 +76,27 @@
 	git revert added &&
 	test $(git rev-parse HEAD^) = $(git rev-parse rename1) &&
 	test -f spoo &&
-	! grep "Add extra line at the end" spoo
+	! grep "Add extra line at the end" spoo &&
+	git reflog -1 | grep revert
 
 '
 
+test_expect_success 'cherry-pick on stat-dirty working tree' '
+	git clone . copy &&
+	(
+		cd copy &&
+		git checkout initial &&
+		test-chmtime +40 oops &&
+		git cherry-pick added
+	)
+'
+
 test_expect_success 'revert forbidden on dirty working tree' '
 
 	echo content >extra_file &&
 	git add extra_file &&
 	test_must_fail git revert HEAD 2>errors &&
-	grep "Dirty index" errors
+	test_i18ngrep "Your local changes would be overwritten by " errors
 
 '
 
diff --git a/t/t3502-cherry-pick-merge.sh b/t/t3502-cherry-pick-merge.sh
index 0ab52da..e37547f 100755
--- a/t/t3502-cherry-pick-merge.sh
+++ b/t/t3502-cherry-pick-merge.sh
@@ -35,7 +35,7 @@
 
 	git reset --hard &&
 	git checkout a^0 &&
-	test_must_fail git cherry-pick -m 1 b &&
+	test_expect_code 128 git cherry-pick -m 1 b &&
 	git diff --exit-code a --
 
 '
diff --git a/t/t3503-cherry-pick-root.sh b/t/t3503-cherry-pick-root.sh
index b0faa29..e27f39d 100755
--- a/t/t3503-cherry-pick-root.sh
+++ b/t/t3503-cherry-pick-root.sh
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-test_description='test cherry-picking a root commit'
+test_description='test cherry-picking (and reverting) a root commit'
 
 . ./test-lib.sh
 
@@ -16,14 +16,62 @@
 	echo second > file2 &&
 	git add file2 &&
 	test_tick &&
-	git commit -m "second"
+	git commit -m "second" &&
+
+	git symbolic-ref HEAD refs/heads/third &&
+	rm .git/index file2 &&
+	echo third > file3 &&
+	git add file3 &&
+	test_tick &&
+	git commit -m "third"
 
 '
 
 test_expect_success 'cherry-pick a root commit' '
 
+	git checkout second^0 &&
 	git cherry-pick master &&
-	test first = $(cat file1)
+	echo first >expect &&
+	test_cmp expect file1
+
+'
+
+test_expect_success 'revert a root commit' '
+
+	git revert master &&
+	test_path_is_missing file1
+
+'
+
+test_expect_success 'cherry-pick a root commit with an external strategy' '
+
+	git cherry-pick --strategy=resolve master &&
+	echo first >expect &&
+	test_cmp expect file1
+
+'
+
+test_expect_success 'revert a root commit with an external strategy' '
+
+	git revert --strategy=resolve master &&
+	test_path_is_missing file1
+
+'
+
+test_expect_success 'cherry-pick two root commits' '
+
+	echo first >expect.file1 &&
+	echo second >expect.file2 &&
+	echo third >expect.file3 &&
+
+	git checkout second^0 &&
+	git cherry-pick master third &&
+
+	test_cmp expect.file1 file1 &&
+	test_cmp expect.file2 file2 &&
+	test_cmp expect.file3 file3 &&
+	git rev-parse --verify HEAD^^ &&
+	test_must_fail git rev-parse --verify HEAD^^^
 
 '
 
diff --git a/t/t3504-cherry-pick-rerere.sh b/t/t3504-cherry-pick-rerere.sh
index f7b3518..e6a6481 100755
--- a/t/t3504-cherry-pick-rerere.sh
+++ b/t/t3504-cherry-pick-rerere.sh
@@ -23,7 +23,7 @@
 test_expect_success 'fixup' '
 	echo foo-dev >foo &&
 	git add foo && test_tick && git commit -q -m 4 &&
-	git reset --hard HEAD^
+	git reset --hard HEAD^ &&
 	echo foo-dev >expect
 '
 
@@ -33,7 +33,7 @@
 '
 
 test_expect_success 'reconfigure' '
-	git config rerere.enabled false
+	git config rerere.enabled false &&
 	git reset --hard
 '
 
diff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh
index e51e505..5a1340c 100755
--- a/t/t3505-cherry-pick-empty.sh
+++ b/t/t3505-cherry-pick-empty.sh
@@ -13,12 +13,35 @@
 
 	git checkout -b empty-branch &&
 	test_tick &&
+	git commit --allow-empty -m "empty" &&
+
+	echo third >> file1 &&
+	git add file1 &&
+	test_tick &&
+	git commit --allow-empty-message -m "" &&
+
+	git checkout master &&
+	git checkout -b empty-branch2 &&
+	test_tick &&
 	git commit --allow-empty -m "empty"
 
 '
 
 test_expect_success 'cherry-pick an empty commit' '
 	git checkout master && {
+		git cherry-pick empty-branch^
+		test "$?" = 1
+	}
+'
+
+test_expect_success 'index lockfile was removed' '
+
+	test ! -f .git/index.lock
+
+'
+
+test_expect_success 'cherry-pick a commit with an empty message' '
+	git checkout master && {
 		git cherry-pick empty-branch
 		test "$?" = 1
 	}
@@ -30,4 +53,52 @@
 
 '
 
+test_expect_success 'cherry pick an empty non-ff commit without --allow-empty' '
+	git checkout master &&
+	echo fourth >>file2 &&
+	git add file2 &&
+	git commit -m "fourth" &&
+	test_must_fail git cherry-pick empty-branch2
+'
+
+test_expect_success 'cherry pick an empty non-ff commit with --allow-empty' '
+	git checkout master &&
+	git cherry-pick --allow-empty empty-branch2
+'
+
+test_expect_success 'cherry pick with --keep-redundant-commits' '
+	git checkout master &&
+	git cherry-pick --keep-redundant-commits HEAD^
+'
+
+test_expect_success 'cherry-pick a commit that becomes no-op (prep)' '
+	git checkout master &&
+	git branch fork &&
+	echo foo >file2 &&
+	git add file2 &&
+	test_tick &&
+	git commit -m "add file2 on master" &&
+
+	git checkout fork &&
+	echo foo >file2 &&
+	git add file2 &&
+	test_tick &&
+	git commit -m "add file2 on the side"
+'
+
+test_expect_success 'cherry-pick a no-op without --keep-redundant' '
+	git reset --hard &&
+	git checkout fork^0 &&
+	test_must_fail git cherry-pick master
+'
+
+test_expect_success 'cherry-pick a no-op with --keep-redundant' '
+	git reset --hard &&
+	git checkout fork^0 &&
+	git cherry-pick --keep-redundant-commits master &&
+	git show -s --format='%s' >actual &&
+	echo "add file2 on master" >expect &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t3506-cherry-pick-ff.sh b/t/t3506-cherry-pick-ff.sh
new file mode 100755
index 0000000..51ca391
--- /dev/null
+++ b/t/t3506-cherry-pick-ff.sh
@@ -0,0 +1,108 @@
+#!/bin/sh
+
+test_description='test cherry-picking with --ff option'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+	echo first > file1 &&
+	git add file1 &&
+	test_tick &&
+	git commit -m "first" &&
+	git tag first &&
+
+	git checkout -b other &&
+	echo second >> file1 &&
+	git add file1 &&
+	test_tick &&
+	git commit -m "second" &&
+	git tag second
+'
+
+test_expect_success 'cherry-pick using --ff fast forwards' '
+	git checkout master &&
+	git reset --hard first &&
+	test_tick &&
+	git cherry-pick --ff second &&
+	test "$(git rev-parse --verify HEAD)" = "$(git rev-parse --verify second)"
+'
+
+test_expect_success 'cherry-pick not using --ff does not fast forwards' '
+	git checkout master &&
+	git reset --hard first &&
+	test_tick &&
+	git cherry-pick second &&
+	test "$(git rev-parse --verify HEAD)" != "$(git rev-parse --verify second)"
+'
+
+#
+# We setup the following graph:
+#
+#	      B---C
+#	     /   /
+#	first---A
+#
+# (This has been taken from t3502-cherry-pick-merge.sh)
+#
+test_expect_success 'merge setup' '
+	git checkout master &&
+	git reset --hard first &&
+	echo new line >A &&
+	git add A &&
+	test_tick &&
+	git commit -m "add line to A" A &&
+	git tag A &&
+	git checkout -b side first &&
+	echo new line >B &&
+	git add B &&
+	test_tick &&
+	git commit -m "add line to B" B &&
+	git tag B &&
+	git checkout master &&
+	git merge side &&
+	git tag C &&
+	git checkout -b new A
+'
+
+test_expect_success 'cherry-pick a non-merge with --ff and -m should fail' '
+	git reset --hard A -- &&
+	test_must_fail git cherry-pick --ff -m 1 B &&
+	git diff --exit-code A --
+'
+
+test_expect_success 'cherry pick a merge with --ff but without -m should fail' '
+	git reset --hard A -- &&
+	test_must_fail git cherry-pick --ff C &&
+	git diff --exit-code A --
+'
+
+test_expect_success 'cherry pick with --ff a merge (1)' '
+	git reset --hard A -- &&
+	git cherry-pick --ff -m 1 C &&
+	git diff --exit-code C &&
+	test "$(git rev-parse --verify HEAD)" = "$(git rev-parse --verify C)"
+'
+
+test_expect_success 'cherry pick with --ff a merge (2)' '
+	git reset --hard B -- &&
+	git cherry-pick --ff -m 2 C &&
+	git diff --exit-code C &&
+	test "$(git rev-parse --verify HEAD)" = "$(git rev-parse --verify C)"
+'
+
+test_expect_success 'cherry pick a merge relative to nonexistent parent with --ff should fail' '
+	git reset --hard B -- &&
+	test_must_fail git cherry-pick --ff -m 3 C
+'
+
+test_expect_success 'cherry pick a root commit with --ff' '
+	git reset --hard first -- &&
+	git rm file1 &&
+	echo first >file2 &&
+	git add file2 &&
+	git commit --amend -m "file2" &&
+	git cherry-pick --ff first &&
+	test "$(git rev-parse --verify HEAD)" = "1df192cd8bc58a2b275d842cede4d221ad9000d1"
+'
+
+test_done
diff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh
new file mode 100755
index 0000000..0c81b3c
--- /dev/null
+++ b/t/t3507-cherry-pick-conflict.sh
@@ -0,0 +1,343 @@
+#!/bin/sh
+
+test_description='test cherry-pick and revert with conflicts
+
+  -
+  + picked: rewrites foo to c
+  + base: rewrites foo to b
+  + initial: writes foo as a, unrelated as unrelated
+
+'
+
+. ./test-lib.sh
+
+test_cmp_rev () {
+	git rev-parse --verify "$1" >expect.rev &&
+	git rev-parse --verify "$2" >actual.rev &&
+	test_cmp expect.rev actual.rev
+}
+
+pristine_detach () {
+	git checkout -f "$1^0" &&
+	git read-tree -u --reset HEAD &&
+	git clean -d -f -f -q -x
+}
+
+test_expect_success setup '
+
+	echo unrelated >unrelated &&
+	git add unrelated &&
+	test_commit initial foo a &&
+	test_commit base foo b &&
+	test_commit picked foo c &&
+	git config advice.detachedhead false
+
+'
+
+test_expect_success 'failed cherry-pick does not advance HEAD' '
+	pristine_detach initial &&
+
+	head=$(git rev-parse HEAD) &&
+	test_must_fail git cherry-pick picked &&
+	newhead=$(git rev-parse HEAD) &&
+
+	test "$head" = "$newhead"
+'
+
+test_expect_success 'advice from failed cherry-pick' "
+	pristine_detach initial &&
+
+	picked=\$(git rev-parse --short picked) &&
+	cat <<-EOF >expected &&
+	error: could not apply \$picked... picked
+	hint: after resolving the conflicts, mark the corrected paths
+	hint: with 'git add <paths>' or 'git rm <paths>'
+	hint: and commit the result with 'git commit'
+	EOF
+	test_must_fail git cherry-pick picked 2>actual &&
+
+	test_i18ncmp expected actual
+"
+
+test_expect_success 'advice from failed cherry-pick --no-commit' "
+	pristine_detach initial &&
+
+	picked=\$(git rev-parse --short picked) &&
+	cat <<-EOF >expected &&
+	error: could not apply \$picked... picked
+	hint: after resolving the conflicts, mark the corrected paths
+	hint: with 'git add <paths>' or 'git rm <paths>'
+	EOF
+	test_must_fail git cherry-pick --no-commit picked 2>actual &&
+
+	test_i18ncmp expected actual
+"
+
+test_expect_success 'failed cherry-pick sets CHERRY_PICK_HEAD' '
+	pristine_detach initial &&
+	test_must_fail git cherry-pick picked &&
+	test_cmp_rev picked CHERRY_PICK_HEAD
+'
+
+test_expect_success 'successful cherry-pick does not set CHERRY_PICK_HEAD' '
+	pristine_detach initial &&
+	git cherry-pick base &&
+	test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
+'
+
+test_expect_success 'cherry-pick --no-commit does not set CHERRY_PICK_HEAD' '
+	pristine_detach initial &&
+	git cherry-pick --no-commit base &&
+	test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
+'
+
+test_expect_success 'cherry-pick w/dirty tree does not set CHERRY_PICK_HEAD' '
+	pristine_detach initial &&
+	echo foo > foo &&
+	test_must_fail git cherry-pick base &&
+	test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
+'
+
+test_expect_success \
+	'cherry-pick --strategy=resolve w/dirty tree does not set CHERRY_PICK_HEAD' '
+	pristine_detach initial &&
+	echo foo > foo &&
+	test_must_fail git cherry-pick --strategy=resolve base &&
+	test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
+'
+
+test_expect_success 'GIT_CHERRY_PICK_HELP suppresses CHERRY_PICK_HEAD' '
+	pristine_detach initial &&
+	(
+		GIT_CHERRY_PICK_HELP="and then do something else" &&
+		export GIT_CHERRY_PICK_HELP &&
+		test_must_fail git cherry-pick picked
+	) &&
+	test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
+'
+
+test_expect_success 'git reset clears CHERRY_PICK_HEAD' '
+	pristine_detach initial &&
+
+	test_must_fail git cherry-pick picked &&
+	git reset &&
+
+	test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
+'
+
+test_expect_success 'failed commit does not clear CHERRY_PICK_HEAD' '
+	pristine_detach initial &&
+
+	test_must_fail git cherry-pick picked &&
+	test_must_fail git commit &&
+
+	test_cmp_rev picked CHERRY_PICK_HEAD
+'
+
+test_expect_success 'cancelled commit does not clear CHERRY_PICK_HEAD' '
+	pristine_detach initial &&
+
+	test_must_fail git cherry-pick picked &&
+	echo resolved >foo &&
+	git add foo &&
+	git update-index --refresh -q &&
+	test_must_fail git diff-index --exit-code HEAD &&
+	(
+		GIT_EDITOR=false &&
+		export GIT_EDITOR &&
+		test_must_fail git commit
+	) &&
+
+	test_cmp_rev picked CHERRY_PICK_HEAD
+'
+
+test_expect_success 'successful commit clears CHERRY_PICK_HEAD' '
+	pristine_detach initial &&
+
+	test_must_fail git cherry-pick picked &&
+	echo resolved >foo &&
+	git add foo &&
+	git commit &&
+
+	test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
+'
+
+test_expect_success 'failed cherry-pick produces dirty index' '
+	pristine_detach initial &&
+
+	test_must_fail git cherry-pick picked &&
+
+	test_must_fail git update-index --refresh -q &&
+	test_must_fail git diff-index --exit-code HEAD
+'
+
+test_expect_success 'failed cherry-pick registers participants in index' '
+	pristine_detach initial &&
+	{
+		git checkout base -- foo &&
+		git ls-files --stage foo &&
+		git checkout initial -- foo &&
+		git ls-files --stage foo &&
+		git checkout picked -- foo &&
+		git ls-files --stage foo
+	} > stages &&
+	sed "
+		1 s/ 0	/ 1	/
+		2 s/ 0	/ 2	/
+		3 s/ 0	/ 3	/
+	" < stages > expected &&
+	git read-tree -u --reset HEAD &&
+
+	test_must_fail git cherry-pick picked &&
+	git ls-files --stage --unmerged > actual &&
+
+	test_cmp expected actual
+'
+
+test_expect_success 'failed cherry-pick describes conflict in work tree' '
+	pristine_detach initial &&
+	cat <<-EOF > expected &&
+	<<<<<<< HEAD
+	a
+	=======
+	c
+	>>>>>>> objid picked
+	EOF
+
+	test_must_fail git cherry-pick picked &&
+
+	sed "s/[a-f0-9]*\.\.\./objid/" foo > actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'diff3 -m style' '
+	pristine_detach initial &&
+	git config merge.conflictstyle diff3 &&
+	cat <<-EOF > expected &&
+	<<<<<<< HEAD
+	a
+	||||||| parent of objid picked
+	b
+	=======
+	c
+	>>>>>>> objid picked
+	EOF
+
+	test_must_fail git cherry-pick picked &&
+
+	sed "s/[a-f0-9]*\.\.\./objid/" foo > actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'revert also handles conflicts sanely' '
+	git config --unset merge.conflictstyle &&
+	pristine_detach initial &&
+	cat <<-EOF > expected &&
+	<<<<<<< HEAD
+	a
+	=======
+	b
+	>>>>>>> parent of objid picked
+	EOF
+	{
+		git checkout picked -- foo &&
+		git ls-files --stage foo &&
+		git checkout initial -- foo &&
+		git ls-files --stage foo &&
+		git checkout base -- foo &&
+		git ls-files --stage foo
+	} > stages &&
+	sed "
+		1 s/ 0	/ 1	/
+		2 s/ 0	/ 2	/
+		3 s/ 0	/ 3	/
+	" < stages > expected-stages &&
+	git read-tree -u --reset HEAD &&
+
+	head=$(git rev-parse HEAD) &&
+	test_must_fail git revert picked &&
+	newhead=$(git rev-parse HEAD) &&
+	git ls-files --stage --unmerged > actual-stages &&
+
+	test "$head" = "$newhead" &&
+	test_must_fail git update-index --refresh -q &&
+	test_must_fail git diff-index --exit-code HEAD &&
+	test_cmp expected-stages actual-stages &&
+	sed "s/[a-f0-9]*\.\.\./objid/" foo > actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'failed revert sets REVERT_HEAD' '
+	pristine_detach initial &&
+	test_must_fail git revert picked &&
+	test_cmp_rev picked REVERT_HEAD
+'
+
+test_expect_success 'successful revert does not set REVERT_HEAD' '
+	pristine_detach base &&
+	git revert base &&
+	test_must_fail git rev-parse --verify CHERRY_PICK_HEAD &&
+	test_must_fail git rev-parse --verify REVERT_HEAD
+'
+
+test_expect_success 'revert --no-commit sets REVERT_HEAD' '
+	pristine_detach base &&
+	git revert --no-commit base &&
+	test_must_fail git rev-parse --verify CHERRY_PICK_HEAD &&
+	test_cmp_rev base REVERT_HEAD
+'
+
+test_expect_success 'revert w/dirty tree does not set REVERT_HEAD' '
+	pristine_detach base &&
+	echo foo > foo &&
+	test_must_fail git revert base &&
+	test_must_fail git rev-parse --verify CHERRY_PICK_HEAD &&
+	test_must_fail git rev-parse --verify REVERT_HEAD
+'
+
+test_expect_success 'GIT_CHERRY_PICK_HELP does not suppress REVERT_HEAD' '
+	pristine_detach initial &&
+	(
+		GIT_CHERRY_PICK_HELP="and then do something else" &&
+		GIT_REVERT_HELP="and then do something else, again" &&
+		export GIT_CHERRY_PICK_HELP GIT_REVERT_HELP &&
+		test_must_fail git revert picked
+	) &&
+	test_must_fail git rev-parse --verify CHERRY_PICK_HEAD &&
+	test_cmp_rev picked REVERT_HEAD
+'
+
+test_expect_success 'git reset clears REVERT_HEAD' '
+	pristine_detach initial &&
+	test_must_fail git revert picked &&
+	git reset &&
+	test_must_fail git rev-parse --verify REVERT_HEAD
+'
+
+test_expect_success 'failed commit does not clear REVERT_HEAD' '
+	pristine_detach initial &&
+	test_must_fail git revert picked &&
+	test_must_fail git commit &&
+	test_cmp_rev picked REVERT_HEAD
+'
+
+test_expect_success 'revert conflict, diff3 -m style' '
+	pristine_detach initial &&
+	git config merge.conflictstyle diff3 &&
+	cat <<-EOF > expected &&
+	<<<<<<< HEAD
+	a
+	||||||| objid picked
+	c
+	=======
+	b
+	>>>>>>> parent of objid picked
+	EOF
+
+	test_must_fail git revert picked &&
+
+	sed "s/[a-f0-9]*\.\.\./objid/" foo > actual &&
+	test_cmp expected actual
+'
+
+test_done
diff --git a/t/t3508-cherry-pick-many-commits.sh b/t/t3508-cherry-pick-many-commits.sh
new file mode 100755
index 0000000..75f7ff4
--- /dev/null
+++ b/t/t3508-cherry-pick-many-commits.sh
@@ -0,0 +1,173 @@
+#!/bin/sh
+
+test_description='test cherry-picking many commits'
+
+. ./test-lib.sh
+
+check_head_differs_from() {
+	head=$(git rev-parse --verify HEAD) &&
+	arg=$(git rev-parse --verify "$1") &&
+	test "$head" != "$arg"
+}
+
+check_head_equals() {
+	head=$(git rev-parse --verify HEAD) &&
+	arg=$(git rev-parse --verify "$1") &&
+	test "$head" = "$arg"
+}
+
+test_expect_success setup '
+	echo first > file1 &&
+	git add file1 &&
+	test_tick &&
+	git commit -m "first" &&
+	git tag first &&
+
+	git checkout -b other &&
+	for val in second third fourth
+	do
+		echo $val >> file1 &&
+		git add file1 &&
+		test_tick &&
+		git commit -m "$val" &&
+		git tag $val
+	done
+'
+
+test_expect_success 'cherry-pick first..fourth works' '
+	git checkout -f master &&
+	git reset --hard first &&
+	test_tick &&
+	git cherry-pick first..fourth &&
+	git diff --quiet other &&
+	git diff --quiet HEAD other &&
+	check_head_differs_from fourth
+'
+
+test_expect_success 'output to keep user entertained during multi-pick' '
+	cat <<-\EOF >expected &&
+	[master OBJID] second
+	 Author: A U Thor <author@example.com>
+	 1 file changed, 1 insertion(+)
+	[master OBJID] third
+	 Author: A U Thor <author@example.com>
+	 1 file changed, 1 insertion(+)
+	[master OBJID] fourth
+	 Author: A U Thor <author@example.com>
+	 1 file changed, 1 insertion(+)
+	EOF
+
+	git checkout -f master &&
+	git reset --hard first &&
+	test_tick &&
+	git cherry-pick first..fourth >actual &&
+	sed -e "s/$_x05[0-9a-f][0-9a-f]/OBJID/" <actual >actual.fuzzy &&
+	test_line_count -ge 3 actual.fuzzy &&
+	test_i18ncmp expected actual.fuzzy
+'
+
+test_expect_success 'cherry-pick --strategy resolve first..fourth works' '
+	git checkout -f master &&
+	git reset --hard first &&
+	test_tick &&
+	git cherry-pick --strategy resolve first..fourth &&
+	git diff --quiet other &&
+	git diff --quiet HEAD other &&
+	check_head_differs_from fourth
+'
+
+test_expect_success 'output during multi-pick indicates merge strategy' '
+	cat <<-\EOF >expected &&
+	Trying simple merge.
+	[master OBJID] second
+	 Author: A U Thor <author@example.com>
+	 1 file changed, 1 insertion(+)
+	Trying simple merge.
+	[master OBJID] third
+	 Author: A U Thor <author@example.com>
+	 1 file changed, 1 insertion(+)
+	Trying simple merge.
+	[master OBJID] fourth
+	 Author: A U Thor <author@example.com>
+	 1 file changed, 1 insertion(+)
+	EOF
+
+	git checkout -f master &&
+	git reset --hard first &&
+	test_tick &&
+	git cherry-pick --strategy resolve first..fourth >actual &&
+	sed -e "s/$_x05[0-9a-f][0-9a-f]/OBJID/" <actual >actual.fuzzy &&
+	test_i18ncmp expected actual.fuzzy
+'
+
+test_expect_success 'cherry-pick --ff first..fourth works' '
+	git checkout -f master &&
+	git reset --hard first &&
+	test_tick &&
+	git cherry-pick --ff first..fourth &&
+	git diff --quiet other &&
+	git diff --quiet HEAD other &&
+	check_head_equals fourth
+'
+
+test_expect_success 'cherry-pick -n first..fourth works' '
+	git checkout -f master &&
+	git reset --hard first &&
+	test_tick &&
+	git cherry-pick -n first..fourth &&
+	git diff --quiet other &&
+	git diff --cached --quiet other &&
+	git diff --quiet HEAD first
+'
+
+test_expect_success 'revert first..fourth works' '
+	git checkout -f master &&
+	git reset --hard fourth &&
+	test_tick &&
+	git revert first..fourth &&
+	git diff --quiet first &&
+	git diff --cached --quiet first &&
+	git diff --quiet HEAD first
+'
+
+test_expect_success 'revert ^first fourth works' '
+	git checkout -f master &&
+	git reset --hard fourth &&
+	test_tick &&
+	git revert ^first fourth &&
+	git diff --quiet first &&
+	git diff --cached --quiet first &&
+	git diff --quiet HEAD first
+'
+
+test_expect_success 'revert fourth fourth~1 fourth~2 works' '
+	git checkout -f master &&
+	git reset --hard fourth &&
+	test_tick &&
+	git revert fourth fourth~1 fourth~2 &&
+	git diff --quiet first &&
+	git diff --cached --quiet first &&
+	git diff --quiet HEAD first
+'
+
+test_expect_success 'cherry-pick -3 fourth works' '
+	git checkout -f master &&
+	git reset --hard first &&
+	test_tick &&
+	git cherry-pick -3 fourth &&
+	git diff --quiet other &&
+	git diff --quiet HEAD other &&
+	check_head_differs_from fourth
+'
+
+test_expect_success 'cherry-pick --stdin works' '
+	git checkout -f master &&
+	git reset --hard first &&
+	test_tick &&
+	git rev-list --reverse first..fourth | git cherry-pick --stdin &&
+	git diff --quiet other &&
+	git diff --quiet HEAD other &&
+	check_head_differs_from fourth
+'
+
+test_done
diff --git a/t/t3509-cherry-pick-merge-df.sh b/t/t3509-cherry-pick-merge-df.sh
new file mode 100755
index 0000000..df921d1
--- /dev/null
+++ b/t/t3509-cherry-pick-merge-df.sh
@@ -0,0 +1,103 @@
+#!/bin/sh
+
+test_description='Test cherry-pick with directory/file conflicts'
+. ./test-lib.sh
+
+test_expect_success 'Initialize repository' '
+	mkdir a &&
+	>a/f &&
+	git add a &&
+	git commit -m a
+'
+
+test_expect_success SYMLINKS 'Setup rename across paths each below D/F conflicts' '
+	mkdir b &&
+	ln -s ../a b/a &&
+	git add b &&
+	git commit -m b &&
+
+	git checkout -b branch &&
+	rm b/a &&
+	mv a b/a &&
+	ln -s b/a a &&
+	git add . &&
+	git commit -m swap &&
+
+	>f1 &&
+	git add f1 &&
+	git commit -m f1
+'
+
+test_expect_success SYMLINKS 'Cherry-pick succeeds with rename across D/F conflicts' '
+	git reset --hard &&
+	git checkout master^0 &&
+	git cherry-pick branch
+'
+
+test_expect_success 'Setup rename with file on one side matching directory name on other' '
+	git checkout --orphan nick-testcase &&
+	git rm -rf . &&
+
+	>empty &&
+	git add empty &&
+	git commit -m "Empty file" &&
+
+	git checkout -b simple &&
+	mv empty file &&
+	mkdir empty &&
+	mv file empty &&
+	git add empty/file &&
+	git commit -m "Empty file under empty dir" &&
+
+	echo content >newfile &&
+	git add newfile &&
+	git commit -m "New file"
+'
+
+test_expect_success 'Cherry-pick succeeds with was_a_dir/file -> was_a_dir (resolve)' '
+	git reset --hard &&
+	git checkout -q nick-testcase^0 &&
+	git cherry-pick --strategy=resolve simple
+'
+
+test_expect_success 'Cherry-pick succeeds with was_a_dir/file -> was_a_dir (recursive)' '
+	git reset --hard &&
+	git checkout -q nick-testcase^0 &&
+	git cherry-pick --strategy=recursive simple
+'
+
+test_expect_success 'Setup rename with file on one side matching different dirname on other' '
+	git reset --hard &&
+	git checkout --orphan mergeme &&
+	git rm -rf . &&
+
+	mkdir sub &&
+	mkdir othersub &&
+	echo content > sub/file &&
+	echo foo > othersub/whatever &&
+	git add -A &&
+	git commit -m "Common commmit" &&
+
+	git rm -rf othersub &&
+	git mv sub/file othersub &&
+	git commit -m "Commit to merge" &&
+
+	git checkout -b newhead mergeme~1 &&
+	>independent-change &&
+	git add independent-change &&
+	git commit -m "Completely unrelated change"
+'
+
+test_expect_success 'Cherry-pick with rename to different D/F conflict succeeds (resolve)' '
+	git reset --hard &&
+	git checkout -q newhead^0 &&
+	git cherry-pick --strategy=resolve mergeme
+'
+
+test_expect_success 'Cherry-pick with rename to different D/F conflict succeeds (recursive)' '
+	git reset --hard &&
+	git checkout -q newhead^0 &&
+	git cherry-pick --strategy=recursive mergeme
+'
+
+test_done
diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh
new file mode 100755
index 0000000..f4e6450
--- /dev/null
+++ b/t/t3510-cherry-pick-sequence.sh
@@ -0,0 +1,520 @@
+#!/bin/sh
+
+test_description='Test cherry-pick continuation features
+
+ +  conflicting: rewrites unrelated to conflicting
+  + yetanotherpick: rewrites foo to e
+  + anotherpick: rewrites foo to d
+  + picked: rewrites foo to c
+  + unrelatedpick: rewrites unrelated to reallyunrelated
+  + base: rewrites foo to b
+  + initial: writes foo as a, unrelated as unrelated
+
+'
+
+. ./test-lib.sh
+
+# Repeat first match 10 times
+_r10='\1\1\1\1\1\1\1\1\1\1'
+
+pristine_detach () {
+	git cherry-pick --quit &&
+	git checkout -f "$1^0" &&
+	git read-tree -u --reset HEAD &&
+	git clean -d -f -f -q -x
+}
+
+test_cmp_rev () {
+	git rev-parse --verify "$1" >expect.rev &&
+	git rev-parse --verify "$2" >actual.rev &&
+	test_cmp expect.rev actual.rev
+}
+
+test_expect_success setup '
+	git config advice.detachedhead false &&
+	echo unrelated >unrelated &&
+	git add unrelated &&
+	test_commit initial foo a &&
+	test_commit base foo b &&
+	test_commit unrelatedpick unrelated reallyunrelated &&
+	test_commit picked foo c &&
+	test_commit anotherpick foo d &&
+	test_commit yetanotherpick foo e &&
+	pristine_detach initial &&
+	test_commit conflicting unrelated
+'
+
+test_expect_success 'cherry-pick persists data on failure' '
+	pristine_detach initial &&
+	test_expect_code 1 git cherry-pick -s base..anotherpick &&
+	test_path_is_dir .git/sequencer &&
+	test_path_is_file .git/sequencer/head &&
+	test_path_is_file .git/sequencer/todo &&
+	test_path_is_file .git/sequencer/opts
+'
+
+test_expect_success 'cherry-pick mid-cherry-pick-sequence' '
+	pristine_detach initial &&
+	test_must_fail git cherry-pick base..anotherpick &&
+	test_cmp_rev picked CHERRY_PICK_HEAD &&
+	# "oops, I forgot that these patches rely on the change from base"
+	git checkout HEAD foo &&
+	git cherry-pick base &&
+	git cherry-pick picked &&
+	git cherry-pick --continue &&
+	git diff --exit-code anotherpick
+'
+
+test_expect_success 'cherry-pick persists opts correctly' '
+	pristine_detach initial &&
+	test_expect_code 128 git cherry-pick -s -m 1 --strategy=recursive -X patience -X ours initial..anotherpick &&
+	test_path_is_dir .git/sequencer &&
+	test_path_is_file .git/sequencer/head &&
+	test_path_is_file .git/sequencer/todo &&
+	test_path_is_file .git/sequencer/opts &&
+	echo "true" >expect &&
+	git config --file=.git/sequencer/opts --get-all options.signoff >actual &&
+	test_cmp expect actual &&
+	echo "1" >expect &&
+	git config --file=.git/sequencer/opts --get-all options.mainline >actual &&
+	test_cmp expect actual &&
+	echo "recursive" >expect &&
+	git config --file=.git/sequencer/opts --get-all options.strategy >actual &&
+	test_cmp expect actual &&
+	cat >expect <<-\EOF &&
+	patience
+	ours
+	EOF
+	git config --file=.git/sequencer/opts --get-all options.strategy-option >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'cherry-pick cleans up sequencer state upon success' '
+	pristine_detach initial &&
+	git cherry-pick initial..picked &&
+	test_path_is_missing .git/sequencer
+'
+
+test_expect_success '--quit does not complain when no cherry-pick is in progress' '
+	pristine_detach initial &&
+	git cherry-pick --quit
+'
+
+test_expect_success '--abort requires cherry-pick in progress' '
+	pristine_detach initial &&
+	test_must_fail git cherry-pick --abort
+'
+
+test_expect_success '--quit cleans up sequencer state' '
+	pristine_detach initial &&
+	test_expect_code 1 git cherry-pick base..picked &&
+	git cherry-pick --quit &&
+	test_path_is_missing .git/sequencer
+'
+
+test_expect_success '--quit keeps HEAD and conflicted index intact' '
+	pristine_detach initial &&
+	cat >expect <<-\EOF &&
+	OBJID
+	:100644 100644 OBJID OBJID M	unrelated
+	OBJID
+	:000000 100644 OBJID OBJID A	foo
+	:000000 100644 OBJID OBJID A	unrelated
+	EOF
+	test_expect_code 1 git cherry-pick base..picked &&
+	git cherry-pick --quit &&
+	test_path_is_missing .git/sequencer &&
+	test_must_fail git update-index --refresh &&
+	{
+		git rev-list HEAD |
+		git diff-tree --root --stdin |
+		sed "s/$_x40/OBJID/g"
+	} >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '--abort to cancel multiple cherry-pick' '
+	pristine_detach initial &&
+	test_expect_code 1 git cherry-pick base..anotherpick &&
+	git cherry-pick --abort &&
+	test_path_is_missing .git/sequencer &&
+	test_cmp_rev initial HEAD &&
+	git update-index --refresh &&
+	git diff-index --exit-code HEAD
+'
+
+test_expect_success '--abort to cancel single cherry-pick' '
+	pristine_detach initial &&
+	test_expect_code 1 git cherry-pick picked &&
+	git cherry-pick --abort &&
+	test_path_is_missing .git/sequencer &&
+	test_cmp_rev initial HEAD &&
+	git update-index --refresh &&
+	git diff-index --exit-code HEAD
+'
+
+test_expect_success 'cherry-pick --abort to cancel multiple revert' '
+	pristine_detach anotherpick &&
+	test_expect_code 1 git revert base..picked &&
+	git cherry-pick --abort &&
+	test_path_is_missing .git/sequencer &&
+	test_cmp_rev anotherpick HEAD &&
+	git update-index --refresh &&
+	git diff-index --exit-code HEAD
+'
+
+test_expect_success 'revert --abort works, too' '
+	pristine_detach anotherpick &&
+	test_expect_code 1 git revert base..picked &&
+	git revert --abort &&
+	test_path_is_missing .git/sequencer &&
+	test_cmp_rev anotherpick HEAD
+'
+
+test_expect_success '--abort to cancel single revert' '
+	pristine_detach anotherpick &&
+	test_expect_code 1 git revert picked &&
+	git revert --abort &&
+	test_path_is_missing .git/sequencer &&
+	test_cmp_rev anotherpick HEAD &&
+	git update-index --refresh &&
+	git diff-index --exit-code HEAD
+'
+
+test_expect_success '--abort keeps unrelated change, easy case' '
+	pristine_detach unrelatedpick &&
+	echo changed >expect &&
+	test_expect_code 1 git cherry-pick picked..yetanotherpick &&
+	echo changed >unrelated &&
+	git cherry-pick --abort &&
+	test_cmp expect unrelated
+'
+
+test_expect_success '--abort refuses to clobber unrelated change, harder case' '
+	pristine_detach initial &&
+	echo changed >expect &&
+	test_expect_code 1 git cherry-pick base..anotherpick &&
+	echo changed >unrelated &&
+	test_must_fail git cherry-pick --abort &&
+	test_cmp expect unrelated &&
+	git rev-list HEAD >log &&
+	test_line_count = 2 log &&
+	test_must_fail git update-index --refresh &&
+
+	git checkout unrelated &&
+	git cherry-pick --abort &&
+	test_cmp_rev initial HEAD
+'
+
+test_expect_success 'cherry-pick still writes sequencer state when one commit is left' '
+	pristine_detach initial &&
+	test_expect_code 1 git cherry-pick base..picked &&
+	test_path_is_dir .git/sequencer &&
+	echo "resolved" >foo &&
+	git add foo &&
+	git commit &&
+	{
+		git rev-list HEAD |
+		git diff-tree --root --stdin |
+		sed "s/$_x40/OBJID/g"
+	} >actual &&
+	cat >expect <<-\EOF &&
+	OBJID
+	:100644 100644 OBJID OBJID M	foo
+	OBJID
+	:100644 100644 OBJID OBJID M	unrelated
+	OBJID
+	:000000 100644 OBJID OBJID A	foo
+	:000000 100644 OBJID OBJID A	unrelated
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success '--abort after last commit in sequence' '
+	pristine_detach initial &&
+	test_expect_code 1 git cherry-pick base..picked &&
+	git cherry-pick --abort &&
+	test_path_is_missing .git/sequencer &&
+	test_cmp_rev initial HEAD &&
+	git update-index --refresh &&
+	git diff-index --exit-code HEAD
+'
+
+test_expect_success 'cherry-pick does not implicitly stomp an existing operation' '
+	pristine_detach initial &&
+	test_expect_code 1 git cherry-pick base..anotherpick &&
+	test-chmtime -v +0 .git/sequencer >expect &&
+	test_expect_code 128 git cherry-pick unrelatedpick &&
+	test-chmtime -v +0 .git/sequencer >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '--continue complains when no cherry-pick is in progress' '
+	pristine_detach initial &&
+	test_expect_code 128 git cherry-pick --continue
+'
+
+test_expect_success '--continue complains when there are unresolved conflicts' '
+	pristine_detach initial &&
+	test_expect_code 1 git cherry-pick base..anotherpick &&
+	test_expect_code 128 git cherry-pick --continue
+'
+
+test_expect_success '--continue of single cherry-pick' '
+	pristine_detach initial &&
+	echo c >expect &&
+	test_must_fail git cherry-pick picked &&
+	echo c >foo &&
+	git add foo &&
+	git cherry-pick --continue &&
+
+	test_cmp expect foo &&
+	test_cmp_rev initial HEAD^ &&
+	git diff --exit-code HEAD &&
+	test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
+'
+
+test_expect_success '--continue of single revert' '
+	pristine_detach initial &&
+	echo resolved >expect &&
+	echo "Revert \"picked\"" >expect.msg &&
+	test_must_fail git revert picked &&
+	echo resolved >foo &&
+	git add foo &&
+	git cherry-pick --continue &&
+
+	git diff --exit-code HEAD &&
+	test_cmp expect foo &&
+	test_cmp_rev initial HEAD^ &&
+	git diff-tree -s --pretty=tformat:%s HEAD >msg &&
+	test_cmp expect.msg msg &&
+	test_must_fail git rev-parse --verify CHERRY_PICK_HEAD &&
+	test_must_fail git rev-parse --verify REVERT_HEAD
+'
+
+test_expect_success '--continue after resolving conflicts' '
+	pristine_detach initial &&
+	echo d >expect &&
+	cat >expect.log <<-\EOF &&
+	OBJID
+	:100644 100644 OBJID OBJID M	foo
+	OBJID
+	:100644 100644 OBJID OBJID M	foo
+	OBJID
+	:100644 100644 OBJID OBJID M	unrelated
+	OBJID
+	:000000 100644 OBJID OBJID A	foo
+	:000000 100644 OBJID OBJID A	unrelated
+	EOF
+	test_must_fail git cherry-pick base..anotherpick &&
+	echo c >foo &&
+	git add foo &&
+	git cherry-pick --continue &&
+	{
+		git rev-list HEAD |
+		git diff-tree --root --stdin |
+		sed "s/$_x40/OBJID/g"
+	} >actual.log &&
+	test_cmp expect foo &&
+	test_cmp expect.log actual.log
+'
+
+test_expect_success '--continue after resolving conflicts and committing' '
+	pristine_detach initial &&
+	test_expect_code 1 git cherry-pick base..anotherpick &&
+	echo "c" >foo &&
+	git add foo &&
+	git commit &&
+	git cherry-pick --continue &&
+	test_path_is_missing .git/sequencer &&
+	{
+		git rev-list HEAD |
+		git diff-tree --root --stdin |
+		sed "s/$_x40/OBJID/g"
+	} >actual &&
+	cat >expect <<-\EOF &&
+	OBJID
+	:100644 100644 OBJID OBJID M	foo
+	OBJID
+	:100644 100644 OBJID OBJID M	foo
+	OBJID
+	:100644 100644 OBJID OBJID M	unrelated
+	OBJID
+	:000000 100644 OBJID OBJID A	foo
+	:000000 100644 OBJID OBJID A	unrelated
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success '--continue asks for help after resolving patch to nil' '
+	pristine_detach conflicting &&
+	test_must_fail git cherry-pick initial..picked &&
+
+	test_cmp_rev unrelatedpick CHERRY_PICK_HEAD &&
+	git checkout HEAD -- unrelated &&
+	test_must_fail git cherry-pick --continue 2>msg &&
+	test_i18ngrep "The previous cherry-pick is now empty" msg
+'
+
+test_expect_success 'follow advice and skip nil patch' '
+	pristine_detach conflicting &&
+	test_must_fail git cherry-pick initial..picked &&
+
+	git checkout HEAD -- unrelated &&
+	test_must_fail git cherry-pick --continue &&
+	git reset &&
+	git cherry-pick --continue &&
+
+	git rev-list initial..HEAD >commits &&
+	test_line_count = 3 commits
+'
+
+test_expect_success '--continue respects opts' '
+	pristine_detach initial &&
+	test_expect_code 1 git cherry-pick -x base..anotherpick &&
+	echo "c" >foo &&
+	git add foo &&
+	git commit &&
+	git cherry-pick --continue &&
+	test_path_is_missing .git/sequencer &&
+	git cat-file commit HEAD >anotherpick_msg &&
+	git cat-file commit HEAD~1 >picked_msg &&
+	git cat-file commit HEAD~2 >unrelatedpick_msg &&
+	git cat-file commit HEAD~3 >initial_msg &&
+	test_must_fail grep "cherry picked from" initial_msg &&
+	grep "cherry picked from" unrelatedpick_msg &&
+	grep "cherry picked from" picked_msg &&
+	grep "cherry picked from" anotherpick_msg
+'
+
+test_expect_success '--continue of single-pick respects -x' '
+	pristine_detach initial &&
+	test_must_fail git cherry-pick -x picked &&
+	echo c >foo &&
+	git add foo &&
+	git cherry-pick --continue &&
+	test_path_is_missing .git/sequencer &&
+	git cat-file commit HEAD >msg &&
+	grep "cherry picked from" msg
+'
+
+test_expect_success '--continue respects -x in first commit in multi-pick' '
+	pristine_detach initial &&
+	test_must_fail git cherry-pick -x picked anotherpick &&
+	echo c >foo &&
+	git add foo &&
+	git cherry-pick --continue &&
+	test_path_is_missing .git/sequencer &&
+	git cat-file commit HEAD^ >msg &&
+	picked=$(git rev-parse --verify picked) &&
+	grep "cherry picked from.*$picked" msg
+'
+
+test_expect_success '--signoff is not automatically propagated to resolved conflict' '
+	pristine_detach initial &&
+	test_expect_code 1 git cherry-pick --signoff base..anotherpick &&
+	echo "c" >foo &&
+	git add foo &&
+	git commit &&
+	git cherry-pick --continue &&
+	test_path_is_missing .git/sequencer &&
+	git cat-file commit HEAD >anotherpick_msg &&
+	git cat-file commit HEAD~1 >picked_msg &&
+	git cat-file commit HEAD~2 >unrelatedpick_msg &&
+	git cat-file commit HEAD~3 >initial_msg &&
+	test_must_fail grep "Signed-off-by:" initial_msg &&
+	grep "Signed-off-by:" unrelatedpick_msg &&
+	test_must_fail grep "Signed-off-by:" picked_msg &&
+	grep "Signed-off-by:" anotherpick_msg
+'
+
+test_expect_success '--signoff dropped for implicit commit of resolution, multi-pick case' '
+	pristine_detach initial &&
+	test_must_fail git cherry-pick -s picked anotherpick &&
+	echo c >foo &&
+	git add foo &&
+	git cherry-pick --continue &&
+
+	git diff --exit-code HEAD &&
+	test_cmp_rev initial HEAD^^ &&
+	git cat-file commit HEAD^ >msg &&
+	! grep Signed-off-by: msg
+'
+
+test_expect_success 'sign-off needs to be reaffirmed after conflict resolution, single-pick case' '
+	pristine_detach initial &&
+	test_must_fail git cherry-pick -s picked &&
+	echo c >foo &&
+	git add foo &&
+	git cherry-pick --continue &&
+
+	git diff --exit-code HEAD &&
+	test_cmp_rev initial HEAD^ &&
+	git cat-file commit HEAD >msg &&
+	! grep Signed-off-by: msg
+'
+
+test_expect_success 'malformed instruction sheet 1' '
+	pristine_detach initial &&
+	test_expect_code 1 git cherry-pick base..anotherpick &&
+	echo "resolved" >foo &&
+	git add foo &&
+	git commit &&
+	sed "s/pick /pick/" .git/sequencer/todo >new_sheet &&
+	cp new_sheet .git/sequencer/todo &&
+	test_expect_code 128 git cherry-pick --continue
+'
+
+test_expect_success 'malformed instruction sheet 2' '
+	pristine_detach initial &&
+	test_expect_code 1 git cherry-pick base..anotherpick &&
+	echo "resolved" >foo &&
+	git add foo &&
+	git commit &&
+	sed "s/pick/revert/" .git/sequencer/todo >new_sheet &&
+	cp new_sheet .git/sequencer/todo &&
+	test_expect_code 128 git cherry-pick --continue
+'
+
+test_expect_success 'empty commit set' '
+	pristine_detach initial &&
+	test_expect_code 128 git cherry-pick base..base
+'
+
+test_expect_success 'malformed instruction sheet 3' '
+	pristine_detach initial &&
+	test_expect_code 1 git cherry-pick base..anotherpick &&
+	echo "resolved" >foo &&
+	git add foo &&
+	git commit &&
+	sed "s/pick \([0-9a-f]*\)/pick $_r10/" .git/sequencer/todo >new_sheet &&
+	cp new_sheet .git/sequencer/todo &&
+	test_expect_code 128 git cherry-pick --continue
+'
+
+test_expect_success 'instruction sheet, fat-fingers version' '
+	pristine_detach initial &&
+	test_expect_code 1 git cherry-pick base..anotherpick &&
+	echo "c" >foo &&
+	git add foo &&
+	git commit &&
+	sed "s/pick \([0-9a-f]*\)/pick 	 \1 	/" .git/sequencer/todo >new_sheet &&
+	cp new_sheet .git/sequencer/todo &&
+	git cherry-pick --continue
+'
+
+test_expect_success 'commit descriptions in insn sheet are optional' '
+	pristine_detach initial &&
+	test_expect_code 1 git cherry-pick base..anotherpick &&
+	echo "c" >foo &&
+	git add foo &&
+	git commit &&
+	cut -d" " -f1,2 .git/sequencer/todo >new_sheet &&
+	cp new_sheet .git/sequencer/todo &&
+	git cherry-pick --continue &&
+	test_path_is_missing .git/sequencer &&
+	git rev-list HEAD >commits &&
+	test_line_count = 4 commits
+'
+
+test_done
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index 76b1bb4..9fd28bcf 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -28,22 +28,6 @@
      git commit -m 'add files with tabs and newlines'
 "
 
-# Determine rm behavior
-# Later we will try removing an unremovable path to make sure
-# git rm barfs, but if the test is run as root that cannot be
-# arranged.
-: >test-file
-chmod a-w .
-rm -f test-file 2>/dev/null
-if test -f test-file
-then
-	test_set_prereq RO_DIR
-else
-	say 'skipping removal failure test (perhaps running as root?)'
-fi
-chmod 775 .
-rm -f test-file
-
 test_expect_success \
     'Pre-check that foo exists and is in index before git rm foo' \
     '[ -f foo ] && git ls-files --error-unmatch foo'
@@ -112,7 +96,7 @@
     "git rm -f 'space embedded' 'tab	embedded' 'newline
 embedded'"
 
-test_expect_success RO_DIR 'Test that "git rm -f" fails if its rm fails' '
+test_expect_success SANITY 'Test that "git rm -f" fails if its rm fails' '
 	chmod a-w . &&
 	test_must_fail git rm -f baz &&
 	chmod 775 .
@@ -256,11 +240,10 @@
 
 test_expect_success 'choking "git rm" should not let it die with cruft' '
 	git reset -q --hard &&
-	H=0000000000000000000000000000000000000000 &&
 	i=0 &&
 	while test $i -lt 12000
 	do
-	    echo "100644 $H 0	some-file-$i"
+	    echo "100644 $_z40 0	some-file-$i"
 	    i=$(( $i + 1 ))
 	done | git update-index --index-info &&
 	git rm -n "some-file-*" | :;
@@ -271,4 +254,12 @@
 	test "$status" != 0
 '
 
+test_expect_success 'rm removes subdirectories recursively' '
+	mkdir -p dir/subdir/subsubdir &&
+	echo content >dir/subdir/subsubdir/file &&
+	git add dir/subdir/subsubdir/file &&
+	git rm -f dir/subdir/subsubdir/file &&
+	! test -d dir
+'
+
 test_done
diff --git a/t/t3700-add.sh b/t/t3700-add.sh
index 85eb0fb..874b3a6 100755
--- a/t/t3700-add.sh
+++ b/t/t3700-add.sh
@@ -26,7 +26,7 @@
 	 chmod 755 xfoo1 &&
 	 git add xfoo1 &&
 	 case "`git ls-files --stage xfoo1`" in
-	 100644" "*xfoo1) echo ok;;
+	 100644" "*xfoo1) echo pass;;
 	 *) echo fail; git ls-files --stage xfoo1; (exit 1);;
 	 esac'
 
@@ -35,7 +35,7 @@
 	ln -s foo xfoo1 &&
 	git add xfoo1 &&
 	case "`git ls-files --stage xfoo1`" in
-	120000" "*xfoo1) echo ok;;
+	120000" "*xfoo1) echo pass;;
 	*) echo fail; git ls-files --stage xfoo1; (exit 1);;
 	esac
 '
@@ -47,7 +47,7 @@
 	 chmod 755 xfoo2 &&
 	 git update-index --add xfoo2 &&
 	 case "`git ls-files --stage xfoo2`" in
-	 100644" "*xfoo2) echo ok;;
+	 100644" "*xfoo2) echo pass;;
 	 *) echo fail; git ls-files --stage xfoo2; (exit 1);;
 	 esac'
 
@@ -56,7 +56,7 @@
 	ln -s foo xfoo2 &&
 	git update-index --add xfoo2 &&
 	case "`git ls-files --stage xfoo2`" in
-	120000" "*xfoo2) echo ok;;
+	120000" "*xfoo2) echo pass;;
 	*) echo fail; git ls-files --stage xfoo2; (exit 1);;
 	esac
 '
@@ -67,7 +67,7 @@
 	 ln -s xfoo2 xfoo3 &&
 	 git update-index --add xfoo3 &&
 	 case "`git ls-files --stage xfoo3`" in
-	 120000" "*xfoo3) echo ok;;
+	 120000" "*xfoo3) echo pass;;
 	 *) echo fail; git ls-files --stage xfoo3; (exit 1);;
 	 esac'
 
@@ -172,14 +172,29 @@
 	test -z "`git diff-index HEAD -- foo`" &&
 	git read-tree HEAD &&
 	case "`git diff-index HEAD -- foo`" in
-	:100644" "*"M	foo") echo ok;;
+	:100644" "*"M	foo") echo pass;;
 	*) echo fail; (exit 1);;
 	esac &&
 	git add --refresh -- foo &&
 	test -z "`git diff-index HEAD -- foo`"
 '
 
-test_expect_success POSIXPERM 'git add should fail atomically upon an unreadable file' '
+test_expect_success 'git add --refresh with pathspec' '
+	git reset --hard &&
+	echo >foo && echo >bar && echo >baz &&
+	git add foo bar baz && H=$(git rev-parse :foo) && git rm -f foo &&
+	echo "100644 $H 3	foo" | git update-index --index-info &&
+	test-chmtime -60 bar baz &&
+	>expect &&
+	git add --refresh bar >actual &&
+	test_cmp expect actual &&
+
+	git diff-files --name-only >actual &&
+	! grep bar actual&&
+	grep baz actual
+'
+
+test_expect_success POSIXPERM,SANITY 'git add should fail atomically upon an unreadable file' '
 	git reset --hard &&
 	date >foo1 &&
 	date >foo2 &&
@@ -190,7 +205,7 @@
 
 rm -f foo2
 
-test_expect_success POSIXPERM 'git add --ignore-errors' '
+test_expect_success POSIXPERM,SANITY 'git add --ignore-errors' '
 	git reset --hard &&
 	date >foo1 &&
 	date >foo2 &&
@@ -201,7 +216,7 @@
 
 rm -f foo2
 
-test_expect_success POSIXPERM 'git add (add.ignore-errors)' '
+test_expect_success POSIXPERM,SANITY 'git add (add.ignore-errors)' '
 	git config add.ignore-errors 1 &&
 	git reset --hard &&
 	date >foo1 &&
@@ -212,7 +227,7 @@
 '
 rm -f foo2
 
-test_expect_success POSIXPERM 'git add (add.ignore-errors = false)' '
+test_expect_success POSIXPERM,SANITY 'git add (add.ignore-errors = false)' '
 	git config add.ignore-errors 0 &&
 	git reset --hard &&
 	date >foo1 &&
@@ -223,7 +238,7 @@
 '
 rm -f foo2
 
-test_expect_success POSIXPERM '--no-ignore-errors overrides config' '
+test_expect_success POSIXPERM,SANITY '--no-ignore-errors overrides config' '
        git config add.ignore-errors 1 &&
        git reset --hard &&
        date >foo1 &&
@@ -255,4 +270,44 @@
 	git add track-this
 '
 
+test_expect_success '"add non-existent" should fail' '
+	test_must_fail git add non-existent &&
+	! (git ls-files | grep "non-existent")
+'
+
+test_expect_success 'git add --dry-run of existing changed file' "
+	echo new >>track-this &&
+	git add --dry-run track-this >actual 2>&1 &&
+	echo \"add 'track-this'\" | test_cmp - actual
+"
+
+test_expect_success 'git add --dry-run of non-existing file' "
+	echo ignored-file >>.gitignore &&
+	test_must_fail git add --dry-run track-this ignored-file >actual 2>&1
+"
+
+test_expect_success 'git add --dry-run of an existing file output' "
+	echo \"fatal: pathspec 'ignored-file' did not match any files\" >expect &&
+	test_i18ncmp expect actual
+"
+
+cat >expect.err <<\EOF
+The following paths are ignored by one of your .gitignore files:
+ignored-file
+Use -f if you really want to add them.
+fatal: no files added
+EOF
+cat >expect.out <<\EOF
+add 'track-this'
+EOF
+
+test_expect_success 'git add --dry-run --ignore-missing of non-existing file' '
+	test_must_fail git add --dry-run --ignore-missing track-this ignored-file >actual.out 2>actual.err
+'
+
+test_expect_success 'git add --dry-run --ignore-missing of non-existing file output' '
+	test_i18ncmp expect.out actual.out &&
+	test_i18ncmp expect.err actual.err
+'
+
 test_done
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index fd2a55a..098a6ae 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -2,22 +2,20 @@
 
 test_description='add -i basic tests'
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-prereq-FILEMODE.sh
 
-if ! test_have_prereq PERL; then
-	say 'skipping git add -i tests, perl not available'
-	test_done
-fi
-
-test_expect_success 'setup (initial)' '
+test_expect_success PERL 'setup (initial)' '
 	echo content >file &&
 	git add file &&
 	echo more >>file &&
 	echo lines >>file
 '
-test_expect_success 'status works (initial)' '
+test_expect_success PERL 'status works (initial)' '
 	git add -i </dev/null >output &&
 	grep "+1/-0 *+2/-0 file" output
 '
+
+test_expect_success PERL 'setup expected' '
 cat >expected <<EOF
 new file mode 100644
 index 0000000..d95f3ad
@@ -26,19 +24,21 @@
 @@ -0,0 +1 @@
 +content
 EOF
-test_expect_success 'diff works (initial)' '
+'
+
+test_expect_success PERL 'diff works (initial)' '
 	(echo d; echo 1) | git add -i >output &&
 	sed -ne "/new file/,/content/p" <output >diff &&
 	test_cmp expected diff
 '
-test_expect_success 'revert works (initial)' '
+test_expect_success PERL 'revert works (initial)' '
 	git add file &&
 	(echo r; echo 1) | git add -i &&
 	git ls-files >output &&
 	! grep . output
 '
 
-test_expect_success 'setup (commit)' '
+test_expect_success PERL 'setup (commit)' '
 	echo baseline >file &&
 	git add file &&
 	git commit -m commit &&
@@ -47,10 +47,12 @@
 	echo more >>file &&
 	echo lines >>file
 '
-test_expect_success 'status works (commit)' '
+test_expect_success PERL 'status works (commit)' '
 	git add -i </dev/null >output &&
 	grep "+1/-0 *+2/-0 file" output
 '
+
+test_expect_success PERL 'setup expected' '
 cat >expected <<EOF
 index 180b47c..b6f2c08 100644
 --- a/file
@@ -59,60 +61,78 @@
  baseline
 +content
 EOF
-test_expect_success 'diff works (commit)' '
+'
+
+test_expect_success PERL 'diff works (commit)' '
 	(echo d; echo 1) | git add -i >output &&
 	sed -ne "/^index/,/content/p" <output >diff &&
 	test_cmp expected diff
 '
-test_expect_success 'revert works (commit)' '
+test_expect_success PERL 'revert works (commit)' '
 	git add file &&
 	(echo r; echo 1) | git add -i &&
 	git add -i </dev/null >output &&
 	grep "unchanged *+3/-0 file" output
 '
 
+
+test_expect_success PERL 'setup expected' '
 cat >expected <<EOF
 EOF
-cat >fake_editor.sh <<EOF
-EOF
-chmod a+x fake_editor.sh
-test_set_editor "$(pwd)/fake_editor.sh"
-test_expect_success 'dummy edit works' '
+'
+
+test_expect_success PERL 'setup fake editor' '
+	>fake_editor.sh &&
+	chmod a+x fake_editor.sh &&
+	test_set_editor "$(pwd)/fake_editor.sh"
+'
+
+test_expect_success PERL 'dummy edit works' '
 	(echo e; echo a) | git add -p &&
 	git diff > diff &&
 	test_cmp expected diff
 '
 
+test_expect_success PERL 'setup patch' '
 cat >patch <<EOF
 @@ -1,1 +1,4 @@
  this
 +patch
--doesn't
+-does not
  apply
 EOF
-echo "#!$SHELL_PATH" >fake_editor.sh
-cat >>fake_editor.sh <<\EOF
+'
+
+test_expect_success PERL 'setup fake editor' '
+	echo "#!$SHELL_PATH" >fake_editor.sh &&
+	cat >>fake_editor.sh <<\EOF &&
 mv -f "$1" oldpatch &&
 mv -f patch "$1"
 EOF
-chmod a+x fake_editor.sh
-test_set_editor "$(pwd)/fake_editor.sh"
-test_expect_success 'bad edit rejected' '
+	chmod a+x fake_editor.sh &&
+	test_set_editor "$(pwd)/fake_editor.sh"
+'
+
+test_expect_success PERL 'bad edit rejected' '
 	git reset &&
 	(echo e; echo n; echo d) | git add -p >output &&
 	grep "hunk does not apply" output
 '
 
+test_expect_success PERL 'setup patch' '
 cat >patch <<EOF
 this patch
 is garbage
 EOF
-test_expect_success 'garbage edit rejected' '
+'
+
+test_expect_success PERL 'garbage edit rejected' '
 	git reset &&
 	(echo e; echo n; echo d) | git add -p >output &&
 	grep "hunk does not apply" output
 '
 
+test_expect_success PERL 'setup patch' '
 cat >patch <<EOF
 @@ -1,0 +1,0 @@
  baseline
@@ -120,6 +140,9 @@
 +newcontent
 +lines
 EOF
+'
+
+test_expect_success PERL 'setup expected' '
 cat >expected <<EOF
 diff --git a/file b/file
 index b5dd6c9..f910ae9 100644
@@ -132,20 +155,29 @@
 +more
  lines
 EOF
-test_expect_success 'real edit works' '
+'
+
+test_expect_success PERL 'real edit works' '
 	(echo e; echo n; echo d) | git add -p &&
 	git diff >output &&
 	test_cmp expected output
 '
 
-if test "$(git config --bool core.filemode)" = false
-then
-	say 'skipping filemode tests (filesystem does not properly support modes)'
-else
-	test_set_prereq FILEMODE
-fi
+test_expect_success PERL 'skip files similarly as commit -a' '
+	git reset &&
+	echo file >.gitignore &&
+	echo changed >file &&
+	echo y | git add -p file &&
+	git diff >output &&
+	git reset &&
+	git commit -am commit &&
+	git diff >expected &&
+	test_cmp expected output &&
+	git reset --hard HEAD^
+'
+rm -f .gitignore
 
-test_expect_success FILEMODE 'patch does not affect mode' '
+test_expect_success PERL,FILEMODE 'patch does not affect mode' '
 	git reset --hard &&
 	echo content >>file &&
 	chmod +x file &&
@@ -154,7 +186,7 @@
 	git diff file | grep "new mode"
 '
 
-test_expect_success FILEMODE 'stage mode but not hunk' '
+test_expect_success PERL,FILEMODE 'stage mode but not hunk' '
 	git reset --hard &&
 	echo content >>file &&
 	chmod +x file &&
@@ -163,15 +195,27 @@
 	git diff          file | grep "+content"
 '
 
+
+test_expect_success PERL,FILEMODE 'stage mode and hunk' '
+	git reset --hard &&
+	echo content >>file &&
+	chmod +x file &&
+	printf "y\\ny\\n" | git add -p &&
+	git diff --cached file | grep "new mode" &&
+	git diff --cached file | grep "+content" &&
+	test -z "$(git diff file)"
+'
+
 # end of tests disabled when filemode is not usable
 
-test_expect_success 'setup again' '
+test_expect_success PERL 'setup again' '
 	git reset --hard &&
 	test_chmod +x file &&
 	echo content >>file
 '
 
 # Write the patch file with a new line at the top and bottom
+test_expect_success PERL 'setup patch' '
 cat >patch <<EOF
 index 180b47c..b6f2c08 100644
 --- a/file
@@ -182,7 +226,10 @@
  content
 +lastline
 EOF
+'
+
 # Expected output, similar to the patch but w/ diff at the top
+test_expect_success PERL 'setup expected' '
 cat >expected <<EOF
 diff --git a/file b/file
 index b6f2c08..61b9053 100755
@@ -194,8 +241,10 @@
  content
 +lastline
 EOF
+'
+
 # Test splitting the first patch, then adding both
-test_expect_success 'add first line works' '
+test_expect_success PERL 'add first line works' '
 	git commit -am "clear local changes" &&
 	git apply patch &&
 	(echo s; echo y; echo y) | git add -p file &&
@@ -203,4 +252,108 @@
 	test_cmp expected diff
 '
 
+test_expect_success PERL 'setup expected' '
+cat >expected <<EOF
+diff --git a/non-empty b/non-empty
+deleted file mode 100644
+index d95f3ad..0000000
+--- a/non-empty
++++ /dev/null
+@@ -1 +0,0 @@
+-content
+EOF
+'
+
+test_expect_success PERL 'deleting a non-empty file' '
+	git reset --hard &&
+	echo content >non-empty &&
+	git add non-empty &&
+	git commit -m non-empty &&
+	rm non-empty &&
+	echo y | git add -p non-empty &&
+	git diff --cached >diff &&
+	test_cmp expected diff
+'
+
+test_expect_success PERL 'setup expected' '
+cat >expected <<EOF
+diff --git a/empty b/empty
+deleted file mode 100644
+index e69de29..0000000
+EOF
+'
+
+test_expect_success PERL 'deleting an empty file' '
+	git reset --hard &&
+	> empty &&
+	git add empty &&
+	git commit -m empty &&
+	rm empty &&
+	echo y | git add -p empty &&
+	git diff --cached >diff &&
+	test_cmp expected diff
+'
+
+test_expect_success PERL 'split hunk setup' '
+	git reset --hard &&
+	for i in 10 20 30 40 50 60
+	do
+		echo $i
+	done >test &&
+	git add test &&
+	test_tick &&
+	git commit -m test &&
+
+	for i in 10 15 20 21 22 23 24 30 40 50 60
+	do
+		echo $i
+	done >test
+'
+
+test_expect_success PERL 'split hunk "add -p (edit)"' '
+	# Split, say Edit and do nothing.  Then:
+	#
+	# 1. Broken version results in a patch that does not apply and
+	# only takes [y/n] (edit again) so the first q is discarded
+	# and then n attempts to discard the edit. Repeat q enough
+	# times to get out.
+	#
+	# 2. Correct version applies the (not)edited version, and asks
+	#    about the next hunk, against wich we say q and program
+	#    exits.
+	for a in s e     q n q q
+	do
+		echo $a
+	done |
+	EDITOR=: git add -p &&
+	git diff >actual &&
+	! grep "^+15" actual
+'
+
+test_expect_success 'patch mode ignores unmerged entries' '
+	git reset --hard &&
+	test_commit conflict &&
+	test_commit non-conflict &&
+	git checkout -b side &&
+	test_commit side conflict.t &&
+	git checkout master &&
+	test_commit master conflict.t &&
+	test_must_fail git merge side &&
+	echo changed >non-conflict.t &&
+	echo y | git add -p >output &&
+	! grep a/conflict.t output &&
+	cat >expected <<-\EOF &&
+	* Unmerged path conflict.t
+	diff --git a/non-conflict.t b/non-conflict.t
+	index f766221..5ea2ed4 100644
+	--- a/non-conflict.t
+	+++ b/non-conflict.t
+	@@ -1 +1 @@
+	-non-conflict
+	+changed
+	EOF
+	git diff --cached >diff &&
+	test_cmp expected diff
+'
+
 test_done
diff --git a/t/t3703-add-magic-pathspec.sh b/t/t3703-add-magic-pathspec.sh
new file mode 100755
index 0000000..5115de7
--- /dev/null
+++ b/t/t3703-add-magic-pathspec.sh
@@ -0,0 +1,58 @@
+#!/bin/sh
+
+test_description='magic pathspec tests using git-add'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	mkdir sub anothersub &&
+	: >sub/foo &&
+	: >anothersub/foo
+'
+
+test_expect_success 'add :/' "
+	cat >expected <<-EOF &&
+	add 'anothersub/foo'
+	add 'expected'
+	add 'sub/actual'
+	add 'sub/foo'
+	EOF
+	(cd sub && git add -n :/ >actual) &&
+	test_cmp expected sub/actual
+"
+
+cat >expected <<EOF
+add 'anothersub/foo'
+EOF
+
+test_expect_success 'add :/anothersub' '
+	(cd sub && git add -n :/anothersub >actual) &&
+	test_cmp expected sub/actual
+'
+
+test_expect_success 'add :/non-existent' '
+	(cd sub && test_must_fail git add -n :/non-existent)
+'
+
+cat >expected <<EOF
+add 'sub/foo'
+EOF
+
+if mkdir ":" 2>/dev/null
+then
+	test_set_prereq COLON_DIR
+fi
+
+test_expect_success COLON_DIR 'a file with the same (long) magic name exists' '
+	: >":(icase)ha" &&
+	test_must_fail git add -n ":(icase)ha" &&
+	git add -n "./:(icase)ha"
+'
+
+test_expect_success COLON_DIR 'a file with the same (short) magic name exists' '
+	: >":/bar" &&
+	test_must_fail git add -n :/bar &&
+	git add -n "./:/bar"
+'
+
+test_done
diff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh
index 6fb027b..8eb4794 100755
--- a/t/t3800-mktag.sh
+++ b/t/t3800-mktag.sh
@@ -22,10 +22,12 @@
 ###########################################################
 # 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)
+test_expect_success 'setup' '
+	echo Hello >A &&
+	git update-index --add A &&
+	git commit -m "Initial commit" &&
+	head=$(git rev-parse --verify HEAD)
+'
 
 ############################################################
 #  1. length check
diff --git a/t/t3900-i18n-commit.sh b/t/t3900-i18n-commit.sh
index 256c4c9..37ddabb 100755
--- a/t/t3900-i18n-commit.sh
+++ b/t/t3900-i18n-commit.sh
@@ -34,6 +34,12 @@
 	test z = "z$E"
 '
 
+test_expect_failure 'UTF-16 refused because of NULs' '
+	echo UTF-16 >F &&
+	git commit -a -F "$TEST_DIRECTORY"/t3900/UTF-16.txt
+'
+
+
 for H in ISO8859-1 eucJP ISO-2022-JP
 do
 	test_expect_success "$H setup" '
@@ -133,4 +139,33 @@
 	'
 done
 
+test_commit_autosquash_flags () {
+	H=$1
+	flag=$2
+	test_expect_success "commit --$flag with $H encoding" '
+		git config i18n.commitencoding $H &&
+		git checkout -b $H-$flag C0 &&
+		echo $H >>F &&
+		git commit -a -F "$TEST_DIRECTORY"/t3900/$H.txt &&
+		test_tick &&
+		echo intermediate stuff >>G &&
+		git add G &&
+		git commit -a -m "intermediate commit" &&
+		test_tick &&
+		echo $H $flag >>F &&
+		git commit -a --$flag HEAD~1 &&
+		E=$(git cat-file commit '$H-$flag' |
+			sed -ne "s/^encoding //p") &&
+		test "z$E" = "z$H" &&
+		git config --unset-all i18n.commitencoding &&
+		git rebase --autosquash -i HEAD^^^ &&
+		git log --oneline >actual &&
+		test_line_count = 3 actual
+	'
+}
+
+test_commit_autosquash_flags eucJP fixup
+
+test_commit_autosquash_flags ISO-2022-JP squash
+
 test_done
diff --git a/t/t3902-quoted.sh b/t/t3902-quoted.sh
index 5868052..534ee08 100755
--- a/t/t3902-quoted.sh
+++ b/t/t3902-quoted.sh
@@ -10,41 +10,43 @@
 FN='濱野'
 GN='純'
 HT='	'
-LF='
-'
 DQ='"'
 
 echo foo 2>/dev/null > "Name and an${HT}HT"
-test -f "Name and an${HT}HT" || {
-	# since FAT/NTFS does not allow tabs in filenames, skip this test
-	say 'Your filesystem does not allow tabs in filenames, test skipped.'
-	test_done
-}
+if ! test -f "Name and an${HT}HT"
+then
+	# FAT/NTFS does not allow tabs in filenames
+	say 'Your filesystem does not allow tabs in filenames'
+else
+	test_set_prereq TABS_IN_FILENAMES
+fi
 
 for_each_name () {
 	for name in \
 	    Name "Name and a${LF}LF" "Name and an${HT}HT" "Name${DQ}" \
 	    "$FN$HT$GN" "$FN$LF$GN" "$FN $GN" "$FN$GN" "$FN$DQ$GN" \
-	    "With SP in it"
+	    "With SP in it" "$FN/file"
 	do
 		eval "$1"
 	done
 }
 
-test_expect_success setup '
+test_expect_success TABS_IN_FILENAMES 'setup' '
 
-	for_each_name "echo initial >\"\$name\""
+	mkdir "$FN" &&
+	for_each_name "echo initial >\"\$name\"" &&
 	git add . &&
 	git commit -q -m Initial &&
 
 	for_each_name "echo second >\"\$name\"" &&
-	git commit -a -m Second
+	git commit -a -m Second &&
 
 	for_each_name "echo modified >\"\$name\""
 
 '
 
-cat >expect.quoted <<\EOF
+test_expect_success TABS_IN_FILENAMES 'setup expected files' '
+cat >expect.quoted <<\EOF &&
 Name
 "Name and a\nLF"
 "Name and an\tHT"
@@ -54,6 +56,7 @@
 "\346\277\261\351\207\216\n\347\264\224"
 "\346\277\261\351\207\216 \347\264\224"
 "\346\277\261\351\207\216\"\347\264\224"
+"\346\277\261\351\207\216/file"
 "\346\277\261\351\207\216\347\264\224"
 EOF
 
@@ -67,67 +70,83 @@
 "濱野\n純"
 濱野 純
 "濱野\"純"
+濱野/file
 濱野純
 EOF
+'
 
-test_expect_success 'check fully quoted output from ls-files' '
+test_expect_success TABS_IN_FILENAMES 'check fully quoted output from ls-files' '
 
 	git ls-files >current && test_cmp expect.quoted current
 
 '
 
-test_expect_success 'check fully quoted output from diff-files' '
+test_expect_success TABS_IN_FILENAMES 'check fully quoted output from diff-files' '
 
 	git diff --name-only >current &&
 	test_cmp expect.quoted current
 
 '
 
-test_expect_success 'check fully quoted output from diff-index' '
+test_expect_success TABS_IN_FILENAMES 'check fully quoted output from diff-index' '
 
 	git diff --name-only HEAD >current &&
 	test_cmp expect.quoted current
 
 '
 
-test_expect_success 'check fully quoted output from diff-tree' '
+test_expect_success TABS_IN_FILENAMES 'check fully quoted output from diff-tree' '
 
 	git diff --name-only HEAD^ HEAD >current &&
 	test_cmp expect.quoted current
 
 '
 
-test_expect_success 'setting core.quotepath' '
+test_expect_success TABS_IN_FILENAMES 'check fully quoted output from ls-tree' '
+
+	git ls-tree --name-only -r HEAD >current &&
+	test_cmp expect.quoted current
+
+'
+
+test_expect_success TABS_IN_FILENAMES 'setting core.quotepath' '
 
 	git config --bool core.quotepath false
 
 '
 
-test_expect_success 'check fully quoted output from ls-files' '
+test_expect_success TABS_IN_FILENAMES 'check fully quoted output from ls-files' '
 
 	git ls-files >current && test_cmp expect.raw current
 
 '
 
-test_expect_success 'check fully quoted output from diff-files' '
+test_expect_success TABS_IN_FILENAMES 'check fully quoted output from diff-files' '
 
 	git diff --name-only >current &&
 	test_cmp expect.raw current
 
 '
 
-test_expect_success 'check fully quoted output from diff-index' '
+test_expect_success TABS_IN_FILENAMES 'check fully quoted output from diff-index' '
 
 	git diff --name-only HEAD >current &&
 	test_cmp expect.raw current
 
 '
 
-test_expect_success 'check fully quoted output from diff-tree' '
+test_expect_success TABS_IN_FILENAMES 'check fully quoted output from diff-tree' '
 
 	git diff --name-only HEAD^ HEAD >current &&
 	test_cmp expect.raw current
 
 '
 
+test_expect_success TABS_IN_FILENAMES 'check fully quoted output from ls-tree' '
+
+	git ls-tree --name-only -r HEAD >current &&
+	test_cmp expect.raw current
+
+'
+
 test_done
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index 7a3fb67..cd04263 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -37,14 +37,32 @@
 	test_cmp output expect
 '
 
-test_expect_success 'apply needs clean working directory' '
-	echo 4 > other-file &&
+test_expect_success 'applying bogus stash does nothing' '
+	test_must_fail git stash apply stash@{1} &&
+	echo 1 >expect &&
+	test_cmp expect file
+'
+
+test_expect_success 'apply does not need clean working directory' '
+	echo 4 >other-file &&
 	git add other-file &&
-	echo 5 > other-file &&
-	test_must_fail git stash apply
+	echo 5 >other-file &&
+	git stash apply &&
+	echo 3 >expect &&
+	test_cmp expect file
+'
+
+test_expect_success 'apply does not clobber working directory changes' '
+	git reset --hard &&
+	echo 4 >file &&
+	test_must_fail git stash apply &&
+	echo 4 >expect &&
+	test_cmp expect file
 '
 
 test_expect_success 'apply stashed changes' '
+	git reset --hard &&
+	echo 5 >other-file &&
 	git add other-file &&
 	test_tick &&
 	git commit -m other-file &&
@@ -69,9 +87,10 @@
 test_expect_success 'unstashing in a subdirectory' '
 	git reset --hard HEAD &&
 	mkdir subdir &&
-	cd subdir &&
-	git stash apply &&
-	cd ..
+	(
+		cd subdir &&
+		git stash apply
+	)
 '
 
 test_expect_success 'drop top stash' '
@@ -81,7 +100,7 @@
 	git stash &&
 	git stash drop &&
 	git stash list > stashlist2 &&
-	diff stashlist1 stashlist2 &&
+	test_cmp stashlist1 stashlist2 &&
 	git stash apply &&
 	test 3 = $(cat file) &&
 	test 1 = $(git show :file) &&
@@ -156,7 +175,7 @@
 
 test_expect_success 'stash branch' '
 	echo foo > file &&
-	git commit file -m first
+	git commit file -m first &&
 	echo bar > file &&
 	echo bar2 > file2 &&
 	git add file2 &&
@@ -194,10 +213,428 @@
 	test ! -s output.out
 '
 
+test_expect_success 'pop -q --index works and is quiet' '
+	echo foo > file &&
+	git add file &&
+	git stash save --quiet &&
+	git stash pop -q --index > output.out 2>&1 &&
+	test foo = "$(git show :file)" &&
+	test ! -s output.out
+'
+
 test_expect_success 'drop -q is quiet' '
 	git stash &&
 	git stash drop -q > output.out 2>&1 &&
 	test ! -s output.out
 '
 
+test_expect_success 'stash -k' '
+	echo bar3 > file &&
+	echo bar4 > file2 &&
+	git add file2 &&
+	git stash -k &&
+	test bar,bar4 = $(cat file),$(cat file2)
+'
+
+test_expect_success 'stash --no-keep-index' '
+	echo bar33 > file &&
+	echo bar44 > file2 &&
+	git add file2 &&
+	git stash --no-keep-index &&
+	test bar,bar2 = $(cat file),$(cat file2)
+'
+
+test_expect_success 'stash --invalid-option' '
+	echo bar5 > file &&
+	echo bar6 > file2 &&
+	git add file2 &&
+	test_must_fail git stash --invalid-option &&
+	test_must_fail git stash save --invalid-option &&
+	test bar5,bar6 = $(cat file),$(cat file2) &&
+	git stash -- -message-starting-with-dash &&
+	test bar,bar2 = $(cat file),$(cat file2)
+'
+
+test_expect_success 'stash an added file' '
+	git reset --hard &&
+	echo new >file3 &&
+	git add file3 &&
+	git stash save "added file" &&
+	! test -r file3 &&
+	git stash apply &&
+	test new = "$(cat file3)"
+'
+
+test_expect_success 'stash rm then recreate' '
+	git reset --hard &&
+	git rm file &&
+	echo bar7 >file &&
+	git stash save "rm then recreate" &&
+	test bar = "$(cat file)" &&
+	git stash apply &&
+	test bar7 = "$(cat file)"
+'
+
+test_expect_success 'stash rm and ignore' '
+	git reset --hard &&
+	git rm file &&
+	echo file >.gitignore &&
+	git stash save "rm and ignore" &&
+	test bar = "$(cat file)" &&
+	test file = "$(cat .gitignore)" &&
+	git stash apply &&
+	! test -r file &&
+	test file = "$(cat .gitignore)"
+'
+
+test_expect_success 'stash rm and ignore (stage .gitignore)' '
+	git reset --hard &&
+	git rm file &&
+	echo file >.gitignore &&
+	git add .gitignore &&
+	git stash save "rm and ignore (stage .gitignore)" &&
+	test bar = "$(cat file)" &&
+	! test -r .gitignore &&
+	git stash apply &&
+	! test -r file &&
+	test file = "$(cat .gitignore)"
+'
+
+test_expect_success SYMLINKS 'stash file to symlink' '
+	git reset --hard &&
+	rm file &&
+	ln -s file2 file &&
+	git stash save "file to symlink" &&
+	test -f file &&
+	test bar = "$(cat file)" &&
+	git stash apply &&
+	case "$(ls -l file)" in *" file -> file2") :;; *) false;; esac
+'
+
+test_expect_success SYMLINKS 'stash file to symlink (stage rm)' '
+	git reset --hard &&
+	git rm file &&
+	ln -s file2 file &&
+	git stash save "file to symlink (stage rm)" &&
+	test -f file &&
+	test bar = "$(cat file)" &&
+	git stash apply &&
+	case "$(ls -l file)" in *" file -> file2") :;; *) false;; esac
+'
+
+test_expect_success SYMLINKS 'stash file to symlink (full stage)' '
+	git reset --hard &&
+	rm file &&
+	ln -s file2 file &&
+	git add file &&
+	git stash save "file to symlink (full stage)" &&
+	test -f file &&
+	test bar = "$(cat file)" &&
+	git stash apply &&
+	case "$(ls -l file)" in *" file -> file2") :;; *) false;; esac
+'
+
+# This test creates a commit with a symlink used for the following tests
+
+test_expect_success SYMLINKS 'stash symlink to file' '
+	git reset --hard &&
+	ln -s file filelink &&
+	git add filelink &&
+	git commit -m "Add symlink" &&
+	rm filelink &&
+	cp file filelink &&
+	git stash save "symlink to file" &&
+	test -h filelink &&
+	case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac &&
+	git stash apply &&
+	! test -h filelink &&
+	test bar = "$(cat file)"
+'
+
+test_expect_success SYMLINKS 'stash symlink to file (stage rm)' '
+	git reset --hard &&
+	git rm filelink &&
+	cp file filelink &&
+	git stash save "symlink to file (stage rm)" &&
+	test -h filelink &&
+	case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac &&
+	git stash apply &&
+	! test -h filelink &&
+	test bar = "$(cat file)"
+'
+
+test_expect_success SYMLINKS 'stash symlink to file (full stage)' '
+	git reset --hard &&
+	rm filelink &&
+	cp file filelink &&
+	git add filelink &&
+	git stash save "symlink to file (full stage)" &&
+	test -h filelink &&
+	case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac &&
+	git stash apply &&
+	! test -h filelink &&
+	test bar = "$(cat file)"
+'
+
+test_expect_failure 'stash directory to file' '
+	git reset --hard &&
+	mkdir dir &&
+	echo foo >dir/file &&
+	git add dir/file &&
+	git commit -m "Add file in dir" &&
+	rm -fr dir &&
+	echo bar >dir &&
+	git stash save "directory to file" &&
+	test -d dir &&
+	test foo = "$(cat dir/file)" &&
+	test_must_fail git stash apply &&
+	test bar = "$(cat dir)" &&
+	git reset --soft HEAD^
+'
+
+test_expect_failure 'stash file to directory' '
+	git reset --hard &&
+	rm file &&
+	mkdir file &&
+	echo foo >file/file &&
+	git stash save "file to directory" &&
+	test -f file &&
+	test bar = "$(cat file)" &&
+	git stash apply &&
+	test -f file/file &&
+	test foo = "$(cat file/file)"
+'
+
+test_expect_success 'stash branch - no stashes on stack, stash-like argument' '
+	git stash clear &&
+	test_when_finished "git reset --hard HEAD" &&
+	git reset --hard &&
+	echo foo >> file &&
+	STASH_ID=$(git stash create) &&
+	git reset --hard &&
+	git stash branch stash-branch ${STASH_ID} &&
+	test_when_finished "git reset --hard HEAD && git checkout master && git branch -D stash-branch" &&
+	test $(git ls-files --modified | wc -l) -eq 1
+'
+
+test_expect_success 'stash branch - stashes on stack, stash-like argument' '
+	git stash clear &&
+	test_when_finished "git reset --hard HEAD" &&
+	git reset --hard &&
+	echo foo >> file &&
+	git stash &&
+	test_when_finished "git stash drop" &&
+	echo bar >> file &&
+	STASH_ID=$(git stash create) &&
+	git reset --hard &&
+	git stash branch stash-branch ${STASH_ID} &&
+	test_when_finished "git reset --hard HEAD && git checkout master && git branch -D stash-branch" &&
+	test $(git ls-files --modified | wc -l) -eq 1
+'
+
+test_expect_success 'stash show format defaults to --stat' '
+	git stash clear &&
+	test_when_finished "git reset --hard HEAD" &&
+	git reset --hard &&
+	echo foo >> file &&
+	git stash &&
+	test_when_finished "git stash drop" &&
+	echo bar >> file &&
+	STASH_ID=$(git stash create) &&
+	git reset --hard &&
+	cat >expected <<-EOF &&
+	 file | 1 +
+	 1 file changed, 1 insertion(+)
+	EOF
+	git stash show ${STASH_ID} >actual &&
+	test_i18ncmp expected actual
+'
+
+test_expect_success 'stash show - stashes on stack, stash-like argument' '
+	git stash clear &&
+	test_when_finished "git reset --hard HEAD" &&
+	git reset --hard &&
+	echo foo >> file &&
+	git stash &&
+	test_when_finished "git stash drop" &&
+	echo bar >> file &&
+	STASH_ID=$(git stash create) &&
+	git reset --hard &&
+	echo "1	0	file" >expected &&
+	git stash show --numstat ${STASH_ID} >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'stash show -p - stashes on stack, stash-like argument' '
+	git stash clear &&
+	test_when_finished "git reset --hard HEAD" &&
+	git reset --hard &&
+	echo foo >> file &&
+	git stash &&
+	test_when_finished "git stash drop" &&
+	echo bar >> file &&
+	STASH_ID=$(git stash create) &&
+	git reset --hard &&
+	cat >expected <<-EOF &&
+	diff --git a/file b/file
+	index 7601807..935fbd3 100644
+	--- a/file
+	+++ b/file
+	@@ -1 +1,2 @@
+	 baz
+	+bar
+	EOF
+	git stash show -p ${STASH_ID} >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'stash show - no stashes on stack, stash-like argument' '
+	git stash clear &&
+	test_when_finished "git reset --hard HEAD" &&
+	git reset --hard &&
+	echo foo >> file &&
+	STASH_ID=$(git stash create) &&
+	git reset --hard &&
+	echo "1	0	file" >expected &&
+	git stash show --numstat ${STASH_ID} >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'stash show -p - no stashes on stack, stash-like argument' '
+	git stash clear &&
+	test_when_finished "git reset --hard HEAD" &&
+	git reset --hard &&
+	echo foo >> file &&
+	STASH_ID=$(git stash create) &&
+	git reset --hard &&
+	cat >expected <<-EOF &&
+	diff --git a/file b/file
+	index 7601807..71b52c4 100644
+	--- a/file
+	+++ b/file
+	@@ -1 +1,2 @@
+	 baz
+	+foo
+	EOF
+	git stash show -p ${STASH_ID} >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'stash drop - fail early if specified stash is not a stash reference' '
+	git stash clear &&
+	test_when_finished "git reset --hard HEAD && git stash clear" &&
+	git reset --hard &&
+	echo foo > file &&
+	git stash &&
+	echo bar > file &&
+	git stash &&
+	test_must_fail git stash drop $(git rev-parse stash@{0}) &&
+	git stash pop &&
+	test bar = "$(cat file)" &&
+	git reset --hard HEAD
+'
+
+test_expect_success 'stash pop - fail early if specified stash is not a stash reference' '
+	git stash clear &&
+	test_when_finished "git reset --hard HEAD && git stash clear" &&
+	git reset --hard &&
+	echo foo > file &&
+	git stash &&
+	echo bar > file &&
+	git stash &&
+	test_must_fail git stash pop $(git rev-parse stash@{0}) &&
+	git stash pop &&
+	test bar = "$(cat file)" &&
+	git reset --hard HEAD
+'
+
+test_expect_success 'ref with non-existent reflog' '
+	git stash clear &&
+	echo bar5 > file &&
+	echo bar6 > file2 &&
+	git add file2 &&
+	git stash &&
+	test_must_fail git rev-parse --quiet --verify does-not-exist &&
+	test_must_fail git stash drop does-not-exist &&
+	test_must_fail git stash drop does-not-exist@{0} &&
+	test_must_fail git stash pop does-not-exist &&
+	test_must_fail git stash pop does-not-exist@{0} &&
+	test_must_fail git stash apply does-not-exist &&
+	test_must_fail git stash apply does-not-exist@{0} &&
+	test_must_fail git stash show does-not-exist &&
+	test_must_fail git stash show does-not-exist@{0} &&
+	test_must_fail git stash branch tmp does-not-exist &&
+	test_must_fail git stash branch tmp does-not-exist@{0} &&
+	git stash drop
+'
+
+test_expect_success 'invalid ref of the form stash@{n}, n >= N' '
+	git stash clear &&
+	test_must_fail git stash drop stash@{0} &&
+	echo bar5 > file &&
+	echo bar6 > file2 &&
+	git add file2 &&
+	git stash &&
+	test_must_fail git stash drop stash@{1} &&
+	test_must_fail git stash pop stash@{1} &&
+	test_must_fail git stash apply stash@{1} &&
+	test_must_fail git stash show stash@{1} &&
+	test_must_fail git stash branch tmp stash@{1} &&
+	git stash drop
+'
+
+test_expect_success 'stash branch should not drop the stash if the branch exists' '
+	git stash clear &&
+	echo foo >file &&
+	git add file &&
+	git commit -m initial &&
+	echo bar >file &&
+	git stash &&
+	test_must_fail git stash branch master stash@{0} &&
+	git rev-parse stash@{0} --
+'
+
+test_expect_success 'stash apply shows status same as git status (relative to current directory)' '
+	git stash clear &&
+	echo 1 >subdir/subfile1 &&
+	echo 2 >subdir/subfile2 &&
+	git add subdir/subfile1 &&
+	git commit -m subdir &&
+	(
+		cd subdir &&
+		echo x >subfile1 &&
+		echo x >../file &&
+		git status >../expect &&
+		git stash &&
+		sane_unset GIT_MERGE_VERBOSITY &&
+		git stash apply
+	) |
+	sed -e 1,2d >actual && # drop "Saved..." and "HEAD is now..."
+	test_cmp expect actual
+'
+
+cat > expect << EOF
+diff --git a/HEAD b/HEAD
+new file mode 100644
+index 0000000..fe0cbee
+--- /dev/null
++++ b/HEAD
+@@ -0,0 +1 @@
++file-not-a-ref
+EOF
+
+test_expect_success 'stash where working directory contains "HEAD" file' '
+	git stash clear &&
+	git reset --hard &&
+	echo file-not-a-ref > HEAD &&
+	git add HEAD &&
+	test_tick &&
+	git stash &&
+	git diff-files --quiet &&
+	git diff-index --cached --quiet HEAD &&
+	test "$(git rev-parse stash^)" = "$(git rev-parse HEAD)" &&
+	git diff stash^..stash > output &&
+	test_cmp output expect
+'
+
 test_done
diff --git a/t/t3904-stash-patch.sh b/t/t3904-stash-patch.sh
new file mode 100755
index 0000000..70655c1
--- /dev/null
+++ b/t/t3904-stash-patch.sh
@@ -0,0 +1,78 @@
+#!/bin/sh
+
+test_description='git checkout --patch'
+. ./lib-patch-mode.sh
+
+test_expect_success PERL 'setup' '
+	mkdir dir &&
+	echo parent > dir/foo &&
+	echo dummy > bar &&
+	echo committed > HEAD &&
+	git add bar dir/foo HEAD &&
+	git commit -m initial &&
+	test_tick &&
+	test_commit second dir/foo head &&
+	echo index > dir/foo &&
+	git add dir/foo &&
+	set_and_save_state bar bar_work bar_index &&
+	save_head
+'
+
+# note: order of files with unstaged changes: HEAD bar dir/foo
+
+test_expect_success PERL 'saying "n" does nothing' '
+	set_state HEAD HEADfile_work HEADfile_index &&
+	set_state dir/foo work index &&
+	(echo n; echo n; echo n) | test_must_fail git stash save -p &&
+	verify_state HEAD HEADfile_work HEADfile_index &&
+	verify_saved_state bar &&
+	verify_state dir/foo work index
+'
+
+test_expect_success PERL 'git stash -p' '
+	(echo y; echo n; echo y) | git stash save -p &&
+	verify_state HEAD committed HEADfile_index &&
+	verify_saved_state bar &&
+	verify_state dir/foo head index &&
+	git reset --hard &&
+	git stash apply &&
+	verify_state HEAD HEADfile_work committed &&
+	verify_state bar dummy dummy &&
+	verify_state dir/foo work head
+'
+
+test_expect_success PERL 'git stash -p --no-keep-index' '
+	set_state HEAD HEADfile_work HEADfile_index &&
+	set_state bar bar_work bar_index &&
+	set_state dir/foo work index &&
+	(echo y; echo n; echo y) | git stash save -p --no-keep-index &&
+	verify_state HEAD committed committed &&
+	verify_state bar bar_work dummy &&
+	verify_state dir/foo head head &&
+	git reset --hard &&
+	git stash apply --index &&
+	verify_state HEAD HEADfile_work HEADfile_index &&
+	verify_state bar dummy bar_index &&
+	verify_state dir/foo work index
+'
+
+test_expect_success PERL 'git stash --no-keep-index -p' '
+	set_state HEAD HEADfile_work HEADfile_index &&
+	set_state bar bar_work bar_index &&
+	set_state dir/foo work index &&
+	(echo y; echo n; echo y) | git stash save --no-keep-index -p &&
+	verify_state HEAD committed committed &&
+	verify_state dir/foo head head &&
+	verify_state bar bar_work dummy &&
+	git reset --hard &&
+	git stash apply --index &&
+	verify_state HEAD HEADfile_work HEADfile_index &&
+	verify_state bar dummy bar_index &&
+	verify_state dir/foo work index
+'
+
+test_expect_success PERL 'none of this moved HEAD' '
+	verify_saved_head
+'
+
+test_done
diff --git a/t/t3905-stash-include-untracked.sh b/t/t3905-stash-include-untracked.sh
new file mode 100755
index 0000000..a5e7e6b
--- /dev/null
+++ b/t/t3905-stash-include-untracked.sh
@@ -0,0 +1,188 @@
+#!/bin/sh
+#
+# Copyright (c) 2011 David Caldwell
+#
+
+test_description='Test git stash --include-untracked'
+
+. ./test-lib.sh
+
+test_expect_success 'stash save --include-untracked some dirty working directory' '
+	echo 1 > file &&
+	git add file &&
+	test_tick &&
+	git commit -m initial &&
+	echo 2 > file &&
+	git add file &&
+	echo 3 > file &&
+	test_tick &&
+	echo 1 > file2 &&
+	echo 1 > HEAD &&
+	mkdir untracked &&
+	echo untracked >untracked/untracked &&
+	git stash --include-untracked &&
+	git diff-files --quiet &&
+	git diff-index --cached --quiet HEAD
+'
+
+cat > expect <<EOF
+?? actual
+?? expect
+EOF
+
+test_expect_success 'stash save --include-untracked cleaned the untracked files' '
+	git status --porcelain >actual &&
+	test_cmp expect actual
+'
+
+cat > expect.diff <<EOF
+diff --git a/HEAD b/HEAD
+new file mode 100644
+index 0000000..d00491f
+--- /dev/null
++++ b/HEAD
+@@ -0,0 +1 @@
++1
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..d00491f
+--- /dev/null
++++ b/file2
+@@ -0,0 +1 @@
++1
+diff --git a/untracked/untracked b/untracked/untracked
+new file mode 100644
+index 0000000..5a72eb2
+--- /dev/null
++++ b/untracked/untracked
+@@ -0,0 +1 @@
++untracked
+EOF
+cat > expect.lstree <<EOF
+HEAD
+file2
+untracked
+EOF
+
+test_expect_success 'stash save --include-untracked stashed the untracked files' '
+	test_path_is_missing file2 &&
+	test_path_is_missing untracked &&
+	test_path_is_missing HEAD &&
+	git diff HEAD stash^3 -- HEAD file2 untracked >actual &&
+	test_cmp expect.diff actual &&
+	git ls-tree --name-only stash^3: >actual &&
+	test_cmp expect.lstree actual
+'
+test_expect_success 'stash save --patch --include-untracked fails' '
+	test_must_fail git stash --patch --include-untracked
+'
+
+test_expect_success 'stash save --patch --all fails' '
+	test_must_fail git stash --patch --all
+'
+
+git clean --force --quiet
+
+cat > expect <<EOF
+ M file
+?? HEAD
+?? actual
+?? expect
+?? file2
+?? untracked/
+EOF
+
+test_expect_success 'stash pop after save --include-untracked leaves files untracked again' '
+	git stash pop &&
+	git status --porcelain >actual &&
+	test_cmp expect actual &&
+	test "1" = "`cat file2`" &&
+	test untracked = "`cat untracked/untracked`"
+'
+
+git clean --force --quiet -d
+
+test_expect_success 'stash save -u dirty index' '
+	echo 4 > file3 &&
+	git add file3 &&
+	test_tick &&
+	git stash -u
+'
+
+cat > expect <<EOF
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..b8626c4
+--- /dev/null
++++ b/file3
+@@ -0,0 +1 @@
++4
+EOF
+
+test_expect_success 'stash save --include-untracked dirty index got stashed' '
+	git stash pop --index &&
+	git diff --cached >actual &&
+	test_cmp expect actual
+'
+
+git reset > /dev/null
+
+# Must direct output somewhere where it won't be considered an untracked file
+test_expect_success 'stash save --include-untracked -q is quiet' '
+	echo 1 > file5 &&
+	git stash save --include-untracked --quiet > .git/stash-output.out 2>&1 &&
+	test_line_count = 0 .git/stash-output.out &&
+	rm -f .git/stash-output.out
+'
+
+test_expect_success 'stash save --include-untracked removed files' '
+	rm -f file &&
+	git stash save --include-untracked &&
+	echo 1 > expect &&
+	test_cmp file expect
+'
+
+rm -f expect
+
+test_expect_success 'stash save --include-untracked removed files got stashed' '
+	git stash pop &&
+	test_path_is_missing file
+'
+
+cat > .gitignore <<EOF
+.gitignore
+ignored
+ignored.d/
+EOF
+
+test_expect_success 'stash save --include-untracked respects .gitignore' '
+	echo ignored > ignored &&
+	mkdir ignored.d &&
+	echo ignored >ignored.d/untracked &&
+	git stash -u &&
+	test -s ignored &&
+	test -s ignored.d/untracked &&
+	test -s .gitignore
+'
+
+test_expect_success 'stash save -u can stash with only untracked files different' '
+	echo 4 > file4 &&
+	git stash -u &&
+	test_path_is_missing file4
+'
+
+test_expect_success 'stash save --all does not respect .gitignore' '
+	git stash -a &&
+	test_path_is_missing ignored &&
+	test_path_is_missing ignored.d &&
+	test_path_is_missing .gitignore
+'
+
+test_expect_success 'stash save --all is stash poppable' '
+	git stash pop &&
+	test -s ignored &&
+	test -s ignored.d/untracked &&
+	test -s .gitignore
+'
+
+test_done
diff --git a/t/t3910-mac-os-precompose.sh b/t/t3910-mac-os-precompose.sh
new file mode 100755
index 0000000..88b7a20
--- /dev/null
+++ b/t/t3910-mac-os-precompose.sh
@@ -0,0 +1,164 @@
+#!/bin/sh
+#
+# Copyright (c) 2012 Torsten Bögershausen
+#
+
+test_description='utf-8 decomposed (nfd) converted to precomposed (nfc)'
+
+. ./test-lib.sh
+
+Adiarnfc=`printf '\303\204'`
+Adiarnfd=`printf 'A\314\210'`
+
+# check if the feature is compiled in
+mkdir junk &&
+>junk/"$Adiarnfc" &&
+case "$(cd junk && echo *)" in
+	"$Adiarnfd")
+	test_nfd=1
+	;;
+	*)	;;
+esac
+rm -rf junk
+
+
+if test "$test_nfd"
+then
+	# create more utf-8 variables
+	Odiarnfc=`printf '\303\226'`
+	Odiarnfd=`printf 'O\314\210'`
+	AEligatu=`printf '\303\206'`
+	Invalidu=`printf '\303\377'`
+
+
+	#Create a string with 255 bytes (decomposed)
+	Alongd=$Adiarnfd$Adiarnfd$Adiarnfd$Adiarnfd$Adiarnfd$Adiarnfd$Adiarnfd #21 Byte
+	Alongd=$Alongd$Alongd$Alongd                                           #63 Byte
+	Alongd=$Alongd$Alongd$Alongd$Alongd$Adiarnfd                           #255 Byte
+
+	#Create a string with 254 bytes (precomposed)
+	Alongc=$AEligatu$AEligatu$AEligatu$AEligatu$AEligatu #10 Byte
+	Alongc=$Alongc$Alongc$Alongc$Alongc$Alongc           #50 Byte
+	Alongc=$Alongc$Alongc$Alongc$Alongc$Alongc           #250 Byte
+	Alongc=$Alongc$AEligatu$AEligatu                     #254 Byte
+
+	test_expect_success "detect if nfd needed" '
+		precomposeunicode=`git config core.precomposeunicode` &&
+		test "$precomposeunicode" = false &&
+		git config core.precomposeunicode true
+	'
+	test_expect_success "setup" '
+		>x &&
+		git add x &&
+		git commit -m "1st commit" &&
+		git rm x &&
+		git commit -m "rm x"
+	'
+	test_expect_success "setup case mac" '
+		git checkout -b mac_os
+	'
+	# This will test nfd2nfc in readdir()
+	test_expect_success "add file Adiarnfc" '
+		echo f.Adiarnfc >f.$Adiarnfc &&
+		git add f.$Adiarnfc &&
+		git commit -m "add f.$Adiarnfc"
+	'
+	# This will test nfd2nfc in git stage()
+	test_expect_success "stage file d.Adiarnfd/f.Adiarnfd" '
+		mkdir d.$Adiarnfd &&
+		echo d.$Adiarnfd/f.$Adiarnfd >d.$Adiarnfd/f.$Adiarnfd &&
+		git stage d.$Adiarnfd/f.$Adiarnfd &&
+		git commit -m "add d.$Adiarnfd/f.$Adiarnfd"
+	'
+	test_expect_success "add link Adiarnfc" '
+		ln -s d.$Adiarnfd/f.$Adiarnfd l.$Adiarnfc &&
+		git add l.$Adiarnfc &&
+		git commit -m "add l.Adiarnfc"
+	'
+	# This will test git log
+	test_expect_success "git log f.Adiar" '
+		git log f.$Adiarnfc > f.Adiarnfc.log &&
+		git log f.$Adiarnfd > f.Adiarnfd.log &&
+		test -s f.Adiarnfc.log &&
+		test -s f.Adiarnfd.log &&
+		test_cmp f.Adiarnfc.log f.Adiarnfd.log &&
+		rm f.Adiarnfc.log f.Adiarnfd.log
+	'
+	# This will test git ls-files
+	test_expect_success "git lsfiles f.Adiar" '
+		git ls-files f.$Adiarnfc > f.Adiarnfc.log &&
+		git ls-files f.$Adiarnfd > f.Adiarnfd.log &&
+		test -s f.Adiarnfc.log &&
+		test -s f.Adiarnfd.log &&
+		test_cmp f.Adiarnfc.log f.Adiarnfd.log &&
+		rm f.Adiarnfc.log f.Adiarnfd.log
+	'
+	# This will test git mv
+	test_expect_success "git mv" '
+		git mv f.$Adiarnfd f.$Odiarnfc &&
+		git mv d.$Adiarnfd d.$Odiarnfc &&
+		git mv l.$Adiarnfd l.$Odiarnfc &&
+		git commit -m "mv Adiarnfd Odiarnfc"
+	'
+	# Files can be checked out as nfc
+	# And the link has been corrected from nfd to nfc
+	test_expect_success "git checkout nfc" '
+		rm f.$Odiarnfc &&
+		git checkout f.$Odiarnfc
+	'
+	# Make it possible to checkout files with their NFD names
+	test_expect_success "git checkout file nfd" '
+		rm -f f.* &&
+		git checkout f.$Odiarnfd
+	'
+	# Make it possible to checkout links with their NFD names
+	test_expect_success "git checkout link nfd" '
+		rm l.* &&
+		git checkout l.$Odiarnfd
+	'
+	test_expect_success "setup case mac2" '
+		git checkout master &&
+		git reset --hard &&
+		git checkout -b mac_os_2
+	'
+	# This will test nfd2nfc in git commit
+	test_expect_success "commit file d2.Adiarnfd/f.Adiarnfd" '
+		mkdir d2.$Adiarnfd &&
+		echo d2.$Adiarnfd/f.$Adiarnfd >d2.$Adiarnfd/f.$Adiarnfd &&
+		git add d2.$Adiarnfd/f.$Adiarnfd &&
+		git commit -m "add d2.$Adiarnfd/f.$Adiarnfd" -- d2.$Adiarnfd/f.$Adiarnfd
+	'
+	test_expect_success "setup for long decomposed filename" '
+		git checkout master &&
+		git reset --hard &&
+		git checkout -b mac_os_long_nfd_fn
+	'
+	test_expect_success "Add long decomposed filename" '
+		echo longd >$Alongd &&
+		git add * &&
+		git commit -m "Long filename"
+	'
+	test_expect_success "setup for long precomposed filename" '
+		git checkout master &&
+		git reset --hard &&
+		git checkout -b mac_os_long_nfc_fn
+	'
+	test_expect_success "Add long precomposed filename" '
+		echo longc >$Alongc &&
+		git add * &&
+		git commit -m "Long filename"
+	'
+	# Test if the global core.precomposeunicode stops autosensing
+	# Must be the last test case
+	test_expect_success "respect git config --global core.precomposeunicode" '
+		git config --global core.precomposeunicode true &&
+		rm -rf .git &&
+		git init &&
+		precomposeunicode=`git config core.precomposeunicode` &&
+		test "$precomposeunicode" = "true"
+	'
+else
+	 say "Skipping nfc/nfd tests"
+fi
+
+test_done
diff --git a/t/t4001-diff-rename.sh b/t/t4001-diff-rename.sh
index 71bac83..844277c 100755
--- a/t/t4001-diff-rename.sh
+++ b/t/t4001-diff-rename.sh
@@ -71,10 +71,35 @@
 	git rm path1 &&
 	mkdir subdir &&
 	git mv another-path subdir/path1 &&
-	git status | grep "renamed: .*path1 -> subdir/path1"'
+	git status | test_i18ngrep "renamed: .*path1 -> subdir/path1"'
 
-test_expect_success  'favour same basenames even with minor differences' '
+test_expect_success 'favour same basenames even with minor differences' '
 	git show HEAD:path1 | sed "s/15/16/" > subdir/path1 &&
-	git status | grep "renamed: .*path1 -> subdir/path1"'
+	git status | test_i18ngrep "renamed: .*path1 -> subdir/path1"'
+
+test_expect_success 'setup for many rename source candidates' '
+	git reset --hard &&
+	for i in 0 1 2 3 4 5 6 7 8 9;
+	do
+		for j in 0 1 2 3 4 5 6 7 8 9;
+		do
+			echo "$i$j" >"path$i$j"
+		done
+	done &&
+	git add "path??" &&
+	test_tick &&
+	git commit -m "hundred" &&
+	(cat path1; echo new) >new-path &&
+	echo old >>path1 &&
+	git add new-path path1 &&
+	git diff -l 4 -C -C --cached --name-status >actual 2>actual.err &&
+	sed -e "s/^\([CM]\)[0-9]*	/\1	/" actual >actual.munged &&
+	cat >expect <<-EOF &&
+	C	path1	new-path
+	M	path1
+	EOF
+	test_cmp expect actual.munged &&
+	grep warning actual.err
+'
 
 test_done
diff --git a/t/t4002-diff-basic.sh b/t/t4002-diff-basic.sh
index 18695ce..a5e8b83 100755
--- a/t/t4002-diff-basic.sh
+++ b/t/t4002-diff-basic.sh
@@ -126,16 +126,13 @@
 :100644 100644 3fdbe17fd013303a2e981e1ca1c6cd6e72789087 7e09d6a3a14bd630913e8c75693cea32157b606d M	Z/NM
 EOF
 
-x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
-x40="$x40$x40$x40$x40$x40$x40$x40$x40"
-z40='0000000000000000000000000000000000000000'
 cmp_diff_files_output () {
     # diff-files never reports additions.  Also it does not fill in the
     # object ID for the changed files because it wants you to look at the
     # filesystem.
     sed <"$2" >.test-tmp \
-	-e '/^:000000 /d;s/'$x40'\( [MCRNDU][0-9]*\)	/'$z40'\1	/' &&
-    diff "$1" .test-tmp
+	-e '/^:000000 /d;s/'$_x40'\( [MCRNDU][0-9]*\)	/'$_z40'\1	/' &&
+    test_cmp "$1" .test-tmp
 }
 
 test_expect_success \
@@ -205,8 +202,8 @@
     'rm -fr Z [A-Z][A-Z] &&
      git read-tree $tree_A &&
      git checkout-index -f -a &&
-     git read-tree --reset $tree_O || return 1
-     git update-index --refresh >/dev/null ;# this can exit non-zero
+     git read-tree --reset $tree_O &&
+     test_must_fail git update-index --refresh -q &&
      git diff-files >.test-a &&
      cmp_diff_files_output .test-a .test-recursive-OA'
 
@@ -215,8 +212,8 @@
     'rm -fr Z [A-Z][A-Z] &&
      git read-tree $tree_B &&
      git checkout-index -f -a &&
-     git read-tree --reset $tree_O || return 1
-     git update-index --refresh >/dev/null ;# this can exit non-zero
+     git read-tree --reset $tree_O &&
+     test_must_fail git update-index --refresh -q &&
      git diff-files >.test-a &&
      cmp_diff_files_output .test-a .test-recursive-OB'
 
@@ -225,8 +222,8 @@
     'rm -fr Z [A-Z][A-Z] &&
      git read-tree $tree_B &&
      git checkout-index -f -a &&
-     git read-tree --reset $tree_A || return 1
-     git update-index --refresh >/dev/null ;# this can exit non-zero
+     git read-tree --reset $tree_A &&
+     test_must_fail git update-index --refresh -q &&
      git diff-files >.test-a &&
      cmp_diff_files_output .test-a .test-recursive-AB'
 
diff --git a/t/t4003-diff-rename-1.sh b/t/t4003-diff-rename-1.sh
index c6130c4..bfa8835 100755
--- a/t/t4003-diff-rename-1.sh
+++ b/t/t4003-diff-rename-1.sh
@@ -29,7 +29,7 @@
 # copy-and-edit one, and rename-and-edit the other.  We do not say
 # anything about rezrov.
 
-GIT_DIFF_OPTS=--unified=0 git diff-index -M -p $tree >current
+GIT_DIFF_OPTS=--unified=0 git diff-index -C -p $tree >current
 cat >expected <<\EOF
 diff --git a/COPYING b/COPYING.1
 copy from COPYING
diff --git a/t/t4004-diff-rename-symlink.sh b/t/t4004-diff-rename-symlink.sh
index a4da119..6e562c8 100755
--- a/t/t4004-diff-rename-symlink.sh
+++ b/t/t4004-diff-rename-symlink.sh
@@ -12,13 +12,7 @@
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/diff-lib.sh
 
-if ! test_have_prereq SYMLINKS
-then
-	say 'Symbolic links not supported, skipping tests.'
-	test_done
-fi
-
-test_expect_success \
+test_expect_success SYMLINKS \
     'prepare reference tree' \
     'echo xyzzy | tr -d '\\\\'012 >yomin &&
      ln -s xyzzy frotz &&
@@ -26,7 +20,7 @@
     tree=$(git write-tree) &&
     echo $tree'
 
-test_expect_success \
+test_expect_success SYMLINKS \
     'prepare work tree' \
     'mv frotz rezrov &&
      rm -f yomin &&
@@ -40,8 +34,9 @@
 # rezrov and nitfol are rename/copy of frotz and bozbar should be
 # a new creation.
 
-GIT_DIFF_OPTS=--unified=0 git diff-index -M -p $tree >current
-cat >expected <<\EOF
+test_expect_success SYMLINKS 'setup diff output' "
+    GIT_DIFF_OPTS=--unified=0 git diff-index -C -p $tree >current &&
+    cat >expected <<\EOF
 diff --git a/bozbar b/bozbar
 new file mode 120000
 --- /dev/null
@@ -65,8 +60,9 @@
 -xyzzy
 \ No newline at end of file
 EOF
+"
 
-test_expect_success \
+test_expect_success SYMLINKS \
     'validate diff output' \
     'compare_diff_patch current expected'
 
diff --git a/t/t4005-diff-rename-2.sh b/t/t4005-diff-rename-2.sh
index 1ba359d..77d7f49 100755
--- a/t/t4005-diff-rename-2.sh
+++ b/t/t4005-diff-rename-2.sh
@@ -29,7 +29,7 @@
 # and COPYING.2 are based on COPYING, and do not say anything about
 # rezrov.
 
-git diff-index -M $tree >current
+git diff-index -C $tree >current
 
 cat >expected <<\EOF
 :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234	COPYING	COPYING.1
diff --git a/t/t4006-diff-mode.sh b/t/t4006-diff-mode.sh
index 8c1b81e..7a3e1f9 100755
--- a/t/t4006-diff-mode.sh
+++ b/t/t4006-diff-mode.sh
@@ -8,25 +8,52 @@
 '
 . ./test-lib.sh
 
-test_expect_success \
-    'setup' \
-    'echo frotz >rezrov &&
-     git update-index --add rezrov &&
-     tree=`git write-tree` &&
-     echo $tree'
+sed_script='s/\(:100644 100755\) \('"$_x40"'\) \2 /\1 X X /'
 
-test_expect_success \
-    'chmod' \
-    'test_chmod +x rezrov &&
-     git diff-index $tree >current'
+test_expect_success 'setup' '
+	echo frotz >rezrov &&
+	git update-index --add rezrov &&
+	tree=`git write-tree` &&
+	echo $tree
+'
 
-_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
-_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
-sed -e 's/\(:100644 100755\) \('"$_x40"'\) \2 /\1 X X /' <current >check
-echo ":100644 100755 X X M	rezrov" >expected
+test_expect_success 'chmod' '
+	test_chmod +x rezrov &&
+	git diff-index $tree >current &&
+	sed -e "$sed_script" <current >check &&
+	echo ":100644 100755 X X M	rezrov" >expected &&
+	test_cmp expected check
+'
 
-test_expect_success \
-    'verify' \
-    'test_cmp expected check'
+test_expect_success 'prepare binary file' '
+	git commit -m rezrov &&
+	printf "\00\01\02\03\04\05\06" >binbin &&
+	git add binbin &&
+	git commit -m binbin
+'
+
+test_expect_success '--stat output after text chmod' '
+	test_chmod -x rezrov &&
+	echo " 0 files changed" >expect &&
+	git diff HEAD --stat >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '--shortstat output after text chmod' '
+	git diff HEAD --shortstat >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '--stat output after binary chmod' '
+	test_chmod +x binbin &&
+	echo " 0 files changed" >expect &&
+	git diff HEAD --stat >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '--shortstat output after binary chmod' '
+	git diff HEAD --shortstat >actual &&
+	test_cmp expect actual
+'
 
 test_done
diff --git a/t/t4008-diff-break-rewrite.sh b/t/t4008-diff-break-rewrite.sh
index e19ca65..73b4a24 100755
--- a/t/t4008-diff-break-rewrite.sh
+++ b/t/t4008-diff-break-rewrite.sh
@@ -155,7 +155,7 @@
      git checkout-index -f -u -a &&
      sed -e "s/git/GIT/" file0 >file1 &&
      sed -e "s/git/GET/" file0 >file2 &&
-     rm -f file0
+     rm -f file0 &&
      git update-index --add --remove file0 file1 file2'
 
 test_expect_success \
@@ -173,8 +173,8 @@
     'compare_diff_raw expected current'
 
 test_expect_success \
-    'run diff with -B -M' \
-    'git diff-index -B -M "$tree" >current'
+    'run diff with -B -C' \
+    'git diff-index -B -C "$tree" >current'
 
 cat >expected <<\EOF
 :100644 100644 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 08bb2fb671deff4c03a4d4a0a1315dff98d5732c C095	file0	file1
diff --git a/t/t4009-diff-rename-4.sh b/t/t4009-diff-rename-4.sh
index de3f174..f22c8e3 100755
--- a/t/t4009-diff-rename-4.sh
+++ b/t/t4009-diff-rename-4.sh
@@ -29,7 +29,7 @@
 # and COPYING.2 are based on COPYING, and do not say anything about
 # rezrov.
 
-git diff-index -z -M $tree >current
+git diff-index -z -C $tree >current
 
 cat >expected <<\EOF
 :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234
diff --git a/t/t4010-diff-pathspec.sh b/t/t4010-diff-pathspec.sh
index 94df7ae..af5134b 100755
--- a/t/t4010-diff-pathspec.sh
+++ b/t/t4010-diff-pathspec.sh
@@ -48,6 +48,14 @@
      compare_diff_raw current expected'
 
 cat >expected <<\EOF
+:100644 100644 766498d93a4b06057a8e49d23f4068f1170ff38f 0a41e115ab61be0328a19b29f18cdcb49338d516 M	path1/file1
+EOF
+test_expect_success \
+    '"*file1" should show path1/file1' \
+    'git diff-index --cached $tree -- "*file1" >current &&
+     compare_diff_raw current expected'
+
+cat >expected <<\EOF
 :100644 100644 766498d93a4b06057a8e49d23f4068f1170ff38f 0a41e115ab61be0328a19b29f18cdcb49338d516 M	file0
 EOF
 test_expect_success \
@@ -70,4 +78,36 @@
 	test_cmp expected current
 '
 
+EMPTY_TREE=4b825dc642cb6eb9a060e54bf8d69288fbee4904
+
+test_expect_success 'diff-tree with wildcard shows dir also matches' '
+	git diff-tree --name-only $EMPTY_TREE $tree -- "f*" >result &&
+	echo file0 >expected &&
+	test_cmp expected result
+'
+
+test_expect_success 'diff-tree -r with wildcard' '
+	git diff-tree -r --name-only $EMPTY_TREE $tree -- "*file1" >result &&
+	echo path1/file1 >expected &&
+	test_cmp expected result
+'
+
+test_expect_success 'diff-tree with wildcard shows dir also matches' '
+	git diff-tree --name-only $tree $tree2 -- "path1/f*" >result &&
+	echo path1 >expected &&
+	test_cmp expected result
+'
+
+test_expect_success 'diff-tree -r with wildcard from beginning' '
+	git diff-tree -r --name-only $tree $tree2 -- "path1/*file1" >result &&
+	echo path1/file1 >expected &&
+	test_cmp expected result
+'
+
+test_expect_success 'diff-tree -r with wildcard' '
+	git diff-tree -r --name-only $tree $tree2 -- "path1/f*" >result &&
+	echo path1/file1 >expected &&
+	test_cmp expected result
+'
+
 test_done
diff --git a/t/t4011-diff-symlink.sh b/t/t4011-diff-symlink.sh
index d7e327c..f0d5041 100755
--- a/t/t4011-diff-symlink.sh
+++ b/t/t4011-diff-symlink.sh
@@ -9,90 +9,134 @@
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/diff-lib.sh
 
-if ! test_have_prereq SYMLINKS
-then
-	say 'Symbolic links not supported, skipping tests.'
-	test_done
-fi
+test_expect_success SYMLINKS 'diff new symlink and file' '
+	cat >expected <<-\EOF &&
+	diff --git a/frotz b/frotz
+	new file mode 120000
+	index 0000000..7c465af
+	--- /dev/null
+	+++ b/frotz
+	@@ -0,0 +1 @@
+	+xyzzy
+	\ No newline at end of file
+	diff --git a/nitfol b/nitfol
+	new file mode 100644
+	index 0000000..7c465af
+	--- /dev/null
+	+++ b/nitfol
+	@@ -0,0 +1 @@
+	+xyzzy
+	EOF
+	ln -s xyzzy frotz &&
+	echo xyzzy >nitfol &&
+	git update-index &&
+	tree=$(git write-tree) &&
+	git update-index --add frotz nitfol &&
+	GIT_DIFF_OPTS=--unified=0 git diff-index -M -p $tree >current &&
+	compare_diff_patch expected current
+'
 
-cat > expected << EOF
-diff --git a/frotz b/frotz
-new file mode 120000
-index 0000000..7c465af
---- /dev/null
-+++ b/frotz
-@@ -0,0 +1 @@
-+xyzzy
-\ No newline at end of file
-EOF
+test_expect_success SYMLINKS 'diff unchanged symlink and file'  '
+	tree=$(git write-tree) &&
+	git update-index frotz nitfol &&
+	test -z "$(git diff-index --name-only $tree)"
+'
 
-test_expect_success \
-    'diff new symlink' \
-    'ln -s xyzzy frotz &&
-    git update-index &&
-    tree=$(git write-tree) &&
-    git update-index --add frotz &&
-    GIT_DIFF_OPTS=--unified=0 git diff-index -M -p $tree > current &&
-    compare_diff_patch current expected'
+test_expect_success SYMLINKS 'diff removed symlink and file' '
+	cat >expected <<-\EOF &&
+	diff --git a/frotz b/frotz
+	deleted file mode 120000
+	index 7c465af..0000000
+	--- a/frotz
+	+++ /dev/null
+	@@ -1 +0,0 @@
+	-xyzzy
+	\ No newline at end of file
+	diff --git a/nitfol b/nitfol
+	deleted file mode 100644
+	index 7c465af..0000000
+	--- a/nitfol
+	+++ /dev/null
+	@@ -1 +0,0 @@
+	-xyzzy
+	EOF
+	mv frotz frotz2 &&
+	mv nitfol nitfol2 &&
+	git diff-index -M -p $tree >current &&
+	compare_diff_patch expected current
+'
 
-test_expect_success \
-    'diff unchanged symlink' \
-    'tree=$(git write-tree) &&
-    git update-index frotz &&
-    test -z "$(git diff-index --name-only $tree)"'
+test_expect_success SYMLINKS 'diff identical, but newly created symlink and file' '
+	>expected &&
+	rm -f frotz nitfol &&
+	echo xyzzy >nitfol &&
+	test-chmtime +10 nitfol &&
+	ln -s xyzzy frotz &&
+	git diff-index -M -p $tree >current &&
+	compare_diff_patch expected current &&
 
-cat > expected << EOF
-diff --git a/frotz b/frotz
-deleted file mode 120000
-index 7c465af..0000000
---- a/frotz
-+++ /dev/null
-@@ -1 +0,0 @@
--xyzzy
-\ No newline at end of file
-EOF
+	>expected &&
+	git diff-index -M -p -w $tree >current &&
+	compare_diff_patch expected current
+'
 
-test_expect_success \
-    'diff removed symlink' \
-    'rm frotz &&
-    git diff-index -M -p $tree > current &&
-    compare_diff_patch current expected'
+test_expect_success SYMLINKS 'diff different symlink and file' '
+	cat >expected <<-\EOF &&
+	diff --git a/frotz b/frotz
+	index 7c465af..df1db54 120000
+	--- a/frotz
+	+++ b/frotz
+	@@ -1 +1 @@
+	-xyzzy
+	\ No newline at end of file
+	+yxyyz
+	\ No newline at end of file
+	diff --git a/nitfol b/nitfol
+	index 7c465af..df1db54 100644
+	--- a/nitfol
+	+++ b/nitfol
+	@@ -1 +1 @@
+	-xyzzy
+	+yxyyz
+	EOF
+	rm -f frotz &&
+	ln -s yxyyz frotz &&
+	echo yxyyz >nitfol &&
+	git diff-index -M -p $tree >current &&
+	compare_diff_patch expected current
+'
 
-cat > expected << EOF
-diff --git a/frotz b/frotz
-EOF
+test_expect_success SYMLINKS 'diff symlinks with non-existing targets' '
+	ln -s narf pinky &&
+	ln -s take\ over brain &&
+	test_must_fail git diff --no-index pinky brain >output 2>output.err &&
+	grep narf output &&
+	! test -s output.err
+'
 
-test_expect_success \
-    'diff identical, but newly created symlink' \
-    'sleep 3 &&
-    ln -s xyzzy frotz &&
-    git diff-index -M -p $tree > current &&
-    compare_diff_patch current expected'
+test_expect_success SYMLINKS 'setup symlinks with attributes' '
+	echo "*.bin diff=bin" >>.gitattributes &&
+	echo content >file.bin &&
+	ln -s file.bin link.bin &&
+	git add -N file.bin link.bin
+'
 
-cat > expected << EOF
-diff --git a/frotz b/frotz
-index 7c465af..df1db54 120000
---- a/frotz
-+++ b/frotz
-@@ -1 +1 @@
--xyzzy
-\ No newline at end of file
-+yxyyz
-\ No newline at end of file
-EOF
+test_expect_success SYMLINKS 'symlinks do not respect userdiff config by path' '
+	cat >expect <<-\EOF &&
+	diff --git a/file.bin b/file.bin
+	index e69de29..d95f3ad 100644
+	Binary files a/file.bin and b/file.bin differ
+	diff --git a/link.bin b/link.bin
+	index e69de29..dce41ec 120000
+	--- a/link.bin
+	+++ b/link.bin
+	@@ -0,0 +1 @@
+	+file.bin
+	\ No newline at end of file
+	EOF
+	git config diff.bin.binary true &&
+	git diff file.bin link.bin >actual &&
+	test_cmp expect actual
+'
 
-test_expect_success \
-    'diff different symlink' \
-    'rm frotz &&
-    ln -s yxyyz frotz &&
-    git diff-index -M -p $tree > current &&
-    compare_diff_patch current expected'
-
-test_expect_success \
-    'diff symlinks with non-existing targets' \
-    'ln -s narf pinky &&
-    ln -s take\ over brain &&
-    test_must_fail git diff --no-index pinky brain > output 2> output.err &&
-    grep narf output &&
-    ! grep error output.err'
 test_done
diff --git a/t/t4012-diff-binary.sh b/t/t4012-diff-binary.sh
index f64aa48..ec4deea 100755
--- a/t/t4012-diff-binary.sh
+++ b/t/t4012-diff-binary.sh
@@ -8,13 +8,21 @@
 
 . ./test-lib.sh
 
-test_expect_success 'prepare repository' \
-	'echo AIT >a && echo BIT >b && echo CIT >c && echo DIT >d &&
-	 git update-index --add a b c d &&
-	 echo git >a &&
-	 cat "$TEST_DIRECTORY"/test4012.png >b &&
-	 echo git >c &&
-	 cat b b >d'
+cat >expect.binary-numstat <<\EOF
+1	1	a
+-	-	b
+1	1	c
+-	-	d
+EOF
+
+test_expect_success 'prepare repository' '
+	echo AIT >a && echo BIT >b && echo CIT >c && echo DIT >d &&
+	git update-index --add a b c d &&
+	echo git >a &&
+	cat "$TEST_DIRECTORY"/test-binary-1.png >b &&
+	echo git >c &&
+	cat b b >d
+'
 
 cat > expected <<\EOF
  a |    2 +-
@@ -23,67 +31,74 @@
  d |  Bin
  4 files changed, 2 insertions(+), 2 deletions(-)
 EOF
-test_expect_success 'diff without --binary' \
-	'git diff | git apply --stat --summary >current &&
-	 test_cmp expected current'
+test_expect_success 'apply --stat output for binary file change' '
+	git diff >diff &&
+	git apply --stat --summary <diff >current &&
+	test_i18ncmp expected current
+'
 
-test_expect_success 'diff with --binary' \
-	'git diff --binary | git apply --stat --summary >current &&
-	 test_cmp expected current'
+test_expect_success 'diff --shortstat output for binary file change' '
+	tail -n 1 expected >expect &&
+	git diff --shortstat >current &&
+	test_i18ncmp expect current
+'
+
+test_expect_success 'diff --shortstat output for binary file change only' '
+	echo " 1 file changed, 0 insertions(+), 0 deletions(-)" >expected &&
+	git diff --shortstat -- b >current &&
+	test_i18ncmp expected current
+'
+
+test_expect_success 'apply --numstat notices binary file change' '
+	git diff >diff &&
+	git apply --numstat <diff >current &&
+	test_cmp expect.binary-numstat current
+'
+
+test_expect_success 'apply --numstat understands diff --binary format' '
+	git diff --binary >diff &&
+	git apply --numstat <diff >current &&
+	test_cmp expect.binary-numstat current
+'
 
 # apply needs to be able to skip the binary material correctly
 # in order to report the line number of a corrupt patch.
-test_expect_success 'apply detecting corrupt patch correctly' \
-	'git diff | sed -e 's/-CIT/xCIT/' >broken &&
-	 if git apply --stat --summary broken 2>detected
-	 then
-		echo unhappy - should have detected an error
-		(exit 1)
-	 else
-		echo happy
-	 fi &&
-	 detected=`cat detected` &&
-	 detected=`expr "$detected" : "fatal.*at line \\([0-9]*\\)\$"` &&
-	 detected=`sed -ne "${detected}p" broken` &&
-	 test "$detected" = xCIT'
+test_expect_success 'apply detecting corrupt patch correctly' '
+	git diff >output &&
+	sed -e "s/-CIT/xCIT/" <output >broken &&
+	test_must_fail git apply --stat --summary broken 2>detected &&
+	detected=`cat detected` &&
+	detected=`expr "$detected" : "fatal.*at line \\([0-9]*\\)\$"` &&
+	detected=`sed -ne "${detected}p" broken` &&
+	test "$detected" = xCIT
+'
 
-test_expect_success 'apply detecting corrupt patch correctly' \
-	'git diff --binary | sed -e 's/-CIT/xCIT/' >broken &&
-	 if git apply --stat --summary broken 2>detected
-	 then
-		echo unhappy - should have detected an error
-		(exit 1)
-	 else
-		echo happy
-	 fi &&
-	 detected=`cat detected` &&
-	 detected=`expr "$detected" : "fatal.*at line \\([0-9]*\\)\$"` &&
-	 detected=`sed -ne "${detected}p" broken` &&
-	 test "$detected" = xCIT'
+test_expect_success 'apply detecting corrupt patch correctly' '
+	git diff --binary | sed -e "s/-CIT/xCIT/" >broken &&
+	test_must_fail git apply --stat --summary broken 2>detected &&
+	detected=`cat detected` &&
+	detected=`expr "$detected" : "fatal.*at line \\([0-9]*\\)\$"` &&
+	detected=`sed -ne "${detected}p" broken` &&
+	test "$detected" = xCIT
+'
 
 test_expect_success 'initial commit' 'git commit -a -m initial'
 
 # Try removal (b), modification (d), and creation (e).
-test_expect_success 'diff-index with --binary' \
-	'echo AIT >a && mv b e && echo CIT >c && cat e >d &&
-	 git update-index --add --remove a b c d e &&
-	 tree0=`git write-tree` &&
-	 git diff --cached --binary >current &&
-	 git apply --stat --summary current'
+test_expect_success 'diff-index with --binary' '
+	echo AIT >a && mv b e && echo CIT >c && cat e >d &&
+	git update-index --add --remove a b c d e &&
+	tree0=`git write-tree` &&
+	git diff --cached --binary >current &&
+	git apply --stat --summary current
+'
 
-test_expect_success 'apply binary patch' \
-	'git reset --hard &&
-	 git apply --binary --index <current &&
-	 tree1=`git write-tree` &&
-	 test "$tree1" = "$tree0"'
-
-q_to_nul() {
-	perl -pe 'y/Q/\000/'
-}
-
-nul_to_q() {
-	perl -pe 'y/\000/Q/'
-}
+test_expect_success 'apply binary patch' '
+	git reset --hard &&
+	git apply --binary --index <current &&
+	tree1=`git write-tree` &&
+	test "$tree1" = "$tree0"
+'
 
 test_expect_success 'diff --no-index with binary creation' '
 	echo Q | q_to_nul >binary &&
@@ -98,4 +113,23 @@
 	test_cmp expected actual
 '
 
+cat >expect <<EOF
+ binfile  |   Bin 0 -> 1026 bytes
+ textfile | 10000 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+EOF
+
+test_expect_success 'diff --stat with binary files and big change count' '
+	printf "\01\00%1024d" 1 >binfile &&
+	git add binfile &&
+	i=0 &&
+	while test $i -lt 10000; do
+		echo $i &&
+		i=$(($i + 1))
+	done >textfile &&
+	git add textfile &&
+	git diff --cached --stat binfile textfile >output &&
+	grep " | " output >actual &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh
index 8b33321..e77c09c 100755
--- a/t/t4013-diff-various.sh
+++ b/t/t4013-diff-various.sh
@@ -80,18 +80,31 @@
 
 	git config log.showroot false &&
 	git commit --amend &&
+
+	GIT_AUTHOR_DATE="2006-06-26 00:06:00 +0000" &&
+	GIT_COMMITTER_DATE="2006-06-26 00:06:00 +0000" &&
+	export GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&
+	git checkout -b rearrange initial &&
+	for i in B A; do echo $i; done >dir/sub &&
+	git add dir/sub &&
+	git commit -m "Rearranged lines in dir/sub" &&
+	git checkout master &&
+
 	git show-branch
 '
 
 : <<\EOF
 ! [initial] Initial
  * [master] Merge branch 'side'
-  ! [side] Side
----
- -  [master] Merge branch 'side'
- *+ [side] Side
- *  [master^] Second
-+*+ [initial] Initial
+  ! [rearrange] Rearranged lines in dir/sub
+   ! [side] Side
+----
+  +  [rearrange] Rearranged lines in dir/sub
+ -   [master] Merge branch 'side'
+ * + [side] Side
+ *   [master^] Third
+ *   [master~2] Second
++*++ [initial] Initial
 EOF
 
 V=`git version | sed -e 's/^git version //' -e 's/\./\\./g'`
@@ -115,7 +128,12 @@
 		} >"$actual" &&
 		if test -f "$expect"
 		then
-			test_cmp "$expect" "$actual" &&
+			case $cmd in
+			*format-patch* | *-stat*)
+				test_i18ncmp "$expect" "$actual";;
+			*)
+				test_cmp "$expect" "$actual";;
+			esac &&
 			rm -f "$actual"
 		else
 			# this is to help developing new tests.
@@ -204,9 +222,20 @@
 log --root -c --patch-with-stat --summary master
 # improved by Timo's patch
 log --root --cc --patch-with-stat --summary master
+log -p --first-parent master
+log -m -p --first-parent master
+log -m -p master
 log -SF master
+log -S F master
 log -SF -p master
+log -SF master --max-count=0
+log -SF master --max-count=1
+log -SF master --max-count=2
+log -GF master
+log -GF -p master
+log -GF -p --pickaxe-all master
 log --decorate --all
+log --decorate=full --all
 
 rev-list --parents HEAD
 rev-list --children HEAD
@@ -234,6 +263,9 @@
 show --root initial
 show side
 show master
+show -c master
+show -m master
+show --first-parent master
 show --stat side
 show --stat --summary side
 show --patch-with-stat side
@@ -273,6 +305,23 @@
 diff --no-index dir dir3
 diff master master^ side
 diff --dirstat master~1 master~2
+diff --dirstat initial rearrange
+diff --dirstat-by-file initial rearrange
 EOF
 
+test_expect_success 'log -S requires an argument' '
+	test_must_fail git log -S
+'
+
+test_expect_success 'diff --cached on unborn branch' '
+	echo ref: refs/heads/unborn >.git/HEAD &&
+	git diff --cached >result &&
+	test_cmp "$TEST_DIRECTORY/t4013/diff.diff_--cached" result
+'
+
+test_expect_success 'diff --cached -- file on unborn branch' '
+	git diff --cached -- file0 >result &&
+	test_cmp "$TEST_DIRECTORY/t4013/diff.diff_--cached_--_file0" result
+'
+
 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
index 3a9f78a..9951e36 100644
--- a/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_master
@@ -1,8 +1,8 @@
 $ git diff-tree --cc --patch-with-stat --summary master
 59d314ad6f356dd08601a4cd5e530381da3e3c64
- dir/sub |    2 ++
- file0   |    3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ 2 files changed, 5 insertions(+)
 
 diff --cc dir/sub
 index cead32e,7289e35..992913c
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
index a61ad8c..cec33fa 100644
--- a/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_side
+++ b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_side
@@ -1,9 +1,9 @@
 $ git diff-tree --cc --patch-with-stat --summary side
 c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
+ 3 files changed, 9 insertions(+)
  create mode 100644 file3
 
 diff --git a/dir/sub b/dir/sub
diff --git a/t/t4013/diff.diff-tree_--cc_--patch-with-stat_master b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_master
index 49f23b9..db3c0a7 100644
--- a/t/t4013/diff.diff-tree_--cc_--patch-with-stat_master
+++ b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_master
@@ -1,8 +1,8 @@
 $ git diff-tree --cc --patch-with-stat master
 59d314ad6f356dd08601a4cd5e530381da3e3c64
- dir/sub |    2 ++
- file0   |    3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ 2 files changed, 5 insertions(+)
 
 diff --cc dir/sub
 index cead32e,7289e35..992913c
diff --git a/t/t4013/diff.diff-tree_--cc_--stat_--summary_master b/t/t4013/diff.diff-tree_--cc_--stat_--summary_master
index cc6eb3b..d019867 100644
--- a/t/t4013/diff.diff-tree_--cc_--stat_--summary_master
+++ b/t/t4013/diff.diff-tree_--cc_--stat_--summary_master
@@ -1,6 +1,6 @@
 $ git diff-tree --cc --stat --summary master
 59d314ad6f356dd08601a4cd5e530381da3e3c64
- dir/sub |    2 ++
- file0   |    3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ 2 files changed, 5 insertions(+)
 $
diff --git a/t/t4013/diff.diff-tree_--cc_--stat_--summary_side b/t/t4013/diff.diff-tree_--cc_--stat_--summary_side
index 50362be..12b2eee 100644
--- a/t/t4013/diff.diff-tree_--cc_--stat_--summary_side
+++ b/t/t4013/diff.diff-tree_--cc_--stat_--summary_side
@@ -1,8 +1,8 @@
 $ git diff-tree --cc --stat --summary side
 c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
+ 3 files changed, 9 insertions(+)
  create mode 100644 file3
 $
diff --git a/t/t4013/diff.diff-tree_--cc_--stat_master b/t/t4013/diff.diff-tree_--cc_--stat_master
index fae7f33..40b9179 100644
--- a/t/t4013/diff.diff-tree_--cc_--stat_master
+++ b/t/t4013/diff.diff-tree_--cc_--stat_master
@@ -1,6 +1,6 @@
 $ git diff-tree --cc --stat master
 59d314ad6f356dd08601a4cd5e530381da3e3c64
- dir/sub |    2 ++
- file0   |    3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ 2 files changed, 5 insertions(+)
 $
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
index d5c333a..817ed06 100644
--- 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
@@ -1,9 +1,9 @@
 $ 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(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 +++
+ 3 files changed, 8 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
 new file mode 100644
diff --git a/t/t4013/diff.diff-tree_--pretty_--patch-with-stat_side b/t/t4013/diff.diff-tree_--pretty_--patch-with-stat_side
index 4d30e7e..fe3f6b7 100644
--- a/t/t4013/diff.diff-tree_--pretty_--patch-with-stat_side
+++ b/t/t4013/diff.diff-tree_--pretty_--patch-with-stat_side
@@ -5,10 +5,10 @@
 
     Side
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
+ 3 files changed, 9 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..7289e35 100644
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
index 7dfa6af..06eb77e 100644
--- a/t/t4013/diff.diff-tree_--pretty_--root_--patch-with-stat_initial
+++ b/t/t4013/diff.diff-tree_--pretty_--root_--patch-with-stat_initial
@@ -5,10 +5,10 @@
 
     Initial
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 +++
- 3 files changed, 8 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 +++
+ 3 files changed, 8 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
 new file mode 100644
diff --git a/t/t4013/diff.diff-tree_--pretty_--root_--stat_--summary_initial b/t/t4013/diff.diff-tree_--pretty_--root_--stat_--summary_initial
index 43bfce2..680eab5 100644
--- a/t/t4013/diff.diff-tree_--pretty_--root_--stat_--summary_initial
+++ b/t/t4013/diff.diff-tree_--pretty_--root_--stat_--summary_initial
@@ -5,10 +5,10 @@
 
     Initial
 
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 +++
- 3 files changed, 8 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 +++
+ 3 files changed, 8 insertions(+)
  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
index 9154aa4..9722d1b 100644
--- a/t/t4013/diff.diff-tree_--pretty_--root_--stat_initial
+++ b/t/t4013/diff.diff-tree_--pretty_--root_--stat_initial
@@ -5,8 +5,8 @@
 
     Initial
 
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 +++
- 3 files changed, 8 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 +++
+ 3 files changed, 8 insertions(+)
 $
diff --git a/t/t4013/diff.diff-tree_--root_--patch-with-stat_initial b/t/t4013/diff.diff-tree_--root_--patch-with-stat_initial
index 1562b62..ad69ffe 100644
--- a/t/t4013/diff.diff-tree_--root_--patch-with-stat_initial
+++ b/t/t4013/diff.diff-tree_--root_--patch-with-stat_initial
@@ -1,9 +1,9 @@
 $ git diff-tree --root --patch-with-stat initial
 444ac553ac7612cc88969031b02b3767fb8a353a
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 +++
- 3 files changed, 8 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 +++
+ 3 files changed, 8 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
 new file mode 100644
diff --git a/t/t4013/diff.diff-tree_-c_--stat_--summary_master b/t/t4013/diff.diff-tree_-c_--stat_--summary_master
index ac9f641..81c3021 100644
--- a/t/t4013/diff.diff-tree_-c_--stat_--summary_master
+++ b/t/t4013/diff.diff-tree_-c_--stat_--summary_master
@@ -1,6 +1,6 @@
 $ git diff-tree -c --stat --summary master
 59d314ad6f356dd08601a4cd5e530381da3e3c64
- dir/sub |    2 ++
- file0   |    3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ 2 files changed, 5 insertions(+)
 $
diff --git a/t/t4013/diff.diff-tree_-c_--stat_--summary_side b/t/t4013/diff.diff-tree_-c_--stat_--summary_side
index 2afcca1..e8dc12b 100644
--- a/t/t4013/diff.diff-tree_-c_--stat_--summary_side
+++ b/t/t4013/diff.diff-tree_-c_--stat_--summary_side
@@ -1,8 +1,8 @@
 $ git diff-tree -c --stat --summary side
 c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
+ 3 files changed, 9 insertions(+)
  create mode 100644 file3
 $
diff --git a/t/t4013/diff.diff-tree_-c_--stat_master b/t/t4013/diff.diff-tree_-c_--stat_master
index c2fe6a9..89d59b1 100644
--- a/t/t4013/diff.diff-tree_-c_--stat_master
+++ b/t/t4013/diff.diff-tree_-c_--stat_master
@@ -1,6 +1,6 @@
 $ git diff-tree -c --stat master
 59d314ad6f356dd08601a4cd5e530381da3e3c64
- dir/sub |    2 ++
- file0   |    3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ 2 files changed, 5 insertions(+)
 $
diff --git a/t/t4013/diff.diff_--cached b/t/t4013/diff.diff_--cached
new file mode 100644
index 0000000..ff16e83
--- /dev/null
+++ b/t/t4013/diff.diff_--cached
@@ -0,0 +1,38 @@
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..992913c
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,8 @@
++A
++B
++C
++D
++E
++F
++1
++2
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..10a8a9f
+--- /dev/null
++++ b/file0
+@@ -0,0 +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/t/t4013/diff.diff_--cached_--_file0 b/t/t4013/diff.diff_--cached_--_file0
new file mode 100644
index 0000000..b9bb858
--- /dev/null
+++ b/t/t4013/diff.diff_--cached_--_file0
@@ -0,0 +1,15 @@
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..10a8a9f
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,9 @@
++1
++2
++3
++4
++5
++6
++A
++B
++C
diff --git a/t/t4013/diff.diff_--dirstat-by-file_initial_rearrange b/t/t4013/diff.diff_--dirstat-by-file_initial_rearrange
new file mode 100644
index 0000000..e48e33f
--- /dev/null
+++ b/t/t4013/diff.diff_--dirstat-by-file_initial_rearrange
@@ -0,0 +1,3 @@
+$ git diff --dirstat-by-file initial rearrange
+ 100.0% dir/
+$
diff --git a/t/t4013/diff.diff_--dirstat_initial_rearrange b/t/t4013/diff.diff_--dirstat_initial_rearrange
new file mode 100644
index 0000000..5fb02c1
--- /dev/null
+++ b/t/t4013/diff.diff_--dirstat_initial_rearrange
@@ -0,0 +1,3 @@
+$ git diff --dirstat initial rearrange
+ 100.0% dir/
+$
diff --git a/t/t4013/diff.diff_--patch-with-stat_-r_initial..side b/t/t4013/diff.diff_--patch-with-stat_-r_initial..side
index 9ed317a..be8d1ea 100644
--- a/t/t4013/diff.diff_--patch-with-stat_-r_initial..side
+++ b/t/t4013/diff.diff_--patch-with-stat_-r_initial..side
@@ -1,8 +1,8 @@
 $ git diff --patch-with-stat -r initial..side
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
+ 3 files changed, 9 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..7289e35 100644
diff --git a/t/t4013/diff.diff_--patch-with-stat_initial..side b/t/t4013/diff.diff_--patch-with-stat_initial..side
index 8b50629..5424e6d 100644
--- a/t/t4013/diff.diff_--patch-with-stat_initial..side
+++ b/t/t4013/diff.diff_--patch-with-stat_initial..side
@@ -1,8 +1,8 @@
 $ git diff --patch-with-stat initial..side
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
+ 3 files changed, 9 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..7289e35 100644
diff --git a/t/t4013/diff.diff_--stat_initial..side b/t/t4013/diff.diff_--stat_initial..side
index 0517b5d..b7741e2 100644
--- a/t/t4013/diff.diff_--stat_initial..side
+++ b/t/t4013/diff.diff_--stat_initial..side
@@ -1,6 +1,6 @@
 $ git diff --stat initial..side
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
+ 3 files changed, 9 insertions(+)
 $
diff --git a/t/t4013/diff.diff_-r_--stat_initial..side b/t/t4013/diff.diff_-r_--stat_initial..side
index 245220d..5d514f5 100644
--- a/t/t4013/diff.diff_-r_--stat_initial..side
+++ b/t/t4013/diff.diff_-r_--stat_initial..side
@@ -1,6 +1,6 @@
 $ git diff -r --stat initial..side
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
+ 3 files changed, 9 insertions(+)
 $
diff --git a/t/t4013/diff.format-patch_--attach_--stdout_--suffix=.diff_initial..side b/t/t4013/diff.format-patch_--attach_--stdout_--suffix=.diff_initial..side
index 52116d3..547ca06 100644
--- a/t/t4013/diff.format-patch_--attach_--stdout_--suffix=.diff_initial..side
+++ b/t/t4013/diff.format-patch_--attach_--stdout_--suffix=.diff_initial..side
@@ -12,10 +12,10 @@
 Content-Transfer-Encoding: 8bit
 
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
+ 3 files changed, 9 insertions(+)
  create mode 100644 file3
 
 
diff --git a/t/t4013/diff.format-patch_--attach_--stdout_initial..master b/t/t4013/diff.format-patch_--attach_--stdout_initial..master
index ce49bd6..52fedc1 100644
--- a/t/t4013/diff.format-patch_--attach_--stdout_initial..master
+++ b/t/t4013/diff.format-patch_--attach_--stdout_initial..master
@@ -14,9 +14,9 @@
 
 This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
  delete mode 100644 file2
 
@@ -73,9 +73,9 @@
 Content-Transfer-Encoding: 8bit
 
 ---
- dir/sub |    2 ++
- file1   |    3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file1   | 3 +++
+ 2 files changed, 5 insertions(+)
  create mode 100644 file1
 
 
@@ -121,10 +121,10 @@
 Content-Transfer-Encoding: 8bit
 
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
+ 3 files changed, 9 insertions(+)
  create mode 100644 file3
 
 
diff --git a/t/t4013/diff.format-patch_--attach_--stdout_initial..master^ b/t/t4013/diff.format-patch_--attach_--stdout_initial..master^
index 5f1b238..1c3cde2 100644
--- a/t/t4013/diff.format-patch_--attach_--stdout_initial..master^
+++ b/t/t4013/diff.format-patch_--attach_--stdout_initial..master^
@@ -14,9 +14,9 @@
 
 This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
  delete mode 100644 file2
 
@@ -73,9 +73,9 @@
 Content-Transfer-Encoding: 8bit
 
 ---
- dir/sub |    2 ++
- file1   |    3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file1   | 3 +++
+ 2 files changed, 5 insertions(+)
  create mode 100644 file1
 
 
diff --git a/t/t4013/diff.format-patch_--attach_--stdout_initial..side b/t/t4013/diff.format-patch_--attach_--stdout_initial..side
index 4a2364a..4717bd8 100644
--- a/t/t4013/diff.format-patch_--attach_--stdout_initial..side
+++ b/t/t4013/diff.format-patch_--attach_--stdout_initial..side
@@ -12,10 +12,10 @@
 Content-Transfer-Encoding: 8bit
 
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
+ 3 files changed, 9 insertions(+)
  create mode 100644 file3
 
 
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..master b/t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..master
index 43b81eb..02c4db7 100644
--- a/t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..master
+++ b/t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..master
@@ -14,9 +14,9 @@
 
 This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
  delete mode 100644 file2
 
@@ -73,9 +73,9 @@
 Content-Transfer-Encoding: 8bit
 
 ---
- dir/sub |    2 ++
- file1   |    3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file1   | 3 +++
+ 2 files changed, 5 insertions(+)
  create mode 100644 file1
 
 
@@ -121,10 +121,10 @@
 Content-Transfer-Encoding: 8bit
 
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
+ 3 files changed, 9 insertions(+)
  create mode 100644 file3
 
 
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master b/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master
index ca3f60b..c7677c5 100644
--- a/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master
+++ b/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master
@@ -14,9 +14,9 @@
 
 This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
  delete mode 100644 file2
 
@@ -73,9 +73,9 @@
 Content-Transfer-Encoding: 8bit
 
 ---
- dir/sub |    2 ++
- file1   |    3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file1   | 3 +++
+ 2 files changed, 5 insertions(+)
  create mode 100644 file1
 
 
@@ -121,10 +121,10 @@
 Content-Transfer-Encoding: 8bit
 
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
+ 3 files changed, 9 insertions(+)
  create mode 100644 file3
 
 
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..master b/t/t4013/diff.format-patch_--inline_--stdout_initial..master
index 08f2301..5b3e34e 100644
--- a/t/t4013/diff.format-patch_--inline_--stdout_initial..master
+++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master
@@ -14,9 +14,9 @@
 
 This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
  delete mode 100644 file2
 
@@ -73,9 +73,9 @@
 Content-Transfer-Encoding: 8bit
 
 ---
- dir/sub |    2 ++
- file1   |    3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file1   | 3 +++
+ 2 files changed, 5 insertions(+)
  create mode 100644 file1
 
 
@@ -121,10 +121,10 @@
 Content-Transfer-Encoding: 8bit
 
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
+ 3 files changed, 9 insertions(+)
  create mode 100644 file3
 
 
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master^
index 07f1230..d13f8a8 100644
--- a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^
+++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master^
@@ -14,9 +14,9 @@
 
 This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
  delete mode 100644 file2
 
@@ -73,9 +73,9 @@
 Content-Transfer-Encoding: 8bit
 
 ---
- dir/sub |    2 ++
- file1   |    3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file1   | 3 +++
+ 2 files changed, 5 insertions(+)
  create mode 100644 file1
 
 
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^
index 29e00ab..caec553 100644
--- a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^
+++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^
@@ -14,9 +14,9 @@
 
 This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
  delete mode 100644 file2
 
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..side b/t/t4013/diff.format-patch_--inline_--stdout_initial..side
index 67633d4..d3a6762 100644
--- a/t/t4013/diff.format-patch_--inline_--stdout_initial..side
+++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..side
@@ -12,10 +12,10 @@
 Content-Transfer-Encoding: 8bit
 
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
+ 3 files changed, 9 insertions(+)
  create mode 100644 file3
 
 
diff --git a/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^ b/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^
index 8dab4bf..244d964 100644
--- a/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^
+++ b/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^
@@ -1,7 +1,7 @@
 $ git format-patch --stdout --cover-letter -n initial..master^
 From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
 From: C O Mitter <committer@example.com>
-Date: Mon, 26 Jun 2006 00:05:00 +0000
+Date: Mon, 26 Jun 2006 00:06:00 +0000
 Subject: [DIFFERENT_PREFIX 0/2] *** SUBJECT HERE ***
 
 *** BLURB HERE ***
@@ -10,14 +10,17 @@
   Second
   Third
 
- dir/sub |    4 ++++
- file0   |    3 +++
- file1   |    3 +++
- file2   |    3 ---
+ dir/sub | 4 ++++
+ file0   | 3 +++
+ file1   | 3 +++
+ file2   | 3 ---
  4 files changed, 10 insertions(+), 3 deletions(-)
  create mode 100644 file1
  delete mode 100644 file2
 
+-- 
+g-i-t--v-e-r-s-i-o-n
+
 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
@@ -25,9 +28,9 @@
 
 This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
  delete mode 100644 file2
 
@@ -70,9 +73,9 @@
 Subject: [DIFFERENT_PREFIX 2/2] Third
 
 ---
- dir/sub |    2 ++
- file1   |    3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file1   | 3 +++
+ 2 files changed, 5 insertions(+)
  create mode 100644 file1
 
 diff --git a/dir/sub b/dir/sub
diff --git a/t/t4013/diff.format-patch_--stdout_--no-numbered_initial..master b/t/t4013/diff.format-patch_--stdout_--no-numbered_initial..master
index f7752eb..bfc287a 100644
--- a/t/t4013/diff.format-patch_--stdout_--no-numbered_initial..master
+++ b/t/t4013/diff.format-patch_--stdout_--no-numbered_initial..master
@@ -6,9 +6,9 @@
 
 This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
  delete mode 100644 file2
 
@@ -51,9 +51,9 @@
 Subject: [PATCH] Third
 
 ---
- dir/sub |    2 ++
- file1   |    3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file1   | 3 +++
+ 2 files changed, 5 insertions(+)
  create mode 100644 file1
 
 diff --git a/dir/sub b/dir/sub
@@ -85,10 +85,10 @@
 Subject: [PATCH] Side
 
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
+ 3 files changed, 9 insertions(+)
  create mode 100644 file3
 
 diff --git a/dir/sub b/dir/sub
diff --git a/t/t4013/diff.format-patch_--stdout_--numbered_initial..master b/t/t4013/diff.format-patch_--stdout_--numbered_initial..master
index 8e67dbf..568f6f5 100644
--- a/t/t4013/diff.format-patch_--stdout_--numbered_initial..master
+++ b/t/t4013/diff.format-patch_--stdout_--numbered_initial..master
@@ -6,9 +6,9 @@
 
 This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
  delete mode 100644 file2
 
@@ -51,9 +51,9 @@
 Subject: [PATCH 2/3] Third
 
 ---
- dir/sub |    2 ++
- file1   |    3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file1   | 3 +++
+ 2 files changed, 5 insertions(+)
  create mode 100644 file1
 
 diff --git a/dir/sub b/dir/sub
@@ -85,10 +85,10 @@
 Subject: [PATCH 3/3] Side
 
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
+ 3 files changed, 9 insertions(+)
  create mode 100644 file3
 
 diff --git a/dir/sub b/dir/sub
diff --git a/t/t4013/diff.format-patch_--stdout_initial..master b/t/t4013/diff.format-patch_--stdout_initial..master
index 7b89978..5f0352f 100644
--- a/t/t4013/diff.format-patch_--stdout_initial..master
+++ b/t/t4013/diff.format-patch_--stdout_initial..master
@@ -6,9 +6,9 @@
 
 This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
  delete mode 100644 file2
 
@@ -51,9 +51,9 @@
 Subject: [PATCH 2/3] Third
 
 ---
- dir/sub |    2 ++
- file1   |    3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file1   | 3 +++
+ 2 files changed, 5 insertions(+)
  create mode 100644 file1
 
 diff --git a/dir/sub b/dir/sub
@@ -85,10 +85,10 @@
 Subject: [PATCH 3/3] Side
 
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
+ 3 files changed, 9 insertions(+)
  create mode 100644 file3
 
 diff --git a/dir/sub b/dir/sub
diff --git a/t/t4013/diff.format-patch_--stdout_initial..master^ b/t/t4013/diff.format-patch_--stdout_initial..master^
index b7f9725..2ae454d 100644
--- a/t/t4013/diff.format-patch_--stdout_initial..master^
+++ b/t/t4013/diff.format-patch_--stdout_initial..master^
@@ -6,9 +6,9 @@
 
 This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
  delete mode 100644 file2
 
@@ -51,9 +51,9 @@
 Subject: [PATCH 2/2] Third
 
 ---
- dir/sub |    2 ++
- file1   |    3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file1   | 3 +++
+ 2 files changed, 5 insertions(+)
  create mode 100644 file1
 
 diff --git a/dir/sub b/dir/sub
diff --git a/t/t4013/diff.format-patch_--stdout_initial..side b/t/t4013/diff.format-patch_--stdout_initial..side
index e765088..a7d52fb 100644
--- a/t/t4013/diff.format-patch_--stdout_initial..side
+++ b/t/t4013/diff.format-patch_--stdout_initial..side
@@ -5,10 +5,10 @@
 Subject: [PATCH] Side
 
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
+ 3 files changed, 9 insertions(+)
  create mode 100644 file3
 
 diff --git a/dir/sub b/dir/sub
diff --git a/t/t4013/diff.log_--decorate=full_--all b/t/t4013/diff.log_--decorate=full_--all
new file mode 100644
index 0000000..44d4525
--- /dev/null
+++ b/t/t4013/diff.log_--decorate=full_--all
@@ -0,0 +1,40 @@
+$ git log --decorate=full --all
+commit cd4e72fd96faed3f0ba949dc42967430374e2290 (refs/heads/rearrange)
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:06:00 2006 +0000
+
+    Rearranged lines in dir/sub
+
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD, refs/heads/master)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a (refs/heads/side)
+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 (refs/heads/initial)
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+$
diff --git a/t/t4013/diff.log_--decorate_--all b/t/t4013/diff.log_--decorate_--all
index 954210e..27d3eab 100644
--- a/t/t4013/diff.log_--decorate_--all
+++ b/t/t4013/diff.log_--decorate_--all
@@ -1,5 +1,11 @@
 $ git log --decorate --all
-commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (master)
+commit cd4e72fd96faed3f0ba949dc42967430374e2290 (rearrange)
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:06:00 2006 +0000
+
+    Rearranged lines in dir/sub
+
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD, master)
 Merge: 9a6d494 c7a2ab9
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
diff --git a/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_ b/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_
index bd7f5c0..a18f147 100644
--- a/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_
+++ b/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_
@@ -12,8 +12,8 @@
 
     Side
 ---
- dir/sub |    2 ++
- 1 files changed, 2 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ 1 file changed, 2 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..7289e35 100644
@@ -31,8 +31,8 @@
 
     Third
 ---
- dir/sub |    2 ++
- 1 files changed, 2 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ 1 file changed, 2 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
 index 8422d40..cead32e 100644
@@ -53,8 +53,8 @@
     
     This is the second commit.
 ---
- dir/sub |    2 ++
- 1 files changed, 2 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ 1 file changed, 2 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..8422d40 100644
diff --git a/t/t4013/diff.log_--patch-with-stat_master b/t/t4013/diff.log_--patch-with-stat_master
index 14595a6..ae425c4 100644
--- a/t/t4013/diff.log_--patch-with-stat_master
+++ b/t/t4013/diff.log_--patch-with-stat_master
@@ -12,10 +12,10 @@
 
     Side
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
+ 3 files changed, 9 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..7289e35 100644
@@ -54,9 +54,9 @@
 
     Third
 ---
- dir/sub |    2 ++
- file1   |    3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file1   | 3 +++
+ 2 files changed, 5 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
 index 8422d40..cead32e 100644
@@ -86,9 +86,9 @@
     
     This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
 
 diff --git a/dir/sub b/dir/sub
diff --git a/t/t4013/diff.log_--patch-with-stat_master_--_dir_ b/t/t4013/diff.log_--patch-with-stat_master_--_dir_
index 5a4e727..d5207ca 100644
--- a/t/t4013/diff.log_--patch-with-stat_master_--_dir_
+++ b/t/t4013/diff.log_--patch-with-stat_master_--_dir_
@@ -12,8 +12,8 @@
 
     Side
 ---
- dir/sub |    2 ++
- 1 files changed, 2 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ 1 file changed, 2 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..7289e35 100644
@@ -31,8 +31,8 @@
 
     Third
 ---
- dir/sub |    2 ++
- 1 files changed, 2 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ 1 file changed, 2 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
 index 8422d40..cead32e 100644
@@ -53,8 +53,8 @@
     
     This is the second commit.
 ---
- dir/sub |    2 ++
- 1 files changed, 2 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ 1 file changed, 2 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..8422d40 100644
diff --git a/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master b/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master
index df0aaa9..0fc1e8c 100644
--- a/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master
@@ -6,9 +6,9 @@
 
     Merge branch 'side'
 
- dir/sub |    2 ++
- file0   |    3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ 2 files changed, 5 insertions(+)
 
 diff --cc dir/sub
 index cead32e,7289e35..992913c
@@ -44,10 +44,10 @@
 
     Side
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
+ 3 files changed, 9 insertions(+)
  create mode 100644 file3
 
 diff --git a/dir/sub b/dir/sub
@@ -87,9 +87,9 @@
 
     Third
 ---
- dir/sub |    2 ++
- file1   |    3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file1   | 3 +++
+ 2 files changed, 5 insertions(+)
  create mode 100644 file1
 
 diff --git a/dir/sub b/dir/sub
@@ -120,9 +120,9 @@
     
     This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
  delete mode 100644 file2
 
@@ -162,10 +162,10 @@
 
     Initial
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 +++
- 3 files changed, 8 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 +++
+ 3 files changed, 8 insertions(+)
  create mode 100644 dir/sub
  create mode 100644 file0
  create mode 100644 file2
diff --git a/t/t4013/diff.log_--root_--patch-with-stat_--summary_master b/t/t4013/diff.log_--root_--patch-with-stat_--summary_master
index c11b5f2c..dffc09d 100644
--- a/t/t4013/diff.log_--root_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.log_--root_--patch-with-stat_--summary_master
@@ -12,10 +12,10 @@
 
     Side
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
+ 3 files changed, 9 insertions(+)
  create mode 100644 file3
 
 diff --git a/dir/sub b/dir/sub
@@ -55,9 +55,9 @@
 
     Third
 ---
- dir/sub |    2 ++
- file1   |    3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file1   | 3 +++
+ 2 files changed, 5 insertions(+)
  create mode 100644 file1
 
 diff --git a/dir/sub b/dir/sub
@@ -88,9 +88,9 @@
     
     This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
  delete mode 100644 file2
 
@@ -130,10 +130,10 @@
 
     Initial
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 +++
- 3 files changed, 8 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 +++
+ 3 files changed, 8 insertions(+)
  create mode 100644 dir/sub
  create mode 100644 file0
  create mode 100644 file2
diff --git a/t/t4013/diff.log_--root_--patch-with-stat_master b/t/t4013/diff.log_--root_--patch-with-stat_master
index 5f0c98f..55aa980 100644
--- a/t/t4013/diff.log_--root_--patch-with-stat_master
+++ b/t/t4013/diff.log_--root_--patch-with-stat_master
@@ -12,10 +12,10 @@
 
     Side
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
+ 3 files changed, 9 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..7289e35 100644
@@ -54,9 +54,9 @@
 
     Third
 ---
- dir/sub |    2 ++
- file1   |    3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file1   | 3 +++
+ 2 files changed, 5 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
 index 8422d40..cead32e 100644
@@ -86,9 +86,9 @@
     
     This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
 
 diff --git a/dir/sub b/dir/sub
@@ -127,10 +127,10 @@
 
     Initial
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 +++
- 3 files changed, 8 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 +++
+ 3 files changed, 8 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
 new file mode 100644
diff --git a/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master b/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master
index e62c368..019d85f 100644
--- a/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master
@@ -6,9 +6,9 @@
 
     Merge branch 'side'
 
- dir/sub |    2 ++
- file0   |    3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ 2 files changed, 5 insertions(+)
 
 diff --combined dir/sub
 index cead32e,7289e35..992913c
@@ -44,10 +44,10 @@
 
     Side
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
+ 3 files changed, 9 insertions(+)
  create mode 100644 file3
 
 diff --git a/dir/sub b/dir/sub
@@ -87,9 +87,9 @@
 
     Third
 ---
- dir/sub |    2 ++
- file1   |    3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file1   | 3 +++
+ 2 files changed, 5 insertions(+)
  create mode 100644 file1
 
 diff --git a/dir/sub b/dir/sub
@@ -120,9 +120,9 @@
     
     This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
  delete mode 100644 file2
 
@@ -162,10 +162,10 @@
 
     Initial
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 +++
- 3 files changed, 8 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 +++
+ 3 files changed, 8 insertions(+)
  create mode 100644 dir/sub
  create mode 100644 file0
  create mode 100644 file2
diff --git a/t/t4013/diff.log_-GF_-p_--pickaxe-all_master b/t/t4013/diff.log_-GF_-p_--pickaxe-all_master
new file mode 100644
index 0000000..d36f880
--- /dev/null
+++ b/t/t4013/diff.log_-GF_-p_--pickaxe-all_master
@@ -0,0 +1,27 @@
+$ git log -GF -p --pickaxe-all 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/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+$
diff --git a/t/t4013/diff.log_-GF_-p_master b/t/t4013/diff.log_-GF_-p_master
new file mode 100644
index 0000000..9d93f2c
--- /dev/null
+++ b/t/t4013/diff.log_-GF_-p_master
@@ -0,0 +1,18 @@
+$ git log -GF -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_-GF_master b/t/t4013/diff.log_-GF_master
new file mode 100644
index 0000000..4c6708d
--- /dev/null
+++ b/t/t4013/diff.log_-GF_master
@@ -0,0 +1,7 @@
+$ git log -GF 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_-SF_master_--max-count=0 b/t/t4013/diff.log_-SF_master_--max-count=0
new file mode 100644
index 0000000..c1fc6c8
--- /dev/null
+++ b/t/t4013/diff.log_-SF_master_--max-count=0
@@ -0,0 +1,2 @@
+$ git log -SF master --max-count=0
+$
diff --git a/t/t4013/diff.log_-SF_master_--max-count=1 b/t/t4013/diff.log_-SF_master_--max-count=1
new file mode 100644
index 0000000..c981a03
--- /dev/null
+++ b/t/t4013/diff.log_-SF_master_--max-count=1
@@ -0,0 +1,7 @@
+$ git log -SF master --max-count=1
+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_-SF_master_--max-count=2 b/t/t4013/diff.log_-SF_master_--max-count=2
new file mode 100644
index 0000000..a6c55fd
--- /dev/null
+++ b/t/t4013/diff.log_-SF_master_--max-count=2
@@ -0,0 +1,7 @@
+$ git log -SF master --max-count=2
+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_-S_F_master b/t/t4013/diff.log_-S_F_master
new file mode 100644
index 0000000..978d2b4
--- /dev/null
+++ b/t/t4013/diff.log_-S_F_master
@@ -0,0 +1,7 @@
+$ git log -S F 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_-m_-p_--first-parent_master b/t/t4013/diff.log_-m_-p_--first-parent_master
new file mode 100644
index 0000000..7a0073f
--- /dev/null
+++ b/t/t4013/diff.log_-m_-p_--first-parent_master
@@ -0,0 +1,100 @@
+$ git log -m -p --first-parent 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 --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
+
+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_-m_-p_master b/t/t4013/diff.log_-m_-p_master
new file mode 100644
index 0000000..9ca62a0
--- /dev/null
+++ b/t/t4013/diff.log_-m_-p_master
@@ -0,0 +1,200 @@
+$ git log -m -p master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (from 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+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
+
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (from c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+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
+
+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_-p_--first-parent_master b/t/t4013/diff.log_-p_--first-parent_master
new file mode 100644
index 0000000..3fc896d
--- /dev/null
+++ b/t/t4013/diff.log_-p_--first-parent_master
@@ -0,0 +1,78 @@
+$ git log -p --first-parent 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 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.show_--first-parent_master b/t/t4013/diff.show_--first-parent_master
new file mode 100644
index 0000000..3dcbe47
--- /dev/null
+++ b/t/t4013/diff.show_--first-parent_master
@@ -0,0 +1,30 @@
+$ git show --first-parent 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 --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
+$
diff --git a/t/t4013/diff.show_--patch-with-stat_--summary_side b/t/t4013/diff.show_--patch-with-stat_--summary_side
index 377f2b7..95a474e 100644
--- a/t/t4013/diff.show_--patch-with-stat_--summary_side
+++ b/t/t4013/diff.show_--patch-with-stat_--summary_side
@@ -5,10 +5,10 @@
 
     Side
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
+ 3 files changed, 9 insertions(+)
  create mode 100644 file3
 
 diff --git a/dir/sub b/dir/sub
diff --git a/t/t4013/diff.show_--patch-with-stat_side b/t/t4013/diff.show_--patch-with-stat_side
index fb14c53..974e99b 100644
--- a/t/t4013/diff.show_--patch-with-stat_side
+++ b/t/t4013/diff.show_--patch-with-stat_side
@@ -5,10 +5,10 @@
 
     Side
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
+ 3 files changed, 9 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..7289e35 100644
diff --git a/t/t4013/diff.show_--stat_--summary_side b/t/t4013/diff.show_--stat_--summary_side
index 5bd5977..a71492f 100644
--- a/t/t4013/diff.show_--stat_--summary_side
+++ b/t/t4013/diff.show_--stat_--summary_side
@@ -5,9 +5,9 @@
 
     Side
 
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
+ 3 files changed, 9 insertions(+)
  create mode 100644 file3
 $
diff --git a/t/t4013/diff.show_--stat_side b/t/t4013/diff.show_--stat_side
index 3b22327..9be7124 100644
--- a/t/t4013/diff.show_--stat_side
+++ b/t/t4013/diff.show_--stat_side
@@ -5,8 +5,8 @@
 
     Side
 
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
+ 3 files changed, 9 insertions(+)
 $
diff --git a/t/t4013/diff.show_-c_master b/t/t4013/diff.show_-c_master
new file mode 100644
index 0000000..81aba8d
--- /dev/null
+++ b/t/t4013/diff.show_-c_master
@@ -0,0 +1,36 @@
+$ git show -c 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 --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
+$
diff --git a/t/t4013/diff.show_-m_master b/t/t4013/diff.show_-m_master
new file mode 100644
index 0000000..4ea2ee4
--- /dev/null
+++ b/t/t4013/diff.show_-m_master
@@ -0,0 +1,93 @@
+$ git show -m master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (from 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+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
+
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (from c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+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.whatchanged_--patch-with-stat_--summary_master_--_dir_ b/t/t4013/diff.whatchanged_--patch-with-stat_--summary_master_--_dir_
index 6a467cc..c8b6af2 100644
--- a/t/t4013/diff.whatchanged_--patch-with-stat_--summary_master_--_dir_
+++ b/t/t4013/diff.whatchanged_--patch-with-stat_--summary_master_--_dir_
@@ -5,8 +5,8 @@
 
     Side
 ---
- dir/sub |    2 ++
- 1 files changed, 2 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ 1 file changed, 2 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..7289e35 100644
@@ -24,8 +24,8 @@
 
     Third
 ---
- dir/sub |    2 ++
- 1 files changed, 2 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ 1 file changed, 2 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
 index 8422d40..cead32e 100644
@@ -46,8 +46,8 @@
     
     This is the second commit.
 ---
- dir/sub |    2 ++
- 1 files changed, 2 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ 1 file changed, 2 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..8422d40 100644
diff --git a/t/t4013/diff.whatchanged_--patch-with-stat_master b/t/t4013/diff.whatchanged_--patch-with-stat_master
index 1e1bbe1..1ac431b 100644
--- a/t/t4013/diff.whatchanged_--patch-with-stat_master
+++ b/t/t4013/diff.whatchanged_--patch-with-stat_master
@@ -5,10 +5,10 @@
 
     Side
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
+ 3 files changed, 9 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..7289e35 100644
@@ -47,9 +47,9 @@
 
     Third
 ---
- dir/sub |    2 ++
- file1   |    3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file1   | 3 +++
+ 2 files changed, 5 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
 index 8422d40..cead32e 100644
@@ -79,9 +79,9 @@
     
     This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
 
 diff --git a/dir/sub b/dir/sub
diff --git a/t/t4013/diff.whatchanged_--patch-with-stat_master_--_dir_ b/t/t4013/diff.whatchanged_--patch-with-stat_master_--_dir_
index 13789f1..b30c285 100644
--- a/t/t4013/diff.whatchanged_--patch-with-stat_master_--_dir_
+++ b/t/t4013/diff.whatchanged_--patch-with-stat_master_--_dir_
@@ -5,8 +5,8 @@
 
     Side
 ---
- dir/sub |    2 ++
- 1 files changed, 2 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ 1 file changed, 2 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..7289e35 100644
@@ -24,8 +24,8 @@
 
     Third
 ---
- dir/sub |    2 ++
- 1 files changed, 2 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ 1 file changed, 2 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
 index 8422d40..cead32e 100644
@@ -46,8 +46,8 @@
     
     This is the second commit.
 ---
- dir/sub |    2 ++
- 1 files changed, 2 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ 1 file changed, 2 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..8422d40 100644
diff --git a/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master b/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master
index e96ff1f..30aae78 100644
--- a/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master
@@ -6,9 +6,9 @@
 
     Merge branch 'side'
 
- dir/sub |    2 ++
- file0   |    3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ 2 files changed, 5 insertions(+)
 
 diff --cc dir/sub
 index cead32e,7289e35..992913c
@@ -44,10 +44,10 @@
 
     Side
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
+ 3 files changed, 9 insertions(+)
  create mode 100644 file3
 
 diff --git a/dir/sub b/dir/sub
@@ -87,9 +87,9 @@
 
     Third
 ---
- dir/sub |    2 ++
- file1   |    3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file1   | 3 +++
+ 2 files changed, 5 insertions(+)
  create mode 100644 file1
 
 diff --git a/dir/sub b/dir/sub
@@ -120,9 +120,9 @@
     
     This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
  delete mode 100644 file2
 
@@ -162,10 +162,10 @@
 
     Initial
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 +++
- 3 files changed, 8 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 +++
+ 3 files changed, 8 insertions(+)
  create mode 100644 dir/sub
  create mode 100644 file0
  create mode 100644 file2
diff --git a/t/t4013/diff.whatchanged_--root_--patch-with-stat_--summary_master b/t/t4013/diff.whatchanged_--root_--patch-with-stat_--summary_master
index 0291153..db90e51 100644
--- a/t/t4013/diff.whatchanged_--root_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.whatchanged_--root_--patch-with-stat_--summary_master
@@ -5,10 +5,10 @@
 
     Side
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
+ 3 files changed, 9 insertions(+)
  create mode 100644 file3
 
 diff --git a/dir/sub b/dir/sub
@@ -48,9 +48,9 @@
 
     Third
 ---
- dir/sub |    2 ++
- file1   |    3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file1   | 3 +++
+ 2 files changed, 5 insertions(+)
  create mode 100644 file1
 
 diff --git a/dir/sub b/dir/sub
@@ -81,9 +81,9 @@
     
     This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
  delete mode 100644 file2
 
@@ -123,10 +123,10 @@
 
     Initial
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 +++
- 3 files changed, 8 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 +++
+ 3 files changed, 8 insertions(+)
  create mode 100644 dir/sub
  create mode 100644 file0
  create mode 100644 file2
diff --git a/t/t4013/diff.whatchanged_--root_--patch-with-stat_master b/t/t4013/diff.whatchanged_--root_--patch-with-stat_master
index 9b0349c..9a6cc92 100644
--- a/t/t4013/diff.whatchanged_--root_--patch-with-stat_master
+++ b/t/t4013/diff.whatchanged_--root_--patch-with-stat_master
@@ -5,10 +5,10 @@
 
     Side
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
+ 3 files changed, 9 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..7289e35 100644
@@ -47,9 +47,9 @@
 
     Third
 ---
- dir/sub |    2 ++
- file1   |    3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file1   | 3 +++
+ 2 files changed, 5 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
 index 8422d40..cead32e 100644
@@ -79,9 +79,9 @@
     
     This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
 
 diff --git a/dir/sub b/dir/sub
@@ -120,10 +120,10 @@
 
     Initial
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 +++
- 3 files changed, 8 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 +++
+ 3 files changed, 8 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
 new file mode 100644
diff --git a/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master b/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master
index c0aff68..d1d32bd 100644
--- a/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master
@@ -6,9 +6,9 @@
 
     Merge branch 'side'
 
- dir/sub |    2 ++
- file0   |    3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ 2 files changed, 5 insertions(+)
 
 diff --combined dir/sub
 index cead32e,7289e35..992913c
@@ -44,10 +44,10 @@
 
     Side
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
- 3 files changed, 9 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
+ 3 files changed, 9 insertions(+)
  create mode 100644 file3
 
 diff --git a/dir/sub b/dir/sub
@@ -87,9 +87,9 @@
 
     Third
 ---
- dir/sub |    2 ++
- file1   |    3 +++
- 2 files changed, 5 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file1   | 3 +++
+ 2 files changed, 5 insertions(+)
  create mode 100644 file1
 
 diff --git a/dir/sub b/dir/sub
@@ -120,9 +120,9 @@
     
     This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
  delete mode 100644 file2
 
@@ -162,10 +162,10 @@
 
     Initial
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 +++
- 3 files changed, 8 insertions(+), 0 deletions(-)
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 +++
+ 3 files changed, 8 insertions(+)
  create mode 100644 dir/sub
  create mode 100644 file0
  create mode 100644 file2
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index 922a894..959aa26 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -6,30 +6,36 @@
 test_description='various format-patch tests'
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
 
 test_expect_success setup '
 
 	for i in 1 2 3 4 5 6 7 8 9 10; do echo "$i"; done >file &&
 	cat file >elif &&
 	git add file elif &&
+	test_tick &&
 	git commit -m Initial &&
 	git checkout -b side &&
 
 	for i in 1 2 5 6 A B C 7 8 9 10; do echo "$i"; done >file &&
 	test_chmod +x elif &&
+	test_tick &&
 	git commit -m "Side changes #1" &&
 
 	for i in D E F; do echo "$i"; done >>file &&
 	git update-index file &&
+	test_tick &&
 	git commit -m "Side changes #2" &&
 	git tag C2 &&
 
 	for i in 5 6 1 2 3 A 4 B C 7 8 9 10 D E F; do echo "$i"; done >file &&
 	git update-index file &&
+	test_tick &&
 	git commit -m "Side changes #3 with \\n backslash-n in it." &&
 
 	git checkout master &&
 	git diff-tree -p C2 | git apply --index &&
+	test_tick &&
 	git commit -m "Master accepts moral equivalent of #2"
 
 '
@@ -51,6 +57,22 @@
 
 '
 
+test_expect_success "format-patch doesn't consider merge commits" '
+
+	git checkout -b slave master &&
+	echo "Another line" >>file &&
+	test_tick &&
+	git commit -am "Slave change #1" &&
+	echo "Yet another line" >>file &&
+	test_tick &&
+	git commit -am "Slave change #2" &&
+	git checkout -b merger master &&
+	test_tick &&
+	git merge --no-ff slave &&
+	cnt=`git format-patch -3 --stdout | grep "^From " | wc -l` &&
+	test $cnt = 3
+'
+
 test_expect_success "format-patch result applies" '
 
 	git checkout -b rebuild-0 master &&
@@ -93,9 +115,9 @@
 	git config --add format.headers "Cc: S. E. Cipient <scipient@example.com>
 " &&
 	git format-patch --stdout master..side > patch2 &&
-	sed -e "/^$/q" patch2 > hdrs2 &&
-	grep "^To: R. E. Cipient <rcipient@example.com>$" hdrs2 &&
-	grep "^Cc: S. E. Cipient <scipient@example.com>$" hdrs2
+	sed -e "/^\$/q" patch2 > hdrs2 &&
+	grep "^To: R. E. Cipient <rcipient@example.com>\$" hdrs2 &&
+	grep "^Cc: S. E. Cipient <scipient@example.com>\$" hdrs2
 
 '
 
@@ -104,9 +126,9 @@
 	git config --replace-all format.headers "To: R. E. Cipient <rcipient@example.com>" &&
 	git config --add format.headers "Cc: S. E. Cipient <scipient@example.com>" &&
 	git format-patch --stdout master..side >patch3 &&
-	sed -e "/^$/q" patch3 > hdrs3 &&
-	grep "^To: R. E. Cipient <rcipient@example.com>$" hdrs3 &&
-	grep "^Cc: S. E. Cipient <scipient@example.com>$" hdrs3
+	sed -e "/^\$/q" patch3 > hdrs3 &&
+	grep "^To: R. E. Cipient <rcipient@example.com>\$" hdrs3 &&
+	grep "^Cc: S. E. Cipient <scipient@example.com>\$" hdrs3
 
 '
 
@@ -115,32 +137,96 @@
 	git config --replace-all format.headers "To: R. E. Cipient <rcipient@example.com>" &&
 	git config --add format.headers "To: S. E. Cipient <scipient@example.com>" &&
 	git format-patch --stdout master..side > patch4 &&
-	sed -e "/^$/q" patch4 > hdrs4 &&
-	grep "^To: R. E. Cipient <rcipient@example.com>,$" hdrs4 &&
-	grep "^ *S. E. Cipient <scipient@example.com>$" hdrs4
+	sed -e "/^\$/q" patch4 > hdrs4 &&
+	grep "^To: R. E. Cipient <rcipient@example.com>,\$" hdrs4 &&
+	grep "^ *S. E. Cipient <scipient@example.com>\$" hdrs4
 '
 
 test_expect_success 'additional command line cc' '
 
 	git config --replace-all format.headers "Cc: R. E. Cipient <rcipient@example.com>" &&
-	git format-patch --cc="S. E. Cipient <scipient@example.com>" --stdout master..side | sed -e "/^$/q" >patch5 &&
-	grep "^Cc: R. E. Cipient <rcipient@example.com>,$" patch5 &&
-	grep "^ *S. E. Cipient <scipient@example.com>$" patch5
+	git format-patch --cc="S. E. Cipient <scipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch5 &&
+	grep "^Cc: R. E. Cipient <rcipient@example.com>,\$" patch5 &&
+	grep "^ *S. E. Cipient <scipient@example.com>\$" patch5
 '
 
 test_expect_success 'command line headers' '
 
 	git config --unset-all format.headers &&
-	git format-patch --add-header="Cc: R. E. Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^$/q" >patch6 &&
-	grep "^Cc: R. E. Cipient <rcipient@example.com>$" patch6
+	git format-patch --add-header="Cc: R. E. Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch6 &&
+	grep "^Cc: R. E. Cipient <rcipient@example.com>\$" patch6
 '
 
 test_expect_success 'configuration headers and command line headers' '
 
 	git config --replace-all format.headers "Cc: R. E. Cipient <rcipient@example.com>" &&
-	git format-patch --add-header="Cc: S. E. Cipient <scipient@example.com>" --stdout master..side | sed -e "/^$/q" >patch7 &&
-	grep "^Cc: R. E. Cipient <rcipient@example.com>,$" patch7 &&
-	grep "^ *S. E. Cipient <scipient@example.com>$" patch7
+	git format-patch --add-header="Cc: S. E. Cipient <scipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch7 &&
+	grep "^Cc: R. E. Cipient <rcipient@example.com>,\$" patch7 &&
+	grep "^ *S. E. Cipient <scipient@example.com>\$" patch7
+'
+
+test_expect_success 'command line To: header' '
+
+	git config --unset-all format.headers &&
+	git format-patch --to="R. E. Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch8 &&
+	grep "^To: R. E. Cipient <rcipient@example.com>\$" patch8
+'
+
+test_expect_success 'configuration To: header' '
+
+	git config format.to "R. E. Cipient <rcipient@example.com>" &&
+	git format-patch --stdout master..side | sed -e "/^\$/q" >patch9 &&
+	grep "^To: R. E. Cipient <rcipient@example.com>\$" patch9
+'
+
+# check_patch <patch>: Verify that <patch> looks like a half-sane
+# patch email to avoid a false positive with !grep
+check_patch () {
+	grep -e "^From:" "$1" &&
+	grep -e "^Date:" "$1" &&
+	grep -e "^Subject:" "$1"
+}
+
+test_expect_success '--no-to overrides config.to' '
+
+	git config --replace-all format.to \
+		"R. E. Cipient <rcipient@example.com>" &&
+	git format-patch --no-to --stdout master..side |
+	sed -e "/^\$/q" >patch10 &&
+	check_patch patch10 &&
+	! grep "^To: R. E. Cipient <rcipient@example.com>\$" patch10
+'
+
+test_expect_success '--no-to and --to replaces config.to' '
+
+	git config --replace-all format.to \
+		"Someone <someone@out.there>" &&
+	git format-patch --no-to --to="Someone Else <else@out.there>" \
+		--stdout master..side |
+	sed -e "/^\$/q" >patch11 &&
+	check_patch patch11 &&
+	! grep "^To: Someone <someone@out.there>\$" patch11 &&
+	grep "^To: Someone Else <else@out.there>\$" patch11
+'
+
+test_expect_success '--no-cc overrides config.cc' '
+
+	git config --replace-all format.cc \
+		"C. E. Cipient <rcipient@example.com>" &&
+	git format-patch --no-cc --stdout master..side |
+	sed -e "/^\$/q" >patch12 &&
+	check_patch patch12 &&
+	! grep "^Cc: C. E. Cipient <rcipient@example.com>\$" patch12
+'
+
+test_expect_success '--no-add-header overrides config.headers' '
+
+	git config --replace-all format.headers \
+		"Header1: B. E. Cipient <rcipient@example.com>" &&
+	git format-patch --no-add-header --stdout master..side |
+	sed -e "/^\$/q" >patch13 &&
+	check_patch patch13 &&
+	! grep "^Header1: B. E. Cipient <rcipient@example.com>\$" patch13
 '
 
 test_expect_success 'multiple files' '
@@ -157,7 +243,7 @@
 	(git format-patch --stdout "$@"; echo $? > status.out) |
 	# Prints everything between the Message-ID and In-Reply-To,
 	# and replaces all Message-ID-lookalikes by a sequence number
-	perl -ne '
+	"$PERL_PATH" -ne '
 		if (/^(message-id|references|in-reply-to)/i) {
 			$printing = 1;
 		} elsif (/^\S/) {
@@ -371,22 +457,22 @@
 '
 
 test_expect_success 'thread via config' '
-	git config format.thread true &&
+	test_config format.thread true &&
 	check_threading expect.thread master
 '
 
 test_expect_success 'thread deep via config' '
-	git config format.thread deep &&
+	test_config format.thread deep &&
 	check_threading expect.deep master
 '
 
 test_expect_success 'thread config + override' '
-	git config format.thread deep &&
+	test_config format.thread deep &&
 	check_threading expect.thread --thread master
 '
 
 test_expect_success 'thread config + --no-thread' '
-	git config format.thread deep &&
+	test_config format.thread deep &&
 	check_threading expect.no-threading --no-thread master
 '
 
@@ -406,9 +492,10 @@
 	git mv file foo &&
 	git commit -m foo &&
 	git format-patch --cover-letter -1 &&
-	! grep "file => foo .* 0 *$" 0000-cover-letter.patch &&
+	check_patch 0000-cover-letter.patch &&
+	! grep "file => foo .* 0 *\$" 0000-cover-letter.patch &&
 	git format-patch --cover-letter -1 -M &&
-	grep "file => foo .* 0 *$" 0000-cover-letter.patch
+	grep "file => foo .* 0 *\$" 0000-cover-letter.patch
 
 '
 
@@ -425,17 +512,12 @@
 test_expect_success 'shortlog of cover-letter wraps overly-long onelines' '
 
 	git format-patch --cover-letter -2 &&
-	sed -e "1,/A U Thor/d" -e "/^$/q" < 0000-cover-letter.patch > output &&
+	sed -e "1,/A U Thor/d" -e "/^\$/q" < 0000-cover-letter.patch > output &&
 	test_cmp expect output
 
 '
 
 cat > expect << EOF
----
- file |   16 ++++++++++++++++
- 1 files changed, 16 insertions(+), 0 deletions(-)
-
-diff --git a/file b/file
 index 40f36c6..2dc5c23 100644
 --- a/file
 +++ b/file
@@ -450,7 +532,30 @@
 test_expect_success 'format-patch respects -U' '
 
 	git format-patch -U4 -2 &&
-	sed -e "1,/^$/d" -e "/^+5/q" < 0001-This-is-an-excessively-long-subject-line-for-a-messa.patch > output &&
+	sed -e "1,/^diff/d" -e "/^+5/q" \
+		<0001-This-is-an-excessively-long-subject-line-for-a-messa.patch \
+		>output &&
+	test_cmp expect output
+
+'
+
+cat > expect << EOF
+
+diff --git a/file b/file
+index 40f36c6..2dc5c23 100644
+--- a/file
++++ b/file
+@@ -14,3 +14,19 @@ C
+ D
+ E
+ F
++5
+EOF
+
+test_expect_success 'format-patch -p suppresses stat' '
+
+	git format-patch -p -2 &&
+	sed -e "1,/^\$/d" -e "/^+5/q" < 0001-This-is-an-excessively-long-subject-line-for-a-messa.patch > output &&
 	test_cmp expect output
 
 '
@@ -493,13 +598,12 @@
 '
 
 test_expect_success 'format-patch from a subdirectory (3)' '
-	here="$TEST_DIRECTORY/$test" &&
 	rm -f 0* &&
 	filename=$(
 		rm -rf sub &&
 		mkdir -p sub/dir &&
 		cd sub/dir &&
-		git format-patch -1 -o "$here"
+		git format-patch -1 -o "$TRASH_DIRECTORY"
 	) &&
 	basename=$(expr "$filename" : ".*/\(.*\)") &&
 	test -f "$basename"
@@ -516,4 +620,275 @@
 	grep "^Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
 '
 
+echo "fatal: --name-only does not make sense" > expect.name-only
+echo "fatal: --name-status does not make sense" > expect.name-status
+echo "fatal: --check does not make sense" > expect.check
+
+test_expect_success 'options no longer allowed for format-patch' '
+	test_must_fail git format-patch --name-only 2> output &&
+	test_i18ncmp expect.name-only output &&
+	test_must_fail git format-patch --name-status 2> output &&
+	test_i18ncmp expect.name-status output &&
+	test_must_fail git format-patch --check 2> output &&
+	test_i18ncmp expect.check output'
+
+test_expect_success 'format-patch --numstat should produce a patch' '
+	git format-patch --numstat --stdout master..side > output &&
+	test 6 = $(grep "^diff --git a/" output | wc -l)'
+
+test_expect_success 'format-patch -- <path>' '
+	git format-patch master..side -- file 2>error &&
+	! grep "Use .--" error
+'
+
+test_expect_success 'format-patch --ignore-if-in-upstream HEAD' '
+	git format-patch --ignore-if-in-upstream HEAD
+'
+
+test_expect_success 'format-patch --signature' '
+	git format-patch --stdout --signature="my sig" -1 >output &&
+	grep "my sig" output
+'
+
+test_expect_success 'format-patch with format.signature config' '
+	git config format.signature "config sig" &&
+	git format-patch --stdout -1 >output &&
+	grep "config sig" output
+'
+
+test_expect_success 'format-patch --signature overrides format.signature' '
+	git config format.signature "config sig" &&
+	git format-patch --stdout --signature="overrides" -1 >output &&
+	! grep "config sig" output &&
+	grep "overrides" output
+'
+
+test_expect_success 'format-patch --no-signature ignores format.signature' '
+	git config format.signature "config sig" &&
+	git format-patch --stdout --signature="my sig" --no-signature \
+		-1 >output &&
+	check_patch output &&
+	! grep "config sig" output &&
+	! grep "my sig" output &&
+	! grep "^-- \$" output
+'
+
+test_expect_success 'format-patch --signature --cover-letter' '
+	git config --unset-all format.signature &&
+	git format-patch --stdout --signature="my sig" --cover-letter \
+		-1 >output &&
+	grep "my sig" output &&
+	test 2 = $(grep "my sig" output | wc -l)
+'
+
+test_expect_success 'format.signature="" supresses signatures' '
+	git config format.signature "" &&
+	git format-patch --stdout -1 >output &&
+	check_patch output &&
+	! grep "^-- \$" output
+'
+
+test_expect_success 'format-patch --no-signature supresses signatures' '
+	git config --unset-all format.signature &&
+	git format-patch --stdout --no-signature -1 >output &&
+	check_patch output &&
+	! grep "^-- \$" output
+'
+
+test_expect_success 'format-patch --signature="" supresses signatures' '
+	git format-patch --stdout --signature="" -1 >output &&
+	check_patch output &&
+	! grep "^-- \$" output
+'
+
+test_expect_success TTY 'format-patch --stdout paginates' '
+	rm -f pager_used &&
+	(
+		GIT_PAGER="wc >pager_used" &&
+		export GIT_PAGER &&
+		test_terminal git format-patch --stdout --all
+	) &&
+	test_path_is_file pager_used
+'
+
+ test_expect_success TTY 'format-patch --stdout pagination can be disabled' '
+	rm -f pager_used &&
+	(
+		GIT_PAGER="wc >pager_used" &&
+		export GIT_PAGER &&
+		test_terminal git --no-pager format-patch --stdout --all &&
+		test_terminal git -c "pager.format-patch=false" format-patch --stdout --all
+	) &&
+	test_path_is_missing pager_used &&
+	test_path_is_missing .git/pager_used
+'
+
+test_expect_success 'format-patch handles multi-line subjects' '
+	rm -rf patches/ &&
+	echo content >>file &&
+	for i in one two three; do echo $i; done >msg &&
+	git add file &&
+	git commit -F msg &&
+	git format-patch -o patches -1 &&
+	grep ^Subject: patches/0001-one.patch >actual &&
+	echo "Subject: [PATCH] one two three" >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'format-patch handles multi-line encoded subjects' '
+	rm -rf patches/ &&
+	echo content >>file &&
+	for i in en två tre; do echo $i; done >msg &&
+	git add file &&
+	git commit -F msg &&
+	git format-patch -o patches -1 &&
+	grep ^Subject: patches/0001-en.patch >actual &&
+	echo "Subject: [PATCH] =?UTF-8?q?en=20tv=C3=A5=20tre?=" >expect &&
+	test_cmp expect actual
+'
+
+M8="foo bar "
+M64=$M8$M8$M8$M8$M8$M8$M8$M8
+M512=$M64$M64$M64$M64$M64$M64$M64$M64
+cat >expect <<'EOF'
+Subject: [PATCH] foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo
+ bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo
+ bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo
+ bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo
+ bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar
+EOF
+test_expect_success 'format-patch wraps extremely long headers (ascii)' '
+	echo content >>file &&
+	git add file &&
+	git commit -m "$M512" &&
+	git format-patch --stdout -1 >patch &&
+	sed -n "/^Subject/p; /^ /p; /^$/q" <patch >subject &&
+	test_cmp expect subject
+'
+
+M8="föö bar "
+M64=$M8$M8$M8$M8$M8$M8$M8$M8
+M512=$M64$M64$M64$M64$M64$M64$M64$M64
+cat >expect <<'EOF'
+Subject: [PATCH] =?UTF-8?q?f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
+ =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
+ =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
+ =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
+ =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
+ =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
+ =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
+ =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
+ =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
+ =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
+ =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
+ =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
+ =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
+ =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
+ =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
+ =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
+ =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
+ =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
+ =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
+ =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
+ =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
+ =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar?=
+EOF
+test_expect_success 'format-patch wraps extremely long headers (rfc2047)' '
+	rm -rf patches/ &&
+	echo content >>file &&
+	git add file &&
+	git commit -m "$M512" &&
+	git format-patch --stdout -1 >patch &&
+	sed -n "/^Subject/p; /^ /p; /^$/q" <patch >subject &&
+	test_cmp expect subject
+'
+
+M8="foo_bar_"
+M64=$M8$M8$M8$M8$M8$M8$M8$M8
+cat >expect <<EOF
+From: $M64
+ <foobar@foo.bar>
+EOF
+test_expect_success 'format-patch wraps non-quotable headers' '
+	rm -rf patches/ &&
+	echo content >>file &&
+	git add file &&
+	git commit -mfoo --author "$M64 <foobar@foo.bar>" &&
+	git format-patch --stdout -1 >patch &&
+	sed -n "/^From: /p; /^ /p; /^$/q" <patch >from &&
+	test_cmp expect from
+'
+
+check_author() {
+	echo content >>file &&
+	git add file &&
+	GIT_AUTHOR_NAME=$1 git commit -m author-check &&
+	git format-patch --stdout -1 >patch &&
+	grep ^From: patch >actual &&
+	test_cmp expect actual
+}
+
+cat >expect <<'EOF'
+From: "Foo B. Bar" <author@example.com>
+EOF
+test_expect_success 'format-patch quotes dot in headers' '
+	check_author "Foo B. Bar"
+'
+
+cat >expect <<'EOF'
+From: "Foo \"The Baz\" Bar" <author@example.com>
+EOF
+test_expect_success 'format-patch quotes double-quote in headers' '
+	check_author "Foo \"The Baz\" Bar"
+'
+
+cat >expect <<'EOF'
+From: =?UTF-8?q?"F=C3=B6o=20B.=20Bar"?= <author@example.com>
+EOF
+test_expect_success 'rfc2047-encoded headers also double-quote 822 specials' '
+	check_author "Föo B. Bar"
+'
+
+cat >expect <<'EOF'
+Subject: header with . in it
+EOF
+test_expect_success 'subject lines do not have 822 atom-quoting' '
+	echo content >>file &&
+	git add file &&
+	git commit -m "header with . in it" &&
+	git format-patch -k -1 --stdout >patch &&
+	grep ^Subject: patch >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+Subject: [PREFIX 1/1] header with . in it
+EOF
+test_expect_success 'subject prefixes have space prepended' '
+	git format-patch -n -1 --stdout --subject-prefix=PREFIX >patch &&
+	grep ^Subject: patch >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+Subject: [1/1] header with . in it
+EOF
+test_expect_success 'empty subject prefix does not have extra space' '
+	git format-patch -n -1 --stdout --subject-prefix= >patch &&
+	grep ^Subject: patch >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'format patch ignores color.ui' '
+	test_unconfig color.ui &&
+	git format-patch --stdout -1 >expect &&
+	test_config color.ui always &&
+	git format-patch --stdout -1 >actual &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh
index 6d13da3..cc3db13 100755
--- a/t/t4015-diff-whitespace.sh
+++ b/t/t4015-diff-whitespace.sh
@@ -93,8 +93,6 @@
 test_expect_success 'another test, without options' 'test_cmp expect out'
 
 cat << EOF > expect
-diff --git a/x b/x
-index d99af23..8b32fb5 100644
 EOF
 git diff -w > out
 test_expect_success 'another test, with -w' 'test_cmp expect out'
@@ -105,7 +103,7 @@
 git diff -w -b --ignore-space-at-eol > out
 test_expect_success 'another test, with -w -b --ignore-space-at-eol' 'test_cmp expect out'
 
-tr 'Q' '\015' << EOF > expect
+tr 'Q_' '\015 ' << EOF > expect
 diff --git a/x b/x
 index d99af23..8b32fb5 100644
 --- a/x
@@ -113,19 +111,19 @@
 @@ -1,6 +1,6 @@
 -whitespace at beginning
 +	whitespace at beginning
- whitespace change
+ whitespace 	 change
 -whitespace in the middle
 +white space in the middle
- whitespace at end
+ whitespace at end__
  unchanged line
- CR at endQ
+ CR at end
 EOF
 git diff -b > out
 test_expect_success 'another test, with -b' 'test_cmp expect out'
 git diff -b --ignore-space-at-eol > out
 test_expect_success 'another test, with -b --ignore-space-at-eol' 'test_cmp expect out'
 
-tr 'Q' '\015' << EOF > expect
+tr 'Q_' '\015 ' << EOF > expect
 diff --git a/x b/x
 index d99af23..8b32fb5 100644
 --- a/x
@@ -137,9 +135,9 @@
 +	whitespace at beginning
 +whitespace 	 change
 +white space in the middle
- whitespace at end
+ whitespace at end__
  unchanged line
- CR at endQ
+ CR at end
 EOF
 git diff --ignore-space-at-eol > out
 test_expect_success 'another test, with --ignore-space-at-eol' 'test_cmp expect out'
@@ -332,7 +330,7 @@
 
 test_expect_success 'check spaces as indentation (indent-with-non-tab: off)' '
 
-	git config core.whitespace "-indent-with-non-tab"
+	git config core.whitespace "-indent-with-non-tab" &&
 	echo "        foo ();" > x &&
 	git diff --check
 
@@ -346,6 +344,13 @@
 
 '
 
+test_expect_success 'ditto, but tabwidth=9' '
+
+	git config core.whitespace "indent-with-non-tab,tabwidth=9" &&
+	git diff --check
+
+'
+
 test_expect_success 'check tabs and spaces as indentation (indent-with-non-tab: on)' '
 
 	git config core.whitespace "indent-with-non-tab" &&
@@ -354,6 +359,69 @@
 
 '
 
+test_expect_success 'ditto, but tabwidth=10' '
+
+	git config core.whitespace "indent-with-non-tab,tabwidth=10" &&
+	test_must_fail git diff --check
+
+'
+
+test_expect_success 'ditto, but tabwidth=20' '
+
+	git config core.whitespace "indent-with-non-tab,tabwidth=20" &&
+	git diff --check
+
+'
+
+test_expect_success 'check tabs as indentation (tab-in-indent: off)' '
+
+	git config core.whitespace "-tab-in-indent" &&
+	echo "	foo ();" > x &&
+	git diff --check
+
+'
+
+test_expect_success 'check tabs as indentation (tab-in-indent: on)' '
+
+	git config core.whitespace "tab-in-indent" &&
+	echo "	foo ();" > x &&
+	test_must_fail git diff --check
+
+'
+
+test_expect_success 'check tabs and spaces as indentation (tab-in-indent: on)' '
+
+	git config core.whitespace "tab-in-indent" &&
+	echo "	                foo ();" > x &&
+	test_must_fail git diff --check
+
+'
+
+test_expect_success 'ditto, but tabwidth=1 (must be irrelevant)' '
+
+	git config core.whitespace "tab-in-indent,tabwidth=1" &&
+	test_must_fail git diff --check
+
+'
+
+test_expect_success 'check tab-in-indent and indent-with-non-tab conflict' '
+
+	git config core.whitespace "tab-in-indent,indent-with-non-tab" &&
+	echo "foo ();" > x &&
+	test_must_fail git diff --check
+
+'
+
+test_expect_success 'check tab-in-indent excluded from wildcard whitespace attribute' '
+
+	git config --unset core.whitespace &&
+	echo "x whitespace" > .gitattributes &&
+	echo "	  foo ();" > x &&
+	git diff --check &&
+	rm -f .gitattributes
+
+'
+
 test_expect_success 'line numbers in --check output are correct' '
 
 	echo "" > x &&
@@ -362,10 +430,17 @@
 
 '
 
-test_expect_success 'checkdiff detects trailing blank lines' '
+test_expect_success 'checkdiff detects new trailing blank lines (1)' '
 	echo "foo();" >x &&
 	echo "" >>x &&
-	git diff --check | grep "ends with blank"
+	git diff --check | grep "new blank line"
+'
+
+test_expect_success 'checkdiff detects new trailing blank lines (2)' '
+	{ echo a; echo b; echo; echo; } >x &&
+	git add x &&
+	{ echo a; echo; echo; echo; echo; } >x &&
+	git diff --check | grep "new blank line"
 '
 
 test_expect_success 'checkdiff allows new blank lines' '
@@ -379,6 +454,55 @@
 	git diff --check
 '
 
+cat <<EOF >expect
+EOF
+test_expect_success 'whitespace-only changes not reported' '
+	git reset --hard &&
+	echo >x "hello world" &&
+	git add x &&
+	git commit -m "hello 1" &&
+	echo >x "hello  world" &&
+	git diff -b >actual &&
+	test_cmp expect actual
+'
+
+cat <<EOF >expect
+diff --git a/x b/z
+similarity index NUM%
+rename from x
+rename to z
+index 380c32a..a97b785 100644
+EOF
+test_expect_success 'whitespace-only changes reported across renames' '
+	git reset --hard &&
+	for i in 1 2 3 4 5 6 7 8 9; do echo "$i$i$i$i$i$i"; done >x &&
+	git add x &&
+	git commit -m "base" &&
+	sed -e "5s/^/ /" x >z &&
+	git rm x &&
+	git add z &&
+	git diff -w -M --cached |
+	sed -e "/^similarity index /s/[0-9][0-9]*/NUM/" >actual &&
+	test_cmp expect actual
+'
+
+cat >expected <<\EOF
+diff --git a/empty b/void
+similarity index 100%
+rename from empty
+rename to void
+EOF
+
+test_expect_success 'rename empty' '
+	git reset --hard &&
+	>empty &&
+	git add empty &&
+	git commit -m empty &&
+	git mv empty void &&
+	git diff -w --cached -M >current &&
+	test_cmp expected current
+'
+
 test_expect_success 'combined diff with autocrlf conversion' '
 
 	git reset --hard &&
@@ -395,4 +519,41 @@
 
 '
 
+# Start testing the colored format for whitespace checks
+
+test_expect_success 'setup diff colors' '
+	git config color.diff always &&
+	git config color.diff.plain normal &&
+	git config color.diff.meta bold &&
+	git config color.diff.frag cyan &&
+	git config color.diff.func normal &&
+	git config color.diff.old red &&
+	git config color.diff.new green &&
+	git config color.diff.commit yellow &&
+	git config color.diff.whitespace "normal red" &&
+
+	git config core.autocrlf false
+'
+cat >expected <<\EOF
+<BOLD>diff --git a/x b/x<RESET>
+<BOLD>index 9daeafb..2874b91 100644<RESET>
+<BOLD>--- a/x<RESET>
+<BOLD>+++ b/x<RESET>
+<CYAN>@@ -1 +1,4 @@<RESET>
+ test<RESET>
+<GREEN>+<RESET><GREEN>{<RESET>
+<GREEN>+<RESET><BRED>	<RESET>
+<GREEN>+<RESET><GREEN>}<RESET>
+EOF
+
+test_expect_success 'diff that introduces a line with only tabs' '
+	git config core.whitespace blank-at-eol &&
+	git reset --hard &&
+	echo "test" > x &&
+	git commit -m "initial" x &&
+	echo "{NTN}" | tr "NT" "\n\t" >> x &&
+	git -c color.diff=always diff | test_decode_color >current &&
+	test_cmp expected current
+'
+
 test_done
diff --git a/t/t4016-diff-quote.sh b/t/t4016-diff-quote.sh
index 55eb5f8..97b8177 100755
--- a/t/t4016-diff-quote.sh
+++ b/t/t4016-diff-quote.sh
@@ -13,12 +13,14 @@
 P2='pathname with SP'
 P3='pathname
 with LF'
-: 2>/dev/null >"$P1" && test -f "$P1" && rm -f "$P1" || {
-	say 'Your filesystem does not allow tabs in filenames, test skipped.'
-	test_done
-}
+if : 2>/dev/null >"$P1" && test -f "$P1" && rm -f "$P1"
+then
+	test_set_prereq TABS_IN_FILENAMES
+else
+	say 'Your filesystem does not allow tabs in filenames'
+fi
 
-test_expect_success setup '
+test_expect_success TABS_IN_FILENAMES setup '
 	echo P0.0 >"$P0.0" &&
 	echo P0.1 >"$P0.1" &&
 	echo P0.2 >"$P0.2" &&
@@ -38,6 +40,7 @@
 	:
 '
 
+test_expect_success TABS_IN_FILENAMES 'setup expected files' '
 cat >expect <<\EOF
  rename pathname.1 => "Rpathname\twith HT.0" (100%)
  rename pathname.3 => "Rpathname\nwith LF.0" (100%)
@@ -47,24 +50,40 @@
  rename pathname.0 => Rpathname.0 (100%)
  rename "pathname\twith HT.0" => Rpathname.1 (100%)
 EOF
-test_expect_success 'git diff --summary -M HEAD' '
+'
+
+test_expect_success TABS_IN_FILENAMES 'git diff --summary -M HEAD' '
 	git diff --summary -M HEAD >actual &&
 	test_cmp expect actual
 '
 
-cat >expect <<\EOF
- pathname.1 => "Rpathname\twith HT.0"            |    0
- pathname.3 => "Rpathname\nwith LF.0"            |    0
- "pathname\twith HT.3" => "Rpathname\nwith LF.1" |    0
- pathname.2 => Rpathname with SP.0               |    0
- "pathname\twith HT.2" => Rpathname with SP.1    |    0
- pathname.0 => Rpathname.0                       |    0
- "pathname\twith HT.0" => Rpathname.1            |    0
- 7 files changed, 0 insertions(+), 0 deletions(-)
-EOF
-test_expect_success 'git diff --stat -M HEAD' '
-	git diff --stat -M HEAD >actual &&
+test_expect_success TABS_IN_FILENAMES 'git diff --numstat -M HEAD' '
+	cat >expect <<-\EOF &&
+	0	0	pathname.1 => "Rpathname\twith HT.0"
+	0	0	pathname.3 => "Rpathname\nwith LF.0"
+	0	0	"pathname\twith HT.3" => "Rpathname\nwith LF.1"
+	0	0	pathname.2 => Rpathname with SP.0
+	0	0	"pathname\twith HT.2" => Rpathname with SP.1
+	0	0	pathname.0 => Rpathname.0
+	0	0	"pathname\twith HT.0" => Rpathname.1
+	EOF
+	git diff --numstat -M HEAD >actual &&
 	test_cmp expect actual
 '
 
+test_expect_success TABS_IN_FILENAMES 'git diff --stat -M HEAD' '
+	cat >expect <<-\EOF &&
+	 pathname.1 => "Rpathname\twith HT.0"            | 0
+	 pathname.3 => "Rpathname\nwith LF.0"            | 0
+	 "pathname\twith HT.3" => "Rpathname\nwith LF.1" | 0
+	 pathname.2 => Rpathname with SP.0               | 0
+	 "pathname\twith HT.2" => Rpathname with SP.1    | 0
+	 pathname.0 => Rpathname.0                       | 0
+	 "pathname\twith HT.0" => Rpathname.1            | 0
+	 7 files changed, 0 insertions(+), 0 deletions(-)
+	EOF
+	git diff --stat -M HEAD >actual &&
+	test_i18ncmp expect actual
+'
+
 test_done
diff --git a/t/t4017-diff-retval.sh b/t/t4017-diff-retval.sh
index 60dd201..95a7ca7 100755
--- a/t/t4017-diff-retval.sh
+++ b/t/t4017-diff-retval.sh
@@ -5,6 +5,9 @@
 . ./test-lib.sh
 
 test_expect_success 'setup' '
+	echo "1 " >a &&
+	git add . &&
+	git commit -m zeroth &&
 	echo 1 >a &&
 	git add . &&
 	git commit -m first &&
@@ -13,67 +16,62 @@
 	git commit -a -m second
 '
 
+test_expect_success 'git diff --quiet -w  HEAD^^ HEAD^' '
+	git diff --quiet -w HEAD^^ HEAD^
+'
+
+test_expect_success 'git diff --quiet HEAD^^ HEAD^' '
+	test_must_fail git diff --quiet HEAD^^ HEAD^
+'
+
+test_expect_success 'git diff --quiet -w  HEAD^ HEAD' '
+	test_must_fail git diff --quiet -w HEAD^ HEAD
+'
+
 test_expect_success 'git diff-tree HEAD^ HEAD' '
-	git diff-tree --exit-code HEAD^ HEAD
-	test $? = 1
+	test_expect_code 1 git diff-tree --exit-code HEAD^ HEAD
 '
 test_expect_success 'git diff-tree HEAD^ HEAD -- a' '
 	git diff-tree --exit-code HEAD^ HEAD -- a
-	test $? = 0
 '
 test_expect_success 'git diff-tree HEAD^ HEAD -- b' '
-	git diff-tree --exit-code HEAD^ HEAD -- b
-	test $? = 1
+	test_expect_code 1 git diff-tree --exit-code HEAD^ HEAD -- b
 '
 test_expect_success 'echo HEAD | git diff-tree --stdin' '
-	echo $(git rev-parse HEAD) | git diff-tree --exit-code --stdin
-	test $? = 1
+	echo $(git rev-parse HEAD) | test_expect_code 1 git diff-tree --exit-code --stdin
 '
 test_expect_success 'git diff-tree HEAD HEAD' '
 	git diff-tree --exit-code HEAD HEAD
-	test $? = 0
 '
 test_expect_success 'git diff-files' '
 	git diff-files --exit-code
-	test $? = 0
 '
 test_expect_success 'git diff-index --cached HEAD' '
 	git diff-index --exit-code --cached HEAD
-	test $? = 0
 '
 test_expect_success 'git diff-index --cached HEAD^' '
-	git diff-index --exit-code --cached HEAD^
-	test $? = 1
+	test_expect_code 1 git diff-index --exit-code --cached HEAD^
 '
 test_expect_success 'git diff-index --cached HEAD^' '
 	echo text >>b &&
 	echo 3 >c &&
-	git add . && {
-		git diff-index --exit-code --cached HEAD^
-		test $? = 1
-	}
+	git add . &&
+	test_expect_code 1 git diff-index --exit-code --cached HEAD^
 '
 test_expect_success 'git diff-tree -Stext HEAD^ HEAD -- b' '
-	git commit -m "text in b" && {
-		git diff-tree -p --exit-code -Stext HEAD^ HEAD -- b
-		test $? = 1
-	}
+	git commit -m "text in b" &&
+	test_expect_code 1 git diff-tree -p --exit-code -Stext HEAD^ HEAD -- b
 '
 test_expect_success 'git diff-tree -Snot-found HEAD^ HEAD -- b' '
 	git diff-tree -p --exit-code -Snot-found HEAD^ HEAD -- b
-	test $? = 0
 '
 test_expect_success 'git diff-files' '
-	echo 3 >>c && {
-		git diff-files --exit-code
-		test $? = 1
-	}
+	echo 3 >>c &&
+	test_expect_code 1 git diff-files --exit-code
 '
 test_expect_success 'git diff-index --cached HEAD' '
-	git update-index c && {
-		git diff-index --exit-code --cached HEAD
-		test $? = 1
-	}
+	git update-index c &&
+	test_expect_code 1 git diff-index --exit-code --cached HEAD
 '
 
 test_expect_success '--check --exit-code returns 0 for no difference' '
@@ -85,31 +83,26 @@
 test_expect_success '--check --exit-code returns 1 for a clean difference' '
 
 	echo "good" > a &&
-	git diff --check --exit-code
-	test $? = 1
+	test_expect_code 1 git diff --check --exit-code
 
 '
 
 test_expect_success '--check --exit-code returns 3 for a dirty difference' '
 
 	echo "bad   " >> a &&
-	git diff --check --exit-code
-	test $? = 3
+	test_expect_code 3 git diff --check --exit-code
 
 '
 
 test_expect_success '--check with --no-pager returns 2 for dirty difference' '
 
-	git --no-pager diff --check
-	test $? = 2
+	test_expect_code 2 git --no-pager diff --check
 
 '
 
-
 test_expect_success 'check should test not just the last line' '
 	echo "" >>a &&
-	git --no-pager diff --check
-	test $? = 2
+	test_expect_code 2 git --no-pager diff --check
 
 '
 
@@ -119,12 +112,26 @@
 	echo binary >>b &&
 	git commit -m "side" b &&
 	test_must_fail git merge master &&
-	git add b && (
-		git --no-pager diff --cached --check >test.out
-		test $? = 2
-	) &&
+	git add b &&
+	test_expect_code 2 git --no-pager diff --cached --check >test.out &&
 	test 3 = $(grep "conflict marker" test.out | wc -l) &&
 	git reset --hard
 '
 
+test_expect_success 'check honors conflict marker length' '
+	git reset --hard &&
+	echo ">>>>>>> boo" >>b &&
+	echo "======" >>a &&
+	git diff --check a &&
+	test_expect_code 2 git diff --check b &&
+	git reset --hard &&
+	echo ">>>>>>>> boo" >>b &&
+	echo "========" >>a &&
+	git diff --check &&
+	echo "b conflict-marker-size=8" >.gitattributes &&
+	test_expect_code 2 git diff --check b &&
+	git diff --check a &&
+	git reset --hard
+'
+
 test_done
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index 5b10e97..4bd2a1c 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -9,8 +9,7 @@
 
 LF='
 '
-
-cat > Beer.java << EOF
+cat >Beer.java <<\EOF
 public class Beer
 {
 	int special;
@@ -29,56 +28,163 @@
 	}
 }
 EOF
+sed 's/beer\\/beer,\\/' <Beer.java >Beer-correct.java
+cat >Beer.perl <<\EOT
+package Beer;
 
-sed 's/beer\\/beer,\\/' < Beer.java > Beer-correct.java
+use strict;
+use warnings;
+use parent qw(Exporter);
+our @EXPORT_OK = qw(round finalround);
 
-builtin_patterns="bibtex cpp html java objc pascal php python ruby tex"
-for p in $builtin_patterns
+sub other; # forward declaration
+
+# hello
+
+sub round {
+	my ($n) = @_;
+	print "$n bottles of beer on the wall ";
+	print "$n bottles of beer\n";
+	print "Take one down, pass it around, ";
+	$n = $n - 1;
+	print "$n bottles of beer on the wall.\n";
+}
+
+sub finalround
+{
+	print "Go to the store, buy some more\n";
+	print "99 bottles of beer on the wall.\n");
+}
+
+sub withheredocument {
+	print <<"EOF"
+decoy here-doc
+EOF
+	# some lines of context
+	# to pad it out
+	print "hello\n";
+}
+
+__END__
+
+=head1 NAME
+
+Beer - subroutine to output fragment of a drinking song
+
+=head1 SYNOPSIS
+
+	use Beer qw(round finalround);
+
+	sub song {
+		for (my $i = 99; $i > 0; $i--) {
+			round $i;
+		}
+		finalround;
+	}
+
+	song;
+
+=cut
+EOT
+sed -e '
+	s/hello/goodbye/
+	s/beer\\/beer,\\/
+	s/more\\/more,\\/
+	s/song;/song();/
+' <Beer.perl >Beer-correct.perl
+
+test_config () {
+	git config "$1" "$2" &&
+	test_when_finished "git config --unset $1"
+}
+
+test_expect_funcname () {
+	lang=${2-java}
+	test_expect_code 1 git diff --no-index -U1 \
+		"Beer.$lang" "Beer-correct.$lang" >diff &&
+	grep "^@@.*@@ $1" diff
+}
+
+for p in bibtex cpp csharp fortran html java matlab objc pascal perl php python ruby tex
 do
 	test_expect_success "builtin $p pattern compiles" '
-		echo "*.java diff=$p" > .gitattributes &&
-		! ( git diff --no-index Beer.java Beer-correct.java 2>&1 |
-			grep "fatal" > /dev/null )
+		echo "*.java diff=$p" >.gitattributes &&
+		test_expect_code 1 git diff --no-index \
+			Beer.java Beer-correct.java 2>msg &&
+		! grep fatal msg &&
+		! grep error msg
+	'
+	test_expect_success "builtin $p wordRegex pattern compiles" '
+		echo "*.java diff=$p" >.gitattributes &&
+		test_expect_code 1 git diff --no-index --word-diff \
+			Beer.java Beer-correct.java 2>msg &&
+		! grep fatal msg &&
+		! grep error msg
 	'
 done
 
 test_expect_success 'default behaviour' '
 	rm -f .gitattributes &&
-	git diff --no-index Beer.java Beer-correct.java |
-	grep "^@@.*@@ public class Beer"
+	test_expect_funcname "public class Beer\$"
+'
+
+test_expect_success 'set up .gitattributes declaring drivers to test' '
+	cat >.gitattributes <<-\EOF
+	*.java diff=java
+	*.perl diff=perl
+	EOF
 '
 
 test_expect_success 'preset java pattern' '
-	echo "*.java diff=java" >.gitattributes &&
-	git diff --no-index Beer.java Beer-correct.java |
-	grep "^@@.*@@ public static void main("
+	test_expect_funcname "public static void main("
 '
 
-git config diff.java.funcname '!static
-!String
-[^ 	].*s.*'
+test_expect_success 'preset perl pattern' '
+	test_expect_funcname "sub round {\$" perl
+'
+
+test_expect_success 'perl pattern accepts K&R style brace placement, too' '
+	test_expect_funcname "sub finalround\$" perl
+'
+
+test_expect_success 'but is not distracted by end of <<here document' '
+	test_expect_funcname "sub withheredocument {\$" perl
+'
+
+test_expect_success 'perl pattern is not distracted by sub within POD' '
+	test_expect_funcname "=head" perl
+'
+
+test_expect_success 'perl pattern gets full line of POD header' '
+	test_expect_funcname "=head1 SYNOPSIS\$" perl
+'
+
+test_expect_success 'perl pattern is not distracted by forward declaration' '
+	test_expect_funcname "package Beer;\$" perl
+'
 
 test_expect_success 'custom pattern' '
-	git diff --no-index Beer.java Beer-correct.java |
-	grep "^@@.*@@ int special;$"
+	test_config diff.java.funcname "!static
+!String
+[^ 	].*s.*" &&
+	test_expect_funcname "int special;\$"
 '
 
 test_expect_success 'last regexp must not be negated' '
-	git config diff.java.funcname "!static" &&
-	git diff --no-index Beer.java Beer-correct.java 2>&1 |
-	grep "fatal: Last expression must not be negated:"
+	test_config diff.java.funcname "!static" &&
+	test_expect_code 128 git diff --no-index Beer.java Beer-correct.java 2>msg &&
+	grep ": Last expression must not be negated:" msg
 '
 
 test_expect_success 'pattern which matches to end of line' '
-	git config diff.java.funcname "Beer$" &&
-	git diff --no-index Beer.java Beer-correct.java |
-	grep "^@@.*@@ Beer"
+	test_config diff.java.funcname "Beer\$" &&
+	test_expect_funcname "Beer\$"
 '
 
 test_expect_success 'alternation in pattern' '
-	git config diff.java.xfuncname "^[ 	]*((public|static).*)$" &&
-	git diff --no-index Beer.java Beer-correct.java |
-	grep "^@@.*@@ public static void main("
+	test_config diff.java.funcname "Beer$" &&
+	test_config diff.java.xfuncname "^[ 	]*((public|static).*)$" &&
+	test_expect_funcname "public static void main("
 '
 
 test_done
diff --git a/t/t4019-diff-wserror.sh b/t/t4019-diff-wserror.sh
index 84a1fe3..a501975 100755
--- a/t/t4019-diff-wserror.sh
+++ b/t/t4019-diff-wserror.sh
@@ -20,11 +20,28 @@
 
 blue_grep='7;34m' ;# ESC [ 7 ; 3 4 m
 
+printf "\033[%s" "$blue_grep" >check-grep
+if (grep "$blue_grep" <check-grep | grep "$blue_grep") >/dev/null 2>&1
+then
+	grep_a=grep
+elif (grep -a "$blue_grep" <check-grep | grep -a "$blue_grep") >/dev/null 2>&1
+then
+	grep_a='grep -a'
+else
+	grep_a=grep ;# expected to fail...
+fi
+rm -f check-grep
+
+prepare_output () {
+	git diff --color >output
+	$grep_a "$blue_grep" output >error
+	$grep_a -v "$blue_grep" output >normal
+	return 0
+}
+
 test_expect_success default '
 
-	git diff --color >output
-	grep "$blue_grep" output >error
-	grep -v "$blue_grep" output >normal
+	prepare_output &&
 
 	grep Eight normal >/dev/null &&
 	grep HT error >/dev/null &&
@@ -34,12 +51,67 @@
 
 '
 
+test_expect_success 'default (attribute)' '
+
+	test_might_fail git config --unset core.whitespace &&
+	echo "F whitespace" >.gitattributes &&
+	prepare_output &&
+
+	grep Eight error >/dev/null &&
+	grep HT error >/dev/null &&
+	grep With error >/dev/null &&
+	grep Return error >/dev/null &&
+	grep No normal >/dev/null
+
+'
+
+test_expect_success 'default, tabwidth=10 (attribute)' '
+
+	git config core.whitespace "tabwidth=10" &&
+	echo "F whitespace" >.gitattributes &&
+	prepare_output &&
+
+	grep Eight normal >/dev/null &&
+	grep HT error >/dev/null &&
+	grep With error >/dev/null &&
+	grep Return error >/dev/null &&
+	grep No normal >/dev/null
+
+'
+
+test_expect_success 'no check (attribute)' '
+
+	test_might_fail git config --unset core.whitespace &&
+	echo "F -whitespace" >.gitattributes &&
+	prepare_output &&
+
+	grep Eight normal >/dev/null &&
+	grep HT normal >/dev/null &&
+	grep With normal >/dev/null &&
+	grep Return normal >/dev/null &&
+	grep No normal >/dev/null
+
+'
+
+test_expect_success 'no check, tabwidth=10 (attribute), must be irrelevant' '
+
+	git config core.whitespace "tabwidth=10" &&
+	echo "F -whitespace" >.gitattributes &&
+	prepare_output &&
+
+	grep Eight normal >/dev/null &&
+	grep HT normal >/dev/null &&
+	grep With normal >/dev/null &&
+	grep Return normal >/dev/null &&
+	grep No normal >/dev/null
+
+'
+
 test_expect_success 'without -trail' '
 
-	git config core.whitespace -trail
-	git diff --color >output
-	grep "$blue_grep" output >error
-	grep -v "$blue_grep" output >normal
+	rm -f .gitattributes &&
+	git config core.whitespace -trail &&
+	prepare_output &&
 
 	grep Eight normal >/dev/null &&
 	grep HT error >/dev/null &&
@@ -51,11 +123,9 @@
 
 test_expect_success 'without -trail (attribute)' '
 
-	git config --unset core.whitespace
-	echo "F whitespace=-trail" >.gitattributes
-	git diff --color >output
-	grep "$blue_grep" output >error
-	grep -v "$blue_grep" output >normal
+	test_might_fail git config --unset core.whitespace &&
+	echo "F whitespace=-trail" >.gitattributes &&
+	prepare_output &&
 
 	grep Eight normal >/dev/null &&
 	grep HT error >/dev/null &&
@@ -67,11 +137,9 @@
 
 test_expect_success 'without -space' '
 
-	rm -f .gitattributes
-	git config core.whitespace -space
-	git diff --color >output
-	grep "$blue_grep" output >error
-	grep -v "$blue_grep" output >normal
+	rm -f .gitattributes &&
+	git config core.whitespace -space &&
+	prepare_output &&
 
 	grep Eight normal >/dev/null &&
 	grep HT normal >/dev/null &&
@@ -83,11 +151,9 @@
 
 test_expect_success 'without -space (attribute)' '
 
-	git config --unset core.whitespace
-	echo "F whitespace=-space" >.gitattributes
-	git diff --color >output
-	grep "$blue_grep" output >error
-	grep -v "$blue_grep" output >normal
+	test_might_fail git config --unset core.whitespace &&
+	echo "F whitespace=-space" >.gitattributes &&
+	prepare_output &&
 
 	grep Eight normal >/dev/null &&
 	grep HT normal >/dev/null &&
@@ -99,11 +165,9 @@
 
 test_expect_success 'with indent-non-tab only' '
 
-	rm -f .gitattributes
-	git config core.whitespace indent,-trailing,-space
-	git diff --color >output
-	grep "$blue_grep" output >error
-	grep -v "$blue_grep" output >normal
+	rm -f .gitattributes &&
+	git config core.whitespace indent,-trailing,-space &&
+	prepare_output &&
 
 	grep Eight error >/dev/null &&
 	grep HT normal >/dev/null &&
@@ -115,11 +179,9 @@
 
 test_expect_success 'with indent-non-tab only (attribute)' '
 
-	git config --unset core.whitespace
-	echo "F whitespace=indent,-trailing,-space" >.gitattributes
-	git diff --color >output
-	grep "$blue_grep" output >error
-	grep -v "$blue_grep" output >normal
+	test_might_fail git config --unset core.whitespace &&
+	echo "F whitespace=indent,-trailing,-space" >.gitattributes &&
+	prepare_output &&
 
 	grep Eight error >/dev/null &&
 	grep HT normal >/dev/null &&
@@ -129,13 +191,39 @@
 
 '
 
+test_expect_success 'with indent-non-tab only, tabwidth=10' '
+
+	rm -f .gitattributes &&
+	git config core.whitespace indent,tabwidth=10,-trailing,-space &&
+	prepare_output &&
+
+	grep Eight normal >/dev/null &&
+	grep HT normal >/dev/null &&
+	grep With normal >/dev/null &&
+	grep Return normal >/dev/null &&
+	grep No normal >/dev/null
+
+'
+
+test_expect_success 'with indent-non-tab only, tabwidth=10 (attribute)' '
+
+	test_might_fail git config --unset core.whitespace &&
+	echo "F whitespace=indent,-trailing,-space,tabwidth=10" >.gitattributes &&
+	prepare_output &&
+
+	grep Eight normal >/dev/null &&
+	grep HT normal >/dev/null &&
+	grep With normal >/dev/null &&
+	grep Return normal >/dev/null &&
+	grep No normal >/dev/null
+
+'
+
 test_expect_success 'with cr-at-eol' '
 
-	rm -f .gitattributes
-	git config core.whitespace cr-at-eol
-	git diff --color >output
-	grep "$blue_grep" output >error
-	grep -v "$blue_grep" output >normal
+	rm -f .gitattributes &&
+	git config core.whitespace cr-at-eol &&
+	prepare_output &&
 
 	grep Eight normal >/dev/null &&
 	grep HT error >/dev/null &&
@@ -147,11 +235,9 @@
 
 test_expect_success 'with cr-at-eol (attribute)' '
 
-	git config --unset core.whitespace
-	echo "F whitespace=trailing,cr-at-eol" >.gitattributes
-	git diff --color >output
-	grep "$blue_grep" output >error
-	grep -v "$blue_grep" output >normal
+	test_might_fail git config --unset core.whitespace &&
+	echo "F whitespace=trailing,cr-at-eol" >.gitattributes &&
+	prepare_output &&
 
 	grep Eight normal >/dev/null &&
 	grep HT error >/dev/null &&
@@ -165,7 +251,7 @@
 
 	rm -f .gitattributes &&
 	test_must_fail git diff --check >output &&
-	grep "ends with blank lines." output &&
+	grep "new blank line at" output &&
 	grep "trailing whitespace" output
 
 '
@@ -178,16 +264,34 @@
 
 '
 
+test_expect_success 'checkdiff shows correct line number for trailing blank lines' '
+
+	printf "a\nb\n" > G &&
+	git add G &&
+	printf "x\nx\nx\na\nb\nc\n\n" > G &&
+	[ "$(git diff --check -- G)" = "G:7: new blank line at EOF." ]
+
+'
+
 test_expect_success 'do not color trailing cr in context' '
-	git config --unset core.whitespace
+	test_might_fail git config --unset core.whitespace &&
 	rm -f .gitattributes &&
 	echo AAAQ | tr Q "\015" >G &&
 	git add G &&
-	echo BBBQ | tr Q "\015" >>G
+	echo BBBQ | tr Q "\015" >>G &&
 	git diff --color G | tr "\015" Q >output &&
 	grep "BBB.*${blue_grep}Q" output &&
 	grep "AAA.*\[mQ" output
 
 '
 
+test_expect_success 'color new trailing blank lines' '
+	{ echo a; echo b; echo; echo; } >x &&
+	git add x &&
+	{ echo a; echo; echo; echo; echo c; echo; echo; echo; echo; } >x &&
+	git diff --color x >output &&
+	cnt=$($grep_a "${blue_grep}" output | wc -l) &&
+	test $cnt = 2
+'
+
 test_done
diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh
index 4ea42e0..2e7d73f 100755
--- a/t/t4020-diff-external.sh
+++ b/t/t4020-diff-external.sh
@@ -4,8 +4,6 @@
 
 . ./test-lib.sh
 
-_z40=0000000000000000000000000000000000000000
-
 test_expect_success setup '
 
 	test_tick &&
@@ -50,7 +48,53 @@
 
 '
 
+test_expect_success SYMLINKS 'typechange diff' '
+	rm -f file &&
+	ln -s elif file &&
+	GIT_EXTERNAL_DIFF=echo git diff  | {
+		read path oldfile oldhex oldmode newfile newhex newmode &&
+		test "z$path" = zfile &&
+		test "z$oldmode" = z100644 &&
+		test "z$newhex" = "z$_z40" &&
+		test "z$newmode" = z120000 &&
+		oh=$(git rev-parse --verify HEAD:file) &&
+		test "z$oh" = "z$oldhex"
+	} &&
+	GIT_EXTERNAL_DIFF=echo git diff --no-ext-diff >actual &&
+	git diff >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'diff.external' '
+	git reset --hard &&
+	echo third >file &&
+	test_config diff.external echo &&
+	git diff | {
+		read path oldfile oldhex oldmode newfile newhex newmode &&
+		test "z$path" = zfile &&
+		test "z$oldmode" = z100644 &&
+		test "z$newhex" = "z$_z40" &&
+		test "z$newmode" = z100644 &&
+		oh=$(git rev-parse --verify HEAD:file) &&
+		test "z$oh" = "z$oldhex"
+	}
+'
+
+test_expect_success 'diff.external should apply only to diff' '
+	test_config diff.external echo &&
+	git log -p -1 HEAD |
+	grep "^diff --git a/file b/file"
+'
+
+test_expect_success 'diff.external and --no-ext-diff' '
+	test_config diff.external echo &&
+	git diff --no-ext-diff |
+	grep "^diff --git a/file b/file"
+'
+
 test_expect_success 'diff attribute' '
+	git reset --hard &&
+	echo third >file &&
 
 	git config diff.parrot.command echo &&
 
@@ -115,12 +159,25 @@
 
 '
 
+test_expect_success 'GIT_EXTERNAL_DIFF trumps diff.external' '
+	>.gitattributes &&
+	test_config diff.external "echo ext-global" &&
+	GIT_EXTERNAL_DIFF="echo ext-env" git diff | grep ext-env
+'
+
+test_expect_success 'attributes trump GIT_EXTERNAL_DIFF and diff.external' '
+	test_config diff.foo.command "echo ext-attribute" &&
+	test_config diff.external "echo ext-global" &&
+	echo "file diff=foo" >.gitattributes &&
+	GIT_EXTERNAL_DIFF="echo ext-env" git diff | grep ext-attribute
+'
+
 test_expect_success 'no diff with -diff' '
 	echo >.gitattributes "file -diff" &&
 	git diff | grep Binary
 '
 
-echo NULZbetweenZwords | perl -pe 'y/Z/\000/' > file
+echo NULZbetweenZwords | "$PERL_PATH" -pe 'y/Z/\000/' > file
 
 test_expect_success 'force diff with "diff"' '
 	echo >.gitattributes "file diff" &&
@@ -166,7 +223,7 @@
 	git update-index --assume-unchanged file &&
 	echo second >file &&
 	git diff --cached >actual &&
-	test_cmp ../t4020/diff.NUL actual
+	test_cmp "$TEST_DIRECTORY"/t4020/diff.NUL actual
 '
 
 test_done
diff --git a/t/t4021-format-patch-numbered.sh b/t/t4021-format-patch-numbered.sh
index 709b323..886494b 100755
--- a/t/t4021-format-patch-numbered.sh
+++ b/t/t4021-format-patch-numbered.sh
@@ -95,7 +95,7 @@
 
 test_expect_success 'format.numbered = auto' '
 
-	git config format.numbered auto
+	git config format.numbered auto &&
 	git format-patch --stdout HEAD~2 > patch5 &&
 	test_numbered patch5
 
diff --git a/t/t4022-diff-rewrite.sh b/t/t4022-diff-rewrite.sh
index 2a537a2..c00a94b 100755
--- a/t/t4022-diff-rewrite.sh
+++ b/t/t4022-diff-rewrite.sh
@@ -11,7 +11,9 @@
 	tr \
 	  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" \
 	  "nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM" \
-	  <"$TEST_DIRECTORY"/../COPYING >test
+	  <"$TEST_DIRECTORY"/../COPYING >test &&
+	echo "to be deleted" >test2 &&
+	git add test2
 
 '
 
@@ -25,5 +27,44 @@
 
 '
 
+cat >expect <<EOF
+diff --git a/test2 b/test2
+deleted file mode 100644
+index 4202011..0000000
+--- a/test2
++++ /dev/null
+@@ -1 +0,0 @@
+-to be deleted
+EOF
+test_expect_success 'show deletion diff without -D' '
+
+	rm test2 &&
+	git diff -- test2 >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<EOF
+diff --git a/test2 b/test2
+deleted file mode 100644
+index 4202011..0000000
+EOF
+test_expect_success 'suppress deletion diff with -D' '
+
+	git diff -D -- test2 >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'show deletion diff with -B' '
+
+	git diff -B -- test >actual &&
+	grep "Linus Torvalds" actual
+'
+
+test_expect_success 'suppress deletion diff with -B -D' '
+
+	git diff -B -D -- test >actual &&
+	grep -v "Linus Torvalds" actual
+'
+
 test_done
 
diff --git a/t/t4023-diff-rename-typechange.sh b/t/t4023-diff-rename-typechange.sh
index 9bdf659..5d20acf 100755
--- a/t/t4023-diff-rename-typechange.sh
+++ b/t/t4023-diff-rename-typechange.sh
@@ -4,13 +4,7 @@
 
 . ./test-lib.sh
 
-if ! test_have_prereq SYMLINKS
-then
-	say 'Symbolic links not supported, skipping tests.'
-	test_done
-fi
-
-test_expect_success setup '
+test_expect_success SYMLINKS setup '
 
 	rm -f foo bar &&
 	cat "$TEST_DIRECTORY"/../COPYING >foo &&
@@ -56,7 +50,7 @@
 
 '
 
-test_expect_success 'cross renames to be detected for regular files' '
+test_expect_success SYMLINKS 'cross renames to be detected for regular files' '
 
 	git diff-tree five six -r --name-status -B -M | sort >actual &&
 	{
@@ -67,7 +61,7 @@
 
 '
 
-test_expect_success 'cross renames to be detected for typechange' '
+test_expect_success SYMLINKS 'cross renames to be detected for typechange' '
 
 	git diff-tree one two -r --name-status -B -M | sort >actual &&
 	{
@@ -78,7 +72,7 @@
 
 '
 
-test_expect_success 'moves and renames' '
+test_expect_success SYMLINKS 'moves and renames' '
 
 	git diff-tree three four -r --name-status -B -M | sort >actual &&
 	{
diff --git a/t/t4026-color.sh b/t/t4026-color.sh
index b61e516..3726a0e 100755
--- a/t/t4026-color.sh
+++ b/t/t4026-color.sh
@@ -8,14 +8,13 @@
 
 color()
 {
-	git config diff.color.new "$1" &&
-	test "`git config --get-color diff.color.new`" = "$2"
+	actual=$(git config --get-color no.such.slot "$1") &&
+	test "$actual" = "$2"
 }
 
 invalid_color()
 {
-	git config diff.color.new "$1" &&
-	test -z "`git config --get-color diff.color.new 2>/dev/null`"
+	test_must_fail git config --get-color no.such.slot "$1"
 }
 
 test_expect_success 'reset' '
@@ -42,6 +41,14 @@
 	color "blue red ul" "[4;34;41m"
 '
 
+test_expect_success 'fg bg attr...' '
+	color "blue bold dim ul blink reverse" "[1;2;4;5;7;34m"
+'
+
+test_expect_success 'long color specification' '
+	color "254 255 bold dim ul blink reverse" "[1;2;4;5;7;38;5;254;48;5;255m"
+'
+
 test_expect_success '256 colors' '
 	color "254 bold 255" "[1;38;5;254;48;5;255m"
 '
@@ -66,4 +73,20 @@
 	invalid_color "dimX"
 '
 
+test_expect_success 'unknown color slots are ignored (diff)' '
+	git config color.diff.nosuchslotwilleverbedefined white &&
+	git diff --color
+'
+
+test_expect_success 'unknown color slots are ignored (branch)' '
+	git config color.branch.nosuchslotwilleverbedefined white &&
+	git branch -a
+'
+
+test_expect_success 'unknown color slots are ignored (status)' '
+	git config color.status.nosuchslotwilleverbedefined white || exit
+	git status
+	case $? in 0|1) : ok ;; *) false ;; esac
+'
+
 test_done
diff --git a/t/t4027-diff-submodule.sh b/t/t4027-diff-submodule.sh
index 5cf8924..518bf95 100755
--- a/t/t4027-diff-submodule.sh
+++ b/t/t4027-diff-submodule.sh
@@ -5,7 +5,6 @@
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/diff-lib.sh
 
-_z40=0000000000000000000000000000000000000000
 test_expect_success setup '
 	test_tick &&
 	test_create_repo sub &&
@@ -32,7 +31,8 @@
 		cd sub &&
 		git rev-list HEAD
 	) &&
-	echo ":160000 160000 $3 $_z40 M	sub" >expect
+	echo ":160000 160000 $3 $_z40 M	sub" >expect &&
+	subtip=$3 subprev=$2
 '
 
 test_expect_success 'git diff --raw HEAD' '
@@ -50,6 +50,261 @@
 	test_cmp expect actual.files
 '
 
+expect_from_to () {
+	printf "%sSubproject commit %s\n+Subproject commit %s\n" \
+		"-" "$1" "$2"
+}
+
+test_expect_success 'git diff HEAD' '
+	git diff HEAD >actual &&
+	sed -e "1,/^@@/d" actual >actual.body &&
+	expect_from_to >expect.body $subtip $subprev &&
+	test_cmp expect.body actual.body
+'
+
+test_expect_success 'git diff HEAD with dirty submodule (work tree)' '
+	echo >>sub/world &&
+	git diff HEAD >actual &&
+	sed -e "1,/^@@/d" actual >actual.body &&
+	expect_from_to >expect.body $subtip $subprev-dirty &&
+	test_cmp expect.body actual.body
+'
+
+test_expect_success 'git diff HEAD with dirty submodule (index)' '
+	(
+		cd sub &&
+		git reset --hard &&
+		echo >>world &&
+		git add world
+	) &&
+	git diff HEAD >actual &&
+	sed -e "1,/^@@/d" actual >actual.body &&
+	expect_from_to >expect.body $subtip $subprev-dirty &&
+	test_cmp expect.body actual.body
+'
+
+test_expect_success 'git diff HEAD with dirty submodule (untracked)' '
+	(
+		cd sub &&
+		git reset --hard &&
+		git clean -qfdx &&
+		>cruft
+	) &&
+	git diff HEAD >actual &&
+	sed -e "1,/^@@/d" actual >actual.body &&
+	expect_from_to >expect.body $subtip $subprev-dirty &&
+	test_cmp expect.body actual.body
+'
+
+test_expect_success 'git diff HEAD with dirty submodule (work tree, refs match)' '
+	git commit -m "x" sub &&
+	echo >>sub/world &&
+	git diff HEAD >actual &&
+	sed -e "1,/^@@/d" actual >actual.body &&
+	expect_from_to >expect.body $subprev $subprev-dirty &&
+	test_cmp expect.body actual.body &&
+	git diff --ignore-submodules HEAD >actual2 &&
+	! test -s actual2 &&
+	git diff --ignore-submodules=untracked HEAD >actual3 &&
+	sed -e "1,/^@@/d" actual3 >actual3.body &&
+	expect_from_to >expect.body $subprev $subprev-dirty &&
+	test_cmp expect.body actual3.body &&
+	git diff --ignore-submodules=dirty HEAD >actual4 &&
+	! test -s actual4
+'
+
+test_expect_success 'git diff HEAD with dirty submodule (work tree, refs match) [.git/config]' '
+	git config diff.ignoreSubmodules all &&
+	git diff HEAD >actual &&
+	! test -s actual &&
+	git config submodule.subname.ignore none &&
+	git config submodule.subname.path sub &&
+	git diff HEAD >actual &&
+	sed -e "1,/^@@/d" actual >actual.body &&
+	expect_from_to >expect.body $subprev $subprev-dirty &&
+	test_cmp expect.body actual.body &&
+	git config submodule.subname.ignore all &&
+	git diff HEAD >actual2 &&
+	! test -s actual2 &&
+	git config submodule.subname.ignore untracked &&
+	git diff HEAD >actual3 &&
+	sed -e "1,/^@@/d" actual3 >actual3.body &&
+	expect_from_to >expect.body $subprev $subprev-dirty &&
+	test_cmp expect.body actual3.body &&
+	git config submodule.subname.ignore dirty &&
+	git diff HEAD >actual4 &&
+	! test -s actual4 &&
+	git diff HEAD --ignore-submodules=none >actual &&
+	sed -e "1,/^@@/d" actual >actual.body &&
+	expect_from_to >expect.body $subprev $subprev-dirty &&
+	test_cmp expect.body actual.body &&
+	git config --remove-section submodule.subname &&
+	git config --unset diff.ignoreSubmodules
+'
+
+test_expect_success 'git diff HEAD with dirty submodule (work tree, refs match) [.gitmodules]' '
+	git config diff.ignoreSubmodules dirty &&
+	git diff HEAD >actual &&
+	! test -s actual &&
+	git config --add -f .gitmodules submodule.subname.ignore none &&
+	git config --add -f .gitmodules submodule.subname.path sub &&
+	git diff HEAD >actual &&
+	sed -e "1,/^@@/d" actual >actual.body &&
+	expect_from_to >expect.body $subprev $subprev-dirty &&
+	test_cmp expect.body actual.body &&
+	git config -f .gitmodules submodule.subname.ignore all &&
+	git config -f .gitmodules submodule.subname.path sub &&
+	git diff HEAD >actual2 &&
+	! test -s actual2 &&
+	git config -f .gitmodules submodule.subname.ignore untracked &&
+	git diff HEAD >actual3 &&
+	sed -e "1,/^@@/d" actual3 >actual3.body &&
+	expect_from_to >expect.body $subprev $subprev-dirty &&
+	test_cmp expect.body actual3.body &&
+	git config -f .gitmodules submodule.subname.ignore dirty &&
+	git diff HEAD >actual4 &&
+	! test -s actual4 &&
+	git config submodule.subname.ignore none &&
+	git config submodule.subname.path sub &&
+	git diff HEAD >actual &&
+	sed -e "1,/^@@/d" actual >actual.body &&
+	expect_from_to >expect.body $subprev $subprev-dirty &&
+	test_cmp expect.body actual.body &&
+	git config --remove-section submodule.subname &&
+	git config --remove-section -f .gitmodules submodule.subname &&
+	git config --unset diff.ignoreSubmodules &&
+	rm .gitmodules
+'
+
+test_expect_success 'git diff HEAD with dirty submodule (index, refs match)' '
+	(
+		cd sub &&
+		git reset --hard &&
+		echo >>world &&
+		git add world
+	) &&
+	git diff HEAD >actual &&
+	sed -e "1,/^@@/d" actual >actual.body &&
+	expect_from_to >expect.body $subprev $subprev-dirty &&
+	test_cmp expect.body actual.body
+'
+
+test_expect_success 'git diff HEAD with dirty submodule (untracked, refs match)' '
+	(
+		cd sub &&
+		git reset --hard &&
+		git clean -qfdx &&
+		>cruft
+	) &&
+	git diff HEAD >actual &&
+	sed -e "1,/^@@/d" actual >actual.body &&
+	expect_from_to >expect.body $subprev $subprev-dirty &&
+	test_cmp expect.body actual.body &&
+	git diff --ignore-submodules=all HEAD >actual2 &&
+	! test -s actual2 &&
+	git diff --ignore-submodules=untracked HEAD >actual3 &&
+	! test -s actual3 &&
+	git diff --ignore-submodules=dirty HEAD >actual4 &&
+	! test -s actual4
+'
+
+test_expect_success 'git diff HEAD with dirty submodule (untracked, refs match) [.git/config]' '
+	git config submodule.subname.ignore all &&
+	git config submodule.subname.path sub &&
+	git diff HEAD >actual2 &&
+	! test -s actual2 &&
+	git config submodule.subname.ignore untracked &&
+	git diff HEAD >actual3 &&
+	! test -s actual3 &&
+	git config submodule.subname.ignore dirty &&
+	git diff HEAD >actual4 &&
+	! test -s actual4 &&
+	git diff --ignore-submodules=none HEAD >actual &&
+	sed -e "1,/^@@/d" actual >actual.body &&
+	expect_from_to >expect.body $subprev $subprev-dirty &&
+	test_cmp expect.body actual.body &&
+	git config --remove-section submodule.subname
+'
+
+test_expect_success 'git diff HEAD with dirty submodule (untracked, refs match) [.gitmodules]' '
+	git config --add -f .gitmodules submodule.subname.ignore all &&
+	git config --add -f .gitmodules submodule.subname.path sub &&
+	git diff HEAD >actual2 &&
+	! test -s actual2 &&
+	git config -f .gitmodules submodule.subname.ignore untracked &&
+	git diff HEAD >actual3 &&
+	! test -s actual3 &&
+	git config -f .gitmodules submodule.subname.ignore dirty &&
+	git diff HEAD >actual4 &&
+	! test -s actual4 &&
+	git config submodule.subname.ignore none &&
+	git config submodule.subname.path sub &&
+	git diff HEAD >actual &&
+	sed -e "1,/^@@/d" actual >actual.body &&
+	expect_from_to >expect.body $subprev $subprev-dirty &&
+	test_cmp expect.body actual.body &&
+	git config --remove-section submodule.subname &&
+	git config --remove-section -f .gitmodules submodule.subname &&
+	rm .gitmodules
+'
+
+test_expect_success 'git diff between submodule commits' '
+	git diff HEAD^..HEAD >actual &&
+	sed -e "1,/^@@/d" actual >actual.body &&
+	expect_from_to >expect.body $subtip $subprev &&
+	test_cmp expect.body actual.body &&
+	git diff --ignore-submodules=dirty HEAD^..HEAD >actual &&
+	sed -e "1,/^@@/d" actual >actual.body &&
+	expect_from_to >expect.body $subtip $subprev &&
+	test_cmp expect.body actual.body &&
+	git diff --ignore-submodules HEAD^..HEAD >actual &&
+	! test -s actual
+'
+
+test_expect_success 'git diff between submodule commits [.git/config]' '
+	git diff HEAD^..HEAD >actual &&
+	sed -e "1,/^@@/d" actual >actual.body &&
+	expect_from_to >expect.body $subtip $subprev &&
+	test_cmp expect.body actual.body &&
+	git config submodule.subname.ignore dirty &&
+	git config submodule.subname.path sub &&
+	git diff HEAD^..HEAD >actual &&
+	sed -e "1,/^@@/d" actual >actual.body &&
+	expect_from_to >expect.body $subtip $subprev &&
+	test_cmp expect.body actual.body &&
+	git config submodule.subname.ignore all &&
+	git diff HEAD^..HEAD >actual &&
+	! test -s actual &&
+	git diff --ignore-submodules=dirty HEAD^..HEAD >actual &&
+	sed -e "1,/^@@/d" actual >actual.body &&
+	expect_from_to >expect.body $subtip $subprev &&
+	git config --remove-section submodule.subname
+'
+
+test_expect_success 'git diff between submodule commits [.gitmodules]' '
+	git diff HEAD^..HEAD >actual &&
+	sed -e "1,/^@@/d" actual >actual.body &&
+	expect_from_to >expect.body $subtip $subprev &&
+	test_cmp expect.body actual.body &&
+	git config --add -f .gitmodules submodule.subname.ignore dirty &&
+	git config --add -f .gitmodules submodule.subname.path sub &&
+	git diff HEAD^..HEAD >actual &&
+	sed -e "1,/^@@/d" actual >actual.body &&
+	expect_from_to >expect.body $subtip $subprev &&
+	test_cmp expect.body actual.body &&
+	git config -f .gitmodules submodule.subname.ignore all &&
+	git diff HEAD^..HEAD >actual &&
+	! test -s actual &&
+	git config submodule.subname.ignore dirty &&
+	git config submodule.subname.path sub &&
+	git diff  HEAD^..HEAD >actual &&
+	sed -e "1,/^@@/d" actual >actual.body &&
+	expect_from_to >expect.body $subtip $subprev &&
+	git config --remove-section submodule.subname &&
+	git config --remove-section -f .gitmodules submodule.subname &&
+	rm .gitmodules
+'
+
 test_expect_success 'git diff (empty submodule dir)' '
 	: >empty &&
 	rm -rf sub/* sub/.git &&
@@ -60,11 +315,11 @@
 test_expect_success 'conflicted submodule setup' '
 
 	# 39 efs
-	c=fffffffffffffffffffffffffffffffffffffff
+	c=fffffffffffffffffffffffffffffffffffffff &&
 	(
-		echo "000000 $_z40 0	sub"
-		echo "160000 1$c 1	sub"
-		echo "160000 2$c 2	sub"
+		echo "000000 $_z40 0	sub" &&
+		echo "160000 1$c 1	sub" &&
+		echo "160000 2$c 2	sub" &&
 		echo "160000 3$c 3	sub"
 	) | git update-index --index-info &&
 	echo >expect.nosub '\''diff --cc sub
diff --git a/t/t4029-diff-trailing-space.sh b/t/t4029-diff-trailing-space.sh
index 3ccc237..36e2f07 100755
--- a/t/t4029-diff-trailing-space.sh
+++ b/t/t4029-diff-trailing-space.sh
@@ -27,7 +27,7 @@
      git config --bool diff.suppressBlankEmpty true &&
      git diff f > actual &&
      test_cmp exp actual &&
-     perl -i.bak -p -e "s/^\$/ /" exp &&
+     "$PERL_PATH" -i.bak -p -e "s/^\$/ /" exp &&
      git config --bool diff.suppressBlankEmpty false &&
      git diff f > actual &&
      test_cmp exp actual &&
diff --git a/t/t4030-diff-textconv.sh b/t/t4030-diff-textconv.sh
index a3f0897..eebb1ee 100755
--- a/t/t4030-diff-textconv.sh
+++ b/t/t4030-diff-textconv.sh
@@ -21,7 +21,7 @@
 
 cat >hexdump <<'EOF'
 #!/bin/sh
-perl -e '$/ = undef; $_ = <>; s/./ord($&)/ge; print $_' < "$1"
+"$PERL_PATH" -e '$/ = undef; $_ = <>; s/./ord($&)/ge; print $_' < "$1"
 EOF
 chmod +x hexdump
 
@@ -48,7 +48,7 @@
 
 test_expect_success 'setup textconv filters' '
 	echo file diff=foo >.gitattributes &&
-	git config diff.foo.textconv "$PWD"/hexdump &&
+	git config diff.foo.textconv "\"$(pwd)\""/hexdump &&
 	git config diff.fail.textconv false
 '
 
@@ -85,13 +85,17 @@
 '
 
 cat >expect.stat <<'EOF'
- file |  Bin 2 -> 4 bytes
- 1 files changed, 0 insertions(+), 0 deletions(-)
+ file | Bin 2 -> 4 bytes
+ 1 file changed, 0 insertions(+), 0 deletions(-)
 EOF
 test_expect_success 'diffstat does not run textconv' '
 	echo file diff=fail >.gitattributes &&
 	git diff --stat HEAD^ HEAD >actual &&
-	test_cmp expect.stat actual
+	test_i18ncmp expect.stat actual &&
+
+	head -n1 <expect.stat >expect.line1 &&
+	head -n1 <actual >actual.line1 &&
+	test_cmp expect.line1 actual.line1
 '
 # restore working setup
 echo file diff=foo >.gitattributes
diff --git a/t/t4031-diff-rewrite-binary.sh b/t/t4031-diff-rewrite-binary.sh
index a894c60..eacc669 100755
--- a/t/t4031-diff-rewrite-binary.sh
+++ b/t/t4031-diff-rewrite-binary.sh
@@ -44,17 +44,30 @@
 	grep "GIT binary patch" diff
 '
 
+test_expect_success 'rewrite diff --numstat shows binary changes' '
+	git diff -B --numstat --summary >diff &&
+	grep -e "-	-	" diff &&
+	grep " rewrite file" diff
+'
+
+test_expect_success 'diff --stat counts binary rewrite as 0 lines' '
+	git diff -B --stat --summary >diff &&
+	grep "Bin" diff &&
+	test_i18ngrep "0 insertions.*0 deletions" diff &&
+	grep " rewrite file" diff
+'
+
 {
 	echo "#!$SHELL_PATH"
 	cat <<'EOF'
-perl -e '$/ = undef; $_ = <>; s/./ord($&)/ge; print $_' < "$1"
+"$PERL_PATH" -e '$/ = undef; $_ = <>; s/./ord($&)/ge; print $_' < "$1"
 EOF
 } >dump
 chmod +x dump
 
 test_expect_success 'setup textconv' '
 	echo file diff=foo >.gitattributes &&
-	git config diff.foo.textconv "$PWD"/dump
+	git config diff.foo.textconv "\"$(pwd)\""/dump
 '
 
 test_expect_success 'rewrite diff respects textconv' '
diff --git a/t/t4033-diff-patience.sh b/t/t4033-diff-patience.sh
index 1eb1498..3c9932e 100755
--- a/t/t4033-diff-patience.sh
+++ b/t/t4033-diff-patience.sh
@@ -3,166 +3,10 @@
 test_description='patience diff algorithm'
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-diff-alternative.sh
 
-cat >file1 <<\EOF
-#include <stdio.h>
+test_diff_frobnitz "patience"
 
-// Frobs foo heartily
-int frobnitz(int foo)
-{
-    int i;
-    for(i = 0; i < 10; i++)
-    {
-        printf("Your answer is: ");
-        printf("%d\n", foo);
-    }
-}
-
-int fact(int n)
-{
-    if(n > 1)
-    {
-        return fact(n-1) * n;
-    }
-    return 1;
-}
-
-int main(int argc, char **argv)
-{
-    frobnitz(fact(10));
-}
-EOF
-
-cat >file2 <<\EOF
-#include <stdio.h>
-
-int fib(int n)
-{
-    if(n > 2)
-    {
-        return fib(n-1) + fib(n-2);
-    }
-    return 1;
-}
-
-// Frobs foo heartily
-int frobnitz(int foo)
-{
-    int i;
-    for(i = 0; i < 10; i++)
-    {
-        printf("%d\n", foo);
-    }
-}
-
-int main(int argc, char **argv)
-{
-    frobnitz(fib(10));
-}
-EOF
-
-cat >expect <<\EOF
-diff --git a/file1 b/file2
-index 6faa5a3..e3af329 100644
---- a/file1
-+++ b/file2
-@@ -1,26 +1,25 @@
- #include <stdio.h>
- 
-+int fib(int n)
-+{
-+    if(n > 2)
-+    {
-+        return fib(n-1) + fib(n-2);
-+    }
-+    return 1;
-+}
-+
- // Frobs foo heartily
- int frobnitz(int foo)
- {
-     int i;
-     for(i = 0; i < 10; i++)
-     {
--        printf("Your answer is: ");
-         printf("%d\n", foo);
-     }
- }
- 
--int fact(int n)
--{
--    if(n > 1)
--    {
--        return fact(n-1) * n;
--    }
--    return 1;
--}
--
- int main(int argc, char **argv)
- {
--    frobnitz(fact(10));
-+    frobnitz(fib(10));
- }
-EOF
-
-test_expect_success 'patience diff' '
-
-	test_must_fail git diff --no-index --patience file1 file2 > output &&
-	test_cmp expect output
-
-'
-
-test_expect_success 'patience diff output is valid' '
-
-	mv file2 expect &&
-	git apply < output &&
-	test_cmp expect file2
-
-'
-
-cat >uniq1 <<\EOF
-1
-2
-3
-4
-5
-6
-EOF
-
-cat >uniq2 <<\EOF
-a
-b
-c
-d
-e
-f
-EOF
-
-cat >expect <<\EOF
-diff --git a/uniq1 b/uniq2
-index b414108..0fdf397 100644
---- a/uniq1
-+++ b/uniq2
-@@ -1,6 +1,6 @@
--1
--2
--3
--4
--5
--6
-+a
-+b
-+c
-+d
-+e
-+f
-EOF
-
-test_expect_success 'completely different files' '
-
-	test_must_fail git diff --no-index --patience uniq1 uniq2 > output &&
-	test_cmp expect output
-
-'
+test_diff_unique "patience"
 
 test_done
diff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh
index 4508eff..30d42cb 100755
--- a/t/t4034-diff-words.sh
+++ b/t/t4034-diff-words.sh
@@ -3,198 +3,385 @@
 test_description='word diff colors'
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh
 
-test_expect_success setup '
+cat >pre.simple <<-\EOF
+	h(4)
 
-	git config diff.color.old red
-	git config diff.color.new green
+	a = b + c
+EOF
+cat >post.simple <<-\EOF
+	h(4),hh[44]
 
-'
+	a = b + c
 
-decrypt_color () {
-	sed \
-		-e 's/.\[1m/<WHITE>/g' \
-		-e 's/.\[31m/<RED>/g' \
-		-e 's/.\[32m/<GREEN>/g' \
-		-e 's/.\[36m/<BROWN>/g' \
-		-e 's/.\[m/<RESET>/g'
-}
+	aa = a
+
+	aeff = aeff * ( aaa )
+EOF
+cat >expect.letter-runs-are-words <<-\EOF
+	<BOLD>diff --git a/pre b/post<RESET>
+	<BOLD>index 330b04f..5ed8eff 100644<RESET>
+	<BOLD>--- a/pre<RESET>
+	<BOLD>+++ b/post<RESET>
+	<CYAN>@@ -1,3 +1,7 @@<RESET>
+	h(4),<GREEN>hh<RESET>[44]
+
+	a = b + c<RESET>
+
+	<GREEN>aa = a<RESET>
+
+	<GREEN>aeff = aeff * ( aaa<RESET> )
+EOF
+cat >expect.non-whitespace-is-word <<-\EOF
+	<BOLD>diff --git a/pre b/post<RESET>
+	<BOLD>index 330b04f..5ed8eff 100644<RESET>
+	<BOLD>--- a/pre<RESET>
+	<BOLD>+++ b/post<RESET>
+	<CYAN>@@ -1,3 +1,7 @@<RESET>
+	h(4)<GREEN>,hh[44]<RESET>
+
+	a = b + c<RESET>
+
+	<GREEN>aa = a<RESET>
+
+	<GREEN>aeff = aeff * ( aaa )<RESET>
+EOF
 
 word_diff () {
-	test_must_fail git diff --no-index "$@" pre post > output &&
-	decrypt_color < output > output.decrypted &&
+	test_must_fail git diff --no-index "$@" pre post >output &&
+	test_decode_color <output >output.decrypted &&
 	test_cmp expect output.decrypted
 }
 
-cat > pre <<\EOF
-h(4)
+test_language_driver () {
+	lang=$1
+	test_expect_success "diff driver '$lang'" '
+		cp "$TEST_DIRECTORY/t4034/'"$lang"'/pre" \
+			"$TEST_DIRECTORY/t4034/'"$lang"'/post" \
+			"$TEST_DIRECTORY/t4034/'"$lang"'/expect" . &&
+		echo "* diff='"$lang"'" >.gitattributes &&
+		word_diff --color-words
+	'
+}
 
-a = b + c
-EOF
+test_expect_success setup '
+	git config diff.color.old red &&
+	git config diff.color.new green &&
+	git config diff.color.func magenta
+'
 
-cat > post <<\EOF
-h(4),hh[44]
-
-a = b + c
-
-aa = a
-
-aeff = aeff * ( aaa )
-EOF
-
-cat > expect <<\EOF
-<WHITE>diff --git a/pre b/post<RESET>
-<WHITE>index 330b04f..5ed8eff 100644<RESET>
-<WHITE>--- a/pre<RESET>
-<WHITE>+++ b/post<RESET>
-<BROWN>@@ -1,3 +1,7 @@<RESET>
-<RED>h(4)<RESET><GREEN>h(4),hh[44]<RESET>
-<RESET>
-a = b + c<RESET>
-
-<GREEN>aa = a<RESET>
-
-<GREEN>aeff = aeff * ( aaa )<RESET>
-EOF
+test_expect_success 'set up pre and post with runs of whitespace' '
+	cp pre.simple pre &&
+	cp post.simple post
+'
 
 test_expect_success 'word diff with runs of whitespace' '
+	cat >expect <<-\EOF &&
+		<BOLD>diff --git a/pre b/post<RESET>
+		<BOLD>index 330b04f..5ed8eff 100644<RESET>
+		<BOLD>--- a/pre<RESET>
+		<BOLD>+++ b/post<RESET>
+		<CYAN>@@ -1,3 +1,7 @@<RESET>
+		<RED>h(4)<RESET><GREEN>h(4),hh[44]<RESET>
 
-	word_diff --color-words
+		a = b + c<RESET>
 
+		<GREEN>aa = a<RESET>
+
+		<GREEN>aeff = aeff * ( aaa )<RESET>
+	EOF
+	word_diff --color-words &&
+	word_diff --word-diff=color &&
+	word_diff --color --word-diff=color
 '
 
-cat > expect <<\EOF
-<WHITE>diff --git a/pre b/post<RESET>
-<WHITE>index 330b04f..5ed8eff 100644<RESET>
-<WHITE>--- a/pre<RESET>
-<WHITE>+++ b/post<RESET>
-<BROWN>@@ -1,3 +1,7 @@<RESET>
-h(4),<GREEN>hh<RESET>[44]
-<RESET>
-a = b + c<RESET>
+test_expect_success '--word-diff=porcelain' '
+	sed 's/#.*$//' >expect <<-\EOF &&
+		diff --git a/pre b/post
+		index 330b04f..5ed8eff 100644
+		--- a/pre
+		+++ b/post
+		@@ -1,3 +1,7 @@
+		-h(4)
+		+h(4),hh[44]
+		~
+		 # significant space
+		~
+		 a = b + c
+		~
+		~
+		+aa = a
+		~
+		~
+		+aeff = aeff * ( aaa )
+		~
+	EOF
+	word_diff --word-diff=porcelain
+'
 
-<GREEN>aa = a<RESET>
+test_expect_success '--word-diff=plain' '
+	cat >expect <<-\EOF &&
+		diff --git a/pre b/post
+		index 330b04f..5ed8eff 100644
+		--- a/pre
+		+++ b/post
+		@@ -1,3 +1,7 @@
+		[-h(4)-]{+h(4),hh[44]+}
 
-<GREEN>aeff = aeff * ( aaa<RESET> )
-EOF
-cp expect expect.letter-runs-are-words
+		a = b + c
+
+		{+aa = a+}
+
+		{+aeff = aeff * ( aaa )+}
+	EOF
+	word_diff --word-diff=plain &&
+	word_diff --word-diff=plain --no-color
+'
+
+test_expect_success '--word-diff=plain --color' '
+	cat >expect <<-\EOF &&
+		<BOLD>diff --git a/pre b/post<RESET>
+		<BOLD>index 330b04f..5ed8eff 100644<RESET>
+		<BOLD>--- a/pre<RESET>
+		<BOLD>+++ b/post<RESET>
+		<CYAN>@@ -1,3 +1,7 @@<RESET>
+		<RED>[-h(4)-]<RESET><GREEN>{+h(4),hh[44]+}<RESET>
+
+		a = b + c<RESET>
+
+		<GREEN>{+aa = a+}<RESET>
+
+		<GREEN>{+aeff = aeff * ( aaa )+}<RESET>
+	EOF
+	word_diff --word-diff=plain --color
+'
+
+test_expect_success 'word diff without context' '
+	cat >expect <<-\EOF &&
+		<BOLD>diff --git a/pre b/post<RESET>
+		<BOLD>index 330b04f..5ed8eff 100644<RESET>
+		<BOLD>--- a/pre<RESET>
+		<BOLD>+++ b/post<RESET>
+		<CYAN>@@ -1 +1 @@<RESET>
+		<RED>h(4)<RESET><GREEN>h(4),hh[44]<RESET>
+		<CYAN>@@ -3,0 +4,4 @@<RESET> <RESET><MAGENTA>a = b + c<RESET>
+
+		<GREEN>aa = a<RESET>
+
+		<GREEN>aeff = aeff * ( aaa )<RESET>
+	EOF
+	word_diff --color-words --unified=0
+'
 
 test_expect_success 'word diff with a regular expression' '
-
+	cp expect.letter-runs-are-words expect &&
 	word_diff --color-words="[a-z]+"
-
 '
 
-test_expect_success 'set a diff driver' '
+test_expect_success 'set up a diff driver' '
 	git config diff.testdriver.wordRegex "[^[:space:]]" &&
-	cat <<EOF > .gitattributes
-pre diff=testdriver
-post diff=testdriver
-EOF
+	cat <<-\EOF >.gitattributes
+		pre diff=testdriver
+		post diff=testdriver
+	EOF
 '
 
 test_expect_success 'option overrides .gitattributes' '
-
+	cp expect.letter-runs-are-words expect &&
 	word_diff --color-words="[a-z]+"
-
 '
 
-cat > expect <<\EOF
-<WHITE>diff --git a/pre b/post<RESET>
-<WHITE>index 330b04f..5ed8eff 100644<RESET>
-<WHITE>--- a/pre<RESET>
-<WHITE>+++ b/post<RESET>
-<BROWN>@@ -1,3 +1,7 @@<RESET>
-h(4)<GREEN>,hh[44]<RESET>
-<RESET>
-a = b + c<RESET>
-
-<GREEN>aa = a<RESET>
-
-<GREEN>aeff = aeff * ( aaa )<RESET>
-EOF
-cp expect expect.non-whitespace-is-word
-
 test_expect_success 'use regex supplied by driver' '
-
+	cp expect.non-whitespace-is-word expect &&
 	word_diff --color-words
-
 '
 
-test_expect_success 'set diff.wordRegex option' '
+test_expect_success 'set up diff.wordRegex option' '
 	git config diff.wordRegex "[[:alnum:]]+"
 '
 
-cp expect.letter-runs-are-words expect
-
 test_expect_success 'command-line overrides config' '
+	cp expect.letter-runs-are-words expect &&
 	word_diff --color-words="[a-z]+"
 '
 
-cp expect.non-whitespace-is-word expect
+test_expect_success 'command-line overrides config: --word-diff-regex' '
+	cat >expect <<-\EOF &&
+		<BOLD>diff --git a/pre b/post<RESET>
+		<BOLD>index 330b04f..5ed8eff 100644<RESET>
+		<BOLD>--- a/pre<RESET>
+		<BOLD>+++ b/post<RESET>
+		<CYAN>@@ -1,3 +1,7 @@<RESET>
+		h(4),<GREEN>{+hh+}<RESET>[44]
+
+		a = b + c<RESET>
+
+		<GREEN>{+aa = a+}<RESET>
+
+		<GREEN>{+aeff = aeff * ( aaa+}<RESET> )
+	EOF
+	word_diff --color --word-diff-regex="[a-z]+"
+'
 
 test_expect_success '.gitattributes override config' '
+	cp expect.non-whitespace-is-word expect &&
 	word_diff --color-words
 '
 
-test_expect_success 'remove diff driver regex' '
-	git config --unset diff.testdriver.wordRegex
+test_expect_success 'setup: remove diff driver regex' '
+	test_might_fail git config --unset diff.testdriver.wordRegex
 '
 
-cat > expect <<\EOF
-<WHITE>diff --git a/pre b/post<RESET>
-<WHITE>index 330b04f..5ed8eff 100644<RESET>
-<WHITE>--- a/pre<RESET>
-<WHITE>+++ b/post<RESET>
-<BROWN>@@ -1,3 +1,7 @@<RESET>
-h(4),<GREEN>hh[44<RESET>]
-<RESET>
-a = b + c<RESET>
-
-<GREEN>aa = a<RESET>
-
-<GREEN>aeff = aeff * ( aaa<RESET> )
-EOF
-
 test_expect_success 'use configured regex' '
+	cat >expect <<-\EOF &&
+		<BOLD>diff --git a/pre b/post<RESET>
+		<BOLD>index 330b04f..5ed8eff 100644<RESET>
+		<BOLD>--- a/pre<RESET>
+		<BOLD>+++ b/post<RESET>
+		<CYAN>@@ -1,3 +1,7 @@<RESET>
+		h(4),<GREEN>hh[44<RESET>]
+
+		a = b + c<RESET>
+
+		<GREEN>aa = a<RESET>
+
+		<GREEN>aeff = aeff * ( aaa<RESET> )
+	EOF
 	word_diff --color-words
 '
 
-echo 'aaa (aaa)' > pre
-echo 'aaa (aaa) aaa' > post
-
-cat > expect <<\EOF
-<WHITE>diff --git a/pre b/post<RESET>
-<WHITE>index c29453b..be22f37 100644<RESET>
-<WHITE>--- a/pre<RESET>
-<WHITE>+++ b/post<RESET>
-<BROWN>@@ -1 +1 @@<RESET>
-aaa (aaa) <GREEN>aaa<RESET>
-EOF
-
 test_expect_success 'test parsing words for newline' '
-
+	echo "aaa (aaa)" >pre &&
+	echo "aaa (aaa) aaa" >post &&
+	cat >expect <<-\EOF &&
+		<BOLD>diff --git a/pre b/post<RESET>
+		<BOLD>index c29453b..be22f37 100644<RESET>
+		<BOLD>--- a/pre<RESET>
+		<BOLD>+++ b/post<RESET>
+		<CYAN>@@ -1 +1 @@<RESET>
+		aaa (aaa) <GREEN>aaa<RESET>
+	EOF
 	word_diff --color-words="a+"
-
-
 '
 
-echo '(:' > pre
-echo '(' > post
-
-cat > expect <<\EOF
-<WHITE>diff --git a/pre b/post<RESET>
-<WHITE>index 289cb9d..2d06f37 100644<RESET>
-<WHITE>--- a/pre<RESET>
-<WHITE>+++ b/post<RESET>
-<BROWN>@@ -1 +1 @@<RESET>
-(<RED>:<RESET>
-EOF
-
 test_expect_success 'test when words are only removed at the end' '
-
+	echo "(:" >pre &&
+	echo "(" >post &&
+	cat >expect <<-\EOF &&
+		<BOLD>diff --git a/pre b/post<RESET>
+		<BOLD>index 289cb9d..2d06f37 100644<RESET>
+		<BOLD>--- a/pre<RESET>
+		<BOLD>+++ b/post<RESET>
+		<CYAN>@@ -1 +1 @@<RESET>
+		(<RED>:<RESET>
+	EOF
 	word_diff --color-words=.
+'
 
+test_expect_success '--word-diff=none' '
+	echo "(:" >pre &&
+	echo "(" >post &&
+	cat >expect <<-\EOF &&
+		diff --git a/pre b/post
+		index 289cb9d..2d06f37 100644
+		--- a/pre
+		+++ b/post
+		@@ -1 +1 @@
+		-(:
+		+(
+	EOF
+	word_diff --word-diff=plain --word-diff=none
+'
+
+test_expect_success 'unset default driver' '
+	test_unconfig diff.wordregex
+'
+
+test_language_driver bibtex
+test_language_driver cpp
+test_language_driver csharp
+test_language_driver fortran
+test_language_driver html
+test_language_driver java
+test_language_driver matlab
+test_language_driver objc
+test_language_driver pascal
+test_language_driver perl
+test_language_driver php
+test_language_driver python
+test_language_driver ruby
+test_language_driver tex
+
+test_expect_success 'word-diff with diff.sbe' '
+	cat >expect <<-\EOF &&
+	diff --git a/pre b/post
+	index a1a53b5..bc8fe6d 100644
+	--- a/pre
+	+++ b/post
+	@@ -1,3 +1,3 @@
+	a
+
+	[-b-]{+c+}
+	EOF
+	cat >pre <<-\EOF &&
+	a
+
+	b
+	EOF
+	cat >post <<-\EOF &&
+	a
+
+	c
+	EOF
+	test_when_finished "git config --unset diff.suppress-blank-empty" &&
+	git config diff.suppress-blank-empty true &&
+	word_diff --word-diff=plain
+'
+
+test_expect_success 'word-diff with no newline at EOF' '
+	cat >expect <<-\EOF &&
+	diff --git a/pre b/post
+	index 7bf316e..3dd0303 100644
+	--- a/pre
+	+++ b/post
+	@@ -1 +1 @@
+	a a [-a-]{+ab+} a a
+	EOF
+	printf "%s" "a a a a a" >pre &&
+	printf "%s" "a a ab a a" >post &&
+	word_diff --word-diff=plain
+'
+
+test_expect_success 'setup history with two files' '
+	echo "a b; c" >a.tex &&
+	echo "a b; c" >z.txt &&
+	git add a.tex z.txt &&
+	git commit -minitial &&
+
+	# modify both
+	echo "a bx; c" >a.tex &&
+	echo "a bx; c" >z.txt &&
+	git commit -mmodified -a
+'
+
+test_expect_success 'wordRegex for the first file does not apply to the second' '
+	echo "*.tex diff=tex" >.gitattributes &&
+	git config diff.tex.wordRegex "[a-z]+|." &&
+	cat >expect <<-\EOF &&
+		diff --git a/a.tex b/a.tex
+		--- a/a.tex
+		+++ b/a.tex
+		@@ -1 +1 @@
+		a [-b-]{+bx+}; c
+		diff --git a/z.txt b/z.txt
+		--- a/z.txt
+		+++ b/z.txt
+		@@ -1 +1 @@
+		a [-b;-]{+bx;+} c
+	EOF
+	git diff --word-diff HEAD~ >actual &&
+	compare_diff_patch expect actual
 '
 
 test_done
diff --git a/t/t4034/bibtex/expect b/t/t4034/bibtex/expect
new file mode 100644
index 0000000..a157774
--- /dev/null
+++ b/t/t4034/bibtex/expect
@@ -0,0 +1,15 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 95cd55b..ddcba9b 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,9 +1,10 @@<RESET>
+@article{aldous1987uie,<RESET>
+  title={{Ultimate instability of exponential back-off protocol for acknowledgment-based transmission control of random access communication channels}},<RESET>
+  author={Aldous, <RED>D.<RESET><GREEN>David<RESET>},
+  journal={Information Theory, IEEE Transactions on},<RESET>
+  volume={<RED>33<RESET><GREEN>Bogus.<RESET>},
+  number={<RED>2<RESET><GREEN>4<RESET>},
+  pages={219--223},<RESET>
+  year=<GREEN>1987,<RESET>
+<GREEN>  note={This is in fact a rather funny read since ethernet works well in practice. The<RESET> {<RED>1987<RESET><GREEN>\em pre} reference is the right one, however.<RESET>}<RED>,<RESET>
+}<RESET>
diff --git a/t/t4034/bibtex/post b/t/t4034/bibtex/post
new file mode 100644
index 0000000..ddcba9b
--- /dev/null
+++ b/t/t4034/bibtex/post
@@ -0,0 +1,10 @@
+@article{aldous1987uie,
+  title={{Ultimate instability of exponential back-off protocol for acknowledgment-based transmission control of random access communication channels}},
+  author={Aldous, David},
+  journal={Information Theory, IEEE Transactions on},
+  volume={Bogus.},
+  number={4},
+  pages={219--223},
+  year=1987,
+  note={This is in fact a rather funny read since ethernet works well in practice. The {\em pre} reference is the right one, however.}
+}
diff --git a/t/t4034/bibtex/pre b/t/t4034/bibtex/pre
new file mode 100644
index 0000000..95cd55b
--- /dev/null
+++ b/t/t4034/bibtex/pre
@@ -0,0 +1,9 @@
+@article{aldous1987uie,
+  title={{Ultimate instability of exponential back-off protocol for acknowledgment-based transmission control of random access communication channels}},
+  author={Aldous, D.},
+  journal={Information Theory, IEEE Transactions on},
+  volume={33},
+  number={2},
+  pages={219--223},
+  year={1987},
+}
diff --git a/t/t4034/cpp/expect b/t/t4034/cpp/expect
new file mode 100644
index 0000000..37d1ea2
--- /dev/null
+++ b/t/t4034/cpp/expect
@@ -0,0 +1,36 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 23d5c8a..7e8c026 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,19 +1,19 @@<RESET>
+Foo() : x(0<RED>&&1<RESET><GREEN>&42<RESET>) { <GREEN>bar(x);<RESET> }
+cout<<"Hello World<RED>!<RESET><GREEN>?<RESET>\n"<<endl;
+<GREEN>(<RESET>1<GREEN>) (<RESET>-1e10<GREEN>) (<RESET>0xabcdef<GREEN>)<RESET> '<RED>x<RESET><GREEN>y<RESET>'
+[<RED>a<RESET><GREEN>x<RESET>] <RED>a<RESET><GREEN>x<RESET>-><RED>b a<RESET><GREEN>y x<RESET>.<RED>b<RESET><GREEN>y<RESET>
+!<RED>a<RESET><GREEN>x<RESET> ~<RED>a a<RESET><GREEN>x x<RESET>++ <RED>a<RESET><GREEN>x<RESET>-- <RED>a<RESET><GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b a<RESET><GREEN>y x<RESET>%<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<<RED>b a<RESET><GREEN>y x<RESET>>><RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>==<RED>b a<RESET><GREEN>y x<RESET>!=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>^<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>|<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>||<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>?<RED>b<RESET><GREEN>y<RESET>:z
+<RED>a<RESET><GREEN>x<RESET>=<RED>b a<RESET><GREEN>y x<RESET>+=<RED>b a<RESET><GREEN>y x<RESET>-=<RED>b a<RESET><GREEN>y x<RESET>*=<RED>b a<RESET><GREEN>y x<RESET>/=<RED>b a<RESET><GREEN>y x<RESET>%=<RED>b a<RESET><GREEN>y x<RESET><<=<RED>b a<RESET><GREEN>y x<RESET>>>=<RED>b a<RESET><GREEN>y x<RESET>&=<RED>b a<RESET><GREEN>y x<RESET>^=<RED>b a<RESET><GREEN>y x<RESET>|=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>,y
+<RED>a<RESET><GREEN>x<RESET>::<RED>b<RESET><GREEN>y<RESET>
diff --git a/t/t4034/cpp/post b/t/t4034/cpp/post
new file mode 100644
index 0000000..7e8c026
--- /dev/null
+++ b/t/t4034/cpp/post
@@ -0,0 +1,19 @@
+Foo() : x(0&42) { bar(x); }
+cout<<"Hello World?\n"<<endl;
+(1) (-1e10) (0xabcdef) 'y'
+[x] x->y x.y
+!x ~x x++ x-- x*y x&y
+x*y x/y x%y
+x+y x-y
+x<<y x>>y
+x<y x<=y x>y x>=y
+x==y x!=y
+x&y
+x^y
+x|y
+x&&y
+x||y
+x?y:z
+x=y x+=y x-=y x*=y x/=y x%=y x<<=y x>>=y x&=y x^=y x|=y
+x,y
+x::y
diff --git a/t/t4034/cpp/pre b/t/t4034/cpp/pre
new file mode 100644
index 0000000..23d5c8a
--- /dev/null
+++ b/t/t4034/cpp/pre
@@ -0,0 +1,19 @@
+Foo():x(0&&1){}
+cout<<"Hello World!\n"<<endl;
+1 -1e10 0xabcdef 'x'
+[a] a->b a.b
+!a ~a a++ a-- a*b a&b
+a*b a/b a%b
+a+b a-b
+a<<b a>>b
+a<b a<=b a>b a>=b
+a==b a!=b
+a&b
+a^b
+a|b
+a&&b
+a||b
+a?b:z
+a=b a+=b a-=b a*=b a/=b a%=b a<<=b a>>=b a&=b a^=b a|=b
+a,y
+a::b
diff --git a/t/t4034/csharp/expect b/t/t4034/csharp/expect
new file mode 100644
index 0000000..e5d1dd2
--- /dev/null
+++ b/t/t4034/csharp/expect
@@ -0,0 +1,35 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 9106d63..dd5f421 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,18 +1,18 @@<RESET>
+Foo() : x(0<RED>&&1<RESET><GREEN>&42<RESET>) { <GREEN>bar(x);<RESET> }
+cout<<"Hello World<RED>!<RESET><GREEN>?<RESET>\n"<<endl;
+<GREEN>(<RESET>1<GREEN>) (<RESET>-1e10<GREEN>) (<RESET>0xabcdef<GREEN>)<RESET> '<RED>x<RESET><GREEN>y<RESET>'
+[<RED>a<RESET><GREEN>x<RESET>] <RED>a<RESET><GREEN>x<RESET>-><RED>b a<RESET><GREEN>y x<RESET>.<RED>b<RESET><GREEN>y<RESET>
+!<RED>a<RESET><GREEN>x<RESET> ~<RED>a a<RESET><GREEN>x x<RESET>++ <RED>a<RESET><GREEN>x<RESET>-- <RED>a<RESET><GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b a<RESET><GREEN>y x<RESET>%<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<<RED>b a<RESET><GREEN>y x<RESET>>><RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>==<RED>b a<RESET><GREEN>y x<RESET>!=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>^<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>|<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>||<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>?<RED>b<RESET><GREEN>y<RESET>:z
+<RED>a<RESET><GREEN>x<RESET>=<RED>b a<RESET><GREEN>y x<RESET>+=<RED>b a<RESET><GREEN>y x<RESET>-=<RED>b a<RESET><GREEN>y x<RESET>*=<RED>b a<RESET><GREEN>y x<RESET>/=<RED>b a<RESET><GREEN>y x<RESET>%=<RED>b a<RESET><GREEN>y x<RESET><<=<RED>b a<RESET><GREEN>y x<RESET>>>=<RED>b a<RESET><GREEN>y x<RESET>&=<RED>b a<RESET><GREEN>y x<RESET>^=<RED>b a<RESET><GREEN>y x<RESET>|=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>,y
diff --git a/t/t4034/csharp/post b/t/t4034/csharp/post
new file mode 100644
index 0000000..dd5f421
--- /dev/null
+++ b/t/t4034/csharp/post
@@ -0,0 +1,18 @@
+Foo() : x(0&42) { bar(x); }
+cout<<"Hello World?\n"<<endl;
+(1) (-1e10) (0xabcdef) 'y'
+[x] x->y x.y
+!x ~x x++ x-- x*y x&y
+x*y x/y x%y
+x+y x-y
+x<<y x>>y
+x<y x<=y x>y x>=y
+x==y x!=y
+x&y
+x^y
+x|y
+x&&y
+x||y
+x?y:z
+x=y x+=y x-=y x*=y x/=y x%=y x<<=y x>>=y x&=y x^=y x|=y
+x,y
diff --git a/t/t4034/csharp/pre b/t/t4034/csharp/pre
new file mode 100644
index 0000000..9106d63
--- /dev/null
+++ b/t/t4034/csharp/pre
@@ -0,0 +1,18 @@
+Foo():x(0&&1){}
+cout<<"Hello World!\n"<<endl;
+1 -1e10 0xabcdef 'x'
+[a] a->b a.b
+!a ~a a++ a-- a*b a&b
+a*b a/b a%b
+a+b a-b
+a<<b a>>b
+a<b a<=b a>b a>=b
+a==b a!=b
+a&b
+a^b
+a|b
+a&&b
+a||b
+a?b:z
+a=b a+=b a-=b a*=b a/=b a%=b a<<=b a>>=b a&=b a^=b a|=b
+a,y
diff --git a/t/t4034/fortran/expect b/t/t4034/fortran/expect
new file mode 100644
index 0000000..b233dbd
--- /dev/null
+++ b/t/t4034/fortran/expect
@@ -0,0 +1,10 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 87f0d0b..d308da2 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,5 +1,5 @@<RESET>
+print *, "Hello World<RED>!<RESET><GREEN>?<RESET>"
+
+DO10I = 1,10<RESET>
+<RED>DO10I<RESET><GREEN>DO 10 I<RESET> = 1,10
+<RED>DO10I<RESET><GREEN>DO 1 0 I<RESET> = 1,10
diff --git a/t/t4034/fortran/post b/t/t4034/fortran/post
new file mode 100644
index 0000000..d308da2
--- /dev/null
+++ b/t/t4034/fortran/post
@@ -0,0 +1,5 @@
+print *, "Hello World?"
+
+DO10I = 1,10
+DO 10 I = 1,10
+DO 1 0 I = 1,10
diff --git a/t/t4034/fortran/pre b/t/t4034/fortran/pre
new file mode 100644
index 0000000..87f0d0b
--- /dev/null
+++ b/t/t4034/fortran/pre
@@ -0,0 +1,5 @@
+print *, "Hello World!"
+
+DO10I = 1,10
+DO10I = 1,10
+DO10I = 1,10
diff --git a/t/t4034/html/expect b/t/t4034/html/expect
new file mode 100644
index 0000000..447b49a
--- /dev/null
+++ b/t/t4034/html/expect
@@ -0,0 +1,8 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 8ca4aea..46921e5 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,3 +1,3 @@<RESET>
+<tag <GREEN>newattr="newvalue"<RESET>><GREEN>added<RESET> content</tag>
+<tag attr=<RED>"value"<RESET><GREEN>"newvalue"<RESET>><RED>content<RESET><GREEN>changed<RESET></tag>
+<<RED>tag<RESET><GREEN>newtag<RESET>>content <RED>&entity;<RESET><GREEN>&newentity;<RESET><<RED>/tag<RESET><GREEN>/newtag<RESET>>
diff --git a/t/t4034/html/post b/t/t4034/html/post
new file mode 100644
index 0000000..46921e5
--- /dev/null
+++ b/t/t4034/html/post
@@ -0,0 +1,3 @@
+<tag newattr="newvalue">added content</tag>
+<tag attr="newvalue">changed</tag>
+<newtag>content &newentity;</newtag>
diff --git a/t/t4034/html/pre b/t/t4034/html/pre
new file mode 100644
index 0000000..8ca4aea
--- /dev/null
+++ b/t/t4034/html/pre
@@ -0,0 +1,3 @@
+<tag>content</tag>
+<tag attr="value">content</tag>
+<tag>content &entity;</tag>
diff --git a/t/t4034/java/expect b/t/t4034/java/expect
new file mode 100644
index 0000000..37d1ea2
--- /dev/null
+++ b/t/t4034/java/expect
@@ -0,0 +1,36 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 23d5c8a..7e8c026 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,19 +1,19 @@<RESET>
+Foo() : x(0<RED>&&1<RESET><GREEN>&42<RESET>) { <GREEN>bar(x);<RESET> }
+cout<<"Hello World<RED>!<RESET><GREEN>?<RESET>\n"<<endl;
+<GREEN>(<RESET>1<GREEN>) (<RESET>-1e10<GREEN>) (<RESET>0xabcdef<GREEN>)<RESET> '<RED>x<RESET><GREEN>y<RESET>'
+[<RED>a<RESET><GREEN>x<RESET>] <RED>a<RESET><GREEN>x<RESET>-><RED>b a<RESET><GREEN>y x<RESET>.<RED>b<RESET><GREEN>y<RESET>
+!<RED>a<RESET><GREEN>x<RESET> ~<RED>a a<RESET><GREEN>x x<RESET>++ <RED>a<RESET><GREEN>x<RESET>-- <RED>a<RESET><GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b a<RESET><GREEN>y x<RESET>%<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<<RED>b a<RESET><GREEN>y x<RESET>>><RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>==<RED>b a<RESET><GREEN>y x<RESET>!=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>^<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>|<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>||<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>?<RED>b<RESET><GREEN>y<RESET>:z
+<RED>a<RESET><GREEN>x<RESET>=<RED>b a<RESET><GREEN>y x<RESET>+=<RED>b a<RESET><GREEN>y x<RESET>-=<RED>b a<RESET><GREEN>y x<RESET>*=<RED>b a<RESET><GREEN>y x<RESET>/=<RED>b a<RESET><GREEN>y x<RESET>%=<RED>b a<RESET><GREEN>y x<RESET><<=<RED>b a<RESET><GREEN>y x<RESET>>>=<RED>b a<RESET><GREEN>y x<RESET>&=<RED>b a<RESET><GREEN>y x<RESET>^=<RED>b a<RESET><GREEN>y x<RESET>|=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>,y
+<RED>a<RESET><GREEN>x<RESET>::<RED>b<RESET><GREEN>y<RESET>
diff --git a/t/t4034/java/post b/t/t4034/java/post
new file mode 100644
index 0000000..7e8c026
--- /dev/null
+++ b/t/t4034/java/post
@@ -0,0 +1,19 @@
+Foo() : x(0&42) { bar(x); }
+cout<<"Hello World?\n"<<endl;
+(1) (-1e10) (0xabcdef) 'y'
+[x] x->y x.y
+!x ~x x++ x-- x*y x&y
+x*y x/y x%y
+x+y x-y
+x<<y x>>y
+x<y x<=y x>y x>=y
+x==y x!=y
+x&y
+x^y
+x|y
+x&&y
+x||y
+x?y:z
+x=y x+=y x-=y x*=y x/=y x%=y x<<=y x>>=y x&=y x^=y x|=y
+x,y
+x::y
diff --git a/t/t4034/java/pre b/t/t4034/java/pre
new file mode 100644
index 0000000..23d5c8a
--- /dev/null
+++ b/t/t4034/java/pre
@@ -0,0 +1,19 @@
+Foo():x(0&&1){}
+cout<<"Hello World!\n"<<endl;
+1 -1e10 0xabcdef 'x'
+[a] a->b a.b
+!a ~a a++ a-- a*b a&b
+a*b a/b a%b
+a+b a-b
+a<<b a>>b
+a<b a<=b a>b a>=b
+a==b a!=b
+a&b
+a^b
+a|b
+a&&b
+a||b
+a?b:z
+a=b a+=b a-=b a*=b a/=b a%=b a<<=b a>>=b a&=b a^=b a|=b
+a,y
+a::b
diff --git a/t/t4034/matlab/expect b/t/t4034/matlab/expect
new file mode 100644
index 0000000..72cf3e9
--- /dev/null
+++ b/t/t4034/matlab/expect
@@ -0,0 +1,14 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index dc204db..70e05f0 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,9 +1,9 @@<RESET>
+(<RED>1<RESET><GREEN>0<RESET>) (<RED>-1e10<RESET><GREEN>-0e10<RESET>) '<RED>b<RESET><GREEN>y<RESET>';
+[<RED>a<RESET><GREEN>x<RESET>] {<RED>a<RESET><GREEN>x<RESET>} <RED>a<RESET><GREEN>x<RESET>.<RED>b<RESET><GREEN>y<RESET>;
+~<RED>a<RESET><GREEN>x<RESET>;
+<RED>a<RESET><GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>.*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b a<RESET><GREEN>y x<RESET>./<RED>b a<RESET><GREEN>y x<RESET>^<RED>b a<RESET><GREEN>y x<RESET>.^<RED>b a<RESET><GREEN>y x<RESET>.\<RED>b a<RESET><GREEN>y x<RESET>.';
+<RED>a<RESET><GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET><GREEN>y<RESET>;
+<RED>a<RESET><GREEN>x<RESET>&<RED>b a<RESET><GREEN>y x<RESET>&&<RED>b a<RESET><GREEN>y x<RESET>|<RED>b a<RESET><GREEN>y x<RESET>||<RED>b<RESET><GREEN>y<RESET>;
+<RED>a<RESET><GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET><GREEN>y<RESET>;
+<RED>a<RESET><GREEN>x<RESET>==<RED>b a<RESET><GREEN>y x<RESET>~=<RED>b<RESET><GREEN>y<RESET>;
+<RED>a<RESET><GREEN>x<RESET>,<RED>b<RESET><GREEN>y<RESET>;
diff --git a/t/t4034/matlab/post b/t/t4034/matlab/post
new file mode 100644
index 0000000..70e05f0
--- /dev/null
+++ b/t/t4034/matlab/post
@@ -0,0 +1,9 @@
+(0) (-0e10) 'y';
+[x] {x} x.y;
+~x;
+x*y x.*y x/y x./y x^y x.^y x.\y x.';
+x+y x-y;
+x&y x&&y x|y x||y;
+x<y x<=y x>y x>=y;
+x==y x~=y;
+x,y;
diff --git a/t/t4034/matlab/pre b/t/t4034/matlab/pre
new file mode 100644
index 0000000..dc204db
--- /dev/null
+++ b/t/t4034/matlab/pre
@@ -0,0 +1,9 @@
+(1) (-1e10) 'b';
+[a] {a} a.b;
+~a;
+a*b a.*b a/b a./b a^b a.^b a.\b a.';
+a+b a-b;
+a&b a&&b a|b a||b;
+a<b a<=b a>b a>=b;
+a==b a~=b;
+a,b;
diff --git a/t/t4034/objc/expect b/t/t4034/objc/expect
new file mode 100644
index 0000000..e5d1dd2
--- /dev/null
+++ b/t/t4034/objc/expect
@@ -0,0 +1,35 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 9106d63..dd5f421 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,18 +1,18 @@<RESET>
+Foo() : x(0<RED>&&1<RESET><GREEN>&42<RESET>) { <GREEN>bar(x);<RESET> }
+cout<<"Hello World<RED>!<RESET><GREEN>?<RESET>\n"<<endl;
+<GREEN>(<RESET>1<GREEN>) (<RESET>-1e10<GREEN>) (<RESET>0xabcdef<GREEN>)<RESET> '<RED>x<RESET><GREEN>y<RESET>'
+[<RED>a<RESET><GREEN>x<RESET>] <RED>a<RESET><GREEN>x<RESET>-><RED>b a<RESET><GREEN>y x<RESET>.<RED>b<RESET><GREEN>y<RESET>
+!<RED>a<RESET><GREEN>x<RESET> ~<RED>a a<RESET><GREEN>x x<RESET>++ <RED>a<RESET><GREEN>x<RESET>-- <RED>a<RESET><GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b a<RESET><GREEN>y x<RESET>%<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<<RED>b a<RESET><GREEN>y x<RESET>>><RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>==<RED>b a<RESET><GREEN>y x<RESET>!=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>^<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>|<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>||<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>?<RED>b<RESET><GREEN>y<RESET>:z
+<RED>a<RESET><GREEN>x<RESET>=<RED>b a<RESET><GREEN>y x<RESET>+=<RED>b a<RESET><GREEN>y x<RESET>-=<RED>b a<RESET><GREEN>y x<RESET>*=<RED>b a<RESET><GREEN>y x<RESET>/=<RED>b a<RESET><GREEN>y x<RESET>%=<RED>b a<RESET><GREEN>y x<RESET><<=<RED>b a<RESET><GREEN>y x<RESET>>>=<RED>b a<RESET><GREEN>y x<RESET>&=<RED>b a<RESET><GREEN>y x<RESET>^=<RED>b a<RESET><GREEN>y x<RESET>|=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>,y
diff --git a/t/t4034/objc/post b/t/t4034/objc/post
new file mode 100644
index 0000000..dd5f421
--- /dev/null
+++ b/t/t4034/objc/post
@@ -0,0 +1,18 @@
+Foo() : x(0&42) { bar(x); }
+cout<<"Hello World?\n"<<endl;
+(1) (-1e10) (0xabcdef) 'y'
+[x] x->y x.y
+!x ~x x++ x-- x*y x&y
+x*y x/y x%y
+x+y x-y
+x<<y x>>y
+x<y x<=y x>y x>=y
+x==y x!=y
+x&y
+x^y
+x|y
+x&&y
+x||y
+x?y:z
+x=y x+=y x-=y x*=y x/=y x%=y x<<=y x>>=y x&=y x^=y x|=y
+x,y
diff --git a/t/t4034/objc/pre b/t/t4034/objc/pre
new file mode 100644
index 0000000..9106d63
--- /dev/null
+++ b/t/t4034/objc/pre
@@ -0,0 +1,18 @@
+Foo():x(0&&1){}
+cout<<"Hello World!\n"<<endl;
+1 -1e10 0xabcdef 'x'
+[a] a->b a.b
+!a ~a a++ a-- a*b a&b
+a*b a/b a%b
+a+b a-b
+a<<b a>>b
+a<b a<=b a>b a>=b
+a==b a!=b
+a&b
+a^b
+a|b
+a&&b
+a||b
+a?b:z
+a=b a+=b a-=b a*=b a/=b a%=b a<<=b a>>=b a&=b a^=b a|=b
+a,y
diff --git a/t/t4034/pascal/expect b/t/t4034/pascal/expect
new file mode 100644
index 0000000..2ce4230
--- /dev/null
+++ b/t/t4034/pascal/expect
@@ -0,0 +1,35 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 077046c..8865e6b 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,18 +1,18 @@<RESET>
+writeln("Hello World<RED>!<RESET><GREEN>?<RESET>");
+<GREEN>(<RESET>1<GREEN>) (<RESET>-1e10<GREEN>) (<RESET>0xabcdef<GREEN>)<RESET> '<RED>x<RESET><GREEN>y<RESET>'
+[<RED>a<RESET><GREEN>x<RESET>] <RED>a<RESET><GREEN>x<RESET>-><RED>b a<RESET><GREEN>y x<RESET>.<RED>b<RESET><GREEN>y<RESET>
+!<RED>a<RESET><GREEN>x<RESET> ~<RED>a a<RESET><GREEN>x x<RESET>++ <RED>a<RESET><GREEN>x<RESET>-- <RED>a<RESET><GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b a<RESET><GREEN>y x<RESET>%<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<<RED>b a<RESET><GREEN>y x<RESET>>><RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>==<RED>b a<RESET><GREEN>y x<RESET>!=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>^<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>|<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>||<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>?<RED>b<RESET><GREEN>y<RESET>:z
+<RED>a<RESET><GREEN>x<RESET>=<RED>b a<RESET><GREEN>y x<RESET>+=<RED>b a<RESET><GREEN>y x<RESET>-=<RED>b a<RESET><GREEN>y x<RESET>*=<RED>b a<RESET><GREEN>y x<RESET>/=<RED>b a<RESET><GREEN>y x<RESET>%=<RED>b a<RESET><GREEN>y x<RESET><<=<RED>b a<RESET><GREEN>y x<RESET>>>=<RED>b a<RESET><GREEN>y x<RESET>&=<RED>b a<RESET><GREEN>y x<RESET>^=<RED>b a<RESET><GREEN>y x<RESET>|=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>,y
+<RED>a<RESET><GREEN>x<RESET>::<RED>b<RESET><GREEN>y<RESET>
diff --git a/t/t4034/pascal/post b/t/t4034/pascal/post
new file mode 100644
index 0000000..8865e6b
--- /dev/null
+++ b/t/t4034/pascal/post
@@ -0,0 +1,18 @@
+writeln("Hello World?");
+(1) (-1e10) (0xabcdef) 'y'
+[x] x->y x.y
+!x ~x x++ x-- x*y x&y
+x*y x/y x%y
+x+y x-y
+x<<y x>>y
+x<y x<=y x>y x>=y
+x==y x!=y
+x&y
+x^y
+x|y
+x&&y
+x||y
+x?y:z
+x=y x+=y x-=y x*=y x/=y x%=y x<<=y x>>=y x&=y x^=y x|=y
+x,y
+x::y
diff --git a/t/t4034/pascal/pre b/t/t4034/pascal/pre
new file mode 100644
index 0000000..077046c
--- /dev/null
+++ b/t/t4034/pascal/pre
@@ -0,0 +1,18 @@
+writeln("Hello World!");
+1 -1e10 0xabcdef 'x'
+[a] a->b a.b
+!a ~a a++ a-- a*b a&b
+a*b a/b a%b
+a+b a-b
+a<<b a>>b
+a<b a<=b a>b a>=b
+a==b a!=b
+a&b
+a^b
+a|b
+a&&b
+a||b
+a?b:z
+a=b a+=b a-=b a*=b a/=b a%=b a<<=b a>>=b a&=b a^=b a|=b
+a,y
+a::b
diff --git a/t/t4034/perl/expect b/t/t4034/perl/expect
new file mode 100644
index 0000000..a1deb6b
--- /dev/null
+++ b/t/t4034/perl/expect
@@ -0,0 +1,13 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index f6610d3..e8b72ef 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -4,8 +4,8 @@<RESET>
+
+package Frotz;<RESET>
+sub new {<RESET>
+	my <GREEN>(<RESET>$class<GREEN>, %opts)<RESET> = <RED>shift<RESET><GREEN>@_<RESET>;
+	return bless { <GREEN>xyzzy => "nitfol", %opts<RESET> }, $class;
+}<RESET>
+
+__END__<RESET>
diff --git a/t/t4034/perl/post b/t/t4034/perl/post
new file mode 100644
index 0000000..e8b72ef
--- /dev/null
+++ b/t/t4034/perl/post
@@ -0,0 +1,22 @@
+#!/usr/bin/perl
+
+use strict;
+
+package Frotz;
+sub new {
+	my ($class, %opts) = @_;
+	return bless { xyzzy => "nitfol", %opts }, $class;
+}
+
+__END__
+=head1 NAME
+
+frotz - Frotz
+
+=head1 SYNOPSIS
+
+  use frotz;
+
+  $nitfol = new Frotz();
+
+=cut
diff --git a/t/t4034/perl/pre b/t/t4034/perl/pre
new file mode 100644
index 0000000..f6610d3
--- /dev/null
+++ b/t/t4034/perl/pre
@@ -0,0 +1,22 @@
+#!/usr/bin/perl
+
+use strict;
+
+package Frotz;
+sub new {
+	my $class = shift;
+	return bless {}, $class;
+}
+
+__END__
+=head1 NAME
+
+frotz - Frotz
+
+=head1 SYNOPSIS
+
+  use frotz;
+
+  $nitfol = new Frotz();
+
+=cut
diff --git a/t/t4034/php/expect b/t/t4034/php/expect
new file mode 100644
index 0000000..0404408
--- /dev/null
+++ b/t/t4034/php/expect
@@ -0,0 +1,35 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index cf6e06b..4420a49 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,18 +1,18 @@<RESET>
+<GREEN>(<RESET>$var<GREEN>)<RESET> $ var
+<?="Hello World<RED>!<RESET><GREEN>?<RESET>"?>
+<GREEN>(<RESET>1<GREEN>) (<RESET>-1e10<GREEN>) (<RESET>0xabcdef<GREEN>)<RESET> '<RED>x<RESET><GREEN>y<RESET>'
+[<RED>a<RESET><GREEN>x<RESET>] <RED>a<RESET><GREEN>x<RESET>-><RED>b a<RESET><GREEN>y x<RESET>.<RED>b<RESET><GREEN>y<RESET>
+!<RED>a<RESET><GREEN>x<RESET> ~<RED>a a<RESET><GREEN>x x<RESET>++ <RED>a<RESET><GREEN>x<RESET>-- <RED>a<RESET><GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b a<RESET><GREEN>y x<RESET>%<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<<RED>b a<RESET><GREEN>y x<RESET>>><RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>==<RED>b a<RESET><GREEN>y x<RESET>!=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>^<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>|<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>||<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>?<RED>b<RESET><GREEN>y<RESET>:z
+<RED>a<RESET><GREEN>x<RESET>=<RED>b a<RESET><GREEN>y x<RESET>+=<RED>b a<RESET><GREEN>y x<RESET>-=<RED>b a<RESET><GREEN>y x<RESET>*=<RED>b a<RESET><GREEN>y x<RESET>/=<RED>b a<RESET><GREEN>y x<RESET>%=<RED>b a<RESET><GREEN>y x<RESET><<=<RED>b a<RESET><GREEN>y x<RESET>>>=<RED>b a<RESET><GREEN>y x<RESET>&=<RED>b a<RESET><GREEN>y x<RESET>^=<RED>b a<RESET><GREEN>y x<RESET>|=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>,y
diff --git a/t/t4034/php/post b/t/t4034/php/post
new file mode 100644
index 0000000..4420a49
--- /dev/null
+++ b/t/t4034/php/post
@@ -0,0 +1,18 @@
+($var) $ var
+<?="Hello World?"?>
+(1) (-1e10) (0xabcdef) 'y'
+[x] x->y x.y
+!x ~x x++ x-- x*y x&y
+x*y x/y x%y
+x+y x-y
+x<<y x>>y
+x<y x<=y x>y x>=y
+x==y x!=y
+x&y
+x^y
+x|y
+x&&y
+x||y
+x?y:z
+x=y x+=y x-=y x*=y x/=y x%=y x<<=y x>>=y x&=y x^=y x|=y
+x,y
diff --git a/t/t4034/php/pre b/t/t4034/php/pre
new file mode 100644
index 0000000..cf6e06b
--- /dev/null
+++ b/t/t4034/php/pre
@@ -0,0 +1,18 @@
+$var $var
+<?= "Hello World!" ?>
+1 -1e10 0xabcdef 'x'
+[a] a->b a.b
+!a ~a a++ a-- a*b a&b
+a*b a/b a%b
+a+b a-b
+a<<b a>>b
+a<b a<=b a>b a>=b
+a==b a!=b
+a&b
+a^b
+a|b
+a&&b
+a||b
+a?b:z
+a=b a+=b a-=b a*=b a/=b a%=b a<<=b a>>=b a&=b a^=b a|=b
+a,y
diff --git a/t/t4034/python/expect b/t/t4034/python/expect
new file mode 100644
index 0000000..8abb8a4
--- /dev/null
+++ b/t/t4034/python/expect
@@ -0,0 +1,34 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 438f776..68baf34 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,17 +1,17 @@<RESET>
+print<RED>u<RESET> "Hello World<RED>!<RESET><GREEN>?<RESET>\n"<GREEN>; print<RESET>
+<GREEN>(<RESET>1<GREEN>) (<RESET>-1e10<GREEN>) (<RESET>0xabcdef<GREEN>) u<RESET>'<RED>x<RESET><GREEN>y<RESET>'
+[<RED>a<RESET><GREEN>x<RESET>] <RED>a<RESET><GREEN>x<RESET>-><RED>b a<RESET><GREEN>y x<RESET>.<RED>b<RESET><GREEN>y<RESET>
+!<RED>a<RESET><GREEN>x<RESET> ~<RED>a a<RESET><GREEN>x x<RESET>++ <RED>a<RESET><GREEN>x<RESET>-- <RED>a<RESET><GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b a<RESET><GREEN>y x<RESET>%<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<<RED>b a<RESET><GREEN>y x<RESET>>><RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>==<RED>b a<RESET><GREEN>y x<RESET>!=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>^<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>|<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>||<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>?<RED>b<RESET><GREEN>y<RESET>:z
+<RED>a<RESET><GREEN>x<RESET>=<RED>b a<RESET><GREEN>y x<RESET>+=<RED>b a<RESET><GREEN>y x<RESET>-=<RED>b a<RESET><GREEN>y x<RESET>*=<RED>b a<RESET><GREEN>y x<RESET>/=<RED>b a<RESET><GREEN>y x<RESET>%=<RED>b a<RESET><GREEN>y x<RESET><<=<RED>b a<RESET><GREEN>y x<RESET>>>=<RED>b a<RESET><GREEN>y x<RESET>&=<RED>b a<RESET><GREEN>y x<RESET>^=<RED>b a<RESET><GREEN>y x<RESET>|=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>,y
diff --git a/t/t4034/python/post b/t/t4034/python/post
new file mode 100644
index 0000000..68baf34
--- /dev/null
+++ b/t/t4034/python/post
@@ -0,0 +1,17 @@
+print "Hello World?\n"; print
+(1) (-1e10) (0xabcdef) u'y'
+[x] x->y x.y
+!x ~x x++ x-- x*y x&y
+x*y x/y x%y
+x+y x-y
+x<<y x>>y
+x<y x<=y x>y x>=y
+x==y x!=y
+x&y
+x^y
+x|y
+x&&y
+x||y
+x?y:z
+x=y x+=y x-=y x*=y x/=y x%=y x<<=y x>>=y x&=y x^=y x|=y
+x,y
diff --git a/t/t4034/python/pre b/t/t4034/python/pre
new file mode 100644
index 0000000..438f776
--- /dev/null
+++ b/t/t4034/python/pre
@@ -0,0 +1,17 @@
+print u"Hello World!\n"
+1 -1e10 0xabcdef 'x'
+[a] a->b a.b
+!a ~a a++ a-- a*b a&b
+a*b a/b a%b
+a+b a-b
+a<<b a>>b
+a<b a<=b a>b a>=b
+a==b a!=b
+a&b
+a^b
+a|b
+a&&b
+a||b
+a?b:z
+a=b a+=b a-=b a*=b a/=b a%=b a<<=b a>>=b a&=b a^=b a|=b
+a,y
diff --git a/t/t4034/ruby/expect b/t/t4034/ruby/expect
new file mode 100644
index 0000000..16e1dd5
--- /dev/null
+++ b/t/t4034/ruby/expect
@@ -0,0 +1,34 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 30ed9a1..7678f14 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,17 +1,17 @@<RESET>
+10.downto(1) {|<RED>x<RESET><GREEN>y<RESET>| puts <RED>x<RESET><GREEN>y<RESET>}
+<GREEN>(<RESET>1<GREEN>) (<RESET>-1e10<GREEN>) (<RESET>0xabcdef<GREEN>)<RESET> '<RED>x<RESET><GREEN>y<RESET>'
+[<RED>a<RESET><GREEN>x<RESET>] <RED>a<RESET><GREEN>x<RESET>-><RED>b a<RESET><GREEN>y x<RESET>.<RED>b<RESET><GREEN>y<RESET>
+!<RED>a<RESET><GREEN>x<RESET> ~<RED>a a<RESET><GREEN>x x<RESET>++ <RED>a<RESET><GREEN>x<RESET>-- <RED>a<RESET><GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b a<RESET><GREEN>y x<RESET>%<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<<RED>b a<RESET><GREEN>y x<RESET>>><RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>==<RED>b a<RESET><GREEN>y x<RESET>!=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>^<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>|<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>||<RED>b<RESET>
+<RED>a?b<RESET><GREEN>y<RESET>
+<GREEN>x?y<RESET>:z
+<RED>a<RESET><GREEN>x<RESET>=<RED>b a<RESET><GREEN>y x<RESET>+=<RED>b a<RESET><GREEN>y x<RESET>-=<RED>b a<RESET><GREEN>y x<RESET>*=<RED>b a<RESET><GREEN>y x<RESET>/=<RED>b a<RESET><GREEN>y x<RESET>%=<RED>b a<RESET><GREEN>y x<RESET><<=<RED>b a<RESET><GREEN>y x<RESET>>>=<RED>b a<RESET><GREEN>y x<RESET>&=<RED>b a<RESET><GREEN>y x<RESET>^=<RED>b a<RESET><GREEN>y x<RESET>|=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>,y
diff --git a/t/t4034/ruby/post b/t/t4034/ruby/post
new file mode 100644
index 0000000..7678f14
--- /dev/null
+++ b/t/t4034/ruby/post
@@ -0,0 +1,17 @@
+10.downto(1) {|y| puts y}
+(1) (-1e10) (0xabcdef) 'y'
+[x] x->y x.y
+!x ~x x++ x-- x*y x&y
+x*y x/y x%y
+x+y x-y
+x<<y x>>y
+x<y x<=y x>y x>=y
+x==y x!=y
+x&y
+x^y
+x|y
+x&&y
+x||y
+x?y:z
+x=y x+=y x-=y x*=y x/=y x%=y x<<=y x>>=y x&=y x^=y x|=y
+x,y
diff --git a/t/t4034/ruby/pre b/t/t4034/ruby/pre
new file mode 100644
index 0000000..30ed9a1
--- /dev/null
+++ b/t/t4034/ruby/pre
@@ -0,0 +1,17 @@
+10.downto(1) {|x| puts x}
+1 -1e10 0xabcdef 'x'
+[a] a->b a.b
+!a ~a a++ a-- a*b a&b
+a*b a/b a%b
+a+b a-b
+a<<b a>>b
+a<b a<=b a>b a>=b
+a==b a!=b
+a&b
+a^b
+a|b
+a&&b
+a||b
+a?b:z
+a=b a+=b a-=b a*=b a/=b a%=b a<<=b a>>=b a&=b a^=b a|=b
+a,y
diff --git a/t/t4034/tex/expect b/t/t4034/tex/expect
new file mode 100644
index 0000000..604969b
--- /dev/null
+++ b/t/t4034/tex/expect
@@ -0,0 +1,9 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 2b2dfcb..65cab61 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,4 +1,4 @@<RESET>
+\section{Something <GREEN>new<RESET>}
+<RED>\emph<RESET><GREEN>\textbf<RESET>{Macro style}
+{<RED>\em<RESET><GREEN>\bfseries<RESET> State toggle style}
+\\[<RED>1em<RESET><GREEN>1cm<RESET>]
diff --git a/t/t4034/tex/post b/t/t4034/tex/post
new file mode 100644
index 0000000..65cab61
--- /dev/null
+++ b/t/t4034/tex/post
@@ -0,0 +1,4 @@
+\section{Something new}
+\textbf{Macro style}
+{\bfseries State toggle style}
+\\[1cm]
diff --git a/t/t4034/tex/pre b/t/t4034/tex/pre
new file mode 100644
index 0000000..2b2dfcb
--- /dev/null
+++ b/t/t4034/tex/pre
@@ -0,0 +1,4 @@
+\section{Something}
+\emph{Macro style}
+{\em State toggle style}
+\\[1em]
diff --git a/t/t4035-diff-quiet.sh b/t/t4035-diff-quiet.sh
index e747e84..231412d 100755
--- a/t/t4035-diff-quiet.sh
+++ b/t/t4035-diff-quiet.sh
@@ -10,71 +10,142 @@
 	git commit -m first &&
 	echo 2 >b &&
 	git add . &&
-	git commit -a -m second
+	git commit -a -m second &&
+	mkdir -p test-outside/repo && (
+		cd test-outside/repo &&
+		git init &&
+		echo "1 1" >a &&
+		git add . &&
+		git commit -m 1
+	) &&
+	mkdir -p test-outside/non/git && (
+		cd test-outside/non/git &&
+		echo "1 1" >a &&
+		echo "1 1" >matching-file &&
+		echo "1 1 " >trailing-space &&
+		echo "1   1" >extra-space &&
+		echo "2" >never-match
+	)
 '
 
 test_expect_success 'git diff-tree HEAD^ HEAD' '
 	git diff-tree --quiet HEAD^ HEAD >cnt
-	test $? = 1 && test $(wc -l <cnt) = 0
+	test $? = 1 && test_line_count = 0 cnt
 '
 test_expect_success 'git diff-tree HEAD^ HEAD -- a' '
 	git diff-tree --quiet HEAD^ HEAD -- a >cnt
-	test $? = 0 && test $(wc -l <cnt) = 0
+	test $? = 0 && test_line_count = 0 cnt
 '
 test_expect_success 'git diff-tree HEAD^ HEAD -- b' '
 	git diff-tree --quiet HEAD^ HEAD -- b >cnt
-	test $? = 1 && test $(wc -l <cnt) = 0
+	test $? = 1 && test_line_count = 0 cnt
 '
 # this diff outputs one line: sha1 of the given head
 test_expect_success 'echo HEAD | git diff-tree --stdin' '
 	echo $(git rev-parse HEAD) | git diff-tree --quiet --stdin >cnt
-	test $? = 1 && test $(wc -l <cnt) = 1
+	test $? = 1 && test_line_count = 1 cnt
 '
 test_expect_success 'git diff-tree HEAD HEAD' '
 	git diff-tree --quiet HEAD HEAD >cnt
-	test $? = 0 && test $(wc -l <cnt) = 0
+	test $? = 0 && test_line_count = 0 cnt
 '
 test_expect_success 'git diff-files' '
 	git diff-files --quiet >cnt
-	test $? = 0 && test $(wc -l <cnt) = 0
+	test $? = 0 && test_line_count = 0 cnt
 '
 test_expect_success 'git diff-index --cached HEAD' '
 	git diff-index --quiet --cached HEAD >cnt
-	test $? = 0 && test $(wc -l <cnt) = 0
+	test $? = 0 && test_line_count = 0 cnt
 '
 test_expect_success 'git diff-index --cached HEAD^' '
 	git diff-index --quiet --cached HEAD^ >cnt
-	test $? = 1 && test $(wc -l <cnt) = 0
+	test $? = 1 && test_line_count = 0 cnt
 '
 test_expect_success 'git diff-index --cached HEAD^' '
 	echo text >>b &&
 	echo 3 >c &&
 	git add . && {
 		git diff-index --quiet --cached HEAD^ >cnt
-		test $? = 1 && test $(wc -l <cnt) = 0
+		test $? = 1 && test_line_count = 0 cnt
 	}
 '
 test_expect_success 'git diff-tree -Stext HEAD^ HEAD -- b' '
 	git commit -m "text in b" && {
 		git diff-tree --quiet -Stext HEAD^ HEAD -- b >cnt
-		test $? = 1 && test $(wc -l <cnt) = 0
+		test $? = 1 && test_line_count = 0 cnt
 	}
 '
 test_expect_success 'git diff-tree -Snot-found HEAD^ HEAD -- b' '
 	git diff-tree --quiet -Snot-found HEAD^ HEAD -- b >cnt
-	test $? = 0 && test $(wc -l <cnt) = 0
+	test $? = 0 && test_line_count = 0 cnt
 '
 test_expect_success 'git diff-files' '
 	echo 3 >>c && {
 		git diff-files --quiet >cnt
-		test $? = 1 && test $(wc -l <cnt) = 0
+		test $? = 1 && test_line_count = 0 cnt
 	}
 '
 test_expect_success 'git diff-index --cached HEAD' '
 	git update-index c && {
 		git diff-index --quiet --cached HEAD >cnt
-		test $? = 1 && test $(wc -l <cnt) = 0
+		test $? = 1 && test_line_count = 0 cnt
 	}
 '
 
+test_expect_success 'git diff, one file outside repo' '
+	(
+		cd test-outside/repo &&
+		test_expect_code 0 git diff --quiet a ../non/git/matching-file &&
+		test_expect_code 1 git diff --quiet a ../non/git/extra-space
+	)
+'
+
+test_expect_success 'git diff, both files outside repo' '
+	(
+		GIT_CEILING_DIRECTORIES="$TRASH_DIRECTORY/test-outside" &&
+		export GIT_CEILING_DIRECTORIES &&
+		cd test-outside/non/git &&
+		test_expect_code 0 git diff --quiet a matching-file &&
+		test_expect_code 1 git diff --quiet a extra-space
+	)
+'
+
+test_expect_success 'git diff --ignore-space-at-eol, one file outside repo' '
+	(
+		cd test-outside/repo &&
+		test_expect_code 0 git diff --quiet --ignore-space-at-eol a ../non/git/trailing-space &&
+		test_expect_code 1 git diff --quiet --ignore-space-at-eol a ../non/git/extra-space
+	)
+'
+
+test_expect_success 'git diff --ignore-space-at-eol, both files outside repo' '
+	(
+		GIT_CEILING_DIRECTORIES="$TRASH_DIRECTORY/test-outside" &&
+		export GIT_CEILING_DIRECTORIES &&
+		cd test-outside/non/git &&
+		test_expect_code 0 git diff --quiet --ignore-space-at-eol a trailing-space &&
+		test_expect_code 1 git diff --quiet --ignore-space-at-eol a extra-space
+	)
+'
+
+test_expect_success 'git diff --ignore-all-space, one file outside repo' '
+	(
+		cd test-outside/repo &&
+		test_expect_code 0 git diff --quiet --ignore-all-space a ../non/git/trailing-space &&
+		test_expect_code 0 git diff --quiet --ignore-all-space a ../non/git/extra-space &&
+		test_expect_code 1 git diff --quiet --ignore-all-space a ../non/git/never-match
+	)
+'
+
+test_expect_success 'git diff --ignore-all-space, both files outside repo' '
+	(
+		GIT_CEILING_DIRECTORIES="$TRASH_DIRECTORY/test-outside" &&
+		export GIT_CEILING_DIRECTORIES &&
+		cd test-outside/non/git &&
+		test_expect_code 0 git diff --quiet --ignore-all-space a trailing-space &&
+		test_expect_code 0 git diff --quiet --ignore-all-space a extra-space &&
+		test_expect_code 1 git diff --quiet --ignore-all-space a never-match
+	)
+'
+
 test_done
diff --git a/t/t4038-diff-combined.sh b/t/t4038-diff-combined.sh
index 2cf7e01..40277c7 100755
--- a/t/t4038-diff-combined.sh
+++ b/t/t4038-diff-combined.sh
@@ -76,9 +76,17 @@
 	verify_helper sidewithone
 '
 
-test_expect_failure 'check combined output (2)' '
+test_expect_success 'check combined output (2)' '
 	git show sidesansone -- >sidesansone &&
 	verify_helper sidesansone
 '
 
+test_expect_success 'diagnose truncated file' '
+	>file &&
+	git add file &&
+	git commit --amend -C HEAD &&
+	git show >out &&
+	grep "diff --cc file" out
+'
+
 test_done
diff --git a/t/t4039-diff-assume-unchanged.sh b/t/t4039-diff-assume-unchanged.sh
new file mode 100755
index 0000000..9d9498b
--- /dev/null
+++ b/t/t4039-diff-assume-unchanged.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+test_description='diff with assume-unchanged entries'
+
+. ./test-lib.sh
+
+# external diff has been tested in t4020-diff-external.sh
+
+test_expect_success 'setup' '
+	echo zero > zero &&
+	git add zero &&
+	git commit -m zero &&
+	echo one > one &&
+	echo two > two &&
+	git add one two &&
+	git commit -m onetwo &&
+	git update-index --assume-unchanged one &&
+	echo borked >> one &&
+	test "$(git ls-files -v one)" = "h one"
+'
+
+test_expect_success 'diff-index does not examine assume-unchanged entries' '
+	git diff-index HEAD^ -- one | grep -q 5626abf0f72e58d7a153368ba57db4c673c0e171
+'
+
+test_expect_success 'diff-files does not examine assume-unchanged entries' '
+	rm one &&
+	test -z "$(git diff-files -- one)"
+'
+
+test_done
diff --git a/t/t4040-whitespace-status.sh b/t/t4040-whitespace-status.sh
new file mode 100755
index 0000000..3c728a3
--- /dev/null
+++ b/t/t4040-whitespace-status.sh
@@ -0,0 +1,75 @@
+#!/bin/sh
+
+test_description='diff --exit-code with whitespace'
+. ./test-lib.sh
+
+test_expect_success setup '
+	mkdir a b &&
+	echo >c &&
+	echo >a/d &&
+	echo >b/e &&
+	git add . &&
+	test_tick &&
+	git commit -m initial &&
+	echo " " >a/d &&
+	test_tick &&
+	git commit -a -m second &&
+	echo "  " >a/d &&
+	echo " " >b/e &&
+	git add a/d
+'
+
+test_expect_success 'diff-tree --exit-code' '
+	test_must_fail git diff --exit-code HEAD^ HEAD &&
+	test_must_fail git diff-tree --exit-code HEAD^ HEAD
+'
+
+test_expect_success 'diff-tree -b --exit-code' '
+	git diff -b --exit-code HEAD^ HEAD &&
+	git diff-tree -b -p --exit-code HEAD^ HEAD &&
+	git diff-tree -b --exit-code HEAD^ HEAD
+'
+
+test_expect_success 'diff-index --cached --exit-code' '
+	test_must_fail git diff --cached --exit-code HEAD &&
+	test_must_fail git diff-index --cached --exit-code HEAD
+'
+
+test_expect_success 'diff-index -b -p --cached --exit-code' '
+	git diff -b --cached --exit-code HEAD &&
+	git diff-index -b -p --cached --exit-code HEAD
+'
+
+test_expect_success 'diff-index --exit-code' '
+	test_must_fail git diff --exit-code HEAD &&
+	test_must_fail git diff-index --exit-code HEAD
+'
+
+test_expect_success 'diff-index -b -p --exit-code' '
+	git diff -b --exit-code HEAD &&
+	git diff-index -b -p --exit-code HEAD
+'
+
+test_expect_success 'diff-files --exit-code' '
+	test_must_fail git diff --exit-code &&
+	test_must_fail git diff-files --exit-code
+'
+
+test_expect_success 'diff-files -b -p --exit-code' '
+	git diff -b --exit-code &&
+	git diff-files -b -p --exit-code
+'
+
+test_expect_success 'diff-files --diff-filter --quiet' '
+	git reset --hard &&
+	rm a/d &&
+	echo x >>b/e &&
+	test_must_fail git diff-files --diff-filter=M --quiet
+'
+
+test_expect_success 'diff-tree --diff-filter --quiet' '
+	git commit -a -m "worktree state" &&
+	test_must_fail git diff-tree --diff-filter=M --quiet HEAD^ HEAD
+'
+
+test_done
diff --git a/t/t4041-diff-submodule-option.sh b/t/t4041-diff-submodule-option.sh
new file mode 100755
index 0000000..6c01d0c
--- /dev/null
+++ b/t/t4041-diff-submodule-option.sh
@@ -0,0 +1,495 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Jens Lehmann, based on t7401 by Ping Yin
+#
+
+test_description='Support for verbose submodule differences in git diff
+
+This test tries to verify the sanity of the --submodule option of git diff.
+'
+
+. ./test-lib.sh
+
+add_file () {
+	sm=$1
+	shift
+	owd=$(pwd)
+	cd "$sm"
+	for name; do
+		echo "$name" > "$name" &&
+		git add "$name" &&
+		test_tick &&
+		git commit -m "Add $name"
+	done >/dev/null
+	git rev-parse --verify HEAD | cut -c1-7
+	cd "$owd"
+}
+commit_file () {
+	test_tick &&
+	git commit "$@" -m "Commit $*" >/dev/null
+}
+
+test_create_repo sm1 &&
+add_file . foo >/dev/null
+
+head1=$(add_file sm1 foo1 foo2)
+
+test_expect_success 'added submodule' "
+	git add sm1 &&
+	git diff-index -p --submodule=log HEAD >actual &&
+	cat >expected <<-EOF &&
+Submodule sm1 0000000...$head1 (new submodule)
+EOF
+	test_cmp expected actual
+"
+
+commit_file sm1 &&
+head2=$(add_file sm1 foo3)
+
+test_expect_success 'modified submodule(forward)' "
+	git diff-index -p --submodule=log HEAD >actual &&
+	cat >expected <<-EOF &&
+Submodule sm1 $head1..$head2:
+  > Add foo3
+EOF
+	test_cmp expected actual
+"
+
+test_expect_success 'modified submodule(forward)' "
+	git diff --submodule=log >actual &&
+	cat >expected <<-EOF &&
+Submodule sm1 $head1..$head2:
+  > Add foo3
+EOF
+	test_cmp expected actual
+"
+
+test_expect_success 'modified submodule(forward) --submodule' "
+	git diff --submodule >actual &&
+	cat >expected <<-EOF &&
+Submodule sm1 $head1..$head2:
+  > Add foo3
+EOF
+	test_cmp expected actual
+"
+
+fullhead1=$(cd sm1; git rev-list --max-count=1 $head1)
+fullhead2=$(cd sm1; git rev-list --max-count=1 $head2)
+test_expect_success 'modified submodule(forward) --submodule=short' "
+	git diff --submodule=short >actual &&
+	cat >expected <<-EOF &&
+diff --git a/sm1 b/sm1
+index $head1..$head2 160000
+--- a/sm1
++++ b/sm1
+@@ -1 +1 @@
+-Subproject commit $fullhead1
++Subproject commit $fullhead2
+EOF
+	test_cmp expected actual
+"
+
+commit_file sm1 &&
+head3=$(
+	cd sm1 &&
+	git reset --hard HEAD~2 >/dev/null &&
+	git rev-parse --verify HEAD | cut -c1-7
+)
+
+test_expect_success 'modified submodule(backward)' "
+	git diff-index -p --submodule=log HEAD >actual &&
+	cat >expected <<-EOF &&
+Submodule sm1 $head2..$head3 (rewind):
+  < Add foo3
+  < Add foo2
+EOF
+	test_cmp expected actual
+"
+
+head4=$(add_file sm1 foo4 foo5) &&
+head4_full=$(GIT_DIR=sm1/.git git rev-parse --verify HEAD)
+test_expect_success 'modified submodule(backward and forward)' "
+	git diff-index -p --submodule=log HEAD >actual &&
+	cat >expected <<-EOF &&
+Submodule sm1 $head2...$head4:
+  > Add foo5
+  > Add foo4
+  < Add foo3
+  < Add foo2
+EOF
+	test_cmp expected actual
+"
+
+commit_file sm1 &&
+mv sm1 sm1-bak &&
+echo sm1 >sm1 &&
+head5=$(git hash-object sm1 | cut -c1-7) &&
+git add sm1 &&
+rm -f sm1 &&
+mv sm1-bak sm1
+
+test_expect_success 'typechanged submodule(submodule->blob), --cached' "
+	git diff --submodule=log --cached >actual &&
+	cat >expected <<-EOF &&
+Submodule sm1 41fbea9...0000000 (submodule deleted)
+diff --git a/sm1 b/sm1
+new file mode 100644
+index 0000000..9da5fb8
+--- /dev/null
++++ b/sm1
+@@ -0,0 +1 @@
++sm1
+EOF
+	test_cmp expected actual
+"
+
+test_expect_success 'typechanged submodule(submodule->blob)' "
+	git diff --submodule=log >actual &&
+	cat >expected <<-EOF &&
+diff --git a/sm1 b/sm1
+deleted file mode 100644
+index 9da5fb8..0000000
+--- a/sm1
++++ /dev/null
+@@ -1 +0,0 @@
+-sm1
+Submodule sm1 0000000...$head4 (new submodule)
+EOF
+	test_cmp expected actual
+"
+
+rm -rf sm1 &&
+git checkout-index sm1
+test_expect_success 'typechanged submodule(submodule->blob)' "
+	git diff-index -p --submodule=log HEAD >actual &&
+	cat >expected <<-EOF &&
+Submodule sm1 $head4...0000000 (submodule deleted)
+diff --git a/sm1 b/sm1
+new file mode 100644
+index 0000000..$head5
+--- /dev/null
++++ b/sm1
+@@ -0,0 +1 @@
++sm1
+EOF
+	test_cmp expected actual
+"
+
+rm -f sm1 &&
+test_create_repo sm1 &&
+head6=$(add_file sm1 foo6 foo7)
+fullhead6=$(cd sm1; git rev-list --max-count=1 $head6)
+test_expect_success 'nonexistent commit' "
+	git diff-index -p --submodule=log HEAD >actual &&
+	cat >expected <<-EOF &&
+Submodule sm1 $head4...$head6 (commits not present)
+EOF
+	test_cmp expected actual
+"
+
+commit_file
+test_expect_success 'typechanged submodule(blob->submodule)' "
+	git diff-index -p --submodule=log HEAD >actual &&
+	cat >expected <<-EOF &&
+diff --git a/sm1 b/sm1
+deleted file mode 100644
+index $head5..0000000
+--- a/sm1
++++ /dev/null
+@@ -1 +0,0 @@
+-sm1
+Submodule sm1 0000000...$head6 (new submodule)
+EOF
+	test_cmp expected actual
+"
+
+commit_file sm1 &&
+test_expect_success 'submodule is up to date' "
+	git diff-index -p --submodule=log HEAD >actual &&
+	cat >expected <<-EOF &&
+EOF
+	test_cmp expected actual
+"
+
+test_expect_success 'submodule contains untracked content' "
+	echo new > sm1/new-file &&
+	git diff-index -p --submodule=log HEAD >actual &&
+	cat >expected <<-EOF &&
+Submodule sm1 contains untracked content
+EOF
+	test_cmp expected actual
+"
+
+test_expect_success 'submodule contains untracked content (untracked ignored)' "
+	git diff-index -p --ignore-submodules=untracked --submodule=log HEAD >actual &&
+	! test -s actual
+"
+
+test_expect_success 'submodule contains untracked content (dirty ignored)' "
+	git diff-index -p --ignore-submodules=dirty --submodule=log HEAD >actual &&
+	! test -s actual
+"
+
+test_expect_success 'submodule contains untracked content (all ignored)' "
+	git diff-index -p --ignore-submodules=all --submodule=log HEAD >actual &&
+	! test -s actual
+"
+
+test_expect_success 'submodule contains untracked and modifed content' "
+	echo new > sm1/foo6 &&
+	git diff-index -p --submodule=log HEAD >actual &&
+	cat >expected <<-EOF &&
+Submodule sm1 contains untracked content
+Submodule sm1 contains modified content
+EOF
+	test_cmp expected actual
+"
+
+test_expect_success 'submodule contains untracked and modifed content (untracked ignored)' "
+	echo new > sm1/foo6 &&
+	git diff-index -p --ignore-submodules=untracked --submodule=log HEAD >actual &&
+	cat >expected <<-EOF &&
+Submodule sm1 contains modified content
+EOF
+	test_cmp expected actual
+"
+
+test_expect_success 'submodule contains untracked and modifed content (dirty ignored)' "
+	echo new > sm1/foo6 &&
+	git diff-index -p --ignore-submodules=dirty --submodule=log HEAD >actual &&
+	! test -s actual
+"
+
+test_expect_success 'submodule contains untracked and modifed content (all ignored)' "
+	echo new > sm1/foo6 &&
+	git diff-index -p --ignore-submodules --submodule=log HEAD >actual &&
+	! test -s actual
+"
+
+test_expect_success 'submodule contains modifed content' "
+	rm -f sm1/new-file &&
+	git diff-index -p --submodule=log HEAD >actual &&
+	cat >expected <<-EOF &&
+Submodule sm1 contains modified content
+EOF
+	test_cmp expected actual
+"
+
+(cd sm1; git commit -mchange foo6 >/dev/null) &&
+head8=$(cd sm1; git rev-parse --verify HEAD | cut -c1-7) &&
+test_expect_success 'submodule is modified' "
+	git diff-index -p --submodule=log HEAD >actual &&
+	cat >expected <<-EOF &&
+Submodule sm1 $head6..$head8:
+  > change
+EOF
+	test_cmp expected actual
+"
+
+test_expect_success 'modified submodule contains untracked content' "
+	echo new > sm1/new-file &&
+	git diff-index -p --submodule=log HEAD >actual &&
+	cat >expected <<-EOF &&
+Submodule sm1 contains untracked content
+Submodule sm1 $head6..$head8:
+  > change
+EOF
+	test_cmp expected actual
+"
+
+test_expect_success 'modified submodule contains untracked content (untracked ignored)' "
+	git diff-index -p --ignore-submodules=untracked --submodule=log HEAD >actual &&
+	cat >expected <<-EOF &&
+Submodule sm1 $head6..$head8:
+  > change
+EOF
+	test_cmp expected actual
+"
+
+test_expect_success 'modified submodule contains untracked content (dirty ignored)' "
+	git diff-index -p --ignore-submodules=dirty --submodule=log HEAD >actual &&
+	cat >expected <<-EOF &&
+Submodule sm1 $head6..$head8:
+  > change
+EOF
+	test_cmp expected actual
+"
+
+test_expect_success 'modified submodule contains untracked content (all ignored)' "
+	git diff-index -p --ignore-submodules=all --submodule=log HEAD >actual &&
+	! test -s actual
+"
+
+test_expect_success 'modified submodule contains untracked and modifed content' "
+	echo modification >> sm1/foo6 &&
+	git diff-index -p --submodule=log HEAD >actual &&
+	cat >expected <<-EOF &&
+Submodule sm1 contains untracked content
+Submodule sm1 contains modified content
+Submodule sm1 $head6..$head8:
+  > change
+EOF
+	test_cmp expected actual
+"
+
+test_expect_success 'modified submodule contains untracked and modifed content (untracked ignored)' "
+	echo modification >> sm1/foo6 &&
+	git diff-index -p --ignore-submodules=untracked --submodule=log HEAD >actual &&
+	cat >expected <<-EOF &&
+Submodule sm1 contains modified content
+Submodule sm1 $head6..$head8:
+  > change
+EOF
+	test_cmp expected actual
+"
+
+test_expect_success 'modified submodule contains untracked and modifed content (dirty ignored)' "
+	echo modification >> sm1/foo6 &&
+	git diff-index -p --ignore-submodules=dirty --submodule=log HEAD >actual &&
+	cat >expected <<-EOF &&
+Submodule sm1 $head6..$head8:
+  > change
+EOF
+	test_cmp expected actual
+"
+
+test_expect_success 'modified submodule contains untracked and modifed content (all ignored)' "
+	echo modification >> sm1/foo6 &&
+	git diff-index -p --ignore-submodules --submodule=log HEAD >actual &&
+	! test -s actual
+"
+
+test_expect_success 'modified submodule contains modifed content' "
+	rm -f sm1/new-file &&
+	git diff-index -p --submodule=log HEAD >actual &&
+	cat >expected <<-EOF &&
+Submodule sm1 contains modified content
+Submodule sm1 $head6..$head8:
+  > change
+EOF
+	test_cmp expected actual
+"
+
+rm -rf sm1
+test_expect_success 'deleted submodule' "
+	git diff-index -p --submodule=log HEAD >actual &&
+	cat >expected <<-EOF &&
+Submodule sm1 $head6...0000000 (submodule deleted)
+EOF
+	test_cmp expected actual
+"
+
+test_create_repo sm2 &&
+head7=$(add_file sm2 foo8 foo9) &&
+git add sm2
+
+test_expect_success 'multiple submodules' "
+	git diff-index -p --submodule=log HEAD >actual &&
+	cat >expected <<-EOF &&
+Submodule sm1 $head6...0000000 (submodule deleted)
+Submodule sm2 0000000...$head7 (new submodule)
+EOF
+	test_cmp expected actual
+"
+
+test_expect_success 'path filter' "
+	git diff-index -p --submodule=log HEAD sm2 >actual &&
+	cat >expected <<-EOF &&
+Submodule sm2 0000000...$head7 (new submodule)
+EOF
+	test_cmp expected actual
+"
+
+commit_file sm2
+test_expect_success 'given commit' "
+	git diff-index -p --submodule=log HEAD^ >actual &&
+	cat >expected <<-EOF &&
+Submodule sm1 $head6...0000000 (submodule deleted)
+Submodule sm2 0000000...$head7 (new submodule)
+EOF
+	test_cmp expected actual
+"
+
+test_expect_success 'given commit --submodule' "
+	git diff-index -p --submodule HEAD^ >actual &&
+	cat >expected <<-EOF &&
+Submodule sm1 $head6...0000000 (submodule deleted)
+Submodule sm2 0000000...$head7 (new submodule)
+EOF
+	test_cmp expected actual
+"
+
+fullhead7=$(cd sm2; git rev-list --max-count=1 $head7)
+
+test_expect_success 'given commit --submodule=short' "
+	git diff-index -p --submodule=short HEAD^ >actual &&
+	cat >expected <<-EOF &&
+diff --git a/sm1 b/sm1
+deleted file mode 160000
+index $head6..0000000
+--- a/sm1
++++ /dev/null
+@@ -1 +0,0 @@
+-Subproject commit $fullhead6
+diff --git a/sm2 b/sm2
+new file mode 160000
+index 0000000..$head7
+--- /dev/null
++++ b/sm2
+@@ -0,0 +1 @@
++Subproject commit $fullhead7
+EOF
+	test_cmp expected actual
+"
+
+test_expect_success 'setup .git file for sm2' '
+	(cd sm2 &&
+	 REAL="$(pwd)/../.real" &&
+	 mv .git "$REAL"
+	 echo "gitdir: $REAL" >.git)
+'
+
+test_expect_success 'diff --submodule with .git file' '
+	git diff --submodule HEAD^ >actual &&
+	cat >expected <<-EOF &&
+Submodule sm1 $head6...0000000 (submodule deleted)
+Submodule sm2 0000000...$head7 (new submodule)
+EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'diff --submodule with objects referenced by alternates' '
+	mkdir sub_alt &&
+	(cd sub_alt &&
+		git init &&
+		echo a >a &&
+		git add a &&
+		git commit -m a
+	) &&
+	mkdir super &&
+	(cd super &&
+		git clone -s ../sub_alt sub &&
+		git init &&
+		git add sub &&
+		git commit -m "sub a"
+	) &&
+	(cd sub_alt &&
+		sha1_before=$(git rev-parse --short HEAD)
+		echo b >b &&
+		git add b &&
+		git commit -m b
+		sha1_after=$(git rev-parse --short HEAD)
+		echo "Submodule sub $sha1_before..$sha1_after:
+  > b" >../expected
+	) &&
+	(cd super &&
+		(cd sub &&
+			git fetch &&
+			git checkout origin/master
+		) &&
+		git diff --submodule > ../actual
+	)
+	test_cmp expected actual
+'
+
+test_done
diff --git a/t/t4042-diff-textconv-caching.sh b/t/t4042-diff-textconv-caching.sh
new file mode 100755
index 0000000..91f8198
--- /dev/null
+++ b/t/t4042-diff-textconv-caching.sh
@@ -0,0 +1,109 @@
+#!/bin/sh
+
+test_description='test textconv caching'
+. ./test-lib.sh
+
+cat >helper <<'EOF'
+#!/bin/sh
+sed 's/^/converted: /' "$@" >helper.out
+cat helper.out
+EOF
+chmod +x helper
+
+test_expect_success 'setup' '
+	echo foo content 1 >foo.bin &&
+	echo bar content 1 >bar.bin &&
+	git add . &&
+	git commit -m one &&
+	echo foo content 2 >foo.bin &&
+	echo bar content 2 >bar.bin &&
+	git commit -a -m two &&
+	echo "*.bin diff=magic" >.gitattributes &&
+	git config diff.magic.textconv ./helper &&
+	git config diff.magic.cachetextconv true
+'
+
+cat >expect <<EOF
+diff --git a/bar.bin b/bar.bin
+index fcf9166..28283d5 100644
+--- a/bar.bin
++++ b/bar.bin
+@@ -1 +1 @@
+-converted: bar content 1
++converted: bar content 2
+diff --git a/foo.bin b/foo.bin
+index d5b9fe3..1345db2 100644
+--- a/foo.bin
++++ b/foo.bin
+@@ -1 +1 @@
+-converted: foo content 1
++converted: foo content 2
+EOF
+
+test_expect_success 'first textconv works' '
+	git diff HEAD^ HEAD >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'cached textconv produces same output' '
+	git diff HEAD^ HEAD >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'cached textconv does not run helper' '
+	rm -f helper.out &&
+	git diff HEAD^ HEAD >actual &&
+	test_cmp expect actual &&
+	! test -r helper.out
+'
+
+cat >expect <<EOF
+diff --git a/bar.bin b/bar.bin
+index fcf9166..28283d5 100644
+--- a/bar.bin
++++ b/bar.bin
+@@ -1,2 +1,2 @@
+ converted: other
+-converted: bar content 1
++converted: bar content 2
+diff --git a/foo.bin b/foo.bin
+index d5b9fe3..1345db2 100644
+--- a/foo.bin
++++ b/foo.bin
+@@ -1,2 +1,2 @@
+ converted: other
+-converted: foo content 1
++converted: foo content 2
+EOF
+test_expect_success 'changing textconv invalidates cache' '
+	echo other >other &&
+	git config diff.magic.textconv "./helper other" &&
+	git diff HEAD^ HEAD >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<EOF
+diff --git a/bar.bin b/bar.bin
+index fcf9166..28283d5 100644
+--- a/bar.bin
++++ b/bar.bin
+@@ -1,2 +1,2 @@
+ converted: other
+-converted: bar content 1
++converted: bar content 2
+diff --git a/foo.bin b/foo.bin
+index d5b9fe3..1345db2 100644
+--- a/foo.bin
++++ b/foo.bin
+@@ -1 +1 @@
+-converted: foo content 1
++converted: foo content 2
+EOF
+test_expect_success 'switching diff driver produces correct results' '
+	git config diff.moremagic.textconv ./helper &&
+	echo foo.bin diff=moremagic >>.gitattributes &&
+	git diff HEAD^ HEAD >actual &&
+	test_cmp expect actual
+'
+
+test_done
diff --git a/t/t4043-diff-rename-binary.sh b/t/t4043-diff-rename-binary.sh
new file mode 100755
index 0000000..2a2cf91
--- /dev/null
+++ b/t/t4043-diff-rename-binary.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Jakub Narebski, Christian Couder
+#
+
+test_description='Move a binary file'
+
+. ./test-lib.sh
+
+
+test_expect_success 'prepare repository' '
+	git init &&
+	echo foo > foo &&
+	echo "barQ" | q_to_nul > bar &&
+	git add . &&
+	git commit -m "Initial commit"
+'
+
+test_expect_success 'move the files into a "sub" directory' '
+	mkdir sub &&
+	git mv bar foo sub/ &&
+	git commit -m "Moved to sub/"
+'
+
+cat > expected <<\EOF
+-	-	bar => sub/bar
+0	0	foo => sub/foo
+
+diff --git a/bar b/sub/bar
+similarity index 100%
+rename from bar
+rename to sub/bar
+diff --git a/foo b/sub/foo
+similarity index 100%
+rename from foo
+rename to sub/foo
+EOF
+
+test_expect_success 'git show -C -C report renames' '
+	git show -C -C --raw --binary --numstat >patch-with-stat &&
+	tail -n 11 patch-with-stat >current &&
+	test_cmp expected current
+'
+
+test_done
diff --git a/t/t4044-diff-index-unique-abbrev.sh b/t/t4044-diff-index-unique-abbrev.sh
new file mode 100755
index 0000000..d5ce72b
--- /dev/null
+++ b/t/t4044-diff-index-unique-abbrev.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+test_description='test unique sha1 abbreviation on "index from..to" line'
+. ./test-lib.sh
+
+cat >expect_initial <<EOF
+100644 blob 51d2738463ea4ca66f8691c91e33ce64b7d41bb1	foo
+EOF
+
+cat >expect_update <<EOF
+100644 blob 51d2738efb4ad8a1e40bed839ab8e116f0a15e47	foo
+EOF
+
+test_expect_success 'setup' '
+	echo 4827 > foo &&
+	git add foo &&
+	git commit -m "initial" &&
+	git cat-file -p HEAD: > actual &&
+	test_cmp expect_initial actual &&
+	echo 11742 > foo &&
+	git commit -a -m "update" &&
+	git cat-file -p HEAD: > actual &&
+	test_cmp expect_update actual
+'
+
+cat >expect <<EOF
+index 51d27384..51d2738e 100644
+EOF
+
+test_expect_success 'diff does not produce ambiguous index line' '
+	git diff HEAD^..HEAD | grep index > actual &&
+	test_cmp expect actual
+'
+
+test_done
diff --git a/t/t4045-diff-relative.sh b/t/t4045-diff-relative.sh
new file mode 100755
index 0000000..3950f50
--- /dev/null
+++ b/t/t4045-diff-relative.sh
@@ -0,0 +1,73 @@
+#!/bin/sh
+
+test_description='diff --relative tests'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	git commit --allow-empty -m empty &&
+	echo content >file1 &&
+	mkdir subdir &&
+	echo other content >subdir/file2 &&
+	git add . &&
+	git commit -m one
+'
+
+check_diff() {
+expect=$1; shift
+cat >expected <<EOF
+diff --git a/$expect b/$expect
+new file mode 100644
+index 0000000..25c05ef
+--- /dev/null
++++ b/$expect
+@@ -0,0 +1 @@
++other content
+EOF
+test_expect_success "-p $*" "
+	git diff -p $* HEAD^ >actual &&
+	test_cmp expected actual
+"
+}
+
+check_numstat() {
+expect=$1; shift
+cat >expected <<EOF
+1	0	$expect
+EOF
+test_expect_success "--numstat $*" "
+	echo '1	0	$expect' >expected &&
+	git diff --numstat $* HEAD^ >actual &&
+	test_cmp expected actual
+"
+}
+
+check_stat() {
+expect=$1; shift
+cat >expected <<EOF
+ $expect | 1 +
+ 1 file changed, 1 insertion(+)
+EOF
+test_expect_success "--stat $*" "
+	git diff --stat $* HEAD^ >actual &&
+	test_i18ncmp expected actual
+"
+}
+
+check_raw() {
+expect=$1; shift
+cat >expected <<EOF
+:000000 100644 0000000000000000000000000000000000000000 25c05ef3639d2d270e7fe765a67668f098092bc5 A	$expect
+EOF
+test_expect_success "--raw $*" "
+	git diff --no-abbrev --raw $* HEAD^ >actual &&
+	test_cmp expected actual
+"
+}
+
+for type in diff numstat stat raw; do
+	check_$type file2 --relative=subdir/
+	check_$type file2 --relative=subdir
+	check_$type dir/file2 --relative=sub
+done
+
+test_done
diff --git a/t/t4046-diff-unmerged.sh b/t/t4046-diff-unmerged.sh
new file mode 100755
index 0000000..25d50a6
--- /dev/null
+++ b/t/t4046-diff-unmerged.sh
@@ -0,0 +1,87 @@
+#!/bin/sh
+
+test_description='diff with unmerged index entries'
+. ./test-lib.sh
+
+test_expect_success setup '
+	for i in 0 1 2 3
+	do
+		blob=$(echo $i | git hash-object --stdin) &&
+		eval "blob$i=$blob" &&
+		eval "m$i=\"100644 \$blob$i $i\"" || break
+	done &&
+	paths= &&
+	for b in o x
+	do
+		for o in o x
+		do
+			for t in o x
+			do
+				path="$b$o$t" &&
+				case "$path" in ooo) continue ;; esac
+				paths="$paths$path " &&
+				p="	$path" &&
+				case "$b" in x) echo "$m1$p" ;; esac &&
+				case "$o" in x) echo "$m2$p" ;; esac &&
+				case "$t" in x) echo "$m3$p" ;; esac ||
+				break
+			done || break
+		done || break
+	done >ls-files-s.expect &&
+	git update-index --index-info <ls-files-s.expect &&
+	git ls-files -s >ls-files-s.actual &&
+	test_cmp ls-files-s.expect ls-files-s.actual
+'
+
+test_expect_success 'diff-files -0' '
+	for path in $paths
+	do
+		>"$path" &&
+		echo ":000000 100644 $_z40 $_z40 U	$path"
+	done >diff-files-0.expect &&
+	git diff-files -0 >diff-files-0.actual &&
+	test_cmp diff-files-0.expect diff-files-0.actual
+'
+
+test_expect_success 'diff-files -1' '
+	for path in $paths
+	do
+		>"$path" &&
+		echo ":000000 100644 $_z40 $_z40 U	$path" &&
+		case "$path" in
+		x??) echo ":100644 100644 $blob1 $_z40 M	$path"
+		esac
+	done >diff-files-1.expect &&
+	git diff-files -1 >diff-files-1.actual &&
+	test_cmp diff-files-1.expect diff-files-1.actual
+'
+
+test_expect_success 'diff-files -2' '
+	for path in $paths
+	do
+		>"$path" &&
+		echo ":000000 100644 $_z40 $_z40 U	$path" &&
+		case "$path" in
+		?x?) echo ":100644 100644 $blob2 $_z40 M	$path"
+		esac
+	done >diff-files-2.expect &&
+	git diff-files -2 >diff-files-2.actual &&
+	test_cmp diff-files-2.expect diff-files-2.actual &&
+	git diff-files >diff-files-default-2.actual &&
+	test_cmp diff-files-2.expect diff-files-default-2.actual
+'
+
+test_expect_success 'diff-files -3' '
+	for path in $paths
+	do
+		>"$path" &&
+		echo ":000000 100644 $_z40 $_z40 U	$path" &&
+		case "$path" in
+		??x) echo ":100644 100644 $blob3 $_z40 M	$path"
+		esac
+	done >diff-files-3.expect &&
+	git diff-files -3 >diff-files-3.actual &&
+	test_cmp diff-files-3.expect diff-files-3.actual
+'
+
+test_done
diff --git a/t/t4047-diff-dirstat.sh b/t/t4047-diff-dirstat.sh
new file mode 100755
index 0000000..ed7e093
--- /dev/null
+++ b/t/t4047-diff-dirstat.sh
@@ -0,0 +1,976 @@
+#!/bin/sh
+
+test_description='diff --dirstat tests'
+. ./test-lib.sh
+
+# set up two commits where the second commit has these files
+# (10 lines in each file):
+#
+#   unchanged/text           (unchanged from 1st commit)
+#   changed/text             (changed 1st line)
+#   rearranged/text          (swapped 1st and 2nd line)
+#   dst/copy/unchanged/text  (copied from src/copy/unchanged/text, unchanged)
+#   dst/copy/changed/text    (copied from src/copy/changed/text, changed)
+#   dst/copy/rearranged/text (copied from src/copy/rearranged/text, rearranged)
+#   dst/move/unchanged/text  (moved from src/move/unchanged/text, unchanged)
+#   dst/move/changed/text    (moved from src/move/changed/text, changed)
+#   dst/move/rearranged/text (moved from src/move/rearranged/text, rearranged)
+
+test_expect_success 'setup' '
+	mkdir unchanged &&
+	mkdir changed &&
+	mkdir rearranged &&
+	mkdir src &&
+	mkdir src/copy &&
+	mkdir src/copy/unchanged &&
+	mkdir src/copy/changed &&
+	mkdir src/copy/rearranged &&
+	mkdir src/move &&
+	mkdir src/move/unchanged &&
+	mkdir src/move/changed &&
+	mkdir src/move/rearranged &&
+	cat <<EOF >unchanged/text &&
+unchanged       line #0
+unchanged       line #1
+unchanged       line #2
+unchanged       line #3
+unchanged       line #4
+unchanged       line #5
+unchanged       line #6
+unchanged       line #7
+unchanged       line #8
+unchanged       line #9
+EOF
+	cat <<EOF >changed/text &&
+changed         line #0
+changed         line #1
+changed         line #2
+changed         line #3
+changed         line #4
+changed         line #5
+changed         line #6
+changed         line #7
+changed         line #8
+changed         line #9
+EOF
+	cat <<EOF >rearranged/text &&
+rearranged      line #0
+rearranged      line #1
+rearranged      line #2
+rearranged      line #3
+rearranged      line #4
+rearranged      line #5
+rearranged      line #6
+rearranged      line #7
+rearranged      line #8
+rearranged      line #9
+EOF
+	cat <<EOF >src/copy/unchanged/text &&
+copy  unchanged line #0
+copy  unchanged line #1
+copy  unchanged line #2
+copy  unchanged line #3
+copy  unchanged line #4
+copy  unchanged line #5
+copy  unchanged line #6
+copy  unchanged line #7
+copy  unchanged line #8
+copy  unchanged line #9
+EOF
+	cat <<EOF >src/copy/changed/text &&
+copy    changed line #0
+copy    changed line #1
+copy    changed line #2
+copy    changed line #3
+copy    changed line #4
+copy    changed line #5
+copy    changed line #6
+copy    changed line #7
+copy    changed line #8
+copy    changed line #9
+EOF
+	cat <<EOF >src/copy/rearranged/text &&
+copy rearranged line #0
+copy rearranged line #1
+copy rearranged line #2
+copy rearranged line #3
+copy rearranged line #4
+copy rearranged line #5
+copy rearranged line #6
+copy rearranged line #7
+copy rearranged line #8
+copy rearranged line #9
+EOF
+	cat <<EOF >src/move/unchanged/text &&
+move  unchanged line #0
+move  unchanged line #1
+move  unchanged line #2
+move  unchanged line #3
+move  unchanged line #4
+move  unchanged line #5
+move  unchanged line #6
+move  unchanged line #7
+move  unchanged line #8
+move  unchanged line #9
+EOF
+	cat <<EOF >src/move/changed/text &&
+move    changed line #0
+move    changed line #1
+move    changed line #2
+move    changed line #3
+move    changed line #4
+move    changed line #5
+move    changed line #6
+move    changed line #7
+move    changed line #8
+move    changed line #9
+EOF
+	cat <<EOF >src/move/rearranged/text &&
+move rearranged line #0
+move rearranged line #1
+move rearranged line #2
+move rearranged line #3
+move rearranged line #4
+move rearranged line #5
+move rearranged line #6
+move rearranged line #7
+move rearranged line #8
+move rearranged line #9
+EOF
+	git add . &&
+	git commit -m "initial" &&
+	mkdir dst &&
+	mkdir dst/copy &&
+	mkdir dst/copy/unchanged &&
+	mkdir dst/copy/changed &&
+	mkdir dst/copy/rearranged &&
+	mkdir dst/move &&
+	mkdir dst/move/unchanged &&
+	mkdir dst/move/changed &&
+	mkdir dst/move/rearranged &&
+	cat <<EOF >changed/text &&
+CHANGED XXXXXXX line #0
+changed         line #1
+changed         line #2
+changed         line #3
+changed         line #4
+changed         line #5
+changed         line #6
+changed         line #7
+changed         line #8
+changed         line #9
+EOF
+	cat <<EOF >rearranged/text &&
+rearranged      line #1
+rearranged      line #0
+rearranged      line #2
+rearranged      line #3
+rearranged      line #4
+rearranged      line #5
+rearranged      line #6
+rearranged      line #7
+rearranged      line #8
+rearranged      line #9
+EOF
+	cat <<EOF >dst/copy/unchanged/text &&
+copy  unchanged line #0
+copy  unchanged line #1
+copy  unchanged line #2
+copy  unchanged line #3
+copy  unchanged line #4
+copy  unchanged line #5
+copy  unchanged line #6
+copy  unchanged line #7
+copy  unchanged line #8
+copy  unchanged line #9
+EOF
+	cat <<EOF >dst/copy/changed/text &&
+copy XXXCHANGED line #0
+copy    changed line #1
+copy    changed line #2
+copy    changed line #3
+copy    changed line #4
+copy    changed line #5
+copy    changed line #6
+copy    changed line #7
+copy    changed line #8
+copy    changed line #9
+EOF
+	cat <<EOF >dst/copy/rearranged/text &&
+copy rearranged line #1
+copy rearranged line #0
+copy rearranged line #2
+copy rearranged line #3
+copy rearranged line #4
+copy rearranged line #5
+copy rearranged line #6
+copy rearranged line #7
+copy rearranged line #8
+copy rearranged line #9
+EOF
+	cat <<EOF >dst/move/unchanged/text &&
+move  unchanged line #0
+move  unchanged line #1
+move  unchanged line #2
+move  unchanged line #3
+move  unchanged line #4
+move  unchanged line #5
+move  unchanged line #6
+move  unchanged line #7
+move  unchanged line #8
+move  unchanged line #9
+EOF
+	cat <<EOF >dst/move/changed/text &&
+move XXXCHANGED line #0
+move    changed line #1
+move    changed line #2
+move    changed line #3
+move    changed line #4
+move    changed line #5
+move    changed line #6
+move    changed line #7
+move    changed line #8
+move    changed line #9
+EOF
+	cat <<EOF >dst/move/rearranged/text &&
+move rearranged line #1
+move rearranged line #0
+move rearranged line #2
+move rearranged line #3
+move rearranged line #4
+move rearranged line #5
+move rearranged line #6
+move rearranged line #7
+move rearranged line #8
+move rearranged line #9
+EOF
+	git add . &&
+	git rm -r src/move/unchanged &&
+	git rm -r src/move/changed &&
+	git rm -r src/move/rearranged &&
+	git commit -m "changes"
+'
+
+cat <<EOF >expect_diff_stat
+1	1	changed/text
+10	0	dst/copy/changed/text
+10	0	dst/copy/rearranged/text
+10	0	dst/copy/unchanged/text
+10	0	dst/move/changed/text
+10	0	dst/move/rearranged/text
+10	0	dst/move/unchanged/text
+1	1	rearranged/text
+0	10	src/move/changed/text
+0	10	src/move/rearranged/text
+0	10	src/move/unchanged/text
+EOF
+
+cat <<EOF >expect_diff_stat_M
+1	1	changed/text
+10	0	dst/copy/changed/text
+10	0	dst/copy/rearranged/text
+10	0	dst/copy/unchanged/text
+1	1	{src => dst}/move/changed/text
+1	1	{src => dst}/move/rearranged/text
+0	0	{src => dst}/move/unchanged/text
+1	1	rearranged/text
+EOF
+
+cat <<EOF >expect_diff_stat_CC
+1	1	changed/text
+1	1	{src => dst}/copy/changed/text
+1	1	{src => dst}/copy/rearranged/text
+0	0	{src => dst}/copy/unchanged/text
+1	1	{src => dst}/move/changed/text
+1	1	{src => dst}/move/rearranged/text
+0	0	{src => dst}/move/unchanged/text
+1	1	rearranged/text
+EOF
+
+test_expect_success 'sanity check setup (--numstat)' '
+	git diff --numstat HEAD^..HEAD >actual_diff_stat &&
+	test_cmp expect_diff_stat actual_diff_stat &&
+	git diff --numstat -M HEAD^..HEAD >actual_diff_stat_M &&
+	test_cmp expect_diff_stat_M actual_diff_stat_M &&
+	git diff --numstat -C -C HEAD^..HEAD >actual_diff_stat_CC &&
+	test_cmp expect_diff_stat_CC actual_diff_stat_CC
+'
+
+# changed/text and rearranged/text falls below default 3% threshold
+cat <<EOF >expect_diff_dirstat
+  10.8% dst/copy/changed/
+  10.8% dst/copy/rearranged/
+  10.8% dst/copy/unchanged/
+  10.8% dst/move/changed/
+  10.8% dst/move/rearranged/
+  10.8% dst/move/unchanged/
+  10.8% src/move/changed/
+  10.8% src/move/rearranged/
+  10.8% src/move/unchanged/
+EOF
+
+# rearranged/text falls below default 3% threshold
+cat <<EOF >expect_diff_dirstat_M
+   5.8% changed/
+  29.3% dst/copy/changed/
+  29.3% dst/copy/rearranged/
+  29.3% dst/copy/unchanged/
+   5.8% dst/move/changed/
+EOF
+
+# rearranged/text falls below default 3% threshold
+cat <<EOF >expect_diff_dirstat_CC
+  32.6% changed/
+  32.6% dst/copy/changed/
+  32.6% dst/move/changed/
+EOF
+
+test_expect_success 'various ways to misspell --dirstat' '
+	test_must_fail git show --dirstat10 &&
+	test_must_fail git show --dirstat10,files &&
+	test_must_fail git show -X=20 &&
+	test_must_fail git show -X=20,cumulative
+'
+
+test_expect_success 'vanilla --dirstat' '
+	git diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&
+	test_cmp expect_diff_dirstat actual_diff_dirstat &&
+	git diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&
+	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+	git diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success 'vanilla -X' '
+	git diff -X HEAD^..HEAD >actual_diff_dirstat &&
+	test_cmp expect_diff_dirstat actual_diff_dirstat &&
+	git diff -X -M HEAD^..HEAD >actual_diff_dirstat_M &&
+	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+	git diff -X -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success 'explicit defaults: --dirstat=changes,noncumulative,3' '
+	git diff --dirstat=changes,noncumulative,3 HEAD^..HEAD >actual_diff_dirstat &&
+	test_cmp expect_diff_dirstat actual_diff_dirstat &&
+	git diff --dirstat=changes,noncumulative,3 -M HEAD^..HEAD >actual_diff_dirstat_M &&
+	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+	git diff --dirstat=changes,noncumulative,3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success 'explicit defaults: -Xchanges,noncumulative,3' '
+	git diff -Xchanges,noncumulative,3 HEAD^..HEAD >actual_diff_dirstat &&
+	test_cmp expect_diff_dirstat actual_diff_dirstat &&
+	git diff -Xchanges,noncumulative,3 -M HEAD^..HEAD >actual_diff_dirstat_M &&
+	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+	git diff -Xchanges,noncumulative,3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success 'later options override earlier options:' '
+	git diff --dirstat=files,10,cumulative,changes,noncumulative,3 HEAD^..HEAD >actual_diff_dirstat &&
+	test_cmp expect_diff_dirstat actual_diff_dirstat &&
+	git diff --dirstat=files,10,cumulative,changes,noncumulative,3 -M HEAD^..HEAD >actual_diff_dirstat_M &&
+	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+	git diff --dirstat=files,10,cumulative,changes,noncumulative,3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+	git diff --dirstat=files --dirstat=10 --dirstat=cumulative --dirstat=changes --dirstat=noncumulative -X3 HEAD^..HEAD >actual_diff_dirstat &&
+	test_cmp expect_diff_dirstat actual_diff_dirstat &&
+	git diff --dirstat=files --dirstat=10 --dirstat=cumulative --dirstat=changes --dirstat=noncumulative -X3 -M HEAD^..HEAD >actual_diff_dirstat_M &&
+	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+	git diff --dirstat=files --dirstat=10 --dirstat=cumulative --dirstat=changes --dirstat=noncumulative -X3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success 'non-defaults in config overridden by explicit defaults on command line' '
+	git -c diff.dirstat=files,cumulative,50 diff --dirstat=changes,noncumulative,3 HEAD^..HEAD >actual_diff_dirstat &&
+	test_cmp expect_diff_dirstat actual_diff_dirstat &&
+	git -c diff.dirstat=files,cumulative,50 diff --dirstat=changes,noncumulative,3 -M HEAD^..HEAD >actual_diff_dirstat_M &&
+	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+	git -c diff.dirstat=files,cumulative,50 diff --dirstat=changes,noncumulative,3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+cat <<EOF >expect_diff_dirstat
+   2.1% changed/
+  10.8% dst/copy/changed/
+  10.8% dst/copy/rearranged/
+  10.8% dst/copy/unchanged/
+  10.8% dst/move/changed/
+  10.8% dst/move/rearranged/
+  10.8% dst/move/unchanged/
+   0.0% rearranged/
+  10.8% src/move/changed/
+  10.8% src/move/rearranged/
+  10.8% src/move/unchanged/
+EOF
+
+cat <<EOF >expect_diff_dirstat_M
+   5.8% changed/
+  29.3% dst/copy/changed/
+  29.3% dst/copy/rearranged/
+  29.3% dst/copy/unchanged/
+   5.8% dst/move/changed/
+   0.1% dst/move/rearranged/
+   0.1% rearranged/
+EOF
+
+cat <<EOF >expect_diff_dirstat_CC
+  32.6% changed/
+  32.6% dst/copy/changed/
+   0.6% dst/copy/rearranged/
+  32.6% dst/move/changed/
+   0.6% dst/move/rearranged/
+   0.6% rearranged/
+EOF
+
+test_expect_success '--dirstat=0' '
+	git diff --dirstat=0 HEAD^..HEAD >actual_diff_dirstat &&
+	test_cmp expect_diff_dirstat actual_diff_dirstat &&
+	git diff --dirstat=0 -M HEAD^..HEAD >actual_diff_dirstat_M &&
+	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+	git diff --dirstat=0 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success '-X0' '
+	git diff -X0 HEAD^..HEAD >actual_diff_dirstat &&
+	test_cmp expect_diff_dirstat actual_diff_dirstat &&
+	git diff -X0 -M HEAD^..HEAD >actual_diff_dirstat_M &&
+	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+	git diff -X0 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success 'diff.dirstat=0' '
+	git -c diff.dirstat=0 diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&
+	test_cmp expect_diff_dirstat actual_diff_dirstat &&
+	git -c diff.dirstat=0 diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&
+	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+	git -c diff.dirstat=0 diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+cat <<EOF >expect_diff_dirstat
+   2.1% changed/
+  10.8% dst/copy/changed/
+  10.8% dst/copy/rearranged/
+  10.8% dst/copy/unchanged/
+  32.5% dst/copy/
+  10.8% dst/move/changed/
+  10.8% dst/move/rearranged/
+  10.8% dst/move/unchanged/
+  32.5% dst/move/
+  65.1% dst/
+   0.0% rearranged/
+  10.8% src/move/changed/
+  10.8% src/move/rearranged/
+  10.8% src/move/unchanged/
+  32.5% src/move/
+EOF
+
+cat <<EOF >expect_diff_dirstat_M
+   5.8% changed/
+  29.3% dst/copy/changed/
+  29.3% dst/copy/rearranged/
+  29.3% dst/copy/unchanged/
+  88.0% dst/copy/
+   5.8% dst/move/changed/
+   0.1% dst/move/rearranged/
+   5.9% dst/move/
+  94.0% dst/
+   0.1% rearranged/
+EOF
+
+cat <<EOF >expect_diff_dirstat_CC
+  32.6% changed/
+  32.6% dst/copy/changed/
+   0.6% dst/copy/rearranged/
+  33.3% dst/copy/
+  32.6% dst/move/changed/
+   0.6% dst/move/rearranged/
+  33.3% dst/move/
+  66.6% dst/
+   0.6% rearranged/
+EOF
+
+test_expect_success '--dirstat=0 --cumulative' '
+	git diff --dirstat=0 --cumulative HEAD^..HEAD >actual_diff_dirstat &&
+	test_cmp expect_diff_dirstat actual_diff_dirstat &&
+	git diff --dirstat=0 --cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&
+	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+	git diff --dirstat=0 --cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success '--dirstat=0,cumulative' '
+	git diff --dirstat=0,cumulative HEAD^..HEAD >actual_diff_dirstat &&
+	test_cmp expect_diff_dirstat actual_diff_dirstat &&
+	git diff --dirstat=0,cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&
+	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+	git diff --dirstat=0,cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success '-X0,cumulative' '
+	git diff -X0,cumulative HEAD^..HEAD >actual_diff_dirstat &&
+	test_cmp expect_diff_dirstat actual_diff_dirstat &&
+	git diff -X0,cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&
+	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+	git diff -X0,cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success 'diff.dirstat=0,cumulative' '
+	git -c diff.dirstat=0,cumulative diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&
+	test_cmp expect_diff_dirstat actual_diff_dirstat &&
+	git -c diff.dirstat=0,cumulative diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&
+	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+	git -c diff.dirstat=0,cumulative diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success 'diff.dirstat=0 & --dirstat=cumulative' '
+	git -c diff.dirstat=0 diff --dirstat=cumulative HEAD^..HEAD >actual_diff_dirstat &&
+	test_cmp expect_diff_dirstat actual_diff_dirstat &&
+	git -c diff.dirstat=0 diff --dirstat=cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&
+	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+	git -c diff.dirstat=0 diff --dirstat=cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+cat <<EOF >expect_diff_dirstat
+   9.0% changed/
+   9.0% dst/copy/changed/
+   9.0% dst/copy/rearranged/
+   9.0% dst/copy/unchanged/
+   9.0% dst/move/changed/
+   9.0% dst/move/rearranged/
+   9.0% dst/move/unchanged/
+   9.0% rearranged/
+   9.0% src/move/changed/
+   9.0% src/move/rearranged/
+   9.0% src/move/unchanged/
+EOF
+
+cat <<EOF >expect_diff_dirstat_M
+  14.2% changed/
+  14.2% dst/copy/changed/
+  14.2% dst/copy/rearranged/
+  14.2% dst/copy/unchanged/
+  14.2% dst/move/changed/
+  14.2% dst/move/rearranged/
+  14.2% rearranged/
+EOF
+
+cat <<EOF >expect_diff_dirstat_CC
+  16.6% changed/
+  16.6% dst/copy/changed/
+  16.6% dst/copy/rearranged/
+  16.6% dst/move/changed/
+  16.6% dst/move/rearranged/
+  16.6% rearranged/
+EOF
+
+test_expect_success '--dirstat-by-file' '
+	git diff --dirstat-by-file HEAD^..HEAD >actual_diff_dirstat &&
+	test_cmp expect_diff_dirstat actual_diff_dirstat &&
+	git diff --dirstat-by-file -M HEAD^..HEAD >actual_diff_dirstat_M &&
+	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+	git diff --dirstat-by-file -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success '--dirstat=files' '
+	git diff --dirstat=files HEAD^..HEAD >actual_diff_dirstat &&
+	test_cmp expect_diff_dirstat actual_diff_dirstat &&
+	git diff --dirstat=files -M HEAD^..HEAD >actual_diff_dirstat_M &&
+	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+	git diff --dirstat=files -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success 'diff.dirstat=files' '
+	git -c diff.dirstat=files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&
+	test_cmp expect_diff_dirstat actual_diff_dirstat &&
+	git -c diff.dirstat=files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&
+	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+	git -c diff.dirstat=files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+cat <<EOF >expect_diff_dirstat
+  27.2% dst/copy/
+  27.2% dst/move/
+  27.2% src/move/
+EOF
+
+cat <<EOF >expect_diff_dirstat_M
+  14.2% changed/
+  14.2% dst/copy/changed/
+  14.2% dst/copy/rearranged/
+  14.2% dst/copy/unchanged/
+  14.2% dst/move/changed/
+  14.2% dst/move/rearranged/
+  14.2% rearranged/
+EOF
+
+cat <<EOF >expect_diff_dirstat_CC
+  16.6% changed/
+  16.6% dst/copy/changed/
+  16.6% dst/copy/rearranged/
+  16.6% dst/move/changed/
+  16.6% dst/move/rearranged/
+  16.6% rearranged/
+EOF
+
+test_expect_success '--dirstat-by-file=10' '
+	git diff --dirstat-by-file=10 HEAD^..HEAD >actual_diff_dirstat &&
+	test_cmp expect_diff_dirstat actual_diff_dirstat &&
+	git diff --dirstat-by-file=10 -M HEAD^..HEAD >actual_diff_dirstat_M &&
+	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+	git diff --dirstat-by-file=10 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success '--dirstat=files,10' '
+	git diff --dirstat=files,10 HEAD^..HEAD >actual_diff_dirstat &&
+	test_cmp expect_diff_dirstat actual_diff_dirstat &&
+	git diff --dirstat=files,10 -M HEAD^..HEAD >actual_diff_dirstat_M &&
+	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+	git diff --dirstat=files,10 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success 'diff.dirstat=10,files' '
+	git -c diff.dirstat=10,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&
+	test_cmp expect_diff_dirstat actual_diff_dirstat &&
+	git -c diff.dirstat=10,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&
+	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+	git -c diff.dirstat=10,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+cat <<EOF >expect_diff_dirstat
+   9.0% changed/
+   9.0% dst/copy/changed/
+   9.0% dst/copy/rearranged/
+   9.0% dst/copy/unchanged/
+  27.2% dst/copy/
+   9.0% dst/move/changed/
+   9.0% dst/move/rearranged/
+   9.0% dst/move/unchanged/
+  27.2% dst/move/
+  54.5% dst/
+   9.0% rearranged/
+   9.0% src/move/changed/
+   9.0% src/move/rearranged/
+   9.0% src/move/unchanged/
+  27.2% src/move/
+EOF
+
+cat <<EOF >expect_diff_dirstat_M
+  14.2% changed/
+  14.2% dst/copy/changed/
+  14.2% dst/copy/rearranged/
+  14.2% dst/copy/unchanged/
+  42.8% dst/copy/
+  14.2% dst/move/changed/
+  14.2% dst/move/rearranged/
+  28.5% dst/move/
+  71.4% dst/
+  14.2% rearranged/
+EOF
+
+cat <<EOF >expect_diff_dirstat_CC
+  16.6% changed/
+  16.6% dst/copy/changed/
+  16.6% dst/copy/rearranged/
+  33.3% dst/copy/
+  16.6% dst/move/changed/
+  16.6% dst/move/rearranged/
+  33.3% dst/move/
+  66.6% dst/
+  16.6% rearranged/
+EOF
+
+test_expect_success '--dirstat-by-file --cumulative' '
+	git diff --dirstat-by-file --cumulative HEAD^..HEAD >actual_diff_dirstat &&
+	test_cmp expect_diff_dirstat actual_diff_dirstat &&
+	git diff --dirstat-by-file --cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&
+	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+	git diff --dirstat-by-file --cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success '--dirstat=files,cumulative' '
+	git diff --dirstat=files,cumulative HEAD^..HEAD >actual_diff_dirstat &&
+	test_cmp expect_diff_dirstat actual_diff_dirstat &&
+	git diff --dirstat=files,cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&
+	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+	git diff --dirstat=files,cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success 'diff.dirstat=cumulative,files' '
+	git -c diff.dirstat=cumulative,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&
+	test_cmp expect_diff_dirstat actual_diff_dirstat &&
+	git -c diff.dirstat=cumulative,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&
+	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+	git -c diff.dirstat=cumulative,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+cat <<EOF >expect_diff_dirstat
+  27.2% dst/copy/
+  27.2% dst/move/
+  54.5% dst/
+  27.2% src/move/
+EOF
+
+cat <<EOF >expect_diff_dirstat_M
+  14.2% changed/
+  14.2% dst/copy/changed/
+  14.2% dst/copy/rearranged/
+  14.2% dst/copy/unchanged/
+  42.8% dst/copy/
+  14.2% dst/move/changed/
+  14.2% dst/move/rearranged/
+  28.5% dst/move/
+  71.4% dst/
+  14.2% rearranged/
+EOF
+
+cat <<EOF >expect_diff_dirstat_CC
+  16.6% changed/
+  16.6% dst/copy/changed/
+  16.6% dst/copy/rearranged/
+  33.3% dst/copy/
+  16.6% dst/move/changed/
+  16.6% dst/move/rearranged/
+  33.3% dst/move/
+  66.6% dst/
+  16.6% rearranged/
+EOF
+
+test_expect_success '--dirstat=files,cumulative,10' '
+	git diff --dirstat=files,cumulative,10 HEAD^..HEAD >actual_diff_dirstat &&
+	test_cmp expect_diff_dirstat actual_diff_dirstat &&
+	git diff --dirstat=files,cumulative,10 -M HEAD^..HEAD >actual_diff_dirstat_M &&
+	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+	git diff --dirstat=files,cumulative,10 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success 'diff.dirstat=10,cumulative,files' '
+	git -c diff.dirstat=10,cumulative,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&
+	test_cmp expect_diff_dirstat actual_diff_dirstat &&
+	git -c diff.dirstat=10,cumulative,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&
+	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+	git -c diff.dirstat=10,cumulative,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+cat <<EOF >expect_diff_dirstat
+  27.2% dst/copy/
+  27.2% dst/move/
+  54.5% dst/
+  27.2% src/move/
+EOF
+
+cat <<EOF >expect_diff_dirstat_M
+  42.8% dst/copy/
+  28.5% dst/move/
+  71.4% dst/
+EOF
+
+cat <<EOF >expect_diff_dirstat_CC
+  33.3% dst/copy/
+  33.3% dst/move/
+  66.6% dst/
+EOF
+
+test_expect_success '--dirstat=files,cumulative,16.7' '
+	git diff --dirstat=files,cumulative,16.7 HEAD^..HEAD >actual_diff_dirstat &&
+	test_cmp expect_diff_dirstat actual_diff_dirstat &&
+	git diff --dirstat=files,cumulative,16.7 -M HEAD^..HEAD >actual_diff_dirstat_M &&
+	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+	git diff --dirstat=files,cumulative,16.7 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success 'diff.dirstat=16.7,cumulative,files' '
+	git -c diff.dirstat=16.7,cumulative,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&
+	test_cmp expect_diff_dirstat actual_diff_dirstat &&
+	git -c diff.dirstat=16.7,cumulative,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&
+	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+	git -c diff.dirstat=16.7,cumulative,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success 'diff.dirstat=16.70,cumulative,files' '
+	git -c diff.dirstat=16.70,cumulative,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&
+	test_cmp expect_diff_dirstat actual_diff_dirstat &&
+	git -c diff.dirstat=16.70,cumulative,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&
+	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+	git -c diff.dirstat=16.70,cumulative,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success '--dirstat=files,cumulative,27.2' '
+	git diff --dirstat=files,cumulative,27.2 HEAD^..HEAD >actual_diff_dirstat &&
+	test_cmp expect_diff_dirstat actual_diff_dirstat &&
+	git diff --dirstat=files,cumulative,27.2 -M HEAD^..HEAD >actual_diff_dirstat_M &&
+	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+	git diff --dirstat=files,cumulative,27.2 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success '--dirstat=files,cumulative,27.09' '
+	git diff --dirstat=files,cumulative,27.09 HEAD^..HEAD >actual_diff_dirstat &&
+	test_cmp expect_diff_dirstat actual_diff_dirstat &&
+	git diff --dirstat=files,cumulative,27.09 -M HEAD^..HEAD >actual_diff_dirstat_M &&
+	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+	git diff --dirstat=files,cumulative,27.09 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+cat <<EOF >expect_diff_dirstat
+  10.6% dst/copy/changed/
+  10.6% dst/copy/rearranged/
+  10.6% dst/copy/unchanged/
+  10.6% dst/move/changed/
+  10.6% dst/move/rearranged/
+  10.6% dst/move/unchanged/
+  10.6% src/move/changed/
+  10.6% src/move/rearranged/
+  10.6% src/move/unchanged/
+EOF
+
+cat <<EOF >expect_diff_dirstat_M
+   5.2% changed/
+  26.3% dst/copy/changed/
+  26.3% dst/copy/rearranged/
+  26.3% dst/copy/unchanged/
+   5.2% dst/move/changed/
+   5.2% dst/move/rearranged/
+   5.2% rearranged/
+EOF
+
+cat <<EOF >expect_diff_dirstat_CC
+  16.6% changed/
+  16.6% dst/copy/changed/
+  16.6% dst/copy/rearranged/
+  16.6% dst/move/changed/
+  16.6% dst/move/rearranged/
+  16.6% rearranged/
+EOF
+
+test_expect_success '--dirstat=lines' '
+	git diff --dirstat=lines HEAD^..HEAD >actual_diff_dirstat &&
+	test_cmp expect_diff_dirstat actual_diff_dirstat &&
+	git diff --dirstat=lines -M HEAD^..HEAD >actual_diff_dirstat_M &&
+	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+	git diff --dirstat=lines -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success 'diff.dirstat=lines' '
+	git -c diff.dirstat=lines diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&
+	test_cmp expect_diff_dirstat actual_diff_dirstat &&
+	git -c diff.dirstat=lines diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&
+	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+	git -c diff.dirstat=lines diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+cat <<EOF >expect_diff_dirstat
+   2.1% changed/
+  10.6% dst/copy/changed/
+  10.6% dst/copy/rearranged/
+  10.6% dst/copy/unchanged/
+  10.6% dst/move/changed/
+  10.6% dst/move/rearranged/
+  10.6% dst/move/unchanged/
+   2.1% rearranged/
+  10.6% src/move/changed/
+  10.6% src/move/rearranged/
+  10.6% src/move/unchanged/
+EOF
+
+cat <<EOF >expect_diff_dirstat_M
+   5.2% changed/
+  26.3% dst/copy/changed/
+  26.3% dst/copy/rearranged/
+  26.3% dst/copy/unchanged/
+   5.2% dst/move/changed/
+   5.2% dst/move/rearranged/
+   5.2% rearranged/
+EOF
+
+cat <<EOF >expect_diff_dirstat_CC
+  16.6% changed/
+  16.6% dst/copy/changed/
+  16.6% dst/copy/rearranged/
+  16.6% dst/move/changed/
+  16.6% dst/move/rearranged/
+  16.6% rearranged/
+EOF
+
+test_expect_success '--dirstat=lines,0' '
+	git diff --dirstat=lines,0 HEAD^..HEAD >actual_diff_dirstat &&
+	test_cmp expect_diff_dirstat actual_diff_dirstat &&
+	git diff --dirstat=lines,0 -M HEAD^..HEAD >actual_diff_dirstat_M &&
+	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+	git diff --dirstat=lines,0 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success 'diff.dirstat=0,lines' '
+	git -c diff.dirstat=0,lines diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&
+	test_cmp expect_diff_dirstat actual_diff_dirstat &&
+	git -c diff.dirstat=0,lines diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&
+	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+	git -c diff.dirstat=0,lines diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+'
+
+test_expect_success '--dirstat=future_param,lines,0 should fail loudly' '
+	test_must_fail git diff --dirstat=future_param,lines,0 HEAD^..HEAD >actual_diff_dirstat 2>actual_error &&
+	test_debug "cat actual_error" &&
+	test_cmp /dev/null actual_diff_dirstat &&
+	test_i18ngrep -q "future_param" actual_error &&
+	test_i18ngrep -q "\--dirstat" actual_error
+'
+
+test_expect_success '--dirstat=dummy1,cumulative,2dummy should report both unrecognized parameters' '
+	test_must_fail git diff --dirstat=dummy1,cumulative,2dummy HEAD^..HEAD >actual_diff_dirstat 2>actual_error &&
+	test_debug "cat actual_error" &&
+	test_cmp /dev/null actual_diff_dirstat &&
+	test_i18ngrep -q "dummy1" actual_error &&
+	test_i18ngrep -q "2dummy" actual_error &&
+	test_i18ngrep -q "\--dirstat" actual_error
+'
+
+test_expect_success 'diff.dirstat=future_param,0,lines should warn, but still work' '
+	git -c diff.dirstat=future_param,0,lines diff --dirstat HEAD^..HEAD >actual_diff_dirstat 2>actual_error &&
+	test_debug "cat actual_error" &&
+	test_cmp expect_diff_dirstat actual_diff_dirstat &&
+	test_i18ngrep -q "future_param" actual_error &&
+	test_i18ngrep -q "diff\\.dirstat" actual_error &&
+
+	git -c diff.dirstat=future_param,0,lines diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M 2>actual_error &&
+	test_debug "cat actual_error" &&
+	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
+	test_i18ngrep -q "future_param" actual_error &&
+	test_i18ngrep -q "diff\\.dirstat" actual_error &&
+
+	git -c diff.dirstat=future_param,0,lines diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC 2>actual_error &&
+	test_debug "cat actual_error" &&
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC &&
+	test_i18ngrep -q "future_param" actual_error &&
+	test_i18ngrep -q "diff\\.dirstat" actual_error
+'
+
+test_done
diff --git a/t/t4048-diff-combined-binary.sh b/t/t4048-diff-combined-binary.sh
new file mode 100755
index 0000000..87a8949
--- /dev/null
+++ b/t/t4048-diff-combined-binary.sh
@@ -0,0 +1,212 @@
+#!/bin/sh
+
+test_description='combined and merge diff handle binary files and textconv'
+. ./test-lib.sh
+
+test_expect_success 'setup binary merge conflict' '
+	echo oneQ1 | q_to_nul >binary &&
+	git add binary &&
+	git commit -m one &&
+	echo twoQ2 | q_to_nul >binary &&
+	git commit -a -m two &&
+	git checkout -b branch-binary HEAD^ &&
+	echo threeQ3 | q_to_nul >binary &&
+	git commit -a -m three &&
+	test_must_fail git merge master &&
+	echo resolvedQhooray | q_to_nul >binary &&
+	git commit -a -m resolved
+'
+
+cat >expect <<'EOF'
+resolved
+
+diff --git a/binary b/binary
+index 7ea6ded..9563691 100644
+Binary files a/binary and b/binary differ
+resolved
+
+diff --git a/binary b/binary
+index 6197570..9563691 100644
+Binary files a/binary and b/binary differ
+EOF
+test_expect_success 'diff -m indicates binary-ness' '
+	git show --format=%s -m >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+resolved
+
+diff --combined binary
+index 7ea6ded,6197570..9563691
+Binary files differ
+EOF
+test_expect_success 'diff -c indicates binary-ness' '
+	git show --format=%s -c >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+resolved
+
+diff --cc binary
+index 7ea6ded,6197570..9563691
+Binary files differ
+EOF
+test_expect_success 'diff --cc indicates binary-ness' '
+	git show --format=%s --cc >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'setup non-binary with binary attribute' '
+	git checkout master &&
+	test_commit one text &&
+	test_commit two text &&
+	git checkout -b branch-text HEAD^ &&
+	test_commit three text &&
+	test_must_fail git merge master &&
+	test_commit resolved text &&
+	echo text -diff >.gitattributes
+'
+
+cat >expect <<'EOF'
+resolved
+
+diff --git a/text b/text
+index 2bdf67a..2ab19ae 100644
+Binary files a/text and b/text differ
+resolved
+
+diff --git a/text b/text
+index f719efd..2ab19ae 100644
+Binary files a/text and b/text differ
+EOF
+test_expect_success 'diff -m respects binary attribute' '
+	git show --format=%s -m >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+resolved
+
+diff --combined text
+index 2bdf67a,f719efd..2ab19ae
+Binary files differ
+EOF
+test_expect_success 'diff -c respects binary attribute' '
+	git show --format=%s -c >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+resolved
+
+diff --cc text
+index 2bdf67a,f719efd..2ab19ae
+Binary files differ
+EOF
+test_expect_success 'diff --cc respects binary attribute' '
+	git show --format=%s --cc >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'setup textconv attribute' '
+	echo "text diff=upcase" >.gitattributes &&
+	git config diff.upcase.textconv "tr a-z A-Z <"
+'
+
+cat >expect <<'EOF'
+resolved
+
+diff --git a/text b/text
+index 2bdf67a..2ab19ae 100644
+--- a/text
++++ b/text
+@@ -1 +1 @@
+-THREE
++RESOLVED
+resolved
+
+diff --git a/text b/text
+index f719efd..2ab19ae 100644
+--- a/text
++++ b/text
+@@ -1 +1 @@
+-TWO
++RESOLVED
+EOF
+test_expect_success 'diff -m respects textconv attribute' '
+	git show --format=%s -m >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+resolved
+
+diff --combined text
+index 2bdf67a,f719efd..2ab19ae
+--- a/text
++++ b/text
+@@@ -1,1 -1,1 +1,1 @@@
+- THREE
+ -TWO
+++RESOLVED
+EOF
+test_expect_success 'diff -c respects textconv attribute' '
+	git show --format=%s -c >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+resolved
+
+diff --cc text
+index 2bdf67a,f719efd..2ab19ae
+--- a/text
++++ b/text
+@@@ -1,1 -1,1 +1,1 @@@
+- THREE
+ -TWO
+++RESOLVED
+EOF
+test_expect_success 'diff --cc respects textconv attribute' '
+	git show --format=%s --cc >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+diff --combined text
+index 2bdf67a,f719efd..2ab19ae
+--- a/text
++++ b/text
+@@@ -1,1 -1,1 +1,1 @@@
+- three
+ -two
+++resolved
+EOF
+test_expect_success 'diff-tree plumbing does not respect textconv' '
+	git diff-tree HEAD -c -p >full &&
+	tail -n +2 full >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+diff --cc text
+index 2bdf67a,f719efd..0000000
+--- a/text
++++ b/text
+@@@ -1,1 -1,1 +1,5 @@@
+++<<<<<<< HEAD
+ +THREE
+++=======
++ TWO
+++>>>>>>> MASTER
+EOF
+test_expect_success 'diff --cc respects textconv on worktree file' '
+	git reset --hard HEAD^ &&
+	test_must_fail git merge master &&
+	git diff >actual &&
+	test_cmp expect actual
+'
+
+test_done
diff --git a/t/t4049-diff-stat-count.sh b/t/t4049-diff-stat-count.sh
new file mode 100755
index 0000000..b41eb61
--- /dev/null
+++ b/t/t4049-diff-stat-count.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+# Copyright (c) 2011, Google Inc.
+
+test_description='diff --stat-count'
+. ./test-lib.sh
+
+test_expect_success setup '
+	>a &&
+	>b &&
+	>c &&
+	>d &&
+	git add a b c d &&
+	chmod +x c d &&
+	echo a >a &&
+	echo b >b &&
+	cat >expect <<-\EOF
+	 a | 1 +
+	 b | 1 +
+	 2 files changed, 2 insertions(+)
+	EOF
+	git diff --stat --stat-count=2 >actual &&
+	test_i18ncmp expect actual
+'
+
+test_done
diff --git a/t/t4050-diff-histogram.sh b/t/t4050-diff-histogram.sh
new file mode 100755
index 0000000..fd3e86a
--- /dev/null
+++ b/t/t4050-diff-histogram.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+test_description='histogram diff algorithm'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-diff-alternative.sh
+
+test_diff_frobnitz "histogram"
+
+test_diff_unique "histogram"
+
+test_done
diff --git a/t/t4051-diff-function-context.sh b/t/t4051-diff-function-context.sh
new file mode 100755
index 0000000..001d678
--- /dev/null
+++ b/t/t4051-diff-function-context.sh
@@ -0,0 +1,92 @@
+#!/bin/sh
+
+test_description='diff function context'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh
+
+
+cat <<\EOF >hello.c
+#include <stdio.h>
+
+static int a(void)
+{
+	/*
+	 * Dummy.
+	 */
+}
+
+static int hello_world(void)
+{
+	/* Classic. */
+	printf("Hello world.\n");
+
+	/* Success! */
+	return 0;
+}
+static int b(void)
+{
+	/*
+	 * Dummy, too.
+	 */
+}
+
+int main(int argc, char **argv)
+{
+	a();
+	b();
+	return hello_world();
+}
+EOF
+
+test_expect_success 'setup' '
+	git add hello.c &&
+	test_tick &&
+	git commit -m initial &&
+
+	grep -v Classic <hello.c >hello.c.new &&
+	mv hello.c.new hello.c
+'
+
+cat <<\EOF >expected
+diff --git a/hello.c b/hello.c
+--- a/hello.c
++++ b/hello.c
+@@ -10,8 +10,7 @@ static int a(void)
+ static int hello_world(void)
+ {
+-	/* Classic. */
+ 	printf("Hello world.\n");
+ 
+ 	/* Success! */
+ 	return 0;
+ }
+EOF
+
+test_expect_success 'diff -U0 -W' '
+	git diff -U0 -W >actual &&
+	compare_diff_patch actual expected
+'
+
+cat <<\EOF >expected
+diff --git a/hello.c b/hello.c
+--- a/hello.c
++++ b/hello.c
+@@ -9,9 +9,8 @@ static int a(void)
+ 
+ static int hello_world(void)
+ {
+-	/* Classic. */
+ 	printf("Hello world.\n");
+ 
+ 	/* Success! */
+ 	return 0;
+ }
+EOF
+
+test_expect_success 'diff -W' '
+	git diff -W >actual &&
+	compare_diff_patch actual expected
+'
+
+test_done
diff --git a/t/t4052-stat-output.sh b/t/t4052-stat-output.sh
new file mode 100755
index 0000000..b68afef
--- /dev/null
+++ b/t/t4052-stat-output.sh
@@ -0,0 +1,336 @@
+#!/bin/sh
+#
+# Copyright (c) 2012 Zbigniew Jędrzejewski-Szmek
+#
+
+test_description='test --stat output of various commands'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
+
+# 120 character name
+name=aaaaaaaaaa
+name=$name$name$name$name$name$name$name$name$name$name$name$name
+test_expect_success 'preparation' '
+	>"$name" &&
+	git add "$name" &&
+	git commit -m message &&
+	echo a >"$name" &&
+	git commit -m message "$name"
+'
+
+while read cmd args
+do
+	cat >expect <<-'EOF'
+	 ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 +
+	EOF
+	test_expect_success "$cmd: small change with long name gives more space to the name" '
+		git $cmd $args >output &&
+		grep " | " output >actual &&
+		test_cmp expect actual
+	'
+
+	cat >expect <<-'EOF'
+	 ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 +
+	EOF
+	test_expect_success "$cmd --stat=width: a long name is given more room when the bar is short" '
+		git $cmd $args --stat=40 >output &&
+		grep " | " output >actual &&
+		test_cmp expect actual
+	'
+
+	test_expect_success "$cmd --stat-width=width with long name" '
+		git $cmd $args --stat-width=40 >output &&
+		grep " | " output >actual &&
+		test_cmp expect actual
+	'
+
+	cat >expect <<-'EOF'
+	 ...aaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 +
+	EOF
+	test_expect_success "$cmd --stat=...,name-width with long name" '
+		git $cmd $args --stat=60,30 >output &&
+		grep " | " output >actual &&
+		test_cmp expect actual
+	'
+
+	test_expect_success "$cmd --stat-name-width with long name" '
+		git $cmd $args --stat-name-width=30 >output &&
+		grep " | " output >actual &&
+		test_cmp expect actual
+	'
+done <<\EOF
+format-patch -1 --stdout
+diff HEAD^ HEAD --stat
+show --stat
+log -1 --stat
+EOF
+
+
+test_expect_success 'preparation for big change tests' '
+	>abcd &&
+	git add abcd &&
+	git commit -m message &&
+	i=0 &&
+	while test $i -lt 1000
+	do
+		echo $i && i=$(($i + 1))
+	done >abcd &&
+	git commit -m message abcd
+'
+
+cat >expect80 <<'EOF'
+ abcd | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+EOF
+cat >expect80-graph <<'EOF'
+|  abcd | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+EOF
+cat >expect200 <<'EOF'
+ abcd | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+EOF
+cat >expect200-graph <<'EOF'
+|  abcd | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+EOF
+while read verb expect cmd args
+do
+	test_expect_success "$cmd $verb COLUMNS (big change)" '
+		COLUMNS=200 git $cmd $args >output
+		grep " | " output >actual &&
+		test_cmp "$expect" actual
+	'
+
+	test "$cmd" != diff || continue
+
+	test_expect_success "$cmd --graph $verb COLUMNS (big change)" '
+		COLUMNS=200 git $cmd $args --graph >output
+		grep " | " output >actual &&
+		test_cmp "$expect-graph" actual
+	'
+done <<\EOF
+ignores expect80 format-patch -1 --stdout
+respects expect200 diff HEAD^ HEAD --stat
+respects expect200 show --stat
+respects expect200 log -1 --stat
+EOF
+
+cat >expect40 <<'EOF'
+ abcd | 1000 ++++++++++++++++++++++++++
+EOF
+cat >expect40-graph <<'EOF'
+|  abcd | 1000 ++++++++++++++++++++++++
+EOF
+while read verb expect cmd args
+do
+	test_expect_success "$cmd $verb not enough COLUMNS (big change)" '
+		COLUMNS=40 git $cmd $args >output
+		grep " | " output >actual &&
+		test_cmp "$expect" actual
+	'
+
+	test "$cmd" != diff || continue
+
+	test_expect_success "$cmd --graph $verb not enough COLUMNS (big change)" '
+		COLUMNS=40 git $cmd $args --graph >output
+		grep " | " output >actual &&
+		test_cmp "$expect-graph" actual
+	'
+done <<\EOF
+ignores expect80 format-patch -1 --stdout
+respects expect40 diff HEAD^ HEAD --stat
+respects expect40 show --stat
+respects expect40 log -1 --stat
+EOF
+
+cat >expect40 <<'EOF'
+ abcd | 1000 ++++++++++++++++++++++++++
+EOF
+cat >expect40-graph <<'EOF'
+|  abcd | 1000 ++++++++++++++++++++++++++
+EOF
+while read verb expect cmd args
+do
+	test_expect_success "$cmd $verb statGraphWidth config" '
+		git -c diff.statGraphWidth=26 $cmd $args >output
+		grep " | " output >actual &&
+		test_cmp "$expect" actual
+	'
+
+	test "$cmd" != diff || continue
+
+	test_expect_success "$cmd --graph $verb statGraphWidth config" '
+		git -c diff.statGraphWidth=26 $cmd $args --graph >output
+		grep " | " output >actual &&
+		test_cmp "$expect-graph" actual
+	'
+done <<\EOF
+ignores expect80 format-patch -1 --stdout
+respects expect40 diff HEAD^ HEAD --stat
+respects expect40 show --stat
+respects expect40 log -1 --stat
+EOF
+
+
+cat >expect <<'EOF'
+ abcd | 1000 ++++++++++++++++++++++++++
+EOF
+cat >expect-graph <<'EOF'
+|  abcd | 1000 ++++++++++++++++++++++++++
+EOF
+while read cmd args
+do
+	test_expect_success "$cmd --stat=width with big change" '
+		git $cmd $args --stat=40 >output
+		grep " | " output >actual &&
+		test_cmp expect actual
+	'
+
+	test_expect_success "$cmd --stat-width=width with big change" '
+		git $cmd $args --stat-width=40 >output
+		grep " | " output >actual &&
+		test_cmp expect actual
+	'
+
+	test_expect_success "$cmd --stat-graph-width with big change" '
+		git $cmd $args --stat-graph-width=26 >output
+		grep " | " output >actual &&
+		test_cmp expect actual
+	'
+
+	test "$cmd" != diff || continue
+
+	test_expect_success "$cmd --stat-width=width --graph with big change" '
+		git $cmd $args --stat-width=40 --graph >output
+		grep " | " output >actual &&
+		test_cmp expect-graph actual
+	'
+
+	test_expect_success "$cmd --stat-graph-width --graph with big change" '
+		git $cmd $args --stat-graph-width=26 --graph >output
+		grep " | " output >actual &&
+		test_cmp expect-graph actual
+	'
+done <<\EOF
+format-patch -1 --stdout
+diff HEAD^ HEAD --stat
+show --stat
+log -1 --stat
+EOF
+
+test_expect_success 'preparation for long filename tests' '
+	cp abcd aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa &&
+	git add aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa &&
+	git commit -m message
+'
+
+cat >expect <<'EOF'
+ ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 ++++++++++++
+EOF
+cat >expect-graph <<'EOF'
+|  ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 ++++++++++++
+EOF
+while read cmd args
+do
+	test_expect_success "$cmd --stat=width with big change is more balanced" '
+		git $cmd $args --stat-width=60 >output &&
+		grep " | " output >actual &&
+		test_cmp expect actual
+	'
+
+	test "$cmd" != diff || continue
+
+	test_expect_success "$cmd --stat=width --graph with big change is balanced" '
+		git $cmd $args --stat-width=60 --graph >output &&
+		grep " | " output >actual &&
+		test_cmp expect-graph actual
+	'
+done <<\EOF
+format-patch -1 --stdout
+diff HEAD^ HEAD --stat
+show --stat
+log -1 --stat
+EOF
+
+cat >expect80 <<'EOF'
+ ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 ++++++++++++++++++++
+EOF
+cat >expect80-graph <<'EOF'
+|  ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 ++++++++++++++++++++
+EOF
+cat >expect200 <<'EOF'
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+EOF
+cat >expect200-graph <<'EOF'
+|  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+EOF
+while read verb expect cmd args
+do
+	test_expect_success "$cmd $verb COLUMNS (long filename)" '
+		COLUMNS=200 git $cmd $args >output
+		grep " | " output >actual &&
+		test_cmp "$expect" actual
+	'
+
+	test "$cmd" != diff || continue
+
+	test_expect_success "$cmd --graph $verb COLUMNS (long filename)" '
+		COLUMNS=200 git $cmd $args --graph >output
+		grep " | " output >actual &&
+		test_cmp "$expect-graph" actual
+	'
+done <<\EOF
+ignores expect80 format-patch -1 --stdout
+respects expect200 diff HEAD^ HEAD --stat
+respects expect200 show --stat
+respects expect200 log -1 --stat
+EOF
+
+cat >expect1 <<'EOF'
+ ...aaaaaaa | 1000 ++++++
+EOF
+cat >expect1-graph <<'EOF'
+|  ...aaaaaaa | 1000 ++++++
+EOF
+while read verb expect cmd args
+do
+	test_expect_success COLUMNS_CAN_BE_1 \
+		"$cmd $verb prefix greater than COLUMNS (big change)" '
+		COLUMNS=1 git $cmd $args >output
+		grep " | " output >actual &&
+		test_cmp "$expect" actual
+	'
+
+	test "$cmd" != diff || continue
+
+	test_expect_success COLUMNS_CAN_BE_1 \
+		"$cmd --graph $verb prefix greater than COLUMNS (big change)" '
+		COLUMNS=1 git $cmd $args --graph >output
+		grep " | " output >actual &&
+		test_cmp "$expect-graph" actual
+	'
+done <<\EOF
+ignores expect80 format-patch -1 --stdout
+respects expect1 diff HEAD^ HEAD --stat
+respects expect1 show --stat
+respects expect1 log -1 --stat
+EOF
+
+cat >expect <<'EOF'
+ abcd | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+EOF
+test_expect_success 'merge --stat respects COLUMNS (big change)' '
+	git checkout -b branch HEAD^^ &&
+	COLUMNS=100 git merge --stat --no-ff master^ >output &&
+	grep " | " output >actual
+	test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 +++++++++++++++++++++++++++++++++++++++
+EOF
+test_expect_success 'merge --stat respects COLUMNS (long filename)' '
+	COLUMNS=100 git merge --stat --no-ff master >output &&
+	grep " | " output >actual
+	test_cmp expect actual
+'
+
+test_done
diff --git a/t/t4053-diff-no-index.sh b/t/t4053-diff-no-index.sh
new file mode 100755
index 0000000..979e983
--- /dev/null
+++ b/t/t4053-diff-no-index.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+test_description='diff --no-index'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	mkdir a &&
+	mkdir b &&
+	echo 1 >a/1 &&
+	echo 2 >a/2 &&
+	git init repo &&
+	echo 1 >repo/a &&
+	mkdir -p non/git &&
+	echo 1 >non/git/a &&
+	echo 1 >non/git/b
+'
+
+test_expect_success 'git diff --no-index directories' '
+	git diff --no-index a b >cnt
+	test $? = 1 && test_line_count = 14 cnt
+'
+
+test_expect_success 'git diff --no-index relative path outside repo' '
+	(
+		cd repo &&
+		test_expect_code 0 git diff --no-index a ../non/git/a &&
+		test_expect_code 0 git diff --no-index ../non/git/a ../non/git/b
+	)
+'
+
+test_done
diff --git a/t/t4100-apply-stat.sh b/t/t4100-apply-stat.sh
index 9b433de..744b8e5 100755
--- a/t/t4100-apply-stat.sh
+++ b/t/t4100-apply-stat.sh
@@ -17,13 +17,13 @@
 	test_expect_success "$title" '
 		git apply --stat --summary \
 			<"$TEST_DIRECTORY/t4100/t-apply-$num.patch" >current &&
-		test_cmp "$TEST_DIRECTORY"/t4100/t-apply-$num.expect current
+		test_i18ncmp "$TEST_DIRECTORY"/t4100/t-apply-$num.expect current
 	'
 
 	test_expect_success "$title with recount" '
 		sed -e "$UNC" <"$TEST_DIRECTORY/t4100/t-apply-$num.patch" |
 		git apply --recount --stat --summary >current &&
-		test_cmp "$TEST_DIRECTORY"/t4100/t-apply-$num.expect current
+		test_i18ncmp "$TEST_DIRECTORY"/t4100/t-apply-$num.expect current
 	'
 done <<\EOF
 rename
diff --git a/t/t4100/t-apply-8.expect b/t/t4100/t-apply-8.expect
index eef7f2e..55a55c3 100644
--- a/t/t4100/t-apply-8.expect
+++ b/t/t4100/t-apply-8.expect
@@ -1,2 +1,2 @@
  t/t4100-apply-stat.sh |    2 +-
- 1 files changed, 1 insertions(+), 1 deletions(-)
+ 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/t/t4100/t-apply-9.expect b/t/t4100/t-apply-9.expect
index eef7f2e..55a55c3 100644
--- a/t/t4100/t-apply-9.expect
+++ b/t/t4100/t-apply-9.expect
@@ -1,2 +1,2 @@
  t/t4100-apply-stat.sh |    2 +-
- 1 files changed, 1 insertions(+), 1 deletions(-)
+ 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/t/t4102-apply-rename.sh b/t/t4102-apply-rename.sh
index 1597965..e3ea3d5 100755
--- a/t/t4102-apply-rename.sh
+++ b/t/t4102-apply-rename.sh
@@ -7,6 +7,7 @@
 
 '
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-prereq-FILEMODE.sh
 
 # setup
 
@@ -31,13 +32,6 @@
 test_expect_success apply \
     'git apply --index --stat --summary --apply test-patch'
 
-if test "$(git config --bool core.filemode)" = false
-then
-	say 'filemode disabled on the filesystem'
-else
-	test_set_prereq FILEMODE
-fi
-
 test_expect_success FILEMODE validate \
 	    'test -f bar && ls -l bar | grep "^-..x......"'
 
diff --git a/t/t4103-apply-binary.sh b/t/t4103-apply-binary.sh
index ad4cc1a..99627bc 100755
--- a/t/t4103-apply-binary.sh
+++ b/t/t4103-apply-binary.sh
@@ -20,30 +20,41 @@
 cat file1 >file2
 cat file1 >file4
 
-git update-index --add --remove file1 file2 file4
-git commit -m 'Initial Version' 2>/dev/null
+test_expect_success 'setup' "
+	git update-index --add --remove file1 file2 file4 &&
+	git commit -m 'Initial Version' 2>/dev/null &&
 
-git checkout -b binary
-perl -pe 'y/x/\000/' <file1 >file3
-cat file3 >file4
-git add file2
-perl -pe 'y/\000/v/' <file3 >file1
-rm -f file2
-git update-index --add --remove file1 file2 file3 file4
-git commit -m 'Second Version'
+	git checkout -b binary &&
+	"$PERL_PATH" -pe 'y/x/\000/' <file1 >file3 &&
+	cat file3 >file4 &&
+	git add file2 &&
+	"$PERL_PATH" -pe 'y/\000/v/' <file3 >file1 &&
+	rm -f file2 &&
+	git update-index --add --remove file1 file2 file3 file4 &&
+	git commit -m 'Second Version' &&
 
-git diff-tree -p master binary >B.diff
-git diff-tree -p -C master binary >C.diff
+	git diff-tree -p master binary >B.diff &&
+	git diff-tree -p -C master binary >C.diff &&
 
-git diff-tree -p --binary master binary >BF.diff
-git diff-tree -p --binary -C master binary >CF.diff
+	git diff-tree -p --binary master binary >BF.diff &&
+	git diff-tree -p --binary -C master binary >CF.diff &&
+
+	git diff-tree -p --full-index master binary >B-index.diff &&
+	git diff-tree -p -C --full-index master binary >C-index.diff &&
+
+	git init other-repo &&
+	(cd other-repo &&
+	 git fetch .. master &&
+	 git reset --hard FETCH_HEAD
+	)
+"
 
 test_expect_success 'stat binary diff -- should not fail.' \
-	'git checkout master
+	'git checkout master &&
 	 git apply --stat --summary B.diff'
 
 test_expect_success 'stat binary diff (copy) -- should not fail.' \
-	'git checkout master
+	'git checkout master &&
 	 git apply --stat --summary C.diff'
 
 test_expect_success 'check binary diff -- should fail.' \
@@ -67,11 +78,11 @@
 '
 
 test_expect_success 'check binary diff with replacement.' \
-	'git checkout master
+	'git checkout master &&
 	 git apply --check --allow-binary-replacement BF.diff'
 
 test_expect_success 'check binary diff with replacement (copy).' \
-	'git checkout master
+	'git checkout master &&
 	 git apply --check --allow-binary-replacement CF.diff'
 
 # Now we start applying them.
@@ -98,6 +109,22 @@
 	'do_reset &&
 	 test_must_fail git apply --index C.diff'
 
+test_expect_success 'apply binary diff with full-index' '
+	do_reset &&
+	git apply B-index.diff
+'
+
+test_expect_success 'apply binary diff with full-index (copy)' '
+	do_reset &&
+	git apply C-index.diff
+'
+
+test_expect_success 'apply full-index binary diff in new repo' '
+	(cd other-repo &&
+	 do_reset &&
+	 test_must_fail git apply ../B-index.diff)
+'
+
 test_expect_success 'apply binary diff without replacement.' \
 	'do_reset &&
 	 git apply BF.diff'
diff --git a/t/t4104-apply-boundary.sh b/t/t4104-apply-boundary.sh
index 0e3ce36..c617c2a 100755
--- a/t/t4104-apply-boundary.sh
+++ b/t/t4104-apply-boundary.sh
@@ -134,4 +134,13 @@
 
 '
 
+test_expect_success 'apply patch with 3 context lines matching at end' '
+	{ echo a; echo b; echo c; echo d; } >file &&
+	git add file &&
+	echo e >>file &&
+	git diff >patch &&
+	>file &&
+	test_must_fail git apply patch
+'
+
 test_done
diff --git a/t/t4107-apply-ignore-whitespace.sh b/t/t4107-apply-ignore-whitespace.sh
new file mode 100755
index 0000000..b04fc8f
--- /dev/null
+++ b/t/t4107-apply-ignore-whitespace.sh
@@ -0,0 +1,185 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Giuseppe Bilotta
+#
+
+test_description='git-apply --ignore-whitespace.
+
+'
+. ./test-lib.sh
+
+# This primes main.c file that indents without using HT at all.
+# Various patches with HT and other spaces are attempted in the test.
+
+cat > patch1.patch <<\EOF
+diff --git a/main.c b/main.c
+new file mode 100644
+--- /dev/null
++++ b/main.c
+@@ -0,0 +1,22 @@
++#include <stdio.h>
++
++void print_int(int num);
++int func(int num);
++
++int main() {
++       int i;
++
++       for (i = 0; i < 10; i++) {
++               print_int(func(i)); /* stuff */
++       }
++
++       return 0;
++}
++
++int func(int num) {
++       return num * num;
++}
++
++void print_int(int num) {
++       printf("%d", num);
++}
+EOF
+
+# Since whitespace is very significant and we want to prevent whitespace
+# mangling when creating this test from a patch, we protect 'fixable'
+# whitespace by replacing spaces with Z and replacing them at patch
+# creation time, hence the sed trick.
+
+# This patch will fail unless whitespace differences are being ignored
+
+sed -e 's/Z/ /g' > patch2.patch <<\EOF
+diff --git a/main.c b/main.c
+--- a/main.c
++++ b/main.c
+@@ -10,6 +10,8 @@
+Z		print_int(func(i)); /* stuff */
+Z	}
+Z
++	printf("\n");
++
+Z	return 0;
+Z}
+Z
+EOF
+
+# This patch will fail even if whitespace differences are being ignored,
+# because of the missing string at EOL. TODO: this testcase should be
+# improved by creating a line that has the same hash with and without
+# the final string.
+
+sed -e 's/Z/ /g' > patch3.patch <<\EOF
+diff --git a/main.c b/main.c
+--- a/main.c
++++ b/main.c
+@@ -10,3 +10,4 @@
+Z	for (i = 0; i < 10; i++) {
+Z		print_int(func(i));Z
++		/* stuff */
+Z	}
+EOF
+
+# This patch will fail even if whitespace differences are being ignored,
+# because of the missing EOL at EOF.
+
+sed -e 's/Z/ /g' > patch4.patch <<\EOF
+diff --git a/main.c b/main.c
+--- a/main.c
++++ b/main.c
+@@ -21,1 +21,1 @@
+-	};Z
+\ No newline at end of file
++	};
+EOF
+
+# This patch will fail unless whitespace differences are being ignored.
+
+sed -e 's/Z/ /g' > patch5.patch <<\EOF
+diff --git a/main.c b/main.c
+--- a/main.c
++++ b/main.c
+@@ -2,2 +2,3 @@
+Z	void print_int(int num);
++	/* a comment */
+Z	int func(int num);
+EOF
+
+# And this is how the final output should be.  Patches introduce
+# HTs but the original SP indents are mostly kept.
+
+sed -e 's/T/	/g' > main.c.final <<\EOF
+#include <stdio.h>
+
+void print_int(int num);
+T/* a comment */
+int func(int num);
+
+int main() {
+       int i;
+
+       for (i = 0; i < 10; i++) {
+               print_int(func(i)); /* stuff */
+       }
+
+Tprintf("\n");
+
+       return 0;
+}
+
+int func(int num) {
+       return num * num;
+}
+
+void print_int(int num) {
+       printf("%d", num);
+}
+EOF
+
+test_expect_success 'file creation' '
+	git apply patch1.patch
+'
+
+test_expect_success 'patch2 fails (retab)' '
+	test_must_fail git apply patch2.patch
+'
+
+test_expect_success 'patch2 applies with --ignore-whitespace' '
+	git apply --ignore-whitespace patch2.patch
+'
+
+test_expect_success 'patch2 reverse applies with --ignore-space-change' '
+	git apply -R --ignore-space-change patch2.patch
+'
+
+git config apply.ignorewhitespace change
+
+test_expect_success 'patch2 applies (apply.ignorewhitespace = change)' '
+	git apply patch2.patch
+'
+
+test_expect_success 'patch3 fails (missing string at EOL)' '
+	test_must_fail git apply patch3.patch
+'
+
+test_expect_success 'patch4 fails (missing EOL at EOF)' '
+	test_must_fail git apply patch4.patch
+'
+
+test_expect_success 'patch5 applies (leading whitespace)' '
+	git apply patch5.patch
+'
+
+test_expect_success 'patches do not mangle whitespace' '
+	test_cmp main.c main.c.final
+'
+
+test_expect_success 're-create file (with --ignore-whitespace)' '
+	rm -f main.c &&
+	git apply patch1.patch
+'
+
+test_expect_success 'patch5 fails (--no-ignore-whitespace)' '
+	test_must_fail git apply --no-ignore-whitespace patch5.patch
+'
+
+test_done
diff --git a/t/t4108-apply-threeway.sh b/t/t4108-apply-threeway.sh
new file mode 100755
index 0000000..fa5d4ef
--- /dev/null
+++ b/t/t4108-apply-threeway.sh
@@ -0,0 +1,157 @@
+#!/bin/sh
+
+test_description='git apply --3way'
+
+. ./test-lib.sh
+
+create_file () {
+	for i
+	do
+		echo "$i"
+	done
+}
+
+sanitize_conflicted_diff () {
+	sed -e '
+		/^index /d
+		s/^\(+[<>][<>][<>][<>]*\) .*/\1/
+	'
+}
+
+test_expect_success setup '
+	test_tick &&
+	create_file >one 1 2 3 4 5 6 7 &&
+	cat one >two &&
+	git add one two &&
+	git commit -m initial &&
+
+	git branch side &&
+
+	test_tick &&
+	create_file >one 1 two 3 4 5 six 7 &&
+	create_file >two 1 two 3 4 5 6 7 &&
+	git commit -a -m master &&
+
+	git checkout side &&
+	create_file >one 1 2 3 4 five 6 7 &&
+	create_file >two 1 2 3 4 five 6 7 &&
+	git commit -a -m side &&
+
+	git checkout master
+'
+
+test_expect_success 'apply without --3way' '
+	git diff side^ side >P.diff &&
+
+	# should fail to apply
+	git reset --hard &&
+	git checkout master^0 &&
+	test_must_fail git apply --index P.diff &&
+	# should leave things intact
+	git diff-files --exit-code &&
+	git diff-index --exit-code --cached HEAD
+'
+
+test_expect_success 'apply with --3way' '
+	# Merging side should be similar to applying this patch
+	git diff ...side >P.diff &&
+
+	# The corresponding conflicted merge
+	git reset --hard &&
+	git checkout master^0 &&
+	test_must_fail git merge --no-commit side &&
+	git ls-files -s >expect.ls &&
+	git diff HEAD | sanitize_conflicted_diff >expect.diff &&
+
+	# should fail to apply
+	git reset --hard &&
+	git checkout master^0 &&
+	test_must_fail git apply --index --3way P.diff &&
+	git ls-files -s >actual.ls &&
+	git diff HEAD | sanitize_conflicted_diff >actual.diff &&
+
+	# The result should resemble the corresponding merge
+	test_cmp expect.ls actual.ls &&
+	test_cmp expect.diff actual.diff
+'
+
+test_expect_success 'apply with --3way with rerere enabled' '
+	git config rerere.enabled true &&
+
+	# Merging side should be similar to applying this patch
+	git diff ...side >P.diff &&
+
+	# The corresponding conflicted merge
+	git reset --hard &&
+	git checkout master^0 &&
+	test_must_fail git merge --no-commit side &&
+
+	# Manually resolve and record the resolution
+	create_file 1 two 3 4 five six 7 >one &&
+	git rerere &&
+	cat one >expect &&
+
+	# should fail to apply
+	git reset --hard &&
+	git checkout master^0 &&
+	test_must_fail git apply --index --3way P.diff &&
+
+	# but rerere should have replayed the recorded resolution
+	test_cmp expect one
+'
+
+test_expect_success 'apply -3 with add/add conflict setup' '
+	git reset --hard &&
+
+	git checkout -b adder &&
+	create_file 1 2 3 4 5 6 7 >three &&
+	create_file 1 2 3 4 5 6 7 >four &&
+	git add three four &&
+	git commit -m "add three and four" &&
+
+	git checkout -b another adder^ &&
+	create_file 1 2 3 4 5 6 7 >three &&
+	create_file 1 2 3 four 5 6 7 >four &&
+	git add three four &&
+	git commit -m "add three and four" &&
+
+	# Merging another should be similar to applying this patch
+	git diff adder...another >P.diff &&
+
+	git checkout adder^0 &&
+	test_must_fail git merge --no-commit another &&
+	git ls-files -s >expect.ls &&
+	git diff HEAD | sanitize_conflicted_diff >expect.diff
+'
+
+test_expect_success 'apply -3 with add/add conflict' '
+	# should fail to apply ...
+	git reset --hard &&
+	git checkout adder^0 &&
+	test_must_fail git apply --index --3way P.diff &&
+	# ... and leave conflicts in the index and in the working tree
+	git ls-files -s >actual.ls &&
+	git diff HEAD | sanitize_conflicted_diff >actual.diff &&
+
+	# The result should resemble the corresponding merge
+	test_cmp expect.ls actual.ls &&
+	test_cmp expect.diff actual.diff
+'
+
+test_expect_success 'apply -3 with add/add conflict (dirty working tree)' '
+	# should fail to apply ...
+	git reset --hard &&
+	git checkout adder^0 &&
+	echo >>four &&
+	cat four >four.save &&
+	cat three >three.save &&
+	git ls-files -s >expect.ls &&
+	test_must_fail git apply --index --3way P.diff &&
+	# ... and should not touch anything
+	git ls-files -s >actual.ls &&
+	test_cmp expect.ls actual.ls &&
+	test_cmp four.save four &&
+	test_cmp three.save three
+'
+
+test_done
diff --git a/t/t4111-apply-subdir.sh b/t/t4111-apply-subdir.sh
new file mode 100755
index 0000000..7c39843
--- /dev/null
+++ b/t/t4111-apply-subdir.sh
@@ -0,0 +1,142 @@
+#!/bin/sh
+
+test_description='patching from inconvenient places'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	cat >patch <<-\EOF &&
+	diff file.orig file
+	--- a/file.orig
+	+++ b/file
+	@@ -1 +1,2 @@
+	 1
+	+2
+	EOF
+	patch="$(pwd)/patch" &&
+
+	echo 1 >preimage &&
+	printf "%s\n" 1 2 >postimage &&
+	echo 3 >other &&
+
+	test_tick &&
+	git commit --allow-empty -m basis
+'
+
+test_expect_success 'setup: subdir' '
+	reset_subdir() {
+		git reset &&
+		mkdir -p sub/dir/b &&
+		mkdir -p objects &&
+		cp "$1" file &&
+		cp "$1" objects/file &&
+		cp "$1" sub/dir/file &&
+		cp "$1" sub/dir/b/file &&
+		git add file sub/dir/file sub/dir/b/file objects/file &&
+		cp "$2" file &&
+		cp "$2" sub/dir/file &&
+		cp "$2" sub/dir/b/file &&
+		cp "$2" objects/file &&
+		test_might_fail git update-index --refresh -q
+	}
+'
+
+test_expect_success 'apply from subdir of toplevel' '
+	cp postimage expected &&
+	reset_subdir other preimage &&
+	(
+		cd sub/dir &&
+		git apply "$patch"
+	) &&
+	test_cmp expected sub/dir/file
+'
+
+test_expect_success 'apply --cached from subdir of toplevel' '
+	cp postimage expected &&
+	cp other expected.working &&
+	reset_subdir preimage other &&
+	(
+		cd sub/dir &&
+		git apply --cached "$patch"
+	) &&
+	git show :sub/dir/file >actual &&
+	test_cmp expected actual &&
+	test_cmp expected.working sub/dir/file
+'
+
+test_expect_success 'apply --index from subdir of toplevel' '
+	cp postimage expected &&
+	reset_subdir preimage other &&
+	(
+		cd sub/dir &&
+		test_must_fail git apply --index "$patch"
+	) &&
+	reset_subdir other preimage &&
+	(
+		cd sub/dir &&
+		test_must_fail git apply --index "$patch"
+	) &&
+	reset_subdir preimage preimage &&
+	(
+		cd sub/dir &&
+		git apply --index "$patch"
+	) &&
+	git show :sub/dir/file >actual &&
+	test_cmp expected actual &&
+	test_cmp expected sub/dir/file
+'
+
+test_expect_success 'apply from .git dir' '
+	cp postimage expected &&
+	cp preimage .git/file &&
+	cp preimage .git/objects/file &&
+	(
+		cd .git &&
+		git apply "$patch"
+	) &&
+	test_cmp expected .git/file
+'
+
+test_expect_success 'apply from subdir of .git dir' '
+	cp postimage expected &&
+	cp preimage .git/file &&
+	cp preimage .git/objects/file &&
+	(
+		cd .git/objects &&
+		git apply "$patch"
+	) &&
+	test_cmp expected .git/objects/file
+'
+
+test_expect_success 'apply --cached from .git dir' '
+	cp postimage expected &&
+	cp other expected.working &&
+	cp other .git/file &&
+	reset_subdir preimage other &&
+	(
+		cd .git &&
+		git apply --cached "$patch"
+	) &&
+	git show :file >actual &&
+	test_cmp expected actual &&
+	test_cmp expected.working file &&
+	test_cmp expected.working .git/file
+'
+
+test_expect_success 'apply --cached from subdir of .git dir' '
+	cp postimage expected &&
+	cp preimage expected.subdir &&
+	cp other .git/file &&
+	cp other .git/objects/file &&
+	reset_subdir preimage other &&
+	(
+		cd .git/objects &&
+		git apply --cached "$patch"
+	) &&
+	git show :file >actual &&
+	git show :objects/file >actual.subdir &&
+	test_cmp expected actual &&
+	test_cmp expected.subdir actual.subdir
+'
+
+test_done
diff --git a/t/t4114-apply-typechange.sh b/t/t4114-apply-typechange.sh
index 99ec13d..f12826f 100755
--- a/t/t4114-apply-typechange.sh
+++ b/t/t4114-apply-typechange.sh
@@ -9,13 +9,7 @@
 
 . ./test-lib.sh
 
-if ! test_have_prereq SYMLINKS
-then
-	say 'Symbolic links not supported, skipping tests.'
-	test_done
-fi
-
-test_expect_success 'setup repository and commits' '
+test_expect_success SYMLINKS 'setup repository and commits' '
 	echo "hello world" > foo &&
 	echo "hi planet" > bar &&
 	git update-index --add foo bar &&
@@ -48,7 +42,7 @@
 	git branch foo-baz-renamed-from-foo
 	'
 
-test_expect_success 'file renamed from foo to foo/baz' '
+test_expect_success SYMLINKS '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
@@ -56,7 +50,7 @@
 test_debug 'cat patch'
 
 
-test_expect_success 'file renamed from foo/baz to foo' '
+test_expect_success SYMLINKS '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
@@ -64,7 +58,7 @@
 test_debug 'cat patch'
 
 
-test_expect_success 'directory becomes file' '
+test_expect_success SYMLINKS 'directory becomes file' '
 	git checkout -f foo-becomes-a-directory &&
 	git diff-tree -p HEAD initial > patch &&
 	git apply --index < patch
@@ -72,7 +66,7 @@
 test_debug 'cat patch'
 
 
-test_expect_success 'file becomes directory' '
+test_expect_success SYMLINKS 'file becomes directory' '
 	git checkout -f initial &&
 	git diff-tree -p HEAD foo-becomes-a-directory > patch &&
 	git apply --index < patch
@@ -80,7 +74,7 @@
 test_debug 'cat patch'
 
 
-test_expect_success 'file becomes symlink' '
+test_expect_success SYMLINKS 'file becomes symlink' '
 	git checkout -f initial &&
 	git diff-tree -p HEAD foo-symlinked-to-bar > patch &&
 	git apply --index < patch
@@ -88,21 +82,21 @@
 test_debug 'cat patch'
 
 
-test_expect_success 'symlink becomes file' '
+test_expect_success SYMLINKS '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 'binary file becomes symlink' '
+test_expect_success SYMLINKS 'binary file becomes symlink' '
 	git checkout -f foo-becomes-binary &&
 	git diff-tree -p --binary HEAD foo-symlinked-to-bar > patch &&
 	git apply --index < patch
 	'
 test_debug 'cat patch'
 
-test_expect_success 'symlink becomes binary file' '
+test_expect_success SYMLINKS 'symlink becomes binary file' '
 	git checkout -f foo-symlinked-to-bar &&
 	git diff-tree -p --binary HEAD foo-becomes-binary > patch &&
 	git apply --index < patch
@@ -110,7 +104,7 @@
 test_debug 'cat patch'
 
 
-test_expect_success 'symlink becomes directory' '
+test_expect_success SYMLINKS 'symlink becomes directory' '
 	git checkout -f foo-symlinked-to-bar &&
 	git diff-tree -p HEAD foo-becomes-a-directory > patch &&
 	git apply --index < patch
@@ -118,7 +112,7 @@
 test_debug 'cat patch'
 
 
-test_expect_success 'directory becomes symlink' '
+test_expect_success SYMLINKS 'directory becomes symlink' '
 	git checkout -f foo-becomes-a-directory &&
 	git diff-tree -p HEAD foo-symlinked-to-bar > patch &&
 	git apply --index < patch
diff --git a/t/t4115-apply-symlink.sh b/t/t4115-apply-symlink.sh
index b852e58..7674dd2 100755
--- a/t/t4115-apply-symlink.sh
+++ b/t/t4115-apply-symlink.sh
@@ -9,13 +9,7 @@
 
 . ./test-lib.sh
 
-if ! test_have_prereq SYMLINKS
-then
-	say 'Symbolic links not supported, skipping tests.'
-	test_done
-fi
-
-test_expect_success setup '
+test_expect_success SYMLINKS setup '
 
 	ln -s path1/path2/path3/path4/path5 link1 &&
 	git add link? &&
@@ -34,7 +28,7 @@
 
 '
 
-test_expect_success 'apply symlink patch' '
+test_expect_success SYMLINKS 'apply symlink patch' '
 
 	git checkout side &&
 	git apply patch &&
@@ -43,7 +37,7 @@
 
 '
 
-test_expect_success 'apply --index symlink patch' '
+test_expect_success SYMLINKS 'apply --index symlink patch' '
 
 	git checkout -f side &&
 	git apply --index patch &&
diff --git a/t/t4116-apply-reverse.sh b/t/t4116-apply-reverse.sh
index 2298ece..fca8153 100755
--- a/t/t4116-apply-reverse.sh
+++ b/t/t4116-apply-reverse.sh
@@ -12,14 +12,14 @@
 test_expect_success setup '
 
 	for i in a b c d e f g h i j k l m n; do echo $i; done >file1 &&
-	perl -pe "y/ijk/\\000\\001\\002/" <file1 >file2 &&
+	"$PERL_PATH" -pe "y/ijk/\\000\\001\\002/" <file1 >file2 &&
 
 	git add file1 file2 &&
 	git commit -m initial &&
 	git tag initial &&
 
 	for i in a b c g h i J K L m o n p q; do echo $i; done >file1 &&
-	perl -pe "y/mon/\\000\\001\\002/" <file1 >file2 &&
+	"$PERL_PATH" -pe "y/mon/\\000\\001\\002/" <file1 >file2 &&
 
 	git commit -a -m second &&
 	git tag second &&
diff --git a/t/t4117-apply-reject.sh b/t/t4117-apply-reject.sh
index e9ccd16..8e15ecb 100755
--- a/t/t4117-apply-reject.sh
+++ b/t/t4117-apply-reject.sh
@@ -46,6 +46,14 @@
 	cat file1 >saved.file1
 '
 
+test_expect_success 'apply --reject is incompatible with --3way' '
+	test_when_finished "cat saved.file1 >file1" &&
+	git diff >patch.0 &&
+	git checkout file1 &&
+	test_must_fail git apply --reject --3way patch.0 &&
+	git diff --exit-code
+'
+
 test_expect_success 'apply without --reject should fail' '
 
 	if git apply patch.1
diff --git a/t/t4119-apply-config.sh b/t/t4119-apply-config.sh
index 3c73a78..3d0384d 100755
--- a/t/t4119-apply-config.sh
+++ b/t/t4119-apply-config.sh
@@ -73,7 +73,7 @@
 test_expect_success 'apply --whitespace=strip in subdir' '
 
 	cd "$D" &&
-	git config --unset-all apply.whitespace
+	git config --unset-all apply.whitespace &&
 	rm -f sub/file1 &&
 	cp saved sub/file1 &&
 	git update-index --refresh &&
diff --git a/t/t4120-apply-popt.sh b/t/t4120-apply-popt.sh
index 83d4ba6..a33d510 100755
--- a/t/t4120-apply-popt.sh
+++ b/t/t4120-apply-popt.sh
@@ -6,20 +6,85 @@
 test_description='git apply -p handling.'
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-prereq-FILEMODE.sh
 
 test_expect_success setup '
 	mkdir sub &&
 	echo A >sub/file1 &&
-	cp sub/file1 file1 &&
+	cp sub/file1 file1.saved &&
 	git add sub/file1 &&
 	echo B >sub/file1 &&
 	git diff >patch.file &&
-	rm sub/file1 &&
-	rmdir sub
+	git checkout -- sub/file1 &&
+	git mv sub süb &&
+	echo B >süb/file1 &&
+	git diff >patch.escaped &&
+	grep "[\]" patch.escaped &&
+	rm süb/file1 &&
+	rmdir süb
 '
 
 test_expect_success 'apply git diff with -p2' '
+	cp file1.saved file1 &&
 	git apply -p2 patch.file
 '
 
+test_expect_success 'apply with too large -p' '
+	cp file1.saved file1 &&
+	test_must_fail git apply --stat -p3 patch.file 2>err &&
+	grep "removing 3 leading" err
+'
+
+test_expect_success 'apply (-p2) traditional diff with funny filenames' '
+	cat >patch.quotes <<-\EOF &&
+	diff -u "a/"sub/file1 "b/"sub/file1
+	--- "a/"sub/file1
+	+++ "b/"sub/file1
+	@@ -1 +1 @@
+	-A
+	+B
+	EOF
+	echo B >expected &&
+
+	cp file1.saved file1 &&
+	git apply -p2 patch.quotes &&
+	test_cmp expected file1
+'
+
+test_expect_success 'apply with too large -p and fancy filename' '
+	cp file1.saved file1 &&
+	test_must_fail git apply --stat -p3 patch.escaped 2>err &&
+	grep "removing 3 leading" err
+'
+
+test_expect_success 'apply (-p2) diff, mode change only' '
+	cat >patch.chmod <<-\EOF &&
+	diff --git a/sub/file1 b/sub/file1
+	old mode 100644
+	new mode 100755
+	EOF
+	test_chmod -x file1 &&
+	git apply --index -p2 patch.chmod &&
+	case $(git ls-files -s file1) in 100755*) : good;; *) false;; esac
+'
+
+test_expect_success FILEMODE 'file mode was changed' '
+	test -x file1
+'
+
+test_expect_success 'apply (-p2) diff, rename' '
+	cat >patch.rename <<-\EOF &&
+	diff --git a/sub/file1 b/sub/file2
+	similarity index 100%
+	rename from sub/file1
+	rename to sub/file2
+	EOF
+	echo A >expected &&
+
+	cp file1.saved file1 &&
+	rm -f file2 &&
+	git apply -p2 patch.rename &&
+	test_cmp expected file2
+'
+
 test_done
diff --git a/t/t4122-apply-symlink-inside.sh b/t/t4122-apply-symlink-inside.sh
index 0d3c1d5..3940737 100755
--- a/t/t4122-apply-symlink-inside.sh
+++ b/t/t4122-apply-symlink-inside.sh
@@ -3,12 +3,6 @@
 test_description='apply to deeper directory without getting fooled with symlink'
 . ./test-lib.sh
 
-if ! test_have_prereq SYMLINKS
-then
-	say 'Symbolic links not supported, skipping tests.'
-	test_done
-fi
-
 lecho () {
 	for l_
 	do
@@ -16,7 +10,7 @@
 	done
 }
 
-test_expect_success setup '
+test_expect_success SYMLINKS setup '
 
 	mkdir -p arch/i386/boot arch/x86_64 &&
 	lecho 1 2 3 4 5 >arch/i386/boot/Makefile &&
@@ -37,7 +31,7 @@
 
 '
 
-test_expect_success apply '
+test_expect_success SYMLINKS apply '
 
 	git checkout test &&
 	git diff --exit-code test &&
@@ -46,7 +40,7 @@
 
 '
 
-test_expect_success 'check result' '
+test_expect_success SYMLINKS 'check result' '
 
 	git diff --exit-code master &&
 	git diff --exit-code --cached master &&
diff --git a/t/t4124-apply-ws-rule.sh b/t/t4124-apply-ws-rule.sh
index fac2093..6f6ee88 100755
--- a/t/t4124-apply-ws-rule.sh
+++ b/t/t4124-apply-ws-rule.sh
@@ -10,22 +10,24 @@
 	#       X  RULE
 	#   	!  trailing-space
 	#   	@  space-before-tab
-	#   	#  indent-with-non-tab
+	#   	#  indent-with-non-tab (default tab width 8)
+	#	=  indent-with-non-tab,tabwidth=16
+	#   	%  tab-in-indent
 	sed -e "s/_/ /g" -e "s/>/	/" <<-\EOF
 		An_SP in an ordinary line>and a HT.
-		>A HT.
-		_>A SP and a HT (@).
-		_>_A SP, a HT and a SP (@).
+		>A HT (%).
+		_>A SP and a HT (@%).
+		_>_A SP, a HT and a SP (@%).
 		_______Seven SP.
 		________Eight SP (#).
-		_______>Seven SP and a HT (@).
-		________>Eight SP and a HT (@#).
-		_______>_Seven SP, a HT and a SP (@).
-		________>_Eight SP, a HT and a SP (@#).
+		_______>Seven SP and a HT (@%).
+		________>Eight SP and a HT (@#%).
+		_______>_Seven SP, a HT and a SP (@%).
+		________>_Eight SP, a HT and a SP (@#%).
 		_______________Fifteen SP (#).
-		_______________>Fifteen SP and a HT (@#).
-		________________Sixteen SP (#).
-		________________>Sixteen SP and a HT (@#).
+		_______________>Fifteen SP and a HT (@#%).
+		________________Sixteen SP (#=).
+		________________>Sixteen SP and a HT (@#%=).
 		_____a__Five SP, a non WS, two SP.
 		A line with a (!) trailing SP_
 		A line with a (!) trailing HT>
@@ -39,12 +41,11 @@
 }
 
 test_fix () {
-
 	# fix should not barf
 	apply_patch --whitespace=fix || return 1
 
 	# find touched lines
-	diff file target | sed -n -e "s/^> //p" >fixed
+	$DIFF file target | sed -n -e "s/^> //p" >fixed
 
 	# the changed lines are all expeced to change
 	fixed_cnt=$(wc -l <fixed)
@@ -85,14 +86,14 @@
 test_expect_success 'whitespace=nowarn, default rule' '
 
 	apply_patch --whitespace=nowarn &&
-	diff file target
+	test_cmp file target
 
 '
 
 test_expect_success 'whitespace=warn, default rule' '
 
 	apply_patch --whitespace=warn &&
-	diff file target
+	test_cmp file target
 
 '
 
@@ -108,7 +109,7 @@
 
 	git config core.whitespace -trailing,-space-before,-indent &&
 	apply_patch --whitespace=error-all &&
-	diff file target
+	test_cmp file target
 
 '
 
@@ -117,7 +118,35 @@
 	git config --unset core.whitespace &&
 	echo "target -whitespace" >.gitattributes &&
 	apply_patch --whitespace=error-all &&
-	diff file target
+	test_cmp file target
+
+'
+
+test_expect_success 'spaces inserted by tab-in-indent' '
+
+	git config core.whitespace -trailing,-space,-indent,tab &&
+	rm -f .gitattributes &&
+	test_fix % &&
+	sed -e "s/_/ /g" -e "s/>/	/" <<-\EOF >expect &&
+		An_SP in an ordinary line>and a HT.
+		________A HT (%).
+		________A SP and a HT (@%).
+		_________A SP, a HT and a SP (@%).
+		_______Seven SP.
+		________Eight SP (#).
+		________Seven SP and a HT (@%).
+		________________Eight SP and a HT (@#%).
+		_________Seven SP, a HT and a SP (@%).
+		_________________Eight SP, a HT and a SP (@#%).
+		_______________Fifteen SP (#).
+		________________Fifteen SP and a HT (@#%).
+		________________Sixteen SP (#=).
+		________________________Sixteen SP and a HT (@#%=).
+		_____a__Five SP, a non WS, two SP.
+		A line with a (!) trailing SP_
+		A line with a (!) trailing HT>
+	EOF
+	test_cmp expect target
 
 '
 
@@ -129,21 +158,36 @@
 		case "$s" in '') ts='@' ;; *) ts= ;; esac
 		for i in - ''
 		do
-			case "$i" in '') ti='#' ;; *) ti= ;; esac
-			rule=${t}trailing,${s}space,${i}indent
+			case "$i" in '') ti='#' ti16='=';; *) ti= ti16= ;; esac
+			for h in - ''
+			do
+				[ -z "$h$i" ] && continue
+				case "$h" in '') th='%' ;; *) th= ;; esac
+				rule=${t}trailing,${s}space,${i}indent,${h}tab
 
-			rm -f .gitattributes
-			test_expect_success "rule=$rule" '
-				git config core.whitespace "$rule" &&
-				test_fix "$tt$ts$ti"
-			'
+				rm -f .gitattributes
+				test_expect_success "rule=$rule" '
+					git config core.whitespace "$rule" &&
+					test_fix "$tt$ts$ti$th"
+				'
 
-			test_expect_success "rule=$rule (attributes)" '
-				git config --unset core.whitespace &&
-				echo "target whitespace=$rule" >.gitattributes &&
-				test_fix "$tt$ts$ti"
-			'
+				test_expect_success "rule=$rule,tabwidth=16" '
+					git config core.whitespace "$rule,tabwidth=16" &&
+					test_fix "$tt$ts$ti16$th"
+				'
 
+				test_expect_success "rule=$rule (attributes)" '
+					git config --unset core.whitespace &&
+					echo "target whitespace=$rule" >.gitattributes &&
+					test_fix "$tt$ts$ti$th"
+				'
+
+				test_expect_success "rule=$rule,tabwidth=16 (attributes)" '
+					echo "target whitespace=$rule,tabwidth=16" >.gitattributes &&
+					test_fix "$tt$ts$ti16$th"
+				'
+
+			done
 		done
 	done
 done
@@ -170,4 +214,276 @@
 	grep "^$" target
 '
 
+test_expect_success 'blank at EOF with --whitespace=fix (1)' '
+	test_might_fail git config --unset core.whitespace &&
+	rm -f .gitattributes &&
+
+	{ echo a; echo b; echo c; } >one &&
+	git add one &&
+	{ echo a; echo b; echo c; } >expect &&
+	{ cat expect; echo; } >one &&
+	git diff -- one >patch &&
+
+	git checkout one &&
+	git apply --whitespace=fix patch &&
+	test_cmp expect one
+'
+
+test_expect_success 'blank at EOF with --whitespace=fix (2)' '
+	{ echo a; echo b; echo c; } >one &&
+	git add one &&
+	{ echo a; echo c; } >expect &&
+	{ cat expect; echo; echo; } >one &&
+	git diff -- one >patch &&
+
+	git checkout one &&
+	git apply --whitespace=fix patch &&
+	test_cmp expect one
+'
+
+test_expect_success 'blank at EOF with --whitespace=fix (3)' '
+	{ echo a; echo b; echo; } >one &&
+	git add one &&
+	{ echo a; echo c; echo; } >expect &&
+	{ cat expect; echo; echo; } >one &&
+	git diff -- one >patch &&
+
+	git checkout one &&
+	git apply --whitespace=fix patch &&
+	test_cmp expect one
+'
+
+test_expect_success 'blank at end of hunk, not at EOF with --whitespace=fix' '
+	{ echo a; echo b; echo; echo; echo; echo; echo; echo d; } >one &&
+	git add one &&
+	{ echo a; echo c; echo; echo; echo; echo; echo; echo; echo d; } >expect &&
+	cp expect one &&
+	git diff -- one >patch &&
+
+	git checkout one &&
+	git apply --whitespace=fix patch &&
+	test_cmp expect one
+'
+
+test_expect_success 'blank at EOF with --whitespace=warn' '
+	{ echo a; echo b; echo c; } >one &&
+	git add one &&
+	echo >>one &&
+	cat one >expect &&
+	git diff -- one >patch &&
+
+	git checkout one &&
+	git apply --whitespace=warn patch 2>error &&
+	test_cmp expect one &&
+	grep "new blank line at EOF" error
+'
+
+test_expect_success 'blank at EOF with --whitespace=error' '
+	{ echo a; echo b; echo c; } >one &&
+	git add one &&
+	cat one >expect &&
+	echo >>one &&
+	git diff -- one >patch &&
+
+	git checkout one &&
+	test_must_fail git apply --whitespace=error patch 2>error &&
+	test_cmp expect one &&
+	grep "new blank line at EOF" error
+'
+
+test_expect_success 'blank but not empty at EOF' '
+	{ echo a; echo b; echo c; } >one &&
+	git add one &&
+	echo "   " >>one &&
+	cat one >expect &&
+	git diff -- one >patch &&
+
+	git checkout one &&
+	git apply --whitespace=warn patch 2>error &&
+	test_cmp expect one &&
+	grep "new blank line at EOF" error
+'
+
+test_expect_success 'applying beyond EOF requires one non-blank context line' '
+	{ echo; echo; echo; echo; } >one &&
+	git add one &&
+	{ echo b; } >>one &&
+	git diff -- one >patch &&
+
+	git checkout one &&
+	{ echo a; echo; } >one &&
+	cp one expect &&
+	test_must_fail git apply --whitespace=fix patch &&
+	test_cmp one expect &&
+	test_must_fail git apply --ignore-space-change --whitespace=fix patch &&
+	test_cmp one expect
+'
+
+test_expect_success 'tons of blanks at EOF should not apply' '
+	for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16; do
+		echo; echo; echo; echo;
+	done >one &&
+	git add one &&
+	echo a >>one &&
+	git diff -- one >patch &&
+
+	>one &&
+	test_must_fail git apply --whitespace=fix patch &&
+	test_must_fail git apply --ignore-space-change --whitespace=fix patch
+'
+
+test_expect_success 'missing blank line at end with --whitespace=fix' '
+	echo a >one &&
+	echo >>one &&
+	git add one &&
+	echo b >>one &&
+	cp one expect &&
+	git diff -- one >patch &&
+	echo a >one &&
+	cp one saved-one &&
+	test_must_fail git apply patch &&
+	git apply --whitespace=fix patch &&
+	test_cmp one expect &&
+	mv saved-one one &&
+	git apply --ignore-space-change --whitespace=fix patch &&
+	test_cmp one expect
+'
+
+test_expect_success 'two missing blank lines at end with --whitespace=fix' '
+	{ echo a; echo; echo b; echo c; } >one &&
+	cp one no-blank-lines &&
+	{ echo; echo; } >>one &&
+	git add one &&
+	echo d >>one &&
+	cp one expect &&
+	echo >>one &&
+	git diff -- one >patch &&
+	cp no-blank-lines one &&
+	test_must_fail git apply patch &&
+	git apply --whitespace=fix patch &&
+	test_cmp one expect &&
+	mv no-blank-lines one &&
+	test_must_fail git apply patch &&
+	git apply --ignore-space-change --whitespace=fix patch &&
+	test_cmp one expect
+'
+
+test_expect_success 'missing blank line at end, insert before end, --whitespace=fix' '
+	{ echo a; echo; } >one &&
+	git add one &&
+	{ echo b; echo a; echo; } >one &&
+	cp one expect &&
+	git diff -- one >patch &&
+	echo a >one &&
+	test_must_fail git apply patch &&
+	git apply --whitespace=fix patch &&
+	test_cmp one expect
+'
+
+test_expect_success 'shrink file with tons of missing blanks at end of file' '
+	{ echo a; echo b; echo c; } >one &&
+	cp one no-blank-lines &&
+	for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16; do
+		echo; echo; echo; echo;
+	done >>one &&
+	git add one &&
+	echo a >one &&
+	cp one expect &&
+	git diff -- one >patch &&
+	cp no-blank-lines one &&
+	test_must_fail git apply patch &&
+	git apply --whitespace=fix patch &&
+	test_cmp one expect &&
+	mv no-blank-lines one &&
+	git apply --ignore-space-change --whitespace=fix patch &&
+	test_cmp one expect
+'
+
+test_expect_success 'missing blanks at EOF must only match blank lines' '
+	{ echo a; echo b; } >one &&
+	git add one &&
+	{ echo c; echo d; } >>one &&
+	git diff -- one >patch &&
+
+	echo a >one &&
+	test_must_fail git apply patch &&
+	test_must_fail git apply --whitespace=fix patch &&
+	test_must_fail git apply --ignore-space-change --whitespace=fix patch
+'
+
+sed -e's/Z//' >one <<EOF
+a
+b
+c
+		      Z
+EOF
+
+test_expect_success 'missing blank line should match context line with spaces' '
+	git add one &&
+	echo d >>one &&
+	git diff -- one >patch &&
+	{ echo a; echo b; echo c; } >one &&
+	cp one expect &&
+	{ echo; echo d; } >>expect &&
+	git add one &&
+
+	git apply --whitespace=fix patch &&
+	test_cmp one expect
+'
+
+sed -e's/Z//' >one <<EOF
+a
+b
+c
+		      Z
+EOF
+
+test_expect_success 'same, but with the --ignore-space-option' '
+	git add one &&
+	echo d >>one &&
+	cp one expect &&
+	git diff -- one >patch &&
+	{ echo a; echo b; echo c; } >one &&
+	git add one &&
+
+	git checkout-index -f one &&
+	git apply --ignore-space-change --whitespace=fix patch &&
+	test_cmp one expect
+'
+
+test_expect_success 'same, but with CR-LF line endings && cr-at-eol set' '
+	git config core.whitespace cr-at-eol &&
+	printf "a\r\n" >one &&
+	printf "b\r\n" >>one &&
+	printf "c\r\n" >>one &&
+	cp one save-one &&
+	printf "                 \r\n" >>one &&
+	git add one &&
+	printf "d\r\n" >>one &&
+	cp one expect &&
+	git diff -- one >patch &&
+	mv save-one one &&
+
+	git apply --ignore-space-change --whitespace=fix patch &&
+	test_cmp one expect
+'
+
+test_expect_success 'same, but with CR-LF line endings && cr-at-eol unset' '
+	git config --unset core.whitespace &&
+	printf "a\r\n" >one &&
+	printf "b\r\n" >>one &&
+	printf "c\r\n" >>one &&
+	cp one save-one &&
+	printf "                 \r\n" >>one &&
+	git add one &&
+	cp one expect &&
+	printf "d\r\n" >>one &&
+	git diff -- one >patch &&
+	mv save-one one &&
+	echo d >>expect &&
+
+	git apply --ignore-space-change --whitespace=fix patch &&
+	test_cmp one expect
+'
+
 test_done
diff --git a/t/t4125-apply-ws-fuzz.sh b/t/t4125-apply-ws-fuzz.sh
index 3b471b6..9671de7 100755
--- a/t/t4125-apply-ws-fuzz.sh
+++ b/t/t4125-apply-ws-fuzz.sh
@@ -37,11 +37,11 @@
 	# patch-2 is the same as patch-1 but is based
 	# on a version that already has whitespace fixed,
 	# and does not introduce whitespace breakages.
-	sed -e "s/ $//" patch-1 >patch-2 &&
+	sed -e "s/ \$//" patch-1 >patch-2 &&
 
 	# If all whitespace breakages are fixed the contents
 	# should look like file-fixed
-	sed -e "s/ $//" file-1 >file-fixed
+	sed -e "s/ \$//" file-1 >file-fixed
 
 '
 
diff --git a/t/t4127-apply-same-fn.sh b/t/t4127-apply-same-fn.sh
index 3a8202e..972946c 100755
--- a/t/t4127-apply-same-fn.sh
+++ b/t/t4127-apply-same-fn.sh
@@ -27,11 +27,11 @@
 	cp same_fn same_fn2 &&
 	git reset --hard &&
 	git apply patch0 &&
-	diff same_fn same_fn2
+	test_cmp same_fn same_fn2
 '
 
 test_expect_success 'apply same filename with overlapping changes' '
-	git reset --hard
+	git reset --hard &&
 	modify "s/^d/z/" same_fn &&
 	git diff > patch0 &&
 	git add same_fn &&
@@ -40,12 +40,12 @@
 	cp same_fn same_fn2 &&
 	git reset --hard &&
 	git apply patch0 &&
-	diff same_fn same_fn2
+	test_cmp same_fn same_fn2
 '
 
 test_expect_success 'apply same new filename after rename' '
-	git reset --hard
-	git mv same_fn new_fn
+	git reset --hard &&
+	git mv same_fn new_fn &&
 	modify "s/^d/z/" new_fn &&
 	git add new_fn &&
 	git diff -M --cached > patch1 &&
@@ -54,16 +54,16 @@
 	cp new_fn new_fn2 &&
 	git reset --hard &&
 	git apply --index patch1 &&
-	diff new_fn new_fn2
+	test_cmp new_fn new_fn2
 '
 
 test_expect_success 'apply same old filename after rename -- should fail.' '
-	git reset --hard
-	git mv same_fn new_fn
+	git reset --hard &&
+	git mv same_fn new_fn &&
 	modify "s/^d/z/" new_fn &&
 	git add new_fn &&
 	git diff -M --cached > patch1 &&
-	git mv new_fn same_fn
+	git mv new_fn same_fn &&
 	modify "s/^e/y/" same_fn &&
 	git diff >> patch1 &&
 	git reset --hard &&
@@ -71,13 +71,13 @@
 '
 
 test_expect_success 'apply A->B (rename), C->A (rename), A->A -- should pass.' '
-	git reset --hard
-	git mv same_fn new_fn
+	git reset --hard &&
+	git mv same_fn new_fn &&
 	modify "s/^d/z/" new_fn &&
 	git add new_fn &&
 	git diff -M --cached > patch1 &&
 	git commit -m "a rename" &&
-	git mv other_fn same_fn
+	git mv other_fn same_fn &&
 	modify "s/^e/y/" same_fn &&
 	git add same_fn &&
 	git diff -M --cached >> patch1 &&
diff --git a/t/t4128-apply-root.sh b/t/t4128-apply-root.sh
index 8f6aea4..6cc741a 100755
--- a/t/t4128-apply-root.sh
+++ b/t/t4128-apply-root.sh
@@ -58,6 +58,23 @@
 '
 
 cat > patch << EOF
+diff --git a/c/newfile2 b/c/newfile2
+new file mode 100644
+index 0000000..d95f3ad
+--- /dev/null
++++ b/c/newfile2
+@@ -0,0 +1 @@
++content
+EOF
+
+test_expect_success 'apply --directory -p (new file)' '
+	git reset --hard initial &&
+	git apply -p2 --directory=some/sub/dir/ --index patch &&
+	test content = $(git show :some/sub/dir/newfile2) &&
+	test content = $(cat some/sub/dir/newfile2)
+'
+
+cat > patch << EOF
 diff --git a/delfile b/delfile
 deleted file mode 100644
 index d95f3ad..0000000
diff --git a/t/t4129-apply-samemode.sh b/t/t4129-apply-samemode.sh
index fc7af04..0d36ebd 100755
--- a/t/t4129-apply-samemode.sh
+++ b/t/t4129-apply-samemode.sh
@@ -3,13 +3,7 @@
 test_description='applying patch with mode bits'
 
 . ./test-lib.sh
-
-if test "$(git config --bool core.filemode)" = false
-then
-	say 'filemode disabled on the filesystem'
-else
-	test_set_prereq FILEMODE
-fi
+. "$TEST_DIRECTORY"/lib-prereq-FILEMODE.sh
 
 test_expect_success setup '
 	echo original >file &&
diff --git a/t/t4130-apply-criss-cross-rename.sh b/t/t4130-apply-criss-cross-rename.sh
index 7cfa2d6..d173acd 100755
--- a/t/t4130-apply-criss-cross-rename.sh
+++ b/t/t4130-apply-criss-cross-rename.sh
@@ -44,7 +44,7 @@
 	git reset --hard &&
 	mv file1 tmp &&
 	mv file2 file1 &&
-	mv file3 file2
+	mv file3 file2 &&
 	mv tmp file3 &&
 	cp file1 file1-swapped &&
 	cp file2 file2-swapped &&
diff --git a/t/t4131-apply-fake-ancestor.sh b/t/t4131-apply-fake-ancestor.sh
index 94373ca..b1361ce 100755
--- a/t/t4131-apply-fake-ancestor.sh
+++ b/t/t4131-apply-fake-ancestor.sh
@@ -11,7 +11,7 @@
 	test_commit 1 &&
 	test_commit 2 &&
 	mkdir sub &&
-	test_commit 3 sub/3 &&
+	test_commit 3 sub/3.t &&
 	test_commit 4
 '
 
diff --git a/t/t4132-apply-removal.sh b/t/t4132-apply-removal.sh
index bb1ffe3..a2bc1cd 100755
--- a/t/t4132-apply-removal.sh
+++ b/t/t4132-apply-removal.sh
@@ -30,6 +30,7 @@
 	epocWest="1969-12-31 16:00:00.000000000 -0800" &&
 	 epocGMT="1970-01-01 00:00:00.000000000 +0000" &&
 	epocEast="1970-01-01 09:00:00.000000000 +0900" &&
+	epocWest2="1969-12-31 16:00:00 -08:00" &&
 
 	sed -e "s/TS0/$epocWest/" -e "s/TS1/$timeWest/" <c >createWest.patch &&
 	sed -e "s/TS0/$epocEast/" -e "s/TS1/$timeEast/" <c >createEast.patch &&
@@ -46,6 +47,7 @@
 	sed -e "s/TS0/$timeWest/" -e "s/TS1/$epocWest/" <d >removeWest.patch &&
 	sed -e "s/TS0/$timeEast/" -e "s/TS1/$epocEast/" <d >removeEast.patch &&
 	sed -e "s/TS0/$timeGMT/" -e "s/TS1/$epocGMT/" <d >removeGMT.patch &&
+	sed -e "s/TS0/$timeWest/" -e "s/TS1/$epocWest2/" <d >removeWest2.patch &&
 
 	echo something >something &&
 	>empty
diff --git a/t/t4133-apply-filenames.sh b/t/t4133-apply-filenames.sh
new file mode 100755
index 0000000..94da990
--- /dev/null
+++ b/t/t4133-apply-filenames.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Andreas Gruenbacher
+#
+
+test_description='git apply filename consistency check'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+	cat > bad1.patch <<EOF &&
+diff --git a/f b/f
+new file mode 100644
+index 0000000..d00491f
+--- /dev/null
++++ b/f-blah
+@@ -0,0 +1 @@
++1
+EOF
+	cat > bad2.patch <<EOF
+diff --git a/f b/f
+deleted file mode 100644
+index d00491f..0000000
+--- b/f-blah
++++ /dev/null
+@@ -1 +0,0 @@
+-1
+EOF
+'
+
+test_expect_success 'apply diff with inconsistent filenames in headers' '
+	test_must_fail git apply bad1.patch 2>err &&
+	grep "inconsistent new filename" err &&
+	test_must_fail git apply bad2.patch 2>err &&
+	grep "inconsistent old filename" err
+'
+
+test_done
diff --git a/t/t4134-apply-submodule.sh b/t/t4134-apply-submodule.sh
new file mode 100755
index 0000000..0043930
--- /dev/null
+++ b/t/t4134-apply-submodule.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Peter Collingbourne
+#
+
+test_description='git apply submodule tests'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+	cat > create-sm.patch <<EOF &&
+diff --git a/dir/sm b/dir/sm
+new file mode 160000
+index 0000000..0123456
+--- /dev/null
++++ b/dir/sm
+@@ -0,0 +1 @@
++Subproject commit 0123456789abcdef0123456789abcdef01234567
+EOF
+	cat > remove-sm.patch <<EOF
+diff --git a/dir/sm b/dir/sm
+deleted file mode 160000
+index 0123456..0000000
+--- a/dir/sm
++++ /dev/null
+@@ -1 +0,0 @@
+-Subproject commit 0123456789abcdef0123456789abcdef01234567
+EOF
+'
+
+test_expect_success 'removing a submodule also removes all leading subdirectories' '
+	git apply --index create-sm.patch &&
+	test -d dir/sm &&
+	git apply --index remove-sm.patch &&
+	test \! -d dir
+'
+
+test_done
diff --git a/t/t4135-apply-weird-filenames.sh b/t/t4135-apply-weird-filenames.sh
new file mode 100755
index 0000000..bf5dc57
--- /dev/null
+++ b/t/t4135-apply-weird-filenames.sh
@@ -0,0 +1,91 @@
+#!/bin/sh
+
+test_description='git apply with weird postimage filenames'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	vector=$TEST_DIRECTORY/t4135 &&
+
+	test_tick &&
+	git commit --allow-empty -m preimage &&
+	git tag preimage &&
+
+	reset_preimage() {
+		git checkout -f preimage^0 &&
+		git read-tree -u --reset HEAD &&
+		git update-index --refresh
+	} &&
+
+	test_when_finished "rm -f \"tab	embedded.txt\"" &&
+	test_when_finished "rm -f '\''\"quoteembedded\".txt'\''" &&
+	if touch -- "tab	embedded.txt" '\''"quoteembedded".txt'\''
+	then
+		test_set_prereq FUNNYNAMES
+	fi
+'
+
+try_filename() {
+	desc=$1
+	postimage=$2
+	prereq=${3:-}
+	exp1=${4:-success}
+	exp2=${5:-success}
+	exp3=${6:-success}
+
+	test_expect_$exp1 $prereq "$desc, git-style file creation patch" "
+		echo postimage >expected &&
+		reset_preimage &&
+		rm -f '$postimage' &&
+		git apply -v \"\$vector\"/'git-$desc.diff' &&
+		test_cmp expected '$postimage'
+	"
+
+	test_expect_$exp2 $prereq "$desc, traditional patch" "
+		echo postimage >expected &&
+		reset_preimage &&
+		echo preimage >'$postimage' &&
+		git apply -v \"\$vector\"/'diff-$desc.diff' &&
+		test_cmp expected '$postimage'
+	"
+
+	test_expect_$exp3 $prereq "$desc, traditional file creation patch" "
+		echo postimage >expected &&
+		reset_preimage &&
+		rm -f '$postimage' &&
+		git apply -v \"\$vector\"/'add-$desc.diff' &&
+		test_cmp expected '$postimage'
+	"
+}
+
+try_filename 'plain'            'postimage.txt'
+try_filename 'with spaces'      'post image.txt'
+try_filename 'with tab'         'post	image.txt' FUNNYNAMES
+try_filename 'with backslash'   'post\image.txt' BSLASHPSPEC
+try_filename 'with quote'       '"postimage".txt' FUNNYNAMES success failure success
+
+test_expect_success 'whitespace-damaged traditional patch' '
+	echo postimage >expected &&
+	reset_preimage &&
+	rm -f postimage.txt &&
+	git apply -v "$vector/damaged.diff" &&
+	test_cmp expected postimage.txt
+'
+
+test_expect_success 'traditional patch with colon in timezone' '
+	echo postimage >expected &&
+	reset_preimage &&
+	rm -f "post image.txt" &&
+	git apply "$vector/funny-tz.diff" &&
+	test_cmp expected "post image.txt"
+'
+
+test_expect_success 'traditional, whitespace-damaged, colon in timezone' '
+	echo postimage >expected &&
+	reset_preimage &&
+	rm -f "post image.txt" &&
+	git apply "$vector/damaged-tz.diff" &&
+	test_cmp expected "post image.txt"
+'
+
+test_done
diff --git a/t/t4135/.gitignore b/t/t4135/.gitignore
new file mode 100644
index 0000000..3e58e65
--- /dev/null
+++ b/t/t4135/.gitignore
@@ -0,0 +1,3 @@
+/file-creation/
+/trad-creation/
+/trad-modification/
diff --git a/t/t4135/add-plain.diff b/t/t4135/add-plain.diff
new file mode 100644
index 0000000..cf5970a
--- /dev/null
+++ b/t/t4135/add-plain.diff
@@ -0,0 +1,5 @@
+diff -pruN a/postimage.txt b/postimage.txt
+--- a/postimage.txt	1969-12-31 18:00:00.000000000 -0600
++++ b/postimage.txt	2010-08-18 20:13:31.484002255 -0500
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/add-with backslash.diff b/t/t4135/add-with backslash.diff
new file mode 100644
index 0000000..c6861e1
--- /dev/null
+++ b/t/t4135/add-with backslash.diff
@@ -0,0 +1,5 @@
+diff -pruN a/post\image.txt b/post\image.txt
+--- a/post\image.txt	1969-12-31 18:00:00.000000000 -0600
++++ b/post\image.txt	2010-08-18 20:13:31.692002255 -0500
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/add-with quote.diff b/t/t4135/add-with quote.diff
new file mode 100644
index 0000000..866de78
--- /dev/null
+++ b/t/t4135/add-with quote.diff
@@ -0,0 +1,5 @@
+diff -pruN a/"postimage".txt b/"postimage".txt
+--- a/"postimage".txt	1969-12-31 18:00:00.000000000 -0600
++++ b/"postimage".txt	2010-08-18 20:13:31.756002255 -0500
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/add-with spaces.diff b/t/t4135/add-with spaces.diff
new file mode 100644
index 0000000..a9a1212
--- /dev/null
+++ b/t/t4135/add-with spaces.diff
@@ -0,0 +1,5 @@
+diff -pruN a/post image.txt b/post image.txt
+--- a/post image.txt	1969-12-31 18:00:00.000000000 -0600
++++ b/post image.txt	2010-08-18 20:13:31.556002255 -0500
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/add-with tab.diff b/t/t4135/add-with tab.diff
new file mode 100644
index 0000000..bb67cb7
--- /dev/null
+++ b/t/t4135/add-with tab.diff
@@ -0,0 +1,5 @@
+diff -pruN a/post	image.txt b/post	image.txt
+--- a/post	image.txt	1969-12-31 18:00:00.000000000 -0600
++++ b/post	image.txt	2010-08-18 20:13:31.628002255 -0500
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/damaged-tz.diff b/t/t4135/damaged-tz.diff
new file mode 100644
index 0000000..07aaf08
--- /dev/null
+++ b/t/t4135/damaged-tz.diff
@@ -0,0 +1,5 @@
+diff -urN -X /usr/people/jes/exclude-linux linux-2.6.12-rc2-mm3-vanilla/post image.txt linux-2.6.12-rc2-mm3/post image.txt
+--- linux-2.6.12-rc2-mm3-vanilla/post image.txt 1969-12-31 16:00:00 -08:00
++++ linux-2.6.12-rc2-mm3/post image.txt 2005-04-12 02:14:06 -07:00
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/damaged.diff b/t/t4135/damaged.diff
new file mode 100644
index 0000000..68f7ede
--- /dev/null
+++ b/t/t4135/damaged.diff
@@ -0,0 +1,5 @@
+diff -pruN a/postimage.txt b/postimage.txt
+--- a/postimage.txt     1969-12-31 18:00:00.000000000 -0600
++++ b/postimage.txt     2010-08-18 20:13:31.484002255 -0500
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/diff-plain.diff b/t/t4135/diff-plain.diff
new file mode 100644
index 0000000..acedcfa
--- /dev/null
+++ b/t/t4135/diff-plain.diff
@@ -0,0 +1,5 @@
+--- postimage.txt.orig	2010-08-18 20:13:31.432002255 -0500
++++ postimage.txt	2010-08-18 20:13:31.432002255 -0500
+@@ -1 +1 @@
+-preimage
++postimage
diff --git a/t/t4135/diff-with backslash.diff b/t/t4135/diff-with backslash.diff
new file mode 100644
index 0000000..9068a61
--- /dev/null
+++ b/t/t4135/diff-with backslash.diff
@@ -0,0 +1,5 @@
+--- post\image.txt.orig	2010-08-18 20:13:31.680002255 -0500
++++ post\image.txt	2010-08-18 20:13:31.680002255 -0500
+@@ -1 +1 @@
+-preimage
++postimage
diff --git a/t/t4135/diff-with quote.diff b/t/t4135/diff-with quote.diff
new file mode 100644
index 0000000..c8e8cc1
--- /dev/null
+++ b/t/t4135/diff-with quote.diff
@@ -0,0 +1,5 @@
+--- "postimage".txt.orig	2010-08-18 20:13:31.744002255 -0500
++++ "postimage".txt	2010-08-18 20:13:31.744002255 -0500
+@@ -1 +1 @@
+-preimage
++postimage
diff --git a/t/t4135/diff-with spaces.diff b/t/t4135/diff-with spaces.diff
new file mode 100644
index 0000000..3512056
--- /dev/null
+++ b/t/t4135/diff-with spaces.diff
@@ -0,0 +1,5 @@
+--- post image.txt.orig	2010-08-18 20:13:31.544002255 -0500
++++ post image.txt	2010-08-18 20:13:31.544002255 -0500
+@@ -1 +1 @@
+-preimage
++postimage
diff --git a/t/t4135/diff-with tab.diff b/t/t4135/diff-with tab.diff
new file mode 100644
index 0000000..4e6d9b2
--- /dev/null
+++ b/t/t4135/diff-with tab.diff
@@ -0,0 +1,5 @@
+--- post	image.txt.orig	2010-08-18 20:13:31.616002255 -0500
++++ post	image.txt	2010-08-18 20:13:31.616002255 -0500
+@@ -1 +1 @@
+-preimage
++postimage
diff --git a/t/t4135/funny-tz.diff b/t/t4135/funny-tz.diff
new file mode 100644
index 0000000..998e3a8
--- /dev/null
+++ b/t/t4135/funny-tz.diff
@@ -0,0 +1,5 @@
+diff -urN -X /usr/people/jes/exclude-linux linux-2.6.12-rc2-mm3-vanilla/post image.txt linux-2.6.12-rc2-mm3/post image.txt
+--- linux-2.6.12-rc2-mm3-vanilla/post image.txt	1969-12-31 16:00:00 -08:00
++++ linux-2.6.12-rc2-mm3/post image.txt	2005-04-12 02:14:06 -07:00
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/git-plain.diff b/t/t4135/git-plain.diff
new file mode 100644
index 0000000..db47d1a
--- /dev/null
+++ b/t/t4135/git-plain.diff
@@ -0,0 +1,7 @@
+diff --git a/postimage.txt b/postimage.txt
+new file mode 100644
+index 0000000..eff0c54
+--- /dev/null
++++ b/postimage.txt
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/git-with backslash.diff b/t/t4135/git-with backslash.diff
new file mode 100644
index 0000000..0e84a10
--- /dev/null
+++ b/t/t4135/git-with backslash.diff
@@ -0,0 +1,7 @@
+diff --git "a/post\\image.txt" "b/post\\image.txt"
+new file mode 100644
+index 0000000..eff0c54
+--- /dev/null
++++ "b/post\\image.txt"
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/git-with quote.diff b/t/t4135/git-with quote.diff
new file mode 100644
index 0000000..bdbea8a
--- /dev/null
+++ b/t/t4135/git-with quote.diff
@@ -0,0 +1,7 @@
+diff --git "a/\"postimage\".txt" "b/\"postimage\".txt"
+new file mode 100644
+index 0000000..eff0c54
+--- /dev/null
++++ "b/\"postimage\".txt"
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/git-with spaces.diff b/t/t4135/git-with spaces.diff
new file mode 100644
index 0000000..baaa810
--- /dev/null
+++ b/t/t4135/git-with spaces.diff
@@ -0,0 +1,7 @@
+diff --git a/post image.txt b/post image.txt
+new file mode 100644
+index 0000000..eff0c54
+--- /dev/null
++++ b/post image.txt	
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/git-with tab.diff b/t/t4135/git-with tab.diff
new file mode 100644
index 0000000..cca3c92
--- /dev/null
+++ b/t/t4135/git-with tab.diff
@@ -0,0 +1,7 @@
+diff --git "a/post\timage.txt" "b/post\timage.txt"
+new file mode 100644
+index 0000000..eff0c54
+--- /dev/null
++++ "b/post\timage.txt"
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/make-patches b/t/t4135/make-patches
new file mode 100755
index 0000000..f5f45dd
--- /dev/null
+++ b/t/t4135/make-patches
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+do_filename() {
+	desc=$1
+	postimage=$2
+
+	rm -fr file-creation &&
+	git init file-creation &&
+	(
+		cd file-creation &&
+		git commit --allow-empty -m init &&
+		echo postimage >"$postimage" &&
+		git add -N "$postimage" &&
+		git diff HEAD >"../git-$desc.diff"
+	) &&
+
+	rm -fr trad-modification &&
+	mkdir trad-modification &&
+	(
+		cd trad-modification &&
+		echo preimage >"$postimage.orig" &&
+		echo postimage >"$postimage" &&
+		! diff -u "$postimage.orig" "$postimage" >"../diff-$desc.diff"
+	) &&
+
+	rm -fr trad-creation &&
+	mkdir trad-creation &&
+	(
+		cd trad-creation &&
+		mkdir a b &&
+		echo postimage >"b/$postimage" &&
+		! diff -pruN a b >"../add-$desc.diff"
+	)
+}
+
+do_filename plain postimage.txt &&
+do_filename 'with spaces' 'post image.txt' &&
+do_filename 'with tab' 'post	image.txt' &&
+do_filename 'with backslash' 'post\image.txt' &&
+do_filename 'with quote' '"postimage".txt' &&
+expand add-plain.diff >damaged.diff ||
+{
+	echo >&2 Failed. &&
+	exit 1
+}
diff --git a/t/t4136-apply-check.sh b/t/t4136-apply-check.sh
new file mode 100755
index 0000000..a321f7c
--- /dev/null
+++ b/t/t4136-apply-check.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+test_description='git apply should exit non-zero with unrecognized input.'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	test_commit 1
+'
+
+test_expect_success 'apply --check exits non-zero with unrecognized input' '
+	test_must_fail git apply --check - <<-\EOF
+	I am not a patch
+	I look nothing like a patch
+	git apply must fail
+	EOF
+'
+
+test_done
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index a12bf84..cdafd7e 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -4,66 +4,71 @@
 
 . ./test-lib.sh
 
-cat >msg <<EOF
-second
+test_expect_success 'setup: messages' '
+	cat >msg <<-\EOF &&
+	second
 
-Lorem ipsum dolor sit amet, consectetuer sadipscing elitr, sed diam nonumy
-eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
-voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita
-kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem
-ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
-tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
-vero eos et accusam et justo duo dolores et ea rebum.
+	Lorem ipsum dolor sit amet, consectetuer sadipscing elitr, sed diam nonumy
+	eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
+	voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita
+	kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem
+	ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
+	tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
+	vero eos et accusam et justo duo dolores et ea rebum.
 
-	Duis autem vel eum iriure dolor in hendrerit in vulputate velit
-	esse molestie consequat, vel illum dolore eu feugiat nulla facilisis
-	at vero eros et accumsan et iusto odio dignissim qui blandit
-	praesent luptatum zzril delenit augue duis dolore te feugait nulla
-	facilisi.
+	EOF
+	q_to_tab <<-\EOF >>msg &&
+	QDuis autem vel eum iriure dolor in hendrerit in vulputate velit
+	Qesse molestie consequat, vel illum dolore eu feugiat nulla facilisis
+	Qat vero eros et accumsan et iusto odio dignissim qui blandit
+	Qpraesent luptatum zzril delenit augue duis dolore te feugait nulla
+	Qfacilisi.
+	EOF
+	cat >>msg <<-\EOF &&
 
+	Lorem ipsum dolor sit amet,
+	consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut
+	laoreet dolore magna aliquam erat volutpat.
 
-Lorem ipsum dolor sit amet,
-consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut
-laoreet dolore magna aliquam erat volutpat.
+	  git
+	  ---
+	  +++
 
-  git
-  ---
-  +++
+	Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit
+	lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure
+	dolor in hendrerit in vulputate velit esse molestie consequat, vel illum
+	dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio
+	dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te
+	feugait nulla facilisi.
+	EOF
 
-Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit
-lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure
-dolor in hendrerit in vulputate velit esse molestie consequat, vel illum
-dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio
-dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te
-feugait nulla facilisi.
-EOF
+	cat >failmail <<-\EOF &&
+	From foo@example.com Fri May 23 10:43:49 2008
+	From:	foo@example.com
+	To:	bar@example.com
+	Subject: Re: [RFC/PATCH] git-foo.sh
+	Date:	Fri, 23 May 2008 05:23:42 +0200
 
-cat >failmail <<EOF
-From foo@example.com Fri May 23 10:43:49 2008
-From:	foo@example.com
-To:	bar@example.com
-Subject: Re: [RFC/PATCH] git-foo.sh
-Date:	Fri, 23 May 2008 05:23:42 +0200
+	Sometimes we have to find out that there'\''s nothing left.
 
-Sometimes we have to find out that there's nothing left.
+	EOF
 
-EOF
+	cat >pine <<-\EOF &&
+	From MAILER-DAEMON Fri May 23 10:43:49 2008
+	Date: 23 May 2008 05:23:42 +0200
+	From: Mail System Internal Data <MAILER-DAEMON@example.com>
+	Subject: DON'\''T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA
+	Message-ID: <foo-0001@example.com>
 
-cat >pine <<EOF
-From MAILER-DAEMON Fri May 23 10:43:49 2008
-Date: 23 May 2008 05:23:42 +0200
-From: Mail System Internal Data <MAILER-DAEMON@example.com>
-Subject: DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA
-Message-ID: <foo-0001@example.com>
+	This text is part of the internal format of your mail folder, and is not
+	a real message.  It is created automatically by the mail system software.
+	If deleted, important folder data will be lost, and it will be re-created
+	with the data reset to initial values.
 
-This text is part of the internal format of your mail folder, and is not
-a real message.  It is created automatically by the mail system software.
-If deleted, important folder data will be lost, and it will be re-created
-with the data reset to initial values.
+	EOF
 
-EOF
-
-echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" >expected
+	signoff="Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
+'
 
 test_expect_success setup '
 	echo hello >file &&
@@ -71,62 +76,140 @@
 	test_tick &&
 	git commit -m first &&
 	git tag first &&
+
 	echo world >>file &&
 	git add file &&
 	test_tick &&
 	git commit -s -F msg &&
 	git tag second &&
+
 	git format-patch --stdout first >patch1 &&
+	{
+		echo "X-Fake-Field: Line One" &&
+		echo "X-Fake-Field: Line Two" &&
+		echo "X-Fake-Field: Line Three" &&
+		git format-patch --stdout first | sed -e "1d"
+	} > patch1.eml &&
+	{
+		echo "X-Fake-Field: Line One" &&
+		echo "X-Fake-Field: Line Two" &&
+		echo "X-Fake-Field: Line Three" &&
+		git format-patch --stdout first | sed -e "1d"
+	} | append_cr >patch1-crlf.eml &&
+	{
+		printf "%255s\\n" ""
+		echo "X-Fake-Field: Line One" &&
+		echo "X-Fake-Field: Line Two" &&
+		echo "X-Fake-Field: Line Three" &&
+		git format-patch --stdout first | sed -e "1d"
+	} > patch1-ws.eml &&
+
 	sed -n -e "3,\$p" msg >file &&
 	git add file &&
 	test_tick &&
 	git commit -m third &&
+
 	git format-patch --stdout first >patch2	&&
+
 	git checkout -b lorem &&
 	sed -n -e "11,\$p" msg >file &&
 	head -n 9 msg >>file &&
 	test_tick &&
 	git commit -a -m "moved stuff" &&
+
 	echo goodbye >another &&
 	git add another &&
 	test_tick &&
 	git commit -m "added another file" &&
-	git format-patch --stdout master >lorem-move.patch
+
+	git format-patch --stdout master >lorem-move.patch &&
+	git format-patch --no-prefix --stdout master >lorem-zero.patch &&
+
+	git checkout -b rename &&
+	git mv file renamed &&
+	git commit -m "renamed a file" &&
+
+	git format-patch -M --stdout lorem >rename.patch &&
+
+	git reset --soft lorem^ &&
+	git commit -m "renamed a file and added another" &&
+
+	git format-patch -M --stdout lorem^ >rename-add.patch &&
+
+	# reset time
+	sane_unset test_tick &&
+	test_tick
 '
 
-# reset time
-unset test_tick
-test_tick
-
 test_expect_success 'am applies patch correctly' '
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
 	git checkout first &&
 	test_tick &&
 	git am <patch1 &&
 	! test -d .git/rebase-apply &&
-	test -z "$(git diff second)" &&
+	git diff --exit-code second &&
 	test "$(git rev-parse second)" = "$(git rev-parse HEAD)" &&
 	test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)"
 '
 
-GIT_AUTHOR_NAME="Another Thor"
-GIT_AUTHOR_EMAIL="a.thor@example.com"
-GIT_COMMITTER_NAME="Co M Miter"
-GIT_COMMITTER_EMAIL="c.miter@example.com"
-export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL
+test_expect_success 'am applies patch e-mail not in a mbox' '
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
+	git checkout first &&
+	git am patch1.eml &&
+	! test -d .git/rebase-apply &&
+	git diff --exit-code second &&
+	test "$(git rev-parse second)" = "$(git rev-parse HEAD)" &&
+	test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)"
+'
+
+test_expect_success 'am applies patch e-mail not in a mbox with CRLF' '
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
+	git checkout first &&
+	git am patch1-crlf.eml &&
+	! test -d .git/rebase-apply &&
+	git diff --exit-code second &&
+	test "$(git rev-parse second)" = "$(git rev-parse HEAD)" &&
+	test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)"
+'
+
+test_expect_success 'am applies patch e-mail with preceding whitespace' '
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
+	git checkout first &&
+	git am patch1-ws.eml &&
+	! test -d .git/rebase-apply &&
+	git diff --exit-code second &&
+	test "$(git rev-parse second)" = "$(git rev-parse HEAD)" &&
+	test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)"
+'
+
+test_expect_success 'setup: new author and committer' '
+	GIT_AUTHOR_NAME="Another Thor" &&
+	GIT_AUTHOR_EMAIL="a.thor@example.com" &&
+	GIT_COMMITTER_NAME="Co M Miter" &&
+	GIT_COMMITTER_EMAIL="c.miter@example.com" &&
+	export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL
+'
 
 compare () {
-	test "$(git cat-file commit "$2" | grep "^$1 ")" = \
-	     "$(git cat-file commit "$3" | grep "^$1 ")"
+	a=$(git cat-file commit "$2" | grep "^$1 ") &&
+	b=$(git cat-file commit "$3" | grep "^$1 ") &&
+	test "$a" = "$b"
 }
 
 test_expect_success 'am changes committer and keeps author' '
 	test_tick &&
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
 	git checkout first &&
 	git am patch2 &&
 	! test -d .git/rebase-apply &&
 	test "$(git rev-parse master^^)" = "$(git rev-parse HEAD^^)" &&
-	test -z "$(git diff master..HEAD)" &&
-	test -z "$(git diff master^..HEAD^)" &&
+	git diff --exit-code master..HEAD &&
+	git diff --exit-code master^..HEAD^ &&
 	compare author master HEAD &&
 	compare author master^ HEAD^ &&
 	test "$GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" = \
@@ -134,41 +217,65 @@
 '
 
 test_expect_success 'am --signoff adds Signed-off-by: line' '
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
 	git checkout -b master2 first &&
 	git am --signoff <patch2 &&
+	printf "%s\n" "$signoff" >expected &&
 	echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" >>expected &&
 	git cat-file commit HEAD^ | grep "Signed-off-by:" >actual &&
-	test_cmp actual expected &&
+	test_cmp expected actual &&
 	echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" >expected &&
 	git cat-file commit HEAD | grep "Signed-off-by:" >actual &&
-	test_cmp actual expected
+	test_cmp expected actual
 '
 
 test_expect_success 'am stays in branch' '
-	test "refs/heads/master2" = "$(git symbolic-ref HEAD)"
+	echo refs/heads/master2 >expected &&
+	git symbolic-ref HEAD >actual &&
+	test_cmp expected actual
 '
 
 test_expect_success 'am --signoff does not add Signed-off-by: line if already there' '
 	git format-patch --stdout HEAD^ >patch3 &&
-	sed -e "/^Subject/ s,\[PATCH,Re: Re: Re: & 1/5 v2," patch3 >patch4
+	sed -e "/^Subject/ s,\[PATCH,Re: Re: Re: & 1/5 v2] [foo," patch3 >patch4 &&
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
 	git checkout HEAD^ &&
 	git am --signoff patch4 &&
-	test "$(git cat-file commit HEAD | grep -c "^Signed-off-by:")" -eq 1
+	git cat-file commit HEAD >actual &&
+	test $(grep -c "^Signed-off-by:" actual) -eq 1
 '
 
 test_expect_success 'am without --keep removes Re: and [PATCH] stuff' '
-	test "$(git rev-parse HEAD)" = "$(git rev-parse master2)"
+	git rev-parse HEAD >expected &&
+	git rev-parse master2 >actual &&
+	test_cmp expected actual
 '
 
 test_expect_success 'am --keep really keeps the subject' '
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
 	git checkout HEAD^ &&
 	git am --keep patch4 &&
 	! test -d .git/rebase-apply &&
-	git cat-file commit HEAD |
-		fgrep "Re: Re: Re: [PATCH 1/5 v2] third"
+	git cat-file commit HEAD >actual &&
+	grep "Re: Re: Re: \[PATCH 1/5 v2\] \[foo\] third" actual
+'
+
+test_expect_success 'am --keep-non-patch really keeps the non-patch part' '
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
+	git checkout HEAD^ &&
+	git am --keep-non-patch patch4 &&
+	! test -d .git/rebase-apply &&
+	git cat-file commit HEAD >actual &&
+	grep "^\[foo\] third" actual
 '
 
 test_expect_success 'am -3 falls back to 3-way merge' '
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
 	git checkout -b lorem2 master2 &&
 	sed -n -e "3,\$p" msg >file &&
 	head -n 9 msg >>file &&
@@ -177,34 +284,89 @@
 	git commit -m "copied stuff" &&
 	git am -3 lorem-move.patch &&
 	! test -d .git/rebase-apply &&
-	test -z "$(git diff lorem)"
+	git diff --exit-code lorem
+'
+
+test_expect_success 'am -3 -p0 can read --no-prefix patch' '
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
+	git checkout -b lorem3 master2 &&
+	sed -n -e "3,\$p" msg >file &&
+	head -n 9 msg >>file &&
+	git add file &&
+	test_tick &&
+	git commit -m "copied stuff" &&
+	git am -3 -p0 lorem-zero.patch &&
+	! test -d .git/rebase-apply &&
+	git diff --exit-code lorem
+'
+
+test_expect_success 'am can rename a file' '
+	grep "^rename from" rename.patch &&
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
+	git checkout lorem^0 &&
+	git am rename.patch &&
+	! test -d .git/rebase-apply &&
+	git update-index --refresh &&
+	git diff --exit-code rename
+'
+
+test_expect_success 'am -3 can rename a file' '
+	grep "^rename from" rename.patch &&
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
+	git checkout lorem^0 &&
+	git am -3 rename.patch &&
+	! test -d .git/rebase-apply &&
+	git update-index --refresh &&
+	git diff --exit-code rename
+'
+
+test_expect_success 'am -3 can rename a file after falling back to 3-way merge' '
+	grep "^rename from" rename-add.patch &&
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
+	git checkout lorem^0 &&
+	git am -3 rename-add.patch &&
+	! test -d .git/rebase-apply &&
+	git update-index --refresh &&
+	git diff --exit-code rename
 '
 
 test_expect_success 'am -3 -q is quiet' '
+	rm -fr .git/rebase-apply &&
+	git checkout -f lorem2 &&
 	git reset master2 --hard &&
 	sed -n -e "3,\$p" msg >file &&
 	head -n 9 msg >>file &&
 	git add file &&
 	test_tick &&
 	git commit -m "copied stuff" &&
-	git am -3 -q lorem-move.patch > output.out 2>&1 &&
+	git am -3 -q lorem-move.patch >output.out 2>&1 &&
 	! test -s output.out
 '
 
 test_expect_success 'am pauses on conflict' '
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
 	git checkout lorem2^^ &&
 	test_must_fail git am lorem-move.patch &&
 	test -d .git/rebase-apply
 '
 
 test_expect_success 'am --skip works' '
+	echo goodbye >expected &&
 	git am --skip &&
 	! test -d .git/rebase-apply &&
-	test -z "$(git diff lorem2^^ -- file)" &&
-	test goodbye = "$(cat another)"
+	git diff --exit-code lorem2^^ -- file &&
+	test_cmp expected another
 '
 
 test_expect_success 'am --resolved works' '
+	echo goodbye >expected &&
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
 	git checkout lorem2^^ &&
 	test_must_fail git am lorem-move.patch &&
 	test -d .git/rebase-apply &&
@@ -212,22 +374,29 @@
 	git add file &&
 	git am --resolved &&
 	! test -d .git/rebase-apply &&
-	test goodbye = "$(cat another)"
+	test_cmp expected another
 '
 
 test_expect_success 'am takes patches from a Pine mailbox' '
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
 	git checkout first &&
 	cat pine patch1 | git am &&
 	! test -d .git/rebase-apply &&
-	test -z "$(git diff master^..HEAD)"
+	git diff --exit-code master^..HEAD
 '
 
 test_expect_success 'am fails on mail without patch' '
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
 	test_must_fail git am <failmail &&
-	rm -r .git/rebase-apply/
+	git am --abort &&
+	! test -d .git/rebase-apply
 '
 
 test_expect_success 'am fails on empty patch' '
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
 	echo "---" >>failmail &&
 	test_must_fail git am <failmail &&
 	git am --skip &&
@@ -236,28 +405,34 @@
 
 test_expect_success 'am works from stdin in subdirectory' '
 	rm -fr subdir &&
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
 	git checkout first &&
 	(
 		mkdir -p subdir &&
 		cd subdir &&
 		git am <../patch1
 	) &&
-	test -z "$(git diff second)"
+	git diff --exit-code second
 '
 
 test_expect_success 'am works from file (relative path given) in subdirectory' '
 	rm -fr subdir &&
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
 	git checkout first &&
 	(
 		mkdir -p subdir &&
 		cd subdir &&
 		git am ../patch1
 	) &&
-	test -z "$(git diff second)"
+	git diff --exit-code second
 '
 
 test_expect_success 'am works from file (absolute path given) in subdirectory' '
 	rm -fr subdir &&
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
 	git checkout first &&
 	P=$(pwd) &&
 	(
@@ -265,27 +440,31 @@
 		cd subdir &&
 		git am "$P/patch1"
 	) &&
-	test -z "$(git diff second)"
+	git diff --exit-code second
 '
 
 test_expect_success 'am --committer-date-is-author-date' '
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
 	git checkout first &&
 	test_tick &&
 	git am --committer-date-is-author-date patch1 &&
-	git cat-file commit HEAD | sed -e "/^$/q" >head1 &&
-	at=$(sed -ne "/^author /s/.*> //p" head1) &&
-	ct=$(sed -ne "/^committer /s/.*> //p" head1) &&
-	test "$at" = "$ct"
+	git cat-file commit HEAD | sed -e "/^\$/q" >head1 &&
+	sed -ne "/^author /s/.*> //p" head1 >at &&
+	sed -ne "/^committer /s/.*> //p" head1 >ct &&
+	test_cmp at ct
 '
 
 test_expect_success 'am without --committer-date-is-author-date' '
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
 	git checkout first &&
 	test_tick &&
 	git am patch1 &&
-	git cat-file commit HEAD | sed -e "/^$/q" >head1 &&
-	at=$(sed -ne "/^author /s/.*> //p" head1) &&
-	ct=$(sed -ne "/^committer /s/.*> //p" head1) &&
-	test "$at" != "$ct"
+	git cat-file commit HEAD | sed -e "/^\$/q" >head1 &&
+	sed -ne "/^author /s/.*> //p" head1 >at &&
+	sed -ne "/^committer /s/.*> //p" head1 >ct &&
+	! test_cmp at ct
 '
 
 # This checks for +0000 because TZ is set to UTC and that should
@@ -293,42 +472,62 @@
 # by test_tick that uses -0700 timezone; if this feature does not
 # work, we will see that instead of +0000.
 test_expect_success 'am --ignore-date' '
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
 	git checkout first &&
 	test_tick &&
 	git am --ignore-date patch1 &&
-	git cat-file commit HEAD | sed -e "/^$/q" >head1 &&
-	at=$(sed -ne "/^author /s/.*> //p" head1) &&
-	echo "$at" | grep "+0000"
+	git cat-file commit HEAD | sed -e "/^\$/q" >head1 &&
+	sed -ne "/^author /s/.*> //p" head1 >at &&
+	grep "+0000" at
 '
 
 test_expect_success 'am into an unborn branch' '
+	git rev-parse first^{tree} >expected &&
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
 	rm -fr subdir &&
-	mkdir -p subdir &&
+	mkdir subdir &&
 	git format-patch --numbered-files -o subdir -1 first &&
 	(
 		cd subdir &&
 		git init &&
 		git am 1
 	) &&
-	result=$(
-		cd subdir && git rev-parse HEAD^{tree}
+	(
+		cd subdir &&
+		git rev-parse HEAD^{tree} >../actual
 	) &&
-	test "z$result" = "z$(git rev-parse first^{tree})"
+	test_cmp expected actual
 '
 
 test_expect_success 'am newline in subject' '
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
 	git checkout first &&
 	test_tick &&
-	sed -e "s/second/second \\\n foo/" patch1 > patchnl &&
-	git am < patchnl > output.out 2>&1 &&
-	grep "^Applying: second \\\n foo$" output.out
+	sed -e "s/second/second \\\n foo/" patch1 >patchnl &&
+	git am <patchnl >output.out 2>&1 &&
+	test_i18ngrep "^Applying: second \\\n foo$" output.out
 '
 
 test_expect_success 'am -q is quiet' '
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
 	git checkout first &&
 	test_tick &&
-	git am -q < patch1 > output.out 2>&1 &&
+	git am -q <patch1 >output.out 2>&1 &&
 	! test -s output.out
 '
 
+test_expect_success 'am empty-file does not infloop' '
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
+	touch empty-file &&
+	test_tick &&
+	test_must_fail git am empty-file 2>actual &&
+	echo Patch format detection failed. >expected &&
+	test_i18ncmp expected actual
+'
+
 test_done
diff --git a/t/t4151-am-abort.sh b/t/t4151-am-abort.sh
index 2b912d7..1176bcc 100755
--- a/t/t4151-am-abort.sh
+++ b/t/t4151-am-abort.sh
@@ -45,9 +45,10 @@
 
 	test_expect_success "am$with3 --skip continue after failed am$with3" '
 		test_must_fail git am$with3 --skip >output &&
-		test "$(grep "^Applying" output)" = "Applying: 6" &&
-		test_cmp file-2-expect file-2 &&
-		test ! -f .git/rr-cache/MERGE_RR
+		test_i18ngrep "^Applying" output >output.applying &&
+		test_i18ngrep "^Applying: 6$" output.applying &&
+		test_i18ncmp file-2-expect file-2 &&
+		test ! -f .git/MERGE_RR
 	'
 
 	test_expect_success "am --abort goes back after failed am$with3" '
@@ -57,9 +58,18 @@
 		test_cmp expect actual &&
 		test_cmp file-2-expect file-2 &&
 		git diff-index --exit-code --cached HEAD &&
-		test ! -f .git/rr-cache/MERGE_RR
+		test ! -f .git/MERGE_RR
 	'
 
 done
 
+test_expect_success 'am --abort will keep the local commits intact' '
+	test_must_fail git am 0004-*.patch &&
+	test_commit unrelated &&
+	git rev-parse HEAD >expect &&
+	git am --abort &&
+	git rev-parse HEAD >actual &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t4152-am-subjects.sh b/t/t4152-am-subjects.sh
new file mode 100755
index 0000000..4c68245
--- /dev/null
+++ b/t/t4152-am-subjects.sh
@@ -0,0 +1,77 @@
+#!/bin/sh
+
+test_description='test subject preservation with format-patch | am'
+. ./test-lib.sh
+
+make_patches() {
+	type=$1
+	subject=$2
+	test_expect_success "create patches with $type subject" '
+		git reset --hard baseline &&
+		echo $type >file &&
+		git commit -a -m "$subject" &&
+		git format-patch -1 --stdout >$type.patch &&
+		git format-patch -1 --stdout -k >$type-k.patch
+	'
+}
+
+check_subject() {
+	git reset --hard baseline &&
+	git am $2 $1.patch &&
+	git log -1 --pretty=format:%B >actual &&
+	test_cmp expect actual
+}
+
+test_expect_success 'setup baseline commit' '
+	test_commit baseline file
+'
+
+SHORT_SUBJECT='short subject'
+make_patches short "$SHORT_SUBJECT"
+
+LONG_SUBJECT1='this is a long subject that is virtually guaranteed'
+LONG_SUBJECT2='to require wrapping via format-patch if it is all'
+LONG_SUBJECT3='going to appear on a single line'
+LONG_SUBJECT="$LONG_SUBJECT1 $LONG_SUBJECT2 $LONG_SUBJECT3"
+make_patches long "$LONG_SUBJECT"
+
+MULTILINE_SUBJECT="$LONG_SUBJECT1
+$LONG_SUBJECT2
+$LONG_SUBJECT3"
+make_patches multiline "$MULTILINE_SUBJECT"
+
+echo "$SHORT_SUBJECT" >expect
+test_expect_success 'short subject preserved (format-patch | am)' '
+	check_subject short
+'
+test_expect_success 'short subject preserved (format-patch -k | am)' '
+	check_subject short-k
+'
+test_expect_success 'short subject preserved (format-patch -k | am -k)' '
+	check_subject short-k -k
+'
+
+echo "$LONG_SUBJECT" >expect
+test_expect_success 'long subject preserved (format-patch | am)' '
+	check_subject long
+'
+test_expect_success 'long subject preserved (format-patch -k | am)' '
+	check_subject long-k
+'
+test_expect_success 'long subject preserved (format-patch -k | am -k)' '
+	check_subject long-k -k
+'
+
+echo "$LONG_SUBJECT" >expect
+test_expect_success 'multiline subject unwrapped (format-patch | am)' '
+	check_subject multiline
+'
+test_expect_success 'multiline subject unwrapped (format-patch -k | am)' '
+	check_subject multiline-k
+'
+echo "$MULTILINE_SUBJECT" >expect
+test_expect_success 'multiline subject preserved (format-patch -k | am -k)' '
+	check_subject multiline-k -k
+'
+
+test_done
diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh
index a6bc028..3ab670d 100755
--- a/t/t4200-rerere.sh
+++ b/t/t4200-rerere.sh
@@ -4,220 +4,391 @@
 #
 
 test_description='git rerere
+
+! [fifth] version1
+ ! [first] first
+  ! [fourth] version1
+   ! [master] initial
+    ! [second] prefer first over second
+     ! [third] version2
+------
+     + [third] version2
++      [fifth] version1
+  +    [fourth] version1
++ +  + [third^] third
+    -  [second] prefer first over second
+ +  +  [first] first
+    +  [second^] second
+++++++ [master] initial
 '
 
 . ./test-lib.sh
 
-cat > a1 << EOF
-Some title
-==========
-Whether 'tis nobler in the mind to suffer
-The slings and arrows of outrageous fortune,
-Or to take arms against a sea of troubles,
-And by opposing end them? To die: to sleep;
-No more; and by a sleep to say we end
-The heart-ache and the thousand natural shocks
-That flesh is heir to, 'tis a consummation
-Devoutly to be wish'd.
-EOF
+test_expect_success 'setup' '
+	cat >a1 <<-\EOF &&
+	Some title
+	==========
+	Whether '\''tis nobler in the mind to suffer
+	The slings and arrows of outrageous fortune,
+	Or to take arms against a sea of troubles,
+	And by opposing end them? To die: to sleep;
+	No more; and by a sleep to say we end
+	The heart-ache and the thousand natural shocks
+	That flesh is heir to, '\''tis a consummation
+	Devoutly to be wish'\''d.
+	EOF
 
-git add a1
-git commit -q -a -m initial
+	git add a1 &&
+	test_tick &&
+	git commit -q -a -m initial &&
 
-git checkout -b first
-cat >> a1 << EOF
-Some title
-==========
-To die, to sleep;
-To sleep: perchance to dream: ay, there's the rub;
-For in that sleep of death what dreams may come
-When we have shuffled off this mortal coil,
-Must give us pause: there's the respect
-That makes calamity of so long life;
-EOF
-git commit -q -a -m first
+	cat >>a1 <<-\EOF &&
+	Some title
+	==========
+	To die, to sleep;
+	To sleep: perchance to dream: ay, there'\''s the rub;
+	For in that sleep of death what dreams may come
+	When we have shuffled off this mortal coil,
+	Must give us pause: there'\''s the respect
+	That makes calamity of so long life;
+	EOF
 
-git checkout -b second master
-git show first:a1 |
-sed -e 's/To die, t/To die! T/' -e 's/Some title/Some Title/' > a1
-echo "* END *" >>a1
-git commit -q -a -m second
+	git checkout -b first &&
+	test_tick &&
+	git commit -q -a -m first &&
+
+	git checkout -b second master &&
+	git show first:a1 |
+	sed -e "s/To die, t/To die! T/" -e "s/Some title/Some Title/" >a1 &&
+	echo "* END *" >>a1 &&
+	test_tick &&
+	git commit -q -a -m second
+'
 
 test_expect_success 'nothing recorded without rerere' '
-	(rm -rf .git/rr-cache; git config rerere.enabled false) &&
+	rm -rf .git/rr-cache &&
+	git config rerere.enabled false &&
 	test_must_fail git merge first &&
 	! test -d .git/rr-cache
 '
 
-# activate rerere, old style
-test_expect_success 'conflicting merge' '
+test_expect_success 'activate rerere, old style (conflicting merge)' '
 	git reset --hard &&
 	mkdir .git/rr-cache &&
-	git config --unset rerere.enabled &&
-	test_must_fail git merge first
-'
+	test_might_fail git config --unset rerere.enabled &&
+	test_must_fail git merge first &&
 
-sha1=$(perl -pe 's/	.*//' .git/MERGE_RR)
-rr=.git/rr-cache/$sha1
-test_expect_success 'recorded preimage' "grep ^=======$ $rr/preimage"
+	sha1=$("$PERL_PATH" -pe "s/	.*//" .git/MERGE_RR) &&
+	rr=.git/rr-cache/$sha1 &&
+	grep "^=======\$" $rr/preimage &&
+	! test -f $rr/postimage &&
+	! test -f $rr/thisimage
+'
 
 test_expect_success 'rerere.enabled works, too' '
 	rm -rf .git/rr-cache &&
 	git config rerere.enabled true &&
 	git reset --hard &&
 	test_must_fail git merge first &&
+
+	sha1=$("$PERL_PATH" -pe "s/	.*//" .git/MERGE_RR) &&
+	rr=.git/rr-cache/$sha1 &&
 	grep ^=======$ $rr/preimage
 '
 
-test_expect_success 'no postimage or thisimage yet' \
-	"test ! -f $rr/postimage -a ! -f $rr/thisimage"
-
-test_expect_success 'preimage has right number of lines' '
-
-	cnt=$(sed -ne "/^<<<<<<</,/^>>>>>>>/p" $rr/preimage | wc -l) &&
-	test $cnt = 13
-
-'
-
-git show first:a1 > a1
-
-cat > expect << EOF
---- a/a1
-+++ b/a1
-@@ -1,4 +1,4 @@
--Some Title
-+Some title
- ==========
- Whether 'tis nobler in the mind to suffer
- The slings and arrows of outrageous fortune,
-@@ -8,21 +8,11 @@
- The heart-ache and the thousand natural shocks
- That flesh is heir to, 'tis a consummation
- Devoutly to be wish'd.
--<<<<<<<
--Some Title
--==========
--To die! To sleep;
--=======
- Some title
- ==========
- To die, to sleep;
-->>>>>>>
- To sleep: perchance to dream: ay, there's the rub;
- For in that sleep of death what dreams may come
- When we have shuffled off this mortal coil,
- Must give us pause: there's the respect
- That makes calamity of so long life;
--<<<<<<<
--=======
--* END *
-->>>>>>>
-EOF
-git rerere diff > out
-
-test_expect_success 'rerere diff' 'test_cmp expect out'
-
-cat > expect << EOF
-a1
-EOF
-
-git rerere status > out
-
-test_expect_success 'rerere status' 'test_cmp expect out'
-
-test_expect_success 'commit succeeds' \
-	"git commit -q -a -m 'prefer first over second'"
-
-test_expect_success 'recorded postimage' "test -f $rr/postimage"
-
-test_expect_success 'another conflicting merge' '
-	git checkout -b third master &&
-	git show second^:a1 | sed "s/To die: t/To die! T/" > a1 &&
-	git commit -q -a -m third &&
-	test_must_fail git pull . first
-'
-
-git show first:a1 | sed 's/To die: t/To die! T/' > expect
-test_expect_success 'rerere kicked in' "! grep ^=======$ a1"
-
-test_expect_success 'rerere prefers first change' 'test_cmp a1 expect'
-
-rm $rr/postimage
-echo "$sha1	a1" | perl -pe 'y/\012/\000/' > .git/MERGE_RR
-
-test_expect_success 'rerere clear' 'git rerere clear'
-
-test_expect_success 'clear removed the directory' "test ! -d $rr"
-
-mkdir $rr
-echo Hello > $rr/preimage
-echo World > $rr/postimage
-
-sha2=4000000000000000000000000000000000000000
-rr2=.git/rr-cache/$sha2
-mkdir $rr2
-echo Hello > $rr2/preimage
-
-almost_15_days_ago=$((60-15*86400))
-just_over_15_days_ago=$((-1-15*86400))
-almost_60_days_ago=$((60-60*86400))
-just_over_60_days_ago=$((-1-60*86400))
-
-test-chmtime =$almost_60_days_ago $rr/preimage
-test-chmtime =$almost_15_days_ago $rr2/preimage
-
-test_expect_success 'garbage collection (part1)' 'git rerere gc'
-
-test_expect_success 'young records still live' \
-	"test -f $rr/preimage && test -f $rr2/preimage"
-
-test-chmtime =$just_over_60_days_ago $rr/preimage
-test-chmtime =$just_over_15_days_ago $rr2/preimage
-
-test_expect_success 'garbage collection (part2)' 'git rerere gc'
-
-test_expect_success 'old records rest in peace' \
-	"test ! -f $rr/preimage && test ! -f $rr2/preimage"
-
-test_expect_success 'file2 added differently in two branches' '
+test_expect_success 'set up rr-cache' '
+	rm -rf .git/rr-cache &&
+	git config rerere.enabled true &&
 	git reset --hard &&
+	test_must_fail git merge first &&
+	sha1=$("$PERL_PATH" -pe "s/	.*//" .git/MERGE_RR) &&
+	rr=.git/rr-cache/$sha1
+'
+
+test_expect_success 'rr-cache looks sane' '
+	# no postimage or thisimage yet
+	! test -f $rr/postimage &&
+	! test -f $rr/thisimage &&
+
+	# preimage has right number of lines
+	cnt=$(sed -ne "/^<<<<<<</,/^>>>>>>>/p" $rr/preimage | wc -l) &&
+	echo $cnt &&
+	test $cnt = 13
+'
+
+test_expect_success 'rerere diff' '
+	git show first:a1 >a1 &&
+	cat >expect <<-\EOF &&
+	--- a/a1
+	+++ b/a1
+	@@ -1,4 +1,4 @@
+	-Some Title
+	+Some title
+	 ==========
+	 Whether '\''tis nobler in the mind to suffer
+	 The slings and arrows of outrageous fortune,
+	@@ -8,21 +8,11 @@
+	 The heart-ache and the thousand natural shocks
+	 That flesh is heir to, '\''tis a consummation
+	 Devoutly to be wish'\''d.
+	-<<<<<<<
+	-Some Title
+	-==========
+	-To die! To sleep;
+	-=======
+	 Some title
+	 ==========
+	 To die, to sleep;
+	->>>>>>>
+	 To sleep: perchance to dream: ay, there'\''s the rub;
+	 For in that sleep of death what dreams may come
+	 When we have shuffled off this mortal coil,
+	 Must give us pause: there'\''s the respect
+	 That makes calamity of so long life;
+	-<<<<<<<
+	-=======
+	-* END *
+	->>>>>>>
+	EOF
+	git rerere diff >out &&
+	test_cmp expect out
+'
+
+test_expect_success 'rerere status' '
+	echo a1 >expect &&
+	git rerere status >out &&
+	test_cmp expect out
+'
+
+test_expect_success 'first postimage wins' '
+	git show first:a1 | sed "s/To die: t/To die! T/" >expect &&
+
+	git commit -q -a -m "prefer first over second" &&
+	test -f $rr/postimage &&
+
+	oldmtimepost=$(test-chmtime -v -60 $rr/postimage | cut -f 1) &&
+
+	git checkout -b third master &&
+	git show second^:a1 | sed "s/To die: t/To die! T/" >a1 &&
+	git commit -q -a -m third &&
+
+	test_must_fail git pull . first &&
+	# rerere kicked in
+	! grep "^=======\$" a1 &&
+	test_cmp expect a1
+'
+
+test_expect_success 'rerere updates postimage timestamp' '
+	newmtimepost=$(test-chmtime -v +0 $rr/postimage | cut -f 1) &&
+	test $oldmtimepost -lt $newmtimepost
+'
+
+test_expect_success 'rerere clear' '
+	rm $rr/postimage &&
+	echo "$sha1	a1" | "$PERL_PATH" -pe "y/\012/\000/" >.git/MERGE_RR &&
+	git rerere clear &&
+	! test -d $rr
+'
+
+test_expect_success 'set up for garbage collection tests' '
+	mkdir -p $rr &&
+	echo Hello >$rr/preimage &&
+	echo World >$rr/postimage &&
+
+	sha2=4000000000000000000000000000000000000000 &&
+	rr2=.git/rr-cache/$sha2 &&
+	mkdir $rr2 &&
+	echo Hello >$rr2/preimage &&
+
+	almost_15_days_ago=$((60-15*86400)) &&
+	just_over_15_days_ago=$((-1-15*86400)) &&
+	almost_60_days_ago=$((60-60*86400)) &&
+	just_over_60_days_ago=$((-1-60*86400)) &&
+
+	test-chmtime =$just_over_60_days_ago $rr/preimage &&
+	test-chmtime =$almost_60_days_ago $rr/postimage &&
+	test-chmtime =$almost_15_days_ago $rr2/preimage
+'
+
+test_expect_success 'gc preserves young or recently used records' '
+	git rerere gc &&
+	test -f $rr/preimage &&
+	test -f $rr2/preimage
+'
+
+test_expect_success 'old records rest in peace' '
+	test-chmtime =$just_over_60_days_ago $rr/postimage &&
+	test-chmtime =$just_over_15_days_ago $rr2/preimage &&
+	git rerere gc &&
+	! test -f $rr/preimage &&
+	! test -f $rr2/preimage
+'
+
+test_expect_success 'setup: file2 added differently in two branches' '
+	git reset --hard &&
+
 	git checkout -b fourth &&
-	echo Hallo > file2 &&
+	echo Hallo >file2 &&
 	git add file2 &&
+	test_tick &&
 	git commit -m version1 &&
+
 	git checkout third &&
-	echo Bello > file2 &&
+	echo Bello >file2 &&
 	git add file2 &&
+	test_tick &&
 	git commit -m version2 &&
+
 	test_must_fail git merge fourth &&
-	echo Cello > file2 &&
+	echo Cello >file2 &&
 	git add file2 &&
 	git commit -m resolution
 '
 
 test_expect_success 'resolution was recorded properly' '
+	echo Cello >expected &&
+
 	git reset --hard HEAD~2 &&
 	git checkout -b fifth &&
-	echo Hallo > file3 &&
+
+	echo Hallo >file3 &&
 	git add file3 &&
+	test_tick &&
 	git commit -m version1 &&
+
 	git checkout third &&
-	echo Bello > file3 &&
+	echo Bello >file3 &&
 	git add file3 &&
+	test_tick &&
 	git commit -m version2 &&
 	git tag version2 &&
+
 	test_must_fail git merge fifth &&
-	test Cello = "$(cat file3)" &&
-	test 0 != $(git ls-files -u | wc -l)
+	test_cmp expected file3 &&
+	test_must_fail git update-index --refresh
 '
 
 test_expect_success 'rerere.autoupdate' '
-	git config rerere.autoupdate true
+	git config rerere.autoupdate true &&
 	git reset --hard &&
 	git checkout version2 &&
 	test_must_fail git merge fifth &&
-	test 0 = $(git ls-files -u | wc -l)
+	git update-index --refresh
+'
 
+test_expect_success 'merge --rerere-autoupdate' '
+	test_might_fail git config --unset rerere.autoupdate &&
+	git reset --hard &&
+	git checkout version2 &&
+	test_must_fail git merge --rerere-autoupdate fifth &&
+	git update-index --refresh
+'
+
+test_expect_success 'merge --no-rerere-autoupdate' '
+	headblob=$(git rev-parse version2:file3) &&
+	mergeblob=$(git rev-parse fifth:file3) &&
+	cat >expected <<-EOF &&
+	100644 $headblob 2	file3
+	100644 $mergeblob 3	file3
+	EOF
+
+	git config rerere.autoupdate true &&
+	git reset --hard &&
+	git checkout version2 &&
+	test_must_fail git merge --no-rerere-autoupdate fifth &&
+	git ls-files -u >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'set up an unresolved merge' '
+	headblob=$(git rev-parse version2:file3) &&
+	mergeblob=$(git rev-parse fifth:file3) &&
+	cat >expected.unresolved <<-EOF &&
+	100644 $headblob 2	file3
+	100644 $mergeblob 3	file3
+	EOF
+
+	test_might_fail git config --unset rerere.autoupdate &&
+	git reset --hard &&
+	git checkout version2 &&
+	fifth=$(git rev-parse fifth) &&
+	echo "$fifth		branch 'fifth' of ." |
+	git fmt-merge-msg >msg &&
+	ancestor=$(git merge-base version2 fifth) &&
+	test_must_fail git merge-recursive "$ancestor" -- HEAD fifth &&
+
+	git ls-files --stage >failedmerge &&
+	cp file3 file3.conflict &&
+
+	git ls-files -u >actual &&
+	test_cmp expected.unresolved actual
+'
+
+test_expect_success 'explicit rerere' '
+	test_might_fail git config --unset rerere.autoupdate &&
+	git rm -fr --cached . &&
+	git update-index --index-info <failedmerge &&
+	cp file3.conflict file3 &&
+	test_must_fail git update-index --refresh -q &&
+
+	git rerere &&
+	git ls-files -u >actual &&
+	test_cmp expected.unresolved actual
+'
+
+test_expect_success 'explicit rerere with autoupdate' '
+	git config rerere.autoupdate true &&
+	git rm -fr --cached . &&
+	git update-index --index-info <failedmerge &&
+	cp file3.conflict file3 &&
+	test_must_fail git update-index --refresh -q &&
+
+	git rerere &&
+	git update-index --refresh
+'
+
+test_expect_success 'explicit rerere --rerere-autoupdate overrides' '
+	git config rerere.autoupdate false &&
+	git rm -fr --cached . &&
+	git update-index --index-info <failedmerge &&
+	cp file3.conflict file3 &&
+	git rerere &&
+	git ls-files -u >actual1 &&
+
+	git rm -fr --cached . &&
+	git update-index --index-info <failedmerge &&
+	cp file3.conflict file3 &&
+	git rerere --rerere-autoupdate &&
+	git update-index --refresh &&
+
+	git rm -fr --cached . &&
+	git update-index --index-info <failedmerge &&
+	cp file3.conflict file3 &&
+	git rerere --rerere-autoupdate --no-rerere-autoupdate &&
+	git ls-files -u >actual2 &&
+
+	git rm -fr --cached . &&
+	git update-index --index-info <failedmerge &&
+	cp file3.conflict file3 &&
+	git rerere --rerere-autoupdate --no-rerere-autoupdate --rerere-autoupdate &&
+	git update-index --refresh &&
+
+	test_cmp expected.unresolved actual1 &&
+	test_cmp expected.unresolved actual2
+'
+
+test_expect_success 'rerere --no-no-rerere-autoupdate' '
+	git rm -fr --cached . &&
+	git update-index --index-info <failedmerge &&
+	cp file3.conflict file3 &&
+	test_must_fail git rerere --no-no-rerere-autoupdate 2>err &&
+	grep [Uu]sage err &&
+	test_must_fail git update-index --refresh
+'
+
+test_expect_success 'rerere -h' '
+	test_must_fail git rerere -h >help &&
+	grep [Uu]sage help
 '
 
 test_done
diff --git a/t/t4201-shortlog.sh b/t/t4201-shortlog.sh
index 405b971..6872ba1 100755
--- a/t/t4201-shortlog.sh
+++ b/t/t4201-shortlog.sh
@@ -8,30 +8,93 @@
 
 . ./test-lib.sh
 
-echo 1 > a1
-git add a1
-tree=$(git write-tree)
-commit=$( (echo "Test"; echo) | git commit-tree $tree )
-git update-ref HEAD $commit
+test_expect_success 'setup' '
+	echo 1 >a1 &&
+	git add a1 &&
+	tree=$(git write-tree) &&
+	commit=$(printf "%s\n" "Test" "" | git commit-tree "$tree") &&
+	git update-ref HEAD "$commit" &&
 
-echo 2 > a1
-git commit --quiet -m "This is a very, very long first line for the commit message to see if it is wrapped correctly" a1
+	echo 2 >a1 &&
+	git commit --quiet -m "This is a very, very long first line for the commit message to see if it is wrapped correctly" a1 &&
 
-# test if the wrapping is still valid when replacing all i's by treble clefs.
-echo 3 > a1
-git commit --quiet -m "$(echo "This is a very, very long first line for the commit message to see if it is wrapped correctly" | sed "s/i/1234/g" | tr 1234 '\360\235\204\236')" a1
+	# test if the wrapping is still valid
+	# when replacing all is by treble clefs.
+	echo 3 >a1 &&
+	git commit --quiet -m "$(
+		echo "This is a very, very long first line for the commit message to see if it is wrapped correctly" |
+		sed "s/i/1234/g" |
+		tr 1234 "\360\235\204\236")" a1 &&
 
-# now fsck up the utf8
-git config i18n.commitencoding non-utf-8
-echo 4 > a1
-git commit --quiet -m "$(echo "This is a very, very long first line for the commit message to see if it is wrapped correctly" | sed "s/i/1234/g" | tr 1234 '\370\235\204\236')" a1
+	# now fsck up the utf8
+	git config i18n.commitencoding non-utf-8 &&
+	echo 4 >a1 &&
+	git commit --quiet -m "$(
+		echo "This is a very, very long first line for the commit message to see if it is wrapped correctly" |
+		sed "s/i/1234/g" |
+		tr 1234 "\370\235\204\236")" a1 &&
 
-echo 5 > a1
-git commit --quiet -m "a								12	34	56	78" a1
+	echo 5 >a1 &&
+	git commit --quiet -m "a								12	34	56	78" a1 &&
 
-git shortlog -w HEAD > out
+	echo 6 >a1 &&
+	git commit --quiet -m "Commit by someone else" \
+		--author="Someone else <not!me>" a1 &&
 
-cat > expect << EOF
+	cat >expect.template <<-\EOF
+	A U Thor (5):
+	      SUBJECT
+	      SUBJECT
+	      SUBJECT
+	      SUBJECT
+	      SUBJECT
+
+	Someone else (1):
+	      SUBJECT
+
+	EOF
+'
+
+fuzz() {
+	file=$1 &&
+	sed "
+			s/$_x40/OBJECT_NAME/g
+			s/$_x05/OBJID/g
+			s/^ \{6\}[CTa].*/      SUBJECT/g
+			s/^ \{8\}[^ ].*/        CONTINUATION/g
+		" <"$file" >"$file.fuzzy" &&
+	sed "/CONTINUATION/ d" <"$file.fuzzy"
+}
+
+test_expect_success 'default output format' '
+	git shortlog HEAD >log &&
+	fuzz log >log.predictable &&
+	test_cmp expect.template log.predictable
+'
+
+test_expect_success 'pretty format' '
+	sed s/SUBJECT/OBJECT_NAME/ expect.template >expect &&
+	git shortlog --format="%H" HEAD >log &&
+	fuzz log >log.predictable &&
+	test_cmp expect log.predictable
+'
+
+test_expect_success '--abbrev' '
+	sed s/SUBJECT/OBJID/ expect.template >expect &&
+	git shortlog --format="%h" --abbrev=5 HEAD >log &&
+	fuzz log >log.predictable &&
+	test_cmp expect log.predictable
+'
+
+test_expect_success 'output from user-defined format is re-wrapped' '
+	sed "s/SUBJECT/two lines/" expect.template >expect &&
+	git shortlog --format="two%nlines" HEAD >log &&
+	fuzz log >log.predictable &&
+	test_cmp expect log.predictable
+'
+
+test_expect_success 'shortlog wrapping' '
+	cat >expect <<\EOF &&
 A U Thor (5):
       Test
       This is a very, very long first line for the commit message to see if
@@ -43,13 +106,46 @@
       a								12	34
          56	78
 
+Someone else (1):
+      Commit by someone else
+
+EOF
+	git shortlog -w HEAD >out &&
+	test_cmp expect out
+'
+
+test_expect_success 'shortlog from non-git directory' '
+	git log HEAD >log &&
+	GIT_DIR=non-existing git shortlog -w <log >out &&
+	test_cmp expect out
+'
+
+iconvfromutf8toiso88591() {
+	printf "%s" "$*" | iconv -f UTF-8 -t ISO8859-1
+}
+
+DSCHO="Jöhännës \"Dschö\" Schindëlin"
+DSCHOE="$DSCHO <Johannes.Schindelin@gmx.de>"
+MSG1="set a1 to 2 and some non-ASCII chars: Äßø"
+MSG2="set a1 to 3 and some non-ASCII chars: áæï"
+cat > expect << EOF
+$DSCHO (2):
+      $MSG1
+      $MSG2
+
 EOF
 
-test_expect_success 'shortlog wrapping' 'test_cmp expect out'
-
-git log HEAD > log
-GIT_DIR=non-existing git shortlog -w < log > out
-
-test_expect_success 'shortlog from non-git directory' 'test_cmp expect out'
+test_expect_success 'shortlog encoding' '
+	git reset --hard "$commit" &&
+	git config --unset i18n.commitencoding &&
+	echo 2 > a1 &&
+	git commit --quiet -m "$MSG1" --author="$DSCHOE" a1 &&
+	git config i18n.commitencoding "ISO8859-1" &&
+	echo 3 > a1 &&
+	git commit --quiet -m "$(iconvfromutf8toiso88591 "$MSG2")" \
+		--author="$(iconvfromutf8toiso88591 "$DSCHOE")" a1 &&
+	git config --unset i18n.commitencoding &&
+	git shortlog HEAD~2.. > out &&
+test_cmp expect out'
 
 test_done
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
index 48e0088..71be59d 100755
--- a/t/t4202-log.sh
+++ b/t/t4202-log.sh
@@ -64,6 +64,27 @@
 '
 
 cat > expect << EOF
+ This is
+  the sixth
+  commit.
+ This is
+  the fifth
+  commit.
+EOF
+
+test_expect_success 'format %w(12,1,2)' '
+
+	git log -2 --format="%w(12,1,2)This is the %s commit." > actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'format %w(,1,2)' '
+
+	git log -2 --format="%w(,1,2)This is%nthe %s%ncommit." > actual &&
+	test_cmp expect actual
+'
+
+cat > expect << EOF
 804a787 sixth
 394ef78 fifth
 5d31159 fourth
@@ -79,13 +100,11 @@
 
 test_expect_success 'diff-filter=A' '
 
-	actual=$(git log --pretty="format:%s" --diff-filter=A HEAD) &&
-	expect=$(echo fifth ; echo fourth ; echo third ; echo initial) &&
-	test "$actual" = "$expect" || {
-		echo Oops
-		echo "Actual: $actual"
-		false
-	}
+	git log --pretty="format:%s" --diff-filter=A HEAD > actual &&
+	git log --pretty="format:%s" --diff-filter A HEAD > actual-separate &&
+	printf "fifth\nfourth\nthird\ninitial" > expect &&
+	test_cmp expect actual &&
+	test_cmp expect actual-separate
 
 '
 
@@ -172,7 +191,7 @@
 test_expect_success 'setup case sensitivity tests' '
 	echo case >one &&
 	test_tick &&
-	git add one
+	git add one &&
 	git commit -a -m Second
 '
 
@@ -182,6 +201,13 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'log --grep option parsing' '
+	echo second >expect &&
+	git log -1 --pretty="tformat:%s" --grep sec >actual &&
+	test_cmp expect actual &&
+	test_must_fail git log -1 --pretty="tformat:%s" --grep
+'
+
 test_expect_success 'log -i --grep' '
 	echo Second >expect &&
 	git log -1 --pretty="tformat:%s" -i --grep=sec >actual &&
@@ -234,7 +260,7 @@
 
 test_expect_success 'log --graph with merge' '
 	git log --graph --date-order --pretty=tformat:%s |
-		sed "s/ *$//" >actual &&
+		sed "s/ *\$//" >actual &&
 	test_cmp expect actual
 '
 
@@ -294,7 +320,7 @@
 test_expect_success 'log --graph with full output' '
 	git log --graph --date-order --pretty=short |
 		git name-rev --name-only --stdin |
-		sed "s/Merge:.*/Merge: A B/;s/ *$//" >actual &&
+		sed "s/Merge:.*/Merge: A B/;s/ *\$//" >actual &&
 	test_cmp expect actual
 '
 
@@ -315,16 +341,16 @@
 	test_commit octopus-b &&
 	git checkout master &&
 	test_commit seventh &&
-	git merge octopus-a octopus-b
+	git merge octopus-a octopus-b &&
 	git merge reach
 '
 
 cat > expect <<\EOF
-*   Merge branch 'reach'
+*   Merge tag 'reach'
 |\
 | \
 |  \
-*-. \   Merge branches 'octopus-a' and 'octopus-b'
+*-. \   Merge tags 'octopus-a' and 'octopus-b'
 |\ \ \
 * | | | seventh
 | | * | octopus-b
@@ -362,9 +388,422 @@
 
 test_expect_success 'log --graph with merge' '
 	git log --graph --date-order --pretty=tformat:%s |
-		sed "s/ *$//" >actual &&
+		sed "s/ *\$//" >actual &&
 	test_cmp expect actual
 '
 
-test_done
+test_expect_success 'log.decorate configuration' '
+	test_might_fail git config --unset-all log.decorate &&
 
+	git log --oneline >expect.none &&
+	git log --oneline --decorate >expect.short &&
+	git log --oneline --decorate=full >expect.full &&
+
+	echo "[log] decorate" >>.git/config &&
+	git log --oneline >actual &&
+	test_cmp expect.short actual &&
+
+	git config --unset-all log.decorate &&
+	git config log.decorate true &&
+	git log --oneline >actual &&
+	test_cmp expect.short actual &&
+	git log --oneline --decorate=full >actual &&
+	test_cmp expect.full actual &&
+	git log --oneline --decorate=no >actual &&
+	test_cmp expect.none actual &&
+
+	git config --unset-all log.decorate &&
+	git config log.decorate no &&
+	git log --oneline >actual &&
+	test_cmp expect.none actual &&
+	git log --oneline --decorate >actual &&
+	test_cmp expect.short actual &&
+	git log --oneline --decorate=full >actual &&
+	test_cmp expect.full actual &&
+
+	git config --unset-all log.decorate &&
+	git config log.decorate 1 &&
+	git log --oneline >actual &&
+	test_cmp expect.short actual &&
+	git log --oneline --decorate=full >actual &&
+	test_cmp expect.full actual &&
+	git log --oneline --decorate=no >actual &&
+	test_cmp expect.none actual &&
+
+	git config --unset-all log.decorate &&
+	git config log.decorate short &&
+	git log --oneline >actual &&
+	test_cmp expect.short actual &&
+	git log --oneline --no-decorate >actual &&
+	test_cmp expect.none actual &&
+	git log --oneline --decorate=full >actual &&
+	test_cmp expect.full actual &&
+
+	git config --unset-all log.decorate &&
+	git config log.decorate full &&
+	git log --oneline >actual &&
+	test_cmp expect.full actual &&
+	git log --oneline --no-decorate >actual &&
+	test_cmp expect.none actual &&
+	git log --oneline --decorate >actual &&
+	test_cmp expect.short actual
+
+	git config --unset-all log.decorate &&
+	git log --pretty=raw >expect.raw &&
+	git config log.decorate full &&
+	git log --pretty=raw >actual &&
+	test_cmp expect.raw actual
+
+'
+
+test_expect_success 'reflog is expected format' '
+	test_might_fail git config --remove-section log &&
+	git log -g --abbrev-commit --pretty=oneline >expect &&
+	git reflog >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'whatchanged is expected format' '
+	git log --no-merges --raw >expect &&
+	git whatchanged >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'log.abbrevCommit configuration' '
+	test_when_finished "git config --unset log.abbrevCommit" &&
+
+	test_might_fail git config --unset log.abbrevCommit &&
+
+	git log --abbrev-commit >expect.log.abbrev &&
+	git log --no-abbrev-commit >expect.log.full &&
+	git log --pretty=raw >expect.log.raw &&
+	git reflog --abbrev-commit >expect.reflog.abbrev &&
+	git reflog --no-abbrev-commit >expect.reflog.full &&
+	git whatchanged --abbrev-commit >expect.whatchanged.abbrev &&
+	git whatchanged --no-abbrev-commit >expect.whatchanged.full &&
+
+	git config log.abbrevCommit true &&
+
+	git log >actual &&
+	test_cmp expect.log.abbrev actual &&
+	git log --no-abbrev-commit >actual &&
+	test_cmp expect.log.full actual &&
+
+	git log --pretty=raw >actual &&
+	test_cmp expect.log.raw actual &&
+
+	git reflog >actual &&
+	test_cmp expect.reflog.abbrev actual &&
+	git reflog --no-abbrev-commit >actual &&
+	test_cmp expect.reflog.full actual &&
+
+	git whatchanged >actual &&
+	test_cmp expect.whatchanged.abbrev actual &&
+	git whatchanged --no-abbrev-commit >actual &&
+	test_cmp expect.whatchanged.full actual
+'
+
+test_expect_success 'show added path under "--follow -M"' '
+	# This tests for a regression introduced in v1.7.2-rc0~103^2~2
+	test_create_repo regression &&
+	(
+		cd regression &&
+		test_commit needs-another-commit &&
+		test_commit foo.bar &&
+		git log -M --follow -p foo.bar.t &&
+		git log -M --follow --stat foo.bar.t &&
+		git log -M --follow --name-only foo.bar.t
+	)
+'
+
+cat >expect <<\EOF
+*   commit COMMIT_OBJECT_NAME
+|\  Merge: MERGE_PARENTS
+| | Author: A U Thor <author@example.com>
+| |
+| |     Merge HEADS DESCRIPTION
+| |
+| * commit COMMIT_OBJECT_NAME
+| | Author: A U Thor <author@example.com>
+| |
+| |     reach
+| | ---
+| |  reach.t | 1 +
+| |  1 file changed, 1 insertion(+)
+| |
+| | diff --git a/reach.t b/reach.t
+| | new file mode 100644
+| | index 0000000..10c9591
+| | --- /dev/null
+| | +++ b/reach.t
+| | @@ -0,0 +1 @@
+| | +reach
+| |
+|  \
+*-. \   commit COMMIT_OBJECT_NAME
+|\ \ \  Merge: MERGE_PARENTS
+| | | | Author: A U Thor <author@example.com>
+| | | |
+| | | |     Merge HEADS DESCRIPTION
+| | | |
+| | * | commit COMMIT_OBJECT_NAME
+| | |/  Author: A U Thor <author@example.com>
+| | |
+| | |       octopus-b
+| | |   ---
+| | |    octopus-b.t | 1 +
+| | |    1 file changed, 1 insertion(+)
+| | |
+| | |   diff --git a/octopus-b.t b/octopus-b.t
+| | |   new file mode 100644
+| | |   index 0000000..d5fcad0
+| | |   --- /dev/null
+| | |   +++ b/octopus-b.t
+| | |   @@ -0,0 +1 @@
+| | |   +octopus-b
+| | |
+| * | commit COMMIT_OBJECT_NAME
+| |/  Author: A U Thor <author@example.com>
+| |
+| |       octopus-a
+| |   ---
+| |    octopus-a.t | 1 +
+| |    1 file changed, 1 insertion(+)
+| |
+| |   diff --git a/octopus-a.t b/octopus-a.t
+| |   new file mode 100644
+| |   index 0000000..11ee015
+| |   --- /dev/null
+| |   +++ b/octopus-a.t
+| |   @@ -0,0 +1 @@
+| |   +octopus-a
+| |
+* | commit COMMIT_OBJECT_NAME
+|/  Author: A U Thor <author@example.com>
+|
+|       seventh
+|   ---
+|    seventh.t | 1 +
+|    1 file changed, 1 insertion(+)
+|
+|   diff --git a/seventh.t b/seventh.t
+|   new file mode 100644
+|   index 0000000..9744ffc
+|   --- /dev/null
+|   +++ b/seventh.t
+|   @@ -0,0 +1 @@
+|   +seventh
+|
+*   commit COMMIT_OBJECT_NAME
+|\  Merge: MERGE_PARENTS
+| | Author: A U Thor <author@example.com>
+| |
+| |     Merge branch 'tangle'
+| |
+| *   commit COMMIT_OBJECT_NAME
+| |\  Merge: MERGE_PARENTS
+| | | Author: A U Thor <author@example.com>
+| | |
+| | |     Merge branch 'side' (early part) into tangle
+| | |
+| * |   commit COMMIT_OBJECT_NAME
+| |\ \  Merge: MERGE_PARENTS
+| | | | Author: A U Thor <author@example.com>
+| | | |
+| | | |     Merge branch 'master' (early part) into tangle
+| | | |
+| * | | commit COMMIT_OBJECT_NAME
+| | | | Author: A U Thor <author@example.com>
+| | | |
+| | | |     tangle-a
+| | | | ---
+| | | |  tangle-a | 1 +
+| | | |  1 file changed, 1 insertion(+)
+| | | |
+| | | | diff --git a/tangle-a b/tangle-a
+| | | | new file mode 100644
+| | | | index 0000000..7898192
+| | | | --- /dev/null
+| | | | +++ b/tangle-a
+| | | | @@ -0,0 +1 @@
+| | | | +a
+| | | |
+* | | |   commit COMMIT_OBJECT_NAME
+|\ \ \ \  Merge: MERGE_PARENTS
+| | | | | Author: A U Thor <author@example.com>
+| | | | |
+| | | | |     Merge branch 'side'
+| | | | |
+| * | | | commit COMMIT_OBJECT_NAME
+| | |_|/  Author: A U Thor <author@example.com>
+| |/| |
+| | | |       side-2
+| | | |   ---
+| | | |    2 | 1 +
+| | | |    1 file changed, 1 insertion(+)
+| | | |
+| | | |   diff --git a/2 b/2
+| | | |   new file mode 100644
+| | | |   index 0000000..0cfbf08
+| | | |   --- /dev/null
+| | | |   +++ b/2
+| | | |   @@ -0,0 +1 @@
+| | | |   +2
+| | | |
+| * | | commit COMMIT_OBJECT_NAME
+| | | | Author: A U Thor <author@example.com>
+| | | |
+| | | |     side-1
+| | | | ---
+| | | |  1 | 1 +
+| | | |  1 file changed, 1 insertion(+)
+| | | |
+| | | | diff --git a/1 b/1
+| | | | new file mode 100644
+| | | | index 0000000..d00491f
+| | | | --- /dev/null
+| | | | +++ b/1
+| | | | @@ -0,0 +1 @@
+| | | | +1
+| | | |
+* | | | commit COMMIT_OBJECT_NAME
+| | | | Author: A U Thor <author@example.com>
+| | | |
+| | | |     Second
+| | | | ---
+| | | |  one | 1 +
+| | | |  1 file changed, 1 insertion(+)
+| | | |
+| | | | diff --git a/one b/one
+| | | | new file mode 100644
+| | | | index 0000000..9a33383
+| | | | --- /dev/null
+| | | | +++ b/one
+| | | | @@ -0,0 +1 @@
+| | | | +case
+| | | |
+* | | | commit COMMIT_OBJECT_NAME
+| |_|/  Author: A U Thor <author@example.com>
+|/| |
+| | |       sixth
+| | |   ---
+| | |    a/two | 1 -
+| | |    1 file changed, 1 deletion(-)
+| | |
+| | |   diff --git a/a/two b/a/two
+| | |   deleted file mode 100644
+| | |   index 9245af5..0000000
+| | |   --- a/a/two
+| | |   +++ /dev/null
+| | |   @@ -1 +0,0 @@
+| | |   -ni
+| | |
+* | | commit COMMIT_OBJECT_NAME
+| | | Author: A U Thor <author@example.com>
+| | |
+| | |     fifth
+| | | ---
+| | |  a/two | 1 +
+| | |  1 file changed, 1 insertion(+)
+| | |
+| | | diff --git a/a/two b/a/two
+| | | new file mode 100644
+| | | index 0000000..9245af5
+| | | --- /dev/null
+| | | +++ b/a/two
+| | | @@ -0,0 +1 @@
+| | | +ni
+| | |
+* | | commit COMMIT_OBJECT_NAME
+|/ /  Author: A U Thor <author@example.com>
+| |
+| |       fourth
+| |   ---
+| |    ein | 1 +
+| |    1 file changed, 1 insertion(+)
+| |
+| |   diff --git a/ein b/ein
+| |   new file mode 100644
+| |   index 0000000..9d7e69f
+| |   --- /dev/null
+| |   +++ b/ein
+| |   @@ -0,0 +1 @@
+| |   +ichi
+| |
+* | commit COMMIT_OBJECT_NAME
+|/  Author: A U Thor <author@example.com>
+|
+|       third
+|   ---
+|    ichi | 1 +
+|    one  | 1 -
+|    2 files changed, 1 insertion(+), 1 deletion(-)
+|
+|   diff --git a/ichi b/ichi
+|   new file mode 100644
+|   index 0000000..9d7e69f
+|   --- /dev/null
+|   +++ b/ichi
+|   @@ -0,0 +1 @@
+|   +ichi
+|   diff --git a/one b/one
+|   deleted file mode 100644
+|   index 9d7e69f..0000000
+|   --- a/one
+|   +++ /dev/null
+|   @@ -1 +0,0 @@
+|   -ichi
+|
+* commit COMMIT_OBJECT_NAME
+| Author: A U Thor <author@example.com>
+|
+|     second
+| ---
+|  one | 2 +-
+|  1 file changed, 1 insertion(+), 1 deletion(-)
+|
+| diff --git a/one b/one
+| index 5626abf..9d7e69f 100644
+| --- a/one
+| +++ b/one
+| @@ -1 +1 @@
+| -one
+| +ichi
+|
+* commit COMMIT_OBJECT_NAME
+  Author: A U Thor <author@example.com>
+
+      initial
+  ---
+   one | 1 +
+   1 file changed, 1 insertion(+)
+
+  diff --git a/one b/one
+  new file mode 100644
+  index 0000000..5626abf
+  --- /dev/null
+  +++ b/one
+  @@ -0,0 +1 @@
+  +one
+EOF
+
+sanitize_output () {
+	sed -e 's/ *$//' \
+	    -e 's/commit [0-9a-f]*$/commit COMMIT_OBJECT_NAME/' \
+	    -e 's/Merge: [ 0-9a-f]*$/Merge: MERGE_PARENTS/' \
+	    -e 's/Merge tag.*/Merge HEADS DESCRIPTION/' \
+	    -e 's/Merge commit.*/Merge HEADS DESCRIPTION/' \
+	    -e 's/, 0 deletions(-)//' \
+	    -e 's/, 0 insertions(+)//' \
+	    -e 's/ 1 files changed, / 1 file changed, /' \
+	    -e 's/, 1 deletions(-)/, 1 deletion(-)/' \
+	    -e 's/, 1 insertions(+)/, 1 insertion(+)/'
+}
+
+test_expect_success 'log --graph with diff and stats' '
+	git log --graph --pretty=short --stat -p >actual &&
+	sanitize_output >actual.sanitized <actual &&
+	test_cmp expect actual.sanitized
+'
+
+test_done
diff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh
index 9a7d1b4..1f182f6 100755
--- a/t/t4203-mailmap.sh
+++ b/t/t4203-mailmap.sh
@@ -4,6 +4,14 @@
 
 . ./test-lib.sh
 
+fuzz_blame () {
+	sed "
+		s/$_x05[0-9a-f][0-9a-f][0-9a-f]/OBJID/g
+		s/$_x05[0-9a-f][0-9a-f]/OBJI/g
+		s/[-0-9]\{10\} [:0-9]\{8\} [-+][0-9]\{4\}/DATE/g
+	" "$@"
+}
+
 test_expect_success setup '
 	echo one >one &&
 	git add one &&
@@ -11,6 +19,7 @@
 	git commit -m initial &&
 	echo two >>one &&
 	git add one &&
+	test_tick &&
 	git commit --author "nick1 <bugs@company.xx>" -m second
 '
 
@@ -54,7 +63,7 @@
 
 EOF
 test_expect_success 'mailmap.file set' '
-	mkdir internal_mailmap &&
+	mkdir -p internal_mailmap &&
 	echo "Internal Guy <bugs@company.xx>" > internal_mailmap/.mailmap &&
 	git config mailmap.file internal_mailmap/.mailmap &&
 	git shortlog HEAD >actual &&
@@ -85,7 +94,7 @@
 
 EOF
 
-test_expect_success 'mailmap.file non-existant' '
+test_expect_success 'mailmap.file non-existent' '
 	rm internal_mailmap/.mailmap &&
 	rmdir internal_mailmap &&
 	git shortlog HEAD >actual &&
@@ -93,6 +102,40 @@
 '
 
 cat >expect <<\EOF
+Internal Guy (1):
+      second
+
+Repo Guy (1):
+      initial
+
+EOF
+
+test_expect_success 'name entry after email entry' '
+	mkdir -p internal_mailmap &&
+	echo "<bugs@company.xy> <bugs@company.xx>" >internal_mailmap/.mailmap &&
+	echo "Internal Guy <bugs@company.xx>" >>internal_mailmap/.mailmap &&
+	git shortlog HEAD >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<\EOF
+Internal Guy (1):
+      second
+
+Repo Guy (1):
+      initial
+
+EOF
+
+test_expect_success 'name entry after email entry, case-insensitive' '
+	mkdir -p internal_mailmap &&
+	echo "<bugs@company.xy> <bugs@company.xx>" >internal_mailmap/.mailmap &&
+	echo "Internal Guy <BUGS@Company.xx>" >>internal_mailmap/.mailmap &&
+	git shortlog HEAD >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<\EOF
 A U Thor (1):
       initial
 
@@ -101,7 +144,7 @@
 
 EOF
 test_expect_success 'No mailmap files, but configured' '
-	rm .mailmap &&
+	rm -f .mailmap internal_mailmap/.mailmap &&
 	git shortlog HEAD >actual &&
 	test_cmp expect actual
 '
@@ -153,7 +196,7 @@
 	test_tick &&
 	git commit --author "CTO <cto@coompany.xx>" -m seventh &&
 
-	mkdir internal_mailmap &&
+	mkdir -p internal_mailmap &&
 	echo "Committed <committer@example.com>" > internal_mailmap/.mailmap &&
 	echo "<cto@company.xx>                       <cto@coompany.xx>" >> internal_mailmap/.mailmap &&
 	echo "Some Dude <some@dude.xx>         nick1 <bugs@company.xx>" >> internal_mailmap/.mailmap &&
@@ -198,18 +241,18 @@
 
 # git blame
 cat >expect <<\EOF
-^3a2fdcb (A U Thor     2005-04-07 15:13:13 -0700 1) one
-7de6f99b (Some Dude    2005-04-07 15:13:13 -0700 2) two
-5815879d (Other Author 2005-04-07 15:14:13 -0700 3) three
-ff859d96 (Other Author 2005-04-07 15:15:13 -0700 4) four
-5ab6d4fa (Santa Claus  2005-04-07 15:16:13 -0700 5) five
-38a42d8b (Santa Claus  2005-04-07 15:17:13 -0700 6) six
-8ddc0386 (CTO          2005-04-07 15:18:13 -0700 7) seven
+^OBJI (A U Thor     DATE 1) one
+OBJID (Some Dude    DATE 2) two
+OBJID (Other Author DATE 3) three
+OBJID (Other Author DATE 4) four
+OBJID (Santa Claus  DATE 5) five
+OBJID (Santa Claus  DATE 6) six
+OBJID (CTO          DATE 7) seven
 EOF
-
 test_expect_success 'Blame output (complex mapping)' '
 	git blame one >actual &&
-	test_cmp expect actual
+	fuzz_blame actual >actual.fuzz &&
+	test_cmp expect actual.fuzz
 '
 
 test_done
diff --git a/t/t4204-patch-id.sh b/t/t4204-patch-id.sh
index 04f7bae..d2c930d 100755
--- a/t/t4204-patch-id.sh
+++ b/t/t4204-patch-id.sh
@@ -18,6 +18,11 @@
 	grep "^[a-f0-9]\{40\} $(git rev-parse HEAD)$" output
 '
 
+calc_patch_id () {
+	git patch-id |
+		sed "s# .*##" > patch-id_"$1"
+}
+
 get_patch_id () {
 	git log -p -1 "$1" | git patch-id |
 		sed "s# .*##" > patch-id_"$1"
@@ -35,4 +40,63 @@
 	! test_cmp patch-id_master patch-id_notsame
 '
 
+test_expect_success 'patch-id supports git-format-patch output' '
+	get_patch_id master &&
+	git checkout same &&
+	git format-patch -1 --stdout | calc_patch_id same &&
+	test_cmp patch-id_master patch-id_same &&
+	set `git format-patch -1 --stdout | git patch-id` &&
+	test "$2" = `git rev-parse HEAD`
+'
+
+test_expect_success 'whitespace is irrelevant in footer' '
+	get_patch_id master &&
+	git checkout same &&
+	git format-patch -1 --stdout | sed "s/ \$//" | calc_patch_id same &&
+	test_cmp patch-id_master patch-id_same
+'
+
+test_expect_success 'patch-id supports git-format-patch MIME output' '
+	get_patch_id master &&
+	git checkout same &&
+	git format-patch -1 --attach --stdout | calc_patch_id same &&
+	test_cmp patch-id_master patch-id_same
+'
+
+cat >nonl <<\EOF
+diff --git i/a w/a
+index e69de29..2e65efe 100644
+--- i/a
++++ w/a
+@@ -0,0 +1 @@
++a
+\ No newline at end of file
+diff --git i/b w/b
+index e69de29..6178079 100644
+--- i/b
++++ w/b
+@@ -0,0 +1 @@
++b
+EOF
+
+cat >withnl <<\EOF
+diff --git i/a w/a
+index e69de29..7898192 100644
+--- i/a
++++ w/a
+@@ -0,0 +1 @@
++a
+diff --git i/b w/b
+index e69de29..6178079 100644
+--- i/b
++++ w/b
+@@ -0,0 +1 @@
++b
+EOF
+
+test_expect_success 'patch-id handles no-nl-at-eof markers' '
+	cat nonl | calc_patch_id nonl &&
+	cat withnl | calc_patch_id withnl &&
+	test_cmp patch-id_nonl patch-id_withnl
+'
 test_done
diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh
new file mode 100755
index 0000000..4afd778
--- /dev/null
+++ b/t/t4205-log-pretty-formats.sh
@@ -0,0 +1,102 @@
+#!/bin/sh
+#
+# Copyright (c) 2010, Will Palmer
+#
+
+test_description='Test pretty formats'
+. ./test-lib.sh
+
+test_expect_success 'set up basic repos' '
+	>foo &&
+	>bar &&
+	git add foo &&
+	test_tick &&
+	git commit -m initial &&
+	git add bar &&
+	test_tick &&
+	git commit -m "add bar"
+'
+
+test_expect_success 'alias builtin format' '
+	git log --pretty=oneline >expected &&
+	git config pretty.test-alias oneline &&
+	git log --pretty=test-alias >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'alias masking builtin format' '
+	git log --pretty=oneline >expected &&
+	git config pretty.oneline "%H" &&
+	git log --pretty=oneline >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'alias user-defined format' '
+	git log --pretty="format:%h" >expected &&
+	git config pretty.test-alias "format:%h" &&
+	git log --pretty=test-alias >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'alias user-defined tformat' '
+	git log --pretty="tformat:%h" >expected &&
+	git config pretty.test-alias "tformat:%h" &&
+	git log --pretty=test-alias >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'alias non-existent format' '
+	git config pretty.test-alias format-that-will-never-exist &&
+	test_must_fail git log --pretty=test-alias
+'
+
+test_expect_success 'alias of an alias' '
+	git log --pretty="tformat:%h" >expected &&
+	git config pretty.test-foo "tformat:%h" &&
+	git config pretty.test-bar test-foo &&
+	git log --pretty=test-bar >actual && test_cmp expected actual
+'
+
+test_expect_success 'alias masking an alias' '
+	git log --pretty=format:"Two %H" >expected &&
+	git config pretty.duplicate "format:One %H" &&
+	git config --add pretty.duplicate "format:Two %H" &&
+	git log --pretty=duplicate >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'alias loop' '
+	git config pretty.test-foo test-bar &&
+	git config pretty.test-bar test-foo &&
+	test_must_fail git log --pretty=test-foo
+'
+
+test_expect_success 'NUL separation' '
+	printf "add bar\0initial" >expected &&
+	git log -z --pretty="format:%s" >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'NUL termination' '
+	printf "add bar\0initial\0" >expected &&
+	git log -z --pretty="tformat:%s" >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'NUL separation with --stat' '
+	stat0_part=$(git diff --stat HEAD^ HEAD) &&
+	stat1_part=$(git diff --stat --root HEAD^) &&
+	printf "add bar\n$stat0_part\n\0initial\n$stat1_part\n" >expected &&
+	git log -z --stat --pretty="format:%s" >actual &&
+	test_cmp expected actual
+'
+
+test_expect_failure 'NUL termination with --stat' '
+	stat0_part=$(git diff --stat HEAD^ HEAD) &&
+	stat1_part=$(git diff --stat --root HEAD^) &&
+	printf "add bar\n$stat0_part\n\0initial\n$stat1_part\n\0" >expected &&
+	git log -z --stat --pretty="tformat:%s" >actual &&
+	test_cmp expected actual
+'
+
+test_done
diff --git a/t/t4206-log-follow-harder-copies.sh b/t/t4206-log-follow-harder-copies.sh
new file mode 100755
index 0000000..ad29e65
--- /dev/null
+++ b/t/t4206-log-follow-harder-copies.sh
@@ -0,0 +1,56 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Bo Yang
+#
+
+test_description='Test --follow should always find copies hard in git log.
+
+'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh
+
+echo >path0 'Line 1
+Line 2
+Line 3
+'
+
+test_expect_success \
+    'add a file path0 and commit.' \
+    'git add path0 &&
+     git commit -m "Add path0"'
+
+echo >path0 'New line 1
+New line 2
+New line 3
+'
+test_expect_success \
+    'Change path0.' \
+    'git add path0 &&
+     git commit -m "Change path0"'
+
+cat <path0 >path1
+test_expect_success \
+    'copy path0 to path1.' \
+    'git add path1 &&
+     git commit -m "Copy path1 from path0"'
+
+test_expect_success \
+    'find the copy path0 -> path1 harder' \
+    'git log --follow --name-status --pretty="format:%s"  path1 > current'
+
+cat >expected <<\EOF
+Copy path1 from path0
+C100	path0	path1
+
+Change path0
+M	path0
+
+Add path0
+A	path0
+EOF
+
+test_expect_success \
+    'validate the output.' \
+    'compare_diff_patch current expected'
+
+test_done
diff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh
new file mode 100755
index 0000000..bbde31b
--- /dev/null
+++ b/t/t4207-log-decoration-colors.sh
@@ -0,0 +1,66 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Nazri Ramliy
+#
+
+test_description='Test for "git log --decorate" colors'
+
+. ./test-lib.sh
+
+get_color ()
+{
+	git config --get-color no.such.slot "$1"
+}
+
+test_expect_success setup '
+	git config diff.color.commit yellow &&
+	git config color.decorate.branch green &&
+	git config color.decorate.remoteBranch red &&
+	git config color.decorate.tag "reverse bold yellow" &&
+	git config color.decorate.stash magenta &&
+	git config color.decorate.HEAD cyan &&
+
+	c_reset=$(get_color reset) &&
+
+	c_commit=$(get_color yellow) &&
+	c_branch=$(get_color green) &&
+	c_remoteBranch=$(get_color red) &&
+	c_tag=$(get_color "reverse bold yellow") &&
+	c_stash=$(get_color magenta) &&
+	c_HEAD=$(get_color cyan) &&
+
+	test_commit A &&
+	git clone . other &&
+	(
+		cd other &&
+		test_commit A1
+	) &&
+
+	git remote add -f other ./other &&
+	test_commit B &&
+	git tag v1.0 &&
+	echo >>A.t &&
+	git stash save Changes to A.t
+'
+
+cat >expected <<EOF
+${c_commit}COMMIT_ID (${c_HEAD}HEAD${c_reset}${c_commit},\
+ ${c_tag}tag: v1.0${c_reset}${c_commit},\
+ ${c_tag}tag: B${c_reset}${c_commit},\
+ ${c_branch}master${c_reset}${c_commit})${c_reset} B
+${c_commit}COMMIT_ID (${c_tag}tag: A1${c_reset}${c_commit},\
+ ${c_remoteBranch}other/master${c_reset}${c_commit})${c_reset} A1
+${c_commit}COMMIT_ID (${c_stash}refs/stash${c_reset}${c_commit})${c_reset}\
+ On master: Changes to A.t
+${c_commit}COMMIT_ID (${c_tag}tag: A${c_reset}${c_commit})${c_reset} A
+EOF
+
+# We want log to show all, but the second parent to refs/stash is irrelevant
+# to this test since it does not contain any decoration, hence --first-parent
+test_expect_success 'Commit Decorations Colored Correctly' '
+	git log --first-parent --abbrev=10 --all --decorate --oneline --color=always |
+	sed "s/[0-9a-f]\{10,10\}/COMMIT_ID/" >out &&
+	test_cmp expected out
+'
+
+test_done
diff --git a/t/t4208-log-magic-pathspec.sh b/t/t4208-log-magic-pathspec.sh
new file mode 100755
index 0000000..2c482b6
--- /dev/null
+++ b/t/t4208-log-magic-pathspec.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+test_description='magic pathspec tests using git-log'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	test_commit initial &&
+	test_tick &&
+	git commit --allow-empty -m empty &&
+	mkdir sub
+'
+
+test_expect_success '"git log :/" should be ambiguous' '
+	test_must_fail git log :/ 2>error &&
+	grep ambiguous error
+'
+
+test_expect_success '"git log :" should be ambiguous' '
+	test_must_fail git log : 2>error &&
+	grep ambiguous error
+'
+
+test_expect_success 'git log -- :' '
+	git log -- :
+'
+
+test_expect_success 'git log HEAD -- :/' '
+	cat >expected <<-EOF &&
+	24b24cf initial
+	EOF
+	(cd sub && git log --oneline HEAD -- :/ >../actual) &&
+	test_cmp expected actual
+'
+
+test_done
diff --git a/t/t4209-log-pickaxe.sh b/t/t4209-log-pickaxe.sh
new file mode 100755
index 0000000..eed7273
--- /dev/null
+++ b/t/t4209-log-pickaxe.sh
@@ -0,0 +1,119 @@
+#!/bin/sh
+
+test_description='log --grep/--author/--regexp-ignore-case/-S/-G'
+. ./test-lib.sh
+
+test_expect_success setup '
+	>file &&
+	git add file &&
+	test_tick &&
+	git commit -m initial &&
+
+	echo Picked >file &&
+	test_tick &&
+	git commit -a --author="Another Person <another@example.com>" -m second
+'
+
+test_expect_success 'log --grep' '
+	git log --grep=initial --format=%H >actual &&
+	git rev-parse --verify HEAD^ >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'log --grep --regexp-ignore-case' '
+	git log --regexp-ignore-case --grep=InItial --format=%H >actual &&
+	git rev-parse --verify HEAD^ >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'log --grep -i' '
+	git log -i --grep=InItial --format=%H >actual &&
+	git rev-parse --verify HEAD^ >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'log --author --regexp-ignore-case' '
+	git log --regexp-ignore-case --author=person --format=%H >actual &&
+	git rev-parse --verify HEAD >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'log --author -i' '
+	git log -i --author=person --format=%H >actual &&
+	git rev-parse --verify HEAD >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'log -G (nomatch)' '
+	git log -Gpicked --format=%H >actual &&
+	>expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'log -G (match)' '
+	git log -GPicked --format=%H >actual &&
+	git rev-parse --verify HEAD >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'log -G --regexp-ignore-case (nomatch)' '
+	git log --regexp-ignore-case -Gpickle --format=%H >actual &&
+	>expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'log -G -i (nomatch)' '
+	git log -i -Gpickle --format=%H >actual &&
+	>expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'log -G --regexp-ignore-case (match)' '
+	git log --regexp-ignore-case -Gpicked --format=%H >actual &&
+	git rev-parse --verify HEAD >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'log -G -i (match)' '
+	git log -i -Gpicked --format=%H >actual &&
+	git rev-parse --verify HEAD >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'log -S (nomatch)' '
+	git log -Spicked --format=%H >actual &&
+	>expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'log -S (match)' '
+	git log -SPicked --format=%H >actual &&
+	git rev-parse --verify HEAD >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'log -S --regexp-ignore-case (match)' '
+	git log --regexp-ignore-case -Spicked --format=%H >actual &&
+	git rev-parse --verify HEAD >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'log -S -i (match)' '
+	git log -i -Spicked --format=%H >actual &&
+	git rev-parse --verify HEAD >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'log -S --regexp-ignore-case (nomatch)' '
+	git log --regexp-ignore-case -Spickle --format=%H >actual &&
+	>expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'log -S -i (nomatch)' '
+	git log -i -Spickle --format=%H >actual &&
+	>expect &&
+	test_cmp expect actual
+'
+
+test_done
diff --git a/t/t4252-am-options.sh b/t/t4252-am-options.sh
index f603c1b..e758e63 100755
--- a/t/t4252-am-options.sh
+++ b/t/t4252-am-options.sh
@@ -59,7 +59,7 @@
 '
 
 test_expect_success 'apply to a funny path' '
-	with_sq="with'\''sq"
+	with_sq="with'\''sq" &&
 	rm -fr .git/rebase-apply &&
 	git reset --hard initial &&
 	git am --directory="$with_sq" "$tm"/am-test-5-2 &&
diff --git a/t/t4253-am-keep-cr-dos.sh b/t/t4253-am-keep-cr-dos.sh
new file mode 100755
index 0000000..553fe3e
--- /dev/null
+++ b/t/t4253-am-keep-cr-dos.sh
@@ -0,0 +1,96 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Stefan-W. Hahn
+#
+
+test_description='git-am mbox with dos line ending.
+
+'
+. ./test-lib.sh
+
+# Three patches which will be added as files with dos line ending.
+
+cat >file1 <<\EOF
+line 1
+EOF
+
+cat >file1a <<\EOF
+line 1
+line 4
+EOF
+
+cat >file2 <<\EOF
+line 1
+line 2
+EOF
+
+cat >file3 <<\EOF
+line 1
+line 2
+line 3
+EOF
+
+test_expect_success 'setup repository with dos files' '
+	append_cr <file1 >file &&
+	git add file &&
+	git commit -m Initial &&
+	git tag initial &&
+	append_cr <file2 >file &&
+	git commit -a -m Second &&
+	append_cr <file3 >file &&
+	git commit -a -m Third
+'
+
+test_expect_success 'am with dos files without --keep-cr' '
+	git checkout -b dosfiles initial &&
+	git format-patch -k initial..master &&
+	test_must_fail git am -k -3 000*.patch &&
+	git am --abort &&
+	rm -rf .git/rebase-apply 000*.patch
+'
+
+test_expect_success 'am with dos files with --keep-cr' '
+	git checkout -b dosfiles-keep-cr initial &&
+	git format-patch -k --stdout initial..master | git am --keep-cr -k -3 &&
+	git diff --exit-code master
+'
+
+test_expect_success 'am with dos files config am.keepcr' '
+	git config am.keepcr 1 &&
+	git checkout -b dosfiles-conf-keepcr initial &&
+	git format-patch -k --stdout initial..master | git am -k -3 &&
+	git diff --exit-code master
+'
+
+test_expect_success 'am with dos files config am.keepcr overridden by --no-keep-cr' '
+	git config am.keepcr 1 &&
+	git checkout -b dosfiles-conf-keepcr-override initial &&
+	git format-patch -k initial..master &&
+	test_must_fail git am -k -3 --no-keep-cr 000*.patch &&
+	git am --abort &&
+	rm -rf .git/rebase-apply 000*.patch
+'
+
+test_expect_success 'am with dos files with --keep-cr continue' '
+	git checkout -b dosfiles-keep-cr-continue initial &&
+	git format-patch -k initial..master &&
+	append_cr <file1a >file &&
+	git commit -m "different patch" file &&
+	test_must_fail git am --keep-cr -k -3 000*.patch &&
+	append_cr <file2 >file &&
+	git add file &&
+	git am -3 --resolved &&
+	git diff --exit-code master
+'
+
+test_expect_success 'am with unix files config am.keepcr overridden by --no-keep-cr' '
+	git config am.keepcr 1 &&
+	git checkout -b unixfiles-conf-keepcr-override initial &&
+	cp -f file1 file &&
+	git commit -m "line ending to unix" file &&
+	git format-patch -k initial..master &&
+	git am -k -3 --no-keep-cr 000*.patch &&
+	git diff --exit-code -w master
+'
+
+test_done
diff --git a/t/t4254-am-corrupt.sh b/t/t4254-am-corrupt.sh
new file mode 100755
index 0000000..b7da95f
--- /dev/null
+++ b/t/t4254-am-corrupt.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+test_description='git am with corrupt input'
+. ./test-lib.sh
+
+# Note the missing "+++" line:
+cat > bad-patch.diff <<'EOF'
+From: A U Thor <au.thor@example.com>
+diff --git a/f b/f
+index 7898192..6178079 100644
+--- a/f
+@@ -1 +1 @@
+-a
++b
+EOF
+
+test_expect_success setup '
+	test $? = 0 &&
+	echo a > f &&
+	git add f &&
+	test_tick &&
+	git commit -m initial
+'
+
+# This used to fail before, too, but with a different diagnostic.
+#   fatal: unable to write file '(null)' mode 100644: Bad address
+# Also, it had the unwanted side-effect of deleting f.
+test_expect_success 'try to apply corrupted patch' '
+	git am bad-patch.diff 2> actual
+	test $? = 1
+'
+
+cat > expected <<EOF
+fatal: git diff header lacks filename information (line 4)
+EOF
+
+test_expect_success 'compare diagnostic; ensure file is still here' '
+	test $? = 0 &&
+	test -f f &&
+	test_cmp expected actual
+'
+
+test_done
diff --git a/t/t4300-merge-tree.sh b/t/t4300-merge-tree.sh
new file mode 100755
index 0000000..46c3fe7
--- /dev/null
+++ b/t/t4300-merge-tree.sh
@@ -0,0 +1,257 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Will Palmer
+#
+
+test_description='git merge-tree'
+. ./test-lib.sh
+
+test_expect_success setup '
+	test_commit "initial" "initial-file" "initial"
+'
+
+test_expect_success 'file add A, !B' '
+	cat >expected <<\EXPECTED &&
+added in remote
+  their  100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
+@@ -0,0 +1 @@
++AAA
+EXPECTED
+
+	git reset --hard initial &&
+	test_commit "add-a-not-b" "ONE" "AAA" &&
+	git merge-tree initial initial add-a-not-b >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'file add !A, B' '
+	cat >expected <<\EXPECTED &&
+added in local
+  our    100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
+EXPECTED
+
+	git reset --hard initial &&
+	test_commit "add-not-a-b" "ONE" "AAA" &&
+	git merge-tree initial add-not-a-b initial >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'file add A, B (same)' '
+	cat >expected <<\EXPECTED &&
+added in both
+  our    100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
+  their  100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
+EXPECTED
+
+	git reset --hard initial &&
+	test_commit "add-a-b-same-A" "ONE" "AAA" &&
+	git reset --hard initial &&
+	test_commit "add-a-b-same-B" "ONE" "AAA" &&
+	git merge-tree initial add-a-b-same-A add-a-b-same-B >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'file add A, B (different)' '
+	cat >expected <<\EXPECTED &&
+added in both
+  our    100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
+  their  100644 ba629238ca89489f2b350e196ca445e09d8bb834 ONE
+@@ -1 +1,5 @@
++<<<<<<< .our
+ AAA
++=======
++BBB
++>>>>>>> .their
+EXPECTED
+
+	git reset --hard initial &&
+	test_commit "add-a-b-diff-A" "ONE" "AAA" &&
+	git reset --hard initial &&
+	test_commit "add-a-b-diff-B" "ONE" "BBB" &&
+	git merge-tree initial add-a-b-diff-A add-a-b-diff-B >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'file change A, !B' '
+	cat >expected <<\EXPECTED &&
+EXPECTED
+
+	git reset --hard initial &&
+	test_commit "change-a-not-b" "initial-file" "BBB" &&
+	git merge-tree initial change-a-not-b initial >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'file change !A, B' '
+	cat >expected <<\EXPECTED &&
+merged
+  result 100644 ba629238ca89489f2b350e196ca445e09d8bb834 initial-file
+  our    100644 e79c5e8f964493290a409888d5413a737e8e5dd5 initial-file
+@@ -1 +1 @@
+-initial
++BBB
+EXPECTED
+
+	git reset --hard initial &&
+	test_commit "change-not-a-b" "initial-file" "BBB" &&
+	git merge-tree initial initial change-not-a-b >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'file change A, B (same)' '
+	cat >expected <<\EXPECTED &&
+EXPECTED
+
+	git reset --hard initial &&
+	test_commit "change-a-b-same-A" "initial-file" "AAA" &&
+	git reset --hard initial &&
+	test_commit "change-a-b-same-B" "initial-file" "AAA" &&
+	git merge-tree initial change-a-b-same-A change-a-b-same-B >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'file change A, B (different)' '
+	cat >expected <<\EXPECTED &&
+changed in both
+  base   100644 e79c5e8f964493290a409888d5413a737e8e5dd5 initial-file
+  our    100644 43d5a8ed6ef6c00ff775008633f95787d088285d initial-file
+  their  100644 ba629238ca89489f2b350e196ca445e09d8bb834 initial-file
+@@ -1 +1,5 @@
++<<<<<<< .our
+ AAA
++=======
++BBB
++>>>>>>> .their
+EXPECTED
+
+	git reset --hard initial &&
+	test_commit "change-a-b-diff-A" "initial-file" "AAA" &&
+	git reset --hard initial &&
+	test_commit "change-a-b-diff-B" "initial-file" "BBB" &&
+	git merge-tree initial change-a-b-diff-A change-a-b-diff-B >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'file change A, B (mixed)' '
+	cat >expected <<\EXPECTED &&
+changed in both
+  base   100644 f4f1f998c7776568c4ff38f516d77fef9399b5a7 ONE
+  our    100644 af14c2c3475337c73759d561ef70b59e5c731176 ONE
+  their  100644 372d761493f524d44d59bd24700c3bdf914c973c ONE
+@@ -7,7 +7,11 @@
+ AAA
+ AAA
+ AAA
++<<<<<<< .our
+ BBB
++=======
++CCC
++>>>>>>> .their
+ AAA
+ AAA
+ AAA
+EXPECTED
+
+	git reset --hard initial &&
+	test_commit "change-a-b-mix-base" "ONE" "
+AAA
+AAA
+AAA
+AAA
+AAA
+AAA
+AAA
+AAA
+AAA
+AAA
+AAA
+AAA
+AAA
+AAA
+AAA" &&
+	test_commit "change-a-b-mix-A" "ONE" \
+		"$(sed -e "1{s/AAA/BBB/;}" -e "10{s/AAA/BBB/;}" <ONE)" &&
+	git reset --hard change-a-b-mix-base &&
+	test_commit "change-a-b-mix-B" "ONE" \
+		"$(sed -e "1{s/AAA/BBB/;}" -e "10{s/AAA/CCC/;}" <ONE)" &&
+	git merge-tree change-a-b-mix-base change-a-b-mix-A change-a-b-mix-B \
+		>actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'file remove A, !B' '
+	cat >expected <<\EXPECTED &&
+removed in local
+  base   100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
+  their  100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
+EXPECTED
+
+	git reset --hard initial &&
+	test_commit "rm-a-not-b-base" "ONE" "AAA" &&
+	git rm ONE &&
+	git commit -m "rm-a-not-b" &&
+	git tag "rm-a-not-b" &&
+	git merge-tree rm-a-not-b-base rm-a-not-b rm-a-not-b-base >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'file remove !A, B' '
+	cat >expected <<\EXPECTED &&
+removed in remote
+  base   100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
+  our    100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
+@@ -1 +0,0 @@
+-AAA
+EXPECTED
+
+	git reset --hard initial &&
+	test_commit "rm-not-a-b-base" "ONE" "AAA" &&
+	git rm ONE &&
+	git commit -m "rm-not-a-b" &&
+	git tag "rm-not-a-b" &&
+	git merge-tree rm-a-not-b-base rm-a-not-b-base rm-a-not-b >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'file change A, remove B' '
+	cat >expected <<\EXPECTED &&
+removed in remote
+  base   100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
+  our    100644 ba629238ca89489f2b350e196ca445e09d8bb834 ONE
+@@ -1 +0,0 @@
+-BBB
+EXPECTED
+
+	git reset --hard initial &&
+	test_commit "change-a-rm-b-base" "ONE" "AAA" &&
+	test_commit "change-a-rm-b-A" "ONE" "BBB" &&
+	git reset --hard change-a-rm-b-base &&
+	git rm ONE &&
+	git commit -m "change-a-rm-b-B" &&
+	git tag "change-a-rm-b-B" &&
+	git merge-tree change-a-rm-b-base change-a-rm-b-A change-a-rm-b-B \
+		>actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'file remove A, change B' '
+	cat >expected <<\EXPECTED &&
+removed in local
+  base   100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
+  their  100644 ba629238ca89489f2b350e196ca445e09d8bb834 ONE
+EXPECTED
+
+	git reset --hard initial &&
+	test_commit "rm-a-change-b-base" "ONE" "AAA" &&
+
+	git rm ONE &&
+	git commit -m "rm-a-change-b-A" &&
+	git tag "rm-a-change-b-A" &&
+	git reset --hard rm-a-change-b-base &&
+	test_commit "rm-a-change-b-B" "ONE" "BBB" &&
+	git merge-tree rm-a-change-b-base rm-a-change-b-A rm-a-change-b-B \
+		>actual &&
+	test_cmp expected actual
+'
+
+test_done
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 5f84b18..ecf00ed 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -26,9 +26,31 @@
 
 . ./test-lib.sh
 UNZIP=${UNZIP:-unzip}
+GZIP=${GZIP:-gzip}
+GUNZIP=${GUNZIP:-gzip -d}
 
 SUBSTFORMAT=%H%n
 
+check_zip() {
+	zipfile=$1.zip
+	listfile=$1.lst
+	dir=$1
+	dir_with_prefix=$dir/$2
+
+	test_expect_success UNZIP " extract ZIP archive" "
+		(mkdir $dir && cd $dir && $UNZIP ../$zipfile)
+	"
+
+	test_expect_success UNZIP " validate filenames" "
+		(cd ${dir_with_prefix}a && find .) | sort >$listfile &&
+		test_cmp a.lst $listfile
+	"
+
+	test_expect_success UNZIP " validate file contents" "
+		diff -r a ${dir_with_prefix}a
+	"
+}
+
 test_expect_success \
     'populate workdir' \
     'mkdir a b c &&
@@ -82,6 +104,12 @@
     'git archive vs. git tar-tree' \
     'test_cmp b.tar b2.tar'
 
+test_expect_success 'git archive on large files' '
+    test_config core.bigfilethreshold 1 &&
+    git archive HEAD >b3.tar &&
+    test_cmp b.tar b3.tar
+'
+
 test_expect_success \
     'git archive in a bare repo' \
     '(cd bare.git && git archive HEAD) >b3.tar'
@@ -173,10 +201,19 @@
       test_cmp a/substfile2 g/prefix/a/substfile2
 '
 
+$UNZIP -v >/dev/null 2>&1
+if [ $? -eq 127 ]; then
+	say "Skipping ZIP tests, because unzip was not found"
+else
+	test_set_prereq UNZIP
+fi
+
 test_expect_success \
     'git archive --format=zip' \
     'git archive --format=zip HEAD >d.zip'
 
+check_zip d
+
 test_expect_success \
     'git archive --format=zip in a bare repo' \
     '(cd bare.git && git archive --format=zip HEAD) >d1.zip'
@@ -189,45 +226,156 @@
     'git archive --format=zip --output=d2.zip HEAD &&
     test_cmp d.zip d2.zip'
 
-$UNZIP -v >/dev/null 2>&1
-if [ $? -eq 127 ]; then
-	say "Skipping ZIP tests, because unzip was not found"
-else
-	test_set_prereq UNZIP
-fi
+test_expect_success 'git archive with --output, inferring format' '
+	git archive --output=d3.zip HEAD &&
+	test_cmp d.zip d3.zip
+'
 
-test_expect_success UNZIP \
-    'extract ZIP archive' \
-    '(mkdir d && cd d && $UNZIP ../d.zip)'
-
-test_expect_success UNZIP \
-    'validate filenames' \
-    '(cd d/a && find .) | sort >d.lst &&
-     test_cmp a.lst d.lst'
-
-test_expect_success UNZIP \
-    'validate file contents' \
-    'diff -r a d/a'
+test_expect_success 'git archive with --output, override inferred format' '
+	git archive --format=tar --output=d4.zip HEAD &&
+	test_cmp b.tar d4.zip
+'
 
 test_expect_success \
     'git archive --format=zip with prefix' \
     'git archive --format=zip --prefix=prefix/ HEAD >e.zip'
 
-test_expect_success UNZIP \
-    'extract ZIP archive with prefix' \
-    '(mkdir e && cd e && $UNZIP ../e.zip)'
+check_zip e prefix/
 
-test_expect_success UNZIP \
-    'validate filenames with prefix' \
-    '(cd e/prefix/a && find .) | sort >e.lst &&
-     test_cmp a.lst e.lst'
+test_expect_success 'git archive -0 --format=zip on large files' '
+	test_config core.bigfilethreshold 1 &&
+	git archive -0 --format=zip HEAD >large.zip
+'
 
-test_expect_success UNZIP \
-    'validate file contents with prefix' \
-    'diff -r a e/prefix/a'
+check_zip large
+
+test_expect_success 'git archive --format=zip on large files' '
+	test_config core.bigfilethreshold 1 &&
+	git archive --format=zip HEAD >large-compressed.zip
+'
+
+check_zip large-compressed
 
 test_expect_success \
     'git archive --list outside of a git repo' \
     'GIT_DIR=some/non-existing/directory git archive --list'
 
+test_expect_success 'clients cannot access unreachable commits' '
+	test_commit unreachable &&
+	sha1=`git rev-parse HEAD` &&
+	git reset --hard HEAD^ &&
+	git archive $sha1 >remote.tar &&
+	test_must_fail git archive --remote=. $sha1 >remote.tar
+'
+
+test_expect_success 'git-archive --prefix=olde-' '
+	git archive --prefix=olde- >h.tar HEAD &&
+	(
+		mkdir h &&
+		cd h &&
+		"$TAR" xf - <../h.tar
+	) &&
+	test -d h/olde-a &&
+	test -d h/olde-a/bin &&
+	test -f h/olde-a/bin/sh
+'
+
+test_expect_success 'setup tar filters' '
+	git config tar.tar.foo.command "tr ab ba" &&
+	git config tar.bar.command "tr ab ba" &&
+	git config tar.bar.remote true
+'
+
+test_expect_success 'archive --list mentions user filter' '
+	git archive --list >output &&
+	grep "^tar\.foo\$" output &&
+	grep "^bar\$" output
+'
+
+test_expect_success 'archive --list shows only enabled remote filters' '
+	git archive --list --remote=. >output &&
+	! grep "^tar\.foo\$" output &&
+	grep "^bar\$" output
+'
+
+test_expect_success 'invoke tar filter by format' '
+	git archive --format=tar.foo HEAD >config.tar.foo &&
+	tr ab ba <config.tar.foo >config.tar &&
+	test_cmp b.tar config.tar &&
+	git archive --format=bar HEAD >config.bar &&
+	tr ab ba <config.bar >config.tar &&
+	test_cmp b.tar config.tar
+'
+
+test_expect_success 'invoke tar filter by extension' '
+	git archive -o config-implicit.tar.foo HEAD &&
+	test_cmp config.tar.foo config-implicit.tar.foo &&
+	git archive -o config-implicit.bar HEAD &&
+	test_cmp config.tar.foo config-implicit.bar
+'
+
+test_expect_success 'default output format remains tar' '
+	git archive -o config-implicit.baz HEAD &&
+	test_cmp b.tar config-implicit.baz
+'
+
+test_expect_success 'extension matching requires dot' '
+	git archive -o config-implicittar.foo HEAD &&
+	test_cmp b.tar config-implicittar.foo
+'
+
+test_expect_success 'only enabled filters are available remotely' '
+	test_must_fail git archive --remote=. --format=tar.foo HEAD \
+		>remote.tar.foo &&
+	git archive --remote=. --format=bar >remote.bar HEAD &&
+	test_cmp remote.bar config.bar
+'
+
+if $GZIP --version >/dev/null 2>&1; then
+	test_set_prereq GZIP
+else
+	say "Skipping some tar.gz tests because gzip not found"
+fi
+
+test_expect_success GZIP 'git archive --format=tgz' '
+	git archive --format=tgz HEAD >j.tgz
+'
+
+test_expect_success GZIP 'git archive --format=tar.gz' '
+	git archive --format=tar.gz HEAD >j1.tar.gz &&
+	test_cmp j.tgz j1.tar.gz
+'
+
+test_expect_success GZIP 'infer tgz from .tgz filename' '
+	git archive --output=j2.tgz HEAD &&
+	test_cmp j.tgz j2.tgz
+'
+
+test_expect_success GZIP 'infer tgz from .tar.gz filename' '
+	git archive --output=j3.tar.gz HEAD &&
+	test_cmp j.tgz j3.tar.gz
+'
+
+if $GUNZIP --version >/dev/null 2>&1; then
+	test_set_prereq GUNZIP
+else
+	say "Skipping some tar.gz tests because gunzip was not found"
+fi
+
+test_expect_success GZIP,GUNZIP 'extract tgz file' '
+	$GUNZIP -c <j.tgz >j.tar &&
+	test_cmp b.tar j.tar
+'
+
+test_expect_success GZIP 'remote tar.gz is allowed by default' '
+	git archive --remote=. --format=tar.gz HEAD >remote.tar.gz &&
+	test_cmp j.tgz remote.tar.gz
+'
+
+test_expect_success GZIP 'remote tar.gz can be disabled' '
+	git config tar.tar.gz.remote false &&
+	test_must_fail git archive --remote=. --format=tar.gz HEAD \
+		>remote.tar.gz
+'
+
 test_done
diff --git a/t/t5001-archive-attr.sh b/t/t5001-archive-attr.sh
index 426b319..f47d871 100755
--- a/t/t5001-archive-attr.sh
+++ b/t/t5001-archive-attr.sh
@@ -4,7 +4,7 @@
 
 . ./test-lib.sh
 
-SUBSTFORMAT=%H%n
+SUBSTFORMAT='%H (%h)%n'
 
 test_expect_exists() {
 	test_expect_success " $1 exists" "test -e $1"
@@ -57,6 +57,15 @@
 test_expect_exists	worktree/ignored-by-tree
 test_expect_missing	worktree/ignored-by-worktree
 
+test_expect_success 'git archive --worktree-attributes option' '
+	git archive --worktree-attributes --worktree-attributes HEAD >worktree.tar &&
+	(mkdir worktree2 && cd worktree2 && "$TAR" xf -) <worktree.tar
+'
+
+test_expect_missing	worktree2/ignored
+test_expect_exists	worktree2/ignored-by-tree
+test_expect_missing	worktree2/ignored-by-worktree
+
 test_expect_success 'git archive vs. bare' '
 	(cd bare && git archive HEAD) >bare-archive.tar &&
 	test_cmp archive.tar bare-archive.tar
diff --git a/t/t5100-mailinfo.sh b/t/t5100-mailinfo.sh
index e70ea94..81904d9 100755
--- a/t/t5100-mailinfo.sh
+++ b/t/t5100-mailinfo.sh
@@ -11,18 +11,30 @@
 	'git mailsplit -o. "$TEST_DIRECTORY"/t5100/sample.mbox >last &&
 	last=`cat last` &&
 	echo total is $last &&
-	test `cat last` = 13'
+	test `cat last` = 16'
+
+check_mailinfo () {
+	mail=$1 opt=$2
+	mo="$mail$opt"
+	git mailinfo -u $opt msg$mo patch$mo <$mail >info$mo &&
+	test_cmp "$TEST_DIRECTORY"/t5100/msg$mo msg$mo &&
+	test_cmp "$TEST_DIRECTORY"/t5100/patch$mo patch$mo &&
+	test_cmp "$TEST_DIRECTORY"/t5100/info$mo info$mo
+}
+
 
 for mail in `echo 00*`
 do
 	test_expect_success "mailinfo $mail" '
-		git mailinfo -u msg$mail patch$mail <$mail >info$mail &&
-		echo msg &&
-		test_cmp "$TEST_DIRECTORY"/t5100/msg$mail msg$mail &&
-		echo patch &&
-		test_cmp "$TEST_DIRECTORY"/t5100/patch$mail patch$mail &&
-		echo info &&
-		test_cmp "$TEST_DIRECTORY"/t5100/info$mail info$mail
+		check_mailinfo $mail "" &&
+		if test -f "$TEST_DIRECTORY"/t5100/msg$mail--scissors
+		then
+			check_mailinfo $mail --scissors
+		fi &&
+		if test -f "$TEST_DIRECTORY"/t5100/msg$mail--no-inbody-headers
+		then
+			check_mailinfo $mail --no-inbody-headers
+		fi
 	'
 done
 
@@ -53,7 +65,7 @@
 	git mailsplit -d3 -o. "$TEST_DIRECTORY"/t5100/nul-plain &&
 	test_cmp "$TEST_DIRECTORY"/t5100/nul-plain 001 &&
 	(cat 001 | git mailinfo msg patch) &&
-	test 4 = $(wc -l < patch)
+	test_line_count = 4 patch
 
 '
 
diff --git a/t/t5100/.gitattributes b/t/t5100/.gitattributes
new file mode 100644
index 0000000..c93f514
--- /dev/null
+++ b/t/t5100/.gitattributes
@@ -0,0 +1,4 @@
+msg*	encoding=UTF-8
+info*	encoding=UTF-8
+rfc2047-info-*	encoding=UTF-8
+sample.mbox	encoding=UTF-8
diff --git a/t/t5100/0010 b/t/t5100/0010
deleted file mode 100644
index f5892c9..0000000
--- a/t/t5100/0010
+++ /dev/null
@@ -1,35 +0,0 @@
-From b9704a518e21158433baa2cc2d591fea687967f6 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Lukas=20Sandstr=C3=B6m?= <lukass@etek.chalmers.se>
-Date: Thu, 10 Jul 2008 23:41:33 +0200
-Subject: Re: discussion that lead to this patch
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-[PATCH] git-mailinfo: Fix getting the subject from the body
-
-"Subject: " isn't in the static array "header", and thus
-memcmp("Subject: ", header[i], 7) will never match.
-
-Signed-off-by: Lukas Sandström <lukass@etek.chalmers.se>
-Signed-off-by: Junio C Hamano <gitster@pobox.com>
----
- builtin-mailinfo.c |    2 +-
- 1 files changed, 1 insertions(+), 1 deletions(-)
-
-diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
-index 962aa34..2d1520f 100644
---- a/builtin-mailinfo.c
-+++ b/builtin-mailinfo.c
-@@ -334,7 +334,7 @@ static int check_header(char *line, unsigned linesize, char **hdr_data, int over
- 		return 1;
- 	if (!memcmp("[PATCH]", line, 7) && isspace(line[7])) {
- 		for (i = 0; header[i]; i++) {
--			if (!memcmp("Subject: ", header[i], 9)) {
-+			if (!memcmp("Subject", header[i], 7)) {
- 				if (! handle_header(line, hdr_data[i], 0)) {
- 					return 1;
- 				}
--- 
-1.5.6.2.455.g1efb2
-
diff --git a/t/t5100/info0014 b/t/t5100/info0014
new file mode 100644
index 0000000..08566b3
--- /dev/null
+++ b/t/t5100/info0014
@@ -0,0 +1,5 @@
+Author: Junio Hamano
+Email: junkio@cox.net
+Subject: BLAH ONE
+Date: Thu, 20 Aug 2009 17:18:22 -0700
+
diff --git a/t/t5100/info0014--scissors b/t/t5100/info0014--scissors
new file mode 100644
index 0000000..ab9c8d0
--- /dev/null
+++ b/t/t5100/info0014--scissors
@@ -0,0 +1,5 @@
+Author: Junio C Hamano
+Email: gitster@pobox.com
+Subject: Teach mailinfo to ignore everything before -- >8 -- mark
+Date: Thu, 20 Aug 2009 17:18:22 -0700
+
diff --git a/t/t5100/info0015 b/t/t5100/info0015
new file mode 100644
index 0000000..0114f10
--- /dev/null
+++ b/t/t5100/info0015
@@ -0,0 +1,5 @@
+Author: 
+Email: 
+Subject: check bogus body header (from)
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+
diff --git a/t/t5100/info0015--no-inbody-headers b/t/t5100/info0015--no-inbody-headers
new file mode 100644
index 0000000..c4d8d77
--- /dev/null
+++ b/t/t5100/info0015--no-inbody-headers
@@ -0,0 +1,5 @@
+Author: A U Thor
+Email: a.u.thor@example.com
+Subject: check bogus body header (from)
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+
diff --git a/t/t5100/info0016 b/t/t5100/info0016
new file mode 100644
index 0000000..38ccd0d
--- /dev/null
+++ b/t/t5100/info0016
@@ -0,0 +1,5 @@
+Author: A U Thor
+Email: a.u.thor@example.com
+Subject: check bogus body header (date)
+Date: bogus 
+
diff --git a/t/t5100/info0016--no-inbody-headers b/t/t5100/info0016--no-inbody-headers
new file mode 100644
index 0000000..f4857d4
--- /dev/null
+++ b/t/t5100/info0016--no-inbody-headers
@@ -0,0 +1,5 @@
+Author: A U Thor
+Email: a.u.thor@example.com
+Subject: check bogus body header (date)
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+
diff --git a/t/t5100/msg0014 b/t/t5100/msg0014
new file mode 100644
index 0000000..62e5cd2
--- /dev/null
+++ b/t/t5100/msg0014
@@ -0,0 +1,18 @@
+In real life, we will see a discussion that inspired this patch
+discussing related and unrelated things around >8 scissors mark
+in this part of the message.
+
+Subject: [PATCH] BLAH TWO
+
+And then we will see the scissors.
+
+ This line is not a scissors mark -- >8 -- but talks about it.
+ - - >8 - - please remove everything above this line - - >8 - -
+
+Subject: [PATCH] Teach mailinfo to ignore everything before -- >8 -- mark
+From: Junio C Hamano <gitster@pobox.com>
+
+This teaches mailinfo the scissors -- >8 -- mark; the command ignores
+everything before it in the message body.
+
+Signed-off-by: Junio C Hamano <gitster@pobox.com>
diff --git a/t/t5100/msg0014--scissors b/t/t5100/msg0014--scissors
new file mode 100644
index 0000000..259c6a4
--- /dev/null
+++ b/t/t5100/msg0014--scissors
@@ -0,0 +1,4 @@
+This teaches mailinfo the scissors -- >8 -- mark; the command ignores
+everything before it in the message body.
+
+Signed-off-by: Junio C Hamano <gitster@pobox.com>
diff --git a/t/t5100/msg0015 b/t/t5100/msg0015
new file mode 100644
index 0000000..4abb3d5
--- /dev/null
+++ b/t/t5100/msg0015
@@ -0,0 +1,2 @@
+  - a list
+  - of stuff
diff --git a/t/t5100/msg0015--no-inbody-headers b/t/t5100/msg0015--no-inbody-headers
new file mode 100644
index 0000000..be5115b
--- /dev/null
+++ b/t/t5100/msg0015--no-inbody-headers
@@ -0,0 +1,3 @@
+From: bogosity
+  - a list
+  - of stuff
diff --git a/t/t5100/msg0016 b/t/t5100/msg0016
new file mode 100644
index 0000000..0d9adad
--- /dev/null
+++ b/t/t5100/msg0016
@@ -0,0 +1,2 @@
+and some content
+
diff --git a/t/t5100/msg0016--no-inbody-headers b/t/t5100/msg0016--no-inbody-headers
new file mode 100644
index 0000000..1063f51
--- /dev/null
+++ b/t/t5100/msg0016--no-inbody-headers
@@ -0,0 +1,4 @@
+Date: bogus
+
+and some content
+
diff --git a/t/t5100/patch0001 b/t/t5100/patch0001
index 8ce1551..02c9774 100644
--- a/t/t5100/patch0001
+++ b/t/t5100/patch0001
@@ -1,5 +1,5 @@
 ---
- foo |    2 +-
+ foo | 2 +-
  1 files changed, 1 insertions(+), 1 deletions(-)
 
 diff --git a/foo b/foo
diff --git a/t/t5100/patch0002 b/t/t5100/patch0002
index 8ce1551..02c9774 100644
--- a/t/t5100/patch0002
+++ b/t/t5100/patch0002
@@ -1,5 +1,5 @@
 ---
- foo |    2 +-
+ foo | 2 +-
  1 files changed, 1 insertions(+), 1 deletions(-)
 
 diff --git a/foo b/foo
diff --git a/t/t5100/patch0003 b/t/t5100/patch0003
index 8ce1551..02c9774 100644
--- a/t/t5100/patch0003
+++ b/t/t5100/patch0003
@@ -1,5 +1,5 @@
 ---
- foo |    2 +-
+ foo | 2 +-
  1 files changed, 1 insertions(+), 1 deletions(-)
 
 diff --git a/foo b/foo
diff --git a/t/t5100/patch0005 b/t/t5100/patch0005
index 7d24b24..ab7a383 100644
--- a/t/t5100/patch0005
+++ b/t/t5100/patch0005
@@ -1,7 +1,7 @@
 ---
 
- Documentation/git-cvsimport-script.txt |    9 ++++++++-
- git-cvsimport-script                   |    4 ++--
+ Documentation/git-cvsimport-script.txt | 9 ++++++++-
+ git-cvsimport-script                   | 4 ++--
  2 files changed, 10 insertions(+), 3 deletions(-)
 
 50452f9c0c2df1f04d83a26266ba704b13861632
diff --git a/t/t5100/patch0006 b/t/t5100/patch0006
index 8ce1551..02c9774 100644
--- a/t/t5100/patch0006
+++ b/t/t5100/patch0006
@@ -1,5 +1,5 @@
 ---
- foo |    2 +-
+ foo | 2 +-
  1 files changed, 1 insertions(+), 1 deletions(-)
 
 diff --git a/foo b/foo
diff --git a/t/t5100/patch0010 b/t/t5100/patch0010
index f055481..436821c 100644
--- a/t/t5100/patch0010
+++ b/t/t5100/patch0010
@@ -1,5 +1,5 @@
 ---
- builtin-mailinfo.c |    2 +-
+ builtin-mailinfo.c | 2 +-
  1 files changed, 1 insertions(+), 1 deletions(-)
 
 diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
diff --git a/t/t5100/patch0011 b/t/t5100/patch0011
index 8841d3c..0988713 100644
--- a/t/t5100/patch0011
+++ b/t/t5100/patch0011
@@ -1,5 +1,5 @@
 ---
- builtin-mailinfo.c  |    4 ++--
+ builtin-mailinfo.c  | 4 ++--
 
 diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
 index 3e5fe51..aabfe5c 100644
diff --git a/t/t5100/patch0014 b/t/t5100/patch0014
new file mode 100644
index 0000000..3f3825f
--- /dev/null
+++ b/t/t5100/patch0014
@@ -0,0 +1,64 @@
+---
+ builtin-mailinfo.c | 37 ++++++++++++++++++++++++++++++++++++-
+ 1 files changed, 36 insertions(+), 1 deletions(-)
+
+diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
+index b0b5d8f..461c47e 100644
+--- a/builtin-mailinfo.c
++++ b/builtin-mailinfo.c
+@@ -712,6 +712,34 @@ static inline int patchbreak(const struct strbuf *line)
+ 	return 0;
+ }
+ 
++static int scissors(const struct strbuf *line)
++{
++	size_t i, len = line->len;
++	int scissors_dashes_seen = 0;
++	const char *buf = line->buf;
++
++	for (i = 0; i < len; i++) {
++		if (isspace(buf[i]))
++			continue;
++		if (buf[i] == '-') {
++			scissors_dashes_seen |= 02;
++			continue;
++		}
++		if (i + 1 < len && !memcmp(buf + i, ">8", 2)) {
++			scissors_dashes_seen |= 01;
++			i++;
++			continue;
++		}
++		if (i + 7 < len && !memcmp(buf + i, "cut here", 8)) {
++			i += 7;
++			continue;
++		}
++		/* everything else --- not scissors */
++		break;
++	}
++	return scissors_dashes_seen == 03;
++}
++
+ static int handle_commit_msg(struct strbuf *line)
+ {
+ 	static int still_looking = 1;
+@@ -723,10 +751,17 @@ static int handle_commit_msg(struct strbuf *line)
+ 		strbuf_ltrim(line);
+ 		if (!line->len)
+ 			return 0;
+-		if ((still_looking = check_header(line, s_hdr_data, 0)) != 0)
++		still_looking = check_header(line, s_hdr_data, 0);
++		if (still_looking)
+ 			return 0;
+ 	}
+ 
++	if (scissors(line)) {
++		fseek(cmitmsg, 0L, SEEK_SET);
++		still_looking = 1;
++		return 0;
++	}
++
+ 	/* normalize the log message to UTF-8. */
+ 	if (metainfo_charset)
+ 		convert_to_utf8(line, charset.buf);
+-- 
+1.6.4.1
diff --git a/t/t5100/patch0014--scissors b/t/t5100/patch0014--scissors
new file mode 100644
index 0000000..3f3825f
--- /dev/null
+++ b/t/t5100/patch0014--scissors
@@ -0,0 +1,64 @@
+---
+ builtin-mailinfo.c | 37 ++++++++++++++++++++++++++++++++++++-
+ 1 files changed, 36 insertions(+), 1 deletions(-)
+
+diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
+index b0b5d8f..461c47e 100644
+--- a/builtin-mailinfo.c
++++ b/builtin-mailinfo.c
+@@ -712,6 +712,34 @@ static inline int patchbreak(const struct strbuf *line)
+ 	return 0;
+ }
+ 
++static int scissors(const struct strbuf *line)
++{
++	size_t i, len = line->len;
++	int scissors_dashes_seen = 0;
++	const char *buf = line->buf;
++
++	for (i = 0; i < len; i++) {
++		if (isspace(buf[i]))
++			continue;
++		if (buf[i] == '-') {
++			scissors_dashes_seen |= 02;
++			continue;
++		}
++		if (i + 1 < len && !memcmp(buf + i, ">8", 2)) {
++			scissors_dashes_seen |= 01;
++			i++;
++			continue;
++		}
++		if (i + 7 < len && !memcmp(buf + i, "cut here", 8)) {
++			i += 7;
++			continue;
++		}
++		/* everything else --- not scissors */
++		break;
++	}
++	return scissors_dashes_seen == 03;
++}
++
+ static int handle_commit_msg(struct strbuf *line)
+ {
+ 	static int still_looking = 1;
+@@ -723,10 +751,17 @@ static int handle_commit_msg(struct strbuf *line)
+ 		strbuf_ltrim(line);
+ 		if (!line->len)
+ 			return 0;
+-		if ((still_looking = check_header(line, s_hdr_data, 0)) != 0)
++		still_looking = check_header(line, s_hdr_data, 0);
++		if (still_looking)
+ 			return 0;
+ 	}
+ 
++	if (scissors(line)) {
++		fseek(cmitmsg, 0L, SEEK_SET);
++		still_looking = 1;
++		return 0;
++	}
++
+ 	/* normalize the log message to UTF-8. */
+ 	if (metainfo_charset)
+ 		convert_to_utf8(line, charset.buf);
+-- 
+1.6.4.1
diff --git a/t/t5100/patch0015 b/t/t5100/patch0015
new file mode 100644
index 0000000..ad64848
--- /dev/null
+++ b/t/t5100/patch0015
@@ -0,0 +1,8 @@
+---
+diff --git a/foo b/foo
+index e69de29..d95f3ad 100644
+--- a/foo
++++ b/foo
+@@ -0,0 +1 @@
++content
+
diff --git a/t/t5100/patch0015--no-inbody-headers b/t/t5100/patch0015--no-inbody-headers
new file mode 100644
index 0000000..ad64848
--- /dev/null
+++ b/t/t5100/patch0015--no-inbody-headers
@@ -0,0 +1,8 @@
+---
+diff --git a/foo b/foo
+index e69de29..d95f3ad 100644
+--- a/foo
++++ b/foo
+@@ -0,0 +1 @@
++content
+
diff --git a/t/t5100/patch0016 b/t/t5100/patch0016
new file mode 100644
index 0000000..ad64848
--- /dev/null
+++ b/t/t5100/patch0016
@@ -0,0 +1,8 @@
+---
+diff --git a/foo b/foo
+index e69de29..d95f3ad 100644
+--- a/foo
++++ b/foo
+@@ -0,0 +1 @@
++content
+
diff --git a/t/t5100/patch0016--no-inbody-headers b/t/t5100/patch0016--no-inbody-headers
new file mode 100644
index 0000000..ad64848
--- /dev/null
+++ b/t/t5100/patch0016--no-inbody-headers
@@ -0,0 +1,8 @@
+---
+diff --git a/foo b/foo
+index e69de29..d95f3ad 100644
+--- a/foo
++++ b/foo
+@@ -0,0 +1 @@
++content
+
diff --git a/t/t5100/sample.mbox b/t/t5100/sample.mbox
index c3074ac..34a09a0 100644
--- a/t/t5100/sample.mbox
+++ b/t/t5100/sample.mbox
@@ -12,7 +12,7 @@
 Here is a patch from A U Thor.
 
 ---
- foo |    2 +-
+ foo | 2 +-
  1 files changed, 1 insertions(+), 1 deletions(-)
 
 diff --git a/foo b/foo
@@ -52,7 +52,7 @@
 Hope this helps.
 
 ---
- foo |    2 +-
+ foo | 2 +-
  1 files changed, 1 insertions(+), 1 deletions(-)
 
 diff --git a/foo b/foo
@@ -83,7 +83,7 @@
 Hopefully this would fix the problem stated there.
 
 ---
- foo |    2 +-
+ foo | 2 +-
  1 files changed, 1 insertions(+), 1 deletions(-)
 
 diff --git a/foo b/foo
@@ -249,8 +249,8 @@
 Signed-off-by: David K=E5gedal <davidk@lysator.liu.se>
 ---
 
- Documentation/git-cvsimport-script.txt |    9 ++++++++-
- git-cvsimport-script                   |    4 ++--
+ Documentation/git-cvsimport-script.txt | 9 ++++++++-
+ git-cvsimport-script                   | 4 ++--
  2 files changed, 10 insertions(+), 3 deletions(-)
 
 50452f9c0c2df1f04d83a26266ba704b13861632
@@ -379,7 +379,7 @@
 Here is a patch from A U Thor.
 
 ---
- foo |    2 +-
+ foo | 2 +-
  1 files changed, 1 insertions(+), 1 deletions(-)
 
 diff --git a/foo b/foo
@@ -449,7 +449,7 @@
 Signed-off-by: Lukas Sandström <lukass@etek.chalmers.se>
 Signed-off-by: Junio C Hamano <gitster@pobox.com>
 ---
- builtin-mailinfo.c |    2 +-
+ builtin-mailinfo.c | 2 +-
  1 files changed, 1 insertions(+), 1 deletions(-)
 
 diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
@@ -482,7 +482,7 @@
 Here comes a commit log message, and
 its second line is here.
 ---
- builtin-mailinfo.c  |    4 ++--
+ builtin-mailinfo.c  | 4 ++--
 
 diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
 index 3e5fe51..aabfe5c 100644
@@ -561,3 +561,125 @@
 Date: Fri, 9 Jun 2006 00:44:16 -0700
 Subject: [PATCH] a patch
 
+From nobody Mon Sep 17 00:00:00 2001
+From: Junio Hamano <junkio@cox.net>
+Date: Thu, 20 Aug 2009 17:18:22 -0700
+Subject: Why doesn't git-am does not like >8 scissors mark?
+
+Subject: [PATCH] BLAH ONE
+
+In real life, we will see a discussion that inspired this patch
+discussing related and unrelated things around >8 scissors mark
+in this part of the message.
+
+Subject: [PATCH] BLAH TWO
+
+And then we will see the scissors.
+
+ This line is not a scissors mark -- >8 -- but talks about it.
+ - - >8 - - please remove everything above this line - - >8 - -
+
+Subject: [PATCH] Teach mailinfo to ignore everything before -- >8 -- mark
+From: Junio C Hamano <gitster@pobox.com>
+
+This teaches mailinfo the scissors -- >8 -- mark; the command ignores
+everything before it in the message body.
+
+Signed-off-by: Junio C Hamano <gitster@pobox.com>
+---
+ builtin-mailinfo.c | 37 ++++++++++++++++++++++++++++++++++++-
+ 1 files changed, 36 insertions(+), 1 deletions(-)
+
+diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
+index b0b5d8f..461c47e 100644
+--- a/builtin-mailinfo.c
++++ b/builtin-mailinfo.c
+@@ -712,6 +712,34 @@ static inline int patchbreak(const struct strbuf *line)
+ 	return 0;
+ }
+ 
++static int scissors(const struct strbuf *line)
++{
++	size_t i, len = line->len;
++	int scissors_dashes_seen = 0;
++	const char *buf = line->buf;
++
++	for (i = 0; i < len; i++) {
++		if (isspace(buf[i]))
++			continue;
++		if (buf[i] == '-') {
++			scissors_dashes_seen |= 02;
++			continue;
++		}
++		if (i + 1 < len && !memcmp(buf + i, ">8", 2)) {
++			scissors_dashes_seen |= 01;
++			i++;
++			continue;
++		}
++		if (i + 7 < len && !memcmp(buf + i, "cut here", 8)) {
++			i += 7;
++			continue;
++		}
++		/* everything else --- not scissors */
++		break;
++	}
++	return scissors_dashes_seen == 03;
++}
++
+ static int handle_commit_msg(struct strbuf *line)
+ {
+ 	static int still_looking = 1;
+@@ -723,10 +751,17 @@ static int handle_commit_msg(struct strbuf *line)
+ 		strbuf_ltrim(line);
+ 		if (!line->len)
+ 			return 0;
+-		if ((still_looking = check_header(line, s_hdr_data, 0)) != 0)
++		still_looking = check_header(line, s_hdr_data, 0);
++		if (still_looking)
+ 			return 0;
+ 	}
+ 
++	if (scissors(line)) {
++		fseek(cmitmsg, 0L, SEEK_SET);
++		still_looking = 1;
++		return 0;
++	}
++
+ 	/* normalize the log message to UTF-8. */
+ 	if (metainfo_charset)
+ 		convert_to_utf8(line, charset.buf);
+-- 
+1.6.4.1
+From nobody Mon Sep 17 00:00:00 2001
+From: A U Thor <a.u.thor@example.com>
+Subject: check bogus body header (from)
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+
+From: bogosity
+  - a list
+  - of stuff
+---
+diff --git a/foo b/foo
+index e69de29..d95f3ad 100644
+--- a/foo
++++ b/foo
+@@ -0,0 +1 @@
++content
+
+From nobody Mon Sep 17 00:00:00 2001
+From: A U Thor <a.u.thor@example.com>
+Subject: check bogus body header (date)
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+
+Date: bogus
+
+and some content
+
+---
+diff --git a/foo b/foo
+index e69de29..d95f3ad 100644
+--- a/foo
++++ b/foo
+@@ -0,0 +1 @@
++content
+
diff --git a/t/t5150-request-pull.sh b/t/t5150-request-pull.sh
new file mode 100755
index 0000000..432f98c
--- /dev/null
+++ b/t/t5150-request-pull.sh
@@ -0,0 +1,237 @@
+#!/bin/sh
+
+test_description='Test workflows involving pull request.'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+
+	git init --bare upstream.git &&
+	git init --bare downstream.git &&
+	git clone upstream.git upstream-private &&
+	git clone downstream.git local &&
+
+	trash_url="file://$TRASH_DIRECTORY" &&
+	downstream_url="$trash_url/downstream.git/" &&
+	upstream_url="$trash_url/upstream.git/" &&
+
+	(
+		cd upstream-private &&
+		cat <<-\EOT >mnemonic.txt &&
+		Thirtey days hath November,
+		Aprile, June, and September:
+		EOT
+		git add mnemonic.txt &&
+		test_tick &&
+		git commit -m "\"Thirty days\", a reminder of month lengths" &&
+		git tag -m "version 1" -a initial &&
+		git push --tags origin master
+	) &&
+	(
+		cd local &&
+		git remote add upstream "$trash_url/upstream.git" &&
+		git fetch upstream &&
+		git pull upstream master &&
+		cat <<-\EOT >>mnemonic.txt &&
+		Of twyecescore-eightt is but eine,
+		And all the remnante be thrycescore-eine.
+		O’course Leap yare comes an’pynes,
+		Ev’rie foure yares, gote it ryghth.
+		An’twyecescore-eight is but twyecescore-nyne.
+		EOT
+		git add mnemonic.txt &&
+		test_tick &&
+		git commit -m "More detail" &&
+		git tag -m "version 2" -a full &&
+		git checkout -b simplify HEAD^ &&
+		mv mnemonic.txt mnemonic.standard &&
+		cat <<-\EOT >mnemonic.clarified &&
+		Thirty days has September,
+		All the rest I can’t remember.
+		EOT
+		git add -N mnemonic.standard mnemonic.clarified &&
+		git commit -a -m "Adapt to use modern, simpler English
+
+But keep the old version, too, in case some people prefer it." &&
+		git checkout master
+	)
+
+'
+
+test_expect_success 'setup: two scripts for reading pull requests' '
+
+	downstream_url_for_sed=$(
+		printf "%s\n" "$downstream_url" |
+		sed -e '\''s/\\/\\\\/g'\'' -e '\''s/[[/.*^$]/\\&/g'\''
+	) &&
+
+	cat <<-\EOT >read-request.sed &&
+	#!/bin/sed -nf
+	# Note that a request could ask for "tag $tagname"
+	/ in the git repository at:$/!d
+	n
+	/^$/ n
+	s/ tag \([^ ]*\)$/ tag--\1/
+	s/^[ 	]*\(.*\) \([^ ]*\)/please pull\
+	\1\
+	\2/p
+	q
+	EOT
+
+	cat <<-EOT >fuzz.sed
+	#!/bin/sed -nf
+	s/$_x40/OBJECT_NAME/g
+	s/A U Thor/AUTHOR/g
+	s/[-0-9]\{10\} [:0-9]\{8\} [-+][0-9]\{4\}/DATE/g
+	s/        [^ ].*/        SUBJECT/g
+	s/  [^ ].* (DATE)/  SUBJECT (DATE)/g
+	s/$downstream_url_for_sed/URL/g
+	s/for-upstream/BRANCH/g
+	s/mnemonic.txt/FILENAME/g
+	s/^version [0-9]/VERSION/
+	/^ FILENAME | *[0-9]* [-+]*\$/ b diffstat
+	/^AUTHOR ([0-9]*):\$/ b shortlog
+	p
+	b
+	: diffstat
+	n
+	/ [0-9]* files* changed/ {
+		a\\
+	DIFFSTAT
+		b
+	}
+	b diffstat
+	: shortlog
+	/^        [a-zA-Z]/ n
+	/^[a-zA-Z]* ([0-9]*):\$/ n
+	/^\$/ N
+	/^\n[a-zA-Z]* ([0-9]*):\$/!{
+		a\\
+	SHORTLOG
+		D
+	}
+	n
+	b shortlog
+	EOT
+
+'
+
+test_expect_success 'pull request when forgot to push' '
+
+	rm -fr downstream.git &&
+	git init --bare downstream.git &&
+	(
+		cd local &&
+		git checkout initial &&
+		git merge --ff-only master &&
+		test_must_fail git request-pull initial "$downstream_url" \
+			2>../err
+	) &&
+	grep "No branch of.*is at:\$" err &&
+	grep "Are you sure you pushed" err
+
+'
+
+test_expect_success 'pull request after push' '
+
+	rm -fr downstream.git &&
+	git init --bare downstream.git &&
+	(
+		cd local &&
+		git checkout initial &&
+		git merge --ff-only master &&
+		git push origin master:for-upstream &&
+		git request-pull initial origin >../request
+	) &&
+	sed -nf read-request.sed <request >digest &&
+	cat digest &&
+	{
+		read task &&
+		read repository &&
+		read branch
+	} <digest &&
+	(
+		cd upstream-private &&
+		git checkout initial &&
+		git pull --ff-only "$repository" "$branch"
+	) &&
+	test "$branch" = for-upstream &&
+	test_cmp local/mnemonic.txt upstream-private/mnemonic.txt
+
+'
+
+test_expect_success 'request names an appropriate branch' '
+
+	rm -fr downstream.git &&
+	git init --bare downstream.git &&
+	(
+		cd local &&
+		git checkout initial &&
+		git merge --ff-only master &&
+		git push --tags origin master simplify &&
+		git push origin master:for-upstream &&
+		git request-pull initial "$downstream_url" >../request
+	) &&
+	sed -nf read-request.sed <request >digest &&
+	cat digest &&
+	{
+		read task &&
+		read repository &&
+		read branch
+	} <digest &&
+	test "$branch" = tags/full
+
+'
+
+test_expect_success 'pull request format' '
+
+	rm -fr downstream.git &&
+	git init --bare downstream.git &&
+	cat <<-\EOT >expect &&
+	The following changes since commit OBJECT_NAME:
+
+	  SUBJECT (DATE)
+
+	are available in the git repository at:
+
+	  URL BRANCH
+
+	for you to fetch changes up to OBJECT_NAME:
+
+	  SUBJECT (DATE)
+
+	----------------------------------------------------------------
+	VERSION
+
+	----------------------------------------------------------------
+	SHORTLOG
+
+	DIFFSTAT
+	EOT
+	(
+		cd local &&
+		git checkout initial &&
+		git merge --ff-only master &&
+		git push origin master:for-upstream &&
+		git request-pull initial "$downstream_url" >../request
+	) &&
+	<request sed -nf fuzz.sed >request.fuzzy &&
+	test_i18ncmp expect request.fuzzy
+
+'
+
+test_expect_success 'request-pull ignores OPTIONS_KEEPDASHDASH poison' '
+
+	(
+		cd local &&
+		OPTIONS_KEEPDASHDASH=Yes &&
+		export OPTIONS_KEEPDASHDASH &&
+		git checkout initial &&
+		git merge --ff-only master &&
+		git push origin master:for-upstream &&
+		git request-pull -- initial "$downstream_url" >../request
+	)
+
+'
+
+test_done
diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh
index e2aa254..2e52f8b 100755
--- a/t/t5300-pack-object.sh
+++ b/t/t5300-pack-object.sh
@@ -12,11 +12,13 @@
 
 test_expect_success \
     'setup' \
-    'rm -f .git/index*
-     perl -e "print \"a\" x 4096;" > a &&
-     perl -e "print \"b\" x 4096;" > b &&
-     perl -e "print \"c\" x 4096;" > c &&
-     git update-index --add a b c &&
+    'rm -f .git/index* &&
+     "$PERL_PATH" -e "print \"a\" x 4096;" > a &&
+     "$PERL_PATH" -e "print \"b\" x 4096;" > b &&
+     "$PERL_PATH" -e "print \"c\" x 4096;" > c &&
+     test-genrandom "seed a" 2097152 > a_big &&
+     test-genrandom "seed b" 2097152 > b_big &&
+     git update-index --add a a_big b b_big c &&
      cat c >d && echo foo >>d && git update-index --add d &&
      tree=`git write-tree` &&
      commit=`git commit-tree $tree </dev/null` && {
@@ -36,6 +38,10 @@
     'pack without delta' \
     'packname_1=$(git pack-objects --window=0 test-1 <obj-list)'
 
+test_expect_success \
+    'pack-objects with bogus arguments' \
+    'test_must_fail git pack-objects --window=0 test-1 blah blah <obj-list'
+
 rm -fr .git2
 mkdir .git2
 
@@ -123,7 +129,7 @@
 cd "$TRASH"
 
 test_expect_success 'compare delta flavors' '
-	perl -e '\''
+	"$PERL_PATH" -e '\''
 		defined($_ = -s $_) or die for @ARGV;
 		exit 1 if $ARGV[0] <= $ARGV[1];
 	'\'' test-2-$packname_2.pack test-3-$packname_3.pack
@@ -145,7 +151,7 @@
 	    git cat-file $t $object || return 1
 	 done <obj-list
     } >current &&
-    diff expect current'
+    test_cmp expect current'
 
 test_expect_success \
     'use packed deltified (REF_DELTA) objects' \
@@ -160,7 +166,7 @@
 	    git cat-file $t $object || return 1
 	 done <obj-list
     } >current &&
-    diff expect current'
+    test_cmp expect current'
 
 test_expect_success \
     'use packed deltified (OFS_DELTA) objects' \
@@ -175,7 +181,7 @@
 	    git cat-file $t $object || return 1
 	 done <obj-list
     } >current &&
-    diff expect current'
+    test_cmp expect current'
 
 unset GIT_OBJECT_DIRECTORY
 
@@ -280,26 +286,8 @@
 
      :'
 
-test_expect_success \
-    'fake a SHA1 hash collision' \
-    'test -f	.git/objects/c8/2de19312b6c3695c0c18f70709a6c535682a67 &&
-     cp -f	.git/objects/9d/235ed07cd19811a6ceb342de82f190e49c9f68 \
-		.git/objects/c8/2de19312b6c3695c0c18f70709a6c535682a67'
-
-test_expect_success \
-    'make sure index-pack detects the SHA1 collision' \
-    'test_must_fail git index-pack -o bad.idx test-3.pack 2>msg &&
-     grep "SHA1 COLLISION FOUND" msg'
-
-test_expect_success \
-    'honor pack.packSizeLimit' \
-    'git config pack.packSizeLimit 200 &&
-     packname_4=$(git pack-objects test-4 <obj-list) &&
-     test 3 = $(ls test-4-*.pack | wc -l)'
-
 test_expect_success 'unpacking with --strict' '
 
-	git config --unset pack.packsizelimit &&
 	for j in a b c d e f g
 	do
 		for i in 0 1 2 3 4 5 6 7 8 9
@@ -392,10 +380,47 @@
 	)
 '
 
-test_expect_success 'tolerate absurdly small packsizelimit' '
-	git config pack.packSizeLimit 2 &&
-	packname_9=$(git pack-objects test-9 <obj-list) &&
-	test $(wc -l <obj-list) = $(ls test-9-*.pack | wc -l)
+test_expect_success 'honor pack.packSizeLimit' '
+	git config pack.packSizeLimit 3m &&
+	packname_10=$(git pack-objects test-10 <obj-list) &&
+	test 2 = $(ls test-10-*.pack | wc -l)
 '
 
+test_expect_success 'verify resulting packs' '
+	git verify-pack test-10-*.pack
+'
+
+test_expect_success 'tolerate packsizelimit smaller than biggest object' '
+	git config pack.packSizeLimit 1 &&
+	packname_11=$(git pack-objects test-11 <obj-list) &&
+	test 5 = $(ls test-11-*.pack | wc -l)
+'
+
+test_expect_success 'verify resulting packs' '
+	git verify-pack test-11-*.pack
+'
+
+#
+# WARNING!
+#
+# The following test is destructive.  Please keep the next
+# two tests at the end of this file.
+#
+
+test_expect_success \
+    'fake a SHA1 hash collision' \
+    'test -f	.git/objects/c8/2de19312b6c3695c0c18f70709a6c535682a67 &&
+     cp -f	.git/objects/9d/235ed07cd19811a6ceb342de82f190e49c9f68 \
+		.git/objects/c8/2de19312b6c3695c0c18f70709a6c535682a67'
+
+test_expect_success \
+    'make sure index-pack detects the SHA1 collision' \
+    'test_must_fail git index-pack -o bad.idx test-3.pack 2>msg &&
+     grep "SHA1 COLLISION FOUND" msg'
+
+test_expect_success \
+    'make sure index-pack detects the SHA1 collision (large blobs)' \
+    'test_must_fail git -c core.bigfilethreshold=1 index-pack -o bad.idx test-3.pack 2>msg &&
+     grep "SHA1 COLLISION FOUND" msg'
+
 test_done
diff --git a/t/t5301-sliding-window.sh b/t/t5301-sliding-window.sh
index 0a24e61..2fc5af6 100755
--- a/t/t5301-sliding-window.sh
+++ b/t/t5301-sliding-window.sh
@@ -8,7 +8,7 @@
 
 test_expect_success \
     'setup' \
-    'rm -f .git/index*
+    'rm -f .git/index* &&
      for i in a b c
      do
          echo $i >$i &&
@@ -48,7 +48,7 @@
      git repack -a -d &&
      test "`git count-objects`" = "0 objects, 0 kilobytes" &&
      pack2=`ls .git/objects/pack/*.pack` &&
-     test -f "$pack2"
+     test -f "$pack2" &&
      test "$pack1" \!= "$pack2"'
 
 test_expect_success \
diff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh
index 4360e77..fe82025 100755
--- a/t/t5302-pack-index.sh
+++ b/t/t5302-pack-index.sh
@@ -8,7 +8,7 @@
 
 test_expect_success \
     'setup' \
-    'rm -rf .git
+    'rm -rf .git &&
      git init &&
      git config pack.threads 1 &&
      i=1 &&
@@ -65,6 +65,18 @@
     'cmp "test-1-${pack1}.idx" "1.idx" &&
      cmp "test-2-${pack2}.idx" "2.idx"'
 
+test_expect_success 'index-pack --verify on index version 1' '
+	git index-pack --verify "test-1-${pack1}.pack"
+'
+
+test_expect_success 'index-pack --verify on index version 2' '
+	git index-pack --verify "test-2-${pack2}.pack"
+'
+
+test_expect_success \
+    'pack-objects --index-version=2, is not accepted' \
+    'test_must_fail git pack-objects --index-version=2, test-3 <obj-list'
+
 test_expect_success \
     'index v2: force some 64-bit offsets with pack-objects' \
     'pack3=$(git pack-objects --index-version=2,0x40000 test-3 <obj-list)'
@@ -74,7 +86,7 @@
 then
 	test_set_prereq OFF64_T
 else
-	say "skipping tests concerning 64-bit offsets"
+	say "# skipping tests concerning 64-bit offsets"
 fi
 
 test_expect_success OFF64_T \
@@ -93,6 +105,16 @@
     '64-bit offsets: index-pack result should match pack-objects one' \
     'cmp "test-3-${pack3}.idx" "3.idx"'
 
+test_expect_success OFF64_T 'index-pack --verify on 64-bit offset v2 (cheat)' '
+	# This cheats by knowing which lower offset should still be encoded
+	# in 64-bit representation.
+	git index-pack --verify --index-version=2,0x40000 "test-3-${pack3}.pack"
+'
+
+test_expect_success OFF64_T 'index-pack --verify on 64-bit offset v2' '
+	git index-pack --verify "test-3-${pack3}.pack"
+'
+
 # returns the object number for given object in given pack index
 index_obj_nr()
 {
@@ -208,9 +230,8 @@
      ( while read obj
        do git cat-file -p $obj >/dev/null || exit 1
        done <obj-list ) &&
-     err=$(test_must_fail git verify-pack \
-       ".git/objects/pack/pack-${pack1}.pack" 2>&1) &&
-     echo "$err" | grep "CRC mismatch"'
+     test_must_fail git verify-pack ".git/objects/pack/pack-${pack1}.pack"
+'
 
 test_expect_success 'running index-pack in the object store' '
     rm -f .git/objects/pack/* &&
diff --git a/t/t5303-pack-corruption-resilience.sh b/t/t5303-pack-corruption-resilience.sh
index 5132d41..5b1250f 100755
--- a/t/t5303-pack-corruption-resilience.sh
+++ b/t/t5303-pack-corruption-resilience.sh
@@ -98,7 +98,7 @@
     'create_new_pack &&
      git prune-packed &&
      chmod +w ${pack}.pack &&
-     perl -i.bak -pe "s/ base /abcdef/" ${pack}.pack &&
+     "$PERL_PATH" -i.bak -pe "s/ base /abcdef/" ${pack}.pack &&
      test_must_fail git cat-file blob $blob_1 > /dev/null &&
      test_must_fail git cat-file blob $blob_2 > /dev/null &&
      test_must_fail git cat-file blob $blob_3 > /dev/null'
@@ -155,7 +155,7 @@
     'create_new_pack &&
      git prune-packed &&
      chmod +w ${pack}.pack &&
-     perl -i.bak -pe "s/ delta1 /abcdefgh/" ${pack}.pack &&
+     "$PERL_PATH" -i.bak -pe "s/ delta1 /abcdefgh/" ${pack}.pack &&
      git cat-file blob $blob_1 > /dev/null &&
      test_must_fail git cat-file blob $blob_2 > /dev/null &&
      test_must_fail git cat-file blob $blob_3 > /dev/null'
@@ -275,4 +275,13 @@
      git cat-file blob $blob_2 > /dev/null &&
      git cat-file blob $blob_3 > /dev/null'
 
+test_expect_success \
+    'corrupting header to have too small output buffer fails unpack' \
+    'create_new_pack &&
+     git prune-packed &&
+     printf "\262\001" | do_corrupt_object $blob_1 0 &&
+     test_must_fail git cat-file blob $blob_1 > /dev/null &&
+     test_must_fail git cat-file blob $blob_2 > /dev/null &&
+     test_must_fail git cat-file blob $blob_3 > /dev/null'
+
 test_done
diff --git a/t/t5304-prune.sh b/t/t5304-prune.sh
index 55ed7c7..d645328 100755
--- a/t/t5304-prune.sh
+++ b/t/t5304-prune.sh
@@ -6,6 +6,18 @@
 test_description='prune'
 . ./test-lib.sh
 
+day=$((60*60*24))
+week=$(($day*7))
+
+add_blob() {
+	before=$(git count-objects | sed "s/ .*//") &&
+	BLOB=$(echo aleph_0 | git hash-object -w --stdin) &&
+	BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") &&
+	test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+	test -f $BLOB_FILE &&
+	test-chmtime =+0 $BLOB_FILE
+}
+
 test_expect_success setup '
 
 	: > file &&
@@ -31,11 +43,7 @@
 
 test_expect_success 'prune --expire' '
 
-	before=$(git count-objects | sed "s/ .*//") &&
-	BLOB=$(echo aleph | git hash-object -w --stdin) &&
-	BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") &&
-	test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
-	test -f $BLOB_FILE &&
+	add_blob &&
 	git prune --expire=1.hour.ago &&
 	test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
 	test -f $BLOB_FILE &&
@@ -48,16 +56,12 @@
 
 test_expect_success 'gc: implicit prune --expire' '
 
-	before=$(git count-objects | sed "s/ .*//") &&
-	BLOB=$(echo aleph_0 | git hash-object -w --stdin) &&
-	BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") &&
-	test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
-	test -f $BLOB_FILE &&
-	test-chmtime =-$((86400*14-30)) $BLOB_FILE &&
+	add_blob &&
+	test-chmtime =-$((2*$week-30)) $BLOB_FILE &&
 	git gc &&
 	test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
 	test -f $BLOB_FILE &&
-	test-chmtime =-$((86400*14+1)) $BLOB_FILE &&
+	test-chmtime =-$((2*$week+1)) $BLOB_FILE &&
 	git gc &&
 	test $before = $(git count-objects | sed "s/ .*//") &&
 	! test -f $BLOB_FILE
@@ -114,12 +118,8 @@
 
 test_expect_success 'gc --no-prune' '
 
-	before=$(git count-objects | sed "s/ .*//") &&
-	BLOB=$(echo aleph_0 | git hash-object -w --stdin) &&
-	BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") &&
-	test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
-	test -f $BLOB_FILE &&
-	test-chmtime =-$((86400*5001)) $BLOB_FILE &&
+	add_blob &&
+	test-chmtime =-$((5001*$day)) $BLOB_FILE &&
 	git config gc.pruneExpire 2.days.ago &&
 	git gc --no-prune &&
 	test 1 = $(git count-objects | sed "s/ .*//") &&
@@ -140,9 +140,8 @@
 
 test_expect_success 'gc --prune=<date>' '
 
-	BLOB=$(echo aleph_0 | git hash-object -w --stdin) &&
-	BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") &&
-	test-chmtime =-$((86400*5001)) $BLOB_FILE &&
+	add_blob &&
+	test-chmtime =-$((5001*$day)) $BLOB_FILE &&
 	git gc --prune=5002.days.ago &&
 	test -f $BLOB_FILE &&
 	git gc --prune=5000.days.ago &&
@@ -150,4 +149,50 @@
 
 '
 
+test_expect_success 'gc --prune=never' '
+
+	add_blob &&
+	git gc --prune=never &&
+	test -f $BLOB_FILE &&
+	git gc --prune=now &&
+	test ! -f $BLOB_FILE
+
+'
+
+test_expect_success 'gc respects gc.pruneExpire=never' '
+
+	git config gc.pruneExpire never &&
+	add_blob &&
+	git gc &&
+	test -f $BLOB_FILE &&
+	git config gc.pruneExpire now &&
+	git gc &&
+	test ! -f $BLOB_FILE
+
+'
+
+test_expect_success 'prune --expire=never' '
+
+	add_blob &&
+	git prune --expire=never &&
+	test -f $BLOB_FILE &&
+	git prune &&
+	test ! -f $BLOB_FILE
+
+'
+
+test_expect_success 'gc: prune old objects after local clone' '
+	add_blob &&
+	test-chmtime =-$((2*$week+1)) $BLOB_FILE &&
+	git clone --no-hardlinks . aclone &&
+	(
+		cd aclone &&
+		test 1 = $(git count-objects | sed "s/ .*//") &&
+		test -f $BLOB_FILE &&
+		git gc --prune &&
+		test 0 = $(git count-objects | sed "s/ .*//") &&
+		! test -f $BLOB_FILE
+	)
+'
+
 test_done
diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh
index f2d5581..0eace37 100755
--- a/t/t5400-send-pack.sh
+++ b/t/t5400-send-pack.sh
@@ -32,7 +32,7 @@
 	done &&
 	git update-ref HEAD "$commit" &&
 	git clone ./. victim &&
-	( cd victim && git log ) &&
+	( cd victim && git config receive.denyCurrentBranch warn && git log ) &&
 	git update-ref HEAD "$zero" &&
 	parent=$zero &&
 	i=0 &&
@@ -94,6 +94,29 @@
 	test_must_fail git send-pack ./victim :extra master
 '
 
+test_expect_success 'cannot override denyDeletes with git -c send-pack' '
+	(
+		cd victim &&
+		test_might_fail git branch -D extra &&
+		git config receive.denyDeletes true &&
+		git branch extra master
+	) &&
+	test_must_fail git -c receive.denyDeletes=false \
+					send-pack ./victim :extra master
+'
+
+test_expect_success 'override denyDeletes with git -c receive-pack' '
+	(
+		cd victim &&
+		test_might_fail git branch -D extra &&
+		git config receive.denyDeletes true &&
+		git branch extra master
+	) &&
+	git send-pack \
+		--receive-pack="git -c receive.denyDeletes=false receive-pack" \
+		./victim :extra master
+'
+
 test_expect_success 'denyNonFastforwards trumps --force' '
 	(
 	    cd victim &&
@@ -106,7 +129,7 @@
 	test "$victim_orig" = "$victim_head"
 '
 
-test_expect_success 'push --all excludes remote tracking hierarchy' '
+test_expect_success 'push --all excludes remote-tracking hierarchy' '
 	mkdir parent &&
 	(
 	    cd parent &&
@@ -129,6 +152,7 @@
 	    cd parent &&
 	    git init &&
 	    echo one >file && git add file && git commit -m one &&
+	    git config receive.denyCurrentBranch warn &&
 	    echo two >file && git commit -a -m two
 	) &&
 	git clone parent child &&
@@ -166,7 +190,7 @@
 	        +refs/heads/master:refs/heads/master
 	) &&
 	parent_head=$(cd parent && git rev-parse --verify master) &&
-	child_head=$(cd parent && git rev-parse --verify master) &&
+	child_head=$(cd child && git rev-parse --verify master) &&
 	test "$parent_head" = "$child_head"
 '
 
@@ -186,20 +210,15 @@
 	        "+refs/heads/*:refs/heads/*"
 	) &&
 	parent_head=$(cd parent && git rev-parse --verify master) &&
-	child_head=$(cd parent && git rev-parse --verify master) &&
+	child_head=$(cd child && git rev-parse --verify master) &&
 	test "$parent_head" = "$child_head"
 '
 
-test_expect_success 'warn pushing to delete current branch' '
+test_expect_success 'deny pushing to delete current branch' '
 	rewound_push_setup &&
 	(
 	    cd child &&
-	    git send-pack ../parent :refs/heads/master 2>errs
-	) &&
-	grep "warning: to refuse deleting" child/errs &&
-	(
-		cd parent &&
-		test_must_fail git rev-parse --verify master
+	    test_must_fail git send-pack ../parent :refs/heads/master 2>errs
 	)
 '
 
diff --git a/t/t5401-update-hooks.sh b/t/t5401-update-hooks.sh
index 64f66c9..17bcb0b 100755
--- a/t/t5401-update-hooks.sh
+++ b/t/t5401-update-hooks.sh
@@ -17,22 +17,22 @@
 	commit1=$(echo modify | git commit-tree $tree1 -p $commit0) &&
 	git update-ref refs/heads/master $commit0 &&
 	git update-ref refs/heads/tofail $commit1 &&
-	git clone ./. victim &&
-	GIT_DIR=victim/.git git update-ref refs/heads/tofail $commit1 &&
+	git clone --bare ./. victim.git &&
+	GIT_DIR=victim.git git update-ref refs/heads/tofail $commit1 &&
 	git update-ref refs/heads/master $commit1 &&
 	git update-ref refs/heads/tofail $commit0
 '
 
-cat >victim/.git/hooks/pre-receive <<'EOF'
+cat >victim.git/hooks/pre-receive <<'EOF'
 #!/bin/sh
 printf %s "$@" >>$GIT_DIR/pre-receive.args
 cat - >$GIT_DIR/pre-receive.stdin
 echo STDOUT pre-receive
 echo STDERR pre-receive >&2
 EOF
-chmod u+x victim/.git/hooks/pre-receive
+chmod u+x victim.git/hooks/pre-receive
 
-cat >victim/.git/hooks/update <<'EOF'
+cat >victim.git/hooks/update <<'EOF'
 #!/bin/sh
 echo "$@" >>$GIT_DIR/update.args
 read x; printf %s "$x" >$GIT_DIR/update.stdin
@@ -40,77 +40,77 @@
 echo STDERR update $1 >&2
 test "$1" = refs/heads/master || exit
 EOF
-chmod u+x victim/.git/hooks/update
+chmod u+x victim.git/hooks/update
 
-cat >victim/.git/hooks/post-receive <<'EOF'
+cat >victim.git/hooks/post-receive <<'EOF'
 #!/bin/sh
 printf %s "$@" >>$GIT_DIR/post-receive.args
 cat - >$GIT_DIR/post-receive.stdin
 echo STDOUT post-receive
 echo STDERR post-receive >&2
 EOF
-chmod u+x victim/.git/hooks/post-receive
+chmod u+x victim.git/hooks/post-receive
 
-cat >victim/.git/hooks/post-update <<'EOF'
+cat >victim.git/hooks/post-update <<'EOF'
 #!/bin/sh
 echo "$@" >>$GIT_DIR/post-update.args
 read x; printf %s "$x" >$GIT_DIR/post-update.stdin
 echo STDOUT post-update
 echo STDERR post-update >&2
 EOF
-chmod u+x victim/.git/hooks/post-update
+chmod u+x victim.git/hooks/post-update
 
 test_expect_success push '
-	test_must_fail git send-pack --force ./victim/.git \
+	test_must_fail git send-pack --force ./victim.git \
 		master tofail >send.out 2>send.err
 '
 
 test_expect_success 'updated as expected' '
-	test $(GIT_DIR=victim/.git git rev-parse master) = $commit1 &&
-	test $(GIT_DIR=victim/.git git rev-parse tofail) = $commit1
+	test $(GIT_DIR=victim.git git rev-parse master) = $commit1 &&
+	test $(GIT_DIR=victim.git git rev-parse tofail) = $commit1
 '
 
 test_expect_success 'hooks ran' '
-	test -f victim/.git/pre-receive.args &&
-	test -f victim/.git/pre-receive.stdin &&
-	test -f victim/.git/update.args &&
-	test -f victim/.git/update.stdin &&
-	test -f victim/.git/post-receive.args &&
-	test -f victim/.git/post-receive.stdin &&
-	test -f victim/.git/post-update.args &&
-	test -f victim/.git/post-update.stdin
+	test -f victim.git/pre-receive.args &&
+	test -f victim.git/pre-receive.stdin &&
+	test -f victim.git/update.args &&
+	test -f victim.git/update.stdin &&
+	test -f victim.git/post-receive.args &&
+	test -f victim.git/post-receive.stdin &&
+	test -f victim.git/post-update.args &&
+	test -f victim.git/post-update.stdin
 '
 
 test_expect_success 'pre-receive hook input' '
 	(echo $commit0 $commit1 refs/heads/master;
 	 echo $commit1 $commit0 refs/heads/tofail
-	) | test_cmp - victim/.git/pre-receive.stdin
+	) | test_cmp - victim.git/pre-receive.stdin
 '
 
 test_expect_success 'update hook arguments' '
 	(echo refs/heads/master $commit0 $commit1;
 	 echo refs/heads/tofail $commit1 $commit0
-	) | test_cmp - victim/.git/update.args
+	) | test_cmp - victim.git/update.args
 '
 
 test_expect_success 'post-receive hook input' '
 	echo $commit0 $commit1 refs/heads/master |
-	test_cmp - victim/.git/post-receive.stdin
+	test_cmp - victim.git/post-receive.stdin
 '
 
 test_expect_success 'post-update hook arguments' '
 	echo refs/heads/master |
-	test_cmp - victim/.git/post-update.args
+	test_cmp - victim.git/post-update.args
 '
 
 test_expect_success 'all hook stdin is /dev/null' '
-	! test -s victim/.git/update.stdin &&
-	! test -s victim/.git/post-update.stdin
+	! test -s victim.git/update.stdin &&
+	! test -s victim.git/post-update.stdin
 '
 
 test_expect_success 'all *-receive hook args are empty' '
-	! test -s victim/.git/pre-receive.args &&
-	! test -s victim/.git/post-receive.args
+	! test -s victim.git/pre-receive.args &&
+	! test -s victim.git/post-receive.args
 '
 
 test_expect_success 'send-pack produced no output' '
@@ -118,20 +118,21 @@
 '
 
 cat <<EOF >expect
-STDOUT pre-receive
-STDERR pre-receive
-STDOUT update refs/heads/master
-STDERR update refs/heads/master
-STDOUT update refs/heads/tofail
-STDERR update refs/heads/tofail
-STDOUT post-receive
-STDERR post-receive
-STDOUT post-update
-STDERR post-update
+remote: STDOUT pre-receive
+remote: STDERR pre-receive
+remote: STDOUT update refs/heads/master
+remote: STDERR update refs/heads/master
+remote: STDOUT update refs/heads/tofail
+remote: STDERR update refs/heads/tofail
+remote: error: hook declined to update refs/heads/tofail
+remote: STDOUT post-receive
+remote: STDERR post-receive
+remote: STDOUT post-update
+remote: STDERR post-update
 EOF
 test_expect_success 'send-pack stderr contains hook messages' '
-	grep ^STD send.err >actual &&
-	test_cmp - actual <expect
+	grep ^remote: send.err | sed "s/ *\$//" >actual &&
+	test_cmp expect actual
 '
 
 test_done
diff --git a/t/t5403-post-checkout-hook.sh b/t/t5403-post-checkout-hook.sh
index 5858b86..1753ef2 100755
--- a/t/t5403-post-checkout-hook.sh
+++ b/t/t5403-post-checkout-hook.sh
@@ -7,19 +7,19 @@
 . ./test-lib.sh
 
 test_expect_success setup '
-	 echo Data for commit0. >a &&
-	 echo Data for commit0. >b &&
-	 git update-index --add a &&
-	 git update-index --add b &&
-	 tree0=$(git write-tree) &&
-	 commit0=$(echo setup | git commit-tree $tree0) &&
-        git update-ref refs/heads/master $commit0 &&
-	 git clone ./. clone1 &&
-	 git clone ./. clone2 &&
-        GIT_DIR=clone2/.git git branch -a new2 &&
-        echo Data for commit1. >clone2/b &&
-	 GIT_DIR=clone2/.git git add clone2/b &&
-	 GIT_DIR=clone2/.git git commit -m new2
+	echo Data for commit0. >a &&
+	echo Data for commit0. >b &&
+	git update-index --add a &&
+	git update-index --add b &&
+	tree0=$(git write-tree) &&
+	commit0=$(echo setup | git commit-tree $tree0) &&
+	git update-ref refs/heads/master $commit0 &&
+	git clone ./. clone1 &&
+	git clone ./. clone2 &&
+	GIT_DIR=clone2/.git git branch new2 &&
+	echo Data for commit1. >clone2/b &&
+	GIT_DIR=clone2/.git git add clone2/b &&
+	GIT_DIR=clone2/.git git commit -m new2
 '
 
 for clone in 1 2; do
@@ -31,44 +31,44 @@
 done
 
 test_expect_success 'post-checkout runs as expected ' '
-        GIT_DIR=clone1/.git git checkout master &&
-        test -e clone1/.git/post-checkout.args
+	GIT_DIR=clone1/.git git checkout master &&
+	test -e clone1/.git/post-checkout.args
 '
 
 test_expect_success 'post-checkout receives the right arguments with HEAD unchanged ' '
-        old=$(awk "{print \$1}" clone1/.git/post-checkout.args) &&
-        new=$(awk "{print \$2}" clone1/.git/post-checkout.args) &&
-        flag=$(awk "{print \$3}" clone1/.git/post-checkout.args) &&
-        test $old = $new -a $flag = 1
+	old=$(awk "{print \$1}" clone1/.git/post-checkout.args) &&
+	new=$(awk "{print \$2}" clone1/.git/post-checkout.args) &&
+	flag=$(awk "{print \$3}" clone1/.git/post-checkout.args) &&
+	test $old = $new -a $flag = 1
 '
 
 test_expect_success 'post-checkout runs as expected ' '
-        GIT_DIR=clone1/.git git checkout master &&
-        test -e clone1/.git/post-checkout.args
+	GIT_DIR=clone1/.git git checkout master &&
+	test -e clone1/.git/post-checkout.args
 '
 
 test_expect_success 'post-checkout args are correct with git checkout -b ' '
-        GIT_DIR=clone1/.git git checkout -b new1 &&
-        old=$(awk "{print \$1}" clone1/.git/post-checkout.args) &&
-        new=$(awk "{print \$2}" clone1/.git/post-checkout.args) &&
-        flag=$(awk "{print \$3}" clone1/.git/post-checkout.args) &&
-        test $old = $new -a $flag = 1
+	GIT_DIR=clone1/.git git checkout -b new1 &&
+	old=$(awk "{print \$1}" clone1/.git/post-checkout.args) &&
+	new=$(awk "{print \$2}" clone1/.git/post-checkout.args) &&
+	flag=$(awk "{print \$3}" clone1/.git/post-checkout.args) &&
+	test $old = $new -a $flag = 1
 '
 
 test_expect_success 'post-checkout receives the right args with HEAD changed ' '
-        GIT_DIR=clone2/.git git checkout new2 &&
-        old=$(awk "{print \$1}" clone2/.git/post-checkout.args) &&
-        new=$(awk "{print \$2}" clone2/.git/post-checkout.args) &&
-        flag=$(awk "{print \$3}" clone2/.git/post-checkout.args) &&
-        test $old != $new -a $flag = 1
+	GIT_DIR=clone2/.git git checkout new2 &&
+	old=$(awk "{print \$1}" clone2/.git/post-checkout.args) &&
+	new=$(awk "{print \$2}" clone2/.git/post-checkout.args) &&
+	flag=$(awk "{print \$3}" clone2/.git/post-checkout.args) &&
+	test $old != $new -a $flag = 1
 '
 
 test_expect_success 'post-checkout receives the right args when not switching branches ' '
-        GIT_DIR=clone2/.git git checkout master b &&
-        old=$(awk "{print \$1}" clone2/.git/post-checkout.args) &&
-        new=$(awk "{print \$2}" clone2/.git/post-checkout.args) &&
-        flag=$(awk "{print \$3}" clone2/.git/post-checkout.args) &&
-        test $old = $new -a $flag = 0
+	GIT_DIR=clone2/.git git checkout master b &&
+	old=$(awk "{print \$1}" clone2/.git/post-checkout.args) &&
+	new=$(awk "{print \$2}" clone2/.git/post-checkout.args) &&
+	flag=$(awk "{print \$3}" clone2/.git/post-checkout.args) &&
+	test $old = $new -a $flag = 0
 '
 
 if test "$(git config --bool core.filemode)" = true; then
diff --git a/t/t5405-send-pack-rewind.sh b/t/t5405-send-pack-rewind.sh
index cb9aacc..4bda18a 100755
--- a/t/t5405-send-pack-rewind.sh
+++ b/t/t5405-send-pack-rewind.sh
@@ -8,6 +8,7 @@
 
 	>file1 && git add file1 && test_tick &&
 	git commit -m Initial &&
+	git config receive.denyCurrentBranch warn &&
 
 	mkdir another && (
 		cd another &&
diff --git a/t/t5407-post-rewrite-hook.sh b/t/t5407-post-rewrite-hook.sh
new file mode 100755
index 0000000..baa670c
--- /dev/null
+++ b/t/t5407-post-rewrite-hook.sh
@@ -0,0 +1,215 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Thomas Rast
+#
+
+test_description='Test the post-rewrite hook.'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	test_commit A foo A &&
+	test_commit B foo B &&
+	test_commit C foo C &&
+	test_commit D foo D &&
+	git checkout A^0 &&
+	test_commit E bar E &&
+	test_commit F foo F &&
+	git checkout master
+'
+
+mkdir .git/hooks
+
+cat >.git/hooks/post-rewrite <<EOF
+#!/bin/sh
+echo \$@ > "$TRASH_DIRECTORY"/post-rewrite.args
+cat > "$TRASH_DIRECTORY"/post-rewrite.data
+EOF
+chmod u+x .git/hooks/post-rewrite
+
+clear_hook_input () {
+	rm -f post-rewrite.args post-rewrite.data
+}
+
+verify_hook_input () {
+	test_cmp "$TRASH_DIRECTORY"/post-rewrite.args expected.args &&
+	test_cmp "$TRASH_DIRECTORY"/post-rewrite.data expected.data
+}
+
+test_expect_success 'git commit --amend' '
+	clear_hook_input &&
+	echo "D new message" > newmsg &&
+	oldsha=$(git rev-parse HEAD^0) &&
+	git commit -Fnewmsg --amend &&
+	echo amend > expected.args &&
+	echo $oldsha $(git rev-parse HEAD^0) > expected.data &&
+	verify_hook_input
+'
+
+test_expect_success 'git commit --amend --no-post-rewrite' '
+	clear_hook_input &&
+	echo "D new message again" > newmsg &&
+	git commit --no-post-rewrite -Fnewmsg --amend &&
+	test ! -f post-rewrite.args &&
+	test ! -f post-rewrite.data
+'
+
+test_expect_success 'git rebase' '
+	git reset --hard D &&
+	clear_hook_input &&
+	test_must_fail git rebase --onto A B &&
+	echo C > foo &&
+	git add foo &&
+	git rebase --continue &&
+	echo rebase >expected.args &&
+	cat >expected.data <<EOF &&
+$(git rev-parse C) $(git rev-parse HEAD^)
+$(git rev-parse D) $(git rev-parse HEAD)
+EOF
+	verify_hook_input
+'
+
+test_expect_success 'git rebase --skip' '
+	git reset --hard D &&
+	clear_hook_input &&
+	test_must_fail git rebase --onto A B &&
+	test_must_fail git rebase --skip &&
+	echo D > foo &&
+	git add foo &&
+	git rebase --continue &&
+	echo rebase >expected.args &&
+	cat >expected.data <<EOF &&
+$(git rev-parse D) $(git rev-parse HEAD)
+EOF
+	verify_hook_input
+'
+
+test_expect_success 'git rebase --skip the last one' '
+	git reset --hard F &&
+	clear_hook_input &&
+	test_must_fail git rebase --onto D A &&
+	git rebase --skip &&
+	echo rebase >expected.args &&
+	cat >expected.data <<EOF &&
+$(git rev-parse E) $(git rev-parse HEAD)
+EOF
+	verify_hook_input
+'
+
+test_expect_success 'git rebase -m' '
+	git reset --hard D &&
+	clear_hook_input &&
+	test_must_fail git rebase -m --onto A B &&
+	echo C > foo &&
+	git add foo &&
+	git rebase --continue &&
+	echo rebase >expected.args &&
+	cat >expected.data <<EOF &&
+$(git rev-parse C) $(git rev-parse HEAD^)
+$(git rev-parse D) $(git rev-parse HEAD)
+EOF
+	verify_hook_input
+'
+
+test_expect_success 'git rebase -m --skip' '
+	git reset --hard D &&
+	clear_hook_input &&
+	test_must_fail git rebase --onto A B &&
+	test_must_fail git rebase --skip &&
+	echo D > foo &&
+	git add foo &&
+	git rebase --continue &&
+	echo rebase >expected.args &&
+	cat >expected.data <<EOF &&
+$(git rev-parse D) $(git rev-parse HEAD)
+EOF
+	verify_hook_input
+'
+
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+set_fake_editor
+
+# Helper to work around the lack of one-shot exporting for
+# test_must_fail (as it is a shell function)
+test_fail_interactive_rebase () {
+	(
+		FAKE_LINES="$1" &&
+		shift &&
+		export FAKE_LINES &&
+		test_must_fail git rebase -i "$@"
+	)
+}
+
+test_expect_success 'git rebase -i (unchanged)' '
+	git reset --hard D &&
+	clear_hook_input &&
+	test_fail_interactive_rebase "1 2" --onto A B &&
+	echo C > foo &&
+	git add foo &&
+	git rebase --continue &&
+	echo rebase >expected.args &&
+	cat >expected.data <<EOF &&
+$(git rev-parse C) $(git rev-parse HEAD^)
+$(git rev-parse D) $(git rev-parse HEAD)
+EOF
+	verify_hook_input
+'
+
+test_expect_success 'git rebase -i (skip)' '
+	git reset --hard D &&
+	clear_hook_input &&
+	test_fail_interactive_rebase "2" --onto A B &&
+	echo D > foo &&
+	git add foo &&
+	git rebase --continue &&
+	echo rebase >expected.args &&
+	cat >expected.data <<EOF &&
+$(git rev-parse D) $(git rev-parse HEAD)
+EOF
+	verify_hook_input
+'
+
+test_expect_success 'git rebase -i (squash)' '
+	git reset --hard D &&
+	clear_hook_input &&
+	test_fail_interactive_rebase "1 squash 2" --onto A B &&
+	echo C > foo &&
+	git add foo &&
+	git rebase --continue &&
+	echo rebase >expected.args &&
+	cat >expected.data <<EOF &&
+$(git rev-parse C) $(git rev-parse HEAD)
+$(git rev-parse D) $(git rev-parse HEAD)
+EOF
+	verify_hook_input
+'
+
+test_expect_success 'git rebase -i (fixup without conflict)' '
+	git reset --hard D &&
+	clear_hook_input &&
+	FAKE_LINES="1 fixup 2" git rebase -i B &&
+	echo rebase >expected.args &&
+	cat >expected.data <<EOF &&
+$(git rev-parse C) $(git rev-parse HEAD)
+$(git rev-parse D) $(git rev-parse HEAD)
+EOF
+	verify_hook_input
+'
+
+test_expect_success 'git rebase -i (double edit)' '
+	git reset --hard D &&
+	clear_hook_input &&
+	FAKE_LINES="edit 1 edit 2" git rebase -i B &&
+	git rebase --continue &&
+	echo something > foo &&
+	git add foo &&
+	git rebase --continue &&
+	echo rebase >expected.args &&
+	cat >expected.data <<EOF &&
+$(git rev-parse C) $(git rev-parse HEAD^)
+$(git rev-parse D) $(git rev-parse HEAD)
+EOF
+	verify_hook_input
+'
+
+test_done
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index a8c2ca2..e80a2af 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -91,13 +91,13 @@
 		prev=$cur &&
 		cur=$(($cur+1))
 	done &&
-	add B1 $A1
+	add B1 $A1 &&
 	echo $ATIP > .git/refs/heads/A &&
 	echo $BTIP > .git/refs/heads/B &&
 	git symbolic-ref HEAD refs/heads/B
 '
 
-pull_to_client 1st "B A" $((11*3))
+pull_to_client 1st "refs/heads/B refs/heads/A" $((11*3))
 
 test_expect_success 'post 1st pull setup' '
 	add A11 $A10 &&
@@ -110,12 +110,28 @@
 	done
 '
 
-pull_to_client 2nd "B" $((64*3))
+pull_to_client 2nd "refs/heads/B" $((64*3))
 
-pull_to_client 3rd "A" $((1*3))
+pull_to_client 3rd "refs/heads/A" $((1*3))
+
+test_expect_success 'single branch clone' '
+	git clone --single-branch "file://$(pwd)/." singlebranch
+'
+
+test_expect_success 'single branch object count' '
+	GIT_DIR=singlebranch/.git git count-objects -v |
+		grep "^in-pack:" > count.singlebranch &&
+	echo "in-pack: 198" >expected &&
+	test_cmp expected count.singlebranch
+'
+
+test_expect_success 'single given branch clone' '
+	git clone --single-branch --branch A "file://$(pwd)/." branch-a &&
+	test_must_fail git --git-dir=branch-a/.git rev-parse origin/B
+'
 
 test_expect_success 'clone shallow' '
-	git clone --depth 2 "file://$(pwd)/." shallow
+	git clone --no-single-branch --depth 2 "file://$(pwd)/." shallow
 '
 
 test_expect_success 'clone shallow object count' '
@@ -139,6 +155,36 @@
 	)
 '
 
+test_expect_success 'simple fetch in shallow repo' '
+	(
+		cd shallow &&
+		git fetch
+	)
+'
+
+test_expect_success 'no changes expected' '
+	(
+		cd shallow &&
+		git count-objects -v
+	) > count.shallow.2 &&
+	cmp count.shallow count.shallow.2
+'
+
+test_expect_success 'fetch same depth in shallow repo' '
+	(
+		cd shallow &&
+		git fetch --depth=2
+	)
+'
+
+test_expect_success 'no changes expected' '
+	(
+		cd shallow &&
+		git count-objects -v
+	) > count.shallow.3 &&
+	cmp count.shallow count.shallow.3
+'
+
 test_expect_success 'add two more' '
 	add B66 $B65 &&
 	add B67 $B66
@@ -201,4 +247,154 @@
 	)
 '
 
+test_expect_success 'additional simple shallow deepenings' '
+	(
+		cd shallow &&
+		git fetch --depth=8 &&
+		git fetch --depth=10 &&
+		git fetch --depth=11
+	)
+'
+
+test_expect_success 'clone shallow object count' '
+	(
+		cd shallow &&
+		git count-objects -v
+	) > count.shallow &&
+	grep "^count: 52" count.shallow
+'
+
+test_expect_success 'clone shallow without --no-single-branch' '
+	git clone --depth 1 "file://$(pwd)/." shallow2
+'
+
+test_expect_success 'clone shallow object count' '
+	(
+		cd shallow2 &&
+		git count-objects -v
+	) > count.shallow2 &&
+	grep "^in-pack: 6" count.shallow2
+'
+
+test_expect_success 'clone shallow with --branch' '
+	git clone --depth 1 --branch A "file://$(pwd)/." shallow3
+'
+
+test_expect_success 'clone shallow object count' '
+	echo "in-pack: 6" > count3.expected &&
+	GIT_DIR=shallow3/.git git count-objects -v |
+		grep "^in-pack" > count3.actual &&
+	test_cmp count3.expected count3.actual
+'
+
+test_expect_success 'clone shallow with detached HEAD' '
+	git checkout HEAD^ &&
+	git clone --depth 1 "file://$(pwd)/." shallow5 &&
+	git checkout - &&
+	GIT_DIR=shallow5/.git git rev-parse HEAD >actual &&
+	git rev-parse HEAD^ >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'shallow clone pulling tags' '
+	git tag -a -m A TAGA1 A &&
+	git tag -a -m B TAGB1 B &&
+	git tag TAGA2 A &&
+	git tag TAGB2 B &&
+	git clone --depth 1 "file://$(pwd)/." shallow6 &&
+
+	cat >taglist.expected <<\EOF &&
+TAGB1
+TAGB2
+EOF
+	GIT_DIR=shallow6/.git git tag -l >taglist.actual &&
+	test_cmp taglist.expected taglist.actual &&
+
+	echo "in-pack: 7" > count6.expected &&
+	GIT_DIR=shallow6/.git git count-objects -v |
+		grep "^in-pack" > count6.actual &&
+	test_cmp count6.expected count6.actual
+'
+
+test_expect_success 'shallow cloning single tag' '
+	git clone --depth 1 --branch=TAGB1 "file://$(pwd)/." shallow7 &&
+	cat >taglist.expected <<\EOF &&
+TAGB1
+TAGB2
+EOF
+	GIT_DIR=shallow7/.git git tag -l >taglist.actual &&
+	test_cmp taglist.expected taglist.actual &&
+
+	echo "in-pack: 7" > count7.expected &&
+	GIT_DIR=shallow7/.git git count-objects -v |
+		grep "^in-pack" > count7.actual &&
+	test_cmp count7.expected count7.actual
+'
+
+test_expect_success 'setup tests for the --stdin parameter' '
+	for head in C D E F
+	do
+		add $head
+	done &&
+	for head in A B C D E F
+	do
+		git tag $head $head
+	done &&
+	cat >input <<-\EOF
+	refs/heads/C
+	refs/heads/A
+	refs/heads/D
+	refs/tags/C
+	refs/heads/B
+	refs/tags/A
+	refs/heads/E
+	refs/tags/B
+	refs/tags/E
+	refs/tags/D
+	EOF
+	sort <input >expect &&
+	(
+		echo refs/heads/E &&
+		echo refs/tags/E &&
+		cat input
+	) >input.dup
+'
+
+test_expect_success 'fetch refs from cmdline' '
+	(
+		cd client &&
+		git fetch-pack --no-progress .. $(cat ../input)
+	) >output &&
+	cut -d " " -f 2 <output | sort >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'fetch refs from stdin' '
+	(
+		cd client &&
+		git fetch-pack --stdin --no-progress .. <../input
+	) >output &&
+	cut -d " " -f 2 <output | sort >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'fetch mixed refs from cmdline and stdin' '
+	(
+		cd client &&
+		tail -n +5 ../input |
+		git fetch-pack --stdin --no-progress .. $(head -n 4 ../input)
+	) >output &&
+	cut -d " " -f 2 <output | sort >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'test duplicate refs from stdin' '
+	(
+	cd client &&
+	test_must_fail git fetch-pack --stdin --no-progress .. <../input.dup
+	) >output &&
+	cut -d " " -f 2 <output | sort >actual &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t5501-fetch-push-alternates.sh b/t/t5501-fetch-push-alternates.sh
new file mode 100755
index 0000000..1bc57ac
--- /dev/null
+++ b/t/t5501-fetch-push-alternates.sh
@@ -0,0 +1,66 @@
+#!/bin/sh
+
+test_description='fetch/push involving alternates'
+. ./test-lib.sh
+
+count_objects () {
+	loose=0 inpack=0
+	eval "$(
+		git count-objects -v |
+		sed -n -e 's/^count: \(.*\)/loose=\1/p' \
+		    -e 's/^in-pack: \(.*\)/inpack=\1/p'
+	)" &&
+	echo $(( $loose + $inpack ))
+}
+
+
+test_expect_success setup '
+	(
+		git init original &&
+		cd original &&
+		i=0 &&
+		while test $i -le 100
+		do
+			echo "$i" >count &&
+			git add count &&
+			git commit -m "$i" || exit
+			i=$(($i + 1))
+		done
+	) &&
+	(
+		git clone --reference=original "file://$(pwd)/original" one &&
+		cd one &&
+		echo Z >count &&
+		git add count &&
+		git commit -m Z &&
+		count_objects >../one.count
+	) &&
+	A=$(pwd)/original/.git/objects &&
+	git init receiver &&
+	echo "$A" >receiver/.git/objects/info/alternates &&
+	git init fetcher &&
+	echo "$A" >fetcher/.git/objects/info/alternates
+'
+
+test_expect_success 'pushing into a repository with the same alternate' '
+	(
+		cd one &&
+		git push ../receiver master:refs/heads/it
+	) &&
+	(
+		cd receiver &&
+		count_objects >../receiver.count
+	) &&
+	test_cmp one.count receiver.count
+'
+
+test_expect_success 'fetching from a repository with the same alternate' '
+	(
+		cd fetcher &&
+		git fetch ../one master:refs/heads/it &&
+		count_objects >../fetcher.count
+	) &&
+	test_cmp one.count fetcher.count
+'
+
+test_done
diff --git a/t/t5502-quickfetch.sh b/t/t5502-quickfetch.sh
index 1037a72..7a46cbd 100755
--- a/t/t5502-quickfetch.sh
+++ b/t/t5502-quickfetch.sh
@@ -57,7 +57,7 @@
 		cd cloned &&
 		git count-objects | sed -e "s/ *objects,.*//"
 	) ) &&
-	test $cnt -eq 6
+	test $cnt -eq 6 &&
 
 	blob=$(git rev-parse HEAD:file | sed -e "s|..|&/|") &&
 	test -f "cloned/.git/objects/$blob" &&
diff --git a/t/t5503-tagfollow.sh b/t/t5503-tagfollow.sh
index d5db75d..60de2d6 100755
--- a/t/t5503-tagfollow.sh
+++ b/t/t5503-tagfollow.sh
@@ -4,11 +4,9 @@
 
 . ./test-lib.sh
 
-case $(uname -s) in
-*MINGW*)
+if ! test_have_prereq NOT_MINGW; then
 	say "GIT_DEBUG_SEND_PACK not supported - skipping tests"
-	test_done
-esac
+fi
 
 # End state of the repository:
 #
@@ -19,7 +17,7 @@
 #     \   C - origin/cat    \
 #      origin/master         master
 
-test_expect_success setup '
+test_expect_success NOT_MINGW setup '
 	test_tick &&
 	echo ichi >file &&
 	git add file &&
@@ -42,13 +40,16 @@
 
 U=UPLOAD_LOG
 
+test_expect_success NOT_MINGW 'setup expect' '
 cat - <<EOF >expect
 #S
 want $A
 #E
 EOF
-test_expect_success 'fetch A (new commit : 1 connection)' '
-	rm -f $U
+'
+
+test_expect_success NOT_MINGW 'fetch A (new commit : 1 connection)' '
+	rm -f $U &&
 	(
 		cd cloned &&
 		GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
@@ -59,7 +60,7 @@
 	test_cmp expect actual
 '
 
-test_expect_success "create tag T on A, create C on branch cat" '
+test_expect_success NOT_MINGW "create tag T on A, create C on branch cat" '
 	git tag -a -m tag1 tag1 $A &&
 	T=$(git rev-parse --verify tag1) &&
 
@@ -71,14 +72,17 @@
 	git checkout master
 '
 
+test_expect_success NOT_MINGW 'setup expect' '
 cat - <<EOF >expect
 #S
 want $C
 want $T
 #E
 EOF
-test_expect_success 'fetch C, T (new branch, tag : 1 connection)' '
-	rm -f $U
+'
+
+test_expect_success NOT_MINGW 'fetch C, T (new branch, tag : 1 connection)' '
+	rm -f $U &&
 	(
 		cd cloned &&
 		GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
@@ -91,7 +95,7 @@
 	test_cmp expect actual
 '
 
-test_expect_success "create commits O, B, tag S on B" '
+test_expect_success NOT_MINGW "create commits O, B, tag S on B" '
 	test_tick &&
 	echo O >file &&
 	git add file &&
@@ -107,14 +111,17 @@
 	S=$(git rev-parse --verify tag2)
 '
 
+test_expect_success NOT_MINGW 'setup expect' '
 cat - <<EOF >expect
 #S
 want $B
 want $S
 #E
 EOF
-test_expect_success 'fetch B, S (commit and tag : 1 connection)' '
-	rm -f $U
+'
+
+test_expect_success NOT_MINGW 'fetch B, S (commit and tag : 1 connection)' '
+	rm -f $U &&
 	(
 		cd cloned &&
 		GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
@@ -127,13 +134,16 @@
 	test_cmp expect actual
 '
 
+test_expect_success NOT_MINGW 'setup expect' '
 cat - <<EOF >expect
 #S
 want $B
 want $S
 #E
 EOF
-test_expect_success 'new clone fetch master and tags' '
+'
+
+test_expect_success NOT_MINGW 'new clone fetch master and tags' '
 	git branch -D cat
 	rm -f $U
 	(
diff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh
new file mode 100755
index 0000000..35ec294
--- /dev/null
+++ b/t/t5504-fetch-receive-strict.sh
@@ -0,0 +1,118 @@
+#!/bin/sh
+
+test_description='fetch/receive strict mode'
+. ./test-lib.sh
+
+test_expect_success setup '
+	echo hello >greetings &&
+	git add greetings &&
+	git commit -m greetings &&
+
+	S=$(git rev-parse :greetings | sed -e "s|^..|&/|") &&
+	X=$(echo bye | git hash-object -w --stdin | sed -e "s|^..|&/|") &&
+	mv -f .git/objects/$X .git/objects/$S &&
+
+	test_must_fail git fsck
+'
+
+test_expect_success 'fetch without strict' '
+	rm -rf dst &&
+	git init dst &&
+	(
+		cd dst &&
+		git config fetch.fsckobjects false &&
+		git config transfer.fsckobjects false &&
+		test_must_fail git fetch ../.git master
+	)
+'
+
+test_expect_success 'fetch with !fetch.fsckobjects' '
+	rm -rf dst &&
+	git init dst &&
+	(
+		cd dst &&
+		git config fetch.fsckobjects false &&
+		git config transfer.fsckobjects true &&
+		test_must_fail git fetch ../.git master
+	)
+'
+
+test_expect_success 'fetch with fetch.fsckobjects' '
+	rm -rf dst &&
+	git init dst &&
+	(
+		cd dst &&
+		git config fetch.fsckobjects true &&
+		git config transfer.fsckobjects false &&
+		test_must_fail git fetch ../.git master
+	)
+'
+
+test_expect_success 'fetch with transfer.fsckobjects' '
+	rm -rf dst &&
+	git init dst &&
+	(
+		cd dst &&
+		git config transfer.fsckobjects true &&
+		test_must_fail git fetch ../.git master
+	)
+'
+
+cat >exp <<EOF
+To dst
+!	refs/heads/master:refs/heads/test	[remote rejected] (missing necessary objects)
+EOF
+
+test_expect_success 'push without strict' '
+	rm -rf dst &&
+	git init dst &&
+	(
+		cd dst &&
+		git config fetch.fsckobjects false &&
+		git config transfer.fsckobjects false
+	) &&
+	test_must_fail git push --porcelain dst master:refs/heads/test >act &&
+	test_cmp exp act
+'
+
+test_expect_success 'push with !receive.fsckobjects' '
+	rm -rf dst &&
+	git init dst &&
+	(
+		cd dst &&
+		git config receive.fsckobjects false &&
+		git config transfer.fsckobjects true
+	) &&
+	test_must_fail git push --porcelain dst master:refs/heads/test >act &&
+	test_cmp exp act
+'
+
+cat >exp <<EOF
+To dst
+!	refs/heads/master:refs/heads/test	[remote rejected] (n/a (unpacker error))
+EOF
+
+test_expect_success 'push with receive.fsckobjects' '
+	rm -rf dst &&
+	git init dst &&
+	(
+		cd dst &&
+		git config receive.fsckobjects true &&
+		git config transfer.fsckobjects false
+	) &&
+	test_must_fail git push --porcelain dst master:refs/heads/test >act &&
+	test_cmp exp act
+'
+
+test_expect_success 'push with transfer.fsckobjects' '
+	rm -rf dst &&
+	git init dst &&
+	(
+		cd dst &&
+		git config transfer.fsckobjects true
+	) &&
+	test_must_fail git push --porcelain dst master:refs/heads/test >act &&
+	test_cmp exp act
+'
+
+test_done
diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
index 852ccb5..e8af615 100755
--- a/t/t5505-remote.sh
+++ b/t/t5505-remote.sh
@@ -107,20 +107,23 @@
 )
 '
 
-test_expect_success 'remove remote protects non-remote branches' '
+test_expect_success 'remove remote protects local branches' '
 (
 	cd test &&
-	(cat >expect1 <<EOF
-Note: A non-remote branch was not removed; to delete it, use:
+	{ cat >expect1 <<EOF
+Note: A branch outside the refs/remotes/ hierarchy was not removed;
+to delete it, use:
   git branch -d master
 EOF
-    cat >expect2 <<EOF
-Note: Non-remote branches were not removed; to delete them, use:
+	} &&
+	{ cat >expect2 <<EOF
+Note: Some branches outside the refs/remotes/ hierarchy were not removed;
+to delete them, use:
   git branch -d foobranch
   git branch -d master
 EOF
-) &&
-	git tag footag
+	} &&
+	git tag footag &&
 	git config --add remote.oops.fetch "+refs/*:refs/*" &&
 	git remote rm oops 2>actual1 &&
 	git branch foobranch &&
@@ -158,7 +161,7 @@
     another
     master
   Local refs configured for 'git push':
-    ahead  forces to master  (fast forwardable)
+    ahead  forces to master  (fast-forwardable)
     master pushes to another (up to date)
 EOF
 
@@ -301,6 +304,106 @@
 	 git rev-parse --verify refs/heads/side)
 '
 
+test_expect_success 'add --mirror=fetch' '
+	mkdir mirror-fetch &&
+	git init mirror-fetch/parent &&
+	(cd mirror-fetch/parent &&
+	 test_commit one) &&
+	git init --bare mirror-fetch/child &&
+	(cd mirror-fetch/child &&
+	 git remote add --mirror=fetch -f parent ../parent)
+'
+
+test_expect_success 'fetch mirrors act as mirrors during fetch' '
+	(cd mirror-fetch/parent &&
+	 git branch new &&
+	 git branch -m master renamed
+	) &&
+	(cd mirror-fetch/child &&
+	 git fetch parent &&
+	 git rev-parse --verify refs/heads/new &&
+	 git rev-parse --verify refs/heads/renamed
+	)
+'
+
+test_expect_success 'fetch mirrors can prune' '
+	(cd mirror-fetch/child &&
+	 git remote prune parent &&
+	 test_must_fail git rev-parse --verify refs/heads/master
+	)
+'
+
+test_expect_success 'fetch mirrors do not act as mirrors during push' '
+	(cd mirror-fetch/parent &&
+	 git checkout HEAD^0
+	) &&
+	(cd mirror-fetch/child &&
+	 git branch -m renamed renamed2 &&
+	 git push parent
+	) &&
+	(cd mirror-fetch/parent &&
+	 git rev-parse --verify renamed &&
+	 test_must_fail git rev-parse --verify refs/heads/renamed2
+	)
+'
+
+test_expect_success 'add fetch mirror with specific branches' '
+	git init --bare mirror-fetch/track &&
+	(cd mirror-fetch/track &&
+	 git remote add --mirror=fetch -t heads/new parent ../parent
+	)
+'
+
+test_expect_success 'fetch mirror respects specific branches' '
+	(cd mirror-fetch/track &&
+	 git fetch parent &&
+	 git rev-parse --verify refs/heads/new &&
+	 test_must_fail git rev-parse --verify refs/heads/renamed
+	)
+'
+
+test_expect_success 'add --mirror=push' '
+	mkdir mirror-push &&
+	git init --bare mirror-push/public &&
+	git init mirror-push/private &&
+	(cd mirror-push/private &&
+	 test_commit one &&
+	 git remote add --mirror=push public ../public
+	)
+'
+
+test_expect_success 'push mirrors act as mirrors during push' '
+	(cd mirror-push/private &&
+	 git branch new &&
+	 git branch -m master renamed &&
+	 git push public
+	) &&
+	(cd mirror-push/private &&
+	 git rev-parse --verify refs/heads/new &&
+	 git rev-parse --verify refs/heads/renamed &&
+	 test_must_fail git rev-parse --verify refs/heads/master
+	)
+'
+
+test_expect_success 'push mirrors do not act as mirrors during fetch' '
+	(cd mirror-push/public &&
+	 git branch -m renamed renamed2 &&
+	 git symbolic-ref HEAD refs/heads/renamed2
+	) &&
+	(cd mirror-push/private &&
+	 git fetch public &&
+	 git rev-parse --verify refs/heads/renamed &&
+	 test_must_fail git rev-parse --verify refs/heads/renamed2
+	)
+'
+
+test_expect_success 'push mirrors do not allow you to specify refs' '
+	git init mirror-push/track &&
+	(cd mirror-push/track &&
+	 test_must_fail git remote add --mirror=push -t new public ../public
+	)
+'
+
 test_expect_success 'add alt && prune' '
 	(mkdir alttst &&
 	 cd alttst &&
@@ -319,6 +422,69 @@
 	 git rev-parse --verify refs/remotes/origin/side2)
 '
 
+cat >test/expect <<\EOF
+some-tag
+EOF
+
+test_expect_success 'add with reachable tags (default)' '
+	(cd one &&
+	 >foobar &&
+	 git add foobar &&
+	 git commit -m "Foobar" &&
+	 git tag -a -m "Foobar tag" foobar-tag &&
+	 git reset --hard HEAD~1 &&
+	 git tag -a -m "Some tag" some-tag) &&
+	(mkdir add-tags &&
+	 cd add-tags &&
+	 git init &&
+	 git remote add -f origin ../one &&
+	 git tag -l some-tag >../test/output &&
+	 git tag -l foobar-tag >>../test/output &&
+	 test_must_fail git config remote.origin.tagopt) &&
+	test_cmp test/expect test/output
+'
+
+cat >test/expect <<\EOF
+some-tag
+foobar-tag
+--tags
+EOF
+
+test_expect_success 'add --tags' '
+	(rm -rf add-tags &&
+	 mkdir add-tags &&
+	 cd add-tags &&
+	 git init &&
+	 git remote add -f --tags origin ../one &&
+	 git tag -l some-tag >../test/output &&
+	 git tag -l foobar-tag >>../test/output &&
+	 git config remote.origin.tagopt >>../test/output) &&
+	test_cmp test/expect test/output
+'
+
+cat >test/expect <<\EOF
+--no-tags
+EOF
+
+test_expect_success 'add --no-tags' '
+	(rm -rf add-tags &&
+	 mkdir add-no-tags &&
+	 cd add-no-tags &&
+	 git init &&
+	 git remote add -f --no-tags origin ../one &&
+	 git tag -l some-tag >../test/output &&
+	 git tag -l foobar-tag >../test/output &&
+	 git config remote.origin.tagopt >>../test/output) &&
+	(cd one &&
+	 git tag -d some-tag foobar-tag) &&
+	test_cmp test/expect test/output
+'
+
+test_expect_success 'reject --no-no-tags' '
+	(cd add-no-tags &&
+	 test_must_fail git remote add -f --no-no-tags neworigin ../one)
+'
+
 cat > one/expect << EOF
   apis/master
   apis/side
@@ -365,6 +531,17 @@
 
 '
 
+test_expect_success 'update --prune' '
+
+	(cd one &&
+	 git branch -m side2 side3) &&
+	(cd test &&
+	 git remote update --prune &&
+	 (cd ../one && git branch -m side3 side2) &&
+	 git rev-parse refs/remotes/origin/side3 &&
+	 test_must_fail git rev-parse refs/remotes/origin/side2)
+'
+
 cat > one/expect << EOF
   apis/master
   apis/side
@@ -408,6 +585,20 @@
 
 '
 
+test_expect_success 'update (with remotes.default defined)' '
+
+	(cd one &&
+	 for b in $(git branch -r)
+	 do
+		git branch -r -d $b || break
+	 done &&
+	 git config remotes.default "drosophila" &&
+	 git remote update &&
+	 git branch -r > output &&
+	 test_cmp expect output)
+
+'
+
 test_expect_success '"remote show" does not show symbolic refs' '
 
 	git clone one three &&
@@ -440,6 +631,37 @@
 
 '
 
+test_expect_success 'rename does not update a non-default fetch refspec' '
+
+	git clone one four.one &&
+	(cd four.one &&
+	 git config remote.origin.fetch +refs/heads/*:refs/heads/origin/* &&
+	 git remote rename origin upstream &&
+	 test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/heads/origin/*" &&
+	 git rev-parse -q origin/master)
+
+'
+
+test_expect_success 'rename a remote with name part of fetch spec' '
+
+	git clone one four.two &&
+	(cd four.two &&
+	 git remote rename origin remote &&
+	 git remote rename remote upstream &&
+	 test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/remotes/upstream/*")
+
+'
+
+test_expect_success 'rename a remote with name prefix of other remote' '
+
+	git clone one four.three &&
+	(cd four.three &&
+	 git remote add o git://example.com/repo.git &&
+	 git remote rename o upstream &&
+	 test "$(git rev-parse origin/master)" = "$(git rev-parse master)")
+
+'
+
 cat > remotes_origin << EOF
 URL: $(pwd)/one
 Push: refs/heads/master:refs/heads/upstream
@@ -482,15 +704,15 @@
 	(
 		cd seven &&
 		git remote prune origin
-	) 2>err &&
+	) >err 2>&1 &&
 	grep "has become dangling" err &&
 
-	: And the dangling symref will not cause other annoying errors
+	: And the dangling symref will not cause other annoying errors &&
 	(
 		cd seven &&
 		git branch -a
 	) 2>err &&
-	! grep "points nowhere" err
+	! grep "points nowhere" err &&
 	(
 		cd seven &&
 		test_must_fail git branch nomore origin
@@ -508,5 +730,271 @@
 	)
 '
 
-test_done
+test_expect_success 'remote set-branches requires a remote' '
+	test_must_fail git remote set-branches &&
+	test_must_fail git remote set-branches --add
+'
 
+test_expect_success 'remote set-branches' '
+	echo "+refs/heads/*:refs/remotes/scratch/*" >expect.initial &&
+	sort <<-\EOF >expect.add &&
+	+refs/heads/*:refs/remotes/scratch/*
+	+refs/heads/other:refs/remotes/scratch/other
+	EOF
+	sort <<-\EOF >expect.replace &&
+	+refs/heads/maint:refs/remotes/scratch/maint
+	+refs/heads/master:refs/remotes/scratch/master
+	+refs/heads/next:refs/remotes/scratch/next
+	EOF
+	sort <<-\EOF >expect.add-two &&
+	+refs/heads/maint:refs/remotes/scratch/maint
+	+refs/heads/master:refs/remotes/scratch/master
+	+refs/heads/next:refs/remotes/scratch/next
+	+refs/heads/pu:refs/remotes/scratch/pu
+	+refs/heads/t/topic:refs/remotes/scratch/t/topic
+	EOF
+	sort <<-\EOF >expect.setup-ffonly &&
+	refs/heads/master:refs/remotes/scratch/master
+	+refs/heads/next:refs/remotes/scratch/next
+	EOF
+	sort <<-\EOF >expect.respect-ffonly &&
+	refs/heads/master:refs/remotes/scratch/master
+	+refs/heads/next:refs/remotes/scratch/next
+	+refs/heads/pu:refs/remotes/scratch/pu
+	EOF
+
+	git clone .git/ setbranches &&
+	(
+		cd setbranches &&
+		git remote rename origin scratch &&
+		git config --get-all remote.scratch.fetch >config-result &&
+		sort <config-result >../actual.initial &&
+
+		git remote set-branches scratch --add other &&
+		git config --get-all remote.scratch.fetch >config-result &&
+		sort <config-result >../actual.add &&
+
+		git remote set-branches scratch maint master next &&
+		git config --get-all remote.scratch.fetch >config-result &&
+		sort <config-result >../actual.replace &&
+
+		git remote set-branches --add scratch pu t/topic &&
+		git config --get-all remote.scratch.fetch >config-result &&
+		sort <config-result >../actual.add-two &&
+
+		git config --unset-all remote.scratch.fetch &&
+		git config remote.scratch.fetch \
+			refs/heads/master:refs/remotes/scratch/master &&
+		git config --add remote.scratch.fetch \
+			+refs/heads/next:refs/remotes/scratch/next &&
+		git config --get-all remote.scratch.fetch >config-result &&
+		sort <config-result >../actual.setup-ffonly &&
+
+		git remote set-branches --add scratch pu &&
+		git config --get-all remote.scratch.fetch >config-result &&
+		sort <config-result >../actual.respect-ffonly
+	) &&
+	test_cmp expect.initial actual.initial &&
+	test_cmp expect.add actual.add &&
+	test_cmp expect.replace actual.replace &&
+	test_cmp expect.add-two actual.add-two &&
+	test_cmp expect.setup-ffonly actual.setup-ffonly &&
+	test_cmp expect.respect-ffonly actual.respect-ffonly
+'
+
+test_expect_success 'remote set-branches with --mirror' '
+	echo "+refs/*:refs/*" >expect.initial &&
+	echo "+refs/heads/master:refs/heads/master" >expect.replace &&
+	git clone --mirror .git/ setbranches-mirror &&
+	(
+		cd setbranches-mirror &&
+		git remote rename origin scratch &&
+		git config --get-all remote.scratch.fetch >../actual.initial &&
+
+		git remote set-branches scratch heads/master &&
+		git config --get-all remote.scratch.fetch >../actual.replace
+	) &&
+	test_cmp expect.initial actual.initial &&
+	test_cmp expect.replace actual.replace
+'
+
+test_expect_success 'new remote' '
+	git remote add someremote foo &&
+	echo foo >expect &&
+	git config --get-all remote.someremote.url >actual &&
+	cmp expect actual
+'
+
+test_expect_success 'remote set-url bar' '
+	git remote set-url someremote bar &&
+	echo bar >expect &&
+	git config --get-all remote.someremote.url >actual &&
+	cmp expect actual
+'
+
+test_expect_success 'remote set-url baz bar' '
+	git remote set-url someremote baz bar &&
+	echo baz >expect &&
+	git config --get-all remote.someremote.url >actual &&
+	cmp expect actual
+'
+
+test_expect_success 'remote set-url zot bar' '
+	test_must_fail git remote set-url someremote zot bar &&
+	echo baz >expect &&
+	git config --get-all remote.someremote.url >actual &&
+	cmp expect actual
+'
+
+test_expect_success 'remote set-url --push zot baz' '
+	test_must_fail git remote set-url --push someremote zot baz &&
+	echo "YYY" >expect &&
+	echo baz >>expect &&
+	test_must_fail git config --get-all remote.someremote.pushurl >actual &&
+	echo "YYY" >>actual &&
+	git config --get-all remote.someremote.url >>actual &&
+	cmp expect actual
+'
+
+test_expect_success 'remote set-url --push zot' '
+	git remote set-url --push someremote zot &&
+	echo zot >expect &&
+	echo "YYY" >>expect &&
+	echo baz >>expect &&
+	git config --get-all remote.someremote.pushurl >actual &&
+	echo "YYY" >>actual &&
+	git config --get-all remote.someremote.url >>actual &&
+	cmp expect actual
+'
+
+test_expect_success 'remote set-url --push qux zot' '
+	git remote set-url --push someremote qux zot &&
+	echo qux >expect &&
+	echo "YYY" >>expect &&
+	echo baz >>expect &&
+	git config --get-all remote.someremote.pushurl >actual &&
+	echo "YYY" >>actual &&
+	git config --get-all remote.someremote.url >>actual &&
+	cmp expect actual
+'
+
+test_expect_success 'remote set-url --push foo qu+x' '
+	git remote set-url --push someremote foo qu+x &&
+	echo foo >expect &&
+	echo "YYY" >>expect &&
+	echo baz >>expect &&
+	git config --get-all remote.someremote.pushurl >actual &&
+	echo "YYY" >>actual &&
+	git config --get-all remote.someremote.url >>actual &&
+	cmp expect actual
+'
+
+test_expect_success 'remote set-url --push --add aaa' '
+	git remote set-url --push --add someremote aaa &&
+	echo foo >expect &&
+	echo aaa >>expect &&
+	echo "YYY" >>expect &&
+	echo baz >>expect &&
+	git config --get-all remote.someremote.pushurl >actual &&
+	echo "YYY" >>actual &&
+	git config --get-all remote.someremote.url >>actual &&
+	cmp expect actual
+'
+
+test_expect_success 'remote set-url --push bar aaa' '
+	git remote set-url --push someremote bar aaa &&
+	echo foo >expect &&
+	echo bar >>expect &&
+	echo "YYY" >>expect &&
+	echo baz >>expect &&
+	git config --get-all remote.someremote.pushurl >actual &&
+	echo "YYY" >>actual &&
+	git config --get-all remote.someremote.url >>actual &&
+	cmp expect actual
+'
+
+test_expect_success 'remote set-url --push --delete bar' '
+	git remote set-url --push --delete someremote bar &&
+	echo foo >expect &&
+	echo "YYY" >>expect &&
+	echo baz >>expect &&
+	git config --get-all remote.someremote.pushurl >actual &&
+	echo "YYY" >>actual &&
+	git config --get-all remote.someremote.url >>actual &&
+	cmp expect actual
+'
+
+test_expect_success 'remote set-url --push --delete foo' '
+	git remote set-url --push --delete someremote foo &&
+	echo "YYY" >expect &&
+	echo baz >>expect &&
+	test_must_fail git config --get-all remote.someremote.pushurl >actual &&
+	echo "YYY" >>actual &&
+	git config --get-all remote.someremote.url >>actual &&
+	cmp expect actual
+'
+
+test_expect_success 'remote set-url --add bbb' '
+	git remote set-url --add someremote bbb &&
+	echo "YYY" >expect &&
+	echo baz >>expect &&
+	echo bbb >>expect &&
+	test_must_fail git config --get-all remote.someremote.pushurl >actual &&
+	echo "YYY" >>actual &&
+	git config --get-all remote.someremote.url >>actual &&
+	cmp expect actual
+'
+
+test_expect_success 'remote set-url --delete .*' '
+	test_must_fail git remote set-url --delete someremote .\* &&
+	echo "YYY" >expect &&
+	echo baz >>expect &&
+	echo bbb >>expect &&
+	test_must_fail git config --get-all remote.someremote.pushurl >actual &&
+	echo "YYY" >>actual &&
+	git config --get-all remote.someremote.url >>actual &&
+	cmp expect actual
+'
+
+test_expect_success 'remote set-url --delete bbb' '
+	git remote set-url --delete someremote bbb &&
+	echo "YYY" >expect &&
+	echo baz >>expect &&
+	test_must_fail git config --get-all remote.someremote.pushurl >actual &&
+	echo "YYY" >>actual &&
+	git config --get-all remote.someremote.url >>actual &&
+	cmp expect actual
+'
+
+test_expect_success 'remote set-url --delete baz' '
+	test_must_fail git remote set-url --delete someremote baz &&
+	echo "YYY" >expect &&
+	echo baz >>expect &&
+	test_must_fail git config --get-all remote.someremote.pushurl >actual &&
+	echo "YYY" >>actual &&
+	git config --get-all remote.someremote.url >>actual &&
+	cmp expect actual
+'
+
+test_expect_success 'remote set-url --add ccc' '
+	git remote set-url --add someremote ccc &&
+	echo "YYY" >expect &&
+	echo baz >>expect &&
+	echo ccc >>expect &&
+	test_must_fail git config --get-all remote.someremote.pushurl >actual &&
+	echo "YYY" >>actual &&
+	git config --get-all remote.someremote.url >>actual &&
+	cmp expect actual
+'
+
+test_expect_success 'remote set-url --delete baz' '
+	git remote set-url --delete someremote baz &&
+	echo "YYY" >expect &&
+	echo ccc >>expect &&
+	test_must_fail git config --get-all remote.someremote.pushurl >actual &&
+	echo "YYY" >>actual &&
+	git config --get-all remote.someremote.url >>actual &&
+	cmp expect actual
+'
+
+test_done
diff --git a/t/t5506-remote-groups.sh b/t/t5506-remote-groups.sh
index 2a1806b..530b016 100755
--- a/t/t5506-remote-groups.sh
+++ b/t/t5506-remote-groups.sh
@@ -43,15 +43,15 @@
 	repo_fetched two
 '
 
-test_expect_success 'nonexistant group produces error' '
-	mark nonexistant &&
+test_expect_success 'nonexistent group produces error' '
+	mark nonexistent &&
 	update_repos &&
-	test_must_fail git remote update nonexistant &&
+	test_must_fail git remote update nonexistent &&
 	! repo_fetched one &&
 	! repo_fetched two
 '
 
-test_expect_success 'updating group updates all members' '
+test_expect_success 'updating group updates all members (remote update)' '
 	mark group-all &&
 	update_repos &&
 	git config --add remotes.all one &&
@@ -61,7 +61,15 @@
 	repo_fetched two
 '
 
-test_expect_success 'updating group does not update non-members' '
+test_expect_success 'updating group updates all members (fetch)' '
+	mark fetch-group-all &&
+	update_repos &&
+	git fetch all &&
+	repo_fetched one &&
+	repo_fetched two
+'
+
+test_expect_success 'updating group does not update non-members (remote update)' '
 	mark group-some &&
 	update_repos &&
 	git config --add remotes.some one &&
@@ -70,6 +78,15 @@
 	! repo_fetched two
 '
 
+test_expect_success 'updating group does not update non-members (fetch)' '
+	mark fetch-group-some &&
+	update_repos &&
+	git config --add remotes.some one &&
+	git remote update some &&
+	repo_fetched one &&
+	! repo_fetched two
+'
+
 test_expect_success 'updating remote name updates that remote' '
 	mark remote-name &&
 	update_repos &&
diff --git a/t/t5509-fetch-push-namespaces.sh b/t/t5509-fetch-push-namespaces.sh
new file mode 100755
index 0000000..cc0b31f
--- /dev/null
+++ b/t/t5509-fetch-push-namespaces.sh
@@ -0,0 +1,85 @@
+#!/bin/sh
+
+test_description='fetch/push involving ref namespaces'
+. ./test-lib.sh
+
+test_expect_success setup '
+	test_tick &&
+	git init original &&
+	(
+		cd original &&
+		echo 0 >count &&
+		git add count &&
+		test_commit 0 &&
+		echo 1 >count &&
+		git add count &&
+		test_commit 1 &&
+		git remote add pushee-namespaced "ext::git --namespace=namespace %s ../pushee" &&
+		git remote add pushee-unnamespaced ../pushee
+	) &&
+	commit0=$(cd original && git rev-parse HEAD^) &&
+	commit1=$(cd original && git rev-parse HEAD) &&
+	git init pushee &&
+	git init puller
+'
+
+test_expect_success 'pushing into a repository using a ref namespace' '
+	(
+		cd original &&
+		git push pushee-namespaced master &&
+		git ls-remote pushee-namespaced >actual &&
+		printf "$commit1\trefs/heads/master\n" >expected &&
+		test_cmp expected actual &&
+		git push pushee-namespaced --tags &&
+		git ls-remote pushee-namespaced >actual &&
+		printf "$commit0\trefs/tags/0\n" >>expected &&
+		printf "$commit1\trefs/tags/1\n" >>expected &&
+		test_cmp expected actual &&
+		# Verify that the GIT_NAMESPACE environment variable works as well
+		GIT_NAMESPACE=namespace git ls-remote "ext::git %s ../pushee" >actual &&
+		test_cmp expected actual &&
+		# Verify that --namespace overrides GIT_NAMESPACE
+		GIT_NAMESPACE=garbage git ls-remote pushee-namespaced >actual &&
+		test_cmp expected actual &&
+		# Try a namespace with no content
+		git ls-remote "ext::git --namespace=garbage %s ../pushee" >actual &&
+		test_cmp /dev/null actual &&
+		git ls-remote pushee-unnamespaced >actual &&
+		sed -e "s|refs/|refs/namespaces/namespace/refs/|" expected >expected.unnamespaced &&
+		test_cmp expected.unnamespaced actual
+	)
+'
+
+test_expect_success 'pulling from a repository using a ref namespace' '
+	(
+		cd puller &&
+		git remote add -f pushee-namespaced "ext::git --namespace=namespace %s ../pushee" &&
+		git for-each-ref refs/ >actual &&
+		printf "$commit1 commit\trefs/remotes/pushee-namespaced/master\n" >expected &&
+		printf "$commit0 commit\trefs/tags/0\n" >>expected &&
+		printf "$commit1 commit\trefs/tags/1\n" >>expected &&
+		test_cmp expected actual
+	)
+'
+
+# This test with clone --mirror checks for possible regressions in clone
+# or the machinery underneath it. It ensures that no future change
+# causes clone to ignore refs in refs/namespaces/*. In particular, it
+# protects against a regression caused by any future change to the refs
+# machinery that might cause it to ignore refs outside of refs/heads/*
+# or refs/tags/*. More generally, this test also checks the high-level
+# functionality of using clone --mirror to back up a set of repos hosted
+# in the namespaces of a single repo.
+test_expect_success 'mirroring a repository using a ref namespace' '
+	git clone --mirror pushee mirror &&
+	(
+		cd mirror &&
+		git for-each-ref refs/ >actual &&
+		printf "$commit1 commit\trefs/namespaces/namespace/refs/heads/master\n" >expected &&
+		printf "$commit0 commit\trefs/namespaces/namespace/refs/tags/0\n" >>expected &&
+		printf "$commit1 commit\trefs/namespaces/namespace/refs/tags/1\n" >>expected &&
+		test_cmp expected actual
+	)
+'
+
+test_done
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index bee3424..d7a19a1 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -9,6 +9,19 @@
 
 D=`pwd`
 
+test_bundle_object_count () {
+	git verify-pack -v "$1" >verify.out &&
+	test "$2" = $(grep '^[0-9a-f]\{40\} ' verify.out | wc -l)
+}
+
+convert_bundle_to_pack () {
+	while read x && test -n "$x"
+	do
+		:;
+	done
+	cat
+}
+
 test_expect_success setup '
 	echo >file original &&
 	git add file &&
@@ -16,27 +29,30 @@
 
 test_expect_success "clone and setup child repos" '
 	git clone . one &&
-	cd one &&
-	echo >file updated by one &&
-	git commit -a -m "updated by one" &&
-	cd .. &&
+	(
+		cd one &&
+		echo >file updated by one &&
+		git commit -a -m "updated by one"
+	) &&
 	git clone . two &&
-	cd two &&
-	git config branch.master.remote one &&
-	git config remote.one.url ../one/.git/ &&
-	git config remote.one.fetch refs/heads/master:refs/heads/one &&
-	cd .. &&
+	(
+		cd two &&
+		git config branch.master.remote one &&
+		git config remote.one.url ../one/.git/ &&
+		git config remote.one.fetch refs/heads/master:refs/heads/one
+	) &&
 	git clone . three &&
-	cd three &&
-	git config branch.master.remote two &&
-	git config branch.master.merge refs/heads/one &&
-	mkdir -p .git/remotes &&
-	{
-		echo "URL: ../two/.git/"
-		echo "Pull: refs/heads/master:refs/heads/two"
-		echo "Pull: refs/heads/one:refs/heads/one"
-	} >.git/remotes/two &&
-	cd .. &&
+	(
+		cd three &&
+		git config branch.master.remote two &&
+		git config branch.master.merge refs/heads/one &&
+		mkdir -p .git/remotes &&
+		{
+			echo "URL: ../two/.git/"
+			echo "Pull: refs/heads/master:refs/heads/two"
+			echo "Pull: refs/heads/one:refs/heads/one"
+		} >.git/remotes/two
+	) &&
 	git clone . bundle &&
 	git clone . seven
 '
@@ -62,11 +78,61 @@
 	master_in_two=`cd ../two && git rev-parse master` &&
 	one_in_two=`cd ../two && git rev-parse one` &&
 	{
-		echo "$master_in_two	not-for-merge"
 		echo "$one_in_two	"
+		echo "$master_in_two	not-for-merge"
 	} >expected &&
 	cut -f -2 .git/FETCH_HEAD >actual &&
-	diff expected actual'
+	test_cmp expected actual'
+
+test_expect_success 'fetch --prune on its own works as expected' '
+	cd "$D" &&
+	git clone . prune &&
+	cd prune &&
+	git fetch origin refs/heads/master:refs/remotes/origin/extrabranch &&
+
+	git fetch --prune origin &&
+	test_must_fail git rev-parse origin/extrabranch
+'
+
+test_expect_success 'fetch --prune with a branch name keeps branches' '
+	cd "$D" &&
+	git clone . prune-branch &&
+	cd prune-branch &&
+	git fetch origin refs/heads/master:refs/remotes/origin/extrabranch &&
+
+	git fetch --prune origin master &&
+	git rev-parse origin/extrabranch
+'
+
+test_expect_success 'fetch --prune with a namespace keeps other namespaces' '
+	cd "$D" &&
+	git clone . prune-namespace &&
+	cd prune-namespace &&
+
+	git fetch --prune origin refs/heads/a/*:refs/remotes/origin/a/* &&
+	git rev-parse origin/master
+'
+
+test_expect_success 'fetch --prune --tags does not delete the remote-tracking branches' '
+	cd "$D" &&
+	git clone . prune-tags &&
+	cd prune-tags &&
+	git fetch origin refs/heads/master:refs/tags/sometag &&
+
+	git fetch --prune --tags origin &&
+	git rev-parse origin/master &&
+	test_must_fail git rev-parse somebranch
+'
+
+test_expect_success 'fetch --prune --tags with branch does not delete other remote-tracking branches' '
+	cd "$D" &&
+	git clone . prune-tags-branch &&
+	cd prune-tags-branch &&
+	git fetch origin refs/heads/master:refs/remotes/origin/extrabranch &&
+
+	git fetch --prune --tags origin master &&
+	git rev-parse origin/extrabranch
+'
 
 test_expect_success 'fetch tags when there is no tags' '
 
@@ -96,6 +162,36 @@
 
 '
 
+test_expect_success 'fetch uses remote ref names to describe new refs' '
+	cd "$D" &&
+	git init descriptive &&
+	(
+		cd descriptive &&
+		git config remote.o.url .. &&
+		git config remote.o.fetch "refs/heads/*:refs/crazyheads/*" &&
+		git config --add remote.o.fetch "refs/others/*:refs/heads/*" &&
+		git fetch o
+	) &&
+	git tag -a -m "Descriptive tag" descriptive-tag &&
+	git branch descriptive-branch &&
+	git checkout descriptive-branch &&
+	echo "Nuts" >crazy &&
+	git add crazy &&
+	git commit -a -m "descriptive commit" &&
+	git update-ref refs/others/crazy HEAD &&
+	(
+		cd descriptive &&
+		git fetch o 2>actual &&
+		grep " -> refs/crazyheads/descriptive-branch$" actual |
+		test_i18ngrep "new branch" &&
+		grep " -> descriptive-tag$" actual |
+		test_i18ngrep "new tag" &&
+		grep " -> crazy$" actual |
+		test_i18ngrep "new ref"
+	) &&
+	git checkout master
+'
+
 test_expect_success 'fetch must not resolve short tag name' '
 
 	cd "$D" &&
@@ -108,17 +204,16 @@
 
 '
 
-test_expect_success 'fetch must not resolve short remote name' '
+test_expect_success 'fetch can now resolve short remote name' '
 
 	cd "$D" &&
-	git update-ref refs/remotes/six/HEAD HEAD
+	git update-ref refs/remotes/six/HEAD HEAD &&
 
 	mkdir six &&
 	cd six &&
 	git init &&
 
-	test_must_fail git fetch .. six:six
-
+	git fetch .. six:six
 '
 
 test_expect_success 'create bundle 1' '
@@ -146,18 +241,12 @@
 	test_must_fail git fetch "$D/bundle1" master:master
 '
 
+
 test_expect_success 'bundle 1 has only 3 files ' '
 	cd "$D" &&
-	(
-		while read x && test -n "$x"
-		do
-			:;
-		done
-		cat
-	) <bundle1 >bundle.pack &&
+	convert_bundle_to_pack <bundle1 >bundle.pack &&
 	git index-pack bundle.pack &&
-	verify=$(git verify-pack -v bundle.pack) &&
-	test 4 = $(echo "$verify" | wc -l)
+	test_bundle_object_count bundle.pack 3
 '
 
 test_expect_success 'unbundle 2' '
@@ -172,15 +261,9 @@
 	git add file2 &&
 	git commit -m add.file2 file2 &&
 	git bundle create bundle3 -1 HEAD &&
-	(
-		while read x && test -n "$x"
-		do
-			:;
-		done
-		cat
-	) <bundle3 >bundle.pack &&
+	convert_bundle_to_pack <bundle3 >bundle.pack &&
 	git index-pack bundle.pack &&
-	test 4 = $(git verify-pack -v bundle.pack | wc -l)
+	test_bundle_object_count bundle.pack 3
 '
 
 test_expect_success 'bundle should be able to create a full history' '
@@ -235,6 +318,38 @@
 	git fetch blub
 '
 
+# URL supplied to fetch does not match the url of the configured branch's remote
+test_expect_success 'fetch from GIT URL with a non-applying branch.<name>.merge [1]' '
+	one_head=$(cd one && git rev-parse HEAD) &&
+	this_head=$(git rev-parse HEAD) &&
+	git update-ref -d FETCH_HEAD &&
+	git fetch one &&
+	test $one_head = "$(git rev-parse --verify FETCH_HEAD)" &&
+	test $this_head = "$(git rev-parse --verify HEAD)"
+'
+
+# URL supplied to fetch matches the url of the configured branch's remote and
+# the merge spec matches the branch the remote HEAD points to
+test_expect_success 'fetch from GIT URL with a non-applying branch.<name>.merge [2]' '
+	one_ref=$(cd one && git symbolic-ref HEAD) &&
+	git config branch.master.remote blub &&
+	git config branch.master.merge "$one_ref" &&
+	git update-ref -d FETCH_HEAD &&
+	git fetch one &&
+	test $one_head = "$(git rev-parse --verify FETCH_HEAD)" &&
+	test $this_head = "$(git rev-parse --verify HEAD)"
+'
+
+# URL supplied to fetch matches the url of the configured branch's remote, but
+# the merge spec does not match the branch the remote HEAD points to
+test_expect_success 'fetch from GIT URL with a non-applying branch.<name>.merge [3]' '
+	git config branch.master.merge "${one_ref}_not" &&
+	git update-ref -d FETCH_HEAD &&
+	git fetch one &&
+	test $one_head = "$(git rev-parse --verify FETCH_HEAD)" &&
+	test $this_head = "$(git rev-parse --verify HEAD)"
+'
+
 # the strange name is: a\!'b
 test_expect_success 'quoting of a strangely named repo' '
 	test_must_fail git fetch "a\\!'\''b" > result 2>&1 &&
@@ -336,4 +451,39 @@
 
 '
 
+test_expect_success 'fetch --dry-run' '
+
+	rm -f .git/FETCH_HEAD &&
+	git fetch --dry-run . &&
+	! test -f .git/FETCH_HEAD
+'
+
+test_expect_success "should be able to fetch with duplicate refspecs" '
+	mkdir dups &&
+	(
+		cd dups &&
+		git init &&
+		git config branch.master.remote three &&
+		git config remote.three.url ../three/.git &&
+		git config remote.three.fetch +refs/heads/*:refs/remotes/origin/* &&
+		git config --add remote.three.fetch +refs/heads/*:refs/remotes/origin/* &&
+		git fetch three
+	)
+'
+
+test_expect_success 'all boundary commits are excluded' '
+	test_commit base &&
+	test_commit oneside &&
+	git checkout HEAD^ &&
+	test_commit otherside &&
+	git checkout master &&
+	test_tick &&
+	git merge otherside &&
+	ad=$(git log --no-walk --format=%ad HEAD) &&
+	git bundle create twoside-boundary.bdl master --since="$ad" &&
+	convert_bundle_to_pack <twoside-boundary.bdl >twoside-boundary.pack &&
+	pack=$(git index-pack --fix-thin --stdin <twoside-boundary.pack) &&
+	test_bundle_object_count .git/objects/pack/pack-${pack##pack	}.pack 3
+'
+
 test_done
diff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh
index 1dd8eed..d16e5d3 100755
--- a/t/t5512-ls-remote.sh
+++ b/t/t5512-ls-remote.sh
@@ -5,7 +5,6 @@
 . ./test-lib.sh
 
 test_expect_success setup '
-
 	>file &&
 	git add file &&
 	test_tick &&
@@ -18,35 +17,113 @@
 	) >expected.all &&
 
 	git remote add self "$(pwd)/.git"
-
 '
 
 test_expect_success 'ls-remote --tags .git' '
-
 	git ls-remote --tags .git >actual &&
 	test_cmp expected.tag actual
-
 '
 
 test_expect_success 'ls-remote .git' '
-
 	git ls-remote .git >actual &&
 	test_cmp expected.all actual
-
 '
 
 test_expect_success 'ls-remote --tags self' '
-
 	git ls-remote --tags self >actual &&
 	test_cmp expected.tag actual
-
 '
 
 test_expect_success 'ls-remote self' '
-
 	git ls-remote self >actual &&
 	test_cmp expected.all actual
+'
 
+test_expect_success 'dies when no remote specified and no default remotes found' '
+	test_must_fail git ls-remote
+'
+
+test_expect_success 'use "origin" when no remote specified' '
+	URL="$(pwd)/.git" &&
+	echo "From $URL" >exp_err &&
+
+	git remote add origin "$URL" &&
+	git ls-remote 2>actual_err >actual &&
+
+	test_cmp exp_err actual_err &&
+	test_cmp expected.all actual
+'
+
+test_expect_success 'suppress "From <url>" with -q' '
+	git ls-remote -q 2>actual_err &&
+	test_must_fail test_cmp exp_err actual_err
+'
+
+test_expect_success 'use branch.<name>.remote if possible' '
+	#
+	# Test that we are indeed using branch.<name>.remote, not "origin", even
+	# though the "origin" remote has been set.
+	#
+
+	# setup a new remote to differentiate from "origin"
+	git clone . other.git &&
+	(
+		cd other.git &&
+		echo "$(git rev-parse HEAD)	HEAD"
+		git show-ref	| sed -e "s/ /	/"
+	) >exp &&
+
+	URL="other.git" &&
+	echo "From $URL" >exp_err &&
+
+	git remote add other $URL &&
+	git config branch.master.remote other &&
+
+	git ls-remote 2>actual_err >actual &&
+	test_cmp exp_err actual_err &&
+	test_cmp exp actual
+'
+
+test_expect_success 'confuses pattern as remote when no remote specified' '
+	cat >exp <<-\EOF &&
+	fatal: '\''refs*master'\'' does not appear to be a git repository
+	fatal: Could not read from remote repository.
+
+	Please make sure you have the correct access rights
+	and the repository exists.
+	EOF
+	#
+	# Do not expect "git ls-remote <pattern>" to work; ls-remote needs
+	# <remote> if you want to feed <pattern>, just like you cannot say
+	# fetch <branch>.
+	# We could just as easily have used "master"; the "*" emphasizes its
+	# role as a pattern.
+	test_must_fail git ls-remote refs*master >actual 2>&1 &&
+	test_cmp exp actual
+'
+
+test_expect_success 'die with non-2 for wrong repository even with --exit-code' '
+	git ls-remote --exit-code ./no-such-repository ;# not &&
+	status=$? &&
+	test $status != 2 && test $status != 0
+'
+
+test_expect_success 'Report success even when nothing matches' '
+	git ls-remote other.git "refs/nsn/*" >actual &&
+	>expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'Report no-match with --exit-code' '
+	test_expect_code 2 git ls-remote --exit-code other.git "refs/nsn/*" >actual &&
+	>expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'Report match with --exit-code' '
+	git ls-remote --exit-code other.git "refs/tags/*" >actual &&
+	git ls-remote . tags/mark >expect &&
+	test_cmp expect actual
 '
 
 test_done
diff --git a/t/t5513-fetch-track.sh b/t/t5513-fetch-track.sh
index 9e74862..65d1e05 100755
--- a/t/t5513-fetch-track.sh
+++ b/t/t5513-fetch-track.sh
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-test_description='fetch follows remote tracking branches correctly'
+test_description='fetch follows remote-tracking branches correctly'
 
 . ./test-lib.sh
 
diff --git a/t/t5514-fetch-multiple.sh b/t/t5514-fetch-multiple.sh
new file mode 100755
index 0000000..227dd56
--- /dev/null
+++ b/t/t5514-fetch-multiple.sh
@@ -0,0 +1,154 @@
+#!/bin/sh
+
+test_description='fetch --all works correctly'
+
+. ./test-lib.sh
+
+setup_repository () {
+	mkdir "$1" && (
+	cd "$1" &&
+	git init &&
+	>file &&
+	git add file &&
+	test_tick &&
+	git commit -m "Initial" &&
+	git checkout -b side &&
+	>elif &&
+	git add elif &&
+	test_tick &&
+	git commit -m "Second" &&
+	git checkout master
+	)
+}
+
+test_expect_success setup '
+	setup_repository one &&
+	setup_repository two &&
+	(
+		cd two && git branch another
+	) &&
+	git clone --mirror two three &&
+	git clone one test
+'
+
+cat > test/expect << EOF
+  one/master
+  one/side
+  origin/HEAD -> origin/master
+  origin/master
+  origin/side
+  three/another
+  three/master
+  three/side
+  two/another
+  two/master
+  two/side
+EOF
+
+test_expect_success 'git fetch --all' '
+	(cd test &&
+	 git remote add one ../one &&
+	 git remote add two ../two &&
+	 git remote add three ../three &&
+	 git fetch --all &&
+	 git branch -r > output &&
+	 test_cmp expect output)
+'
+
+test_expect_success 'git fetch --all should continue if a remote has errors' '
+	(git clone one test2 &&
+	 cd test2 &&
+	 git remote add bad ../non-existing &&
+	 git remote add one ../one &&
+	 git remote add two ../two &&
+	 git remote add three ../three &&
+	 test_must_fail git fetch --all &&
+	 git branch -r > output &&
+	 test_cmp ../test/expect output)
+'
+
+test_expect_success 'git fetch --all does not allow non-option arguments' '
+	(cd test &&
+	 test_must_fail git fetch --all origin &&
+	 test_must_fail git fetch --all origin master)
+'
+
+cat > expect << EOF
+  origin/HEAD -> origin/master
+  origin/master
+  origin/side
+  three/another
+  three/master
+  three/side
+EOF
+
+test_expect_success 'git fetch --multiple (but only one remote)' '
+	(git clone one test3 &&
+	 cd test3 &&
+	 git remote add three ../three &&
+	 git fetch --multiple three &&
+	 git branch -r > output &&
+	 test_cmp ../expect output)
+'
+
+cat > expect << EOF
+  one/master
+  one/side
+  two/another
+  two/master
+  two/side
+EOF
+
+test_expect_success 'git fetch --multiple (two remotes)' '
+	(git clone one test4 &&
+	 cd test4 &&
+	 git remote rm origin &&
+	 git remote add one ../one &&
+	 git remote add two ../two &&
+	 git fetch --multiple one two &&
+	 git branch -r > output &&
+	 test_cmp ../expect output)
+'
+
+test_expect_success 'git fetch --multiple (bad remote names)' '
+	(cd test4 &&
+	 test_must_fail git fetch --multiple four)
+'
+
+
+test_expect_success 'git fetch --all (skipFetchAll)' '
+	(cd test4 &&
+	 for b in $(git branch -r)
+	 do
+		git branch -r -d $b || break
+	 done &&
+	 git remote add three ../three &&
+	 git config remote.three.skipFetchAll true &&
+	 git fetch --all &&
+	 git branch -r > output &&
+	 test_cmp ../expect output)
+'
+
+cat > expect << EOF
+  one/master
+  one/side
+  three/another
+  three/master
+  three/side
+  two/another
+  two/master
+  two/side
+EOF
+
+test_expect_success 'git fetch --multiple (ignoring skipFetchAll)' '
+	(cd test4 &&
+	 for b in $(git branch -r)
+	 do
+		git branch -r -d $b || break
+	 done &&
+	 git fetch --multiple one two three &&
+	 git branch -r > output &&
+	 test_cmp ../expect output)
+'
+
+test_done
diff --git a/t/t5515/fetch.br-branches-default b/t/t5515/fetch.br-branches-default
index 2e0414f..a1bc3d5 100644
--- a/t/t5515/fetch.br-branches-default
+++ b/t/t5515/fetch.br-branches-default
@@ -1,8 +1,8 @@
 # br-branches-default
 754b754407bf032e9a2f9d5a9ad05ca79a6b228f		branch 'master' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-branches-default-merge b/t/t5515/fetch.br-branches-default-merge
index ca2cc1d..12ab08e 100644
--- a/t/t5515/fetch.br-branches-default-merge
+++ b/t/t5515/fetch.br-branches-default-merge
@@ -1,9 +1,9 @@
 # br-branches-default-merge
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	branch 'master' of ../
 0567da4d5edd2ff4bb292a465ba9e64dcad9536b		branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	branch 'master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-branches-default-merge_branches-default b/t/t5515/fetch.br-branches-default-merge_branches-default
index 7d947cd..5442752 100644
--- a/t/t5515/fetch.br-branches-default-merge_branches-default
+++ b/t/t5515/fetch.br-branches-default-merge_branches-default
@@ -1,9 +1,9 @@
 # br-branches-default-merge branches-default
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	branch 'master' of ../
 0567da4d5edd2ff4bb292a465ba9e64dcad9536b		branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	branch 'master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-branches-default-octopus b/t/t5515/fetch.br-branches-default-octopus
index ec39c54..498a761 100644
--- a/t/t5515/fetch.br-branches-default-octopus
+++ b/t/t5515/fetch.br-branches-default-octopus
@@ -1,10 +1,10 @@
 # br-branches-default-octopus
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	branch 'master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689		branch 'one' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8		branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	branch 'master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-branches-default-octopus_branches-default b/t/t5515/fetch.br-branches-default-octopus_branches-default
index 6bf42e2..0857f13 100644
--- a/t/t5515/fetch.br-branches-default-octopus_branches-default
+++ b/t/t5515/fetch.br-branches-default-octopus_branches-default
@@ -1,10 +1,10 @@
 # br-branches-default-octopus branches-default
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	branch 'master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689		branch 'one' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8		branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	branch 'master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-branches-default_branches-default b/t/t5515/fetch.br-branches-default_branches-default
index 4a2bf3c..8cbd718 100644
--- a/t/t5515/fetch.br-branches-default_branches-default
+++ b/t/t5515/fetch.br-branches-default_branches-default
@@ -1,8 +1,8 @@
 # br-branches-default branches-default
 754b754407bf032e9a2f9d5a9ad05ca79a6b228f		branch 'master' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-branches-one b/t/t5515/fetch.br-branches-one
index 12ac8d2..c98f670 100644
--- a/t/t5515/fetch.br-branches-one
+++ b/t/t5515/fetch.br-branches-one
@@ -1,8 +1,8 @@
 # br-branches-one
 8e32a6d901327a23ef831511badce7bf3bf46689		branch 'one' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-branches-one-merge b/t/t5515/fetch.br-branches-one-merge
index b4b3b35..54a7742 100644
--- a/t/t5515/fetch.br-branches-one-merge
+++ b/t/t5515/fetch.br-branches-one-merge
@@ -1,9 +1,9 @@
 # br-branches-one-merge
-8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	branch 'one' of ../
 0567da4d5edd2ff4bb292a465ba9e64dcad9536b		branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	branch 'one' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-branches-one-merge_branches-one b/t/t5515/fetch.br-branches-one-merge_branches-one
index 2ecef38..b4d1bb0 100644
--- a/t/t5515/fetch.br-branches-one-merge_branches-one
+++ b/t/t5515/fetch.br-branches-one-merge_branches-one
@@ -1,9 +1,9 @@
 # br-branches-one-merge branches-one
-8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	branch 'one' of ../
 0567da4d5edd2ff4bb292a465ba9e64dcad9536b		branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	branch 'one' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-branches-one-octopus b/t/t5515/fetch.br-branches-one-octopus
index 96e3029..97c4b54 100644
--- a/t/t5515/fetch.br-branches-one-octopus
+++ b/t/t5515/fetch.br-branches-one-octopus
@@ -1,9 +1,9 @@
 # br-branches-one-octopus
 8e32a6d901327a23ef831511badce7bf3bf46689		branch 'one' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8		branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-branches-one-octopus_branches-one b/t/t5515/fetch.br-branches-one-octopus_branches-one
index 55e0bad..df705f7 100644
--- a/t/t5515/fetch.br-branches-one-octopus_branches-one
+++ b/t/t5515/fetch.br-branches-one-octopus_branches-one
@@ -1,9 +1,9 @@
 # br-branches-one-octopus branches-one
 8e32a6d901327a23ef831511badce7bf3bf46689		branch 'one' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8		branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-branches-one_branches-one b/t/t5515/fetch.br-branches-one_branches-one
index 281fa09..96890e5 100644
--- a/t/t5515/fetch.br-branches-one_branches-one
+++ b/t/t5515/fetch.br-branches-one_branches-one
@@ -1,8 +1,8 @@
 # br-branches-one branches-one
 8e32a6d901327a23ef831511badce7bf3bf46689		branch 'one' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-explicit b/t/t5515/fetch.br-config-explicit
index e2fa9c8..68fc927 100644
--- a/t/t5515/fetch.br-config-explicit
+++ b/t/t5515/fetch.br-config-explicit
@@ -3,9 +3,9 @@
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	branch 'one' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	branch 'two' of ../
 0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-explicit-merge b/t/t5515/fetch.br-config-explicit-merge
index ec1a723..5ce764a 100644
--- a/t/t5515/fetch.br-config-explicit-merge
+++ b/t/t5515/fetch.br-config-explicit-merge
@@ -1,11 +1,11 @@
 # br-config-explicit-merge
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b		branch 'three' of ../
 754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	branch 'master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	branch 'one' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	branch 'two' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b		branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-explicit-merge_config-explicit b/t/t5515/fetch.br-config-explicit-merge_config-explicit
index 54f6891..b1152b7 100644
--- a/t/t5515/fetch.br-config-explicit-merge_config-explicit
+++ b/t/t5515/fetch.br-config-explicit-merge_config-explicit
@@ -1,11 +1,11 @@
 # br-config-explicit-merge config-explicit
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b		branch 'three' of ../
 754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	branch 'master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	branch 'one' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	branch 'two' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b		branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-explicit-octopus b/t/t5515/fetch.br-config-explicit-octopus
index 7011dfc..110577b 100644
--- a/t/t5515/fetch.br-config-explicit-octopus
+++ b/t/t5515/fetch.br-config-explicit-octopus
@@ -1,11 +1,11 @@
 # br-config-explicit-octopus
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	branch 'master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689		branch 'one' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8		branch 'two' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	branch 'master' of ../
 0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-explicit-octopus_config-explicit b/t/t5515/fetch.br-config-explicit-octopus_config-explicit
index bdad51f..a29dd8b 100644
--- a/t/t5515/fetch.br-config-explicit-octopus_config-explicit
+++ b/t/t5515/fetch.br-config-explicit-octopus_config-explicit
@@ -1,11 +1,11 @@
 # br-config-explicit-octopus config-explicit
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	branch 'master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689		branch 'one' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8		branch 'two' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	branch 'master' of ../
 0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-explicit_config-explicit b/t/t5515/fetch.br-config-explicit_config-explicit
index 1b237dd..b19b016 100644
--- a/t/t5515/fetch.br-config-explicit_config-explicit
+++ b/t/t5515/fetch.br-config-explicit_config-explicit
@@ -3,9 +3,9 @@
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	branch 'one' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	branch 'two' of ../
 0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-glob b/t/t5515/fetch.br-config-glob
index e75ec2f..946d70c 100644
--- a/t/t5515/fetch.br-config-glob
+++ b/t/t5515/fetch.br-config-glob
@@ -3,9 +3,9 @@
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	branch 'one' of ../
 0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	branch 'three' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-glob-merge b/t/t5515/fetch.br-config-glob-merge
index ce8f739..89f2596 100644
--- a/t/t5515/fetch.br-config-glob-merge
+++ b/t/t5515/fetch.br-config-glob-merge
@@ -1,11 +1,11 @@
 # br-config-glob-merge
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b		branch 'three' of ../
 754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	branch 'master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	branch 'one' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b		branch 'three' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-glob-merge_config-glob b/t/t5515/fetch.br-config-glob-merge_config-glob
index 5817bed..2ba4832 100644
--- a/t/t5515/fetch.br-config-glob-merge_config-glob
+++ b/t/t5515/fetch.br-config-glob-merge_config-glob
@@ -1,11 +1,11 @@
 # br-config-glob-merge config-glob
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b		branch 'three' of ../
 754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	branch 'master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	branch 'one' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b		branch 'three' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-glob-octopus b/t/t5515/fetch.br-config-glob-octopus
index 938e532..64994df 100644
--- a/t/t5515/fetch.br-config-glob-octopus
+++ b/t/t5515/fetch.br-config-glob-octopus
@@ -1,11 +1,11 @@
 # br-config-glob-octopus
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	branch 'master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689		branch 'one' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	branch 'three' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8		branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	branch 'master' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	branch 'three' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-glob-octopus_config-glob b/t/t5515/fetch.br-config-glob-octopus_config-glob
index c9225bf..681a725 100644
--- a/t/t5515/fetch.br-config-glob-octopus_config-glob
+++ b/t/t5515/fetch.br-config-glob-octopus_config-glob
@@ -1,11 +1,11 @@
 # br-config-glob-octopus config-glob
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	branch 'master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689		branch 'one' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	branch 'three' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8		branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	branch 'master' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	branch 'three' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-glob_config-glob b/t/t5515/fetch.br-config-glob_config-glob
index a6c20f9..19daf0c 100644
--- a/t/t5515/fetch.br-config-glob_config-glob
+++ b/t/t5515/fetch.br-config-glob_config-glob
@@ -3,9 +3,9 @@
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	branch 'one' of ../
 0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	branch 'three' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-explicit b/t/t5515/fetch.br-remote-explicit
index 83534d2..ab44bc5 100644
--- a/t/t5515/fetch.br-remote-explicit
+++ b/t/t5515/fetch.br-remote-explicit
@@ -3,9 +3,9 @@
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	branch 'one' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	branch 'two' of ../
 0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-explicit-merge b/t/t5515/fetch.br-remote-explicit-merge
index a9064dd..d018b35 100644
--- a/t/t5515/fetch.br-remote-explicit-merge
+++ b/t/t5515/fetch.br-remote-explicit-merge
@@ -1,11 +1,11 @@
 # br-remote-explicit-merge
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b		branch 'three' of ../
 754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	branch 'master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	branch 'one' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	branch 'two' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b		branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-explicit-merge_remote-explicit b/t/t5515/fetch.br-remote-explicit-merge_remote-explicit
index 732a37e..0d3d780 100644
--- a/t/t5515/fetch.br-remote-explicit-merge_remote-explicit
+++ b/t/t5515/fetch.br-remote-explicit-merge_remote-explicit
@@ -1,11 +1,11 @@
 # br-remote-explicit-merge remote-explicit
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b		branch 'three' of ../
 754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	branch 'master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	branch 'one' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	branch 'two' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b		branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-explicit-octopus b/t/t5515/fetch.br-remote-explicit-octopus
index ecf020d..6f84304 100644
--- a/t/t5515/fetch.br-remote-explicit-octopus
+++ b/t/t5515/fetch.br-remote-explicit-octopus
@@ -1,11 +1,11 @@
 # br-remote-explicit-octopus
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	branch 'master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689		branch 'one' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8		branch 'two' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	branch 'master' of ../
 0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-explicit-octopus_remote-explicit b/t/t5515/fetch.br-remote-explicit-octopus_remote-explicit
index af77531..3546a83 100644
--- a/t/t5515/fetch.br-remote-explicit-octopus_remote-explicit
+++ b/t/t5515/fetch.br-remote-explicit-octopus_remote-explicit
@@ -1,11 +1,11 @@
 # br-remote-explicit-octopus remote-explicit
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	branch 'master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689		branch 'one' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8		branch 'two' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	branch 'master' of ../
 0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-explicit_remote-explicit b/t/t5515/fetch.br-remote-explicit_remote-explicit
index 51fae56..01e014e 100644
--- a/t/t5515/fetch.br-remote-explicit_remote-explicit
+++ b/t/t5515/fetch.br-remote-explicit_remote-explicit
@@ -3,9 +3,9 @@
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	branch 'one' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	branch 'two' of ../
 0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-glob b/t/t5515/fetch.br-remote-glob
index 94e6ad3..09bfcee 100644
--- a/t/t5515/fetch.br-remote-glob
+++ b/t/t5515/fetch.br-remote-glob
@@ -3,9 +3,9 @@
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	branch 'one' of ../
 0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	branch 'three' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-glob-merge b/t/t5515/fetch.br-remote-glob-merge
index 09362e2..7e1a433 100644
--- a/t/t5515/fetch.br-remote-glob-merge
+++ b/t/t5515/fetch.br-remote-glob-merge
@@ -1,11 +1,11 @@
 # br-remote-glob-merge
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b		branch 'three' of ../
 754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	branch 'master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	branch 'one' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b		branch 'three' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-glob-merge_remote-glob b/t/t5515/fetch.br-remote-glob-merge_remote-glob
index e2eabec..53571bb 100644
--- a/t/t5515/fetch.br-remote-glob-merge_remote-glob
+++ b/t/t5515/fetch.br-remote-glob-merge_remote-glob
@@ -1,11 +1,11 @@
 # br-remote-glob-merge remote-glob
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b		branch 'three' of ../
 754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	branch 'master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	branch 'one' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b		branch 'three' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-glob-octopus b/t/t5515/fetch.br-remote-glob-octopus
index b08e046..c7c8b6d 100644
--- a/t/t5515/fetch.br-remote-glob-octopus
+++ b/t/t5515/fetch.br-remote-glob-octopus
@@ -1,11 +1,11 @@
 # br-remote-glob-octopus
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	branch 'master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689		branch 'one' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	branch 'three' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8		branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	branch 'master' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	branch 'three' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-glob-octopus_remote-glob b/t/t5515/fetch.br-remote-glob-octopus_remote-glob
index d4d547c..36076fb 100644
--- a/t/t5515/fetch.br-remote-glob-octopus_remote-glob
+++ b/t/t5515/fetch.br-remote-glob-octopus_remote-glob
@@ -1,11 +1,11 @@
 # br-remote-glob-octopus remote-glob
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	branch 'master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689		branch 'one' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	branch 'three' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8		branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	branch 'master' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	branch 'three' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-glob_remote-glob b/t/t5515/fetch.br-remote-glob_remote-glob
index 646dbc8..20ba5cb 100644
--- a/t/t5515/fetch.br-remote-glob_remote-glob
+++ b/t/t5515/fetch.br-remote-glob_remote-glob
@@ -3,9 +3,9 @@
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	branch 'one' of ../
 0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	branch 'three' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-unconfig b/t/t5515/fetch.br-unconfig
index 65ce6d9..887ccfc 100644
--- a/t/t5515/fetch.br-unconfig
+++ b/t/t5515/fetch.br-unconfig
@@ -3,9 +3,9 @@
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	branch 'one' of ../
 0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	branch 'three' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-unconfig_--tags_.._.git b/t/t5515/fetch.br-unconfig_--tags_.._.git
index 8258c80..1669cc4 100644
--- a/t/t5515/fetch.br-unconfig_--tags_.._.git
+++ b/t/t5515/fetch.br-unconfig_--tags_.._.git
@@ -1,7 +1,7 @@
 # br-unconfig --tags ../.git
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-unconfig_.._.git_one_tag_tag-one_tag_tag-three-file b/t/t5515/fetch.br-unconfig_.._.git_one_tag_tag-one_tag_tag-three-file
index f02bab2..7411536 100644
--- a/t/t5515/fetch.br-unconfig_.._.git_one_tag_tag-one_tag_tag-three-file
+++ b/t/t5515/fetch.br-unconfig_.._.git_one_tag_tag-one_tag_tag-three-file
@@ -2,7 +2,7 @@
 8e32a6d901327a23ef831511badce7bf3bf46689		branch 'one' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689		tag 'tag-one' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-unconfig_.._.git_tag_tag-one-tree_tag_tag-three-file b/t/t5515/fetch.br-unconfig_.._.git_tag_tag-one-tree_tag_tag-three-file
index 85de411..7726983 100644
--- a/t/t5515/fetch.br-unconfig_.._.git_tag_tag-one-tree_tag_tag-three-file
+++ b/t/t5515/fetch.br-unconfig_.._.git_tag_tag-one-tree_tag_tag-three-file
@@ -1,7 +1,7 @@
 # br-unconfig ../.git tag tag-one-tree tag tag-three-file
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-unconfig_.._.git_tag_tag-one_tag_tag-three b/t/t5515/fetch.br-unconfig_.._.git_tag_tag-one_tag_tag-three
index 0da2337..7b3750c 100644
--- a/t/t5515/fetch.br-unconfig_.._.git_tag_tag-one_tag_tag-three
+++ b/t/t5515/fetch.br-unconfig_.._.git_tag_tag-one_tag_tag-three
@@ -1,7 +1,7 @@
 # br-unconfig ../.git tag tag-one tag tag-three
 8e32a6d901327a23ef831511badce7bf3bf46689		tag 'tag-one' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b		tag 'tag-three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899		tag 'tag-three' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-unconfig_branches-default b/t/t5515/fetch.br-unconfig_branches-default
index fc7041e..da30e3c 100644
--- a/t/t5515/fetch.br-unconfig_branches-default
+++ b/t/t5515/fetch.br-unconfig_branches-default
@@ -1,8 +1,8 @@
 # br-unconfig branches-default
 754b754407bf032e9a2f9d5a9ad05ca79a6b228f		branch 'master' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-unconfig_branches-one b/t/t5515/fetch.br-unconfig_branches-one
index e94cde7..e461431 100644
--- a/t/t5515/fetch.br-unconfig_branches-one
+++ b/t/t5515/fetch.br-unconfig_branches-one
@@ -1,8 +1,8 @@
 # br-unconfig branches-one
 8e32a6d901327a23ef831511badce7bf3bf46689		branch 'one' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-unconfig_config-explicit b/t/t5515/fetch.br-unconfig_config-explicit
index 01a283e..ed323c9 100644
--- a/t/t5515/fetch.br-unconfig_config-explicit
+++ b/t/t5515/fetch.br-unconfig_config-explicit
@@ -3,9 +3,9 @@
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	branch 'one' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	branch 'two' of ../
 0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-unconfig_config-glob b/t/t5515/fetch.br-unconfig_config-glob
index 3a556c5..2372ed0 100644
--- a/t/t5515/fetch.br-unconfig_config-glob
+++ b/t/t5515/fetch.br-unconfig_config-glob
@@ -3,9 +3,9 @@
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	branch 'one' of ../
 0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	branch 'three' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-unconfig_remote-explicit b/t/t5515/fetch.br-unconfig_remote-explicit
index db216df..6318dd1 100644
--- a/t/t5515/fetch.br-unconfig_remote-explicit
+++ b/t/t5515/fetch.br-unconfig_remote-explicit
@@ -3,9 +3,9 @@
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	branch 'one' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	branch 'two' of ../
 0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-unconfig_remote-glob b/t/t5515/fetch.br-unconfig_remote-glob
index aee65c2..1d9afad 100644
--- a/t/t5515/fetch.br-unconfig_remote-glob
+++ b/t/t5515/fetch.br-unconfig_remote-glob
@@ -3,9 +3,9 @@
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	branch 'one' of ../
 0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	branch 'three' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master b/t/t5515/fetch.master
index 950fd07..9b29d67 100644
--- a/t/t5515/fetch.master
+++ b/t/t5515/fetch.master
@@ -3,9 +3,9 @@
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	branch 'one' of ../
 0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	branch 'three' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_--tags_.._.git b/t/t5515/fetch.master_--tags_.._.git
index 0e59950..8a74935 100644
--- a/t/t5515/fetch.master_--tags_.._.git
+++ b/t/t5515/fetch.master_--tags_.._.git
@@ -1,7 +1,7 @@
 # master --tags ../.git
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_.._.git_one_tag_tag-one_tag_tag-three-file b/t/t5515/fetch.master_.._.git_one_tag_tag-one_tag_tag-three-file
index 8286852..0672d12 100644
--- a/t/t5515/fetch.master_.._.git_one_tag_tag-one_tag_tag-three-file
+++ b/t/t5515/fetch.master_.._.git_one_tag_tag-one_tag_tag-three-file
@@ -2,7 +2,7 @@
 8e32a6d901327a23ef831511badce7bf3bf46689		branch 'one' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689		tag 'tag-one' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_.._.git_tag_tag-one-tree_tag_tag-three-file b/t/t5515/fetch.master_.._.git_tag_tag-one-tree_tag_tag-three-file
index 2e133ef..0fd737c 100644
--- a/t/t5515/fetch.master_.._.git_tag_tag-one-tree_tag_tag-three-file
+++ b/t/t5515/fetch.master_.._.git_tag_tag-one-tree_tag_tag-three-file
@@ -1,7 +1,7 @@
 # master ../.git tag tag-one-tree tag tag-three-file
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_.._.git_tag_tag-one_tag_tag-three b/t/t5515/fetch.master_.._.git_tag_tag-one_tag_tag-three
index 92b18b4..e488986 100644
--- a/t/t5515/fetch.master_.._.git_tag_tag-one_tag_tag-three
+++ b/t/t5515/fetch.master_.._.git_tag_tag-one_tag_tag-three
@@ -1,7 +1,7 @@
 # master ../.git tag tag-one tag tag-three
 8e32a6d901327a23ef831511badce7bf3bf46689		tag 'tag-one' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b		tag 'tag-three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899		tag 'tag-three' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_branches-default b/t/t5515/fetch.master_branches-default
index 603d6d2..2eedd3b 100644
--- a/t/t5515/fetch.master_branches-default
+++ b/t/t5515/fetch.master_branches-default
@@ -1,8 +1,8 @@
 # master branches-default
 754b754407bf032e9a2f9d5a9ad05ca79a6b228f		branch 'master' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_branches-one b/t/t5515/fetch.master_branches-one
index fe9bb0b..901ce21 100644
--- a/t/t5515/fetch.master_branches-one
+++ b/t/t5515/fetch.master_branches-one
@@ -1,8 +1,8 @@
 # master branches-one
 8e32a6d901327a23ef831511badce7bf3bf46689		branch 'one' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_config-explicit b/t/t5515/fetch.master_config-explicit
index 4be97c7..251c826 100644
--- a/t/t5515/fetch.master_config-explicit
+++ b/t/t5515/fetch.master_config-explicit
@@ -3,9 +3,9 @@
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	branch 'one' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	branch 'two' of ../
 0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_config-glob b/t/t5515/fetch.master_config-glob
index cb0726f..27c158e 100644
--- a/t/t5515/fetch.master_config-glob
+++ b/t/t5515/fetch.master_config-glob
@@ -3,9 +3,9 @@
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	branch 'one' of ../
 0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	branch 'three' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_remote-explicit b/t/t5515/fetch.master_remote-explicit
index 44a1ca8..b3cfe6b 100644
--- a/t/t5515/fetch.master_remote-explicit
+++ b/t/t5515/fetch.master_remote-explicit
@@ -3,9 +3,9 @@
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	branch 'one' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	branch 'two' of ../
 0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_remote-glob b/t/t5515/fetch.master_remote-glob
index 724e8db..118befd 100644
--- a/t/t5515/fetch.master_remote-glob
+++ b/t/t5515/fetch.master_remote-glob
@@ -3,9 +3,9 @@
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	branch 'one' of ../
 0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	branch 'three' of ../
 6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f	not-for-merge	tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5	not-for-merge	tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689	not-for-merge	tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba	not-for-merge	tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b	not-for-merge	tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899	not-for-merge	tag 'tag-three' of ../
 0e3b14047d3ee365f4f2a1b673db059c3972589c	not-for-merge	tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8	not-for-merge	tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba	not-for-merge	tag 'tag-two' of ../
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 2d2633f..b5417cc 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -12,6 +12,7 @@
 	(
 		cd testrepo &&
 		git init &&
+		git config receive.denyCurrentBranch warn &&
 		mv .git/hooks .git/hooks-disabled
 	)
 }
@@ -39,6 +40,40 @@
 	)
 }
 
+mk_test_with_hooks() {
+	mk_test "$@" &&
+	(
+		cd testrepo &&
+		mkdir .git/hooks &&
+		cd .git/hooks &&
+
+		cat >pre-receive <<-'EOF' &&
+		#!/bin/sh
+		cat - >>pre-receive.actual
+		EOF
+
+		cat >update <<-'EOF' &&
+		#!/bin/sh
+		printf "%s %s %s\n" "$@" >>update.actual
+		EOF
+
+		cat >post-receive <<-'EOF' &&
+		#!/bin/sh
+		cat - >>post-receive.actual
+		EOF
+
+		cat >post-update <<-'EOF' &&
+		#!/bin/sh
+		for ref in "$@"
+		do
+			printf "%s\n" "$ref" >>post-update.actual
+		done
+		EOF
+
+		chmod +x pre-receive update post-receive post-update
+	)
+}
+
 mk_child() {
 	rm -rf "$1" &&
 	git clone testrepo "$1"
@@ -63,13 +98,13 @@
 
 test_expect_success setup '
 
-	: >path1 &&
+	>path1 &&
 	git add path1 &&
 	test_tick &&
 	git commit -a -m repo &&
 	the_first_commit=$(git show-ref -s --verify refs/heads/master) &&
 
-	: >path2 &&
+	>path2 &&
 	git add path2 &&
 	test_tick &&
 	git commit -a -m second &&
@@ -122,6 +157,23 @@
 	)
 '
 
+test_expect_success 'fetch with pushInsteadOf (should not rewrite)' '
+	mk_empty &&
+	(
+		TRASH=$(pwd)/ &&
+		cd testrepo &&
+		git config "url.trash/.pushInsteadOf" "$TRASH" &&
+		git config remote.up.url "$TRASH." &&
+		git config remote.up.fetch "refs/heads/*:refs/remotes/origin/*" &&
+		git fetch up &&
+
+		r=$(git show-ref -s --verify refs/remotes/origin/master) &&
+		test "z$r" = "z$the_commit" &&
+
+		test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+	)
+'
+
 test_expect_success 'push without wildcard' '
 	mk_empty &&
 
@@ -162,6 +214,36 @@
 	)
 '
 
+test_expect_success 'push with pushInsteadOf' '
+	mk_empty &&
+	TRASH="$(pwd)/" &&
+	git config "url.$TRASH.pushInsteadOf" trash/ &&
+	git push trash/testrepo refs/heads/master:refs/remotes/origin/master &&
+	(
+		cd testrepo &&
+		r=$(git show-ref -s --verify refs/remotes/origin/master) &&
+		test "z$r" = "z$the_commit" &&
+
+		test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+	)
+'
+
+test_expect_success 'push with pushInsteadOf and explicit pushurl (pushInsteadOf should not rewrite)' '
+	mk_empty &&
+	TRASH="$(pwd)/" &&
+	git config "url.trash2/.pushInsteadOf" trash/ &&
+	git config remote.r.url trash/wrong &&
+	git config remote.r.pushurl "$TRASH/testrepo" &&
+	git push r refs/heads/master:refs/remotes/origin/master &&
+	(
+		cd testrepo &&
+		r=$(git show-ref -s --verify refs/remotes/origin/master) &&
+		test "z$r" = "z$the_commit" &&
+
+		test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+	)
+'
+
 test_expect_success 'push with matching heads' '
 
 	mk_test heads/master &&
@@ -319,7 +401,7 @@
 
 '
 
-test_expect_success 'push head with non-existant, incomplete dest' '
+test_expect_success 'push head with non-existent, incomplete dest' '
 
 	mk_test &&
 	git push testrepo master:branch &&
@@ -327,7 +409,7 @@
 
 '
 
-test_expect_success 'push tag with non-existant, incomplete dest' '
+test_expect_success 'push tag with non-existent, incomplete dest' '
 
 	mk_test &&
 	git tag -f v1.0 &&
@@ -336,14 +418,14 @@
 
 '
 
-test_expect_success 'push sha1 with non-existant, incomplete dest' '
+test_expect_success 'push sha1 with non-existent, incomplete dest' '
 
 	mk_test &&
 	test_must_fail git push testrepo `git rev-parse master`:foo
 
 '
 
-test_expect_success 'push ref expression with non-existant, incomplete dest' '
+test_expect_success 'push ref expression with non-existent, incomplete dest' '
 
 	mk_test &&
 	test_must_fail git push testrepo master^:branch
@@ -388,7 +470,7 @@
 
 '
 
-test_expect_success 'push HEAD with non-existant, incomplete dest' '
+test_expect_success 'push HEAD with non-existent, incomplete dest' '
 
 	mk_test &&
 	git checkout master &&
@@ -435,8 +517,10 @@
 test_expect_success 'push with dry-run' '
 
 	mk_test heads/master &&
-	(cd testrepo &&
-	 old_commit=$(git show-ref -s --verify refs/heads/master)) &&
+	(
+		cd testrepo &&
+		old_commit=$(git show-ref -s --verify refs/heads/master)
+	) &&
 	git push --dry-run testrepo &&
 	check_push_result $old_commit heads/master
 '
@@ -445,10 +529,13 @@
 
 	mk_test heads/master &&
 	mk_child child &&
-	(cd child &&
+	(
+		cd child &&
 		git pull .. master &&
 		git push &&
-	test $(git rev-parse master) = $(git rev-parse remotes/origin/master))
+		test $(git rev-parse master) = \
+			$(git rev-parse remotes/origin/master)
+	)
 
 '
 
@@ -458,10 +545,13 @@
 	mk_child child1 &&
 	mk_child child2 &&
 	(cd child1 && git pull .. master && git push) &&
-	(cd child2 &&
+	(
+		cd child2 &&
 		git pull ../child1 master &&
 		git push &&
-	test $(git rev-parse master) = $(git rev-parse remotes/origin/master))
+		test $(git rev-parse master) = \
+			$(git rev-parse remotes/origin/master)
+	)
 
 '
 
@@ -469,9 +559,11 @@
 
 	mk_test heads/master &&
 	mk_child child &&
-	(cd child &&
+	(
+		cd child &&
 		git push &&
-	! test -f .git/refs/remotes/origin/master)
+		! test -f .git/refs/remotes/origin/master
+	)
 
 '
 
@@ -480,13 +572,15 @@
 	mk_test heads/master &&
 	mk_child child &&
 	mkdir testrepo/.git/hooks &&
-	echo exit 1 >testrepo/.git/hooks/pre-receive &&
+	echo "#!/no/frobnication/today" >testrepo/.git/hooks/pre-receive &&
 	chmod +x testrepo/.git/hooks/pre-receive &&
-	(cd child &&
+	(
+		cd child &&
 		git pull .. master
 		test_must_fail git push &&
 		test $(git rev-parse master) != \
-			$(git rev-parse remotes/origin/master))
+			$(git rev-parse remotes/origin/master)
+	)
 
 '
 
@@ -499,36 +593,232 @@
 
 '
 
+test_expect_success 'pushing valid refs triggers post-receive and post-update hooks' '
+	mk_test_with_hooks heads/master heads/next &&
+	orgmaster=$(cd testrepo && git show-ref -s --verify refs/heads/master) &&
+	newmaster=$(git show-ref -s --verify refs/heads/master) &&
+	orgnext=$(cd testrepo && git show-ref -s --verify refs/heads/next) &&
+	newnext=$_z40 &&
+	git push testrepo refs/heads/master:refs/heads/master :refs/heads/next &&
+	(
+		cd testrepo/.git &&
+		cat >pre-receive.expect <<-EOF &&
+		$orgmaster $newmaster refs/heads/master
+		$orgnext $newnext refs/heads/next
+		EOF
+
+		cat >update.expect <<-EOF &&
+		refs/heads/master $orgmaster $newmaster
+		refs/heads/next $orgnext $newnext
+		EOF
+
+		cat >post-receive.expect <<-EOF &&
+		$orgmaster $newmaster refs/heads/master
+		$orgnext $newnext refs/heads/next
+		EOF
+
+		cat >post-update.expect <<-EOF &&
+		refs/heads/master
+		refs/heads/next
+		EOF
+
+		test_cmp pre-receive.expect pre-receive.actual &&
+		test_cmp update.expect update.actual &&
+		test_cmp post-receive.expect post-receive.actual &&
+		test_cmp post-update.expect post-update.actual
+	)
+'
+
+test_expect_success 'deleting dangling ref triggers hooks with correct args' '
+	mk_test_with_hooks heads/master &&
+	rm -f testrepo/.git/objects/??/* &&
+	git push testrepo :refs/heads/master &&
+	(
+		cd testrepo/.git &&
+		cat >pre-receive.expect <<-EOF &&
+		$_z40 $_z40 refs/heads/master
+		EOF
+
+		cat >update.expect <<-EOF &&
+		refs/heads/master $_z40 $_z40
+		EOF
+
+		cat >post-receive.expect <<-EOF &&
+		$_z40 $_z40 refs/heads/master
+		EOF
+
+		cat >post-update.expect <<-EOF &&
+		refs/heads/master
+		EOF
+
+		test_cmp pre-receive.expect pre-receive.actual &&
+		test_cmp update.expect update.actual &&
+		test_cmp post-receive.expect post-receive.actual &&
+		test_cmp post-update.expect post-update.actual
+	)
+'
+
+test_expect_success 'deletion of a non-existent ref is not fed to post-receive and post-update hooks' '
+	mk_test_with_hooks heads/master &&
+	orgmaster=$(cd testrepo && git show-ref -s --verify refs/heads/master) &&
+	newmaster=$(git show-ref -s --verify refs/heads/master) &&
+	git push testrepo master :refs/heads/nonexistent &&
+	(
+		cd testrepo/.git &&
+		cat >pre-receive.expect <<-EOF &&
+		$orgmaster $newmaster refs/heads/master
+		$_z40 $_z40 refs/heads/nonexistent
+		EOF
+
+		cat >update.expect <<-EOF &&
+		refs/heads/master $orgmaster $newmaster
+		refs/heads/nonexistent $_z40 $_z40
+		EOF
+
+		cat >post-receive.expect <<-EOF &&
+		$orgmaster $newmaster refs/heads/master
+		EOF
+
+		cat >post-update.expect <<-EOF &&
+		refs/heads/master
+		EOF
+
+		test_cmp pre-receive.expect pre-receive.actual &&
+		test_cmp update.expect update.actual &&
+		test_cmp post-receive.expect post-receive.actual &&
+		test_cmp post-update.expect post-update.actual
+	)
+'
+
+test_expect_success 'deletion of a non-existent ref alone does trigger post-receive and post-update hooks' '
+	mk_test_with_hooks heads/master &&
+	git push testrepo :refs/heads/nonexistent &&
+	(
+		cd testrepo/.git &&
+		cat >pre-receive.expect <<-EOF &&
+		$_z40 $_z40 refs/heads/nonexistent
+		EOF
+
+		cat >update.expect <<-EOF &&
+		refs/heads/nonexistent $_z40 $_z40
+		EOF
+
+		test_cmp pre-receive.expect pre-receive.actual &&
+		test_cmp update.expect update.actual &&
+		test_path_is_missing post-receive.actual &&
+		test_path_is_missing post-update.actual
+	)
+'
+
+test_expect_success 'mixed ref updates, deletes, invalid deletes trigger hooks with correct input' '
+	mk_test_with_hooks heads/master heads/next heads/pu &&
+	orgmaster=$(cd testrepo && git show-ref -s --verify refs/heads/master) &&
+	newmaster=$(git show-ref -s --verify refs/heads/master) &&
+	orgnext=$(cd testrepo && git show-ref -s --verify refs/heads/next) &&
+	newnext=$_z40 &&
+	orgpu=$(cd testrepo && git show-ref -s --verify refs/heads/pu) &&
+	newpu=$(git show-ref -s --verify refs/heads/master) &&
+	git push testrepo refs/heads/master:refs/heads/master \
+	    refs/heads/master:refs/heads/pu :refs/heads/next \
+	    :refs/heads/nonexistent &&
+	(
+		cd testrepo/.git &&
+		cat >pre-receive.expect <<-EOF &&
+		$orgmaster $newmaster refs/heads/master
+		$orgnext $newnext refs/heads/next
+		$orgpu $newpu refs/heads/pu
+		$_z40 $_z40 refs/heads/nonexistent
+		EOF
+
+		cat >update.expect <<-EOF &&
+		refs/heads/master $orgmaster $newmaster
+		refs/heads/next $orgnext $newnext
+		refs/heads/pu $orgpu $newpu
+		refs/heads/nonexistent $_z40 $_z40
+		EOF
+
+		cat >post-receive.expect <<-EOF &&
+		$orgmaster $newmaster refs/heads/master
+		$orgnext $newnext refs/heads/next
+		$orgpu $newpu refs/heads/pu
+		EOF
+
+		cat >post-update.expect <<-EOF &&
+		refs/heads/master
+		refs/heads/next
+		refs/heads/pu
+		EOF
+
+		test_cmp pre-receive.expect pre-receive.actual &&
+		test_cmp update.expect update.actual &&
+		test_cmp post-receive.expect post-receive.actual &&
+		test_cmp post-update.expect post-update.actual
+	)
+'
+
+test_expect_success 'allow deleting a ref using --delete' '
+	mk_test heads/master &&
+	(cd testrepo && git config receive.denyDeleteCurrent warn) &&
+	git push testrepo --delete master &&
+	(cd testrepo && test_must_fail git rev-parse --verify refs/heads/master)
+'
+
+test_expect_success 'allow deleting a tag using --delete' '
+	mk_test heads/master &&
+	git tag -a -m dummy_message deltag heads/master &&
+	git push testrepo --tags &&
+	(cd testrepo && git rev-parse --verify -q refs/tags/deltag) &&
+	git push testrepo --delete tag deltag &&
+	(cd testrepo && test_must_fail git rev-parse --verify refs/tags/deltag)
+'
+
+test_expect_success 'push --delete without args aborts' '
+	mk_test heads/master &&
+	test_must_fail git push testrepo --delete
+'
+
+test_expect_success 'push --delete refuses src:dest refspecs' '
+	mk_test heads/master &&
+	test_must_fail git push testrepo --delete master:foo
+'
+
 test_expect_success 'warn on push to HEAD of non-bare repository' '
-	mk_test heads/master
-	(cd testrepo &&
+	mk_test heads/master &&
+	(
+		cd testrepo &&
 		git checkout master &&
-		git config receive.denyCurrentBranch warn) &&
+		git config receive.denyCurrentBranch warn
+	) &&
 	git push testrepo master 2>stderr &&
 	grep "warning: updating the current branch" stderr
 '
 
 test_expect_success 'deny push to HEAD of non-bare repository' '
-	mk_test heads/master
-	(cd testrepo &&
+	mk_test heads/master &&
+	(
+		cd testrepo &&
 		git checkout master &&
-		git config receive.denyCurrentBranch true) &&
+		git config receive.denyCurrentBranch true
+	) &&
 	test_must_fail git push testrepo master
 '
 
 test_expect_success 'allow push to HEAD of bare repository (bare)' '
-	mk_test heads/master
-	(cd testrepo &&
+	mk_test heads/master &&
+	(
+		cd testrepo &&
 		git checkout master &&
 		git config receive.denyCurrentBranch true &&
-		git config core.bare true) &&
+		git config core.bare true
+	) &&
 	git push testrepo master 2>stderr &&
 	! grep "warning: updating the current branch" stderr
 '
 
 test_expect_success 'allow push to HEAD of non-bare repository (config)' '
-	mk_test heads/master
-	(cd testrepo &&
+	mk_test heads/master &&
+	(
+		cd testrepo &&
 		git checkout master &&
 		git config receive.denyCurrentBranch false
 	) &&
@@ -541,7 +831,8 @@
 	git branch second $the_first_commit &&
 	git checkout second &&
 	echo ".." > testrepo/.git/branches/branch1 &&
-	(cd testrepo &&
+	(
+		cd testrepo &&
 		git fetch branch1 &&
 		r=$(git show-ref -s --verify refs/heads/branch1) &&
 		test "z$r" = "z$the_commit" &&
@@ -553,7 +844,8 @@
 test_expect_success 'fetch with branches containing #' '
 	mk_empty &&
 	echo "..#second" > testrepo/.git/branches/branch2 &&
-	(cd testrepo &&
+	(
+		cd testrepo &&
 		git fetch branch2 &&
 		r=$(git show-ref -s --verify refs/heads/branch2) &&
 		test "z$r" = "z$the_first_commit" &&
@@ -567,7 +859,8 @@
 	git checkout second &&
 	echo "testrepo" > .git/branches/branch1 &&
 	git push branch1 &&
-	(cd testrepo &&
+	(
+		cd testrepo &&
 		r=$(git show-ref -s --verify refs/heads/master) &&
 		test "z$r" = "z$the_first_commit" &&
 		test 1 = $(git for-each-ref refs/heads | wc -l)
@@ -578,7 +871,8 @@
 	mk_empty &&
 	echo "testrepo#branch3" > .git/branches/branch2 &&
 	git push branch2 &&
-	(cd testrepo &&
+	(
+		cd testrepo &&
 		r=$(git show-ref -s --verify refs/heads/branch3) &&
 		test "z$r" = "z$the_first_commit" &&
 		test 1 = $(git for-each-ref refs/heads | wc -l)
@@ -586,4 +880,119 @@
 	git checkout master
 '
 
+test_expect_success 'push into aliased refs (consistent)' '
+	mk_test heads/master &&
+	mk_child child1 &&
+	mk_child child2 &&
+	(
+		cd child1 &&
+		git branch foo &&
+		git symbolic-ref refs/heads/bar refs/heads/foo
+		git config receive.denyCurrentBranch false
+	) &&
+	(
+		cd child2 &&
+		>path2 &&
+		git add path2 &&
+		test_tick &&
+		git commit -a -m child2 &&
+		git branch foo &&
+		git branch bar &&
+		git push ../child1 foo bar
+	)
+'
+
+test_expect_success 'push into aliased refs (inconsistent)' '
+	mk_test heads/master &&
+	mk_child child1 &&
+	mk_child child2 &&
+	(
+		cd child1 &&
+		git branch foo &&
+		git symbolic-ref refs/heads/bar refs/heads/foo
+		git config receive.denyCurrentBranch false
+	) &&
+	(
+		cd child2 &&
+		>path2 &&
+		git add path2 &&
+		test_tick &&
+		git commit -a -m child2 &&
+		git branch foo &&
+		>path3 &&
+		git add path3 &&
+		test_tick &&
+		git commit -a -m child2 &&
+		git branch bar &&
+		test_must_fail git push ../child1 foo bar 2>stderr &&
+		grep "refusing inconsistent update" stderr
+	)
+'
+
+test_expect_success 'push --porcelain' '
+	mk_empty &&
+	echo >.git/foo  "To testrepo" &&
+	echo >>.git/foo "*	refs/heads/master:refs/remotes/origin/master	[new branch]"  &&
+	echo >>.git/foo "Done" &&
+	git push >.git/bar --porcelain  testrepo refs/heads/master:refs/remotes/origin/master &&
+	(
+		cd testrepo &&
+		r=$(git show-ref -s --verify refs/remotes/origin/master) &&
+		test "z$r" = "z$the_commit" &&
+		test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+	) &&
+	test_cmp .git/foo .git/bar
+'
+
+test_expect_success 'push --porcelain bad url' '
+	mk_empty &&
+	test_must_fail git push >.git/bar --porcelain asdfasdfasd refs/heads/master:refs/remotes/origin/master &&
+	test_must_fail grep -q Done .git/bar
+'
+
+test_expect_success 'push --porcelain rejected' '
+	mk_empty &&
+	git push testrepo refs/heads/master:refs/remotes/origin/master &&
+	(cd testrepo &&
+		git reset --hard origin/master^
+		git config receive.denyCurrentBranch true) &&
+
+	echo >.git/foo  "To testrepo"  &&
+	echo >>.git/foo "!	refs/heads/master:refs/heads/master	[remote rejected] (branch is currently checked out)" &&
+
+	test_must_fail git push >.git/bar --porcelain  testrepo refs/heads/master:refs/heads/master &&
+	test_cmp .git/foo .git/bar
+'
+
+test_expect_success 'push --porcelain --dry-run rejected' '
+	mk_empty &&
+	git push testrepo refs/heads/master:refs/remotes/origin/master &&
+	(cd testrepo &&
+		git reset --hard origin/master
+		git config receive.denyCurrentBranch true) &&
+
+	echo >.git/foo  "To testrepo"  &&
+	echo >>.git/foo "!	refs/heads/master^:refs/heads/master	[rejected] (non-fast-forward)" &&
+	echo >>.git/foo "Done" &&
+
+	test_must_fail git push >.git/bar --porcelain  --dry-run testrepo refs/heads/master^:refs/heads/master &&
+	test_cmp .git/foo .git/bar
+'
+
+test_expect_success 'push --prune' '
+	mk_test heads/master heads/second heads/foo heads/bar &&
+	git push --prune testrepo &&
+	check_push_result $the_commit heads/master &&
+	check_push_result $the_first_commit heads/second &&
+	! check_push_result $the_first_commit heads/foo heads/bar
+'
+
+test_expect_success 'push --prune refspec' '
+	mk_test tmp/master tmp/second tmp/foo tmp/bar &&
+	git push --prune testrepo "refs/heads/*:refs/tmp/*" &&
+	check_push_result $the_commit tmp/master &&
+	check_push_result $the_first_commit tmp/second &&
+	! check_push_result $the_first_commit tmp/foo tmp/bar
+'
+
 test_done
diff --git a/t/t5517-push-mirror.sh b/t/t5517-push-mirror.sh
index ea49ded..e2ad260 100755
--- a/t/t5517-push-mirror.sh
+++ b/t/t5517-push-mirror.sh
@@ -19,7 +19,8 @@
 	mkdir mirror &&
 	(
 		cd mirror &&
-		git init
+		git init &&
+		git config receive.denyCurrentBranch warn
 	) &&
 	mkdir master &&
 	(
diff --git a/t/t5518-fetch-exit-status.sh b/t/t5518-fetch-exit-status.sh
index c6bc65f..c2060bb 100755
--- a/t/t5518-fetch-exit-status.sh
+++ b/t/t5518-fetch-exit-status.sh
@@ -22,7 +22,7 @@
 	git commit -a -m next
 '
 
-test_expect_success 'non fast forward fetch' '
+test_expect_success 'non-fast-forward fetch' '
 
 	test_must_fail git fetch . master:side
 
diff --git a/t/t5519-push-alternates.sh b/t/t5519-push-alternates.sh
index 96be523..c00c9b0 100755
--- a/t/t5519-push-alternates.sh
+++ b/t/t5519-push-alternates.sh
@@ -123,7 +123,7 @@
 	(
 		cd alice-pub &&
 		git cat-file commit master >../bob-work/commit
-	)
+	) &&
 	(
 		# This time Bob does not pull from Alice, and
 		# the master branch at her public repository points
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index e78d402..35304b4 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -4,6 +4,11 @@
 
 . ./test-lib.sh
 
+modify () {
+	sed -e "$1" <"$2" >"$2.x" &&
+	mv "$2.x" "$2"
+}
+
 D=`pwd`
 
 test_expect_success setup '
@@ -26,7 +31,7 @@
 test_expect_success 'checking the results' '
 	test -f file &&
 	test -f cloned/file &&
-	diff file cloned/file
+	test_cmp file cloned/file
 '
 
 test_expect_success 'pulling into void using master:master' '
@@ -41,6 +46,17 @@
 	test_cmp file cloned-uho/file
 '
 
+test_expect_success 'pulling into void does not overwrite untracked files' '
+	git init cloned-untracked &&
+	(
+		cd cloned-untracked &&
+		echo untracked >file &&
+		test_must_fail git pull .. master &&
+		echo untracked >expect &&
+		test_cmp expect file
+	)
+'
+
 test_expect_success 'test . as a remote' '
 
 	git branch copy master &&
@@ -78,16 +94,35 @@
 	test $(git rev-parse HEAD^) = $(git rev-parse copy) &&
 	test new = $(git show HEAD:file2)
 '
+test_expect_success 'pull.rebase' '
+	git reset --hard before-rebase &&
+	git config --bool pull.rebase true &&
+	test_when_finished "git config --unset pull.rebase" &&
+	git pull . copy &&
+	test $(git rev-parse HEAD^) = $(git rev-parse copy) &&
+	test new = $(git show HEAD:file2)
+'
 
 test_expect_success 'branch.to-rebase.rebase' '
 	git reset --hard before-rebase &&
-	git config branch.to-rebase.rebase 1 &&
+	git config --bool branch.to-rebase.rebase true &&
+	test_when_finished "git config --unset branch.to-rebase.rebase" &&
 	git pull . copy &&
-	git config branch.to-rebase.rebase 0 &&
 	test $(git rev-parse HEAD^) = $(git rev-parse copy) &&
 	test new = $(git show HEAD:file2)
 '
 
+test_expect_success 'branch.to-rebase.rebase should override pull.rebase' '
+	git reset --hard before-rebase &&
+	git config --bool pull.rebase true &&
+	test_when_finished "git config --unset pull.rebase" &&
+	git config --bool branch.to-rebase.rebase false &&
+	test_when_finished "git config --unset branch.to-rebase.rebase" &&
+	git pull . copy &&
+	test $(git rev-parse HEAD^) != $(git rev-parse copy) &&
+	test new = $(git show HEAD:file2)
+'
+
 test_expect_success '--rebase with rebased upstream' '
 
 	git remote add -f me . &&
@@ -149,4 +184,79 @@
 
 '
 
+test_expect_success 'pull --rebase works on branch yet to be born' '
+	git rev-parse master >expect &&
+	mkdir empty_repo &&
+	(cd empty_repo &&
+	 git init &&
+	 git pull --rebase .. master &&
+	 git rev-parse HEAD >../actual
+	) &&
+	test_cmp expect actual
+'
+
+test_expect_success 'setup for detecting upstreamed changes' '
+	mkdir src &&
+	(cd src &&
+	 git init &&
+	 printf "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n" > stuff &&
+	 git add stuff &&
+	 git commit -m "Initial revision"
+	) &&
+	git clone src dst &&
+	(cd src &&
+	 modify s/5/43/ stuff &&
+	 git commit -a -m "5->43" &&
+	 modify s/6/42/ stuff &&
+	 git commit -a -m "Make it bigger"
+	) &&
+	(cd dst &&
+	 modify s/5/43/ stuff &&
+	 git commit -a -m "Independent discovery of 5->43"
+	)
+'
+
+test_expect_success 'git pull --rebase detects upstreamed changes' '
+	(cd dst &&
+	 git pull --rebase &&
+	 test -z "$(git ls-files -u)"
+	)
+'
+
+test_expect_success 'setup for avoiding reapplying old patches' '
+	(cd dst &&
+	 test_might_fail git rebase --abort &&
+	 git reset --hard origin/master
+	) &&
+	git clone --bare src src-replace.git &&
+	rm -rf src &&
+	mv src-replace.git src &&
+	(cd dst &&
+	 modify s/2/22/ stuff &&
+	 git commit -a -m "Change 2" &&
+	 modify s/3/33/ stuff &&
+	 git commit -a -m "Change 3" &&
+	 modify s/4/44/ stuff &&
+	 git commit -a -m "Change 4" &&
+	 git push &&
+
+	 modify s/44/55/ stuff &&
+	 git commit --amend -a -m "Modified Change 4"
+	)
+'
+
+test_expect_success 'git pull --rebase does not reapply old patches' '
+	(cd dst &&
+	 test_must_fail git pull --rebase &&
+	 test 1 = $(find .git/rebase-apply -name "000*" | wc -l)
+	)
+'
+
+test_expect_success 'git pull --rebase against local branch' '
+	git checkout -b copy2 to-rebase-orig &&
+	git pull --rebase . to-rebase &&
+	test "conflicting modification" = "$(cat file)" &&
+	test file = "$(cat file2)"
+'
+
 test_done
diff --git a/t/t5521-pull-options.sh b/t/t5521-pull-options.sh
index 83e2e8a..1b06691 100755
--- a/t/t5521-pull-options.sh
+++ b/t/t5521-pull-options.sh
@@ -4,8 +4,6 @@
 
 . ./test-lib.sh
 
-D=`pwd`
-
 test_expect_success 'setup' '
 	mkdir parent &&
 	(cd parent && git init &&
@@ -13,48 +11,83 @@
 	 git commit -m one)
 '
 
-cd "$D"
-
 test_expect_success 'git pull -q' '
 	mkdir clonedq &&
-	cd clonedq &&
-	git pull -q "$D/parent" >out 2>err &&
-	test ! -s out
+	(cd clonedq && git init &&
+	git pull -q "../parent" >out 2>err &&
+	test ! -s err &&
+	test ! -s out)
 '
 
-cd "$D"
-
 test_expect_success 'git pull' '
 	mkdir cloned &&
-	cd cloned &&
-	git pull "$D/parent" >out 2>err &&
-	test -s out
+	(cd cloned && git init &&
+	git pull "../parent" >out 2>err &&
+	test -s err &&
+	test ! -s out)
 '
-cd "$D"
 
 test_expect_success 'git pull -v' '
 	mkdir clonedv &&
-	cd clonedv &&
-	git pull -v "$D/parent" >out 2>err &&
-	test -s out
+	(cd clonedv && git init &&
+	git pull -v "../parent" >out 2>err &&
+	test -s err &&
+	test ! -s out)
 '
 
-cd "$D"
-
 test_expect_success 'git pull -v -q' '
 	mkdir clonedvq &&
-	cd clonedvq &&
-	git pull -v -q "$D/parent" >out 2>err &&
-	test ! -s out
+	(cd clonedvq && git init &&
+	git pull -v -q "../parent" >out 2>err &&
+	test ! -s out &&
+	test ! -s err)
 '
 
-cd "$D"
-
 test_expect_success 'git pull -q -v' '
 	mkdir clonedqv &&
-	cd clonedqv &&
-	git pull -q -v "$D/parent" >out 2>err &&
-	test -s out
+	(cd clonedqv && git init &&
+	git pull -q -v "../parent" >out 2>err &&
+	test ! -s out &&
+	test -s err)
+'
+
+test_expect_success 'git pull --force' '
+	mkdir clonedoldstyle &&
+	(cd clonedoldstyle && git init &&
+	cat >>.git/config <<-\EOF &&
+	[remote "one"]
+		url = ../parent
+		fetch = refs/heads/master:refs/heads/mirror
+	[remote "two"]
+		url = ../parent
+		fetch = refs/heads/master:refs/heads/origin
+	[branch "master"]
+		remote = two
+		merge = refs/heads/master
+	EOF
+	git pull two &&
+	test_commit A &&
+	git branch -f origin &&
+	git pull --all --force
+	)
+'
+
+test_expect_success 'git pull --all' '
+	mkdir clonedmulti &&
+	(cd clonedmulti && git init &&
+	cat >>.git/config <<-\EOF &&
+	[remote "one"]
+		url = ../parent
+		fetch = refs/heads/*:refs/remotes/one/*
+	[remote "two"]
+		url = ../parent
+		fetch = refs/heads/*:refs/remotes/two/*
+	[branch "master"]
+		remote = one
+		merge = refs/heads/master
+	EOF
+	git pull --all
+	)
 '
 
 test_done
diff --git a/t/t5522-pull-symlink.sh b/t/t5522-pull-symlink.sh
index 86bbd7d..8e9b204 100755
--- a/t/t5522-pull-symlink.sh
+++ b/t/t5522-pull-symlink.sh
@@ -4,12 +4,6 @@
 
 . ./test-lib.sh
 
-if ! test_have_prereq SYMLINKS
-then
-	say 'Symbolic links not supported, skipping tests.'
-	test_done
-fi
-
 # The scenario we are building:
 #
 #   trash\ directory/
@@ -20,17 +14,23 @@
 #
 # The working directory is subdir-link.
 
-mkdir subdir
-echo file >subdir/file
-git add subdir/file
-git commit -q -m file
-git clone -q . clone-repo
-ln -s clone-repo/subdir/ subdir-link
-
+test_expect_success SYMLINKS setup '
+	mkdir subdir &&
+	echo file >subdir/file &&
+	git add subdir/file &&
+	git commit -q -m file &&
+	git clone -q . clone-repo &&
+	ln -s clone-repo/subdir/ subdir-link &&
+	(
+		cd clone-repo &&
+		git config receive.denyCurrentBranch warn
+	) &&
+	git config receive.denyCurrentBranch warn
+'
 
 # Demonstrate that things work if we just avoid the symlink
 #
-test_expect_success 'pulling from real subdir' '
+test_expect_success SYMLINKS 'pulling from real subdir' '
 	(
 		echo real >subdir/file &&
 		git commit -m real subdir/file &&
@@ -58,7 +58,7 @@
 # directory.  A POSIX shell's "cd" works a little differently
 # than chdir() in C; "cd -P" is much closer to chdir().
 #
-test_expect_success 'pulling from symlinked subdir' '
+test_expect_success SYMLINKS 'pulling from symlinked subdir' '
 	(
 		echo link >subdir/file &&
 		git commit -m link subdir/file &&
@@ -71,7 +71,7 @@
 # Prove that the remote end really is a repo, and other commands
 # work fine in this context.  It's just that "git pull" breaks.
 #
-test_expect_success 'pushing from symlinked subdir' '
+test_expect_success SYMLINKS 'pushing from symlinked subdir' '
 	(
 		cd subdir-link/ &&
 		echo push >file &&
diff --git a/t/t5523-push-upstream.sh b/t/t5523-push-upstream.sh
new file mode 100755
index 0000000..3683df1
--- /dev/null
+++ b/t/t5523-push-upstream.sh
@@ -0,0 +1,119 @@
+#!/bin/sh
+
+test_description='push with --set-upstream'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
+
+ensure_fresh_upstream() {
+	rm -rf parent && git init --bare parent
+}
+
+test_expect_success 'setup bare parent' '
+	ensure_fresh_upstream &&
+	git remote add upstream parent
+'
+
+test_expect_success 'setup local commit' '
+	echo content >file &&
+	git add file &&
+	git commit -m one
+'
+
+check_config() {
+	(echo $2; echo $3) >expect.$1
+	(git config branch.$1.remote
+	 git config branch.$1.merge) >actual.$1
+	test_cmp expect.$1 actual.$1
+}
+
+test_expect_success 'push -u master:master' '
+	git push -u upstream master:master &&
+	check_config master upstream refs/heads/master
+'
+
+test_expect_success 'push -u master:other' '
+	git push -u upstream master:other &&
+	check_config master upstream refs/heads/other
+'
+
+test_expect_success 'push -u --dry-run master:otherX' '
+	git push -u --dry-run upstream master:otherX &&
+	check_config master upstream refs/heads/other
+'
+
+test_expect_success 'push -u master2:master2' '
+	git branch master2 &&
+	git push -u upstream master2:master2 &&
+	check_config master2 upstream refs/heads/master2
+'
+
+test_expect_success 'push -u master2:other2' '
+	git push -u upstream master2:other2 &&
+	check_config master2 upstream refs/heads/other2
+'
+
+test_expect_success 'push -u :master2' '
+	git push -u upstream :master2 &&
+	check_config master2 upstream refs/heads/other2
+'
+
+test_expect_success 'push -u --all' '
+	git branch all1 &&
+	git branch all2 &&
+	git push -u --all &&
+	check_config all1 upstream refs/heads/all1 &&
+	check_config all2 upstream refs/heads/all2
+'
+
+test_expect_success 'push -u HEAD' '
+	git checkout -b headbranch &&
+	git push -u upstream HEAD &&
+	check_config headbranch upstream refs/heads/headbranch
+'
+
+test_expect_success TTY 'progress messages go to tty' '
+	ensure_fresh_upstream &&
+
+	test_terminal git push -u upstream master >out 2>err &&
+	grep "Writing objects" err
+'
+
+test_expect_success 'progress messages do not go to non-tty' '
+	ensure_fresh_upstream &&
+
+	# skip progress messages, since stderr is non-tty
+	git push -u upstream master >out 2>err &&
+	! grep "Writing objects" err
+'
+
+test_expect_success 'progress messages go to non-tty (forced)' '
+	ensure_fresh_upstream &&
+
+	# force progress messages to stderr, even though it is non-tty
+	git push -u --progress upstream master >out 2>err &&
+	grep "Writing objects" err
+'
+
+test_expect_success TTY 'push -q suppresses progress' '
+	ensure_fresh_upstream &&
+
+	test_terminal git push -u -q upstream master >out 2>err &&
+	! grep "Writing objects" err
+'
+
+test_expect_success TTY 'push --no-progress suppresses progress' '
+	ensure_fresh_upstream &&
+
+	test_terminal git push -u --no-progress upstream master >out 2>err &&
+	! grep "Unpacking objects" err &&
+	! grep "Writing objects" err
+'
+
+test_expect_success TTY 'quiet push' '
+	ensure_fresh_upstream &&
+
+	test_terminal git push --quiet --no-progress upstream master 2>&1 | tee output &&
+	test_cmp /dev/null output
+'
+
+test_done
diff --git a/t/t5524-pull-msg.sh b/t/t5524-pull-msg.sh
new file mode 100755
index 0000000..8cccecc
--- /dev/null
+++ b/t/t5524-pull-msg.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+test_description='git pull message generation'
+
+. ./test-lib.sh
+
+dollar='$Dollar'
+
+test_expect_success setup '
+	test_commit initial afile original &&
+	git clone . cloned &&
+	(
+		cd cloned &&
+		echo added >bfile &&
+		git add bfile &&
+		test_tick &&
+		git commit -m "add bfile"
+	) &&
+	test_tick && test_tick &&
+	echo "original $dollar" >afile &&
+	git add afile &&
+	git commit -m "do not clobber $dollar signs"
+'
+
+test_expect_success pull '
+(
+	cd cloned &&
+	git pull --log &&
+	git log -2 &&
+	git cat-file commit HEAD >result &&
+	grep Dollar result
+)
+'
+
+test_done
diff --git a/t/t5525-fetch-tagopt.sh b/t/t5525-fetch-tagopt.sh
new file mode 100755
index 0000000..4fbf7a1
--- /dev/null
+++ b/t/t5525-fetch-tagopt.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+test_description='tagopt variable affects "git fetch" and is overridden by commandline.'
+
+. ./test-lib.sh
+
+setup_clone () {
+	git clone --mirror . $1 &&
+	git remote add remote_$1 $1 &&
+	(cd $1 &&
+	git tag tag_$1)
+}
+
+test_expect_success setup '
+	test_commit test &&
+	setup_clone one &&
+	git config remote.remote_one.tagopt --no-tags &&
+	setup_clone two &&
+	git config remote.remote_two.tagopt --tags
+	'
+
+test_expect_success "fetch with tagopt=--no-tags does not get tag" '
+	git fetch remote_one &&
+	test_must_fail git show-ref tag_one
+	'
+
+test_expect_success "fetch --tags with tagopt=--no-tags gets tag" '
+	git fetch --tags remote_one &&
+	git show-ref tag_one
+	'
+
+test_expect_success "fetch --no-tags with tagopt=--tags does not get tag" '
+	git fetch --no-tags remote_two &&
+	test_must_fail git show-ref tag_two
+	'
+
+test_expect_success "fetch with tagopt=--tags gets tag" '
+	git fetch remote_two &&
+	git show-ref tag_two
+	'
+test_done
diff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh
new file mode 100755
index 0000000..ca5b027
--- /dev/null
+++ b/t/t5526-fetch-submodules.sh
@@ -0,0 +1,453 @@
+#!/bin/sh
+# Copyright (c) 2010, Jens Lehmann
+
+test_description='Recursive "git fetch" for submodules'
+
+. ./test-lib.sh
+
+pwd=$(pwd)
+
+add_upstream_commit() {
+	(
+		cd submodule &&
+		head1=$(git rev-parse --short HEAD) &&
+		echo new >> subfile &&
+		test_tick &&
+		git add subfile &&
+		git commit -m new subfile &&
+		head2=$(git rev-parse --short HEAD) &&
+		echo "From $pwd/submodule" > ../expect.err &&
+		echo "   $head1..$head2  master     -> origin/master" >> ../expect.err
+	) &&
+	(
+		cd deepsubmodule &&
+		head1=$(git rev-parse --short HEAD) &&
+		echo new >> deepsubfile &&
+		test_tick &&
+		git add deepsubfile &&
+		git commit -m new deepsubfile &&
+		head2=$(git rev-parse --short HEAD) &&
+		echo "From $pwd/deepsubmodule" >> ../expect.err &&
+		echo "   $head1..$head2  master     -> origin/master" >> ../expect.err
+	)
+}
+
+test_expect_success setup '
+	mkdir deepsubmodule &&
+	(
+		cd deepsubmodule &&
+		git init &&
+		echo deepsubcontent > deepsubfile &&
+		git add deepsubfile &&
+		git commit -m new deepsubfile
+	) &&
+	mkdir submodule &&
+	(
+		cd submodule &&
+		git init &&
+		echo subcontent > subfile &&
+		git add subfile &&
+		git submodule add "$pwd/deepsubmodule" subdir/deepsubmodule &&
+		git commit -a -m new
+	) &&
+	git submodule add "$pwd/submodule" submodule &&
+	git commit -am initial &&
+	git clone . downstream &&
+	(
+		cd downstream &&
+		git submodule update --init --recursive
+	) &&
+	echo "Fetching submodule submodule" > expect.out &&
+	echo "Fetching submodule submodule/subdir/deepsubmodule" >> expect.out
+'
+
+test_expect_success "fetch --recurse-submodules recurses into submodules" '
+	add_upstream_commit &&
+	(
+		cd downstream &&
+		git fetch --recurse-submodules >../actual.out 2>../actual.err
+	) &&
+	test_i18ncmp expect.out actual.out &&
+	test_i18ncmp expect.err actual.err
+'
+
+test_expect_success "fetch alone only fetches superproject" '
+	add_upstream_commit &&
+	(
+		cd downstream &&
+		git fetch >../actual.out 2>../actual.err
+	) &&
+	! test -s actual.out &&
+	! test -s actual.err
+'
+
+test_expect_success "fetch --no-recurse-submodules only fetches superproject" '
+	(
+		cd downstream &&
+		git fetch --no-recurse-submodules >../actual.out 2>../actual.err
+	) &&
+	! test -s actual.out &&
+	! test -s actual.err
+'
+
+test_expect_success "using fetchRecurseSubmodules=true in .gitmodules recurses into submodules" '
+	(
+		cd downstream &&
+		git config -f .gitmodules submodule.submodule.fetchRecurseSubmodules true &&
+		git fetch >../actual.out 2>../actual.err
+	) &&
+	test_i18ncmp expect.out actual.out &&
+	test_i18ncmp expect.err actual.err
+'
+
+test_expect_success "--no-recurse-submodules overrides .gitmodules config" '
+	add_upstream_commit &&
+	(
+		cd downstream &&
+		git fetch --no-recurse-submodules >../actual.out 2>../actual.err
+	) &&
+	! test -s actual.out &&
+	! test -s actual.err
+'
+
+test_expect_success "using fetchRecurseSubmodules=false in .git/config overrides setting in .gitmodules" '
+	(
+		cd downstream &&
+		git config submodule.submodule.fetchRecurseSubmodules false &&
+		git fetch >../actual.out 2>../actual.err
+	) &&
+	! test -s actual.out &&
+	! test -s actual.err
+'
+
+test_expect_success "--recurse-submodules overrides fetchRecurseSubmodules setting from .git/config" '
+	(
+		cd downstream &&
+		git fetch --recurse-submodules >../actual.out 2>../actual.err &&
+		git config --unset -f .gitmodules submodule.submodule.fetchRecurseSubmodules &&
+		git config --unset submodule.submodule.fetchRecurseSubmodules
+	) &&
+	test_i18ncmp expect.out actual.out &&
+	test_i18ncmp expect.err actual.err
+'
+
+test_expect_success "--quiet propagates to submodules" '
+	(
+		cd downstream &&
+		git fetch --recurse-submodules --quiet >../actual.out 2>../actual.err
+	) &&
+	! test -s actual.out &&
+	! test -s actual.err
+'
+
+test_expect_success "--dry-run propagates to submodules" '
+	add_upstream_commit &&
+	(
+		cd downstream &&
+		git fetch --recurse-submodules --dry-run >../actual.out 2>../actual.err
+	) &&
+	test_i18ncmp expect.out actual.out &&
+	test_i18ncmp expect.err actual.err
+'
+
+test_expect_success "Without --dry-run propagates to submodules" '
+	(
+		cd downstream &&
+		git fetch --recurse-submodules >../actual.out 2>../actual.err
+	) &&
+	test_i18ncmp expect.out actual.out &&
+	test_i18ncmp expect.err actual.err
+'
+
+test_expect_success "recurseSubmodules=true propagates into submodules" '
+	add_upstream_commit &&
+	(
+		cd downstream &&
+		git config fetch.recurseSubmodules true
+		git fetch >../actual.out 2>../actual.err
+	) &&
+	test_i18ncmp expect.out actual.out &&
+	test_i18ncmp expect.err actual.err
+'
+
+test_expect_success "--recurse-submodules overrides config in submodule" '
+	add_upstream_commit &&
+	(
+		cd downstream &&
+		(
+			cd submodule &&
+			git config fetch.recurseSubmodules false
+		) &&
+		git fetch --recurse-submodules >../actual.out 2>../actual.err
+	) &&
+	test_i18ncmp expect.out actual.out &&
+	test_i18ncmp expect.err actual.err
+'
+
+test_expect_success "--no-recurse-submodules overrides config setting" '
+	add_upstream_commit &&
+	(
+		cd downstream &&
+		git config fetch.recurseSubmodules true
+		git fetch --no-recurse-submodules >../actual.out 2>../actual.err
+	) &&
+	! test -s actual.out &&
+	! test -s actual.err
+'
+
+test_expect_success "Recursion doesn't happen when no new commits are fetched in the superproject" '
+	(
+		cd downstream &&
+		(
+			cd submodule &&
+			git config --unset fetch.recurseSubmodules
+		) &&
+		git config --unset fetch.recurseSubmodules
+		git fetch >../actual.out 2>../actual.err
+	) &&
+	! test -s actual.out &&
+	! test -s actual.err
+'
+
+test_expect_success "Recursion stops when no new submodule commits are fetched" '
+	head1=$(git rev-parse --short HEAD) &&
+	git add submodule &&
+	git commit -m "new submodule" &&
+	head2=$(git rev-parse --short HEAD) &&
+	echo "Fetching submodule submodule" > expect.out.sub &&
+	echo "From $pwd/." > expect.err.sub &&
+	echo "   $head1..$head2  master     -> origin/master" >> expect.err.sub
+	head -2 expect.err >> expect.err.sub &&
+	(
+		cd downstream &&
+		git fetch >../actual.out 2>../actual.err
+	) &&
+	test_i18ncmp expect.err.sub actual.err &&
+	test_i18ncmp expect.out.sub actual.out
+'
+
+test_expect_success "Recursion doesn't happen when new superproject commits don't change any submodules" '
+	add_upstream_commit &&
+	head1=$(git rev-parse --short HEAD) &&
+	echo a > file &&
+	git add file &&
+	git commit -m "new file" &&
+	head2=$(git rev-parse --short HEAD) &&
+	echo "From $pwd/." > expect.err.file &&
+	echo "   $head1..$head2  master     -> origin/master" >> expect.err.file &&
+	(
+		cd downstream &&
+		git fetch >../actual.out 2>../actual.err
+	) &&
+	! test -s actual.out &&
+	test_i18ncmp expect.err.file actual.err
+'
+
+test_expect_success "Recursion picks up config in submodule" '
+	(
+		cd downstream &&
+		git fetch --recurse-submodules &&
+		(
+			cd submodule &&
+			git config fetch.recurseSubmodules true
+		)
+	) &&
+	add_upstream_commit &&
+	head1=$(git rev-parse --short HEAD) &&
+	git add submodule &&
+	git commit -m "new submodule" &&
+	head2=$(git rev-parse --short HEAD) &&
+	echo "From $pwd/." > expect.err.sub &&
+	echo "   $head1..$head2  master     -> origin/master" >> expect.err.sub &&
+	cat expect.err >> expect.err.sub &&
+	(
+		cd downstream &&
+		git fetch >../actual.out 2>../actual.err &&
+		(
+			cd submodule &&
+			git config --unset fetch.recurseSubmodules
+		)
+	) &&
+	test_i18ncmp expect.err.sub actual.err &&
+	test_i18ncmp expect.out actual.out
+'
+
+test_expect_success "Recursion picks up all submodules when necessary" '
+	add_upstream_commit &&
+	(
+		cd submodule &&
+		(
+			cd subdir/deepsubmodule &&
+			git fetch &&
+			git checkout -q FETCH_HEAD
+		) &&
+		head1=$(git rev-parse --short HEAD^) &&
+		git add subdir/deepsubmodule &&
+		git commit -m "new deepsubmodule"
+		head2=$(git rev-parse --short HEAD) &&
+		echo "From $pwd/submodule" > ../expect.err.sub &&
+		echo "   $head1..$head2  master     -> origin/master" >> ../expect.err.sub
+	) &&
+	head1=$(git rev-parse --short HEAD) &&
+	git add submodule &&
+	git commit -m "new submodule" &&
+	head2=$(git rev-parse --short HEAD) &&
+	echo "From $pwd/." > expect.err.2 &&
+	echo "   $head1..$head2  master     -> origin/master" >> expect.err.2 &&
+	cat expect.err.sub >> expect.err.2 &&
+	tail -2 expect.err >> expect.err.2 &&
+	(
+		cd downstream &&
+		git fetch >../actual.out 2>../actual.err
+	) &&
+	test_i18ncmp expect.err.2 actual.err &&
+	test_i18ncmp expect.out actual.out
+'
+
+test_expect_success "'--recurse-submodules=on-demand' doesn't recurse when no new commits are fetched in the superproject (and ignores config)" '
+	add_upstream_commit &&
+	(
+		cd submodule &&
+		(
+			cd subdir/deepsubmodule &&
+			git fetch &&
+			git checkout -q FETCH_HEAD
+		) &&
+		head1=$(git rev-parse --short HEAD^) &&
+		git add subdir/deepsubmodule &&
+		git commit -m "new deepsubmodule"
+		head2=$(git rev-parse --short HEAD) &&
+		echo "From $pwd/submodule" > ../expect.err.sub &&
+		echo "   $head1..$head2  master     -> origin/master" >> ../expect.err.sub
+	) &&
+	(
+		cd downstream &&
+		git config fetch.recurseSubmodules true &&
+		git fetch --recurse-submodules=on-demand >../actual.out 2>../actual.err &&
+		git config --unset fetch.recurseSubmodules
+	) &&
+	! test -s actual.out &&
+	! test -s actual.err
+'
+
+test_expect_success "'--recurse-submodules=on-demand' recurses as deep as necessary (and ignores config)" '
+	head1=$(git rev-parse --short HEAD) &&
+	git add submodule &&
+	git commit -m "new submodule" &&
+	head2=$(git rev-parse --short HEAD) &&
+	tail -2 expect.err > expect.err.deepsub &&
+	echo "From $pwd/." > expect.err &&
+	echo "   $head1..$head2  master     -> origin/master" >> expect.err
+	cat expect.err.sub >> expect.err &&
+	cat expect.err.deepsub >> expect.err &&
+	(
+		cd downstream &&
+		git config fetch.recurseSubmodules false &&
+		(
+			cd submodule &&
+			git config -f .gitmodules submodule.subdir/deepsubmodule.fetchRecursive false
+		) &&
+		git fetch --recurse-submodules=on-demand >../actual.out 2>../actual.err &&
+		git config --unset fetch.recurseSubmodules
+		(
+			cd submodule &&
+			git config --unset -f .gitmodules submodule.subdir/deepsubmodule.fetchRecursive
+		)
+	) &&
+	test_i18ncmp expect.out actual.out &&
+	test_i18ncmp expect.err actual.err
+'
+
+test_expect_success "'--recurse-submodules=on-demand' stops when no new submodule commits are found in the superproject (and ignores config)" '
+	add_upstream_commit &&
+	head1=$(git rev-parse --short HEAD) &&
+	echo a >> file &&
+	git add file &&
+	git commit -m "new file" &&
+	head2=$(git rev-parse --short HEAD) &&
+	echo "From $pwd/." > expect.err.file &&
+	echo "   $head1..$head2  master     -> origin/master" >> expect.err.file &&
+	(
+		cd downstream &&
+		git fetch --recurse-submodules=on-demand >../actual.out 2>../actual.err
+	) &&
+	! test -s actual.out &&
+	test_i18ncmp expect.err.file actual.err
+'
+
+test_expect_success "'fetch.recurseSubmodules=on-demand' overrides global config" '
+	(
+		cd downstream &&
+		git fetch --recurse-submodules
+	) &&
+	add_upstream_commit &&
+	git config --global fetch.recurseSubmodules false &&
+	head1=$(git rev-parse --short HEAD) &&
+	git add submodule &&
+	git commit -m "new submodule" &&
+	head2=$(git rev-parse --short HEAD) &&
+	echo "From $pwd/." > expect.err.2 &&
+	echo "   $head1..$head2  master     -> origin/master" >> expect.err.2
+	head -2 expect.err >> expect.err.2 &&
+	(
+		cd downstream &&
+		git config fetch.recurseSubmodules on-demand &&
+		git fetch >../actual.out 2>../actual.err
+	) &&
+	git config --global --unset fetch.recurseSubmodules &&
+	(
+		cd downstream &&
+		git config --unset fetch.recurseSubmodules
+	) &&
+	test_i18ncmp expect.out.sub actual.out &&
+	test_i18ncmp expect.err.2 actual.err
+'
+
+test_expect_success "'submodule.<sub>.fetchRecurseSubmodules=on-demand' overrides fetch.recurseSubmodules" '
+	(
+		cd downstream &&
+		git fetch --recurse-submodules
+	) &&
+	add_upstream_commit &&
+	git config fetch.recurseSubmodules false &&
+	head1=$(git rev-parse --short HEAD) &&
+	git add submodule &&
+	git commit -m "new submodule" &&
+	head2=$(git rev-parse --short HEAD) &&
+	echo "From $pwd/." > expect.err.2 &&
+	echo "   $head1..$head2  master     -> origin/master" >> expect.err.2
+	head -2 expect.err >> expect.err.2 &&
+	(
+		cd downstream &&
+		git config submodule.submodule.fetchRecurseSubmodules on-demand &&
+		git fetch >../actual.out 2>../actual.err
+	) &&
+	git config --unset fetch.recurseSubmodules &&
+	(
+		cd downstream &&
+		git config --unset submodule.submodule.fetchRecurseSubmodules
+	) &&
+	test_i18ncmp expect.out.sub actual.out &&
+	test_i18ncmp expect.err.2 actual.err
+'
+
+test_expect_success "don't fetch submodule when newly recorded commits are already present" '
+	(
+		cd submodule &&
+		git checkout -q HEAD^^
+	) &&
+	head1=$(git rev-parse --short HEAD) &&
+	git add submodule &&
+	git commit -m "submodule rewound" &&
+	head2=$(git rev-parse --short HEAD) &&
+	echo "From $pwd/." > expect.err &&
+	echo "   $head1..$head2  master     -> origin/master" >> expect.err &&
+	(
+		cd downstream &&
+		git fetch >../actual.out 2>../actual.err
+	) &&
+	! test -s actual.out &&
+	test_i18ncmp expect.err actual.err
+'
+
+test_done
diff --git a/t/t5527-fetch-odd-refs.sh b/t/t5527-fetch-odd-refs.sh
new file mode 100755
index 0000000..edea9f9
--- /dev/null
+++ b/t/t5527-fetch-odd-refs.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+test_description='test fetching of oddly-named refs'
+. ./test-lib.sh
+
+# afterwards we will have:
+#  HEAD - two
+#  refs/for/refs/heads/master - one
+#  refs/heads/master - three
+test_expect_success 'setup repo with odd suffix ref' '
+	echo content >file &&
+	git add . &&
+	git commit -m one &&
+	git update-ref refs/for/refs/heads/master HEAD &&
+	echo content >>file &&
+	git commit -a -m two &&
+	echo content >>file &&
+	git commit -a -m three &&
+	git checkout HEAD^
+'
+
+test_expect_success 'suffix ref is ignored during fetch' '
+	git clone --bare file://"$PWD" suffix &&
+	echo three >expect &&
+	git --git-dir=suffix log -1 --format=%s refs/heads/master >actual &&
+	test_cmp expect actual
+'
+
+test_done
diff --git a/t/t5528-push-default.sh b/t/t5528-push-default.sh
new file mode 100755
index 0000000..4736da8
--- /dev/null
+++ b/t/t5528-push-default.sh
@@ -0,0 +1,118 @@
+#!/bin/sh
+
+test_description='check various push.default settings'
+. ./test-lib.sh
+
+test_expect_success 'setup bare remotes' '
+	git init --bare repo1 &&
+	git remote add parent1 repo1 &&
+	git init --bare repo2 &&
+	git remote add parent2 repo2 &&
+	test_commit one &&
+	git push parent1 HEAD &&
+	git push parent2 HEAD
+'
+
+# $1 = local revision
+# $2 = remote revision (tested to be equal to the local one)
+check_pushed_commit () {
+	git log -1 --format='%h %s' "$1" >expect &&
+	git --git-dir=repo1 log -1 --format='%h %s' "$2" >actual &&
+	test_cmp expect actual
+}
+
+# $1 = push.default value
+# $2 = expected target branch for the push
+test_push_success () {
+	git -c push.default="$1" push &&
+	check_pushed_commit HEAD "$2"
+}
+
+# $1 = push.default value
+# check that push fails and does not modify any remote branch
+test_push_failure () {
+	git --git-dir=repo1 log --no-walk --format='%h %s' --all >expect &&
+	test_must_fail git -c push.default="$1" push &&
+	git --git-dir=repo1 log --no-walk --format='%h %s' --all >actual &&
+	test_cmp expect actual
+}
+
+test_expect_success '"upstream" pushes to configured upstream' '
+	git checkout master &&
+	test_config branch.master.remote parent1 &&
+	test_config branch.master.merge refs/heads/foo &&
+	test_commit two &&
+	test_push_success upstream foo
+'
+
+test_expect_success '"upstream" does not push on unconfigured remote' '
+	git checkout master &&
+	test_unconfig branch.master.remote &&
+	test_config push.default upstream &&
+	test_commit three &&
+	test_push_failure upstream
+'
+
+test_expect_success '"upstream" does not push on unconfigured branch' '
+	git checkout master &&
+	test_config branch.master.remote parent1 &&
+	test_unconfig branch.master.merge &&
+	test_config push.default upstream
+	test_commit four &&
+	test_push_failure upstream
+'
+
+test_expect_success '"upstream" does not push when remotes do not match' '
+	git checkout master &&
+	test_config branch.master.remote parent1 &&
+	test_config branch.master.merge refs/heads/foo &&
+	test_config push.default upstream &&
+	test_commit five &&
+	test_must_fail git push parent2
+'
+
+test_expect_success 'push from/to new branch with upstream, matching and simple' '
+	git checkout -b new-branch &&
+	test_push_failure simple &&
+	test_push_failure matching &&
+	test_push_failure upstream
+'
+
+test_expect_success 'push from/to new branch with current creates remote branch' '
+	test_config branch.new-branch.remote repo1 &&
+	git checkout new-branch &&
+	test_push_success current new-branch
+'
+
+test_expect_success 'push to existing branch, with no upstream configured' '
+	test_config branch.master.remote repo1 &&
+	git checkout master &&
+	test_push_failure simple &&
+	test_push_failure upstream
+'
+
+test_expect_success 'push to existing branch, upstream configured with same name' '
+	test_config branch.master.remote repo1 &&
+	test_config branch.master.merge refs/heads/master &&
+	git checkout master &&
+	test_commit six &&
+	test_push_success upstream master &&
+	test_commit seven &&
+	test_push_success simple master
+'
+
+test_expect_success 'push to existing branch, upstream configured with different name' '
+	test_config branch.master.remote repo1 &&
+	test_config branch.master.merge refs/heads/other-name &&
+	git checkout master &&
+	test_commit eight &&
+	test_push_success upstream other-name &&
+	test_commit nine &&
+	test_push_failure simple &&
+	git --git-dir=repo1 log -1 --format="%h %s" "other-name" >expect-other-name &&
+	test_push_success current master &&
+	git --git-dir=repo1 log -1 --format="%h %s" "other-name" >actual-other-name &&
+	test_cmp expect-other-name actual-other-name
+'
+
+test_done
diff --git a/t/t5530-upload-pack-error.sh b/t/t5530-upload-pack-error.sh
index 757cc19..6b2a5f4 100755
--- a/t/t5530-upload-pack-error.sh
+++ b/t/t5530-upload-pack-error.sh
@@ -32,9 +32,9 @@
 
 test_expect_success 'upload-pack fails due to error in pack-objects packing' '
 
-	! echo "0032want $(git rev-parse HEAD)
-00000009done
-0000" | git upload-pack . > /dev/null 2> output.err &&
+	printf "0032want %s\n00000009done\n0000" \
+		$(git rev-parse HEAD) >input &&
+	test_must_fail git upload-pack . <input >/dev/null 2>output.err &&
 	grep "unable to read" output.err &&
 	grep "pack-objects died" output.err
 '
@@ -51,17 +51,29 @@
 '
 test_expect_success 'upload-pack fails due to error in rev-list' '
 
-	! echo "0032want $(git rev-parse HEAD)
-0034shallow $(git rev-parse HEAD^)00000009done
-0000" | git upload-pack . > /dev/null 2> output.err &&
-	grep "waitpid (async) failed" output.err
+	printf "0032want %s\n0034shallow %s00000009done\n0000" \
+		$(git rev-parse HEAD) $(git rev-parse HEAD^) >input &&
+	test_must_fail git upload-pack . <input >/dev/null 2>output.err &&
+	# pack-objects survived
+	grep "Total.*, reused" output.err &&
+	# but there was an error, which must have been in rev-list
+	grep "bad tree object" output.err
+'
+
+test_expect_success 'upload-pack error message when bad ref requested' '
+
+	printf "0045want %s multi_ack_detailed\n00000009done\n0000" \
+		"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef" >input &&
+	test_must_fail git upload-pack . <input >output 2>output.err &&
+	grep -q "not our ref" output.err &&
+	! grep -q multi_ack_detailed output.err
 '
 
 test_expect_success 'upload-pack fails due to error in pack-objects enumeration' '
 
-	! echo "0032want $(git rev-parse HEAD)
-00000009done
-0000" | git upload-pack . > /dev/null 2> output.err &&
+	printf "0032want %s\n00000009done\n0000" \
+		$(git rev-parse HEAD) >input &&
+	test_must_fail git upload-pack . <input >/dev/null 2>output.err &&
 	grep "bad tree object" output.err &&
 	grep "pack-objects died" output.err
 '
diff --git a/t/t5531-deep-submodule-push.sh b/t/t5531-deep-submodule-push.sh
new file mode 100755
index 0000000..1947c28
--- /dev/null
+++ b/t/t5531-deep-submodule-push.sh
@@ -0,0 +1,216 @@
+#!/bin/sh
+
+test_description='unpack-objects'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+	mkdir pub.git &&
+	GIT_DIR=pub.git git init --bare &&
+	GIT_DIR=pub.git git config receive.fsckobjects true &&
+	mkdir work &&
+	(
+		cd work &&
+		git init &&
+		mkdir -p gar/bage &&
+		(
+			cd gar/bage &&
+			git init &&
+			>junk &&
+			git add junk &&
+			git commit -m "Initial junk"
+		) &&
+		git add gar/bage &&
+		git commit -m "Initial superproject"
+	)
+'
+
+test_expect_success push '
+	(
+		cd work &&
+		git push ../pub.git master
+	)
+'
+
+test_expect_success 'push if submodule has no remote' '
+	(
+		cd work/gar/bage &&
+		>junk2 &&
+		git add junk2 &&
+		git commit -m "Second junk"
+	) &&
+	(
+		cd work &&
+		git add gar/bage &&
+		git commit -m "Second commit for gar/bage" &&
+		git push --recurse-submodules=check ../pub.git master
+	)
+'
+
+test_expect_success 'push fails if submodule commit not on remote' '
+	(
+		cd work/gar &&
+		git clone --bare bage ../../submodule.git &&
+		cd bage &&
+		git remote add origin ../../../submodule.git &&
+		git fetch &&
+		>junk3 &&
+		git add junk3 &&
+		git commit -m "Third junk"
+	) &&
+	(
+		cd work &&
+		git add gar/bage &&
+		git commit -m "Third commit for gar/bage" &&
+		test_must_fail git push --recurse-submodules=check ../pub.git master
+	)
+'
+
+test_expect_success 'push succeeds after commit was pushed to remote' '
+	(
+		cd work/gar/bage &&
+		git push origin master
+	) &&
+	(
+		cd work &&
+		git push --recurse-submodules=check ../pub.git master
+	)
+'
+
+test_expect_success 'push fails when commit on multiple branches if one branch has no remote' '
+	(
+		cd work/gar/bage &&
+		>junk4 &&
+		git add junk4 &&
+		git commit -m "Fourth junk"
+	) &&
+	(
+		cd work &&
+		git branch branch2 &&
+		git add gar/bage &&
+		git commit -m "Fourth commit for gar/bage" &&
+		git checkout branch2 &&
+		(
+			cd gar/bage &&
+			git checkout HEAD~1
+		) &&
+		>junk1 &&
+		git add junk1 &&
+		git commit -m "First junk" &&
+		test_must_fail git push --recurse-submodules=check ../pub.git
+	)
+'
+
+test_expect_success 'push succeeds if submodule has no remote and is on the first superproject commit' '
+	git init --bare a
+	git clone a a1 &&
+	(
+		cd a1 &&
+		git init b
+		(
+			cd b &&
+			>junk &&
+			git add junk &&
+			git commit -m "initial"
+		) &&
+		git add b &&
+		git commit -m "added submodule" &&
+		git push --recurse-submodule=check origin master
+	)
+'
+
+test_expect_success 'push unpushed submodules when not needed' '
+	(
+		cd work &&
+		(
+			cd gar/bage &&
+			git checkout master &&
+			>junk5 &&
+			git add junk5 &&
+			git commit -m "Fifth junk" &&
+			git push &&
+			git rev-parse origin/master >../../../expected
+		) &&
+		git checkout master &&
+		git add gar/bage &&
+		git commit -m "Fifth commit for gar/bage" &&
+		git push --recurse-submodules=on-demand ../pub.git master
+	) &&
+	(
+		cd submodule.git &&
+		git rev-parse master >../actual
+	) &&
+	test_cmp expected actual
+'
+
+test_expect_success 'push unpushed submodules when not needed 2' '
+	(
+		cd submodule.git &&
+		git rev-parse master >../expected
+	) &&
+	(
+		cd work &&
+		(
+			cd gar/bage &&
+			>junk6 &&
+			git add junk6 &&
+			git commit -m "Sixth junk"
+		) &&
+		>junk2 &&
+		git add junk2 &&
+		git commit -m "Second junk for work" &&
+		git push --recurse-submodules=on-demand ../pub.git master
+	) &&
+	(
+		cd submodule.git &&
+		git rev-parse master >../actual
+	) &&
+	test_cmp expected actual
+'
+
+test_expect_success 'push unpushed submodules recursively' '
+	(
+		cd work &&
+		(
+			cd gar/bage &&
+			git checkout master &&
+			> junk7 &&
+			git add junk7 &&
+			git commit -m "Seventh junk" &&
+			git rev-parse master >../../../expected
+		) &&
+		git checkout master &&
+		git add gar/bage &&
+		git commit -m "Seventh commit for gar/bage" &&
+		git push --recurse-submodules=on-demand ../pub.git master
+	) &&
+	(
+		cd submodule.git &&
+		git rev-parse master >../actual
+	) &&
+	test_cmp expected actual
+'
+
+test_expect_success 'push unpushable submodule recursively fails' '
+	(
+		cd work &&
+		(
+			cd gar/bage &&
+			git rev-parse origin/master >../../../expected &&
+			git checkout master~0 &&
+			> junk8 &&
+			git add junk8 &&
+			git commit -m "Eighth junk"
+		) &&
+		git add gar/bage &&
+		git commit -m "Eighth commit for gar/bage" &&
+		test_must_fail git push --recurse-submodules=on-demand ../pub.git master
+	) &&
+	(
+		cd submodule.git &&
+		git rev-parse master >../actual
+	) &&
+	test_cmp expected actual
+'
+
+test_done
diff --git a/t/t5532-fetch-proxy.sh b/t/t5532-fetch-proxy.sh
new file mode 100755
index 0000000..5531bd1
--- /dev/null
+++ b/t/t5532-fetch-proxy.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+test_description='fetching via git:// using core.gitproxy'
+. ./test-lib.sh
+
+test_expect_success 'setup remote repo' '
+	git init remote &&
+	(cd remote &&
+	 echo content >file &&
+	 git add file &&
+	 git commit -m one
+	)
+'
+
+cat >proxy <<'EOF'
+#!/bin/sh
+echo >&2 "proxying for $*"
+cmd=`"$PERL_PATH" -e '
+	read(STDIN, $buf, 4);
+	my $n = hex($buf) - 4;
+	read(STDIN, $buf, $n);
+	my ($cmd, $other) = split /\0/, $buf;
+	# drop absolute-path on repo name
+	$cmd =~ s{ /}{ };
+	print $cmd;
+'`
+echo >&2 "Running '$cmd'"
+exec $cmd
+EOF
+chmod +x proxy
+test_expect_success 'setup local repo' '
+	git remote add fake git://example.com/remote &&
+	git config core.gitproxy ./proxy
+'
+
+test_expect_success 'fetch through proxy works' '
+	git fetch fake &&
+	echo one >expect &&
+	git log -1 --format=%s FETCH_HEAD >actual &&
+	test_cmp expect actual
+'
+
+test_done
diff --git a/t/t5540-http-push.sh b/t/t5540-http-push.sh
index f4a2cf6..1eea647 100755
--- a/t/t5540-http-push.sh
+++ b/t/t5540-http-push.sh
@@ -3,23 +3,22 @@
 # Copyright (c) 2008 Clemens Buchacher <drizzd@aon.at>
 #
 
-test_description='test http-push
+test_description='test WebDAV http-push
 
 This test runs various sanity checks on http-push.'
 
 . ./test-lib.sh
 
-ROOT_PATH="$PWD"
-LIB_HTTPD_DAV=t
-LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5540'}
-
 if git http-push > /dev/null 2>&1 || [ $? -eq 128 ]
 then
-	say "skipping test, USE_CURL_MULTI is not defined"
+	skip_all="skipping test, USE_CURL_MULTI is not defined"
 	test_done
 fi
 
+LIB_HTTPD_DAV=t
+LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5540'}
 . "$TEST_DIRECTORY"/lib-httpd.sh
+ROOT_PATH="$PWD"
 start_httpd
 
 test_expect_success 'setup remote repository' '
@@ -36,16 +35,33 @@
 	cd test_repo.git &&
 	git --bare update-server-info &&
 	mv hooks/post-update.sample hooks/post-update &&
+	ORIG_HEAD=$(git rev-parse --verify HEAD) &&
 	cd - &&
 	mv test_repo.git "$HTTPD_DOCUMENT_ROOT_PATH"
 '
 
-test_expect_success 'clone remote repository' '
-	cd "$ROOT_PATH" &&
-	git clone $HTTPD_URL/test_repo.git test_repo_clone
+test_expect_success 'create password-protected repository' '
+	mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb" &&
+	cp -Rf "$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
+	       "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git"
 '
 
-test_expect_failure 'push to remote repository with packed refs' '
+test_expect_success 'setup askpass helper' '
+	cat >askpass <<-\EOF &&
+	#!/bin/sh
+	echo user@host
+	EOF
+	chmod +x askpass &&
+	GIT_ASKPASS="$PWD/askpass" &&
+	export GIT_ASKPASS
+'
+
+test_expect_success 'clone remote repository' '
+	cd "$ROOT_PATH" &&
+	git clone $HTTPD_URL/dumb/test_repo.git test_repo_clone
+'
+
+test_expect_success 'push to remote repository with packed refs' '
 	cd "$ROOT_PATH"/test_repo_clone &&
 	: >path2 &&
 	git add path2 &&
@@ -57,11 +73,15 @@
 	 test $HEAD = $(git rev-parse --verify HEAD))
 '
 
-test_expect_success ' push to remote repository with unpacked refs' '
+test_expect_success 'push already up-to-date' '
+	git push
+'
+
+test_expect_success 'push to remote repository with unpacked refs' '
 	(cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
 	 rm packed-refs &&
-	 git update-ref refs/heads/master \
-		0c973ae9bd51902a28466f3850b543fa66a6aaf4) &&
+	 git update-ref refs/heads/master $ORIG_HEAD &&
+	 git --bare update-server-info) &&
 	git push &&
 	(cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
 	 test $HEAD = $(git rev-parse --verify HEAD))
@@ -71,7 +91,7 @@
 	cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
 		"$HTTPD_DOCUMENT_ROOT_PATH"/test_repo_unpacked.git &&
 
-	git clone $HTTPD_URL/test_repo_unpacked.git \
+	git clone $HTTPD_URL/dumb/test_repo_unpacked.git \
 		"$ROOT_PATH"/fetch_unpacked &&
 
 	# By reset, we force git to retrieve the object
@@ -80,14 +100,14 @@
 	 git remote rm origin &&
 	 git reflog expire --expire=0 --all &&
 	 git prune &&
-	 git push -f -v $HTTPD_URL/test_repo_unpacked.git master)
+	 git push -f -v $HTTPD_URL/dumb/test_repo_unpacked.git master)
 '
 
 test_expect_success 'http-push fetches packed objects' '
 	cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
 		"$HTTPD_DOCUMENT_ROOT_PATH"/test_repo_packed.git &&
 
-	git clone $HTTPD_URL/test_repo_packed.git \
+	git clone $HTTPD_URL/dumb/test_repo_packed.git \
 		"$ROOT_PATH"/test_repo_clone_packed &&
 
 	(cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo_packed.git &&
@@ -100,7 +120,7 @@
 	 git remote rm origin &&
 	 git reflog expire --expire=0 --all &&
 	 git prune &&
-	 git push -f -v $HTTPD_URL/test_repo_packed.git master)
+	 git push -f -v $HTTPD_URL/dumb/test_repo_packed.git master)
 '
 
 test_expect_success 'create and delete remote branch' '
@@ -111,10 +131,7 @@
 	test_tick &&
 	git commit -m dev &&
 	git push origin dev &&
-	git fetch &&
 	git push origin :dev &&
-	git branch -d -r origin/dev &&
-	git fetch &&
 	test_must_fail git show-ref --verify refs/remotes/origin/dev
 '
 
@@ -131,11 +148,36 @@
 x40="$x38$x2"
 
 test_expect_success 'PUT and MOVE sends object to URLs with SHA-1 hash suffix' '
-	sed -e "s/PUT /OP /" -e "s/MOVE /OP /" "$HTTPD_ROOT_PATH"/access.log |
-	grep -e "\"OP .*/objects/$x2/${x38}_$x40 HTTP/[.0-9]*\" 20[0-9] "
+	sed \
+		-e "s/PUT /OP /" \
+		-e "s/MOVE /OP /" \
+	    -e "s|/objects/$x2/${x38}_$x40|WANTED_PATH_REQUEST|" \
+		"$HTTPD_ROOT_PATH"/access.log |
+	grep -e "\"OP .*WANTED_PATH_REQUEST HTTP/[.0-9]*\" 20[0-9] "
 
 '
 
+test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
+	"$ROOT_PATH"/test_repo_clone master
+
+test_expect_success 'push to password-protected repository (user in URL)' '
+	test_commit pw-user &&
+	git push "$HTTPD_URL_USER/auth/dumb/test_repo.git" HEAD &&
+	git rev-parse --verify HEAD >expect &&
+	git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git" \
+		rev-parse --verify HEAD >actual &&
+	test_cmp expect actual
+'
+
+test_expect_failure 'push to password-protected repository (no user in URL)' '
+	test_commit pw-nouser &&
+	git push "$HTTPD_URL/auth/dumb/test_repo.git" HEAD &&
+	git rev-parse --verify HEAD >expect &&
+	git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git" \
+		rev-parse --verify HEAD >actual &&
+	test_cmp expect actual
+'
+
 stop_httpd
 
 test_done
diff --git a/t/t5541-http-push.sh b/t/t5541-http-push.sh
new file mode 100755
index 0000000..312e484
--- /dev/null
+++ b/t/t5541-http-push.sh
@@ -0,0 +1,270 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Clemens Buchacher <drizzd@aon.at>
+#
+
+test_description='test smart pushing over http via http-backend'
+. ./test-lib.sh
+
+if test -n "$NO_CURL"; then
+	skip_all='skipping test, git built without http support'
+	test_done
+fi
+
+ROOT_PATH="$PWD"
+LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5541'}
+. "$TEST_DIRECTORY"/lib-httpd.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
+start_httpd
+
+test_expect_success 'setup remote repository' '
+	cd "$ROOT_PATH" &&
+	mkdir test_repo &&
+	cd test_repo &&
+	git init &&
+	: >path1 &&
+	git add path1 &&
+	test_tick &&
+	git commit -m initial &&
+	cd - &&
+	git clone --bare test_repo test_repo.git &&
+	cd test_repo.git &&
+	git config http.receivepack true &&
+	git config core.logallrefupdates true &&
+	ORIG_HEAD=$(git rev-parse --verify HEAD) &&
+	cd - &&
+	mv test_repo.git "$HTTPD_DOCUMENT_ROOT_PATH"
+'
+
+cat >exp <<EOF
+GET  /smart/test_repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+POST /smart/test_repo.git/git-upload-pack HTTP/1.1 200
+EOF
+test_expect_success 'no empty path components' '
+	# In the URL, add a trailing slash, and see if git appends yet another
+	# slash.
+	cd "$ROOT_PATH" &&
+	git clone $HTTPD_URL/smart/test_repo.git/ test_repo_clone &&
+
+	sed -e "
+		s/^.* \"//
+		s/\"//
+		s/ [1-9][0-9]*\$//
+		s/^GET /GET  /
+	" >act <"$HTTPD_ROOT_PATH"/access.log &&
+
+	# Clear the log, so that it does not affect the "used receive-pack
+	# service" test which reads the log too.
+	#
+	# We do this before the actual comparison to ensure the log is cleared.
+	echo > "$HTTPD_ROOT_PATH"/access.log &&
+
+	test_cmp exp act
+'
+
+test_expect_success 'clone remote repository' '
+	rm -rf test_repo_clone &&
+	git clone $HTTPD_URL/smart/test_repo.git test_repo_clone
+'
+
+test_expect_success 'push to remote repository (standard)' '
+	cd "$ROOT_PATH"/test_repo_clone &&
+	: >path2 &&
+	git add path2 &&
+	test_tick &&
+	git commit -m path2 &&
+	HEAD=$(git rev-parse --verify HEAD) &&
+	GIT_CURL_VERBOSE=1 git push -v -v 2>err &&
+	! grep "Expect: 100-continue" err &&
+	grep "POST git-receive-pack ([0-9]* bytes)" err &&
+	(cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
+	 test $HEAD = $(git rev-parse --verify HEAD))
+'
+
+test_expect_success 'push already up-to-date' '
+	git push
+'
+
+test_expect_success 'create and delete remote branch' '
+	cd "$ROOT_PATH"/test_repo_clone &&
+	git checkout -b dev &&
+	: >path3 &&
+	git add path3 &&
+	test_tick &&
+	git commit -m dev &&
+	git push origin dev &&
+	git push origin :dev &&
+	test_must_fail git show-ref --verify refs/remotes/origin/dev
+'
+
+cat >"$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git/hooks/update" <<EOF
+#!/bin/sh
+exit 1
+EOF
+chmod a+x "$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git/hooks/update"
+
+cat >exp <<EOF
+remote: error: hook declined to update refs/heads/dev2
+To http://127.0.0.1:$LIB_HTTPD_PORT/smart/test_repo.git
+ ! [remote rejected] dev2 -> dev2 (hook declined)
+error: failed to push some refs to 'http://127.0.0.1:$LIB_HTTPD_PORT/smart/test_repo.git'
+EOF
+
+test_expect_success 'rejected update prints status' '
+	cd "$ROOT_PATH"/test_repo_clone &&
+	git checkout -b dev2 &&
+	: >path4 &&
+	git add path4 &&
+	test_tick &&
+	git commit -m dev2 &&
+	test_must_fail git push origin dev2 2>act &&
+	sed -e "/^remote: /s/ *$//" <act >cmp &&
+	test_cmp exp cmp
+'
+rm -f "$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git/hooks/update"
+
+cat >exp <<EOF
+
+GET  /smart/test_repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+POST /smart/test_repo.git/git-upload-pack HTTP/1.1 200
+GET  /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
+POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200
+GET  /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
+GET  /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
+POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200
+GET  /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
+POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200
+GET  /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
+POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200
+EOF
+test_expect_success 'used receive-pack service' '
+	sed -e "
+		s/^.* \"//
+		s/\"//
+		s/ [1-9][0-9]*\$//
+		s/^GET /GET  /
+	" >act <"$HTTPD_ROOT_PATH"/access.log &&
+	test_cmp exp act
+'
+
+test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
+	"$ROOT_PATH"/test_repo_clone master
+
+test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper' '
+	# create a dissimilarly-named remote ref so that git is unable to match the
+	# two refs (viz. local, remote) unless an explicit refspec is provided.
+	git push origin master:retsam
+
+	echo "change changed" > path2 &&
+	git commit -a -m path2 --amend &&
+
+	# push master too; this ensures there is at least one '"'push'"' command to
+	# the remote helper and triggers interaction with the helper.
+	test_must_fail git push -v origin +master master:retsam >output 2>&1'
+
+test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper: remote output' '
+	grep "^ + [a-f0-9]*\.\.\.[a-f0-9]* *master -> master (forced update)$" output &&
+	grep "^ ! \[rejected\] *master -> retsam (non-fast-forward)$" output
+'
+
+test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper: our output' '
+	test_i18ngrep "Updates were rejected because" \
+		output
+'
+
+test_expect_success 'push (chunked)' '
+	git checkout master &&
+	test_commit commit path3 &&
+	HEAD=$(git rev-parse --verify HEAD) &&
+	git config http.postbuffer 4 &&
+	test_when_finished "git config --unset http.postbuffer" &&
+	git push -v -v origin $BRANCH 2>err &&
+	grep "POST git-receive-pack (chunked)" err &&
+	(cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
+	 test $HEAD = $(git rev-parse --verify HEAD))
+'
+
+test_expect_success 'push --all can push to empty repo' '
+	d=$HTTPD_DOCUMENT_ROOT_PATH/empty-all.git &&
+	git init --bare "$d" &&
+	git --git-dir="$d" config http.receivepack true &&
+	git push --all "$HTTPD_URL"/smart/empty-all.git
+'
+
+test_expect_success 'push --mirror can push to empty repo' '
+	d=$HTTPD_DOCUMENT_ROOT_PATH/empty-mirror.git &&
+	git init --bare "$d" &&
+	git --git-dir="$d" config http.receivepack true &&
+	git push --mirror "$HTTPD_URL"/smart/empty-mirror.git
+'
+
+test_expect_success 'push --all to repo with alternates' '
+	s=$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git &&
+	d=$HTTPD_DOCUMENT_ROOT_PATH/alternates-all.git &&
+	git clone --bare --shared "$s" "$d" &&
+	git --git-dir="$d" config http.receivepack true &&
+	git --git-dir="$d" repack -adl &&
+	git push --all "$HTTPD_URL"/smart/alternates-all.git
+'
+
+test_expect_success 'push --mirror to repo with alternates' '
+	s=$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git &&
+	d=$HTTPD_DOCUMENT_ROOT_PATH/alternates-mirror.git &&
+	git clone --bare --shared "$s" "$d" &&
+	git --git-dir="$d" config http.receivepack true &&
+	git --git-dir="$d" repack -adl &&
+	git push --mirror "$HTTPD_URL"/smart/alternates-mirror.git
+'
+
+test_expect_success TTY 'push shows progress when stderr is a tty' '
+	cd "$ROOT_PATH"/test_repo_clone &&
+	test_commit noisy &&
+	test_terminal git push >output 2>&1 &&
+	grep "^Writing objects" output
+'
+
+test_expect_success TTY 'push --quiet silences status and progress' '
+	cd "$ROOT_PATH"/test_repo_clone &&
+	test_commit quiet &&
+	test_terminal git push --quiet >output 2>&1 &&
+	test_cmp /dev/null output
+'
+
+test_expect_success TTY 'push --no-progress silences progress but not status' '
+	cd "$ROOT_PATH"/test_repo_clone &&
+	test_commit no-progress &&
+	test_terminal git push --no-progress >output 2>&1 &&
+	grep "^To http" output &&
+	! grep "^Writing objects"
+'
+
+test_expect_success 'push --progress shows progress to non-tty' '
+	cd "$ROOT_PATH"/test_repo_clone &&
+	test_commit progress &&
+	git push --progress >output 2>&1 &&
+	grep "^To http" output &&
+	grep "^Writing objects" output
+'
+
+test_expect_success 'http push gives sane defaults to reflog' '
+	cd "$ROOT_PATH"/test_repo_clone &&
+	test_commit reflog-test &&
+	git push "$HTTPD_URL"/smart/test_repo.git &&
+	git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
+		log -g -1 --format="%gn <%ge>" >actual &&
+	echo "anonymous <anonymous@http.127.0.0.1>" >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'http push respects GIT_COMMITTER_* in reflog' '
+	cd "$ROOT_PATH"/test_repo_clone &&
+	test_commit custom-reflog-test &&
+	git push "$HTTPD_URL"/smart_custom_env/test_repo.git &&
+	git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
+		log -g -1 --format="%gn <%ge>" >actual &&
+	echo "Custom User <custom@example.com>" >expect &&
+	test_cmp expect actual
+'
+
+stop_httpd
+test_done
diff --git a/t/t5550-http-fetch.sh b/t/t5550-http-fetch.sh
index 0e69324..b06f817 100755
--- a/t/t5550-http-fetch.sh
+++ b/t/t5550-http-fetch.sh
@@ -1,47 +1,158 @@
 #!/bin/sh
 
-test_description='test fetching over http'
+test_description='test dumb fetching over http via static file'
 . ./test-lib.sh
 
 if test -n "$NO_CURL"; then
-	say 'skipping test, git built without http support'
+	skip_all='skipping test, git built without http support'
 	test_done
 fi
 
-. "$TEST_DIRECTORY"/lib-httpd.sh
 LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5550'}
+. "$TEST_DIRECTORY"/lib-httpd.sh
 start_httpd
 
 test_expect_success 'setup repository' '
-	echo content >file &&
+	echo content1 >file &&
 	git add file &&
 	git commit -m one
+	echo content2 >file &&
+	git add file &&
+	git commit -m two
 '
 
-test_expect_success 'create http-accessible bare repository' '
-	mkdir "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+test_expect_success 'create http-accessible bare repository with loose objects' '
+	cp -a .git "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
 	(cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
-	 git --bare init &&
+	 git config core.bare true &&
+	 mkdir -p hooks &&
 	 echo "exec git update-server-info" >hooks/post-update &&
-	 chmod +x hooks/post-update
+	 chmod +x hooks/post-update &&
+	 hooks/post-update
 	) &&
 	git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
 	git push public master:master
 '
 
 test_expect_success 'clone http repository' '
-	git clone $HTTPD_URL/repo.git clone &&
+	git clone $HTTPD_URL/dumb/repo.git clone-tmpl &&
+	cp -R clone-tmpl clone &&
 	test_cmp file clone/file
 '
 
+test_expect_success 'create password-protected repository' '
+	mkdir "$HTTPD_DOCUMENT_ROOT_PATH/auth/" &&
+	cp -Rf "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
+	       "$HTTPD_DOCUMENT_ROOT_PATH/auth/repo.git"
+'
+
+test_expect_success 'setup askpass helpers' '
+	cat >askpass <<-EOF &&
+	#!/bin/sh
+	echo >>"$PWD/askpass-query" "askpass: \$*" &&
+	cat "$PWD/askpass-response"
+	EOF
+	chmod +x askpass &&
+	GIT_ASKPASS="$PWD/askpass" &&
+	export GIT_ASKPASS
+'
+
+expect_askpass() {
+	dest=$HTTPD_DEST
+	{
+		case "$1" in
+		none)
+			;;
+		pass)
+			echo "askpass: Password for 'http://$2@$dest': "
+			;;
+		both)
+			echo "askpass: Username for 'http://$dest': "
+			echo "askpass: Password for 'http://$2@$dest': "
+			;;
+		*)
+			false
+			;;
+		esac
+	} >askpass-expect &&
+	test_cmp askpass-expect askpass-query
+}
+
+test_expect_success 'cloning password-protected repository can fail' '
+	>askpass-query &&
+	echo wrong >askpass-response &&
+	test_must_fail git clone "$HTTPD_URL/auth/repo.git" clone-auth-fail &&
+	expect_askpass both wrong
+'
+
+test_expect_success 'http auth can use user/pass in URL' '
+	>askpass-query &&
+	echo wrong >askpass-response &&
+	git clone "$HTTPD_URL_USER_PASS/auth/repo.git" clone-auth-none &&
+	expect_askpass none
+'
+
+test_expect_success 'http auth can use just user in URL' '
+	>askpass-query &&
+	echo user@host >askpass-response &&
+	git clone "$HTTPD_URL_USER/auth/repo.git" clone-auth-pass &&
+	expect_askpass pass user@host
+'
+
+test_expect_success 'http auth can request both user and pass' '
+	>askpass-query &&
+	echo user@host >askpass-response &&
+	git clone "$HTTPD_URL/auth/repo.git" clone-auth-both &&
+	expect_askpass both user@host
+'
+
+test_expect_success 'http auth respects credential helper config' '
+	test_config_global credential.helper "!f() {
+		cat >/dev/null
+		echo username=user@host
+		echo password=user@host
+	}; f" &&
+	>askpass-query &&
+	echo wrong >askpass-response &&
+	git clone "$HTTPD_URL/auth/repo.git" clone-auth-helper &&
+	expect_askpass none
+'
+
+test_expect_success 'http auth can get username from config' '
+	test_config_global "credential.$HTTPD_URL.username" user@host &&
+	>askpass-query &&
+	echo user@host >askpass-response &&
+	git clone "$HTTPD_URL/auth/repo.git" clone-auth-user &&
+	expect_askpass pass user@host
+'
+
+test_expect_success 'configured username does not override URL' '
+	test_config_global "credential.$HTTPD_URL.username" wrong &&
+	>askpass-query &&
+	echo user@host >askpass-response &&
+	git clone "$HTTPD_URL_USER/auth/repo.git" clone-auth-user2 &&
+	expect_askpass pass user@host
+'
+
 test_expect_success 'fetch changes via http' '
 	echo content >>file &&
 	git commit -a -m two &&
-	git push public
+	git push public &&
 	(cd clone && git pull) &&
 	test_cmp file clone/file
 '
 
+test_expect_success 'fetch changes via manual http-fetch' '
+	cp -R clone-tmpl clone2 &&
+
+	HEAD=$(git rev-parse --verify HEAD) &&
+	(cd clone2 &&
+	 git http-fetch -a -w heads/master-new $HEAD $(git config remote.origin.url) &&
+	 git checkout master-new &&
+	 test $HEAD = $(git rev-parse --verify HEAD)) &&
+	test_cmp file clone2/file
+'
+
 test_expect_success 'http remote detects correct HEAD' '
 	git push public master:other &&
 	(cd clone &&
@@ -55,10 +166,46 @@
 
 test_expect_success 'fetch packed objects' '
 	cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/repo.git "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git &&
-	cd "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git &&
-	git --bare repack &&
-	git --bare prune-packed &&
-	git clone $HTTPD_URL/repo_pack.git
+	(cd "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git &&
+	 git --bare repack -a -d
+	) &&
+	git clone $HTTPD_URL/dumb/repo_pack.git
+'
+
+test_expect_success 'fetch notices corrupt pack' '
+	cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git "$HTTPD_DOCUMENT_ROOT_PATH"/repo_bad1.git &&
+	(cd "$HTTPD_DOCUMENT_ROOT_PATH"/repo_bad1.git &&
+	 p=`ls objects/pack/pack-*.pack` &&
+	 chmod u+w $p &&
+	 printf %0256d 0 | dd of=$p bs=256 count=1 seek=1 conv=notrunc
+	) &&
+	mkdir repo_bad1.git &&
+	(cd repo_bad1.git &&
+	 git --bare init &&
+	 test_must_fail git --bare fetch $HTTPD_URL/dumb/repo_bad1.git &&
+	 test 0 = `ls objects/pack/pack-*.pack | wc -l`
+	)
+'
+
+test_expect_success 'fetch notices corrupt idx' '
+	cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git "$HTTPD_DOCUMENT_ROOT_PATH"/repo_bad2.git &&
+	(cd "$HTTPD_DOCUMENT_ROOT_PATH"/repo_bad2.git &&
+	 p=`ls objects/pack/pack-*.idx` &&
+	 chmod u+w $p &&
+	 printf %0256d 0 | dd of=$p bs=256 count=1 seek=1 conv=notrunc
+	) &&
+	mkdir repo_bad2.git &&
+	(cd repo_bad2.git &&
+	 git --bare init &&
+	 test_must_fail git --bare fetch $HTTPD_URL/dumb/repo_bad2.git &&
+	 test 0 = `ls objects/pack | wc -l`
+	)
+'
+
+test_expect_success 'did not use upload-pack service' '
+	grep '/git-upload-pack' <"$HTTPD_ROOT_PATH"/access.log >act
+	: >exp
+	test_cmp exp act
 '
 
 stop_httpd
diff --git a/t/t5551-http-fetch.sh b/t/t5551-http-fetch.sh
new file mode 100755
index 0000000..fadf2f2
--- /dev/null
+++ b/t/t5551-http-fetch.sh
@@ -0,0 +1,144 @@
+#!/bin/sh
+
+test_description='test smart fetching over http via http-backend'
+. ./test-lib.sh
+
+if test -n "$NO_CURL"; then
+	skip_all='skipping test, git built without http support'
+	test_done
+fi
+
+LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5551'}
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+test_expect_success 'setup repository' '
+	echo content >file &&
+	git add file &&
+	git commit -m one
+'
+
+test_expect_success 'create http-accessible bare repository' '
+	mkdir "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+	(cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+	 git --bare init
+	) &&
+	git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+	git push public master:master
+'
+
+cat >exp <<EOF
+> GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1
+> Accept: */*
+> Pragma: no-cache
+< HTTP/1.1 200 OK
+< Pragma: no-cache
+< Cache-Control: no-cache, max-age=0, must-revalidate
+< Content-Type: application/x-git-upload-pack-advertisement
+> POST /smart/repo.git/git-upload-pack HTTP/1.1
+> Accept-Encoding: deflate, gzip
+> Content-Type: application/x-git-upload-pack-request
+> Accept: application/x-git-upload-pack-result
+> Content-Length: xxx
+< HTTP/1.1 200 OK
+< Pragma: no-cache
+< Cache-Control: no-cache, max-age=0, must-revalidate
+< Content-Type: application/x-git-upload-pack-result
+EOF
+test_expect_success 'clone http repository' '
+	GIT_CURL_VERBOSE=1 git clone --quiet $HTTPD_URL/smart/repo.git clone 2>err &&
+	test_cmp file clone/file &&
+	tr '\''\015'\'' Q <err |
+	sed -e "
+		s/Q\$//
+		/^[*] /d
+		/^$/d
+		/^< $/d
+
+		/^[^><]/{
+			s/^/> /
+		}
+
+		/^> User-Agent: /d
+		/^> Host: /d
+		/^> POST /,$ {
+			/^> Accept: [*]\\/[*]/d
+		}
+		s/^> Content-Length: .*/> Content-Length: xxx/
+		/^> 00..want /d
+		/^> 00.*done/d
+
+		/^< Server: /d
+		/^< Expires: /d
+		/^< Date: /d
+		/^< Content-Length: /d
+		/^< Transfer-Encoding: /d
+	" >act &&
+	test_cmp exp act
+'
+
+test_expect_success 'fetch changes via http' '
+	echo content >>file &&
+	git commit -a -m two &&
+	git push public
+	(cd clone && git pull) &&
+	test_cmp file clone/file
+'
+
+cat >exp <<EOF
+GET  /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+POST /smart/repo.git/git-upload-pack HTTP/1.1 200
+GET  /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+POST /smart/repo.git/git-upload-pack HTTP/1.1 200
+EOF
+test_expect_success 'used upload-pack service' '
+	sed -e "
+		s/^.* \"//
+		s/\"//
+		s/ [1-9][0-9]*\$//
+		s/^GET /GET  /
+	" >act <"$HTTPD_ROOT_PATH"/access.log &&
+	test_cmp exp act
+'
+
+test_expect_success 'follow redirects (301)' '
+	git clone $HTTPD_URL/smart-redir-perm/repo.git --quiet repo-p
+'
+
+test_expect_success 'follow redirects (302)' '
+	git clone $HTTPD_URL/smart-redir-temp/repo.git --quiet repo-t
+'
+
+test -n "$GIT_TEST_LONG" && test_set_prereq EXPENSIVE
+
+test_expect_success EXPENSIVE 'create 50,000 tags in the repo' '
+	(
+	cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+	for i in `seq 50000`
+	do
+		echo "commit refs/heads/too-many-refs"
+		echo "mark :$i"
+		echo "committer git <git@example.com> $i +0000"
+		echo "data 0"
+		echo "M 644 inline bla.txt"
+		echo "data 4"
+		echo "bla"
+		# make every commit dangling by always
+		# rewinding the branch after each commit
+		echo "reset refs/heads/too-many-refs"
+		echo "from :1"
+	done | git fast-import --export-marks=marks &&
+
+	# now assign tags to all the dangling commits we created above
+	tag=$("$PERL_PATH" -e "print \"bla\" x 30") &&
+	sed -e "s/^:\(.\+\) \(.\+\)$/\2 refs\/tags\/$tag-\1/" <marks >>packed-refs
+	)
+'
+
+test_expect_success EXPENSIVE 'clone the 50,000 tag repo to check OS command line overflow' '
+	git clone $HTTPD_URL/smart/repo.git too-many-refs 2>err &&
+	test_line_count = 0 err
+'
+
+stop_httpd
+test_done
diff --git a/t/t5560-http-backend-noserver.sh b/t/t5560-http-backend-noserver.sh
new file mode 100755
index 0000000..ef98d95
--- /dev/null
+++ b/t/t5560-http-backend-noserver.sh
@@ -0,0 +1,74 @@
+#!/bin/sh
+
+test_description='test git-http-backend-noserver'
+. ./test-lib.sh
+
+HTTPD_DOCUMENT_ROOT_PATH="$TRASH_DIRECTORY"
+
+test_have_prereq MINGW && export GREP_OPTIONS=-U
+
+run_backend() {
+	echo "$2" |
+	QUERY_STRING="${1#*\?}" \
+	PATH_TRANSLATED="$HTTPD_DOCUMENT_ROOT_PATH/${1%%\?*}" \
+	git http-backend >act.out 2>act.err
+}
+
+GET() {
+	REQUEST_METHOD="GET" && export REQUEST_METHOD &&
+	run_backend "/repo.git/$1" &&
+	sane_unset REQUEST_METHOD &&
+	if ! grep "Status" act.out >act
+	then
+		printf "Status: 200 OK\r\n" >act
+	fi
+	printf "Status: $2\r\n" >exp &&
+	test_cmp exp act
+}
+
+POST() {
+	REQUEST_METHOD="POST" && export REQUEST_METHOD &&
+	CONTENT_TYPE="application/x-$1-request" && export CONTENT_TYPE &&
+	run_backend "/repo.git/$1" "$2" &&
+	sane_unset REQUEST_METHOD &&
+	sane_unset CONTENT_TYPE &&
+	if ! grep "Status" act.out >act
+	then
+		printf "Status: 200 OK\r\n" >act
+	fi
+	printf "Status: $3\r\n" >exp &&
+	test_cmp exp act
+}
+
+log_div() {
+	return 0
+}
+
+. "$TEST_DIRECTORY"/t556x_common
+
+expect_aliased() {
+	REQUEST_METHOD="GET" && export REQUEST_METHOD &&
+	if test $1 = 0; then
+		run_backend "$2"
+	else
+		run_backend "$2" &&
+		echo "fatal: '$2': aliased" >exp.err &&
+		test_cmp exp.err act.err
+	fi
+	unset REQUEST_METHOD
+}
+
+test_expect_success 'http-backend blocks bad PATH_INFO' '
+	config http.getanyfile true &&
+
+	expect_aliased 0 /repo.git/HEAD &&
+
+	expect_aliased 1 /repo.git/../HEAD &&
+	expect_aliased 1 /../etc/passwd &&
+	expect_aliased 1 ../etc/passwd &&
+	expect_aliased 1 /etc//passwd &&
+	expect_aliased 1 /etc/./passwd &&
+	expect_aliased 1 //domain/data.txt
+'
+
+test_done
diff --git a/t/t5561-http-backend.sh b/t/t5561-http-backend.sh
new file mode 100755
index 0000000..b5d7fbc
--- /dev/null
+++ b/t/t5561-http-backend.sh
@@ -0,0 +1,149 @@
+#!/bin/sh
+
+test_description='test git-http-backend'
+. ./test-lib.sh
+
+if test -n "$NO_CURL"; then
+	skip_all='skipping test, git built without http support'
+	test_done
+fi
+
+LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5561'}
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+GET() {
+	curl --include "$HTTPD_URL/$SMART/repo.git/$1" >out 2>/dev/null &&
+	tr '\015' Q <out |
+	sed '
+		s/Q$//
+		1q
+	' >act &&
+	echo "HTTP/1.1 $2" >exp &&
+	test_cmp exp act
+}
+
+POST() {
+	curl --include --data "$2" \
+	--header "Content-Type: application/x-$1-request" \
+	"$HTTPD_URL/smart/repo.git/$1" >out 2>/dev/null &&
+	tr '\015' Q <out |
+	sed '
+		s/Q$//
+		1q
+	' >act &&
+	echo "HTTP/1.1 $3" >exp &&
+	test_cmp exp act
+}
+
+log_div() {
+	echo >>"$HTTPD_ROOT_PATH"/access.log
+	echo "###  $1" >>"$HTTPD_ROOT_PATH"/access.log
+	echo "###" >>"$HTTPD_ROOT_PATH"/access.log
+}
+
+. "$TEST_DIRECTORY"/t556x_common
+
+cat >exp <<EOF
+
+###  refs/heads/master
+###
+GET  /smart/repo.git/refs/heads/master HTTP/1.1 404 -
+
+###  getanyfile default
+###
+GET  /smart/repo.git/HEAD HTTP/1.1 200
+GET  /smart/repo.git/info/refs HTTP/1.1 200
+GET  /smart/repo.git/objects/info/packs HTTP/1.1 200
+GET  /smart/repo.git/objects/info/alternates HTTP/1.1 200 -
+GET  /smart/repo.git/objects/info/http-alternates HTTP/1.1 200 -
+GET  /smart/repo.git/$LOOSE_URL HTTP/1.1 200
+GET  /smart/repo.git/$PACK_URL HTTP/1.1 200
+GET  /smart/repo.git/$IDX_URL HTTP/1.1 200
+
+###  no git-daemon-export-ok
+###
+GET  /smart_noexport/repo.git/HEAD HTTP/1.1 404 -
+GET  /smart_noexport/repo.git/info/refs HTTP/1.1 404 -
+GET  /smart_noexport/repo.git/objects/info/packs HTTP/1.1 404 -
+GET  /smart_noexport/repo.git/objects/info/alternates HTTP/1.1 404 -
+GET  /smart_noexport/repo.git/objects/info/http-alternates HTTP/1.1 404 -
+GET  /smart_noexport/repo.git/$LOOSE_URL HTTP/1.1 404 -
+GET  /smart_noexport/repo.git/$PACK_URL HTTP/1.1 404 -
+GET  /smart_noexport/repo.git/$IDX_URL HTTP/1.1 404 -
+
+###  git-daemon-export-ok
+###
+GET  /smart_noexport/repo.git/HEAD HTTP/1.1 200
+GET  /smart_noexport/repo.git/info/refs HTTP/1.1 200
+GET  /smart_noexport/repo.git/objects/info/packs HTTP/1.1 200
+GET  /smart_noexport/repo.git/objects/info/alternates HTTP/1.1 200 -
+GET  /smart_noexport/repo.git/objects/info/http-alternates HTTP/1.1 200 -
+GET  /smart_noexport/repo.git/$LOOSE_URL HTTP/1.1 200
+GET  /smart_noexport/repo.git/$PACK_URL HTTP/1.1 200
+GET  /smart_noexport/repo.git/$IDX_URL HTTP/1.1 200
+
+###  getanyfile true
+###
+GET  /smart/repo.git/HEAD HTTP/1.1 200
+GET  /smart/repo.git/info/refs HTTP/1.1 200
+GET  /smart/repo.git/objects/info/packs HTTP/1.1 200
+GET  /smart/repo.git/objects/info/alternates HTTP/1.1 200 -
+GET  /smart/repo.git/objects/info/http-alternates HTTP/1.1 200 -
+GET  /smart/repo.git/$LOOSE_URL HTTP/1.1 200
+GET  /smart/repo.git/$PACK_URL HTTP/1.1 200
+GET  /smart/repo.git/$IDX_URL HTTP/1.1 200
+
+###  getanyfile false
+###
+GET  /smart/repo.git/HEAD HTTP/1.1 403 -
+GET  /smart/repo.git/info/refs HTTP/1.1 403 -
+GET  /smart/repo.git/objects/info/packs HTTP/1.1 403 -
+GET  /smart/repo.git/objects/info/alternates HTTP/1.1 403 -
+GET  /smart/repo.git/objects/info/http-alternates HTTP/1.1 403 -
+GET  /smart/repo.git/$LOOSE_URL HTTP/1.1 403 -
+GET  /smart/repo.git/$PACK_URL HTTP/1.1 403 -
+GET  /smart/repo.git/$IDX_URL HTTP/1.1 403 -
+
+###  uploadpack default
+###
+GET  /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+POST /smart/repo.git/git-upload-pack HTTP/1.1 200 -
+
+###  uploadpack true
+###
+GET  /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+POST /smart/repo.git/git-upload-pack HTTP/1.1 200 -
+
+###  uploadpack false
+###
+GET  /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 403 -
+POST /smart/repo.git/git-upload-pack HTTP/1.1 403 -
+
+###  receivepack default
+###
+GET  /smart/repo.git/info/refs?service=git-receive-pack HTTP/1.1 403 -
+POST /smart/repo.git/git-receive-pack HTTP/1.1 403 -
+
+###  receivepack true
+###
+GET  /smart/repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
+POST /smart/repo.git/git-receive-pack HTTP/1.1 200 -
+
+###  receivepack false
+###
+GET  /smart/repo.git/info/refs?service=git-receive-pack HTTP/1.1 403 -
+POST /smart/repo.git/git-receive-pack HTTP/1.1 403 -
+EOF
+test_expect_success 'server request log matches test results' '
+	sed -e "
+		s/^.* \"//
+		s/\"//
+		s/ [1-9][0-9]*\$//
+		s/^GET /GET  /
+	" >act <"$HTTPD_ROOT_PATH"/access.log &&
+	test_cmp exp act
+'
+
+stop_httpd
+test_done
diff --git a/t/t556x_common b/t/t556x_common
new file mode 100755
index 0000000..82926cf
--- /dev/null
+++ b/t/t556x_common
@@ -0,0 +1,122 @@
+#!/bin/sh
+
+find_file() {
+	cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+	find $1 -type f |
+	sed -e 1q
+}
+
+config() {
+	git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" config $1 $2
+}
+
+test_expect_success 'setup repository' '
+	echo content >file &&
+	git add file &&
+	git commit -m one &&
+
+	mkdir "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+	(cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+	 git --bare init &&
+	 : >objects/info/alternates &&
+	 : >objects/info/http-alternates
+	) &&
+	git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+	git push public master:master &&
+
+	(cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+	 git repack -a -d
+	) &&
+
+	echo other >file &&
+	git add file &&
+	git commit -m two &&
+	git push public master:master &&
+
+	LOOSE_URL=$(find_file objects/??) &&
+	PACK_URL=$(find_file objects/pack/*.pack) &&
+	IDX_URL=$(find_file objects/pack/*.idx)
+'
+
+get_static_files() {
+	GET HEAD "$1" &&
+	GET info/refs "$1" &&
+	GET objects/info/packs "$1" &&
+	GET objects/info/alternates "$1" &&
+	GET objects/info/http-alternates "$1" &&
+	GET $LOOSE_URL "$1" &&
+	GET $PACK_URL "$1" &&
+	GET $IDX_URL "$1"
+}
+
+SMART=smart
+GIT_HTTP_EXPORT_ALL=1 && export GIT_HTTP_EXPORT_ALL
+test_expect_success 'direct refs/heads/master not found' '
+	log_div "refs/heads/master" &&
+	GET refs/heads/master "404 Not Found"
+'
+test_expect_success 'static file is ok' '
+	log_div "getanyfile default" &&
+	get_static_files "200 OK"
+'
+SMART=smart_noexport
+unset GIT_HTTP_EXPORT_ALL
+test_expect_success 'no export by default' '
+	log_div "no git-daemon-export-ok" &&
+	get_static_files "404 Not Found"
+'
+test_expect_success 'export if git-daemon-export-ok' '
+	log_div "git-daemon-export-ok" &&
+        (cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+	 touch git-daemon-export-ok
+	) &&
+        get_static_files "200 OK"
+'
+SMART=smart
+GIT_HTTP_EXPORT_ALL=1 && export GIT_HTTP_EXPORT_ALL
+test_expect_success 'static file if http.getanyfile true is ok' '
+	log_div "getanyfile true" &&
+	config http.getanyfile true &&
+	get_static_files "200 OK"
+'
+test_expect_success 'static file if http.getanyfile false fails' '
+	log_div "getanyfile false" &&
+	config http.getanyfile false &&
+	get_static_files "403 Forbidden"
+'
+
+test_expect_success 'http.uploadpack default enabled' '
+	log_div "uploadpack default" &&
+	GET info/refs?service=git-upload-pack "200 OK"  &&
+	POST git-upload-pack 0000 "200 OK"
+'
+test_expect_success 'http.uploadpack true' '
+	log_div "uploadpack true" &&
+	config http.uploadpack true &&
+	GET info/refs?service=git-upload-pack "200 OK" &&
+	POST git-upload-pack 0000 "200 OK"
+'
+test_expect_success 'http.uploadpack false' '
+	log_div "uploadpack false" &&
+	config http.uploadpack false &&
+	GET info/refs?service=git-upload-pack "403 Forbidden" &&
+	POST git-upload-pack 0000 "403 Forbidden"
+'
+
+test_expect_success 'http.receivepack default disabled' '
+	log_div "receivepack default" &&
+	GET info/refs?service=git-receive-pack "403 Forbidden"  &&
+	POST git-receive-pack 0000 "403 Forbidden"
+'
+test_expect_success 'http.receivepack true' '
+	log_div "receivepack true" &&
+	config http.receivepack true &&
+	GET info/refs?service=git-receive-pack "200 OK" &&
+	POST git-receive-pack 0000 "200 OK"
+'
+test_expect_success 'http.receivepack false' '
+	log_div "receivepack false" &&
+	config http.receivepack false &&
+	GET info/refs?service=git-receive-pack "403 Forbidden" &&
+	POST git-receive-pack 0000 "403 Forbidden"
+'
diff --git a/t/t5570-git-daemon.sh b/t/t5570-git-daemon.sh
new file mode 100755
index 0000000..a3a4e47
--- /dev/null
+++ b/t/t5570-git-daemon.sh
@@ -0,0 +1,146 @@
+#!/bin/sh
+
+test_description='test fetching over git protocol'
+. ./test-lib.sh
+
+LIB_GIT_DAEMON_PORT=${LIB_GIT_DAEMON_PORT-5570}
+. "$TEST_DIRECTORY"/lib-git-daemon.sh
+start_git_daemon
+
+test_expect_success 'setup repository' '
+	echo content >file &&
+	git add file &&
+	git commit -m one
+'
+
+test_expect_success 'create git-accessible bare repository' '
+	mkdir "$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo.git" &&
+	(cd "$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo.git" &&
+	 git --bare init &&
+	 : >git-daemon-export-ok
+	) &&
+	git remote add public "$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo.git" &&
+	git push public master:master
+'
+
+test_expect_success 'clone git repository' '
+	git clone "$GIT_DAEMON_URL/repo.git" clone &&
+	test_cmp file clone/file
+'
+
+test_expect_success 'fetch changes via git protocol' '
+	echo content >>file &&
+	git commit -a -m two &&
+	git push public &&
+	(cd clone && git pull) &&
+	test_cmp file clone/file
+'
+
+test_expect_failure 'remote detects correct HEAD' '
+	git push public master:other &&
+	(cd clone &&
+	 git remote set-head -d origin &&
+	 git remote set-head -a origin &&
+	 git symbolic-ref refs/remotes/origin/HEAD > output &&
+	 echo refs/remotes/origin/master > expect &&
+	 test_cmp expect output
+	)
+'
+
+test_expect_success 'prepare pack objects' '
+	cp -R "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo.git "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_pack.git &&
+	(cd "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_pack.git &&
+	 git --bare repack -a -d
+	)
+'
+
+test_expect_success 'fetch notices corrupt pack' '
+	cp -R "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_pack.git "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_bad1.git &&
+	(cd "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_bad1.git &&
+	 p=`ls objects/pack/pack-*.pack` &&
+	 chmod u+w $p &&
+	 printf %0256d 0 | dd of=$p bs=256 count=1 seek=1 conv=notrunc
+	) &&
+	mkdir repo_bad1.git &&
+	(cd repo_bad1.git &&
+	 git --bare init &&
+	 test_must_fail git --bare fetch "$GIT_DAEMON_URL/repo_bad1.git" &&
+	 test 0 = `ls objects/pack/pack-*.pack | wc -l`
+	)
+'
+
+test_expect_success 'fetch notices corrupt idx' '
+	cp -R "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_pack.git "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_bad2.git &&
+	(cd "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_bad2.git &&
+	 p=`ls objects/pack/pack-*.idx` &&
+	 chmod u+w $p &&
+	 printf %0256d 0 | dd of=$p bs=256 count=1 seek=1 conv=notrunc
+	) &&
+	mkdir repo_bad2.git &&
+	(cd repo_bad2.git &&
+	 git --bare init &&
+	 test_must_fail git --bare fetch "$GIT_DAEMON_URL/repo_bad2.git" &&
+	 test 0 = `ls objects/pack | wc -l`
+	)
+'
+
+test_remote_error()
+{
+	do_export=YesPlease
+	while test $# -gt 0
+	do
+		case $1 in
+		-x)
+			shift
+			chmod -x "$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo.git"
+			;;
+		-n)
+			shift
+			do_export=
+			;;
+		*)
+			break
+		esac
+	done
+
+	msg=$1
+	shift
+	cmd=$1
+	shift
+	repo=$1
+	shift || error "invalid number of arguments"
+
+	if test -x "$GIT_DAEMON_DOCUMENT_ROOT_PATH/$repo"
+	then
+		if test -n "$do_export"
+		then
+			: >"$GIT_DAEMON_DOCUMENT_ROOT_PATH/$repo/git-daemon-export-ok"
+		else
+			rm -f "$GIT_DAEMON_DOCUMENT_ROOT_PATH/$repo/git-daemon-export-ok"
+		fi
+	fi
+
+	test_must_fail git "$cmd" "$GIT_DAEMON_URL/$repo" "$@" 2>output &&
+	echo "fatal: remote error: $msg: /$repo" >expect &&
+	test_cmp expect output
+	ret=$?
+	chmod +x "$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo.git"
+	(exit $ret)
+}
+
+msg="access denied or repository not exported"
+test_expect_success 'clone non-existent' "test_remote_error    '$msg' clone nowhere.git    "
+test_expect_success 'push disabled'      "test_remote_error    '$msg' push  repo.git master"
+test_expect_success 'read access denied' "test_remote_error -x '$msg' fetch repo.git       "
+test_expect_success 'not exported'       "test_remote_error -n '$msg' fetch repo.git       "
+
+stop_git_daemon
+start_git_daemon --informative-errors
+
+test_expect_success 'clone non-existent' "test_remote_error    'no such repository'      clone nowhere.git    "
+test_expect_success 'push disabled'      "test_remote_error    'service not enabled'     push  repo.git master"
+test_expect_success 'read access denied' "test_remote_error -x 'no such repository'      fetch repo.git       "
+test_expect_success 'not exported'       "test_remote_error -n 'repository not exported' fetch repo.git       "
+
+stop_git_daemon
+test_done
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index 2335d8b..67869b4 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -9,10 +9,13 @@
 	rm -fr .git &&
 	test_create_repo src &&
 	(
-		cd src
-		>file
-		git add file
-		git commit -m initial
+		cd src &&
+		>file &&
+		git add file &&
+		git commit -m initial &&
+		echo 1 >file &&
+		git add file &&
+		git commit -m updated
 	)
 
 '
@@ -31,10 +34,10 @@
 
 '
 
-test_expect_success 'output from clone' '
+test_expect_success C_LOCALE_OUTPUT 'output from clone' '
 	rm -fr dst &&
 	git clone -n "file://$(pwd)/src" dst >output &&
-	test $(grep Initialized output | wc -l) = 1
+	test $(grep Clon output | wc -l) = 1
 '
 
 test_expect_success 'clone does not keep pack' '
@@ -88,6 +91,26 @@
 
 '
 
+test_expect_success 'clone --mirror with detached HEAD' '
+
+	( cd src && git checkout HEAD^ && git rev-parse HEAD >../expected ) &&
+	git clone --mirror src mirror.detached &&
+	( cd src && git checkout - ) &&
+	GIT_DIR=mirror.detached git rev-parse HEAD >actual &&
+	test_cmp expected actual
+
+'
+
+test_expect_success 'clone --bare with detached HEAD' '
+
+	( cd src && git checkout HEAD^ && git rev-parse HEAD >../expected ) &&
+	git clone --bare src bare.detached &&
+	( cd src && git checkout - ) &&
+	GIT_DIR=bare.detached git rev-parse HEAD >actual &&
+	test_cmp expected actual
+
+'
+
 test_expect_success 'clone --bare names the local repository <name>.git' '
 
 	git clone --bare src &&
@@ -149,11 +172,13 @@
 	(
 		cd src-0 && git init
 	) &&
-	git clone src-0 target-6 &&
+	git clone "file://$(pwd)/src-0" target-6 2>err-6 &&
+	! grep "fatal:" err-6 &&
 	(
 		cd src-0 && test_commit A
 	) &&
-	git clone src-0 target-7 &&
+	git clone "file://$(pwd)/src-0" target-7 2>err-7 &&
+	! grep "fatal:" err-7 &&
 	# There is no reason to insist they are bit-for-bit
 	# identical, but this test should suffice for now.
 	test_cmp target-6/.git/config target-7/.git/config
@@ -161,10 +186,7 @@
 
 test_expect_success 'clone respects global branch.autosetuprebase' '
 	(
-		HOME=$(pwd) &&
-		export HOME &&
 		test_config="$HOME/.gitconfig" &&
-		unset GIT_CONFIG_NOGLOBAL &&
 		git config -f "$test_config" branch.autosetuprebase remote &&
 		rm -fr dst &&
 		git clone src dst &&
@@ -174,4 +196,88 @@
 	)
 '
 
+test_expect_success 'respect url-encoding of file://' '
+	git init x+y &&
+	git clone "file://$PWD/x+y" xy-url-1 &&
+	git clone "file://$PWD/x%2By" xy-url-2
+'
+
+test_expect_success 'do not query-string-decode + in URLs' '
+	rm -rf x+y &&
+	git init "x y" &&
+	test_must_fail git clone "file://$PWD/x+y" xy-no-plus
+'
+
+test_expect_success 'do not respect url-encoding of non-url path' '
+	git init x+y &&
+	test_must_fail git clone x%2By xy-regular &&
+	git clone x+y xy-regular
+'
+
+test_expect_success 'clone separate gitdir' '
+	rm -rf dst &&
+	git clone --separate-git-dir realgitdir src dst &&
+	test -d realgitdir/refs
+'
+
+test_expect_success 'clone separate gitdir: output' '
+	echo "gitdir: `pwd`/realgitdir" >expected &&
+	test_cmp expected dst/.git
+'
+
+test_expect_success 'clone from .git file' '
+	git clone dst/.git dst2
+'
+
+test_expect_success 'fetch from .git gitfile' '
+	(
+		cd dst2 &&
+		git fetch ../dst/.git
+	)
+'
+
+test_expect_success 'fetch from gitfile parent' '
+	(
+		cd dst2 &&
+		git fetch ../dst
+	)
+'
+
+test_expect_success 'clone separate gitdir where target already exists' '
+	rm -rf dst &&
+	test_must_fail git clone --separate-git-dir realgitdir src dst
+'
+
+test_expect_success 'clone --reference from original' '
+	git clone --shared --bare src src-1 &&
+	git clone --bare src src-2 &&
+	git clone --reference=src-2 --bare src-1 target-8 &&
+	grep /src-2/ target-8/objects/info/alternates
+'
+
+test_expect_success 'clone with more than one --reference' '
+	git clone --bare src src-3 &&
+	git clone --bare src src-4 &&
+	git clone --reference=src-3 --reference=src-4 src target-9 &&
+	grep /src-3/ target-9/.git/objects/info/alternates &&
+	grep /src-4/ target-9/.git/objects/info/alternates
+'
+
+test_expect_success 'clone from original with relative alternate' '
+	mkdir nest &&
+	git clone --bare src nest/src-5 &&
+	echo ../../../src/.git/objects >nest/src-5/objects/info/alternates &&
+	git clone --bare nest/src-5 target-10 &&
+	grep /src/\\.git/objects target-10/objects/info/alternates
+'
+
+test_expect_success 'clone checking out a tag' '
+	git clone --branch=some-tag src dst.tag &&
+	GIT_DIR=src/.git git rev-parse some-tag >expected &&
+	test_cmp expected dst.tag/.git/HEAD &&
+	GIT_DIR=dst.tag/.git git config remote.origin.fetch >fetch.actual &&
+	echo "+refs/heads/*:refs/remotes/origin/*" >fetch.expected &&
+	test_cmp fetch.expected fetch.actual
+'
+
 test_done
diff --git a/t/t5602-clone-remote-exec.sh b/t/t5602-clone-remote-exec.sh
index deffdae..3f353d9 100755
--- a/t/t5602-clone-remote-exec.sh
+++ b/t/t5602-clone-remote-exec.sh
@@ -5,21 +5,29 @@
 . ./test-lib.sh
 
 test_expect_success setup '
-	echo "#!/bin/sh" > not_ssh
-	echo "echo \"\$*\" > not_ssh_output" >> not_ssh
-	echo "exit 1" >> not_ssh
+	echo "#!/bin/sh" > not_ssh &&
+	echo "echo \"\$*\" > not_ssh_output" >> not_ssh &&
+	echo "exit 1" >> not_ssh &&
 	chmod +x not_ssh
 '
 
 test_expect_success 'clone calls git upload-pack unqualified with no -u option' '
-	GIT_SSH=./not_ssh git clone localhost:/path/to/repo junk
-	echo "localhost git-upload-pack '\''/path/to/repo'\''" >expected
+	(
+		GIT_SSH=./not_ssh &&
+		export GIT_SSH &&
+		test_must_fail git clone localhost:/path/to/repo junk
+	) &&
+	echo "localhost git-upload-pack '\''/path/to/repo'\''" >expected &&
 	test_cmp expected not_ssh_output
 '
 
 test_expect_success 'clone calls specified git upload-pack with -u option' '
-	GIT_SSH=./not_ssh git clone -u ./something/bin/git-upload-pack localhost:/path/to/repo junk
-	echo "localhost ./something/bin/git-upload-pack '\''/path/to/repo'\''" >expected
+	(
+		GIT_SSH=./not_ssh &&
+		export GIT_SSH &&
+		test_must_fail git clone -u ./something/bin/git-upload-pack localhost:/path/to/repo junk
+	) &&
+	echo "localhost ./something/bin/git-upload-pack '\''/path/to/repo'\''" >expected &&
 	test_cmp expected not_ssh_output
 '
 
diff --git a/t/t5700-clone-reference.sh b/t/t5700-clone-reference.sh
index 1c10916..c47d450 100755
--- a/t/t5700-clone-reference.sh
+++ b/t/t5700-clone-reference.sh
@@ -34,7 +34,7 @@
 cd "$base_dir"
 
 test_expect_success 'existence of info/alternates' \
-'test `wc -l <C/.git/objects/info/alternates` = 2'
+'test_line_count = 2 C/.git/objects/info/alternates'
 
 cd "$base_dir"
 
@@ -48,22 +48,22 @@
 'cd C &&
 echo "0 objects, 0 kilobytes" > expected &&
 git count-objects > current &&
-diff expected current'
+test_cmp expected current'
 
 cd "$base_dir"
 
-rm -f "$U"
+rm -f "$U.D"
 
 test_expect_success 'cloning with reference (no -l -s)' \
-'GIT_DEBUG_SEND_PACK=3 git clone --reference B "file://$(pwd)/A" D 3>"$U"'
+'GIT_DEBUG_SEND_PACK=3 git clone --reference B "file://$(pwd)/A" D 3>"$U.D"'
 
 test_expect_success 'fetched no objects' \
-'! grep "^want" "$U"'
+'! grep "^want" "$U.D"'
 
 cd "$base_dir"
 
 test_expect_success 'existence of info/alternates' \
-'test `wc -l <D/.git/objects/info/alternates` = 1'
+'test_line_count = 1 D/.git/objects/info/alternates'
 
 cd "$base_dir"
 
@@ -75,7 +75,7 @@
 test_expect_success 'that reference gets used' \
 'cd D && echo "0 objects, 0 kilobytes" > expected &&
 git count-objects > current &&
-diff expected current'
+test_cmp expected current'
 
 cd "$base_dir"
 
@@ -100,7 +100,7 @@
 'cd C &&
 echo "2 objects" > expected &&
 git count-objects | cut -d, -f1 > current &&
-diff expected current'
+test_cmp expected current'
 
 cd "$base_dir"
 
@@ -116,7 +116,7 @@
 'cd D &&
 echo "5 objects" > expected &&
 git count-objects | cut -d, -f1 > current &&
-diff expected current'
+test_cmp expected current'
 
 cd "$base_dir"
 
@@ -146,4 +146,39 @@
 
 cd "$base_dir"
 
+test_expect_success 'clone with reference from a tagged repository' '
+	(
+		cd A && git tag -a -m 'tagged' HEAD
+	) &&
+	git clone --reference=A A I
+'
+
+test_expect_success 'prepare branched repository' '
+	git clone A J &&
+	(
+		cd J &&
+		git checkout -b other master^ &&
+		echo other >otherfile &&
+		git add otherfile &&
+		git commit -m other &&
+		git checkout master
+	)
+'
+
+rm -f "$U.K"
+
+test_expect_success 'fetch with incomplete alternates' '
+	git init K &&
+	echo "$base_dir/A/.git/objects" >K/.git/objects/info/alternates &&
+	(
+		cd K &&
+		git remote add J "file://$base_dir/J" &&
+		GIT_DEBUG_SEND_PACK=3 git fetch J 3>"$U.K"
+	) &&
+	master_object=$(cd A && git for-each-ref --format="%(objectname)" refs/heads/master) &&
+	! grep "^want $master_object" "$U.K" &&
+	tag_object=$(cd A && git for-each-ref --format="%(objectname)" refs/tags/HEAD) &&
+	! grep "^want $tag_object" "$U.K"
+'
+
 test_done
diff --git a/t/t5701-clone-local.sh b/t/t5701-clone-local.sh
index 19b5c0d..7ff6e0e 100755
--- a/t/t5701-clone-local.sh
+++ b/t/t5701-clone-local.sh
@@ -3,123 +3,95 @@
 test_description='test local clone'
 . ./test-lib.sh
 
-D=`pwd`
+repo_is_hardlinked() {
+	find "$1/objects" -type f -links 1 >output &&
+	test_line_count = 0 output
+}
 
 test_expect_success 'preparing origin repository' '
 	: >file && git add . && git commit -m1 &&
 	git clone --bare . a.git &&
 	git clone --bare . x &&
 	test "$(GIT_CONFIG=a.git/config git config --bool core.bare)" = true &&
-	test "$(GIT_CONFIG=x/config git config --bool core.bare)" = true
+	test "$(GIT_CONFIG=x/config git config --bool core.bare)" = true &&
 	git bundle create b1.bundle --all &&
 	git bundle create b2.bundle master &&
 	mkdir dir &&
-	cp b1.bundle dir/b3
+	cp b1.bundle dir/b3 &&
 	cp b1.bundle b4
 '
 
 test_expect_success 'local clone without .git suffix' '
-	cd "$D" &&
 	git clone -l -s a b &&
-	cd b &&
+	(cd b &&
 	test "$(GIT_CONFIG=.git/config git config --bool core.bare)" = false &&
-	git fetch
+	git fetch)
 '
 
 test_expect_success 'local clone with .git suffix' '
-	cd "$D" &&
 	git clone -l -s a.git c &&
-	cd c &&
-	git fetch
+	(cd c && git fetch)
 '
 
 test_expect_success 'local clone from x' '
-	cd "$D" &&
 	git clone -l -s x y &&
-	cd y &&
-	git fetch
+	(cd y && git fetch)
 '
 
 test_expect_success 'local clone from x.git that does not exist' '
-	cd "$D" &&
-	if git clone -l -s x.git z
-	then
-		echo "Oops, should have failed"
-		false
-	else
-		echo happy
-	fi
+	test_must_fail git clone -l -s x.git z
 '
 
 test_expect_success 'With -no-hardlinks, local will make a copy' '
-	cd "$D" &&
 	git clone --bare --no-hardlinks x w &&
-	cd w &&
-	linked=$(find objects -type f ! -links 1 | wc -l) &&
-	test 0 = $linked
+	! repo_is_hardlinked w
 '
 
 test_expect_success 'Even without -l, local will make a hardlink' '
-	cd "$D" &&
 	rm -fr w &&
 	git clone -l --bare x w &&
-	cd w &&
-	copied=$(find objects -type f -links 1 | wc -l) &&
-	test 0 = $copied
+	repo_is_hardlinked w
 '
 
 test_expect_success 'local clone of repo with nonexistent ref in HEAD' '
-	cd "$D" &&
 	echo "ref: refs/heads/nonexistent" > a.git/HEAD &&
 	git clone a d &&
-	cd d &&
+	(cd d &&
 	git fetch &&
-	test ! -e .git/refs/remotes/origin/HEAD'
+	test ! -e .git/refs/remotes/origin/HEAD)
+'
 
 test_expect_success 'bundle clone without .bundle suffix' '
-	cd "$D" &&
 	git clone dir/b3 &&
-	cd b3 &&
-	git fetch
+	(cd b3 && git fetch)
 '
 
 test_expect_success 'bundle clone with .bundle suffix' '
-	cd "$D" &&
 	git clone b1.bundle &&
-	cd b1 &&
-	git fetch
+	(cd b1 && git fetch)
 '
 
 test_expect_success 'bundle clone from b4' '
-	cd "$D" &&
 	git clone b4 bdl &&
-	cd bdl &&
-	git fetch
+	(cd bdl && git fetch)
 '
 
 test_expect_success 'bundle clone from b4.bundle that does not exist' '
-	cd "$D" &&
-	if git clone b4.bundle bb
-	then
-		echo "Oops, should have failed"
-		false
-	else
-		echo happy
-	fi
+	test_must_fail git clone b4.bundle bb
 '
 
 test_expect_success 'bundle clone with nonexistent HEAD' '
-	cd "$D" &&
 	git clone b2.bundle b2 &&
-	cd b2 &&
-	git fetch
-	test ! -e .git/refs/heads/master
+	(cd b2 &&
+	git fetch &&
+	test_must_fail git rev-parse --verify refs/heads/master)
 '
 
 test_expect_success 'clone empty repository' '
-	cd "$D" &&
 	mkdir empty &&
-	(cd empty && git init) &&
+	(cd empty &&
+	 git init &&
+	 git config receive.denyCurrentBranch warn) &&
 	git clone empty empty-clone &&
 	test_tick &&
 	(cd empty-clone
@@ -133,7 +105,6 @@
 '
 
 test_expect_success 'clone empty repository, and then push should not segfault.' '
-	cd "$D" &&
 	rm -fr empty/ empty-clone/ &&
 	mkdir empty &&
 	(cd empty && git init) &&
@@ -142,4 +113,25 @@
 	test_must_fail git push)
 '
 
+test_expect_success 'cloning non-existent directory fails' '
+	rm -rf does-not-exist &&
+	test_must_fail git clone does-not-exist
+'
+
+test_expect_success 'cloning non-git directory fails' '
+	rm -rf not-a-git-repo not-a-git-repo-clone &&
+	mkdir not-a-git-repo &&
+	test_must_fail git clone not-a-git-repo not-a-git-repo-clone
+'
+
+test_expect_success 'cloning file:// does not hardlink' '
+	git clone --bare file://"$(pwd)"/a non-local &&
+	! repo_is_hardlinked non-local
+'
+
+test_expect_success 'cloning a local path with --no-local does not hardlink' '
+	git clone --bare --no-local a force-nonlocal &&
+	! repo_is_hardlinked force-nonlocal
+'
+
 test_done
diff --git a/t/t5702-clone-options.sh b/t/t5702-clone-options.sh
index 27825f5..02cb024 100755
--- a/t/t5702-clone-options.sh
+++ b/t/t5702-clone-options.sh
@@ -27,7 +27,8 @@
 '
 test_expect_success 'redirected clone -v' '
 
-	git clone -v "file://$(pwd)/parent" clone-redirected-v >out 2>err &&
+	git clone --progress "file://$(pwd)/parent" clone-redirected-progress \
+		>out 2>err &&
 	test -s err
 
 '
diff --git a/t/t5704-bundle.sh b/t/t5704-bundle.sh
index a8f4419..9e43731 100755
--- a/t/t5704-bundle.sh
+++ b/t/t5704-bundle.sh
@@ -4,30 +4,58 @@
 . ./test-lib.sh
 
 test_expect_success 'setup' '
-
-	: > file &&
-	git add file &&
-	test_tick &&
-	git commit -m initial &&
+	test_commit initial &&
 	test_tick &&
 	git tag -m tag tag &&
-	: > file2 &&
-	git add file2 &&
-	: > file3 &&
-	test_tick &&
-	git commit -m second &&
-	git add file3 &&
-	test_tick &&
-	git commit -m third
-
+	test_commit second &&
+	test_commit third &&
+	git tag -d initial &&
+	git tag -d second &&
+	git tag -d third
 '
 
 test_expect_success 'tags can be excluded by rev-list options' '
-
 	git bundle create bundle --all --since=7.Apr.2005.15:16:00.-0700 &&
 	git ls-remote bundle > output &&
 	! grep tag output
+'
 
+test_expect_success 'die if bundle file cannot be created' '
+	mkdir adir &&
+	test_must_fail git bundle create adir --all
+'
+
+test_expect_failure 'bundle --stdin' '
+	echo master | git bundle create stdin-bundle.bdl --stdin &&
+	git ls-remote stdin-bundle.bdl >output &&
+	grep master output
+'
+
+test_expect_failure 'bundle --stdin <rev-list options>' '
+	echo master | git bundle create hybrid-bundle.bdl --stdin tag &&
+	git ls-remote hybrid-bundle.bdl >output &&
+	grep master output
+'
+
+test_expect_success 'empty bundle file is rejected' '
+	: >empty-bundle &&
+	test_must_fail git fetch empty-bundle
+'
+
+# This triggers a bug in older versions where the resulting line (with
+# --pretty=oneline) was longer than a 1024-char buffer.
+test_expect_success 'ridiculously long subject in boundary' '
+	: >file4 &&
+	test_tick &&
+	git add file4 &&
+	printf "%01200d\n" 0 | git commit -F - &&
+	test_commit fifth &&
+	git bundle create long-subject-bundle.bdl HEAD^..HEAD &&
+	git bundle list-heads long-subject-bundle.bdl >heads &&
+	test -s heads &&
+	git fetch long-subject-bundle.bdl &&
+	sed -n "/^-/{p;q;}" long-subject-bundle.bdl >boundary &&
+	grep "^-[0-9a-f]\\{40\\} " boundary
 '
 
 test_done
diff --git a/t/t5705-clone-2gb.sh b/t/t5705-clone-2gb.sh
index 9f52154..e9783c3 100755
--- a/t/t5705-clone-2gb.sh
+++ b/t/t5705-clone-2gb.sh
@@ -3,16 +3,18 @@
 test_description='Test cloning a repository larger than 2 gigabyte'
 . ./test-lib.sh
 
-test -z "$GIT_TEST_CLONE_2GB" &&
-say "Skipping expensive 2GB clone test; enable it with GIT_TEST_CLONE_2GB=t" &&
-test_done &&
-exit
+if test -z "$GIT_TEST_CLONE_2GB"
+then
+	say 'Skipping expensive 2GB clone test; enable it with GIT_TEST_CLONE_2GB=t'
+else
+	test_set_prereq CLONE_2GB
+fi
 
-test_expect_success 'setup' '
+test_expect_success CLONE_2GB 'setup' '
 
 	git config pack.compression 0 &&
 	git config pack.depth 0 &&
-	blobsize=$((20*1024*1024)) &&
+	blobsize=$((100*1024*1024)) &&
 	blobcount=$((2*1024*1024*1024/$blobsize+1)) &&
 	i=1 &&
 	(while test $i -le $blobcount
@@ -31,14 +33,20 @@
 	 echo "data 5" &&
 	 echo ">2gb" &&
 	 cat commit) |
-	git fast-import &&
+	git fast-import --big-file-threshold=2 &&
 	test ! -f exit-status
 
 '
 
-test_expect_success 'clone' '
+test_expect_success CLONE_2GB 'clone - bare' '
 
-	git clone --bare --no-hardlinks . clone
+	git clone --bare --no-hardlinks . clone-bare
+
+'
+
+test_expect_success CLONE_2GB 'clone - with worktree, file:// protocol' '
+
+	git clone file://. clone-wt
 
 '
 
diff --git a/t/t5706-clone-branch.sh b/t/t5706-clone-branch.sh
new file mode 100755
index 0000000..56be67e
--- /dev/null
+++ b/t/t5706-clone-branch.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+
+test_description='clone --branch option'
+. ./test-lib.sh
+
+check_HEAD() {
+	echo refs/heads/"$1" >expect &&
+	git symbolic-ref HEAD >actual &&
+	test_cmp expect actual
+}
+
+check_file() {
+	echo "$1" >expect &&
+	test_cmp expect file
+}
+
+test_expect_success 'setup' '
+	mkdir parent &&
+	(cd parent && git init &&
+	 echo one >file && git add file && git commit -m one &&
+	 git checkout -b two &&
+	 echo two >file && git add file && git commit -m two &&
+	 git checkout master)
+'
+
+test_expect_success 'vanilla clone chooses HEAD' '
+	git clone parent clone &&
+	(cd clone &&
+	 check_HEAD master &&
+	 check_file one
+	)
+'
+
+test_expect_success 'clone -b chooses specified branch' '
+	git clone -b two parent clone-two &&
+	(cd clone-two &&
+	 check_HEAD two &&
+	 check_file two
+	)
+'
+
+test_expect_success 'clone -b sets up tracking' '
+	(cd clone-two &&
+	 echo origin >expect &&
+	 git config branch.two.remote >actual &&
+	 echo refs/heads/two >>expect &&
+	 git config branch.two.merge >>actual &&
+	 test_cmp expect actual
+	)
+'
+
+test_expect_success 'clone -b does not munge remotes/origin/HEAD' '
+	(cd clone-two &&
+	 echo refs/remotes/origin/master >expect &&
+	 git symbolic-ref refs/remotes/origin/HEAD >actual &&
+	 test_cmp expect actual
+	)
+'
+
+test_expect_success 'clone -b with bogus branch' '
+	test_must_fail git clone -b bogus parent clone-bogus
+'
+
+test_done
diff --git a/t/t5707-clone-detached.sh b/t/t5707-clone-detached.sh
new file mode 100755
index 0000000..8b0d607
--- /dev/null
+++ b/t/t5707-clone-detached.sh
@@ -0,0 +1,76 @@
+#!/bin/sh
+
+test_description='test cloning a repository with detached HEAD'
+. ./test-lib.sh
+
+head_is_detached() {
+	git --git-dir=$1/.git rev-parse --verify HEAD &&
+	test_must_fail git --git-dir=$1/.git symbolic-ref HEAD
+}
+
+test_expect_success 'setup' '
+	echo one >file &&
+	git add file &&
+	git commit -m one &&
+	echo two >file &&
+	git commit -a -m two &&
+	git tag two &&
+	echo three >file &&
+	git commit -a -m three
+'
+
+test_expect_success 'clone repo (detached HEAD points to branch)' '
+	git checkout master^0 &&
+	git clone "file://$PWD" detached-branch
+'
+test_expect_success 'cloned HEAD matches' '
+	echo three >expect &&
+	git --git-dir=detached-branch/.git log -1 --format=%s >actual &&
+	test_cmp expect actual
+'
+test_expect_failure 'cloned HEAD is detached' '
+	head_is_detached detached-branch
+'
+
+test_expect_success 'clone repo (detached HEAD points to tag)' '
+	git checkout two^0 &&
+	git clone "file://$PWD" detached-tag
+'
+test_expect_success 'cloned HEAD matches' '
+	echo two >expect &&
+	git --git-dir=detached-tag/.git log -1 --format=%s >actual &&
+	test_cmp expect actual
+'
+test_expect_success 'cloned HEAD is detached' '
+	head_is_detached detached-tag
+'
+
+test_expect_success 'clone repo (detached HEAD points to history)' '
+	git checkout two^ &&
+	git clone "file://$PWD" detached-history
+'
+test_expect_success 'cloned HEAD matches' '
+	echo one >expect &&
+	git --git-dir=detached-history/.git log -1 --format=%s >actual &&
+	test_cmp expect actual
+'
+test_expect_success 'cloned HEAD is detached' '
+	head_is_detached detached-history
+'
+
+test_expect_success 'clone repo (orphan detached HEAD)' '
+	git checkout master^0 &&
+	echo four >file &&
+	git commit -a -m four &&
+	git clone "file://$PWD" detached-orphan
+'
+test_expect_success 'cloned HEAD matches' '
+	echo four >expect &&
+	git --git-dir=detached-orphan/.git log -1 --format=%s >actual &&
+	test_cmp expect actual
+'
+test_expect_success 'cloned HEAD is detached' '
+	head_is_detached detached-orphan
+'
+
+test_done
diff --git a/t/t5708-clone-config.sh b/t/t5708-clone-config.sh
new file mode 100755
index 0000000..27d730c
--- /dev/null
+++ b/t/t5708-clone-config.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+test_description='tests for git clone -c key=value'
+. ./test-lib.sh
+
+test_expect_success 'clone -c sets config in cloned repo' '
+	rm -rf child &&
+	git clone -c core.foo=bar . child &&
+	echo bar >expect &&
+	git --git-dir=child/.git config core.foo >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'clone -c can set multi-keys' '
+	rm -rf child &&
+	git clone -c core.foo=bar -c core.foo=baz . child &&
+	{ echo bar; echo baz; } >expect &&
+	git --git-dir=child/.git config --get-all core.foo >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'clone -c without a value is boolean true' '
+	rm -rf child &&
+	git clone -c core.foo . child &&
+	echo true >expect &&
+	git --git-dir=child/.git config --bool core.foo >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'clone -c config is available during clone' '
+	echo content >file &&
+	git add file &&
+	git commit -m one &&
+	rm -rf child &&
+	git clone -c core.autocrlf . child &&
+	printf "content\\r\\n" >expect &&
+	test_cmp expect child/file
+'
+
+test_done
diff --git a/t/t5710-info-alternate.sh b/t/t5710-info-alternate.sh
index ef7127c..aa04529 100755
--- a/t/t5710-info-alternate.sh
+++ b/t/t5710-info-alternate.sh
@@ -18,7 +18,7 @@
 
 test_valid_repo() {
 	git fsck --full > fsck.log &&
-	test `wc -l < fsck.log` = 0
+	test_line_count = 0 fsck.log
 }
 
 base_dir=`pwd`
diff --git a/t/t5800-remote-helpers.sh b/t/t5800-remote-helpers.sh
new file mode 100755
index 0000000..5702334
--- /dev/null
+++ b/t/t5800-remote-helpers.sh
@@ -0,0 +1,148 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Sverre Rabbelier
+#
+
+test_description='Test remote-helper import and export commands'
+
+. ./test-lib.sh
+
+if ! test_have_prereq PYTHON ; then
+	skip_all='skipping git-remote-hg tests, python not available'
+	test_done
+fi
+
+"$PYTHON_PATH" -c '
+import sys
+if sys.hexversion < 0x02040000:
+    sys.exit(1)
+' || {
+	skip_all='skipping git-remote-hg tests, python version < 2.4'
+	test_done
+}
+
+compare_refs() {
+	git --git-dir="$1/.git" rev-parse --verify $2 >expect &&
+	git --git-dir="$3/.git" rev-parse --verify $4 >actual &&
+	test_cmp expect actual
+}
+
+test_expect_success 'setup repository' '
+	git init --bare server/.git &&
+	git clone server public &&
+	(cd public &&
+	 echo content >file &&
+	 git add file &&
+	 git commit -m one &&
+	 git push origin master)
+'
+
+test_expect_success 'cloning from local repo' '
+	git clone "testgit::${PWD}/server" localclone &&
+	test_cmp public/file localclone/file
+'
+
+test_expect_success 'cloning from remote repo' '
+	git clone "testgit::file://${PWD}/server" clone &&
+	test_cmp public/file clone/file
+'
+
+test_expect_success 'create new commit on remote' '
+	(cd public &&
+	 echo content >>file &&
+	 git commit -a -m two &&
+	 git push)
+'
+
+test_expect_success 'pulling from local repo' '
+	(cd localclone && git pull) &&
+	test_cmp public/file localclone/file
+'
+
+test_expect_success 'pulling from remote remote' '
+	(cd clone && git pull) &&
+	test_cmp public/file clone/file
+'
+
+test_expect_success 'pushing to local repo' '
+	(cd localclone &&
+	echo content >>file &&
+	git commit -a -m three &&
+	git push) &&
+	compare_refs localclone HEAD server HEAD
+'
+
+# Generally, skip this test.  It demonstrates a now-fixed race in
+# git-remote-testgit, but is too slow to leave in for general use.
+: test_expect_success 'racily pushing to local repo' '
+	test_when_finished "rm -rf server2 localclone2" &&
+	cp -a server server2 &&
+	git clone "testgit::${PWD}/server2" localclone2 &&
+	(cd localclone2 &&
+	echo content >>file &&
+	git commit -a -m three &&
+	GIT_REMOTE_TESTGIT_SLEEPY=2 git push) &&
+	compare_refs localclone2 HEAD server2 HEAD
+'
+
+test_expect_success 'synch with changes from localclone' '
+	(cd clone &&
+	 git pull)
+'
+
+test_expect_success 'pushing remote local repo' '
+	(cd clone &&
+	echo content >>file &&
+	git commit -a -m four &&
+	git push) &&
+	compare_refs clone HEAD server HEAD
+'
+
+test_expect_success 'fetch new branch' '
+	(cd public &&
+	 git checkout -b new &&
+	 echo content >>file &&
+	 git commit -a -m five &&
+	 git push origin new
+	) &&
+	(cd localclone &&
+	 git fetch origin new
+	) &&
+	compare_refs public HEAD localclone FETCH_HEAD
+'
+
+test_expect_success 'fetch multiple branches' '
+	(cd localclone &&
+	 git fetch
+	) &&
+	compare_refs server master localclone refs/remotes/origin/master &&
+	compare_refs server new localclone refs/remotes/origin/new
+'
+
+test_expect_success 'push when remote has extra refs' '
+	(cd clone &&
+	 echo content >>file &&
+	 git commit -a -m six &&
+	 git push
+	) &&
+	compare_refs clone master server master
+'
+
+test_expect_success 'push new branch by name' '
+	(cd clone &&
+	 git checkout -b new-name  &&
+	 echo content >>file &&
+	 git commit -a -m seven &&
+	 git push origin new-name
+	) &&
+	compare_refs clone HEAD server refs/heads/new-name
+'
+
+test_expect_failure 'push new branch with old:new refspec' '
+	(cd clone &&
+	 git push origin new-name:new-refspec
+	) &&
+	compare_refs clone HEAD server refs/heads/new-refspec
+'
+
+test_done
diff --git a/t/t5900-repo-selection.sh b/t/t5900-repo-selection.sh
new file mode 100755
index 0000000..3d5b418
--- /dev/null
+++ b/t/t5900-repo-selection.sh
@@ -0,0 +1,100 @@
+#!/bin/sh
+
+test_description='selecting remote repo in ambiguous cases'
+. ./test-lib.sh
+
+reset() {
+	rm -rf foo foo.git fetch clone
+}
+
+make_tree() {
+	git init "$1" &&
+	(cd "$1" && test_commit "$1")
+}
+
+make_bare() {
+	git init --bare "$1" &&
+	(cd "$1" &&
+	 tree=`git hash-object -w -t tree /dev/null` &&
+	 commit=$(echo "$1" | git commit-tree $tree) &&
+	 git update-ref HEAD $commit
+	)
+}
+
+get() {
+	git init --bare fetch &&
+	(cd fetch && git fetch "../$1") &&
+	git clone "$1" clone
+}
+
+check() {
+	echo "$1" >expect &&
+	(cd fetch && git log -1 --format=%s FETCH_HEAD) >actual.fetch &&
+	(cd clone && git log -1 --format=%s HEAD) >actual.clone &&
+	test_cmp expect actual.fetch &&
+	test_cmp expect actual.clone
+}
+
+test_expect_success 'find .git dir in worktree' '
+	reset &&
+	make_tree foo &&
+	get foo &&
+	check foo
+'
+
+test_expect_success 'automagically add .git suffix' '
+	reset &&
+	make_bare foo.git &&
+	get foo &&
+	check foo.git
+'
+
+test_expect_success 'automagically add .git suffix to worktree' '
+	reset &&
+	make_tree foo.git &&
+	get foo &&
+	check foo.git
+'
+
+test_expect_success 'prefer worktree foo over bare foo.git' '
+	reset &&
+	make_tree foo &&
+	make_bare foo.git &&
+	get foo &&
+	check foo
+'
+
+test_expect_success 'prefer bare foo over bare foo.git' '
+	reset &&
+	make_bare foo &&
+	make_bare foo.git &&
+	get foo &&
+	check foo
+'
+
+test_expect_success 'disambiguate with full foo.git' '
+	reset &&
+	make_bare foo &&
+	make_bare foo.git &&
+	get foo.git &&
+	check foo.git
+'
+
+test_expect_success 'we are not fooled by non-git foo directory' '
+	reset &&
+	make_bare foo.git &&
+	mkdir foo &&
+	get foo &&
+	check foo.git
+'
+
+test_expect_success 'prefer inner .git over outer bare' '
+	reset &&
+	make_tree foo &&
+	make_bare foo.git &&
+	mv foo/.git foo.git &&
+	get foo.git &&
+	check foo
+'
+
+test_done
diff --git a/t/t6000-rev-list-misc.sh b/t/t6000-rev-list-misc.sh
new file mode 100755
index 0000000..b10685a
--- /dev/null
+++ b/t/t6000-rev-list-misc.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+
+test_description='miscellaneous rev-list tests'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+	echo content1 >wanted_file &&
+	echo content2 >unwanted_file &&
+	git add wanted_file unwanted_file &&
+	git commit -m one
+'
+
+test_expect_success 'rev-list --objects heeds pathspecs' '
+	git rev-list --objects HEAD -- wanted_file >output &&
+	grep wanted_file output &&
+	! grep unwanted_file output
+'
+
+test_expect_success 'rev-list --objects with pathspecs and deeper paths' '
+	mkdir foo &&
+	>foo/file &&
+	git add foo/file &&
+	git commit -m two &&
+
+	git rev-list --objects HEAD -- foo >output &&
+	grep foo/file output &&
+
+	git rev-list --objects HEAD -- foo/file >output &&
+	grep foo/file output &&
+	! grep unwanted_file output
+'
+
+test_expect_success 'rev-list --objects with pathspecs and copied files' '
+	git checkout --orphan junio-testcase &&
+	git rm -rf . &&
+
+	mkdir two &&
+	echo frotz >one &&
+	cp one two/three &&
+	git add one two/three &&
+	test_tick &&
+	git commit -m that &&
+
+	ONE=$(git rev-parse HEAD:one)
+	git rev-list --objects HEAD two >output &&
+	grep "$ONE two/three" output &&
+	! grep one output
+'
+
+test_done
diff --git a/t/t6000lib.sh b/t/t6000lib.sh
deleted file mode 100755
index f55627b..0000000
--- a/t/t6000lib.sh
+++ /dev/null
@@ -1,125 +0,0 @@
-[ -d .git/refs/tags ] || mkdir -p .git/refs/tags
-
-:> sed.script
-
-# Answer the sha1 has associated with the tag. The tag must exist in .git or .git/refs/tags
-tag()
-{
-	_tag=$1
-	[ -f .git/refs/tags/$_tag ] || error "tag: \"$_tag\" does not exist"
-	cat .git/refs/tags/$_tag
-}
-
-# Generate a commit using the text specified to make it unique and the tree
-# named by the tag specified.
-unique_commit()
-{
-	_text=$1
-        _tree=$2
-	shift 2
-	echo $_text | git commit-tree $(tag $_tree) "$@"
-}
-
-# Save the output of a command into the tag specified. Prepend
-# a substitution script for the tag onto the front of sed.script
-save_tag()
-{
-	_tag=$1
-	[ -n "$_tag" ] || error "usage: save_tag tag commit-args ..."
-	shift 1
-	"$@" >.git/refs/tags/$_tag
-
-        echo "s/$(tag $_tag)/$_tag/g" > sed.script.tmp
-	cat sed.script >> sed.script.tmp
-	rm sed.script
-	mv sed.script.tmp sed.script
-}
-
-# Replace unhelpful sha1 hashses with their symbolic equivalents
-entag()
-{
-	sed -f sed.script
-}
-
-# Execute a command after first saving, then setting the GIT_AUTHOR_EMAIL
-# tag to a specified value. Restore the original value on return.
-as_author()
-{
-	_author=$1
-	shift 1
-        _save=$GIT_AUTHOR_EMAIL
-
-	GIT_AUTHOR_EMAIL="$_author"
-	export GIT_AUTHOR_EMAIL
-	"$@"
-	if test -z "$_save"
-	then
-		unset GIT_AUTHOR_EMAIL
-	else
-		GIT_AUTHOR_EMAIL="$_save"
-		export GIT_AUTHOR_EMAIL
-	fi
-}
-
-commit_date()
-{
-        _commit=$1
-	git cat-file commit $_commit | sed -n "s/^committer .*> \([0-9]*\) .*/\1/p"
-}
-
-on_committer_date()
-{
-    _date=$1
-    shift 1
-    GIT_COMMITTER_DATE="$_date"
-    export GIT_COMMITTER_DATE
-    "$@"
-    unset GIT_COMMITTER_DATE
-}
-
-# Execute a command and suppress any error output.
-hide_error()
-{
-	"$@" 2>/dev/null
-}
-
-check_output()
-{
-	_name=$1
-	shift 1
-	if eval "$*" | entag > $_name.actual
-	then
-		diff $_name.expected $_name.actual
-	else
-		return 1;
-	fi
-}
-
-# Turn a reasonable test description into a reasonable test name.
-# All alphanums translated into -'s which are then compressed and stripped
-# from front and back.
-name_from_description()
-{
-	perl -pe '
-		s/[^A-Za-z0-9.]/-/g;
-		s/-+/-/g;
-		s/-$//;
-		s/^-//;
-		y/A-Z/a-z/;
-	'
-}
-
-
-# Execute the test described by the first argument, by eval'ing
-# command line specified in the 2nd argument. Check the status code
-# is zero and that the output matches the stream read from
-# stdin.
-test_output_expect_success()
-{
-	_description=$1
-        _test=$2
-        [ $# -eq 2 ] || error "usage: test_output_expect_success description test <<EOF ... EOF"
-        _name=$(echo $_description | name_from_description)
-	cat > $_name.expected
-	test_expect_success "$_description" "check_output $_name \"$_test\""
-}
diff --git a/t/t6001-rev-list-graft.sh b/t/t6001-rev-list-graft.sh
index b2131cd..8efcd13 100755
--- a/t/t6001-rev-list-graft.sh
+++ b/t/t6001-rev-list-graft.sh
@@ -84,28 +84,28 @@
 		git rev-list --parents --pretty=raw $arg |
 		sed -n -e 's/^commit //p' >test.actual
 	fi
-	diff test.expect test.actual
+	test_cmp test.expect test.actual
 }
 
 for type in basic parents parents-raw
 do
 	test_expect_success 'without grafts' "
-		rm -f .git/info/grafts
+		rm -f .git/info/grafts &&
 		check $type $B2 -- $B2 $B1 $B0
 	"
 
 	test_expect_success 'with grafts' "
-		echo '$B0 $A2' >.git/info/grafts
+		echo '$B0 $A2' >.git/info/grafts &&
 		check $type $B2 -- $B2 $B1 $B0 $A2 $A1 $A0
 	"
 
 	test_expect_success 'without grafts, with pathlimit' "
-		rm -f .git/info/grafts
+		rm -f .git/info/grafts &&
 		check $type $B2 subdir -- $B2 $B0
 	"
 
 	test_expect_success 'with grafts, with pathlimit' "
-		echo '$B0 $A2' >.git/info/grafts
+		echo '$B0 $A2' >.git/info/grafts &&
 		check $type $B2 subdir -- $B2 $B0 $A2 $A0
 	"
 
diff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh
index b4e8fba..fb07536 100755
--- a/t/t6002-rev-list-bisect.sh
+++ b/t/t6002-rev-list-bisect.sh
@@ -5,7 +5,7 @@
 test_description='Tests git rev-list --bisect functionality'
 
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/t6000lib.sh # t6xxx specific functions
+. "$TEST_DIRECTORY"/lib-t6000.sh # t6xxx specific functions
 
 # usage: test_bisection max-diff bisect-option head ^prune...
 #
diff --git a/t/t6003-rev-list-topo-order.sh b/t/t6003-rev-list-topo-order.sh
index 2c73f2d..e4c52b0 100755
--- a/t/t6003-rev-list-topo-order.sh
+++ b/t/t6003-rev-list-topo-order.sh
@@ -6,7 +6,7 @@
 test_description='Tests git rev-list --topo-order functionality'
 
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/t6000lib.sh # t6xxx specific functions
+. "$TEST_DIRECTORY"/lib-t6000.sh # t6xxx specific functions
 
 list_duplicates()
 {
diff --git a/t/t6004-rev-list-path-optim.sh b/t/t6004-rev-list-path-optim.sh
index 5dabf1c..3e8c42e 100755
--- a/t/t6004-rev-list-path-optim.sh
+++ b/t/t6004-rev-list-path-optim.sh
@@ -1,51 +1,96 @@
 #!/bin/sh
 
-test_description='git rev-list trivial path optimization test'
+test_description='git rev-list trivial path optimization test
+
+   d/z1
+   b0                             b1
+   o------------------------*----o master
+  /                        /
+ o---------o----o----o----o side
+ a0        c0   c1   a1   c2
+ d/f0      d/f1
+ d/z0
+
+'
 
 . ./test-lib.sh
 
 test_expect_success setup '
-echo Hello > a &&
-git add a &&
-git commit -m "Initial commit" a &&
-initial=$(git rev-parse --verify HEAD)
+	echo Hello >a &&
+	mkdir d &&
+	echo World >d/f &&
+	echo World >d/z &&
+	git add a d &&
+	test_tick &&
+	git commit -m "Initial commit" &&
+	git rev-parse --verify HEAD &&
+	git tag initial
 '
 
 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_tick &&
+	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_expect_success 'further setup' '
 	git checkout -b side &&
 	echo Irrelevant >c &&
-	git add c &&
+	echo Irrelevant >d/f &&
+	git add c d/f &&
+	test_tick &&
 	git commit -m "Side makes an irrelevant commit" &&
+	git tag side_c0 &&
 	echo "More Irrelevancy" >c &&
 	git add c &&
+	test_tick &&
 	git commit -m "Side makes another irrelevant commit" &&
 	echo Bye >a &&
 	git add a &&
+	test_tick &&
 	git commit -m "Side touches a" &&
-	side=$(git rev-parse --verify HEAD) &&
+	git tag side_a1 &&
 	echo "Yet more Irrelevancy" >c &&
 	git add c &&
+	test_tick &&
 	git commit -m "Side makes yet another irrelevant commit" &&
 	git checkout master &&
 	echo Another >b &&
-	git add b &&
+	echo Munged >d/z &&
+	git add b d/z &&
+	test_tick &&
 	git commit -m "Master touches b" &&
+	git tag master_b0 &&
 	git merge side &&
 	echo Touched >b &&
 	git add b &&
+	test_tick &&
 	git commit -m "Master touches b again"
 '
 
 test_expect_success 'path optimization 2' '
-	( echo "$side"; echo "$initial" ) >expected &&
+	git rev-parse side_a1 initial >expected &&
 	git rev-list HEAD -- a >actual &&
 	test_cmp expected actual
 '
 
+test_expect_success 'pathspec with leading path' '
+	git rev-parse master^ master_b0 side_c0 initial >expected &&
+	git rev-list HEAD -- d >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'pathspec with glob (1)' '
+	git rev-parse master^ master_b0 side_c0 initial >expected &&
+	git rev-list HEAD -- "d/*" >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'pathspec with glob (2)' '
+	git rev-parse side_c0 initial >expected &&
+	git rev-list HEAD -- "d/[a-m]*" >actual &&
+	test_cmp expected actual
+'
+
 test_done
diff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh
index 59d1f62..f94f0c4 100755
--- a/t/t6006-rev-list-format.sh
+++ b/t/t6006-rev-list-format.sh
@@ -19,6 +19,13 @@
 "
 }
 
+test_format percent %%h <<'EOF'
+commit 131a310eb913d107dd3c09a65d1651175898735d
+%h
+commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+%h
+EOF
+
 test_format hash %H%n%h <<'EOF'
 commit 131a310eb913d107dd3c09a65d1651175898735d
 131a310eb913d107dd3c09a65d1651175898735d
@@ -94,6 +101,15 @@
 commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
 EOF
 
+test_format raw-body %B <<'EOF'
+commit 131a310eb913d107dd3c09a65d1651175898735d
+changed foo
+
+commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+added foo
+
+EOF
+
 test_format colors %Credfoo%Cgreenbar%Cbluebaz%Cresetxyzzy <<'EOF'
 commit 131a310eb913d107dd3c09a65d1651175898735d
 foobarbazxyzzy
@@ -146,6 +162,14 @@
 commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
 EOF
 
+test_expect_success '%x00 shows NUL' '
+	echo  >expect commit f58db70b055c5718631e5c61528b28b12090cdea &&
+	echo >>expect fooQbar &&
+	git rev-list -1 --format=foo%x00bar HEAD >actual.nul &&
+	nul_to_q <actual.nul >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success '%ad respects --date=' '
 	echo 2005-04-07 >expect.ad-short &&
 	git log -1 --date=short --pretty=tformat:%ad >output.ad-short master &&
@@ -162,4 +186,108 @@
 	}
 '
 
+test_expect_success 'del LF before empty (1)' '
+	git show -s --pretty=format:"%s%n%-b%nThanks%n" HEAD^^ >actual &&
+	test_line_count = 2 actual
+'
+
+test_expect_success 'del LF before empty (2)' '
+	git show -s --pretty=format:"%s%n%-b%nThanks%n" HEAD >actual &&
+	test_line_count = 6 actual &&
+	grep "^$" actual
+'
+
+test_expect_success 'add LF before non-empty (1)' '
+	git show -s --pretty=format:"%s%+b%nThanks%n" HEAD^^ >actual &&
+	test_line_count = 2 actual
+'
+
+test_expect_success 'add LF before non-empty (2)' '
+	git show -s --pretty=format:"%s%+b%nThanks%n" HEAD >actual &&
+	test_line_count = 6 actual &&
+	grep "^$" actual
+'
+
+test_expect_success 'add SP before non-empty (1)' '
+	git show -s --pretty=format:"%s% bThanks" HEAD^^ >actual &&
+	test $(wc -w <actual) = 2
+'
+
+test_expect_success 'add SP before non-empty (2)' '
+	git show -s --pretty=format:"%s% sThanks" HEAD^^ >actual &&
+	test $(wc -w <actual) = 4
+'
+
+test_expect_success '--abbrev' '
+	echo SHORT SHORT SHORT >expect2 &&
+	echo LONG LONG LONG >expect3 &&
+	git log -1 --format="%h %h %h" HEAD >actual1 &&
+	git log -1 --abbrev=5 --format="%h %h %h" HEAD >actual2 &&
+	git log -1 --abbrev=5 --format="%H %H %H" HEAD >actual3 &&
+	sed -e "s/$_x40/LONG/g" -e "s/$_x05/SHORT/g" <actual2 >fuzzy2 &&
+	sed -e "s/$_x40/LONG/g" -e "s/$_x05/SHORT/g" <actual3 >fuzzy3 &&
+	test_cmp expect2 fuzzy2 &&
+	test_cmp expect3 fuzzy3 &&
+	! test_cmp actual1 actual2
+'
+
+test_expect_success '%H is not affected by --abbrev-commit' '
+	git log -1 --format=%H --abbrev-commit --abbrev=20 HEAD >actual &&
+	len=$(wc -c <actual) &&
+	test $len = 41
+'
+
+test_expect_success '%h is not affected by --abbrev-commit' '
+	git log -1 --format=%h --abbrev-commit --abbrev=20 HEAD >actual &&
+	len=$(wc -c <actual) &&
+	test $len = 21
+'
+
+test_expect_success '"%h %gD: %gs" is same as git-reflog' '
+	git reflog >expect &&
+	git log -g --format="%h %gD: %gs" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '"%h %gD: %gs" is same as git-reflog (with date)' '
+	git reflog --date=raw >expect &&
+	git log -g --format="%h %gD: %gs" --date=raw >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '"%h %gD: %gs" is same as git-reflog (with --abbrev)' '
+	git reflog --abbrev=13 --date=raw >expect &&
+	git log -g --abbrev=13 --format="%h %gD: %gs" --date=raw >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '%gd shortens ref name' '
+	echo "master@{0}" >expect.gd-short &&
+	git log -g -1 --format=%gd refs/heads/master >actual.gd-short &&
+	test_cmp expect.gd-short actual.gd-short
+'
+
+test_expect_success 'reflog identity' '
+	echo "C O Mitter:committer@example.com" >expect &&
+	git log -g -1 --format="%gn:%ge" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'oneline with empty message' '
+	git commit -m "dummy" --allow-empty &&
+	git commit -m "dummy" --allow-empty &&
+	git filter-branch --msg-filter "sed -e s/dummy//" HEAD^^.. &&
+	git rev-list --oneline HEAD >test.txt &&
+	test_line_count = 5 test.txt &&
+	git rev-list --oneline --graph HEAD >testg.txt &&
+	test_line_count = 5 testg.txt
+'
+
+test_expect_success 'single-character name is parsed correctly' '
+	git commit --author="a <a@example.com>" --allow-empty -m foo &&
+	echo "a <a@example.com>" >expect &&
+	git log -1 --format="%an <%ae>" >actual &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t6007-rev-list-cherry-pick-file.sh b/t/t6007-rev-list-cherry-pick-file.sh
index 4b8611c..28d4f6b 100755
--- a/t/t6007-rev-list-cherry-pick-file.sh
+++ b/t/t6007-rev-list-cherry-pick-file.sh
@@ -4,13 +4,14 @@
 
 . ./test-lib.sh
 
-# A---B
+# A---B---D---F
 #  \
 #   \
-#    C
+#    C---E
 #
 # B changes a file foo.c, adding a line of text.  C changes foo.c as
 # well as bar.c, but the change in foo.c was identical to change B.
+# D and C change bar in the same way, E and F differently.
 
 test_expect_success setup '
 	echo Hallo > foo &&
@@ -25,19 +26,162 @@
 	test_tick &&
 	git commit -m "C" &&
 	git tag C &&
+	echo Dello > bar &&
+	git add bar &&
+	test_tick &&
+	git commit -m "E" &&
+	git tag E &&
 	git checkout master &&
 	git checkout branch foo &&
 	test_tick &&
 	git commit -m "B" &&
-	git tag B
+	git tag B &&
+	echo Cello > bar &&
+	git add bar &&
+	test_tick &&
+	git commit -m "D" &&
+	git tag D &&
+	echo Nello > bar &&
+	git add bar &&
+	test_tick &&
+	git commit -m "F" &&
+	git tag F
+'
+
+cat >expect <<EOF
+<tags/B
+>tags/C
+EOF
+
+test_expect_success '--left-right' '
+	git rev-list --left-right B...C > actual &&
+	git name-rev --stdin --name-only --refs="*tags/*" \
+		< actual > actual.named &&
+	test_cmp actual.named expect
+'
+
+test_expect_success '--count' '
+	git rev-list --count B...C > actual &&
+	test "$(cat actual)" = 2
 '
 
 test_expect_success '--cherry-pick foo comes up empty' '
 	test -z "$(git rev-list --left-right --cherry-pick B...C -- foo)"
 '
 
+cat >expect <<EOF
+>tags/C
+EOF
+
 test_expect_success '--cherry-pick bar does not come up empty' '
-	! test -z "$(git rev-list --left-right --cherry-pick B...C -- bar)"
+	git rev-list --left-right --cherry-pick B...C -- bar > actual &&
+	git name-rev --stdin --name-only --refs="*tags/*" \
+		< actual > actual.named &&
+	test_cmp actual.named expect
+'
+
+test_expect_success 'bar does not come up empty' '
+	git rev-list --left-right B...C -- bar > actual &&
+	git name-rev --stdin --name-only --refs="*tags/*" \
+		< actual > actual.named &&
+	test_cmp actual.named expect
+'
+
+cat >expect <<EOF
+<tags/F
+>tags/E
+EOF
+
+test_expect_success '--cherry-pick bar does not come up empty (II)' '
+	git rev-list --left-right --cherry-pick F...E -- bar > actual &&
+	git name-rev --stdin --name-only --refs="*tags/*" \
+		< actual > actual.named &&
+	test_cmp actual.named expect
+'
+
+cat >expect <<EOF
++tags/F
+=tags/D
++tags/E
+=tags/C
+EOF
+
+test_expect_success '--cherry-mark' '
+	git rev-list --cherry-mark F...E -- bar > actual &&
+	git name-rev --stdin --name-only --refs="*tags/*" \
+		< actual > actual.named &&
+	test_cmp actual.named expect
+'
+
+cat >expect <<EOF
+<tags/F
+=tags/D
+>tags/E
+=tags/C
+EOF
+
+test_expect_success '--cherry-mark --left-right' '
+	git rev-list --cherry-mark --left-right F...E -- bar > actual &&
+	git name-rev --stdin --name-only --refs="*tags/*" \
+		< actual > actual.named &&
+	test_cmp actual.named expect
+'
+
+cat >expect <<EOF
+tags/E
+EOF
+
+test_expect_success '--cherry-pick --right-only' '
+	git rev-list --cherry-pick --right-only F...E -- bar > actual &&
+	git name-rev --stdin --name-only --refs="*tags/*" \
+		< actual > actual.named &&
+	test_cmp actual.named expect
+'
+
+test_expect_success '--cherry-pick --left-only' '
+	git rev-list --cherry-pick --left-only E...F -- bar > actual &&
+	git name-rev --stdin --name-only --refs="*tags/*" \
+		< actual > actual.named &&
+	test_cmp actual.named expect
+'
+
+cat >expect <<EOF
++tags/E
+=tags/C
+EOF
+
+test_expect_success '--cherry' '
+	git rev-list --cherry F...E -- bar > actual &&
+	git name-rev --stdin --name-only --refs="*tags/*" \
+		< actual > actual.named &&
+	test_cmp actual.named expect
+'
+
+cat >expect <<EOF
+1	1
+EOF
+
+test_expect_success '--cherry --count' '
+	git rev-list --cherry --count F...E -- bar > actual &&
+	test_cmp actual expect
+'
+
+cat >expect <<EOF
+2	2
+EOF
+
+test_expect_success '--cherry-mark --count' '
+	git rev-list --cherry-mark --count F...E -- bar > actual &&
+	test_cmp actual expect
+'
+
+cat >expect <<EOF
+1	1	2
+EOF
+
+test_expect_success '--cherry-mark --left-right --count' '
+	git rev-list --cherry-mark --left-right --count F...E -- bar > actual &&
+	test_cmp actual expect
 '
 
 test_expect_success '--cherry-pick with independent, but identical branches' '
@@ -54,4 +198,13 @@
 		HEAD...master -- foo)"
 '
 
+cat >expect <<EOF
+1	2
+EOF
+
+test_expect_success '--count --left-right' '
+	git rev-list --count --left-right C...D > actual &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t6009-rev-list-parent.sh b/t/t6009-rev-list-parent.sh
index c8a96a9..3050740 100755
--- a/t/t6009-rev-list-parent.sh
+++ b/t/t6009-rev-list-parent.sh
@@ -1,14 +1,15 @@
 #!/bin/sh
 
-test_description='properly cull all ancestors'
+test_description='ancestor culling and limiting by parent number'
 
 . ./test-lib.sh
 
-commit () {
-	test_tick &&
-	echo $1 >file &&
-	git commit -a -m $1 &&
-	git tag $1
+check_revlist () {
+	rev_list_args="$1" &&
+	shift &&
+	git rev-parse "$@" >expect &&
+	git rev-list $rev_list_args --all >actual &&
+	test_cmp expect actual
 }
 
 test_expect_success setup '
@@ -16,13 +17,13 @@
 	touch file &&
 	git add file &&
 
-	commit one &&
+	test_commit one &&
 
-	test_tick=$(($test_tick - 2400))
+	test_tick=$(($test_tick - 2400)) &&
 
-	commit two &&
-	commit three &&
-	commit four &&
+	test_commit two &&
+	test_commit three &&
+	test_commit four &&
 
 	git log --pretty=oneline --abbrev-commit
 '
@@ -35,4 +36,101 @@
 
 '
 
+test_expect_success 'setup roots, merges and octopuses' '
+
+	git checkout --orphan newroot &&
+	test_commit five &&
+	git checkout -b sidebranch two &&
+	test_commit six &&
+	git checkout -b anotherbranch three &&
+	test_commit seven &&
+	git checkout -b yetanotherbranch four &&
+	test_commit eight &&
+	git checkout master &&
+	test_merge normalmerge newroot &&
+	test_tick &&
+	git merge -m tripus sidebranch anotherbranch &&
+	git tag tripus &&
+	git checkout -b tetrabranch normalmerge &&
+	test_tick &&
+	git merge -m tetrapus sidebranch anotherbranch yetanotherbranch &&
+	git tag tetrapus &&
+	git checkout master
+'
+
+test_expect_success 'rev-list roots' '
+
+	check_revlist "--max-parents=0" one five
+'
+
+test_expect_success 'rev-list no merges' '
+
+	check_revlist "--max-parents=1" one eight seven six five four three two &&
+	check_revlist "--no-merges" one eight seven six five four three two
+'
+
+test_expect_success 'rev-list no octopuses' '
+
+	check_revlist "--max-parents=2" one normalmerge eight seven six five four three two
+'
+
+test_expect_success 'rev-list no roots' '
+
+	check_revlist "--min-parents=1" tetrapus tripus normalmerge eight seven six four three two
+'
+
+test_expect_success 'rev-list merges' '
+
+	check_revlist "--min-parents=2" tetrapus tripus normalmerge &&
+	check_revlist "--merges" tetrapus tripus normalmerge
+'
+
+test_expect_success 'rev-list octopus' '
+
+	check_revlist "--min-parents=3" tetrapus tripus
+'
+
+test_expect_success 'rev-list ordinary commits' '
+
+	check_revlist "--min-parents=1 --max-parents=1" eight seven six four three two
+'
+
+test_expect_success 'rev-list --merges --no-merges yields empty set' '
+
+	check_revlist "--min-parents=2 --no-merges" &&
+	check_revlist "--merges --no-merges" &&
+	check_revlist "--no-merges --merges"
+'
+
+test_expect_success 'rev-list override and infinities' '
+
+	check_revlist "--min-parents=2 --max-parents=1 --max-parents=3" tripus normalmerge &&
+	check_revlist "--min-parents=1 --min-parents=2 --max-parents=7" tetrapus tripus normalmerge &&
+	check_revlist "--min-parents=2 --max-parents=8" tetrapus tripus normalmerge &&
+	check_revlist "--min-parents=2 --max-parents=-1" tetrapus tripus normalmerge &&
+	check_revlist "--min-parents=2 --no-max-parents" tetrapus tripus normalmerge &&
+	check_revlist "--max-parents=0 --min-parents=1 --no-min-parents" one five
+'
+
+test_expect_success 'dodecapus' '
+
+	roots= &&
+	for i in 1 2 3 4 5 6 7 8 9 10 11
+	do
+		git checkout -b root$i five &&
+		test_commit $i &&
+		roots="$roots root$i" ||
+		return
+	done &&
+	git checkout master &&
+	test_tick &&
+	git merge -m dodecapus $roots &&
+	git tag dodecapus &&
+
+	check_revlist "--min-parents=4" dodecapus tetrapus &&
+	check_revlist "--min-parents=8" dodecapus &&
+	check_revlist "--min-parents=12" dodecapus &&
+	check_revlist "--min-parents=13" &&
+	check_revlist "--min-parents=4 --max-parents=11" tetrapus
+'
 test_done
diff --git a/t/t6010-merge-base.sh b/t/t6010-merge-base.sh
index 0144d9e..f80bba8 100755
--- a/t/t6010-merge-base.sh
+++ b/t/t6010-merge-base.sh
@@ -3,13 +3,11 @@
 # Copyright (c) 2005 Junio C Hamano
 #
 
-test_description='Merge base computation.
+test_description='Merge base and parent list computation.
 '
 
 . ./test-lib.sh
 
-T=$(git write-tree)
-
 M=1130000000
 Z=+0000
 
@@ -19,159 +17,217 @@
 GIT_AUTHOR_EMAIL=git@au.thor.xz
 export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL
 
-doit() {
-	OFFSET=$1; shift
-	NAME=$1; shift
-	PARENTS=
+doit () {
+	OFFSET=$1 &&
+	NAME=$2 &&
+	shift 2 &&
+
+	PARENTS= &&
 	for P
 	do
 		PARENTS="${PARENTS}-p $P "
-	done
-	GIT_COMMITTER_DATE="$(($M + $OFFSET)) $Z"
-	GIT_AUTHOR_DATE=$GIT_COMMITTER_DATE
-	export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
-	commit=$(echo $NAME | git commit-tree $T $PARENTS)
-	echo $commit >.git/refs/tags/$NAME
+	done &&
+
+	GIT_COMMITTER_DATE="$(($M + $OFFSET)) $Z" &&
+	GIT_AUTHOR_DATE=$GIT_COMMITTER_DATE &&
+	export GIT_COMMITTER_DATE GIT_AUTHOR_DATE &&
+
+	commit=$(echo $NAME | git commit-tree $T $PARENTS) &&
+
+	echo $commit >.git/refs/tags/$NAME &&
 	echo $commit
 }
 
-#  E---D---C---B---A
-#  \'-_         \   \
-#   \  `---------G   \
-#    \                \
-#     F----------------H
+test_expect_success 'setup' '
+	T=$(git mktree </dev/null)
+'
 
-# Setup...
-E=$(doit 5 E)
-D=$(doit 4 D $E)
-F=$(doit 6 F $E)
-C=$(doit 3 C $D)
-B=$(doit 2 B $C)
-A=$(doit 1 A $B)
-G=$(doit 7 G $B $E)
-H=$(doit 8 H $A $F)
+test_expect_success 'set up G and H' '
+	# E---D---C---B---A
+	# \"-_         \   \
+	#  \  `---------G   \
+	#   \                \
+	#    F----------------H
+	E=$(doit 5 E) &&
+	D=$(doit 4 D $E) &&
+	F=$(doit 6 F $E) &&
+	C=$(doit 3 C $D) &&
+	B=$(doit 2 B $C) &&
+	A=$(doit 1 A $B) &&
+	G=$(doit 7 G $B $E) &&
+	H=$(doit 8 H $A $F)
+'
 
-test_expect_success 'compute merge-base (single)' \
-    'MB=$(git merge-base G H) &&
-     expr "$(git name-rev "$MB")" : "[0-9a-f]* tags/B"'
+test_expect_success 'merge-base G H' '
+	git name-rev $B >expected &&
 
-test_expect_success 'compute merge-base (all)' \
-    'MB=$(git merge-base --all G H) &&
-     expr "$(git name-rev "$MB")" : "[0-9a-f]* tags/B"'
+	MB=$(git merge-base G H) &&
+	git name-rev "$MB" >actual.single &&
 
-test_expect_success 'compute merge-base with show-branch' \
-    'MB=$(git show-branch --merge-base G H) &&
-     expr "$(git name-rev "$MB")" : "[0-9a-f]* tags/B"'
+	MB=$(git merge-base --all G H) &&
+	git name-rev "$MB" >actual.all &&
 
-# 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.
+	MB=$(git show-branch --merge-base G H) &&
+	git name-rev "$MB" >actual.sb &&
 
-S=$(doit  0 S)
+	test_cmp expected actual.single &&
+	test_cmp expected actual.all &&
+	test_cmp expected actual.sb
+'
 
-C0=$(doit -3 C0 $S)
-C1=$(doit -2 C1 $C0)
-C2=$(doit -1 C2 $C1)
+test_expect_success 'merge-base/show-branch --independent' '
+	git name-rev "$H" >expected1 &&
+	git name-rev "$H" "$G" >expected2 &&
 
-L0=$(doit  1 L0 $S)
-L1=$(doit  2 L1 $L0)
-L2=$(doit  3 L2 $L1)
+	parents=$(git merge-base --independent H) &&
+	git name-rev $parents >actual1.mb &&
+	parents=$(git merge-base --independent A H G) &&
+	git name-rev $parents >actual2.mb &&
 
-R0=$(doit  1 R0 $S)
-R1=$(doit  2 R1 $R0)
-R2=$(doit  3 R2 $R1)
+	parents=$(git show-branch --independent H) &&
+	git name-rev $parents >actual1.sb &&
+	parents=$(git show-branch --independent A H G) &&
+	git name-rev $parents >actual2.sb &&
 
-PL=$(doit  4 PL $L2 $C2)
-PR=$(doit  4 PR $C2 $R2)
+	test_cmp expected1 actual1.mb &&
+	test_cmp expected2 actual2.mb &&
+	test_cmp expected1 actual1.sb &&
+	test_cmp expected2 actual2.sb
+'
 
-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 'unsynchronized clocks' '
+	# This test is 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.
 
-test_expect_success 'compute merge-base (all)' \
-    'MB=$(git merge-base --all PL PR) &&
-     expr "$(git name-rev "$MB")" : "[0-9a-f]* tags/C2"'
+	S=$(doit  0 S) &&
 
-# Another set to demonstrate base between one commit and a merge
-# in the documentation.
-#
-# * C (MMC) * B (MMB) * A  (MMA)
-# * o       * o       * o
-# * o       * o       * o
-# * o       * o       * o
-# * o       | _______/
-# |         |/
-# |         * 1 (MM1)
-# | _______/
-# |/
-# * root (MMR)
+	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) &&
+
+	git name-rev $C2 >expected &&
+
+	MB=$(git merge-base PL PR) &&
+	git name-rev "$MB" >actual.single &&
+
+	MB=$(git merge-base --all PL PR) &&
+	git name-rev "$MB" >actual.all &&
+
+	test_cmp expected actual.single &&
+	test_cmp expected actual.all
+'
+
+test_expect_success '--independent with unsynchronized clocks' '
+	IB=$(doit 0 IB) &&
+	I1=$(doit -10 I1 $IB) &&
+	I2=$(doit  -9 I2 $I1) &&
+	I3=$(doit  -8 I3 $I2) &&
+	I4=$(doit  -7 I4 $I3) &&
+	I5=$(doit  -6 I5 $I4) &&
+	I6=$(doit  -5 I6 $I5) &&
+	I7=$(doit  -4 I7 $I6) &&
+	I8=$(doit  -3 I8 $I7) &&
+	IH=$(doit  -2 IH $I8) &&
+
+	echo $IH >expected &&
+	git merge-base --independent IB IH >actual &&
+	test_cmp expected actual
+'
 
 test_expect_success 'merge-base for octopus-step (setup)' '
-	test_tick && git commit --allow-empty -m root && git tag MMR &&
-	test_tick && git commit --allow-empty -m 1 && git tag MM1 &&
-	test_tick && git commit --allow-empty -m o &&
-	test_tick && git commit --allow-empty -m o &&
-	test_tick && git commit --allow-empty -m o &&
-	test_tick && git commit --allow-empty -m A && git tag MMA &&
+	# Another set to demonstrate base between one commit and a merge
+	# in the documentation.
+	#
+	# * C (MMC) * B (MMB) * A  (MMA)
+	# * o       * o       * o
+	# * o       * o       * o
+	# * o       * o       * o
+	# * o       | _______/
+	# |         |/
+	# |         * 1 (MM1)
+	# | _______/
+	# |/
+	# * root (MMR)
+
+	test_commit MMR &&
+	test_commit MM1 &&
+	test_commit MM-o &&
+	test_commit MM-p &&
+	test_commit MM-q &&
+	test_commit MMA &&
 	git checkout MM1 &&
-	test_tick && git commit --allow-empty -m o &&
-	test_tick && git commit --allow-empty -m o &&
-	test_tick && git commit --allow-empty -m o &&
-	test_tick && git commit --allow-empty -m B && git tag MMB &&
+	test_commit MM-r &&
+	test_commit MM-s &&
+	test_commit MM-t &&
+	test_commit MMB &&
 	git checkout MMR &&
-	test_tick && git commit --allow-empty -m o &&
-	test_tick && git commit --allow-empty -m o &&
-	test_tick && git commit --allow-empty -m o &&
-	test_tick && git commit --allow-empty -m o &&
-	test_tick && git commit --allow-empty -m C && git tag MMC
+	test_commit MM-u &&
+	test_commit MM-v &&
+	test_commit MM-w &&
+	test_commit MM-x &&
+	test_commit MMC
 '
 
 test_expect_success 'merge-base A B C' '
-	MB=$(git merge-base --all MMA MMB MMC) &&
-	MM1=$(git rev-parse --verify MM1) &&
-	test "$MM1" = "$MB"
+	git rev-parse --verify MM1 >expected &&
+	git rev-parse --verify MMR >expected.sb &&
+
+	git merge-base --all MMA MMB MMC >actual &&
+	git merge-base --all --octopus MMA MMB MMC >actual.common &&
+	git show-branch --merge-base MMA MMB MMC >actual.sb &&
+
+	test_cmp expected actual &&
+	test_cmp expected.sb actual.common &&
+	test_cmp expected.sb actual.sb
 '
 
-test_expect_success 'merge-base A B C using show-branch' '
-	MB=$(git show-branch --merge-base MMA MMB MMC) &&
-	MMR=$(git rev-parse --verify MMR) &&
-	test "$MMR" = "$MB"
-'
-
-test_expect_success 'criss-cross merge-base for octopus-step (setup)' '
+test_expect_success 'criss-cross merge-base for octopus-step' '
 	git reset --hard MMR &&
-	test_tick && git commit --allow-empty -m 1 && git tag CC1 &&
+	test_commit CC1 &&
 	git reset --hard E &&
-	test_tick && git commit --allow-empty -m 2 && git tag CC2 &&
-	test_tick && git merge -s ours CC1 &&
-	test_tick && git commit --allow-empty -m o &&
-	test_tick && git commit --allow-empty -m B && git tag CCB &&
+	test_commit CC2 &&
+	test_tick &&
+	git merge -s ours CC1 &&
+	test_commit CC-o &&
+	test_commit CCB &&
 	git reset --hard CC1 &&
-	test_tick && git merge -s ours CC2 &&
-	test_tick && git commit --allow-empty -m A && git tag CCA
-'
+	git merge -s ours CC2 &&
+	test_commit CCA &&
 
-test_expect_success 'merge-base B A^^ A^^2' '
-	MB0=$(git merge-base --all CCB CCA^^ CCA^^2 | sort) &&
-	MB1=$(git rev-parse CC1 CC2 | sort) &&
-	test "$MB0" = "$MB1"
+	git rev-parse CC1 CC2 >expected &&
+	git merge-base --all CCB CCA^^ CCA^^2 >actual &&
+
+	sort expected >expected.sorted &&
+	sort actual >actual.sorted &&
+	test_cmp expected.sorted actual.sorted
 '
 
 test_done
diff --git a/t/t6011-rev-list-with-bad-commit.sh b/t/t6011-rev-list-with-bad-commit.sh
index e51eb41..bbb0581 100755
--- a/t/t6011-rev-list-with-bad-commit.sh
+++ b/t/t6011-rev-list-with-bad-commit.sh
@@ -37,7 +37,7 @@
 
 test_expect_success 'corrupt second commit object' \
    '
-   perl -i.bak -pe "s/second commit/socond commit/" .git/objects/pack/*.pack &&
+   "$PERL_PATH" -i.bak -pe "s/second commit/socond commit/" .git/objects/pack/*.pack &&
    test_must_fail git fsck --full
    '
 
diff --git a/t/t6012-rev-list-simplify.sh b/t/t6012-rev-list-simplify.sh
index 510bb96..839ad97 100755
--- a/t/t6012-rev-list-simplify.sh
+++ b/t/t6012-rev-list-simplify.sh
@@ -8,9 +8,6 @@
 	git tag "$1"
 }
 
-_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
-_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
-
 unnote () {
 	git name-rev --tags --stdin | sed -e "s|$_x40 (tags/\([^)]*\)) |\1 |g"
 }
@@ -89,5 +86,6 @@
 check_result 'I E C B A' --simplify-merges -- file
 check_result 'I B A' -- file
 check_result 'I B A' --topo-order -- file
+check_result 'H' --first-parent -- another-file
 
 test_done
diff --git a/t/t6013-rev-list-reverse-parents.sh b/t/t6013-rev-list-reverse-parents.sh
index 59fc2f0..892a537 100755
--- a/t/t6013-rev-list-reverse-parents.sh
+++ b/t/t6013-rev-list-reverse-parents.sh
@@ -25,7 +25,7 @@
 
 test_expect_success '--reverse --parents --full-history combines correctly' '
 	git rev-list --parents --full-history master -- foo |
-		perl -e "print reverse <>" > expected &&
+		"$PERL_PATH" -e "print reverse <>" > expected &&
 	git rev-list --reverse --parents --full-history master -- foo \
 		> actual &&
 	test_cmp actual expected
@@ -33,7 +33,7 @@
 
 test_expect_success '--boundary does too' '
 	git rev-list --boundary --parents --full-history master ^root -- foo |
-		perl -e "print reverse <>" > expected &&
+		"$PERL_PATH" -e "print reverse <>" > expected &&
 	git rev-list --boundary --reverse --parents --full-history \
 		master ^root -- foo > actual &&
 	test_cmp actual expected
diff --git a/t/t6015-rev-list-show-all-parents.sh b/t/t6015-rev-list-show-all-parents.sh
new file mode 100755
index 0000000..8b146fb
--- /dev/null
+++ b/t/t6015-rev-list-show-all-parents.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+test_description='--show-all --parents does not rewrite TREESAME commits'
+
+. ./test-lib.sh
+
+test_expect_success 'set up --show-all --parents test' '
+	test_commit one foo.txt &&
+	commit1=`git rev-list -1 HEAD` &&
+	test_commit two bar.txt &&
+	commit2=`git rev-list -1 HEAD` &&
+	test_commit three foo.txt &&
+	commit3=`git rev-list -1 HEAD`
+	'
+
+test_expect_success '--parents rewrites TREESAME parents correctly' '
+	echo $commit3 $commit1 > expected &&
+	echo $commit1 >> expected &&
+	git rev-list --parents HEAD -- foo.txt > actual &&
+	test_cmp expected actual
+	'
+
+test_expect_success '--parents --show-all does not rewrites TREESAME parents' '
+	echo $commit3 $commit2 > expected &&
+	echo $commit2 $commit1 >> expected &&
+	echo $commit1 >> expected &&
+	git rev-list --parents --show-all HEAD -- foo.txt > actual &&
+	test_cmp expected actual
+	'
+
+test_done
diff --git a/t/t6016-rev-list-graph-simplify-history.sh b/t/t6016-rev-list-graph-simplify-history.sh
new file mode 100755
index 0000000..f7181d1
--- /dev/null
+++ b/t/t6016-rev-list-graph-simplify-history.sh
@@ -0,0 +1,267 @@
+#!/bin/sh
+
+# There's more than one "correct" way to represent the history graphically.
+# These tests depend on the current behavior of the graphing code.  If the
+# graphing code is ever changed to draw the output differently, these tests
+# cases will need to be updated to know about the new layout.
+
+test_description='--graph and simplified history'
+
+. ./test-lib.sh
+
+test_expect_success 'set up rev-list --graph test' '
+	# 3 commits on branch A
+	test_commit A1 foo.txt &&
+	test_commit A2 bar.txt &&
+	test_commit A3 bar.txt &&
+	git branch -m master A &&
+
+	# 2 commits on branch B, started from A1
+	git checkout -b B A1 &&
+	test_commit B1 foo.txt &&
+	test_commit B2 abc.txt &&
+
+	# 2 commits on branch C, started from A2
+	git checkout -b C A2 &&
+	test_commit C1 xyz.txt &&
+	test_commit C2 xyz.txt &&
+
+	# Octopus merge B and C into branch A
+	git checkout A &&
+	git merge B C &&
+	git tag A4 &&
+
+	test_commit A5 bar.txt &&
+
+	# More commits on C, then merge C into A
+	git checkout C &&
+	test_commit C3 foo.txt &&
+	test_commit C4 bar.txt &&
+	git checkout A &&
+	git merge -s ours C &&
+	git tag A6 &&
+
+	test_commit A7 bar.txt &&
+
+	# Store commit names in variables for later use
+	A1=$(git rev-parse --verify A1) &&
+	A2=$(git rev-parse --verify A2) &&
+	A3=$(git rev-parse --verify A3) &&
+	A4=$(git rev-parse --verify A4) &&
+	A5=$(git rev-parse --verify A5) &&
+	A6=$(git rev-parse --verify A6) &&
+	A7=$(git rev-parse --verify A7) &&
+	B1=$(git rev-parse --verify B1) &&
+	B2=$(git rev-parse --verify B2) &&
+	C1=$(git rev-parse --verify C1) &&
+	C2=$(git rev-parse --verify C2) &&
+	C3=$(git rev-parse --verify C3) &&
+	C4=$(git rev-parse --verify C4)
+	'
+
+test_expect_success '--graph --all' '
+	rm -f expected &&
+	echo "* $A7" >> expected &&
+	echo "*   $A6" >> expected &&
+	echo "|\\  " >> expected &&
+	echo "| * $C4" >> expected &&
+	echo "| * $C3" >> expected &&
+	echo "* | $A5" >> expected &&
+	echo "| |     " >> expected &&
+	echo "|  \\    " >> expected &&
+	echo "*-. \\   $A4" >> expected &&
+	echo "|\\ \\ \\  " >> expected &&
+	echo "| | |/  " >> expected &&
+	echo "| | * $C2" >> expected &&
+	echo "| | * $C1" >> expected &&
+	echo "| * | $B2" >> expected &&
+	echo "| * | $B1" >> expected &&
+	echo "* | | $A3" >> expected &&
+	echo "| |/  " >> expected &&
+	echo "|/|   " >> expected &&
+	echo "* | $A2" >> expected &&
+	echo "|/  " >> expected &&
+	echo "* $A1" >> expected &&
+	git rev-list --graph --all > actual &&
+	test_cmp expected actual
+	'
+
+# Make sure the graph_is_interesting() code still realizes
+# that undecorated merges are interesting, even with --simplify-by-decoration
+test_expect_success '--graph --simplify-by-decoration' '
+	rm -f expected &&
+	git tag -d A4 &&
+	echo "* $A7" >> expected &&
+	echo "*   $A6" >> expected &&
+	echo "|\\  " >> expected &&
+	echo "| * $C4" >> expected &&
+	echo "| * $C3" >> expected &&
+	echo "* | $A5" >> expected &&
+	echo "| |     " >> expected &&
+	echo "|  \\    " >> expected &&
+	echo "*-. \\   $A4" >> expected &&
+	echo "|\\ \\ \\  " >> expected &&
+	echo "| | |/  " >> expected &&
+	echo "| | * $C2" >> expected &&
+	echo "| | * $C1" >> expected &&
+	echo "| * | $B2" >> expected &&
+	echo "| * | $B1" >> expected &&
+	echo "* | | $A3" >> expected &&
+	echo "| |/  " >> expected &&
+	echo "|/|   " >> expected &&
+	echo "* | $A2" >> expected &&
+	echo "|/  " >> expected &&
+	echo "* $A1" >> expected &&
+	git rev-list --graph --all --simplify-by-decoration > actual &&
+	test_cmp expected actual
+	'
+
+test_expect_success 'setup: get rid of decorations on B' '
+	git tag -d B2 &&
+	git tag -d B1 &&
+	git branch -d B
+'
+
+# Graph with branch B simplified away
+test_expect_success '--graph --simplify-by-decoration prune branch B' '
+	rm -f expected &&
+	echo "* $A7" >> expected &&
+	echo "*   $A6" >> expected &&
+	echo "|\\  " >> expected &&
+	echo "| * $C4" >> expected &&
+	echo "| * $C3" >> expected &&
+	echo "* | $A5" >> expected &&
+	echo "* |   $A4" >> expected &&
+	echo "|\\ \\  " >> expected &&
+	echo "| |/  " >> expected &&
+	echo "| * $C2" >> expected &&
+	echo "| * $C1" >> expected &&
+	echo "* | $A3" >> expected &&
+	echo "|/  " >> expected &&
+	echo "* $A2" >> expected &&
+	echo "* $A1" >> expected &&
+	git rev-list --graph --simplify-by-decoration --all > actual &&
+	test_cmp expected actual
+	'
+
+test_expect_success '--graph --full-history -- bar.txt' '
+	rm -f expected &&
+	echo "* $A7" >> expected &&
+	echo "*   $A6" >> expected &&
+	echo "|\\  " >> expected &&
+	echo "| * $C4" >> expected &&
+	echo "* | $A5" >> expected &&
+	echo "* |   $A4" >> expected &&
+	echo "|\\ \\  " >> expected &&
+	echo "| |/  " >> expected &&
+	echo "* | $A3" >> expected &&
+	echo "|/  " >> expected &&
+	echo "* $A2" >> expected &&
+	git rev-list --graph --full-history --all -- bar.txt > actual &&
+	test_cmp expected actual
+	'
+
+test_expect_success '--graph --full-history --simplify-merges -- bar.txt' '
+	rm -f expected &&
+	echo "* $A7" >> expected &&
+	echo "*   $A6" >> expected &&
+	echo "|\\  " >> expected &&
+	echo "| * $C4" >> expected &&
+	echo "* | $A5" >> expected &&
+	echo "* | $A3" >> expected &&
+	echo "|/  " >> expected &&
+	echo "* $A2" >> expected &&
+	git rev-list --graph --full-history --simplify-merges --all \
+		-- bar.txt > actual &&
+	test_cmp expected actual
+	'
+
+test_expect_success '--graph -- bar.txt' '
+	rm -f expected &&
+	echo "* $A7" >> expected &&
+	echo "* $A5" >> expected &&
+	echo "* $A3" >> expected &&
+	echo "| * $C4" >> expected &&
+	echo "|/  " >> expected &&
+	echo "* $A2" >> expected &&
+	git rev-list --graph --all -- bar.txt > actual &&
+	test_cmp expected actual
+	'
+
+test_expect_success '--graph --sparse -- bar.txt' '
+	rm -f expected &&
+	echo "* $A7" >> expected &&
+	echo "* $A6" >> expected &&
+	echo "* $A5" >> expected &&
+	echo "* $A4" >> expected &&
+	echo "* $A3" >> expected &&
+	echo "| * $C4" >> expected &&
+	echo "| * $C3" >> expected &&
+	echo "| * $C2" >> expected &&
+	echo "| * $C1" >> expected &&
+	echo "|/  " >> expected &&
+	echo "* $A2" >> expected &&
+	echo "* $A1" >> expected &&
+	git rev-list --graph --sparse --all -- bar.txt > actual &&
+	test_cmp expected actual
+	'
+
+test_expect_success '--graph ^C4' '
+	rm -f expected &&
+	echo "* $A7" >> expected &&
+	echo "* $A6" >> expected &&
+	echo "* $A5" >> expected &&
+	echo "*   $A4" >> expected &&
+	echo "|\\  " >> expected &&
+	echo "| * $B2" >> expected &&
+	echo "| * $B1" >> expected &&
+	echo "* $A3" >> expected &&
+	git rev-list --graph --all ^C4 > actual &&
+	test_cmp expected actual
+	'
+
+test_expect_success '--graph ^C3' '
+	rm -f expected &&
+	echo "* $A7" >> expected &&
+	echo "*   $A6" >> expected &&
+	echo "|\\  " >> expected &&
+	echo "| * $C4" >> expected &&
+	echo "* $A5" >> expected &&
+	echo "*   $A4" >> expected &&
+	echo "|\\  " >> expected &&
+	echo "| * $B2" >> expected &&
+	echo "| * $B1" >> expected &&
+	echo "* $A3" >> expected &&
+	git rev-list --graph --all ^C3 > actual &&
+	test_cmp expected actual
+	'
+
+# I don't think the ordering of the boundary commits is really
+# that important, but this test depends on it.  If the ordering ever changes
+# in the code, we'll need to update this test.
+test_expect_success '--graph --boundary ^C3' '
+	rm -f expected &&
+	echo "* $A7" >> expected &&
+	echo "*   $A6" >> expected &&
+	echo "|\\  " >> expected &&
+	echo "| * $C4" >> expected &&
+	echo "* | $A5" >> expected &&
+	echo "| |     " >> expected &&
+	echo "|  \\    " >> expected &&
+	echo "*-. \\   $A4" >> expected &&
+	echo "|\\ \\ \\  " >> expected &&
+	echo "| * | | $B2" >> expected &&
+	echo "| * | | $B1" >> expected &&
+	echo "* | | | $A3" >> expected &&
+	echo "o | | | $A2" >> expected &&
+	echo "|/ / /  " >> expected &&
+	echo "o | | $A1" >> expected &&
+	echo " / /  " >> expected &&
+	echo "| o $C3" >> expected &&
+	echo "|/  " >> expected &&
+	echo "o $C2" >> expected &&
+	git rev-list --graph --boundary --all ^C3 > actual &&
+	test_cmp expected actual
+	'
+
+test_done
diff --git a/t/t6017-rev-list-stdin.sh b/t/t6017-rev-list-stdin.sh
new file mode 100755
index 0000000..667b375
--- /dev/null
+++ b/t/t6017-rev-list-stdin.sh
@@ -0,0 +1,78 @@
+#!/bin/sh
+#
+# Copyright (c) 2009, Junio C Hamano
+#
+
+test_description='log family learns --stdin'
+
+. ./test-lib.sh
+
+check () {
+	for cmd in rev-list "log --stat"
+	do
+		for i in "$@"
+		do
+			printf "%s\n" $i
+		done >input &&
+		test_expect_success "check $cmd $*" '
+			git $cmd $(cat input) >expect &&
+			git $cmd --stdin <input >actual &&
+			sed -e "s/^/input /" input &&
+			sed -e "s/^/output /" expect &&
+			test_cmp expect actual
+		'
+	done
+}
+
+them='1 2 3 4 5 6 7'
+
+test_expect_success setup '
+	(
+		for i in 0 $them
+		do
+			for j in $them
+			do
+				echo $i.$j >file-$j &&
+				git add file-$j || exit
+			done &&
+			test_tick &&
+			git commit -m $i || exit
+		done &&
+		for i in $them
+		do
+			git checkout -b side-$i master~$i &&
+			echo updated $i >file-$i &&
+			git add file-$i &&
+			test_tick &&
+			git commit -m side-$i || exit
+		done
+	)
+'
+
+check master
+check side-1 ^side-4
+check side-1 ^side-7 --
+check side-1 ^side-7 -- file-1
+check side-1 ^side-7 -- file-2
+check side-3 ^side-4 -- file-3
+check side-3 ^side-2
+check side-3 ^side-2 -- file-1
+
+test_expect_success 'not only --stdin' '
+	cat >expect <<-EOF &&
+	7
+
+	file-1
+	file-2
+	EOF
+	cat >input <<-EOF &&
+	^master^
+	--
+	file-2
+	EOF
+	git log --pretty=tformat:%s --name-only --stdin master -- file-1 \
+		<input >actual &&
+	test_cmp expect actual
+'
+
+test_done
diff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh
new file mode 100755
index 0000000..f00cebf
--- /dev/null
+++ b/t/t6018-rev-list-glob.sh
@@ -0,0 +1,266 @@
+#!/bin/sh
+
+test_description='rev-list/rev-parse --glob'
+
+. ./test-lib.sh
+
+commit () {
+	test_tick &&
+	echo $1 > foo &&
+	git add foo &&
+	git commit -m "$1"
+}
+
+compare () {
+	# Split arguments on whitespace.
+	git $1 $2 >expected &&
+	git $1 $3 >actual &&
+	test_cmp expected actual
+}
+
+test_expect_success 'setup' '
+
+	commit master &&
+	git checkout -b subspace/one master &&
+	commit one &&
+	git checkout -b subspace/two master &&
+	commit two &&
+	git checkout -b subspace-x master &&
+	commit subspace-x &&
+	git checkout -b other/three master &&
+	commit three &&
+	git checkout -b someref master &&
+	commit some &&
+	git checkout master &&
+	commit master2 &&
+	git tag foo/bar master &&
+	commit master3 &&
+	git update-ref refs/remotes/foo/baz master &&
+	commit master4
+'
+
+test_expect_success 'rev-parse --glob=refs/heads/subspace/*' '
+
+	compare rev-parse "subspace/one subspace/two" "--glob=refs/heads/subspace/*"
+
+'
+
+test_expect_success 'rev-parse --glob=heads/subspace/*' '
+
+	compare rev-parse "subspace/one subspace/two" "--glob=heads/subspace/*"
+
+'
+
+test_expect_success 'rev-parse --glob=refs/heads/subspace/' '
+
+	compare rev-parse "subspace/one subspace/two" "--glob=refs/heads/subspace/"
+
+'
+
+test_expect_success 'rev-parse --glob=heads/subspace/' '
+
+	compare rev-parse "subspace/one subspace/two" "--glob=heads/subspace/"
+
+'
+
+test_expect_success 'rev-parse --glob=heads/subspace' '
+
+	compare rev-parse "subspace/one subspace/two" "--glob=heads/subspace"
+
+'
+
+test_expect_failure 'rev-parse accepts --glob as detached option' '
+
+	compare rev-parse "subspace/one subspace/two" "--glob heads/subspace"
+
+'
+
+test_expect_failure 'rev-parse is not confused by option-like glob' '
+
+	compare rev-parse "master" "--glob --symbolic master"
+
+'
+
+test_expect_success 'rev-parse --branches=subspace/*' '
+
+	compare rev-parse "subspace/one subspace/two" "--branches=subspace/*"
+
+'
+
+test_expect_success 'rev-parse --branches=subspace/' '
+
+	compare rev-parse "subspace/one subspace/two" "--branches=subspace/"
+
+'
+
+test_expect_success 'rev-parse --branches=subspace' '
+
+	compare rev-parse "subspace/one subspace/two" "--branches=subspace"
+
+'
+
+test_expect_success 'rev-parse --glob=heads/subspace/* --glob=heads/other/*' '
+
+	compare rev-parse "subspace/one subspace/two other/three" "--glob=heads/subspace/* --glob=heads/other/*"
+
+'
+
+test_expect_success 'rev-parse --glob=heads/someref/* master' '
+
+	compare rev-parse "master" "--glob=heads/someref/* master"
+
+'
+
+test_expect_success 'rev-parse --glob=heads/*' '
+
+	compare rev-parse "master other/three someref subspace-x subspace/one subspace/two" "--glob=heads/*"
+
+'
+
+test_expect_success 'rev-parse --tags=foo' '
+
+	compare rev-parse "foo/bar" "--tags=foo"
+
+'
+
+test_expect_success 'rev-parse --remotes=foo' '
+
+	compare rev-parse "foo/baz" "--remotes=foo"
+
+'
+
+test_expect_success 'rev-list --glob=refs/heads/subspace/*' '
+
+	compare rev-list "subspace/one subspace/two" "--glob=refs/heads/subspace/*"
+
+'
+
+test_expect_success 'rev-list --glob refs/heads/subspace/*' '
+
+	compare rev-list "subspace/one subspace/two" "--glob refs/heads/subspace/*"
+
+'
+
+test_expect_success 'rev-list not confused by option-like --glob arg' '
+
+	compare rev-list "master" "--glob -0 master"
+
+'
+
+test_expect_success 'rev-list --glob=heads/subspace/*' '
+
+	compare rev-list "subspace/one subspace/two" "--glob=heads/subspace/*"
+
+'
+
+test_expect_success 'rev-list --glob=refs/heads/subspace/' '
+
+	compare rev-list "subspace/one subspace/two" "--glob=refs/heads/subspace/"
+
+'
+
+test_expect_success 'rev-list --glob=heads/subspace/' '
+
+	compare rev-list "subspace/one subspace/two" "--glob=heads/subspace/"
+
+'
+
+test_expect_success 'rev-list --glob=heads/subspace' '
+
+	compare rev-list "subspace/one subspace/two" "--glob=heads/subspace"
+
+'
+
+test_expect_success 'rev-list --branches=subspace/*' '
+
+	compare rev-list "subspace/one subspace/two" "--branches=subspace/*"
+
+'
+
+test_expect_success 'rev-list --branches=subspace/' '
+
+	compare rev-list "subspace/one subspace/two" "--branches=subspace/"
+
+'
+
+test_expect_success 'rev-list --branches=subspace' '
+
+	compare rev-list "subspace/one subspace/two" "--branches=subspace"
+
+'
+
+test_expect_success 'rev-list --branches' '
+
+	compare rev-list "master subspace-x someref other/three subspace/one subspace/two" "--branches"
+
+'
+
+test_expect_success 'rev-list --glob=heads/someref/* master' '
+
+	compare rev-list "master" "--glob=heads/someref/* master"
+
+'
+
+test_expect_success 'rev-list --glob=heads/subspace/* --glob=heads/other/*' '
+
+	compare rev-list "subspace/one subspace/two other/three" "--glob=heads/subspace/* --glob=heads/other/*"
+
+'
+
+test_expect_success 'rev-list --glob=heads/*' '
+
+	compare rev-list "master other/three someref subspace-x subspace/one subspace/two" "--glob=heads/*"
+
+'
+
+test_expect_success 'rev-list --tags=foo' '
+
+	compare rev-list "foo/bar" "--tags=foo"
+
+'
+
+test_expect_success 'rev-list --tags' '
+
+	compare rev-list "foo/bar" "--tags"
+
+'
+
+test_expect_success 'rev-list --remotes=foo' '
+
+	compare rev-list "foo/baz" "--remotes=foo"
+
+'
+
+test_expect_success 'shortlog accepts --glob/--tags/--remotes' '
+
+	compare shortlog "subspace/one subspace/two" --branches=subspace &&
+	compare shortlog \
+	  "master subspace-x someref other/three subspace/one subspace/two" \
+	  --branches &&
+	compare shortlog master "--glob=heads/someref/* master" &&
+	compare shortlog "subspace/one subspace/two other/three" \
+	  "--glob=heads/subspace/* --glob=heads/other/*" &&
+	compare shortlog \
+	  "master other/three someref subspace-x subspace/one subspace/two" \
+	  "--glob=heads/*" &&
+	compare shortlog foo/bar --tags=foo &&
+	compare shortlog foo/bar --tags &&
+	compare shortlog foo/baz --remotes=foo
+
+'
+
+test_expect_failure 'shortlog accepts --glob as detached option' '
+
+	compare shortlog \
+	  "master other/three someref subspace-x subspace/one subspace/two" \
+	  "--glob heads/*"
+
+'
+
+test_expect_failure 'shortlog --glob is not confused by option-like argument' '
+
+	compare shortlog master "--glob -e master"
+
+'
+
+test_done
diff --git a/t/t6019-rev-list-ancestry-path.sh b/t/t6019-rev-list-ancestry-path.sh
new file mode 100755
index 0000000..39b4cb0
--- /dev/null
+++ b/t/t6019-rev-list-ancestry-path.sh
@@ -0,0 +1,111 @@
+#!/bin/sh
+
+test_description='--ancestry-path'
+
+#          D---E-------F
+#         /     \       \
+#    B---C---G---H---I---J
+#   /                     \
+#  A-------K---------------L--M
+#
+#  D..M                 == E F G H I J K L M
+#  --ancestry-path D..M == E F H I J L M
+#
+#  D..M -- M.t                 == M
+#  --ancestry-path D..M -- M.t == M
+
+. ./test-lib.sh
+
+test_merge () {
+	test_tick &&
+	git merge -s ours -m "$2" "$1" &&
+	git tag "$2"
+}
+
+test_expect_success setup '
+	test_commit A &&
+	test_commit B &&
+	test_commit C &&
+	test_commit D &&
+	test_commit E &&
+	test_commit F &&
+	git reset --hard C &&
+	test_commit G &&
+	test_merge E H &&
+	test_commit I &&
+	test_merge F J &&
+	git reset --hard A &&
+	test_commit K &&
+	test_merge J L &&
+	test_commit M
+'
+
+test_expect_success 'rev-list D..M' '
+	for c in E F G H I J K L M; do echo $c; done >expect &&
+	git rev-list --format=%s D..M |
+	sed -e "/^commit /d" |
+	sort >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'rev-list --ancestry-path D..M' '
+	for c in E F H I J L M; do echo $c; done >expect &&
+	git rev-list --ancestry-path --format=%s D..M |
+	sed -e "/^commit /d" |
+	sort >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'rev-list D..M -- M.t' '
+	echo M >expect &&
+	git rev-list --format=%s D..M -- M.t |
+	sed -e "/^commit /d" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'rev-list --ancestry-patch D..M -- M.t' '
+	echo M >expect &&
+	git rev-list --ancestry-path --format=%s D..M -- M.t |
+	sed -e "/^commit /d" >actual &&
+	test_cmp expect actual
+'
+
+#   b---bc
+#  / \ /
+# a   X
+#  \ / \
+#   c---cb
+#
+# All refnames prefixed with 'x' to avoid confusion with the tags
+# generated by test_commit on case-insensitive systems.
+test_expect_success 'setup criss-cross' '
+	mkdir criss-cross &&
+	(cd criss-cross &&
+	 git init &&
+	 test_commit A &&
+	 git checkout -b xb master &&
+	 test_commit B &&
+	 git checkout -b xc master &&
+	 test_commit C &&
+	 git checkout -b xbc xb -- &&
+	 git merge xc &&
+	 git checkout -b xcb xc -- &&
+	 git merge xb &&
+	 git checkout master)
+'
+
+# no commits in bc descend from cb
+test_expect_success 'criss-cross: rev-list --ancestry-path cb..bc' '
+	(cd criss-cross &&
+	 git rev-list --ancestry-path xcb..xbc > actual &&
+	 test -z "$(cat actual)")
+'
+
+# no commits in repository descend from cb
+test_expect_success 'criss-cross: rev-list --ancestry-path --all ^cb' '
+	(cd criss-cross &&
+	 git rev-list --ancestry-path --all ^xcb > actual &&
+	 test -z "$(cat actual)")
+'
+
+test_done
diff --git a/t/t6020-merge-df.sh b/t/t6020-merge-df.sh
index e71c687..27c3d73 100755
--- a/t/t6020-merge-df.sh
+++ b/t/t6020-merge-df.sh
@@ -6,23 +6,28 @@
 test_description='Test merge with directory/file conflicts'
 . ./test-lib.sh
 
-test_expect_success 'prepare repository' \
-'echo "Hello" > init &&
-git add init &&
-git commit -m "Initial commit" &&
-git branch B &&
-mkdir dir &&
-echo "foo" > dir/foo &&
-git add dir/foo &&
-git commit -m "File: dir/foo" &&
-git checkout B &&
-echo "file dir" > dir &&
-git add dir &&
-git commit -m "File: dir"'
+test_expect_success 'prepare repository' '
+	echo Hello >init &&
+	git add init &&
+	git commit -m initial &&
 
-test_expect_code 1 'Merge with d/f conflicts' 'git merge "merge msg" B master'
+	git branch B &&
+	mkdir dir &&
+	echo foo >dir/foo &&
+	git add dir/foo &&
+	git commit -m "File: dir/foo" &&
 
-test_expect_failure 'F/D conflict' '
+	git checkout B &&
+	echo file dir >dir &&
+	git add dir &&
+	git commit -m "File: dir"
+'
+
+test_expect_success 'Merge with d/f conflicts' '
+	test_expect_code 1 git merge "merge msg" B master
+'
+
+test_expect_success 'F/D conflict' '
 	git reset --hard &&
 	git checkout master &&
 	rm .git/index &&
@@ -45,4 +50,61 @@
 	git merge master
 '
 
+test_expect_success 'setup modify/delete + directory/file conflict' '
+	git checkout --orphan modify &&
+	git rm -rf . &&
+	git clean -fdqx &&
+
+	printf "a\nb\nc\nd\ne\nf\ng\nh\n" >letters &&
+	git add letters &&
+	git commit -m initial &&
+
+	# Throw in letters.txt for sorting order fun
+	# ("letters.txt" sorts between "letters" and "letters/file")
+	echo i >>letters &&
+	echo "version 2" >letters.txt &&
+	git add letters letters.txt &&
+	git commit -m modified &&
+
+	git checkout -b delete HEAD^ &&
+	git rm letters &&
+	mkdir letters &&
+	>letters/file &&
+	echo "version 1" >letters.txt &&
+	git add letters letters.txt &&
+	git commit -m deleted
+'
+
+test_expect_success 'modify/delete + directory/file conflict' '
+	git checkout delete^0 &&
+	test_must_fail git merge modify &&
+
+	test 5 -eq $(git ls-files -s | wc -l) &&
+	test 4 -eq $(git ls-files -u | wc -l) &&
+	test 1 -eq $(git ls-files -o | wc -l) &&
+
+	test -f letters/file &&
+	test -f letters.txt &&
+	test -f letters~modify
+'
+
+test_expect_success 'modify/delete + directory/file conflict; other way' '
+	# Yes, we really need the double reset since "letters" appears as
+	# both a file and a directory.
+	git reset --hard &&
+	git reset --hard &&
+	git clean -f &&
+	git checkout modify^0 &&
+
+	test_must_fail git merge delete &&
+
+	test 5 -eq $(git ls-files -s | wc -l) &&
+	test 4 -eq $(git ls-files -u | wc -l) &&
+	test 1 -eq $(git ls-files -o | wc -l) &&
+
+	test -f letters/file &&
+	test -f letters.txt &&
+	test -f letters~HEAD
+'
+
 test_done
diff --git a/t/t6022-merge-rename.sh b/t/t6022-merge-rename.sh
index e3f7ae8..1104249 100755
--- a/t/t6022-merge-rename.sh
+++ b/t/t6022-merge-rename.sh
@@ -3,6 +3,11 @@
 test_description='Merge-recursive merging renames'
 . ./test-lib.sh
 
+modify () {
+	sed -e "$1" <"$2" >"$2.x" &&
+	mv "$2.x" "$2"
+}
+
 test_expect_success setup \
 '
 cat >A <<\EOF &&
@@ -94,245 +99,147 @@
 
 test_expect_success 'pull renaming branch into unrenaming one' \
 '
-	git show-branch
-	git pull . white && {
-		echo "BAD: should have conflicted"
-		return 1
-	}
-	git ls-files -s
-	test "$(git ls-files -u B | wc -l)" -eq 3 || {
-		echo "BAD: should have left stages for B"
-		return 1
-	}
-	test "$(git ls-files -s N | wc -l)" -eq 1 || {
-		echo "BAD: should have merged N"
-		return 1
-	}
+	git show-branch &&
+	test_expect_code 1 git pull . white &&
+	git ls-files -s &&
+	git ls-files -u B >b.stages &&
+	test_line_count = 3 b.stages &&
+	git ls-files -s N >n.stages &&
+	test_line_count = 1 n.stages &&
 	sed -ne "/^g/{
 	p
 	q
-	}" B | grep master || {
-		echo "BAD: should have listed our change first"
-		return 1
-	}
-	test "$(git diff white N | wc -l)" -eq 0 || {
-		echo "BAD: should have taken colored branch"
-		return 1
-	}
+	}" B | grep master &&
+	git diff --exit-code white N
 '
 
 test_expect_success 'pull renaming branch into another renaming one' \
 '
-	rm -f B
-	git reset --hard
-	git checkout red
-	git pull . white && {
-		echo "BAD: should have conflicted"
-		return 1
-	}
-	test "$(git ls-files -u B | wc -l)" -eq 3 || {
-		echo "BAD: should have left stages"
-		return 1
-	}
-	test "$(git ls-files -s N | wc -l)" -eq 1 || {
-		echo "BAD: should have merged N"
-		return 1
-	}
+	rm -f B &&
+	git reset --hard &&
+	git checkout red &&
+	test_expect_code 1 git pull . white &&
+	git ls-files -u B >b.stages &&
+	test_line_count = 3 b.stages &&
+	git ls-files -s N >n.stages &&
+	test_line_count = 1 n.stages &&
 	sed -ne "/^g/{
 	p
 	q
-	}" B | grep red || {
-		echo "BAD: should have listed our change first"
-		return 1
-	}
-	test "$(git diff white N | wc -l)" -eq 0 || {
-		echo "BAD: should have taken colored branch"
-		return 1
-	}
+	}" B | grep red &&
+	git diff --exit-code white N
 '
 
 test_expect_success 'pull unrenaming branch into renaming one' \
 '
-	git reset --hard
-	git show-branch
-	git pull . master && {
-		echo "BAD: should have conflicted"
-		return 1
-	}
-	test "$(git ls-files -u B | wc -l)" -eq 3 || {
-		echo "BAD: should have left stages"
-		return 1
-	}
-	test "$(git ls-files -s N | wc -l)" -eq 1 || {
-		echo "BAD: should have merged N"
-		return 1
-	}
+	git reset --hard &&
+	git show-branch &&
+	test_expect_code 1 git pull . master &&
+	git ls-files -u B >b.stages &&
+	test_line_count = 3 b.stages &&
+	git ls-files -s N >n.stages &&
+	test_line_count = 1 n.stages &&
 	sed -ne "/^g/{
 	p
 	q
-	}" B | grep red || {
-		echo "BAD: should have listed our change first"
-		return 1
-	}
-	test "$(git diff white N | wc -l)" -eq 0 || {
-		echo "BAD: should have taken colored branch"
-		return 1
-	}
+	}" B | grep red &&
+	git diff --exit-code white N
 '
 
 test_expect_success 'pull conflicting renames' \
 '
-	git reset --hard
-	git show-branch
-	git pull . blue && {
-		echo "BAD: should have conflicted"
-		return 1
-	}
-	test "$(git ls-files -u A | wc -l)" -eq 1 || {
-		echo "BAD: should have left a stage"
-		return 1
-	}
-	test "$(git ls-files -u B | wc -l)" -eq 1 || {
-		echo "BAD: should have left a stage"
-		return 1
-	}
-	test "$(git ls-files -u C | wc -l)" -eq 1 || {
-		echo "BAD: should have left a stage"
-		return 1
-	}
-	test "$(git ls-files -s N | wc -l)" -eq 1 || {
-		echo "BAD: should have merged N"
-		return 1
-	}
+	git reset --hard &&
+	git show-branch &&
+	test_expect_code 1 git pull . blue &&
+	git ls-files -u A >a.stages &&
+	test_line_count = 1 a.stages &&
+	git ls-files -u B >b.stages &&
+	test_line_count = 1 b.stages &&
+	git ls-files -u C >c.stages &&
+	test_line_count = 1 c.stages &&
+	git ls-files -s N >n.stages &&
+	test_line_count = 1 n.stages &&
 	sed -ne "/^g/{
 	p
 	q
-	}" B | grep red || {
-		echo "BAD: should have listed our change first"
-		return 1
-	}
-	test "$(git diff white N | wc -l)" -eq 0 || {
-		echo "BAD: should have taken colored branch"
-		return 1
-	}
+	}" B | grep red &&
+	git diff --exit-code white N
 '
 
 test_expect_success 'interference with untracked working tree file' '
-
-	git reset --hard
-	git show-branch
-	echo >A this file should not matter
-	git pull . white && {
-		echo "BAD: should have conflicted"
-		return 1
-	}
-	test -f A || {
-		echo "BAD: should have left A intact"
-		return 1
-	}
+	git reset --hard &&
+	git show-branch &&
+	echo >A this file should not matter &&
+	test_expect_code 1 git pull . white &&
+	test_path_is_file A
 '
 
 test_expect_success 'interference with untracked working tree file' '
-
-	git reset --hard
-	git checkout white
-	git show-branch
-	rm -f A
-	echo >A this file should not matter
-	git pull . red && {
-		echo "BAD: should have conflicted"
-		return 1
-	}
-	test -f A || {
-		echo "BAD: should have left A intact"
-		return 1
-	}
+	git reset --hard &&
+	git checkout white &&
+	git show-branch &&
+	rm -f A &&
+	echo >A this file should not matter &&
+	test_expect_code 1 git pull . red &&
+	test_path_is_file A
 '
 
 test_expect_success 'interference with untracked working tree file' '
-
-	git reset --hard
-	rm -f A M
-	git checkout -f master
-	git tag -f anchor
-	git show-branch
-	git pull . yellow || {
-		echo "BAD: should have cleanly merged"
-		return 1
-	}
-	test -f M && {
-		echo "BAD: should have removed M"
-		return 1
-	}
+	git reset --hard &&
+	rm -f A M &&
+	git checkout -f master &&
+	git tag -f anchor &&
+	git show-branch &&
+	git pull . yellow &&
+	test_path_is_missing M &&
 	git reset --hard anchor
 '
 
 test_expect_success 'updated working tree file should prevent the merge' '
-
-	git reset --hard
-	rm -f A M
-	git checkout -f master
-	git tag -f anchor
-	git show-branch
-	echo >>M one line addition
-	cat M >M.saved
-	git pull . yellow && {
-		echo "BAD: should have complained"
-		return 1
-	}
-	diff M M.saved || {
-		echo "BAD: should have left M intact"
-		return 1
-	}
+	git reset --hard &&
+	rm -f A M &&
+	git checkout -f master &&
+	git tag -f anchor &&
+	git show-branch &&
+	echo >>M one line addition &&
+	cat M >M.saved &&
+	test_expect_code 128 git pull . yellow &&
+	test_cmp M M.saved &&
 	rm -f M.saved
 '
 
 test_expect_success 'updated working tree file should prevent the merge' '
-
-	git reset --hard
-	rm -f A M
-	git checkout -f master
-	git tag -f anchor
-	git show-branch
-	echo >>M one line addition
-	cat M >M.saved
-	git update-index M
-	git pull . yellow && {
-		echo "BAD: should have complained"
-		return 1
-	}
-	diff M M.saved || {
-		echo "BAD: should have left M intact"
-		return 1
-	}
+	git reset --hard &&
+	rm -f A M &&
+	git checkout -f master &&
+	git tag -f anchor &&
+	git show-branch &&
+	echo >>M one line addition &&
+	cat M >M.saved &&
+	git update-index M &&
+	test_expect_code 128 git pull . yellow &&
+	test_cmp M M.saved &&
 	rm -f M.saved
 '
 
 test_expect_success 'interference with untracked working tree file' '
-
-	git reset --hard
-	rm -f A M
-	git checkout -f yellow
-	git tag -f anchor
-	git show-branch
-	echo >M this file should not matter
-	git pull . master || {
-		echo "BAD: should have cleanly merged"
-		return 1
-	}
-	test -f M || {
-		echo "BAD: should have left M intact"
-		return 1
-	}
-	git ls-files -s | grep M && {
-		echo "BAD: M must be untracked in the result"
-		return 1
-	}
+	git reset --hard &&
+	rm -f A M &&
+	git checkout -f yellow &&
+	git tag -f anchor &&
+	git show-branch &&
+	echo >M this file should not matter &&
+	git pull . master &&
+	test_path_is_file M &&
+	! {
+		git ls-files -s |
+		grep M
+	} &&
 	git reset --hard anchor
 '
 
 test_expect_success 'merge of identical changes in a renamed file' '
-	rm -f A M N
+	rm -f A M N &&
 	git reset --hard &&
 	git checkout change+rename &&
 	GIT_MERGE_VERBOSITY=3 git merge change | grep "^Skipped B" &&
@@ -341,4 +248,656 @@
 	GIT_MERGE_VERBOSITY=3 git merge change+rename | grep "^Skipped B"
 '
 
+test_expect_success 'setup for rename + d/f conflicts' '
+	git reset --hard &&
+	git checkout --orphan dir-in-way &&
+	git rm -rf . &&
+	git clean -fdqx &&
+
+	mkdir sub &&
+	mkdir dir &&
+	printf "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n" >sub/file &&
+	echo foo >dir/file-in-the-way &&
+	git add -A &&
+	git commit -m "Common commmit" &&
+
+	echo 11 >>sub/file &&
+	echo more >>dir/file-in-the-way &&
+	git add -u &&
+	git commit -m "Commit to merge, with dir in the way" &&
+
+	git checkout -b dir-not-in-way &&
+	git reset --soft HEAD^ &&
+	git rm -rf dir &&
+	git commit -m "Commit to merge, with dir removed" -- dir sub/file &&
+
+	git checkout -b renamed-file-has-no-conflicts dir-in-way~1 &&
+	git rm -rf dir &&
+	git rm sub/file &&
+	printf "1\n2\n3\n4\n5555\n6\n7\n8\n9\n10\n" >dir &&
+	git add dir &&
+	git commit -m "Independent change" &&
+
+	git checkout -b renamed-file-has-conflicts dir-in-way~1 &&
+	git rm -rf dir &&
+	git mv sub/file dir &&
+	echo 12 >>dir &&
+	git add dir &&
+	git commit -m "Conflicting change"
+'
+
+printf "1\n2\n3\n4\n5555\n6\n7\n8\n9\n10\n11\n" >expected
+
+test_expect_success 'Rename+D/F conflict; renamed file merges + dir not in way' '
+	git reset --hard &&
+	git checkout -q renamed-file-has-no-conflicts^0 &&
+	git merge --strategy=recursive dir-not-in-way &&
+	git diff --quiet &&
+	test -f dir &&
+	test_cmp expected dir
+'
+
+test_expect_success 'Rename+D/F conflict; renamed file merges but dir in way' '
+	git reset --hard &&
+	rm -rf dir~* &&
+	git checkout -q renamed-file-has-no-conflicts^0 &&
+	test_must_fail git merge --strategy=recursive dir-in-way >output &&
+
+	grep "CONFLICT (modify/delete): dir/file-in-the-way" output &&
+	grep "Auto-merging dir" output &&
+	grep "Adding as dir~HEAD instead" output &&
+
+	test 3 -eq "$(git ls-files -u | wc -l)" &&
+	test 2 -eq "$(git ls-files -u dir/file-in-the-way | wc -l)" &&
+
+	test_must_fail git diff --quiet &&
+	test_must_fail git diff --cached --quiet &&
+
+	test -f dir/file-in-the-way &&
+	test -f dir~HEAD &&
+	test_cmp expected dir~HEAD
+'
+
+test_expect_success 'Same as previous, but merged other way' '
+	git reset --hard &&
+	rm -rf dir~* &&
+	git checkout -q dir-in-way^0 &&
+	test_must_fail git merge --strategy=recursive renamed-file-has-no-conflicts >output 2>errors &&
+
+	! grep "error: refusing to lose untracked file at" errors &&
+	grep "CONFLICT (modify/delete): dir/file-in-the-way" output &&
+	grep "Auto-merging dir" output &&
+	grep "Adding as dir~renamed-file-has-no-conflicts instead" output &&
+
+	test 3 -eq "$(git ls-files -u | wc -l)" &&
+	test 2 -eq "$(git ls-files -u dir/file-in-the-way | wc -l)" &&
+
+	test_must_fail git diff --quiet &&
+	test_must_fail git diff --cached --quiet &&
+
+	test -f dir/file-in-the-way &&
+	test -f dir~renamed-file-has-no-conflicts &&
+	test_cmp expected dir~renamed-file-has-no-conflicts
+'
+
+cat >expected <<\EOF &&
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+<<<<<<< HEAD:dir
+12
+=======
+11
+>>>>>>> dir-not-in-way:sub/file
+EOF
+
+test_expect_success 'Rename+D/F conflict; renamed file cannot merge, dir not in way' '
+	git reset --hard &&
+	rm -rf dir~* &&
+	git checkout -q renamed-file-has-conflicts^0 &&
+	test_must_fail git merge --strategy=recursive dir-not-in-way &&
+
+	test 3 -eq "$(git ls-files -u | wc -l)" &&
+	test 3 -eq "$(git ls-files -u dir | wc -l)" &&
+
+	test_must_fail git diff --quiet &&
+	test_must_fail git diff --cached --quiet &&
+
+	test -f dir &&
+	test_cmp expected dir
+'
+
+test_expect_success 'Rename+D/F conflict; renamed file cannot merge and dir in the way' '
+	modify s/dir-not-in-way/dir-in-way/ expected &&
+
+	git reset --hard &&
+	rm -rf dir~* &&
+	git checkout -q renamed-file-has-conflicts^0 &&
+	test_must_fail git merge --strategy=recursive dir-in-way &&
+
+	test 5 -eq "$(git ls-files -u | wc -l)" &&
+	test 3 -eq "$(git ls-files -u dir | grep -v file-in-the-way | wc -l)" &&
+	test 2 -eq "$(git ls-files -u dir/file-in-the-way | wc -l)" &&
+
+	test_must_fail git diff --quiet &&
+	test_must_fail git diff --cached --quiet &&
+
+	test -f dir/file-in-the-way &&
+	test -f dir~HEAD &&
+	test_cmp expected dir~HEAD
+'
+
+cat >expected <<\EOF &&
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+<<<<<<< HEAD:sub/file
+11
+=======
+12
+>>>>>>> renamed-file-has-conflicts:dir
+EOF
+
+test_expect_success 'Same as previous, but merged other way' '
+	git reset --hard &&
+	rm -rf dir~* &&
+	git checkout -q dir-in-way^0 &&
+	test_must_fail git merge --strategy=recursive renamed-file-has-conflicts &&
+
+	test 5 -eq "$(git ls-files -u | wc -l)" &&
+	test 3 -eq "$(git ls-files -u dir | grep -v file-in-the-way | wc -l)" &&
+	test 2 -eq "$(git ls-files -u dir/file-in-the-way | wc -l)" &&
+
+	test_must_fail git diff --quiet &&
+	test_must_fail git diff --cached --quiet &&
+
+	test -f dir/file-in-the-way &&
+	test -f dir~renamed-file-has-conflicts &&
+	test_cmp expected dir~renamed-file-has-conflicts
+'
+
+test_expect_success 'setup both rename source and destination involved in D/F conflict' '
+	git reset --hard &&
+	git checkout --orphan rename-dest &&
+	git rm -rf . &&
+	git clean -fdqx &&
+
+	mkdir one &&
+	echo stuff >one/file &&
+	git add -A &&
+	git commit -m "Common commmit" &&
+
+	git mv one/file destdir &&
+	git commit -m "Renamed to destdir" &&
+
+	git checkout -b source-conflict HEAD~1 &&
+	git rm -rf one &&
+	mkdir destdir &&
+	touch one destdir/foo &&
+	git add -A &&
+	git commit -m "Conflicts in the way"
+'
+
+test_expect_success 'both rename source and destination involved in D/F conflict' '
+	git reset --hard &&
+	rm -rf dir~* &&
+	git checkout -q rename-dest^0 &&
+	test_must_fail git merge --strategy=recursive source-conflict &&
+
+	test 1 -eq "$(git ls-files -u | wc -l)" &&
+
+	test_must_fail git diff --quiet &&
+
+	test -f destdir/foo &&
+	test -f one &&
+	test -f destdir~HEAD &&
+	test "stuff" = "$(cat destdir~HEAD)"
+'
+
+test_expect_success 'setup pair rename to parent of other (D/F conflicts)' '
+	git reset --hard &&
+	git checkout --orphan rename-two &&
+	git rm -rf . &&
+	git clean -fdqx &&
+
+	mkdir one &&
+	mkdir two &&
+	echo stuff >one/file &&
+	echo other >two/file &&
+	git add -A &&
+	git commit -m "Common commmit" &&
+
+	git rm -rf one &&
+	git mv two/file one &&
+	git commit -m "Rename two/file -> one" &&
+
+	git checkout -b rename-one HEAD~1 &&
+	git rm -rf two &&
+	git mv one/file two &&
+	rm -r one &&
+	git commit -m "Rename one/file -> two"
+'
+
+test_expect_success 'pair rename to parent of other (D/F conflicts) w/ untracked dir' '
+	git checkout -q rename-one^0 &&
+	mkdir one &&
+	test_must_fail git merge --strategy=recursive rename-two &&
+
+	test 2 -eq "$(git ls-files -u | wc -l)" &&
+	test 1 -eq "$(git ls-files -u one | wc -l)" &&
+	test 1 -eq "$(git ls-files -u two | wc -l)" &&
+
+	test_must_fail git diff --quiet &&
+
+	test 4 -eq $(find . | grep -v .git | wc -l) &&
+
+	test -d one &&
+	test -f one~rename-two &&
+	test -f two &&
+	test "other" = $(cat one~rename-two) &&
+	test "stuff" = $(cat two)
+'
+
+test_expect_success 'pair rename to parent of other (D/F conflicts) w/ clean start' '
+	git reset --hard &&
+	git clean -fdqx &&
+	test_must_fail git merge --strategy=recursive rename-two &&
+
+	test 2 -eq "$(git ls-files -u | wc -l)" &&
+	test 1 -eq "$(git ls-files -u one | wc -l)" &&
+	test 1 -eq "$(git ls-files -u two | wc -l)" &&
+
+	test_must_fail git diff --quiet &&
+
+	test 3 -eq $(find . | grep -v .git | wc -l) &&
+
+	test -f one &&
+	test -f two &&
+	test "other" = $(cat one) &&
+	test "stuff" = $(cat two)
+'
+
+test_expect_success 'setup rename of one file to two, with directories in the way' '
+	git reset --hard &&
+	git checkout --orphan first-rename &&
+	git rm -rf . &&
+	git clean -fdqx &&
+
+	echo stuff >original &&
+	git add -A &&
+	git commit -m "Common commmit" &&
+
+	mkdir two &&
+	>two/file &&
+	git add two/file &&
+	git mv original one &&
+	git commit -m "Put two/file in the way, rename to one" &&
+
+	git checkout -b second-rename HEAD~1 &&
+	mkdir one &&
+	>one/file &&
+	git add one/file &&
+	git mv original two &&
+	git commit -m "Put one/file in the way, rename to two"
+'
+
+test_expect_success 'check handling of differently renamed file with D/F conflicts' '
+	git checkout -q first-rename^0 &&
+	test_must_fail git merge --strategy=recursive second-rename &&
+
+	test 5 -eq "$(git ls-files -s | wc -l)" &&
+	test 3 -eq "$(git ls-files -u | wc -l)" &&
+	test 1 -eq "$(git ls-files -u one | wc -l)" &&
+	test 1 -eq "$(git ls-files -u two | wc -l)" &&
+	test 1 -eq "$(git ls-files -u original | wc -l)" &&
+	test 2 -eq "$(git ls-files -o | wc -l)" &&
+
+	test -f one/file &&
+	test -f two/file &&
+	test -f one~HEAD &&
+	test -f two~second-rename &&
+	! test -f original
+'
+
+test_expect_success 'setup rename one file to two; directories moving out of the way' '
+	git reset --hard &&
+	git checkout --orphan first-rename-redo &&
+	git rm -rf . &&
+	git clean -fdqx &&
+
+	echo stuff >original &&
+	mkdir one two &&
+	touch one/file two/file &&
+	git add -A &&
+	git commit -m "Common commmit" &&
+
+	git rm -rf one &&
+	git mv original one &&
+	git commit -m "Rename to one" &&
+
+	git checkout -b second-rename-redo HEAD~1 &&
+	git rm -rf two &&
+	git mv original two &&
+	git commit -m "Rename to two"
+'
+
+test_expect_success 'check handling of differently renamed file with D/F conflicts' '
+	git checkout -q first-rename-redo^0 &&
+	test_must_fail git merge --strategy=recursive second-rename-redo &&
+
+	test 3 -eq "$(git ls-files -u | wc -l)" &&
+	test 1 -eq "$(git ls-files -u one | wc -l)" &&
+	test 1 -eq "$(git ls-files -u two | wc -l)" &&
+	test 1 -eq "$(git ls-files -u original | wc -l)" &&
+	test 0 -eq "$(git ls-files -o | wc -l)" &&
+
+	test -f one &&
+	test -f two &&
+	! test -f original
+'
+
+test_expect_success 'setup avoid unnecessary update, normal rename' '
+	git reset --hard &&
+	git checkout --orphan avoid-unnecessary-update-1 &&
+	git rm -rf . &&
+	git clean -fdqx &&
+
+	printf "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n" >original &&
+	git add -A &&
+	git commit -m "Common commmit" &&
+
+	git mv original rename &&
+	echo 11 >>rename &&
+	git add -u &&
+	git commit -m "Renamed and modified" &&
+
+	git checkout -b merge-branch-1 HEAD~1 &&
+	echo "random content" >random-file &&
+	git add -A &&
+	git commit -m "Random, unrelated changes"
+'
+
+test_expect_success 'avoid unnecessary update, normal rename' '
+	git checkout -q avoid-unnecessary-update-1^0 &&
+	test-chmtime =1000000000 rename &&
+	test-chmtime -v +0 rename >expect &&
+	git merge merge-branch-1 &&
+	test-chmtime -v +0 rename >actual &&
+	test_cmp expect actual # "rename" should have stayed intact
+'
+
+test_expect_success 'setup to test avoiding unnecessary update, with D/F conflict' '
+	git reset --hard &&
+	git checkout --orphan avoid-unnecessary-update-2 &&
+	git rm -rf . &&
+	git clean -fdqx &&
+
+	mkdir df &&
+	printf "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n" >df/file &&
+	git add -A &&
+	git commit -m "Common commmit" &&
+
+	git mv df/file temp &&
+	rm -rf df &&
+	git mv temp df &&
+	echo 11 >>df &&
+	git add -u &&
+	git commit -m "Renamed and modified" &&
+
+	git checkout -b merge-branch-2 HEAD~1 &&
+	>unrelated-change &&
+	git add unrelated-change &&
+	git commit -m "Only unrelated changes"
+'
+
+test_expect_success 'avoid unnecessary update, with D/F conflict' '
+	git checkout -q avoid-unnecessary-update-2^0 &&
+	test-chmtime =1000000000 df &&
+	test-chmtime -v +0 df >expect &&
+	git merge merge-branch-2 &&
+	test-chmtime -v +0 df >actual &&
+	test_cmp expect actual # "df" should have stayed intact
+'
+
+test_expect_success 'setup avoid unnecessary update, dir->(file,nothing)' '
+	git rm -rf . &&
+	git clean -fdqx &&
+	rm -rf .git &&
+	git init &&
+
+	>irrelevant &&
+	mkdir df &&
+	>df/file &&
+	git add -A &&
+	git commit -mA &&
+
+	git checkout -b side
+	git rm -rf df &&
+	git commit -mB &&
+
+	git checkout master &&
+	git rm -rf df &&
+	echo bla >df &&
+	git add -A &&
+	git commit -m "Add a newfile"
+'
+
+test_expect_success 'avoid unnecessary update, dir->(file,nothing)' '
+	git checkout -q master^0 &&
+	test-chmtime =1000000000 df &&
+	test-chmtime -v +0 df >expect &&
+	git merge side &&
+	test-chmtime -v +0 df >actual &&
+	test_cmp expect actual # "df" should have stayed intact
+'
+
+test_expect_success 'setup avoid unnecessary update, modify/delete' '
+	git rm -rf . &&
+	git clean -fdqx &&
+	rm -rf .git &&
+	git init &&
+
+	>irrelevant &&
+	>file &&
+	git add -A &&
+	git commit -mA &&
+
+	git checkout -b side
+	git rm -f file &&
+	git commit -m "Delete file" &&
+
+	git checkout master &&
+	echo bla >file &&
+	git add -A &&
+	git commit -m "Modify file"
+'
+
+test_expect_success 'avoid unnecessary update, modify/delete' '
+	git checkout -q master^0 &&
+	test-chmtime =1000000000 file &&
+	test-chmtime -v +0 file >expect &&
+	test_must_fail git merge side &&
+	test-chmtime -v +0 file >actual &&
+	test_cmp expect actual # "file" should have stayed intact
+'
+
+test_expect_success 'setup avoid unnecessary update, rename/add-dest' '
+	git rm -rf . &&
+	git clean -fdqx &&
+	rm -rf .git &&
+	git init &&
+
+	printf "1\n2\n3\n4\n5\n6\n7\n8\n" >file &&
+	git add -A &&
+	git commit -mA &&
+
+	git checkout -b side
+	cp file newfile &&
+	git add -A &&
+	git commit -m "Add file copy" &&
+
+	git checkout master &&
+	git mv file newfile &&
+	git commit -m "Rename file"
+'
+
+test_expect_success 'avoid unnecessary update, rename/add-dest' '
+	git checkout -q master^0 &&
+	test-chmtime =1000000000 newfile &&
+	test-chmtime -v +0 newfile >expect &&
+	git merge side &&
+	test-chmtime -v +0 newfile >actual &&
+	test_cmp expect actual # "file" should have stayed intact
+'
+
+test_expect_success 'setup merge of rename + small change' '
+	git reset --hard &&
+	git checkout --orphan rename-plus-small-change &&
+	git rm -rf . &&
+	git clean -fdqx &&
+
+	echo ORIGINAL >file &&
+	git add file &&
+
+	test_tick &&
+	git commit -m Initial &&
+	git checkout -b rename_branch &&
+	git mv file renamed_file &&
+	git commit -m Rename &&
+	git checkout rename-plus-small-change &&
+	echo NEW-VERSION >file &&
+	git commit -a -m Reformat
+'
+
+test_expect_success 'merge rename + small change' '
+	git merge rename_branch &&
+
+	test 1 -eq $(git ls-files -s | wc -l) &&
+	test 0 -eq $(git ls-files -o | wc -l) &&
+	test $(git rev-parse HEAD:renamed_file) = $(git rev-parse HEAD~1:file)
+'
+
+test_expect_success 'setup for use of extended merge markers' '
+	git rm -rf . &&
+	git clean -fdqx &&
+	rm -rf .git &&
+	git init &&
+
+	printf "1\n2\n3\n4\n5\n6\n7\n8\n" >original_file &&
+	git add original_file &&
+	git commit -mA &&
+
+	git checkout -b rename &&
+	echo 9 >>original_file &&
+	git add original_file &&
+	git mv original_file renamed_file &&
+	git commit -mB &&
+
+	git checkout master &&
+	echo 8.5 >>original_file &&
+	git add original_file &&
+	git commit -mC
+'
+
+cat >expected <<\EOF &&
+1
+2
+3
+4
+5
+6
+7
+8
+<<<<<<< HEAD:renamed_file
+9
+=======
+8.5
+>>>>>>> master^0:original_file
+EOF
+
+test_expect_success 'merge master into rename has correct extended markers' '
+	git checkout rename^0 &&
+	test_must_fail git merge -s recursive master^0 &&
+	test_cmp expected renamed_file
+'
+
+cat >expected <<\EOF &&
+1
+2
+3
+4
+5
+6
+7
+8
+<<<<<<< HEAD:original_file
+8.5
+=======
+9
+>>>>>>> rename^0:renamed_file
+EOF
+
+test_expect_success 'merge rename into master has correct extended markers' '
+	git reset --hard &&
+	git checkout master^0 &&
+	test_must_fail git merge -s recursive rename^0 &&
+	test_cmp expected renamed_file
+'
+
+test_expect_success 'setup spurious "refusing to lose untracked" message' '
+	git rm -rf . &&
+	git clean -fdqx &&
+	rm -rf .git &&
+	git init &&
+
+	> irrelevant_file &&
+	printf "1\n2\n3\n4\n5\n6\n7\n8\n" >original_file &&
+	git add irrelevant_file original_file &&
+	git commit -mA &&
+
+	git checkout -b rename &&
+	git mv original_file renamed_file &&
+	git commit -mB &&
+
+	git checkout master &&
+	git rm original_file &&
+	git commit -mC
+'
+
+test_expect_success 'no spurious "refusing to lose untracked" message' '
+	git checkout master^0 &&
+	test_must_fail git merge rename^0 2>errors.txt &&
+	! grep "refusing to lose untracked file" errors.txt
+'
+
+test_expect_success 'do not follow renames for empty files' '
+	git checkout -f -b empty-base &&
+	>empty1 &&
+	git add empty1 &&
+	git commit -m base &&
+	echo content >empty1 &&
+	git add empty1 &&
+	git commit -m fill &&
+	git checkout -b empty-topic HEAD^ &&
+	git mv empty1 empty2 &&
+	git commit -m rename &&
+	test_must_fail git merge empty-base &&
+	>expect &&
+	test_cmp expect empty2
+'
+
 test_done
diff --git a/t/t6023-merge-file.sh b/t/t6023-merge-file.sh
index 7dcf391..432f086 100755
--- a/t/t6023-merge-file.sh
+++ b/t/t6023-merge-file.sh
@@ -64,6 +64,18 @@
 test_expect_success "merge without conflict" \
 	"git merge-file test.txt orig.txt new2.txt"
 
+test_expect_success 'works in subdirectory' '
+	mkdir dir &&
+	cp new1.txt dir/a.txt &&
+	cp orig.txt dir/o.txt &&
+	cp new2.txt dir/b.txt &&
+	( cd dir && git merge-file a.txt o.txt b.txt )
+'
+
+cp new1.txt test.txt
+test_expect_success "merge without conflict (--quiet)" \
+	"git merge-file --quiet test.txt orig.txt new2.txt"
+
 cp new1.txt test2.txt
 test_expect_success "merge without conflict (missing LF at EOF)" \
 	"git merge-file test2.txt orig.txt new2.txt"
@@ -142,12 +154,12 @@
 
 test_expect_success 'binary files cannot be merged' '
 	test_must_fail git merge-file -p \
-		orig.txt "$TEST_DIRECTORY"/test4012.png new1.txt 2> merge.err &&
+		orig.txt "$TEST_DIRECTORY"/test-binary-1.png new1.txt 2> merge.err &&
 	grep "Cannot merge binary files" merge.err
 '
 
-sed -e "s/deerit.$/deerit;/" -e "s/me;$/me./" < new5.txt > new6.txt
-sed -e "s/deerit.$/deerit,/" -e "s/me;$/me,/" < new5.txt > new7.txt
+sed -e "s/deerit.\$/deerit;/" -e "s/me;\$/me./" < new5.txt > new6.txt
+sed -e "s/deerit.\$/deerit,/" -e "s/me;\$/me,/" < new5.txt > new7.txt
 
 test_expect_success 'MERGE_ZEALOUS simplifies non-conflicts' '
 
@@ -177,7 +189,7 @@
 
 In loco pascuae ibi me collocavit;
 super aquam refectionis educavit me.
-|||||||
+||||||| new5.txt
 et nihil mihi deerit.
 In loco pascuae ibi me collocavit,
 super aquam refectionis educavit me;
@@ -211,4 +223,41 @@
 	test_cmp expect actual
 '
 
+cat >expect <<\EOF
+Dominus regit me,
+<<<<<<<<<< new8.txt
+et nihil mihi deerit;
+
+
+
+
+In loco pascuae ibi me collocavit;
+super aquam refectionis educavit me.
+|||||||||| new5.txt
+et nihil mihi deerit.
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+==========
+et nihil mihi deerit,
+
+
+
+
+In loco pascuae ibi me collocavit --
+super aquam refectionis educavit me,
+>>>>>>>>>> new9.txt
+animam meam convertit,
+deduxit me super semitas jusitiae,
+propter nomen suum.
+Nam et si ambulavero in medio umbrae mortis,
+non timebo mala, quoniam TU mecum es:
+virga tua et baculus tuus ipsa me consolata sunt.
+EOF
+
+test_expect_success 'marker size' '
+	test_must_fail git merge-file -p --marker-size=10 \
+		new8.txt new5.txt new9.txt >actual &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t6024-recursive-merge.sh b/t/t6024-recursive-merge.sh
index b3fbf65..755d30c 100755
--- a/t/t6024-recursive-merge.sh
+++ b/t/t6024-recursive-merge.sh
@@ -104,7 +104,7 @@
 	test_tick &&
 	git commit -m delete &&
 	git checkout -b rename HEAD^ &&
-	git mv a1 a2
+	git mv a1 a2 &&
 	test_tick &&
 	git commit -m rename &&
 	test_must_fail git merge delete &&
diff --git a/t/t6026-merge-attr.sh b/t/t6026-merge-attr.sh
index 1ba0a25..5e43997 100755
--- a/t/t6026-merge-attr.sh
+++ b/t/t6026-merge-attr.sh
@@ -70,6 +70,18 @@
 
 '
 
+test_expect_success 'retry the merge with longer context' '
+	echo text conflict-marker-size=32 >>.gitattributes &&
+	git checkout -m text &&
+	sed -ne "/^\([<=>]\)\1\1\1*/{
+		s/ .*$//
+		p
+	}" >actual text &&
+	grep ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" actual &&
+	grep "================================" actual &&
+	grep "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" actual
+'
+
 cat >./custom-merge <<\EOF
 #!/bin/sh
 
diff --git a/t/t6027-merge-binary.sh b/t/t6027-merge-binary.sh
index b519626..0773541 100755
--- a/t/t6027-merge-binary.sh
+++ b/t/t6027-merge-binary.sh
@@ -6,7 +6,7 @@
 
 test_expect_success setup '
 
-	cat "$TEST_DIRECTORY"/test4012.png >m &&
+	cat "$TEST_DIRECTORY"/test-binary-1.png >m &&
 	git add m &&
 	git ls-files -s | sed -e "s/ 0	/ 1	/" >E1 &&
 	test_tick &&
diff --git a/t/t6028-merge-up-to-date.sh b/t/t6028-merge-up-to-date.sh
index f8f3e3f..c518e9c 100755
--- a/t/t6028-merge-up-to-date.sh
+++ b/t/t6028-merge-up-to-date.sh
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-test_description='merge fast forward and up to date'
+test_description='merge fast-forward and up to date'
 
 . ./test-lib.sh
 
@@ -16,7 +16,12 @@
 	test_tick &&
 	git commit -m second &&
 	git tag c1 &&
-	git branch test
+	git branch test &&
+	echo third >file &&
+	git add file &&
+	test_tick &&
+	git commit -m third &&
+	git tag c2
 '
 
 test_expect_success 'merge -s recursive up-to-date' '
@@ -74,4 +79,14 @@
 
 '
 
+test_expect_success 'merge fast-forward octopus' '
+
+	git reset --hard c0 &&
+	test_tick &&
+	git merge c1 c2
+	expect=$(git rev-parse c2) &&
+	current=$(git rev-parse HEAD) &&
+	test "$expect" = "$current"
+'
+
 test_done
diff --git a/t/t6029-merge-subtree.sh b/t/t6029-merge-subtree.sh
index 5bbfa44..73fc240 100755
--- a/t/t6029-merge-subtree.sh
+++ b/t/t6029-merge-subtree.sh
@@ -6,7 +6,7 @@
 
 test_expect_success setup '
 
-	s="1 2 3 4 5 6 7 8"
+	s="1 2 3 4 5 6 7 8" &&
 	for i in $s; do echo $i; done >hello &&
 	git add hello &&
 	git commit -m initial &&
@@ -52,6 +52,7 @@
 	git merge -s ours --no-commit gui/master &&
 	git read-tree --prefix=git-gui/ -u gui/master &&
 	git commit -m "Merge git-gui as our subdirectory" &&
+	git checkout -b work &&
 	git ls-files -s >actual &&
 	(
 		echo "100644 $o1 0	git-gui/git-gui.sh"
@@ -65,9 +66,10 @@
 	echo git-gui2 > git-gui.sh &&
 	o3=$(git hash-object git-gui.sh) &&
 	git add git-gui.sh &&
+	git checkout -b master2 &&
 	git commit -m "update git-gui" &&
 	cd ../git &&
-	git pull -s subtree gui master &&
+	git pull -s subtree gui master2 &&
 	git ls-files -s >actual &&
 	(
 		echo "100644 $o3 0	git-gui/git-gui.sh"
@@ -76,4 +78,47 @@
 	test_cmp expected actual
 '
 
+test_expect_success 'initial ambiguous subtree' '
+	cd ../git &&
+	git reset --hard master &&
+	git checkout -b master2 &&
+	git merge -s ours --no-commit gui/master &&
+	git read-tree --prefix=git-gui2/ -u gui/master &&
+	git commit -m "Merge git-gui2 as our subdirectory" &&
+	git checkout -b work2 &&
+	git ls-files -s >actual &&
+	(
+		echo "100644 $o1 0	git-gui/git-gui.sh"
+		echo "100644 $o1 0	git-gui2/git-gui.sh"
+		echo "100644 $o2 0	git.c"
+	) >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'merge using explicit' '
+	cd ../git &&
+	git reset --hard master2 &&
+	git pull -Xsubtree=git-gui gui master2 &&
+	git ls-files -s >actual &&
+	(
+		echo "100644 $o3 0	git-gui/git-gui.sh"
+		echo "100644 $o1 0	git-gui2/git-gui.sh"
+		echo "100644 $o2 0	git.c"
+	) >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'merge2 using explicit' '
+	cd ../git &&
+	git reset --hard master2 &&
+	git pull -Xsubtree=git-gui2 gui master2 &&
+	git ls-files -s >actual &&
+	(
+		echo "100644 $o1 0	git-gui/git-gui.sh"
+		echo "100644 $o3 0	git-gui2/git-gui.sh"
+		echo "100644 $o2 0	git.c"
+	) >expected &&
+	test_cmp expected actual
+'
+
 test_done
diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh
index 1315bab..72e28ee 100755
--- a/t/t6030-bisect-porcelain.sh
+++ b/t/t6030-bisect-porcelain.sh
@@ -126,6 +126,18 @@
 	test -z "$(git for-each-ref "refs/heads/bisect")"
 '
 
+test_expect_success 'bisect reset removes bisect state after --no-checkout' '
+	git bisect reset &&
+	git bisect start --no-checkout &&
+	git bisect good $HASH1 &&
+	git bisect bad $HASH3 &&
+	git bisect next &&
+	git bisect reset &&
+	test -z "$(git for-each-ref "refs/bisect/*")" &&
+	test -z "$(git for-each-ref "refs/heads/bisect")" &&
+	test -z "$(git for-each-ref "BISECT_HEAD")"
+'
+
 test_expect_success 'bisect start: back in good branch' '
 	git branch > branch.output &&
 	grep "* other" branch.output > /dev/null &&
@@ -138,15 +150,23 @@
 	grep "* other" branch.output > /dev/null
 '
 
-test_expect_success 'bisect start: no ".git/BISECT_START" if junk rev' '
-	git bisect start $HASH4 $HASH1 -- &&
-	git bisect good &&
+test_expect_success 'bisect start: no ".git/BISECT_START" created if junk rev' '
+	git bisect reset &&
 	test_must_fail git bisect start $HASH4 foo -- &&
 	git branch > branch.output &&
 	grep "* other" branch.output > /dev/null &&
 	test_must_fail test -e .git/BISECT_START
 '
 
+test_expect_success 'bisect start: existing ".git/BISECT_START" not modified if junk rev' '
+	git bisect start $HASH4 $HASH1 -- &&
+	git bisect good &&
+	cp .git/BISECT_START saved &&
+	test_must_fail git bisect start $HASH4 foo -- &&
+	git branch > branch.output &&
+	test_i18ngrep "* (no branch)" branch.output > /dev/null &&
+	test_cmp saved .git/BISECT_START
+'
 test_expect_success 'bisect start: no ".git/BISECT_START" if mistaken rev' '
 	git bisect start $HASH4 $HASH1 -- &&
 	git bisect good &&
@@ -175,7 +195,7 @@
 	git bisect start $HASH4 $HASH1 &&
 	git bisect skip &&
 	git bisect bad > my_bisect_log.txt &&
-	grep "$HASH2 is first bad commit" my_bisect_log.txt &&
+	grep "$HASH2 is the first bad commit" my_bisect_log.txt &&
 	git bisect reset
 '
 
@@ -261,7 +281,7 @@
      git bisect good $HASH1 &&
      git bisect bad $HASH4 &&
      git bisect run ./test_script.sh > my_bisect_log.txt &&
-     grep "$HASH3 is first bad commit" my_bisect_log.txt &&
+     grep "$HASH3 is the first bad commit" my_bisect_log.txt &&
      git bisect reset'
 
 # We want to automatically find the commit that
@@ -274,7 +294,7 @@
      chmod +x test_script.sh &&
      git bisect start $HASH4 $HASH1 &&
      git bisect run ./test_script.sh > my_bisect_log.txt &&
-     grep "$HASH4 is first bad commit" my_bisect_log.txt &&
+     grep "$HASH4 is the first bad commit" my_bisect_log.txt &&
      git bisect reset'
 
 # $HASH1 is good, $HASH5 is bad, we skip $HASH3
@@ -287,14 +307,14 @@
 	git bisect start $HASH5 $HASH1 &&
 	git bisect skip &&
 	git bisect good > my_bisect_log.txt &&
-	grep "$HASH5 is first bad commit" my_bisect_log.txt &&
+	grep "$HASH5 is the first bad commit" my_bisect_log.txt &&
 	git bisect log > log_to_replay.txt &&
 	git bisect reset
 '
 
 test_expect_success 'bisect skip and bisect replay' '
 	git bisect replay log_to_replay.txt > my_bisect_log.txt &&
-	grep "$HASH5 is first bad commit" my_bisect_log.txt &&
+	grep "$HASH5 is the first bad commit" my_bisect_log.txt &&
 	git bisect reset
 '
 
@@ -335,7 +355,7 @@
 	chmod +x test_script.sh &&
 	git bisect start $HASH7 $HASH1 &&
 	git bisect run ./test_script.sh > my_bisect_log.txt &&
-	grep "$HASH6 is first bad commit" my_bisect_log.txt
+	grep "$HASH6 is the first bad commit" my_bisect_log.txt
 '
 
 test_expect_success 'bisect skip only one range' '
@@ -385,7 +405,7 @@
 	rev_hash6=$(git rev-parse --verify HEAD) &&
 	test "$rev_hash6" = "$HASH6" &&
 	git bisect good > my_bisect_log.txt &&
-	grep "$HASH7 is first bad commit" my_bisect_log.txt &&
+	grep "$HASH7 is the first bad commit" my_bisect_log.txt &&
 	git bisect reset &&
 	rev_hash6=$(git rev-parse --verify bisect) &&
 	test "$rev_hash6" = "$HASH6" &&
@@ -423,7 +443,7 @@
 	grep "merge base must be tested" my_bisect_log.txt &&
 	grep $HASH4 my_bisect_log.txt &&
 	git bisect skip > my_bisect_log.txt 2>&1 &&
-	grep "Warning" my_bisect_log.txt &&
+	grep "warning" my_bisect_log.txt &&
 	grep $SIDE_HASH6 my_bisect_log.txt &&
 	git bisect reset
 '
@@ -460,7 +480,7 @@
 	git merge -m "merge HASH7 and SIDE_HASH7" "$HASH7" &&
 	B_HASH=$(git rev-parse --verify HEAD) &&
 	git merge-base --all "$A_HASH" "$B_HASH" > merge_bases.txt &&
-	test $(wc -l < merge_bases.txt) = "2" &&
+	test_line_count = 2 merge_bases.txt &&
 	grep "$HASH5" merge_bases.txt &&
 	grep "$SIDE_HASH5" merge_bases.txt
 '
@@ -517,13 +537,13 @@
 	add_line_into_file "2(para): line 2 on parallel branch" dir2/file2 &&
 	PARA_HASH2=$(git rev-parse --verify HEAD) &&
 	add_line_into_file "3(para): line 3 on parallel branch" dir2/file3 &&
-	PARA_HASH3=$(git rev-parse --verify HEAD)
+	PARA_HASH3=$(git rev-parse --verify HEAD) &&
 	git merge -m "merge HASH4 and PARA_HASH3" "$HASH4" &&
-	PARA_HASH4=$(git rev-parse --verify HEAD)
+	PARA_HASH4=$(git rev-parse --verify HEAD) &&
 	add_line_into_file "5(para): add line on parallel branch" dir1/file1 &&
-	PARA_HASH5=$(git rev-parse --verify HEAD)
+	PARA_HASH5=$(git rev-parse --verify HEAD) &&
 	add_line_into_file "6(para): add line on parallel branch" dir2/file2 &&
-	PARA_HASH6=$(git rev-parse --verify HEAD)
+	PARA_HASH6=$(git rev-parse --verify HEAD) &&
 	git merge -m "merge HASH7 and PARA_HASH6" "$HASH7" &&
 	PARA_HASH7=$(git rev-parse --verify HEAD)
 '
@@ -534,7 +554,7 @@
 	para1=$(git rev-parse --verify HEAD) &&
 	test "$para1" = "$PARA_HASH1" &&
 	git bisect bad > my_bisect_log.txt &&
-	grep "$PARA_HASH1 is first bad commit" my_bisect_log.txt
+	grep "$PARA_HASH1 is the first bad commit" my_bisect_log.txt
 '
 
 test_expect_success 'restricting bisection on one dir and a file' '
@@ -552,7 +572,7 @@
 	para1=$(git rev-parse --verify HEAD) &&
 	test "$para1" = "$PARA_HASH1" &&
 	git bisect good > my_bisect_log.txt &&
-	grep "$PARA_HASH4 is first bad commit" my_bisect_log.txt
+	grep "$PARA_HASH4 is the first bad commit" my_bisect_log.txt
 '
 
 test_expect_success 'skipping away from skipped commit' '
@@ -567,6 +587,160 @@
 	test "$para3" = "$PARA_HASH3"
 '
 
+test_expect_success 'erroring out when using bad path parameters' '
+	test_must_fail git bisect start $PARA_HASH7 $HASH1 -- foobar 2> error.txt &&
+	grep "bad path parameters" error.txt
+'
+
+test_expect_success 'test bisection on bare repo - --no-checkout specified' '
+	git clone --bare . bare.nocheckout &&
+	(
+		cd bare.nocheckout &&
+		git bisect start --no-checkout &&
+		git bisect good $HASH1 &&
+		git bisect bad $HASH4 &&
+		git bisect run eval \
+			"test \$(git rev-list BISECT_HEAD ^$HASH2 --max-count=1 | wc -l) = 0" \
+			>../nocheckout.log &&
+		git bisect reset
+	) &&
+	grep "$HASH3 is the first bad commit" nocheckout.log
+'
+
+
+test_expect_success 'test bisection on bare repo - --no-checkout defaulted' '
+	git clone --bare . bare.defaulted &&
+	(
+		cd bare.defaulted &&
+		git bisect start &&
+		git bisect good $HASH1 &&
+		git bisect bad $HASH4 &&
+		git bisect run eval \
+			"test \$(git rev-list BISECT_HEAD ^$HASH2 --max-count=1 | wc -l) = 0" \
+			>../defaulted.log &&
+		git bisect reset
+	) &&
+	grep "$HASH3 is the first bad commit" defaulted.log
+'
+
 #
+# This creates a broken branch which cannot be checked out because
+# the tree created has been deleted.
 #
+# H1-H2-H3-H4-H5-H6-H7  <--other
+#            \
+#             S5-S6'-S7'-S8'-S9  <--broken
+#
+# Commits marked with ' have a missing tree.
+#
+test_expect_success 'broken branch creation' '
+	git bisect reset &&
+	git checkout -b broken $HASH4 &&
+	git tag BROKEN_HASH4 $HASH4 &&
+	add_line_into_file "5(broken): first line on a broken branch" hello2 &&
+	git tag BROKEN_HASH5 &&
+	mkdir missing &&
+	:> missing/MISSING &&
+	git add missing/MISSING &&
+	git commit -m "6(broken): Added file that will be deleted"
+	git tag BROKEN_HASH6 &&
+	add_line_into_file "7(broken): second line on a broken branch" hello2 &&
+	git tag BROKEN_HASH7 &&
+	add_line_into_file "8(broken): third line on a broken branch" hello2 &&
+	git tag BROKEN_HASH8 &&
+	git rm missing/MISSING &&
+	git commit -m "9(broken): Remove missing file"
+	git tag BROKEN_HASH9 &&
+	rm .git/objects/39/f7e61a724187ab767d2e08442d9b6b9dab587d
+'
+
+echo "" > expected.ok
+cat > expected.missing-tree.default <<EOF
+fatal: unable to read tree 39f7e61a724187ab767d2e08442d9b6b9dab587d
+EOF
+
+test_expect_success 'bisect fails if tree is broken on start commit' '
+	git bisect reset &&
+	test_must_fail git bisect start BROKEN_HASH7 BROKEN_HASH4 2>error.txt &&
+	test_cmp expected.missing-tree.default error.txt
+'
+
+test_expect_success 'bisect fails if tree is broken on trial commit' '
+	git bisect reset &&
+	test_must_fail git bisect start BROKEN_HASH9 BROKEN_HASH4 2>error.txt &&
+	git reset --hard broken &&
+	git checkout broken &&
+	test_cmp expected.missing-tree.default error.txt
+'
+
+check_same()
+{
+	echo "Checking $1 is the same as $2" &&
+	git rev-parse "$1" > expected.same &&
+	git rev-parse "$2" > expected.actual &&
+	test_cmp expected.same expected.actual
+}
+
+test_expect_success 'bisect: --no-checkout - start commit bad' '
+	git bisect reset &&
+	git bisect start BROKEN_HASH7 BROKEN_HASH4 --no-checkout &&
+	check_same BROKEN_HASH6 BISECT_HEAD &&
+	git bisect reset
+'
+
+test_expect_success 'bisect: --no-checkout - trial commit bad' '
+	git bisect reset &&
+	git bisect start broken BROKEN_HASH4 --no-checkout &&
+	check_same BROKEN_HASH6 BISECT_HEAD &&
+	git bisect reset
+'
+
+test_expect_success 'bisect: --no-checkout - target before breakage' '
+	git bisect reset &&
+	git bisect start broken BROKEN_HASH4 --no-checkout &&
+	check_same BROKEN_HASH6 BISECT_HEAD &&
+	git bisect bad BISECT_HEAD &&
+	check_same BROKEN_HASH5 BISECT_HEAD &&
+	git bisect bad BISECT_HEAD &&
+	check_same BROKEN_HASH5 bisect/bad &&
+	git bisect reset
+'
+
+test_expect_success 'bisect: --no-checkout - target in breakage' '
+	git bisect reset &&
+	git bisect start broken BROKEN_HASH4 --no-checkout &&
+	check_same BROKEN_HASH6 BISECT_HEAD &&
+	git bisect bad BISECT_HEAD &&
+	check_same BROKEN_HASH5 BISECT_HEAD &&
+	git bisect good BISECT_HEAD &&
+	check_same BROKEN_HASH6 bisect/bad &&
+	git bisect reset
+'
+
+test_expect_success 'bisect: --no-checkout - target after breakage' '
+	git bisect reset &&
+	git bisect start broken BROKEN_HASH4 --no-checkout &&
+	check_same BROKEN_HASH6 BISECT_HEAD &&
+	git bisect good BISECT_HEAD &&
+	check_same BROKEN_HASH8 BISECT_HEAD &&
+	git bisect good BISECT_HEAD &&
+	check_same BROKEN_HASH9 bisect/bad &&
+	git bisect reset
+'
+
+test_expect_success 'bisect: demonstrate identification of damage boundary' "
+	git bisect reset &&
+	git checkout broken &&
+	git bisect start broken master --no-checkout &&
+	git bisect run \"\$SHELL_PATH\" -c '
+		GOOD=\$(git for-each-ref \"--format=%(objectname)\" refs/bisect/good-*) &&
+		git rev-list --objects BISECT_HEAD --not \$GOOD >tmp.\$\$ &&
+		git pack-objects --stdout >/dev/null < tmp.\$\$
+		rc=\$?
+		rm -f tmp.\$\$
+		test \$rc = 0' &&
+	check_same BROKEN_HASH6 bisect/bad &&
+	git bisect reset
+"
+
 test_done
diff --git a/t/t6031-merge-recursive.sh b/t/t6031-merge-recursive.sh
index 8a3304f..1cd649e 100755
--- a/t/t6031-merge-recursive.sh
+++ b/t/t6031-merge-recursive.sh
@@ -2,11 +2,7 @@
 
 test_description='merge-recursive: handle file mode'
 . ./test-lib.sh
-
-if ! test "$(git config --bool core.filemode)" = false
-then
-	test_set_prereq FILEMODE
-fi
+. "$TEST_DIRECTORY"/lib-prereq-FILEMODE.sh
 
 test_expect_success 'mode change in one branch: keep changed version' '
 	: >file1 &&
@@ -57,4 +53,35 @@
 	test -x file2
 '
 
+test_expect_success 'merging with triple rename across D/F conflict' '
+	git reset --hard HEAD &&
+	git checkout -b main &&
+	git rm -rf . &&
+
+	echo "just a file" >sub1 &&
+	mkdir -p sub2 &&
+	echo content1 >sub2/file1 &&
+	echo content2 >sub2/file2 &&
+	echo content3 >sub2/file3 &&
+	mkdir simple &&
+	echo base >simple/bar &&
+	git add -A &&
+	test_tick &&
+	git commit -m base &&
+
+	git checkout -b other &&
+	echo more >>simple/bar &&
+	test_tick &&
+	git commit -a -m changesimplefile &&
+
+	git checkout main &&
+	git rm sub1 &&
+	git mv sub2 sub1 &&
+	test_tick &&
+	git commit -m changefiletodir &&
+
+	test_tick &&
+	git merge other
+'
+
 test_done
diff --git a/t/t6032-merge-large-rename.sh b/t/t6032-merge-large-rename.sh
index eac5eba..15beecc 100755
--- a/t/t6032-merge-large-rename.sh
+++ b/t/t6032-merge-large-rename.sh
@@ -70,4 +70,34 @@
 test_rename 5 ok
 test_rename 6 fail
 
+test_expect_success 'setup large simple rename' '
+	git config --unset merge.renamelimit &&
+	git config --unset diff.renamelimit &&
+
+	git reset --hard initial &&
+	for i in $(count 200); do
+		make_text foo bar baz >$i
+	done &&
+	git add . &&
+	git commit -m create-files &&
+
+	git branch simple-change &&
+	git checkout -b simple-rename &&
+
+	mkdir builtin &&
+	git mv [0-9]* builtin/ &&
+	git commit -m renamed &&
+
+	git checkout simple-change &&
+	>unrelated-change &&
+	git add unrelated-change &&
+	git commit -m unrelated-change
+'
+
+test_expect_success 'massive simple rename does not spam added files' '
+	sane_unset GIT_MERGE_VERBOSITY &&
+	git merge --no-stat simple-rename | grep -v Removing >output &&
+	test_line_count -lt 5 output
+'
+
 test_done
diff --git a/t/t6033-merge-crlf.sh b/t/t6033-merge-crlf.sh
index 75d9602..e8d65ee 100755
--- a/t/t6033-merge-crlf.sh
+++ b/t/t6033-merge-crlf.sh
@@ -1,13 +1,5 @@
 #!/bin/sh
 
-append_cr () {
-	sed -e 's/$/Q/' | tr Q '\015'
-}
-
-remove_cr () {
-	tr '\015' Q | sed -e 's/Q$//'
-}
-
 test_description='merge conflict in crlf repo
 
 		b---M
diff --git a/t/t6035-merge-dir-to-symlink.sh b/t/t6035-merge-dir-to-symlink.sh
new file mode 100755
index 0000000..2599ae5
--- /dev/null
+++ b/t/t6035-merge-dir-to-symlink.sh
@@ -0,0 +1,151 @@
+#!/bin/sh
+
+test_description='merging when a directory was replaced with a symlink'
+. ./test-lib.sh
+
+test_expect_success SYMLINKS 'create a commit where dir a/b changed to symlink' '
+	mkdir -p a/b/c a/b-2/c &&
+	> a/b/c/d &&
+	> a/b-2/c/d &&
+	> a/x &&
+	git add -A &&
+	git commit -m base &&
+	git tag start &&
+	rm -rf a/b &&
+	ln -s b-2 a/b &&
+	git add -A &&
+	git commit -m "dir to symlink"
+'
+
+test_expect_success SYMLINKS 'checkout does not clobber untracked symlink' '
+	git checkout HEAD^0 &&
+	git reset --hard master &&
+	git rm --cached a/b &&
+	git commit -m "untracked symlink remains" &&
+	test_must_fail git checkout start^0
+'
+
+test_expect_success SYMLINKS 'a/b-2/c/d is kept when clobbering symlink b' '
+	git checkout HEAD^0 &&
+	git reset --hard master &&
+	git rm --cached a/b &&
+	git commit -m "untracked symlink remains" &&
+	git checkout -f start^0 &&
+	test -f a/b-2/c/d
+'
+
+test_expect_success SYMLINKS 'checkout should not have deleted a/b-2/c/d' '
+	git checkout HEAD^0 &&
+	git reset --hard master &&
+	 git checkout start^0 &&
+	 test -f a/b-2/c/d
+'
+
+test_expect_success SYMLINKS 'setup for merge test' '
+	git reset --hard &&
+	test -f a/b-2/c/d &&
+	echo x > a/x &&
+	git add a/x &&
+	git commit -m x &&
+	git tag baseline
+'
+
+test_expect_success SYMLINKS 'Handle D/F conflict, do not lose a/b-2/c/d in merge (resolve)' '
+	git reset --hard &&
+	git checkout baseline^0 &&
+	git merge -s resolve master &&
+	test -h a/b &&
+	test -f a/b-2/c/d
+'
+
+test_expect_success SYMLINKS 'Handle D/F conflict, do not lose a/b-2/c/d in merge (recursive)' '
+	git reset --hard &&
+	git checkout baseline^0 &&
+	git merge -s recursive master &&
+	test -h a/b &&
+	test -f a/b-2/c/d
+'
+
+test_expect_success SYMLINKS 'Handle F/D conflict, do not lose a/b-2/c/d in merge (resolve)' '
+	git reset --hard &&
+	git checkout master^0 &&
+	git merge -s resolve baseline^0 &&
+	test -h a/b &&
+	test -f a/b-2/c/d
+'
+
+test_expect_success SYMLINKS 'Handle F/D conflict, do not lose a/b-2/c/d in merge (recursive)' '
+	git reset --hard &&
+	git checkout master^0 &&
+	git merge -s recursive baseline^0 &&
+	test -h a/b &&
+	test -f a/b-2/c/d
+'
+
+test_expect_failure SYMLINKS 'do not lose untracked in merge (resolve)' '
+	git reset --hard &&
+	git checkout baseline^0 &&
+	>a/b/c/e &&
+	test_must_fail git merge -s resolve master &&
+	test -f a/b/c/e &&
+	test -f a/b-2/c/d
+'
+
+test_expect_success SYMLINKS 'do not lose untracked in merge (recursive)' '
+	git reset --hard &&
+	git checkout baseline^0 &&
+	>a/b/c/e &&
+	test_must_fail git merge -s recursive master &&
+	test -f a/b/c/e &&
+	test -f a/b-2/c/d
+'
+
+test_expect_success SYMLINKS 'do not lose modifications in merge (resolve)' '
+	git reset --hard &&
+	git checkout baseline^0 &&
+	echo more content >>a/b/c/d &&
+	test_must_fail git merge -s resolve master
+'
+
+test_expect_success SYMLINKS 'do not lose modifications in merge (recursive)' '
+	git reset --hard &&
+	git checkout baseline^0 &&
+	echo more content >>a/b/c/d &&
+	test_must_fail git merge -s recursive master
+'
+
+test_expect_success SYMLINKS 'setup a merge where dir a/b-2 changed to symlink' '
+	git reset --hard &&
+	git checkout start^0 &&
+	rm -rf a/b-2 &&
+	ln -s b a/b-2 &&
+	git add -A &&
+	git commit -m "dir a/b-2 to symlink" &&
+	git tag test2
+'
+
+test_expect_success SYMLINKS 'merge should not have D/F conflicts (resolve)' '
+	git reset --hard &&
+	git checkout baseline^0 &&
+	git merge -s resolve test2 &&
+	test -h a/b-2 &&
+	test -f a/b/c/d
+'
+
+test_expect_success SYMLINKS 'merge should not have D/F conflicts (recursive)' '
+	git reset --hard &&
+	git checkout baseline^0 &&
+	git merge -s recursive test2 &&
+	test -h a/b-2 &&
+	test -f a/b/c/d
+'
+
+test_expect_success SYMLINKS 'merge should not have F/D conflicts (recursive)' '
+	git reset --hard &&
+	git checkout -b foo test2 &&
+	git merge -s recursive baseline^0 &&
+	test -h a/b-2 &&
+	test -f a/b/c/d
+'
+
+test_done
diff --git a/t/t6036-recursive-corner-cases.sh b/t/t6036-recursive-corner-cases.sh
new file mode 100755
index 0000000..dfee7d1
--- /dev/null
+++ b/t/t6036-recursive-corner-cases.sh
@@ -0,0 +1,776 @@
+#!/bin/sh
+
+test_description='recursive merge corner cases involving criss-cross merges'
+
+. ./test-lib.sh
+
+get_clean_checkout () {
+	git reset --hard &&
+	git clean -fdqx &&
+	git checkout "$1"
+}
+
+#
+#  L1  L2
+#   o---o
+#  / \ / \
+# o   X   ?
+#  \ / \ /
+#   o---o
+#  R1  R2
+#
+
+test_expect_success 'setup basic criss-cross + rename with no modifications' '
+	ten="0 1 2 3 4 5 6 7 8 9" &&
+	for i in $ten
+	do
+		echo line $i in a sample file
+	done >one &&
+	for i in $ten
+	do
+		echo line $i in another sample file
+	done >two &&
+	git add one two &&
+	test_tick && git commit -m initial &&
+
+	git branch L1 &&
+	git checkout -b R1 &&
+	git mv one three &&
+	test_tick && git commit -m R1 &&
+
+	git checkout L1 &&
+	git mv two three &&
+	test_tick && git commit -m L1 &&
+
+	git checkout L1^0 &&
+	test_tick && git merge -s ours R1 &&
+	git tag L2 &&
+
+	git checkout R1^0 &&
+	test_tick && git merge -s ours L1 &&
+	git tag R2
+'
+
+test_expect_success 'merge simple rename+criss-cross with no modifications' '
+	git reset --hard &&
+	git checkout L2^0 &&
+
+	test_must_fail git merge -s recursive R2^0 &&
+
+	test 2 = $(git ls-files -s | wc -l) &&
+	test 2 = $(git ls-files -u | wc -l) &&
+	test 2 = $(git ls-files -o | wc -l) &&
+
+	test $(git rev-parse :2:three) = $(git rev-parse L2:three) &&
+	test $(git rev-parse :3:three) = $(git rev-parse R2:three) &&
+
+	test $(git rev-parse L2:three) = $(git hash-object three~HEAD) &&
+	test $(git rev-parse R2:three) = $(git hash-object three~R2^0)
+'
+
+#
+# Same as before, but modify L1 slightly:
+#
+#  L1m L2
+#   o---o
+#  / \ / \
+# o   X   ?
+#  \ / \ /
+#   o---o
+#  R1  R2
+#
+
+test_expect_success 'setup criss-cross + rename merges with basic modification' '
+	git rm -rf . &&
+	git clean -fdqx &&
+	rm -rf .git &&
+	git init &&
+
+	ten="0 1 2 3 4 5 6 7 8 9"
+	for i in $ten
+	do
+		echo line $i in a sample file
+	done >one &&
+	for i in $ten
+	do
+		echo line $i in another sample file
+	done >two &&
+	git add one two &&
+	test_tick && git commit -m initial &&
+
+	git branch L1 &&
+	git checkout -b R1 &&
+	git mv one three &&
+	echo more >>two &&
+	git add two &&
+	test_tick && git commit -m R1 &&
+
+	git checkout L1 &&
+	git mv two three &&
+	test_tick && git commit -m L1 &&
+
+	git checkout L1^0 &&
+	test_tick && git merge -s ours R1 &&
+	git tag L2 &&
+
+	git checkout R1^0 &&
+	test_tick && git merge -s ours L1 &&
+	git tag R2
+'
+
+test_expect_success 'merge criss-cross + rename merges with basic modification' '
+	git reset --hard &&
+	git checkout L2^0 &&
+
+	test_must_fail git merge -s recursive R2^0 &&
+
+	test 2 = $(git ls-files -s | wc -l) &&
+	test 2 = $(git ls-files -u | wc -l) &&
+	test 2 = $(git ls-files -o | wc -l) &&
+
+	test $(git rev-parse :2:three) = $(git rev-parse L2:three) &&
+	test $(git rev-parse :3:three) = $(git rev-parse R2:three) &&
+
+	test $(git rev-parse L2:three) = $(git hash-object three~HEAD) &&
+	test $(git rev-parse R2:three) = $(git hash-object three~R2^0)
+'
+
+#
+# For the next test, we start with three commits in two lines of development
+# which setup a rename/add conflict:
+#   Commit A: File 'a' exists
+#   Commit B: Rename 'a' -> 'new_a'
+#   Commit C: Modify 'a', create different 'new_a'
+# Later, two different people merge and resolve differently:
+#   Commit D: Merge B & C, ignoring separately created 'new_a'
+#   Commit E: Merge B & C making use of some piece of secondary 'new_a'
+# Finally, someone goes to merge D & E.  Does git detect the conflict?
+#
+#      B   D
+#      o---o
+#     / \ / \
+#  A o   X   ? F
+#     \ / \ /
+#      o---o
+#      C   E
+#
+
+test_expect_success 'setup differently handled merges of rename/add conflict' '
+	git rm -rf . &&
+	git clean -fdqx &&
+	rm -rf .git &&
+	git init &&
+
+	printf "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n" >a &&
+	git add a &&
+	test_tick && git commit -m A &&
+
+	git branch B &&
+	git checkout -b C &&
+	echo 10 >>a &&
+	echo "other content" >>new_a &&
+	git add a new_a &&
+	test_tick && git commit -m C &&
+
+	git checkout B &&
+	git mv a new_a &&
+	test_tick && git commit -m B &&
+
+	git checkout B^0 &&
+	test_must_fail git merge C &&
+	git clean -f &&
+	test_tick && git commit -m D &&
+	git tag D &&
+
+	git checkout C^0 &&
+	test_must_fail git merge B &&
+	rm new_a~HEAD new_a &&
+	printf "Incorrectly merged content" >>new_a &&
+	git add -u &&
+	test_tick && git commit -m E &&
+	git tag E
+'
+
+test_expect_success 'git detects differently handled merges conflict' '
+	git reset --hard &&
+	git checkout D^0 &&
+
+	git merge -s recursive E^0 && {
+		echo "BAD: should have conflicted"
+		test "Incorrectly merged content" = "$(cat new_a)" &&
+			echo "BAD: Silently accepted wrong content"
+		return 1
+	}
+
+	test 3 = $(git ls-files -s | wc -l) &&
+	test 3 = $(git ls-files -u | wc -l) &&
+	test 0 = $(git ls-files -o | wc -l) &&
+
+	test $(git rev-parse :2:new_a) = $(git rev-parse D:new_a) &&
+	test $(git rev-parse :3:new_a) = $(git rev-parse E:new_a) &&
+
+	git cat-file -p B:new_a >>merged &&
+	git cat-file -p C:new_a >>merge-me &&
+	>empty &&
+	test_must_fail git merge-file \
+		-L "Temporary merge branch 2" \
+		-L "" \
+		-L "Temporary merge branch 1" \
+		merged empty merge-me &&
+	test $(git rev-parse :1:new_a) = $(git hash-object merged)
+'
+
+#
+# criss-cross + modify/delete:
+#
+#      B   D
+#      o---o
+#     / \ / \
+#  A o   X   ? F
+#     \ / \ /
+#      o---o
+#      C   E
+#
+#   Commit A: file with contents 'A\n'
+#   Commit B: file with contents 'B\n'
+#   Commit C: file not present
+#   Commit D: file with contents 'B\n'
+#   Commit E: file not present
+#
+# Merging commits D & E should result in modify/delete conflict.
+
+test_expect_success 'setup criss-cross + modify/delete resolved differently' '
+	git rm -rf . &&
+	git clean -fdqx &&
+	rm -rf .git &&
+	git init &&
+
+	echo A >file &&
+	git add file &&
+	test_tick &&
+	git commit -m A &&
+
+	git branch B &&
+	git checkout -b C &&
+	git rm file &&
+	test_tick &&
+	git commit -m C &&
+
+	git checkout B &&
+	echo B >file &&
+	git add file &&
+	test_tick &&
+	git commit -m B &&
+
+	git checkout B^0 &&
+	test_must_fail git merge C &&
+	echo B >file &&
+	git add file &&
+	test_tick &&
+	git commit -m D &&
+	git tag D &&
+
+	git checkout C^0 &&
+	test_must_fail git merge B &&
+	git rm file &&
+	test_tick &&
+	git commit -m E &&
+	git tag E
+'
+
+test_expect_success 'git detects conflict merging criss-cross+modify/delete' '
+	git checkout D^0 &&
+
+	test_must_fail git merge -s recursive E^0 &&
+
+	test 2 -eq $(git ls-files -s | wc -l) &&
+	test 2 -eq $(git ls-files -u | wc -l) &&
+
+	test $(git rev-parse :1:file) = $(git rev-parse master:file) &&
+	test $(git rev-parse :2:file) = $(git rev-parse B:file)
+'
+
+test_expect_success 'git detects conflict merging criss-cross+modify/delete, reverse direction' '
+	git reset --hard &&
+	git checkout E^0 &&
+
+	test_must_fail git merge -s recursive D^0 &&
+
+	test 2 -eq $(git ls-files -s | wc -l) &&
+	test 2 -eq $(git ls-files -u | wc -l) &&
+
+	test $(git rev-parse :1:file) = $(git rev-parse master:file) &&
+	test $(git rev-parse :3:file) = $(git rev-parse B:file)
+'
+
+#
+# criss-cross + modify/modify with very contrived file contents:
+#
+#      B   D
+#      o---o
+#     / \ / \
+#  A o   X   ? F
+#     \ / \ /
+#      o---o
+#      C   E
+#
+#   Commit A: file with contents 'A\n'
+#   Commit B: file with contents 'B\n'
+#   Commit C: file with contents 'C\n'
+#   Commit D: file with contents 'D\n'
+#   Commit E: file with contents:
+#      <<<<<<< Temporary merge branch 1
+#      C
+#      =======
+#      B
+#      >>>>>>> Temporary merge branch 2
+#
+# Now, when we merge commits D & E, does git detect the conflict?
+
+test_expect_success 'setup differently handled merges of content conflict' '
+	git clean -fdqx &&
+	rm -rf .git &&
+	git init &&
+
+	echo A >file &&
+	git add file &&
+	test_tick &&
+	git commit -m A &&
+
+	git branch B &&
+	git checkout -b C &&
+	echo C >file &&
+	git add file &&
+	test_tick &&
+	git commit -m C &&
+
+	git checkout B &&
+	echo B >file &&
+	git add file &&
+	test_tick &&
+	git commit -m B &&
+
+	git checkout B^0 &&
+	test_must_fail git merge C &&
+	echo D >file &&
+	git add file &&
+	test_tick &&
+	git commit -m D &&
+	git tag D &&
+
+	git checkout C^0 &&
+	test_must_fail git merge B &&
+	cat <<EOF >file &&
+<<<<<<< Temporary merge branch 1
+C
+=======
+B
+>>>>>>> Temporary merge branch 2
+EOF
+	git add file &&
+	test_tick &&
+	git commit -m E &&
+	git tag E
+'
+
+test_expect_failure 'git detects conflict w/ criss-cross+contrived resolution' '
+	git checkout D^0 &&
+
+	test_must_fail git merge -s recursive E^0 &&
+
+	test 3 -eq $(git ls-files -s | wc -l) &&
+	test 3 -eq $(git ls-files -u | wc -l) &&
+	test 0 -eq $(git ls-files -o | wc -l) &&
+
+	test $(git rev-parse :2:file) = $(git rev-parse D:file) &&
+	test $(git rev-parse :3:file) = $(git rev-parse E:file)
+'
+
+#
+# criss-cross + d/f conflict via add/add:
+#   Commit A: Neither file 'a' nor directory 'a/' exist.
+#   Commit B: Introduce 'a'
+#   Commit C: Introduce 'a/file'
+#   Commit D: Merge B & C, keeping 'a' and deleting 'a/'
+#
+# Two different later cases:
+#   Commit E1: Merge B & C, deleting 'a' but keeping 'a/file'
+#   Commit E2: Merge B & C, deleting 'a' but keeping a slightly modified 'a/file'
+#
+#      B   D
+#      o---o
+#     / \ / \
+#  A o   X   ? F
+#     \ / \ /
+#      o---o
+#      C   E1 or E2
+#
+# Merging D & E1 requires we first create a virtual merge base X from
+# merging A & B in memory.  Now, if X could keep both 'a' and 'a/file' in
+# the index, then the merge of D & E1 could be resolved cleanly with both
+# 'a' and 'a/file' removed.  Since git does not currently allow creating
+# such a tree, the best we can do is have X contain both 'a~<unique>' and
+# 'a/file' resulting in the merge of D and E1 having a rename/delete
+# conflict for 'a'.  (Although this merge appears to be unsolvable with git
+# currently, git could do a lot better than it currently does with these
+# d/f conflicts, which is the purpose of this test.)
+#
+# Merge of D & E2 has similar issues for path 'a', but should always result
+# in a modify/delete conflict for path 'a/file'.
+#
+# We run each merge in both directions, to check for directional issues
+# with D/F conflict handling.
+#
+
+test_expect_success 'setup differently handled merges of directory/file conflict' '
+	git rm -rf . &&
+	git clean -fdqx &&
+	rm -rf .git &&
+	git init &&
+
+	>ignore-me &&
+	git add ignore-me &&
+	test_tick &&
+	git commit -m A &&
+	git tag A &&
+
+	git branch B &&
+	git checkout -b C &&
+	mkdir a &&
+	echo 10 >a/file &&
+	git add a/file &&
+	test_tick &&
+	git commit -m C &&
+
+	git checkout B &&
+	echo 5 >a &&
+	git add a &&
+	test_tick &&
+	git commit -m B &&
+
+	git checkout B^0 &&
+	test_must_fail git merge C &&
+	git clean -f &&
+	rm -rf a/ &&
+	echo 5 >a &&
+	git add a &&
+	test_tick &&
+	git commit -m D &&
+	git tag D &&
+
+	git checkout C^0 &&
+	test_must_fail git merge B &&
+	git clean -f &&
+	git rm --cached a &&
+	echo 10 >a/file &&
+	git add a/file &&
+	test_tick &&
+	git commit -m E1 &&
+	git tag E1 &&
+
+	git checkout C^0 &&
+	test_must_fail git merge B &&
+	git clean -f &&
+	git rm --cached a &&
+	printf "10\n11\n" >a/file &&
+	git add a/file &&
+	test_tick &&
+	git commit -m E2 &&
+	git tag E2
+'
+
+test_expect_success 'merge of D & E1 fails but has appropriate contents' '
+	get_clean_checkout D^0 &&
+
+	test_must_fail git merge -s recursive E1^0 &&
+
+	test 2 -eq $(git ls-files -s | wc -l) &&
+	test 1 -eq $(git ls-files -u | wc -l) &&
+	test 0 -eq $(git ls-files -o | wc -l) &&
+
+	test $(git rev-parse :0:ignore-me) = $(git rev-parse A:ignore-me) &&
+	test $(git rev-parse :2:a) = $(git rev-parse B:a)
+'
+
+test_expect_success 'merge of E1 & D fails but has appropriate contents' '
+	get_clean_checkout E1^0 &&
+
+	test_must_fail git merge -s recursive D^0 &&
+
+	test 2 -eq $(git ls-files -s | wc -l) &&
+	test 1 -eq $(git ls-files -u | wc -l) &&
+	test 0 -eq $(git ls-files -o | wc -l) &&
+
+	test $(git rev-parse :0:ignore-me) = $(git rev-parse A:ignore-me) &&
+	test $(git rev-parse :3:a) = $(git rev-parse B:a)
+'
+
+test_expect_success 'merge of D & E2 fails but has appropriate contents' '
+	get_clean_checkout D^0 &&
+
+	test_must_fail git merge -s recursive E2^0 &&
+
+	test 4 -eq $(git ls-files -s | wc -l) &&
+	test 3 -eq $(git ls-files -u | wc -l) &&
+	test 1 -eq $(git ls-files -o | wc -l) &&
+
+	test $(git rev-parse :2:a) = $(git rev-parse B:a) &&
+	test $(git rev-parse :3:a/file) = $(git rev-parse E2:a/file) &&
+	test $(git rev-parse :1:a/file) = $(git rev-parse C:a/file) &&
+	test $(git rev-parse :0:ignore-me) = $(git rev-parse A:ignore-me) &&
+
+	test -f a~HEAD
+'
+
+test_expect_success 'merge of E2 & D fails but has appropriate contents' '
+	get_clean_checkout E2^0 &&
+
+	test_must_fail git merge -s recursive D^0 &&
+
+	test 4 -eq $(git ls-files -s | wc -l) &&
+	test 3 -eq $(git ls-files -u | wc -l) &&
+	test 1 -eq $(git ls-files -o | wc -l) &&
+
+	test $(git rev-parse :3:a) = $(git rev-parse B:a) &&
+	test $(git rev-parse :2:a/file) = $(git rev-parse E2:a/file) &&
+	test $(git rev-parse :1:a/file) = $(git rev-parse C:a/file)
+	test $(git rev-parse :0:ignore-me) = $(git rev-parse A:ignore-me) &&
+
+	test -f a~D^0
+'
+
+#
+# criss-cross with rename/rename(1to2)/modify followed by
+# rename/rename(2to1)/modify:
+#
+#      B   D
+#      o---o
+#     / \ / \
+#  A o   X   ? F
+#     \ / \ /
+#      o---o
+#      C   E
+#
+#   Commit A: new file: a
+#   Commit B: rename a->b, modifying by adding a line
+#   Commit C: rename a->c
+#   Commit D: merge B&C, resolving conflict by keeping contents in newname
+#   Commit E: merge B&C, resolving conflict similar to D but adding another line
+#
+# There is a conflict merging B & C, but one of filename not of file
+# content.  Whoever created D and E chose specific resolutions for that
+# conflict resolution.  Now, since: (1) there is no content conflict
+# merging B & C, (2) D does not modify that merged content further, and (3)
+# both D & E resolve the name conflict in the same way, the modification to
+# newname in E should not cause any conflicts when it is merged with D.
+# (Note that this can be accomplished by having the virtual merge base have
+# the merged contents of b and c stored in a file named a, which seems like
+# the most logical choice anyway.)
+#
+# Comment from Junio: I do not necessarily agree with the choice "a", but
+# it feels sound to say "B and C do not agree what the final pathname
+# should be, but we know this content was derived from the common A:a so we
+# use one path whose name is arbitrary in the virtual merge base X between
+# D and E" and then further let the rename detection to notice that that
+# arbitrary path gets renamed between X-D to "newname" and X-E also to
+# "newname" to resolve it as both sides renaming it to the same new
+# name. It is akin to what we do at the content level, i.e. "B and C do not
+# agree what the final contents should be, so we leave the conflict marker
+# but that may cancel out at the final merge stage".
+
+test_expect_success 'setup rename/rename(1to2)/modify followed by what looks like rename/rename(2to1)/modify' '
+	git reset --hard &&
+	git rm -rf . &&
+	git clean -fdqx &&
+	rm -rf .git &&
+	git init &&
+
+	printf "1\n2\n3\n4\n5\n6\n" >a &&
+	git add a &&
+	git commit -m A &&
+	git tag A &&
+
+	git checkout -b B A &&
+	git mv a b &&
+	echo 7 >>b &&
+	git add -u &&
+	git commit -m B &&
+
+	git checkout -b C A &&
+	git mv a c &&
+	git commit -m C &&
+
+	git checkout -q B^0 &&
+	git merge --no-commit -s ours C^0 &&
+	git mv b newname &&
+	git commit -m "Merge commit C^0 into HEAD" &&
+	git tag D &&
+
+	git checkout -q C^0 &&
+	git merge --no-commit -s ours B^0 &&
+	git mv c newname &&
+	printf "7\n8\n" >>newname &&
+	git add -u &&
+	git commit -m "Merge commit B^0 into HEAD" &&
+	git tag E
+'
+
+test_expect_success 'handle rename/rename(1to2)/modify followed by what looks like rename/rename(2to1)/modify' '
+	git checkout D^0 &&
+
+	git merge -s recursive E^0 &&
+
+	test 1 -eq $(git ls-files -s | wc -l) &&
+	test 0 -eq $(git ls-files -u | wc -l) &&
+	test 0 -eq $(git ls-files -o | wc -l) &&
+
+	test $(git rev-parse HEAD:newname) = $(git rev-parse E:newname)
+'
+
+#
+# criss-cross with rename/rename(1to2)/add-source + resolvable modify/modify:
+#
+#      B   D
+#      o---o
+#     / \ / \
+#  A o   X   ? F
+#     \ / \ /
+#      o---o
+#      C   E
+#
+#   Commit A: new file: a
+#   Commit B: rename a->b
+#   Commit C: rename a->c, add different a
+#   Commit D: merge B&C, keeping b&c and (new) a modified at beginning
+#   Commit E: merge B&C, keeping b&c and (new) a modified at end
+#
+# Merging commits D & E should result in no conflict; doing so correctly
+# requires getting the virtual merge base (from merging B&C) right, handling
+# renaming carefully (both in the virtual merge base and later), and getting
+# content merge handled.
+
+test_expect_success 'setup criss-cross + rename/rename/add + modify/modify' '
+	git rm -rf . &&
+	git clean -fdqx &&
+	rm -rf .git &&
+	git init &&
+
+	printf "lots\nof\nwords\nand\ncontent\n" >a &&
+	git add a &&
+	git commit -m A &&
+	git tag A &&
+
+	git checkout -b B A &&
+	git mv a b &&
+	git commit -m B &&
+
+	git checkout -b C A &&
+	git mv a c &&
+	printf "2\n3\n4\n5\n6\n7\n" >a &&
+	git add a &&
+	git commit -m C &&
+
+	git checkout B^0 &&
+	git merge --no-commit -s ours C^0 &&
+	git checkout C -- a c &&
+	mv a old_a &&
+	echo 1 >a &&
+	cat old_a >>a &&
+	rm old_a &&
+	git add -u &&
+	git commit -m "Merge commit C^0 into HEAD" &&
+	git tag D &&
+
+	git checkout C^0 &&
+	git merge --no-commit -s ours B^0 &&
+	git checkout B -- b &&
+	echo 8 >>a &&
+	git add -u &&
+	git commit -m "Merge commit B^0 into HEAD" &&
+	git tag E
+'
+
+test_expect_failure 'detect rename/rename/add-source for virtual merge-base' '
+	git checkout D^0 &&
+
+	git merge -s recursive E^0 &&
+
+	test 3 -eq $(git ls-files -s | wc -l) &&
+	test 0 -eq $(git ls-files -u | wc -l) &&
+	test 0 -eq $(git ls-files -o | wc -l) &&
+
+	test $(git rev-parse HEAD:b) = $(git rev-parse A:a) &&
+	test $(git rev-parse HEAD:c) = $(git rev-parse A:a) &&
+	test "$(cat a)" = "$(printf "1\n2\n3\n4\n5\n6\n7\n8\n")"
+'
+
+#
+# criss-cross with rename/rename(1to2)/add-dest + simple modify:
+#
+#      B   D
+#      o---o
+#     / \ / \
+#  A o   X   ? F
+#     \ / \ /
+#      o---o
+#      C   E
+#
+#   Commit A: new file: a
+#   Commit B: rename a->b, add c
+#   Commit C: rename a->c
+#   Commit D: merge B&C, keeping A:a and B:c
+#   Commit E: merge B&C, keeping A:a and slightly modified c from B
+#
+# Merging commits D & E should result in no conflict.  The virtual merge
+# base of B & C needs to not delete B:c for that to work, though...
+
+test_expect_success 'setup criss-cross+rename/rename/add-dest + simple modify' '
+	git rm -rf . &&
+	git clean -fdqx &&
+	rm -rf .git &&
+	git init &&
+
+	>a &&
+	git add a &&
+	git commit -m A &&
+	git tag A &&
+
+	git checkout -b B A &&
+	git mv a b &&
+	printf "1\n2\n3\n4\n5\n6\n7\n" >c &&
+	git add c &&
+	git commit -m B &&
+
+	git checkout -b C A &&
+	git mv a c &&
+	git commit -m C &&
+
+	git checkout B^0 &&
+	git merge --no-commit -s ours C^0 &&
+	git mv b a &&
+	git commit -m "D is like B but renames b back to a" &&
+	git tag D &&
+
+	git checkout B^0 &&
+	git merge --no-commit -s ours C^0 &&
+	git mv b a &&
+	echo 8 >>c &&
+	git add c &&
+	git commit -m "E like D but has mod in c" &&
+	git tag E
+'
+
+test_expect_success 'virtual merge base handles rename/rename(1to2)/add-dest' '
+	git checkout D^0 &&
+
+	git merge -s recursive E^0 &&
+
+	test 2 -eq $(git ls-files -s | wc -l) &&
+	test 0 -eq $(git ls-files -u | wc -l) &&
+	test 0 -eq $(git ls-files -o | wc -l) &&
+
+	test $(git rev-parse HEAD:a) = $(git rev-parse A:a) &&
+	test $(git rev-parse HEAD:c) = $(git rev-parse E:c)
+'
+
+test_done
diff --git a/t/t6037-merge-ours-theirs.sh b/t/t6037-merge-ours-theirs.sh
new file mode 100755
index 0000000..2cf42c7
--- /dev/null
+++ b/t/t6037-merge-ours-theirs.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+
+test_description='Merge-recursive ours and theirs variants'
+. ./test-lib.sh
+
+test_expect_success setup '
+	for i in 1 2 3 4 5 6 7 8 9
+	do
+		echo "$i"
+	done >file &&
+	git add file &&
+	cp file elif &&
+	git commit -m initial &&
+
+	sed -e "s/1/one/" -e "s/9/nine/" >file <elif &&
+	git commit -a -m ours &&
+
+	git checkout -b side HEAD^ &&
+
+	sed -e "s/9/nueve/" >file <elif &&
+	git commit -a -m theirs &&
+
+	git checkout master^0
+'
+
+test_expect_success 'plain recursive - should conflict' '
+	git reset --hard master &&
+	test_must_fail git merge -s recursive side &&
+	grep nine file &&
+	grep nueve file &&
+	! grep 9 file &&
+	grep one file &&
+	! grep 1 file
+'
+
+test_expect_success 'recursive favouring theirs' '
+	git reset --hard master &&
+	git merge -s recursive -Xtheirs side &&
+	! grep nine file &&
+	grep nueve file &&
+	! grep 9 file &&
+	grep one file &&
+	! grep 1 file
+'
+
+test_expect_success 'recursive favouring ours' '
+	git reset --hard master &&
+	git merge -s recursive -X ours side &&
+	grep nine file &&
+	! grep nueve file &&
+	! grep 9 file &&
+	grep one file &&
+	! grep 1 file
+'
+
+test_expect_success 'pull with -X' '
+	git reset --hard master && git pull -s recursive -Xours . side &&
+	git reset --hard master && git pull -s recursive -X ours . side &&
+	git reset --hard master && git pull -s recursive -Xtheirs . side &&
+	git reset --hard master && git pull -s recursive -X theirs . side &&
+	git reset --hard master && test_must_fail git pull -s recursive -X bork . side
+'
+
+test_done
diff --git a/t/t6038-merge-text-auto.sh b/t/t6038-merge-text-auto.sh
new file mode 100755
index 0000000..d9c2d38
--- /dev/null
+++ b/t/t6038-merge-text-auto.sh
@@ -0,0 +1,191 @@
+#!/bin/sh
+
+test_description='CRLF merge conflict across text=auto change
+
+* [master] remove .gitattributes
+ ! [side] add line from b
+--
+ + [side] add line from b
+*  [master] remove .gitattributes
+*  [master^] add line from a
+*  [master~2] normalize file
+*+ [side^] Initial
+'
+
+. ./test-lib.sh
+
+test_have_prereq SED_STRIPS_CR && SED_OPTIONS=-b
+
+test_expect_success setup '
+	git config core.autocrlf false &&
+
+	echo first line | append_cr >file &&
+	echo first line >control_file &&
+	echo only line >inert_file &&
+
+	git add file control_file inert_file &&
+	test_tick &&
+	git commit -m "Initial" &&
+	git tag initial &&
+	git branch side &&
+
+	echo "* text=auto" >.gitattributes &&
+	touch file &&
+	git add .gitattributes file &&
+	test_tick &&
+	git commit -m "normalize file" &&
+
+	echo same line | append_cr >>file &&
+	echo same line >>control_file &&
+	git add file control_file &&
+	test_tick &&
+	git commit -m "add line from a" &&
+	git tag a &&
+
+	git rm .gitattributes &&
+	rm file &&
+	git checkout file &&
+	test_tick &&
+	git commit -m "remove .gitattributes" &&
+	git tag c &&
+
+	git checkout side &&
+	echo same line | append_cr >>file &&
+	echo same line >>control_file &&
+	git add file control_file &&
+	test_tick &&
+	git commit -m "add line from b" &&
+	git tag b &&
+
+	git checkout master
+'
+
+test_expect_success 'set up fuzz_conflict() helper' '
+	fuzz_conflict() {
+		sed $SED_OPTIONS -e "s/^\([<>=]......\) .*/\1/" "$@"
+	}
+'
+
+test_expect_success 'Merge after setting text=auto' '
+	cat <<-\EOF >expected &&
+	first line
+	same line
+	EOF
+
+	git config merge.renormalize true &&
+	git rm -fr . &&
+	rm -f .gitattributes &&
+	git reset --hard a &&
+	git merge b &&
+	test_cmp expected file
+'
+
+test_expect_success 'Merge addition of text=auto' '
+	cat <<-\EOF >expected &&
+	first line
+	same line
+	EOF
+
+	git config merge.renormalize true &&
+	git rm -fr . &&
+	rm -f .gitattributes &&
+	git reset --hard b &&
+	git merge a &&
+	test_cmp expected file
+'
+
+test_expect_success 'Detect CRLF/LF conflict after setting text=auto' '
+	q_to_cr <<-\EOF >expected &&
+	<<<<<<<
+	first line
+	same line
+	=======
+	first lineQ
+	same lineQ
+	>>>>>>>
+	EOF
+
+	git config merge.renormalize false &&
+	rm -f .gitattributes &&
+	git reset --hard a &&
+	test_must_fail git merge b &&
+	fuzz_conflict file >file.fuzzy &&
+	test_cmp expected file.fuzzy
+'
+
+test_expect_success 'Detect LF/CRLF conflict from addition of text=auto' '
+	q_to_cr <<-\EOF >expected &&
+	<<<<<<<
+	first lineQ
+	same lineQ
+	=======
+	first line
+	same line
+	>>>>>>>
+	EOF
+
+	git config merge.renormalize false &&
+	rm -f .gitattributes &&
+	git reset --hard b &&
+	test_must_fail git merge a &&
+	fuzz_conflict file >file.fuzzy &&
+	test_cmp expected file.fuzzy
+'
+
+test_expect_failure 'checkout -m after setting text=auto' '
+	cat <<-\EOF >expected &&
+	first line
+	same line
+	EOF
+
+	git config merge.renormalize true &&
+	git rm -fr . &&
+	rm -f .gitattributes &&
+	git reset --hard initial &&
+	git checkout a -- . &&
+	git checkout -m b &&
+	test_cmp expected file
+'
+
+test_expect_failure 'checkout -m addition of text=auto' '
+	cat <<-\EOF >expected &&
+	first line
+	same line
+	EOF
+
+	git config merge.renormalize true &&
+	git rm -fr . &&
+	rm -f .gitattributes file &&
+	git reset --hard initial &&
+	git checkout b -- . &&
+	git checkout -m a &&
+	test_cmp expected file
+'
+
+test_expect_failure 'cherry-pick patch from after text=auto was added' '
+	append_cr <<-\EOF >expected &&
+	first line
+	same line
+	EOF
+
+	git config merge.renormalize true &&
+	git rm -fr . &&
+	git reset --hard b &&
+	test_must_fail git cherry-pick a >err 2>&1 &&
+	grep "[Nn]othing added" err &&
+	test_cmp expected file
+'
+
+test_expect_success 'Test delete/normalize conflict' '
+	git checkout -f side &&
+	git rm -fr . &&
+	rm -f .gitattributes &&
+	git reset --hard initial &&
+	git rm file &&
+	git commit -m "remove file" &&
+	git checkout master &&
+	git reset --hard a^ &&
+	git merge side
+'
+
+test_done
diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh
index 00e1de9..ec2b516 100755
--- a/t/t6040-tracking-info.sh
+++ b/t/t6040-tracking-info.sh
@@ -48,20 +48,36 @@
 		git branch -v
 	) |
 	sed -n -e "$script" >actual &&
-	test_cmp expect actual
+	test_i18ncmp expect actual
+'
+
+cat >expect <<\EOF
+b1 origin/master: ahead 1, behind 1
+b2 origin/master: ahead 1, behind 1
+b3 origin/master: behind 1
+b4 origin/master: ahead 2
+EOF
+
+test_expect_success 'branch -vv' '
+	(
+		cd test &&
+		git branch -vv
+	) |
+	sed -n -e "$script" >actual &&
+	test_i18ncmp expect actual
 '
 
 test_expect_success 'checkout' '
 	(
 		cd test && git checkout b1
 	) >actual &&
-	grep "have 1 and 1 different" actual
+	test_i18ngrep "have 1 and 1 different" actual
 '
 
 test_expect_success 'checkout with local tracked branch' '
 	git checkout master &&
-	git checkout follower >actual
-	grep "is ahead of" actual
+	git checkout follower >actual &&
+	test_i18ngrep "is ahead of" actual
 '
 
 test_expect_success 'status' '
@@ -69,24 +85,59 @@
 		cd test &&
 		git checkout b1 >/dev/null &&
 		# reports nothing to commit
-		test_must_fail git status
+		test_must_fail git commit --dry-run
 	) >actual &&
-	grep "have 1 and 1 different" actual
+	test_i18ngrep "have 1 and 1 different" actual
 '
 
-test_expect_success 'status when tracking lightweight tags' '
+test_expect_success 'fail to track lightweight tags' '
 	git checkout master &&
 	git tag light &&
-	git branch --track lighttrack light >actual &&
-	grep "set up to track" actual &&
-	git checkout lighttrack
+	test_must_fail git branch --track lighttrack light >actual &&
+	test_i18ngrep ! "set up to track" actual &&
+	test_must_fail git checkout lighttrack
 '
 
-test_expect_success 'status when tracking annotated tags' '
+test_expect_success 'fail to track annotated tags' '
 	git checkout master &&
 	git tag -m heavy heavy &&
-	git branch --track heavytrack heavy >actual &&
-	grep "set up to track" actual &&
-	git checkout heavytrack
+	test_must_fail git branch --track heavytrack heavy >actual &&
+	test_i18ngrep ! "set up to track" actual &&
+	test_must_fail git checkout heavytrack
 '
+
+test_expect_success 'setup tracking with branch --set-upstream on existing branch' '
+	git branch from-master master &&
+	test_must_fail git config branch.from-master.merge > actual &&
+	git branch --set-upstream from-master master &&
+	git config branch.from-master.merge > actual &&
+	grep -q "^refs/heads/master$" actual
+'
+
+test_expect_success '--set-upstream does not change branch' '
+	git branch from-master2 master &&
+	test_must_fail git config branch.from-master2.merge > actual &&
+	git rev-list from-master2 &&
+	git update-ref refs/heads/from-master2 from-master2^ &&
+	git rev-parse from-master2 >expect2 &&
+	git branch --set-upstream from-master2 master &&
+	git config branch.from-master.merge > actual &&
+	git rev-parse from-master2 >actual2 &&
+	grep -q "^refs/heads/master$" actual &&
+	cmp expect2 actual2
+'
+
+test_expect_success '--set-upstream @{-1}' '
+	git checkout from-master &&
+	git checkout from-master2 &&
+	git config branch.from-master2.merge > expect2 &&
+	git branch --set-upstream @{-1} follower &&
+	git config branch.from-master.merge > actual &&
+	git config branch.from-master2.merge > actual2 &&
+	git branch --set-upstream from-master follower &&
+	git config branch.from-master.merge > expect &&
+	test_cmp expect2 actual2 &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t6042-merge-rename-corner-cases.sh b/t/t6042-merge-rename-corner-cases.sh
new file mode 100755
index 0000000..466fa38
--- /dev/null
+++ b/t/t6042-merge-rename-corner-cases.sh
@@ -0,0 +1,578 @@
+#!/bin/sh
+
+test_description="recursive merge corner cases w/ renames but not criss-crosses"
+# t6036 has corner cases that involve both criss-cross merges and renames
+
+. ./test-lib.sh
+
+test_expect_success 'setup rename/delete + untracked file' '
+	echo "A pretty inscription" >ring &&
+	git add ring &&
+	test_tick &&
+	git commit -m beginning &&
+
+	git branch people &&
+	git checkout -b rename-the-ring &&
+	git mv ring one-ring-to-rule-them-all &&
+	test_tick &&
+	git commit -m fullname &&
+
+	git checkout people &&
+	git rm ring &&
+	echo gollum >owner &&
+	git add owner &&
+	test_tick &&
+	git commit -m track-people-instead-of-objects &&
+	echo "Myyy PRECIOUSSS" >ring
+'
+
+test_expect_success "Does git preserve Gollum's precious artifact?" '
+	test_must_fail git merge -s recursive rename-the-ring &&
+
+	# Make sure git did not delete an untracked file
+	test -f ring
+'
+
+# Testcase setup for rename/modify/add-source:
+#   Commit A: new file: a
+#   Commit B: modify a slightly
+#   Commit C: rename a->b, add completely different a
+#
+# We should be able to merge B & C cleanly
+
+test_expect_success 'setup rename/modify/add-source conflict' '
+	git rm -rf . &&
+	git clean -fdqx &&
+	rm -rf .git &&
+	git init &&
+
+	printf "1\n2\n3\n4\n5\n6\n7\n" >a &&
+	git add a &&
+	git commit -m A &&
+	git tag A &&
+
+	git checkout -b B A &&
+	echo 8 >>a &&
+	git add a &&
+	git commit -m B &&
+
+	git checkout -b C A &&
+	git mv a b &&
+	echo something completely different >a &&
+	git add a &&
+	git commit -m C
+'
+
+test_expect_failure 'rename/modify/add-source conflict resolvable' '
+	git checkout B^0 &&
+
+	git merge -s recursive C^0 &&
+
+	test $(git rev-parse B:a) = $(git rev-parse b) &&
+	test $(git rev-parse C:a) = $(git rev-parse a)
+'
+
+test_expect_success 'setup resolvable conflict missed if rename missed' '
+	git rm -rf . &&
+	git clean -fdqx &&
+	rm -rf .git &&
+	git init &&
+
+	printf "1\n2\n3\n4\n5\n" >a &&
+	echo foo >b &&
+	git add a b &&
+	git commit -m A &&
+	git tag A &&
+
+	git checkout -b B A &&
+	git mv a c &&
+	echo "Completely different content" >a &&
+	git add a &&
+	git commit -m B &&
+
+	git checkout -b C A &&
+	echo 6 >>a &&
+	git add a &&
+	git commit -m C
+'
+
+test_expect_failure 'conflict caused if rename not detected' '
+	git checkout -q C^0 &&
+	git merge -s recursive B^0 &&
+
+	test 3 -eq $(git ls-files -s | wc -l) &&
+	test 0 -eq $(git ls-files -u | wc -l) &&
+	test 0 -eq $(git ls-files -o | wc -l) &&
+
+	test_line_count = 6 c &&
+	test $(git rev-parse HEAD:a) = $(git rev-parse B:a) &&
+	test $(git rev-parse HEAD:b) = $(git rev-parse A:b)
+'
+
+test_expect_success 'setup conflict resolved wrong if rename missed' '
+	git reset --hard &&
+	git clean -f &&
+
+	git checkout -b D A &&
+	echo 7 >>a &&
+	git add a &&
+	git mv a c &&
+	echo "Completely different content" >a &&
+	git add a &&
+	git commit -m D &&
+
+	git checkout -b E A &&
+	git rm a &&
+	echo "Completely different content" >>a &&
+	git add a &&
+	git commit -m E
+'
+
+test_expect_failure 'missed conflict if rename not detected' '
+	git checkout -q E^0 &&
+	test_must_fail git merge -s recursive D^0
+'
+
+# Tests for undetected rename/add-source causing a file to erroneously be
+# deleted (and for mishandled rename/rename(1to1) causing the same issue).
+#
+# This test uses a rename/rename(1to1)+add-source conflict (1to1 means the
+# same file is renamed on both sides to the same thing; it should trigger
+# the 1to2 logic, which it would do if the add-source didn't cause issues
+# for git's rename detection):
+#   Commit A: new file: a
+#   Commit B: rename a->b
+#   Commit C: rename a->b, add unrelated a
+
+test_expect_success 'setup undetected rename/add-source causes data loss' '
+	git rm -rf . &&
+	git clean -fdqx &&
+	rm -rf .git &&
+	git init &&
+
+	printf "1\n2\n3\n4\n5\n" >a &&
+	git add a &&
+	git commit -m A &&
+	git tag A &&
+
+	git checkout -b B A &&
+	git mv a b &&
+	git commit -m B &&
+
+	git checkout -b C A &&
+	git mv a b &&
+	echo foobar >a &&
+	git add a &&
+	git commit -m C
+'
+
+test_expect_failure 'detect rename/add-source and preserve all data' '
+	git checkout B^0 &&
+
+	git merge -s recursive C^0 &&
+
+	test 2 -eq $(git ls-files -s | wc -l) &&
+	test 2 -eq $(git ls-files -u | wc -l) &&
+	test 0 -eq $(git ls-files -o | wc -l) &&
+
+	test -f a &&
+	test -f b &&
+
+	test $(git rev-parse HEAD:b) = $(git rev-parse A:a) &&
+	test $(git rev-parse HEAD:a) = $(git rev-parse C:a)
+'
+
+test_expect_failure 'detect rename/add-source and preserve all data, merge other way' '
+	git checkout C^0 &&
+
+	git merge -s recursive B^0 &&
+
+	test 2 -eq $(git ls-files -s | wc -l) &&
+	test 2 -eq $(git ls-files -u | wc -l) &&
+	test 0 -eq $(git ls-files -o | wc -l) &&
+
+	test -f a &&
+	test -f b &&
+
+	test $(git rev-parse HEAD:b) = $(git rev-parse A:a) &&
+	test $(git rev-parse HEAD:a) = $(git rev-parse C:a)
+'
+
+test_expect_success 'setup content merge + rename/directory conflict' '
+	git rm -rf . &&
+	git clean -fdqx &&
+	rm -rf .git &&
+	git init &&
+
+	printf "1\n2\n3\n4\n5\n6\n" >file &&
+	git add file &&
+	test_tick &&
+	git commit -m base &&
+	git tag base &&
+
+	git checkout -b right &&
+	echo 7 >>file &&
+	mkdir newfile &&
+	echo junk >newfile/realfile &&
+	git add file newfile/realfile &&
+	test_tick &&
+	git commit -m right &&
+
+	git checkout -b left-conflict base &&
+	echo 8 >>file &&
+	git add file &&
+	git mv file newfile &&
+	test_tick &&
+	git commit -m left &&
+
+	git checkout -b left-clean base &&
+	echo 0 >newfile &&
+	cat file >>newfile &&
+	git add newfile &&
+	git rm file &&
+	test_tick &&
+	git commit -m left
+'
+
+test_expect_success 'rename/directory conflict + clean content merge' '
+	git reset --hard &&
+	git reset --hard &&
+	git clean -fdqx &&
+
+	git checkout left-clean^0 &&
+
+	test_must_fail git merge -s recursive right^0 &&
+
+	test 2 -eq $(git ls-files -s | wc -l) &&
+	test 1 -eq $(git ls-files -u | wc -l) &&
+	test 1 -eq $(git ls-files -o | wc -l) &&
+
+	echo 0 >expect &&
+	git cat-file -p base:file >>expect &&
+	echo 7 >>expect &&
+	test_cmp expect newfile~HEAD &&
+
+	test $(git rev-parse :2:newfile) = $(git hash-object expect) &&
+
+	test -f newfile/realfile &&
+	test -f newfile~HEAD
+'
+
+test_expect_success 'rename/directory conflict + content merge conflict' '
+	git reset --hard &&
+	git reset --hard &&
+	git clean -fdqx &&
+
+	git checkout left-conflict^0 &&
+
+	test_must_fail git merge -s recursive right^0 &&
+
+	test 4 -eq $(git ls-files -s | wc -l) &&
+	test 3 -eq $(git ls-files -u | wc -l) &&
+	test 1 -eq $(git ls-files -o | wc -l) &&
+
+	git cat-file -p left-conflict:newfile >left &&
+	git cat-file -p base:file    >base &&
+	git cat-file -p right:file   >right &&
+	test_must_fail git merge-file \
+		-L "HEAD:newfile" \
+		-L "" \
+		-L "right^0:file" \
+		left base right &&
+	test_cmp left newfile~HEAD &&
+
+	test $(git rev-parse :1:newfile) = $(git rev-parse base:file) &&
+	test $(git rev-parse :2:newfile) = $(git rev-parse left-conflict:newfile) &&
+	test $(git rev-parse :3:newfile) = $(git rev-parse right:file) &&
+
+	test -f newfile/realfile &&
+	test -f newfile~HEAD
+'
+
+test_expect_success 'setup content merge + rename/directory conflict w/ disappearing dir' '
+	git reset --hard &&
+	git rm -rf . &&
+	git clean -fdqx &&
+	rm -rf .git &&
+	git init &&
+
+	mkdir sub &&
+	printf "1\n2\n3\n4\n5\n6\n" >sub/file &&
+	git add sub/file &&
+	test_tick &&
+	git commit -m base &&
+	git tag base &&
+
+	git checkout -b right &&
+	echo 7 >>sub/file &&
+	git add sub/file &&
+	test_tick &&
+	git commit -m right &&
+
+	git checkout -b left base &&
+	echo 0 >newfile &&
+	cat sub/file >>newfile &&
+	git rm sub/file &&
+	mv newfile sub &&
+	git add sub &&
+	test_tick &&
+	git commit -m left
+'
+
+test_expect_success 'disappearing dir in rename/directory conflict handled' '
+	git reset --hard &&
+	git clean -fdqx &&
+
+	git checkout left^0 &&
+
+	git merge -s recursive right^0 &&
+
+	test 1 -eq $(git ls-files -s | wc -l) &&
+	test 0 -eq $(git ls-files -u | wc -l) &&
+	test 0 -eq $(git ls-files -o | wc -l) &&
+
+	echo 0 >expect &&
+	git cat-file -p base:sub/file >>expect &&
+	echo 7 >>expect &&
+	test_cmp expect sub &&
+
+	test -f sub
+'
+
+# Test for all kinds of things that can go wrong with rename/rename (2to1):
+#   Commit A: new files: a & b
+#   Commit B: rename a->c, modify b
+#   Commit C: rename b->c, modify a
+#
+# Merging of B & C should NOT be clean.  Questions:
+#   * Both a & b should be removed by the merge; are they?
+#   * The two c's should contain modifications to a & b; do they?
+#   * The index should contain two files, both for c; does it?
+#   * The working copy should have two files, both of form c~<unique>; does it?
+#   * Nothing else should be present.  Is anything?
+
+test_expect_success 'setup rename/rename (2to1) + modify/modify' '
+	git rm -rf . &&
+	git clean -fdqx &&
+	rm -rf .git &&
+	git init &&
+
+	printf "1\n2\n3\n4\n5\n" >a &&
+	printf "5\n4\n3\n2\n1\n" >b &&
+	git add a b &&
+	git commit -m A &&
+	git tag A &&
+
+	git checkout -b B A &&
+	git mv a c &&
+	echo 0 >>b &&
+	git add b &&
+	git commit -m B &&
+
+	git checkout -b C A &&
+	git mv b c &&
+	echo 6 >>a &&
+	git add a &&
+	git commit -m C
+'
+
+test_expect_success 'handle rename/rename (2to1) conflict correctly' '
+	git checkout B^0 &&
+
+	test_must_fail git merge -s recursive C^0 >out &&
+	grep "CONFLICT (rename/rename)" out &&
+
+	test 2 -eq $(git ls-files -s | wc -l) &&
+	test 2 -eq $(git ls-files -u | wc -l) &&
+	test 2 -eq $(git ls-files -u c | wc -l) &&
+	test 3 -eq $(git ls-files -o | wc -l) &&
+
+	test ! -f a &&
+	test ! -f b &&
+	test -f c~HEAD &&
+	test -f c~C^0 &&
+
+	test $(git hash-object c~HEAD) = $(git rev-parse C:a) &&
+	test $(git hash-object c~C^0) = $(git rev-parse B:b)
+'
+
+# Testcase setup for simple rename/rename (1to2) conflict:
+#   Commit A: new file: a
+#   Commit B: rename a->b
+#   Commit C: rename a->c
+test_expect_success 'setup simple rename/rename (1to2) conflict' '
+	git rm -rf . &&
+	git clean -fdqx &&
+	rm -rf .git &&
+	git init &&
+
+	echo stuff >a &&
+	git add a &&
+	test_tick &&
+	git commit -m A &&
+	git tag A &&
+
+	git checkout -b B A &&
+	git mv a b &&
+	test_tick &&
+	git commit -m B &&
+
+	git checkout -b C A &&
+	git mv a c &&
+	test_tick &&
+	git commit -m C
+'
+
+test_expect_success 'merge has correct working tree contents' '
+	git checkout C^0 &&
+
+	test_must_fail git merge -s recursive B^0 &&
+
+	test 3 -eq $(git ls-files -s | wc -l) &&
+	test 3 -eq $(git ls-files -u | wc -l) &&
+	test 0 -eq $(git ls-files -o | wc -l) &&
+
+	test $(git rev-parse :1:a) = $(git rev-parse A:a) &&
+	test $(git rev-parse :3:b) = $(git rev-parse A:a) &&
+	test $(git rev-parse :2:c) = $(git rev-parse A:a) &&
+
+	test ! -f a &&
+	test $(git hash-object b) = $(git rev-parse A:a) &&
+	test $(git hash-object c) = $(git rev-parse A:a)
+'
+
+# Testcase setup for rename/rename(1to2)/add-source conflict:
+#   Commit A: new file: a
+#   Commit B: rename a->b
+#   Commit C: rename a->c, add completely different a
+#
+# Merging of B & C should NOT be clean; there's a rename/rename conflict
+
+test_expect_success 'setup rename/rename(1to2)/add-source conflict' '
+	git rm -rf . &&
+	git clean -fdqx &&
+	rm -rf .git &&
+	git init &&
+
+	printf "1\n2\n3\n4\n5\n6\n7\n" >a &&
+	git add a &&
+	git commit -m A &&
+	git tag A &&
+
+	git checkout -b B A &&
+	git mv a b &&
+	git commit -m B &&
+
+	git checkout -b C A &&
+	git mv a c &&
+	echo something completely different >a &&
+	git add a &&
+	git commit -m C
+'
+
+test_expect_failure 'detect conflict with rename/rename(1to2)/add-source merge' '
+	git checkout B^0 &&
+
+	test_must_fail git merge -s recursive C^0 &&
+
+	test 4 -eq $(git ls-files -s | wc -l) &&
+	test 0 -eq $(git ls-files -o | wc -l) &&
+
+	test $(git rev-parse 3:a) = $(git rev-parse C:a) &&
+	test $(git rev-parse 1:a) = $(git rev-parse A:a) &&
+	test $(git rev-parse 2:b) = $(git rev-parse B:b) &&
+	test $(git rev-parse 3:c) = $(git rev-parse C:c) &&
+
+	test -f a &&
+	test -f b &&
+	test -f c
+'
+
+test_expect_success 'setup rename/rename(1to2)/add-source resolvable conflict' '
+	git rm -rf . &&
+	git clean -fdqx &&
+	rm -rf .git &&
+	git init &&
+
+	>a &&
+	git add a &&
+	test_tick &&
+	git commit -m base &&
+	git tag A &&
+
+	git checkout -b B A &&
+	git mv a b &&
+	test_tick &&
+	git commit -m one &&
+
+	git checkout -b C A &&
+	git mv a b &&
+	echo important-info >a &&
+	git add a &&
+	test_tick &&
+	git commit -m two
+'
+
+test_expect_failure 'rename/rename/add-source still tracks new a file' '
+	git checkout C^0 &&
+	git merge -s recursive B^0 &&
+
+	test 2 -eq $(git ls-files -s | wc -l) &&
+	test 0 -eq $(git ls-files -o | wc -l) &&
+
+	test $(git rev-parse HEAD:a) = $(git rev-parse C:a) &&
+	test $(git rev-parse HEAD:b) = $(git rev-parse A:a)
+'
+
+test_expect_success 'setup rename/rename(1to2)/add-dest conflict' '
+	git rm -rf . &&
+	git clean -fdqx &&
+	rm -rf .git &&
+	git init &&
+
+	echo stuff >a &&
+	git add a &&
+	test_tick &&
+	git commit -m base &&
+	git tag A &&
+
+	git checkout -b B A &&
+	git mv a b &&
+	echo precious-data >c &&
+	git add c &&
+	test_tick &&
+	git commit -m one &&
+
+	git checkout -b C A &&
+	git mv a c &&
+	echo important-info >b &&
+	git add b &&
+	test_tick &&
+	git commit -m two
+'
+
+test_expect_success 'rename/rename/add-dest merge still knows about conflicting file versions' '
+	git checkout C^0 &&
+	test_must_fail git merge -s recursive B^0 &&
+
+	test 5 -eq $(git ls-files -s | wc -l) &&
+	test 2 -eq $(git ls-files -u b | wc -l) &&
+	test 2 -eq $(git ls-files -u c | wc -l) &&
+	test 4 -eq $(git ls-files -o | wc -l) &&
+
+	test $(git rev-parse :1:a) = $(git rev-parse A:a) &&
+	test $(git rev-parse :2:b) = $(git rev-parse C:b) &&
+	test $(git rev-parse :3:b) = $(git rev-parse B:b) &&
+	test $(git rev-parse :2:c) = $(git rev-parse C:c) &&
+	test $(git rev-parse :3:c) = $(git rev-parse B:c) &&
+
+	test $(git hash-object c~HEAD) = $(git rev-parse C:c) &&
+	test $(git hash-object c~B\^0) = $(git rev-parse B:c) &&
+	test $(git hash-object b~HEAD) = $(git rev-parse C:b) &&
+	test $(git hash-object b~B\^0) = $(git rev-parse B:b) &&
+
+	test ! -f b &&
+	test ! -f c
+'
+
+test_done
diff --git a/t/t6050-replace.sh b/t/t6050-replace.sh
new file mode 100755
index 0000000..5c87f28
--- /dev/null
+++ b/t/t6050-replace.sh
@@ -0,0 +1,255 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Christian Couder
+#
+test_description='Tests replace refs functionality'
+
+exec </dev/null
+
+. ./test-lib.sh
+
+add_and_commit_file()
+{
+    _file="$1"
+    _msg="$2"
+
+    git add $_file || return $?
+    test_tick || return $?
+    git commit --quiet -m "$_file: $_msg"
+}
+
+HASH1=
+HASH2=
+HASH3=
+HASH4=
+HASH5=
+HASH6=
+HASH7=
+
+test_expect_success 'set up buggy branch' '
+     echo "line 1" >> hello &&
+     echo "line 2" >> hello &&
+     echo "line 3" >> hello &&
+     echo "line 4" >> hello &&
+     add_and_commit_file hello "4 lines" &&
+     HASH1=$(git rev-parse --verify HEAD) &&
+     echo "line BUG" >> hello &&
+     echo "line 6" >> hello &&
+     echo "line 7" >> hello &&
+     echo "line 8" >> hello &&
+     add_and_commit_file hello "4 more lines with a BUG" &&
+     HASH2=$(git rev-parse --verify HEAD) &&
+     echo "line 9" >> hello &&
+     echo "line 10" >> hello &&
+     add_and_commit_file hello "2 more lines" &&
+     HASH3=$(git rev-parse --verify HEAD) &&
+     echo "line 11" >> hello &&
+     add_and_commit_file hello "1 more line" &&
+     HASH4=$(git rev-parse --verify HEAD) &&
+     sed -e "s/BUG/5/" hello > hello.new &&
+     mv hello.new hello &&
+     add_and_commit_file hello "BUG fixed" &&
+     HASH5=$(git rev-parse --verify HEAD) &&
+     echo "line 12" >> hello &&
+     echo "line 13" >> hello &&
+     add_and_commit_file hello "2 more lines" &&
+     HASH6=$(git rev-parse --verify HEAD) &&
+     echo "line 14" >> hello &&
+     echo "line 15" >> hello &&
+     echo "line 16" >> hello &&
+     add_and_commit_file hello "again 3 more lines" &&
+     HASH7=$(git rev-parse --verify HEAD)
+'
+
+test_expect_success 'replace the author' '
+     git cat-file commit $HASH2 | grep "author A U Thor" &&
+     R=$(git cat-file commit $HASH2 | sed -e "s/A U/O/" | git hash-object -t commit --stdin -w) &&
+     git cat-file commit $R | grep "author O Thor" &&
+     git update-ref refs/replace/$HASH2 $R &&
+     git show HEAD~5 | grep "O Thor" &&
+     git show $HASH2 | grep "O Thor"
+'
+
+test_expect_success 'test --no-replace-objects option' '
+     git cat-file commit $HASH2 | grep "author O Thor" &&
+     git --no-replace-objects cat-file commit $HASH2 | grep "author A U Thor" &&
+     git show $HASH2 | grep "O Thor" &&
+     git --no-replace-objects show $HASH2 | grep "A U Thor"
+'
+
+test_expect_success 'test GIT_NO_REPLACE_OBJECTS env variable' '
+     GIT_NO_REPLACE_OBJECTS=1 git cat-file commit $HASH2 | grep "author A U Thor" &&
+     GIT_NO_REPLACE_OBJECTS=1 git show $HASH2 | grep "A U Thor"
+'
+
+cat >tag.sig <<EOF
+object $HASH2
+type commit
+tag mytag
+tagger T A Gger <> 0 +0000
+
+EOF
+
+test_expect_success 'tag replaced commit' '
+     git mktag <tag.sig >.git/refs/tags/mytag 2>message
+'
+
+test_expect_success '"git fsck" works' '
+     git fsck master > fsck_master.out &&
+     grep "dangling commit $R" fsck_master.out &&
+     grep "dangling tag $(cat .git/refs/tags/mytag)" fsck_master.out &&
+     test -z "$(git fsck)"
+'
+
+test_expect_success 'repack, clone and fetch work' '
+     git repack -a -d &&
+     git clone --no-hardlinks . clone_dir &&
+     (
+	  cd clone_dir &&
+	  git show HEAD~5 | grep "A U Thor" &&
+	  git show $HASH2 | grep "A U Thor" &&
+	  git cat-file commit $R &&
+	  git repack -a -d &&
+	  test_must_fail git cat-file commit $R &&
+	  git fetch ../ "refs/replace/*:refs/replace/*" &&
+	  git show HEAD~5 | grep "O Thor" &&
+	  git show $HASH2 | grep "O Thor" &&
+	  git cat-file commit $R
+     )
+'
+
+test_expect_success '"git replace" listing and deleting' '
+     test "$HASH2" = "$(git replace -l)" &&
+     test "$HASH2" = "$(git replace)" &&
+     aa=${HASH2%??????????????????????????????????????} &&
+     test "$HASH2" = "$(git replace -l "$aa*")" &&
+     test_must_fail git replace -d $R &&
+     test_must_fail git replace -d &&
+     test_must_fail git replace -l -d $HASH2 &&
+     git replace -d $HASH2 &&
+     git show $HASH2 | grep "A U Thor" &&
+     test -z "$(git replace -l)"
+'
+
+test_expect_success '"git replace" replacing' '
+     git replace $HASH2 $R &&
+     git show $HASH2 | grep "O Thor" &&
+     test_must_fail git replace $HASH2 $R &&
+     git replace -f $HASH2 $R &&
+     test_must_fail git replace -f &&
+     test "$HASH2" = "$(git replace)"
+'
+
+# This creates a side branch where the bug in H2
+# does not appear because P2 is created by applying
+# H2 and squashing H5 into it.
+# P3, P4 and P6 are created by cherry-picking H3, H4
+# and H6 respectively.
+#
+# At this point, we should have the following:
+#
+#    P2--P3--P4--P6
+#   /
+# H1-H2-H3-H4-H5-H6-H7
+#
+# Then we replace H6 with P6.
+#
+test_expect_success 'create parallel branch without the bug' '
+     git replace -d $HASH2 &&
+     git show $HASH2 | grep "A U Thor" &&
+     git checkout $HASH1 &&
+     git cherry-pick $HASH2 &&
+     git show $HASH5 | git apply &&
+     git commit --amend -m "hello: 4 more lines WITHOUT the bug" hello &&
+     PARA2=$(git rev-parse --verify HEAD) &&
+     git cherry-pick $HASH3 &&
+     PARA3=$(git rev-parse --verify HEAD) &&
+     git cherry-pick $HASH4 &&
+     PARA4=$(git rev-parse --verify HEAD) &&
+     git cherry-pick $HASH6 &&
+     PARA6=$(git rev-parse --verify HEAD) &&
+     git replace $HASH6 $PARA6 &&
+     git checkout master &&
+     cur=$(git rev-parse --verify HEAD) &&
+     test "$cur" = "$HASH7" &&
+     git log --pretty=oneline | grep $PARA2 &&
+     git remote add cloned ./clone_dir
+'
+
+test_expect_success 'push to cloned repo' '
+     git push cloned $HASH6^:refs/heads/parallel &&
+     (
+	  cd clone_dir &&
+	  git checkout parallel &&
+	  git log --pretty=oneline | grep $PARA2
+     )
+'
+
+test_expect_success 'push branch with replacement' '
+     git cat-file commit $PARA3 | grep "author A U Thor" &&
+     S=$(git cat-file commit $PARA3 | sed -e "s/A U/O/" | git hash-object -t commit --stdin -w) &&
+     git cat-file commit $S | grep "author O Thor" &&
+     git replace $PARA3 $S &&
+     git show $HASH6~2 | grep "O Thor" &&
+     git show $PARA3 | grep "O Thor" &&
+     git push cloned $HASH6^:refs/heads/parallel2 &&
+     (
+	  cd clone_dir &&
+	  git checkout parallel2 &&
+	  git log --pretty=oneline | grep $PARA3 &&
+	  git show $PARA3 | grep "A U Thor"
+     )
+'
+
+test_expect_success 'fetch branch with replacement' '
+     git branch tofetch $HASH6 &&
+     (
+	  cd clone_dir &&
+	  git fetch origin refs/heads/tofetch:refs/heads/parallel3 &&
+	  git log --pretty=oneline parallel3 > output.txt &&
+	  ! grep $PARA3 output.txt &&
+	  git show $PARA3 > para3.txt &&
+	  grep "A U Thor" para3.txt &&
+	  git fetch origin "refs/replace/*:refs/replace/*" &&
+	  git log --pretty=oneline parallel3 > output.txt &&
+	  grep $PARA3 output.txt &&
+	  git show $PARA3 > para3.txt &&
+	  grep "O Thor" para3.txt
+     )
+'
+
+test_expect_success 'bisect and replacements' '
+     git bisect start $HASH7 $HASH1 &&
+     test "$PARA3" = "$(git rev-parse --verify HEAD)" &&
+     git bisect reset &&
+     GIT_NO_REPLACE_OBJECTS=1 git bisect start $HASH7 $HASH1 &&
+     test "$HASH4" = "$(git rev-parse --verify HEAD)" &&
+     git bisect reset &&
+     git --no-replace-objects bisect start $HASH7 $HASH1 &&
+     test "$HASH4" = "$(git rev-parse --verify HEAD)" &&
+     git bisect reset
+'
+
+test_expect_success 'index-pack and replacements' '
+	git --no-replace-objects rev-list --objects HEAD |
+	git --no-replace-objects pack-objects test- &&
+	git index-pack test-*.pack
+'
+
+test_expect_success 'not just commits' '
+	echo replaced >file &&
+	git add file &&
+	REPLACED=$(git rev-parse :file) &&
+	mv file file.replaced &&
+
+	echo original >file &&
+	git add file &&
+	ORIGINAL=$(git rev-parse :file) &&
+	git update-ref refs/replace/$ORIGINAL $REPLACED &&
+	mv file file.original &&
+
+	git checkout file &&
+	test_cmp file.replaced file
+'
+
+test_done
diff --git a/t/t6060-merge-index.sh b/t/t6060-merge-index.sh
new file mode 100755
index 0000000..debadbd
--- /dev/null
+++ b/t/t6060-merge-index.sh
@@ -0,0 +1,100 @@
+#!/bin/sh
+
+test_description='basic git merge-index / git-merge-one-file tests'
+. ./test-lib.sh
+
+test_expect_success 'setup diverging branches' '
+	for i in 1 2 3 4 5 6 7 8 9 10; do
+		echo $i
+	done >file &&
+	git add file &&
+	git commit -m base &&
+	git tag base &&
+	sed s/2/two/ <file >tmp &&
+	mv tmp file &&
+	git commit -a -m two &&
+	git tag two &&
+	git checkout -b other HEAD^ &&
+	sed s/10/ten/ <file >tmp &&
+	mv tmp file &&
+	git commit -a -m ten &&
+	git tag ten
+'
+
+cat >expect-merged <<'EOF'
+1
+two
+3
+4
+5
+6
+7
+8
+9
+ten
+EOF
+
+test_expect_success 'read-tree does not resolve content merge' '
+	git read-tree -i -m base ten two &&
+	echo file >expect &&
+	git diff-files --name-only --diff-filter=U >unmerged &&
+	test_cmp expect unmerged
+'
+
+test_expect_success 'git merge-index git-merge-one-file resolves' '
+	git merge-index git-merge-one-file -a &&
+	git diff-files --name-only --diff-filter=U >unmerged &&
+	>expect &&
+	test_cmp expect unmerged &&
+	test_cmp expect-merged file &&
+	git cat-file blob :file >file-index &&
+	test_cmp expect-merged file-index
+'
+
+test_expect_success 'setup bare merge' '
+	git clone --bare . bare.git &&
+	(cd bare.git &&
+	 GIT_INDEX_FILE=$PWD/merge.index &&
+	 export GIT_INDEX_FILE &&
+	 git read-tree -i -m base ten two
+	)
+'
+
+test_expect_success 'merge-one-file fails without a work tree' '
+	(cd bare.git &&
+	 GIT_INDEX_FILE=$PWD/merge.index &&
+	 export GIT_INDEX_FILE &&
+	 test_must_fail git merge-index git-merge-one-file -a
+	)
+'
+
+test_expect_success 'merge-one-file respects GIT_WORK_TREE' '
+	(cd bare.git &&
+	 mkdir work &&
+	 GIT_WORK_TREE=$PWD/work &&
+	 export GIT_WORK_TREE &&
+	 GIT_INDEX_FILE=$PWD/merge.index &&
+	 export GIT_INDEX_FILE &&
+	 git merge-index git-merge-one-file -a &&
+	 git cat-file blob :file >work/file-index
+	) &&
+	test_cmp expect-merged bare.git/work/file &&
+	test_cmp expect-merged bare.git/work/file-index
+'
+
+test_expect_success 'merge-one-file respects core.worktree' '
+	mkdir subdir &&
+	git clone . subdir/child &&
+	(cd subdir &&
+	 GIT_DIR=$PWD/child/.git &&
+	 export GIT_DIR &&
+	 git config core.worktree "$PWD/child" &&
+	 git read-tree -i -m base ten two &&
+	 git merge-index git-merge-one-file -a &&
+	 git cat-file blob :file >file-index
+	) &&
+	test_cmp expect-merged subdir/child/file &&
+	test_cmp expect-merged subdir/file-index
+'
+
+test_done
diff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh
index f105fab..e673c25 100755
--- a/t/t6101-rev-parse-parents.sh
+++ b/t/t6101-rev-parse-parents.sh
@@ -6,7 +6,7 @@
 test_description='Test git rev-parse with different parent options'
 
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/t6000lib.sh # t6xxx specific functions
+. "$TEST_DIRECTORY"/lib-t6000.sh # t6xxx specific functions
 
 date >path0
 git update-index --add path0
diff --git a/t/t6110-rev-list-sparse.sh b/t/t6110-rev-list-sparse.sh
new file mode 100755
index 0000000..656ac7f
--- /dev/null
+++ b/t/t6110-rev-list-sparse.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+test_description='operations that cull histories in unusual ways'
+. ./test-lib.sh
+
+test_expect_success setup '
+	test_commit A &&
+	test_commit B &&
+	test_commit C &&
+	git checkout -b side HEAD^ &&
+	test_commit D &&
+	test_commit E &&
+	git merge master
+'
+
+test_expect_success 'rev-list --first-parent --boundary' '
+	git rev-list --first-parent --boundary HEAD^..
+'
+
+test_done
diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh
index 8c7e081..f67aa6f 100755
--- a/t/t6120-describe.sh
+++ b/t/t6120-describe.sh
@@ -8,7 +8,7 @@
  o----o----o----o----o----.    /
        \        A    c        /
         .------------o---o---o
-                     D   e
+                   D,R   e
 '
 . ./test-lib.sh
 
@@ -34,6 +34,8 @@
 	echo one >file && git add file && git commit -m initial &&
 	one=$(git rev-parse HEAD) &&
 
+	git describe --always HEAD &&
+
 	test_tick &&
 	echo two >file && git add file && git commit -m second &&
 	two=$(git rev-parse HEAD) &&
@@ -66,6 +68,8 @@
 	echo D >another && git add another && git commit -m D &&
 	test_tick &&
 	git tag -a -m D D &&
+	test_tick &&
+	git tag -a -m R R &&
 
 	test_tick &&
 	echo DD >another && git commit -a -m another &&
@@ -87,15 +91,21 @@
 
 check_describe A-* HEAD
 check_describe A-* HEAD^
-check_describe D-* HEAD^^
+check_describe R-* HEAD^^
 check_describe A-* HEAD^^2
 check_describe B HEAD^^2^
+check_describe R-* HEAD^^^
 
 check_describe c-* --tags HEAD
 check_describe c-* --tags HEAD^
 check_describe e-* --tags HEAD^^
 check_describe c-* --tags HEAD^^2
 check_describe B --tags HEAD^^2^
+check_describe e --tags HEAD^^^
+
+check_describe heads/master --all HEAD
+check_describe tags/c-* --all HEAD^
+check_describe tags/e --all HEAD^^^
 
 check_describe B-0-* --long HEAD^^2^
 check_describe A-3-* --long HEAD^^2
@@ -114,7 +124,7 @@
 EOF
 check_describe A-* HEAD
 test_expect_success 'warning was displayed for Q' '
-	test_cmp err.expect err.actual
+	test_i18ncmp err.expect err.actual
 '
 test_expect_success 'rename tag Q back to A' '
 	mv .git/refs/tags/Q .git/refs/tags/A
@@ -123,6 +133,20 @@
 test_expect_success 'pack tag refs' 'git pack-refs'
 check_describe A-* HEAD
 
+check_describe "A-*[0-9a-f]" --dirty
+
+test_expect_success 'set-up dirty work tree' '
+	echo >>file
+'
+
+check_describe "A-*[0-9a-f]-dirty" --dirty
+
+check_describe "A-*[0-9a-f].mod" --dirty=.mod
+
+test_expect_success 'describe --dirty HEAD' '
+	test_must_fail git describe --dirty HEAD
+'
+
 test_expect_success 'set-up matching pattern tests' '
 	git tag -a -m test-annotated test-annotated &&
 	echo >>file &&
diff --git a/t/t6200-fmt-merge-msg.sh b/t/t6200-fmt-merge-msg.sh
index 42f6fff..992c2a0 100755
--- a/t/t6200-fmt-merge-msg.sh
+++ b/t/t6200-fmt-merge-msg.sh
@@ -7,73 +7,79 @@
 
 . ./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 &&
+	test_tick &&
 	git commit -m "Initial" &&
 
+	git clone . remote &&
+
 	echo uno >one &&
 	echo dos >two &&
 	git add two &&
-	setdate &&
+	test_tick &&
 	git commit -a -m "Second" &&
 
 	git checkout -b left &&
 
-	echo $datestamp >one &&
-	setdate &&
+	echo "c1" >one &&
+	test_tick &&
 	git commit -a -m "Common #1" &&
 
-	echo $datestamp >one &&
-	setdate &&
+	echo "c2" >one &&
+	test_tick &&
 	git commit -a -m "Common #2" &&
 
 	git branch right &&
 
-	echo $datestamp >two &&
-	setdate &&
-	git commit -a -m "Left #3" &&
+	echo "l3" >two &&
+	test_tick &&
+	GIT_COMMITTER_NAME="Another Committer" \
+	GIT_AUTHOR_NAME="Another Author" git commit -a -m "Left #3" &&
 
-	echo $datestamp >two &&
-	setdate &&
-	git commit -a -m "Left #4" &&
+	echo "l4" >two &&
+	test_tick &&
+	GIT_COMMITTER_NAME="Another Committer" \
+	GIT_AUTHOR_NAME="Another Author" git commit -a -m "Left #4" &&
 
-	echo $datestamp >two &&
-	setdate &&
-	git commit -a -m "Left #5" &&
+	echo "l5" >two &&
+	test_tick &&
+	GIT_COMMITTER_NAME="Another Committer" \
+	GIT_AUTHOR_NAME="Another Author" git commit -a -m "Left #5" &&
+	git tag tag-l5 &&
 
 	git checkout right &&
 
-	echo $datestamp >three &&
+	echo "r3" >three &&
 	git add three &&
-	setdate &&
+	test_tick &&
 	git commit -a -m "Right #3" &&
+	git tag tag-r3 &&
 
-	echo $datestamp >three &&
-	setdate &&
+	echo "r4" >three &&
+	test_tick &&
 	git commit -a -m "Right #4" &&
 
-	echo $datestamp >three &&
-	setdate &&
+	echo "r5" >three &&
+	test_tick &&
 	git commit -a -m "Right #5" &&
 
-	git show-branch
+	git checkout -b long &&
+	i=0 &&
+	while test $i -lt 30
+	do
+		test_commit $i one &&
+		i=$(($i+1))
+	done &&
+
+	git show-branch &&
+
+	apos="'\''"
 '
 
-cat >expected <<\EOF
-Merge branch 'left'
-EOF
-
-test_expect_success 'merge-msg test #1' '
+test_expect_success 'message for merging local branch' '
+	echo "Merge branch ${apos}left${apos}" >expected &&
 
 	git checkout master &&
 	git fetch . left &&
@@ -82,11 +88,8 @@
 	test_cmp expected actual
 '
 
-cat >expected <<EOF
-Merge branch 'left' of $(pwd)
-EOF
-
-test_expect_success 'merge-msg test #2' '
+test_expect_success 'message for merging external branch' '
+	echo "Merge branch ${apos}left${apos} of $(pwd)" >expected &&
 
 	git checkout master &&
 	git fetch "$(pwd)" left &&
@@ -95,141 +98,247 @@
 	test_cmp expected actual
 '
 
-cat >expected <<\EOF
-Merge branch 'left'
+test_expect_success '[merge] summary/log configuration' '
+	cat >expected <<-EOF &&
+	Merge branch ${apos}left${apos}
 
-* left:
-  Left #5
-  Left #4
-  Left #3
-  Common #2
-  Common #1
-EOF
+	# By Another Author (3) and A U Thor (2)
+	# Via Another Committer
+	* left:
+	  Left #5
+	  Left #4
+	  Left #3
+	  Common #2
+	  Common #1
+	EOF
 
-test_expect_success 'merge-msg test #3-1' '
-
-	git config --unset-all merge.log
-	git config --unset-all merge.summary
 	git config merge.log true &&
+	test_might_fail git config --unset-all merge.summary &&
 
 	git checkout master &&
-	setdate &&
+	test_tick &&
 	git fetch . left &&
 
-	git fmt-merge-msg <.git/FETCH_HEAD >actual &&
-	test_cmp expected actual
-'
+	git fmt-merge-msg <.git/FETCH_HEAD >actual1 &&
 
-test_expect_success 'merge-msg test #3-2' '
-
-	git config --unset-all merge.log
-	git config --unset-all merge.summary
+	test_might_fail git config --unset-all merge.log &&
 	git config merge.summary true &&
 
 	git checkout master &&
-	setdate &&
+	test_tick &&
 	git fetch . left &&
 
-	git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+	git fmt-merge-msg <.git/FETCH_HEAD >actual2 &&
+
+	test_cmp expected actual1 &&
+	test_cmp expected actual2
+'
+
+test_expect_success 'setup: clear [merge] configuration' '
+	test_might_fail git config --unset-all merge.log &&
+	test_might_fail git config --unset-all merge.summary
+'
+
+test_expect_success 'setup FETCH_HEAD' '
+	git checkout master &&
+	test_tick &&
+	git fetch . left
+'
+
+test_expect_success 'merge.log=3 limits shortlog length' '
+	cat >expected <<-EOF &&
+	Merge branch ${apos}left${apos}
+
+	# By Another Author (3) and A U Thor (2)
+	# Via Another Committer
+	* left: (5 commits)
+	  Left #5
+	  Left #4
+	  Left #3
+	  ...
+	EOF
+
+	git -c merge.log=3 fmt-merge-msg <.git/FETCH_HEAD >actual &&
 	test_cmp expected actual
 '
 
-cat >expected <<\EOF
-Merge branches 'left' and 'right'
+test_expect_success 'merge.log=5 shows all 5 commits' '
+	cat >expected <<-EOF &&
+	Merge branch ${apos}left${apos}
 
-* left:
-  Left #5
-  Left #4
-  Left #3
-  Common #2
-  Common #1
+	# By Another Author (3) and A U Thor (2)
+	# Via Another Committer
+	* left:
+	  Left #5
+	  Left #4
+	  Left #3
+	  Common #2
+	  Common #1
+	EOF
 
-* right:
-  Right #5
-  Right #4
-  Right #3
-  Common #2
-  Common #1
-EOF
+	git -c merge.log=5 fmt-merge-msg <.git/FETCH_HEAD >actual &&
+	test_cmp expected actual
+'
 
-test_expect_success 'merge-msg test #4-1' '
+test_expect_success 'merge.log=0 disables shortlog' '
+	echo "Merge branch ${apos}left${apos}" >expected
+	git -c merge.log=0 fmt-merge-msg <.git/FETCH_HEAD >actual &&
+	test_cmp expected actual
+'
 
-	git config --unset-all merge.log
-	git config --unset-all merge.summary
+test_expect_success '--log=3 limits shortlog length' '
+	cat >expected <<-EOF &&
+	Merge branch ${apos}left${apos}
+
+	# By Another Author (3) and A U Thor (2)
+	# Via Another Committer
+	* left: (5 commits)
+	  Left #5
+	  Left #4
+	  Left #3
+	  ...
+	EOF
+
+	git fmt-merge-msg --log=3 <.git/FETCH_HEAD >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success '--log=5 shows all 5 commits' '
+	cat >expected <<-EOF &&
+	Merge branch ${apos}left${apos}
+
+	# By Another Author (3) and A U Thor (2)
+	# Via Another Committer
+	* left:
+	  Left #5
+	  Left #4
+	  Left #3
+	  Common #2
+	  Common #1
+	EOF
+
+	git fmt-merge-msg --log=5 <.git/FETCH_HEAD >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success '--no-log disables shortlog' '
+	echo "Merge branch ${apos}left${apos}" >expected &&
+	git fmt-merge-msg --no-log <.git/FETCH_HEAD >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success '--log=0 disables shortlog' '
+	echo "Merge branch ${apos}left${apos}" >expected &&
+	git fmt-merge-msg --no-log <.git/FETCH_HEAD >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'fmt-merge-msg -m' '
+	echo "Sync with left" >expected &&
+	cat >expected.log <<-EOF &&
+	Sync with left
+
+	# By Another Author (3) and A U Thor (2)
+	# Via Another Committer
+	* ${apos}left${apos} of $(pwd):
+	  Left #5
+	  Left #4
+	  Left #3
+	  Common #2
+	  Common #1
+	EOF
+
+	test_might_fail git config --unset merge.log &&
+	test_might_fail git config --unset merge.summary &&
+	git checkout master &&
+	git fetch "$(pwd)" left &&
+	git fmt-merge-msg -m "Sync with left" <.git/FETCH_HEAD >actual &&
+	git fmt-merge-msg --log -m "Sync with left" \
+					<.git/FETCH_HEAD >actual.log &&
 	git config merge.log true &&
+	git fmt-merge-msg -m "Sync with left" \
+					<.git/FETCH_HEAD >actual.log-config &&
+	git fmt-merge-msg --no-log -m "Sync with left" \
+					<.git/FETCH_HEAD >actual.nolog &&
 
-	git checkout master &&
-	setdate &&
-	git fetch . left right &&
-
-	git fmt-merge-msg <.git/FETCH_HEAD >actual &&
-	test_cmp expected actual
+	test_cmp expected actual &&
+	test_cmp expected.log actual.log &&
+	test_cmp expected.log actual.log-config &&
+	test_cmp expected actual.nolog
 '
 
-test_expect_success 'merge-msg test #4-2' '
+test_expect_success 'setup: expected shortlog for two branches' '
+	cat >expected <<-EOF
+	Merge branches ${apos}left${apos} and ${apos}right${apos}
 
-	git config --unset-all merge.log
-	git config --unset-all merge.summary
+	# By Another Author (3) and A U Thor (2)
+	# Via Another Committer
+	* 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 'shortlog for two branches' '
+	git config merge.log true &&
+	test_might_fail git config --unset-all merge.summary &&
+	git checkout master &&
+	test_tick &&
+	git fetch . left right &&
+	git fmt-merge-msg <.git/FETCH_HEAD >actual1 &&
+
+	test_might_fail git config --unset-all merge.log &&
 	git config merge.summary true &&
-
 	git checkout master &&
-	setdate &&
+	test_tick &&
 	git fetch . left right &&
+	git fmt-merge-msg <.git/FETCH_HEAD >actual2 &&
 
-	git fmt-merge-msg <.git/FETCH_HEAD >actual &&
-	test_cmp expected actual
-'
-
-test_expect_success 'merge-msg test #5-1' '
-
-	git config --unset-all merge.log
-	git config --unset-all merge.summary
 	git config merge.log yes &&
-
+	test_might_fail git config --unset-all merge.summary &&
 	git checkout master &&
-	setdate &&
+	test_tick &&
 	git fetch . left right &&
+	git fmt-merge-msg <.git/FETCH_HEAD >actual3 &&
 
-	git fmt-merge-msg <.git/FETCH_HEAD >actual &&
-	test_cmp expected actual
-'
-
-test_expect_success 'merge-msg test #5-2' '
-
-	git config --unset-all merge.log
-	git config --unset-all merge.summary
+	test_might_fail git config --unset-all merge.log &&
 	git config merge.summary yes &&
-
 	git checkout master &&
-	setdate &&
+	test_tick &&
 	git fetch . left right &&
+	git fmt-merge-msg <.git/FETCH_HEAD >actual4 &&
 
-	git fmt-merge-msg <.git/FETCH_HEAD >actual &&
-	test_cmp expected actual
+	test_cmp expected actual1 &&
+	test_cmp expected actual2 &&
+	test_cmp expected actual3 &&
+	test_cmp expected actual4
 '
 
 test_expect_success 'merge-msg -F' '
-
-	git config --unset-all merge.log
-	git config --unset-all merge.summary
+	test_might_fail git config --unset-all merge.log &&
 	git config merge.summary yes &&
-
 	git checkout master &&
-	setdate &&
+	test_tick &&
 	git fetch . left right &&
-
 	git fmt-merge-msg -F .git/FETCH_HEAD >actual &&
 	test_cmp expected actual
 '
 
 test_expect_success 'merge-msg -F in subdirectory' '
-
-	git config --unset-all merge.log
-	git config --unset-all merge.summary
+	test_might_fail git config --unset-all merge.log &&
 	git config merge.summary yes &&
-
 	git checkout master &&
-	setdate &&
+	test_tick &&
 	git fetch . left right &&
 	mkdir sub &&
 	cp .git/FETCH_HEAD sub/FETCH_HEAD &&
@@ -240,4 +349,127 @@
 	test_cmp expected actual
 '
 
+test_expect_success 'merge-msg with nothing to merge' '
+	test_might_fail git config --unset-all merge.log &&
+	git config merge.summary yes &&
+
+	>empty &&
+
+	(
+		cd remote &&
+		git checkout -b unrelated &&
+		test_tick &&
+		git fetch origin &&
+		git fmt-merge-msg <.git/FETCH_HEAD >../actual
+	) &&
+
+	test_cmp empty actual
+'
+
+test_expect_success 'merge-msg tag' '
+	cat >expected <<-EOF &&
+	Merge tag ${apos}tag-r3${apos}
+
+	* tag ${apos}tag-r3${apos}:
+	  Right #3
+	  Common #2
+	  Common #1
+	EOF
+
+	test_might_fail git config --unset-all merge.log &&
+	git config merge.summary yes &&
+
+	git checkout master &&
+	test_tick &&
+	git fetch . tag tag-r3 &&
+
+	git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'merge-msg two tags' '
+	cat >expected <<-EOF &&
+	Merge tags ${apos}tag-r3${apos} and ${apos}tag-l5${apos}
+
+	* tag ${apos}tag-r3${apos}:
+	  Right #3
+	  Common #2
+	  Common #1
+
+	# By Another Author (3) and A U Thor (2)
+	# Via Another Committer
+	* tag ${apos}tag-l5${apos}:
+	  Left #5
+	  Left #4
+	  Left #3
+	  Common #2
+	  Common #1
+	EOF
+
+	test_might_fail git config --unset-all merge.log &&
+	git config merge.summary yes &&
+
+	git checkout master &&
+	test_tick &&
+	git fetch . tag tag-r3 tag tag-l5 &&
+
+	git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'merge-msg tag and branch' '
+	cat >expected <<-EOF &&
+	Merge branch ${apos}left${apos}, tag ${apos}tag-r3${apos}
+
+	* tag ${apos}tag-r3${apos}:
+	  Right #3
+	  Common #2
+	  Common #1
+
+	# By Another Author (3) and A U Thor (2)
+	# Via Another Committer
+	* left:
+	  Left #5
+	  Left #4
+	  Left #3
+	  Common #2
+	  Common #1
+	EOF
+
+	test_might_fail git config --unset-all merge.log &&
+	git config merge.summary yes &&
+
+	git checkout master &&
+	test_tick &&
+	git fetch . tag tag-r3 left &&
+
+	git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'merge-msg lots of commits' '
+	{
+		cat <<-EOF &&
+		Merge branch ${apos}long${apos}
+
+		* long: (35 commits)
+		EOF
+
+		i=29 &&
+		while test $i -gt 9
+		do
+			echo "  $i" &&
+			i=$(($i-1))
+		done &&
+		echo "  ..."
+	} >expected &&
+
+	git checkout master &&
+	test_tick &&
+	git fetch . long &&
+
+	git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+	test_cmp expected actual
+'
+
 test_done
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index 8052c86..1721784 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -6,6 +6,7 @@
 test_description='for-each-ref test'
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-gpg.sh
 
 # Mon Jul 3 15:18:43 2006 +0000
 datestamp=1151939923
@@ -37,11 +38,13 @@
 	case "$1" in
 		head) ref=refs/heads/master ;;
 		 tag) ref=refs/tags/testtag ;;
+		   *) ref=$1 ;;
 	esac
 	printf '%s\n' "$3" >expected
-	test_expect_${4:-success} "basic atom: $1 $2" "
+	test_expect_${4:-success} $PREREQ "basic atom: $1 $2" "
 		git for-each-ref --format='%($2)' $ref >actual &&
-		test_cmp expected actual
+		sanitize_pgp <actual >actual.clean &&
+		test_cmp expected actual.clean
 	"
 }
 
@@ -71,7 +74,10 @@
 test_atom head creator 'C O Mitter <committer@example.com> 1151939923 +0200'
 test_atom head creatordate 'Mon Jul 3 17:18:43 2006 +0200'
 test_atom head subject 'Initial'
+test_atom head contents:subject 'Initial'
 test_atom head body ''
+test_atom head contents:body ''
+test_atom head contents:signature ''
 test_atom head contents 'Initial
 '
 
@@ -101,7 +107,10 @@
 test_atom tag creator 'C O Mitter <committer@example.com> 1151939925 +0200'
 test_atom tag creatordate 'Mon Jul 3 17:18:45 2006 +0200'
 test_atom tag subject 'Tagging at 1151939927'
+test_atom tag contents:subject 'Tagging at 1151939927'
 test_atom tag body ''
+test_atom tag contents:body ''
+test_atom tag contents:signature ''
 test_atom tag contents 'Tagging at 1151939927
 '
 
@@ -295,6 +304,15 @@
 	test_cmp expected actual
 '
 
+cat >expected <<EOF
+67a36f1
+EOF
+
+test_expect_success 'Check short objectname format' '
+	git for-each-ref --format="%(objectname:short)" refs/heads >actual &&
+	test_cmp expected actual
+'
+
 test_expect_success 'Check for invalid refname format' '
 	test_must_fail git for-each-ref --format="%(refname:INVALID)"
 '
@@ -350,4 +368,92 @@
 
 '
 
+test_expect_success 'create tag with subject and body content' '
+	cat >>msg <<-\EOF &&
+		the subject line
+
+		first body line
+		second body line
+	EOF
+	git tag -F msg subject-body
+'
+test_atom refs/tags/subject-body subject 'the subject line'
+test_atom refs/tags/subject-body body 'first body line
+second body line
+'
+test_atom refs/tags/subject-body contents 'the subject line
+
+first body line
+second body line
+'
+
+test_expect_success 'create tag with multiline subject' '
+	cat >msg <<-\EOF &&
+		first subject line
+		second subject line
+
+		first body line
+		second body line
+	EOF
+	git tag -F msg multiline
+'
+test_atom refs/tags/multiline subject 'first subject line second subject line'
+test_atom refs/tags/multiline contents:subject 'first subject line second subject line'
+test_atom refs/tags/multiline body 'first body line
+second body line
+'
+test_atom refs/tags/multiline contents:body 'first body line
+second body line
+'
+test_atom refs/tags/multiline contents:signature ''
+test_atom refs/tags/multiline contents 'first subject line
+second subject line
+
+first body line
+second body line
+'
+
+test_expect_success GPG 'create signed tags' '
+	git tag -s -m "" signed-empty &&
+	git tag -s -m "subject line" signed-short &&
+	cat >msg <<-\EOF &&
+	subject line
+
+	body contents
+	EOF
+	git tag -s -F msg signed-long
+'
+
+sig='-----BEGIN PGP SIGNATURE-----
+-----END PGP SIGNATURE-----
+'
+
+PREREQ=GPG
+test_atom refs/tags/signed-empty subject ''
+test_atom refs/tags/signed-empty contents:subject ''
+test_atom refs/tags/signed-empty body "$sig"
+test_atom refs/tags/signed-empty contents:body ''
+test_atom refs/tags/signed-empty contents:signature "$sig"
+test_atom refs/tags/signed-empty contents "$sig"
+
+test_atom refs/tags/signed-short subject 'subject line'
+test_atom refs/tags/signed-short contents:subject 'subject line'
+test_atom refs/tags/signed-short body "$sig"
+test_atom refs/tags/signed-short contents:body ''
+test_atom refs/tags/signed-short contents:signature "$sig"
+test_atom refs/tags/signed-short contents "subject line
+$sig"
+
+test_atom refs/tags/signed-long subject 'subject line'
+test_atom refs/tags/signed-long contents:subject 'subject line'
+test_atom refs/tags/signed-long body "body contents
+$sig"
+test_atom refs/tags/signed-long contents:body 'body contents
+'
+test_atom refs/tags/signed-long contents:signature "$sig"
+test_atom refs/tags/signed-long contents "subject line
+
+body contents
+$sig"
+
 test_done
diff --git a/t/t6500-gc.sh b/t/t6500-gc.sh
new file mode 100755
index 0000000..82f3639
--- /dev/null
+++ b/t/t6500-gc.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+test_description='basic git gc tests
+'
+
+. ./test-lib.sh
+
+test_expect_success 'gc empty repository' '
+	git gc
+'
+
+test_expect_success 'gc --gobbledegook' '
+	test_expect_code 129 git gc --nonsense 2>err &&
+	grep "[Uu]sage: git gc" err
+'
+
+test_expect_success 'gc -h with invalid configuration' '
+	mkdir broken &&
+	(
+		cd broken &&
+		git init &&
+		echo "[gc] pruneexpire = CORRUPT" >>.git/config &&
+		test_expect_code 129 git gc -h >usage 2>&1
+	) &&
+	grep "[Uu]sage" broken/usage
+'
+
+test_done
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index 10b8f8c..a845b15 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -61,7 +61,7 @@
 test_expect_success \
     'checking -f on untracked file with existing target' \
     'touch path0/untracked1 &&
-     git mv -f untracked1 path0
+     test_must_fail git mv -f untracked1 path0 &&
      test ! -f .git/index.lock &&
      test -f untracked1 &&
      test -f path0/untracked1'
@@ -189,13 +189,25 @@
 
 )'
 
+test_expect_success 'git mv to move multiple sources into a directory' '
+	rm -fr .git && git init &&
+	mkdir dir other &&
+	>dir/a.txt &&
+	>dir/b.txt &&
+	git add dir/?.txt &&
+	git mv dir/a.txt dir/b.txt other &&
+	git ls-files >actual &&
+	{ echo other/a.txt; echo other/b.txt; } >expect &&
+	test_cmp expect actual
+'
+
 test_expect_success 'git mv should not change sha1 of moved cache entry' '
 
 	rm -fr .git &&
 	git init &&
 	echo 1 >dirty &&
 	git add dirty &&
-	entry="$(git ls-files --stage dirty | cut -f 1)"
+	entry="$(git ls-files --stage dirty | cut -f 1)" &&
 	git mv dirty dirty2 &&
 	[ "$entry" = "$(git ls-files --stage dirty2 | cut -f 1)" ] &&
 	echo 2 >dirty2 &&
diff --git a/t/t7002-grep.sh b/t/t7002-grep.sh
deleted file mode 100755
index b13aa7e..0000000
--- a/t/t7002-grep.sh
+++ /dev/null
@@ -1,282 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2006 Junio C Hamano
-#
-
-test_description='git grep various.
-'
-
-. ./test-lib.sh
-
-cat >hello.c <<EOF
-#include <stdio.h>
-int main(int argc, const char **argv)
-{
-	printf("Hello world.\n");
-	return 0;
-}
-EOF
-
-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 ww w >w &&
-	echo x x xx x >x &&
-	echo y yy >y &&
-	echo zzz > z &&
-	mkdir t &&
-	echo test >t/t &&
-	git add file w x y z t/t hello.c &&
-	test_tick &&
-	git commit -m initial
-'
-
-test_expect_success 'grep should not segfault with a bad input' '
-	test_must_fail git grep "("
-'
-
-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 (w)" '
-		: >expected &&
-		! git grep -n -w -e "^w" >actual &&
-		test_cmp 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
-	'
-
-	test_expect_success "grep $L (t-1)" '
-		echo "${HC}t/t:1:test" >expected &&
-		git grep -n -e test $H >actual &&
-		diff expected actual
-	'
-
-	test_expect_success "grep $L (t-2)" '
-		echo "${HC}t:1:test" >expected &&
-		(
-			cd t &&
-			git grep -n -e test $H
-		) >actual &&
-		diff expected actual
-	'
-
-	test_expect_success "grep $L (t-3)" '
-		echo "${HC}t/t:1:test" >expected &&
-		(
-			cd t &&
-			git grep --full-name -n -e test $H
-		) >actual &&
-		diff expected actual
-	'
-
-	test_expect_success "grep -c $L (no /dev/null)" '
-		! git grep -c test $H | grep /dev/null
-        '
-
-done
-
-cat >expected <<EOF
-file:foo mmap bar_mmap
-EOF
-
-test_expect_success 'grep -e A --and -e B' '
-	git grep -e "foo mmap" --and -e bar_mmap >actual &&
-	test_cmp expected actual
-'
-
-cat >expected <<EOF
-file:foo_mmap bar mmap
-file:foo_mmap bar mmap baz
-EOF
-
-
-test_expect_success 'grep ( -e A --or -e B ) --and -e B' '
-	git grep \( -e foo_ --or -e baz \) \
-		--and -e " mmap" >actual &&
-	test_cmp expected actual
-'
-
-cat >expected <<EOF
-file:foo mmap bar
-EOF
-
-test_expect_success 'grep -e A --and --not -e B' '
-	git grep -e "foo mmap" --and --not -e bar_mmap >actual &&
-	test_cmp expected actual
-'
-
-cat >expected <<EOF
-y:y yy
---
-z:zzz
-EOF
-
-# Create 1024 file names that sort between "y" and "z" to make sure
-# the two files are handled by different calls to an external grep.
-# This depends on MAXARGS in builtin-grep.c being 1024 or less.
-c32="0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v"
-test_expect_success 'grep -C1, hunk mark between files' '
-	for a in $c32; do for b in $c32; do : >y-$a$b; done; done &&
-	git add y-?? &&
-	git grep -C1 "^[yz]" >actual &&
-	test_cmp expected actual
-'
-
-test_expect_success 'grep -C1 --no-ext-grep, hunk mark between files' '
-	git grep -C1 --no-ext-grep "^[yz]" >actual &&
-	test_cmp expected actual
-'
-
-test_expect_success 'log grep setup' '
-	echo a >>file &&
-	test_tick &&
-	GIT_AUTHOR_NAME="With * Asterisk" \
-	GIT_AUTHOR_EMAIL="xyzzy@frotz.com" \
-	git commit -a -m "second" &&
-
-	echo a >>file &&
-	test_tick &&
-	git commit -a -m "third"
-
-'
-
-test_expect_success 'log grep (1)' '
-	git log --author=author --pretty=tformat:%s >actual &&
-	( echo third ; echo initial ) >expect &&
-	test_cmp expect actual
-'
-
-test_expect_success 'log grep (2)' '
-	git log --author=" * " -F --pretty=tformat:%s >actual &&
-	( echo second ) >expect &&
-	test_cmp expect actual
-'
-
-test_expect_success 'log grep (3)' '
-	git log --author="^A U" --pretty=tformat:%s >actual &&
-	( echo third ; echo initial ) >expect &&
-	test_cmp expect actual
-'
-
-test_expect_success 'log grep (4)' '
-	git log --author="frotz\.com>$" --pretty=tformat:%s >actual &&
-	( echo second ) >expect &&
-	test_cmp expect actual
-'
-
-test_expect_success 'log grep (5)' '
-	git log --author=Thor -F --grep=Thu --pretty=tformat:%s >actual &&
-	( echo third ; echo initial ) >expect &&
-	test_cmp expect actual
-'
-
-test_expect_success 'log grep (6)' '
-	git log --author=-0700  --pretty=tformat:%s >actual &&
-	>expect &&
-	test_cmp expect actual
-'
-
-test_expect_success 'grep with CE_VALID file' '
-	git update-index --assume-unchanged t/t &&
-	rm t/t &&
-	test "$(git grep --no-ext-grep test)" = "t/t:test" &&
-	git update-index --no-assume-unchanged t/t &&
-	git checkout t/t
-'
-
-cat >expected <<EOF
-hello.c=#include <stdio.h>
-hello.c:	return 0;
-EOF
-
-test_expect_success 'grep -p with userdiff' '
-	git config diff.custom.funcname "^#" &&
-	echo "hello.c diff=custom" >.gitattributes &&
-	git grep -p return >actual &&
-	test_cmp expected actual
-'
-
-cat >expected <<EOF
-hello.c=int main(int argc, const char **argv)
-hello.c:	return 0;
-EOF
-
-test_expect_success 'grep -p' '
-	rm -f .gitattributes &&
-	git grep -p return >actual &&
-	test_cmp expected actual
-'
-
-cat >expected <<EOF
-hello.c-#include <stdio.h>
-hello.c=int main(int argc, const char **argv)
-hello.c-{
-hello.c-	printf("Hello world.\n");
-hello.c:	return 0;
-EOF
-
-test_expect_success 'grep -p -B5' '
-	git grep -p -B5 return >actual &&
-	test_cmp expected actual
-'
-
-test_done
diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh
index 329c851..4d13e10 100755
--- a/t/t7003-filter-branch.sh
+++ b/t/t7003-filter-branch.sh
@@ -3,31 +3,35 @@
 test_description='git filter-branch'
 . ./test-lib.sh
 
-make_commit () {
-	lower=$(echo $1 | tr '[A-Z]' '[a-z]')
-	echo $lower > $lower
-	git add $lower
-	test_tick
-	git commit -m $1
-	git tag $1
-}
-
 test_expect_success 'setup' '
-	make_commit A
-	make_commit B
-	git checkout -b branch B
-	make_commit D
-	mkdir dir
-	make_commit dir/D
-	make_commit E
-	git checkout master
-	make_commit C
-	git checkout branch
-	git merge C
-	git tag F
-	make_commit G
-	make_commit H
+	test_commit A &&
+	GIT_COMMITTER_DATE="@0 +0000" GIT_AUTHOR_DATE="@0 +0000" &&
+	test_commit --notick B &&
+	git checkout -b branch B &&
+	test_commit D &&
+	mkdir dir &&
+	test_commit dir/D &&
+	test_commit E &&
+	git checkout master &&
+	test_commit C &&
+	git checkout branch &&
+	git merge C &&
+	git tag F &&
+	test_commit G &&
+	test_commit H
 '
+# * (HEAD, branch) H
+# * G
+# *   Merge commit 'C' into branch
+# |\
+# | * (master) C
+# * | E
+# * | dir/D
+# * | D
+# |/
+# * B
+# * A
+
 
 H=$(git rev-parse H)
 
@@ -65,14 +69,14 @@
 '
 
 test_expect_success 'rewrite, renaming a specific file' '
-	git filter-branch -f --tree-filter "mv d doh || :" HEAD
+	git filter-branch -f --tree-filter "mv D.t doh || :" HEAD
 '
 
 test_expect_success 'test that the file was renamed' '
-	test d = "$(git show HEAD:doh --)" &&
-	! test -f d &&
+	test D = "$(git show HEAD:doh --)" &&
+	! test -f D.t &&
 	test -f doh &&
-	test d = "$(cat doh)"
+	test D = "$(cat doh)"
 '
 
 test_expect_success 'rewrite, renaming a specific directory' '
@@ -80,18 +84,18 @@
 '
 
 test_expect_success 'test that the directory was renamed' '
-	test dir/d = "$(git show HEAD:diroh/d --)" &&
+	test dir/D = "$(git show HEAD:diroh/D.t --)" &&
 	! test -d dir &&
 	test -d diroh &&
 	! test -d diroh/dir &&
-	test -f diroh/d &&
-	test dir/d = "$(cat diroh/d)"
+	test -f diroh/D.t &&
+	test dir/D = "$(cat diroh/D.t)"
 '
 
 git tag oldD HEAD~4
 test_expect_success 'rewrite one branch, keeping a side branch' '
 	git branch modD oldD &&
-	git filter-branch -f --tree-filter "mv b boh || :" D..modD
+	git filter-branch -f --tree-filter "mv B.t boh || :" D..modD
 '
 
 test_expect_success 'common ancestor is still common (unchanged)' '
@@ -104,13 +108,13 @@
 	git add subdir/new &&
 	test_tick &&
 	git commit -m "subdir" &&
-	echo H > a &&
+	echo H > A.t &&
 	test_tick &&
-	git commit -m "not subdir" a &&
+	git commit -m "not subdir" A.t &&
 	echo A > subdir/new &&
 	test_tick &&
 	git commit -m "again subdir" subdir/new &&
-	git rm a &&
+	git rm A.t &&
 	test_tick &&
 	git commit -m "again not subdir" &&
 	git branch sub &&
@@ -134,7 +138,7 @@
 	git add subdir/new &&
 	test_tick &&
 	git commit -m "subdir on master" subdir/new &&
-	git rm a &&
+	git rm A.t &&
 	test_tick &&
 	git commit -m "again subdir on master" &&
 	git merge branch
@@ -143,11 +147,12 @@
 test_expect_success 'use index-filter to move into a subdirectory' '
 	git branch directorymoved &&
 	git filter-branch -f --index-filter \
-		 "git ls-files -s | sed \"s-\\t-&newsubdir/-\" |
+		 "git ls-files -s | sed \"s-	-&newsubdir/-\" |
 	          GIT_INDEX_FILE=\$GIT_INDEX_FILE.new \
 			git update-index --index-info &&
 		  mv \"\$GIT_INDEX_FILE.new\" \"\$GIT_INDEX_FILE\"" directorymoved &&
-	test -z "$(git diff HEAD directorymoved:newsubdir)"'
+	git diff --exit-code HEAD directorymoved:newsubdir
+'
 
 test_expect_success 'stops when msg filter fails' '
 	old=$(git rev-parse HEAD) &&
@@ -282,10 +287,85 @@
 
 test_expect_success 'Prune empty commits' '
 	git rev-list HEAD > expect &&
-	make_commit to_remove &&
-	git filter-branch -f --index-filter "git update-index --remove to_remove" --prune-empty HEAD &&
+	test_commit to_remove &&
+	git filter-branch -f --index-filter "git update-index --remove to_remove.t" --prune-empty HEAD &&
 	git rev-list HEAD > actual &&
 	test_cmp expect actual
 '
 
+test_expect_success '--remap-to-ancestor with filename filters' '
+	git checkout master &&
+	git reset --hard A &&
+	test_commit add-foo foo 1 &&
+	git branch moved-foo &&
+	test_commit add-bar bar a &&
+	git branch invariant &&
+	orig_invariant=$(git rev-parse invariant) &&
+	git branch moved-bar &&
+	test_commit change-foo foo 2 &&
+	git filter-branch -f --remap-to-ancestor \
+		moved-foo moved-bar A..master \
+		-- -- foo &&
+	test $(git rev-parse moved-foo) = $(git rev-parse moved-bar) &&
+	test $(git rev-parse moved-foo) = $(git rev-parse master^) &&
+	test $orig_invariant = $(git rev-parse invariant)
+'
+
+test_expect_success 'automatic remapping to ancestor with filename filters' '
+	git checkout master &&
+	git reset --hard A &&
+	test_commit add-foo2 foo 1 &&
+	git branch moved-foo2 &&
+	test_commit add-bar2 bar a &&
+	git branch invariant2 &&
+	orig_invariant=$(git rev-parse invariant2) &&
+	git branch moved-bar2 &&
+	test_commit change-foo2 foo 2 &&
+	git filter-branch -f \
+		moved-foo2 moved-bar2 A..master \
+		-- -- foo &&
+	test $(git rev-parse moved-foo2) = $(git rev-parse moved-bar2) &&
+	test $(git rev-parse moved-foo2) = $(git rev-parse master^) &&
+	test $orig_invariant = $(git rev-parse invariant2)
+'
+
+test_expect_success 'setup submodule' '
+	rm -fr ?* .git &&
+	git init &&
+	test_commit file &&
+	mkdir submod &&
+	submodurl="$PWD/submod" &&
+	( cd submod &&
+	  git init &&
+	  test_commit file-in-submod ) &&
+	git submodule add "$submodurl" &&
+	git commit -m "added submodule" &&
+	test_commit add-file &&
+	( cd submod && test_commit add-in-submodule ) &&
+	git add submod &&
+	git commit -m "changed submodule" &&
+	git branch original HEAD
+'
+
+orig_head=`git show-ref --hash --head HEAD`
+
+test_expect_success 'rewrite submodule with another content' '
+	git filter-branch --tree-filter "test -d submod && {
+					 rm -rf submod &&
+					 git rm -rf --quiet submod &&
+					 mkdir submod &&
+					 : > submod/file
+					 } || :" HEAD &&
+	test $orig_head != `git show-ref --hash --head HEAD`
+'
+
+test_expect_success 'replace submodule revision' '
+	git reset --hard original &&
+	git filter-branch -f --tree-filter \
+	    "if git ls-files --error-unmatch -- submod > /dev/null 2>&1
+	     then git update-index --cacheinfo 160000 0123456789012345678901234567890123456789 submod
+	     fi" HEAD &&
+	test $orig_head != `git show-ref --hash --head HEAD`
+'
+
 test_done
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index 73dbc43..5189446 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -8,6 +8,7 @@
 Tests for operations with tags.'
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-gpg.sh
 
 # creating and listing lightweight tags:
 
@@ -257,6 +258,55 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'tag -l can accept multiple patterns' '
+	git tag -l "v1*" "v0*" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'listing tags in column' '
+	COLUMNS=40 git tag -l --column=row >actual &&
+	cat >expected <<\EOF &&
+a1      aa1     cba     t210    t211
+v0.2.1  v1.0    v1.0.1  v1.1.3
+EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'listing tags in column with column.*' '
+	git config column.tag row &&
+	git config column.ui dense &&
+	COLUMNS=40 git tag -l >actual &&
+	git config --unset column.ui &&
+	git config --unset column.tag &&
+	cat >expected <<\EOF &&
+a1      aa1   cba     t210    t211
+v0.2.1  v1.0  v1.0.1  v1.1.3
+EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'listing tag with -n --column should fail' '
+	test_must_fail git tag --column -n
+'
+
+test_expect_success 'listing tags -n in column with column.ui ignored' '
+	git config column.ui "row dense" &&
+	COLUMNS=40 git tag -l -n >actual &&
+	git config --unset column.ui &&
+	cat >expected <<\EOF &&
+a1              Foo
+aa1             Foo
+cba             Foo
+t210            Foo
+t211            Foo
+v0.2.1          Foo
+v1.0            Foo
+v1.0.1          Foo
+v1.1.3          Foo
+EOF
+	test_cmp expected actual
+'
+
 # creating and verifying lightweight tags:
 
 test_expect_success \
@@ -580,23 +630,18 @@
 	test_cmp expect actual
 '
 
-# subsequent tests require gpg; check if it is available
-gpg --version >/dev/null 2>/dev/null
-if [ $? -eq 127 ]; then
-	say "gpg not found - skipping tag signing and verification tests"
-else
-	# As said here: http://www.gnupg.org/documentation/faqs.html#q6.19
-	# the gpg version 1.0.6 didn't parse trust packets correctly, so for
-	# that version, creation of signed tags using the generated key fails.
-	case "$(gpg --version)" in
-	'gpg (GnuPG) 1.0.6'*)
-		say "Skipping signed tag tests, because a bug in 1.0.6 version"
-		;;
-	*)
-		test_set_prereq GPG
-		;;
-	esac
-fi
+test_expect_success 'annotations for blobs are empty' '
+	blob=$(git hash-object -w --stdin <<-\EOF
+	Blob paragraph 1.
+
+	Blob paragraph 2.
+	EOF
+	) &&
+	git tag tag-blob $blob &&
+	echo "tag-blob        " >expect &&
+	git tag -n1 -l tag-blob >actual &&
+	test_cmp expect actual
+'
 
 # trying to verify annotated non-signed tags:
 
@@ -620,16 +665,6 @@
 
 # creating and verifying signed tags:
 
-# key generation info: gpg --homedir t/t7004 --gen-key
-# Type DSA and Elgamal, size 2048 bits, no expiration date.
-# Name and email: C O Mitter <committer@example.com>
-# No password given, to enable non-interactive operation.
-
-cp -R "$TEST_DIRECTORY"/t7004 ./gpghome
-chmod 0700 gpghome
-GNUPGHOME="$(pwd)/gpghome"
-export GNUPGHOME
-
 get_tag_header signed-tag $commit commit $time >expect
 echo 'A signed tag message' >>expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
@@ -1030,6 +1065,72 @@
 	test_cmp expect actual
 '
 
+# usage with rfc1991 signatures
+echo "rfc1991" > gpghome/gpg.conf
+get_tag_header rfc1991-signed-tag $commit commit $time >expect
+echo "RFC1991 signed tag" >>expect
+echo '-----BEGIN PGP MESSAGE-----' >>expect
+test_expect_success GPG \
+	'creating a signed tag with rfc1991' '
+	git tag -s -m "RFC1991 signed tag" rfc1991-signed-tag $commit &&
+	get_tag_msg rfc1991-signed-tag >actual &&
+	test_cmp expect actual
+'
+
+cat >fakeeditor <<'EOF'
+#!/bin/sh
+cp "$1" actual
+EOF
+chmod +x fakeeditor
+
+test_expect_success GPG \
+	'reediting a signed tag body omits signature' '
+	echo "RFC1991 signed tag" >expect &&
+	GIT_EDITOR=./fakeeditor git tag -f -s rfc1991-signed-tag $commit &&
+	test_cmp expect actual
+'
+
+test_expect_success GPG \
+	'verifying rfc1991 signature' '
+	git tag -v rfc1991-signed-tag
+'
+
+test_expect_success GPG \
+	'list tag with rfc1991 signature' '
+	echo "rfc1991-signed-tag RFC1991 signed tag" >expect &&
+	git tag -l -n1 rfc1991-signed-tag >actual &&
+	test_cmp expect actual &&
+	git tag -l -n2 rfc1991-signed-tag >actual &&
+	test_cmp expect actual &&
+	git tag -l -n999 rfc1991-signed-tag >actual &&
+	test_cmp expect actual
+'
+
+rm -f gpghome/gpg.conf
+
+test_expect_success GPG \
+	'verifying rfc1991 signature without --rfc1991' '
+	git tag -v rfc1991-signed-tag
+'
+
+test_expect_success GPG \
+	'list tag with rfc1991 signature without --rfc1991' '
+	echo "rfc1991-signed-tag RFC1991 signed tag" >expect &&
+	git tag -l -n1 rfc1991-signed-tag >actual &&
+	test_cmp expect actual &&
+	git tag -l -n2 rfc1991-signed-tag >actual &&
+	test_cmp expect actual &&
+	git tag -l -n999 rfc1991-signed-tag >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success GPG \
+	'reediting a signed tag body omits signature' '
+	echo "RFC1991 signed tag" >expect &&
+	GIT_EDITOR=./fakeeditor git tag -f -s rfc1991-signed-tag $commit &&
+	test_cmp expect actual
+'
+
 # try to sign with bad user.signingkey
 git config user.signingkey BobTheMouse
 test_expect_success GPG \
@@ -1051,13 +1152,22 @@
 
 test_expect_success \
 	'message in editor has initial comment' '
-	GIT_EDITOR=cat git tag -a initial-comment > actual
+	! (GIT_EDITOR=cat git tag -a initial-comment > actual)
+'
+
+test_expect_success 'message in editor has initial comment: first line' '
 	# check the first line --- should be empty
-	first=$(sed -e 1q <actual) &&
-	test -z "$first" &&
+	echo >first.expect &&
+	sed -e 1q <actual >first.actual &&
+	test_i18ncmp first.expect first.actual
+'
+
+test_expect_success \
+	'message in editor has initial comment: remainder' '
 	# remove commented lines from the remainder -- should be empty
-	rest=$(sed -e 1d -e '/^#/d' <actual) &&
-	test -z "$rest"
+	>rest.expect
+	sed -e 1d -e '/^#/d' <actual >rest.actual &&
+	test_cmp rest.expect rest.actual
 '
 
 get_tag_header reuse $commit commit $time >expect
@@ -1097,7 +1207,7 @@
 test_expect_success 'creating second commit and tag' '
 	echo foo-2.0 >foo &&
 	git add foo &&
-	git commit -m second
+	git commit -m second &&
 	git tag v2.0
 '
 
@@ -1122,18 +1232,18 @@
 EOF
 
 test_expect_success 'checking that first commit is in all tags (hash)' "
-	git tag -l --contains $hash1 v* >actual
+	git tag -l --contains $hash1 v* >actual &&
 	test_cmp expected actual
 "
 
 # other ways of specifying the commit
 test_expect_success 'checking that first commit is in all tags (tag)' "
-	git tag -l --contains v1.0 v* >actual
+	git tag -l --contains v1.0 v* >actual &&
 	test_cmp expected actual
 "
 
 test_expect_success 'checking that first commit is in all tags (relative)' "
-	git tag -l --contains HEAD~2 v* >actual
+	git tag -l --contains HEAD~2 v* >actual &&
 	test_cmp expected actual
 "
 
@@ -1142,7 +1252,7 @@
 EOF
 
 test_expect_success 'checking that second commit only has one tag' "
-	git tag -l --contains $hash2 v* >actual
+	git tag -l --contains $hash2 v* >actual &&
 	test_cmp expected actual
 "
 
@@ -1151,7 +1261,7 @@
 EOF
 
 test_expect_success 'checking that third commit has no tags' "
-	git tag -l --contains $hash3 v* >actual
+	git tag -l --contains $hash3 v* >actual &&
 	test_cmp expected actual
 "
 
@@ -1161,7 +1271,7 @@
 	git branch stable v2.0 &&
         git checkout stable &&
 	echo foo-3.0 > foo &&
-	git commit foo -m fourth
+	git commit foo -m fourth &&
 	git tag v3.0
 '
 
@@ -1172,7 +1282,7 @@
 EOF
 
 test_expect_success 'checking that branch head only has one tag' "
-	git tag -l --contains $hash4 v* >actual
+	git tag -l --contains $hash4 v* >actual &&
 	test_cmp expected actual
 "
 
@@ -1186,7 +1296,7 @@
 EOF
 
 test_expect_success 'checking that original branch head has one tag now' "
-	git tag -l --contains $hash3 v* >actual
+	git tag -l --contains $hash3 v* >actual &&
 	test_cmp expected actual
 "
 
@@ -1201,19 +1311,58 @@
 EOF
 
 test_expect_success 'checking that initial commit is in all tags' "
-	git tag -l --contains $hash1 v* >actual
+	git tag -l --contains $hash1 v* >actual &&
 	test_cmp expected actual
 "
 
 # mixing modes and options:
 
 test_expect_success 'mixing incompatibles modes and options is forbidden' '
-	test_must_fail git tag -a
-	test_must_fail git tag -l -v
-	test_must_fail git tag -n 100
-	test_must_fail git tag -l -m msg
-	test_must_fail git tag -l -F some file
+	test_must_fail git tag -a &&
+	test_must_fail git tag -l -v &&
+	test_must_fail git tag -n 100 &&
+	test_must_fail git tag -l -m msg &&
+	test_must_fail git tag -l -F some file &&
 	test_must_fail git tag -v -s
 '
 
+# check points-at
+
+test_expect_success '--points-at cannot be used in non-list mode' '
+	test_must_fail git tag --points-at=v4.0 foo
+'
+
+test_expect_success '--points-at finds lightweight tags' '
+	echo v4.0 >expect &&
+	git tag --points-at v4.0 >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '--points-at finds annotated tags of commits' '
+	git tag -m "v4.0, annotated" annotated-v4.0 v4.0 &&
+	echo annotated-v4.0 >expect &&
+	git tag -l --points-at v4.0 "annotated*" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '--points-at finds annotated tags of tags' '
+	git tag -m "describing the v4.0 tag object" \
+		annotated-again-v4.0 annotated-v4.0 &&
+	cat >expect <<-\EOF &&
+	annotated-again-v4.0
+	annotated-v4.0
+	EOF
+	git tag --points-at=annotated-v4.0 >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'multiple --points-at are OR-ed together' '
+	cat >expect <<-\EOF &&
+	v2.0
+	v3.0
+	EOF
+	git tag --points-at=v2.0 --points-at=v3.0 >actual &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t7005-editor.sh b/t/t7005-editor.sh
index b647957..1b530b5 100755
--- a/t/t7005-editor.sh
+++ b/t/t7005-editor.sh
@@ -4,7 +4,21 @@
 
 . ./test-lib.sh
 
-for i in GIT_EDITOR core_editor EDITOR VISUAL vi
+unset EDITOR VISUAL GIT_EDITOR
+
+test_expect_success 'determine default editor' '
+
+	vi=$(TERM=vt100 git var GIT_EDITOR) &&
+	test -n "$vi"
+
+'
+
+if ! expr "$vi" : '[a-z]*$' >/dev/null
+then
+	vi=
+fi
+
+for i in GIT_EDITOR core_editor EDITOR VISUAL $vi
 do
 	cat >e-$i.sh <<-EOF
 	#!$SHELL_PATH
@@ -12,20 +26,19 @@
 	EOF
 	chmod +x e-$i.sh
 done
-unset vi
-mv e-vi.sh vi
-unset EDITOR VISUAL GIT_EDITOR
+
+if ! test -z "$vi"
+then
+	mv e-$vi.sh $vi
+fi
 
 test_expect_success setup '
 
-	msg="Hand edited" &&
+	msg="Hand-edited" &&
+	test_commit "$msg" &&
 	echo "$msg" >expect &&
-	git add vi &&
-	test_tick &&
-	git commit -m "$msg" &&
-	git show -s --pretty=oneline |
-	sed -e "s/^[0-9a-f]* //" >actual &&
-	diff actual expect
+	git show -s --format=%s > actual &&
+	test_cmp actual expect
 
 '
 
@@ -42,9 +55,19 @@
 	fi
 '
 
+test_expect_success 'dumb should prefer EDITOR to VISUAL' '
+
+	EDITOR=./e-EDITOR.sh &&
+	VISUAL=./e-VISUAL.sh &&
+	export EDITOR VISUAL &&
+	git commit --amend &&
+	test "$(git show -s --format=%s)" = "Edited by EDITOR"
+
+'
+
 TERM=vt100
 export TERM
-for i in vi EDITOR VISUAL core_editor GIT_EDITOR
+for i in $vi EDITOR VISUAL core_editor GIT_EDITOR
 do
 	echo "Edited by $i" >expect
 	unset EDITOR VISUAL GIT_EDITOR
@@ -62,13 +85,13 @@
 		git --exec-path=. commit --amend &&
 		git show -s --pretty=oneline |
 		sed -e "s/^[0-9a-f]* //" >actual &&
-		diff actual expect
+		test_cmp actual expect
 	'
 done
 
 unset EDITOR VISUAL GIT_EDITOR
 git config --unset-all core.editor
-for i in vi EDITOR VISUAL core_editor GIT_EDITOR
+for i in $vi EDITOR VISUAL core_editor GIT_EDITOR
 do
 	echo "Edited by $i" >expect
 	case "$i" in
@@ -84,17 +107,17 @@
 		git --exec-path=. commit --amend &&
 		git show -s --pretty=oneline |
 		sed -e "s/^[0-9a-f]* //" >actual &&
-		diff actual expect
+		test_cmp actual expect
 	'
 done
 
-if ! echo 'echo space > "$1"' > "e space.sh"
+if echo 'echo space > "$1"' > "e space.sh"
 then
-	say "Skipping; FS does not support spaces in filenames"
-	test_done
+	# FS supports spaces in filenames
+	test_set_prereq SPACES_IN_FILENAMES
 fi
 
-test_expect_success 'editor with a space' '
+test_expect_success SPACES_IN_FILENAMES 'editor with a space' '
 
 	chmod a+x "e space.sh" &&
 	GIT_EDITOR="./e\ space.sh" git commit --amend &&
@@ -103,7 +126,7 @@
 '
 
 unset GIT_EDITOR
-test_expect_success 'core.editor with a space' '
+test_expect_success SPACES_IN_FILENAMES 'core.editor with a space' '
 
 	git config core.editor \"./e\ space.sh\" &&
 	git commit --amend &&
diff --git a/t/t7006-pager.sh b/t/t7006-pager.sh
new file mode 100755
index 0000000..ff25908
--- /dev/null
+++ b/t/t7006-pager.sh
@@ -0,0 +1,450 @@
+#!/bin/sh
+
+test_description='Test automatic use of a pager.'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-pager.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
+
+test_expect_success 'setup' '
+	sane_unset GIT_PAGER GIT_PAGER_IN_USE &&
+	test_unconfig core.pager &&
+
+	PAGER="cat >paginated.out" &&
+	export PAGER &&
+
+	test_commit initial
+'
+
+test_expect_success TTY 'some commands use a pager' '
+	rm -f paginated.out &&
+	test_terminal git log &&
+	test -e paginated.out
+'
+
+test_expect_failure TTY 'pager runs from subdir' '
+	echo subdir/paginated.out >expected &&
+	mkdir -p subdir &&
+	rm -f paginated.out subdir/paginated.out &&
+	(
+		cd subdir &&
+		test_terminal git log
+	) &&
+	{
+		ls paginated.out subdir/paginated.out ||
+		:
+	} >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success TTY 'some commands do not use a pager' '
+	rm -f paginated.out &&
+	test_terminal git rev-list HEAD &&
+	! test -e paginated.out
+'
+
+test_expect_success 'no pager when stdout is a pipe' '
+	rm -f paginated.out &&
+	git log | cat &&
+	! test -e paginated.out
+'
+
+test_expect_success 'no pager when stdout is a regular file' '
+	rm -f paginated.out &&
+	git log >file &&
+	! test -e paginated.out
+'
+
+test_expect_success TTY 'git --paginate rev-list uses a pager' '
+	rm -f paginated.out &&
+	test_terminal git --paginate rev-list HEAD &&
+	test -e paginated.out
+'
+
+test_expect_success 'no pager even with --paginate when stdout is a pipe' '
+	rm -f file paginated.out &&
+	git --paginate log | cat &&
+	! test -e paginated.out
+'
+
+test_expect_success TTY 'no pager with --no-pager' '
+	rm -f paginated.out &&
+	test_terminal git --no-pager log &&
+	! test -e paginated.out
+'
+
+test_expect_success TTY 'configuration can disable pager' '
+	rm -f paginated.out &&
+	test_unconfig pager.grep &&
+	test_terminal git grep initial &&
+	test -e paginated.out &&
+
+	rm -f paginated.out &&
+	test_config pager.grep false &&
+	test_terminal git grep initial &&
+	! test -e paginated.out
+'
+
+test_expect_success TTY 'git config uses a pager if configured to' '
+	rm -f paginated.out &&
+	test_config pager.config true &&
+	test_terminal git config --list &&
+	test -e paginated.out
+'
+
+test_expect_success TTY 'configuration can enable pager (from subdir)' '
+	rm -f paginated.out &&
+	mkdir -p subdir &&
+	test_config pager.bundle true &&
+
+	git bundle create test.bundle --all &&
+	rm -f paginated.out subdir/paginated.out &&
+	(
+		cd subdir &&
+		test_terminal git bundle unbundle ../test.bundle
+	) &&
+	{
+		test -e paginated.out ||
+		test -e subdir/paginated.out
+	}
+'
+
+# A colored commit log will begin with an appropriate ANSI escape
+# for the first color; the text "commit" comes later.
+colorful() {
+	read firstline <$1
+	! expr "$firstline" : "[a-zA-Z]" >/dev/null
+}
+
+test_expect_success 'tests can detect color' '
+	rm -f colorful.log colorless.log &&
+	git log --no-color >colorless.log &&
+	git log --color >colorful.log &&
+	! colorful colorless.log &&
+	colorful colorful.log
+'
+
+test_expect_success 'no color when stdout is a regular file' '
+	rm -f colorless.log &&
+	test_config color.ui auto &&
+	git log >colorless.log &&
+	! colorful colorless.log
+'
+
+test_expect_success TTY 'color when writing to a pager' '
+	rm -f paginated.out &&
+	test_config color.ui auto &&
+	(
+		TERM=vt100 &&
+		export TERM &&
+		test_terminal git log
+	) &&
+	colorful paginated.out
+'
+
+test_expect_success TTY 'colors are suppressed by color.pager' '
+	rm -f paginated.out &&
+	test_config color.ui auto &&
+	test_config color.pager false &&
+	(
+		TERM=vt100 &&
+		export TERM &&
+		test_terminal git log
+	) &&
+	! colorful paginated.out
+'
+
+test_expect_success 'color when writing to a file intended for a pager' '
+	rm -f colorful.log &&
+	test_config color.ui auto &&
+	(
+		TERM=vt100 &&
+		GIT_PAGER_IN_USE=true &&
+		export TERM GIT_PAGER_IN_USE &&
+		git log >colorful.log
+	) &&
+	colorful colorful.log
+'
+
+test_expect_success TTY 'colors are sent to pager for external commands' '
+	test_config alias.externallog "!git log" &&
+	test_config color.ui auto &&
+	(
+		TERM=vt100 &&
+		export TERM &&
+		test_terminal git -p externallog
+	) &&
+	colorful paginated.out
+'
+
+# Use this helper to make it easy for the caller of your
+# terminal-using function to specify whether it should fail.
+# If you write
+#
+#	your_test() {
+#		parse_args "$@"
+#
+#		$test_expectation "$cmd - behaves well" "
+#			...
+#			$full_command &&
+#			...
+#		"
+#	}
+#
+# then your test can be used like this:
+#
+#	your_test expect_(success|failure) [test_must_fail] 'git foo'
+#
+parse_args() {
+	test_expectation="test_$1"
+	shift
+	if test "$1" = test_must_fail
+	then
+		full_command="test_must_fail test_terminal "
+		shift
+	else
+		full_command="test_terminal "
+	fi
+	cmd=$1
+	full_command="$full_command $1"
+}
+
+test_default_pager() {
+	parse_args "$@"
+
+	$test_expectation SIMPLEPAGER,TTY "$cmd - default pager is used by default" "
+		sane_unset PAGER GIT_PAGER &&
+		test_unconfig core.pager &&
+		rm -f default_pager_used &&
+		cat >\$less <<-\EOF &&
+		#!/bin/sh
+		wc >default_pager_used
+		EOF
+		chmod +x \$less &&
+		(
+			PATH=.:\$PATH &&
+			export PATH &&
+			$full_command
+		) &&
+		test -e default_pager_used
+	"
+}
+
+test_PAGER_overrides() {
+	parse_args "$@"
+
+	$test_expectation TTY "$cmd - PAGER overrides default pager" "
+		sane_unset GIT_PAGER &&
+		test_unconfig core.pager &&
+		rm -f PAGER_used &&
+		PAGER='wc >PAGER_used' &&
+		export PAGER &&
+		$full_command &&
+		test -e PAGER_used
+	"
+}
+
+test_core_pager_overrides() {
+	if_local_config=
+	used_if_wanted='overrides PAGER'
+	test_core_pager "$@"
+}
+
+test_local_config_ignored() {
+	if_local_config='! '
+	used_if_wanted='is not used'
+	test_core_pager "$@"
+}
+
+test_core_pager() {
+	parse_args "$@"
+
+	$test_expectation TTY "$cmd - repository-local core.pager setting $used_if_wanted" "
+		sane_unset GIT_PAGER &&
+		rm -f core.pager_used &&
+		PAGER=wc &&
+		export PAGER &&
+		test_config core.pager 'wc >core.pager_used' &&
+		$full_command &&
+		${if_local_config}test -e core.pager_used
+	"
+}
+
+test_core_pager_subdir() {
+	if_local_config=
+	used_if_wanted='overrides PAGER'
+	test_pager_subdir_helper "$@"
+}
+
+test_no_local_config_subdir() {
+	if_local_config='! '
+	used_if_wanted='is not used'
+	test_pager_subdir_helper "$@"
+}
+
+test_pager_subdir_helper() {
+	parse_args "$@"
+
+	$test_expectation TTY "$cmd - core.pager $used_if_wanted from subdirectory" "
+		sane_unset GIT_PAGER &&
+		rm -f core.pager_used &&
+		rm -fr sub &&
+		PAGER=wc &&
+		stampname=\$(pwd)/core.pager_used &&
+		export PAGER stampname &&
+		test_config core.pager 'wc >\"\$stampname\"' &&
+		mkdir sub &&
+		(
+			cd sub &&
+			$full_command
+		) &&
+		${if_local_config}test -e core.pager_used
+	"
+}
+
+test_GIT_PAGER_overrides() {
+	parse_args "$@"
+
+	$test_expectation TTY "$cmd - GIT_PAGER overrides core.pager" "
+		rm -f GIT_PAGER_used &&
+		test_config core.pager wc &&
+		GIT_PAGER='wc >GIT_PAGER_used' &&
+		export GIT_PAGER &&
+		$full_command &&
+		test -e GIT_PAGER_used
+	"
+}
+
+test_doesnt_paginate() {
+	parse_args "$@"
+
+	$test_expectation TTY "no pager for '$cmd'" "
+		rm -f GIT_PAGER_used &&
+		GIT_PAGER='wc >GIT_PAGER_used' &&
+		export GIT_PAGER &&
+		$full_command &&
+		! test -e GIT_PAGER_used
+	"
+}
+
+test_pager_choices() {
+	test_default_pager        expect_success "$@"
+	test_PAGER_overrides      expect_success "$@"
+	test_core_pager_overrides expect_success "$@"
+	test_core_pager_subdir    expect_success "$@"
+	test_GIT_PAGER_overrides  expect_success "$@"
+}
+
+test_expect_success 'setup: some aliases' '
+	git config alias.aliasedlog log &&
+	git config alias.true "!true"
+'
+
+test_pager_choices                       'git log'
+test_pager_choices                       'git -p log'
+test_pager_choices                       'git aliasedlog'
+
+test_default_pager        expect_success 'git -p aliasedlog'
+test_PAGER_overrides      expect_success 'git -p aliasedlog'
+test_core_pager_overrides expect_success 'git -p aliasedlog'
+test_core_pager_subdir    expect_failure 'git -p aliasedlog'
+test_GIT_PAGER_overrides  expect_success 'git -p aliasedlog'
+
+test_default_pager        expect_success 'git -p true'
+test_PAGER_overrides      expect_success 'git -p true'
+test_core_pager_overrides expect_success 'git -p true'
+test_core_pager_subdir    expect_failure 'git -p true'
+test_GIT_PAGER_overrides  expect_success 'git -p true'
+
+test_default_pager        expect_success test_must_fail 'git -p request-pull'
+test_PAGER_overrides      expect_success test_must_fail 'git -p request-pull'
+test_core_pager_overrides expect_success test_must_fail 'git -p request-pull'
+test_core_pager_subdir    expect_failure test_must_fail 'git -p request-pull'
+test_GIT_PAGER_overrides  expect_success test_must_fail 'git -p request-pull'
+
+test_default_pager        expect_success test_must_fail 'git -p'
+test_PAGER_overrides      expect_success test_must_fail 'git -p'
+test_local_config_ignored expect_failure test_must_fail 'git -p'
+test_no_local_config_subdir expect_success test_must_fail 'git -p'
+test_GIT_PAGER_overrides  expect_success test_must_fail 'git -p'
+
+test_doesnt_paginate      expect_failure test_must_fail 'git -p nonsense'
+
+test_pager_choices                       'git shortlog'
+test_expect_success 'setup: configure shortlog not to paginate' '
+	git config pager.shortlog false
+'
+test_doesnt_paginate      expect_success 'git shortlog'
+test_no_local_config_subdir expect_success 'git shortlog'
+test_default_pager        expect_success 'git -p shortlog'
+test_core_pager_subdir    expect_success 'git -p shortlog'
+
+test_core_pager_subdir    expect_success test_must_fail \
+					 'git -p apply </dev/null'
+
+test_expect_success TTY 'command-specific pager' '
+	sane_unset PAGER GIT_PAGER &&
+	echo "foo:initial" >expect &&
+	>actual &&
+	test_unconfig core.pager &&
+	test_config pager.log "sed s/^/foo:/ >actual" &&
+	test_terminal git log --format=%s -1 &&
+	test_cmp expect actual
+'
+
+test_expect_success TTY 'command-specific pager overrides core.pager' '
+	sane_unset PAGER GIT_PAGER &&
+	echo "foo:initial" >expect &&
+	>actual &&
+	test_config core.pager "exit 1"
+	test_config pager.log "sed s/^/foo:/ >actual" &&
+	test_terminal git log --format=%s -1 &&
+	test_cmp expect actual
+'
+
+test_expect_success TTY 'command-specific pager overridden by environment' '
+	GIT_PAGER="sed s/^/foo:/ >actual" && export GIT_PAGER &&
+	>actual &&
+	echo "foo:initial" >expect &&
+	test_config pager.log "exit 1" &&
+	test_terminal git log --format=%s -1 &&
+	test_cmp expect actual
+'
+
+test_expect_success 'setup external command' '
+	cat >git-external <<-\EOF &&
+	#!/bin/sh
+	git "$@"
+	EOF
+	chmod +x git-external
+'
+
+test_expect_success TTY 'command-specific pager works for external commands' '
+	sane_unset PAGER GIT_PAGER &&
+	echo "foo:initial" >expect &&
+	>actual &&
+	test_config pager.external "sed s/^/foo:/ >actual" &&
+	test_terminal git --exec-path="`pwd`" external log --format=%s -1 &&
+	test_cmp expect actual
+'
+
+test_expect_success TTY 'sub-commands of externals use their own pager' '
+	sane_unset PAGER GIT_PAGER &&
+	echo "foo:initial" >expect &&
+	>actual &&
+	test_config pager.log "sed s/^/foo:/ >actual" &&
+	test_terminal git --exec-path=. external log --format=%s -1 &&
+	test_cmp expect actual
+'
+
+test_expect_success TTY 'external command pagers override sub-commands' '
+	sane_unset PAGER GIT_PAGER &&
+	>expect &&
+	>actual &&
+	test_config pager.external false &&
+	test_config pager.log "sed s/^/log:/ >actual" &&
+	test_terminal git --exec-path=. external log --format=%s -1 &&
+	test_cmp expect actual
+'
+
+test_done
diff --git a/t/t7007-show.sh b/t/t7007-show.sh
index cce222f..a40cd36 100755
--- a/t/t7007-show.sh
+++ b/t/t7007-show.sh
@@ -17,4 +17,95 @@
 	test_must_fail git --no-pager show foo-tag
 '
 
+test_expect_success 'set up a bit of history' '
+	test_commit main1 &&
+	test_commit main2 &&
+	test_commit main3 &&
+	git tag -m "annotated tag" annotated &&
+	git checkout -b side HEAD^^ &&
+	test_commit side2 &&
+	test_commit side3
+'
+
+test_expect_success 'showing two commits' '
+	cat >expect <<-EOF &&
+	commit $(git rev-parse main2)
+	commit $(git rev-parse main3)
+	EOF
+	git show main2 main3 >actual &&
+	grep ^commit actual >actual.filtered &&
+	test_cmp expect actual.filtered
+'
+
+test_expect_success 'showing a range walks (linear)' '
+	cat >expect <<-EOF &&
+	commit $(git rev-parse main3)
+	commit $(git rev-parse main2)
+	EOF
+	git show main1..main3 >actual &&
+	grep ^commit actual >actual.filtered &&
+	test_cmp expect actual.filtered
+'
+
+test_expect_success 'showing a range walks (Y shape, ^ first)' '
+	cat >expect <<-EOF &&
+	commit $(git rev-parse main3)
+	commit $(git rev-parse main2)
+	EOF
+	git show ^side3 main3 >actual &&
+	grep ^commit actual >actual.filtered &&
+	test_cmp expect actual.filtered
+'
+
+test_expect_success 'showing a range walks (Y shape, ^ last)' '
+	cat >expect <<-EOF &&
+	commit $(git rev-parse main3)
+	commit $(git rev-parse main2)
+	EOF
+	git show main3 ^side3 >actual &&
+	grep ^commit actual >actual.filtered &&
+	test_cmp expect actual.filtered
+'
+
+test_expect_success 'showing with -N walks' '
+	cat >expect <<-EOF &&
+	commit $(git rev-parse main3)
+	commit $(git rev-parse main2)
+	EOF
+	git show -2 main3 >actual &&
+	grep ^commit actual >actual.filtered &&
+	test_cmp expect actual.filtered
+'
+
+test_expect_success 'showing annotated tag' '
+	cat >expect <<-EOF &&
+	tag annotated
+	commit $(git rev-parse annotated^{commit})
+	EOF
+	git show annotated >actual &&
+	grep -E "^(commit|tag)" actual >actual.filtered &&
+	test_cmp expect actual.filtered
+'
+
+test_expect_success 'showing annotated tag plus commit' '
+	cat >expect <<-EOF &&
+	tag annotated
+	commit $(git rev-parse annotated^{commit})
+	commit $(git rev-parse side3)
+	EOF
+	git show annotated side3 >actual &&
+	grep -E "^(commit|tag)" actual >actual.filtered &&
+	test_cmp expect actual.filtered
+'
+
+test_expect_success 'showing range' '
+	cat >expect <<-EOF &&
+	commit $(git rev-parse main3)
+	commit $(git rev-parse main2)
+	EOF
+	git show ^side3 annotated >actual &&
+	grep -E "^(commit|tag)" actual >actual.filtered &&
+	test_cmp expect actual.filtered
+'
+
 test_done
diff --git a/t/t7008-grep-binary.sh b/t/t7008-grep-binary.sh
new file mode 100755
index 0000000..fd6410f
--- /dev/null
+++ b/t/t7008-grep-binary.sh
@@ -0,0 +1,126 @@
+#!/bin/sh
+
+test_description='git grep in binary files'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' "
+	echo 'binaryQfile' | q_to_nul >a &&
+	git add a &&
+	git commit -m.
+"
+
+test_expect_success 'git grep ina a' '
+	echo Binary file a matches >expect &&
+	git grep ina a >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git grep -ah ina a' '
+	git grep -ah ina a >actual &&
+	test_cmp a actual
+'
+
+test_expect_success 'git grep -I ina a' '
+	: >expect &&
+	test_must_fail git grep -I ina a >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git grep -c ina a' '
+	echo a:1 >expect &&
+	git grep -c ina a >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git grep -l ina a' '
+	echo a >expect &&
+	git grep -l ina a >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git grep -L bar a' '
+	echo a >expect &&
+	git grep -L bar a >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git grep -q ina a' '
+	: >expect &&
+	git grep -q ina a >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git grep -F ile a' '
+	git grep -F ile a
+'
+
+test_expect_success 'git grep -Fi iLE a' '
+	git grep -Fi iLE a
+'
+
+# This test actually passes on platforms where regexec() supports the
+# flag REG_STARTEND.
+test_expect_success 'git grep ile a' '
+	git grep ile a
+'
+
+test_expect_failure 'git grep .fi a' '
+	git grep .fi a
+'
+
+test_expect_success 'git grep -F y<NUL>f a' "
+	printf 'yQf' | q_to_nul >f &&
+	git grep -f f -F a
+"
+
+test_expect_success 'git grep -F y<NUL>x a' "
+	printf 'yQx' | q_to_nul >f &&
+	test_must_fail git grep -f f -F a
+"
+
+test_expect_success 'git grep -Fi Y<NUL>f a' "
+	printf 'YQf' | q_to_nul >f &&
+	git grep -f f -Fi a
+"
+
+test_expect_success 'git grep -Fi Y<NUL>x a' "
+	printf 'YQx' | q_to_nul >f &&
+	test_must_fail git grep -f f -Fi a
+"
+
+test_expect_success 'git grep y<NUL>f a' "
+	printf 'yQf' | q_to_nul >f &&
+	git grep -f f a
+"
+
+test_expect_success 'git grep y<NUL>x a' "
+	printf 'yQx' | q_to_nul >f &&
+	test_must_fail git grep -f f a
+"
+
+test_expect_success 'grep respects binary diff attribute' '
+	echo text >t &&
+	git add t &&
+	echo t:text >expect &&
+	git grep text t >actual &&
+	test_cmp expect actual &&
+	echo "t -diff" >.gitattributes &&
+	echo "Binary file t matches" >expect &&
+	git grep text t >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'grep respects not-binary diff attribute' '
+	echo binQary | q_to_nul >b &&
+	git add b &&
+	echo "Binary file b matches" >expect &&
+	git grep bin b >actual &&
+	test_cmp expect actual &&
+	echo "b diff" >.gitattributes &&
+	echo "b:binQary" >expect &&
+	git grep bin b | nul_to_q >actual &&
+	test_cmp expect actual
+'
+
+test_done
diff --git a/t/t7010-setup.sh b/t/t7010-setup.sh
index d8a7c79..0335a9a 100755
--- a/t/t7010-setup.sh
+++ b/t/t7010-setup.sh
@@ -103,14 +103,10 @@
 	git add a &&
 	(
 		cd a/b &&
-		if git ls-files "../e/f"
-		then
-			echo Gaah, should have failed
-			exit 1
-		else
-			: happy
-		fi
-	)
+		git ls-files "../e/f"
+	)  >current &&
+	echo ../e/f >expect &&
+	test_cmp expect current
 
 '
 
diff --git a/t/t7011-skip-worktree-reading.sh b/t/t7011-skip-worktree-reading.sh
new file mode 100755
index 0000000..8f3b54d
--- /dev/null
+++ b/t/t7011-skip-worktree-reading.sh
@@ -0,0 +1,163 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Nguyễn Thái Ngọc Duy
+#
+
+test_description='skip-worktree bit test'
+
+. ./test-lib.sh
+
+cat >expect.full <<EOF
+H 1
+H 2
+H init.t
+H sub/1
+H sub/2
+EOF
+
+cat >expect.skip <<EOF
+S 1
+H 2
+H init.t
+S sub/1
+H sub/2
+EOF
+
+NULL_SHA1=e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
+
+setup_absent() {
+	test -f 1 && rm 1
+	git update-index --remove 1 &&
+	git update-index --add --cacheinfo 100644 $NULL_SHA1 1 &&
+	git update-index --skip-worktree 1
+}
+
+test_absent() {
+	echo "100644 $NULL_SHA1 0	1" > expected &&
+	git ls-files --stage 1 > result &&
+	test_cmp expected result &&
+	test ! -f 1
+}
+
+setup_dirty() {
+	git update-index --force-remove 1 &&
+	echo dirty > 1 &&
+	git update-index --add --cacheinfo 100644 $NULL_SHA1 1 &&
+	git update-index --skip-worktree 1
+}
+
+test_dirty() {
+	echo "100644 $NULL_SHA1 0	1" > expected &&
+	git ls-files --stage 1 > result &&
+	test_cmp expected result &&
+	echo dirty > expected
+	test_cmp expected 1
+}
+
+test_expect_success 'setup' '
+	test_commit init &&
+	mkdir sub &&
+	touch ./1 ./2 sub/1 sub/2 &&
+	git add 1 2 sub/1 sub/2 &&
+	git update-index --skip-worktree 1 sub/1 &&
+	git ls-files -t > result &&
+	test_cmp expect.skip result
+'
+
+test_expect_success 'update-index' '
+	setup_absent &&
+	git update-index 1 &&
+	test_absent
+'
+
+test_expect_success 'update-index' '
+	setup_dirty &&
+	git update-index 1 &&
+	test_dirty
+'
+
+test_expect_success 'update-index --remove' '
+	setup_absent &&
+	git update-index --remove 1 &&
+	test -z "$(git ls-files 1)" &&
+	test ! -f 1
+'
+
+test_expect_success 'update-index --remove' '
+	setup_dirty &&
+	git update-index --remove 1 &&
+	test -z "$(git ls-files 1)" &&
+	echo dirty > expected &&
+	test_cmp expected 1
+'
+
+test_expect_success 'ls-files --delete' '
+	setup_absent &&
+	test -z "$(git ls-files -d)"
+'
+
+test_expect_success 'ls-files --delete' '
+	setup_dirty &&
+	test -z "$(git ls-files -d)"
+'
+
+test_expect_success 'ls-files --modified' '
+	setup_absent &&
+	test -z "$(git ls-files -m)"
+'
+
+test_expect_success 'ls-files --modified' '
+	setup_dirty &&
+	test -z "$(git ls-files -m)"
+'
+
+test_expect_success 'grep with skip-worktree file' '
+	git update-index --no-skip-worktree 1 &&
+	echo test > 1 &&
+	git update-index 1 &&
+	git update-index --skip-worktree 1 &&
+	rm 1 &&
+	test "$(git grep --no-ext-grep test)" = "1:test"
+'
+
+echo ":000000 100644 $_z40 $NULL_SHA1 A	1" > expected
+test_expect_success 'diff-index does not examine skip-worktree absent entries' '
+	setup_absent &&
+	git diff-index HEAD -- 1 > result &&
+	test_cmp expected result
+'
+
+test_expect_success 'diff-index does not examine skip-worktree dirty entries' '
+	setup_dirty &&
+	git diff-index HEAD -- 1 > result &&
+	test_cmp expected result
+'
+
+test_expect_success 'diff-files does not examine skip-worktree absent entries' '
+	setup_absent &&
+	test -z "$(git diff-files -- one)"
+'
+
+test_expect_success 'diff-files does not examine skip-worktree dirty entries' '
+	setup_dirty &&
+	test -z "$(git diff-files -- one)"
+'
+
+test_expect_success 'git-rm succeeds on skip-worktree absent entries' '
+	setup_absent &&
+	git rm 1
+'
+
+test_expect_success 'commit on skip-worktree absent entries' '
+	git reset &&
+	setup_absent &&
+	test_must_fail git commit -m null 1
+'
+
+test_expect_success 'commit on skip-worktree dirty entries' '
+	git reset &&
+	setup_dirty &&
+	test_must_fail git commit -m null 1
+'
+
+test_done
diff --git a/t/t7012-skip-worktree-writing.sh b/t/t7012-skip-worktree-writing.sh
new file mode 100755
index 0000000..9ceaa40
--- /dev/null
+++ b/t/t7012-skip-worktree-writing.sh
@@ -0,0 +1,146 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Nguyễn Thái Ngọc Duy
+#
+
+test_description='test worktree writing operations when skip-worktree is used'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	test_commit init &&
+	echo modified >> init.t &&
+	touch added &&
+	git add init.t added &&
+	git commit -m "modified and added" &&
+	git tag top
+'
+
+test_expect_success 'read-tree updates worktree, absent case' '
+	git checkout -f top &&
+	git update-index --skip-worktree init.t &&
+	rm init.t &&
+	git read-tree -m -u HEAD^ &&
+	echo init > expected &&
+	test_cmp expected init.t
+'
+
+test_expect_success 'read-tree updates worktree, dirty case' '
+	git checkout -f top &&
+	git update-index --skip-worktree init.t &&
+	echo dirty >> init.t &&
+	test_must_fail git read-tree -m -u HEAD^ &&
+	grep -q dirty init.t &&
+	test "$(git ls-files -t init.t)" = "S init.t" &&
+	git update-index --no-skip-worktree init.t
+'
+
+test_expect_success 'read-tree removes worktree, absent case' '
+	git checkout -f top &&
+	git update-index --skip-worktree added &&
+	rm added &&
+	git read-tree -m -u HEAD^ &&
+	test ! -f added
+'
+
+test_expect_success 'read-tree removes worktree, dirty case' '
+	git checkout -f top &&
+	git update-index --skip-worktree added &&
+	echo dirty >> added &&
+	test_must_fail git read-tree -m -u HEAD^ &&
+	grep -q dirty added &&
+	test "$(git ls-files -t added)" = "S added" &&
+	git update-index --no-skip-worktree added
+'
+
+NULL_SHA1=e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
+
+setup_absent() {
+	test -f 1 && rm 1
+	git update-index --remove 1 &&
+	git update-index --add --cacheinfo 100644 $NULL_SHA1 1 &&
+	git update-index --skip-worktree 1
+}
+
+test_absent() {
+	echo "100644 $NULL_SHA1 0	1" > expected &&
+	git ls-files --stage 1 > result &&
+	test_cmp expected result &&
+	test ! -f 1
+}
+
+setup_dirty() {
+	git update-index --force-remove 1 &&
+	echo dirty > 1 &&
+	git update-index --add --cacheinfo 100644 $NULL_SHA1 1 &&
+	git update-index --skip-worktree 1
+}
+
+test_dirty() {
+	echo "100644 $NULL_SHA1 0	1" > expected &&
+	git ls-files --stage 1 > result &&
+	test_cmp expected result &&
+	echo dirty > expected
+	test_cmp expected 1
+}
+
+cat >expected <<EOF
+S 1
+H 2
+H init.t
+S sub/1
+H sub/2
+EOF
+
+test_expect_success 'index setup' '
+	git checkout -f init &&
+	mkdir sub &&
+	touch ./1 ./2 sub/1 sub/2 &&
+	git add 1 2 sub/1 sub/2 &&
+	git update-index --skip-worktree 1 sub/1 &&
+	git ls-files -t > result &&
+	test_cmp expected result
+'
+
+test_expect_success 'git-add ignores worktree content' '
+	setup_absent &&
+	git add 1 &&
+	test_absent
+'
+
+test_expect_success 'git-add ignores worktree content' '
+	setup_dirty &&
+	git add 1 &&
+	test_dirty
+'
+
+test_expect_success 'git-rm fails if worktree is dirty' '
+	setup_dirty &&
+	test_must_fail git rm 1 &&
+	test_dirty
+'
+
+cat >expected <<EOF
+Would remove expected
+Would remove result
+EOF
+test_expect_success 'git-clean, absent case' '
+	setup_absent &&
+	git clean -n > result &&
+	test_i18ncmp expected result
+'
+
+test_expect_success 'git-clean, dirty case' '
+	setup_dirty &&
+	git clean -n > result &&
+	test_i18ncmp expected result
+'
+
+#TODO test_expect_failure 'git-apply adds file' false
+#TODO test_expect_failure 'git-apply updates file' false
+#TODO test_expect_failure 'git-apply removes file' false
+#TODO test_expect_failure 'git-mv to skip-worktree' false
+#TODO test_expect_failure 'git-mv from skip-worktree' false
+#TODO test_expect_failure 'git-checkout' false
+
+test_done
diff --git a/t/t7060-wtstatus.sh b/t/t7060-wtstatus.sh
new file mode 100755
index 0000000..f4f38a5
--- /dev/null
+++ b/t/t7060-wtstatus.sh
@@ -0,0 +1,217 @@
+#!/bin/sh
+
+test_description='basic work tree status reporting'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+	test_commit A &&
+	test_commit B oneside added &&
+	git checkout A^0 &&
+	test_commit C oneside created
+'
+
+test_expect_success 'A/A conflict' '
+	git checkout B^0 &&
+	test_must_fail git merge C
+'
+
+test_expect_success 'Report path with conflict' '
+	git diff --cached --name-status >actual &&
+	echo "U	oneside" >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'Report new path with conflict' '
+	git diff --cached --name-status HEAD^ >actual &&
+	echo "U	oneside" >expect &&
+	test_cmp expect actual
+'
+
+cat >expect <<EOF
+# On branch side
+# You have unmerged paths.
+#   (fix conflicts and run "git commit")
+#
+# Unmerged paths:
+#   (use "git add/rm <file>..." as appropriate to mark resolution)
+#
+#	deleted by us:      foo
+#
+no changes added to commit (use "git add" and/or "git commit -a")
+EOF
+
+test_expect_success 'M/D conflict does not segfault' '
+	mkdir mdconflict &&
+	(
+		cd mdconflict &&
+		git init &&
+		test_commit initial foo "" &&
+		test_commit modify foo foo &&
+		git checkout -b side HEAD^ &&
+		git rm foo &&
+		git commit -m delete &&
+		test_must_fail git merge master &&
+		test_must_fail git commit --dry-run >../actual &&
+		test_i18ncmp ../expect ../actual &&
+		git status >../actual &&
+		test_i18ncmp ../expect ../actual
+	)
+'
+
+test_expect_success 'rename & unmerged setup' '
+	git rm -f -r . &&
+	cat "$TEST_DIRECTORY/README" >ONE &&
+	git add ONE &&
+	test_tick &&
+	git commit -m "One commit with ONE" &&
+
+	echo Modified >TWO &&
+	cat ONE >>TWO &&
+	cat ONE >>THREE &&
+	git add TWO THREE &&
+	sha1=$(git rev-parse :ONE) &&
+	git rm --cached ONE &&
+	(
+		echo "100644 $sha1 1	ONE" &&
+		echo "100644 $sha1 2	ONE" &&
+		echo "100644 $sha1 3	ONE"
+	) | git update-index --index-info &&
+	echo Further >>THREE
+'
+
+test_expect_success 'rename & unmerged status' '
+	git status -suno >actual &&
+	cat >expect <<-EOF &&
+	UU ONE
+	AM THREE
+	A  TWO
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'git diff-index --cached shows 2 added + 1 unmerged' '
+	cat >expected <<-EOF &&
+	U	ONE
+	A	THREE
+	A	TWO
+	EOF
+	git diff-index --cached --name-status HEAD >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'git diff-index --cached -M shows 2 added + 1 unmerged' '
+	cat >expected <<-EOF &&
+	U	ONE
+	A	THREE
+	A	TWO
+	EOF
+	git diff-index --cached --name-status HEAD >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'git diff-index --cached -C shows 2 copies + 1 unmerged' '
+	cat >expected <<-EOF &&
+	U	ONE
+	C	ONE	THREE
+	C	ONE	TWO
+	EOF
+	git diff-index --cached -C --name-status HEAD |
+	sed "s/^C[0-9]*/C/g" >actual &&
+	test_cmp expected actual
+'
+
+
+test_expect_success 'status when conflicts with add and rm advice (deleted by them)' '
+	git reset --hard &&
+	git checkout master &&
+	test_commit init main.txt init &&
+	git checkout -b second_branch &&
+	git rm main.txt &&
+	git commit -m "main.txt deleted on second_branch" &&
+	test_commit second conflict.txt second &&
+	git checkout master &&
+	test_commit on_second main.txt on_second &&
+	test_commit master conflict.txt master &&
+	test_must_fail git merge second_branch &&
+	cat >expected <<-\EOF &&
+	# On branch master
+	# You have unmerged paths.
+	#   (fix conflicts and run "git commit")
+	#
+	# Unmerged paths:
+	#   (use "git add/rm <file>..." as appropriate to mark resolution)
+	#
+	#	both added:         conflict.txt
+	#	deleted by them:    main.txt
+	#
+	no changes added to commit (use "git add" and/or "git commit -a")
+	EOF
+	git status --untracked-files=no >actual &&
+	test_i18ncmp expected actual
+'
+
+
+test_expect_success 'prepare for conflicts' '
+	git reset --hard &&
+	git checkout -b conflict &&
+	test_commit one main.txt one &&
+	git branch conflict_second &&
+	git mv main.txt sub_master.txt &&
+	git commit -m "main.txt renamed in sub_master.txt" &&
+	git checkout conflict_second &&
+	git mv main.txt sub_second.txt &&
+	git commit -m "main.txt renamed in sub_second.txt"
+'
+
+
+test_expect_success 'status when conflicts with add and rm advice (both deleted)' '
+	test_must_fail git merge conflict &&
+	cat >expected <<-\EOF &&
+	# On branch conflict_second
+	# You have unmerged paths.
+	#   (fix conflicts and run "git commit")
+	#
+	# Unmerged paths:
+	#   (use "git add/rm <file>..." as appropriate to mark resolution)
+	#
+	#	both deleted:       main.txt
+	#	added by them:      sub_master.txt
+	#	added by us:        sub_second.txt
+	#
+	no changes added to commit (use "git add" and/or "git commit -a")
+	EOF
+	git status --untracked-files=no >actual &&
+	test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status when conflicts with only rm advice (both deleted)' '
+	git reset --hard conflict_second &&
+	test_must_fail git merge conflict &&
+	git add sub_master.txt &&
+	git add sub_second.txt &&
+	cat >expected <<-\EOF &&
+	# On branch conflict_second
+	# You have unmerged paths.
+	#   (fix conflicts and run "git commit")
+	#
+	# Changes to be committed:
+	#
+	#	new file:   sub_master.txt
+	#
+	# Unmerged paths:
+	#   (use "git rm <file>..." to mark resolution)
+	#
+	#	both deleted:       main.txt
+	#
+	# Untracked files not listed (use -u option to show untracked files)
+	EOF
+	git status --untracked-files=no >actual &&
+	test_i18ncmp expected actual &&
+	git reset --hard &&
+	git checkout master
+'
+
+
+test_done
diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh
index e637c7d..b096dc8 100755
--- a/t/t7102-reset.sh
+++ b/t/t7102-reset.sh
@@ -139,19 +139,19 @@
 test_expect_success \
 	'resetting to HEAD with no changes should succeed and do nothing' '
 	git reset --hard &&
-		check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+		check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc &&
 	git reset --hard HEAD &&
-		check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+		check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc &&
 	git reset --soft &&
-		check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+		check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc &&
 	git reset --soft HEAD &&
-		check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+		check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc &&
 	git reset --mixed &&
-		check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+		check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc &&
 	git reset --mixed HEAD &&
-		check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+		check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc &&
 	git reset &&
-		check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+		check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc &&
 	git reset HEAD &&
 		check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
 '
@@ -419,13 +419,29 @@
 '
 
 cat > expect << EOF
-file2: locally modified
+Unstaged changes after reset:
+M	file2
 EOF
 
 test_expect_success '--mixed refreshes the index' '
 	echo 123 >> file2 &&
 	git reset --mixed HEAD > output &&
-	test_cmp expect output
+	test_i18ncmp expect output
+'
+
+test_expect_success 'resetting specific path that is unmerged' '
+	git rm --cached file2 &&
+	F1=$(git rev-parse HEAD:file1) &&
+	F2=$(git rev-parse HEAD:file2) &&
+	F3=$(git rev-parse HEAD:secondfile) &&
+	{
+		echo "100644 $F1 1	file2" &&
+		echo "100644 $F2 2	file2" &&
+		echo "100644 $F3 3	file2"
+	} | git update-index --index-info &&
+	git ls-files -u &&
+	test_must_fail git reset HEAD file2 &&
+	git diff-index --exit-code --cached HEAD
 '
 
 test_expect_success 'disambiguation (1)' '
diff --git a/t/t7103-reset-bare.sh b/t/t7103-reset-bare.sh
index 42bf518..1eef93c 100755
--- a/t/t7103-reset-bare.sh
+++ b/t/t7103-reset-bare.sh
@@ -11,16 +11,57 @@
 	git commit -a -m two
 '
 
+test_expect_success '"hard" reset requires a worktree' '
+	(cd .git &&
+	 test_must_fail git reset --hard)
+'
+
+test_expect_success '"merge" reset requires a worktree' '
+	(cd .git &&
+	 test_must_fail git reset --merge)
+'
+
+test_expect_success '"keep" reset requires a worktree' '
+	(cd .git &&
+	 test_must_fail git reset --keep)
+'
+
+test_expect_success '"mixed" reset is ok' '
+	(cd .git && git reset)
+'
+
+test_expect_success '"soft" reset is ok' '
+	(cd .git && git reset --soft)
+'
+
+test_expect_success 'hard reset works with GIT_WORK_TREE' '
+	mkdir worktree &&
+	GIT_WORK_TREE=$PWD/worktree GIT_DIR=$PWD/.git git reset --hard &&
+	test_cmp file worktree/file
+'
+
 test_expect_success 'setup bare' '
 	git clone --bare . bare.git &&
 	cd bare.git
 '
 
-test_expect_success 'hard reset is not allowed' '
-	test_must_fail  git reset --hard HEAD^
+test_expect_success '"hard" reset is not allowed in bare' '
+	test_must_fail git reset --hard HEAD^
 '
 
-test_expect_success 'soft reset is allowed' '
+test_expect_success '"merge" reset is not allowed in bare' '
+	test_must_fail git reset --merge HEAD^
+'
+
+test_expect_success '"keep" reset is not allowed in bare' '
+	test_must_fail git reset --keep HEAD^
+'
+
+test_expect_success '"mixed" reset is not allowed in bare' '
+	test_must_fail git reset --mixed HEAD^
+'
+
+test_expect_success '"soft" reset is allowed in bare' '
 	git reset --soft HEAD^ &&
 	test "`git show --pretty=format:%s | head -n 1`" = "one"
 '
diff --git a/t/t7105-reset-patch.sh b/t/t7105-reset-patch.sh
new file mode 100755
index 0000000..95fab20
--- /dev/null
+++ b/t/t7105-reset-patch.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+test_description='git reset --patch'
+. ./lib-patch-mode.sh
+
+test_expect_success PERL 'setup' '
+	mkdir dir &&
+	echo parent > dir/foo &&
+	echo dummy > bar &&
+	git add dir &&
+	git commit -m initial &&
+	test_tick &&
+	test_commit second dir/foo head &&
+	set_and_save_state bar bar_work bar_index &&
+	save_head
+'
+
+# note: bar sorts before foo, so the first 'n' is always to skip 'bar'
+
+test_expect_success PERL 'saying "n" does nothing' '
+	set_and_save_state dir/foo work work &&
+	(echo n; echo n) | git reset -p &&
+	verify_saved_state dir/foo &&
+	verify_saved_state bar
+'
+
+test_expect_success PERL 'git reset -p' '
+	(echo n; echo y) | git reset -p &&
+	verify_state dir/foo work head &&
+	verify_saved_state bar
+'
+
+test_expect_success PERL 'git reset -p HEAD^' '
+	(echo n; echo y) | git reset -p HEAD^ &&
+	verify_state dir/foo work parent &&
+	verify_saved_state bar
+'
+
+# The idea in the rest is that bar sorts first, so we always say 'y'
+# first and if the path limiter fails it'll apply to bar instead of
+# dir/foo.  There's always an extra 'n' to reject edits to dir/foo in
+# the failure case (and thus get out of the loop).
+
+test_expect_success PERL 'git reset -p dir' '
+	set_state dir/foo work work &&
+	(echo y; echo n) | git reset -p dir &&
+	verify_state dir/foo work head &&
+	verify_saved_state bar
+'
+
+test_expect_success PERL 'git reset -p -- foo (inside dir)' '
+	set_state dir/foo work work &&
+	(echo y; echo n) | (cd dir && git reset -p -- foo) &&
+	verify_state dir/foo work head &&
+	verify_saved_state bar
+'
+
+test_expect_success PERL 'git reset -p HEAD^ -- dir' '
+	(echo y; echo n) | git reset -p HEAD^ -- dir &&
+	verify_state dir/foo work parent &&
+	verify_saved_state bar
+'
+
+test_expect_success PERL 'none of this moved HEAD' '
+	verify_saved_head
+'
+
+
+test_done
diff --git a/t/t7110-reset-merge.sh b/t/t7110-reset-merge.sh
new file mode 100755
index 0000000..a82a07a
--- /dev/null
+++ b/t/t7110-reset-merge.sh
@@ -0,0 +1,295 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Christian Couder
+#
+
+test_description='Tests for "git reset" with "--merge" and "--keep" options'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+    for i in 1 2 3; do echo line $i; done >file1 &&
+    cat file1 >file2 &&
+    git add file1 file2 &&
+    test_tick &&
+    git commit -m "Initial commit" &&
+    git tag initial &&
+    echo line 4 >>file1 &&
+    cat file1 >file2 &&
+    test_tick &&
+    git commit -m "add line 4 to file1" file1 &&
+    git tag second
+'
+
+# The next test will test the following:
+#
+#           working index HEAD target         working index HEAD
+#           ----------------------------------------------------
+# file1:     C       C     C    D     --merge  D       D     D
+# file2:     C       D     D    D     --merge  C       D     D
+test_expect_success 'reset --merge is ok with changes in file it does not touch' '
+    git reset --merge HEAD^ &&
+    ! grep 4 file1 &&
+    grep 4 file2 &&
+    test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
+    test -z "$(git diff --cached)"
+'
+
+test_expect_success 'reset --merge is ok when switching back' '
+    git reset --merge second &&
+    grep 4 file1 &&
+    grep 4 file2 &&
+    test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
+    test -z "$(git diff --cached)"
+'
+
+# The next test will test the following:
+#
+#           working index HEAD target         working index HEAD
+#           ----------------------------------------------------
+# file1:     C       C     C    D     --keep   D       D     D
+# file2:     C       D     D    D     --keep   C       D     D
+test_expect_success 'reset --keep is ok with changes in file it does not touch' '
+    git reset --hard second &&
+    cat file1 >file2 &&
+    git reset --keep HEAD^ &&
+    ! grep 4 file1 &&
+    grep 4 file2 &&
+    test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
+    test -z "$(git diff --cached)"
+'
+
+test_expect_success 'reset --keep is ok when switching back' '
+    git reset --keep second &&
+    grep 4 file1 &&
+    grep 4 file2 &&
+    test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
+    test -z "$(git diff --cached)"
+'
+
+# The next test will test the following:
+#
+#           working index HEAD target         working index HEAD
+#           ----------------------------------------------------
+# file1:     B       B     C    D     --merge  D       D     D
+# file2:     C       D     D    D     --merge  C       D     D
+test_expect_success 'reset --merge discards changes added to index (1)' '
+    git reset --hard second &&
+    cat file1 >file2 &&
+    echo "line 5" >> file1 &&
+    git add file1 &&
+    git reset --merge HEAD^ &&
+    ! grep 4 file1 &&
+    ! grep 5 file1 &&
+    grep 4 file2 &&
+    test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
+    test -z "$(git diff --cached)"
+'
+
+test_expect_success 'reset --merge is ok again when switching back (1)' '
+    git reset --hard initial &&
+    echo "line 5" >> file2 &&
+    git add file2 &&
+    git reset --merge second &&
+    ! grep 4 file2 &&
+    ! grep 5 file1 &&
+    grep 4 file1 &&
+    test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
+    test -z "$(git diff --cached)"
+'
+
+# The next test will test the following:
+#
+#           working index HEAD target         working index HEAD
+#           ----------------------------------------------------
+# file1:     B       B     C    D     --keep   (disallowed)
+test_expect_success 'reset --keep fails with changes in index in files it touches' '
+    git reset --hard second &&
+    echo "line 5" >> file1 &&
+    git add file1 &&
+    test_must_fail git reset --keep HEAD^
+'
+
+# The next test will test the following:
+#
+#           working index HEAD target         working index HEAD
+#           ----------------------------------------------------
+# file1:     C       C     C    D     --merge  D       D     D
+# file2:     C       C     D    D     --merge  D       D     D
+test_expect_success 'reset --merge discards changes added to index (2)' '
+    git reset --hard second &&
+    echo "line 4" >> file2 &&
+    git add file2 &&
+    git reset --merge HEAD^ &&
+    ! grep 4 file2 &&
+    test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
+    test -z "$(git diff)" &&
+    test -z "$(git diff --cached)"
+'
+
+test_expect_success 'reset --merge is ok again when switching back (2)' '
+    git reset --hard initial &&
+    git reset --merge second &&
+    ! grep 4 file2 &&
+    grep 4 file1 &&
+    test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
+    test -z "$(git diff --cached)"
+'
+
+# The next test will test the following:
+#
+#           working index HEAD target         working index HEAD
+#           ----------------------------------------------------
+# file1:     C       C     C    D     --keep   D       D     D
+# file2:     C       C     D    D     --keep   C       D     D
+test_expect_success 'reset --keep keeps changes it does not touch' '
+    git reset --hard second &&
+    echo "line 4" >> file2 &&
+    git add file2 &&
+    git reset --keep HEAD^ &&
+    grep 4 file2 &&
+    test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
+    test -z "$(git diff --cached)"
+'
+
+test_expect_success 'reset --keep keeps changes when switching back' '
+    git reset --keep second &&
+    grep 4 file2 &&
+    grep 4 file1 &&
+    test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
+    test -z "$(git diff --cached)"
+'
+
+# The next test will test the following:
+#
+#           working index HEAD target         working index HEAD
+#           ----------------------------------------------------
+# file1:     A       B     B    C     --merge  (disallowed)
+test_expect_success 'reset --merge fails with changes in file it touches' '
+    git reset --hard second &&
+    echo "line 5" >> file1 &&
+    test_tick &&
+    git commit -m "add line 5" file1 &&
+    sed -e "s/line 1/changed line 1/" <file1 >file3 &&
+    mv file3 file1 &&
+    test_must_fail git reset --merge HEAD^ 2>err.log &&
+    grep file1 err.log | grep "not uptodate"
+'
+
+# The next test will test the following:
+#
+#           working index HEAD target         working index HEAD
+#           ----------------------------------------------------
+# file1:     A       B     B    C     --keep   (disallowed)
+test_expect_success 'reset --keep fails with changes in file it touches' '
+    git reset --hard second &&
+    echo "line 5" >> file1 &&
+    test_tick &&
+    git commit -m "add line 5" file1 &&
+    sed -e "s/line 1/changed line 1/" <file1 >file3 &&
+    mv file3 file1 &&
+    test_must_fail git reset --keep HEAD^ 2>err.log &&
+    grep file1 err.log | grep "not uptodate"
+'
+
+test_expect_success 'setup 3 different branches' '
+    git reset --hard second &&
+    git branch branch1 &&
+    git branch branch2 &&
+    git branch branch3 &&
+    git checkout branch1 &&
+    echo "line 5 in branch1" >> file1 &&
+    test_tick &&
+    git commit -a -m "change in branch1" &&
+    git checkout branch2 &&
+    echo "line 5 in branch2" >> file1 &&
+    test_tick &&
+    git commit -a -m "change in branch2" &&
+    git tag third &&
+    git checkout branch3 &&
+    echo a new file >file3 &&
+    rm -f file1 &&
+    git add file3 &&
+    test_tick &&
+    git commit -a -m "change in branch3"
+'
+
+# The next test will test the following:
+#
+#           working index HEAD target         working index HEAD
+#           ----------------------------------------------------
+# file1:     X       U     B    C     --merge  C       C     C
+test_expect_success '"reset --merge HEAD^" is ok with pending merge' '
+    git checkout third &&
+    test_must_fail git merge branch1 &&
+    git reset --merge HEAD^ &&
+    test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
+    test -z "$(git diff --cached)" &&
+    test -z "$(git diff)"
+'
+
+# The next test will test the following:
+#
+#           working index HEAD target         working index HEAD
+#           ----------------------------------------------------
+# file1:     X       U     B    C     --keep   (disallowed)
+test_expect_success '"reset --keep HEAD^" fails with pending merge' '
+    git reset --hard third &&
+    test_must_fail git merge branch1 &&
+    test_must_fail git reset --keep HEAD^ 2>err.log &&
+    test_i18ngrep "middle of a merge" err.log
+'
+
+# The next test will test the following:
+#
+#           working index HEAD target         working index HEAD
+#           ----------------------------------------------------
+# file1:     X       U     B    B     --merge  B       B     B
+test_expect_success '"reset --merge HEAD" is ok with pending merge' '
+    git reset --hard third &&
+    test_must_fail git merge branch1 &&
+    git reset --merge HEAD &&
+    test "$(git rev-parse HEAD)" = "$(git rev-parse third)" &&
+    test -z "$(git diff --cached)" &&
+    test -z "$(git diff)"
+'
+
+# The next test will test the following:
+#
+#           working index HEAD target         working index HEAD
+#           ----------------------------------------------------
+# file1:     X       U     B    B     --keep   (disallowed)
+test_expect_success '"reset --keep HEAD" fails with pending merge' '
+    git reset --hard third &&
+    test_must_fail git merge branch1 &&
+    test_must_fail git reset --keep HEAD 2>err.log &&
+    test_i18ngrep "middle of a merge" err.log
+'
+
+test_expect_success '--merge is ok with added/deleted merge' '
+    git reset --hard third &&
+    rm -f file2 &&
+    test_must_fail git merge branch3 &&
+    ! test -f file2 &&
+    test -f file3 &&
+    git diff --exit-code file3 &&
+    git diff --exit-code branch3 file3 &&
+    git reset --merge HEAD &&
+    ! test -f file3 &&
+    ! test -f file2 &&
+    git diff --exit-code --cached
+'
+
+test_expect_success '--keep fails with added/deleted merge' '
+    git reset --hard third &&
+    rm -f file2 &&
+    test_must_fail git merge branch3 &&
+    ! test -f file2 &&
+    test -f file3 &&
+    git diff --exit-code file3 &&
+    git diff --exit-code branch3 file3 &&
+    test_must_fail git reset --keep HEAD 2>err.log &&
+    test_i18ngrep "middle of a merge" err.log
+'
+
+test_done
diff --git a/t/t7111-reset-table.sh b/t/t7111-reset-table.sh
new file mode 100755
index 0000000..ce421ad
--- /dev/null
+++ b/t/t7111-reset-table.sh
@@ -0,0 +1,121 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Christian Couder
+#
+
+test_description='Tests to check that "reset" options follow a known table'
+
+. ./test-lib.sh
+
+
+test_expect_success 'creating initial commits' '
+    test_commit E file1 &&
+    test_commit D file1 &&
+    test_commit C file1
+'
+
+while read W1 I1 H1 T opt W2 I2 H2
+do
+    test_expect_success "check: $W1 $I1 $H1 $T --$opt $W2 $I2 $H2" '
+	git reset --hard C &&
+	if test "$I1" != "$H1"
+	then
+	    echo "$I1" >file1 &&
+	    git add file1
+	fi &&
+	if test "$W1" != "$I1"
+	then
+	    echo "$W1" >file1
+	fi &&
+	if test "$W2" != "XXXXX"
+	then
+	    git reset --$opt $T &&
+	    test "$(cat file1)" = "$W2" &&
+	    git checkout-index -f -- file1 &&
+	    test "$(cat file1)" = "$I2" &&
+	    git checkout -f HEAD -- file1 &&
+	    test "$(cat file1)" = "$H2"
+	else
+	    test_must_fail git reset --$opt $T
+	fi
+    '
+done <<\EOF
+A B C D soft   A B D
+A B C D mixed  A D D
+A B C D hard   D D D
+A B C D merge  XXXXX
+A B C D keep   XXXXX
+A B C C soft   A B C
+A B C C mixed  A C C
+A B C C hard   C C C
+A B C C merge  XXXXX
+A B C C keep   A C C
+B B C D soft   B B D
+B B C D mixed  B D D
+B B C D hard   D D D
+B B C D merge  D D D
+B B C D keep   XXXXX
+B B C C soft   B B C
+B B C C mixed  B C C
+B B C C hard   C C C
+B B C C merge  C C C
+B B C C keep   B C C
+B C C D soft   B C D
+B C C D mixed  B D D
+B C C D hard   D D D
+B C C D merge  XXXXX
+B C C D keep   XXXXX
+B C C C soft   B C C
+B C C C mixed  B C C
+B C C C hard   C C C
+B C C C merge  B C C
+B C C C keep   B C C
+EOF
+
+test_expect_success 'setting up branches to test with unmerged entries' '
+    git reset --hard C &&
+    git branch branch1 &&
+    git branch branch2 &&
+    git checkout branch1 &&
+    test_commit B1 file1 &&
+    git checkout branch2 &&
+    test_commit B file1
+'
+
+while read W1 I1 H1 T opt W2 I2 H2
+do
+    test_expect_success "check: $W1 $I1 $H1 $T --$opt $W2 $I2 $H2" '
+	git reset --hard B &&
+	test_must_fail git merge branch1 &&
+	cat file1 >X_file1 &&
+	if test "$W2" != "XXXXX"
+	then
+	    git reset --$opt $T &&
+	    if test "$W2" = "X"
+	    then
+		test_cmp file1 X_file1
+	    else
+		test "$(cat file1)" = "$W2"
+	    fi &&
+	    git checkout-index -f -- file1 &&
+	    test "$(cat file1)" = "$I2" &&
+	    git checkout -f HEAD -- file1 &&
+	    test "$(cat file1)" = "$H2"
+	else
+	    test_must_fail git reset --$opt $T
+	fi
+    '
+done <<\EOF
+X U B C soft   XXXXX
+X U B C mixed  X C C
+X U B C hard   C C C
+X U B C merge  C C C
+X U B C keep   XXXXX
+X U B B soft   XXXXX
+X U B B mixed  X B B
+X U B B hard   B B B
+X U B B merge  B B B
+X U B B keep   XXXXX
+EOF
+
+test_done
diff --git a/t/t7201-co.sh b/t/t7201-co.sh
index ebfd34d..be9672e 100755
--- a/t/t7201-co.sh
+++ b/t/t7201-co.sh
@@ -11,10 +11,12 @@
   ! [master] Initial A one, A two
    * [renamer] Renamer R one->uno, M two
     ! [side] Side M one, D two, A three
-  ---
-    + [side] Side M one, D two, A three
-   *  [renamer] Renamer R one->uno, M two
-  +*+ [master] Initial A one, A two
+     ! [simple] Simple D one, M two
+  ----
+     + [simple] Simple D one, M two
+    +  [side] Side M one, D two, A three
+   *   [renamer] Renamer R one->uno, M two
+  +*++ [master] Initial A one, A two
 
 '
 
@@ -52,6 +54,11 @@
 	git update-index --add --remove one two three &&
 	git commit -m "Side M one, D two, A three" &&
 
+	git checkout -b simple master &&
+	rm -f one &&
+	fill a c e > two &&
+	git commit -a -m "Simple D one, M two" &&
+
 	git checkout master
 '
 
@@ -166,19 +173,81 @@
 	! test -s current
 '
 
-test_expect_success 'checkout to detach HEAD' '
+test_expect_success 'format of merge conflict from checkout -m' '
 
+	git checkout -f master && git clean -f &&
+
+	fill b d > two &&
+	git checkout -m simple &&
+
+	git ls-files >current &&
+	fill same two two two >expect &&
+	test_cmp current expect &&
+
+	cat <<-EOF >expect &&
+	<<<<<<< simple
+	a
+	c
+	e
+	=======
+	b
+	d
+	>>>>>>> local
+	EOF
+	test_cmp two expect
+'
+
+test_expect_success 'checkout --merge --conflict=diff3 <branch>' '
+
+	git checkout -f master && git reset --hard && git clean -f &&
+
+	fill b d > two &&
+	git checkout --merge --conflict=diff3 simple &&
+
+	cat <<-EOF >expect &&
+	<<<<<<< simple
+	a
+	c
+	e
+	||||||| master
+	a
+	b
+	c
+	d
+	e
+	=======
+	b
+	d
+	>>>>>>> local
+	EOF
+	test_cmp two expect
+'
+
+test_expect_success 'checkout to detach HEAD (with advice declined)' '
+
+	git config advice.detachedHead false &&
 	git checkout -f renamer && git clean -f &&
 	git checkout renamer^ 2>messages &&
-	(cat >messages.expect <<EOF
-Note: moving to '\''renamer^'\'' which isn'\''t a local branch
-If you want to create a new branch from this checkout, you may do so
-(now or later) by using -b with the checkout command again. Example:
-  git checkout -b <new_branch_name>
-HEAD is now at 7329388... Initial A one, A two
-EOF
-) &&
-	test_cmp messages.expect messages &&
+	test_i18ngrep "HEAD is now at 7329388" messages &&
+	test_line_count = 1 messages &&
+	H=$(git rev-parse --verify HEAD) &&
+	M=$(git show-ref -s --verify refs/heads/master) &&
+	test "z$H" = "z$M" &&
+	if git symbolic-ref HEAD >/dev/null 2>&1
+	then
+		echo "OOPS, HEAD is still symbolic???"
+		false
+	else
+		: happy
+	fi
+'
+
+test_expect_success 'checkout to detach HEAD' '
+	git config advice.detachedHead true &&
+	git checkout -f renamer && git clean -f &&
+	git checkout renamer^ 2>messages &&
+	test_i18ngrep "HEAD is now at 7329388" messages &&
+	test_line_count -gt 1 messages &&
 	H=$(git rev-parse --verify HEAD) &&
 	M=$(git show-ref -s --verify refs/heads/master) &&
 	test "z$H" = "z$M" &&
@@ -339,6 +408,15 @@
     test "z$(git rev-parse master^0)" = "z$(git rev-parse HEAD)"
 '
 
+test_expect_success 'checkout w/--track from tag fails' '
+    git checkout master^0 &&
+    test_must_fail git symbolic-ref HEAD &&
+    test_must_fail git checkout --track -b track frotz &&
+    test_must_fail git rev-parse --verify track &&
+    test_must_fail git symbolic-ref HEAD &&
+    test "z$(git rev-parse master^0)" = "z$(git rev-parse HEAD)"
+'
+
 test_expect_success 'detach a symbolic link HEAD' '
     git checkout master &&
     git config --bool core.prefersymlinkrefs yes &&
@@ -354,7 +432,6 @@
 test_expect_success \
     'checkout with --track fakes a sensible -b <name>' '
     git update-ref refs/remotes/origin/koala/bear renamer &&
-    git update-ref refs/new/koala/bear renamer &&
 
     git checkout --track origin/koala/bear &&
     test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
@@ -370,12 +447,6 @@
 
     git checkout --track remotes/origin/koala/bear &&
     test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
-    test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
-
-    git checkout master && git branch -D koala/bear &&
-
-    git checkout --track refs/new/koala/bear &&
-    test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
     test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)"
 '
 
@@ -469,7 +540,7 @@
 	(
 		echo "<<<<<<< ours"
 		echo ourside
-		echo "|||||||"
+		echo "||||||| base"
 		echo original
 		echo "======="
 		echo theirside
@@ -513,7 +584,7 @@
 	(
 		echo "<<<<<<< ours"
 		echo ourside
-		echo "|||||||"
+		echo "||||||| base"
 		echo original
 		echo "======="
 		echo theirside
@@ -542,4 +613,61 @@
 	! grep "^Previous HEAD" error.log
 '
 
+(
+ echo "#!$SHELL_PATH"
+ cat <<\EOF
+O=$1 A=$2 B=$3
+cat "$A" >.tmp
+exec >"$A"
+echo '<<<<<<< filfre-theirs'
+cat "$B"
+echo '||||||| filfre-common'
+cat "$O"
+echo '======='
+cat ".tmp"
+echo '>>>>>>> filfre-ours'
+rm -f .tmp
+exit 1
+EOF
+) >filfre.sh
+chmod +x filfre.sh
+
+test_expect_success 'custom merge driver with checkout -m' '
+	git reset --hard &&
+
+	git config merge.filfre.driver "./filfre.sh %O %A %B" &&
+	git config merge.filfre.name "Feel-free merge driver" &&
+	git config merge.filfre.recursive binary &&
+	echo "arm merge=filfre" >.gitattributes &&
+
+	git checkout -b left &&
+	echo neutral >arm &&
+	git add arm .gitattributes &&
+	test_tick &&
+	git commit -m neutral &&
+	git branch right &&
+
+	echo left >arm &&
+	test_tick &&
+	git commit -a -m left &&
+	git checkout right &&
+
+	echo right >arm &&
+	test_tick &&
+	git commit -a -m right &&
+
+	test_must_fail git merge left &&
+	(
+		for t in filfre-common left right
+		do
+			grep $t arm || exit 1
+		done
+		exit 0
+	) &&
+
+	mv arm expect &&
+	git checkout -m arm &&
+	test_cmp expect arm
+'
+
 test_done
diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh
index 929d5d4..ccfb54d 100755
--- a/t/t7300-clean.sh
+++ b/t/t7300-clean.sh
@@ -22,6 +22,25 @@
 
 '
 
+test_expect_success 'git clean with skip-worktree .gitignore' '
+	git update-index --skip-worktree .gitignore &&
+	rm .gitignore &&
+	mkdir -p build docs &&
+	touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+	git clean &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test ! -f a.out &&
+	test ! -f src/part3.c &&
+	test -f docs/manual.txt &&
+	test -f obj.o &&
+	test -f build/lib.so &&
+	git update-index --no-skip-worktree .gitignore &&
+	git checkout .gitignore
+'
+
 test_expect_success 'git clean' '
 
 	mkdir -p build docs &&
@@ -91,7 +110,7 @@
 
 '
 
-test_expect_success 'git clean with relative prefix' '
+test_expect_success C_LOCALE_OUTPUT 'git clean with relative prefix' '
 
 	mkdir -p build docs &&
 	touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
@@ -106,7 +125,7 @@
 	}
 '
 
-test_expect_success 'git clean with absolute path' '
+test_expect_success C_LOCALE_OUTPUT 'git clean with absolute path' '
 
 	mkdir -p build docs &&
 	touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
@@ -160,11 +179,11 @@
 
 '
 
-test_expect_success 'git clean symbolic link' '
+test_expect_success SYMLINKS 'git clean symbolic link' '
 
 	mkdir -p build docs &&
 	touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
-	ln -s docs/manual.txt src/part4.c
+	ln -s docs/manual.txt src/part4.c &&
 	git clean &&
 	test -f Makefile &&
 	test -f README &&
@@ -358,7 +377,7 @@
 
 '
 
-test_expect_success 'core.excludesfile' '
+test_expect_success C_LOCALE_OUTPUT 'core.excludesfile' '
 
 	echo excludes >excludes &&
 	echo included >included &&
@@ -369,15 +388,93 @@
 
 '
 
-test_expect_success 'removal failure' '
+test_expect_success SANITY 'removal failure' '
 
 	mkdir foo &&
 	touch foo/bar &&
 	(exec <foo/bar &&
 	 chmod 0 foo &&
-	 test_must_fail git clean -f -d)
-
+	 test_must_fail git clean -f -d &&
+	 chmod 755 foo)
 '
-chmod 755 foo
+
+test_expect_success 'nested git work tree' '
+	rm -fr foo bar baz &&
+	mkdir -p foo bar baz/boo &&
+	(
+		cd foo &&
+		git init &&
+		>hello.world
+		git add . &&
+		git commit -a -m nested
+	) &&
+	(
+		cd bar &&
+		>goodbye.people
+	) &&
+	(
+		cd baz/boo &&
+		git init &&
+		>deeper.world
+		git add . &&
+		git commit -a -m deeply.nested
+	) &&
+	git clean -f -d &&
+	test -f foo/.git/index &&
+	test -f foo/hello.world &&
+	test -f baz/boo/.git/index &&
+	test -f baz/boo/deeper.world &&
+	! test -d bar
+'
+
+test_expect_success 'force removal of nested git work tree' '
+	rm -fr foo bar baz &&
+	mkdir -p foo bar baz/boo &&
+	(
+		cd foo &&
+		git init &&
+		>hello.world
+		git add . &&
+		git commit -a -m nested
+	) &&
+	(
+		cd bar &&
+		>goodbye.people
+	) &&
+	(
+		cd baz/boo &&
+		git init &&
+		>deeper.world
+		git add . &&
+		git commit -a -m deeply.nested
+	) &&
+	git clean -f -f -d &&
+	! test -d foo &&
+	! test -d bar &&
+	! test -d baz
+'
+
+test_expect_success 'git clean -e' '
+	rm -fr repo &&
+	mkdir repo &&
+	(
+		cd repo &&
+		git init &&
+		touch known 1 2 3 &&
+		git add known &&
+		git clean -f -e 1 -e 2 &&
+		test -e 1 &&
+		test -e 2 &&
+		! (test -e 3) &&
+		test -e known
+	)
+'
+
+test_expect_success SANITY 'git clean -d with an unreadable empty directory' '
+	mkdir foo &&
+	chmod a= foo &&
+	git clean -dfx foo &&
+	! test -d foo
+'
 
 test_done
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index 0f2ccc6..c73bec9 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -11,226 +11,330 @@
 
 . ./test-lib.sh
 
-#
-# Test setup:
-#  -create a repository in directory init
-#  -add a couple of files
-#  -add directory init to 'superproject', this creates a DIRLINK entry
-#  -add a couple of regular files to enable testing of submodule filtering
-#  -mv init subrepo
-#  -add an entry to .gitmodules for submodule 'example'
-#
-test_expect_success 'Prepare submodule testing' '
-	: > t &&
+test_expect_success 'setup - initial commit' '
+	>t &&
 	git add t &&
 	git commit -m "initial commit" &&
-	git branch initial HEAD &&
-	mkdir init &&
-	cd init &&
-	git init &&
-	echo a >a &&
-	git add a &&
-	git commit -m "submodule commit 1" &&
-	git tag -a -m "rev-1" rev-1 &&
-	rev1=$(git rev-parse HEAD) &&
-	if test -z "$rev1"
-	then
-		echo "[OOPS] submodule git rev-parse returned nothing"
-		false
-	fi &&
-	cd .. &&
-	echo a >a &&
-	echo z >z &&
-	git add a init z &&
-	git commit -m "super commit 1" &&
-	mv init .subrepo &&
-	GIT_CONFIG=.gitmodules git config submodule.example.url git://example.com/init.git
+	git branch initial
 '
 
-test_expect_success 'Prepare submodule add testing' '
-	submodurl=$(pwd)
+test_expect_success 'setup - repository in init subdirectory' '
+	mkdir init &&
 	(
-		mkdir addtest &&
-		cd addtest &&
-		git init
+		cd init &&
+		git init &&
+		echo a >a &&
+		git add a &&
+		git commit -m "submodule commit 1" &&
+		git tag -a -m "rev-1" rev-1
 	)
 '
 
+test_expect_success 'setup - commit with gitlink' '
+	echo a >a &&
+	echo z >z &&
+	git add a init z &&
+	git commit -m "super commit 1"
+'
+
+test_expect_success 'setup - hide init subdirectory' '
+	mv init .subrepo
+'
+
+test_expect_success 'setup - repository to add submodules to' '
+	git init addtest &&
+	git init addtest-ignore
+'
+
+# The 'submodule add' tests need some repository to add as a submodule.
+# The trash directory is a good one as any. We need to canonicalize
+# the name, though, as some tests compare it to the absolute path git
+# generates, which will expand symbolic links.
+submodurl=$(pwd -P)
+
+listbranches() {
+	git for-each-ref --format='%(refname)' 'refs/heads/*'
+}
+
+inspect() {
+	dir=$1 &&
+	dotdot="${2:-..}" &&
+
+	(
+		cd "$dir" &&
+		listbranches >"$dotdot/heads" &&
+		{ git symbolic-ref HEAD || :; } >"$dotdot/head" &&
+		git rev-parse HEAD >"$dotdot/head-sha1" &&
+		git update-index --refresh &&
+		git diff-files --exit-code &&
+		git clean -n -d -x >"$dotdot/untracked"
+	)
+}
+
 test_expect_success 'submodule add' '
+	echo "refs/heads/master" >expect &&
+	>empty &&
+
 	(
 		cd addtest &&
-		git submodule add "$submodurl" submod &&
+		git submodule add -q "$submodurl" submod >actual &&
+		test ! -s actual &&
+		echo "gitdir: ../.git/modules/submod" >expect &&
+		test_cmp expect submod/.git &&
+		(
+			cd submod &&
+			git config core.worktree >actual &&
+			echo "../../../submod" >expect &&
+			test_cmp expect actual &&
+			rm -f actual expect
+		) &&
 		git submodule init
+	) &&
+
+	rm -f heads head untracked &&
+	inspect addtest/submod ../.. &&
+	test_cmp expect heads &&
+	test_cmp expect head &&
+	test_cmp empty untracked
+'
+
+test_expect_success 'submodule add to .gitignored path fails' '
+	(
+		cd addtest-ignore &&
+		cat <<-\EOF >expect &&
+		The following path is ignored by one of your .gitignore files:
+		submod
+		Use -f if you really want to add it.
+		EOF
+		# Does not use test_commit due to the ignore
+		echo "*" > .gitignore &&
+		git add --force .gitignore &&
+		git commit -m"Ignore everything" &&
+		! git submodule add "$submodurl" submod >actual 2>&1 &&
+		test_i18ncmp expect actual
+	)
+'
+
+test_expect_success 'submodule add to .gitignored path with --force' '
+	(
+		cd addtest-ignore &&
+		git submodule add --force "$submodurl" submod
 	)
 '
 
 test_expect_success 'submodule add --branch' '
+	echo "refs/heads/initial" >expect-head &&
+	cat <<-\EOF >expect-heads &&
+	refs/heads/initial
+	refs/heads/master
+	EOF
+	>empty &&
+
 	(
 		cd addtest &&
 		git submodule add -b initial "$submodurl" submod-branch &&
-		git submodule init &&
-		cd submod-branch &&
-		git branch | grep initial
-	)
+		git submodule init
+	) &&
+
+	rm -f heads head untracked &&
+	inspect addtest/submod-branch ../.. &&
+	test_cmp expect-heads heads &&
+	test_cmp expect-head head &&
+	test_cmp empty untracked
 '
 
 test_expect_success 'submodule add with ./ in path' '
+	echo "refs/heads/master" >expect &&
+	>empty &&
+
 	(
 		cd addtest &&
 		git submodule add "$submodurl" ././dotsubmod/./frotz/./ &&
 		git submodule init
-	)
+	) &&
+
+	rm -f heads head untracked &&
+	inspect addtest/dotsubmod/frotz ../../.. &&
+	test_cmp expect heads &&
+	test_cmp expect head &&
+	test_cmp empty untracked
 '
 
 test_expect_success 'submodule add with // in path' '
+	echo "refs/heads/master" >expect &&
+	>empty &&
+
 	(
 		cd addtest &&
 		git submodule add "$submodurl" slashslashsubmod///frotz// &&
 		git submodule init
-	)
+	) &&
+
+	rm -f heads head untracked &&
+	inspect addtest/slashslashsubmod/frotz ../../.. &&
+	test_cmp expect heads &&
+	test_cmp expect head &&
+	test_cmp empty untracked
 '
 
 test_expect_success 'submodule add with /.. in path' '
+	echo "refs/heads/master" >expect &&
+	>empty &&
+
 	(
 		cd addtest &&
 		git submodule add "$submodurl" dotdotsubmod/../realsubmod/frotz/.. &&
 		git submodule init
-	)
+	) &&
+
+	rm -f heads head untracked &&
+	inspect addtest/realsubmod ../.. &&
+	test_cmp expect heads &&
+	test_cmp expect head &&
+	test_cmp empty untracked
 '
 
 test_expect_success 'submodule add with ./, /.. and // in path' '
+	echo "refs/heads/master" >expect &&
+	>empty &&
+
 	(
 		cd addtest &&
 		git submodule add "$submodurl" dot/dotslashsubmod/./../..////realsubmod2/a/b/c/d/../../../../frotz//.. &&
 		git submodule init
-	)
+	) &&
+
+	rm -f heads head untracked &&
+	inspect addtest/realsubmod2 ../.. &&
+	test_cmp expect heads &&
+	test_cmp expect head &&
+	test_cmp empty untracked
+'
+
+test_expect_success 'setup - add an example entry to .gitmodules' '
+	GIT_CONFIG=.gitmodules \
+	git config submodule.example.url git://example.com/init.git
 '
 
 test_expect_success 'status should fail for unmapped paths' '
-	if git submodule status
-	then
-		echo "[OOPS] submodule status succeeded"
-		false
-	elif ! GIT_CONFIG=.gitmodules git config submodule.example.path init
-	then
-		echo "[OOPS] git config failed to update .gitmodules"
-		false
-	fi
+	test_must_fail git submodule status
+'
+
+test_expect_success 'setup - map path in .gitmodules' '
+	cat <<\EOF >expect &&
+[submodule "example"]
+	url = git://example.com/init.git
+	path = init
+EOF
+
+	GIT_CONFIG=.gitmodules git config submodule.example.path init &&
+
+	test_cmp expect .gitmodules
 '
 
 test_expect_success 'status should only print one line' '
-	lines=$(git submodule status | wc -l) &&
-	test $lines = 1
+	git submodule status >lines &&
+	test_line_count = 1 lines
+'
+
+test_expect_success 'setup - fetch commit name from submodule' '
+	rev1=$(cd .subrepo && git rev-parse HEAD) &&
+	printf "rev1: %s\n" "$rev1" &&
+	test -n "$rev1"
 '
 
 test_expect_success 'status should initially be "missing"' '
-	git submodule status | grep "^-$rev1"
+	git submodule status >lines &&
+	grep "^-$rev1" lines
 '
 
 test_expect_success 'init should register submodule url in .git/config' '
+	echo git://example.com/init.git >expect &&
+
 	git submodule init &&
-	url=$(git config submodule.example.url) &&
-	if test "$url" != "git://example.com/init.git"
-	then
-		echo "[OOPS] init succeeded but submodule url is wrong"
-		false
-	elif test_must_fail git config submodule.example.url ./.subrepo
-	then
-		echo "[OOPS] init succeeded but update of url failed"
-		false
-	fi
+	git config submodule.example.url >url &&
+	git config submodule.example.url ./.subrepo &&
+
+	test_cmp expect url
 '
 
 test_expect_success 'update should fail when path is used by a file' '
+	echo hello >expect &&
+
 	echo "hello" >init &&
-	if git submodule update
-	then
-		echo "[OOPS] update should have failed"
-		false
-	elif test "$(cat init)" != "hello"
-	then
-		echo "[OOPS] update failed but init file was molested"
-		false
-	else
-		rm init
-	fi
+	test_must_fail git submodule update &&
+
+	test_cmp expect init
 '
 
 test_expect_success 'update should fail when path is used by a nonempty directory' '
+	echo hello >expect &&
+
+	rm -fr init &&
 	mkdir init &&
 	echo "hello" >init/a &&
-	if git submodule update
-	then
-		echo "[OOPS] update should have failed"
-		false
-	elif test "$(cat init/a)" != "hello"
-	then
-		echo "[OOPS] update failed but init/a was molested"
-		false
-	else
-		rm init/a
-	fi
+
+	test_must_fail git submodule update &&
+
+	test_cmp expect init/a
 '
 
 test_expect_success 'update should work when path is an empty dir' '
-	rm -rf init &&
+	rm -fr init &&
+	rm -f head-sha1 &&
+	echo "$rev1" >expect &&
+
 	mkdir init &&
-	git submodule update &&
-	head=$(cd init && git rev-parse HEAD) &&
-	if test -z "$head"
-	then
-		echo "[OOPS] Failed to obtain submodule head"
-		false
-	elif test "$head" != "$rev1"
-	then
-		echo "[OOPS] Submodule head is $head but should have been $rev1"
-		false
-	fi
+	git submodule update -q >update.out &&
+	test ! -s update.out &&
+
+	inspect init &&
+	test_cmp expect head-sha1
 '
 
 test_expect_success 'status should be "up-to-date" after update' '
-	git submodule status | grep "^ $rev1"
+	git submodule status >list &&
+	grep "^ $rev1" list
 '
 
 test_expect_success 'status should be "modified" after submodule commit' '
-	cd init &&
-	echo b >b &&
-	git add b &&
-	git commit -m "submodule commit 2" &&
-	rev2=$(git rev-parse HEAD) &&
-	cd .. &&
-	if test -z "$rev2"
-	then
-		echo "[OOPS] submodule git rev-parse returned nothing"
-		false
-	fi &&
-	git submodule status | grep "^+$rev2"
+	(
+		cd init &&
+		echo b >b &&
+		git add b &&
+		git commit -m "submodule commit 2"
+	) &&
+
+	rev2=$(cd init && git rev-parse HEAD) &&
+	test -n "$rev2" &&
+	git submodule status >list &&
+
+	grep "^+$rev2" list
 '
 
 test_expect_success 'the --cached sha1 should be rev1' '
-	git submodule --cached status | grep "^+$rev1"
+	git submodule --cached status >list &&
+	grep "^+$rev1" list
 '
 
 test_expect_success 'git diff should report the SHA1 of the new submodule commit' '
-	git diff | grep "^+Subproject commit $rev2"
+	git diff >diff &&
+	grep "^+Subproject commit $rev2" diff
 '
 
 test_expect_success 'update should checkout rev1' '
+	rm -f head-sha1 &&
+	echo "$rev1" >expect &&
+
 	git submodule update init &&
-	head=$(cd init && git rev-parse HEAD) &&
-	if test -z "$head"
-	then
-		echo "[OOPS] submodule git rev-parse returned nothing"
-		false
-	elif test "$head" != "$rev1"
-	then
-		echo "[OOPS] init did not checkout correct head"
-		false
-	fi
+	inspect init &&
+
+	test_cmp expect head-sha1
 '
 
 test_expect_success 'status should be "up-to-date" after update' '
-	git submodule status | grep "^ $rev1"
+	git submodule status >list &&
+	grep "^ $rev1" list
 '
 
 test_expect_success 'checkout superproject with subproject already present' '
@@ -239,6 +343,8 @@
 '
 
 test_expect_success 'apply submodule diff' '
+	>empty &&
+
 	git branch second &&
 	(
 		cd init &&
@@ -251,21 +357,24 @@
 	git format-patch -1 --stdout >P.diff &&
 	git checkout second &&
 	git apply --index P.diff &&
-	D=$(git diff --cached master) &&
-	test -z "$D"
+
+	git diff --cached master >staged &&
+	test_cmp empty staged
 '
 
 test_expect_success 'update --init' '
-
 	mv init init2 &&
 	git config -f .gitmodules submodule.example.url "$(pwd)/init2" &&
-	git config --remove-section submodule.example
-	git submodule update init > update.out &&
-	grep "not initialized" update.out &&
-	test ! -d init/.git &&
-	git submodule update --init init &&
-	test -d init/.git
+	git config --remove-section submodule.example &&
+	test_must_fail git config submodule.example.url &&
 
+	git submodule update init > update.out &&
+	cat update.out &&
+	test_i18ngrep "not initialized" update.out &&
+	test_must_fail git rev-parse --resolve-git-dir init/.git &&
+
+	git submodule update --init init &&
+	git rev-parse --resolve-git-dir init/.git
 '
 
 test_expect_success 'do not add files from a submodule' '
@@ -299,6 +408,15 @@
 
 '
 
+test_expect_success 'moving to a commit without submodule does not leave empty dir' '
+	rm -rf init &&
+	mkdir init &&
+	git reset --hard &&
+	git checkout initial &&
+	test ! -d init &&
+	git checkout second
+'
+
 test_expect_success 'submodule <invalid-path> warns' '
 
 	git submodule no-such-submodule 2> output.err &&
@@ -306,4 +424,243 @@
 
 '
 
+test_expect_success 'add submodules without specifying an explicit path' '
+	mkdir repo &&
+	(
+		cd repo &&
+		git init &&
+		echo r >r &&
+		git add r &&
+		git commit -m "repo commit 1"
+	) &&
+	git clone --bare repo/ bare.git &&
+	(
+		cd addtest &&
+		git submodule add "$submodurl/repo" &&
+		git config -f .gitmodules submodule.repo.path repo &&
+		git submodule add "$submodurl/bare.git" &&
+		git config -f .gitmodules submodule.bare.path bare
+	)
+'
+
+test_expect_success 'add should fail when path is used by a file' '
+	(
+		cd addtest &&
+		touch file &&
+		test_must_fail	git submodule add "$submodurl/repo" file
+	)
+'
+
+test_expect_success 'add should fail when path is used by an existing directory' '
+	(
+		cd addtest &&
+		mkdir empty-dir &&
+		test_must_fail git submodule add "$submodurl/repo" empty-dir
+	)
+'
+
+test_expect_success 'use superproject as upstream when path is relative and no url is set there' '
+	(
+		cd addtest &&
+		git submodule add ../repo relative &&
+		test "$(git config -f .gitmodules submodule.relative.url)" = ../repo &&
+		git submodule sync relative &&
+		test "$(git config submodule.relative.url)" = "$submodurl/repo"
+	)
+'
+
+test_expect_success 'set up for relative path tests' '
+	mkdir reltest &&
+	(
+		cd reltest &&
+		git init &&
+		mkdir sub &&
+		(
+			cd sub &&
+			git init &&
+			test_commit foo
+		) &&
+		git add sub &&
+		git config -f .gitmodules submodule.sub.path sub &&
+		git config -f .gitmodules submodule.sub.url ../subrepo &&
+		cp .git/config pristine-.git-config &&
+		cp .gitmodules pristine-.gitmodules
+	)
+'
+
+test_expect_success '../subrepo works with URL - ssh://hostname/repo' '
+	(
+		cd reltest &&
+		cp pristine-.git-config .git/config &&
+		cp pristine-.gitmodules .gitmodules &&
+		git config remote.origin.url ssh://hostname/repo &&
+		git submodule init &&
+		test "$(git config submodule.sub.url)" = ssh://hostname/subrepo
+	)
+'
+
+test_expect_success '../subrepo works with port-qualified URL - ssh://hostname:22/repo' '
+	(
+		cd reltest &&
+		cp pristine-.git-config .git/config &&
+		cp pristine-.gitmodules .gitmodules &&
+		git config remote.origin.url ssh://hostname:22/repo &&
+		git submodule init &&
+		test "$(git config submodule.sub.url)" = ssh://hostname:22/subrepo
+	)
+'
+
+# About the choice of the path in the next test:
+# - double-slash side-steps path mangling issues on Windows
+# - it is still an absolute local path
+# - there cannot be a server with a blank in its name just in case the
+#   path is used erroneously to access a //server/share style path
+test_expect_success '../subrepo path works with local path - //somewhere else/repo' '
+	(
+		cd reltest &&
+		cp pristine-.git-config .git/config &&
+		cp pristine-.gitmodules .gitmodules &&
+		git config remote.origin.url "//somewhere else/repo" &&
+		git submodule init &&
+		test "$(git config submodule.sub.url)" = "//somewhere else/subrepo"
+	)
+'
+
+test_expect_success '../subrepo works with file URL - file:///tmp/repo' '
+	(
+		cd reltest &&
+		cp pristine-.git-config .git/config &&
+		cp pristine-.gitmodules .gitmodules &&
+		git config remote.origin.url file:///tmp/repo &&
+		git submodule init &&
+		test "$(git config submodule.sub.url)" = file:///tmp/subrepo
+	)
+'
+
+test_expect_success '../subrepo works with helper URL- helper:://hostname/repo' '
+	(
+		cd reltest &&
+		cp pristine-.git-config .git/config &&
+		cp pristine-.gitmodules .gitmodules &&
+		git config remote.origin.url helper:://hostname/repo &&
+		git submodule init &&
+		test "$(git config submodule.sub.url)" = helper:://hostname/subrepo
+	)
+'
+
+test_expect_success '../subrepo works with scp-style URL - user@host:repo' '
+	(
+		cd reltest &&
+		cp pristine-.git-config .git/config &&
+		git config remote.origin.url user@host:repo &&
+		git submodule init &&
+		test "$(git config submodule.sub.url)" = user@host:subrepo
+	)
+'
+
+test_expect_success '../subrepo works with scp-style URL - user@host:path/to/repo' '
+	(
+		cd reltest &&
+		cp pristine-.git-config .git/config &&
+		cp pristine-.gitmodules .gitmodules &&
+		git config remote.origin.url user@host:path/to/repo &&
+		git submodule init &&
+		test "$(git config submodule.sub.url)" = user@host:path/to/subrepo
+	)
+'
+
+test_expect_success '../subrepo works with relative local path - foo' '
+	(
+		cd reltest &&
+		cp pristine-.git-config .git/config &&
+		cp pristine-.gitmodules .gitmodules &&
+		git config remote.origin.url foo &&
+		# actual: fails with an error
+		git submodule init &&
+		test "$(git config submodule.sub.url)" = subrepo
+	)
+'
+
+test_expect_success '../subrepo works with relative local path - foo/bar' '
+	(
+		cd reltest &&
+		cp pristine-.git-config .git/config &&
+		cp pristine-.gitmodules .gitmodules &&
+		git config remote.origin.url foo/bar &&
+		git submodule init &&
+		test "$(git config submodule.sub.url)" = foo/subrepo
+	)
+'
+
+test_expect_success '../subrepo works with relative local path - ./foo' '
+	(
+		cd reltest &&
+		cp pristine-.git-config .git/config &&
+		cp pristine-.gitmodules .gitmodules &&
+		git config remote.origin.url ./foo &&
+		git submodule init &&
+		test "$(git config submodule.sub.url)" = subrepo
+	)
+'
+
+test_expect_success '../subrepo works with relative local path - ./foo/bar' '
+	(
+		cd reltest &&
+		cp pristine-.git-config .git/config &&
+		cp pristine-.gitmodules .gitmodules &&
+		git config remote.origin.url ./foo/bar &&
+		git submodule init &&
+		test "$(git config submodule.sub.url)" = foo/subrepo
+	)
+'
+
+test_expect_success '../subrepo works with relative local path - ../foo' '
+	(
+		cd reltest &&
+		cp pristine-.git-config .git/config &&
+		cp pristine-.gitmodules .gitmodules &&
+		git config remote.origin.url ../foo &&
+		git submodule init &&
+		test "$(git config submodule.sub.url)" = ../subrepo
+	)
+'
+
+test_expect_success '../subrepo works with relative local path - ../foo/bar' '
+	(
+		cd reltest &&
+		cp pristine-.git-config .git/config &&
+		cp pristine-.gitmodules .gitmodules &&
+		git config remote.origin.url ../foo/bar &&
+		git submodule init &&
+		test "$(git config submodule.sub.url)" = ../foo/subrepo
+	)
+'
+
+test_expect_success '../bar/a/b/c works with relative local path - ../foo/bar.git' '
+	(
+		cd reltest &&
+		cp pristine-.git-config .git/config &&
+		cp pristine-.gitmodules .gitmodules &&
+		mkdir -p a/b/c &&
+		(cd a/b/c; git init) &&
+		git config remote.origin.url ../foo/bar.git &&
+		git submodule add ../bar/a/b/c ./a/b/c &&
+		git submodule init &&
+		test "$(git config submodule.a/b/c.url)" = ../foo/bar/a/b/c
+	)
+'
+
+test_expect_success 'moving the superproject does not break submodules' '
+	(
+		cd addtest &&
+		git submodule status >expect
+	)
+	mv addtest addtest2 &&
+	(
+		cd addtest2 &&
+		git submodule status >actual &&
+		test_cmp expect actual
+	)
+'
+
 test_done
diff --git a/t/t7401-submodule-summary.sh b/t/t7401-submodule-summary.sh
index 6149829..30b429e 100755
--- a/t/t7401-submodule-summary.sh
+++ b/t/t7401-submodule-summary.sh
@@ -37,11 +37,12 @@
 test_expect_success 'added submodule' "
 	git add sm1 &&
 	git submodule summary >actual &&
-	diff actual - <<-EOF
+	cat >expected <<-EOF &&
 * sm1 0000000...$head1 (2):
   > Add foo2
 
 EOF
+	test_cmp expected actual
 "
 
 commit_file sm1 &&
@@ -49,34 +50,47 @@
 
 test_expect_success 'modified submodule(forward)' "
 	git submodule summary >actual &&
-	diff actual - <<-EOF
+	cat >expected <<-EOF &&
 * sm1 $head1...$head2 (1):
   > Add foo3
 
 EOF
+	test_cmp expected actual
+"
+
+test_expect_success 'modified submodule(forward), --files' "
+	git submodule summary --files >actual &&
+	cat >expected <<-EOF &&
+* sm1 $head1...$head2 (1):
+  > Add foo3
+
+EOF
+	test_cmp expected actual
 "
 
 commit_file sm1 &&
-cd sm1 &&
-git reset --hard HEAD~2 >/dev/null &&
-head3=$(git rev-parse --verify HEAD | cut -c1-7) &&
-cd ..
+head3=$(
+	cd sm1 &&
+	git reset --hard HEAD~2 >/dev/null &&
+	git rev-parse --verify HEAD | cut -c1-7
+)
 
 test_expect_success 'modified submodule(backward)' "
     git submodule summary >actual &&
-    diff actual - <<-EOF
+    cat >expected <<-EOF &&
 * sm1 $head2...$head3 (2):
   < Add foo3
   < Add foo2
 
 EOF
+	test_cmp expected actual
 "
 
 head4=$(add_file sm1 foo4 foo5) &&
 head4_full=$(GIT_DIR=sm1/.git git rev-parse --verify HEAD)
 test_expect_success 'modified submodule(backward and forward)' "
     git submodule summary >actual &&
-    diff actual - <<-EOF
+    cat >expected <<-EOF &&
 * sm1 $head2...$head4 (4):
   > Add foo5
   > Add foo4
@@ -84,17 +98,19 @@
   < Add foo2
 
 EOF
+	test_cmp expected actual
 "
 
 test_expect_success '--summary-limit' "
     git submodule summary -n 3 >actual &&
-    diff actual - <<-EOF
+    cat >expected <<-EOF &&
 * sm1 $head2...$head4 (4):
   > Add foo5
   > Add foo4
   < Add foo3
 
 EOF
+    test_cmp expected actual
 "
 
 commit_file sm1 &&
@@ -107,21 +123,33 @@
 
 test_expect_success 'typechanged submodule(submodule->blob), --cached' "
     git submodule summary --cached >actual &&
-    diff actual - <<-EOF
+    cat >expected <<-EOF &&
 * sm1 $head4(submodule)->$head5(blob) (3):
   < Add foo5
 
 EOF
+	test_i18ncmp actual expected
+"
+
+test_expect_success 'typechanged submodule(submodule->blob), --files' "
+    git submodule summary --files >actual &&
+    cat >expected <<-EOF &&
+* sm1 $head5(blob)->$head4(submodule) (3):
+  > Add foo5
+
+EOF
+    test_i18ncmp actual expected
 "
 
 rm -rf sm1 &&
 git checkout-index sm1
 test_expect_success 'typechanged submodule(submodule->blob)' "
     git submodule summary >actual &&
-    diff actual - <<-EOF
+    cat >expected <<-EOF &&
 * sm1 $head4(submodule)->$head5(blob):
 
 EOF
+    test_i18ncmp actual expected
 "
 
 rm -f sm1 &&
@@ -129,31 +157,34 @@
 head6=$(add_file sm1 foo6 foo7)
 test_expect_success 'nonexistent commit' "
     git submodule summary >actual &&
-    diff actual - <<-EOF
+    cat >expected <<-EOF &&
 * sm1 $head4...$head6:
   Warn: sm1 doesn't contain commit $head4_full
 
 EOF
+    test_i18ncmp actual expected
 "
 
 commit_file
 test_expect_success 'typechanged submodule(blob->submodule)' "
     git submodule summary >actual &&
-    diff actual - <<-EOF
+    cat >expected <<-EOF &&
 * sm1 $head5(blob)->$head6(submodule) (2):
   > Add foo7
 
 EOF
+    test_i18ncmp expected actual
 "
 
 commit_file sm1 &&
 rm -rf sm1
 test_expect_success 'deleted submodule' "
     git submodule summary >actual &&
-    diff actual - <<-EOF
+    cat >expected <<-EOF &&
 * sm1 $head6...0000000:
 
 EOF
+    test_cmp expected actual
 "
 
 test_create_repo sm2 &&
@@ -162,40 +193,43 @@
 
 test_expect_success 'multiple submodules' "
     git submodule summary >actual &&
-    diff actual - <<-EOF
+    cat >expected <<-EOF &&
 * sm1 $head6...0000000:
 
 * sm2 0000000...$head7 (2):
   > Add foo9
 
 EOF
+    test_cmp expected actual
 "
 
 test_expect_success 'path filter' "
     git submodule summary sm2 >actual &&
-    diff actual - <<-EOF
+    cat >expected <<-EOF &&
 * sm2 0000000...$head7 (2):
   > Add foo9
 
 EOF
+    test_cmp expected actual
 "
 
 commit_file sm2
 test_expect_success 'given commit' "
     git submodule summary HEAD^ >actual &&
-    diff actual - <<-EOF
+    cat >expected <<-EOF &&
 * sm1 $head6...0000000:
 
 * sm2 0000000...$head7 (2):
   > Add foo9
 
 EOF
+    test_cmp expected actual
 "
 
 test_expect_success '--for-status' "
     git submodule summary --for-status HEAD^ >actual &&
-    test_cmp actual - <<EOF
-# Modified submodules:
+    test_i18ncmp actual - <<EOF
+# Submodule changes to be committed:
 #
 # * sm1 $head6...0000000:
 #
@@ -205,4 +239,15 @@
 EOF
 "
 
+test_expect_success 'fail when using --files together with --cached' "
+    test_must_fail git submodule summary --files --cached
+"
+
+test_expect_success 'should not fail in an empty repo' "
+    git init xyzzy &&
+    cd xyzzy &&
+    git submodule summary >output 2>&1 &&
+    test_cmp output /dev/null
+"
+
 test_done
diff --git a/t/t7403-submodule-sync.sh b/t/t7403-submodule-sync.sh
index 7538756..524d5c1 100755
--- a/t/t7403-submodule-sync.sh
+++ b/t/t7403-submodule-sync.sh
@@ -14,7 +14,7 @@
 	echo file > file &&
 	git add file &&
 	test_tick &&
-	git commit -m upstream
+	git commit -m upstream &&
 	git clone . super &&
 	git clone super submodule &&
 	(cd super &&
@@ -23,7 +23,12 @@
 	 git commit -m "submodule"
 	) &&
 	git clone super super-clone &&
-	(cd super-clone && git submodule update --init)
+	(cd super-clone && git submodule update --init) &&
+	git clone super empty-clone &&
+	(cd empty-clone && git submodule init) &&
+	git clone super top-only-clone &&
+	git clone super relative-clone &&
+	(cd relative-clone && git submodule update --init)
 '
 
 test_expect_success 'change submodule' '
@@ -42,7 +47,7 @@
 	) &&
 	mv submodule moved-submodule &&
 	(cd super &&
-	 git config -f .gitmodules submodule.submodule.url ../moved-submodule
+	 git config -f .gitmodules submodule.submodule.url ../moved-submodule &&
 	 test_tick &&
 	 git commit -a -m moved-submodule
 	)
@@ -50,15 +55,123 @@
 
 test_expect_success '"git submodule sync" should update submodule URLs' '
 	(cd super-clone &&
-	 git pull &&
+	 git pull --no-recurse-submodules &&
 	 git submodule sync
 	) &&
-	test -d "$(git config -f super-clone/submodule/.git/config \
-	                        remote.origin.url)" &&
+	test -d "$(cd super-clone/submodule &&
+	 git config remote.origin.url
+	)" &&
 	(cd super-clone/submodule &&
 	 git checkout master &&
 	 git pull
+	) &&
+	(cd super-clone &&
+	 test -d "$(git config submodule.submodule.url)"
 	)
 '
 
+test_expect_success '"git submodule sync" should update known submodule URLs' '
+	(cd empty-clone &&
+	 git pull &&
+	 git submodule sync &&
+	 test -d "$(git config submodule.submodule.url)"
+	)
+'
+
+test_expect_success '"git submodule sync" should not vivify uninteresting submodule' '
+	(cd top-only-clone &&
+	 git pull &&
+	 git submodule sync &&
+	 test -z "$(git config submodule.submodule.url)" &&
+	 git submodule sync submodule &&
+	 test -z "$(git config submodule.submodule.url)"
+	)
+'
+
+test_expect_success '"git submodule sync" handles origin URL of the form foo' '
+	(cd relative-clone &&
+	 git remote set-url origin foo &&
+	 git submodule sync &&
+	(cd submodule &&
+	 #actual fails with: "cannot strip off url foo
+	 test "$(git config remote.origin.url)" = "../submodule"
+	)
+	)
+'
+
+test_expect_success '"git submodule sync" handles origin URL of the form foo/bar' '
+	(cd relative-clone &&
+	 git remote set-url origin foo/bar &&
+	 git submodule sync &&
+	(cd submodule &&
+	 #actual foo/submodule
+	 test "$(git config remote.origin.url)" = "../foo/submodule"
+	)
+	)
+'
+
+test_expect_success '"git submodule sync" handles origin URL of the form ./foo' '
+	(cd relative-clone &&
+	 git remote set-url origin ./foo &&
+	 git submodule sync &&
+	(cd submodule &&
+	 #actual ./submodule
+	 test "$(git config remote.origin.url)" = "../submodule"
+	)
+	)
+'
+
+test_expect_success '"git submodule sync" handles origin URL of the form ./foo/bar' '
+	(cd relative-clone &&
+	 git remote set-url origin ./foo/bar &&
+	 git submodule sync &&
+	(cd submodule &&
+	 #actual ./foo/submodule
+	 test "$(git config remote.origin.url)" = "../foo/submodule"
+	)
+	)
+'
+
+test_expect_success '"git submodule sync" handles origin URL of the form ../foo' '
+	(cd relative-clone &&
+	 git remote set-url origin ../foo &&
+	 git submodule sync &&
+	(cd submodule &&
+	 #actual ../submodule
+	 test "$(git config remote.origin.url)" = "../../submodule"
+	)
+	)
+'
+
+test_expect_success '"git submodule sync" handles origin URL of the form ../foo/bar' '
+	(cd relative-clone &&
+	 git remote set-url origin ../foo/bar &&
+	 git submodule sync &&
+	(cd submodule &&
+	 #actual ../foo/submodule
+	 test "$(git config remote.origin.url)" = "../../foo/submodule"
+	)
+	)
+'
+
+test_expect_success '"git submodule sync" handles origin URL of the form ../foo/bar with deeply nested submodule' '
+	(cd relative-clone &&
+	 git remote set-url origin ../foo/bar &&
+	 mkdir -p a/b/c &&
+	 ( cd a/b/c &&
+	   git init &&
+	   :> .gitignore &&
+	   git add .gitignore &&
+	   test_tick &&
+	   git commit -m "initial commit" ) &&
+	 git submodule add ../bar/a/b/c ./a/b/c &&
+	 git submodule sync &&
+	(cd a/b/c &&
+	 #actual ../foo/bar/a/b/c
+	 test "$(git config remote.origin.url)" = "../../../../foo/bar/a/b/c"
+	)
+	)
+'
+
+
 test_done
diff --git a/t/t7405-submodule-merge.sh b/t/t7405-submodule-merge.sh
index 9a21f78..0d5b42a 100755
--- a/t/t7405-submodule-merge.sh
+++ b/t/t7405-submodule-merge.sh
@@ -45,7 +45,7 @@
 	 git commit -m sub-b) &&
 	git add sub &&
 	test_tick &&
-	git commit -m b
+	git commit -m b &&
 
 	git checkout -b c a &&
 	git merge -s ours b &&
@@ -54,21 +54,229 @@
 	git merge -s ours a
 '
 
-test_expect_success 'merging with modify/modify conflict' '
+# History setup
+#
+#             b
+#           /   \
+#  init -- a     d
+#    \      \   /
+#     g       c
+#
+# a in the main repository records to sub-a in the submodule and
+# analogous b and c. d should be automatically found by merging c into
+# b in the main repository.
+test_expect_success 'setup for merge search' '
+	mkdir merge-search &&
+	(cd merge-search &&
+	git init &&
+	mkdir sub &&
+	(cd sub &&
+	 git init &&
+	 echo "file-a" > file-a &&
+	 git add file-a &&
+	 git commit -m "sub-a" &&
+	 git branch sub-a) &&
+	git commit --allow-empty -m init &&
+	git branch init &&
+	git add sub &&
+	git commit -m "a" &&
+	git branch a &&
 
-	git checkout -b test1 a &&
-	test_must_fail git merge b &&
-	test -f .git/MERGE_MSG &&
-	git diff &&
-	test -n "$(git ls-files -u)"
+	git checkout -b b &&
+	(cd sub &&
+	 git checkout -b sub-b &&
+	 echo "file-b" > file-b &&
+	 git add file-b &&
+	 git commit -m "sub-b") &&
+	git commit -a -m "b" &&
+
+	git checkout -b c a &&
+	(cd sub &&
+	 git checkout -b sub-c sub-a &&
+	 echo "file-c" > file-c &&
+	 git add file-c &&
+	 git commit -m "sub-c") &&
+	git commit -a -m "c" &&
+
+	git checkout -b d a &&
+	(cd sub &&
+	 git checkout -b sub-d sub-b &&
+	 git merge sub-c) &&
+	git commit -a -m "d" &&
+	git branch test b &&
+
+	git checkout -b g init &&
+	(cd sub &&
+	 git checkout -b sub-g sub-c) &&
+	git add sub &&
+	git commit -a -m "g")
 '
 
-test_expect_success 'merging with a modify/modify conflict between merge bases' '
+test_expect_success 'merge with one side as a fast-forward of the other' '
+	(cd merge-search &&
+	 git checkout -b test-forward b &&
+	 git merge d &&
+	 git ls-tree test-forward sub | cut -f1 | cut -f3 -d" " > actual &&
+	 (cd sub &&
+	  git rev-parse sub-d > ../expect) &&
+	 test_cmp actual expect)
+'
 
+test_expect_success 'merging should conflict for non fast-forward' '
+	(cd merge-search &&
+	 git checkout -b test-nonforward b &&
+	 (cd sub &&
+	  git rev-parse sub-d > ../expect) &&
+	 test_must_fail git merge c 2> actual  &&
+	 grep $(cat expect) actual > /dev/null &&
+	 git reset --hard)
+'
+
+test_expect_success 'merging should fail for ambiguous common parent' '
+	(cd merge-search &&
+	git checkout -b test-ambiguous b &&
+	(cd sub &&
+	 git checkout -b ambiguous sub-b &&
+	 git merge sub-c &&
+	 git rev-parse sub-d > ../expect1 &&
+	 git rev-parse ambiguous > ../expect2) &&
+	test_must_fail git merge c 2> actual &&
+	grep $(cat expect1) actual > /dev/null &&
+	grep $(cat expect2) actual > /dev/null &&
+	git reset --hard)
+'
+
+# in a situation like this
+#
+# submodule tree:
+#
+#    sub-a --- sub-b --- sub-d
+#
+# main tree:
+#
+#    e (sub-a)
+#   /
+#  bb (sub-b)
+#   \
+#    f (sub-d)
+#
+# A merge between e and f should fail because one of the submodule
+# commits (sub-a) does not descend from the submodule merge-base (sub-b).
+#
+test_expect_success 'merging should fail for changes that are backwards' '
+	(cd merge-search &&
+	git checkout -b bb a &&
+	(cd sub &&
+	 git checkout sub-b) &&
+	git commit -a -m "bb" &&
+
+	git checkout -b e bb &&
+	(cd sub &&
+	 git checkout sub-a) &&
+	git commit -a -m "e" &&
+
+	git checkout -b f bb &&
+	(cd sub &&
+	 git checkout sub-d) &&
+	git commit -a -m "f" &&
+
+	git checkout -b test-backward e &&
+	test_must_fail git merge f)
+'
+
+
+# Check that the conflicting submodule is detected when it is
+# in the common ancestor. status should be 'U00...00"
+test_expect_success 'git submodule status should display the merge conflict properly with merge base' '
+       (cd merge-search &&
+       cat >.gitmodules <<EOF &&
+[submodule "sub"]
+       path = sub
+       url = $TRASH_DIRECTORY/sub
+EOF
+       cat >expect <<EOF &&
+U0000000000000000000000000000000000000000 sub
+EOF
+       git submodule status > actual &&
+       test_cmp expect actual &&
+	git reset --hard)
+'
+
+# Check that the conflicting submodule is detected when it is
+# not in the common ancestor. status should be 'U00...00"
+test_expect_success 'git submodule status should display the merge conflict properly without merge-base' '
+       (cd merge-search &&
+	git checkout -b test-no-merge-base g &&
+	test_must_fail git merge b &&
+       cat >.gitmodules <<EOF &&
+[submodule "sub"]
+       path = sub
+       url = $TRASH_DIRECTORY/sub
+EOF
+       cat >expect <<EOF &&
+U0000000000000000000000000000000000000000 sub
+EOF
+       git submodule status > actual &&
+       test_cmp expect actual &&
+       git reset --hard)
+'
+
+
+test_expect_success 'merging with a modify/modify conflict between merge bases' '
 	git reset --hard HEAD &&
 	git checkout -b test2 c &&
 	git merge d
+'
 
+# canonical criss-cross history in top and submodule
+test_expect_success 'setup for recursive merge with submodule' '
+	mkdir merge-recursive &&
+	(cd merge-recursive &&
+	 git init &&
+	 mkdir sub &&
+	 (cd sub &&
+	  git init &&
+	  test_commit a &&
+	  git checkout -b sub-b master &&
+	  test_commit b &&
+	  git checkout -b sub-c master &&
+	  test_commit c &&
+	  git checkout -b sub-bc sub-b &&
+	  git merge sub-c &&
+	  git checkout -b sub-cb sub-c &&
+	  git merge sub-b &&
+	  git checkout master) &&
+	 git add sub &&
+	 git commit -m a &&
+	 git checkout -b top-b master &&
+	 (cd sub && git checkout sub-b) &&
+	 git add sub &&
+	 git commit -m b &&
+	 git checkout -b top-c master &&
+	 (cd sub && git checkout sub-c) &&
+	 git add sub &&
+	 git commit -m c &&
+	 git checkout -b top-bc top-b &&
+	 git merge -s ours --no-commit top-c &&
+	 (cd sub && git checkout sub-bc) &&
+	 git add sub &&
+	 git commit -m bc &&
+	 git checkout -b top-cb top-c &&
+	 git merge -s ours --no-commit top-b &&
+	 (cd sub && git checkout sub-cb) &&
+	 git add sub &&
+	 git commit -m cb)
+'
+
+# merge should leave submodule unmerged in index
+test_expect_success 'recursive merge with submodule' '
+	(cd merge-recursive &&
+	 test_must_fail git merge top-bc &&
+	 echo "160000 $(git rev-parse top-cb:sub) 2	sub" > expect2 &&
+	 echo "160000 $(git rev-parse top-bc:sub) 3	sub" > expect3 &&
+	 git ls-files -u > actual &&
+	 grep "$(cat expect2)" actual > /dev/null &&
+	 grep "$(cat expect3)" actual > /dev/null)
 '
 
 test_done
diff --git a/t/t7406-submodule-reference.sh b/t/t7406-submodule-reference.sh
deleted file mode 100755
index cc16d3f..0000000
--- a/t/t7406-submodule-reference.sh
+++ /dev/null
@@ -1,81 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2009, Red Hat Inc, Author: Michael S. Tsirkin (mst@redhat.com)
-#
-
-test_description='test clone --reference'
-. ./test-lib.sh
-
-base_dir=`pwd`
-
-U=$base_dir/UPLOAD_LOG
-
-test_expect_success 'preparing first repository' \
-'test_create_repo A && cd A &&
-echo first > file1 &&
-git add file1 &&
-git commit -m A-initial'
-
-cd "$base_dir"
-
-test_expect_success 'preparing second repository' \
-'git clone A B && cd B &&
-echo second > file2 &&
-git add file2 &&
-git commit -m B-addition &&
-git repack -a -d &&
-git prune'
-
-cd "$base_dir"
-
-test_expect_success 'preparing supermodule' \
-'test_create_repo super && cd super &&
-echo file > file &&
-git add file &&
-git commit -m B-super-initial'
-
-cd "$base_dir"
-
-test_expect_success 'submodule add --reference' \
-'cd super && git submodule add --reference ../B "file://$base_dir/A" sub &&
-git commit -m B-super-added'
-
-cd "$base_dir"
-
-test_expect_success 'after add: existence of info/alternates' \
-'test `wc -l <super/sub/.git/objects/info/alternates` = 1'
-
-cd "$base_dir"
-
-test_expect_success 'that reference gets used with add' \
-'cd super/sub &&
-echo "0 objects, 0 kilobytes" > expected &&
-git count-objects > current &&
-diff expected current'
-
-cd "$base_dir"
-
-test_expect_success 'cloning supermodule' \
-'git clone super super-clone'
-
-cd "$base_dir"
-
-test_expect_success 'update with reference' \
-'cd super-clone && git submodule update --init --reference ../B'
-
-cd "$base_dir"
-
-test_expect_success 'after update: existence of info/alternates' \
-'test `wc -l <super-clone/sub/.git/objects/info/alternates` = 1'
-
-cd "$base_dir"
-
-test_expect_success 'that reference gets used with update' \
-'cd super-clone/sub &&
-echo "0 objects, 0 kilobytes" > expected &&
-git count-objects > current &&
-diff expected current'
-
-cd "$base_dir"
-
-test_done
diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
index 2d33d9e..ce61d4c 100755
--- a/t/t7406-submodule-update.sh
+++ b/t/t7406-submodule-update.sh
@@ -14,8 +14,8 @@
 
 compare_head()
 {
-    sha_master=`git-rev-list --max-count=1 master`
-    sha_head=`git-rev-list --max-count=1 HEAD`
+    sha_master=`git rev-list --max-count=1 master`
+    sha_head=`git rev-list --max-count=1 HEAD`
 
     test "$sha_master" = "$sha_head"
 }
@@ -25,9 +25,12 @@
 	echo file > file &&
 	git add file &&
 	test_tick &&
-	git commit -m upstream
+	git commit -m upstream &&
 	git clone . super &&
 	git clone super submodule &&
+	git clone super rebasing &&
+	git clone super merging &&
+	git clone super none &&
 	(cd super &&
 	 git submodule add ../submodule submodule &&
 	 test_tick &&
@@ -45,6 +48,21 @@
 	 ) &&
 	 git add submodule &&
 	 git commit -m "submodule update"
+	) &&
+	(cd super &&
+	 git submodule add ../rebasing rebasing &&
+	 test_tick &&
+	 git commit -m "rebasing"
+	) &&
+	(cd super &&
+	 git submodule add ../merging merging &&
+	 test_tick &&
+	 git commit -m "rebasing"
+	)
+	(cd super &&
+	 git submodule add ../none none &&
+	 test_tick &&
+	 git commit -m "none"
 	)
 '
 
@@ -62,6 +80,49 @@
 	)
 '
 
+apos="'";
+test_expect_success 'submodule update does not fetch already present commits' '
+	(cd submodule &&
+	  echo line3 >> file &&
+	  git add file &&
+	  test_tick &&
+	  git commit -m "upstream line3"
+	) &&
+	(cd super/submodule &&
+	  head=$(git rev-parse --verify HEAD) &&
+	  echo "Submodule path ${apos}submodule$apos: checked out $apos$head$apos" > ../../expected &&
+	  git reset --hard HEAD~1
+	) &&
+	(cd super &&
+	  git submodule update > ../actual 2> ../actual.err
+	) &&
+	test_i18ncmp expected actual &&
+	! test -s actual.err
+'
+
+test_expect_success 'submodule update should fail due to local changes' '
+	(cd super/submodule &&
+	 git reset --hard HEAD~1 &&
+	 echo "local change" > file
+	) &&
+	(cd super &&
+	 (cd submodule &&
+	  compare_head
+	 ) &&
+	 test_must_fail git submodule update submodule
+	)
+'
+test_expect_success 'submodule update should throw away changes with --force ' '
+	(cd super &&
+	 (cd submodule &&
+	  compare_head
+	 ) &&
+	 git submodule update --force submodule &&
+	 cd submodule &&
+	 ! compare_head
+	)
+'
+
 test_expect_success 'submodule update --rebase staying on master' '
 	(cd super/submodule &&
 	  git checkout master
@@ -177,21 +238,414 @@
 
 test_expect_success 'submodule init picks up rebase' '
 	(cd super &&
-	 git config submodule.rebasing.url git://non-existing/git &&
-	 git config submodule.rebasing.path does-not-matter &&
-	 git config submodule.rebasing.update rebase &&
+	 git config -f .gitmodules submodule.rebasing.update rebase &&
 	 git submodule init rebasing &&
-	 test "rebase" = $(git config submodule.rebasing.update)
+	 test "rebase" = "$(git config submodule.rebasing.update)"
 	)
 '
 
 test_expect_success 'submodule init picks up merge' '
 	(cd super &&
-	 git config submodule.merging.url git://non-existing/git &&
-	 git config submodule.merging.path does-not-matter &&
-	 git config submodule.merging.update merge &&
+	 git config -f .gitmodules submodule.merging.update merge &&
 	 git submodule init merging &&
-	 test "merge" = $(git config submodule.merging.update)
+	 test "merge" = "$(git config submodule.merging.update)"
+	)
+'
+
+test_expect_success 'submodule update --merge  - ignores --merge  for new submodules' '
+	(cd super &&
+	 rm -rf submodule &&
+	 git submodule update submodule &&
+	 git status -s submodule >expect &&
+	 rm -rf submodule &&
+	 git submodule update --merge submodule &&
+	 git status -s submodule >actual &&
+	 test_cmp expect actual
+	)
+'
+
+test_expect_success 'submodule update --rebase - ignores --rebase for new submodules' '
+	(cd super &&
+	 rm -rf submodule &&
+	 git submodule update submodule &&
+	 git status -s submodule >expect &&
+	 rm -rf submodule &&
+	 git submodule update --rebase submodule &&
+	 git status -s submodule >actual &&
+	 test_cmp expect actual
+	)
+'
+
+test_expect_success 'submodule update ignores update=merge config for new submodules' '
+	(cd super &&
+	 rm -rf submodule &&
+	 git submodule update submodule &&
+	 git status -s submodule >expect &&
+	 rm -rf submodule &&
+	 git config submodule.submodule.update merge &&
+	 git submodule update submodule &&
+	 git status -s submodule >actual &&
+	 git config --unset submodule.submodule.update &&
+	 test_cmp expect actual
+	)
+'
+
+test_expect_success 'submodule update ignores update=rebase config for new submodules' '
+	(cd super &&
+	 rm -rf submodule &&
+	 git submodule update submodule &&
+	 git status -s submodule >expect &&
+	 rm -rf submodule &&
+	 git config submodule.submodule.update rebase &&
+	 git submodule update submodule &&
+	 git status -s submodule >actual &&
+	 git config --unset submodule.submodule.update &&
+	 test_cmp expect actual
+	)
+'
+
+test_expect_success 'submodule init picks up update=none' '
+	(cd super &&
+	 git config -f .gitmodules submodule.none.update none &&
+	 git submodule init none &&
+	 test "none" = "$(git config submodule.none.update)"
+	)
+'
+
+test_expect_success 'submodule update - update=none in .git/config' '
+	(cd super &&
+	 git config submodule.submodule.update none &&
+	 (cd submodule &&
+	  git checkout master &&
+	  compare_head
+	 ) &&
+	 git diff --raw | grep "	submodule" &&
+	 git submodule update &&
+	 git diff --raw | grep "	submodule" &&
+	 (cd submodule &&
+	  compare_head
+	 ) &&
+	 git config --unset submodule.submodule.update &&
+	 git submodule update submodule
+	)
+'
+
+test_expect_success 'submodule update - update=none in .git/config but --checkout given' '
+	(cd super &&
+	 git config submodule.submodule.update none &&
+	 (cd submodule &&
+	  git checkout master &&
+	  compare_head
+	 ) &&
+	 git diff --raw | grep "	submodule" &&
+	 git submodule update --checkout &&
+	 test_must_fail git diff --raw \| grep "	submodule" &&
+	 (cd submodule &&
+	  test_must_fail compare_head
+	 ) &&
+	 git config --unset submodule.submodule.update
+	)
+'
+
+test_expect_success 'submodule update --init skips submodule with update=none' '
+	(cd super &&
+	 git add .gitmodules &&
+	 git commit -m ".gitmodules"
+	) &&
+	git clone super cloned &&
+	(cd cloned &&
+	 git submodule update --init &&
+	 test -e submodule/.git &&
+	 test_must_fail test -e none/.git
+	)
+'
+
+test_expect_success 'submodule update continues after checkout error' '
+	(cd super &&
+	 git reset --hard HEAD &&
+	 git submodule add ../submodule submodule2 &&
+	 git submodule init &&
+	 git commit -am "new_submodule" &&
+	 (cd submodule2 &&
+	  git rev-parse --max-count=1 HEAD > ../expect
+	 ) &&
+	 (cd submodule &&
+	  test_commit "update_submodule" file
+	 ) &&
+	 (cd submodule2 &&
+	  test_commit "update_submodule2" file
+	 ) &&
+	 git add submodule &&
+	 git add submodule2 &&
+	 git commit -m "two_new_submodule_commits" &&
+	 (cd submodule &&
+	  echo "" > file
+	 ) &&
+	 git checkout HEAD^ &&
+	 test_must_fail git submodule update &&
+	 (cd submodule2 &&
+	  git rev-parse --max-count=1 HEAD > ../actual
+	 ) &&
+	 test_cmp expect actual
+	)
+'
+test_expect_success 'submodule update continues after recursive checkout error' '
+	(cd super &&
+	 git reset --hard HEAD &&
+	 git checkout master &&
+	 git submodule update &&
+	 (cd submodule &&
+	  git submodule add ../submodule subsubmodule &&
+	  git submodule init &&
+	  git commit -m "new_subsubmodule"
+	 ) &&
+	 git add submodule &&
+	 git commit -m "update_submodule" &&
+	 (cd submodule &&
+	  (cd subsubmodule &&
+	   test_commit "update_subsubmodule" file
+	  ) &&
+	  git add subsubmodule &&
+	  test_commit "update_submodule_again" file &&
+	  (cd subsubmodule &&
+	   test_commit "update_subsubmodule_again" file
+	  ) &&
+	  test_commit "update_submodule_again_again" file
+	 ) &&
+	 (cd submodule2 &&
+	  git rev-parse --max-count=1 HEAD > ../expect &&
+	  test_commit "update_submodule2_again" file
+	 ) &&
+	 git add submodule &&
+	 git add submodule2 &&
+	 git commit -m "new_commits" &&
+	 git checkout HEAD^ &&
+	 (cd submodule &&
+	  git checkout HEAD^ &&
+	  (cd subsubmodule &&
+	   echo "" > file
+	  )
+	 ) &&
+	 test_must_fail git submodule update --recursive &&
+	 (cd submodule2 &&
+	  git rev-parse --max-count=1 HEAD > ../actual
+	 ) &&
+	 test_cmp expect actual
+	)
+'
+
+test_expect_success 'submodule update exit immediately in case of merge conflict' '
+	(cd super &&
+	 git checkout master &&
+	 git reset --hard HEAD &&
+	 (cd submodule &&
+	  (cd subsubmodule &&
+	   git reset --hard HEAD
+	  )
+	 ) &&
+	 git submodule update --recursive &&
+	 (cd submodule &&
+	  test_commit "update_submodule_2" file
+	 ) &&
+	 (cd submodule2 &&
+	  test_commit "update_submodule2_2" file
+	 ) &&
+	 git add submodule &&
+	 git add submodule2 &&
+	 git commit -m "two_new_submodule_commits" &&
+	 (cd submodule &&
+	  git checkout master &&
+	  test_commit "conflict" file &&
+	  echo "conflict" > file
+	 ) &&
+	 git checkout HEAD^ &&
+	 (cd submodule2 &&
+	  git rev-parse --max-count=1 HEAD > ../expect
+	 ) &&
+	 git config submodule.submodule.update merge &&
+	 test_must_fail git submodule update &&
+	 (cd submodule2 &&
+	  git rev-parse --max-count=1 HEAD > ../actual
+	 ) &&
+	 test_cmp expect actual
+	)
+'
+
+test_expect_success 'submodule update exit immediately after recursive rebase error' '
+	(cd super &&
+	 git checkout master &&
+	 git reset --hard HEAD &&
+	 (cd submodule &&
+	  git reset --hard HEAD &&
+	  git submodule update --recursive
+	 ) &&
+	 (cd submodule &&
+	  test_commit "update_submodule_3" file
+	 ) &&
+	 (cd submodule2 &&
+	  test_commit "update_submodule2_3" file
+	 ) &&
+	 git add submodule &&
+	 git add submodule2 &&
+	 git commit -m "two_new_submodule_commits" &&
+	 (cd submodule &&
+	  git checkout master &&
+	  test_commit "conflict2" file &&
+	  echo "conflict" > file
+	 ) &&
+	 git checkout HEAD^ &&
+	 (cd submodule2 &&
+	  git rev-parse --max-count=1 HEAD > ../expect
+	 ) &&
+	 git config submodule.submodule.update rebase &&
+	 test_must_fail git submodule update &&
+	 (cd submodule2 &&
+	  git rev-parse --max-count=1 HEAD > ../actual
+	 ) &&
+	 test_cmp expect actual
+	)
+'
+
+test_expect_success 'add different submodules to the same path' '
+	(cd super &&
+	 git submodule add ../submodule s1 &&
+	 test_must_fail git submodule add ../merging s1
+	)
+'
+
+test_expect_success 'submodule add places git-dir in superprojects git-dir' '
+	(cd super &&
+	 mkdir deeper &&
+	 git submodule add ../submodule deeper/submodule &&
+	 (cd deeper/submodule &&
+	  git log > ../../expected
+	 ) &&
+	 (cd .git/modules/deeper/submodule &&
+	  git log > ../../../../actual
+	 ) &&
+	 test_cmp actual expected
+	)
+'
+
+test_expect_success 'submodule update places git-dir in superprojects git-dir' '
+	(cd super &&
+	 git commit -m "added submodule"
+	) &&
+	git clone super super2 &&
+	(cd super2 &&
+	 git submodule init deeper/submodule &&
+	 git submodule update &&
+	 (cd deeper/submodule &&
+	  git log > ../../expected
+	 ) &&
+	 (cd .git/modules/deeper/submodule &&
+	  git log > ../../../../actual
+	 ) &&
+	 test_cmp actual expected
+	)
+'
+
+test_expect_success 'submodule add places git-dir in superprojects git-dir recursive' '
+	(cd super2 &&
+	 (cd deeper/submodule &&
+	  git submodule add ../submodule subsubmodule &&
+	  (cd subsubmodule &&
+	   git log > ../../../expected
+	  ) &&
+	  git commit -m "added subsubmodule" &&
+	  git push
+	 ) &&
+	 (cd .git/modules/deeper/submodule/modules/subsubmodule &&
+	  git log > ../../../../../actual
+	 ) &&
+	 git add deeper/submodule &&
+	 git commit -m "update submodule" &&
+	 git push &&
+	 test_cmp actual expected
+	)
+'
+
+test_expect_success 'submodule update places git-dir in superprojects git-dir recursive' '
+	mkdir super_update_r &&
+	(cd super_update_r &&
+	 git init --bare
+	) &&
+	mkdir subsuper_update_r &&
+	(cd subsuper_update_r &&
+	 git init --bare
+	) &&
+	mkdir subsubsuper_update_r &&
+	(cd subsubsuper_update_r &&
+	 git init --bare
+	) &&
+	git clone subsubsuper_update_r subsubsuper_update_r2 &&
+	(cd subsubsuper_update_r2 &&
+	 test_commit "update_subsubsuper" file &&
+	 git push origin master
+	) &&
+	git clone subsuper_update_r subsuper_update_r2 &&
+	(cd subsuper_update_r2 &&
+	 test_commit "update_subsuper" file &&
+	 git submodule add ../subsubsuper_update_r subsubmodule &&
+	 git commit -am "subsubmodule" &&
+	 git push origin master
+	) &&
+	git clone super_update_r super_update_r2 &&
+	(cd super_update_r2 &&
+	 test_commit "update_super" file &&
+	 git submodule add ../subsuper_update_r submodule &&
+	 git commit -am "submodule" &&
+	 git push origin master
+	) &&
+	rm -rf super_update_r2 &&
+	git clone super_update_r super_update_r2 &&
+	(cd super_update_r2 &&
+	 git submodule update --init --recursive &&
+	 (cd submodule/subsubmodule &&
+	  git log > ../../expected
+	 ) &&
+	 (cd .git/modules/submodule/modules/subsubmodule
+	  git log > ../../../../../actual
+	 )
+	 test_cmp actual expected
+	)
+'
+
+test_expect_success 'submodule add properly re-creates deeper level submodules' '
+	(cd super &&
+	 git reset --hard master &&
+	 rm -rf deeper/ &&
+	 git submodule add ../submodule deeper/submodule
+	)
+'
+
+test_expect_success 'submodule update properly revives a moved submodule' '
+	(cd super &&
+	 git commit -am "pre move" &&
+	 git status >expect&&
+	 H=$(cd submodule2; git rev-parse HEAD) &&
+	 git rm --cached submodule2 &&
+	 rm -rf submodule2 &&
+	 mkdir -p "moved/sub module" &&
+	 git update-index --add --cacheinfo 160000 $H "moved/sub module" &&
+	 git config -f .gitmodules submodule.submodule2.path "moved/sub module"
+	 git commit -am "post move" &&
+	 git submodule update &&
+	 git status >actual &&
+	 test_cmp expect actual
+	)
+'
+
+test_expect_success SYMLINKS 'submodule update can handle symbolic links in pwd' '
+	mkdir -p linked/dir &&
+	ln -s linked/dir linkto &&
+	(
+		cd linkto &&
+		git clone "$TRASH_DIRECTORY"/super_update_r2 super &&
+		(
+			cd super &&
+			git submodule update --init --recursive
+		)
 	)
 '
 
diff --git a/t/t7407-submodule-foreach.sh b/t/t7407-submodule-foreach.sh
new file mode 100755
index 0000000..9b69fe2
--- /dev/null
+++ b/t/t7407-submodule-foreach.sh
@@ -0,0 +1,316 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Johan Herland
+#
+
+test_description='Test "git submodule foreach"
+
+This test verifies that "git submodule foreach" correctly visits all submodules
+that are currently checked out.
+'
+
+. ./test-lib.sh
+
+
+test_expect_success 'setup a submodule tree' '
+	echo file > file &&
+	git add file &&
+	test_tick &&
+	git commit -m upstream &&
+	git clone . super &&
+	git clone super submodule &&
+	(
+		cd super &&
+		git submodule add ../submodule sub1 &&
+		git submodule add ../submodule sub2 &&
+		git submodule add ../submodule sub3 &&
+		git config -f .gitmodules --rename-section \
+			submodule.sub1 submodule.foo1 &&
+		git config -f .gitmodules --rename-section \
+			submodule.sub2 submodule.foo2 &&
+		git config -f .gitmodules --rename-section \
+			submodule.sub3 submodule.foo3 &&
+		git add .gitmodules &&
+		test_tick &&
+		git commit -m "submodules" &&
+		git submodule init sub1 &&
+		git submodule init sub2 &&
+		git submodule init sub3
+	) &&
+	(
+		cd submodule &&
+		echo different > file &&
+		git add file &&
+		test_tick &&
+		git commit -m "different"
+	) &&
+	(
+		cd super &&
+		(
+			cd sub3 &&
+			git pull
+		) &&
+		git add sub3 &&
+		test_tick &&
+		git commit -m "update sub3"
+	)
+'
+
+sub1sha1=$(cd super/sub1 && git rev-parse HEAD)
+sub3sha1=$(cd super/sub3 && git rev-parse HEAD)
+
+pwd=$(pwd)
+
+cat > expect <<EOF
+Entering 'sub1'
+$pwd/clone-foo1-sub1-$sub1sha1
+Entering 'sub3'
+$pwd/clone-foo3-sub3-$sub3sha1
+EOF
+
+test_expect_success 'test basic "submodule foreach" usage' '
+	git clone super clone &&
+	(
+		cd clone &&
+		git submodule update --init -- sub1 sub3 &&
+		git submodule foreach "echo \$toplevel-\$name-\$path-\$sha1" > ../actual &&
+		git config foo.bar zar &&
+		git submodule foreach "git config --file \"\$toplevel/.git/config\" foo.bar"
+	) &&
+	test_i18ncmp expect actual
+'
+
+test_expect_success 'setup nested submodules' '
+	git clone submodule nested1 &&
+	git clone submodule nested2 &&
+	git clone submodule nested3 &&
+	(
+		cd nested3 &&
+		git submodule add ../submodule submodule &&
+		test_tick &&
+		git commit -m "submodule" &&
+		git submodule init submodule
+	) &&
+	(
+		cd nested2 &&
+		git submodule add ../nested3 nested3 &&
+		test_tick &&
+		git commit -m "nested3" &&
+		git submodule init nested3
+	) &&
+	(
+		cd nested1 &&
+		git submodule add ../nested2 nested2 &&
+		test_tick &&
+		git commit -m "nested2" &&
+		git submodule init nested2
+	) &&
+	(
+		cd super &&
+		git submodule add ../nested1 nested1 &&
+		test_tick &&
+		git commit -m "nested1" &&
+		git submodule init nested1
+	)
+'
+
+test_expect_success 'use "submodule foreach" to checkout 2nd level submodule' '
+	git clone super clone2 &&
+	(
+		cd clone2 &&
+		test_must_fail git rev-parse --resolve-git-dir sub1/.git &&
+		test_must_fail git rev-parse --resolve-git-dir sub2/.git &&
+		test_must_fail git rev-parse --resolve-git-dir sub3/.git &&
+		test_must_fail git rev-parse --resolve-git-dir nested1/.git &&
+		git submodule update --init &&
+		git rev-parse --resolve-git-dir sub1/.git &&
+		git rev-parse --resolve-git-dir sub2/.git &&
+		git rev-parse --resolve-git-dir sub3/.git &&
+		git rev-parse --resolve-git-dir nested1/.git &&
+		test_must_fail git rev-parse --resolve-git-dir nested1/nested2/.git &&
+		git submodule foreach "git submodule update --init" &&
+		git rev-parse --resolve-git-dir nested1/nested1/nested2/.git
+		test_must_fail git rev-parse --resolve-git-dir nested1/nested2/nested3/.git
+	)
+'
+
+test_expect_success 'use "foreach --recursive" to checkout all submodules' '
+	(
+		cd clone2 &&
+		git submodule foreach --recursive "git submodule update --init" &&
+		git rev-parse --resolve-git-dir nested1/nested2/nested3/.git &&
+		git rev-parse --resolve-git-dir nested1/nested2/nested3/submodule/.git
+	)
+'
+
+cat > expect <<EOF
+Entering 'nested1'
+Entering 'nested1/nested2'
+Entering 'nested1/nested2/nested3'
+Entering 'nested1/nested2/nested3/submodule'
+Entering 'sub1'
+Entering 'sub2'
+Entering 'sub3'
+EOF
+
+test_expect_success 'test messages from "foreach --recursive"' '
+	(
+		cd clone2 &&
+		git submodule foreach --recursive "true" > ../actual
+	) &&
+	test_i18ncmp expect actual
+'
+
+cat > expect <<EOF
+nested1-nested1
+nested2-nested2
+nested3-nested3
+submodule-submodule
+foo1-sub1
+foo2-sub2
+foo3-sub3
+EOF
+
+test_expect_success 'test "foreach --quiet --recursive"' '
+	(
+		cd clone2 &&
+		git submodule foreach -q --recursive "echo \$name-\$path" > ../actual
+	) &&
+	test_cmp expect actual
+'
+
+test_expect_success 'use "update --recursive" to checkout all submodules' '
+	git clone super clone3 &&
+	(
+		cd clone3 &&
+		test_must_fail git rev-parse --resolve-git-dir sub1/.git &&
+		test_must_fail git rev-parse --resolve-git-dir sub2/.git &&
+		test_must_fail git rev-parse --resolve-git-dir sub3/.git &&
+		test_must_fail git rev-parse --resolve-git-dir nested1/.git &&
+		git submodule update --init --recursive &&
+		git rev-parse --resolve-git-dir sub1/.git &&
+		git rev-parse --resolve-git-dir sub2/.git &&
+		git rev-parse --resolve-git-dir sub3/.git &&
+		git rev-parse --resolve-git-dir nested1/.git &&
+		git rev-parse --resolve-git-dir nested1/nested2/.git &&
+		git rev-parse --resolve-git-dir nested1/nested2/nested3/.git &&
+		git rev-parse --resolve-git-dir nested1/nested2/nested3/submodule/.git
+	)
+'
+
+nested1sha1=$(cd clone3/nested1 && git rev-parse HEAD)
+nested2sha1=$(cd clone3/nested1/nested2 && git rev-parse HEAD)
+nested3sha1=$(cd clone3/nested1/nested2/nested3 && git rev-parse HEAD)
+submodulesha1=$(cd clone3/nested1/nested2/nested3/submodule && git rev-parse HEAD)
+sub1sha1=$(cd clone3/sub1 && git rev-parse HEAD)
+sub2sha1=$(cd clone3/sub2 && git rev-parse HEAD)
+sub3sha1=$(cd clone3/sub3 && git rev-parse HEAD)
+sub1sha1_short=$(cd clone3/sub1 && git rev-parse --short HEAD)
+sub2sha1_short=$(cd clone3/sub2 && git rev-parse --short HEAD)
+
+cat > expect <<EOF
+ $nested1sha1 nested1 (heads/master)
+ $nested2sha1 nested1/nested2 (heads/master)
+ $nested3sha1 nested1/nested2/nested3 (heads/master)
+ $submodulesha1 nested1/nested2/nested3/submodule (heads/master)
+ $sub1sha1 sub1 ($sub1sha1_short)
+ $sub2sha1 sub2 ($sub2sha1_short)
+ $sub3sha1 sub3 (heads/master)
+EOF
+
+test_expect_success 'test "status --recursive"' '
+	(
+		cd clone3 &&
+		git submodule status --recursive > ../actual
+	) &&
+	test_cmp expect actual
+'
+
+sed -e "/nested1 /s/.*/+$nested1sha1 nested1 (file2~1)/;/sub[1-3]/d" < expect > expect2
+mv -f expect2 expect
+
+test_expect_success 'ensure "status --cached --recursive" preserves the --cached flag' '
+	(
+		cd clone3 &&
+		(
+			cd nested1 &&
+			test_commit file2
+		) &&
+		git submodule status --cached --recursive -- nested1 > ../actual
+	) &&
+	if test_have_prereq MINGW
+	then
+		dos2unix actual
+	fi &&
+	test_cmp expect actual
+'
+
+test_expect_success 'use "git clone --recursive" to checkout all submodules' '
+	git clone --recursive super clone4 &&
+	(
+		cd clone4 &&
+		git rev-parse --resolve-git-dir .git &&
+		git rev-parse --resolve-git-dir sub1/.git &&
+		git rev-parse --resolve-git-dir sub2/.git &&
+		git rev-parse --resolve-git-dir sub3/.git &&
+		git rev-parse --resolve-git-dir nested1/.git &&
+		git rev-parse --resolve-git-dir nested1/nested2/.git &&
+		git rev-parse --resolve-git-dir nested1/nested2/nested3/.git &&
+		git rev-parse --resolve-git-dir nested1/nested2/nested3/submodule/.git
+	)
+'
+
+test_expect_success 'test "update --recursive" with a flag with spaces' '
+	git clone super "common objects" &&
+	git clone super clone5 &&
+	(
+		cd clone5 &&
+		test_must_fail git rev-parse --resolve-git-dir d nested1/.git &&
+		git submodule update --init --recursive --reference="$(dirname "$PWD")/common objects" &&
+		git rev-parse --resolve-git-dir nested1/.git &&
+		git rev-parse --resolve-git-dir nested1/nested2/.git &&
+		git rev-parse --resolve-git-dir nested1/nested2/nested3/.git &&
+		test -f .git/modules/nested1/objects/info/alternates &&
+		test -f .git/modules/nested1/modules/nested2/objects/info/alternates &&
+		test -f .git/modules/nested1/modules/nested2/modules/nested3/objects/info/alternates
+	)
+'
+
+test_expect_success 'use "update --recursive nested1" to checkout all submodules rooted in nested1' '
+	git clone super clone6 &&
+	(
+		cd clone6 &&
+		test_must_fail git rev-parse --resolve-git-dir sub1/.git &&
+		test_must_fail git rev-parse --resolve-git-dir sub2/.git &&
+		test_must_fail git rev-parse --resolve-git-dir sub3/.git &&
+		test_must_fail git rev-parse --resolve-git-dir nested1/.git &&
+		git submodule update --init --recursive -- nested1 &&
+		test_must_fail git rev-parse --resolve-git-dir sub1/.git &&
+		test_must_fail git rev-parse --resolve-git-dir sub2/.git &&
+		test_must_fail git rev-parse --resolve-git-dir sub3/.git &&
+		git rev-parse --resolve-git-dir nested1/.git &&
+		git rev-parse --resolve-git-dir nested1/nested2/.git &&
+		git rev-parse --resolve-git-dir nested1/nested2/nested3/.git &&
+		git rev-parse --resolve-git-dir nested1/nested2/nested3/submodule/.git
+	)
+'
+
+test_expect_success 'command passed to foreach retains notion of stdin' '
+	(
+		cd super &&
+		git submodule foreach echo success >../expected &&
+		yes | git submodule foreach "read y && test \"x\$y\" = xy && echo success" >../actual
+	) &&
+	test_cmp expected actual
+'
+
+test_expect_success 'command passed to foreach --recursive retains notion of stdin' '
+	(
+		cd clone2 &&
+		git submodule foreach --recursive echo success >../expected &&
+		yes | git submodule foreach --recursive "read y && test \"x\$y\" = xy && echo success" >../actual
+	) &&
+	test_cmp expected actual
+'
+
+test_done
diff --git a/t/t7408-submodule-reference.sh b/t/t7408-submodule-reference.sh
new file mode 100755
index 0000000..b770b2f
--- /dev/null
+++ b/t/t7408-submodule-reference.sh
@@ -0,0 +1,81 @@
+#!/bin/sh
+#
+# Copyright (c) 2009, Red Hat Inc, Author: Michael S. Tsirkin (mst@redhat.com)
+#
+
+test_description='test clone --reference'
+. ./test-lib.sh
+
+base_dir=`pwd`
+
+U=$base_dir/UPLOAD_LOG
+
+test_expect_success 'preparing first repository' \
+'test_create_repo A && cd A &&
+echo first > file1 &&
+git add file1 &&
+git commit -m A-initial'
+
+cd "$base_dir"
+
+test_expect_success 'preparing second repository' \
+'git clone A B && cd B &&
+echo second > file2 &&
+git add file2 &&
+git commit -m B-addition &&
+git repack -a -d &&
+git prune'
+
+cd "$base_dir"
+
+test_expect_success 'preparing superproject' \
+'test_create_repo super && cd super &&
+echo file > file &&
+git add file &&
+git commit -m B-super-initial'
+
+cd "$base_dir"
+
+test_expect_success 'submodule add --reference' \
+'cd super && git submodule add --reference ../B "file://$base_dir/A" sub &&
+git commit -m B-super-added'
+
+cd "$base_dir"
+
+test_expect_success 'after add: existence of info/alternates' \
+'test_line_count = 1 super/.git/modules/sub/objects/info/alternates'
+
+cd "$base_dir"
+
+test_expect_success 'that reference gets used with add' \
+'cd super/sub &&
+echo "0 objects, 0 kilobytes" > expected &&
+git count-objects > current &&
+diff expected current'
+
+cd "$base_dir"
+
+test_expect_success 'cloning superproject' \
+'git clone super super-clone'
+
+cd "$base_dir"
+
+test_expect_success 'update with reference' \
+'cd super-clone && git submodule update --init --reference ../B'
+
+cd "$base_dir"
+
+test_expect_success 'after update: existence of info/alternates' \
+'test_line_count = 1 super-clone/.git/modules/sub/objects/info/alternates'
+
+cd "$base_dir"
+
+test_expect_success 'that reference gets used with update' \
+'cd super-clone/sub &&
+echo "0 objects, 0 kilobytes" > expected &&
+git count-objects > current &&
+diff expected current'
+
+cd "$base_dir"
+
+test_done
diff --git a/t/t7500-commit.sh b/t/t7500-commit.sh
index 8eec0fa..1c908f4 100755
--- a/t/t7500-commit.sh
+++ b/t/t7500-commit.sh
@@ -10,7 +10,12 @@
 . ./test-lib.sh
 
 commit_msg_is () {
-	test "`git log --pretty=format:%s%b -1`" = "$1"
+	expect=commit_msg_is.expect
+	actual=commit_msg_is.actual
+
+	printf "%s" "$(git log --pretty=format:%s%b -1)" >$expect &&
+	printf "%s" "$1" >$actual &&
+	test_i18ncmp $expect $actual
 }
 
 # A sanity check to see if commit is working at all.
@@ -23,13 +28,21 @@
 test_expect_success 'nonexistent template file should return error' '
 	echo changes >> foo &&
 	git add foo &&
-	test_must_fail git commit --template "$PWD"/notexist
+	(
+		GIT_EDITOR="echo hello >\"\$1\"" &&
+		export GIT_EDITOR &&
+		test_must_fail git commit --template "$PWD"/notexist
+	)
 '
 
 test_expect_success 'nonexistent template file in config should return error' '
 	git config commit.template "$PWD"/notexist &&
-	test_must_fail git commit &&
-	git config --unset commit.template
+	test_when_finished "git config --unset commit.template" &&
+	(
+		GIT_EDITOR="echo hello >\"\$1\"" &&
+		export GIT_EDITOR &&
+		test_must_fail git commit
+	)
 '
 
 # From now on we'll use a template file that exists.
@@ -110,6 +123,20 @@
 	commit_msg_is "standard input msg"
 '
 
+cat >"$TEMPLATE" <<\EOF
+
+
+### template
+
+EOF
+test_expect_success 'commit message from template with whitespace issue' '
+	echo "content galore" >>foo &&
+	git add foo &&
+	GIT_EDITOR="$TEST_DIRECTORY"/t7500/add-whitespaced-content git commit \
+		--template "$TEMPLATE" &&
+	commit_msg_is "commit message"
+'
+
 test_expect_success 'using alternate GIT_INDEX_FILE (1)' '
 
 	cp .git/index saved-index &&
@@ -150,7 +177,7 @@
 test_expect_success '--signoff' '
 	echo "yet another content *narf*" >> foo &&
 	echo "zort" | git commit -s -F - foo &&
-	git cat-file commit HEAD | sed "1,/^$/d" > output &&
+	git cat-file commit HEAD | sed "1,/^\$/d" > output &&
 	test_cmp expect output
 '
 
@@ -193,4 +220,106 @@
 	commit_msg_is "-F log"
 '
 
+test_expect_success 'Commit without message is allowed with --allow-empty-message' '
+	echo "more content" >>foo &&
+	git add foo &&
+	>empty &&
+	git commit --allow-empty-message <empty &&
+	commit_msg_is ""
+'
+
+test_expect_success 'Commit without message is no-no without --allow-empty-message' '
+	echo "more content" >>foo &&
+	git add foo &&
+	>empty &&
+	test_must_fail git commit <empty
+'
+
+test_expect_success 'Commit a message with --allow-empty-message' '
+	echo "even more content" >>foo &&
+	git add foo &&
+	git commit --allow-empty-message -m"hello there" &&
+	commit_msg_is "hello there"
+'
+
+commit_for_rebase_autosquash_setup () {
+	echo "first content line" >>foo &&
+	git add foo &&
+	cat >log <<EOF &&
+target message subject line
+
+target message body line 1
+target message body line 2
+EOF
+	git commit -F log &&
+	echo "second content line" >>foo &&
+	git add foo &&
+	git commit -m "intermediate commit" &&
+	echo "third content line" >>foo &&
+	git add foo
+}
+
+test_expect_success 'commit --fixup provides correct one-line commit message' '
+	commit_for_rebase_autosquash_setup &&
+	git commit --fixup HEAD~1 &&
+	commit_msg_is "fixup! target message subject line"
+'
+
+test_expect_success 'commit --squash works with -F' '
+	commit_for_rebase_autosquash_setup &&
+	echo "log message from file" >msgfile &&
+	git commit --squash HEAD~1 -F msgfile  &&
+	commit_msg_is "squash! target message subject linelog message from file"
+'
+
+test_expect_success 'commit --squash works with -m' '
+	commit_for_rebase_autosquash_setup &&
+	git commit --squash HEAD~1 -m "foo bar\nbaz" &&
+	commit_msg_is "squash! target message subject linefoo bar\nbaz"
+'
+
+test_expect_success 'commit --squash works with -C' '
+	commit_for_rebase_autosquash_setup &&
+	git commit --squash HEAD~1 -C HEAD &&
+	commit_msg_is "squash! target message subject lineintermediate commit"
+'
+
+test_expect_success 'commit --squash works with -c' '
+	commit_for_rebase_autosquash_setup &&
+	test_set_editor "$TEST_DIRECTORY"/t7500/edit-content &&
+	git commit --squash HEAD~1 -c HEAD &&
+	commit_msg_is "squash! target message subject lineedited commit"
+'
+
+test_expect_success 'commit --squash works with -C for same commit' '
+	commit_for_rebase_autosquash_setup &&
+	git commit --squash HEAD -C HEAD &&
+	commit_msg_is "squash! intermediate commit"
+'
+
+test_expect_success 'commit --squash works with -c for same commit' '
+	commit_for_rebase_autosquash_setup &&
+	test_set_editor "$TEST_DIRECTORY"/t7500/edit-content &&
+	git commit --squash HEAD -c HEAD &&
+	commit_msg_is "squash! edited commit"
+'
+
+test_expect_success 'commit --squash works with editor' '
+	commit_for_rebase_autosquash_setup &&
+	test_set_editor "$TEST_DIRECTORY"/t7500/add-content &&
+	git commit --squash HEAD~1 &&
+	commit_msg_is "squash! target message subject linecommit message"
+'
+
+test_expect_success 'invalid message options when using --fixup' '
+	echo changes >>foo &&
+	echo "message" >log &&
+	git add foo &&
+	test_must_fail git commit --fixup HEAD~1 --squash HEAD~2 &&
+	test_must_fail git commit --fixup HEAD~1 -C HEAD~2 &&
+	test_must_fail git commit --fixup HEAD~1 -c HEAD~2 &&
+	test_must_fail git commit --fixup HEAD~1 -m "cmdline message" &&
+	test_must_fail git commit --fixup HEAD~1 -F log
+'
+
 test_done
diff --git a/t/t7500/add-whitespaced-content b/t/t7500/add-whitespaced-content
new file mode 100755
index 0000000..ccf07c6
--- /dev/null
+++ b/t/t7500/add-whitespaced-content
@@ -0,0 +1,8 @@
+#!/bin/sh
+sed -e 's/|$//' >>"$1" <<\EOF
+
+ |
+commit message  	 |
+
+EOF
+exit 0
diff --git a/t/t7500/edit-content b/t/t7500/edit-content
new file mode 100755
index 0000000..08db9fd
--- /dev/null
+++ b/t/t7500/edit-content
@@ -0,0 +1,4 @@
+#!/bin/sh
+sed -e "s/intermediate/edited/g" <"$1" >"$1-"
+mv "$1-" "$1"
+exit 0
diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh
index e2ef532..195e747 100755
--- a/t/t7501-commit.sh
+++ b/t/t7501-commit.sh
@@ -8,160 +8,244 @@
 
 test_description='git commit'
 . ./test-lib.sh
+. "$TEST_DIRECTORY/diff-lib.sh"
+
+author='The Real Author <someguy@his.email.org>'
 
 test_tick
 
-test_expect_success \
-	"initial status" \
-	"echo 'bongo bongo' >file &&
-	 git add file && \
-	 git status | grep 'Initial commit'"
+test_expect_success 'initial status' '
+	echo bongo bongo >file &&
+	git add file &&
+	git status >actual &&
+	test_i18ngrep "Initial commit" actual
+'
 
-test_expect_success \
-	"fail initial amend" \
-	"test_must_fail git commit --amend"
+test_expect_success 'fail initial amend' '
+	test_must_fail git commit --amend
+'
 
-test_expect_success \
-	"initial commit" \
-	"git commit -m initial"
+test_expect_success 'setup: initial commit' '
+	git commit -m initial
+'
 
-test_expect_success \
-	"invalid options 1" \
-	"test_must_fail git commit -m foo -m bar -F file"
+test_expect_success '-m and -F do not mix' '
+	git checkout HEAD file && echo >>file && git add file &&
+	test_must_fail git commit -m foo -m bar -F file
+'
 
-test_expect_success \
-	"invalid options 2" \
-	"test_must_fail git commit -C HEAD -m illegal"
+test_expect_success '-m and -C do not mix' '
+	git checkout HEAD file && echo >>file && git add file &&
+	test_must_fail git commit -C HEAD -m illegal
+'
 
-test_expect_success \
-	"using paths with -a" \
-	"echo King of the bongo >file &&
-	test_must_fail git commit -m foo -a file"
+test_expect_success 'paths and -a do not mix' '
+	echo King of the bongo >file &&
+	test_must_fail git commit -m foo -a file
+'
 
-test_expect_success PERL \
-	"using paths with --interactive" \
-	"echo bong-o-bong >file &&
-	! (echo 7 | git commit -m foo --interactive file)"
+test_expect_success PERL 'can use paths with --interactive' '
+	echo bong-o-bong >file &&
+	# 2: update, 1:st path, that is all, 7: quit
+	( echo 2; echo 1; echo; echo 7 ) |
+	git commit -m foo --interactive file &&
+	git reset --hard HEAD^
+'
 
-test_expect_success \
-	"using invalid commit with -C" \
-	"test_must_fail git commit -C bogus"
+test_expect_success 'using invalid commit with -C' '
+	test_must_fail git commit -C bogus
+'
 
-test_expect_success \
-	"testing nothing to commit" \
-	"test_must_fail git commit -m initial"
+test_expect_success 'nothing to commit' '
+	test_must_fail git commit -m initial
+'
 
-test_expect_success \
-	"next commit" \
-	"echo 'bongo bongo bongo' >file \
-	 git commit -m next -a"
+test_expect_success 'setup: non-initial commit' '
+	echo bongo bongo bongo >file &&
+	git commit -m next -a
+'
 
-test_expect_success \
-	"commit message from non-existing file" \
-	"echo 'more bongo: bongo bongo bongo bongo' >file && \
-	 test_must_fail git commit -F gah -a"
+test_expect_success 'commit message from non-existing file' '
+	echo more bongo: bongo bongo bongo bongo >file &&
+	test_must_fail git commit -F gah -a
+'
 
-# Empty except stray tabs and spaces on a few lines.
-sed -e 's/@$//' >msg <<EOF
-		@
+test_expect_success 'empty commit message' '
+	# Empty except stray tabs and spaces on a few lines.
+	sed -e "s/@//g" >msg <<-\EOF &&
+		@		@
+		@@
+		@  @
+		@Signed-off-by: hula@
+	EOF
+	test_must_fail git commit -F msg -a
+'
 
-  @
-Signed-off-by: hula
-EOF
-test_expect_success \
-	"empty commit message" \
-	"test_must_fail git commit -F msg -a"
+test_expect_success 'template "emptyness" check does not kick in with -F' '
+	git checkout HEAD file && echo >>file && git add file &&
+	git commit -t file -F file
+'
 
-test_expect_success \
-	"commit message from file" \
-	"echo 'this is the commit message, coming from a file' >msg && \
-	 git commit -F msg -a"
+test_expect_success 'template "emptyness" check' '
+	git checkout HEAD file && echo >>file && git add file &&
+	test_must_fail git commit -t file 2>err &&
+	test_i18ngrep "did not edit" err
+'
 
-cat >editor <<\EOF
-#!/bin/sh
-sed -e "s/a file/an amend commit/g" < "$1" > "$1-"
-mv "$1-" "$1"
-EOF
-chmod 755 editor
+test_expect_success 'setup: commit message from file' '
+	git checkout HEAD file && echo >>file && git add file &&
+	echo this is the commit message, coming from a file >msg &&
+	git commit -F msg -a
+'
 
-test_expect_success \
-	"amend commit" \
-	"VISUAL=./editor git commit --amend"
+test_expect_success 'amend commit' '
+	cat >editor <<-\EOF &&
+	#!/bin/sh
+	sed -e "s/a file/an amend commit/g" < "$1" > "$1-"
+	mv "$1-" "$1"
+	EOF
+	chmod 755 editor &&
+	EDITOR=./editor git commit --amend
+'
 
-test_expect_success \
-	"passing -m and -F" \
-	"echo 'enough with the bongos' >file && \
-	 test_must_fail git commit -F msg -m amending ."
+test_expect_success 'amend --only ignores staged contents' '
+	cp file file.expect &&
+	echo changed >file &&
+	git add file &&
+	git commit --no-edit --amend --only &&
+	git cat-file blob HEAD:file >file.actual &&
+	test_cmp file.expect file.actual &&
+	git diff --exit-code
+'
 
-test_expect_success \
-	"using message from other commit" \
-	"git commit -C HEAD^ ."
+test_expect_success 'set up editor' '
+	cat >editor <<-\EOF &&
+	#!/bin/sh
+	sed -e "s/unamended/amended/g" <"$1" >"$1-"
+	mv "$1-" "$1"
+	EOF
+	chmod 755 editor
+'
 
-cat >editor <<\EOF
-#!/bin/sh
-sed -e "s/amend/older/g"  < "$1" > "$1-"
-mv "$1-" "$1"
-EOF
-chmod 755 editor
+test_expect_success 'amend without launching editor' '
+	echo unamended >expect &&
+	git commit --allow-empty -m "unamended" &&
+	echo needs more bongo >file &&
+	git add file &&
+	EDITOR=./editor git commit --no-edit --amend &&
+	git diff --exit-code HEAD -- file &&
+	git diff-tree -s --format=%s HEAD >msg &&
+	test_cmp expect msg
+'
 
-test_expect_success \
-	"editing message from other commit" \
-	"echo 'hula hula' >file && \
-	 VISUAL=./editor git commit -c HEAD^ -a"
+test_expect_success '--amend --edit' '
+	echo amended >expect &&
+	git commit --allow-empty -m "unamended" &&
+	echo bongo again >file &&
+	git add file &&
+	EDITOR=./editor git commit --edit --amend &&
+	git diff-tree -s --format=%s HEAD >msg &&
+	test_cmp expect msg
+'
 
-test_expect_success \
-	"message from stdin" \
-	"echo 'silly new contents' >file && \
-	 echo commit message from stdin | git commit -F - -a"
+test_expect_success '--amend --edit of empty message' '
+	cat >replace <<-\EOF &&
+	#!/bin/sh
+	echo "amended" >"$1"
+	EOF
+	chmod 755 replace &&
+	git commit --allow-empty --allow-empty-message -m "" &&
+	echo more bongo >file &&
+	git add file &&
+	EDITOR=./replace git commit --edit --amend &&
+	git diff-tree -s --format=%s HEAD >msg &&
+	./replace expect &&
+	test_cmp expect msg
+'
 
-test_expect_success \
-	"overriding author from command line" \
-	"echo 'gak' >file && \
-	 git commit -m 'author' --author 'Rubber Duck <rduck@convoy.org>' -a"
+test_expect_success '-m --edit' '
+	echo amended >expect &&
+	git commit --allow-empty -m buffer &&
+	echo bongo bongo >file &&
+	git add file &&
+	EDITOR=./editor git commit -m unamended --edit &&
+	git diff-tree -s  --format=%s HEAD >msg &&
+	test_cmp expect msg
+'
 
-test_expect_success PERL \
-	"interactive add" \
-	"echo 7 | git commit --interactive | grep 'What now'"
+test_expect_success '-m and -F do not mix' '
+	echo enough with the bongos >file &&
+	test_must_fail git commit -F msg -m amending .
+'
 
-test_expect_success \
-	"showing committed revisions" \
-	"git rev-list HEAD >current"
+test_expect_success 'using message from other commit' '
+	git commit -C HEAD^ .
+'
 
-cat >editor <<\EOF
-#!/bin/sh
-sed -e "s/good/bad/g" < "$1" > "$1-"
-mv "$1-" "$1"
-EOF
-chmod 755 editor
+test_expect_success 'editing message from other commit' '
+	cat >editor <<-\EOF &&
+	#!/bin/sh
+	sed -e "s/amend/older/g"  < "$1" > "$1-"
+	mv "$1-" "$1"
+	EOF
+	chmod 755 editor &&
+	echo hula hula >file &&
+	EDITOR=./editor git commit -c HEAD^ -a
+'
 
-cat >msg <<EOF
-A good commit message.
-EOF
+test_expect_success 'message from stdin' '
+	echo silly new contents >file &&
+	echo commit message from stdin |
+	git commit -F - -a
+'
 
-test_expect_success \
-	'editor not invoked if -F is given' '
-	 echo "moo" >file &&
-	 VISUAL=./editor git commit -a -F msg &&
-	 git show -s --pretty=format:"%s" | grep -q good &&
-	 echo "quack" >file &&
-	 echo "Another good message." | VISUAL=./editor git commit -a -F - &&
-	 git show -s --pretty=format:"%s" | grep -q good
-	 '
-# We could just check the head sha1, but checking each commit makes it
-# easier to isolate bugs.
+test_expect_success 'overriding author from command line' '
+	echo gak >file &&
+	git commit -m author \
+		--author "Rubber Duck <rduck@convoy.org>" -a >output 2>&1 &&
+	grep Rubber.Duck output
+'
 
-cat >expected <<\EOF
-72c0dc9855b0c9dadcbfd5a31cab072e0cb774ca
-9b88fc14ce6b32e3d9ee021531a54f18a5cf38a2
-3536bbb352c3a1ef9a420f5b4242d48578b92aa7
-d381ac431806e53f3dd7ac2f1ae0534f36d738b9
-4fd44095ad6334f3ef72e4c5ec8ddf108174b54a
-402702b49136e7587daa9280e91e4bb7cb2179f7
-EOF
+test_expect_success PERL 'interactive add' '
+	echo 7 |
+	git commit --interactive |
+	grep "What now"
+'
 
-test_expect_success \
-    'validate git rev-list output.' \
-    'test_cmp expected current'
+test_expect_success PERL "commit --interactive doesn't change index if editor aborts" '
+	echo zoo >file &&
+	test_must_fail git diff --exit-code >diff1 &&
+	(echo u ; echo "*" ; echo q) |
+	(
+		EDITOR=: &&
+		export EDITOR &&
+		test_must_fail git commit --interactive
+	) &&
+	git diff >diff2 &&
+	compare_diff_patch diff1 diff2
+'
+
+test_expect_success 'editor not invoked if -F is given' '
+	cat >editor <<-\EOF &&
+	#!/bin/sh
+	sed -e s/good/bad/g <"$1" >"$1-"
+	mv "$1-" "$1"
+	EOF
+	chmod 755 editor &&
+
+	echo A good commit message. >msg &&
+	echo moo >file &&
+
+	EDITOR=./editor git commit -a -F msg &&
+	git show -s --pretty=format:%s >subject &&
+	grep -q good subject &&
+
+	echo quack >file &&
+	echo Another good message. |
+	EDITOR=./editor git commit -a -F - &&
+	git show -s --pretty=format:%s >subject &&
+	grep -q good subject
+'
 
 test_expect_success 'partial commit that involves removal (1)' '
 
@@ -195,7 +279,6 @@
 
 '
 
-author="The Real Author <someguy@his.email.org>"
 test_expect_success 'amend commit to fix author' '
 
 	oldtick=$GIT_AUTHOR_DATE &&
@@ -211,6 +294,25 @@
 
 '
 
+test_expect_success 'amend commit to fix date' '
+
+	test_tick &&
+	newtick=$GIT_AUTHOR_DATE &&
+	git reset --hard &&
+	git cat-file -p HEAD |
+	sed -e "s/author.*/author $author $newtick/" \
+		-e "s/^\(committer.*> \).*$/\1$GIT_COMMITTER_DATE/" > \
+		expected &&
+	git commit --amend --date="$newtick" &&
+	git cat-file -p HEAD > current &&
+	test_cmp expected current
+
+'
+
+test_expect_success 'commit complains about bogus date' '
+	test_must_fail git commit --amend --date=10.11.2010
+'
+
 test_expect_success 'sign off (1)' '
 
 	echo 1 >positive &&
@@ -247,6 +349,47 @@
 
 '
 
+test_expect_success 'signoff gap' '
+
+	echo 3 >positive &&
+	git add positive &&
+	alt="Alt-RFC-822-Header: Value" &&
+	git commit -s -m "welcome
+
+$alt" &&
+	git cat-file commit HEAD | sed -e "1,/^\$/d" > actual &&
+	(
+		echo welcome
+		echo
+		echo $alt
+		git var GIT_COMMITTER_IDENT |
+		sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /"
+	) >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'signoff gap 2' '
+
+	echo 4 >positive &&
+	git add positive &&
+	alt="fixed: 34" &&
+	git commit -s -m "welcome
+
+We have now
+$alt" &&
+	git cat-file commit HEAD | sed -e "1,/^\$/d" > actual &&
+	(
+		echo welcome
+		echo
+		echo We have now
+		echo $alt
+		echo
+		git var GIT_COMMITTER_IDENT |
+		sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /"
+	) >expected &&
+	test_cmp expected actual
+'
+
 test_expect_success 'multiple -m' '
 
 	>negative &&
@@ -264,7 +407,6 @@
 
 '
 
-author="The Real Author <someguy@his.email.org>"
 test_expect_success 'amend commit to fix author' '
 
 	oldtick=$GIT_AUTHOR_DATE &&
@@ -291,15 +433,8 @@
 
 test_expect_success 'same tree (single parent)' '
 
-	git reset --hard
-
-	if git commit -m empty
-	then
-		echo oops -- should have complained
-		false
-	else
-		: happy
-	fi
+	git reset --hard &&
+	test_must_fail git commit -m empty
 
 '
 
@@ -365,4 +500,28 @@
 
 '
 
+test_expect_success 'amend can copy notes' '
+
+	git config notes.rewrite.amend true &&
+	git config notes.rewriteRef "refs/notes/*" &&
+	test_commit foo &&
+	git notes add -m"a note" &&
+	test_tick &&
+	git commit --amend -m"new foo" &&
+	test "$(git notes show)" = "a note"
+
+'
+
+test_expect_success 'commit a file whose name is a dash' '
+	git reset --hard &&
+	for i in 1 2 3 4 5
+	do
+		echo $i
+	done >./- &&
+	git add ./- &&
+	test_tick &&
+	git commit -m "add dash" >output </dev/null &&
+	test_i18ngrep " changed, 5 insertions" output
+'
+
 test_done
diff --git a/t/t7502-commit.sh b/t/t7502-commit.sh
index 56cd866..181456a 100755
--- a/t/t7502-commit.sh
+++ b/t/t7502-commit.sh
@@ -4,8 +4,79 @@
 
 . ./test-lib.sh
 
+# Arguments: [<prefix] [<commit message>] [<commit options>]
+check_summary_oneline() {
+	test_tick &&
+	git commit ${3+"$3"} -m "$2" | head -1 > act &&
+
+	# branch name
+	SUMMARY_PREFIX="$(git name-rev --name-only HEAD)" &&
+
+	# append the "special" prefix, like "root-commit", "detached HEAD"
+	if test -n "$1"
+	then
+		SUMMARY_PREFIX="$SUMMARY_PREFIX ($1)"
+	fi
+
+	# abbrev SHA-1
+	SUMMARY_POSTFIX="$(git log -1 --pretty='format:%h')"
+	echo "[$SUMMARY_PREFIX $SUMMARY_POSTFIX] $2" >exp &&
+
+	test_i18ncmp exp act
+}
+
+test_expect_success 'output summary format' '
+
+	echo new >file1 &&
+	git add file1 &&
+	check_summary_oneline "root-commit" "initial" &&
+
+	echo change >>file1 &&
+	git add file1
+'
+
+test_expect_success 'output summary format: root-commit' '
+	check_summary_oneline "" "a change"
+'
+
+test_expect_success 'output summary format for commit with an empty diff' '
+
+	check_summary_oneline "" "empty" "--allow-empty"
+'
+
+test_expect_success 'output summary format for merges' '
+
+	git checkout -b recursive-base &&
+	test_commit base file1 &&
+
+	git checkout -b recursive-a recursive-base &&
+	test_commit commit-a file1 &&
+
+	git checkout -b recursive-b recursive-base &&
+	test_commit commit-b file1 &&
+
+	# conflict
+	git checkout recursive-a &&
+	test_must_fail git merge recursive-b &&
+	# resolve the conflict
+	echo commit-a > file1 &&
+	git add file1 &&
+	check_summary_oneline "" "Merge"
+'
+
+output_tests_cleanup() {
+	# this is needed for "do not fire editor in the presence of conflicts"
+	git checkout master &&
+
+	# this is needed for the "partial removal" test to pass
+	git rm file1 &&
+	git commit -m "cleanup"
+}
+
 test_expect_success 'the basics' '
 
+	output_tests_cleanup &&
+
 	echo doing partial >"commit is" &&
 	mkdir not &&
 	echo very much encouraged but we should >not/forbid &&
@@ -35,7 +106,7 @@
 
 '
 
-test_expect_success 'partial modification in a subdirecotry' '
+test_expect_success 'partial modification in a subdirectory' '
 
 	test_tick &&
 	git commit -m "partial commit to subdirectory" not &&
@@ -147,19 +218,21 @@
 
 '
 
-echo "sample
-
-# Please enter the commit message for your changes. Lines starting
-# with '#' will be ignored, and an empty message aborts the commit." >expect
-
 test_expect_success 'cleanup commit messages (strip,-F,-e)' '
 
 	echo >>negative &&
 	{ echo;echo sample;echo; } >text &&
 	git commit -e -F text -a &&
-	head -n 4 .git/COMMIT_EDITMSG >actual &&
-	test_cmp expect actual
+	head -n 4 .git/COMMIT_EDITMSG >actual
+'
 
+echo "sample
+
+# Please enter the commit message for your changes. Lines starting
+# with '#' will be ignored, and an empty message aborts the commit." >expect
+
+test_expect_success 'cleanup commit messages (strip,-F,-e): output' '
+	test_i18ncmp expect actual
 '
 
 echo "#
@@ -167,11 +240,10 @@
 #" >> expect
 
 test_expect_success 'author different from committer' '
-
 	echo >>negative &&
-	git commit -e -m "sample"
+	test_might_fail git commit -e -m "sample" &&
 	head -n 7 .git/COMMIT_EDITMSG >actual &&
-	test_cmp expect actual
+	test_i18ncmp expect actual
 '
 
 mv expect expect.tmp
@@ -184,14 +256,14 @@
 
 	echo >>negative &&
 	(
-		unset GIT_COMMITTER_EMAIL
-		unset GIT_COMMITTER_NAME
+		sane_unset GIT_COMMITTER_EMAIL &&
+		sane_unset GIT_COMMITTER_NAME &&
 		# must fail because there is no change
 		test_must_fail git commit -e -m "sample"
 	) &&
 	head -n 8 .git/COMMIT_EDITMSG |	\
-	sed "s/^# Committer: .*/# Committer:/" >actual &&
-	test_cmp expect actual
+	sed "s/^# Committer: .*/# Committer:/" >actual
+	test_i18ncmp expect actual
 '
 
 pwd=`pwd`
@@ -258,4 +330,122 @@
 
 '
 
+test_expect_success 'A single-liner subject with a token plus colon is not a footer' '
+
+	git reset --hard &&
+	git commit -s -m "hello: kitty" --allow-empty &&
+	git cat-file commit HEAD | sed -e "1,/^$/d" >actual &&
+	test_line_count = 3 actual
+
+'
+
+cat >.git/FAKE_EDITOR <<EOF
+#!$SHELL_PATH
+mv "\$1" "\$1.orig"
+(
+	echo message
+	cat "\$1.orig"
+) >"\$1"
+EOF
+
+echo '## Custom template' >template
+
+clear_config () {
+	(
+		git config --unset-all "$1"
+		case $? in
+		0|5)	exit 0 ;;
+		*)	exit 1 ;;
+		esac
+	)
+}
+
+try_commit () {
+	git reset --hard &&
+	echo >>negative &&
+	GIT_EDITOR=.git/FAKE_EDITOR git commit -a $* $use_template &&
+	case "$use_template" in
+	'')
+		test_i18ngrep ! "^## Custom template" .git/COMMIT_EDITMSG ;;
+	*)
+		test_i18ngrep "^## Custom template" .git/COMMIT_EDITMSG ;;
+	esac
+}
+
+try_commit_status_combo () {
+
+	test_expect_success 'commit' '
+		clear_config commit.status &&
+		try_commit "" &&
+		test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+	'
+
+	test_expect_success 'commit' '
+		clear_config commit.status &&
+		try_commit "" &&
+		test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+	'
+
+	test_expect_success 'commit --status' '
+		clear_config commit.status &&
+		try_commit --status &&
+		test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+	'
+
+	test_expect_success 'commit --no-status' '
+		clear_config commit.status &&
+		try_commit --no-status &&
+		test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
+	'
+
+	test_expect_success 'commit with commit.status = yes' '
+		clear_config commit.status &&
+		git config commit.status yes &&
+		try_commit "" &&
+		test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+	'
+
+	test_expect_success 'commit with commit.status = no' '
+		clear_config commit.status &&
+		git config commit.status no &&
+		try_commit "" &&
+		test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
+	'
+
+	test_expect_success 'commit --status with commit.status = yes' '
+		clear_config commit.status &&
+		git config commit.status yes &&
+		try_commit --status &&
+		test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+	'
+
+	test_expect_success 'commit --no-status with commit.status = yes' '
+		clear_config commit.status &&
+		git config commit.status yes &&
+		try_commit --no-status &&
+		test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
+	'
+
+	test_expect_success 'commit --status with commit.status = no' '
+		clear_config commit.status &&
+		git config commit.status no &&
+		try_commit --status &&
+		test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+	'
+
+	test_expect_success 'commit --no-status with commit.status = no' '
+		clear_config commit.status &&
+		git config commit.status no &&
+		try_commit --no-status &&
+		test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
+	'
+
+}
+
+try_commit_status_combo
+
+use_template="-t template"
+
+try_commit_status_combo
+
 test_done
diff --git a/t/t7503-pre-commit-hook.sh b/t/t7503-pre-commit-hook.sh
index 8528f64..984889b 100755
--- a/t/t7503-pre-commit-hook.sh
+++ b/t/t7503-pre-commit-hook.sh
@@ -84,5 +84,56 @@
 	git commit --no-verify -m "more content"
 
 '
+chmod +x "$HOOK"
+
+# a hook that checks $GIT_PREFIX and succeeds inside the
+# success/ subdirectory only
+cat > "$HOOK" <<EOF
+#!/bin/sh
+test \$GIT_PREFIX = success/
+EOF
+
+test_expect_success 'with hook requiring GIT_PREFIX' '
+
+	echo "more content" >> file &&
+	git add file &&
+	mkdir success &&
+	(
+		cd success &&
+		git commit -m "hook requires GIT_PREFIX = success/"
+	) &&
+	rmdir success
+'
+
+test_expect_success 'with failing hook requiring GIT_PREFIX' '
+
+	echo "more content" >> file &&
+	git add file &&
+	mkdir fail &&
+	(
+		cd fail &&
+		test_must_fail git commit -m "hook must fail"
+	) &&
+	rmdir fail &&
+	git checkout -- file
+'
+
+test_expect_success 'check the author in hook' '
+	write_script "$HOOK" <<-\EOF &&
+	test "$GIT_AUTHOR_NAME" = "New Author" &&
+	test "$GIT_AUTHOR_EMAIL" = "newauthor@example.com"
+	EOF
+	test_must_fail git commit --allow-empty -m "by a.u.thor" &&
+	(
+		GIT_AUTHOR_NAME="New Author" &&
+		GIT_AUTHOR_EMAIL="newauthor@example.com" &&
+		export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL &&
+		git commit --allow-empty -m "by new.author via env" &&
+		git show -s
+	) &&
+	git commit --author="New Author <newauthor@example.com>" \
+		--allow-empty -m "by new.author via command line" &&
+	git show -s
+'
 
 test_done
diff --git a/t/t7505-prepare-commit-msg-hook.sh b/t/t7505-prepare-commit-msg-hook.sh
index ff18962..5b4b694 100755
--- a/t/t7505-prepare-commit-msg-hook.sh
+++ b/t/t7505-prepare-commit-msg-hook.sh
@@ -132,6 +132,18 @@
 
 '
 
+test_expect_success 'with hook (merge)' '
+
+	head=`git rev-parse HEAD` &&
+	git checkout -b other HEAD@{1} &&
+	echo "more" >> file &&
+	git add file &&
+	git commit -m other &&
+	git checkout - &&
+	git merge other &&
+	test "`git log -1 --pretty=format:%s`" = merge
+'
+
 cat > "$HOOK" <<'EOF'
 #!/bin/sh
 exit 1
diff --git a/t/t7506-status-submodule.sh b/t/t7506-status-submodule.sh
index d9a08aa..d31b34d 100755
--- a/t/t7506-status-submodule.sh
+++ b/t/t7506-status-submodule.sh
@@ -4,35 +4,271 @@
 
 . ./test-lib.sh
 
+test_create_repo_with_commit () {
+	test_create_repo "$1" &&
+	(
+		cd "$1" &&
+		: >bar &&
+		git add bar &&
+		git commit -m " Add bar" &&
+		: >foo &&
+		git add foo &&
+		git commit -m " Add foo"
+	)
+}
+
 test_expect_success 'setup' '
-	test_create_repo sub
-	cd sub &&
-	: >bar &&
-	git add bar &&
-	git commit -m " Add bar" &&
-	cd .. &&
-	git add sub &&
+	test_create_repo_with_commit sub &&
+	echo output > .gitignore &&
+	git add sub .gitignore &&
 	git commit -m "Add submodule sub"
 '
 
 test_expect_success 'status clean' '
-	git status |
-	grep "nothing to commit"
+	git status >output &&
+	test_i18ngrep "nothing to commit" output
 '
-test_expect_success 'status -a clean' '
-	git status -a |
-	grep "nothing to commit"
+
+test_expect_success 'commit --dry-run -a clean' '
+	test_must_fail git commit --dry-run -a >output &&
+	test_i18ngrep "nothing to commit" output
 '
+
+test_expect_success 'status with modified file in submodule' '
+	(cd sub && git reset --hard) &&
+	echo "changed" >sub/foo &&
+	git status >output &&
+	test_i18ngrep "modified:   sub (modified content)" output
+'
+
+test_expect_success 'status with modified file in submodule (porcelain)' '
+	(cd sub && git reset --hard) &&
+	echo "changed" >sub/foo &&
+	git status --porcelain >output &&
+	diff output - <<-\EOF
+	 M sub
+	EOF
+'
+
+test_expect_success 'status with added file in submodule' '
+	(cd sub && git reset --hard && echo >foo && git add foo) &&
+	git status >output &&
+	test_i18ngrep "modified:   sub (modified content)" output
+'
+
+test_expect_success 'status with added file in submodule (porcelain)' '
+	(cd sub && git reset --hard && echo >foo && git add foo) &&
+	git status --porcelain >output &&
+	diff output - <<-\EOF
+	 M sub
+	EOF
+'
+
+test_expect_success 'status with untracked file in submodule' '
+	(cd sub && git reset --hard) &&
+	echo "content" >sub/new-file &&
+	git status >output &&
+	test_i18ngrep "modified:   sub (untracked content)" output
+'
+
+test_expect_success 'status -uno with untracked file in submodule' '
+	git status -uno >output &&
+	test_i18ngrep "^nothing to commit" output
+'
+
+test_expect_success 'status with untracked file in submodule (porcelain)' '
+	git status --porcelain >output &&
+	diff output - <<-\EOF
+	 M sub
+	EOF
+'
+
+test_expect_success 'status with added and untracked file in submodule' '
+	(cd sub && git reset --hard && echo >foo && git add foo) &&
+	echo "content" >sub/new-file &&
+	git status >output &&
+	test_i18ngrep "modified:   sub (modified content, untracked content)" output
+'
+
+test_expect_success 'status with added and untracked file in submodule (porcelain)' '
+	(cd sub && git reset --hard && echo >foo && git add foo) &&
+	echo "content" >sub/new-file &&
+	git status --porcelain >output &&
+	diff output - <<-\EOF
+	 M sub
+	EOF
+'
+
+test_expect_success 'status with modified file in modified submodule' '
+	(cd sub && git reset --hard) &&
+	rm sub/new-file &&
+	(cd sub && echo "next change" >foo && git commit -m "next change" foo) &&
+	echo "changed" >sub/foo &&
+	git status >output &&
+	test_i18ngrep "modified:   sub (new commits, modified content)" output
+'
+
+test_expect_success 'status with modified file in modified submodule (porcelain)' '
+	(cd sub && git reset --hard) &&
+	echo "changed" >sub/foo &&
+	git status --porcelain >output &&
+	diff output - <<-\EOF
+	 M sub
+	EOF
+'
+
+test_expect_success 'status with added file in modified submodule' '
+	(cd sub && git reset --hard && echo >foo && git add foo) &&
+	git status >output &&
+	test_i18ngrep "modified:   sub (new commits, modified content)" output
+'
+
+test_expect_success 'status with added file in modified submodule (porcelain)' '
+	(cd sub && git reset --hard && echo >foo && git add foo) &&
+	git status --porcelain >output &&
+	diff output - <<-\EOF
+	 M sub
+	EOF
+'
+
+test_expect_success 'status with untracked file in modified submodule' '
+	(cd sub && git reset --hard) &&
+	echo "content" >sub/new-file &&
+	git status >output &&
+	test_i18ngrep "modified:   sub (new commits, untracked content)" output
+'
+
+test_expect_success 'status with untracked file in modified submodule (porcelain)' '
+	git status --porcelain >output &&
+	diff output - <<-\EOF
+	 M sub
+	EOF
+'
+
+test_expect_success 'status with added and untracked file in modified submodule' '
+	(cd sub && git reset --hard && echo >foo && git add foo) &&
+	echo "content" >sub/new-file &&
+	git status >output &&
+	test_i18ngrep "modified:   sub (new commits, modified content, untracked content)" output
+'
+
+test_expect_success 'status with added and untracked file in modified submodule (porcelain)' '
+	(cd sub && git reset --hard && echo >foo && git add foo) &&
+	echo "content" >sub/new-file &&
+	git status --porcelain >output &&
+	diff output - <<-\EOF
+	 M sub
+	EOF
+'
+
+test_expect_success 'setup .git file for sub' '
+	(cd sub &&
+	 rm -f new-file
+	 REAL="$(pwd)/../.real" &&
+	 mv .git "$REAL"
+	 echo "gitdir: $REAL" >.git) &&
+	 echo .real >>.gitignore &&
+	 git commit -m "added .real to .gitignore" .gitignore
+'
+
+test_expect_success 'status with added file in modified submodule with .git file' '
+	(cd sub && git reset --hard && echo >foo && git add foo) &&
+	git status >output &&
+	test_i18ngrep "modified:   sub (new commits, modified content)" output
+'
+
 test_expect_success 'rm submodule contents' '
 	rm -rf sub/* sub/.git
 '
+
 test_expect_success 'status clean (empty submodule dir)' '
-	git status |
-	grep "nothing to commit"
+	git status >output &&
+	test_i18ngrep "nothing to commit" output
 '
+
 test_expect_success 'status -a clean (empty submodule dir)' '
-	git status -a |
-	grep "nothing to commit"
+	test_must_fail git commit --dry-run -a >output &&
+	test_i18ngrep "nothing to commit" output
+'
+
+cat >status_expect <<\EOF
+AA .gitmodules
+A  sub1
+EOF
+
+test_expect_success 'status with merge conflict in .gitmodules' '
+	git clone . super &&
+	test_create_repo_with_commit sub1 &&
+	test_tick &&
+	test_create_repo_with_commit sub2 &&
+	(
+		cd super &&
+		prev=$(git rev-parse HEAD) &&
+		git checkout -b add_sub1 &&
+		git submodule add ../sub1 &&
+		git commit -m "add sub1" &&
+		git checkout -b add_sub2 $prev &&
+		git submodule add ../sub2 &&
+		git commit -m "add sub2" &&
+		git checkout -b merge_conflict_gitmodules &&
+		test_must_fail git merge add_sub1 &&
+		git status -s >../status_actual 2>&1
+	) &&
+	test_cmp status_actual status_expect
+'
+
+sha1_merge_sub1=$(cd sub1 && git rev-parse HEAD)
+sha1_merge_sub2=$(cd sub2 && git rev-parse HEAD)
+short_sha1_merge_sub1=$(cd sub1 && git rev-parse --short HEAD)
+short_sha1_merge_sub2=$(cd sub2 && git rev-parse --short HEAD)
+cat >diff_expect <<\EOF
+diff --cc .gitmodules
+index badaa4c,44f999a..0000000
+--- a/.gitmodules
++++ b/.gitmodules
+@@@ -1,3 -1,3 +1,9 @@@
+++<<<<<<< HEAD
+ +[submodule "sub2"]
+ +	path = sub2
+ +	url = ../sub2
+++=======
++ [submodule "sub1"]
++ 	path = sub1
++ 	url = ../sub1
+++>>>>>>> add_sub1
+EOF
+
+cat >diff_submodule_expect <<\EOF
+diff --cc .gitmodules
+index badaa4c,44f999a..0000000
+--- a/.gitmodules
++++ b/.gitmodules
+@@@ -1,3 -1,3 +1,9 @@@
+++<<<<<<< HEAD
+ +[submodule "sub2"]
+ +	path = sub2
+ +	url = ../sub2
+++=======
++ [submodule "sub1"]
++ 	path = sub1
++ 	url = ../sub1
+++>>>>>>> add_sub1
+EOF
+
+test_expect_success 'diff with merge conflict in .gitmodules' '
+	(
+		cd super &&
+		git diff >../diff_actual 2>&1
+	) &&
+	test_cmp diff_actual diff_expect
+'
+
+test_expect_success 'diff --submodule with merge conflict in .gitmodules' '
+	(
+		cd super &&
+		git diff --submodule >../diff_submodule_actual 2>&1
+	) &&
+	test_cmp diff_submodule_actual diff_submodule_expect
 '
 
 test_done
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index 93f875f..c206f47 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -7,44 +7,90 @@
 
 . ./test-lib.sh
 
+test_expect_success 'status -h in broken repository' '
+	mkdir broken &&
+	test_when_finished "rm -fr broken" &&
+	(
+		cd broken &&
+		git init &&
+		echo "[status] showuntrackedfiles = CORRUPT" >>.git/config &&
+		test_expect_code 129 git status -h >usage 2>&1
+	) &&
+	test_i18ngrep "[Uu]sage" broken/usage
+'
+
+test_expect_success 'commit -h in broken repository' '
+	mkdir broken &&
+	test_when_finished "rm -fr broken" &&
+	(
+		cd broken &&
+		git init &&
+		echo "[status] showuntrackedfiles = CORRUPT" >>.git/config &&
+		test_expect_code 129 git commit -h >usage 2>&1
+	) &&
+	test_i18ngrep "[Uu]sage" broken/usage
+'
+
 test_expect_success 'setup' '
-	: > tracked &&
-	: > modified &&
+	: >tracked &&
+	: >modified &&
 	mkdir dir1 &&
-	: > dir1/tracked &&
-	: > dir1/modified &&
+	: >dir1/tracked &&
+	: >dir1/modified &&
 	mkdir dir2 &&
-	: > dir1/tracked &&
-	: > dir1/modified &&
+	: >dir1/tracked &&
+	: >dir1/modified &&
 	git add . &&
 
 	git status >output &&
 
 	test_tick &&
 	git commit -m initial &&
-	: > untracked &&
-	: > dir1/untracked &&
-	: > dir2/untracked &&
-	echo 1 > dir1/modified &&
-	echo 2 > dir2/modified &&
-	echo 3 > dir2/added &&
+	: >untracked &&
+	: >dir1/untracked &&
+	: >dir2/untracked &&
+	echo 1 >dir1/modified &&
+	echo 2 >dir2/modified &&
+	echo 3 >dir2/added &&
 	git add dir2/added
 '
 
 test_expect_success 'status (1)' '
-
-	grep "use \"git rm --cached <file>\.\.\.\" to unstage" output
-
+	test_i18ngrep "use \"git rm --cached <file>\.\.\.\" to unstage" output
 '
 
-cat > expect << \EOF
+test_expect_success 'status --column' '
+	COLUMNS=50 git status --column="column dense" >output &&
+	cat >expect <<\EOF &&
 # On branch master
 # Changes to be committed:
 #   (use "git reset HEAD <file>..." to unstage)
 #
 #	new file:   dir2/added
 #
-# Changed but not updated:
+# Changes not staged for commit:
+#   (use "git add <file>..." to update what will be committed)
+#   (use "git checkout -- <file>..." to discard changes in working directory)
+#
+#	modified:   dir1/modified
+#
+# Untracked files:
+#   (use "git add <file>..." to include in what will be committed)
+#
+#	dir1/untracked dir2/untracked untracked
+#	dir2/modified  output
+EOF
+	test_cmp expect output
+'
+
+cat >expect <<\EOF
+# On branch master
+# Changes to be committed:
+#   (use "git reset HEAD <file>..." to unstage)
+#
+#	new file:   dir2/added
+#
+# Changes not staged for commit:
 #   (use "git add <file>..." to update what will be committed)
 #   (use "git checkout -- <file>..." to discard changes in working directory)
 #
@@ -62,12 +108,208 @@
 EOF
 
 test_expect_success 'status (2)' '
+	git status >output &&
+	test_i18ncmp expect output
+'
 
-	git status > output &&
+cat >expect <<\EOF
+# On branch master
+# Changes to be committed:
+#	new file:   dir2/added
+#
+# Changes not staged for commit:
+#	modified:   dir1/modified
+#
+# Untracked files:
+#	dir1/untracked
+#	dir2/modified
+#	dir2/untracked
+#	expect
+#	output
+#	untracked
+EOF
+
+test_expect_success 'status (advice.statusHints false)' '
+	test_when_finished "git config --unset advice.statusHints" &&
+	git config advice.statusHints false &&
+	git status >output &&
+	test_i18ncmp expect output
+
+'
+
+cat >expect <<\EOF
+ M dir1/modified
+A  dir2/added
+?? dir1/untracked
+?? dir2/modified
+?? dir2/untracked
+?? expect
+?? output
+?? untracked
+EOF
+
+test_expect_success 'status -s' '
+
+	git status -s >output &&
 	test_cmp expect output
 
 '
 
+test_expect_success 'status with gitignore' '
+	{
+		echo ".gitignore" &&
+		echo "expect" &&
+		echo "output" &&
+		echo "untracked"
+	} >.gitignore &&
+
+	cat >expect <<-\EOF &&
+	 M dir1/modified
+	A  dir2/added
+	?? dir2/modified
+	EOF
+	git status -s >output &&
+	test_cmp expect output &&
+
+	cat >expect <<-\EOF &&
+	 M dir1/modified
+	A  dir2/added
+	?? dir2/modified
+	!! .gitignore
+	!! dir1/untracked
+	!! dir2/untracked
+	!! expect
+	!! output
+	!! untracked
+	EOF
+	git status -s --ignored >output &&
+	test_cmp expect output &&
+
+	cat >expect <<-\EOF &&
+	# On branch master
+	# Changes to be committed:
+	#   (use "git reset HEAD <file>..." to unstage)
+	#
+	#	new file:   dir2/added
+	#
+	# Changes not staged for commit:
+	#   (use "git add <file>..." to update what will be committed)
+	#   (use "git checkout -- <file>..." to discard changes in working directory)
+	#
+	#	modified:   dir1/modified
+	#
+	# Untracked files:
+	#   (use "git add <file>..." to include in what will be committed)
+	#
+	#	dir2/modified
+	# Ignored files:
+	#   (use "git add -f <file>..." to include in what will be committed)
+	#
+	#	.gitignore
+	#	dir1/untracked
+	#	dir2/untracked
+	#	expect
+	#	output
+	#	untracked
+	EOF
+	git status --ignored >output &&
+	test_i18ncmp expect output
+'
+
+test_expect_success 'status with gitignore (nothing untracked)' '
+	{
+		echo ".gitignore" &&
+		echo "expect" &&
+		echo "dir2/modified" &&
+		echo "output" &&
+		echo "untracked"
+	} >.gitignore &&
+
+	cat >expect <<-\EOF &&
+	 M dir1/modified
+	A  dir2/added
+	EOF
+	git status -s >output &&
+	test_cmp expect output &&
+
+	cat >expect <<-\EOF &&
+	 M dir1/modified
+	A  dir2/added
+	!! .gitignore
+	!! dir1/untracked
+	!! dir2/modified
+	!! dir2/untracked
+	!! expect
+	!! output
+	!! untracked
+	EOF
+	git status -s --ignored >output &&
+	test_cmp expect output &&
+
+	cat >expect <<-\EOF &&
+	# On branch master
+	# Changes to be committed:
+	#   (use "git reset HEAD <file>..." to unstage)
+	#
+	#	new file:   dir2/added
+	#
+	# Changes not staged for commit:
+	#   (use "git add <file>..." to update what will be committed)
+	#   (use "git checkout -- <file>..." to discard changes in working directory)
+	#
+	#	modified:   dir1/modified
+	#
+	# Ignored files:
+	#   (use "git add -f <file>..." to include in what will be committed)
+	#
+	#	.gitignore
+	#	dir1/untracked
+	#	dir2/modified
+	#	dir2/untracked
+	#	expect
+	#	output
+	#	untracked
+	EOF
+	git status --ignored >output &&
+	test_i18ncmp expect output
+'
+
+rm -f .gitignore
+
+cat >expect <<\EOF
+## master
+ M dir1/modified
+A  dir2/added
+?? dir1/untracked
+?? dir2/modified
+?? dir2/untracked
+?? expect
+?? output
+?? untracked
+EOF
+
+test_expect_success 'status -s -b' '
+
+	git status -s -b >output &&
+	test_cmp expect output
+
+'
+
+test_expect_success 'status -s -z -b' '
+	tr "\\n" Q <expect >expect.q &&
+	mv expect.q expect &&
+	git status -s -z -b >output &&
+	nul_to_q <output >output.q &&
+	mv output.q output &&
+	test_cmp expect output
+'
+
+test_expect_success 'setup dir3' '
+	mkdir dir3 &&
+	: >dir3/untracked1 &&
+	: >dir3/untracked2
+'
+
 cat >expect <<EOF
 # On branch master
 # Changes to be committed:
@@ -75,7 +317,7 @@
 #
 #	new file:   dir2/added
 #
-# Changed but not updated:
+# Changes not staged for commit:
 #   (use "git add <file>..." to update what will be committed)
 #   (use "git checkout -- <file>..." to discard changes in working directory)
 #
@@ -84,16 +326,46 @@
 # Untracked files not listed (use -u option to show untracked files)
 EOF
 test_expect_success 'status -uno' '
-	mkdir dir3 &&
-	: > dir3/untracked1 &&
-	: > dir3/untracked2 &&
 	git status -uno >output &&
-	test_cmp expect output
+	test_i18ncmp expect output
 '
 
 test_expect_success 'status (status.showUntrackedFiles no)' '
 	git config status.showuntrackedfiles no
+	test_when_finished "git config --unset status.showuntrackedfiles" &&
 	git status >output &&
+	test_i18ncmp expect output
+'
+
+cat >expect <<EOF
+# On branch master
+# Changes to be committed:
+#	new file:   dir2/added
+#
+# Changes not staged for commit:
+#	modified:   dir1/modified
+#
+# Untracked files not listed
+EOF
+git config advice.statusHints false
+test_expect_success 'status -uno (advice.statusHints false)' '
+	git status -uno >output &&
+	test_i18ncmp expect output
+'
+git config --unset advice.statusHints
+
+cat >expect << EOF
+ M dir1/modified
+A  dir2/added
+EOF
+test_expect_success 'status -s -uno' '
+	git status -s -uno >output &&
+	test_cmp expect output
+'
+
+test_expect_success 'status -s (status.showUntrackedFiles no)' '
+	git config status.showuntrackedfiles no
+	git status -s >output &&
 	test_cmp expect output
 '
 
@@ -104,7 +376,7 @@
 #
 #	new file:   dir2/added
 #
-# Changed but not updated:
+# Changes not staged for commit:
 #   (use "git add <file>..." to update what will be committed)
 #   (use "git checkout -- <file>..." to discard changes in working directory)
 #
@@ -123,12 +395,35 @@
 EOF
 test_expect_success 'status -unormal' '
 	git status -unormal >output &&
-	test_cmp expect output
+	test_i18ncmp expect output
 '
 
 test_expect_success 'status (status.showUntrackedFiles normal)' '
 	git config status.showuntrackedfiles normal
+	test_when_finished "git config --unset status.showuntrackedfiles" &&
 	git status >output &&
+	test_i18ncmp expect output
+'
+
+cat >expect <<EOF
+ M dir1/modified
+A  dir2/added
+?? dir1/untracked
+?? dir2/modified
+?? dir2/untracked
+?? dir3/
+?? expect
+?? output
+?? untracked
+EOF
+test_expect_success 'status -s -unormal' '
+	git status -s -unormal >output &&
+	test_cmp expect output
+'
+
+test_expect_success 'status -s (status.showUntrackedFiles normal)' '
+	git config status.showuntrackedfiles normal
+	git status -s >output &&
 	test_cmp expect output
 '
 
@@ -139,7 +434,7 @@
 #
 #	new file:   dir2/added
 #
-# Changed but not updated:
+# Changes not staged for commit:
 #   (use "git add <file>..." to update what will be committed)
 #   (use "git checkout -- <file>..." to discard changes in working directory)
 #
@@ -159,24 +454,51 @@
 EOF
 test_expect_success 'status -uall' '
 	git status -uall >output &&
-	test_cmp expect output
+	test_i18ncmp expect output
 '
+
 test_expect_success 'status (status.showUntrackedFiles all)' '
 	git config status.showuntrackedfiles all
+	test_when_finished "git config --unset status.showuntrackedfiles" &&
 	git status >output &&
+	test_i18ncmp expect output
+'
+
+test_expect_success 'teardown dir3' '
+	rm -rf dir3
+'
+
+cat >expect <<EOF
+ M dir1/modified
+A  dir2/added
+?? dir1/untracked
+?? dir2/modified
+?? dir2/untracked
+?? expect
+?? output
+?? untracked
+EOF
+test_expect_success 'status -s -uall' '
+	git config --unset status.showuntrackedfiles
+	git status -s -uall >output &&
+	test_cmp expect output
+'
+test_expect_success 'status -s (status.showUntrackedFiles all)' '
+	git config status.showuntrackedfiles all
+	git status -s >output &&
 	rm -rf dir3 &&
 	git config --unset status.showuntrackedfiles &&
 	test_cmp expect output
 '
 
-cat > expect << \EOF
+cat >expect <<\EOF
 # On branch master
 # Changes to be committed:
 #   (use "git reset HEAD <file>..." to unstage)
 #
 #	new file:   ../dir2/added
 #
-# Changed but not updated:
+# Changes not staged for commit:
 #   (use "git add <file>..." to update what will be committed)
 #   (use "git checkout -- <file>..." to discard changes in working directory)
 #
@@ -194,20 +516,190 @@
 EOF
 
 test_expect_success 'status with relative paths' '
+	(cd dir1 && git status) >output &&
+	test_i18ncmp expect output
+'
 
-	(cd dir1 && git status) > output &&
+cat >expect <<\EOF
+ M modified
+A  ../dir2/added
+?? untracked
+?? ../dir2/modified
+?? ../dir2/untracked
+?? ../expect
+?? ../output
+?? ../untracked
+EOF
+test_expect_success 'status -s with relative paths' '
+
+	(cd dir1 && git status -s) >output &&
 	test_cmp expect output
 
 '
 
-cat > expect << \EOF
+cat >expect <<\EOF
+ M dir1/modified
+A  dir2/added
+?? dir1/untracked
+?? dir2/modified
+?? dir2/untracked
+?? expect
+?? output
+?? untracked
+EOF
+
+test_expect_success 'status --porcelain ignores relative paths setting' '
+
+	(cd dir1 && git status --porcelain) >output &&
+	test_cmp expect output
+
+'
+
+test_expect_success 'setup unique colors' '
+
+	git config status.color.untracked blue &&
+	git config status.color.branch green
+
+'
+
+cat >expect <<\EOF
+# On branch <GREEN>master<RESET>
+# Changes to be committed:
+#   (use "git reset HEAD <file>..." to unstage)
+#
+#	<GREEN>new file:   dir2/added<RESET>
+#
+# Changes not staged for commit:
+#   (use "git add <file>..." to update what will be committed)
+#   (use "git checkout -- <file>..." to discard changes in working directory)
+#
+#	<RED>modified:   dir1/modified<RESET>
+#
+# Untracked files:
+#   (use "git add <file>..." to include in what will be committed)
+#
+#	<BLUE>dir1/untracked<RESET>
+#	<BLUE>dir2/modified<RESET>
+#	<BLUE>dir2/untracked<RESET>
+#	<BLUE>expect<RESET>
+#	<BLUE>output<RESET>
+#	<BLUE>untracked<RESET>
+EOF
+
+test_expect_success 'status with color.ui' '
+	git config color.ui always &&
+	test_when_finished "git config --unset color.ui" &&
+	git status | test_decode_color >output &&
+	test_i18ncmp expect output
+'
+
+test_expect_success 'status with color.status' '
+	git config color.status always &&
+	test_when_finished "git config --unset color.status" &&
+	git status | test_decode_color >output &&
+	test_i18ncmp expect output
+'
+
+cat >expect <<\EOF
+ <RED>M<RESET> dir1/modified
+<GREEN>A<RESET>  dir2/added
+<BLUE>??<RESET> dir1/untracked
+<BLUE>??<RESET> dir2/modified
+<BLUE>??<RESET> dir2/untracked
+<BLUE>??<RESET> expect
+<BLUE>??<RESET> output
+<BLUE>??<RESET> untracked
+EOF
+
+test_expect_success 'status -s with color.ui' '
+
+	git config color.ui always &&
+	git status -s | test_decode_color >output &&
+	test_cmp expect output
+
+'
+
+test_expect_success 'status -s with color.status' '
+
+	git config --unset color.ui &&
+	git config color.status always &&
+	git status -s | test_decode_color >output &&
+	test_cmp expect output
+
+'
+
+cat >expect <<\EOF
+## <GREEN>master<RESET>
+ <RED>M<RESET> dir1/modified
+<GREEN>A<RESET>  dir2/added
+<BLUE>??<RESET> dir1/untracked
+<BLUE>??<RESET> dir2/modified
+<BLUE>??<RESET> dir2/untracked
+<BLUE>??<RESET> expect
+<BLUE>??<RESET> output
+<BLUE>??<RESET> untracked
+EOF
+
+test_expect_success 'status -s -b with color.status' '
+
+	git status -s -b | test_decode_color >output &&
+	test_cmp expect output
+
+'
+
+cat >expect <<\EOF
+ M dir1/modified
+A  dir2/added
+?? dir1/untracked
+?? dir2/modified
+?? dir2/untracked
+?? expect
+?? output
+?? untracked
+EOF
+
+test_expect_success 'status --porcelain ignores color.ui' '
+
+	git config --unset color.status &&
+	git config color.ui always &&
+	git status --porcelain | test_decode_color >output &&
+	test_cmp expect output
+
+'
+
+test_expect_success 'status --porcelain ignores color.status' '
+
+	git config --unset color.ui &&
+	git config color.status always &&
+	git status --porcelain | test_decode_color >output &&
+	test_cmp expect output
+
+'
+
+# recover unconditionally from color tests
+git config --unset color.status
+git config --unset color.ui
+
+test_expect_success 'status --porcelain respects -b' '
+
+	git status --porcelain -b >output &&
+	{
+		echo "## master" &&
+		cat expect
+	} >tmp &&
+	mv tmp expect &&
+	test_cmp expect output
+
+'
+
+cat >expect <<\EOF
 # On branch master
 # Changes to be committed:
 #   (use "git reset HEAD <file>..." to unstage)
 #
 #	new file:   dir2/added
 #
-# Changed but not updated:
+# Changes not staged for commit:
 #   (use "git add <file>..." to update what will be committed)
 #   (use "git checkout -- <file>..." to discard changes in working directory)
 #
@@ -224,10 +716,32 @@
 #	untracked
 EOF
 
+
 test_expect_success 'status without relative paths' '
 
-	git config status.relativePaths false
-	(cd dir1 && git status) > output &&
+	git config status.relativePaths false &&
+	test_when_finished "git config --unset status.relativePaths" &&
+	(cd dir1 && git status) >output &&
+	test_i18ncmp expect output
+
+'
+
+cat >expect <<\EOF
+ M dir1/modified
+A  dir2/added
+?? dir1/untracked
+?? dir2/modified
+?? dir2/untracked
+?? expect
+?? output
+?? untracked
+EOF
+
+test_expect_success 'status -s without relative paths' '
+
+	git config status.relativePaths false &&
+	test_when_finished "git config --unset status.relativePaths" &&
+	(cd dir1 && git status -s) >output &&
 	test_cmp expect output
 
 '
@@ -248,8 +762,18 @@
 #	output
 #	untracked
 EOF
-test_expect_success 'status of partial commit excluding new file in index' '
-	git status dir1/modified >output &&
+test_expect_success 'dry-run of partial commit excluding new file in index' '
+	git commit --dry-run dir1/modified >output &&
+	test_i18ncmp expect output
+'
+
+cat >expect <<EOF
+:100644 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0000000000000000000000000000000000000000 M	dir1/modified
+EOF
+test_expect_success 'status refreshes the index' '
+	touch dir2/added &&
+	git status &&
+	git diff-files >output &&
 	test_cmp expect output
 '
 
@@ -271,7 +795,7 @@
 #	new file:   dir2/added
 #	new file:   sm
 #
-# Changed but not updated:
+# Changes not staged for commit:
 #   (use "git add <file>..." to update what will be committed)
 #   (use "git checkout -- <file>..." to discard changes in working directory)
 #
@@ -289,12 +813,34 @@
 EOF
 test_expect_success 'status submodule summary is disabled by default' '
 	git status >output &&
-	test_cmp expect output
+	test_i18ncmp expect output
 '
 
 # we expect the same as the previous test
 test_expect_success 'status --untracked-files=all does not show submodule' '
 	git status --untracked-files=all >output &&
+	test_i18ncmp expect output
+'
+
+cat >expect <<EOF
+ M dir1/modified
+A  dir2/added
+A  sm
+?? dir1/untracked
+?? dir2/modified
+?? dir2/untracked
+?? expect
+?? output
+?? untracked
+EOF
+test_expect_success 'status -s submodule summary is disabled by default' '
+	git status -s >output &&
+	test_cmp expect output
+'
+
+# we expect the same as the previous test
+test_expect_success 'status -s --untracked-files=all does not show submodule' '
+	git status -s --untracked-files=all >output &&
 	test_cmp expect output
 '
 
@@ -308,13 +854,13 @@
 #	new file:   dir2/added
 #	new file:   sm
 #
-# Changed but not updated:
+# Changes not staged for commit:
 #   (use "git add <file>..." to update what will be committed)
 #   (use "git checkout -- <file>..." to discard changes in working directory)
 #
 #	modified:   dir1/modified
 #
-# Modified submodules:
+# Submodule changes to be committed:
 #
 # * sm 0000000...$head (1):
 #   > Add foo
@@ -332,13 +878,28 @@
 test_expect_success 'status submodule summary' '
 	git config status.submodulesummary 10 &&
 	git status >output &&
+	test_i18ncmp expect output
+'
+
+cat >expect <<EOF
+ M dir1/modified
+A  dir2/added
+A  sm
+?? dir1/untracked
+?? dir2/modified
+?? dir2/untracked
+?? expect
+?? output
+?? untracked
+EOF
+test_expect_success 'status -s submodule summary' '
+	git status -s >output &&
 	test_cmp expect output
 '
 
-
 cat >expect <<EOF
 # On branch master
-# Changed but not updated:
+# Changes not staged for commit:
 #   (use "git add <file>..." to update what will be committed)
 #   (use "git checkout -- <file>..." to discard changes in working directory)
 #
@@ -355,10 +916,33 @@
 #	untracked
 no changes added to commit (use "git add" and/or "git commit -a")
 EOF
-test_expect_success 'status submodule summary (clean submodule)' '
+test_expect_success 'status submodule summary (clean submodule): commit' '
 	git commit -m "commit submodule" &&
 	git config status.submodulesummary 10 &&
-	test_must_fail git status >output &&
+	test_must_fail git commit --dry-run >output &&
+	test_i18ncmp expect output &&
+	git status >output &&
+	test_i18ncmp expect output
+'
+
+cat >expect <<EOF
+ M dir1/modified
+?? dir1/untracked
+?? dir2/modified
+?? dir2/untracked
+?? expect
+?? output
+?? untracked
+EOF
+test_expect_success 'status -s submodule summary (clean submodule)' '
+	git status -s >output &&
+	test_cmp expect output
+'
+
+test_expect_success 'status -z implies porcelain' '
+	git status --porcelain |
+	"$PERL_PATH" -pe "s/\012/\000/g" >expect &&
+	git status -z >output &&
 	test_cmp expect output
 '
 
@@ -370,13 +954,13 @@
 #	new file:   dir2/added
 #	new file:   sm
 #
-# Changed but not updated:
+# Changes not staged for commit:
 #   (use "git add <file>..." to update what will be committed)
 #   (use "git checkout -- <file>..." to discard changes in working directory)
 #
 #	modified:   dir1/modified
 #
-# Modified submodules:
+# Submodule changes to be committed:
 #
 # * sm 0000000...$head (1):
 #   > Add foo
@@ -391,10 +975,327 @@
 #	output
 #	untracked
 EOF
-test_expect_success 'status submodule summary (--amend)' '
+test_expect_success 'commit --dry-run submodule summary (--amend)' '
 	git config status.submodulesummary 10 &&
-	git status --amend >output &&
-	test_cmp expect output
+	git commit --dry-run --amend >output &&
+	test_i18ncmp expect output
+'
+
+test_expect_success POSIXPERM,SANITY 'status succeeds in a read-only repository' '
+	(
+		chmod a-w .git &&
+		# make dir1/tracked stat-dirty
+		>dir1/tracked1 && mv -f dir1/tracked1 dir1/tracked &&
+		git status -s >output &&
+		! grep dir1/tracked output &&
+		# make sure "status" succeeded without writing index out
+		git diff-files | grep dir1/tracked
+	)
+	status=$?
+	chmod 775 .git
+	(exit $status)
+'
+
+(cd sm && echo > bar && git add bar && git commit -q -m 'Add bar') && git add sm
+new_head=$(cd sm && git rev-parse --short=7 --verify HEAD)
+touch .gitmodules
+
+cat > expect << EOF
+# On branch master
+# Changes to be committed:
+#   (use "git reset HEAD <file>..." to unstage)
+#
+#	modified:   sm
+#
+# Changes not staged for commit:
+#   (use "git add <file>..." to update what will be committed)
+#   (use "git checkout -- <file>..." to discard changes in working directory)
+#
+#	modified:   dir1/modified
+#
+# Submodule changes to be committed:
+#
+# * sm $head...$new_head (1):
+#   > Add bar
+#
+# Untracked files:
+#   (use "git add <file>..." to include in what will be committed)
+#
+#	.gitmodules
+#	dir1/untracked
+#	dir2/modified
+#	dir2/untracked
+#	expect
+#	output
+#	untracked
+EOF
+
+test_expect_success '--ignore-submodules=untracked suppresses submodules with untracked content' '
+	echo modified  sm/untracked &&
+	git status --ignore-submodules=untracked >output &&
+	test_i18ncmp expect output
+'
+
+test_expect_success '.gitmodules ignore=untracked suppresses submodules with untracked content' '
+	git config diff.ignoreSubmodules dirty &&
+	git status >output &&
+	test_i18ncmp expect output &&
+	git config --add -f .gitmodules submodule.subname.ignore untracked &&
+	git config --add -f .gitmodules submodule.subname.path sm &&
+	git status >output &&
+	test_i18ncmp expect output &&
+	git config -f .gitmodules  --remove-section submodule.subname &&
+	git config --unset diff.ignoreSubmodules
+'
+
+test_expect_success '.git/config ignore=untracked suppresses submodules with untracked content' '
+	git config --add -f .gitmodules submodule.subname.ignore none &&
+	git config --add -f .gitmodules submodule.subname.path sm &&
+	git config --add submodule.subname.ignore untracked &&
+	git config --add submodule.subname.path sm &&
+	git status >output &&
+	test_i18ncmp expect output &&
+	git config --remove-section submodule.subname &&
+	git config --remove-section -f .gitmodules submodule.subname
+'
+
+test_expect_success '--ignore-submodules=dirty suppresses submodules with untracked content' '
+	git status --ignore-submodules=dirty >output &&
+	test_i18ncmp expect output
+'
+
+test_expect_success '.gitmodules ignore=dirty suppresses submodules with untracked content' '
+	git config diff.ignoreSubmodules dirty &&
+	git status >output &&
+	! test -s actual &&
+	git config --add -f .gitmodules submodule.subname.ignore dirty &&
+	git config --add -f .gitmodules submodule.subname.path sm &&
+	git status >output &&
+	test_i18ncmp expect output &&
+	git config -f .gitmodules  --remove-section submodule.subname &&
+	git config --unset diff.ignoreSubmodules
+'
+
+test_expect_success '.git/config ignore=dirty suppresses submodules with untracked content' '
+	git config --add -f .gitmodules submodule.subname.ignore none &&
+	git config --add -f .gitmodules submodule.subname.path sm &&
+	git config --add submodule.subname.ignore dirty &&
+	git config --add submodule.subname.path sm &&
+	git status >output &&
+	test_i18ncmp expect output &&
+	git config --remove-section submodule.subname &&
+	git config -f .gitmodules  --remove-section submodule.subname
+'
+
+test_expect_success '--ignore-submodules=dirty suppresses submodules with modified content' '
+	echo modified >sm/foo &&
+	git status --ignore-submodules=dirty >output &&
+	test_i18ncmp expect output
+'
+
+test_expect_success '.gitmodules ignore=dirty suppresses submodules with modified content' '
+	git config --add -f .gitmodules submodule.subname.ignore dirty &&
+	git config --add -f .gitmodules submodule.subname.path sm &&
+	git status >output &&
+	test_i18ncmp expect output &&
+	git config -f .gitmodules  --remove-section submodule.subname
+'
+
+test_expect_success '.git/config ignore=dirty suppresses submodules with modified content' '
+	git config --add -f .gitmodules submodule.subname.ignore none &&
+	git config --add -f .gitmodules submodule.subname.path sm &&
+	git config --add submodule.subname.ignore dirty &&
+	git config --add submodule.subname.path sm &&
+	git status >output &&
+	test_i18ncmp expect output &&
+	git config --remove-section submodule.subname &&
+	git config -f .gitmodules  --remove-section submodule.subname
+'
+
+cat > expect << EOF
+# On branch master
+# Changes to be committed:
+#   (use "git reset HEAD <file>..." to unstage)
+#
+#	modified:   sm
+#
+# Changes not staged for commit:
+#   (use "git add <file>..." to update what will be committed)
+#   (use "git checkout -- <file>..." to discard changes in working directory)
+#   (commit or discard the untracked or modified content in submodules)
+#
+#	modified:   dir1/modified
+#	modified:   sm (modified content)
+#
+# Submodule changes to be committed:
+#
+# * sm $head...$new_head (1):
+#   > Add bar
+#
+# Untracked files:
+#   (use "git add <file>..." to include in what will be committed)
+#
+#	.gitmodules
+#	dir1/untracked
+#	dir2/modified
+#	dir2/untracked
+#	expect
+#	output
+#	untracked
+EOF
+
+test_expect_success "--ignore-submodules=untracked doesn't suppress submodules with modified content" '
+	git status --ignore-submodules=untracked > output &&
+	test_i18ncmp expect output
+'
+
+test_expect_success ".gitmodules ignore=untracked doesn't suppress submodules with modified content" '
+	git config --add -f .gitmodules submodule.subname.ignore untracked &&
+	git config --add -f .gitmodules submodule.subname.path sm &&
+	git status >output &&
+	test_i18ncmp expect output &&
+	git config -f .gitmodules  --remove-section submodule.subname
+'
+
+test_expect_success ".git/config ignore=untracked doesn't suppress submodules with modified content" '
+	git config --add -f .gitmodules submodule.subname.ignore none &&
+	git config --add -f .gitmodules submodule.subname.path sm &&
+	git config --add submodule.subname.ignore untracked &&
+	git config --add submodule.subname.path sm &&
+	git status >output &&
+	test_i18ncmp expect output &&
+	git config --remove-section submodule.subname &&
+	git config -f .gitmodules  --remove-section submodule.subname
+'
+
+head2=$(cd sm && git commit -q -m "2nd commit" foo && git rev-parse --short=7 --verify HEAD)
+
+cat > expect << EOF
+# On branch master
+# Changes to be committed:
+#   (use "git reset HEAD <file>..." to unstage)
+#
+#	modified:   sm
+#
+# Changes not staged for commit:
+#   (use "git add <file>..." to update what will be committed)
+#   (use "git checkout -- <file>..." to discard changes in working directory)
+#
+#	modified:   dir1/modified
+#	modified:   sm (new commits)
+#
+# Submodule changes to be committed:
+#
+# * sm $head...$new_head (1):
+#   > Add bar
+#
+# Submodules changed but not updated:
+#
+# * sm $new_head...$head2 (1):
+#   > 2nd commit
+#
+# Untracked files:
+#   (use "git add <file>..." to include in what will be committed)
+#
+#	.gitmodules
+#	dir1/untracked
+#	dir2/modified
+#	dir2/untracked
+#	expect
+#	output
+#	untracked
+EOF
+
+test_expect_success "--ignore-submodules=untracked doesn't suppress submodule summary" '
+	git status --ignore-submodules=untracked > output &&
+	test_i18ncmp expect output
+'
+
+test_expect_success ".gitmodules ignore=untracked doesn't suppress submodule summary" '
+	git config --add -f .gitmodules submodule.subname.ignore untracked &&
+	git config --add -f .gitmodules submodule.subname.path sm &&
+	git status >output &&
+	test_i18ncmp expect output &&
+	git config -f .gitmodules  --remove-section submodule.subname
+'
+
+test_expect_success ".git/config ignore=untracked doesn't suppress submodule summary" '
+	git config --add -f .gitmodules submodule.subname.ignore none &&
+	git config --add -f .gitmodules submodule.subname.path sm &&
+	git config --add submodule.subname.ignore untracked &&
+	git config --add submodule.subname.path sm &&
+	git status >output &&
+	test_i18ncmp expect output &&
+	git config --remove-section submodule.subname &&
+	git config -f .gitmodules  --remove-section submodule.subname
+'
+
+test_expect_success "--ignore-submodules=dirty doesn't suppress submodule summary" '
+	git status --ignore-submodules=dirty > output &&
+	test_i18ncmp expect output
+'
+test_expect_success ".gitmodules ignore=dirty doesn't suppress submodule summary" '
+	git config --add -f .gitmodules submodule.subname.ignore dirty &&
+	git config --add -f .gitmodules submodule.subname.path sm &&
+	git status >output &&
+	test_i18ncmp expect output &&
+	git config -f .gitmodules  --remove-section submodule.subname
+'
+
+test_expect_success ".git/config ignore=dirty doesn't suppress submodule summary" '
+	git config --add -f .gitmodules submodule.subname.ignore none &&
+	git config --add -f .gitmodules submodule.subname.path sm &&
+	git config --add submodule.subname.ignore dirty &&
+	git config --add submodule.subname.path sm &&
+	git status >output &&
+	test_i18ncmp expect output &&
+	git config --remove-section submodule.subname &&
+	git config -f .gitmodules  --remove-section submodule.subname
+'
+
+cat > expect << EOF
+# On branch master
+# Changes not staged for commit:
+#   (use "git add <file>..." to update what will be committed)
+#   (use "git checkout -- <file>..." to discard changes in working directory)
+#
+#	modified:   dir1/modified
+#
+# Untracked files:
+#   (use "git add <file>..." to include in what will be committed)
+#
+#	.gitmodules
+#	dir1/untracked
+#	dir2/modified
+#	dir2/untracked
+#	expect
+#	output
+#	untracked
+no changes added to commit (use "git add" and/or "git commit -a")
+EOF
+
+test_expect_success "--ignore-submodules=all suppresses submodule summary" '
+	git status --ignore-submodules=all > output &&
+	test_i18ncmp expect output
+'
+
+test_expect_failure '.gitmodules ignore=all suppresses submodule summary' '
+	git config --add -f .gitmodules submodule.subname.ignore all &&
+	git config --add -f .gitmodules submodule.subname.path sm &&
+	git status > output &&
+	test_cmp expect output &&
+	git config -f .gitmodules  --remove-section submodule.subname
+'
+
+test_expect_failure '.git/config ignore=all suppresses submodule summary' '
+	git config --add -f .gitmodules submodule.subname.ignore none &&
+	git config --add -f .gitmodules submodule.subname.path sm &&
+	git config --add submodule.subname.ignore all &&
+	git config --add submodule.subname.path sm &&
+	git status > output &&
+	test_cmp expect output &&
+	git config --remove-section submodule.subname &&
+	git config -f .gitmodules  --remove-section submodule.subname
 '
 
 test_done
diff --git a/t/t7509-commit.sh b/t/t7509-commit.sh
new file mode 100755
index 0000000..b61fd3c
--- /dev/null
+++ b/t/t7509-commit.sh
@@ -0,0 +1,189 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Erick Mattos
+#
+
+test_description='git commit --reset-author'
+
+. ./test-lib.sh
+
+author_header () {
+	git cat-file commit "$1" |
+	sed -n -e '/^$/q' -e '/^author /p'
+}
+
+message_body () {
+	git cat-file commit "$1" |
+	sed -e '1,/^$/d'
+}
+
+test_expect_success '-C option copies authorship and message' '
+	echo "Initial" >foo &&
+	git add foo &&
+	test_tick &&
+	git commit -m "Initial Commit" --author Frigate\ \<flying@over.world\> &&
+	git tag Initial &&
+	echo "Test 1" >>foo &&
+	test_tick &&
+	git commit -a -C Initial &&
+	author_header Initial >expect &&
+	author_header HEAD >actual &&
+	test_cmp expect actual &&
+
+	message_body Initial >expect &&
+	message_body HEAD >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '-C option copies only the message with --reset-author' '
+	echo "Test 2" >>foo &&
+	test_tick &&
+	git commit -a -C Initial --reset-author &&
+	echo "author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE" >expect &&
+	author_header HEAD >actual &&
+	test_cmp expect actual &&
+
+	message_body Initial >expect &&
+	message_body HEAD >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '-c option copies authorship and message' '
+	echo "Test 3" >>foo &&
+	test_tick &&
+	EDITOR=: VISUAL=: git commit -a -c Initial &&
+	author_header Initial >expect &&
+	author_header HEAD >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '-c option copies only the message with --reset-author' '
+	echo "Test 4" >>foo &&
+	test_tick &&
+	EDITOR=: VISUAL=: git commit -a -c Initial --reset-author &&
+	echo "author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE" >expect &&
+	author_header HEAD >actual &&
+	test_cmp expect actual &&
+
+	message_body Initial >expect &&
+	message_body HEAD >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '--amend option copies authorship' '
+	git checkout Initial &&
+	echo "Test 5" >>foo &&
+	test_tick &&
+	git commit -a --amend -m "amend test" &&
+	author_header Initial >expect &&
+	author_header HEAD >actual &&
+
+	echo "amend test" >expect &&
+	message_body HEAD >actual &&
+	test_cmp expect actual
+'
+
+sha1_file() {
+	echo "$*" | sed "s#..#.git/objects/&/#"
+}
+remove_object() {
+	rm -f $(sha1_file "$*")
+}
+no_reflog() {
+	cp .git/config .git/config.saved &&
+	echo "[core] logallrefupdates = false" >>.git/config &&
+	test_when_finished "mv -f .git/config.saved .git/config" &&
+
+	if test -e .git/logs
+	then
+		mv .git/logs . &&
+		test_when_finished "mv logs .git/"
+	fi
+}
+
+test_expect_success '--amend option with empty author' '
+	git cat-file commit Initial >tmp &&
+	sed "s/author [^<]* </author  </" tmp >empty-author &&
+	no_reflog &&
+	sha=$(git hash-object -t commit -w empty-author) &&
+	test_when_finished "remove_object $sha" &&
+	git checkout $sha &&
+	test_when_finished "git checkout Initial" &&
+	echo "Empty author test" >>foo &&
+	test_tick &&
+	test_must_fail git commit -a -m "empty author" --amend 2>err &&
+	grep "empty ident" err
+'
+
+test_expect_success '--amend option with missing author' '
+	git cat-file commit Initial >tmp &&
+	sed "s/author [^<]* </author </" tmp >malformed &&
+	no_reflog &&
+	sha=$(git hash-object -t commit -w malformed) &&
+	test_when_finished "remove_object $sha" &&
+	git checkout $sha &&
+	test_when_finished "git checkout Initial" &&
+	echo "Missing author test" >>foo &&
+	test_tick &&
+	test_must_fail git commit -a -m "malformed author" --amend 2>err &&
+	grep "empty ident" err
+'
+
+test_expect_success '--reset-author makes the commit ours even with --amend option' '
+	git checkout Initial &&
+	echo "Test 6" >>foo &&
+	test_tick &&
+	git commit -a --reset-author -m "Changed again" --amend &&
+	echo "author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE" >expect &&
+	author_header HEAD >actual &&
+	test_cmp expect actual &&
+
+	echo "Changed again" >expect &&
+	message_body HEAD >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '--reset-author and --author are mutually exclusive' '
+	git checkout Initial &&
+	echo "Test 7" >>foo &&
+	test_tick &&
+	test_must_fail git commit -a --reset-author --author="Xyzzy <frotz@nitfol.xz>"
+'
+
+test_expect_success '--reset-author should be rejected without -c/-C/--amend' '
+	git checkout Initial &&
+	echo "Test 7" >>foo &&
+	test_tick &&
+	test_must_fail git commit -a --reset-author -m done
+'
+
+test_expect_success 'commit respects CHERRY_PICK_HEAD and MERGE_MSG' '
+	echo "cherry-pick 1a" >>foo &&
+	test_tick &&
+	git commit -am "cherry-pick 1" --author="Cherry <cherry@pick.er>" &&
+	git tag cherry-pick-head &&
+	git rev-parse cherry-pick-head >.git/CHERRY_PICK_HEAD &&
+	echo "This is a MERGE_MSG" >.git/MERGE_MSG &&
+	echo "cherry-pick 1b" >>foo &&
+	test_tick &&
+	git commit -a &&
+	author_header cherry-pick-head >expect &&
+	author_header HEAD >actual &&
+	test_cmp expect actual &&
+
+	echo "This is a MERGE_MSG" >expect &&
+	message_body HEAD >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '--reset-author with CHERRY_PICK_HEAD' '
+	git rev-parse cherry-pick-head >.git/CHERRY_PICK_HEAD &&
+	echo "cherry-pick 2" >>foo &&
+	test_tick &&
+	git commit -am "cherry-pick 2" --reset-author &&
+	echo "author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE" >expect &&
+	author_header HEAD >actual &&
+	test_cmp expect actual
+'
+
+test_done
diff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh
new file mode 100755
index 0000000..1d3c56f
--- /dev/null
+++ b/t/t7510-signed-commit.sh
@@ -0,0 +1,80 @@
+#!/bin/sh
+
+test_description='signed commit tests'
+. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-gpg.sh"
+
+test_expect_success GPG 'create signed commits' '
+	echo 1 >file && git add file &&
+	test_tick && git commit -S -m initial &&
+	git tag initial &&
+	git branch side &&
+
+	echo 2 >file && test_tick && git commit -a -S -m second &&
+	git tag second &&
+
+	git checkout side &&
+	echo 3 >elif && git add elif &&
+	test_tick && git commit -m "third on side" &&
+
+	git checkout master &&
+	test_tick && git merge -S side &&
+	git tag merge &&
+
+	echo 4 >file && test_tick && git commit -a -m "fourth unsigned" &&
+	git tag fourth-unsigned &&
+
+	test_tick && git commit --amend -S -m "fourth signed" &&
+	git tag fourth-signed
+'
+
+test_expect_success GPG 'show signatures' '
+	(
+		for commit in initial second merge master
+		do
+			git show --pretty=short --show-signature $commit >actual &&
+			grep "Good signature from" actual || exit 1
+			! grep "BAD signature from" actual || exit 1
+			echo $commit OK
+		done
+	) &&
+	(
+		for commit in merge^2 fourth-unsigned
+		do
+			git show --pretty=short --show-signature $commit >actual &&
+			grep "Good signature from" actual && exit 1
+			! grep "BAD signature from" actual || exit 1
+			echo $commit OK
+		done
+	)
+'
+
+test_expect_success GPG 'detect fudged signature' '
+	git cat-file commit master >raw &&
+
+	sed -e "s/fourth signed/4th forged/" raw >forged1 &&
+	git hash-object -w -t commit forged1 >forged1.commit &&
+	git show --pretty=short --show-signature $(cat forged1.commit) >actual1 &&
+	grep "BAD signature from" actual1 &&
+	! grep "Good signature from" actual1
+'
+
+test_expect_success GPG 'detect fudged signature with NUL' '
+	git cat-file commit master >raw &&
+	cat raw >forged2 &&
+	echo Qwik | tr "Q" "\000" >>forged2 &&
+	git hash-object -w -t commit forged2 >forged2.commit &&
+	git show --pretty=short --show-signature $(cat forged2.commit) >actual2 &&
+	grep "BAD signature from" actual2 &&
+	! grep "Good signature from" actual2
+'
+
+test_expect_success GPG 'amending already signed commit' '
+	git checkout fourth-signed^0 &&
+	git commit --amend -S --no-edit &&
+	git show -s --show-signature HEAD >actual &&
+	grep "Good signature from" actual &&
+	! grep "BAD signature from" actual
+'
+
+test_done
diff --git a/t/t7511-status-index.sh b/t/t7511-status-index.sh
new file mode 100755
index 0000000..b5fdc04
--- /dev/null
+++ b/t/t7511-status-index.sh
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+test_description='git status with certain file name lengths'
+
+. ./test-lib.sh
+
+files="0 1 2 3 4 5 6 7 8 9 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"
+
+check() {
+	len=$1
+	prefix=$2
+
+	for i in $files
+	do
+		: >$prefix$i
+	done
+
+	test_expect_success "status, filename length $len" "
+		git add $prefix* &&
+		git status
+	"
+	rm $prefix* .git/index
+}
+
+check  1
+check  2 p
+check  3 px
+check  4 pre
+check  5 pref
+check  6 prefi
+check  7 prefix
+check  8 prefix-
+check  9 prefix-p
+check 10 prefix-pr
+check 11 prefix-pre
+check 12 prefix-pref
+check 13 prefix-prefi
+check 14 prefix-prefix
+check 15 prefix-prefix-
+check 16 prefix-prefix-p
+check 17 prefix-prefix-pr
+check 18 prefix-prefix-pre
+check 19 prefix-prefix-pref
+check 20 prefix-prefix-prefi
+check 21 prefix-prefix-prefix
+check 22 prefix-prefix-prefix-
+check 23 prefix-prefix-prefix-p
+check 24 prefix-prefix-prefix-pr
+
+test_done
diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh
new file mode 100755
index 0000000..b3f6eb9
--- /dev/null
+++ b/t/t7512-status-help.sh
@@ -0,0 +1,649 @@
+#!/bin/sh
+#
+# Copyright (c) 2012 Valentin Duperray, Lucien Kong, Franck Jonas,
+#		     Thomas Nguy, Khoi Nguyen
+#		     Grenoble INP Ensimag
+#
+
+test_description='git status advices'
+
+. ./test-lib.sh
+
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+set_fake_editor
+
+test_expect_success 'prepare for conflicts' '
+	test_commit init main.txt init &&
+	git branch conflicts &&
+	test_commit on_master main.txt on_master &&
+	git checkout conflicts &&
+	test_commit on_conflicts main.txt on_conflicts
+'
+
+
+test_expect_success 'status when conflicts unresolved' '
+	test_must_fail git merge master &&
+	cat >expected <<-\EOF &&
+	# On branch conflicts
+	# You have unmerged paths.
+	#   (fix conflicts and run "git commit")
+	#
+	# Unmerged paths:
+	#   (use "git add <file>..." to mark resolution)
+	#
+	#	both modified:      main.txt
+	#
+	no changes added to commit (use "git add" and/or "git commit -a")
+	EOF
+	git status --untracked-files=no >actual &&
+	test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status when conflicts resolved before commit' '
+	git reset --hard conflicts &&
+	test_must_fail git merge master &&
+	echo one >main.txt &&
+	git add main.txt &&
+	cat >expected <<-\EOF &&
+	# On branch conflicts
+	# All conflicts fixed but you are still merging.
+	#   (use "git commit" to conclude merge)
+	#
+	# Changes to be committed:
+	#
+	#	modified:   main.txt
+	#
+	# Untracked files not listed (use -u option to show untracked files)
+	EOF
+	git status --untracked-files=no >actual &&
+	test_i18ncmp expected actual
+'
+
+
+test_expect_success 'prepare for rebase conflicts' '
+	git reset --hard master &&
+	git checkout -b rebase_conflicts &&
+	test_commit one_rebase main.txt one &&
+	test_commit two_rebase main.txt two &&
+	test_commit three_rebase main.txt three
+'
+
+
+test_expect_success 'status when rebase in progress before resolving conflicts' '
+	test_when_finished "git rebase --abort" &&
+	test_must_fail git rebase HEAD^ --onto HEAD^^ &&
+	cat >expected <<-\EOF &&
+	# Not currently on any branch.
+	# You are currently rebasing.
+	#   (fix conflicts and then run "git rebase --continue")
+	#   (use "git rebase --skip" to skip this patch)
+	#   (use "git rebase --abort" to check out the original branch)
+	#
+	# Unmerged paths:
+	#   (use "git reset HEAD <file>..." to unstage)
+	#   (use "git add <file>..." to mark resolution)
+	#
+	#	both modified:      main.txt
+	#
+	no changes added to commit (use "git add" and/or "git commit -a")
+	EOF
+	git status --untracked-files=no >actual &&
+	test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status when rebase in progress before rebase --continue' '
+	git reset --hard rebase_conflicts &&
+	test_when_finished "git rebase --abort" &&
+	test_must_fail git rebase HEAD^ --onto HEAD^^ &&
+	echo three >main.txt &&
+	git add main.txt &&
+	cat >expected <<-\EOF &&
+	# Not currently on any branch.
+	# You are currently rebasing.
+	#   (all conflicts fixed: run "git rebase --continue")
+	#
+	# Changes to be committed:
+	#   (use "git reset HEAD <file>..." to unstage)
+	#
+	#	modified:   main.txt
+	#
+	# Untracked files not listed (use -u option to show untracked files)
+	EOF
+	git status --untracked-files=no >actual &&
+	test_i18ncmp expected actual
+'
+
+
+test_expect_success 'prepare for rebase_i_conflicts' '
+	git reset --hard master &&
+	git checkout -b rebase_i_conflicts &&
+	test_commit one_unmerge main.txt one_unmerge &&
+	git branch rebase_i_conflicts_second &&
+	test_commit one_master main.txt one_master &&
+	git checkout rebase_i_conflicts_second &&
+	test_commit one_second main.txt one_second
+'
+
+
+test_expect_success 'status during rebase -i when conflicts unresolved' '
+	test_when_finished "git rebase --abort" &&
+	test_must_fail git rebase -i rebase_i_conflicts &&
+	cat >expected <<-\EOF &&
+	# Not currently on any branch.
+	# You are currently rebasing.
+	#   (fix conflicts and then run "git rebase --continue")
+	#   (use "git rebase --skip" to skip this patch)
+	#   (use "git rebase --abort" to check out the original branch)
+	#
+	# Unmerged paths:
+	#   (use "git reset HEAD <file>..." to unstage)
+	#   (use "git add <file>..." to mark resolution)
+	#
+	#	both modified:      main.txt
+	#
+	no changes added to commit (use "git add" and/or "git commit -a")
+	EOF
+	git status --untracked-files=no >actual &&
+	test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status during rebase -i after resolving conflicts' '
+	git reset --hard rebase_i_conflicts_second &&
+	test_when_finished "git rebase --abort" &&
+	test_must_fail git rebase -i rebase_i_conflicts &&
+	git add main.txt &&
+	cat >expected <<-\EOF &&
+	# Not currently on any branch.
+	# You are currently rebasing.
+	#   (all conflicts fixed: run "git rebase --continue")
+	#
+	# Changes to be committed:
+	#   (use "git reset HEAD <file>..." to unstage)
+	#
+	#	modified:   main.txt
+	#
+	# Untracked files not listed (use -u option to show untracked files)
+	EOF
+	git status --untracked-files=no >actual &&
+	test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status when rebasing -i in edit mode' '
+	git reset --hard master &&
+	git checkout -b rebase_i_edit &&
+	test_commit one_rebase_i main.txt one &&
+	test_commit two_rebase_i main.txt two &&
+	test_commit three_rebase_i main.txt three &&
+	FAKE_LINES="1 edit 2" &&
+	export FAKE_LINES &&
+	test_when_finished "git rebase --abort" &&
+	git rebase -i HEAD~2 &&
+	cat >expected <<-\EOF &&
+	# Not currently on any branch.
+	# You are currently editing a commit during a rebase.
+	#   (use "git commit --amend" to amend the current commit)
+	#   (use "git rebase --continue" once you are satisfied with your changes)
+	#
+	nothing to commit (use -u to show untracked files)
+	EOF
+	git status --untracked-files=no >actual &&
+	test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status when splitting a commit' '
+	git reset --hard master &&
+	git checkout -b split_commit &&
+	test_commit one_split main.txt one &&
+	test_commit two_split main.txt two &&
+	test_commit three_split main.txt three &&
+	test_commit four_split main.txt four &&
+	FAKE_LINES="1 edit 2 3" &&
+	export FAKE_LINES &&
+	test_when_finished "git rebase --abort" &&
+	git rebase -i HEAD~3 &&
+	git reset HEAD^ &&
+	cat >expected <<-\EOF &&
+	# Not currently on any branch.
+	# You are currently splitting a commit during a rebase.
+	#   (Once your working directory is clean, run "git rebase --continue")
+	#
+	# Changes not staged for commit:
+	#   (use "git add <file>..." to update what will be committed)
+	#   (use "git checkout -- <file>..." to discard changes in working directory)
+	#
+	#	modified:   main.txt
+	#
+	no changes added to commit (use "git add" and/or "git commit -a")
+	EOF
+	git status --untracked-files=no >actual &&
+	test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status after editing the last commit with --amend during a rebase -i' '
+	git reset --hard master &&
+	git checkout -b amend_last &&
+	test_commit one_amend main.txt one &&
+	test_commit two_amend main.txt two &&
+	test_commit three_amend main.txt three &&
+	test_commit four_amend main.txt four &&
+	FAKE_LINES="1 2 edit 3" &&
+	export FAKE_LINES &&
+	test_when_finished "git rebase --abort" &&
+	git rebase -i HEAD~3 &&
+	git commit --amend -m "foo" &&
+	cat >expected <<-\EOF &&
+	# Not currently on any branch.
+	# You are currently editing a commit during a rebase.
+	#   (use "git commit --amend" to amend the current commit)
+	#   (use "git rebase --continue" once you are satisfied with your changes)
+	#
+	nothing to commit (use -u to show untracked files)
+	EOF
+	git status --untracked-files=no >actual &&
+	test_i18ncmp expected actual
+'
+
+
+test_expect_success 'prepare for several edits' '
+	git reset --hard master &&
+	git checkout -b several_edits &&
+	test_commit one_edits main.txt one &&
+	test_commit two_edits main.txt two &&
+	test_commit three_edits main.txt three &&
+	test_commit four_edits main.txt four
+'
+
+
+test_expect_success 'status: (continue first edit) second edit' '
+	FAKE_LINES="edit 1 edit 2 3" &&
+	export FAKE_LINES &&
+	test_when_finished "git rebase --abort" &&
+	git rebase -i HEAD~3 &&
+	git rebase --continue &&
+	cat >expected <<-\EOF &&
+	# Not currently on any branch.
+	# You are currently editing a commit during a rebase.
+	#   (use "git commit --amend" to amend the current commit)
+	#   (use "git rebase --continue" once you are satisfied with your changes)
+	#
+	nothing to commit (use -u to show untracked files)
+	EOF
+	git status --untracked-files=no >actual &&
+	test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status: (continue first edit) second edit and split' '
+	git reset --hard several_edits &&
+	FAKE_LINES="edit 1 edit 2 3" &&
+	export FAKE_LINES &&
+	test_when_finished "git rebase --abort" &&
+	git rebase -i HEAD~3 &&
+	git rebase --continue &&
+	git reset HEAD^ &&
+	cat >expected <<-\EOF &&
+	# Not currently on any branch.
+	# You are currently splitting a commit during a rebase.
+	#   (Once your working directory is clean, run "git rebase --continue")
+	#
+	# Changes not staged for commit:
+	#   (use "git add <file>..." to update what will be committed)
+	#   (use "git checkout -- <file>..." to discard changes in working directory)
+	#
+	#	modified:   main.txt
+	#
+	no changes added to commit (use "git add" and/or "git commit -a")
+	EOF
+	git status --untracked-files=no >actual &&
+	test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status: (continue first edit) second edit and amend' '
+	git reset --hard several_edits &&
+	FAKE_LINES="edit 1 edit 2 3" &&
+	export FAKE_LINES &&
+	test_when_finished "git rebase --abort" &&
+	git rebase -i HEAD~3 &&
+	git rebase --continue &&
+	git commit --amend -m "foo" &&
+	cat >expected <<-\EOF &&
+	# Not currently on any branch.
+	# You are currently editing a commit during a rebase.
+	#   (use "git commit --amend" to amend the current commit)
+	#   (use "git rebase --continue" once you are satisfied with your changes)
+	#
+	nothing to commit (use -u to show untracked files)
+	EOF
+	git status --untracked-files=no >actual &&
+	test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status: (amend first edit) second edit' '
+	git reset --hard several_edits &&
+	FAKE_LINES="edit 1 edit 2 3" &&
+	export FAKE_LINES &&
+	test_when_finished "git rebase --abort" &&
+	git rebase -i HEAD~3 &&
+	git commit --amend -m "a" &&
+	git rebase --continue &&
+	cat >expected <<-\EOF &&
+	# Not currently on any branch.
+	# You are currently editing a commit during a rebase.
+	#   (use "git commit --amend" to amend the current commit)
+	#   (use "git rebase --continue" once you are satisfied with your changes)
+	#
+	nothing to commit (use -u to show untracked files)
+	EOF
+	git status --untracked-files=no >actual &&
+	test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status: (amend first edit) second edit and split' '
+	git reset --hard several_edits &&
+	FAKE_LINES="edit 1 edit 2 3" &&
+	export FAKE_LINES &&
+	test_when_finished "git rebase --abort" &&
+	git rebase -i HEAD~3 &&
+	git commit --amend -m "b" &&
+	git rebase --continue &&
+	git reset HEAD^ &&
+	cat >expected <<-\EOF &&
+	# Not currently on any branch.
+	# You are currently splitting a commit during a rebase.
+	#   (Once your working directory is clean, run "git rebase --continue")
+	#
+	# Changes not staged for commit:
+	#   (use "git add <file>..." to update what will be committed)
+	#   (use "git checkout -- <file>..." to discard changes in working directory)
+	#
+	#	modified:   main.txt
+	#
+	no changes added to commit (use "git add" and/or "git commit -a")
+	EOF
+	git status --untracked-files=no >actual &&
+	test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status: (amend first edit) second edit and amend' '
+	git reset --hard several_edits &&
+	FAKE_LINES="edit 1 edit 2 3" &&
+	export FAKE_LINES &&
+	test_when_finished "git rebase --abort" &&
+	git rebase -i HEAD~3 &&
+	git commit --amend -m "c" &&
+	git rebase --continue &&
+	git commit --amend -m "d" &&
+	cat >expected <<-\EOF &&
+	# Not currently on any branch.
+	# You are currently editing a commit during a rebase.
+	#   (use "git commit --amend" to amend the current commit)
+	#   (use "git rebase --continue" once you are satisfied with your changes)
+	#
+	nothing to commit (use -u to show untracked files)
+	EOF
+	git status --untracked-files=no >actual &&
+	test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status: (split first edit) second edit' '
+	git reset --hard several_edits &&
+	FAKE_LINES="edit 1 edit 2 3" &&
+	export FAKE_LINES &&
+	test_when_finished "git rebase --abort" &&
+	git rebase -i HEAD~3 &&
+	git reset HEAD^ &&
+	git add main.txt &&
+	git commit -m "e" &&
+	git rebase --continue &&
+	cat >expected <<-\EOF &&
+	# Not currently on any branch.
+	# You are currently editing a commit during a rebase.
+	#   (use "git commit --amend" to amend the current commit)
+	#   (use "git rebase --continue" once you are satisfied with your changes)
+	#
+	nothing to commit (use -u to show untracked files)
+	EOF
+	git status --untracked-files=no >actual &&
+	test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status: (split first edit) second edit and split' '
+	git reset --hard several_edits &&
+	FAKE_LINES="edit 1 edit 2 3" &&
+	export FAKE_LINES &&
+	test_when_finished "git rebase --abort" &&
+	git rebase -i HEAD~3 &&
+	git reset HEAD^ &&
+	git add main.txt &&
+	git commit --amend -m "f" &&
+	git rebase --continue &&
+	git reset HEAD^ &&
+	cat >expected <<-\EOF &&
+	# Not currently on any branch.
+	# You are currently splitting a commit during a rebase.
+	#   (Once your working directory is clean, run "git rebase --continue")
+	#
+	# Changes not staged for commit:
+	#   (use "git add <file>..." to update what will be committed)
+	#   (use "git checkout -- <file>..." to discard changes in working directory)
+	#
+	#	modified:   main.txt
+	#
+	no changes added to commit (use "git add" and/or "git commit -a")
+	EOF
+	git status --untracked-files=no >actual &&
+	test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status: (split first edit) second edit and amend' '
+	git reset --hard several_edits &&
+	FAKE_LINES="edit 1 edit 2 3" &&
+	export FAKE_LINES &&
+	test_when_finished "git rebase --abort" &&
+	git rebase -i HEAD~3 &&
+	git reset HEAD^ &&
+	git add main.txt &&
+	git commit --amend -m "g" &&
+	git rebase --continue &&
+	git commit --amend -m "h" &&
+	cat >expected <<-\EOF &&
+	# Not currently on any branch.
+	# You are currently editing a commit during a rebase.
+	#   (use "git commit --amend" to amend the current commit)
+	#   (use "git rebase --continue" once you are satisfied with your changes)
+	#
+	nothing to commit (use -u to show untracked files)
+	EOF
+	git status --untracked-files=no >actual &&
+	test_i18ncmp expected actual
+'
+
+
+test_expect_success 'prepare am_session' '
+	git reset --hard master &&
+	git checkout -b am_session &&
+	test_commit one_am one.txt "one" &&
+	test_commit two_am two.txt "two" &&
+	test_commit three_am three.txt "three"
+'
+
+
+test_expect_success 'status in an am session: file already exists' '
+	git checkout -b am_already_exists &&
+	test_when_finished "rm Maildir/* && git am --abort" &&
+	git format-patch -1 -oMaildir &&
+	test_must_fail git am Maildir/*.patch &&
+	cat >expected <<-\EOF &&
+	# On branch am_already_exists
+	# You are in the middle of an am session.
+	#   (fix conflicts and then run "git am --resolved")
+	#   (use "git am --skip" to skip this patch)
+	#   (use "git am --abort" to restore the original branch)
+	#
+	nothing to commit (use -u to show untracked files)
+	EOF
+	git status --untracked-files=no >actual &&
+	test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status in an am session: file does not exist' '
+	git reset --hard am_session &&
+	git checkout -b am_not_exists &&
+	git rm three.txt &&
+	git commit -m "delete three.txt" &&
+	test_when_finished "rm Maildir/* && git am --abort" &&
+	git format-patch -1 -oMaildir &&
+	test_must_fail git am Maildir/*.patch &&
+	cat >expected <<-\EOF &&
+	# On branch am_not_exists
+	# You are in the middle of an am session.
+	#   (fix conflicts and then run "git am --resolved")
+	#   (use "git am --skip" to skip this patch)
+	#   (use "git am --abort" to restore the original branch)
+	#
+	nothing to commit (use -u to show untracked files)
+	EOF
+	git status --untracked-files=no >actual &&
+	test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status in an am session: empty patch' '
+	git reset --hard am_session &&
+	git checkout -b am_empty &&
+	test_when_finished "rm Maildir/* && git am --abort" &&
+	git format-patch -3 -oMaildir &&
+	git rm one.txt two.txt three.txt &&
+	git commit -m "delete all am_empty" &&
+	echo error >Maildir/0002-two_am.patch &&
+	test_must_fail git am Maildir/*.patch &&
+	cat >expected <<-\EOF &&
+	# On branch am_empty
+	# You are in the middle of an am session.
+	# The current patch is empty.
+	#   (use "git am --skip" to skip this patch)
+	#   (use "git am --abort" to restore the original branch)
+	#
+	nothing to commit (use -u to show untracked files)
+	EOF
+	git status --untracked-files=no >actual &&
+	test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status when bisecting' '
+	git reset --hard master &&
+	git checkout -b bisect &&
+	test_commit one_bisect main.txt one &&
+	test_commit two_bisect main.txt two &&
+	test_commit three_bisect main.txt three &&
+	test_when_finished "git bisect reset" &&
+	git bisect start &&
+	git bisect bad &&
+	git bisect good one_bisect &&
+	cat >expected <<-\EOF &&
+	# Not currently on any branch.
+	# You are currently bisecting.
+	#   (use "git bisect reset" to get back to the original branch)
+	#
+	nothing to commit (use -u to show untracked files)
+	EOF
+	git status --untracked-files=no >actual &&
+	test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status when rebase conflicts with statushints disabled' '
+	git reset --hard master &&
+	git checkout -b statushints_disabled &&
+	test_when_finished "git config --local advice.statushints true" &&
+	git config --local advice.statushints false &&
+	test_commit one_statushints main.txt one &&
+	test_commit two_statushints main.txt two &&
+	test_commit three_statushints main.txt three &&
+	test_when_finished "git rebase --abort" &&
+	test_must_fail git rebase HEAD^ --onto HEAD^^ &&
+	cat >expected <<-\EOF &&
+	# Not currently on any branch.
+	# You are currently rebasing.
+	#
+	# Unmerged paths:
+	#	both modified:      main.txt
+	#
+	no changes added to commit
+	EOF
+	git status --untracked-files=no >actual &&
+	test_i18ncmp expected actual
+'
+
+
+test_expect_success 'prepare for cherry-pick conflicts' '
+	git reset --hard master &&
+	git checkout -b cherry_branch &&
+	test_commit one_cherry main.txt one &&
+	test_commit two_cherries main.txt two &&
+	git checkout -b cherry_branch_second &&
+	test_commit second_cherry main.txt second &&
+	git checkout cherry_branch &&
+	test_commit three_cherries main.txt three
+'
+
+
+test_expect_success 'status when cherry-picking before resolving conflicts' '
+	test_when_finished "git cherry-pick --abort" &&
+	test_must_fail git cherry-pick cherry_branch_second &&
+	cat >expected <<-\EOF &&
+	# On branch cherry_branch
+	# You are currently cherry-picking.
+	#   (fix conflicts and run "git commit")
+	#
+	# Unmerged paths:
+	#   (use "git add <file>..." to mark resolution)
+	#
+	#	both modified:      main.txt
+	#
+	no changes added to commit (use "git add" and/or "git commit -a")
+	EOF
+	git status --untracked-files=no >actual &&
+	test_i18ncmp expected actual
+'
+
+
+test_expect_success 'status when cherry-picking after resolving conflicts' '
+	git reset --hard cherry_branch &&
+	test_when_finished "git cherry-pick --abort" &&
+	test_must_fail git cherry-pick cherry_branch_second &&
+	echo end >main.txt &&
+	git add main.txt &&
+	cat >expected <<-\EOF &&
+	# On branch cherry_branch
+	# You are currently cherry-picking.
+	#   (all conflicts fixed: run "git commit")
+	#
+	# Changes to be committed:
+	#
+	#	modified:   main.txt
+	#
+	# Untracked files not listed (use -u option to show untracked files)
+	EOF
+	git status --untracked-files=no >actual &&
+	test_i18ncmp expected actual
+'
+
+
+test_done
diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh
index e5b210b..9e27bbf 100755
--- a/t/t7600-merge.sh
+++ b/t/t7600-merge.sh
@@ -5,190 +5,109 @@
 
 test_description='git merge
 
-Testing basic merge operations/option parsing.'
+Testing basic merge operations/option parsing.
+
+! [c0] commit 0
+ ! [c1] commit 1
+  ! [c2] commit 2
+   ! [c3] commit 3
+    ! [c4] c4
+     ! [c5] c5
+      ! [c6] c6
+       * [master] Merge commit 'c1'
+--------
+       - [master] Merge commit 'c1'
+ +     * [c1] commit 1
+      +  [c6] c6
+     +   [c5] c5
+    ++   [c4] c4
+   ++++  [c3] commit 3
+  +      [c2] commit 2
++++++++* [c0] commit 0
+'
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-gpg.sh
 
-cat >file <<EOF
-1
-2
-3
-4
-5
-6
-7
-8
-9
-EOF
+printf '%s\n' 1 2 3 4 5 6 7 8 9 >file
+printf '%s\n' '1 X' 2 3 4 5 6 7 8 9 >file.1
+printf '%s\n' 1 2 3 4 '5 X' 6 7 8 9 >file.5
+printf '%s\n' 1 2 3 4 5 6 7 8 '9 X' >file.9
+printf '%s\n' '1 X' 2 3 4 5 6 7 8 9 >result.1
+printf '%s\n' '1 X' 2 3 4 '5 X' 6 7 8 9 >result.1-5
+printf '%s\n' '1 X' 2 3 4 '5 X' 6 7 8 '9 X' >result.1-5-9
+>empty
 
-cat >file.1 <<EOF
-1 X
-2
-3
-4
-5
-6
-7
-8
-9
-EOF
-
-cat >file.5 <<EOF
-1
-2
-3
-4
-5 X
-6
-7
-8
-9
-EOF
-
-cat >file.9 <<EOF
-1
-2
-3
-4
-5
-6
-7
-8
-9 X
-EOF
-
-cat  >result.1 <<EOF
-1 X
-2
-3
-4
-5
-6
-7
-8
-9
-EOF
-
-cat >result.1-5 <<EOF
-1 X
-2
-3
-4
-5 X
-6
-7
-8
-9
-EOF
-
-cat >result.1-5-9 <<EOF
-1 X
-2
-3
-4
-5 X
-6
-7
-8
-9 X
-EOF
-
-create_merge_msgs() {
-	echo "Merge commit 'c2'" >msg.1-5 &&
-	echo "Merge commit 'c2'; commit 'c3'" >msg.1-5-9 &&
-	echo "Squashed commit of the following:" >squash.1 &&
-	echo >>squash.1 &&
-	git log --no-merges ^HEAD c1 >>squash.1 &&
-	echo "Squashed commit of the following:" >squash.1-5 &&
-	echo >>squash.1-5 &&
-	git log --no-merges ^HEAD c2 >>squash.1-5 &&
-	echo "Squashed commit of the following:" >squash.1-5-9 &&
-	echo >>squash.1-5-9 &&
-	git log --no-merges ^HEAD c2 c3 >>squash.1-5-9 &&
-	echo > msg.nolog &&
-	echo "* commit 'c3':" >msg.log &&
-	echo "  commit 3" >>msg.log &&
-	echo >>msg.log
+create_merge_msgs () {
+	echo "Merge tag 'c2'" >msg.1-5 &&
+	echo "Merge tags 'c2' and 'c3'" >msg.1-5-9 &&
+	{
+		echo "Squashed commit of the following:" &&
+		echo &&
+		git log --no-merges ^HEAD c1
+	} >squash.1 &&
+	{
+		echo "Squashed commit of the following:" &&
+		echo &&
+		git log --no-merges ^HEAD c2
+	} >squash.1-5 &&
+	{
+		echo "Squashed commit of the following:" &&
+		echo &&
+		git log --no-merges ^HEAD c2 c3
+	} >squash.1-5-9 &&
+	echo >msg.nolog &&
+	{
+		echo "* tag 'c3':" &&
+		echo "  commit 3" &&
+		echo
+	} >msg.log
 }
 
-verify_diff() {
-	if ! test_cmp "$1" "$2"
-	then
-		echo "$3"
-		false
-	fi
-}
-
-verify_merge() {
-	verify_diff "$2" "$1" "[OOPS] bad merge result" &&
-	if test $(git ls-files -u | wc -l) -gt 0
-	then
-		echo "[OOPS] unmerged files"
-		false
-	fi &&
-	if test_must_fail git diff --exit-code
-	then
-		echo "[OOPS] working tree != index"
-		false
-	fi &&
+verify_merge () {
+	test_cmp "$2" "$1" &&
+	git update-index --refresh &&
+	git diff --exit-code &&
 	if test -n "$3"
 	then
 		git show -s --pretty=format:%s HEAD >msg.act &&
-		verify_diff "$3" msg.act "[OOPS] bad merge message"
+		test_cmp "$3" msg.act
 	fi
 }
 
-verify_head() {
-	if test "$1" != "$(git rev-parse HEAD)"
-	then
-		echo "[OOPS] HEAD != $1"
-		false
-	fi
+verify_head () {
+	echo "$1" >head.expected &&
+	git rev-parse HEAD >head.actual &&
+	test_cmp head.expected head.actual
 }
 
-verify_parents() {
-	i=1
-	while test $# -gt 0
+verify_parents () {
+	printf '%s\n' "$@" >parents.expected &&
+	>parents.actual &&
+	i=1 &&
+	while test $i -le $#
 	do
-		if test "$1" != "$(git rev-parse HEAD^$i)"
-		then
-			echo "[OOPS] HEAD^$i != $1"
-			return 1
-		fi
-		i=$(expr $i + 1)
-		shift
-	done
+		git rev-parse HEAD^$i >>parents.actual &&
+		i=$(expr $i + 1) ||
+		return 1
+	done &&
+	test_must_fail git rev-parse --verify "HEAD^$i" &&
+	test_cmp parents.expected parents.actual
 }
 
-verify_mergeheads() {
-	i=1
-	if ! test -f .git/MERGE_HEAD
-	then
-		echo "[OOPS] MERGE_HEAD is missing"
-		false
-	fi &&
-	while test $# -gt 0
+verify_mergeheads () {
+	printf '%s\n' "$@" >mergehead.expected &&
+	while read sha1 rest
 	do
-		head=$(head -n $i .git/MERGE_HEAD | sed -ne \$p)
-		if test "$1" != "$head"
-		then
-			echo "[OOPS] MERGE_HEAD $i != $1"
-			return 1
-		fi
-		i=$(expr $i + 1)
-		shift
-	done
+		git rev-parse $sha1
+	done <.git/MERGE_HEAD >mergehead.actual &&
+	test_cmp mergehead.expected mergehead.actual
 }
 
-verify_no_mergehead() {
-	if test -f .git/MERGE_HEAD
-	then
-		echo "[OOPS] MERGE_HEAD exists"
-		false
-	fi
+verify_no_mergehead () {
+	! test -e .git/MERGE_HEAD
 }
 
-
 test_expect_success 'setup' '
 	git add file &&
 	test_tick &&
@@ -219,7 +138,7 @@
 	create_merge_msgs
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'test option parsing' '
 	test_must_fail git merge -$ c1 &&
@@ -230,18 +149,66 @@
 	test_must_fail git merge
 '
 
+test_expect_success 'merge -h with invalid index' '
+	mkdir broken &&
+	(
+		cd broken &&
+		git init &&
+		>.git/index &&
+		test_expect_code 129 git merge -h 2>usage
+	) &&
+	grep "[Uu]sage: git merge" broken/usage
+'
+
 test_expect_success 'reject non-strategy with a git-merge-foo name' '
 	test_must_fail git merge -s index c1
 '
 
 test_expect_success 'merge c0 with c1' '
+	echo "OBJID HEAD@{0}: merge c1: Fast-forward" >reflog.expected &&
+
 	git reset --hard c0 &&
 	git merge c1 &&
 	verify_merge file result.1 &&
+	verify_head "$c1" &&
+
+	git reflog -1 >reflog.actual &&
+	sed "s/$_x05[0-9a-f]*/OBJID/g" reflog.actual >reflog.fuzzy &&
+	test_cmp reflog.expected reflog.fuzzy
+'
+
+test_debug 'git log --graph --decorate --oneline --all'
+
+test_expect_success 'merge c0 with c1 with --ff-only' '
+	git reset --hard c0 &&
+	git merge --ff-only c1 &&
+	git merge --ff-only HEAD c0 c1 &&
+	verify_merge file result.1 &&
 	verify_head "$c1"
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
+
+test_expect_success 'merge from unborn branch' '
+	git checkout -f master &&
+	test_might_fail git branch -D kid &&
+
+	echo "OBJID HEAD@{0}: initial pull" >reflog.expected &&
+
+	git checkout --orphan kid &&
+	test_when_finished "git checkout -f master" &&
+	git rm -fr . &&
+	test_tick &&
+	git merge --ff-only c1 &&
+	verify_merge file result.1 &&
+	verify_head "$c1" &&
+
+	git reflog -1 >reflog.actual &&
+	sed "s/$_x05[0-9a-f][0-9a-f]/OBJID/g" reflog.actual >reflog.fuzzy &&
+	test_cmp reflog.expected reflog.fuzzy
+'
+
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c2' '
 	git reset --hard c1 &&
@@ -251,7 +218,7 @@
 	verify_parents $c1 $c2
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c2 and c3' '
 	git reset --hard c1 &&
@@ -261,7 +228,31 @@
 	verify_parents $c1 $c2 $c3
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
+
+test_expect_success 'merges with --ff-only' '
+	git reset --hard c1 &&
+	test_tick &&
+	test_must_fail git merge --ff-only c2 &&
+	test_must_fail git merge --ff-only c3 &&
+	test_must_fail git merge --ff-only c2 c3 &&
+	git reset --hard c0 &&
+	git merge c3 &&
+	verify_head $c3
+'
+
+test_expect_success 'merges with merge.ff=only' '
+	git reset --hard c1 &&
+	test_tick &&
+	test_when_finished "git config --unset merge.ff" &&
+	git config merge.ff only &&
+	test_must_fail git merge c2 &&
+	test_must_fail git merge c3 &&
+	test_must_fail git merge c2 c3 &&
+	git reset --hard c0 &&
+	git merge c3 &&
+	verify_head $c3
+'
 
 test_expect_success 'merge c0 with c1 (no-commit)' '
 	git reset --hard c0 &&
@@ -270,7 +261,7 @@
 	verify_head $c1
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c2 (no-commit)' '
 	git reset --hard c1 &&
@@ -280,7 +271,7 @@
 	verify_mergeheads $c2
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c2 and c3 (no-commit)' '
 	git reset --hard c1 &&
@@ -290,7 +281,7 @@
 	verify_mergeheads $c2 $c3
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c0 with c1 (squash)' '
 	git reset --hard c0 &&
@@ -298,10 +289,21 @@
 	verify_merge file result.1 &&
 	verify_head $c0 &&
 	verify_no_mergehead &&
-	verify_diff squash.1 .git/SQUASH_MSG "[OOPS] bad squash message"
+	test_cmp squash.1 .git/SQUASH_MSG
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
+
+test_expect_success 'merge c0 with c1 (squash, ff-only)' '
+	git reset --hard c0 &&
+	git merge --squash --ff-only c1 &&
+	verify_merge file result.1 &&
+	verify_head $c0 &&
+	verify_no_mergehead &&
+	test_cmp squash.1 .git/SQUASH_MSG
+'
+
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c2 (squash)' '
 	git reset --hard c1 &&
@@ -309,10 +311,17 @@
 	verify_merge file result.1-5 &&
 	verify_head $c1 &&
 	verify_no_mergehead &&
-	verify_diff squash.1-5 .git/SQUASH_MSG "[OOPS] bad squash message"
+	test_cmp squash.1-5 .git/SQUASH_MSG
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
+
+test_expect_success 'unsuccesful merge of c1 with c2 (squash, ff-only)' '
+	git reset --hard c1 &&
+	test_must_fail git merge --squash --ff-only c2
+'
+
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c2 and c3 (squash)' '
 	git reset --hard c1 &&
@@ -320,10 +329,10 @@
 	verify_merge file result.1-5-9 &&
 	verify_head $c1 &&
 	verify_no_mergehead &&
-	verify_diff squash.1-5-9 .git/SQUASH_MSG "[OOPS] bad squash message"
+	test_cmp squash.1-5-9 .git/SQUASH_MSG
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c2 (no-commit in config)' '
 	git reset --hard c1 &&
@@ -334,7 +343,40 @@
 	verify_mergeheads $c2
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
+
+test_expect_success 'merge c1 with c2 (log in config)' '
+	git config branch.master.mergeoptions "" &&
+	git reset --hard c1 &&
+	git merge --log c2 &&
+	git show -s --pretty=tformat:%s%n%b >expect &&
+
+	git config branch.master.mergeoptions --log &&
+	git reset --hard c1 &&
+	git merge c2 &&
+	git show -s --pretty=tformat:%s%n%b >actual &&
+
+	test_cmp expect actual
+'
+
+test_expect_success 'merge c1 with c2 (log in config gets overridden)' '
+	test_when_finished "git config --remove-section branch.master" &&
+	test_when_finished "git config --remove-section merge" &&
+	test_might_fail git config --remove-section branch.master &&
+	test_might_fail git config --remove-section merge &&
+
+	git reset --hard c1 &&
+	git merge c2 &&
+	git show -s --pretty=tformat:%s%n%b >expect &&
+
+	git config branch.master.mergeoptions "--no-log" &&
+	git config merge.log true &&
+	git reset --hard c1 &&
+	git merge c2 &&
+	git show -s --pretty=tformat:%s%n%b >actual &&
+
+	test_cmp expect actual
+'
 
 test_expect_success 'merge c1 with c2 (squash in config)' '
 	git reset --hard c1 &&
@@ -343,10 +385,10 @@
 	verify_merge file result.1-5 &&
 	verify_head $c1 &&
 	verify_no_mergehead &&
-	verify_diff squash.1-5 .git/SQUASH_MSG "[OOPS] bad squash message"
+	test_cmp squash.1-5 .git/SQUASH_MSG
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'override config option -n with --summary' '
 	git reset --hard c1 &&
@@ -376,7 +418,7 @@
 	fi
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'override config option --stat' '
 	git reset --hard c1 &&
@@ -392,7 +434,7 @@
 	fi
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c2 (override --no-commit)' '
 	git reset --hard c1 &&
@@ -403,7 +445,7 @@
 	verify_parents $c1 $c2
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c2 (override --squash)' '
 	git reset --hard c1 &&
@@ -414,7 +456,7 @@
 	verify_parents $c1 $c2
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c0 with c1 (no-ff)' '
 	git reset --hard c0 &&
@@ -425,13 +467,52 @@
 	verify_parents $c0 $c1
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
+
+test_expect_success 'merge c0 with c1 (merge.ff=false)' '
+	git reset --hard c0 &&
+	git config merge.ff false &&
+	test_tick &&
+	git merge c1 &&
+	git config --remove-section merge &&
+	verify_merge file result.1 &&
+	verify_parents $c0 $c1
+'
+test_debug 'git log --graph --decorate --oneline --all'
+
+test_expect_success 'combine branch.master.mergeoptions with merge.ff' '
+	git reset --hard c0 &&
+	git config branch.master.mergeoptions --ff &&
+	git config merge.ff false &&
+	test_tick &&
+	git merge c1 &&
+	git config --remove-section "branch.master" &&
+	git config --remove-section "merge" &&
+	verify_merge file result.1 &&
+	verify_parents "$c0"
+'
+
+test_expect_success 'tolerate unknown values for merge.ff' '
+	git reset --hard c0 &&
+	git config merge.ff something-new &&
+	test_tick &&
+	git merge c1 2>message &&
+	git config --remove-section "merge" &&
+	verify_head "$c1" &&
+	test_cmp empty message
+'
 
 test_expect_success 'combining --squash and --no-ff is refused' '
+	git reset --hard c0 &&
 	test_must_fail git merge --squash --no-ff c1 &&
 	test_must_fail git merge --no-ff --squash c1
 '
 
+test_expect_success 'combining --ff-only and --no-ff is refused' '
+	test_must_fail git merge --ff-only --no-ff c1 &&
+	test_must_fail git merge --no-ff --ff-only c1
+'
+
 test_expect_success 'merge c0 with c1 (ff overrides no-ff)' '
 	git reset --hard c0 &&
 	git config branch.master.mergeoptions "--no-ff" &&
@@ -444,20 +525,20 @@
 	git reset --hard c0 &&
 	git merge --no-log c2 &&
 	git show -s --pretty=format:%b HEAD >msg.act &&
-	verify_diff msg.nolog msg.act "[OOPS] bad merge log message" &&
+	test_cmp msg.nolog msg.act &&
 
 	git merge --log c3 &&
 	git show -s --pretty=format:%b HEAD >msg.act &&
-	verify_diff msg.log msg.act "[OOPS] bad merge log message" &&
+	test_cmp msg.log msg.act &&
 
 	git reset --hard HEAD^ &&
 	git config merge.log yes &&
 	git merge c3 &&
 	git show -s --pretty=format:%b HEAD >msg.act &&
-	verify_diff msg.log msg.act "[OOPS] bad merge log message"
+	test_cmp msg.log msg.act
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c0, c2, c0, and c1' '
        git reset --hard c1 &&
@@ -468,7 +549,7 @@
        verify_parents $c1 $c2
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c0, c2, c0, and c1' '
        git reset --hard c1 &&
@@ -479,7 +560,7 @@
        verify_parents $c1 $c2
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c1 and c2' '
        git reset --hard c1 &&
@@ -490,7 +571,7 @@
        verify_parents $c1 $c2
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge fast-forward in a dirty tree' '
        git reset --hard c0 &&
@@ -500,49 +581,56 @@
        git merge c2
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'in-index merge' '
 	git reset --hard c0 &&
-	git merge --no-ff -s resolve c1 > out &&
-	grep "Wonderful." out &&
+	git merge --no-ff -s resolve c1 >out &&
+	test_i18ngrep "Wonderful." out &&
 	verify_parents $c0 $c1
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'refresh the index before merging' '
 	git reset --hard c1 &&
-	sleep 1 &&
-	touch file &&
+	cp file file.n && mv -f file.n file &&
 	git merge c3
 '
 
-cat >expected <<EOF
-Merge branch 'c5' (early part)
+cat >expected.branch <<\EOF
+Merge branch 'c5-branch' (early part)
+EOF
+cat >expected.tag <<\EOF
+Merge commit 'c5~1'
 EOF
 
 test_expect_success 'merge early part of c2' '
 	git reset --hard c3 &&
-	echo c4 > c4.c &&
+	echo c4 >c4.c &&
 	git add c4.c &&
 	git commit -m c4 &&
 	git tag c4 &&
-	echo c5 > c5.c &&
+	echo c5 >c5.c &&
 	git add c5.c &&
 	git commit -m c5 &&
 	git tag c5 &&
 	git reset --hard c3 &&
-	echo c6 > c6.c &&
+	echo c6 >c6.c &&
 	git add c6.c &&
 	git commit -m c6 &&
 	git tag c6 &&
+	git branch -f c5-branch c5 &&
+	git merge c5-branch~1 &&
+	git show -s --pretty=format:%s HEAD >actual.branch &&
+	git reset --keep HEAD^ &&
 	git merge c5~1 &&
-	git show -s --pretty=format:%s HEAD > actual &&
-	test_cmp actual expected
+	git show -s --pretty=format:%s HEAD >actual.tag &&
+	test_cmp expected.branch actual.branch &&
+	test_cmp expected.tag actual.tag
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge --no-ff --no-commit && commit' '
 	git reset --hard c0 &&
@@ -551,13 +639,60 @@
 	verify_parents $c0 $c1
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'amending no-ff merge commit' '
 	EDITOR=: git commit --amend &&
 	verify_parents $c0 $c1
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
+
+cat >editor <<\EOF
+#!/bin/sh
+# Add a new message string that was not in the template
+(
+	echo "Merge work done on the side branch c1"
+	echo
+	cat <"$1"
+) >"$1.tmp" && mv "$1.tmp" "$1"
+# strip comments and blank lines from end of message
+sed -e '/^#/d' < "$1" | sed -e :a -e '/^\n*$/{$d;N;ba' -e '}' > expected
+EOF
+chmod 755 editor
+
+test_expect_success 'merge --no-ff --edit' '
+	git reset --hard c0 &&
+	EDITOR=./editor git merge --no-ff --edit c1 &&
+	verify_parents $c0 $c1 &&
+	git cat-file commit HEAD >raw &&
+	grep "work done on the side branch" raw &&
+	sed "1,/^$/d" >actual raw &&
+	test_cmp actual expected
+'
+
+test_expect_success GPG 'merge --ff-only tag' '
+	git reset --hard c0 &&
+	git commit --allow-empty -m "A newer commit" &&
+	git tag -s -m "A newer commit" signed &&
+	git reset --hard c0 &&
+
+	git merge --ff-only signed &&
+	git rev-parse signed^0 >expect &&
+	git rev-parse HEAD >actual &&
+	test_cmp actual expect
+'
+
+test_expect_success GPG 'merge --no-edit tag should skip editor' '
+	git reset --hard c0 &&
+	git commit --allow-empty -m "A newer commit" &&
+	git tag -f -s -m "A newer commit" signed &&
+	git reset --hard c0 &&
+
+	EDITOR=false git merge --no-edit signed &&
+	git rev-parse signed^0 >expect &&
+	git rev-parse HEAD^2 >actual &&
+	test_cmp actual expect
+'
 
 test_done
diff --git a/t/t7601-merge-pull-config.sh b/t/t7601-merge-pull-config.sh
index 7ba94ea..b44b293 100755
--- a/t/t7601-merge-pull-config.sh
+++ b/t/t7601-merge-pull-config.sh
@@ -114,13 +114,13 @@
 test_expect_success 'merge picks up the best result' '
 	git config --unset-all pull.twohead &&
 	git reset --hard c5 &&
-	git merge -s resolve c6
+	test_must_fail git merge -s resolve c6 &&
 	resolve_count=$(conflict_count) &&
 	git reset --hard c5 &&
-	git merge -s recursive c6
+	test_must_fail git merge -s recursive c6 &&
 	recursive_count=$(conflict_count) &&
 	git reset --hard c5 &&
-	git merge -s recursive -s resolve c6
+	test_must_fail git merge -s recursive -s resolve c6 &&
 	auto_count=$(conflict_count) &&
 	test $auto_count = $recursive_count &&
 	test $auto_count != $resolve_count
@@ -129,13 +129,13 @@
 test_expect_success 'merge picks up the best result (from config)' '
 	git config pull.twohead "recursive resolve" &&
 	git reset --hard c5 &&
-	git merge -s resolve c6
+	test_must_fail git merge -s resolve c6 &&
 	resolve_count=$(conflict_count) &&
 	git reset --hard c5 &&
-	git merge -s recursive c6
+	test_must_fail git merge -s recursive c6 &&
 	recursive_count=$(conflict_count) &&
 	git reset --hard c5 &&
-	git merge c6
+	test_must_fail git merge c6 &&
 	auto_count=$(conflict_count) &&
 	test $auto_count = $recursive_count &&
 	test $auto_count != $resolve_count
diff --git a/t/t7602-merge-octopus-many.sh b/t/t7602-merge-octopus-many.sh
index 01e5415..955f09f 100755
--- a/t/t7602-merge-octopus-many.sh
+++ b/t/t7602-merge-octopus-many.sh
@@ -31,7 +31,7 @@
 	do
 		refs="$refs c$i"
 		i=`expr $i + 1`
-	done
+	done &&
 	git merge $refs &&
 	test "$(git rev-parse c1)" != "$(git rev-parse HEAD)" &&
 	i=1 &&
@@ -49,4 +49,53 @@
 	done
 '
 
+cat >expected <<\EOF
+Trying simple merge with c2
+Trying simple merge with c3
+Trying simple merge with c4
+Merge made by the 'octopus' strategy.
+ c2.c | 1 +
+ c3.c | 1 +
+ c4.c | 1 +
+ 3 files changed, 3 insertions(+)
+ create mode 100644 c2.c
+ create mode 100644 c3.c
+ create mode 100644 c4.c
+EOF
+
+test_expect_success 'merge output uses pretty names' '
+	git reset --hard c1 &&
+	git merge c2 c3 c4 >actual &&
+	test_i18ncmp expected actual
+'
+
+cat >expected <<\EOF
+Merge made by the 'recursive' strategy.
+ c5.c | 1 +
+ 1 file changed, 1 insertion(+)
+ create mode 100644 c5.c
+EOF
+
+test_expect_success 'merge reduces irrelevant remote heads' '
+	GIT_MERGE_VERBOSITY=0 git merge c4 c5 >actual &&
+	test_i18ncmp expected actual
+'
+
+cat >expected <<\EOF
+Fast-forwarding to: c1
+Trying simple merge with c2
+Merge made by the 'octopus' strategy.
+ c1.c | 1 +
+ c2.c | 1 +
+ 2 files changed, 2 insertions(+)
+ create mode 100644 c1.c
+ create mode 100644 c2.c
+EOF
+
+test_expect_success 'merge fast-forward output uses pretty names' '
+	git reset --hard c0 &&
+	git merge c1 c2 >actual &&
+	test_i18ncmp expected actual
+'
+
 test_done
diff --git a/t/t7603-merge-reduce-heads.sh b/t/t7603-merge-reduce-heads.sh
index 7e17eb4..9894895 100755
--- a/t/t7603-merge-reduce-heads.sh
+++ b/t/t7603-merge-reduce-heads.sh
@@ -57,7 +57,36 @@
 	test -f c2.c &&
 	test -f c3.c &&
 	test -f c4.c &&
-	test -f c5.c
+	test -f c5.c &&
+	git show --format=%s -s >actual &&
+	! grep c1 actual &&
+	grep c2 actual &&
+	grep c3 actual &&
+	! grep c4 actual &&
+	grep c5 actual
+'
+
+test_expect_success 'pull c2, c3, c4, c5 into c1' '
+	git reset --hard c1 &&
+	git pull . c2 c3 c4 c5 &&
+	test "$(git rev-parse c1)" != "$(git rev-parse HEAD)" &&
+	test "$(git rev-parse c1)" = "$(git rev-parse HEAD^1)" &&
+	test "$(git rev-parse c2)" = "$(git rev-parse HEAD^2)" &&
+	test "$(git rev-parse c3)" = "$(git rev-parse HEAD^3)" &&
+	test "$(git rev-parse c5)" = "$(git rev-parse HEAD^4)" &&
+	git diff --exit-code &&
+	test -f c0.c &&
+	test -f c1.c &&
+	test -f c2.c &&
+	test -f c3.c &&
+	test -f c4.c &&
+	test -f c5.c &&
+	git show --format=%s -s >actual &&
+	! grep c1 actual &&
+	grep c2 actual &&
+	grep c3 actual &&
+	! grep c4 actual &&
+	grep c5 actual
 '
 
 test_expect_success 'setup' '
@@ -113,4 +142,23 @@
 	test $(git rev-parse HEAD^1) = $(git rev-parse E2) &&
 	test $(git rev-parse HEAD^2) = $(git rev-parse I2)
 '
+
+test_expect_success 'fast-forward to redundant refs' '
+	git reset --hard c0 &&
+	git merge c4 c5
+'
+
+test_expect_success 'verify merge result' '
+	test $(git rev-parse HEAD) = $(git rev-parse c5)
+'
+
+test_expect_success 'merge up-to-date redundant refs' '
+	git reset --hard c5 &&
+	git merge c0 c4
+'
+
+test_expect_success 'verify merge result' '
+	test $(git rev-parse HEAD) = $(git rev-parse c5)
+'
+
 test_done
diff --git a/t/t7604-merge-custom-message.sh b/t/t7604-merge-custom-message.sh
index de977c5..89619cf 100755
--- a/t/t7604-merge-custom-message.sh
+++ b/t/t7604-merge-custom-message.sh
@@ -6,6 +6,15 @@
 
 . ./test-lib.sh
 
+create_merge_msgs() {
+	echo >exp.subject "custom message"
+
+	cp exp.subject exp.log &&
+	echo >>exp.log "" &&
+	echo >>exp.log "* tag 'c2':" &&
+	echo >>exp.log "  c2"
+}
+
 test_expect_success 'setup' '
 	echo c0 > c0.c &&
 	git add c0.c &&
@@ -19,19 +28,23 @@
 	echo c2 > c2.c &&
 	git add c2.c &&
 	git commit -m c2 &&
-	git tag c2
+	git tag c2 &&
+	create_merge_msgs
 '
 
-cat >expected <<\EOF
-custom message
 
-Merge commit 'c2'
-EOF
 test_expect_success 'merge c2 with a custom message' '
 	git reset --hard c1 &&
-	git merge -m "custom message" c2 &&
-	git cat-file commit HEAD | sed -e "1,/^$/d" > actual &&
-	test_cmp expected actual
+	git merge -m "$(cat exp.subject)" c2 &&
+	git cat-file commit HEAD | sed -e "1,/^$/d" >actual &&
+	test_cmp exp.subject actual
+'
+
+test_expect_success 'merge --log appends to custom message' '
+	git reset --hard c1 &&
+	git merge --log -m "$(cat exp.subject)" c2 &&
+	git cat-file commit HEAD | sed -e "1,/^$/d" >actual &&
+	test_cmp exp.log actual
 '
 
 test_done
diff --git a/t/t7606-merge-custom.sh b/t/t7606-merge-custom.sh
index 52a451d..8e8c4d7 100755
--- a/t/t7606-merge-custom.sh
+++ b/t/t7606-merge-custom.sh
@@ -1,49 +1,93 @@
 #!/bin/sh
 
-test_description='git merge
+test_description="git merge
 
-Testing a custom strategy.'
+Testing a custom strategy.
+
+*   (HEAD, master) Merge commit 'c3'
+|\
+| * (tag: c3) c3
+* | (tag: c1) c1
+|/
+| * tag: c2) c2
+|/
+* (tag: c0) c0
+"
 
 . ./test-lib.sh
 
-cat >git-merge-theirs <<EOF
-#!$SHELL_PATH
-eval git read-tree --reset -u \\\$\$#
-EOF
-chmod +x git-merge-theirs
-PATH=.:$PATH
-export PATH
+test_expect_success 'set up custom strategy' '
+	cat >git-merge-theirs <<-EOF &&
+	#!$SHELL_PATH
+	eval git read-tree --reset -u \\\$\$#
+	EOF
+
+	chmod +x git-merge-theirs &&
+	PATH=.:$PATH &&
+	export PATH
+'
 
 test_expect_success 'setup' '
-	echo c0 >c0.c &&
-	git add c0.c &&
-	git commit -m c0 &&
-	git tag c0 &&
-	echo c1 >c1.c &&
-	git add c1.c &&
-	git commit -m c1 &&
-	git tag c1 &&
-	git reset --hard c0 &&
+	test_commit c0 c0.c &&
+	test_commit c1 c1.c &&
+	git reset --keep c0 &&
 	echo c1c1 >c1.c &&
-	echo c2 >c2.c &&
-	git add c1.c c2.c &&
-	git commit -m c2 &&
-	git tag c2
+	git add c1.c &&
+	test_commit c2 c2.c &&
+	git reset --keep c0 &&
+	test_commit c3 c3.c
 '
 
 test_expect_success 'merge c2 with a custom strategy' '
 	git reset --hard c1 &&
+
+	git rev-parse c1 >head.old &&
+	git rev-parse c2 >second-parent.expected &&
+	git rev-parse c2^{tree} >tree.expected &&
 	git merge -s theirs c2 &&
-	test "$(git rev-parse c1)" != "$(git rev-parse HEAD)" &&
-	test "$(git rev-parse c1)" = "$(git rev-parse HEAD^1)" &&
-	test "$(git rev-parse c2)" = "$(git rev-parse HEAD^2)" &&
-	test "$(git rev-parse c2^{tree})" = "$(git rev-parse HEAD^{tree})" &&
+
+	git rev-parse HEAD >head.new &&
+	git rev-parse HEAD^1 >first-parent &&
+	git rev-parse HEAD^2 >second-parent &&
+	git rev-parse HEAD^{tree} >tree &&
+	git update-index --refresh &&
 	git diff --exit-code &&
 	git diff --exit-code c2 HEAD &&
 	git diff --exit-code c2 &&
+
+	! test_cmp head.old head.new &&
+	test_cmp head.old first-parent &&
+	test_cmp second-parent.expected second-parent &&
+	test_cmp tree.expected tree &&
 	test -f c0.c &&
 	grep c1c1 c1.c &&
 	test -f c2.c
 '
 
+test_expect_success 'trivial merge with custom strategy' '
+	git reset --hard c1 &&
+
+	git rev-parse c1 >head.old &&
+	git rev-parse c3 >second-parent.expected &&
+	git rev-parse c3^{tree} >tree.expected &&
+	git merge -s theirs c3 &&
+
+	git rev-parse HEAD >head.new &&
+	git rev-parse HEAD^1 >first-parent &&
+	git rev-parse HEAD^2 >second-parent &&
+	git rev-parse HEAD^{tree} >tree &&
+	git update-index --refresh &&
+	git diff --exit-code &&
+	git diff --exit-code c3 HEAD &&
+	git diff --exit-code c3 &&
+
+	! test_cmp head.old head.new &&
+	test_cmp head.old first-parent &&
+	test_cmp second-parent.expected second-parent &&
+	test_cmp tree.expected tree &&
+	test -f c0.c &&
+	! test -e c1.c &&
+	test -f c3.c
+'
+
 test_done
diff --git a/t/t7607-merge-overwrite.sh b/t/t7607-merge-overwrite.sh
index 49f4e15..6547eb8 100755
--- a/t/t7607-merge-overwrite.sh
+++ b/t/t7607-merge-overwrite.sh
@@ -7,48 +7,54 @@
 . ./test-lib.sh
 
 test_expect_success 'setup' '
-	echo c0 > c0.c &&
-	git add c0.c &&
-	git commit -m c0 &&
-	git tag c0 &&
-	echo c1 > c1.c &&
-	git add c1.c &&
-	git commit -m c1 &&
-	git tag c1 &&
+	test_commit c0 c0.c &&
+	test_commit c1 c1.c &&
+	test_commit c1a c1.c "c1 a" &&
 	git reset --hard c0 &&
-	echo c2 > c2.c &&
-	git add c2.c &&
-	git commit -m c2 &&
-	git tag c2 &&
-	git reset --hard c1 &&
-	echo "c1 a" > c1.c &&
-	git add c1.c &&
-	git commit -m "c1 a" &&
-	git tag c1a &&
+	test_commit c2 c2.c &&
+	git reset --hard c0 &&
+	mkdir sub &&
+	echo "sub/f" > sub/f &&
+	mkdir sub2 &&
+	echo "sub2/f" > sub2/f &&
+	git add sub/f sub2/f &&
+	git commit -m sub &&
+	git tag sub &&
 	echo "VERY IMPORTANT CHANGES" > important
 '
 
 test_expect_success 'will not overwrite untracked file' '
 	git reset --hard c1 &&
-	cat important > c2.c &&
-	! git merge c2 &&
+	cp important c2.c &&
+	test_must_fail git merge c2 &&
+	test_path_is_missing .git/MERGE_HEAD &&
 	test_cmp important c2.c
 '
 
+test_expect_success 'will overwrite tracked file' '
+	git reset --hard c1 &&
+	cp important c2.c &&
+	git add c2.c &&
+	git commit -m important &&
+	git checkout c2
+'
+
 test_expect_success 'will not overwrite new file' '
 	git reset --hard c1 &&
-	cat important > c2.c &&
+	cp important c2.c &&
 	git add c2.c &&
-	! git merge c2 &&
+	test_must_fail git merge c2 &&
+	test_path_is_missing .git/MERGE_HEAD &&
 	test_cmp important c2.c
 '
 
 test_expect_success 'will not overwrite staged changes' '
 	git reset --hard c1 &&
-	cat important > c2.c &&
+	cp important c2.c &&
 	git add c2.c &&
 	rm c2.c &&
-	! git merge c2 &&
+	test_must_fail git merge c2 &&
+	test_path_is_missing .git/MERGE_HEAD &&
 	git checkout c2.c &&
 	test_cmp important c2.c
 '
@@ -57,8 +63,8 @@
 	git reset --hard c1 &&
 	git rm c1.c &&
 	git commit -m "rm c1.c" &&
-	cat important > c1.c &&
-	! git merge c1a &&
+	cp important c1.c &&
+	test_must_fail git merge c1a &&
 	test_cmp important c1.c
 '
 
@@ -66,9 +72,10 @@
 	git reset --hard c1 &&
 	git rm c1.c &&
 	git commit -m "rm c1.c" &&
-	cat important > c1.c &&
+	cp important c1.c &&
 	git add c1.c &&
-	! git merge c1a &&
+	test_must_fail git merge c1a &&
+	test_path_is_missing .git/MERGE_HEAD &&
 	test_cmp important c1.c
 '
 
@@ -76,12 +83,111 @@
 	git reset --hard c1 &&
 	git rm c1.c &&
 	git commit -m "rm c1.c" &&
-	cat important > c1.c &&
+	cp important c1.c &&
 	git add c1.c &&
 	rm c1.c &&
-	! git merge c1a &&
+	test_must_fail git merge c1a &&
+	test_path_is_missing .git/MERGE_HEAD &&
 	git checkout c1.c &&
 	test_cmp important c1.c
 '
 
+test_expect_failure 'will not overwrite unstaged changes in renamed file' '
+	git reset --hard c1 &&
+	git mv c1.c other.c &&
+	git commit -m rename &&
+	cp important other.c &&
+	git merge c1a &&
+	test_cmp important other.c
+'
+
+test_expect_success 'will not overwrite untracked subtree' '
+	git reset --hard c0 &&
+	rm -rf sub &&
+	mkdir -p sub/f &&
+	cp important sub/f/important &&
+	test_must_fail git merge sub &&
+	test_path_is_missing .git/MERGE_HEAD &&
+	test_cmp important sub/f/important
+'
+
+cat >expect <<\EOF
+error: The following untracked working tree files would be overwritten by merge:
+	sub
+	sub2
+Please move or remove them before you can merge.
+Aborting
+EOF
+
+test_expect_success 'will not overwrite untracked file in leading path' '
+	git reset --hard c0 &&
+	rm -rf sub &&
+	cp important sub &&
+	cp important sub2 &&
+	test_must_fail git merge sub 2>out &&
+	test_cmp out expect &&
+	test_path_is_missing .git/MERGE_HEAD &&
+	test_cmp important sub &&
+	test_cmp important sub2 &&
+	rm -f sub sub2
+'
+
+test_expect_success SYMLINKS 'will not overwrite untracked symlink in leading path' '
+	git reset --hard c0 &&
+	rm -rf sub &&
+	mkdir sub2 &&
+	ln -s sub2 sub &&
+	test_must_fail git merge sub &&
+	test_path_is_missing .git/MERGE_HEAD
+'
+
+test_expect_success SYMLINKS 'will not be confused by symlink in leading path' '
+	git reset --hard c0 &&
+	rm -rf sub &&
+	ln -s sub2 sub &&
+	git add sub &&
+	git commit -m ln &&
+	git checkout sub
+'
+
+cat >expect <<\EOF
+error: Untracked working tree file 'c0.c' would be overwritten by merge.
+fatal: read-tree failed
+EOF
+
+test_expect_success 'will not overwrite untracked file on unborn branch' '
+	git reset --hard c0 &&
+	git rm -fr . &&
+	git checkout --orphan new &&
+	cp important c0.c &&
+	test_must_fail git merge c0 2>out &&
+	test_i18ncmp out expect
+'
+
+test_expect_success 'will not overwrite untracked file on unborn branch .git/MERGE_HEAD sanity etc.' '
+	test_when_finished "rm c0.c" &&
+	test_path_is_missing .git/MERGE_HEAD &&
+	test_cmp important c0.c
+'
+
+test_expect_success 'failed merge leaves unborn branch in the womb' '
+	test_must_fail git rev-parse --verify HEAD
+'
+
+test_expect_success 'set up unborn branch and content' '
+	git symbolic-ref HEAD refs/heads/unborn &&
+	rm -f .git/index &&
+	echo foo > tracked-file &&
+	git add tracked-file &&
+	echo bar > untracked-file
+'
+
+test_expect_success 'will not clobber WT/index when merging into unborn' '
+	git merge master &&
+	grep foo tracked-file &&
+	git show :tracked-file >expect &&
+	grep foo expect &&
+	grep bar untracked-file
+'
+
 test_done
diff --git a/t/t7608-merge-messages.sh b/t/t7608-merge-messages.sh
new file mode 100755
index 0000000..8e7e0a5
--- /dev/null
+++ b/t/t7608-merge-messages.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+test_description='test auto-generated merge messages'
+. ./test-lib.sh
+
+check_oneline() {
+	echo "$1" | sed "s/Q/'/g" >expect &&
+	git log -1 --pretty=tformat:%s >actual &&
+	test_cmp expect actual
+}
+
+test_expect_success 'merge local branch' '
+	test_commit master-1 &&
+	git checkout -b local-branch &&
+	test_commit branch-1 &&
+	git checkout master &&
+	test_commit master-2 &&
+	git merge local-branch &&
+	check_oneline "Merge branch Qlocal-branchQ"
+'
+
+test_expect_success 'merge octopus branches' '
+	git checkout -b octopus-a master &&
+	test_commit octopus-1 &&
+	git checkout -b octopus-b master &&
+	test_commit octopus-2 &&
+	git checkout master &&
+	git merge octopus-a octopus-b &&
+	check_oneline "Merge branches Qoctopus-aQ and Qoctopus-bQ"
+'
+
+test_expect_success 'merge tag' '
+	git checkout -b tag-branch master &&
+	test_commit tag-1 &&
+	git checkout master &&
+	test_commit master-3 &&
+	git merge tag-1 &&
+	check_oneline "Merge tag Qtag-1Q"
+'
+
+test_expect_success 'ambiguous tag' '
+	git checkout -b ambiguous master &&
+	test_commit ambiguous &&
+	git checkout master &&
+	test_commit master-4 &&
+	git merge ambiguous &&
+	check_oneline "Merge tag QambiguousQ"
+'
+
+test_expect_success 'remote-tracking branch' '
+	git checkout -b remote master &&
+	test_commit remote-1 &&
+	git update-ref refs/remotes/origin/master remote &&
+	git checkout master &&
+	test_commit master-5 &&
+	git merge origin/master &&
+	check_oneline "Merge remote-tracking branch Qorigin/masterQ"
+'
+
+test_done
diff --git a/t/t7609-merge-co-error-msgs.sh b/t/t7609-merge-co-error-msgs.sh
new file mode 100755
index 0000000..0e4a682
--- /dev/null
+++ b/t/t7609-merge-co-error-msgs.sh
@@ -0,0 +1,138 @@
+#!/bin/sh
+
+test_description='unpack-trees error messages'
+
+. ./test-lib.sh
+
+
+test_expect_success 'setup' '
+	echo one >one &&
+	git add one &&
+	git commit -a -m First &&
+
+	git checkout -b branch &&
+	echo two >two &&
+	echo three >three &&
+	echo four >four &&
+	echo five >five &&
+	git add two three four five &&
+	git commit -m Second &&
+
+	git checkout master &&
+	echo other >two &&
+	echo other >three &&
+	echo other >four &&
+	echo other >five
+'
+
+cat >expect <<\EOF
+error: The following untracked working tree files would be overwritten by merge:
+	five
+	four
+	three
+	two
+Please move or remove them before you can merge.
+Aborting
+EOF
+
+test_expect_success 'untracked files overwritten by merge (fast and non-fast forward)' '
+	test_must_fail git merge branch 2>out &&
+	test_cmp out expect &&
+	git commit --allow-empty -m empty &&
+	(
+		GIT_MERGE_VERBOSITY=0 &&
+		export GIT_MERGE_VERBOSITY &&
+		test_must_fail git merge branch 2>out2
+	) &&
+	test_cmp out2 expect &&
+	git reset --hard HEAD^
+'
+
+cat >expect <<\EOF
+error: Your local changes to the following files would be overwritten by merge:
+	four
+	three
+	two
+Please, commit your changes or stash them before you can merge.
+error: The following untracked working tree files would be overwritten by merge:
+	five
+Please move or remove them before you can merge.
+Aborting
+EOF
+
+test_expect_success 'untracked files or local changes ovewritten by merge' '
+	git add two &&
+	git add three &&
+	git add four &&
+	test_must_fail git merge branch 2>out &&
+	test_cmp out expect
+'
+
+cat >expect <<\EOF
+error: Your local changes to the following files would be overwritten by checkout:
+	rep/one
+	rep/two
+Please, commit your changes or stash them before you can switch branches.
+Aborting
+EOF
+
+test_expect_success 'cannot switch branches because of local changes' '
+	git add five &&
+	mkdir rep &&
+	echo one >rep/one &&
+	echo two >rep/two &&
+	git add rep/one rep/two &&
+	git commit -m Fourth &&
+	git checkout master &&
+	echo uno >rep/one &&
+	echo dos >rep/two &&
+	test_must_fail git checkout branch 2>out &&
+	test_cmp out expect
+'
+
+cat >expect <<\EOF
+error: Your local changes to the following files would be overwritten by checkout:
+	rep/one
+	rep/two
+Please, commit your changes or stash them before you can switch branches.
+Aborting
+EOF
+
+test_expect_success 'not uptodate file porcelain checkout error' '
+	git add rep/one rep/two &&
+	test_must_fail git checkout branch 2>out &&
+	test_cmp out expect
+'
+
+cat >expect <<\EOF
+error: Updating the following directories would lose untracked files in it:
+	rep
+	rep2
+
+Aborting
+EOF
+
+test_expect_success 'not_uptodate_dir porcelain checkout error' '
+	git init uptodate &&
+	cd uptodate &&
+	mkdir rep &&
+	mkdir rep2 &&
+	touch rep/foo &&
+	touch rep2/foo &&
+	git add rep/foo rep2/foo &&
+	git commit -m init &&
+	git checkout -b branch &&
+	git rm rep -r &&
+	git rm rep2 -r &&
+	>rep &&
+	>rep2 &&
+	git add rep rep2&&
+	git commit -m "added test as a file" &&
+	git checkout master &&
+	>rep/untracked-file &&
+	>rep2/untracked-file &&
+	test_must_fail git checkout branch 2>out &&
+	test_cmp out ../expect
+'
+
+test_done
diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh
index e768c3e..f5e16fc 100755
--- a/t/t7610-mergetool.sh
+++ b/t/t7610-mergetool.sh
@@ -14,76 +14,461 @@
 # running mergetool
 
 test_expect_success 'setup' '
+    git config rerere.enabled true &&
     echo master >file1 &&
+    echo master spaced >"spaced name" &&
+    echo master file11 >file11 &&
+    echo master file12 >file12 &&
+    echo master file13 >file13 &&
+    echo master file14 >file14 &&
     mkdir subdir &&
     echo master sub >subdir/file3 &&
-    git add file1 subdir/file3 &&
-    git commit -m "added file1" &&
+    test_create_repo submod &&
+    (
+	cd submod &&
+	: >foo &&
+	git add foo &&
+	git commit -m "Add foo"
+    ) &&
+    git submodule add git://example.com/submod submod &&
+    git add file1 "spaced name" file1[1-4] subdir/file3 .gitmodules submod &&
+    git commit -m "add initial versions" &&
 
     git checkout -b branch1 master &&
+    git submodule update -N &&
     echo branch1 change >file1 &&
     echo branch1 newfile >file2 &&
+    echo branch1 spaced >"spaced name" &&
+    echo branch1 both added >both &&
+    echo branch1 change file11 >file11 &&
+    echo branch1 change file13 >file13 &&
     echo branch1 sub >subdir/file3 &&
-    git add file1 file2 subdir/file3 &&
+    (
+	cd submod &&
+	echo branch1 submodule >bar &&
+	git add bar &&
+	git commit -m "Add bar on branch1" &&
+	git checkout -b submod-branch1
+    ) &&
+    git add file1 "spaced name" file11 file13 file2 subdir/file3 submod &&
+    git add both &&
+    git rm file12 &&
     git commit -m "branch1 changes" &&
 
     git checkout master &&
+    git submodule update -N &&
     echo master updated >file1 &&
     echo master new >file2 &&
+    echo master updated spaced >"spaced name" &&
+    echo master both added >both &&
+    echo master updated file12 >file12 &&
+    echo master updated file14 >file14 &&
     echo master new sub >subdir/file3 &&
-    git add file1 file2 subdir/file3 &&
+    (
+	cd submod &&
+	echo master submodule >bar &&
+	git add bar &&
+	git commit -m "Add bar on master" &&
+	git checkout -b submod-master
+    ) &&
+    git add file1 "spaced name" file12 file14 file2 subdir/file3 submod &&
+    git add both &&
+    git rm file11 &&
     git commit -m "master updates" &&
 
     git config merge.tool mytool &&
     git config mergetool.mytool.cmd "cat \"\$REMOTE\" >\"\$MERGED\"" &&
-    git config mergetool.mytool.trustExitCode true
+    git config mergetool.mytool.trustExitCode true &&
+    git config mergetool.mybase.cmd "cat \"\$BASE\" >\"\$MERGED\"" &&
+    git config mergetool.mybase.trustExitCode true
 '
 
 test_expect_success 'custom mergetool' '
     git checkout -b test1 branch1 &&
+    git submodule update -N &&
     test_must_fail git merge master >/dev/null 2>&1 &&
-    ( yes "" | git mergetool file1 >/dev/null 2>&1 ) &&
-    ( yes "" | git mergetool file2 >/dev/null 2>&1 ) &&
+    ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
+    ( yes "" | git mergetool file1 file1 ) &&
+    ( yes "" | git mergetool file2 "spaced name" >/dev/null 2>&1 ) &&
     ( yes "" | git mergetool subdir/file3 >/dev/null 2>&1 ) &&
+    ( yes "d" | git mergetool file11 >/dev/null 2>&1 ) &&
+    ( yes "d" | git mergetool file12 >/dev/null 2>&1 ) &&
+    ( yes "l" | git mergetool submod >/dev/null 2>&1 ) &&
     test "$(cat file1)" = "master updated" &&
     test "$(cat file2)" = "master new" &&
     test "$(cat subdir/file3)" = "master new sub" &&
+    test "$(cat submod/bar)" = "branch1 submodule" &&
     git commit -m "branch1 resolved with mergetool"
 '
 
 test_expect_success 'mergetool crlf' '
     git config core.autocrlf true &&
-    git checkout -b test2 branch1
+    git checkout -b test2 branch1 &&
     test_must_fail git merge master >/dev/null 2>&1 &&
     ( yes "" | git mergetool file1 >/dev/null 2>&1 ) &&
     ( yes "" | git mergetool file2 >/dev/null 2>&1 ) &&
+    ( yes "" | git mergetool "spaced name" >/dev/null 2>&1 ) &&
+    ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
     ( yes "" | git mergetool subdir/file3 >/dev/null 2>&1 ) &&
+    ( yes "d" | git mergetool file11 >/dev/null 2>&1 ) &&
+    ( yes "d" | git mergetool file12 >/dev/null 2>&1 ) &&
+    ( yes "r" | git mergetool submod >/dev/null 2>&1 ) &&
     test "$(printf x | cat file1 -)" = "$(printf "master updated\r\nx")" &&
     test "$(printf x | cat file2 -)" = "$(printf "master new\r\nx")" &&
     test "$(printf x | cat subdir/file3 -)" = "$(printf "master new sub\r\nx")" &&
+    git submodule update -N &&
+    test "$(cat submod/bar)" = "master submodule" &&
     git commit -m "branch1 resolved with mergetool - autocrlf" &&
     git config core.autocrlf false &&
     git reset --hard
 '
 
 test_expect_success 'mergetool in subdir' '
-    git checkout -b test3 branch1
-    cd subdir && (
-    test_must_fail git merge master >/dev/null 2>&1 &&
-    ( yes "" | git mergetool file3 >/dev/null 2>&1 ) &&
-    test "$(cat file3)" = "master new sub" )
+    git checkout -b test3 branch1 &&
+    git submodule update -N &&
+    (
+	cd subdir &&
+	test_must_fail git merge master >/dev/null 2>&1 &&
+	( yes "" | git mergetool file3 >/dev/null 2>&1 ) &&
+	test "$(cat file3)" = "master new sub"
+    )
 '
 
-# We can't merge files from parent directories when running mergetool
-# from a subdir. Is this a bug?
-#
-#test_expect_failure 'mergetool in subdir' '
-#    cd subdir && (
-#    ( yes "" | git mergetool ../file1 >/dev/null 2>&1 ) &&
-#    ( yes "" | git mergetool ../file2 >/dev/null 2>&1 ) &&
-#    test "$(cat ../file1)" = "master updated" &&
-#    test "$(cat ../file2)" = "master new" &&
-#    git commit -m "branch1 resolved with mergetool - subdir" )
-#'
+test_expect_success 'mergetool on file in parent dir' '
+    (
+	cd subdir &&
+	( yes "" | git mergetool ../file1 >/dev/null 2>&1 ) &&
+	( yes "" | git mergetool ../file2 ../spaced\ name >/dev/null 2>&1 ) &&
+	( yes "" | git mergetool ../both >/dev/null 2>&1 ) &&
+	( yes "d" | git mergetool ../file11 >/dev/null 2>&1 ) &&
+	( yes "d" | git mergetool ../file12 >/dev/null 2>&1 ) &&
+	( yes "l" | git mergetool ../submod >/dev/null 2>&1 ) &&
+	test "$(cat ../file1)" = "master updated" &&
+	test "$(cat ../file2)" = "master new" &&
+	test "$(cat ../submod/bar)" = "branch1 submodule" &&
+	git commit -m "branch1 resolved with mergetool - subdir"
+    )
+'
+
+test_expect_success 'mergetool skips autoresolved' '
+    git checkout -b test4 branch1 &&
+    git submodule update -N &&
+    test_must_fail git merge master &&
+    test -n "$(git ls-files -u)" &&
+    ( yes "d" | git mergetool file11 >/dev/null 2>&1 ) &&
+    ( yes "d" | git mergetool file12 >/dev/null 2>&1 ) &&
+    ( yes "l" | git mergetool submod >/dev/null 2>&1 ) &&
+    output="$(git mergetool --no-prompt)" &&
+    test "$output" = "No files need merging" &&
+    git reset --hard
+'
+
+test_expect_success 'mergetool merges all from subdir' '
+    (
+	cd subdir &&
+	git config rerere.enabled false &&
+	test_must_fail git merge master &&
+	( yes "r" | git mergetool ../submod ) &&
+	( yes "d" "d" | git mergetool --no-prompt ) &&
+	test "$(cat ../file1)" = "master updated" &&
+	test "$(cat ../file2)" = "master new" &&
+	test "$(cat file3)" = "master new sub" &&
+	( cd .. && git submodule update -N ) &&
+	test "$(cat ../submod/bar)" = "master submodule" &&
+	git commit -m "branch2 resolved by mergetool from subdir"
+    )
+'
+
+test_expect_success 'mergetool skips resolved paths when rerere is active' '
+    git config rerere.enabled true &&
+    rm -rf .git/rr-cache &&
+    git checkout -b test5 branch1
+    git submodule update -N &&
+    test_must_fail git merge master >/dev/null 2>&1 &&
+    ( yes "l" | git mergetool --no-prompt submod >/dev/null 2>&1 ) &&
+    ( yes "d" "d" | git mergetool --no-prompt >/dev/null 2>&1 ) &&
+    git submodule update -N &&
+    output="$(yes "n" | git mergetool --no-prompt)" &&
+    test "$output" = "No files need merging" &&
+    git reset --hard
+'
+
+test_expect_success 'mergetool takes partial path' '
+    git config rerere.enabled false &&
+    git checkout -b test12 branch1 &&
+    git submodule update -N &&
+    test_must_fail git merge master &&
+
+    #shouldnt need these lines
+    #( yes "d" | git mergetool file11 >/dev/null 2>&1 ) &&
+    #( yes "d" | git mergetool file12 >/dev/null 2>&1 ) &&
+    #( yes "l" | git mergetool submod >/dev/null 2>&1 ) &&
+    #( yes "" | git mergetool file1 file2 >/dev/null 2>&1 ) &&
+
+    ( yes "" | git mergetool subdir ) &&
+
+    test "$(cat subdir/file3)" = "master new sub" &&
+    git reset --hard
+'
+
+test_expect_success 'deleted vs modified submodule' '
+    git checkout -b test6 branch1 &&
+    git submodule update -N &&
+    mv submod submod-movedaside &&
+    git rm submod &&
+    git commit -m "Submodule deleted from branch" &&
+    git checkout -b test6.a test6 &&
+    test_must_fail git merge master &&
+    test -n "$(git ls-files -u)" &&
+    ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
+    ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
+    ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
+    ( yes "r" | git mergetool submod ) &&
+    rmdir submod && mv submod-movedaside submod &&
+    test "$(cat submod/bar)" = "branch1 submodule" &&
+    git submodule update -N &&
+    test "$(cat submod/bar)" = "master submodule" &&
+    output="$(git mergetool --no-prompt)" &&
+    test "$output" = "No files need merging" &&
+    git commit -m "Merge resolved by keeping module" &&
+
+    mv submod submod-movedaside &&
+    git checkout -b test6.b test6 &&
+    git submodule update -N &&
+    test_must_fail git merge master &&
+    test -n "$(git ls-files -u)" &&
+    ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
+    ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
+    ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
+    ( yes "l" | git mergetool submod ) &&
+    test ! -e submod &&
+    output="$(git mergetool --no-prompt)" &&
+    test "$output" = "No files need merging" &&
+    git commit -m "Merge resolved by deleting module" &&
+
+    mv submod-movedaside submod &&
+    git checkout -b test6.c master &&
+    git submodule update -N &&
+    test_must_fail git merge test6 &&
+    test -n "$(git ls-files -u)" &&
+    ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
+    ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
+    ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
+    ( yes "r" | git mergetool submod ) &&
+    test ! -e submod &&
+    test -d submod.orig &&
+    git submodule update -N &&
+    output="$(git mergetool --no-prompt)" &&
+    test "$output" = "No files need merging" &&
+    git commit -m "Merge resolved by deleting module" &&
+    mv submod.orig submod &&
+
+    git checkout -b test6.d master &&
+    git submodule update -N &&
+    test_must_fail git merge test6 &&
+    test -n "$(git ls-files -u)" &&
+    ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
+    ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
+    ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
+    ( yes "l" | git mergetool submod ) &&
+    test "$(cat submod/bar)" = "master submodule" &&
+    git submodule update -N &&
+    test "$(cat submod/bar)" = "master submodule" &&
+    output="$(git mergetool --no-prompt)" &&
+    test "$output" = "No files need merging" &&
+    git commit -m "Merge resolved by keeping module" &&
+    git reset --hard HEAD
+'
+
+test_expect_success 'file vs modified submodule' '
+    git checkout -b test7 branch1 &&
+    git submodule update -N &&
+    mv submod submod-movedaside &&
+    git rm submod &&
+    echo not a submodule >submod &&
+    git add submod &&
+    git commit -m "Submodule path becomes file" &&
+    git checkout -b test7.a branch1 &&
+    test_must_fail git merge master &&
+    test -n "$(git ls-files -u)" &&
+    ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
+    ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
+    ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
+    ( yes "r" | git mergetool submod ) &&
+    rmdir submod && mv submod-movedaside submod &&
+    test "$(cat submod/bar)" = "branch1 submodule" &&
+    git submodule update -N &&
+    test "$(cat submod/bar)" = "master submodule" &&
+    output="$(git mergetool --no-prompt)" &&
+    test "$output" = "No files need merging" &&
+    git commit -m "Merge resolved by keeping module" &&
+
+    mv submod submod-movedaside &&
+    git checkout -b test7.b test7 &&
+    test_must_fail git merge master &&
+    test -n "$(git ls-files -u)" &&
+    ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
+    ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
+    ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
+    ( yes "l" | git mergetool submod ) &&
+    git submodule update -N &&
+    test "$(cat submod)" = "not a submodule" &&
+    output="$(git mergetool --no-prompt)" &&
+    test "$output" = "No files need merging" &&
+    git commit -m "Merge resolved by keeping file" &&
+
+    git checkout -b test7.c master &&
+    rmdir submod && mv submod-movedaside submod &&
+    test ! -e submod.orig &&
+    git submodule update -N &&
+    test_must_fail git merge test7 &&
+    test -n "$(git ls-files -u)" &&
+    ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
+    ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
+    ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
+    ( yes "r" | git mergetool submod ) &&
+    test -d submod.orig &&
+    git submodule update -N &&
+    test "$(cat submod)" = "not a submodule" &&
+    output="$(git mergetool --no-prompt)" &&
+    test "$output" = "No files need merging" &&
+    git commit -m "Merge resolved by keeping file" &&
+
+    git checkout -b test7.d master &&
+    rmdir submod && mv submod.orig submod &&
+    git submodule update -N &&
+    test_must_fail git merge test7 &&
+    test -n "$(git ls-files -u)" &&
+    ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
+    ( yes "" | git mergetool both>/dev/null 2>&1 ) &&
+    ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
+    ( yes "l" | git mergetool submod ) &&
+    test "$(cat submod/bar)" = "master submodule" &&
+    git submodule update -N &&
+    test "$(cat submod/bar)" = "master submodule" &&
+    output="$(git mergetool --no-prompt)" &&
+    test "$output" = "No files need merging" &&
+    git commit -m "Merge resolved by keeping module"
+'
+
+test_expect_success 'submodule in subdirectory' '
+    git checkout -b test10 branch1 &&
+    git submodule update -N &&
+    (
+	cd subdir &&
+	test_create_repo subdir_module &&
+	(
+	    cd subdir_module &&
+	    : >file15 &&
+	    git add file15 &&
+	    git commit -m "add initial versions"
+	)
+    ) &&
+    git submodule add git://example.com/subsubmodule subdir/subdir_module &&
+    git add subdir/subdir_module &&
+    git commit -m "add submodule in subdirectory" &&
+
+    git checkout -b test10.a test10 &&
+    git submodule update -N &&
+    (
+	cd subdir/subdir_module &&
+	git checkout -b super10.a &&
+	echo test10.a >file15 &&
+	git add file15 &&
+	git commit -m "on branch 10.a"
+    ) &&
+    git add subdir/subdir_module &&
+    git commit -m "change submodule in subdirectory on test10.a" &&
+
+    git checkout -b test10.b test10 &&
+    git submodule update -N &&
+    (
+	cd subdir/subdir_module &&
+	git checkout -b super10.b &&
+	echo test10.b >file15 &&
+	git add file15 &&
+	git commit -m "on branch 10.b"
+    ) &&
+    git add subdir/subdir_module &&
+    git commit -m "change submodule in subdirectory on test10.b" &&
+
+    test_must_fail git merge test10.a >/dev/null 2>&1 &&
+    (
+	cd subdir &&
+	( yes "l" | git mergetool subdir_module )
+    ) &&
+    test "$(cat subdir/subdir_module/file15)" = "test10.b" &&
+    git submodule update -N &&
+    test "$(cat subdir/subdir_module/file15)" = "test10.b" &&
+    git reset --hard &&
+    git submodule update -N &&
+
+    test_must_fail git merge test10.a >/dev/null 2>&1 &&
+    ( yes "r" | git mergetool subdir/subdir_module ) &&
+    test "$(cat subdir/subdir_module/file15)" = "test10.b" &&
+    git submodule update -N &&
+    test "$(cat subdir/subdir_module/file15)" = "test10.a" &&
+    git commit -m "branch1 resolved with mergetool" &&
+    rm -rf subdir/subdir_module
+'
+
+test_expect_success 'directory vs modified submodule' '
+    git checkout -b test11 branch1 &&
+    mv submod submod-movedaside &&
+    git rm submod &&
+    mkdir submod &&
+    echo not a submodule >submod/file16 &&
+    git add submod/file16 &&
+    git commit -m "Submodule path becomes directory" &&
+
+    test_must_fail git merge master &&
+    test -n "$(git ls-files -u)" &&
+    ( yes "l" | git mergetool submod ) &&
+    test "$(cat submod/file16)" = "not a submodule" &&
+    rm -rf submod.orig &&
+
+    git reset --hard >/dev/null 2>&1 &&
+    test_must_fail git merge master &&
+    test -n "$(git ls-files -u)" &&
+    test ! -e submod.orig &&
+    ( yes "r" | git mergetool submod ) &&
+    test -d submod.orig &&
+    test "$(cat submod.orig/file16)" = "not a submodule" &&
+    rm -r submod.orig &&
+    mv submod-movedaside/.git submod &&
+    ( cd submod && git clean -f && git reset --hard ) &&
+    git submodule update -N &&
+    test "$(cat submod/bar)" = "master submodule" &&
+    git reset --hard >/dev/null 2>&1 && rm -rf submod-movedaside &&
+
+    git checkout -b test11.c master &&
+    git submodule update -N &&
+    test_must_fail git merge test11 &&
+    test -n "$(git ls-files -u)" &&
+    ( yes "l" | git mergetool submod ) &&
+    git submodule update -N &&
+    test "$(cat submod/bar)" = "master submodule" &&
+
+    git reset --hard >/dev/null 2>&1 &&
+    git submodule update -N &&
+    test_must_fail git merge test11 &&
+    test -n "$(git ls-files -u)" &&
+    test ! -e submod.orig &&
+    ( yes "r" | git mergetool submod ) &&
+    test "$(cat submod/file16)" = "not a submodule" &&
+
+    git reset --hard master >/dev/null 2>&1 &&
+    ( cd submod && git clean -f && git reset --hard ) &&
+    git submodule update -N
+'
+
+test_expect_success 'file with no base' '
+    git checkout -b test13 branch1 &&
+    test_must_fail git merge master &&
+    git mergetool --no-prompt --tool mybase -- both &&
+    >expected &&
+    test_cmp both expected &&
+    git reset --hard master >/dev/null 2>&1
+'
 
 test_done
diff --git a/t/t7611-merge-abort.sh b/t/t7611-merge-abort.sh
new file mode 100755
index 0000000..7b4798e
--- /dev/null
+++ b/t/t7611-merge-abort.sh
@@ -0,0 +1,319 @@
+#!/bin/sh
+
+test_description='test aborting in-progress merges
+
+Set up repo with conflicting and non-conflicting branches:
+
+There are three files foo/bar/baz, and the following graph illustrates the
+content of these files in each commit:
+
+# foo/bar/baz --- foo/bar/bazz     <-- master
+#             \
+#              --- foo/barf/bazf   <-- conflict_branch
+#               \
+#                --- foo/bart/baz  <-- clean_branch
+
+Next, test git merge --abort with the following variables:
+- before/after successful merge (should fail when not in merge context)
+- with/without conflicts
+- clean/dirty index before merge
+- clean/dirty worktree before merge
+- dirty index before merge matches contents on remote branch
+- changed/unchanged worktree after merge
+- changed/unchanged index after merge
+'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	# Create the above repo
+	echo foo > foo &&
+	echo bar > bar &&
+	echo baz > baz &&
+	git add foo bar baz &&
+	git commit -m initial &&
+	echo bazz > baz &&
+	git commit -a -m "second" &&
+	git checkout -b conflict_branch HEAD^ &&
+	echo barf > bar &&
+	echo bazf > baz &&
+	git commit -a -m "conflict" &&
+	git checkout -b clean_branch HEAD^ &&
+	echo bart > bar &&
+	git commit -a -m "clean" &&
+	git checkout master
+'
+
+pre_merge_head="$(git rev-parse HEAD)"
+
+test_expect_success 'fails without MERGE_HEAD (unstarted merge)' '
+	test_must_fail git merge --abort 2>output &&
+	test_i18ngrep MERGE_HEAD output
+'
+
+test_expect_success 'fails without MERGE_HEAD (unstarted merge): .git/MERGE_HEAD sanity' '
+	test ! -f .git/MERGE_HEAD &&
+	test "$pre_merge_head" = "$(git rev-parse HEAD)"
+'
+
+test_expect_success 'fails without MERGE_HEAD (completed merge)' '
+	git merge clean_branch &&
+	test ! -f .git/MERGE_HEAD &&
+	# Merge successfully completed
+	post_merge_head="$(git rev-parse HEAD)" &&
+	test_must_fail git merge --abort 2>output &&
+	test_i18ngrep MERGE_HEAD output
+'
+
+test_expect_success 'fails without MERGE_HEAD (completed merge): .git/MERGE_HEAD sanity' '
+	test ! -f .git/MERGE_HEAD &&
+	test "$post_merge_head" = "$(git rev-parse HEAD)"
+'
+
+test_expect_success 'Forget previous merge' '
+	git reset --hard "$pre_merge_head"
+'
+
+test_expect_success 'Abort after --no-commit' '
+	# Redo merge, but stop before creating merge commit
+	git merge --no-commit clean_branch &&
+	test -f .git/MERGE_HEAD &&
+	# Abort non-conflicting merge
+	git merge --abort &&
+	test ! -f .git/MERGE_HEAD &&
+	test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+	test -z "$(git diff)" &&
+	test -z "$(git diff --staged)"
+'
+
+test_expect_success 'Abort after conflicts' '
+	# Create conflicting merge
+	test_must_fail git merge conflict_branch &&
+	test -f .git/MERGE_HEAD &&
+	# Abort conflicting merge
+	git merge --abort &&
+	test ! -f .git/MERGE_HEAD &&
+	test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+	test -z "$(git diff)" &&
+	test -z "$(git diff --staged)"
+'
+
+test_expect_success 'Clean merge with dirty index fails' '
+	echo xyzzy >> foo &&
+	git add foo &&
+	git diff --staged > expect &&
+	test_must_fail git merge clean_branch &&
+	test ! -f .git/MERGE_HEAD &&
+	test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+	test -z "$(git diff)" &&
+	git diff --staged > actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'Conflicting merge with dirty index fails' '
+	test_must_fail git merge conflict_branch &&
+	test ! -f .git/MERGE_HEAD &&
+	test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+	test -z "$(git diff)" &&
+	git diff --staged > actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'Reset index (but preserve worktree changes)' '
+	git reset "$pre_merge_head" &&
+	git diff > actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'Abort clean merge with non-conflicting dirty worktree' '
+	git merge --no-commit clean_branch &&
+	test -f .git/MERGE_HEAD &&
+	# Abort merge
+	git merge --abort &&
+	test ! -f .git/MERGE_HEAD &&
+	test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+	test -z "$(git diff --staged)" &&
+	git diff > actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'Abort conflicting merge with non-conflicting dirty worktree' '
+	test_must_fail git merge conflict_branch &&
+	test -f .git/MERGE_HEAD &&
+	# Abort merge
+	git merge --abort &&
+	test ! -f .git/MERGE_HEAD &&
+	test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+	test -z "$(git diff --staged)" &&
+	git diff > actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'Reset worktree changes' '
+	git reset --hard "$pre_merge_head"
+'
+
+test_expect_success 'Fail clean merge with conflicting dirty worktree' '
+	echo xyzzy >> bar &&
+	git diff > expect &&
+	test_must_fail git merge --no-commit clean_branch &&
+	test ! -f .git/MERGE_HEAD &&
+	test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+	test -z "$(git diff --staged)" &&
+	git diff > actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'Fail conflicting merge with conflicting dirty worktree' '
+	test_must_fail git merge conflict_branch &&
+	test ! -f .git/MERGE_HEAD &&
+	test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+	test -z "$(git diff --staged)" &&
+	git diff > actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'Reset worktree changes' '
+	git reset --hard "$pre_merge_head"
+'
+
+test_expect_success 'Fail clean merge with matching dirty worktree' '
+	echo bart > bar &&
+	git diff > expect &&
+	test_must_fail git merge --no-commit clean_branch &&
+	test ! -f .git/MERGE_HEAD &&
+	test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+	test -z "$(git diff --staged)" &&
+	git diff > actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'Abort clean merge with matching dirty index' '
+	git add bar &&
+	git diff --staged > expect &&
+	git merge --no-commit clean_branch &&
+	test -f .git/MERGE_HEAD &&
+	### When aborting the merge, git will discard all staged changes,
+	### including those that were staged pre-merge. In other words,
+	### --abort will LOSE any staged changes (the staged changes that
+	### are lost must match the merge result, or the merge would not
+	### have been allowed to start). Change expectations accordingly:
+	rm expect &&
+	touch expect &&
+	# Abort merge
+	git merge --abort &&
+	test ! -f .git/MERGE_HEAD &&
+	test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+	git diff --staged > actual &&
+	test_cmp expect actual &&
+	test -z "$(git diff)"
+'
+
+test_expect_success 'Reset worktree changes' '
+	git reset --hard "$pre_merge_head"
+'
+
+test_expect_success 'Fail conflicting merge with matching dirty worktree' '
+	echo barf > bar &&
+	git diff > expect &&
+	test_must_fail git merge conflict_branch &&
+	test ! -f .git/MERGE_HEAD &&
+	test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+	test -z "$(git diff --staged)" &&
+	git diff > actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'Abort conflicting merge with matching dirty index' '
+	git add bar &&
+	git diff --staged > expect &&
+	test_must_fail git merge conflict_branch &&
+	test -f .git/MERGE_HEAD &&
+	### When aborting the merge, git will discard all staged changes,
+	### including those that were staged pre-merge. In other words,
+	### --abort will LOSE any staged changes (the staged changes that
+	### are lost must match the merge result, or the merge would not
+	### have been allowed to start). Change expectations accordingly:
+	rm expect &&
+	touch expect &&
+	# Abort merge
+	git merge --abort &&
+	test ! -f .git/MERGE_HEAD &&
+	test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+	git diff --staged > actual &&
+	test_cmp expect actual &&
+	test -z "$(git diff)"
+'
+
+test_expect_success 'Reset worktree changes' '
+	git reset --hard "$pre_merge_head"
+'
+
+test_expect_success 'Abort merge with pre- and post-merge worktree changes' '
+	# Pre-merge worktree changes
+	echo xyzzy > foo &&
+	echo barf > bar &&
+	git add bar &&
+	git diff > expect &&
+	git diff --staged > expect-staged &&
+	# Perform merge
+	test_must_fail git merge conflict_branch &&
+	test -f .git/MERGE_HEAD &&
+	# Post-merge worktree changes
+	echo yzxxz > foo &&
+	echo blech > baz &&
+	### When aborting the merge, git will discard staged changes (bar)
+	### and unmerged changes (baz). Other changes that are neither
+	### staged nor marked as unmerged (foo), will be preserved. For
+	### these changed, git cannot tell pre-merge changes apart from
+	### post-merge changes, so the post-merge changes will be
+	### preserved. Change expectations accordingly:
+	git diff -- foo > expect &&
+	rm expect-staged &&
+	touch expect-staged &&
+	# Abort merge
+	git merge --abort &&
+	test ! -f .git/MERGE_HEAD &&
+	test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+	git diff > actual &&
+	test_cmp expect actual &&
+	git diff --staged > actual-staged &&
+	test_cmp expect-staged actual-staged
+'
+
+test_expect_success 'Reset worktree changes' '
+	git reset --hard "$pre_merge_head"
+'
+
+test_expect_success 'Abort merge with pre- and post-merge index changes' '
+	# Pre-merge worktree changes
+	echo xyzzy > foo &&
+	echo barf > bar &&
+	git add bar &&
+	git diff > expect &&
+	git diff --staged > expect-staged &&
+	# Perform merge
+	test_must_fail git merge conflict_branch &&
+	test -f .git/MERGE_HEAD &&
+	# Post-merge worktree changes
+	echo yzxxz > foo &&
+	echo blech > baz &&
+	git add foo bar &&
+	### When aborting the merge, git will discard all staged changes
+	### (foo, bar and baz), and no changes will be preserved. Whether
+	### the changes were staged pre- or post-merge does not matter
+	### (except for not preventing starting the merge).
+	### Change expectations accordingly:
+	rm expect expect-staged &&
+	touch expect &&
+	touch expect-staged &&
+	# Abort merge
+	git merge --abort &&
+	test ! -f .git/MERGE_HEAD &&
+	test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+	git diff > actual &&
+	test_cmp expect actual &&
+	git diff --staged > actual-staged &&
+	test_cmp expect-staged actual-staged
+'
+
+test_done
diff --git a/t/t7700-repack.sh b/t/t7700-repack.sh
index f4aa054..d954b84 100755
--- a/t/t7700-repack.sh
+++ b/t/t7700-repack.sh
@@ -8,6 +8,7 @@
 	echo content1 > file1 &&
 	echo content2 > file2 &&
 	git add . &&
+	test_tick &&
 	git commit -m initial_commit &&
 	# Create two packs
 	# The first pack will contain all of the objects except one
@@ -40,6 +41,7 @@
 	echo content3 > file3 &&
 	objsha1=$(GIT_OBJECT_DIRECTORY=alt_objects git hash-object -w file3) &&
 	git add file3 &&
+	test_tick &&
 	git commit -m commit_file3 &&
 	git repack -a -d -l &&
 	git prune-packed &&
@@ -54,7 +56,7 @@
 '
 
 test_expect_success 'packed obs in alt ODB are repacked even when local repo is packless' '
-	mkdir alt_objects/pack
+	mkdir alt_objects/pack &&
 	mv .git/objects/pack/* alt_objects/pack &&
 	git repack -a &&
 	myidx=$(ls -1 .git/objects/pack/*.idx) &&
@@ -73,6 +75,7 @@
 	rm -f .git/objects/pack/* &&
 	echo new_content >> file1 &&
 	git add file1 &&
+	test_tick &&
 	git commit -m more_content &&
 	git repack &&
 	git repack -a -d &&
@@ -92,14 +95,14 @@
 	# swap the .keep so the commit object is in the pack with .keep
 	for p in alt_objects/pack/*.pack
 	do
-		base_name=$(basename $p .pack)
+		base_name=$(basename $p .pack) &&
 		if test -f alt_objects/pack/$base_name.keep
 		then
 			rm alt_objects/pack/$base_name.keep
 		else
 			touch alt_objects/pack/$base_name.keep
 		fi
-	done
+	done &&
 	git repack -a -d &&
 	myidx=$(ls -1 .git/objects/pack/*.idx) &&
 	test -f "$myidx" &&
@@ -118,8 +121,8 @@
 	mv .git/objects/pack/* alt_objects/pack/ &&
 	csha1=$(git rev-parse HEAD^{commit}) &&
 	git reset --hard HEAD^ &&
-	sleep 1 &&
-	git reflog expire --expire=now --expire-unreachable=now --all &&
+	test_tick &&
+	git reflog expire --expire=$test_tick --expire-unreachable=$test_tick --all &&
 	# The pack-objects call on the next line is equivalent to
 	# git repack -A -d without the call to prune-packed
 	git pack-objects --honor-pack-keep --non-empty --all --reflog \
@@ -156,7 +159,7 @@
 	H1=$(git rev-parse HEAD^) &&
 	H2=$(git rev-parse HEAD^^) &&
 	echo "$H0 $H2" > .git/info/grafts &&
-	git reflog expire --expire=now --expire-unreachable=now --all &&
+	git reflog expire --expire=$test_tick --expire-unreachable=$test_tick --all &&
 	git repack -a -d &&
 	git cat-file -t $H1
 	'
diff --git a/t/t7701-repack-unpack-unreachable.sh b/t/t7701-repack-unpack-unreachable.sh
index 5babdf2..b8d4cde 100755
--- a/t/t7701-repack-unpack-unreachable.sh
+++ b/t/t7701-repack-unpack-unreachable.sh
@@ -11,17 +11,20 @@
 test_expect_success '-A with -d option leaves unreachable objects unpacked' '
 	echo content > file1 &&
 	git add . &&
+	test_tick &&
 	git commit -m initial_commit &&
 	# create a transient branch with unique content
 	git checkout -b transient_branch &&
 	echo more content >> file1 &&
 	# record the objects created in the database for file, commit, tree
 	fsha1=$(git hash-object file1) &&
+	test_tick &&
 	git commit -a -m more_content &&
 	csha1=$(git rev-parse HEAD^{commit}) &&
 	tsha1=$(git rev-parse HEAD^{tree}) &&
 	git checkout master &&
 	echo even more content >> file1 &&
+	test_tick &&
 	git commit -a -m even_more_content &&
 	# delete the transient branch
 	git branch -D transient_branch &&
@@ -34,9 +37,11 @@
 	git show $fsha1 &&
 	git show $csha1 &&
 	git show $tsha1 &&
-	# now expire the reflog
-	sleep 1 &&
-	git reflog expire --expire-unreachable=now --all &&
+	# now expire the reflog, while keeping reachable ones but expiring
+	# unreachables immediately
+	test_tick &&
+	sometimeago=$(( $test_tick - 10000 )) &&
+	git reflog expire --expire=$sometimeago --expire-unreachable=$test_tick --all &&
 	# and repack
 	git repack -A -d -l &&
 	# verify objects are retained unpacked
@@ -71,7 +76,7 @@
 	test 1 = $(ls -1 .git/objects/pack/pack-*.pack | wc -l) &&
 	packfile=$(ls .git/objects/pack/pack-*.pack) &&
 	git branch -D transient_branch &&
-	sleep 1 &&
+	test_tick &&
 	git repack -A -l &&
 	test ! -f "$fsha1path" &&
 	test ! -f "$csha1path" &&
@@ -90,4 +95,18 @@
 	compare_mtimes < mtimes
 '
 
+test_expect_success 'do not bother loosening old objects' '
+	obj1=$(echo one | git hash-object -w --stdin) &&
+	obj2=$(echo two | git hash-object -w --stdin) &&
+	pack1=$(echo $obj1 | git pack-objects .git/objects/pack/pack) &&
+	pack2=$(echo $obj2 | git pack-objects .git/objects/pack/pack) &&
+	git prune-packed &&
+	git cat-file -p $obj1 &&
+	git cat-file -p $obj2 &&
+	test-chmtime =-86400 .git/objects/pack/pack-$pack2.pack &&
+	git repack -A -d --unpack-unreachable=1.hour.ago &&
+	git cat-file -p $obj1 &&
+	test_must_fail git cat-file -p $obj2
+'
+
 test_done
diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh
index ebdccf9..9c3e997 100755
--- a/t/t7800-difftool.sh
+++ b/t/t7800-difftool.sh
@@ -1,6 +1,6 @@
 #!/bin/sh
 #
-# Copyright (c) 2009 David Aguilar
+# Copyright (c) 2009, 2010 David Aguilar
 #
 
 test_description='git-difftool
@@ -10,19 +10,16 @@
 
 . ./test-lib.sh
 
-if ! test_have_prereq PERL; then
-	say 'skipping difftool tests, perl not available'
-	test_done
-fi
-
 remove_config_vars()
 {
 	# Unset all config variables used by git-difftool
 	git config --unset diff.tool
+	git config --unset diff.guitool
 	git config --unset difftool.test-tool.cmd
 	git config --unset difftool.prompt
 	git config --unset merge.tool
 	git config --unset mergetool.test-tool.cmd
+	git config --unset mergetool.prompt
 	return 0
 }
 
@@ -31,21 +28,31 @@
 	# Restores the test defaults used by several tests
 	remove_config_vars
 	unset GIT_DIFF_TOOL
-	unset GIT_MERGE_TOOL
 	unset GIT_DIFFTOOL_PROMPT
 	unset GIT_DIFFTOOL_NO_PROMPT
 	git config diff.tool test-tool &&
 	git config difftool.test-tool.cmd 'cat $LOCAL'
+	git config difftool.bogus-tool.cmd false
 }
 
 prompt_given()
 {
 	prompt="$1"
-	test "$prompt" = "Hit return to launch 'test-tool': branch"
+	test "$prompt" = "Launch 'test-tool' [Y/n]: branch"
+}
+
+stdin_contains()
+{
+	grep >/dev/null "$1"
+}
+
+stdin_doesnot_contain()
+{
+	! stdin_contains "$1"
 }
 
 # Create a file on master and change it on branch
-test_expect_success 'setup' '
+test_expect_success PERL 'setup' '
 	echo master >file &&
 	git add file &&
 	git commit -m "added file" &&
@@ -57,7 +64,7 @@
 '
 
 # Configure a custom difftool.<tool>.cmd and use it
-test_expect_success 'custom commands' '
+test_expect_success PERL 'custom commands' '
 	restore_test_defaults &&
 	git config difftool.test-tool.cmd "cat \$REMOTE" &&
 
@@ -70,15 +77,59 @@
 '
 
 # Ensures that git-difftool ignores bogus --tool values
-test_expect_success 'difftool ignores bad --tool values' '
-	diff=$(git difftool --no-prompt --tool=bogus-tool branch)
+test_expect_success PERL 'difftool ignores bad --tool values' '
+	diff=$(git difftool --no-prompt --tool=bad-tool branch)
 	test "$?" = 1 &&
 	test "$diff" = ""
 '
 
+test_expect_success PERL 'difftool forwards arguments to diff' '
+	>for-diff &&
+	git add for-diff &&
+	echo changes>for-diff &&
+	git add for-diff &&
+	diff=$(git difftool --cached --no-prompt -- for-diff) &&
+	test "$diff" = "" &&
+	git reset -- for-diff &&
+	rm for-diff
+'
+
+test_expect_success PERL 'difftool honors --gui' '
+	git config merge.tool bogus-tool &&
+	git config diff.tool bogus-tool &&
+	git config diff.guitool test-tool &&
+
+	diff=$(git difftool --no-prompt --gui branch) &&
+	test "$diff" = "branch" &&
+
+	restore_test_defaults
+'
+
+test_expect_success PERL 'difftool --gui last setting wins' '
+	git config diff.guitool bogus-tool &&
+	git difftool --no-prompt --gui --no-gui &&
+
+	git config merge.tool bogus-tool &&
+	git config diff.tool bogus-tool &&
+	git config diff.guitool test-tool &&
+	diff=$(git difftool --no-prompt --no-gui --gui branch) &&
+	test "$diff" = "branch" &&
+
+	restore_test_defaults
+'
+
+test_expect_success PERL 'difftool --gui works without configured diff.guitool' '
+	git config diff.tool test-tool &&
+
+	diff=$(git difftool --no-prompt --gui branch) &&
+	test "$diff" = "branch" &&
+
+	restore_test_defaults
+'
+
 # Specify the diff tool using $GIT_DIFF_TOOL
-test_expect_success 'GIT_DIFF_TOOL variable' '
-	git config --unset diff.tool
+test_expect_success PERL 'GIT_DIFF_TOOL variable' '
+	test_might_fail git config --unset diff.tool &&
 	GIT_DIFF_TOOL=test-tool &&
 	export GIT_DIFF_TOOL &&
 
@@ -90,19 +141,11 @@
 
 # Test the $GIT_*_TOOL variables and ensure
 # that $GIT_DIFF_TOOL always wins unless --tool is specified
-test_expect_success 'GIT_DIFF_TOOL overrides' '
+test_expect_success PERL 'GIT_DIFF_TOOL overrides' '
 	git config diff.tool bogus-tool &&
 	git config merge.tool bogus-tool &&
 
-	GIT_MERGE_TOOL=test-tool &&
-	export GIT_MERGE_TOOL &&
-	diff=$(git difftool --no-prompt branch) &&
-	test "$diff" = "branch" &&
-	unset GIT_MERGE_TOOL &&
-
-	GIT_MERGE_TOOL=bogus-tool &&
 	GIT_DIFF_TOOL=test-tool &&
-	export GIT_MERGE_TOOL &&
 	export GIT_DIFF_TOOL &&
 
 	diff=$(git difftool --no-prompt branch) &&
@@ -119,7 +162,7 @@
 
 # Test that we don't have to pass --no-prompt to difftool
 # when $GIT_DIFFTOOL_NO_PROMPT is true
-test_expect_success 'GIT_DIFFTOOL_NO_PROMPT variable' '
+test_expect_success PERL 'GIT_DIFFTOOL_NO_PROMPT variable' '
 	GIT_DIFFTOOL_NO_PROMPT=true &&
 	export GIT_DIFFTOOL_NO_PROMPT &&
 
@@ -131,19 +174,19 @@
 
 # git-difftool supports the difftool.prompt variable.
 # Test that GIT_DIFFTOOL_PROMPT can override difftool.prompt = false
-test_expect_success 'GIT_DIFFTOOL_PROMPT variable' '
+test_expect_success PERL 'GIT_DIFFTOOL_PROMPT variable' '
 	git config difftool.prompt false &&
 	GIT_DIFFTOOL_PROMPT=true &&
 	export GIT_DIFFTOOL_PROMPT &&
 
-	prompt=$(echo | git difftool --prompt branch | tail -1) &&
+	prompt=$(echo | git difftool branch | tail -1) &&
 	prompt_given "$prompt" &&
 
 	restore_test_defaults
 '
 
 # Test that we don't have to pass --no-prompt when difftool.prompt is false
-test_expect_success 'difftool.prompt config variable is false' '
+test_expect_success PERL 'difftool.prompt config variable is false' '
 	git config difftool.prompt false &&
 
 	diff=$(git difftool branch) &&
@@ -152,8 +195,19 @@
 	restore_test_defaults
 '
 
+# Test that we don't have to pass --no-prompt when mergetool.prompt is false
+test_expect_success PERL 'difftool merge.prompt = false' '
+	test_might_fail git config --unset difftool.prompt &&
+	git config mergetool.prompt false &&
+
+	diff=$(git difftool branch) &&
+	test "$diff" = "branch" &&
+
+	restore_test_defaults
+'
+
 # Test that the -y flag can override difftool.prompt = true
-test_expect_success 'difftool.prompt can overridden with -y' '
+test_expect_success PERL 'difftool.prompt can overridden with -y' '
 	git config difftool.prompt true &&
 
 	diff=$(git difftool -y branch) &&
@@ -163,7 +217,7 @@
 '
 
 # Test that the --prompt flag can override difftool.prompt = false
-test_expect_success 'difftool.prompt can overridden with --prompt' '
+test_expect_success PERL 'difftool.prompt can overridden with --prompt' '
 	git config difftool.prompt false &&
 
 	prompt=$(echo | git difftool --prompt branch | tail -1) &&
@@ -173,7 +227,7 @@
 '
 
 # Test that the last flag passed on the command-line wins
-test_expect_success 'difftool last flag wins' '
+test_expect_success PERL 'difftool last flag wins' '
 	diff=$(git difftool --prompt --no-prompt branch) &&
 	test "$diff" = "branch" &&
 
@@ -187,8 +241,8 @@
 
 # git-difftool falls back to git-mergetool config variables
 # so test that behavior here
-test_expect_success 'difftool + mergetool config variables' '
-	remove_config_vars
+test_expect_success PERL 'difftool + mergetool config variables' '
+	remove_config_vars &&
 	git config merge.tool test-tool &&
 	git config mergetool.test-tool.cmd "cat \$LOCAL" &&
 
@@ -205,12 +259,119 @@
 	restore_test_defaults
 '
 
-test_expect_success 'difftool.<tool>.path' '
+test_expect_success PERL 'difftool.<tool>.path' '
 	git config difftool.tkdiff.path echo &&
 	diff=$(git difftool --tool=tkdiff --no-prompt branch) &&
 	git config --unset difftool.tkdiff.path &&
 	lines=$(echo "$diff" | grep file | wc -l) &&
-	test "$lines" -eq 1
+	test "$lines" -eq 1 &&
+
+	restore_test_defaults
+'
+
+test_expect_success PERL 'difftool --extcmd=cat' '
+	diff=$(git difftool --no-prompt --extcmd=cat branch) &&
+	test "$diff" = branch"$LF"master
+'
+
+test_expect_success PERL 'difftool --extcmd cat' '
+	diff=$(git difftool --no-prompt --extcmd cat branch) &&
+	test "$diff" = branch"$LF"master
+'
+
+test_expect_success PERL 'difftool -x cat' '
+	diff=$(git difftool --no-prompt -x cat branch) &&
+	test "$diff" = branch"$LF"master
+'
+
+test_expect_success PERL 'difftool --extcmd echo arg1' '
+	diff=$(git difftool --no-prompt --extcmd sh\ -c\ \"echo\ \$1\" branch) &&
+	test "$diff" = file
+'
+
+test_expect_success PERL 'difftool --extcmd cat arg1' '
+	diff=$(git difftool --no-prompt --extcmd sh\ -c\ \"cat\ \$1\" branch) &&
+	test "$diff" = master
+'
+
+test_expect_success PERL 'difftool --extcmd cat arg2' '
+	diff=$(git difftool --no-prompt --extcmd sh\ -c\ \"cat\ \$2\" branch) &&
+	test "$diff" = branch
+'
+
+# Create a second file on master and a different version on branch
+test_expect_success PERL 'setup with 2 files different' '
+	echo m2 >file2 &&
+	git add file2 &&
+	git commit -m "added file2" &&
+
+	git checkout branch &&
+	echo br2 >file2 &&
+	git add file2 &&
+	git commit -a -m "branch changed file2" &&
+	git checkout master
+'
+
+test_expect_success PERL 'say no to the first file' '
+	diff=$( (echo n; echo) | git difftool -x cat branch ) &&
+
+	echo "$diff" | stdin_contains m2 &&
+	echo "$diff" | stdin_contains br2 &&
+	echo "$diff" | stdin_doesnot_contain master &&
+	echo "$diff" | stdin_doesnot_contain branch
+'
+
+test_expect_success PERL 'say no to the second file' '
+	diff=$( (echo; echo n) | git difftool -x cat branch ) &&
+
+	echo "$diff" | stdin_contains master &&
+	echo "$diff" | stdin_contains branch &&
+	echo "$diff" | stdin_doesnot_contain m2 &&
+	echo "$diff" | stdin_doesnot_contain br2
+'
+
+test_expect_success PERL 'difftool --tool-help' '
+	tool_help=$(git difftool --tool-help) &&
+	echo "$tool_help" | stdin_contains tool
+'
+
+test_expect_success PERL 'setup change in subdirectory' '
+	git checkout master &&
+	mkdir sub &&
+	echo master >sub/sub &&
+	git add sub/sub &&
+	git commit -m "added sub/sub" &&
+	echo test >>file &&
+	echo test >>sub/sub &&
+	git add . &&
+	git commit -m "modified both"
+'
+
+test_expect_success PERL 'difftool -d' '
+	diff=$(git difftool -d --extcmd ls branch) &&
+	echo "$diff" | stdin_contains sub &&
+	echo "$diff" | stdin_contains file
+'
+
+test_expect_success PERL 'difftool --dir-diff' '
+	diff=$(git difftool --dir-diff --extcmd ls branch) &&
+	echo "$diff" | stdin_contains sub &&
+	echo "$diff" | stdin_contains file
+'
+
+test_expect_success PERL 'difftool --dir-diff ignores --prompt' '
+	diff=$(git difftool --dir-diff --prompt --extcmd ls branch) &&
+	echo "$diff" | stdin_contains sub &&
+	echo "$diff" | stdin_contains file
+'
+
+test_expect_success PERL 'difftool --dir-diff from subdirectory' '
+	(
+		cd sub &&
+		diff=$(git difftool --dir-diff --extcmd ls branch) &&
+		echo "$diff" | stdin_contains sub &&
+		echo "$diff" | stdin_contains file
+	)
 '
 
 test_done
diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh
new file mode 100755
index 0000000..24e9b19
--- /dev/null
+++ b/t/t7810-grep.sh
@@ -0,0 +1,924 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Junio C Hamano
+#
+
+test_description='git grep various.
+'
+
+. ./test-lib.sh
+
+cat >hello.c <<EOF
+#include <stdio.h>
+int main(int argc, const char **argv)
+{
+	printf("Hello world.\n");
+	return 0;
+	/* char ?? */
+}
+EOF
+
+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 Hello world
+		echo HeLLo world
+		echo Hello_world
+		echo HeLLo_world
+	} >hello_world &&
+	{
+		echo "a+b*c"
+		echo "a+bc"
+		echo "abc"
+	} >ab &&
+	echo vvv >v &&
+	echo ww w >w &&
+	echo x x xx x >x &&
+	echo y yy >y &&
+	echo zzz > z &&
+	mkdir t &&
+	echo test >t/t &&
+	echo vvv >t/v &&
+	mkdir t/a &&
+	echo vvv >t/a/v &&
+	{
+		echo "line without leading space1"
+		echo " line with leading space1"
+		echo " line with leading space2"
+		echo " line with leading space3"
+		echo "line without leading space2"
+	} >space &&
+	git add . &&
+	test_tick &&
+	git commit -m initial
+'
+
+test_expect_success 'grep should not segfault with a bad input' '
+	test_must_fail git grep "("
+'
+
+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 -c grep.linenumber=false grep -n -w -e mmap $H >actual &&
+		test_cmp expected actual
+	'
+
+	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 -c grep.linenumber=true grep -w -e mmap $H >actual &&
+		test_cmp expected actual
+	'
+
+	test_expect_success "grep -w $L" '
+		{
+			echo ${HC}file:foo mmap bar
+			echo ${HC}file:foo_mmap bar mmap
+			echo ${HC}file:foo mmap bar_mmap
+			echo ${HC}file:foo_mmap bar mmap baz
+		} >expected &&
+		git -c grep.linenumber=true grep --no-line-number -w -e mmap $H >actual &&
+		test_cmp expected actual
+	'
+
+	test_expect_success "grep -w $L (w)" '
+		: >expected &&
+		test_must_fail git grep -n -w -e "^w" >actual &&
+		test_cmp 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 &&
+		test_cmp 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 &&
+		test_cmp 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
+			test_cmp 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
+			test_cmp expected actual
+		fi
+	'
+
+	test_expect_success "grep $L (t-1)" '
+		echo "${HC}t/t:1:test" >expected &&
+		git grep -n -e test $H >actual &&
+		test_cmp expected actual
+	'
+
+	test_expect_success "grep $L (t-2)" '
+		echo "${HC}t:1:test" >expected &&
+		(
+			cd t &&
+			git grep -n -e test $H
+		) >actual &&
+		test_cmp expected actual
+	'
+
+	test_expect_success "grep $L (t-3)" '
+		echo "${HC}t/t:1:test" >expected &&
+		(
+			cd t &&
+			git grep --full-name -n -e test $H
+		) >actual &&
+		test_cmp expected actual
+	'
+
+	test_expect_success "grep -c $L (no /dev/null)" '
+		! git grep -c test $H | grep /dev/null
+        '
+
+	test_expect_success "grep --max-depth -1 $L" '
+		{
+			echo ${HC}t/a/v:1:vvv
+			echo ${HC}t/v:1:vvv
+			echo ${HC}v:1:vvv
+		} >expected &&
+		git grep --max-depth -1 -n -e vvv $H >actual &&
+		test_cmp expected actual
+	'
+
+	test_expect_success "grep --max-depth 0 $L" '
+		{
+			echo ${HC}v:1:vvv
+		} >expected &&
+		git grep --max-depth 0 -n -e vvv $H >actual &&
+		test_cmp expected actual
+	'
+
+	test_expect_success "grep --max-depth 0 -- '*' $L" '
+		{
+			echo ${HC}t/a/v:1:vvv
+			echo ${HC}t/v:1:vvv
+			echo ${HC}v:1:vvv
+		} >expected &&
+		git grep --max-depth 0 -n -e vvv $H -- "*" >actual &&
+		test_cmp expected actual
+	'
+
+	test_expect_success "grep --max-depth 1 $L" '
+		{
+			echo ${HC}t/v:1:vvv
+			echo ${HC}v:1:vvv
+		} >expected &&
+		git grep --max-depth 1 -n -e vvv $H >actual &&
+		test_cmp expected actual
+	'
+
+	test_expect_success "grep --max-depth 0 -- t $L" '
+		{
+			echo ${HC}t/v:1:vvv
+		} >expected &&
+		git grep --max-depth 0 -n -e vvv $H -- t >actual &&
+		test_cmp expected actual
+	'
+
+	test_expect_success "grep --max-depth 0 -- . t $L" '
+		{
+			echo ${HC}t/v:1:vvv
+			echo ${HC}v:1:vvv
+		} >expected &&
+		git grep --max-depth 0 -n -e vvv $H -- . t >actual &&
+		test_cmp expected actual
+	'
+
+	test_expect_success "grep --max-depth 0 -- t . $L" '
+		{
+			echo ${HC}t/v:1:vvv
+			echo ${HC}v:1:vvv
+		} >expected &&
+		git grep --max-depth 0 -n -e vvv $H -- t . >actual &&
+		test_cmp expected actual
+	'
+	test_expect_success "grep $L with grep.extendedRegexp=false" '
+		echo "ab:a+bc" >expected &&
+		git -c grep.extendedRegexp=false grep "a+b*c" ab >actual &&
+		test_cmp expected actual
+	'
+
+	test_expect_success "grep $L with grep.extendedRegexp=true" '
+		echo "ab:abc" >expected &&
+		git -c grep.extendedRegexp=true grep "a+b*c" ab >actual &&
+		test_cmp expected actual
+	'
+done
+
+cat >expected <<EOF
+file
+EOF
+test_expect_success 'grep -l -C' '
+	git grep -l -C1 foo >actual &&
+	test_cmp expected actual
+'
+
+cat >expected <<EOF
+file:5
+EOF
+test_expect_success 'grep -l -C' '
+	git grep -c -C1 foo >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'grep -L -C' '
+	git ls-files >expected &&
+	git grep -L -C1 nonexistent_string >actual &&
+	test_cmp expected actual
+'
+
+cat >expected <<EOF
+file:foo mmap bar_mmap
+EOF
+
+test_expect_success 'grep -e A --and -e B' '
+	git grep -e "foo mmap" --and -e bar_mmap >actual &&
+	test_cmp expected actual
+'
+
+cat >expected <<EOF
+file:foo_mmap bar mmap
+file:foo_mmap bar mmap baz
+EOF
+
+
+test_expect_success 'grep ( -e A --or -e B ) --and -e B' '
+	git grep \( -e foo_ --or -e baz \) \
+		--and -e " mmap" >actual &&
+	test_cmp expected actual
+'
+
+cat >expected <<EOF
+file:foo mmap bar
+EOF
+
+test_expect_success 'grep -e A --and --not -e B' '
+	git grep -e "foo mmap" --and --not -e bar_mmap >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'grep should ignore GREP_OPTIONS' '
+	GREP_OPTIONS=-v git grep " mmap bar\$" >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'grep -f, non-existent file' '
+	test_must_fail git grep -f patterns
+'
+
+cat >expected <<EOF
+file:foo mmap bar
+file:foo_mmap bar
+file:foo_mmap bar mmap
+file:foo mmap bar_mmap
+file:foo_mmap bar mmap baz
+EOF
+
+cat >pattern <<EOF
+mmap
+EOF
+
+test_expect_success 'grep -f, one pattern' '
+	git grep -f pattern >actual &&
+	test_cmp expected actual
+'
+
+cat >expected <<EOF
+file:foo mmap bar
+file:foo_mmap bar
+file:foo_mmap bar mmap
+file:foo mmap bar_mmap
+file:foo_mmap bar mmap baz
+t/a/v:vvv
+t/v:vvv
+v:vvv
+EOF
+
+cat >patterns <<EOF
+mmap
+vvv
+EOF
+
+test_expect_success 'grep -f, multiple patterns' '
+	git grep -f patterns >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'grep, multiple patterns' '
+	git grep "$(cat patterns)" >actual &&
+	test_cmp expected actual
+'
+
+cat >expected <<EOF
+file:foo mmap bar
+file:foo_mmap bar
+file:foo_mmap bar mmap
+file:foo mmap bar_mmap
+file:foo_mmap bar mmap baz
+t/a/v:vvv
+t/v:vvv
+v:vvv
+EOF
+
+cat >patterns <<EOF
+
+mmap
+
+vvv
+
+EOF
+
+test_expect_success 'grep -f, ignore empty lines' '
+	git grep -f patterns >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'grep -f, ignore empty lines, read patterns from stdin' '
+	git grep -f - <patterns >actual &&
+	test_cmp expected actual
+'
+
+cat >expected <<EOF
+y:y yy
+--
+z:zzz
+EOF
+
+test_expect_success 'grep -q, silently report matches' '
+	>empty &&
+	git grep -q mmap >actual &&
+	test_cmp empty actual &&
+	test_must_fail git grep -q qfwfq >actual &&
+	test_cmp empty actual
+'
+
+# Create 1024 file names that sort between "y" and "z" to make sure
+# the two files are handled by different calls to an external grep.
+# This depends on MAXARGS in builtin-grep.c being 1024 or less.
+c32="0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v"
+test_expect_success 'grep -C1, hunk mark between files' '
+	for a in $c32; do for b in $c32; do : >y-$a$b; done; done &&
+	git add y-?? &&
+	git grep -C1 "^[yz]" >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'grep -C1 hunk mark between files' '
+	git grep -C1 "^[yz]" >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'log grep setup' '
+	echo a >>file &&
+	test_tick &&
+	GIT_AUTHOR_NAME="With * Asterisk" \
+	GIT_AUTHOR_EMAIL="xyzzy@frotz.com" \
+	git commit -a -m "second" &&
+
+	echo a >>file &&
+	test_tick &&
+	git commit -a -m "third" &&
+
+	echo a >>file &&
+	test_tick &&
+	GIT_AUTHOR_NAME="Night Fall" \
+	GIT_AUTHOR_EMAIL="nitfol@frobozz.com" \
+	git commit -a -m "fourth"
+'
+
+test_expect_success 'log grep (1)' '
+	git log --author=author --pretty=tformat:%s >actual &&
+	( echo third ; echo initial ) >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'log grep (2)' '
+	git log --author=" * " -F --pretty=tformat:%s >actual &&
+	( echo second ) >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'log grep (3)' '
+	git log --author="^A U" --pretty=tformat:%s >actual &&
+	( echo third ; echo initial ) >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'log grep (4)' '
+	git log --author="frotz\.com>$" --pretty=tformat:%s >actual &&
+	( echo second ) >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'log grep (5)' '
+	git log --author=Thor -F --pretty=tformat:%s >actual &&
+	( echo third ; echo initial ) >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'log grep (6)' '
+	git log --author=-0700  --pretty=tformat:%s >actual &&
+	>expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'log --grep --author implicitly uses all-match' '
+	# grep matches initial and second but not third
+	# author matches only initial and third
+	git log --author="A U Thor" --grep=s --grep=l --format=%s >actual &&
+	echo initial >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'log with multiple --author uses union' '
+	git log --author="Thor" --author="Aster" --format=%s >actual &&
+	{
+	    echo third && echo second && echo initial
+	} >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'log with --grep and multiple --author uses all-match' '
+	git log --author="Thor" --author="Night" --grep=i --format=%s >actual &&
+	{
+	    echo third && echo initial
+	} >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'log with --grep and multiple --author uses all-match' '
+	git log --author="Thor" --author="Night" --grep=q --format=%s >actual &&
+	>expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'grep with CE_VALID file' '
+	git update-index --assume-unchanged t/t &&
+	rm t/t &&
+	test "$(git grep test)" = "t/t:test" &&
+	git update-index --no-assume-unchanged t/t &&
+	git checkout t/t
+'
+
+cat >expected <<EOF
+hello.c=#include <stdio.h>
+hello.c:	return 0;
+EOF
+
+test_expect_success 'grep -p with userdiff' '
+	git config diff.custom.funcname "^#" &&
+	echo "hello.c diff=custom" >.gitattributes &&
+	git grep -p return >actual &&
+	test_cmp expected actual
+'
+
+cat >expected <<EOF
+hello.c=int main(int argc, const char **argv)
+hello.c:	return 0;
+EOF
+
+test_expect_success 'grep -p' '
+	rm -f .gitattributes &&
+	git grep -p return >actual &&
+	test_cmp expected actual
+'
+
+cat >expected <<EOF
+hello.c-#include <stdio.h>
+hello.c=int main(int argc, const char **argv)
+hello.c-{
+hello.c-	printf("Hello world.\n");
+hello.c:	return 0;
+EOF
+
+test_expect_success 'grep -p -B5' '
+	git grep -p -B5 return >actual &&
+	test_cmp expected actual
+'
+
+cat >expected <<EOF
+hello.c=int main(int argc, const char **argv)
+hello.c-{
+hello.c-	printf("Hello world.\n");
+hello.c:	return 0;
+hello.c-	/* char ?? */
+hello.c-}
+EOF
+
+test_expect_success 'grep -W' '
+	git grep -W return >actual &&
+	test_cmp expected actual
+'
+
+cat >expected <<EOF
+hello.c=	printf("Hello world.\n");
+hello.c:	return 0;
+hello.c-	/* char ?? */
+EOF
+
+test_expect_success 'grep -W with userdiff' '
+	test_when_finished "rm -f .gitattributes" &&
+	git config diff.custom.xfuncname "(printf.*|})$" &&
+	echo "hello.c diff=custom" >.gitattributes &&
+	git grep -W return >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'grep from a subdirectory to search wider area (1)' '
+	mkdir -p s &&
+	(
+		cd s && git grep "x x x" ..
+	)
+'
+
+test_expect_success 'grep from a subdirectory to search wider area (2)' '
+	mkdir -p s &&
+	(
+		cd s || exit 1
+		( git grep xxyyzz .. >out ; echo $? >status )
+		! test -s out &&
+		test 1 = $(cat status)
+	)
+'
+
+cat >expected <<EOF
+hello.c:int main(int argc, const char **argv)
+EOF
+
+test_expect_success 'grep -Fi' '
+	git grep -Fi "CHAR *" >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'outside of git repository' '
+	rm -fr non &&
+	mkdir -p non/git/sub &&
+	echo hello >non/git/file1 &&
+	echo world >non/git/sub/file2 &&
+	{
+		echo file1:hello &&
+		echo sub/file2:world
+	} >non/expect.full &&
+	echo file2:world >non/expect.sub &&
+	(
+		GIT_CEILING_DIRECTORIES="$(pwd)/non/git" &&
+		export GIT_CEILING_DIRECTORIES &&
+		cd non/git &&
+		test_must_fail git grep o &&
+		git grep --no-index o >../actual.full &&
+		test_cmp ../expect.full ../actual.full
+		cd sub &&
+		test_must_fail git grep o &&
+		git grep --no-index o >../../actual.sub &&
+		test_cmp ../../expect.sub ../../actual.sub
+	) &&
+
+	echo ".*o*" >non/git/.gitignore &&
+	(
+		GIT_CEILING_DIRECTORIES="$(pwd)/non/git" &&
+		export GIT_CEILING_DIRECTORIES &&
+		cd non/git &&
+		test_must_fail git grep o &&
+		git grep --no-index --exclude-standard o >../actual.full &&
+		test_cmp ../expect.full ../actual.full &&
+
+		{
+			echo ".gitignore:.*o*"
+			cat ../expect.full
+		} >../expect.with.ignored &&
+		git grep --no-index --no-exclude o >../actual.full &&
+		test_cmp ../expect.with.ignored ../actual.full
+	)
+'
+
+test_expect_success 'inside git repository but with --no-index' '
+	rm -fr is &&
+	mkdir -p is/git/sub &&
+	echo hello >is/git/file1 &&
+	echo world >is/git/sub/file2 &&
+	echo ".*o*" >is/git/.gitignore &&
+	{
+		echo file1:hello &&
+		echo sub/file2:world
+	} >is/expect.unignored &&
+	{
+		echo ".gitignore:.*o*" &&
+		cat is/expect.unignored
+	} >is/expect.full &&
+	: >is/expect.empty &&
+	echo file2:world >is/expect.sub &&
+	(
+		cd is/git &&
+		git init &&
+		test_must_fail git grep o >../actual.full &&
+		test_cmp ../expect.empty ../actual.full &&
+
+		git grep --untracked o >../actual.unignored &&
+		test_cmp ../expect.unignored ../actual.unignored &&
+
+		git grep --no-index o >../actual.full &&
+		test_cmp ../expect.full ../actual.full &&
+
+		git grep --no-index --exclude-standard o >../actual.unignored &&
+		test_cmp ../expect.unignored ../actual.unignored &&
+
+		cd sub &&
+		test_must_fail git grep o >../../actual.sub &&
+		test_cmp ../../expect.empty ../../actual.sub &&
+
+		git grep --no-index o >../../actual.sub &&
+		test_cmp ../../expect.sub ../../actual.sub &&
+
+		git grep --untracked o >../../actual.sub &&
+		test_cmp ../../expect.sub ../../actual.sub
+	)
+'
+
+test_expect_success 'setup double-dash tests' '
+cat >double-dash <<EOF &&
+--
+->
+other
+EOF
+git add double-dash
+'
+
+cat >expected <<EOF
+double-dash:->
+EOF
+test_expect_success 'grep -- pattern' '
+	git grep -- "->" >actual &&
+	test_cmp expected actual
+'
+test_expect_success 'grep -- pattern -- pathspec' '
+	git grep -- "->" -- double-dash >actual &&
+	test_cmp expected actual
+'
+test_expect_success 'grep -e pattern -- path' '
+	git grep -e "->" -- double-dash >actual &&
+	test_cmp expected actual
+'
+
+cat >expected <<EOF
+double-dash:--
+EOF
+test_expect_success 'grep -e -- -- path' '
+	git grep -e -- -- double-dash >actual &&
+	test_cmp expected actual
+'
+
+cat >expected <<EOF
+hello.c:int main(int argc, const char **argv)
+hello.c:	printf("Hello world.\n");
+EOF
+
+test_expect_success LIBPCRE 'grep --perl-regexp pattern' '
+	git grep --perl-regexp "\p{Ps}.*?\p{Pe}" hello.c >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success LIBPCRE 'grep -P pattern' '
+	git grep -P "\p{Ps}.*?\p{Pe}" hello.c >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'grep pattern with grep.extendedRegexp=true' '
+	>empty &&
+	test_must_fail git -c grep.extendedregexp=true \
+		grep "\p{Ps}.*?\p{Pe}" hello.c >actual &&
+	test_cmp empty actual
+'
+
+test_expect_success LIBPCRE 'grep -P pattern with grep.extendedRegexp=true' '
+	git -c grep.extendedregexp=true \
+		grep -P "\p{Ps}.*?\p{Pe}" hello.c >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success LIBPCRE 'grep -P -v pattern' '
+	{
+		echo "ab:a+b*c"
+		echo "ab:a+bc"
+	} >expected &&
+	git grep -P -v "abc" ab >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success LIBPCRE 'grep -P -i pattern' '
+	cat >expected <<-EOF &&
+	hello.c:	printf("Hello world.\n");
+	EOF
+	git grep -P -i "PRINTF\([^\d]+\)" hello.c >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success LIBPCRE 'grep -P -w pattern' '
+	{
+		echo "hello_world:Hello world"
+		echo "hello_world:HeLLo world"
+	} >expected &&
+	git grep -P -w "He((?i)ll)o" hello_world >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'grep -G invalidpattern properly dies ' '
+	test_must_fail git grep -G "a["
+'
+
+test_expect_success 'grep -E invalidpattern properly dies ' '
+	test_must_fail git grep -E "a["
+'
+
+test_expect_success LIBPCRE 'grep -P invalidpattern properly dies ' '
+	test_must_fail git grep -P "a["
+'
+
+test_expect_success 'grep -G -E -F pattern' '
+	echo "ab:a+b*c" >expected &&
+	git grep -G -E -F "a+b*c" ab >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'grep -E -F -G pattern' '
+	echo "ab:a+bc" >expected &&
+	git grep -E -F -G "a+b*c" ab >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'grep -F -G -E pattern' '
+	echo "ab:abc" >expected &&
+	git grep -F -G -E "a+b*c" ab >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'grep -G -F -P -E pattern' '
+	>empty &&
+	test_must_fail git grep -G -F -P -E "a\x{2b}b\x{2a}c" ab >actual &&
+	test_cmp empty actual
+'
+
+test_expect_success LIBPCRE 'grep -G -F -E -P pattern' '
+	echo "ab:a+b*c" >expected &&
+	git grep -G -F -E -P "a\x{2b}b\x{2a}c" ab >actual &&
+	test_cmp expected actual
+'
+
+test_config() {
+	git config "$1" "$2" &&
+	test_when_finished "git config --unset $1"
+}
+
+cat >expected <<EOF
+hello.c<RED>:<RESET>int main(int argc, const char **argv)
+hello.c<RED>-<RESET>{
+<RED>--<RESET>
+hello.c<RED>:<RESET>	/* char ?? */
+hello.c<RED>-<RESET>}
+<RED>--<RESET>
+hello_world<RED>:<RESET>Hello_world
+hello_world<RED>-<RESET>HeLLo_world
+EOF
+
+test_expect_success 'grep --color, separator' '
+	test_config color.grep.context		normal &&
+	test_config color.grep.filename		normal &&
+	test_config color.grep.function		normal &&
+	test_config color.grep.linenumber	normal &&
+	test_config color.grep.match		normal &&
+	test_config color.grep.selected		normal &&
+	test_config color.grep.separator	red &&
+
+	git grep --color=always -A1 -e char -e lo_w hello.c hello_world |
+	test_decode_color >actual &&
+	test_cmp expected actual
+'
+
+cat >expected <<EOF
+hello.c:int main(int argc, const char **argv)
+hello.c:	/* char ?? */
+
+hello_world:Hello_world
+EOF
+
+test_expect_success 'grep --break' '
+	git grep --break -e char -e lo_w hello.c hello_world >actual &&
+	test_cmp expected actual
+'
+
+cat >expected <<EOF
+hello.c:int main(int argc, const char **argv)
+hello.c-{
+--
+hello.c:	/* char ?? */
+hello.c-}
+
+hello_world:Hello_world
+hello_world-HeLLo_world
+EOF
+
+test_expect_success 'grep --break with context' '
+	git grep --break -A1 -e char -e lo_w hello.c hello_world >actual &&
+	test_cmp expected actual
+'
+
+cat >expected <<EOF
+hello.c
+int main(int argc, const char **argv)
+	/* char ?? */
+hello_world
+Hello_world
+EOF
+
+test_expect_success 'grep --heading' '
+	git grep --heading -e char -e lo_w hello.c hello_world >actual &&
+	test_cmp expected actual
+'
+
+cat >expected <<EOF
+<BOLD;GREEN>hello.c<RESET>
+2:int main(int argc, const <BLACK;BYELLOW>char<RESET> **argv)
+6:	/* <BLACK;BYELLOW>char<RESET> ?? */
+
+<BOLD;GREEN>hello_world<RESET>
+3:Hel<BLACK;BYELLOW>lo_w<RESET>orld
+EOF
+
+test_expect_success 'mimic ack-grep --group' '
+	test_config color.grep.context		normal &&
+	test_config color.grep.filename		"bold green" &&
+	test_config color.grep.function		normal &&
+	test_config color.grep.linenumber	normal &&
+	test_config color.grep.match		"black yellow" &&
+	test_config color.grep.selected		normal &&
+	test_config color.grep.separator	normal &&
+
+	git grep --break --heading -n --color \
+		-e char -e lo_w hello.c hello_world |
+	test_decode_color >actual &&
+	test_cmp expected actual
+'
+
+cat >expected <<EOF
+space: line with leading space1
+space: line with leading space2
+space: line with leading space3
+EOF
+
+test_expect_success LIBPCRE 'grep -E "^ "' '
+	git grep -E "^ " space >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success LIBPCRE 'grep -P "^ "' '
+	git grep -P "^ " space >actual &&
+	test_cmp expected actual
+'
+
+test_done
diff --git a/t/t7811-grep-open.sh b/t/t7811-grep-open.sh
new file mode 100755
index 0000000..a895778
--- /dev/null
+++ b/t/t7811-grep-open.sh
@@ -0,0 +1,168 @@
+#!/bin/sh
+
+test_description='git grep --open-files-in-pager
+'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-pager.sh
+unset PAGER GIT_PAGER
+
+test_expect_success 'setup' '
+	test_commit initial grep.h "
+enum grep_pat_token {
+	GREP_PATTERN,
+	GREP_PATTERN_HEAD,
+	GREP_PATTERN_BODY,
+	GREP_AND,
+	GREP_OPEN_PAREN,
+	GREP_CLOSE_PAREN,
+	GREP_NOT,
+	GREP_OR,
+};" &&
+
+	test_commit add-user revision.c "
+	}
+	if (seen_dashdash)
+		read_pathspec_from_stdin(revs, &sb, prune);
+	strbuf_release(&sb);
+}
+
+static void add_grep(struct rev_info *revs, const char *ptn, enum grep_pat_token what)
+{
+	append_grep_pattern(&revs->grep_filter, ptn, \"command line\", 0, what);
+" &&
+
+	mkdir subdir &&
+	test_commit subdir subdir/grep.c "enum grep_pat_token" &&
+
+	test_commit uninteresting unrelated "hello, world" &&
+
+	echo GREP_PATTERN >untracked
+'
+
+test_expect_success SIMPLEPAGER 'git grep -O' '
+	cat >$less <<-\EOF &&
+	#!/bin/sh
+	printf "%s\n" "$@" >pager-args
+	EOF
+	chmod +x $less &&
+	cat >expect.less <<-\EOF &&
+	+/*GREP_PATTERN
+	grep.h
+	EOF
+	echo grep.h >expect.notless &&
+	>empty &&
+
+	PATH=.:$PATH git grep -O GREP_PATTERN >out &&
+	{
+		test_cmp expect.less pager-args ||
+		test_cmp expect.notless pager-args
+	} &&
+	test_cmp empty out
+'
+
+test_expect_success 'git grep -O --cached' '
+	test_must_fail git grep --cached -O GREP_PATTERN >out 2>msg &&
+	test_i18ngrep open-files-in-pager msg
+'
+
+test_expect_success 'git grep -O --no-index' '
+	rm -f expect.less pager-args out &&
+	cat >expect <<-\EOF &&
+	grep.h
+	untracked
+	EOF
+	>empty &&
+
+	(
+		GIT_PAGER='\''printf "%s\n" >pager-args'\'' &&
+		export GIT_PAGER &&
+		git grep --no-index -O GREP_PATTERN >out
+	) &&
+	test_cmp expect pager-args &&
+	test_cmp empty out
+'
+
+test_expect_success 'setup: fake "less"' '
+	cat >less <<-\EOF &&
+	#!/bin/sh
+	printf "%s\n" "$@" >actual
+	EOF
+	chmod +x less
+'
+
+test_expect_success 'git grep -O jumps to line in less' '
+	cat >expect <<-\EOF &&
+	+/*GREP_PATTERN
+	grep.h
+	EOF
+	>empty &&
+
+	GIT_PAGER=./less git grep -O GREP_PATTERN >out &&
+	test_cmp expect actual &&
+	test_cmp empty out &&
+
+	git grep -O./less GREP_PATTERN >out2 &&
+	test_cmp expect actual &&
+	test_cmp empty out2
+'
+
+test_expect_success 'modified file' '
+	rm -f actual &&
+	cat >expect <<-\EOF &&
+	+/*enum grep_pat_token
+	grep.h
+	revision.c
+	subdir/grep.c
+	unrelated
+	EOF
+	>empty &&
+
+	echo "enum grep_pat_token" >unrelated &&
+	test_when_finished "git checkout HEAD unrelated" &&
+	GIT_PAGER=./less git grep -F -O "enum grep_pat_token" >out &&
+	test_cmp expect actual &&
+	test_cmp empty out
+'
+
+test_config() {
+	git config "$1" "$2" &&
+	test_when_finished "git config --unset $1"
+}
+
+test_expect_success 'copes with color settings' '
+	rm -f actual &&
+	echo grep.h >expect &&
+	test_config color.grep always &&
+	test_config color.grep.filename yellow &&
+	test_config color.grep.separator green &&
+	git grep -O'\''printf "%s\n" >actual'\'' GREP_AND &&
+	test_cmp expect actual
+'
+
+test_expect_success 'run from subdir' '
+	rm -f actual &&
+	echo grep.c >expect &&
+	>empty &&
+
+	(
+		cd subdir &&
+		export GIT_PAGER &&
+		GIT_PAGER='\''printf "%s\n" >../args'\'' &&
+		git grep -O "enum grep_pat_token" >../out &&
+		git grep -O"pwd >../dir; :" "enum grep_pat_token" >../out2
+	) &&
+	case $(cat dir) in
+	*subdir)
+		: good
+		;;
+	*)
+		false
+		;;
+	esac &&
+	test_cmp expect args &&
+	test_cmp empty out &&
+	test_cmp empty out2
+'
+
+test_done
diff --git a/t/t8001-annotate.sh b/t/t8001-annotate.sh
index 45cb60e..41962f0 100755
--- a/t/t8001-annotate.sh
+++ b/t/t8001-annotate.sh
@@ -6,10 +6,11 @@
 PROG='git annotate'
 . "$TEST_DIRECTORY"/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_expect_success 'Annotating an old revision works' '
+	git annotate file master >result &&
+	awk "{ print \$3; }" <result >authors &&
+	test 2 = $(grep A <authors | wc -l) &&
+	test 2 = $(grep B <authors | wc -l)
+'
 
 test_done
diff --git a/t/t8002-blame.sh b/t/t8002-blame.sh
index 597cf04..e2896cf 100755
--- a/t/t8002-blame.sh
+++ b/t/t8002-blame.sh
@@ -6,4 +6,9 @@
 PROG='git blame -c'
 . "$TEST_DIRECTORY"/annotate-tests.sh
 
+PROG='git blame -c -e'
+test_expect_success 'Blame --show-email works' '
+    check_count "<A@test.git>" 1 "<B@test.git>" 1 "<B1@test.git>" 1 "<B2@test.git>" 1 "<author@example.com>" 1 "<C@test.git>" 1 "<D@test.git>" 1 "<E at test dot git>" 1
+'
+
 test_done
diff --git a/t/t8003-blame-corner-cases.sh b/t/t8003-blame-corner-cases.sh
new file mode 100755
index 0000000..230143c
--- /dev/null
+++ b/t/t8003-blame-corner-cases.sh
@@ -0,0 +1,188 @@
+#!/bin/sh
+
+test_description='git blame corner cases'
+. ./test-lib.sh
+
+pick_fc='s/^[0-9a-f^]* *\([^ ]*\) *(\([^ ]*\) .*/\1-\2/'
+
+test_expect_success setup '
+
+	echo A A A A A >one &&
+	echo B B B B B >two &&
+	echo C C C C C >tres &&
+	echo ABC >mouse &&
+	for i in 1 2 3 4 5 6 7 8 9
+	do
+		echo $i
+	done >nine_lines &&
+	for i in 1 2 3 4 5 6 7 8 9 a
+	do
+		echo $i
+	done >ten_lines &&
+	git add one two tres mouse nine_lines ten_lines &&
+	test_tick &&
+	GIT_AUTHOR_NAME=Initial git commit -m Initial &&
+
+	cat one >uno &&
+	mv two dos &&
+	cat one >>tres &&
+	echo DEF >>mouse
+	git add uno dos tres mouse &&
+	test_tick &&
+	GIT_AUTHOR_NAME=Second git commit -a -m Second &&
+
+	echo GHIJK >>mouse &&
+	git add mouse &&
+	test_tick &&
+	GIT_AUTHOR_NAME=Third git commit -m Third &&
+
+	cat mouse >cow &&
+	git add cow &&
+	test_tick &&
+	GIT_AUTHOR_NAME=Fourth git commit -m Fourth &&
+
+	{
+		echo ABC
+		echo DEF
+		echo XXXX
+		echo GHIJK
+	} >cow &&
+	git add cow &&
+	test_tick &&
+	GIT_AUTHOR_NAME=Fifth git commit -m Fifth
+'
+
+test_expect_success 'straight copy without -C' '
+
+	git blame uno | grep Second
+
+'
+
+test_expect_success 'straight move without -C' '
+
+	git blame dos | grep Initial
+
+'
+
+test_expect_success 'straight copy with -C' '
+
+	git blame -C1 uno | grep Second
+
+'
+
+test_expect_success 'straight move with -C' '
+
+	git blame -C1 dos | grep Initial
+
+'
+
+test_expect_success 'straight copy with -C -C' '
+
+	git blame -C -C1 uno | grep Initial
+
+'
+
+test_expect_success 'straight move with -C -C' '
+
+	git blame -C -C1 dos | grep Initial
+
+'
+
+test_expect_success 'append without -C' '
+
+	git blame -L2 tres | grep Second
+
+'
+
+test_expect_success 'append with -C' '
+
+	git blame -L2 -C1 tres | grep Second
+
+'
+
+test_expect_success 'append with -C -C' '
+
+	git blame -L2 -C -C1 tres | grep Second
+
+'
+
+test_expect_success 'append with -C -C -C' '
+
+	git blame -L2 -C -C -C1 tres | grep Initial
+
+'
+
+test_expect_success 'blame wholesale copy' '
+
+	git blame -f -C -C1 HEAD^ -- cow | sed -e "$pick_fc" >current &&
+	{
+		echo mouse-Initial
+		echo mouse-Second
+		echo mouse-Third
+	} >expected &&
+	test_cmp expected current
+
+'
+
+test_expect_success 'blame wholesale copy and more' '
+
+	git blame -f -C -C1 HEAD -- cow | sed -e "$pick_fc" >current &&
+	{
+		echo mouse-Initial
+		echo mouse-Second
+		echo cow-Fifth
+		echo mouse-Third
+	} >expected &&
+	test_cmp expected current
+
+'
+
+test_expect_success 'blame path that used to be a directory' '
+	mkdir path &&
+	echo A A A A A >path/file &&
+	echo B B B B B >path/elif &&
+	git add path &&
+	test_tick &&
+	git commit -m "path was a directory" &&
+	rm -fr path &&
+	echo A A A A A >path &&
+	git add path &&
+	test_tick &&
+	git commit -m "path is a regular file" &&
+	git blame HEAD^.. -- path
+'
+
+test_expect_success 'blame to a commit with no author name' '
+  TREE=`git rev-parse HEAD:`
+  cat >badcommit <<EOF
+tree $TREE
+author <noname> 1234567890 +0000
+committer David Reiss <dreiss@facebook.com> 1234567890 +0000
+
+some message
+EOF
+  COMMIT=`git hash-object -t commit -w badcommit`
+  git --no-pager blame $COMMIT -- uno >/dev/null
+'
+
+test_expect_success 'blame -L with invalid start' '
+	test_must_fail git blame -L5 tres 2>errors &&
+	grep "has only 2 lines" errors
+'
+
+test_expect_success 'blame -L with invalid end' '
+	test_must_fail git blame -L1,5 tres 2>errors &&
+	grep "has only 2 lines" errors
+'
+
+test_expect_success 'indent of line numbers, nine lines' '
+	git blame nine_lines >actual &&
+	test $(grep -c "  " actual) = 0
+'
+
+test_expect_success 'indent of line numbers, ten lines' '
+	git blame ten_lines >actual &&
+	test $(grep -c "  " actual) = 9
+'
+
+test_done
diff --git a/t/t8003-blame.sh b/t/t8003-blame.sh
deleted file mode 100755
index 13c25f1..0000000
--- a/t/t8003-blame.sh
+++ /dev/null
@@ -1,147 +0,0 @@
-#!/bin/sh
-
-test_description='git blame corner cases'
-. ./test-lib.sh
-
-pick_fc='s/^[0-9a-f^]* *\([^ ]*\) *(\([^ ]*\) .*/\1-\2/'
-
-test_expect_success setup '
-
-	echo A A A A A >one &&
-	echo B B B B B >two &&
-	echo C C C C C >tres &&
-	echo ABC >mouse &&
-	git add one two tres mouse &&
-	test_tick &&
-	GIT_AUTHOR_NAME=Initial git commit -m Initial &&
-
-	cat one >uno &&
-	mv two dos &&
-	cat one >>tres &&
-	echo DEF >>mouse
-	git add uno dos tres mouse &&
-	test_tick &&
-	GIT_AUTHOR_NAME=Second git commit -a -m Second &&
-
-	echo GHIJK >>mouse &&
-	git add mouse &&
-	test_tick &&
-	GIT_AUTHOR_NAME=Third git commit -m Third &&
-
-	cat mouse >cow &&
-	git add cow &&
-	test_tick &&
-	GIT_AUTHOR_NAME=Fourth git commit -m Fourth &&
-
-	{
-		echo ABC
-		echo DEF
-		echo XXXX
-		echo GHIJK
-	} >cow &&
-	git add cow &&
-	test_tick &&
-	GIT_AUTHOR_NAME=Fifth git commit -m Fifth
-'
-
-test_expect_success 'straight copy without -C' '
-
-	git blame uno | grep Second
-
-'
-
-test_expect_success 'straight move without -C' '
-
-	git blame dos | grep Initial
-
-'
-
-test_expect_success 'straight copy with -C' '
-
-	git blame -C1 uno | grep Second
-
-'
-
-test_expect_success 'straight move with -C' '
-
-	git blame -C1 dos | grep Initial
-
-'
-
-test_expect_success 'straight copy with -C -C' '
-
-	git blame -C -C1 uno | grep Initial
-
-'
-
-test_expect_success 'straight move with -C -C' '
-
-	git blame -C -C1 dos | grep Initial
-
-'
-
-test_expect_success 'append without -C' '
-
-	git blame -L2 tres | grep Second
-
-'
-
-test_expect_success 'append with -C' '
-
-	git blame -L2 -C1 tres | grep Second
-
-'
-
-test_expect_success 'append with -C -C' '
-
-	git blame -L2 -C -C1 tres | grep Second
-
-'
-
-test_expect_success 'append with -C -C -C' '
-
-	git blame -L2 -C -C -C1 tres | grep Initial
-
-'
-
-test_expect_success 'blame wholesale copy' '
-
-	git blame -f -C -C1 HEAD^ -- cow | sed -e "$pick_fc" >current &&
-	{
-		echo mouse-Initial
-		echo mouse-Second
-		echo mouse-Third
-	} >expected &&
-	test_cmp expected current
-
-'
-
-test_expect_success 'blame wholesale copy and more' '
-
-	git blame -f -C -C1 HEAD -- cow | sed -e "$pick_fc" >current &&
-	{
-		echo mouse-Initial
-		echo mouse-Second
-		echo cow-Fifth
-		echo mouse-Third
-	} >expected &&
-	test_cmp expected current
-
-'
-
-test_expect_success 'blame path that used to be a directory' '
-	mkdir path &&
-	echo A A A A A >path/file &&
-	echo B B B B B >path/elif &&
-	git add path &&
-	test_tick &&
-	git commit -m "path was a directory" &&
-	rm -fr path &&
-	echo A A A A A >path &&
-	git add path &&
-	test_tick &&
-	git commit -m "path is a regular file" &&
-	git blame HEAD^.. -- path
-'
-
-test_done
diff --git a/t/t8004-blame.sh b/t/t8004-blame-with-conflicts.sh
similarity index 100%
rename from t/t8004-blame.sh
rename to t/t8004-blame-with-conflicts.sh
diff --git a/t/t8005/iso8859-5.txt b/t/t8005/iso8859-5.txt
deleted file mode 100644
index 2e4b80c..0000000
--- a/t/t8005/iso8859-5.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-ISO8859_5_NAME="¸ÒÐÝ ¿ÕâàÞÒØç ÁØÔÞàÞÒ"
-ISO8859_5_MSG="ÂÕáâÞÒÞÕ áÞÞÑéÕÝØÕ"
diff --git a/t/t8006-blame-textconv.sh b/t/t8006-blame-textconv.sh
new file mode 100755
index 0000000..bf6caa4
--- /dev/null
+++ b/t/t8006-blame-textconv.sh
@@ -0,0 +1,161 @@
+#!/bin/sh
+
+test_description='git blame textconv support'
+. ./test-lib.sh
+
+find_blame() {
+	sed -e 's/^[^(]*//'
+}
+
+cat >helper <<'EOF'
+#!/bin/sh
+grep -q '^bin: ' "$1" || { echo "E: $1 is not \"binary\" file" 1>&2; exit 1; }
+"$PERL_PATH" -p -e 's/^bin: /converted: /' "$1"
+EOF
+chmod +x helper
+
+test_expect_success 'setup ' '
+	echo "bin: test number 0" >zero.bin &&
+	echo "bin: test 1" >one.bin &&
+	echo "bin: test number 2" >two.bin &&
+	if test_have_prereq SYMLINKS; then
+		ln -s one.bin symlink.bin
+	fi &&
+	git add . &&
+	GIT_AUTHOR_NAME=Number1 git commit -a -m First --date="2010-01-01 18:00:00" &&
+	echo "bin: test 1 version 2" >one.bin &&
+	echo "bin: test number 2 version 2" >>two.bin &&
+	if test_have_prereq SYMLINKS; then
+		rm symlink.bin &&
+		ln -s two.bin symlink.bin
+	fi &&
+	GIT_AUTHOR_NAME=Number2 git commit -a -m Second --date="2010-01-01 20:00:00"
+'
+
+cat >expected <<EOF
+(Number2 2010-01-01 20:00:00 +0000 1) bin: test 1 version 2
+EOF
+
+test_expect_success 'no filter specified' '
+	git blame one.bin >blame &&
+	find_blame Number2 <blame >result &&
+	test_cmp expected result
+'
+
+test_expect_success 'setup textconv filters' '
+	echo "*.bin diff=test" >.gitattributes &&
+	echo "zero.bin eol=crlf" >>.gitattributes &&
+	git config diff.test.textconv ./helper &&
+	git config diff.test.cachetextconv false
+'
+
+test_expect_success 'blame with --no-textconv' '
+	git blame --no-textconv one.bin >blame &&
+	find_blame <blame> result &&
+	test_cmp expected result
+'
+
+cat >expected <<EOF
+(Number2 2010-01-01 20:00:00 +0000 1) converted: test 1 version 2
+EOF
+
+test_expect_success 'basic blame on last commit' '
+	git blame one.bin >blame &&
+	find_blame  <blame >result &&
+	test_cmp expected result
+'
+
+cat >expected <<EOF
+(Number1 2010-01-01 18:00:00 +0000 1) converted: test number 2
+(Number2 2010-01-01 20:00:00 +0000 2) converted: test number 2 version 2
+EOF
+
+test_expect_success 'blame --textconv going through revisions' '
+	git blame --textconv two.bin >blame &&
+	find_blame <blame >result &&
+	test_cmp expected result
+'
+
+test_expect_success 'blame --textconv with local changes' '
+	test_when_finished "git checkout zero.bin" &&
+	printf "bin: updated number 0\015" >zero.bin &&
+	git blame --textconv zero.bin >blame &&
+	expect="(Not Committed Yet ....-..-.. ..:..:.. +0000 1)" &&
+	expect="$expect converted: updated number 0" &&
+	expr "$(find_blame <blame)" : "^$expect"
+'
+
+test_expect_success 'setup +cachetextconv' '
+	git config diff.test.cachetextconv true
+'
+
+cat >expected_one <<EOF
+(Number2 2010-01-01 20:00:00 +0000 1) converted: test 1 version 2
+EOF
+
+test_expect_success 'blame --textconv works with textconvcache' '
+	git blame --textconv two.bin >blame &&
+	find_blame <blame >result &&
+	test_cmp expected result &&
+	git blame --textconv one.bin >blame &&
+	find_blame  <blame >result &&
+	test_cmp expected_one result
+'
+
+test_expect_success 'setup -cachetextconv' '
+	git config diff.test.cachetextconv false
+'
+
+test_expect_success 'make a new commit' '
+	echo "bin: test number 2 version 3" >>two.bin &&
+	GIT_AUTHOR_NAME=Number3 git commit -a -m Third --date="2010-01-01 22:00:00"
+'
+
+test_expect_success 'blame from previous revision' '
+	git blame HEAD^ two.bin >blame &&
+	find_blame <blame >result &&
+	test_cmp expected result
+'
+
+cat >expected <<EOF
+(Number2 2010-01-01 20:00:00 +0000 1) two.bin
+EOF
+
+test_expect_success SYMLINKS 'blame with --no-textconv (on symlink)' '
+	git blame --no-textconv symlink.bin >blame &&
+	find_blame <blame >result &&
+	test_cmp expected result
+'
+
+test_expect_success SYMLINKS 'blame --textconv (on symlink)' '
+	git blame --textconv symlink.bin >blame &&
+	find_blame <blame >result &&
+	test_cmp expected result
+'
+
+# cp two.bin three.bin  and make small tweak
+# (this will direct blame -C -C three.bin to consider two.bin and symlink.bin)
+test_expect_success SYMLINKS 'make another new commit' '
+	cat >three.bin <<\EOF &&
+bin: test number 2
+bin: test number 2 version 2
+bin: test number 2 version 3
+bin: test number 3
+EOF
+	git add three.bin &&
+	GIT_AUTHOR_NAME=Number4 git commit -a -m Fourth --date="2010-01-01 23:00:00"
+'
+
+test_expect_success SYMLINKS 'blame on last commit (-C -C, symlink)' '
+	git blame -C -C three.bin >blame &&
+	find_blame <blame >result &&
+	cat >expected <<\EOF &&
+(Number1 2010-01-01 18:00:00 +0000 1) converted: test number 2
+(Number2 2010-01-01 20:00:00 +0000 2) converted: test number 2 version 2
+(Number3 2010-01-01 22:00:00 +0000 3) converted: test number 2 version 3
+(Number4 2010-01-01 23:00:00 +0000 4) converted: test number 3
+EOF
+	test_cmp expected result
+'
+
+test_done
diff --git a/t/t8007-cat-file-textconv.sh b/t/t8007-cat-file-textconv.sh
new file mode 100755
index 0000000..78a0085
--- /dev/null
+++ b/t/t8007-cat-file-textconv.sh
@@ -0,0 +1,98 @@
+#!/bin/sh
+
+test_description='git cat-file textconv support'
+. ./test-lib.sh
+
+cat >helper <<'EOF'
+#!/bin/sh
+grep -q '^bin: ' "$1" || { echo "E: $1 is not \"binary\" file" 1>&2; exit 1; }
+sed 's/^bin: /converted: /' "$1"
+EOF
+chmod +x helper
+
+test_expect_success 'setup ' '
+	echo "bin: test" >one.bin &&
+	if test_have_prereq SYMLINKS; then
+		ln -s one.bin symlink.bin
+	fi &&
+	git add . &&
+	GIT_AUTHOR_NAME=Number1 git commit -a -m First --date="2010-01-01 18:00:00" &&
+	echo "bin: test version 2" >one.bin &&
+	GIT_AUTHOR_NAME=Number2 git commit -a -m Second --date="2010-01-01 20:00:00"
+'
+
+cat >expected <<EOF
+fatal: git cat-file --textconv: unable to run textconv on :one.bin
+EOF
+
+test_expect_success 'no filter specified' '
+	git cat-file --textconv :one.bin 2>result
+	test_cmp expected result
+'
+
+test_expect_success 'setup textconv filters' '
+	echo "*.bin diff=test" >.gitattributes &&
+	git config diff.test.textconv ./helper &&
+	git config diff.test.cachetextconv false
+'
+
+cat >expected <<EOF
+bin: test version 2
+EOF
+
+test_expect_success 'cat-file without --textconv' '
+	git cat-file blob :one.bin >result &&
+	test_cmp expected result
+'
+
+cat >expected <<EOF
+bin: test
+EOF
+
+test_expect_success 'cat-file without --textconv on previous commit' '
+	git cat-file -p HEAD^:one.bin >result &&
+	test_cmp expected result
+'
+
+cat >expected <<EOF
+converted: test version 2
+EOF
+
+test_expect_success 'cat-file --textconv on last commit' '
+	git cat-file --textconv :one.bin >result &&
+	test_cmp expected result
+'
+
+cat >expected <<EOF
+converted: test
+EOF
+
+test_expect_success 'cat-file --textconv on previous commit' '
+	git cat-file --textconv HEAD^:one.bin >result &&
+	test_cmp expected result
+'
+
+test_expect_success SYMLINKS 'cat-file without --textconv (symlink)' '
+	git cat-file blob :symlink.bin >result &&
+	printf "%s" "one.bin" >expected
+	test_cmp expected result
+'
+
+
+test_expect_success SYMLINKS 'cat-file --textconv on index (symlink)' '
+	! git cat-file --textconv :symlink.bin 2>result &&
+	cat >expected <<\EOF &&
+fatal: git cat-file --textconv: unable to run textconv on :symlink.bin
+EOF
+	test_cmp expected result
+'
+
+test_expect_success SYMLINKS 'cat-file --textconv on HEAD (symlink)' '
+	! git cat-file --textconv HEAD:symlink.bin 2>result &&
+	cat >expected <<EOF &&
+fatal: git cat-file --textconv: unable to run textconv on HEAD:symlink.bin
+EOF
+	test_cmp expected result
+'
+
+test_done
diff --git a/t/t8008-blame-formats.sh b/t/t8008-blame-formats.sh
new file mode 100755
index 0000000..d15f8b3
--- /dev/null
+++ b/t/t8008-blame-formats.sh
@@ -0,0 +1,90 @@
+#!/bin/sh
+
+test_description='blame output in various formats on a simple case'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	echo a >file &&
+	git add file
+	test_tick &&
+	git commit -m one &&
+	echo b >>file &&
+	echo c >>file &&
+	echo d >>file &&
+	test_tick &&
+	git commit -a -m two
+'
+
+cat >expect <<'EOF'
+^baf5e0b (A U Thor 2005-04-07 15:13:13 -0700 1) a
+8825379d (A U Thor 2005-04-07 15:14:13 -0700 2) b
+8825379d (A U Thor 2005-04-07 15:14:13 -0700 3) c
+8825379d (A U Thor 2005-04-07 15:14:13 -0700 4) d
+EOF
+test_expect_success 'normal blame output' '
+	git blame file >actual &&
+	test_cmp expect actual
+'
+
+ID1=baf5e0b3869e0b2b2beb395a3720c7b51eac94fc
+COMMIT1='author A U Thor
+author-mail <author@example.com>
+author-time 1112911993
+author-tz -0700
+committer C O Mitter
+committer-mail <committer@example.com>
+committer-time 1112911993
+committer-tz -0700
+summary one
+boundary
+filename file'
+ID2=8825379dfb8a1267b58e8e5bcf69eec838f685ec
+COMMIT2='author A U Thor
+author-mail <author@example.com>
+author-time 1112912053
+author-tz -0700
+committer C O Mitter
+committer-mail <committer@example.com>
+committer-time 1112912053
+committer-tz -0700
+summary two
+previous baf5e0b3869e0b2b2beb395a3720c7b51eac94fc file
+filename file'
+
+cat >expect <<EOF
+$ID1 1 1 1
+$COMMIT1
+	a
+$ID2 2 2 3
+$COMMIT2
+	b
+$ID2 3 3
+	c
+$ID2 4 4
+	d
+EOF
+test_expect_success 'blame --porcelain output' '
+	git blame --porcelain file >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<EOF
+$ID1 1 1 1
+$COMMIT1
+	a
+$ID2 2 2 3
+$COMMIT2
+	b
+$ID2 3 3
+$COMMIT2
+	c
+$ID2 4 4
+$COMMIT2
+	d
+EOF
+test_expect_success 'blame --line-porcelain output' '
+	git blame --line-porcelain file >actual &&
+	test_cmp expect actual
+'
+
+test_done
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index fb606a9..8c12c65 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -3,20 +3,17 @@
 test_description='git send-email'
 . ./test-lib.sh
 
-if ! test_have_prereq PERL; then
-	say 'skipping git send-email tests, perl not available'
-	test_done
-fi
+# May be altered later in the test
+PREREQ="PERL"
 
-PROG='git send-email'
-test_expect_success \
+test_expect_success $PREREQ \
     'prepare reference tree' \
     'echo "1A quick brown fox jumps over the" >file &&
      echo "lazy dog" >>file &&
      git add file &&
      GIT_AUTHOR_NAME="A" git commit -a -m "Initial."'
 
-test_expect_success \
+test_expect_success $PREREQ \
     'Setup helper tool' \
     '(echo "#!$SHELL_PATH"
       echo shift
@@ -26,6 +23,7 @@
       echo do
       echo "  echo \"!\$a!\""
       echo "done >commandline\$output"
+      test_have_prereq MINGW && echo "dos2unix commandline\$output"
       echo "cat > msgtxt\$output"
       ) >fake.sendmail &&
      chmod +x ./fake.sendmail &&
@@ -36,7 +34,7 @@
 	rm -f commandline* msgtxt*
 }
 
-test_expect_success 'Extract patches' '
+test_expect_success $PREREQ 'Extract patches' '
     patches=`git format-patch -s --cc="One <one@example.com>" --cc=two@example.com -n HEAD^1`
 '
 
@@ -57,44 +55,91 @@
 
 # Exit immediately to prevent hang if a no-confirm test fails
 check_no_confirm () {
-	test -f no_confirm_okay || {
-		say 'No confirm test failed; skipping remaining tests to prevent hanging'
-		test_done
-	}
+	if ! test -f no_confirm_okay
+	then
+		say 'confirm test failed; skipping remaining tests to prevent hanging'
+		PREREQ="$PREREQ,CHECK_NO_CONFIRM"
+	fi
+	return 0
 }
 
-test_expect_success 'No confirm with --suppress-cc' '
-	test_no_confirm --suppress-cc=sob
+test_expect_success $PREREQ 'No confirm with --suppress-cc' '
+	test_no_confirm --suppress-cc=sob &&
+	check_no_confirm
 '
-check_no_confirm
 
-test_expect_success 'No confirm with --confirm=never' '
-	test_no_confirm --confirm=never
+
+test_expect_success $PREREQ 'No confirm with --confirm=never' '
+	test_no_confirm --confirm=never &&
+	check_no_confirm
 '
-check_no_confirm
 
 # leave sendemail.confirm set to never after this so that none of the
 # remaining tests prompt unintentionally.
-test_expect_success 'No confirm with sendemail.confirm=never' '
+test_expect_success $PREREQ 'No confirm with sendemail.confirm=never' '
 	git config sendemail.confirm never &&
-	test_no_confirm --compose --subject=foo
+	test_no_confirm --compose --subject=foo &&
+	check_no_confirm
 '
-check_no_confirm
 
-test_expect_success 'Send patches' '
+test_expect_success $PREREQ 'Send patches' '
      git send-email --suppress-cc=sob --from="Example <nobody@example.com>" --to=nobody@example.com --smtp-server="$(pwd)/fake.sendmail" $patches 2>errors
 '
 
+test_expect_success $PREREQ 'setup expect' '
 cat >expected <<\EOF
 !nobody@example.com!
 !author@example.com!
 !one@example.com!
 !two@example.com!
 EOF
-test_expect_success \
+'
+
+test_expect_success $PREREQ \
     'Verify commandline' \
     'test_cmp expected commandline1'
 
+test_expect_success $PREREQ 'Send patches with --envelope-sender' '
+    clean_fake_sendmail &&
+     git send-email --envelope-sender="Patch Contributer <patch@example.com>" --suppress-cc=sob --from="Example <nobody@example.com>" --to=nobody@example.com --smtp-server="$(pwd)/fake.sendmail" $patches 2>errors
+'
+
+test_expect_success $PREREQ 'setup expect' '
+cat >expected <<\EOF
+!patch@example.com!
+!-i!
+!nobody@example.com!
+!author@example.com!
+!one@example.com!
+!two@example.com!
+EOF
+'
+
+test_expect_success $PREREQ \
+    'Verify commandline' \
+    'test_cmp expected commandline1'
+
+test_expect_success $PREREQ 'Send patches with --envelope-sender=auto' '
+    clean_fake_sendmail &&
+     git send-email --envelope-sender=auto --suppress-cc=sob --from="Example <nobody@example.com>" --to=nobody@example.com --smtp-server="$(pwd)/fake.sendmail" $patches 2>errors
+'
+
+test_expect_success $PREREQ 'setup expect' '
+cat >expected <<\EOF
+!nobody@example.com!
+!-i!
+!nobody@example.com!
+!author@example.com!
+!one@example.com!
+!two@example.com!
+EOF
+'
+
+test_expect_success $PREREQ \
+    'Verify commandline' \
+    'test_cmp expected commandline1'
+
+test_expect_success $PREREQ 'setup expect' "
 cat >expected-show-all-headers <<\EOF
 0001-Second.patch
 (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
@@ -103,10 +148,18 @@
 Dry-OK. Log says:
 Server: relay.example.com
 MAIL FROM:<from@example.com>
-RCPT TO:<to@example.com>,<cc@example.com>,<author@example.com>,<one@example.com>,<two@example.com>,<bcc@example.com>
+RCPT TO:<to@example.com>
+RCPT TO:<cc@example.com>
+RCPT TO:<author@example.com>
+RCPT TO:<one@example.com>
+RCPT TO:<two@example.com>
+RCPT TO:<bcc@example.com>
 From: Example <from@example.com>
 To: to@example.com
-Cc: cc@example.com, A <author@example.com>, One <one@example.com>, two@example.com
+Cc: cc@example.com,
+	A <author@example.com>,
+	One <one@example.com>,
+	two@example.com
 Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
 Message-Id: MESSAGE-ID-STRING
@@ -116,8 +169,9 @@
 
 Result: OK
 EOF
+"
 
-test_expect_success 'Show all headers' '
+test_expect_success $PREREQ 'Show all headers' '
 	git send-email \
 		--dry-run \
 		--suppress-cc=sob \
@@ -135,7 +189,7 @@
 	test_cmp expected-show-all-headers actual-show-all-headers
 '
 
-test_expect_success 'Prompting works' '
+test_expect_success $PREREQ 'Prompting works' '
 	clean_fake_sendmail &&
 	(echo "Example <from@example.com>"
 	 echo "to@example.com"
@@ -144,14 +198,32 @@
 		--smtp-server="$(pwd)/fake.sendmail" \
 		$patches \
 		2>errors &&
-		grep "^From: Example <from@example.com>$" msgtxt1 &&
-		grep "^To: to@example.com$" msgtxt1
+		grep "^From: Example <from@example.com>\$" msgtxt1 &&
+		grep "^To: to@example.com\$" msgtxt1
 '
 
-test_expect_success 'cccmd works' '
+test_expect_success $PREREQ 'tocmd works' '
+	clean_fake_sendmail &&
+	cp $patches tocmd.patch &&
+	echo tocmd--tocmd@example.com >>tocmd.patch &&
+	{
+	  echo "#!$SHELL_PATH"
+	  echo sed -n -e s/^tocmd--//p \"\$1\"
+	} > tocmd-sed &&
+	chmod +x tocmd-sed &&
+	git send-email \
+		--from="Example <nobody@example.com>" \
+		--to-cmd=./tocmd-sed \
+		--smtp-server="$(pwd)/fake.sendmail" \
+		tocmd.patch \
+		&&
+	grep "^To: tocmd@example.com" msgtxt1
+'
+
+test_expect_success $PREREQ 'cccmd works' '
 	clean_fake_sendmail &&
 	cp $patches cccmd.patch &&
-	echo cccmd--cccmd@example.com >>cccmd.patch &&
+	echo "cccmd--  cccmd@example.com" >>cccmd.patch &&
 	{
 	  echo "#!$SHELL_PATH"
 	  echo sed -n -e s/^cccmd--//p \"\$1\"
@@ -164,13 +236,13 @@
 		--smtp-server="$(pwd)/fake.sendmail" \
 		cccmd.patch \
 		&&
-	grep ^Cc:.*cccmd@example.com msgtxt1
+	grep "^	cccmd@example.com" msgtxt1
 '
 
-z8=zzzzzzzz
-z64=$z8$z8$z8$z8$z8$z8$z8$z8
-z512=$z64$z64$z64$z64$z64$z64$z64$z64
-test_expect_success 'reject long lines' '
+test_expect_success $PREREQ 'reject long lines' '
+	z8=zzzzzzzz &&
+	z64=$z8$z8$z8$z8$z8$z8$z8$z8 &&
+	z512=$z64$z64$z64$z64$z64$z64$z64$z64 &&
 	clean_fake_sendmail &&
 	cp $patches longline.patch &&
 	echo $z512$z512 >>longline.patch &&
@@ -183,33 +255,33 @@
 	grep longline.patch errors
 '
 
-test_expect_success 'no patch was sent' '
+test_expect_success $PREREQ 'no patch was sent' '
 	! test -e commandline1
 '
 
-test_expect_success 'Author From: in message body' '
+test_expect_success $PREREQ 'Author From: in message body' '
 	clean_fake_sendmail &&
 	git send-email \
 		--from="Example <nobody@example.com>" \
 		--to=nobody@example.com \
 		--smtp-server="$(pwd)/fake.sendmail" \
 		$patches &&
-	sed "1,/^$/d" < msgtxt1 > msgbody1
+	sed "1,/^\$/d" < msgtxt1 > msgbody1 &&
 	grep "From: A <author@example.com>" msgbody1
 '
 
-test_expect_success 'Author From: not in message body' '
+test_expect_success $PREREQ 'Author From: not in message body' '
 	clean_fake_sendmail &&
 	git send-email \
 		--from="A <author@example.com>" \
 		--to=nobody@example.com \
 		--smtp-server="$(pwd)/fake.sendmail" \
 		$patches &&
-	sed "1,/^$/d" < msgtxt1 > msgbody1
+	sed "1,/^\$/d" < msgtxt1 > msgbody1 &&
 	! grep "From: A <author@example.com>" msgbody1
 '
 
-test_expect_success 'allow long lines with --no-validate' '
+test_expect_success $PREREQ 'allow long lines with --no-validate' '
 	git send-email \
 		--from="Example <nobody@example.com>" \
 		--to=nobody@example.com \
@@ -219,19 +291,19 @@
 		2>errors
 '
 
-test_expect_success 'Invalid In-Reply-To' '
+test_expect_success $PREREQ 'Invalid In-Reply-To' '
 	clean_fake_sendmail &&
 	git send-email \
 		--from="Example <nobody@example.com>" \
 		--to=nobody@example.com \
 		--in-reply-to=" " \
 		--smtp-server="$(pwd)/fake.sendmail" \
-		$patches
-		2>errors
+		$patches \
+		2>errors &&
 	! grep "^In-Reply-To: < *>" msgtxt1
 '
 
-test_expect_success 'Valid In-Reply-To when prompting' '
+test_expect_success $PREREQ 'Valid In-Reply-To when prompting' '
 	clean_fake_sendmail &&
 	(echo "From Example <from@example.com>"
 	 echo "To Example <to@example.com>"
@@ -242,7 +314,50 @@
 	! grep "^In-Reply-To: < *>" msgtxt1
 '
 
-test_expect_success 'setup fake editor' '
+test_expect_success $PREREQ 'In-Reply-To without --chain-reply-to' '
+	clean_fake_sendmail &&
+	echo "<unique-message-id@example.com>" >expect &&
+	git send-email \
+		--from="Example <nobody@example.com>" \
+		--to=nobody@example.com \
+		--nochain-reply-to \
+		--in-reply-to="$(cat expect)" \
+		--smtp-server="$(pwd)/fake.sendmail" \
+		$patches $patches $patches \
+		2>errors &&
+	# The first message is a reply to --in-reply-to
+	sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt1 >actual &&
+	test_cmp expect actual &&
+	# Second and subsequent messages are replies to the first one
+	sed -n -e "s/^Message-Id: *\(.*\)/\1/p" msgtxt1 >expect &&
+	sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt2 >actual &&
+	test_cmp expect actual &&
+	sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt3 >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success $PREREQ 'In-Reply-To with --chain-reply-to' '
+	clean_fake_sendmail &&
+	echo "<unique-message-id@example.com>" >expect &&
+	git send-email \
+		--from="Example <nobody@example.com>" \
+		--to=nobody@example.com \
+		--chain-reply-to \
+		--in-reply-to="$(cat expect)" \
+		--smtp-server="$(pwd)/fake.sendmail" \
+		$patches $patches $patches \
+		2>errors &&
+	sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt1 >actual &&
+	test_cmp expect actual &&
+	sed -n -e "s/^Message-Id: *\(.*\)/\1/p" msgtxt1 >expect &&
+	sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt2 >actual &&
+	test_cmp expect actual &&
+	sed -n -e "s/^Message-Id: *\(.*\)/\1/p" msgtxt2 >expect &&
+	sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt3 >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success $PREREQ 'setup fake editor' '
 	(echo "#!$SHELL_PATH" &&
 	 echo "echo fake edit >>\"\$1\""
 	) >fake-editor &&
@@ -251,7 +366,7 @@
 
 test_set_editor "$(pwd)/fake-editor"
 
-test_expect_success '--compose works' '
+test_expect_success $PREREQ '--compose works' '
 	clean_fake_sendmail &&
 	git send-email \
 	--compose --subject foo \
@@ -262,14 +377,15 @@
 	2>errors
 '
 
-test_expect_success 'first message is compose text' '
+test_expect_success $PREREQ 'first message is compose text' '
 	grep "^fake edit" msgtxt1
 '
 
-test_expect_success 'second message is patch' '
+test_expect_success $PREREQ 'second message is patch' '
 	grep "Subject:.*Second" msgtxt2
 '
 
+test_expect_success $PREREQ 'setup expect' "
 cat >expected-suppress-sob <<\EOF
 0001-Second.patch
 (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
@@ -278,10 +394,17 @@
 Dry-OK. Log says:
 Server: relay.example.com
 MAIL FROM:<from@example.com>
-RCPT TO:<to@example.com>,<cc@example.com>,<author@example.com>,<one@example.com>,<two@example.com>
+RCPT TO:<to@example.com>
+RCPT TO:<cc@example.com>
+RCPT TO:<author@example.com>
+RCPT TO:<one@example.com>
+RCPT TO:<two@example.com>
 From: Example <from@example.com>
 To: to@example.com
-Cc: cc@example.com, A <author@example.com>, One <one@example.com>, two@example.com
+Cc: cc@example.com,
+	A <author@example.com>,
+	One <one@example.com>,
+	two@example.com
 Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
 Message-Id: MESSAGE-ID-STRING
@@ -289,6 +412,7 @@
 
 Result: OK
 EOF
+"
 
 test_suppression () {
 	git send-email \
@@ -305,11 +429,12 @@
 	test_cmp expected-suppress-$1${2+"-$2"} actual-suppress-$1${2+"-$2"}
 }
 
-test_expect_success 'sendemail.cc set' '
+test_expect_success $PREREQ 'sendemail.cc set' '
 	git config sendemail.cc cc@example.com &&
 	test_suppression sob
 '
 
+test_expect_success $PREREQ 'setup expect' "
 cat >expected-suppress-sob <<\EOF
 0001-Second.patch
 (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
@@ -318,10 +443,15 @@
 Dry-OK. Log says:
 Server: relay.example.com
 MAIL FROM:<from@example.com>
-RCPT TO:<to@example.com>,<author@example.com>,<one@example.com>,<two@example.com>
+RCPT TO:<to@example.com>
+RCPT TO:<author@example.com>
+RCPT TO:<one@example.com>
+RCPT TO:<two@example.com>
 From: Example <from@example.com>
 To: to@example.com
-Cc: A <author@example.com>, One <one@example.com>, two@example.com
+Cc: A <author@example.com>,
+	One <one@example.com>,
+	two@example.com
 Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
 Message-Id: MESSAGE-ID-STRING
@@ -329,12 +459,14 @@
 
 Result: OK
 EOF
+"
 
-test_expect_success 'sendemail.cc unset' '
+test_expect_success $PREREQ 'sendemail.cc unset' '
 	git config --unset sendemail.cc &&
 	test_suppression sob
 '
 
+test_expect_success $PREREQ 'setup expect' "
 cat >expected-suppress-cccmd <<\EOF
 0001-Second.patch
 (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
@@ -344,10 +476,17 @@
 Dry-OK. Log says:
 Server: relay.example.com
 MAIL FROM:<from@example.com>
-RCPT TO:<to@example.com>,<author@example.com>,<one@example.com>,<two@example.com>,<committer@example.com>
+RCPT TO:<to@example.com>
+RCPT TO:<author@example.com>
+RCPT TO:<one@example.com>
+RCPT TO:<two@example.com>
+RCPT TO:<committer@example.com>
 From: Example <from@example.com>
 To: to@example.com
-Cc: A <author@example.com>, One <one@example.com>, two@example.com, C O Mitter <committer@example.com>
+Cc: A <author@example.com>,
+	One <one@example.com>,
+	two@example.com,
+	C O Mitter <committer@example.com>
 Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
 Message-Id: MESSAGE-ID-STRING
@@ -355,14 +494,16 @@
 
 Result: OK
 EOF
+"
 
-test_expect_success 'sendemail.cccmd' '
+test_expect_success $PREREQ 'sendemail.cccmd' '
 	echo echo cc-cmd@example.com > cccmd &&
 	chmod +x cccmd &&
 	git config sendemail.cccmd ./cccmd &&
 	test_suppression cccmd
 '
 
+test_expect_success $PREREQ 'setup expect' '
 cat >expected-suppress-all <<\EOF
 0001-Second.patch
 Dry-OK. Log says:
@@ -378,11 +519,13 @@
 
 Result: OK
 EOF
+'
 
-test_expect_success '--suppress-cc=all' '
+test_expect_success $PREREQ '--suppress-cc=all' '
 	test_suppression all
 '
 
+test_expect_success $PREREQ 'setup expect' "
 cat >expected-suppress-body <<\EOF
 0001-Second.patch
 (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
@@ -392,10 +535,17 @@
 Dry-OK. Log says:
 Server: relay.example.com
 MAIL FROM:<from@example.com>
-RCPT TO:<to@example.com>,<author@example.com>,<one@example.com>,<two@example.com>,<cc-cmd@example.com>
+RCPT TO:<to@example.com>
+RCPT TO:<author@example.com>
+RCPT TO:<one@example.com>
+RCPT TO:<two@example.com>
+RCPT TO:<cc-cmd@example.com>
 From: Example <from@example.com>
 To: to@example.com
-Cc: A <author@example.com>, One <one@example.com>, two@example.com, cc-cmd@example.com
+Cc: A <author@example.com>,
+	One <one@example.com>,
+	two@example.com,
+	cc-cmd@example.com
 Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
 Message-Id: MESSAGE-ID-STRING
@@ -403,11 +553,13 @@
 
 Result: OK
 EOF
+"
 
-test_expect_success '--suppress-cc=body' '
+test_expect_success $PREREQ '--suppress-cc=body' '
 	test_suppression body
 '
 
+test_expect_success $PREREQ 'setup expect' "
 cat >expected-suppress-body-cccmd <<\EOF
 0001-Second.patch
 (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
@@ -416,10 +568,15 @@
 Dry-OK. Log says:
 Server: relay.example.com
 MAIL FROM:<from@example.com>
-RCPT TO:<to@example.com>,<author@example.com>,<one@example.com>,<two@example.com>
+RCPT TO:<to@example.com>
+RCPT TO:<author@example.com>
+RCPT TO:<one@example.com>
+RCPT TO:<two@example.com>
 From: Example <from@example.com>
 To: to@example.com
-Cc: A <author@example.com>, One <one@example.com>, two@example.com
+Cc: A <author@example.com>,
+	One <one@example.com>,
+	two@example.com
 Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
 Message-Id: MESSAGE-ID-STRING
@@ -427,11 +584,13 @@
 
 Result: OK
 EOF
+"
 
-test_expect_success '--suppress-cc=body --suppress-cc=cccmd' '
+test_expect_success $PREREQ '--suppress-cc=body --suppress-cc=cccmd' '
 	test_suppression body cccmd
 '
 
+test_expect_success $PREREQ 'setup expect' "
 cat >expected-suppress-sob <<\EOF
 0001-Second.patch
 (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
@@ -440,10 +599,15 @@
 Dry-OK. Log says:
 Server: relay.example.com
 MAIL FROM:<from@example.com>
-RCPT TO:<to@example.com>,<author@example.com>,<one@example.com>,<two@example.com>
+RCPT TO:<to@example.com>
+RCPT TO:<author@example.com>
+RCPT TO:<one@example.com>
+RCPT TO:<two@example.com>
 From: Example <from@example.com>
 To: to@example.com
-Cc: A <author@example.com>, One <one@example.com>, two@example.com
+Cc: A <author@example.com>,
+	One <one@example.com>,
+	two@example.com
 Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
 Message-Id: MESSAGE-ID-STRING
@@ -451,12 +615,14 @@
 
 Result: OK
 EOF
+"
 
-test_expect_success '--suppress-cc=sob' '
-	git config --unset sendemail.cccmd
+test_expect_success $PREREQ '--suppress-cc=sob' '
+	test_might_fail git config --unset sendemail.cccmd &&
 	test_suppression sob
 '
 
+test_expect_success $PREREQ 'setup expect' "
 cat >expected-suppress-bodycc <<\EOF
 0001-Second.patch
 (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
@@ -466,10 +632,17 @@
 Dry-OK. Log says:
 Server: relay.example.com
 MAIL FROM:<from@example.com>
-RCPT TO:<to@example.com>,<author@example.com>,<one@example.com>,<two@example.com>,<committer@example.com>
+RCPT TO:<to@example.com>
+RCPT TO:<author@example.com>
+RCPT TO:<one@example.com>
+RCPT TO:<two@example.com>
+RCPT TO:<committer@example.com>
 From: Example <from@example.com>
 To: to@example.com
-Cc: A <author@example.com>, One <one@example.com>, two@example.com, C O Mitter <committer@example.com>
+Cc: A <author@example.com>,
+	One <one@example.com>,
+	two@example.com,
+	C O Mitter <committer@example.com>
 Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
 Message-Id: MESSAGE-ID-STRING
@@ -477,11 +650,13 @@
 
 Result: OK
 EOF
+"
 
-test_expect_success '--suppress-cc=bodycc' '
+test_expect_success $PREREQ '--suppress-cc=bodycc' '
 	test_suppression bodycc
 '
 
+test_expect_success $PREREQ 'setup expect' "
 cat >expected-suppress-cc <<\EOF
 0001-Second.patch
 (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
@@ -489,10 +664,13 @@
 Dry-OK. Log says:
 Server: relay.example.com
 MAIL FROM:<from@example.com>
-RCPT TO:<to@example.com>,<author@example.com>,<committer@example.com>
+RCPT TO:<to@example.com>
+RCPT TO:<author@example.com>
+RCPT TO:<committer@example.com>
 From: Example <from@example.com>
 To: to@example.com
-Cc: A <author@example.com>, C O Mitter <committer@example.com>
+Cc: A <author@example.com>,
+	C O Mitter <committer@example.com>
 Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
 Message-Id: MESSAGE-ID-STRING
@@ -500,8 +678,9 @@
 
 Result: OK
 EOF
+"
 
-test_expect_success '--suppress-cc=cc' '
+test_expect_success $PREREQ '--suppress-cc=cc' '
 	test_suppression cc
 '
 
@@ -516,23 +695,23 @@
 	grep "Send this email" stdout
 }
 
-test_expect_success '--confirm=always' '
+test_expect_success $PREREQ '--confirm=always' '
 	test_confirm --confirm=always --suppress-cc=all
 '
 
-test_expect_success '--confirm=auto' '
+test_expect_success $PREREQ '--confirm=auto' '
 	test_confirm --confirm=auto
 '
 
-test_expect_success '--confirm=cc' '
+test_expect_success $PREREQ '--confirm=cc' '
 	test_confirm --confirm=cc
 '
 
-test_expect_success '--confirm=compose' '
+test_expect_success $PREREQ '--confirm=compose' '
 	test_confirm --confirm=compose --compose
 '
 
-test_expect_success 'confirm by default (due to cc)' '
+test_expect_success $PREREQ 'confirm by default (due to cc)' '
 	CONFIRM=$(git config --get sendemail.confirm) &&
 	git config --unset sendemail.confirm &&
 	test_confirm
@@ -541,7 +720,7 @@
 	test $ret = "0"
 '
 
-test_expect_success 'confirm by default (due to --compose)' '
+test_expect_success $PREREQ 'confirm by default (due to --compose)' '
 	CONFIRM=$(git config --get sendemail.confirm) &&
 	git config --unset sendemail.confirm &&
 	test_confirm --suppress-cc=all --compose
@@ -550,7 +729,7 @@
 	test $ret = "0"
 '
 
-test_expect_success 'confirm detects EOF (inform assumes y)' '
+test_expect_success $PREREQ 'confirm detects EOF (inform assumes y)' '
 	CONFIRM=$(git config --get sendemail.confirm) &&
 	git config --unset sendemail.confirm &&
 	rm -fr outdir &&
@@ -566,7 +745,7 @@
 	test $ret = "0"
 '
 
-test_expect_success 'confirm detects EOF (auto causes failure)' '
+test_expect_success $PREREQ 'confirm detects EOF (auto causes failure)' '
 	CONFIRM=$(git config --get sendemail.confirm) &&
 	git config sendemail.confirm auto &&
 	GIT_SEND_EMAIL_NOTTY=1 &&
@@ -581,7 +760,7 @@
 	test $ret = "0"
 '
 
-test_expect_success 'confirm doesnt loop forever' '
+test_expect_success $PREREQ 'confirm doesnt loop forever' '
 	CONFIRM=$(git config --get sendemail.confirm) &&
 	git config sendemail.confirm auto &&
 	GIT_SEND_EMAIL_NOTTY=1 &&
@@ -596,7 +775,7 @@
 	test $ret = "0"
 '
 
-test_expect_success 'utf8 Cc is rfc2047 encoded' '
+test_expect_success $PREREQ 'utf8 Cc is rfc2047 encoded' '
 	clean_fake_sendmail &&
 	rm -fr outdir &&
 	git format-patch -1 -o outdir --cc="àéìöú <utf8@example.com>" &&
@@ -605,11 +784,11 @@
 	--to=nobody@example.com \
 	--smtp-server="$(pwd)/fake.sendmail" \
 	outdir/*.patch &&
-	grep "^Cc:" msgtxt1 |
+	grep "^	" msgtxt1 |
 	grep "=?UTF-8?q?=C3=A0=C3=A9=C3=AC=C3=B6=C3=BA?= <utf8@example.com>"
 '
 
-test_expect_success '--compose adds MIME for utf8 body' '
+test_expect_success $PREREQ '--compose adds MIME for utf8 body' '
 	clean_fake_sendmail &&
 	(echo "#!$SHELL_PATH" &&
 	 echo "echo utf8 body: àéìöú >>\"\$1\""
@@ -626,7 +805,7 @@
 	grep "^Content-Type: text/plain; charset=UTF-8" msgtxt1
 '
 
-test_expect_success '--compose respects user mime type' '
+test_expect_success $PREREQ '--compose respects user mime type' '
 	clean_fake_sendmail &&
 	(echo "#!$SHELL_PATH" &&
 	 echo "(echo MIME-Version: 1.0"
@@ -649,7 +828,7 @@
 	! grep "^Content-Type: text/plain; charset=UTF-8" msgtxt1
 '
 
-test_expect_success '--compose adds MIME for utf8 subject' '
+test_expect_success $PREREQ '--compose adds MIME for utf8 subject' '
 	clean_fake_sendmail &&
 	  GIT_EDITOR="\"$(pwd)/fake-editor\"" \
 	  git send-email \
@@ -662,7 +841,7 @@
 	grep "^Subject: =?UTF-8?q?utf8-s=C3=BCbj=C3=ABct?=" msgtxt1
 '
 
-test_expect_success 'detects ambiguous reference/file conflict' '
+test_expect_success $PREREQ 'detects ambiguous reference/file conflict' '
 	echo master > master &&
 	git add master &&
 	git commit -m"add master" &&
@@ -670,7 +849,7 @@
 	grep disambiguate errors
 '
 
-test_expect_success 'feed two files' '
+test_expect_success $PREREQ 'feed two files' '
 	rm -fr outdir &&
 	git format-patch -2 -o outdir &&
 	git send-email \
@@ -683,7 +862,7 @@
 	test "z$(sed -n -e 2p subjects)" = "zSubject: [PATCH 2/2] add master"
 '
 
-test_expect_success 'in-reply-to but no threading' '
+test_expect_success $PREREQ 'in-reply-to but no threading' '
 	git send-email \
 		--dry-run \
 		--from="Example <nobody@example.com>" \
@@ -694,7 +873,7 @@
 	grep "In-Reply-To: <in-reply-id@example.com>"
 '
 
-test_expect_success 'no in-reply-to and no threading' '
+test_expect_success $PREREQ 'no in-reply-to and no threading' '
 	git send-email \
 		--dry-run \
 		--from="Example <nobody@example.com>" \
@@ -704,7 +883,7 @@
 	! grep "In-Reply-To: " stdout
 '
 
-test_expect_success 'threading but no chain-reply-to' '
+test_expect_success $PREREQ 'threading but no chain-reply-to' '
 	git send-email \
 		--dry-run \
 		--from="Example <nobody@example.com>" \
@@ -715,4 +894,307 @@
 	grep "In-Reply-To: " stdout
 '
 
+test_expect_success $PREREQ 'warning with an implicit --chain-reply-to' '
+	git send-email \
+	--dry-run \
+	--from="Example <nobody@example.com>" \
+	--to=nobody@example.com \
+	outdir/000?-*.patch 2>errors >out &&
+	grep "no-chain-reply-to" errors
+'
+
+test_expect_success $PREREQ 'no warning with an explicit --chain-reply-to' '
+	git send-email \
+	--dry-run \
+	--from="Example <nobody@example.com>" \
+	--to=nobody@example.com \
+	--chain-reply-to \
+	outdir/000?-*.patch 2>errors >out &&
+	! grep "no-chain-reply-to" errors
+'
+
+test_expect_success $PREREQ 'no warning with an explicit --no-chain-reply-to' '
+	git send-email \
+	--dry-run \
+	--from="Example <nobody@example.com>" \
+	--to=nobody@example.com \
+	--nochain-reply-to \
+	outdir/000?-*.patch 2>errors >out &&
+	! grep "no-chain-reply-to" errors
+'
+
+test_expect_success $PREREQ 'no warning with sendemail.chainreplyto = false' '
+	git config sendemail.chainreplyto false &&
+	git send-email \
+	--dry-run \
+	--from="Example <nobody@example.com>" \
+	--to=nobody@example.com \
+	outdir/000?-*.patch 2>errors >out &&
+	! grep "no-chain-reply-to" errors
+'
+
+test_expect_success $PREREQ 'no warning with sendemail.chainreplyto = true' '
+	git config sendemail.chainreplyto true &&
+	git send-email \
+	--dry-run \
+	--from="Example <nobody@example.com>" \
+	--to=nobody@example.com \
+	outdir/000?-*.patch 2>errors >out &&
+	! grep "no-chain-reply-to" errors
+'
+
+test_expect_success $PREREQ 'sendemail.to works' '
+	git config --replace-all sendemail.to "Somebody <somebody@ex.com>" &&
+	git send-email \
+		--dry-run \
+		--from="Example <nobody@example.com>" \
+		$patches $patches >stdout &&
+	grep "To: Somebody <somebody@ex.com>" stdout
+'
+
+test_expect_success $PREREQ '--no-to overrides sendemail.to' '
+	git send-email \
+		--dry-run \
+		--from="Example <nobody@example.com>" \
+		--no-to \
+		--to=nobody@example.com \
+		$patches $patches >stdout &&
+	grep "To: nobody@example.com" stdout &&
+	! grep "To: Somebody <somebody@ex.com>" stdout
+'
+
+test_expect_success $PREREQ 'sendemail.cc works' '
+	git config --replace-all sendemail.cc "Somebody <somebody@ex.com>" &&
+	git send-email \
+		--dry-run \
+		--from="Example <nobody@example.com>" \
+		--to=nobody@example.com \
+		$patches $patches >stdout &&
+	grep "Cc: Somebody <somebody@ex.com>" stdout
+'
+
+test_expect_success $PREREQ '--no-cc overrides sendemail.cc' '
+	git send-email \
+		--dry-run \
+		--from="Example <nobody@example.com>" \
+		--no-cc \
+		--cc=bodies@example.com \
+		--to=nobody@example.com \
+		$patches $patches >stdout &&
+	grep "Cc: bodies@example.com" stdout &&
+	! grep "Cc: Somebody <somebody@ex.com>" stdout
+'
+
+test_expect_success $PREREQ 'sendemail.bcc works' '
+	git config --replace-all sendemail.bcc "Other <other@ex.com>" &&
+	git send-email \
+		--dry-run \
+		--from="Example <nobody@example.com>" \
+		--to=nobody@example.com \
+		--smtp-server relay.example.com \
+		$patches $patches >stdout &&
+	grep "RCPT TO:<other@ex.com>" stdout
+'
+
+test_expect_success $PREREQ '--no-bcc overrides sendemail.bcc' '
+	git send-email \
+		--dry-run \
+		--from="Example <nobody@example.com>" \
+		--no-bcc \
+		--bcc=bodies@example.com \
+		--to=nobody@example.com \
+		--smtp-server relay.example.com \
+		$patches $patches >stdout &&
+	grep "RCPT TO:<bodies@example.com>" stdout &&
+	! grep "RCPT TO:<other@ex.com>" stdout
+'
+
+test_expect_success $PREREQ 'patches To headers are used by default' '
+	patch=`git format-patch -1 --to="bodies@example.com"` &&
+	test_when_finished "rm $patch" &&
+	git send-email \
+		--dry-run \
+		--from="Example <nobody@example.com>" \
+		--smtp-server relay.example.com \
+		$patch >stdout &&
+	grep "RCPT TO:<bodies@example.com>" stdout
+'
+
+test_expect_success $PREREQ 'patches To headers are appended to' '
+	patch=`git format-patch -1 --to="bodies@example.com"` &&
+	test_when_finished "rm $patch" &&
+	git send-email \
+		--dry-run \
+		--from="Example <nobody@example.com>" \
+		--to=nobody@example.com \
+		--smtp-server relay.example.com \
+		$patch >stdout &&
+	grep "RCPT TO:<bodies@example.com>" stdout &&
+	grep "RCPT TO:<nobody@example.com>" stdout
+'
+
+test_expect_success $PREREQ 'To headers from files reset each patch' '
+	patch1=`git format-patch -1 --to="bodies@example.com"` &&
+	patch2=`git format-patch -1 --to="other@example.com" HEAD~` &&
+	test_when_finished "rm $patch1 && rm $patch2" &&
+	git send-email \
+		--dry-run \
+		--from="Example <nobody@example.com>" \
+		--to="nobody@example.com" \
+		--smtp-server relay.example.com \
+		$patch1 $patch2 >stdout &&
+	test $(grep -c "RCPT TO:<bodies@example.com>" stdout) = 1 &&
+	test $(grep -c "RCPT TO:<nobody@example.com>" stdout) = 2 &&
+	test $(grep -c "RCPT TO:<other@example.com>" stdout) = 1
+'
+
+test_expect_success $PREREQ 'setup expect' '
+cat >email-using-8bit <<EOF
+From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
+Message-Id: <bogus-message-id@example.com>
+From: author@example.com
+Date: Sat, 12 Jun 2010 15:53:58 +0200
+Subject: subject goes here
+
+Dieser deutsche Text enthält einen Umlaut!
+EOF
+'
+
+test_expect_success $PREREQ 'setup expect' '
+cat >content-type-decl <<EOF
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+EOF
+'
+
+test_expect_success $PREREQ 'asks about and fixes 8bit encodings' '
+	clean_fake_sendmail &&
+	echo |
+	git send-email --from=author@example.com --to=nobody@example.com \
+			--smtp-server="$(pwd)/fake.sendmail" \
+			email-using-8bit >stdout &&
+	grep "do not declare a Content-Transfer-Encoding" stdout &&
+	grep email-using-8bit stdout &&
+	grep "Which 8bit encoding" stdout &&
+	egrep "Content|MIME" msgtxt1 >actual &&
+	test_cmp actual content-type-decl
+'
+
+test_expect_success $PREREQ 'sendemail.8bitEncoding works' '
+	clean_fake_sendmail &&
+	git config sendemail.assume8bitEncoding UTF-8 &&
+	echo bogus |
+	git send-email --from=author@example.com --to=nobody@example.com \
+			--smtp-server="$(pwd)/fake.sendmail" \
+			email-using-8bit >stdout &&
+	egrep "Content|MIME" msgtxt1 >actual &&
+	test_cmp actual content-type-decl
+'
+
+test_expect_success $PREREQ '--8bit-encoding overrides sendemail.8bitEncoding' '
+	clean_fake_sendmail &&
+	git config sendemail.assume8bitEncoding "bogus too" &&
+	echo bogus |
+	git send-email --from=author@example.com --to=nobody@example.com \
+			--smtp-server="$(pwd)/fake.sendmail" \
+			--8bit-encoding=UTF-8 \
+			email-using-8bit >stdout &&
+	egrep "Content|MIME" msgtxt1 >actual &&
+	test_cmp actual content-type-decl
+'
+
+test_expect_success $PREREQ 'setup expect' '
+cat >email-using-8bit <<EOF
+From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
+Message-Id: <bogus-message-id@example.com>
+From: author@example.com
+Date: Sat, 12 Jun 2010 15:53:58 +0200
+Subject: Dieser Betreff enthält auch einen Umlaut!
+
+Nothing to see here.
+EOF
+'
+
+test_expect_success $PREREQ 'setup expect' '
+cat >expected <<EOF
+Subject: =?UTF-8?q?Dieser=20Betreff=20enth=C3=A4lt=20auch=20einen=20Umlaut!?=
+EOF
+'
+
+test_expect_success $PREREQ '--8bit-encoding also treats subject' '
+	clean_fake_sendmail &&
+	echo bogus |
+	git send-email --from=author@example.com --to=nobody@example.com \
+			--smtp-server="$(pwd)/fake.sendmail" \
+			--8bit-encoding=UTF-8 \
+			email-using-8bit >stdout &&
+	grep "Subject" msgtxt1 >actual &&
+	test_cmp expected actual
+'
+
+# Note that the patches in this test are deliberately out of order; we
+# want to make sure it works even if the cover-letter is not in the
+# first mail.
+test_expect_success $PREREQ 'refusing to send cover letter template' '
+	clean_fake_sendmail &&
+	rm -fr outdir &&
+	git format-patch --cover-letter -2 -o outdir &&
+	test_must_fail git send-email \
+	  --from="Example <nobody@example.com>" \
+	  --to=nobody@example.com \
+	  --smtp-server="$(pwd)/fake.sendmail" \
+	  outdir/0002-*.patch \
+	  outdir/0000-*.patch \
+	  outdir/0001-*.patch \
+	  2>errors >out &&
+	grep "SUBJECT HERE" errors &&
+	test -z "$(ls msgtxt*)"
+'
+
+test_expect_success $PREREQ '--force sends cover letter template anyway' '
+	clean_fake_sendmail &&
+	rm -fr outdir &&
+	git format-patch --cover-letter -2 -o outdir &&
+	git send-email \
+	  --force \
+	  --from="Example <nobody@example.com>" \
+	  --to=nobody@example.com \
+	  --smtp-server="$(pwd)/fake.sendmail" \
+	  outdir/0002-*.patch \
+	  outdir/0000-*.patch \
+	  outdir/0001-*.patch \
+	  2>errors >out &&
+	! grep "SUBJECT HERE" errors &&
+	test -n "$(ls msgtxt*)"
+'
+
+test_expect_success $PREREQ 'sendemail.aliasfiletype=mailrc' '
+	clean_fake_sendmail &&
+	echo "alias sbd  somebody@example.org" >.mailrc &&
+	git config --replace-all sendemail.aliasesfile "$(pwd)/.mailrc" &&
+	git config sendemail.aliasfiletype mailrc &&
+	git send-email \
+	  --from="Example <nobody@example.com>" \
+	  --to=sbd \
+	  --smtp-server="$(pwd)/fake.sendmail" \
+	  outdir/0001-*.patch \
+	  2>errors >out &&
+	grep "^!somebody@example\.org!$" commandline1
+'
+
+test_expect_success $PREREQ 'sendemail.aliasfile=~/.mailrc' '
+	clean_fake_sendmail &&
+	echo "alias sbd  someone@example.org" >~/.mailrc &&
+	git config --replace-all sendemail.aliasesfile "~/.mailrc" &&
+	git config sendemail.aliasfiletype mailrc &&
+	git send-email \
+	  --from="Example <nobody@example.com>" \
+	  --to=sbd \
+	  --smtp-server="$(pwd)/fake.sendmail" \
+	  outdir/0001-*.patch \
+	  2>errors >out &&
+	grep "^!someone@example\.org!$" commandline1
+'
+
 test_done
diff --git a/t/t9002-column.sh b/t/t9002-column.sh
new file mode 100755
index 0000000..8998352
--- /dev/null
+++ b/t/t9002-column.sh
@@ -0,0 +1,180 @@
+#!/bin/sh
+
+test_description='git column'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	cat >lista <<\EOF
+one
+two
+three
+four
+five
+six
+seven
+eight
+nine
+ten
+eleven
+EOF
+'
+
+test_expect_success 'never' '
+	git column --indent=Z --mode=never <lista >actual &&
+	test_cmp lista actual
+'
+
+test_expect_success 'always' '
+	cat >expected <<\EOF &&
+Zone
+Ztwo
+Zthree
+Zfour
+Zfive
+Zsix
+Zseven
+Zeight
+Znine
+Zten
+Zeleven
+EOF
+	git column --indent=Z --mode=plain <lista >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success '80 columns' '
+	cat >expected <<\EOF &&
+one    two    three  four   five   six    seven  eight  nine   ten    eleven
+EOF
+	COLUMNS=80 git column --mode=column <lista >actual &&
+	test_cmp expected actual
+'
+
+cat >expected <<\EOF
+one
+two
+three
+four
+five
+six
+seven
+eight
+nine
+ten
+eleven
+EOF
+
+test_expect_success COLUMNS_CAN_BE_1 'COLUMNS = 1' '
+	COLUMNS=1 git column --mode=column <lista >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'width = 1' '
+	git column --mode=column --width=1 <lista >actual &&
+	test_cmp expected actual
+'
+
+COLUMNS=20
+export COLUMNS
+
+test_expect_success '20 columns' '
+	cat >expected <<\EOF &&
+one    seven
+two    eight
+three  nine
+four   ten
+five   eleven
+six
+EOF
+	git column --mode=column <lista >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success '20 columns, nodense' '
+	cat >expected <<\EOF &&
+one    seven
+two    eight
+three  nine
+four   ten
+five   eleven
+six
+EOF
+	git column --mode=column,nodense < lista > actual &&
+	test_cmp expected actual
+'
+
+test_expect_success '20 columns, dense' '
+	cat >expected <<\EOF &&
+one   five  nine
+two   six   ten
+three seven eleven
+four  eight
+EOF
+	git column --mode=column,dense < lista > actual &&
+	test_cmp expected actual
+'
+
+test_expect_success '20 columns, padding 2' '
+	cat >expected <<\EOF &&
+one     seven
+two     eight
+three   nine
+four    ten
+five    eleven
+six
+EOF
+	git column --mode=column --padding 2 <lista >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success '20 columns, indented' '
+	cat >expected <<\EOF &&
+  one    seven
+  two    eight
+  three  nine
+  four   ten
+  five   eleven
+  six
+EOF
+	git column --mode=column --indent="  " <lista >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success '20 columns, row first' '
+	cat >expected <<\EOF &&
+one    two
+three  four
+five   six
+seven  eight
+nine   ten
+eleven
+EOF
+	git column --mode=row <lista >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success '20 columns, row first, nodense' '
+	cat >expected <<\EOF &&
+one    two
+three  four
+five   six
+seven  eight
+nine   ten
+eleven
+EOF
+	git column --mode=row,nodense <lista >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success '20 columns, row first, dense' '
+	cat >expected <<\EOF &&
+one   two    three
+four  five   six
+seven eight  nine
+ten   eleven
+EOF
+	git column --mode=row,dense <lista >actual &&
+	test_cmp expected actual
+'
+
+test_done
diff --git a/t/t9010-svn-fe.sh b/t/t9010-svn-fe.sh
new file mode 100755
index 0000000..b7eed24
--- /dev/null
+++ b/t/t9010-svn-fe.sh
@@ -0,0 +1,1114 @@
+#!/bin/sh
+
+test_description='check svn dumpfile importer'
+
+. ./test-lib.sh
+
+reinit_git () {
+	if ! test_declared_prereq PIPE
+	then
+		echo >&4 "reinit_git: need to declare PIPE prerequisite"
+		return 127
+	fi
+	rm -fr .git &&
+	rm -f stream backflow &&
+	git init &&
+	mkfifo stream backflow
+}
+
+try_dump () {
+	input=$1 &&
+	maybe_fail_svnfe=${2:+test_$2} &&
+	maybe_fail_fi=${3:+test_$3} &&
+
+	{
+		$maybe_fail_svnfe test-svn-fe "$input" >stream 3<backflow &
+	} &&
+	$maybe_fail_fi git fast-import --cat-blob-fd=3 <stream 3>backflow &&
+	wait $!
+}
+
+properties () {
+	while test "$#" -ne 0
+	do
+		property="$1" &&
+		value="$2" &&
+		printf "%s\n" "K ${#property}" &&
+		printf "%s\n" "$property" &&
+		printf "%s\n" "V ${#value}" &&
+		printf "%s\n" "$value" &&
+		shift 2 ||
+		return 1
+	done
+}
+
+text_no_props () {
+	text="$1
+" &&
+	printf "%s\n" "Prop-content-length: 10" &&
+	printf "%s\n" "Text-content-length: ${#text}" &&
+	printf "%s\n" "Content-length: $((${#text} + 10))" &&
+	printf "%s\n" "" "PROPS-END" &&
+	printf "%s\n" "$text"
+}
+
+>empty
+
+test_expect_success 'setup: have pipes?' '
+	rm -f frob &&
+	if mkfifo frob
+	then
+		test_set_prereq PIPE
+	fi
+'
+
+test_expect_success PIPE 'empty dump' '
+	reinit_git &&
+	echo "SVN-fs-dump-format-version: 2" >input &&
+	try_dump input
+'
+
+test_expect_success PIPE 'v4 dumps not supported' '
+	reinit_git &&
+	echo "SVN-fs-dump-format-version: 4" >v4.dump &&
+	try_dump v4.dump must_fail
+'
+
+test_expect_failure PIPE 'empty revision' '
+	reinit_git &&
+	printf "rev <nobody, nobody@local>: %s\n" "" "" >expect &&
+	cat >emptyrev.dump <<-\EOF &&
+	SVN-fs-dump-format-version: 3
+
+	Revision-number: 1
+	Prop-content-length: 0
+	Content-length: 0
+
+	Revision-number: 2
+	Prop-content-length: 0
+	Content-length: 0
+
+	EOF
+	try_dump emptyrev.dump &&
+	git log -p --format="rev <%an, %ae>: %s" HEAD >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success PIPE 'empty properties' '
+	reinit_git &&
+	printf "rev <nobody, nobody@local>: %s\n" "" "" >expect &&
+	cat >emptyprop.dump <<-\EOF &&
+	SVN-fs-dump-format-version: 3
+
+	Revision-number: 1
+	Prop-content-length: 10
+	Content-length: 10
+
+	PROPS-END
+
+	Revision-number: 2
+	Prop-content-length: 10
+	Content-length: 10
+
+	PROPS-END
+	EOF
+	try_dump emptyprop.dump &&
+	git log -p --format="rev <%an, %ae>: %s" HEAD >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success PIPE 'author name and commit message' '
+	reinit_git &&
+	echo "<author@example.com, author@example.com@local>" >expect.author &&
+	cat >message <<-\EOF &&
+	A concise summary of the change
+
+	A detailed description of the change, why it is needed, what
+	was broken and why applying this is the best course of action.
+
+	* file.c
+	    Details pertaining to an individual file.
+	EOF
+	{
+		properties \
+			svn:author author@example.com \
+			svn:log "$(cat message)" &&
+		echo PROPS-END
+	} >props &&
+	{
+		echo "SVN-fs-dump-format-version: 3" &&
+		echo &&
+		echo "Revision-number: 1" &&
+		echo Prop-content-length: $(wc -c <props) &&
+		echo Content-length: $(wc -c <props) &&
+		echo &&
+		cat props
+	} >log.dump &&
+	try_dump log.dump &&
+	git log -p --format="%B" HEAD >actual.log &&
+	git log --format="<%an, %ae>" >actual.author &&
+	test_cmp message actual.log &&
+	test_cmp expect.author actual.author
+'
+
+test_expect_success PIPE 'unsupported properties are ignored' '
+	reinit_git &&
+	echo author >expect &&
+	cat >extraprop.dump <<-\EOF &&
+	SVN-fs-dump-format-version: 3
+
+	Revision-number: 1
+	Prop-content-length: 56
+	Content-length: 56
+
+	K 8
+	nonsense
+	V 1
+	y
+	K 10
+	svn:author
+	V 6
+	author
+	PROPS-END
+	EOF
+	try_dump extraprop.dump &&
+	git log -p --format=%an HEAD >actual &&
+	test_cmp expect actual
+'
+
+test_expect_failure PIPE 'timestamp and empty file' '
+	echo author@example.com >expect.author &&
+	echo 1999-01-01 >expect.date &&
+	echo file >expect.files &&
+	reinit_git &&
+	{
+		properties \
+			svn:author author@example.com \
+			svn:date "1999-01-01T00:01:002.000000Z" \
+			svn:log "add empty file" &&
+		echo PROPS-END
+	} >props &&
+	{
+		cat <<-EOF &&
+		SVN-fs-dump-format-version: 3
+
+		Revision-number: 1
+		EOF
+		echo Prop-content-length: $(wc -c <props) &&
+		echo Content-length: $(wc -c <props) &&
+		echo &&
+		cat props &&
+		cat <<-\EOF
+
+		Node-path: empty-file
+		Node-kind: file
+		Node-action: add
+		Content-length: 0
+
+		EOF
+	} >emptyfile.dump &&
+	try_dump emptyfile.dump &&
+	git log --format=%an HEAD >actual.author &&
+	git log --date=short --format=%ad HEAD >actual.date &&
+	git ls-tree -r --name-only HEAD >actual.files &&
+	test_cmp expect.author actual.author &&
+	test_cmp expect.date actual.date &&
+	test_cmp expect.files actual.files &&
+	git checkout HEAD empty-file &&
+	test_cmp empty file
+'
+
+test_expect_success PIPE 'directory with files' '
+	reinit_git &&
+	printf "%s\n" directory/file1 directory/file2 >expect.files &&
+	echo hi >hi &&
+	echo hello >hello &&
+	{
+		properties \
+			svn:author author@example.com \
+			svn:date "1999-02-01T00:01:002.000000Z" \
+			svn:log "add directory with some files in it" &&
+		echo PROPS-END
+	} >props &&
+	{
+		cat <<-EOF &&
+		SVN-fs-dump-format-version: 3
+
+		Revision-number: 1
+		EOF
+		echo Prop-content-length: $(wc -c <props) &&
+		echo Content-length: $(wc -c <props) &&
+		echo &&
+		cat props &&
+		cat <<-\EOF &&
+
+		Node-path: directory
+		Node-kind: dir
+		Node-action: add
+		Prop-content-length: 10
+		Content-length: 10
+
+		PROPS-END
+
+		Node-path: directory/file1
+		Node-kind: file
+		Node-action: add
+		EOF
+		text_no_props hello &&
+		cat <<-\EOF &&
+		Node-path: directory/file2
+		Node-kind: file
+		Node-action: add
+		EOF
+		text_no_props hi
+	} >directory.dump &&
+	try_dump directory.dump &&
+
+	git ls-tree -r --name-only HEAD >actual.files &&
+	git checkout HEAD directory &&
+	test_cmp expect.files actual.files &&
+	test_cmp hello directory/file1 &&
+	test_cmp hi directory/file2
+'
+
+test_expect_success PIPE 'branch name with backslash' '
+	reinit_git &&
+	sort <<-\EOF >expect.branch-files &&
+	trunk/file1
+	trunk/file2
+	"branches/UpdateFOPto094\\/file1"
+	"branches/UpdateFOPto094\\/file2"
+	EOF
+
+	echo hi >hi &&
+	echo hello >hello &&
+	{
+		properties \
+			svn:author author@example.com \
+			svn:date "1999-02-02T00:01:02.000000Z" \
+			svn:log "add directory with some files in it" &&
+		echo PROPS-END
+	} >props.setup &&
+	{
+		properties \
+			svn:author brancher@example.com \
+			svn:date "2007-12-06T21:38:34.000000Z" \
+			svn:log "Updating fop to .94 and adjust fo-stylesheets" &&
+		echo PROPS-END
+	} >props.branch &&
+	{
+		cat <<-EOF &&
+		SVN-fs-dump-format-version: 3
+
+		Revision-number: 1
+		EOF
+		echo Prop-content-length: $(wc -c <props.setup) &&
+		echo Content-length: $(wc -c <props.setup) &&
+		echo &&
+		cat props.setup &&
+		cat <<-\EOF &&
+
+		Node-path: trunk
+		Node-kind: dir
+		Node-action: add
+		Prop-content-length: 10
+		Content-length: 10
+
+		PROPS-END
+
+		Node-path: branches
+		Node-kind: dir
+		Node-action: add
+		Prop-content-length: 10
+		Content-length: 10
+
+		PROPS-END
+
+		Node-path: trunk/file1
+		Node-kind: file
+		Node-action: add
+		EOF
+		text_no_props hello &&
+		cat <<-\EOF &&
+		Node-path: trunk/file2
+		Node-kind: file
+		Node-action: add
+		EOF
+		text_no_props hi &&
+		cat <<-\EOF &&
+
+		Revision-number: 2
+		EOF
+		echo Prop-content-length: $(wc -c <props.branch) &&
+		echo Content-length: $(wc -c <props.branch) &&
+		echo &&
+		cat props.branch &&
+		cat <<-\EOF
+
+		Node-path: branches/UpdateFOPto094\
+		Node-kind: dir
+		Node-action: add
+		Node-copyfrom-rev: 1
+		Node-copyfrom-path: trunk
+
+		Node-kind: dir
+		Node-action: add
+		Prop-content-length: 34
+		Content-length: 34
+
+		K 13
+		svn:mergeinfo
+		V 0
+
+		PROPS-END
+		EOF
+	} >branch.dump &&
+	try_dump branch.dump &&
+
+	git ls-tree -r --name-only HEAD |
+	sort >actual.branch-files &&
+	test_cmp expect.branch-files actual.branch-files
+'
+
+test_expect_success PIPE 'node without action' '
+	reinit_git &&
+	cat >inaction.dump <<-\EOF &&
+	SVN-fs-dump-format-version: 3
+
+	Revision-number: 1
+	Prop-content-length: 10
+	Content-length: 10
+
+	PROPS-END
+
+	Node-path: directory
+	Node-kind: dir
+	Prop-content-length: 10
+	Content-length: 10
+
+	PROPS-END
+	EOF
+	try_dump inaction.dump must_fail
+'
+
+test_expect_success PIPE 'action: add node without text' '
+	reinit_git &&
+	cat >textless.dump <<-\EOF &&
+	SVN-fs-dump-format-version: 3
+
+	Revision-number: 1
+	Prop-content-length: 10
+	Content-length: 10
+
+	PROPS-END
+
+	Node-path: textless
+	Node-kind: file
+	Node-action: add
+	Prop-content-length: 10
+	Content-length: 10
+
+	PROPS-END
+	EOF
+	try_dump textless.dump must_fail
+'
+
+test_expect_failure PIPE 'change file mode but keep old content' '
+	reinit_git &&
+	cat >expect <<-\EOF &&
+	OBJID
+	:120000 100644 OBJID OBJID T	greeting
+	OBJID
+	:100644 120000 OBJID OBJID T	greeting
+	OBJID
+	:000000 100644 OBJID OBJID A	greeting
+	EOF
+	echo "link hello" >expect.blob &&
+	echo hello >hello &&
+	cat >filemode.dump <<-\EOF &&
+	SVN-fs-dump-format-version: 3
+
+	Revision-number: 1
+	Prop-content-length: 10
+	Content-length: 10
+
+	PROPS-END
+
+	Node-path: greeting
+	Node-kind: file
+	Node-action: add
+	Prop-content-length: 10
+	Text-content-length: 11
+	Content-length: 21
+
+	PROPS-END
+	link hello
+
+	Revision-number: 2
+	Prop-content-length: 10
+	Content-length: 10
+
+	PROPS-END
+
+	Node-path: greeting
+	Node-kind: file
+	Node-action: change
+	Prop-content-length: 33
+	Content-length: 33
+
+	K 11
+	svn:special
+	V 1
+	*
+	PROPS-END
+
+	Revision-number: 3
+	Prop-content-length: 10
+	Content-length: 10
+
+	PROPS-END
+
+	Node-path: greeting
+	Node-kind: file
+	Node-action: change
+	Prop-content-length: 10
+	Content-length: 10
+
+	PROPS-END
+	EOF
+	try_dump filemode.dump &&
+	{
+		git rev-list HEAD |
+		git diff-tree --root --stdin |
+		sed "s/$_x40/OBJID/g"
+	} >actual &&
+	git show HEAD:greeting >actual.blob &&
+	git show HEAD^:greeting >actual.target &&
+	test_cmp expect actual &&
+	test_cmp expect.blob actual.blob &&
+	test_cmp hello actual.target
+'
+
+test_expect_success PIPE 'NUL in property value' '
+	reinit_git &&
+	echo "commit message" >expect.message &&
+	{
+		properties \
+			unimportant "something with a NUL (Q)" \
+			svn:log "commit message"&&
+		echo PROPS-END
+	} |
+	q_to_nul >props &&
+	{
+		cat <<-\EOF &&
+		SVN-fs-dump-format-version: 3
+
+		Revision-number: 1
+		EOF
+		echo Prop-content-length: $(wc -c <props) &&
+		echo Content-length: $(wc -c <props) &&
+		echo &&
+		cat props
+	} >nulprop.dump &&
+	try_dump nulprop.dump &&
+	git diff-tree --always -s --format=%s HEAD >actual.message &&
+	test_cmp expect.message actual.message
+'
+
+test_expect_success PIPE 'NUL in log message, file content, and property name' '
+	# Caveat: svnadmin 1.6.16 (r1073529) truncates at \0 in the
+	# svn:specialQnotreally example.
+	reinit_git &&
+	cat >expect <<-\EOF &&
+	OBJID
+	:100644 100644 OBJID OBJID M	greeting
+	OBJID
+	:000000 100644 OBJID OBJID A	greeting
+	EOF
+	printf "\n%s\n" "something with an ASCII NUL (Q)" >expect.message &&
+	printf "%s\n" "helQo" >expect.hello1 &&
+	printf "%s\n" "link hello" >expect.hello2 &&
+	{
+		properties svn:log "something with an ASCII NUL (Q)" &&
+		echo PROPS-END
+	} |
+	q_to_nul >props &&
+	{
+		q_to_nul <<-\EOF &&
+		SVN-fs-dump-format-version: 3
+
+		Revision-number: 1
+		Prop-content-length: 10
+		Content-length: 10
+
+		PROPS-END
+
+		Node-path: greeting
+		Node-kind: file
+		Node-action: add
+		Prop-content-length: 10
+		Text-content-length: 6
+		Content-length: 16
+
+		PROPS-END
+		helQo
+
+		Revision-number: 2
+		EOF
+		echo Prop-content-length: $(wc -c <props) &&
+		echo Content-length: $(wc -c <props) &&
+		echo &&
+		cat props &&
+		q_to_nul <<-\EOF
+
+		Node-path: greeting
+		Node-kind: file
+		Node-action: change
+		Prop-content-length: 43
+		Text-content-length: 11
+		Content-length: 54
+
+		K 21
+		svn:specialQnotreally
+		V 1
+		*
+		PROPS-END
+		link hello
+		EOF
+	} >8bitclean.dump &&
+	try_dump 8bitclean.dump &&
+	{
+		git rev-list HEAD |
+		git diff-tree --root --stdin |
+		sed "s/$_x40/OBJID/g"
+	} >actual &&
+	{
+		git cat-file commit HEAD | nul_to_q &&
+		echo
+	} |
+	sed -ne "/^\$/,\$ p" >actual.message &&
+	git cat-file blob HEAD^:greeting | nul_to_q >actual.hello1 &&
+	git cat-file blob HEAD:greeting | nul_to_q >actual.hello2 &&
+	test_cmp expect actual &&
+	test_cmp expect.message actual.message &&
+	test_cmp expect.hello1 actual.hello1 &&
+	test_cmp expect.hello2 actual.hello2
+'
+
+test_expect_success PIPE 'change file mode and reiterate content' '
+	reinit_git &&
+	cat >expect <<-\EOF &&
+	OBJID
+	:120000 100644 OBJID OBJID T	greeting
+	OBJID
+	:100644 120000 OBJID OBJID T	greeting
+	OBJID
+	:000000 100644 OBJID OBJID A	greeting
+	EOF
+	echo "link hello" >expect.blob &&
+	echo hello >hello &&
+	cat >filemode2.dump <<-\EOF &&
+	SVN-fs-dump-format-version: 3
+
+	Revision-number: 1
+	Prop-content-length: 10
+	Content-length: 10
+
+	PROPS-END
+
+	Node-path: greeting
+	Node-kind: file
+	Node-action: add
+	Prop-content-length: 10
+	Text-content-length: 11
+	Content-length: 21
+
+	PROPS-END
+	link hello
+
+	Revision-number: 2
+	Prop-content-length: 10
+	Content-length: 10
+
+	PROPS-END
+
+	Node-path: greeting
+	Node-kind: file
+	Node-action: change
+	Prop-content-length: 33
+	Text-content-length: 11
+	Content-length: 44
+
+	K 11
+	svn:special
+	V 1
+	*
+	PROPS-END
+	link hello
+
+	Revision-number: 3
+	Prop-content-length: 10
+	Content-length: 10
+
+	PROPS-END
+
+	Node-path: greeting
+	Node-kind: file
+	Node-action: change
+	Prop-content-length: 10
+	Text-content-length: 11
+	Content-length: 21
+
+	PROPS-END
+	link hello
+	EOF
+	try_dump filemode2.dump &&
+	{
+		git rev-list HEAD |
+		git diff-tree --root --stdin |
+		sed "s/$_x40/OBJID/g"
+	} >actual &&
+	git show HEAD:greeting >actual.blob &&
+	git show HEAD^:greeting >actual.target &&
+	test_cmp expect actual &&
+	test_cmp expect.blob actual.blob &&
+	test_cmp hello actual.target
+'
+
+test_expect_success PIPE 'deltas supported' '
+	reinit_git &&
+	{
+		# (old) h + (inline) ello + (old) \n
+		printf "SVNQ%b%b%s" "Q\003\006\005\004" "\001Q\0204\001\002" "ello" |
+		q_to_nul
+	} >delta &&
+	{
+		properties \
+			svn:author author@example.com \
+			svn:date "1999-01-05T00:01:002.000000Z" \
+			svn:log "add greeting" &&
+		echo PROPS-END
+	} >props &&
+	{
+		properties \
+			svn:author author@example.com \
+			svn:date "1999-01-06T00:01:002.000000Z" \
+			svn:log "change it" &&
+		echo PROPS-END
+	} >props2 &&
+	{
+		echo SVN-fs-dump-format-version: 3 &&
+		echo &&
+		echo Revision-number: 1 &&
+		echo Prop-content-length: $(wc -c <props) &&
+		echo Content-length: $(wc -c <props) &&
+		echo &&
+		cat props &&
+		cat <<-\EOF &&
+
+		Node-path: hello
+		Node-kind: file
+		Node-action: add
+		Prop-content-length: 10
+		Text-content-length: 3
+		Content-length: 13
+
+		PROPS-END
+		hi
+
+		EOF
+		echo Revision-number: 2 &&
+		echo Prop-content-length: $(wc -c <props2) &&
+		echo Content-length: $(wc -c <props2) &&
+		echo &&
+		cat props2 &&
+		cat <<-\EOF &&
+
+		Node-path: hello
+		Node-kind: file
+		Node-action: change
+		Text-delta: true
+		Prop-content-length: 10
+		EOF
+		echo Text-content-length: $(wc -c <delta) &&
+		echo Content-length: $((10 + $(wc -c <delta))) &&
+		echo &&
+		echo PROPS-END &&
+		cat delta
+	} >delta.dump &&
+	try_dump delta.dump
+'
+
+test_expect_success PIPE 'property deltas supported' '
+	reinit_git &&
+	cat >expect <<-\EOF &&
+	OBJID
+	:100755 100644 OBJID OBJID M	script.sh
+	EOF
+	{
+		properties \
+			svn:author author@example.com \
+			svn:date "1999-03-06T00:01:002.000000Z" \
+			svn:log "make an executable, or chmod -x it" &&
+		echo PROPS-END
+	} >revprops &&
+	{
+		echo SVN-fs-dump-format-version: 3 &&
+		echo &&
+		echo Revision-number: 1 &&
+		echo Prop-content-length: $(wc -c <revprops) &&
+		echo Content-length: $(wc -c <revprops) &&
+		echo &&
+		cat revprops &&
+		echo &&
+		cat <<-\EOF &&
+		Node-path: script.sh
+		Node-kind: file
+		Node-action: add
+		Text-content-length: 0
+		Prop-content-length: 39
+		Content-length: 39
+
+		K 14
+		svn:executable
+		V 4
+		true
+		PROPS-END
+
+		EOF
+		echo Revision-number: 2 &&
+		echo Prop-content-length: $(wc -c <revprops) &&
+		echo Content-length: $(wc -c <revprops) &&
+		echo &&
+		cat revprops &&
+		echo &&
+		cat <<-\EOF
+		Node-path: script.sh
+		Node-kind: file
+		Node-action: change
+		Prop-delta: true
+		Prop-content-length: 30
+		Content-length: 30
+
+		D 14
+		svn:executable
+		PROPS-END
+		EOF
+	} >propdelta.dump &&
+	try_dump propdelta.dump &&
+	{
+		git rev-list HEAD |
+		git diff-tree --stdin |
+		sed "s/$_x40/OBJID/g"
+	} >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success PIPE 'properties on /' '
+	reinit_git &&
+	cat <<-\EOF >expect &&
+	OBJID
+	OBJID
+	:000000 100644 OBJID OBJID A	greeting
+	EOF
+	sed -e "s/X$//" <<-\EOF >changeroot.dump &&
+	SVN-fs-dump-format-version: 3
+
+	Revision-number: 1
+	Prop-content-length: 10
+	Content-length: 10
+
+	PROPS-END
+
+	Node-path: greeting
+	Node-kind: file
+	Node-action: add
+	Text-content-length: 0
+	Prop-content-length: 10
+	Content-length: 10
+
+	PROPS-END
+
+	Revision-number: 2
+	Prop-content-length: 10
+	Content-length: 10
+
+	PROPS-END
+
+	Node-path: X
+	Node-kind: dir
+	Node-action: change
+	Prop-delta: true
+	Prop-content-length: 43
+	Content-length: 43
+
+	K 10
+	svn:ignore
+	V 11
+	build-area
+
+	PROPS-END
+	EOF
+	try_dump changeroot.dump &&
+	{
+		git rev-list HEAD |
+		git diff-tree --root --always --stdin |
+		sed "s/$_x40/OBJID/g"
+	} >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success PIPE 'deltas for typechange' '
+	reinit_git &&
+	cat >expect <<-\EOF &&
+	OBJID
+	:120000 100644 OBJID OBJID T	test-file
+	OBJID
+	:100755 120000 OBJID OBJID T	test-file
+	OBJID
+	:000000 100755 OBJID OBJID A	test-file
+	EOF
+	cat >deleteprop.dump <<-\EOF &&
+	SVN-fs-dump-format-version: 3
+
+	Revision-number: 1
+	Prop-content-length: 10
+	Content-length: 10
+
+	PROPS-END
+
+	Node-path: test-file
+	Node-kind: file
+	Node-action: add
+	Prop-delta: true
+	Prop-content-length: 35
+	Text-content-length: 17
+	Content-length: 52
+
+	K 14
+	svn:executable
+	V 0
+
+	PROPS-END
+	link testing 123
+
+	Revision-number: 2
+	Prop-content-length: 10
+	Content-length: 10
+
+	PROPS-END
+
+	Node-path: test-file
+	Node-kind: file
+	Node-action: change
+	Prop-delta: true
+	Prop-content-length: 53
+	Text-content-length: 17
+	Content-length: 70
+
+	K 11
+	svn:special
+	V 1
+	*
+	D 14
+	svn:executable
+	PROPS-END
+	link testing 231
+
+	Revision-number: 3
+	Prop-content-length: 10
+	Content-length: 10
+
+	PROPS-END
+
+	Node-path: test-file
+	Node-kind: file
+	Node-action: change
+	Prop-delta: true
+	Prop-content-length: 27
+	Text-content-length: 17
+	Content-length: 44
+
+	D 11
+	svn:special
+	PROPS-END
+	link testing 321
+	EOF
+	try_dump deleteprop.dump &&
+	{
+		git rev-list HEAD |
+		git diff-tree --root --stdin |
+		sed "s/$_x40/OBJID/g"
+	} >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success PIPE 'deltas need not consume the whole preimage' '
+	reinit_git &&
+	cat >expect <<-\EOF &&
+	OBJID
+	:120000 100644 OBJID OBJID T	postimage
+	OBJID
+	:100644 120000 OBJID OBJID T	postimage
+	OBJID
+	:000000 100644 OBJID OBJID A	postimage
+	EOF
+	echo "first preimage" >expect.1 &&
+	printf target >expect.2 &&
+	printf lnk >expect.3 &&
+	{
+		printf "SVNQ%b%b%b" "QQ\017\001\017" "\0217" "first preimage\n" |
+		q_to_nul
+	} >delta.1 &&
+	{
+		properties svn:special "*" &&
+		echo PROPS-END
+	} >symlink.props &&
+	{
+		printf "SVNQ%b%b%b" "Q\002\013\004\012" "\0201\001\001\0211" "lnk target" |
+		q_to_nul
+	} >delta.2 &&
+	{
+		printf "SVNQ%b%b" "Q\004\003\004Q" "\001Q\002\002" |
+		q_to_nul
+	} >delta.3 &&
+	{
+		cat <<-\EOF &&
+		SVN-fs-dump-format-version: 3
+
+		Revision-number: 1
+		Prop-content-length: 10
+		Content-length: 10
+
+		PROPS-END
+
+		Node-path: postimage
+		Node-kind: file
+		Node-action: add
+		Text-delta: true
+		Prop-content-length: 10
+		EOF
+		echo Text-content-length: $(wc -c <delta.1) &&
+		echo Content-length: $((10 + $(wc -c <delta.1))) &&
+		echo &&
+		echo PROPS-END &&
+		cat delta.1 &&
+		cat <<-\EOF &&
+
+		Revision-number: 2
+		Prop-content-length: 10
+		Content-length: 10
+
+		PROPS-END
+
+		Node-path: postimage
+		Node-kind: file
+		Node-action: change
+		Text-delta: true
+		EOF
+		echo Prop-content-length: $(wc -c <symlink.props) &&
+		echo Text-content-length: $(wc -c <delta.2) &&
+		echo Content-length: $(($(wc -c <symlink.props) + $(wc -c <delta.2))) &&
+		echo &&
+		cat symlink.props &&
+		cat delta.2 &&
+		cat <<-\EOF &&
+
+		Revision-number: 3
+		Prop-content-length: 10
+		Content-length: 10
+
+		PROPS-END
+
+		Node-path: postimage
+		Node-kind: file
+		Node-action: change
+		Text-delta: true
+		Prop-content-length: 10
+		EOF
+		echo Text-content-length: $(wc -c <delta.3) &&
+		echo Content-length: $((10 + $(wc -c <delta.3))) &&
+		echo &&
+		echo PROPS-END &&
+		cat delta.3 &&
+		echo
+	} >deltapartial.dump &&
+	try_dump deltapartial.dump &&
+	{
+		git rev-list HEAD |
+		git diff-tree --root --stdin |
+		sed "s/$_x40/OBJID/g"
+	} >actual &&
+	test_cmp expect actual &&
+	git show HEAD:postimage >actual.3 &&
+	git show HEAD^:postimage >actual.2 &&
+	git show HEAD^^:postimage >actual.1 &&
+	test_cmp expect.1 actual.1 &&
+	test_cmp expect.2 actual.2 &&
+	test_cmp expect.3 actual.3
+'
+
+test_expect_success PIPE 'no hang for delta trying to read past end of preimage' '
+	reinit_git &&
+	{
+		# COPY 1
+		printf "SVNQ%b%b" "Q\001\001\002Q" "\001Q" |
+		q_to_nul
+	} >greedy.delta &&
+	{
+		cat <<-\EOF &&
+		SVN-fs-dump-format-version: 3
+
+		Revision-number: 1
+		Prop-content-length: 10
+		Content-length: 10
+
+		PROPS-END
+
+		Node-path: bootstrap
+		Node-kind: file
+		Node-action: add
+		Text-delta: true
+		Prop-content-length: 10
+		EOF
+		echo Text-content-length: $(wc -c <greedy.delta) &&
+		echo Content-length: $((10 + $(wc -c <greedy.delta))) &&
+		echo &&
+		echo PROPS-END &&
+		cat greedy.delta &&
+		echo
+	} >greedydelta.dump &&
+	try_dump greedydelta.dump must_fail might_fail
+'
+
+test_expect_success 'set up svn repo' '
+	svnconf=$PWD/svnconf &&
+	mkdir -p "$svnconf" &&
+
+	if
+		svnadmin -h >/dev/null 2>&1 &&
+		svnadmin create simple-svn &&
+		svnadmin load simple-svn <"$TEST_DIRECTORY/t9135/svn.dump" &&
+		svn export --config-dir "$svnconf" "file://$PWD/simple-svn" simple-svnco
+	then
+		test_set_prereq SVNREPO
+	fi
+'
+
+test_expect_success SVNREPO,PIPE 't9135/svn.dump' '
+	mkdir -p simple-git &&
+	(
+		cd simple-git &&
+		reinit_git &&
+		try_dump "$TEST_DIRECTORY/t9135/svn.dump"
+	) &&
+	(
+		cd simple-svnco &&
+		git init &&
+		git add . &&
+		git fetch ../simple-git master &&
+		git diff --exit-code FETCH_HEAD
+	)
+'
+
+test_done
diff --git a/t/t9011-svn-da.sh b/t/t9011-svn-da.sh
new file mode 100755
index 0000000..b38d16f
--- /dev/null
+++ b/t/t9011-svn-da.sh
@@ -0,0 +1,248 @@
+#!/bin/sh
+
+test_description='test parsing of svndiff0 files
+
+Using the "test-svn-fe -d" helper, check that svn-fe correctly
+interprets deltas using various facilities (some from the spec,
+some only learned from practice).
+'
+. ./test-lib.sh
+
+>empty
+printf foo >preimage
+
+test_expect_success 'reject empty delta' '
+	test_must_fail test-svn-fe -d preimage empty 0
+'
+
+test_expect_success 'delta can empty file' '
+	printf "SVNQ" | q_to_nul >clear.delta &&
+	test-svn-fe -d preimage clear.delta 4 >actual &&
+	test_cmp empty actual
+'
+
+test_expect_success 'reject svndiff2' '
+	printf "SVN\002" >bad.filetype &&
+	test_must_fail test-svn-fe -d preimage bad.filetype 4
+'
+
+test_expect_success 'one-window empty delta' '
+	printf "SVNQ%s" "QQQQQ" | q_to_nul >clear.onewindow &&
+	test-svn-fe -d preimage clear.onewindow 9 >actual &&
+	test_cmp empty actual
+'
+
+test_expect_success 'reject incomplete window header' '
+	printf "SVNQ%s" "QQQQQ" | q_to_nul >clear.onewindow &&
+	printf "SVNQ%s" "QQ" | q_to_nul >clear.partialwindow &&
+	test_must_fail test-svn-fe -d preimage clear.onewindow 6 &&
+	test_must_fail test-svn-fe -d preimage clear.partialwindow 6
+'
+
+test_expect_success 'reject declared delta longer than actual delta' '
+	printf "SVNQ%s" "QQQQQ" | q_to_nul >clear.onewindow &&
+	printf "SVNQ%s" "QQ" | q_to_nul >clear.partialwindow &&
+	test_must_fail test-svn-fe -d preimage clear.onewindow 14 &&
+	test_must_fail test-svn-fe -d preimage clear.partialwindow 9
+'
+
+test_expect_success 'two-window empty delta' '
+	printf "SVNQ%s%s" "QQQQQ" "QQQQQ" | q_to_nul >clear.twowindow &&
+	test-svn-fe -d preimage clear.twowindow 14 >actual &&
+	test_must_fail test-svn-fe -d preimage clear.twowindow 13 &&
+	test_cmp empty actual
+'
+
+test_expect_success 'noisy zeroes' '
+	printf "SVNQ%s" \
+		"RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQ" |
+		tr R "\200" |
+		q_to_nul >clear.noisy &&
+	len=$(wc -c <clear.noisy) &&
+	test-svn-fe -d preimage clear.noisy $len &&
+	test_cmp empty actual
+'
+
+test_expect_success 'reject variable-length int in magic' '
+	printf "SVNRQ" | tr R "\200" | q_to_nul >clear.badmagic &&
+	test_must_fail test-svn-fe -d preimage clear.badmagic 5
+'
+
+test_expect_success 'reject truncated integer' '
+	printf "SVNQ%s%s" "QQQQQ" "QQQQRRQ" |
+		tr R "\200" |
+		q_to_nul >clear.fullint &&
+	printf "SVNQ%s%s" "QQQQQ" "QQQQRR" |
+		tr RT "\201" |
+		q_to_nul >clear.partialint &&
+	test_must_fail test-svn-fe -d preimage clear.fullint 15 &&
+	test-svn-fe -d preimage clear.fullint 16 &&
+	test_must_fail test-svn-fe -d preimage clear.partialint 15
+'
+
+test_expect_success 'nonempty (but unused) preimage view' '
+	printf "SVNQ%b" "Q\003QQQ" | q_to_nul >clear.readpreimage &&
+	test-svn-fe -d preimage clear.readpreimage 9 >actual &&
+	test_cmp empty actual
+'
+
+test_expect_success 'preimage view: right endpoint cannot backtrack' '
+	printf "SVNQ%b%b" "Q\003QQQ" "Q\002QQQ" |
+		q_to_nul >clear.backtrack &&
+	test_must_fail test-svn-fe -d preimage clear.backtrack 14
+'
+
+test_expect_success 'preimage view: left endpoint can advance' '
+	printf "SVNQ%b%b" "Q\003QQQ" "\001\002QQQ" |
+		q_to_nul >clear.preshrink &&
+	printf "SVNQ%b%b" "Q\003QQQ" "\001\001QQQ" |
+		q_to_nul >clear.shrinkbacktrack &&
+	test-svn-fe -d preimage clear.preshrink 14 >actual &&
+	test_must_fail test-svn-fe -d preimage clear.shrinkbacktrack 14 &&
+	test_cmp empty actual
+'
+
+test_expect_success 'preimage view: offsets compared by value' '
+	printf "SVNQ%b%b" "\001\001QQQ" "\0200Q\003QQQ" |
+		q_to_nul >clear.noisybacktrack &&
+	printf "SVNQ%b%b" "\001\001QQQ" "\0200\001\002QQQ" |
+		q_to_nul >clear.noisyadvance &&
+	test_must_fail test-svn-fe -d preimage clear.noisybacktrack 15 &&
+	test-svn-fe -d preimage clear.noisyadvance 15 &&
+	test_cmp empty actual
+'
+
+test_expect_success 'preimage view: reject truncated preimage' '
+	printf "SVNQ%b" "\010QQQQ" | q_to_nul >clear.lateemptyread &&
+	printf "SVNQ%b" "\010\001QQQ" | q_to_nul >clear.latenonemptyread &&
+	printf "SVNQ%b" "\001\010QQQ" | q_to_nul >clear.longread &&
+	test_must_fail test-svn-fe -d preimage clear.lateemptyread 9 &&
+	test_must_fail test-svn-fe -d preimage clear.latenonemptyread 9 &&
+	test_must_fail test-svn-fe -d preimage clear.longread 9
+'
+
+test_expect_success 'forbid unconsumed inline data' '
+	printf "SVNQ%b%s%b%s" "QQQQ\003" "bar" "QQQQ\001" "x" |
+		q_to_nul >inline.clear &&
+	test_must_fail test-svn-fe -d preimage inline.clear 18 >actual
+'
+
+test_expect_success 'reject truncated inline data' '
+	printf "SVNQ%b%s" "QQQQ\003" "b" | q_to_nul >inline.trunc &&
+	test_must_fail test-svn-fe -d preimage inline.trunc 10
+'
+
+test_expect_success 'reject truncated inline data (after instruction section)' '
+	printf "SVNQ%b%b%s" "QQ\001\001\003" "\0201" "b" | q_to_nul >insn.trunc &&
+	test_must_fail test-svn-fe -d preimage insn.trunc 11
+'
+
+test_expect_success 'copyfrom_data' '
+	echo hi >expect &&
+	printf "SVNQ%b%b%b" "QQ\003\001\003" "\0203" "hi\n" | q_to_nul >copydat &&
+	test-svn-fe -d preimage copydat 13 >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'multiple copyfrom_data' '
+	echo hi >expect &&
+	printf "SVNQ%b%b%b%b%b" "QQ\003\002\003" "\0201\0202" "hi\n" \
+		"QQQ\002Q" "\0200Q" | q_to_nul >copy.multi &&
+	len=$(wc -c <copy.multi) &&
+	test-svn-fe -d preimage copy.multi $len >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'incomplete multiple insn' '
+	printf "SVNQ%b%b%b" "QQ\003\002\003" "\0203\0200" "hi\n" |
+		q_to_nul >copy.partial &&
+	len=$(wc -c <copy.partial) &&
+	test_must_fail test-svn-fe -d preimage copy.partial $len
+'
+
+test_expect_success 'catch attempt to copy missing data' '
+	printf "SVNQ%b%b%s%b%s" "QQ\002\002\001" "\0201\0201" "X" \
+			"QQQQ\002" "YZ" |
+		q_to_nul >copy.incomplete &&
+	len=$(wc -c <copy.incomplete) &&
+	test_must_fail test-svn-fe -d preimage copy.incomplete $len
+'
+
+test_expect_success 'copyfrom target to repeat data' '
+	printf foofoo >expect &&
+	printf "SVNQ%b%b%s" "QQ\006\004\003" "\0203\0100\003Q" "foo" |
+		q_to_nul >copytarget.repeat &&
+	len=$(wc -c <copytarget.repeat) &&
+	test-svn-fe -d preimage copytarget.repeat $len >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'copyfrom target out of order' '
+	printf foooof >expect &&
+	printf "SVNQ%b%b%s" \
+		"QQ\006\007\003" "\0203\0101\002\0101\001\0101Q" "foo" |
+		q_to_nul >copytarget.reverse &&
+	len=$(wc -c <copytarget.reverse) &&
+	test-svn-fe -d preimage copytarget.reverse $len >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'catch copyfrom future' '
+	printf "SVNQ%b%b%s" "QQ\004\004\003" "\0202\0101\002\0201" "XYZ" |
+		q_to_nul >copytarget.infuture &&
+	len=$(wc -c <copytarget.infuture) &&
+	test_must_fail test-svn-fe -d preimage copytarget.infuture $len
+'
+
+test_expect_success 'copy to sustain' '
+	printf XYXYXYXYXYXZ >expect &&
+	printf "SVNQ%b%b%s" "QQ\014\004\003" "\0202\0111Q\0201" "XYZ" |
+		q_to_nul >copytarget.sustain &&
+	len=$(wc -c <copytarget.sustain) &&
+	test-svn-fe -d preimage copytarget.sustain $len >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'catch copy that overflows' '
+	printf "SVNQ%b%b%s" "QQ\003\003\001" "\0201\0177Q" X |
+		q_to_nul >copytarget.overflow &&
+	len=$(wc -c <copytarget.overflow) &&
+	test_must_fail test-svn-fe -d preimage copytarget.overflow $len
+'
+
+test_expect_success 'copyfrom source' '
+	printf foo >expect &&
+	printf "SVNQ%b%b" "Q\003\003\002Q" "\003Q" | q_to_nul >copysource.all &&
+	test-svn-fe -d preimage copysource.all 11 >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'copy backwards' '
+	printf oof >expect &&
+	printf "SVNQ%b%b" "Q\003\003\006Q" "\001\002\001\001\001Q" |
+		q_to_nul >copysource.rev &&
+	test-svn-fe -d preimage copysource.rev 15 >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'offsets are relative to window' '
+	printf fo >expect &&
+	printf "SVNQ%b%b%b%b" "Q\003\001\002Q" "\001Q" \
+		"\002\001\001\002Q" "\001Q" |
+		q_to_nul >copysource.two &&
+	test-svn-fe -d preimage copysource.two 18 >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'example from notes/svndiff' '
+	printf aaaaccccdddddddd >expect &&
+	printf aaaabbbbcccc >source &&
+	printf "SVNQ%b%b%s" "Q\014\020\007\001" \
+		"\004Q\004\010\0201\0107\010" d |
+		q_to_nul >delta.example &&
+	len=$(wc -c <delta.example) &&
+	test-svn-fe -d source delta.example $len >actual &&
+	test_cmp expect actual
+'
+
+test_done
diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh
index 570e035..749b75e 100755
--- a/t/t9100-git-svn-basic.sh
+++ b/t/t9100-git-svn-basic.sh
@@ -15,24 +15,25 @@
 	test_set_prereq UTF8
 	;;
 *)
-	say "UTF-8 locale not set, some tests skipped ($GIT_SVN_LC_ALL)"
+	say "# UTF-8 locale not set, some tests skipped ($GIT_SVN_LC_ALL)"
 	;;
 esac
 
 test_expect_success \
     'initialize git svn' '
 	mkdir import &&
-	cd import &&
-	echo foo > foo &&
-	ln -s foo foo.link
-	mkdir -p dir/a/b/c/d/e &&
-	echo "deep dir" > dir/a/b/c/d/e/file &&
-	mkdir bar &&
-	echo "zzz" > bar/zzz &&
-	echo "#!/bin/sh" > exec.sh &&
-	chmod +x exec.sh &&
-	svn_cmd import -m "import for git svn" . "$svnrepo" >/dev/null &&
-	cd .. &&
+	(
+		cd import &&
+		echo foo >foo &&
+		ln -s foo foo.link
+		mkdir -p dir/a/b/c/d/e &&
+		echo "deep dir" >dir/a/b/c/d/e/file &&
+		mkdir bar &&
+		echo "zzz" >bar/zzz &&
+		echo "#!/bin/sh" >exec.sh &&
+		chmod +x exec.sh &&
+		svn_cmd import -m "import for git svn" . "$svnrepo" >/dev/null
+	) &&
 	rm -rf import &&
 	git svn init "$svnrepo"'
 
@@ -64,7 +65,8 @@
 	git update-index --add dir/file/file &&
 	git commit -m '$name' &&
 	test_must_fail git svn set-tree --find-copies-harder --rmdir \
-		${remotes_git_svn}..mybranch" || true
+		${remotes_git_svn}..mybranch
+"
 
 
 name='detect node change from directory to file #1'
@@ -78,7 +80,8 @@
 	git update-index --add -- bar &&
 	git commit -m "$name" &&
 	test_must_fail git svn set-tree --find-copies-harder --rmdir \
-		${remotes_git_svn}..mybranch2' || true
+		${remotes_git_svn}..mybranch2
+'
 
 
 name='detect node change from file to directory #2'
@@ -91,9 +94,12 @@
 	echo yyy > bar/zzz/yyy &&
 	git update-index --add bar/zzz/yyy &&
 	git commit -m "$name" &&
-	test_must_fail git svn set-tree --find-copies-harder --rmdir \
-		${remotes_git_svn}..mybranch3' || true
-
+	git svn set-tree --find-copies-harder --rmdir \
+		${remotes_git_svn}..mybranch3 &&
+	svn_cmd up "$SVN_TREE" &&
+	test -d "$SVN_TREE"/bar/zzz &&
+	test -e "$SVN_TREE"/bar/zzz/yyy
+'
 
 name='detect node change from directory to file #2'
 test_expect_success "$name" '
@@ -106,7 +112,8 @@
 	git update-index --add -- dir &&
 	git commit -m "$name" &&
 	test_must_fail git svn set-tree --find-copies-harder --rmdir \
-		${remotes_git_svn}..mybranch4' || true
+		${remotes_git_svn}..mybranch4
+'
 
 
 name='remove executable bit from a file'
@@ -133,33 +140,33 @@
 	test -x "$SVN_TREE"/exec.sh'
 
 
-name='executable file becomes a symlink to bar/zzz (file)'
+name='executable file becomes a symlink to file'
 test_expect_success "$name" '
 	rm exec.sh &&
-	ln -s bar/zzz exec.sh &&
+	ln -s file exec.sh &&
 	git update-index exec.sh &&
 	git commit -m "$name" &&
 	git svn set-tree --find-copies-harder --rmdir \
 		${remotes_git_svn}..mybranch5 &&
 	svn_cmd up "$SVN_TREE" &&
-	test -L "$SVN_TREE"/exec.sh'
+	test -h "$SVN_TREE"/exec.sh'
 
 name='new symlink is added to a file that was also just made executable'
 
 test_expect_success "$name" '
-	chmod +x bar/zzz &&
-	ln -s bar/zzz exec-2.sh &&
-	git update-index --add bar/zzz exec-2.sh &&
+	chmod +x file &&
+	ln -s file exec-2.sh &&
+	git update-index --add file exec-2.sh &&
 	git commit -m "$name" &&
 	git svn set-tree --find-copies-harder --rmdir \
 		${remotes_git_svn}..mybranch5 &&
 	svn_cmd up "$SVN_TREE" &&
-	test -x "$SVN_TREE"/bar/zzz &&
-	test -L "$SVN_TREE"/exec-2.sh'
+	test -x "$SVN_TREE"/file &&
+	test -h "$SVN_TREE"/exec-2.sh'
 
 name='modify a symlink to become a file'
 test_expect_success "$name" '
-	echo git help > help || true &&
+	echo git help >help &&
 	rm exec-2.sh &&
 	cp help exec-2.sh &&
 	git update-index exec-2.sh &&
@@ -168,7 +175,7 @@
 		${remotes_git_svn}..mybranch5 &&
 	svn_cmd up "$SVN_TREE" &&
 	test -f "$SVN_TREE"/exec-2.sh &&
-	test ! -L "$SVN_TREE"/exec-2.sh &&
+	test ! -h "$SVN_TREE"/exec-2.sh &&
 	test_cmp help "$SVN_TREE"/exec-2.sh'
 
 name="commit with UTF-8 message: locale: $GIT_SVN_LC_ALL"
@@ -194,14 +201,15 @@
 rm -f expected
 if test_have_prereq UTF8
 then
-	echo tree bf522353586b1b883488f2bc73dab0d9f774b9a9 > expected
+	echo tree dc68b14b733e4ec85b04ab6f712340edc5dc936e > expected
 fi
 cat >> expected <<\EOF
-tree 83654bb36f019ae4fe77a0171f81075972087624
-tree 031b8d557afc6fea52894eaebb45bec52f1ba6d1
-tree 0b094cbff17168f24c302e297f55bfac65eb8bd3
-tree d667270a1f7b109f5eb3aaea21ede14b56bfdd6e
-tree 56a30b966619b863674f5978696f4a3594f2fca9
+tree c3322890dcf74901f32d216f05c5044f670ce632
+tree d3ccd5035feafd17b030c5732e7808cc49122853
+tree d03e1630363d4881e68929d532746b20b0986b83
+tree 149d63cd5878155c846e8c55d7d8487de283f89e
+tree 312b76e4f64ce14893aeac8591eb3960b065e247
+tree 149d63cd5878155c846e8c55d7d8487de283f89e
 tree d667270a1f7b109f5eb3aaea21ede14b56bfdd6e
 tree 8f51f74cf0163afc9ad68a4b1537288c4558b5a4
 EOF
@@ -271,6 +279,17 @@
 	test -z \"\`git diff refs/heads/my-bar refs/remotes/bar\`\"
 	"
 
+test_expect_success 'dcommit should not fail with a touched file' '
+	test_commit "commit-new-file-foo2" foo2 &&
+	test-chmtime =-60 foo &&
+	git svn dcommit
+'
+
+test_expect_success 'rebase should not fail with a touched file' '
+	test-chmtime =-60 foo &&
+	git svn rebase
+'
+
 test_expect_success 'able to set-tree to a subdirectory' "
 	echo cba > d &&
 	git update-index d &&
diff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh
index 9da4178..8869f50 100755
--- a/t/t9101-git-svn-props.sh
+++ b/t/t9101-git-svn-props.sh
@@ -53,8 +53,9 @@
 
 rm -rf import
 test_expect_success 'checkout working copy from svn' 'svn co "$svnrepo" test_wc'
-test_expect_success 'setup some commits to svn' \
-	'cd test_wc &&
+test_expect_success 'setup some commits to svn' '
+	(
+		cd test_wc &&
 		echo Greetings >> kw.c &&
 		poke kw.c &&
 		svn_cmd commit -m "Not yet an Id" &&
@@ -63,8 +64,9 @@
 		svn_cmd commit -m "Modified file, but still not yet an Id" &&
 		svn_cmd propset svn:keywords Id kw.c &&
 		poke kw.c &&
-		svn_cmd commit -m "Propset Id" &&
-	cd ..'
+		svn_cmd commit -m "Propset Id"
+	)
+'
 
 test_expect_success 'initialize git svn' 'git svn init "$svnrepo"'
 test_expect_success 'fetch revisions from svn' 'git svn fetch'
@@ -81,13 +83,15 @@
 got="`sed -ne 2p kw.c`"
 test_expect_success 'raw $Id$ found in kw.c' "test '$expect' = '$got'"
 
-test_expect_success "propset CR on crlf files" \
-	'cd test_wc &&
+test_expect_success "propset CR on crlf files" '
+	(
+		cd test_wc &&
 		svn_cmd propset svn:eol-style CR empty &&
 		svn_cmd propset svn:eol-style CR crlf &&
 		svn_cmd propset svn:eol-style CR ne_crlf &&
-		svn_cmd commit -m "propset CR on crlf files" &&
-	 cd ..'
+		svn_cmd commit -m "propset CR on crlf files"
+	 )
+'
 
 test_expect_success 'fetch and pull latest from svn and checkout a new wc' \
 	'git svn fetch &&
@@ -137,17 +141,20 @@
 EOF
 
 test_expect_success 'test show-ignore' "
-	cd test_wc &&
-	mkdir -p deeply/nested/directory &&
-	touch deeply/nested/directory/.keep &&
-	svn_cmd add deeply &&
-	svn_cmd up &&
-	svn_cmd propset -R svn:ignore 'no-such-file*' .
-	svn_cmd commit -m 'propset svn:ignore'
-	cd .. &&
+	(
+		cd test_wc &&
+		mkdir -p deeply/nested/directory &&
+		touch deeply/nested/directory/.keep &&
+		svn_cmd add deeply &&
+		svn_cmd up &&
+		svn_cmd propset -R svn:ignore '
+no-such-file*
+' .
+		svn_cmd commit -m 'propset svn:ignore'
+	) &&
 	git svn show-ignore > show-ignore.got &&
 	cmp show-ignore.expect show-ignore.got
-	"
+"
 
 cat >create-ignore.expect <<\EOF
 /no-such-file*
@@ -171,6 +178,7 @@
 	"
 
 cat >prop.expect <<\EOF
+
 no-such-file*
 
 EOF
diff --git a/t/t9102-git-svn-deep-rmdir.sh b/t/t9102-git-svn-deep-rmdir.sh
index 028fb19..eb70f48 100755
--- a/t/t9102-git-svn-deep-rmdir.sh
+++ b/t/t9102-git-svn-deep-rmdir.sh
@@ -4,13 +4,14 @@
 
 test_expect_success 'initialize repo' '
 	mkdir import &&
-	cd import &&
-	mkdir -p deeply/nested/directory/number/1 &&
-	mkdir -p deeply/nested/directory/number/2 &&
-	echo foo > deeply/nested/directory/number/1/file &&
-	echo foo > deeply/nested/directory/number/2/another &&
-	svn_cmd import -m "import for git svn" . "$svnrepo" &&
-	cd ..
+	(
+		cd import &&
+		mkdir -p deeply/nested/directory/number/1 &&
+		mkdir -p deeply/nested/directory/number/2 &&
+		echo foo >deeply/nested/directory/number/1/file &&
+		echo foo >deeply/nested/directory/number/2/another &&
+		svn_cmd import -m "import for git svn" . "$svnrepo"
+	)
 	'
 
 test_expect_success 'mirror via git svn' '
diff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh
index 78610b6..13b179e 100755
--- a/t/t9104-git-svn-follow-parent.sh
+++ b/t/t9104-git-svn-follow-parent.sh
@@ -8,22 +8,24 @@
 
 test_expect_success 'initialize repo' '
 	mkdir import &&
-	cd import &&
-	mkdir -p trunk &&
-	echo hello > trunk/readme &&
-	svn_cmd import -m "initial" . "$svnrepo" &&
-	cd .. &&
+	(
+		cd import &&
+		mkdir -p trunk &&
+		echo hello >trunk/readme &&
+		svn_cmd import -m "initial" . "$svnrepo"
+	) &&
 	svn_cmd co "$svnrepo" wc &&
-	cd wc &&
-	echo world >> trunk/readme &&
-	poke trunk/readme &&
-	svn_cmd commit -m "another commit" &&
-	svn_cmd up &&
-	svn_cmd mv trunk thunk &&
-	echo goodbye >> thunk/readme &&
-	poke thunk/readme &&
-	svn_cmd commit -m "bye now" &&
-	cd ..
+	(
+		cd wc &&
+		echo world >>trunk/readme &&
+		poke trunk/readme &&
+		svn_cmd commit -m "another commit" &&
+		svn_cmd up &&
+		svn_cmd mv trunk thunk &&
+		echo goodbye >>thunk/readme &&
+		poke thunk/readme &&
+		svn_cmd commit -m "bye now"
+	)
 	'
 
 test_expect_success 'init and fetch a moved directory' '
@@ -83,16 +85,17 @@
         '
 
 test_expect_success 'follow higher-level parent' '
-        svn mkdir -m "follow higher-level parent" "$svnrepo"/blob &&
-        svn co "$svnrepo"/blob blob &&
-        cd blob &&
-                echo hi > hi &&
-                svn add hi &&
-                svn commit -m "hihi" &&
-                cd ..
-        svn mkdir -m "new glob at top level" "$svnrepo"/glob &&
-        svn mv -m "move blob down a level" "$svnrepo"/blob "$svnrepo"/glob/blob &&
-        git svn init --minimize-url -i blob "$svnrepo"/glob/blob &&
+	svn mkdir -m "follow higher-level parent" "$svnrepo"/blob &&
+	svn co "$svnrepo"/blob blob &&
+	(
+		cd blob &&
+		echo hi > hi &&
+		svn add hi &&
+		svn commit -m "hihi"
+	) &&
+	svn mkdir -m "new glob at top level" "$svnrepo"/glob &&
+	svn mv -m "move blob down a level" "$svnrepo"/blob "$svnrepo"/glob/blob &&
+	git svn init --minimize-url -i blob "$svnrepo"/glob/blob &&
         git svn fetch -i blob
         '
 
@@ -117,18 +120,23 @@
 	   import/trunk/subversion/bindings/swig/perl/t/larger-parent &&
 	  echo "bad delete test 2" > \
 	   import/trunk/subversion/bindings/swig/perl/another-larger &&
-	cd import &&
-	  svn import -m "r9270 test" . "$svnrepo"/r9270 &&
-	cd .. &&
+	(
+		cd import &&
+		svn import -m "r9270 test" . "$svnrepo"/r9270
+	) &&
 	svn_cmd co "$svnrepo"/r9270/trunk/subversion/bindings/swig/perl r9270 &&
-	cd r9270 &&
-	  svn mkdir native &&
-	  svn mv t native/t &&
-	  for i in a b c; do svn mv $i.pm native/$i.pm; done &&
-	  echo z >> native/t/c.t &&
-	  poke native/t/c.t &&
-	  svn commit -m "reorg test" &&
-	cd .. &&
+	(
+		cd r9270 &&
+		svn mkdir native &&
+		svn mv t native/t &&
+		for i in a b c
+		do
+			svn mv $i.pm native/$i.pm
+		done &&
+		echo z >>native/t/c.t &&
+		poke native/t/c.t &&
+		svn commit -m "reorg test"
+	) &&
 	git svn init --minimize-url -i r9270-t \
 	  "$svnrepo"/r9270/trunk/subversion/bindings/swig/perl/native/t &&
 	git svn fetch -i r9270-t &&
@@ -172,17 +180,17 @@
 	git update-ref refs/remotes/flunk@18 refs/remotes/stunk~2 &&
 	git update-ref -d refs/remotes/stunk &&
 	git config --unset svn-remote.svn.fetch stunk &&
-	mkdir -p "$GIT_DIR"/svn/flunk@18 &&
-	rev_map=$(cd "$GIT_DIR"/svn/stunk && ls .rev_map*) &&
-	dd if="$GIT_DIR"/svn/stunk/$rev_map \
-	   of="$GIT_DIR"/svn/flunk@18/$rev_map bs=24 count=1 &&
-	rm -rf "$GIT_DIR"/svn/stunk &&
+	mkdir -p "$GIT_DIR"/svn/refs/remotes/flunk@18 &&
+	rev_map=$(cd "$GIT_DIR"/svn/refs/remotes/stunk && ls .rev_map*) &&
+	dd if="$GIT_DIR"/svn/refs/remotes/stunk/$rev_map \
+	   of="$GIT_DIR"/svn/refs/remotes/flunk@18/$rev_map bs=24 count=1 &&
+	rm -rf "$GIT_DIR"/svn/refs/remotes/stunk &&
 	git svn init --minimize-url -i flunk "$svnrepo"/flunk &&
 	git svn fetch -i flunk &&
 	git svn init --minimize-url -i stunk "$svnrepo"/stunk &&
 	git svn fetch -i stunk &&
 	git svn init --minimize-url -i flunked "$svnrepo"/flunked &&
-	git svn fetch -i flunked
+	git svn fetch -i flunked &&
 	test "`git rev-parse --verify refs/remotes/flunk@18`" \
 	   = "`git rev-parse --verify refs/remotes/stunk`" &&
 	test "`git rev-parse --verify refs/remotes/flunk~1`" \
diff --git a/t/t9105-git-svn-commit-diff.sh b/t/t9105-git-svn-commit-diff.sh
index dd48e9c..5d0afea 100755
--- a/t/t9105-git-svn-commit-diff.sh
+++ b/t/t9105-git-svn-commit-diff.sh
@@ -6,10 +6,11 @@
 
 test_expect_success 'initialize repo' '
 	mkdir import &&
-	cd import &&
-	echo hello > readme &&
-	svn_cmd import -m "initial" . "$svnrepo" &&
-	cd .. &&
+	(
+		cd import &&
+		echo hello >readme &&
+		svn_cmd import -m "initial" . "$svnrepo"
+	) &&
 	echo hello > readme &&
 	git update-index --add readme &&
 	git commit -a -m "initial" &&
diff --git a/t/t9106-git-svn-commit-diff-clobber.sh b/t/t9106-git-svn-commit-diff-clobber.sh
index 12f21b7..f6d7ac7 100755
--- a/t/t9106-git-svn-commit-diff-clobber.sh
+++ b/t/t9106-git-svn-commit-diff-clobber.sh
@@ -6,21 +6,23 @@
 
 test_expect_success 'initialize repo' '
 	mkdir import &&
-	cd import &&
-	echo initial > file &&
-	svn_cmd import -m "initial" . "$svnrepo" &&
-	cd .. &&
+	(
+		cd import &&
+		echo initial >file &&
+		svn_cmd import -m "initial" . "$svnrepo"
+	) &&
 	echo initial > file &&
 	git update-index --add file &&
 	git commit -a -m "initial"
 	'
 test_expect_success 'commit change from svn side' '
 	svn_cmd co "$svnrepo" t.svn &&
-	cd t.svn &&
-	echo second line from svn >> file &&
-	poke file &&
-	svn_cmd commit -m "second line from svn" &&
-	cd .. &&
+	(
+		cd t.svn &&
+		echo second line from svn >>file &&
+		poke file &&
+		svn_cmd commit -m "second line from svn"
+	) &&
 	rm -rf t.svn
 	'
 
@@ -44,11 +46,12 @@
 	git svn fetch &&
 	git reset --hard refs/${remotes_git_svn} &&
 	svn_cmd co "$svnrepo" t.svn &&
-	cd t.svn &&
-	echo fourth line from svn >> file &&
-	poke file &&
-	svn_cmd commit -m "fourth line from svn" &&
-	cd .. &&
+	(
+		cd t.svn &&
+		echo fourth line from svn >>file &&
+		poke file &&
+		svn_cmd commit -m "fourth line from svn"
+	) &&
 	rm -rf t.svn &&
 	echo "fourth line from git" >> file &&
 	git commit -a -m "fourth line from git" &&
@@ -68,11 +71,12 @@
 
 test_expect_success 'commit another change from svn side' '
 	svn_cmd co "$svnrepo" t.svn &&
-	cd t.svn &&
-		echo third line from svn >> file &&
+	(
+		cd t.svn &&
+		echo third line from svn >>file &&
 		poke file &&
-		svn_cmd commit -m "third line from svn" &&
-	cd .. &&
+		svn_cmd commit -m "third line from svn"
+	) &&
 	rm -rf t.svn
 	'
 
diff --git a/t/t9107-git-svn-migrate.sh b/t/t9107-git-svn-migrate.sh
index 3a9e077..289fc31 100755
--- a/t/t9107-git-svn-migrate.sh
+++ b/t/t9107-git-svn-migrate.sh
@@ -6,19 +6,19 @@
 test_expect_success 'setup old-looking metadata' '
 	cp "$GIT_DIR"/config "$GIT_DIR"/config-old-git-svn &&
 	mkdir import &&
-	cd import &&
-		for i in trunk branches/a branches/b \
-		         tags/0.1 tags/0.2 tags/0.3; do
-			mkdir -p $i && \
-			echo hello >> $i/README || exit 1
-		done && \
+	(
+		cd import &&
+		for i in trunk branches/a branches/b tags/0.1 tags/0.2 tags/0.3
+		do
+			mkdir -p $i &&
+			echo hello >>$i/README ||
+			exit 1
+		done &&
 		svn_cmd import -m test . "$svnrepo"
-		cd .. &&
+	) &&
 	git svn init "$svnrepo" &&
 	git svn fetch &&
-	mv "$GIT_DIR"/svn/* "$GIT_DIR"/ &&
-	mv "$GIT_DIR"/svn/.metadata "$GIT_DIR"/ &&
-	rmdir "$GIT_DIR"/svn &&
+	rm -rf "$GIT_DIR"/svn &&
 	git update-ref refs/heads/git-svn-HEAD refs/${remotes_git_svn} &&
 	git update-ref refs/heads/svn-HEAD refs/${remotes_git_svn} &&
 	git update-ref -d refs/${remotes_git_svn} refs/${remotes_git_svn}
@@ -56,7 +56,15 @@
 	git config --add svn-remote.svn.fetch "branches/b:refs/remotes/b" &&
 	for i in tags/0.1 tags/0.2 tags/0.3; do
 		git config --add svn-remote.svn.fetch \
-		                 $i:refs/remotes/$i || exit 1; done
+		                 $i:refs/remotes/$i || exit 1; done &&
+	git config --get-all svn-remote.svn.fetch > fetch.out &&
+	grep "^trunk:refs/remotes/trunk$" fetch.out &&
+	grep "^branches/a:refs/remotes/a$" fetch.out &&
+	grep "^branches/b:refs/remotes/b$" fetch.out &&
+	grep "^tags/0\.1:refs/remotes/tags/0\.1$" fetch.out &&
+	grep "^tags/0\.2:refs/remotes/tags/0\.2$" fetch.out &&
+	grep "^tags/0\.3:refs/remotes/tags/0\.3$" fetch.out &&
+	grep "^:refs/${remotes_git_svn}" fetch.out
 	'
 
 # refs should all be different, but the trees should all be the same:
@@ -79,36 +87,36 @@
 	rm -rf "$GIT_DIR"/svn &&
 	for i in `cat fetch.out`; do
 		path=`expr $i : "\([^:]*\):.*$"`
-		ref=`expr $i : "[^:]*:refs/remotes/\(.*\)$"`
+		ref=`expr $i : "[^:]*:\(refs/remotes/.*\)$"`
 		if test -z "$ref"; then continue; fi
 		if test -n "$path"; then path="/$path"; fi
 		( mkdir -p "$GIT_DIR"/svn/$ref/info/ &&
 		echo "$svnrepo"$path > "$GIT_DIR"/svn/$ref/info/url ) || exit 1;
 	done &&
 	git svn migrate --minimize &&
-	test -z "`git config -l |grep -v "^svn-remote\.git-svn\."`" &&
+	test -z "`git config -l | grep "^svn-remote\.git-svn\."`" &&
 	git config --get-all svn-remote.svn.fetch > fetch.out &&
 	grep "^trunk:refs/remotes/trunk$" fetch.out &&
 	grep "^branches/a:refs/remotes/a$" fetch.out &&
 	grep "^branches/b:refs/remotes/b$" fetch.out &&
 	grep "^tags/0\.1:refs/remotes/tags/0\.1$" fetch.out &&
 	grep "^tags/0\.2:refs/remotes/tags/0\.2$" fetch.out &&
-	grep "^tags/0\.3:refs/remotes/tags/0\.3$" fetch.out
+	grep "^tags/0\.3:refs/remotes/tags/0\.3$" fetch.out &&
 	grep "^:refs/${remotes_git_svn}" fetch.out
 	'
 
 test_expect_success  ".rev_db auto-converted to .rev_map.UUID" '
 	git svn fetch -i trunk &&
-	test -z "$(ls "$GIT_DIR"/svn/trunk/.rev_db.* 2>/dev/null)" &&
-	expect="$(ls "$GIT_DIR"/svn/trunk/.rev_map.*)" &&
+	test -z "$(ls "$GIT_DIR"/svn/refs/remotes/trunk/.rev_db.* 2>/dev/null)" &&
+	expect="$(ls "$GIT_DIR"/svn/refs/remotes/trunk/.rev_map.*)" &&
 	test -n "$expect" &&
 	rev_db="$(echo $expect | sed -e "s,_map,_db,")" &&
 	convert_to_rev_db "$expect" "$rev_db" &&
 	rm -f "$expect" &&
 	test -f "$rev_db" &&
 	git svn fetch -i trunk &&
-	test -z "$(ls "$GIT_DIR"/svn/trunk/.rev_db.* 2>/dev/null)" &&
-	test ! -e "$GIT_DIR"/svn/trunk/.rev_db &&
+	test -z "$(ls "$GIT_DIR"/svn/refs/remotes/trunk/.rev_db.* 2>/dev/null)" &&
+	test ! -e "$GIT_DIR"/svn/refs/remotes/trunk/.rev_db &&
 	test -f "$expect"
 	'
 
diff --git a/t/t9114-git-svn-dcommit-merge.sh b/t/t9114-git-svn-dcommit-merge.sh
index 84f7c9b..3077851 100755
--- a/t/t9114-git-svn-dcommit-merge.sh
+++ b/t/t9114-git-svn-dcommit-merge.sh
@@ -37,11 +37,12 @@
 test_expect_success 'setup svn repository' '
 	svn_cmd co "$svnrepo" mysvnwork &&
 	mkdir -p mysvnwork/trunk &&
-	cd mysvnwork &&
-		big_text_block >> trunk/README &&
+	(
+		cd mysvnwork &&
+		big_text_block >>trunk/README &&
 		svn_cmd add trunk &&
-		svn_cmd ci -m "first commit" trunk &&
-		cd ..
+		svn_cmd ci -m "first commit" trunk
+	)
 	'
 
 test_expect_success 'setup git mirror and merge' '
diff --git a/t/t9115-git-svn-dcommit-funky-renames.sh b/t/t9115-git-svn-dcommit-funky-renames.sh
index 9be7aef..6a48e40 100755
--- a/t/t9115-git-svn-dcommit-funky-renames.sh
+++ b/t/t9115-git-svn-dcommit-funky-renames.sh
@@ -19,7 +19,7 @@
 	'
 
 test_expect_success 'create file in existing ugly and empty dir' '
-	mkdir "#{bad_directory_name}" &&
+	mkdir -p "#{bad_directory_name}" &&
 	echo hi > "#{bad_directory_name}/ foo" &&
 	git update-index --add "#{bad_directory_name}/ foo" &&
 	git commit -m "new file in ugly parent" &&
@@ -37,7 +37,7 @@
 	git update-index --add pretty &&
 	git commit -m "pretty :x" &&
 	git svn dcommit &&
-	mkdir regular_dir_name &&
+	mkdir -p regular_dir_name &&
 	git mv pretty regular_dir_name/pretty &&
 	git commit -m "moved pretty file" &&
 	git svn dcommit
@@ -61,11 +61,12 @@
 
 test_expect_success 'clone the repository to test rebase' '
 	git svn clone "$svnrepo" test-rebase &&
-	cd test-rebase &&
-		echo test-rebase > test-rebase &&
+	(
+		cd test-rebase &&
+		echo test-rebase >test-rebase &&
 		git add test-rebase &&
-		git commit -m test-rebase &&
-		cd ..
+		git commit -m test-rebase
+	)
 	'
 
 test_expect_success 'make a commit to test rebase' '
diff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh
index 0374a74..cf4c052 100755
--- a/t/t9116-git-svn-log.sh
+++ b/t/t9116-git-svn-log.sh
@@ -8,14 +8,16 @@
 
 test_expect_success 'setup repository and import' '
 	mkdir import &&
-	cd import &&
-		for i in trunk branches/a branches/b \
-		         tags/0.1 tags/0.2 tags/0.3; do
-			mkdir -p $i && \
-			echo hello >> $i/README || exit 1
-		done && \
+	(
+		cd import &&
+		for i in trunk branches/a branches/b tags/0.1 tags/0.2 tags/0.3
+		do
+			mkdir -p $i &&
+			echo hello >>$i/README ||
+			exit 1
+		done &&
 		svn_cmd import -m test . "$svnrepo"
-		cd .. &&
+	) &&
 	git svn init "$svnrepo" -T trunk -b branches -t tags &&
 	git svn fetch &&
 	git reset --hard trunk &&
@@ -58,6 +60,21 @@
 	git svn log -r 1:4 | grep '^r[0-9]' | cut -d'|' -f1 | test_cmp expected-range-r1-r2-r4 -
 	"
 
+test_expect_success 'test ascending revision range with --show-commit' "
+	git reset --hard trunk &&
+	git svn log --show-commit -r 1:4 | grep '^r[0-9]' | cut -d'|' -f1 | test_cmp expected-range-r1-r2-r4 -
+	"
+
+test_expect_success 'test ascending revision range with --show-commit (sha1)' "
+	git svn find-rev r1 >expected-range-r1-r2-r4-sha1 &&
+	git svn find-rev r2 >>expected-range-r1-r2-r4-sha1 &&
+	git svn find-rev r4 >>expected-range-r1-r2-r4-sha1 &&
+	git reset --hard trunk &&
+	git svn log --show-commit -r 1:4 | grep '^r[0-9]' | cut -d'|' -f2 >out &&
+	git rev-parse \$(cat out) >actual &&
+	test_cmp expected-range-r1-r2-r4-sha1 actual
+	"
+
 printf 'r4 \nr2 \nr1 \n' > expected-range-r4-r2-r1
 
 test_expect_success 'test descending revision range' "
diff --git a/t/t9118-git-svn-funky-branch-names.sh b/t/t9118-git-svn-funky-branch-names.sh
index ac52bff..63fc982 100755
--- a/t/t9118-git-svn-funky-branch-names.sh
+++ b/t/t9118-git-svn-funky-branch-names.sh
@@ -21,34 +21,59 @@
 	                      "$svnrepo/pr ject/branches/more fun plugin!" &&
 	svn_cmd cp -m "scary" "$svnrepo/pr ject/branches/fun plugin" \
 	              "$svnrepo/pr ject/branches/$scary_uri" &&
+	svn_cmd cp -m "leading dot" "$svnrepo/pr ject/trunk" \
+			"$svnrepo/pr ject/branches/.leading_dot" &&
+	svn_cmd cp -m "trailing dot" "$svnrepo/pr ject/trunk" \
+			"$svnrepo/pr ject/branches/trailing_dot." &&
+	svn_cmd cp -m "trailing .lock" "$svnrepo/pr ject/trunk" \
+			"$svnrepo/pr ject/branches/trailing_dotlock.lock" &&
+	svn_cmd cp -m "reflog" "$svnrepo/pr ject/trunk" \
+			"$svnrepo/pr ject/branches/not-a%40{0}reflog" &&
 	start_httpd
 	'
 
 test_expect_success 'test clone with funky branch names' '
 	git svn clone -s "$svnrepo/pr ject" project &&
-	cd project &&
+	(
+		cd project &&
 		git rev-parse "refs/remotes/fun%20plugin" &&
 		git rev-parse "refs/remotes/more%20fun%20plugin!" &&
 		git rev-parse "refs/remotes/$scary_ref" &&
-	cd ..
+		git rev-parse "refs/remotes/%2Eleading_dot" &&
+		git rev-parse "refs/remotes/trailing_dot%2E" &&
+		git rev-parse "refs/remotes/trailing_dotlock%2Elock" &&
+		git rev-parse "refs/remotes/not-a%40{0}reflog"
+	)
 	'
 
 test_expect_success 'test dcommit to funky branch' "
-	cd project &&
-	git reset --hard 'refs/remotes/more%20fun%20plugin!' &&
-	echo hello >> foo &&
-	git commit -m 'hello' -- foo &&
-	git svn dcommit &&
-	cd ..
+	(
+		cd project &&
+		git reset --hard 'refs/remotes/more%20fun%20plugin!' &&
+		echo hello >> foo &&
+		git commit -m 'hello' -- foo &&
+		git svn dcommit
+	)
 	"
 
 test_expect_success 'test dcommit to scary branch' '
-	cd project &&
-	git reset --hard "refs/remotes/$scary_ref" &&
-	echo urls are scary >> foo &&
-	git commit -m "eep" -- foo &&
-	git svn dcommit &&
-	cd ..
+	(
+		cd project &&
+		git reset --hard "refs/remotes/$scary_ref" &&
+		echo urls are scary >> foo &&
+		git commit -m "eep" -- foo &&
+		git svn dcommit
+	)
+	'
+
+test_expect_success 'test dcommit to trailing_dotlock branch' '
+	(
+		cd project &&
+		git reset --hard "refs/remotes/trailing_dotlock%2Elock" &&
+		echo who names branches like this anyway? >> foo &&
+		git commit -m "bar" -- foo &&
+		git svn dcommit
+	)
 	'
 
 stop_httpd
diff --git a/t/t9119-git-svn-info.sh b/t/t9119-git-svn-info.sh
index 95741cb..ff19695 100755
--- a/t/t9119-git-svn-info.sh
+++ b/t/t9119-git-svn-info.sh
@@ -7,68 +7,61 @@
 . ./lib-git-svn.sh
 
 # Tested with: svn, version 1.4.4 (r25188)
+# Tested with: svn, version 1.6.[12345689]
 v=`svn_cmd --version | sed -n -e 's/^svn, version \(1\.[0-9]*\.[0-9]*\).*$/\1/p'`
 case $v in
-1.[45].*)
+1.[456].*)
 	;;
 *)
-	say "skipping svn-info test (SVN version: $v not supported)"
+	skip_all="skipping svn-info test (SVN version: $v not supported)"
 	test_done
 	;;
 esac
 
-ptouch() {
-	perl -w -e '
-		use strict;
-		use POSIX qw(mktime);
-		die "ptouch requires exactly 2 arguments" if @ARGV != 2;
-		my $text_last_updated = shift @ARGV;
-		my $git_file = shift @ARGV;
-		die "\"$git_file\" does not exist" if ! -e $git_file;
-		if ($text_last_updated
-		    =~ /(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/) {
-			my $mtime = mktime($6, $5, $4, $3, $2 - 1, $1 - 1900);
-			my $atime = $mtime;
-			utime $atime, $mtime, $git_file;
-		}
-	' "`svn_cmd info $2 | grep '^Text Last Updated:'`" "$1"
+# On the "Text Last Updated" line, "git svn info" does not return the
+# same value as "svn info" (i.e. the commit timestamp that touched the
+# path most recently); do not expect that field to match.
+test_cmp_info () {
+	sed -e '/^Text Last Updated:/d' "$1" >tmp.expect
+	sed -e '/^Text Last Updated:/d' "$2" >tmp.actual
+	test_cmp tmp.expect tmp.actual &&
+	rm -f tmp.expect tmp.actual
 }
 
 quoted_svnrepo="$(echo $svnrepo | sed 's/ /%20/')"
 
 test_expect_success 'setup repository and import' '
 	mkdir info &&
-	cd info &&
-		echo FIRST > A &&
-		echo one > file &&
+	(
+		cd info &&
+		echo FIRST >A &&
+		echo one >file &&
 		ln -s file symlink-file &&
 		mkdir directory &&
 		touch directory/.placeholder &&
 		ln -s directory symlink-directory &&
-		svn_cmd import -m "initial" . "$svnrepo" &&
-	cd .. &&
+		svn_cmd import -m "initial" . "$svnrepo"
+	) &&
 	svn_cmd co "$svnrepo" svnwc &&
-	cd svnwc &&
-		echo foo > foo &&
+	(
+		cd svnwc &&
+		echo foo >foo &&
 		svn_cmd add foo &&
 		svn_cmd commit -m "change outside directory" &&
-		svn_cmd update &&
-	cd .. &&
+		svn_cmd update
+	) &&
 	mkdir gitwc &&
-	cd gitwc &&
+	(
+		cd gitwc &&
 		git svn init "$svnrepo" &&
-		git svn fetch &&
-	cd .. &&
-	ptouch gitwc/file svnwc/file &&
-	ptouch gitwc/directory svnwc/directory &&
-	ptouch gitwc/symlink-file svnwc/symlink-file &&
-	ptouch gitwc/symlink-directory svnwc/symlink-directory
+		git svn fetch
+	)
 	'
 
 test_expect_success 'info' "
 	(cd svnwc; svn info) > expected.info &&
 	(cd gitwc; git svn info) > actual.info &&
-	test_cmp expected.info actual.info
+	test_cmp_info expected.info actual.info
 	"
 
 test_expect_success 'info --url' '
@@ -78,7 +71,7 @@
 test_expect_success 'info .' "
 	(cd svnwc; svn info .) > expected.info-dot &&
 	(cd gitwc; git svn info .) > actual.info-dot &&
-	test_cmp expected.info-dot actual.info-dot
+	test_cmp_info expected.info-dot actual.info-dot
 	"
 
 test_expect_success 'info --url .' '
@@ -88,7 +81,7 @@
 test_expect_success 'info file' "
 	(cd svnwc; svn info file) > expected.info-file &&
 	(cd gitwc; git svn info file) > actual.info-file &&
-	test_cmp expected.info-file actual.info-file
+	test_cmp_info expected.info-file actual.info-file
 	"
 
 test_expect_success 'info --url file' '
@@ -98,13 +91,13 @@
 test_expect_success 'info directory' "
 	(cd svnwc; svn info directory) > expected.info-directory &&
 	(cd gitwc; git svn info directory) > actual.info-directory &&
-	test_cmp expected.info-directory actual.info-directory
+	test_cmp_info expected.info-directory actual.info-directory
 	"
 
 test_expect_success 'info inside directory' "
 	(cd svnwc/directory; svn info) > expected.info-inside-directory &&
 	(cd gitwc/directory; git svn info) > actual.info-inside-directory &&
-	test_cmp expected.info-inside-directory actual.info-inside-directory
+	test_cmp_info expected.info-inside-directory actual.info-inside-directory
 	"
 
 test_expect_success 'info --url directory' '
@@ -114,7 +107,7 @@
 test_expect_success 'info symlink-file' "
 	(cd svnwc; svn info symlink-file) > expected.info-symlink-file &&
 	(cd gitwc; git svn info symlink-file) > actual.info-symlink-file &&
-	test_cmp expected.info-symlink-file actual.info-symlink-file
+	test_cmp_info expected.info-symlink-file actual.info-symlink-file
 	"
 
 test_expect_success 'info --url symlink-file' '
@@ -127,7 +120,7 @@
 		> expected.info-symlink-directory &&
 	(cd gitwc; git svn info symlink-directory) \
 		> actual.info-symlink-directory &&
-	test_cmp expected.info-symlink-directory actual.info-symlink-directory
+	test_cmp_info expected.info-symlink-directory actual.info-symlink-directory
 	"
 
 test_expect_success 'info --url symlink-directory' '
@@ -137,17 +130,18 @@
 
 test_expect_success 'info added-file' "
 	echo two > gitwc/added-file &&
-	cd gitwc &&
-		git add added-file &&
-	cd .. &&
+	(
+		cd gitwc &&
+		git add added-file
+	) &&
 	cp gitwc/added-file svnwc/added-file &&
-	ptouch gitwc/added-file svnwc/added-file &&
-	cd svnwc &&
-		svn_cmd add added-file > /dev/null &&
-	cd .. &&
+	(
+		cd svnwc &&
+		svn_cmd add added-file > /dev/null
+	) &&
 	(cd svnwc; svn info added-file) > expected.info-added-file &&
 	(cd gitwc; git svn info added-file) > actual.info-added-file &&
-	test_cmp expected.info-added-file actual.info-added-file
+	test_cmp_info expected.info-added-file actual.info-added-file
 	"
 
 test_expect_success 'info --url added-file' '
@@ -157,19 +151,20 @@
 
 test_expect_success 'info added-directory' "
 	mkdir gitwc/added-directory svnwc/added-directory &&
-	ptouch gitwc/added-directory svnwc/added-directory &&
 	touch gitwc/added-directory/.placeholder &&
-	cd svnwc &&
-		svn_cmd add added-directory > /dev/null &&
-	cd .. &&
-	cd gitwc &&
-		git add added-directory &&
-	cd .. &&
+	(
+		cd svnwc &&
+		svn_cmd add added-directory > /dev/null
+	) &&
+	(
+		cd gitwc &&
+		git add added-directory
+	) &&
 	(cd svnwc; svn info added-directory) \
 		> expected.info-added-directory &&
 	(cd gitwc; git svn info added-directory) \
 		> actual.info-added-directory &&
-	test_cmp expected.info-added-directory actual.info-added-directory
+	test_cmp_info expected.info-added-directory actual.info-added-directory
 	"
 
 test_expect_success 'info --url added-directory' '
@@ -178,21 +173,22 @@
 	'
 
 test_expect_success 'info added-symlink-file' "
-	cd gitwc &&
+	(
+		cd gitwc &&
 		ln -s added-file added-symlink-file &&
-		git add added-symlink-file &&
-	cd .. &&
-	cd svnwc &&
+		git add added-symlink-file
+	) &&
+	(
+		cd svnwc &&
 		ln -s added-file added-symlink-file &&
-		svn_cmd add added-symlink-file > /dev/null &&
-	cd .. &&
-	ptouch gitwc/added-symlink-file svnwc/added-symlink-file &&
+		svn_cmd add added-symlink-file > /dev/null
+	) &&
 	(cd svnwc; svn info added-symlink-file) \
 		> expected.info-added-symlink-file &&
 	(cd gitwc; git svn info added-symlink-file) \
 		> actual.info-added-symlink-file &&
-	test_cmp expected.info-added-symlink-file \
-		 actual.info-added-symlink-file
+	test_cmp_info expected.info-added-symlink-file \
+		actual.info-added-symlink-file
 	"
 
 test_expect_success 'info --url added-symlink-file' '
@@ -201,21 +197,22 @@
 	'
 
 test_expect_success 'info added-symlink-directory' "
-	cd gitwc &&
+	(
+		cd gitwc &&
 		ln -s added-directory added-symlink-directory &&
-		git add added-symlink-directory &&
-	cd .. &&
-	cd svnwc &&
+		git add added-symlink-directory
+	) &&
+	(
+		cd svnwc &&
 		ln -s added-directory added-symlink-directory &&
-		svn_cmd add added-symlink-directory > /dev/null &&
-	cd .. &&
-	ptouch gitwc/added-symlink-directory svnwc/added-symlink-directory &&
+		svn_cmd add added-symlink-directory > /dev/null
+	) &&
 	(cd svnwc; svn info added-symlink-directory) \
 		> expected.info-added-symlink-directory &&
 	(cd gitwc; git svn info added-symlink-directory) \
 		> actual.info-added-symlink-directory &&
-	test_cmp expected.info-added-symlink-directory \
-		 actual.info-added-symlink-directory
+	test_cmp_info expected.info-added-symlink-directory \
+		actual.info-added-symlink-directory
 	"
 
 test_expect_success 'info --url added-symlink-directory' '
@@ -223,25 +220,18 @@
 	     = "$quoted_svnrepo/added-symlink-directory"
 	'
 
-# The next few tests replace the "Text Last Updated" value with a
-# placeholder since git doesn't have a way to know the date that a
-# now-deleted file was last checked out locally.  Internally it
-# simply reuses the Last Changed Date.
-
 test_expect_success 'info deleted-file' "
-	cd gitwc &&
-		git rm -f file > /dev/null &&
-	cd .. &&
-	cd svnwc &&
-		svn_cmd rm --force file > /dev/null &&
-	cd .. &&
-	(cd svnwc; svn info file) |
-	sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
-		> expected.info-deleted-file &&
-	(cd gitwc; git svn info file) |
-	sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
-		> actual.info-deleted-file &&
-	test_cmp expected.info-deleted-file actual.info-deleted-file
+	(
+		cd gitwc &&
+		git rm -f file > /dev/null
+	) &&
+	(
+		cd svnwc &&
+		svn_cmd rm --force file > /dev/null
+	) &&
+	(cd svnwc; svn info file) >expected.info-deleted-file &&
+	(cd gitwc; git svn info file) >actual.info-deleted-file &&
+	test_cmp_info expected.info-deleted-file actual.info-deleted-file
 	"
 
 test_expect_success 'info --url file (deleted)' '
@@ -250,19 +240,17 @@
 	'
 
 test_expect_success 'info deleted-directory' "
-	cd gitwc &&
-		git rm -r -f directory > /dev/null &&
-	cd .. &&
-	cd svnwc &&
-		svn_cmd rm --force directory > /dev/null &&
-	cd .. &&
-	(cd svnwc; svn info directory) |
-	sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
-		> expected.info-deleted-directory &&
-	(cd gitwc; git svn info directory) |
-	sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
-		> actual.info-deleted-directory &&
-	test_cmp expected.info-deleted-directory actual.info-deleted-directory
+	(
+		cd gitwc &&
+		git rm -r -f directory > /dev/null
+	) &&
+	(
+		cd svnwc &&
+		svn_cmd rm --force directory > /dev/null
+	) &&
+	(cd svnwc; svn info directory) >expected.info-deleted-directory &&
+	(cd gitwc; git svn info directory) >actual.info-deleted-directory &&
+	test_cmp_info expected.info-deleted-directory actual.info-deleted-directory
 	"
 
 test_expect_success 'info --url directory (deleted)' '
@@ -271,20 +259,17 @@
 	'
 
 test_expect_success 'info deleted-symlink-file' "
-	cd gitwc &&
-		git rm -f symlink-file > /dev/null &&
-	cd .. &&
-	cd svnwc &&
-		svn_cmd rm --force symlink-file > /dev/null &&
-	cd .. &&
-	(cd svnwc; svn info symlink-file) |
-	sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
-		> expected.info-deleted-symlink-file &&
-	(cd gitwc; git svn info symlink-file) |
-	sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
-		> actual.info-deleted-symlink-file &&
-	test_cmp expected.info-deleted-symlink-file \
-		 actual.info-deleted-symlink-file
+	(
+		cd gitwc &&
+		git rm -f symlink-file > /dev/null
+	) &&
+	(
+		cd svnwc &&
+		svn_cmd rm --force symlink-file > /dev/null
+	) &&
+	(cd svnwc; svn info symlink-file) >expected.info-deleted-symlink-file &&
+	(cd gitwc; git svn info symlink-file) >actual.info-deleted-symlink-file &&
+	test_cmp_info expected.info-deleted-symlink-file actual.info-deleted-symlink-file
 	"
 
 test_expect_success 'info --url symlink-file (deleted)' '
@@ -293,20 +278,17 @@
 	'
 
 test_expect_success 'info deleted-symlink-directory' "
-	cd gitwc &&
-		git rm -f symlink-directory > /dev/null &&
-	cd .. &&
-	cd svnwc &&
-		svn_cmd rm --force symlink-directory > /dev/null &&
-	cd .. &&
-	(cd svnwc; svn info symlink-directory) |
-	sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
-		 > expected.info-deleted-symlink-directory &&
-	(cd gitwc; git svn info symlink-directory) |
-	sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
-		 > actual.info-deleted-symlink-directory &&
-	test_cmp expected.info-deleted-symlink-directory \
-		 actual.info-deleted-symlink-directory
+	(
+		cd gitwc &&
+		git rm -f symlink-directory > /dev/null
+	) &&
+	(
+		cd svnwc &&
+		svn_cmd rm --force symlink-directory > /dev/null
+	) &&
+	(cd svnwc; svn info symlink-directory) >expected.info-deleted-symlink-directory &&
+	(cd gitwc; git svn info symlink-directory) >actual.info-deleted-symlink-directory &&
+	test_cmp_info expected.info-deleted-symlink-directory actual.info-deleted-symlink-directory
 	"
 
 test_expect_success 'info --url symlink-directory (deleted)' '
@@ -345,9 +327,10 @@
 	'
 
 test_expect_success 'info unknown-symlink-file' "
-	cd gitwc &&
-		ln -s unknown-file unknown-symlink-file &&
-	cd .. &&
+	(
+		cd gitwc &&
+		ln -s unknown-file unknown-symlink-file
+	) &&
 	(cd gitwc; test_must_fail git svn info unknown-symlink-file) \
 		 2> actual.info-unknown-symlink-file &&
 	grep unknown-symlink-file actual.info-unknown-symlink-file
@@ -360,9 +343,10 @@
 	'
 
 test_expect_success 'info unknown-symlink-directory' "
-	cd gitwc &&
-		ln -s unknown-directory unknown-symlink-directory &&
-	cd .. &&
+	(
+		cd gitwc &&
+		ln -s unknown-directory unknown-symlink-directory
+	) &&
 	(cd gitwc; test_must_fail git svn info unknown-symlink-directory) \
 		 2> actual.info-unknown-symlink-directory &&
 	grep unknown-symlink-directory actual.info-unknown-symlink-directory
diff --git a/t/t9120-git-svn-clone-with-percent-escapes.sh b/t/t9120-git-svn-clone-with-percent-escapes.sh
index f159ab6..1d92c05 100755
--- a/t/t9120-git-svn-clone-with-percent-escapes.sh
+++ b/t/t9120-git-svn-clone-with-percent-escapes.sh
@@ -10,15 +10,68 @@
 	mkdir project project/trunk project/branches project/tags &&
 	echo foo > project/trunk/foo &&
 	svn_cmd import -m "$test_description" project "$svnrepo/pr ject" &&
+	svn_cmd cp -m "branch" "$svnrepo/pr ject/trunk" \
+	  "$svnrepo/pr ject/branches/b" &&
+	svn_cmd cp -m "tag" "$svnrepo/pr ject/trunk" \
+	  "$svnrepo/pr ject/tags/v1" &&
 	rm -rf project &&
 	start_httpd
 '
 
 test_expect_success 'test clone with percent escapes' '
 	git svn clone "$svnrepo/pr%20ject" clone &&
-	cd clone &&
-		git rev-parse refs/${remotes_git_svn} &&
-	cd ..
+	(
+		cd clone &&
+		git rev-parse refs/${remotes_git_svn}
+	)
+'
+
+# SVN works either way, so should we...
+
+test_expect_success 'svn checkout with percent escapes' '
+	svn_cmd checkout "$svnrepo/pr%20ject" svn.percent &&
+	svn_cmd checkout "$svnrepo/pr%20ject/trunk" svn.percent.trunk
+'
+
+test_expect_success 'svn checkout with space' '
+	svn_cmd checkout "$svnrepo/pr ject" svn.space &&
+	svn_cmd checkout "$svnrepo/pr ject/trunk" svn.space.trunk
+'
+
+test_expect_success 'test clone trunk with percent escapes and minimize-url' '
+	git svn clone --minimize-url "$svnrepo/pr%20ject/trunk" minimize &&
+	(
+		cd minimize &&
+		git rev-parse refs/${remotes_git_svn}
+	)
+'
+
+test_expect_success 'test clone trunk with percent escapes' '
+	git svn clone "$svnrepo/pr%20ject/trunk" trunk &&
+	(
+		cd trunk &&
+		git rev-parse refs/${remotes_git_svn}
+	)
+'
+
+test_expect_success 'test clone --stdlayout with percent escapes' '
+	git svn clone --stdlayout "$svnrepo/pr%20ject" percent &&
+	(
+		cd percent &&
+		git rev-parse refs/remotes/trunk^0 &&
+		git rev-parse refs/remotes/b^0 &&
+		git rev-parse refs/remotes/tags/v1^0
+	)
+'
+
+test_expect_success 'test clone -s with unescaped space' '
+	git svn clone -s "$svnrepo/pr ject" space &&
+	(
+		cd space &&
+		git rev-parse refs/remotes/trunk^0 &&
+		git rev-parse refs/remotes/b^0 &&
+		git rev-parse refs/remotes/tags/v1^0
+	)
 '
 
 stop_httpd
diff --git a/t/t9123-git-svn-rebuild-with-rewriteroot.sh b/t/t9123-git-svn-rebuild-with-rewriteroot.sh
index 0455216..fd81847 100755
--- a/t/t9123-git-svn-rebuild-with-rewriteroot.sh
+++ b/t/t9123-git-svn-rebuild-with-rewriteroot.sh
@@ -8,15 +8,15 @@
 . ./lib-git-svn.sh
 
 mkdir import
-cd import
+(cd import
 	touch foo
 	svn_cmd import -m 'import for git svn' . "$svnrepo" >/dev/null
-cd ..
+)
 rm -rf import
 
 test_expect_success 'init, fetch and checkout repository' '
 	git svn init --rewrite-root=http://invalid.invalid/ "$svnrepo" &&
-	git svn fetch
+	git svn fetch &&
 	git checkout -b mybranch ${remotes_git_svn}
 	'
 
diff --git a/t/t9124-git-svn-dcommit-auto-props.sh b/t/t9124-git-svn-dcommit-auto-props.sh
index d6b076f..aa841e1 100755
--- a/t/t9124-git-svn-dcommit-auto-props.sh
+++ b/t/t9124-git-svn-dcommit-auto-props.sh
@@ -24,7 +24,7 @@
 		svn_cmd import -m "import for git svn" . "$svnrepo"
 	) &&
 	rm -rf import &&
-	git svn init "$svnrepo"
+	git svn init "$svnrepo" &&
 	git svn fetch
 '
 
diff --git a/t/t9125-git-svn-multi-glob-branch-names.sh b/t/t9125-git-svn-multi-glob-branch-names.sh
index c194186..096abd1 100755
--- a/t/t9125-git-svn-multi-glob-branch-names.sh
+++ b/t/t9125-git-svn-multi-glob-branch-names.sh
@@ -19,19 +19,19 @@
 test_expect_success 'test clone with multi-glob in branch names' '
 	git svn clone -T trunk -b branches/*/* -t tags \
 	              "$svnrepo/project" project &&
-	cd project &&
+	(cd project &&
 		git rev-parse "refs/remotes/v14.1/beta" &&
-		git rev-parse "refs/remotes/v14.1/gold" &&
-	cd ..
+		git rev-parse "refs/remotes/v14.1/gold"
+	)
 	'
 
 test_expect_success 'test dcommit to multi-globbed branch' "
-	cd project &&
+	(cd project &&
 	git reset --hard 'refs/remotes/v14.1/gold' &&
 	echo hello >> foo &&
 	git commit -m 'hello' -- foo &&
-	git svn dcommit &&
-	cd ..
+	git svn dcommit
+	)
 	"
 
 test_done
diff --git a/t/t9127-git-svn-partial-rebuild.sh b/t/t9127-git-svn-partial-rebuild.sh
index 4aab8ec..2e4789d 100755
--- a/t/t9127-git-svn-partial-rebuild.sh
+++ b/t/t9127-git-svn-partial-rebuild.sh
@@ -9,27 +9,27 @@
 test_expect_success 'initialize svnrepo' '
 	mkdir import &&
 	(
-		cd import &&
+		(cd import &&
 		mkdir trunk branches tags &&
-		cd trunk &&
-		echo foo > foo &&
-		cd .. &&
+		(cd trunk &&
+		echo foo > foo
+		) &&
 		svn_cmd import -m "import for git-svn" . "$svnrepo" >/dev/null &&
 		svn_cmd copy "$svnrepo"/trunk "$svnrepo"/branches/a \
-			-m "created branch a" &&
-		cd .. &&
+			-m "created branch a"
+		) &&
 		rm -rf import &&
 		svn_cmd co "$svnrepo"/trunk trunk &&
-		cd trunk &&
+		(cd trunk &&
 		echo bar >> foo &&
-		svn_cmd ci -m "updated trunk" &&
-		cd .. &&
+		svn_cmd ci -m "updated trunk"
+		) &&
 		svn_cmd co "$svnrepo"/branches/a a &&
-		cd a &&
+		(cd a &&
 		echo baz >> a &&
 		svn_cmd add a &&
-		svn_cmd ci -m "updated a" &&
-		cd .. &&
+		svn_cmd ci -m "updated a"
+		) &&
 		git svn init --stdlayout "$svnrepo"
 	)
 '
@@ -41,11 +41,11 @@
 test_expect_success 'make full git mirror of SVN' '
 	mkdir mirror &&
 	(
-		cd mirror &&
+		(cd mirror &&
 		git init &&
 		git svn init --stdlayout "$svnrepo" &&
-		git svn fetch &&
-		cd ..
+		git svn fetch
+		)
 	)
 '
 
diff --git a/t/t9128-git-svn-cmd-branch.sh b/t/t9128-git-svn-cmd-branch.sh
index 807e494..4b034a6 100755
--- a/t/t9128-git-svn-cmd-branch.sh
+++ b/t/t9128-git-svn-cmd-branch.sh
@@ -9,19 +9,19 @@
 test_expect_success 'initialize svnrepo' '
 	mkdir import &&
 	(
-		cd import &&
+		(cd import &&
 		mkdir trunk branches tags &&
-		cd trunk &&
-		echo foo > foo &&
-		cd .. &&
-		svn_cmd import -m "import for git-svn" . "$svnrepo" >/dev/null &&
-		cd .. &&
+		(cd trunk &&
+		echo foo > foo
+		) &&
+		svn_cmd import -m "import for git-svn" . "$svnrepo" >/dev/null
+		) &&
 		rm -rf import &&
 		svn_cmd co "$svnrepo"/trunk trunk &&
-		cd trunk &&
+		(cd trunk &&
 		echo bar >> foo &&
-		svn_cmd ci -m "updated trunk" &&
-		cd .. &&
+		svn_cmd ci -m "updated trunk"
+		) &&
 		rm -rf trunk
 	)
 '
diff --git a/t/t9129-git-svn-i18n-commitencoding.sh b/t/t9129-git-svn-i18n-commitencoding.sh
index b9224bd..9a40f1e 100755
--- a/t/t9129-git-svn-i18n-commitencoding.sh
+++ b/t/t9129-git-svn-i18n-commitencoding.sh
@@ -14,10 +14,22 @@
 	test_cmp current "$1"
 }
 
+a_utf8_locale=$(locale -a | sed -n '/\.[uU][tT][fF]-*8$/{
+	p
+	q
+}')
+
+if test -n "$a_utf8_locale"
+then
+	test_set_prereq UTF8
+else
+	say "# UTF-8 locale not available, some tests are skipped"
+fi
+
 compare_svn_head_with () {
 	# extract just the log message and strip out committer info.
 	# don't use --limit here since svn 1.1.x doesn't have it,
-	LC_ALL=en_US.UTF-8 svn log `git svn info --url` | perl -w -e '
+	LC_ALL="$a_utf8_locale" svn log `git svn info --url` | "$PERL_PATH" -w -e '
 		use bytes;
 		$/ = ("-"x72) . "\n";
 		my @x = <STDIN>;
@@ -69,12 +81,6 @@
 	'
 done
 
-if locale -a |grep -q en_US.utf8; then
-	test_set_prereq UTF8
-else
-	say "UTF-8 locale not available, test skipped"
-fi
-
 test_expect_success UTF8 'ISO-8859-1 should match UTF-8 in svn' '
 	(
 		cd ISO8859-1 &&
diff --git a/t/t9130-git-svn-authors-file.sh b/t/t9130-git-svn-authors-file.sh
index f5abdb3..c3443ce 100755
--- a/t/t9130-git-svn-authors-file.sh
+++ b/t/t9130-git-svn-authors-file.sh
@@ -20,7 +20,7 @@
 	'
 
 test_expect_success 'start import with incomplete authors file' '
-	! git svn clone --authors-file=svn-authors "$svnrepo" x
+	test_must_fail git svn clone --authors-file=svn-authors "$svnrepo" x
 	'
 
 test_expect_success 'imported 2 revisions successfully' '
@@ -63,7 +63,7 @@
 	'
 
 test_expect_success 'fetch fails on ee' '
-	( cd aa-work && ! git svn fetch --authors-file=../svn-authors )
+	( cd aa-work && test_must_fail git svn fetch --authors-file=../svn-authors )
 	'
 
 tmp_config_get () {
@@ -91,4 +91,24 @@
 	)
 	'
 
+test_expect_success 'fresh clone with svn.authors-file in config' '
+	(
+		rm -r "$GIT_DIR" &&
+		test x = x"$(git config svn.authorsfile)" &&
+		test_config="$HOME"/.gitconfig &&
+		sane_unset GIT_DIR &&
+		sane_unset GIT_CONFIG &&
+		git config --global \
+		  svn.authorsfile "$HOME"/svn-authors &&
+		test x"$HOME"/svn-authors = x"$(git config svn.authorsfile)" &&
+		git svn clone "$svnrepo" gitconfig.clone &&
+		cd gitconfig.clone &&
+		nr_ex=$(git log | grep "^Author:.*example.com" | wc -l) &&
+		nr_rev=$(git rev-list HEAD | wc -l) &&
+		test $nr_rev -eq $nr_ex
+	)
+'
+
+test_debug 'GIT_DIR=gitconfig.clone/.git git log'
+
 test_done
diff --git a/t/t9131-git-svn-empty-symlink.sh b/t/t9131-git-svn-empty-symlink.sh
index 9a24a65..f762038 100755
--- a/t/t9131-git-svn-empty-symlink.sh
+++ b/t/t9131-git-svn-empty-symlink.sh
@@ -88,7 +88,7 @@
 test_expect_success '"bar" is an empty file' 'test -f x/bar && ! test -s x/bar'
 test_expect_success 'get "bar" => symlink fix from svn' \
 		'(cd x && git svn rebase)'
-test_expect_success SYMLINKS '"bar" becomes a symlink' 'test -L x/bar'
+test_expect_success SYMLINKS '"bar" becomes a symlink' 'test -h x/bar'
 
 
 test_expect_success 'clone using git svn' 'git svn clone -r1 "$svnrepo" y'
diff --git a/t/t9135-git-svn-moved-branch-empty-file.sh b/t/t9135-git-svn-moved-branch-empty-file.sh
index 03705fa..5280e5f 100755
--- a/t/t9135-git-svn-moved-branch-empty-file.sh
+++ b/t/t9135-git-svn-moved-branch-empty-file.sh
@@ -10,7 +10,12 @@
 test_expect_success 'clone using git svn' 'git svn clone -s "$svnrepo" x'
 
 test_expect_success 'test that b1 exists and is empty' '
-	(cd x && test -f b1 && ! test -s b1)
+	(
+		cd x &&
+		git reset --hard branch-c &&
+		test -f b1 &&
+		! test -s b1
+	)
 	'
 
 test_done
diff --git a/t/t9137-git-svn-dcommit-clobber-series.sh b/t/t9137-git-svn-dcommit-clobber-series.sh
index 636ca0a..c17aa31 100755
--- a/t/t9137-git-svn-dcommit-clobber-series.sh
+++ b/t/t9137-git-svn-dcommit-clobber-series.sh
@@ -6,10 +6,10 @@
 
 test_expect_success 'initialize repo' '
 	mkdir import &&
-	cd import &&
+	(cd import &&
 	awk "BEGIN { for (i = 1; i < 64; i++) { print i } }" > file
-	svn_cmd import -m "initial" . "$svnrepo" &&
-	cd .. &&
+	svn_cmd import -m "initial" . "$svnrepo"
+	) &&
 	git svn init "$svnrepo" &&
 	git svn fetch &&
 	test -e file
@@ -19,14 +19,14 @@
 	test x"`sed -n -e 58p < file`" = x58 &&
 	test x"`sed -n -e 61p < file`" = x61 &&
 	svn_cmd co "$svnrepo" tmp &&
-	cd tmp &&
-		perl -i.bak -p -e "s/^58$/5588/" file &&
-		perl -i.bak -p -e "s/^61$/6611/" file &&
+	(cd tmp &&
+		"$PERL_PATH" -i.bak -p -e "s/^58$/5588/" file &&
+		"$PERL_PATH" -i.bak -p -e "s/^61$/6611/" file &&
 		poke file &&
 		test x"`sed -n -e 58p < file`" = x5588 &&
 		test x"`sed -n -e 61p < file`" = x6611 &&
-		svn_cmd commit -m "58 => 5588, 61 => 6611" &&
-		cd ..
+		svn_cmd commit -m "58 => 5588, 61 => 6611"
+	)
 	'
 
 test_expect_success 'some unrelated changes to git' "
@@ -40,8 +40,8 @@
 test_expect_success 'change file but in unrelated area' "
 	test x\"\`sed -n -e 4p < file\`\" = x4 &&
 	test x\"\`sed -n -e 7p < file\`\" = x7 &&
-	perl -i.bak -p -e 's/^4\$/4444/' file &&
-	perl -i.bak -p -e 's/^7\$/7777/' file &&
+	"$PERL_PATH" -i.bak -p -e 's/^4\$/4444/' file &&
+	"$PERL_PATH" -i.bak -p -e 's/^7\$/7777/' file &&
 	test x\"\`sed -n -e 4p < file\`\" = x4444 &&
 	test x\"\`sed -n -e 7p < file\`\" = x7777 &&
 	git commit -m '4 => 4444, 7 => 7777' file &&
diff --git a/t/t9138-git-svn-authors-prog.sh b/t/t9138-git-svn-authors-prog.sh
index a4b00f2..83cc5fc 100755
--- a/t/t9138-git-svn-authors-prog.sh
+++ b/t/t9138-git-svn-authors-prog.sh
@@ -66,4 +66,18 @@
 	)
 	'
 
+git --git-dir=x/.git config --unset svn.authorsfile
+git --git-dir=x/.git config --unset svn.authorsprog
+
+test_expect_success 'authors-prog handled special characters in username' '
+	svn mkdir -m bad --username "xyz; touch evil" "$svnrepo"/bad &&
+	(
+		cd x &&
+		git svn --authors-prog=../svn-authors-prog fetch &&
+		git rev-list -1 --pretty=raw refs/remotes/git-svn |
+		grep "^author xyz; touch evil <xyz; touch evil@example\.com> " &&
+		! test -f evil
+	)
+'
+
 test_done
diff --git a/t/t9139-git-svn-non-utf8-commitencoding.sh b/t/t9139-git-svn-non-utf8-commitencoding.sh
index f337959..22d80b0 100755
--- a/t/t9139-git-svn-non-utf8-commitencoding.sh
+++ b/t/t9139-git-svn-non-utf8-commitencoding.sh
@@ -39,7 +39,7 @@
 	(
 		cd $H &&
 		git config --unset i18n.commitencoding &&
-		! git svn dcommit
+		test_must_fail git svn dcommit
 	)
 	'
 done
diff --git a/t/t9140-git-svn-reset.sh b/t/t9140-git-svn-reset.sh
index 0735526..e855904 100755
--- a/t/t9140-git-svn-reset.sh
+++ b/t/t9140-git-svn-reset.sh
@@ -41,7 +41,7 @@
 test_expect_success 'fetch fails on modified hidden file' '
 	( cd g &&
 	  git svn find-rev refs/remotes/git-svn > ../expect &&
-	  ! git svn fetch 2> ../errors &&
+	  test_must_fail git svn fetch 2> ../errors &&
 	  git svn find-rev refs/remotes/git-svn > ../expect2 ) &&
 	fgrep "not found in commit" errors &&
 	test_cmp expect expect2
diff --git a/t/t9142-git-svn-shallow-clone.sh b/t/t9142-git-svn-shallow-clone.sh
index 1236acc..e21ee5f 100755
--- a/t/t9142-git-svn-shallow-clone.sh
+++ b/t/t9142-git-svn-shallow-clone.sh
@@ -17,11 +17,10 @@
 		> foo &&
 		svn_cmd add foo &&
 		svn_cmd commit -m "add foo"
-	)
+	) &&
+	start_httpd
 '
 
-start_httpd
-
 test_expect_success 'clone trunk with "-r HEAD"' '
 	git svn clone -r HEAD "$svnrepo/trunk" g &&
 	( cd g && git rev-parse --symbolic --verify HEAD )
diff --git a/t/t9143-git-svn-gc.sh b/t/t9143-git-svn-gc.sh
index f2ba2d1..4594e1a 100755
--- a/t/t9143-git-svn-gc.sh
+++ b/t/t9143-git-svn-gc.sh
@@ -28,26 +28,24 @@
 test_expect_success 'Fetch repo' 'git svn fetch'
 
 test_expect_success 'make backup copy of unhandled.log' '
-	 cp .git/svn/git-svn/unhandled.log tmp
+	 cp .git/svn/refs/remotes/git-svn/unhandled.log tmp
 	'
 
-test_expect_success 'create leftover index' '> .git/svn/git-svn/index'
+test_expect_success 'create leftover index' '> .git/svn/refs/remotes/git-svn/index'
 
 test_expect_success 'git svn gc runs' 'git svn gc'
 
-test_expect_success 'git svn index removed' '! test -f .git/svn/git-svn/index'
+test_expect_success 'git svn index removed' '! test -f .git/svn/refs/remotes/git-svn/index'
 
-if perl -MCompress::Zlib -e 0 2>/dev/null
+if test -r .git/svn/refs/remotes/git-svn/unhandled.log.gz
 then
 	test_expect_success 'git svn gc produces a valid gzip file' '
-		 gunzip .git/svn/git-svn/unhandled.log.gz
+		 gunzip .git/svn/refs/remotes/git-svn/unhandled.log.gz
 		'
-else
-	say "Perl Compress::Zlib unavailable, skipping gunzip test"
 fi
 
 test_expect_success 'git svn gc does not change unhandled.log files' '
-	 test_cmp .git/svn/git-svn/unhandled.log tmp/unhandled.log
+	 test_cmp .git/svn/refs/remotes/git-svn/unhandled.log tmp/unhandled.log
 	'
 
 test_done
diff --git a/t/t9144-git-svn-old-rev_map.sh b/t/t9144-git-svn-old-rev_map.sh
new file mode 100755
index 0000000..7600a35
--- /dev/null
+++ b/t/t9144-git-svn-old-rev_map.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Eric Wong
+
+test_description='git svn old rev_map preservd'
+. ./lib-git-svn.sh
+
+test_expect_success 'setup test repository with old layout' '
+	mkdir i &&
+	(cd i && > a) &&
+	svn_cmd import -m- i "$svnrepo" &&
+	git svn init "$svnrepo" &&
+	git svn fetch &&
+	test -d .git/svn/refs/remotes/git-svn/ &&
+	! test -e .git/svn/git-svn/ &&
+	mv .git/svn/refs/remotes/git-svn .git/svn/ &&
+	rm -r .git/svn/refs
+'
+
+test_expect_success 'old layout continues to work' '
+	svn_cmd import -m- i "$svnrepo/b" &&
+	git svn rebase &&
+	echo a >> b/a &&
+	git add b/a &&
+	git commit -m- -a &&
+	git svn dcommit &&
+	! test -d .git/svn/refs/ &&
+	test -e .git/svn/git-svn/
+'
+
+test_done
diff --git a/t/t9145-git-svn-master-branch.sh b/t/t9145-git-svn-master-branch.sh
new file mode 100755
index 0000000..16852d2
--- /dev/null
+++ b/t/t9145-git-svn-master-branch.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Eric Wong
+#
+test_description='git svn initial master branch is "trunk" if possible'
+. ./lib-git-svn.sh
+
+test_expect_success 'setup test repository' '
+	mkdir i &&
+	> i/a &&
+	svn_cmd import -m trunk i "$svnrepo/trunk" &&
+	svn_cmd import -m b/a i "$svnrepo/branches/a" &&
+	svn_cmd import -m b/b i "$svnrepo/branches/b"
+'
+
+test_expect_success 'git svn clone --stdlayout sets up trunk as master' '
+	git svn clone -s "$svnrepo" g &&
+	(
+		cd g &&
+		test x`git rev-parse --verify refs/remotes/trunk^0` = \
+		     x`git rev-parse --verify refs/heads/master^0`
+	)
+'
+
+test_done
diff --git a/t/t9146-git-svn-empty-dirs.sh b/t/t9146-git-svn-empty-dirs.sh
new file mode 100755
index 0000000..6d3130e
--- /dev/null
+++ b/t/t9146-git-svn-empty-dirs.sh
@@ -0,0 +1,159 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Eric Wong
+
+test_description='git svn creates empty directories'
+. ./lib-git-svn.sh
+
+test_expect_success 'initialize repo' '
+	for i in a b c d d/e d/e/f "weird file name"
+	do
+		svn_cmd mkdir -m "mkdir $i" "$svnrepo"/"$i"
+	done
+'
+
+test_expect_success 'clone' 'git svn clone "$svnrepo" cloned'
+
+test_expect_success 'empty directories exist' '
+	(
+		cd cloned &&
+		for i in a b c d d/e d/e/f "weird file name"
+		do
+			if ! test -d "$i"
+			then
+				echo >&2 "$i does not exist"
+				exit 1
+			fi
+		done
+	)
+'
+
+test_expect_success 'option automkdirs set to false' '
+	(
+		git svn init "$svnrepo" cloned-no-mkdirs &&
+		cd cloned-no-mkdirs &&
+		git config svn-remote.svn.automkdirs false &&
+		git svn fetch &&
+		for i in a b c d d/e d/e/f "weird file name"
+		do
+			if test -d "$i"
+			then
+				echo >&2 "$i exists"
+				exit 1
+			fi
+		done
+	)
+'
+
+test_expect_success 'more emptiness' '
+	svn_cmd mkdir -m "bang bang"  "$svnrepo"/"! !"
+'
+
+test_expect_success 'git svn rebase creates empty directory' '
+	( cd cloned && git svn rebase ) &&
+	test -d cloned/"! !"
+'
+
+test_expect_success 'git svn mkdirs recreates empty directories' '
+	(
+		cd cloned &&
+		rm -r * &&
+		git svn mkdirs &&
+		for i in a b c d d/e d/e/f "weird file name" "! !"
+		do
+			if ! test -d "$i"
+			then
+				echo >&2 "$i does not exist"
+				exit 1
+			fi
+		done
+	)
+'
+
+test_expect_success 'git svn mkdirs -r works' '
+	(
+		cd cloned &&
+		rm -r * &&
+		git svn mkdirs -r7 &&
+		for i in a b c d d/e d/e/f "weird file name"
+		do
+			if ! test -d "$i"
+			then
+				echo >&2 "$i does not exist"
+				exit 1
+			fi
+		done
+
+		if test -d "! !"
+		then
+			echo >&2 "$i should not exist"
+			exit 1
+		fi
+
+		git svn mkdirs -r8 &&
+		if ! test -d "! !"
+		then
+			echo >&2 "$i not exist"
+			exit 1
+		fi
+	)
+'
+
+test_expect_success 'initialize trunk' '
+	for i in trunk trunk/a trunk/"weird file name"
+	do
+		svn_cmd mkdir -m "mkdir $i" "$svnrepo"/"$i"
+	done
+'
+
+test_expect_success 'clone trunk' 'git svn clone -s "$svnrepo" trunk'
+
+test_expect_success 'empty directories in trunk exist' '
+	(
+		cd trunk &&
+		for i in a "weird file name"
+		do
+			if ! test -d "$i"
+			then
+				echo >&2 "$i does not exist"
+				exit 1
+			fi
+		done
+	)
+'
+
+test_expect_success 'remove a top-level directory from svn' '
+	svn_cmd rm -m "remove d" "$svnrepo"/d
+'
+
+test_expect_success 'removed top-level directory does not exist' '
+	git svn clone "$svnrepo" removed &&
+	test ! -e removed/d
+
+'
+unhandled=.git/svn/refs/remotes/git-svn/unhandled.log
+test_expect_success 'git svn gc-ed files work' '
+	(
+		cd removed &&
+		git svn gc &&
+		: Compress::Zlib may not be available &&
+		if test -f "$unhandled".gz
+		then
+			svn_cmd mkdir -m gz "$svnrepo"/gz &&
+			git reset --hard $(git rev-list HEAD | tail -1) &&
+			git svn rebase &&
+			test -f "$unhandled".gz &&
+			test -f "$unhandled" &&
+			for i in a b c "weird file name" gz "! !"
+			do
+				if ! test -d "$i"
+				then
+					echo >&2 "$i does not exist"
+					exit 1
+				fi
+			done
+		fi
+	)
+'
+
+test_done
diff --git a/t/t9150-svk-mergetickets.sh b/t/t9150-svk-mergetickets.sh
new file mode 100755
index 0000000..24c2421
--- /dev/null
+++ b/t/t9150-svk-mergetickets.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Sam Vilain
+#
+
+test_description='git-svn svk merge tickets'
+
+. ./lib-git-svn.sh
+
+test_expect_success 'load svk depot' "
+	svnadmin load -q '$rawsvnrepo' \
+	  < '$TEST_DIRECTORY/t9150/svk-merge.dump' &&
+	git svn init --minimize-url -R svkmerge \
+	  --rewrite-root=http://svn.example.org \
+	  -T trunk -b branches '$svnrepo' &&
+	git svn fetch --all
+	"
+
+uuid=b48289b2-9c08-4d72-af37-0358a40b9c15
+
+test_expect_success 'svk merges were represented coming in' "
+	[ `git cat-file commit HEAD | grep parent | wc -l` -eq 2 ]
+	"
+
+test_done
diff --git a/t/t9150/make-svk-dump b/t/t9150/make-svk-dump
new file mode 100644
index 0000000..2242f14
--- /dev/null
+++ b/t/t9150/make-svk-dump
@@ -0,0 +1,57 @@
+#!/bin/sh
+#
+# this script sets up a Subversion repository for Makefile in the
+# first ever git merge, as if it were done with svk.
+#
+
+set -e
+
+svk depotmap foo ~/.svk/foo
+svk co /foo/ foo
+cd foo
+mkdir trunk
+mkdir branches
+svk add trunk branches
+svk commit -m "Setup trunk and branches"
+cd trunk
+
+git cat-file blob 6683463e:Makefile > Makefile
+svk add Makefile 
+
+svk commit -m "ancestor"
+cd ..
+svk cp trunk branches/left
+
+svk commit -m "make left branch"
+cd branches/left/
+
+git cat-file blob 5873b67e:Makefile > Makefile
+svk commit -m "left update 1"
+
+cd ../../trunk
+git cat-file blob 75118b13:Makefile > Makefile
+svk commit -m "trunk update"
+
+cd ../branches/left
+git cat-file blob b5039db6:Makefile > Makefile
+svk commit -m "left update 2"
+
+cd ../../trunk
+svk sm /foo/branches/left
+# in theory we could delete the "left" branch here, but it's not
+# required so don't do it, in case people start getting ideas ;)
+svk commit -m "merge branch 'left' into 'trunk'"
+
+git cat-file blob b51ad431:Makefile > Makefile
+
+svk diff Makefile && echo "Hey!  No differences, magic"
+
+cd ../..
+
+svnadmin dump ~/.svk/foo > svk-merge.dump
+
+svk co -d foo
+rm -rf foo
+svk depotmap -d /foo/
+rm -rf ~/.svk/foo
+
diff --git a/t/t9150/svk-merge.dump b/t/t9150/svk-merge.dump
new file mode 100644
index 0000000..42f70db
--- /dev/null
+++ b/t/t9150/svk-merge.dump
@@ -0,0 +1,616 @@
+SVN-fs-dump-format-version: 2
+
+UUID: b48289b2-9c08-4d72-af37-0358a40b9c15
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2009-10-19T23:44:03.722969Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 123
+Content-length: 123
+
+K 7
+svn:log
+V 24
+Setup trunk and branches
+K 10
+svn:author
+V 4
+samv
+K 8
+svn:date
+V 27
+2009-10-19T23:44:04.927533Z
+PROPS-END
+
+Node-path: branches
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: trunk
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Revision-number: 2
+Prop-content-length: 106
+Content-length: 106
+
+K 7
+svn:log
+V 8
+ancestor
+K 10
+svn:author
+V 4
+samv
+K 8
+svn:date
+V 27
+2009-10-19T23:44:05.835585Z
+PROPS-END
+
+Node-path: trunk/Makefile
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 2401
+Text-content-md5: bfd8ff778d1492dc6758567373176a89
+Content-length: 2411
+
+PROPS-END
+# -DCOLLISION_CHECK if you believe that SHA1's
+# 1461501637330902918203684832716283019655932542976 hashes do not give you
+# enough guarantees about no collisions between objects ever hapenning.
+#
+# -DNSEC if you want git to care about sub-second file mtimes and ctimes.
+# Note that you need some new 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).
+CFLAGS=-g -O3 -Wall
+
+CC=gcc
+
+
+PROG=   update-cache show-diff init-db write-tree read-tree commit-tree \
+	cat-file fsck-cache checkout-cache diff-tree rev-tree show-files \
+	check-files ls-tree merge-base
+
+all: $(PROG)
+
+install: $(PROG)
+	install $(PROG) $(HOME)/bin/
+
+LIBS= -lssl -lz
+
+init-db: init-db.o
+
+update-cache: update-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o update-cache update-cache.o read-cache.o $(LIBS)
+
+show-diff: show-diff.o read-cache.o
+	$(CC) $(CFLAGS) -o show-diff show-diff.o read-cache.o $(LIBS)
+
+write-tree: write-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o write-tree write-tree.o read-cache.o $(LIBS)
+
+read-tree: read-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o read-tree read-tree.o read-cache.o $(LIBS)
+
+commit-tree: commit-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o commit-tree commit-tree.o read-cache.o $(LIBS)
+
+cat-file: cat-file.o read-cache.o
+	$(CC) $(CFLAGS) -o cat-file cat-file.o read-cache.o $(LIBS)
+
+fsck-cache: fsck-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o fsck-cache fsck-cache.o read-cache.o $(LIBS)
+
+checkout-cache: checkout-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o checkout-cache checkout-cache.o read-cache.o $(LIBS)
+
+diff-tree: diff-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o diff-tree diff-tree.o read-cache.o $(LIBS)
+
+rev-tree: rev-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o rev-tree rev-tree.o read-cache.o $(LIBS)
+
+show-files: show-files.o read-cache.o
+	$(CC) $(CFLAGS) -o show-files show-files.o read-cache.o $(LIBS)
+
+check-files: check-files.o read-cache.o
+	$(CC) $(CFLAGS) -o check-files check-files.o read-cache.o $(LIBS)
+
+ls-tree: ls-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o ls-tree ls-tree.o read-cache.o $(LIBS)
+
+merge-base: merge-base.o read-cache.o
+	$(CC) $(CFLAGS) -o merge-base merge-base.o read-cache.o $(LIBS)
+
+read-cache.o: cache.h
+show-diff.o: cache.h
+
+clean:
+	rm -f *.o $(PROG)
+
+backup: clean
+	cd .. ; tar czvf dircache.tar.gz dir-cache
+
+
+Revision-number: 3
+Prop-content-length: 115
+Content-length: 115
+
+K 7
+svn:log
+V 16
+make left branch
+K 10
+svn:author
+V 4
+samv
+K 8
+svn:date
+V 27
+2009-10-19T23:44:06.719737Z
+PROPS-END
+
+Node-path: branches/left
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 2
+Node-copyfrom-path: trunk
+
+
+Revision-number: 4
+Prop-content-length: 112
+Content-length: 112
+
+K 7
+svn:log
+V 13
+left update 1
+K 10
+svn:author
+V 4
+samv
+K 8
+svn:date
+V 27
+2009-10-19T23:44:07.167666Z
+PROPS-END
+
+Node-path: branches/left/Makefile
+Node-kind: file
+Node-action: change
+Text-content-length: 2465
+Text-content-md5: 16e38d9753b061731650561ce01b1195
+Content-length: 2465
+
+# -DCOLLISION_CHECK if you believe that SHA1's
+# 1461501637330902918203684832716283019655932542976 hashes do not give you
+# enough guarantees about no collisions between objects ever hapenning.
+#
+# -DNSEC if you want git to care about sub-second file mtimes and ctimes.
+# Note that you need some new 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).
+CFLAGS=-g -O3 -Wall
+
+CC=gcc
+
+
+PROG=   update-cache show-diff init-db write-tree read-tree commit-tree \
+	cat-file fsck-cache checkout-cache diff-tree rev-tree show-files \
+	check-files ls-tree merge-base
+
+all: $(PROG)
+
+install: $(PROG)
+	install $(PROG) $(HOME)/bin/
+
+LIBS= -lssl -lz
+
+init-db: init-db.o
+
+update-cache: update-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o update-cache update-cache.o read-cache.o $(LIBS)
+
+show-diff: show-diff.o read-cache.o
+	$(CC) $(CFLAGS) -o show-diff show-diff.o read-cache.o $(LIBS)
+
+write-tree: write-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o write-tree write-tree.o read-cache.o $(LIBS)
+
+read-tree: read-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o read-tree read-tree.o read-cache.o $(LIBS)
+
+commit-tree: commit-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o commit-tree commit-tree.o read-cache.o $(LIBS)
+
+cat-file: cat-file.o read-cache.o
+	$(CC) $(CFLAGS) -o cat-file cat-file.o read-cache.o $(LIBS)
+
+fsck-cache: fsck-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o fsck-cache fsck-cache.o read-cache.o $(LIBS)
+
+checkout-cache: checkout-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o checkout-cache checkout-cache.o read-cache.o $(LIBS)
+
+diff-tree: diff-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o diff-tree diff-tree.o read-cache.o $(LIBS)
+
+rev-tree: rev-tree.o read-cache.o object.o commit.o tree.o blob.o
+	$(CC) $(CFLAGS) -o rev-tree rev-tree.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+show-files: show-files.o read-cache.o
+	$(CC) $(CFLAGS) -o show-files show-files.o read-cache.o $(LIBS)
+
+check-files: check-files.o read-cache.o
+	$(CC) $(CFLAGS) -o check-files check-files.o read-cache.o $(LIBS)
+
+ls-tree: ls-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o ls-tree ls-tree.o read-cache.o $(LIBS)
+
+merge-base: merge-base.o read-cache.o
+	$(CC) $(CFLAGS) -o merge-base merge-base.o read-cache.o $(LIBS)
+
+read-cache.o: cache.h
+show-diff.o: cache.h
+
+clean:
+	rm -f *.o $(PROG)
+
+backup: clean
+	cd .. ; tar czvf dircache.tar.gz dir-cache
+
+
+Revision-number: 5
+Prop-content-length: 111
+Content-length: 111
+
+K 7
+svn:log
+V 12
+trunk update
+K 10
+svn:author
+V 4
+samv
+K 8
+svn:date
+V 27
+2009-10-19T23:44:07.619633Z
+PROPS-END
+
+Node-path: trunk/Makefile
+Node-kind: file
+Node-action: change
+Text-content-length: 2521
+Text-content-md5: 0668418a621333f4aa8b6632cd63e2a0
+Content-length: 2521
+
+# -DCOLLISION_CHECK if you believe that SHA1's
+# 1461501637330902918203684832716283019655932542976 hashes do not give you
+# enough guarantees about no collisions between objects ever hapenning.
+#
+# -DNSEC if you want git to care about sub-second file mtimes and ctimes.
+# Note that you need some new 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).
+CFLAGS=-g -O3 -Wall
+
+CC=gcc
+
+
+PROG=   update-cache show-diff init-db write-tree read-tree commit-tree \
+	cat-file fsck-cache checkout-cache diff-tree rev-tree show-files \
+	check-files ls-tree merge-base merge-cache
+
+all: $(PROG)
+
+install: $(PROG)
+	install $(PROG) $(HOME)/bin/
+
+LIBS= -lssl -lz
+
+init-db: init-db.o
+
+update-cache: update-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o update-cache update-cache.o read-cache.o $(LIBS)
+
+show-diff: show-diff.o read-cache.o
+	$(CC) $(CFLAGS) -o show-diff show-diff.o read-cache.o $(LIBS)
+
+write-tree: write-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o write-tree write-tree.o read-cache.o $(LIBS)
+
+read-tree: read-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o read-tree read-tree.o read-cache.o $(LIBS)
+
+commit-tree: commit-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o commit-tree commit-tree.o read-cache.o $(LIBS)
+
+cat-file: cat-file.o read-cache.o
+	$(CC) $(CFLAGS) -o cat-file cat-file.o read-cache.o $(LIBS)
+
+fsck-cache: fsck-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o fsck-cache fsck-cache.o read-cache.o $(LIBS)
+
+checkout-cache: checkout-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o checkout-cache checkout-cache.o read-cache.o $(LIBS)
+
+diff-tree: diff-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o diff-tree diff-tree.o read-cache.o $(LIBS)
+
+rev-tree: rev-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o rev-tree rev-tree.o read-cache.o $(LIBS)
+
+show-files: show-files.o read-cache.o
+	$(CC) $(CFLAGS) -o show-files show-files.o read-cache.o $(LIBS)
+
+check-files: check-files.o read-cache.o
+	$(CC) $(CFLAGS) -o check-files check-files.o read-cache.o $(LIBS)
+
+ls-tree: ls-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o ls-tree ls-tree.o read-cache.o $(LIBS)
+
+merge-base: merge-base.o read-cache.o
+	$(CC) $(CFLAGS) -o merge-base merge-base.o read-cache.o $(LIBS)
+
+merge-cache: merge-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o merge-cache merge-cache.o read-cache.o $(LIBS)
+
+read-cache.o: cache.h
+show-diff.o: cache.h
+
+clean:
+	rm -f *.o $(PROG)
+
+backup: clean
+	cd .. ; tar czvf dircache.tar.gz dir-cache
+
+
+Revision-number: 6
+Prop-content-length: 112
+Content-length: 112
+
+K 7
+svn:log
+V 13
+left update 2
+K 10
+svn:author
+V 4
+samv
+K 8
+svn:date
+V 27
+2009-10-19T23:44:08.067554Z
+PROPS-END
+
+Node-path: branches/left/Makefile
+Node-kind: file
+Node-action: change
+Text-content-length: 2593
+Text-content-md5: 5ccff689fb290e00b85fe18ee50c54ba
+Content-length: 2593
+
+# -DCOLLISION_CHECK if you believe that SHA1's
+# 1461501637330902918203684832716283019655932542976 hashes do not give you
+# enough guarantees about no collisions between objects ever hapenning.
+#
+# -DNSEC if you want git to care about sub-second file mtimes and ctimes.
+# Note that you need some new 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).
+CFLAGS=-g -O3 -Wall
+
+CC=gcc
+
+
+PROG=   update-cache show-diff init-db write-tree read-tree commit-tree \
+	cat-file fsck-cache checkout-cache diff-tree rev-tree show-files \
+	check-files ls-tree merge-base
+
+all: $(PROG)
+
+install: $(PROG)
+	install $(PROG) $(HOME)/bin/
+
+LIBS= -lssl -lz
+
+init-db: init-db.o
+
+update-cache: update-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o update-cache update-cache.o read-cache.o $(LIBS)
+
+show-diff: show-diff.o read-cache.o
+	$(CC) $(CFLAGS) -o show-diff show-diff.o read-cache.o $(LIBS)
+
+write-tree: write-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o write-tree write-tree.o read-cache.o $(LIBS)
+
+read-tree: read-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o read-tree read-tree.o read-cache.o $(LIBS)
+
+commit-tree: commit-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o commit-tree commit-tree.o read-cache.o $(LIBS)
+
+cat-file: cat-file.o read-cache.o
+	$(CC) $(CFLAGS) -o cat-file cat-file.o read-cache.o $(LIBS)
+
+fsck-cache: fsck-cache.o read-cache.o object.o commit.o tree.o blob.o
+	$(CC) $(CFLAGS) -o fsck-cache fsck-cache.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+checkout-cache: checkout-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o checkout-cache checkout-cache.o read-cache.o $(LIBS)
+
+diff-tree: diff-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o diff-tree diff-tree.o read-cache.o $(LIBS)
+
+rev-tree: rev-tree.o read-cache.o object.o commit.o tree.o blob.o
+	$(CC) $(CFLAGS) -o rev-tree rev-tree.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+show-files: show-files.o read-cache.o
+	$(CC) $(CFLAGS) -o show-files show-files.o read-cache.o $(LIBS)
+
+check-files: check-files.o read-cache.o
+	$(CC) $(CFLAGS) -o check-files check-files.o read-cache.o $(LIBS)
+
+ls-tree: ls-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o ls-tree ls-tree.o read-cache.o $(LIBS)
+
+merge-base: merge-base.o read-cache.o object.o commit.o tree.o blob.o
+	$(CC) $(CFLAGS) -o merge-base merge-base.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+read-cache.o: cache.h
+show-diff.o: cache.h
+
+clean:
+	rm -f *.o $(PROG)
+
+backup: clean
+	cd .. ; tar czvf dircache.tar.gz dir-cache
+
+
+Revision-number: 7
+Prop-content-length: 131
+Content-length: 131
+
+K 7
+svn:log
+V 32
+merge branch 'left' into 'trunk'
+K 10
+svn:author
+V 4
+samv
+K 8
+svn:date
+V 27
+2009-10-19T23:44:08.971801Z
+PROPS-END
+
+Node-path: trunk
+Node-kind: dir
+Node-action: change
+Prop-content-length: 83
+Content-length: 83
+
+K 9
+svk:merge
+V 53
+b48289b2-9c08-4d72-af37-0358a40b9c15:/branches/left:6
+PROPS-END
+
+
+Node-path: trunk/Makefile
+Node-kind: file
+Node-action: change
+Text-content-length: 2713
+Text-content-md5: 0afbe34f244cd662b1f97d708c687f90
+Content-length: 2713
+
+# -DCOLLISION_CHECK if you believe that SHA1's
+# 1461501637330902918203684832716283019655932542976 hashes do not give you
+# enough guarantees about no collisions between objects ever hapenning.
+#
+# -DNSEC if you want git to care about sub-second file mtimes and ctimes.
+# Note that you need some new 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).
+CFLAGS=-g -O3 -Wall
+
+CC=gcc
+
+
+PROG=   update-cache show-diff init-db write-tree read-tree commit-tree \
+	cat-file fsck-cache checkout-cache diff-tree rev-tree show-files \
+	check-files ls-tree merge-base merge-cache
+
+all: $(PROG)
+
+install: $(PROG)
+	install $(PROG) $(HOME)/bin/
+
+LIBS= -lssl -lz
+
+init-db: init-db.o
+
+update-cache: update-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o update-cache update-cache.o read-cache.o $(LIBS)
+
+show-diff: show-diff.o read-cache.o
+	$(CC) $(CFLAGS) -o show-diff show-diff.o read-cache.o $(LIBS)
+
+write-tree: write-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o write-tree write-tree.o read-cache.o $(LIBS)
+
+read-tree: read-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o read-tree read-tree.o read-cache.o $(LIBS)
+
+commit-tree: commit-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o commit-tree commit-tree.o read-cache.o $(LIBS)
+
+cat-file: cat-file.o read-cache.o
+	$(CC) $(CFLAGS) -o cat-file cat-file.o read-cache.o $(LIBS)
+
+fsck-cache: fsck-cache.o read-cache.o object.o commit.o tree.o blob.o
+	$(CC) $(CFLAGS) -o fsck-cache fsck-cache.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+checkout-cache: checkout-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o checkout-cache checkout-cache.o read-cache.o $(LIBS)
+
+diff-tree: diff-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o diff-tree diff-tree.o read-cache.o $(LIBS)
+
+rev-tree: rev-tree.o read-cache.o object.o commit.o tree.o blob.o
+	$(CC) $(CFLAGS) -o rev-tree rev-tree.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+show-files: show-files.o read-cache.o
+	$(CC) $(CFLAGS) -o show-files show-files.o read-cache.o $(LIBS)
+
+check-files: check-files.o read-cache.o
+	$(CC) $(CFLAGS) -o check-files check-files.o read-cache.o $(LIBS)
+
+ls-tree: ls-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o ls-tree ls-tree.o read-cache.o $(LIBS)
+
+merge-base: merge-base.o read-cache.o object.o commit.o tree.o blob.o
+	$(CC) $(CFLAGS) -o merge-base merge-base.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+merge-cache: merge-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o merge-cache merge-cache.o read-cache.o $(LIBS)
+
+read-cache.o: cache.h
+show-diff.o: cache.h
+
+clean:
+	rm -f *.o $(PROG)
+
+backup: clean
+	cd .. ; tar czvf dircache.tar.gz dir-cache
+
+
diff --git a/t/t9151-svn-mergeinfo.sh b/t/t9151-svn-mergeinfo.sh
new file mode 100755
index 0000000..4f6c06e
--- /dev/null
+++ b/t/t9151-svn-mergeinfo.sh
@@ -0,0 +1,57 @@
+#!/bin/sh
+#
+# Copyright (c) 2007, 2009 Sam Vilain
+#
+
+test_description='git-svn svn mergeinfo properties'
+
+. ./lib-git-svn.sh
+
+test_expect_success 'load svn dump' "
+	svnadmin load -q '$rawsvnrepo' \
+	  < '$TEST_DIRECTORY/t9151/svn-mergeinfo.dump' &&
+	git svn init --minimize-url -R svnmerge \
+	  --rewrite-root=http://svn.example.org \
+	  -T trunk -b branches '$svnrepo' &&
+	git svn fetch --all
+	"
+
+test_expect_success 'all svn merges became git merge commits' '
+	unmarked=$(git rev-list --parents --all --grep=Merge |
+		grep -v " .* " | cut -f1 -d" ") &&
+	[ -z "$unmarked" ]
+	'
+
+test_expect_success 'cherry picks did not become git merge commits' '
+	bad_cherries=$(git rev-list --parents --all --grep=Cherry |
+		grep " .* " | cut -f1 -d" ") &&
+	[ -z "$bad_cherries" ]
+	'
+
+test_expect_success 'svn non-merge merge commits did not become git merge commits' '
+	bad_non_merges=$(git rev-list --parents --all --grep=non-merge |
+		grep " .* " | cut -f1 -d" ") &&
+	[ -z "$bad_non_merges" ]
+	'
+
+test_expect_success 'commit made to merged branch is reachable from the merge' '
+	before_commit=$(git rev-list --all --grep="trunk commit before merging trunk to b2") &&
+	merge_commit=$(git rev-list --all --grep="Merge trunk to b2") &&
+	not_reachable=$(git rev-list -1 $before_commit --not $merge_commit) &&
+	[ -z "$not_reachable" ]
+	'
+
+test_expect_success 'merging two branches in one commit is detected correctly' '
+	f1_commit=$(git rev-list --all --grep="make f1 branch from trunk") &&
+	f2_commit=$(git rev-list --all --grep="make f2 branch from trunk") &&
+	merge_commit=$(git rev-list --all --grep="Merge f1 and f2 to trunk") &&
+	not_reachable=$(git rev-list -1 $f1_commit $f2_commit --not $merge_commit) &&
+	[ -z "$not_reachable" ]
+	'
+
+test_expect_failure 'everything got merged in the end' '
+	unmerged=$(git rev-list --all --not master) &&
+	[ -z "$unmerged" ]
+	'
+
+test_done
diff --git a/t/t9151/.gitignore b/t/t9151/.gitignore
new file mode 100644
index 0000000..587c37d
--- /dev/null
+++ b/t/t9151/.gitignore
@@ -0,0 +1,2 @@
+foo
+foo.svn
diff --git a/t/t9151/make-svnmerge-dump b/t/t9151/make-svnmerge-dump
new file mode 100644
index 0000000..e1e138c
--- /dev/null
+++ b/t/t9151/make-svnmerge-dump
@@ -0,0 +1,305 @@
+#!/bin/sh
+#
+# this script sets up a Subversion repository for Makefile in the
+# first ever git merge, as if it were done with svnmerge (SVN 1.5+)
+#
+
+rm -rf foo.svn foo
+set -e
+
+mkdir foo.svn
+svnadmin create foo.svn
+svn co file://`pwd`/foo.svn foo
+
+commit() {
+    i=$(( $1 + 1 ))
+    shift;
+    svn commit -m "(r$i) $*" >/dev/null || exit 1
+    echo $i
+}
+
+say() {
+    echo " * $*"
+}
+
+i=0
+cd foo
+mkdir trunk
+mkdir branches
+mkdir tags
+svn add trunk branches tags
+i=$(commit $i "Setup trunk, branches, and tags")
+
+git cat-file blob 6683463e:Makefile > trunk/Makefile
+svn add trunk/Makefile 
+
+say "Committing ANCESTOR"
+i=$(commit $i "ancestor")
+svn cp trunk branches/left
+
+say "Committing BRANCH POINT"
+i=$(commit $i "make left branch")
+svn cp trunk branches/right
+
+say "Committing other BRANCH POINT"
+i=$(commit $i "make right branch")
+
+say "Committing LEFT UPDATE"
+git cat-file blob 5873b67e:Makefile > branches/left/Makefile
+i=$(commit $i "left update 1")
+
+git cat-file blob 75118b13:Makefile > branches/right/Makefile
+say "Committing RIGHT UPDATE"
+pre_right_update_1=$i
+i=$(commit $i "right update 1")
+
+say "Making more commits on LEFT"
+git cat-file blob ff5ebe39:Makefile > branches/left/Makefile
+i=$(commit $i "left update 2")
+git cat-file blob b5039db6:Makefile > branches/left/Makefile
+i=$(commit $i "left update 3")
+
+say "Making a LEFT SUB-BRANCH"
+svn cp branches/left branches/left-sub
+sub_left_make=$i
+i=$(commit $i "make left sub-branch")
+
+say "Making a commit on LEFT SUB-BRANCH"
+echo "crunch" > branches/left-sub/README
+svn add branches/left-sub/README
+i=$(commit $i "left sub-branch update 1")
+
+say "Merging LEFT to TRUNK"
+svn update
+cd trunk
+svn merge ../branches/left --accept postpone
+git cat-file blob b5039db6:Makefile > Makefile
+svn resolved Makefile
+i=$(commit $i "Merge left to trunk 1")
+cd ..
+
+say "Making more commits on LEFT and RIGHT"
+echo "touche" > branches/left/zlonk
+svn add branches/left/zlonk
+i=$(commit $i "left update 4")
+echo "thwacke" > branches/right/bang
+svn add branches/right/bang
+i=$(commit $i "right update 2")
+
+say "Squash merge of RIGHT tip 2 commits onto TRUNK"
+svn update
+cd trunk
+svn merge -r$pre_right_update_1:$i ../branches/right
+i=$(commit $i "Cherry-pick right 2 commits to trunk")
+cd ..
+
+say "Merging RIGHT to TRUNK"
+svn update
+cd trunk
+svn merge ../branches/right --accept postpone
+git cat-file blob b51ad431:Makefile > Makefile
+svn resolved Makefile
+i=$(commit $i "Merge right to trunk 1")
+cd ..
+
+say "Making more commits on RIGHT and TRUNK"
+echo "whamm" > branches/right/urkkk
+svn add branches/right/urkkk
+i=$(commit $i "right update 3")
+echo "pow" > trunk/vronk
+svn add trunk/vronk
+i=$(commit $i "trunk update 1")
+
+say "Merging RIGHT to LEFT SUB-BRANCH"
+svn update
+cd branches/left-sub
+svn merge ../right --accept postpone
+git cat-file blob b51ad431:Makefile > Makefile
+svn resolved Makefile
+i=$(commit $i "Merge right to left sub-branch")
+cd ../..
+
+say "Making more commits on LEFT SUB-BRANCH and LEFT"
+echo "zowie" > branches/left-sub/wham_eth
+svn add branches/left-sub/wham_eth
+pre_sub_left_update_2=$i
+i=$(commit $i "left sub-branch update 2")
+sub_left_update_2=$i
+echo "eee_yow" > branches/left/glurpp
+svn add branches/left/glurpp
+i=$(commit $i "left update 5")
+
+say "Cherry pick LEFT SUB-BRANCH commit to LEFT"
+svn update
+cd branches/left
+svn merge -r$pre_sub_left_update_2:$sub_left_update_2 ../left-sub
+i=$(commit $i "Cherry-pick left sub-branch commit to left")
+cd ../..
+
+say "Merging LEFT SUB-BRANCH back to LEFT"
+svn update
+cd branches/left
+# it's only a merge because the previous merge cherry-picked the top commit
+svn merge -r$sub_left_make:$sub_left_update_2 ../left-sub --accept postpone
+i=$(commit $i "Merge left sub-branch to left")
+cd ../..
+
+say "Merging EVERYTHING to TRUNK"
+svn update
+cd trunk
+svn merge ../branches/left --accept postpone
+svn resolved bang
+i=$(commit $i "Merge left to trunk 2")
+# this merge, svn happily updates the mergeinfo, but there is actually
+# nothing to merge.  git-svn will not make a meaningless merge commit.
+svn merge ../branches/right --accept postpone
+i=$(commit $i "non-merge right to trunk 2")
+cd ..
+
+say "Branching b1 from trunk"
+svn update
+svn cp trunk branches/b1
+i=$(commit $i "make b1 branch from trunk")
+
+say "Branching b2 from trunk"
+svn update
+svn cp trunk branches/b2
+i=$(commit $i "make b2 branch from trunk")
+
+say "Make a commit to b2"
+svn update
+cd branches/b2
+echo "b2" > b2file
+svn add b2file
+i=$(commit $i "b2 update 1")
+cd ../..
+
+say "Make a commit to b1"
+svn update
+cd branches/b1
+echo "b1" > b1file
+svn add b1file
+i=$(commit $i "b1 update 1")
+cd ../..
+
+say "Merge b1 to trunk"
+svn update
+cd trunk
+svn merge ../branches/b1/ --accept postpone
+i=$(commit $i "Merge b1 to trunk")
+cd ..
+
+say "Make a commit to trunk before merging trunk to b2"
+svn update
+cd trunk
+echo "trunk" > trunkfile
+svn add trunkfile
+i=$(commit $i "trunk commit before merging trunk to b2")
+cd ..
+
+say "Merge trunk to b2"
+svn update
+cd branches/b2
+svn merge ../../trunk/ --accept postpone
+i=$(commit $i "Merge trunk to b2")
+cd ../..
+
+say "Merge b2 to trunk"
+svn update
+cd trunk
+svn merge ../branches/b2/ --accept postpone
+svn resolved b1file
+svn resolved trunkfile
+i=$(commit $i "Merge b2 to trunk")
+cd ..
+
+say "Creating f1 from trunk with a new file"
+svn update
+svn cp trunk branches/f1
+cd branches/f1
+echo "f1" > f1file
+svn add f1file
+cd ../..
+i=$(commit $i "make f1 branch from trunk with a new file")
+
+say "Creating f2 from trunk with a new file"
+svn update
+svn cp trunk branches/f2
+cd branches/f2
+echo "f2" > f2file
+svn add f2file
+cd ../..
+i=$(commit $i "make f2 branch from trunk with a new file")
+
+say "Merge f1 and f2 to trunk in one go"
+svn update
+cd trunk
+svn merge ../branches/f1/ --accept postpone
+svn merge ../branches/f2/ --accept postpone
+i=$(commit $i "Merge f1 and f2 to trunk")
+cd ..
+
+say "Adding subdirectory to LEFT"
+svn update
+cd branches/left
+mkdir subdir
+echo "Yeehaw" > subdir/cowboy
+svn add subdir
+i=$(commit $i "add subdirectory to left branch")
+cd ../../
+
+say "Merging LEFT to TRUNK"
+svn update
+cd trunk
+svn merge ../branches/left --accept postpone
+i=$(commit $i "merge left to trunk")
+cd ..
+
+say "Make PARTIAL branch"
+svn update
+svn cp trunk/subdir branches/partial
+i=$(commit $i "make partial branch")
+
+say "Make a commit to PARTIAL"
+svn update
+cd branches/partial
+echo "racecar" > palindromes
+svn add palindromes
+i=$(commit $i "partial update")
+cd ../../
+
+say "Merge PARTIAL to TRUNK"
+svn update
+cd trunk/subdir
+svn merge ../../branches/partial --accept postpone
+i=$(commit $i "merge partial to trunk")
+cd ../../
+
+say "Tagging trunk"
+svn update
+svn cp trunk tags/v1.0
+i=$(commit $i "tagging v1.0")
+
+say "Branching BUGFIX from v1.0"
+svn update
+svn cp tags/v1.0 branches/bugfix
+i=$(commit $i "make bugfix branch from tag")
+
+say "Make a commit to BUGFIX"
+svn update
+cd branches/bugfix/
+echo "kayak" >> subdir/palindromes
+i=$(commit $i "commit to bugfix")
+cd ../../
+
+say "Merge BUGFIX to TRUNK"
+svn update
+cd trunk
+svn merge ../branches/bugfix/ --accept postpone
+i=$(commit $i "Merge BUGFIX to TRUNK")
+cd ..
+
+cd ..
+svnadmin dump foo.svn > svn-mergeinfo.dump
+
+rm -rf foo foo.svn
diff --git a/t/t9151/svn-mergeinfo.dump b/t/t9151/svn-mergeinfo.dump
new file mode 100644
index 0000000..47cafcf
--- /dev/null
+++ b/t/t9151/svn-mergeinfo.dump
@@ -0,0 +1,2388 @@
+SVN-fs-dump-format-version: 2
+
+UUID: d6191530-2693-4a8e-98e7-b194d4c3edd8
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2010-01-19T04:14:02.832406Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 134
+Content-length: 134
+
+K 7
+svn:log
+V 36
+(r1) Setup trunk, branches, and tags
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:03.055172Z
+PROPS-END
+
+Node-path: branches
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: tags
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: trunk
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Revision-number: 2
+Prop-content-length: 111
+Content-length: 111
+
+K 7
+svn:log
+V 13
+(r2) ancestor
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:04.064506Z
+PROPS-END
+
+Node-path: trunk/Makefile
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 2401
+Text-content-md5: bfd8ff778d1492dc6758567373176a89
+Text-content-sha1: 103205ce331f7d64086dba497574734f78439590
+Content-length: 2411
+
+PROPS-END
+# -DCOLLISION_CHECK if you believe that SHA1's
+# 1461501637330902918203684832716283019655932542976 hashes do not give you
+# enough guarantees about no collisions between objects ever hapenning.
+#
+# -DNSEC if you want git to care about sub-second file mtimes and ctimes.
+# Note that you need some new 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).
+CFLAGS=-g -O3 -Wall
+
+CC=gcc
+
+
+PROG=   update-cache show-diff init-db write-tree read-tree commit-tree \
+	cat-file fsck-cache checkout-cache diff-tree rev-tree show-files \
+	check-files ls-tree merge-base
+
+all: $(PROG)
+
+install: $(PROG)
+	install $(PROG) $(HOME)/bin/
+
+LIBS= -lssl -lz
+
+init-db: init-db.o
+
+update-cache: update-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o update-cache update-cache.o read-cache.o $(LIBS)
+
+show-diff: show-diff.o read-cache.o
+	$(CC) $(CFLAGS) -o show-diff show-diff.o read-cache.o $(LIBS)
+
+write-tree: write-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o write-tree write-tree.o read-cache.o $(LIBS)
+
+read-tree: read-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o read-tree read-tree.o read-cache.o $(LIBS)
+
+commit-tree: commit-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o commit-tree commit-tree.o read-cache.o $(LIBS)
+
+cat-file: cat-file.o read-cache.o
+	$(CC) $(CFLAGS) -o cat-file cat-file.o read-cache.o $(LIBS)
+
+fsck-cache: fsck-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o fsck-cache fsck-cache.o read-cache.o $(LIBS)
+
+checkout-cache: checkout-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o checkout-cache checkout-cache.o read-cache.o $(LIBS)
+
+diff-tree: diff-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o diff-tree diff-tree.o read-cache.o $(LIBS)
+
+rev-tree: rev-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o rev-tree rev-tree.o read-cache.o $(LIBS)
+
+show-files: show-files.o read-cache.o
+	$(CC) $(CFLAGS) -o show-files show-files.o read-cache.o $(LIBS)
+
+check-files: check-files.o read-cache.o
+	$(CC) $(CFLAGS) -o check-files check-files.o read-cache.o $(LIBS)
+
+ls-tree: ls-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o ls-tree ls-tree.o read-cache.o $(LIBS)
+
+merge-base: merge-base.o read-cache.o
+	$(CC) $(CFLAGS) -o merge-base merge-base.o read-cache.o $(LIBS)
+
+read-cache.o: cache.h
+show-diff.o: cache.h
+
+clean:
+	rm -f *.o $(PROG)
+
+backup: clean
+	cd .. ; tar czvf dircache.tar.gz dir-cache
+
+
+Revision-number: 3
+Prop-content-length: 119
+Content-length: 119
+
+K 7
+svn:log
+V 21
+(r3) make left branch
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:06.040389Z
+PROPS-END
+
+Node-path: branches/left
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 1
+Node-copyfrom-path: trunk
+
+
+Node-path: branches/left/Makefile
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 2
+Node-copyfrom-path: trunk/Makefile
+Text-copy-source-md5: bfd8ff778d1492dc6758567373176a89
+Text-copy-source-sha1: 103205ce331f7d64086dba497574734f78439590
+
+
+Revision-number: 4
+Prop-content-length: 120
+Content-length: 120
+
+K 7
+svn:log
+V 22
+(r4) make right branch
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:08.040905Z
+PROPS-END
+
+Node-path: branches/right
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 1
+Node-copyfrom-path: trunk
+
+
+Node-path: branches/right/Makefile
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 2
+Node-copyfrom-path: trunk/Makefile
+Text-copy-source-md5: bfd8ff778d1492dc6758567373176a89
+Text-copy-source-sha1: 103205ce331f7d64086dba497574734f78439590
+
+
+Revision-number: 5
+Prop-content-length: 116
+Content-length: 116
+
+K 7
+svn:log
+V 18
+(r5) left update 1
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:09.049169Z
+PROPS-END
+
+Node-path: branches/left/Makefile
+Node-kind: file
+Node-action: change
+Text-content-length: 2465
+Text-content-md5: 16e38d9753b061731650561ce01b1195
+Text-content-sha1: 36da4b84ea9b64218ab48171dfc5c48ae025f38b
+Content-length: 2465
+
+# -DCOLLISION_CHECK if you believe that SHA1's
+# 1461501637330902918203684832716283019655932542976 hashes do not give you
+# enough guarantees about no collisions between objects ever hapenning.
+#
+# -DNSEC if you want git to care about sub-second file mtimes and ctimes.
+# Note that you need some new 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).
+CFLAGS=-g -O3 -Wall
+
+CC=gcc
+
+
+PROG=   update-cache show-diff init-db write-tree read-tree commit-tree \
+	cat-file fsck-cache checkout-cache diff-tree rev-tree show-files \
+	check-files ls-tree merge-base
+
+all: $(PROG)
+
+install: $(PROG)
+	install $(PROG) $(HOME)/bin/
+
+LIBS= -lssl -lz
+
+init-db: init-db.o
+
+update-cache: update-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o update-cache update-cache.o read-cache.o $(LIBS)
+
+show-diff: show-diff.o read-cache.o
+	$(CC) $(CFLAGS) -o show-diff show-diff.o read-cache.o $(LIBS)
+
+write-tree: write-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o write-tree write-tree.o read-cache.o $(LIBS)
+
+read-tree: read-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o read-tree read-tree.o read-cache.o $(LIBS)
+
+commit-tree: commit-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o commit-tree commit-tree.o read-cache.o $(LIBS)
+
+cat-file: cat-file.o read-cache.o
+	$(CC) $(CFLAGS) -o cat-file cat-file.o read-cache.o $(LIBS)
+
+fsck-cache: fsck-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o fsck-cache fsck-cache.o read-cache.o $(LIBS)
+
+checkout-cache: checkout-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o checkout-cache checkout-cache.o read-cache.o $(LIBS)
+
+diff-tree: diff-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o diff-tree diff-tree.o read-cache.o $(LIBS)
+
+rev-tree: rev-tree.o read-cache.o object.o commit.o tree.o blob.o
+	$(CC) $(CFLAGS) -o rev-tree rev-tree.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+show-files: show-files.o read-cache.o
+	$(CC) $(CFLAGS) -o show-files show-files.o read-cache.o $(LIBS)
+
+check-files: check-files.o read-cache.o
+	$(CC) $(CFLAGS) -o check-files check-files.o read-cache.o $(LIBS)
+
+ls-tree: ls-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o ls-tree ls-tree.o read-cache.o $(LIBS)
+
+merge-base: merge-base.o read-cache.o
+	$(CC) $(CFLAGS) -o merge-base merge-base.o read-cache.o $(LIBS)
+
+read-cache.o: cache.h
+show-diff.o: cache.h
+
+clean:
+	rm -f *.o $(PROG)
+
+backup: clean
+	cd .. ; tar czvf dircache.tar.gz dir-cache
+
+
+Revision-number: 6
+Prop-content-length: 117
+Content-length: 117
+
+K 7
+svn:log
+V 19
+(r6) right update 1
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:10.049350Z
+PROPS-END
+
+Node-path: branches/right/Makefile
+Node-kind: file
+Node-action: change
+Text-content-length: 2521
+Text-content-md5: 0668418a621333f4aa8b6632cd63e2a0
+Text-content-sha1: 4f29afd038e52f45acb5ef8c41acfc70062a741a
+Content-length: 2521
+
+# -DCOLLISION_CHECK if you believe that SHA1's
+# 1461501637330902918203684832716283019655932542976 hashes do not give you
+# enough guarantees about no collisions between objects ever hapenning.
+#
+# -DNSEC if you want git to care about sub-second file mtimes and ctimes.
+# Note that you need some new 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).
+CFLAGS=-g -O3 -Wall
+
+CC=gcc
+
+
+PROG=   update-cache show-diff init-db write-tree read-tree commit-tree \
+	cat-file fsck-cache checkout-cache diff-tree rev-tree show-files \
+	check-files ls-tree merge-base merge-cache
+
+all: $(PROG)
+
+install: $(PROG)
+	install $(PROG) $(HOME)/bin/
+
+LIBS= -lssl -lz
+
+init-db: init-db.o
+
+update-cache: update-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o update-cache update-cache.o read-cache.o $(LIBS)
+
+show-diff: show-diff.o read-cache.o
+	$(CC) $(CFLAGS) -o show-diff show-diff.o read-cache.o $(LIBS)
+
+write-tree: write-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o write-tree write-tree.o read-cache.o $(LIBS)
+
+read-tree: read-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o read-tree read-tree.o read-cache.o $(LIBS)
+
+commit-tree: commit-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o commit-tree commit-tree.o read-cache.o $(LIBS)
+
+cat-file: cat-file.o read-cache.o
+	$(CC) $(CFLAGS) -o cat-file cat-file.o read-cache.o $(LIBS)
+
+fsck-cache: fsck-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o fsck-cache fsck-cache.o read-cache.o $(LIBS)
+
+checkout-cache: checkout-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o checkout-cache checkout-cache.o read-cache.o $(LIBS)
+
+diff-tree: diff-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o diff-tree diff-tree.o read-cache.o $(LIBS)
+
+rev-tree: rev-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o rev-tree rev-tree.o read-cache.o $(LIBS)
+
+show-files: show-files.o read-cache.o
+	$(CC) $(CFLAGS) -o show-files show-files.o read-cache.o $(LIBS)
+
+check-files: check-files.o read-cache.o
+	$(CC) $(CFLAGS) -o check-files check-files.o read-cache.o $(LIBS)
+
+ls-tree: ls-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o ls-tree ls-tree.o read-cache.o $(LIBS)
+
+merge-base: merge-base.o read-cache.o
+	$(CC) $(CFLAGS) -o merge-base merge-base.o read-cache.o $(LIBS)
+
+merge-cache: merge-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o merge-cache merge-cache.o read-cache.o $(LIBS)
+
+read-cache.o: cache.h
+show-diff.o: cache.h
+
+clean:
+	rm -f *.o $(PROG)
+
+backup: clean
+	cd .. ; tar czvf dircache.tar.gz dir-cache
+
+
+Revision-number: 7
+Prop-content-length: 116
+Content-length: 116
+
+K 7
+svn:log
+V 18
+(r7) left update 2
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:11.049209Z
+PROPS-END
+
+Node-path: branches/left/Makefile
+Node-kind: file
+Node-action: change
+Text-content-length: 2529
+Text-content-md5: f6b197cc3f2e89a83e545d4bb003de73
+Text-content-sha1: 2f656677cfec0bceec85e53036ffb63e25126f8e
+Content-length: 2529
+
+# -DCOLLISION_CHECK if you believe that SHA1's
+# 1461501637330902918203684832716283019655932542976 hashes do not give you
+# enough guarantees about no collisions between objects ever hapenning.
+#
+# -DNSEC if you want git to care about sub-second file mtimes and ctimes.
+# Note that you need some new 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).
+CFLAGS=-g -O3 -Wall
+
+CC=gcc
+
+
+PROG=   update-cache show-diff init-db write-tree read-tree commit-tree \
+	cat-file fsck-cache checkout-cache diff-tree rev-tree show-files \
+	check-files ls-tree merge-base
+
+all: $(PROG)
+
+install: $(PROG)
+	install $(PROG) $(HOME)/bin/
+
+LIBS= -lssl -lz
+
+init-db: init-db.o
+
+update-cache: update-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o update-cache update-cache.o read-cache.o $(LIBS)
+
+show-diff: show-diff.o read-cache.o
+	$(CC) $(CFLAGS) -o show-diff show-diff.o read-cache.o $(LIBS)
+
+write-tree: write-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o write-tree write-tree.o read-cache.o $(LIBS)
+
+read-tree: read-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o read-tree read-tree.o read-cache.o $(LIBS)
+
+commit-tree: commit-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o commit-tree commit-tree.o read-cache.o $(LIBS)
+
+cat-file: cat-file.o read-cache.o
+	$(CC) $(CFLAGS) -o cat-file cat-file.o read-cache.o $(LIBS)
+
+fsck-cache: fsck-cache.o read-cache.o object.o commit.o tree.o blob.o
+	$(CC) $(CFLAGS) -o fsck-cache fsck-cache.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+checkout-cache: checkout-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o checkout-cache checkout-cache.o read-cache.o $(LIBS)
+
+diff-tree: diff-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o diff-tree diff-tree.o read-cache.o $(LIBS)
+
+rev-tree: rev-tree.o read-cache.o object.o commit.o tree.o blob.o
+	$(CC) $(CFLAGS) -o rev-tree rev-tree.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+show-files: show-files.o read-cache.o
+	$(CC) $(CFLAGS) -o show-files show-files.o read-cache.o $(LIBS)
+
+check-files: check-files.o read-cache.o
+	$(CC) $(CFLAGS) -o check-files check-files.o read-cache.o $(LIBS)
+
+ls-tree: ls-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o ls-tree ls-tree.o read-cache.o $(LIBS)
+
+merge-base: merge-base.o read-cache.o
+	$(CC) $(CFLAGS) -o merge-base merge-base.o read-cache.o $(LIBS)
+
+read-cache.o: cache.h
+show-diff.o: cache.h
+
+clean:
+	rm -f *.o $(PROG)
+
+backup: clean
+	cd .. ; tar czvf dircache.tar.gz dir-cache
+
+
+Revision-number: 8
+Prop-content-length: 116
+Content-length: 116
+
+K 7
+svn:log
+V 18
+(r8) left update 3
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:12.049234Z
+PROPS-END
+
+Node-path: branches/left/Makefile
+Node-kind: file
+Node-action: change
+Text-content-length: 2593
+Text-content-md5: 5ccff689fb290e00b85fe18ee50c54ba
+Text-content-sha1: a13de8e23f1483efca3e57b2b64b0ae6f740ce10
+Content-length: 2593
+
+# -DCOLLISION_CHECK if you believe that SHA1's
+# 1461501637330902918203684832716283019655932542976 hashes do not give you
+# enough guarantees about no collisions between objects ever hapenning.
+#
+# -DNSEC if you want git to care about sub-second file mtimes and ctimes.
+# Note that you need some new 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).
+CFLAGS=-g -O3 -Wall
+
+CC=gcc
+
+
+PROG=   update-cache show-diff init-db write-tree read-tree commit-tree \
+	cat-file fsck-cache checkout-cache diff-tree rev-tree show-files \
+	check-files ls-tree merge-base
+
+all: $(PROG)
+
+install: $(PROG)
+	install $(PROG) $(HOME)/bin/
+
+LIBS= -lssl -lz
+
+init-db: init-db.o
+
+update-cache: update-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o update-cache update-cache.o read-cache.o $(LIBS)
+
+show-diff: show-diff.o read-cache.o
+	$(CC) $(CFLAGS) -o show-diff show-diff.o read-cache.o $(LIBS)
+
+write-tree: write-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o write-tree write-tree.o read-cache.o $(LIBS)
+
+read-tree: read-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o read-tree read-tree.o read-cache.o $(LIBS)
+
+commit-tree: commit-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o commit-tree commit-tree.o read-cache.o $(LIBS)
+
+cat-file: cat-file.o read-cache.o
+	$(CC) $(CFLAGS) -o cat-file cat-file.o read-cache.o $(LIBS)
+
+fsck-cache: fsck-cache.o read-cache.o object.o commit.o tree.o blob.o
+	$(CC) $(CFLAGS) -o fsck-cache fsck-cache.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+checkout-cache: checkout-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o checkout-cache checkout-cache.o read-cache.o $(LIBS)
+
+diff-tree: diff-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o diff-tree diff-tree.o read-cache.o $(LIBS)
+
+rev-tree: rev-tree.o read-cache.o object.o commit.o tree.o blob.o
+	$(CC) $(CFLAGS) -o rev-tree rev-tree.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+show-files: show-files.o read-cache.o
+	$(CC) $(CFLAGS) -o show-files show-files.o read-cache.o $(LIBS)
+
+check-files: check-files.o read-cache.o
+	$(CC) $(CFLAGS) -o check-files check-files.o read-cache.o $(LIBS)
+
+ls-tree: ls-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o ls-tree ls-tree.o read-cache.o $(LIBS)
+
+merge-base: merge-base.o read-cache.o object.o commit.o tree.o blob.o
+	$(CC) $(CFLAGS) -o merge-base merge-base.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+read-cache.o: cache.h
+show-diff.o: cache.h
+
+clean:
+	rm -f *.o $(PROG)
+
+backup: clean
+	cd .. ; tar czvf dircache.tar.gz dir-cache
+
+
+Revision-number: 9
+Prop-content-length: 123
+Content-length: 123
+
+K 7
+svn:log
+V 25
+(r9) make left sub-branch
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:14.040894Z
+PROPS-END
+
+Node-path: branches/left-sub
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 3
+Node-copyfrom-path: branches/left
+
+
+Node-path: branches/left-sub/Makefile
+Node-kind: file
+Node-action: delete
+
+Node-path: branches/left-sub/Makefile
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 8
+Node-copyfrom-path: branches/left/Makefile
+Text-copy-source-md5: 5ccff689fb290e00b85fe18ee50c54ba
+Text-copy-source-sha1: a13de8e23f1483efca3e57b2b64b0ae6f740ce10
+
+
+
+
+Revision-number: 10
+Prop-content-length: 128
+Content-length: 128
+
+K 7
+svn:log
+V 30
+(r10) left sub-branch update 1
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:15.049935Z
+PROPS-END
+
+Node-path: branches/left-sub/README
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 7
+Text-content-md5: fdbcfb6be9afe1121862143f226b51cf
+Text-content-sha1: 1d1f5ea4ceb584337ffe59b8980d92e3b78dfef4
+Content-length: 17
+
+PROPS-END
+crunch
+
+
+Revision-number: 11
+Prop-content-length: 125
+Content-length: 125
+
+K 7
+svn:log
+V 27
+(r11) Merge left to trunk 1
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:18.056594Z
+PROPS-END
+
+Node-path: trunk
+Node-kind: dir
+Node-action: change
+Prop-content-length: 54
+Content-length: 54
+
+K 13
+svn:mergeinfo
+V 19
+/branches/left:2-10
+PROPS-END
+
+
+Node-path: trunk/Makefile
+Node-kind: file
+Node-action: change
+Text-content-length: 2593
+Text-content-md5: 5ccff689fb290e00b85fe18ee50c54ba
+Text-content-sha1: a13de8e23f1483efca3e57b2b64b0ae6f740ce10
+Content-length: 2593
+
+# -DCOLLISION_CHECK if you believe that SHA1's
+# 1461501637330902918203684832716283019655932542976 hashes do not give you
+# enough guarantees about no collisions between objects ever hapenning.
+#
+# -DNSEC if you want git to care about sub-second file mtimes and ctimes.
+# Note that you need some new 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).
+CFLAGS=-g -O3 -Wall
+
+CC=gcc
+
+
+PROG=   update-cache show-diff init-db write-tree read-tree commit-tree \
+	cat-file fsck-cache checkout-cache diff-tree rev-tree show-files \
+	check-files ls-tree merge-base
+
+all: $(PROG)
+
+install: $(PROG)
+	install $(PROG) $(HOME)/bin/
+
+LIBS= -lssl -lz
+
+init-db: init-db.o
+
+update-cache: update-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o update-cache update-cache.o read-cache.o $(LIBS)
+
+show-diff: show-diff.o read-cache.o
+	$(CC) $(CFLAGS) -o show-diff show-diff.o read-cache.o $(LIBS)
+
+write-tree: write-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o write-tree write-tree.o read-cache.o $(LIBS)
+
+read-tree: read-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o read-tree read-tree.o read-cache.o $(LIBS)
+
+commit-tree: commit-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o commit-tree commit-tree.o read-cache.o $(LIBS)
+
+cat-file: cat-file.o read-cache.o
+	$(CC) $(CFLAGS) -o cat-file cat-file.o read-cache.o $(LIBS)
+
+fsck-cache: fsck-cache.o read-cache.o object.o commit.o tree.o blob.o
+	$(CC) $(CFLAGS) -o fsck-cache fsck-cache.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+checkout-cache: checkout-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o checkout-cache checkout-cache.o read-cache.o $(LIBS)
+
+diff-tree: diff-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o diff-tree diff-tree.o read-cache.o $(LIBS)
+
+rev-tree: rev-tree.o read-cache.o object.o commit.o tree.o blob.o
+	$(CC) $(CFLAGS) -o rev-tree rev-tree.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+show-files: show-files.o read-cache.o
+	$(CC) $(CFLAGS) -o show-files show-files.o read-cache.o $(LIBS)
+
+check-files: check-files.o read-cache.o
+	$(CC) $(CFLAGS) -o check-files check-files.o read-cache.o $(LIBS)
+
+ls-tree: ls-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o ls-tree ls-tree.o read-cache.o $(LIBS)
+
+merge-base: merge-base.o read-cache.o object.o commit.o tree.o blob.o
+	$(CC) $(CFLAGS) -o merge-base merge-base.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+read-cache.o: cache.h
+show-diff.o: cache.h
+
+clean:
+	rm -f *.o $(PROG)
+
+backup: clean
+	cd .. ; tar czvf dircache.tar.gz dir-cache
+
+
+Revision-number: 12
+Prop-content-length: 117
+Content-length: 117
+
+K 7
+svn:log
+V 19
+(r12) left update 4
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:19.049620Z
+PROPS-END
+
+Node-path: branches/left/zlonk
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 7
+Text-content-md5: 8b9d8c7c2aaa6167e7d3407a773bbbba
+Text-content-sha1: 9716527ebd70a75c27625cacbeb2d897c6e86178
+Content-length: 17
+
+PROPS-END
+touche
+
+
+Revision-number: 13
+Prop-content-length: 118
+Content-length: 118
+
+K 7
+svn:log
+V 20
+(r13) right update 2
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:20.049659Z
+PROPS-END
+
+Node-path: branches/right/bang
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 8
+Text-content-md5: 34c28f1d2dc6a9adeccc4265bf7516cb
+Text-content-sha1: 0bc5bb345c0e71d28f784f12e0bd2d384c283062
+Content-length: 18
+
+PROPS-END
+thwacke
+
+
+Revision-number: 14
+Prop-content-length: 140
+Content-length: 140
+
+K 7
+svn:log
+V 42
+(r14) Cherry-pick right 2 commits to trunk
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:23.041991Z
+PROPS-END
+
+Node-path: trunk
+Node-kind: dir
+Node-action: change
+Prop-content-length: 75
+Content-length: 75
+
+K 13
+svn:mergeinfo
+V 40
+/branches/left:2-10
+/branches/right:6-13
+PROPS-END
+
+
+Node-path: trunk/Makefile
+Node-kind: file
+Node-action: change
+Text-content-length: 2713
+Text-content-md5: 0afbe34f244cd662b1f97d708c687f90
+Text-content-sha1: 46d9377d783e67a9b581da110352e799517c8a14
+Content-length: 2713
+
+# -DCOLLISION_CHECK if you believe that SHA1's
+# 1461501637330902918203684832716283019655932542976 hashes do not give you
+# enough guarantees about no collisions between objects ever hapenning.
+#
+# -DNSEC if you want git to care about sub-second file mtimes and ctimes.
+# Note that you need some new 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).
+CFLAGS=-g -O3 -Wall
+
+CC=gcc
+
+
+PROG=   update-cache show-diff init-db write-tree read-tree commit-tree \
+	cat-file fsck-cache checkout-cache diff-tree rev-tree show-files \
+	check-files ls-tree merge-base merge-cache
+
+all: $(PROG)
+
+install: $(PROG)
+	install $(PROG) $(HOME)/bin/
+
+LIBS= -lssl -lz
+
+init-db: init-db.o
+
+update-cache: update-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o update-cache update-cache.o read-cache.o $(LIBS)
+
+show-diff: show-diff.o read-cache.o
+	$(CC) $(CFLAGS) -o show-diff show-diff.o read-cache.o $(LIBS)
+
+write-tree: write-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o write-tree write-tree.o read-cache.o $(LIBS)
+
+read-tree: read-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o read-tree read-tree.o read-cache.o $(LIBS)
+
+commit-tree: commit-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o commit-tree commit-tree.o read-cache.o $(LIBS)
+
+cat-file: cat-file.o read-cache.o
+	$(CC) $(CFLAGS) -o cat-file cat-file.o read-cache.o $(LIBS)
+
+fsck-cache: fsck-cache.o read-cache.o object.o commit.o tree.o blob.o
+	$(CC) $(CFLAGS) -o fsck-cache fsck-cache.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+checkout-cache: checkout-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o checkout-cache checkout-cache.o read-cache.o $(LIBS)
+
+diff-tree: diff-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o diff-tree diff-tree.o read-cache.o $(LIBS)
+
+rev-tree: rev-tree.o read-cache.o object.o commit.o tree.o blob.o
+	$(CC) $(CFLAGS) -o rev-tree rev-tree.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+show-files: show-files.o read-cache.o
+	$(CC) $(CFLAGS) -o show-files show-files.o read-cache.o $(LIBS)
+
+check-files: check-files.o read-cache.o
+	$(CC) $(CFLAGS) -o check-files check-files.o read-cache.o $(LIBS)
+
+ls-tree: ls-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o ls-tree ls-tree.o read-cache.o $(LIBS)
+
+merge-base: merge-base.o read-cache.o object.o commit.o tree.o blob.o
+	$(CC) $(CFLAGS) -o merge-base merge-base.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+merge-cache: merge-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o merge-cache merge-cache.o read-cache.o $(LIBS)
+
+read-cache.o: cache.h
+show-diff.o: cache.h
+
+clean:
+	rm -f *.o $(PROG)
+
+backup: clean
+	cd .. ; tar czvf dircache.tar.gz dir-cache
+
+
+Node-path: trunk/bang
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 13
+Node-copyfrom-path: branches/right/bang
+Text-copy-source-md5: 34c28f1d2dc6a9adeccc4265bf7516cb
+Text-copy-source-sha1: 0bc5bb345c0e71d28f784f12e0bd2d384c283062
+
+
+Revision-number: 15
+Prop-content-length: 126
+Content-length: 126
+
+K 7
+svn:log
+V 28
+(r15) Merge right to trunk 1
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:26.054456Z
+PROPS-END
+
+Node-path: trunk
+Node-kind: dir
+Node-action: change
+Prop-content-length: 75
+Content-length: 75
+
+K 13
+svn:mergeinfo
+V 40
+/branches/left:2-10
+/branches/right:2-14
+PROPS-END
+
+
+Revision-number: 16
+Prop-content-length: 118
+Content-length: 118
+
+K 7
+svn:log
+V 20
+(r16) right update 3
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:27.049955Z
+PROPS-END
+
+Node-path: branches/right/urkkk
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 6
+Text-content-md5: 5889c8392e16251b0c80927607a03036
+Text-content-sha1: 3934264d277a0cf886b6b1c7f2b9e56da2525302
+Content-length: 16
+
+PROPS-END
+whamm
+
+
+Revision-number: 17
+Prop-content-length: 118
+Content-length: 118
+
+K 7
+svn:log
+V 20
+(r17) trunk update 1
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:28.049615Z
+PROPS-END
+
+Node-path: trunk/vronk
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 4
+Text-content-md5: b2f80fa02a7f1364b9c29d3da44bf9f9
+Text-content-sha1: e994d980c0f2d7a3f76138bf96d57f36f9633828
+Content-length: 14
+
+PROPS-END
+pow
+
+
+Revision-number: 18
+Prop-content-length: 134
+Content-length: 134
+
+K 7
+svn:log
+V 36
+(r18) Merge right to left sub-branch
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:31.061460Z
+PROPS-END
+
+Node-path: branches/left-sub
+Node-kind: dir
+Node-action: change
+Prop-content-length: 55
+Content-length: 55
+
+K 13
+svn:mergeinfo
+V 20
+/branches/right:2-17
+PROPS-END
+
+
+Node-path: branches/left-sub/Makefile
+Node-kind: file
+Node-action: change
+Text-content-length: 2713
+Text-content-md5: 0afbe34f244cd662b1f97d708c687f90
+Text-content-sha1: 46d9377d783e67a9b581da110352e799517c8a14
+Content-length: 2713
+
+# -DCOLLISION_CHECK if you believe that SHA1's
+# 1461501637330902918203684832716283019655932542976 hashes do not give you
+# enough guarantees about no collisions between objects ever hapenning.
+#
+# -DNSEC if you want git to care about sub-second file mtimes and ctimes.
+# Note that you need some new 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).
+CFLAGS=-g -O3 -Wall
+
+CC=gcc
+
+
+PROG=   update-cache show-diff init-db write-tree read-tree commit-tree \
+	cat-file fsck-cache checkout-cache diff-tree rev-tree show-files \
+	check-files ls-tree merge-base merge-cache
+
+all: $(PROG)
+
+install: $(PROG)
+	install $(PROG) $(HOME)/bin/
+
+LIBS= -lssl -lz
+
+init-db: init-db.o
+
+update-cache: update-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o update-cache update-cache.o read-cache.o $(LIBS)
+
+show-diff: show-diff.o read-cache.o
+	$(CC) $(CFLAGS) -o show-diff show-diff.o read-cache.o $(LIBS)
+
+write-tree: write-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o write-tree write-tree.o read-cache.o $(LIBS)
+
+read-tree: read-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o read-tree read-tree.o read-cache.o $(LIBS)
+
+commit-tree: commit-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o commit-tree commit-tree.o read-cache.o $(LIBS)
+
+cat-file: cat-file.o read-cache.o
+	$(CC) $(CFLAGS) -o cat-file cat-file.o read-cache.o $(LIBS)
+
+fsck-cache: fsck-cache.o read-cache.o object.o commit.o tree.o blob.o
+	$(CC) $(CFLAGS) -o fsck-cache fsck-cache.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+checkout-cache: checkout-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o checkout-cache checkout-cache.o read-cache.o $(LIBS)
+
+diff-tree: diff-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o diff-tree diff-tree.o read-cache.o $(LIBS)
+
+rev-tree: rev-tree.o read-cache.o object.o commit.o tree.o blob.o
+	$(CC) $(CFLAGS) -o rev-tree rev-tree.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+show-files: show-files.o read-cache.o
+	$(CC) $(CFLAGS) -o show-files show-files.o read-cache.o $(LIBS)
+
+check-files: check-files.o read-cache.o
+	$(CC) $(CFLAGS) -o check-files check-files.o read-cache.o $(LIBS)
+
+ls-tree: ls-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o ls-tree ls-tree.o read-cache.o $(LIBS)
+
+merge-base: merge-base.o read-cache.o object.o commit.o tree.o blob.o
+	$(CC) $(CFLAGS) -o merge-base merge-base.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+merge-cache: merge-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o merge-cache merge-cache.o read-cache.o $(LIBS)
+
+read-cache.o: cache.h
+show-diff.o: cache.h
+
+clean:
+	rm -f *.o $(PROG)
+
+backup: clean
+	cd .. ; tar czvf dircache.tar.gz dir-cache
+
+
+Node-path: branches/left-sub/bang
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 17
+Node-copyfrom-path: branches/right/bang
+Text-copy-source-md5: 34c28f1d2dc6a9adeccc4265bf7516cb
+Text-copy-source-sha1: 0bc5bb345c0e71d28f784f12e0bd2d384c283062
+
+
+Node-path: branches/left-sub/urkkk
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 17
+Node-copyfrom-path: branches/right/urkkk
+Text-copy-source-md5: 5889c8392e16251b0c80927607a03036
+Text-copy-source-sha1: 3934264d277a0cf886b6b1c7f2b9e56da2525302
+
+
+Revision-number: 19
+Prop-content-length: 128
+Content-length: 128
+
+K 7
+svn:log
+V 30
+(r19) left sub-branch update 2
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:32.049244Z
+PROPS-END
+
+Node-path: branches/left-sub/wham_eth
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 6
+Text-content-md5: 757bcd5818572ef3f9580052617c1c8b
+Text-content-sha1: b165019b005c199237ba822c4404e771e93b654a
+Content-length: 16
+
+PROPS-END
+zowie
+
+
+Revision-number: 20
+Prop-content-length: 117
+Content-length: 117
+
+K 7
+svn:log
+V 19
+(r20) left update 5
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:33.049332Z
+PROPS-END
+
+Node-path: branches/left/glurpp
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 8
+Text-content-md5: 14a169f628e0bb59df9c2160649d0a30
+Text-content-sha1: ef7d929e52177767ecfcd28941f6b7f04b4131e3
+Content-length: 18
+
+PROPS-END
+eee_yow
+
+
+Revision-number: 21
+Prop-content-length: 146
+Content-length: 146
+
+K 7
+svn:log
+V 48
+(r21) Cherry-pick left sub-branch commit to left
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:36.041839Z
+PROPS-END
+
+Node-path: branches/left
+Node-kind: dir
+Node-action: change
+Prop-content-length: 56
+Content-length: 56
+
+K 13
+svn:mergeinfo
+V 21
+/branches/left-sub:19
+PROPS-END
+
+
+Node-path: branches/left/wham_eth
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 19
+Node-copyfrom-path: branches/left-sub/wham_eth
+Text-copy-source-md5: 757bcd5818572ef3f9580052617c1c8b
+Text-copy-source-sha1: b165019b005c199237ba822c4404e771e93b654a
+
+
+Revision-number: 22
+Prop-content-length: 133
+Content-length: 133
+
+K 7
+svn:log
+V 35
+(r22) Merge left sub-branch to left
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:39.045014Z
+PROPS-END
+
+Node-path: branches/left
+Node-kind: dir
+Node-action: change
+Prop-content-length: 79
+Content-length: 79
+
+K 13
+svn:mergeinfo
+V 44
+/branches/left-sub:4-19
+/branches/right:2-17
+PROPS-END
+
+
+Node-path: branches/left/Makefile
+Node-kind: file
+Node-action: change
+Text-content-length: 2713
+Text-content-md5: 0afbe34f244cd662b1f97d708c687f90
+Text-content-sha1: 46d9377d783e67a9b581da110352e799517c8a14
+Content-length: 2713
+
+# -DCOLLISION_CHECK if you believe that SHA1's
+# 1461501637330902918203684832716283019655932542976 hashes do not give you
+# enough guarantees about no collisions between objects ever hapenning.
+#
+# -DNSEC if you want git to care about sub-second file mtimes and ctimes.
+# Note that you need some new 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).
+CFLAGS=-g -O3 -Wall
+
+CC=gcc
+
+
+PROG=   update-cache show-diff init-db write-tree read-tree commit-tree \
+	cat-file fsck-cache checkout-cache diff-tree rev-tree show-files \
+	check-files ls-tree merge-base merge-cache
+
+all: $(PROG)
+
+install: $(PROG)
+	install $(PROG) $(HOME)/bin/
+
+LIBS= -lssl -lz
+
+init-db: init-db.o
+
+update-cache: update-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o update-cache update-cache.o read-cache.o $(LIBS)
+
+show-diff: show-diff.o read-cache.o
+	$(CC) $(CFLAGS) -o show-diff show-diff.o read-cache.o $(LIBS)
+
+write-tree: write-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o write-tree write-tree.o read-cache.o $(LIBS)
+
+read-tree: read-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o read-tree read-tree.o read-cache.o $(LIBS)
+
+commit-tree: commit-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o commit-tree commit-tree.o read-cache.o $(LIBS)
+
+cat-file: cat-file.o read-cache.o
+	$(CC) $(CFLAGS) -o cat-file cat-file.o read-cache.o $(LIBS)
+
+fsck-cache: fsck-cache.o read-cache.o object.o commit.o tree.o blob.o
+	$(CC) $(CFLAGS) -o fsck-cache fsck-cache.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+checkout-cache: checkout-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o checkout-cache checkout-cache.o read-cache.o $(LIBS)
+
+diff-tree: diff-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o diff-tree diff-tree.o read-cache.o $(LIBS)
+
+rev-tree: rev-tree.o read-cache.o object.o commit.o tree.o blob.o
+	$(CC) $(CFLAGS) -o rev-tree rev-tree.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+show-files: show-files.o read-cache.o
+	$(CC) $(CFLAGS) -o show-files show-files.o read-cache.o $(LIBS)
+
+check-files: check-files.o read-cache.o
+	$(CC) $(CFLAGS) -o check-files check-files.o read-cache.o $(LIBS)
+
+ls-tree: ls-tree.o read-cache.o
+	$(CC) $(CFLAGS) -o ls-tree ls-tree.o read-cache.o $(LIBS)
+
+merge-base: merge-base.o read-cache.o object.o commit.o tree.o blob.o
+	$(CC) $(CFLAGS) -o merge-base merge-base.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+merge-cache: merge-cache.o read-cache.o
+	$(CC) $(CFLAGS) -o merge-cache merge-cache.o read-cache.o $(LIBS)
+
+read-cache.o: cache.h
+show-diff.o: cache.h
+
+clean:
+	rm -f *.o $(PROG)
+
+backup: clean
+	cd .. ; tar czvf dircache.tar.gz dir-cache
+
+
+Node-path: branches/left/README
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 18
+Node-copyfrom-path: branches/left-sub/README
+Text-copy-source-md5: fdbcfb6be9afe1121862143f226b51cf
+Text-copy-source-sha1: 1d1f5ea4ceb584337ffe59b8980d92e3b78dfef4
+
+
+Node-path: branches/left/bang
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 18
+Node-copyfrom-path: branches/left-sub/bang
+Text-copy-source-md5: 34c28f1d2dc6a9adeccc4265bf7516cb
+Text-copy-source-sha1: 0bc5bb345c0e71d28f784f12e0bd2d384c283062
+
+
+Node-path: branches/left/urkkk
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 18
+Node-copyfrom-path: branches/left-sub/urkkk
+Text-copy-source-md5: 5889c8392e16251b0c80927607a03036
+Text-copy-source-sha1: 3934264d277a0cf886b6b1c7f2b9e56da2525302
+
+
+Revision-number: 23
+Prop-content-length: 125
+Content-length: 125
+
+K 7
+svn:log
+V 27
+(r23) Merge left to trunk 2
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:42.052798Z
+PROPS-END
+
+Node-path: trunk
+Node-kind: dir
+Node-action: change
+Prop-content-length: 99
+Content-length: 99
+
+K 13
+svn:mergeinfo
+V 64
+/branches/left:2-22
+/branches/left-sub:4-19
+/branches/right:2-17
+PROPS-END
+
+
+Node-path: trunk/README
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 22
+Node-copyfrom-path: branches/left/README
+Text-copy-source-md5: fdbcfb6be9afe1121862143f226b51cf
+Text-copy-source-sha1: 1d1f5ea4ceb584337ffe59b8980d92e3b78dfef4
+
+
+Node-path: trunk/glurpp
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 22
+Node-copyfrom-path: branches/left/glurpp
+Text-copy-source-md5: 14a169f628e0bb59df9c2160649d0a30
+Text-copy-source-sha1: ef7d929e52177767ecfcd28941f6b7f04b4131e3
+
+
+Node-path: trunk/urkkk
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 22
+Node-copyfrom-path: branches/left/urkkk
+Text-copy-source-md5: 5889c8392e16251b0c80927607a03036
+Text-copy-source-sha1: 3934264d277a0cf886b6b1c7f2b9e56da2525302
+
+
+Node-path: trunk/wham_eth
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 22
+Node-copyfrom-path: branches/left/wham_eth
+Text-copy-source-md5: 757bcd5818572ef3f9580052617c1c8b
+Text-copy-source-sha1: b165019b005c199237ba822c4404e771e93b654a
+
+
+Node-path: trunk/zlonk
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 22
+Node-copyfrom-path: branches/left/zlonk
+Text-copy-source-md5: 8b9d8c7c2aaa6167e7d3407a773bbbba
+Text-copy-source-sha1: 9716527ebd70a75c27625cacbeb2d897c6e86178
+
+
+Revision-number: 24
+Prop-content-length: 130
+Content-length: 130
+
+K 7
+svn:log
+V 32
+(r24) non-merge right to trunk 2
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:44.038434Z
+PROPS-END
+
+Node-path: trunk
+Node-kind: dir
+Node-action: change
+Prop-content-length: 99
+Content-length: 99
+
+K 13
+svn:mergeinfo
+V 64
+/branches/left:2-22
+/branches/left-sub:4-19
+/branches/right:2-22
+PROPS-END
+
+
+Revision-number: 25
+Prop-content-length: 129
+Content-length: 129
+
+K 7
+svn:log
+V 31
+(r25) make b1 branch from trunk
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:18:56.084589Z
+PROPS-END
+
+Node-path: branches/b1
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 24
+Node-copyfrom-path: trunk
+
+
+Revision-number: 26
+Prop-content-length: 129
+Content-length: 129
+
+K 7
+svn:log
+V 31
+(r26) make b2 branch from trunk
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:18:59.076940Z
+PROPS-END
+
+Node-path: branches/b2
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 25
+Node-copyfrom-path: trunk
+
+
+Revision-number: 27
+Prop-content-length: 115
+Content-length: 115
+
+K 7
+svn:log
+V 17
+(r27) b2 update 1
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:01.095762Z
+PROPS-END
+
+Node-path: branches/b2/b2file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 3
+Text-content-md5: 5edbdd57cba621eb3c6e601bf563b4dc
+Text-content-sha1: 9d4b38049776bd0a2074d67cad23f8eaed35a3b3
+Content-length: 13
+
+PROPS-END
+b2
+
+
+Revision-number: 28
+Prop-content-length: 115
+Content-length: 115
+
+K 7
+svn:log
+V 17
+(r28) b1 update 1
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:03.097465Z
+PROPS-END
+
+Node-path: branches/b1/b1file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 3
+Text-content-md5: 08778dfd9ac4f603231896aba7aad523
+Text-content-sha1: b551771aa4ad5b14123fc3bd98d89db2bc0edd4f
+Content-length: 13
+
+PROPS-END
+b1
+
+
+Revision-number: 29
+Prop-content-length: 121
+Content-length: 121
+
+K 7
+svn:log
+V 23
+(r29) Merge b1 to trunk
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:06.073175Z
+PROPS-END
+
+Node-path: trunk
+Node-kind: dir
+Node-action: change
+Prop-content-length: 118
+Content-length: 118
+
+K 13
+svn:mergeinfo
+V 83
+/branches/b1:25-28
+/branches/left:2-22
+/branches/left-sub:4-19
+/branches/right:2-22
+PROPS-END
+
+
+Node-path: trunk/b1file
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 28
+Node-copyfrom-path: branches/b1/b1file
+Text-copy-source-md5: 08778dfd9ac4f603231896aba7aad523
+Text-copy-source-sha1: b551771aa4ad5b14123fc3bd98d89db2bc0edd4f
+
+
+Revision-number: 30
+Prop-content-length: 143
+Content-length: 143
+
+K 7
+svn:log
+V 45
+(r30) trunk commit before merging trunk to b2
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:08.096353Z
+PROPS-END
+
+Node-path: trunk/trunkfile
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 6
+Text-content-md5: edf45fe5c98c5367733b39bbb2bb20d9
+Text-content-sha1: 7361d1685e5c86dfc523620cfaf598f196f86239
+Content-length: 16
+
+PROPS-END
+trunk
+
+
+Revision-number: 31
+Prop-content-length: 121
+Content-length: 121
+
+K 7
+svn:log
+V 23
+(r31) Merge trunk to b2
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:11.081541Z
+PROPS-END
+
+Node-path: branches/b2
+Node-kind: dir
+Node-action: change
+Prop-content-length: 131
+Content-length: 131
+
+K 13
+svn:mergeinfo
+V 96
+/branches/b1:25-28
+/branches/left:2-22
+/branches/left-sub:4-19
+/branches/right:2-22
+/trunk:26-30
+PROPS-END
+
+
+Node-path: branches/b2/b1file
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 30
+Node-copyfrom-path: trunk/b1file
+Text-copy-source-md5: 08778dfd9ac4f603231896aba7aad523
+Text-copy-source-sha1: b551771aa4ad5b14123fc3bd98d89db2bc0edd4f
+
+
+Node-path: branches/b2/trunkfile
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 30
+Node-copyfrom-path: trunk/trunkfile
+Text-copy-source-md5: edf45fe5c98c5367733b39bbb2bb20d9
+Text-copy-source-sha1: 7361d1685e5c86dfc523620cfaf598f196f86239
+
+
+Revision-number: 32
+Prop-content-length: 121
+Content-length: 121
+
+K 7
+svn:log
+V 23
+(r32) Merge b2 to trunk
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:14.117939Z
+PROPS-END
+
+Node-path: trunk
+Node-kind: dir
+Node-action: change
+Prop-content-length: 138
+Content-length: 138
+
+K 13
+svn:mergeinfo
+V 102
+/branches/b1:25-28
+/branches/b2:26-31
+/branches/left:2-22
+/branches/left-sub:4-19
+/branches/right:2-22
+PROPS-END
+
+
+Node-path: trunk/b2file
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 31
+Node-copyfrom-path: branches/b2/b2file
+Text-copy-source-md5: 5edbdd57cba621eb3c6e601bf563b4dc
+Text-copy-source-sha1: 9d4b38049776bd0a2074d67cad23f8eaed35a3b3
+
+
+Revision-number: 33
+Prop-content-length: 145
+Content-length: 145
+
+K 7
+svn:log
+V 47
+(r33) make f1 branch from trunk with a new file
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:17.105832Z
+PROPS-END
+
+Node-path: branches/f1
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 32
+Node-copyfrom-path: trunk
+
+
+Node-path: branches/f1/f1file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 3
+Text-content-md5: 2b1abc6b6c5c0018851f9f8e6475563b
+Text-content-sha1: aece6dfba588900e00d95601d22b4408d49580af
+Content-length: 13
+
+PROPS-END
+f1
+
+
+Revision-number: 34
+Prop-content-length: 145
+Content-length: 145
+
+K 7
+svn:log
+V 47
+(r34) make f2 branch from trunk with a new file
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:20.110057Z
+PROPS-END
+
+Node-path: branches/f2
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 33
+Node-copyfrom-path: trunk
+
+
+Node-path: branches/f2/f2file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 3
+Text-content-md5: 575c5638d60271457e54ab7d07309502
+Text-content-sha1: 1c49a440c352f3473efa9512255033b94dc7def0
+Content-length: 13
+
+PROPS-END
+f2
+
+
+Revision-number: 35
+Prop-content-length: 128
+Content-length: 128
+
+K 7
+svn:log
+V 30
+(r35) Merge f1 and f2 to trunk
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:24.081490Z
+PROPS-END
+
+Node-path: trunk
+Node-kind: dir
+Node-action: change
+Prop-content-length: 173
+Content-length: 173
+
+K 13
+svn:mergeinfo
+V 137
+/branches/b1:25-28
+/branches/b2:26-31
+/branches/f1:33-34
+/branches/f2:34
+/branches/left:2-22
+/branches/left-sub:4-19
+/branches/right:2-22
+PROPS-END
+
+
+Node-path: trunk/f1file
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 34
+Node-copyfrom-path: branches/f1/f1file
+Text-copy-source-md5: 2b1abc6b6c5c0018851f9f8e6475563b
+Text-copy-source-sha1: aece6dfba588900e00d95601d22b4408d49580af
+
+
+Node-path: trunk/f2file
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 34
+Node-copyfrom-path: branches/f2/f2file
+Text-copy-source-md5: 575c5638d60271457e54ab7d07309502
+Text-copy-source-sha1: 1c49a440c352f3473efa9512255033b94dc7def0
+
+
+Revision-number: 36
+Prop-content-length: 135
+Content-length: 135
+
+K 7
+svn:log
+V 37
+(r36) add subdirectory to left branch
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:26.113516Z
+PROPS-END
+
+Node-path: branches/left/subdir
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: branches/left/subdir/cowboy
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 7
+Text-content-md5: f1d6530278ad409e68cc675476ad995f
+Text-content-sha1: 732d9e3e5c391ffd767a98b45ddcc848de778cea
+Content-length: 17
+
+PROPS-END
+Yeehaw
+
+
+Revision-number: 37
+Prop-content-length: 123
+Content-length: 123
+
+K 7
+svn:log
+V 25
+(r37) merge left to trunk
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:29.073699Z
+PROPS-END
+
+Node-path: trunk
+Node-kind: dir
+Node-action: change
+Prop-content-length: 173
+Content-length: 173
+
+K 13
+svn:mergeinfo
+V 137
+/branches/b1:25-28
+/branches/b2:26-31
+/branches/f1:33-34
+/branches/f2:34
+/branches/left:2-36
+/branches/left-sub:4-19
+/branches/right:2-22
+PROPS-END
+
+
+Node-path: trunk/subdir
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 36
+Node-copyfrom-path: branches/left/subdir
+
+
+Revision-number: 38
+Prop-content-length: 123
+Content-length: 123
+
+K 7
+svn:log
+V 25
+(r38) make partial branch
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:32.072243Z
+PROPS-END
+
+Node-path: branches/partial
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 37
+Node-copyfrom-path: trunk/subdir
+
+
+Revision-number: 39
+Prop-content-length: 118
+Content-length: 118
+
+K 7
+svn:log
+V 20
+(r39) partial update
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:34.097961Z
+PROPS-END
+
+Node-path: branches/partial/palindromes
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 8
+Text-content-md5: 5d1c2024fb5efc4eef812856df1b080c
+Text-content-sha1: 5f8509ddd14c91a52864dd1447344e706f9bbc69
+Content-length: 18
+
+PROPS-END
+racecar
+
+
+Revision-number: 40
+Prop-content-length: 126
+Content-length: 126
+
+K 7
+svn:log
+V 28
+(r40) merge partial to trunk
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:37.080211Z
+PROPS-END
+
+Node-path: trunk/subdir
+Node-kind: dir
+Node-action: change
+Prop-content-length: 246
+Content-length: 246
+
+K 13
+svn:mergeinfo
+V 210
+/branches/b1/subdir:25-28
+/branches/b2/subdir:26-31
+/branches/f1/subdir:33-34
+/branches/f2/subdir:34
+/branches/left/subdir:2-36
+/branches/left-sub/subdir:4-19
+/branches/partial:38-39
+/branches/right/subdir:2-22
+PROPS-END
+
+
+Node-path: trunk/subdir/palindromes
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 39
+Node-copyfrom-path: branches/partial/palindromes
+Text-copy-source-md5: 5d1c2024fb5efc4eef812856df1b080c
+Text-copy-source-sha1: 5f8509ddd14c91a52864dd1447344e706f9bbc69
+
+
+Revision-number: 41
+Prop-content-length: 116
+Content-length: 116
+
+K 7
+svn:log
+V 18
+(r41) tagging v1.0
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:40.083460Z
+PROPS-END
+
+Node-path: tags/v1.0
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 40
+Node-copyfrom-path: trunk
+
+
+Revision-number: 42
+Prop-content-length: 131
+Content-length: 131
+
+K 7
+svn:log
+V 33
+(r42) make bugfix branch from tag
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:43.118075Z
+PROPS-END
+
+Node-path: branches/bugfix
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 41
+Node-copyfrom-path: tags/v1.0
+
+
+Revision-number: 43
+Prop-content-length: 120
+Content-length: 120
+
+K 7
+svn:log
+V 22
+(r43) commit to bugfix
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:45.079536Z
+PROPS-END
+
+Node-path: branches/bugfix/subdir/palindromes
+Node-kind: file
+Node-action: change
+Text-content-length: 14
+Text-content-md5: 3b12d98578a3f4320ba97e66da54fe5f
+Text-content-sha1: 672931c9e8ac2c408209efab2f015638b6d64042
+Content-length: 14
+
+racecar
+kayak
+
+
+Revision-number: 44
+Prop-content-length: 125
+Content-length: 125
+
+K 7
+svn:log
+V 27
+(r44) Merge BUGFIX to TRUNK
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:48.078914Z
+PROPS-END
+
+Node-path: trunk
+Node-kind: dir
+Node-action: change
+Prop-content-length: 210
+Content-length: 210
+
+K 13
+svn:mergeinfo
+V 174
+/branches/b1:25-28
+/branches/b2:26-31
+/branches/bugfix:42-43
+/branches/f1:33-34
+/branches/f2:34
+/branches/left:2-36
+/branches/left-sub:4-19
+/branches/right:2-22
+/tags/v1.0:41
+PROPS-END
+
+
+Node-path: trunk/subdir
+Node-kind: dir
+Node-action: change
+Prop-content-length: 297
+Content-length: 297
+
+K 13
+svn:mergeinfo
+V 261
+/branches/b1/subdir:25-28
+/branches/b2/subdir:26-31
+/branches/bugfix/subdir:42-43
+/branches/f1/subdir:33-34
+/branches/f2/subdir:34
+/branches/left/subdir:2-36
+/branches/left-sub/subdir:4-19
+/branches/partial:38-39
+/branches/right/subdir:2-22
+/tags/v1.0/subdir:41
+PROPS-END
+
+
+Node-path: trunk/subdir/palindromes
+Node-kind: file
+Node-action: change
+Text-content-length: 14
+Text-content-md5: 3b12d98578a3f4320ba97e66da54fe5f
+Text-content-sha1: 672931c9e8ac2c408209efab2f015638b6d64042
+Content-length: 14
+
+racecar
+kayak
+
+
diff --git a/t/t9152-svn-empty-dirs-after-gc.sh b/t/t9152-svn-empty-dirs-after-gc.sh
new file mode 100755
index 0000000..301e779
--- /dev/null
+++ b/t/t9152-svn-empty-dirs-after-gc.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Robert Zeh
+
+test_description='git svn creates empty directories, calls git gc, makes sure they are still empty'
+. ./lib-git-svn.sh
+
+test_expect_success 'initialize repo' '
+	for i in a b c d d/e d/e/f "weird file name"
+	do
+		svn_cmd mkdir -m "mkdir $i" "$svnrepo"/"$i"
+	done
+'
+
+test_expect_success 'clone' 'git svn clone "$svnrepo" cloned'
+
+test_expect_success 'git svn gc runs' '
+	(
+		cd cloned &&
+		git svn gc
+	)
+'
+
+test_expect_success 'git svn mkdirs recreates empty directories after git svn gc' '
+	(
+		cd cloned &&
+		rm -r * &&
+		git svn mkdirs &&
+		for i in a b c d d/e d/e/f "weird file name"
+		do
+			if ! test -d "$i"
+			then
+				echo >&2 "$i does not exist"
+				exit 1
+			fi
+		done
+	)
+'
+
+test_done
diff --git a/t/t9153-git-svn-rewrite-uuid.sh b/t/t9153-git-svn-rewrite-uuid.sh
new file mode 100755
index 0000000..88a2cfa
--- /dev/null
+++ b/t/t9153-git-svn-rewrite-uuid.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Jay Soffian
+#
+
+test_description='git svn --rewrite-uuid test'
+
+. ./lib-git-svn.sh
+
+uuid=6cc8ada4-5932-4b4a-8242-3534ed8a3232
+
+test_expect_success 'load svn repo' "
+	svnadmin load -q '$rawsvnrepo' < '$TEST_DIRECTORY/t9153/svn.dump' &&
+	git svn init --minimize-url --rewrite-uuid='$uuid' '$svnrepo' &&
+	git svn fetch
+	"
+
+test_expect_success 'verify uuid' "
+	git cat-file commit refs/remotes/git-svn~0 | \
+	   grep '^${git_svn_id}: .*@2 $uuid$' &&
+	git cat-file commit refs/remotes/git-svn~1 | \
+	   grep '^${git_svn_id}: .*@1 $uuid$'
+	"
+
+test_done
diff --git a/t/t9153/svn.dump b/t/t9153/svn.dump
new file mode 100644
index 0000000..0ddfe70
--- /dev/null
+++ b/t/t9153/svn.dump
@@ -0,0 +1,75 @@
+SVN-fs-dump-format-version: 2
+
+UUID: b4885626-c94f-4a6c-b179-00c030fc68e8
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2010-01-23T06:41:03.908576Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 109
+Content-length: 109
+
+K 7
+svn:log
+V 11
+initial foo
+K 10
+svn:author
+V 3
+jay
+K 8
+svn:date
+V 27
+2010-01-23T06:41:48.353776Z
+PROPS-END
+
+Node-path: foo
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 4
+Text-content-md5: d3b07384d113edec49eaa6238ad5ff00
+Text-content-sha1: f1d2d2f924e986ac86fdf7b36c94bcdf32beec15
+Content-length: 14
+
+PROPS-END
+foo
+
+
+Revision-number: 2
+Prop-content-length: 110
+Content-length: 110
+
+K 7
+svn:log
+V 12
+now with bar
+K 10
+svn:author
+V 3
+jay
+K 8
+svn:date
+V 27
+2010-01-23T06:42:14.214640Z
+PROPS-END
+
+Node-path: foo
+Node-kind: file
+Node-action: change
+Text-content-length: 8
+Text-content-md5: f47c75614087a8dd938ba4acff252494
+Text-content-sha1: 4e48e2c9a3d2ca8a708cb0cc545700544efb5021
+Content-length: 8
+
+foo
+bar
+
+
diff --git a/t/t9154-git-svn-fancy-glob.sh b/t/t9154-git-svn-fancy-glob.sh
new file mode 100755
index 0000000..a6a56a6
--- /dev/null
+++ b/t/t9154-git-svn-fancy-glob.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Jay Soffian
+#
+
+test_description='git svn fancy glob test'
+
+. ./lib-git-svn.sh
+
+test_expect_success 'load svn repo' "
+	svnadmin load -q '$rawsvnrepo' < '$TEST_DIRECTORY/t9154/svn.dump' &&
+	git svn init --minimize-url -T trunk '$svnrepo' &&
+	git svn fetch
+	"
+
+test_expect_success 'add red branch' "
+	git config svn-remote.svn.branches 'branches/{red}:refs/remotes/*' &&
+	git svn fetch &&
+	git rev-parse refs/remotes/red &&
+	test_must_fail git rev-parse refs/remotes/green &&
+	test_must_fail git rev-parse refs/remotes/blue
+	"
+
+test_expect_success 'add green branch' "
+	GIT_CONFIG=.git/svn/.metadata git config --unset svn-remote.svn.branches-maxRev &&
+	git config svn-remote.svn.branches 'branches/{red,green}:refs/remotes/*' &&
+	git svn fetch &&
+	git rev-parse refs/remotes/red &&
+	git rev-parse refs/remotes/green &&
+	test_must_fail git rev-parse refs/remotes/blue
+	"
+
+test_expect_success 'add all branches' "
+	GIT_CONFIG=.git/svn/.metadata git config --unset svn-remote.svn.branches-maxRev &&
+	git config svn-remote.svn.branches 'branches/*:refs/remotes/*' &&
+	git svn fetch &&
+	git rev-parse refs/remotes/red &&
+	git rev-parse refs/remotes/green &&
+	git rev-parse refs/remotes/blue
+	"
+
+test_done
diff --git a/t/t9154/svn.dump b/t/t9154/svn.dump
new file mode 100644
index 0000000..3dfabb6
--- /dev/null
+++ b/t/t9154/svn.dump
@@ -0,0 +1,222 @@
+SVN-fs-dump-format-version: 2
+
+UUID: a18093a0-5f0b-44e0-8d88-8911ac7078db
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2010-01-23T07:40:25.660053Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 104
+Content-length: 104
+
+K 7
+svn:log
+V 7
+initial
+K 10
+svn:author
+V 3
+jay
+K 8
+svn:date
+V 27
+2010-01-23T07:41:33.636365Z
+PROPS-END
+
+Node-path: trunk
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: trunk/foo
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 4
+Text-content-md5: d3b07384d113edec49eaa6238ad5ff00
+Text-content-sha1: f1d2d2f924e986ac86fdf7b36c94bcdf32beec15
+Content-length: 14
+
+PROPS-END
+foo
+
+
+Revision-number: 2
+Prop-content-length: 110
+Content-length: 110
+
+K 7
+svn:log
+V 12
+add branches
+K 10
+svn:author
+V 3
+jay
+K 8
+svn:date
+V 27
+2010-01-23T07:42:37.290694Z
+PROPS-END
+
+Node-path: branches
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: branches/blue
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 1
+Node-copyfrom-path: trunk
+
+
+Node-path: branches/green
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 1
+Node-copyfrom-path: trunk
+
+
+Node-path: branches/red
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 1
+Node-copyfrom-path: trunk
+
+
+Revision-number: 3
+Prop-content-length: 108
+Content-length: 108
+
+K 7
+svn:log
+V 10
+red change
+K 10
+svn:author
+V 3
+jay
+K 8
+svn:date
+V 27
+2010-01-23T07:43:02.208918Z
+PROPS-END
+
+Node-path: branches/red/foo
+Node-kind: file
+Node-action: change
+Text-content-length: 8
+Text-content-md5: 64c3c8cf7d0233ab7627623a68888bd1
+Text-content-sha1: 95a0492027876adfd3891ec71ee37b79ee44d640
+Content-length: 8
+
+foo
+red
+
+
+Revision-number: 4
+Prop-content-length: 110
+Content-length: 110
+
+K 7
+svn:log
+V 12
+green change
+K 10
+svn:author
+V 3
+jay
+K 8
+svn:date
+V 27
+2010-01-23T07:43:15.746586Z
+PROPS-END
+
+Node-path: branches/green/foo
+Node-kind: file
+Node-action: change
+Text-content-length: 10
+Text-content-md5: 0209b6450891abc033d5eaaa9d3a8023
+Text-content-sha1: 87fc3bef9faeec48c0cd61dfc9851db377fdccf7
+Content-length: 10
+
+foo
+green
+
+
+Revision-number: 5
+Prop-content-length: 109
+Content-length: 109
+
+K 7
+svn:log
+V 11
+blue change
+K 10
+svn:author
+V 3
+jay
+K 8
+svn:date
+V 27
+2010-01-23T07:43:29.364811Z
+PROPS-END
+
+Node-path: branches/blue/foo
+Node-kind: file
+Node-action: change
+Text-content-length: 9
+Text-content-md5: 9fbe4c13d0bae86386ae5209b2e6b275
+Text-content-sha1: cc4575083459a16f9aaef796c4a2456d64691ba0
+Content-length: 9
+
+foo
+blue
+
+
+Revision-number: 6
+Prop-content-length: 110
+Content-length: 110
+
+K 7
+svn:log
+V 12
+trunk change
+K 10
+svn:author
+V 3
+jay
+K 8
+svn:date
+V 27
+2010-01-23T07:44:01.313130Z
+PROPS-END
+
+Node-path: trunk/foo
+Node-kind: file
+Node-action: change
+Text-content-length: 10
+Text-content-md5: 1c4db977d7a57c3bae582aab87948516
+Text-content-sha1: 469c08df449e702cf2a1fe746244a9ef3f837fad
+Content-length: 10
+
+foo
+trunk
+
+
diff --git a/t/t9155-git-svn-fetch-deleted-tag.sh b/t/t9155-git-svn-fetch-deleted-tag.sh
new file mode 100755
index 0000000..a486a98
--- /dev/null
+++ b/t/t9155-git-svn-fetch-deleted-tag.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+test_description='git svn fetch deleted tag'
+
+. ./lib-git-svn.sh
+
+test_expect_success 'setup svn repo' '
+	mkdir -p import/trunk/subdir &&
+	mkdir -p import/branches &&
+	mkdir -p import/tags &&
+	echo "base" >import/trunk/subdir/file &&
+	svn_cmd import -m "import for git svn" import "$svnrepo" &&
+	rm -rf import &&
+
+	svn_cmd mkdir -m "create mybranch directory" "$svnrepo/branches/mybranch" &&
+	svn_cmd cp -m "create branch mybranch" "$svnrepo/trunk" "$svnrepo/branches/mybranch/trunk" &&
+
+	svn_cmd co "$svnrepo/trunk" svn_project &&
+	(cd svn_project &&
+		echo "trunk change" >>subdir/file &&
+		svn_cmd ci -m "trunk change" subdir/file &&
+
+		svn_cmd switch "$svnrepo/branches/mybranch/trunk" &&
+		echo "branch change" >>subdir/file &&
+		svn_cmd ci -m "branch change" subdir/file
+	) &&
+
+	svn_cmd cp -m "create mytag attempt 1" -r5 "$svnrepo/trunk/subdir" "$svnrepo/tags/mytag" &&
+	svn_cmd rm -m "delete mytag attempt 1" "$svnrepo/tags/mytag" &&
+	svn_cmd cp -m "create mytag attempt 2" -r5 "$svnrepo/branches/mybranch/trunk/subdir" "$svnrepo/tags/mytag"
+'
+
+test_expect_success 'fetch deleted tags from same revision with checksum error' '
+	git svn init --stdlayout "$svnrepo" git_project &&
+	cd git_project &&
+	git svn fetch &&
+
+	git diff --exit-code mybranch:trunk/subdir/file tags/mytag:file &&
+	git diff --exit-code master:subdir/file tags/mytag^:file
+'
+
+test_done
diff --git a/t/t9156-git-svn-fetch-deleted-tag-2.sh b/t/t9156-git-svn-fetch-deleted-tag-2.sh
new file mode 100755
index 0000000..5ce7e2f
--- /dev/null
+++ b/t/t9156-git-svn-fetch-deleted-tag-2.sh
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+test_description='git svn fetch deleted tag 2'
+
+. ./lib-git-svn.sh
+
+test_expect_success 'setup svn repo' '
+	mkdir -p import/branches &&
+	mkdir -p import/tags &&
+	mkdir -p import/trunk/subdir1 &&
+	mkdir -p import/trunk/subdir2 &&
+	mkdir -p import/trunk/subdir3 &&
+	echo "file1" >import/trunk/subdir1/file &&
+	echo "file2" >import/trunk/subdir2/file &&
+	echo "file3" >import/trunk/subdir3/file &&
+	svn_cmd import -m "import for git svn" import "$svnrepo" &&
+	rm -rf import &&
+
+	svn_cmd co "$svnrepo/trunk" svn_project &&
+	(cd svn_project &&
+		echo "change1" >>subdir1/file &&
+		echo "change2" >>subdir2/file &&
+		echo "change3" >>subdir3/file &&
+		svn_cmd ci -m "change" .
+	) &&
+
+	svn_cmd cp -m "create mytag 1" -r2 "$svnrepo/trunk/subdir1" "$svnrepo/tags/mytag" &&
+	svn_cmd rm -m "delete mytag 1" "$svnrepo/tags/mytag" &&
+	svn_cmd cp -m "create mytag 2" -r2 "$svnrepo/trunk/subdir2" "$svnrepo/tags/mytag" &&
+	svn_cmd rm -m "delete mytag 2" "$svnrepo/tags/mytag" &&
+	svn_cmd cp -m "create mytag 3" -r2 "$svnrepo/trunk/subdir3" "$svnrepo/tags/mytag"
+'
+
+test_expect_success 'fetch deleted tags from same revision with no checksum error' '
+	git svn init --stdlayout "$svnrepo" git_project &&
+	cd git_project &&
+	git svn fetch &&
+
+	git diff --exit-code master:subdir3/file tags/mytag:file &&
+	git diff --exit-code master:subdir2/file tags/mytag^:file &&
+	git diff --exit-code master:subdir1/file tags/mytag^^:file
+'
+
+test_done
diff --git a/t/t9157-git-svn-fetch-merge.sh b/t/t9157-git-svn-fetch-merge.sh
new file mode 100755
index 0000000..991d2aa
--- /dev/null
+++ b/t/t9157-git-svn-fetch-merge.sh
@@ -0,0 +1,58 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Steven Walter
+#
+
+test_description='git svn merge detection'
+. ./lib-git-svn.sh
+
+svn_ver="$(svn --version --quiet)"
+case $svn_ver in
+0.* | 1.[0-4].*)
+	skip_all="skipping git-svn test - SVN too old ($svn_ver)"
+	test_done
+	;;
+esac
+
+test_expect_success 'initialize source svn repo' '
+	svn_cmd mkdir -m x "$svnrepo"/trunk &&
+	svn_cmd mkdir -m x "$svnrepo"/branches &&
+	svn_cmd co "$svnrepo"/trunk "$SVN_TREE" &&
+	(
+		cd "$SVN_TREE" &&
+		touch foo &&
+		svn add foo &&
+		svn commit -m "initial commit" &&
+		svn cp -m branch "$svnrepo"/trunk "$svnrepo"/branches/branch1 &&
+		touch bar &&
+		svn add bar &&
+		svn commit -m x &&
+		svn cp -m branch "$svnrepo"/trunk "$svnrepo"/branches/branch2 &&
+		svn switch "$svnrepo"/branches/branch1 &&
+		touch baz &&
+		svn add baz &&
+		svn commit -m x &&
+		svn switch "$svnrepo"/trunk &&
+		svn merge "$svnrepo"/branches/branch1 &&
+		svn commit -m "merge" &&
+		svn switch "$svnrepo"/branches/branch1 &&
+		svn commit -m x &&
+		svn switch "$svnrepo"/branches/branch2 &&
+		svn merge "$svnrepo"/branches/branch1 &&
+		svn commit -m "merge branch1" &&
+		svn switch "$svnrepo"/trunk &&
+		svn merge "$svnrepo"/branches/branch2 &&
+		svn resolved baz &&
+		svn commit -m "merge branch2"
+	) &&
+	rm -rf "$SVN_TREE"
+'
+
+test_expect_success 'clone svn repo' '
+	git svn init -s "$svnrepo" &&
+	git svn fetch
+'
+
+test_expect_success 'verify merge commit' 'git rev-parse HEAD^2'
+
+test_done
diff --git a/t/t9158-git-svn-mergeinfo.sh b/t/t9158-git-svn-mergeinfo.sh
new file mode 100755
index 0000000..8c9539e
--- /dev/null
+++ b/t/t9158-git-svn-mergeinfo.sh
@@ -0,0 +1,54 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Steven Walter
+#
+
+test_description='git svn mergeinfo propagation'
+
+. ./lib-git-svn.sh
+
+say 'define NO_SVN_TESTS to skip git svn tests'
+
+test_expect_success 'initialize source svn repo' '
+	svn_cmd mkdir -m x "$svnrepo"/trunk &&
+	svn_cmd co "$svnrepo"/trunk "$SVN_TREE" &&
+	(
+		cd "$SVN_TREE" &&
+		touch foo &&
+		svn_cmd add foo &&
+		svn_cmd commit -m "initial commit"
+	) &&
+	rm -rf "$SVN_TREE"
+'
+
+test_expect_success 'clone svn repo' '
+	git svn init "$svnrepo"/trunk &&
+	git svn fetch
+'
+
+test_expect_success 'change svn:mergeinfo' '
+	touch bar &&
+	git add bar &&
+	git commit -m "bar" &&
+	git svn dcommit --mergeinfo="/branches/foo:1-10"
+'
+
+test_expect_success 'verify svn:mergeinfo' '
+	mergeinfo=$(svn_cmd propget svn:mergeinfo "$svnrepo"/trunk)
+	test "$mergeinfo" = "/branches/foo:1-10"
+'
+
+test_expect_success 'change svn:mergeinfo multiline' '
+	touch baz &&
+	git add baz &&
+	git commit -m "baz" &&
+	git svn dcommit --mergeinfo="/branches/bar:1-10 /branches/other:3-5,8,10-11"
+'
+
+test_expect_success 'verify svn:mergeinfo multiline' '
+	mergeinfo=$(svn_cmd propget svn:mergeinfo "$svnrepo"/trunk)
+	test "$mergeinfo" = "/branches/bar:1-10
+/branches/other:3-5,8,10-11"
+'
+
+test_done
diff --git a/t/t9159-git-svn-no-parent-mergeinfo.sh b/t/t9159-git-svn-no-parent-mergeinfo.sh
new file mode 100755
index 0000000..69e4815
--- /dev/null
+++ b/t/t9159-git-svn-no-parent-mergeinfo.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+test_description='git svn handling of root commits in merge ranges'
+. ./lib-git-svn.sh
+
+svn_ver="$(svn --version --quiet)"
+case $svn_ver in
+0.* | 1.[0-4].*)
+	skip_all="skipping git-svn test - SVN too old ($svn_ver)"
+	test_done
+	;;
+esac
+
+test_expect_success 'test handling of root commits in merge ranges' '
+	mkdir -p init/trunk init/branches init/tags &&
+	echo "r1" > init/trunk/file.txt &&
+	svn_cmd import -m "initial import" init "$svnrepo" &&
+	svn_cmd co "$svnrepo" tmp &&
+	(
+		cd tmp &&
+		echo "r2" > trunk/file.txt &&
+		svn_cmd commit -m "Modify file.txt on trunk" &&
+		svn_cmd cp trunk@1 branches/a &&
+		svn_cmd commit -m "Create branch a from trunk r1" &&
+		svn_cmd propset svn:mergeinfo /trunk:1-2 branches/a &&
+		svn_cmd commit -m "Fake merge of trunk r2 into branch a" &&
+		mkdir branches/b &&
+		echo "r5" > branches/b/file2.txt &&
+		svn_cmd add branches/b &&
+		svn_cmd commit -m "Create branch b from thin air" &&
+		echo "r6" > branches/b/file2.txt &&
+		svn_cmd commit -m "Modify file2.txt on branch b" &&
+		svn_cmd cp branches/b@5 branches/c &&
+		svn_cmd commit -m "Create branch c from branch b r5" &&
+		svn_cmd propset svn:mergeinfo /branches/b:5-6 branches/c &&
+		svn_cmd commit -m "Fake merge of branch b r6 into branch c"
+	) &&
+	git svn init -s "$svnrepo" &&
+	git svn fetch
+	'
+
+test_done
diff --git a/t/t9160-git-svn-preserve-empty-dirs.sh b/t/t9160-git-svn-preserve-empty-dirs.sh
new file mode 100755
index 0000000..b4a4434
--- /dev/null
+++ b/t/t9160-git-svn-preserve-empty-dirs.sh
@@ -0,0 +1,153 @@
+#!/bin/sh
+#
+# Copyright (c) 2011 Ray Chen
+#
+
+test_description='git svn test (option --preserve-empty-dirs)
+
+This test uses git to clone a Subversion repository that contains empty
+directories, and checks that corresponding directories are created in the
+local Git repository with placeholder files.'
+
+. ./lib-git-svn.sh
+
+say 'define NO_SVN_TESTS to skip git svn tests'
+GIT_REPO=git-svn-repo
+
+test_expect_success 'initialize source svn repo containing empty dirs' '
+	svn_cmd mkdir -m x "$svnrepo"/trunk &&
+	svn_cmd co "$svnrepo"/trunk "$SVN_TREE" &&
+	(
+		cd "$SVN_TREE" &&
+		mkdir -p 1 2 3/a 3/b 4 5 6 &&
+		echo "First non-empty file"  > 2/file1.txt &&
+		echo "Second non-empty file" > 2/file2.txt &&
+		echo "Third non-empty file"  > 3/a/file1.txt &&
+		echo "Fourth non-empty file" > 3/b/file1.txt &&
+		svn_cmd add 1 2 3 4 5 6 &&
+		svn_cmd commit -m "initial commit" &&
+
+		mkdir 4/a &&
+		svn_cmd add 4/a &&
+		svn_cmd commit -m "nested empty directory" &&
+		mkdir 4/a/b &&
+		svn_cmd add 4/a/b &&
+		svn_cmd commit -m "deeply nested empty directory" &&
+		mkdir 4/a/b/c &&
+		svn_cmd add 4/a/b/c &&
+		svn_cmd commit -m "really deeply nested empty directory" &&
+		echo "Kill the placeholder file" > 4/a/b/c/foo &&
+		svn_cmd add 4/a/b/c/foo &&
+		svn_cmd commit -m "Regular file to remove placeholder" &&
+
+		svn_cmd del 2/file2.txt &&
+		svn_cmd del 3/b &&
+		svn_cmd commit -m "delete non-last entry in directory" &&
+
+		svn_cmd del 2/file1.txt &&
+		svn_cmd del 3/a &&
+		svn_cmd commit -m "delete last entry in directory" &&
+
+		echo "Conflict file" > 5/.placeholder &&
+		mkdir 6/.placeholder &&
+		svn_cmd add 5/.placeholder 6/.placeholder &&
+		svn_cmd commit -m "Placeholder Namespace conflict"
+	) &&
+	rm -rf "$SVN_TREE"
+'
+
+test_expect_success 'clone svn repo with --preserve-empty-dirs' '
+	git svn clone "$svnrepo"/trunk --preserve-empty-dirs "$GIT_REPO"
+'
+
+# "$GIT_REPO"/1 should only contain the placeholder file.
+test_expect_success 'directory empty from inception' '
+	test -f "$GIT_REPO"/1/.gitignore &&
+	test $(find "$GIT_REPO"/1 -type f | wc -l) = "1"
+'
+
+# "$GIT_REPO"/2 and "$GIT_REPO"/3 should only contain the placeholder file.
+test_expect_success 'directory empty from subsequent svn commit' '
+	test -f "$GIT_REPO"/2/.gitignore &&
+	test $(find "$GIT_REPO"/2 -type f | wc -l) = "1" &&
+	test -f "$GIT_REPO"/3/.gitignore &&
+	test $(find "$GIT_REPO"/3 -type f | wc -l) = "1"
+'
+
+# No placeholder files should exist in "$GIT_REPO"/4, even though one was
+# generated for every sub-directory at some point in the repo's history.
+test_expect_success 'add entry to previously empty directory' '
+	test $(find "$GIT_REPO"/4 -type f | wc -l) = "1" &&
+	test -f "$GIT_REPO"/4/a/b/c/foo
+'
+
+# The HEAD~2 commit should not have introduced .gitignore placeholder files.
+test_expect_success 'remove non-last entry from directory' '
+	(
+		cd "$GIT_REPO" &&
+		git checkout HEAD~2
+	) &&
+	test_must_fail test -f "$GIT_REPO"/2/.gitignore &&
+	test_must_fail test -f "$GIT_REPO"/3/.gitignore
+'
+
+# After re-cloning the repository with --placeholder-file specified, there
+# should be 5 files named ".placeholder" in the local Git repo.
+test_expect_success 'clone svn repo with --placeholder-file specified' '
+	rm -rf "$GIT_REPO" &&
+	git svn clone "$svnrepo"/trunk --preserve-empty-dirs \
+		--placeholder-file=.placeholder "$GIT_REPO" &&
+	find "$GIT_REPO" -type f -name ".placeholder" &&
+	test $(find "$GIT_REPO" -type f -name ".placeholder" | wc -l) = "5"
+'
+
+# "$GIT_REPO"/5/.placeholder should be a file, and non-empty.
+test_expect_success 'placeholder namespace conflict with file' '
+	test -s "$GIT_REPO"/5/.placeholder
+'
+
+# "$GIT_REPO"/6/.placeholder should be a directory, and the "$GIT_REPO"/6 tree
+# should only contain one file: the placeholder.
+test_expect_success 'placeholder namespace conflict with directory' '
+	test -d "$GIT_REPO"/6/.placeholder &&
+	test -f "$GIT_REPO"/6/.placeholder/.placeholder &&
+	test $(find "$GIT_REPO"/6 -type f | wc -l) = "1"
+'
+
+# Prepare a second set of svn commits to test persistence during rebase.
+test_expect_success 'second set of svn commits and rebase' '
+	svn_cmd co "$svnrepo"/trunk "$SVN_TREE" &&
+	(
+		cd "$SVN_TREE" &&
+		mkdir -p 7 &&
+		echo "This should remove placeholder" > 1/file1.txt &&
+		echo "This should not remove placeholder" > 5/file1.txt &&
+		svn_cmd add 7 1/file1.txt 5/file1.txt &&
+		svn_cmd commit -m "subsequent svn commit for persistence tests"
+	) &&
+	rm -rf "$SVN_TREE" &&
+	(
+		cd "$GIT_REPO" &&
+		git svn rebase
+	)
+'
+
+# Check that --preserve-empty-dirs and --placeholder-file flag state
+# stays persistent over multiple invocations.
+test_expect_success 'flag persistence during subsqeuent rebase' '
+	test -f "$GIT_REPO"/7/.placeholder &&
+	test $(find "$GIT_REPO"/7 -type f | wc -l) = "1"
+'
+
+# Check that placeholder files are properly removed when unnecessary,
+# even across multiple invocations.
+test_expect_success 'placeholder list persistence during subsqeuent rebase' '
+	test -f "$GIT_REPO"/1/file1.txt &&
+	test $(find "$GIT_REPO"/1 -type f | wc -l) = "1" &&
+
+	test -f "$GIT_REPO"/5/file1.txt &&
+	test -f "$GIT_REPO"/5/.placeholder &&
+	test $(find "$GIT_REPO"/5 -type f | wc -l) = "2"
+'
+
+test_done
diff --git a/t/t9161-git-svn-mergeinfo-push.sh b/t/t9161-git-svn-mergeinfo-push.sh
new file mode 100755
index 0000000..6ef0c0b
--- /dev/null
+++ b/t/t9161-git-svn-mergeinfo-push.sh
@@ -0,0 +1,104 @@
+#!/bin/sh
+#
+# Portions copyright (c) 2007, 2009 Sam Vilain
+# Portions copyright (c) 2011 Bryan Jacobs
+#
+
+test_description='git-svn svn mergeinfo propagation'
+
+. ./lib-git-svn.sh
+
+test_expect_success 'load svn dump' "
+	svnadmin load -q '$rawsvnrepo' \
+	  < '$TEST_DIRECTORY/t9161/branches.dump' &&
+	git svn init --minimize-url -R svnmerge \
+	  -T trunk -b branches '$svnrepo' &&
+	git svn fetch --all
+	"
+
+test_expect_success 'propagate merge information' '
+	git config svn.pushmergeinfo yes &&
+	git checkout svnb1 &&
+	git merge --no-ff svnb2 &&
+	git svn dcommit
+	'
+
+test_expect_success 'check svn:mergeinfo' '
+	mergeinfo=$(svn_cmd propget svn:mergeinfo "$svnrepo"/branches/svnb1)
+	test "$mergeinfo" = "/branches/svnb2:3,8"
+	'
+
+test_expect_success 'merge another branch' '
+	git merge --no-ff svnb3 &&
+	git svn dcommit
+	'
+
+test_expect_success 'check primary parent mergeinfo respected' '
+	mergeinfo=$(svn_cmd propget svn:mergeinfo "$svnrepo"/branches/svnb1)
+	test "$mergeinfo" = "/branches/svnb2:3,8
+/branches/svnb3:4,9"
+	'
+
+test_expect_success 'merge existing merge' '
+	git merge --no-ff svnb4 &&
+	git svn dcommit
+	'
+
+test_expect_success "check both parents' mergeinfo respected" '
+	mergeinfo=$(svn_cmd propget svn:mergeinfo "$svnrepo"/branches/svnb1)
+	test "$mergeinfo" = "/branches/svnb2:3,8
+/branches/svnb3:4,9
+/branches/svnb4:5-6,10-12
+/branches/svnb5:6,11"
+	'
+
+test_expect_success 'make further commits to branch' '
+	git checkout svnb2 &&
+	touch newb2file &&
+	git add newb2file &&
+	git commit -m "later b2 commit" &&
+	touch newb2file-2 &&
+	git add newb2file-2 &&
+	git commit -m "later b2 commit 2" &&
+	git svn dcommit
+	'
+
+test_expect_success 'second forward merge' '
+	git checkout svnb1 &&
+	git merge --no-ff svnb2 &&
+	git svn dcommit
+	'
+
+test_expect_success 'check new mergeinfo added' '
+	mergeinfo=$(svn_cmd propget svn:mergeinfo "$svnrepo"/branches/svnb1)
+	test "$mergeinfo" = "/branches/svnb2:3,8,16-17
+/branches/svnb3:4,9
+/branches/svnb4:5-6,10-12
+/branches/svnb5:6,11"
+	'
+
+test_expect_success 'reintegration merge' '
+	git checkout svnb4 &&
+	git merge --no-ff svnb1 &&
+	git svn dcommit
+	'
+
+test_expect_success 'check reintegration mergeinfo' '
+	mergeinfo=$(svn_cmd propget svn:mergeinfo "$svnrepo"/branches/svnb4)
+	test "$mergeinfo" = "/branches/svnb1:2-4,7-9,13-18
+/branches/svnb2:3,8,16-17
+/branches/svnb3:4,9
+/branches/svnb4:5-6,10-12
+/branches/svnb5:6,11"
+	'
+
+test_expect_success 'dcommit a merge at the top of a stack' '
+	git checkout svnb1 &&
+	touch anotherfile &&
+	git add anotherfile &&
+	git commit -m "a commit" &&
+	git merge svnb4 &&
+	git svn dcommit
+	'
+
+test_done
diff --git a/t/t9161/branches.dump b/t/t9161/branches.dump
new file mode 100644
index 0000000..e61c3e7
--- /dev/null
+++ b/t/t9161/branches.dump
@@ -0,0 +1,374 @@
+SVN-fs-dump-format-version: 2
+
+UUID: 1ef08553-f2d1-45df-b38c-19af6b7c926d
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2011-09-02T16:08:02.941384Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 114
+Content-length: 114
+
+K 7
+svn:log
+V 12
+Base commit
+
+K 10
+svn:author
+V 7
+bjacobs
+K 8
+svn:date
+V 27
+2011-09-02T16:08:27.205062Z
+PROPS-END
+
+Node-path: branches
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: trunk
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Revision-number: 2
+Prop-content-length: 121
+Content-length: 121
+
+K 7
+svn:log
+V 19
+Create branch svnb1
+K 10
+svn:author
+V 7
+bjacobs
+K 8
+svn:date
+V 27
+2011-09-02T16:09:43.628137Z
+PROPS-END
+
+Node-path: branches/svnb1
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 1
+Node-copyfrom-path: trunk
+
+
+Revision-number: 3
+Prop-content-length: 121
+Content-length: 121
+
+K 7
+svn:log
+V 19
+Create branch svnb2
+K 10
+svn:author
+V 7
+bjacobs
+K 8
+svn:date
+V 27
+2011-09-02T16:09:46.339930Z
+PROPS-END
+
+Node-path: branches/svnb2
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 1
+Node-copyfrom-path: trunk
+
+
+Revision-number: 4
+Prop-content-length: 121
+Content-length: 121
+
+K 7
+svn:log
+V 19
+Create branch svnb3
+K 10
+svn:author
+V 7
+bjacobs
+K 8
+svn:date
+V 27
+2011-09-02T16:09:49.394515Z
+PROPS-END
+
+Node-path: branches/svnb3
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 1
+Node-copyfrom-path: trunk
+
+
+Revision-number: 5
+Prop-content-length: 121
+Content-length: 121
+
+K 7
+svn:log
+V 19
+Create branch svnb4
+K 10
+svn:author
+V 7
+bjacobs
+K 8
+svn:date
+V 27
+2011-09-02T16:09:54.114607Z
+PROPS-END
+
+Node-path: branches/svnb4
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 1
+Node-copyfrom-path: trunk
+
+
+Revision-number: 6
+Prop-content-length: 121
+Content-length: 121
+
+K 7
+svn:log
+V 19
+Create branch svnb5
+K 10
+svn:author
+V 7
+bjacobs
+K 8
+svn:date
+V 27
+2011-09-02T16:09:58.602623Z
+PROPS-END
+
+Node-path: branches/svnb5
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 1
+Node-copyfrom-path: trunk
+
+
+Revision-number: 7
+Prop-content-length: 110
+Content-length: 110
+
+K 7
+svn:log
+V 9
+b1 commit
+K 10
+svn:author
+V 7
+bjacobs
+K 8
+svn:date
+V 27
+2011-09-02T16:10:20.292369Z
+PROPS-END
+
+Node-path: branches/svnb1/b1file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 0
+Text-content-md5: d41d8cd98f00b204e9800998ecf8427e
+Text-content-sha1: da39a3ee5e6b4b0d3255bfef95601890afd80709
+Content-length: 10
+
+PROPS-END
+
+
+Revision-number: 8
+Prop-content-length: 110
+Content-length: 110
+
+K 7
+svn:log
+V 9
+b2 commit
+K 10
+svn:author
+V 7
+bjacobs
+K 8
+svn:date
+V 27
+2011-09-02T16:10:38.429199Z
+PROPS-END
+
+Node-path: branches/svnb2/b2file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 0
+Text-content-md5: d41d8cd98f00b204e9800998ecf8427e
+Text-content-sha1: da39a3ee5e6b4b0d3255bfef95601890afd80709
+Content-length: 10
+
+PROPS-END
+
+
+Revision-number: 9
+Prop-content-length: 110
+Content-length: 110
+
+K 7
+svn:log
+V 9
+b3 commit
+K 10
+svn:author
+V 7
+bjacobs
+K 8
+svn:date
+V 27
+2011-09-02T16:10:52.843023Z
+PROPS-END
+
+Node-path: branches/svnb3/b3file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 0
+Text-content-md5: d41d8cd98f00b204e9800998ecf8427e
+Text-content-sha1: da39a3ee5e6b4b0d3255bfef95601890afd80709
+Content-length: 10
+
+PROPS-END
+
+
+Revision-number: 10
+Prop-content-length: 110
+Content-length: 110
+
+K 7
+svn:log
+V 9
+b4 commit
+K 10
+svn:author
+V 7
+bjacobs
+K 8
+svn:date
+V 27
+2011-09-02T16:11:17.489870Z
+PROPS-END
+
+Node-path: branches/svnb4/b4file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 0
+Text-content-md5: d41d8cd98f00b204e9800998ecf8427e
+Text-content-sha1: da39a3ee5e6b4b0d3255bfef95601890afd80709
+Content-length: 10
+
+PROPS-END
+
+
+Revision-number: 11
+Prop-content-length: 110
+Content-length: 110
+
+K 7
+svn:log
+V 9
+b5 commit
+K 10
+svn:author
+V 7
+bjacobs
+K 8
+svn:date
+V 27
+2011-09-02T16:11:32.277404Z
+PROPS-END
+
+Node-path: branches/svnb5/b5file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 0
+Text-content-md5: d41d8cd98f00b204e9800998ecf8427e
+Text-content-sha1: da39a3ee5e6b4b0d3255bfef95601890afd80709
+Content-length: 10
+
+PROPS-END
+
+
+Revision-number: 12
+Prop-content-length: 192
+Content-length: 192
+
+K 7
+svn:log
+V 90
+Merge remote-tracking branch 'svnb5' into HEAD
+
+* svnb5:
+  b5 commit
+  Create branch svnb5
+K 10
+svn:author
+V 7
+bjacobs
+K 8
+svn:date
+V 27
+2011-09-02T16:11:54.274722Z
+PROPS-END
+
+Node-path: branches/svnb4
+Node-kind: dir
+Node-action: change
+Prop-content-length: 56
+Content-length: 56
+
+K 13
+svn:mergeinfo
+V 21
+/branches/svnb5:6,11
+
+PROPS-END
+
+
+Node-path: branches/svnb4/b5file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 0
+Text-content-md5: d41d8cd98f00b204e9800998ecf8427e
+Text-content-sha1: da39a3ee5e6b4b0d3255bfef95601890afd80709
+Content-length: 10
+
+PROPS-END
+
+
diff --git a/t/t9162-git-svn-dcommit-interactive.sh b/t/t9162-git-svn-dcommit-interactive.sh
new file mode 100755
index 0000000..e38d9fa
--- /dev/null
+++ b/t/t9162-git-svn-dcommit-interactive.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+#
+# Copyright (c) 2011 Frédéric Heitzmann
+
+test_description='git svn dcommit --interactive series'
+. ./lib-git-svn.sh
+
+test_expect_success 'initialize repo' '
+	svn_cmd mkdir -m"mkdir test-interactive" "$svnrepo/test-interactive" &&
+	git svn clone "$svnrepo/test-interactive" test-interactive &&
+	cd test-interactive &&
+	touch foo && git add foo && git commit -m"foo: first commit" &&
+	git svn dcommit
+	'
+
+test_expect_success 'answers: y [\n] yes' '
+	(
+		echo "change #1" >> foo && git commit -a -m"change #1" &&
+		echo "change #2" >> foo && git commit -a -m"change #2" &&
+		echo "change #3" >> foo && git commit -a -m"change #3" &&
+		( echo "y
+
+y" | GIT_SVN_NOTTY=1 git svn dcommit --interactive ) &&
+		test $(git rev-parse HEAD) = $(git rev-parse remotes/git-svn)
+	)
+	'
+
+test_expect_success 'answers: yes yes no' '
+	(
+		echo "change #1" >> foo && git commit -a -m"change #1" &&
+		echo "change #2" >> foo && git commit -a -m"change #2" &&
+		echo "change #3" >> foo && git commit -a -m"change #3" &&
+		( echo "yes
+yes
+no" | GIT_SVN_NOTTY=1 git svn dcommit --interactive ) &&
+		test $(git rev-parse HEAD^^^) = $(git rev-parse remotes/git-svn) &&
+		git reset --hard remotes/git-svn
+	)
+	'
+
+test_expect_success 'answers: yes quit' '
+	(
+		echo "change #1" >> foo && git commit -a -m"change #1" &&
+		echo "change #2" >> foo && git commit -a -m"change #2" &&
+		echo "change #3" >> foo && git commit -a -m"change #3" &&
+		( echo "yes
+quit" | GIT_SVN_NOTTY=1 git svn dcommit --interactive ) &&
+		test $(git rev-parse HEAD^^^) = $(git rev-parse remotes/git-svn) &&
+		git reset --hard remotes/git-svn
+	)
+	'
+
+test_expect_success 'answers: all' '
+	(
+		echo "change #1" >> foo && git commit -a -m"change #1" &&
+		echo "change #2" >> foo && git commit -a -m"change #2" &&
+		echo "change #3" >> foo && git commit -a -m"change #3" &&
+		( echo "all" | GIT_SVN_NOTTY=1 git svn dcommit --interactive ) &&
+		test $(git rev-parse HEAD) = $(git rev-parse remotes/git-svn) &&
+		git reset --hard remotes/git-svn
+	)
+	'
+
+test_done
diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh
index fc3795d..b59be9a 100755
--- a/t/t9200-git-cvsexportcommit.sh
+++ b/t/t9200-git-cvsexportcommit.sh
@@ -5,22 +5,23 @@
 test_description='Test export of commits to CVS'
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-prereq-FILEMODE.sh
 
 if ! test_have_prereq PERL; then
-	say 'skipping git cvsexportcommit tests, perl not available'
+	skip_all='skipping git cvsexportcommit tests, perl not available'
 	test_done
 fi
 
 cvs >/dev/null 2>&1
 if test $? -ne 1
 then
-    say 'skipping git cvsexportcommit tests, cvs not found'
+    skip_all='skipping git cvsexportcommit tests, cvs not found'
     test_done
 fi
 
-CVSROOT=$(pwd)/cvsroot
-CVSWORK=$(pwd)/cvswork
-GIT_DIR=$(pwd)/.git
+CVSROOT=$PWD/cvsroot
+CVSWORK=$PWD/cvswork
+GIT_DIR=$PWD/.git
 export CVSROOT CVSWORK GIT_DIR
 
 rm -rf "$CVSROOT" "$CVSWORK"
@@ -49,8 +50,8 @@
     'mkdir A B C D E F &&
      echo hello1 >A/newfile1.txt &&
      echo hello2 >B/newfile2.txt &&
-     cp "$TEST_DIRECTORY"/test9200a.png C/newfile3.png &&
-     cp "$TEST_DIRECTORY"/test9200a.png D/newfile4.png &&
+     cp "$TEST_DIRECTORY"/test-binary-1.png C/newfile3.png &&
+     cp "$TEST_DIRECTORY"/test-binary-1.png D/newfile4.png &&
      git add A/newfile1.txt &&
      git add B/newfile2.txt &&
      git add C/newfile3.png &&
@@ -63,10 +64,10 @@
      check_entries B "newfile2.txt/1.1/" &&
      check_entries C "newfile3.png/1.1/-kb" &&
      check_entries D "newfile4.png/1.1/-kb" &&
-     diff A/newfile1.txt ../A/newfile1.txt &&
-     diff B/newfile2.txt ../B/newfile2.txt &&
-     diff C/newfile3.png ../C/newfile3.png &&
-     diff D/newfile4.png ../D/newfile4.png
+     test_cmp A/newfile1.txt ../A/newfile1.txt &&
+     test_cmp B/newfile2.txt ../B/newfile2.txt &&
+     test_cmp C/newfile3.png ../C/newfile3.png &&
+     test_cmp D/newfile4.png ../D/newfile4.png
      )'
 
 test_expect_success \
@@ -75,8 +76,8 @@
      rm -f B/newfile2.txt &&
      rm -f C/newfile3.png &&
      echo Hello5  >E/newfile5.txt &&
-     cp "$TEST_DIRECTORY"/test9200b.png D/newfile4.png &&
-     cp "$TEST_DIRECTORY"/test9200a.png F/newfile6.png &&
+     cp "$TEST_DIRECTORY"/test-binary-2.png D/newfile4.png &&
+     cp "$TEST_DIRECTORY"/test-binary-1.png F/newfile6.png &&
      git add E/newfile5.txt &&
      git add F/newfile6.png &&
      git commit -a -m "Test: Remove, add and update" &&
@@ -89,10 +90,10 @@
      check_entries D "newfile4.png/1.2/-kb" &&
      check_entries E "newfile5.txt/1.1/" &&
      check_entries F "newfile6.png/1.1/-kb" &&
-     diff A/newfile1.txt ../A/newfile1.txt &&
-     diff D/newfile4.png ../D/newfile4.png &&
-     diff E/newfile5.txt ../E/newfile5.txt &&
-     diff F/newfile6.png ../F/newfile6.png
+     test_cmp A/newfile1.txt ../A/newfile1.txt &&
+     test_cmp D/newfile4.png ../D/newfile4.png &&
+     test_cmp E/newfile5.txt ../E/newfile5.txt &&
+     test_cmp F/newfile6.png ../F/newfile6.png
      )'
 
 # Should fail (but only on the git cvsexportcommit stage)
@@ -137,9 +138,9 @@
      check_entries D "" &&
      check_entries E "newfile5.txt/1.1/" &&
      check_entries F "newfile6.png/1.1/-kb" &&
-     diff A/newfile1.txt ../A/newfile1.txt &&
-     diff E/newfile5.txt ../E/newfile5.txt &&
-     diff F/newfile6.png ../F/newfile6.png
+     test_cmp A/newfile1.txt ../A/newfile1.txt &&
+     test_cmp E/newfile5.txt ../E/newfile5.txt &&
+     test_cmp F/newfile6.png ../F/newfile6.png
      )'
 
 test_expect_success \
@@ -155,8 +156,8 @@
      check_entries D "" &&
      check_entries E "newfile5.txt/1.1/" &&
      check_entries F "newfile6.png/1.1/-kb" &&
-     diff E/newfile5.txt ../E/newfile5.txt &&
-     diff F/newfile6.png ../F/newfile6.png
+     test_cmp E/newfile5.txt ../E/newfile5.txt &&
+     test_cmp F/newfile6.png ../F/newfile6.png
      )'
 
 test_expect_success \
@@ -164,7 +165,7 @@
      'mkdir "G g" &&
       echo ok then >"G g/with spaces.txt" &&
       git add "G g/with spaces.txt" && \
-      cp "$TEST_DIRECTORY"/test9200a.png "G g/with spaces.png" && \
+      cp "$TEST_DIRECTORY"/test-binary-1.png "G g/with spaces.png" && \
       git add "G g/with spaces.png" &&
       git commit -a -m "With spaces" &&
       id=$(git rev-list --max-count=1 HEAD) &&
@@ -176,7 +177,7 @@
 test_expect_success \
      'Update file with spaces in file name' \
      'echo Ok then >>"G g/with spaces.txt" &&
-      cat "$TEST_DIRECTORY"/test9200a.png >>"G g/with spaces.png" && \
+      cat "$TEST_DIRECTORY"/test-binary-1.png >>"G g/with spaces.png" && \
       git add "G g/with spaces.png" &&
       git commit -a -m "Update with spaces" &&
       id=$(git rev-list --max-count=1 HEAD) &&
@@ -201,7 +202,7 @@
      'mkdir -p Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö &&
       echo Foo >Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.txt &&
       git add Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.txt &&
-      cp "$TEST_DIRECTORY"/test9200a.png Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.png &&
+      cp "$TEST_DIRECTORY"/test-binary-1.png Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.png &&
       git add Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.png &&
       git commit -a -m "Går det så går det" && \
       id=$(git rev-list --max-count=1 HEAD) &&
@@ -229,11 +230,6 @@
       test_must_fail git cvsexportcommit -c $id
       )'
 
-if ! test "$(git config --bool core.filemode)" = false
-then
-	test_set_prereq FILEMODE
-fi
-
 test_expect_success FILEMODE \
      'Retain execute bit' \
      'mkdir G &&
@@ -325,7 +321,7 @@
 
 	(mkdir shared &&
 	 cd shared &&
-	 unset GIT_DIR &&
+	 sane_unset GIT_DIR &&
 	 cvs co . &&
 	 git init &&
 	 git add " space" &&
diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
index 821be7c..2fcf269 100755
--- a/t/t9300-fast-import.sh
+++ b/t/t9300-fast-import.sh
@@ -7,6 +7,30 @@
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
 
+# Print $1 bytes from stdin to stdout.
+#
+# This could be written as "head -c $1", but IRIX "head" does not
+# support the -c option.
+head_c () {
+	"$PERL_PATH" -e '
+		my $len = $ARGV[1];
+		while ($len > 0) {
+			my $s;
+			my $nread = sysread(STDIN, $s, $len);
+			die "cannot read: $!" unless defined($nread);
+			print $s;
+			$len -= $nread;
+		}
+	' - "$1"
+}
+
+verify_packs () {
+	for p in .git/objects/pack/*.pack
+	do
+		git verify-pack "$@" "$p" || return
+	done
+}
+
 file2_data='file2
 second line of EOF'
 
@@ -23,11 +47,26 @@
 file6_data='#!/bin/sh
 echo "$@"'
 
+>empty
+
+test_expect_success 'setup: have pipes?' '
+	rm -f frob &&
+	if mkfifo frob
+	then
+		test_set_prereq PIPE
+	fi
+'
+
 ###
 ### series A
 ###
 
 test_tick
+
+test_expect_success 'empty stream succeeds' '
+	git fast-import </dev/null
+'
+
 cat >input <<INPUT_END
 blob
 mark :2
@@ -62,14 +101,21 @@
 An annotated tag without a tagger
 EOF
 
+tag series-A-blob
+from :3
+data <<EOF
+An annotated tag that annotates a blob.
+EOF
+
 INPUT_END
 test_expect_success \
     'A: create pack from stdin' \
     'git fast-import --export-marks=marks.out <input &&
 	 git whatchanged master'
-test_expect_success \
-	'A: verify pack' \
-	'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
+
+test_expect_success 'A: verify pack' '
+	verify_packs
+'
 
 cat >expect <<EOF
 author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
@@ -120,6 +166,18 @@
 '
 
 cat >expect <<EOF
+object $(git rev-parse refs/heads/master:file3)
+type blob
+tag series-A-blob
+
+An annotated tag that annotates a blob.
+EOF
+test_expect_success 'A: verify tag/series-A-blob' '
+	git cat-file tag tags/series-A-blob >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<EOF
 :2 `git rev-parse --verify master:file2`
 :3 `git rev-parse --verify master:file3`
 :4 `git rev-parse --verify master:file4`
@@ -138,6 +196,55 @@
 	test_cmp expect marks.new'
 
 test_tick
+new_blob=$(echo testing | git hash-object --stdin)
+cat >input <<INPUT_END
+tag series-A-blob-2
+from $(git rev-parse refs/heads/master:file3)
+data <<EOF
+Tag blob by sha1.
+EOF
+
+blob
+mark :6
+data <<EOF
+testing
+EOF
+
+commit refs/heads/new_blob
+committer  <> 0 +0000
+data 0
+M 644 :6 new_blob
+#pretend we got sha1 from fast-import
+ls "new_blob"
+
+tag series-A-blob-3
+from $new_blob
+data <<EOF
+Tag new_blob.
+EOF
+INPUT_END
+
+cat >expect <<EOF
+object $(git rev-parse refs/heads/master:file3)
+type blob
+tag series-A-blob-2
+
+Tag blob by sha1.
+object $new_blob
+type blob
+tag series-A-blob-3
+
+Tag new_blob.
+EOF
+
+test_expect_success \
+	'A: tag blob by sha1' \
+	'git fast-import <input &&
+	git cat-file tag tags/series-A-blob-2 >actual &&
+	git cat-file tag tags/series-A-blob-3 >>actual &&
+	test_cmp expect actual'
+
+test_tick
 cat >input <<INPUT_END
 commit refs/heads/verify--import-marks
 committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
@@ -153,9 +260,11 @@
 	'A: verify marks import does not crash' \
 	'git fast-import --import-marks=marks.out <input &&
 	 git whatchanged verify--import-marks'
-test_expect_success \
-	'A: verify pack' \
-	'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
+
+test_expect_success 'A: verify pack' '
+	verify_packs
+'
+
 cat >expect <<EOF
 :000000 100755 0000000000000000000000000000000000000000 7123f7f44e39be127c5eb701e5968176ee9d78b1 A	copy-of-file2
 EOF
@@ -166,6 +275,63 @@
 	 test `git rev-parse --verify master:file2` \
 	    = `git rev-parse --verify verify--import-marks:copy-of-file2`'
 
+test_tick
+mt=$(git hash-object --stdin < /dev/null)
+: >input.blob
+: >marks.exp
+: >tree.exp
+
+cat >input.commit <<EOF
+commit refs/heads/verify--dump-marks
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+test the sparse array dumping routines with exponentially growing marks
+COMMIT
+EOF
+
+i=0
+l=4
+m=6
+n=7
+while test "$i" -lt 27; do
+    cat >>input.blob <<EOF
+blob
+mark :$l
+data 0
+blob
+mark :$m
+data 0
+blob
+mark :$n
+data 0
+EOF
+    echo "M 100644 :$l l$i" >>input.commit
+    echo "M 100644 :$m m$i" >>input.commit
+    echo "M 100644 :$n n$i" >>input.commit
+
+    echo ":$l $mt" >>marks.exp
+    echo ":$m $mt" >>marks.exp
+    echo ":$n $mt" >>marks.exp
+
+    printf "100644 blob $mt\tl$i\n" >>tree.exp
+    printf "100644 blob $mt\tm$i\n" >>tree.exp
+    printf "100644 blob $mt\tn$i\n" >>tree.exp
+
+    l=$(($l + $l))
+    m=$(($m + $m))
+    n=$(($l + $n))
+
+    i=$((1 + $i))
+done
+
+sort tree.exp > tree.exp_s
+
+test_expect_success 'A: export marks with large values' '
+	cat input.blob input.commit | git fast-import --export-marks=marks.large &&
+	git ls-tree refs/heads/verify--dump-marks >tree.out &&
+	test_cmp tree.exp_s tree.out &&
+	test_cmp marks.exp marks.large'
+
 ###
 ### series B
 ###
@@ -235,6 +401,105 @@
 	 test `git rev-parse master` = `git rev-parse TEMP_TAG^`'
 rm -f .git/TEMP_TAG
 
+git gc 2>/dev/null >/dev/null
+git prune 2>/dev/null >/dev/null
+
+cat >input <<INPUT_END
+commit refs/heads/empty-committer-1
+committer  <> $GIT_COMMITTER_DATE
+data <<COMMIT
+empty commit
+COMMIT
+INPUT_END
+test_expect_success 'B: accept empty committer' '
+	git fast-import <input &&
+	out=$(git fsck) &&
+	echo "$out" &&
+	test -z "$out"
+'
+git update-ref -d refs/heads/empty-committer-1 || true
+
+git gc 2>/dev/null >/dev/null
+git prune 2>/dev/null >/dev/null
+
+cat >input <<INPUT_END
+commit refs/heads/empty-committer-2
+committer <a@b.com> $GIT_COMMITTER_DATE
+data <<COMMIT
+empty commit
+COMMIT
+INPUT_END
+test_expect_success 'B: accept and fixup committer with no name' '
+	git fast-import <input &&
+	out=$(git fsck) &&
+	echo "$out" &&
+	test -z "$out"
+'
+git update-ref -d refs/heads/empty-committer-2 || true
+
+git gc 2>/dev/null >/dev/null
+git prune 2>/dev/null >/dev/null
+
+cat >input <<INPUT_END
+commit refs/heads/invalid-committer
+committer Name email> $GIT_COMMITTER_DATE
+data <<COMMIT
+empty commit
+COMMIT
+INPUT_END
+test_expect_success 'B: fail on invalid committer (1)' '
+	test_must_fail git fast-import <input
+'
+git update-ref -d refs/heads/invalid-committer || true
+
+cat >input <<INPUT_END
+commit refs/heads/invalid-committer
+committer Name <e<mail> $GIT_COMMITTER_DATE
+data <<COMMIT
+empty commit
+COMMIT
+INPUT_END
+test_expect_success 'B: fail on invalid committer (2)' '
+	test_must_fail git fast-import <input
+'
+git update-ref -d refs/heads/invalid-committer || true
+
+cat >input <<INPUT_END
+commit refs/heads/invalid-committer
+committer Name <email>> $GIT_COMMITTER_DATE
+data <<COMMIT
+empty commit
+COMMIT
+INPUT_END
+test_expect_success 'B: fail on invalid committer (3)' '
+	test_must_fail git fast-import <input
+'
+git update-ref -d refs/heads/invalid-committer || true
+
+cat >input <<INPUT_END
+commit refs/heads/invalid-committer
+committer Name <email $GIT_COMMITTER_DATE
+data <<COMMIT
+empty commit
+COMMIT
+INPUT_END
+test_expect_success 'B: fail on invalid committer (4)' '
+	test_must_fail git fast-import <input
+'
+git update-ref -d refs/heads/invalid-committer || true
+
+cat >input <<INPUT_END
+commit refs/heads/invalid-committer
+committer Name<email> $GIT_COMMITTER_DATE
+data <<COMMIT
+empty commit
+COMMIT
+INPUT_END
+test_expect_success 'B: fail on invalid committer (5)' '
+	test_must_fail git fast-import <input
+'
+git update-ref -d refs/heads/invalid-committer || true
+
 ###
 ### series C
 ###
@@ -259,12 +524,14 @@
     'C: incremental import create pack from stdin' \
     'git fast-import <input &&
 	 git whatchanged branch'
-test_expect_success \
-	'C: verify pack' \
-	'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
+
+test_expect_success 'C: verify pack' '
+	verify_packs
+'
+
 test_expect_success \
 	'C: validate reuse existing blob' \
-	'test $newf = `git rev-parse --verify branch:file2/newf`
+	'test $newf = `git rev-parse --verify branch:file2/newf` &&
 	 test $oldf = `git rev-parse --verify branch:file2/oldf`'
 
 cat >expect <<EOF
@@ -317,9 +584,10 @@
     'D: inline data in commit' \
     'git fast-import <input &&
 	 git whatchanged branch'
-test_expect_success \
-	'D: verify pack' \
-	'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
+
+test_expect_success 'D: verify pack' '
+	verify_packs
+'
 
 cat >expect <<EOF
 :000000 100755 0000000000000000000000000000000000000000 35a59026a33beac1569b1c7f66f3090ce9c09afc A	newdir/exec.sh
@@ -363,9 +631,10 @@
 test_expect_success \
     'E: rfc2822 date, --date-format=rfc2822' \
     'git fast-import --date-format=rfc2822 <input'
-test_expect_success \
-	'E: verify pack' \
-	'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
+
+test_expect_success 'E: verify pack' '
+	verify_packs
+'
 
 cat >expect <<EOF
 author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> 1170778938 -0500
@@ -414,9 +683,10 @@
 		fi
 	 fi
 	'
-test_expect_success \
-	'F: verify pack' \
-	'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
+
+test_expect_success 'F: verify pack' '
+	verify_packs
+'
 
 cat >expect <<EOF
 tree `git rev-parse branch~1^{tree}`
@@ -450,9 +720,11 @@
 test_expect_success \
     'G: non-fast-forward update forced' \
     'git fast-import --force <input'
-test_expect_success \
-	'G: verify pack' \
-	'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
+
+test_expect_success 'G: verify pack' '
+	verify_packs
+'
+
 test_expect_success \
 	'G: branch changed, but logged' \
 	'test $old_branch != `git rev-parse --verify branch^0` &&
@@ -487,9 +759,10 @@
     'H: deletall, add 1' \
     'git fast-import <input &&
 	 git whatchanged H'
-test_expect_success \
-	'H: verify pack' \
-	'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
+
+test_expect_success 'H: verify pack' '
+	verify_packs
+'
 
 cat >expect <<EOF
 :100755 000000 f1fb5da718392694d0076d677d6d0e364c79b0bc 0000000000000000000000000000000000000000 D	file2/newf
@@ -565,6 +838,18 @@
 	'test 1 = `git rev-list J | wc -l` &&
 	 test 0 = `git ls-tree J | wc -l`'
 
+cat >input <<INPUT_END
+reset refs/heads/J2
+
+tag wrong_tag
+from refs/heads/J2
+data <<EOF
+Tag branch that was reset.
+EOF
+INPUT_END
+test_expect_success \
+	'J: tag must fail on empty branch' \
+	'test_must_fail git fast-import <input'
 ###
 ### series K
 ###
@@ -645,6 +930,47 @@
 	 git diff-tree --abbrev --raw L^ L >output &&
 	 test_cmp expect output'
 
+cat >input <<INPUT_END
+blob
+mark :1
+data <<EOF
+the data
+EOF
+
+commit refs/heads/L2
+committer C O Mitter <committer@example.com> 1112912473 -0700
+data <<COMMIT
+init L2
+COMMIT
+M 644 :1 a/b/c
+M 644 :1 a/b/d
+M 644 :1 a/e/f
+
+commit refs/heads/L2
+committer C O Mitter <committer@example.com> 1112912473 -0700
+data <<COMMIT
+update L2
+COMMIT
+C a g
+C a/e g/b
+M 644 :1 g/b/h
+INPUT_END
+
+cat <<EOF >expect
+g/b/f
+g/b/h
+EOF
+
+test_expect_success \
+    'L: nested tree copy does not corrupt deltas' \
+	'git fast-import <input &&
+	git ls-tree L2 g/b/ >tmp &&
+	cat tmp | cut -f 2 >actual &&
+	test_cmp expect actual &&
+	git fsck `git rev-parse L2`'
+
+git update-ref -d refs/heads/L2
+
 ###
 ### series M
 ###
@@ -796,6 +1122,341 @@
 	'git fast-import <input &&
 	 test `git rev-parse N2^{tree}` = `git rev-parse N3^{tree}`'
 
+test_expect_success \
+	'N: copy directory by id' \
+	'cat >expect <<-\EOF &&
+	:100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100	file2/newf	file3/newf
+	:100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100	file2/oldf	file3/oldf
+	EOF
+	 subdir=$(git rev-parse refs/heads/branch^0:file2) &&
+	 cat >input <<-INPUT_END &&
+	commit refs/heads/N4
+	committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+	data <<COMMIT
+	copy by tree hash
+	COMMIT
+
+	from refs/heads/branch^0
+	M 040000 $subdir file3
+	INPUT_END
+	 git fast-import <input &&
+	 git diff-tree -C --find-copies-harder -r N4^ N4 >actual &&
+	 compare_diff_raw expect actual'
+
+test_expect_success PIPE 'N: read and copy directory' '
+	cat >expect <<-\EOF
+	:100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100	file2/newf	file3/newf
+	:100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100	file2/oldf	file3/oldf
+	EOF
+	git update-ref -d refs/heads/N4 &&
+	rm -f backflow &&
+	mkfifo backflow &&
+	(
+		exec <backflow &&
+		cat <<-EOF &&
+		commit refs/heads/N4
+		committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+		data <<COMMIT
+		copy by tree hash, part 2
+		COMMIT
+
+		from refs/heads/branch^0
+		ls "file2"
+		EOF
+		read mode type tree filename &&
+		echo "M 040000 $tree file3"
+	) |
+	git fast-import --cat-blob-fd=3 3>backflow &&
+	git diff-tree -C --find-copies-harder -r N4^ N4 >actual &&
+	compare_diff_raw expect actual
+'
+
+test_expect_success PIPE 'N: empty directory reads as missing' '
+	cat <<-\EOF >expect &&
+	OBJNAME
+	:000000 100644 OBJNAME OBJNAME A	unrelated
+	EOF
+	echo "missing src" >expect.response &&
+	git update-ref -d refs/heads/read-empty &&
+	rm -f backflow &&
+	mkfifo backflow &&
+	(
+		exec <backflow &&
+		cat <<-EOF &&
+		commit refs/heads/read-empty
+		committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+		data <<COMMIT
+		read "empty" (missing) directory
+		COMMIT
+
+		M 100644 inline src/greeting
+		data <<BLOB
+		hello
+		BLOB
+		C src/greeting dst1/non-greeting
+		C src/greeting unrelated
+		# leave behind "empty" src directory
+		D src/greeting
+		ls "src"
+		EOF
+		read -r line &&
+		printf "%s\n" "$line" >response &&
+		cat <<-\EOF
+		D dst1
+		D dst2
+		EOF
+	) |
+	git fast-import --cat-blob-fd=3 3>backflow &&
+	test_cmp expect.response response &&
+	git rev-list read-empty |
+	git diff-tree -r --root --stdin |
+	sed "s/$_x40/OBJNAME/g" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success \
+	'N: copy root directory by tree hash' \
+	'cat >expect <<-\EOF &&
+	:100755 000000 f1fb5da718392694d0076d677d6d0e364c79b0bc 0000000000000000000000000000000000000000 D	file3/newf
+	:100644 000000 7123f7f44e39be127c5eb701e5968176ee9d78b1 0000000000000000000000000000000000000000 D	file3/oldf
+	EOF
+	 root=$(git rev-parse refs/heads/branch^0^{tree}) &&
+	 cat >input <<-INPUT_END &&
+	commit refs/heads/N6
+	committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+	data <<COMMIT
+	copy root directory by tree hash
+	COMMIT
+
+	from refs/heads/branch^0
+	M 040000 $root ""
+	INPUT_END
+	 git fast-import <input &&
+	 git diff-tree -C --find-copies-harder -r N4 N6 >actual &&
+	 compare_diff_raw expect actual'
+
+test_expect_success \
+	'N: delete directory by copying' \
+	'cat >expect <<-\EOF &&
+	OBJID
+	:100644 000000 OBJID OBJID D	foo/bar/qux
+	OBJID
+	:000000 100644 OBJID OBJID A	foo/bar/baz
+	:000000 100644 OBJID OBJID A	foo/bar/qux
+	EOF
+	 empty_tree=$(git mktree </dev/null) &&
+	 cat >input <<-INPUT_END &&
+	commit refs/heads/N-delete
+	committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+	data <<COMMIT
+	collect data to be deleted
+	COMMIT
+
+	deleteall
+	M 100644 inline foo/bar/baz
+	data <<DATA_END
+	hello
+	DATA_END
+	C "foo/bar/baz" "foo/bar/qux"
+	C "foo/bar/baz" "foo/bar/quux/1"
+	C "foo/bar/baz" "foo/bar/quuux"
+	M 040000 $empty_tree foo/bar/quux
+	M 040000 $empty_tree foo/bar/quuux
+
+	commit refs/heads/N-delete
+	committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+	data <<COMMIT
+	delete subdirectory
+	COMMIT
+
+	M 040000 $empty_tree foo/bar/qux
+	INPUT_END
+	 git fast-import <input &&
+	 git rev-list N-delete |
+		git diff-tree -r --stdin --root --always |
+		sed -e "s/$_x40/OBJID/g" >actual &&
+	 test_cmp expect actual'
+
+test_expect_success \
+	'N: modify copied tree' \
+	'cat >expect <<-\EOF &&
+	:100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 C100	newdir/interesting	file3/file5
+	:100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100	file2/newf	file3/newf
+	:100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100	file2/oldf	file3/oldf
+	EOF
+	 subdir=$(git rev-parse refs/heads/branch^0:file2) &&
+	 cat >input <<-INPUT_END &&
+	commit refs/heads/N5
+	committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+	data <<COMMIT
+	copy by tree hash
+	COMMIT
+
+	from refs/heads/branch^0
+	M 040000 $subdir file3
+
+	commit refs/heads/N5
+	committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+	data <<COMMIT
+	modify directory copy
+	COMMIT
+
+	M 644 inline file3/file5
+	data <<EOF
+	$file5_data
+	EOF
+	INPUT_END
+	 git fast-import <input &&
+	 git diff-tree -C --find-copies-harder -r N5^^ N5 >actual &&
+	 compare_diff_raw expect actual'
+
+test_expect_success \
+	'N: reject foo/ syntax' \
+	'subdir=$(git rev-parse refs/heads/branch^0:file2) &&
+	 test_must_fail git fast-import <<-INPUT_END
+	commit refs/heads/N5B
+	committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+	data <<COMMIT
+	copy with invalid syntax
+	COMMIT
+
+	from refs/heads/branch^0
+	M 040000 $subdir file3/
+	INPUT_END'
+
+test_expect_success \
+	'N: reject foo/ syntax in copy source' \
+	'test_must_fail git fast-import <<-INPUT_END
+	commit refs/heads/N5C
+	committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+	data <<COMMIT
+	copy with invalid syntax
+	COMMIT
+
+	from refs/heads/branch^0
+	C file2/ file3
+	INPUT_END'
+
+test_expect_success \
+	'N: reject foo/ syntax in rename source' \
+	'test_must_fail git fast-import <<-INPUT_END
+	commit refs/heads/N5D
+	committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+	data <<COMMIT
+	rename with invalid syntax
+	COMMIT
+
+	from refs/heads/branch^0
+	R file2/ file3
+	INPUT_END'
+
+test_expect_success \
+	'N: reject foo/ syntax in ls argument' \
+	'test_must_fail git fast-import <<-INPUT_END
+	commit refs/heads/N5E
+	committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+	data <<COMMIT
+	copy with invalid syntax
+	COMMIT
+
+	from refs/heads/branch^0
+	ls "file2/"
+	INPUT_END'
+
+test_expect_success \
+	'N: copy to root by id and modify' \
+	'echo "hello, world" >expect.foo &&
+	 echo hello >expect.bar &&
+	 git fast-import <<-SETUP_END &&
+	commit refs/heads/N7
+	committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+	data <<COMMIT
+	hello, tree
+	COMMIT
+
+	deleteall
+	M 644 inline foo/bar
+	data <<EOF
+	hello
+	EOF
+	SETUP_END
+
+	 tree=$(git rev-parse --verify N7:) &&
+	 git fast-import <<-INPUT_END &&
+	commit refs/heads/N8
+	committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+	data <<COMMIT
+	copy to root by id and modify
+	COMMIT
+
+	M 040000 $tree ""
+	M 644 inline foo/foo
+	data <<EOF
+	hello, world
+	EOF
+	INPUT_END
+	 git show N8:foo/foo >actual.foo &&
+	 git show N8:foo/bar >actual.bar &&
+	 test_cmp expect.foo actual.foo &&
+	 test_cmp expect.bar actual.bar'
+
+test_expect_success \
+	'N: extract subtree' \
+	'branch=$(git rev-parse --verify refs/heads/branch^{tree}) &&
+	 cat >input <<-INPUT_END &&
+	commit refs/heads/N9
+	committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+	data <<COMMIT
+	extract subtree branch:newdir
+	COMMIT
+
+	M 040000 $branch ""
+	C "newdir" ""
+	INPUT_END
+	 git fast-import <input &&
+	 git diff --exit-code branch:newdir N9'
+
+test_expect_success \
+	'N: modify subtree, extract it, and modify again' \
+	'echo hello >expect.baz &&
+	 echo hello, world >expect.qux &&
+	 git fast-import <<-SETUP_END &&
+	commit refs/heads/N10
+	committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+	data <<COMMIT
+	hello, tree
+	COMMIT
+
+	deleteall
+	M 644 inline foo/bar/baz
+	data <<EOF
+	hello
+	EOF
+	SETUP_END
+
+	 tree=$(git rev-parse --verify N10:) &&
+	 git fast-import <<-INPUT_END &&
+	commit refs/heads/N11
+	committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+	data <<COMMIT
+	copy to root by id and modify
+	COMMIT
+
+	M 040000 $tree ""
+	M 100644 inline foo/bar/qux
+	data <<EOF
+	hello, world
+	EOF
+	R "foo" ""
+	C "bar/qux" "bar/quux"
+	INPUT_END
+	 git show N11:bar/baz >actual.baz &&
+	 git show N11:bar/qux >actual.qux &&
+	 git show N11:bar/quux >actual.quux &&
+	 test_cmp expect.baz actual.baz &&
+	 test_cmp expect.qux actual.qux &&
+	 test_cmp expect.qux actual.quux'
+
 ###
 ### series O
 ###
@@ -996,14 +1657,13 @@
 INPUT_END
 
 test_expect_success \
-	'P: supermodule & submodule mix' \
+	'P: superproject & submodule mix' \
 	'git fast-import <input &&
 	 git checkout subuse1 &&
-	 rm -rf sub && mkdir sub && cd sub &&
+	 rm -rf sub && mkdir sub && (cd sub &&
 	 git init &&
 	 git fetch --update-head-ok .. refs/heads/sub:refs/heads/master &&
-	 git checkout master &&
-	 cd .. &&
+	 git checkout master) &&
 	 git submodule init &&
 	 git submodule update'
 
@@ -1088,4 +1748,1198 @@
 test_expect_success 'P: fail on blob mark in gitlink' '
     test_must_fail git fast-import <input'
 
+###
+### series Q (notes)
+###
+
+note1_data="The first note for the first commit"
+note2_data="The first note for the second commit"
+note3_data="The first note for the third commit"
+note1b_data="The second note for the first commit"
+note1c_data="The third note for the first commit"
+note2b_data="The second note for the second commit"
+
+test_tick
+cat >input <<INPUT_END
+blob
+mark :2
+data <<EOF
+$file2_data
+EOF
+
+commit refs/heads/notes-test
+mark :3
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+first (:3)
+COMMIT
+
+M 644 :2 file2
+
+blob
+mark :4
+data $file4_len
+$file4_data
+commit refs/heads/notes-test
+mark :5
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+second (:5)
+COMMIT
+
+M 644 :4 file4
+
+commit refs/heads/notes-test
+mark :6
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+third (:6)
+COMMIT
+
+M 644 inline file5
+data <<EOF
+$file5_data
+EOF
+
+M 755 inline file6
+data <<EOF
+$file6_data
+EOF
+
+blob
+mark :7
+data <<EOF
+$note1_data
+EOF
+
+blob
+mark :8
+data <<EOF
+$note2_data
+EOF
+
+commit refs/notes/foobar
+mark :9
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+notes (:9)
+COMMIT
+
+N :7 :3
+N :8 :5
+N inline :6
+data <<EOF
+$note3_data
+EOF
+
+commit refs/notes/foobar
+mark :10
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+notes (:10)
+COMMIT
+
+N inline :3
+data <<EOF
+$note1b_data
+EOF
+
+commit refs/notes/foobar2
+mark :11
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+notes (:11)
+COMMIT
+
+N inline :3
+data <<EOF
+$note1c_data
+EOF
+
+commit refs/notes/foobar
+mark :12
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+notes (:12)
+COMMIT
+
+deleteall
+N inline :5
+data <<EOF
+$note2b_data
+EOF
+
+INPUT_END
+
+test_expect_success \
+	'Q: commit notes' \
+	'git fast-import <input &&
+	 git whatchanged notes-test'
+
+test_expect_success 'Q: verify pack' '
+	verify_packs
+'
+
+commit1=$(git rev-parse notes-test~2)
+commit2=$(git rev-parse notes-test^)
+commit3=$(git rev-parse notes-test)
+
+cat >expect <<EOF
+author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+
+first (:3)
+EOF
+test_expect_success \
+	'Q: verify first commit' \
+	'git cat-file commit notes-test~2 | sed 1d >actual &&
+	test_cmp expect actual'
+
+cat >expect <<EOF
+parent $commit1
+author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+
+second (:5)
+EOF
+test_expect_success \
+	'Q: verify second commit' \
+	'git cat-file commit notes-test^ | sed 1d >actual &&
+	test_cmp expect actual'
+
+cat >expect <<EOF
+parent $commit2
+author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+
+third (:6)
+EOF
+test_expect_success \
+	'Q: verify third commit' \
+	'git cat-file commit notes-test | sed 1d >actual &&
+	test_cmp expect actual'
+
+cat >expect <<EOF
+author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+
+notes (:9)
+EOF
+test_expect_success \
+	'Q: verify first notes commit' \
+	'git cat-file commit refs/notes/foobar~2 | sed 1d >actual &&
+	test_cmp expect actual'
+
+cat >expect.unsorted <<EOF
+100644 blob $commit1
+100644 blob $commit2
+100644 blob $commit3
+EOF
+cat expect.unsorted | sort >expect
+test_expect_success \
+	'Q: verify first notes tree' \
+	'git cat-file -p refs/notes/foobar~2^{tree} | sed "s/ [0-9a-f]*	/ /" >actual &&
+	 test_cmp expect actual'
+
+echo "$note1_data" >expect
+test_expect_success \
+	'Q: verify first note for first commit' \
+	'git cat-file blob refs/notes/foobar~2:$commit1 >actual && test_cmp expect actual'
+
+echo "$note2_data" >expect
+test_expect_success \
+	'Q: verify first note for second commit' \
+	'git cat-file blob refs/notes/foobar~2:$commit2 >actual && test_cmp expect actual'
+
+echo "$note3_data" >expect
+test_expect_success \
+	'Q: verify first note for third commit' \
+	'git cat-file blob refs/notes/foobar~2:$commit3 >actual && test_cmp expect actual'
+
+cat >expect <<EOF
+parent `git rev-parse --verify refs/notes/foobar~2`
+author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+
+notes (:10)
+EOF
+test_expect_success \
+	'Q: verify second notes commit' \
+	'git cat-file commit refs/notes/foobar^ | sed 1d >actual &&
+	test_cmp expect actual'
+
+cat >expect.unsorted <<EOF
+100644 blob $commit1
+100644 blob $commit2
+100644 blob $commit3
+EOF
+cat expect.unsorted | sort >expect
+test_expect_success \
+	'Q: verify second notes tree' \
+	'git cat-file -p refs/notes/foobar^^{tree} | sed "s/ [0-9a-f]*	/ /" >actual &&
+	 test_cmp expect actual'
+
+echo "$note1b_data" >expect
+test_expect_success \
+	'Q: verify second note for first commit' \
+	'git cat-file blob refs/notes/foobar^:$commit1 >actual && test_cmp expect actual'
+
+echo "$note2_data" >expect
+test_expect_success \
+	'Q: verify first note for second commit' \
+	'git cat-file blob refs/notes/foobar^:$commit2 >actual && test_cmp expect actual'
+
+echo "$note3_data" >expect
+test_expect_success \
+	'Q: verify first note for third commit' \
+	'git cat-file blob refs/notes/foobar^:$commit3 >actual && test_cmp expect actual'
+
+cat >expect <<EOF
+author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+
+notes (:11)
+EOF
+test_expect_success \
+	'Q: verify third notes commit' \
+	'git cat-file commit refs/notes/foobar2 | sed 1d >actual &&
+	test_cmp expect actual'
+
+cat >expect.unsorted <<EOF
+100644 blob $commit1
+EOF
+cat expect.unsorted | sort >expect
+test_expect_success \
+	'Q: verify third notes tree' \
+	'git cat-file -p refs/notes/foobar2^{tree} | sed "s/ [0-9a-f]*	/ /" >actual &&
+	 test_cmp expect actual'
+
+echo "$note1c_data" >expect
+test_expect_success \
+	'Q: verify third note for first commit' \
+	'git cat-file blob refs/notes/foobar2:$commit1 >actual && test_cmp expect actual'
+
+cat >expect <<EOF
+parent `git rev-parse --verify refs/notes/foobar^`
+author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+
+notes (:12)
+EOF
+test_expect_success \
+	'Q: verify fourth notes commit' \
+	'git cat-file commit refs/notes/foobar | sed 1d >actual &&
+	test_cmp expect actual'
+
+cat >expect.unsorted <<EOF
+100644 blob $commit2
+EOF
+cat expect.unsorted | sort >expect
+test_expect_success \
+	'Q: verify fourth notes tree' \
+	'git cat-file -p refs/notes/foobar^{tree} | sed "s/ [0-9a-f]*	/ /" >actual &&
+	 test_cmp expect actual'
+
+echo "$note2b_data" >expect
+test_expect_success \
+	'Q: verify second note for second commit' \
+	'git cat-file blob refs/notes/foobar:$commit2 >actual && test_cmp expect actual'
+
+cat >input <<EOF
+reset refs/heads/Q0
+
+commit refs/heads/note-Q0
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+Note for an empty branch.
+COMMIT
+
+N inline refs/heads/Q0
+data <<NOTE
+some note
+NOTE
+EOF
+test_expect_success \
+	'Q: deny note on empty branch' \
+	'test_must_fail git fast-import <input'
+###
+### series R (feature and option)
+###
+
+cat >input <<EOF
+feature no-such-feature-exists
+EOF
+
+test_expect_success 'R: abort on unsupported feature' '
+	test_must_fail git fast-import <input
+'
+
+cat >input <<EOF
+feature date-format=now
+EOF
+
+test_expect_success 'R: supported feature is accepted' '
+	git fast-import <input
+'
+
+cat >input << EOF
+blob
+data 3
+hi
+feature date-format=now
+EOF
+
+test_expect_success 'R: abort on receiving feature after data command' '
+	test_must_fail git fast-import <input
+'
+
+cat >input << EOF
+feature import-marks=git.marks
+feature import-marks=git2.marks
+EOF
+
+test_expect_success 'R: only one import-marks feature allowed per stream' '
+	test_must_fail git fast-import <input
+'
+
+cat >input << EOF
+feature export-marks=git.marks
+blob
+mark :1
+data 3
+hi
+
+EOF
+
+test_expect_success \
+    'R: export-marks feature results in a marks file being created' \
+    'cat input | git fast-import &&
+    grep :1 git.marks'
+
+test_expect_success \
+    'R: export-marks options can be overridden by commandline options' \
+    'cat input | git fast-import --export-marks=other.marks &&
+    grep :1 other.marks'
+
+test_expect_success 'R: catch typo in marks file name' '
+	test_must_fail git fast-import --import-marks=nonexistent.marks </dev/null &&
+	echo "feature import-marks=nonexistent.marks" |
+	test_must_fail git fast-import
+'
+
+test_expect_success 'R: import and output marks can be the same file' '
+	rm -f io.marks &&
+	blob=$(echo hi | git hash-object --stdin) &&
+	cat >expect <<-EOF &&
+	:1 $blob
+	:2 $blob
+	EOF
+	git fast-import --export-marks=io.marks <<-\EOF &&
+	blob
+	mark :1
+	data 3
+	hi
+
+	EOF
+	git fast-import --import-marks=io.marks --export-marks=io.marks <<-\EOF &&
+	blob
+	mark :2
+	data 3
+	hi
+
+	EOF
+	test_cmp expect io.marks
+'
+
+test_expect_success 'R: --import-marks=foo --output-marks=foo to create foo fails' '
+	rm -f io.marks &&
+	test_must_fail git fast-import --import-marks=io.marks --export-marks=io.marks <<-\EOF
+	blob
+	mark :1
+	data 3
+	hi
+
+	EOF
+'
+
+test_expect_success 'R: --import-marks-if-exists' '
+	rm -f io.marks &&
+	blob=$(echo hi | git hash-object --stdin) &&
+	echo ":1 $blob" >expect &&
+	git fast-import --import-marks-if-exists=io.marks --export-marks=io.marks <<-\EOF &&
+	blob
+	mark :1
+	data 3
+	hi
+
+	EOF
+	test_cmp expect io.marks
+'
+
+test_expect_success 'R: feature import-marks-if-exists' '
+	rm -f io.marks &&
+	>expect &&
+
+	git fast-import --export-marks=io.marks <<-\EOF &&
+	feature import-marks-if-exists=not_io.marks
+	EOF
+	test_cmp expect io.marks &&
+
+	blob=$(echo hi | git hash-object --stdin) &&
+
+	echo ":1 $blob" >io.marks &&
+	echo ":1 $blob" >expect &&
+	echo ":2 $blob" >>expect &&
+
+	git fast-import --export-marks=io.marks <<-\EOF &&
+	feature import-marks-if-exists=io.marks
+	blob
+	mark :2
+	data 3
+	hi
+
+	EOF
+	test_cmp expect io.marks &&
+
+	echo ":3 $blob" >>expect &&
+
+	git fast-import --import-marks=io.marks \
+			--export-marks=io.marks <<-\EOF &&
+	feature import-marks-if-exists=not_io.marks
+	blob
+	mark :3
+	data 3
+	hi
+
+	EOF
+	test_cmp expect io.marks &&
+
+	>expect &&
+
+	git fast-import --import-marks-if-exists=not_io.marks \
+			--export-marks=io.marks <<-\EOF
+	feature import-marks-if-exists=io.marks
+	EOF
+	test_cmp expect io.marks
+'
+
+cat >input << EOF
+feature import-marks=marks.out
+feature export-marks=marks.new
+EOF
+
+test_expect_success \
+    'R: import to output marks works without any content' \
+    'cat input | git fast-import &&
+    test_cmp marks.out marks.new'
+
+cat >input <<EOF
+feature import-marks=nonexistent.marks
+feature export-marks=marks.new
+EOF
+
+test_expect_success \
+    'R: import marks prefers commandline marks file over the stream' \
+    'cat input | git fast-import --import-marks=marks.out &&
+    test_cmp marks.out marks.new'
+
+
+cat >input <<EOF
+feature import-marks=nonexistent.marks
+feature export-marks=combined.marks
+EOF
+
+test_expect_success 'R: multiple --import-marks= should be honoured' '
+    head -n2 marks.out > one.marks &&
+    tail -n +3 marks.out > two.marks &&
+    git fast-import --import-marks=one.marks --import-marks=two.marks <input &&
+    test_cmp marks.out combined.marks
+'
+
+cat >input <<EOF
+feature relative-marks
+feature import-marks=relative.in
+feature export-marks=relative.out
+EOF
+
+test_expect_success 'R: feature relative-marks should be honoured' '
+    mkdir -p .git/info/fast-import/ &&
+    cp marks.new .git/info/fast-import/relative.in &&
+    git fast-import <input &&
+    test_cmp marks.new .git/info/fast-import/relative.out
+'
+
+cat >input <<EOF
+feature relative-marks
+feature import-marks=relative.in
+feature no-relative-marks
+feature export-marks=non-relative.out
+EOF
+
+test_expect_success 'R: feature no-relative-marks should be honoured' '
+    git fast-import <input &&
+    test_cmp marks.new non-relative.out
+'
+
+test_expect_success 'R: feature ls supported' '
+	echo "feature ls" |
+	git fast-import
+'
+
+test_expect_success 'R: feature cat-blob supported' '
+	echo "feature cat-blob" |
+	git fast-import
+'
+
+test_expect_success 'R: cat-blob-fd must be a nonnegative integer' '
+	test_must_fail git fast-import --cat-blob-fd=-1 </dev/null
+'
+
+test_expect_success NOT_MINGW 'R: print old blob' '
+	blob=$(echo "yes it can" | git hash-object -w --stdin) &&
+	cat >expect <<-EOF &&
+	${blob} blob 11
+	yes it can
+
+	EOF
+	echo "cat-blob $blob" |
+	git fast-import --cat-blob-fd=6 6>actual &&
+	test_cmp expect actual
+'
+
+test_expect_success NOT_MINGW 'R: in-stream cat-blob-fd not respected' '
+	echo hello >greeting &&
+	blob=$(git hash-object -w greeting) &&
+	cat >expect <<-EOF &&
+	${blob} blob 6
+	hello
+
+	EOF
+	git fast-import --cat-blob-fd=3 3>actual.3 >actual.1 <<-EOF &&
+	cat-blob $blob
+	EOF
+	test_cmp expect actual.3 &&
+	test_cmp empty actual.1 &&
+	git fast-import 3>actual.3 >actual.1 <<-EOF &&
+	option cat-blob-fd=3
+	cat-blob $blob
+	EOF
+	test_cmp empty actual.3 &&
+	test_cmp expect actual.1
+'
+
+test_expect_success NOT_MINGW 'R: print new blob' '
+	blob=$(echo "yep yep yep" | git hash-object --stdin) &&
+	cat >expect <<-EOF &&
+	${blob} blob 12
+	yep yep yep
+
+	EOF
+	git fast-import --cat-blob-fd=6 6>actual <<-\EOF &&
+	blob
+	mark :1
+	data <<BLOB_END
+	yep yep yep
+	BLOB_END
+	cat-blob :1
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success NOT_MINGW 'R: print new blob by sha1' '
+	blob=$(echo "a new blob named by sha1" | git hash-object --stdin) &&
+	cat >expect <<-EOF &&
+	${blob} blob 25
+	a new blob named by sha1
+
+	EOF
+	git fast-import --cat-blob-fd=6 6>actual <<-EOF &&
+	blob
+	data <<BLOB_END
+	a new blob named by sha1
+	BLOB_END
+	cat-blob $blob
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'setup: big file' '
+	(
+		echo "the quick brown fox jumps over the lazy dog" >big &&
+		for i in 1 2 3
+		do
+			cat big big big big >bigger &&
+			cat bigger bigger bigger bigger >big ||
+			exit
+		done
+	)
+'
+
+test_expect_success 'R: print two blobs to stdout' '
+	blob1=$(git hash-object big) &&
+	blob1_len=$(wc -c <big) &&
+	blob2=$(echo hello | git hash-object --stdin) &&
+	{
+		echo ${blob1} blob $blob1_len &&
+		cat big &&
+		cat <<-EOF
+
+		${blob2} blob 6
+		hello
+
+		EOF
+	} >expect &&
+	{
+		cat <<-\END_PART1 &&
+			blob
+			mark :1
+			data <<data_end
+		END_PART1
+		cat big &&
+		cat <<-\EOF
+			data_end
+			blob
+			mark :2
+			data <<data_end
+			hello
+			data_end
+			cat-blob :1
+			cat-blob :2
+		EOF
+	} |
+	git fast-import >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success PIPE 'R: copy using cat-file' '
+	expect_id=$(git hash-object big) &&
+	expect_len=$(wc -c <big) &&
+	echo $expect_id blob $expect_len >expect.response &&
+
+	rm -f blobs &&
+	cat >frontend <<-\FRONTEND_END &&
+	#!/bin/sh
+	FRONTEND_END
+
+	mkfifo blobs &&
+	(
+		export GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL GIT_COMMITTER_DATE &&
+		cat <<-\EOF &&
+		feature cat-blob
+		blob
+		mark :1
+		data <<BLOB
+		EOF
+		cat big &&
+		cat <<-\EOF &&
+		BLOB
+		cat-blob :1
+		EOF
+
+		read blob_id type size <&3 &&
+		echo "$blob_id $type $size" >response &&
+		head_c $size >blob <&3 &&
+		read newline <&3 &&
+
+		cat <<-EOF &&
+		commit refs/heads/copied
+		committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+		data <<COMMIT
+		copy big file as file3
+		COMMIT
+		M 644 inline file3
+		data <<BLOB
+		EOF
+		cat blob &&
+		echo BLOB
+	) 3<blobs |
+	git fast-import --cat-blob-fd=3 3>blobs &&
+	git show copied:file3 >actual &&
+	test_cmp expect.response response &&
+	test_cmp big actual
+'
+
+test_expect_success PIPE 'R: print blob mid-commit' '
+	rm -f blobs &&
+	echo "A blob from _before_ the commit." >expect &&
+	mkfifo blobs &&
+	(
+		exec 3<blobs &&
+		cat <<-EOF &&
+		feature cat-blob
+		blob
+		mark :1
+		data <<BLOB
+		A blob from _before_ the commit.
+		BLOB
+		commit refs/heads/temporary
+		committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+		data <<COMMIT
+		Empty commit
+		COMMIT
+		cat-blob :1
+		EOF
+
+		read blob_id type size <&3 &&
+		head_c $size >actual <&3 &&
+		read newline <&3 &&
+
+		echo
+	) |
+	git fast-import --cat-blob-fd=3 3>blobs &&
+	test_cmp expect actual
+'
+
+test_expect_success PIPE 'R: print staged blob within commit' '
+	rm -f blobs &&
+	echo "A blob from _within_ the commit." >expect &&
+	mkfifo blobs &&
+	(
+		exec 3<blobs &&
+		cat <<-EOF &&
+		feature cat-blob
+		commit refs/heads/within
+		committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+		data <<COMMIT
+		Empty commit
+		COMMIT
+		M 644 inline within
+		data <<BLOB
+		A blob from _within_ the commit.
+		BLOB
+		EOF
+
+		to_get=$(
+			echo "A blob from _within_ the commit." |
+			git hash-object --stdin
+		) &&
+		echo "cat-blob $to_get" &&
+
+		read blob_id type size <&3 &&
+		head_c $size >actual <&3 &&
+		read newline <&3 &&
+
+		echo deleteall
+	) |
+	git fast-import --cat-blob-fd=3 3>blobs &&
+	test_cmp expect actual
+'
+
+cat >input << EOF
+option git quiet
+blob
+data 3
+hi
+
+EOF
+
+test_expect_success 'R: quiet option results in no stats being output' '
+    cat input | git fast-import 2> output &&
+    test_cmp empty output
+'
+
+test_expect_success 'R: feature done means terminating "done" is mandatory' '
+	echo feature done | test_must_fail git fast-import &&
+	test_must_fail git fast-import --done </dev/null
+'
+
+test_expect_success 'R: terminating "done" with trailing gibberish is ok' '
+	git fast-import <<-\EOF &&
+	feature done
+	done
+	trailing gibberish
+	EOF
+	git fast-import <<-\EOF
+	done
+	more trailing gibberish
+	EOF
+'
+
+test_expect_success 'R: terminating "done" within commit' '
+	cat >expect <<-\EOF &&
+	OBJID
+	:000000 100644 OBJID OBJID A	hello.c
+	:000000 100644 OBJID OBJID A	hello2.c
+	EOF
+	git fast-import <<-EOF &&
+	commit refs/heads/done-ends
+	committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+	data <<EOT
+	Commit terminated by "done" command
+	EOT
+	M 100644 inline hello.c
+	data <<EOT
+	Hello, world.
+	EOT
+	C hello.c hello2.c
+	done
+	EOF
+	git rev-list done-ends |
+	git diff-tree -r --stdin --root --always |
+	sed -e "s/$_x40/OBJID/g" >actual &&
+	test_cmp expect actual
+'
+
+cat >input <<EOF
+option git non-existing-option
+EOF
+
+test_expect_success 'R: die on unknown option' '
+    test_must_fail git fast-import <input
+'
+
+test_expect_success 'R: unknown commandline options are rejected' '\
+    test_must_fail git fast-import --non-existing-option < /dev/null
+'
+
+test_expect_success 'R: die on invalid option argument' '
+	echo "option git active-branches=-5" |
+	test_must_fail git fast-import &&
+	echo "option git depth=" |
+	test_must_fail git fast-import &&
+	test_must_fail git fast-import --depth="5 elephants" </dev/null
+'
+
+cat >input <<EOF
+option non-existing-vcs non-existing-option
+EOF
+
+test_expect_success 'R: ignore non-git options' '
+    git fast-import <input
+'
+
+##
+## R: very large blobs
+##
+blobsize=$((2*1024*1024 + 53))
+test-genrandom bar $blobsize >expect
+cat >input <<INPUT_END
+commit refs/heads/big-file
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+R - big file
+COMMIT
+
+M 644 inline big1
+data $blobsize
+INPUT_END
+cat expect >>input
+cat >>input <<INPUT_END
+M 644 inline big2
+data $blobsize
+INPUT_END
+cat expect >>input
+echo >>input
+
+test_expect_success \
+	'R: blob bigger than threshold' \
+	'test_create_repo R &&
+	 git --git-dir=R/.git fast-import --big-file-threshold=1 <input'
+
+test_expect_success 'R: verify created pack' '
+	(
+		cd R &&
+		verify_packs -v > ../verify
+	)
+'
+
+test_expect_success \
+	'R: verify written objects' \
+	'git --git-dir=R/.git cat-file blob big-file:big1 >actual &&
+	 test_cmp expect actual &&
+	 a=$(git --git-dir=R/.git rev-parse big-file:big1) &&
+	 b=$(git --git-dir=R/.git rev-parse big-file:big2) &&
+	 test $a = $b'
+test_expect_success \
+	'R: blob appears only once' \
+	'n=$(grep $a verify | wc -l) &&
+	 test 1 = $n'
+
+###
+### series S
+###
+#
+# Make sure missing spaces and EOLs after mark references
+# cause errors.
+#
+# Setup:
+#
+#   1--2--4
+#    \   /
+#     -3-
+#
+#   commit marks:  301, 302, 303, 304
+#   blob marks:              403, 404, resp.
+#   note mark:          202
+#
+# The error message when a space is missing not at the
+# end of the line is:
+#
+#   Missing space after ..
+#
+# or when extra characters come after the mark at the end
+# of the line:
+#
+#   Garbage after ..
+#
+# or when the dataref is neither "inline " or a known SHA1,
+#
+#   Invalid dataref ..
+#
+test_tick
+
+cat >input <<INPUT_END
+commit refs/heads/S
+mark :301
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+commit 1
+COMMIT
+M 100644 inline hello.c
+data <<BLOB
+blob 1
+BLOB
+
+commit refs/heads/S
+mark :302
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+commit 2
+COMMIT
+from :301
+M 100644 inline hello.c
+data <<BLOB
+blob 2
+BLOB
+
+blob
+mark :403
+data <<BLOB
+blob 3
+BLOB
+
+blob
+mark :202
+data <<BLOB
+note 2
+BLOB
+INPUT_END
+
+test_expect_success 'S: initialize for S tests' '
+	git fast-import --export-marks=marks <input
+'
+
+#
+# filemodify, three datarefs
+#
+test_expect_success 'S: filemodify with garbage after mark must fail' '
+	test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
+	commit refs/heads/S
+	committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+	data <<COMMIT
+	commit N
+	COMMIT
+	M 100644 :403x hello.c
+	EOF
+	cat err &&
+	test_i18ngrep "space after mark" err
+'
+
+# inline is misspelled; fast-import thinks it is some unknown dataref
+test_expect_success 'S: filemodify with garbage after inline must fail' '
+	test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
+	commit refs/heads/S
+	committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+	data <<COMMIT
+	commit N
+	COMMIT
+	M 100644 inlineX hello.c
+	data <<BLOB
+	inline
+	BLOB
+	EOF
+	cat err &&
+	test_i18ngrep "nvalid dataref" err
+'
+
+test_expect_success 'S: filemodify with garbage after sha1 must fail' '
+	sha1=$(grep :403 marks | cut -d\  -f2) &&
+	test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
+	commit refs/heads/S
+	committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+	data <<COMMIT
+	commit N
+	COMMIT
+	M 100644 ${sha1}x hello.c
+	EOF
+	cat err &&
+	test_i18ngrep "space after SHA1" err
+'
+
+#
+# notemodify, three ways to say dataref
+#
+test_expect_success 'S: notemodify with garabge after mark dataref must fail' '
+	test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
+	commit refs/heads/S
+	committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+	data <<COMMIT
+	commit S note dataref markref
+	COMMIT
+	N :202x :302
+	EOF
+	cat err &&
+	test_i18ngrep "space after mark" err
+'
+
+test_expect_success 'S: notemodify with garbage after inline dataref must fail' '
+	test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
+	commit refs/heads/S
+	committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+	data <<COMMIT
+	commit S note dataref inline
+	COMMIT
+	N inlineX :302
+	data <<BLOB
+	note blob
+	BLOB
+	EOF
+	cat err &&
+	test_i18ngrep "nvalid dataref" err
+'
+
+test_expect_success 'S: notemodify with garbage after sha1 dataref must fail' '
+	sha1=$(grep :202 marks | cut -d\  -f2) &&
+	test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
+	commit refs/heads/S
+	committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+	data <<COMMIT
+	commit S note dataref sha1
+	COMMIT
+	N ${sha1}x :302
+	EOF
+	cat err &&
+	test_i18ngrep "space after SHA1" err
+'
+
+#
+# notemodify, mark in committish
+#
+test_expect_success 'S: notemodify with garbarge after mark committish must fail' '
+	test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
+	commit refs/heads/Snotes
+	committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+	data <<COMMIT
+	commit S note committish
+	COMMIT
+	N :202 :302x
+	EOF
+	cat err &&
+	test_i18ngrep "after mark" err
+'
+
+#
+# from
+#
+test_expect_success 'S: from with garbage after mark must fail' '
+	# no &&
+	git fast-import --import-marks=marks --export-marks=marks <<-EOF 2>err
+	commit refs/heads/S2
+	mark :303
+	committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+	data <<COMMIT
+	commit 3
+	COMMIT
+	from :301x
+	M 100644 :403 hello.c
+	EOF
+
+	ret=$? &&
+	echo returned $ret &&
+	test $ret -ne 0 && # failed, but it created the commit
+
+	# go create the commit, need it for merge test
+	git fast-import --import-marks=marks --export-marks=marks <<-EOF &&
+	commit refs/heads/S2
+	mark :303
+	committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+	data <<COMMIT
+	commit 3
+	COMMIT
+	from :301
+	M 100644 :403 hello.c
+	EOF
+
+	# now evaluate the error
+	cat err &&
+	test_i18ngrep "after mark" err
+'
+
+
+#
+# merge
+#
+test_expect_success 'S: merge with garbage after mark must fail' '
+	test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
+	commit refs/heads/S
+	mark :304
+	committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+	data <<COMMIT
+	merge 4
+	COMMIT
+	from :302
+	merge :303x
+	M 100644 :403 hello.c
+	EOF
+	cat err &&
+	test_i18ngrep "after mark" err
+'
+
+#
+# tag, from markref
+#
+test_expect_success 'S: tag with garbage after mark must fail' '
+	test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
+	tag refs/tags/Stag
+	from :302x
+	tagger $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+	data <<TAG
+	tag S
+	TAG
+	EOF
+	cat err &&
+	test_i18ngrep "after mark" err
+'
+
+#
+# cat-blob markref
+#
+test_expect_success 'S: cat-blob with garbage after mark must fail' '
+	test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
+	cat-blob :403x
+	EOF
+	cat err &&
+	test_i18ngrep "after mark" err
+'
+
+#
+# ls markref
+#
+test_expect_success 'S: ls with garbage after mark must fail' '
+	test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
+	ls :302x hello.c
+	EOF
+	cat err &&
+	test_i18ngrep "space after mark" err
+'
+
+test_expect_success 'S: ls with garbage after sha1 must fail' '
+	sha1=$(grep :302 marks | cut -d\  -f2) &&
+	test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
+	ls ${sha1}x hello.c
+	EOF
+	cat err &&
+	test_i18ngrep "space after tree-ish" err
+'
+
 test_done
diff --git a/t/t9301-fast-export.sh b/t/t9301-fast-export.sh
deleted file mode 100755
index 356964e..0000000
--- a/t/t9301-fast-export.sh
+++ /dev/null
@@ -1,374 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2007 Johannes E. Schindelin
-#
-
-test_description='git fast-export'
-. ./test-lib.sh
-
-test_expect_success 'setup' '
-
-	echo break it > file0 &&
-	git add file0 &&
-	test_tick &&
-	echo Wohlauf > file &&
-	git add file &&
-	test_tick &&
-	git commit -m initial &&
-	echo die Luft > file &&
-	echo geht frisch > file2 &&
-	git add file file2 &&
-	test_tick &&
-	git commit -m second &&
-	echo und > file2 &&
-	test_tick &&
-	git commit -m third file2 &&
-	test_tick &&
-	git tag rein &&
-	git checkout -b wer HEAD^ &&
-	echo lange > file2
-	test_tick &&
-	git commit -m sitzt file2 &&
-	test_tick &&
-	git tag -a -m valentin muss &&
-	git merge -s ours master
-
-'
-
-test_expect_success 'fast-export | fast-import' '
-
-	MASTER=$(git rev-parse --verify master) &&
-	REIN=$(git rev-parse --verify rein) &&
-	WER=$(git rev-parse --verify wer) &&
-	MUSS=$(git rev-parse --verify muss) &&
-	mkdir new &&
-	git --git-dir=new/.git init &&
-	git fast-export --all |
-	(cd new &&
-	 git fast-import &&
-	 test $MASTER = $(git rev-parse --verify refs/heads/master) &&
-	 test $REIN = $(git rev-parse --verify refs/tags/rein) &&
-	 test $WER = $(git rev-parse --verify refs/heads/wer) &&
-	 test $MUSS = $(git rev-parse --verify refs/tags/muss))
-
-'
-
-test_expect_success 'fast-export master~2..master' '
-
-	git fast-export master~2..master |
-		sed "s/master/partial/" |
-		(cd new &&
-		 git fast-import &&
-		 test $MASTER != $(git rev-parse --verify refs/heads/partial) &&
-		 git diff --exit-code master partial &&
-		 git diff --exit-code master^ partial^ &&
-		 test_must_fail git rev-parse partial~2)
-
-'
-
-test_expect_success 'iso-8859-1' '
-
-	git config i18n.commitencoding ISO8859-1 &&
-	# use author and committer name in ISO-8859-1 to match it.
-	. "$TEST_DIRECTORY"/t3901-8859-1.txt &&
-	test_tick &&
-	echo rosten >file &&
-	git commit -s -m den file &&
-	git fast-export wer^..wer |
-		sed "s/wer/i18n/" |
-		(cd new &&
-		 git fast-import &&
-		 git cat-file commit i18n | grep "Áéí óú")
-
-'
-test_expect_success 'import/export-marks' '
-
-	git checkout -b marks master &&
-	git fast-export --export-marks=tmp-marks HEAD &&
-	test -s tmp-marks &&
-	test $(wc -l < tmp-marks) -eq 3 &&
-	test $(
-		git fast-export --import-marks=tmp-marks\
-		--export-marks=tmp-marks HEAD |
-		grep ^commit |
-		wc -l) \
-	-eq 0 &&
-	echo change > file &&
-	git commit -m "last commit" file &&
-	test $(
-		git fast-export --import-marks=tmp-marks \
-		--export-marks=tmp-marks HEAD |
-		grep ^commit\  |
-		wc -l) \
-	-eq 1 &&
-	test $(wc -l < tmp-marks) -eq 4
-
-'
-
-cat > signed-tag-import << EOF
-tag sign-your-name
-from $(git rev-parse HEAD)
-tagger C O Mitter <committer@example.com> 1112911993 -0700
-data 210
-A message for a sign
------BEGIN PGP SIGNATURE-----
-Version: GnuPG v1.4.5 (GNU/Linux)
-
-fakedsignaturefakedsignaturefakedsignaturefakedsignaturfakedsign
-aturefakedsignaturefake=
-=/59v
------END PGP SIGNATURE-----
-EOF
-
-test_expect_success 'set up faked signed tag' '
-
-	cat signed-tag-import | git fast-import
-
-'
-
-test_expect_success 'signed-tags=abort' '
-
-	test_must_fail git fast-export --signed-tags=abort sign-your-name
-
-'
-
-test_expect_success 'signed-tags=verbatim' '
-
-	git fast-export --signed-tags=verbatim sign-your-name > output &&
-	grep PGP output
-
-'
-
-test_expect_success 'signed-tags=strip' '
-
-	git fast-export --signed-tags=strip sign-your-name > output &&
-	! grep PGP output
-
-'
-
-test_expect_success 'setup submodule' '
-
-	git checkout -f master &&
-	mkdir sub &&
-	cd sub &&
-	git init  &&
-	echo test file > file &&
-	git add file &&
-	git commit -m sub_initial &&
-	cd .. &&
-	git submodule add "`pwd`/sub" sub &&
-	git commit -m initial &&
-	test_tick &&
-	cd sub &&
-	echo more data >> file &&
-	git add file &&
-	git commit -m sub_second &&
-	cd .. &&
-	git add sub &&
-	git commit -m second
-
-'
-
-test_expect_success 'submodule fast-export | fast-import' '
-
-	SUBENT1=$(git ls-tree master^ sub) &&
-	SUBENT2=$(git ls-tree master sub) &&
-	rm -rf new &&
-	mkdir new &&
-	git --git-dir=new/.git init &&
-	git fast-export --signed-tags=strip --all |
-	(cd new &&
-	 git fast-import &&
-	 test "$SUBENT1" = "$(git ls-tree refs/heads/master^ sub)" &&
-	 test "$SUBENT2" = "$(git ls-tree refs/heads/master sub)" &&
-	 git checkout master &&
-	 git submodule init &&
-	 git submodule update &&
-	 cmp sub/file ../sub/file)
-
-'
-
-GIT_AUTHOR_NAME='A U Thor'; export GIT_AUTHOR_NAME
-GIT_COMMITTER_NAME='C O Mitter'; export GIT_COMMITTER_NAME
-
-test_expect_success 'setup copies' '
-
-	git config --unset i18n.commitencoding &&
-	git checkout -b copy rein &&
-	git mv file file3 &&
-	git commit -m move1 &&
-	test_tick &&
-	cp file2 file4 &&
-	git add file4 &&
-	git mv file2 file5 &&
-	git commit -m copy1 &&
-	test_tick &&
-	cp file3 file6 &&
-	git add file6 &&
-	git commit -m copy2 &&
-	test_tick &&
-	echo more text >> file6 &&
-	echo even more text >> file6 &&
-	git add file6 &&
-	git commit -m modify &&
-	test_tick &&
-	cp file6 file7 &&
-	echo test >> file7 &&
-	git add file7 &&
-	git commit -m copy_modify
-
-'
-
-test_expect_success 'fast-export -C -C | fast-import' '
-
-	ENTRY=$(git rev-parse --verify copy) &&
-	rm -rf new &&
-	mkdir new &&
-	git --git-dir=new/.git init &&
-	git fast-export -C -C --signed-tags=strip --all > output &&
-	grep "^C \"file6\" \"file7\"\$" output &&
-	cat output |
-	(cd new &&
-	 git fast-import &&
-	 test $ENTRY = $(git rev-parse --verify refs/heads/copy))
-
-'
-
-test_expect_success 'fast-export | fast-import when master is tagged' '
-
-	git tag -m msg last &&
-	git fast-export -C -C --signed-tags=strip --all > output &&
-	test $(grep -c "^tag " output) = 3
-
-'
-
-cat > tag-content << EOF
-object $(git rev-parse HEAD)
-type commit
-tag rosten
-EOF
-
-test_expect_success 'cope with tagger-less tags' '
-
-	TAG=$(git hash-object -t tag -w tag-content) &&
-	git update-ref refs/tags/sonnenschein $TAG &&
-	git fast-export -C -C --signed-tags=strip --all > output &&
-	test $(grep -c "^tag " output) = 4 &&
-	! grep "Unspecified Tagger" output &&
-	git fast-export -C -C --signed-tags=strip --all \
-		--fake-missing-tagger > output &&
-	test $(grep -c "^tag " output) = 4 &&
-	grep "Unspecified Tagger" output
-
-'
-
-test_expect_success 'setup for limiting exports by PATH' '
-	mkdir limit-by-paths &&
-	cd limit-by-paths &&
-	git init &&
-	echo hi > there &&
-	git add there &&
-	git commit -m "First file" &&
-	echo foo > bar &&
-	git add bar &&
-	git commit -m "Second file" &&
-	git tag -a -m msg mytag &&
-	echo morefoo >> bar &&
-	git add bar &&
-	git commit -m "Change to second file" &&
-	cd ..
-'
-
-cat > limit-by-paths/expected << EOF
-blob
-mark :1
-data 3
-hi
-
-reset refs/tags/mytag
-commit refs/tags/mytag
-mark :2
-author A U Thor <author@example.com> 1112912713 -0700
-committer C O Mitter <committer@example.com> 1112912713 -0700
-data 11
-First file
-M 100644 :1 there
-
-EOF
-
-test_expect_success 'dropping tag of filtered out object' '
-	cd limit-by-paths &&
-	git fast-export --tag-of-filtered-object=drop mytag -- there > output &&
-	test_cmp output expected &&
-	cd ..
-'
-
-cat >> limit-by-paths/expected << EOF
-tag mytag
-from :2
-tagger C O Mitter <committer@example.com> 1112912713 -0700
-data 4
-msg
-
-EOF
-
-test_expect_success 'rewriting tag of filtered out object' '
-	cd limit-by-paths &&
-	git fast-export --tag-of-filtered-object=rewrite mytag -- there > output &&
-	test_cmp output expected &&
-	cd ..
-'
-
-cat > limit-by-paths/expected << EOF
-blob
-mark :1
-data 4
-foo
-
-blob
-mark :2
-data 3
-hi
-
-reset refs/heads/master
-commit refs/heads/master
-mark :3
-author A U Thor <author@example.com> 1112912713 -0700
-committer C O Mitter <committer@example.com> 1112912713 -0700
-data 12
-Second file
-M 100644 :1 bar
-M 100644 :2 there
-
-EOF
-
-test_expect_failure 'no exact-ref revisions included' '
-	cd limit-by-paths &&
-	git fast-export master~2..master~1 > output &&
-	test_cmp output expected &&
-	cd ..
-'
-
-
-test_expect_success 'set-up a few more tags for tag export tests' '
-	git checkout -f master &&
-	HEAD_TREE=`git show -s --pretty=raw HEAD | grep tree | sed "s/tree //"` &&
-	git tag    tree_tag        -m "tagging a tree" $HEAD_TREE &&
-	git tag -a tree_tag-obj    -m "tagging a tree" $HEAD_TREE &&
-	git tag    tag-obj_tag     -m "tagging a tag" tree_tag-obj &&
-	git tag -a tag-obj_tag-obj -m "tagging a tag" tree_tag-obj
-'
-
-test_expect_success 'tree_tag'        '
-	mkdir result &&
-	(cd result && git init) &&
-	git fast-export tree_tag > fe-stream &&
-	(cd result && git fast-import < ../fe-stream)
-'
-
-# NEEDSWORK: not just check return status, but validate the output
-test_expect_success 'tree_tag-obj'    'git fast-export tree_tag-obj'
-test_expect_success 'tag-obj_tag'     'git fast-export tag-obj_tag'
-test_expect_success 'tag-obj_tag-obj' 'git fast-export tag-obj_tag-obj'
-
-test_done
diff --git a/t/t9301-fast-import-notes.sh b/t/t9301-fast-import-notes.sh
new file mode 100755
index 0000000..83acf68
--- /dev/null
+++ b/t/t9301-fast-import-notes.sh
@@ -0,0 +1,682 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Johan Herland
+#
+
+test_description='test git fast-import of notes objects'
+. ./test-lib.sh
+
+
+test_tick
+cat >input <<INPUT_END
+commit refs/heads/master
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+first commit
+COMMIT
+
+M 644 inline foo
+data <<EOF
+file foo in first commit
+EOF
+
+M 755 inline bar
+data <<EOF
+file bar in first commit
+EOF
+
+M 644 inline baz/xyzzy
+data <<EOF
+file baz/xyzzy in first commit
+EOF
+
+commit refs/heads/master
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+second commit
+COMMIT
+
+M 644 inline foo
+data <<EOF
+file foo in second commit
+EOF
+
+M 755 inline baz/xyzzy
+data <<EOF
+file baz/xyzzy in second commit
+EOF
+
+commit refs/heads/master
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+third commit
+COMMIT
+
+M 644 inline foo
+data <<EOF
+file foo in third commit
+EOF
+
+commit refs/heads/master
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+fourth commit
+COMMIT
+
+M 755 inline bar
+data <<EOF
+file bar in fourth commit
+EOF
+
+INPUT_END
+
+test_expect_success 'set up master branch' '
+
+	git fast-import <input &&
+	git whatchanged master
+'
+
+commit4=$(git rev-parse refs/heads/master)
+commit3=$(git rev-parse "$commit4^")
+commit2=$(git rev-parse "$commit4~2")
+commit1=$(git rev-parse "$commit4~3")
+
+test_tick
+cat >input <<INPUT_END
+commit refs/notes/test
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+first notes commit
+COMMIT
+
+M 644 inline $commit1
+data <<EOF
+first note for first commit
+EOF
+
+M 755 inline $commit2
+data <<EOF
+first note for second commit
+EOF
+
+INPUT_END
+
+cat >expect <<EXPECT_END
+    fourth commit
+    third commit
+    second commit
+    first note for second commit
+    first commit
+    first note for first commit
+EXPECT_END
+
+test_expect_success 'add notes with simple M command' '
+
+	git fast-import <input &&
+	GIT_NOTES_REF=refs/notes/test git log | grep "^    " > actual &&
+	test_cmp expect actual
+
+'
+
+test_tick
+cat >input <<INPUT_END
+feature notes
+commit refs/notes/test
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+second notes commit
+COMMIT
+
+from refs/notes/test^0
+N inline $commit3
+data <<EOF
+first note for third commit
+EOF
+
+N inline $commit4
+data <<EOF
+first note for fourth commit
+EOF
+
+INPUT_END
+
+cat >expect <<EXPECT_END
+    fourth commit
+    first note for fourth commit
+    third commit
+    first note for third commit
+    second commit
+    first note for second commit
+    first commit
+    first note for first commit
+EXPECT_END
+
+test_expect_success 'add notes with simple N command' '
+
+	git fast-import <input &&
+	GIT_NOTES_REF=refs/notes/test git log | grep "^    " > actual &&
+	test_cmp expect actual
+
+'
+
+test_tick
+cat >input <<INPUT_END
+commit refs/notes/test
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+third notes commit
+COMMIT
+
+from refs/notes/test^0
+N inline $commit1
+data <<EOF
+second note for first commit
+EOF
+
+N inline $commit2
+data <<EOF
+second note for second commit
+EOF
+
+N inline $commit3
+data <<EOF
+second note for third commit
+EOF
+
+N inline $commit4
+data <<EOF
+second note for fourth commit
+EOF
+
+INPUT_END
+
+cat >expect <<EXPECT_END
+    fourth commit
+    second note for fourth commit
+    third commit
+    second note for third commit
+    second commit
+    second note for second commit
+    first commit
+    second note for first commit
+EXPECT_END
+
+test_expect_success 'update existing notes with N command' '
+
+	git fast-import <input &&
+	GIT_NOTES_REF=refs/notes/test git log | grep "^    " > actual &&
+	test_cmp expect actual
+
+'
+
+test_tick
+cat >input <<INPUT_END
+commit refs/notes/test
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+fourth notes commit
+COMMIT
+
+from refs/notes/test^0
+M 644 inline $(echo "$commit3" | sed "s|^..|&/|")
+data <<EOF
+prefix of note for third commit
+EOF
+
+M 644 inline $(echo "$commit4" | sed "s|^..|&/|")
+data <<EOF
+prefix of note for fourth commit
+EOF
+
+M 644 inline $(echo "$commit4" | sed "s|^\(..\)\(..\)|\1/\2/|")
+data <<EOF
+pre-prefix of note for fourth commit
+EOF
+
+N inline $commit1
+data <<EOF
+third note for first commit
+EOF
+
+N inline $commit2
+data <<EOF
+third note for second commit
+EOF
+
+N inline $commit3
+data <<EOF
+third note for third commit
+EOF
+
+N inline $commit4
+data <<EOF
+third note for fourth commit
+EOF
+
+
+INPUT_END
+
+whitespace="    "
+
+cat >expect <<EXPECT_END
+    fourth commit
+    pre-prefix of note for fourth commit
+$whitespace
+    prefix of note for fourth commit
+$whitespace
+    third note for fourth commit
+    third commit
+    prefix of note for third commit
+$whitespace
+    third note for third commit
+    second commit
+    third note for second commit
+    first commit
+    third note for first commit
+EXPECT_END
+
+test_expect_success 'add concatentation notes with M command' '
+
+	git fast-import <input &&
+	GIT_NOTES_REF=refs/notes/test git log | grep "^    " > actual &&
+	test_cmp expect actual
+
+'
+
+test_tick
+cat >input <<INPUT_END
+commit refs/notes/test
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+fifth notes commit
+COMMIT
+
+from refs/notes/test^0
+deleteall
+
+INPUT_END
+
+cat >expect <<EXPECT_END
+    fourth commit
+    third commit
+    second commit
+    first commit
+EXPECT_END
+
+test_expect_success 'verify that deleteall also removes notes' '
+
+	git fast-import <input &&
+	GIT_NOTES_REF=refs/notes/test git log | grep "^    " > actual &&
+	test_cmp expect actual
+
+'
+
+test_tick
+cat >input <<INPUT_END
+commit refs/notes/test
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+sixth notes commit
+COMMIT
+
+from refs/notes/test^0
+M 644 inline $commit1
+data <<EOF
+third note for first commit
+EOF
+
+M 644 inline $commit3
+data <<EOF
+third note for third commit
+EOF
+
+N inline $commit1
+data <<EOF
+fourth note for first commit
+EOF
+
+N inline $commit3
+data <<EOF
+fourth note for third commit
+EOF
+
+INPUT_END
+
+cat >expect <<EXPECT_END
+    fourth commit
+    third commit
+    fourth note for third commit
+    second commit
+    first commit
+    fourth note for first commit
+EXPECT_END
+
+test_expect_success 'verify that later N commands override earlier M commands' '
+
+	git fast-import <input &&
+	GIT_NOTES_REF=refs/notes/test git log | grep "^    " > actual &&
+	test_cmp expect actual
+
+'
+
+# Write fast-import commands to create the given number of commits
+fast_import_commits () {
+	my_ref=$1
+	my_num_commits=$2
+	my_append_to_file=$3
+	my_i=0
+	while test $my_i -lt $my_num_commits
+	do
+		my_i=$(($my_i + 1))
+		test_tick
+		cat >>"$my_append_to_file" <<INPUT_END
+commit $my_ref
+mark :$my_i
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+commit #$my_i
+COMMIT
+
+M 644 inline file
+data <<EOF
+file contents in commit #$my_i
+EOF
+
+INPUT_END
+	done
+}
+
+# Write fast-import commands to create the given number of notes annotating
+# the commits created by fast_import_commits()
+fast_import_notes () {
+	my_notes_ref=$1
+	my_num_commits=$2
+	my_append_to_file=$3
+	my_note_append=$4
+	test_tick
+	cat >>"$my_append_to_file" <<INPUT_END
+commit $my_notes_ref
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+committing $my_num_commits notes
+COMMIT
+
+INPUT_END
+
+	my_i=0
+	while test $my_i -lt $my_num_commits
+	do
+		my_i=$(($my_i + 1))
+		cat >>"$my_append_to_file" <<INPUT_END
+N inline :$my_i
+data <<EOF
+note for commit #$my_i$my_note_append
+EOF
+
+INPUT_END
+	done
+}
+
+
+rm input expect
+num_commits=400
+# Create lots of commits
+fast_import_commits "refs/heads/many_commits" $num_commits input
+# Create one note per above commit
+fast_import_notes "refs/notes/many_notes" $num_commits input
+# Add a couple of non-notes as well
+test_tick
+cat >>input <<INPUT_END
+commit refs/notes/many_notes
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+committing some non-notes to the notes tree
+COMMIT
+
+M 755 inline foobar/non-note.txt
+data <<EOF
+This is not a note, but rather a regular file residing in a notes tree
+EOF
+
+M 644 inline deadbeef
+data <<EOF
+Non-note file
+EOF
+
+M 644 inline de/adbeef
+data <<EOF
+Another non-note file
+EOF
+
+INPUT_END
+# Finally create the expected output from all these notes and commits
+i=$num_commits
+while test $i -gt 0
+do
+	cat >>expect <<EXPECT_END
+    commit #$i
+    note for commit #$i
+EXPECT_END
+	i=$(($i - 1))
+done
+
+test_expect_success 'add lots of commits and notes' '
+
+	git fast-import <input &&
+	GIT_NOTES_REF=refs/notes/many_notes git log refs/heads/many_commits |
+	    grep "^    " > actual &&
+	test_cmp expect actual
+
+'
+
+test_expect_success 'verify that lots of notes trigger a fanout scheme' '
+
+	# None of the entries in the top-level notes tree should be a full SHA1
+	git ls-tree --name-only refs/notes/many_notes |
+	while read path
+	do
+		if test $(expr length "$path") -ge 40
+		then
+			return 1
+		fi
+	done
+
+'
+
+cat >>expect_non-note1 << EOF
+This is not a note, but rather a regular file residing in a notes tree
+EOF
+
+cat >>expect_non-note2 << EOF
+Non-note file
+EOF
+
+cat >>expect_non-note3 << EOF
+Another non-note file
+EOF
+
+test_expect_success 'verify that non-notes are untouched by a fanout change' '
+
+	git cat-file -p refs/notes/many_notes:foobar/non-note.txt > actual &&
+	test_cmp expect_non-note1 actual &&
+	git cat-file -p refs/notes/many_notes:deadbeef > actual &&
+	test_cmp expect_non-note2 actual &&
+	git cat-file -p refs/notes/many_notes:de/adbeef > actual &&
+	test_cmp expect_non-note3 actual
+
+'
+
+# Change the notes for the three top commits
+test_tick
+cat >input <<INPUT_END
+commit refs/notes/many_notes
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+changing notes for the top three commits
+COMMIT
+from refs/notes/many_notes^0
+INPUT_END
+
+rm expect
+i=$num_commits
+j=0
+while test $j -lt 3
+do
+	cat >>input <<INPUT_END
+N inline refs/heads/many_commits~$j
+data <<EOF
+changed note for commit #$i
+EOF
+INPUT_END
+	cat >>expect <<EXPECT_END
+    commit #$i
+    changed note for commit #$i
+EXPECT_END
+	i=$(($i - 1))
+	j=$(($j + 1))
+done
+
+test_expect_success 'change a few existing notes' '
+
+	git fast-import <input &&
+	GIT_NOTES_REF=refs/notes/many_notes git log -n3 refs/heads/many_commits |
+	    grep "^    " > actual &&
+	test_cmp expect actual
+
+'
+
+test_expect_success 'verify that changing notes respect existing fanout' '
+
+	# None of the entries in the top-level notes tree should be a full SHA1
+	git ls-tree --name-only refs/notes/many_notes |
+	while read path
+	do
+		if test $(expr length "$path") -ge 40
+		then
+			return 1
+		fi
+	done
+
+'
+
+remaining_notes=10
+test_tick
+cat >input <<INPUT_END
+commit refs/notes/many_notes
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+removing all notes but $remaining_notes
+COMMIT
+from refs/notes/many_notes^0
+INPUT_END
+
+i=$(($num_commits - $remaining_notes))
+for sha1 in $(git rev-list -n $i refs/heads/many_commits)
+do
+	cat >>input <<INPUT_END
+N 0000000000000000000000000000000000000000 $sha1
+INPUT_END
+done
+
+i=$num_commits
+rm expect
+while test $i -gt 0
+do
+	cat >>expect <<EXPECT_END
+    commit #$i
+EXPECT_END
+	if test $i -le $remaining_notes
+	then
+		cat >>expect <<EXPECT_END
+    note for commit #$i
+EXPECT_END
+	fi
+	i=$(($i - 1))
+done
+
+test_expect_success 'remove lots of notes' '
+
+	git fast-import <input &&
+	GIT_NOTES_REF=refs/notes/many_notes git log refs/heads/many_commits |
+	    grep "^    " > actual &&
+	test_cmp expect actual
+
+'
+
+test_expect_success 'verify that removing notes trigger fanout consolidation' '
+
+	# All entries in the top-level notes tree should be a full SHA1
+	git ls-tree --name-only -r refs/notes/many_notes |
+	while read path
+	do
+		# Explicitly ignore the non-note paths
+		test "$path" = "foobar/non-note.txt" && continue
+		test "$path" = "deadbeef" && continue
+		test "$path" = "de/adbeef" && continue
+
+		if test $(expr length "$path") -ne 40
+		then
+			return 1
+		fi
+	done
+
+'
+
+test_expect_success 'verify that non-notes are untouched by a fanout change' '
+
+	git cat-file -p refs/notes/many_notes:foobar/non-note.txt > actual &&
+	test_cmp expect_non-note1 actual &&
+	git cat-file -p refs/notes/many_notes:deadbeef > actual &&
+	test_cmp expect_non-note2 actual &&
+	git cat-file -p refs/notes/many_notes:de/adbeef > actual &&
+	test_cmp expect_non-note3 actual
+
+'
+
+
+rm input expect
+num_notes_refs=10
+num_commits=16
+some_commits=8
+# Create commits
+fast_import_commits "refs/heads/more_commits" $num_commits input
+# Create one note per above commit per notes ref
+i=0
+while test $i -lt $num_notes_refs
+do
+	i=$(($i + 1))
+	fast_import_notes "refs/notes/more_notes_$i" $num_commits input
+done
+# Trigger branch reloading in git-fast-import by repeating the note creation
+i=0
+while test $i -lt $num_notes_refs
+do
+	i=$(($i + 1))
+	fast_import_notes "refs/notes/more_notes_$i" $some_commits input " (2)"
+done
+# Finally create the expected output from the notes in refs/notes/more_notes_1
+i=$num_commits
+while test $i -gt 0
+do
+	note_data="note for commit #$i"
+	if test $i -le $some_commits
+	then
+		note_data="$note_data (2)"
+	fi
+	cat >>expect <<EXPECT_END
+    commit #$i
+    $note_data
+EXPECT_END
+	i=$(($i - 1))
+done
+
+test_expect_success "add notes to $num_commits commits in each of $num_notes_refs refs" '
+
+	git fast-import --active-branches=5 <input &&
+	GIT_NOTES_REF=refs/notes/more_notes_1 git log refs/heads/more_commits |
+	    grep "^    " > actual &&
+	test_cmp expect actual
+
+'
+
+test_done
diff --git a/t/t9350-fast-export.sh b/t/t9350-fast-export.sh
new file mode 100755
index 0000000..3e821f9
--- /dev/null
+++ b/t/t9350-fast-export.sh
@@ -0,0 +1,443 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Johannes E. Schindelin
+#
+
+test_description='git fast-export'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+
+	echo break it > file0 &&
+	git add file0 &&
+	test_tick &&
+	echo Wohlauf > file &&
+	git add file &&
+	test_tick &&
+	git commit -m initial &&
+	echo die Luft > file &&
+	echo geht frisch > file2 &&
+	git add file file2 &&
+	test_tick &&
+	git commit -m second &&
+	echo und > file2 &&
+	test_tick &&
+	git commit -m third file2 &&
+	test_tick &&
+	git tag rein &&
+	git checkout -b wer HEAD^ &&
+	echo lange > file2 &&
+	test_tick &&
+	git commit -m sitzt file2 &&
+	test_tick &&
+	git tag -a -m valentin muss &&
+	git merge -s ours master
+
+'
+
+test_expect_success 'fast-export | fast-import' '
+
+	MASTER=$(git rev-parse --verify master) &&
+	REIN=$(git rev-parse --verify rein) &&
+	WER=$(git rev-parse --verify wer) &&
+	MUSS=$(git rev-parse --verify muss) &&
+	mkdir new &&
+	git --git-dir=new/.git init &&
+	git fast-export --all |
+	(cd new &&
+	 git fast-import &&
+	 test $MASTER = $(git rev-parse --verify refs/heads/master) &&
+	 test $REIN = $(git rev-parse --verify refs/tags/rein) &&
+	 test $WER = $(git rev-parse --verify refs/heads/wer) &&
+	 test $MUSS = $(git rev-parse --verify refs/tags/muss))
+
+'
+
+test_expect_success 'fast-export master~2..master' '
+
+	git fast-export master~2..master |
+		sed "s/master/partial/" |
+		(cd new &&
+		 git fast-import &&
+		 test $MASTER != $(git rev-parse --verify refs/heads/partial) &&
+		 git diff --exit-code master partial &&
+		 git diff --exit-code master^ partial^ &&
+		 test_must_fail git rev-parse partial~2)
+
+'
+
+test_expect_success 'iso-8859-1' '
+
+	git config i18n.commitencoding ISO8859-1 &&
+	# use author and committer name in ISO-8859-1 to match it.
+	. "$TEST_DIRECTORY"/t3901-8859-1.txt &&
+	test_tick &&
+	echo rosten >file &&
+	git commit -s -m den file &&
+	git fast-export wer^..wer |
+		sed "s/wer/i18n/" |
+		(cd new &&
+		 git fast-import &&
+		 git cat-file commit i18n | grep "Áéí óú")
+
+'
+test_expect_success 'import/export-marks' '
+
+	git checkout -b marks master &&
+	git fast-export --export-marks=tmp-marks HEAD &&
+	test -s tmp-marks &&
+	test_line_count = 3 tmp-marks &&
+	test $(
+		git fast-export --import-marks=tmp-marks\
+		--export-marks=tmp-marks HEAD |
+		grep ^commit |
+		wc -l) \
+	-eq 0 &&
+	echo change > file &&
+	git commit -m "last commit" file &&
+	test $(
+		git fast-export --import-marks=tmp-marks \
+		--export-marks=tmp-marks HEAD |
+		grep ^commit\  |
+		wc -l) \
+	-eq 1 &&
+	test_line_count = 4 tmp-marks
+
+'
+
+cat > signed-tag-import << EOF
+tag sign-your-name
+from $(git rev-parse HEAD)
+tagger C O Mitter <committer@example.com> 1112911993 -0700
+data 210
+A message for a sign
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.5 (GNU/Linux)
+
+fakedsignaturefakedsignaturefakedsignaturefakedsignaturfakedsign
+aturefakedsignaturefake=
+=/59v
+-----END PGP SIGNATURE-----
+EOF
+
+test_expect_success 'set up faked signed tag' '
+
+	cat signed-tag-import | git fast-import
+
+'
+
+test_expect_success 'signed-tags=abort' '
+
+	test_must_fail git fast-export --signed-tags=abort sign-your-name
+
+'
+
+test_expect_success 'signed-tags=verbatim' '
+
+	git fast-export --signed-tags=verbatim sign-your-name > output &&
+	grep PGP output
+
+'
+
+test_expect_success 'signed-tags=strip' '
+
+	git fast-export --signed-tags=strip sign-your-name > output &&
+	! grep PGP output
+
+'
+
+test_expect_success 'setup submodule' '
+
+	git checkout -f master &&
+	mkdir sub &&
+	(
+		cd sub &&
+		git init  &&
+		echo test file > file &&
+		git add file &&
+		git commit -m sub_initial
+	) &&
+	git submodule add "`pwd`/sub" sub &&
+	git commit -m initial &&
+	test_tick &&
+	(
+		cd sub &&
+		echo more data >> file &&
+		git add file &&
+		git commit -m sub_second
+	) &&
+	git add sub &&
+	git commit -m second
+
+'
+
+test_expect_success 'submodule fast-export | fast-import' '
+
+	SUBENT1=$(git ls-tree master^ sub) &&
+	SUBENT2=$(git ls-tree master sub) &&
+	rm -rf new &&
+	mkdir new &&
+	git --git-dir=new/.git init &&
+	git fast-export --signed-tags=strip --all |
+	(cd new &&
+	 git fast-import &&
+	 test "$SUBENT1" = "$(git ls-tree refs/heads/master^ sub)" &&
+	 test "$SUBENT2" = "$(git ls-tree refs/heads/master sub)" &&
+	 git checkout master &&
+	 git submodule init &&
+	 git submodule update &&
+	 cmp sub/file ../sub/file)
+
+'
+
+GIT_AUTHOR_NAME='A U Thor'; export GIT_AUTHOR_NAME
+GIT_COMMITTER_NAME='C O Mitter'; export GIT_COMMITTER_NAME
+
+test_expect_success 'setup copies' '
+
+	git config --unset i18n.commitencoding &&
+	git checkout -b copy rein &&
+	git mv file file3 &&
+	git commit -m move1 &&
+	test_tick &&
+	cp file2 file4 &&
+	git add file4 &&
+	git mv file2 file5 &&
+	git commit -m copy1 &&
+	test_tick &&
+	cp file3 file6 &&
+	git add file6 &&
+	git commit -m copy2 &&
+	test_tick &&
+	echo more text >> file6 &&
+	echo even more text >> file6 &&
+	git add file6 &&
+	git commit -m modify &&
+	test_tick &&
+	cp file6 file7 &&
+	echo test >> file7 &&
+	git add file7 &&
+	git commit -m copy_modify
+
+'
+
+test_expect_success 'fast-export -C -C | fast-import' '
+
+	ENTRY=$(git rev-parse --verify copy) &&
+	rm -rf new &&
+	mkdir new &&
+	git --git-dir=new/.git init &&
+	git fast-export -C -C --signed-tags=strip --all > output &&
+	grep "^C file6 file7\$" output &&
+	cat output |
+	(cd new &&
+	 git fast-import &&
+	 test $ENTRY = $(git rev-parse --verify refs/heads/copy))
+
+'
+
+test_expect_success 'fast-export | fast-import when master is tagged' '
+
+	git tag -m msg last &&
+	git fast-export -C -C --signed-tags=strip --all > output &&
+	test $(grep -c "^tag " output) = 3
+
+'
+
+cat > tag-content << EOF
+object $(git rev-parse HEAD)
+type commit
+tag rosten
+EOF
+
+test_expect_success 'cope with tagger-less tags' '
+
+	TAG=$(git hash-object -t tag -w tag-content) &&
+	git update-ref refs/tags/sonnenschein $TAG &&
+	git fast-export -C -C --signed-tags=strip --all > output &&
+	test $(grep -c "^tag " output) = 4 &&
+	! grep "Unspecified Tagger" output &&
+	git fast-export -C -C --signed-tags=strip --all \
+		--fake-missing-tagger > output &&
+	test $(grep -c "^tag " output) = 4 &&
+	grep "Unspecified Tagger" output
+
+'
+
+test_expect_success 'setup for limiting exports by PATH' '
+	mkdir limit-by-paths &&
+	(
+		cd limit-by-paths &&
+		git init &&
+		echo hi > there &&
+		git add there &&
+		git commit -m "First file" &&
+		echo foo > bar &&
+		git add bar &&
+		git commit -m "Second file" &&
+		git tag -a -m msg mytag &&
+		echo morefoo >> bar &&
+		git add bar &&
+		git commit -m "Change to second file"
+	)
+'
+
+cat > limit-by-paths/expected << EOF
+blob
+mark :1
+data 3
+hi
+
+reset refs/tags/mytag
+commit refs/tags/mytag
+mark :2
+author A U Thor <author@example.com> 1112912713 -0700
+committer C O Mitter <committer@example.com> 1112912713 -0700
+data 11
+First file
+M 100644 :1 there
+
+EOF
+
+test_expect_success 'dropping tag of filtered out object' '
+(
+	cd limit-by-paths &&
+	git fast-export --tag-of-filtered-object=drop mytag -- there > output &&
+	test_cmp output expected
+)
+'
+
+cat >> limit-by-paths/expected << EOF
+tag mytag
+from :2
+tagger C O Mitter <committer@example.com> 1112912713 -0700
+data 4
+msg
+
+EOF
+
+test_expect_success 'rewriting tag of filtered out object' '
+(
+	cd limit-by-paths &&
+	git fast-export --tag-of-filtered-object=rewrite mytag -- there > output &&
+	test_cmp output expected
+)
+'
+
+cat > limit-by-paths/expected << EOF
+blob
+mark :1
+data 4
+foo
+
+blob
+mark :2
+data 3
+hi
+
+reset refs/heads/master
+commit refs/heads/master
+mark :3
+author A U Thor <author@example.com> 1112912713 -0700
+committer C O Mitter <committer@example.com> 1112912713 -0700
+data 12
+Second file
+M 100644 :1 bar
+M 100644 :2 there
+
+EOF
+
+test_expect_failure 'no exact-ref revisions included' '
+	(
+		cd limit-by-paths &&
+		git fast-export master~2..master~1 > output &&
+		test_cmp output expected
+	)
+'
+
+test_expect_success 'path limiting with import-marks does not lose unmodified files'        '
+	git checkout -b simple marks~2 &&
+	git fast-export --export-marks=marks simple -- file > /dev/null &&
+	echo more content >> file &&
+	test_tick &&
+	git commit -mnext file &&
+	git fast-export --import-marks=marks simple -- file file0 | grep file0
+'
+
+test_expect_success 'full-tree re-shows unmodified files'        '
+	git checkout -f simple &&
+	test $(git fast-export --full-tree simple | grep -c file0) -eq 3
+'
+
+test_expect_success 'set-up a few more tags for tag export tests' '
+	git checkout -f master &&
+	HEAD_TREE=`git show -s --pretty=raw HEAD | grep tree | sed "s/tree //"` &&
+	git tag    tree_tag        -m "tagging a tree" $HEAD_TREE &&
+	git tag -a tree_tag-obj    -m "tagging a tree" $HEAD_TREE &&
+	git tag    tag-obj_tag     -m "tagging a tag" tree_tag-obj &&
+	git tag -a tag-obj_tag-obj -m "tagging a tag" tree_tag-obj
+'
+
+test_expect_success 'tree_tag'        '
+	mkdir result &&
+	(cd result && git init) &&
+	git fast-export tree_tag > fe-stream &&
+	(cd result && git fast-import < ../fe-stream)
+'
+
+# NEEDSWORK: not just check return status, but validate the output
+test_expect_success 'tree_tag-obj'    'git fast-export tree_tag-obj'
+test_expect_success 'tag-obj_tag'     'git fast-export tag-obj_tag'
+test_expect_success 'tag-obj_tag-obj' 'git fast-export tag-obj_tag-obj'
+
+test_expect_success SYMLINKS 'directory becomes symlink'        '
+	git init dirtosymlink &&
+	git init result &&
+	(
+		cd dirtosymlink &&
+		mkdir foo &&
+		mkdir bar &&
+		echo hello > foo/world &&
+		echo hello > bar/world &&
+		git add foo/world bar/world &&
+		git commit -q -mone &&
+		git rm -r foo &&
+		ln -s bar foo &&
+		git add foo &&
+		git commit -q -mtwo
+	) &&
+	(
+		cd dirtosymlink &&
+		git fast-export master -- foo |
+		(cd ../result && git fast-import --quiet)
+	) &&
+	(cd result && git show master:foo)
+'
+
+test_expect_success 'fast-export quotes pathnames' '
+	git init crazy-paths &&
+	(cd crazy-paths &&
+	 blob=`echo foo | git hash-object -w --stdin` &&
+	 git update-index --add \
+		--cacheinfo 100644 $blob "$(printf "path with\\nnewline")" \
+		--cacheinfo 100644 $blob "path with \"quote\"" \
+		--cacheinfo 100644 $blob "path with \\backslash" \
+		--cacheinfo 100644 $blob "path with space" &&
+	 git commit -m addition &&
+	 git ls-files -z -s | "$PERL_PATH" -0pe "s{\\t}{$&subdir/}" >index &&
+	 git read-tree --empty &&
+	 git update-index -z --index-info <index &&
+	 git commit -m rename &&
+	 git read-tree --empty &&
+	 git commit -m deletion &&
+	 git fast-export -M HEAD >export.out &&
+	 git rev-list HEAD >expect &&
+	 git init result &&
+	 cd result &&
+	 git fast-import <../export.out &&
+	 git rev-list HEAD >actual &&
+	 test_cmp ../expect actual
+	)
+'
+
+test_done
diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh
index 64f947d..806623e 100755
--- a/t/t9400-git-cvsserver-server.sh
+++ b/t/t9400-git-cvsserver-server.sh
@@ -11,17 +11,17 @@
 . ./test-lib.sh
 
 if ! test_have_prereq PERL; then
-	say 'skipping git cvsserver tests, perl not available'
+	skip_all='skipping git cvsserver tests, perl not available'
 	test_done
 fi
 cvs >/dev/null 2>&1
 if test $? -ne 1
 then
-    say 'skipping git-cvsserver tests, cvs not found'
+    skip_all='skipping git-cvsserver tests, cvs not found'
     test_done
 fi
-perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || {
-    say 'skipping git-cvsserver tests, Perl SQLite interface unavailable'
+"$PERL_PATH" -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || {
+    skip_all='skipping git-cvsserver tests, Perl SQLite interface unavailable'
     test_done
 }
 
@@ -48,14 +48,16 @@
   git pull secondroot master &&
   git clone -q --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 &&
   GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true &&
-  GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log"
+  GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log" &&
+  GIT_DIR="$SERVERDIR" git config gitcvs.authdb "$SERVERDIR/auth.db" &&
+  echo cvsuser:cvGVEarMLnhlA > "$SERVERDIR/auth.db"
 '
 
 # note that cvs doesn't accept absolute pathnames
 # as argument to co -d
 test_expect_success 'basic checkout' \
   'GIT_CONFIG="$git_config" cvs -Q co -d cvswork master &&
-   test "$(echo $(grep -v ^D cvswork/CVS/Entries|cut -d/ -f2,3,5 | head -n 1))" = "empty/1.1/"
+   test "$(echo $(grep -v ^D cvswork/CVS/Entries|cut -d/ -f2,3,5 | head -n 1))" = "empty/1.1/" &&
    test "$(echo $(grep -v ^D cvswork/CVS/Entries|cut -d/ -f2,3,5 | sed -ne \$p))" = "secondrootfile/1.1/"'
 
 #------------------------
@@ -94,9 +96,17 @@
 END VERIFICATION REQUEST
 EOF
 
+cat >login-git-ok <<EOF
+BEGIN VERIFICATION REQUEST
+$SERVERDIR
+cvsuser
+Ah<Z:yZZ30 e
+END VERIFICATION REQUEST
+EOF
+
 test_expect_success 'pserver authentication' \
   'cat request-anonymous | git-cvsserver pserver >log 2>&1 &&
-   sed -ne \$p log | grep "^I LOVE YOU$"'
+   sed -ne \$p log | grep "^I LOVE YOU\$"'
 
 test_expect_success 'pserver authentication failure (non-anonymous user)' \
   'if cat request-git | git-cvsserver pserver >log 2>&1
@@ -105,11 +115,15 @@
    else
        true
    fi &&
-   sed -ne \$p log | grep "^I HATE YOU$"'
+   sed -ne \$p log | grep "^I HATE YOU\$"'
+
+test_expect_success 'pserver authentication success (non-anonymous user with password)' \
+  'cat login-git-ok | git-cvsserver pserver >log 2>&1 &&
+   sed -ne \$p log | grep "^I LOVE YOU\$"'
 
 test_expect_success 'pserver authentication (login)' \
   'cat login-anonymous | git-cvsserver pserver >log 2>&1 &&
-   sed -ne \$p log | grep "^I LOVE YOU$"'
+   sed -ne \$p log | grep "^I LOVE YOU\$"'
 
 test_expect_success 'pserver authentication failure (login/non-anonymous user)' \
   'if cat login-git | git-cvsserver pserver >log 2>&1
@@ -118,7 +132,7 @@
    else
        true
    fi &&
-   sed -ne \$p log | grep "^I HATE YOU$"'
+   sed -ne \$p log | grep "^I HATE YOU\$"'
 
 
 # misuse pserver authentication for testing of req_Root
@@ -156,7 +170,7 @@
 
 test_expect_success 'req_Root (strict paths)' \
   'cat request-anonymous | git-cvsserver --strict-paths pserver "$SERVERDIR" >log 2>&1 &&
-   sed -ne \$p log | grep "^I LOVE YOU$"'
+   sed -ne \$p log | grep "^I LOVE YOU\$"'
 
 test_expect_success 'req_Root failure (strict-paths)' '
     ! cat request-anonymous |
@@ -165,7 +179,7 @@
 
 test_expect_success 'req_Root (w/o strict-paths)' \
   'cat request-anonymous | git-cvsserver pserver "$WORKDIR/" >log 2>&1 &&
-   sed -ne \$p log | grep "^I LOVE YOU$"'
+   sed -ne \$p log | grep "^I LOVE YOU\$"'
 
 test_expect_success 'req_Root failure (w/o strict-paths)' '
     ! cat request-anonymous |
@@ -183,7 +197,7 @@
 
 test_expect_success 'req_Root (base-path)' \
   'cat request-base | git-cvsserver --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" >log 2>&1 &&
-   sed -ne \$p log | grep "^I LOVE YOU$"'
+   sed -ne \$p log | grep "^I LOVE YOU\$"'
 
 test_expect_success 'req_Root failure (base-path)' '
     ! cat request-anonymous |
@@ -194,14 +208,14 @@
 
 test_expect_success 'req_Root (export-all)' \
   'cat request-anonymous | git-cvsserver --export-all pserver "$WORKDIR" >log 2>&1 &&
-   sed -ne \$p log | grep "^I LOVE YOU$"'
+   sed -ne \$p log | grep "^I LOVE YOU\$"'
 
 test_expect_success 'req_Root failure (export-all w/o whitelist)' \
   '! (cat request-anonymous | git-cvsserver --export-all pserver >log 2>&1 || false)'
 
 test_expect_success 'req_Root (everything together)' \
   'cat request-base | git-cvsserver --export-all --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" >log 2>&1 &&
-   sed -ne \$p log | grep "^I LOVE YOU$"'
+   sed -ne \$p log | grep "^I LOVE YOU\$"'
 
 GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true || exit 1
 
@@ -226,7 +240,7 @@
   'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true &&
    GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled false &&
    GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1 &&
-   diff -q cvswork cvswork2'
+   test_cmp cvswork cvswork2'
 
 rm -fr cvswork2
 test_expect_success 'gitcvs.ext.enabled = false' \
@@ -247,7 +261,7 @@
   'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true &&
    GIT_DIR="$SERVERDIR" git config gitcvs.dbname %Ggitcvs.%a.%m.sqlite &&
    GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1 &&
-   diff -q cvswork cvswork2 &&
+   test_cmp cvswork cvswork2 &&
    test -f "$SERVERDIR/gitcvs.ext.master.sqlite" &&
    cmp "$SERVERDIR/gitcvs.master.sqlite" "$SERVERDIR/gitcvs.ext.master.sqlite"'
 
@@ -257,7 +271,7 @@
    GIT_DIR="$SERVERDIR" git config gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite &&
    GIT_DIR="$SERVERDIR" git config gitcvs.dbname %Ggitcvs2.%a.%m.sqlite &&
    GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1 &&
-   diff -q cvswork cvswork2 &&
+   test_cmp cvswork cvswork2 &&
    test -f "$SERVERDIR/gitcvs1.ext.master.sqlite" &&
    test ! -f "$SERVERDIR/gitcvs2.ext.master.sqlite" &&
    cmp "$SERVERDIR/gitcvs.master.sqlite" "$SERVERDIR/gitcvs1.ext.master.sqlite"'
@@ -282,7 +296,7 @@
    cd cvswork &&
    GIT_CONFIG="$git_config" cvs -Q update &&
    test "$(echo $(grep testfile1 CVS/Entries|cut -d/ -f2,3,5))" = "testfile1/1.1/" &&
-   diff -q testfile1 ../testfile1'
+   test_cmp testfile1 ../testfile1'
 
 cd "$WORKDIR"
 test_expect_success 'cvs update (update existing file)' \
@@ -293,7 +307,7 @@
    cd cvswork &&
    GIT_CONFIG="$git_config" cvs -Q update &&
    test "$(echo $(grep testfile1 CVS/Entries|cut -d/ -f2,3,5))" = "testfile1/1.2/" &&
-   diff -q testfile1 ../testfile1'
+   test_cmp testfile1 ../testfile1'
 
 cd "$WORKDIR"
 #TODO: cvsserver doesn't support update w/o -d
@@ -322,7 +336,7 @@
    (for dir in A A/B A/B/C A/D E; do
       filename="file_in_$(echo $dir|sed -e "s#/# #g")" &&
       if test "$(echo $(grep -v ^D $dir/CVS/Entries|cut -d/ -f2,3,5))" = "$filename/1.1/" &&
-           diff -q "$dir/$filename" "../$dir/$filename"; then
+	test_cmp "$dir/$filename" "../$dir/$filename"; then
         :
       else
         echo >failure
@@ -349,7 +363,7 @@
    cd cvswork &&
    GIT_CONFIG="$git_config" cvs -Q update &&
    test "$(echo $(grep testfile1 CVS/Entries|cut -d/ -f2,3,5))" = "testfile1/1.4/" &&
-   diff -q testfile1 ../testfile1'
+   test_cmp testfile1 ../testfile1'
 
 cd "$WORKDIR"
 test_expect_success 'cvs update (merge)' \
@@ -366,7 +380,7 @@
    cd cvswork &&
    GIT_CONFIG="$git_config" cvs -Q update &&
    test "$(echo $(grep merge CVS/Entries|cut -d/ -f2,3,5))" = "merge/1.1/" &&
-   diff -q merge ../merge &&
+   test_cmp merge ../merge &&
    ( echo Line 0; cat merge ) >merge.tmp &&
    mv merge.tmp merge &&
    cd "$WORKDIR" &&
@@ -377,7 +391,7 @@
    cd cvswork &&
    sleep 1 && touch merge &&
    GIT_CONFIG="$git_config" cvs -Q update &&
-   diff -q merge ../expected'
+   test_cmp merge ../expected'
 
 cd "$WORKDIR"
 
@@ -402,13 +416,13 @@
    git push gitcvs.git >/dev/null &&
    cd cvswork &&
    GIT_CONFIG="$git_config" cvs -Q update &&
-   diff -q merge ../expected.C'
+   test_cmp merge ../expected.C'
 
 cd "$WORKDIR"
 test_expect_success 'cvs update (-C)' \
   'cd cvswork &&
    GIT_CONFIG="$git_config" cvs -Q update -C &&
-   diff -q merge ../merge'
+   test_cmp merge ../merge'
 
 cd "$WORKDIR"
 test_expect_success 'cvs update (merge no-op)' \
@@ -420,7 +434,7 @@
     cd cvswork &&
     sleep 1 && touch merge &&
     GIT_CONFIG="$git_config" cvs -Q update &&
-    diff -q merge ../merge'
+    test_cmp merge ../merge'
 
 cd "$WORKDIR"
 test_expect_success 'cvs update (-p)' '
@@ -435,7 +449,7 @@
     rm -f failures &&
     for i in merge no-lf empty really-empty; do
         GIT_CONFIG="$git_config" cvs update -p "$i" >$i.out
-        diff $i.out ../$i >>failures 2>&1
+	test_cmp $i.out ../$i >>failures 2>&1
     done &&
     test -z "$(cat failures)"
 '
@@ -462,14 +476,14 @@
     cd cvswork &&
     GIT_CONFIG="$git_config" cvs update &&
     GIT_CONFIG="$git_config" cvs status | grep "^File: status.file" >../out &&
-    test $(wc -l <../out) = 2
+    test_line_count = 2 ../out
 '
 
 cd "$WORKDIR"
 test_expect_success 'cvs status (nonrecursive)' '
     cd cvswork &&
     GIT_CONFIG="$git_config" cvs status -l | grep "^File: status.file" >../out &&
-    test $(wc -l <../out) = 1
+    test_line_count = 1 ../out
 '
 
 cd "$WORKDIR"
@@ -486,8 +500,8 @@
 cd "$WORKDIR"
 test_expect_success 'cvs co -c (shows module database)' '
     GIT_CONFIG="$git_config" cvs co -c > out &&
-    grep "^master[	 ]\+master$" < out &&
-    ! grep -v "^master[	 ]\+master$" < out
+    grep "^master[	 ][ 	]*master$" <out &&
+    ! grep -v "^master[	 ][ 	]*master$" <out
 '
 
 #------------
diff --git a/t/t9401-git-cvsserver-crlf.sh b/t/t9401-git-cvsserver-crlf.sh
index aca40c1..ff6d6fb 100755
--- a/t/t9401-git-cvsserver-crlf.sh
+++ b/t/t9401-git-cvsserver-crlf.sh
@@ -11,14 +11,6 @@
 
 . ./test-lib.sh
 
-q_to_nul () {
-    perl -pe 'y/Q/\000/'
-}
-
-q_to_cr () {
-    tr Q '\015'
-}
-
 marked_as () {
     foundEntry="$(grep "^/$2/" "$1/CVS/Entries")"
     if [ x"$foundEntry" = x"" ] ; then
@@ -49,16 +41,16 @@
 cvs >/dev/null 2>&1
 if test $? -ne 1
 then
-    say 'skipping git-cvsserver tests, cvs not found'
+    skip_all='skipping git-cvsserver tests, cvs not found'
     test_done
 fi
 if ! test_have_prereq PERL
 then
-    say 'skipping git-cvsserver tests, perl not available'
+    skip_all='skipping git-cvsserver tests, perl not available'
     test_done
 fi
-perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || {
-    say 'skipping git-cvsserver tests, Perl SQLite interface unavailable'
+"$PERL_PATH" -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || {
+    skip_all='skipping git-cvsserver tests, Perl SQLite interface unavailable'
     test_done
 }
 
@@ -78,7 +70,7 @@
     mkdir subdir &&
     echo "Another text file" > subdir/file.h &&
     echo "Another binary: Q (this time CR)" | q_to_cr > subdir/withCr.bin &&
-    echo "Mixed up NUL, but marked text: Q <- there" | q_to_nul > mixedUp.c
+    echo "Mixed up NUL, but marked text: Q <- there" | q_to_nul > mixedUp.c &&
     echo "Unspecified" > subdir/unspecified.other &&
     echo "/*.bin -crlf" > .gitattributes &&
     echo "/*.c crlf" >> .gitattributes &&
@@ -137,21 +129,22 @@
 '
 
 test_expect_success 'adding files' '
-    cd cvswork/subdir &&
+    (cd cvswork &&
+    (cd subdir &&
     echo "more text" > src.c &&
     GIT_CONFIG="$git_config" cvs -Q add src.c >cvs.log 2>&1 &&
     marked_as . src.c "" &&
-    echo "psuedo-binary" > temp.bin &&
-    cd .. &&
+    echo "psuedo-binary" > temp.bin
+    ) &&
     GIT_CONFIG="$git_config" cvs -Q add subdir/temp.bin >cvs.log 2>&1 &&
     marked_as subdir temp.bin "-kb" &&
     cd subdir &&
     GIT_CONFIG="$git_config" cvs -Q ci -m "adding files" >cvs.log 2>&1 &&
     marked_as . temp.bin "-kb" &&
     marked_as . src.c ""
+    )
 '
 
-cd "$WORKDIR"
 test_expect_success 'updating' '
     git pull gitcvs.git &&
     echo 'hi' > subdir/newfile.bin &&
@@ -161,9 +154,9 @@
     git add subdir/newfile.bin subdir/file.h subdir/newfile.c binfile.bin &&
     git commit -q -m "Add and change some files" &&
     git push gitcvs.git >/dev/null &&
-    cd cvswork &&
-    GIT_CONFIG="$git_config" cvs -Q update &&
-    cd .. &&
+    (cd cvswork &&
+    GIT_CONFIG="$git_config" cvs -Q update
+    ) &&
     marked_as cvswork textfile.c "" &&
     marked_as cvswork binfile.bin -kb &&
     marked_as cvswork .gitattributes "" &&
@@ -241,35 +234,35 @@
 '
 
 test_expect_success 'add text (guess)' '
-    cd cvswork &&
+    (cd cvswork &&
     echo "simpleText" > simpleText.c &&
-    GIT_CONFIG="$git_config" cvs -Q add simpleText.c &&
-    cd .. &&
+    GIT_CONFIG="$git_config" cvs -Q add simpleText.c
+    ) &&
     marked_as cvswork simpleText.c ""
 '
 
 test_expect_success 'add bin (guess)' '
-    cd cvswork &&
+    (cd cvswork &&
     echo "simpleBin: NUL: Q <- there" | q_to_nul > simpleBin.bin &&
-    GIT_CONFIG="$git_config" cvs -Q add simpleBin.bin &&
-    cd .. &&
+    GIT_CONFIG="$git_config" cvs -Q add simpleBin.bin
+    ) &&
     marked_as cvswork simpleBin.bin -kb
 '
 
 test_expect_success 'remove files (guess)' '
-    cd cvswork &&
+    (cd cvswork &&
     GIT_CONFIG="$git_config" cvs -Q rm -f subdir/file.h &&
-    cd subdir &&
-    GIT_CONFIG="$git_config" cvs -Q rm -f withCr.bin &&
-    cd ../.. &&
+    (cd subdir &&
+    GIT_CONFIG="$git_config" cvs -Q rm -f withCr.bin
+    )) &&
     marked_as cvswork/subdir withCr.bin -kb &&
     marked_as cvswork/subdir file.h ""
 '
 
 test_expect_success 'cvs ci (guess)' '
-    cd cvswork &&
-    GIT_CONFIG="$git_config" cvs -Q ci -m "add/rm files" >cvs.log 2>&1 &&
-    cd .. &&
+    (cd cvswork &&
+    GIT_CONFIG="$git_config" cvs -Q ci -m "add/rm files" >cvs.log 2>&1
+    ) &&
     marked_as cvswork textfile.c "" &&
     marked_as cvswork binfile.bin -kb &&
     marked_as cvswork .gitattributes "" &&
@@ -286,9 +279,9 @@
 '
 
 test_expect_success 'update subdir of other copy (guess)' '
-    cd cvswork2/subdir &&
-    GIT_CONFIG="$git_config" cvs -Q update &&
-    cd ../.. &&
+    (cd cvswork2/subdir &&
+    GIT_CONFIG="$git_config" cvs -Q update
+    ) &&
     marked_as cvswork2 textfile.c "" &&
     marked_as cvswork2 binfile.bin -kb &&
     marked_as cvswork2 .gitattributes "" &&
@@ -312,11 +305,11 @@
     git add multilineTxt.c &&
     git commit -q -m "modify multiline file" >> "${WORKDIR}/marked.log" &&
     git push gitcvs.git >/dev/null &&
-    cd cvswork2 &&
+    (cd cvswork2 &&
     sed "s/1/replaced_1/" < multilineTxt.c > ml.temp &&
     mv ml.temp multilineTxt.c &&
-    GIT_CONFIG="$git_config" cvs update > cvs.log 2>&1 &&
-    cd .. &&
+    GIT_CONFIG="$git_config" cvs update > cvs.log 2>&1
+    ) &&
     marked_as cvswork2 textfile.c "" &&
     marked_as cvswork2 binfile.bin -kb &&
     marked_as cvswork2 .gitattributes "" &&
diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh
index 6275181..90bb605 100755
--- a/t/t9500-gitweb-standalone-no-errors.sh
+++ b/t/t9500-gitweb-standalone-no-errors.sh
@@ -9,73 +9,8 @@
 commandline, and checks that it would not write any errors
 or warnings to log.'
 
-gitweb_init () {
-	safe_pwd="$(perl -MPOSIX=getcwd -e 'print quotemeta(getcwd)')"
-	cat >gitweb_config.perl <<EOF
-#!/usr/bin/perl
 
-# gitweb configuration for tests
-
-our \$version = "current";
-our \$GIT = "git";
-our \$projectroot = "$safe_pwd";
-our \$project_maxdepth = 8;
-our \$home_link_str = "projects";
-our \$site_name = "[localhost]";
-our \$site_header = "";
-our \$site_footer = "";
-our \$home_text = "indextext.html";
-our @stylesheets = ("file:///$TEST_DIRECTORY/../gitweb/gitweb.css");
-our \$logo = "file:///$TEST_DIRECTORY/../gitweb/git-logo.png";
-our \$favicon = "file:///$TEST_DIRECTORY/../gitweb/git-favicon.png";
-our \$projects_list = "";
-our \$export_ok = "";
-our \$strict_export = "";
-
-EOF
-
-	cat >.git/description <<EOF
-$0 test repository
-EOF
-}
-
-gitweb_run () {
-	GATEWAY_INTERFACE="CGI/1.1"
-	HTTP_ACCEPT="*/*"
-	REQUEST_METHOD="GET"
-	SCRIPT_NAME="$TEST_DIRECTORY/../gitweb/gitweb.perl"
-	QUERY_STRING=""$1""
-	PATH_INFO=""$2""
-	export GATEWAY_INTERFACE HTTP_ACCEPT REQUEST_METHOD \
-		SCRIPT_NAME QUERY_STRING PATH_INFO
-
-	GITWEB_CONFIG=$(pwd)/gitweb_config.perl
-	export GITWEB_CONFIG
-
-	# some of git commands write to STDERR on error, but this is not
-	# written to web server logs, so we are not interested in that:
-	# we are interested only in properly formatted errors/warnings
-	rm -f gitweb.log &&
-	perl -- "$SCRIPT_NAME" \
-		>/dev/null 2>gitweb.log &&
-	if grep "^[[]" gitweb.log >/dev/null 2>&1; then false; else true; fi
-
-	# gitweb.log is left for debugging
-}
-
-. ./test-lib.sh
-
-if ! test_have_prereq PERL; then
-	say 'skipping gitweb tests, perl not available'
-	test_done
-fi
-
-perl -MEncode -e 'decode_utf8("", Encode::FB_CROAK)' >/dev/null 2>&1 || {
-    say 'skipping gitweb tests, perl version is too old'
-    test_done
-}
-
-gitweb_init
+. ./gitweb-lib.sh
 
 # ----------------------------------------------------------------------
 # no commits (empty, just initialized repository)
@@ -83,42 +18,34 @@
 test_expect_success \
 	'no commits: projects_list (implicit)' \
 	'gitweb_run'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'no commits: projects_index' \
 	'gitweb_run "a=project_index"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'no commits: .git summary (implicit)' \
 	'gitweb_run "p=.git"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'no commits: .git commit (implicit HEAD)' \
 	'gitweb_run "p=.git;a=commit"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'no commits: .git commitdiff (implicit HEAD)' \
 	'gitweb_run "p=.git;a=commitdiff"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'no commits: .git tree (implicit HEAD)' \
 	'gitweb_run "p=.git;a=tree"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'no commits: .git heads' \
 	'gitweb_run "p=.git;a=heads"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'no commits: .git tags' \
 	'gitweb_run "p=.git;a=tags"'
-test_debug 'cat gitweb.log'
 
 
 # ----------------------------------------------------------------------
@@ -134,52 +61,42 @@
 test_expect_success \
 	'projects_list (implicit)' \
 	'gitweb_run'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'projects_index' \
 	'gitweb_run "a=project_index"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'.git summary (implicit)' \
 	'gitweb_run "p=.git"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'.git commit (implicit HEAD)' \
 	'gitweb_run "p=.git;a=commit"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'.git commitdiff (implicit HEAD, root commit)' \
 	'gitweb_run "p=.git;a=commitdiff"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'.git commitdiff_plain (implicit HEAD, root commit)' \
 	'gitweb_run "p=.git;a=commitdiff_plain"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'.git commit (HEAD)' \
 	'gitweb_run "p=.git;a=commit;h=HEAD"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'.git tree (implicit HEAD)' \
 	'gitweb_run "p=.git;a=tree"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'.git blob (file)' \
 	'gitweb_run "p=.git;a=blob;f=file"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'.git blob_plain (file)' \
 	'gitweb_run "p=.git;a=blob_plain;f=file"'
-test_debug 'cat gitweb.log'
 
 # ----------------------------------------------------------------------
 # nonexistent objects
@@ -187,37 +104,30 @@
 test_expect_success \
 	'.git commit (non-existent)' \
 	'gitweb_run "p=.git;a=commit;h=non-existent"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'.git commitdiff (non-existent)' \
 	'gitweb_run "p=.git;a=commitdiff;h=non-existent"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'.git commitdiff (non-existent vs HEAD)' \
 	'gitweb_run "p=.git;a=commitdiff;hp=non-existent;h=HEAD"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'.git tree (0000000000000000000000000000000000000000)' \
 	'gitweb_run "p=.git;a=tree;h=0000000000000000000000000000000000000000"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'.git tag (0000000000000000000000000000000000000000)' \
 	'gitweb_run "p=.git;a=tag;h=0000000000000000000000000000000000000000"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'.git blob (non-existent)' \
 	'gitweb_run "p=.git;a=blob;f=non-existent"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'.git blob_plain (non-existent)' \
 	'gitweb_run "p=.git;a=blob_plain;f=non-existent"'
-test_debug 'cat gitweb.log'
 
 
 # ----------------------------------------------------------------------
@@ -226,7 +136,6 @@
 test_expect_success \
 	'commitdiff(0): root' \
 	'gitweb_run "p=.git;a=commitdiff"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'commitdiff(0): file added' \
@@ -234,21 +143,18 @@
 	 git add new_file &&
 	 git commit -a -m "File added." &&
 	 gitweb_run "p=.git;a=commitdiff"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'commitdiff(0): mode change' \
 	'test_chmod +x new_file &&
 	 git commit -a -m "Mode changed." &&
 	 gitweb_run "p=.git;a=commitdiff"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'commitdiff(0): file renamed' \
 	'git mv new_file renamed_file &&
 	 git commit -a -m "File renamed." &&
 	 gitweb_run "p=.git;a=commitdiff"'
-test_debug 'cat gitweb.log'
 
 test_expect_success SYMLINKS \
 	'commitdiff(0): file to symlink' \
@@ -256,7 +162,6 @@
 	 ln -s file renamed_file &&
 	 git commit -a -m "File to symlink." &&
 	 gitweb_run "p=.git;a=commitdiff"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'commitdiff(0): file deleted' \
@@ -264,7 +169,6 @@
 	 rm -f renamed_file &&
 	 git commit -a -m "File removed." &&
 	 gitweb_run "p=.git;a=commitdiff"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'commitdiff(0): file copied / new file' \
@@ -272,7 +176,6 @@
 	 git add file2 &&
 	 git commit -a -m "File copied." &&
 	 gitweb_run "p=.git;a=commitdiff"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'commitdiff(0): mode change and modified' \
@@ -280,7 +183,6 @@
 	 test_chmod +x file2 &&
 	 git commit -a -m "Mode change and modification." &&
 	 gitweb_run "p=.git;a=commitdiff"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'commitdiff(0): renamed and modified' \
@@ -298,7 +200,6 @@
 	 echo "Propter nomen suum." >> file3 &&
 	 git commit -a -m "File rename and modification." &&
 	 gitweb_run "p=.git;a=commitdiff"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'commitdiff(0): renamed, mode change and modified' \
@@ -307,7 +208,6 @@
 	 test_chmod +x file2 &&
 	 git commit -a -m "File rename, mode change and modification." &&
 	 gitweb_run "p=.git;a=commitdiff"'
-test_debug 'cat gitweb.log'
 
 # ----------------------------------------------------------------------
 # commitdiff testing (taken from t4114-apply-typechange.sh)
@@ -344,42 +244,81 @@
 test_expect_success \
 	'commitdiff(2): file renamed from foo to foo/baz' \
 	'gitweb_run "p=.git;a=commitdiff;hp=initial;h=foo-baz-renamed-from-foo"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'commitdiff(2): file renamed from foo/baz to foo' \
 	'gitweb_run "p=.git;a=commitdiff;hp=foo-baz-renamed-from-foo;h=initial"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'commitdiff(2): directory becomes file' \
 	'gitweb_run "p=.git;a=commitdiff;hp=foo-becomes-a-directory;h=initial"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'commitdiff(2): file becomes directory' \
 	'gitweb_run "p=.git;a=commitdiff;hp=initial;h=foo-becomes-a-directory"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'commitdiff(2): file becomes symlink' \
 	'gitweb_run "p=.git;a=commitdiff;hp=initial;h=foo-symlinked-to-bar"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'commitdiff(2): symlink becomes file' \
 	'gitweb_run "p=.git;a=commitdiff;hp=foo-symlinked-to-bar;h=foo-back-to-file"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'commitdiff(2): symlink becomes directory' \
 	'gitweb_run "p=.git;a=commitdiff;hp=foo-symlinked-to-bar;h=foo-becomes-a-directory"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'commitdiff(2): directory becomes symlink' \
 	'gitweb_run "p=.git;a=commitdiff;hp=foo-becomes-a-directory;h=foo-symlinked-to-bar"'
-test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# commitdiff testing (incomplete lines)
+
+test_expect_success 'setup incomplete lines' '
+	cat >file<<-\EOF &&
+	Dominus regit me,
+	et nihil mihi deerit.
+	In loco pascuae ibi me collocavit,
+	super aquam refectionis educavit me;
+	animam meam convertit,
+	deduxit me super semitas jusitiae,
+	propter nomen suum.
+	CHANGE_ME
+	EOF
+	git commit -a -m "Preparing for incomplete lines" &&
+	echo "incomplete" | tr -d "\\012" >>file &&
+	git commit -a -m "Add incomplete line" &&
+	git tag incomplete_lines_add &&
+	sed -e s/CHANGE_ME/change_me/ <file >file+ &&
+	mv -f file+ file &&
+	git commit -a -m "Incomplete context line" &&
+	git tag incomplete_lines_ctx &&
+	echo "Dominus regit me," >file &&
+	echo "incomplete line" | tr -d "\\012" >>file &&
+	git commit -a -m "Change incomplete line" &&
+	git tag incomplete_lines_chg
+	echo "Dominus regit me," >file &&
+	git commit -a -m "Remove incomplete line" &&
+	git tag incomplete_lines_rem
+'
+
+test_expect_success 'commitdiff(1): addition of incomplete line' '
+	gitweb_run "p=.git;a=commitdiff;h=incomplete_lines_add"
+'
+
+test_expect_success 'commitdiff(1): incomplete line as context line' '
+	gitweb_run "p=.git;a=commitdiff;h=incomplete_lines_ctx"
+'
+
+test_expect_success 'commitdiff(1): change incomplete line' '
+	gitweb_run "p=.git;a=commitdiff;h=incomplete_lines_chg"
+'
+
+test_expect_success 'commitdiff(1): removal of incomplete line' '
+	gitweb_run "p=.git;a=commitdiff;h=incomplete_lines_rem"
+'
 
 # ----------------------------------------------------------------------
 # commit, commitdiff: merge, large
@@ -390,17 +329,16 @@
 	 git add b &&
 	 git commit -a -m "On branch" &&
 	 git checkout master &&
-	 git pull . b'
+	 git pull . b &&
+	 git tag merge_commit'
 
 test_expect_success \
 	'commit(0): merge commit' \
 	'gitweb_run "p=.git;a=commit"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'commitdiff(0): merge commit' \
 	'gitweb_run "p=.git;a=commitdiff"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'Prepare large commit' \
@@ -436,12 +374,33 @@
 test_expect_success \
 	'commit(1): large commit' \
 	'gitweb_run "p=.git;a=commit;h=b"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'commitdiff(1): large commit' \
 	'gitweb_run "p=.git;a=commitdiff;h=b"'
-test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# side-by-side diff
+
+test_expect_success 'side-by-side: addition of incomplete line' '
+	gitweb_run "p=.git;a=commitdiff;h=incomplete_lines_add;ds=sidebyside"
+'
+
+test_expect_success 'side-by-side: incomplete line as context line' '
+	gitweb_run "p=.git;a=commitdiff;h=incomplete_lines_ctx;ds=sidebyside"
+'
+
+test_expect_success 'side-by-side: changed incomplete line' '
+	gitweb_run "p=.git;a=commitdiff;h=incomplete_lines_chg;ds=sidebyside"
+'
+
+test_expect_success 'side-by-side: removal of incomplete line' '
+	gitweb_run "p=.git;a=commitdiff;h=incomplete_lines_rem;ds=sidebyside"
+'
+
+test_expect_success 'side-by-side: merge commit' '
+	gitweb_run "p=.git;a=commitdiff;h=merge_commit;ds=sidebyside"
+'
 
 # ----------------------------------------------------------------------
 # tags testing
@@ -459,17 +418,14 @@
 	 git tag lightweight/tag-tree HEAD^{tree} &&
 	 git tag lightweight/tag-blob HEAD:file &&
 	 gitweb_run "p=.git;a=tags"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'tag: Tag to commit object' \
 	'gitweb_run "p=.git;a=tag;h=tag-commit"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'tag: on lightweight tag (invalid)' \
 	'gitweb_run "p=.git;a=tag;h=lightweight/tag-commit"'
-test_debug 'cat gitweb.log'
 
 # ----------------------------------------------------------------------
 # logs
@@ -477,22 +433,18 @@
 test_expect_success \
 	'logs: log (implicit HEAD)' \
 	'gitweb_run "p=.git;a=log"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'logs: shortlog (implicit HEAD)' \
 	'gitweb_run "p=.git;a=shortlog"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'logs: history (implicit HEAD, file)' \
 	'gitweb_run "p=.git;a=history;f=file"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'logs: history (implicit HEAD, non-existent file)' \
 	'gitweb_run "p=.git;a=history;f=non-existent"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'logs: history (implicit HEAD, deleted file)' \
@@ -503,55 +455,53 @@
 	 git rm deleted_file &&
 	 git commit -m "Delete file" &&
 	 gitweb_run "p=.git;a=history;f=deleted_file"'
-test_debug 'cat gitweb.log'
 
 # ----------------------------------------------------------------------
 # path_info links
 test_expect_success \
 	'path_info: project' \
 	'gitweb_run "" "/.git"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'path_info: project/branch' \
 	'gitweb_run "" "/.git/b"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'path_info: project/branch:file' \
 	'gitweb_run "" "/.git/master:file"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'path_info: project/branch:dir/' \
 	'gitweb_run "" "/.git/master:foo/"'
-test_debug 'cat gitweb.log'
+
+test_expect_success \
+	'path_info: project/branch (non-existent)' \
+	'gitweb_run "" "/.git/non-existent"'
+
+test_expect_success \
+	'path_info: project/branch:filename (non-existent branch)' \
+	'gitweb_run "" "/.git/non-existent:non-existent"'
 
 test_expect_success \
 	'path_info: project/branch:file (non-existent)' \
 	'gitweb_run "" "/.git/master:non-existent"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'path_info: project/branch:dir/ (non-existent)' \
 	'gitweb_run "" "/.git/master:non-existent/"'
-test_debug 'cat gitweb.log'
 
 
 test_expect_success \
 	'path_info: project/branch:/file' \
 	'gitweb_run "" "/.git/master:/file"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'path_info: project/:/file (implicit HEAD)' \
 	'gitweb_run "" "/.git/:/file"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'path_info: project/:/ (implicit HEAD, top tree)' \
 	'gitweb_run "" "/.git/:/"'
-test_debug 'cat gitweb.log'
 
 
 # ----------------------------------------------------------------------
@@ -560,17 +510,14 @@
 test_expect_success \
 	'feeds: OPML' \
 	'gitweb_run "a=opml"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'feed: RSS' \
 	'gitweb_run "p=.git;a=rss"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'feed: Atom' \
 	'gitweb_run "p=.git;a=atom"'
-test_debug 'cat gitweb.log'
 
 # ----------------------------------------------------------------------
 # encoding/decoding
@@ -578,27 +525,28 @@
 test_expect_success \
 	'encode(commit): utf8' \
 	'. "$TEST_DIRECTORY"/t3901-utf8.txt &&
+	 test_when_finished "GIT_AUTHOR_NAME=\"A U Thor\"" &&
+	 test_when_finished "GIT_COMMITTER_NAME=\"C O Mitter\"" &&
 	 echo "UTF-8" >> file &&
 	 git add file &&
 	 git commit -F "$TEST_DIRECTORY"/t3900/1-UTF-8.txt &&
 	 gitweb_run "p=.git;a=commit"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'encode(commit): iso-8859-1' \
 	'. "$TEST_DIRECTORY"/t3901-8859-1.txt &&
+	 test_when_finished "GIT_AUTHOR_NAME=\"A U Thor\"" &&
+	 test_when_finished "GIT_COMMITTER_NAME=\"C O Mitter\"" &&
 	 echo "ISO-8859-1" >> file &&
 	 git add file &&
 	 git config i18n.commitencoding ISO-8859-1 &&
+	 test_when_finished "git config --unset i18n.commitencoding" &&
 	 git commit -F "$TEST_DIRECTORY"/t3900/ISO8859-1.txt &&
-	 git config --unset i18n.commitencoding &&
 	 gitweb_run "p=.git;a=commit"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'encode(log): utf-8 and iso-8859-1' \
 	'gitweb_run "p=.git;a=log"'
-test_debug 'cat gitweb.log'
 
 # ----------------------------------------------------------------------
 # extra options
@@ -606,27 +554,22 @@
 test_expect_success \
 	'opt: log --no-merges' \
 	'gitweb_run "p=.git;a=log;opt=--no-merges"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'opt: atom --no-merges' \
 	'gitweb_run "p=.git;a=log;opt=--no-merges"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'opt: "file" history --no-merges' \
 	'gitweb_run "p=.git;a=history;f=file;opt=--no-merges"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'opt: log --no-such-option (invalid option)' \
 	'gitweb_run "p=.git;a=log;opt=--no-such-option"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'opt: tree --no-merges (invalid option for action)' \
 	'gitweb_run "p=.git;a=tree;opt=--no-merges"'
-test_debug 'cat gitweb.log'
 
 # ----------------------------------------------------------------------
 # testing config_to_multi / cloneurl
@@ -634,14 +577,12 @@
 test_expect_success \
        'URL: no project URLs, no base URL' \
        'gitweb_run "p=.git;a=summary"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'URL: project URLs via gitweb.url' \
        'git config --add gitweb.url git://example.com/git/trash.git &&
         git config --add gitweb.url http://example.com/git/trash.git &&
         gitweb_run "p=.git;a=summary"'
-test_debug 'cat gitweb.log'
 
 cat >.git/cloneurl <<\EOF
 git://example.com/git/trash.git
@@ -651,22 +592,27 @@
 test_expect_success \
        'URL: project URLs via cloneurl file' \
        'gitweb_run "p=.git;a=summary"'
-test_debug 'cat gitweb.log'
 
 # ----------------------------------------------------------------------
 # gitweb config and repo config
 
-cat >>gitweb_config.perl <<EOF
+cat >>gitweb_config.perl <<\EOF
 
-\$feature{'blame'}{'override'} = 1;
-\$feature{'snapshot'}{'override'} = 1;
-\$feature{'avatar'}{'override'} = 1;
+# turn on override for each overridable feature
+foreach my $key (keys %feature) {
+	if ($feature{$key}{'sub'}) {
+		$feature{$key}{'override'} = 1;
+	}
+}
 EOF
 
 test_expect_success \
+	'config override: projects list (implicit)' \
+	'gitweb_run'
+
+test_expect_success \
 	'config override: tree view, features not overridden in repo config' \
 	'gitweb_run "p=.git;a=tree"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'config override: tree view, features disabled in repo config' \
@@ -674,14 +620,12 @@
 	 git config gitweb.snapshot none &&
 	 git config gitweb.avatar gravatar &&
 	 gitweb_run "p=.git;a=tree"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'config override: tree view, features enabled in repo config (1)' \
 	'git config gitweb.blame yes &&
 	 git config gitweb.snapshot "zip,tgz, tbz2" &&
 	 gitweb_run "p=.git;a=tree"'
-test_debug 'cat gitweb.log'
 
 cat >.git/config <<\EOF
 # testing noval and alternate separator
@@ -692,7 +636,45 @@
 test_expect_success \
 	'config override: tree view, features enabled in repo config (2)' \
 	'gitweb_run "p=.git;a=tree"'
-test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# searching
+
+cat >>gitweb_config.perl <<\EOF
+
+# enable search
+$feature{'search'}{'default'} = [1];
+$feature{'grep'}{'default'} = [1];
+$feature{'pickaxe'}{'default'} = [1];
+EOF
+
+test_expect_success \
+	'search: preparation' \
+	'echo "1st MATCH" >>file &&
+	 echo "2nd MATCH" >>file &&
+	 echo "MATCH" >>bar &&
+	 git add file bar &&
+	 git commit -m "Added MATCH word"'
+
+test_expect_success \
+	'search: commit author' \
+	'gitweb_run "p=.git;a=search;h=HEAD;st=author;s=A+U+Thor"'
+
+test_expect_success \
+	'search: commit message' \
+	'gitweb_run "p=.git;a=search;h=HEAD;st=commitr;s=MATCH"'
+
+test_expect_success \
+	'search: grep' \
+	'gitweb_run "p=.git;a=search;h=HEAD;st=grep;s=MATCH"'
+
+test_expect_success \
+	'search: pickaxe' \
+	'gitweb_run "p=.git;a=search;h=HEAD;st=pickaxe;s=MATCH"'
+
+test_expect_success \
+	'search: projects' \
+	'gitweb_run "a=project_list;s=.git"'
 
 # ----------------------------------------------------------------------
 # non-ASCII in README.html
@@ -702,6 +684,107 @@
 	'echo "<b>UTF-8 example:</b><br />" > .git/README.html &&
 	 cat "$TEST_DIRECTORY"/t3900/1-UTF-8.txt >> .git/README.html &&
 	 gitweb_run "p=.git;a=summary"'
-test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# syntax highlighting
+
+
+highlight --version >/dev/null 2>&1
+if [ $? -eq 127 ]; then
+	say "Skipping syntax highlighting test, because 'highlight' was not found"
+else
+	test_set_prereq HIGHLIGHT
+	cat >>gitweb_config.perl <<-\EOF
+	our $highlight_bin = "highlight";
+	$feature{'highlight'}{'override'} = 1;
+	EOF
+fi
+
+test_expect_success HIGHLIGHT \
+	'syntax highlighting (no highlight, unknown syntax)' \
+	'git config gitweb.highlight yes &&
+	 gitweb_run "p=.git;a=blob;f=file"'
+
+test_expect_success HIGHLIGHT \
+	'syntax highlighting (highlighted, shell script)' \
+	'git config gitweb.highlight yes &&
+	 echo "#!/usr/bin/sh" > test.sh &&
+	 git add test.sh &&
+	 git commit -m "Add test.sh" &&
+	 gitweb_run "p=.git;a=blob;f=test.sh"'
+
+# ----------------------------------------------------------------------
+# forks of projects
+
+cat >>gitweb_config.perl <<\EOF &&
+$feature{'forks'}{'default'} = [1];
+EOF
+
+test_expect_success \
+	'forks: prepare' \
+	'git init --bare foo.git &&
+	 git --git-dir=foo.git --work-tree=. add file &&
+	 git --git-dir=foo.git --work-tree=. commit -m "Initial commit" &&
+	 echo "foo" > foo.git/description &&
+	 mkdir -p foo &&
+	 (cd foo &&
+	  git clone --shared --bare ../foo.git foo-forked.git &&
+	  echo "fork of foo" > foo-forked.git/description)'
+
+test_expect_success \
+	'forks: projects list' \
+	'gitweb_run'
+
+test_expect_success \
+	'forks: forks action' \
+	'gitweb_run "p=foo.git;a=forks"'
+
+# ----------------------------------------------------------------------
+# content tags (tag cloud)
+
+cat >>gitweb_config.perl <<-\EOF &&
+# we don't test _setting_ content tags, so any true value is good
+$feature{'ctags'}{'default'} = ['ctags_script.cgi'];
+EOF
+
+test_expect_success \
+	'ctags: tag cloud in projects list' \
+	'mkdir .git/ctags &&
+	 echo "2" > .git/ctags/foo &&
+	 echo "1" > .git/ctags/bar &&
+	gitweb_run'
+
+test_expect_success \
+	'ctags: search projects by existing tag' \
+	'gitweb_run "by_tag=foo"'
+
+test_expect_success \
+	'ctags: search projects by non existent tag' \
+	'gitweb_run "by_tag=non-existent"'
+
+test_expect_success \
+	'ctags: malformed tag weights' \
+	'mkdir -p .git/ctags &&
+	 echo "not-a-number" > .git/ctags/nan &&
+	 echo "not-a-number-2" > .git/ctags/nan2 &&
+	 echo "0.1" >.git/ctags/floating-point &&
+	 gitweb_run'
+
+# ----------------------------------------------------------------------
+# categories
+
+test_expect_success \
+	'categories: projects list, only default category' \
+	'echo "\$projects_list_group_categories = 1;" >>gitweb_config.perl &&
+	 gitweb_run'
+
+# ----------------------------------------------------------------------
+# unborn branches
+
+test_expect_success \
+	'unborn HEAD: "summary" page (with "heads" subview)' \
+	'git checkout orphan_branch || git checkout --orphan orphan_branch &&
+	 test_when_finished "git checkout master" &&
+	 gitweb_run "p=.git;a=summary"'
 
 test_done
diff --git a/t/t9501-gitweb-standalone-http-status.sh b/t/t9501-gitweb-standalone-http-status.sh
new file mode 100755
index 0000000..ef86948
--- /dev/null
+++ b/t/t9501-gitweb-standalone-http-status.sh
@@ -0,0 +1,212 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Mark Rada
+#
+
+test_description='gitweb as standalone script (http status tests).
+
+This test runs gitweb (git web interface) as a CGI script from the
+commandline, and checks that it returns the expected HTTP status
+code and message.'
+
+
+. ./gitweb-lib.sh
+
+#
+# Gitweb only provides the functionality tested by the 'modification times'
+# tests if it can access a date parser from one of these modules:
+#
+perl -MHTTP::Date -e 0 >/dev/null 2>&1 && test_set_prereq DATE_PARSER
+perl -MTime::ParseDate -e 0 >/dev/null 2>&1 && test_set_prereq DATE_PARSER
+
+# ----------------------------------------------------------------------
+# snapshot settings
+
+test_expect_success 'setup' "
+	test_commit 'SnapshotTests' 'i can has snapshot'
+"
+
+
+cat >>gitweb_config.perl <<\EOF
+$feature{'snapshot'}{'override'} = 0;
+EOF
+
+test_expect_success \
+    'snapshots: tgz only default format enabled' \
+    'gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tgz" &&
+    grep "Status: 200 OK" gitweb.output &&
+    gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tbz2" &&
+    grep "403 - Unsupported snapshot format" gitweb.output &&
+    gitweb_run "p=.git;a=snapshot;h=HEAD;sf=txz" &&
+    grep "403 - Snapshot format not allowed" gitweb.output &&
+    gitweb_run "p=.git;a=snapshot;h=HEAD;sf=zip" &&
+    grep "403 - Unsupported snapshot format" gitweb.output'
+
+
+cat >>gitweb_config.perl <<\EOF
+$feature{'snapshot'}{'default'} = ['tgz','tbz2','txz','zip'];
+EOF
+
+test_expect_success \
+    'snapshots: all enabled in default, use default disabled value' \
+    'gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tgz" &&
+    grep "Status: 200 OK" gitweb.output &&
+    gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tbz2" &&
+    grep "Status: 200 OK" gitweb.output &&
+    gitweb_run "p=.git;a=snapshot;h=HEAD;sf=txz" &&
+    grep "403 - Snapshot format not allowed" gitweb.output &&
+    gitweb_run "p=.git;a=snapshot;h=HEAD;sf=zip" &&
+    grep "Status: 200 OK" gitweb.output'
+
+
+cat >>gitweb_config.perl <<\EOF
+$known_snapshot_formats{'zip'}{'disabled'} = 1;
+EOF
+
+test_expect_success \
+    'snapshots: zip explicitly disabled' \
+    'gitweb_run "p=.git;a=snapshot;h=HEAD;sf=zip" &&
+    grep "403 - Snapshot format not allowed" gitweb.output'
+test_debug 'cat gitweb.output'
+
+
+cat >>gitweb_config.perl <<\EOF
+$known_snapshot_formats{'tgz'}{'disabled'} = 0;
+EOF
+
+test_expect_success \
+    'snapshots: tgz explicitly enabled' \
+    'gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tgz" &&
+    grep "Status: 200 OK" gitweb.output'
+test_debug 'cat gitweb.headers'
+
+
+# ----------------------------------------------------------------------
+# snapshot hash ids
+
+test_expect_success 'snapshots: good tree-ish id' '
+	gitweb_run "p=.git;a=snapshot;h=master;sf=tgz" &&
+	grep "Status: 200 OK" gitweb.output
+'
+test_debug 'cat gitweb.headers'
+
+test_expect_success 'snapshots: bad tree-ish id' '
+	gitweb_run "p=.git;a=snapshot;h=frizzumFrazzum;sf=tgz" &&
+	grep "404 - Object does not exist" gitweb.output
+'
+test_debug 'cat gitweb.output'
+
+test_expect_success 'snapshots: bad tree-ish id (tagged object)' '
+	echo object > tag-object &&
+	git add tag-object &&
+	test_tick && git commit -m "Object to be tagged" &&
+	git tag tagged-object `git hash-object tag-object` &&
+	gitweb_run "p=.git;a=snapshot;h=tagged-object;sf=tgz" &&
+	grep "400 - Object is not a tree-ish" gitweb.output
+'
+test_debug 'cat gitweb.output'
+
+test_expect_success 'snapshots: good object id' '
+	ID=`git rev-parse --verify HEAD` &&
+	gitweb_run "p=.git;a=snapshot;h=$ID;sf=tgz" &&
+	grep "Status: 200 OK" gitweb.output
+'
+test_debug 'cat gitweb.headers'
+
+test_expect_success 'snapshots: bad object id' '
+	gitweb_run "p=.git;a=snapshot;h=abcdef01234;sf=tgz" &&
+	grep "404 - Object does not exist" gitweb.output
+'
+test_debug 'cat gitweb.output'
+
+# ----------------------------------------------------------------------
+# modification times (Last-Modified and If-Modified-Since)
+
+test_expect_success DATE_PARSER 'modification: feed last-modified' '
+	gitweb_run "p=.git;a=atom;h=master" &&
+	grep "Status: 200 OK" gitweb.headers &&
+	grep "Last-modified: Thu, 7 Apr 2005 22:14:13 +0000" gitweb.headers
+'
+test_debug 'cat gitweb.headers'
+
+test_expect_success DATE_PARSER 'modification: feed if-modified-since (modified)' '
+	export HTTP_IF_MODIFIED_SINCE="Wed, 6 Apr 2005 22:14:13 +0000" &&
+	test_when_finished "unset HTTP_IF_MODIFIED_SINCE" &&
+	gitweb_run "p=.git;a=atom;h=master" &&
+	grep "Status: 200 OK" gitweb.headers
+'
+test_debug 'cat gitweb.headers'
+
+test_expect_success DATE_PARSER 'modification: feed if-modified-since (unmodified)' '
+	export HTTP_IF_MODIFIED_SINCE="Thu, 7 Apr 2005 22:14:13 +0000" &&
+	test_when_finished "unset HTTP_IF_MODIFIED_SINCE" &&
+	gitweb_run "p=.git;a=atom;h=master" &&
+	grep "Status: 304 Not Modified" gitweb.headers
+'
+test_debug 'cat gitweb.headers'
+
+test_expect_success DATE_PARSER 'modification: snapshot last-modified' '
+	gitweb_run "p=.git;a=snapshot;h=master;sf=tgz" &&
+	grep "Status: 200 OK" gitweb.headers &&
+	grep "Last-modified: Thu, 7 Apr 2005 22:14:13 +0000" gitweb.headers
+'
+test_debug 'cat gitweb.headers'
+
+test_expect_success DATE_PARSER 'modification: snapshot if-modified-since (modified)' '
+	export HTTP_IF_MODIFIED_SINCE="Wed, 6 Apr 2005 22:14:13 +0000" &&
+	test_when_finished "unset HTTP_IF_MODIFIED_SINCE" &&
+	gitweb_run "p=.git;a=snapshot;h=master;sf=tgz" &&
+	grep "Status: 200 OK" gitweb.headers
+'
+test_debug 'cat gitweb.headers'
+
+test_expect_success DATE_PARSER 'modification: snapshot if-modified-since (unmodified)' '
+	export HTTP_IF_MODIFIED_SINCE="Thu, 7 Apr 2005 22:14:13 +0000" &&
+	test_when_finished "unset HTTP_IF_MODIFIED_SINCE" &&
+	gitweb_run "p=.git;a=snapshot;h=master;sf=tgz" &&
+	grep "Status: 304 Not Modified" gitweb.headers
+'
+test_debug 'cat gitweb.headers'
+
+test_expect_success DATE_PARSER 'modification: tree snapshot' '
+	ID=`git rev-parse --verify HEAD^{tree}` &&
+	export HTTP_IF_MODIFIED_SINCE="Wed, 6 Apr 2005 22:14:13 +0000" &&
+	test_when_finished "unset HTTP_IF_MODIFIED_SINCE" &&
+	gitweb_run "p=.git;a=snapshot;h=$ID;sf=tgz" &&
+	grep "Status: 200 OK" gitweb.headers &&
+	! grep -i "last-modified" gitweb.headers
+'
+test_debug 'cat gitweb.headers'
+
+# ----------------------------------------------------------------------
+# load checking
+
+# always hit the load limit
+cat >>gitweb_config.perl <<\EOF
+our $maxload = -1;
+EOF
+
+test_expect_success 'load checking: load too high (default action)' '
+	gitweb_run "p=.git" &&
+	grep "Status: 503 Service Unavailable" gitweb.headers &&
+	grep "503 - The load average on the server is too high" gitweb.body
+'
+test_debug 'cat gitweb.headers'
+
+# turn off load checking
+cat >>gitweb_config.perl <<\EOF
+our $maxload = undef;
+EOF
+
+
+# ----------------------------------------------------------------------
+# invalid arguments
+
+test_expect_success 'invalid arguments: invalid regexp (in project search)' '
+	gitweb_run "a=project_list;s=*\.git;sr=1" &&
+	grep "Status: 400" gitweb.headers &&
+	grep "400 - Invalid.*regexp" gitweb.body
+'
+test_debug 'cat gitweb.headers'
+
+test_done
diff --git a/t/t9502-gitweb-standalone-parse-output.sh b/t/t9502-gitweb-standalone-parse-output.sh
new file mode 100755
index 0000000..731e64c
--- /dev/null
+++ b/t/t9502-gitweb-standalone-parse-output.sh
@@ -0,0 +1,189 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Mark Rada
+#
+
+test_description='gitweb as standalone script (parsing script output).
+
+This test runs gitweb (git web interface) as a CGI script from the
+commandline, and checks that it produces the correct output, either
+in the HTTP header or the actual script output.'
+
+
+. ./gitweb-lib.sh
+
+# ----------------------------------------------------------------------
+# snapshot file name and prefix
+
+cat >>gitweb_config.perl <<\EOF
+
+$known_snapshot_formats{'tar'} = {
+	'display' => 'tar',
+	'type' => 'application/x-tar',
+	'suffix' => '.tar',
+	'format' => 'tar',
+};
+
+$feature{'snapshot'}{'default'} = ['tar'];
+EOF
+
+# Call check_snapshot with the arguments "<basename> [<prefix>]"
+#
+# This will check that gitweb HTTP header contains proposed filename
+# as <basename> with '.tar' suffix added, and that generated tarfile
+# (gitweb message body) has <prefix> as prefix for al files in tarfile
+#
+# <prefix> default to <basename>
+check_snapshot () {
+	basename=$1
+	prefix=${2:-"$1"}
+	echo "basename=$basename"
+	grep "filename=.*$basename.tar" gitweb.headers >/dev/null 2>&1 &&
+	"$TAR" tf gitweb.body >file_list &&
+	! grep -v "^$prefix/" file_list
+}
+
+test_expect_success setup '
+	test_commit first foo &&
+	git branch xx/test &&
+	FULL_ID=$(git rev-parse --verify HEAD) &&
+	SHORT_ID=$(git rev-parse --verify --short=7 HEAD)
+'
+test_debug '
+	echo "FULL_ID  = $FULL_ID"
+	echo "SHORT_ID = $SHORT_ID"
+'
+
+test_expect_success 'snapshot: full sha1' '
+	gitweb_run "p=.git;a=snapshot;h=$FULL_ID;sf=tar" &&
+	check_snapshot ".git-$SHORT_ID"
+'
+test_debug 'cat gitweb.headers && cat file_list'
+
+test_expect_success 'snapshot: shortened sha1' '
+	gitweb_run "p=.git;a=snapshot;h=$SHORT_ID;sf=tar" &&
+	check_snapshot ".git-$SHORT_ID"
+'
+test_debug 'cat gitweb.headers && cat file_list'
+
+test_expect_success 'snapshot: almost full sha1' '
+	ID=$(git rev-parse --short=30 HEAD) &&
+	gitweb_run "p=.git;a=snapshot;h=$ID;sf=tar" &&
+	check_snapshot ".git-$SHORT_ID"
+'
+test_debug 'cat gitweb.headers && cat file_list'
+
+test_expect_success 'snapshot: HEAD' '
+	gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tar" &&
+	check_snapshot ".git-HEAD-$SHORT_ID"
+'
+test_debug 'cat gitweb.headers && cat file_list'
+
+test_expect_success 'snapshot: short branch name (master)' '
+	gitweb_run "p=.git;a=snapshot;h=master;sf=tar" &&
+	ID=$(git rev-parse --verify --short=7 master) &&
+	check_snapshot ".git-master-$ID"
+'
+test_debug 'cat gitweb.headers && cat file_list'
+
+test_expect_success 'snapshot: short tag name (first)' '
+	gitweb_run "p=.git;a=snapshot;h=first;sf=tar" &&
+	ID=$(git rev-parse --verify --short=7 first) &&
+	check_snapshot ".git-first-$ID"
+'
+test_debug 'cat gitweb.headers && cat file_list'
+
+test_expect_success 'snapshot: full branch name (refs/heads/master)' '
+	gitweb_run "p=.git;a=snapshot;h=refs/heads/master;sf=tar" &&
+	ID=$(git rev-parse --verify --short=7 master) &&
+	check_snapshot ".git-master-$ID"
+'
+test_debug 'cat gitweb.headers && cat file_list'
+
+test_expect_success 'snapshot: full tag name (refs/tags/first)' '
+	gitweb_run "p=.git;a=snapshot;h=refs/tags/first;sf=tar" &&
+	check_snapshot ".git-first"
+'
+test_debug 'cat gitweb.headers && cat file_list'
+
+test_expect_success 'snapshot: hierarchical branch name (xx/test)' '
+	gitweb_run "p=.git;a=snapshot;h=xx/test;sf=tar" &&
+	! grep "filename=.*/" gitweb.headers
+'
+test_debug 'cat gitweb.headers'
+
+# ----------------------------------------------------------------------
+# forks of projects
+
+test_expect_success 'forks: setup' '
+	git init --bare foo.git &&
+	echo file > file &&
+	git --git-dir=foo.git --work-tree=. add file &&
+	git --git-dir=foo.git --work-tree=. commit -m "Initial commit" &&
+	echo "foo" > foo.git/description &&
+	git clone --bare foo.git foo.bar.git &&
+	echo "foo.bar" > foo.bar.git/description &&
+	git clone --bare foo.git foo_baz.git &&
+	echo "foo_baz" > foo_baz.git/description &&
+	rm -fr   foo &&
+	mkdir -p foo &&
+	(
+		cd foo &&
+		git clone --shared --bare ../foo.git foo-forked.git &&
+		echo "fork of foo" > foo-forked.git/description
+	)
+'
+
+test_expect_success 'forks: not skipped unless "forks" feature enabled' '
+	gitweb_run "a=project_list" &&
+	grep -q ">\\.git<"               gitweb.body &&
+	grep -q ">foo\\.git<"            gitweb.body &&
+	grep -q ">foo_baz\\.git<"        gitweb.body &&
+	grep -q ">foo\\.bar\\.git<"      gitweb.body &&
+	grep -q ">foo_baz\\.git<"        gitweb.body &&
+	grep -q ">foo/foo-forked\\.git<" gitweb.body &&
+	grep -q ">fork of .*<"           gitweb.body
+'
+
+cat >>gitweb_config.perl <<\EOF &&
+$feature{'forks'}{'default'} = [1];
+EOF
+
+test_expect_success 'forks: forks skipped if "forks" feature enabled' '
+	gitweb_run "a=project_list" &&
+	grep -q ">\\.git<"               gitweb.body &&
+	grep -q ">foo\\.git<"            gitweb.body &&
+	grep -q ">foo_baz\\.git<"        gitweb.body &&
+	grep -q ">foo\\.bar\\.git<"      gitweb.body &&
+	grep -q ">foo_baz\\.git<"        gitweb.body &&
+	grep -v ">foo/foo-forked\\.git<" gitweb.body &&
+	grep -v ">fork of .*<"           gitweb.body
+'
+
+test_expect_success 'forks: "forks" action for forked repository' '
+	gitweb_run "p=foo.git;a=forks" &&
+	grep -q ">foo/foo-forked\\.git<" gitweb.body &&
+	grep -q ">fork of foo<"          gitweb.body
+'
+
+test_expect_success 'forks: can access forked repository' '
+	gitweb_run "p=foo/foo-forked.git;a=summary" &&
+	grep -q "200 OK"        gitweb.headers &&
+	grep -q ">fork of foo<" gitweb.body
+'
+
+test_expect_success 'forks: project_index lists all projects (incl. forks)' '
+	cat >expected <<-\EOF
+	.git
+	foo.bar.git
+	foo.git
+	foo/foo-forked.git
+	foo_baz.git
+	EOF
+	gitweb_run "a=project_index" &&
+	sed -e "s/ .*//" <gitweb.body | sort >actual &&
+	test_cmp expected actual
+'
+
+
+test_done
diff --git a/t/t9600-cvsimport.sh b/t/t9600-cvsimport.sh
index 363345f..4c384ff 100755
--- a/t/t9600-cvsimport.sh
+++ b/t/t9600-cvsimport.sh
@@ -3,21 +3,18 @@
 test_description='git cvsimport basic tests'
 . ./lib-cvs.sh
 
-if ! test_have_prereq PERL; then
-	say 'skipping git cvsimport tests, perl not available'
-	test_done
-fi
+test_expect_success PERL 'setup cvsroot environment' '
+	CVSROOT=$(pwd)/cvsroot &&
+	export CVSROOT
+'
 
-CVSROOT=$(pwd)/cvsroot
-export CVSROOT
+test_expect_success PERL 'setup cvsroot' '$CVS init'
 
-test_expect_success 'setup cvsroot' '$CVS init'
-
-test_expect_success 'setup a cvs module' '
+test_expect_success PERL 'setup a cvs module' '
 
 	mkdir "$CVSROOT/module" &&
 	$CVS co -d module-cvs module &&
-	cd module-cvs &&
+	(cd module-cvs &&
 	cat <<EOF >o_fortuna &&
 O Fortuna
 velut luna
@@ -41,22 +38,28 @@
 
 These public domain lyrics make an excellent sample text.
 EOF
-	$CVS commit -F message &&
-	cd ..
+	$CVS commit -F message
+	)
 '
 
-test_expect_success 'import a trivial module' '
+test_expect_success PERL 'import a trivial module' '
 
-	git cvsimport -a -z 0 -C module-git module &&
+	git cvsimport -a -R -z 0 -C module-git module &&
 	test_cmp module-cvs/o_fortuna module-git/o_fortuna
 
 '
 
-test_expect_success 'pack refs' 'cd module-git && git gc && cd ..'
+test_expect_success PERL 'pack refs' '(cd module-git && git gc)'
 
-test_expect_success 'update cvs module' '
+test_expect_success PERL 'initial import has correct .git/cvs-revisions' '
 
-	cd module-cvs &&
+	(cd module-git &&
+	 git log --format="o_fortuna 1.1 %H" -1) > expected &&
+	test_cmp expected module-git/.git/cvs-revisions
+'
+
+test_expect_success PERL 'update cvs module' '
+	(cd module-cvs &&
 	cat <<EOF >o_fortuna &&
 O Fortune,
 like the moon
@@ -79,53 +82,78 @@
 
 My Latin is terrible.
 EOF
-	$CVS commit -F message &&
-	cd ..
+	$CVS commit -F message
+	)
 '
 
-test_expect_success 'update git module' '
+test_expect_success PERL 'update git module' '
 
-	cd module-git &&
+	(cd module-git &&
+	git config cvsimport.trackRevisions true &&
 	git cvsimport -a -z 0 module &&
-	git merge origin &&
-	cd .. &&
+	git merge origin
+	) &&
 	test_cmp module-cvs/o_fortuna module-git/o_fortuna
 
 '
 
-test_expect_success 'update cvs module' '
+test_expect_success PERL 'update has correct .git/cvs-revisions' '
 
-	cd module-cvs &&
+	(cd module-git &&
+	 git log --format="o_fortuna 1.1 %H" -1 HEAD^ &&
+	 git log --format="o_fortuna 1.2 %H" -1 HEAD) > expected &&
+	test_cmp expected module-git/.git/cvs-revisions
+'
+
+test_expect_success PERL 'update cvs module' '
+
+	(cd module-cvs &&
 		echo 1 >tick &&
 		$CVS add tick &&
 		$CVS commit -m 1
-	cd ..
-
+	)
 '
 
-test_expect_success 'cvsimport.module config works' '
+test_expect_success PERL 'cvsimport.module config works' '
 
-	cd module-git &&
+	(cd module-git &&
 		git config cvsimport.module module &&
+		git config cvsimport.trackRevisions true &&
 		git cvsimport -a -z0 &&
-		git merge origin &&
-	cd .. &&
+		git merge origin
+	) &&
 	test_cmp module-cvs/tick module-git/tick
 
 '
 
-test_expect_success 'import from a CVS working tree' '
+test_expect_success PERL 'second update has correct .git/cvs-revisions' '
+
+	(cd module-git &&
+	 git log --format="o_fortuna 1.1 %H" -1 HEAD^^ &&
+	 git log --format="o_fortuna 1.2 %H" -1 HEAD^
+	 git log --format="tick 1.1 %H" -1 HEAD) > expected &&
+	test_cmp expected module-git/.git/cvs-revisions
+'
+
+test_expect_success PERL 'import from a CVS working tree' '
 
 	$CVS co -d import-from-wt module &&
-	cd import-from-wt &&
+	(cd import-from-wt &&
+		git config cvsimport.trackRevisions false &&
 		git cvsimport -a -z0 &&
 		echo 1 >expect &&
 		git log -1 --pretty=format:%s%n >actual &&
-		test_cmp actual expect &&
-	cd ..
+		test_cmp actual expect
+	)
 
 '
 
-test_expect_success 'test entire HEAD' 'test_cmp_branch_tree master'
+test_expect_success PERL 'no .git/cvs-revisions created by default' '
+
+	! test -e import-from-wt/.git/cvs-revisions
+
+'
+
+test_expect_success PERL 'test entire HEAD' 'test_cmp_branch_tree master'
 
 test_done
diff --git a/t/t9601-cvsimport-vendor-branch.sh b/t/t9601-cvsimport-vendor-branch.sh
index 3afaf56..827d39f 100755
--- a/t/t9601-cvsimport-vendor-branch.sh
+++ b/t/t9601-cvsimport-vendor-branch.sh
@@ -34,50 +34,49 @@
 test_description='git cvsimport handling of vendor branches'
 . ./lib-cvs.sh
 
-CVSROOT="$TEST_DIRECTORY"/t9601/cvsroot
-export CVSROOT
+setup_cvs_test_repository t9601
 
-test_expect_success 'import a module with a vendor branch' '
+test_expect_success PERL 'import a module with a vendor branch' '
 
 	git cvsimport -C module-git module
 
 '
 
-test_expect_success 'check HEAD out of cvs repository' 'test_cvs_co master'
+test_expect_success PERL 'check HEAD out of cvs repository' 'test_cvs_co master'
 
-test_expect_success 'check master out of git repository' 'test_git_co master'
+test_expect_success PERL 'check master out of git repository' 'test_git_co master'
 
-test_expect_success 'check a file that was imported once' '
+test_expect_success PERL 'check a file that was imported once' '
 
 	test_cmp_branch_file master imported-once.txt
 
 '
 
-test_expect_failure 'check a file that was imported twice' '
+test_expect_failure PERL 'check a file that was imported twice' '
 
 	test_cmp_branch_file master imported-twice.txt
 
 '
 
-test_expect_success 'check a file that was imported then modified on HEAD' '
+test_expect_success PERL 'check a file that was imported then modified on HEAD' '
 
 	test_cmp_branch_file master imported-modified.txt
 
 '
 
-test_expect_success 'check a file that was imported, modified, then imported again' '
+test_expect_success PERL 'check a file that was imported, modified, then imported again' '
 
 	test_cmp_branch_file master imported-modified-imported.txt
 
 '
 
-test_expect_success 'check a file that was added to HEAD then imported' '
+test_expect_success PERL 'check a file that was added to HEAD then imported' '
 
 	test_cmp_branch_file master added-imported.txt
 
 '
 
-test_expect_success 'a vendor branch whose tag has been removed' '
+test_expect_success PERL 'a vendor branch whose tag has been removed' '
 
 	test_cmp_branch_file master imported-anonymously.txt
 
diff --git a/t/t9602-cvsimport-branches-tags.sh b/t/t9602-cvsimport-branches-tags.sh
index 67878b2..e1db323 100755
--- a/t/t9602-cvsimport-branches-tags.sh
+++ b/t/t9602-cvsimport-branches-tags.sh
@@ -6,70 +6,69 @@
 test_description='git cvsimport handling of branches and tags'
 . ./lib-cvs.sh
 
-CVSROOT="$TEST_DIRECTORY"/t9602/cvsroot
-export CVSROOT
+setup_cvs_test_repository t9602
 
-test_expect_success 'import module' '
+test_expect_success PERL 'import module' '
 
 	git cvsimport -C module-git module
 
 '
 
-test_expect_success 'test branch master' '
+test_expect_success PERL 'test branch master' '
 
 	test_cmp_branch_tree master
 
 '
 
-test_expect_success 'test branch vendorbranch' '
+test_expect_success PERL 'test branch vendorbranch' '
 
 	test_cmp_branch_tree vendorbranch
 
 '
 
-test_expect_failure 'test branch B_FROM_INITIALS' '
+test_expect_failure PERL 'test branch B_FROM_INITIALS' '
 
 	test_cmp_branch_tree B_FROM_INITIALS
 
 '
 
-test_expect_failure 'test branch B_FROM_INITIALS_BUT_ONE' '
+test_expect_failure PERL 'test branch B_FROM_INITIALS_BUT_ONE' '
 
 	test_cmp_branch_tree B_FROM_INITIALS_BUT_ONE
 
 '
 
-test_expect_failure 'test branch B_MIXED' '
+test_expect_failure PERL 'test branch B_MIXED' '
 
 	test_cmp_branch_tree B_MIXED
 
 '
 
-test_expect_success 'test branch B_SPLIT' '
+test_expect_success PERL 'test branch B_SPLIT' '
 
 	test_cmp_branch_tree B_SPLIT
 
 '
 
-test_expect_failure 'test tag vendortag' '
+test_expect_failure PERL 'test tag vendortag' '
 
 	test_cmp_branch_tree vendortag
 
 '
 
-test_expect_success 'test tag T_ALL_INITIAL_FILES' '
+test_expect_success PERL 'test tag T_ALL_INITIAL_FILES' '
 
 	test_cmp_branch_tree T_ALL_INITIAL_FILES
 
 '
 
-test_expect_failure 'test tag T_ALL_INITIAL_FILES_BUT_ONE' '
+test_expect_failure PERL 'test tag T_ALL_INITIAL_FILES_BUT_ONE' '
 
 	test_cmp_branch_tree T_ALL_INITIAL_FILES_BUT_ONE
 
 '
 
-test_expect_failure 'test tag T_MIXED' '
+test_expect_failure PERL 'test tag T_MIXED' '
 
 	test_cmp_branch_tree T_MIXED
 
diff --git a/t/t9603-cvsimport-patchsets.sh b/t/t9603-cvsimport-patchsets.sh
index 958bdce..52034c8 100755
--- a/t/t9603-cvsimport-patchsets.sh
+++ b/t/t9603-cvsimport-patchsets.sh
@@ -14,18 +14,17 @@
 test_description='git cvsimport testing for correct patchset estimation'
 . ./lib-cvs.sh
 
-CVSROOT="$TEST_DIRECTORY"/t9603/cvsroot
-export CVSROOT
+setup_cvs_test_repository t9603
 
 test_expect_failure 'import with criss cross times on revisions' '
 
     git cvsimport -p"-x" -C module-git module &&
-    cd module-git &&
+    (cd module-git &&
         git log --pretty=format:%s > ../actual-master &&
         git log A~2..A --pretty="format:%s %ad" -- > ../actual-A &&
         echo "" >> ../actual-master &&
-        echo "" >> ../actual-A &&
-    cd .. &&
+	echo "" >> ../actual-A
+    ) &&
     echo "Rev 4
 Rev 3
 Rev 2
diff --git a/t/t9700-perl-git.sh b/t/t9700-perl-git.sh
index 4eb7d3f..435d896 100755
--- a/t/t9700-perl-git.sh
+++ b/t/t9700-perl-git.sh
@@ -7,12 +7,12 @@
 . ./test-lib.sh
 
 if ! test_have_prereq PERL; then
-	say 'skipping perl interface tests, perl not available'
+	skip_all='skipping perl interface tests, perl not available'
 	test_done
 fi
 
-perl -MTest::More -e 0 2>/dev/null || {
-	say "Perl Test::More unavailable, skipping test"
+"$PERL_PATH" -MTest::More -e 0 2>/dev/null || {
+	skip_all="Perl Test::More unavailable, skipping test"
 	test_done
 }
 
@@ -43,11 +43,18 @@
      git config --add test.booltrue true &&
      git config --add test.boolfalse no &&
      git config --add test.boolother other &&
-     git config --add test.int 2k
+     git config --add test.int 2k &&
+     git config --add test.path "~/foo" &&
+     git config --add test.pathexpanded "$HOME/foo" &&
+     git config --add test.pathmulti foo &&
+     git config --add test.pathmulti bar
      '
 
+# The external test will outputs its own plan
+test_external_has_tap=1
+
 test_external_without_stderr \
     'Perl API' \
-    perl "$TEST_DIRECTORY"/t9700/test.pl
+    "$PERL_PATH" "$TEST_DIRECTORY"/t9700/test.pl
 
 test_done
diff --git a/t/t9700/test.pl b/t/t9700/test.pl
index 6c70aec..3b9b484 100755
--- a/t/t9700/test.pl
+++ b/t/t9700/test.pl
@@ -1,19 +1,26 @@
 #!/usr/bin/perl
 use lib (split(/:/, $ENV{GITPERLLIB}));
 
-use 5.006002;
+use 5.008;
 use warnings;
 use strict;
 
 use Test::More qw(no_plan);
 
+BEGIN {
+	# t9700-perl-git.sh kicks off our testing, so we have to go from
+	# there.
+	Test::More->builder->current_test(1);
+	Test::More->builder->no_ending(1);
+}
+
 use Cwd;
 use File::Basename;
 
 BEGIN { use_ok('Git') }
 
 # set up
-our $abs_repo_dir = Cwd->cwd;
+our $abs_repo_dir = cwd();
 ok(our $r = Git->repository(Directory => "."), "open repository");
 
 # config
@@ -26,6 +33,10 @@
 is($r->config_int("test.nonexistent"), undef, "config_int: nonexistent");
 ok($r->config_bool("test.booltrue"), "config_bool: true");
 ok(!$r->config_bool("test.boolfalse"), "config_bool: false");
+is($r->config_path("test.path"), $r->config("test.pathexpanded"),
+   "config_path: ~/foo expansion");
+is_deeply([$r->config_path("test.pathmulti")], ["foo", "bar"],
+   "config_path: multiple values");
 our $ansi_green = "\x1b[32m";
 is($r->get_color("color.test.slot1", "red"), $ansi_green, "get_color");
 # Cannot test $r->get_colorbool("color.foo")) because we do not
@@ -105,3 +116,18 @@
 like($last_commit, qr/^[0-9a-fA-F]{40}$/, 'rev-parse returned hash');
 my $dir_commit = $r2->command_oneline('log', '-n1', '--pretty=format:%H', '.');
 isnt($last_commit, $dir_commit, 'log . does not show last commit');
+
+# commands outside working tree
+chdir($abs_repo_dir . '/..');
+my $r3 = Git->repository(Directory => $abs_repo_dir);
+my $tmpfile3 = "$abs_repo_dir/file3.tmp";
+open TEMPFILE3, "+>$tmpfile3" or die "Can't open $tmpfile3: $!";
+is($r3->cat_blob($file1hash, \*TEMPFILE3), 15, "cat_blob(outside): size");
+close TEMPFILE3;
+unlink $tmpfile3;
+chdir($abs_repo_dir);
+
+printf "1..%d\n", Test::More->builder->current_test;
+
+my $is_passing = eval { Test::More->is_passing };
+exit($is_passing ? 0 : 1) unless $@ =~ /Can't locate object method/;
diff --git a/t/t9800-git-p4-basic.sh b/t/t9800-git-p4-basic.sh
new file mode 100755
index 0000000..b7ad716
--- /dev/null
+++ b/t/t9800-git-p4-basic.sh
@@ -0,0 +1,179 @@
+#!/bin/sh
+
+test_description='git p4 tests'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+	start_p4d
+'
+
+test_expect_success 'add p4 files' '
+	(
+		cd "$cli" &&
+		echo file1 >file1 &&
+		p4 add file1 &&
+		p4 submit -d "file1" &&
+		echo file2 >file2 &&
+		p4 add file2 &&
+		p4 submit -d "file2"
+	)
+'
+
+test_expect_success 'basic git p4 clone' '
+	git p4 clone --dest="$git" //depot &&
+	test_when_finished cleanup_git &&
+	(
+		cd "$git" &&
+		git log --oneline >lines &&
+		test_line_count = 1 lines
+	)
+'
+
+test_expect_success 'git p4 clone @all' '
+	git p4 clone --dest="$git" //depot@all &&
+	test_when_finished cleanup_git &&
+	(
+		cd "$git" &&
+		git log --oneline >lines &&
+		test_line_count = 2 lines
+	)
+'
+
+test_expect_success 'git p4 sync uninitialized repo' '
+	test_create_repo "$git" &&
+	test_when_finished cleanup_git &&
+	(
+		cd "$git" &&
+		test_must_fail git p4 sync 2>errs &&
+		test_i18ngrep "Perhaps you never did" errs
+	)
+'
+
+#
+# Create a git repo by hand.  Add a commit so that HEAD is valid.
+# Test imports a new p4 repository into a new git branch.
+#
+test_expect_success 'git p4 sync new branch' '
+	test_create_repo "$git" &&
+	test_when_finished cleanup_git &&
+	(
+		cd "$git" &&
+		test_commit head &&
+		git p4 sync --branch=refs/remotes/p4/depot //depot@all &&
+		git log --oneline p4/depot >lines &&
+		test_line_count = 2 lines
+	)
+'
+
+test_expect_success 'clone two dirs' '
+	(
+		cd "$cli" &&
+		mkdir sub1 sub2 &&
+		echo sub1/f1 >sub1/f1 &&
+		echo sub2/f2 >sub2/f2 &&
+		p4 add sub1/f1 &&
+		p4 submit -d "sub1/f1" &&
+		p4 add sub2/f2 &&
+		p4 submit -d "sub2/f2"
+	) &&
+	git p4 clone --dest="$git" //depot/sub1 //depot/sub2 &&
+	test_when_finished cleanup_git &&
+	(
+		cd "$git" &&
+		git ls-files >lines &&
+		test_line_count = 2 lines &&
+		git log --oneline p4/master >lines &&
+		test_line_count = 1 lines
+	)
+'
+
+test_expect_success 'clone two dirs, @all' '
+	(
+		cd "$cli" &&
+		echo sub1/f3 >sub1/f3 &&
+		p4 add sub1/f3 &&
+		p4 submit -d "sub1/f3"
+	) &&
+	git p4 clone --dest="$git" //depot/sub1@all //depot/sub2@all &&
+	test_when_finished cleanup_git &&
+	(
+		cd "$git" &&
+		git ls-files >lines &&
+		test_line_count = 3 lines &&
+		git log --oneline p4/master >lines &&
+		test_line_count = 3 lines
+	)
+'
+
+test_expect_success 'clone two dirs, @all, conflicting files' '
+	(
+		cd "$cli" &&
+		echo sub2/f3 >sub2/f3 &&
+		p4 add sub2/f3 &&
+		p4 submit -d "sub2/f3"
+	) &&
+	git p4 clone --dest="$git" //depot/sub1@all //depot/sub2@all &&
+	test_when_finished cleanup_git &&
+	(
+		cd "$git" &&
+		git ls-files >lines &&
+		test_line_count = 3 lines &&
+		git log --oneline p4/master >lines &&
+		test_line_count = 4 lines &&
+		echo sub2/f3 >expected &&
+		test_cmp expected f3
+	)
+'
+
+test_expect_success 'exit when p4 fails to produce marshaled output' '
+	mkdir badp4dir &&
+	test_when_finished "rm badp4dir/p4 && rmdir badp4dir" &&
+	cat >badp4dir/p4 <<-EOF &&
+	#!$SHELL_PATH
+	exit 1
+	EOF
+	chmod 755 badp4dir/p4 &&
+	(
+		PATH="$TRASH_DIRECTORY/badp4dir:$PATH" &&
+		export PATH &&
+		test_expect_code 1 git p4 clone --dest="$git" //depot >errs 2>&1
+	) &&
+	cat errs &&
+	! test_i18ngrep Traceback errs
+'
+
+test_expect_success 'clone bare' '
+	rm -rf "$git" &&
+	git p4 clone --dest="$git" --bare //depot &&
+	test_when_finished cleanup_git &&
+	(
+		cd "$git" &&
+		test ! -d .git &&
+		bare=`git config --get core.bare` &&
+		test "$bare" = true
+	)
+'
+
+# Sleep a bit so that the top-most p4 change did not happen "now".  Then
+# import the repo and make sure that the initial import has the same time
+# as the top-most change.
+test_expect_success 'initial import time from top change time' '
+	p4change=$(p4 -G changes -m 1 //depot/... | marshal_dump change) &&
+	p4time=$(p4 -G changes -m 1 //depot/... | marshal_dump time) &&
+	sleep 3 &&
+	git p4 clone --dest="$git" //depot &&
+	test_when_finished cleanup_git &&
+	(
+		cd "$git" &&
+		gittime=$(git show -s --raw --pretty=format:%at HEAD) &&
+		echo $p4time $gittime &&
+		test $p4time = $gittime
+	)
+'
+
+test_expect_success 'kill p4d' '
+	kill_p4d
+'
+
+test_done
diff --git a/t/t9801-git-p4-branch.sh b/t/t9801-git-p4-branch.sh
new file mode 100755
index 0000000..99fe16b
--- /dev/null
+++ b/t/t9801-git-p4-branch.sh
@@ -0,0 +1,417 @@
+#!/bin/sh
+
+test_description='git p4 tests for p4 branches'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+	start_p4d
+'
+
+#
+# 1: //depot/main/f1
+# 2: //depot/main/f2
+# 3: integrate //depot/main/... -> //depot/branch1/...
+# 4: //depot/main/f4
+# 5: //depot/branch1/f5
+# .: named branch branch2
+# 6: integrate -b branch2
+# 7: //depot/branch2/f7
+# 8: //depot/main/f8
+#
+test_expect_success 'basic p4 branches' '
+	(
+		cd "$cli" &&
+		mkdir -p main &&
+
+		echo f1 >main/f1 &&
+		p4 add main/f1 &&
+		p4 submit -d "main/f1" &&
+
+		echo f2 >main/f2 &&
+		p4 add main/f2 &&
+		p4 submit -d "main/f2" &&
+
+		p4 integrate //depot/main/... //depot/branch1/... &&
+		p4 submit -d "integrate main to branch1" &&
+
+		echo f4 >main/f4 &&
+		p4 add main/f4 &&
+		p4 submit -d "main/f4" &&
+
+		echo f5 >branch1/f5 &&
+		p4 add branch1/f5 &&
+		p4 submit -d "branch1/f5" &&
+
+		p4 branch -i <<-EOF &&
+		Branch: branch2
+		View: //depot/main/... //depot/branch2/...
+		EOF
+
+		p4 integrate -b branch2 &&
+		p4 submit -d "integrate main to branch2" &&
+
+		echo f7 >branch2/f7 &&
+		p4 add branch2/f7 &&
+		p4 submit -d "branch2/f7" &&
+
+		echo f8 >main/f8 &&
+		p4 add main/f8 &&
+		p4 submit -d "main/f8"
+	)
+'
+
+test_expect_success 'import main, no branch detection' '
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot/main@all &&
+	(
+		cd "$git" &&
+		git log --oneline --graph --decorate --all &&
+		git rev-list master >wc &&
+		test_line_count = 4 wc
+	)
+'
+
+test_expect_success 'import branch1, no branch detection' '
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot/branch1@all &&
+	(
+		cd "$git" &&
+		git log --oneline --graph --decorate --all &&
+		git rev-list master >wc &&
+		test_line_count = 2 wc
+	)
+'
+
+test_expect_success 'import branch2, no branch detection' '
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot/branch2@all &&
+	(
+		cd "$git" &&
+		git log --oneline --graph --decorate --all &&
+		git rev-list master >wc &&
+		test_line_count = 2 wc
+	)
+'
+
+test_expect_success 'import depot, no branch detection' '
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot@all &&
+	(
+		cd "$git" &&
+		git log --oneline --graph --decorate --all &&
+		git rev-list master >wc &&
+		test_line_count = 8 wc
+	)
+'
+
+test_expect_success 'import depot, branch detection' '
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" --detect-branches //depot@all &&
+	(
+		cd "$git" &&
+
+		git log --oneline --graph --decorate --all &&
+
+		# 4 main commits
+		git rev-list master >wc &&
+		test_line_count = 4 wc &&
+
+		# 3 main, 1 integrate, 1 on branch2
+		git rev-list p4/depot/branch2 >wc &&
+		test_line_count = 5 wc &&
+
+		# no branch1, since no p4 branch created for it
+		test_must_fail git show-ref p4/depot/branch1
+	)
+'
+
+test_expect_success 'import depot, branch detection, branchList branch definition' '
+	test_when_finished cleanup_git &&
+	test_create_repo "$git" &&
+	(
+		cd "$git" &&
+		git config git-p4.branchList main:branch1 &&
+		git p4 clone --dest=. --detect-branches //depot@all &&
+
+		git log --oneline --graph --decorate --all &&
+
+		# 4 main commits
+		git rev-list master >wc &&
+		test_line_count = 4 wc &&
+
+		# 3 main, 1 integrate, 1 on branch2
+		git rev-list p4/depot/branch2 >wc &&
+		test_line_count = 5 wc &&
+
+		# 2 main, 1 integrate, 1 on branch1
+		git rev-list p4/depot/branch1 >wc &&
+		test_line_count = 4 wc
+	)
+'
+
+test_expect_success 'restart p4d' '
+	kill_p4d &&
+	start_p4d
+'
+
+#
+# 1: //depot/branch1/file1
+#    //depot/branch1/file2
+# 2: integrate //depot/branch1/... -> //depot/branch2/...
+# 3: //depot/branch1/file3
+# 4: //depot/branch1/file2 (edit)
+# 5: integrate //depot/branch1/... -> //depot/branch3/...
+#
+## Create a simple branch structure in P4 depot.
+test_expect_success 'add simple p4 branches' '
+	(
+		cd "$cli" &&
+		mkdir branch1 &&
+		cd branch1 &&
+		echo file1 >file1 &&
+		echo file2 >file2 &&
+		p4 add file1 file2 &&
+		p4 submit -d "Create branch1" &&
+		p4 integrate //depot/branch1/... //depot/branch2/... &&
+		p4 submit -d "Integrate branch2 from branch1" &&
+		echo file3 >file3 &&
+		p4 add file3 &&
+		p4 submit -d "add file3 in branch1" &&
+		p4 open file2 &&
+		echo update >>file2 &&
+		p4 submit -d "update file2 in branch1" &&
+		p4 integrate //depot/branch1/... //depot/branch3/... &&
+		p4 submit -d "Integrate branch3 from branch1"
+	)
+'
+
+# Configure branches through git-config and clone them.
+# All files are tested to make sure branches were cloned correctly.
+# Finally, make an update to branch1 on P4 side to check if it is imported
+# correctly by git p4.
+test_expect_success 'git p4 clone simple branches' '
+	test_when_finished cleanup_git &&
+	test_create_repo "$git" &&
+	(
+		cd "$git" &&
+		git config git-p4.branchList branch1:branch2 &&
+		git config --add git-p4.branchList branch1:branch3 &&
+		git p4 clone --dest=. --detect-branches //depot@all &&
+		git log --all --graph --decorate --stat &&
+		git reset --hard p4/depot/branch1 &&
+		test -f file1 &&
+		test -f file2 &&
+		test -f file3 &&
+		grep update file2 &&
+		git reset --hard p4/depot/branch2 &&
+		test -f file1 &&
+		test -f file2 &&
+		test ! -f file3 &&
+		! grep update file2 &&
+		git reset --hard p4/depot/branch3 &&
+		test -f file1 &&
+		test -f file2 &&
+		test -f file3 &&
+		grep update file2 &&
+		cd "$cli" &&
+		cd branch1 &&
+		p4 edit file2 &&
+		echo file2_ >>file2 &&
+		p4 submit -d "update file2 in branch1" &&
+		cd "$git" &&
+		git reset --hard p4/depot/branch1 &&
+		git p4 rebase &&
+		grep file2_ file2
+	)
+'
+
+# Create a complex branch structure in P4 depot to check if they are correctly
+# cloned. The branches are created from older changelists to check if git p4 is
+# able to correctly detect them.
+# The final expected structure is:
+# `branch1
+# | `- file1
+# | `- file2 (updated)
+# | `- file3
+# `branch2
+# | `- file1
+# | `- file2
+# `branch3
+# | `- file1
+# | `- file2 (updated)
+# | `- file3
+# `branch4
+# | `- file1
+# | `- file2
+# `branch5
+#   `- file1
+#   `- file2
+#   `- file3
+test_expect_success 'git p4 add complex branches' '
+	(
+		cd "$cli" &&
+		changelist=$(p4 changes -m1 //depot/... | cut -d" " -f2) &&
+		changelist=$(($changelist - 5)) &&
+		p4 integrate //depot/branch1/...@$changelist //depot/branch4/... &&
+		p4 submit -d "Integrate branch4 from branch1@${changelist}" &&
+		changelist=$(($changelist + 2)) &&
+		p4 integrate //depot/branch1/...@$changelist //depot/branch5/... &&
+		p4 submit -d "Integrate branch5 from branch1@${changelist}"
+	)
+'
+
+# Configure branches through git-config and clone them. git p4 will only be able
+# to clone the original structure if it is able to detect the origin changelist
+# of each branch.
+test_expect_success 'git p4 clone complex branches' '
+	test_when_finished cleanup_git &&
+	test_create_repo "$git" &&
+	(
+		cd "$git" &&
+		git config git-p4.branchList branch1:branch2 &&
+		git config --add git-p4.branchList branch1:branch3 &&
+		git config --add git-p4.branchList branch1:branch4 &&
+		git config --add git-p4.branchList branch1:branch5 &&
+		git p4 clone --dest=. --detect-branches //depot@all &&
+		git log --all --graph --decorate --stat &&
+		git reset --hard p4/depot/branch1 &&
+		test_path_is_file file1 &&
+		test_path_is_file file2 &&
+		test_path_is_file file3 &&
+		grep update file2 &&
+		git reset --hard p4/depot/branch2 &&
+		test_path_is_file file1 &&
+		test_path_is_file file2 &&
+		test_path_is_missing file3 &&
+		! grep update file2 &&
+		git reset --hard p4/depot/branch3 &&
+		test_path_is_file file1 &&
+		test_path_is_file file2 &&
+		test_path_is_file file3 &&
+		grep update file2 &&
+		git reset --hard p4/depot/branch4 &&
+		test_path_is_file file1 &&
+		test_path_is_file file2 &&
+		test_path_is_missing file3 &&
+		! grep update file2 &&
+		git reset --hard p4/depot/branch5 &&
+		test_path_is_file file1 &&
+		test_path_is_file file2 &&
+		test_path_is_file file3 &&
+		! grep update file2 &&
+		test_path_is_missing .git/git-p4-tmp
+	)
+'
+
+# Move branch3/file3 to branch4/file3 in a single changelist
+test_expect_success 'git p4 submit to two branches in a single changelist' '
+	(
+		cd "$cli" &&
+		p4 integrate //depot/branch3/file3 //depot/branch4/file3 &&
+		p4 delete //depot/branch3/file3 &&
+		p4 submit -d "Move branch3/file3 to branch4/file3"
+	)
+'
+
+# Confirm that changes to two branches done in a single changelist
+# are correctly imported by git p4
+test_expect_success 'git p4 sync changes to two branches in the same changelist' '
+	test_when_finished cleanup_git &&
+	test_create_repo "$git" &&
+	(
+		cd "$git" &&
+		git config git-p4.branchList branch1:branch2 &&
+		git config --add git-p4.branchList branch1:branch3 &&
+		git config --add git-p4.branchList branch1:branch4 &&
+		git config --add git-p4.branchList branch1:branch5 &&
+		git p4 clone --dest=. --detect-branches //depot@all &&
+		git log --all --graph --decorate --stat &&
+		git reset --hard p4/depot/branch1 &&
+		test_path_is_file file1 &&
+		test_path_is_file file2 &&
+		test_path_is_file file3 &&
+		grep update file2 &&
+		git reset --hard p4/depot/branch2 &&
+		test_path_is_file file1 &&
+		test_path_is_file file2 &&
+		test_path_is_missing file3 &&
+		! grep update file2 &&
+		git reset --hard p4/depot/branch3 &&
+		test_path_is_file file1 &&
+		test_path_is_file file2 &&
+		test_path_is_missing file3 &&
+		grep update file2 &&
+		git reset --hard p4/depot/branch4 &&
+		test_path_is_file file1 &&
+		test_path_is_file file2 &&
+		test_path_is_file file3 &&
+		! grep update file2 &&
+		git reset --hard p4/depot/branch5 &&
+		test_path_is_file file1 &&
+		test_path_is_file file2 &&
+		test_path_is_file file3 &&
+		! grep update file2 &&
+		test_path_is_missing .git/git-p4-tmp
+	)
+'
+
+# Create a branch by integrating a single file
+test_expect_success 'git p4 file subset branch' '
+	(
+		cd "$cli" &&
+		p4 integrate //depot/branch1/file1 //depot/branch6/file1 &&
+		p4 submit -d "Integrate file1 alone from branch1 to branch6"
+	)
+'
+
+# Check if git p4 creates a new branch containing a single file,
+# instead of keeping the old files from the original branch
+test_expect_failure 'git p4 clone file subset branch' '
+	test_when_finished cleanup_git &&
+	test_create_repo "$git" &&
+	(
+		cd "$git" &&
+		git config git-p4.branchList branch1:branch2 &&
+		git config --add git-p4.branchList branch1:branch3 &&
+		git config --add git-p4.branchList branch1:branch4 &&
+		git config --add git-p4.branchList branch1:branch5 &&
+		git config --add git-p4.branchList branch1:branch6 &&
+		git p4 clone --dest=. --detect-branches //depot@all &&
+		git log --all --graph --decorate --stat &&
+		git reset --hard p4/depot/branch1 &&
+		test_path_is_file file1 &&
+		test_path_is_file file2 &&
+		test_path_is_file file3 &&
+		grep update file2 &&
+		git reset --hard p4/depot/branch2 &&
+		test_path_is_file file1 &&
+		test_path_is_file file2 &&
+		test_path_is_missing file3 &&
+		! grep update file2 &&
+		git reset --hard p4/depot/branch3 &&
+		test_path_is_file file1 &&
+		test_path_is_file file2 &&
+		test_path_is_missing file3 &&
+		grep update file2 &&
+		git reset --hard p4/depot/branch4 &&
+		test_path_is_file file1 &&
+		test_path_is_file file2 &&
+		test_path_is_file file3 &&
+		! grep update file2 &&
+		git reset --hard p4/depot/branch5 &&
+		test_path_is_file file1 &&
+		test_path_is_file file2 &&
+		test_path_is_file file3 &&
+		! grep update file2 &&
+		git reset --hard p4/depot/branch6 &&
+		test_path_is_file file1 &&
+		test_path_is_missing file2 &&
+		test_path_is_missing file3
+	)
+'
+test_expect_success 'kill p4d' '
+	kill_p4d
+'
+
+test_done
diff --git a/t/t9802-git-p4-filetype.sh b/t/t9802-git-p4-filetype.sh
new file mode 100755
index 0000000..21924df
--- /dev/null
+++ b/t/t9802-git-p4-filetype.sh
@@ -0,0 +1,139 @@
+#!/bin/sh
+
+test_description='git p4 filetype tests'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+	start_p4d
+'
+
+test_expect_success 'utf-16 file create' '
+	(
+		cd "$cli" &&
+
+		# p4 saves this verbatim
+		printf "three\nline\ntext\n" >f-ascii &&
+		p4 add -t text f-ascii &&
+
+		# p4 adds \377\376 header
+		cp f-ascii f-ascii-as-utf16 &&
+		p4 add -t utf16 f-ascii-as-utf16 &&
+
+		# p4 saves this exactly as iconv produced it
+		printf "three\nline\ntext\n" | iconv -f ascii -t utf-16 >f-utf16 &&
+		p4 add -t utf16 f-utf16 &&
+
+		# this also is unchanged
+		cp f-utf16 f-utf16-as-text &&
+		p4 add -t text f-utf16-as-text &&
+
+		p4 submit -d "f files" &&
+
+		# force update of client files
+		p4 sync -f
+	)
+'
+
+test_expect_success 'utf-16 file test' '
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot@all &&
+	(
+		cd "$git" &&
+
+		test_cmp "$cli/f-ascii" f-ascii &&
+		test_cmp "$cli/f-ascii-as-utf16" f-ascii-as-utf16 &&
+		test_cmp "$cli/f-utf16" f-utf16 &&
+		test_cmp "$cli/f-utf16-as-text" f-utf16-as-text
+	)
+'
+
+test_expect_success 'keyword file create' '
+	(
+		cd "$cli" &&
+
+		printf "id\n\$Id\$\n\$Author\$\ntext\n" >k-text-k &&
+		p4 add -t text+k k-text-k &&
+
+		cp k-text-k k-text-ko &&
+		p4 add -t text+ko k-text-ko &&
+
+		cat k-text-k | iconv -f ascii -t utf-16 >k-utf16-k &&
+		p4 add -t utf16+k k-utf16-k &&
+
+		cp k-utf16-k k-utf16-ko &&
+		p4 add -t utf16+ko k-utf16-ko &&
+
+		p4 submit -d "k files" &&
+		p4 sync -f
+	)
+'
+
+build_smush() {
+	cat >k_smush.py <<-\EOF &&
+	import re, sys
+	sys.stdout.write(re.sub(r'(?i)\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\$', r'$\1$', sys.stdin.read()))
+	EOF
+	cat >ko_smush.py <<-\EOF
+	import re, sys
+	sys.stdout.write(re.sub(r'(?i)\$(Id|Header):[^$]*\$', r'$\1$', sys.stdin.read()))
+	EOF
+}
+
+test_expect_success 'keyword file test' '
+	build_smush &&
+	test_when_finished rm -f k_smush.py ko_smush.py &&
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot@all &&
+	(
+		cd "$git" &&
+
+		# text, ensure unexpanded
+		"$PYTHON_PATH" "$TRASH_DIRECTORY/k_smush.py" <"$cli/k-text-k" >cli-k-text-k-smush &&
+		test_cmp cli-k-text-k-smush k-text-k &&
+		"$PYTHON_PATH" "$TRASH_DIRECTORY/ko_smush.py" <"$cli/k-text-ko" >cli-k-text-ko-smush &&
+		test_cmp cli-k-text-ko-smush k-text-ko &&
+
+		# utf16, even though p4 expands keywords, git p4 does not
+		# try to undo that
+		test_cmp "$cli/k-utf16-k" k-utf16-k &&
+		test_cmp "$cli/k-utf16-ko" k-utf16-ko
+	)
+'
+
+build_gendouble() {
+	cat >gendouble.py <<-\EOF
+	import sys
+	import struct
+	import array
+
+	s = array.array("c", '\0' * 26)
+	struct.pack_into(">L", s,  0, 0x00051607)  # AppleDouble
+	struct.pack_into(">L", s,  4, 0x00020000)  # version 2
+	s.tofile(sys.stdout)
+	EOF
+}
+
+test_expect_success 'ignore apple' '
+	test_when_finished rm -f gendouble.py &&
+	build_gendouble &&
+	(
+		cd "$cli" &&
+		test-genrandom apple 1024 >double.png &&
+		"$PYTHON_PATH" "$TRASH_DIRECTORY/gendouble.py" >%double.png &&
+		p4 add -t apple double.png &&
+		p4 submit -d appledouble
+	) &&
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot@all &&
+	(
+		cd "$git" &&
+		test ! -f double.png
+	)
+'
+
+test_expect_success 'kill p4d' '
+	kill_p4d
+'
+
+test_done
diff --git a/t/t9803-git-p4-shell-metachars.sh b/t/t9803-git-p4-shell-metachars.sh
new file mode 100755
index 0000000..fbacff3
--- /dev/null
+++ b/t/t9803-git-p4-shell-metachars.sh
@@ -0,0 +1,112 @@
+#!/bin/sh
+
+test_description='git p4 transparency to shell metachars in filenames'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+	start_p4d
+'
+
+test_expect_success 'init depot' '
+	(
+		cd "$cli" &&
+		echo file1 >file1 &&
+		p4 add file1 &&
+		p4 submit -d "file1"
+	)
+'
+
+test_expect_success 'shell metachars in filenames' '
+	git p4 clone --dest="$git" //depot &&
+	test_when_finished cleanup_git &&
+	(
+		cd "$git" &&
+		git config git-p4.skipSubmitEditCheck true &&
+		echo f1 >foo\$bar &&
+		git add foo\$bar &&
+		echo f2 >"file with spaces" &&
+		git add "file with spaces" &&
+		git commit -m "add files" &&
+		P4EDITOR=touch git p4 submit
+	) &&
+	(
+		cd "$cli" &&
+		p4 sync ... &&
+		test -e "file with spaces" &&
+		test -e "foo\$bar"
+	)
+'
+
+test_expect_success 'deleting with shell metachars' '
+	git p4 clone --dest="$git" //depot &&
+	test_when_finished cleanup_git &&
+	(
+		cd "$git" &&
+		git config git-p4.skipSubmitEditCheck true &&
+		git rm foo\$bar &&
+		git rm file\ with\ spaces &&
+		git commit -m "remove files" &&
+		P4EDITOR=touch git p4 submit
+	) &&
+	(
+		cd "$cli" &&
+		p4 sync ... &&
+		test ! -e "file with spaces" &&
+		test ! -e foo\$bar
+	)
+'
+
+# Create a branch with a shell metachar in its name
+#
+# 1. //depot/main
+# 2. //depot/branch$3
+
+test_expect_success 'branch with shell char' '
+	test_when_finished cleanup_git &&
+	test_create_repo "$git" &&
+	(
+		cd "$cli" &&
+
+		mkdir -p main &&
+
+		echo f1 >main/f1 &&
+		p4 add main/f1 &&
+		p4 submit -d "main/f1" &&
+
+		p4 integrate //depot/main/... //depot/branch\$3/... &&
+		p4 submit -d "integrate main to branch\$3" &&
+
+		echo f1 >branch\$3/shell_char_branch_file &&
+		p4 add branch\$3/shell_char_branch_file &&
+		p4 submit -d "branch\$3/shell_char_branch_file" &&
+
+		p4 branch -i <<-EOF &&
+		Branch: branch\$3
+		View: //depot/main/... //depot/branch\$3/...
+		EOF
+
+		p4 edit main/f1 &&
+		echo "a change" >> main/f1 &&
+		p4 submit -d "a change" main/f1 &&
+
+		p4 integrate -b branch\$3 &&
+		p4 resolve -am branch\$3/... &&
+		p4 submit -d "integrate main to branch\$3" &&
+
+		cd "$git" &&
+
+		git config git-p4.branchList main:branch\$3 &&
+		git p4 clone --dest=. --detect-branches //depot@all &&
+		git log --all --graph --decorate --stat &&
+		git reset --hard p4/depot/branch\$3 &&
+		test -f shell_char_branch_file &&
+		test -f f1
+	)
+'
+
+test_expect_success 'kill p4d' '
+	kill_p4d
+'
+
+test_done
diff --git a/t/t9804-git-p4-label.sh b/t/t9804-git-p4-label.sh
new file mode 100755
index 0000000..e30f80e
--- /dev/null
+++ b/t/t9804-git-p4-label.sh
@@ -0,0 +1,115 @@
+#!/bin/sh
+
+test_description='git p4 label tests'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+	start_p4d
+'
+
+# Basic p4 label tests.
+#
+# Note: can't have more than one label per commit - others
+# are silently discarded.
+#
+test_expect_success 'basic p4 labels' '
+	test_when_finished cleanup_git &&
+	(
+		cd "$cli" &&
+		mkdir -p main &&
+
+		echo f1 >main/f1 &&
+		p4 add main/f1 &&
+		p4 submit -d "main/f1" &&
+
+		echo f2 >main/f2 &&
+		p4 add main/f2 &&
+		p4 submit -d "main/f2" &&
+
+		echo f3 >main/file_with_\$metachar &&
+		p4 add main/file_with_\$metachar &&
+		p4 submit -d "file with metachar" &&
+
+		p4 tag -l tag_f1_only main/f1 &&
+		p4 tag -l tag_with\$_shell_char main/... &&
+
+		echo f4 >main/f4 &&
+		p4 add main/f4 &&
+		p4 submit -d "main/f4" &&
+
+		p4 label -i <<-EOF &&
+		Label: long_label
+		Description:
+		   A Label first line
+		   A Label second line
+		View:	//depot/...
+		EOF
+
+		p4 tag -l long_label ... &&
+
+		p4 labels ... &&
+
+		git p4 clone --dest="$git" --detect-labels //depot@all &&
+		cd "$git" &&
+
+		git tag &&
+		git tag >taglist &&
+		test_line_count = 3 taglist &&
+
+		cd main &&
+		git checkout tag_tag_f1_only &&
+		! test -f f2 &&
+		git checkout tag_tag_with\$_shell_char &&
+		test -f f1 && test -f f2 && test -f file_with_\$metachar &&
+
+		git show tag_long_label | grep -q "A Label second line"
+	)
+'
+
+# Test some label corner cases:
+#
+# - two tags on the same file; both should be available
+# - a tag that is only on one file; this kind of tag
+#   cannot be imported (at least not easily).
+
+test_expect_failure 'two labels on the same changelist' '
+	test_when_finished cleanup_git &&
+	(
+		cd "$cli" &&
+		mkdir -p main &&
+
+		p4 edit main/f1 main/f2 &&
+		echo "hello world" >main/f1 &&
+		echo "not in the tag" >main/f2 &&
+		p4 submit -d "main/f[12]: testing two labels" &&
+
+		p4 tag -l tag_f1_1 main/... &&
+		p4 tag -l tag_f1_2 main/... &&
+
+		p4 labels ... &&
+
+		git p4 clone --dest="$git" --detect-labels //depot@all &&
+		cd "$git" &&
+
+		git tag | grep tag_f1 &&
+		git tag | grep -q tag_f1_1 &&
+		git tag | grep -q tag_f1_2 &&
+
+		cd main &&
+
+		git checkout tag_tag_f1_1 &&
+		ls &&
+		test -f f1 &&
+
+		git checkout tag_tag_f1_2 &&
+		ls &&
+		test -f f1
+	)
+'
+
+test_expect_success 'kill p4d' '
+	kill_p4d
+'
+
+test_done
diff --git a/t/t9805-git-p4-skip-submit-edit.sh b/t/t9805-git-p4-skip-submit-edit.sh
new file mode 100755
index 0000000..fb3c8ec
--- /dev/null
+++ b/t/t9805-git-p4-skip-submit-edit.sh
@@ -0,0 +1,103 @@
+#!/bin/sh
+
+test_description='git p4 skipSubmitEdit config variables'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+	start_p4d
+'
+
+test_expect_success 'init depot' '
+	(
+		cd "$cli" &&
+		echo file1 >file1 &&
+		p4 add file1 &&
+		p4 submit -d "change 1"
+	)
+'
+
+# this works because EDITOR is set to :
+test_expect_success 'no config, unedited, say yes' '
+	git p4 clone --dest="$git" //depot &&
+	test_when_finished cleanup_git &&
+	(
+		cd "$git" &&
+		echo line >>file1 &&
+		git commit -a -m "change 2" &&
+		echo y | git p4 submit &&
+		p4 changes //depot/... >wc &&
+		test_line_count = 2 wc
+	)
+'
+
+test_expect_success 'no config, unedited, say no' '
+	git p4 clone --dest="$git" //depot &&
+	test_when_finished cleanup_git &&
+	(
+		cd "$git" &&
+		echo line >>file1 &&
+		git commit -a -m "change 3 (not really)" &&
+		printf "bad response\nn\n" | git p4 submit &&
+		p4 changes //depot/... >wc &&
+		test_line_count = 2 wc
+	)
+'
+
+test_expect_success 'skipSubmitEdit' '
+	git p4 clone --dest="$git" //depot &&
+	test_when_finished cleanup_git &&
+	(
+		cd "$git" &&
+		git config git-p4.skipSubmitEdit true &&
+		# will fail if editor is even invoked
+		git config core.editor /bin/false &&
+		echo line >>file1 &&
+		git commit -a -m "change 3" &&
+		git p4 submit &&
+		p4 changes //depot/... >wc &&
+		test_line_count = 3 wc
+	)
+'
+
+test_expect_success 'skipSubmitEditCheck' '
+	git p4 clone --dest="$git" //depot &&
+	test_when_finished cleanup_git &&
+	(
+		cd "$git" &&
+		git config git-p4.skipSubmitEditCheck true &&
+		echo line >>file1 &&
+		git commit -a -m "change 4" &&
+		git p4 submit &&
+		p4 changes //depot/... >wc &&
+		test_line_count = 4 wc
+	)
+'
+
+# check the normal case, where the template really is edited
+test_expect_success 'no config, edited' '
+	git p4 clone --dest="$git" //depot &&
+	test_when_finished cleanup_git &&
+	test_when_finished "rm ed.sh" &&
+	cat >ed.sh <<-EOF &&
+		#!$SHELL_PATH
+		sleep 1
+		touch "\$1"
+		exit 0
+	EOF
+	chmod 755 ed.sh &&
+	(
+		cd "$git" &&
+		echo line >>file1 &&
+		git commit -a -m "change 5" &&
+		P4EDITOR="" EDITOR="\"$TRASH_DIRECTORY/ed.sh\"" git p4 submit &&
+		p4 changes //depot/... >wc &&
+		test_line_count = 5 wc
+	)
+'
+
+test_expect_success 'kill p4d' '
+	kill_p4d
+'
+
+test_done
diff --git a/t/t9806-git-p4-options.sh b/t/t9806-git-p4-options.sh
new file mode 100755
index 0000000..fa40cc8
--- /dev/null
+++ b/t/t9806-git-p4-options.sh
@@ -0,0 +1,167 @@
+#!/bin/sh
+
+test_description='git p4 options'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+	start_p4d
+'
+
+test_expect_success 'init depot' '
+	(
+		cd "$cli" &&
+		echo file1 >file1 &&
+		p4 add file1 &&
+		p4 submit -d "change 1" &&
+		echo file2 >file2 &&
+		p4 add file2 &&
+		p4 submit -d "change 2" &&
+		echo file3 >file3 &&
+		p4 add file3 &&
+		p4 submit -d "change 3"
+	)
+'
+
+test_expect_success 'clone no --git-dir' '
+	test_must_fail git p4 clone --git-dir=xx //depot
+'
+
+test_expect_success 'clone --branch' '
+	git p4 clone --branch=refs/remotes/p4/sb --dest="$git" //depot &&
+	test_when_finished cleanup_git &&
+	(
+		cd "$git" &&
+		git ls-files >files &&
+		test_line_count = 0 files &&
+		test_path_is_file .git/refs/remotes/p4/sb
+	)
+'
+
+test_expect_success 'clone --changesfile' '
+	test_when_finished "rm cf" &&
+	printf "1\n3\n" >cf &&
+	git p4 clone --changesfile="$TRASH_DIRECTORY/cf" --dest="$git" //depot &&
+	test_when_finished cleanup_git &&
+	(
+		cd "$git" &&
+		git log --oneline p4/master >lines &&
+		test_line_count = 2 lines
+		test_path_is_file file1 &&
+		test_path_is_missing file2 &&
+		test_path_is_file file3
+	)
+'
+
+test_expect_success 'clone --changesfile, @all' '
+	test_when_finished "rm cf" &&
+	printf "1\n3\n" >cf &&
+	test_must_fail git p4 clone --changesfile="$TRASH_DIRECTORY/cf" --dest="$git" //depot@all
+'
+
+# imports both master and p4/master in refs/heads
+# requires --import-local on sync to find p4 refs/heads
+# does not update master on sync, just p4/master
+test_expect_success 'clone/sync --import-local' '
+	git p4 clone --import-local --dest="$git" //depot@1,2 &&
+	test_when_finished cleanup_git &&
+	(
+		cd "$git" &&
+		git log --oneline refs/heads/master >lines &&
+		test_line_count = 2 lines &&
+		git log --oneline refs/heads/p4/master >lines &&
+		test_line_count = 2 lines &&
+		test_must_fail git p4 sync &&
+
+		git p4 sync --import-local &&
+		git log --oneline refs/heads/master >lines &&
+		test_line_count = 2 lines &&
+		git log --oneline refs/heads/p4/master >lines &&
+		test_line_count = 3 lines
+	)
+'
+
+test_expect_success 'clone --max-changes' '
+	git p4 clone --dest="$git" --max-changes 2 //depot@all &&
+	test_when_finished cleanup_git &&
+	(
+		cd "$git" &&
+		git log --oneline refs/heads/master >lines &&
+		test_line_count = 2 lines
+	)
+'
+
+test_expect_success 'clone --keep-path' '
+	(
+		cd "$cli" &&
+		mkdir -p sub/dir &&
+		echo f4 >sub/dir/f4 &&
+		p4 add sub/dir/f4 &&
+		p4 submit -d "change 4"
+	) &&
+	git p4 clone --dest="$git" --keep-path //depot/sub/dir@all &&
+	test_when_finished cleanup_git &&
+	(
+		cd "$git" &&
+		test_path_is_missing f4 &&
+		test_path_is_file sub/dir/f4
+	) &&
+	cleanup_git &&
+	git p4 clone --dest="$git" //depot/sub/dir@all &&
+	(
+		cd "$git" &&
+		test_path_is_file f4 &&
+		test_path_is_missing sub/dir/f4
+	)
+'
+
+# clone --use-client-spec must still specify a depot path
+# if given, it should rearrange files according to client spec
+# when it has view lines that match the depot path
+# XXX: should clone/sync just use the client spec exactly, rather
+# than needing depot paths?
+test_expect_success 'clone --use-client-spec' '
+	(
+		# big usage message
+		exec >/dev/null &&
+		test_must_fail git p4 clone --dest="$git" --use-client-spec
+	) &&
+	cli2=$(test-path-utils real_path "$TRASH_DIRECTORY/cli2") &&
+	mkdir -p "$cli2" &&
+	test_when_finished "rmdir \"$cli2\"" &&
+	(
+		cd "$cli2" &&
+		p4 client -i <<-EOF
+		Client: client2
+		Description: client2
+		Root: $cli2
+		View: //depot/sub/... //client2/bus/...
+		EOF
+	) &&
+	P4CLIENT=client2 &&
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" --use-client-spec //depot/... &&
+	(
+		cd "$git" &&
+		test_path_is_file bus/dir/f4 &&
+		test_path_is_missing file1
+	) &&
+	cleanup_git &&
+
+	# same thing again, this time with variable instead of option
+	(
+		cd "$git" &&
+		git init &&
+		git config git-p4.useClientSpec true &&
+		git p4 sync //depot/... &&
+		git checkout -b master p4/master &&
+		test_path_is_file bus/dir/f4 &&
+		test_path_is_missing file1
+	)
+'
+
+test_expect_success 'kill p4d' '
+	kill_p4d
+'
+
+test_done
diff --git a/t/t9807-git-p4-submit.sh b/t/t9807-git-p4-submit.sh
new file mode 100755
index 0000000..9394fd4
--- /dev/null
+++ b/t/t9807-git-p4-submit.sh
@@ -0,0 +1,344 @@
+#!/bin/sh
+
+test_description='git p4 submit'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+	start_p4d
+'
+
+test_expect_success 'init depot' '
+	(
+		cd "$cli" &&
+		echo file1 >file1 &&
+		p4 add file1 &&
+		p4 submit -d "change 1"
+	)
+'
+
+test_expect_success 'submit with no client dir' '
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot &&
+	(
+		cd "$git" &&
+		echo file2 >file2 &&
+		git add file2 &&
+		git commit -m "git commit 2" &&
+		rm -rf "$cli" &&
+		git config git-p4.skipSubmitEdit true &&
+		git p4 submit
+	) &&
+	(
+		cd "$cli" &&
+		test_path_is_file file1 &&
+		test_path_is_file file2
+	)
+'
+
+# make two commits, but tell it to apply only from HEAD^
+test_expect_success 'submit --origin' '
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot &&
+	(
+		cd "$git" &&
+		test_commit "file3" &&
+		test_commit "file4" &&
+		git config git-p4.skipSubmitEdit true &&
+		git p4 submit --origin=HEAD^
+	) &&
+	(
+		cd "$cli" &&
+		test_path_is_missing "file3.t" &&
+		test_path_is_file "file4.t"
+	)
+'
+
+test_expect_success 'submit with allowSubmit' '
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot &&
+	(
+		cd "$git" &&
+		test_commit "file5" &&
+		git config git-p4.skipSubmitEdit true &&
+		git config git-p4.allowSubmit "nobranch" &&
+		test_must_fail git p4 submit &&
+		git config git-p4.allowSubmit "nobranch,master" &&
+		git p4 submit
+	)
+'
+
+test_expect_success 'submit with master branch name from argv' '
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot &&
+	(
+		cd "$git" &&
+		test_commit "file6" &&
+		git config git-p4.skipSubmitEdit true &&
+		test_must_fail git p4 submit nobranch &&
+		git branch otherbranch &&
+		git reset --hard HEAD^ &&
+		test_commit "file7" &&
+		git p4 submit otherbranch
+	) &&
+	(
+		cd "$cli" &&
+		test_path_is_file "file6.t" &&
+		test_path_is_missing "file7.t"
+	)
+'
+
+#
+# Basic submit tests, the five handled cases
+#
+
+test_expect_success 'submit modify' '
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot &&
+	(
+		cd "$git" &&
+		git config git-p4.skipSubmitEdit true &&
+		echo line >>file1 &&
+		git add file1 &&
+		git commit -m file1 &&
+		git p4 submit
+	) &&
+	(
+		cd "$cli" &&
+		test_path_is_file file1 &&
+		test_line_count = 2 file1
+	)
+'
+
+test_expect_success 'submit add' '
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot &&
+	(
+		cd "$git" &&
+		git config git-p4.skipSubmitEdit true &&
+		echo file13 >file13 &&
+		git add file13 &&
+		git commit -m file13 &&
+		git p4 submit
+	) &&
+	(
+		cd "$cli" &&
+		test_path_is_file file13
+	)
+'
+
+test_expect_success 'submit delete' '
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot &&
+	(
+		cd "$git" &&
+		git config git-p4.skipSubmitEdit true &&
+		git rm file4.t &&
+		git commit -m "delete file4.t" &&
+		git p4 submit
+	) &&
+	(
+		cd "$cli" &&
+		test_path_is_missing file4.t
+	)
+'
+
+test_expect_success 'submit copy' '
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot &&
+	(
+		cd "$git" &&
+		git config git-p4.skipSubmitEdit true &&
+		git config git-p4.detectCopies true &&
+		git config git-p4.detectCopiesHarder true &&
+		cp file5.t file5.ta &&
+		git add file5.ta &&
+		git commit -m "copy to file5.ta" &&
+		git p4 submit
+	) &&
+	(
+		cd "$cli" &&
+		test_path_is_file file5.ta &&
+		test ! -w file5.ta
+	)
+'
+
+test_expect_success 'submit rename' '
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot &&
+	(
+		cd "$git" &&
+		git config git-p4.skipSubmitEdit true &&
+		git config git-p4.detectRenames true &&
+		git mv file6.t file6.ta &&
+		git commit -m "rename file6.t to file6.ta" &&
+		git p4 submit
+	) &&
+	(
+		cd "$cli" &&
+		test_path_is_missing file6.t &&
+		test_path_is_file file6.ta &&
+		test ! -w file6.ta
+	)
+'
+
+#
+# Converting git commit message to p4 change description, including
+# parsing out the optional Jobs: line.
+#
+test_expect_success 'simple one-line description' '
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot &&
+	(
+		cd "$git" &&
+		echo desc2 >desc2 &&
+		git add desc2 &&
+		cat >msg <<-EOF &&
+		One-line description line for desc2.
+		EOF
+		git commit -F - <msg &&
+		git config git-p4.skipSubmitEdit true &&
+		git p4 submit &&
+		change=$(p4 -G changes -m 1 //depot/... | \
+			 marshal_dump change) &&
+		# marshal_dump always adds a newline
+		p4 -G describe $change | marshal_dump desc | sed \$d >pmsg &&
+		test_cmp msg pmsg
+	)
+'
+
+test_expect_success 'description with odd formatting' '
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot &&
+	(
+		cd "$git" &&
+		echo desc3 >desc3 &&
+		git add desc3 &&
+		(
+			printf "subject line\n\n\tExtra tab\nline.\n\n" &&
+			printf "Description:\n\tBogus description marker\n\n" &&
+			# git commit eats trailing newlines; only use one
+			printf "Files:\n\tBogus descs marker\n"
+		) >msg &&
+		git commit -F - <msg &&
+		git config git-p4.skipSubmitEdit true &&
+		git p4 submit &&
+		change=$(p4 -G changes -m 1 //depot/... | \
+			 marshal_dump change) &&
+		# marshal_dump always adds a newline
+		p4 -G describe $change | marshal_dump desc | sed \$d >pmsg &&
+		test_cmp msg pmsg
+	)
+'
+
+make_job() {
+	name="$1" &&
+	tab="$(printf \\t)" &&
+	p4 job -o | \
+	sed -e "/^Job:/s/.*/Job: $name/" \
+	    -e "/^Description/{ n; s/.*/$tab job text/; }" | \
+	p4 job -i
+}
+
+test_expect_success 'description with Jobs section at end' '
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot &&
+	(
+		cd "$git" &&
+		echo desc4 >desc4 &&
+		git add desc4 &&
+		echo 6060842 >jobname &&
+		(
+			printf "subject line\n\n\tExtra tab\nline.\n\n" &&
+			printf "Files:\n\tBogus files marker\n" &&
+			printf "Junk: 3164175\n" &&
+			printf "Jobs: $(cat jobname)\n"
+		) >msg &&
+		git commit -F - <msg &&
+		git config git-p4.skipSubmitEdit true &&
+		# build a job
+		make_job $(cat jobname) &&
+		git p4 submit &&
+		change=$(p4 -G changes -m 1 //depot/... | \
+			 marshal_dump change) &&
+		# marshal_dump always adds a newline
+		p4 -G describe $change | marshal_dump desc | sed \$d >pmsg &&
+		# make sure Jobs line and all following is gone
+		sed "/^Jobs:/,\$d" msg >jmsg &&
+		test_cmp jmsg pmsg &&
+		# make sure p4 knows about job
+		p4 -G describe $change | marshal_dump job0 >job0 &&
+		test_cmp jobname job0
+	)
+'
+
+test_expect_success 'description with Jobs and values on separate lines' '
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot &&
+	(
+		cd "$git" &&
+		echo desc5 >desc5 &&
+		git add desc5 &&
+		echo PROJ-6060842 >jobname1 &&
+		echo PROJ-6060847 >jobname2 &&
+		(
+			printf "subject line\n\n\tExtra tab\nline.\n\n" &&
+			printf "Files:\n\tBogus files marker\n" &&
+			printf "Junk: 3164175\n" &&
+			printf "Jobs:\n" &&
+			printf "\t$(cat jobname1)\n" &&
+			printf "\t$(cat jobname2)\n"
+		) >msg &&
+		git commit -F - <msg &&
+		git config git-p4.skipSubmitEdit true &&
+		# build two jobs
+		make_job $(cat jobname1) &&
+		make_job $(cat jobname2) &&
+		git p4 submit &&
+		change=$(p4 -G changes -m 1 //depot/... | \
+			 marshal_dump change) &&
+		# marshal_dump always adds a newline
+		p4 -G describe $change | marshal_dump desc | sed \$d >pmsg &&
+		# make sure Jobs line and all following is gone
+		sed "/^Jobs:/,\$d" msg >jmsg &&
+		test_cmp jmsg pmsg &&
+		# make sure p4 knows about the two jobs
+		p4 -G describe $change >change &&
+		(
+			marshal_dump job0 <change &&
+			marshal_dump job1 <change
+		) | sort >jobs &&
+		cat jobname1 jobname2 | sort >expected &&
+		test_cmp expected jobs
+	)
+'
+
+test_expect_success 'description with Jobs section and bogus following text' '
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot &&
+	(
+		cd "$git" &&
+		echo desc6 >desc6 &&
+		git add desc6 &&
+		echo 6060843 >jobname &&
+		(
+			printf "subject line\n\n\tExtra tab\nline.\n\n" &&
+			printf "Files:\n\tBogus files marker\n" &&
+			printf "Junk: 3164175\n" &&
+			printf "Jobs: $(cat jobname)\n" &&
+			printf "MoreJunk: 3711\n"
+		) >msg &&
+		git commit -F - <msg &&
+		git config git-p4.skipSubmitEdit true &&
+		# build a job
+		make_job $(cat jobname) &&
+		test_must_fail git p4 submit 2>err &&
+		test_i18ngrep "Unknown field name" err
+	)
+'
+
+test_expect_success 'kill p4d' '
+	kill_p4d
+'
+
+test_done
diff --git a/t/t9808-git-p4-chdir.sh b/t/t9808-git-p4-chdir.sh
new file mode 100755
index 0000000..dc92e60
--- /dev/null
+++ b/t/t9808-git-p4-chdir.sh
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+test_description='git p4 relative chdir'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+	start_p4d
+'
+
+test_expect_success 'init depot' '
+	(
+		cd "$cli" &&
+		echo file1 >file1 &&
+		p4 add file1 &&
+		p4 submit -d "change 1"
+	)
+'
+
+# P4 reads from P4CONFIG file to find its server params, if the
+# environment variable is set
+test_expect_success 'P4CONFIG and absolute dir clone' '
+	printf "P4PORT=$P4PORT\nP4CLIENT=$P4CLIENT\n" >p4config &&
+	test_when_finished "rm p4config" &&
+	test_when_finished cleanup_git &&
+	(
+		P4CONFIG=p4config && export P4CONFIG &&
+		sane_unset P4PORT P4CLIENT &&
+		git p4 clone --verbose --dest="$git" //depot
+	)
+'
+
+# same thing, but with relative directory name, note missing $ on --dest
+test_expect_success 'P4CONFIG and relative dir clone' '
+	printf "P4PORT=$P4PORT\nP4CLIENT=$P4CLIENT\n" >p4config &&
+	test_when_finished "rm p4config" &&
+	test_when_finished cleanup_git &&
+	(
+		P4CONFIG=p4config && export P4CONFIG &&
+		sane_unset P4PORT P4CLIENT &&
+		git p4 clone --verbose --dest="git" //depot
+	)
+'
+
+test_expect_success 'kill p4d' '
+	kill_p4d
+'
+
+test_done
diff --git a/t/t9809-git-p4-client-view.sh b/t/t9809-git-p4-client-view.sh
new file mode 100755
index 0000000..7d993ef
--- /dev/null
+++ b/t/t9809-git-p4-client-view.sh
@@ -0,0 +1,854 @@
+#!/bin/sh
+
+test_description='git p4 client view'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+	start_p4d
+'
+
+#
+# Construct a client with this list of View lines
+#
+client_view() {
+	(
+		cat <<-EOF &&
+		Client: client
+		Description: client
+		Root: $cli
+		View:
+		EOF
+		for arg ; do
+			printf "\t$arg\n"
+		done
+	) | p4 client -i
+}
+
+#
+# Verify these files exist, exactly.  Caller creates
+# a list of files in file "files".
+#
+check_files_exist() {
+	ok=0 &&
+	num=$# &&
+	for arg ; do
+		test_path_is_file "$arg" &&
+		ok=$(($ok + 1))
+	done &&
+	test $ok -eq $num &&
+	test_line_count = $num files
+}
+
+#
+# Sync up the p4 client, make sure the given files (and only
+# those) exist.
+#
+client_verify() {
+	(
+		cd "$cli" &&
+		p4 sync &&
+		find . -type f ! -name files >files &&
+		check_files_exist "$@"
+	)
+}
+
+#
+# Make sure the named files, exactly, exist.
+#
+git_verify() {
+	(
+		cd "$git" &&
+		git ls-files >files &&
+		check_files_exist "$@"
+	)
+}
+
+# //depot
+#   - dir1
+#     - file11
+#     - file12
+#   - dir2
+#     - file21
+#     - file22
+init_depot() {
+	for d in 1 2 ; do
+		mkdir -p dir$d &&
+		for f in 1 2 ; do
+			echo dir$d/file$d$f >dir$d/file$d$f &&
+			p4 add dir$d/file$d$f &&
+			p4 submit -d "dir$d/file$d$f"
+		done
+	done &&
+	find . -type f ! -name files >files &&
+	check_files_exist dir1/file11 dir1/file12 \
+			  dir2/file21 dir2/file22
+}
+
+test_expect_success 'init depot' '
+	(
+		cd "$cli" &&
+		init_depot
+	)
+'
+
+# double % for printf
+test_expect_success 'unsupported view wildcard %%n' '
+	client_view "//depot/%%%%1/sub/... //client/sub/%%%%1/..." &&
+	test_when_finished cleanup_git &&
+	test_must_fail git p4 clone --use-client-spec --dest="$git" //depot
+'
+
+test_expect_success 'unsupported view wildcard *' '
+	client_view "//depot/*/bar/... //client/*/bar/..." &&
+	test_when_finished cleanup_git &&
+	test_must_fail git p4 clone --use-client-spec --dest="$git" //depot
+'
+
+test_expect_success 'wildcard ... only supported at end of spec 1' '
+	client_view "//depot/.../file11 //client/.../file11" &&
+	test_when_finished cleanup_git &&
+	test_must_fail git p4 clone --use-client-spec --dest="$git" //depot
+'
+
+test_expect_success 'wildcard ... only supported at end of spec 2' '
+	client_view "//depot/.../a/... //client/.../a/..." &&
+	test_when_finished cleanup_git &&
+	test_must_fail git p4 clone --use-client-spec --dest="$git" //depot
+'
+
+test_expect_success 'basic map' '
+	client_view "//depot/dir1/... //client/cli1/..." &&
+	files="cli1/file11 cli1/file12" &&
+	client_verify $files &&
+	test_when_finished cleanup_git &&
+	git p4 clone --use-client-spec --dest="$git" //depot &&
+	git_verify $files
+'
+
+test_expect_success 'client view with no mappings' '
+	client_view &&
+	client_verify &&
+	test_when_finished cleanup_git &&
+	git p4 clone --use-client-spec --dest="$git" //depot &&
+	git_verify
+'
+
+test_expect_success 'single file map' '
+	client_view "//depot/dir1/file11 //client/file11" &&
+	files="file11" &&
+	client_verify $files &&
+	test_when_finished cleanup_git &&
+	git p4 clone --use-client-spec --dest="$git" //depot &&
+	git_verify $files
+'
+
+test_expect_success 'later mapping takes precedence (entire repo)' '
+	client_view "//depot/dir1/... //client/cli1/..." \
+		    "//depot/... //client/cli2/..." &&
+	files="cli2/dir1/file11 cli2/dir1/file12
+	       cli2/dir2/file21 cli2/dir2/file22" &&
+	client_verify $files &&
+	test_when_finished cleanup_git &&
+	git p4 clone --use-client-spec --dest="$git" //depot &&
+	git_verify $files
+'
+
+test_expect_success 'later mapping takes precedence (partial repo)' '
+	client_view "//depot/dir1/... //client/..." \
+		    "//depot/dir2/... //client/..." &&
+	files="file21 file22" &&
+	client_verify $files &&
+	test_when_finished cleanup_git &&
+	git p4 clone --use-client-spec --dest="$git" //depot &&
+	git_verify $files
+'
+
+# Reading the view backwards,
+#   dir2 goes to cli12
+#   dir1 cannot go to cli12 since it was filled by dir2
+#   dir1 also does not go to cli3, since the second rule
+#     noticed that it matched, but was already filled
+test_expect_success 'depot path matching rejected client path' '
+	client_view "//depot/dir1/... //client/cli3/..." \
+		    "//depot/dir1/... //client/cli12/..." \
+		    "//depot/dir2/... //client/cli12/..." &&
+	files="cli12/file21 cli12/file22" &&
+	client_verify $files &&
+	test_when_finished cleanup_git &&
+	git p4 clone --use-client-spec --dest="$git" //depot &&
+	git_verify $files
+'
+
+# since both have the same //client/..., the exclusion
+# rule keeps everything out
+test_expect_success 'exclusion wildcard, client rhs same (odd)' '
+	client_view "//depot/... //client/..." \
+		    "-//depot/dir2/... //client/..." &&
+	client_verify &&
+	test_when_finished cleanup_git &&
+	git p4 clone --use-client-spec --dest="$git" //depot &&
+	git_verify
+'
+
+test_expect_success 'exclusion wildcard, client rhs different (normal)' '
+	client_view "//depot/... //client/..." \
+		    "-//depot/dir2/... //client/dir2/..." &&
+	files="dir1/file11 dir1/file12" &&
+	client_verify $files &&
+	test_when_finished cleanup_git &&
+	git p4 clone --use-client-spec --dest="$git" //depot &&
+	git_verify $files
+'
+
+test_expect_success 'exclusion single file' '
+	client_view "//depot/... //client/..." \
+		    "-//depot/dir2/file22 //client/file22" &&
+	files="dir1/file11 dir1/file12 dir2/file21" &&
+	client_verify $files &&
+	test_when_finished cleanup_git &&
+	git p4 clone --use-client-spec --dest="$git" //depot &&
+	git_verify $files
+'
+
+test_expect_success 'overlay wildcard' '
+	client_view "//depot/dir1/... //client/cli/..." \
+		    "+//depot/dir2/... //client/cli/...\n" &&
+	files="cli/file11 cli/file12 cli/file21 cli/file22" &&
+	client_verify $files &&
+	test_when_finished cleanup_git &&
+	git p4 clone --use-client-spec --dest="$git" //depot &&
+	git_verify $files
+'
+
+test_expect_success 'overlay single file' '
+	client_view "//depot/dir1/... //client/cli/..." \
+		    "+//depot/dir2/file21 //client/cli/file21" &&
+	files="cli/file11 cli/file12 cli/file21" &&
+	client_verify $files &&
+	test_when_finished cleanup_git &&
+	git p4 clone --use-client-spec --dest="$git" //depot &&
+	git_verify $files
+'
+
+test_expect_success 'exclusion with later inclusion' '
+	client_view "//depot/... //client/..." \
+		    "-//depot/dir2/... //client/dir2/..." \
+		    "//depot/dir2/... //client/dir2incl/..." &&
+	files="dir1/file11 dir1/file12 dir2incl/file21 dir2incl/file22" &&
+	client_verify $files &&
+	test_when_finished cleanup_git &&
+	git p4 clone --use-client-spec --dest="$git" //depot &&
+	git_verify $files
+'
+
+test_expect_success 'quotes on rhs only' '
+	client_view "//depot/dir1/... \"//client/cdir 1/...\"" &&
+	client_verify "cdir 1/file11" "cdir 1/file12" &&
+	test_when_finished cleanup_git &&
+	git p4 clone --use-client-spec --dest="$git" //depot &&
+	git_verify "cdir 1/file11" "cdir 1/file12"
+'
+
+#
+# Submit tests
+#
+
+# clone sets variable
+test_expect_success 'clone --use-client-spec sets useClientSpec' '
+	client_view "//depot/... //client/..." &&
+	test_when_finished cleanup_git &&
+	git p4 clone --use-client-spec --dest="$git" //depot &&
+	(
+		cd "$git" &&
+		git config --bool git-p4.useClientSpec >actual &&
+		echo true >true &&
+		test_cmp actual true
+	)
+'
+
+# clone just a subdir of the client spec
+test_expect_success 'subdir clone' '
+	client_view "//depot/... //client/..." &&
+	files="dir1/file11 dir1/file12 dir2/file21 dir2/file22" &&
+	client_verify $files &&
+	test_when_finished cleanup_git &&
+	git p4 clone --use-client-spec --dest="$git" //depot/dir1 &&
+	git_verify dir1/file11 dir1/file12
+'
+
+#
+# submit back, see what happens:  five cases
+#
+test_expect_success 'subdir clone, submit modify' '
+	client_view "//depot/... //client/..." &&
+	test_when_finished cleanup_git &&
+	git p4 clone --use-client-spec --dest="$git" //depot/dir1 &&
+	(
+		cd "$git" &&
+		git config git-p4.skipSubmitEdit true &&
+		echo line >>dir1/file12 &&
+		git add dir1/file12 &&
+		git commit -m dir1/file12 &&
+		git p4 submit
+	) &&
+	(
+		cd "$cli" &&
+		test_path_is_file dir1/file12 &&
+		test_line_count = 2 dir1/file12
+	)
+'
+
+test_expect_success 'subdir clone, submit add' '
+	client_view "//depot/... //client/..." &&
+	test_when_finished cleanup_git &&
+	git p4 clone --use-client-spec --dest="$git" //depot/dir1 &&
+	(
+		cd "$git" &&
+		git config git-p4.skipSubmitEdit true &&
+		echo file13 >dir1/file13 &&
+		git add dir1/file13 &&
+		git commit -m dir1/file13 &&
+		git p4 submit
+	) &&
+	(
+		cd "$cli" &&
+		test_path_is_file dir1/file13
+	)
+'
+
+test_expect_success 'subdir clone, submit delete' '
+	client_view "//depot/... //client/..." &&
+	test_when_finished cleanup_git &&
+	git p4 clone --use-client-spec --dest="$git" //depot/dir1 &&
+	(
+		cd "$git" &&
+		git config git-p4.skipSubmitEdit true &&
+		git rm dir1/file12 &&
+		git commit -m "delete dir1/file12" &&
+		git p4 submit
+	) &&
+	(
+		cd "$cli" &&
+		test_path_is_missing dir1/file12
+	)
+'
+
+test_expect_success 'subdir clone, submit copy' '
+	client_view "//depot/... //client/..." &&
+	test_when_finished cleanup_git &&
+	git p4 clone --use-client-spec --dest="$git" //depot/dir1 &&
+	(
+		cd "$git" &&
+		git config git-p4.skipSubmitEdit true &&
+		git config git-p4.detectCopies true &&
+		cp dir1/file11 dir1/file11a &&
+		git add dir1/file11a &&
+		git commit -m "copy to dir1/file11a" &&
+		git p4 submit
+	) &&
+	(
+		cd "$cli" &&
+		test_path_is_file dir1/file11a &&
+		test ! -w dir1/file11a
+	)
+'
+
+test_expect_success 'subdir clone, submit rename' '
+	client_view "//depot/... //client/..." &&
+	test_when_finished cleanup_git &&
+	git p4 clone --use-client-spec --dest="$git" //depot/dir1 &&
+	(
+		cd "$git" &&
+		git config git-p4.skipSubmitEdit true &&
+		git config git-p4.detectRenames true &&
+		git mv dir1/file13 dir1/file13a &&
+		git commit -m "rename dir1/file13 to dir1/file13a" &&
+		git p4 submit
+	) &&
+	(
+		cd "$cli" &&
+		test_path_is_missing dir1/file13 &&
+		test_path_is_file dir1/file13a &&
+		test ! -w dir1/file13a
+	)
+'
+
+# see t9800 for the non-client-spec case, and the rest of the wildcard tests
+test_expect_success 'wildcard files submit back to p4, client-spec case' '
+	client_view "//depot/... //client/..." &&
+	test_when_finished cleanup_git &&
+	git p4 clone --use-client-spec --dest="$git" //depot/dir1 &&
+	(
+		cd "$git" &&
+		echo git-wild-hash >dir1/git-wild#hash &&
+		echo git-wild-star >dir1/git-wild\*star &&
+		echo git-wild-at >dir1/git-wild@at &&
+		echo git-wild-percent >dir1/git-wild%percent &&
+		git add dir1/git-wild* &&
+		git commit -m "add some wildcard filenames" &&
+		git config git-p4.skipSubmitEditCheck true &&
+		git p4 submit
+	) &&
+	(
+		cd "$cli" &&
+		test_path_is_file dir1/git-wild#hash &&
+		test_path_is_file dir1/git-wild\*star &&
+		test_path_is_file dir1/git-wild@at &&
+		test_path_is_file dir1/git-wild%percent
+	) &&
+	(
+		# delete these carefully, cannot just do "p4 delete"
+		# on files with wildcards; but git-p4 knows how
+		cd "$git" &&
+		git rm dir1/git-wild* &&
+		git commit -m "clean up the wildcards" &&
+		git p4 submit
+	)
+'
+
+test_expect_success 'reinit depot' '
+	(
+		cd "$cli" &&
+		rm files &&
+		p4 delete */* &&
+		p4 submit -d "delete all files" &&
+		init_depot
+	)
+'
+
+#
+# What happens when two files of the same name are overlayed together?
+# The last-listed file should take preference.
+#
+# //depot
+#   - dir1
+#     - file11
+#     - file12
+#     - filecollide
+#   - dir2
+#     - file21
+#     - file22
+#     - filecollide
+#
+test_expect_success 'overlay collision setup' '
+	client_view "//depot/... //client/..." &&
+	(
+		cd "$cli" &&
+		p4 sync &&
+		echo dir1/filecollide >dir1/filecollide &&
+		p4 add dir1/filecollide &&
+		p4 submit -d dir1/filecollide &&
+		echo dir2/filecollide >dir2/filecollide &&
+		p4 add dir2/filecollide &&
+		p4 submit -d dir2/filecollide
+	)
+'
+
+test_expect_success 'overlay collision 1 to 2' '
+	client_view "//depot/dir1/... //client/..." \
+		    "+//depot/dir2/... //client/..." &&
+	files="file11 file12 file21 file22 filecollide" &&
+	echo dir2/filecollide >actual &&
+	client_verify $files &&
+	test_cmp actual "$cli"/filecollide &&
+	test_when_finished cleanup_git &&
+	git p4 clone --use-client-spec --dest="$git" //depot &&
+	git_verify $files &&
+	test_cmp actual "$git"/filecollide
+'
+
+test_expect_failure 'overlay collision 2 to 1' '
+	client_view "//depot/dir2/... //client/..." \
+		    "+//depot/dir1/... //client/..." &&
+	files="file11 file12 file21 file22 filecollide" &&
+	echo dir1/filecollide >actual &&
+	client_verify $files &&
+	test_cmp actual "$cli"/filecollide &&
+	test_when_finished cleanup_git &&
+	git p4 clone --use-client-spec --dest="$git" //depot &&
+	git_verify $files &&
+	test_cmp actual "$git"/filecollide
+'
+
+test_expect_success 'overlay collision delete 2' '
+	client_view "//depot/... //client/..." &&
+	(
+		cd "$cli" &&
+		p4 sync &&
+		p4 delete dir2/filecollide &&
+		p4 submit -d "remove dir2/filecollide"
+	)
+'
+
+# no filecollide, got deleted with dir2
+test_expect_failure 'overlay collision 1 to 2, but 2 deleted' '
+	client_view "//depot/dir1/... //client/..." \
+		    "+//depot/dir2/... //client/..." &&
+	files="file11 file12 file21 file22" &&
+	client_verify $files &&
+	test_when_finished cleanup_git &&
+	git p4 clone --use-client-spec --dest="$git" //depot &&
+	git_verify $files
+'
+
+test_expect_success 'overlay collision update 1' '
+	client_view "//depot/dir1/... //client/dir1/..." &&
+	(
+		cd "$cli" &&
+		p4 sync &&
+		p4 open dir1/filecollide &&
+		echo dir1/filecollide update >dir1/filecollide &&
+		p4 submit -d "update dir1/filecollide"
+	)
+'
+
+# still no filecollide, dir2 still wins with the deletion even though the
+# change to dir1 is more recent
+test_expect_failure 'overlay collision 1 to 2, but 2 deleted, then 1 updated' '
+	client_view "//depot/dir1/... //client/..." \
+		    "+//depot/dir2/... //client/..." &&
+	files="file11 file12 file21 file22" &&
+	client_verify $files &&
+	test_when_finished cleanup_git &&
+	git p4 clone --use-client-spec --dest="$git" //depot &&
+	git_verify $files
+'
+
+test_expect_success 'overlay collision delete filecollides' '
+	client_view "//depot/... //client/..." &&
+	(
+		cd "$cli" &&
+		p4 sync &&
+		p4 delete dir1/filecollide dir2/filecollide &&
+		p4 submit -d "remove filecollides"
+	)
+'
+
+#
+# Overlays as part of sync, rather than initial checkout:
+#   1.  add a file in dir1
+#   2.  sync to include it
+#   3.  add same file in dir2
+#   4.  sync, make sure content switches as dir2 has priority
+#   5.  add another file in dir1
+#   6.  sync
+#   7.  add/delete same file in dir2
+#   8.  sync, make sure it disappears, again dir2 wins
+#   9.  cleanup
+#
+# //depot
+#   - dir1
+#     - file11
+#     - file12
+#     - colA
+#     - colB
+#   - dir2
+#     - file21
+#     - file22
+#     - colA
+#     - colB
+#
+test_expect_success 'overlay sync: add colA in dir1' '
+	client_view "//depot/dir1/... //client/dir1/..." &&
+	(
+		cd "$cli" &&
+		p4 sync &&
+		echo dir1/colA >dir1/colA &&
+		p4 add dir1/colA &&
+		p4 submit -d dir1/colA
+	)
+'
+
+test_expect_success 'overlay sync: initial git checkout' '
+	client_view "//depot/dir1/... //client/..." \
+		    "+//depot/dir2/... //client/..." &&
+	files="file11 file12 file21 file22 colA" &&
+	echo dir1/colA >actual &&
+	client_verify $files &&
+	test_cmp actual "$cli"/colA &&
+	git p4 clone --use-client-spec --dest="$git" //depot &&
+	git_verify $files &&
+	test_cmp actual "$git"/colA
+'
+
+test_expect_success 'overlay sync: add colA in dir2' '
+	client_view "//depot/dir2/... //client/dir2/..." &&
+	(
+		cd "$cli" &&
+		p4 sync &&
+		echo dir2/colA >dir2/colA &&
+		p4 add dir2/colA &&
+		p4 submit -d dir2/colA
+	)
+'
+
+test_expect_success 'overlay sync: colA content switch' '
+	client_view "//depot/dir1/... //client/..." \
+		    "+//depot/dir2/... //client/..." &&
+	files="file11 file12 file21 file22 colA" &&
+	echo dir2/colA >actual &&
+	client_verify $files &&
+	test_cmp actual "$cli"/colA &&
+	(
+		cd "$git" &&
+		git p4 sync --use-client-spec &&
+		git merge --ff-only p4/master
+	) &&
+	git_verify $files &&
+	test_cmp actual "$git"/colA
+'
+
+test_expect_success 'overlay sync: add colB in dir1' '
+	client_view "//depot/dir1/... //client/dir1/..." &&
+	(
+		cd "$cli" &&
+		p4 sync &&
+		echo dir1/colB >dir1/colB &&
+		p4 add dir1/colB &&
+		p4 submit -d dir1/colB
+	)
+'
+
+test_expect_success 'overlay sync: colB appears' '
+	client_view "//depot/dir1/... //client/..." \
+		    "+//depot/dir2/... //client/..." &&
+	files="file11 file12 file21 file22 colA colB" &&
+	echo dir1/colB >actual &&
+	client_verify $files &&
+	test_cmp actual "$cli"/colB &&
+	(
+		cd "$git" &&
+		git p4 sync --use-client-spec &&
+		git merge --ff-only p4/master
+	) &&
+	git_verify $files &&
+	test_cmp actual "$git"/colB
+'
+
+test_expect_success 'overlay sync: add/delete colB in dir2' '
+	client_view "//depot/dir2/... //client/dir2/..." &&
+	(
+		cd "$cli" &&
+		p4 sync &&
+		echo dir2/colB >dir2/colB &&
+		p4 add dir2/colB &&
+		p4 submit -d dir2/colB &&
+		p4 delete dir2/colB &&
+		p4 submit -d "delete dir2/colB"
+	)
+'
+
+test_expect_success 'overlay sync: colB disappears' '
+	client_view "//depot/dir1/... //client/..." \
+		    "+//depot/dir2/... //client/..." &&
+	files="file11 file12 file21 file22 colA" &&
+	client_verify $files &&
+	test_when_finished cleanup_git &&
+	(
+		cd "$git" &&
+		git p4 sync --use-client-spec &&
+		git merge --ff-only p4/master
+	) &&
+	git_verify $files
+'
+
+test_expect_success 'overlay sync: cleanup' '
+	client_view "//depot/... //client/..." &&
+	(
+		cd "$cli" &&
+		p4 sync &&
+		p4 delete dir1/colA dir2/colA dir1/colB &&
+		p4 submit -d "remove overlay sync files"
+	)
+'
+
+#
+# Overlay tests again, but swapped so dir1 has priority.
+#   1.  add a file in dir1
+#   2.  sync to include it
+#   3.  add same file in dir2
+#   4.  sync, make sure content does not switch
+#   5.  add another file in dir1
+#   6.  sync
+#   7.  add/delete same file in dir2
+#   8.  sync, make sure it is still there
+#   9.  cleanup
+#
+# //depot
+#   - dir1
+#     - file11
+#     - file12
+#     - colA
+#     - colB
+#   - dir2
+#     - file21
+#     - file22
+#     - colA
+#     - colB
+#
+test_expect_success 'overlay sync swap: add colA in dir1' '
+	client_view "//depot/dir1/... //client/dir1/..." &&
+	(
+		cd "$cli" &&
+		p4 sync &&
+		echo dir1/colA >dir1/colA &&
+		p4 add dir1/colA &&
+		p4 submit -d dir1/colA
+	)
+'
+
+test_expect_success 'overlay sync swap: initial git checkout' '
+	client_view "//depot/dir2/... //client/..." \
+		    "+//depot/dir1/... //client/..." &&
+	files="file11 file12 file21 file22 colA" &&
+	echo dir1/colA >actual &&
+	client_verify $files &&
+	test_cmp actual "$cli"/colA &&
+	git p4 clone --use-client-spec --dest="$git" //depot &&
+	git_verify $files &&
+	test_cmp actual "$git"/colA
+'
+
+test_expect_success 'overlay sync swap: add colA in dir2' '
+	client_view "//depot/dir2/... //client/dir2/..." &&
+	(
+		cd "$cli" &&
+		p4 sync &&
+		echo dir2/colA >dir2/colA &&
+		p4 add dir2/colA &&
+		p4 submit -d dir2/colA
+	)
+'
+
+test_expect_failure 'overlay sync swap: colA no content switch' '
+	client_view "//depot/dir2/... //client/..." \
+		    "+//depot/dir1/... //client/..." &&
+	files="file11 file12 file21 file22 colA" &&
+	echo dir1/colA >actual &&
+	client_verify $files &&
+	test_cmp actual "$cli"/colA &&
+	(
+		cd "$git" &&
+		git p4 sync --use-client-spec &&
+		git merge --ff-only p4/master
+	) &&
+	git_verify $files &&
+	test_cmp actual "$git"/colA
+'
+
+test_expect_success 'overlay sync swap: add colB in dir1' '
+	client_view "//depot/dir1/... //client/dir1/..." &&
+	(
+		cd "$cli" &&
+		p4 sync &&
+		echo dir1/colB >dir1/colB &&
+		p4 add dir1/colB &&
+		p4 submit -d dir1/colB
+	)
+'
+
+test_expect_success 'overlay sync swap: colB appears' '
+	client_view "//depot/dir2/... //client/..." \
+		    "+//depot/dir1/... //client/..." &&
+	files="file11 file12 file21 file22 colA colB" &&
+	echo dir1/colB >actual &&
+	client_verify $files &&
+	test_cmp actual "$cli"/colB &&
+	(
+		cd "$git" &&
+		git p4 sync --use-client-spec &&
+		git merge --ff-only p4/master
+	) &&
+	git_verify $files &&
+	test_cmp actual "$git"/colB
+'
+
+test_expect_success 'overlay sync swap: add/delete colB in dir2' '
+	client_view "//depot/dir2/... //client/dir2/..." &&
+	(
+		cd "$cli" &&
+		p4 sync &&
+		echo dir2/colB >dir2/colB &&
+		p4 add dir2/colB &&
+		p4 submit -d dir2/colB &&
+		p4 delete dir2/colB &&
+		p4 submit -d "delete dir2/colB"
+	)
+'
+
+test_expect_failure 'overlay sync swap: colB no change' '
+	client_view "//depot/dir2/... //client/..." \
+		    "+//depot/dir1/... //client/..." &&
+	files="file11 file12 file21 file22 colA colB" &&
+	echo dir1/colB >actual &&
+	client_verify $files &&
+	test_cmp actual "$cli"/colB &&
+	test_when_finished cleanup_git &&
+	(
+		cd "$git" &&
+		git p4 sync --use-client-spec &&
+		git merge --ff-only p4/master
+	) &&
+	git_verify $files &&
+	test_cmp actual "$cli"/colB
+'
+
+test_expect_success 'overlay sync swap: cleanup' '
+	client_view "//depot/... //client/..." &&
+	(
+		cd "$cli" &&
+		p4 sync &&
+		p4 delete dir1/colA dir2/colA dir1/colB &&
+		p4 submit -d "remove overlay sync files"
+	)
+'
+
+#
+# Rename directories to test quoting in depot-side mappings
+# //depot
+#    - "dir 1"
+#       - file11
+#       - file12
+#    - "dir 2"
+#       - file21
+#       - file22
+#
+test_expect_success 'rename files to introduce spaces' '
+	client_view "//depot/... //client/..." &&
+	client_verify dir1/file11 dir1/file12 \
+		      dir2/file21 dir2/file22 &&
+	(
+		cd "$cli" &&
+		p4 open dir1/... &&
+		p4 move dir1/... "dir 1"/... &&
+		p4 open dir2/... &&
+		p4 move dir2/... "dir 2"/... &&
+		p4 submit -d "rename with spaces"
+	) &&
+	client_verify "dir 1/file11" "dir 1/file12" \
+		      "dir 2/file21" "dir 2/file22"
+'
+
+test_expect_success 'quotes on lhs only' '
+	client_view "\"//depot/dir 1/...\" //client/cdir1/..." &&
+	files="cdir1/file11 cdir1/file12" &&
+	client_verify $files &&
+	test_when_finished cleanup_git &&
+	git p4 clone --use-client-spec --dest="$git" //depot &&
+	client_verify $files
+'
+
+test_expect_success 'quotes on both sides' '
+	client_view "\"//depot/dir 1/...\" \"//client/cdir 1/...\"" &&
+	client_verify "cdir 1/file11" "cdir 1/file12" &&
+	test_when_finished cleanup_git &&
+	git p4 clone --use-client-spec --dest="$git" //depot &&
+	git_verify "cdir 1/file11" "cdir 1/file12"
+'
+
+test_expect_success 'kill p4d' '
+	kill_p4d
+'
+
+test_done
diff --git a/t/t9810-git-p4-rcs.sh b/t/t9810-git-p4-rcs.sh
new file mode 100755
index 0000000..e9daa9c
--- /dev/null
+++ b/t/t9810-git-p4-rcs.sh
@@ -0,0 +1,388 @@
+#!/bin/sh
+
+test_description='git p4 rcs keywords'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+	start_p4d
+'
+
+#
+# Make one file with keyword lines at the top, and
+# enough plain text to be able to test modifications
+# far away from the keywords.
+#
+test_expect_success 'init depot' '
+	(
+		cd "$cli" &&
+		cat <<-\EOF >filek &&
+		$Id$
+		/* $Revision$ */
+		# $Change$
+		line4
+		line5
+		line6
+		line7
+		line8
+		EOF
+		cp filek fileko &&
+		sed -i "s/Revision/Revision: do not scrub me/" fileko
+		cp fileko file_text &&
+		sed -i "s/Id/Id: do not scrub me/" file_text
+		p4 add -t text+k filek &&
+		p4 submit -d "filek" &&
+		p4 add -t text+ko fileko &&
+		p4 submit -d "fileko" &&
+		p4 add -t text file_text &&
+		p4 submit -d "file_text"
+	)
+'
+
+#
+# Generate these in a function to make it easy to use single quote marks.
+#
+write_scrub_scripts () {
+	cat >"$TRASH_DIRECTORY/scrub_k.py" <<-\EOF &&
+	import re, sys
+	sys.stdout.write(re.sub(r'(?i)\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\$', r'$\1$', sys.stdin.read()))
+	EOF
+	cat >"$TRASH_DIRECTORY/scrub_ko.py" <<-\EOF
+	import re, sys
+	sys.stdout.write(re.sub(r'(?i)\$(Id|Header):[^$]*\$', r'$\1$', sys.stdin.read()))
+	EOF
+}
+
+test_expect_success 'scrub scripts' '
+	write_scrub_scripts
+'
+
+#
+# Compare $cli/file to its scrubbed version, should be different.
+# Compare scrubbed $cli/file to $git/file, should be same.
+#
+scrub_k_check () {
+	file="$1" &&
+	scrub="$TRASH_DIRECTORY/$file" &&
+	"$PYTHON_PATH" "$TRASH_DIRECTORY/scrub_k.py" <"$git/$file" >"$scrub" &&
+	! test_cmp "$cli/$file" "$scrub" &&
+	test_cmp "$git/$file" "$scrub" &&
+	rm "$scrub"
+}
+scrub_ko_check () {
+	file="$1" &&
+	scrub="$TRASH_DIRECTORY/$file" &&
+	"$PYTHON_PATH" "$TRASH_DIRECTORY/scrub_ko.py" <"$git/$file" >"$scrub" &&
+	! test_cmp "$cli/$file" "$scrub" &&
+	test_cmp "$git/$file" "$scrub" &&
+	rm "$scrub"
+}
+
+#
+# Modify far away from keywords.  If no RCS lines show up
+# in the diff, there is no conflict.
+#
+test_expect_success 'edit far away from RCS lines' '
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot &&
+	(
+		cd "$git" &&
+		git config git-p4.skipSubmitEdit true &&
+		sed -i "s/^line7/line7 edit/" filek &&
+		git commit -m "filek line7 edit" filek &&
+		git p4 submit &&
+		scrub_k_check filek
+	)
+'
+
+#
+# Modify near the keywords.  This will require RCS scrubbing.
+#
+test_expect_success 'edit near RCS lines' '
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot &&
+	(
+		cd "$git" &&
+		git config git-p4.skipSubmitEdit true &&
+		git config git-p4.attemptRCSCleanup true &&
+		sed -i "s/^line4/line4 edit/" filek &&
+		git commit -m "filek line4 edit" filek &&
+		git p4 submit &&
+		scrub_k_check filek
+	)
+'
+
+#
+# Modify the keywords themselves.  This also will require RCS scrubbing.
+#
+test_expect_success 'edit keyword lines' '
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot &&
+	(
+		cd "$git" &&
+		git config git-p4.skipSubmitEdit true &&
+		git config git-p4.attemptRCSCleanup true &&
+		sed -i "/Revision/d" filek &&
+		git commit -m "filek remove Revision line" filek &&
+		git p4 submit &&
+		scrub_k_check filek
+	)
+'
+
+#
+# Scrubbing text+ko files should not alter all keywords, just Id, Header.
+#
+test_expect_success 'scrub ko files differently' '
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot &&
+	(
+		cd "$git" &&
+		git config git-p4.skipSubmitEdit true &&
+		git config git-p4.attemptRCSCleanup true &&
+		sed -i "s/^line4/line4 edit/" fileko &&
+		git commit -m "fileko line4 edit" fileko &&
+		git p4 submit &&
+		scrub_ko_check fileko &&
+		! scrub_k_check fileko
+	)
+'
+
+# hack; git p4 submit should do it on its own
+test_expect_success 'cleanup after failure' '
+	(
+		cd "$cli" &&
+		p4 revert ...
+	)
+'
+
+#
+# Do not scrub anything but +k or +ko files.  Sneak a change into
+# the cli file so that submit will get a conflict.  Make sure that
+# scrubbing doesn't make a mess of things.
+#
+# Assumes that git-p4 exits leaving the p4 file open, with the
+# conflict-generating patch unapplied.
+#
+# This might happen only if the git repo is behind the p4 repo at
+# submit time, and there is a conflict.
+#
+test_expect_success 'do not scrub plain text' '
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot &&
+	(
+		cd "$git" &&
+		git config git-p4.skipSubmitEdit true &&
+		git config git-p4.attemptRCSCleanup true &&
+		sed -i "s/^line4/line4 edit/" file_text &&
+		git commit -m "file_text line4 edit" file_text &&
+		(
+			cd "$cli" &&
+			p4 open file_text &&
+			sed -i "s/^line5/line5 p4 edit/" file_text &&
+			p4 submit -d "file5 p4 edit"
+		) &&
+		! git p4 submit &&
+		(
+			# exepct something like:
+			#    file_text - file(s) not opened on this client
+			# but not copious diff output
+			cd "$cli" &&
+			p4 diff file_text >wc &&
+			test_line_count = 1 wc
+		)
+	)
+'
+
+# hack; git p4 submit should do it on its own
+test_expect_success 'cleanup after failure 2' '
+	(
+		cd "$cli" &&
+		p4 revert ...
+	)
+'
+
+create_kw_file () {
+	cat <<\EOF >"$1"
+/* A file
+	Id: $Id$
+	Revision: $Revision$
+	File: $File$
+ */
+int main(int argc, const char **argv) {
+	return 0;
+}
+EOF
+}
+
+test_expect_success 'add kwfile' '
+	(
+		cd "$cli" &&
+		echo file1 >file1 &&
+		p4 add file1 &&
+		p4 submit -d "file 1" &&
+		create_kw_file kwfile1.c &&
+		p4 add kwfile1.c &&
+		p4 submit -d "Add rcw kw file" kwfile1.c
+	)
+'
+
+p4_append_to_file () {
+	f="$1" &&
+	p4 edit -t ktext "$f" &&
+	echo "/* $(date) */" >>"$f" &&
+	p4 submit -d "appending a line in p4"
+}
+
+# Create some files with RCS keywords. If they get modified
+# elsewhere then the version number gets bumped which then
+# results in a merge conflict if we touch the RCS kw lines,
+# even though the change itself would otherwise apply cleanly.
+test_expect_success 'cope with rcs keyword expansion damage' '
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot &&
+	(
+		cd "$git" &&
+		git config git-p4.skipSubmitEdit true &&
+		git config git-p4.attemptRCSCleanup true &&
+		(cd "$cli" && p4_append_to_file kwfile1.c) &&
+		old_lines=$(wc -l <kwfile1.c) &&
+		"$PERL_PATH" -n -i -e "print unless m/Revision:/" kwfile1.c &&
+		new_lines=$(wc -l <kwfile1.c) &&
+		test $new_lines = $(($old_lines - 1)) &&
+
+		git add kwfile1.c &&
+		git commit -m "Zap an RCS kw line" &&
+		git p4 submit &&
+		git p4 rebase &&
+		git diff p4/master &&
+		git p4 commit &&
+		echo "try modifying in both" &&
+		cd "$cli" &&
+		p4 edit kwfile1.c &&
+		echo "line from p4" >>kwfile1.c &&
+		p4 submit -d "add a line in p4" kwfile1.c &&
+		cd "$git" &&
+		echo "line from git at the top" | cat - kwfile1.c >kwfile1.c.new &&
+		mv kwfile1.c.new kwfile1.c &&
+		git commit -m "Add line in git at the top" kwfile1.c &&
+		git p4 rebase &&
+		git p4 submit
+	)
+'
+
+test_expect_success 'cope with rcs keyword file deletion' '
+	test_when_finished cleanup_git &&
+	(
+		cd "$cli" &&
+		echo "\$Revision\$" >kwdelfile.c &&
+		p4 add -t ktext kwdelfile.c &&
+		p4 submit -d "Add file to be deleted" &&
+		cat kwdelfile.c &&
+		grep 1 kwdelfile.c
+	) &&
+	git p4 clone --dest="$git" //depot &&
+	(
+		cd "$git" &&
+		grep Revision kwdelfile.c &&
+		git rm -f kwdelfile.c &&
+		git commit -m "Delete a file containing RCS keywords" &&
+		git config git-p4.skipSubmitEdit true &&
+		git config git-p4.attemptRCSCleanup true &&
+		git p4 submit
+	) &&
+	(
+		cd "$cli" &&
+		p4 sync &&
+		! test -f kwdelfile.c
+	)
+'
+
+# If you add keywords in git of the form $Header$ then everything should
+# work fine without any special handling.
+test_expect_success 'Add keywords in git which match the default p4 values' '
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot &&
+	(
+		cd "$git" &&
+		echo "NewKW: \$Revision\$" >>kwfile1.c &&
+		git add kwfile1.c &&
+		git commit -m "Adding RCS keywords in git" &&
+		git config git-p4.skipSubmitEdit true &&
+		git config git-p4.attemptRCSCleanup true &&
+		git p4 submit
+	) &&
+	(
+		cd "$cli" &&
+		p4 sync &&
+		test -f kwfile1.c &&
+		grep "NewKW.*Revision.*[0-9]" kwfile1.c
+
+	)
+'
+
+# If you add keywords in git of the form $Header:#1$ then things will fail
+# unless git-p4 takes steps to scrub the *git* commit.
+#
+test_expect_failure 'Add keywords in git which do not match the default p4 values' '
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot &&
+	(
+		cd "$git" &&
+		echo "NewKW2: \$Revision:1\$" >>kwfile1.c &&
+		git add kwfile1.c &&
+		git commit -m "Adding RCS keywords in git" &&
+		git config git-p4.skipSubmitEdit true &&
+		git config git-p4.attemptRCSCleanup true &&
+		git p4 submit
+	) &&
+	(
+		cd "$cli" &&
+		p4 sync &&
+		grep "NewKW2.*Revision.*[0-9]" kwfile1.c
+
+	)
+'
+
+# Check that the existing merge conflict handling still works.
+# Modify kwfile1.c in git, and delete in p4. We should be able
+# to skip the git commit.
+#
+test_expect_success 'merge conflict handling still works' '
+	test_when_finished cleanup_git &&
+	(
+		cd "$cli" &&
+		echo "Hello:\$Id\$" >merge2.c &&
+		echo "World" >>merge2.c &&
+		p4 add -t ktext merge2.c &&
+		p4 submit -d "add merge test file"
+	) &&
+	git p4 clone --dest="$git" //depot &&
+	(
+		cd "$git" &&
+		sed -e "/Hello/d" merge2.c >merge2.c.tmp &&
+		mv merge2.c.tmp merge2.c &&
+		git add merge2.c &&
+		git commit -m "Modifying merge2.c"
+	) &&
+	(
+		cd "$cli" &&
+		p4 delete merge2.c &&
+		p4 submit -d "remove merge test file"
+	) &&
+	(
+		cd "$git" &&
+		test -f merge2.c &&
+		git config git-p4.skipSubmitEdit true &&
+		git config git-p4.attemptRCSCleanup true &&
+		!(echo "s" | git p4 submit) &&
+		git rebase --skip &&
+		! test -f merge2.c
+	)
+'
+
+
+test_expect_success 'kill p4d' '
+	kill_p4d
+'
+
+test_done
diff --git a/t/t9811-git-p4-label-import.sh b/t/t9811-git-p4-label-import.sh
new file mode 100755
index 0000000..095238f
--- /dev/null
+++ b/t/t9811-git-p4-label-import.sh
@@ -0,0 +1,222 @@
+#!/bin/sh
+
+test_description='git p4 label tests'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+	start_p4d
+'
+
+# Basic p4 label import tests.
+#
+test_expect_success 'basic p4 labels' '
+	test_when_finished cleanup_git &&
+	(
+		cd "$cli" &&
+		mkdir -p main &&
+
+		echo f1 >main/f1 &&
+		p4 add main/f1 &&
+		p4 submit -d "main/f1" &&
+
+		echo f2 >main/f2 &&
+		p4 add main/f2 &&
+		p4 submit -d "main/f2" &&
+
+		echo f3 >main/file_with_\$metachar &&
+		p4 add main/file_with_\$metachar &&
+		p4 submit -d "file with metachar" &&
+
+		p4 tag -l TAG_F1_ONLY main/f1 &&
+		p4 tag -l TAG_WITH\$_SHELL_CHAR main/... &&
+		p4 tag -l this_tag_will_be\ skipped main/... &&
+
+		echo f4 >main/f4 &&
+		p4 add main/f4 &&
+		p4 submit -d "main/f4" &&
+
+		p4 label -i <<-EOF &&
+		Label: TAG_LONG_LABEL
+		Description:
+		   A Label first line
+		   A Label second line
+		View:	//depot/...
+		EOF
+
+		p4 tag -l TAG_LONG_LABEL ... &&
+
+		p4 labels ... &&
+
+		git p4 clone --dest="$git" //depot@all &&
+		cd "$git" &&
+		git config git-p4.labelImportRegexp ".*TAG.*" &&
+		git p4 sync --import-labels --verbose &&
+
+		git tag &&
+		git tag >taglist &&
+		test_line_count = 3 taglist &&
+
+		cd main &&
+		git checkout TAG_F1_ONLY &&
+		! test -f f2 &&
+		git checkout TAG_WITH\$_SHELL_CHAR &&
+		test -f f1 && test -f f2 && test -f file_with_\$metachar &&
+
+		git show TAG_LONG_LABEL | grep -q "A Label second line"
+	)
+'
+# Test some label corner cases:
+#
+# - two tags on the same file; both should be available
+# - a tag that is only on one file; this kind of tag
+#   cannot be imported (at least not easily).
+
+test_expect_success 'two labels on the same changelist' '
+	test_when_finished cleanup_git &&
+	(
+		cd "$cli" &&
+		mkdir -p main &&
+
+		p4 edit main/f1 main/f2 &&
+		echo "hello world" >main/f1 &&
+		echo "not in the tag" >main/f2 &&
+		p4 submit -d "main/f[12]: testing two labels" &&
+
+		p4 tag -l TAG_F1_1 main/... &&
+		p4 tag -l TAG_F1_2 main/... &&
+
+		p4 labels ... &&
+
+		git p4 clone --dest="$git" //depot@all &&
+		cd "$git" &&
+		git p4 sync --import-labels &&
+
+		git tag | grep TAG_F1 &&
+		git tag | grep -q TAG_F1_1 &&
+		git tag | grep -q TAG_F1_2 &&
+
+		cd main &&
+
+		git checkout TAG_F1_1 &&
+		ls &&
+		test -f f1 &&
+
+		git checkout TAG_F1_2 &&
+		ls &&
+		test -f f1
+	)
+'
+
+# Export some git tags to p4
+test_expect_success 'export git tags to p4' '
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot@all &&
+	(
+		cd "$git" &&
+		git tag -m "A tag created in git:xyzzy" GIT_TAG_1 &&
+		echo "hello world" >main/f10 &&
+		git add main/f10 &&
+		git commit -m "Adding file for export test" &&
+		git config git-p4.skipSubmitEdit true &&
+		git p4 submit &&
+		git tag -m "Another git tag" GIT_TAG_2 &&
+		git tag LIGHTWEIGHT_TAG &&
+		git p4 rebase --import-labels --verbose &&
+		git p4 submit --export-labels --verbose
+	) &&
+	(
+		cd "$cli" &&
+		p4 sync ... &&
+		p4 labels ... | grep GIT_TAG_1 &&
+		p4 labels ... | grep GIT_TAG_2 &&
+		p4 labels ... | grep LIGHTWEIGHT_TAG &&
+		p4 label -o GIT_TAG_1 | grep "tag created in git:xyzzy" &&
+		p4 sync ...@GIT_TAG_1 &&
+		! test -f main/f10
+		p4 sync ...@GIT_TAG_2 &&
+		test -f main/f10
+	)
+'
+
+# Export a tag from git where an affected file is deleted later on
+# Need to create git tags after rebase, since only then can the
+# git commits be mapped to p4 changelists.
+test_expect_success 'export git tags to p4 with deletion' '
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot@all &&
+	(
+		cd "$git" &&
+		git p4 sync --import-labels &&
+		echo "deleted file" >main/deleted_file &&
+		git add main/deleted_file &&
+		git commit -m "create deleted file" &&
+		git rm main/deleted_file &&
+		echo "new file" >main/f11 &&
+		git add main/f11 &&
+		git commit -m "delete the deleted file" &&
+		git config git-p4.skipSubmitEdit true &&
+		git p4 submit &&
+		git p4 rebase --import-labels --verbose &&
+		git tag -m "tag on deleted file" GIT_TAG_ON_DELETED HEAD~1 &&
+		git tag -m "tag after deletion" GIT_TAG_AFTER_DELETION HEAD &&
+		git p4 submit --export-labels --verbose
+	) &&
+	(
+		cd "$cli" &&
+		p4 sync ... &&
+		p4 sync ...@GIT_TAG_ON_DELETED &&
+		test -f main/deleted_file &&
+		p4 sync ...@GIT_TAG_AFTER_DELETION &&
+		! test -f main/deleted_file &&
+		echo "checking label contents" &&
+		p4 label -o GIT_TAG_ON_DELETED | grep "tag on deleted file"
+	)
+'
+
+# Create a tag in git that cannot be exported to p4
+test_expect_success 'tag that cannot be exported' '
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot@all &&
+	(
+		cd "$git" &&
+		git checkout -b a_branch &&
+		echo "hello" >main/f12 &&
+		git add main/f12 &&
+		git commit -m "adding f12" &&
+		git tag -m "tag on a_branch" GIT_TAG_ON_A_BRANCH &&
+		git checkout master &&
+		git p4 submit --export-labels
+	) &&
+	(
+		cd "$cli" &&
+		p4 sync ... &&
+		!(p4 labels | grep GIT_TAG_ON_A_BRANCH)
+	)
+'
+
+test_expect_success 'use git config to enable import/export of tags' '
+	git p4 clone --verbose --dest="$git" //depot@all &&
+	(
+		cd "$git" &&
+		git config git-p4.exportLabels true &&
+		git config git-p4.importLabels true &&
+		git tag CFG_A_GIT_TAG &&
+		git p4 rebase --verbose &&
+		git p4 submit --verbose &&
+		git tag &&
+		git tag | grep TAG_F1_1
+	) &&
+	(
+		cd "$cli" &&
+		p4 labels &&
+		p4 labels | grep CFG_A_GIT_TAG
+	)
+'
+
+
+test_expect_success 'kill p4d' '
+	kill_p4d
+'
+
+test_done
diff --git a/t/t9812-git-p4-wildcards.sh b/t/t9812-git-p4-wildcards.sh
new file mode 100755
index 0000000..143d413
--- /dev/null
+++ b/t/t9812-git-p4-wildcards.sh
@@ -0,0 +1,147 @@
+#!/bin/sh
+
+test_description='git p4 wildcards'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+	start_p4d
+'
+
+test_expect_success 'add p4 files with wildcards in the names' '
+	(
+		cd "$cli" &&
+		printf "file2\nhas\nsome\nrandom\ntext\n" >file2 &&
+		p4 add file2 &&
+		echo file-wild-hash >file-wild#hash &&
+		echo file-wild-star >file-wild\*star &&
+		echo file-wild-at >file-wild@at &&
+		echo file-wild-percent >file-wild%percent &&
+		p4 add -f file-wild* &&
+		p4 submit -d "file wildcards"
+	)
+'
+
+test_expect_success 'wildcard files git p4 clone' '
+	git p4 clone --dest="$git" //depot &&
+	test_when_finished cleanup_git &&
+	(
+		cd "$git" &&
+		test -f file-wild#hash &&
+		test -f file-wild\*star &&
+		test -f file-wild@at &&
+		test -f file-wild%percent
+	)
+'
+
+test_expect_success 'wildcard files submit back to p4, add' '
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot &&
+	(
+		cd "$git" &&
+		echo git-wild-hash >git-wild#hash &&
+		echo git-wild-star >git-wild\*star &&
+		echo git-wild-at >git-wild@at &&
+		echo git-wild-percent >git-wild%percent &&
+		git add git-wild* &&
+		git commit -m "add some wildcard filenames" &&
+		git config git-p4.skipSubmitEdit true &&
+		git p4 submit
+	) &&
+	(
+		cd "$cli" &&
+		test_path_is_file git-wild#hash &&
+		test_path_is_file git-wild\*star &&
+		test_path_is_file git-wild@at &&
+		test_path_is_file git-wild%percent
+	)
+'
+
+test_expect_success 'wildcard files submit back to p4, modify' '
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot &&
+	(
+		cd "$git" &&
+		echo new-line >>git-wild#hash &&
+		echo new-line >>git-wild\*star &&
+		echo new-line >>git-wild@at &&
+		echo new-line >>git-wild%percent &&
+		git add git-wild* &&
+		git commit -m "modify the wildcard files" &&
+		git config git-p4.skipSubmitEdit true &&
+		git p4 submit
+	) &&
+	(
+		cd "$cli" &&
+		test_line_count = 2 git-wild#hash &&
+		test_line_count = 2 git-wild\*star &&
+		test_line_count = 2 git-wild@at &&
+		test_line_count = 2 git-wild%percent
+	)
+'
+
+test_expect_success 'wildcard files submit back to p4, copy' '
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot &&
+	(
+		cd "$git" &&
+		cp file2 git-wild-cp#hash &&
+		git add git-wild-cp#hash &&
+		cp git-wild\*star file-wild-3 &&
+		git add file-wild-3 &&
+		git commit -m "wildcard copies" &&
+		git config git-p4.detectCopies true &&
+		git config git-p4.detectCopiesHarder true &&
+		git config git-p4.skipSubmitEdit true &&
+		git p4 submit
+	) &&
+	(
+		cd "$cli" &&
+		test_path_is_file git-wild-cp#hash &&
+		test_path_is_file file-wild-3
+	)
+'
+
+test_expect_success 'wildcard files submit back to p4, rename' '
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot &&
+	(
+		cd "$git" &&
+		git mv git-wild@at file-wild-4 &&
+		git mv file-wild-3 git-wild-cp%percent &&
+		git commit -m "wildcard renames" &&
+		git config git-p4.detectRenames true &&
+		git config git-p4.skipSubmitEdit true &&
+		git p4 submit
+	) &&
+	(
+		cd "$cli" &&
+		test_path_is_missing git-wild@at &&
+		test_path_is_file git-wild-cp%percent
+	)
+'
+
+test_expect_success 'wildcard files submit back to p4, delete' '
+	test_when_finished cleanup_git &&
+	git p4 clone --dest="$git" //depot &&
+	(
+		cd "$git" &&
+		git rm git-wild* &&
+		git commit -m "delete the wildcard files" &&
+		git config git-p4.skipSubmitEdit true &&
+		git p4 submit
+	) &&
+	(
+		cd "$cli" &&
+		test_path_is_missing git-wild#hash &&
+		test_path_is_missing git-wild\*star &&
+		test_path_is_missing git-wild@at &&
+		test_path_is_missing git-wild%percent
+	)
+'
+
+test_expect_success 'kill p4d' '
+	kill_p4d
+'
+
+test_done
diff --git a/t/t9813-git-p4-preserve-users.sh b/t/t9813-git-p4-preserve-users.sh
new file mode 100755
index 0000000..f2e85e5
--- /dev/null
+++ b/t/t9813-git-p4-preserve-users.sh
@@ -0,0 +1,153 @@
+#!/bin/sh
+
+test_description='git p4 preserve users'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+	start_p4d
+'
+
+test_expect_success 'create files' '
+	(
+		cd "$cli" &&
+		p4 client -o | sed "/LineEnd/s/:.*/:unix/" | p4 client -i &&
+		echo file1 >file1 &&
+		echo file2 >file2 &&
+		p4 add file1 file2 &&
+		p4 submit -d "add files"
+	)
+'
+
+p4_add_user() {
+	name=$1 fullname=$2 &&
+	p4 user -f -i <<-EOF &&
+	User: $name
+	Email: $name@localhost
+	FullName: $fullname
+	EOF
+	p4 passwd -P secret $name
+}
+
+p4_grant_admin() {
+	name=$1 &&
+	{
+		p4 protect -o &&
+		echo "    admin user $name * //depot/..."
+	} | p4 protect -i
+}
+
+p4_check_commit_author() {
+	file=$1 user=$2 &&
+	p4 changes -m 1 //depot/$file | grep -q $user
+}
+
+make_change_by_user() {
+	file=$1 name=$2 email=$3 &&
+	echo "username: a change by $name" >>"$file" &&
+	git add "$file" &&
+	git commit --author "$name <$email>" -m "a change by $name"
+}
+
+# Test username support, submitting as user 'alice'
+test_expect_success 'preserve users' '
+	p4_add_user alice Alice &&
+	p4_add_user bob Bob &&
+	p4_grant_admin alice &&
+	git p4 clone --dest="$git" //depot &&
+	test_when_finished cleanup_git &&
+	(
+		cd "$git" &&
+		echo "username: a change by alice" >>file1 &&
+		echo "username: a change by bob" >>file2 &&
+		git commit --author "Alice <alice@localhost>" -m "a change by alice" file1 &&
+		git commit --author "Bob <bob@localhost>" -m "a change by bob" file2 &&
+		git config git-p4.skipSubmitEditCheck true &&
+		P4EDITOR=touch P4USER=alice P4PASSWD=secret git p4 commit --preserve-user &&
+		p4_check_commit_author file1 alice &&
+		p4_check_commit_author file2 bob
+	)
+'
+
+# Test username support, submitting as bob, who lacks admin rights. Should
+# not submit change to p4 (git diff should show deltas).
+test_expect_success 'refuse to preserve users without perms' '
+	git p4 clone --dest="$git" //depot &&
+	test_when_finished cleanup_git &&
+	(
+		cd "$git" &&
+		git config git-p4.skipSubmitEditCheck true &&
+		echo "username-noperms: a change by alice" >>file1 &&
+		git commit --author "Alice <alice@localhost>" -m "perms: a change by alice" file1 &&
+		P4EDITOR=touch P4USER=bob P4PASSWD=secret &&
+		export P4EDITOR P4USER P4PASSWD &&
+		test_must_fail git p4 commit --preserve-user &&
+		! git diff --exit-code HEAD..p4/master
+	)
+'
+
+# What happens with unknown author? Without allowMissingP4Users it should fail.
+test_expect_success 'preserve user where author is unknown to p4' '
+	git p4 clone --dest="$git" //depot &&
+	test_when_finished cleanup_git &&
+	(
+		cd "$git" &&
+		git config git-p4.skipSubmitEditCheck true &&
+		echo "username-bob: a change by bob" >>file1 &&
+		git commit --author "Bob <bob@localhost>" -m "preserve: a change by bob" file1 &&
+		echo "username-unknown: a change by charlie" >>file1 &&
+		git commit --author "Charlie <charlie@localhost>" -m "preserve: a change by charlie" file1 &&
+		P4EDITOR=touch P4USER=alice P4PASSWD=secret &&
+		export P4EDITOR P4USER P4PASSWD &&
+		test_must_fail git p4 commit --preserve-user &&
+		! git diff --exit-code HEAD..p4/master &&
+
+		echo "$0: repeat with allowMissingP4Users enabled" &&
+		git config git-p4.allowMissingP4Users true &&
+		git config git-p4.preserveUser true &&
+		git p4 commit &&
+		git diff --exit-code HEAD..p4/master &&
+		p4_check_commit_author file1 alice
+	)
+'
+
+# If we're *not* using --preserve-user, git-p4 should warn if we're submitting
+# changes that are not all ours.
+# Test: user in p4 and user unknown to p4.
+# Test: warning disabled and user is the same.
+test_expect_success 'not preserving user with mixed authorship' '
+	git p4 clone --dest="$git" //depot &&
+	test_when_finished cleanup_git &&
+	(
+		cd "$git" &&
+		git config git-p4.skipSubmitEditCheck true &&
+		p4_add_user derek Derek &&
+
+		make_change_by_user usernamefile3 Derek derek@localhost &&
+		P4EDITOR=cat P4USER=alice P4PASSWD=secret &&
+		export P4EDITOR P4USER P4PASSWD &&
+		git p4 commit |\
+		grep "git author derek@localhost does not match" &&
+
+		make_change_by_user usernamefile3 Charlie charlie@localhost &&
+		git p4 commit |\
+		grep "git author charlie@localhost does not match" &&
+
+		make_change_by_user usernamefile3 alice alice@localhost &&
+		git p4 commit |\
+		test_must_fail grep "git author.*does not match" &&
+
+		git config git-p4.skipUserNameCheck true &&
+		make_change_by_user usernamefile3 Charlie charlie@localhost &&
+		git p4 commit |\
+		test_must_fail grep "git author.*does not match" &&
+
+		p4_check_commit_author usernamefile3 alice
+	)
+'
+
+test_expect_success 'kill p4d' '
+	kill_p4d
+'
+
+test_done
diff --git a/t/t9814-git-p4-rename.sh b/t/t9814-git-p4-rename.sh
new file mode 100755
index 0000000..3bf1224
--- /dev/null
+++ b/t/t9814-git-p4-rename.sh
@@ -0,0 +1,206 @@
+#!/bin/sh
+
+test_description='git p4 rename'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+	start_p4d
+'
+
+# We rely on this behavior to detect for p4 move availability.
+test_expect_success 'p4 help unknown returns 1' '
+	(
+		cd "$cli" &&
+		(
+			p4 help client >errs 2>&1
+			echo $? >retval
+		)
+		echo 0 >expected &&
+		test_cmp expected retval &&
+		rm retval &&
+		(
+			p4 help nosuchcommand >errs 2>&1
+			echo $? >retval
+		)
+		echo 1 >expected &&
+		test_cmp expected retval &&
+		rm retval
+	)
+'
+
+test_expect_success 'create files' '
+	(
+		cd "$cli" &&
+		p4 client -o | sed "/LineEnd/s/:.*/:unix/" | p4 client -i &&
+		cat >file1 <<-EOF &&
+		A large block of text
+		in file1 that will generate
+		enough context so that rename
+		and copy detection will find
+		something interesting to do.
+		EOF
+		cat >file2 <<-EOF &&
+		/*
+		 * This blob looks a bit
+		 * different.
+		 */
+		int main(int argc, char **argv)
+		{
+			char text[200];
+
+			strcpy(text, "copy/rename this");
+			printf("text is %s\n", text);
+			return 0;
+		}
+		EOF
+		p4 add file1 file2 &&
+		p4 submit -d "add files"
+	)
+'
+
+# Rename a file and confirm that rename is not detected in P4.
+# Rename the new file again with detectRenames option enabled and confirm that
+# this is detected in P4.
+# Rename the new file again adding an extra line, configure a big threshold in
+# detectRenames and confirm that rename is not detected in P4.
+# Repeat, this time with a smaller threshold and confirm that the rename is
+# detected in P4.
+test_expect_success 'detect renames' '
+	git p4 clone --dest="$git" //depot@all &&
+	test_when_finished cleanup_git &&
+	(
+		cd "$git" &&
+		git config git-p4.skipSubmitEdit true &&
+
+		git mv file1 file4 &&
+		git commit -a -m "Rename file1 to file4" &&
+		git diff-tree -r -M HEAD &&
+		git p4 submit &&
+		p4 filelog //depot/file4 >filelog &&
+		! grep " from //depot" filelog &&
+
+		git mv file4 file5 &&
+		git commit -a -m "Rename file4 to file5" &&
+		git diff-tree -r -M HEAD &&
+		git config git-p4.detectRenames true &&
+		git p4 submit &&
+		p4 filelog //depot/file5 >filelog &&
+		grep " from //depot/file4" filelog &&
+
+		git mv file5 file6 &&
+		echo update >>file6 &&
+		git add file6 &&
+		git commit -a -m "Rename file5 to file6 with changes" &&
+		git diff-tree -r -M HEAD &&
+		level=$(git diff-tree -r -M HEAD | sed 1d | cut -f1 | cut -d" " -f5 | sed "s/R0*//") &&
+		test -n "$level" && test "$level" -gt 0 && test "$level" -lt 98 &&
+		git config git-p4.detectRenames $(($level + 2)) &&
+		git p4 submit &&
+		p4 filelog //depot/file6 >filelog &&
+		! grep " from //depot" filelog &&
+
+		git mv file6 file7 &&
+		echo update >>file7 &&
+		git add file7 &&
+		git commit -a -m "Rename file6 to file7 with changes" &&
+		git diff-tree -r -M HEAD &&
+		level=$(git diff-tree -r -M HEAD | sed 1d | cut -f1 | cut -d" " -f5 | sed "s/R0*//") &&
+		test -n "$level" && test "$level" -gt 2 && test "$level" -lt 100 &&
+		git config git-p4.detectRenames $(($level - 2)) &&
+		git p4 submit &&
+		p4 filelog //depot/file7 >filelog &&
+		grep " from //depot/file6" filelog
+	)
+'
+
+# Copy a file and confirm that copy is not detected in P4.
+# Copy a file with detectCopies option enabled and confirm that copy is not
+# detected in P4.
+# Modify and copy a file with detectCopies option enabled and confirm that copy
+# is detected in P4.
+# Copy a file with detectCopies and detectCopiesHarder options enabled and
+# confirm that copy is detected in P4.
+# Modify and copy a file, configure a bigger threshold in detectCopies and
+# confirm that copy is not detected in P4.
+# Modify and copy a file, configure a smaller threshold in detectCopies and
+# confirm that copy is detected in P4.
+test_expect_success 'detect copies' '
+	git p4 clone --dest="$git" //depot@all &&
+	test_when_finished cleanup_git &&
+	(
+		cd "$git" &&
+		git config git-p4.skipSubmitEdit true &&
+
+		cp file2 file8 &&
+		git add file8 &&
+		git commit -a -m "Copy file2 to file8" &&
+		git diff-tree -r -C HEAD &&
+		git p4 submit &&
+		p4 filelog //depot/file8 &&
+		p4 filelog //depot/file8 | test_must_fail grep -q "branch from" &&
+
+		cp file2 file9 &&
+		git add file9 &&
+		git commit -a -m "Copy file2 to file9" &&
+		git diff-tree -r -C HEAD &&
+		git config git-p4.detectCopies true &&
+		git p4 submit &&
+		p4 filelog //depot/file9 &&
+		p4 filelog //depot/file9 | test_must_fail grep -q "branch from" &&
+
+		echo "file2" >>file2 &&
+		cp file2 file10 &&
+		git add file2 file10 &&
+		git commit -a -m "Modify and copy file2 to file10" &&
+		git diff-tree -r -C HEAD &&
+		git p4 submit &&
+		p4 filelog //depot/file10 &&
+		p4 filelog //depot/file10 | grep -q "branch from //depot/file" &&
+
+		cp file2 file11 &&
+		git add file11 &&
+		git commit -a -m "Copy file2 to file11" &&
+		git diff-tree -r -C --find-copies-harder HEAD &&
+		src=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) &&
+		test "$src" = file10 &&
+		git config git-p4.detectCopiesHarder true &&
+		git p4 submit &&
+		p4 filelog //depot/file11 &&
+		p4 filelog //depot/file11 | grep -q "branch from //depot/file" &&
+
+		cp file2 file12 &&
+		echo "some text" >>file12 &&
+		git add file12 &&
+		git commit -a -m "Copy file2 to file12 with changes" &&
+		git diff-tree -r -C --find-copies-harder HEAD &&
+		level=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f1 | cut -d" " -f5 | sed "s/C0*//") &&
+		test -n "$level" && test "$level" -gt 0 && test "$level" -lt 98 &&
+		src=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) &&
+		test "$src" = file10 -o "$src" = file11 &&
+		git config git-p4.detectCopies $(($level + 2)) &&
+		git p4 submit &&
+		p4 filelog //depot/file12 &&
+		p4 filelog //depot/file12 | test_must_fail grep -q "branch from" &&
+
+		cp file2 file13 &&
+		echo "different text" >>file13 &&
+		git add file13 &&
+		git commit -a -m "Copy file2 to file13 with changes" &&
+		git diff-tree -r -C --find-copies-harder HEAD &&
+		level=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f1 | cut -d" " -f5 | sed "s/C0*//") &&
+		test -n "$level" && test "$level" -gt 2 && test "$level" -lt 100 &&
+		src=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) &&
+		test "$src" = file10 -o "$src" = file11 -o "$src" = file12 &&
+		git config git-p4.detectCopies $(($level - 2)) &&
+		git p4 submit &&
+		p4 filelog //depot/file13 &&
+		p4 filelog //depot/file13 | grep -q "branch from //depot/file"
+	)
+'
+
+test_expect_success 'kill p4d' '
+	kill_p4d
+'
+
+test_done
diff --git a/t/t9901-git-web--browse.sh b/t/t9901-git-web--browse.sh
new file mode 100755
index 0000000..b0a6bad
--- /dev/null
+++ b/t/t9901-git-web--browse.sh
@@ -0,0 +1,63 @@
+#!/bin/sh
+#
+
+test_description='git web--browse basic tests
+
+This test checks that git web--browse can handle various valid URLs.'
+
+. ./test-lib.sh
+
+test_web_browse () {
+	# browser=$1 url=$2
+	git web--browse --browser="$1" "$2" >actual &&
+	tr -d '\015' <actual >text &&
+	test_cmp expect text
+}
+
+test_expect_success \
+	'URL with an ampersand in it' '
+	echo http://example.com/foo\&bar >expect &&
+	git config browser.custom.cmd echo &&
+	test_web_browse custom http://example.com/foo\&bar
+'
+
+test_expect_success \
+	'URL with a semi-colon in it' '
+	echo http://example.com/foo\;bar >expect &&
+	git config browser.custom.cmd echo &&
+	test_web_browse custom http://example.com/foo\;bar
+'
+
+test_expect_success \
+	'URL with a hash in it' '
+	echo http://example.com/foo#bar >expect &&
+	git config browser.custom.cmd echo &&
+	test_web_browse custom http://example.com/foo#bar
+'
+
+test_expect_success \
+	'browser paths are properly quoted' '
+	echo fake: http://example.com/foo >expect &&
+	cat >"fake browser" <<-\EOF &&
+	#!/bin/sh
+	echo fake: "$@"
+	EOF
+	chmod +x "fake browser" &&
+	git config browser.w3m.path "`pwd`/fake browser" &&
+	test_web_browse w3m http://example.com/foo
+'
+
+test_expect_success \
+	'browser command allows arbitrary shell code' '
+	echo "arg: http://example.com/foo" >expect &&
+	git config browser.custom.cmd "
+		f() {
+			for i in \"\$@\"; do
+				echo arg: \$i
+			done
+		}
+		f" &&
+	test_web_browse custom http://example.com/foo
+'
+
+test_done
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
new file mode 100755
index 0000000..92d7eb4
--- /dev/null
+++ b/t/t9902-completion.sh
@@ -0,0 +1,231 @@
+#!/bin/sh
+#
+# Copyright (c) 2012 Felipe Contreras
+#
+
+test_description='test bash completion'
+
+. ./lib-bash.sh
+
+complete ()
+{
+	# do nothing
+	return 0
+}
+
+. "$GIT_BUILD_DIR/contrib/completion/git-completion.bash"
+
+# We don't need this function to actually join words or do anything special.
+# Also, it's cleaner to avoid touching bash's internal completion variables.
+# So let's override it with a minimal version for testing purposes.
+_get_comp_words_by_ref ()
+{
+	while [ $# -gt 0 ]; do
+		case "$1" in
+		cur)
+			cur=${_words[_cword]}
+			;;
+		prev)
+			prev=${_words[_cword-1]}
+			;;
+		words)
+			words=("${_words[@]}")
+			;;
+		cword)
+			cword=$_cword
+			;;
+		esac
+		shift
+	done
+}
+
+print_comp ()
+{
+	local IFS=$'\n'
+	echo "${COMPREPLY[*]}" > out
+}
+
+run_completion ()
+{
+	local -a COMPREPLY _words
+	local _cword
+	_words=( $1 )
+	(( _cword = ${#_words[@]} - 1 ))
+	__git_wrap__git_main && print_comp
+}
+
+test_completion ()
+{
+	test $# -gt 1 && echo "$2" > expected
+	run_completion "$@" &&
+	test_cmp expected out
+}
+
+newline=$'\n'
+
+test_expect_success '__gitcomp - trailing space - options' '
+	sed -e "s/Z$//" >expected <<-\EOF &&
+	--reuse-message=Z
+	--reedit-message=Z
+	--reset-author Z
+	EOF
+	(
+		local -a COMPREPLY &&
+		cur="--re" &&
+		__gitcomp "--dry-run --reuse-message= --reedit-message=
+				--reset-author" &&
+		IFS="$newline" &&
+		echo "${COMPREPLY[*]}" > out
+	) &&
+	test_cmp expected out
+'
+
+test_expect_success '__gitcomp - trailing space - config keys' '
+	sed -e "s/Z$//" >expected <<-\EOF &&
+	branch.Z
+	branch.autosetupmerge Z
+	branch.autosetuprebase Z
+	browser.Z
+	EOF
+	(
+		local -a COMPREPLY &&
+		cur="br" &&
+		__gitcomp "branch. branch.autosetupmerge
+				branch.autosetuprebase browser." &&
+		IFS="$newline" &&
+		echo "${COMPREPLY[*]}" > out
+	) &&
+	test_cmp expected out
+'
+
+test_expect_success '__gitcomp - option parameter' '
+	sed -e "s/Z$//" >expected <<-\EOF &&
+	recursive Z
+	resolve Z
+	EOF
+	(
+		local -a COMPREPLY &&
+		cur="--strategy=re" &&
+		__gitcomp "octopus ours recursive resolve subtree
+			" "" "re" &&
+		IFS="$newline" &&
+		echo "${COMPREPLY[*]}" > out
+	) &&
+	test_cmp expected out
+'
+
+test_expect_success '__gitcomp - prefix' '
+	sed -e "s/Z$//" >expected <<-\EOF &&
+	branch.maint.merge Z
+	branch.maint.mergeoptions Z
+	EOF
+	(
+		local -a COMPREPLY &&
+		cur="branch.me" &&
+		__gitcomp "remote merge mergeoptions rebase
+			" "branch.maint." "me" &&
+		IFS="$newline" &&
+		echo "${COMPREPLY[*]}" > out
+	) &&
+	test_cmp expected out
+'
+
+test_expect_success '__gitcomp - suffix' '
+	sed -e "s/Z$//" >expected <<-\EOF &&
+	branch.master.Z
+	branch.maint.Z
+	EOF
+	(
+		local -a COMPREPLY &&
+		cur="branch.me" &&
+		__gitcomp "master maint next pu
+			" "branch." "ma" "." &&
+		IFS="$newline" &&
+		echo "${COMPREPLY[*]}" > out
+	) &&
+	test_cmp expected out
+'
+
+test_expect_success 'basic' '
+	run_completion "git \"\"" &&
+	# built-in
+	grep -q "^add \$" out &&
+	# script
+	grep -q "^filter-branch \$" out &&
+	# plumbing
+	! grep -q "^ls-files \$" out &&
+
+	run_completion "git f" &&
+	! grep -q -v "^f" out
+'
+
+test_expect_success 'double dash "git" itself' '
+	sed -e "s/Z$//" >expected <<-\EOF &&
+	--paginate Z
+	--no-pager Z
+	--git-dir=
+	--bare Z
+	--version Z
+	--exec-path Z
+	--exec-path=
+	--html-path Z
+	--info-path Z
+	--work-tree=
+	--namespace=
+	--no-replace-objects Z
+	--help Z
+	EOF
+	test_completion "git --"
+'
+
+test_expect_success 'double dash "git checkout"' '
+	sed -e "s/Z$//" >expected <<-\EOF &&
+	--quiet Z
+	--ours Z
+	--theirs Z
+	--track Z
+	--no-track Z
+	--merge Z
+	--conflict=
+	--orphan Z
+	--patch Z
+	EOF
+	test_completion "git checkout --"
+'
+
+test_expect_success 'general options' '
+	test_completion "git --ver" "--version " &&
+	test_completion "git --hel" "--help " &&
+	sed -e "s/Z$//" >expected <<-\EOF &&
+	--exec-path Z
+	--exec-path=
+	EOF
+	test_completion "git --exe" &&
+	test_completion "git --htm" "--html-path " &&
+	test_completion "git --pag" "--paginate " &&
+	test_completion "git --no-p" "--no-pager " &&
+	test_completion "git --git" "--git-dir=" &&
+	test_completion "git --wor" "--work-tree=" &&
+	test_completion "git --nam" "--namespace=" &&
+	test_completion "git --bar" "--bare " &&
+	test_completion "git --inf" "--info-path " &&
+	test_completion "git --no-r" "--no-replace-objects "
+'
+
+test_expect_success 'general options plus command' '
+	test_completion "git --version check" "checkout " &&
+	test_completion "git --paginate check" "checkout " &&
+	test_completion "git --git-dir=foo check" "checkout " &&
+	test_completion "git --bare check" "checkout " &&
+	test_completion "git --help des" "describe " &&
+	test_completion "git --exec-path=foo check" "checkout " &&
+	test_completion "git --html-path check" "checkout " &&
+	test_completion "git --no-pager check" "checkout " &&
+	test_completion "git --work-tree=foo check" "checkout " &&
+	test_completion "git --namespace=foo check" "checkout " &&
+	test_completion "git --paginate check" "checkout " &&
+	test_completion "git --info-path check" "checkout " &&
+	test_completion "git --no-replace-objects check" "checkout "
+'
+
+test_done
diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
new file mode 100755
index 0000000..f17c1f8
--- /dev/null
+++ b/t/t9903-bash-prompt.sh
@@ -0,0 +1,456 @@
+#!/bin/sh
+#
+# Copyright (c) 2012 SZEDER Gábor
+#
+
+test_description='test git-specific bash prompt functions'
+
+. ./lib-bash.sh
+
+. "$GIT_BUILD_DIR/contrib/completion/git-prompt.sh"
+
+actual="$TRASH_DIRECTORY/actual"
+
+test_expect_success 'setup for prompt tests' '
+	mkdir -p subdir/subsubdir &&
+	git init otherrepo &&
+	echo 1 > file &&
+	git add file &&
+	test_tick &&
+	git commit -m initial &&
+	git tag -a -m msg1 t1 &&
+	git checkout -b b1 &&
+	echo 2 > file &&
+	git commit -m "second b1" file &&
+	echo 3 > file &&
+	git commit -m "third b1" file &&
+	git tag -a -m msg2 t2 &&
+	git checkout -b b2 master &&
+	echo 0 > file &&
+	git commit -m "second b2" file &&
+	git checkout master
+'
+
+test_expect_success 'gitdir - from command line (through $__git_dir)' '
+	echo "$TRASH_DIRECTORY/otherrepo/.git" > expected &&
+	(
+		__git_dir="$TRASH_DIRECTORY/otherrepo/.git" &&
+		__gitdir > "$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'gitdir - repo as argument' '
+	echo "otherrepo/.git" > expected &&
+	__gitdir "otherrepo" > "$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'gitdir - remote as argument' '
+	echo "remote" > expected &&
+	__gitdir "remote" > "$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'gitdir - .git directory in cwd' '
+	echo ".git" > expected &&
+	__gitdir > "$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'gitdir - .git directory in parent' '
+	echo "$TRASH_DIRECTORY/.git" > expected &&
+	(
+		cd subdir/subsubdir &&
+		__gitdir > "$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'gitdir - cwd is a .git directory' '
+	echo "." > expected &&
+	(
+		cd .git &&
+		__gitdir > "$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'gitdir - parent is a .git directory' '
+	echo "$TRASH_DIRECTORY/.git" > expected &&
+	(
+		cd .git/refs/heads &&
+		__gitdir > "$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'gitdir - $GIT_DIR set while .git directory in cwd' '
+	echo "$TRASH_DIRECTORY/otherrepo/.git" > expected &&
+	(
+		GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" &&
+		export GIT_DIR &&
+		__gitdir > "$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'gitdir - $GIT_DIR set while .git directory in parent' '
+	echo "$TRASH_DIRECTORY/otherrepo/.git" > expected &&
+	(
+		GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" &&
+		export GIT_DIR &&
+		cd subdir &&
+		__gitdir > "$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'gitdir - non-existing $GIT_DIR' '
+	(
+		GIT_DIR="$TRASH_DIRECTORY/non-existing" &&
+		export GIT_DIR &&
+		test_must_fail __gitdir
+	)
+'
+
+test_expect_success 'gitdir - gitfile in cwd' '
+	echo "$TRASH_DIRECTORY/otherrepo/.git" > expected &&
+	echo "gitdir: $TRASH_DIRECTORY/otherrepo/.git" > subdir/.git &&
+	test_when_finished "rm -f subdir/.git" &&
+	(
+		cd subdir &&
+		__gitdir > "$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'gitdir - gitfile in parent' '
+	echo "$TRASH_DIRECTORY/otherrepo/.git" > expected &&
+	echo "gitdir: $TRASH_DIRECTORY/otherrepo/.git" > subdir/.git &&
+	test_when_finished "rm -f subdir/.git" &&
+	(
+		cd subdir/subsubdir &&
+		__gitdir > "$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success SYMLINKS 'gitdir - resulting path avoids symlinks' '
+	echo "$TRASH_DIRECTORY/otherrepo/.git" > expected &&
+	mkdir otherrepo/dir &&
+	test_when_finished "rm -rf otherrepo/dir" &&
+	ln -s otherrepo/dir link &&
+	test_when_finished "rm -f link" &&
+	(
+		cd link &&
+		__gitdir > "$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'gitdir - not a git repository' '
+	(
+		cd subdir/subsubdir &&
+		GIT_CEILING_DIRECTORIES="$TRASH_DIRECTORY" &&
+		export GIT_CEILING_DIRECTORIES &&
+		test_must_fail __gitdir
+	)
+'
+
+test_expect_success 'prompt - branch name' '
+	printf " (master)" > expected &&
+	__git_ps1 > "$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - detached head' '
+	printf " ((%s...))" $(git log -1 --format="%h" b1^) > expected &&
+	git checkout b1^ &&
+	test_when_finished "git checkout master" &&
+	__git_ps1 > "$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - describe detached head - contains' '
+	printf " ((t2~1))" > expected &&
+	git checkout b1^ &&
+	test_when_finished "git checkout master" &&
+	(
+		GIT_PS1_DESCRIBE_STYLE=contains &&
+		__git_ps1 > "$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - describe detached head - branch' '
+	printf " ((b1~1))" > expected &&
+	git checkout b1^ &&
+	test_when_finished "git checkout master" &&
+	(
+		GIT_PS1_DESCRIBE_STYLE=branch &&
+		__git_ps1 > "$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - describe detached head - describe' '
+	printf " ((t1-1-g%s))" $(git log -1 --format="%h" b1^) > expected &&
+	git checkout b1^ &&
+	test_when_finished "git checkout master" &&
+	(
+		GIT_PS1_DESCRIBE_STYLE=describe &&
+		__git_ps1 > "$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - describe detached head - default' '
+	printf " ((t2))" > expected &&
+	git checkout --detach b1 &&
+	test_when_finished "git checkout master" &&
+	__git_ps1 > "$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - inside .git directory' '
+	printf " (GIT_DIR!)" > expected &&
+	(
+		cd .git &&
+		__git_ps1 > "$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - deep inside .git directory' '
+	printf " (GIT_DIR!)" > expected &&
+	(
+		cd .git/refs/heads &&
+		__git_ps1 > "$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - inside bare repository' '
+	printf " (BARE:master)" > expected &&
+	git init --bare bare.git &&
+	test_when_finished "rm -rf bare.git" &&
+	(
+		cd bare.git &&
+		__git_ps1 > "$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - interactive rebase' '
+	printf " (b1|REBASE-i)" > expected
+	echo "#!$SHELL_PATH" >fake_editor.sh &&
+	cat >>fake_editor.sh <<\EOF &&
+echo "edit $(git log -1 --format="%h")" > "$1"
+EOF
+	test_when_finished "rm -f fake_editor.sh" &&
+	chmod a+x fake_editor.sh &&
+	test_set_editor "$TRASH_DIRECTORY/fake_editor.sh" &&
+	git checkout b1 &&
+	test_when_finished "git checkout master" &&
+	git rebase -i HEAD^ &&
+	test_when_finished "git rebase --abort"
+	__git_ps1 > "$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - rebase merge' '
+	printf " (b2|REBASE-m)" > expected &&
+	git checkout b2 &&
+	test_when_finished "git checkout master" &&
+	test_must_fail git rebase --merge b1 b2 &&
+	test_when_finished "git rebase --abort" &&
+	__git_ps1 > "$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - rebase' '
+	printf " ((t2)|REBASE)" > expected &&
+	git checkout b2 &&
+	test_when_finished "git checkout master" &&
+	test_must_fail git rebase b1 b2 &&
+	test_when_finished "git rebase --abort" &&
+	__git_ps1 > "$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - merge' '
+	printf " (b1|MERGING)" > expected &&
+	git checkout b1 &&
+	test_when_finished "git checkout master" &&
+	test_must_fail git merge b2 &&
+	test_when_finished "git reset --hard" &&
+	__git_ps1 > "$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - cherry-pick' '
+	printf " (master|CHERRY-PICKING)" > expected &&
+	test_must_fail git cherry-pick b1 &&
+	test_when_finished "git reset --hard" &&
+	__git_ps1 > "$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bisect' '
+	printf " (master|BISECTING)" > expected &&
+	git bisect start &&
+	test_when_finished "git bisect reset" &&
+	__git_ps1 > "$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - dirty status indicator - clean' '
+	printf " (master)" > expected &&
+	(
+		GIT_PS1_SHOWDIRTYSTATE=y &&
+		__git_ps1 > "$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - dirty status indicator - dirty worktree' '
+	printf " (master *)" > expected &&
+	echo "dirty" > file &&
+	test_when_finished "git reset --hard" &&
+	(
+		GIT_PS1_SHOWDIRTYSTATE=y &&
+		__git_ps1 > "$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - dirty status indicator - dirty index' '
+	printf " (master +)" > expected &&
+	echo "dirty" > file &&
+	test_when_finished "git reset --hard" &&
+	git add -u &&
+	(
+		GIT_PS1_SHOWDIRTYSTATE=y &&
+		__git_ps1 > "$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - dirty status indicator - dirty index and worktree' '
+	printf " (master *+)" > expected &&
+	echo "dirty index" > file &&
+	test_when_finished "git reset --hard" &&
+	git add -u &&
+	echo "dirty worktree" > file &&
+	(
+		GIT_PS1_SHOWDIRTYSTATE=y &&
+		__git_ps1 > "$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - dirty status indicator - before root commit' '
+	printf " (master #)" > expected &&
+	(
+		GIT_PS1_SHOWDIRTYSTATE=y &&
+		cd otherrepo &&
+		__git_ps1 > "$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - dirty status indicator - disabled by config' '
+	printf " (master)" > expected &&
+	echo "dirty" > file &&
+	test_when_finished "git reset --hard" &&
+	test_config bash.showDirtyState false &&
+	(
+		GIT_PS1_SHOWDIRTYSTATE=y &&
+		__git_ps1 > "$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - dirty status indicator - not shown inside .git directory' '
+	printf " (GIT_DIR!)" > expected &&
+	echo "dirty" > file &&
+	test_when_finished "git reset --hard" &&
+	(
+		GIT_PS1_SHOWDIRTYSTATE=y &&
+		cd .git &&
+		__git_ps1 > "$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - stash status indicator - no stash' '
+	printf " (master)" > expected &&
+	(
+		GIT_PS1_SHOWSTASHSTATE=y &&
+		__git_ps1 > "$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - stash status indicator - stash' '
+	printf " (master $)" > expected &&
+	echo 2 >file &&
+	git stash &&
+	test_when_finished "git stash drop" &&
+	(
+		GIT_PS1_SHOWSTASHSTATE=y &&
+		__git_ps1 > "$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - stash status indicator - not shown inside .git directory' '
+	printf " (GIT_DIR!)" > expected &&
+	echo 2 >file &&
+	git stash &&
+	test_when_finished "git stash drop" &&
+	(
+		GIT_PS1_SHOWSTASHSTATE=y &&
+		cd .git &&
+		__git_ps1 > "$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - untracked files status indicator - no untracked files' '
+	printf " (master)" > expected &&
+	(
+		GIT_PS1_SHOWUNTRACKEDFILES=y &&
+		cd otherrepo &&
+		__git_ps1 > "$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - untracked files status indicator - untracked files' '
+	printf " (master %%)" > expected &&
+	(
+		GIT_PS1_SHOWUNTRACKEDFILES=y &&
+		__git_ps1 > "$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - untracked files status indicator - not shown inside .git directory' '
+	printf " (GIT_DIR!)" > expected &&
+	(
+		GIT_PS1_SHOWUNTRACKEDFILES=y &&
+		cd .git &&
+		__git_ps1 > "$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - format string starting with dash' '
+	printf -- "-master" > expected &&
+	__git_ps1 "-%s" > "$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_done
diff --git a/t/test4012.png b/t/test-binary-1.png
similarity index 100%
rename from t/test4012.png
rename to t/test-binary-1.png
Binary files differ
diff --git a/t/test9200b.png b/t/test-binary-2.png
similarity index 100%
rename from t/test9200b.png
rename to t/test-binary-2.png
Binary files differ
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
new file mode 100644
index 0000000..80daaca
--- /dev/null
+++ b/t/test-lib-functions.sh
@@ -0,0 +1,574 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see http://www.gnu.org/licenses/ .
+
+# The semantics of the editor variables are that of invoking
+# sh -c "$EDITOR \"$@\"" files ...
+#
+# If our trash directory contains shell metacharacters, they will be
+# interpreted if we just set $EDITOR directly, so do a little dance with
+# environment variables to work around this.
+#
+# In particular, quoting isn't enough, as the path may contain the same quote
+# that we're using.
+test_set_editor () {
+	FAKE_EDITOR="$1"
+	export FAKE_EDITOR
+	EDITOR='"$FAKE_EDITOR"'
+	export EDITOR
+}
+
+test_decode_color () {
+	awk '
+		function name(n) {
+			if (n == 0) return "RESET";
+			if (n == 1) return "BOLD";
+			if (n == 30) return "BLACK";
+			if (n == 31) return "RED";
+			if (n == 32) return "GREEN";
+			if (n == 33) return "YELLOW";
+			if (n == 34) return "BLUE";
+			if (n == 35) return "MAGENTA";
+			if (n == 36) return "CYAN";
+			if (n == 37) return "WHITE";
+			if (n == 40) return "BLACK";
+			if (n == 41) return "BRED";
+			if (n == 42) return "BGREEN";
+			if (n == 43) return "BYELLOW";
+			if (n == 44) return "BBLUE";
+			if (n == 45) return "BMAGENTA";
+			if (n == 46) return "BCYAN";
+			if (n == 47) return "BWHITE";
+		}
+		{
+			while (match($0, /\033\[[0-9;]*m/) != 0) {
+				printf "%s<", substr($0, 1, RSTART-1);
+				codes = substr($0, RSTART+2, RLENGTH-3);
+				if (length(codes) == 0)
+					printf "%s", name(0)
+				else {
+					n = split(codes, ary, ";");
+					sep = "";
+					for (i = 1; i <= n; i++) {
+						printf "%s%s", sep, name(ary[i]);
+						sep = ";"
+					}
+				}
+				printf ">";
+				$0 = substr($0, RSTART + RLENGTH, length($0) - RSTART - RLENGTH + 1);
+			}
+			print
+		}
+	'
+}
+
+nul_to_q () {
+	"$PERL_PATH" -pe 'y/\000/Q/'
+}
+
+q_to_nul () {
+	"$PERL_PATH" -pe 'y/Q/\000/'
+}
+
+q_to_cr () {
+	tr Q '\015'
+}
+
+q_to_tab () {
+	tr Q '\011'
+}
+
+append_cr () {
+	sed -e 's/$/Q/' | tr Q '\015'
+}
+
+remove_cr () {
+	tr '\015' Q | sed -e 's/Q$//'
+}
+
+# In some bourne shell implementations, the "unset" builtin returns
+# nonzero status when a variable to be unset was not set in the first
+# place.
+#
+# Use sane_unset when that should not be considered an error.
+
+sane_unset () {
+	unset "$@"
+	return 0
+}
+
+test_tick () {
+	if test -z "${test_tick+set}"
+	then
+		test_tick=1112911993
+	else
+		test_tick=$(($test_tick + 60))
+	fi
+	GIT_COMMITTER_DATE="$test_tick -0700"
+	GIT_AUTHOR_DATE="$test_tick -0700"
+	export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
+}
+
+# Stop execution and start a shell. This is useful for debugging tests and
+# only makes sense together with "-v".
+#
+# Be sure to remove all invocations of this command before submitting.
+
+test_pause () {
+	if test "$verbose" = t; then
+		"$SHELL_PATH" <&6 >&3 2>&4
+	else
+		error >&5 "test_pause requires --verbose"
+	fi
+}
+
+# Call test_commit with the arguments "<message> [<file> [<contents>]]"
+#
+# This will commit a file with the given contents and the given commit
+# message.  It will also add a tag with <message> as name.
+#
+# Both <file> and <contents> default to <message>.
+
+test_commit () {
+	notick= &&
+	if test "z$1" = "z--notick"
+	then
+		notick=yes
+		shift
+	fi &&
+	file=${2:-"$1.t"} &&
+	echo "${3-$1}" > "$file" &&
+	git add "$file" &&
+	if test -z "$notick"
+	then
+		test_tick
+	fi &&
+	git commit -m "$1" &&
+	git tag "$1"
+}
+
+# Call test_merge with the arguments "<message> <commit>", where <commit>
+# can be a tag pointing to the commit-to-merge.
+
+test_merge () {
+	test_tick &&
+	git merge -m "$1" "$2" &&
+	git tag "$1"
+}
+
+# This function helps systems where core.filemode=false is set.
+# Use it instead of plain 'chmod +x' to set or unset the executable bit
+# of a file in the working directory and add it to the index.
+
+test_chmod () {
+	chmod "$@" &&
+	git update-index --add "--chmod=$@"
+}
+
+# Unset a configuration variable, but don't fail if it doesn't exist.
+test_unconfig () {
+	git config --unset-all "$@"
+	config_status=$?
+	case "$config_status" in
+	5) # ok, nothing to unset
+		config_status=0
+		;;
+	esac
+	return $config_status
+}
+
+# Set git config, automatically unsetting it after the test is over.
+test_config () {
+	test_when_finished "test_unconfig '$1'" &&
+	git config "$@"
+}
+
+test_config_global () {
+	test_when_finished "test_unconfig --global '$1'" &&
+	git config --global "$@"
+}
+
+write_script () {
+	{
+		echo "#!${2-"$SHELL_PATH"}" &&
+		cat
+	} >"$1" &&
+	chmod +x "$1"
+}
+
+# Use test_set_prereq to tell that a particular prerequisite is available.
+# The prerequisite can later be checked for in two ways:
+#
+# - Explicitly using test_have_prereq.
+#
+# - Implicitly by specifying the prerequisite tag in the calls to
+#   test_expect_{success,failure,code}.
+#
+# The single parameter is the prerequisite tag (a simple word, in all
+# capital letters by convention).
+
+test_set_prereq () {
+	satisfied="$satisfied$1 "
+}
+satisfied=" "
+
+test_have_prereq () {
+	# prerequisites can be concatenated with ','
+	save_IFS=$IFS
+	IFS=,
+	set -- $*
+	IFS=$save_IFS
+
+	total_prereq=0
+	ok_prereq=0
+	missing_prereq=
+
+	for prerequisite
+	do
+		total_prereq=$(($total_prereq + 1))
+		case $satisfied in
+		*" $prerequisite "*)
+			ok_prereq=$(($ok_prereq + 1))
+			;;
+		*)
+			# Keep a list of missing prerequisites
+			if test -z "$missing_prereq"
+			then
+				missing_prereq=$prerequisite
+			else
+				missing_prereq="$prerequisite,$missing_prereq"
+			fi
+		esac
+	done
+
+	test $total_prereq = $ok_prereq
+}
+
+test_declared_prereq () {
+	case ",$test_prereq," in
+	*,$1,*)
+		return 0
+		;;
+	esac
+	return 1
+}
+
+test_expect_failure () {
+	test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
+	test "$#" = 2 ||
+	error "bug in the test script: not 2 or 3 parameters to test-expect-failure"
+	export test_prereq
+	if ! test_skip "$@"
+	then
+		say >&3 "checking known breakage: $2"
+		if test_run_ "$2" expecting_failure
+		then
+			test_known_broken_ok_ "$1"
+		else
+			test_known_broken_failure_ "$1"
+		fi
+	fi
+	echo >&3 ""
+}
+
+test_expect_success () {
+	test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
+	test "$#" = 2 ||
+	error "bug in the test script: not 2 or 3 parameters to test-expect-success"
+	export test_prereq
+	if ! test_skip "$@"
+	then
+		say >&3 "expecting success: $2"
+		if test_run_ "$2"
+		then
+			test_ok_ "$1"
+		else
+			test_failure_ "$@"
+		fi
+	fi
+	echo >&3 ""
+}
+
+# test_external runs external test scripts that provide continuous
+# test output about their progress, and succeeds/fails on
+# zero/non-zero exit code.  It outputs the test output on stdout even
+# in non-verbose mode, and announces the external script with "# run
+# <n>: ..." before running it.  When providing relative paths, keep in
+# mind that all scripts run in "trash directory".
+# Usage: test_external description command arguments...
+# Example: test_external 'Perl API' perl ../path/to/test.pl
+test_external () {
+	test "$#" = 4 && { test_prereq=$1; shift; } || test_prereq=
+	test "$#" = 3 ||
+	error >&5 "bug in the test script: not 3 or 4 parameters to test_external"
+	descr="$1"
+	shift
+	export test_prereq
+	if ! test_skip "$descr" "$@"
+	then
+		# Announce the script to reduce confusion about the
+		# test output that follows.
+		say_color "" "# run $test_count: $descr ($*)"
+		# Export TEST_DIRECTORY, TRASH_DIRECTORY and GIT_TEST_LONG
+		# to be able to use them in script
+		export TEST_DIRECTORY TRASH_DIRECTORY GIT_TEST_LONG
+		# Run command; redirect its stderr to &4 as in
+		# test_run_, but keep its stdout on our stdout even in
+		# non-verbose mode.
+		"$@" 2>&4
+		if [ "$?" = 0 ]
+		then
+			if test $test_external_has_tap -eq 0; then
+				test_ok_ "$descr"
+			else
+				say_color "" "# test_external test $descr was ok"
+				test_success=$(($test_success + 1))
+			fi
+		else
+			if test $test_external_has_tap -eq 0; then
+				test_failure_ "$descr" "$@"
+			else
+				say_color error "# test_external test $descr failed: $@"
+				test_failure=$(($test_failure + 1))
+			fi
+		fi
+	fi
+}
+
+# Like test_external, but in addition tests that the command generated
+# no output on stderr.
+test_external_without_stderr () {
+	# The temporary file has no (and must have no) security
+	# implications.
+	tmp=${TMPDIR:-/tmp}
+	stderr="$tmp/git-external-stderr.$$.tmp"
+	test_external "$@" 4> "$stderr"
+	[ -f "$stderr" ] || error "Internal error: $stderr disappeared."
+	descr="no stderr: $1"
+	shift
+	say >&3 "# expecting no stderr from previous command"
+	if [ ! -s "$stderr" ]; then
+		rm "$stderr"
+
+		if test $test_external_has_tap -eq 0; then
+			test_ok_ "$descr"
+		else
+			say_color "" "# test_external_without_stderr test $descr was ok"
+			test_success=$(($test_success + 1))
+		fi
+	else
+		if [ "$verbose" = t ]; then
+			output=`echo; echo "# Stderr is:"; cat "$stderr"`
+		else
+			output=
+		fi
+		# rm first in case test_failure exits.
+		rm "$stderr"
+		if test $test_external_has_tap -eq 0; then
+			test_failure_ "$descr" "$@" "$output"
+		else
+			say_color error "# test_external_without_stderr test $descr failed: $@: $output"
+			test_failure=$(($test_failure + 1))
+		fi
+	fi
+}
+
+# debugging-friendly alternatives to "test [-f|-d|-e]"
+# The commands test the existence or non-existence of $1. $2 can be
+# given to provide a more precise diagnosis.
+test_path_is_file () {
+	if ! [ -f "$1" ]
+	then
+		echo "File $1 doesn't exist. $*"
+		false
+	fi
+}
+
+test_path_is_dir () {
+	if ! [ -d "$1" ]
+	then
+		echo "Directory $1 doesn't exist. $*"
+		false
+	fi
+}
+
+test_path_is_missing () {
+	if [ -e "$1" ]
+	then
+		echo "Path exists:"
+		ls -ld "$1"
+		if [ $# -ge 1 ]; then
+			echo "$*"
+		fi
+		false
+	fi
+}
+
+# test_line_count checks that a file has the number of lines it
+# ought to. For example:
+#
+#	test_expect_success 'produce exactly one line of output' '
+#		do something >output &&
+#		test_line_count = 1 output
+#	'
+#
+# is like "test $(wc -l <output) = 1" except that it passes the
+# output through when the number of lines is wrong.
+
+test_line_count () {
+	if test $# != 3
+	then
+		error "bug in the test script: not 3 parameters to test_line_count"
+	elif ! test $(wc -l <"$3") "$1" "$2"
+	then
+		echo "test_line_count: line count for $3 !$1 $2"
+		cat "$3"
+		return 1
+	fi
+}
+
+# This is not among top-level (test_expect_success | test_expect_failure)
+# but is a prefix that can be used in the test script, like:
+#
+#	test_expect_success 'complain and die' '
+#           do something &&
+#           do something else &&
+#	    test_must_fail git checkout ../outerspace
+#	'
+#
+# Writing this as "! git checkout ../outerspace" is wrong, because
+# the failure could be due to a segv.  We want a controlled failure.
+
+test_must_fail () {
+	"$@"
+	exit_code=$?
+	if test $exit_code = 0; then
+		echo >&2 "test_must_fail: command succeeded: $*"
+		return 1
+	elif test $exit_code -gt 129 -a $exit_code -le 192; then
+		echo >&2 "test_must_fail: died by signal: $*"
+		return 1
+	elif test $exit_code = 127; then
+		echo >&2 "test_must_fail: command not found: $*"
+		return 1
+	fi
+	return 0
+}
+
+# Similar to test_must_fail, but tolerates success, too.  This is
+# meant to be used in contexts like:
+#
+#	test_expect_success 'some command works without configuration' '
+#		test_might_fail git config --unset all.configuration &&
+#		do something
+#	'
+#
+# Writing "git config --unset all.configuration || :" would be wrong,
+# because we want to notice if it fails due to segv.
+
+test_might_fail () {
+	"$@"
+	exit_code=$?
+	if test $exit_code -gt 129 -a $exit_code -le 192; then
+		echo >&2 "test_might_fail: died by signal: $*"
+		return 1
+	elif test $exit_code = 127; then
+		echo >&2 "test_might_fail: command not found: $*"
+		return 1
+	fi
+	return 0
+}
+
+# Similar to test_must_fail and test_might_fail, but check that a
+# given command exited with a given exit code. Meant to be used as:
+#
+#	test_expect_success 'Merge with d/f conflicts' '
+#		test_expect_code 1 git merge "merge msg" B master
+#	'
+
+test_expect_code () {
+	want_code=$1
+	shift
+	"$@"
+	exit_code=$?
+	if test $exit_code = $want_code
+	then
+		return 0
+	fi
+
+	echo >&2 "test_expect_code: command exited with $exit_code, we wanted $want_code $*"
+	return 1
+}
+
+# test_cmp is a helper function to compare actual and expected output.
+# You can use it like:
+#
+#	test_expect_success 'foo works' '
+#		echo expected >expected &&
+#		foo >actual &&
+#		test_cmp expected actual
+#	'
+#
+# This could be written as either "cmp" or "diff -u", but:
+# - cmp's output is not nearly as easy to read as diff -u
+# - not all diff versions understand "-u"
+
+test_cmp() {
+	$GIT_TEST_CMP "$@"
+}
+
+# This function can be used to schedule some commands to be run
+# unconditionally at the end of the test to restore sanity:
+#
+#	test_expect_success 'test core.capslock' '
+#		git config core.capslock true &&
+#		test_when_finished "git config --unset core.capslock" &&
+#		hello world
+#	'
+#
+# That would be roughly equivalent to
+#
+#	test_expect_success 'test core.capslock' '
+#		git config core.capslock true &&
+#		hello world
+#		git config --unset core.capslock
+#	'
+#
+# except that the greeting and config --unset must both succeed for
+# the test to pass.
+#
+# Note that under --immediate mode, no clean-up is done to help diagnose
+# what went wrong.
+
+test_when_finished () {
+	test_cleanup="{ $*
+		} && (exit \"\$eval_ret\"); eval_ret=\$?; $test_cleanup"
+}
+
+# Most tests can use the created repository, but some may need to create more.
+# Usage: test_create_repo <directory>
+test_create_repo () {
+	test "$#" = 1 ||
+	error "bug in the test script: not 1 parameter to test-create-repo"
+	repo="$1"
+	mkdir -p "$repo"
+	(
+		cd "$repo" || error "Cannot setup test environment"
+		"$GIT_EXEC_PATH/git-init" "--template=$GIT_BUILD_DIR/templates/blt/" >&3 2>&4 ||
+		error "cannot run git init -- have you built things yet?"
+		mv .git/hooks .git/hooks-disabled
+	) || exit
+}
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 5fdc5d9..acda33d 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -2,6 +2,18 @@
 #
 # Copyright (c) 2005 Junio C Hamano
 #
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see http://www.gnu.org/licenses/ .
 
 # if --tee was passed, write the output not only to the terminal, but
 # additionally to the file test-results/$BASENAME.out, too.
@@ -30,41 +42,42 @@
 TERM=dumb
 export LANG LC_ALL PAGER TERM TZ
 EDITOR=:
-VISUAL=:
-unset GIT_EDITOR
-unset AUTHOR_DATE
-unset AUTHOR_EMAIL
-unset AUTHOR_NAME
-unset COMMIT_AUTHOR_EMAIL
-unset COMMIT_AUTHOR_NAME
-unset EMAIL
-unset GIT_ALTERNATE_OBJECT_DIRECTORIES
-unset GIT_AUTHOR_DATE
+# A call to "unset" with no arguments causes at least Solaris 10
+# /usr/xpg4/bin/sh and /bin/ksh to bail out.  So keep the unsets
+# deriving from the command substitution clustered with the other
+# ones.
+unset VISUAL EMAIL LANGUAGE COLUMNS $(perl -e '
+	my @env = keys %ENV;
+	my $ok = join("|", qw(
+		TRACE
+		DEBUG
+		USE_LOOKUP
+		TEST
+		.*_TEST
+		PROVE
+		VALGRIND
+		PERF_AGGREGATING_LATER
+	));
+	my @vars = grep(/^GIT_/ && !/^GIT_($ok)/o, @env);
+	print join("\n", @vars);
+')
 GIT_AUTHOR_EMAIL=author@example.com
 GIT_AUTHOR_NAME='A U Thor'
-unset GIT_COMMITTER_DATE
 GIT_COMMITTER_EMAIL=committer@example.com
 GIT_COMMITTER_NAME='C O Mitter'
-unset GIT_DIFF_OPTS
-unset GIT_DIR
-unset GIT_WORK_TREE
-unset GIT_EXTERNAL_DIFF
-unset GIT_INDEX_FILE
-unset GIT_OBJECT_DIRECTORY
-unset GIT_CEILING_DIRECTORIES
-unset SHA1_FILE_DIRECTORIES
-unset SHA1_FILE_DIRECTORY
 GIT_MERGE_VERBOSITY=5
-export GIT_MERGE_VERBOSITY
+GIT_MERGE_AUTOEDIT=no
+export GIT_MERGE_VERBOSITY GIT_MERGE_AUTOEDIT
 export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
 export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME
-export EDITOR VISUAL
-GIT_TEST_CMP=${GIT_TEST_CMP:-diff -u}
+export EDITOR
 
 # Protect ourselves from common misconfiguration to export
 # CDPATH into the environment
 unset CDPATH
 
+unset GREP_OPTIONS
+
 case $(echo $GIT_TRACE |tr "[A-Z]" "[a-z]") in
 	1|2|true)
 		echo "* warning: Some tests will not work if GIT_TRACE" \
@@ -74,6 +87,21 @@
 		;;
 esac
 
+# Convenience
+#
+# A regexp to match 5 and 40 hexdigits
+_x05='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
+_x40="$_x05$_x05$_x05$_x05$_x05$_x05$_x05$_x05"
+
+# Zero SHA-1
+_z40=0000000000000000000000000000000000000000
+
+# Line feed
+LF='
+'
+
+export _x05 _x40 _z40 LF
+
 # Each test should start with something like this, after copyright notices:
 #
 # test_description='Description of this test...
@@ -104,16 +132,20 @@
 	-v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
 		verbose=t; shift ;;
 	-q|--q|--qu|--qui|--quie|--quiet)
-		quiet=t; shift ;;
+		# Ignore --quiet under a TAP::Harness. Saying how many tests
+		# passed without the ok/not ok details is always an error.
+		test -z "$HARNESS_ACTIVE" && quiet=t; shift ;;
+	--with-dashes)
+		with_dashes=t; shift ;;
 	--no-color)
 		color=; shift ;;
-	--no-python)
-		# noop now...
-		shift ;;
 	--va|--val|--valg|--valgr|--valgri|--valgrin|--valgrind)
 		valgrind=t; verbose=t; shift ;;
 	--tee)
 		shift ;; # was handled already
+	--root=*)
+		root=$(expr "z$1" : 'z[^=]*=\(.*\)')
+		shift ;;
 	*)
 		echo "error: unknown test option '$1'" >&2; exit 1 ;;
 	esac
@@ -132,7 +164,7 @@
 			*) test -n "$quiet" && return;;
 		esac
 		shift
-		printf "* %s" "$*"
+		printf "%s" "$*"
 		tput sgr0
 		echo
 		)
@@ -141,7 +173,7 @@
 	say_color() {
 		test -z "$1" && test -n "$quiet" && return
 		shift
-		echo "* $*"
+		echo "$*"
 	}
 fi
 
@@ -165,6 +197,7 @@
 fi
 
 exec 5>&1
+exec 6<&0
 if test "$verbose" = "t"
 then
 	exec 4>&2 3>&1
@@ -178,6 +211,8 @@
 test_broken=0
 test_success=0
 
+test_external_has_tap=0
+
 die () {
 	code=$?
 	if test -n "$GIT_EXIT_OK"
@@ -192,127 +227,60 @@
 GIT_EXIT_OK=
 trap 'die' EXIT
 
-# The semantics of the editor variables are that of invoking
-# sh -c "$EDITOR \"$@\"" files ...
-#
-# If our trash directory contains shell metacharacters, they will be
-# interpreted if we just set $EDITOR directly, so do a little dance with
-# environment variables to work around this.
-#
-# In particular, quoting isn't enough, as the path may contain the same quote
-# that we're using.
-test_set_editor () {
-	FAKE_EDITOR="$1"
-	export FAKE_EDITOR
-	VISUAL='"$FAKE_EDITOR"'
-	export VISUAL
-}
-
-test_tick () {
-	if test -z "${test_tick+set}"
-	then
-		test_tick=1112911993
-	else
-		test_tick=$(($test_tick + 60))
-	fi
-	GIT_COMMITTER_DATE="$test_tick -0700"
-	GIT_AUTHOR_DATE="$test_tick -0700"
-	export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
-}
-
-# Call test_commit with the arguments "<message> [<file> [<contents>]]"
-#
-# This will commit a file with the given contents and the given commit
-# message.  It will also add a tag with <message> as name.
-#
-# Both <file> and <contents> default to <message>.
-
-test_commit () {
-	file=${2:-"$1.t"}
-	echo "${3-$1}" > "$file" &&
-	git add "$file" &&
-	test_tick &&
-	git commit -m "$1" &&
-	git tag "$1"
-}
-
-# Call test_merge with the arguments "<message> <commit>", where <commit>
-# can be a tag pointing to the commit-to-merge.
-
-test_merge () {
-	test_tick &&
-	git merge -m "$1" "$2" &&
-	git tag "$1"
-}
-
-# This function helps systems where core.filemode=false is set.
-# Use it instead of plain 'chmod +x' to set or unset the executable bit
-# of a file in the working directory and add it to the index.
-
-test_chmod () {
-	chmod "$@" &&
-	git update-index --add "--chmod=$@"
-}
-
-# Use test_set_prereq to tell that a particular prerequisite is available.
-# The prerequisite can later be checked for in two ways:
-#
-# - Explicitly using test_have_prereq.
-#
-# - Implicitly by specifying the prerequisite tag in the calls to
-#   test_expect_{success,failure,code}.
-#
-# The single parameter is the prerequisite tag (a simple word, in all
-# capital letters by convention).
-
-test_set_prereq () {
-	satisfied="$satisfied$1 "
-}
-satisfied=" "
-
-test_have_prereq () {
-	case $satisfied in
-	*" $1 "*)
-		: yes, have it ;;
-	*)
-		! : nope ;;
-	esac
-}
+# The user-facing functions are loaded from a separate file so that
+# test_perf subshells can have them too
+. "${TEST_DIRECTORY:-.}"/test-lib-functions.sh
 
 # You are not expected to call test_ok_ and test_failure_ directly, use
 # the text_expect_* functions instead.
 
 test_ok_ () {
 	test_success=$(($test_success + 1))
-	say_color "" "  ok $test_count: $@"
+	say_color "" "ok $test_count - $@"
 }
 
 test_failure_ () {
 	test_failure=$(($test_failure + 1))
-	say_color error "FAIL $test_count: $1"
+	say_color error "not ok - $test_count $1"
 	shift
-	echo "$@" | sed -e 's/^/	/'
+	echo "$@" | sed -e 's/^/#	/'
 	test "$immediate" = "" || { GIT_EXIT_OK=t; exit 1; }
 }
 
 test_known_broken_ok_ () {
 	test_fixed=$(($test_fixed+1))
-	say_color "" "  FIXED $test_count: $@"
+	say_color "" "ok $test_count - $@ # TODO known breakage"
 }
 
 test_known_broken_failure_ () {
 	test_broken=$(($test_broken+1))
-	say_color skip "  still broken $test_count: $@"
+	say_color skip "not ok $test_count - $@ # TODO known breakage"
 }
 
 test_debug () {
 	test "$debug" = "" || eval "$1"
 }
 
+test_eval_ () {
+	# This is a separate function because some tests use
+	# "return" to end a test_expect_success block early.
+	eval </dev/null >&3 2>&4 "$*"
+}
+
 test_run_ () {
-	eval >&3 2>&4 "$1"
-	eval_ret="$?"
-	return 0
+	test_cleanup=:
+	expecting_failure=$2
+	test_eval_ "$1"
+	eval_ret=$?
+
+	if test -z "$immediate" || test $eval_ret = 0 || test -n "$expecting_failure"
+	then
+		test_eval_ "$test_cleanup"
+	fi
+	if test "$verbose" = "t" && test -n "$HARNESS_ACTIVE"; then
+		echo ""
+	fi
+	return "$eval_ret"
 }
 
 test_skip () {
@@ -323,17 +291,24 @@
 		case $this_test.$test_count in
 		$skp)
 			to_skip=t
+			break
 		esac
 	done
-	if test -z "$to_skip" && test -n "$prereq" &&
-	   ! test_have_prereq "$prereq"
+	if test -z "$to_skip" && test -n "$test_prereq" &&
+	   ! test_have_prereq "$test_prereq"
 	then
 		to_skip=t
 	fi
 	case "$to_skip" in
 	t)
+		of_prereq=
+		if test "$missing_prereq" != "$test_prereq"
+		then
+			of_prereq=" of $test_prereq"
+		fi
+
 		say_color skip >&3 "skipping test: $@"
-		say_color skip "skip $test_count: $1"
+		say_color skip "ok $test_count # skip $1 (missing $missing_prereq${of_prereq})"
 		: true
 		;;
 	*)
@@ -342,204 +317,64 @@
 	esac
 }
 
-test_expect_failure () {
-	test "$#" = 3 && { prereq=$1; shift; } || prereq=
-	test "$#" = 2 ||
-	error "bug in the test script: not 2 or 3 parameters to test-expect-failure"
-	if ! test_skip "$@"
-	then
-		say >&3 "checking known breakage: $2"
-		test_run_ "$2"
-		if [ "$?" = 0 -a "$eval_ret" = 0 ]
-		then
-			test_known_broken_ok_ "$1"
-		else
-			test_known_broken_failure_ "$1"
-		fi
-	fi
-	echo >&3 ""
-}
-
-test_expect_success () {
-	test "$#" = 3 && { prereq=$1; shift; } || prereq=
-	test "$#" = 2 ||
-	error "bug in the test script: not 2 or 3 parameters to test-expect-success"
-	if ! test_skip "$@"
-	then
-		say >&3 "expecting success: $2"
-		test_run_ "$2"
-		if [ "$?" = 0 -a "$eval_ret" = 0 ]
-		then
-			test_ok_ "$1"
-		else
-			test_failure_ "$@"
-		fi
-	fi
-	echo >&3 ""
-}
-
-test_expect_code () {
-	test "$#" = 4 && { prereq=$1; shift; } || prereq=
-	test "$#" = 3 ||
-	error "bug in the test script: not 3 or 4 parameters to test-expect-code"
-	if ! test_skip "$@"
-	then
-		say >&3 "expecting exit code $1: $3"
-		test_run_ "$3"
-		if [ "$?" = 0 -a "$eval_ret" = "$1" ]
-		then
-			test_ok_ "$2"
-		else
-			test_failure_ "$@"
-		fi
-	fi
-	echo >&3 ""
-}
-
-# test_external runs external test scripts that provide continuous
-# test output about their progress, and succeeds/fails on
-# zero/non-zero exit code.  It outputs the test output on stdout even
-# in non-verbose mode, and announces the external script with "* run
-# <n>: ..." before running it.  When providing relative paths, keep in
-# mind that all scripts run in "trash directory".
-# Usage: test_external description command arguments...
-# Example: test_external 'Perl API' perl ../path/to/test.pl
-test_external () {
-	test "$#" = 4 && { prereq=$1; shift; } || prereq=
-	test "$#" = 3 ||
-	error >&5 "bug in the test script: not 3 or 4 parameters to test_external"
-	descr="$1"
-	shift
-	if ! test_skip "$descr" "$@"
-	then
-		# Announce the script to reduce confusion about the
-		# test output that follows.
-		say_color "" " run $test_count: $descr ($*)"
-		# Run command; redirect its stderr to &4 as in
-		# test_run_, but keep its stdout on our stdout even in
-		# non-verbose mode.
-		"$@" 2>&4
-		if [ "$?" = 0 ]
-		then
-			test_ok_ "$descr"
-		else
-			test_failure_ "$descr" "$@"
-		fi
-	fi
-}
-
-# Like test_external, but in addition tests that the command generated
-# no output on stderr.
-test_external_without_stderr () {
-	# The temporary file has no (and must have no) security
-	# implications.
-	tmp="$TMPDIR"; if [ -z "$tmp" ]; then tmp=/tmp; fi
-	stderr="$tmp/git-external-stderr.$$.tmp"
-	test_external "$@" 4> "$stderr"
-	[ -f "$stderr" ] || error "Internal error: $stderr disappeared."
-	descr="no stderr: $1"
-	shift
-	say >&3 "expecting no stderr from previous command"
-	if [ ! -s "$stderr" ]; then
-		rm "$stderr"
-		test_ok_ "$descr"
-	else
-		if [ "$verbose" = t ]; then
-			output=`echo; echo Stderr is:; cat "$stderr"`
-		else
-			output=
-		fi
-		# rm first in case test_failure exits.
-		rm "$stderr"
-		test_failure_ "$descr" "$@" "$output"
-	fi
-}
-
-# This is not among top-level (test_expect_success | test_expect_failure)
-# but is a prefix that can be used in the test script, like:
-#
-#	test_expect_success 'complain and die' '
-#           do something &&
-#           do something else &&
-#	    test_must_fail git checkout ../outerspace
-#	'
-#
-# Writing this as "! git checkout ../outerspace" is wrong, because
-# the failure could be due to a segv.  We want a controlled failure.
-
-test_must_fail () {
-	"$@"
-	test $? -gt 0 -a $? -le 129 -o $? -gt 192
-}
-
-# test_cmp is a helper function to compare actual and expected output.
-# You can use it like:
-#
-#	test_expect_success 'foo works' '
-#		echo expected >expected &&
-#		foo >actual &&
-#		test_cmp expected actual
-#	'
-#
-# This could be written as either "cmp" or "diff -u", but:
-# - cmp's output is not nearly as easy to read as diff -u
-# - not all diff versions understand "-u"
-
-test_cmp() {
-	$GIT_TEST_CMP "$@"
-}
-
-# Most tests can use the created repository, but some may need to create more.
-# Usage: test_create_repo <directory>
-test_create_repo () {
-	test "$#" = 1 ||
-	error "bug in the test script: not 1 parameter to test-create-repo"
-	owd=`pwd`
-	repo="$1"
-	mkdir -p "$repo"
-	cd "$repo" || error "Cannot setup test environment"
-	"$GIT_EXEC_PATH/git-init" "--template=$TEST_DIRECTORY/../templates/blt/" >&3 2>&4 ||
-	error "cannot run git init -- have you built things yet?"
-	mv .git/hooks .git/hooks-disabled
-	cd "$owd"
+# stub; perf-lib overrides it
+test_at_end_hook_ () {
+	:
 }
 
 test_done () {
 	GIT_EXIT_OK=t
-	test_results_dir="$TEST_DIRECTORY/test-results"
-	mkdir -p "$test_results_dir"
-	test_results_path="$test_results_dir/${0%.sh}-$$"
 
-	echo "total $test_count" >> $test_results_path
-	echo "success $test_success" >> $test_results_path
-	echo "fixed $test_fixed" >> $test_results_path
-	echo "broken $test_broken" >> $test_results_path
-	echo "failed $test_failure" >> $test_results_path
-	echo "" >> $test_results_path
+	if test -z "$HARNESS_ACTIVE"; then
+		test_results_dir="$TEST_OUTPUT_DIRECTORY/test-results"
+		mkdir -p "$test_results_dir"
+		test_results_path="$test_results_dir/${0%.sh}-$$.counts"
+
+		cat >>"$test_results_path" <<-EOF
+		total $test_count
+		success $test_success
+		fixed $test_fixed
+		broken $test_broken
+		failed $test_failure
+
+		EOF
+	fi
 
 	if test "$test_fixed" != 0
 	then
-		say_color pass "fixed $test_fixed known breakage(s)"
+		say_color pass "# fixed $test_fixed known breakage(s)"
 	fi
 	if test "$test_broken" != 0
 	then
-		say_color error "still have $test_broken known breakage(s)"
+		say_color error "# still have $test_broken known breakage(s)"
 		msg="remaining $(($test_count-$test_broken)) test(s)"
 	else
 		msg="$test_count test(s)"
 	fi
 	case "$test_failure" in
 	0)
-		say_color pass "passed all $msg"
+		# Maybe print SKIP message
+		[ -z "$skip_all" ] || skip_all=" # SKIP $skip_all"
+
+		if test $test_external_has_tap -eq 0; then
+			say_color pass "# passed all $msg"
+			say "1..$test_count$skip_all"
+		fi
 
 		test -d "$remove_trash" &&
 		cd "$(dirname "$remove_trash")" &&
 		rm -rf "$(basename "$remove_trash")"
 
+		test_at_end_hook_
+
 		exit 0 ;;
 
 	*)
-		say_color error "failed $test_failure among $msg"
+		if test $test_external_has_tap -eq 0; then
+			say_color error "# failed $test_failure among $msg"
+			say "1..$test_count"
+		fi
+
 		exit 1 ;;
 
 	esac
@@ -547,20 +382,23 @@
 
 # Test the binaries we have just built.  The tests are kept in
 # t/ subdirectory and are run in 'trash directory' subdirectory.
-TEST_DIRECTORY=$(pwd)
-if test -z "$valgrind"
+if test -z "$TEST_DIRECTORY"
 then
-	if test -z "$GIT_TEST_INSTALLED"
-	then
-		PATH=$TEST_DIRECTORY/..:$PATH
-		GIT_EXEC_PATH=$TEST_DIRECTORY/..
-	else
-		GIT_EXEC_PATH=$($GIT_TEST_INSTALLED/git --exec-path)  ||
-		error "Cannot run git from $GIT_TEST_INSTALLED."
-		PATH=$GIT_TEST_INSTALLED:$TEST_DIRECTORY/..:$PATH
-		GIT_EXEC_PATH=${GIT_TEST_EXEC_PATH:-$GIT_EXEC_PATH}
-	fi
-else
+	# We allow tests to override this, in case they want to run tests
+	# outside of t/, e.g. for running tests on the test library
+	# itself.
+	TEST_DIRECTORY=$(pwd)
+fi
+if test -z "$TEST_OUTPUT_DIRECTORY"
+then
+	# Similarly, override this to store the test-results subdir
+	# elsewhere
+	TEST_OUTPUT_DIRECTORY=$TEST_DIRECTORY
+fi
+GIT_BUILD_DIR="$TEST_DIRECTORY"/..
+
+if test -n "$valgrind"
+then
 	make_symlink () {
 		test -h "$2" &&
 		test "$1" = "$(readlink "$2")" || {
@@ -581,11 +419,16 @@
 	}
 
 	make_valgrind_symlink () {
-		# handle only executables
-		test -x "$1" || return
+		# handle only executables, unless they are shell libraries that
+		# need to be in the exec-path.  We will just use "#!" as a
+		# guess for a shell-script, since we have no idea what the user
+		# may have configured as the shell path.
+		test -x "$1" ||
+		test "#!" = "$(head -c 2 <"$1")" ||
+		return;
 
 		base=$(basename "$1")
-		symlink_target=$TEST_DIRECTORY/../$base
+		symlink_target=$GIT_BUILD_DIR/$base
 		# do not override scripts
 		if test -x "$symlink_target" &&
 		    test ! -d "$symlink_target" &&
@@ -604,10 +447,12 @@
 	# override all git executables in TEST_DIRECTORY/..
 	GIT_VALGRIND=$TEST_DIRECTORY/valgrind
 	mkdir -p "$GIT_VALGRIND"/bin
-	for file in $TEST_DIRECTORY/../git* $TEST_DIRECTORY/../test-*
+	for file in $GIT_BUILD_DIR/git* $GIT_BUILD_DIR/test-*
 	do
 		make_valgrind_symlink $file
 	done
+	# special-case the mergetools loadables
+	make_symlink "$GIT_BUILD_DIR"/mergetools "$GIT_VALGRIND/bin/mergetools"
 	OLDIFS=$IFS
 	IFS=:
 	for path in $PATH
@@ -622,37 +467,88 @@
 	PATH=$GIT_VALGRIND/bin:$PATH
 	GIT_EXEC_PATH=$GIT_VALGRIND/bin
 	export GIT_VALGRIND
+elif test -n "$GIT_TEST_INSTALLED" ; then
+	GIT_EXEC_PATH=$($GIT_TEST_INSTALLED/git --exec-path)  ||
+	error "Cannot run git from $GIT_TEST_INSTALLED."
+	PATH=$GIT_TEST_INSTALLED:$GIT_BUILD_DIR:$PATH
+	GIT_EXEC_PATH=${GIT_TEST_EXEC_PATH:-$GIT_EXEC_PATH}
+else # normal case, use ../bin-wrappers only unless $with_dashes:
+	git_bin_dir="$GIT_BUILD_DIR/bin-wrappers"
+	if ! test -x "$git_bin_dir/git" ; then
+		if test -z "$with_dashes" ; then
+			say "$git_bin_dir/git is not executable; using GIT_EXEC_PATH"
+		fi
+		with_dashes=t
+	fi
+	PATH="$git_bin_dir:$PATH"
+	GIT_EXEC_PATH=$GIT_BUILD_DIR
+	if test -n "$with_dashes" ; then
+		PATH="$GIT_BUILD_DIR:$PATH"
+	fi
 fi
-GIT_TEMPLATE_DIR=$(pwd)/../templates/blt
+GIT_TEMPLATE_DIR="$GIT_BUILD_DIR"/templates/blt
 unset GIT_CONFIG
 GIT_CONFIG_NOSYSTEM=1
-GIT_CONFIG_NOGLOBAL=1
-export PATH GIT_EXEC_PATH GIT_TEMPLATE_DIR GIT_CONFIG_NOSYSTEM GIT_CONFIG_NOGLOBAL
+GIT_ATTR_NOSYSTEM=1
+export PATH GIT_EXEC_PATH GIT_TEMPLATE_DIR GIT_CONFIG_NOSYSTEM GIT_ATTR_NOSYSTEM
 
-GITPERLLIB=$(pwd)/../perl/blib/lib:$(pwd)/../perl/blib/arch/auto/Git
+. "$GIT_BUILD_DIR"/GIT-BUILD-OPTIONS
+
+export PERL_PATH
+
+if test -z "$GIT_TEST_CMP"
+then
+	if test -n "$GIT_TEST_CMP_USE_COPIED_CONTEXT"
+	then
+		GIT_TEST_CMP="$DIFF -c"
+	else
+		GIT_TEST_CMP="$DIFF -u"
+	fi
+fi
+
+GITPERLLIB="$GIT_BUILD_DIR"/perl/blib/lib:"$GIT_BUILD_DIR"/perl/blib/arch/auto/Git
 export GITPERLLIB
-test -d ../templates/blt || {
+test -d "$GIT_BUILD_DIR"/templates/blt || {
 	error "You haven't built things yet, have you?"
 }
 
-if ! test -x ../test-chmtime; then
+if test -z "$GIT_TEST_INSTALLED" && test -z "$NO_PYTHON"
+then
+	GITPYTHONLIB="$GIT_BUILD_DIR/git_remote_helpers/build/lib"
+	export GITPYTHONLIB
+	test -d "$GIT_BUILD_DIR"/git_remote_helpers/build || {
+		error "You haven't built git_remote_helpers yet, have you?"
+	}
+fi
+
+if ! test -x "$GIT_BUILD_DIR"/test-chmtime; then
 	echo >&2 'You need to build test-chmtime:'
 	echo >&2 'Run "make test-chmtime" in the source (toplevel) directory'
 	exit 1
 fi
 
-. ../GIT-BUILD-OPTIONS
-
 # Test repository
 test="trash directory.$(basename "$0" .sh)"
-test ! -z "$debug" || remove_trash="$TEST_DIRECTORY/$test"
+test -n "$root" && test="$root/$test"
+case "$test" in
+/*) TRASH_DIRECTORY="$test" ;;
+ *) TRASH_DIRECTORY="$TEST_OUTPUT_DIRECTORY/$test" ;;
+esac
+test ! -z "$debug" || remove_trash=$TRASH_DIRECTORY
 rm -fr "$test" || {
 	GIT_EXIT_OK=t
 	echo >&5 "FATAL: Cannot prepare test area"
 	exit 1
 }
 
-test_create_repo "$test"
+HOME="$TRASH_DIRECTORY"
+export HOME
+
+if test -z "$TEST_NO_CREATE_REPO"; then
+	test_create_repo "$test"
+else
+	mkdir -p "$test"
+fi
 # Use -P to resolve symlinks in our working directory so that the cwd
 # in subprocesses like git equals our $PWD (for pathname comparisons).
 cd -P "$test" || exit 1
@@ -661,22 +557,29 @@
 this_test=${this_test%%-*}
 for skp in $GIT_SKIP_TESTS
 do
-	to_skip=
-	for skp in $GIT_SKIP_TESTS
-	do
-		case "$this_test" in
-		$skp)
-			to_skip=t
-		esac
-	done
-	case "$to_skip" in
-	t)
+	case "$this_test" in
+	$skp)
 		say_color skip >&3 "skipping test $this_test altogether"
-		say_color skip "skip all tests in $this_test"
+		skip_all="skip all tests in $this_test"
 		test_done
 	esac
 done
 
+# Provide an implementation of the 'yes' utility
+yes () {
+	if test $# = 0
+	then
+		y=y
+	else
+		y="$*"
+	fi
+
+	while echo "$y"
+	do
+		:
+	done
+}
+
 # Fix some commands on Windows
 case $(uname -s) in
 *MINGW*)
@@ -697,16 +600,69 @@
 	# no POSIX permissions
 	# backslashes in pathspec are converted to '/'
 	# exec does not inherit the PID
+	test_set_prereq MINGW
+	test_set_prereq SED_STRIPS_CR
+	;;
+*CYGWIN*)
+	test_set_prereq POSIXPERM
+	test_set_prereq EXECKEEPSPID
+	test_set_prereq NOT_MINGW
+	test_set_prereq SED_STRIPS_CR
 	;;
 *)
 	test_set_prereq POSIXPERM
 	test_set_prereq BSLASHPSPEC
 	test_set_prereq EXECKEEPSPID
+	test_set_prereq NOT_MINGW
 	;;
 esac
 
+( COLUMNS=1 && test $COLUMNS = 1 ) && test_set_prereq COLUMNS_CAN_BE_1
 test -z "$NO_PERL" && test_set_prereq PERL
+test -z "$NO_PYTHON" && test_set_prereq PYTHON
+test -n "$USE_LIBPCRE" && test_set_prereq LIBPCRE
+test -z "$NO_GETTEXT" && test_set_prereq GETTEXT
+
+# Can we rely on git's output in the C locale?
+if test -n "$GETTEXT_POISON"
+then
+	GIT_GETTEXT_POISON=YesPlease
+	export GIT_GETTEXT_POISON
+	test_set_prereq GETTEXT_POISON
+else
+	test_set_prereq C_LOCALE_OUTPUT
+fi
+
+# Use this instead of test_cmp to compare files that contain expected and
+# actual output from git commands that can be translated.  When running
+# under GETTEXT_POISON this pretends that the command produced expected
+# results.
+test_i18ncmp () {
+	test -n "$GETTEXT_POISON" || test_cmp "$@"
+}
+
+# Use this instead of "grep expected-string actual" to see if the
+# output from a git command that can be translated either contains an
+# expected string, or does not contain an unwanted one.  When running
+# under GETTEXT_POISON this pretends that the command produced expected
+# results.
+test_i18ngrep () {
+	if test -n "$GETTEXT_POISON"
+	then
+	    : # pretend success
+	elif test "x!" = "x$1"
+	then
+		shift
+		! grep "$@"
+	else
+		grep "$@"
+	fi
+}
 
 # test whether the filesystem supports symbolic links
 ln -s x y 2>/dev/null && test -h y 2>/dev/null && test_set_prereq SYMLINKS
 rm -f y
+
+# When the tests are run as root, permission tests will report that
+# things are writable when they shouldn't be.
+test -w / || test_set_prereq SANITY
diff --git a/t/test-terminal.perl b/t/test-terminal.perl
new file mode 100755
index 0000000..10172ae
--- /dev/null
+++ b/t/test-terminal.perl
@@ -0,0 +1,80 @@
+#!/usr/bin/perl
+use 5.008;
+use strict;
+use warnings;
+use IO::Pty;
+use File::Copy;
+
+# Run @$argv in the background with stdio redirected to $out and $err.
+sub start_child {
+	my ($argv, $out, $err) = @_;
+	my $pid = fork;
+	if (not defined $pid) {
+		die "fork failed: $!"
+	} elsif ($pid == 0) {
+		open STDOUT, ">&", $out;
+		open STDERR, ">&", $err;
+		close $out;
+		exec(@$argv) or die "cannot exec '$argv->[0]': $!"
+	}
+	return $pid;
+}
+
+# Wait for $pid to finish.
+sub finish_child {
+	# Simplified from wait_or_whine() in run-command.c.
+	my ($pid) = @_;
+
+	my $waiting = waitpid($pid, 0);
+	if ($waiting < 0) {
+		die "waitpid failed: $!";
+	} elsif ($? & 127) {
+		my $code = $? & 127;
+		warn "died of signal $code";
+		return $code - 128;
+	} else {
+		return $? >> 8;
+	}
+}
+
+sub xsendfile {
+	my ($out, $in) = @_;
+
+	# Note: the real sendfile() cannot read from a terminal.
+
+	# It is unspecified by POSIX whether reads
+	# from a disconnected terminal will return
+	# EIO (as in AIX 4.x, IRIX, and Linux) or
+	# end-of-file.  Either is fine.
+	copy($in, $out, 4096) or $!{EIO} or die "cannot copy from child: $!";
+}
+
+sub copy_stdio {
+	my ($out, $err) = @_;
+	my $pid = fork;
+	defined $pid or die "fork failed: $!";
+	if (!$pid) {
+		close($out);
+		xsendfile(\*STDERR, $err);
+		exit 0;
+	}
+	close($err);
+	xsendfile(\*STDOUT, $out);
+	finish_child($pid) == 0
+		or exit 1;
+}
+
+if ($#ARGV < 1) {
+	die "usage: test-terminal program args";
+}
+my $master_out = new IO::Pty;
+my $master_err = new IO::Pty;
+$master_out->set_raw();
+$master_err->set_raw();
+$master_out->slave->set_raw();
+$master_err->slave->set_raw();
+my $pid = start_child(\@ARGV, $master_out->slave, $master_err->slave);
+close $master_out->slave;
+close $master_err->slave;
+copy_stdio($master_out, $master_err);
+exit(finish_child($pid));
diff --git a/t/test9200a.png b/t/test9200a.png
deleted file mode 100644
index 7b181d1..0000000
--- a/t/test9200a.png
+++ /dev/null
Binary files differ
diff --git a/t/valgrind/default.supp b/t/valgrind/default.supp
index 9e013fa..0a6724f 100644
--- a/t/valgrind/default.supp
+++ b/t/valgrind/default.supp
@@ -43,3 +43,9 @@
 	fun:write_buffer
 	fun:write_loose_object
 }
+
+{
+	ignore-sse-strlen-invalid-read-size
+	Memcheck:Addr4
+	fun:copy_ref
+}
diff --git a/tag.c b/tag.c
index 4470d2b..78d272b 100644
--- a/tag.c
+++ b/tag.c
@@ -4,6 +4,9 @@
 #include "tree.h"
 #include "blob.h"
 
+#define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----"
+#define PGP_MESSAGE "-----BEGIN PGP MESSAGE-----"
+
 const char *tag_type = "tag";
 
 struct object *deref_tag(struct object *o, const char *warn, int warnlen)
@@ -21,6 +24,18 @@
 	return o;
 }
 
+struct object *deref_tag_noverify(struct object *o)
+{
+	while (o && o->type == OBJ_TAG) {
+		o = parse_object(o->sha1);
+		if (o && o->type == OBJ_TAG && ((struct tag *)o)->tagged)
+			o = ((struct tag *)o)->tagged;
+		else
+			o = NULL;
+	}
+	return o;
+}
+
 struct tag *lookup_tag(const unsigned char *sha1)
 {
 	struct object *obj = lookup_object(sha1);
@@ -28,51 +43,58 @@
 		return create_object(sha1, OBJ_TAG, alloc_tag_node());
 	if (!obj->type)
 		obj->type = OBJ_TAG;
-        if (obj->type != OBJ_TAG) {
-                error("Object %s is a %s, not a tag",
-                      sha1_to_hex(sha1), typename(obj->type));
-                return NULL;
-        }
-        return (struct tag *) obj;
+	if (obj->type != OBJ_TAG) {
+		error("Object %s is a %s, not a tag",
+		      sha1_to_hex(sha1), typename(obj->type));
+		return NULL;
+	}
+	return (struct tag *) obj;
 }
 
-int parse_tag_buffer(struct tag *item, void *data, unsigned long size)
+static unsigned long parse_tag_date(const char *buf, const char *tail)
 {
-	int typelen, taglen;
-	unsigned char sha1[20];
-	const char *type_line, *tag_line, *sig_line;
-	char type[20];
-	const char *start = data;
+	const char *dateptr;
 
-        if (item->object.parsed)
-                return 0;
-        item->object.parsed = 1;
+	while (buf < tail && *buf++ != '>')
+		/* nada */;
+	if (buf >= tail)
+		return 0;
+	dateptr = buf;
+	while (buf < tail && *buf++ != '\n')
+		/* nada */;
+	if (buf >= tail)
+		return 0;
+	/* dateptr < buf && buf[-1] == '\n', so strtoul will stop at buf-1 */
+	return strtoul(dateptr, NULL, 10);
+}
+
+int parse_tag_buffer(struct tag *item, const void *data, unsigned long size)
+{
+	unsigned char sha1[20];
+	char type[20];
+	const char *bufptr = data;
+	const char *tail = bufptr + size;
+	const char *nl;
+
+	if (item->object.parsed)
+		return 0;
+	item->object.parsed = 1;
 
 	if (size < 64)
 		return -1;
-	if (memcmp("object ", data, 7) || get_sha1_hex((char *) data + 7, sha1))
+	if (memcmp("object ", bufptr, 7) || get_sha1_hex(bufptr + 7, sha1) || bufptr[47] != '\n')
 		return -1;
+	bufptr += 48; /* "object " + sha1 + "\n" */
 
-	type_line = (char *) data + 48;
-	if (memcmp("\ntype ", type_line-1, 6))
+	if (prefixcmp(bufptr, "type "))
 		return -1;
-
-	tag_line = memchr(type_line, '\n', size - (type_line - start));
-	if (!tag_line || memcmp("tag ", ++tag_line, 4))
+	bufptr += 5;
+	nl = memchr(bufptr, '\n', tail - bufptr);
+	if (!nl || sizeof(type) <= (nl - bufptr))
 		return -1;
-
-	sig_line = memchr(tag_line, '\n', size - (tag_line - start));
-	if (!sig_line)
-		return -1;
-	sig_line++;
-
-	typelen = tag_line - type_line - strlen("type \n");
-	if (typelen >= 20)
-		return -1;
-	memcpy(type, type_line + 5, typelen);
-	type[typelen] = '\0';
-	taglen = sig_line - tag_line - strlen("tag \n");
-	item->tag = xmemdupz(tag_line + 4, taglen);
+	strncpy(type, bufptr, nl - bufptr);
+	type[nl - bufptr] = '\0';
+	bufptr = nl + 1;
 
 	if (!strcmp(type, blob_type)) {
 		item->tagged = &lookup_blob(sha1)->object;
@@ -87,6 +109,22 @@
 		item->tagged = NULL;
 	}
 
+	if (bufptr + 4 < tail && !prefixcmp(bufptr, "tag "))
+		; 		/* good */
+	else
+		return -1;
+	bufptr += 4;
+	nl = memchr(bufptr, '\n', tail - bufptr);
+	if (!nl)
+		return -1;
+	item->tag = xmemdupz(bufptr, nl - bufptr);
+	bufptr = nl + 1;
+
+	if (bufptr + 7 < tail && !prefixcmp(bufptr, "tagger "))
+		item->date = parse_tag_date(bufptr, tail);
+	else
+		item->date = 0;
+
 	return 0;
 }
 
@@ -112,3 +150,20 @@
 	free(data);
 	return ret;
 }
+
+/*
+ * Look at a signed tag object, and return the offset where
+ * the embedded detached signature begins, or the end of the
+ * data when there is no such signature.
+ */
+size_t parse_signature(const char *buf, unsigned long size)
+{
+	char *eol;
+	size_t len = 0;
+	while (len < size && prefixcmp(buf + len, PGP_SIGNATURE) &&
+			prefixcmp(buf + len, PGP_MESSAGE)) {
+		eol = memchr(buf + len, '\n', size - len);
+		len += eol ? eol - (buf + len) + 1 : size - len;
+	}
+	return len;
+}
diff --git a/tag.h b/tag.h
index 7a0cb00..bc8a1e4 100644
--- a/tag.h
+++ b/tag.h
@@ -9,12 +9,14 @@
 	struct object object;
 	struct object *tagged;
 	char *tag;
-	char *signature; /* not actually implemented */
+	unsigned long date;
 };
 
 extern struct tag *lookup_tag(const unsigned char *sha1);
-extern int parse_tag_buffer(struct tag *item, void *data, unsigned long size);
+extern int parse_tag_buffer(struct tag *item, const void *data, unsigned long size);
 extern int parse_tag(struct tag *item);
 extern struct object *deref_tag(struct object *, const char *, int);
+extern struct object *deref_tag_noverify(struct object *);
+extern size_t parse_signature(const char *buf, unsigned long size);
 
 #endif /* TAG_H */
diff --git a/templates/Makefile b/templates/Makefile
index a12c6e2..d22a71a 100644
--- a/templates/Makefile
+++ b/templates/Makefile
@@ -11,6 +11,16 @@
 template_instdir ?= $(prefix)/share/git-core/templates
 # DESTDIR=
 
+ifndef SHELL_PATH
+	SHELL_PATH = /bin/sh
+endif
+ifndef PERL_PATH
+	PERL_PATH = perl
+endif
+
+SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
+PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
+
 # Shell quote (do not use $(call) to accommodate ancient setups);
 DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
 template_instdir_SQ = $(subst ','\'',$(template_instdir))
@@ -33,8 +43,11 @@
 		case "$$boilerplate" in \
 		*--) continue;; \
 		esac && \
-		cp $$boilerplate blt/$$dst && \
-		if test -x "blt/$$dst"; then rx=rx; else rx=r; fi && \
+		sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
+		    -e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \
+		    -e 's|@PERL_PATH@|$(PERL_PATH_SQ)|g' $$boilerplate > \
+			blt/$$dst && \
+		if test -x "$$boilerplate"; then rx=rx; else rx=r; fi && \
 		chmod a+$$rx "blt/$$dst" || exit; \
 	done && \
 	date >$@
@@ -50,4 +63,4 @@
 install: all
 	$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(template_instdir_SQ)'
 	(cd blt && $(TAR) cf - .) | \
-	(cd '$(DESTDIR_SQ)$(template_instdir_SQ)' && umask 022 && $(TAR) xfo -)
+	(cd '$(DESTDIR_SQ)$(template_instdir_SQ)' && umask 022 && $(TAR) xof -)
diff --git a/templates/hooks--commit-msg.sample b/templates/hooks--commit-msg.sample
index 6ef1d29..b58d118 100755
--- a/templates/hooks--commit-msg.sample
+++ b/templates/hooks--commit-msg.sample
@@ -1,7 +1,7 @@
 #!/bin/sh
 #
 # An example hook script to check the commit log message.
-# Called by git-commit with one argument, the name of the file
+# Called by "git commit" with one argument, the name of the file
 # that has the commit message.  The hook should exit with non-zero
 # status after issuing an appropriate message if it wants to stop the
 # commit.  The hook is allowed to edit the commit message file.
diff --git a/templates/hooks--post-commit.sample b/templates/hooks--post-commit.sample
deleted file mode 100755
index 2266821..0000000
--- a/templates/hooks--post-commit.sample
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/sh
-#
-# An example hook script that is called after a successful
-# commit is made.
-#
-# To enable this hook, rename this file to "post-commit".
-
-: Nothing
diff --git a/templates/hooks--post-receive.sample b/templates/hooks--post-receive.sample
deleted file mode 100755
index 18d2e0f..0000000
--- a/templates/hooks--post-receive.sample
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/sh
-#
-# An example hook script for the "post-receive" event.
-#
-# The "post-receive" script is run after receive-pack has accepted a pack
-# and the repository has been updated.  It is passed arguments in through
-# stdin in the form
-#  <oldrev> <newrev> <refname>
-# For example:
-#  aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master
-#
-# see contrib/hooks/ for an sample, or uncomment the next line and
-# rename the file to "post-receive".
-
-#. /usr/share/doc/git-core/contrib/hooks/post-receive-email
diff --git a/templates/hooks--post-update.sample b/templates/hooks--post-update.sample
index 5323b56..ec17ec1 100755
--- a/templates/hooks--post-update.sample
+++ b/templates/hooks--post-update.sample
@@ -5,4 +5,4 @@
 #
 # To enable this hook, rename this file to "post-update".
 
-exec git-update-server-info
+exec git update-server-info
diff --git a/templates/hooks--pre-commit.sample b/templates/hooks--pre-commit.sample
index b11ad6a..18c4829 100755
--- a/templates/hooks--pre-commit.sample
+++ b/templates/hooks--pre-commit.sample
@@ -1,28 +1,42 @@
 #!/bin/sh
 #
 # An example hook script to verify what is about to be committed.
-# Called by git-commit with no arguments.  The hook should
+# Called by "git commit" with no arguments.  The hook should
 # exit with non-zero status after issuing an appropriate message if
 # it wants to stop the commit.
 #
 # To enable this hook, rename this file to "pre-commit".
 
+if git rev-parse --verify HEAD >/dev/null 2>&1
+then
+	against=HEAD
+else
+	# Initial commit: diff against an empty tree object
+	against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
+fi
+
 # If you want to allow non-ascii filenames set this variable to true.
 allownonascii=$(git config hooks.allownonascii)
 
+# Redirect output to stderr.
+exec 1>&2
+
 # Cross platform projects tend to avoid non-ascii filenames; prevent
 # them from being added to the repository. We exploit the fact that the
 # printable range starts at the space character and ends with tilde.
 if [ "$allownonascii" != "true" ] &&
-	test "$(git diff --cached --name-only --diff-filter=A -z |
-	  LC_ALL=C tr -d '[ -~]\0')"
+	# Note that the use of brackets around a tr range is ok here, (it's
+	# even required, for portability to Solaris 10's /usr/bin/tr), since
+	# the square bracket bytes happen to fall in the designated range.
+	test $(git diff --cached --name-only --diff-filter=A -z $against |
+	  LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
 then
-	echo "Error: Attempt to add a non-ascii filename."
+	echo "Error: Attempt to add a non-ascii file name."
 	echo
-	echo "This can cause problems if you want to work together"
-	echo "with people on other platforms than you."
+	echo "This can cause problems if you want to work"
+	echo "with people on other platforms."
 	echo
-	echo "To be portable it is adviseable to rename the file ..."
+	echo "To be portable it is advisable to rename the file ..."
 	echo
 	echo "If you know what you are doing you can disable this"
 	echo "check using:"
@@ -32,12 +46,5 @@
 	exit 1
 fi
 
-if git-rev-parse --verify HEAD >/dev/null 2>&1
-then
-	against=HEAD
-else
-	# Initial commit: diff against an empty tree object
-	against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
-fi
-
+# If there are whitespace errors, print the offending file names and fail.
 exec git diff-index --check --cached $against --
diff --git a/templates/hooks--pre-rebase.sample b/templates/hooks--pre-rebase.sample
index be1b06e..053f111 100755
--- a/templates/hooks--pre-rebase.sample
+++ b/templates/hooks--pre-rebase.sample
@@ -2,7 +2,7 @@
 #
 # Copyright (c) 2006, 2008 Junio C Hamano
 #
-# The "pre-rebase" hook is run just before "git-rebase" starts doing
+# The "pre-rebase" hook is run just before "git rebase" starts doing
 # its job, and can prevent the command from running by exiting with
 # non-zero status.
 #
@@ -43,7 +43,7 @@
 }
 
 # Is topic fully merged to master?
-not_in_master=`git-rev-list --pretty=oneline ^master "$topic"`
+not_in_master=`git rev-list --pretty=oneline ^master "$topic"`
 if test -z "$not_in_master"
 then
 	echo >&2 "$topic is fully merged to master; better remove it."
@@ -51,11 +51,11 @@
 fi
 
 # Is topic ever merged to next?  If so you should not be rebasing it.
-only_next_1=`git-rev-list ^master "^$topic" ${publish} | sort`
-only_next_2=`git-rev-list ^master           ${publish} | sort`
+only_next_1=`git rev-list ^master "^$topic" ${publish} | sort`
+only_next_2=`git rev-list ^master           ${publish} | sort`
 if test "$only_next_1" = "$only_next_2"
 then
-	not_in_topic=`git-rev-list "^$topic" master`
+	not_in_topic=`git rev-list "^$topic" master`
 	if test -z "$not_in_topic"
 	then
 		echo >&2 "$topic is already up-to-date with master"
@@ -64,8 +64,8 @@
 		exit 0
 	fi
 else
-	not_in_next=`git-rev-list --pretty=oneline ^${publish} "$topic"`
-	perl -e '
+	not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"`
+	@PERL_PATH@ -e '
 		my $topic = $ARGV[0];
 		my $msg = "* $topic has commits already merged to public branch:\n";
 		my (%not_in_next) = map {
@@ -157,13 +157,13 @@
 
 To compute (1):
 
-	git-rev-list ^master ^topic next
-	git-rev-list ^master        next
+	git rev-list ^master ^topic next
+	git rev-list ^master        next
 
 	if these match, topic has not merged in next at all.
 
 To compute (2):
 
-	git-rev-list master..topic
+	git rev-list master..topic
 
 	if this is empty, it is fully merged to "master".
diff --git a/templates/hooks--prepare-commit-msg.sample b/templates/hooks--prepare-commit-msg.sample
index 3652424..86b8f22 100755
--- a/templates/hooks--prepare-commit-msg.sample
+++ b/templates/hooks--prepare-commit-msg.sample
@@ -1,7 +1,7 @@
 #!/bin/sh
 #
 # An example hook script to prepare the commit log message.
-# Called by git-commit with the name of the file that has the
+# Called by "git commit" with the name of the file that has the
 # commit message, followed by the description of the commit
 # message's source.  The hook's purpose is to edit the commit
 # message file.  If the hook fails with a non-zero status,
@@ -22,10 +22,10 @@
 
 case "$2,$3" in
   merge,)
-    perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;;
+    @PERL_PATH@ -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;;
 
 # ,|template,)
-#   perl -i.bak -pe '
+#   @PERL_PATH@ -i.bak -pe '
 #      print "\n" . `git diff --cached --name-status -r`
 #	 if /^#/ && $first++ == 0' "$1" ;;
 
diff --git a/templates/hooks--update.sample b/templates/hooks--update.sample
index fd63b2d..71ab04e 100755
--- a/templates/hooks--update.sample
+++ b/templates/hooks--update.sample
@@ -1,7 +1,7 @@
 #!/bin/sh
 #
 # An example hook script to blocks unannotated tags from entering.
-# Called by git-receive-pack with arguments: refname sha1-old sha1-new
+# Called by "git receive-pack" with arguments: refname sha1-old sha1-new
 #
 # To enable this hook, rename this file to "update".
 #
@@ -64,7 +64,7 @@
 if [ "$newrev" = "$zero" ]; then
 	newrev_type=delete
 else
-	newrev_type=$(git-cat-file -t $newrev)
+	newrev_type=$(git cat-file -t $newrev)
 fi
 
 case "$refname","$newrev_type" in
diff --git a/templates/info--exclude b/templates/info--exclude
index 2c87b72..a5196d1 100644
--- a/templates/info--exclude
+++ b/templates/info--exclude
@@ -1,4 +1,4 @@
-# git-ls-files --others --exclude-from=.git/info/exclude
+# git ls-files --others --exclude-from=.git/info/exclude
 # Lines that start with '#' are comments.
 # For a project mostly in C, the following would be a good set of
 # exclude patterns (uncomment them if you want to use them):
diff --git a/test-chmtime.c b/test-chmtime.c
index fe476cb..92713d1 100644
--- a/test-chmtime.c
+++ b/test-chmtime.c
@@ -1,7 +1,7 @@
 /*
  * This program can either change modification time of the given
  * file(s) or just print it. The program does not change atime nor
- * ctime (their values are explicitely preserved).
+ * ctime (their values are explicitly preserved).
  *
  * The mtime can be changed to an absolute value:
  *
diff --git a/test-ctype.c b/test-ctype.c
index 033c749..707a821 100644
--- a/test-ctype.c
+++ b/test-ctype.c
@@ -1,78 +1,42 @@
 #include "cache.h"
 
+static int rc;
 
-static int test_isdigit(int c)
+static void report_error(const char *class, int ch)
 {
-	return isdigit(c);
+	printf("%s classifies char %d (0x%02x) wrongly\n", class, ch, ch);
+	rc = 1;
 }
 
-static int test_isspace(int c)
+static int is_in(const char *s, int ch)
 {
-	return isspace(c);
+	/* We can't find NUL using strchr.  It's classless anyway. */
+	if (ch == '\0')
+		return 0;
+	return !!strchr(s, ch);
 }
 
-static int test_isalpha(int c)
-{
-	return isalpha(c);
-}
-
-static int test_isalnum(int c)
-{
-	return isalnum(c);
-}
-
-static int test_is_glob_special(int c)
-{
-	return is_glob_special(c);
-}
-
-static int test_is_regex_special(int c)
-{
-	return is_regex_special(c);
+#define TEST_CLASS(t,s) {			\
+	int i;					\
+	for (i = 0; i < 256; i++) {		\
+		if (is_in(s, i) != t(i))	\
+			report_error(#t, i);	\
+	}					\
 }
 
 #define DIGIT "0123456789"
 #define LOWER "abcdefghijklmnopqrstuvwxyz"
 #define UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
 
-static const struct ctype_class {
-	const char *name;
-	int (*test_fn)(int);
-	const char *members;
-} classes[] = {
-	{ "isdigit", test_isdigit, DIGIT },
-	{ "isspace", test_isspace, " \n\r\t" },
-	{ "isalpha", test_isalpha, LOWER UPPER },
-	{ "isalnum", test_isalnum, LOWER UPPER DIGIT },
-	{ "is_glob_special", test_is_glob_special, "*?[\\" },
-	{ "is_regex_special", test_is_regex_special, "$()*+.?[\\^{|" },
-	{ NULL }
-};
-
-static int test_class(const struct ctype_class *test)
-{
-	int i, rc = 0;
-
-	for (i = 0; i < 256; i++) {
-		int expected = i ? !!strchr(test->members, i) : 0;
-		int actual = test->test_fn(i);
-
-		if (actual != expected) {
-			rc = 1;
-			printf("%s classifies char %d (0x%02x) wrongly\n",
-			       test->name, i, i);
-		}
-	}
-	return rc;
-}
-
 int main(int argc, char **argv)
 {
-	const struct ctype_class *test;
-	int rc = 0;
-
-	for (test = classes; test->name; test++)
-		rc |= test_class(test);
+	TEST_CLASS(isdigit, DIGIT);
+	TEST_CLASS(isspace, " \n\r\t");
+	TEST_CLASS(isalpha, LOWER UPPER);
+	TEST_CLASS(isalnum, LOWER UPPER DIGIT);
+	TEST_CLASS(is_glob_special, "*?[\\");
+	TEST_CLASS(is_regex_special, "$()*+.?[\\^{|");
+	TEST_CLASS(is_pathspec_magic, "!\"#%&',-/:;<=>@_`~");
 
 	return rc;
 }
diff --git a/test-date.c b/test-date.c
index 62e8f23..10afaab 100644
--- a/test-date.c
+++ b/test-date.c
@@ -1,20 +1,71 @@
 #include "cache.h"
 
+static const char *usage_msg = "\n"
+"  test-date show [time_t]...\n"
+"  test-date parse [date]...\n"
+"  test-date approxidate [date]...\n";
+
+static void show_dates(char **argv, struct timeval *now)
+{
+	struct strbuf buf = STRBUF_INIT;
+
+	for (; *argv; argv++) {
+		time_t t = atoi(*argv);
+		show_date_relative(t, 0, now, &buf);
+		printf("%s -> %s\n", *argv, buf.buf);
+	}
+	strbuf_release(&buf);
+}
+
+static void parse_dates(char **argv, struct timeval *now)
+{
+	for (; *argv; argv++) {
+		char result[100];
+		unsigned long t;
+		int tz;
+
+		result[0] = 0;
+		parse_date(*argv, result, sizeof(result));
+		if (sscanf(result, "%lu %d", &t, &tz) == 2)
+			printf("%s -> %s\n",
+			       *argv, show_date(t, tz, DATE_ISO8601));
+		else
+			printf("%s -> bad\n", *argv);
+	}
+}
+
+static void parse_approxidate(char **argv, struct timeval *now)
+{
+	for (; *argv; argv++) {
+		time_t t;
+		t = approxidate_relative(*argv, now);
+		printf("%s -> %s\n", *argv, show_date(t, 0, DATE_ISO8601));
+	}
+}
+
 int main(int argc, char **argv)
 {
-	int i;
+	struct timeval now;
+	const char *x;
 
-	for (i = 1; i < argc; i++) {
-		char result[100];
-		time_t t;
-
-		memcpy(result, "bad", 4);
-		parse_date(argv[i], result, sizeof(result));
-		t = strtoul(result, NULL, 0);
-		printf("%s -> %s -> %s", argv[i], result, ctime(&t));
-
-		t = approxidate(argv[i]);
-		printf("%s -> %s\n", argv[i], ctime(&t));
+	x = getenv("TEST_DATE_NOW");
+	if (x) {
+		now.tv_sec = atoi(x);
+		now.tv_usec = 0;
 	}
+	else
+		gettimeofday(&now, NULL);
+
+	argv++;
+	if (!*argv)
+		usage(usage_msg);
+	if (!strcmp(*argv, "show"))
+		show_dates(argv+1, &now);
+	else if (!strcmp(*argv, "parse"))
+		parse_dates(argv+1, &now);
+	else if (!strcmp(*argv, "approxidate"))
+		parse_approxidate(argv+1, &now);
+	else
+		usage(usage_msg);
 	return 0;
 }
diff --git a/test-delta.c b/test-delta.c
index 3d885ff..af40a3c 100644
--- a/test-delta.c
+++ b/test-delta.c
@@ -1,7 +1,7 @@
 /*
  * test-delta.c: test code to exercise diff-delta.c and patch-delta.c
  *
- * (C) 2005 Nicolas Pitre <nico@cam.org>
+ * (C) 2005 Nicolas Pitre <nico@fluxnic.net>
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
diff --git a/test-dump-cache-tree.c b/test-dump-cache-tree.c
index 1f73f1e..a6ffdf3 100644
--- a/test-dump-cache-tree.c
+++ b/test-dump-cache-tree.c
@@ -59,6 +59,6 @@
 	struct cache_tree *another = cache_tree();
 	if (read_cache() < 0)
 		die("unable to read index file");
-	cache_tree_update(another, active_cache, active_nr, 0, 1);
+	cache_tree_update(another, active_cache, active_nr, WRITE_TREE_DRY_RUN);
 	return dump_cache_tree(active_cache_tree, another, "");
 }
diff --git a/test-genrandom.c b/test-genrandom.c
index 8ad276d..b3c28d9 100644
--- a/test-genrandom.c
+++ b/test-genrandom.c
@@ -4,8 +4,7 @@
  * Copyright (C) 2007 by Nicolas Pitre, licensed under the GPL version 2.
  */
 
-#include <stdio.h>
-#include <stdlib.h>
+#include "git-compat-util.h"
 
 int main(int argc, char *argv[])
 {
diff --git a/test-index-version.c b/test-index-version.c
new file mode 100644
index 0000000..bfaad9e
--- /dev/null
+++ b/test-index-version.c
@@ -0,0 +1,14 @@
+#include "cache.h"
+
+int main(int argc, const char **argv)
+{
+	struct cache_header hdr;
+	int version;
+
+	memset(&hdr,0,sizeof(hdr));
+	if (read(0, &hdr, sizeof(hdr)) != sizeof(hdr))
+		return 0;
+	version = ntohl(hdr.hdr_version);
+	printf("%d\n", version);
+	return 0;
+}
diff --git a/test-line-buffer.c b/test-line-buffer.c
new file mode 100644
index 0000000..ef1d7ba
--- /dev/null
+++ b/test-line-buffer.c
@@ -0,0 +1,91 @@
+/*
+ * test-line-buffer.c: code to exercise the svn importer's input helper
+ */
+
+#include "git-compat-util.h"
+#include "strbuf.h"
+#include "vcs-svn/line_buffer.h"
+
+static uint32_t strtouint32(const char *s)
+{
+	char *end;
+	uintmax_t n = strtoumax(s, &end, 10);
+	if (*s == '\0' || *end != '\0')
+		die("invalid count: %s", s);
+	return (uint32_t) n;
+}
+
+static void handle_command(const char *command, const char *arg, struct line_buffer *buf)
+{
+	switch (*command) {
+	case 'b':
+		if (!prefixcmp(command, "binary ")) {
+			struct strbuf sb = STRBUF_INIT;
+			strbuf_addch(&sb, '>');
+			buffer_read_binary(buf, &sb, strtouint32(arg));
+			fwrite(sb.buf, 1, sb.len, stdout);
+			strbuf_release(&sb);
+			return;
+		}
+	case 'c':
+		if (!prefixcmp(command, "copy ")) {
+			buffer_copy_bytes(buf, strtouint32(arg));
+			return;
+		}
+	case 's':
+		if (!prefixcmp(command, "skip ")) {
+			buffer_skip_bytes(buf, strtouint32(arg));
+			return;
+		}
+	default:
+		die("unrecognized command: %s", command);
+	}
+}
+
+static void handle_line(const char *line, struct line_buffer *stdin_buf)
+{
+	const char *arg = strchr(line, ' ');
+	if (!arg)
+		die("no argument in line: %s", line);
+	handle_command(line, arg + 1, stdin_buf);
+}
+
+int main(int argc, char *argv[])
+{
+	struct line_buffer stdin_buf = LINE_BUFFER_INIT;
+	struct line_buffer file_buf = LINE_BUFFER_INIT;
+	struct line_buffer *input = &stdin_buf;
+	const char *filename;
+	char *s;
+
+	if (argc == 1)
+		filename = NULL;
+	else if (argc == 2)
+		filename = argv[1];
+	else
+		usage("test-line-buffer [file | &fd] < script");
+
+	if (buffer_init(&stdin_buf, NULL))
+		die_errno("open error");
+	if (filename) {
+		if (*filename == '&') {
+			if (buffer_fdinit(&file_buf, strtouint32(filename + 1)))
+				die_errno("error opening fd %s", filename + 1);
+		} else {
+			if (buffer_init(&file_buf, filename))
+				die_errno("error opening %s", filename);
+		}
+		input = &file_buf;
+	}
+
+	while ((s = buffer_read_line(&stdin_buf)))
+		handle_line(s, input);
+
+	if (filename && buffer_deinit(&file_buf))
+		die("error reading from %s", filename);
+	if (buffer_deinit(&stdin_buf))
+		die("input error");
+	if (ferror(stdout))
+		die("output error");
+	return 0;
+}
diff --git a/test-mergesort.c b/test-mergesort.c
new file mode 100644
index 0000000..3f388b4
--- /dev/null
+++ b/test-mergesort.c
@@ -0,0 +1,52 @@
+#include "cache.h"
+#include "mergesort.h"
+
+struct line {
+	char *text;
+	struct line *next;
+};
+
+static void *get_next(const void *a)
+{
+	return ((const struct line *)a)->next;
+}
+
+static void set_next(void *a, void *b)
+{
+	((struct line *)a)->next = b;
+}
+
+static int compare_strings(const void *a, const void *b)
+{
+	const struct line *x = a, *y = b;
+	return strcmp(x->text, y->text);
+}
+
+int main(int argc, const char **argv)
+{
+	struct line *line, *p = NULL, *lines = NULL;
+	struct strbuf sb = STRBUF_INIT;
+
+	for (;;) {
+		if (strbuf_getwholeline(&sb, stdin, '\n'))
+			break;
+		line = xmalloc(sizeof(struct line));
+		line->text = strbuf_detach(&sb, NULL);
+		if (p) {
+			line->next = p->next;
+			p->next = line;
+		} else {
+			line->next = NULL;
+			lines = line;
+		}
+		p = line;
+	}
+
+	lines = llist_mergesort(lines, get_next, set_next, compare_strings);
+
+	while (lines) {
+		printf("%s", lines->text);
+		lines = lines->next;
+	}
+	return 0;
+}
diff --git a/test-mktemp.c b/test-mktemp.c
new file mode 100644
index 0000000..c8c5421
--- /dev/null
+++ b/test-mktemp.c
@@ -0,0 +1,14 @@
+/*
+ * test-mktemp.c: code to exercise the creation of temporary files
+ */
+#include "git-compat-util.h"
+
+int main(int argc, char *argv[])
+{
+	if (argc != 2)
+		usage("Expected 1 parameter defining the temporary file template");
+
+	xmkstemp(xstrdup(argv[1]));
+
+	return 0;
+}
diff --git a/test-parse-options.c b/test-parse-options.c
index efa734b..3c9510a 100644
--- a/test-parse-options.c
+++ b/test-parse-options.c
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "parse-options.h"
+#include "string-list.h"
 
 static int boolean = 0;
 static int integer = 0;
@@ -8,6 +9,8 @@
 static int verbose = 0, dry_run = 0, quiet = 0;
 static char *string = NULL;
 static char *file = NULL;
+static int ambiguous;
+static struct string_list list;
 
 static int length_callback(const struct option *opt, const char *arg, int unset)
 {
@@ -34,7 +37,11 @@
 		NULL
 	};
 	struct option options[] = {
-		OPT_BOOLEAN('b', "boolean", &boolean, "get a boolean"),
+		OPT_BOOL(0, "yes", &boolean, "get a boolean"),
+		OPT_BOOL('D', "no-doubt", &boolean, "begins with 'no-'"),
+		{ OPTION_SET_INT, 'B', "no-fear", &boolean, NULL,
+		  "be brave", PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 },
+		OPT_COUNTUP('b', "boolean", &boolean, "increment by one"),
 		OPT_BIT('4', "or4", &boolean,
 			"bitwise-or boolean with ...0100", 4),
 		OPT_NEGBIT(0, "neg-or4", &boolean, "same as --no-or4", 4),
@@ -45,25 +52,31 @@
 		OPT_DATE('t', NULL, &timestamp, "get timestamp of <time>"),
 		OPT_CALLBACK('L', "length", &integer, "str",
 			"get length of <str>", length_callback),
-		OPT_FILENAME('F', "file", &file, "set file to <FILE>"),
+		OPT_FILENAME('F', "file", &file, "set file to <file>"),
 		OPT_GROUP("String options"),
 		OPT_STRING('s', "string", &string, "string", "get a string"),
 		OPT_STRING(0, "string2", &string, "str", "get another string"),
 		OPT_STRING(0, "st", &string, "st", "get another string (pervert ordering)"),
 		OPT_STRING('o', NULL, &string, "str", "get another string"),
+		OPT_NOOP_NOARG(0, "obsolete"),
 		OPT_SET_PTR(0, "default-string", &string,
 			"set string to default", (unsigned long)"default"),
+		OPT_STRING_LIST(0, "list", &list, "str", "add str to list"),
 		OPT_GROUP("Magic arguments"),
 		OPT_ARGUMENT("quux", "means --quux"),
 		OPT_NUMBER_CALLBACK(&integer, "set integer to NUM",
 			number_callback),
-		{ OPTION_BOOLEAN, '+', NULL, &boolean, NULL, "same as -b",
+		{ OPTION_COUNTUP, '+', NULL, &boolean, NULL, "same as -b",
 		  PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH },
+		{ OPTION_COUNTUP, 0, "ambiguous", &ambiguous, NULL,
+		  "positive ambiguity", PARSE_OPT_NOARG | PARSE_OPT_NONEG },
+		{ OPTION_COUNTUP, 0, "no-ambiguous", &ambiguous, NULL,
+		  "negative ambiguity", PARSE_OPT_NOARG | PARSE_OPT_NONEG },
 		OPT_GROUP("Standard options"),
 		OPT__ABBREV(&abbrev),
-		OPT__VERBOSE(&verbose),
-		OPT__DRY_RUN(&dry_run),
-		OPT__QUIET(&quiet),
+		OPT__VERBOSE(&verbose, "be verbose"),
+		OPT__DRY_RUN(&dry_run, "dry run"),
+		OPT__QUIET(&quiet, "be quiet"),
 		OPT_END(),
 	};
 	int i;
@@ -80,6 +93,9 @@
 	printf("dry run: %s\n", dry_run ? "yes" : "no");
 	printf("file: %s\n", file ? file : "(not set)");
 
+	for (i = 0; i < list.nr; i++)
+		printf("list: %s\n", list.items[i].string);
+
 	for (i = 0; i < argc; i++)
 		printf("arg %02d: %s\n", i, argv[i]);
 
diff --git a/test-path-utils.c b/test-path-utils.c
index d261398..3bc20e9 100644
--- a/test-path-utils.c
+++ b/test-path-utils.c
@@ -11,9 +11,18 @@
 		return 0;
 	}
 
-	if (argc >= 2 && !strcmp(argv[1], "make_absolute_path")) {
+	if (argc >= 2 && !strcmp(argv[1], "real_path")) {
 		while (argc > 2) {
-			puts(make_absolute_path(argv[2]));
+			puts(real_path(argv[2]));
+			argc--;
+			argv++;
+		}
+		return 0;
+	}
+
+	if (argc >= 2 && !strcmp(argv[1], "absolute_path")) {
+		while (argc > 2) {
+			puts(absolute_path(argv[2]));
 			argc--;
 			argv++;
 		}
@@ -26,6 +35,19 @@
 		return 0;
 	}
 
+	if (argc >= 4 && !strcmp(argv[1], "prefix_path")) {
+		char *prefix = argv[2];
+		int prefix_len = strlen(prefix);
+		int nongit_ok;
+		setup_git_directory_gently(&nongit_ok);
+		while (argc > 3) {
+			puts(prefix_path(prefix, prefix_len, argv[3]));
+			argc--;
+			argv++;
+		}
+		return 0;
+	}
+
 	if (argc == 4 && !strcmp(argv[1], "strip_path_suffix")) {
 		char *prefix = strip_path_suffix(argv[2], argv[3]);
 		printf("%s\n", prefix ? prefix : "(null)");
diff --git a/test-revision-walking.c b/test-revision-walking.c
new file mode 100644
index 0000000..3ade02c
--- /dev/null
+++ b/test-revision-walking.c
@@ -0,0 +1,66 @@
+/*
+ * test-revision-walking.c: test revision walking API.
+ *
+ * (C) 2012 Heiko Voigt <hvoigt@hvoigt.net>
+ *
+ * This code is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "cache.h"
+#include "commit.h"
+#include "diff.h"
+#include "revision.h"
+
+static void print_commit(struct commit *commit)
+{
+	struct strbuf sb = STRBUF_INIT;
+	struct pretty_print_context ctx = {0};
+	ctx.date_mode = DATE_NORMAL;
+	format_commit_message(commit, " %m %s", &sb, &ctx);
+	printf("%s\n", sb.buf);
+	strbuf_release(&sb);
+}
+
+static int run_revision_walk(void)
+{
+	struct rev_info rev;
+	struct commit *commit;
+	const char *argv[] = {NULL, "--all", NULL};
+	int argc = ARRAY_SIZE(argv) - 1;
+	int got_revision = 0;
+
+	init_revisions(&rev, NULL);
+	setup_revisions(argc, argv, &rev, NULL);
+	if (prepare_revision_walk(&rev))
+		die("revision walk setup failed");
+
+	while ((commit = get_revision(&rev)) != NULL) {
+		print_commit(commit);
+		got_revision = 1;
+	}
+
+	reset_revision_walk();
+	return got_revision;
+}
+
+int main(int argc, char **argv)
+{
+	if (argc < 2)
+		return 1;
+
+	if (!strcmp(argv[1], "run-twice")) {
+		printf("1st\n");
+		if (!run_revision_walk())
+			return 1;
+		printf("2nd\n");
+		if (!run_revision_walk())
+			return 1;
+
+		return 0;
+	}
+
+	fprintf(stderr, "check usage\n");
+	return 1;
+}
diff --git a/test-run-command.c b/test-run-command.c
new file mode 100644
index 0000000..37918e1
--- /dev/null
+++ b/test-run-command.c
@@ -0,0 +1,37 @@
+/*
+ * test-run-command.c: test run command API.
+ *
+ * (C) 2009 Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
+ *
+ * This code is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "git-compat-util.h"
+#include "run-command.h"
+#include <string.h>
+#include <errno.h>
+
+int main(int argc, char **argv)
+{
+	struct child_process proc;
+
+	memset(&proc, 0, sizeof(proc));
+
+	if (argc < 3)
+		return 1;
+	proc.argv = (const char **)argv+2;
+
+	if (!strcmp(argv[1], "start-command-ENOENT")) {
+		if (start_command(&proc) < 0 && errno == ENOENT)
+			return 0;
+		fprintf(stderr, "FAIL %s\n", argv[1]);
+		return 1;
+	}
+	if (!strcmp(argv[1], "run-command"))
+		exit(run_command(&proc));
+
+	fprintf(stderr, "check usage\n");
+	return 1;
+}
diff --git a/test-scrap-cache-tree.c b/test-scrap-cache-tree.c
new file mode 100644
index 0000000..4728013
--- /dev/null
+++ b/test-scrap-cache-tree.c
@@ -0,0 +1,17 @@
+#include "cache.h"
+#include "tree.h"
+#include "cache-tree.h"
+
+static struct lock_file index_lock;
+
+int main(int ac, char **av)
+{
+	int fd = hold_locked_index(&index_lock, 1);
+	if (read_cache() < 0)
+		die("unable to read index file");
+	active_cache_tree = NULL;
+	if (write_cache(fd, active_cache, active_nr)
+	    || commit_lock_file(&index_lock))
+		die("unable to write index file");
+	return 0;
+}
diff --git a/test-subprocess.c b/test-subprocess.c
new file mode 100644
index 0000000..f2d4c0d
--- /dev/null
+++ b/test-subprocess.c
@@ -0,0 +1,20 @@
+#include "cache.h"
+#include "run-command.h"
+
+int main(int argc, const char **argv)
+{
+	struct child_process cp;
+	int nogit = 0;
+
+	setup_git_directory_gently(&nogit);
+	if (nogit)
+		die("No git repo found");
+	if (argc > 1 && !strcmp(argv[1], "--setup-work-tree")) {
+		setup_work_tree();
+		argv++;
+	}
+	memset(&cp, 0, sizeof(cp));
+	cp.git_cmd = 1;
+	cp.argv = argv + 1;
+	return run_command(&cp);
+}
diff --git a/test-svn-fe.c b/test-svn-fe.c
new file mode 100644
index 0000000..83633a2
--- /dev/null
+++ b/test-svn-fe.c
@@ -0,0 +1,52 @@
+/*
+ * test-svn-fe: Code to exercise the svn import lib
+ */
+
+#include "git-compat-util.h"
+#include "vcs-svn/svndump.h"
+#include "vcs-svn/svndiff.h"
+#include "vcs-svn/sliding_window.h"
+#include "vcs-svn/line_buffer.h"
+
+static const char test_svnfe_usage[] =
+	"test-svn-fe (<dumpfile> | [-d] <preimage> <delta> <len>)";
+
+static int apply_delta(int argc, char *argv[])
+{
+	struct line_buffer preimage = LINE_BUFFER_INIT;
+	struct line_buffer delta = LINE_BUFFER_INIT;
+	struct sliding_view preimage_view = SLIDING_VIEW_INIT(&preimage, -1);
+
+	if (argc != 5)
+		usage(test_svnfe_usage);
+
+	if (buffer_init(&preimage, argv[2]))
+		die_errno("cannot open preimage");
+	if (buffer_init(&delta, argv[3]))
+		die_errno("cannot open delta");
+	if (svndiff0_apply(&delta, (off_t) strtoull(argv[4], NULL, 0),
+					&preimage_view, stdout))
+		return 1;
+	if (buffer_deinit(&preimage))
+		die_errno("cannot close preimage");
+	if (buffer_deinit(&delta))
+		die_errno("cannot close delta");
+	strbuf_release(&preimage_view.buf);
+	return 0;
+}
+
+int main(int argc, char *argv[])
+{
+	if (argc == 2) {
+		if (svndump_init(argv[1]))
+			return 1;
+		svndump_read(NULL);
+		svndump_deinit();
+		svndump_reset();
+		return 0;
+	}
+
+	if (argc >= 2 && !strcmp(argv[1], "-d"))
+		return apply_delta(argc, argv);
+	usage(test_svnfe_usage);
+}
diff --git a/thread-utils.c b/thread-utils.c
index 55e7e29..7f4b76a 100644
--- a/thread-utils.c
+++ b/thread-utils.c
@@ -1,9 +1,7 @@
 #include "cache.h"
+#include "thread-utils.h"
 
-#ifdef _WIN32
-#  define WIN32_LEAN_AND_MEAN
-#  include <windows.h>
-#elif defined(hpux) || defined(__hpux) || defined(_hpux)
+#if defined(hpux) || defined(__hpux) || defined(_hpux)
 #  include <sys/pstat.h>
 #endif
 
@@ -46,3 +44,18 @@
 
 	return 1;
 }
+
+int init_recursive_mutex(pthread_mutex_t *m)
+{
+	pthread_mutexattr_t a;
+	int ret;
+
+	ret = pthread_mutexattr_init(&a);
+	if (!ret) {
+		ret = pthread_mutexattr_settype(&a, PTHREAD_MUTEX_RECURSIVE);
+		if (!ret)
+			ret = pthread_mutex_init(m, &a);
+		pthread_mutexattr_destroy(&a);
+	}
+	return ret;
+}
diff --git a/thread-utils.h b/thread-utils.h
index cce4b77..6fb98c3 100644
--- a/thread-utils.h
+++ b/thread-utils.h
@@ -1,6 +1,11 @@
 #ifndef THREAD_COMPAT_H
 #define THREAD_COMPAT_H
 
-extern int online_cpus(void);
+#ifndef NO_PTHREADS
+#include <pthread.h>
 
+extern int online_cpus(void);
+extern int init_recursive_mutex(pthread_mutex_t*);
+
+#endif
 #endif /* THREAD_COMPAT_H */
diff --git a/trace.c b/trace.c
index 4229ae1..d953416 100644
--- a/trace.c
+++ b/trace.c
@@ -25,10 +25,10 @@
 #include "cache.h"
 #include "quote.h"
 
-/* Get a trace file descriptor from GIT_TRACE env variable. */
-static int get_trace_fd(int *need_close)
+/* Get a trace file descriptor from "key" env variable. */
+static int get_trace_fd(const char *key, int *need_close)
 {
-	char *trace = getenv("GIT_TRACE");
+	char *trace = getenv(key);
 
 	if (!trace || !strcmp(trace, "") ||
 	    !strcmp(trace, "0") || !strcasecmp(trace, "false"))
@@ -50,10 +50,10 @@
 		return fd;
 	}
 
-	fprintf(stderr, "What does '%s' for GIT_TRACE mean?\n", trace);
+	fprintf(stderr, "What does '%s' for %s mean?\n", trace, key);
 	fprintf(stderr, "If you want to trace into a file, "
-		"then please set GIT_TRACE to an absolute pathname "
-		"(starting with /).\n");
+		"then please set %s to an absolute pathname "
+		"(starting with /).\n", key);
 	fprintf(stderr, "Defaulting to tracing on stderr...\n");
 
 	return STDERR_FILENO;
@@ -62,32 +62,44 @@
 static const char err_msg[] = "Could not trace into fd given by "
 	"GIT_TRACE environment variable";
 
+void trace_vprintf(const char *key, const char *fmt, va_list ap)
+{
+	struct strbuf buf = STRBUF_INIT;
+
+	if (!trace_want(key))
+		return;
+
+	set_try_to_free_routine(NULL);	/* is never reset */
+	strbuf_vaddf(&buf, fmt, ap);
+	trace_strbuf(key, &buf);
+	strbuf_release(&buf);
+}
+
+static void trace_printf_key(const char *key, const char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	trace_vprintf(key, fmt, ap);
+	va_end(ap);
+}
+
 void trace_printf(const char *fmt, ...)
 {
-	struct strbuf buf;
 	va_list ap;
-	int fd, len, need_close = 0;
+	va_start(ap, fmt);
+	trace_vprintf("GIT_TRACE", fmt, ap);
+	va_end(ap);
+}
 
-	fd = get_trace_fd(&need_close);
+void trace_strbuf(const char *key, const struct strbuf *buf)
+{
+	int fd, need_close = 0;
+
+	fd = get_trace_fd(key, &need_close);
 	if (!fd)
 		return;
 
-	strbuf_init(&buf, 64);
-	va_start(ap, fmt);
-	len = vsnprintf(buf.buf, strbuf_avail(&buf), fmt, ap);
-	va_end(ap);
-	if (len >= strbuf_avail(&buf)) {
-		strbuf_grow(&buf, len - strbuf_avail(&buf) + 128);
-		va_start(ap, fmt);
-		len = vsnprintf(buf.buf, strbuf_avail(&buf), fmt, ap);
-		va_end(ap);
-		if (len >= strbuf_avail(&buf))
-			die("broken vsnprintf");
-	}
-	strbuf_setlen(&buf, len);
-
-	write_or_whine_pipe(fd, buf.buf, buf.len, err_msg);
-	strbuf_release(&buf);
+	write_or_whine_pipe(fd, buf->buf, buf->len, err_msg);
 
 	if (need_close)
 		close(fd);
@@ -95,27 +107,18 @@
 
 void trace_argv_printf(const char **argv, const char *fmt, ...)
 {
-	struct strbuf buf;
+	struct strbuf buf = STRBUF_INIT;
 	va_list ap;
-	int fd, len, need_close = 0;
+	int fd, need_close = 0;
 
-	fd = get_trace_fd(&need_close);
+	fd = get_trace_fd("GIT_TRACE", &need_close);
 	if (!fd)
 		return;
 
-	strbuf_init(&buf, 64);
+	set_try_to_free_routine(NULL);	/* is never reset */
 	va_start(ap, fmt);
-	len = vsnprintf(buf.buf, strbuf_avail(&buf), fmt, ap);
+	strbuf_vaddf(&buf, fmt, ap);
 	va_end(ap);
-	if (len >= strbuf_avail(&buf)) {
-		strbuf_grow(&buf, len - strbuf_avail(&buf) + 128);
-		va_start(ap, fmt);
-		len = vsnprintf(buf.buf, strbuf_avail(&buf), fmt, ap);
-		va_end(ap);
-		if (len >= strbuf_avail(&buf))
-			die("broken vsnprintf");
-	}
-	strbuf_setlen(&buf, len);
 
 	sq_quote_argv(&buf, argv, 0);
 	strbuf_addch(&buf, '\n');
@@ -125,3 +128,61 @@
 	if (need_close)
 		close(fd);
 }
+
+static const char *quote_crnl(const char *path)
+{
+	static char new_path[PATH_MAX];
+	const char *p2 = path;
+	char *p1 = new_path;
+
+	if (!path)
+		return NULL;
+
+	while (*p2) {
+		switch (*p2) {
+		case '\\': *p1++ = '\\'; *p1++ = '\\'; break;
+		case '\n': *p1++ = '\\'; *p1++ = 'n'; break;
+		case '\r': *p1++ = '\\'; *p1++ = 'r'; break;
+		default:
+			*p1++ = *p2;
+		}
+		p2++;
+	}
+	*p1 = '\0';
+	return new_path;
+}
+
+/* FIXME: move prefix to startup_info struct and get rid of this arg */
+void trace_repo_setup(const char *prefix)
+{
+	static const char *key = "GIT_TRACE_SETUP";
+	const char *git_work_tree;
+	char cwd[PATH_MAX];
+
+	if (!trace_want(key))
+		return;
+
+	if (!getcwd(cwd, PATH_MAX))
+		die("Unable to get current working directory");
+
+	if (!(git_work_tree = get_git_work_tree()))
+		git_work_tree = "(null)";
+
+	if (!prefix)
+		prefix = "(null)";
+
+	trace_printf_key(key, "setup: git_dir: %s\n", quote_crnl(get_git_dir()));
+	trace_printf_key(key, "setup: worktree: %s\n", quote_crnl(git_work_tree));
+	trace_printf_key(key, "setup: cwd: %s\n", quote_crnl(cwd));
+	trace_printf_key(key, "setup: prefix: %s\n", quote_crnl(prefix));
+}
+
+int trace_want(const char *key)
+{
+	const char *trace = getenv(key);
+
+	if (!trace || !strcmp(trace, "") ||
+	    !strcmp(trace, "0") || !strcasecmp(trace, "false"))
+		return 0;
+	return 1;
+}
diff --git a/transport-helper.c b/transport-helper.c
new file mode 100644
index 0000000..61c928f
--- /dev/null
+++ b/transport-helper.c
@@ -0,0 +1,1204 @@
+#include "cache.h"
+#include "transport.h"
+#include "quote.h"
+#include "run-command.h"
+#include "commit.h"
+#include "diff.h"
+#include "revision.h"
+#include "quote.h"
+#include "remote.h"
+#include "string-list.h"
+#include "thread-utils.h"
+#include "sigchain.h"
+
+static int debug;
+
+struct helper_data {
+	const char *name;
+	struct child_process *helper;
+	FILE *out;
+	unsigned fetch : 1,
+		import : 1,
+		export : 1,
+		option : 1,
+		push : 1,
+		connect : 1,
+		no_disconnect_req : 1;
+	char *export_marks;
+	char *import_marks;
+	/* These go from remote name (as in "list") to private name */
+	struct refspec *refspecs;
+	int refspec_nr;
+	/* Transport options for fetch-pack/send-pack (should one of
+	 * those be invoked).
+	 */
+	struct git_transport_options transport_options;
+};
+
+static void sendline(struct helper_data *helper, struct strbuf *buffer)
+{
+	if (debug)
+		fprintf(stderr, "Debug: Remote helper: -> %s", buffer->buf);
+	if (write_in_full(helper->helper->in, buffer->buf, buffer->len)
+		!= buffer->len)
+		die_errno("Full write to remote helper failed");
+}
+
+static int recvline_fh(FILE *helper, struct strbuf *buffer)
+{
+	strbuf_reset(buffer);
+	if (debug)
+		fprintf(stderr, "Debug: Remote helper: Waiting...\n");
+	if (strbuf_getline(buffer, helper, '\n') == EOF) {
+		if (debug)
+			fprintf(stderr, "Debug: Remote helper quit.\n");
+		exit(128);
+	}
+
+	if (debug)
+		fprintf(stderr, "Debug: Remote helper: <- %s\n", buffer->buf);
+	return 0;
+}
+
+static int recvline(struct helper_data *helper, struct strbuf *buffer)
+{
+	return recvline_fh(helper->out, buffer);
+}
+
+static void xchgline(struct helper_data *helper, struct strbuf *buffer)
+{
+	sendline(helper, buffer);
+	recvline(helper, buffer);
+}
+
+static void write_constant(int fd, const char *str)
+{
+	if (debug)
+		fprintf(stderr, "Debug: Remote helper: -> %s", str);
+	if (write_in_full(fd, str, strlen(str)) != strlen(str))
+		die_errno("Full write to remote helper failed");
+}
+
+static const char *remove_ext_force(const char *url)
+{
+	if (url) {
+		const char *colon = strchr(url, ':');
+		if (colon && colon[1] == ':')
+			return colon + 2;
+	}
+	return url;
+}
+
+static void do_take_over(struct transport *transport)
+{
+	struct helper_data *data;
+	data = (struct helper_data *)transport->data;
+	transport_take_over(transport, data->helper);
+	fclose(data->out);
+	free(data);
+}
+
+static struct child_process *get_helper(struct transport *transport)
+{
+	struct helper_data *data = transport->data;
+	struct strbuf buf = STRBUF_INIT;
+	struct child_process *helper;
+	const char **refspecs = NULL;
+	int refspec_nr = 0;
+	int refspec_alloc = 0;
+	int duped;
+	int code;
+	char git_dir_buf[sizeof(GIT_DIR_ENVIRONMENT) + PATH_MAX + 1];
+	const char *helper_env[] = {
+		git_dir_buf,
+		NULL
+	};
+
+
+	if (data->helper)
+		return data->helper;
+
+	helper = xcalloc(1, sizeof(*helper));
+	helper->in = -1;
+	helper->out = -1;
+	helper->err = 0;
+	helper->argv = xcalloc(4, sizeof(*helper->argv));
+	strbuf_addf(&buf, "git-remote-%s", data->name);
+	helper->argv[0] = strbuf_detach(&buf, NULL);
+	helper->argv[1] = transport->remote->name;
+	helper->argv[2] = remove_ext_force(transport->url);
+	helper->git_cmd = 0;
+	helper->silent_exec_failure = 1;
+
+	snprintf(git_dir_buf, sizeof(git_dir_buf), "%s=%s", GIT_DIR_ENVIRONMENT, get_git_dir());
+	helper->env = helper_env;
+
+	code = start_command(helper);
+	if (code < 0 && errno == ENOENT)
+		die("Unable to find remote helper for '%s'", data->name);
+	else if (code != 0)
+		exit(code);
+
+	data->helper = helper;
+	data->no_disconnect_req = 0;
+
+	/*
+	 * Open the output as FILE* so strbuf_getline() can be used.
+	 * Do this with duped fd because fclose() will close the fd,
+	 * and stuff like taking over will require the fd to remain.
+	 */
+	duped = dup(helper->out);
+	if (duped < 0)
+		die_errno("Can't dup helper output fd");
+	data->out = xfdopen(duped, "r");
+
+	write_constant(helper->in, "capabilities\n");
+
+	while (1) {
+		const char *capname;
+		int mandatory = 0;
+		recvline(data, &buf);
+
+		if (!*buf.buf)
+			break;
+
+		if (*buf.buf == '*') {
+			capname = buf.buf + 1;
+			mandatory = 1;
+		} else
+			capname = buf.buf;
+
+		if (debug)
+			fprintf(stderr, "Debug: Got cap %s\n", capname);
+		if (!strcmp(capname, "fetch"))
+			data->fetch = 1;
+		else if (!strcmp(capname, "option"))
+			data->option = 1;
+		else if (!strcmp(capname, "push"))
+			data->push = 1;
+		else if (!strcmp(capname, "import"))
+			data->import = 1;
+		else if (!strcmp(capname, "export"))
+			data->export = 1;
+		else if (!data->refspecs && !prefixcmp(capname, "refspec ")) {
+			ALLOC_GROW(refspecs,
+				   refspec_nr + 1,
+				   refspec_alloc);
+			refspecs[refspec_nr++] = xstrdup(capname + strlen("refspec "));
+		} else if (!strcmp(capname, "connect")) {
+			data->connect = 1;
+		} else if (!prefixcmp(capname, "export-marks ")) {
+			struct strbuf arg = STRBUF_INIT;
+			strbuf_addstr(&arg, "--export-marks=");
+			strbuf_addstr(&arg, capname + strlen("export-marks "));
+			data->export_marks = strbuf_detach(&arg, NULL);
+		} else if (!prefixcmp(capname, "import-marks")) {
+			struct strbuf arg = STRBUF_INIT;
+			strbuf_addstr(&arg, "--import-marks=");
+			strbuf_addstr(&arg, capname + strlen("import-marks "));
+			data->import_marks = strbuf_detach(&arg, NULL);
+		} else if (mandatory) {
+			die("Unknown mandatory capability %s. This remote "
+			    "helper probably needs newer version of Git.",
+			    capname);
+		}
+	}
+	if (refspecs) {
+		int i;
+		data->refspec_nr = refspec_nr;
+		data->refspecs = parse_fetch_refspec(refspec_nr, refspecs);
+		for (i = 0; i < refspec_nr; i++) {
+			free((char *)refspecs[i]);
+		}
+		free(refspecs);
+	}
+	strbuf_release(&buf);
+	if (debug)
+		fprintf(stderr, "Debug: Capabilities complete.\n");
+	return data->helper;
+}
+
+static int disconnect_helper(struct transport *transport)
+{
+	struct helper_data *data = transport->data;
+	int res = 0;
+
+	if (data->helper) {
+		if (debug)
+			fprintf(stderr, "Debug: Disconnecting.\n");
+		if (!data->no_disconnect_req) {
+			/*
+			 * Ignore write errors; there's nothing we can do,
+			 * since we're about to close the pipe anyway. And the
+			 * most likely error is EPIPE due to the helper dying
+			 * to report an error itself.
+			 */
+			sigchain_push(SIGPIPE, SIG_IGN);
+			xwrite(data->helper->in, "\n", 1);
+			sigchain_pop(SIGPIPE);
+		}
+		close(data->helper->in);
+		close(data->helper->out);
+		fclose(data->out);
+		res = finish_command(data->helper);
+		free((char *)data->helper->argv[0]);
+		free(data->helper->argv);
+		free(data->helper);
+		data->helper = NULL;
+	}
+	return res;
+}
+
+static const char *unsupported_options[] = {
+	TRANS_OPT_UPLOADPACK,
+	TRANS_OPT_RECEIVEPACK,
+	TRANS_OPT_THIN,
+	TRANS_OPT_KEEP
+	};
+static const char *boolean_options[] = {
+	TRANS_OPT_THIN,
+	TRANS_OPT_KEEP,
+	TRANS_OPT_FOLLOWTAGS
+	};
+
+static int set_helper_option(struct transport *transport,
+			  const char *name, const char *value)
+{
+	struct helper_data *data = transport->data;
+	struct strbuf buf = STRBUF_INIT;
+	int i, ret, is_bool = 0;
+
+	get_helper(transport);
+
+	if (!data->option)
+		return 1;
+
+	for (i = 0; i < ARRAY_SIZE(unsupported_options); i++) {
+		if (!strcmp(name, unsupported_options[i]))
+			return 1;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(boolean_options); i++) {
+		if (!strcmp(name, boolean_options[i])) {
+			is_bool = 1;
+			break;
+		}
+	}
+
+	strbuf_addf(&buf, "option %s ", name);
+	if (is_bool)
+		strbuf_addstr(&buf, value ? "true" : "false");
+	else
+		quote_c_style(value, &buf, NULL, 0);
+	strbuf_addch(&buf, '\n');
+
+	xchgline(data, &buf);
+
+	if (!strcmp(buf.buf, "ok"))
+		ret = 0;
+	else if (!prefixcmp(buf.buf, "error")) {
+		ret = -1;
+	} else if (!strcmp(buf.buf, "unsupported"))
+		ret = 1;
+	else {
+		warning("%s unexpectedly said: '%s'", data->name, buf.buf);
+		ret = 1;
+	}
+	strbuf_release(&buf);
+	return ret;
+}
+
+static void standard_options(struct transport *t)
+{
+	char buf[16];
+	int n;
+	int v = t->verbose;
+
+	set_helper_option(t, "progress", t->progress ? "true" : "false");
+
+	n = snprintf(buf, sizeof(buf), "%d", v + 1);
+	if (n >= sizeof(buf))
+		die("impossibly large verbosity value");
+	set_helper_option(t, "verbosity", buf);
+}
+
+static int release_helper(struct transport *transport)
+{
+	int res = 0;
+	struct helper_data *data = transport->data;
+	free_refspec(data->refspec_nr, data->refspecs);
+	data->refspecs = NULL;
+	res = disconnect_helper(transport);
+	free(transport->data);
+	return res;
+}
+
+static int fetch_with_fetch(struct transport *transport,
+			    int nr_heads, struct ref **to_fetch)
+{
+	struct helper_data *data = transport->data;
+	int i;
+	struct strbuf buf = STRBUF_INIT;
+
+	standard_options(transport);
+
+	for (i = 0; i < nr_heads; i++) {
+		const struct ref *posn = to_fetch[i];
+		if (posn->status & REF_STATUS_UPTODATE)
+			continue;
+
+		strbuf_addf(&buf, "fetch %s %s\n",
+			    sha1_to_hex(posn->old_sha1), posn->name);
+	}
+
+	strbuf_addch(&buf, '\n');
+	sendline(data, &buf);
+
+	while (1) {
+		recvline(data, &buf);
+
+		if (!prefixcmp(buf.buf, "lock ")) {
+			const char *name = buf.buf + 5;
+			if (transport->pack_lockfile)
+				warning("%s also locked %s", data->name, name);
+			else
+				transport->pack_lockfile = xstrdup(name);
+		}
+		else if (!buf.len)
+			break;
+		else
+			warning("%s unexpectedly said: '%s'", data->name, buf.buf);
+	}
+	strbuf_release(&buf);
+	return 0;
+}
+
+static int get_importer(struct transport *transport, struct child_process *fastimport)
+{
+	struct child_process *helper = get_helper(transport);
+	memset(fastimport, 0, sizeof(*fastimport));
+	fastimport->in = helper->out;
+	fastimport->argv = xcalloc(5, sizeof(*fastimport->argv));
+	fastimport->argv[0] = "fast-import";
+	fastimport->argv[1] = "--quiet";
+
+	fastimport->git_cmd = 1;
+	return start_command(fastimport);
+}
+
+static int get_exporter(struct transport *transport,
+			struct child_process *fastexport,
+			struct string_list *revlist_args)
+{
+	struct helper_data *data = transport->data;
+	struct child_process *helper = get_helper(transport);
+	int argc = 0, i;
+	memset(fastexport, 0, sizeof(*fastexport));
+
+	/* we need to duplicate helper->in because we want to use it after
+	 * fastexport is done with it. */
+	fastexport->out = dup(helper->in);
+	fastexport->argv = xcalloc(5 + revlist_args->nr, sizeof(*fastexport->argv));
+	fastexport->argv[argc++] = "fast-export";
+	fastexport->argv[argc++] = "--use-done-feature";
+	if (data->export_marks)
+		fastexport->argv[argc++] = data->export_marks;
+	if (data->import_marks)
+		fastexport->argv[argc++] = data->import_marks;
+
+	for (i = 0; i < revlist_args->nr; i++)
+		fastexport->argv[argc++] = revlist_args->items[i].string;
+
+	fastexport->git_cmd = 1;
+	return start_command(fastexport);
+}
+
+static int fetch_with_import(struct transport *transport,
+			     int nr_heads, struct ref **to_fetch)
+{
+	struct child_process fastimport;
+	struct helper_data *data = transport->data;
+	int i;
+	struct ref *posn;
+	struct strbuf buf = STRBUF_INIT;
+
+	get_helper(transport);
+
+	if (get_importer(transport, &fastimport))
+		die("Couldn't run fast-import");
+
+	for (i = 0; i < nr_heads; i++) {
+		posn = to_fetch[i];
+		if (posn->status & REF_STATUS_UPTODATE)
+			continue;
+
+		strbuf_addf(&buf, "import %s\n", posn->name);
+		sendline(data, &buf);
+		strbuf_reset(&buf);
+	}
+
+	write_constant(data->helper->in, "\n");
+
+	if (finish_command(&fastimport))
+		die("Error while running fast-import");
+	free(fastimport.argv);
+	fastimport.argv = NULL;
+
+	for (i = 0; i < nr_heads; i++) {
+		char *private;
+		posn = to_fetch[i];
+		if (posn->status & REF_STATUS_UPTODATE)
+			continue;
+		if (data->refspecs)
+			private = apply_refspecs(data->refspecs, data->refspec_nr, posn->name);
+		else
+			private = xstrdup(posn->name);
+		if (private) {
+			read_ref(private, posn->old_sha1);
+			free(private);
+		}
+	}
+	strbuf_release(&buf);
+	return 0;
+}
+
+static int process_connect_service(struct transport *transport,
+				   const char *name, const char *exec)
+{
+	struct helper_data *data = transport->data;
+	struct strbuf cmdbuf = STRBUF_INIT;
+	struct child_process *helper;
+	int r, duped, ret = 0;
+	FILE *input;
+
+	helper = get_helper(transport);
+
+	/*
+	 * Yes, dup the pipe another time, as we need unbuffered version
+	 * of input pipe as FILE*. fclose() closes the underlying fd and
+	 * stream buffering only can be changed before first I/O operation
+	 * on it.
+	 */
+	duped = dup(helper->out);
+	if (duped < 0)
+		die_errno("Can't dup helper output fd");
+	input = xfdopen(duped, "r");
+	setvbuf(input, NULL, _IONBF, 0);
+
+	/*
+	 * Handle --upload-pack and friends. This is fire and forget...
+	 * just warn if it fails.
+	 */
+	if (strcmp(name, exec)) {
+		r = set_helper_option(transport, "servpath", exec);
+		if (r > 0)
+			warning("Setting remote service path not supported by protocol.");
+		else if (r < 0)
+			warning("Invalid remote service path.");
+	}
+
+	if (data->connect)
+		strbuf_addf(&cmdbuf, "connect %s\n", name);
+	else
+		goto exit;
+
+	sendline(data, &cmdbuf);
+	recvline_fh(input, &cmdbuf);
+	if (!strcmp(cmdbuf.buf, "")) {
+		data->no_disconnect_req = 1;
+		if (debug)
+			fprintf(stderr, "Debug: Smart transport connection "
+				"ready.\n");
+		ret = 1;
+	} else if (!strcmp(cmdbuf.buf, "fallback")) {
+		if (debug)
+			fprintf(stderr, "Debug: Falling back to dumb "
+				"transport.\n");
+	} else
+		die("Unknown response to connect: %s",
+			cmdbuf.buf);
+
+exit:
+	fclose(input);
+	return ret;
+}
+
+static int process_connect(struct transport *transport,
+				     int for_push)
+{
+	struct helper_data *data = transport->data;
+	const char *name;
+	const char *exec;
+
+	name = for_push ? "git-receive-pack" : "git-upload-pack";
+	if (for_push)
+		exec = data->transport_options.receivepack;
+	else
+		exec = data->transport_options.uploadpack;
+
+	return process_connect_service(transport, name, exec);
+}
+
+static int connect_helper(struct transport *transport, const char *name,
+		   const char *exec, int fd[2])
+{
+	struct helper_data *data = transport->data;
+
+	/* Get_helper so connect is inited. */
+	get_helper(transport);
+	if (!data->connect)
+		die("Operation not supported by protocol.");
+
+	if (!process_connect_service(transport, name, exec))
+		die("Can't connect to subservice %s.", name);
+
+	fd[0] = data->helper->out;
+	fd[1] = data->helper->in;
+	return 0;
+}
+
+static int fetch(struct transport *transport,
+		 int nr_heads, struct ref **to_fetch)
+{
+	struct helper_data *data = transport->data;
+	int i, count;
+
+	if (process_connect(transport, 0)) {
+		do_take_over(transport);
+		return transport->fetch(transport, nr_heads, to_fetch);
+	}
+
+	count = 0;
+	for (i = 0; i < nr_heads; i++)
+		if (!(to_fetch[i]->status & REF_STATUS_UPTODATE))
+			count++;
+
+	if (!count)
+		return 0;
+
+	if (data->fetch)
+		return fetch_with_fetch(transport, nr_heads, to_fetch);
+
+	if (data->import)
+		return fetch_with_import(transport, nr_heads, to_fetch);
+
+	return -1;
+}
+
+static void push_update_ref_status(struct strbuf *buf,
+				   struct ref **ref,
+				   struct ref *remote_refs)
+{
+	char *refname, *msg;
+	int status;
+
+	if (!prefixcmp(buf->buf, "ok ")) {
+		status = REF_STATUS_OK;
+		refname = buf->buf + 3;
+	} else if (!prefixcmp(buf->buf, "error ")) {
+		status = REF_STATUS_REMOTE_REJECT;
+		refname = buf->buf + 6;
+	} else
+		die("expected ok/error, helper said '%s'", buf->buf);
+
+	msg = strchr(refname, ' ');
+	if (msg) {
+		struct strbuf msg_buf = STRBUF_INIT;
+		const char *end;
+
+		*msg++ = '\0';
+		if (!unquote_c_style(&msg_buf, msg, &end))
+			msg = strbuf_detach(&msg_buf, NULL);
+		else
+			msg = xstrdup(msg);
+		strbuf_release(&msg_buf);
+
+		if (!strcmp(msg, "no match")) {
+			status = REF_STATUS_NONE;
+			free(msg);
+			msg = NULL;
+		}
+		else if (!strcmp(msg, "up to date")) {
+			status = REF_STATUS_UPTODATE;
+			free(msg);
+			msg = NULL;
+		}
+		else if (!strcmp(msg, "non-fast forward")) {
+			status = REF_STATUS_REJECT_NONFASTFORWARD;
+			free(msg);
+			msg = NULL;
+		}
+	}
+
+	if (*ref)
+		*ref = find_ref_by_name(*ref, refname);
+	if (!*ref)
+		*ref = find_ref_by_name(remote_refs, refname);
+	if (!*ref) {
+		warning("helper reported unexpected status of %s", refname);
+		return;
+	}
+
+	if ((*ref)->status != REF_STATUS_NONE) {
+		/*
+		 * Earlier, the ref was marked not to be pushed, so ignore the ref
+		 * status reported by the remote helper if the latter is 'no match'.
+		 */
+		if (status == REF_STATUS_NONE)
+			return;
+	}
+
+	(*ref)->status = status;
+	(*ref)->remote_status = msg;
+}
+
+static void push_update_refs_status(struct helper_data *data,
+				    struct ref *remote_refs)
+{
+	struct strbuf buf = STRBUF_INIT;
+	struct ref *ref = remote_refs;
+	for (;;) {
+		recvline(data, &buf);
+		if (!buf.len)
+			break;
+
+		push_update_ref_status(&buf, &ref, remote_refs);
+	}
+	strbuf_release(&buf);
+}
+
+static int push_refs_with_push(struct transport *transport,
+		struct ref *remote_refs, int flags)
+{
+	int force_all = flags & TRANSPORT_PUSH_FORCE;
+	int mirror = flags & TRANSPORT_PUSH_MIRROR;
+	struct helper_data *data = transport->data;
+	struct strbuf buf = STRBUF_INIT;
+	struct ref *ref;
+
+	get_helper(transport);
+	if (!data->push)
+		return 1;
+
+	for (ref = remote_refs; ref; ref = ref->next) {
+		if (!ref->peer_ref && !mirror)
+			continue;
+
+		/* Check for statuses set by set_ref_status_for_push() */
+		switch (ref->status) {
+		case REF_STATUS_REJECT_NONFASTFORWARD:
+		case REF_STATUS_UPTODATE:
+			continue;
+		default:
+			; /* do nothing */
+		}
+
+		if (force_all)
+			ref->force = 1;
+
+		strbuf_addstr(&buf, "push ");
+		if (!ref->deletion) {
+			if (ref->force)
+				strbuf_addch(&buf, '+');
+			if (ref->peer_ref)
+				strbuf_addstr(&buf, ref->peer_ref->name);
+			else
+				strbuf_addstr(&buf, sha1_to_hex(ref->new_sha1));
+		}
+		strbuf_addch(&buf, ':');
+		strbuf_addstr(&buf, ref->name);
+		strbuf_addch(&buf, '\n');
+	}
+	if (buf.len == 0)
+		return 0;
+
+	standard_options(transport);
+
+	if (flags & TRANSPORT_PUSH_DRY_RUN) {
+		if (set_helper_option(transport, "dry-run", "true") != 0)
+			die("helper %s does not support dry-run", data->name);
+	}
+
+	strbuf_addch(&buf, '\n');
+	sendline(data, &buf);
+	strbuf_release(&buf);
+
+	push_update_refs_status(data, remote_refs);
+	return 0;
+}
+
+static int push_refs_with_export(struct transport *transport,
+		struct ref *remote_refs, int flags)
+{
+	struct ref *ref;
+	struct child_process *helper, exporter;
+	struct helper_data *data = transport->data;
+	struct string_list revlist_args = STRING_LIST_INIT_NODUP;
+	struct strbuf buf = STRBUF_INIT;
+
+	helper = get_helper(transport);
+
+	write_constant(helper->in, "export\n");
+
+	strbuf_reset(&buf);
+
+	for (ref = remote_refs; ref; ref = ref->next) {
+		char *private;
+		unsigned char sha1[20];
+
+		if (!data->refspecs)
+			continue;
+		private = apply_refspecs(data->refspecs, data->refspec_nr, ref->name);
+		if (private && !get_sha1(private, sha1)) {
+			strbuf_addf(&buf, "^%s", private);
+			string_list_append(&revlist_args, strbuf_detach(&buf, NULL));
+		}
+		free(private);
+
+		if (ref->deletion) {
+			die("remote-helpers do not support ref deletion");
+		}
+
+		if (ref->peer_ref)
+			string_list_append(&revlist_args, ref->peer_ref->name);
+
+	}
+
+	if (get_exporter(transport, &exporter, &revlist_args))
+		die("Couldn't run fast-export");
+
+	if (finish_command(&exporter))
+		die("Error while running fast-export");
+	push_update_refs_status(data, remote_refs);
+	return 0;
+}
+
+static int push_refs(struct transport *transport,
+		struct ref *remote_refs, int flags)
+{
+	struct helper_data *data = transport->data;
+
+	if (process_connect(transport, 1)) {
+		do_take_over(transport);
+		return transport->push_refs(transport, remote_refs, flags);
+	}
+
+	if (!remote_refs) {
+		fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
+			"Perhaps you should specify a branch such as 'master'.\n");
+		return 0;
+	}
+
+	if (data->push)
+		return push_refs_with_push(transport, remote_refs, flags);
+
+	if (data->export)
+		return push_refs_with_export(transport, remote_refs, flags);
+
+	return -1;
+}
+
+
+static int has_attribute(const char *attrs, const char *attr) {
+	int len;
+	if (!attrs)
+		return 0;
+
+	len = strlen(attr);
+	for (;;) {
+		const char *space = strchrnul(attrs, ' ');
+		if (len == space - attrs && !strncmp(attrs, attr, len))
+			return 1;
+		if (!*space)
+			return 0;
+		attrs = space + 1;
+	}
+}
+
+static struct ref *get_refs_list(struct transport *transport, int for_push)
+{
+	struct helper_data *data = transport->data;
+	struct child_process *helper;
+	struct ref *ret = NULL;
+	struct ref **tail = &ret;
+	struct ref *posn;
+	struct strbuf buf = STRBUF_INIT;
+
+	helper = get_helper(transport);
+
+	if (process_connect(transport, for_push)) {
+		do_take_over(transport);
+		return transport->get_refs_list(transport, for_push);
+	}
+
+	if (data->push && for_push)
+		write_str_in_full(helper->in, "list for-push\n");
+	else
+		write_str_in_full(helper->in, "list\n");
+
+	while (1) {
+		char *eov, *eon;
+		recvline(data, &buf);
+
+		if (!*buf.buf)
+			break;
+
+		eov = strchr(buf.buf, ' ');
+		if (!eov)
+			die("Malformed response in ref list: %s", buf.buf);
+		eon = strchr(eov + 1, ' ');
+		*eov = '\0';
+		if (eon)
+			*eon = '\0';
+		*tail = alloc_ref(eov + 1);
+		if (buf.buf[0] == '@')
+			(*tail)->symref = xstrdup(buf.buf + 1);
+		else if (buf.buf[0] != '?')
+			get_sha1_hex(buf.buf, (*tail)->old_sha1);
+		if (eon) {
+			if (has_attribute(eon + 1, "unchanged")) {
+				(*tail)->status |= REF_STATUS_UPTODATE;
+				read_ref((*tail)->name, (*tail)->old_sha1);
+			}
+		}
+		tail = &((*tail)->next);
+	}
+	if (debug)
+		fprintf(stderr, "Debug: Read ref listing.\n");
+	strbuf_release(&buf);
+
+	for (posn = ret; posn; posn = posn->next)
+		resolve_remote_symref(posn, ret);
+
+	return ret;
+}
+
+int transport_helper_init(struct transport *transport, const char *name)
+{
+	struct helper_data *data = xcalloc(sizeof(*data), 1);
+	data->name = name;
+
+	if (getenv("GIT_TRANSPORT_HELPER_DEBUG"))
+		debug = 1;
+
+	transport->data = data;
+	transport->set_option = set_helper_option;
+	transport->get_refs_list = get_refs_list;
+	transport->fetch = fetch;
+	transport->push_refs = push_refs;
+	transport->disconnect = release_helper;
+	transport->connect = connect_helper;
+	transport->smart_options = &(data->transport_options);
+	return 0;
+}
+
+/*
+ * Linux pipes can buffer 65536 bytes at once (and most platforms can
+ * buffer less), so attempt reads and writes with up to that size.
+ */
+#define BUFFERSIZE 65536
+/* This should be enough to hold debugging message. */
+#define PBUFFERSIZE 8192
+
+/* Print bidirectional transfer loop debug message. */
+static void transfer_debug(const char *fmt, ...)
+{
+	va_list args;
+	char msgbuf[PBUFFERSIZE];
+	static int debug_enabled = -1;
+
+	if (debug_enabled < 0)
+		debug_enabled = getenv("GIT_TRANSLOOP_DEBUG") ? 1 : 0;
+	if (!debug_enabled)
+		return;
+
+	va_start(args, fmt);
+	vsnprintf(msgbuf, PBUFFERSIZE, fmt, args);
+	va_end(args);
+	fprintf(stderr, "Transfer loop debugging: %s\n", msgbuf);
+}
+
+/* Stream state: More data may be coming in this direction. */
+#define SSTATE_TRANSFERING 0
+/*
+ * Stream state: No more data coming in this direction, flushing rest of
+ * data.
+ */
+#define SSTATE_FLUSHING 1
+/* Stream state: Transfer in this direction finished. */
+#define SSTATE_FINISHED 2
+
+#define STATE_NEEDS_READING(state) ((state) <= SSTATE_TRANSFERING)
+#define STATE_NEEDS_WRITING(state) ((state) <= SSTATE_FLUSHING)
+#define STATE_NEEDS_CLOSING(state) ((state) == SSTATE_FLUSHING)
+
+/* Unidirectional transfer. */
+struct unidirectional_transfer {
+	/* Source */
+	int src;
+	/* Destination */
+	int dest;
+	/* Is source socket? */
+	int src_is_sock;
+	/* Is destination socket? */
+	int dest_is_sock;
+	/* Transfer state (TRANSFERING/FLUSHING/FINISHED) */
+	int state;
+	/* Buffer. */
+	char buf[BUFFERSIZE];
+	/* Buffer used. */
+	size_t bufuse;
+	/* Name of source. */
+	const char *src_name;
+	/* Name of destination. */
+	const char *dest_name;
+};
+
+/* Closes the target (for writing) if transfer has finished. */
+static void udt_close_if_finished(struct unidirectional_transfer *t)
+{
+	if (STATE_NEEDS_CLOSING(t->state) && !t->bufuse) {
+		t->state = SSTATE_FINISHED;
+		if (t->dest_is_sock)
+			shutdown(t->dest, SHUT_WR);
+		else
+			close(t->dest);
+		transfer_debug("Closed %s.", t->dest_name);
+	}
+}
+
+/*
+ * Tries to read read data from source into buffer. If buffer is full,
+ * no data is read. Returns 0 on success, -1 on error.
+ */
+static int udt_do_read(struct unidirectional_transfer *t)
+{
+	ssize_t bytes;
+
+	if (t->bufuse == BUFFERSIZE)
+		return 0;	/* No space for more. */
+
+	transfer_debug("%s is readable", t->src_name);
+	bytes = read(t->src, t->buf + t->bufuse, BUFFERSIZE - t->bufuse);
+	if (bytes < 0 && errno != EWOULDBLOCK && errno != EAGAIN &&
+		errno != EINTR) {
+		error("read(%s) failed: %s", t->src_name, strerror(errno));
+		return -1;
+	} else if (bytes == 0) {
+		transfer_debug("%s EOF (with %i bytes in buffer)",
+			t->src_name, t->bufuse);
+		t->state = SSTATE_FLUSHING;
+	} else if (bytes > 0) {
+		t->bufuse += bytes;
+		transfer_debug("Read %i bytes from %s (buffer now at %i)",
+			(int)bytes, t->src_name, (int)t->bufuse);
+	}
+	return 0;
+}
+
+/* Tries to write data from buffer into destination. If buffer is empty,
+ * no data is written. Returns 0 on success, -1 on error.
+ */
+static int udt_do_write(struct unidirectional_transfer *t)
+{
+	ssize_t bytes;
+
+	if (t->bufuse == 0)
+		return 0;	/* Nothing to write. */
+
+	transfer_debug("%s is writable", t->dest_name);
+	bytes = write(t->dest, t->buf, t->bufuse);
+	if (bytes < 0 && errno != EWOULDBLOCK && errno != EAGAIN &&
+		errno != EINTR) {
+		error("write(%s) failed: %s", t->dest_name, strerror(errno));
+		return -1;
+	} else if (bytes > 0) {
+		t->bufuse -= bytes;
+		if (t->bufuse)
+			memmove(t->buf, t->buf + bytes, t->bufuse);
+		transfer_debug("Wrote %i bytes to %s (buffer now at %i)",
+			(int)bytes, t->dest_name, (int)t->bufuse);
+	}
+	return 0;
+}
+
+
+/* State of bidirectional transfer loop. */
+struct bidirectional_transfer_state {
+	/* Direction from program to git. */
+	struct unidirectional_transfer ptg;
+	/* Direction from git to program. */
+	struct unidirectional_transfer gtp;
+};
+
+static void *udt_copy_task_routine(void *udt)
+{
+	struct unidirectional_transfer *t = (struct unidirectional_transfer *)udt;
+	while (t->state != SSTATE_FINISHED) {
+		if (STATE_NEEDS_READING(t->state))
+			if (udt_do_read(t))
+				return NULL;
+		if (STATE_NEEDS_WRITING(t->state))
+			if (udt_do_write(t))
+				return NULL;
+		if (STATE_NEEDS_CLOSING(t->state))
+			udt_close_if_finished(t);
+	}
+	return udt;	/* Just some non-NULL value. */
+}
+
+#ifndef NO_PTHREADS
+
+/*
+ * Join thread, with apporiate errors on failure. Name is name for the
+ * thread (for error messages). Returns 0 on success, 1 on failure.
+ */
+static int tloop_join(pthread_t thread, const char *name)
+{
+	int err;
+	void *tret;
+	err = pthread_join(thread, &tret);
+	if (!tret) {
+		error("%s thread failed", name);
+		return 1;
+	}
+	if (err) {
+		error("%s thread failed to join: %s", name, strerror(err));
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * Spawn the transfer tasks and then wait for them. Returns 0 on success,
+ * -1 on failure.
+ */
+static int tloop_spawnwait_tasks(struct bidirectional_transfer_state *s)
+{
+	pthread_t gtp_thread;
+	pthread_t ptg_thread;
+	int err;
+	int ret = 0;
+	err = pthread_create(&gtp_thread, NULL, udt_copy_task_routine,
+		&s->gtp);
+	if (err)
+		die("Can't start thread for copying data: %s", strerror(err));
+	err = pthread_create(&ptg_thread, NULL, udt_copy_task_routine,
+		&s->ptg);
+	if (err)
+		die("Can't start thread for copying data: %s", strerror(err));
+
+	ret |= tloop_join(gtp_thread, "Git to program copy");
+	ret |= tloop_join(ptg_thread, "Program to git copy");
+	return ret;
+}
+#else
+
+/* Close the source and target (for writing) for transfer. */
+static void udt_kill_transfer(struct unidirectional_transfer *t)
+{
+	t->state = SSTATE_FINISHED;
+	/*
+	 * Socket read end left open isn't a disaster if nobody
+	 * attempts to read from it (mingw compat headers do not
+	 * have SHUT_RD)...
+	 *
+	 * We can't fully close the socket since otherwise gtp
+	 * task would first close the socket it sends data to
+	 * while closing the ptg file descriptors.
+	 */
+	if (!t->src_is_sock)
+		close(t->src);
+	if (t->dest_is_sock)
+		shutdown(t->dest, SHUT_WR);
+	else
+		close(t->dest);
+}
+
+/*
+ * Join process, with apporiate errors on failure. Name is name for the
+ * process (for error messages). Returns 0 on success, 1 on failure.
+ */
+static int tloop_join(pid_t pid, const char *name)
+{
+	int tret;
+	if (waitpid(pid, &tret, 0) < 0) {
+		error("%s process failed to wait: %s", name, strerror(errno));
+		return 1;
+	}
+	if (!WIFEXITED(tret) || WEXITSTATUS(tret)) {
+		error("%s process failed", name);
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * Spawn the transfer tasks and then wait for them. Returns 0 on success,
+ * -1 on failure.
+ */
+static int tloop_spawnwait_tasks(struct bidirectional_transfer_state *s)
+{
+	pid_t pid1, pid2;
+	int ret = 0;
+
+	/* Fork thread #1: git to program. */
+	pid1 = fork();
+	if (pid1 < 0)
+		die_errno("Can't start thread for copying data");
+	else if (pid1 == 0) {
+		udt_kill_transfer(&s->ptg);
+		exit(udt_copy_task_routine(&s->gtp) ? 0 : 1);
+	}
+
+	/* Fork thread #2: program to git. */
+	pid2 = fork();
+	if (pid2 < 0)
+		die_errno("Can't start thread for copying data");
+	else if (pid2 == 0) {
+		udt_kill_transfer(&s->gtp);
+		exit(udt_copy_task_routine(&s->ptg) ? 0 : 1);
+	}
+
+	/*
+	 * Close both streams in parent as to not interfere with
+	 * end of file detection and wait for both tasks to finish.
+	 */
+	udt_kill_transfer(&s->gtp);
+	udt_kill_transfer(&s->ptg);
+	ret |= tloop_join(pid1, "Git to program copy");
+	ret |= tloop_join(pid2, "Program to git copy");
+	return ret;
+}
+#endif
+
+/*
+ * Copies data from stdin to output and from input to stdout simultaneously.
+ * Additionally filtering through given filter. If filter is NULL, uses
+ * identity filter.
+ */
+int bidirectional_transfer_loop(int input, int output)
+{
+	struct bidirectional_transfer_state state;
+
+	/* Fill the state fields. */
+	state.ptg.src = input;
+	state.ptg.dest = 1;
+	state.ptg.src_is_sock = (input == output);
+	state.ptg.dest_is_sock = 0;
+	state.ptg.state = SSTATE_TRANSFERING;
+	state.ptg.bufuse = 0;
+	state.ptg.src_name = "remote input";
+	state.ptg.dest_name = "stdout";
+
+	state.gtp.src = 0;
+	state.gtp.dest = output;
+	state.gtp.src_is_sock = 0;
+	state.gtp.dest_is_sock = (input == output);
+	state.gtp.state = SSTATE_TRANSFERING;
+	state.gtp.bufuse = 0;
+	state.gtp.src_name = "stdin";
+	state.gtp.dest_name = "remote output";
+
+	return tloop_spawnwait_tasks(&state);
+}
diff --git a/transport.c b/transport.c
index de0d587..1811b50 100644
--- a/transport.c
+++ b/transport.c
@@ -1,9 +1,6 @@
 #include "cache.h"
 #include "transport.h"
 #include "run-command.h"
-#ifndef NO_CURL
-#include "http.h"
-#endif
 #include "pkt-line.h"
 #include "fetch-pack.h"
 #include "send-pack.h"
@@ -11,6 +8,10 @@
 #include "bundle.h"
 #include "dir.h"
 #include "refs.h"
+#include "branch.h"
+#include "url.h"
+#include "submodule.h"
+#include "string-list.h"
 
 /* rsync support */
 
@@ -138,6 +139,53 @@
 	}
 }
 
+static void set_upstreams(struct transport *transport, struct ref *refs,
+	int pretend)
+{
+	struct ref *ref;
+	for (ref = refs; ref; ref = ref->next) {
+		const char *localname;
+		const char *tmp;
+		const char *remotename;
+		unsigned char sha[20];
+		int flag = 0;
+		/*
+		 * Check suitability for tracking. Must be successful /
+		 * already up-to-date ref create/modify (not delete).
+		 */
+		if (ref->status != REF_STATUS_OK &&
+			ref->status != REF_STATUS_UPTODATE)
+			continue;
+		if (!ref->peer_ref)
+			continue;
+		if (is_null_sha1(ref->new_sha1))
+			continue;
+
+		/* Follow symbolic refs (mainly for HEAD). */
+		localname = ref->peer_ref->name;
+		remotename = ref->name;
+		tmp = resolve_ref_unsafe(localname, sha, 1, &flag);
+		if (tmp && flag & REF_ISSYMREF &&
+			!prefixcmp(tmp, "refs/heads/"))
+			localname = tmp;
+
+		/* Both source and destination must be local branches. */
+		if (!localname || prefixcmp(localname, "refs/heads/"))
+			continue;
+		if (!remotename || prefixcmp(remotename, "refs/heads/"))
+			continue;
+
+		if (!pretend)
+			install_branch_config(BRANCH_CONFIG_VERBOSE,
+				localname + 11, transport->remote->name,
+				remotename);
+		else
+			printf("Would set upstream of '%s' to '%s' of '%s'\n",
+				localname + 11, remotename + 11,
+				transport->remote->name);
+	}
+}
+
 static const char *rsync_url(const char *url)
 {
 	return prefixcmp(url, "rsync://") ? skip_prefix(url, "rsync:") : url;
@@ -146,7 +194,7 @@
 static struct ref *get_refs_via_rsync(struct transport *transport, int for_push)
 {
 	struct strbuf buf = STRBUF_INIT, temp_dir = STRBUF_INIT;
-	struct ref dummy, *tail = &dummy;
+	struct ref dummy = {NULL}, *tail = &dummy;
 	struct child_process rsync;
 	const char *args[5];
 	int temp_dir_len;
@@ -168,7 +216,7 @@
 	rsync.argv = args;
 	rsync.stdout_to_stderr = 1;
 	args[0] = "rsync";
-	args[1] = (transport->verbose > 0) ? "-rv" : "-r";
+	args[1] = (transport->verbose > 1) ? "-rv" : "-r";
 	args[2] = buf.buf;
 	args[3] = temp_dir.buf;
 	args[4] = NULL;
@@ -207,7 +255,7 @@
 }
 
 static int fetch_objs_via_rsync(struct transport *transport,
-				int nr_objs, const struct ref **to_fetch)
+				int nr_objs, struct ref **to_fetch)
 {
 	struct strbuf buf = STRBUF_INIT;
 	struct child_process rsync;
@@ -221,7 +269,7 @@
 	rsync.argv = args;
 	rsync.stdout_to_stderr = 1;
 	args[0] = "rsync";
-	args[1] = (transport->verbose > 0) ? "-rv" : "-r";
+	args[1] = (transport->verbose > 1) ? "-rv" : "-r";
 	args[2] = "--ignore-existing";
 	args[3] = "--exclude";
 	args[4] = "info";
@@ -304,7 +352,7 @@
 	args[i++] = "-a";
 	if (flags & TRANSPORT_PUSH_DRY_RUN)
 		args[i++] = "--dry-run";
-	if (transport->verbose > 0)
+	if (transport->verbose > 1)
 		args[i++] = "-v";
 	args[i++] = "--ignore-existing";
 	args[i++] = "--exclude";
@@ -352,178 +400,6 @@
 	return result;
 }
 
-/* Generic functions for using commit walkers */
-
-#ifndef NO_CURL /* http fetch is the only user */
-static int fetch_objs_via_walker(struct transport *transport,
-				 int nr_objs, const struct ref **to_fetch)
-{
-	char *dest = xstrdup(transport->url);
-	struct walker *walker = transport->data;
-	char **objs = xmalloc(nr_objs * sizeof(*objs));
-	int i;
-
-	walker->get_all = 1;
-	walker->get_tree = 1;
-	walker->get_history = 1;
-	walker->get_verbosely = transport->verbose >= 0;
-	walker->get_recover = 0;
-
-	for (i = 0; i < nr_objs; i++)
-		objs[i] = xstrdup(sha1_to_hex(to_fetch[i]->old_sha1));
-
-	if (walker_fetch(walker, nr_objs, objs, NULL, NULL))
-		die("Fetch failed.");
-
-	for (i = 0; i < nr_objs; i++)
-		free(objs[i]);
-	free(objs);
-	free(dest);
-	return 0;
-}
-#endif /* NO_CURL */
-
-static int disconnect_walker(struct transport *transport)
-{
-	struct walker *walker = transport->data;
-	if (walker)
-		walker_free(walker);
-	return 0;
-}
-
-#ifndef NO_CURL
-static int curl_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags)
-{
-	const char **argv;
-	int argc;
-	int err;
-
-	if (flags & TRANSPORT_PUSH_MIRROR)
-		return error("http transport does not support mirror mode");
-
-	argv = xmalloc((refspec_nr + 12) * sizeof(char *));
-	argv[0] = "http-push";
-	argc = 1;
-	if (flags & TRANSPORT_PUSH_ALL)
-		argv[argc++] = "--all";
-	if (flags & TRANSPORT_PUSH_FORCE)
-		argv[argc++] = "--force";
-	if (flags & TRANSPORT_PUSH_DRY_RUN)
-		argv[argc++] = "--dry-run";
-	if (flags & TRANSPORT_PUSH_VERBOSE)
-		argv[argc++] = "--verbose";
-	argv[argc++] = transport->url;
-	while (refspec_nr--)
-		argv[argc++] = *refspec++;
-	argv[argc] = NULL;
-	err = run_command_v_opt(argv, RUN_GIT_CMD);
-	switch (err) {
-	case -ERR_RUN_COMMAND_FORK:
-		error("unable to fork for %s", argv[0]);
-	case -ERR_RUN_COMMAND_EXEC:
-		error("unable to exec %s", argv[0]);
-		break;
-	case -ERR_RUN_COMMAND_WAITPID:
-	case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
-	case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
-	case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
-		error("%s died with strange error", argv[0]);
-	}
-	return !!err;
-}
-
-static struct ref *get_refs_via_curl(struct transport *transport, int for_push)
-{
-	struct strbuf buffer = STRBUF_INIT;
-	char *data, *start, *mid;
-	char *ref_name;
-	char *refs_url;
-	int i = 0;
-	int http_ret;
-
-	struct ref *refs = NULL;
-	struct ref *ref = NULL;
-	struct ref *last_ref = NULL;
-
-	struct walker *walker;
-
-	if (for_push)
-		return NULL;
-
-	if (!transport->data)
-		transport->data = get_http_walker(transport->url,
-						transport->remote);
-
-	walker = transport->data;
-
-	refs_url = xmalloc(strlen(transport->url) + 11);
-	sprintf(refs_url, "%s/info/refs", transport->url);
-
-	http_ret = http_get_strbuf(refs_url, &buffer, HTTP_NO_CACHE);
-	switch (http_ret) {
-	case HTTP_OK:
-		break;
-	case HTTP_MISSING_TARGET:
-		die("%s not found: did you run git update-server-info on the"
-		    " server?", refs_url);
-	default:
-		http_error(refs_url, http_ret);
-		die("HTTP request failed");
-	}
-
-	data = buffer.buf;
-	start = NULL;
-	mid = data;
-	while (i < buffer.len) {
-		if (!start)
-			start = &data[i];
-		if (data[i] == '\t')
-			mid = &data[i];
-		if (data[i] == '\n') {
-			data[i] = 0;
-			ref_name = mid + 1;
-			ref = xmalloc(sizeof(struct ref) +
-				      strlen(ref_name) + 1);
-			memset(ref, 0, sizeof(struct ref));
-			strcpy(ref->name, ref_name);
-			get_sha1_hex(start, ref->old_sha1);
-			if (!refs)
-				refs = ref;
-			if (last_ref)
-				last_ref->next = ref;
-			last_ref = ref;
-			start = NULL;
-		}
-		i++;
-	}
-
-	strbuf_release(&buffer);
-
-	ref = alloc_ref("HEAD");
-	if (!walker->fetch_ref(walker, ref) &&
-	    !resolve_remote_symref(ref, refs)) {
-		ref->next = refs;
-		refs = ref;
-	} else {
-		free(ref);
-	}
-
-	strbuf_release(&buffer);
-	free(refs_url);
-	return refs;
-}
-
-static int fetch_objs_via_curl(struct transport *transport,
-				 int nr_objs, const struct ref **to_fetch)
-{
-	if (!transport->data)
-		transport->data = get_http_walker(transport->url,
-						transport->remote);
-	return fetch_objs_via_walker(transport, nr_objs, to_fetch);
-}
-
-#endif
-
 struct bundle_transport_data {
 	int fd;
 	struct bundle_header header;
@@ -554,10 +430,11 @@
 }
 
 static int fetch_refs_from_bundle(struct transport *transport,
-			       int nr_heads, const struct ref **to_fetch)
+			       int nr_heads, struct ref **to_fetch)
 {
 	struct bundle_transport_data *data = transport->data;
-	return unbundle(&data->header, data->fd);
+	return unbundle(&data->header, data->fd,
+			transport->progress ? BUNDLE_VERBOSE : 0);
 }
 
 static int close_bundle(struct transport *transport)
@@ -570,41 +447,40 @@
 }
 
 struct git_transport_data {
-	unsigned thin : 1;
-	unsigned keep : 1;
-	unsigned followtags : 1;
-	int depth;
+	struct git_transport_options options;
 	struct child_process *conn;
 	int fd[2];
-	const char *uploadpack;
-	const char *receivepack;
+	unsigned got_remote_heads : 1;
 	struct extra_have_objects extra_have;
 };
 
-static int set_git_option(struct transport *connection,
+static int set_git_option(struct git_transport_options *opts,
 			  const char *name, const char *value)
 {
-	struct git_transport_data *data = connection->data;
 	if (!strcmp(name, TRANS_OPT_UPLOADPACK)) {
-		data->uploadpack = value;
+		opts->uploadpack = value;
 		return 0;
 	} else if (!strcmp(name, TRANS_OPT_RECEIVEPACK)) {
-		data->receivepack = value;
+		opts->receivepack = value;
 		return 0;
 	} else if (!strcmp(name, TRANS_OPT_THIN)) {
-		data->thin = !!value;
+		opts->thin = !!value;
 		return 0;
 	} else if (!strcmp(name, TRANS_OPT_FOLLOWTAGS)) {
-		data->followtags = !!value;
+		opts->followtags = !!value;
 		return 0;
 	} else if (!strcmp(name, TRANS_OPT_KEEP)) {
-		data->keep = !!value;
+		opts->keep = !!value;
 		return 0;
 	} else if (!strcmp(name, TRANS_OPT_DEPTH)) {
 		if (!value)
-			data->depth = 0;
-		else
-			data->depth = atoi(value);
+			opts->depth = 0;
+		else {
+			char *end;
+			opts->depth = strtol(value, &end, 0);
+			if (*end)
+				die("transport: invalid depth option '%s'", value);
+		}
 		return 0;
 	}
 	return 1;
@@ -613,9 +489,15 @@
 static int connect_setup(struct transport *transport, int for_push, int verbose)
 {
 	struct git_transport_data *data = transport->data;
+
+	if (data->conn)
+		return 0;
+
 	data->conn = git_connect(data->fd, transport->url,
-				 for_push ? data->receivepack : data->uploadpack,
+				 for_push ? data->options.receivepack :
+				 data->options.uploadpack,
 				 verbose ? CONNECT_VERBOSE : 0);
+
 	return 0;
 }
 
@@ -625,14 +507,15 @@
 	struct ref *refs;
 
 	connect_setup(transport, for_push, 0);
-	get_remote_heads(data->fd[0], &refs, 0, NULL,
+	get_remote_heads(data->fd[0], &refs,
 			 for_push ? REF_NORMAL : 0, &data->extra_have);
+	data->got_remote_heads = 1;
 
 	return refs;
 }
 
 static int fetch_refs_via_pack(struct transport *transport,
-			       int nr_heads, const struct ref **to_fetch)
+			       int nr_heads, struct ref **to_fetch)
 {
 	struct git_transport_data *data = transport->data;
 	char **heads = xmalloc(nr_heads * sizeof(*heads));
@@ -644,22 +527,23 @@
 	struct ref *refs_tmp = NULL;
 
 	memset(&args, 0, sizeof(args));
-	args.uploadpack = data->uploadpack;
-	args.keep_pack = data->keep;
+	args.uploadpack = data->options.uploadpack;
+	args.keep_pack = data->options.keep;
 	args.lock_pack = 1;
-	args.use_thin_pack = data->thin;
-	args.include_tag = data->followtags;
-	args.verbose = (transport->verbose > 0);
+	args.use_thin_pack = data->options.thin;
+	args.include_tag = data->options.followtags;
+	args.verbose = (transport->verbose > 1);
 	args.quiet = (transport->verbose < 0);
-	args.no_progress = args.quiet || (!transport->progress && !isatty(1));
-	args.depth = data->depth;
+	args.no_progress = !transport->progress;
+	args.depth = data->options.depth;
 
 	for (i = 0; i < nr_heads; i++)
 		origh[i] = heads[i] = xstrdup(to_fetch[i]->name);
 
-	if (!data->conn) {
+	if (!data->got_remote_heads) {
 		connect_setup(transport, 0, 0);
-		get_remote_heads(data->fd[0], &refs_tmp, 0, NULL, 0, NULL);
+		get_remote_heads(data->fd[0], &refs_tmp, 0, NULL);
+		data->got_remote_heads = 1;
 	}
 
 	refs = fetch_pack(&args, data->fd, data->conn,
@@ -670,6 +554,7 @@
 	if (finish_connect(data->conn))
 		refs = NULL;
 	data->conn = NULL;
+	data->got_remote_heads = 0;
 
 	free_refs(refs_tmp);
 
@@ -681,7 +566,22 @@
 	return (refs ? 0 : -1);
 }
 
-static int refs_pushed(struct ref *ref)
+static int push_had_errors(struct ref *ref)
+{
+	for (; ref; ref = ref->next) {
+		switch (ref->status) {
+		case REF_STATUS_NONE:
+		case REF_STATUS_UPTODATE:
+		case REF_STATUS_OK:
+			break;
+		default:
+			return 1;
+		}
+	}
+	return 0;
+}
+
+int transport_refs_pushed(struct ref *ref)
 {
 	for (; ref; ref = ref->next) {
 		switch(ref->status) {
@@ -695,7 +595,7 @@
 	return 0;
 }
 
-static void update_tracking_ref(struct remote *remote, struct ref *ref, int verbose)
+void transport_update_tracking_ref(struct remote *remote, struct ref *ref, int verbose)
 {
 	struct refspec rs;
 
@@ -717,8 +617,6 @@
 	}
 }
 
-#define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
-
 static void print_ref_status(char flag, const char *summary, struct ref *to, struct ref *from, const char *msg, int porcelain)
 {
 	if (porcelain) {
@@ -731,7 +629,7 @@
 		else
 			fprintf(stdout, "%s\n", summary);
 	} else {
-		fprintf(stderr, " %c %-*s ", flag, SUMMARY_WIDTH, summary);
+		fprintf(stderr, " %c %-*s ", flag, TRANSPORT_SUMMARY_WIDTH, summary);
 		if (from)
 			fprintf(stderr, "%s -> %s", prettify_refname(from->name), prettify_refname(to->name));
 		else
@@ -783,7 +681,7 @@
 static int print_one_push_status(struct ref *ref, const char *dest, int count, int porcelain)
 {
 	if (!count)
-		fprintf(stderr, "To %s\n", dest);
+		fprintf(porcelain ? stdout : stderr, "To %s\n", dest);
 
 	switch(ref->status) {
 	case REF_STATUS_NONE:
@@ -799,7 +697,7 @@
 		break;
 	case REF_STATUS_REJECT_NONFASTFORWARD:
 		print_ref_status('!', "[rejected]", ref, ref->peer_ref,
-						 "non-fast forward", porcelain);
+						 "non-fast-forward", porcelain);
 		break;
 	case REF_STATUS_REMOTE_REJECT:
 		print_ref_status('!', "[remote rejected]", ref,
@@ -819,11 +717,15 @@
 	return 1;
 }
 
-static void print_push_status(const char *dest, struct ref *refs,
-							  int verbose, int porcelain)
+void transport_print_push_status(const char *dest, struct ref *refs,
+				  int verbose, int porcelain, int *nonfastforward)
 {
 	struct ref *ref;
 	int n = 0;
+	unsigned char head_sha1[20];
+	char *head;
+
+	head = resolve_refdup("HEAD", head_sha1, 1, NULL);
 
 	if (verbose) {
 		for (ref = refs; ref; ref = ref->next)
@@ -835,15 +737,23 @@
 		if (ref->status == REF_STATUS_OK)
 			n += print_one_push_status(ref, dest, n, porcelain);
 
+	*nonfastforward = 0;
 	for (ref = refs; ref; ref = ref->next) {
 		if (ref->status != REF_STATUS_NONE &&
 		    ref->status != REF_STATUS_UPTODATE &&
 		    ref->status != REF_STATUS_OK)
 			n += print_one_push_status(ref, dest, n, porcelain);
+		if (ref->status == REF_STATUS_REJECT_NONFASTFORWARD &&
+		    *nonfastforward != NON_FF_HEAD) {
+			if (!strcmp(head, ref->name))
+				*nonfastforward = NON_FF_HEAD;
+			else
+				*nonfastforward = NON_FF_OTHER;
+		}
 	}
 }
 
-static void verify_remote_names(int nr_heads, const char **heads)
+void transport_verify_remote_names(int nr_heads, const char **heads)
 {
 	int i;
 
@@ -859,18 +769,10 @@
 			continue;
 
 		remote = remote ? (remote + 1) : local;
-		switch (check_ref_format(remote)) {
-		case 0: /* ok */
-		case CHECK_REF_FORMAT_ONELEVEL:
-			/* ok but a single level -- that is fine for
-			 * a match pattern.
-			 */
-		case CHECK_REF_FORMAT_WILDCARD:
-			/* ok but ends with a pattern-match character */
-			continue;
-		}
-		die("remote part of refspec is not a valid name in %s",
-		    heads[i]);
+		if (check_refname_format(remote,
+				REFNAME_ALLOW_ONELEVEL|REFNAME_REFSPEC_PATTERN))
+			die("remote part of refspec is not a valid name in %s",
+				heads[i]);
 	}
 }
 
@@ -880,19 +782,23 @@
 	struct send_pack_args args;
 	int ret;
 
-	if (!data->conn) {
+	if (!data->got_remote_heads) {
 		struct ref *tmp_refs;
 		connect_setup(transport, 1, 0);
 
-		get_remote_heads(data->fd[0], &tmp_refs, 0, NULL, REF_NORMAL,
-				 NULL);
+		get_remote_heads(data->fd[0], &tmp_refs, REF_NORMAL, NULL);
+		data->got_remote_heads = 1;
 	}
 
+	memset(&args, 0, sizeof(args));
 	args.send_mirror = !!(flags & TRANSPORT_PUSH_MIRROR);
 	args.force_update = !!(flags & TRANSPORT_PUSH_FORCE);
-	args.use_thin_pack = data->thin;
-	args.verbose = !!(flags & TRANSPORT_PUSH_VERBOSE);
+	args.use_thin_pack = data->options.thin;
+	args.verbose = (transport->verbose > 0);
+	args.quiet = (transport->verbose < 0);
+	args.progress = transport->progress;
 	args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN);
+	args.porcelain = !!(flags & TRANSPORT_PUSH_PORCELAIN);
 
 	ret = send_pack(&args, data->fd, data->conn, remote_refs,
 			&data->extra_have);
@@ -901,15 +807,28 @@
 	close(data->fd[0]);
 	ret |= finish_connect(data->conn);
 	data->conn = NULL;
+	data->got_remote_heads = 0;
 
 	return ret;
 }
 
+static int connect_git(struct transport *transport, const char *name,
+		       const char *executable, int fd[2])
+{
+	struct git_transport_data *data = transport->data;
+	data->conn = git_connect(data->fd, transport->url,
+				 executable, 0);
+	fd[0] = data->fd[0];
+	fd[1] = data->fd[1];
+	return 0;
+}
+
 static int disconnect_git(struct transport *transport)
 {
 	struct git_transport_data *data = transport->data;
 	if (data->conn) {
-		packet_flush(data->fd[1]);
+		if (data->got_remote_heads)
+			packet_flush(data->fd[1]);
 		close(data->fd[0]);
 		close(data->fd[1]);
 		finish_connect(data->conn);
@@ -919,6 +838,32 @@
 	return 0;
 }
 
+void transport_take_over(struct transport *transport,
+			 struct child_process *child)
+{
+	struct git_transport_data *data;
+
+	if (!transport->smart_options)
+		die("Bug detected: Taking over transport requires non-NULL "
+		    "smart_options field.");
+
+	data = xcalloc(1, sizeof(*data));
+	data->options = *transport->smart_options;
+	data->conn = child;
+	data->fd[0] = data->conn->out;
+	data->fd[1] = data->conn->in;
+	data->got_remote_heads = 0;
+	transport->data = data;
+
+	transport->set_option = NULL;
+	transport->get_refs_list = get_refs_via_connect;
+	transport->fetch = fetch_refs_via_pack;
+	transport->push = NULL;
+	transport->push_refs = git_transport_push;
+	transport->disconnect = disconnect_git;
+	transport->smart_options = &(data->options);
+}
+
 static int is_local(const char *url)
 {
 	const char *colon = strchr(url, ':');
@@ -935,54 +880,89 @@
 	return S_ISREG(buf.st_mode);
 }
 
+static int external_specification_len(const char *url)
+{
+	return strchr(url, ':') - url;
+}
+
 struct transport *transport_get(struct remote *remote, const char *url)
 {
+	const char *helper;
 	struct transport *ret = xcalloc(1, sizeof(*ret));
 
+	ret->progress = isatty(2);
+
+	if (!remote)
+		die("No remote provided to transport_get()");
+
+	ret->got_remote_refs = 0;
 	ret->remote = remote;
+	helper = remote->foreign_vcs;
+
+	if (!url && remote->url)
+		url = remote->url[0];
 	ret->url = url;
 
-	if (!prefixcmp(url, "rsync:")) {
+	/* maybe it is a foreign URL? */
+	if (url) {
+		const char *p = url;
+
+		while (is_urlschemechar(p == url, *p))
+			p++;
+		if (!prefixcmp(p, "::"))
+			helper = xstrndup(url, p - url);
+	}
+
+	if (helper) {
+		transport_helper_init(ret, helper);
+	} else if (!prefixcmp(url, "rsync:")) {
 		ret->get_refs_list = get_refs_via_rsync;
 		ret->fetch = fetch_objs_via_rsync;
 		ret->push = rsync_transport_push;
-
-	} else if (!prefixcmp(url, "http://")
-	        || !prefixcmp(url, "https://")
-	        || !prefixcmp(url, "ftp://")) {
-#ifdef NO_CURL
-		error("git was compiled without libcurl support.");
-#else
-		ret->get_refs_list = get_refs_via_curl;
-		ret->fetch = fetch_objs_via_curl;
-		ret->push = curl_transport_push;
-#endif
-		ret->disconnect = disconnect_walker;
-
-	} else if (is_local(url) && is_file(url)) {
+		ret->smart_options = NULL;
+	} else if (is_local(url) && is_file(url) && is_bundle(url, 1)) {
 		struct bundle_transport_data *data = xcalloc(1, sizeof(*data));
 		ret->data = data;
 		ret->get_refs_list = get_refs_from_bundle;
 		ret->fetch = fetch_refs_from_bundle;
 		ret->disconnect = close_bundle;
-
-	} else {
+		ret->smart_options = NULL;
+	} else if (!is_url(url)
+		|| !prefixcmp(url, "file://")
+		|| !prefixcmp(url, "git://")
+		|| !prefixcmp(url, "ssh://")
+		|| !prefixcmp(url, "git+ssh://")
+		|| !prefixcmp(url, "ssh+git://")) {
+		/* These are builtin smart transports. */
 		struct git_transport_data *data = xcalloc(1, sizeof(*data));
 		ret->data = data;
-		ret->set_option = set_git_option;
+		ret->set_option = NULL;
 		ret->get_refs_list = get_refs_via_connect;
 		ret->fetch = fetch_refs_via_pack;
 		ret->push_refs = git_transport_push;
+		ret->connect = connect_git;
 		ret->disconnect = disconnect_git;
+		ret->smart_options = &(data->options);
 
-		data->thin = 1;
 		data->conn = NULL;
-		data->uploadpack = "git-upload-pack";
-		if (remote && remote->uploadpack)
-			data->uploadpack = remote->uploadpack;
-		data->receivepack = "git-receive-pack";
-		if (remote && remote->receivepack)
-			data->receivepack = remote->receivepack;
+		data->got_remote_heads = 0;
+	} else {
+		/* Unknown protocol in URL. Pass to external handler. */
+		int len = external_specification_len(url);
+		char *handler = xmalloc(len + 1);
+		handler[len] = 0;
+		strncpy(handler, url, len);
+		transport_helper_init(ret, handler);
+	}
+
+	if (ret->smart_options) {
+		ret->smart_options->thin = 1;
+		ret->smart_options->uploadpack = "git-upload-pack";
+		if (remote->uploadpack)
+			ret->smart_options->uploadpack = remote->uploadpack;
+		ret->smart_options->receivepack = "git-receive-pack";
+		if (remote->receivepack)
+			ret->smart_options->receivepack = remote->receivepack;
 	}
 
 	return ret;
@@ -991,49 +971,154 @@
 int transport_set_option(struct transport *transport,
 			 const char *name, const char *value)
 {
+	int git_reports = 1, protocol_reports = 1;
+
+	if (transport->smart_options)
+		git_reports = set_git_option(transport->smart_options,
+					     name, value);
+
 	if (transport->set_option)
-		return transport->set_option(transport, name, value);
+		protocol_reports = transport->set_option(transport, name,
+							value);
+
+	/* If either report is 0, report 0 (success). */
+	if (!git_reports || !protocol_reports)
+		return 0;
+	/* If either reports -1 (invalid value), report -1. */
+	if ((git_reports == -1) || (protocol_reports == -1))
+		return -1;
+	/* Otherwise if both report unknown, report unknown. */
 	return 1;
 }
 
-int transport_push(struct transport *transport,
-		   int refspec_nr, const char **refspec, int flags)
+void transport_set_verbosity(struct transport *transport, int verbosity,
+	int force_progress)
 {
-	verify_remote_names(refspec_nr, refspec);
+	if (verbosity >= 1)
+		transport->verbose = verbosity <= 3 ? verbosity : 3;
+	if (verbosity < 0)
+		transport->verbose = -1;
 
-	if (transport->push)
+	/**
+	 * Rules used to determine whether to report progress (processing aborts
+	 * when a rule is satisfied):
+	 *
+	 *   . Report progress, if force_progress is 1 (ie. --progress).
+	 *   . Don't report progress, if force_progress is 0 (ie. --no-progress).
+	 *   . Don't report progress, if verbosity < 0 (ie. -q/--quiet ).
+	 *   . Report progress if isatty(2) is 1.
+	 **/
+	if (force_progress >= 0)
+		transport->progress = !!force_progress;
+	else
+		transport->progress = verbosity >= 0 && isatty(2);
+}
+
+static void die_with_unpushed_submodules(struct string_list *needs_pushing)
+{
+	int i;
+
+	fprintf(stderr, "The following submodule paths contain changes that can\n"
+			"not be found on any remote:\n");
+	for (i = 0; i < needs_pushing->nr; i++)
+		printf("  %s\n", needs_pushing->items[i].string);
+	fprintf(stderr, "\nPlease try\n\n"
+			"	git push --recurse-submodules=on-demand\n\n"
+			"or cd to the path and use\n\n"
+			"	git push\n\n"
+			"to push them to a remote.\n\n");
+
+	string_list_clear(needs_pushing, 0);
+
+	die("Aborting.");
+}
+
+int transport_push(struct transport *transport,
+		   int refspec_nr, const char **refspec, int flags,
+		   int *nonfastforward)
+{
+	*nonfastforward = 0;
+	transport_verify_remote_names(refspec_nr, refspec);
+
+	if (transport->push) {
+		/* Maybe FIXME. But no important transport uses this case. */
+		if (flags & TRANSPORT_PUSH_SET_UPSTREAM)
+			die("This transport does not support using --set-upstream");
+
 		return transport->push(transport, refspec_nr, refspec, flags);
-	if (transport->push_refs) {
+	} else if (transport->push_refs) {
 		struct ref *remote_refs =
 			transport->get_refs_list(transport, 1);
 		struct ref *local_refs = get_local_heads();
 		int match_flags = MATCH_REFS_NONE;
-		int verbose = flags & TRANSPORT_PUSH_VERBOSE;
+		int verbose = (transport->verbose > 0);
+		int quiet = (transport->verbose < 0);
 		int porcelain = flags & TRANSPORT_PUSH_PORCELAIN;
-		int ret;
+		int pretend = flags & TRANSPORT_PUSH_DRY_RUN;
+		int push_ret, ret, err;
 
 		if (flags & TRANSPORT_PUSH_ALL)
 			match_flags |= MATCH_REFS_ALL;
 		if (flags & TRANSPORT_PUSH_MIRROR)
 			match_flags |= MATCH_REFS_MIRROR;
+		if (flags & TRANSPORT_PUSH_PRUNE)
+			match_flags |= MATCH_REFS_PRUNE;
 
-		if (match_refs(local_refs, &remote_refs,
-			       refspec_nr, refspec, match_flags)) {
+		if (match_push_refs(local_refs, &remote_refs,
+				    refspec_nr, refspec, match_flags)) {
 			return -1;
 		}
 
-		ret = transport->push_refs(transport, remote_refs, flags);
+		set_ref_status_for_push(remote_refs,
+			flags & TRANSPORT_PUSH_MIRROR,
+			flags & TRANSPORT_PUSH_FORCE);
 
-		print_push_status(transport->url, remote_refs, verbose | porcelain, porcelain);
+		if ((flags & TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND) && !is_bare_repository()) {
+			struct ref *ref = remote_refs;
+			for (; ref; ref = ref->next)
+				if (!is_null_sha1(ref->new_sha1) &&
+				    !push_unpushed_submodules(ref->new_sha1,
+					    transport->remote->name))
+				    die ("Failed to push all needed submodules!");
+		}
+
+		if ((flags & (TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND |
+			      TRANSPORT_RECURSE_SUBMODULES_CHECK)) && !is_bare_repository()) {
+			struct ref *ref = remote_refs;
+			struct string_list needs_pushing;
+
+			memset(&needs_pushing, 0, sizeof(struct string_list));
+			needs_pushing.strdup_strings = 1;
+			for (; ref; ref = ref->next)
+				if (!is_null_sha1(ref->new_sha1) &&
+				    find_unpushed_submodules(ref->new_sha1,
+					    transport->remote->name, &needs_pushing))
+					die_with_unpushed_submodules(&needs_pushing);
+		}
+
+		push_ret = transport->push_refs(transport, remote_refs, flags);
+		err = push_had_errors(remote_refs);
+		ret = push_ret | err;
+
+		if (!quiet || err)
+			transport_print_push_status(transport->url, remote_refs,
+					verbose | porcelain, porcelain,
+					nonfastforward);
+
+		if (flags & TRANSPORT_PUSH_SET_UPSTREAM)
+			set_upstreams(transport, remote_refs, pretend);
 
 		if (!(flags & TRANSPORT_PUSH_DRY_RUN)) {
 			struct ref *ref;
 			for (ref = remote_refs; ref; ref = ref->next)
-				update_tracking_ref(transport->remote, ref, verbose);
+				transport_update_tracking_ref(transport->remote, ref, verbose);
 		}
 
-		if (!ret && !refs_pushed(remote_refs))
+		if (porcelain && !push_ret)
+			puts("Done");
+		else if (!quiet && !ret && !transport_refs_pushed(remote_refs))
 			fprintf(stderr, "Everything up-to-date\n");
+
 		return ret;
 	}
 	return 1;
@@ -1041,27 +1126,46 @@
 
 const struct ref *transport_get_remote_refs(struct transport *transport)
 {
-	if (!transport->remote_refs)
+	if (!transport->got_remote_refs) {
 		transport->remote_refs = transport->get_refs_list(transport, 0);
+		transport->got_remote_refs = 1;
+	}
+
 	return transport->remote_refs;
 }
 
-int transport_fetch_refs(struct transport *transport, const struct ref *refs)
+int transport_fetch_refs(struct transport *transport, struct ref *refs)
 {
 	int rc;
-	int nr_heads = 0, nr_alloc = 0;
-	const struct ref **heads = NULL;
-	const struct ref *rm;
+	int nr_heads = 0, nr_alloc = 0, nr_refs = 0;
+	struct ref **heads = NULL;
+	struct ref *rm;
 
 	for (rm = refs; rm; rm = rm->next) {
+		nr_refs++;
 		if (rm->peer_ref &&
+		    !is_null_sha1(rm->old_sha1) &&
 		    !hashcmp(rm->peer_ref->old_sha1, rm->old_sha1))
 			continue;
 		ALLOC_GROW(heads, nr_heads + 1, nr_alloc);
 		heads[nr_heads++] = rm;
 	}
 
+	if (!nr_heads) {
+		/*
+		 * When deepening of a shallow repository is requested,
+		 * then local and remote refs are likely to still be equal.
+		 * Just feed them all to the fetch method in that case.
+		 * This condition shouldn't be met in a non-deepening fetch
+		 * (see builtin-fetch.c:quickfetch()).
+		 */
+		heads = xmalloc(nr_refs * sizeof(*heads));
+		for (rm = refs; rm; rm = rm->next)
+			heads[nr_heads++] = rm;
+	}
+
 	rc = transport->fetch(transport, nr_heads, heads);
+
 	free(heads);
 	return rc;
 }
@@ -1075,6 +1179,15 @@
 	}
 }
 
+int transport_connect(struct transport *transport, const char *name,
+		      const char *exec, int fd[2])
+{
+	if (transport->connect)
+		return transport->connect(transport, name, exec, fd);
+	else
+		die("Operation not supported by protocol");
+}
+
 int transport_disconnect(struct transport *transport)
 {
 	int ret = 0;
@@ -1085,7 +1198,7 @@
 }
 
 /*
- * Strip username (and password) from an url and return
+ * Strip username (and password) from a URL and return
  * it in a newly allocated string.
  */
 char *transport_anonymize_url(const char *url)
@@ -1131,3 +1244,51 @@
 literal_copy:
 	return xstrdup(url);
 }
+
+struct alternate_refs_data {
+	alternate_ref_fn *fn;
+	void *data;
+};
+
+static int refs_from_alternate_cb(struct alternate_object_database *e,
+				  void *data)
+{
+	char *other;
+	size_t len;
+	struct remote *remote;
+	struct transport *transport;
+	const struct ref *extra;
+	struct alternate_refs_data *cb = data;
+
+	e->name[-1] = '\0';
+	other = xstrdup(real_path(e->base));
+	e->name[-1] = '/';
+	len = strlen(other);
+
+	while (other[len-1] == '/')
+		other[--len] = '\0';
+	if (len < 8 || memcmp(other + len - 8, "/objects", 8))
+		return 0;
+	/* Is this a git repository with refs? */
+	memcpy(other + len - 8, "/refs", 6);
+	if (!is_directory(other))
+		return 0;
+	other[len - 8] = '\0';
+	remote = remote_get(other);
+	transport = transport_get(remote, other);
+	for (extra = transport_get_remote_refs(transport);
+	     extra;
+	     extra = extra->next)
+		cb->fn(extra, cb->data);
+	transport_disconnect(transport);
+	free(other);
+	return 0;
+}
+
+void for_each_alternate_ref(alternate_ref_fn fn, void *data)
+{
+	struct alternate_refs_data cb;
+	cb.fn = fn;
+	cb.data = data;
+	foreach_alt_odb(refs_from_alternate_cb, &cb);
+}
diff --git a/transport.h b/transport.h
index 51b5397..b866c12 100644
--- a/transport.h
+++ b/transport.h
@@ -4,6 +4,15 @@
 #include "cache.h"
 #include "remote.h"
 
+struct git_transport_options {
+	unsigned thin : 1;
+	unsigned keep : 1;
+	unsigned followtags : 1;
+	int depth;
+	const char *uploadpack;
+	const char *receivepack;
+};
+
 struct transport {
 	struct remote *remote;
 	const char *url;
@@ -11,6 +20,12 @@
 	const struct ref *remote_refs;
 
 	/**
+	 * Indicates whether we already called get_refs_list(); set by
+	 * transport.c::transport_get_remote_refs().
+	 */
+	unsigned got_remote_refs : 1;
+
+	/**
 	 * Returns 0 if successful, positive if the option is not
 	 * recognized or is inapplicable, and negative if the option
 	 * is applicable but the value is invalid.
@@ -18,24 +33,79 @@
 	int (*set_option)(struct transport *connection, const char *name,
 			  const char *value);
 
+	/**
+	 * Returns a list of the remote side's refs. In order to allow
+	 * the transport to try to share connections, for_push is a
+	 * hint as to whether the ultimate operation is a push or a fetch.
+	 *
+	 * If the transport is able to determine the remote hash for
+	 * the ref without a huge amount of effort, it should store it
+	 * in the ref's old_sha1 field; otherwise it should be all 0.
+	 **/
 	struct ref *(*get_refs_list)(struct transport *transport, int for_push);
-	int (*fetch)(struct transport *transport, int refs_nr, const struct ref **refs);
+
+	/**
+	 * Fetch the objects for the given refs. Note that this gets
+	 * an array, and should ignore the list structure.
+	 *
+	 * If the transport did not get hashes for refs in
+	 * get_refs_list(), it should set the old_sha1 fields in the
+	 * provided refs now.
+	 **/
+	int (*fetch)(struct transport *transport, int refs_nr, struct ref **refs);
+
+	/**
+	 * Push the objects and refs. Send the necessary objects, and
+	 * then, for any refs where peer_ref is set and
+	 * peer_ref->new_sha1 is different from old_sha1, tell the
+	 * remote side to update each ref in the list from old_sha1 to
+	 * peer_ref->new_sha1.
+	 *
+	 * Where possible, set the status for each ref appropriately.
+	 *
+	 * The transport must modify new_sha1 in the ref to the new
+	 * value if the remote accepted the change. Note that this
+	 * could be a different value from peer_ref->new_sha1 if the
+	 * process involved generating new commits.
+	 **/
 	int (*push_refs)(struct transport *transport, struct ref *refs, int flags);
 	int (*push)(struct transport *connection, int refspec_nr, const char **refspec, int flags);
+	int (*connect)(struct transport *connection, const char *name,
+		       const char *executable, int fd[2]);
 
+	/** get_refs_list(), fetch(), and push_refs() can keep
+	 * resources (such as a connection) reserved for futher
+	 * use. disconnect() releases these resources.
+	 **/
 	int (*disconnect)(struct transport *connection);
 	char *pack_lockfile;
-	signed verbose : 2;
-	/* Force progress even if the output is not a tty */
+	signed verbose : 3;
+	/**
+	 * Transports should not set this directly, and should use this
+	 * value without having to check isatty(2), -q/--quiet
+	 * (transport->verbose < 0), etc. - checking has already been done
+	 * in transport_set_verbosity().
+	 **/
 	unsigned progress : 1;
+	/*
+	 * If transport is at least potentially smart, this points to
+	 * git_transport_options structure to use in case transport
+	 * actually turns out to be smart.
+	 */
+	struct git_transport_options *smart_options;
 };
 
 #define TRANSPORT_PUSH_ALL 1
 #define TRANSPORT_PUSH_FORCE 2
 #define TRANSPORT_PUSH_DRY_RUN 4
 #define TRANSPORT_PUSH_MIRROR 8
-#define TRANSPORT_PUSH_VERBOSE 16
-#define TRANSPORT_PUSH_PORCELAIN 32
+#define TRANSPORT_PUSH_PORCELAIN 16
+#define TRANSPORT_PUSH_SET_UPSTREAM 32
+#define TRANSPORT_RECURSE_SUBMODULES_CHECK 64
+#define TRANSPORT_PUSH_PRUNE 128
+#define TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND 256
+
+#define TRANSPORT_SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
 
 /* Returns a transport suitable for the url */
 struct transport *transport_get(struct remote *, const char *);
@@ -66,15 +136,42 @@
  **/
 int transport_set_option(struct transport *transport, const char *name,
 			 const char *value);
+void transport_set_verbosity(struct transport *transport, int verbosity,
+	int force_progress);
 
+#define NON_FF_HEAD 1
+#define NON_FF_OTHER 2
 int transport_push(struct transport *connection,
-		   int refspec_nr, const char **refspec, int flags);
+		   int refspec_nr, const char **refspec, int flags,
+		   int * nonfastforward);
 
 const struct ref *transport_get_remote_refs(struct transport *transport);
 
-int transport_fetch_refs(struct transport *transport, const struct ref *refs);
+int transport_fetch_refs(struct transport *transport, struct ref *refs);
 void transport_unlock_pack(struct transport *transport);
 int transport_disconnect(struct transport *transport);
 char *transport_anonymize_url(const char *url);
+void transport_take_over(struct transport *transport,
+			 struct child_process *child);
+
+int transport_connect(struct transport *transport, const char *name,
+		      const char *exec, int fd[2]);
+
+/* Transport methods defined outside transport.c */
+int transport_helper_init(struct transport *transport, const char *name);
+int bidirectional_transfer_loop(int input, int output);
+
+/* common methods used by transport.c and builtin-send-pack.c */
+void transport_verify_remote_names(int nr_heads, const char **heads);
+
+void transport_update_tracking_ref(struct remote *remote, struct ref *ref, int verbose);
+
+int transport_refs_pushed(struct ref *ref);
+
+void transport_print_push_status(const char *dest, struct ref *refs,
+		  int verbose, int porcelain, int *nonfastforward);
+
+typedef void alternate_ref_fn(const struct ref *, void *);
+extern void for_each_alternate_ref(alternate_ref_fn, void *);
 
 #endif
diff --git a/tree-diff.c b/tree-diff.c
index 0459e54..28ad6db 100644
--- a/tree-diff.c
+++ b/tree-diff.c
@@ -6,47 +6,30 @@
 #include "diffcore.h"
 #include "tree.h"
 
-static char *malloc_base(const char *base, int baselen, const char *path, int pathlen)
-{
-	char *newbase = xmalloc(baselen + pathlen + 2);
-	memcpy(newbase, base, baselen);
-	memcpy(newbase + baselen, path, pathlen);
-	memcpy(newbase + baselen + pathlen, "/", 2);
-	return newbase;
-}
+static void show_entry(struct diff_options *opt, const char *prefix,
+		       struct tree_desc *desc, struct strbuf *base);
 
-static char *malloc_fullname(const char *base, int baselen, const char *path, int pathlen)
-{
-	char *fullname = xmalloc(baselen + pathlen + 1);
-	memcpy(fullname, base, baselen);
-	memcpy(fullname + baselen, path, pathlen);
-	fullname[baselen + pathlen] = 0;
-	return fullname;
-}
-
-static void show_entry(struct diff_options *opt, const char *prefix, struct tree_desc *desc,
-		       const char *base, int baselen);
-
-static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const char *base, int baselen, struct diff_options *opt)
+static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2,
+			      struct strbuf *base, struct diff_options *opt)
 {
 	unsigned mode1, mode2;
 	const char *path1, *path2;
 	const unsigned char *sha1, *sha2;
 	int cmp, pathlen1, pathlen2;
-	char *fullname;
+	int old_baselen = base->len;
 
 	sha1 = tree_entry_extract(t1, &path1, &mode1);
 	sha2 = tree_entry_extract(t2, &path2, &mode2);
 
-	pathlen1 = tree_entry_len(path1, sha1);
-	pathlen2 = tree_entry_len(path2, sha2);
+	pathlen1 = tree_entry_len(&t1->entry);
+	pathlen2 = tree_entry_len(&t2->entry);
 	cmp = base_name_compare(path1, pathlen1, mode1, path2, pathlen2, mode2);
 	if (cmp < 0) {
-		show_entry(opt, "-", t1, base, baselen);
+		show_entry(opt, "-", t1, base);
 		return -1;
 	}
 	if (cmp > 0) {
-		show_entry(opt, "+", t2, base, baselen);
+		show_entry(opt, "+", t2, base);
 		return 1;
 	}
 	if (!DIFF_OPT_TST(opt, FIND_COPIES_HARDER) && !hashcmp(sha1, sha2) && mode1 == mode2)
@@ -57,180 +40,57 @@
 	 * file, we need to consider it a remove and an add.
 	 */
 	if (S_ISDIR(mode1) != S_ISDIR(mode2)) {
-		show_entry(opt, "-", t1, base, baselen);
-		show_entry(opt, "+", t2, base, baselen);
+		show_entry(opt, "-", t1, base);
+		show_entry(opt, "+", t2, base);
 		return 0;
 	}
 
+	strbuf_add(base, path1, pathlen1);
 	if (DIFF_OPT_TST(opt, RECURSIVE) && S_ISDIR(mode1)) {
-		int retval;
-		char *newbase = malloc_base(base, baselen, path1, pathlen1);
 		if (DIFF_OPT_TST(opt, TREE_IN_RECURSIVE)) {
-			newbase[baselen + pathlen1] = 0;
 			opt->change(opt, mode1, mode2,
-				    sha1, sha2, newbase);
-			newbase[baselen + pathlen1] = '/';
+				    sha1, sha2, base->buf, 0, 0);
 		}
-		retval = diff_tree_sha1(sha1, sha2, newbase, opt);
-		free(newbase);
-		return retval;
+		strbuf_addch(base, '/');
+		diff_tree_sha1(sha1, sha2, base->buf, opt);
+	} else {
+		opt->change(opt, mode1, mode2, sha1, sha2, base->buf, 0, 0);
 	}
-
-	fullname = malloc_fullname(base, baselen, path1, pathlen1);
-	opt->change(opt, mode1, mode2, sha1, sha2, fullname);
-	free(fullname);
+	strbuf_setlen(base, old_baselen);
 	return 0;
 }
 
-/*
- * Is a tree entry interesting given the pathspec we have?
- *
- * Return:
- *  - 2 for "yes, and all subsequent entries will be"
- *  - 1 for yes
- *  - zero for no
- *  - negative for "no, and no subsequent entries will be either"
- */
-static int tree_entry_interesting(struct tree_desc *desc, const char *base, int baselen, struct diff_options *opt)
-{
-	const char *path;
-	const unsigned char *sha1;
-	unsigned mode;
-	int i;
-	int pathlen;
-	int never_interesting = -1;
-
-	if (!opt->nr_paths)
-		return 1;
-
-	sha1 = tree_entry_extract(desc, &path, &mode);
-
-	pathlen = tree_entry_len(path, sha1);
-
-	for (i = 0; i < opt->nr_paths; i++) {
-		const char *match = opt->paths[i];
-		int matchlen = opt->pathlens[i];
-		int m = -1; /* signals that we haven't called strncmp() */
-
-		if (baselen >= matchlen) {
-			/* If it doesn't match, move along... */
-			if (strncmp(base, match, matchlen))
-				continue;
-
-			/*
-			 * If the base is a subdirectory of a path which
-			 * was specified, all of them are interesting.
-			 */
-			if (!matchlen ||
-			    base[matchlen] == '/' ||
-			    match[matchlen - 1] == '/')
-				return 2;
-
-			/* Just a random prefix match */
-			continue;
-		}
-
-		/* Does the base match? */
-		if (strncmp(base, match, baselen))
-			continue;
-
-		match += baselen;
-		matchlen -= baselen;
-
-		if (never_interesting) {
-			/*
-			 * We have not seen any match that sorts later
-			 * than the current path.
-			 */
-
-			/*
-			 * Does match sort strictly earlier than path
-			 * with their common parts?
-			 */
-			m = strncmp(match, path,
-				    (matchlen < pathlen) ? matchlen : pathlen);
-			if (m < 0)
-				continue;
-
-			/*
-			 * If we come here even once, that means there is at
-			 * least one pathspec that would sort equal to or
-			 * later than the path we are currently looking at.
-			 * In other words, if we have never reached this point
-			 * after iterating all pathspecs, it means all
-			 * pathspecs are either outside of base, or inside the
-			 * base but sorts strictly earlier than the current
-			 * one.  In either case, they will never match the
-			 * subsequent entries.  In such a case, we initialized
-			 * the variable to -1 and that is what will be
-			 * returned, allowing the caller to terminate early.
-			 */
-			never_interesting = 0;
-		}
-
-		if (pathlen > matchlen)
-			continue;
-
-		if (matchlen > pathlen) {
-			if (match[pathlen] != '/')
-				continue;
-			if (!S_ISDIR(mode))
-				continue;
-		}
-
-		if (m == -1)
-			/*
-			 * we cheated and did not do strncmp(), so we do
-			 * that here.
-			 */
-			m = strncmp(match, path, pathlen);
-
-		/*
-		 * If common part matched earlier then it is a hit,
-		 * because we rejected the case where path is not a
-		 * leading directory and is shorter than match.
-		 */
-		if (!m)
-			return 1;
-	}
-	return never_interesting; /* No matches */
-}
-
 /* A whole sub-tree went away or appeared */
-static void show_tree(struct diff_options *opt, const char *prefix, struct tree_desc *desc, const char *base, int baselen)
+static void show_tree(struct diff_options *opt, const char *prefix,
+		      struct tree_desc *desc, struct strbuf *base)
 {
-	int all_interesting = 0;
-	while (desc->size) {
-		int show;
-
-		if (all_interesting)
-			show = 1;
-		else {
-			show = tree_entry_interesting(desc, base, baselen,
-						      opt);
-			if (show == 2)
-				all_interesting = 1;
+	enum interesting match = entry_not_interesting;
+	for (; desc->size; update_tree_entry(desc)) {
+		if (match != all_entries_interesting) {
+			match = tree_entry_interesting(&desc->entry, base, 0,
+						       &opt->pathspec);
+			if (match == all_entries_not_interesting)
+				break;
+			if (match == entry_not_interesting)
+				continue;
 		}
-		if (show < 0)
-			break;
-		if (show)
-			show_entry(opt, prefix, desc, base, baselen);
-		update_tree_entry(desc);
+		show_entry(opt, prefix, desc, base);
 	}
 }
 
 /* A file entry went away or appeared */
-static void show_entry(struct diff_options *opt, const char *prefix, struct tree_desc *desc,
-		       const char *base, int baselen)
+static void show_entry(struct diff_options *opt, const char *prefix,
+		       struct tree_desc *desc, struct strbuf *base)
 {
 	unsigned mode;
 	const char *path;
 	const unsigned char *sha1 = tree_entry_extract(desc, &path, &mode);
-	int pathlen = tree_entry_len(path, sha1);
+	int pathlen = tree_entry_len(&desc->entry);
+	int old_baselen = base->len;
 
+	strbuf_add(base, path, pathlen);
 	if (DIFF_OPT_TST(opt, RECURSIVE) && S_ISDIR(mode)) {
 		enum object_type type;
-		char *newbase = malloc_base(base, baselen, path, pathlen);
 		struct tree_desc inner;
 		void *tree;
 		unsigned long size;
@@ -239,72 +99,70 @@
 		if (!tree || type != OBJ_TREE)
 			die("corrupt tree sha %s", sha1_to_hex(sha1));
 
-		if (DIFF_OPT_TST(opt, TREE_IN_RECURSIVE)) {
-			newbase[baselen + pathlen] = 0;
-			opt->add_remove(opt, *prefix, mode, sha1, newbase);
-			newbase[baselen + pathlen] = '/';
-		}
+		if (DIFF_OPT_TST(opt, TREE_IN_RECURSIVE))
+			opt->add_remove(opt, *prefix, mode, sha1, base->buf, 0);
+
+		strbuf_addch(base, '/');
 
 		init_tree_desc(&inner, tree, size);
-		show_tree(opt, prefix, &inner, newbase, baselen + 1 + pathlen);
-
+		show_tree(opt, prefix, &inner, base);
 		free(tree);
-		free(newbase);
-	} else {
-		char *fullname = malloc_fullname(base, baselen, path, pathlen);
-		opt->add_remove(opt, prefix[0], mode, sha1, fullname);
-		free(fullname);
-	}
+	} else
+		opt->add_remove(opt, prefix[0], mode, sha1, base->buf, 0);
+
+	strbuf_setlen(base, old_baselen);
 }
 
-static void skip_uninteresting(struct tree_desc *t, const char *base, int baselen, struct diff_options *opt)
+static void skip_uninteresting(struct tree_desc *t, struct strbuf *base,
+			       struct diff_options *opt,
+			       enum interesting *match)
 {
-	int all_interesting = 0;
 	while (t->size) {
-		int show;
-
-		if (all_interesting)
-			show = 1;
-		else {
-			show = tree_entry_interesting(t, base, baselen, opt);
-			if (show == 2)
-				all_interesting = 1;
+		*match = tree_entry_interesting(&t->entry, base, 0, &opt->pathspec);
+		if (*match) {
+			if (*match == all_entries_not_interesting)
+				t->size = 0;
+			break;
 		}
-		if (!show) {
-			update_tree_entry(t);
-			continue;
-		}
-		/* Skip it all? */
-		if (show < 0)
-			t->size = 0;
-		return;
+		update_tree_entry(t);
 	}
 }
 
-int diff_tree(struct tree_desc *t1, struct tree_desc *t2, const char *base, struct diff_options *opt)
+int diff_tree(struct tree_desc *t1, struct tree_desc *t2,
+	      const char *base_str, struct diff_options *opt)
 {
-	int baselen = strlen(base);
+	struct strbuf base;
+	int baselen = strlen(base_str);
+	enum interesting t1_match = entry_not_interesting;
+	enum interesting t2_match = entry_not_interesting;
+
+	/* Enable recursion indefinitely */
+	opt->pathspec.recursive = DIFF_OPT_TST(opt, RECURSIVE);
+	opt->pathspec.max_depth = -1;
+
+	strbuf_init(&base, PATH_MAX);
+	strbuf_add(&base, base_str, baselen);
 
 	for (;;) {
-		if (DIFF_OPT_TST(opt, QUIET) && DIFF_OPT_TST(opt, HAS_CHANGES))
+		if (diff_can_quit_early(opt))
 			break;
-		if (opt->nr_paths) {
-			skip_uninteresting(t1, base, baselen, opt);
-			skip_uninteresting(t2, base, baselen, opt);
+		if (opt->pathspec.nr) {
+			skip_uninteresting(t1, &base, opt, &t1_match);
+			skip_uninteresting(t2, &base, opt, &t2_match);
 		}
 		if (!t1->size) {
 			if (!t2->size)
 				break;
-			show_entry(opt, "+", t2, base, baselen);
+			show_entry(opt, "+", t2, &base);
 			update_tree_entry(t2);
 			continue;
 		}
 		if (!t2->size) {
-			show_entry(opt, "-", t1, base, baselen);
+			show_entry(opt, "-", t1, &base);
 			update_tree_entry(t1);
 			continue;
 		}
-		switch (compare_tree_entry(t1, t2, base, baselen, opt)) {
+		switch (compare_tree_entry(t1, t2, &base, opt)) {
 		case -1:
 			update_tree_entry(t1);
 			continue;
@@ -317,6 +175,8 @@
 		}
 		die("git diff-tree: internal error");
 	}
+
+	strbuf_release(&base);
 	return 0;
 }
 
@@ -345,10 +205,11 @@
 
 	diff_setup(&diff_opts);
 	DIFF_OPT_SET(&diff_opts, RECURSIVE);
-	diff_opts.detect_rename = DIFF_DETECT_RENAME;
+	DIFF_OPT_SET(&diff_opts, FIND_COPIES_HARDER);
 	diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
-	diff_opts.single_follow = opt->paths[0];
+	diff_opts.single_follow = opt->pathspec.raw[0];
 	diff_opts.break_opt = opt->break_opt;
+	diff_opts.rename_score = opt->rename_score;
 	paths[0] = NULL;
 	diff_tree_setup_paths(paths, &diff_opts);
 	if (diff_setup_done(&diff_opts) < 0)
@@ -358,6 +219,7 @@
 	diff_tree_release_paths(&diff_opts);
 
 	/* Go through the new set of filepairing, and see if we find a more interesting one */
+	opt->found_follow = 0;
 	for (i = 0; i < q->nr; i++) {
 		struct diff_filepair *p = q->queue[i];
 
@@ -366,15 +228,26 @@
 		 * diff_queued_diff, we will also use that as the path in
 		 * the future!
 		 */
-		if ((p->status == 'R' || p->status == 'C') && !strcmp(p->two->path, opt->paths[0])) {
+		if ((p->status == 'R' || p->status == 'C') &&
+		    !strcmp(p->two->path, opt->pathspec.raw[0])) {
 			/* Switch the file-pairs around */
 			q->queue[i] = choice;
 			choice = p;
 
 			/* Update the path we use from now on.. */
 			diff_tree_release_paths(opt);
-			opt->paths[0] = xstrdup(p->one->path);
-			diff_tree_setup_paths(opt->paths, opt);
+			opt->pathspec.raw[0] = xstrdup(p->one->path);
+			diff_tree_setup_paths(opt->pathspec.raw, opt);
+
+			/*
+			 * The caller expects us to return a set of vanilla
+			 * filepairs to let a later call to diffcore_std()
+			 * it makes to sort the renames out (among other
+			 * things), but we already have found renames
+			 * ourselves; signal diffcore_std() not to muck with
+			 * rename information.
+			 */
+			opt->found_follow = 1;
 			break;
 		}
 	}
@@ -411,7 +284,7 @@
 	init_tree_desc(&t1, tree1, size1);
 	init_tree_desc(&t2, tree2, size2);
 	retval = diff_tree(&t1, &t2, base, opt);
-	if (DIFF_OPT_TST(opt, FOLLOW_RENAMES) && diff_might_be_rename()) {
+	if (!*base && DIFF_OPT_TST(opt, FOLLOW_RENAMES) && diff_might_be_rename()) {
 		init_tree_desc(&t1, tree1, size1);
 		init_tree_desc(&t2, tree2, size2);
 		try_to_follow_renames(&t1, &t2, base, opt);
@@ -439,36 +312,12 @@
 	return retval;
 }
 
-static int count_paths(const char **paths)
-{
-	int i = 0;
-	while (*paths++)
-		i++;
-	return i;
-}
-
 void diff_tree_release_paths(struct diff_options *opt)
 {
-	free(opt->pathlens);
+	free_pathspec(&opt->pathspec);
 }
 
 void diff_tree_setup_paths(const char **p, struct diff_options *opt)
 {
-	opt->nr_paths = 0;
-	opt->pathlens = NULL;
-	opt->paths = NULL;
-
-	if (p) {
-		int i;
-
-		opt->paths = p;
-		opt->nr_paths = count_paths(p);
-		if (opt->nr_paths == 0) {
-			opt->pathlens = NULL;
-			return;
-		}
-		opt->pathlens = xmalloc(opt->nr_paths * sizeof(int));
-		for (i=0; i < opt->nr_paths; i++)
-			opt->pathlens[i] = strlen(p[i]);
-	}
+	init_pathspec(&opt->pathspec, p);
 }
diff --git a/tree-walk.c b/tree-walk.c
index 02e2aed..492c7cd 100644
--- a/tree-walk.c
+++ b/tree-walk.c
@@ -1,5 +1,7 @@
 #include "cache.h"
 #include "tree-walk.h"
+#include "unpack-trees.h"
+#include "dir.h"
 #include "tree.h"
 
 static const char *get_mode(const char *str, unsigned int *modep)
@@ -60,13 +62,6 @@
 	return buf;
 }
 
-static int entry_compare(struct name_entry *a, struct name_entry *b)
-{
-	return df_name_compare(
-			a->path, tree_entry_len(a->path, a->sha1), a->mode,
-			b->path, tree_entry_len(b->path, b->sha1), b->mode);
-}
-
 static void entry_clear(struct name_entry *a)
 {
 	memset(a, 0, sizeof(*a));
@@ -121,7 +116,7 @@
 
 char *make_traverse_path(char *path, const struct traverse_info *info, const struct name_entry *n)
 {
-	int len = tree_entry_len(n->path, n->sha1);
+	int len = tree_entry_len(n);
 	int pathlen = info->pathlen;
 
 	path[pathlen + len] = 0;
@@ -131,74 +126,303 @@
 			break;
 		path[--pathlen] = '/';
 		n = &info->name;
-		len = tree_entry_len(n->path, n->sha1);
+		len = tree_entry_len(n);
 		info = info->prev;
 		pathlen -= len;
 	}
 	return path;
 }
 
+struct tree_desc_skip {
+	struct tree_desc_skip *prev;
+	const void *ptr;
+};
+
+struct tree_desc_x {
+	struct tree_desc d;
+	struct tree_desc_skip *skip;
+};
+
+static int name_compare(const char *a, int a_len,
+			const char *b, int b_len)
+{
+	int len = (a_len < b_len) ? a_len : b_len;
+	int cmp = memcmp(a, b, len);
+	if (cmp)
+		return cmp;
+	return (a_len - b_len);
+}
+
+static int check_entry_match(const char *a, int a_len, const char *b, int b_len)
+{
+	/*
+	 * The caller wants to pick *a* from a tree or nothing.
+	 * We are looking at *b* in a tree.
+	 *
+	 * (0) If a and b are the same name, we are trivially happy.
+	 *
+	 * There are three possibilities where *a* could be hiding
+	 * behind *b*.
+	 *
+	 * (1) *a* == "t",   *b* == "ab"  i.e. *b* sorts earlier than *a* no
+	 *                                matter what.
+	 * (2) *a* == "t",   *b* == "t-2" and "t" is a subtree in the tree;
+	 * (3) *a* == "t-2", *b* == "t"   and "t-2" is a blob in the tree.
+	 *
+	 * Otherwise we know *a* won't appear in the tree without
+	 * scanning further.
+	 */
+
+	int cmp = name_compare(a, a_len, b, b_len);
+
+	/* Most common case first -- reading sync'd trees */
+	if (!cmp)
+		return cmp;
+
+	if (0 < cmp) {
+		/* a comes after b; it does not matter if it is case (3)
+		if (b_len < a_len && !memcmp(a, b, b_len) && a[b_len] < '/')
+			return 1;
+		*/
+		return 1; /* keep looking */
+	}
+
+	/* b comes after a; are we looking at case (2)? */
+	if (a_len < b_len && !memcmp(a, b, a_len) && b[a_len] < '/')
+		return 1; /* keep looking */
+
+	return -1; /* a cannot appear in the tree */
+}
+
+/*
+ * From the extended tree_desc, extract the first name entry, while
+ * paying attention to the candidate "first" name.  Most importantly,
+ * when looking for an entry, if there are entries that sorts earlier
+ * in the tree object representation than that name, skip them and
+ * process the named entry first.  We will remember that we haven't
+ * processed the first entry yet, and in the later call skip the
+ * entry we processed early when update_extended_entry() is called.
+ *
+ * E.g. if the underlying tree object has these entries:
+ *
+ *    blob    "t-1"
+ *    blob    "t-2"
+ *    tree    "t"
+ *    blob    "t=1"
+ *
+ * and the "first" asks for "t", remember that we still need to
+ * process "t-1" and "t-2" but extract "t".  After processing the
+ * entry "t" from this call, the caller will let us know by calling
+ * update_extended_entry() that we can remember "t" has been processed
+ * already.
+ */
+
+static void extended_entry_extract(struct tree_desc_x *t,
+				   struct name_entry *a,
+				   const char *first,
+				   int first_len)
+{
+	const char *path;
+	int len;
+	struct tree_desc probe;
+	struct tree_desc_skip *skip;
+
+	/*
+	 * Extract the first entry from the tree_desc, but skip the
+	 * ones that we already returned in earlier rounds.
+	 */
+	while (1) {
+		if (!t->d.size) {
+			entry_clear(a);
+			break; /* not found */
+		}
+		entry_extract(&t->d, a);
+		for (skip = t->skip; skip; skip = skip->prev)
+			if (a->path == skip->ptr)
+				break; /* found */
+		if (!skip)
+			break;
+		/* We have processed this entry already. */
+		update_tree_entry(&t->d);
+	}
+
+	if (!first || !a->path)
+		return;
+
+	/*
+	 * The caller wants "first" from this tree, or nothing.
+	 */
+	path = a->path;
+	len = tree_entry_len(a);
+	switch (check_entry_match(first, first_len, path, len)) {
+	case -1:
+		entry_clear(a);
+	case 0:
+		return;
+	default:
+		break;
+	}
+
+	/*
+	 * We need to look-ahead -- we suspect that a subtree whose
+	 * name is "first" may be hiding behind the current entry "path".
+	 */
+	probe = t->d;
+	while (probe.size) {
+		entry_extract(&probe, a);
+		path = a->path;
+		len = tree_entry_len(a);
+		switch (check_entry_match(first, first_len, path, len)) {
+		case -1:
+			entry_clear(a);
+		case 0:
+			return;
+		default:
+			update_tree_entry(&probe);
+			break;
+		}
+		/* keep looking */
+	}
+	entry_clear(a);
+}
+
+static void update_extended_entry(struct tree_desc_x *t, struct name_entry *a)
+{
+	if (t->d.entry.path == a->path) {
+		update_tree_entry(&t->d);
+	} else {
+		/* we have returned this entry early */
+		struct tree_desc_skip *skip = xmalloc(sizeof(*skip));
+		skip->ptr = a->path;
+		skip->prev = t->skip;
+		t->skip = skip;
+	}
+}
+
+static void free_extended_entry(struct tree_desc_x *t)
+{
+	struct tree_desc_skip *p, *s;
+
+	for (s = t->skip; s; s = p) {
+		p = s->prev;
+		free(s);
+	}
+}
+
+static inline int prune_traversal(struct name_entry *e,
+				  struct traverse_info *info,
+				  struct strbuf *base,
+				  int still_interesting)
+{
+	if (!info->pathspec || still_interesting == 2)
+		return 2;
+	if (still_interesting < 0)
+		return still_interesting;
+	return tree_entry_interesting(e, base, 0, info->pathspec);
+}
+
 int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
 {
 	int ret = 0;
+	int error = 0;
 	struct name_entry *entry = xmalloc(n*sizeof(*entry));
+	int i;
+	struct tree_desc_x *tx = xcalloc(n, sizeof(*tx));
+	struct strbuf base = STRBUF_INIT;
+	int interesting = 1;
 
+	for (i = 0; i < n; i++)
+		tx[i].d = t[i];
+
+	if (info->prev) {
+		strbuf_grow(&base, info->pathlen);
+		make_traverse_path(base.buf, info->prev, &info->name);
+		base.buf[info->pathlen-1] = '/';
+		strbuf_setlen(&base, info->pathlen);
+	}
 	for (;;) {
-		unsigned long mask = 0;
-		unsigned long dirmask = 0;
-		int i, last;
+		unsigned long mask, dirmask;
+		const char *first = NULL;
+		int first_len = 0;
+		struct name_entry *e = NULL;
+		int len;
 
-		last = -1;
 		for (i = 0; i < n; i++) {
-			if (!t[i].size)
-				continue;
-			entry_extract(t+i, entry+i);
-			if (last >= 0) {
-				int cmp = entry_compare(entry+i, entry+last);
+			e = entry + i;
+			extended_entry_extract(tx + i, e, NULL, 0);
+		}
 
-				/*
-				 * Is the new name bigger than the old one?
-				 * Ignore it
-				 */
-				if (cmp > 0)
-					continue;
-				/*
-				 * Is the new name smaller than the old one?
-				 * Ignore all old ones
-				 */
-				if (cmp < 0)
-					mask = 0;
+		/*
+		 * A tree may have "t-2" at the current location even
+		 * though it may have "t" that is a subtree behind it,
+		 * and another tree may return "t".  We want to grab
+		 * all "t" from all trees to match in such a case.
+		 */
+		for (i = 0; i < n; i++) {
+			e = entry + i;
+			if (!e->path)
+				continue;
+			len = tree_entry_len(e);
+			if (!first) {
+				first = e->path;
+				first_len = len;
+				continue;
 			}
+			if (name_compare(e->path, len, first, first_len) < 0) {
+				first = e->path;
+				first_len = len;
+			}
+		}
+
+		if (first) {
+			for (i = 0; i < n; i++) {
+				e = entry + i;
+				extended_entry_extract(tx + i, e, first, first_len);
+				/* Cull the ones that are not the earliest */
+				if (!e->path)
+					continue;
+				len = tree_entry_len(e);
+				if (name_compare(e->path, len, first, first_len))
+					entry_clear(e);
+			}
+		}
+
+		/* Now we have in entry[i] the earliest name from the trees */
+		mask = 0;
+		dirmask = 0;
+		for (i = 0; i < n; i++) {
+			if (!entry[i].path)
+				continue;
 			mask |= 1ul << i;
 			if (S_ISDIR(entry[i].mode))
 				dirmask |= 1ul << i;
-			last = i;
+			e = &entry[i];
 		}
 		if (!mask)
 			break;
-		dirmask &= mask;
-
-		/*
-		 * Clear all the unused name-entries.
-		 */
-		for (i = 0; i < n; i++) {
-			if (mask & (1ul << i))
-				continue;
-			entry_clear(entry + i);
-		}
-		ret = info->fn(n, mask, dirmask, entry, info);
-		if (ret < 0)
+		interesting = prune_traversal(e, info, &base, interesting);
+		if (interesting < 0)
 			break;
-		if (ret)
+		if (interesting) {
+			ret = info->fn(n, mask, dirmask, entry, info);
+			if (ret < 0) {
+				error = ret;
+				if (!info->show_all_errors)
+					break;
+			}
 			mask &= ret;
-		ret = 0;
-		for (i = 0; i < n; i++) {
-			if (mask & (1ul << i))
-				update_tree_entry(t + i);
 		}
+		ret = 0;
+		for (i = 0; i < n; i++)
+			if (mask & (1ul << i))
+				update_extended_entry(tx + i, entry + i);
 	}
 	free(entry);
-	return ret;
+	for (i = 0; i < n; i++)
+		free_extended_entry(tx + i);
+	free(tx);
+	strbuf_release(&base);
+	return error;
 }
 
 static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char *result, unsigned *mode)
@@ -210,8 +434,8 @@
 		int entrylen, cmp;
 
 		sha1 = tree_entry_extract(t, &entry, mode);
+		entrylen = tree_entry_len(&t->entry);
 		update_tree_entry(t);
-		entrylen = tree_entry_len(entry, sha1);
 		if (entrylen > namelen)
 			continue;
 		cmp = memcmp(name, entry, entrylen);
@@ -241,7 +465,6 @@
 	int retval;
 	void *tree;
 	unsigned long size;
-	struct tree_desc t;
 	unsigned char root[20];
 
 	tree = read_object_with_reference(tree_sha1, tree_type, &size, root);
@@ -250,11 +473,200 @@
 
 	if (name[0] == '\0') {
 		hashcpy(sha1, root);
+		free(tree);
 		return 0;
 	}
 
-	init_tree_desc(&t, tree, size);
-	retval = find_tree_entry(&t, name, sha1, mode);
+	if (!size) {
+		retval = -1;
+	} else {
+		struct tree_desc t;
+		init_tree_desc(&t, tree, size);
+		retval = find_tree_entry(&t, name, sha1, mode);
+	}
 	free(tree);
 	return retval;
 }
+
+static int match_entry(const struct name_entry *entry, int pathlen,
+		       const char *match, int matchlen,
+		       int *never_interesting)
+{
+	int m = -1; /* signals that we haven't called strncmp() */
+
+	if (*never_interesting) {
+		/*
+		 * We have not seen any match that sorts later
+		 * than the current path.
+		 */
+
+		/*
+		 * Does match sort strictly earlier than path
+		 * with their common parts?
+		 */
+		m = strncmp(match, entry->path,
+			    (matchlen < pathlen) ? matchlen : pathlen);
+		if (m < 0)
+			return 0;
+
+		/*
+		 * If we come here even once, that means there is at
+		 * least one pathspec that would sort equal to or
+		 * later than the path we are currently looking at.
+		 * In other words, if we have never reached this point
+		 * after iterating all pathspecs, it means all
+		 * pathspecs are either outside of base, or inside the
+		 * base but sorts strictly earlier than the current
+		 * one.  In either case, they will never match the
+		 * subsequent entries.  In such a case, we initialized
+		 * the variable to -1 and that is what will be
+		 * returned, allowing the caller to terminate early.
+		 */
+		*never_interesting = 0;
+	}
+
+	if (pathlen > matchlen)
+		return 0;
+
+	if (matchlen > pathlen) {
+		if (match[pathlen] != '/')
+			return 0;
+		if (!S_ISDIR(entry->mode))
+			return 0;
+	}
+
+	if (m == -1)
+		/*
+		 * we cheated and did not do strncmp(), so we do
+		 * that here.
+		 */
+		m = strncmp(match, entry->path, pathlen);
+
+	/*
+	 * If common part matched earlier then it is a hit,
+	 * because we rejected the case where path is not a
+	 * leading directory and is shorter than match.
+	 */
+	if (!m)
+		return 1;
+
+	return 0;
+}
+
+static int match_dir_prefix(const char *base,
+			    const char *match, int matchlen)
+{
+	if (strncmp(base, match, matchlen))
+		return 0;
+
+	/*
+	 * If the base is a subdirectory of a path which
+	 * was specified, all of them are interesting.
+	 */
+	if (!matchlen ||
+	    base[matchlen] == '/' ||
+	    match[matchlen - 1] == '/')
+		return 1;
+
+	/* Just a random prefix match */
+	return 0;
+}
+
+/*
+ * Is a tree entry interesting given the pathspec we have?
+ *
+ * Pre-condition: either baselen == base_offset (i.e. empty path)
+ * or base[baselen-1] == '/' (i.e. with trailing slash).
+ */
+enum interesting tree_entry_interesting(const struct name_entry *entry,
+					struct strbuf *base, int base_offset,
+					const struct pathspec *ps)
+{
+	int i;
+	int pathlen, baselen = base->len - base_offset;
+	int never_interesting = ps->has_wildcard ?
+		entry_not_interesting : all_entries_not_interesting;
+
+	if (!ps->nr) {
+		if (!ps->recursive || ps->max_depth == -1)
+			return all_entries_interesting;
+		return within_depth(base->buf + base_offset, baselen,
+				    !!S_ISDIR(entry->mode),
+				    ps->max_depth) ?
+			entry_interesting : entry_not_interesting;
+	}
+
+	pathlen = tree_entry_len(entry);
+
+	for (i = ps->nr - 1; i >= 0; i--) {
+		const struct pathspec_item *item = ps->items+i;
+		const char *match = item->match;
+		const char *base_str = base->buf + base_offset;
+		int matchlen = item->len;
+
+		if (baselen >= matchlen) {
+			/* If it doesn't match, move along... */
+			if (!match_dir_prefix(base_str, match, matchlen))
+				goto match_wildcards;
+
+			if (!ps->recursive || ps->max_depth == -1)
+				return all_entries_interesting;
+
+			return within_depth(base_str + matchlen + 1,
+					    baselen - matchlen - 1,
+					    !!S_ISDIR(entry->mode),
+					    ps->max_depth) ?
+				entry_interesting : entry_not_interesting;
+		}
+
+		/* Either there must be no base, or the base must match. */
+		if (baselen == 0 || !strncmp(base_str, match, baselen)) {
+			if (match_entry(entry, pathlen,
+					match + baselen, matchlen - baselen,
+					&never_interesting))
+				return entry_interesting;
+
+			if (item->use_wildcard) {
+				if (!fnmatch(match + baselen, entry->path, 0))
+					return entry_interesting;
+
+				/*
+				 * Match all directories. We'll try to
+				 * match files later on.
+				 */
+				if (ps->recursive && S_ISDIR(entry->mode))
+					return entry_interesting;
+			}
+
+			continue;
+		}
+
+match_wildcards:
+		if (!item->use_wildcard)
+			continue;
+
+		/*
+		 * Concatenate base and entry->path into one and do
+		 * fnmatch() on it.
+		 */
+
+		strbuf_add(base, entry->path, pathlen);
+
+		if (!fnmatch(match, base->buf + base_offset, 0)) {
+			strbuf_setlen(base, base_offset + baselen);
+			return entry_interesting;
+		}
+		strbuf_setlen(base, base_offset + baselen);
+
+		/*
+		 * Match all directories. We'll try to match files
+		 * later on.
+		 * max_depth is ignored but we may consider support it
+		 * in future, see
+		 * http://thread.gmane.org/gmane.comp.version-control.git/163757/focus=163840
+		 */
+		if (ps->recursive && S_ISDIR(entry->mode))
+			return entry_interesting;
+	}
+	return never_interesting; /* No matches */
+}
diff --git a/tree-walk.h b/tree-walk.h
index 42110a4..2bf0db9 100644
--- a/tree-walk.h
+++ b/tree-walk.h
@@ -20,15 +20,18 @@
 	return desc->entry.sha1;
 }
 
-static inline int tree_entry_len(const char *name, const unsigned char *sha1)
+static inline int tree_entry_len(const struct name_entry *ne)
 {
-	return (const char *)sha1 - name - 1;
+	return (const char *)ne->sha1 - ne->path - 1;
 }
 
 void update_tree_entry(struct tree_desc *);
 void init_tree_desc(struct tree_desc *desc, const void *buf, unsigned long size);
 
-/* Helper function that does both of the above and returns true for success */
+/*
+ * Helper function that does both tree_entry_extract() and update_tree_entry()
+ * and returns true for success
+ */
 int tree_entry(struct tree_desc *, struct name_entry *);
 
 void *fill_tree_descriptor(struct tree_desc *desc, const unsigned char *sha1);
@@ -41,10 +44,12 @@
 	struct traverse_info *prev;
 	struct name_entry name;
 	int pathlen;
+	struct pathspec *pathspec;
 
 	unsigned long conflicts;
 	traverse_callback_t fn;
 	void *data;
+	int show_all_errors;
 };
 
 int get_tree_entry(const unsigned char *, const char *, unsigned char *, unsigned *);
@@ -53,7 +58,19 @@
 
 static inline int traverse_path_len(const struct traverse_info *info, const struct name_entry *n)
 {
-	return info->pathlen + tree_entry_len(n->path, n->sha1);
+	return info->pathlen + tree_entry_len(n);
 }
 
+/* in general, positive means "kind of interesting" */
+enum interesting {
+	all_entries_not_interesting = -1, /* no, and no subsequent entries will be either */
+	entry_not_interesting = 0,
+	entry_interesting = 1,
+	all_entries_interesting = 2 /* yes, and all subsequent entries will be */
+};
+
+extern enum interesting tree_entry_interesting(const struct name_entry *,
+					       struct strbuf *, int,
+					       const struct pathspec *ps);
+
 #endif
diff --git a/tree.c b/tree.c
index 5ab90af..62fed63 100644
--- a/tree.c
+++ b/tree.c
@@ -22,7 +22,8 @@
 	ce = xcalloc(1, size);
 
 	ce->ce_mode = create_ce_mode(mode);
-	ce->ce_flags = create_ce_flags(baselen + len, stage);
+	ce->ce_flags = create_ce_flags(stage);
+	ce->ce_namelen = baselen + len;
 	memcpy(ce->name, base, baselen);
 	memcpy(ce->name + baselen, pathname, len+1);
 	hashcpy(ce->sha1, sha1);
@@ -45,62 +46,15 @@
 				  ADD_CACHE_JUST_APPEND);
 }
 
-static int match_tree_entry(const char *base, int baselen, const char *path, unsigned int mode, const char **paths)
-{
-	const char *match;
-	int pathlen;
-
-	if (!paths)
-		return 1;
-	pathlen = strlen(path);
-	while ((match = *paths++) != NULL) {
-		int matchlen = strlen(match);
-
-		if (baselen >= matchlen) {
-			/* If it doesn't match, move along... */
-			if (strncmp(base, match, matchlen))
-				continue;
-			/* pathspecs match only at the directory boundaries */
-			if (!matchlen ||
-			    baselen == matchlen ||
-			    base[matchlen] == '/' ||
-			    match[matchlen - 1] == '/')
-				return 1;
-			continue;
-		}
-
-		/* Does the base match? */
-		if (strncmp(base, match, baselen))
-			continue;
-
-		match += baselen;
-		matchlen -= baselen;
-
-		if (pathlen > matchlen)
-			continue;
-
-		if (matchlen > pathlen) {
-			if (match[pathlen] != '/')
-				continue;
-			if (!S_ISDIR(mode))
-				continue;
-		}
-
-		if (strncmp(path, match, pathlen))
-			continue;
-
-		return 1;
-	}
-	return 0;
-}
-
-int read_tree_recursive(struct tree *tree,
-			const char *base, int baselen,
-			int stage, const char **match,
-			read_tree_fn_t fn, void *context)
+static int read_tree_1(struct tree *tree, struct strbuf *base,
+		       int stage, struct pathspec *pathspec,
+		       read_tree_fn_t fn, void *context)
 {
 	struct tree_desc desc;
 	struct name_entry entry;
+	unsigned char sha1[20];
+	int len, oldlen = base->len;
+	enum interesting retval = entry_not_interesting;
 
 	if (parse_tree(tree))
 		return -1;
@@ -108,10 +62,16 @@
 	init_tree_desc(&desc, tree->buffer, tree->size);
 
 	while (tree_entry(&desc, &entry)) {
-		if (!match_tree_entry(base, baselen, entry.path, entry.mode, match))
-			continue;
+		if (retval != all_entries_interesting) {
+			retval = tree_entry_interesting(&entry, base, 0, pathspec);
+			if (retval == all_entries_not_interesting)
+				break;
+			if (retval == entry_not_interesting)
+				continue;
+		}
 
-		switch (fn(entry.sha1, base, baselen, entry.path, entry.mode, stage, context)) {
+		switch (fn(entry.sha1, base->buf, base->len,
+			   entry.path, entry.mode, stage, context)) {
 		case 0:
 			continue;
 		case READ_TREE_RECURSIVE:
@@ -119,67 +79,66 @@
 		default:
 			return -1;
 		}
-		if (S_ISDIR(entry.mode)) {
-			int retval;
-			char *newbase;
-			unsigned int pathlen = tree_entry_len(entry.path, entry.sha1);
 
-			newbase = xmalloc(baselen + 1 + pathlen);
-			memcpy(newbase, base, baselen);
-			memcpy(newbase + baselen, entry.path, pathlen);
-			newbase[baselen + pathlen] = '/';
-			retval = read_tree_recursive(lookup_tree(entry.sha1),
-						     newbase,
-						     baselen + pathlen + 1,
-						     stage, match, fn, context);
-			free(newbase);
-			if (retval)
-				return -1;
-			continue;
-		} else if (S_ISGITLINK(entry.mode)) {
-			int retval;
-			struct strbuf path;
-			unsigned int entrylen;
+		if (S_ISDIR(entry.mode))
+			hashcpy(sha1, entry.sha1);
+		else if (S_ISGITLINK(entry.mode)) {
 			struct commit *commit;
 
-			entrylen = tree_entry_len(entry.path, entry.sha1);
-			strbuf_init(&path, baselen + entrylen + 1);
-			strbuf_add(&path, base, baselen);
-			strbuf_add(&path, entry.path, entrylen);
-			strbuf_addch(&path, '/');
-
 			commit = lookup_commit(entry.sha1);
 			if (!commit)
-				die("Commit %s in submodule path %s not found",
-				    sha1_to_hex(entry.sha1), path.buf);
+				die("Commit %s in submodule path %s%s not found",
+				    sha1_to_hex(entry.sha1),
+				    base->buf, entry.path);
 
 			if (parse_commit(commit))
-				die("Invalid commit %s in submodule path %s",
-				    sha1_to_hex(entry.sha1), path.buf);
+				die("Invalid commit %s in submodule path %s%s",
+				    sha1_to_hex(entry.sha1),
+				    base->buf, entry.path);
 
-			retval = read_tree_recursive(commit->tree,
-						     path.buf, path.len,
-						     stage, match, fn, context);
-			strbuf_release(&path);
-			if (retval)
-				return -1;
-			continue;
+			hashcpy(sha1, commit->tree->object.sha1);
 		}
+		else
+			continue;
+
+		len = tree_entry_len(&entry);
+		strbuf_add(base, entry.path, len);
+		strbuf_addch(base, '/');
+		retval = read_tree_1(lookup_tree(sha1),
+				     base, stage, pathspec,
+				     fn, context);
+		strbuf_setlen(base, oldlen);
+		if (retval)
+			return -1;
 	}
 	return 0;
 }
 
+int read_tree_recursive(struct tree *tree,
+			const char *base, int baselen,
+			int stage, struct pathspec *pathspec,
+			read_tree_fn_t fn, void *context)
+{
+	struct strbuf sb = STRBUF_INIT;
+	int ret;
+
+	strbuf_add(&sb, base, baselen);
+	ret = read_tree_1(tree, &sb, stage, pathspec, fn, context);
+	strbuf_release(&sb);
+	return ret;
+}
+
 static int cmp_cache_name_compare(const void *a_, const void *b_)
 {
 	const struct cache_entry *ce1, *ce2;
 
 	ce1 = *((const struct cache_entry **)a_);
 	ce2 = *((const struct cache_entry **)b_);
-	return cache_name_compare(ce1->name, ce1->ce_flags,
-				  ce2->name, ce2->ce_flags);
+	return cache_name_stage_compare(ce1->name, ce1->ce_namelen, ce_stage(ce1),
+				  ce2->name, ce2->ce_namelen, ce_stage(ce2));
 }
 
-int read_tree(struct tree *tree, int stage, const char **match)
+int read_tree(struct tree *tree, int stage, struct pathspec *match)
 {
 	read_tree_fn_t fn = NULL;
 	int i, err;
diff --git a/tree.h b/tree.h
index 2ff01a4..69bcb5e 100644
--- a/tree.h
+++ b/tree.h
@@ -25,9 +25,9 @@
 
 extern int read_tree_recursive(struct tree *tree,
 			       const char *base, int baselen,
-			       int stage, const char **match,
+			       int stage, struct pathspec *pathspec,
 			       read_tree_fn_t fn, void *context);
 
-extern int read_tree(struct tree *tree, int stage, const char **paths);
+extern int read_tree(struct tree *tree, int stage, struct pathspec *pathspec);
 
 #endif /* TREE_H */
diff --git a/unix-socket.c b/unix-socket.c
new file mode 100644
index 0000000..01f119f
--- /dev/null
+++ b/unix-socket.c
@@ -0,0 +1,122 @@
+#include "cache.h"
+#include "unix-socket.h"
+
+static int unix_stream_socket(void)
+{
+	int fd = socket(AF_UNIX, SOCK_STREAM, 0);
+	if (fd < 0)
+		die_errno("unable to create socket");
+	return fd;
+}
+
+static int chdir_len(const char *orig, int len)
+{
+	char *path = xmemdupz(orig, len);
+	int r = chdir(path);
+	free(path);
+	return r;
+}
+
+struct unix_sockaddr_context {
+	char orig_dir[PATH_MAX];
+};
+
+static void unix_sockaddr_cleanup(struct unix_sockaddr_context *ctx)
+{
+	if (!ctx->orig_dir[0])
+		return;
+	/*
+	 * If we fail, we can't just return an error, since we have
+	 * moved the cwd of the whole process, which could confuse calling
+	 * code.  We are better off to just die.
+	 */
+	if (chdir(ctx->orig_dir) < 0)
+		die("unable to restore original working directory");
+}
+
+static int unix_sockaddr_init(struct sockaddr_un *sa, const char *path,
+			      struct unix_sockaddr_context *ctx)
+{
+	int size = strlen(path) + 1;
+
+	ctx->orig_dir[0] = '\0';
+	if (size > sizeof(sa->sun_path)) {
+		const char *slash = find_last_dir_sep(path);
+		const char *dir;
+
+		if (!slash) {
+			errno = ENAMETOOLONG;
+			return -1;
+		}
+
+		dir = path;
+		path = slash + 1;
+		size = strlen(path) + 1;
+		if (size > sizeof(sa->sun_path)) {
+			errno = ENAMETOOLONG;
+			return -1;
+		}
+
+		if (!getcwd(ctx->orig_dir, sizeof(ctx->orig_dir))) {
+			errno = ENAMETOOLONG;
+			return -1;
+		}
+		if (chdir_len(dir, slash - dir) < 0)
+			return -1;
+	}
+
+	memset(sa, 0, sizeof(*sa));
+	sa->sun_family = AF_UNIX;
+	memcpy(sa->sun_path, path, size);
+	return 0;
+}
+
+int unix_stream_connect(const char *path)
+{
+	int fd, saved_errno;
+	struct sockaddr_un sa;
+	struct unix_sockaddr_context ctx;
+
+	if (unix_sockaddr_init(&sa, path, &ctx) < 0)
+		return -1;
+	fd = unix_stream_socket();
+	if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0)
+		goto fail;
+	unix_sockaddr_cleanup(&ctx);
+	return fd;
+
+fail:
+	saved_errno = errno;
+	unix_sockaddr_cleanup(&ctx);
+	close(fd);
+	errno = saved_errno;
+	return -1;
+}
+
+int unix_stream_listen(const char *path)
+{
+	int fd, saved_errno;
+	struct sockaddr_un sa;
+	struct unix_sockaddr_context ctx;
+
+	if (unix_sockaddr_init(&sa, path, &ctx) < 0)
+		return -1;
+	fd = unix_stream_socket();
+
+	unlink(path);
+	if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0)
+		goto fail;
+
+	if (listen(fd, 5) < 0)
+		goto fail;
+
+	unix_sockaddr_cleanup(&ctx);
+	return fd;
+
+fail:
+	saved_errno = errno;
+	unix_sockaddr_cleanup(&ctx);
+	close(fd);
+	errno = saved_errno;
+	return -1;
+}
diff --git a/unix-socket.h b/unix-socket.h
new file mode 100644
index 0000000..e271aee
--- /dev/null
+++ b/unix-socket.h
@@ -0,0 +1,7 @@
+#ifndef UNIX_SOCKET_H
+#define UNIX_SOCKET_H
+
+int unix_stream_connect(const char *path);
+int unix_stream_listen(const char *path);
+
+#endif /* UNIX_SOCKET_H */
diff --git a/unpack-file.c b/unpack-file.c
deleted file mode 100644
index ac9cbf7..0000000
--- a/unpack-file.c
+++ /dev/null
@@ -1,41 +0,0 @@
-#include "cache.h"
-#include "blob.h"
-#include "exec_cmd.h"
-
-static char *create_temp_file(unsigned char *sha1)
-{
-	static char path[50];
-	void *buf;
-	enum object_type type;
-	unsigned long size;
-	int fd;
-
-	buf = read_sha1_file(sha1, &type, &size);
-	if (!buf || type != OBJ_BLOB)
-		die("unable to read blob object %s", sha1_to_hex(sha1));
-
-	strcpy(path, ".merge_file_XXXXXX");
-	fd = xmkstemp(path);
-	if (write_in_full(fd, buf, size) != size)
-		die_errno("unable to write temp-file");
-	close(fd);
-	return path;
-}
-
-int main(int argc, char **argv)
-{
-	unsigned char sha1[20];
-
-	git_extract_argv0_path(argv[0]);
-
-	if (argc != 2)
-		usage("git unpack-file <sha1>");
-	if (get_sha1(argv[1], sha1))
-		die("Not a valid object name %s", argv[1]);
-
-	setup_git_directory();
-	git_config(git_default_config, NULL);
-
-	puts(create_temp_file(sha1));
-	return 0;
-}
diff --git a/unpack-trees.c b/unpack-trees.c
index 720f7a1..6d96366 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -13,31 +13,108 @@
  * Error messages expected by scripts out of plumbing commands such as
  * read-tree.  Non-scripted Porcelain is not required to use these messages
  * and in fact are encouraged to reword them to better suit their particular
- * situation better.  See how "git checkout" replaces not_uptodate_file to
- * explain why it does not allow switching between branches when you have
- * local changes, for example.
+ * situation better.  See how "git checkout" and "git merge" replaces
+ * them using setup_unpack_trees_porcelain(), for example.
  */
-static struct unpack_trees_error_msgs unpack_plumbing_errors = {
-	/* would_overwrite */
+static const char *unpack_plumbing_errors[NB_UNPACK_TREES_ERROR_TYPES] = {
+	/* ERROR_WOULD_OVERWRITE */
 	"Entry '%s' would be overwritten by merge. Cannot merge.",
 
-	/* not_uptodate_file */
+	/* ERROR_NOT_UPTODATE_FILE */
 	"Entry '%s' not uptodate. Cannot merge.",
 
-	/* not_uptodate_dir */
+	/* ERROR_NOT_UPTODATE_DIR */
 	"Updating '%s' would lose untracked files in it",
 
-	/* would_lose_untracked */
-	"Untracked working tree file '%s' would be %s by merge.",
+	/* ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN */
+	"Untracked working tree file '%s' would be overwritten by merge.",
 
-	/* bind_overlap */
+	/* ERROR_WOULD_LOSE_UNTRACKED_REMOVED */
+	"Untracked working tree file '%s' would be removed by merge.",
+
+	/* ERROR_BIND_OVERLAP */
 	"Entry '%s' overlaps with '%s'.  Cannot bind.",
+
+	/* ERROR_SPARSE_NOT_UPTODATE_FILE */
+	"Entry '%s' not uptodate. Cannot update sparse checkout.",
+
+	/* ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN */
+	"Working tree file '%s' would be overwritten by sparse checkout update.",
+
+	/* ERROR_WOULD_LOSE_ORPHANED_REMOVED */
+	"Working tree file '%s' would be removed by sparse checkout update.",
 };
 
-#define ERRORMSG(o,fld) \
-	( ((o) && (o)->msgs.fld) \
-	? ((o)->msgs.fld) \
-	: (unpack_plumbing_errors.fld) )
+#define ERRORMSG(o,type) \
+	( ((o) && (o)->msgs[(type)]) \
+	  ? ((o)->msgs[(type)])      \
+	  : (unpack_plumbing_errors[(type)]) )
+
+void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
+				  const char *cmd)
+{
+	int i;
+	const char **msgs = opts->msgs;
+	const char *msg;
+	char *tmp;
+	const char *cmd2 = strcmp(cmd, "checkout") ? cmd : "switch branches";
+	if (advice_commit_before_merge)
+		msg = "Your local changes to the following files would be overwritten by %s:\n%%s"
+			"Please, commit your changes or stash them before you can %s.";
+	else
+		msg = "Your local changes to the following files would be overwritten by %s:\n%%s";
+	tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen(cmd2) - 2);
+	sprintf(tmp, msg, cmd, cmd2);
+	msgs[ERROR_WOULD_OVERWRITE] = tmp;
+	msgs[ERROR_NOT_UPTODATE_FILE] = tmp;
+
+	msgs[ERROR_NOT_UPTODATE_DIR] =
+		"Updating the following directories would lose untracked files in it:\n%s";
+
+	if (advice_commit_before_merge)
+		msg = "The following untracked working tree files would be %s by %s:\n%%s"
+			"Please move or remove them before you can %s.";
+	else
+		msg = "The following untracked working tree files would be %s by %s:\n%%s";
+	tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen("removed") + strlen(cmd2) - 4);
+	sprintf(tmp, msg, "removed", cmd, cmd2);
+	msgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] = tmp;
+	tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen("overwritten") + strlen(cmd2) - 4);
+	sprintf(tmp, msg, "overwritten", cmd, cmd2);
+	msgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] = tmp;
+
+	/*
+	 * Special case: ERROR_BIND_OVERLAP refers to a pair of paths, we
+	 * cannot easily display it as a list.
+	 */
+	msgs[ERROR_BIND_OVERLAP] = "Entry '%s' overlaps with '%s'.  Cannot bind.";
+
+	msgs[ERROR_SPARSE_NOT_UPTODATE_FILE] =
+		"Cannot update sparse checkout: the following entries are not up-to-date:\n%s";
+	msgs[ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN] =
+		"The following Working tree files would be overwritten by sparse checkout update:\n%s";
+	msgs[ERROR_WOULD_LOSE_ORPHANED_REMOVED] =
+		"The following Working tree files would be removed by sparse checkout update:\n%s";
+
+	opts->show_all_errors = 1;
+	/* rejected paths may not have a static buffer */
+	for (i = 0; i < ARRAY_SIZE(opts->unpack_rejects); i++)
+		opts->unpack_rejects[i].strdup_strings = 1;
+}
+
+static void do_add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
+			 unsigned int set, unsigned int clear)
+{
+	clear |= CE_HASHED | CE_UNHASHED;
+
+	if (set & CE_REMOVE)
+		set |= CE_WT_REMOVE;
+
+	ce->next = NULL;
+	ce->ce_flags = (ce->ce_flags & ~clear) | set;
+	add_index_entry(&o->result, ce,
+			ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
+}
 
 static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
 	unsigned int set, unsigned int clear)
@@ -45,12 +122,51 @@
 	unsigned int size = ce_size(ce);
 	struct cache_entry *new = xmalloc(size);
 
-	clear |= CE_HASHED | CE_UNHASHED;
-
 	memcpy(new, ce, size);
-	new->next = NULL;
-	new->ce_flags = (new->ce_flags & ~clear) | set;
-	add_index_entry(&o->result, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
+	do_add_entry(o, new, set, clear);
+}
+
+/*
+ * add error messages on path <path>
+ * corresponding to the type <e> with the message <msg>
+ * indicating if it should be display in porcelain or not
+ */
+static int add_rejected_path(struct unpack_trees_options *o,
+			     enum unpack_trees_error_types e,
+			     const char *path)
+{
+	if (!o->show_all_errors)
+		return error(ERRORMSG(o, e), path);
+
+	/*
+	 * Otherwise, insert in a list for future display by
+	 * display_error_msgs()
+	 */
+	string_list_append(&o->unpack_rejects[e], path);
+	return -1;
+}
+
+/*
+ * display all the error messages stored in a nice way
+ */
+static void display_error_msgs(struct unpack_trees_options *o)
+{
+	int e, i;
+	int something_displayed = 0;
+	for (e = 0; e < NB_UNPACK_TREES_ERROR_TYPES; e++) {
+		struct string_list *rejects = &o->unpack_rejects[e];
+		if (rejects->nr > 0) {
+			struct strbuf path = STRBUF_INIT;
+			something_displayed = 1;
+			for (i = 0; i < rejects->nr; i++)
+				strbuf_addf(&path, "\t%s\n", rejects->items[i].string);
+			error(ERRORMSG(o, e), path.buf);
+			strbuf_release(&path);
+		}
+		string_list_clear(rejects, 0);
+	}
+	if (something_displayed)
+		fprintf(stderr, "Aborting\n");
 }
 
 /*
@@ -59,9 +175,9 @@
  */
 static void unlink_entry(struct cache_entry *ce)
 {
-	if (has_symlink_or_noent_leading_path(ce->name, ce_namelen(ce)))
+	if (!check_leading_path(ce->name, ce_namelen(ce)))
 		return;
-	if (unlink_or_warn(ce->name))
+	if (remove_or_warn(ce->ce_mode, ce->name))
 		return;
 	schedule_dir_for_removal(ce->name, ce_namelen(ce));
 }
@@ -78,7 +194,7 @@
 	if (o->update && o->verbose_update) {
 		for (total = cnt = 0; cnt < index->cache_nr; cnt++) {
 			struct cache_entry *ce = index->cache[cnt];
-			if (ce->ce_flags & (CE_UPDATE | CE_REMOVE))
+			if (ce->ce_flags & (CE_UPDATE | CE_WT_REMOVE))
 				total++;
 		}
 
@@ -92,10 +208,11 @@
 	for (i = 0; i < index->cache_nr; i++) {
 		struct cache_entry *ce = index->cache[i];
 
-		if (ce->ce_flags & CE_REMOVE) {
+		if (ce->ce_flags & CE_WT_REMOVE) {
 			display_progress(progress, ++cnt);
-			if (o->update)
+			if (o->update && !o->dry_run)
 				unlink_entry(ce);
+			continue;
 		}
 	}
 	remove_marked_cache_entries(&o->result);
@@ -107,7 +224,7 @@
 		if (ce->ce_flags & CE_UPDATE) {
 			display_progress(progress, ++cnt);
 			ce->ce_flags &= ~CE_UPDATE;
-			if (o->update) {
+			if (o->update && !o->dry_run) {
 				errs |= checkout_entry(ce, &state, NULL);
 			}
 		}
@@ -118,6 +235,62 @@
 	return errs != 0;
 }
 
+static int verify_uptodate_sparse(struct cache_entry *ce, struct unpack_trees_options *o);
+static int verify_absent_sparse(struct cache_entry *ce, enum unpack_trees_error_types, struct unpack_trees_options *o);
+
+static int apply_sparse_checkout(struct cache_entry *ce, struct unpack_trees_options *o)
+{
+	int was_skip_worktree = ce_skip_worktree(ce);
+
+	if (ce->ce_flags & CE_NEW_SKIP_WORKTREE)
+		ce->ce_flags |= CE_SKIP_WORKTREE;
+	else
+		ce->ce_flags &= ~CE_SKIP_WORKTREE;
+
+	/*
+	 * if (!was_skip_worktree && !ce_skip_worktree()) {
+	 *	This is perfectly normal. Move on;
+	 * }
+	 */
+
+	/*
+	 * Merge strategies may set CE_UPDATE|CE_REMOVE outside checkout
+	 * area as a result of ce_skip_worktree() shortcuts in
+	 * verify_absent() and verify_uptodate().
+	 * Make sure they don't modify worktree if they are already
+	 * outside checkout area
+	 */
+	if (was_skip_worktree && ce_skip_worktree(ce)) {
+		ce->ce_flags &= ~CE_UPDATE;
+
+		/*
+		 * By default, when CE_REMOVE is on, CE_WT_REMOVE is also
+		 * on to get that file removed from both index and worktree.
+		 * If that file is already outside worktree area, don't
+		 * bother remove it.
+		 */
+		if (ce->ce_flags & CE_REMOVE)
+			ce->ce_flags &= ~CE_WT_REMOVE;
+	}
+
+	if (!was_skip_worktree && ce_skip_worktree(ce)) {
+		/*
+		 * If CE_UPDATE is set, verify_uptodate() must be called already
+		 * also stat info may have lost after merged_entry() so calling
+		 * verify_uptodate() again may fail
+		 */
+		if (!(ce->ce_flags & CE_UPDATE) && verify_uptodate_sparse(ce, o))
+			return -1;
+		ce->ce_flags |= CE_WT_REMOVE;
+	}
+	if (was_skip_worktree && !ce_skip_worktree(ce)) {
+		if (verify_absent_sparse(ce, ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o))
+			return -1;
+		ce->ce_flags |= CE_UPDATE;
+	}
+	return 0;
+}
+
 static inline int call_unpack_fn(struct cache_entry **src, struct unpack_trees_options *o)
 {
 	int ret = o->fn(src, o);
@@ -126,24 +299,149 @@
 	return ret;
 }
 
-static int unpack_index_entry(struct cache_entry *ce, struct unpack_trees_options *o)
+static void mark_ce_used(struct cache_entry *ce, struct unpack_trees_options *o)
 {
-	struct cache_entry *src[5] = { ce, NULL, };
+	ce->ce_flags |= CE_UNPACKED;
 
-	o->pos++;
+	if (o->cache_bottom < o->src_index->cache_nr &&
+	    o->src_index->cache[o->cache_bottom] == ce) {
+		int bottom = o->cache_bottom;
+		while (bottom < o->src_index->cache_nr &&
+		       o->src_index->cache[bottom]->ce_flags & CE_UNPACKED)
+			bottom++;
+		o->cache_bottom = bottom;
+	}
+}
+
+static void mark_all_ce_unused(struct index_state *index)
+{
+	int i;
+	for (i = 0; i < index->cache_nr; i++)
+		index->cache[i]->ce_flags &= ~(CE_UNPACKED | CE_ADDED | CE_NEW_SKIP_WORKTREE);
+}
+
+static int locate_in_src_index(struct cache_entry *ce,
+			       struct unpack_trees_options *o)
+{
+	struct index_state *index = o->src_index;
+	int len = ce_namelen(ce);
+	int pos = index_name_pos(index, ce->name, len);
+	if (pos < 0)
+		pos = -1 - pos;
+	return pos;
+}
+
+/*
+ * We call unpack_index_entry() with an unmerged cache entry
+ * only in diff-index, and it wants a single callback.  Skip
+ * the other unmerged entry with the same name.
+ */
+static void mark_ce_used_same_name(struct cache_entry *ce,
+				   struct unpack_trees_options *o)
+{
+	struct index_state *index = o->src_index;
+	int len = ce_namelen(ce);
+	int pos;
+
+	for (pos = locate_in_src_index(ce, o); pos < index->cache_nr; pos++) {
+		struct cache_entry *next = index->cache[pos];
+		if (len != ce_namelen(next) ||
+		    memcmp(ce->name, next->name, len))
+			break;
+		mark_ce_used(next, o);
+	}
+}
+
+static struct cache_entry *next_cache_entry(struct unpack_trees_options *o)
+{
+	const struct index_state *index = o->src_index;
+	int pos = o->cache_bottom;
+
+	while (pos < index->cache_nr) {
+		struct cache_entry *ce = index->cache[pos];
+		if (!(ce->ce_flags & CE_UNPACKED))
+			return ce;
+		pos++;
+	}
+	return NULL;
+}
+
+static void add_same_unmerged(struct cache_entry *ce,
+			      struct unpack_trees_options *o)
+{
+	struct index_state *index = o->src_index;
+	int len = ce_namelen(ce);
+	int pos = index_name_pos(index, ce->name, len);
+
+	if (0 <= pos)
+		die("programming error in a caller of mark_ce_used_same_name");
+	for (pos = -pos - 1; pos < index->cache_nr; pos++) {
+		struct cache_entry *next = index->cache[pos];
+		if (len != ce_namelen(next) ||
+		    memcmp(ce->name, next->name, len))
+			break;
+		add_entry(o, next, 0, 0);
+		mark_ce_used(next, o);
+	}
+}
+
+static int unpack_index_entry(struct cache_entry *ce,
+			      struct unpack_trees_options *o)
+{
+	struct cache_entry *src[MAX_UNPACK_TREES + 1] = { NULL, };
+	int ret;
+
+	src[0] = ce;
+
+	mark_ce_used(ce, o);
 	if (ce_stage(ce)) {
 		if (o->skip_unmerged) {
 			add_entry(o, ce, 0, 0);
 			return 0;
 		}
 	}
-	return call_unpack_fn(src, o);
+	ret = call_unpack_fn(src, o);
+	if (ce_stage(ce))
+		mark_ce_used_same_name(ce, o);
+	return ret;
 }
 
-static int traverse_trees_recursive(int n, unsigned long dirmask, unsigned long df_conflicts, struct name_entry *names, struct traverse_info *info)
+static int find_cache_pos(struct traverse_info *, const struct name_entry *);
+
+static void restore_cache_bottom(struct traverse_info *info, int bottom)
 {
-	int i;
+	struct unpack_trees_options *o = info->data;
+
+	if (o->diff_index_cached)
+		return;
+	o->cache_bottom = bottom;
+}
+
+static int switch_cache_bottom(struct traverse_info *info)
+{
+	struct unpack_trees_options *o = info->data;
+	int ret, pos;
+
+	if (o->diff_index_cached)
+		return 0;
+	ret = o->cache_bottom;
+	pos = find_cache_pos(info->prev, &info->name);
+
+	if (pos < -1)
+		o->cache_bottom = -2 - pos;
+	else if (pos < 0)
+		o->cache_bottom = o->src_index->cache_nr;
+	return ret;
+}
+
+static int traverse_trees_recursive(int n, unsigned long dirmask,
+				    unsigned long df_conflicts,
+				    struct name_entry *names,
+				    struct traverse_info *info)
+{
+	int i, ret, bottom;
 	struct tree_desc t[MAX_UNPACK_TREES];
+	void *buf[MAX_UNPACK_TREES];
 	struct traverse_info newinfo;
 	struct name_entry *p;
 
@@ -153,17 +451,26 @@
 
 	newinfo = *info;
 	newinfo.prev = info;
+	newinfo.pathspec = info->pathspec;
 	newinfo.name = *p;
-	newinfo.pathlen += tree_entry_len(p->path, p->sha1) + 1;
+	newinfo.pathlen += tree_entry_len(p) + 1;
 	newinfo.conflicts |= df_conflicts;
 
 	for (i = 0; i < n; i++, dirmask >>= 1) {
 		const unsigned char *sha1 = NULL;
 		if (dirmask & 1)
 			sha1 = names[i].sha1;
-		fill_tree_descriptor(t+i, sha1);
+		buf[i] = fill_tree_descriptor(t+i, sha1);
 	}
-	return traverse_trees(n, t, &newinfo);
+
+	bottom = switch_cache_bottom(&newinfo);
+	ret = traverse_trees(n, t, &newinfo);
+	restore_cache_bottom(&newinfo, bottom);
+
+	for (i = 0; i < n; i++)
+		free(buf[i]);
+
+	return ret;
 }
 
 /*
@@ -195,7 +502,7 @@
 	ce_len -= pathlen;
 	ce_name = ce->name + pathlen;
 
-	len = tree_entry_len(n->path, n->sha1);
+	len = tree_entry_len(n);
 	return df_name_compare(ce_name, ce_len, S_IFREG, n->path, len, n->mode);
 }
 
@@ -212,13 +519,28 @@
 	return ce_namelen(ce) > traverse_path_len(info, n);
 }
 
+static int ce_in_traverse_path(const struct cache_entry *ce,
+			       const struct traverse_info *info)
+{
+	if (!info->prev)
+		return 1;
+	if (do_compare_entry(ce, info->prev, &info->name))
+		return 0;
+	/*
+	 * If ce (blob) is the same name as the path (which is a tree
+	 * we will be descending into), it won't be inside it.
+	 */
+	return (info->pathlen < ce_namelen(ce));
+}
+
 static struct cache_entry *create_ce_entry(const struct traverse_info *info, const struct name_entry *n, int stage)
 {
 	int len = traverse_path_len(info, n);
 	struct cache_entry *ce = xcalloc(1, cache_entry_size(len));
 
 	ce->ce_mode = create_ce_mode(n->mode);
-	ce->ce_flags = create_ce_flags(len, stage);
+	ce->ce_flags = create_ce_flags(stage);
+	ce->ce_namelen = len;
 	hashcpy(ce->sha1, n->sha1);
 	make_traverse_path(ce->name, info, n);
 
@@ -273,10 +595,137 @@
 
 	for (i = 0; i < n; i++)
 		if (src[i] && src[i] != o->df_conflict_entry)
-			add_entry(o, src[i], 0, 0);
+			do_add_entry(o, src[i], 0, 0);
 	return 0;
 }
 
+static int unpack_failed(struct unpack_trees_options *o, const char *message)
+{
+	discard_index(&o->result);
+	if (!o->gently && !o->exiting_early) {
+		if (message)
+			return error("%s", message);
+		return -1;
+	}
+	return -1;
+}
+
+/* NEEDSWORK: give this a better name and share with tree-walk.c */
+static int name_compare(const char *a, int a_len,
+			const char *b, int b_len)
+{
+	int len = (a_len < b_len) ? a_len : b_len;
+	int cmp = memcmp(a, b, len);
+	if (cmp)
+		return cmp;
+	return (a_len - b_len);
+}
+
+/*
+ * The tree traversal is looking at name p.  If we have a matching entry,
+ * return it.  If name p is a directory in the index, do not return
+ * anything, as we will want to match it when the traversal descends into
+ * the directory.
+ */
+static int find_cache_pos(struct traverse_info *info,
+			  const struct name_entry *p)
+{
+	int pos;
+	struct unpack_trees_options *o = info->data;
+	struct index_state *index = o->src_index;
+	int pfxlen = info->pathlen;
+	int p_len = tree_entry_len(p);
+
+	for (pos = o->cache_bottom; pos < index->cache_nr; pos++) {
+		struct cache_entry *ce = index->cache[pos];
+		const char *ce_name, *ce_slash;
+		int cmp, ce_len;
+
+		if (ce->ce_flags & CE_UNPACKED) {
+			/*
+			 * cache_bottom entry is already unpacked, so
+			 * we can never match it; don't check it
+			 * again.
+			 */
+			if (pos == o->cache_bottom)
+				++o->cache_bottom;
+			continue;
+		}
+		if (!ce_in_traverse_path(ce, info))
+			continue;
+		ce_name = ce->name + pfxlen;
+		ce_slash = strchr(ce_name, '/');
+		if (ce_slash)
+			ce_len = ce_slash - ce_name;
+		else
+			ce_len = ce_namelen(ce) - pfxlen;
+		cmp = name_compare(p->path, p_len, ce_name, ce_len);
+		/*
+		 * Exact match; if we have a directory we need to
+		 * delay returning it.
+		 */
+		if (!cmp)
+			return ce_slash ? -2 - pos : pos;
+		if (0 < cmp)
+			continue; /* keep looking */
+		/*
+		 * ce_name sorts after p->path; could it be that we
+		 * have files under p->path directory in the index?
+		 * E.g.  ce_name == "t-i", and p->path == "t"; we may
+		 * have "t/a" in the index.
+		 */
+		if (p_len < ce_len && !memcmp(ce_name, p->path, p_len) &&
+		    ce_name[p_len] < '/')
+			continue; /* keep looking */
+		break;
+	}
+	return -1;
+}
+
+static struct cache_entry *find_cache_entry(struct traverse_info *info,
+					    const struct name_entry *p)
+{
+	int pos = find_cache_pos(info, p);
+	struct unpack_trees_options *o = info->data;
+
+	if (0 <= pos)
+		return o->src_index->cache[pos];
+	else
+		return NULL;
+}
+
+static void debug_path(struct traverse_info *info)
+{
+	if (info->prev) {
+		debug_path(info->prev);
+		if (*info->prev->name.path)
+			putchar('/');
+	}
+	printf("%s", info->name.path);
+}
+
+static void debug_name_entry(int i, struct name_entry *n)
+{
+	printf("ent#%d %06o %s\n", i,
+	       n->path ? n->mode : 0,
+	       n->path ? n->path : "(missing)");
+}
+
+static void debug_unpack_callback(int n,
+				  unsigned long mask,
+				  unsigned long dirmask,
+				  struct name_entry *names,
+				  struct traverse_info *info)
+{
+	int i;
+	printf("* unpack mask %lu, dirmask %lu, cnt %d ",
+	       mask, dirmask, n);
+	debug_path(info);
+	putchar('\n');
+	for (i = 0; i < n; i++)
+		debug_name_entry(i, names + i);
+}
+
 static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, struct name_entry *names, struct traverse_info *info)
 {
 	struct cache_entry *src[MAX_UNPACK_TREES + 1] = { NULL, };
@@ -287,25 +736,38 @@
 	while (!p->mode)
 		p++;
 
+	if (o->debug_unpack)
+		debug_unpack_callback(n, mask, dirmask, names, info);
+
 	/* Are we supposed to look at the index too? */
 	if (o->merge) {
-		while (o->pos < o->src_index->cache_nr) {
-			struct cache_entry *ce = o->src_index->cache[o->pos];
-			int cmp = compare_entry(ce, info, p);
+		while (1) {
+			int cmp;
+			struct cache_entry *ce;
+
+			if (o->diff_index_cached)
+				ce = next_cache_entry(o);
+			else
+				ce = find_cache_entry(info, p);
+
+			if (!ce)
+				break;
+			cmp = compare_entry(ce, info, p);
 			if (cmp < 0) {
 				if (unpack_index_entry(ce, o) < 0)
-					return -1;
+					return unpack_failed(o, NULL);
 				continue;
 			}
 			if (!cmp) {
-				o->pos++;
 				if (ce_stage(ce)) {
 					/*
-					 * If we skip unmerged index entries, we'll skip this
-					 * entry *and* the tree entries associated with it!
+					 * If we skip unmerged index
+					 * entries, we'll skip this
+					 * entry *and* the tree
+					 * entries associated with it!
 					 */
 					if (o->skip_unmerged) {
-						add_entry(o, ce, 0, 0);
+						add_same_unmerged(ce, o);
 						return mask;
 					}
 				}
@@ -318,6 +780,13 @@
 	if (unpack_nondirectories(n, mask, dirmask, src, names, info) < 0)
 		return -1;
 
+	if (o->merge && src[0]) {
+		if (ce_stage(src[0]))
+			mark_ce_used_same_name(src[0], o);
+		else
+			mark_ce_used(src[0], o);
+	}
+
 	/* Now handle any directories.. */
 	if (dirmask) {
 		unsigned long conflicts = mask & ~dirmask;
@@ -334,11 +803,13 @@
 			matches = cache_tree_matches_traversal(o->src_index->cache_tree,
 							       names, info);
 			/*
-			 * Everything under the name matches.  Adjust o->pos to
-			 * skip the entire hierarchy.
+			 * Everything under the name matches; skip the
+			 * entire hierarchy.  diff_index_cached codepath
+			 * special cases D/F conflicts in such a way that
+			 * it does not do any look-ahead, so this is safe.
 			 */
 			if (matches) {
-				o->pos += matches;
+				o->cache_bottom += matches;
 				return mask;
 			}
 		}
@@ -352,25 +823,188 @@
 	return mask;
 }
 
-static int unpack_failed(struct unpack_trees_options *o, const char *message)
+static int clear_ce_flags_1(struct cache_entry **cache, int nr,
+			    char *prefix, int prefix_len,
+			    int select_mask, int clear_mask,
+			    struct exclude_list *el, int defval);
+
+/* Whole directory matching */
+static int clear_ce_flags_dir(struct cache_entry **cache, int nr,
+			      char *prefix, int prefix_len,
+			      char *basename,
+			      int select_mask, int clear_mask,
+			      struct exclude_list *el, int defval)
 {
-	discard_index(&o->result);
-	if (!o->gently) {
-		if (message)
-			return error("%s", message);
-		return -1;
+	struct cache_entry **cache_end;
+	int dtype = DT_DIR;
+	int ret = excluded_from_list(prefix, prefix_len, basename, &dtype, el);
+
+	prefix[prefix_len++] = '/';
+
+	/* If undecided, use matching result of parent dir in defval */
+	if (ret < 0)
+		ret = defval;
+
+	for (cache_end = cache; cache_end != cache + nr; cache_end++) {
+		struct cache_entry *ce = *cache_end;
+		if (strncmp(ce->name, prefix, prefix_len))
+			break;
 	}
-	return -1;
+
+	/*
+	 * TODO: check el, if there are no patterns that may conflict
+	 * with ret (iow, we know in advance the incl/excl
+	 * decision for the entire directory), clear flag here without
+	 * calling clear_ce_flags_1(). That function will call
+	 * the expensive excluded_from_list() on every entry.
+	 */
+	return clear_ce_flags_1(cache, cache_end - cache,
+				prefix, prefix_len,
+				select_mask, clear_mask,
+				el, ret);
 }
 
 /*
+ * Traverse the index, find every entry that matches according to
+ * o->el. Do "ce_flags &= ~clear_mask" on those entries. Return the
+ * number of traversed entries.
+ *
+ * If select_mask is non-zero, only entries whose ce_flags has on of
+ * those bits enabled are traversed.
+ *
+ * cache	: pointer to an index entry
+ * prefix_len	: an offset to its path
+ *
+ * The current path ("prefix") including the trailing '/' is
+ *   cache[0]->name[0..(prefix_len-1)]
+ * Top level path has prefix_len zero.
+ */
+static int clear_ce_flags_1(struct cache_entry **cache, int nr,
+			    char *prefix, int prefix_len,
+			    int select_mask, int clear_mask,
+			    struct exclude_list *el, int defval)
+{
+	struct cache_entry **cache_end = cache + nr;
+
+	/*
+	 * Process all entries that have the given prefix and meet
+	 * select_mask condition
+	 */
+	while(cache != cache_end) {
+		struct cache_entry *ce = *cache;
+		const char *name, *slash;
+		int len, dtype, ret;
+
+		if (select_mask && !(ce->ce_flags & select_mask)) {
+			cache++;
+			continue;
+		}
+
+		if (prefix_len && strncmp(ce->name, prefix, prefix_len))
+			break;
+
+		name = ce->name + prefix_len;
+		slash = strchr(name, '/');
+
+		/* If it's a directory, try whole directory match first */
+		if (slash) {
+			int processed;
+
+			len = slash - name;
+			memcpy(prefix + prefix_len, name, len);
+
+			/*
+			 * terminate the string (no trailing slash),
+			 * clear_c_f_dir needs it
+			 */
+			prefix[prefix_len + len] = '\0';
+			processed = clear_ce_flags_dir(cache, cache_end - cache,
+						       prefix, prefix_len + len,
+						       prefix + prefix_len,
+						       select_mask, clear_mask,
+						       el, defval);
+
+			/* clear_c_f_dir eats a whole dir already? */
+			if (processed) {
+				cache += processed;
+				continue;
+			}
+
+			prefix[prefix_len + len++] = '/';
+			cache += clear_ce_flags_1(cache, cache_end - cache,
+						  prefix, prefix_len + len,
+						  select_mask, clear_mask, el, defval);
+			continue;
+		}
+
+		/* Non-directory */
+		dtype = ce_to_dtype(ce);
+		ret = excluded_from_list(ce->name, ce_namelen(ce), name, &dtype, el);
+		if (ret < 0)
+			ret = defval;
+		if (ret > 0)
+			ce->ce_flags &= ~clear_mask;
+		cache++;
+	}
+	return nr - (cache_end - cache);
+}
+
+static int clear_ce_flags(struct cache_entry **cache, int nr,
+			    int select_mask, int clear_mask,
+			    struct exclude_list *el)
+{
+	char prefix[PATH_MAX];
+	return clear_ce_flags_1(cache, nr,
+				prefix, 0,
+				select_mask, clear_mask,
+				el, 0);
+}
+
+/*
+ * Set/Clear CE_NEW_SKIP_WORKTREE according to $GIT_DIR/info/sparse-checkout
+ */
+static void mark_new_skip_worktree(struct exclude_list *el,
+				   struct index_state *the_index,
+				   int select_flag, int skip_wt_flag)
+{
+	int i;
+
+	/*
+	 * 1. Pretend the narrowest worktree: only unmerged entries
+	 * are checked out
+	 */
+	for (i = 0; i < the_index->cache_nr; i++) {
+		struct cache_entry *ce = the_index->cache[i];
+
+		if (select_flag && !(ce->ce_flags & select_flag))
+			continue;
+
+		if (!ce_stage(ce))
+			ce->ce_flags |= skip_wt_flag;
+		else
+			ce->ce_flags &= ~skip_wt_flag;
+	}
+
+	/*
+	 * 2. Widen worktree according to sparse-checkout file.
+	 * Matched entries will have skip_wt_flag cleared (i.e. "in")
+	 */
+	clear_ce_flags(the_index->cache, the_index->cache_nr,
+		       select_flag, skip_wt_flag, el);
+}
+
+static int verify_absent(struct cache_entry *, enum unpack_trees_error_types, struct unpack_trees_options *);
+/*
  * N-way merge "len" trees.  Returns 0 on success, -1 on failure to manipulate the
  * resulting index, -2 on failure to reflect the changes to the work tree.
+ *
+ * CE_ADDED, CE_UNPACKED and CE_NEW_SKIP_WORKTREE are used internally
  */
 int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o)
 {
-	int ret;
+	int i, ret;
 	static struct cache_entry *dfc;
+	struct exclude_list el;
 
 	if (len > MAX_UNPACK_TREES)
 		die("unpack_trees takes at most %d trees", MAX_UNPACK_TREES);
@@ -380,13 +1014,33 @@
 	state.quiet = 1;
 	state.refresh_cache = 1;
 
+	memset(&el, 0, sizeof(el));
+	if (!core_apply_sparse_checkout || !o->update)
+		o->skip_sparse_checkout = 1;
+	if (!o->skip_sparse_checkout) {
+		if (add_excludes_from_file_to_list(git_path("info/sparse-checkout"), "", 0, NULL, &el, 0) < 0)
+			o->skip_sparse_checkout = 1;
+		else
+			o->el = &el;
+	}
+
+	if (o->dir) {
+		o->path_exclude_check = xmalloc(sizeof(struct path_exclude_check));
+		path_exclude_check_init(o->path_exclude_check, o->dir);
+	}
 	memset(&o->result, 0, sizeof(o->result));
 	o->result.initialized = 1;
-	if (o->src_index) {
-		o->result.timestamp.sec = o->src_index->timestamp.sec;
-		o->result.timestamp.nsec = o->src_index->timestamp.nsec;
-	}
+	o->result.timestamp.sec = o->src_index->timestamp.sec;
+	o->result.timestamp.nsec = o->src_index->timestamp.nsec;
+	o->result.version = o->src_index->version;
 	o->merge_size = len;
+	mark_all_ce_unused(o->src_index);
+
+	/*
+	 * Sparse checkout loop #1: set NEW_SKIP_WORKTREE on existing entries
+	 */
+	if (!o->skip_sparse_checkout)
+		mark_new_skip_worktree(o->el, o->src_index, 0, CE_NEW_SKIP_WORKTREE);
 
 	if (!dfc)
 		dfc = xcalloc(1, cache_entry_size(0));
@@ -399,35 +1053,127 @@
 		setup_traverse_info(&info, prefix);
 		info.fn = unpack_callback;
 		info.data = o;
+		info.show_all_errors = o->show_all_errors;
+		info.pathspec = o->pathspec;
+
+		if (o->prefix) {
+			/*
+			 * Unpack existing index entries that sort before the
+			 * prefix the tree is spliced into.  Note that o->merge
+			 * is always true in this case.
+			 */
+			while (1) {
+				struct cache_entry *ce = next_cache_entry(o);
+				if (!ce)
+					break;
+				if (ce_in_traverse_path(ce, &info))
+					break;
+				if (unpack_index_entry(ce, o) < 0)
+					goto return_failed;
+			}
+		}
 
 		if (traverse_trees(len, t, &info) < 0)
-			return unpack_failed(o, NULL);
+			goto return_failed;
 	}
 
 	/* Any left-over entries in the index? */
 	if (o->merge) {
-		while (o->pos < o->src_index->cache_nr) {
-			struct cache_entry *ce = o->src_index->cache[o->pos];
+		while (1) {
+			struct cache_entry *ce = next_cache_entry(o);
+			if (!ce)
+				break;
 			if (unpack_index_entry(ce, o) < 0)
-				return unpack_failed(o, NULL);
+				goto return_failed;
 		}
 	}
+	mark_all_ce_unused(o->src_index);
 
-	if (o->trivial_merges_only && o->nontrivial_merge)
-		return unpack_failed(o, "Merge requires file-level merging");
+	if (o->trivial_merges_only && o->nontrivial_merge) {
+		ret = unpack_failed(o, "Merge requires file-level merging");
+		goto done;
+	}
+
+	if (!o->skip_sparse_checkout) {
+		int empty_worktree = 1;
+
+		/*
+		 * Sparse checkout loop #2: set NEW_SKIP_WORKTREE on entries not in loop #1
+		 * If the will have NEW_SKIP_WORKTREE, also set CE_SKIP_WORKTREE
+		 * so apply_sparse_checkout() won't attempt to remove it from worktree
+		 */
+		mark_new_skip_worktree(o->el, &o->result, CE_ADDED, CE_SKIP_WORKTREE | CE_NEW_SKIP_WORKTREE);
+
+		ret = 0;
+		for (i = 0; i < o->result.cache_nr; i++) {
+			struct cache_entry *ce = o->result.cache[i];
+
+			/*
+			 * Entries marked with CE_ADDED in merged_entry() do not have
+			 * verify_absent() check (the check is effectively disabled
+			 * because CE_NEW_SKIP_WORKTREE is set unconditionally).
+			 *
+			 * Do the real check now because we have had
+			 * correct CE_NEW_SKIP_WORKTREE
+			 */
+			if (ce->ce_flags & CE_ADDED &&
+			    verify_absent(ce, ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o)) {
+				if (!o->show_all_errors)
+					goto return_failed;
+				ret = -1;
+			}
+
+			if (apply_sparse_checkout(ce, o)) {
+				if (!o->show_all_errors)
+					goto return_failed;
+				ret = -1;
+			}
+			if (!ce_skip_worktree(ce))
+				empty_worktree = 0;
+
+		}
+		if (ret < 0)
+			goto return_failed;
+		/*
+		 * Sparse checkout is meant to narrow down checkout area
+		 * but it does not make sense to narrow down to empty working
+		 * tree. This is usually a mistake in sparse checkout rules.
+		 * Do not allow users to do that.
+		 */
+		if (o->result.cache_nr && empty_worktree) {
+			ret = unpack_failed(o, "Sparse checkout leaves no entry on working directory");
+			goto done;
+		}
+	}
 
 	o->src_index = NULL;
 	ret = check_updates(o) ? (-2) : 0;
 	if (o->dst_index)
 		*o->dst_index = o->result;
+
+done:
+	free_excludes(&el);
+	if (o->path_exclude_check) {
+		path_exclude_check_clear(o->path_exclude_check);
+		free(o->path_exclude_check);
+	}
 	return ret;
+
+return_failed:
+	if (o->show_all_errors)
+		display_error_msgs(o);
+	mark_all_ce_unused(o->src_index);
+	ret = unpack_failed(o, NULL);
+	if (o->exiting_early)
+		ret = 0;
+	goto done;
 }
 
 /* Here come the merge functions */
 
 static int reject_merge(struct cache_entry *ce, struct unpack_trees_options *o)
 {
-	return error(ERRORMSG(o, would_overwrite), ce->name);
+	return add_rejected_path(o, ERROR_WOULD_OVERWRITE, ce->name);
 }
 
 static int same(struct cache_entry *a, struct cache_entry *b)
@@ -436,6 +1182,8 @@
 		return 0;
 	if (!a && !b)
 		return 1;
+	if ((a->ce_flags | b->ce_flags) & CE_CONFLICTED)
+		return 0;
 	return a->ce_mode == b->ce_mode &&
 	       !hashcmp(a->sha1, b->sha1);
 }
@@ -445,21 +1193,33 @@
  * When a CE gets turned into an unmerged entry, we
  * want it to be up-to-date
  */
-static int verify_uptodate(struct cache_entry *ce,
-		struct unpack_trees_options *o)
+static int verify_uptodate_1(struct cache_entry *ce,
+				   struct unpack_trees_options *o,
+				   enum unpack_trees_error_types error_type)
 {
 	struct stat st;
 
-	if (o->index_only || o->reset || ce_uptodate(ce))
+	if (o->index_only)
+		return 0;
+
+	/*
+	 * CE_VALID and CE_SKIP_WORKTREE cheat, we better check again
+	 * if this entry is truly up-to-date because this file may be
+	 * overwritten.
+	 */
+	if ((ce->ce_flags & CE_VALID) || ce_skip_worktree(ce))
+		; /* keep checking */
+	else if (o->reset || ce_uptodate(ce))
 		return 0;
 
 	if (!lstat(ce->name, &st)) {
-		unsigned changed = ie_match_stat(o->src_index, ce, &st, CE_MATCH_IGNORE_VALID);
+		int flags = CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE;
+		unsigned changed = ie_match_stat(o->src_index, ce, &st, flags);
 		if (!changed)
 			return 0;
 		/*
 		 * NEEDSWORK: the current default policy is to allow
-		 * submodule to be out of sync wrt the supermodule
+		 * submodule to be out of sync wrt the superproject
 		 * index.  This needs to be tightened later for
 		 * submodules that are marked to be automatically
 		 * checked out.
@@ -471,7 +1231,21 @@
 	if (errno == ENOENT)
 		return 0;
 	return o->gently ? -1 :
-		error(ERRORMSG(o, not_uptodate_file), ce->name);
+		add_rejected_path(o, error_type, ce->name);
+}
+
+static int verify_uptodate(struct cache_entry *ce,
+			   struct unpack_trees_options *o)
+{
+	if (!o->skip_sparse_checkout && (ce->ce_flags & CE_NEW_SKIP_WORKTREE))
+		return 0;
+	return verify_uptodate_1(ce, o, ERROR_NOT_UPTODATE_FILE);
+}
+
+static int verify_uptodate_sparse(struct cache_entry *ce,
+				  struct unpack_trees_options *o)
+{
+	return verify_uptodate_1(ce, o, ERROR_SPARSE_NOT_UPTODATE_FILE);
 }
 
 static void invalidate_ce_path(struct cache_entry *ce, struct unpack_trees_options *o)
@@ -487,13 +1261,15 @@
  * Currently, git does not checkout subprojects during a superproject
  * checkout, so it is not going to overwrite anything.
  */
-static int verify_clean_submodule(struct cache_entry *ce, const char *action,
+static int verify_clean_submodule(struct cache_entry *ce,
+				      enum unpack_trees_error_types error_type,
 				      struct unpack_trees_options *o)
 {
 	return 0;
 }
 
-static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
+static int verify_clean_subdirectory(struct cache_entry *ce,
+				      enum unpack_trees_error_types error_type,
 				      struct unpack_trees_options *o)
 {
 	/*
@@ -514,15 +1290,17 @@
 		 */
 		if (!hashcmp(sha1, ce->sha1))
 			return 0;
-		return verify_clean_submodule(ce, action, o);
+		return verify_clean_submodule(ce, error_type, o);
 	}
 
 	/*
 	 * First let's make sure we do not have a local modification
 	 * in that directory.
 	 */
-	namelen = strlen(ce->name);
-	for (i = o->pos; i < o->src_index->cache_nr; i++) {
+	namelen = ce_namelen(ce);
+	for (i = locate_in_src_index(ce, o);
+	     i < o->src_index->cache_nr;
+	     i++) {
 		struct cache_entry *ce2 = o->src_index->cache[i];
 		int len = ce_namelen(ce2);
 		if (len < namelen ||
@@ -530,12 +1308,14 @@
 		    ce2->name[namelen] != '/')
 			break;
 		/*
-		 * ce2->name is an entry in the subdirectory.
+		 * ce2->name is an entry in the subdirectory to be
+		 * removed.
 		 */
 		if (!ce_stage(ce2)) {
 			if (verify_uptodate(ce2, o))
 				return -1;
 			add_entry(o, ce2, CE_REMOVE, 0);
+			mark_ce_used(ce2, o);
 		}
 		cnt++;
 	}
@@ -554,7 +1334,7 @@
 	i = read_directory(&d, pathbuf, namelen+1, NULL);
 	if (i)
 		return o->gently ? -1 :
-			error(ERRORMSG(o, not_uptodate_dir), ce->name);
+			add_rejected_path(o, ERROR_NOT_UPTODATE_DIR, ce->name);
 	free(pathbuf);
 	return cnt;
 }
@@ -567,98 +1347,123 @@
  * See if we can find a case-insensitive match in the index that also
  * matches the stat information, and assume it's that other file!
  */
-static int icase_exists(struct unpack_trees_options *o, struct cache_entry *dst, struct stat *st)
+static int icase_exists(struct unpack_trees_options *o, const char *name, int len, struct stat *st)
 {
 	struct cache_entry *src;
 
-	src = index_name_exists(o->src_index, dst->name, ce_namelen(dst), 1);
-	return src && !ie_match_stat(o->src_index, src, st, CE_MATCH_IGNORE_VALID);
+	src = index_name_exists(o->src_index, name, len, 1);
+	return src && !ie_match_stat(o->src_index, src, st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);
+}
+
+static int check_ok_to_remove(const char *name, int len, int dtype,
+			      struct cache_entry *ce, struct stat *st,
+			      enum unpack_trees_error_types error_type,
+			      struct unpack_trees_options *o)
+{
+	struct cache_entry *result;
+
+	/*
+	 * It may be that the 'lstat()' succeeded even though
+	 * target 'ce' was absent, because there is an old
+	 * entry that is different only in case..
+	 *
+	 * Ignore that lstat() if it matches.
+	 */
+	if (ignore_case && icase_exists(o, name, len, st))
+		return 0;
+
+	if (o->dir &&
+	    path_excluded(o->path_exclude_check, name, -1, &dtype))
+		/*
+		 * ce->name is explicitly excluded, so it is Ok to
+		 * overwrite it.
+		 */
+		return 0;
+	if (S_ISDIR(st->st_mode)) {
+		/*
+		 * We are checking out path "foo" and
+		 * found "foo/." in the working tree.
+		 * This is tricky -- if we have modified
+		 * files that are in "foo/" we would lose
+		 * them.
+		 */
+		if (verify_clean_subdirectory(ce, error_type, o) < 0)
+			return -1;
+		return 0;
+	}
+
+	/*
+	 * The previous round may already have decided to
+	 * delete this path, which is in a subdirectory that
+	 * is being replaced with a blob.
+	 */
+	result = index_name_exists(&o->result, name, len, 0);
+	if (result) {
+		if (result->ce_flags & CE_REMOVE)
+			return 0;
+	}
+
+	return o->gently ? -1 :
+		add_rejected_path(o, error_type, name);
 }
 
 /*
  * We do not want to remove or overwrite a working tree file that
  * is not tracked, unless it is ignored.
  */
-static int verify_absent(struct cache_entry *ce, const char *action,
-			 struct unpack_trees_options *o)
+static int verify_absent_1(struct cache_entry *ce,
+				 enum unpack_trees_error_types error_type,
+				 struct unpack_trees_options *o)
 {
+	int len;
 	struct stat st;
 
 	if (o->index_only || o->reset || !o->update)
 		return 0;
 
-	if (has_symlink_or_noent_leading_path(ce->name, ce_namelen(ce)))
+	len = check_leading_path(ce->name, ce_namelen(ce));
+	if (!len)
 		return 0;
+	else if (len > 0) {
+		char path[PATH_MAX + 1];
+		memcpy(path, ce->name, len);
+		path[len] = 0;
+		if (lstat(path, &st))
+			return error("cannot stat '%s': %s", path,
+					strerror(errno));
 
-	if (!lstat(ce->name, &st)) {
-		int ret;
-		int dtype = ce_to_dtype(ce);
-		struct cache_entry *result;
-
-		/*
-		 * It may be that the 'lstat()' succeeded even though
-		 * target 'ce' was absent, because there is an old
-		 * entry that is different only in case..
-		 *
-		 * Ignore that lstat() if it matches.
-		 */
-		if (ignore_case && icase_exists(o, ce, &st))
-			return 0;
-
-		if (o->dir && excluded(o->dir, ce->name, &dtype))
-			/*
-			 * ce->name is explicitly excluded, so it is Ok to
-			 * overwrite it.
-			 */
-			return 0;
-		if (S_ISDIR(st.st_mode)) {
-			/*
-			 * We are checking out path "foo" and
-			 * found "foo/." in the working tree.
-			 * This is tricky -- if we have modified
-			 * files that are in "foo/" we would lose
-			 * it.
-			 */
-			ret = verify_clean_subdirectory(ce, action, o);
-			if (ret < 0)
-				return ret;
-
-			/*
-			 * If this removed entries from the index,
-			 * what that means is:
-			 *
-			 * (1) the caller unpack_callback() saw path/foo
-			 * in the index, and it has not removed it because
-			 * it thinks it is handling 'path' as blob with
-			 * D/F conflict;
-			 * (2) we will return "ok, we placed a merged entry
-			 * in the index" which would cause o->pos to be
-			 * incremented by one;
-			 * (3) however, original o->pos now has 'path/foo'
-			 * marked with "to be removed".
-			 *
-			 * We need to increment it by the number of
-			 * deleted entries here.
-			 */
-			o->pos += ret;
-			return 0;
-		}
-
-		/*
-		 * The previous round may already have decided to
-		 * delete this path, which is in a subdirectory that
-		 * is being replaced with a blob.
-		 */
-		result = index_name_exists(&o->result, ce->name, ce_namelen(ce), 0);
-		if (result) {
-			if (result->ce_flags & CE_REMOVE)
-				return 0;
-		}
-
-		return o->gently ? -1 :
-			error(ERRORMSG(o, would_lose_untracked), ce->name, action);
+		return check_ok_to_remove(path, len, DT_UNKNOWN, NULL, &st,
+				error_type, o);
+	} else if (lstat(ce->name, &st)) {
+		if (errno != ENOENT)
+			return error("cannot stat '%s': %s", ce->name,
+				     strerror(errno));
+		return 0;
+	} else {
+		return check_ok_to_remove(ce->name, ce_namelen(ce),
+					  ce_to_dtype(ce), ce, &st,
+					  error_type, o);
 	}
-	return 0;
+}
+
+static int verify_absent(struct cache_entry *ce,
+			 enum unpack_trees_error_types error_type,
+			 struct unpack_trees_options *o)
+{
+	if (!o->skip_sparse_checkout && (ce->ce_flags & CE_NEW_SKIP_WORKTREE))
+		return 0;
+	return verify_absent_1(ce, error_type, o);
+}
+
+static int verify_absent_sparse(struct cache_entry *ce,
+			 enum unpack_trees_error_types error_type,
+			 struct unpack_trees_options *o)
+{
+	enum unpack_trees_error_types orphaned_error = error_type;
+	if (orphaned_error == ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN)
+		orphaned_error = ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN;
+
+	return verify_absent_1(ce, orphaned_error, o);
 }
 
 static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
@@ -666,7 +1471,26 @@
 {
 	int update = CE_UPDATE;
 
-	if (old) {
+	if (!old) {
+		/*
+		 * New index entries. In sparse checkout, the following
+		 * verify_absent() will be delayed until after
+		 * traverse_trees() finishes in unpack_trees(), then:
+		 *
+		 *  - CE_NEW_SKIP_WORKTREE will be computed correctly
+		 *  - verify_absent() be called again, this time with
+		 *    correct CE_NEW_SKIP_WORKTREE
+		 *
+		 * verify_absent() call here does nothing in sparse
+		 * checkout (i.e. o->skip_sparse_checkout == 0)
+		 */
+		update |= CE_ADDED;
+		merge->ce_flags |= CE_NEW_SKIP_WORKTREE;
+
+		if (verify_absent(merge, ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o))
+			return -1;
+		invalidate_ce_path(merge, o);
+	} else if (!(old->ce_flags & CE_CONFLICTED)) {
 		/*
 		 * See if we can re-use the old CE directly?
 		 * That way we get the uptodate stat info.
@@ -680,13 +1504,16 @@
 		} else {
 			if (verify_uptodate(old, o))
 				return -1;
+			/* Migrate old flags over */
+			update |= old->ce_flags & (CE_SKIP_WORKTREE | CE_NEW_SKIP_WORKTREE);
 			invalidate_ce_path(old, o);
 		}
-	}
-	else {
-		if (verify_absent(merge, "overwritten", o))
-			return -1;
-		invalidate_ce_path(merge, o);
+	} else {
+		/*
+		 * Previously unmerged entry left as an existence
+		 * marker by read_index_unmerged();
+		 */
+		invalidate_ce_path(old, o);
 	}
 
 	add_entry(o, merge, update, CE_STAGEMASK);
@@ -698,11 +1525,11 @@
 {
 	/* Did it exist in the index? */
 	if (!old) {
-		if (verify_absent(ce, "removed", o))
+		if (verify_absent(ce, ERROR_WOULD_LOSE_UNTRACKED_REMOVED, o))
 			return -1;
 		return 0;
 	}
-	if (verify_uptodate(old, o))
+	if (!(old->ce_flags & CE_CONFLICTED) && verify_uptodate(old, o))
 		return -1;
 	add_entry(o, ce, CE_REMOVE, 0);
 	invalidate_ce_path(ce, o);
@@ -767,7 +1594,8 @@
 		remote = NULL;
 	}
 
-	/* First, if there's a #16 situation, note that to prevent #13
+	/*
+	 * First, if there's a #16 situation, note that to prevent #13
 	 * and #14.
 	 */
 	if (!same(remote, head)) {
@@ -781,7 +1609,8 @@
 		}
 	}
 
-	/* We start with cases where the index is allowed to match
+	/*
+	 * We start with cases where the index is allowed to match
 	 * something other than the head: #14(ALT) and #2ALT, where it
 	 * is permitted to match the result instead.
 	 */
@@ -811,12 +1640,13 @@
 	if (!head && !remote && any_anc_missing)
 		return 0;
 
-	/* Under the new "aggressive" rule, we resolve mostly trivial
+	/*
+	 * Under the "aggressive" rule, we resolve mostly trivial
 	 * cases that we historically had git-merge-one-file resolve.
 	 */
 	if (o->aggressive) {
-		int head_deleted = !head && !df_conflict_head;
-		int remote_deleted = !remote && !df_conflict_remote;
+		int head_deleted = !head;
+		int remote_deleted = !remote;
 		struct cache_entry *ce = NULL;
 
 		if (index)
@@ -844,7 +1674,7 @@
 			if (index)
 				return deleted_entry(index, index, o);
 			if (ce && !head_deleted) {
-				if (verify_absent(ce, "removed", o))
+				if (verify_absent(ce, ERROR_WOULD_LOSE_UNTRACKED_REMOVED, o))
 					return -1;
 			}
 			return 0;
@@ -895,7 +1725,7 @@
  * Two-way merge.
  *
  * The rule is to "carry forward" what is in the index without losing
- * information across a "fast forward", favoring a successful merge
+ * information across a "fast-forward", favoring a successful merge
  * over a merge failure when it makes sense.  For details of the
  * "carry forward" rule, please see <Documentation/git-read-tree.txt>.
  *
@@ -973,11 +1803,11 @@
 	struct cache_entry *a = src[1];
 
 	if (o->merge_size != 1)
-		return error("Cannot do a bind merge of %d trees\n",
+		return error("Cannot do a bind merge of %d trees",
 			     o->merge_size);
 	if (a && old)
 		return o->gently ? -1 :
-			error(ERRORMSG(o, bind_overlap), a->name, old->name);
+			error(ERRORMSG(o, ERROR_BIND_OVERLAP), a->name, old->name);
 	if (!a)
 		return keep_entry(old, o);
 	else
@@ -1004,10 +1834,10 @@
 
 	if (old && same(old, a)) {
 		int update = 0;
-		if (o->reset && !ce_uptodate(old)) {
+		if (o->reset && !ce_uptodate(old) && !ce_skip_worktree(old)) {
 			struct stat st;
 			if (lstat(old->name, &st) ||
-			    ie_match_stat(o->src_index, old, &st, CE_MATCH_IGNORE_VALID))
+			    ie_match_stat(o->src_index, old, &st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE))
 				update |= CE_UPDATE;
 		}
 		add_entry(o, old, update, 0);
diff --git a/unpack-trees.h b/unpack-trees.h
index d19df44..ec74a9f 100644
--- a/unpack-trees.h
+++ b/unpack-trees.h
@@ -1,21 +1,36 @@
 #ifndef UNPACK_TREES_H
 #define UNPACK_TREES_H
 
+#include "string-list.h"
+
 #define MAX_UNPACK_TREES 8
 
 struct unpack_trees_options;
+struct exclude_list;
 
 typedef int (*merge_fn_t)(struct cache_entry **src,
 		struct unpack_trees_options *options);
 
-struct unpack_trees_error_msgs {
-	const char *would_overwrite;
-	const char *not_uptodate_file;
-	const char *not_uptodate_dir;
-	const char *would_lose_untracked;
-	const char *bind_overlap;
+enum unpack_trees_error_types {
+	ERROR_WOULD_OVERWRITE = 0,
+	ERROR_NOT_UPTODATE_FILE,
+	ERROR_NOT_UPTODATE_DIR,
+	ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN,
+	ERROR_WOULD_LOSE_UNTRACKED_REMOVED,
+	ERROR_BIND_OVERLAP,
+	ERROR_SPARSE_NOT_UPTODATE_FILE,
+	ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN,
+	ERROR_WOULD_LOSE_ORPHANED_REMOVED,
+	NB_UNPACK_TREES_ERROR_TYPES
 };
 
+/*
+ * Sets the list of user-friendly error messages to be used by the
+ * command "cmd" (either merge or checkout), and show_all_errors to 1.
+ */
+void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
+				  const char *cmd);
+
 struct unpack_trees_options {
 	unsigned int reset,
 		     merge,
@@ -28,12 +43,24 @@
 		     skip_unmerged,
 		     initial_checkout,
 		     diff_index_cached,
-		     gently;
+		     debug_unpack,
+		     skip_sparse_checkout,
+		     gently,
+		     exiting_early,
+		     show_all_errors,
+		     dry_run;
 	const char *prefix;
-	int pos;
+	int cache_bottom;
 	struct dir_struct *dir;
+	struct path_exclude_check *path_exclude_check;
+	struct pathspec *pathspec;
 	merge_fn_t fn;
-	struct unpack_trees_error_msgs msgs;
+	const char *msgs[NB_UNPACK_TREES_ERROR_TYPES];
+	/*
+	 * Store error messages in an array, each case
+	 * corresponding to a error message type
+	 */
+	struct string_list unpack_rejects[NB_UNPACK_TREES_ERROR_TYPES];
 
 	int head_idx;
 	int merge_size;
@@ -44,6 +71,8 @@
 	struct index_state *dst_index;
 	struct index_state *src_index;
 	struct index_state result;
+
+	struct exclude_list *el; /* for internal use */
 };
 
 extern int unpack_trees(unsigned n, struct tree_desc *t,
diff --git a/update-server-info.c b/update-server-info.c
deleted file mode 100644
index 7b38fd8..0000000
--- a/update-server-info.c
+++ /dev/null
@@ -1,28 +0,0 @@
-#include "cache.h"
-#include "exec_cmd.h"
-
-static const char update_server_info_usage[] =
-"git update-server-info [--force]";
-
-int main(int ac, char **av)
-{
-	int i;
-	int force = 0;
-	for (i = 1; i < ac; i++) {
-		if (av[i][0] == '-') {
-			if (!strcmp("--force", av[i]) ||
-			    !strcmp("-f", av[i]))
-				force = 1;
-			else
-				usage(update_server_info_usage);
-		}
-	}
-	if (i != ac)
-		usage(update_server_info_usage);
-
-	git_extract_argv0_path(av[0]);
-
-	setup_git_directory();
-
-	return !!update_server_info(force);
-}
diff --git a/upload-pack.c b/upload-pack.c
index f7d308a..bb08e2e 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -10,8 +10,9 @@
 #include "revision.h"
 #include "list-objects.h"
 #include "run-command.h"
+#include "sigchain.h"
 
-static const char upload_pack_usage[] = "git upload-pack [--strict] [--timeout=nn] <dir>";
+static const char upload_pack_usage[] = "git upload-pack [--strict] [--timeout=<n>] <dir>";
 
 /* bits #0..7 in revision.h, #8..10 in commit.c */
 #define THEY_HAVE	(1u << 11)
@@ -27,17 +28,21 @@
 static unsigned long oldest_have;
 
 static int multi_ack, nr_our_refs;
+static int no_done;
 static int use_thin_pack, use_ofs_delta, use_include_tag;
 static int no_progress, daemon_mode;
 static int shallow_nr;
 static struct object_array have_obj;
 static struct object_array want_obj;
+static struct object_array extra_edge_obj;
 static unsigned int timeout;
 /* 0 for no sideband,
  * otherwise maximum packet size (up to 65520 bytes).
  */
 static int use_sideband;
 static int debug_fd;
+static int advertise_refs;
+static int stateless_rpc;
 
 static void reset_timeout(void)
 {
@@ -79,22 +84,11 @@
 	commit->buffer = NULL;
 }
 
-static void show_object(struct object *obj, const struct name_path *path, const char *component)
+static void show_object(struct object *obj,
+			const struct name_path *path, const char *component,
+			void *cb_data)
 {
-	/* An object with name "foo\n0000000..." can be used to
-	 * confuse downstream git-pack-objects very badly.
-	 */
-	const char *name = path_name(path, component);
-	const char *ep = strchr(name, '\n');
-	if (ep) {
-		fprintf(pack_pipe, "%s %.*s\n", sha1_to_hex(obj->sha1),
-		       (int) (ep - name),
-		       name);
-	}
-	else
-		fprintf(pack_pipe, "%s %s\n",
-				sha1_to_hex(obj->sha1), name);
-	free((char *)name);
+	show_object_with_name(pack_pipe, obj, path, component);
 }
 
 static void show_edge(struct commit *commit)
@@ -102,12 +96,12 @@
 	fprintf(pack_pipe, "-%s\n", sha1_to_hex(commit->object.sha1));
 }
 
-static int do_rev_list(int fd, void *create_full_pack)
+static int do_rev_list(int in, int out, void *user_data)
 {
 	int i;
 	struct rev_info revs;
 
-	pack_pipe = fdopen(fd, "w");
+	pack_pipe = xfdopen(out, "w");
 	init_revisions(&revs, NULL);
 	revs.tag_objects = 1;
 	revs.tree_objects = 1;
@@ -115,26 +109,25 @@
 	if (use_thin_pack)
 		revs.edge_hint = 1;
 
-	if (create_full_pack) {
-		const char *args[] = {"rev-list", "--all", NULL};
-		setup_revisions(2, args, &revs, NULL);
-	} else {
-		for (i = 0; i < want_obj.nr; i++) {
-			struct object *o = want_obj.objects[i].item;
-			/* why??? */
-			o->flags &= ~UNINTERESTING;
-			add_pending_object(&revs, o, NULL);
-		}
-		for (i = 0; i < have_obj.nr; i++) {
-			struct object *o = have_obj.objects[i].item;
-			o->flags |= UNINTERESTING;
-			add_pending_object(&revs, o, NULL);
-		}
-		setup_revisions(0, NULL, &revs, NULL);
+	for (i = 0; i < want_obj.nr; i++) {
+		struct object *o = want_obj.objects[i].item;
+		/* why??? */
+		o->flags &= ~UNINTERESTING;
+		add_pending_object(&revs, o, NULL);
 	}
+	for (i = 0; i < have_obj.nr; i++) {
+		struct object *o = have_obj.objects[i].item;
+		o->flags |= UNINTERESTING;
+		add_pending_object(&revs, o, NULL);
+	}
+	setup_revisions(0, NULL, &revs, NULL);
 	if (prepare_revision_walk(&revs))
 		die("revision walk setup failed");
 	mark_edges_uninteresting(revs.commits, &revs, show_edge);
+	if (use_thin_pack)
+		for (i = 0; i < extra_edge_obj.nr; i++)
+			fprintf(pack_pipe, "-%s\n", sha1_to_hex(
+					extra_edge_obj.objects[i].item->sha1));
 	traverse_commit_list(&revs, show_commit, show_object, NULL);
 	fflush(pack_pipe);
 	fclose(pack_pipe);
@@ -154,14 +147,8 @@
 	const char *argv[10];
 	int arg = 0;
 
-	if (shallow_nr) {
-		rev_list.proc = do_rev_list;
-		rev_list.data = 0;
-		if (start_async(&rev_list))
-			die("git upload-pack: unable to fork git-rev-list");
-		argv[arg++] = "pack-objects";
-	} else {
-		argv[arg++] = "pack-objects";
+	argv[arg++] = "pack-objects";
+	if (!shallow_nr) {
 		argv[arg++] = "--revs";
 		if (create_full_pack)
 			argv[arg++] = "--all";
@@ -179,7 +166,7 @@
 	argv[arg++] = NULL;
 
 	memset(&pack_objects, 0, sizeof(pack_objects));
-	pack_objects.in = shallow_nr ? rev_list.out : -1;
+	pack_objects.in = -1;
 	pack_objects.out = -1;
 	pack_objects.err = -1;
 	pack_objects.git_cmd = 1;
@@ -188,9 +175,15 @@
 	if (start_command(&pack_objects))
 		die("git upload-pack: unable to fork git-pack-objects");
 
-	/* pass on revisions we (don't) want */
-	if (!shallow_nr) {
-		FILE *pipe_fd = fdopen(pack_objects.in, "w");
+	if (shallow_nr) {
+		memset(&rev_list, 0, sizeof(rev_list));
+		rev_list.proc = do_rev_list;
+		rev_list.out = pack_objects.in;
+		if (start_async(&rev_list))
+			die("git upload-pack: unable to fork git-rev-list");
+	}
+	else {
+		FILE *pipe_fd = xfdopen(pack_objects.in, "w");
 		if (!create_full_pack) {
 			int i;
 			for (i = 0; i < want_obj.nr; i++)
@@ -243,6 +236,23 @@
 			}
 			continue;
 		}
+		if (0 <= pe && (pfd[pe].revents & (POLLIN|POLLHUP))) {
+			/* Status ready; we ship that in the side-band
+			 * or dump to the standard error.
+			 */
+			sz = xread(pack_objects.err, progress,
+				  sizeof(progress));
+			if (0 < sz)
+				send_client_data(2, progress, sz);
+			else if (sz == 0) {
+				close(pack_objects.err);
+				pack_objects.err = -1;
+			}
+			else
+				goto fail;
+			/* give priority to status messages */
+			continue;
+		}
 		if (0 <= pu && (pfd[pu].revents & (POLLIN|POLLHUP))) {
 			/* Data ready; we keep the last byte to ourselves
 			 * in case we detect broken rev-list, so that we
@@ -262,7 +272,7 @@
 			sz = xread(pack_objects.out, cp,
 				  sizeof(data) - outsz);
 			if (0 < sz)
-					;
+				;
 			else if (sz == 0) {
 				close(pack_objects.out);
 				pack_objects.out = -1;
@@ -280,21 +290,6 @@
 			if (sz < 0)
 				goto fail;
 		}
-		if (0 <= pe && (pfd[pe].revents & (POLLIN|POLLHUP))) {
-			/* Status ready; we ship that in the side-band
-			 * or dump to the standard error.
-			 */
-			sz = xread(pack_objects.err, progress,
-				  sizeof(progress));
-			if (0 < sz)
-				send_client_data(2, progress, sz);
-			else if (sz == 0) {
-				close(pack_objects.err);
-				pack_objects.err = -1;
-			}
-			else
-				goto fail;
-		}
 	}
 
 	if (finish_command(&pack_objects)) {
@@ -361,7 +356,7 @@
 {
 	struct commit_list *work = NULL;
 
-	insert_by_date(want, &work);
+	commit_list_insert_by_date(want, &work);
 	while (work) {
 		struct commit_list *list = work->next;
 		struct commit *commit = work->item;
@@ -382,7 +377,7 @@
 		for (list = commit->parents; list; list = list->next) {
 			struct commit *parent = list->item;
 			if (!(parent->object.flags & REACHABLE))
-				insert_by_date(parent, &work);
+				commit_list_insert_by_date(parent, &work);
 		}
 	}
 	want->object.flags |= REACHABLE;
@@ -423,36 +418,59 @@
 {
 	static char line[1000];
 	unsigned char sha1[20];
-	char hex[41], last_hex[41];
+	char last_hex[41];
+	int got_common = 0;
+	int got_other = 0;
+	int sent_ready = 0;
 
 	save_commit_buffer = 0;
 
-	for(;;) {
+	for (;;) {
 		int len = packet_read_line(0, line, sizeof(line));
 		reset_timeout();
 
 		if (!len) {
+			if (multi_ack == 2 && got_common
+			    && !got_other && ok_to_give_up()) {
+				sent_ready = 1;
+				packet_write(1, "ACK %s ready\n", last_hex);
+			}
 			if (have_obj.nr == 0 || multi_ack)
 				packet_write(1, "NAK\n");
+
+			if (no_done && sent_ready) {
+				packet_write(1, "ACK %s\n", last_hex);
+				return 0;
+			}
+			if (stateless_rpc)
+				exit(0);
+			got_common = 0;
+			got_other = 0;
 			continue;
 		}
 		strip(line, len);
 		if (!prefixcmp(line, "have ")) {
 			switch (got_sha1(line+5, sha1)) {
 			case -1: /* they have what we do not */
-				if (multi_ack && ok_to_give_up())
-					packet_write(1, "ACK %s continue\n",
-						     sha1_to_hex(sha1));
+				got_other = 1;
+				if (multi_ack && ok_to_give_up()) {
+					const char *hex = sha1_to_hex(sha1);
+					if (multi_ack == 2) {
+						sent_ready = 1;
+						packet_write(1, "ACK %s ready\n", hex);
+					} else
+						packet_write(1, "ACK %s continue\n", hex);
+				}
 				break;
 			default:
-				memcpy(hex, sha1_to_hex(sha1), 41);
-				if (multi_ack) {
-					const char *msg = "ACK %s continue\n";
-					packet_write(1, msg, hex);
-					memcpy(last_hex, hex, 41);
-				}
+				got_common = 1;
+				memcpy(last_hex, sha1_to_hex(sha1), 41);
+				if (multi_ack == 2)
+					packet_write(1, "ACK %s common\n", last_hex);
+				else if (multi_ack)
+					packet_write(1, "ACK %s continue\n", last_hex);
 				else if (have_obj.nr == 1)
-					packet_write(1, "ACK %s\n", hex);
+					packet_write(1, "ACK %s\n", last_hex);
 				break;
 			}
 			continue;
@@ -470,17 +488,104 @@
 	}
 }
 
+static void check_non_tip(void)
+{
+	static const char *argv[] = {
+		"rev-list", "--stdin", NULL,
+	};
+	static struct child_process cmd;
+	struct object *o;
+	char namebuf[42]; /* ^ + SHA-1 + LF */
+	int i;
+
+	/* In the normal in-process case non-tip request can never happen */
+	if (!stateless_rpc)
+		goto error;
+
+	cmd.argv = argv;
+	cmd.git_cmd = 1;
+	cmd.no_stderr = 1;
+	cmd.in = -1;
+	cmd.out = -1;
+
+	if (start_command(&cmd))
+		goto error;
+
+	/*
+	 * If rev-list --stdin encounters an unknown commit, it
+	 * terminates, which will cause SIGPIPE in the write loop
+	 * below.
+	 */
+	sigchain_push(SIGPIPE, SIG_IGN);
+
+	namebuf[0] = '^';
+	namebuf[41] = '\n';
+	for (i = get_max_object_index(); 0 < i; ) {
+		o = get_indexed_object(--i);
+		if (!o)
+			continue;
+		if (!(o->flags & OUR_REF))
+			continue;
+		memcpy(namebuf + 1, sha1_to_hex(o->sha1), 40);
+		if (write_in_full(cmd.in, namebuf, 42) < 0)
+			goto error;
+	}
+	namebuf[40] = '\n';
+	for (i = 0; i < want_obj.nr; i++) {
+		o = want_obj.objects[i].item;
+		if (o->flags & OUR_REF)
+			continue;
+		memcpy(namebuf, sha1_to_hex(o->sha1), 40);
+		if (write_in_full(cmd.in, namebuf, 41) < 0)
+			goto error;
+	}
+	close(cmd.in);
+
+	sigchain_pop(SIGPIPE);
+
+	/*
+	 * The commits out of the rev-list are not ancestors of
+	 * our ref.
+	 */
+	i = read_in_full(cmd.out, namebuf, 1);
+	if (i)
+		goto error;
+	close(cmd.out);
+
+	/*
+	 * rev-list may have died by encountering a bad commit
+	 * in the history, in which case we do want to bail out
+	 * even when it showed no commit.
+	 */
+	if (finish_command(&cmd))
+		goto error;
+
+	/* All the non-tip ones are ancestors of what we advertised */
+	return;
+
+error:
+	/* Pick one of them (we know there at least is one) */
+	for (i = 0; i < want_obj.nr; i++) {
+		o = want_obj.objects[i].item;
+		if (!(o->flags & OUR_REF))
+			die("git upload-pack: not our ref %s",
+			    sha1_to_hex(o->sha1));
+	}
+}
+
 static void receive_needs(void)
 {
-	struct object_array shallows = {0, 0, NULL};
+	struct object_array shallows = OBJECT_ARRAY_INIT;
 	static char line[1000];
 	int len, depth = 0;
+	int has_non_tip = 0;
 
 	shallow_nr = 0;
 	if (debug_fd)
-		write_in_full(debug_fd, "#S\n", 3);
+		write_str_in_full(debug_fd, "#S\n");
 	for (;;) {
 		struct object *o;
+		const char *features;
 		unsigned char sha1_buf[20];
 		len = packet_read_line(0, line, sizeof(line));
 		reset_timeout();
@@ -492,7 +597,6 @@
 		if (!prefixcmp(line, "shallow ")) {
 			unsigned char sha1[20];
 			struct object *object;
-			use_thin_pack = 0;
 			if (get_sha1(line + 8, sha1))
 				die("invalid shallow line: %s", line);
 			object = parse_object(sha1);
@@ -504,7 +608,6 @@
 		}
 		if (!prefixcmp(line, "deepen ")) {
 			char *end;
-			use_thin_pack = 0;
 			depth = strtol(line + 7, &end, 0);
 			if (end == line + 7 || depth <= 0)
 				die("Invalid deepen: %s", line);
@@ -514,39 +617,51 @@
 		    get_sha1_hex(line+5, sha1_buf))
 			die("git upload-pack: protocol error, "
 			    "expected to get sha, not '%s'", line);
-		if (strstr(line+45, "multi_ack"))
+
+		features = line + 45;
+
+		if (parse_feature_request(features, "multi_ack_detailed"))
+			multi_ack = 2;
+		else if (parse_feature_request(features, "multi_ack"))
 			multi_ack = 1;
-		if (strstr(line+45, "thin-pack"))
+		if (parse_feature_request(features, "no-done"))
+			no_done = 1;
+		if (parse_feature_request(features, "thin-pack"))
 			use_thin_pack = 1;
-		if (strstr(line+45, "ofs-delta"))
+		if (parse_feature_request(features, "ofs-delta"))
 			use_ofs_delta = 1;
-		if (strstr(line+45, "side-band-64k"))
+		if (parse_feature_request(features, "side-band-64k"))
 			use_sideband = LARGE_PACKET_MAX;
-		else if (strstr(line+45, "side-band"))
+		else if (parse_feature_request(features, "side-band"))
 			use_sideband = DEFAULT_PACKET_MAX;
-		if (strstr(line+45, "no-progress"))
+		if (parse_feature_request(features, "no-progress"))
 			no_progress = 1;
-		if (strstr(line+45, "include-tag"))
+		if (parse_feature_request(features, "include-tag"))
 			use_include_tag = 1;
 
-		/* We have sent all our refs already, and the other end
-		 * should have chosen out of them; otherwise they are
-		 * asking for nonsense.
-		 *
-		 * Hmph.  We may later want to allow "want" line that
-		 * asks for something like "master~10" (symbolic)...
-		 * would it make sense?  I don't know.
-		 */
 		o = lookup_object(sha1_buf);
-		if (!o || !(o->flags & OUR_REF))
-			die("git upload-pack: not our ref %s", line+5);
+		if (!o)
+			die("git upload-pack: not our ref %s",
+			    sha1_to_hex(sha1_buf));
 		if (!(o->flags & WANTED)) {
 			o->flags |= WANTED;
+			if (!(o->flags & OUR_REF))
+				has_non_tip = 1;
 			add_object_array(o, NULL, &want_obj);
 		}
 	}
 	if (debug_fd)
-		write_in_full(debug_fd, "#E\n", 3);
+		write_str_in_full(debug_fd, "#E\n");
+
+	/*
+	 * We have sent all our refs already, and the other end
+	 * should have chosen out of them. When we are operating
+	 * in the stateless RPC mode, however, their choice may
+	 * have been based on the set of older refs advertised
+	 * by another process that handled the initial request.
+	 */
+	if (has_non_tip)
+		check_non_tip();
 
 	if (!use_sideband && daemon_mode)
 		no_progress = 1;
@@ -587,6 +702,7 @@
 							NULL, &want_obj);
 					parents = parents->next;
 				}
+				add_object_array(object, NULL, &extra_edge_obj);
 			}
 			/* make sure commit traversal conforms to client */
 			register_shallow(object->sha1);
@@ -607,36 +723,61 @@
 {
 	static const char *capabilities = "multi_ack thin-pack side-band"
 		" side-band-64k ofs-delta shallow no-progress"
-		" include-tag";
-	struct object *o = parse_object(sha1);
+		" include-tag multi_ack_detailed";
+	struct object *o = lookup_unknown_object(sha1);
+	const char *refname_nons = strip_namespace(refname);
 
-	if (!o)
-		die("git upload-pack: cannot find object %s:", sha1_to_hex(sha1));
+	if (o->type == OBJ_NONE) {
+		o->type = sha1_object_info(sha1, NULL);
+		if (o->type < 0)
+		    die("git upload-pack: cannot find object %s:", sha1_to_hex(sha1));
+	}
 
 	if (capabilities)
-		packet_write(1, "%s %s%c%s\n", sha1_to_hex(sha1), refname,
-			0, capabilities);
+		packet_write(1, "%s %s%c%s%s\n", sha1_to_hex(sha1), refname_nons,
+			     0, capabilities,
+			     stateless_rpc ? " no-done" : "");
 	else
-		packet_write(1, "%s %s\n", sha1_to_hex(sha1), refname);
+		packet_write(1, "%s %s\n", sha1_to_hex(sha1), refname_nons);
 	capabilities = NULL;
 	if (!(o->flags & OUR_REF)) {
 		o->flags |= OUR_REF;
 		nr_our_refs++;
 	}
 	if (o->type == OBJ_TAG) {
-		o = deref_tag(o, refname, 0);
+		o = deref_tag_noverify(o);
 		if (o)
-			packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname);
+			packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname_nons);
+	}
+	return 0;
+}
+
+static int mark_our_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+{
+	struct object *o = parse_object(sha1);
+	if (!o)
+		die("git upload-pack: cannot find object %s:", sha1_to_hex(sha1));
+	if (!(o->flags & OUR_REF)) {
+		o->flags |= OUR_REF;
+		nr_our_refs++;
 	}
 	return 0;
 }
 
 static void upload_pack(void)
 {
-	reset_timeout();
-	head_ref(send_ref, NULL);
-	for_each_ref(send_ref, NULL);
-	packet_flush(1);
+	if (advertise_refs || !stateless_rpc) {
+		reset_timeout();
+		head_ref_namespaced(send_ref, NULL);
+		for_each_namespaced_ref(send_ref, NULL);
+		packet_flush(1);
+	} else {
+		head_ref_namespaced(mark_our_ref, NULL);
+		for_each_namespaced_ref(mark_our_ref, NULL);
+	}
+	if (advertise_refs)
+		return;
+
 	receive_needs();
 	if (want_obj.nr) {
 		get_common_commits();
@@ -650,13 +791,25 @@
 	int i;
 	int strict = 0;
 
+	git_setup_gettext();
+
+	packet_trace_identity("upload-pack");
 	git_extract_argv0_path(argv[0]);
+	read_replace_refs = 0;
 
 	for (i = 1; i < argc; i++) {
 		char *arg = argv[i];
 
 		if (arg[0] != '-')
 			break;
+		if (!strcmp(arg, "--advertise-refs")) {
+			advertise_refs = 1;
+			continue;
+		}
+		if (!strcmp(arg, "--stateless-rpc")) {
+			stateless_rpc = 1;
+			continue;
+		}
 		if (!strcmp(arg, "--strict")) {
 			strict = 1;
 			continue;
diff --git a/url.c b/url.c
new file mode 100644
index 0000000..335d97d
--- /dev/null
+++ b/url.c
@@ -0,0 +1,132 @@
+#include "cache.h"
+#include "url.h"
+
+int is_urlschemechar(int first_flag, int ch)
+{
+	/*
+	 * The set of valid URL schemes, as per STD66 (RFC3986) is
+	 * '[A-Za-z][A-Za-z0-9+.-]*'. But use sightly looser check
+	 * of '[A-Za-z0-9][A-Za-z0-9+.-]*' because earlier version
+	 * of check used '[A-Za-z0-9]+' so not to break any remote
+	 * helpers.
+	 */
+	int alphanumeric, special;
+	alphanumeric = ch > 0 && isalnum(ch);
+	special = ch == '+' || ch == '-' || ch == '.';
+	return alphanumeric || (!first_flag && special);
+}
+
+int is_url(const char *url)
+{
+	/* Is "scheme" part reasonable? */
+	if (!url || !is_urlschemechar(1, *url++))
+		return 0;
+	while (*url && *url != ':') {
+		if (!is_urlschemechar(0, *url++))
+			return 0;
+	}
+	/* We've seen "scheme"; we want colon-slash-slash */
+	return (url[0] == ':' && url[1] == '/' && url[2] == '/');
+}
+
+static int url_decode_char(const char *q)
+{
+	int i;
+	unsigned char val = 0;
+	for (i = 0; i < 2; i++) {
+		unsigned char c = *q++;
+		val <<= 4;
+		if (c >= '0' && c <= '9')
+			val += c - '0';
+		else if (c >= 'a' && c <= 'f')
+			val += c - 'a' + 10;
+		else if (c >= 'A' && c <= 'F')
+			val += c - 'A' + 10;
+		else
+			return -1;
+	}
+	return val;
+}
+
+static char *url_decode_internal(const char **query, int len,
+				 const char *stop_at, struct strbuf *out,
+				 int decode_plus)
+{
+	const char *q = *query;
+
+	while (len) {
+		unsigned char c = *q;
+
+		if (!c)
+			break;
+		if (stop_at && strchr(stop_at, c)) {
+			q++;
+			len--;
+			break;
+		}
+
+		if (c == '%') {
+			int val = url_decode_char(q + 1);
+			if (0 <= val) {
+				strbuf_addch(out, val);
+				q += 3;
+				len -= 3;
+				continue;
+			}
+		}
+
+		if (decode_plus && c == '+')
+			strbuf_addch(out, ' ');
+		else
+			strbuf_addch(out, c);
+		q++;
+		len--;
+	}
+	*query = q;
+	return strbuf_detach(out, NULL);
+}
+
+char *url_decode(const char *url)
+{
+	return url_decode_mem(url, strlen(url));
+}
+
+char *url_decode_mem(const char *url, int len)
+{
+	struct strbuf out = STRBUF_INIT;
+	const char *colon = memchr(url, ':', len);
+
+	/* Skip protocol part if present */
+	if (colon && url < colon) {
+		strbuf_add(&out, url, colon - url);
+		len -= colon - url;
+		url = colon;
+	}
+	return url_decode_internal(&url, len, NULL, &out, 0);
+}
+
+char *url_decode_parameter_name(const char **query)
+{
+	struct strbuf out = STRBUF_INIT;
+	return url_decode_internal(query, -1, "&=", &out, 1);
+}
+
+char *url_decode_parameter_value(const char **query)
+{
+	struct strbuf out = STRBUF_INIT;
+	return url_decode_internal(query, -1, "&", &out, 1);
+}
+
+void end_url_with_slash(struct strbuf *buf, const char *url)
+{
+	strbuf_addstr(buf, url);
+	if (buf->len && buf->buf[buf->len - 1] != '/')
+		strbuf_addstr(buf, "/");
+}
+
+void str_end_url_with_slash(const char *url, char **dest) {
+	struct strbuf buf = STRBUF_INIT;
+	end_url_with_slash(&buf, url);
+	free(*dest);
+	*dest = strbuf_detach(&buf, NULL);
+}
diff --git a/url.h b/url.h
new file mode 100644
index 0000000..abdaf6f
--- /dev/null
+++ b/url.h
@@ -0,0 +1,14 @@
+#ifndef URL_H
+#define URL_H
+
+extern int is_url(const char *url);
+extern int is_urlschemechar(int first_flag, int ch);
+extern char *url_decode(const char *url);
+extern char *url_decode_mem(const char *url, int len);
+extern char *url_decode_parameter_name(const char **query);
+extern char *url_decode_parameter_value(const char **query);
+
+extern void end_url_with_slash(struct strbuf *buf, const char *url);
+extern void str_end_url_with_slash(const char *url, char **dest);
+
+#endif /* URL_H */
diff --git a/usage.c b/usage.c
index b6aea45..a2a6678 100644
--- a/usage.c
+++ b/usage.c
@@ -4,54 +4,81 @@
  * Copyright (C) Linus Torvalds, 2005
  */
 #include "git-compat-util.h"
+#include "cache.h"
 
-static void report(const char *prefix, const char *err, va_list params)
+void vreportf(const char *prefix, const char *err, va_list params)
 {
-	char msg[1024];
+	char msg[4096];
 	vsnprintf(msg, sizeof(msg), err, params);
 	fprintf(stderr, "%s%s\n", prefix, msg);
 }
 
-static NORETURN void usage_builtin(const char *err)
+void vwritef(int fd, const char *prefix, const char *err, va_list params)
 {
-	fprintf(stderr, "usage: %s\n", err);
+	char msg[4096];
+	int len = vsnprintf(msg, sizeof(msg), err, params);
+	if (len > sizeof(msg))
+		len = sizeof(msg);
+
+	write_in_full(fd, prefix, strlen(prefix));
+	write_in_full(fd, msg, len);
+	write_in_full(fd, "\n", 1);
+}
+
+static NORETURN void usage_builtin(const char *err, va_list params)
+{
+	vreportf("usage: ", err, params);
 	exit(129);
 }
 
 static NORETURN void die_builtin(const char *err, va_list params)
 {
-	report("fatal: ", err, params);
+	vreportf("fatal: ", err, params);
 	exit(128);
 }
 
 static void error_builtin(const char *err, va_list params)
 {
-	report("error: ", err, params);
+	vreportf("error: ", err, params);
 }
 
 static void warn_builtin(const char *warn, va_list params)
 {
-	report("warning: ", warn, params);
+	vreportf("warning: ", warn, params);
 }
 
 /* If we are in a dlopen()ed .so write to a global variable would segfault
  * (ugh), so keep things static. */
-static void (*usage_routine)(const char *err) NORETURN = usage_builtin;
-static void (*die_routine)(const char *err, va_list params) NORETURN = die_builtin;
+static NORETURN_PTR void (*usage_routine)(const char *err, va_list params) = usage_builtin;
+static NORETURN_PTR void (*die_routine)(const char *err, va_list params) = die_builtin;
 static void (*error_routine)(const char *err, va_list params) = error_builtin;
 static void (*warn_routine)(const char *err, va_list params) = warn_builtin;
 
-void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN)
+void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list params))
 {
 	die_routine = routine;
 }
 
-void usage(const char *err)
+void set_error_routine(void (*routine)(const char *err, va_list params))
 {
-	usage_routine(err);
+	error_routine = routine;
 }
 
-void die(const char *err, ...)
+void NORETURN usagef(const char *err, ...)
+{
+	va_list params;
+
+	va_start(params, err);
+	usage_routine(err, params);
+	va_end(params);
+}
+
+void NORETURN usage(const char *err)
+{
+	usagef("%s", err);
+}
+
+void NORETURN die(const char *err, ...)
 {
 	va_list params;
 
@@ -60,7 +87,7 @@
 	va_end(params);
 }
 
-void die_errno(const char *fmt, ...)
+void NORETURN die_errno(const char *fmt, ...)
 {
 	va_list params;
 	char fmt_with_err[1024];
diff --git a/userdiff.c b/userdiff.c
index 57529ae..1e7184f 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -1,3 +1,4 @@
+#include "cache.h"
 #include "userdiff.h"
 #include "cache.h"
 #include "attr.h"
@@ -7,10 +8,27 @@
 static int drivers_alloc;
 
 #define PATTERNS(name, pattern, word_regex)			\
-	{ name, NULL, -1, { pattern, REG_EXTENDED }, word_regex }
+	{ name, NULL, -1, { pattern, REG_EXTENDED },		\
+	  word_regex "|[^[:space:]]|[\xc0-\xff][\x80-\xbf]+" }
+#define IPATTERN(name, pattern, word_regex)			\
+	{ name, NULL, -1, { pattern, REG_EXTENDED | REG_ICASE }, \
+	  word_regex "|[^[:space:]]|[\xc0-\xff][\x80-\xbf]+" }
 static struct userdiff_driver builtin_drivers[] = {
+IPATTERN("fortran",
+	 "!^([C*]|[ \t]*!)\n"
+	 "!^[ \t]*MODULE[ \t]+PROCEDURE[ \t]\n"
+	 "^[ \t]*((END[ \t]+)?(PROGRAM|MODULE|BLOCK[ \t]+DATA"
+		"|([^'\" \t]+[ \t]+)*(SUBROUTINE|FUNCTION))[ \t]+[A-Z].*)$",
+	 /* -- */
+	 "[a-zA-Z][a-zA-Z0-9_]*"
+	 "|\\.([Ee][Qq]|[Nn][Ee]|[Gg][TtEe]|[Ll][TtEe]|[Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee]|[Aa][Nn][Dd]|[Oo][Rr]|[Nn]?[Ee][Qq][Vv]|[Nn][Oo][Tt])\\."
+	 /* numbers and format statements like 2E14.4, or ES12.6, 9X.
+	  * Don't worry about format statements without leading digits since
+	  * they would have been matched above as a variable anyway. */
+	 "|[-+]?[0-9.]+([AaIiDdEeFfLlTtXx][Ss]?[-+]?[0-9.]*)?(_[a-zA-Z0-9][a-zA-Z0-9_]*)?"
+	 "|//|\\*\\*|::|[/<>=]="),
 PATTERNS("html", "^[ \t]*(<[Hh][1-6][ \t].*>.*)$",
-	 "[^<>= \t]+|[^[:space:]]|[\x80-\xff]+"),
+	 "[^<>= \t]+"),
 PATTERNS("java",
 	 "!^[ \t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\n"
 	 "^[ \t]*(([A-Za-z_][A-Za-z_0-9]*[ \t]+)+[A-Za-z_][A-Za-z_0-9]*[ \t]*\\([^;]*)$",
@@ -18,8 +36,10 @@
 	 "[a-zA-Z_][a-zA-Z0-9_]*"
 	 "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
 	 "|[-+*/<>%&^|=!]="
-	 "|--|\\+\\+|<<=?|>>>?=?|&&|\\|\\|"
-	 "|[^[:space:]]|[\x80-\xff]+"),
+	 "|--|\\+\\+|<<=?|>>>?=?|&&|\\|\\|"),
+PATTERNS("matlab",
+	 "^[[:space:]]*((classdef|function)[[:space:]].*)$|^%%[[:space:]].*$",
+	 "[a-zA-Z_][a-zA-Z0-9_]*|[-+0-9.e]+|[=~<>]=|\\.[*/\\^']|\\|\\||&&"),
 PATTERNS("objc",
 	 /* Negate C statements that can look like functions */
 	 "!^[ \t]*(do|for|if|else|return|switch|while)\n"
@@ -32,56 +52,98 @@
 	 /* -- */
 	 "[a-zA-Z_][a-zA-Z0-9_]*"
 	 "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
-	 "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"
-	 "|[^[:space:]]|[\x80-\xff]+"),
+	 "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"),
 PATTERNS("pascal",
-	 "^((procedure|function|constructor|destructor|interface|"
+	 "^(((class[ \t]+)?(procedure|function)|constructor|destructor|interface|"
 		"implementation|initialization|finalization)[ \t]*.*)$"
 	 "\n"
 	 "^(.*=[ \t]*(class|record).*)$",
 	 /* -- */
 	 "[a-zA-Z_][a-zA-Z0-9_]*"
 	 "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+"
-	 "|<>|<=|>=|:=|\\.\\."
-	 "|[^[:space:]]|[\x80-\xff]+"),
-PATTERNS("php", "^[\t ]*((function|class).*)",
+	 "|<>|<=|>=|:=|\\.\\."),
+PATTERNS("perl",
+	 "^package .*\n"
+	 "^sub [[:alnum:]_':]+[ \t]*"
+		"(\\([^)]*\\)[ \t]*)?" /* prototype */
+		/*
+		 * Attributes.  A regex can't count nested parentheses,
+		 * so just slurp up whatever we see, taking care not
+		 * to accept lines like "sub foo; # defined elsewhere".
+		 *
+		 * An attribute could contain a semicolon, but at that
+		 * point it seems reasonable enough to give up.
+		 */
+		"(:[^;#]*)?"
+		"(\\{[ \t]*)?" /* brace can come here or on the next line */
+		"(#.*)?$\n" /* comment */
+	 "^(BEGIN|END|INIT|CHECK|UNITCHECK|AUTOLOAD|DESTROY)[ \t]*"
+		"(\\{[ \t]*)?" /* brace can come here or on the next line */
+		"(#.*)?$\n"
+	 "^=head[0-9] .*",	/* POD */
+	 /* -- */
+	 "[[:alpha:]_'][[:alnum:]_']*"
+	 "|0[xb]?[0-9a-fA-F_]*"
+	 /* taking care not to interpret 3..5 as (3.)(.5) */
+	 "|[0-9a-fA-F_]+(\\.[0-9a-fA-F_]+)?([eE][-+]?[0-9_]+)?"
+	 "|=>|-[rwxoRWXOezsfdlpSugkbctTBMAC>]|~~|::"
+	 "|&&=|\\|\\|=|//=|\\*\\*="
+	 "|&&|\\|\\||//|\\+\\+|--|\\*\\*|\\.\\.\\.?"
+	 "|[-+*/%.^&<>=!|]="
+	 "|=~|!~"
+	 "|<<|<>|<=>|>>"),
+PATTERNS("php",
+	 "^[\t ]*(((public|protected|private|static)[\t ]+)*function.*)$\n"
+	 "^[\t ]*(class.*)$",
 	 /* -- */
 	 "[a-zA-Z_][a-zA-Z0-9_]*"
 	 "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+"
-	 "|[-+*/<>%&^|=!.]=|--|\\+\\+|<<=?|>>=?|===|&&|\\|\\||::|->"
-	 "|[^[:space:]]|[\x80-\xff]+"),
+	 "|[-+*/<>%&^|=!.]=|--|\\+\\+|<<=?|>>=?|===|&&|\\|\\||::|->"),
 PATTERNS("python", "^[ \t]*((class|def)[ \t].*)$",
 	 /* -- */
 	 "[a-zA-Z_][a-zA-Z0-9_]*"
 	 "|[-+0-9.e]+[jJlL]?|0[xX]?[0-9a-fA-F]+[lL]?"
-	 "|[-+*/<>%&^|=!]=|//=?|<<=?|>>=?|\\*\\*=?"
-	 "|[^[:space:]|[\x80-\xff]+"),
+	 "|[-+*/<>%&^|=!]=|//=?|<<=?|>>=?|\\*\\*=?"),
 	 /* -- */
 PATTERNS("ruby", "^[ \t]*((class|module|def)[ \t].*)$",
 	 /* -- */
 	 "(@|@@|\\$)?[a-zA-Z_][a-zA-Z0-9_]*"
 	 "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+|\\?(\\\\C-)?(\\\\M-)?."
-	 "|//=?|[-+*/<>%&^|=!]=|<<=?|>>=?|===|\\.{1,3}|::|[!=]~"
-	 "|[^[:space:]|[\x80-\xff]+"),
+	 "|//=?|[-+*/<>%&^|=!]=|<<=?|>>=?|===|\\.{1,3}|::|[!=]~"),
 PATTERNS("bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$",
 	 "[={}\"]|[^={}\" \t]+"),
 PATTERNS("tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$",
-	 "\\\\[a-zA-Z@]+|\\\\.|[a-zA-Z0-9\x80-\xff]+|[^[:space:]]"),
+	 "\\\\[a-zA-Z@]+|\\\\.|[a-zA-Z0-9\x80-\xff]+"),
 PATTERNS("cpp",
 	 /* Jump targets or access declarations */
 	 "!^[ \t]*[A-Za-z_][A-Za-z_0-9]*:.*$\n"
 	 /* C/++ functions/methods at top level */
-	 "^([A-Za-z_][A-Za-z_0-9]*([ \t]+[A-Za-z_][A-Za-z_0-9]*([ \t]*::[ \t]*[^[:space:]]+)?){1,}[ \t]*\\([^;]*)$\n"
+	 "^([A-Za-z_][A-Za-z_0-9]*([ \t*]+[A-Za-z_][A-Za-z_0-9]*([ \t]*::[ \t]*[^[:space:]]+)?){1,}[ \t]*\\([^;]*)$\n"
 	 /* compound type at top level */
 	 "^((struct|class|enum)[^;]*)$",
 	 /* -- */
 	 "[a-zA-Z_][a-zA-Z0-9_]*"
 	 "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
-	 "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"
-	 "|[^[:space:]]|[\x80-\xff]+"),
+	 "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"),
+PATTERNS("csharp",
+	 /* Keywords */
+	 "!^[ \t]*(do|while|for|if|else|instanceof|new|return|switch|case|throw|catch|using)\n"
+	 /* Methods and constructors */
+	 "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[<>@._[:alnum:]]+[ \t]*\\(.*\\))[ \t]*$\n"
+	 /* Properties */
+	 "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[@._[:alnum:]]+)[ \t]*$\n"
+	 /* Type definitions */
+	 "^[ \t]*(((static|public|internal|private|protected|new|unsafe|sealed|abstract|partial)[ \t]+)*(class|enum|interface|struct)[ \t]+.*)$\n"
+	 /* Namespace */
+	 "^[ \t]*(namespace[ \t]+.*)$",
+	 /* -- */
+	 "[a-zA-Z_][a-zA-Z0-9_]*"
+	 "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
+	 "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"),
 { "default", NULL, -1, { NULL, 0 } },
 };
 #undef PATTERNS
+#undef IPATTERN
 
 static struct userdiff_driver driver_true = {
 	"diff=true",
@@ -148,14 +210,7 @@
 	if (git_config_string(&f->pattern, k, v) < 0)
 		return -1;
 	f->cflags = cflags;
-	return 1;
-}
-
-static int parse_string(const char **d, const char *k, const char *v)
-{
-	if (git_config_string(d, k, v) < 0)
-		return -1;
-	return 1;
+	return 0;
 }
 
 static int parse_tristate(int *b, const char *k, const char *v)
@@ -164,7 +219,13 @@
 		*b = -1;
 	else
 		*b = git_config_bool(k, v);
-	return 1;
+	return 0;
+}
+
+static int parse_bool(int *b, const char *k, const char *v)
+{
+	*b = git_config_bool(k, v);
+	return 0;
 }
 
 int userdiff_config(const char *k, const char *v)
@@ -178,11 +239,13 @@
 	if ((drv = parse_driver(k, v, "binary")))
 		return parse_tristate(&drv->binary, k, v);
 	if ((drv = parse_driver(k, v, "command")))
-		return parse_string(&drv->external, k, v);
+		return git_config_string(&drv->external, k, v);
 	if ((drv = parse_driver(k, v, "textconv")))
-		return parse_string(&drv->textconv, k, v);
+		return git_config_string(&drv->textconv, k, v);
+	if ((drv = parse_driver(k, v, "cachetextconv")))
+		return parse_bool(&drv->textconv_want_cache, k, v);
 	if ((drv = parse_driver(k, v, "wordregex")))
-		return parse_string(&drv->word_regex, k, v);
+		return git_config_string(&drv->word_regex, k, v);
 
 	return 0;
 }
@@ -198,12 +261,12 @@
 	struct git_attr_check check;
 
 	if (!attr)
-		attr = git_attr("diff", 4);
+		attr = git_attr("diff");
 	check.attr = attr;
 
 	if (!path)
 		return NULL;
-	if (git_checkattr(path, 1, &check))
+	if (git_check_attr(path, 1, &check))
 		return NULL;
 
 	if (ATTR_TRUE(check.value))
@@ -214,3 +277,20 @@
 		return NULL;
 	return userdiff_find_by_name(check.value);
 }
+
+struct userdiff_driver *userdiff_get_textconv(struct userdiff_driver *driver)
+{
+	if (!driver->textconv)
+		return NULL;
+
+	if (driver->textconv_want_cache && !driver->textconv_cache) {
+		struct notes_cache *c = xmalloc(sizeof(*c));
+		struct strbuf name = STRBUF_INIT;
+
+		strbuf_addf(&name, "textconv/%s", driver->name);
+		notes_cache_init(c, name.buf, driver->textconv);
+		driver->textconv_cache = c;
+	}
+
+	return driver;
+}
diff --git a/userdiff.h b/userdiff.h
index c315159..4a7e78f 100644
--- a/userdiff.h
+++ b/userdiff.h
@@ -1,6 +1,8 @@
 #ifndef USERDIFF_H
 #define USERDIFF_H
 
+#include "notes-cache.h"
+
 struct userdiff_funcname {
 	const char *pattern;
 	int cflags;
@@ -13,10 +15,14 @@
 	struct userdiff_funcname funcname;
 	const char *word_regex;
 	const char *textconv;
+	struct notes_cache *textconv_cache;
+	int textconv_want_cache;
 };
 
 int userdiff_config(const char *k, const char *v);
 struct userdiff_driver *userdiff_find_by_name(const char *name);
 struct userdiff_driver *userdiff_find_by_path(const char *path);
 
+struct userdiff_driver *userdiff_get_textconv(struct userdiff_driver *driver);
+
 #endif /* USERDIFF */
diff --git a/utf8.c b/utf8.c
index db706ac..a544f15 100644
--- a/utf8.c
+++ b/utf8.c
@@ -1,4 +1,5 @@
 #include "git-compat-util.h"
+#include "strbuf.h"
 #include "utf8.h"
 
 /* This code is originally from http://www.cl.cam.ac.uk/~mgk25/ucs/ */
@@ -162,7 +163,7 @@
  * If the string was not a valid UTF-8, *start pointer is set to NULL
  * and the return value is undefined.
  */
-ucs_char_t pick_one_utf8_char(const char **start, size_t *remainder_p)
+static ucs_char_t pick_one_utf8_char(const char **start, size_t *remainder_p)
 {
 	unsigned char *s = (unsigned char *)*start;
 	ucs_char_t ch;
@@ -279,14 +280,41 @@
 	return 1;
 }
 
-static void print_spaces(int count)
+static void strbuf_addchars(struct strbuf *sb, int c, size_t n)
 {
-	static const char s[] = "                    ";
-	while (count >= sizeof(s)) {
-		fwrite(s, sizeof(s) - 1, 1, stdout);
-		count -= sizeof(s) - 1;
+	strbuf_grow(sb, n);
+	memset(sb->buf + sb->len, c, n);
+	strbuf_setlen(sb, sb->len + n);
+}
+
+static void strbuf_add_indented_text(struct strbuf *buf, const char *text,
+				     int indent, int indent2)
+{
+	if (indent < 0)
+		indent = 0;
+	while (*text) {
+		const char *eol = strchrnul(text, '\n');
+		if (*eol == '\n')
+			eol++;
+		strbuf_addchars(buf, ' ', indent);
+		strbuf_add(buf, text, eol - text);
+		text = eol;
+		indent = indent2;
 	}
-	fwrite(s, count, 1, stdout);
+}
+
+static size_t display_mode_esc_sequence_len(const char *s)
+{
+	const char *p = s;
+	if (*p++ != '\033')
+		return 0;
+	if (*p++ != '[')
+		return 0;
+	while (isdigit(*p) || *p == ';')
+		p++;
+	if (*p++ != 'm')
+		return 0;
+	return p - s;
 }
 
 /*
@@ -295,51 +323,97 @@
  * If indent is negative, assume that already -indent columns have been
  * consumed (and no extra indent is necessary for the first line).
  */
-int print_wrapped_text(const char *text, int indent, int indent2, int width)
+int strbuf_add_wrapped_text(struct strbuf *buf,
+		const char *text, int indent1, int indent2, int width)
 {
-	int w = indent, assume_utf8 = is_utf8(text);
-	const char *bol = text, *space = NULL;
+	int indent, w, assume_utf8 = 1;
+	const char *bol, *space, *start = text;
+	size_t orig_len = buf->len;
 
+	if (width <= 0) {
+		strbuf_add_indented_text(buf, text, indent1, indent2);
+		return 1;
+	}
+
+retry:
+	bol = text;
+	w = indent = indent1;
+	space = NULL;
 	if (indent < 0) {
 		w = -indent;
 		space = text;
 	}
 
 	for (;;) {
-		char c = *text;
+		char c;
+		size_t skip;
+
+		while ((skip = display_mode_esc_sequence_len(text)))
+			text += skip;
+
+		c = *text;
 		if (!c || isspace(c)) {
 			if (w < width || !space) {
 				const char *start = bol;
+				if (!c && text == start)
+					return w;
 				if (space)
 					start = space;
 				else
-					print_spaces(indent);
-				fwrite(start, text - start, 1, stdout);
+					strbuf_addchars(buf, ' ', indent);
+				strbuf_add(buf, start, text - start);
 				if (!c)
 					return w;
-				else if (c == '\t')
-					w |= 0x07;
 				space = text;
+				if (c == '\t')
+					w |= 0x07;
+				else if (c == '\n') {
+					space++;
+					if (*space == '\n') {
+						strbuf_addch(buf, '\n');
+						goto new_line;
+					}
+					else if (!isalnum(*space))
+						goto new_line;
+					else
+						strbuf_addch(buf, ' ');
+				}
 				w++;
 				text++;
 			}
 			else {
-				putchar('\n');
+new_line:
+				strbuf_addch(buf, '\n');
 				text = bol = space + isspace(*space);
 				space = NULL;
 				w = indent = indent2;
 			}
 			continue;
 		}
-		if (assume_utf8)
+		if (assume_utf8) {
 			w += utf8_width(&text, NULL);
-		else {
+			if (!text) {
+				assume_utf8 = 0;
+				text = start;
+				strbuf_setlen(buf, orig_len);
+				goto retry;
+			}
+		} else {
 			w++;
 			text++;
 		}
 	}
 }
 
+int strbuf_add_wrapped_bytes(struct strbuf *buf, const char *data, int len,
+			     int indent, int indent2, int width)
+{
+	char *tmp = xstrndup(data, len);
+	int r = strbuf_add_wrapped_text(buf, tmp, indent, indent2, width);
+	free(tmp);
+	return r;
+}
+
 int is_encoding_utf8(const char *name)
 {
 	if (!name)
@@ -359,19 +433,12 @@
 #else
 	typedef char * iconv_ibp;
 #endif
-char *reencode_string(const char *in, const char *out_encoding, const char *in_encoding)
+char *reencode_string_iconv(const char *in, size_t insz, iconv_t conv)
 {
-	iconv_t conv;
-	size_t insz, outsz, outalloc;
+	size_t outsz, outalloc;
 	char *out, *outpos;
 	iconv_ibp cp;
 
-	if (!in_encoding)
-		return NULL;
-	conv = iconv_open(out_encoding, in_encoding);
-	if (conv == (iconv_t) -1)
-		return NULL;
-	insz = strlen(in);
 	outsz = insz;
 	outalloc = outsz + 1; /* for terminating NUL */
 	out = xmalloc(outalloc);
@@ -385,7 +452,6 @@
 			size_t sofar;
 			if (errno != E2BIG) {
 				free(out);
-				iconv_close(conv);
 				return NULL;
 			}
 			/* insz has remaining number of bytes.
@@ -404,6 +470,20 @@
 			break;
 		}
 	}
+	return out;
+}
+
+char *reencode_string(const char *in, const char *out_encoding, const char *in_encoding)
+{
+	iconv_t conv;
+	char *out;
+
+	if (!in_encoding)
+		return NULL;
+	conv = iconv_open(out_encoding, in_encoding);
+	if (conv == (iconv_t) -1)
+		return NULL;
+	out = reencode_string_iconv(in, strlen(in), conv);
 	iconv_close(conv);
 	return out;
 }
diff --git a/utf8.h b/utf8.h
index 2f1b14f..3c0ae76 100644
--- a/utf8.h
+++ b/utf8.h
@@ -3,15 +3,18 @@
 
 typedef unsigned int ucs_char_t;  /* assuming 32bit int */
 
-ucs_char_t pick_one_utf8_char(const char **start, size_t *remainder_p);
 int utf8_width(const char **start, size_t *remainder_p);
 int utf8_strwidth(const char *string);
 int is_utf8(const char *text);
 int is_encoding_utf8(const char *name);
 
-int print_wrapped_text(const char *text, int indent, int indent2, int len);
+int strbuf_add_wrapped_text(struct strbuf *buf,
+		const char *text, int indent, int indent2, int width);
+int strbuf_add_wrapped_bytes(struct strbuf *buf, const char *data, int len,
+			     int indent, int indent2, int width);
 
 #ifndef NO_ICONV
+char *reencode_string_iconv(const char *in, size_t insz, iconv_t conv);
 char *reencode_string(const char *in, const char *out_encoding, const char *in_encoding);
 #else
 #define reencode_string(a,b,c) NULL
diff --git a/var.c b/var.c
deleted file mode 100644
index 7362ed8..0000000
--- a/var.c
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * GIT - The information manager from hell
- *
- * Copyright (C) Eric Biederman, 2005
- */
-#include "cache.h"
-#include "exec_cmd.h"
-
-static const char var_usage[] = "git var [-l | <variable>]";
-
-struct git_var {
-	const char *name;
-	const char *(*read)(int);
-};
-static struct git_var git_vars[] = {
-	{ "GIT_COMMITTER_IDENT", git_committer_info },
-	{ "GIT_AUTHOR_IDENT",   git_author_info },
-	{ "", NULL },
-};
-
-static void list_vars(void)
-{
-	struct git_var *ptr;
-	for(ptr = git_vars; ptr->read; ptr++) {
-		printf("%s=%s\n", ptr->name, ptr->read(IDENT_WARN_ON_NO_NAME));
-	}
-}
-
-static const char *read_var(const char *var)
-{
-	struct git_var *ptr;
-	const char *val;
-	val = NULL;
-	for(ptr = git_vars; ptr->read; ptr++) {
-		if (strcmp(var, ptr->name) == 0) {
-			val = ptr->read(IDENT_ERROR_ON_NO_NAME);
-			break;
-		}
-	}
-	return val;
-}
-
-static int show_config(const char *var, const char *value, void *cb)
-{
-	if (value)
-		printf("%s=%s\n", var, value);
-	else
-		printf("%s\n", var);
-	return git_default_config(var, value, cb);
-}
-
-int main(int argc, char **argv)
-{
-	const char *val;
-	int nongit;
-	if (argc != 2) {
-		usage(var_usage);
-	}
-
-	git_extract_argv0_path(argv[0]);
-
-	setup_git_directory_gently(&nongit);
-	val = NULL;
-
-	if (strcmp(argv[1], "-l") == 0) {
-		git_config(show_config, NULL);
-		list_vars();
-		return 0;
-	}
-	git_config(git_default_config, NULL);
-	val = read_var(argv[1]);
-	if (!val)
-		usage(var_usage);
-
-	printf("%s\n", val);
-
-	return 0;
-}
diff --git a/varint.c b/varint.c
new file mode 100644
index 0000000..4ed7729
--- /dev/null
+++ b/varint.c
@@ -0,0 +1,29 @@
+#include "varint.h"
+
+uintmax_t decode_varint(const unsigned char **bufp)
+{
+	const unsigned char *buf = *bufp;
+	unsigned char c = *buf++;
+	uintmax_t val = c & 127;
+	while (c & 128) {
+		val += 1;
+		if (!val || MSB(val, 7))
+			return 0; /* overflow */
+		c = *buf++;
+		val = (val << 7) + (c & 127);
+	}
+	*bufp = buf;
+	return val;
+}
+
+int encode_varint(uintmax_t value, unsigned char *buf)
+{
+	unsigned char varint[16];
+	unsigned pos = sizeof(varint) - 1;
+	varint[pos] = value & 127;
+	while (value >>= 7)
+		varint[--pos] = 128 | (--value & 127);
+	if (buf)
+		memcpy(buf, varint + pos, sizeof(varint) - pos);
+	return sizeof(varint) - pos;
+}
diff --git a/varint.h b/varint.h
new file mode 100644
index 0000000..0321195
--- /dev/null
+++ b/varint.h
@@ -0,0 +1,9 @@
+#ifndef VARINT_H
+#define VARINT_H
+
+#include "git-compat-util.h"
+
+extern int encode_varint(uintmax_t, unsigned char *);
+extern uintmax_t decode_varint(const unsigned char **);
+
+#endif /* VARINT_H */
diff --git a/vcs-svn/LICENSE b/vcs-svn/LICENSE
new file mode 100644
index 0000000..eb91858
--- /dev/null
+++ b/vcs-svn/LICENSE
@@ -0,0 +1,32 @@
+Copyright (C) 2010 David Barr <david.barr@cordelta.com>.
+All rights reserved.
+
+Copyright (C) 2010 Jonathan Nieder <jrnieder@gmail.com>.
+
+Copyright (C) 2005 Stefan Hegny, hydrografix Consulting GmbH,
+Frankfurt/Main, Germany
+and others, see http://svn2cc.sarovar.org
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice(s), this list of conditions and the following disclaimer
+   unmodified other than the allowable addition of one or more
+   copyright notices.
+2. Redistributions in binary form must reproduce the above copyright
+   notice(s), this list of conditions and the following disclaimer in
+   the documentation and/or other materials provided with the
+   distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vcs-svn/fast_export.c b/vcs-svn/fast_export.c
new file mode 100644
index 0000000..1f04697
--- /dev/null
+++ b/vcs-svn/fast_export.c
@@ -0,0 +1,311 @@
+/*
+ * Licensed under a two-clause BSD-style license.
+ * See LICENSE for details.
+ */
+
+#include "git-compat-util.h"
+#include "strbuf.h"
+#include "quote.h"
+#include "fast_export.h"
+#include "repo_tree.h"
+#include "strbuf.h"
+#include "svndiff.h"
+#include "sliding_window.h"
+#include "line_buffer.h"
+
+#define MAX_GITSVN_LINE_LEN 4096
+
+static uint32_t first_commit_done;
+static struct line_buffer postimage = LINE_BUFFER_INIT;
+static struct line_buffer report_buffer = LINE_BUFFER_INIT;
+
+/* NEEDSWORK: move to fast_export_init() */
+static int init_postimage(void)
+{
+	static int postimage_initialized;
+	if (postimage_initialized)
+		return 0;
+	postimage_initialized = 1;
+	return buffer_tmpfile_init(&postimage);
+}
+
+void fast_export_init(int fd)
+{
+	first_commit_done = 0;
+	if (buffer_fdinit(&report_buffer, fd))
+		die_errno("cannot read from file descriptor %d", fd);
+}
+
+void fast_export_deinit(void)
+{
+	if (buffer_deinit(&report_buffer))
+		die_errno("error closing fast-import feedback stream");
+}
+
+void fast_export_delete(const char *path)
+{
+	putchar('D');
+	putchar(' ');
+	quote_c_style(path, NULL, stdout, 0);
+	putchar('\n');
+}
+
+static void fast_export_truncate(const char *path, uint32_t mode)
+{
+	fast_export_modify(path, mode, "inline");
+	printf("data 0\n\n");
+}
+
+void fast_export_modify(const char *path, uint32_t mode, const char *dataref)
+{
+	/* Mode must be 100644, 100755, 120000, or 160000. */
+	if (!dataref) {
+		fast_export_truncate(path, mode);
+		return;
+	}
+	printf("M %06"PRIo32" %s ", mode, dataref);
+	quote_c_style(path, NULL, stdout, 0);
+	putchar('\n');
+}
+
+static char gitsvnline[MAX_GITSVN_LINE_LEN];
+void fast_export_begin_commit(uint32_t revision, const char *author,
+			const struct strbuf *log,
+			const char *uuid, const char *url,
+			unsigned long timestamp)
+{
+	static const struct strbuf empty = STRBUF_INIT;
+	if (!log)
+		log = &empty;
+	if (*uuid && *url) {
+		snprintf(gitsvnline, MAX_GITSVN_LINE_LEN,
+				"\n\ngit-svn-id: %s@%"PRIu32" %s\n",
+				 url, revision, uuid);
+	} else {
+		*gitsvnline = '\0';
+	}
+	printf("commit refs/heads/master\n");
+	printf("mark :%"PRIu32"\n", revision);
+	printf("committer %s <%s@%s> %ld +0000\n",
+		   *author ? author : "nobody",
+		   *author ? author : "nobody",
+		   *uuid ? uuid : "local", timestamp);
+	printf("data %"PRIuMAX"\n",
+		(uintmax_t) (log->len + strlen(gitsvnline)));
+	fwrite(log->buf, log->len, 1, stdout);
+	printf("%s\n", gitsvnline);
+	if (!first_commit_done) {
+		if (revision > 1)
+			printf("from :%"PRIu32"\n", revision - 1);
+		first_commit_done = 1;
+	}
+}
+
+void fast_export_end_commit(uint32_t revision)
+{
+	printf("progress Imported commit %"PRIu32".\n\n", revision);
+}
+
+static void ls_from_rev(uint32_t rev, const char *path)
+{
+	/* ls :5 path/to/old/file */
+	printf("ls :%"PRIu32" ", rev);
+	quote_c_style(path, NULL, stdout, 0);
+	putchar('\n');
+	fflush(stdout);
+}
+
+static void ls_from_active_commit(const char *path)
+{
+	/* ls "path/to/file" */
+	printf("ls \"");
+	quote_c_style(path, NULL, stdout, 1);
+	printf("\"\n");
+	fflush(stdout);
+}
+
+static const char *get_response_line(void)
+{
+	const char *line = buffer_read_line(&report_buffer);
+	if (line)
+		return line;
+	if (buffer_ferror(&report_buffer))
+		die_errno("error reading from fast-import");
+	die("unexpected end of fast-import feedback");
+}
+
+static void die_short_read(struct line_buffer *input)
+{
+	if (buffer_ferror(input))
+		die_errno("error reading dump file");
+	die("invalid dump: unexpected end of file");
+}
+
+static int ends_with(const char *s, size_t len, const char *suffix)
+{
+	const size_t suffixlen = strlen(suffix);
+	if (len < suffixlen)
+		return 0;
+	return !memcmp(s + len - suffixlen, suffix, suffixlen);
+}
+
+static int parse_cat_response_line(const char *header, off_t *len)
+{
+	size_t headerlen = strlen(header);
+	uintmax_t n;
+	const char *type;
+	const char *end;
+
+	if (ends_with(header, headerlen, " missing"))
+		return error("cat-blob reports missing blob: %s", header);
+	type = strstr(header, " blob ");
+	if (!type)
+		return error("cat-blob header has wrong object type: %s", header);
+	n = strtoumax(type + strlen(" blob "), (char **) &end, 10);
+	if (end == type + strlen(" blob "))
+		return error("cat-blob header does not contain length: %s", header);
+	if (memchr(type + strlen(" blob "), '-', end - type - strlen(" blob ")))
+		return error("cat-blob header contains negative length: %s", header);
+	if (n == UINTMAX_MAX || n > maximum_signed_value_of_type(off_t))
+		return error("blob too large for current definition of off_t");
+	*len = n;
+	if (*end)
+		return error("cat-blob header contains garbage after length: %s", header);
+	return 0;
+}
+
+static void check_preimage_overflow(off_t a, off_t b)
+{
+	if (signed_add_overflows(a, b))
+		die("blob too large for current definition of off_t");
+}
+
+static long apply_delta(off_t len, struct line_buffer *input,
+			const char *old_data, uint32_t old_mode)
+{
+	long ret;
+	struct sliding_view preimage = SLIDING_VIEW_INIT(&report_buffer, 0);
+	FILE *out;
+
+	if (init_postimage() || !(out = buffer_tmpfile_rewind(&postimage)))
+		die("cannot open temporary file for blob retrieval");
+	if (old_data) {
+		const char *response;
+		printf("cat-blob %s\n", old_data);
+		fflush(stdout);
+		response = get_response_line();
+		if (parse_cat_response_line(response, &preimage.max_off))
+			die("invalid cat-blob response: %s", response);
+		check_preimage_overflow(preimage.max_off, 1);
+	}
+	if (old_mode == REPO_MODE_LNK) {
+		strbuf_addstr(&preimage.buf, "link ");
+		check_preimage_overflow(preimage.max_off, strlen("link "));
+		preimage.max_off += strlen("link ");
+		check_preimage_overflow(preimage.max_off, 1);
+	}
+	if (svndiff0_apply(input, len, &preimage, out))
+		die("cannot apply delta");
+	if (old_data) {
+		/* Read the remainder of preimage and trailing newline. */
+		assert(!signed_add_overflows(preimage.max_off, 1));
+		preimage.max_off++;	/* room for newline */
+		if (move_window(&preimage, preimage.max_off - 1, 1))
+			die("cannot seek to end of input");
+		if (preimage.buf.buf[0] != '\n')
+			die("missing newline after cat-blob response");
+	}
+	ret = buffer_tmpfile_prepare_to_read(&postimage);
+	if (ret < 0)
+		die("cannot read temporary file for blob retrieval");
+	strbuf_release(&preimage.buf);
+	return ret;
+}
+
+void fast_export_data(uint32_t mode, off_t len, struct line_buffer *input)
+{
+	assert(len >= 0);
+	if (mode == REPO_MODE_LNK) {
+		/* svn symlink blobs start with "link " */
+		if (len < 5)
+			die("invalid dump: symlink too short for \"link\" prefix");
+		len -= 5;
+		if (buffer_skip_bytes(input, 5) != 5)
+			die_short_read(input);
+	}
+	printf("data %"PRIuMAX"\n", (uintmax_t) len);
+	if (buffer_copy_bytes(input, len) != len)
+		die_short_read(input);
+	fputc('\n', stdout);
+}
+
+static int parse_ls_response(const char *response, uint32_t *mode,
+					struct strbuf *dataref)
+{
+	const char *tab;
+	const char *response_end;
+
+	assert(response);
+	response_end = response + strlen(response);
+
+	if (*response == 'm') {	/* Missing. */
+		errno = ENOENT;
+		return -1;
+	}
+
+	/* Mode. */
+	if (response_end - response < (signed) strlen("100644") ||
+	    response[strlen("100644")] != ' ')
+		die("invalid ls response: missing mode: %s", response);
+	*mode = 0;
+	for (; *response != ' '; response++) {
+		char ch = *response;
+		if (ch < '0' || ch > '7')
+			die("invalid ls response: mode is not octal: %s", response);
+		*mode *= 8;
+		*mode += ch - '0';
+	}
+
+	/* ' blob ' or ' tree ' */
+	if (response_end - response < (signed) strlen(" blob ") ||
+	    (response[1] != 'b' && response[1] != 't'))
+		die("unexpected ls response: not a tree or blob: %s", response);
+	response += strlen(" blob ");
+
+	/* Dataref. */
+	tab = memchr(response, '\t', response_end - response);
+	if (!tab)
+		die("invalid ls response: missing tab: %s", response);
+	strbuf_add(dataref, response, tab - response);
+	return 0;
+}
+
+int fast_export_ls_rev(uint32_t rev, const char *path,
+				uint32_t *mode, struct strbuf *dataref)
+{
+	ls_from_rev(rev, path);
+	return parse_ls_response(get_response_line(), mode, dataref);
+}
+
+int fast_export_ls(const char *path, uint32_t *mode, struct strbuf *dataref)
+{
+	ls_from_active_commit(path);
+	return parse_ls_response(get_response_line(), mode, dataref);
+}
+
+void fast_export_blob_delta(uint32_t mode,
+				uint32_t old_mode, const char *old_data,
+				off_t len, struct line_buffer *input)
+{
+	long postimage_len;
+
+	assert(len >= 0);
+	postimage_len = apply_delta(len, input, old_data, old_mode);
+	if (mode == REPO_MODE_LNK) {
+		buffer_skip_bytes(&postimage, strlen("link "));
+		postimage_len -= strlen("link ");
+	}
+	printf("data %ld\n", postimage_len);
+	buffer_copy_bytes(&postimage, postimage_len);
+	fputc('\n', stdout);
+}
diff --git a/vcs-svn/fast_export.h b/vcs-svn/fast_export.h
new file mode 100644
index 0000000..8823aca
--- /dev/null
+++ b/vcs-svn/fast_export.h
@@ -0,0 +1,27 @@
+#ifndef FAST_EXPORT_H_
+#define FAST_EXPORT_H_
+
+struct strbuf;
+struct line_buffer;
+
+void fast_export_init(int fd);
+void fast_export_deinit(void);
+
+void fast_export_delete(const char *path);
+void fast_export_modify(const char *path, uint32_t mode, const char *dataref);
+void fast_export_begin_commit(uint32_t revision, const char *author,
+			const struct strbuf *log, const char *uuid,
+			const char *url, unsigned long timestamp);
+void fast_export_end_commit(uint32_t revision);
+void fast_export_data(uint32_t mode, off_t len, struct line_buffer *input);
+void fast_export_blob_delta(uint32_t mode,
+			uint32_t old_mode, const char *old_data,
+			off_t len, struct line_buffer *input);
+
+/* If there is no such file at that rev, returns -1, errno == ENOENT. */
+int fast_export_ls_rev(uint32_t rev, const char *path,
+			uint32_t *mode_out, struct strbuf *dataref_out);
+int fast_export_ls(const char *path,
+			uint32_t *mode_out, struct strbuf *dataref_out);
+
+#endif
diff --git a/vcs-svn/line_buffer.c b/vcs-svn/line_buffer.c
new file mode 100644
index 0000000..57cc1ce
--- /dev/null
+++ b/vcs-svn/line_buffer.c
@@ -0,0 +1,126 @@
+/*
+ * Licensed under a two-clause BSD-style license.
+ * See LICENSE for details.
+ */
+
+#include "git-compat-util.h"
+#include "line_buffer.h"
+#include "strbuf.h"
+
+#define COPY_BUFFER_LEN 4096
+
+int buffer_init(struct line_buffer *buf, const char *filename)
+{
+	buf->infile = filename ? fopen(filename, "r") : stdin;
+	if (!buf->infile)
+		return -1;
+	return 0;
+}
+
+int buffer_fdinit(struct line_buffer *buf, int fd)
+{
+	buf->infile = fdopen(fd, "r");
+	if (!buf->infile)
+		return -1;
+	return 0;
+}
+
+int buffer_tmpfile_init(struct line_buffer *buf)
+{
+	buf->infile = tmpfile();
+	if (!buf->infile)
+		return -1;
+	return 0;
+}
+
+int buffer_deinit(struct line_buffer *buf)
+{
+	int err;
+	if (buf->infile == stdin)
+		return ferror(buf->infile);
+	err = ferror(buf->infile);
+	err |= fclose(buf->infile);
+	return err;
+}
+
+FILE *buffer_tmpfile_rewind(struct line_buffer *buf)
+{
+	rewind(buf->infile);
+	return buf->infile;
+}
+
+long buffer_tmpfile_prepare_to_read(struct line_buffer *buf)
+{
+	long pos = ftell(buf->infile);
+	if (pos < 0)
+		return error("ftell error: %s", strerror(errno));
+	if (fseek(buf->infile, 0, SEEK_SET))
+		return error("seek error: %s", strerror(errno));
+	return pos;
+}
+
+int buffer_ferror(struct line_buffer *buf)
+{
+	return ferror(buf->infile);
+}
+
+int buffer_read_char(struct line_buffer *buf)
+{
+	return fgetc(buf->infile);
+}
+
+/* Read a line without trailing newline. */
+char *buffer_read_line(struct line_buffer *buf)
+{
+	char *end;
+	if (!fgets(buf->line_buffer, sizeof(buf->line_buffer), buf->infile))
+		/* Error or data exhausted. */
+		return NULL;
+	end = buf->line_buffer + strlen(buf->line_buffer);
+	if (end[-1] == '\n')
+		end[-1] = '\0';
+	else if (feof(buf->infile))
+		; /* No newline at end of file.  That's fine. */
+	else
+		/*
+		 * Line was too long.
+		 * There is probably a saner way to deal with this,
+		 * but for now let's return an error.
+		 */
+		return NULL;
+	return buf->line_buffer;
+}
+
+size_t buffer_read_binary(struct line_buffer *buf,
+				struct strbuf *sb, size_t size)
+{
+	return strbuf_fread(sb, size, buf->infile);
+}
+
+off_t buffer_copy_bytes(struct line_buffer *buf, off_t nbytes)
+{
+	char byte_buffer[COPY_BUFFER_LEN];
+	off_t done = 0;
+	while (done < nbytes && !feof(buf->infile) && !ferror(buf->infile)) {
+		off_t len = nbytes - done;
+		size_t in = len < COPY_BUFFER_LEN ? len : COPY_BUFFER_LEN;
+		in = fread(byte_buffer, 1, in, buf->infile);
+		done += in;
+		fwrite(byte_buffer, 1, in, stdout);
+		if (ferror(stdout))
+			return done + buffer_skip_bytes(buf, nbytes - done);
+	}
+	return done;
+}
+
+off_t buffer_skip_bytes(struct line_buffer *buf, off_t nbytes)
+{
+	char byte_buffer[COPY_BUFFER_LEN];
+	off_t done = 0;
+	while (done < nbytes && !feof(buf->infile) && !ferror(buf->infile)) {
+		off_t len = nbytes - done;
+		size_t in = len < COPY_BUFFER_LEN ? len : COPY_BUFFER_LEN;
+		done += fread(byte_buffer, 1, in, buf->infile);
+	}
+	return done;
+}
diff --git a/vcs-svn/line_buffer.h b/vcs-svn/line_buffer.h
new file mode 100644
index 0000000..ee23b4f
--- /dev/null
+++ b/vcs-svn/line_buffer.h
@@ -0,0 +1,30 @@
+#ifndef LINE_BUFFER_H_
+#define LINE_BUFFER_H_
+
+#include "strbuf.h"
+
+#define LINE_BUFFER_LEN 10000
+
+struct line_buffer {
+	char line_buffer[LINE_BUFFER_LEN];
+	FILE *infile;
+};
+#define LINE_BUFFER_INIT { "", NULL }
+
+int buffer_init(struct line_buffer *buf, const char *filename);
+int buffer_fdinit(struct line_buffer *buf, int fd);
+int buffer_deinit(struct line_buffer *buf);
+
+int buffer_tmpfile_init(struct line_buffer *buf);
+FILE *buffer_tmpfile_rewind(struct line_buffer *buf);	/* prepare to write. */
+long buffer_tmpfile_prepare_to_read(struct line_buffer *buf);
+
+int buffer_ferror(struct line_buffer *buf);
+char *buffer_read_line(struct line_buffer *buf);
+int buffer_read_char(struct line_buffer *buf);
+size_t buffer_read_binary(struct line_buffer *buf, struct strbuf *sb, size_t len);
+/* Returns number of bytes read (not necessarily written). */
+off_t buffer_copy_bytes(struct line_buffer *buf, off_t len);
+off_t buffer_skip_bytes(struct line_buffer *buf, off_t len);
+
+#endif
diff --git a/vcs-svn/line_buffer.txt b/vcs-svn/line_buffer.txt
new file mode 100644
index 0000000..8e139eb
--- /dev/null
+++ b/vcs-svn/line_buffer.txt
@@ -0,0 +1,77 @@
+line_buffer API
+===============
+
+The line_buffer library provides a convenient interface for
+mostly-line-oriented input.
+
+Each line is not permitted to exceed 10000 bytes.  The provided
+functions are not thread-safe or async-signal-safe, and like
+`fgets()`, they generally do not function correctly if interrupted
+by a signal without SA_RESTART set.
+
+Calling sequence
+----------------
+
+The calling program:
+
+ - initializes a `struct line_buffer` to LINE_BUFFER_INIT
+ - specifies a file to read with `buffer_init`
+ - processes input with `buffer_read_line`, `buffer_skip_bytes`,
+   and `buffer_copy_bytes`
+ - closes the file with `buffer_deinit`, perhaps to start over and
+   read another file.
+
+When finished, the caller can use `buffer_reset` to deallocate
+resources.
+
+Using temporary files
+---------------------
+
+Temporary files provide a place to store data that should not outlive
+the calling program.  A program
+
+ - initializes a `struct line_buffer` to LINE_BUFFER_INIT
+ - requests a temporary file with `buffer_tmpfile_init`
+ - acquires an output handle by calling `buffer_tmpfile_rewind`
+ - uses standard I/O functions like `fprintf` and `fwrite` to fill
+   the temporary file
+ - declares writing is over with `buffer_tmpfile_prepare_to_read`
+ - can re-read what was written with `buffer_read_line`,
+   `buffer_copy_bytes`, and so on
+ - can reuse the temporary file by calling `buffer_tmpfile_rewind`
+   again
+ - removes the temporary file with `buffer_deinit`, perhaps to
+   reuse the line_buffer for some other file.
+
+When finished, the calling program can use `buffer_reset` to deallocate
+resources.
+
+Functions
+---------
+
+`buffer_init`, `buffer_fdinit`::
+	Open the named file or file descriptor for input.
+	buffer_init(buf, NULL) prepares to read from stdin.
+	On failure, returns -1 (with errno indicating the nature
+	of the failure).
+
+`buffer_deinit`::
+	Stop reading from the current file (closing it unless
+	it was stdin).  Returns nonzero if `fclose` fails or
+	the error indicator was set.
+
+`buffer_read_line`::
+	Read a line and strip off the trailing newline.
+	On failure or end of file, returns NULL.
+
+`buffer_copy_bytes`::
+	Read `len` bytes of input and dump them to the standard output
+	stream.  Returns early for error or end of file.
+
+`buffer_skip_bytes`::
+	Discards `len` bytes from the input stream (stopping early
+	if necessary because of an error or eof).  Return value is
+	the number of bytes successfully read.
+
+`buffer_reset`::
+	Deallocates non-static buffers.
diff --git a/vcs-svn/repo_tree.c b/vcs-svn/repo_tree.c
new file mode 100644
index 0000000..67d27f0
--- /dev/null
+++ b/vcs-svn/repo_tree.c
@@ -0,0 +1,48 @@
+/*
+ * Licensed under a two-clause BSD-style license.
+ * See LICENSE for details.
+ */
+
+#include "git-compat-util.h"
+#include "strbuf.h"
+#include "repo_tree.h"
+#include "fast_export.h"
+
+const char *repo_read_path(const char *path, uint32_t *mode_out)
+{
+	int err;
+	static struct strbuf buf = STRBUF_INIT;
+
+	strbuf_reset(&buf);
+	err = fast_export_ls(path, mode_out, &buf);
+	if (err) {
+		if (errno != ENOENT)
+			die_errno("BUG: unexpected fast_export_ls error");
+		/* Treat missing paths as directories. */
+		*mode_out = REPO_MODE_DIR;
+		return NULL;
+	}
+	return buf.buf;
+}
+
+void repo_copy(uint32_t revision, const char *src, const char *dst)
+{
+	int err;
+	uint32_t mode;
+	static struct strbuf data = STRBUF_INIT;
+
+	strbuf_reset(&data);
+	err = fast_export_ls_rev(revision, src, &mode, &data);
+	if (err) {
+		if (errno != ENOENT)
+			die_errno("BUG: unexpected fast_export_ls_rev error");
+		fast_export_delete(dst);
+		return;
+	}
+	fast_export_modify(dst, mode, data.buf);
+}
+
+void repo_delete(const char *path)
+{
+	fast_export_delete(path);
+}
diff --git a/vcs-svn/repo_tree.h b/vcs-svn/repo_tree.h
new file mode 100644
index 0000000..889c6a3
--- /dev/null
+++ b/vcs-svn/repo_tree.h
@@ -0,0 +1,23 @@
+#ifndef REPO_TREE_H_
+#define REPO_TREE_H_
+
+struct strbuf;
+
+#define REPO_MODE_DIR 0040000
+#define REPO_MODE_BLB 0100644
+#define REPO_MODE_EXE 0100755
+#define REPO_MODE_LNK 0120000
+
+uint32_t next_blob_mark(void);
+void repo_copy(uint32_t revision, const char *src, const char *dst);
+void repo_add(const char *path, uint32_t mode, uint32_t blob_mark);
+const char *repo_read_path(const char *path, uint32_t *mode_out);
+void repo_delete(const char *path);
+void repo_commit(uint32_t revision, const char *author,
+		const struct strbuf *log, const char *uuid, const char *url,
+		long unsigned timestamp);
+void repo_diff(uint32_t r1, uint32_t r2);
+void repo_init(void);
+void repo_reset(void);
+
+#endif
diff --git a/vcs-svn/sliding_window.c b/vcs-svn/sliding_window.c
new file mode 100644
index 0000000..f11d490
--- /dev/null
+++ b/vcs-svn/sliding_window.c
@@ -0,0 +1,79 @@
+/*
+ * Licensed under a two-clause BSD-style license.
+ * See LICENSE for details.
+ */
+
+#include "git-compat-util.h"
+#include "sliding_window.h"
+#include "line_buffer.h"
+#include "strbuf.h"
+
+static int input_error(struct line_buffer *file)
+{
+	if (!buffer_ferror(file))
+		return error("delta preimage ends early");
+	return error("cannot read delta preimage: %s", strerror(errno));
+}
+
+static int skip_or_whine(struct line_buffer *file, off_t gap)
+{
+	if (buffer_skip_bytes(file, gap) != gap)
+		return input_error(file);
+	return 0;
+}
+
+static int read_to_fill_or_whine(struct line_buffer *file,
+				struct strbuf *buf, size_t width)
+{
+	buffer_read_binary(file, buf, width - buf->len);
+	if (buf->len != width)
+		return input_error(file);
+	return 0;
+}
+
+static int check_offset_overflow(off_t offset, uintmax_t len)
+{
+	if (len > maximum_signed_value_of_type(off_t))
+		return error("unrepresentable length in delta: "
+				"%"PRIuMAX" > OFF_MAX", len);
+	if (signed_add_overflows(offset, (off_t) len))
+		return error("unrepresentable offset in delta: "
+				"%"PRIuMAX" + %"PRIuMAX" > OFF_MAX",
+				(uintmax_t) offset, len);
+	return 0;
+}
+
+int move_window(struct sliding_view *view, off_t off, size_t width)
+{
+	off_t file_offset;
+	assert(view);
+	assert(view->width <= view->buf.len);
+	assert(!check_offset_overflow(view->off, view->buf.len));
+
+	if (check_offset_overflow(off, width))
+		return -1;
+	if (off < view->off || off + width < view->off + view->width)
+		return error("invalid delta: window slides left");
+	if (view->max_off >= 0 && view->max_off < off + (off_t) width)
+		return error("delta preimage ends early");
+
+	file_offset = view->off + view->buf.len;
+	if (off < file_offset) {
+		/* Move the overlapping region into place. */
+		strbuf_remove(&view->buf, 0, off - view->off);
+	} else {
+		/* Seek ahead to skip the gap. */
+		if (skip_or_whine(view->file, off - file_offset))
+			return -1;
+		strbuf_setlen(&view->buf, 0);
+	}
+
+	if (view->buf.len > width)
+		; /* Already read. */
+	else if (read_to_fill_or_whine(view->file, &view->buf, width))
+		return -1;
+
+	view->off = off;
+	view->width = width;
+	return 0;
+}
diff --git a/vcs-svn/sliding_window.h b/vcs-svn/sliding_window.h
new file mode 100644
index 0000000..b43a825
--- /dev/null
+++ b/vcs-svn/sliding_window.h
@@ -0,0 +1,18 @@
+#ifndef SLIDING_WINDOW_H_
+#define SLIDING_WINDOW_H_
+
+#include "strbuf.h"
+
+struct sliding_view {
+	struct line_buffer *file;
+	off_t off;
+	size_t width;
+	off_t max_off;	/* -1 means unlimited */
+	struct strbuf buf;
+};
+
+#define SLIDING_VIEW_INIT(input, len)	{ (input), 0, 0, (len), STRBUF_INIT }
+
+extern int move_window(struct sliding_view *view, off_t off, size_t width);
+
+#endif
diff --git a/vcs-svn/svndiff.c b/vcs-svn/svndiff.c
new file mode 100644
index 0000000..74c97c4
--- /dev/null
+++ b/vcs-svn/svndiff.c
@@ -0,0 +1,309 @@
+/*
+ * Licensed under a two-clause BSD-style license.
+ * See LICENSE for details.
+ */
+
+#include "git-compat-util.h"
+#include "sliding_window.h"
+#include "line_buffer.h"
+#include "svndiff.h"
+
+/*
+ * svndiff0 applier
+ *
+ * See http://svn.apache.org/repos/asf/subversion/trunk/notes/svndiff.
+ *
+ * svndiff0 ::= 'SVN\0' window*
+ * window ::= int int int int int instructions inline_data;
+ * instructions ::= instruction*;
+ * instruction ::= view_selector int int
+ *   | copyfrom_data int
+ *   | packed_view_selector int
+ *   | packed_copyfrom_data
+ *   ;
+ * view_selector ::= copyfrom_source
+ *   | copyfrom_target
+ *   ;
+ * copyfrom_source ::= # binary 00 000000;
+ * copyfrom_target ::= # binary 01 000000;
+ * copyfrom_data ::= # binary 10 000000;
+ * packed_view_selector ::= # view_selector OR-ed with 6 bit value;
+ * packed_copyfrom_data ::= # copyfrom_data OR-ed with 6 bit value;
+ * int ::= highdigit* lowdigit;
+ * highdigit ::= # binary 1000 0000 OR-ed with 7 bit value;
+ * lowdigit ::= # 7 bit value;
+ */
+
+#define INSN_MASK	0xc0
+#define INSN_COPYFROM_SOURCE	0x00
+#define INSN_COPYFROM_TARGET	0x40
+#define INSN_COPYFROM_DATA	0x80
+#define OPERAND_MASK	0x3f
+
+#define VLI_CONTINUE	0x80
+#define VLI_DIGIT_MASK	0x7f
+#define VLI_BITS_PER_DIGIT 7
+
+struct window {
+	struct sliding_view *in;
+	struct strbuf out;
+	struct strbuf instructions;
+	struct strbuf data;
+};
+
+#define WINDOW_INIT(w)	{ (w), STRBUF_INIT, STRBUF_INIT, STRBUF_INIT }
+
+static void window_release(struct window *ctx)
+{
+	strbuf_release(&ctx->out);
+	strbuf_release(&ctx->instructions);
+	strbuf_release(&ctx->data);
+}
+
+static int write_strbuf(struct strbuf *sb, FILE *out)
+{
+	if (fwrite(sb->buf, 1, sb->len, out) == sb->len)	/* Success. */
+		return 0;
+	return error("cannot write delta postimage: %s", strerror(errno));
+}
+
+static int error_short_read(struct line_buffer *input)
+{
+	if (buffer_ferror(input))
+		return error("error reading delta: %s", strerror(errno));
+	return error("invalid delta: unexpected end of file");
+}
+
+static int read_chunk(struct line_buffer *delta, off_t *delta_len,
+		      struct strbuf *buf, size_t len)
+{
+	assert(*delta_len >= 0);
+	strbuf_reset(buf);
+	if (len > (uintmax_t) *delta_len ||
+	    buffer_read_binary(delta, buf, len) != len)
+		return error_short_read(delta);
+	*delta_len -= buf->len;
+	return 0;
+}
+
+static int read_magic(struct line_buffer *in, off_t *len)
+{
+	static const char magic[] = {'S', 'V', 'N', '\0'};
+	struct strbuf sb = STRBUF_INIT;
+
+	if (read_chunk(in, len, &sb, sizeof(magic))) {
+		strbuf_release(&sb);
+		return -1;
+	}
+	if (memcmp(sb.buf, magic, sizeof(magic))) {
+		strbuf_release(&sb);
+		return error("invalid delta: unrecognized file type");
+	}
+	strbuf_release(&sb);
+	return 0;
+}
+
+static int read_int(struct line_buffer *in, uintmax_t *result, off_t *len)
+{
+	uintmax_t rv = 0;
+	off_t sz;
+	for (sz = *len; sz; sz--) {
+		const int ch = buffer_read_char(in);
+		if (ch == EOF)
+			break;
+
+		rv <<= VLI_BITS_PER_DIGIT;
+		rv += (ch & VLI_DIGIT_MASK);
+		if (ch & VLI_CONTINUE)
+			continue;
+
+		*result = rv;
+		*len = sz - 1;
+		return 0;
+	}
+	return error_short_read(in);
+}
+
+static int parse_int(const char **buf, size_t *result, const char *end)
+{
+	size_t rv = 0;
+	const char *pos;
+	for (pos = *buf; pos != end; pos++) {
+		unsigned char ch = *pos;
+
+		rv <<= VLI_BITS_PER_DIGIT;
+		rv += (ch & VLI_DIGIT_MASK);
+		if (ch & VLI_CONTINUE)
+			continue;
+
+		*result = rv;
+		*buf = pos + 1;
+		return 0;
+	}
+	return error("invalid delta: unexpected end of instructions section");
+}
+
+static int read_offset(struct line_buffer *in, off_t *result, off_t *len)
+{
+	uintmax_t val;
+	if (read_int(in, &val, len))
+		return -1;
+	if (val > maximum_signed_value_of_type(off_t))
+		return error("unrepresentable offset in delta: %"PRIuMAX"", val);
+	*result = val;
+	return 0;
+}
+
+static int read_length(struct line_buffer *in, size_t *result, off_t *len)
+{
+	uintmax_t val;
+	if (read_int(in, &val, len))
+		return -1;
+	if (val > SIZE_MAX)
+		return error("unrepresentable length in delta: %"PRIuMAX"", val);
+	*result = val;
+	return 0;
+}
+
+static int copyfrom_source(struct window *ctx, const char **instructions,
+			   size_t nbytes, const char *insns_end)
+{
+	size_t offset;
+	if (parse_int(instructions, &offset, insns_end))
+		return -1;
+	if (unsigned_add_overflows(offset, nbytes) ||
+	    offset + nbytes > ctx->in->width)
+		return error("invalid delta: copies source data outside view");
+	strbuf_add(&ctx->out, ctx->in->buf.buf + offset, nbytes);
+	return 0;
+}
+
+static int copyfrom_target(struct window *ctx, const char **instructions,
+			   size_t nbytes, const char *instructions_end)
+{
+	size_t offset;
+	if (parse_int(instructions, &offset, instructions_end))
+		return -1;
+	if (offset >= ctx->out.len)
+		return error("invalid delta: copies from the future");
+	for (; nbytes > 0; nbytes--)
+		strbuf_addch(&ctx->out, ctx->out.buf[offset++]);
+	return 0;
+}
+
+static int copyfrom_data(struct window *ctx, size_t *data_pos, size_t nbytes)
+{
+	const size_t pos = *data_pos;
+	if (unsigned_add_overflows(pos, nbytes) ||
+	    pos + nbytes > ctx->data.len)
+		return error("invalid delta: copies unavailable inline data");
+	strbuf_add(&ctx->out, ctx->data.buf + pos, nbytes);
+	*data_pos += nbytes;
+	return 0;
+}
+
+static int parse_first_operand(const char **buf, size_t *out, const char *end)
+{
+	size_t result = (unsigned char) *(*buf)++ & OPERAND_MASK;
+	if (result) {	/* immediate operand */
+		*out = result;
+		return 0;
+	}
+	return parse_int(buf, out, end);
+}
+
+static int execute_one_instruction(struct window *ctx,
+				const char **instructions, size_t *data_pos)
+{
+	unsigned int instruction;
+	const char *insns_end = ctx->instructions.buf + ctx->instructions.len;
+	size_t nbytes;
+	assert(ctx);
+	assert(instructions && *instructions);
+	assert(data_pos);
+
+	instruction = (unsigned char) **instructions;
+	if (parse_first_operand(instructions, &nbytes, insns_end))
+		return -1;
+	switch (instruction & INSN_MASK) {
+	case INSN_COPYFROM_SOURCE:
+		return copyfrom_source(ctx, instructions, nbytes, insns_end);
+	case INSN_COPYFROM_TARGET:
+		return copyfrom_target(ctx, instructions, nbytes, insns_end);
+	case INSN_COPYFROM_DATA:
+		return copyfrom_data(ctx, data_pos, nbytes);
+	default:
+		return error("invalid delta: unrecognized instruction");
+	}
+}
+
+static int apply_window_in_core(struct window *ctx)
+{
+	const char *instructions;
+	size_t data_pos = 0;
+
+	/*
+	 * Fill ctx->out.buf using data from the source, target,
+	 * and inline data views.
+	 */
+	for (instructions = ctx->instructions.buf;
+	     instructions != ctx->instructions.buf + ctx->instructions.len;
+	     )
+		if (execute_one_instruction(ctx, &instructions, &data_pos))
+			return -1;
+	if (data_pos != ctx->data.len)
+		return error("invalid delta: does not copy all inline data");
+	return 0;
+}
+
+static int apply_one_window(struct line_buffer *delta, off_t *delta_len,
+			    struct sliding_view *preimage, FILE *out)
+{
+	int rv = -1;
+	struct window ctx = WINDOW_INIT(preimage);
+	size_t out_len;
+	size_t instructions_len;
+	size_t data_len;
+	assert(delta_len);
+
+	/* "source view" offset and length already handled; */
+	if (read_length(delta, &out_len, delta_len) ||
+	    read_length(delta, &instructions_len, delta_len) ||
+	    read_length(delta, &data_len, delta_len) ||
+	    read_chunk(delta, delta_len, &ctx.instructions, instructions_len) ||
+	    read_chunk(delta, delta_len, &ctx.data, data_len))
+		goto error_out;
+	strbuf_grow(&ctx.out, out_len);
+	if (apply_window_in_core(&ctx))
+		goto error_out;
+	if (ctx.out.len != out_len) {
+		rv = error("invalid delta: incorrect postimage length");
+		goto error_out;
+	}
+	if (write_strbuf(&ctx.out, out))
+		goto error_out;
+	rv = 0;
+error_out:
+	window_release(&ctx);
+	return rv;
+}
+
+int svndiff0_apply(struct line_buffer *delta, off_t delta_len,
+			struct sliding_view *preimage, FILE *postimage)
+{
+	assert(delta && preimage && postimage && delta_len >= 0);
+
+	if (read_magic(delta, &delta_len))
+		return -1;
+	while (delta_len) {	/* For each window: */
+		off_t pre_off = -1;
+		size_t pre_len;
+
+		if (read_offset(delta, &pre_off, &delta_len) ||
+		    read_length(delta, &pre_len, &delta_len) ||
+		    move_window(preimage, pre_off, pre_len) ||
+		    apply_one_window(delta, &delta_len, preimage, postimage))
+			return -1;
+	}
+	return 0;
+}
diff --git a/vcs-svn/svndiff.h b/vcs-svn/svndiff.h
new file mode 100644
index 0000000..74eb464
--- /dev/null
+++ b/vcs-svn/svndiff.h
@@ -0,0 +1,10 @@
+#ifndef SVNDIFF_H_
+#define SVNDIFF_H_
+
+struct line_buffer;
+struct sliding_view;
+
+extern int svndiff0_apply(struct line_buffer *delta, off_t delta_len,
+		struct sliding_view *preimage, FILE *postimage);
+
+#endif
diff --git a/vcs-svn/svndump.c b/vcs-svn/svndump.c
new file mode 100644
index 0000000..2b168ae
--- /dev/null
+++ b/vcs-svn/svndump.c
@@ -0,0 +1,509 @@
+/*
+ * Parse and rearrange a svnadmin dump.
+ * Create the dump with:
+ * svnadmin dump --incremental -r<startrev>:<endrev> <repository> >outfile
+ *
+ * Licensed under a two-clause BSD-style license.
+ * See LICENSE for details.
+ */
+
+#include "cache.h"
+#include "repo_tree.h"
+#include "fast_export.h"
+#include "line_buffer.h"
+#include "strbuf.h"
+#include "svndump.h"
+
+/*
+ * Compare start of string to literal of equal length;
+ * must be guarded by length test.
+ */
+#define constcmp(s, ref) memcmp(s, ref, sizeof(ref) - 1)
+
+#define REPORT_FILENO 3
+
+#define NODEACT_REPLACE 4
+#define NODEACT_DELETE 3
+#define NODEACT_ADD 2
+#define NODEACT_CHANGE 1
+#define NODEACT_UNKNOWN 0
+
+/* States: */
+#define DUMP_CTX 0	/* dump metadata */
+#define REV_CTX  1	/* revision metadata */
+#define NODE_CTX 2	/* node metadata */
+#define INTERNODE_CTX 3	/* between nodes */
+
+#define DATE_RFC2822_LEN 31
+
+static struct line_buffer input = LINE_BUFFER_INIT;
+
+static struct {
+	uint32_t action, srcRev, type;
+	off_t prop_length, text_length;
+	struct strbuf src, dst;
+	uint32_t text_delta, prop_delta;
+} node_ctx;
+
+static struct {
+	uint32_t revision;
+	unsigned long timestamp;
+	struct strbuf log, author;
+} rev_ctx;
+
+static struct {
+	uint32_t version;
+	struct strbuf uuid, url;
+} dump_ctx;
+
+static void reset_node_ctx(char *fname)
+{
+	node_ctx.type = 0;
+	node_ctx.action = NODEACT_UNKNOWN;
+	node_ctx.prop_length = -1;
+	node_ctx.text_length = -1;
+	strbuf_reset(&node_ctx.src);
+	node_ctx.srcRev = 0;
+	strbuf_reset(&node_ctx.dst);
+	if (fname)
+		strbuf_addstr(&node_ctx.dst, fname);
+	node_ctx.text_delta = 0;
+	node_ctx.prop_delta = 0;
+}
+
+static void reset_rev_ctx(uint32_t revision)
+{
+	rev_ctx.revision = revision;
+	rev_ctx.timestamp = 0;
+	strbuf_reset(&rev_ctx.log);
+	strbuf_reset(&rev_ctx.author);
+}
+
+static void reset_dump_ctx(const char *url)
+{
+	strbuf_reset(&dump_ctx.url);
+	if (url)
+		strbuf_addstr(&dump_ctx.url, url);
+	dump_ctx.version = 1;
+	strbuf_reset(&dump_ctx.uuid);
+}
+
+static void handle_property(const struct strbuf *key_buf,
+				struct strbuf *val,
+				uint32_t *type_set)
+{
+	const char *key = key_buf->buf;
+	size_t keylen = key_buf->len;
+
+	switch (keylen + 1) {
+	case sizeof("svn:log"):
+		if (constcmp(key, "svn:log"))
+			break;
+		if (!val)
+			die("invalid dump: unsets svn:log");
+		strbuf_swap(&rev_ctx.log, val);
+		break;
+	case sizeof("svn:author"):
+		if (constcmp(key, "svn:author"))
+			break;
+		if (!val)
+			strbuf_reset(&rev_ctx.author);
+		else
+			strbuf_swap(&rev_ctx.author, val);
+		break;
+	case sizeof("svn:date"):
+		if (constcmp(key, "svn:date"))
+			break;
+		if (!val)
+			die("invalid dump: unsets svn:date");
+		if (parse_date_basic(val->buf, &rev_ctx.timestamp, NULL))
+			warning("invalid timestamp: %s", val->buf);
+		break;
+	case sizeof("svn:executable"):
+	case sizeof("svn:special"):
+		if (keylen == strlen("svn:executable") &&
+		    constcmp(key, "svn:executable"))
+			break;
+		if (keylen == strlen("svn:special") &&
+		    constcmp(key, "svn:special"))
+			break;
+		if (*type_set) {
+			if (!val)
+				return;
+			die("invalid dump: sets type twice");
+		}
+		if (!val) {
+			node_ctx.type = REPO_MODE_BLB;
+			return;
+		}
+		*type_set = 1;
+		node_ctx.type = keylen == strlen("svn:executable") ?
+				REPO_MODE_EXE :
+				REPO_MODE_LNK;
+	}
+}
+
+static void die_short_read(void)
+{
+	if (buffer_ferror(&input))
+		die_errno("error reading dump file");
+	die("invalid dump: unexpected end of file");
+}
+
+static void read_props(void)
+{
+	static struct strbuf key = STRBUF_INIT;
+	static struct strbuf val = STRBUF_INIT;
+	const char *t;
+	/*
+	 * NEEDSWORK: to support simple mode changes like
+	 *	K 11
+	 *	svn:special
+	 *	V 1
+	 *	*
+	 *	D 14
+	 *	svn:executable
+	 * we keep track of whether a mode has been set and reset to
+	 * plain file only if not.  We should be keeping track of the
+	 * symlink and executable bits separately instead.
+	 */
+	uint32_t type_set = 0;
+	while ((t = buffer_read_line(&input)) && strcmp(t, "PROPS-END")) {
+		uint32_t len;
+		const char type = t[0];
+		int ch;
+
+		if (!type || t[1] != ' ')
+			die("invalid property line: %s", t);
+		len = atoi(&t[2]);
+		strbuf_reset(&val);
+		buffer_read_binary(&input, &val, len);
+		if (val.len < len)
+			die_short_read();
+
+		/* Discard trailing newline. */
+		ch = buffer_read_char(&input);
+		if (ch == EOF)
+			die_short_read();
+		if (ch != '\n')
+			die("invalid dump: expected newline after %s", val.buf);
+
+		switch (type) {
+		case 'K':
+			strbuf_swap(&key, &val);
+			continue;
+		case 'D':
+			handle_property(&val, NULL, &type_set);
+			continue;
+		case 'V':
+			handle_property(&key, &val, &type_set);
+			strbuf_reset(&key);
+			continue;
+		default:
+			die("invalid property line: %s", t);
+		}
+	}
+}
+
+static void handle_node(void)
+{
+	const uint32_t type = node_ctx.type;
+	const int have_props = node_ctx.prop_length != -1;
+	const int have_text = node_ctx.text_length != -1;
+	/*
+	 * Old text for this node:
+	 *  NULL	- directory or bug
+	 *  empty_blob	- empty
+	 *  "<dataref>"	- data retrievable from fast-import
+	 */
+	static const char *const empty_blob = "::empty::";
+	const char *old_data = NULL;
+	uint32_t old_mode = REPO_MODE_BLB;
+
+	if (node_ctx.action == NODEACT_DELETE) {
+		if (have_text || have_props || node_ctx.srcRev)
+			die("invalid dump: deletion node has "
+				"copyfrom info, text, or properties");
+		repo_delete(node_ctx.dst.buf);
+		return;
+	}
+	if (node_ctx.action == NODEACT_REPLACE) {
+		repo_delete(node_ctx.dst.buf);
+		node_ctx.action = NODEACT_ADD;
+	}
+	if (node_ctx.srcRev) {
+		repo_copy(node_ctx.srcRev, node_ctx.src.buf, node_ctx.dst.buf);
+		if (node_ctx.action == NODEACT_ADD)
+			node_ctx.action = NODEACT_CHANGE;
+	}
+	if (have_text && type == REPO_MODE_DIR)
+		die("invalid dump: directories cannot have text attached");
+
+	/*
+	 * Find old content (old_data) and decide on the new mode.
+	 */
+	if (node_ctx.action == NODEACT_CHANGE && !*node_ctx.dst.buf) {
+		if (type != REPO_MODE_DIR)
+			die("invalid dump: root of tree is not a regular file");
+		old_data = NULL;
+	} else if (node_ctx.action == NODEACT_CHANGE) {
+		uint32_t mode;
+		old_data = repo_read_path(node_ctx.dst.buf, &mode);
+		if (mode == REPO_MODE_DIR && type != REPO_MODE_DIR)
+			die("invalid dump: cannot modify a directory into a file");
+		if (mode != REPO_MODE_DIR && type == REPO_MODE_DIR)
+			die("invalid dump: cannot modify a file into a directory");
+		node_ctx.type = mode;
+		old_mode = mode;
+	} else if (node_ctx.action == NODEACT_ADD) {
+		if (type == REPO_MODE_DIR)
+			old_data = NULL;
+		else if (have_text)
+			old_data = empty_blob;
+		else
+			die("invalid dump: adds node without text");
+	} else {
+		die("invalid dump: Node-path block lacks Node-action");
+	}
+
+	/*
+	 * Adjust mode to reflect properties.
+	 */
+	if (have_props) {
+		if (!node_ctx.prop_delta)
+			node_ctx.type = type;
+		if (node_ctx.prop_length)
+			read_props();
+	}
+
+	/*
+	 * Save the result.
+	 */
+	if (type == REPO_MODE_DIR)	/* directories are not tracked. */
+		return;
+	assert(old_data);
+	if (old_data == empty_blob)
+		/* For the fast_export_* functions, NULL means empty. */
+		old_data = NULL;
+	if (!have_text) {
+		fast_export_modify(node_ctx.dst.buf, node_ctx.type, old_data);
+		return;
+	}
+	if (!node_ctx.text_delta) {
+		fast_export_modify(node_ctx.dst.buf, node_ctx.type, "inline");
+		fast_export_data(node_ctx.type, node_ctx.text_length, &input);
+		return;
+	}
+	fast_export_modify(node_ctx.dst.buf, node_ctx.type, "inline");
+	fast_export_blob_delta(node_ctx.type, old_mode, old_data,
+				node_ctx.text_length, &input);
+}
+
+static void begin_revision(void)
+{
+	if (!rev_ctx.revision)	/* revision 0 gets no git commit. */
+		return;
+	fast_export_begin_commit(rev_ctx.revision, rev_ctx.author.buf,
+		&rev_ctx.log, dump_ctx.uuid.buf, dump_ctx.url.buf,
+		rev_ctx.timestamp);
+}
+
+static void end_revision(void)
+{
+	if (rev_ctx.revision)
+		fast_export_end_commit(rev_ctx.revision);
+}
+
+void svndump_read(const char *url)
+{
+	char *val;
+	char *t;
+	uint32_t active_ctx = DUMP_CTX;
+	uint32_t len;
+
+	reset_dump_ctx(url);
+	while ((t = buffer_read_line(&input))) {
+		val = strchr(t, ':');
+		if (!val)
+			continue;
+		val++;
+		if (*val != ' ')
+			continue;
+		val++;
+
+		/* strlen(key) + 1 */
+		switch (val - t - 1) {
+		case sizeof("SVN-fs-dump-format-version"):
+			if (constcmp(t, "SVN-fs-dump-format-version"))
+				continue;
+			dump_ctx.version = atoi(val);
+			if (dump_ctx.version > 3)
+				die("expected svn dump format version <= 3, found %"PRIu32,
+				    dump_ctx.version);
+			break;
+		case sizeof("UUID"):
+			if (constcmp(t, "UUID"))
+				continue;
+			strbuf_reset(&dump_ctx.uuid);
+			strbuf_addstr(&dump_ctx.uuid, val);
+			break;
+		case sizeof("Revision-number"):
+			if (constcmp(t, "Revision-number"))
+				continue;
+			if (active_ctx == NODE_CTX)
+				handle_node();
+			if (active_ctx == REV_CTX)
+				begin_revision();
+			if (active_ctx != DUMP_CTX)
+				end_revision();
+			active_ctx = REV_CTX;
+			reset_rev_ctx(atoi(val));
+			break;
+		case sizeof("Node-path"):
+			if (constcmp(t, "Node-"))
+				continue;
+			if (!constcmp(t + strlen("Node-"), "path")) {
+				if (active_ctx == NODE_CTX)
+					handle_node();
+				if (active_ctx == REV_CTX)
+					begin_revision();
+				active_ctx = NODE_CTX;
+				reset_node_ctx(val);
+				break;
+			}
+			if (constcmp(t + strlen("Node-"), "kind"))
+				continue;
+			if (!strcmp(val, "dir"))
+				node_ctx.type = REPO_MODE_DIR;
+			else if (!strcmp(val, "file"))
+				node_ctx.type = REPO_MODE_BLB;
+			else
+				fprintf(stderr, "Unknown node-kind: %s\n", val);
+			break;
+		case sizeof("Node-action"):
+			if (constcmp(t, "Node-action"))
+				continue;
+			if (!strcmp(val, "delete")) {
+				node_ctx.action = NODEACT_DELETE;
+			} else if (!strcmp(val, "add")) {
+				node_ctx.action = NODEACT_ADD;
+			} else if (!strcmp(val, "change")) {
+				node_ctx.action = NODEACT_CHANGE;
+			} else if (!strcmp(val, "replace")) {
+				node_ctx.action = NODEACT_REPLACE;
+			} else {
+				fprintf(stderr, "Unknown node-action: %s\n", val);
+				node_ctx.action = NODEACT_UNKNOWN;
+			}
+			break;
+		case sizeof("Node-copyfrom-path"):
+			if (constcmp(t, "Node-copyfrom-path"))
+				continue;
+			strbuf_reset(&node_ctx.src);
+			strbuf_addstr(&node_ctx.src, val);
+			break;
+		case sizeof("Node-copyfrom-rev"):
+			if (constcmp(t, "Node-copyfrom-rev"))
+				continue;
+			node_ctx.srcRev = atoi(val);
+			break;
+		case sizeof("Text-content-length"):
+			if (constcmp(t, "Text") && constcmp(t, "Prop"))
+				continue;
+			if (constcmp(t + 4, "-content-length"))
+				continue;
+			{
+				char *end;
+				uintmax_t len;
+
+				len = strtoumax(val, &end, 10);
+				if (!isdigit(*val) || *end)
+					die("invalid dump: non-numeric length %s", val);
+				if (len > maximum_signed_value_of_type(off_t))
+					die("unrepresentable length in dump: %s", val);
+
+				if (*t == 'T')
+					node_ctx.text_length = (off_t) len;
+				else
+					node_ctx.prop_length = (off_t) len;
+				break;
+			}
+		case sizeof("Text-delta"):
+			if (!constcmp(t, "Text-delta")) {
+				node_ctx.text_delta = !strcmp(val, "true");
+				break;
+			}
+			if (constcmp(t, "Prop-delta"))
+				continue;
+			node_ctx.prop_delta = !strcmp(val, "true");
+			break;
+		case sizeof("Content-length"):
+			if (constcmp(t, "Content-length"))
+				continue;
+			len = atoi(val);
+			t = buffer_read_line(&input);
+			if (!t)
+				die_short_read();
+			if (*t)
+				die("invalid dump: expected blank line after content length header");
+			if (active_ctx == REV_CTX) {
+				read_props();
+			} else if (active_ctx == NODE_CTX) {
+				handle_node();
+				active_ctx = INTERNODE_CTX;
+			} else {
+				fprintf(stderr, "Unexpected content length header: %"PRIu32"\n", len);
+				if (buffer_skip_bytes(&input, len) != len)
+					die_short_read();
+			}
+		}
+	}
+	if (buffer_ferror(&input))
+		die_short_read();
+	if (active_ctx == NODE_CTX)
+		handle_node();
+	if (active_ctx == REV_CTX)
+		begin_revision();
+	if (active_ctx != DUMP_CTX)
+		end_revision();
+}
+
+int svndump_init(const char *filename)
+{
+	if (buffer_init(&input, filename))
+		return error("cannot open %s: %s", filename, strerror(errno));
+	fast_export_init(REPORT_FILENO);
+	strbuf_init(&dump_ctx.uuid, 4096);
+	strbuf_init(&dump_ctx.url, 4096);
+	strbuf_init(&rev_ctx.log, 4096);
+	strbuf_init(&rev_ctx.author, 4096);
+	strbuf_init(&node_ctx.src, 4096);
+	strbuf_init(&node_ctx.dst, 4096);
+	reset_dump_ctx(NULL);
+	reset_rev_ctx(0);
+	reset_node_ctx(NULL);
+	return 0;
+}
+
+void svndump_deinit(void)
+{
+	fast_export_deinit();
+	reset_dump_ctx(NULL);
+	reset_rev_ctx(0);
+	reset_node_ctx(NULL);
+	strbuf_release(&rev_ctx.log);
+	strbuf_release(&node_ctx.src);
+	strbuf_release(&node_ctx.dst);
+	if (buffer_deinit(&input))
+		fprintf(stderr, "Input error\n");
+	if (ferror(stdout))
+		fprintf(stderr, "Output error\n");
+}
+
+void svndump_reset(void)
+{
+	strbuf_release(&dump_ctx.uuid);
+	strbuf_release(&dump_ctx.url);
+	strbuf_release(&rev_ctx.log);
+	strbuf_release(&rev_ctx.author);
+}
diff --git a/vcs-svn/svndump.h b/vcs-svn/svndump.h
new file mode 100644
index 0000000..df9ceb0
--- /dev/null
+++ b/vcs-svn/svndump.h
@@ -0,0 +1,9 @@
+#ifndef SVNDUMP_H_
+#define SVNDUMP_H_
+
+int svndump_init(const char *filename);
+void svndump_read(const char *url);
+void svndump_deinit(void);
+void svndump_reset(void);
+
+#endif
diff --git a/version.c b/version.c
new file mode 100644
index 0000000..f98d5a6
--- /dev/null
+++ b/version.c
@@ -0,0 +1,17 @@
+#include "git-compat-util.h"
+#include "version.h"
+
+const char git_version_string[] = GIT_VERSION;
+
+const char *git_user_agent(void)
+{
+	static const char *agent = NULL;
+
+	if (!agent) {
+		agent = getenv("GIT_USER_AGENT");
+		if (!agent)
+			agent = GIT_USER_AGENT;
+	}
+
+	return agent;
+}
diff --git a/version.h b/version.h
new file mode 100644
index 0000000..fd9cdd6
--- /dev/null
+++ b/version.h
@@ -0,0 +1,8 @@
+#ifndef VERSION_H
+#define VERSION_H
+
+extern const char git_version_string[];
+
+const char *git_user_agent(void);
+
+#endif /* VERSION_H */
diff --git a/walker.c b/walker.c
index 11d9052..be389dc 100644
--- a/walker.c
+++ b/walker.c
@@ -190,7 +190,7 @@
 {
 	if (!get_sha1_hex(target, sha1))
 		return 0;
-	if (!check_ref_format(target)) {
+	if (!check_refname_format(target, 0)) {
 		struct ref *ref = alloc_ref(target);
 		if (!walker->fetch_ref(walker, ref)) {
 			hashcpy(sha1, ref->old_sha1);
@@ -207,7 +207,7 @@
 	struct commit *commit = lookup_commit_reference_gently(sha1, 1);
 	if (commit) {
 		commit->object.flags |= COMPLETE;
-		insert_by_date(commit, &complete);
+		commit_list_insert_by_date(commit, &complete);
 	}
 	return 0;
 }
diff --git a/walker.h b/walker.h
index 8a149e1..95e5765 100644
--- a/walker.h
+++ b/walker.h
@@ -34,6 +34,6 @@
 
 void walker_free(struct walker *walker);
 
-struct walker *get_http_walker(const char *url, struct remote *remote);
+struct walker *get_http_walker(const char *url);
 
 #endif /* WALKER_H */
diff --git a/wrap-for-bin.sh b/wrap-for-bin.sh
new file mode 100644
index 0000000..53a8dd0
--- /dev/null
+++ b/wrap-for-bin.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+# wrap-for-bin.sh: Template for git executable wrapper scripts
+# to run test suite against sandbox, but with only bindir-installed
+# executables in PATH.  The Makefile copies this into various
+# files in bin-wrappers, substituting
+# @@BUILD_DIR@@ and @@PROG@@.
+
+GIT_EXEC_PATH='@@BUILD_DIR@@'
+if test -n "$NO_SET_GIT_TEMPLATE_DIR"
+then
+	unset GIT_TEMPLATE_DIR
+else
+	GIT_TEMPLATE_DIR='@@BUILD_DIR@@/templates/blt'
+	export GIT_TEMPLATE_DIR
+fi
+GITPERLLIB='@@BUILD_DIR@@/perl/blib/lib'
+GIT_TEXTDOMAINDIR='@@BUILD_DIR@@/po/build/locale'
+PATH='@@BUILD_DIR@@/bin-wrappers:'"$PATH"
+export GIT_EXEC_PATH GITPERLLIB PATH GIT_TEXTDOMAINDIR
+
+exec "${GIT_EXEC_PATH}/@@PROG@@" "$@"
diff --git a/wrapper.c b/wrapper.c
index c9be140..b5e33e4 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -3,11 +3,38 @@
  */
 #include "cache.h"
 
+static void do_nothing(size_t size)
+{
+}
+
+static void (*try_to_free_routine)(size_t size) = do_nothing;
+
+static void memory_limit_check(size_t size)
+{
+	static int limit = -1;
+	if (limit == -1) {
+		const char *env = getenv("GIT_ALLOC_LIMIT");
+		limit = env ? atoi(env) * 1024 : 0;
+	}
+	if (limit && size > limit)
+		die("attempting to allocate %"PRIuMAX" over limit %d",
+		    (intmax_t)size, limit);
+}
+
+try_to_free_t set_try_to_free_routine(try_to_free_t routine)
+{
+	try_to_free_t old = try_to_free_routine;
+	if (!routine)
+		routine = do_nothing;
+	try_to_free_routine = routine;
+	return old;
+}
+
 char *xstrdup(const char *str)
 {
 	char *ret = strdup(str);
 	if (!ret) {
-		release_pack_memory(strlen(str) + 1, -1);
+		try_to_free_routine(strlen(str) + 1);
 		ret = strdup(str);
 		if (!ret)
 			die("Out of memory, strdup failed");
@@ -17,16 +44,20 @@
 
 void *xmalloc(size_t size)
 {
-	void *ret = malloc(size);
+	void *ret;
+
+	memory_limit_check(size);
+	ret = malloc(size);
 	if (!ret && !size)
 		ret = malloc(1);
 	if (!ret) {
-		release_pack_memory(size, -1);
+		try_to_free_routine(size);
 		ret = malloc(size);
 		if (!ret && !size)
 			ret = malloc(1);
 		if (!ret)
-			die("Out of memory, malloc failed");
+			die("Out of memory, malloc failed (tried to allocate %lu bytes)",
+			    (unsigned long)size);
 	}
 #ifdef XMALLOC_POISON
 	memset(ret, 0xA5, size);
@@ -34,6 +65,16 @@
 	return ret;
 }
 
+void *xmallocz(size_t size)
+{
+	void *ret;
+	if (unsigned_add_overflows(size, 1))
+		die("Data too large to fit into virtual memory space.");
+	ret = xmalloc(size + 1);
+	((char*)ret)[size] = 0;
+	return ret;
+}
+
 /*
  * xmemdupz() allocates (len + 1) bytes of memory, duplicates "len" bytes of
  * "data" to the allocated memory, zero terminates the allocated memory,
@@ -42,10 +83,7 @@
  */
 void *xmemdupz(const void *data, size_t len)
 {
-	char *p = xmalloc(len + 1);
-	memcpy(p, data, len);
-	p[len] = '\0';
-	return p;
+	return memcpy(xmallocz(len), data, len);
 }
 
 char *xstrndup(const char *str, size_t len)
@@ -56,11 +94,14 @@
 
 void *xrealloc(void *ptr, size_t size)
 {
-	void *ret = realloc(ptr, size);
+	void *ret;
+
+	memory_limit_check(size);
+	ret = realloc(ptr, size);
 	if (!ret && !size)
 		ret = realloc(ptr, 1);
 	if (!ret) {
-		release_pack_memory(size, -1);
+		try_to_free_routine(size);
 		ret = realloc(ptr, size);
 		if (!ret && !size)
 			ret = realloc(ptr, 1);
@@ -72,11 +113,14 @@
 
 void *xcalloc(size_t nmemb, size_t size)
 {
-	void *ret = calloc(nmemb, size);
+	void *ret;
+
+	memory_limit_check(size * nmemb);
+	ret = calloc(nmemb, size);
 	if (!ret && (!nmemb || !size))
 		ret = calloc(1, 1);
 	if (!ret) {
-		release_pack_memory(nmemb * size, -1);
+		try_to_free_routine(nmemb * size);
 		ret = calloc(nmemb, size);
 		if (!ret && (!nmemb || !size))
 			ret = calloc(1, 1);
@@ -86,21 +130,6 @@
 	return ret;
 }
 
-void *xmmap(void *start, size_t length,
-	int prot, int flags, int fd, off_t offset)
-{
-	void *ret = mmap(start, length, prot, flags, fd, offset);
-	if (ret == MAP_FAILED) {
-		if (!length)
-			return NULL;
-		release_pack_memory(length, fd);
-		ret = mmap(start, length, prot, flags, fd, offset);
-		if (ret == MAP_FAILED)
-			die_errno("Out of memory? mmap failed");
-	}
-	return ret;
-}
-
 /*
  * xread() is the same a read(), but it automatically restarts read()
  * operations with a recoverable error (EAGAIN and EINTR). xread()
@@ -140,8 +169,10 @@
 
 	while (count > 0) {
 		ssize_t loaded = xread(fd, p, count);
-		if (loaded <= 0)
-			return total ? total : loaded;
+		if (loaded < 0)
+			return -1;
+		if (loaded == 0)
+			return total;
 		count -= loaded;
 		p += loaded;
 		total += loaded;
@@ -190,118 +221,196 @@
 int xmkstemp(char *template)
 {
 	int fd;
+	char origtemplate[PATH_MAX];
+	strlcpy(origtemplate, template, sizeof(origtemplate));
 
 	fd = mkstemp(template);
-	if (fd < 0)
-		die_errno("Unable to create temporary file");
+	if (fd < 0) {
+		int saved_errno = errno;
+		const char *nonrelative_template;
+
+		if (!template[0])
+			template = origtemplate;
+
+		nonrelative_template = absolute_path(template);
+		errno = saved_errno;
+		die_errno("Unable to create temporary file '%s'",
+			nonrelative_template);
+	}
 	return fd;
 }
 
-/*
- * zlib wrappers to make sure we don't silently miss errors
- * at init time.
- */
-void git_inflate_init(z_streamp strm)
+/* git_mkstemp() - create tmp file honoring TMPDIR variable */
+int git_mkstemp(char *path, size_t len, const char *template)
 {
-	const char *err;
+	const char *tmp;
+	size_t n;
 
-	switch (inflateInit(strm)) {
-	case Z_OK:
-		return;
-
-	case Z_MEM_ERROR:
-		err = "out of memory";
-		break;
-	case Z_VERSION_ERROR:
-		err = "wrong version";
-		break;
-	default:
-		err = "error";
+	tmp = getenv("TMPDIR");
+	if (!tmp)
+		tmp = "/tmp";
+	n = snprintf(path, len, "%s/%s", tmp, template);
+	if (len <= n) {
+		errno = ENAMETOOLONG;
+		return -1;
 	}
-	die("inflateInit: %s (%s)", err, strm->msg ? strm->msg : "no message");
+	return mkstemp(path);
 }
 
-void git_inflate_end(z_streamp strm)
+/* git_mkstemps() - create tmp file with suffix honoring TMPDIR variable. */
+int git_mkstemps(char *path, size_t len, const char *template, int suffix_len)
 {
-	if (inflateEnd(strm) != Z_OK)
-		error("inflateEnd: %s", strm->msg ? strm->msg : "failed");
-}
+	const char *tmp;
+	size_t n;
 
-int git_inflate(z_streamp strm, int flush)
-{
-	int ret = inflate(strm, flush);
-	const char *err;
-
-	switch (ret) {
-	/* Out of memory is fatal. */
-	case Z_MEM_ERROR:
-		die("inflate: out of memory");
-
-	/* Data corruption errors: we may want to recover from them (fsck) */
-	case Z_NEED_DICT:
-		err = "needs dictionary"; break;
-	case Z_DATA_ERROR:
-		err = "data stream error"; break;
-	case Z_STREAM_ERROR:
-		err = "stream consistency error"; break;
-	default:
-		err = "unknown error"; break;
-
-	/* Z_BUF_ERROR: normal, needs more space in the output buffer */
-	case Z_BUF_ERROR:
-	case Z_OK:
-	case Z_STREAM_END:
-		return ret;
+	tmp = getenv("TMPDIR");
+	if (!tmp)
+		tmp = "/tmp";
+	n = snprintf(path, len, "%s/%s", tmp, template);
+	if (len <= n) {
+		errno = ENAMETOOLONG;
+		return -1;
 	}
-	error("inflate: %s (%s)", err, strm->msg ? strm->msg : "no message");
-	return ret;
+	return mkstemps(path, suffix_len);
 }
 
-int odb_mkstemp(char *template, size_t limit, const char *pattern)
+/* Adapted from libiberty's mkstemp.c. */
+
+#undef TMP_MAX
+#define TMP_MAX 16384
+
+int git_mkstemps_mode(char *pattern, int suffix_len, int mode)
+{
+	static const char letters[] =
+		"abcdefghijklmnopqrstuvwxyz"
+		"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+		"0123456789";
+	static const int num_letters = 62;
+	uint64_t value;
+	struct timeval tv;
+	char *template;
+	size_t len;
+	int fd, count;
+
+	len = strlen(pattern);
+
+	if (len < 6 + suffix_len) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	if (strncmp(&pattern[len - 6 - suffix_len], "XXXXXX", 6)) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	/*
+	 * Replace pattern's XXXXXX characters with randomness.
+	 * Try TMP_MAX different filenames.
+	 */
+	gettimeofday(&tv, NULL);
+	value = ((size_t)(tv.tv_usec << 16)) ^ tv.tv_sec ^ getpid();
+	template = &pattern[len - 6 - suffix_len];
+	for (count = 0; count < TMP_MAX; ++count) {
+		uint64_t v = value;
+		/* Fill in the random bits. */
+		template[0] = letters[v % num_letters]; v /= num_letters;
+		template[1] = letters[v % num_letters]; v /= num_letters;
+		template[2] = letters[v % num_letters]; v /= num_letters;
+		template[3] = letters[v % num_letters]; v /= num_letters;
+		template[4] = letters[v % num_letters]; v /= num_letters;
+		template[5] = letters[v % num_letters]; v /= num_letters;
+
+		fd = open(pattern, O_CREAT | O_EXCL | O_RDWR, mode);
+		if (fd > 0)
+			return fd;
+		/*
+		 * Fatal error (EPERM, ENOSPC etc).
+		 * It doesn't make sense to loop.
+		 */
+		if (errno != EEXIST)
+			break;
+		/*
+		 * This is a random value.  It is only necessary that
+		 * the next TMP_MAX values generated by adding 7777 to
+		 * VALUE are different with (module 2^32).
+		 */
+		value += 7777;
+	}
+	/* We return the null string if we can't find a unique file name.  */
+	pattern[0] = '\0';
+	return -1;
+}
+
+int git_mkstemp_mode(char *pattern, int mode)
+{
+	/* mkstemp is just mkstemps with no suffix */
+	return git_mkstemps_mode(pattern, 0, mode);
+}
+
+int gitmkstemps(char *pattern, int suffix_len)
+{
+	return git_mkstemps_mode(pattern, suffix_len, 0600);
+}
+
+int xmkstemp_mode(char *template, int mode)
 {
 	int fd;
+	char origtemplate[PATH_MAX];
+	strlcpy(origtemplate, template, sizeof(origtemplate));
 
-	snprintf(template, limit, "%s/%s",
-		 get_object_directory(), pattern);
-	fd = mkstemp(template);
-	if (0 <= fd)
-		return fd;
+	fd = git_mkstemp_mode(template, mode);
+	if (fd < 0) {
+		int saved_errno = errno;
+		const char *nonrelative_template;
 
-	/* slow path */
-	/* some mkstemp implementations erase template on failure */
-	snprintf(template, limit, "%s/%s",
-		 get_object_directory(), pattern);
-	safe_create_leading_directories(template);
-	return xmkstemp(template);
+		if (!template[0])
+			template = origtemplate;
+
+		nonrelative_template = absolute_path(template);
+		errno = saved_errno;
+		die_errno("Unable to create temporary file '%s'",
+			nonrelative_template);
+	}
+	return fd;
 }
 
-int odb_pack_keep(char *name, size_t namesz, unsigned char *sha1)
+static int warn_if_unremovable(const char *op, const char *file, int rc)
 {
-	int fd;
-
-	snprintf(name, namesz, "%s/pack/pack-%s.keep",
-		 get_object_directory(), sha1_to_hex(sha1));
-	fd = open(name, O_RDWR|O_CREAT|O_EXCL, 0600);
-	if (0 <= fd)
-		return fd;
-
-	/* slow path */
-	safe_create_leading_directories(name);
-	return open(name, O_RDWR|O_CREAT|O_EXCL, 0600);
-}
-
-int unlink_or_warn(const char *file)
-{
-	int rc = unlink(file);
-
 	if (rc < 0) {
 		int err = errno;
 		if (ENOENT != err) {
-			warning("unable to unlink %s: %s",
-				file, strerror(errno));
+			warning("unable to %s %s: %s",
+				op, file, strerror(errno));
 			errno = err;
 		}
 	}
 	return rc;
 }
 
+int unlink_or_warn(const char *file)
+{
+	return warn_if_unremovable("unlink", file, unlink(file));
+}
+
+int rmdir_or_warn(const char *file)
+{
+	return warn_if_unremovable("rmdir", file, rmdir(file));
+}
+
+int remove_or_warn(unsigned int mode, const char *file)
+{
+	return S_ISGITLINK(mode) ? rmdir_or_warn(file) : unlink_or_warn(file);
+}
+
+struct passwd *xgetpwuid_self(void)
+{
+	struct passwd *pw;
+
+	errno = 0;
+	pw = getpwuid(getuid());
+	if (!pw)
+		die(_("unable to look up current user in the passwd file: %s"),
+		    errno ? strerror(errno) : _("no such user"));
+	return pw;
+}
diff --git a/ws.c b/ws.c
index 59d0883..b498d75 100644
--- a/ws.c
+++ b/ws.c
@@ -10,12 +10,16 @@
 static struct whitespace_rule {
 	const char *rule_name;
 	unsigned rule_bits;
-	unsigned loosens_error;
+	unsigned loosens_error:1,
+		exclude_default:1;
 } whitespace_rule_names[] = {
 	{ "trailing-space", WS_TRAILING_SPACE, 0 },
 	{ "space-before-tab", WS_SPACE_BEFORE_TAB, 0 },
 	{ "indent-with-non-tab", WS_INDENT_WITH_NON_TAB, 0 },
 	{ "cr-at-eol", WS_CR_AT_EOL, 1 },
+	{ "blank-at-eol", WS_BLANK_AT_EOL, 0 },
+	{ "blank-at-eof", WS_BLANK_AT_EOF, 0 },
+	{ "tab-in-indent", WS_TAB_IN_INDENT, 0, 1 },
 };
 
 unsigned parse_whitespace_rule(const char *string)
@@ -52,8 +56,21 @@
 				rule |= whitespace_rule_names[i].rule_bits;
 			break;
 		}
+		if (strncmp(string, "tabwidth=", 9) == 0) {
+			unsigned tabwidth = atoi(string + 9);
+			if (0 < tabwidth && tabwidth < 0100) {
+				rule &= ~WS_TAB_WIDTH_MASK;
+				rule |= tabwidth;
+			}
+			else
+				warning("tabwidth %.*s out of range",
+					(int)(len - 9), string + 9);
+		}
 		string = ep;
 	}
+
+	if (rule & WS_TAB_IN_INDENT && rule & WS_INDENT_WITH_NON_TAB)
+		die("cannot enforce both tab-in-indent and indent-with-non-tab");
 	return rule;
 }
 
@@ -62,7 +79,7 @@
 	static struct git_attr *attr_whitespace;
 
 	if (!attr_whitespace)
-		attr_whitespace = git_attr("whitespace", 10);
+		attr_whitespace = git_attr("whitespace");
 	check[0].attr = attr_whitespace;
 }
 
@@ -71,21 +88,22 @@
 	struct git_attr_check attr_whitespace_rule;
 
 	setup_whitespace_attr_check(&attr_whitespace_rule);
-	if (!git_checkattr(pathname, 1, &attr_whitespace_rule)) {
+	if (!git_check_attr(pathname, 1, &attr_whitespace_rule)) {
 		const char *value;
 
 		value = attr_whitespace_rule.value;
 		if (ATTR_TRUE(value)) {
 			/* true (whitespace) */
-			unsigned all_rule = 0;
+			unsigned all_rule = ws_tab_width(whitespace_rule_cfg);
 			int i;
 			for (i = 0; i < ARRAY_SIZE(whitespace_rule_names); i++)
-				if (!whitespace_rule_names[i].loosens_error)
+				if (!whitespace_rule_names[i].loosens_error &&
+				    !whitespace_rule_names[i].exclude_default)
 					all_rule |= whitespace_rule_names[i].rule_bits;
 			return all_rule;
 		} else if (ATTR_FALSE(value)) {
 			/* false (-whitespace) */
-			return 0;
+			return ws_tab_width(whitespace_rule_cfg);
 		} else if (ATTR_UNSET(value)) {
 			/* reset to default (!whitespace) */
 			return whitespace_rule_cfg;
@@ -102,8 +120,17 @@
 char *whitespace_error_string(unsigned ws)
 {
 	struct strbuf err = STRBUF_INIT;
-	if (ws & WS_TRAILING_SPACE)
+	if ((ws & WS_TRAILING_SPACE) == WS_TRAILING_SPACE)
 		strbuf_addstr(&err, "trailing whitespace");
+	else {
+		if (ws & WS_BLANK_AT_EOL)
+			strbuf_addstr(&err, "trailing whitespace");
+		if (ws & WS_BLANK_AT_EOF) {
+			if (err.len)
+				strbuf_addstr(&err, ", ");
+			strbuf_addstr(&err, "new blank line at EOF");
+		}
+	}
 	if (ws & WS_SPACE_BEFORE_TAB) {
 		if (err.len)
 			strbuf_addstr(&err, ", ");
@@ -114,6 +141,11 @@
 			strbuf_addstr(&err, ", ");
 		strbuf_addstr(&err, "indent with spaces");
 	}
+	if (ws & WS_TAB_IN_INDENT) {
+		if (err.len)
+			strbuf_addstr(&err, ", ");
+		strbuf_addstr(&err, "tab in indent");
+	}
 	return strbuf_detach(&err, NULL);
 }
 
@@ -141,19 +173,22 @@
 	}
 
 	/* Check for trailing whitespace. */
-	if (ws_rule & WS_TRAILING_SPACE) {
+	if (ws_rule & WS_BLANK_AT_EOL) {
 		for (i = len - 1; i >= 0; i--) {
 			if (isspace(line[i])) {
 				trailing_whitespace = i;
-				result |= WS_TRAILING_SPACE;
+				result |= WS_BLANK_AT_EOL;
 			}
 			else
 				break;
 		}
 	}
 
-	/* Check for space before tab in initial indent. */
-	for (i = 0; i < len; i++) {
+	if (trailing_whitespace == -1)
+		trailing_whitespace = len;
+
+	/* Check indentation */
+	for (i = 0; i < trailing_whitespace; i++) {
 		if (line[i] == ' ')
 			continue;
 		if (line[i] != '\t')
@@ -164,16 +199,24 @@
 				fputs(ws, stream);
 				fwrite(line + written, i - written, 1, stream);
 				fputs(reset, stream);
+				fwrite(line + i, 1, 1, stream);
 			}
-		} else if (stream)
-			fwrite(line + written, i - written, 1, stream);
-		if (stream)
-			fwrite(line + i, 1, 1, stream);
+		} else if (ws_rule & WS_TAB_IN_INDENT) {
+			result |= WS_TAB_IN_INDENT;
+			if (stream) {
+				fwrite(line + written, i - written, 1, stream);
+				fputs(ws, stream);
+				fwrite(line + i, 1, 1, stream);
+				fputs(reset, stream);
+			}
+		} else if (stream) {
+			fwrite(line + written, i - written + 1, 1, stream);
+		}
 		written = i + 1;
 	}
 
 	/* Check for indent using non-tab. */
-	if ((ws_rule & WS_INDENT_WITH_NON_TAB) && i - written >= 8) {
+	if ((ws_rule & WS_INDENT_WITH_NON_TAB) && i - written >= ws_tab_width(ws_rule)) {
 		result |= WS_INDENT_WITH_NON_TAB;
 		if (stream) {
 			fputs(ws, stream);
@@ -188,8 +231,6 @@
 		 * Now the rest of the line starts at "written".
 		 * The non-highlighted part ends at "trailing_whitespace".
 		 */
-		if (trailing_whitespace == -1)
-			trailing_whitespace = len;
 
 		/* Emit non-highlighted (middle) segment. */
 		if (trailing_whitespace - written > 0) {
@@ -241,8 +282,8 @@
 	return 1;
 }
 
-/* Copy the line to the buffer while fixing whitespaces */
-int ws_fix_copy(char *dst, const char *src, int len, unsigned ws_rule, int *error_count)
+/* Copy the line onto the end of the strbuf while fixing whitespaces */
+void ws_fix_copy(struct strbuf *dst, const char *src, int len, unsigned ws_rule, int *error_count)
 {
 	/*
 	 * len is number of bytes to be copied from src, starting
@@ -256,12 +297,11 @@
 	int last_tab_in_indent = -1;
 	int last_space_in_indent = -1;
 	int need_fix_leading_space = 0;
-	char *buf;
 
 	/*
 	 * Strip trailing whitespace
 	 */
-	if (ws_rule & WS_TRAILING_SPACE) {
+	if (ws_rule & WS_BLANK_AT_EOL) {
 		if (0 < len && src[len - 1] == '\n') {
 			add_nl_to_tail = 1;
 			len--;
@@ -290,13 +330,12 @@
 		} else if (ch == ' ') {
 			last_space_in_indent = i;
 			if ((ws_rule & WS_INDENT_WITH_NON_TAB) &&
-			    8 <= i - last_tab_in_indent)
+			    ws_tab_width(ws_rule) <= i - last_tab_in_indent)
 				need_fix_leading_space = 1;
 		} else
 			break;
 	}
 
-	buf = dst;
 	if (need_fix_leading_space) {
 		/* Process indent ourselves */
 		int consecutive_spaces = 0;
@@ -318,28 +357,42 @@
 			char ch = src[i];
 			if (ch != ' ') {
 				consecutive_spaces = 0;
-				*dst++ = ch;
+				strbuf_addch(dst, ch);
 			} else {
 				consecutive_spaces++;
-				if (consecutive_spaces == 8) {
-					*dst++ = '\t';
+				if (consecutive_spaces == ws_tab_width(ws_rule)) {
+					strbuf_addch(dst, '\t');
 					consecutive_spaces = 0;
 				}
 			}
 		}
 		while (0 < consecutive_spaces--)
-			*dst++ = ' ';
+			strbuf_addch(dst, ' ');
+		len -= last;
+		src += last;
+		fixed = 1;
+	} else if ((ws_rule & WS_TAB_IN_INDENT) && last_tab_in_indent >= 0) {
+		/* Expand tabs into spaces */
+		int start = dst->len;
+		int last = last_tab_in_indent + 1;
+		for (i = 0; i < last; i++) {
+			if (src[i] == '\t')
+				do {
+					strbuf_addch(dst, ' ');
+				} while ((dst->len - start) % ws_tab_width(ws_rule));
+			else
+				strbuf_addch(dst, src[i]);
+		}
 		len -= last;
 		src += last;
 		fixed = 1;
 	}
 
-	memcpy(dst, src, len);
+	strbuf_add(dst, src, len);
 	if (add_cr_to_tail)
-		dst[len++] = '\r';
+		strbuf_addch(dst, '\r');
 	if (add_nl_to_tail)
-		dst[len++] = '\n';
+		strbuf_addch(dst, '\n');
 	if (fixed && error_count)
 		(*error_count)++;
-	return dst + len - buf;
 }
diff --git a/wt-status.c b/wt-status.c
index 47735d8..c110cbc 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1,6 +1,5 @@
 #include "cache.h"
 #include "wt-status.h"
-#include "color.h"
 #include "object.h"
 #include "dir.h"
 #include "commit.h"
@@ -10,225 +9,656 @@
 #include "quote.h"
 #include "run-command.h"
 #include "remote.h"
+#include "refs.h"
+#include "submodule.h"
+#include "column.h"
+#include "strbuf.h"
 
-int wt_status_relative_paths = 1;
-int wt_status_use_color = -1;
-static int wt_status_submodule_summary;
-static char wt_status_colors[][COLOR_MAXLEN] = {
+static char default_wt_status_colors[][COLOR_MAXLEN] = {
 	GIT_COLOR_NORMAL, /* WT_STATUS_HEADER */
 	GIT_COLOR_GREEN,  /* WT_STATUS_UPDATED */
 	GIT_COLOR_RED,    /* WT_STATUS_CHANGED */
 	GIT_COLOR_RED,    /* WT_STATUS_UNTRACKED */
 	GIT_COLOR_RED,    /* WT_STATUS_NOBRANCH */
+	GIT_COLOR_RED,    /* WT_STATUS_UNMERGED */
+	GIT_COLOR_GREEN,  /* WT_STATUS_LOCAL_BRANCH */
+	GIT_COLOR_RED,    /* WT_STATUS_REMOTE_BRANCH */
+	GIT_COLOR_NIL,    /* WT_STATUS_ONBRANCH */
 };
 
-enum untracked_status_type show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
-
-static int parse_status_slot(const char *var, int offset)
+static const char *color(int slot, struct wt_status *s)
 {
-	if (!strcasecmp(var+offset, "header"))
-		return WT_STATUS_HEADER;
-	if (!strcasecmp(var+offset, "updated")
-		|| !strcasecmp(var+offset, "added"))
-		return WT_STATUS_UPDATED;
-	if (!strcasecmp(var+offset, "changed"))
-		return WT_STATUS_CHANGED;
-	if (!strcasecmp(var+offset, "untracked"))
-		return WT_STATUS_UNTRACKED;
-	if (!strcasecmp(var+offset, "nobranch"))
-		return WT_STATUS_NOBRANCH;
-	die("bad config variable '%s'", var);
+	const char *c = "";
+	if (want_color(s->use_color))
+		c = s->color_palette[slot];
+	if (slot == WT_STATUS_ONBRANCH && color_is_nil(c))
+		c = s->color_palette[WT_STATUS_HEADER];
+	return c;
 }
 
-static const char *color(int slot)
+static void status_vprintf(struct wt_status *s, int at_bol, const char *color,
+		const char *fmt, va_list ap, const char *trail)
 {
-	return wt_status_use_color > 0 ? wt_status_colors[slot] : "";
+	struct strbuf sb = STRBUF_INIT;
+	struct strbuf linebuf = STRBUF_INIT;
+	const char *line, *eol;
+
+	strbuf_vaddf(&sb, fmt, ap);
+	if (!sb.len) {
+		strbuf_addch(&sb, '#');
+		if (!trail)
+			strbuf_addch(&sb, ' ');
+		color_print_strbuf(s->fp, color, &sb);
+		if (trail)
+			fprintf(s->fp, "%s", trail);
+		strbuf_release(&sb);
+		return;
+	}
+	for (line = sb.buf; *line; line = eol + 1) {
+		eol = strchr(line, '\n');
+
+		strbuf_reset(&linebuf);
+		if (at_bol) {
+			strbuf_addch(&linebuf, '#');
+			if (*line != '\n' && *line != '\t')
+				strbuf_addch(&linebuf, ' ');
+		}
+		if (eol)
+			strbuf_add(&linebuf, line, eol - line);
+		else
+			strbuf_addstr(&linebuf, line);
+		color_print_strbuf(s->fp, color, &linebuf);
+		if (eol)
+			fprintf(s->fp, "\n");
+		else
+			break;
+		at_bol = 1;
+	}
+	if (trail)
+		fprintf(s->fp, "%s", trail);
+	strbuf_release(&linebuf);
+	strbuf_release(&sb);
+}
+
+void status_printf_ln(struct wt_status *s, const char *color,
+			const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	status_vprintf(s, 1, color, fmt, ap, "\n");
+	va_end(ap);
+}
+
+void status_printf(struct wt_status *s, const char *color,
+			const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	status_vprintf(s, 1, color, fmt, ap, NULL);
+	va_end(ap);
+}
+
+void status_printf_more(struct wt_status *s, const char *color,
+			const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	status_vprintf(s, 0, color, fmt, ap, NULL);
+	va_end(ap);
 }
 
 void wt_status_prepare(struct wt_status *s)
 {
 	unsigned char sha1[20];
-	const char *head;
 
 	memset(s, 0, sizeof(*s));
-	head = resolve_ref("HEAD", sha1, 0, NULL);
-	s->branch = head ? xstrdup(head) : NULL;
+	memcpy(s->color_palette, default_wt_status_colors,
+	       sizeof(default_wt_status_colors));
+	s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
+	s->use_color = -1;
+	s->relative_paths = 1;
+	s->branch = resolve_refdup("HEAD", sha1, 0, NULL);
 	s->reference = "HEAD";
 	s->fp = stdout;
 	s->index_file = get_index_file();
+	s->change.strdup_strings = 1;
+	s->untracked.strdup_strings = 1;
+	s->ignored.strdup_strings = 1;
+}
+
+static void wt_status_print_unmerged_header(struct wt_status *s)
+{
+	int i;
+	int del_mod_conflict = 0;
+	int both_deleted = 0;
+	int not_deleted = 0;
+	const char *c = color(WT_STATUS_HEADER, s);
+
+	status_printf_ln(s, c, _("Unmerged paths:"));
+
+	for (i = 0; i < s->change.nr; i++) {
+		struct string_list_item *it = &(s->change.items[i]);
+		struct wt_status_change_data *d = it->util;
+
+		switch (d->stagemask) {
+		case 0:
+			break;
+		case 1:
+			both_deleted = 1;
+			break;
+		case 3:
+		case 5:
+			del_mod_conflict = 1;
+			break;
+		default:
+			not_deleted = 1;
+			break;
+		}
+	}
+
+	if (!advice_status_hints)
+		return;
+	if (s->whence != FROM_COMMIT)
+		;
+	else if (!s->is_initial)
+		status_printf_ln(s, c, _("  (use \"git reset %s <file>...\" to unstage)"), s->reference);
+	else
+		status_printf_ln(s, c, _("  (use \"git rm --cached <file>...\" to unstage)"));
+
+	if (!both_deleted) {
+		if (!del_mod_conflict)
+			status_printf_ln(s, c, _("  (use \"git add <file>...\" to mark resolution)"));
+		else
+			status_printf_ln(s, c, _("  (use \"git add/rm <file>...\" as appropriate to mark resolution)"));
+	} else if (!del_mod_conflict && !not_deleted) {
+		status_printf_ln(s, c, _("  (use \"git rm <file>...\" to mark resolution)"));
+	} else {
+		status_printf_ln(s, c, _("  (use \"git add/rm <file>...\" as appropriate to mark resolution)"));
+	}
+	status_printf_ln(s, c, "");
 }
 
 static void wt_status_print_cached_header(struct wt_status *s)
 {
-	const char *c = color(WT_STATUS_HEADER);
-	color_fprintf_ln(s->fp, c, "# Changes to be committed:");
-	if (!s->is_initial) {
-		color_fprintf_ln(s->fp, c, "#   (use \"git reset %s <file>...\" to unstage)", s->reference);
-	} else {
-		color_fprintf_ln(s->fp, c, "#   (use \"git rm --cached <file>...\" to unstage)");
-	}
-	color_fprintf_ln(s->fp, c, "#");
+	const char *c = color(WT_STATUS_HEADER, s);
+
+	status_printf_ln(s, c, _("Changes to be committed:"));
+	if (!advice_status_hints)
+		return;
+	if (s->whence != FROM_COMMIT)
+		; /* NEEDSWORK: use "git reset --unresolve"??? */
+	else if (!s->is_initial)
+		status_printf_ln(s, c, _("  (use \"git reset %s <file>...\" to unstage)"), s->reference);
+	else
+		status_printf_ln(s, c, _("  (use \"git rm --cached <file>...\" to unstage)"));
+	status_printf_ln(s, c, "");
 }
 
 static void wt_status_print_dirty_header(struct wt_status *s,
-					 int has_deleted)
+					 int has_deleted,
+					 int has_dirty_submodules)
 {
-	const char *c = color(WT_STATUS_HEADER);
-	color_fprintf_ln(s->fp, c, "# Changed but not updated:");
+	const char *c = color(WT_STATUS_HEADER, s);
+
+	status_printf_ln(s, c, _("Changes not staged for commit:"));
+	if (!advice_status_hints)
+		return;
 	if (!has_deleted)
-		color_fprintf_ln(s->fp, c, "#   (use \"git add <file>...\" to update what will be committed)");
+		status_printf_ln(s, c, _("  (use \"git add <file>...\" to update what will be committed)"));
 	else
-		color_fprintf_ln(s->fp, c, "#   (use \"git add/rm <file>...\" to update what will be committed)");
-	color_fprintf_ln(s->fp, c, "#   (use \"git checkout -- <file>...\" to discard changes in working directory)");
-	color_fprintf_ln(s->fp, c, "#");
+		status_printf_ln(s, c, _("  (use \"git add/rm <file>...\" to update what will be committed)"));
+	status_printf_ln(s, c, _("  (use \"git checkout -- <file>...\" to discard changes in working directory)"));
+	if (has_dirty_submodules)
+		status_printf_ln(s, c, _("  (commit or discard the untracked or modified content in submodules)"));
+	status_printf_ln(s, c, "");
 }
 
-static void wt_status_print_untracked_header(struct wt_status *s)
+static void wt_status_print_other_header(struct wt_status *s,
+					 const char *what,
+					 const char *how)
 {
-	const char *c = color(WT_STATUS_HEADER);
-	color_fprintf_ln(s->fp, c, "# Untracked files:");
-	color_fprintf_ln(s->fp, c, "#   (use \"git add <file>...\" to include in what will be committed)");
-	color_fprintf_ln(s->fp, c, "#");
+	const char *c = color(WT_STATUS_HEADER, s);
+	status_printf_ln(s, c, _("%s files:"), what);
+	if (!advice_status_hints)
+		return;
+	status_printf_ln(s, c, _("  (use \"git %s <file>...\" to include in what will be committed)"), how);
+	status_printf_ln(s, c, "");
 }
 
 static void wt_status_print_trailer(struct wt_status *s)
 {
-	color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
+	status_printf_ln(s, color(WT_STATUS_HEADER, s), "");
 }
 
 #define quote_path quote_path_relative
 
-static void wt_status_print_filepair(struct wt_status *s,
-				     int t, struct diff_filepair *p)
+static void wt_status_print_unmerged_data(struct wt_status *s,
+					  struct string_list_item *it)
 {
-	const char *c = color(t);
+	const char *c = color(WT_STATUS_UNMERGED, s);
+	struct wt_status_change_data *d = it->util;
+	struct strbuf onebuf = STRBUF_INIT;
+	const char *one, *how = _("bug");
+
+	one = quote_path(it->string, -1, &onebuf, s->prefix);
+	status_printf(s, color(WT_STATUS_HEADER, s), "\t");
+	switch (d->stagemask) {
+	case 1: how = _("both deleted:"); break;
+	case 2: how = _("added by us:"); break;
+	case 3: how = _("deleted by them:"); break;
+	case 4: how = _("added by them:"); break;
+	case 5: how = _("deleted by us:"); break;
+	case 6: how = _("both added:"); break;
+	case 7: how = _("both modified:"); break;
+	}
+	status_printf_more(s, c, "%-20s%s\n", how, one);
+	strbuf_release(&onebuf);
+}
+
+static void wt_status_print_change_data(struct wt_status *s,
+					int change_type,
+					struct string_list_item *it)
+{
+	struct wt_status_change_data *d = it->util;
+	const char *c = color(change_type, s);
+	int status = status;
+	char *one_name;
+	char *two_name;
 	const char *one, *two;
 	struct strbuf onebuf = STRBUF_INIT, twobuf = STRBUF_INIT;
+	struct strbuf extra = STRBUF_INIT;
 
-	one = quote_path(p->one->path, -1, &onebuf, s->prefix);
-	two = quote_path(p->two->path, -1, &twobuf, s->prefix);
+	one_name = two_name = it->string;
+	switch (change_type) {
+	case WT_STATUS_UPDATED:
+		status = d->index_status;
+		if (d->head_path)
+			one_name = d->head_path;
+		break;
+	case WT_STATUS_CHANGED:
+		if (d->new_submodule_commits || d->dirty_submodule) {
+			strbuf_addstr(&extra, " (");
+			if (d->new_submodule_commits)
+				strbuf_addf(&extra, _("new commits, "));
+			if (d->dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
+				strbuf_addf(&extra, _("modified content, "));
+			if (d->dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)
+				strbuf_addf(&extra, _("untracked content, "));
+			strbuf_setlen(&extra, extra.len - 2);
+			strbuf_addch(&extra, ')');
+		}
+		status = d->worktree_status;
+		break;
+	}
 
-	color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t");
-	switch (p->status) {
+	one = quote_path(one_name, -1, &onebuf, s->prefix);
+	two = quote_path(two_name, -1, &twobuf, s->prefix);
+
+	status_printf(s, color(WT_STATUS_HEADER, s), "\t");
+	switch (status) {
 	case DIFF_STATUS_ADDED:
-		color_fprintf(s->fp, c, "new file:   %s", one);
+		status_printf_more(s, c, _("new file:   %s"), one);
 		break;
 	case DIFF_STATUS_COPIED:
-		color_fprintf(s->fp, c, "copied:     %s -> %s", one, two);
+		status_printf_more(s, c, _("copied:     %s -> %s"), one, two);
 		break;
 	case DIFF_STATUS_DELETED:
-		color_fprintf(s->fp, c, "deleted:    %s", one);
+		status_printf_more(s, c, _("deleted:    %s"), one);
 		break;
 	case DIFF_STATUS_MODIFIED:
-		color_fprintf(s->fp, c, "modified:   %s", one);
+		status_printf_more(s, c, _("modified:   %s"), one);
 		break;
 	case DIFF_STATUS_RENAMED:
-		color_fprintf(s->fp, c, "renamed:    %s -> %s", one, two);
+		status_printf_more(s, c, _("renamed:    %s -> %s"), one, two);
 		break;
 	case DIFF_STATUS_TYPE_CHANGED:
-		color_fprintf(s->fp, c, "typechange: %s", one);
+		status_printf_more(s, c, _("typechange: %s"), one);
 		break;
 	case DIFF_STATUS_UNKNOWN:
-		color_fprintf(s->fp, c, "unknown:    %s", one);
+		status_printf_more(s, c, _("unknown:    %s"), one);
 		break;
 	case DIFF_STATUS_UNMERGED:
-		color_fprintf(s->fp, c, "unmerged:   %s", one);
+		status_printf_more(s, c, _("unmerged:   %s"), one);
 		break;
 	default:
-		die("bug: unhandled diff status %c", p->status);
+		die(_("bug: unhandled diff status %c"), status);
 	}
-	fprintf(s->fp, "\n");
+	if (extra.len) {
+		status_printf_more(s, color(WT_STATUS_HEADER, s), "%s", extra.buf);
+		strbuf_release(&extra);
+	}
+	status_printf_more(s, GIT_COLOR_NORMAL, "\n");
 	strbuf_release(&onebuf);
 	strbuf_release(&twobuf);
 }
 
-static void wt_status_print_updated_cb(struct diff_queue_struct *q,
-		struct diff_options *options,
-		void *data)
+static void wt_status_collect_changed_cb(struct diff_queue_struct *q,
+					 struct diff_options *options,
+					 void *data)
 {
 	struct wt_status *s = data;
+	int i;
+
+	if (!q->nr)
+		return;
+	s->workdir_dirty = 1;
+	for (i = 0; i < q->nr; i++) {
+		struct diff_filepair *p;
+		struct string_list_item *it;
+		struct wt_status_change_data *d;
+
+		p = q->queue[i];
+		it = string_list_insert(&s->change, p->one->path);
+		d = it->util;
+		if (!d) {
+			d = xcalloc(1, sizeof(*d));
+			it->util = d;
+		}
+		if (!d->worktree_status)
+			d->worktree_status = p->status;
+		d->dirty_submodule = p->two->dirty_submodule;
+		if (S_ISGITLINK(p->two->mode))
+			d->new_submodule_commits = !!hashcmp(p->one->sha1, p->two->sha1);
+	}
+}
+
+static int unmerged_mask(const char *path)
+{
+	int pos, mask;
+	struct cache_entry *ce;
+
+	pos = cache_name_pos(path, strlen(path));
+	if (0 <= pos)
+		return 0;
+
+	mask = 0;
+	pos = -pos-1;
+	while (pos < active_nr) {
+		ce = active_cache[pos++];
+		if (strcmp(ce->name, path) || !ce_stage(ce))
+			break;
+		mask |= (1 << (ce_stage(ce) - 1));
+	}
+	return mask;
+}
+
+static void wt_status_collect_updated_cb(struct diff_queue_struct *q,
+					 struct diff_options *options,
+					 void *data)
+{
+	struct wt_status *s = data;
+	int i;
+
+	for (i = 0; i < q->nr; i++) {
+		struct diff_filepair *p;
+		struct string_list_item *it;
+		struct wt_status_change_data *d;
+
+		p = q->queue[i];
+		it = string_list_insert(&s->change, p->two->path);
+		d = it->util;
+		if (!d) {
+			d = xcalloc(1, sizeof(*d));
+			it->util = d;
+		}
+		if (!d->index_status)
+			d->index_status = p->status;
+		switch (p->status) {
+		case DIFF_STATUS_COPIED:
+		case DIFF_STATUS_RENAMED:
+			d->head_path = xstrdup(p->one->path);
+			break;
+		case DIFF_STATUS_UNMERGED:
+			d->stagemask = unmerged_mask(p->two->path);
+			break;
+		}
+	}
+}
+
+static void wt_status_collect_changes_worktree(struct wt_status *s)
+{
+	struct rev_info rev;
+
+	init_revisions(&rev, NULL);
+	setup_revisions(0, NULL, &rev, NULL);
+	rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
+	DIFF_OPT_SET(&rev.diffopt, DIRTY_SUBMODULES);
+	if (!s->show_untracked_files)
+		DIFF_OPT_SET(&rev.diffopt, IGNORE_UNTRACKED_IN_SUBMODULES);
+	if (s->ignore_submodule_arg) {
+		DIFF_OPT_SET(&rev.diffopt, OVERRIDE_SUBMODULE_CONFIG);
+		handle_ignore_submodules_arg(&rev.diffopt, s->ignore_submodule_arg);
+	}
+	rev.diffopt.format_callback = wt_status_collect_changed_cb;
+	rev.diffopt.format_callback_data = s;
+	init_pathspec(&rev.prune_data, s->pathspec);
+	run_diff_files(&rev, 0);
+}
+
+static void wt_status_collect_changes_index(struct wt_status *s)
+{
+	struct rev_info rev;
+	struct setup_revision_opt opt;
+
+	init_revisions(&rev, NULL);
+	memset(&opt, 0, sizeof(opt));
+	opt.def = s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference;
+	setup_revisions(0, NULL, &rev, &opt);
+
+	if (s->ignore_submodule_arg) {
+		DIFF_OPT_SET(&rev.diffopt, OVERRIDE_SUBMODULE_CONFIG);
+		handle_ignore_submodules_arg(&rev.diffopt, s->ignore_submodule_arg);
+	}
+
+	rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
+	rev.diffopt.format_callback = wt_status_collect_updated_cb;
+	rev.diffopt.format_callback_data = s;
+	rev.diffopt.detect_rename = 1;
+	rev.diffopt.rename_limit = 200;
+	rev.diffopt.break_opt = 0;
+	init_pathspec(&rev.prune_data, s->pathspec);
+	run_diff_index(&rev, 1);
+}
+
+static void wt_status_collect_changes_initial(struct wt_status *s)
+{
+	struct pathspec pathspec;
+	int i;
+
+	init_pathspec(&pathspec, s->pathspec);
+	for (i = 0; i < active_nr; i++) {
+		struct string_list_item *it;
+		struct wt_status_change_data *d;
+		struct cache_entry *ce = active_cache[i];
+
+		if (!ce_path_match(ce, &pathspec))
+			continue;
+		it = string_list_insert(&s->change, ce->name);
+		d = it->util;
+		if (!d) {
+			d = xcalloc(1, sizeof(*d));
+			it->util = d;
+		}
+		if (ce_stage(ce)) {
+			d->index_status = DIFF_STATUS_UNMERGED;
+			d->stagemask |= (1 << (ce_stage(ce) - 1));
+		}
+		else
+			d->index_status = DIFF_STATUS_ADDED;
+	}
+	free_pathspec(&pathspec);
+}
+
+static void wt_status_collect_untracked(struct wt_status *s)
+{
+	int i;
+	struct dir_struct dir;
+
+	if (!s->show_untracked_files)
+		return;
+	memset(&dir, 0, sizeof(dir));
+	if (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES)
+		dir.flags |=
+			DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;
+	setup_standard_excludes(&dir);
+
+	fill_directory(&dir, s->pathspec);
+	for (i = 0; i < dir.nr; i++) {
+		struct dir_entry *ent = dir.entries[i];
+		if (cache_name_is_other(ent->name, ent->len) &&
+		    match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL))
+			string_list_insert(&s->untracked, ent->name);
+		free(ent);
+	}
+
+	if (s->show_ignored_files) {
+		dir.nr = 0;
+		dir.flags = DIR_SHOW_IGNORED | DIR_SHOW_OTHER_DIRECTORIES;
+		fill_directory(&dir, s->pathspec);
+		for (i = 0; i < dir.nr; i++) {
+			struct dir_entry *ent = dir.entries[i];
+			if (cache_name_is_other(ent->name, ent->len) &&
+			    match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL))
+				string_list_insert(&s->ignored, ent->name);
+			free(ent);
+		}
+	}
+
+	free(dir.entries);
+}
+
+void wt_status_collect(struct wt_status *s)
+{
+	wt_status_collect_changes_worktree(s);
+
+	if (s->is_initial)
+		wt_status_collect_changes_initial(s);
+	else
+		wt_status_collect_changes_index(s);
+	wt_status_collect_untracked(s);
+}
+
+static void wt_status_print_unmerged(struct wt_status *s)
+{
 	int shown_header = 0;
 	int i;
-	for (i = 0; i < q->nr; i++) {
-		if (q->queue[i]->status == 'U')
+
+	for (i = 0; i < s->change.nr; i++) {
+		struct wt_status_change_data *d;
+		struct string_list_item *it;
+		it = &(s->change.items[i]);
+		d = it->util;
+		if (!d->stagemask)
+			continue;
+		if (!shown_header) {
+			wt_status_print_unmerged_header(s);
+			shown_header = 1;
+		}
+		wt_status_print_unmerged_data(s, it);
+	}
+	if (shown_header)
+		wt_status_print_trailer(s);
+
+}
+
+static void wt_status_print_updated(struct wt_status *s)
+{
+	int shown_header = 0;
+	int i;
+
+	for (i = 0; i < s->change.nr; i++) {
+		struct wt_status_change_data *d;
+		struct string_list_item *it;
+		it = &(s->change.items[i]);
+		d = it->util;
+		if (!d->index_status ||
+		    d->index_status == DIFF_STATUS_UNMERGED)
 			continue;
 		if (!shown_header) {
 			wt_status_print_cached_header(s);
 			s->commitable = 1;
 			shown_header = 1;
 		}
-		wt_status_print_filepair(s, WT_STATUS_UPDATED, q->queue[i]);
+		wt_status_print_change_data(s, WT_STATUS_UPDATED, it);
 	}
 	if (shown_header)
 		wt_status_print_trailer(s);
 }
 
-static void wt_status_print_changed_cb(struct diff_queue_struct *q,
-                        struct diff_options *options,
-                        void *data)
+/*
+ * -1 : has delete
+ *  0 : no change
+ *  1 : some change but no delete
+ */
+static int wt_status_check_worktree_changes(struct wt_status *s,
+					     int *dirty_submodules)
 {
-	struct wt_status *s = data;
 	int i;
-	if (q->nr) {
-		int has_deleted = 0;
-		s->workdir_dirty = 1;
-		for (i = 0; i < q->nr; i++)
-			if (q->queue[i]->status == DIFF_STATUS_DELETED) {
-				has_deleted = 1;
-				break;
-			}
-		wt_status_print_dirty_header(s, has_deleted);
-	}
-	for (i = 0; i < q->nr; i++)
-		wt_status_print_filepair(s, WT_STATUS_CHANGED, q->queue[i]);
-	if (q->nr)
-		wt_status_print_trailer(s);
-}
+	int changes = 0;
 
-static void wt_status_print_updated(struct wt_status *s)
-{
-	struct rev_info rev;
-	init_revisions(&rev, NULL);
-	setup_revisions(0, NULL, &rev,
-		s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference);
-	rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
-	rev.diffopt.format_callback = wt_status_print_updated_cb;
-	rev.diffopt.format_callback_data = s;
-	rev.diffopt.detect_rename = 1;
-	rev.diffopt.rename_limit = 200;
-	rev.diffopt.break_opt = 0;
-	run_diff_index(&rev, 1);
+	*dirty_submodules = 0;
+
+	for (i = 0; i < s->change.nr; i++) {
+		struct wt_status_change_data *d;
+		d = s->change.items[i].util;
+		if (!d->worktree_status ||
+		    d->worktree_status == DIFF_STATUS_UNMERGED)
+			continue;
+		if (!changes)
+			changes = 1;
+		if (d->dirty_submodule)
+			*dirty_submodules = 1;
+		if (d->worktree_status == DIFF_STATUS_DELETED)
+			changes = -1;
+	}
+	return changes;
 }
 
 static void wt_status_print_changed(struct wt_status *s)
 {
-	struct rev_info rev;
-	init_revisions(&rev, "");
-	setup_revisions(0, NULL, &rev, NULL);
-	rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
-	rev.diffopt.format_callback = wt_status_print_changed_cb;
-	rev.diffopt.format_callback_data = s;
-	run_diff_files(&rev, 0);
+	int i, dirty_submodules;
+	int worktree_changes = wt_status_check_worktree_changes(s, &dirty_submodules);
+
+	if (!worktree_changes)
+		return;
+
+	wt_status_print_dirty_header(s, worktree_changes < 0, dirty_submodules);
+
+	for (i = 0; i < s->change.nr; i++) {
+		struct wt_status_change_data *d;
+		struct string_list_item *it;
+		it = &(s->change.items[i]);
+		d = it->util;
+		if (!d->worktree_status ||
+		    d->worktree_status == DIFF_STATUS_UNMERGED)
+			continue;
+		wt_status_print_change_data(s, WT_STATUS_CHANGED, it);
+	}
+	wt_status_print_trailer(s);
 }
 
-static void wt_status_print_submodule_summary(struct wt_status *s)
+static void wt_status_print_submodule_summary(struct wt_status *s, int uncommitted)
 {
 	struct child_process sm_summary;
 	char summary_limit[64];
 	char index[PATH_MAX];
-	const char *env[] = { index, NULL };
-	const char *argv[] = {
-		"submodule",
-		"summary",
-		"--cached",
-		"--for-status",
-		"--summary-limit",
-		summary_limit,
-		s->amend ? "HEAD^" : "HEAD",
-		NULL
-	};
+	const char *env[] = { NULL, NULL };
+	const char *argv[8];
 
-	sprintf(summary_limit, "%d", wt_status_submodule_summary);
+	env[0] =	index;
+	argv[0] =	"submodule";
+	argv[1] =	"summary";
+	argv[2] =	uncommitted ? "--files" : "--cached";
+	argv[3] =	"--for-status";
+	argv[4] =	"--summary-limit";
+	argv[5] =	summary_limit;
+	argv[6] =	uncommitted ? NULL : (s->amend ? "HEAD^" : "HEAD");
+	argv[7] =	NULL;
+
+	sprintf(summary_limit, "%d", s->submodule_summary);
 	snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", s->index_file);
 
 	memset(&sm_summary, 0, sizeof(sm_summary));
@@ -241,46 +671,65 @@
 	run_command(&sm_summary);
 }
 
-static void wt_status_print_untracked(struct wt_status *s)
+static void wt_status_print_other(struct wt_status *s,
+				  struct string_list *l,
+				  const char *what,
+				  const char *how)
 {
-	struct dir_struct dir;
 	int i;
-	int shown_header = 0;
 	struct strbuf buf = STRBUF_INIT;
+	static struct string_list output = STRING_LIST_INIT_DUP;
+	struct column_options copts;
 
-	memset(&dir, 0, sizeof(dir));
+	if (!l->nr)
+		return;
 
-	if (!s->untracked)
-		dir.flags |=
-			DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;
-	setup_standard_excludes(&dir);
+	wt_status_print_other_header(s, what, how);
 
-	fill_directory(&dir, NULL);
-	for(i = 0; i < dir.nr; i++) {
-		struct dir_entry *ent = dir.entries[i];
-		if (!cache_name_is_other(ent->name, ent->len))
+	for (i = 0; i < l->nr; i++) {
+		struct string_list_item *it;
+		const char *path;
+		it = &(l->items[i]);
+		path = quote_path(it->string, strlen(it->string),
+				  &buf, s->prefix);
+		if (column_active(s->colopts)) {
+			string_list_append(&output, path);
 			continue;
-		if (!shown_header) {
-			s->workdir_untracked = 1;
-			wt_status_print_untracked_header(s);
-			shown_header = 1;
 		}
-		color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t");
-		color_fprintf_ln(s->fp, color(WT_STATUS_UNTRACKED), "%s",
-				quote_path(ent->name, ent->len,
-					&buf, s->prefix));
+		status_printf(s, color(WT_STATUS_HEADER, s), "\t");
+		status_printf_more(s, color(WT_STATUS_UNTRACKED, s),
+				   "%s\n", path);
 	}
+
+	strbuf_release(&buf);
+	if (!column_active(s->colopts))
+		return;
+
+	strbuf_addf(&buf, "%s#\t%s",
+		    color(WT_STATUS_HEADER, s),
+		    color(WT_STATUS_UNTRACKED, s));
+	memset(&copts, 0, sizeof(copts));
+	copts.padding = 1;
+	copts.indent = buf.buf;
+	if (want_color(s->use_color))
+		copts.nl = GIT_COLOR_RESET "\n";
+	print_columns(&output, s->colopts, &copts);
+	string_list_clear(&output, 0);
 	strbuf_release(&buf);
 }
 
 static void wt_status_print_verbose(struct wt_status *s)
 {
 	struct rev_info rev;
+	struct setup_revision_opt opt;
 
 	init_revisions(&rev, NULL);
 	DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV);
-	setup_revisions(0, NULL, &rev,
-		s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference);
+
+	memset(&opt, 0, sizeof(opt));
+	opt.def = s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference;
+	setup_revisions(0, NULL, &rev, &opt);
+
 	rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
 	rev.diffopt.detect_rename = 1;
 	rev.diffopt.file = s->fp;
@@ -292,7 +741,7 @@
 	 * will have checked isatty on stdout).
 	 */
 	if (s->fp != stdout)
-		DIFF_OPT_CLR(&rev.diffopt, COLOR_DIFF);
+		rev.diffopt.use_color = 0;
 	run_diff_index(&rev, 1);
 }
 
@@ -310,104 +759,467 @@
 		return;
 
 	for (cp = sb.buf; (ep = strchr(cp, '\n')) != NULL; cp = ep + 1)
-		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER),
+		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s),
 				 "# %.*s", (int)(ep - cp), cp);
-	color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
+	color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
+}
+
+static int has_unmerged(struct wt_status *s)
+{
+	int i;
+
+	for (i = 0; i < s->change.nr; i++) {
+		struct wt_status_change_data *d;
+		d = s->change.items[i].util;
+		if (d->stagemask)
+			return 1;
+	}
+	return 0;
+}
+
+static void show_merge_in_progress(struct wt_status *s,
+				struct wt_status_state *state,
+				const char *color)
+{
+	if (has_unmerged(s)) {
+		status_printf_ln(s, color, _("You have unmerged paths."));
+		if (advice_status_hints)
+			status_printf_ln(s, color,
+				_("  (fix conflicts and run \"git commit\")"));
+	} else {
+		status_printf_ln(s, color,
+			_("All conflicts fixed but you are still merging."));
+		if (advice_status_hints)
+			status_printf_ln(s, color,
+				_("  (use \"git commit\" to conclude merge)"));
+	}
+	wt_status_print_trailer(s);
+}
+
+static void show_am_in_progress(struct wt_status *s,
+				struct wt_status_state *state,
+				const char *color)
+{
+	status_printf_ln(s, color,
+		_("You are in the middle of an am session."));
+	if (state->am_empty_patch)
+		status_printf_ln(s, color,
+			_("The current patch is empty."));
+	if (advice_status_hints) {
+		if (!state->am_empty_patch)
+			status_printf_ln(s, color,
+				_("  (fix conflicts and then run \"git am --resolved\")"));
+		status_printf_ln(s, color,
+			_("  (use \"git am --skip\" to skip this patch)"));
+		status_printf_ln(s, color,
+			_("  (use \"git am --abort\" to restore the original branch)"));
+	}
+	wt_status_print_trailer(s);
+}
+
+static char *read_line_from_git_path(const char *filename)
+{
+	struct strbuf buf = STRBUF_INIT;
+	FILE *fp = fopen(git_path("%s", filename), "r");
+	if (!fp) {
+		strbuf_release(&buf);
+		return NULL;
+	}
+	strbuf_getline(&buf, fp, '\n');
+	if (!fclose(fp)) {
+		return strbuf_detach(&buf, NULL);
+	} else {
+		strbuf_release(&buf);
+		return NULL;
+	}
+}
+
+static int split_commit_in_progress(struct wt_status *s)
+{
+	int split_in_progress = 0;
+	char *head = read_line_from_git_path("HEAD");
+	char *orig_head = read_line_from_git_path("ORIG_HEAD");
+	char *rebase_amend = read_line_from_git_path("rebase-merge/amend");
+	char *rebase_orig_head = read_line_from_git_path("rebase-merge/orig-head");
+
+	if (!head || !orig_head || !rebase_amend || !rebase_orig_head ||
+	    !s->branch || strcmp(s->branch, "HEAD"))
+		return split_in_progress;
+
+	if (!strcmp(rebase_amend, rebase_orig_head)) {
+		if (strcmp(head, rebase_amend))
+			split_in_progress = 1;
+	} else if (strcmp(orig_head, rebase_orig_head)) {
+		split_in_progress = 1;
+	}
+
+	if (!s->amend && !s->nowarn && !s->workdir_dirty)
+		split_in_progress = 0;
+
+	free(head);
+	free(orig_head);
+	free(rebase_amend);
+	free(rebase_orig_head);
+	return split_in_progress;
+}
+
+static void show_rebase_in_progress(struct wt_status *s,
+				struct wt_status_state *state,
+				const char *color)
+{
+	struct stat st;
+
+	if (has_unmerged(s)) {
+		status_printf_ln(s, color, _("You are currently rebasing."));
+		if (advice_status_hints) {
+			status_printf_ln(s, color,
+				_("  (fix conflicts and then run \"git rebase --continue\")"));
+			status_printf_ln(s, color,
+				_("  (use \"git rebase --skip\" to skip this patch)"));
+			status_printf_ln(s, color,
+				_("  (use \"git rebase --abort\" to check out the original branch)"));
+		}
+	} else if (state->rebase_in_progress || !stat(git_path("MERGE_MSG"), &st)) {
+		status_printf_ln(s, color, _("You are currently rebasing."));
+		if (advice_status_hints)
+			status_printf_ln(s, color,
+				_("  (all conflicts fixed: run \"git rebase --continue\")"));
+	} else if (split_commit_in_progress(s)) {
+		status_printf_ln(s, color, _("You are currently splitting a commit during a rebase."));
+		if (advice_status_hints)
+			status_printf_ln(s, color,
+				_("  (Once your working directory is clean, run \"git rebase --continue\")"));
+	} else {
+		status_printf_ln(s, color, _("You are currently editing a commit during a rebase."));
+		if (advice_status_hints && !s->amend) {
+			status_printf_ln(s, color,
+				_("  (use \"git commit --amend\" to amend the current commit)"));
+			status_printf_ln(s, color,
+				_("  (use \"git rebase --continue\" once you are satisfied with your changes)"));
+		}
+	}
+	wt_status_print_trailer(s);
+}
+
+static void show_cherry_pick_in_progress(struct wt_status *s,
+					struct wt_status_state *state,
+					const char *color)
+{
+	status_printf_ln(s, color, _("You are currently cherry-picking."));
+	if (advice_status_hints) {
+		if (has_unmerged(s))
+			status_printf_ln(s, color,
+				_("  (fix conflicts and run \"git commit\")"));
+		else
+			status_printf_ln(s, color,
+				_("  (all conflicts fixed: run \"git commit\")"));
+	}
+	wt_status_print_trailer(s);
+}
+
+static void show_bisect_in_progress(struct wt_status *s,
+				struct wt_status_state *state,
+				const char *color)
+{
+	status_printf_ln(s, color, _("You are currently bisecting."));
+	if (advice_status_hints)
+		status_printf_ln(s, color,
+			_("  (use \"git bisect reset\" to get back to the original branch)"));
+	wt_status_print_trailer(s);
+}
+
+static void wt_status_print_state(struct wt_status *s)
+{
+	const char *state_color = color(WT_STATUS_HEADER, s);
+	struct wt_status_state state;
+	struct stat st;
+
+	memset(&state, 0, sizeof(state));
+
+	if (!stat(git_path("MERGE_HEAD"), &st)) {
+		state.merge_in_progress = 1;
+	} else if (!stat(git_path("rebase-apply"), &st)) {
+		if (!stat(git_path("rebase-apply/applying"), &st)) {
+			state.am_in_progress = 1;
+			if (!stat(git_path("rebase-apply/patch"), &st) && !st.st_size)
+				state.am_empty_patch = 1;
+		} else {
+			state.rebase_in_progress = 1;
+		}
+	} else if (!stat(git_path("rebase-merge"), &st)) {
+		if (!stat(git_path("rebase-merge/interactive"), &st))
+			state.rebase_interactive_in_progress = 1;
+		else
+			state.rebase_in_progress = 1;
+	} else if (!stat(git_path("CHERRY_PICK_HEAD"), &st)) {
+		state.cherry_pick_in_progress = 1;
+	}
+	if (!stat(git_path("BISECT_LOG"), &st))
+		state.bisect_in_progress = 1;
+
+	if (state.merge_in_progress)
+		show_merge_in_progress(s, &state, state_color);
+	else if (state.am_in_progress)
+		show_am_in_progress(s, &state, state_color);
+	else if (state.rebase_in_progress || state.rebase_interactive_in_progress)
+		show_rebase_in_progress(s, &state, state_color);
+	else if (state.cherry_pick_in_progress)
+		show_cherry_pick_in_progress(s, &state, state_color);
+	if (state.bisect_in_progress)
+		show_bisect_in_progress(s, &state, state_color);
 }
 
 void wt_status_print(struct wt_status *s)
 {
-	unsigned char sha1[20];
-	const char *branch_color = color(WT_STATUS_HEADER);
+	const char *branch_color = color(WT_STATUS_ONBRANCH, s);
+	const char *branch_status_color = color(WT_STATUS_HEADER, s);
 
-	s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0;
 	if (s->branch) {
-		const char *on_what = "On branch ";
+		const char *on_what = _("On branch ");
 		const char *branch_name = s->branch;
 		if (!prefixcmp(branch_name, "refs/heads/"))
 			branch_name += 11;
 		else if (!strcmp(branch_name, "HEAD")) {
 			branch_name = "";
-			branch_color = color(WT_STATUS_NOBRANCH);
-			on_what = "Not currently on any branch.";
+			branch_status_color = color(WT_STATUS_NOBRANCH, s);
+			on_what = _("Not currently on any branch.");
 		}
-		color_fprintf(s->fp, color(WT_STATUS_HEADER), "# ");
-		color_fprintf_ln(s->fp, branch_color, "%s%s", on_what, branch_name);
+		status_printf(s, color(WT_STATUS_HEADER, s), "");
+		status_printf_more(s, branch_status_color, "%s", on_what);
+		status_printf_more(s, branch_color, "%s\n", branch_name);
 		if (!s->is_initial)
 			wt_status_print_tracking(s);
 	}
 
+	wt_status_print_state(s);
 	if (s->is_initial) {
-		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
-		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "# Initial commit");
-		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
+		status_printf_ln(s, color(WT_STATUS_HEADER, s), "");
+		status_printf_ln(s, color(WT_STATUS_HEADER, s), _("Initial commit"));
+		status_printf_ln(s, color(WT_STATUS_HEADER, s), "");
 	}
 
 	wt_status_print_updated(s);
+	wt_status_print_unmerged(s);
 	wt_status_print_changed(s);
-	if (wt_status_submodule_summary)
-		wt_status_print_submodule_summary(s);
-	if (show_untracked_files)
-		wt_status_print_untracked(s);
-	else if (s->commitable)
-		 fprintf(s->fp, "# Untracked files not listed (use -u option to show untracked files)\n");
+	if (s->submodule_summary &&
+	    (!s->ignore_submodule_arg ||
+	     strcmp(s->ignore_submodule_arg, "all"))) {
+		wt_status_print_submodule_summary(s, 0);  /* staged */
+		wt_status_print_submodule_summary(s, 1);  /* unstaged */
+	}
+	if (s->show_untracked_files) {
+		wt_status_print_other(s, &s->untracked, _("Untracked"), "add");
+		if (s->show_ignored_files)
+			wt_status_print_other(s, &s->ignored, _("Ignored"), "add -f");
+	} else if (s->commitable)
+		status_printf_ln(s, GIT_COLOR_NORMAL, _("Untracked files not listed%s"),
+			advice_status_hints
+			? _(" (use -u option to show untracked files)") : "");
 
 	if (s->verbose)
 		wt_status_print_verbose(s);
 	if (!s->commitable) {
 		if (s->amend)
-			fprintf(s->fp, "# No changes\n");
+			status_printf_ln(s, GIT_COLOR_NORMAL, _("No changes"));
 		else if (s->nowarn)
 			; /* nothing */
 		else if (s->workdir_dirty)
-			printf("no changes added to commit (use \"git add\" and/or \"git commit -a\")\n");
-		else if (s->workdir_untracked)
-			printf("nothing added to commit but untracked files present (use \"git add\" to track)\n");
+			printf(_("no changes added to commit%s\n"),
+				advice_status_hints
+				? _(" (use \"git add\" and/or \"git commit -a\")") : "");
+		else if (s->untracked.nr)
+			printf(_("nothing added to commit but untracked files present%s\n"),
+				advice_status_hints
+				? _(" (use \"git add\" to track)") : "");
 		else if (s->is_initial)
-			printf("nothing to commit (create/copy files and use \"git add\" to track)\n");
-		else if (!show_untracked_files)
-			printf("nothing to commit (use -u to show untracked files)\n");
+			printf(_("nothing to commit%s\n"), advice_status_hints
+				? _(" (create/copy files and use \"git add\" to track)") : "");
+		else if (!s->show_untracked_files)
+			printf(_("nothing to commit%s\n"), advice_status_hints
+				? _(" (use -u to show untracked files)") : "");
 		else
-			printf("nothing to commit (working directory clean)\n");
+			printf(_("nothing to commit%s\n"), advice_status_hints
+				? _(" (working directory clean)") : "");
 	}
 }
 
-int git_status_config(const char *k, const char *v, void *cb)
+static void wt_shortstatus_unmerged(struct string_list_item *it,
+			   struct wt_status *s)
 {
-	if (!strcmp(k, "status.submodulesummary")) {
-		int is_bool;
-		wt_status_submodule_summary = git_config_bool_or_int(k, v, &is_bool);
-		if (is_bool && wt_status_submodule_summary)
-			wt_status_submodule_summary = -1;
-		return 0;
+	struct wt_status_change_data *d = it->util;
+	const char *how = "??";
+
+	switch (d->stagemask) {
+	case 1: how = "DD"; break; /* both deleted */
+	case 2: how = "AU"; break; /* added by us */
+	case 3: how = "UD"; break; /* deleted by them */
+	case 4: how = "UA"; break; /* added by them */
+	case 5: how = "DU"; break; /* deleted by us */
+	case 6: how = "AA"; break; /* both added */
+	case 7: how = "UU"; break; /* both modified */
 	}
-	if (!strcmp(k, "status.color") || !strcmp(k, "color.status")) {
-		wt_status_use_color = git_config_colorbool(k, v, -1);
-		return 0;
+	color_fprintf(s->fp, color(WT_STATUS_UNMERGED, s), "%s", how);
+	if (s->null_termination) {
+		fprintf(stdout, " %s%c", it->string, 0);
+	} else {
+		struct strbuf onebuf = STRBUF_INIT;
+		const char *one;
+		one = quote_path(it->string, -1, &onebuf, s->prefix);
+		printf(" %s\n", one);
+		strbuf_release(&onebuf);
 	}
-	if (!prefixcmp(k, "status.color.") || !prefixcmp(k, "color.status.")) {
-		int slot = parse_status_slot(k, 13);
-		if (!v)
-			return config_error_nonbool(k);
-		color_parse(v, k, wt_status_colors[slot]);
-		return 0;
+}
+
+static void wt_shortstatus_status(struct string_list_item *it,
+			 struct wt_status *s)
+{
+	struct wt_status_change_data *d = it->util;
+
+	if (d->index_status)
+		color_fprintf(s->fp, color(WT_STATUS_UPDATED, s), "%c", d->index_status);
+	else
+		putchar(' ');
+	if (d->worktree_status)
+		color_fprintf(s->fp, color(WT_STATUS_CHANGED, s), "%c", d->worktree_status);
+	else
+		putchar(' ');
+	putchar(' ');
+	if (s->null_termination) {
+		fprintf(stdout, "%s%c", it->string, 0);
+		if (d->head_path)
+			fprintf(stdout, "%s%c", d->head_path, 0);
+	} else {
+		struct strbuf onebuf = STRBUF_INIT;
+		const char *one;
+		if (d->head_path) {
+			one = quote_path(d->head_path, -1, &onebuf, s->prefix);
+			if (*one != '"' && strchr(one, ' ') != NULL) {
+				putchar('"');
+				strbuf_addch(&onebuf, '"');
+				one = onebuf.buf;
+			}
+			printf("%s -> ", one);
+			strbuf_release(&onebuf);
+		}
+		one = quote_path(it->string, -1, &onebuf, s->prefix);
+		if (*one != '"' && strchr(one, ' ') != NULL) {
+			putchar('"');
+			strbuf_addch(&onebuf, '"');
+			one = onebuf.buf;
+		}
+		printf("%s\n", one);
+		strbuf_release(&onebuf);
 	}
-	if (!strcmp(k, "status.relativepaths")) {
-		wt_status_relative_paths = git_config_bool(k, v);
-		return 0;
+}
+
+static void wt_shortstatus_other(struct string_list_item *it,
+				 struct wt_status *s, const char *sign)
+{
+	if (s->null_termination) {
+		fprintf(stdout, "%s %s%c", sign, it->string, 0);
+	} else {
+		struct strbuf onebuf = STRBUF_INIT;
+		const char *one;
+		one = quote_path(it->string, -1, &onebuf, s->prefix);
+		color_fprintf(s->fp, color(WT_STATUS_UNTRACKED, s), "%s", sign);
+		printf(" %s\n", one);
+		strbuf_release(&onebuf);
 	}
-	if (!strcmp(k, "status.showuntrackedfiles")) {
-		if (!v)
-			return config_error_nonbool(k);
-		else if (!strcmp(v, "no"))
-			show_untracked_files = SHOW_NO_UNTRACKED_FILES;
-		else if (!strcmp(v, "normal"))
-			show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
-		else if (!strcmp(v, "all"))
-			show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
+}
+
+static void wt_shortstatus_print_tracking(struct wt_status *s)
+{
+	struct branch *branch;
+	const char *header_color = color(WT_STATUS_HEADER, s);
+	const char *branch_color_local = color(WT_STATUS_LOCAL_BRANCH, s);
+	const char *branch_color_remote = color(WT_STATUS_REMOTE_BRANCH, s);
+
+	const char *base;
+	const char *branch_name;
+	int num_ours, num_theirs;
+
+	color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "## ");
+
+	if (!s->branch)
+		return;
+	branch_name = s->branch;
+
+	if (!prefixcmp(branch_name, "refs/heads/"))
+		branch_name += 11;
+	else if (!strcmp(branch_name, "HEAD")) {
+		branch_name = _("HEAD (no branch)");
+		branch_color_local = color(WT_STATUS_NOBRANCH, s);
+	}
+
+	branch = branch_get(s->branch + 11);
+	if (s->is_initial)
+		color_fprintf(s->fp, header_color, _("Initial commit on "));
+	if (!stat_tracking_info(branch, &num_ours, &num_theirs)) {
+		color_fprintf(s->fp, branch_color_local, "%s", branch_name);
+		fputc(s->null_termination ? '\0' : '\n', s->fp);
+		return;
+	}
+
+	base = branch->merge[0]->dst;
+	base = shorten_unambiguous_ref(base, 0);
+	color_fprintf(s->fp, branch_color_local, "%s", branch_name);
+	color_fprintf(s->fp, header_color, "...");
+	color_fprintf(s->fp, branch_color_remote, "%s", base);
+
+	color_fprintf(s->fp, header_color, " [");
+	if (!num_ours) {
+		color_fprintf(s->fp, header_color, _("behind "));
+		color_fprintf(s->fp, branch_color_remote, "%d", num_theirs);
+	} else if (!num_theirs) {
+		color_fprintf(s->fp, header_color, _("ahead "));
+		color_fprintf(s->fp, branch_color_local, "%d", num_ours);
+	} else {
+		color_fprintf(s->fp, header_color, _("ahead "));
+		color_fprintf(s->fp, branch_color_local, "%d", num_ours);
+		color_fprintf(s->fp, header_color, _(", behind "));
+		color_fprintf(s->fp, branch_color_remote, "%d", num_theirs);
+	}
+
+	color_fprintf(s->fp, header_color, "]");
+	fputc(s->null_termination ? '\0' : '\n', s->fp);
+}
+
+void wt_shortstatus_print(struct wt_status *s)
+{
+	int i;
+
+	if (s->show_branch)
+		wt_shortstatus_print_tracking(s);
+
+	for (i = 0; i < s->change.nr; i++) {
+		struct wt_status_change_data *d;
+		struct string_list_item *it;
+
+		it = &(s->change.items[i]);
+		d = it->util;
+		if (d->stagemask)
+			wt_shortstatus_unmerged(it, s);
 		else
-			return error("Invalid untracked files mode '%s'", v);
-		return 0;
+			wt_shortstatus_status(it, s);
 	}
-	return git_diff_ui_config(k, v, cb);
+	for (i = 0; i < s->untracked.nr; i++) {
+		struct string_list_item *it;
+
+		it = &(s->untracked.items[i]);
+		wt_shortstatus_other(it, s, "??");
+	}
+	for (i = 0; i < s->ignored.nr; i++) {
+		struct string_list_item *it;
+
+		it = &(s->ignored.items[i]);
+		wt_shortstatus_other(it, s, "!!");
+	}
+}
+
+void wt_porcelain_print(struct wt_status *s)
+{
+	s->use_color = 0;
+	s->relative_paths = 0;
+	s->prefix = NULL;
+	wt_shortstatus_print(s);
 }
diff --git a/wt-status.h b/wt-status.h
index 78add09..f8fc58c 100644
--- a/wt-status.h
+++ b/wt-status.h
@@ -2,13 +2,20 @@
 #define STATUS_H
 
 #include <stdio.h>
+#include "string-list.h"
+#include "color.h"
 
 enum color_wt_status {
-	WT_STATUS_HEADER,
+	WT_STATUS_HEADER = 0,
 	WT_STATUS_UPDATED,
 	WT_STATUS_CHANGED,
 	WT_STATUS_UNTRACKED,
 	WT_STATUS_NOBRANCH,
+	WT_STATUS_UNMERGED,
+	WT_STATUS_LOCAL_BRANCH,
+	WT_STATUS_REMOTE_BRANCH,
+	WT_STATUS_ONBRANCH,
+	WT_STATUS_MAXSLOT
 };
 
 enum untracked_status_type {
@@ -16,29 +23,76 @@
 	SHOW_NORMAL_UNTRACKED_FILES,
 	SHOW_ALL_UNTRACKED_FILES
 };
-extern enum untracked_status_type show_untracked_files;
+
+/* from where does this commit originate */
+enum commit_whence {
+	FROM_COMMIT,     /* normal */
+	FROM_MERGE,      /* commit came from merge */
+	FROM_CHERRY_PICK /* commit came from cherry-pick */
+};
+
+struct wt_status_change_data {
+	int worktree_status;
+	int index_status;
+	int stagemask;
+	char *head_path;
+	unsigned dirty_submodule       : 2;
+	unsigned new_submodule_commits : 1;
+};
 
 struct wt_status {
 	int is_initial;
 	char *branch;
 	const char *reference;
+	const char **pathspec;
 	int verbose;
 	int amend;
-	int untracked;
+	enum commit_whence whence;
 	int nowarn;
+	int use_color;
+	int relative_paths;
+	int submodule_summary;
+	int show_ignored_files;
+	enum untracked_status_type show_untracked_files;
+	const char *ignore_submodule_arg;
+	char color_palette[WT_STATUS_MAXSLOT][COLOR_MAXLEN];
+	unsigned colopts;
+	int null_termination;
+	int show_branch;
+
 	/* These are computed during processing of the individual sections */
 	int commitable;
 	int workdir_dirty;
-	int workdir_untracked;
 	const char *index_file;
 	FILE *fp;
 	const char *prefix;
+	struct string_list change;
+	struct string_list untracked;
+	struct string_list ignored;
 };
 
-int git_status_config(const char *var, const char *value, void *cb);
-extern int wt_status_use_color;
-extern int wt_status_relative_paths;
+struct wt_status_state {
+	int merge_in_progress;
+	int am_in_progress;
+	int am_empty_patch;
+	int rebase_in_progress;
+	int rebase_interactive_in_progress;
+	int cherry_pick_in_progress;
+	int bisect_in_progress;
+};
+
 void wt_status_prepare(struct wt_status *s);
 void wt_status_print(struct wt_status *s);
+void wt_status_collect(struct wt_status *s);
+
+void wt_shortstatus_print(struct wt_status *s);
+void wt_porcelain_print(struct wt_status *s);
+
+void status_printf_ln(struct wt_status *s, const char *color, const char *fmt, ...)
+	;
+void status_printf(struct wt_status *s, const char *color, const char *fmt, ...)
+	;
+void status_printf_more(struct wt_status *s, const char *color, const char *fmt, ...)
+	__attribute__((format(printf, 3, 4)));
 
 #endif /* STATUS_H */
diff --git a/xdiff-interface.c b/xdiff-interface.c
index 01f14fb..ecfa05f 100644
--- a/xdiff-interface.c
+++ b/xdiff-interface.c
@@ -138,65 +138,22 @@
 
 int xdi_diff_outf(mmfile_t *mf1, mmfile_t *mf2,
 		  xdiff_emit_consume_fn fn, void *consume_callback_data,
-		  xpparam_t const *xpp,
-		  xdemitconf_t const *xecfg, xdemitcb_t *xecb)
+		  xpparam_t const *xpp, xdemitconf_t const *xecfg)
 {
 	int ret;
 	struct xdiff_emit_state state;
-
-	memset(&state, 0, sizeof(state));
-	state.consume = fn;
-	state.consume_callback_data = consume_callback_data;
-	xecb->outf = xdiff_outf;
-	xecb->priv = &state;
-	strbuf_init(&state.remainder, 0);
-	ret = xdi_diff(mf1, mf2, xpp, xecfg, xecb);
-	strbuf_release(&state.remainder);
-	return ret;
-}
-
-struct xdiff_emit_hunk_state {
-	xdiff_emit_hunk_consume_fn consume;
-	void *consume_callback_data;
-};
-
-static int process_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
-			xdemitconf_t const *xecfg)
-{
-	long s1, s2, same, p_next, t_next;
-	xdchange_t *xch, *xche;
-	struct xdiff_emit_hunk_state *state = ecb->priv;
-	xdiff_emit_hunk_consume_fn fn = state->consume;
-	void *consume_callback_data = state->consume_callback_data;
-
-	for (xch = xscr; xch; xch = xche->next) {
-		xche = xdl_get_hunk(xch, xecfg);
-
-		s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0);
-		s2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0);
-		same = s2 + XDL_MAX(xch->i1 - s1, 0);
-		p_next = xche->i1 + xche->chg1;
-		t_next = xche->i2 + xche->chg2;
-
-		fn(consume_callback_data, same, p_next, t_next);
-	}
-	return 0;
-}
-
-int xdi_diff_hunks(mmfile_t *mf1, mmfile_t *mf2,
-		   xdiff_emit_hunk_consume_fn fn, void *consume_callback_data,
-		   xpparam_t const *xpp, xdemitconf_t *xecfg)
-{
-	struct xdiff_emit_hunk_state state;
 	xdemitcb_t ecb;
 
 	memset(&state, 0, sizeof(state));
-	memset(&ecb, 0, sizeof(ecb));
 	state.consume = fn;
 	state.consume_callback_data = consume_callback_data;
-	xecfg->emit_func = (void (*)())process_diff;
+	memset(&ecb, 0, sizeof(ecb));
+	ecb.outf = xdiff_outf;
 	ecb.priv = &state;
-	return xdi_diff(mf1, mf2, xpp, xecfg, &ecb);
+	strbuf_init(&state.remainder, 0);
+	ret = xdi_diff(mf1, mf2, xpp, xecfg, &ecb);
+	strbuf_release(&state.remainder);
+	return ret;
 }
 
 int read_mmfile(mmfile_t *ptr, const char *filename)
@@ -211,13 +168,32 @@
 		return error("Could not open %s", filename);
 	sz = xsize_t(st.st_size);
 	ptr->ptr = xmalloc(sz ? sz : 1);
-	if (sz && fread(ptr->ptr, sz, 1, f) != 1)
+	if (sz && fread(ptr->ptr, sz, 1, f) != 1) {
+		fclose(f);
 		return error("Could not read %s", filename);
+	}
 	fclose(f);
 	ptr->size = sz;
 	return 0;
 }
 
+void read_mmblob(mmfile_t *ptr, const unsigned char *sha1)
+{
+	unsigned long size;
+	enum object_type type;
+
+	if (!hashcmp(sha1, null_sha1)) {
+		ptr->ptr = xstrdup("");
+		ptr->size = 0;
+		return;
+	}
+
+	ptr->ptr = read_sha1_file(sha1, &type, &size);
+	if (!ptr->ptr || type != OBJ_BLOB)
+		die("unable to read blob object %s", sha1_to_hex(sha1));
+	ptr->size = size;
+}
+
 #define FIRST_FEW_BYTES 8000
 int buffer_is_binary(const char *ptr, unsigned long size)
 {
@@ -268,9 +244,8 @@
 	result = pmatch[i].rm_eo - pmatch[i].rm_so;
 	if (result > buffer_size)
 		result = buffer_size;
-	else
-		while (result > 0 && (isspace(line[result - 1])))
-			result--;
+	while (result > 0 && (isspace(line[result - 1])))
+		result--;
 	memcpy(buffer, line, result);
  fail:
 	free(line_buffer);
@@ -328,7 +303,7 @@
 
 int git_xmerge_config(const char *var, const char *value, void *cb)
 {
-	if (!strcasecmp(var, "merge.conflictstyle")) {
+	if (!strcmp(var, "merge.conflictstyle")) {
 		if (!value)
 			die("'%s' is not a boolean", var);
 		if (!strcmp(value, "diff3"))
diff --git a/xdiff-interface.h b/xdiff-interface.h
index 55572c3..eff7762 100644
--- a/xdiff-interface.h
+++ b/xdiff-interface.h
@@ -4,20 +4,16 @@
 #include "xdiff/xdiff.h"
 
 typedef void (*xdiff_emit_consume_fn)(void *, char *, unsigned long);
-typedef void (*xdiff_emit_hunk_consume_fn)(void *, long, long, long);
 
 int xdi_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdemitconf_t const *xecfg, xdemitcb_t *ecb);
 int xdi_diff_outf(mmfile_t *mf1, mmfile_t *mf2,
 		  xdiff_emit_consume_fn fn, void *consume_callback_data,
-		  xpparam_t const *xpp,
-		  xdemitconf_t const *xecfg, xdemitcb_t *xecb);
-int xdi_diff_hunks(mmfile_t *mf1, mmfile_t *mf2,
-		   xdiff_emit_hunk_consume_fn fn, void *consume_callback_data,
-		   xpparam_t const *xpp, xdemitconf_t *xecfg);
+		  xpparam_t const *xpp, xdemitconf_t const *xecfg);
 int parse_hunk_header(char *line, int len,
 		      int *ob, int *on,
 		      int *nb, int *nn);
 int read_mmfile(mmfile_t *ptr, const char *filename);
+void read_mmblob(mmfile_t *ptr, const unsigned char *sha1);
 int buffer_is_binary(const char *ptr, unsigned long size);
 
 extern void xdiff_set_find_func(xdemitconf_t *xecfg, const char *line, int cflags);
diff --git a/xdiff/xdiff.h b/xdiff/xdiff.h
index 4da052a..219a3bb 100644
--- a/xdiff/xdiff.h
+++ b/xdiff/xdiff.h
@@ -32,16 +32,16 @@
 #define XDF_IGNORE_WHITESPACE (1 << 2)
 #define XDF_IGNORE_WHITESPACE_CHANGE (1 << 3)
 #define XDF_IGNORE_WHITESPACE_AT_EOL (1 << 4)
-#define XDF_PATIENCE_DIFF (1 << 5)
 #define XDF_WHITESPACE_FLAGS (XDF_IGNORE_WHITESPACE | XDF_IGNORE_WHITESPACE_CHANGE | XDF_IGNORE_WHITESPACE_AT_EOL)
 
-#define XDL_PATCH_NORMAL '-'
-#define XDL_PATCH_REVERSE '+'
-#define XDL_PATCH_MODEMASK ((1 << 8) - 1)
-#define XDL_PATCH_IGNOREBSPACE (1 << 8)
+#define XDF_PATIENCE_DIFF (1 << 5)
+#define XDF_HISTOGRAM_DIFF (1 << 6)
+#define XDF_DIFF_ALGORITHM_MASK (XDF_PATIENCE_DIFF | XDF_HISTOGRAM_DIFF)
+#define XDF_DIFF_ALG(x) ((x) & XDF_DIFF_ALGORITHM_MASK)
 
 #define XDL_EMIT_FUNCNAMES (1 << 0)
 #define XDL_EMIT_COMMON (1 << 1)
+#define XDL_EMIT_FUNCCONTEXT (1 << 2)
 
 #define XDL_MMB_READONLY (1 << 0)
 
@@ -56,11 +56,14 @@
 #define XDL_MERGE_EAGER 1
 #define XDL_MERGE_ZEALOUS 2
 #define XDL_MERGE_ZEALOUS_ALNUM 3
-#define XDL_MERGE_LEVEL_MASK 0x0f
+
+/* merge favor modes */
+#define XDL_MERGE_FAVOR_OURS 1
+#define XDL_MERGE_FAVOR_THEIRS 2
+#define XDL_MERGE_FAVOR_UNION 3
 
 /* merge output styles */
-#define XDL_MERGE_DIFF3 0x8000
-#define XDL_MERGE_STYLE_MASK 0x8000
+#define XDL_MERGE_DIFF3 1
 
 typedef struct s_mmfile {
 	char *ptr;
@@ -83,13 +86,17 @@
 
 typedef long (*find_func_t)(const char *line, long line_len, char *buffer, long buffer_size, void *priv);
 
+typedef int (*xdl_emit_hunk_consume_func_t)(long start_a, long count_a,
+					    long start_b, long count_b,
+					    void *cb_data);
+
 typedef struct s_xdemitconf {
 	long ctxlen;
 	long interhunkctxlen;
 	unsigned long flags;
 	find_func_t find_func;
 	void *find_func_priv;
-	void (*emit_func)();
+	xdl_emit_hunk_consume_func_t hunk_func;
 } xdemitconf_t;
 
 typedef struct s_bdiffparam {
@@ -102,15 +109,26 @@
 #define xdl_realloc(ptr,x) realloc(ptr,x)
 
 void *xdl_mmfile_first(mmfile_t *mmf, long *size);
-void *xdl_mmfile_next(mmfile_t *mmf, long *size);
 long xdl_mmfile_size(mmfile_t *mmf);
 
 int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
 	     xdemitconf_t const *xecfg, xdemitcb_t *ecb);
 
-int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1,
-		mmfile_t *mf2, const char *name2,
-		xpparam_t const *xpp, int level, mmbuffer_t *result);
+typedef struct s_xmparam {
+	xpparam_t xpp;
+	int marker_size;
+	int level;
+	int favor;
+	int style;
+	const char *ancestor;	/* label for orig */
+	const char *file1;	/* label for mf1 */
+	const char *file2;	/* label for mf2 */
+} xmparam_t;
+
+#define DEFAULT_CONFLICT_MARKER_SIZE 7
+
+int xdl_merge(mmfile_t *orig, mmfile_t *mf1, mmfile_t *mf2,
+		xmparam_t const *xmp, mmbuffer_t *result);
 
 #ifdef __cplusplus
 }
diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c
index da67c04..1b7012a 100644
--- a/xdiff/xdiffi.c
+++ b/xdiff/xdiffi.c
@@ -328,9 +328,12 @@
 	xdalgoenv_t xenv;
 	diffdata_t dd1, dd2;
 
-	if (xpp->flags & XDF_PATIENCE_DIFF)
+	if (XDF_DIFF_ALG(xpp->flags) == XDF_PATIENCE_DIFF)
 		return xdl_do_patience_diff(mf1, mf2, xpp, xe);
 
+	if (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF)
+		return xdl_do_histogram_diff(mf1, mf2, xpp, xe);
+
 	if (xdl_prepare_env(mf1, mf2, xpp, xe) < 0) {
 
 		return -1;
@@ -535,13 +538,26 @@
 	}
 }
 
+static int xdl_call_hunk_func(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
+			      xdemitconf_t const *xecfg)
+{
+	xdchange_t *xch, *xche;
+
+	for (xch = xscr; xch; xch = xche->next) {
+		xche = xdl_get_hunk(xch, xecfg);
+		if (xecfg->hunk_func(xch->i1, xche->i1 + xche->chg1 - xch->i1,
+				     xch->i2, xche->i2 + xche->chg2 - xch->i2,
+				     ecb->priv) < 0)
+			return -1;
+	}
+	return 0;
+}
 
 int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
 	     xdemitconf_t const *xecfg, xdemitcb_t *ecb) {
 	xdchange_t *xscr;
 	xdfenv_t xe;
-	emit_func_t ef = xecfg->emit_func ?
-		(emit_func_t)xecfg->emit_func : xdl_emit_diff;
+	emit_func_t ef = xecfg->hunk_func ? xdl_call_hunk_func : xdl_emit_diff;
 
 	if (xdl_do_diff(mf1, mf2, xpp, &xe) < 0) {
 
diff --git a/xdiff/xdiffi.h b/xdiff/xdiffi.h
index ad033a8..7a92ea9 100644
--- a/xdiff/xdiffi.h
+++ b/xdiff/xdiffi.h
@@ -57,5 +57,7 @@
 		  xdemitconf_t const *xecfg);
 int xdl_do_patience_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
 		xdfenv_t *env);
+int xdl_do_histogram_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
+		xdfenv_t *env);
 
 #endif /* #if !defined(XDIFFI_H) */
diff --git a/xdiff/xemit.c b/xdiff/xemit.c
index c4bedf0..d11dbf9 100644
--- a/xdiff/xemit.c
+++ b/xdiff/xemit.c
@@ -85,30 +85,9 @@
 	return -1;
 }
 
-static void xdl_find_func(xdfile_t *xf, long i, char *buf, long sz, long *ll,
-		find_func_t ff, void *ff_priv) {
-
-	/*
-	 * Be quite stupid about this for now.  Find a line in the old file
-	 * before the start of the hunk (and context) which starts with a
-	 * plausible character.
-	 */
-
-	const char *rec;
-	long len;
-
-	while (i-- > 0) {
-		len = xdl_get_rec(xf, i, &rec);
-		if ((*ll = ff(rec, len, buf, sz, ff_priv)) >= 0)
-			return;
-	}
-	*ll = 0;
-}
-
-
 static int xdl_emit_common(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
                            xdemitconf_t const *xecfg) {
-	xdfile_t *xdf = &xe->xdf1;
+	xdfile_t *xdf = &xe->xdf2;
 	const char *rchg = xdf->rchg;
 	long ix;
 
@@ -121,13 +100,40 @@
 	return 0;
 }
 
+struct func_line {
+	long len;
+	char buf[80];
+};
+
+static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,
+			  struct func_line *func_line, long start, long limit)
+{
+	find_func_t ff = xecfg->find_func ? xecfg->find_func : def_ff;
+	long l, size, step = (start > limit) ? -1 : 1;
+	char *buf, dummy[1];
+
+	buf = func_line ? func_line->buf : dummy;
+	size = func_line ? sizeof(func_line->buf) : sizeof(dummy);
+
+	for (l = start; l != limit && 0 <= l && l < xe->xdf1.nrec; l += step) {
+		const char *rec;
+		long reclen = xdl_get_rec(&xe->xdf1, l, &rec);
+		long len = ff(rec, reclen, buf, size, xecfg->find_func_priv);
+		if (len >= 0) {
+			if (func_line)
+				func_line->len = len;
+			return l;
+		}
+	}
+	return -1;
+}
+
 int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
 		  xdemitconf_t const *xecfg) {
 	long s1, s2, e1, e2, lctx;
 	xdchange_t *xch, *xche;
-	char funcbuf[80];
-	long funclen = 0;
-	find_func_t ff = xecfg->find_func ?  xecfg->find_func : def_ff;
+	long funclineprev = -1;
+	struct func_line func_line = { 0 };
 
 	if (xecfg->flags & XDL_EMIT_COMMON)
 		return xdl_emit_common(xe, xscr, ecb, xecfg);
@@ -138,6 +144,17 @@
 		s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0);
 		s2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0);
 
+		if (xecfg->flags & XDL_EMIT_FUNCCONTEXT) {
+			long fs1 = get_func_line(xe, xecfg, NULL, xch->i1, -1);
+			if (fs1 < 0)
+				fs1 = 0;
+			if (fs1 < s1) {
+				s2 -= s1 - fs1;
+				s1 = fs1;
+			}
+		}
+
+ again:
 		lctx = xecfg->ctxlen;
 		lctx = XDL_MIN(lctx, xe->xdf1.nrec - (xche->i1 + xche->chg1));
 		lctx = XDL_MIN(lctx, xe->xdf2.nrec - (xche->i2 + xche->chg2));
@@ -145,24 +162,50 @@
 		e1 = xche->i1 + xche->chg1 + lctx;
 		e2 = xche->i2 + xche->chg2 + lctx;
 
+		if (xecfg->flags & XDL_EMIT_FUNCCONTEXT) {
+			long fe1 = get_func_line(xe, xecfg, NULL,
+						 xche->i1 + xche->chg1,
+						 xe->xdf1.nrec);
+			if (fe1 < 0)
+				fe1 = xe->xdf1.nrec;
+			if (fe1 > e1) {
+				e2 += fe1 - e1;
+				e1 = fe1;
+			}
+
+			/*
+			 * Overlap with next change?  Then include it
+			 * in the current hunk and start over to find
+			 * its new end.
+			 */
+			if (xche->next) {
+				long l = xche->next->i1;
+				if (l <= e1 ||
+				    get_func_line(xe, xecfg, NULL, l, e1) < 0) {
+					xche = xche->next;
+					goto again;
+				}
+			}
+		}
+
 		/*
 		 * Emit current hunk header.
 		 */
 
 		if (xecfg->flags & XDL_EMIT_FUNCNAMES) {
-			xdl_find_func(&xe->xdf1, s1, funcbuf,
-				      sizeof(funcbuf), &funclen,
-				      ff, xecfg->find_func_priv);
+			get_func_line(xe, xecfg, &func_line,
+				      s1 - 1, funclineprev);
+			funclineprev = s1 - 1;
 		}
 		if (xdl_emit_hunk_hdr(s1 + 1, e1 - s1, s2 + 1, e2 - s2,
-				      funcbuf, funclen, ecb) < 0)
+				      func_line.buf, func_line.len, ecb) < 0)
 			return -1;
 
 		/*
 		 * Emit pre-context.
 		 */
-		for (; s1 < xch->i1; s1++)
-			if (xdl_emit_record(&xe->xdf1, s1, " ", ecb) < 0)
+		for (; s2 < xch->i2; s2++)
+			if (xdl_emit_record(&xe->xdf2, s2, " ", ecb) < 0)
 				return -1;
 
 		for (s1 = xch->i1, s2 = xch->i2;; xch = xch->next) {
@@ -170,7 +213,7 @@
 			 * Merge previous with current change atom.
 			 */
 			for (; s1 < xch->i1 && s2 < xch->i2; s1++, s2++)
-				if (xdl_emit_record(&xe->xdf1, s1, " ", ecb) < 0)
+				if (xdl_emit_record(&xe->xdf2, s2, " ", ecb) < 0)
 					return -1;
 
 			/*
@@ -196,8 +239,8 @@
 		/*
 		 * Emit post-context.
 		 */
-		for (s1 = xche->i1 + xche->chg1; s1 < e1; s1++)
-			if (xdl_emit_record(&xe->xdf1, s1, " ", ecb) < 0)
+		for (s2 = xche->i2 + xche->chg2; s2 < e2; s2++)
+			if (xdl_emit_record(&xe->xdf2, s2, " ", ecb) < 0)
 				return -1;
 	}
 
diff --git a/xdiff/xhistogram.c b/xdiff/xhistogram.c
new file mode 100644
index 0000000..bf99787
--- /dev/null
+++ b/xdiff/xhistogram.c
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in JGit's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "xinclude.h"
+#include "xtypes.h"
+#include "xdiff.h"
+
+#define MAX_PTR	UINT_MAX
+#define MAX_CNT	UINT_MAX
+
+#define LINE_END(n) (line##n + count##n - 1)
+#define LINE_END_PTR(n) (*line##n + *count##n - 1)
+
+struct histindex {
+	struct record {
+		unsigned int ptr, cnt;
+		struct record *next;
+	} **records, /* an ocurrence */
+	  **line_map; /* map of line to record chain */
+	chastore_t rcha;
+	unsigned int *next_ptrs;
+	unsigned int table_bits,
+		     records_size,
+		     line_map_size;
+
+	unsigned int max_chain_length,
+		     key_shift,
+		     ptr_shift;
+
+	unsigned int cnt,
+		     has_common;
+
+	xdfenv_t *env;
+	xpparam_t const *xpp;
+};
+
+struct region {
+	unsigned int begin1, end1;
+	unsigned int begin2, end2;
+};
+
+#define LINE_MAP(i, a) (i->line_map[(a) - i->ptr_shift])
+
+#define NEXT_PTR(index, ptr) \
+	(index->next_ptrs[(ptr) - index->ptr_shift])
+
+#define CNT(index, ptr) \
+	((LINE_MAP(index, ptr))->cnt)
+
+#define REC(env, s, l) \
+	(env->xdf##s.recs[l - 1])
+
+static int cmp_recs(xpparam_t const *xpp,
+	xrecord_t *r1, xrecord_t *r2)
+{
+	return r1->ha == r2->ha &&
+		xdl_recmatch(r1->ptr, r1->size, r2->ptr, r2->size,
+			    xpp->flags);
+}
+
+#define CMP_ENV(xpp, env, s1, l1, s2, l2) \
+	(cmp_recs(xpp, REC(env, s1, l1), REC(env, s2, l2)))
+
+#define CMP(i, s1, l1, s2, l2) \
+	(cmp_recs(i->xpp, REC(i->env, s1, l1), REC(i->env, s2, l2)))
+
+#define TABLE_HASH(index, side, line) \
+	XDL_HASHLONG((REC(index->env, side, line))->ha, index->table_bits)
+
+static int scanA(struct histindex *index, int line1, int count1)
+{
+	unsigned int ptr, tbl_idx;
+	unsigned int chain_len;
+	struct record **rec_chain, *rec;
+
+	for (ptr = LINE_END(1); line1 <= ptr; ptr--) {
+		tbl_idx = TABLE_HASH(index, 1, ptr);
+		rec_chain = index->records + tbl_idx;
+		rec = *rec_chain;
+
+		chain_len = 0;
+		while (rec) {
+			if (CMP(index, 1, rec->ptr, 1, ptr)) {
+				/*
+				 * ptr is identical to another element. Insert
+				 * it onto the front of the existing element
+				 * chain.
+				 */
+				NEXT_PTR(index, ptr) = rec->ptr;
+				rec->ptr = ptr;
+				/* cap rec->cnt at MAX_CNT */
+				rec->cnt = XDL_MIN(MAX_CNT, rec->cnt + 1);
+				LINE_MAP(index, ptr) = rec;
+				goto continue_scan;
+			}
+
+			rec = rec->next;
+			chain_len++;
+		}
+
+		if (chain_len == index->max_chain_length)
+			return -1;
+
+		/*
+		 * This is the first time we have ever seen this particular
+		 * element in the sequence. Construct a new chain for it.
+		 */
+		if (!(rec = xdl_cha_alloc(&index->rcha)))
+			return -1;
+		rec->ptr = ptr;
+		rec->cnt = 1;
+		rec->next = *rec_chain;
+		*rec_chain = rec;
+		LINE_MAP(index, ptr) = rec;
+
+continue_scan:
+		; /* no op */
+	}
+
+	return 0;
+}
+
+static int try_lcs(struct histindex *index, struct region *lcs, int b_ptr,
+	int line1, int count1, int line2, int count2)
+{
+	unsigned int b_next = b_ptr + 1;
+	struct record *rec = index->records[TABLE_HASH(index, 2, b_ptr)];
+	unsigned int as, ae, bs, be, np, rc;
+	int should_break;
+
+	for (; rec; rec = rec->next) {
+		if (rec->cnt > index->cnt) {
+			if (!index->has_common)
+				index->has_common = CMP(index, 1, rec->ptr, 2, b_ptr);
+			continue;
+		}
+
+		as = rec->ptr;
+		if (!CMP(index, 1, as, 2, b_ptr))
+			continue;
+
+		index->has_common = 1;
+		for (;;) {
+			should_break = 0;
+			np = NEXT_PTR(index, as);
+			bs = b_ptr;
+			ae = as;
+			be = bs;
+			rc = rec->cnt;
+
+			while (line1 < as && line2 < bs
+				&& CMP(index, 1, as - 1, 2, bs - 1)) {
+				as--;
+				bs--;
+				if (1 < rc)
+					rc = XDL_MIN(rc, CNT(index, as));
+			}
+			while (ae < LINE_END(1) && be < LINE_END(2)
+				&& CMP(index, 1, ae + 1, 2, be + 1)) {
+				ae++;
+				be++;
+				if (1 < rc)
+					rc = XDL_MIN(rc, CNT(index, ae));
+			}
+
+			if (b_next <= be)
+				b_next = be + 1;
+			if (lcs->end1 - lcs->begin1 < ae - as || rc < index->cnt) {
+				lcs->begin1 = as;
+				lcs->begin2 = bs;
+				lcs->end1 = ae;
+				lcs->end2 = be;
+				index->cnt = rc;
+			}
+
+			if (np == 0)
+				break;
+
+			while (np <= ae) {
+				np = NEXT_PTR(index, np);
+				if (np == 0) {
+					should_break = 1;
+					break;
+				}
+			}
+
+			if (should_break)
+				break;
+
+			as = np;
+		}
+	}
+	return b_next;
+}
+
+static int find_lcs(struct histindex *index, struct region *lcs,
+	int line1, int count1, int line2, int count2) {
+	int b_ptr;
+
+	if (scanA(index, line1, count1))
+		return -1;
+
+	index->cnt = index->max_chain_length + 1;
+
+	for (b_ptr = line2; b_ptr <= LINE_END(2); )
+		b_ptr = try_lcs(index, lcs, b_ptr, line1, count1, line2, count2);
+
+	return index->has_common && index->max_chain_length < index->cnt;
+}
+
+static int fall_back_to_classic_diff(struct histindex *index,
+		int line1, int count1, int line2, int count2)
+{
+	xpparam_t xpp;
+	xpp.flags = index->xpp->flags & ~XDF_DIFF_ALGORITHM_MASK;
+
+	return xdl_fall_back_diff(index->env, &xpp,
+				  line1, count1, line2, count2);
+}
+
+static int histogram_diff(xpparam_t const *xpp, xdfenv_t *env,
+	int line1, int count1, int line2, int count2)
+{
+	struct histindex index;
+	struct region lcs;
+	int sz;
+	int result = -1;
+
+	if (count1 <= 0 && count2 <= 0)
+		return 0;
+
+	if (LINE_END(1) >= MAX_PTR)
+		return -1;
+
+	if (!count1) {
+		while(count2--)
+			env->xdf2.rchg[line2++ - 1] = 1;
+		return 0;
+	} else if (!count2) {
+		while(count1--)
+			env->xdf1.rchg[line1++ - 1] = 1;
+		return 0;
+	}
+
+	memset(&index, 0, sizeof(index));
+
+	index.env = env;
+	index.xpp = xpp;
+
+	index.records = NULL;
+	index.line_map = NULL;
+	/* in case of early xdl_cha_free() */
+	index.rcha.head = NULL;
+
+	index.table_bits = xdl_hashbits(count1);
+	sz = index.records_size = 1 << index.table_bits;
+	sz *= sizeof(struct record *);
+	if (!(index.records = (struct record **) xdl_malloc(sz)))
+		goto cleanup;
+	memset(index.records, 0, sz);
+
+	sz = index.line_map_size = count1;
+	sz *= sizeof(struct record *);
+	if (!(index.line_map = (struct record **) xdl_malloc(sz)))
+		goto cleanup;
+	memset(index.line_map, 0, sz);
+
+	sz = index.line_map_size;
+	sz *= sizeof(unsigned int);
+	if (!(index.next_ptrs = (unsigned int *) xdl_malloc(sz)))
+		goto cleanup;
+	memset(index.next_ptrs, 0, sz);
+
+	/* lines / 4 + 1 comes from xprepare.c:xdl_prepare_ctx() */
+	if (xdl_cha_init(&index.rcha, sizeof(struct record), count1 / 4 + 1) < 0)
+		goto cleanup;
+
+	index.ptr_shift = line1;
+	index.max_chain_length = 64;
+
+	memset(&lcs, 0, sizeof(lcs));
+	if (find_lcs(&index, &lcs, line1, count1, line2, count2))
+		result = fall_back_to_classic_diff(&index, line1, count1, line2, count2);
+	else {
+		if (lcs.begin1 == 0 && lcs.begin2 == 0) {
+			while (count1--)
+				env->xdf1.rchg[line1++ - 1] = 1;
+			while (count2--)
+				env->xdf2.rchg[line2++ - 1] = 1;
+			result = 0;
+		} else {
+			result = histogram_diff(xpp, env,
+						line1, lcs.begin1 - line1,
+						line2, lcs.begin2 - line2);
+			if (result)
+				goto cleanup;
+			result = histogram_diff(xpp, env,
+						lcs.end1 + 1, LINE_END(1) - lcs.end1,
+						lcs.end2 + 1, LINE_END(2) - lcs.end2);
+			if (result)
+				goto cleanup;
+		}
+	}
+
+cleanup:
+	xdl_free(index.records);
+	xdl_free(index.line_map);
+	xdl_free(index.next_ptrs);
+	xdl_cha_free(&index.rcha);
+
+	return result;
+}
+
+int xdl_do_histogram_diff(mmfile_t *file1, mmfile_t *file2,
+	xpparam_t const *xpp, xdfenv_t *env)
+{
+	if (xdl_prepare_env(file1, file2, xpp, env) < 0)
+		return -1;
+
+	return histogram_diff(xpp, env,
+		env->xdf1.dstart + 1, env->xdf1.dend - env->xdf1.dstart + 1,
+		env->xdf2.dstart + 1, env->xdf2.dend - env->xdf2.dstart + 1);
+}
diff --git a/xdiff/xmacros.h b/xdiff/xmacros.h
index 8ef232c..165a895 100644
--- a/xdiff/xmacros.h
+++ b/xdiff/xmacros.h
@@ -30,6 +30,7 @@
 #define XDL_MAX(a, b) ((a) > (b) ? (a): (b))
 #define XDL_ABS(v) ((v) >= 0 ? (v): -(v))
 #define XDL_ISDIGIT(c) ((c) >= '0' && (c) <= '9')
+#define XDL_ISSPACE(c) (isspace((unsigned char)(c)))
 #define XDL_ADDBITS(v,b)	((v) + ((v) >> (b)))
 #define XDL_MASKBITS(b)		((1UL << (b)) - 1)
 #define XDL_HASHLONG(v,b)	(XDL_ADDBITS((unsigned long)(v), b) & XDL_MASKBITS(b))
diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c
index 1cb65a9..9e13b25 100644
--- a/xdiff/xmerge.c
+++ b/xdiff/xmerge.c
@@ -28,6 +28,7 @@
 	 * 0 = conflict,
 	 * 1 = no conflict, take first,
 	 * 2 = no conflict, take second.
+	 * 3 = no conflict, take both.
 	 */
 	int mode;
 	/*
@@ -144,13 +145,16 @@
 
 static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,
 			      xdfenv_t *xe2, const char *name2,
+			      const char *name3,
 			      int size, int i, int style,
-			      xdmerge_t *m, char *dest)
+			      xdmerge_t *m, char *dest, int marker_size)
 {
-	const int marker_size = 7;
 	int marker1_size = (name1 ? strlen(name1) + 1 : 0);
 	int marker2_size = (name2 ? strlen(name2) + 1 : 0);
-	int j;
+	int marker3_size = (name3 ? strlen(name3) + 1 : 0);
+
+	if (marker_size <= 0)
+		marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
 
 	/* Before conflicting part */
 	size += xdl_recs_copy(xe1, i, m->i1 - i, 0,
@@ -159,8 +163,8 @@
 	if (!dest) {
 		size += marker_size + 1 + marker1_size;
 	} else {
-		for (j = 0; j < marker_size; j++)
-			dest[size++] = '<';
+		memset(dest + size, '<', marker_size);
+		size += marker_size;
 		if (marker1_size) {
 			dest[size] = ' ';
 			memcpy(dest + size + 1, name1, marker1_size - 1);
@@ -176,10 +180,15 @@
 	if (style == XDL_MERGE_DIFF3) {
 		/* Shared preimage */
 		if (!dest) {
-			size += marker_size + 1;
+			size += marker_size + 1 + marker3_size;
 		} else {
-			for (j = 0; j < marker_size; j++)
-				dest[size++] = '|';
+			memset(dest + size, '|', marker_size);
+			size += marker_size;
+			if (marker3_size) {
+				dest[size] = ' ';
+				memcpy(dest + size + 1, name3, marker3_size - 1);
+				size += marker3_size;
+			}
 			dest[size++] = '\n';
 		}
 		size += xdl_orig_copy(xe1, m->i0, m->chg0, 1,
@@ -189,8 +198,8 @@
 	if (!dest) {
 		size += marker_size + 1;
 	} else {
-		for (j = 0; j < marker_size; j++)
-			dest[size++] = '=';
+		memset(dest + size, '=', marker_size);
+		size += marker_size;
 		dest[size++] = '\n';
 	}
 
@@ -200,8 +209,8 @@
 	if (!dest) {
 		size += marker_size + 1 + marker2_size;
 	} else {
-		for (j = 0; j < marker_size; j++)
-			dest[size++] = '>';
+		memset(dest + size, '>', marker_size);
+		size += marker_size;
 		if (marker2_size) {
 			dest[size] = ' ';
 			memcpy(dest + size + 1, name2, marker2_size - 1);
@@ -214,22 +223,35 @@
 
 static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,
 				 xdfenv_t *xe2, const char *name2,
-				 xdmerge_t *m, char *dest, int style)
+				 const char *ancestor_name,
+				 int favor,
+				 xdmerge_t *m, char *dest, int style,
+				 int marker_size)
 {
 	int size, i;
 
 	for (size = i = 0; m; m = m->next) {
+		if (favor && !m->mode)
+			m->mode = favor;
+
 		if (m->mode == 0)
 			size = fill_conflict_hunk(xe1, name1, xe2, name2,
-						  size, i, style, m, dest);
-		else if (m->mode == 1)
-			size += xdl_recs_copy(xe1, i, m->i1 + m->chg1 - i, 0,
+						  ancestor_name,
+						  size, i, style, m, dest,
+						  marker_size);
+		else if (m->mode & 3) {
+			/* Before conflicting part */
+			size += xdl_recs_copy(xe1, i, m->i1 - i, 0,
 					      dest ? dest + size : NULL);
-		else if (m->mode == 2)
-			size += xdl_recs_copy(xe2, m->i2 - m->i1 + i,
-					      m->i1 + m->chg2 - i, 0,
-					      dest ? dest + size : NULL);
-		else
+			/* Postimage from side #1 */
+			if (m->mode & 1)
+				size += xdl_recs_copy(xe1, m->i1, m->chg1, 1,
+						      dest ? dest + size : NULL);
+			/* Postimage from side #2 */
+			if (m->mode & 2)
+				size += xdl_recs_copy(xe2, m->i2, m->chg2, 1,
+						      dest ? dest + size : NULL);
+		} else
 			continue;
 		i = m->i1 + m->chg1;
 	}
@@ -314,7 +336,7 @@
 static int line_contains_alnum(const char *ptr, long size)
 {
 	while (size--)
-		if (isalnum(*(ptr++)))
+		if (isalnum((unsigned char)*(ptr++)))
 			return 1;
 	return 0;
 }
@@ -384,13 +406,19 @@
  *
  * returns < 0 on error, == 0 for no conflicts, else number of conflicts
  */
-static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
-		xdfenv_t *xe2, xdchange_t *xscr2, const char *name2,
-		int flags, xpparam_t const *xpp, mmbuffer_t *result) {
+static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,
+		xdfenv_t *xe2, xdchange_t *xscr2,
+		xmparam_t const *xmp, mmbuffer_t *result)
+{
 	xdmerge_t *changes, *c;
+	xpparam_t const *xpp = &xmp->xpp;
+	const char *const ancestor_name = xmp->ancestor;
+	const char *const name1 = xmp->file1;
+	const char *const name2 = xmp->file2;
 	int i0, i1, i2, chg0, chg1, chg2;
-	int level = flags & XDL_MERGE_LEVEL_MASK;
-	int style = flags & XDL_MERGE_STYLE_MASK;
+	int level = xmp->level;
+	int style = xmp->style;
+	int favor = xmp->favor;
 
 	if (style == XDL_MERGE_DIFF3) {
 		/*
@@ -522,26 +550,31 @@
 	}
 	/* output */
 	if (result) {
+		int marker_size = xmp->marker_size;
 		int size = xdl_fill_merge_buffer(xe1, name1, xe2, name2,
-			changes, NULL, style);
+						 ancestor_name,
+						 favor, changes, NULL, style,
+						 marker_size);
 		result->ptr = xdl_malloc(size);
 		if (!result->ptr) {
 			xdl_cleanup_merge(changes);
 			return -1;
 		}
 		result->size = size;
-		xdl_fill_merge_buffer(xe1, name1, xe2, name2, changes,
-				      result->ptr, style);
+		xdl_fill_merge_buffer(xe1, name1, xe2, name2,
+				      ancestor_name, favor, changes,
+				      result->ptr, style, marker_size);
 	}
 	return xdl_cleanup_merge(changes);
 }
 
-int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1,
-		mmfile_t *mf2, const char *name2,
-		xpparam_t const *xpp, int flags, mmbuffer_t *result) {
+int xdl_merge(mmfile_t *orig, mmfile_t *mf1, mmfile_t *mf2,
+		xmparam_t const *xmp, mmbuffer_t *result)
+{
 	xdchange_t *xscr1, *xscr2;
 	xdfenv_t xe1, xe2;
 	int status;
+	xpparam_t const *xpp = &xmp->xpp;
 
 	result->ptr = NULL;
 	result->size = 0;
@@ -572,9 +605,9 @@
 		memcpy(result->ptr, mf1->ptr, mf1->size);
 		result->size = mf1->size;
 	} else {
-		status = xdl_do_merge(&xe1, xscr1, name1,
-				      &xe2, xscr2, name2,
-				      flags, xpp, result);
+		status = xdl_do_merge(&xe1, xscr1,
+				      &xe2, xscr2,
+				      xmp, result);
 	}
 	xdl_free_script(xscr1);
 	xdl_free_script(xscr2);
diff --git a/xdiff/xpatience.c b/xdiff/xpatience.c
index e42c16a..04e1a1a 100644
--- a/xdiff/xpatience.c
+++ b/xdiff/xpatience.c
@@ -287,34 +287,11 @@
 static int fall_back_to_classic_diff(struct hashmap *map,
 		int line1, int count1, int line2, int count2)
 {
-	/*
-	 * This probably does not work outside Git, since
-	 * we have a very simple mmfile structure.
-	 *
-	 * Note: ideally, we would reuse the prepared environment, but
-	 * the libxdiff interface does not (yet) allow for diffing only
-	 * ranges of lines instead of the whole files.
-	 */
-	mmfile_t subfile1, subfile2;
 	xpparam_t xpp;
-	xdfenv_t env;
+	xpp.flags = map->xpp->flags & ~XDF_DIFF_ALGORITHM_MASK;
 
-	subfile1.ptr = (char *)map->env->xdf1.recs[line1 - 1]->ptr;
-	subfile1.size = map->env->xdf1.recs[line1 + count1 - 2]->ptr +
-		map->env->xdf1.recs[line1 + count1 - 2]->size - subfile1.ptr;
-	subfile2.ptr = (char *)map->env->xdf2.recs[line2 - 1]->ptr;
-	subfile2.size = map->env->xdf2.recs[line2 + count2 - 2]->ptr +
-		map->env->xdf2.recs[line2 + count2 - 2]->size - subfile2.ptr;
-	xpp.flags = map->xpp->flags & ~XDF_PATIENCE_DIFF;
-	if (xdl_do_diff(&subfile1, &subfile2, &xpp, &env) < 0)
-		return -1;
-
-	memcpy(map->env->xdf1.rchg + line1 - 1, env.xdf1.rchg, count1);
-	memcpy(map->env->xdf2.rchg + line2 - 1, env.xdf2.rchg, count2);
-
-	xdl_free_env(&env);
-
-	return 0;
+	return xdl_fall_back_diff(map->env, &xpp,
+				  line1, count1, line2, count2);
 }
 
 /*
diff --git a/xdiff/xprepare.c b/xdiff/xprepare.c
index 1689085..63a22c6 100644
--- a/xdiff/xprepare.c
+++ b/xdiff/xprepare.c
@@ -26,6 +26,8 @@
 #define XDL_KPDIS_RUN 4
 #define XDL_MAX_EQLIMIT 1024
 #define XDL_SIMSCAN_WINDOW 100
+#define XDL_GUESS_NLINES1 256
+#define XDL_GUESS_NLINES2 20
 
 
 typedef struct s_xdlclass {
@@ -34,6 +36,7 @@
 	char const *line;
 	long size;
 	long idx;
+	long len1, len2;
 } xdlclass_t;
 
 typedef struct s_xdlclassifier {
@@ -41,6 +44,8 @@
 	long hsize;
 	xdlclass_t **rchash;
 	chastore_t ncha;
+	xdlclass_t **rcrecs;
+	long alloc;
 	long count;
 	long flags;
 } xdlclassifier_t;
@@ -50,22 +55,20 @@
 
 static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags);
 static void xdl_free_classifier(xdlclassifier_t *cf);
-static int xdl_classify_record(xdlclassifier_t *cf, xrecord_t **rhash, unsigned int hbits,
-			       xrecord_t *rec);
-static int xdl_prepare_ctx(mmfile_t *mf, long narec, xpparam_t const *xpp,
+static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash,
+			       unsigned int hbits, xrecord_t *rec);
+static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,
 			   xdlclassifier_t *cf, xdfile_t *xdf);
 static void xdl_free_ctx(xdfile_t *xdf);
 static int xdl_clean_mmatch(char const *dis, long i, long s, long e);
-static int xdl_cleanup_records(xdfile_t *xdf1, xdfile_t *xdf2);
+static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2);
 static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2);
-static int xdl_optimize_ctxs(xdfile_t *xdf1, xdfile_t *xdf2);
+static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2);
 
 
 
 
 static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags) {
-	long i;
-
 	cf->flags = flags;
 
 	cf->hbits = xdl_hashbits((unsigned int) size);
@@ -80,8 +83,15 @@
 		xdl_cha_free(&cf->ncha);
 		return -1;
 	}
-	for (i = 0; i < cf->hsize; i++)
-		cf->rchash[i] = NULL;
+	memset(cf->rchash, 0, cf->hsize * sizeof(xdlclass_t *));
+
+	cf->alloc = size;
+	if (!(cf->rcrecs = (xdlclass_t **) xdl_malloc(cf->alloc * sizeof(xdlclass_t *)))) {
+
+		xdl_free(cf->rchash);
+		xdl_cha_free(&cf->ncha);
+		return -1;
+	}
 
 	cf->count = 0;
 
@@ -91,16 +101,18 @@
 
 static void xdl_free_classifier(xdlclassifier_t *cf) {
 
+	xdl_free(cf->rcrecs);
 	xdl_free(cf->rchash);
 	xdl_cha_free(&cf->ncha);
 }
 
 
-static int xdl_classify_record(xdlclassifier_t *cf, xrecord_t **rhash, unsigned int hbits,
-			       xrecord_t *rec) {
+static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash,
+			       unsigned int hbits, xrecord_t *rec) {
 	long hi;
 	char const *line;
 	xdlclass_t *rcrec;
+	xdlclass_t **rcrecs;
 
 	line = rec->ptr;
 	hi = (long) XDL_HASHLONG(rec->ha, cf->hbits);
@@ -116,13 +128,25 @@
 			return -1;
 		}
 		rcrec->idx = cf->count++;
+		if (cf->count > cf->alloc) {
+			cf->alloc *= 2;
+			if (!(rcrecs = (xdlclass_t **) xdl_realloc(cf->rcrecs, cf->alloc * sizeof(xdlclass_t *)))) {
+
+				return -1;
+			}
+			cf->rcrecs = rcrecs;
+		}
+		cf->rcrecs[rcrec->idx] = rcrec;
 		rcrec->line = line;
 		rcrec->size = rec->size;
 		rcrec->ha = rec->ha;
+		rcrec->len1 = rcrec->len2 = 0;
 		rcrec->next = cf->rchash[hi];
 		cf->rchash[hi] = rcrec;
 	}
 
+	(pass == 1) ? rcrec->len1++ : rcrec->len2++;
+
 	rec->ha = (unsigned long) rcrec->idx;
 
 	hi = (long) XDL_HASHLONG(rec->ha, hbits);
@@ -133,10 +157,10 @@
 }
 
 
-static int xdl_prepare_ctx(mmfile_t *mf, long narec, xpparam_t const *xpp,
+static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,
 			   xdlclassifier_t *cf, xdfile_t *xdf) {
 	unsigned int hbits;
-	long i, nrec, hsize, bsize;
+	long nrec, hsize, bsize;
 	unsigned long hav;
 	char const *blk, *cur, *top, *prev;
 	xrecord_t *crec;
@@ -146,96 +170,59 @@
 	char *rchg;
 	long *rindex;
 
-	if (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0) {
+	ha = NULL;
+	rindex = NULL;
+	rchg = NULL;
+	rhash = NULL;
+	recs = NULL;
 
-		return -1;
+	if (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)
+		goto abort;
+	if (!(recs = (xrecord_t **) xdl_malloc(narec * sizeof(xrecord_t *))))
+		goto abort;
+
+	if (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF)
+		hbits = hsize = 0;
+	else {
+		hbits = xdl_hashbits((unsigned int) narec);
+		hsize = 1 << hbits;
+		if (!(rhash = (xrecord_t **) xdl_malloc(hsize * sizeof(xrecord_t *))))
+			goto abort;
+		memset(rhash, 0, hsize * sizeof(xrecord_t *));
 	}
-	if (!(recs = (xrecord_t **) xdl_malloc(narec * sizeof(xrecord_t *)))) {
-
-		xdl_cha_free(&xdf->rcha);
-		return -1;
-	}
-
-	hbits = xdl_hashbits((unsigned int) narec);
-	hsize = 1 << hbits;
-	if (!(rhash = (xrecord_t **) xdl_malloc(hsize * sizeof(xrecord_t *)))) {
-
-		xdl_free(recs);
-		xdl_cha_free(&xdf->rcha);
-		return -1;
-	}
-	for (i = 0; i < hsize; i++)
-		rhash[i] = NULL;
 
 	nrec = 0;
 	if ((cur = blk = xdl_mmfile_first(mf, &bsize)) != NULL) {
-		for (top = blk + bsize;;) {
-			if (cur >= top) {
-				if (!(cur = blk = xdl_mmfile_next(mf, &bsize)))
-					break;
-				top = blk + bsize;
-			}
+		for (top = blk + bsize; cur < top; ) {
 			prev = cur;
 			hav = xdl_hash_record(&cur, top, xpp->flags);
 			if (nrec >= narec) {
 				narec *= 2;
-				if (!(rrecs = (xrecord_t **) xdl_realloc(recs, narec * sizeof(xrecord_t *)))) {
-
-					xdl_free(rhash);
-					xdl_free(recs);
-					xdl_cha_free(&xdf->rcha);
-					return -1;
-				}
+				if (!(rrecs = (xrecord_t **) xdl_realloc(recs, narec * sizeof(xrecord_t *))))
+					goto abort;
 				recs = rrecs;
 			}
-			if (!(crec = xdl_cha_alloc(&xdf->rcha))) {
-
-				xdl_free(rhash);
-				xdl_free(recs);
-				xdl_cha_free(&xdf->rcha);
-				return -1;
-			}
+			if (!(crec = xdl_cha_alloc(&xdf->rcha)))
+				goto abort;
 			crec->ptr = prev;
 			crec->size = (long) (cur - prev);
 			crec->ha = hav;
 			recs[nrec++] = crec;
 
-			if (xdl_classify_record(cf, rhash, hbits, crec) < 0) {
-
-				xdl_free(rhash);
-				xdl_free(recs);
-				xdl_cha_free(&xdf->rcha);
-				return -1;
-			}
+			if ((XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) &&
+			    xdl_classify_record(pass, cf, rhash, hbits, crec) < 0)
+				goto abort;
 		}
 	}
 
-	if (!(rchg = (char *) xdl_malloc((nrec + 2) * sizeof(char)))) {
-
-		xdl_free(rhash);
-		xdl_free(recs);
-		xdl_cha_free(&xdf->rcha);
-		return -1;
-	}
+	if (!(rchg = (char *) xdl_malloc((nrec + 2) * sizeof(char))))
+		goto abort;
 	memset(rchg, 0, (nrec + 2) * sizeof(char));
 
-	if (!(rindex = (long *) xdl_malloc((nrec + 1) * sizeof(long)))) {
-
-		xdl_free(rchg);
-		xdl_free(rhash);
-		xdl_free(recs);
-		xdl_cha_free(&xdf->rcha);
-		return -1;
-	}
-	if (!(ha = (unsigned long *) xdl_malloc((nrec + 1) * sizeof(unsigned long)))) {
-
-		xdl_free(rindex);
-		xdl_free(rchg);
-		xdl_free(rhash);
-		xdl_free(recs);
-		xdl_cha_free(&xdf->rcha);
-		return -1;
-	}
+	if (!(rindex = (long *) xdl_malloc((nrec + 1) * sizeof(long))))
+		goto abort;
+	if (!(ha = (unsigned long *) xdl_malloc((nrec + 1) * sizeof(unsigned long))))
+		goto abort;
 
 	xdf->nrec = nrec;
 	xdf->recs = recs;
@@ -249,6 +236,15 @@
 	xdf->dend = nrec - 1;
 
 	return 0;
+
+abort:
+	xdl_free(ha);
+	xdl_free(rindex);
+	xdl_free(rchg);
+	xdl_free(rhash);
+	xdl_free(recs);
+	xdl_cha_free(&xdf->rcha);
+	return -1;
 }
 
 
@@ -265,39 +261,52 @@
 
 int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
 		    xdfenv_t *xe) {
-	long enl1, enl2;
+	long enl1, enl2, sample;
 	xdlclassifier_t cf;
 
-	enl1 = xdl_guess_lines(mf1) + 1;
-	enl2 = xdl_guess_lines(mf2) + 1;
+	memset(&cf, 0, sizeof(cf));
 
-	if (xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0) {
+	/*
+	 * For histogram diff, we can afford a smaller sample size and
+	 * thus a poorer estimate of the number of lines, as the hash
+	 * table (rhash) won't be filled up/grown. The number of lines
+	 * (nrecs) will be updated correctly anyway by
+	 * xdl_prepare_ctx().
+	 */
+	sample = (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF
+		  ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1);
 
+	enl1 = xdl_guess_lines(mf1, sample) + 1;
+	enl2 = xdl_guess_lines(mf2, sample) + 1;
+
+	if (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF &&
+	    xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0)
 		return -1;
-	}
 
-	if (xdl_prepare_ctx(mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {
+	if (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {
 
 		xdl_free_classifier(&cf);
 		return -1;
 	}
-	if (xdl_prepare_ctx(mf2, enl2, xpp, &cf, &xe->xdf2) < 0) {
+	if (xdl_prepare_ctx(2, mf2, enl2, xpp, &cf, &xe->xdf2) < 0) {
 
 		xdl_free_ctx(&xe->xdf1);
 		xdl_free_classifier(&cf);
 		return -1;
 	}
 
-	xdl_free_classifier(&cf);
-
-	if (!(xpp->flags & XDF_PATIENCE_DIFF) &&
-			xdl_optimize_ctxs(&xe->xdf1, &xe->xdf2) < 0) {
+	if ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&
+	    (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) &&
+	    xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) {
 
 		xdl_free_ctx(&xe->xdf2);
 		xdl_free_ctx(&xe->xdf1);
 		return -1;
 	}
 
+	if (!(xpp->flags & XDF_HISTOGRAM_DIFF))
+		xdl_free_classifier(&cf);
+
 	return 0;
 }
 
@@ -372,11 +381,10 @@
  * matches on the other file. Also, lines that have multiple matches
  * might be potentially discarded if they happear in a run of discardable.
  */
-static int xdl_cleanup_records(xdfile_t *xdf1, xdfile_t *xdf2) {
-	long i, nm, rhi, nreff, mlim;
-	unsigned long hav;
+static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) {
+	long i, nm, nreff, mlim;
 	xrecord_t **recs;
-	xrecord_t *rec;
+	xdlclass_t *rcrec;
 	char *dis, *dis1, *dis2;
 
 	if (!(dis = (char *) xdl_malloc(xdf1->nrec + xdf2->nrec + 2))) {
@@ -390,22 +398,16 @@
 	if ((mlim = xdl_bogosqrt(xdf1->nrec)) > XDL_MAX_EQLIMIT)
 		mlim = XDL_MAX_EQLIMIT;
 	for (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) {
-		hav = (*recs)->ha;
-		rhi = (long) XDL_HASHLONG(hav, xdf2->hbits);
-		for (nm = 0, rec = xdf2->rhash[rhi]; rec; rec = rec->next)
-			if (rec->ha == hav && ++nm == mlim)
-				break;
+		rcrec = cf->rcrecs[(*recs)->ha];
+		nm = rcrec ? rcrec->len2 : 0;
 		dis1[i] = (nm == 0) ? 0: (nm >= mlim) ? 2: 1;
 	}
 
 	if ((mlim = xdl_bogosqrt(xdf2->nrec)) > XDL_MAX_EQLIMIT)
 		mlim = XDL_MAX_EQLIMIT;
 	for (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) {
-		hav = (*recs)->ha;
-		rhi = (long) XDL_HASHLONG(hav, xdf1->hbits);
-		for (nm = 0, rec = xdf1->rhash[rhi]; rec; rec = rec->next)
-			if (rec->ha == hav && ++nm == mlim)
-				break;
+		rcrec = cf->rcrecs[(*recs)->ha];
+		nm = rcrec ? rcrec->len1 : 0;
 		dis2[i] = (nm == 0) ? 0: (nm >= mlim) ? 2: 1;
 	}
 
@@ -468,10 +470,10 @@
 }
 
 
-static int xdl_optimize_ctxs(xdfile_t *xdf1, xdfile_t *xdf2) {
+static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) {
 
 	if (xdl_trim_ends(xdf1, xdf2) < 0 ||
-	    xdl_cleanup_records(xdf1, xdf2) < 0) {
+	    xdl_cleanup_records(cf, xdf1, xdf2) < 0) {
 
 		return -1;
 	}
diff --git a/xdiff/xutils.c b/xdiff/xutils.c
index 04ad468..9504eae 100644
--- a/xdiff/xutils.c
+++ b/xdiff/xutils.c
@@ -20,14 +20,12 @@
  *
  */
 
+#include <limits.h>
+#include <assert.h>
 #include "xinclude.h"
 
 
 
-#define XDL_GUESS_NLINES 256
-
-
-
 
 long xdl_bogosqrt(long n) {
 	long i;
@@ -71,12 +69,6 @@
 }
 
 
-void *xdl_mmfile_next(mmfile_t *mmf, long *size)
-{
-	return NULL;
-}
-
-
 long xdl_mmfile_size(mmfile_t *mmf)
 {
 	return mmf->size;
@@ -130,47 +122,12 @@
 	return data;
 }
 
-
-void *xdl_cha_first(chastore_t *cha) {
-	chanode_t *sncur;
-
-	if (!(cha->sncur = sncur = cha->head))
-		return NULL;
-
-	cha->scurr = 0;
-
-	return (char *) sncur + sizeof(chanode_t) + cha->scurr;
-}
-
-
-void *xdl_cha_next(chastore_t *cha) {
-	chanode_t *sncur;
-
-	if (!(sncur = cha->sncur))
-		return NULL;
-	cha->scurr += cha->isize;
-	if (cha->scurr == sncur->icurr) {
-		if (!(sncur = cha->sncur = sncur->next))
-			return NULL;
-		cha->scurr = 0;
-	}
-
-	return (char *) sncur + sizeof(chanode_t) + cha->scurr;
-}
-
-
-long xdl_guess_lines(mmfile_t *mf) {
+long xdl_guess_lines(mmfile_t *mf, long sample) {
 	long nl = 0, size, tsize = 0;
 	char const *data, *cur, *top;
 
 	if ((cur = data = xdl_mmfile_first(mf, &size)) != NULL) {
-		for (top = data + size; nl < XDL_GUESS_NLINES;) {
-			if (cur >= top) {
-				tsize += (long) (cur - data);
-				if (!(cur = data = xdl_mmfile_next(mf, &size)))
-					break;
-				top = data + size;
-			}
+		for (top = data + size; nl < sample && cur < top; ) {
 			nl++;
 			if (!(cur = memchr(cur, '\n', top - cur)))
 				cur = top;
@@ -190,48 +147,68 @@
 {
 	int i1, i2;
 
+	if (s1 == s2 && !memcmp(l1, l2, s1))
+		return 1;
+	if (!(flags & XDF_WHITESPACE_FLAGS))
+		return 0;
+
+	i1 = 0;
+	i2 = 0;
+
+	/*
+	 * -w matches everything that matches with -b, and -b in turn
+	 * matches everything that matches with --ignore-space-at-eol.
+	 *
+	 * Each flavor of ignoring needs different logic to skip whitespaces
+	 * while we have both sides to compare.
+	 */
 	if (flags & XDF_IGNORE_WHITESPACE) {
-		for (i1 = i2 = 0; i1 < s1 && i2 < s2; ) {
-			if (isspace(l1[i1]))
-				while (isspace(l1[i1]) && i1 < s1)
-					i1++;
-			if (isspace(l2[i2]))
-				while (isspace(l2[i2]) && i2 < s2)
-					i2++;
-			if (i1 < s1 && i2 < s2 && l1[i1++] != l2[i2++])
+		goto skip_ws;
+		while (i1 < s1 && i2 < s2) {
+			if (l1[i1++] != l2[i2++])
 				return 0;
+		skip_ws:
+			while (i1 < s1 && XDL_ISSPACE(l1[i1]))
+				i1++;
+			while (i2 < s2 && XDL_ISSPACE(l2[i2]))
+				i2++;
 		}
-		return (i1 >= s1 && i2 >= s2);
 	} else if (flags & XDF_IGNORE_WHITESPACE_CHANGE) {
-		for (i1 = i2 = 0; i1 < s1 && i2 < s2; ) {
-			if (isspace(l1[i1])) {
-				if (!isspace(l2[i2]))
-					return 0;
-				while (isspace(l1[i1]) && i1 < s1)
+		while (i1 < s1 && i2 < s2) {
+			if (XDL_ISSPACE(l1[i1]) && XDL_ISSPACE(l2[i2])) {
+				/* Skip matching spaces and try again */
+				while (i1 < s1 && XDL_ISSPACE(l1[i1]))
 					i1++;
-				while (isspace(l2[i2]) && i2 < s2)
+				while (i2 < s2 && XDL_ISSPACE(l2[i2]))
 					i2++;
-			} else if (l1[i1++] != l2[i2++])
+				continue;
+			}
+			if (l1[i1++] != l2[i2++])
 				return 0;
 		}
-		return (i1 >= s1 && i2 >= s2);
 	} else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL) {
-		for (i1 = i2 = 0; i1 < s1 && i2 < s2; ) {
-			if (l1[i1] != l2[i2]) {
-				while (i1 < s1 && isspace(l1[i1]))
-					i1++;
-				while (i2 < s2 && isspace(l2[i2]))
-					i2++;
-				if (i1 < s1 || i2 < s2)
-					return 0;
-				return 1;
-			}
+		while (i1 < s1 && i2 < s2 && l1[i1++] == l2[i2++])
+			; /* keep going */
+	}
+
+	/*
+	 * After running out of one side, the remaining side must have
+	 * nothing but whitespace for the lines to match.  Note that
+	 * ignore-whitespace-at-eol case may break out of the loop
+	 * while there still are characters remaining on both lines.
+	 */
+	if (i1 < s1) {
+		while (i1 < s1 && XDL_ISSPACE(l1[i1]))
 			i1++;
+		if (s1 != i1)
+			return 0;
+	}
+	if (i2 < s2) {
+		while (i2 < s2 && XDL_ISSPACE(l2[i2]))
 			i2++;
-		}
-		return i1 >= s1 && i2 >= s2;
-	} else
-		return s1 == s2 && !memcmp(l1, l2, s1);
+		return (s2 == i2);
+	}
+	return 1;
 }
 
 static unsigned long xdl_hash_record_with_whitespace(char const **data,
@@ -240,20 +217,22 @@
 	char const *ptr = *data;
 
 	for (; ptr < top && *ptr != '\n'; ptr++) {
-		if (isspace(*ptr)) {
+		if (XDL_ISSPACE(*ptr)) {
 			const char *ptr2 = ptr;
-			while (ptr + 1 < top && isspace(ptr[1])
+			int at_eol;
+			while (ptr + 1 < top && XDL_ISSPACE(ptr[1])
 					&& ptr[1] != '\n')
 				ptr++;
+			at_eol = (top <= ptr + 1 || ptr[1] == '\n');
 			if (flags & XDF_IGNORE_WHITESPACE)
 				; /* already handled */
 			else if (flags & XDF_IGNORE_WHITESPACE_CHANGE
-					&& ptr[1] != '\n') {
+				 && !at_eol) {
 				ha += (ha << 5);
 				ha ^= (unsigned long) ' ';
 			}
 			else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL
-					&& ptr[1] != '\n') {
+				 && !at_eol) {
 				while (ptr2 != ptr + 1) {
 					ha += (ha << 5);
 					ha ^= (unsigned long) *ptr2;
@@ -270,6 +249,109 @@
 	return ha;
 }
 
+#ifdef XDL_FAST_HASH
+
+#define REPEAT_BYTE(x)  ((~0ul / 0xff) * (x))
+
+#define ONEBYTES	REPEAT_BYTE(0x01)
+#define NEWLINEBYTES	REPEAT_BYTE(0x0a)
+#define HIGHBITS	REPEAT_BYTE(0x80)
+
+/* Return the high bit set in the first byte that is a zero */
+static inline unsigned long has_zero(unsigned long a)
+{
+	return ((a - ONEBYTES) & ~a) & HIGHBITS;
+}
+
+static inline long count_masked_bytes(unsigned long mask)
+{
+	if (sizeof(long) == 8) {
+		/*
+		 * Jan Achrenius on G+: microoptimized version of
+		 * the simpler "(mask & ONEBYTES) * ONEBYTES >> 56"
+		 * that works for the bytemasks without having to
+		 * mask them first.
+		 */
+		/*
+		 * return mask * 0x0001020304050608 >> 56;
+		 *
+		 * Doing it like this avoids warnings on 32-bit machines.
+		 */
+		long a = (REPEAT_BYTE(0x01) / 0xff + 1);
+		return mask * a >> (sizeof(long) * 7);
+	} else {
+		/* Carl Chatfield / Jan Achrenius G+ version for 32-bit */
+		/* (000000 0000ff 00ffff ffffff) -> ( 1 1 2 3 ) */
+		long a = (0x0ff0001 + mask) >> 23;
+		/* Fix the 1 for 00 case */
+		return a & mask;
+	}
+}
+
+unsigned long xdl_hash_record(char const **data, char const *top, long flags)
+{
+	unsigned long hash = 5381;
+	unsigned long a = 0, mask = 0;
+	char const *ptr = *data;
+	char const *end = top - sizeof(unsigned long) + 1;
+
+	if (flags & XDF_WHITESPACE_FLAGS)
+		return xdl_hash_record_with_whitespace(data, top, flags);
+
+	ptr -= sizeof(unsigned long);
+	do {
+		hash += hash << 5;
+		hash ^= a;
+		ptr += sizeof(unsigned long);
+		if (ptr >= end)
+			break;
+		a = *(unsigned long *)ptr;
+		/* Do we have any '\n' bytes in this word? */
+		mask = has_zero(a ^ NEWLINEBYTES);
+	} while (!mask);
+
+	if (ptr >= end) {
+		/*
+		 * There is only a partial word left at the end of the
+		 * buffer. Because we may work with a memory mapping,
+		 * we have to grab the rest byte by byte instead of
+		 * blindly reading it.
+		 *
+		 * To avoid problems with masking in a signed value,
+		 * we use an unsigned char here.
+		 */
+		const char *p;
+		for (p = top - 1; p >= ptr; p--)
+			a = (a << 8) + *((const unsigned char *)p);
+		mask = has_zero(a ^ NEWLINEBYTES);
+		if (!mask)
+			/*
+			 * No '\n' found in the partial word.  Make a
+			 * mask that matches what we read.
+			 */
+			mask = 1UL << (8 * (top - ptr) + 7);
+	}
+
+	/* The mask *below* the first high bit set */
+	mask = (mask - 1) & ~mask;
+	mask >>= 7;
+	hash += hash << 5;
+	hash ^= a & mask;
+
+	/* Advance past the last (possibly partial) word */
+	ptr += count_masked_bytes(mask);
+
+	if (ptr < top) {
+		assert(*ptr == '\n');
+		ptr++;
+	}
+
+	*data = ptr;
+
+	return hash;
+}
+
+#else /* XDL_FAST_HASH */
 
 unsigned long xdl_hash_record(char const **data, char const *top, long flags) {
 	unsigned long ha = 5381;
@@ -287,6 +369,7 @@
 	return ha;
 }
 
+#endif /* XDL_FAST_HASH */
 
 unsigned int xdl_hashbits(unsigned int size) {
 	unsigned int val = 1, bits = 0;
@@ -318,20 +401,6 @@
 	return str - out;
 }
 
-
-long xdl_atol(char const *str, char const **next) {
-	long val, base;
-	char const *top;
-
-	for (top = str; XDL_ISDIGIT(*top); top++);
-	if (next)
-		*next = top;
-	for (val = 0, base = 1, top--; top >= str; top--, base *= 10)
-		val += base * (long)(*top - '0');
-	return val;
-}
-
-
 int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2,
 		      const char *func, long funclen, xdemitcb_t *ecb) {
 	int nb = 0;
@@ -380,3 +449,34 @@
 
 	return 0;
 }
+
+int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,
+		int line1, int count1, int line2, int count2)
+{
+	/*
+	 * This probably does not work outside Git, since
+	 * we have a very simple mmfile structure.
+	 *
+	 * Note: ideally, we would reuse the prepared environment, but
+	 * the libxdiff interface does not (yet) allow for diffing only
+	 * ranges of lines instead of the whole files.
+	 */
+	mmfile_t subfile1, subfile2;
+	xdfenv_t env;
+
+	subfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1]->ptr;
+	subfile1.size = diff_env->xdf1.recs[line1 + count1 - 2]->ptr +
+		diff_env->xdf1.recs[line1 + count1 - 2]->size - subfile1.ptr;
+	subfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1]->ptr;
+	subfile2.size = diff_env->xdf2.recs[line2 + count2 - 2]->ptr +
+		diff_env->xdf2.recs[line2 + count2 - 2]->size - subfile2.ptr;
+	if (xdl_do_diff(&subfile1, &subfile2, xpp, &env) < 0)
+		return -1;
+
+	memcpy(diff_env->xdf1.rchg + line1 - 1, env.xdf1.rchg, count1);
+	memcpy(diff_env->xdf2.rchg + line2 - 1, env.xdf2.rchg, count2);
+
+	xdl_free_env(&env);
+
+	return 0;
+}
diff --git a/xdiff/xutils.h b/xdiff/xutils.h
index d5de829..ad1428e 100644
--- a/xdiff/xutils.h
+++ b/xdiff/xutils.h
@@ -31,16 +31,15 @@
 int xdl_cha_init(chastore_t *cha, long isize, long icount);
 void xdl_cha_free(chastore_t *cha);
 void *xdl_cha_alloc(chastore_t *cha);
-void *xdl_cha_first(chastore_t *cha);
-void *xdl_cha_next(chastore_t *cha);
-long xdl_guess_lines(mmfile_t *mf);
+long xdl_guess_lines(mmfile_t *mf, long sample);
 int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags);
 unsigned long xdl_hash_record(char const **data, char const *top, long flags);
 unsigned int xdl_hashbits(unsigned int size);
 int xdl_num_out(char *out, long val);
-long xdl_atol(char const *str, char const **next);
 int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2,
 		      const char *func, long funclen, xdemitcb_t *ecb);
+int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,
+		       int line1, int count1, int line2, int count2);
 
 
 
diff --git a/zlib.c b/zlib.c
new file mode 100644
index 0000000..2b2c0c7
--- /dev/null
+++ b/zlib.c
@@ -0,0 +1,258 @@
+/*
+ * zlib wrappers to make sure we don't silently miss errors
+ * at init time.
+ */
+#include "cache.h"
+
+static const char *zerr_to_string(int status)
+{
+	switch (status) {
+	case Z_MEM_ERROR:
+		return "out of memory";
+	case Z_VERSION_ERROR:
+		return "wrong version";
+	case Z_NEED_DICT:
+		return "needs dictionary";
+	case Z_DATA_ERROR:
+		return "data stream error";
+	case Z_STREAM_ERROR:
+		return "stream consistency error";
+	default:
+		return "unknown error";
+	}
+}
+
+/*
+ * avail_in and avail_out in zlib are counted in uInt, which typically
+ * limits the size of the buffer we can use to 4GB when interacting
+ * with zlib in a single call to inflate/deflate.
+ */
+/* #define ZLIB_BUF_MAX ((uInt)-1) */
+#define ZLIB_BUF_MAX ((uInt) 1024 * 1024 * 1024) /* 1GB */
+static inline uInt zlib_buf_cap(unsigned long len)
+{
+	return (ZLIB_BUF_MAX < len) ? ZLIB_BUF_MAX : len;
+}
+
+static void zlib_pre_call(git_zstream *s)
+{
+	s->z.next_in = s->next_in;
+	s->z.next_out = s->next_out;
+	s->z.total_in = s->total_in;
+	s->z.total_out = s->total_out;
+	s->z.avail_in = zlib_buf_cap(s->avail_in);
+	s->z.avail_out = zlib_buf_cap(s->avail_out);
+}
+
+static void zlib_post_call(git_zstream *s)
+{
+	unsigned long bytes_consumed;
+	unsigned long bytes_produced;
+
+	bytes_consumed = s->z.next_in - s->next_in;
+	bytes_produced = s->z.next_out - s->next_out;
+	if (s->z.total_out != s->total_out + bytes_produced)
+		die("BUG: total_out mismatch");
+	if (s->z.total_in != s->total_in + bytes_consumed)
+		die("BUG: total_in mismatch");
+
+	s->total_out = s->z.total_out;
+	s->total_in = s->z.total_in;
+	s->next_in = s->z.next_in;
+	s->next_out = s->z.next_out;
+	s->avail_in -= bytes_consumed;
+	s->avail_out -= bytes_produced;
+}
+
+void git_inflate_init(git_zstream *strm)
+{
+	int status;
+
+	zlib_pre_call(strm);
+	status = inflateInit(&strm->z);
+	zlib_post_call(strm);
+	if (status == Z_OK)
+		return;
+	die("inflateInit: %s (%s)", zerr_to_string(status),
+	    strm->z.msg ? strm->z.msg : "no message");
+}
+
+void git_inflate_init_gzip_only(git_zstream *strm)
+{
+	/*
+	 * Use default 15 bits, +16 is to accept only gzip and to
+	 * yield Z_DATA_ERROR when fed zlib format.
+	 */
+	const int windowBits = 15 + 16;
+	int status;
+
+	zlib_pre_call(strm);
+	status = inflateInit2(&strm->z, windowBits);
+	zlib_post_call(strm);
+	if (status == Z_OK)
+		return;
+	die("inflateInit2: %s (%s)", zerr_to_string(status),
+	    strm->z.msg ? strm->z.msg : "no message");
+}
+
+void git_inflate_end(git_zstream *strm)
+{
+	int status;
+
+	zlib_pre_call(strm);
+	status = inflateEnd(&strm->z);
+	zlib_post_call(strm);
+	if (status == Z_OK)
+		return;
+	error("inflateEnd: %s (%s)", zerr_to_string(status),
+	      strm->z.msg ? strm->z.msg : "no message");
+}
+
+int git_inflate(git_zstream *strm, int flush)
+{
+	int status;
+
+	for (;;) {
+		zlib_pre_call(strm);
+		/* Never say Z_FINISH unless we are feeding everything */
+		status = inflate(&strm->z,
+				 (strm->z.avail_in != strm->avail_in)
+				 ? 0 : flush);
+		if (status == Z_MEM_ERROR)
+			die("inflate: out of memory");
+		zlib_post_call(strm);
+
+		/*
+		 * Let zlib work another round, while we can still
+		 * make progress.
+		 */
+		if ((strm->avail_out && !strm->z.avail_out) &&
+		    (status == Z_OK || status == Z_BUF_ERROR))
+			continue;
+		break;
+	}
+
+	switch (status) {
+	/* Z_BUF_ERROR: normal, needs more space in the output buffer */
+	case Z_BUF_ERROR:
+	case Z_OK:
+	case Z_STREAM_END:
+		return status;
+	default:
+		break;
+	}
+	error("inflate: %s (%s)", zerr_to_string(status),
+	      strm->z.msg ? strm->z.msg : "no message");
+	return status;
+}
+
+#if defined(NO_DEFLATE_BOUND) || ZLIB_VERNUM < 0x1200
+#define deflateBound(c,s)  ((s) + (((s) + 7) >> 3) + (((s) + 63) >> 6) + 11)
+#endif
+
+unsigned long git_deflate_bound(git_zstream *strm, unsigned long size)
+{
+	return deflateBound(&strm->z, size);
+}
+
+void git_deflate_init(git_zstream *strm, int level)
+{
+	int status;
+
+	zlib_pre_call(strm);
+	status = deflateInit(&strm->z, level);
+	zlib_post_call(strm);
+	if (status == Z_OK)
+		return;
+	die("deflateInit: %s (%s)", zerr_to_string(status),
+	    strm->z.msg ? strm->z.msg : "no message");
+}
+
+void git_deflate_init_gzip(git_zstream *strm, int level)
+{
+	/*
+	 * Use default 15 bits, +16 is to generate gzip header/trailer
+	 * instead of the zlib wrapper.
+	 */
+	const int windowBits = 15 + 16;
+	int status;
+
+	zlib_pre_call(strm);
+	status = deflateInit2(&strm->z, level,
+				  Z_DEFLATED, windowBits,
+				  8, Z_DEFAULT_STRATEGY);
+	zlib_post_call(strm);
+	if (status == Z_OK)
+		return;
+	die("deflateInit2: %s (%s)", zerr_to_string(status),
+	    strm->z.msg ? strm->z.msg : "no message");
+}
+
+int git_deflate_abort(git_zstream *strm)
+{
+	int status;
+
+	zlib_pre_call(strm);
+	status = deflateEnd(&strm->z);
+	zlib_post_call(strm);
+	return status;
+}
+
+void git_deflate_end(git_zstream *strm)
+{
+	int status = git_deflate_abort(strm);
+
+	if (status == Z_OK)
+		return;
+	error("deflateEnd: %s (%s)", zerr_to_string(status),
+	      strm->z.msg ? strm->z.msg : "no message");
+}
+
+int git_deflate_end_gently(git_zstream *strm)
+{
+	int status;
+
+	zlib_pre_call(strm);
+	status = deflateEnd(&strm->z);
+	zlib_post_call(strm);
+	return status;
+}
+
+int git_deflate(git_zstream *strm, int flush)
+{
+	int status;
+
+	for (;;) {
+		zlib_pre_call(strm);
+
+		/* Never say Z_FINISH unless we are feeding everything */
+		status = deflate(&strm->z,
+				 (strm->z.avail_in != strm->avail_in)
+				 ? 0 : flush);
+		if (status == Z_MEM_ERROR)
+			die("deflate: out of memory");
+		zlib_post_call(strm);
+
+		/*
+		 * Let zlib work another round, while we can still
+		 * make progress.
+		 */
+		if ((strm->avail_out && !strm->z.avail_out) &&
+		    (status == Z_OK || status == Z_BUF_ERROR))
+			continue;
+		break;
+	}
+
+	switch (status) {
+	/* Z_BUF_ERROR: normal, needs more space in the output buffer */
+	case Z_BUF_ERROR:
+	case Z_OK:
+	case Z_STREAM_END:
+		return status;
+	default:
+		break;
+	}
+	error("deflate: %s (%s)", zerr_to_string(status),
+	      strm->z.msg ? strm->z.msg : "no message");
+	return status;
+}